From ee29c25b29eaa4fac4e897442634b69ecc8d8125 Mon Sep 17 00:00:00 2001 From: jacqueline Date: Thu, 28 Mar 2024 14:32:49 +1100 Subject: Fork ESP-IDF's bluetooth component i want better sbc encoding, and no cla will stop me --- lib/bt/host/bluedroid/Kconfig.in | 1195 +++ lib/bt/host/bluedroid/api/esp_a2dp_api.c | 353 + lib/bt/host/bluedroid/api/esp_avrc_api.c | 477 ++ lib/bt/host/bluedroid/api/esp_bluedroid_hci.c | 89 + lib/bt/host/bluedroid/api/esp_bt_device.c | 78 + lib/bt/host/bluedroid/api/esp_bt_main.c | 230 + lib/bt/host/bluedroid/api/esp_gap_ble_api.c | 1601 ++++ lib/bt/host/bluedroid/api/esp_gap_bt_api.c | 497 ++ lib/bt/host/bluedroid/api/esp_gatt_common_api.c | 75 + lib/bt/host/bluedroid/api/esp_gattc_api.c | 786 ++ lib/bt/host/bluedroid/api/esp_gatts_api.c | 442 ++ lib/bt/host/bluedroid/api/esp_hf_ag_api.c | 578 ++ lib/bt/host/bluedroid/api/esp_hf_client_api.c | 561 ++ lib/bt/host/bluedroid/api/esp_hidd_api.c | 170 + lib/bt/host/bluedroid/api/esp_hidh_api.c | 250 + lib/bt/host/bluedroid/api/esp_l2cap_bt_api.c | 131 + lib/bt/host/bluedroid/api/esp_sdp_api.c | 130 + lib/bt/host/bluedroid/api/esp_spp_api.c | 255 + .../host/bluedroid/api/include/api/esp_a2dp_api.h | 463 ++ .../host/bluedroid/api/include/api/esp_avrc_api.h | 736 ++ .../bluedroid/api/include/api/esp_bluedroid_hci.h | 84 + .../host/bluedroid/api/include/api/esp_bt_defs.h | 205 + .../host/bluedroid/api/include/api/esp_bt_device.h | 99 + .../host/bluedroid/api/include/api/esp_bt_main.h | 99 + .../bluedroid/api/include/api/esp_gap_ble_api.h | 2547 +++++++ .../bluedroid/api/include/api/esp_gap_bt_api.h | 916 +++ .../api/include/api/esp_gatt_common_api.h | 48 + .../host/bluedroid/api/include/api/esp_gatt_defs.h | 485 ++ .../host/bluedroid/api/include/api/esp_gattc_api.h | 913 +++ .../host/bluedroid/api/include/api/esp_gatts_api.h | 600 ++ .../host/bluedroid/api/include/api/esp_hf_ag_api.h | 716 ++ .../bluedroid/api/include/api/esp_hf_client_api.h | 736 ++ .../host/bluedroid/api/include/api/esp_hf_defs.h | 246 + .../host/bluedroid/api/include/api/esp_hidd_api.h | 413 ++ .../host/bluedroid/api/include/api/esp_hidh_api.h | 482 ++ .../bluedroid/api/include/api/esp_l2cap_bt_api.h | 251 + .../host/bluedroid/api/include/api/esp_sdp_api.h | 271 + .../host/bluedroid/api/include/api/esp_spp_api.h | 439 ++ lib/bt/host/bluedroid/bta/ar/bta_ar.c | 318 + lib/bt/host/bluedroid/bta/ar/include/bta_ar_int.h | 66 + lib/bt/host/bluedroid/bta/av/bta_av_aact.c | 3030 ++++++++ lib/bt/host/bluedroid/bta/av/bta_av_act.c | 1900 +++++ lib/bt/host/bluedroid/bta/av/bta_av_api.c | 616 ++ lib/bt/host/bluedroid/bta/av/bta_av_cfg.c | 110 + lib/bt/host/bluedroid/bta/av/bta_av_ci.c | 94 + lib/bt/host/bluedroid/bta/av/bta_av_main.c | 1416 ++++ lib/bt/host/bluedroid/bta/av/bta_av_sbc.c | 603 ++ lib/bt/host/bluedroid/bta/av/bta_av_ssm.c | 580 ++ lib/bt/host/bluedroid/bta/av/include/bta_av_int.h | 710 ++ lib/bt/host/bluedroid/bta/dm/bta_dm_act.c | 6747 +++++++++++++++++ lib/bt/host/bluedroid/bta/dm/bta_dm_api.c | 3424 +++++++++ lib/bt/host/bluedroid/bta/dm/bta_dm_cfg.c | 485 ++ lib/bt/host/bluedroid/bta/dm/bta_dm_ci.c | 85 + lib/bt/host/bluedroid/bta/dm/bta_dm_co.c | 458 ++ lib/bt/host/bluedroid/bta/dm/bta_dm_main.c | 549 ++ lib/bt/host/bluedroid/bta/dm/bta_dm_pm.c | 1172 +++ lib/bt/host/bluedroid/bta/dm/bta_dm_qos.c | 68 + lib/bt/host/bluedroid/bta/dm/bta_dm_sco.c | 674 ++ lib/bt/host/bluedroid/bta/dm/include/bta_dm_int.h | 1892 +++++ lib/bt/host/bluedroid/bta/gatt/bta_gatt_common.c | 25 + lib/bt/host/bluedroid/bta/gatt/bta_gattc_act.c | 2498 +++++++ lib/bt/host/bluedroid/bta/gatt/bta_gattc_api.c | 1221 +++ lib/bt/host/bluedroid/bta/gatt/bta_gattc_cache.c | 2216 ++++++ lib/bt/host/bluedroid/bta/gatt/bta_gattc_ci.c | 137 + lib/bt/host/bluedroid/bta/gatt/bta_gattc_co.c | 693 ++ lib/bt/host/bluedroid/bta/gatt/bta_gattc_main.c | 550 ++ lib/bt/host/bluedroid/bta/gatt/bta_gattc_utils.c | 1020 +++ lib/bt/host/bluedroid/bta/gatt/bta_gatts_act.c | 1070 +++ lib/bt/host/bluedroid/bta/gatt/bta_gatts_api.c | 680 ++ lib/bt/host/bluedroid/bta/gatt/bta_gatts_co.c | 251 + lib/bt/host/bluedroid/bta/gatt/bta_gatts_main.c | 168 + lib/bt/host/bluedroid/bta/gatt/bta_gatts_utils.c | 224 + .../bluedroid/bta/gatt/include/bta_gattc_int.h | 559 ++ .../bluedroid/bta/gatt/include/bta_gatts_int.h | 265 + lib/bt/host/bluedroid/bta/hd/bta_hd_act.c | 789 ++ lib/bt/host/bluedroid/bta/hd/bta_hd_api.c | 287 + lib/bt/host/bluedroid/bta/hd/bta_hd_main.c | 337 + lib/bt/host/bluedroid/bta/hd/include/bta_hd_int.h | 168 + lib/bt/host/bluedroid/bta/hf_ag/bta_ag_act.c | 816 ++ lib/bt/host/bluedroid/bta/hf_ag/bta_ag_api.c | 344 + lib/bt/host/bluedroid/bta/hf_ag/bta_ag_at.c | 201 + lib/bt/host/bluedroid/bta/hf_ag/bta_ag_cfg.c | 60 + lib/bt/host/bluedroid/bta/hf_ag/bta_ag_cmd.c | 1719 +++++ lib/bt/host/bluedroid/bta/hf_ag/bta_ag_main.c | 980 +++ lib/bt/host/bluedroid/bta/hf_ag/bta_ag_rfc.c | 406 + lib/bt/host/bluedroid/bta/hf_ag/bta_ag_sco.c | 1847 +++++ lib/bt/host/bluedroid/bta/hf_ag/bta_ag_sdp.c | 449 ++ .../host/bluedroid/bta/hf_ag/include/bta_ag_at.h | 126 + .../host/bluedroid/bta/hf_ag/include/bta_ag_int.h | 462 ++ .../bluedroid/bta/hf_client/bta_hf_client_act.c | 780 ++ .../bluedroid/bta/hf_client/bta_hf_client_api.c | 339 + .../bluedroid/bta/hf_client/bta_hf_client_at.c | 1862 +++++ .../bluedroid/bta/hf_client/bta_hf_client_cmd.c | 91 + .../bluedroid/bta/hf_client/bta_hf_client_main.c | 693 ++ .../bluedroid/bta/hf_client/bta_hf_client_rfc.c | 246 + .../bluedroid/bta/hf_client/bta_hf_client_sco.c | 944 +++ .../bluedroid/bta/hf_client/bta_hf_client_sdp.c | 367 + .../bta/hf_client/include/bta_hf_client_at.h | 110 + .../bta/hf_client/include/bta_hf_client_int.h | 316 + lib/bt/host/bluedroid/bta/hh/bta_hh_act.c | 1280 ++++ lib/bt/host/bluedroid/bta/hh/bta_hh_api.c | 477 ++ lib/bt/host/bluedroid/bta/hh/bta_hh_cfg.c | 64 + lib/bt/host/bluedroid/bta/hh/bta_hh_le.c | 3021 ++++++++ lib/bt/host/bluedroid/bta/hh/bta_hh_main.c | 568 ++ lib/bt/host/bluedroid/bta/hh/bta_hh_utils.c | 531 ++ lib/bt/host/bluedroid/bta/hh/include/bta_hh_int.h | 403 + lib/bt/host/bluedroid/bta/include/bta/bta_ag_api.h | 630 ++ lib/bt/host/bluedroid/bta/include/bta/bta_ag_co.h | 163 + lib/bt/host/bluedroid/bta/include/bta/bta_api.h | 3155 ++++++++ lib/bt/host/bluedroid/bta/include/bta/bta_ar_api.h | 144 + lib/bt/host/bluedroid/bta/include/bta/bta_av_api.h | 874 +++ lib/bt/host/bluedroid/bta/include/bta/bta_av_ci.h | 77 + lib/bt/host/bluedroid/bta/include/bta/bta_av_co.h | 393 + lib/bt/host/bluedroid/bta/include/bta/bta_av_sbc.h | 222 + lib/bt/host/bluedroid/bta/include/bta/bta_dm_ci.h | 68 + lib/bt/host/bluedroid/bta/include/bta/bta_dm_co.h | 218 + .../host/bluedroid/bta/include/bta/bta_gap_bt_co.h | 21 + .../host/bluedroid/bta/include/bta/bta_gatt_api.h | 1580 ++++ .../bluedroid/bta/include/bta/bta_gatt_common.h | 28 + .../host/bluedroid/bta/include/bta/bta_gattc_ci.h | 117 + .../host/bluedroid/bta/include/bta/bta_gattc_co.h | 140 + .../host/bluedroid/bta/include/bta/bta_gatts_co.h | 88 + lib/bt/host/bluedroid/bta/include/bta/bta_hd_api.h | 295 + .../bluedroid/bta/include/bta/bta_hf_client_api.h | 418 ++ .../bluedroid/bta/include/bta/bta_hf_client_co.h | 107 + .../host/bluedroid/bta/include/bta/bta_hfp_defs.h | 39 + lib/bt/host/bluedroid/bta/include/bta/bta_hh_api.h | 565 ++ lib/bt/host/bluedroid/bta/include/bta/bta_hh_co.h | 132 + lib/bt/host/bluedroid/bta/include/bta/bta_jv_api.h | 972 +++ lib/bt/host/bluedroid/bta/include/bta/bta_jv_co.h | 55 + .../host/bluedroid/bta/include/bta/bta_sdp_api.h | 168 + lib/bt/host/bluedroid/bta/include/bta/bta_sys.h | 292 + lib/bt/host/bluedroid/bta/include/bta/utl.h | 184 + lib/bt/host/bluedroid/bta/jv/bta_jv_act.c | 3049 ++++++++ lib/bt/host/bluedroid/bta/jv/bta_jv_api.c | 1236 ++++ lib/bt/host/bluedroid/bta/jv/bta_jv_cfg.c | 67 + lib/bt/host/bluedroid/bta/jv/bta_jv_main.c | 110 + lib/bt/host/bluedroid/bta/jv/include/bta_jv_int.h | 493 ++ lib/bt/host/bluedroid/bta/sdp/bta_sdp.c | 83 + lib/bt/host/bluedroid/bta/sdp/bta_sdp_act.c | 586 ++ lib/bt/host/bluedroid/bta/sdp/bta_sdp_api.c | 203 + lib/bt/host/bluedroid/bta/sdp/bta_sdp_cfg.c | 49 + .../host/bluedroid/bta/sdp/include/bta_sdp_int.h | 111 + lib/bt/host/bluedroid/bta/sys/bta_sys_conn.c | 663 ++ lib/bt/host/bluedroid/bta/sys/bta_sys_main.c | 766 ++ .../host/bluedroid/bta/sys/include/bta_sys_int.h | 101 + lib/bt/host/bluedroid/bta/sys/utl.c | 319 + lib/bt/host/bluedroid/btc/core/btc_ble_storage.c | 1101 +++ lib/bt/host/bluedroid/btc/core/btc_config.c | 351 + lib/bt/host/bluedroid/btc/core/btc_dev.c | 58 + lib/bt/host/bluedroid/btc/core/btc_dm.c | 1080 +++ lib/bt/host/bluedroid/btc/core/btc_main.c | 192 + lib/bt/host/bluedroid/btc/core/btc_profile_queue.c | 163 + lib/bt/host/bluedroid/btc/core/btc_sec.c | 0 lib/bt/host/bluedroid/btc/core/btc_sm.c | 191 + lib/bt/host/bluedroid/btc/core/btc_storage.c | 603 ++ lib/bt/host/bluedroid/btc/core/btc_util.c | 419 ++ .../bluedroid/btc/include/btc/btc_ble_storage.h | 113 + lib/bt/host/bluedroid/btc/include/btc/btc_common.h | 27 + lib/bt/host/bluedroid/btc/include/btc/btc_config.h | 52 + lib/bt/host/bluedroid/btc/include/btc/btc_dev.h | 40 + lib/bt/host/bluedroid/btc/include/btc/btc_dm.h | 92 + lib/bt/host/bluedroid/btc/include/btc/btc_main.h | 68 + .../bluedroid/btc/include/btc/btc_profile_queue.h | 47 + lib/bt/host/bluedroid/btc/include/btc/btc_sm.h | 107 + .../host/bluedroid/btc/include/btc/btc_storage.h | 197 + lib/bt/host/bluedroid/btc/include/btc/btc_util.h | 67 + .../btc/profile/esp/ble_button/button_pro.c | 334 + .../bluedroid/btc/profile/esp/include/button_pro.h | 112 + .../btc/profile/esp/include/wx_airsync_prf.h | 102 + .../profile/esp/wechat_AirSync/wx_airsync_prf.c | 263 + .../bluedroid/btc/profile/std/a2dp/bta_av_co.c | 1764 +++++ .../host/bluedroid/btc/profile/std/a2dp/btc_a2dp.c | 146 + .../btc/profile/std/a2dp/btc_a2dp_control.c | 189 + .../bluedroid/btc/profile/std/a2dp/btc_a2dp_sink.c | 752 ++ .../btc/profile/std/a2dp/btc_a2dp_source.c | 1650 +++++ .../host/bluedroid/btc/profile/std/a2dp/btc_av.c | 1735 +++++ .../btc/profile/std/a2dp/include/btc_av_co.h | 221 + .../bluedroid/btc/profile/std/avrc/bta_avrc_co.c | 99 + .../host/bluedroid/btc/profile/std/avrc/btc_avrc.c | 1458 ++++ .../btc/profile/std/battery/battery_prf.c | 597 ++ .../profile/std/battery/include/srvc_battery_int.h | 79 + .../bluedroid/btc/profile/std/dis/dis_profile.c | 291 + .../btc/profile/std/dis/include/srvc_dis_int.h | 82 + .../bluedroid/btc/profile/std/gap/bta_gap_bt_co.c | 32 + .../bluedroid/btc/profile/std/gap/btc_gap_ble.c | 2336 ++++++ .../bluedroid/btc/profile/std/gap/btc_gap_bt.c | 1201 +++ .../btc/profile/std/gatt/btc_gatt_common.c | 38 + .../bluedroid/btc/profile/std/gatt/btc_gatt_util.c | 193 + .../bluedroid/btc/profile/std/gatt/btc_gattc.c | 1060 +++ .../bluedroid/btc/profile/std/gatt/btc_gatts.c | 992 +++ .../bluedroid/btc/profile/std/hf_ag/bta_ag_co.c | 590 ++ .../bluedroid/btc/profile/std/hf_ag/btc_hf_ag.c | 1628 ++++ .../btc/profile/std/hf_client/bta_hf_client_co.c | 505 ++ .../btc/profile/std/hf_client/btc_hf_client.c | 1180 +++ .../host/bluedroid/btc/profile/std/hid/bta_hh_co.c | 152 + lib/bt/host/bluedroid/btc/profile/std/hid/btc_hd.c | 964 +++ lib/bt/host/bluedroid/btc/profile/std/hid/btc_hh.c | 1569 ++++ .../bluedroid/btc/profile/std/include/bt_sdp.h | 112 + .../bluedroid/btc/profile/std/include/btc_a2dp.h | 100 + .../btc/profile/std/include/btc_a2dp_control.h | 102 + .../btc/profile/std/include/btc_a2dp_sink.h | 131 + .../btc/profile/std/include/btc_a2dp_source.h | 245 + .../bluedroid/btc/profile/std/include/btc_av.h | 228 + .../bluedroid/btc/profile/std/include/btc_av_api.h | 193 + .../bluedroid/btc/profile/std/include/btc_avrc.h | 185 + .../btc/profile/std/include/btc_gap_ble.h | 421 ++ .../bluedroid/btc/profile/std/include/btc_gap_bt.h | 179 + .../btc/profile/std/include/btc_gatt_common.h | 29 + .../btc/profile/std/include/btc_gatt_util.h | 32 + .../bluedroid/btc/profile/std/include/btc_gattc.h | 252 + .../bluedroid/btc/profile/std/include/btc_gatts.h | 171 + .../bluedroid/btc/profile/std/include/btc_hd.h | 112 + .../bluedroid/btc/profile/std/include/btc_hf_ag.h | 255 + .../btc/profile/std/include/btc_hf_client.h | 170 + .../bluedroid/btc/profile/std/include/btc_hh.h | 189 + .../bluedroid/btc/profile/std/include/btc_l2cap.h | 72 + .../bluedroid/btc/profile/std/include/btc_sdp.h | 53 + .../bluedroid/btc/profile/std/include/btc_spp.h | 94 + .../bluedroid/btc/profile/std/include/dis_api.h | 338 + .../bluedroid/btc/profile/std/include/srvc_api.h | 210 + .../bluedroid/btc/profile/std/l2cap/btc_l2cap.c | 1257 ++++ .../host/bluedroid/btc/profile/std/sdp/btc_sdp.c | 1218 +++ .../bluedroid/btc/profile/std/smp/esp_app_sec.c | 93 + .../btc/profile/std/smp/include/esp_sec_api.h | 71 + .../host/bluedroid/btc/profile/std/spp/btc_spp.c | 1690 +++++ .../common/include/common/bluedroid_user_config.h | 513 ++ .../common/include/common/bt_common_types.h | 35 + .../host/bluedroid/common/include/common/bt_defs.h | 65 + .../bluedroid/common/include/common/bt_target.h | 2378 ++++++ .../bluedroid/common/include/common/bt_trace.h | 581 ++ .../common/include/common/bt_vendor_lib.h | 361 + lib/bt/host/bluedroid/common/include/common/bte.h | 116 + .../bluedroid/common/include/common/bte_appl.h | 41 + .../bluedroid/config/include/config/stack_config.h | 21 + lib/bt/host/bluedroid/config/stack_config.c | 55 + lib/bt/host/bluedroid/device/bdaddr.c | 126 + lib/bt/host/bluedroid/device/controller.c | 600 ++ .../host/bluedroid/device/include/device/bdaddr.h | 63 + .../bluedroid/device/include/device/controller.h | 96 + .../device/include/device/device_features.h | 29 + .../bluedroid/device/include/device/event_mask.h | 30 + .../host/bluedroid/device/include/device/interop.h | 45 + .../device/include/device/interop_database.h | 50 + .../host/bluedroid/device/include/device/version.h | 31 + lib/bt/host/bluedroid/device/interop.c | 62 + .../external/sbc/decoder/include/oi_assert.h | 85 + .../external/sbc/decoder/include/oi_bitstream.h | 123 + .../external/sbc/decoder/include/oi_bt_spec.h | 229 + .../external/sbc/decoder/include/oi_codec_sbc.h | 488 ++ .../sbc/decoder/include/oi_codec_sbc_private.h | 229 + .../external/sbc/decoder/include/oi_common.h | 43 + .../external/sbc/decoder/include/oi_cpu_dep.h | 505 ++ .../external/sbc/decoder/include/oi_modules.h | 170 + .../external/sbc/decoder/include/oi_osinterface.h | 196 + .../external/sbc/decoder/include/oi_status.h | 578 ++ .../external/sbc/decoder/include/oi_stddefs.h | 232 + .../external/sbc/decoder/include/oi_string.h | 207 + .../external/sbc/decoder/include/oi_time.h | 199 + .../external/sbc/decoder/include/oi_utils.h | 376 + .../bluedroid/external/sbc/decoder/srce/alloc.c | 82 + .../external/sbc/decoder/srce/bitalloc-sbc.c | 168 + .../bluedroid/external/sbc/decoder/srce/bitalloc.c | 405 + .../external/sbc/decoder/srce/bitstream-decode.c | 95 + .../external/sbc/decoder/srce/decoder-oina.c | 141 + .../external/sbc/decoder/srce/decoder-private.c | 259 + .../external/sbc/decoder/srce/decoder-sbc.c | 472 ++ .../bluedroid/external/sbc/decoder/srce/dequant.c | 215 + .../external/sbc/decoder/srce/framing-sbc.c | 59 + .../bluedroid/external/sbc/decoder/srce/framing.c | 253 + .../external/sbc/decoder/srce/oi_codec_version.c | 61 + .../external/sbc/decoder/srce/readsamplesjoint.inc | 111 + .../sbc/decoder/srce/synthesis-8-generated.c | 139 + .../external/sbc/decoder/srce/synthesis-dct8.c | 308 + .../external/sbc/decoder/srce/synthesis-sbc.c | 511 ++ .../external/sbc/encoder/include/sbc_dct.h | 91 + .../sbc/encoder/include/sbc_enc_func_declare.h | 56 + .../external/sbc/encoder/include/sbc_encoder.h | 208 + .../external/sbc/encoder/include/sbc_if.h | 47 + .../external/sbc/encoder/include/sbc_types.h | 59 + .../external/sbc/encoder/srce/sbc_analysis.c | 1104 +++ .../bluedroid/external/sbc/encoder/srce/sbc_dct.c | 244 + .../external/sbc/encoder/srce/sbc_dct_coeffs.c | 203 + .../sbc/encoder/srce/sbc_enc_bit_alloc_mono.c | 189 + .../sbc/encoder/srce/sbc_enc_bit_alloc_ste.c | 193 + .../external/sbc/encoder/srce/sbc_enc_coeffs.c | 318 + .../external/sbc/encoder/srce/sbc_encoder.c | 333 + .../external/sbc/encoder/srce/sbc_packing.c | 263 + .../bluedroid/external/sbc/plc/include/sbc_plc.h | 83 + lib/bt/host/bluedroid/external/sbc/plc/sbc_plc.c | 298 + lib/bt/host/bluedroid/hci/hci_audio.c | 7 + lib/bt/host/bluedroid/hci/hci_hal_h4.c | 652 ++ lib/bt/host/bluedroid/hci/hci_layer.c | 610 ++ lib/bt/host/bluedroid/hci/hci_packet_factory.c | 284 + lib/bt/host/bluedroid/hci/hci_packet_parser.c | 285 + .../host/bluedroid/hci/include/hci/bt_vendor_lib.h | 361 + lib/bt/host/bluedroid/hci/include/hci/hci_audio.h | 42 + lib/bt/host/bluedroid/hci/include/hci/hci_hal.h | 96 + .../host/bluedroid/hci/include/hci/hci_internals.h | 31 + lib/bt/host/bluedroid/hci/include/hci/hci_layer.h | 113 + .../bluedroid/hci/include/hci/hci_packet_factory.h | 57 + .../bluedroid/hci/include/hci/hci_packet_parser.h | 112 + .../host/bluedroid/hci/include/hci/hci_trans_int.h | 47 + .../bluedroid/hci/include/hci/packet_fragmenter.h | 62 + lib/bt/host/bluedroid/hci/packet_fragmenter.c | 238 + lib/bt/host/bluedroid/main/bte_init.c | 503 ++ lib/bt/host/bluedroid/main/bte_main.c | 247 + lib/bt/host/bluedroid/stack/a2dp/a2d_api.c | 434 ++ lib/bt/host/bluedroid/stack/a2dp/a2d_sbc.c | 228 + lib/bt/host/bluedroid/stack/a2dp/include/a2d_int.h | 77 + lib/bt/host/bluedroid/stack/avct/avct_api.c | 444 ++ lib/bt/host/bluedroid/stack/avct/avct_ccb.c | 145 + lib/bt/host/bluedroid/stack/avct/avct_l2c.c | 406 + lib/bt/host/bluedroid/stack/avct/avct_lcb.c | 445 ++ lib/bt/host/bluedroid/stack/avct/avct_lcb_act.c | 685 ++ .../host/bluedroid/stack/avct/include/avct_defs.h | 62 + .../host/bluedroid/stack/avct/include/avct_int.h | 237 + lib/bt/host/bluedroid/stack/avdt/avdt_ad.c | 596 ++ lib/bt/host/bluedroid/stack/avdt/avdt_api.c | 1303 ++++ lib/bt/host/bluedroid/stack/avdt/avdt_ccb.c | 456 ++ lib/bt/host/bluedroid/stack/avdt/avdt_ccb_act.c | 1084 +++ lib/bt/host/bluedroid/stack/avdt/avdt_l2c.c | 501 ++ lib/bt/host/bluedroid/stack/avdt/avdt_msg.c | 1784 +++++ lib/bt/host/bluedroid/stack/avdt/avdt_scb.c | 796 ++ lib/bt/host/bluedroid/stack/avdt/avdt_scb_act.c | 2187 ++++++ .../host/bluedroid/stack/avdt/include/avdt_defs.h | 207 + .../host/bluedroid/stack/avdt/include/avdt_int.h | 761 ++ lib/bt/host/bluedroid/stack/avrc/avrc_api.c | 1106 +++ lib/bt/host/bluedroid/stack/avrc/avrc_bld_ct.c | 329 + lib/bt/host/bluedroid/stack/avrc/avrc_bld_tg.c | 892 +++ lib/bt/host/bluedroid/stack/avrc/avrc_opt.c | 233 + lib/bt/host/bluedroid/stack/avrc/avrc_pars_ct.c | 165 + lib/bt/host/bluedroid/stack/avrc/avrc_pars_tg.c | 311 + lib/bt/host/bluedroid/stack/avrc/avrc_sdp.c | 403 + lib/bt/host/bluedroid/stack/avrc/avrc_utils.c | 239 + .../host/bluedroid/stack/avrc/include/avrc_int.h | 158 + lib/bt/host/bluedroid/stack/btm/btm_acl.c | 2757 +++++++ lib/bt/host/bluedroid/stack/btm/btm_ble.c | 2934 ++++++++ lib/bt/host/bluedroid/stack/btm/btm_ble_5_gap.c | 1455 ++++ lib/bt/host/bluedroid/stack/btm/btm_ble_addr.c | 608 ++ .../host/bluedroid/stack/btm/btm_ble_adv_filter.c | 1306 ++++ .../host/bluedroid/stack/btm/btm_ble_batchscan.c | 953 +++ lib/bt/host/bluedroid/stack/btm/btm_ble_bgconn.c | 836 +++ .../host/bluedroid/stack/btm/btm_ble_cont_energy.c | 108 + lib/bt/host/bluedroid/stack/btm/btm_ble_gap.c | 4724 ++++++++++++ .../host/bluedroid/stack/btm/btm_ble_multi_adv.c | 878 +++ lib/bt/host/bluedroid/stack/btm/btm_ble_privacy.c | 1086 +++ lib/bt/host/bluedroid/stack/btm/btm_dev.c | 741 ++ lib/bt/host/bluedroid/stack/btm/btm_devctl.c | 1294 ++++ lib/bt/host/bluedroid/stack/btm/btm_inq.c | 2973 ++++++++ lib/bt/host/bluedroid/stack/btm/btm_main.c | 152 + lib/bt/host/bluedroid/stack/btm/btm_pm.c | 965 +++ lib/bt/host/bluedroid/stack/btm/btm_sco.c | 1907 +++++ lib/bt/host/bluedroid/stack/btm/btm_sec.c | 6382 ++++++++++++++++ .../host/bluedroid/stack/btm/include/btm_ble_int.h | 548 ++ lib/bt/host/bluedroid/stack/btm/include/btm_int.h | 1258 ++++ lib/bt/host/bluedroid/stack/btu/btu_hcif.c | 2385 ++++++ lib/bt/host/bluedroid/stack/btu/btu_init.c | 276 + lib/bt/host/bluedroid/stack/btu/btu_task.c | 693 ++ lib/bt/host/bluedroid/stack/gap/gap_api.c | 109 + lib/bt/host/bluedroid/stack/gap/gap_ble.c | 823 +++ lib/bt/host/bluedroid/stack/gap/gap_conn.c | 1211 +++ lib/bt/host/bluedroid/stack/gap/gap_utils.c | 141 + lib/bt/host/bluedroid/stack/gap/include/gap_int.h | 159 + lib/bt/host/bluedroid/stack/gatt/att_protocol.c | 638 ++ lib/bt/host/bluedroid/stack/gatt/gatt_api.c | 1805 +++++ lib/bt/host/bluedroid/stack/gatt/gatt_attr.c | 797 ++ lib/bt/host/bluedroid/stack/gatt/gatt_auth.c | 522 ++ lib/bt/host/bluedroid/stack/gatt/gatt_cl.c | 1241 ++++ lib/bt/host/bluedroid/stack/gatt/gatt_db.c | 1620 ++++ lib/bt/host/bluedroid/stack/gatt/gatt_main.c | 1250 ++++ lib/bt/host/bluedroid/stack/gatt/gatt_sr.c | 1900 +++++ lib/bt/host/bluedroid/stack/gatt/gatt_sr_hash.c | 256 + lib/bt/host/bluedroid/stack/gatt/gatt_utils.c | 2948 ++++++++ .../host/bluedroid/stack/gatt/include/gatt_int.h | 788 ++ lib/bt/host/bluedroid/stack/hcic/hciblecmds.c | 1912 +++++ lib/bt/host/bluedroid/stack/hcic/hcicmds.c | 1913 +++++ lib/bt/host/bluedroid/stack/hid/hidd_api.c | 586 ++ lib/bt/host/bluedroid/stack/hid/hidd_conn.c | 784 ++ lib/bt/host/bluedroid/stack/hid/hidh_api.c | 654 ++ lib/bt/host/bluedroid/stack/hid/hidh_conn.c | 1044 +++ lib/bt/host/bluedroid/stack/hid/include/hid_conn.h | 72 + lib/bt/host/bluedroid/stack/hid/include/hid_int.h | 144 + .../host/bluedroid/stack/include/stack/a2d_api.h | 295 + .../host/bluedroid/stack/include/stack/a2d_sbc.h | 193 + .../stack/include/stack/acl_hci_link_interface.h | 15 + .../host/bluedroid/stack/include/stack/avct_api.h | 279 + .../host/bluedroid/stack/include/stack/avdt_api.h | 1016 +++ .../host/bluedroid/stack/include/stack/avdtc_api.h | 230 + .../host/bluedroid/stack/include/stack/avrc_api.h | 655 ++ .../host/bluedroid/stack/include/stack/avrc_defs.h | 1360 ++++ .../host/bluedroid/stack/include/stack/bt_types.h | 781 ++ .../host/bluedroid/stack/include/stack/btm_api.h | 4363 +++++++++++ .../bluedroid/stack/include/stack/btm_ble_api.h | 2724 +++++++ lib/bt/host/bluedroid/stack/include/stack/btu.h | 311 + .../host/bluedroid/stack/include/stack/dyn_mem.h | 234 + .../host/bluedroid/stack/include/stack/gap_api.h | 403 + .../host/bluedroid/stack/include/stack/gatt_api.h | 1298 ++++ .../host/bluedroid/stack/include/stack/gattdefs.h | 128 + .../host/bluedroid/stack/include/stack/hcidefs.h | 2730 +++++++ .../host/bluedroid/stack/include/stack/hcimsgs.h | 1065 +++ .../host/bluedroid/stack/include/stack/hidd_api.h | 273 + .../host/bluedroid/stack/include/stack/hiddefs.h | 163 + .../host/bluedroid/stack/include/stack/hidh_api.h | 258 + .../host/bluedroid/stack/include/stack/l2c_api.h | 1270 ++++ .../bluedroid/stack/include/stack/l2cap_client.h | 80 + .../stack/include/stack/l2cap_hci_link_interface.h | 15 + .../host/bluedroid/stack/include/stack/l2cdefs.h | 336 + .../host/bluedroid/stack/include/stack/port_api.h | 710 ++ .../host/bluedroid/stack/include/stack/port_ext.h | 31 + .../bluedroid/stack/include/stack/profiles_api.h | 69 + .../host/bluedroid/stack/include/stack/rfcdefs.h | 248 + .../host/bluedroid/stack/include/stack/sdp_api.h | 726 ++ .../host/bluedroid/stack/include/stack/sdpdefs.h | 325 + .../host/bluedroid/stack/include/stack/smp_api.h | 508 ++ .../host/bluedroid/stack/l2cap/include/l2c_int.h | 824 +++ lib/bt/host/bluedroid/stack/l2cap/l2c_api.c | 2456 ++++++ lib/bt/host/bluedroid/stack/l2cap/l2c_ble.c | 1707 +++++ lib/bt/host/bluedroid/stack/l2cap/l2c_csm.c | 1263 ++++ lib/bt/host/bluedroid/stack/l2cap/l2c_fcr.c | 2229 ++++++ lib/bt/host/bluedroid/stack/l2cap/l2c_link.c | 1509 ++++ lib/bt/host/bluedroid/stack/l2cap/l2c_main.c | 1065 +++ lib/bt/host/bluedroid/stack/l2cap/l2c_ucd.c | 1079 +++ lib/bt/host/bluedroid/stack/l2cap/l2c_utils.c | 3731 ++++++++++ lib/bt/host/bluedroid/stack/l2cap/l2cap_client.c | 462 ++ .../host/bluedroid/stack/rfcomm/include/port_int.h | 248 + .../host/bluedroid/stack/rfcomm/include/rfc_int.h | 378 + lib/bt/host/bluedroid/stack/rfcomm/port_api.c | 1883 +++++ lib/bt/host/bluedroid/stack/rfcomm/port_rfc.c | 1132 +++ lib/bt/host/bluedroid/stack/rfcomm/port_utils.c | 576 ++ lib/bt/host/bluedroid/stack/rfcomm/rfc_l2cap_if.c | 432 ++ lib/bt/host/bluedroid/stack/rfcomm/rfc_mx_fsm.c | 683 ++ lib/bt/host/bluedroid/stack/rfcomm/rfc_port_fsm.c | 908 +++ lib/bt/host/bluedroid/stack/rfcomm/rfc_port_if.c | 379 + lib/bt/host/bluedroid/stack/rfcomm/rfc_ts_frames.c | 953 +++ lib/bt/host/bluedroid/stack/rfcomm/rfc_utils.c | 510 ++ lib/bt/host/bluedroid/stack/sdp/include/sdpint.h | 319 + lib/bt/host/bluedroid/stack/sdp/sdp_api.c | 1242 ++++ lib/bt/host/bluedroid/stack/sdp/sdp_db.c | 992 +++ lib/bt/host/bluedroid/stack/sdp/sdp_discovery.c | 1026 +++ lib/bt/host/bluedroid/stack/sdp/sdp_main.c | 786 ++ lib/bt/host/bluedroid/stack/sdp/sdp_server.c | 838 +++ lib/bt/host/bluedroid/stack/sdp/sdp_utils.c | 1030 +++ lib/bt/host/bluedroid/stack/smp/aes.c | 938 +++ lib/bt/host/bluedroid/stack/smp/include/aes.h | 162 + .../bluedroid/stack/smp/include/p_256_ecc_pp.h | 74 + .../stack/smp/include/p_256_multprecision.h | 60 + lib/bt/host/bluedroid/stack/smp/include/smp_int.h | 545 ++ lib/bt/host/bluedroid/stack/smp/p_256_curvepara.c | 78 + lib/bt/host/bluedroid/stack/smp/p_256_ecc_pp.c | 283 + .../host/bluedroid/stack/smp/p_256_multprecision.c | 647 ++ lib/bt/host/bluedroid/stack/smp/smp_act.c | 2194 ++++++ lib/bt/host/bluedroid/stack/smp/smp_api.c | 615 ++ lib/bt/host/bluedroid/stack/smp/smp_br_main.c | 369 + lib/bt/host/bluedroid/stack/smp/smp_cmac.c | 369 + lib/bt/host/bluedroid/stack/smp/smp_keys.c | 2293 ++++++ lib/bt/host/bluedroid/stack/smp/smp_l2c.c | 344 + lib/bt/host/bluedroid/stack/smp/smp_main.c | 810 ++ lib/bt/host/bluedroid/stack/smp/smp_utils.c | 1645 +++++ lib/bt/host/nimble/Kconfig.in | 1016 +++ .../host/nimble/esp-hci/include/esp_nimble_hci.h | 52 + lib/bt/host/nimble/esp-hci/src/esp_nimble_hci.c | 300 + lib/bt/host/nimble/nimble/.gitignore | 5 + lib/bt/host/nimble/nimble/.mailmap | 8 + lib/bt/host/nimble/nimble/.rat-excludes | 37 + lib/bt/host/nimble/nimble/.style_ignored_dirs | 4 + lib/bt/host/nimble/nimble/.travis.yml | 156 + lib/bt/host/nimble/nimble/CODING_STANDARDS.md | 267 + lib/bt/host/nimble/nimble/LICENSE | 217 + lib/bt/host/nimble/nimble/NOTICE | 9 + lib/bt/host/nimble/nimble/README.md | 172 + lib/bt/host/nimble/nimble/RELEASE_NOTES.md | 25 + lib/bt/host/nimble/nimble/apps/README.md | 54 + lib/bt/host/nimble/nimble/apps/advertiser/pkg.yml | 33 + .../host/nimble/nimble/apps/advertiser/src/main.c | 136 + .../host/nimble/nimble/apps/advertiser/syscfg.yml | 23 + lib/bt/host/nimble/nimble/apps/blecent/pkg.yml | 35 + .../host/nimble/nimble/apps/blecent/src/blecent.h | 111 + lib/bt/host/nimble/nimble/apps/blecent/src/main.c | 538 ++ lib/bt/host/nimble/nimble/apps/blecent/src/misc.c | 209 + lib/bt/host/nimble/nimble/apps/blecent/src/peer.c | 807 ++ lib/bt/host/nimble/nimble/apps/blecent/syscfg.yml | 33 + lib/bt/host/nimble/nimble/apps/blecsc/README.md | 9 + lib/bt/host/nimble/nimble/apps/blecsc/pkg.yml | 38 + .../nimble/nimble/apps/blecsc/src/blecsc_sens.h | 105 + .../host/nimble/nimble/apps/blecsc/src/gatt_svr.c | 385 + lib/bt/host/nimble/nimble/apps/blecsc/src/main.c | 310 + lib/bt/host/nimble/nimble/apps/blecsc/syscfg.yml | 41 + lib/bt/host/nimble/nimble/apps/blehci/pkg.yml | 33 + lib/bt/host/nimble/nimble/apps/blehci/src/main.c | 45 + lib/bt/host/nimble/nimble/apps/blehci/syscfg.yml | 27 + lib/bt/host/nimble/nimble/apps/blehr/README.md | 9 + lib/bt/host/nimble/nimble/apps/blehr/pkg.yml | 38 + .../host/nimble/nimble/apps/blehr/src/blehr_sens.h | 46 + .../host/nimble/nimble/apps/blehr/src/gatt_svr.c | 177 + lib/bt/host/nimble/nimble/apps/blehr/src/main.c | 261 + lib/bt/host/nimble/nimble/apps/blehr/syscfg.yml | 38 + lib/bt/host/nimble/nimble/apps/blemesh/pkg.yml | 35 + lib/bt/host/nimble/nimble/apps/blemesh/src/main.c | 468 ++ lib/bt/host/nimble/nimble/apps/blemesh/syscfg.yml | 42 + .../host/nimble/nimble/apps/blemesh_light/pkg.yml | 35 + .../nimble/apps/blemesh_light/src/light_model.c | 242 + .../nimble/apps/blemesh_light/src/light_model.h | 37 + .../nimble/nimble/apps/blemesh_light/src/main.c | 113 + .../nimble/nimble/apps/blemesh_light/src/ws2812.c | 138 + .../nimble/nimble/apps/blemesh_light/src/ws2812.h | 42 + .../nimble/nimble/apps/blemesh_light/syscfg.yml | 66 + .../nimble/apps/blemesh_models_example_1/README.md | 79 + .../nimble/apps/blemesh_models_example_1/pkg.yml | 32 + .../apps/blemesh_models_example_1/src/main.c | 700 ++ .../apps/blemesh_models_example_1/syscfg.yml | 38 + .../nimble/apps/blemesh_models_example_2/README.md | 101 + .../nimble/apps/blemesh_models_example_2/pkg.yml | 38 + .../apps/blemesh_models_example_2/src/app_gpio.c | 95 + .../apps/blemesh_models_example_2/src/app_gpio.h | 36 + .../apps/blemesh_models_example_2/src/ble_mesh.c | 116 + .../apps/blemesh_models_example_2/src/ble_mesh.h | 73 + .../apps/blemesh_models_example_2/src/common.h | 33 + .../src/device_composition.c | 2863 +++++++ .../src/device_composition.h | 177 + .../apps/blemesh_models_example_2/src/main.c | 250 + .../src/no_transition_work_handler.c | 89 + .../src/no_transition_work_handler.h | 34 + .../apps/blemesh_models_example_2/src/publisher.c | 266 + .../apps/blemesh_models_example_2/src/publisher.h | 46 + .../blemesh_models_example_2/src/state_binding.c | 308 + .../blemesh_models_example_2/src/state_binding.h | 53 + .../apps/blemesh_models_example_2/src/storage.c | 255 + .../apps/blemesh_models_example_2/src/storage.h | 47 + .../apps/blemesh_models_example_2/src/transition.c | 792 ++ .../apps/blemesh_models_example_2/src/transition.h | 87 + .../apps/blemesh_models_example_2/syscfg.yml | 60 + .../host/nimble/nimble/apps/blemesh_shell/pkg.yml | 35 + .../nimble/nimble/apps/blemesh_shell/src/main.c | 114 + .../nimble/nimble/apps/blemesh_shell/syscfg.yml | 60 + lib/bt/host/nimble/nimble/apps/bleprph/pkg.yml | 44 + .../host/nimble/nimble/apps/bleprph/src/bleprph.h | 61 + .../host/nimble/nimble/apps/bleprph/src/gatt_svr.c | 204 + lib/bt/host/nimble/nimble/apps/bleprph/src/main.c | 372 + lib/bt/host/nimble/nimble/apps/bleprph/src/misc.c | 43 + lib/bt/host/nimble/nimble/apps/bleprph/src/phy.c | 128 + lib/bt/host/nimble/nimble/apps/bleprph/syscfg.yml | 71 + lib/bt/host/nimble/nimble/apps/blestress/README.md | 201 + lib/bt/host/nimble/nimble/apps/blestress/pkg.yml | 37 + .../host/nimble/nimble/apps/blestress/src/main.c | 99 + .../host/nimble/nimble/apps/blestress/src/misc.c | 224 + .../host/nimble/nimble/apps/blestress/src/misc.h | 54 + .../nimble/nimble/apps/blestress/src/rx_stress.c | 1489 ++++ .../nimble/nimble/apps/blestress/src/rx_stress.h | 53 + .../host/nimble/nimble/apps/blestress/src/stress.c | 392 + .../host/nimble/nimble/apps/blestress/src/stress.h | 376 + .../nimble/nimble/apps/blestress/src/stress_gatt.c | 155 + .../nimble/nimble/apps/blestress/src/stress_gatt.h | 54 + .../nimble/nimble/apps/blestress/src/tx_stress.c | 1670 +++++ .../nimble/nimble/apps/blestress/src/tx_stress.h | 49 + .../host/nimble/nimble/apps/blestress/syscfg.yml | 77 + lib/bt/host/nimble/nimble/apps/btshell/pkg.yml | 39 + .../host/nimble/nimble/apps/btshell/src/btshell.h | 216 + lib/bt/host/nimble/nimble/apps/btshell/src/cmd.c | 4688 ++++++++++++ lib/bt/host/nimble/nimble/apps/btshell/src/cmd.h | 68 + .../host/nimble/nimble/apps/btshell/src/cmd_gatt.c | 587 ++ .../host/nimble/nimble/apps/btshell/src/cmd_gatt.h | 39 + .../nimble/nimble/apps/btshell/src/cmd_l2cap.c | 325 + .../nimble/nimble/apps/btshell/src/cmd_l2cap.h | 33 + .../host/nimble/nimble/apps/btshell/src/gatt_svr.c | 646 ++ lib/bt/host/nimble/nimble/apps/btshell/src/main.c | 2669 +++++++ lib/bt/host/nimble/nimble/apps/btshell/src/misc.c | 163 + lib/bt/host/nimble/nimble/apps/btshell/src/parse.c | 736 ++ lib/bt/host/nimble/nimble/apps/btshell/syscfg.yml | 45 + lib/bt/host/nimble/nimble/apps/bttester/README | 14 + lib/bt/host/nimble/nimble/apps/bttester/pkg.yml | 42 + .../host/nimble/nimble/apps/bttester/src/atomic.h | 405 + .../nimble/nimble/apps/bttester/src/bttester.c | 379 + .../nimble/nimble/apps/bttester/src/bttester.h | 1321 ++++ .../nimble/apps/bttester/src/bttester_pipe.h | 40 + lib/bt/host/nimble/nimble/apps/bttester/src/gap.c | 1775 +++++ lib/bt/host/nimble/nimble/apps/bttester/src/gatt.c | 2098 ++++++ .../host/nimble/nimble/apps/bttester/src/gatt_cl.c | 1483 ++++ lib/bt/host/nimble/nimble/apps/bttester/src/glue.c | 129 + lib/bt/host/nimble/nimble/apps/bttester/src/glue.h | 63 + .../host/nimble/nimble/apps/bttester/src/l2cap.c | 765 ++ lib/bt/host/nimble/nimble/apps/bttester/src/main.c | 72 + lib/bt/host/nimble/nimble/apps/bttester/src/mesh.c | 993 +++ .../nimble/nimble/apps/bttester/src/rtt_pipe.c | 136 + .../nimble/nimble/apps/bttester/src/uart_pipe.c | 281 + lib/bt/host/nimble/nimble/apps/bttester/syscfg.yml | 142 + lib/bt/host/nimble/nimble/apps/central/pkg.yml | 33 + lib/bt/host/nimble/nimble/apps/central/src/main.c | 185 + lib/bt/host/nimble/nimble/apps/central/syscfg.yml | 26 + .../host/nimble/nimble/apps/ext_advertiser/pkg.yml | 38 + .../nimble/nimble/apps/ext_advertiser/src/main.c | 544 ++ .../nimble/apps/ext_advertiser/src/patterns.h | 186 + .../nimble/nimble/apps/ext_advertiser/syscfg.yml | 51 + .../host/nimble/nimble/apps/mesh_badge/README.md | 48 + lib/bt/host/nimble/nimble/apps/mesh_badge/pkg.yml | 37 + .../host/nimble/nimble/apps/mesh_badge/src/board.h | 15 + .../nimble/nimble/apps/mesh_badge/src/gatt_svr.c | 226 + .../host/nimble/nimble/apps/mesh_badge/src/main.c | 393 + .../host/nimble/nimble/apps/mesh_badge/src/mesh.c | 313 + .../host/nimble/nimble/apps/mesh_badge/src/mesh.h | 14 + .../nimble/nimble/apps/mesh_badge/src/mesh_badge.h | 28 + .../nimble/nimble/apps/mesh_badge/src/reel_board.c | 508 ++ .../host/nimble/nimble/apps/mesh_badge/syscfg.yml | 61 + lib/bt/host/nimble/nimble/apps/peripheral/pkg.yml | 35 + .../host/nimble/nimble/apps/peripheral/src/main.c | 166 + .../host/nimble/nimble/apps/peripheral/syscfg.yml | 26 + lib/bt/host/nimble/nimble/apps/scanner/pkg.yml | 34 + lib/bt/host/nimble/nimble/apps/scanner/src/main.c | 260 + lib/bt/host/nimble/nimble/apps/scanner/syscfg.yml | 26 + lib/bt/host/nimble/nimble/babblesim/README.md | 1 + .../nimble/babblesim/core/include/argparse.h | 30 + .../nimble/nimble/babblesim/core/include/cmsis.h | 51 + .../nimble/babblesim/core/include/core_cm4.h | 27 + .../nimble/babblesim/core/include/time_machine.h | 48 + lib/bt/host/nimble/nimble/babblesim/core/pkg.yml | 32 + .../nimble/nimble/babblesim/core/src/argparse.c | 179 + .../host/nimble/nimble/babblesim/core/src/cmsis.c | 81 + .../nimble/nimble/babblesim/core/src/irq_handler.c | 68 + .../nimble/nimble/babblesim/core/src/main_config.c | 84 + .../nimble/babblesim/core/src/time_machine.c | 255 + .../nimble/nimble/babblesim/edtt/hci_test/pkg.yml | 32 + .../nimble/babblesim/edtt/hci_test/src/main.c | 45 + .../nimble/babblesim/edtt/hci_test/syscfg.yml | 30 + .../edtt/hci_transport/include/ble_hci_edtt.h | 35 + .../edtt/hci_transport/include/commands.h | 242 + .../edtt/hci_transport/include/edtt_driver.h | 38 + .../nimble/babblesim/edtt/hci_transport/pkg.yml | 38 + .../edtt/hci_transport/src/ble_hci_edtt.c | 813 ++ .../edtt/hci_transport/src/edtt_driver_bsim.c | 289 + .../nimble/babblesim/edtt/hci_transport/syscfg.yml | 40 + .../nimble/babblesim/hw/bsp/nrf52_bsim/bsp.yml | 60 + .../babblesim/hw/bsp/nrf52_bsim/include/bsp/bsp.h | 91 + .../hw/bsp/nrf52_bsim/include/os/os_arch.h | 64 + .../babblesim/hw/bsp/nrf52_bsim/include/os/sim.h | 60 + .../hw/bsp/nrf52_bsim/nordic_pca10040_debug.cmd | 22 + .../hw/bsp/nrf52_bsim/nordic_pca10040_debug.sh | 45 + .../hw/bsp/nrf52_bsim/nordic_pca10040_download.cmd | 22 + .../hw/bsp/nrf52_bsim/nordic_pca10040_download.sh | 40 + .../nimble/babblesim/hw/bsp/nrf52_bsim/pkg.yml | 37 + .../hw/bsp/nrf52_bsim/src/arch/bsim_arch/os_arch.c | 169 + .../src/arch/bsim_arch/startup_nrf52_bsim.c | 231 + .../babblesim/hw/bsp/nrf52_bsim/src/hal_bsp.c | 160 + .../nimble/babblesim/hw/bsp/nrf52_bsim/syscfg.yml | 74 + .../mcu/nordic/nrf52_bsim/include/mcu/cmsis_nvic.h | 28 + .../mcu/nordic/nrf52_bsim/include/mcu/cortex_m4.h | 34 + .../hw/mcu/nordic/nrf52_bsim/include/mcu/mcu.h | 69 + .../hw/mcu/nordic/nrf52_bsim/include/mcu/mcu_sim.h | 38 + .../mcu/nordic/nrf52_bsim/include/mcu/native_bsp.h | 34 + .../nordic/nrf52_bsim/include/mcu/nrf52_clock.h | 50 + .../mcu/nordic/nrf52_bsim/include/mcu/nrf52_hal.h | 101 + .../nordic/nrf52_bsim/include/mcu/nrf52_periph.h | 33 + .../babblesim/hw/mcu/nordic/nrf52_bsim/pkg.yml | 32 + .../hw/mcu/nordic/nrf52_bsim/src/hal_flash.c | 289 + .../hw/mcu/nordic/nrf52_bsim/src/hal_hw_id.c | 73 + .../hw/mcu/nordic/nrf52_bsim/src/hal_native_priv.h | 27 + .../hw/mcu/nordic/nrf52_bsim/src/hal_os_tick.c | 226 + .../hw/mcu/nordic/nrf52_bsim/src/hal_reset_cause.c | 47 + .../hw/mcu/nordic/nrf52_bsim/src/hal_system.c | 128 + .../hw/mcu/nordic/nrf52_bsim/src/hal_timer.c | 949 +++ .../hw/mcu/nordic/nrf52_bsim/src/hal_uart.c | 478 ++ .../hw/mcu/nordic/nrf52_bsim/src/hal_watchdog.c | 36 + .../hw/mcu/nordic/nrf52_bsim/src/native_uart_cfg.c | 248 + .../nordic/nrf52_bsim/src/native_uart_cfg_priv.h | 31 + .../hw/mcu/nordic/nrf52_bsim/src/nrf52_clock.c | 103 + .../hw/mcu/nordic/nrf52_bsim/src/system_nrf52.c | 37 + .../babblesim/hw/mcu/nordic/nrf52_bsim/syscfg.yml | 526 ++ lib/bt/host/nimble/nimble/babblesim/sdk/.gitignore | 3 + lib/bt/host/nimble/nimble/babblesim/sdk/pkg.yml | 47 + .../nimble/babblesim/sdk/scripts/link_babblesim.sh | 47 + .../nimble/babblesim/sdk/scripts/link_nrfx.sh | 21 + .../nimble/babblesim/targets/blecent/pkg.yml | 23 + .../nimble/babblesim/targets/blecent/syscfg.yml | 20 + .../nimble/babblesim/targets/blecent/target.yml | 21 + .../nimble/nimble/babblesim/targets/blehci/pkg.yml | 23 + .../nimble/babblesim/targets/blehci/syscfg.yml | 21 + .../nimble/babblesim/targets/blehci/target.yml | 21 + .../nimble/babblesim/targets/bleprph/pkg.yml | 23 + .../nimble/babblesim/targets/bleprph/syscfg.yml | 20 + .../nimble/babblesim/targets/bleprph/target.yml | 21 + .../nimble/babblesim/targets/btshell/pkg.yml | 23 + .../nimble/babblesim/targets/btshell/syscfg.yml | 20 + .../nimble/babblesim/targets/btshell/target.yml | 21 + .../nimble/babblesim/targets/edtthci/pkg.yml | 24 + .../nimble/babblesim/targets/edtthci/syscfg.yml | 55 + .../nimble/babblesim/targets/edtthci/target.yml | 22 + lib/bt/host/nimble/nimble/docs/.gitignore | 5 + lib/bt/host/nimble/nimble/docs/Makefile | 25 + lib/bt/host/nimble/nimble/docs/README.rst | 33 + lib/bt/host/nimble/nimble/docs/ble_hs/ble_att.rst | 22 + lib/bt/host/nimble/nimble/docs/ble_hs/ble_gap.rst | 14 + .../host/nimble/nimble/docs/ble_hs/ble_gattc.rst | 15 + .../host/nimble/nimble/docs/ble_hs/ble_gatts.rst | 15 + lib/bt/host/nimble/nimble/docs/ble_hs/ble_hs.rst | 27 + .../host/nimble/nimble/docs/ble_hs/ble_hs_id.rst | 45 + .../nimble/docs/ble_hs/ble_hs_return_codes.rst | 437 ++ lib/bt/host/nimble/nimble/docs/ble_sec.rst | 76 + .../host/nimble/nimble/docs/ble_setup/ble_addr.rst | 63 + .../nimble/nimble/docs/ble_setup/ble_lp_clock.rst | 67 + .../nimble/docs/ble_setup/ble_setup_intro.rst | 13 + .../nimble/nimble/docs/ble_setup/ble_sync_cb.rst | 80 + .../nimble/nimble/docs/btshell/btshell_GAP.rst | 660 ++ .../nimble/nimble/docs/btshell/btshell_GATT.rst | 108 + .../nimble/nimble/docs/btshell/btshell_advdata.rst | 47 + .../nimble/nimble/docs/btshell/btshell_api.rst | 153 + lib/bt/host/nimble/nimble/docs/conf.py | 177 + lib/bt/host/nimble/nimble/docs/doxygen.xml | 2433 ++++++ lib/bt/host/nimble/nimble/docs/index.rst | 122 + lib/bt/host/nimble/nimble/docs/mesh/index.rst | 95 + .../nimble/docs/mesh/mesh_lightning_model.jpg | Bin 0 -> 120157 bytes .../host/nimble/nimble/docs/mesh/mesh_topology.jpg | Bin 0 -> 120443 bytes lib/bt/host/nimble/nimble/docs/mesh/sample.rst | 30 + lib/bt/host/nimble/nimble/ext/tinycrypt/AUTHORS | 15 + lib/bt/host/nimble/nimble/ext/tinycrypt/LICENSE | 61 + lib/bt/host/nimble/nimble/ext/tinycrypt/README | 71 + lib/bt/host/nimble/nimble/ext/tinycrypt/VERSION | 1 + .../ext/tinycrypt/documentation/tinycrypt.rst | 352 + .../nimble/ext/tinycrypt/include/tinycrypt/aes.h | 130 + .../ext/tinycrypt/include/tinycrypt/cbc_mode.h | 151 + .../ext/tinycrypt/include/tinycrypt/ccm_mode.h | 211 + .../ext/tinycrypt/include/tinycrypt/cmac_mode.h | 194 + .../ext/tinycrypt/include/tinycrypt/constants.h | 61 + .../ext/tinycrypt/include/tinycrypt/ctr_mode.h | 108 + .../ext/tinycrypt/include/tinycrypt/ctr_prng.h | 166 + .../nimble/ext/tinycrypt/include/tinycrypt/ecc.h | 545 ++ .../ext/tinycrypt/include/tinycrypt/ecc_dh.h | 131 + .../ext/tinycrypt/include/tinycrypt/ecc_dsa.h | 139 + .../include/tinycrypt/ecc_platform_specific.h | 81 + .../nimble/ext/tinycrypt/include/tinycrypt/hmac.h | 139 + .../ext/tinycrypt/include/tinycrypt/hmac_prng.h | 164 + .../ext/tinycrypt/include/tinycrypt/sha256.h | 129 + .../nimble/ext/tinycrypt/include/tinycrypt/utils.h | 95 + .../nimble/nimble/ext/tinycrypt/src/aes_decrypt.c | 164 + .../nimble/nimble/ext/tinycrypt/src/aes_encrypt.c | 191 + .../nimble/nimble/ext/tinycrypt/src/cbc_mode.c | 114 + .../nimble/nimble/ext/tinycrypt/src/ccm_mode.c | 266 + .../nimble/nimble/ext/tinycrypt/src/cmac_mode.c | 254 + .../nimble/nimble/ext/tinycrypt/src/ctr_mode.c | 85 + .../nimble/nimble/ext/tinycrypt/src/ctr_prng.c | 283 + lib/bt/host/nimble/nimble/ext/tinycrypt/src/ecc.c | 942 +++ .../host/nimble/nimble/ext/tinycrypt/src/ecc_dh.c | 200 + .../host/nimble/nimble/ext/tinycrypt/src/ecc_dsa.c | 295 + .../ext/tinycrypt/src/ecc_platform_specific.c | 105 + lib/bt/host/nimble/nimble/ext/tinycrypt/src/hmac.c | 148 + .../nimble/nimble/ext/tinycrypt/src/hmac_prng.c | 212 + .../host/nimble/nimble/ext/tinycrypt/src/sha256.c | 217 + .../host/nimble/nimble/ext/tinycrypt/src/utils.c | 74 + .../nimble/controller/include/controller/ble_hw.h | 110 + .../nimble/controller/include/controller/ble_ll.h | 632 ++ .../controller/include/controller/ble_ll_adv.h | 209 + .../controller/include/controller/ble_ll_conn.h | 508 ++ .../controller/include/controller/ble_ll_ctrl.h | 357 + .../controller/include/controller/ble_ll_hci.h | 94 + .../controller/include/controller/ble_ll_iso.h | 53 + .../controller/include/controller/ble_ll_plna.h | 45 + .../controller/include/controller/ble_ll_resolv.h | 122 + .../controller/include/controller/ble_ll_rfmgmt.h | 65 + .../controller/include/controller/ble_ll_scan.h | 271 + .../include/controller/ble_ll_scan_aux.h | 54 + .../controller/include/controller/ble_ll_sched.h | 213 + .../controller/include/controller/ble_ll_sync.h | 74 + .../controller/include/controller/ble_ll_test.h | 35 + .../controller/include/controller/ble_ll_tmr.h | 169 + .../controller/include/controller/ble_ll_trace.h | 96 + .../controller/include/controller/ble_ll_utils.h | 39 + .../include/controller/ble_ll_whitelist.h | 52 + .../nimble/controller/include/controller/ble_phy.h | 246 + .../controller/include/controller/ble_phy_trace.h | 96 + .../host/nimble/nimble/nimble/controller/pkg.yml | 40 + .../nimble/nimble/nimble/controller/src/ble_ll.c | 2010 +++++ .../nimble/nimble/controller/src/ble_ll_adv.c | 5148 +++++++++++++ .../nimble/nimble/controller/src/ble_ll_conn.c | 4341 +++++++++++ .../nimble/nimble/controller/src/ble_ll_conn_hci.c | 2133 ++++++ .../nimble/controller/src/ble_ll_conn_priv.h | 281 + .../nimble/nimble/controller/src/ble_ll_ctrl.c | 3214 ++++++++ .../nimble/controller/src/ble_ll_ctrl_priv.h | 35 + .../nimble/nimble/controller/src/ble_ll_dtm.c | 718 ++ .../nimble/nimble/controller/src/ble_ll_dtm_priv.h | 40 + .../nimble/nimble/controller/src/ble_ll_hci.c | 1867 +++++ .../nimble/nimble/controller/src/ble_ll_hci_ev.c | 612 ++ .../nimble/nimble/controller/src/ble_ll_hci_priv.h | 37 + .../nimble/nimble/controller/src/ble_ll_hci_vs.c | 341 + .../nimble/nimble/controller/src/ble_ll_iso.c | 146 + .../nimble/nimble/controller/src/ble_ll_priv.h | 53 + .../nimble/nimble/controller/src/ble_ll_rand.c | 210 + .../nimble/nimble/controller/src/ble_ll_resolv.c | 772 ++ .../nimble/nimble/controller/src/ble_ll_rfmgmt.c | 350 + .../nimble/nimble/controller/src/ble_ll_scan.c | 2735 +++++++ .../nimble/nimble/controller/src/ble_ll_scan_aux.c | 1763 +++++ .../nimble/nimble/controller/src/ble_ll_sched.c | 1246 ++++ .../nimble/nimble/controller/src/ble_ll_supp_cmd.c | 649 ++ .../nimble/nimble/controller/src/ble_ll_sync.c | 2279 ++++++ .../nimble/nimble/controller/src/ble_ll_trace.c | 56 + .../nimble/nimble/controller/src/ble_ll_utils.c | 423 ++ .../nimble/controller/src/ble_ll_whitelist.c | 293 + .../nimble/nimble/controller/syscfg.defunct.yml | 78 + .../nimble/nimble/nimble/controller/syscfg.hbd.yml | 25 + .../nimble/nimble/nimble/controller/syscfg.yml | 516 ++ .../nimble/nimble/nimble/controller/test/pkg.yml | 34 + .../nimble/controller/test/src/ble_ll_csa2_test.c | 287 + .../nimble/controller/test/src/ble_ll_csa2_test.h | 27 + .../nimble/controller/test/src/ble_ll_test.c | 36 + .../nimble/nimble/controller/test/syscfg.yml | 25 + .../nimble/nimble/drivers/dialog_cmac/README.md | 68 + .../nimble/drivers/dialog_cmac/include/ble/xcvr.h | 39 + .../nimble/nimble/drivers/dialog_cmac/pkg.yml | 33 + .../nimble/nimble/drivers/dialog_cmac/src/ble_hw.c | 345 + .../nimble/drivers/dialog_cmac/src/ble_hw_priv.h | 30 + .../nimble/drivers/dialog_cmac/src/ble_phy.c | 1793 +++++ .../nimble/nimble/drivers/dialog_cmac/src/ble_rf.c | 747 ++ .../nimble/drivers/dialog_cmac/src/ble_rf_priv.h | 38 + .../nimble/nimble/drivers/dialog_cmac/syscfg.yml | 29 + .../nimble/drivers/native/include/ble/xcvr.h | 46 + .../nimble/nimble/nimble/drivers/native/pkg.yml | 30 + .../nimble/nimble/drivers/native/src/ble_hw.c | 254 + .../nimble/nimble/drivers/native/src/ble_phy.c | 652 ++ .../nimble/nimble/drivers/nrf51/include/ble/xcvr.h | 48 + .../nimble/nimble/nimble/drivers/nrf51/pkg.yml | 31 + .../nimble/nimble/drivers/nrf51/src/ble_hw.c | 483 ++ .../nimble/nimble/drivers/nrf51/src/ble_phy.c | 1524 ++++ .../nimble/nimble/drivers/nrf52/include/ble/xcvr.h | 52 + .../nimble/nimble/nimble/drivers/nrf52/pkg.yml | 31 + .../nimble/nimble/drivers/nrf52/src/ble_hw.c | 511 ++ .../nimble/nimble/drivers/nrf52/src/ble_phy.c | 2257 ++++++ .../nimble/drivers/nrf52/src/ble_phy_trace.c | 44 + .../nimble/nimble/nimble/drivers/nrf52/syscfg.yml | 72 + .../nimble/drivers/nrf5340/include/ble/xcvr.h | 50 + .../nimble/nimble/nimble/drivers/nrf5340/pkg.yml | 31 + .../nimble/nimble/drivers/nrf5340/src/ble_hw.c | 471 ++ .../nimble/nimble/drivers/nrf5340/src/ble_phy.c | 2038 +++++ .../nimble/drivers/nrf5340/src/ble_phy_trace.c | 45 + .../nimble/nimble/drivers/nrf5340/syscfg.yml | 62 + .../nimble/nimble/drivers/plna/sky66112/pkg.yml | 31 + .../nimble/drivers/plna/sky66112/src/sky66112.c | 117 + .../nimble/nimble/drivers/plna/sky66112/syscfg.yml | 66 + .../nimble/nimble/host/include/host/ble_aes_ccm.h | 41 + .../nimble/nimble/host/include/host/ble_att.h | 213 + .../nimble/nimble/host/include/host/ble_ead.h | 132 + .../nimble/host/include/host/ble_eddystone.h | 117 + .../nimble/nimble/host/include/host/ble_esp_gap.h | 246 + .../nimble/nimble/host/include/host/ble_esp_gatt.h | 24 + .../nimble/nimble/host/include/host/ble_esp_hs.h | 56 + .../nimble/nimble/host/include/host/ble_gap.h | 2739 +++++++ .../nimble/nimble/host/include/host/ble_gatt.h | 1256 ++++ .../nimble/nimble/host/include/host/ble_hs.h | 389 + .../nimble/nimble/host/include/host/ble_hs_adv.h | 217 + .../nimble/nimble/host/include/host/ble_hs_hci.h | 116 + .../nimble/nimble/host/include/host/ble_hs_id.h | 132 + .../nimble/nimble/host/include/host/ble_hs_log.h | 53 + .../nimble/nimble/host/include/host/ble_hs_mbuf.h | 82 + .../nimble/nimble/host/include/host/ble_hs_pvcy.h | 77 + .../nimble/nimble/host/include/host/ble_hs_stop.h | 70 + .../nimble/nimble/host/include/host/ble_ibeacon.h | 34 + .../nimble/nimble/host/include/host/ble_l2cap.h | 266 + .../nimble/nimble/host/include/host/ble_sm.h | 124 + .../nimble/nimble/host/include/host/ble_store.h | 393 + .../nimble/nimble/host/include/host/ble_uuid.h | 188 + .../nimble/nimble/host/mesh/include/mesh/access.h | 628 ++ .../nimble/nimble/host/mesh/include/mesh/atomic.h | 409 + .../nimble/nimble/host/mesh/include/mesh/cdb.h | 265 + .../nimble/nimble/host/mesh/include/mesh/cfg.h | 491 ++ .../nimble/nimble/host/mesh/include/mesh/cfg_cli.h | 345 + .../nimble/nimble/host/mesh/include/mesh/cfg_srv.h | 40 + .../nimble/nimble/host/mesh/include/mesh/glue.h | 699 ++ .../nimble/host/mesh/include/mesh/health_cli.h | 77 + .../nimble/host/mesh/include/mesh/health_srv.h | 100 + .../nimble/host/mesh/include/mesh/heartbeat.h | 123 + .../nimble/nimble/host/mesh/include/mesh/main.h | 589 ++ .../nimble/nimble/host/mesh/include/mesh/mesh.h | 33 + .../nimble/host/mesh/include/mesh/model_cli.h | 49 + .../nimble/host/mesh/include/mesh/model_srv.h | 67 + .../nimble/nimble/host/mesh/include/mesh/msg.h | 225 + .../nimble/nimble/host/mesh/include/mesh/porting.h | 27 + .../nimble/nimble/host/mesh/include/mesh/proxy.h | 43 + .../nimble/nimble/host/mesh/include/mesh/slist.h | 468 ++ .../nimble/nimble/host/mesh/include/mesh/testing.h | 105 + lib/bt/host/nimble/nimble/nimble/host/mesh/pkg.yml | 49 + .../nimble/nimble/nimble/host/mesh/src/access.c | 1313 ++++ .../nimble/nimble/nimble/host/mesh/src/access.h | 88 + .../host/nimble/nimble/nimble/host/mesh/src/adv.c | 257 + .../host/nimble/nimble/nimble/host/mesh/src/adv.h | 131 + .../nimble/nimble/nimble/host/mesh/src/adv_ext.c | 345 + .../nimble/nimble/host/mesh/src/adv_legacy.c | 252 + .../nimble/nimble/nimble/host/mesh/src/aes-ccm.c | 224 + .../nimble/nimble/nimble/host/mesh/src/app_keys.c | 717 ++ .../nimble/nimble/nimble/host/mesh/src/app_keys.h | 68 + .../nimble/nimble/nimble/host/mesh/src/atomic.h | 409 + .../nimble/nimble/nimble/host/mesh/src/beacon.c | 472 ++ .../nimble/nimble/nimble/host/mesh/src/beacon.h | 27 + .../host/nimble/nimble/nimble/host/mesh/src/cdb.c | 1142 +++ .../nimble/nimble/nimble/host/mesh/src/cdb_priv.h | 12 + .../host/nimble/nimble/nimble/host/mesh/src/cfg.c | 388 + .../host/nimble/nimble/nimble/host/mesh/src/cfg.h | 10 + .../nimble/nimble/nimble/host/mesh/src/cfg_cli.c | 2714 +++++++ .../nimble/nimble/nimble/host/mesh/src/cfg_srv.c | 2645 +++++++ .../nimble/nimble/nimble/host/mesh/src/crypto.c | 605 ++ .../nimble/nimble/nimble/host/mesh/src/crypto.h | 171 + .../nimble/nimble/host/mesh/src/foundation.h | 137 + .../nimble/nimble/nimble/host/mesh/src/friend.c | 1791 +++++ .../nimble/nimble/nimble/host/mesh/src/friend.h | 56 + .../host/nimble/nimble/nimble/host/mesh/src/glue.c | 1039 +++ .../nimble/nimble/host/mesh/src/health_cli.c | 525 ++ .../nimble/nimble/host/mesh/src/health_srv.c | 475 ++ .../nimble/nimble/nimble/host/mesh/src/heartbeat.c | 464 ++ .../nimble/nimble/nimble/host/mesh/src/heartbeat.h | 43 + .../nimble/nimble/host/mesh/src/light_model.c | 57 + .../nimble/nimble/host/mesh/src/light_model.h | 19 + .../host/nimble/nimble/nimble/host/mesh/src/lpn.c | 1101 +++ .../host/nimble/nimble/nimble/host/mesh/src/lpn.h | 59 + .../host/nimble/nimble/nimble/host/mesh/src/mesh.c | 416 ++ .../nimble/nimble/nimble/host/mesh/src/mesh_priv.h | 53 + .../nimble/nimble/nimble/host/mesh/src/model_cli.c | 304 + .../nimble/nimble/nimble/host/mesh/src/model_srv.c | 282 + .../host/nimble/nimble/nimble/host/mesh/src/msg.c | 84 + .../host/nimble/nimble/nimble/host/mesh/src/net.c | 1223 +++ .../host/nimble/nimble/nimble/host/mesh/src/net.h | 328 + .../nimble/nimble/nimble/host/mesh/src/pb_adv.c | 911 +++ .../nimble/nimble/nimble/host/mesh/src/pb_gatt.c | 174 + .../nimble/nimble/host/mesh/src/pb_gatt_srv.c | 465 ++ .../nimble/nimble/host/mesh/src/pb_gatt_srv.h | 31 + .../host/nimble/nimble/nimble/host/mesh/src/prov.c | 459 ++ .../host/nimble/nimble/nimble/host/mesh/src/prov.h | 177 + .../nimble/nimble/host/mesh/src/prov_bearer.h | 116 + .../nimble/nimble/host/mesh/src/prov_device.c | 636 ++ .../nimble/nimble/host/mesh/src/provisioner.c | 751 ++ .../nimble/nimble/host/mesh/src/provisioner.h | 10 + .../nimble/nimble/nimble/host/mesh/src/proxy.c | 1576 ++++ .../nimble/nimble/nimble/host/mesh/src/proxy.h | 51 + .../nimble/nimble/nimble/host/mesh/src/proxy_msg.c | 235 + .../nimble/nimble/nimble/host/mesh/src/proxy_msg.h | 67 + .../nimble/nimble/nimble/host/mesh/src/proxy_srv.c | 1013 +++ .../host/nimble/nimble/nimble/host/mesh/src/rpl.c | 380 + .../host/nimble/nimble/nimble/host/mesh/src/rpl.h | 32 + .../nimble/nimble/nimble/host/mesh/src/settings.c | 216 + .../nimble/nimble/nimble/host/mesh/src/settings.h | 26 + .../nimble/nimble/nimble/host/mesh/src/shell.c | 3690 +++++++++ .../nimble/nimble/nimble/host/mesh/src/shell.h | 6 + .../nimble/nimble/nimble/host/mesh/src/subnet.c | 885 +++ .../nimble/nimble/nimble/host/mesh/src/subnet.h | 206 + .../nimble/nimble/nimble/host/mesh/src/testing.c | 205 + .../nimble/nimble/nimble/host/mesh/src/testing.h | 23 + .../nimble/nimble/nimble/host/mesh/src/transport.c | 1933 +++++ .../nimble/nimble/nimble/host/mesh/src/transport.h | 106 + .../host/nimble/nimble/nimble/host/mesh/syscfg.yml | 987 +++ lib/bt/host/nimble/nimble/nimble/host/pkg.yml | 50 + .../host/nimble/nimble/nimble/host/pts/README.txt | 8 + .../host/nimble/nimble/nimble/host/pts/pts-gap.txt | 367 + .../nimble/nimble/nimble/host/pts/pts-gatt.txt | 508 ++ .../nimble/nimble/nimble/host/pts/pts-l2cap.txt | 304 + .../host/nimble/nimble/nimble/host/pts/pts-sm.txt | 310 + .../host/pts/tpg/94654-20170317-085122560.tpg | 1026 +++ .../host/pts/tpg/94654-20170317-085441153.pts | 289 + .../ans/include/services/ans/ble_svc_ans.h | 85 + .../nimble/nimble/nimble/host/services/ans/pkg.yml | 34 + .../nimble/host/services/ans/src/ble_svc_ans.c | 463 ++ .../nimble/nimble/host/services/ans/syscfg.yml | 30 + .../bas/include/services/bas/ble_svc_bas.h | 33 + .../nimble/nimble/nimble/host/services/bas/pkg.yml | 34 + .../nimble/host/services/bas/src/ble_svc_bas.c | 127 + .../nimble/nimble/host/services/bas/syscfg.yml | 34 + .../services/bleuart/include/bleuart/bleuart.h | 42 + .../nimble/nimble/host/services/bleuart/pkg.yml | 37 + .../nimble/host/services/bleuart/src/bleuart.c | 204 + .../nimble/nimble/host/services/bleuart/syscfg.yml | 28 + .../cts/include/services/cts/ble_svc_cts.h | 160 + .../nimble/nimble/nimble/host/services/cts/pkg.yml | 34 + .../nimble/host/services/cts/src/ble_svc_cts.c | 279 + .../nimble/nimble/host/services/cts/syscfg.yml | 18 + .../dis/include/services/dis/ble_svc_dis.h | 122 + .../nimble/nimble/nimble/host/services/dis/pkg.yml | 34 + .../nimble/host/services/dis/src/ble_svc_dis.c | 363 + .../nimble/nimble/host/services/dis/syscfg.yml | 109 + .../gap/include/services/gap/ble_svc_gap.h | 65 + .../nimble/nimble/nimble/host/services/gap/pkg.yml | 34 + .../nimble/host/services/gap/src/ble_svc_gap.c | 335 + .../nimble/nimble/host/services/gap/syscfg.yml | 83 + .../gatt/include/services/gatt/ble_svc_gatt.h | 53 + .../nimble/nimble/host/services/gatt/pkg.yml | 34 + .../nimble/host/services/gatt/src/ble_svc_gatt.c | 235 + .../nimble/nimble/host/services/gatt/syscfg.yml | 24 + .../hid/include/services/hid/ble_svc_hid.h | 112 + .../nimble/nimble/nimble/host/services/hid/pkg.yml | 34 + .../nimble/host/services/hid/src/ble_svc_hid.c | 694 ++ .../services/hr/include/services/hr/ble_svc_hr.h | 29 + .../nimble/nimble/nimble/host/services/hr/pkg.yml | 21 + .../nimble/host/services/hr/src/ble_svc_hr.c | 233 + .../nimble/nimble/host/services/hr/syscfg.yml | 11 + .../htp/include/services/htp/ble_svc_htp.h | 50 + .../nimble/nimble/nimble/host/services/htp/pkg.yml | 21 + .../nimble/host/services/htp/src/ble_svc_htp.c | 291 + .../nimble/nimble/host/services/htp/syscfg.yml | 11 + .../ias/include/services/ias/ble_svc_ias.h | 38 + .../nimble/nimble/nimble/host/services/ias/pkg.yml | 34 + .../nimble/host/services/ias/src/ble_svc_ias.c | 149 + .../nimble/nimble/host/services/ias/syscfg.yml | 23 + .../ipss/include/services/ipss/ble_svc_ipss.h | 38 + .../nimble/nimble/host/services/ipss/pkg.yml | 35 + .../nimble/host/services/ipss/src/ble_svc_ipss.c | 51 + .../nimble/nimble/host/services/ipss/syscfg.yml | 24 + .../lls/include/services/lls/ble_svc_lls.h | 51 + .../nimble/nimble/nimble/host/services/lls/pkg.yml | 34 + .../nimble/host/services/lls/src/ble_svc_lls.c | 192 + .../nimble/nimble/host/services/lls/syscfg.yml | 22 + .../prox/include/services/prox/ble_svc_prox.h | 32 + .../nimble/nimble/host/services/prox/pkg.yml | 21 + .../nimble/host/services/prox/src/ble_svc_prox.c | 263 + .../nimble/nimble/host/services/prox/syscfg.yml | 11 + .../sps/include/services/sps/ble_svc_sps.h | 32 + .../nimble/nimble/nimble/host/services/sps/pkg.yml | 34 + .../nimble/host/services/sps/src/ble_svc_sps.c | 140 + .../tps/include/services/tps/ble_svc_tps.h | 31 + .../nimble/nimble/nimble/host/services/tps/pkg.yml | 34 + .../nimble/host/services/tps/src/ble_svc_tps.c | 107 + .../nimble/nimble/host/services/tps/syscfg.yml | 23 + .../nimble/nimble/nimble/host/src/ble_aes_ccm.c | 307 + .../host/nimble/nimble/nimble/host/src/ble_att.c | 604 ++ .../nimble/nimble/nimble/host/src/ble_att_clt.c | 1068 +++ .../nimble/nimble/nimble/host/src/ble_att_cmd.c | 638 ++ .../nimble/nimble/host/src/ble_att_cmd_priv.h | 463 ++ .../nimble/nimble/nimble/host/src/ble_att_priv.h | 317 + .../nimble/nimble/nimble/host/src/ble_att_svr.c | 3361 +++++++++ .../host/nimble/nimble/nimble/host/src/ble_ead.c | 209 + .../nimble/nimble/nimble/host/src/ble_eddystone.c | 178 + .../host/nimble/nimble/nimble/host/src/ble_gap.c | 7813 ++++++++++++++++++++ .../nimble/nimble/nimble/host/src/ble_gap_priv.h | 168 + .../nimble/nimble/nimble/host/src/ble_gatt_priv.h | 246 + .../host/nimble/nimble/nimble/host/src/ble_gattc.c | 5040 +++++++++++++ .../nimble/nimble/host/src/ble_gattc_cache.c | 728 ++ .../nimble/nimble/host/src/ble_gattc_cache_conn.c | 1794 +++++ .../nimble/nimble/host/src/ble_gattc_cache_priv.h | 184 + .../host/nimble/nimble/nimble/host/src/ble_gatts.c | 3245 ++++++++ .../nimble/nimble/nimble/host/src/ble_gatts_lcl.c | 214 + lib/bt/host/nimble/nimble/nimble/host/src/ble_hs.c | 891 +++ .../nimble/nimble/nimble/host/src/ble_hs_adv.c | 959 +++ .../nimble/nimble/host/src/ble_hs_adv_priv.h | 36 + .../nimble/nimble/nimble/host/src/ble_hs_atomic.c | 120 + .../nimble/nimble/host/src/ble_hs_atomic_priv.h | 41 + .../nimble/nimble/nimble/host/src/ble_hs_cfg.c | 33 + .../nimble/nimble/nimble/host/src/ble_hs_conn.c | 603 ++ .../nimble/nimble/host/src/ble_hs_conn_priv.h | 148 + .../nimble/nimble/nimble/host/src/ble_hs_flow.c | 305 + .../nimble/nimble/host/src/ble_hs_flow_priv.h | 36 + .../nimble/nimble/nimble/host/src/ble_hs_hci.c | 691 ++ .../nimble/nimble/nimble/host/src/ble_hs_hci_cmd.c | 122 + .../nimble/nimble/nimble/host/src/ble_hs_hci_evt.c | 1319 ++++ .../nimble/nimble/host/src/ble_hs_hci_priv.h | 135 + .../nimble/nimble/host/src/ble_hs_hci_util.c | 360 + .../host/nimble/nimble/nimble/host/src/ble_hs_id.c | 399 + .../nimble/nimble/nimble/host/src/ble_hs_id_priv.h | 45 + .../nimble/nimble/nimble/host/src/ble_hs_log.c | 48 + .../nimble/nimble/nimble/host/src/ble_hs_mbuf.c | 174 + .../nimble/nimble/host/src/ble_hs_mbuf_priv.h | 38 + .../nimble/nimble/nimble/host/src/ble_hs_misc.c | 149 + .../nimble/nimble/nimble/host/src/ble_hs_mqueue.c | 88 + .../nimble/nimble/host/src/ble_hs_periodic_sync.c | 157 + .../nimble/host/src/ble_hs_periodic_sync_priv.h | 55 + .../nimble/nimble/nimble/host/src/ble_hs_priv.h | 161 + .../nimble/nimble/nimble/host/src/ble_hs_pvcy.c | 378 + .../nimble/nimble/host/src/ble_hs_pvcy_priv.h | 47 + .../nimble/nimble/nimble/host/src/ble_hs_resolv.c | 803 ++ .../nimble/nimble/host/src/ble_hs_resolv_priv.h | 115 + .../nimble/nimble/host/src/ble_hs_shutdown.c | 69 + .../nimble/nimble/nimble/host/src/ble_hs_startup.c | 393 + .../nimble/nimble/host/src/ble_hs_startup_priv.h | 33 + .../nimble/nimble/nimble/host/src/ble_hs_stop.c | 290 + .../nimble/nimble/nimble/host/src/ble_ibeacon.c | 83 + .../host/nimble/nimble/nimble/host/src/ble_l2cap.c | 523 ++ .../nimble/nimble/nimble/host/src/ble_l2cap_coc.c | 659 ++ .../nimble/nimble/host/src/ble_l2cap_coc_priv.h | 106 + .../nimble/nimble/nimble/host/src/ble_l2cap_priv.h | 145 + .../nimble/nimble/nimble/host/src/ble_l2cap_sig.c | 2030 +++++ .../nimble/nimble/host/src/ble_l2cap_sig_cmd.c | 120 + .../nimble/nimble/host/src/ble_l2cap_sig_priv.h | 184 + lib/bt/host/nimble/nimble/nimble/host/src/ble_sm.c | 3091 ++++++++ .../nimble/nimble/nimble/host/src/ble_sm_alg.c | 772 ++ .../nimble/nimble/nimble/host/src/ble_sm_cmd.c | 73 + .../nimble/nimble/nimble/host/src/ble_sm_lgcy.c | 256 + .../nimble/nimble/nimble/host/src/ble_sm_priv.h | 442 ++ .../host/nimble/nimble/nimble/host/src/ble_sm_sc.c | 928 +++ .../host/nimble/nimble/nimble/host/src/ble_store.c | 586 ++ .../nimble/nimble/nimble/host/src/ble_store_util.c | 368 + .../host/nimble/nimble/nimble/host/src/ble_uuid.c | 259 + .../nimble/nimble/nimble/host/src/ble_uuid_priv.h | 45 + .../config/include/store/config/ble_store_config.h | 39 + .../nimble/nimble/nimble/host/store/config/pkg.yml | 38 + .../host/store/config/src/ble_store_config.c | 953 +++ .../host/store/config/src/ble_store_config_conf.c | 311 + .../host/store/config/src/ble_store_config_priv.h | 87 + .../nimble/host/store/config/src/ble_store_nvs.c | 844 +++ .../nimble/nimble/host/store/config/syscfg.yml | 27 + .../store/ram/include/store/ram/ble_store_ram.h | 44 + .../nimble/nimble/nimble/host/store/ram/pkg.yml | 37 + .../nimble/host/store/ram/src/ble_store_ram.c | 682 ++ .../nimble/nimble/nimble/host/store/ram/syscfg.yml | 32 + lib/bt/host/nimble/nimble/nimble/host/syscfg.yml | 464 ++ lib/bt/host/nimble/nimble/nimble/host/test/pkg.yml | 37 + .../nimble/nimble/host/test/src/ble_att_clt_test.c | 548 ++ .../nimble/nimble/host/test/src/ble_att_svr_test.c | 2138 ++++++ .../nimble/nimble/host/test/src/ble_gap_test.c | 3169 ++++++++ .../nimble/host/test/src/ble_gatt_conn_test.c | 746 ++ .../nimble/host/test/src/ble_gatt_disc_c_test.c | 722 ++ .../nimble/host/test/src/ble_gatt_disc_d_test.c | 446 ++ .../nimble/host/test/src/ble_gatt_disc_s_test.c | 631 ++ .../nimble/host/test/src/ble_gatt_find_s_test.c | 433 ++ .../nimble/host/test/src/ble_gatt_read_test.c | 923 +++ .../nimble/host/test/src/ble_gatt_write_test.c | 832 +++ .../nimble/host/test/src/ble_gatts_notify_test.c | 1090 +++ .../nimble/host/test/src/ble_gatts_read_test.c | 257 + .../nimble/host/test/src/ble_gatts_reg_test.c | 719 ++ .../nimble/nimble/host/test/src/ble_hs_adv_test.c | 1280 ++++ .../nimble/nimble/host/test/src/ble_hs_conn_test.c | 224 + .../nimble/nimble/host/test/src/ble_hs_hci_test.c | 342 + .../nimble/nimble/host/test/src/ble_hs_id_test.c | 124 + .../nimble/nimble/host/test/src/ble_hs_pvcy_test.c | 509 ++ .../nimble/nimble/host/test/src/ble_hs_stop_test.c | 203 + .../nimble/nimble/host/test/src/ble_hs_test.c | 83 + .../nimble/nimble/host/test/src/ble_hs_test.h | 62 + .../nimble/nimble/host/test/src/ble_hs_test_util.c | 2049 +++++ .../nimble/nimble/host/test/src/ble_hs_test_util.h | 307 + .../nimble/host/test/src/ble_hs_test_util_hci.c | 614 ++ .../nimble/host/test/src/ble_hs_test_util_hci.h | 105 + .../nimble/nimble/host/test/src/ble_l2cap_test.c | 1564 ++++ .../nimble/nimble/host/test/src/ble_os_test.c | 387 + .../nimble/nimble/host/test/src/ble_sm_lgcy_test.c | 849 +++ .../nimble/nimble/host/test/src/ble_sm_sc_test.c | 5544 ++++++++++++++ .../nimble/nimble/host/test/src/ble_sm_test.c | 414 ++ .../nimble/nimble/host/test/src/ble_sm_test_util.c | 3063 ++++++++ .../nimble/nimble/host/test/src/ble_sm_test_util.h | 129 + .../nimble/nimble/host/test/src/ble_store_test.c | 435 ++ .../nimble/nimble/host/test/src/ble_uuid_test.c | 76 + .../host/nimble/nimble/nimble/host/test/syscfg.yml | 32 + .../nimble/nimble/nimble/host/tools/log2smtest.rb | 1029 +++ .../nimble/host/util/include/host/util/util.h | 46 + lib/bt/host/nimble/nimble/nimble/host/util/pkg.yml | 29 + .../host/nimble/nimble/nimble/host/util/src/addr.c | 109 + .../host/nimble/nimble/nimble/host/util/syscfg.yml | 19 + .../host/nimble/nimble/nimble/include/nimble/ble.h | 315 + .../nimble/nimble/include/nimble/hci_common.h | 2045 +++++ .../nimble/nimble/include/nimble/nimble_npl.h | 180 + .../nimble/nimble/include/nimble/nimble_opt.h | 34 + .../nimble/nimble/include/nimble/nimble_opt_auto.h | 121 + lib/bt/host/nimble/nimble/nimble/pkg.yml | 30 + lib/bt/host/nimble/nimble/nimble/syscfg.yml | 127 + .../nimble/nimble/nimble/transport/apollo3/pkg.yml | 46 + .../nimble/transport/apollo3/src/apollo3_ble_hci.c | 351 + .../nimble/nimble/transport/apollo3/syscfg.yml | 37 + .../hci_h4/include/nimble/transport/hci_h4.h | 68 + .../nimble/nimble/transport/common/hci_h4/pkg.yml | 26 + .../nimble/transport/common/hci_h4/src/hci_h4.c | 318 + .../transport/dialog_cmac/cmac_driver/diag/pkg.yml | 28 + .../dialog_cmac/cmac_driver/diag/src/cmac_diag.c | 66 + .../cmac_driver/include/cmac_driver/cmac_diag.h | 34 + .../cmac_driver/include/cmac_driver/cmac_host.h | 35 + .../cmac_driver/include/cmac_driver/cmac_shared.h | 194 + .../transport/dialog_cmac/cmac_driver/pkg.yml | 39 + .../cmac_driver/scripts/build_libcmac.sh | 45 + .../cmac_driver/scripts/create_cmac_bin.sh | 31 + .../dialog_cmac/cmac_driver/src/cmac_host.c | 375 + .../dialog_cmac/cmac_driver/src/cmac_mbox.c | 174 + .../dialog_cmac/cmac_driver/src/cmac_rand.c | 145 + .../dialog_cmac/cmac_driver/src/cmac_shared.c | 96 + .../transport/dialog_cmac/cmac_driver/syscfg.yml | 104 + .../nimble/nimble/transport/dialog_cmac/pkg.yml | 33 + .../nimble/transport/dialog_cmac/src/hci_cmac.c | 213 + .../emspi/include/transport/emspi/ble_hci_emspi.h | 33 + .../nimble/nimble/nimble/transport/emspi/pkg.yml | 36 + .../nimble/transport/emspi/src/ble_hci_emspi.c | 698 ++ .../nimble/nimble/transport/emspi/syscfg.yml | 64 + .../nimble/transport/include/nimble/transport.h | 362 + .../transport/include/nimble/transport/monitor.h | 70 + .../transport/include/nimble/transport_impl.h | 50 + .../nimble/nimble/nimble/transport/nrf5340/pkg.yml | 36 + .../nimble/transport/nrf5340/src/nrf5340_ble_hci.c | 181 + .../nimble/nimble/transport/nrf5340/syscfg.yml | 20 + lib/bt/host/nimble/nimble/nimble/transport/pkg.yml | 62 + .../socket/include/socket/ble_hci_socket.h | 34 + .../nimble/nimble/nimble/transport/socket/pkg.yml | 38 + .../nimble/transport/socket/src/ble_hci_socket.c | 854 +++ .../nimble/nimble/transport/socket/syscfg.yml | 52 + .../nimble/nimble/nimble/transport/src/monitor.c | 542 ++ .../nimble/nimble/transport/src/monitor_priv.h | 98 + .../nimble/nimble/nimble/transport/src/transport.c | 284 + .../nimble/nimble/transport/syscfg.defunct.yml | 40 + .../nimble/nimble/transport/syscfg.monitor.yml | 58 + .../host/nimble/nimble/nimble/transport/syscfg.yml | 97 + .../nimble/nimble/nimble/transport/uart/pkg.yml | 32 + .../nimble/nimble/transport/uart/src/hci_uart.c | 247 + .../nimble/nimble/nimble/transport/uart/syscfg.yml | 39 + .../nimble/nimble/nimble/transport/uart_ll/pkg.yml | 32 + .../nimble/nimble/transport/uart_ll/src/hci_uart.c | 245 + .../nimble/nimble/transport/uart_ll/syscfg.yml | 39 + .../nimble/nimble/nimble/transport/usb/pkg.yml | 37 + .../nimble/nimble/transport/usb/src/ble_hci_usb.c | 267 + .../nimble/nimble/nimble/transport/usb/syscfg.yml | 19 + .../nimble/nimble/porting/examples/dummy/Makefile | 82 + .../nimble/nimble/porting/examples/dummy/main.c | 27 + .../nimble/nimble/porting/examples/linux/Makefile | 105 + .../nimble/nimble/porting/examples/linux/README.md | 73 + .../nimble/nimble/porting/examples/linux/ble.c | 116 + .../porting/examples/linux/include/logcfg/logcfg.h | 25 + .../porting/examples/linux/include/syscfg/syscfg.h | 1204 +++ .../examples/linux/include/sysflash/sysflash.h | 24 + .../nimble/nimble/porting/examples/linux/main.c | 92 + .../nimble/porting/examples/linux_blemesh/Makefile | 107 + .../nimble/porting/examples/linux_blemesh/ble.c | 442 ++ .../examples/linux_blemesh/include/logcfg/logcfg.h | 151 + .../examples/linux_blemesh/include/syscfg/syscfg.h | 1779 +++++ .../linux_blemesh/include/sysflash/sysflash.h | 24 + .../nimble/porting/examples/linux_blemesh/main.c | 100 + .../nimble/nimble/porting/examples/nuttx/Make.defs | 58 + .../nimble/nimble/porting/examples/nuttx/ble.c | 129 + .../porting/examples/nuttx/include/logcfg/logcfg.h | 25 + .../porting/examples/nuttx/include/syscfg/syscfg.h | 1206 +++ .../examples/nuttx/include/sysflash/sysflash.h | 24 + .../nimble/nimble/porting/examples/nuttx/main.c | 130 + .../nimble/porting/nimble/Makefile.controller | 32 + .../nimble/nimble/porting/nimble/Makefile.defs | 75 + .../nimble/nimble/porting/nimble/Makefile.mesh | 24 + .../nimble/porting/nimble/Makefile.tinycrypt | 26 + .../nimble/porting/nimble/include/hal/hal_gpio.h | 170 + .../porting/nimble/include/hal/hal_os_tick.h | 53 + .../nimble/porting/nimble/include/hal/hal_system.h | 96 + .../nimble/porting/nimble/include/hal/hal_timer.h | 173 + .../nimble/porting/nimble/include/hal/hal_uart.h | 141 + .../porting/nimble/include/hal/hal_watchdog.h | 55 + .../nimble/nimble/porting/nimble/include/log/log.h | 34 + .../porting/nimble/include/log_common/ignore.h | 64 + .../porting/nimble/include/log_common/log_common.h | 145 + .../nimble/porting/nimble/include/logcfg/logcfg.h | 169 + .../nimble/nimble/porting/nimble/include/mem/mem.h | 107 + .../nimble/porting/nimble/include/modlog/modlog.h | 102 + .../porting/nimble/include/nimble/nimble_port.h | 83 + .../porting/nimble/include/nimble/storage_port.h | 47 + .../nimble/porting/nimble/include/os/endian.h | 289 + .../nimble/nimble/porting/nimble/include/os/os.h | 59 + .../nimble/porting/nimble/include/os/os_cputime.h | 240 + .../nimble/porting/nimble/include/os/os_error.h | 62 + .../nimble/porting/nimble/include/os/os_mbuf.h | 1145 +++ .../nimble/porting/nimble/include/os/os_mempool.h | 414 ++ .../porting/nimble/include/os/os_trace_api.h | 277 + .../nimble/porting/nimble/include/os/queue.h | 218 + .../nimble/nimble/porting/nimble/include/os/util.h | 38 + .../nimble/porting/nimble/include/stats/stats.h | 80 + .../nimble/porting/nimble/include/syscfg/syscfg.h | 1521 ++++ .../porting/nimble/include/sysflash/sysflash.h | 24 + .../porting/nimble/include/sysinit/sysinit.h | 38 + lib/bt/host/nimble/nimble/porting/nimble/pkg.yml | 43 + .../host/nimble/nimble/porting/nimble/src/endian.c | 268 + .../nimble/nimble/porting/nimble/src/hal_timer.c | 953 +++ .../nimble/nimble/porting/nimble/src/hal_uart.c | 171 + lib/bt/host/nimble/nimble/porting/nimble/src/mem.c | 326 + .../nimble/nimble/porting/nimble/src/nimble_port.c | 291 + .../nimble/nimble/porting/nimble/src/os_cputime.c | 126 + .../nimble/porting/nimble/src/os_cputime_pwr2.c | 95 + .../nimble/nimble/porting/nimble/src/os_mbuf.c | 1253 ++++ .../nimble/nimble/porting/nimble/src/os_mempool.c | 486 ++ .../nimble/porting/nimble/src/os_msys_init.c | 209 + .../npl/dummy/include/nimble/nimble_npl_os.h | 60 + .../nimble/porting/npl/dummy/src/npl_os_dummy.c | 200 + .../npl/freertos/include/nimble/nimble_npl_os.h | 392 + .../freertos/include/nimble/nimble_port_freertos.h | 56 + .../npl/freertos/include/nimble/npl_freertos.h | 120 + .../npl/freertos/src/nimble_port_freertos.c | 83 + .../porting/npl/freertos/src/npl_os_freertos.c | 1269 ++++ .../porting/npl/linux/include/console/console.h | 35 + .../npl/linux/include/nimble/nimble_npl_os.h | 51 + .../porting/npl/linux/include/nimble/os_types.h | 87 + .../nimble/porting/npl/linux/src/os_atomic.c | 36 + .../nimble/porting/npl/linux/src/os_callout.c | 158 + .../nimble/porting/npl/linux/src/os_eventq.cc | 157 + .../nimble/nimble/porting/npl/linux/src/os_mutex.c | 81 + .../nimble/nimble/porting/npl/linux/src/os_sem.c | 93 + .../nimble/nimble/porting/npl/linux/src/os_task.c | 114 + .../nimble/nimble/porting/npl/linux/src/os_time.c | 80 + .../nimble/nimble/porting/npl/linux/src/wqueue.h | 89 + .../nimble/nimble/porting/npl/linux/test/Makefile | 120 + .../porting/npl/linux/test/test_npl_callout.c | 116 + .../porting/npl/linux/test/test_npl_eventq.c | 131 + .../porting/npl/linux/test/test_npl_mempool.c | 111 + .../nimble/porting/npl/linux/test/test_npl_sem.c | 155 + .../nimble/porting/npl/linux/test/test_npl_task.c | 98 + .../nimble/porting/npl/linux/test/test_util.h | 56 + .../npl/mynewt/include/nimble/nimble_npl_os.h | 293 + .../host/nimble/nimble/porting/npl/mynewt/pkg.yml | 27 + .../porting/npl/nuttx/include/console/console.h | 36 + .../porting/npl/nuttx/include/modlog/modlog.h | 68 + .../npl/nuttx/include/nimble/nimble_npl_os.h | 71 + .../porting/npl/nuttx/include/nimble/os_types.h | 90 + .../nimble/porting/npl/nuttx/src/os_atomic.c | 39 + .../nimble/porting/npl/nuttx/src/os_callout.c | 209 + .../nimble/porting/npl/nuttx/src/os_eventq.c | 182 + .../nimble/nimble/porting/npl/nuttx/src/os_mutex.c | 82 + .../nimble/nimble/porting/npl/nuttx/src/os_sem.c | 100 + .../nimble/nimble/porting/npl/nuttx/src/os_task.c | 126 + .../nimble/nimble/porting/npl/nuttx/src/os_time.c | 85 + .../nimble/nimble/porting/npl/nuttx/src/wqueue.h | 104 + .../nimble/nimble/porting/npl/nuttx/test/Make.defs | 58 + .../porting/npl/nuttx/test/test_npl_callout.c | 116 + .../porting/npl/nuttx/test/test_npl_eventq.c | 131 + .../porting/npl/nuttx/test/test_npl_mempool.c | 111 + .../nimble/porting/npl/nuttx/test/test_npl_sem.c | 155 + .../nimble/porting/npl/nuttx/test/test_npl_task.c | 98 + .../nimble/porting/npl/nuttx/test/test_util.h | 56 + .../porting/npl/riot/include/logcfg/logcfg.h | 25 + .../npl/riot/include/nimble/nimble_npl_os.h | 297 + .../npl/riot/include/npl_syscfg/npl_sycfg.h | 1 + .../porting/npl/riot/include/syscfg/syscfg.h | 1803 +++++ .../porting/npl/riot/include/sysflash/sysflash.h | 24 + .../nimble/porting/npl/riot/src/npl_os_riot.c | 69 + .../nimble/nimble/porting/npl/riot/src/nrf5x_isr.c | 58 + .../nimble/porting/targets/dummy_app/pkg.yml | 24 + .../nimble/porting/targets/dummy_app/src/dummy.c | 26 + .../nimble/nimble/porting/targets/linux/pkg.yml | 40 + .../nimble/nimble/porting/targets/linux/syscfg.yml | 26 + .../nimble/nimble/porting/targets/linux/target.yml | 20 + .../nimble/porting/targets/linux_blemesh/pkg.yml | 40 + .../porting/targets/linux_blemesh/syscfg.yml | 52 + .../porting/targets/linux_blemesh/target.yml | 21 + .../nimble/nimble/porting/targets/nuttx/pkg.yml | 40 + .../nimble/nimble/porting/targets/nuttx/syscfg.yml | 28 + .../nimble/nimble/porting/targets/nuttx/target.yml | 20 + .../nimble/porting/targets/porting_default/pkg.yml | 40 + .../porting/targets/porting_default/syscfg.yml | 22 + .../porting/targets/porting_default/target.yml | 21 + .../nimble/nimble/porting/targets/riot/pkg.yml | 34 + .../nimble/nimble/porting/targets/riot/syscfg.yml | 36 + .../nimble/nimble/porting/targets/riot/target.yml | 21 + .../nimble/porting/update_generated_files.sh | 42 + lib/bt/host/nimble/nimble/repository.yml | 56 + lib/bt/host/nimble/nimble/sbom.yml | 5 + .../host/nimble/nimble/targets/dialog_cmac/pkg.yml | 24 + .../nimble/nimble/targets/dialog_cmac/syscfg.yml | 35 + .../nimble/nimble/targets/dialog_cmac/target.yml | 22 + .../targets/nordic_pca10056-blehci-usb/pkg.yml | 27 + .../targets/nordic_pca10056-blehci-usb/syscfg.yml | 24 + .../targets/nordic_pca10056-blehci-usb/target.yml | 21 + .../targets/nordic_pca10095_net-blehci/pkg.yml | 24 + .../targets/nordic_pca10095_net-blehci/syscfg.yml | 42 + .../targets/nordic_pca10095_net-blehci/target.yml | 22 + lib/bt/host/nimble/nimble/targets/unittest/pkg.yml | 27 + .../host/nimble/nimble/targets/unittest/target.yml | 23 + lib/bt/host/nimble/nimble/uncrustify.cfg | 1959 +++++ lib/bt/host/nimble/nimble/version.yml | 22 + lib/bt/host/nimble/port/include/console/console.h | 14 + lib/bt/host/nimble/port/include/esp_nimble_cfg.h | 1829 +++++ lib/bt/host/nimble/port/include/esp_nimble_mem.h | 27 + lib/bt/host/nimble/port/src/nvs_port.c | 40 + 1346 files changed, 581796 insertions(+) create mode 100644 lib/bt/host/bluedroid/Kconfig.in create mode 100644 lib/bt/host/bluedroid/api/esp_a2dp_api.c create mode 100644 lib/bt/host/bluedroid/api/esp_avrc_api.c create mode 100644 lib/bt/host/bluedroid/api/esp_bluedroid_hci.c create mode 100644 lib/bt/host/bluedroid/api/esp_bt_device.c create mode 100644 lib/bt/host/bluedroid/api/esp_bt_main.c create mode 100644 lib/bt/host/bluedroid/api/esp_gap_ble_api.c create mode 100644 lib/bt/host/bluedroid/api/esp_gap_bt_api.c create mode 100644 lib/bt/host/bluedroid/api/esp_gatt_common_api.c create mode 100644 lib/bt/host/bluedroid/api/esp_gattc_api.c create mode 100644 lib/bt/host/bluedroid/api/esp_gatts_api.c create mode 100644 lib/bt/host/bluedroid/api/esp_hf_ag_api.c create mode 100644 lib/bt/host/bluedroid/api/esp_hf_client_api.c create mode 100644 lib/bt/host/bluedroid/api/esp_hidd_api.c create mode 100644 lib/bt/host/bluedroid/api/esp_hidh_api.c create mode 100644 lib/bt/host/bluedroid/api/esp_l2cap_bt_api.c create mode 100644 lib/bt/host/bluedroid/api/esp_sdp_api.c create mode 100644 lib/bt/host/bluedroid/api/esp_spp_api.c create mode 100644 lib/bt/host/bluedroid/api/include/api/esp_a2dp_api.h create mode 100644 lib/bt/host/bluedroid/api/include/api/esp_avrc_api.h create mode 100644 lib/bt/host/bluedroid/api/include/api/esp_bluedroid_hci.h create mode 100644 lib/bt/host/bluedroid/api/include/api/esp_bt_defs.h create mode 100644 lib/bt/host/bluedroid/api/include/api/esp_bt_device.h create mode 100644 lib/bt/host/bluedroid/api/include/api/esp_bt_main.h create mode 100644 lib/bt/host/bluedroid/api/include/api/esp_gap_ble_api.h create mode 100644 lib/bt/host/bluedroid/api/include/api/esp_gap_bt_api.h create mode 100644 lib/bt/host/bluedroid/api/include/api/esp_gatt_common_api.h create mode 100644 lib/bt/host/bluedroid/api/include/api/esp_gatt_defs.h create mode 100644 lib/bt/host/bluedroid/api/include/api/esp_gattc_api.h create mode 100644 lib/bt/host/bluedroid/api/include/api/esp_gatts_api.h create mode 100644 lib/bt/host/bluedroid/api/include/api/esp_hf_ag_api.h create mode 100644 lib/bt/host/bluedroid/api/include/api/esp_hf_client_api.h create mode 100644 lib/bt/host/bluedroid/api/include/api/esp_hf_defs.h create mode 100644 lib/bt/host/bluedroid/api/include/api/esp_hidd_api.h create mode 100644 lib/bt/host/bluedroid/api/include/api/esp_hidh_api.h create mode 100644 lib/bt/host/bluedroid/api/include/api/esp_l2cap_bt_api.h create mode 100644 lib/bt/host/bluedroid/api/include/api/esp_sdp_api.h create mode 100644 lib/bt/host/bluedroid/api/include/api/esp_spp_api.h create mode 100644 lib/bt/host/bluedroid/bta/ar/bta_ar.c create mode 100644 lib/bt/host/bluedroid/bta/ar/include/bta_ar_int.h create mode 100644 lib/bt/host/bluedroid/bta/av/bta_av_aact.c create mode 100644 lib/bt/host/bluedroid/bta/av/bta_av_act.c create mode 100644 lib/bt/host/bluedroid/bta/av/bta_av_api.c create mode 100644 lib/bt/host/bluedroid/bta/av/bta_av_cfg.c create mode 100644 lib/bt/host/bluedroid/bta/av/bta_av_ci.c create mode 100644 lib/bt/host/bluedroid/bta/av/bta_av_main.c create mode 100644 lib/bt/host/bluedroid/bta/av/bta_av_sbc.c create mode 100644 lib/bt/host/bluedroid/bta/av/bta_av_ssm.c create mode 100644 lib/bt/host/bluedroid/bta/av/include/bta_av_int.h create mode 100644 lib/bt/host/bluedroid/bta/dm/bta_dm_act.c create mode 100644 lib/bt/host/bluedroid/bta/dm/bta_dm_api.c create mode 100644 lib/bt/host/bluedroid/bta/dm/bta_dm_cfg.c create mode 100644 lib/bt/host/bluedroid/bta/dm/bta_dm_ci.c create mode 100644 lib/bt/host/bluedroid/bta/dm/bta_dm_co.c create mode 100644 lib/bt/host/bluedroid/bta/dm/bta_dm_main.c create mode 100644 lib/bt/host/bluedroid/bta/dm/bta_dm_pm.c create mode 100644 lib/bt/host/bluedroid/bta/dm/bta_dm_qos.c create mode 100644 lib/bt/host/bluedroid/bta/dm/bta_dm_sco.c create mode 100644 lib/bt/host/bluedroid/bta/dm/include/bta_dm_int.h create mode 100644 lib/bt/host/bluedroid/bta/gatt/bta_gatt_common.c create mode 100644 lib/bt/host/bluedroid/bta/gatt/bta_gattc_act.c create mode 100644 lib/bt/host/bluedroid/bta/gatt/bta_gattc_api.c create mode 100644 lib/bt/host/bluedroid/bta/gatt/bta_gattc_cache.c create mode 100644 lib/bt/host/bluedroid/bta/gatt/bta_gattc_ci.c create mode 100644 lib/bt/host/bluedroid/bta/gatt/bta_gattc_co.c create mode 100644 lib/bt/host/bluedroid/bta/gatt/bta_gattc_main.c create mode 100644 lib/bt/host/bluedroid/bta/gatt/bta_gattc_utils.c create mode 100644 lib/bt/host/bluedroid/bta/gatt/bta_gatts_act.c create mode 100644 lib/bt/host/bluedroid/bta/gatt/bta_gatts_api.c create mode 100644 lib/bt/host/bluedroid/bta/gatt/bta_gatts_co.c create mode 100644 lib/bt/host/bluedroid/bta/gatt/bta_gatts_main.c create mode 100644 lib/bt/host/bluedroid/bta/gatt/bta_gatts_utils.c create mode 100644 lib/bt/host/bluedroid/bta/gatt/include/bta_gattc_int.h create mode 100644 lib/bt/host/bluedroid/bta/gatt/include/bta_gatts_int.h create mode 100644 lib/bt/host/bluedroid/bta/hd/bta_hd_act.c create mode 100644 lib/bt/host/bluedroid/bta/hd/bta_hd_api.c create mode 100644 lib/bt/host/bluedroid/bta/hd/bta_hd_main.c create mode 100644 lib/bt/host/bluedroid/bta/hd/include/bta_hd_int.h create mode 100644 lib/bt/host/bluedroid/bta/hf_ag/bta_ag_act.c create mode 100644 lib/bt/host/bluedroid/bta/hf_ag/bta_ag_api.c create mode 100644 lib/bt/host/bluedroid/bta/hf_ag/bta_ag_at.c create mode 100644 lib/bt/host/bluedroid/bta/hf_ag/bta_ag_cfg.c create mode 100644 lib/bt/host/bluedroid/bta/hf_ag/bta_ag_cmd.c create mode 100644 lib/bt/host/bluedroid/bta/hf_ag/bta_ag_main.c create mode 100644 lib/bt/host/bluedroid/bta/hf_ag/bta_ag_rfc.c create mode 100644 lib/bt/host/bluedroid/bta/hf_ag/bta_ag_sco.c create mode 100644 lib/bt/host/bluedroid/bta/hf_ag/bta_ag_sdp.c create mode 100644 lib/bt/host/bluedroid/bta/hf_ag/include/bta_ag_at.h create mode 100644 lib/bt/host/bluedroid/bta/hf_ag/include/bta_ag_int.h create mode 100644 lib/bt/host/bluedroid/bta/hf_client/bta_hf_client_act.c create mode 100644 lib/bt/host/bluedroid/bta/hf_client/bta_hf_client_api.c create mode 100644 lib/bt/host/bluedroid/bta/hf_client/bta_hf_client_at.c create mode 100644 lib/bt/host/bluedroid/bta/hf_client/bta_hf_client_cmd.c create mode 100644 lib/bt/host/bluedroid/bta/hf_client/bta_hf_client_main.c create mode 100644 lib/bt/host/bluedroid/bta/hf_client/bta_hf_client_rfc.c create mode 100644 lib/bt/host/bluedroid/bta/hf_client/bta_hf_client_sco.c create mode 100644 lib/bt/host/bluedroid/bta/hf_client/bta_hf_client_sdp.c create mode 100644 lib/bt/host/bluedroid/bta/hf_client/include/bta_hf_client_at.h create mode 100644 lib/bt/host/bluedroid/bta/hf_client/include/bta_hf_client_int.h create mode 100644 lib/bt/host/bluedroid/bta/hh/bta_hh_act.c create mode 100644 lib/bt/host/bluedroid/bta/hh/bta_hh_api.c create mode 100644 lib/bt/host/bluedroid/bta/hh/bta_hh_cfg.c create mode 100644 lib/bt/host/bluedroid/bta/hh/bta_hh_le.c create mode 100644 lib/bt/host/bluedroid/bta/hh/bta_hh_main.c create mode 100644 lib/bt/host/bluedroid/bta/hh/bta_hh_utils.c create mode 100644 lib/bt/host/bluedroid/bta/hh/include/bta_hh_int.h create mode 100644 lib/bt/host/bluedroid/bta/include/bta/bta_ag_api.h create mode 100644 lib/bt/host/bluedroid/bta/include/bta/bta_ag_co.h create mode 100644 lib/bt/host/bluedroid/bta/include/bta/bta_api.h create mode 100644 lib/bt/host/bluedroid/bta/include/bta/bta_ar_api.h create mode 100644 lib/bt/host/bluedroid/bta/include/bta/bta_av_api.h create mode 100644 lib/bt/host/bluedroid/bta/include/bta/bta_av_ci.h create mode 100644 lib/bt/host/bluedroid/bta/include/bta/bta_av_co.h create mode 100644 lib/bt/host/bluedroid/bta/include/bta/bta_av_sbc.h create mode 100644 lib/bt/host/bluedroid/bta/include/bta/bta_dm_ci.h create mode 100644 lib/bt/host/bluedroid/bta/include/bta/bta_dm_co.h create mode 100644 lib/bt/host/bluedroid/bta/include/bta/bta_gap_bt_co.h create mode 100644 lib/bt/host/bluedroid/bta/include/bta/bta_gatt_api.h create mode 100644 lib/bt/host/bluedroid/bta/include/bta/bta_gatt_common.h create mode 100644 lib/bt/host/bluedroid/bta/include/bta/bta_gattc_ci.h create mode 100644 lib/bt/host/bluedroid/bta/include/bta/bta_gattc_co.h create mode 100644 lib/bt/host/bluedroid/bta/include/bta/bta_gatts_co.h create mode 100644 lib/bt/host/bluedroid/bta/include/bta/bta_hd_api.h create mode 100644 lib/bt/host/bluedroid/bta/include/bta/bta_hf_client_api.h create mode 100644 lib/bt/host/bluedroid/bta/include/bta/bta_hf_client_co.h create mode 100644 lib/bt/host/bluedroid/bta/include/bta/bta_hfp_defs.h create mode 100644 lib/bt/host/bluedroid/bta/include/bta/bta_hh_api.h create mode 100644 lib/bt/host/bluedroid/bta/include/bta/bta_hh_co.h create mode 100644 lib/bt/host/bluedroid/bta/include/bta/bta_jv_api.h create mode 100644 lib/bt/host/bluedroid/bta/include/bta/bta_jv_co.h create mode 100644 lib/bt/host/bluedroid/bta/include/bta/bta_sdp_api.h create mode 100644 lib/bt/host/bluedroid/bta/include/bta/bta_sys.h create mode 100644 lib/bt/host/bluedroid/bta/include/bta/utl.h create mode 100644 lib/bt/host/bluedroid/bta/jv/bta_jv_act.c create mode 100644 lib/bt/host/bluedroid/bta/jv/bta_jv_api.c create mode 100644 lib/bt/host/bluedroid/bta/jv/bta_jv_cfg.c create mode 100644 lib/bt/host/bluedroid/bta/jv/bta_jv_main.c create mode 100644 lib/bt/host/bluedroid/bta/jv/include/bta_jv_int.h create mode 100644 lib/bt/host/bluedroid/bta/sdp/bta_sdp.c create mode 100644 lib/bt/host/bluedroid/bta/sdp/bta_sdp_act.c create mode 100644 lib/bt/host/bluedroid/bta/sdp/bta_sdp_api.c create mode 100644 lib/bt/host/bluedroid/bta/sdp/bta_sdp_cfg.c create mode 100644 lib/bt/host/bluedroid/bta/sdp/include/bta_sdp_int.h create mode 100644 lib/bt/host/bluedroid/bta/sys/bta_sys_conn.c create mode 100644 lib/bt/host/bluedroid/bta/sys/bta_sys_main.c create mode 100644 lib/bt/host/bluedroid/bta/sys/include/bta_sys_int.h create mode 100644 lib/bt/host/bluedroid/bta/sys/utl.c create mode 100644 lib/bt/host/bluedroid/btc/core/btc_ble_storage.c create mode 100644 lib/bt/host/bluedroid/btc/core/btc_config.c create mode 100644 lib/bt/host/bluedroid/btc/core/btc_dev.c create mode 100644 lib/bt/host/bluedroid/btc/core/btc_dm.c create mode 100644 lib/bt/host/bluedroid/btc/core/btc_main.c create mode 100644 lib/bt/host/bluedroid/btc/core/btc_profile_queue.c create mode 100644 lib/bt/host/bluedroid/btc/core/btc_sec.c create mode 100644 lib/bt/host/bluedroid/btc/core/btc_sm.c create mode 100644 lib/bt/host/bluedroid/btc/core/btc_storage.c create mode 100644 lib/bt/host/bluedroid/btc/core/btc_util.c create mode 100644 lib/bt/host/bluedroid/btc/include/btc/btc_ble_storage.h create mode 100644 lib/bt/host/bluedroid/btc/include/btc/btc_common.h create mode 100644 lib/bt/host/bluedroid/btc/include/btc/btc_config.h create mode 100644 lib/bt/host/bluedroid/btc/include/btc/btc_dev.h create mode 100644 lib/bt/host/bluedroid/btc/include/btc/btc_dm.h create mode 100644 lib/bt/host/bluedroid/btc/include/btc/btc_main.h create mode 100644 lib/bt/host/bluedroid/btc/include/btc/btc_profile_queue.h create mode 100644 lib/bt/host/bluedroid/btc/include/btc/btc_sm.h create mode 100644 lib/bt/host/bluedroid/btc/include/btc/btc_storage.h create mode 100644 lib/bt/host/bluedroid/btc/include/btc/btc_util.h create mode 100644 lib/bt/host/bluedroid/btc/profile/esp/ble_button/button_pro.c create mode 100644 lib/bt/host/bluedroid/btc/profile/esp/include/button_pro.h create mode 100644 lib/bt/host/bluedroid/btc/profile/esp/include/wx_airsync_prf.h create mode 100644 lib/bt/host/bluedroid/btc/profile/esp/wechat_AirSync/wx_airsync_prf.c create mode 100644 lib/bt/host/bluedroid/btc/profile/std/a2dp/bta_av_co.c create mode 100644 lib/bt/host/bluedroid/btc/profile/std/a2dp/btc_a2dp.c create mode 100644 lib/bt/host/bluedroid/btc/profile/std/a2dp/btc_a2dp_control.c create mode 100644 lib/bt/host/bluedroid/btc/profile/std/a2dp/btc_a2dp_sink.c create mode 100644 lib/bt/host/bluedroid/btc/profile/std/a2dp/btc_a2dp_source.c create mode 100644 lib/bt/host/bluedroid/btc/profile/std/a2dp/btc_av.c create mode 100644 lib/bt/host/bluedroid/btc/profile/std/a2dp/include/btc_av_co.h create mode 100644 lib/bt/host/bluedroid/btc/profile/std/avrc/bta_avrc_co.c create mode 100644 lib/bt/host/bluedroid/btc/profile/std/avrc/btc_avrc.c create mode 100644 lib/bt/host/bluedroid/btc/profile/std/battery/battery_prf.c create mode 100644 lib/bt/host/bluedroid/btc/profile/std/battery/include/srvc_battery_int.h create mode 100644 lib/bt/host/bluedroid/btc/profile/std/dis/dis_profile.c create mode 100644 lib/bt/host/bluedroid/btc/profile/std/dis/include/srvc_dis_int.h create mode 100644 lib/bt/host/bluedroid/btc/profile/std/gap/bta_gap_bt_co.c create mode 100644 lib/bt/host/bluedroid/btc/profile/std/gap/btc_gap_ble.c create mode 100644 lib/bt/host/bluedroid/btc/profile/std/gap/btc_gap_bt.c create mode 100644 lib/bt/host/bluedroid/btc/profile/std/gatt/btc_gatt_common.c create mode 100644 lib/bt/host/bluedroid/btc/profile/std/gatt/btc_gatt_util.c create mode 100644 lib/bt/host/bluedroid/btc/profile/std/gatt/btc_gattc.c create mode 100644 lib/bt/host/bluedroid/btc/profile/std/gatt/btc_gatts.c create mode 100644 lib/bt/host/bluedroid/btc/profile/std/hf_ag/bta_ag_co.c create mode 100644 lib/bt/host/bluedroid/btc/profile/std/hf_ag/btc_hf_ag.c create mode 100644 lib/bt/host/bluedroid/btc/profile/std/hf_client/bta_hf_client_co.c create mode 100644 lib/bt/host/bluedroid/btc/profile/std/hf_client/btc_hf_client.c create mode 100644 lib/bt/host/bluedroid/btc/profile/std/hid/bta_hh_co.c create mode 100644 lib/bt/host/bluedroid/btc/profile/std/hid/btc_hd.c create mode 100644 lib/bt/host/bluedroid/btc/profile/std/hid/btc_hh.c create mode 100644 lib/bt/host/bluedroid/btc/profile/std/include/bt_sdp.h create mode 100644 lib/bt/host/bluedroid/btc/profile/std/include/btc_a2dp.h create mode 100644 lib/bt/host/bluedroid/btc/profile/std/include/btc_a2dp_control.h create mode 100644 lib/bt/host/bluedroid/btc/profile/std/include/btc_a2dp_sink.h create mode 100644 lib/bt/host/bluedroid/btc/profile/std/include/btc_a2dp_source.h create mode 100644 lib/bt/host/bluedroid/btc/profile/std/include/btc_av.h create mode 100644 lib/bt/host/bluedroid/btc/profile/std/include/btc_av_api.h create mode 100644 lib/bt/host/bluedroid/btc/profile/std/include/btc_avrc.h create mode 100644 lib/bt/host/bluedroid/btc/profile/std/include/btc_gap_ble.h create mode 100644 lib/bt/host/bluedroid/btc/profile/std/include/btc_gap_bt.h create mode 100644 lib/bt/host/bluedroid/btc/profile/std/include/btc_gatt_common.h create mode 100644 lib/bt/host/bluedroid/btc/profile/std/include/btc_gatt_util.h create mode 100644 lib/bt/host/bluedroid/btc/profile/std/include/btc_gattc.h create mode 100644 lib/bt/host/bluedroid/btc/profile/std/include/btc_gatts.h create mode 100644 lib/bt/host/bluedroid/btc/profile/std/include/btc_hd.h create mode 100644 lib/bt/host/bluedroid/btc/profile/std/include/btc_hf_ag.h create mode 100644 lib/bt/host/bluedroid/btc/profile/std/include/btc_hf_client.h create mode 100644 lib/bt/host/bluedroid/btc/profile/std/include/btc_hh.h create mode 100644 lib/bt/host/bluedroid/btc/profile/std/include/btc_l2cap.h create mode 100644 lib/bt/host/bluedroid/btc/profile/std/include/btc_sdp.h create mode 100644 lib/bt/host/bluedroid/btc/profile/std/include/btc_spp.h create mode 100644 lib/bt/host/bluedroid/btc/profile/std/include/dis_api.h create mode 100644 lib/bt/host/bluedroid/btc/profile/std/include/srvc_api.h create mode 100644 lib/bt/host/bluedroid/btc/profile/std/l2cap/btc_l2cap.c create mode 100644 lib/bt/host/bluedroid/btc/profile/std/sdp/btc_sdp.c create mode 100644 lib/bt/host/bluedroid/btc/profile/std/smp/esp_app_sec.c create mode 100644 lib/bt/host/bluedroid/btc/profile/std/smp/include/esp_sec_api.h create mode 100644 lib/bt/host/bluedroid/btc/profile/std/spp/btc_spp.c create mode 100644 lib/bt/host/bluedroid/common/include/common/bluedroid_user_config.h create mode 100644 lib/bt/host/bluedroid/common/include/common/bt_common_types.h create mode 100644 lib/bt/host/bluedroid/common/include/common/bt_defs.h create mode 100644 lib/bt/host/bluedroid/common/include/common/bt_target.h create mode 100644 lib/bt/host/bluedroid/common/include/common/bt_trace.h create mode 100644 lib/bt/host/bluedroid/common/include/common/bt_vendor_lib.h create mode 100644 lib/bt/host/bluedroid/common/include/common/bte.h create mode 100644 lib/bt/host/bluedroid/common/include/common/bte_appl.h create mode 100644 lib/bt/host/bluedroid/config/include/config/stack_config.h create mode 100644 lib/bt/host/bluedroid/config/stack_config.c create mode 100644 lib/bt/host/bluedroid/device/bdaddr.c create mode 100644 lib/bt/host/bluedroid/device/controller.c create mode 100644 lib/bt/host/bluedroid/device/include/device/bdaddr.h create mode 100644 lib/bt/host/bluedroid/device/include/device/controller.h create mode 100644 lib/bt/host/bluedroid/device/include/device/device_features.h create mode 100644 lib/bt/host/bluedroid/device/include/device/event_mask.h create mode 100644 lib/bt/host/bluedroid/device/include/device/interop.h create mode 100644 lib/bt/host/bluedroid/device/include/device/interop_database.h create mode 100644 lib/bt/host/bluedroid/device/include/device/version.h create mode 100644 lib/bt/host/bluedroid/device/interop.c create mode 100644 lib/bt/host/bluedroid/external/sbc/decoder/include/oi_assert.h create mode 100644 lib/bt/host/bluedroid/external/sbc/decoder/include/oi_bitstream.h create mode 100644 lib/bt/host/bluedroid/external/sbc/decoder/include/oi_bt_spec.h create mode 100644 lib/bt/host/bluedroid/external/sbc/decoder/include/oi_codec_sbc.h create mode 100644 lib/bt/host/bluedroid/external/sbc/decoder/include/oi_codec_sbc_private.h create mode 100644 lib/bt/host/bluedroid/external/sbc/decoder/include/oi_common.h create mode 100644 lib/bt/host/bluedroid/external/sbc/decoder/include/oi_cpu_dep.h create mode 100644 lib/bt/host/bluedroid/external/sbc/decoder/include/oi_modules.h create mode 100644 lib/bt/host/bluedroid/external/sbc/decoder/include/oi_osinterface.h create mode 100644 lib/bt/host/bluedroid/external/sbc/decoder/include/oi_status.h create mode 100644 lib/bt/host/bluedroid/external/sbc/decoder/include/oi_stddefs.h create mode 100644 lib/bt/host/bluedroid/external/sbc/decoder/include/oi_string.h create mode 100644 lib/bt/host/bluedroid/external/sbc/decoder/include/oi_time.h create mode 100644 lib/bt/host/bluedroid/external/sbc/decoder/include/oi_utils.h create mode 100644 lib/bt/host/bluedroid/external/sbc/decoder/srce/alloc.c create mode 100644 lib/bt/host/bluedroid/external/sbc/decoder/srce/bitalloc-sbc.c create mode 100644 lib/bt/host/bluedroid/external/sbc/decoder/srce/bitalloc.c create mode 100644 lib/bt/host/bluedroid/external/sbc/decoder/srce/bitstream-decode.c create mode 100644 lib/bt/host/bluedroid/external/sbc/decoder/srce/decoder-oina.c create mode 100644 lib/bt/host/bluedroid/external/sbc/decoder/srce/decoder-private.c create mode 100644 lib/bt/host/bluedroid/external/sbc/decoder/srce/decoder-sbc.c create mode 100644 lib/bt/host/bluedroid/external/sbc/decoder/srce/dequant.c create mode 100644 lib/bt/host/bluedroid/external/sbc/decoder/srce/framing-sbc.c create mode 100644 lib/bt/host/bluedroid/external/sbc/decoder/srce/framing.c create mode 100644 lib/bt/host/bluedroid/external/sbc/decoder/srce/oi_codec_version.c create mode 100644 lib/bt/host/bluedroid/external/sbc/decoder/srce/readsamplesjoint.inc create mode 100644 lib/bt/host/bluedroid/external/sbc/decoder/srce/synthesis-8-generated.c create mode 100644 lib/bt/host/bluedroid/external/sbc/decoder/srce/synthesis-dct8.c create mode 100644 lib/bt/host/bluedroid/external/sbc/decoder/srce/synthesis-sbc.c create mode 100644 lib/bt/host/bluedroid/external/sbc/encoder/include/sbc_dct.h create mode 100644 lib/bt/host/bluedroid/external/sbc/encoder/include/sbc_enc_func_declare.h create mode 100644 lib/bt/host/bluedroid/external/sbc/encoder/include/sbc_encoder.h create mode 100644 lib/bt/host/bluedroid/external/sbc/encoder/include/sbc_if.h create mode 100644 lib/bt/host/bluedroid/external/sbc/encoder/include/sbc_types.h create mode 100644 lib/bt/host/bluedroid/external/sbc/encoder/srce/sbc_analysis.c create mode 100644 lib/bt/host/bluedroid/external/sbc/encoder/srce/sbc_dct.c create mode 100644 lib/bt/host/bluedroid/external/sbc/encoder/srce/sbc_dct_coeffs.c create mode 100644 lib/bt/host/bluedroid/external/sbc/encoder/srce/sbc_enc_bit_alloc_mono.c create mode 100644 lib/bt/host/bluedroid/external/sbc/encoder/srce/sbc_enc_bit_alloc_ste.c create mode 100644 lib/bt/host/bluedroid/external/sbc/encoder/srce/sbc_enc_coeffs.c create mode 100644 lib/bt/host/bluedroid/external/sbc/encoder/srce/sbc_encoder.c create mode 100644 lib/bt/host/bluedroid/external/sbc/encoder/srce/sbc_packing.c create mode 100644 lib/bt/host/bluedroid/external/sbc/plc/include/sbc_plc.h create mode 100644 lib/bt/host/bluedroid/external/sbc/plc/sbc_plc.c create mode 100644 lib/bt/host/bluedroid/hci/hci_audio.c create mode 100644 lib/bt/host/bluedroid/hci/hci_hal_h4.c create mode 100644 lib/bt/host/bluedroid/hci/hci_layer.c create mode 100644 lib/bt/host/bluedroid/hci/hci_packet_factory.c create mode 100644 lib/bt/host/bluedroid/hci/hci_packet_parser.c create mode 100644 lib/bt/host/bluedroid/hci/include/hci/bt_vendor_lib.h create mode 100644 lib/bt/host/bluedroid/hci/include/hci/hci_audio.h create mode 100644 lib/bt/host/bluedroid/hci/include/hci/hci_hal.h create mode 100644 lib/bt/host/bluedroid/hci/include/hci/hci_internals.h create mode 100644 lib/bt/host/bluedroid/hci/include/hci/hci_layer.h create mode 100644 lib/bt/host/bluedroid/hci/include/hci/hci_packet_factory.h create mode 100644 lib/bt/host/bluedroid/hci/include/hci/hci_packet_parser.h create mode 100644 lib/bt/host/bluedroid/hci/include/hci/hci_trans_int.h create mode 100644 lib/bt/host/bluedroid/hci/include/hci/packet_fragmenter.h create mode 100644 lib/bt/host/bluedroid/hci/packet_fragmenter.c create mode 100644 lib/bt/host/bluedroid/main/bte_init.c create mode 100644 lib/bt/host/bluedroid/main/bte_main.c create mode 100644 lib/bt/host/bluedroid/stack/a2dp/a2d_api.c create mode 100644 lib/bt/host/bluedroid/stack/a2dp/a2d_sbc.c create mode 100644 lib/bt/host/bluedroid/stack/a2dp/include/a2d_int.h create mode 100644 lib/bt/host/bluedroid/stack/avct/avct_api.c create mode 100644 lib/bt/host/bluedroid/stack/avct/avct_ccb.c create mode 100644 lib/bt/host/bluedroid/stack/avct/avct_l2c.c create mode 100644 lib/bt/host/bluedroid/stack/avct/avct_lcb.c create mode 100644 lib/bt/host/bluedroid/stack/avct/avct_lcb_act.c create mode 100644 lib/bt/host/bluedroid/stack/avct/include/avct_defs.h create mode 100644 lib/bt/host/bluedroid/stack/avct/include/avct_int.h create mode 100644 lib/bt/host/bluedroid/stack/avdt/avdt_ad.c create mode 100644 lib/bt/host/bluedroid/stack/avdt/avdt_api.c create mode 100644 lib/bt/host/bluedroid/stack/avdt/avdt_ccb.c create mode 100644 lib/bt/host/bluedroid/stack/avdt/avdt_ccb_act.c create mode 100644 lib/bt/host/bluedroid/stack/avdt/avdt_l2c.c create mode 100644 lib/bt/host/bluedroid/stack/avdt/avdt_msg.c create mode 100644 lib/bt/host/bluedroid/stack/avdt/avdt_scb.c create mode 100644 lib/bt/host/bluedroid/stack/avdt/avdt_scb_act.c create mode 100644 lib/bt/host/bluedroid/stack/avdt/include/avdt_defs.h create mode 100644 lib/bt/host/bluedroid/stack/avdt/include/avdt_int.h create mode 100644 lib/bt/host/bluedroid/stack/avrc/avrc_api.c create mode 100644 lib/bt/host/bluedroid/stack/avrc/avrc_bld_ct.c create mode 100644 lib/bt/host/bluedroid/stack/avrc/avrc_bld_tg.c create mode 100644 lib/bt/host/bluedroid/stack/avrc/avrc_opt.c create mode 100644 lib/bt/host/bluedroid/stack/avrc/avrc_pars_ct.c create mode 100644 lib/bt/host/bluedroid/stack/avrc/avrc_pars_tg.c create mode 100644 lib/bt/host/bluedroid/stack/avrc/avrc_sdp.c create mode 100644 lib/bt/host/bluedroid/stack/avrc/avrc_utils.c create mode 100644 lib/bt/host/bluedroid/stack/avrc/include/avrc_int.h create mode 100644 lib/bt/host/bluedroid/stack/btm/btm_acl.c create mode 100644 lib/bt/host/bluedroid/stack/btm/btm_ble.c create mode 100644 lib/bt/host/bluedroid/stack/btm/btm_ble_5_gap.c create mode 100644 lib/bt/host/bluedroid/stack/btm/btm_ble_addr.c create mode 100644 lib/bt/host/bluedroid/stack/btm/btm_ble_adv_filter.c create mode 100644 lib/bt/host/bluedroid/stack/btm/btm_ble_batchscan.c create mode 100644 lib/bt/host/bluedroid/stack/btm/btm_ble_bgconn.c create mode 100644 lib/bt/host/bluedroid/stack/btm/btm_ble_cont_energy.c create mode 100644 lib/bt/host/bluedroid/stack/btm/btm_ble_gap.c create mode 100644 lib/bt/host/bluedroid/stack/btm/btm_ble_multi_adv.c create mode 100644 lib/bt/host/bluedroid/stack/btm/btm_ble_privacy.c create mode 100644 lib/bt/host/bluedroid/stack/btm/btm_dev.c create mode 100644 lib/bt/host/bluedroid/stack/btm/btm_devctl.c create mode 100644 lib/bt/host/bluedroid/stack/btm/btm_inq.c create mode 100644 lib/bt/host/bluedroid/stack/btm/btm_main.c create mode 100644 lib/bt/host/bluedroid/stack/btm/btm_pm.c create mode 100644 lib/bt/host/bluedroid/stack/btm/btm_sco.c create mode 100644 lib/bt/host/bluedroid/stack/btm/btm_sec.c create mode 100644 lib/bt/host/bluedroid/stack/btm/include/btm_ble_int.h create mode 100644 lib/bt/host/bluedroid/stack/btm/include/btm_int.h create mode 100644 lib/bt/host/bluedroid/stack/btu/btu_hcif.c create mode 100644 lib/bt/host/bluedroid/stack/btu/btu_init.c create mode 100644 lib/bt/host/bluedroid/stack/btu/btu_task.c create mode 100644 lib/bt/host/bluedroid/stack/gap/gap_api.c create mode 100644 lib/bt/host/bluedroid/stack/gap/gap_ble.c create mode 100644 lib/bt/host/bluedroid/stack/gap/gap_conn.c create mode 100644 lib/bt/host/bluedroid/stack/gap/gap_utils.c create mode 100644 lib/bt/host/bluedroid/stack/gap/include/gap_int.h create mode 100644 lib/bt/host/bluedroid/stack/gatt/att_protocol.c create mode 100644 lib/bt/host/bluedroid/stack/gatt/gatt_api.c create mode 100644 lib/bt/host/bluedroid/stack/gatt/gatt_attr.c create mode 100644 lib/bt/host/bluedroid/stack/gatt/gatt_auth.c create mode 100644 lib/bt/host/bluedroid/stack/gatt/gatt_cl.c create mode 100644 lib/bt/host/bluedroid/stack/gatt/gatt_db.c create mode 100644 lib/bt/host/bluedroid/stack/gatt/gatt_main.c create mode 100644 lib/bt/host/bluedroid/stack/gatt/gatt_sr.c create mode 100644 lib/bt/host/bluedroid/stack/gatt/gatt_sr_hash.c create mode 100644 lib/bt/host/bluedroid/stack/gatt/gatt_utils.c create mode 100644 lib/bt/host/bluedroid/stack/gatt/include/gatt_int.h create mode 100644 lib/bt/host/bluedroid/stack/hcic/hciblecmds.c create mode 100644 lib/bt/host/bluedroid/stack/hcic/hcicmds.c create mode 100644 lib/bt/host/bluedroid/stack/hid/hidd_api.c create mode 100644 lib/bt/host/bluedroid/stack/hid/hidd_conn.c create mode 100644 lib/bt/host/bluedroid/stack/hid/hidh_api.c create mode 100644 lib/bt/host/bluedroid/stack/hid/hidh_conn.c create mode 100644 lib/bt/host/bluedroid/stack/hid/include/hid_conn.h create mode 100644 lib/bt/host/bluedroid/stack/hid/include/hid_int.h create mode 100644 lib/bt/host/bluedroid/stack/include/stack/a2d_api.h create mode 100644 lib/bt/host/bluedroid/stack/include/stack/a2d_sbc.h create mode 100644 lib/bt/host/bluedroid/stack/include/stack/acl_hci_link_interface.h create mode 100644 lib/bt/host/bluedroid/stack/include/stack/avct_api.h create mode 100644 lib/bt/host/bluedroid/stack/include/stack/avdt_api.h create mode 100644 lib/bt/host/bluedroid/stack/include/stack/avdtc_api.h create mode 100644 lib/bt/host/bluedroid/stack/include/stack/avrc_api.h create mode 100644 lib/bt/host/bluedroid/stack/include/stack/avrc_defs.h create mode 100644 lib/bt/host/bluedroid/stack/include/stack/bt_types.h create mode 100644 lib/bt/host/bluedroid/stack/include/stack/btm_api.h create mode 100644 lib/bt/host/bluedroid/stack/include/stack/btm_ble_api.h create mode 100644 lib/bt/host/bluedroid/stack/include/stack/btu.h create mode 100644 lib/bt/host/bluedroid/stack/include/stack/dyn_mem.h create mode 100644 lib/bt/host/bluedroid/stack/include/stack/gap_api.h create mode 100644 lib/bt/host/bluedroid/stack/include/stack/gatt_api.h create mode 100644 lib/bt/host/bluedroid/stack/include/stack/gattdefs.h create mode 100644 lib/bt/host/bluedroid/stack/include/stack/hcidefs.h create mode 100644 lib/bt/host/bluedroid/stack/include/stack/hcimsgs.h create mode 100644 lib/bt/host/bluedroid/stack/include/stack/hidd_api.h create mode 100644 lib/bt/host/bluedroid/stack/include/stack/hiddefs.h create mode 100644 lib/bt/host/bluedroid/stack/include/stack/hidh_api.h create mode 100644 lib/bt/host/bluedroid/stack/include/stack/l2c_api.h create mode 100644 lib/bt/host/bluedroid/stack/include/stack/l2cap_client.h create mode 100644 lib/bt/host/bluedroid/stack/include/stack/l2cap_hci_link_interface.h create mode 100644 lib/bt/host/bluedroid/stack/include/stack/l2cdefs.h create mode 100644 lib/bt/host/bluedroid/stack/include/stack/port_api.h create mode 100644 lib/bt/host/bluedroid/stack/include/stack/port_ext.h create mode 100644 lib/bt/host/bluedroid/stack/include/stack/profiles_api.h create mode 100644 lib/bt/host/bluedroid/stack/include/stack/rfcdefs.h create mode 100644 lib/bt/host/bluedroid/stack/include/stack/sdp_api.h create mode 100644 lib/bt/host/bluedroid/stack/include/stack/sdpdefs.h create mode 100644 lib/bt/host/bluedroid/stack/include/stack/smp_api.h create mode 100644 lib/bt/host/bluedroid/stack/l2cap/include/l2c_int.h create mode 100644 lib/bt/host/bluedroid/stack/l2cap/l2c_api.c create mode 100644 lib/bt/host/bluedroid/stack/l2cap/l2c_ble.c create mode 100644 lib/bt/host/bluedroid/stack/l2cap/l2c_csm.c create mode 100644 lib/bt/host/bluedroid/stack/l2cap/l2c_fcr.c create mode 100644 lib/bt/host/bluedroid/stack/l2cap/l2c_link.c create mode 100644 lib/bt/host/bluedroid/stack/l2cap/l2c_main.c create mode 100644 lib/bt/host/bluedroid/stack/l2cap/l2c_ucd.c create mode 100644 lib/bt/host/bluedroid/stack/l2cap/l2c_utils.c create mode 100644 lib/bt/host/bluedroid/stack/l2cap/l2cap_client.c create mode 100644 lib/bt/host/bluedroid/stack/rfcomm/include/port_int.h create mode 100644 lib/bt/host/bluedroid/stack/rfcomm/include/rfc_int.h create mode 100644 lib/bt/host/bluedroid/stack/rfcomm/port_api.c create mode 100644 lib/bt/host/bluedroid/stack/rfcomm/port_rfc.c create mode 100644 lib/bt/host/bluedroid/stack/rfcomm/port_utils.c create mode 100644 lib/bt/host/bluedroid/stack/rfcomm/rfc_l2cap_if.c create mode 100644 lib/bt/host/bluedroid/stack/rfcomm/rfc_mx_fsm.c create mode 100644 lib/bt/host/bluedroid/stack/rfcomm/rfc_port_fsm.c create mode 100644 lib/bt/host/bluedroid/stack/rfcomm/rfc_port_if.c create mode 100644 lib/bt/host/bluedroid/stack/rfcomm/rfc_ts_frames.c create mode 100644 lib/bt/host/bluedroid/stack/rfcomm/rfc_utils.c create mode 100644 lib/bt/host/bluedroid/stack/sdp/include/sdpint.h create mode 100644 lib/bt/host/bluedroid/stack/sdp/sdp_api.c create mode 100644 lib/bt/host/bluedroid/stack/sdp/sdp_db.c create mode 100644 lib/bt/host/bluedroid/stack/sdp/sdp_discovery.c create mode 100644 lib/bt/host/bluedroid/stack/sdp/sdp_main.c create mode 100644 lib/bt/host/bluedroid/stack/sdp/sdp_server.c create mode 100644 lib/bt/host/bluedroid/stack/sdp/sdp_utils.c create mode 100644 lib/bt/host/bluedroid/stack/smp/aes.c create mode 100644 lib/bt/host/bluedroid/stack/smp/include/aes.h create mode 100644 lib/bt/host/bluedroid/stack/smp/include/p_256_ecc_pp.h create mode 100644 lib/bt/host/bluedroid/stack/smp/include/p_256_multprecision.h create mode 100644 lib/bt/host/bluedroid/stack/smp/include/smp_int.h create mode 100644 lib/bt/host/bluedroid/stack/smp/p_256_curvepara.c create mode 100644 lib/bt/host/bluedroid/stack/smp/p_256_ecc_pp.c create mode 100644 lib/bt/host/bluedroid/stack/smp/p_256_multprecision.c create mode 100644 lib/bt/host/bluedroid/stack/smp/smp_act.c create mode 100644 lib/bt/host/bluedroid/stack/smp/smp_api.c create mode 100644 lib/bt/host/bluedroid/stack/smp/smp_br_main.c create mode 100644 lib/bt/host/bluedroid/stack/smp/smp_cmac.c create mode 100644 lib/bt/host/bluedroid/stack/smp/smp_keys.c create mode 100644 lib/bt/host/bluedroid/stack/smp/smp_l2c.c create mode 100644 lib/bt/host/bluedroid/stack/smp/smp_main.c create mode 100644 lib/bt/host/bluedroid/stack/smp/smp_utils.c create mode 100644 lib/bt/host/nimble/Kconfig.in create mode 100644 lib/bt/host/nimble/esp-hci/include/esp_nimble_hci.h create mode 100644 lib/bt/host/nimble/esp-hci/src/esp_nimble_hci.c create mode 100644 lib/bt/host/nimble/nimble/.gitignore create mode 100644 lib/bt/host/nimble/nimble/.mailmap create mode 100644 lib/bt/host/nimble/nimble/.rat-excludes create mode 100644 lib/bt/host/nimble/nimble/.style_ignored_dirs create mode 100644 lib/bt/host/nimble/nimble/.travis.yml create mode 100644 lib/bt/host/nimble/nimble/CODING_STANDARDS.md create mode 100644 lib/bt/host/nimble/nimble/LICENSE create mode 100644 lib/bt/host/nimble/nimble/NOTICE create mode 100644 lib/bt/host/nimble/nimble/README.md create mode 100644 lib/bt/host/nimble/nimble/RELEASE_NOTES.md create mode 100644 lib/bt/host/nimble/nimble/apps/README.md create mode 100644 lib/bt/host/nimble/nimble/apps/advertiser/pkg.yml create mode 100644 lib/bt/host/nimble/nimble/apps/advertiser/src/main.c create mode 100644 lib/bt/host/nimble/nimble/apps/advertiser/syscfg.yml create mode 100644 lib/bt/host/nimble/nimble/apps/blecent/pkg.yml create mode 100644 lib/bt/host/nimble/nimble/apps/blecent/src/blecent.h create mode 100644 lib/bt/host/nimble/nimble/apps/blecent/src/main.c create mode 100644 lib/bt/host/nimble/nimble/apps/blecent/src/misc.c create mode 100644 lib/bt/host/nimble/nimble/apps/blecent/src/peer.c create mode 100644 lib/bt/host/nimble/nimble/apps/blecent/syscfg.yml create mode 100644 lib/bt/host/nimble/nimble/apps/blecsc/README.md create mode 100644 lib/bt/host/nimble/nimble/apps/blecsc/pkg.yml create mode 100644 lib/bt/host/nimble/nimble/apps/blecsc/src/blecsc_sens.h create mode 100644 lib/bt/host/nimble/nimble/apps/blecsc/src/gatt_svr.c create mode 100644 lib/bt/host/nimble/nimble/apps/blecsc/src/main.c create mode 100644 lib/bt/host/nimble/nimble/apps/blecsc/syscfg.yml create mode 100644 lib/bt/host/nimble/nimble/apps/blehci/pkg.yml create mode 100644 lib/bt/host/nimble/nimble/apps/blehci/src/main.c create mode 100644 lib/bt/host/nimble/nimble/apps/blehci/syscfg.yml create mode 100644 lib/bt/host/nimble/nimble/apps/blehr/README.md create mode 100644 lib/bt/host/nimble/nimble/apps/blehr/pkg.yml create mode 100644 lib/bt/host/nimble/nimble/apps/blehr/src/blehr_sens.h create mode 100644 lib/bt/host/nimble/nimble/apps/blehr/src/gatt_svr.c create mode 100644 lib/bt/host/nimble/nimble/apps/blehr/src/main.c create mode 100644 lib/bt/host/nimble/nimble/apps/blehr/syscfg.yml create mode 100644 lib/bt/host/nimble/nimble/apps/blemesh/pkg.yml create mode 100644 lib/bt/host/nimble/nimble/apps/blemesh/src/main.c create mode 100644 lib/bt/host/nimble/nimble/apps/blemesh/syscfg.yml create mode 100644 lib/bt/host/nimble/nimble/apps/blemesh_light/pkg.yml create mode 100644 lib/bt/host/nimble/nimble/apps/blemesh_light/src/light_model.c create mode 100644 lib/bt/host/nimble/nimble/apps/blemesh_light/src/light_model.h create mode 100644 lib/bt/host/nimble/nimble/apps/blemesh_light/src/main.c create mode 100644 lib/bt/host/nimble/nimble/apps/blemesh_light/src/ws2812.c create mode 100644 lib/bt/host/nimble/nimble/apps/blemesh_light/src/ws2812.h create mode 100644 lib/bt/host/nimble/nimble/apps/blemesh_light/syscfg.yml create mode 100644 lib/bt/host/nimble/nimble/apps/blemesh_models_example_1/README.md create mode 100644 lib/bt/host/nimble/nimble/apps/blemesh_models_example_1/pkg.yml create mode 100644 lib/bt/host/nimble/nimble/apps/blemesh_models_example_1/src/main.c create mode 100644 lib/bt/host/nimble/nimble/apps/blemesh_models_example_1/syscfg.yml create mode 100644 lib/bt/host/nimble/nimble/apps/blemesh_models_example_2/README.md create mode 100644 lib/bt/host/nimble/nimble/apps/blemesh_models_example_2/pkg.yml create mode 100644 lib/bt/host/nimble/nimble/apps/blemesh_models_example_2/src/app_gpio.c create mode 100644 lib/bt/host/nimble/nimble/apps/blemesh_models_example_2/src/app_gpio.h create mode 100644 lib/bt/host/nimble/nimble/apps/blemesh_models_example_2/src/ble_mesh.c create mode 100644 lib/bt/host/nimble/nimble/apps/blemesh_models_example_2/src/ble_mesh.h create mode 100644 lib/bt/host/nimble/nimble/apps/blemesh_models_example_2/src/common.h create mode 100644 lib/bt/host/nimble/nimble/apps/blemesh_models_example_2/src/device_composition.c create mode 100644 lib/bt/host/nimble/nimble/apps/blemesh_models_example_2/src/device_composition.h create mode 100644 lib/bt/host/nimble/nimble/apps/blemesh_models_example_2/src/main.c create mode 100644 lib/bt/host/nimble/nimble/apps/blemesh_models_example_2/src/no_transition_work_handler.c create mode 100644 lib/bt/host/nimble/nimble/apps/blemesh_models_example_2/src/no_transition_work_handler.h create mode 100644 lib/bt/host/nimble/nimble/apps/blemesh_models_example_2/src/publisher.c create mode 100644 lib/bt/host/nimble/nimble/apps/blemesh_models_example_2/src/publisher.h create mode 100644 lib/bt/host/nimble/nimble/apps/blemesh_models_example_2/src/state_binding.c create mode 100644 lib/bt/host/nimble/nimble/apps/blemesh_models_example_2/src/state_binding.h create mode 100644 lib/bt/host/nimble/nimble/apps/blemesh_models_example_2/src/storage.c create mode 100644 lib/bt/host/nimble/nimble/apps/blemesh_models_example_2/src/storage.h create mode 100644 lib/bt/host/nimble/nimble/apps/blemesh_models_example_2/src/transition.c create mode 100644 lib/bt/host/nimble/nimble/apps/blemesh_models_example_2/src/transition.h create mode 100644 lib/bt/host/nimble/nimble/apps/blemesh_models_example_2/syscfg.yml create mode 100644 lib/bt/host/nimble/nimble/apps/blemesh_shell/pkg.yml create mode 100644 lib/bt/host/nimble/nimble/apps/blemesh_shell/src/main.c create mode 100644 lib/bt/host/nimble/nimble/apps/blemesh_shell/syscfg.yml create mode 100644 lib/bt/host/nimble/nimble/apps/bleprph/pkg.yml create mode 100644 lib/bt/host/nimble/nimble/apps/bleprph/src/bleprph.h create mode 100644 lib/bt/host/nimble/nimble/apps/bleprph/src/gatt_svr.c create mode 100644 lib/bt/host/nimble/nimble/apps/bleprph/src/main.c create mode 100644 lib/bt/host/nimble/nimble/apps/bleprph/src/misc.c create mode 100644 lib/bt/host/nimble/nimble/apps/bleprph/src/phy.c create mode 100644 lib/bt/host/nimble/nimble/apps/bleprph/syscfg.yml create mode 100644 lib/bt/host/nimble/nimble/apps/blestress/README.md create mode 100644 lib/bt/host/nimble/nimble/apps/blestress/pkg.yml create mode 100644 lib/bt/host/nimble/nimble/apps/blestress/src/main.c create mode 100644 lib/bt/host/nimble/nimble/apps/blestress/src/misc.c create mode 100644 lib/bt/host/nimble/nimble/apps/blestress/src/misc.h create mode 100644 lib/bt/host/nimble/nimble/apps/blestress/src/rx_stress.c create mode 100644 lib/bt/host/nimble/nimble/apps/blestress/src/rx_stress.h create mode 100644 lib/bt/host/nimble/nimble/apps/blestress/src/stress.c create mode 100644 lib/bt/host/nimble/nimble/apps/blestress/src/stress.h create mode 100644 lib/bt/host/nimble/nimble/apps/blestress/src/stress_gatt.c create mode 100644 lib/bt/host/nimble/nimble/apps/blestress/src/stress_gatt.h create mode 100644 lib/bt/host/nimble/nimble/apps/blestress/src/tx_stress.c create mode 100644 lib/bt/host/nimble/nimble/apps/blestress/src/tx_stress.h create mode 100644 lib/bt/host/nimble/nimble/apps/blestress/syscfg.yml create mode 100644 lib/bt/host/nimble/nimble/apps/btshell/pkg.yml create mode 100644 lib/bt/host/nimble/nimble/apps/btshell/src/btshell.h create mode 100644 lib/bt/host/nimble/nimble/apps/btshell/src/cmd.c create mode 100644 lib/bt/host/nimble/nimble/apps/btshell/src/cmd.h create mode 100644 lib/bt/host/nimble/nimble/apps/btshell/src/cmd_gatt.c create mode 100644 lib/bt/host/nimble/nimble/apps/btshell/src/cmd_gatt.h create mode 100644 lib/bt/host/nimble/nimble/apps/btshell/src/cmd_l2cap.c create mode 100644 lib/bt/host/nimble/nimble/apps/btshell/src/cmd_l2cap.h create mode 100644 lib/bt/host/nimble/nimble/apps/btshell/src/gatt_svr.c create mode 100644 lib/bt/host/nimble/nimble/apps/btshell/src/main.c create mode 100644 lib/bt/host/nimble/nimble/apps/btshell/src/misc.c create mode 100644 lib/bt/host/nimble/nimble/apps/btshell/src/parse.c create mode 100644 lib/bt/host/nimble/nimble/apps/btshell/syscfg.yml create mode 100644 lib/bt/host/nimble/nimble/apps/bttester/README create mode 100644 lib/bt/host/nimble/nimble/apps/bttester/pkg.yml create mode 100644 lib/bt/host/nimble/nimble/apps/bttester/src/atomic.h create mode 100644 lib/bt/host/nimble/nimble/apps/bttester/src/bttester.c create mode 100644 lib/bt/host/nimble/nimble/apps/bttester/src/bttester.h create mode 100644 lib/bt/host/nimble/nimble/apps/bttester/src/bttester_pipe.h create mode 100644 lib/bt/host/nimble/nimble/apps/bttester/src/gap.c create mode 100644 lib/bt/host/nimble/nimble/apps/bttester/src/gatt.c create mode 100644 lib/bt/host/nimble/nimble/apps/bttester/src/gatt_cl.c create mode 100644 lib/bt/host/nimble/nimble/apps/bttester/src/glue.c create mode 100644 lib/bt/host/nimble/nimble/apps/bttester/src/glue.h create mode 100644 lib/bt/host/nimble/nimble/apps/bttester/src/l2cap.c create mode 100644 lib/bt/host/nimble/nimble/apps/bttester/src/main.c create mode 100644 lib/bt/host/nimble/nimble/apps/bttester/src/mesh.c create mode 100644 lib/bt/host/nimble/nimble/apps/bttester/src/rtt_pipe.c create mode 100644 lib/bt/host/nimble/nimble/apps/bttester/src/uart_pipe.c create mode 100644 lib/bt/host/nimble/nimble/apps/bttester/syscfg.yml create mode 100644 lib/bt/host/nimble/nimble/apps/central/pkg.yml create mode 100755 lib/bt/host/nimble/nimble/apps/central/src/main.c create mode 100644 lib/bt/host/nimble/nimble/apps/central/syscfg.yml create mode 100644 lib/bt/host/nimble/nimble/apps/ext_advertiser/pkg.yml create mode 100644 lib/bt/host/nimble/nimble/apps/ext_advertiser/src/main.c create mode 100644 lib/bt/host/nimble/nimble/apps/ext_advertiser/src/patterns.h create mode 100644 lib/bt/host/nimble/nimble/apps/ext_advertiser/syscfg.yml create mode 100644 lib/bt/host/nimble/nimble/apps/mesh_badge/README.md create mode 100644 lib/bt/host/nimble/nimble/apps/mesh_badge/pkg.yml create mode 100644 lib/bt/host/nimble/nimble/apps/mesh_badge/src/board.h create mode 100644 lib/bt/host/nimble/nimble/apps/mesh_badge/src/gatt_svr.c create mode 100644 lib/bt/host/nimble/nimble/apps/mesh_badge/src/main.c create mode 100644 lib/bt/host/nimble/nimble/apps/mesh_badge/src/mesh.c create mode 100644 lib/bt/host/nimble/nimble/apps/mesh_badge/src/mesh.h create mode 100644 lib/bt/host/nimble/nimble/apps/mesh_badge/src/mesh_badge.h create mode 100644 lib/bt/host/nimble/nimble/apps/mesh_badge/src/reel_board.c create mode 100644 lib/bt/host/nimble/nimble/apps/mesh_badge/syscfg.yml create mode 100644 lib/bt/host/nimble/nimble/apps/peripheral/pkg.yml create mode 100755 lib/bt/host/nimble/nimble/apps/peripheral/src/main.c create mode 100644 lib/bt/host/nimble/nimble/apps/peripheral/syscfg.yml create mode 100644 lib/bt/host/nimble/nimble/apps/scanner/pkg.yml create mode 100644 lib/bt/host/nimble/nimble/apps/scanner/src/main.c create mode 100644 lib/bt/host/nimble/nimble/apps/scanner/syscfg.yml create mode 100644 lib/bt/host/nimble/nimble/babblesim/README.md create mode 100644 lib/bt/host/nimble/nimble/babblesim/core/include/argparse.h create mode 100644 lib/bt/host/nimble/nimble/babblesim/core/include/cmsis.h create mode 100644 lib/bt/host/nimble/nimble/babblesim/core/include/core_cm4.h create mode 100644 lib/bt/host/nimble/nimble/babblesim/core/include/time_machine.h create mode 100644 lib/bt/host/nimble/nimble/babblesim/core/pkg.yml create mode 100644 lib/bt/host/nimble/nimble/babblesim/core/src/argparse.c create mode 100644 lib/bt/host/nimble/nimble/babblesim/core/src/cmsis.c create mode 100644 lib/bt/host/nimble/nimble/babblesim/core/src/irq_handler.c create mode 100644 lib/bt/host/nimble/nimble/babblesim/core/src/main_config.c create mode 100644 lib/bt/host/nimble/nimble/babblesim/core/src/time_machine.c create mode 100644 lib/bt/host/nimble/nimble/babblesim/edtt/hci_test/pkg.yml create mode 100644 lib/bt/host/nimble/nimble/babblesim/edtt/hci_test/src/main.c create mode 100644 lib/bt/host/nimble/nimble/babblesim/edtt/hci_test/syscfg.yml create mode 100644 lib/bt/host/nimble/nimble/babblesim/edtt/hci_transport/include/ble_hci_edtt.h create mode 100644 lib/bt/host/nimble/nimble/babblesim/edtt/hci_transport/include/commands.h create mode 100644 lib/bt/host/nimble/nimble/babblesim/edtt/hci_transport/include/edtt_driver.h create mode 100644 lib/bt/host/nimble/nimble/babblesim/edtt/hci_transport/pkg.yml create mode 100644 lib/bt/host/nimble/nimble/babblesim/edtt/hci_transport/src/ble_hci_edtt.c create mode 100644 lib/bt/host/nimble/nimble/babblesim/edtt/hci_transport/src/edtt_driver_bsim.c create mode 100644 lib/bt/host/nimble/nimble/babblesim/edtt/hci_transport/syscfg.yml create mode 100644 lib/bt/host/nimble/nimble/babblesim/hw/bsp/nrf52_bsim/bsp.yml create mode 100644 lib/bt/host/nimble/nimble/babblesim/hw/bsp/nrf52_bsim/include/bsp/bsp.h create mode 100644 lib/bt/host/nimble/nimble/babblesim/hw/bsp/nrf52_bsim/include/os/os_arch.h create mode 100644 lib/bt/host/nimble/nimble/babblesim/hw/bsp/nrf52_bsim/include/os/sim.h create mode 100755 lib/bt/host/nimble/nimble/babblesim/hw/bsp/nrf52_bsim/nordic_pca10040_debug.cmd create mode 100755 lib/bt/host/nimble/nimble/babblesim/hw/bsp/nrf52_bsim/nordic_pca10040_debug.sh create mode 100755 lib/bt/host/nimble/nimble/babblesim/hw/bsp/nrf52_bsim/nordic_pca10040_download.cmd create mode 100755 lib/bt/host/nimble/nimble/babblesim/hw/bsp/nrf52_bsim/nordic_pca10040_download.sh create mode 100644 lib/bt/host/nimble/nimble/babblesim/hw/bsp/nrf52_bsim/pkg.yml create mode 100644 lib/bt/host/nimble/nimble/babblesim/hw/bsp/nrf52_bsim/src/arch/bsim_arch/os_arch.c create mode 100644 lib/bt/host/nimble/nimble/babblesim/hw/bsp/nrf52_bsim/src/arch/bsim_arch/startup_nrf52_bsim.c create mode 100644 lib/bt/host/nimble/nimble/babblesim/hw/bsp/nrf52_bsim/src/hal_bsp.c create mode 100644 lib/bt/host/nimble/nimble/babblesim/hw/bsp/nrf52_bsim/syscfg.yml create mode 100644 lib/bt/host/nimble/nimble/babblesim/hw/mcu/nordic/nrf52_bsim/include/mcu/cmsis_nvic.h create mode 100644 lib/bt/host/nimble/nimble/babblesim/hw/mcu/nordic/nrf52_bsim/include/mcu/cortex_m4.h create mode 100644 lib/bt/host/nimble/nimble/babblesim/hw/mcu/nordic/nrf52_bsim/include/mcu/mcu.h create mode 100644 lib/bt/host/nimble/nimble/babblesim/hw/mcu/nordic/nrf52_bsim/include/mcu/mcu_sim.h create mode 100644 lib/bt/host/nimble/nimble/babblesim/hw/mcu/nordic/nrf52_bsim/include/mcu/native_bsp.h create mode 100644 lib/bt/host/nimble/nimble/babblesim/hw/mcu/nordic/nrf52_bsim/include/mcu/nrf52_clock.h create mode 100644 lib/bt/host/nimble/nimble/babblesim/hw/mcu/nordic/nrf52_bsim/include/mcu/nrf52_hal.h create mode 100644 lib/bt/host/nimble/nimble/babblesim/hw/mcu/nordic/nrf52_bsim/include/mcu/nrf52_periph.h create mode 100644 lib/bt/host/nimble/nimble/babblesim/hw/mcu/nordic/nrf52_bsim/pkg.yml create mode 100644 lib/bt/host/nimble/nimble/babblesim/hw/mcu/nordic/nrf52_bsim/src/hal_flash.c create mode 100644 lib/bt/host/nimble/nimble/babblesim/hw/mcu/nordic/nrf52_bsim/src/hal_hw_id.c create mode 100644 lib/bt/host/nimble/nimble/babblesim/hw/mcu/nordic/nrf52_bsim/src/hal_native_priv.h create mode 100644 lib/bt/host/nimble/nimble/babblesim/hw/mcu/nordic/nrf52_bsim/src/hal_os_tick.c create mode 100644 lib/bt/host/nimble/nimble/babblesim/hw/mcu/nordic/nrf52_bsim/src/hal_reset_cause.c create mode 100644 lib/bt/host/nimble/nimble/babblesim/hw/mcu/nordic/nrf52_bsim/src/hal_system.c create mode 100644 lib/bt/host/nimble/nimble/babblesim/hw/mcu/nordic/nrf52_bsim/src/hal_timer.c create mode 100644 lib/bt/host/nimble/nimble/babblesim/hw/mcu/nordic/nrf52_bsim/src/hal_uart.c create mode 100644 lib/bt/host/nimble/nimble/babblesim/hw/mcu/nordic/nrf52_bsim/src/hal_watchdog.c create mode 100644 lib/bt/host/nimble/nimble/babblesim/hw/mcu/nordic/nrf52_bsim/src/native_uart_cfg.c create mode 100644 lib/bt/host/nimble/nimble/babblesim/hw/mcu/nordic/nrf52_bsim/src/native_uart_cfg_priv.h create mode 100644 lib/bt/host/nimble/nimble/babblesim/hw/mcu/nordic/nrf52_bsim/src/nrf52_clock.c create mode 100644 lib/bt/host/nimble/nimble/babblesim/hw/mcu/nordic/nrf52_bsim/src/system_nrf52.c create mode 100644 lib/bt/host/nimble/nimble/babblesim/hw/mcu/nordic/nrf52_bsim/syscfg.yml create mode 100644 lib/bt/host/nimble/nimble/babblesim/sdk/.gitignore create mode 100644 lib/bt/host/nimble/nimble/babblesim/sdk/pkg.yml create mode 100755 lib/bt/host/nimble/nimble/babblesim/sdk/scripts/link_babblesim.sh create mode 100755 lib/bt/host/nimble/nimble/babblesim/sdk/scripts/link_nrfx.sh create mode 100644 lib/bt/host/nimble/nimble/babblesim/targets/blecent/pkg.yml create mode 100644 lib/bt/host/nimble/nimble/babblesim/targets/blecent/syscfg.yml create mode 100644 lib/bt/host/nimble/nimble/babblesim/targets/blecent/target.yml create mode 100644 lib/bt/host/nimble/nimble/babblesim/targets/blehci/pkg.yml create mode 100644 lib/bt/host/nimble/nimble/babblesim/targets/blehci/syscfg.yml create mode 100644 lib/bt/host/nimble/nimble/babblesim/targets/blehci/target.yml create mode 100644 lib/bt/host/nimble/nimble/babblesim/targets/bleprph/pkg.yml create mode 100644 lib/bt/host/nimble/nimble/babblesim/targets/bleprph/syscfg.yml create mode 100644 lib/bt/host/nimble/nimble/babblesim/targets/bleprph/target.yml create mode 100644 lib/bt/host/nimble/nimble/babblesim/targets/btshell/pkg.yml create mode 100644 lib/bt/host/nimble/nimble/babblesim/targets/btshell/syscfg.yml create mode 100644 lib/bt/host/nimble/nimble/babblesim/targets/btshell/target.yml create mode 100644 lib/bt/host/nimble/nimble/babblesim/targets/edtthci/pkg.yml create mode 100644 lib/bt/host/nimble/nimble/babblesim/targets/edtthci/syscfg.yml create mode 100644 lib/bt/host/nimble/nimble/babblesim/targets/edtthci/target.yml create mode 100644 lib/bt/host/nimble/nimble/docs/.gitignore create mode 100644 lib/bt/host/nimble/nimble/docs/Makefile create mode 100644 lib/bt/host/nimble/nimble/docs/README.rst create mode 100644 lib/bt/host/nimble/nimble/docs/ble_hs/ble_att.rst create mode 100644 lib/bt/host/nimble/nimble/docs/ble_hs/ble_gap.rst create mode 100644 lib/bt/host/nimble/nimble/docs/ble_hs/ble_gattc.rst create mode 100644 lib/bt/host/nimble/nimble/docs/ble_hs/ble_gatts.rst create mode 100644 lib/bt/host/nimble/nimble/docs/ble_hs/ble_hs.rst create mode 100644 lib/bt/host/nimble/nimble/docs/ble_hs/ble_hs_id.rst create mode 100644 lib/bt/host/nimble/nimble/docs/ble_hs/ble_hs_return_codes.rst create mode 100644 lib/bt/host/nimble/nimble/docs/ble_sec.rst create mode 100644 lib/bt/host/nimble/nimble/docs/ble_setup/ble_addr.rst create mode 100644 lib/bt/host/nimble/nimble/docs/ble_setup/ble_lp_clock.rst create mode 100644 lib/bt/host/nimble/nimble/docs/ble_setup/ble_setup_intro.rst create mode 100644 lib/bt/host/nimble/nimble/docs/ble_setup/ble_sync_cb.rst create mode 100644 lib/bt/host/nimble/nimble/docs/btshell/btshell_GAP.rst create mode 100644 lib/bt/host/nimble/nimble/docs/btshell/btshell_GATT.rst create mode 100644 lib/bt/host/nimble/nimble/docs/btshell/btshell_advdata.rst create mode 100644 lib/bt/host/nimble/nimble/docs/btshell/btshell_api.rst create mode 100644 lib/bt/host/nimble/nimble/docs/conf.py create mode 100644 lib/bt/host/nimble/nimble/docs/doxygen.xml create mode 100644 lib/bt/host/nimble/nimble/docs/index.rst create mode 100644 lib/bt/host/nimble/nimble/docs/mesh/index.rst create mode 100644 lib/bt/host/nimble/nimble/docs/mesh/mesh_lightning_model.jpg create mode 100644 lib/bt/host/nimble/nimble/docs/mesh/mesh_topology.jpg create mode 100644 lib/bt/host/nimble/nimble/docs/mesh/sample.rst create mode 100644 lib/bt/host/nimble/nimble/ext/tinycrypt/AUTHORS create mode 100644 lib/bt/host/nimble/nimble/ext/tinycrypt/LICENSE create mode 100644 lib/bt/host/nimble/nimble/ext/tinycrypt/README create mode 100644 lib/bt/host/nimble/nimble/ext/tinycrypt/VERSION create mode 100644 lib/bt/host/nimble/nimble/ext/tinycrypt/documentation/tinycrypt.rst create mode 100644 lib/bt/host/nimble/nimble/ext/tinycrypt/include/tinycrypt/aes.h create mode 100644 lib/bt/host/nimble/nimble/ext/tinycrypt/include/tinycrypt/cbc_mode.h create mode 100644 lib/bt/host/nimble/nimble/ext/tinycrypt/include/tinycrypt/ccm_mode.h create mode 100644 lib/bt/host/nimble/nimble/ext/tinycrypt/include/tinycrypt/cmac_mode.h create mode 100644 lib/bt/host/nimble/nimble/ext/tinycrypt/include/tinycrypt/constants.h create mode 100644 lib/bt/host/nimble/nimble/ext/tinycrypt/include/tinycrypt/ctr_mode.h create mode 100644 lib/bt/host/nimble/nimble/ext/tinycrypt/include/tinycrypt/ctr_prng.h create mode 100644 lib/bt/host/nimble/nimble/ext/tinycrypt/include/tinycrypt/ecc.h create mode 100644 lib/bt/host/nimble/nimble/ext/tinycrypt/include/tinycrypt/ecc_dh.h create mode 100644 lib/bt/host/nimble/nimble/ext/tinycrypt/include/tinycrypt/ecc_dsa.h create mode 100644 lib/bt/host/nimble/nimble/ext/tinycrypt/include/tinycrypt/ecc_platform_specific.h create mode 100644 lib/bt/host/nimble/nimble/ext/tinycrypt/include/tinycrypt/hmac.h create mode 100644 lib/bt/host/nimble/nimble/ext/tinycrypt/include/tinycrypt/hmac_prng.h create mode 100644 lib/bt/host/nimble/nimble/ext/tinycrypt/include/tinycrypt/sha256.h create mode 100644 lib/bt/host/nimble/nimble/ext/tinycrypt/include/tinycrypt/utils.h create mode 100644 lib/bt/host/nimble/nimble/ext/tinycrypt/src/aes_decrypt.c create mode 100644 lib/bt/host/nimble/nimble/ext/tinycrypt/src/aes_encrypt.c create mode 100644 lib/bt/host/nimble/nimble/ext/tinycrypt/src/cbc_mode.c create mode 100644 lib/bt/host/nimble/nimble/ext/tinycrypt/src/ccm_mode.c create mode 100644 lib/bt/host/nimble/nimble/ext/tinycrypt/src/cmac_mode.c create mode 100644 lib/bt/host/nimble/nimble/ext/tinycrypt/src/ctr_mode.c create mode 100644 lib/bt/host/nimble/nimble/ext/tinycrypt/src/ctr_prng.c create mode 100644 lib/bt/host/nimble/nimble/ext/tinycrypt/src/ecc.c create mode 100644 lib/bt/host/nimble/nimble/ext/tinycrypt/src/ecc_dh.c create mode 100644 lib/bt/host/nimble/nimble/ext/tinycrypt/src/ecc_dsa.c create mode 100644 lib/bt/host/nimble/nimble/ext/tinycrypt/src/ecc_platform_specific.c create mode 100644 lib/bt/host/nimble/nimble/ext/tinycrypt/src/hmac.c create mode 100644 lib/bt/host/nimble/nimble/ext/tinycrypt/src/hmac_prng.c create mode 100644 lib/bt/host/nimble/nimble/ext/tinycrypt/src/sha256.c create mode 100644 lib/bt/host/nimble/nimble/ext/tinycrypt/src/utils.c create mode 100644 lib/bt/host/nimble/nimble/nimble/controller/include/controller/ble_hw.h create mode 100644 lib/bt/host/nimble/nimble/nimble/controller/include/controller/ble_ll.h create mode 100644 lib/bt/host/nimble/nimble/nimble/controller/include/controller/ble_ll_adv.h create mode 100644 lib/bt/host/nimble/nimble/nimble/controller/include/controller/ble_ll_conn.h create mode 100644 lib/bt/host/nimble/nimble/nimble/controller/include/controller/ble_ll_ctrl.h create mode 100644 lib/bt/host/nimble/nimble/nimble/controller/include/controller/ble_ll_hci.h create mode 100644 lib/bt/host/nimble/nimble/nimble/controller/include/controller/ble_ll_iso.h create mode 100644 lib/bt/host/nimble/nimble/nimble/controller/include/controller/ble_ll_plna.h create mode 100644 lib/bt/host/nimble/nimble/nimble/controller/include/controller/ble_ll_resolv.h create mode 100644 lib/bt/host/nimble/nimble/nimble/controller/include/controller/ble_ll_rfmgmt.h create mode 100644 lib/bt/host/nimble/nimble/nimble/controller/include/controller/ble_ll_scan.h create mode 100644 lib/bt/host/nimble/nimble/nimble/controller/include/controller/ble_ll_scan_aux.h create mode 100644 lib/bt/host/nimble/nimble/nimble/controller/include/controller/ble_ll_sched.h create mode 100644 lib/bt/host/nimble/nimble/nimble/controller/include/controller/ble_ll_sync.h create mode 100644 lib/bt/host/nimble/nimble/nimble/controller/include/controller/ble_ll_test.h create mode 100644 lib/bt/host/nimble/nimble/nimble/controller/include/controller/ble_ll_tmr.h create mode 100644 lib/bt/host/nimble/nimble/nimble/controller/include/controller/ble_ll_trace.h create mode 100644 lib/bt/host/nimble/nimble/nimble/controller/include/controller/ble_ll_utils.h create mode 100644 lib/bt/host/nimble/nimble/nimble/controller/include/controller/ble_ll_whitelist.h create mode 100644 lib/bt/host/nimble/nimble/nimble/controller/include/controller/ble_phy.h create mode 100644 lib/bt/host/nimble/nimble/nimble/controller/include/controller/ble_phy_trace.h create mode 100644 lib/bt/host/nimble/nimble/nimble/controller/pkg.yml create mode 100644 lib/bt/host/nimble/nimble/nimble/controller/src/ble_ll.c create mode 100644 lib/bt/host/nimble/nimble/nimble/controller/src/ble_ll_adv.c create mode 100644 lib/bt/host/nimble/nimble/nimble/controller/src/ble_ll_conn.c create mode 100644 lib/bt/host/nimble/nimble/nimble/controller/src/ble_ll_conn_hci.c create mode 100644 lib/bt/host/nimble/nimble/nimble/controller/src/ble_ll_conn_priv.h create mode 100644 lib/bt/host/nimble/nimble/nimble/controller/src/ble_ll_ctrl.c create mode 100644 lib/bt/host/nimble/nimble/nimble/controller/src/ble_ll_ctrl_priv.h create mode 100644 lib/bt/host/nimble/nimble/nimble/controller/src/ble_ll_dtm.c create mode 100644 lib/bt/host/nimble/nimble/nimble/controller/src/ble_ll_dtm_priv.h create mode 100644 lib/bt/host/nimble/nimble/nimble/controller/src/ble_ll_hci.c create mode 100644 lib/bt/host/nimble/nimble/nimble/controller/src/ble_ll_hci_ev.c create mode 100644 lib/bt/host/nimble/nimble/nimble/controller/src/ble_ll_hci_priv.h create mode 100644 lib/bt/host/nimble/nimble/nimble/controller/src/ble_ll_hci_vs.c create mode 100644 lib/bt/host/nimble/nimble/nimble/controller/src/ble_ll_iso.c create mode 100644 lib/bt/host/nimble/nimble/nimble/controller/src/ble_ll_priv.h create mode 100644 lib/bt/host/nimble/nimble/nimble/controller/src/ble_ll_rand.c create mode 100644 lib/bt/host/nimble/nimble/nimble/controller/src/ble_ll_resolv.c create mode 100644 lib/bt/host/nimble/nimble/nimble/controller/src/ble_ll_rfmgmt.c create mode 100644 lib/bt/host/nimble/nimble/nimble/controller/src/ble_ll_scan.c create mode 100644 lib/bt/host/nimble/nimble/nimble/controller/src/ble_ll_scan_aux.c create mode 100644 lib/bt/host/nimble/nimble/nimble/controller/src/ble_ll_sched.c create mode 100644 lib/bt/host/nimble/nimble/nimble/controller/src/ble_ll_supp_cmd.c create mode 100644 lib/bt/host/nimble/nimble/nimble/controller/src/ble_ll_sync.c create mode 100644 lib/bt/host/nimble/nimble/nimble/controller/src/ble_ll_trace.c create mode 100644 lib/bt/host/nimble/nimble/nimble/controller/src/ble_ll_utils.c create mode 100644 lib/bt/host/nimble/nimble/nimble/controller/src/ble_ll_whitelist.c create mode 100644 lib/bt/host/nimble/nimble/nimble/controller/syscfg.defunct.yml create mode 100644 lib/bt/host/nimble/nimble/nimble/controller/syscfg.hbd.yml create mode 100644 lib/bt/host/nimble/nimble/nimble/controller/syscfg.yml create mode 100644 lib/bt/host/nimble/nimble/nimble/controller/test/pkg.yml create mode 100644 lib/bt/host/nimble/nimble/nimble/controller/test/src/ble_ll_csa2_test.c create mode 100644 lib/bt/host/nimble/nimble/nimble/controller/test/src/ble_ll_csa2_test.h create mode 100644 lib/bt/host/nimble/nimble/nimble/controller/test/src/ble_ll_test.c create mode 100644 lib/bt/host/nimble/nimble/nimble/controller/test/syscfg.yml create mode 100644 lib/bt/host/nimble/nimble/nimble/drivers/dialog_cmac/README.md create mode 100644 lib/bt/host/nimble/nimble/nimble/drivers/dialog_cmac/include/ble/xcvr.h create mode 100644 lib/bt/host/nimble/nimble/nimble/drivers/dialog_cmac/pkg.yml create mode 100644 lib/bt/host/nimble/nimble/nimble/drivers/dialog_cmac/src/ble_hw.c create mode 100644 lib/bt/host/nimble/nimble/nimble/drivers/dialog_cmac/src/ble_hw_priv.h create mode 100644 lib/bt/host/nimble/nimble/nimble/drivers/dialog_cmac/src/ble_phy.c create mode 100644 lib/bt/host/nimble/nimble/nimble/drivers/dialog_cmac/src/ble_rf.c create mode 100644 lib/bt/host/nimble/nimble/nimble/drivers/dialog_cmac/src/ble_rf_priv.h create mode 100644 lib/bt/host/nimble/nimble/nimble/drivers/dialog_cmac/syscfg.yml create mode 100644 lib/bt/host/nimble/nimble/nimble/drivers/native/include/ble/xcvr.h create mode 100644 lib/bt/host/nimble/nimble/nimble/drivers/native/pkg.yml create mode 100644 lib/bt/host/nimble/nimble/nimble/drivers/native/src/ble_hw.c create mode 100644 lib/bt/host/nimble/nimble/nimble/drivers/native/src/ble_phy.c create mode 100644 lib/bt/host/nimble/nimble/nimble/drivers/nrf51/include/ble/xcvr.h create mode 100644 lib/bt/host/nimble/nimble/nimble/drivers/nrf51/pkg.yml create mode 100644 lib/bt/host/nimble/nimble/nimble/drivers/nrf51/src/ble_hw.c create mode 100644 lib/bt/host/nimble/nimble/nimble/drivers/nrf51/src/ble_phy.c create mode 100644 lib/bt/host/nimble/nimble/nimble/drivers/nrf52/include/ble/xcvr.h create mode 100644 lib/bt/host/nimble/nimble/nimble/drivers/nrf52/pkg.yml create mode 100644 lib/bt/host/nimble/nimble/nimble/drivers/nrf52/src/ble_hw.c create mode 100644 lib/bt/host/nimble/nimble/nimble/drivers/nrf52/src/ble_phy.c create mode 100644 lib/bt/host/nimble/nimble/nimble/drivers/nrf52/src/ble_phy_trace.c create mode 100644 lib/bt/host/nimble/nimble/nimble/drivers/nrf52/syscfg.yml create mode 100644 lib/bt/host/nimble/nimble/nimble/drivers/nrf5340/include/ble/xcvr.h create mode 100644 lib/bt/host/nimble/nimble/nimble/drivers/nrf5340/pkg.yml create mode 100644 lib/bt/host/nimble/nimble/nimble/drivers/nrf5340/src/ble_hw.c create mode 100644 lib/bt/host/nimble/nimble/nimble/drivers/nrf5340/src/ble_phy.c create mode 100644 lib/bt/host/nimble/nimble/nimble/drivers/nrf5340/src/ble_phy_trace.c create mode 100644 lib/bt/host/nimble/nimble/nimble/drivers/nrf5340/syscfg.yml create mode 100644 lib/bt/host/nimble/nimble/nimble/drivers/plna/sky66112/pkg.yml create mode 100644 lib/bt/host/nimble/nimble/nimble/drivers/plna/sky66112/src/sky66112.c create mode 100644 lib/bt/host/nimble/nimble/nimble/drivers/plna/sky66112/syscfg.yml create mode 100644 lib/bt/host/nimble/nimble/nimble/host/include/host/ble_aes_ccm.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/include/host/ble_att.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/include/host/ble_ead.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/include/host/ble_eddystone.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/include/host/ble_esp_gap.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/include/host/ble_esp_gatt.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/include/host/ble_esp_hs.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/include/host/ble_gap.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/include/host/ble_gatt.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/include/host/ble_hs.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/include/host/ble_hs_adv.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/include/host/ble_hs_hci.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/include/host/ble_hs_id.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/include/host/ble_hs_log.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/include/host/ble_hs_mbuf.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/include/host/ble_hs_pvcy.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/include/host/ble_hs_stop.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/include/host/ble_ibeacon.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/include/host/ble_l2cap.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/include/host/ble_sm.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/include/host/ble_store.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/include/host/ble_uuid.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/mesh/include/mesh/access.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/mesh/include/mesh/atomic.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/mesh/include/mesh/cdb.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/mesh/include/mesh/cfg.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/mesh/include/mesh/cfg_cli.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/mesh/include/mesh/cfg_srv.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/mesh/include/mesh/glue.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/mesh/include/mesh/health_cli.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/mesh/include/mesh/health_srv.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/mesh/include/mesh/heartbeat.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/mesh/include/mesh/main.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/mesh/include/mesh/mesh.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/mesh/include/mesh/model_cli.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/mesh/include/mesh/model_srv.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/mesh/include/mesh/msg.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/mesh/include/mesh/porting.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/mesh/include/mesh/proxy.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/mesh/include/mesh/slist.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/mesh/include/mesh/testing.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/mesh/pkg.yml create mode 100644 lib/bt/host/nimble/nimble/nimble/host/mesh/src/access.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/mesh/src/access.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/mesh/src/adv.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/mesh/src/adv.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/mesh/src/adv_ext.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/mesh/src/adv_legacy.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/mesh/src/aes-ccm.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/mesh/src/app_keys.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/mesh/src/app_keys.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/mesh/src/atomic.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/mesh/src/beacon.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/mesh/src/beacon.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/mesh/src/cdb.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/mesh/src/cdb_priv.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/mesh/src/cfg.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/mesh/src/cfg.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/mesh/src/cfg_cli.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/mesh/src/cfg_srv.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/mesh/src/crypto.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/mesh/src/crypto.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/mesh/src/foundation.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/mesh/src/friend.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/mesh/src/friend.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/mesh/src/glue.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/mesh/src/health_cli.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/mesh/src/health_srv.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/mesh/src/heartbeat.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/mesh/src/heartbeat.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/mesh/src/light_model.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/mesh/src/light_model.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/mesh/src/lpn.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/mesh/src/lpn.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/mesh/src/mesh.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/mesh/src/mesh_priv.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/mesh/src/model_cli.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/mesh/src/model_srv.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/mesh/src/msg.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/mesh/src/net.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/mesh/src/net.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/mesh/src/pb_adv.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/mesh/src/pb_gatt.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/mesh/src/pb_gatt_srv.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/mesh/src/pb_gatt_srv.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/mesh/src/prov.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/mesh/src/prov.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/mesh/src/prov_bearer.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/mesh/src/prov_device.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/mesh/src/provisioner.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/mesh/src/provisioner.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/mesh/src/proxy.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/mesh/src/proxy.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/mesh/src/proxy_msg.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/mesh/src/proxy_msg.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/mesh/src/proxy_srv.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/mesh/src/rpl.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/mesh/src/rpl.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/mesh/src/settings.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/mesh/src/settings.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/mesh/src/shell.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/mesh/src/shell.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/mesh/src/subnet.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/mesh/src/subnet.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/mesh/src/testing.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/mesh/src/testing.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/mesh/src/transport.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/mesh/src/transport.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/mesh/syscfg.yml create mode 100644 lib/bt/host/nimble/nimble/nimble/host/pkg.yml create mode 100644 lib/bt/host/nimble/nimble/nimble/host/pts/README.txt create mode 100644 lib/bt/host/nimble/nimble/nimble/host/pts/pts-gap.txt create mode 100644 lib/bt/host/nimble/nimble/nimble/host/pts/pts-gatt.txt create mode 100644 lib/bt/host/nimble/nimble/nimble/host/pts/pts-l2cap.txt create mode 100644 lib/bt/host/nimble/nimble/nimble/host/pts/pts-sm.txt create mode 100644 lib/bt/host/nimble/nimble/nimble/host/pts/tpg/94654-20170317-085122560.tpg create mode 100644 lib/bt/host/nimble/nimble/nimble/host/pts/tpg/94654-20170317-085441153.pts create mode 100644 lib/bt/host/nimble/nimble/nimble/host/services/ans/include/services/ans/ble_svc_ans.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/services/ans/pkg.yml create mode 100644 lib/bt/host/nimble/nimble/nimble/host/services/ans/src/ble_svc_ans.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/services/ans/syscfg.yml create mode 100644 lib/bt/host/nimble/nimble/nimble/host/services/bas/include/services/bas/ble_svc_bas.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/services/bas/pkg.yml create mode 100644 lib/bt/host/nimble/nimble/nimble/host/services/bas/src/ble_svc_bas.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/services/bas/syscfg.yml create mode 100644 lib/bt/host/nimble/nimble/nimble/host/services/bleuart/include/bleuart/bleuart.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/services/bleuart/pkg.yml create mode 100644 lib/bt/host/nimble/nimble/nimble/host/services/bleuart/src/bleuart.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/services/bleuart/syscfg.yml create mode 100644 lib/bt/host/nimble/nimble/nimble/host/services/cts/include/services/cts/ble_svc_cts.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/services/cts/pkg.yml create mode 100644 lib/bt/host/nimble/nimble/nimble/host/services/cts/src/ble_svc_cts.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/services/cts/syscfg.yml create mode 100644 lib/bt/host/nimble/nimble/nimble/host/services/dis/include/services/dis/ble_svc_dis.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/services/dis/pkg.yml create mode 100644 lib/bt/host/nimble/nimble/nimble/host/services/dis/src/ble_svc_dis.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/services/dis/syscfg.yml create mode 100644 lib/bt/host/nimble/nimble/nimble/host/services/gap/include/services/gap/ble_svc_gap.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/services/gap/pkg.yml create mode 100644 lib/bt/host/nimble/nimble/nimble/host/services/gap/src/ble_svc_gap.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/services/gap/syscfg.yml create mode 100644 lib/bt/host/nimble/nimble/nimble/host/services/gatt/include/services/gatt/ble_svc_gatt.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/services/gatt/pkg.yml create mode 100644 lib/bt/host/nimble/nimble/nimble/host/services/gatt/src/ble_svc_gatt.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/services/gatt/syscfg.yml create mode 100644 lib/bt/host/nimble/nimble/nimble/host/services/hid/include/services/hid/ble_svc_hid.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/services/hid/pkg.yml create mode 100644 lib/bt/host/nimble/nimble/nimble/host/services/hid/src/ble_svc_hid.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/services/hr/include/services/hr/ble_svc_hr.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/services/hr/pkg.yml create mode 100644 lib/bt/host/nimble/nimble/nimble/host/services/hr/src/ble_svc_hr.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/services/hr/syscfg.yml create mode 100644 lib/bt/host/nimble/nimble/nimble/host/services/htp/include/services/htp/ble_svc_htp.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/services/htp/pkg.yml create mode 100644 lib/bt/host/nimble/nimble/nimble/host/services/htp/src/ble_svc_htp.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/services/htp/syscfg.yml create mode 100644 lib/bt/host/nimble/nimble/nimble/host/services/ias/include/services/ias/ble_svc_ias.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/services/ias/pkg.yml create mode 100644 lib/bt/host/nimble/nimble/nimble/host/services/ias/src/ble_svc_ias.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/services/ias/syscfg.yml create mode 100644 lib/bt/host/nimble/nimble/nimble/host/services/ipss/include/services/ipss/ble_svc_ipss.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/services/ipss/pkg.yml create mode 100644 lib/bt/host/nimble/nimble/nimble/host/services/ipss/src/ble_svc_ipss.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/services/ipss/syscfg.yml create mode 100644 lib/bt/host/nimble/nimble/nimble/host/services/lls/include/services/lls/ble_svc_lls.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/services/lls/pkg.yml create mode 100644 lib/bt/host/nimble/nimble/nimble/host/services/lls/src/ble_svc_lls.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/services/lls/syscfg.yml create mode 100644 lib/bt/host/nimble/nimble/nimble/host/services/prox/include/services/prox/ble_svc_prox.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/services/prox/pkg.yml create mode 100644 lib/bt/host/nimble/nimble/nimble/host/services/prox/src/ble_svc_prox.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/services/prox/syscfg.yml create mode 100644 lib/bt/host/nimble/nimble/nimble/host/services/sps/include/services/sps/ble_svc_sps.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/services/sps/pkg.yml create mode 100644 lib/bt/host/nimble/nimble/nimble/host/services/sps/src/ble_svc_sps.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/services/tps/include/services/tps/ble_svc_tps.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/services/tps/pkg.yml create mode 100644 lib/bt/host/nimble/nimble/nimble/host/services/tps/src/ble_svc_tps.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/services/tps/syscfg.yml create mode 100644 lib/bt/host/nimble/nimble/nimble/host/src/ble_aes_ccm.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/src/ble_att.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/src/ble_att_clt.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/src/ble_att_cmd.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/src/ble_att_cmd_priv.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/src/ble_att_priv.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/src/ble_att_svr.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/src/ble_ead.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/src/ble_eddystone.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/src/ble_gap.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/src/ble_gap_priv.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/src/ble_gatt_priv.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/src/ble_gattc.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/src/ble_gattc_cache.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/src/ble_gattc_cache_conn.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/src/ble_gattc_cache_priv.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/src/ble_gatts.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/src/ble_gatts_lcl.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/src/ble_hs.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/src/ble_hs_adv.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/src/ble_hs_adv_priv.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/src/ble_hs_atomic.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/src/ble_hs_atomic_priv.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/src/ble_hs_cfg.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/src/ble_hs_conn.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/src/ble_hs_conn_priv.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/src/ble_hs_flow.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/src/ble_hs_flow_priv.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/src/ble_hs_hci.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/src/ble_hs_hci_cmd.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/src/ble_hs_hci_evt.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/src/ble_hs_hci_priv.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/src/ble_hs_hci_util.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/src/ble_hs_id.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/src/ble_hs_id_priv.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/src/ble_hs_log.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/src/ble_hs_mbuf.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/src/ble_hs_mbuf_priv.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/src/ble_hs_misc.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/src/ble_hs_mqueue.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/src/ble_hs_periodic_sync.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/src/ble_hs_periodic_sync_priv.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/src/ble_hs_priv.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/src/ble_hs_pvcy.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/src/ble_hs_pvcy_priv.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/src/ble_hs_resolv.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/src/ble_hs_resolv_priv.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/src/ble_hs_shutdown.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/src/ble_hs_startup.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/src/ble_hs_startup_priv.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/src/ble_hs_stop.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/src/ble_ibeacon.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/src/ble_l2cap.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/src/ble_l2cap_coc.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/src/ble_l2cap_coc_priv.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/src/ble_l2cap_priv.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/src/ble_l2cap_sig.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/src/ble_l2cap_sig_cmd.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/src/ble_l2cap_sig_priv.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/src/ble_sm.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/src/ble_sm_alg.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/src/ble_sm_cmd.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/src/ble_sm_lgcy.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/src/ble_sm_priv.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/src/ble_sm_sc.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/src/ble_store.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/src/ble_store_util.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/src/ble_uuid.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/src/ble_uuid_priv.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/store/config/include/store/config/ble_store_config.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/store/config/pkg.yml create mode 100644 lib/bt/host/nimble/nimble/nimble/host/store/config/src/ble_store_config.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/store/config/src/ble_store_config_conf.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/store/config/src/ble_store_config_priv.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/store/config/src/ble_store_nvs.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/store/config/syscfg.yml create mode 100644 lib/bt/host/nimble/nimble/nimble/host/store/ram/include/store/ram/ble_store_ram.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/store/ram/pkg.yml create mode 100644 lib/bt/host/nimble/nimble/nimble/host/store/ram/src/ble_store_ram.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/store/ram/syscfg.yml create mode 100644 lib/bt/host/nimble/nimble/nimble/host/syscfg.yml create mode 100644 lib/bt/host/nimble/nimble/nimble/host/test/pkg.yml create mode 100644 lib/bt/host/nimble/nimble/nimble/host/test/src/ble_att_clt_test.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/test/src/ble_att_svr_test.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/test/src/ble_gap_test.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/test/src/ble_gatt_conn_test.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/test/src/ble_gatt_disc_c_test.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/test/src/ble_gatt_disc_d_test.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/test/src/ble_gatt_disc_s_test.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/test/src/ble_gatt_find_s_test.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/test/src/ble_gatt_read_test.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/test/src/ble_gatt_write_test.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/test/src/ble_gatts_notify_test.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/test/src/ble_gatts_read_test.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/test/src/ble_gatts_reg_test.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/test/src/ble_hs_adv_test.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/test/src/ble_hs_conn_test.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/test/src/ble_hs_hci_test.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/test/src/ble_hs_id_test.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/test/src/ble_hs_pvcy_test.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/test/src/ble_hs_stop_test.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/test/src/ble_hs_test.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/test/src/ble_hs_test.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/test/src/ble_hs_test_util.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/test/src/ble_hs_test_util.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/test/src/ble_hs_test_util_hci.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/test/src/ble_hs_test_util_hci.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/test/src/ble_l2cap_test.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/test/src/ble_os_test.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/test/src/ble_sm_lgcy_test.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/test/src/ble_sm_sc_test.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/test/src/ble_sm_test.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/test/src/ble_sm_test_util.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/test/src/ble_sm_test_util.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/test/src/ble_store_test.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/test/src/ble_uuid_test.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/test/syscfg.yml create mode 100755 lib/bt/host/nimble/nimble/nimble/host/tools/log2smtest.rb create mode 100644 lib/bt/host/nimble/nimble/nimble/host/util/include/host/util/util.h create mode 100644 lib/bt/host/nimble/nimble/nimble/host/util/pkg.yml create mode 100644 lib/bt/host/nimble/nimble/nimble/host/util/src/addr.c create mode 100644 lib/bt/host/nimble/nimble/nimble/host/util/syscfg.yml create mode 100644 lib/bt/host/nimble/nimble/nimble/include/nimble/ble.h create mode 100644 lib/bt/host/nimble/nimble/nimble/include/nimble/hci_common.h create mode 100644 lib/bt/host/nimble/nimble/nimble/include/nimble/nimble_npl.h create mode 100644 lib/bt/host/nimble/nimble/nimble/include/nimble/nimble_opt.h create mode 100644 lib/bt/host/nimble/nimble/nimble/include/nimble/nimble_opt_auto.h create mode 100644 lib/bt/host/nimble/nimble/nimble/pkg.yml create mode 100644 lib/bt/host/nimble/nimble/nimble/syscfg.yml create mode 100644 lib/bt/host/nimble/nimble/nimble/transport/apollo3/pkg.yml create mode 100644 lib/bt/host/nimble/nimble/nimble/transport/apollo3/src/apollo3_ble_hci.c create mode 100644 lib/bt/host/nimble/nimble/nimble/transport/apollo3/syscfg.yml create mode 100644 lib/bt/host/nimble/nimble/nimble/transport/common/hci_h4/include/nimble/transport/hci_h4.h create mode 100644 lib/bt/host/nimble/nimble/nimble/transport/common/hci_h4/pkg.yml create mode 100644 lib/bt/host/nimble/nimble/nimble/transport/common/hci_h4/src/hci_h4.c create mode 100644 lib/bt/host/nimble/nimble/nimble/transport/dialog_cmac/cmac_driver/diag/pkg.yml create mode 100644 lib/bt/host/nimble/nimble/nimble/transport/dialog_cmac/cmac_driver/diag/src/cmac_diag.c create mode 100644 lib/bt/host/nimble/nimble/nimble/transport/dialog_cmac/cmac_driver/include/cmac_driver/cmac_diag.h create mode 100644 lib/bt/host/nimble/nimble/nimble/transport/dialog_cmac/cmac_driver/include/cmac_driver/cmac_host.h create mode 100644 lib/bt/host/nimble/nimble/nimble/transport/dialog_cmac/cmac_driver/include/cmac_driver/cmac_shared.h create mode 100644 lib/bt/host/nimble/nimble/nimble/transport/dialog_cmac/cmac_driver/pkg.yml create mode 100755 lib/bt/host/nimble/nimble/nimble/transport/dialog_cmac/cmac_driver/scripts/build_libcmac.sh create mode 100755 lib/bt/host/nimble/nimble/nimble/transport/dialog_cmac/cmac_driver/scripts/create_cmac_bin.sh create mode 100644 lib/bt/host/nimble/nimble/nimble/transport/dialog_cmac/cmac_driver/src/cmac_host.c create mode 100644 lib/bt/host/nimble/nimble/nimble/transport/dialog_cmac/cmac_driver/src/cmac_mbox.c create mode 100644 lib/bt/host/nimble/nimble/nimble/transport/dialog_cmac/cmac_driver/src/cmac_rand.c create mode 100644 lib/bt/host/nimble/nimble/nimble/transport/dialog_cmac/cmac_driver/src/cmac_shared.c create mode 100644 lib/bt/host/nimble/nimble/nimble/transport/dialog_cmac/cmac_driver/syscfg.yml create mode 100644 lib/bt/host/nimble/nimble/nimble/transport/dialog_cmac/pkg.yml create mode 100644 lib/bt/host/nimble/nimble/nimble/transport/dialog_cmac/src/hci_cmac.c create mode 100644 lib/bt/host/nimble/nimble/nimble/transport/emspi/include/transport/emspi/ble_hci_emspi.h create mode 100644 lib/bt/host/nimble/nimble/nimble/transport/emspi/pkg.yml create mode 100644 lib/bt/host/nimble/nimble/nimble/transport/emspi/src/ble_hci_emspi.c create mode 100644 lib/bt/host/nimble/nimble/nimble/transport/emspi/syscfg.yml create mode 100644 lib/bt/host/nimble/nimble/nimble/transport/include/nimble/transport.h create mode 100644 lib/bt/host/nimble/nimble/nimble/transport/include/nimble/transport/monitor.h create mode 100644 lib/bt/host/nimble/nimble/nimble/transport/include/nimble/transport_impl.h create mode 100644 lib/bt/host/nimble/nimble/nimble/transport/nrf5340/pkg.yml create mode 100644 lib/bt/host/nimble/nimble/nimble/transport/nrf5340/src/nrf5340_ble_hci.c create mode 100644 lib/bt/host/nimble/nimble/nimble/transport/nrf5340/syscfg.yml create mode 100644 lib/bt/host/nimble/nimble/nimble/transport/pkg.yml create mode 100644 lib/bt/host/nimble/nimble/nimble/transport/socket/include/socket/ble_hci_socket.h create mode 100644 lib/bt/host/nimble/nimble/nimble/transport/socket/pkg.yml create mode 100644 lib/bt/host/nimble/nimble/nimble/transport/socket/src/ble_hci_socket.c create mode 100644 lib/bt/host/nimble/nimble/nimble/transport/socket/syscfg.yml create mode 100644 lib/bt/host/nimble/nimble/nimble/transport/src/monitor.c create mode 100644 lib/bt/host/nimble/nimble/nimble/transport/src/monitor_priv.h create mode 100644 lib/bt/host/nimble/nimble/nimble/transport/src/transport.c create mode 100644 lib/bt/host/nimble/nimble/nimble/transport/syscfg.defunct.yml create mode 100644 lib/bt/host/nimble/nimble/nimble/transport/syscfg.monitor.yml create mode 100644 lib/bt/host/nimble/nimble/nimble/transport/syscfg.yml create mode 100644 lib/bt/host/nimble/nimble/nimble/transport/uart/pkg.yml create mode 100644 lib/bt/host/nimble/nimble/nimble/transport/uart/src/hci_uart.c create mode 100644 lib/bt/host/nimble/nimble/nimble/transport/uart/syscfg.yml create mode 100644 lib/bt/host/nimble/nimble/nimble/transport/uart_ll/pkg.yml create mode 100644 lib/bt/host/nimble/nimble/nimble/transport/uart_ll/src/hci_uart.c create mode 100644 lib/bt/host/nimble/nimble/nimble/transport/uart_ll/syscfg.yml create mode 100644 lib/bt/host/nimble/nimble/nimble/transport/usb/pkg.yml create mode 100644 lib/bt/host/nimble/nimble/nimble/transport/usb/src/ble_hci_usb.c create mode 100644 lib/bt/host/nimble/nimble/nimble/transport/usb/syscfg.yml create mode 100644 lib/bt/host/nimble/nimble/porting/examples/dummy/Makefile create mode 100644 lib/bt/host/nimble/nimble/porting/examples/dummy/main.c create mode 100644 lib/bt/host/nimble/nimble/porting/examples/linux/Makefile create mode 100644 lib/bt/host/nimble/nimble/porting/examples/linux/README.md create mode 100644 lib/bt/host/nimble/nimble/porting/examples/linux/ble.c create mode 100644 lib/bt/host/nimble/nimble/porting/examples/linux/include/logcfg/logcfg.h create mode 100644 lib/bt/host/nimble/nimble/porting/examples/linux/include/syscfg/syscfg.h create mode 100644 lib/bt/host/nimble/nimble/porting/examples/linux/include/sysflash/sysflash.h create mode 100644 lib/bt/host/nimble/nimble/porting/examples/linux/main.c create mode 100644 lib/bt/host/nimble/nimble/porting/examples/linux_blemesh/Makefile create mode 100644 lib/bt/host/nimble/nimble/porting/examples/linux_blemesh/ble.c create mode 100644 lib/bt/host/nimble/nimble/porting/examples/linux_blemesh/include/logcfg/logcfg.h create mode 100644 lib/bt/host/nimble/nimble/porting/examples/linux_blemesh/include/syscfg/syscfg.h create mode 100644 lib/bt/host/nimble/nimble/porting/examples/linux_blemesh/include/sysflash/sysflash.h create mode 100644 lib/bt/host/nimble/nimble/porting/examples/linux_blemesh/main.c create mode 100644 lib/bt/host/nimble/nimble/porting/examples/nuttx/Make.defs create mode 100644 lib/bt/host/nimble/nimble/porting/examples/nuttx/ble.c create mode 100644 lib/bt/host/nimble/nimble/porting/examples/nuttx/include/logcfg/logcfg.h create mode 100644 lib/bt/host/nimble/nimble/porting/examples/nuttx/include/syscfg/syscfg.h create mode 100644 lib/bt/host/nimble/nimble/porting/examples/nuttx/include/sysflash/sysflash.h create mode 100644 lib/bt/host/nimble/nimble/porting/examples/nuttx/main.c create mode 100644 lib/bt/host/nimble/nimble/porting/nimble/Makefile.controller create mode 100644 lib/bt/host/nimble/nimble/porting/nimble/Makefile.defs create mode 100644 lib/bt/host/nimble/nimble/porting/nimble/Makefile.mesh create mode 100644 lib/bt/host/nimble/nimble/porting/nimble/Makefile.tinycrypt create mode 100644 lib/bt/host/nimble/nimble/porting/nimble/include/hal/hal_gpio.h create mode 100644 lib/bt/host/nimble/nimble/porting/nimble/include/hal/hal_os_tick.h create mode 100644 lib/bt/host/nimble/nimble/porting/nimble/include/hal/hal_system.h create mode 100644 lib/bt/host/nimble/nimble/porting/nimble/include/hal/hal_timer.h create mode 100644 lib/bt/host/nimble/nimble/porting/nimble/include/hal/hal_uart.h create mode 100644 lib/bt/host/nimble/nimble/porting/nimble/include/hal/hal_watchdog.h create mode 100644 lib/bt/host/nimble/nimble/porting/nimble/include/log/log.h create mode 100644 lib/bt/host/nimble/nimble/porting/nimble/include/log_common/ignore.h create mode 100644 lib/bt/host/nimble/nimble/porting/nimble/include/log_common/log_common.h create mode 100644 lib/bt/host/nimble/nimble/porting/nimble/include/logcfg/logcfg.h create mode 100644 lib/bt/host/nimble/nimble/porting/nimble/include/mem/mem.h create mode 100644 lib/bt/host/nimble/nimble/porting/nimble/include/modlog/modlog.h create mode 100644 lib/bt/host/nimble/nimble/porting/nimble/include/nimble/nimble_port.h create mode 100644 lib/bt/host/nimble/nimble/porting/nimble/include/nimble/storage_port.h create mode 100644 lib/bt/host/nimble/nimble/porting/nimble/include/os/endian.h create mode 100644 lib/bt/host/nimble/nimble/porting/nimble/include/os/os.h create mode 100644 lib/bt/host/nimble/nimble/porting/nimble/include/os/os_cputime.h create mode 100644 lib/bt/host/nimble/nimble/porting/nimble/include/os/os_error.h create mode 100644 lib/bt/host/nimble/nimble/porting/nimble/include/os/os_mbuf.h create mode 100644 lib/bt/host/nimble/nimble/porting/nimble/include/os/os_mempool.h create mode 100644 lib/bt/host/nimble/nimble/porting/nimble/include/os/os_trace_api.h create mode 100644 lib/bt/host/nimble/nimble/porting/nimble/include/os/queue.h create mode 100644 lib/bt/host/nimble/nimble/porting/nimble/include/os/util.h create mode 100644 lib/bt/host/nimble/nimble/porting/nimble/include/stats/stats.h create mode 100644 lib/bt/host/nimble/nimble/porting/nimble/include/syscfg/syscfg.h create mode 100644 lib/bt/host/nimble/nimble/porting/nimble/include/sysflash/sysflash.h create mode 100644 lib/bt/host/nimble/nimble/porting/nimble/include/sysinit/sysinit.h create mode 100644 lib/bt/host/nimble/nimble/porting/nimble/pkg.yml create mode 100644 lib/bt/host/nimble/nimble/porting/nimble/src/endian.c create mode 100644 lib/bt/host/nimble/nimble/porting/nimble/src/hal_timer.c create mode 100644 lib/bt/host/nimble/nimble/porting/nimble/src/hal_uart.c create mode 100644 lib/bt/host/nimble/nimble/porting/nimble/src/mem.c create mode 100644 lib/bt/host/nimble/nimble/porting/nimble/src/nimble_port.c create mode 100644 lib/bt/host/nimble/nimble/porting/nimble/src/os_cputime.c create mode 100644 lib/bt/host/nimble/nimble/porting/nimble/src/os_cputime_pwr2.c create mode 100644 lib/bt/host/nimble/nimble/porting/nimble/src/os_mbuf.c create mode 100644 lib/bt/host/nimble/nimble/porting/nimble/src/os_mempool.c create mode 100644 lib/bt/host/nimble/nimble/porting/nimble/src/os_msys_init.c create mode 100644 lib/bt/host/nimble/nimble/porting/npl/dummy/include/nimble/nimble_npl_os.h create mode 100644 lib/bt/host/nimble/nimble/porting/npl/dummy/src/npl_os_dummy.c create mode 100644 lib/bt/host/nimble/nimble/porting/npl/freertos/include/nimble/nimble_npl_os.h create mode 100644 lib/bt/host/nimble/nimble/porting/npl/freertos/include/nimble/nimble_port_freertos.h create mode 100644 lib/bt/host/nimble/nimble/porting/npl/freertos/include/nimble/npl_freertos.h create mode 100644 lib/bt/host/nimble/nimble/porting/npl/freertos/src/nimble_port_freertos.c create mode 100644 lib/bt/host/nimble/nimble/porting/npl/freertos/src/npl_os_freertos.c create mode 100644 lib/bt/host/nimble/nimble/porting/npl/linux/include/console/console.h create mode 100644 lib/bt/host/nimble/nimble/porting/npl/linux/include/nimble/nimble_npl_os.h create mode 100644 lib/bt/host/nimble/nimble/porting/npl/linux/include/nimble/os_types.h create mode 100644 lib/bt/host/nimble/nimble/porting/npl/linux/src/os_atomic.c create mode 100644 lib/bt/host/nimble/nimble/porting/npl/linux/src/os_callout.c create mode 100644 lib/bt/host/nimble/nimble/porting/npl/linux/src/os_eventq.cc create mode 100644 lib/bt/host/nimble/nimble/porting/npl/linux/src/os_mutex.c create mode 100644 lib/bt/host/nimble/nimble/porting/npl/linux/src/os_sem.c create mode 100644 lib/bt/host/nimble/nimble/porting/npl/linux/src/os_task.c create mode 100644 lib/bt/host/nimble/nimble/porting/npl/linux/src/os_time.c create mode 100644 lib/bt/host/nimble/nimble/porting/npl/linux/src/wqueue.h create mode 100644 lib/bt/host/nimble/nimble/porting/npl/linux/test/Makefile create mode 100644 lib/bt/host/nimble/nimble/porting/npl/linux/test/test_npl_callout.c create mode 100644 lib/bt/host/nimble/nimble/porting/npl/linux/test/test_npl_eventq.c create mode 100644 lib/bt/host/nimble/nimble/porting/npl/linux/test/test_npl_mempool.c create mode 100644 lib/bt/host/nimble/nimble/porting/npl/linux/test/test_npl_sem.c create mode 100644 lib/bt/host/nimble/nimble/porting/npl/linux/test/test_npl_task.c create mode 100644 lib/bt/host/nimble/nimble/porting/npl/linux/test/test_util.h create mode 100644 lib/bt/host/nimble/nimble/porting/npl/mynewt/include/nimble/nimble_npl_os.h create mode 100644 lib/bt/host/nimble/nimble/porting/npl/mynewt/pkg.yml create mode 100644 lib/bt/host/nimble/nimble/porting/npl/nuttx/include/console/console.h create mode 100644 lib/bt/host/nimble/nimble/porting/npl/nuttx/include/modlog/modlog.h create mode 100644 lib/bt/host/nimble/nimble/porting/npl/nuttx/include/nimble/nimble_npl_os.h create mode 100644 lib/bt/host/nimble/nimble/porting/npl/nuttx/include/nimble/os_types.h create mode 100644 lib/bt/host/nimble/nimble/porting/npl/nuttx/src/os_atomic.c create mode 100644 lib/bt/host/nimble/nimble/porting/npl/nuttx/src/os_callout.c create mode 100644 lib/bt/host/nimble/nimble/porting/npl/nuttx/src/os_eventq.c create mode 100644 lib/bt/host/nimble/nimble/porting/npl/nuttx/src/os_mutex.c create mode 100644 lib/bt/host/nimble/nimble/porting/npl/nuttx/src/os_sem.c create mode 100644 lib/bt/host/nimble/nimble/porting/npl/nuttx/src/os_task.c create mode 100644 lib/bt/host/nimble/nimble/porting/npl/nuttx/src/os_time.c create mode 100644 lib/bt/host/nimble/nimble/porting/npl/nuttx/src/wqueue.h create mode 100644 lib/bt/host/nimble/nimble/porting/npl/nuttx/test/Make.defs create mode 100644 lib/bt/host/nimble/nimble/porting/npl/nuttx/test/test_npl_callout.c create mode 100644 lib/bt/host/nimble/nimble/porting/npl/nuttx/test/test_npl_eventq.c create mode 100644 lib/bt/host/nimble/nimble/porting/npl/nuttx/test/test_npl_mempool.c create mode 100644 lib/bt/host/nimble/nimble/porting/npl/nuttx/test/test_npl_sem.c create mode 100644 lib/bt/host/nimble/nimble/porting/npl/nuttx/test/test_npl_task.c create mode 100644 lib/bt/host/nimble/nimble/porting/npl/nuttx/test/test_util.h create mode 100644 lib/bt/host/nimble/nimble/porting/npl/riot/include/logcfg/logcfg.h create mode 100644 lib/bt/host/nimble/nimble/porting/npl/riot/include/nimble/nimble_npl_os.h create mode 120000 lib/bt/host/nimble/nimble/porting/npl/riot/include/npl_syscfg/npl_sycfg.h create mode 100644 lib/bt/host/nimble/nimble/porting/npl/riot/include/syscfg/syscfg.h create mode 100644 lib/bt/host/nimble/nimble/porting/npl/riot/include/sysflash/sysflash.h create mode 100644 lib/bt/host/nimble/nimble/porting/npl/riot/src/npl_os_riot.c create mode 100644 lib/bt/host/nimble/nimble/porting/npl/riot/src/nrf5x_isr.c create mode 100644 lib/bt/host/nimble/nimble/porting/targets/dummy_app/pkg.yml create mode 100644 lib/bt/host/nimble/nimble/porting/targets/dummy_app/src/dummy.c create mode 100644 lib/bt/host/nimble/nimble/porting/targets/linux/pkg.yml create mode 100644 lib/bt/host/nimble/nimble/porting/targets/linux/syscfg.yml create mode 100644 lib/bt/host/nimble/nimble/porting/targets/linux/target.yml create mode 100644 lib/bt/host/nimble/nimble/porting/targets/linux_blemesh/pkg.yml create mode 100644 lib/bt/host/nimble/nimble/porting/targets/linux_blemesh/syscfg.yml create mode 100644 lib/bt/host/nimble/nimble/porting/targets/linux_blemesh/target.yml create mode 100644 lib/bt/host/nimble/nimble/porting/targets/nuttx/pkg.yml create mode 100644 lib/bt/host/nimble/nimble/porting/targets/nuttx/syscfg.yml create mode 100644 lib/bt/host/nimble/nimble/porting/targets/nuttx/target.yml create mode 100644 lib/bt/host/nimble/nimble/porting/targets/porting_default/pkg.yml create mode 100644 lib/bt/host/nimble/nimble/porting/targets/porting_default/syscfg.yml create mode 100644 lib/bt/host/nimble/nimble/porting/targets/porting_default/target.yml create mode 100644 lib/bt/host/nimble/nimble/porting/targets/riot/pkg.yml create mode 100644 lib/bt/host/nimble/nimble/porting/targets/riot/syscfg.yml create mode 100644 lib/bt/host/nimble/nimble/porting/targets/riot/target.yml create mode 100755 lib/bt/host/nimble/nimble/porting/update_generated_files.sh create mode 100644 lib/bt/host/nimble/nimble/repository.yml create mode 100644 lib/bt/host/nimble/nimble/sbom.yml create mode 100644 lib/bt/host/nimble/nimble/targets/dialog_cmac/pkg.yml create mode 100644 lib/bt/host/nimble/nimble/targets/dialog_cmac/syscfg.yml create mode 100644 lib/bt/host/nimble/nimble/targets/dialog_cmac/target.yml create mode 100644 lib/bt/host/nimble/nimble/targets/nordic_pca10056-blehci-usb/pkg.yml create mode 100644 lib/bt/host/nimble/nimble/targets/nordic_pca10056-blehci-usb/syscfg.yml create mode 100644 lib/bt/host/nimble/nimble/targets/nordic_pca10056-blehci-usb/target.yml create mode 100644 lib/bt/host/nimble/nimble/targets/nordic_pca10095_net-blehci/pkg.yml create mode 100644 lib/bt/host/nimble/nimble/targets/nordic_pca10095_net-blehci/syscfg.yml create mode 100644 lib/bt/host/nimble/nimble/targets/nordic_pca10095_net-blehci/target.yml create mode 100644 lib/bt/host/nimble/nimble/targets/unittest/pkg.yml create mode 100644 lib/bt/host/nimble/nimble/targets/unittest/target.yml create mode 100644 lib/bt/host/nimble/nimble/uncrustify.cfg create mode 100644 lib/bt/host/nimble/nimble/version.yml create mode 100644 lib/bt/host/nimble/port/include/console/console.h create mode 100644 lib/bt/host/nimble/port/include/esp_nimble_cfg.h create mode 100644 lib/bt/host/nimble/port/include/esp_nimble_mem.h create mode 100644 lib/bt/host/nimble/port/src/nvs_port.c (limited to 'lib/bt/host') diff --git a/lib/bt/host/bluedroid/Kconfig.in b/lib/bt/host/bluedroid/Kconfig.in new file mode 100644 index 00000000..4ef80cff --- /dev/null +++ b/lib/bt/host/bluedroid/Kconfig.in @@ -0,0 +1,1195 @@ +config BT_BTC_TASK_STACK_SIZE + int "Bluetooth event (callback to application) task stack size" + depends on BT_BLUEDROID_ENABLED + default 3072 + help + This select btc task stack size + +choice BT_BLUEDROID_PINNED_TO_CORE_CHOICE + prompt "The cpu core which Bluedroid run" + depends on BT_BLUEDROID_ENABLED && !FREERTOS_UNICORE + help + Which the cpu core to run Bluedroid. Can choose core0 and core1. + Can not specify no-affinity. + + config BT_BLUEDROID_PINNED_TO_CORE_0 + bool "Core 0 (PRO CPU)" + config BT_BLUEDROID_PINNED_TO_CORE_1 + bool "Core 1 (APP CPU)" + depends on !FREERTOS_UNICORE +endchoice + +config BT_BLUEDROID_PINNED_TO_CORE + int + depends on BT_BLUEDROID_ENABLED + default 0 if BT_BLUEDROID_PINNED_TO_CORE_0 + default 1 if BT_BLUEDROID_PINNED_TO_CORE_1 + default 0 + +config BT_BTU_TASK_STACK_SIZE + int "Bluetooth Bluedroid Host Stack task stack size" + depends on BT_BLUEDROID_ENABLED + default 4352 + help + This select btu task stack size + +config BT_BLUEDROID_MEM_DEBUG + bool "Bluedroid memory debug" + depends on BT_BLUEDROID_ENABLED + default n + help + Bluedroid memory debug + +config BT_BLUEDROID_ESP_COEX_VSC + bool "Enable Espressif Vendor-specific HCI commands for coexist status configuration" + depends on BT_BLUEDROID_ENABLED + default y + help + Enable Espressif Vendor-specific HCI commands for coexist status configuration + +config BT_CLASSIC_ENABLED + bool "Classic Bluetooth" + depends on BT_BLUEDROID_ENABLED && ((BT_CONTROLLER_ENABLED && SOC_BT_CLASSIC_SUPPORTED) || BT_CONTROLLER_DISABLED) + default n + help + For now this option needs "SMP_ENABLE" to be set to yes + +config BT_CLASSIC_BQB_ENABLED + bool "Host Qualitifcation support for Classic Bluetooth" + depends on BT_CLASSIC_ENABLED + default n + help + This enables functionalities of Host qualification for Classic Bluetooth. + +config BT_A2DP_ENABLE + bool "A2DP" + depends on BT_CLASSIC_ENABLED + default n + help + Advanced Audio Distrubution Profile + +config BT_SPP_ENABLED + bool "SPP" + depends on BT_CLASSIC_ENABLED + default n + help + This enables the Serial Port Profile + +config BT_L2CAP_ENABLED + bool "BT L2CAP" + depends on BT_CLASSIC_ENABLED + default n + help + This enables the Logical Link Control and Adaptation Layer Protocol. + Only supported classic bluetooth. + +menuconfig BT_HFP_ENABLE + bool "Hands Free/Handset Profile" + depends on BT_CLASSIC_ENABLED + default n + help + Hands Free Unit and Audio Gateway can be included simultaneously + but they cannot run simultaneously due to internal limitations. + +config BT_HFP_CLIENT_ENABLE + bool "Hands Free Unit" + depends on BT_HFP_ENABLE + default y + +config BT_HFP_AG_ENABLE + bool "Audio Gateway" + depends on BT_HFP_ENABLE + default y + +choice BT_HFP_AUDIO_DATA_PATH + prompt "audio(SCO) data path" + depends on BT_HFP_ENABLE + help + SCO data path, i.e. HCI or PCM. This option is set using API + "esp_bredr_sco_datapath_set" in Bluetooth host. Default SCO data + path can also be set in Bluetooth Controller. + + config BT_HFP_AUDIO_DATA_PATH_PCM + bool "PCM" + config BT_HFP_AUDIO_DATA_PATH_HCI + bool "HCI" +endchoice + +config BT_HFP_WBS_ENABLE + bool "Wide Band Speech" + depends on BT_HFP_AUDIO_DATA_PATH_HCI + default y + help + This enables Wide Band Speech. Should disable it when SCO data path is PCM. + Otherwise there will be no data transmited via GPIOs. + + +menuconfig BT_HID_ENABLED + bool "Classic BT HID" + depends on BT_CLASSIC_ENABLED + default n + help + This enables the BT HID Host + +config BT_HID_HOST_ENABLED + bool "Classic BT HID Host" + depends on BT_HID_ENABLED + default n + help + This enables the BT HID Host + +config BT_HID_DEVICE_ENABLED + bool "Classic BT HID Device" + depends on BT_HID_ENABLED + help + This enables the BT HID Device + +config BT_BLE_ENABLED + bool "Bluetooth Low Energy" + depends on BT_BLUEDROID_ENABLED + default y + help + This enables Bluetooth Low Energy + +config BT_GATTS_ENABLE + bool "Include GATT server module(GATTS)" + depends on BT_BLE_ENABLED + default y + help + This option can be disabled when the app work only on gatt client mode + +config BT_GATTS_PPCP_CHAR_GAP + bool "Enable Peripheral Preferred Connection Parameters characteristic in GAP service" + depends on BT_GATTS_ENABLE + default n + help + This enables "Peripheral Preferred Connection Parameters" characteristic (UUID: 0x2A04) in GAP service that has + connection parameters like min/max connection interval, slave latency and supervision timeout multiplier + +config BT_BLE_BLUFI_ENABLE + bool "Include blufi function" + depends on BT_GATTS_ENABLE + default n + help + This option can be close when the app does not require blufi function. + +config BT_GATT_MAX_SR_PROFILES + int "Max GATT Server Profiles" + depends on BT_GATTS_ENABLE && BT_BLUEDROID_ENABLED + range 1 32 + default 8 + help + Maximum GATT Server Profiles Count + +config BT_GATT_MAX_SR_ATTRIBUTES + int "Max GATT Service Attributes" + depends on BT_GATTS_ENABLE && BT_BLUEDROID_ENABLED + range 1 500 + default 100 + help + Maximum GATT Service Attributes Count + + +choice BT_GATTS_SEND_SERVICE_CHANGE_MODE + prompt "GATTS Service Change Mode" + default BT_GATTS_SEND_SERVICE_CHANGE_AUTO + depends on BT_GATTS_ENABLE + help + Service change indication mode for GATT Server. + + config BT_GATTS_SEND_SERVICE_CHANGE_MANUAL + bool "GATTS manually send service change indication" + help + Manually send service change indication through API esp_ble_gatts_send_service_change_indication() + + config BT_GATTS_SEND_SERVICE_CHANGE_AUTO + bool "GATTS automatically send service change indication" + help + Let Bluedroid handle the service change indication internally + +endchoice + +config BT_GATTS_SEND_SERVICE_CHANGE_MODE + int + depends on BT_GATTS_ENABLE + default 0 if BT_GATTS_SEND_SERVICE_CHANGE_AUTO + default 1 if BT_GATTS_SEND_SERVICE_CHANGE_MANUAL + default 0 + +config BT_GATTS_ROBUST_CACHING_ENABLED + bool "Enable Robust Caching on Server Side" + depends on BT_GATTS_ENABLE + default n + help + This option enables the GATT robust caching feature on the server. + if turned on, the Client Supported Features characteristic, Database Hash characteristic, + and Server Supported Features characteristic will be included in the GAP SERVICE. + + +config BT_GATTS_DEVICE_NAME_WRITABLE + bool "Allow to write device name by GATT clients" + depends on BT_GATTS_ENABLE + default n + help + Enabling this option allows remote GATT clients to write device name + +config BT_GATTS_APPEARANCE_WRITABLE + bool "Allow to write appearance by GATT clients" + depends on BT_GATTS_ENABLE + default n + help + Enabling this option allows remote GATT clients to write appearance + +config BT_GATTC_ENABLE + bool "Include GATT client module(GATTC)" + depends on BT_BLE_ENABLED + default y + help + This option can be close when the app work only on gatt server mode + +config BT_GATTC_MAX_CACHE_CHAR + int "Max gattc cache characteristic for discover" + depends on BT_GATTC_ENABLE + range 1 500 + default 40 + help + Maximum GATTC cache characteristic count + +config BT_GATTC_NOTIF_REG_MAX + int "Max gattc notify(indication) register number" + depends on BT_GATTC_ENABLE + range 1 64 + default 5 + help + Maximum GATTC notify(indication) register number + +config BT_GATTC_CACHE_NVS_FLASH + bool "Save gattc cache data to nvs flash" + depends on BT_GATTC_ENABLE + default n + help + This select can save gattc cache data to nvs flash + +config BT_GATTC_CONNECT_RETRY_COUNT + int "The number of attempts to reconnect if the connection establishment failed" + depends on BT_GATTC_ENABLE + range 0 7 + default 3 + help + The number of attempts to reconnect if the connection establishment failed + +config BT_BLE_SMP_ENABLE + bool "Include BLE security module(SMP)" + depends on BT_BLE_ENABLED + default y + help + This option can be close when the app not used the ble security connect. + +config BT_SMP_SLAVE_CON_PARAMS_UPD_ENABLE + bool "Slave enable connection parameters update during pairing" + depends on BT_BLE_SMP_ENABLE + default n + help + In order to reduce the pairing time, slave actively initiates connection parameters + update during pairing. + +config BT_STACK_NO_LOG + bool "Disable BT debug logs (minimize bin size)" + depends on BT_BLUEDROID_ENABLED + default n + help + This select can save the rodata code size + +menu "BT DEBUG LOG LEVEL" + depends on BT_BLUEDROID_ENABLED && !BT_STACK_NO_LOG + + choice BT_LOG_HCI_TRACE_LEVEL + prompt "HCI layer" + default BT_LOG_HCI_TRACE_LEVEL_WARNING + depends on BT_BLUEDROID_ENABLED && !BT_STACK_NO_LOG + help + Define BT trace level for HCI layer + + config BT_LOG_HCI_TRACE_LEVEL_NONE + bool "NONE" + config BT_LOG_HCI_TRACE_LEVEL_ERROR + bool "ERROR" + config BT_LOG_HCI_TRACE_LEVEL_WARNING + bool "WARNING" + config BT_LOG_HCI_TRACE_LEVEL_API + bool "API" + config BT_LOG_HCI_TRACE_LEVEL_EVENT + bool "EVENT" + config BT_LOG_HCI_TRACE_LEVEL_DEBUG + bool "DEBUG" + config BT_LOG_HCI_TRACE_LEVEL_VERBOSE + bool "VERBOSE" + endchoice + + config BT_LOG_HCI_TRACE_LEVEL + int + depends on BT_BLUEDROID_ENABLED + default 0 if BT_LOG_HCI_TRACE_LEVEL_NONE + default 1 if BT_LOG_HCI_TRACE_LEVEL_ERROR + default 2 if BT_LOG_HCI_TRACE_LEVEL_WARNING + default 3 if BT_LOG_HCI_TRACE_LEVEL_API + default 4 if BT_LOG_HCI_TRACE_LEVEL_EVENT + default 5 if BT_LOG_HCI_TRACE_LEVEL_DEBUG + default 6 if BT_LOG_HCI_TRACE_LEVEL_VERBOSE + default 2 + + choice BT_LOG_BTM_TRACE_LEVEL + prompt "BTM layer" + default BT_LOG_BTM_TRACE_LEVEL_WARNING + depends on BT_BLUEDROID_ENABLED && !BT_STACK_NO_LOG + help + Define BT trace level for BTM layer + + config BT_LOG_BTM_TRACE_LEVEL_NONE + bool "NONE" + config BT_LOG_BTM_TRACE_LEVEL_ERROR + bool "ERROR" + config BT_LOG_BTM_TRACE_LEVEL_WARNING + bool "WARNING" + config BT_LOG_BTM_TRACE_LEVEL_API + bool "API" + config BT_LOG_BTM_TRACE_LEVEL_EVENT + bool "EVENT" + config BT_LOG_BTM_TRACE_LEVEL_DEBUG + bool "DEBUG" + config BT_LOG_BTM_TRACE_LEVEL_VERBOSE + bool "VERBOSE" + endchoice + + config BT_LOG_BTM_TRACE_LEVEL + int + depends on BT_BLUEDROID_ENABLED + default 0 if BT_LOG_BTM_TRACE_LEVEL_NONE + default 1 if BT_LOG_BTM_TRACE_LEVEL_ERROR + default 2 if BT_LOG_BTM_TRACE_LEVEL_WARNING + default 3 if BT_LOG_BTM_TRACE_LEVEL_API + default 4 if BT_LOG_BTM_TRACE_LEVEL_EVENT + default 5 if BT_LOG_BTM_TRACE_LEVEL_DEBUG + default 6 if BT_LOG_BTM_TRACE_LEVEL_VERBOSE + default 2 + + choice BT_LOG_L2CAP_TRACE_LEVEL + prompt "L2CAP layer" + default BT_LOG_L2CAP_TRACE_LEVEL_WARNING + depends on BT_BLUEDROID_ENABLED && !BT_STACK_NO_LOG + help + Define BT trace level for L2CAP layer + + config BT_LOG_L2CAP_TRACE_LEVEL_NONE + bool "NONE" + config BT_LOG_L2CAP_TRACE_LEVEL_ERROR + bool "ERROR" + config BT_LOG_L2CAP_TRACE_LEVEL_WARNING + bool "WARNING" + config BT_LOG_L2CAP_TRACE_LEVEL_API + bool "API" + config BT_LOG_L2CAP_TRACE_LEVEL_EVENT + bool "EVENT" + config BT_LOG_L2CAP_TRACE_LEVEL_DEBUG + bool "DEBUG" + config BT_LOG_L2CAP_TRACE_LEVEL_VERBOSE + bool "VERBOSE" + endchoice + + config BT_LOG_L2CAP_TRACE_LEVEL + int + depends on BT_BLUEDROID_ENABLED + default 0 if BT_LOG_L2CAP_TRACE_LEVEL_NONE + default 1 if BT_LOG_L2CAP_TRACE_LEVEL_ERROR + default 2 if BT_LOG_L2CAP_TRACE_LEVEL_WARNING + default 3 if BT_LOG_L2CAP_TRACE_LEVEL_API + default 4 if BT_LOG_L2CAP_TRACE_LEVEL_EVENT + default 5 if BT_LOG_L2CAP_TRACE_LEVEL_DEBUG + default 6 if BT_LOG_L2CAP_TRACE_LEVEL_VERBOSE + default 2 + + choice BT_LOG_RFCOMM_TRACE_LEVEL + prompt "RFCOMM layer" + default BT_LOG_RFCOMM_TRACE_LEVEL_WARNING + depends on BT_BLUEDROID_ENABLED && !BT_STACK_NO_LOG + help + Define BT trace level for RFCOMM layer + + config BT_LOG_RFCOMM_TRACE_LEVEL_NONE + bool "NONE" + config BT_LOG_RFCOMM_TRACE_LEVEL_ERROR + bool "ERROR" + config BT_LOG_RFCOMM_TRACE_LEVEL_WARNING + bool "WARNING" + config BT_LOG_RFCOMM_TRACE_LEVEL_API + bool "API" + config BT_LOG_RFCOMM_TRACE_LEVEL_EVENT + bool "EVENT" + config BT_LOG_RFCOMM_TRACE_LEVEL_DEBUG + bool "DEBUG" + config BT_LOG_RFCOMM_TRACE_LEVEL_VERBOSE + bool "VERBOSE" + endchoice + + config BT_LOG_RFCOMM_TRACE_LEVEL + int + depends on BT_BLUEDROID_ENABLED + default 0 if BT_LOG_RFCOMM_TRACE_LEVEL_NONE + default 1 if BT_LOG_RFCOMM_TRACE_LEVEL_ERROR + default 2 if BT_LOG_RFCOMM_TRACE_LEVEL_WARNING + default 3 if BT_LOG_RFCOMM_TRACE_LEVEL_API + default 4 if BT_LOG_RFCOMM_TRACE_LEVEL_EVENT + default 5 if BT_LOG_RFCOMM_TRACE_LEVEL_DEBUG + default 6 if BT_LOG_RFCOMM_TRACE_LEVEL_VERBOSE + default 2 + + choice BT_LOG_SDP_TRACE_LEVEL + prompt "SDP layer" + default BT_LOG_SDP_TRACE_LEVEL_WARNING + depends on BT_BLUEDROID_ENABLED && !BT_STACK_NO_LOG + help + Define BT trace level for SDP layer + + config BT_LOG_SDP_TRACE_LEVEL_NONE + bool "NONE" + config BT_LOG_SDP_TRACE_LEVEL_ERROR + bool "ERROR" + config BT_LOG_SDP_TRACE_LEVEL_WARNING + bool "WARNING" + config BT_LOG_SDP_TRACE_LEVEL_API + bool "API" + config BT_LOG_SDP_TRACE_LEVEL_EVENT + bool "EVENT" + config BT_LOG_SDP_TRACE_LEVEL_DEBUG + bool "DEBUG" + config BT_LOG_SDP_TRACE_LEVEL_VERBOSE + bool "VERBOSE" + endchoice + + config BT_LOG_SDP_TRACE_LEVEL + int + depends on BT_BLUEDROID_ENABLED + default 0 if BT_LOG_SDP_TRACE_LEVEL_NONE + default 1 if BT_LOG_SDP_TRACE_LEVEL_ERROR + default 2 if BT_LOG_SDP_TRACE_LEVEL_WARNING + default 3 if BT_LOG_SDP_TRACE_LEVEL_API + default 4 if BT_LOG_SDP_TRACE_LEVEL_EVENT + default 5 if BT_LOG_SDP_TRACE_LEVEL_DEBUG + default 6 if BT_LOG_SDP_TRACE_LEVEL_VERBOSE + default 2 + + choice BT_LOG_GAP_TRACE_LEVEL + prompt "GAP layer" + default BT_LOG_GAP_TRACE_LEVEL_WARNING + depends on BT_BLUEDROID_ENABLED && !BT_STACK_NO_LOG + help + Define BT trace level for GAP layer + + config BT_LOG_GAP_TRACE_LEVEL_NONE + bool "NONE" + config BT_LOG_GAP_TRACE_LEVEL_ERROR + bool "ERROR" + config BT_LOG_GAP_TRACE_LEVEL_WARNING + bool "WARNING" + config BT_LOG_GAP_TRACE_LEVEL_API + bool "API" + config BT_LOG_GAP_TRACE_LEVEL_EVENT + bool "EVENT" + config BT_LOG_GAP_TRACE_LEVEL_DEBUG + bool "DEBUG" + config BT_LOG_GAP_TRACE_LEVEL_VERBOSE + bool "VERBOSE" + endchoice + + config BT_LOG_GAP_TRACE_LEVEL + int + depends on BT_BLUEDROID_ENABLED + default 0 if BT_LOG_GAP_TRACE_LEVEL_NONE + default 1 if BT_LOG_GAP_TRACE_LEVEL_ERROR + default 2 if BT_LOG_GAP_TRACE_LEVEL_WARNING + default 3 if BT_LOG_GAP_TRACE_LEVEL_API + default 4 if BT_LOG_GAP_TRACE_LEVEL_EVENT + default 5 if BT_LOG_GAP_TRACE_LEVEL_DEBUG + default 6 if BT_LOG_GAP_TRACE_LEVEL_VERBOSE + default 2 + + choice BT_LOG_BNEP_TRACE_LEVEL + prompt "BNEP layer" + default BT_LOG_BNEP_TRACE_LEVEL_WARNING + depends on BT_BLUEDROID_ENABLED && !BT_STACK_NO_LOG + help + Define BT trace level for BNEP layer + + config BT_LOG_BNEP_TRACE_LEVEL_NONE + bool "NONE" + config BT_LOG_BNEP_TRACE_LEVEL_ERROR + bool "ERROR" + config BT_LOG_BNEP_TRACE_LEVEL_WARNING + bool "WARNING" + config BT_LOG_BNEP_TRACE_LEVEL_API + bool "API" + config BT_LOG_BNEP_TRACE_LEVEL_EVENT + bool "EVENT" + config BT_LOG_BNEP_TRACE_LEVEL_DEBUG + bool "DEBUG" + config BT_LOG_BNEP_TRACE_LEVEL_VERBOSE + bool "VERBOSE" + endchoice + + config BT_LOG_BNEP_TRACE_LEVEL + int + depends on BT_BLUEDROID_ENABLED + default 0 if BT_LOG_BNEP_TRACE_LEVEL_NONE + default 1 if BT_LOG_BNEP_TRACE_LEVEL_ERROR + default 2 if BT_LOG_BNEP_TRACE_LEVEL_WARNING + default 3 if BT_LOG_BNEP_TRACE_LEVEL_API + default 4 if BT_LOG_BNEP_TRACE_LEVEL_EVENT + default 5 if BT_LOG_BNEP_TRACE_LEVEL_DEBUG + default 6 if BT_LOG_BNEP_TRACE_LEVEL_VERBOSE + default 2 + + choice BT_LOG_PAN_TRACE_LEVEL + prompt "PAN layer" + default BT_LOG_PAN_TRACE_LEVEL_WARNING + depends on BT_BLUEDROID_ENABLED && !BT_STACK_NO_LOG + help + Define BT trace level for PAN layer + + config BT_LOG_PAN_TRACE_LEVEL_NONE + bool "NONE" + config BT_LOG_PAN_TRACE_LEVEL_ERROR + bool "ERROR" + config BT_LOG_PAN_TRACE_LEVEL_WARNING + bool "WARNING" + config BT_LOG_PAN_TRACE_LEVEL_API + bool "API" + config BT_LOG_PAN_TRACE_LEVEL_EVENT + bool "EVENT" + config BT_LOG_PAN_TRACE_LEVEL_DEBUG + bool "DEBUG" + config BT_LOG_PAN_TRACE_LEVEL_VERBOSE + bool "VERBOSE" + endchoice + + config BT_LOG_PAN_TRACE_LEVEL + int + depends on BT_BLUEDROID_ENABLED + default 0 if BT_LOG_PAN_TRACE_LEVEL_NONE + default 1 if BT_LOG_PAN_TRACE_LEVEL_ERROR + default 2 if BT_LOG_PAN_TRACE_LEVEL_WARNING + default 3 if BT_LOG_PAN_TRACE_LEVEL_API + default 4 if BT_LOG_PAN_TRACE_LEVEL_EVENT + default 5 if BT_LOG_PAN_TRACE_LEVEL_DEBUG + default 6 if BT_LOG_PAN_TRACE_LEVEL_VERBOSE + default 2 + + choice BT_LOG_A2D_TRACE_LEVEL + prompt "A2D layer" + default BT_LOG_A2D_TRACE_LEVEL_WARNING + depends on BT_BLUEDROID_ENABLED && !BT_STACK_NO_LOG + help + Define BT trace level for A2D layer + + config BT_LOG_A2D_TRACE_LEVEL_NONE + bool "NONE" + config BT_LOG_A2D_TRACE_LEVEL_ERROR + bool "ERROR" + config BT_LOG_A2D_TRACE_LEVEL_WARNING + bool "WARNING" + config BT_LOG_A2D_TRACE_LEVEL_API + bool "API" + config BT_LOG_A2D_TRACE_LEVEL_EVENT + bool "EVENT" + config BT_LOG_A2D_TRACE_LEVEL_DEBUG + bool "DEBUG" + config BT_LOG_A2D_TRACE_LEVEL_VERBOSE + bool "VERBOSE" + endchoice + + config BT_LOG_A2D_TRACE_LEVEL + int + depends on BT_BLUEDROID_ENABLED + default 0 if BT_LOG_A2D_TRACE_LEVEL_NONE + default 1 if BT_LOG_A2D_TRACE_LEVEL_ERROR + default 2 if BT_LOG_A2D_TRACE_LEVEL_WARNING + default 3 if BT_LOG_A2D_TRACE_LEVEL_API + default 4 if BT_LOG_A2D_TRACE_LEVEL_EVENT + default 5 if BT_LOG_A2D_TRACE_LEVEL_DEBUG + default 6 if BT_LOG_A2D_TRACE_LEVEL_VERBOSE + default 2 + + choice BT_LOG_AVDT_TRACE_LEVEL + prompt "AVDT layer" + default BT_LOG_AVDT_TRACE_LEVEL_WARNING + depends on BT_BLUEDROID_ENABLED && !BT_STACK_NO_LOG + help + Define BT trace level for AVDT layer + + config BT_LOG_AVDT_TRACE_LEVEL_NONE + bool "NONE" + config BT_LOG_AVDT_TRACE_LEVEL_ERROR + bool "ERROR" + config BT_LOG_AVDT_TRACE_LEVEL_WARNING + bool "WARNING" + config BT_LOG_AVDT_TRACE_LEVEL_API + bool "API" + config BT_LOG_AVDT_TRACE_LEVEL_EVENT + bool "EVENT" + config BT_LOG_AVDT_TRACE_LEVEL_DEBUG + bool "DEBUG" + config BT_LOG_AVDT_TRACE_LEVEL_VERBOSE + bool "VERBOSE" + endchoice + + config BT_LOG_AVDT_TRACE_LEVEL + int + depends on BT_BLUEDROID_ENABLED + default 0 if BT_LOG_AVDT_TRACE_LEVEL_NONE + default 1 if BT_LOG_AVDT_TRACE_LEVEL_ERROR + default 2 if BT_LOG_AVDT_TRACE_LEVEL_WARNING + default 3 if BT_LOG_AVDT_TRACE_LEVEL_API + default 4 if BT_LOG_AVDT_TRACE_LEVEL_EVENT + default 5 if BT_LOG_AVDT_TRACE_LEVEL_DEBUG + default 6 if BT_LOG_AVDT_TRACE_LEVEL_VERBOSE + default 2 + + choice BT_LOG_AVCT_TRACE_LEVEL + prompt "AVCT layer" + default BT_LOG_AVCT_TRACE_LEVEL_WARNING + depends on BT_BLUEDROID_ENABLED && !BT_STACK_NO_LOG + help + Define BT trace level for AVCT layer + + config BT_LOG_AVCT_TRACE_LEVEL_NONE + bool "NONE" + config BT_LOG_AVCT_TRACE_LEVEL_ERROR + bool "ERROR" + config BT_LOG_AVCT_TRACE_LEVEL_WARNING + bool "WARNING" + config BT_LOG_AVCT_TRACE_LEVEL_API + bool "API" + config BT_LOG_AVCT_TRACE_LEVEL_EVENT + bool "EVENT" + config BT_LOG_AVCT_TRACE_LEVEL_DEBUG + bool "DEBUG" + config BT_LOG_AVCT_TRACE_LEVEL_VERBOSE + bool "VERBOSE" + endchoice + + config BT_LOG_AVCT_TRACE_LEVEL + int + depends on BT_BLUEDROID_ENABLED + default 0 if BT_LOG_AVCT_TRACE_LEVEL_NONE + default 1 if BT_LOG_AVCT_TRACE_LEVEL_ERROR + default 2 if BT_LOG_AVCT_TRACE_LEVEL_WARNING + default 3 if BT_LOG_AVCT_TRACE_LEVEL_API + default 4 if BT_LOG_AVCT_TRACE_LEVEL_EVENT + default 5 if BT_LOG_AVCT_TRACE_LEVEL_DEBUG + default 6 if BT_LOG_AVCT_TRACE_LEVEL_VERBOSE + default 2 + + choice BT_LOG_AVRC_TRACE_LEVEL + prompt "AVRC layer" + default BT_LOG_AVRC_TRACE_LEVEL_WARNING + depends on BT_BLUEDROID_ENABLED && !BT_STACK_NO_LOG + help + Define BT trace level for AVRC layer + + config BT_LOG_AVRC_TRACE_LEVEL_NONE + bool "NONE" + config BT_LOG_AVRC_TRACE_LEVEL_ERROR + bool "ERROR" + config BT_LOG_AVRC_TRACE_LEVEL_WARNING + bool "WARNING" + config BT_LOG_AVRC_TRACE_LEVEL_API + bool "API" + config BT_LOG_AVRC_TRACE_LEVEL_EVENT + bool "EVENT" + config BT_LOG_AVRC_TRACE_LEVEL_DEBUG + bool "DEBUG" + config BT_LOG_AVRC_TRACE_LEVEL_VERBOSE + bool "VERBOSE" + endchoice + + config BT_LOG_AVRC_TRACE_LEVEL + int + depends on BT_BLUEDROID_ENABLED + default 0 if BT_LOG_AVRC_TRACE_LEVEL_NONE + default 1 if BT_LOG_AVRC_TRACE_LEVEL_ERROR + default 2 if BT_LOG_AVRC_TRACE_LEVEL_WARNING + default 3 if BT_LOG_AVRC_TRACE_LEVEL_API + default 4 if BT_LOG_AVRC_TRACE_LEVEL_EVENT + default 5 if BT_LOG_AVRC_TRACE_LEVEL_DEBUG + default 6 if BT_LOG_AVRC_TRACE_LEVEL_VERBOSE + default 2 + + choice BT_LOG_MCA_TRACE_LEVEL + prompt "MCA layer" + default BT_LOG_MCA_TRACE_LEVEL_WARNING + depends on BT_BLUEDROID_ENABLED && !BT_STACK_NO_LOG + help + Define BT trace level for MCA layer + + config BT_LOG_MCA_TRACE_LEVEL_NONE + bool "NONE" + config BT_LOG_MCA_TRACE_LEVEL_ERROR + bool "ERROR" + config BT_LOG_MCA_TRACE_LEVEL_WARNING + bool "WARNING" + config BT_LOG_MCA_TRACE_LEVEL_API + bool "API" + config BT_LOG_MCA_TRACE_LEVEL_EVENT + bool "EVENT" + config BT_LOG_MCA_TRACE_LEVEL_DEBUG + bool "DEBUG" + config BT_LOG_MCA_TRACE_LEVEL_VERBOSE + bool "VERBOSE" + endchoice + + config BT_LOG_MCA_TRACE_LEVEL + int + depends on BT_BLUEDROID_ENABLED + default 0 if BT_LOG_MCA_TRACE_LEVEL_NONE + default 1 if BT_LOG_MCA_TRACE_LEVEL_ERROR + default 2 if BT_LOG_MCA_TRACE_LEVEL_WARNING + default 3 if BT_LOG_MCA_TRACE_LEVEL_API + default 4 if BT_LOG_MCA_TRACE_LEVEL_EVENT + default 5 if BT_LOG_MCA_TRACE_LEVEL_DEBUG + default 6 if BT_LOG_MCA_TRACE_LEVEL_VERBOSE + default 2 + + choice BT_LOG_HID_TRACE_LEVEL + prompt "HID layer" + default BT_LOG_HID_TRACE_LEVEL_WARNING + depends on BT_BLUEDROID_ENABLED && !BT_STACK_NO_LOG + help + Define BT trace level for HID layer + + config BT_LOG_HID_TRACE_LEVEL_NONE + bool "NONE" + config BT_LOG_HID_TRACE_LEVEL_ERROR + bool "ERROR" + config BT_LOG_HID_TRACE_LEVEL_WARNING + bool "WARNING" + config BT_LOG_HID_TRACE_LEVEL_API + bool "API" + config BT_LOG_HID_TRACE_LEVEL_EVENT + bool "EVENT" + config BT_LOG_HID_TRACE_LEVEL_DEBUG + bool "DEBUG" + config BT_LOG_HID_TRACE_LEVEL_VERBOSE + bool "VERBOSE" + endchoice + + config BT_LOG_HID_TRACE_LEVEL + int + depends on BT_BLUEDROID_ENABLED + default 0 if BT_LOG_HID_TRACE_LEVEL_NONE + default 1 if BT_LOG_HID_TRACE_LEVEL_ERROR + default 2 if BT_LOG_HID_TRACE_LEVEL_WARNING + default 3 if BT_LOG_HID_TRACE_LEVEL_API + default 4 if BT_LOG_HID_TRACE_LEVEL_EVENT + default 5 if BT_LOG_HID_TRACE_LEVEL_DEBUG + default 6 if BT_LOG_HID_TRACE_LEVEL_VERBOSE + default 2 + + choice BT_LOG_APPL_TRACE_LEVEL + prompt "APPL layer" + default BT_LOG_APPL_TRACE_LEVEL_WARNING + depends on BT_BLUEDROID_ENABLED && !BT_STACK_NO_LOG + help + Define BT trace level for APPL layer + + config BT_LOG_APPL_TRACE_LEVEL_NONE + bool "NONE" + config BT_LOG_APPL_TRACE_LEVEL_ERROR + bool "ERROR" + config BT_LOG_APPL_TRACE_LEVEL_WARNING + bool "WARNING" + config BT_LOG_APPL_TRACE_LEVEL_API + bool "API" + config BT_LOG_APPL_TRACE_LEVEL_EVENT + bool "EVENT" + config BT_LOG_APPL_TRACE_LEVEL_DEBUG + bool "DEBUG" + config BT_LOG_APPL_TRACE_LEVEL_VERBOSE + bool "VERBOSE" + endchoice + + config BT_LOG_APPL_TRACE_LEVEL + int + depends on BT_BLUEDROID_ENABLED + default 0 if BT_LOG_APPL_TRACE_LEVEL_NONE + default 1 if BT_LOG_APPL_TRACE_LEVEL_ERROR + default 2 if BT_LOG_APPL_TRACE_LEVEL_WARNING + default 3 if BT_LOG_APPL_TRACE_LEVEL_API + default 4 if BT_LOG_APPL_TRACE_LEVEL_EVENT + default 5 if BT_LOG_APPL_TRACE_LEVEL_DEBUG + default 6 if BT_LOG_APPL_TRACE_LEVEL_VERBOSE + default 2 + + choice BT_LOG_GATT_TRACE_LEVEL + prompt "GATT layer" + default BT_LOG_GATT_TRACE_LEVEL_WARNING + depends on BT_BLUEDROID_ENABLED && !BT_STACK_NO_LOG + help + Define BT trace level for GATT layer + + config BT_LOG_GATT_TRACE_LEVEL_NONE + bool "NONE" + config BT_LOG_GATT_TRACE_LEVEL_ERROR + bool "ERROR" + config BT_LOG_GATT_TRACE_LEVEL_WARNING + bool "WARNING" + config BT_LOG_GATT_TRACE_LEVEL_API + bool "API" + config BT_LOG_GATT_TRACE_LEVEL_EVENT + bool "EVENT" + config BT_LOG_GATT_TRACE_LEVEL_DEBUG + bool "DEBUG" + config BT_LOG_GATT_TRACE_LEVEL_VERBOSE + bool "VERBOSE" + endchoice + + config BT_LOG_GATT_TRACE_LEVEL + int + depends on BT_BLUEDROID_ENABLED + default 0 if BT_LOG_GATT_TRACE_LEVEL_NONE + default 1 if BT_LOG_GATT_TRACE_LEVEL_ERROR + default 2 if BT_LOG_GATT_TRACE_LEVEL_WARNING + default 3 if BT_LOG_GATT_TRACE_LEVEL_API + default 4 if BT_LOG_GATT_TRACE_LEVEL_EVENT + default 5 if BT_LOG_GATT_TRACE_LEVEL_DEBUG + default 6 if BT_LOG_GATT_TRACE_LEVEL_VERBOSE + default 2 + + choice BT_LOG_SMP_TRACE_LEVEL + prompt "SMP layer" + default BT_LOG_SMP_TRACE_LEVEL_WARNING + depends on BT_BLUEDROID_ENABLED && !BT_STACK_NO_LOG + help + Define BT trace level for SMP layer + + config BT_LOG_SMP_TRACE_LEVEL_NONE + bool "NONE" + config BT_LOG_SMP_TRACE_LEVEL_ERROR + bool "ERROR" + config BT_LOG_SMP_TRACE_LEVEL_WARNING + bool "WARNING" + config BT_LOG_SMP_TRACE_LEVEL_API + bool "API" + config BT_LOG_SMP_TRACE_LEVEL_EVENT + bool "EVENT" + config BT_LOG_SMP_TRACE_LEVEL_DEBUG + bool "DEBUG" + config BT_LOG_SMP_TRACE_LEVEL_VERBOSE + bool "VERBOSE" + endchoice + + config BT_LOG_SMP_TRACE_LEVEL + int + depends on BT_BLUEDROID_ENABLED + default 0 if BT_LOG_SMP_TRACE_LEVEL_NONE + default 1 if BT_LOG_SMP_TRACE_LEVEL_ERROR + default 2 if BT_LOG_SMP_TRACE_LEVEL_WARNING + default 3 if BT_LOG_SMP_TRACE_LEVEL_API + default 4 if BT_LOG_SMP_TRACE_LEVEL_EVENT + default 5 if BT_LOG_SMP_TRACE_LEVEL_DEBUG + default 6 if BT_LOG_SMP_TRACE_LEVEL_VERBOSE + default 2 + + choice BT_LOG_BTIF_TRACE_LEVEL + prompt "BTIF layer" + default BT_LOG_BTIF_TRACE_LEVEL_WARNING + depends on BT_BLUEDROID_ENABLED && !BT_STACK_NO_LOG + help + Define BT trace level for BTIF layer + + config BT_LOG_BTIF_TRACE_LEVEL_NONE + bool "NONE" + config BT_LOG_BTIF_TRACE_LEVEL_ERROR + bool "ERROR" + config BT_LOG_BTIF_TRACE_LEVEL_WARNING + bool "WARNING" + config BT_LOG_BTIF_TRACE_LEVEL_API + bool "API" + config BT_LOG_BTIF_TRACE_LEVEL_EVENT + bool "EVENT" + config BT_LOG_BTIF_TRACE_LEVEL_DEBUG + bool "DEBUG" + config BT_LOG_BTIF_TRACE_LEVEL_VERBOSE + bool "VERBOSE" + endchoice + + config BT_LOG_BTIF_TRACE_LEVEL + int + depends on BT_BLUEDROID_ENABLED + default 0 if BT_LOG_BTIF_TRACE_LEVEL_NONE + default 1 if BT_LOG_BTIF_TRACE_LEVEL_ERROR + default 2 if BT_LOG_BTIF_TRACE_LEVEL_WARNING + default 3 if BT_LOG_BTIF_TRACE_LEVEL_API + default 4 if BT_LOG_BTIF_TRACE_LEVEL_EVENT + default 5 if BT_LOG_BTIF_TRACE_LEVEL_DEBUG + default 6 if BT_LOG_BTIF_TRACE_LEVEL_VERBOSE + default 2 + + choice BT_LOG_BTC_TRACE_LEVEL + prompt "BTC layer" + default BT_LOG_BTC_TRACE_LEVEL_WARNING + depends on BT_BLUEDROID_ENABLED && !BT_STACK_NO_LOG + help + Define BT trace level for BTC layer + + config BT_LOG_BTC_TRACE_LEVEL_NONE + bool "NONE" + config BT_LOG_BTC_TRACE_LEVEL_ERROR + bool "ERROR" + config BT_LOG_BTC_TRACE_LEVEL_WARNING + bool "WARNING" + config BT_LOG_BTC_TRACE_LEVEL_API + bool "API" + config BT_LOG_BTC_TRACE_LEVEL_EVENT + bool "EVENT" + config BT_LOG_BTC_TRACE_LEVEL_DEBUG + bool "DEBUG" + config BT_LOG_BTC_TRACE_LEVEL_VERBOSE + bool "VERBOSE" + endchoice + + config BT_LOG_BTC_TRACE_LEVEL + int + depends on BT_BLUEDROID_ENABLED + default 0 if BT_LOG_BTC_TRACE_LEVEL_NONE + default 1 if BT_LOG_BTC_TRACE_LEVEL_ERROR + default 2 if BT_LOG_BTC_TRACE_LEVEL_WARNING + default 3 if BT_LOG_BTC_TRACE_LEVEL_API + default 4 if BT_LOG_BTC_TRACE_LEVEL_EVENT + default 5 if BT_LOG_BTC_TRACE_LEVEL_DEBUG + default 6 if BT_LOG_BTC_TRACE_LEVEL_VERBOSE + default 2 + + choice BT_LOG_OSI_TRACE_LEVEL + prompt "OSI layer" + default BT_LOG_OSI_TRACE_LEVEL_WARNING + depends on BT_BLUEDROID_ENABLED && !BT_STACK_NO_LOG + help + Define BT trace level for OSI layer + + config BT_LOG_OSI_TRACE_LEVEL_NONE + bool "NONE" + config BT_LOG_OSI_TRACE_LEVEL_ERROR + bool "ERROR" + config BT_LOG_OSI_TRACE_LEVEL_WARNING + bool "WARNING" + config BT_LOG_OSI_TRACE_LEVEL_API + bool "API" + config BT_LOG_OSI_TRACE_LEVEL_EVENT + bool "EVENT" + config BT_LOG_OSI_TRACE_LEVEL_DEBUG + bool "DEBUG" + config BT_LOG_OSI_TRACE_LEVEL_VERBOSE + bool "VERBOSE" + endchoice + + config BT_LOG_OSI_TRACE_LEVEL + int + depends on BT_BLUEDROID_ENABLED + default 0 if BT_LOG_OSI_TRACE_LEVEL_NONE + default 1 if BT_LOG_OSI_TRACE_LEVEL_ERROR + default 2 if BT_LOG_OSI_TRACE_LEVEL_WARNING + default 3 if BT_LOG_OSI_TRACE_LEVEL_API + default 4 if BT_LOG_OSI_TRACE_LEVEL_EVENT + default 5 if BT_LOG_OSI_TRACE_LEVEL_DEBUG + default 6 if BT_LOG_OSI_TRACE_LEVEL_VERBOSE + default 2 + + choice BT_LOG_BLUFI_TRACE_LEVEL + prompt "BLUFI layer" + default BT_LOG_BLUFI_TRACE_LEVEL_WARNING + depends on BT_BLUEDROID_ENABLED && !BT_STACK_NO_LOG + help + Define BT trace level for BLUFI layer + + config BT_LOG_BLUFI_TRACE_LEVEL_NONE + bool "NONE" + config BT_LOG_BLUFI_TRACE_LEVEL_ERROR + bool "ERROR" + config BT_LOG_BLUFI_TRACE_LEVEL_WARNING + bool "WARNING" + config BT_LOG_BLUFI_TRACE_LEVEL_API + bool "API" + config BT_LOG_BLUFI_TRACE_LEVEL_EVENT + bool "EVENT" + config BT_LOG_BLUFI_TRACE_LEVEL_DEBUG + bool "DEBUG" + config BT_LOG_BLUFI_TRACE_LEVEL_VERBOSE + bool "VERBOSE" + endchoice + + config BT_LOG_BLUFI_TRACE_LEVEL + int + depends on BT_BLUEDROID_ENABLED + default 0 if BT_LOG_BLUFI_TRACE_LEVEL_NONE + default 1 if BT_LOG_BLUFI_TRACE_LEVEL_ERROR + default 2 if BT_LOG_BLUFI_TRACE_LEVEL_WARNING + default 3 if BT_LOG_BLUFI_TRACE_LEVEL_API + default 4 if BT_LOG_BLUFI_TRACE_LEVEL_EVENT + default 5 if BT_LOG_BLUFI_TRACE_LEVEL_DEBUG + default 6 if BT_LOG_BLUFI_TRACE_LEVEL_VERBOSE + default 2 + +endmenu #BT DEBUG LOG LEVEL + +config BT_ACL_CONNECTIONS + int "BT/BLE MAX ACL CONNECTIONS(1~9)" + depends on BT_BLUEDROID_ENABLED + range 1 9 + default 4 + help + Maximum BT/BLE connection count. The ESP32-C3/S3 chip supports a maximum of 10 instances, + including ADV, SCAN and connections. The ESP32-C3/S3 chip can connect up to 9 devices if + ADV or SCAN uses only one. If ADV and SCAN are both used, The ESP32-C3/S3 chip is connected + to a maximum of 8 devices. Because Bluetooth cannot reclaim used instances once ADV or SCAN + is used. + +config BT_MULTI_CONNECTION_ENBALE + bool "Enable BLE multi-conections" + depends on BT_BLE_ENABLED + default y + help + Enable this option if there are multiple connections + +config BT_ALLOCATION_FROM_SPIRAM_FIRST + bool "BT/BLE will first malloc the memory from the PSRAM" + depends on BT_BLUEDROID_ENABLED + default n + help + This select can save the internal RAM if there have the PSRAM + +config BT_BLE_DYNAMIC_ENV_MEMORY + bool "Use dynamic memory allocation in BT/BLE stack" + depends on BT_BLUEDROID_ENABLED + default n + help + This select can make the allocation of memory will become more flexible + +config BT_BLE_HOST_QUEUE_CONG_CHECK + bool "BLE queue congestion check" + depends on BT_BLE_ENABLED + default n + help + When scanning and scan duplicate is not enabled, if there are a lot of adv packets around + or application layer handling adv packets is slow, it will cause the controller memory + to run out. if enabled, adv packets will be lost when host queue is congested. + +config BT_SMP_ENABLE + bool + depends on BT_BLUEDROID_ENABLED + default BT_CLASSIC_ENABLED || BT_BLE_SMP_ENABLE + +config BT_SMP_MAX_BONDS + int "BT/BLE maximum bond device count" + depends on BT_SMP_ENABLE + range 1 32 + default 15 + help + The number of security records for peer devices. + +config BT_BLE_ACT_SCAN_REP_ADV_SCAN + bool "Report adv data and scan response individually when BLE active scan" + depends on BT_BLUEDROID_ENABLED && BT_BLE_ENABLED + default n + help + Originally, when doing BLE active scan, Bluedroid will not report adv to application layer + until receive scan response. This option is used to disable the behavior. When enable this option, + Bluedroid will report adv data or scan response to application layer immediately. + + # Memory reserved at start of DRAM for Bluetooth stack + +config BT_BLE_ESTAB_LINK_CONN_TOUT + int "Timeout of BLE connection establishment" + depends on BT_BLE_ENABLED + range 1 60 + default 30 + help + Bluetooth Connection establishment maximum time, if connection time exceeds this value, the connection + establishment fails, ESP_GATTC_OPEN_EVT or ESP_GATTS_OPEN_EVT is triggered. + +config BT_MAX_DEVICE_NAME_LEN + int "length of bluetooth device name" + depends on BT_BLUEDROID_ENABLED + range 32 248 + default 32 + help + Bluetooth Device name length shall be no larger than 248 octets, If the broadcast data cannot contain + the complete device name, then only the shortname will be displayed, the rest parts that can't fit in + will be truncated. + +config BT_BLE_RPA_SUPPORTED + bool "Update RPA to Controller" + depends on (BT_BLUEDROID_ENABLED && ((BT_CONTROLLER_ENABLED && !SOC_BLE_DEVICE_PRIVACY_SUPPORTED) || BT_CONTROLLER_DISABLED)) # NOERROR + default n if (BT_CONTROLLER_ENABLED && !SOC_BLE_DEVICE_PRIVACY_SUPPORTED) + default y if BT_CONTROLLER_DISABLED + help + This enables controller RPA list function. + For ESP32, ESP32 only support network privacy mode. If this option is enabled, ESP32 will only accept + advertising packets from peer devices that contain private address, HW will not receive the advertising + packets contain identity address after IRK changed. If this option is disabled, address resolution will + be performed in the host, so the functions that require controller to resolve address in the white list + cannot be used. This option is disabled by default on ESP32, please enable or disable this option according + to your own needs. + + For other BLE chips, devices support network privacy mode and device privacy mode, + users can switch the two modes according to their own needs. So this option is enabled by default. + +config BT_BLE_RPA_TIMEOUT + int "Timeout of resolvable private address" + depends on BT_BLE_ENABLED + range 1 3600 + default 900 + help + This set RPA timeout of Controller and Host. + Default is 900 s (15 minutes). Range is 1 s to 1 hour (3600 s). + +config BT_BLE_50_FEATURES_SUPPORTED + bool "Enable BLE 5.0 features" + depends on (BT_BLE_ENABLED && ((BT_CONTROLLER_ENABLED && SOC_BLE_50_SUPPORTED) || BT_CONTROLLER_DISABLED)) + default y + help + Enabling this option activates BLE 5.0 features. + This option is universally supported in chips that support BLE, except for ESP32. + +config BT_BLE_42_FEATURES_SUPPORTED + bool "Enable BLE 4.2 features" + depends on (BT_BLE_ENABLED && ((BT_CONTROLLER_ENABLED && SOC_BLE_SUPPORTED) || BT_CONTROLLER_DISABLED)) + default n + help + This enables BLE 4.2 features. + +config BT_BLE_FEAT_PERIODIC_ADV_SYNC_TRANSFER + bool "Enable BLE periodic advertising sync transfer feature" + depends on (BT_BLE_50_FEATURES_SUPPORTED && ((BT_CONTROLLER_ENABLED && SOC_ESP_NIMBLE_CONTROLLER) || BT_CONTROLLER_DISABLED)) # NOERROR + default n + help + This enables BLE periodic advertising sync transfer feature + +config BT_BLE_FEAT_PERIODIC_ADV_ENH + bool "Enable periodic adv enhancements(adi support)" + depends on (BT_BLE_50_FEATURES_SUPPORTED && ((BT_CONTROLLER_ENABLED && SOC_ESP_NIMBLE_CONTROLLER) || BT_CONTROLLER_DISABLED)) # NOERROR + default n + help + Enable the periodic advertising enhancements + +config BT_BLE_FEAT_CREATE_SYNC_ENH + bool "Enable create sync enhancements(reporting disable and duplicate filtering enable support)" + depends on (BT_BLE_50_FEATURES_SUPPORTED && ((BT_CONTROLLER_ENABLED && SOC_ESP_NIMBLE_CONTROLLER) || BT_CONTROLLER_DISABLED)) # NOERROR + default n + help + Enable the create sync enhancements + +config BT_BLE_HIGH_DUTY_ADV_INTERVAL + bool "Enable BLE high duty advertising interval feature" + depends on BT_BLE_ENABLED + default n + help + This enable BLE high duty advertising interval feature diff --git a/lib/bt/host/bluedroid/api/esp_a2dp_api.c b/lib/bt/host/bluedroid/api/esp_a2dp_api.c new file mode 100644 index 00000000..be8adcf2 --- /dev/null +++ b/lib/bt/host/bluedroid/api/esp_a2dp_api.c @@ -0,0 +1,353 @@ +/* + * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "common/bt_target.h" +#include +#include "esp_err.h" +#include "esp_a2dp_api.h" +#include "esp_bt_main.h" +#include "btc/btc_manage.h" +#include "btc_av.h" + +#if BTC_AV_INCLUDED + +#if BTC_AV_SINK_INCLUDED +esp_err_t esp_a2d_sink_init(void) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + + if (g_a2dp_on_init || g_a2dp_sink_ongoing_deinit) { + return ESP_ERR_INVALID_STATE; + } + + btc_msg_t msg; + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_A2DP; + msg.act = BTC_AV_SINK_API_INIT_EVT; + + /* Switch to BTC context */ + bt_status_t stat = btc_transfer_context(&msg, NULL, 0, NULL, NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_a2d_sink_deinit(void) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + + if (g_a2dp_on_deinit || g_a2dp_sink_ongoing_deinit) { + return ESP_ERR_INVALID_STATE; + } + + btc_msg_t msg; + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_A2DP; + msg.act = BTC_AV_SINK_API_DEINIT_EVT; + + /* Switch to BTC context */ + bt_status_t stat = btc_transfer_context(&msg, NULL, 0, NULL, NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_a2d_sink_register_data_callback(esp_a2d_sink_data_cb_t callback) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + + if (g_a2dp_sink_ongoing_deinit) { + return ESP_ERR_INVALID_STATE; + } + + btc_msg_t msg; + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_A2DP; + msg.act = BTC_AV_SINK_API_REG_DATA_CB_EVT; + + btc_av_args_t arg; + memset(&arg, 0, sizeof(btc_av_args_t)); + arg.data_cb = callback; + + /* Switch to BTC context */ + bt_status_t stat = btc_transfer_context(&msg, &arg, sizeof(btc_av_args_t), NULL, NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_a2d_sink_connect(esp_bd_addr_t remote_bda) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + + if (g_a2dp_on_deinit || g_a2dp_sink_ongoing_deinit) { + return ESP_ERR_INVALID_STATE; + } + + bt_status_t stat; + btc_av_args_t arg; + btc_msg_t msg; + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_A2DP; + msg.act = BTC_AV_SINK_API_CONNECT_EVT; + + memset(&arg, 0, sizeof(btc_av_args_t)); + + /* Switch to BTC context */ + memcpy(&(arg.connect), remote_bda, sizeof(bt_bdaddr_t)); + stat = btc_transfer_context(&msg, &arg, sizeof(btc_av_args_t), NULL, NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_a2d_sink_disconnect(esp_bd_addr_t remote_bda) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + + if (g_a2dp_on_deinit || g_a2dp_sink_ongoing_deinit) { + return ESP_ERR_INVALID_STATE; + } + + bt_status_t stat; + btc_av_args_t arg; + btc_msg_t msg; + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_A2DP; + msg.act = BTC_AV_SINK_API_DISCONNECT_EVT; + + /* Switch to BTC context */ + memcpy(&(arg.disconn), remote_bda, sizeof(bt_bdaddr_t)); + stat = btc_transfer_context(&msg, &arg, sizeof(btc_av_args_t), NULL, NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_a2d_sink_set_delay_value(uint16_t delay_value) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + + if (g_a2dp_on_deinit || g_a2dp_sink_ongoing_deinit) { + return ESP_ERR_INVALID_STATE; + } + + bt_status_t stat; + btc_av_args_t arg; + btc_msg_t msg; + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_A2DP; + msg.act = BTC_AV_SINK_API_SET_DELAY_VALUE_EVT; + + memset(&arg, 0, sizeof(btc_av_args_t)); + arg.delay_value = delay_value; + + /* Switch to BTC context */ + stat = btc_transfer_context(&msg, &arg, sizeof(btc_av_args_t), NULL, NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_a2d_sink_get_delay_value(void) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + + if (g_a2dp_on_deinit || g_a2dp_sink_ongoing_deinit) { + return ESP_ERR_INVALID_STATE; + } + + bt_status_t stat; + btc_msg_t msg; + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_A2DP; + msg.act = BTC_AV_SINK_API_GET_DELAY_VALUE_EVT; + + /* Switch to BTC context */ + stat = btc_transfer_context(&msg, NULL, 0, NULL, NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} +#endif /* BTC_AV_SINK_INCLUDED */ + +esp_err_t esp_a2d_register_callback(esp_a2d_cb_t callback) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + + if (g_a2dp_sink_ongoing_deinit || g_a2dp_source_ongoing_deinit) { + return ESP_ERR_INVALID_STATE; + } + + if (callback == NULL) { + return ESP_FAIL; + } + + btc_profile_cb_set(BTC_PID_A2DP, callback); + return ESP_OK; +} + +esp_err_t esp_a2d_media_ctrl(esp_a2d_media_ctrl_t ctrl) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + + if (g_a2dp_on_deinit || g_a2dp_sink_ongoing_deinit || g_a2dp_source_ongoing_deinit) { + return ESP_ERR_INVALID_STATE; + } + + if (ctrl == ESP_A2D_MEDIA_CTRL_STOP) { + LOG_WARN("ESP_A2D_MEDIA_CTRL_STOP is deprecated, using ESP_A2D_MEDIA_CTRL_SUSPEND instead.\n"); + ctrl = ESP_A2D_MEDIA_CTRL_SUSPEND; + } + + bt_status_t stat; + btc_av_args_t arg; + btc_msg_t msg; + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_A2DP; + msg.act = BTC_AV_API_MEDIA_CTRL_EVT; + + memset(&arg, 0, sizeof(btc_av_args_t)); + + /* Switch to BTC context */ + arg.ctrl = ctrl; + stat = btc_transfer_context(&msg, &arg, sizeof(btc_av_args_t), NULL, NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +#if BTC_AV_SRC_INCLUDED +esp_err_t esp_a2d_source_init(void) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + + if (g_a2dp_on_init || g_a2dp_source_ongoing_deinit) { + return ESP_ERR_INVALID_STATE; + } + + btc_msg_t msg; + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_A2DP; + msg.act = BTC_AV_SRC_API_INIT_EVT; + + /* Switch to BTC context */ + bt_status_t stat = btc_transfer_context(&msg, NULL, 0, NULL, NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_a2d_source_deinit(void) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + + if (g_a2dp_on_deinit || g_a2dp_source_ongoing_deinit) { + return ESP_ERR_INVALID_STATE; + } + + btc_msg_t msg; + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_A2DP; + msg.act = BTC_AV_SRC_API_DEINIT_EVT; + + /* Switch to BTC context */ + bt_status_t stat = btc_transfer_context(&msg, NULL, 0, NULL, NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_a2d_source_connect(esp_bd_addr_t remote_bda) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + + if (g_a2dp_on_deinit || g_a2dp_source_ongoing_deinit) { + return ESP_ERR_INVALID_STATE; + } + + bt_status_t stat; + btc_av_args_t arg; + btc_msg_t msg; + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_A2DP; + msg.act = BTC_AV_SRC_API_CONNECT_EVT; + + memset(&arg, 0, sizeof(btc_av_args_t)); + + /* Switch to BTC context */ + memcpy(&(arg.src_connect), remote_bda, sizeof(bt_bdaddr_t)); + stat = btc_transfer_context(&msg, &arg, sizeof(btc_av_args_t), NULL, NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_a2d_source_disconnect(esp_bd_addr_t remote_bda) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + + if (g_a2dp_on_deinit || g_a2dp_source_ongoing_deinit) { + return ESP_ERR_INVALID_STATE; + } + + bt_status_t stat; + btc_av_args_t arg; + btc_msg_t msg; + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_A2DP; + msg.act = BTC_AV_SRC_API_DISCONNECT_EVT; + + memset(&arg, 0, sizeof(btc_av_args_t)); + + /* Switch to BTC context */ + memcpy(&(arg.src_disconn), remote_bda, sizeof(bt_bdaddr_t)); + stat = btc_transfer_context(&msg, &arg, sizeof(btc_av_args_t), NULL, NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_a2d_source_register_data_callback(esp_a2d_source_data_cb_t callback) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + + if (g_a2dp_source_ongoing_deinit) { + return ESP_ERR_INVALID_STATE; + } + + btc_msg_t msg; + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_A2DP; + msg.act = BTC_AV_SRC_API_REG_DATA_CB_EVT; + + btc_av_args_t arg; + memset(&arg, 0, sizeof(btc_av_args_t)); + arg.src_data_cb = callback; + + /* Switch to BTC context */ + bt_status_t stat = btc_transfer_context(&msg, &arg, sizeof(btc_av_args_t), NULL, NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +#endif /* BTC_AV_SRC_INCLUDED */ + +#endif /* #if BTC_AV_INCLUDED */ diff --git a/lib/bt/host/bluedroid/api/esp_avrc_api.c b/lib/bt/host/bluedroid/api/esp_avrc_api.c new file mode 100644 index 00000000..2d3e1532 --- /dev/null +++ b/lib/bt/host/bluedroid/api/esp_avrc_api.c @@ -0,0 +1,477 @@ +/* + * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "common/bt_target.h" +#include +#include "esp_err.h" +#include "esp_avrc_api.h" +#include "esp_bt_main.h" +#include "btc/btc_manage.h" +#include "btc_avrc.h" + +#if BTC_AV_INCLUDED + +esp_err_t esp_avrc_ct_register_callback(esp_avrc_ct_cb_t callback) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + + if (callback == NULL) { + return ESP_ERR_INVALID_ARG; + } + + btc_profile_cb_set(BTC_PID_AVRC_CT, callback); + return ESP_OK; +} + +esp_err_t esp_avrc_ct_init(void) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + + btc_msg_t msg; + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_AVRC_CT; + msg.act = BTC_AVRC_CT_API_INIT_EVT; + + /* Switch to BTC context */ + bt_status_t stat = btc_transfer_context(&msg, NULL, 0, NULL, NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_avrc_ct_deinit(void) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + + btc_msg_t msg; + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_AVRC_CT; + msg.act = BTC_AVRC_CT_API_DEINIT_EVT; + + /* Switch to BTC context */ + bt_status_t stat = btc_transfer_context(&msg, NULL, 0, NULL, NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_avrc_ct_send_set_player_value_cmd(uint8_t tl, uint8_t attr_id, uint8_t value_id) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + + if (tl > ESP_AVRC_TRANS_LABEL_MAX || attr_id > ESP_AVRC_PS_MAX_ATTR - 1) { + return ESP_ERR_INVALID_ARG; + } + + btc_msg_t msg; + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_AVRC_CT; + msg.act = BTC_AVRC_CTRL_API_SND_SET_PLAYER_SETTING_EVT; + + btc_avrc_args_t arg; + memset(&arg, 0, sizeof(btc_avrc_args_t)); + + arg.ps_cmd.tl = tl; + arg.ps_cmd.attr_id = attr_id; + arg.ps_cmd.value_id = value_id; + + /* Switch to BTC context */ + bt_status_t stat = btc_transfer_context(&msg, &arg, sizeof(btc_avrc_args_t), NULL, NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_avrc_ct_send_get_rn_capabilities_cmd(uint8_t tl) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + + if (tl > ESP_AVRC_TRANS_LABEL_MAX) { + return ESP_ERR_INVALID_ARG; + } + + btc_msg_t msg; + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_AVRC_CT; + msg.act = BTC_AVRC_STATUS_API_SND_GET_RN_CAPS_EVT; + + btc_avrc_args_t arg; + memset(&arg, 0, sizeof(btc_avrc_args_t)); + + arg.get_caps_cmd.tl = tl; + + /* Switch to BTC context */ + bt_status_t stat = btc_transfer_context(&msg, &arg, sizeof(btc_avrc_args_t), NULL, NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_avrc_ct_send_register_notification_cmd(uint8_t tl, uint8_t event_id, uint32_t event_parameter) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + + if (tl > ESP_AVRC_TRANS_LABEL_MAX || event_id > ESP_AVRC_RN_MAX_EVT - 1) { + return ESP_ERR_INVALID_ARG; + } + + if (!btc_avrc_ct_rn_evt_supported(event_id)) { + return ESP_ERR_NOT_SUPPORTED; + } + + btc_msg_t msg; + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_AVRC_CT; + msg.act = BTC_AVRC_NOTIFY_API_SND_REG_NOTIFY_EVT; + + btc_avrc_args_t arg; + memset(&arg, 0, sizeof(btc_avrc_args_t)); + + arg.rn_cmd.tl = tl; + arg.rn_cmd.event_id = event_id; + arg.rn_cmd.event_parameter = event_parameter; + + /* Switch to BTC context */ + bt_status_t stat = btc_transfer_context(&msg, &arg, sizeof(btc_avrc_args_t), NULL, NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_avrc_ct_send_set_absolute_volume_cmd(uint8_t tl, uint8_t volume) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + + if (tl > ESP_AVRC_TRANS_LABEL_MAX) { + return ESP_ERR_INVALID_ARG; + } + + if (volume > BTC_AVRC_MAX_VOLUME) { + return ESP_ERR_INVALID_ARG; + } + + if (!btc_avrc_ct_rn_evt_supported(ESP_AVRC_RN_VOLUME_CHANGE)) { + return ESP_ERR_NOT_SUPPORTED; + } + + btc_msg_t msg; + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_AVRC_CT; + msg.act = BTC_AVRC_CTRL_API_SND_SET_ABSOLUTE_VOLUME_EVT; + + btc_avrc_args_t arg; + memset(&arg, 0, sizeof(btc_avrc_args_t)); + + arg.set_abs_vol_cmd.tl = tl; + arg.set_abs_vol_cmd.volume = volume; + + /* Switch to BTC context */ + bt_status_t stat = btc_transfer_context(&msg, &arg, sizeof(btc_avrc_args_t), NULL, NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_avrc_ct_send_metadata_cmd(uint8_t tl, uint8_t attr_mask) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + + if (tl > ESP_AVRC_TRANS_LABEL_MAX) { + return ESP_ERR_INVALID_ARG; + } + + btc_msg_t msg; + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_AVRC_CT; + msg.act = BTC_AVRC_STATUS_API_SND_META_EVT; + + btc_avrc_args_t arg; + memset(&arg, 0, sizeof(btc_avrc_args_t)); + + arg.md_cmd.tl = tl; + arg.md_cmd.attr_mask = attr_mask; + + /* Switch to BTC context */ + bt_status_t stat = btc_transfer_context(&msg, &arg, sizeof(btc_avrc_args_t), NULL, NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_avrc_ct_send_passthrough_cmd(uint8_t tl, uint8_t key_code, uint8_t key_state) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + + if (tl > ESP_AVRC_TRANS_LABEL_MAX || key_state > ESP_AVRC_PT_CMD_STATE_RELEASED) { + return ESP_ERR_INVALID_ARG; + } + + btc_msg_t msg; + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_AVRC_CT; + msg.act = BTC_AVRC_CTRL_API_SND_PTCMD_EVT; + + btc_avrc_args_t arg; + memset(&arg, 0, sizeof(btc_avrc_args_t)); + + arg.pt_cmd.tl = tl; + arg.pt_cmd.key_code = key_code; + arg.pt_cmd.key_state = key_state; + + /* Switch to BTC context */ + bt_status_t stat = btc_transfer_context(&msg, &arg, sizeof(btc_avrc_args_t), NULL, NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +/*********************************************************************************************/ +/** following is the API of AVRCP target role **/ +/*********************************************************************************************/ + +esp_err_t esp_avrc_tg_register_callback(esp_avrc_tg_cb_t callback) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + + if (callback == NULL) { + return ESP_ERR_INVALID_ARG; + } + + btc_profile_cb_set(BTC_PID_AVRC_TG, callback); + return ESP_OK; +} + +esp_err_t esp_avrc_tg_init(void) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + + btc_msg_t msg; + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_AVRC_TG; + msg.act = BTC_AVRC_TG_API_INIT_EVT; + + /* Switch to BTC context */ + bt_status_t stat = btc_transfer_context(&msg, NULL, 0, NULL, NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_avrc_tg_deinit(void) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + + btc_msg_t msg; + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_AVRC_TG; + msg.act = BTC_AVRC_TG_API_DEINIT_EVT; + + /* Switch to BTC context */ + bt_status_t stat = btc_transfer_context(&msg, NULL, 0, NULL, NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +bool esp_avrc_psth_bit_mask_operation(esp_avrc_bit_mask_op_t op, esp_avrc_psth_bit_mask_t *psth, + esp_avrc_pt_cmd_t cmd) +{ + if (!psth || + cmd > ESP_AVRC_PT_CMD_VENDOR) { + return false; + } + + uint16_t *p = &psth->bits[(uint8_t)cmd >> 4]; + uint16_t mask = (uint16_t)1 << ((uint8_t)cmd & 0x0F); + switch (op) { + case ESP_AVRC_BIT_MASK_OP_SET: + *p |= mask; + break; + case ESP_AVRC_BIT_MASK_OP_CLEAR: + *p &= ~mask; + break; + case ESP_AVRC_BIT_MASK_OP_TEST: + return (*p & mask); + default: + return false; + } + + return true; +} + +esp_err_t esp_avrc_tg_get_psth_cmd_filter(esp_avrc_psth_filter_t filter, esp_avrc_psth_bit_mask_t *cmd_set) +{ + if ((esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) || + (! btc_avrc_tg_init_p())) { + return ESP_ERR_INVALID_STATE; + } + if (filter >= ESP_AVRC_PSTH_FILTER_SUPPORT_MAX || + cmd_set == NULL) { + return ESP_ERR_INVALID_ARG; + } + + if (filter == ESP_AVRC_PSTH_FILTER_ALLOWED_CMD) { + const uint16_t *allowed_cmd_set = btc_avrc_tg_get_allowed_command(); + memcpy(cmd_set, allowed_cmd_set, sizeof(esp_avrc_psth_bit_mask_t)); + } else if (filter == ESP_AVRC_PSTH_FILTER_SUPPORTED_CMD) { + const uint16_t *supported_cmd_set = btc_avrc_tg_get_supported_command(); + memcpy(cmd_set, supported_cmd_set, sizeof(esp_avrc_psth_bit_mask_t)); + } else { + } + + return ESP_OK; +} + +esp_err_t esp_avrc_tg_set_psth_cmd_filter(esp_avrc_psth_filter_t filter, const esp_avrc_psth_bit_mask_t *cmd_set) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + if (filter >= ESP_AVRC_PSTH_FILTER_SUPPORT_MAX || + cmd_set == NULL) { + return ESP_ERR_INVALID_ARG; + } + if (filter == ESP_AVRC_PSTH_FILTER_ALLOWED_CMD) { + return ESP_ERR_NOT_SUPPORTED; + } + if (filter == ESP_AVRC_PSTH_FILTER_SUPPORTED_CMD) { + bool allowed = btc_avrc_tg_check_supported_command(cmd_set->bits); + if (!allowed) { + return ESP_ERR_NOT_SUPPORTED; + } + btc_msg_t msg; + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_AVRC_TG; + msg.act = BTC_AVRC_TG_API_SET_PSTH_SUPPORTED_CMD_EVT; + + btc_avrc_tg_args_t arg; + memset(&arg, 0, sizeof(btc_avrc_tg_args_t)); + arg.set_psth_cmd = (uint16_t *)cmd_set->bits; + + /* Switch to BTC context */ + bt_status_t stat = btc_transfer_context(&msg, &arg, sizeof(btc_avrc_tg_args_t), + btc_avrc_tg_arg_deep_copy, btc_avrc_tg_arg_deep_free); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; + } else { + return ESP_FAIL; + } +} + +esp_err_t esp_avrc_tg_get_rn_evt_cap(esp_avrc_rn_evt_cap_t cap, esp_avrc_rn_evt_cap_mask_t *evt_set) +{ + if ((esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) || + (! btc_avrc_tg_init_p())) { + return ESP_ERR_INVALID_STATE; + } + if (cap >= ESP_AVRC_RN_CAP_MAX || + evt_set == NULL) { + return ESP_ERR_INVALID_ARG; + } + + if (cap == ESP_AVRC_RN_CAP_ALLOWED_EVT) { + evt_set->bits = btc_avrc_tg_get_rn_allowed_evt(); + } else if (cap == ESP_AVRC_RN_CAP_SUPPORTED_EVT) { + evt_set->bits = btc_avrc_tg_get_rn_supported_evt(); + } else { + } + + return ESP_OK; +} + +esp_err_t esp_avrc_tg_set_rn_evt_cap(const esp_avrc_rn_evt_cap_mask_t *evt_set) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + if (evt_set == NULL) { + return ESP_ERR_INVALID_ARG; + } + + bool allowed = btc_avrc_tg_check_rn_supported_evt(evt_set->bits); + if (!allowed) { + return ESP_ERR_NOT_SUPPORTED; + } + + btc_msg_t msg; + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_AVRC_TG; + msg.act = BTC_AVRC_TG_API_SET_RN_SUPPORTED_EVT; + + btc_avrc_tg_args_t arg; + memset(&arg, 0, sizeof(btc_avrc_tg_args_t)); + + arg.set_rn_evt = evt_set->bits; + + /* Switch to BTC context */ + bt_status_t stat = btc_transfer_context(&msg, &arg, sizeof(btc_avrc_tg_args_t), NULL, NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +bool esp_avrc_rn_evt_bit_mask_operation(esp_avrc_bit_mask_op_t op, esp_avrc_rn_evt_cap_mask_t *events, + esp_avrc_rn_event_ids_t event_id) +{ + if (!events || + event_id >= ESP_AVRC_RN_MAX_EVT) { + return false; + } + + uint16_t *p = &events->bits; + uint16_t mask = (uint16_t)1 << ((uint8_t)event_id & 0x0F); + switch (op) { + case ESP_AVRC_BIT_MASK_OP_SET: + *p |= mask; + break; + case ESP_AVRC_BIT_MASK_OP_CLEAR: + *p &= ~mask; + break; + case ESP_AVRC_BIT_MASK_OP_TEST: + return (*p & mask); + default: + return false; + } + + return true; +} + +esp_err_t esp_avrc_tg_send_rn_rsp(esp_avrc_rn_event_ids_t event_id, esp_avrc_rn_rsp_t rsp, + esp_avrc_rn_param_t *param) +{ + if ((esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) || + (! btc_avrc_tg_connected_p())) { + return ESP_ERR_INVALID_STATE; + } + + if ( ! btc_avrc_tg_rn_evt_supported((uint8_t)event_id)) { + return ESP_ERR_NOT_SUPPORTED; + } + + btc_msg_t msg; + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_AVRC_TG; + msg.act = BTC_AVRC_TG_API_SEND_RN_RSP_EVT; + + btc_avrc_tg_args_t arg; + memset(&arg, 0, sizeof(btc_avrc_tg_args_t)); + + arg.rn_rsp.event_id = event_id; + arg.rn_rsp.rsp = rsp; + memcpy(&arg.rn_rsp.param, param, sizeof(esp_avrc_rn_param_t)); + + /* Switch to BTC context */ + bt_status_t stat = btc_transfer_context(&msg, &arg, sizeof(btc_avrc_tg_args_t), NULL, NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; + +} + +#endif /* #if BTC_AV_INCLUDED */ diff --git a/lib/bt/host/bluedroid/api/esp_bluedroid_hci.c b/lib/bt/host/bluedroid/api/esp_bluedroid_hci.c new file mode 100644 index 00000000..892f6790 --- /dev/null +++ b/lib/bt/host/bluedroid/api/esp_bluedroid_hci.c @@ -0,0 +1,89 @@ +/* + * SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "esp_log.h" +#include "esp_bluedroid_hci.h" +#include "common/bt_target.h" +#include "hci/hci_trans_int.h" +#if (BT_CONTROLLER_INCLUDED == TRUE) +#include "esp_bt.h" +#endif + +#define LOG_TAG "HCI_API" + +static esp_bluedroid_hci_driver_operations_t s_hci_driver_ops = { 0 }; + +esp_err_t esp_bluedroid_attach_hci_driver(const esp_bluedroid_hci_driver_operations_t *p_ops) +{ + if (!p_ops) { + ESP_LOGE(LOG_TAG, "%s invalid function parameter", __func__); + return ESP_FAIL; + } + + s_hci_driver_ops.send = p_ops->send; + s_hci_driver_ops.check_send_available = p_ops->check_send_available; + s_hci_driver_ops.register_host_callback = p_ops->register_host_callback; + + return ESP_OK; +} + +esp_err_t esp_bluedroid_detach_hci_driver(void) +{ + s_hci_driver_ops.send = NULL; + s_hci_driver_ops.check_send_available = NULL; + s_hci_driver_ops.register_host_callback = NULL; + + return ESP_OK; +} + +/**************************************************************** + * INTERNAL USE * + ****************************************************************/ + +bool hci_host_check_send_available(void) +{ + bool can_send = false; +#if (BT_CONTROLLER_INCLUDED == TRUE) + can_send = esp_vhci_host_check_send_available(); +#else /* BT_CONTROLLER_INCLUDED == TRUE */ + if (s_hci_driver_ops.check_send_available) { + can_send = s_hci_driver_ops.check_send_available(); + } +#endif /* BT_CONTROLLER_INCLUDED == TRUE */ + return can_send; +} + +void hci_host_send_packet(uint8_t *data, uint16_t len) +{ +#if (BT_CONTROLLER_INCLUDED == TRUE) + esp_vhci_host_send_packet(data, len); +#else /* BT_CONTROLLER_INCLUDED == TRUE */ + if (s_hci_driver_ops.send) { + s_hci_driver_ops.send(data, len); + } +#endif /* BT_CONTROLLER_INCLUDED == TRUE */ +} + +esp_err_t hci_host_register_callback(const esp_bluedroid_hci_driver_callbacks_t *callback) +{ + esp_err_t ret = ESP_FAIL; + + if (!callback) { + ESP_LOGE(LOG_TAG, "%s invalid function parameter", __func__); + return ESP_FAIL; + } + +#if (BT_CONTROLLER_INCLUDED == TRUE) + ret = esp_vhci_host_register_callback((esp_vhci_host_callback_t *)callback); +#else /* BT_CONTROLLER_INCLUDED == TRUE */ + if (s_hci_driver_ops.register_host_callback) { + ret = s_hci_driver_ops.register_host_callback(callback); + } +#endif /* BT_CONTROLLER_INCLUDED == TRUE */ + + return ret; +} diff --git a/lib/bt/host/bluedroid/api/esp_bt_device.c b/lib/bt/host/bluedroid/api/esp_bt_device.c new file mode 100644 index 00000000..c37b9adb --- /dev/null +++ b/lib/bt/host/bluedroid/api/esp_bt_device.c @@ -0,0 +1,78 @@ +/* + * SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include "esp_bt_device.h" +#include "esp_bt_main.h" +#include "device/controller.h" +#include "btc/btc_task.h" +#include "btc/btc_dev.h" +#include "btc/btc_config.h" + +const uint8_t *esp_bt_dev_get_address(void) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return NULL; + } + return controller_get_interface()->get_address()->address; +} + +esp_err_t esp_bt_dev_set_device_name(const char *name) +{ + btc_msg_t msg = {0}; + btc_dev_args_t arg; + + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + if (!name){ + return ESP_ERR_INVALID_ARG; + } + if (strlen(name) > BTC_MAX_LOC_BD_NAME_LEN) { + return ESP_ERR_INVALID_ARG; + } + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_DEV; + msg.act = BTC_DEV_ACT_SET_DEVICE_NAME; + arg.set_dev_name.device_name = (char *)osi_malloc((BTC_MAX_LOC_BD_NAME_LEN + 1) * sizeof(char)); + if (!arg.set_dev_name.device_name) { + return ESP_ERR_NO_MEM; + } + + strcpy(arg.set_dev_name.device_name, name); + + return (btc_transfer_context(&msg, &arg, sizeof(btc_dev_args_t), NULL, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +#if (ESP_COEX_VSC_INCLUDED == TRUE) +esp_err_t esp_bt_dev_coex_status_config(esp_bt_dev_coex_type_t type, esp_bt_dev_coex_op_t op, uint8_t status) +{ + btc_msg_t msg = {0}; + btc_dev_args_t arg; + + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_DEV; + msg.act = BTC_DEV_ACT_CFG_COEX_STATUS; + arg.cfg_coex_status.type = type; + arg.cfg_coex_status.op = op; + arg.cfg_coex_status.status = status; + + return (btc_transfer_context(&msg, &arg, sizeof(btc_dev_args_t), NULL, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} +#endif + +esp_err_t esp_bt_config_file_path_update(const char *file_path) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_UNINITIALIZED); + + return btc_config_file_path_update(file_path); +} diff --git a/lib/bt/host/bluedroid/api/esp_bt_main.c b/lib/bt/host/bluedroid/api/esp_bt_main.c new file mode 100644 index 00000000..497ba769 --- /dev/null +++ b/lib/bt/host/bluedroid/api/esp_bt_main.c @@ -0,0 +1,230 @@ +/* + * SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + + +#include "common/bt_target.h" +#include "esp_bt_main.h" +#include "btc/btc_task.h" +#include "btc/btc_main.h" +#if (BT_CONTROLLER_INCLUDED == TRUE) +#include "esp_bt.h" +#endif +#include "osi/future.h" +#include "osi/allocator.h" +#include "config/stack_config.h" + +static bool bd_already_enable = false; +static bool bd_already_init = false; + +esp_bluedroid_status_t esp_bluedroid_get_status(void) +{ + if (bd_already_init) { + if (bd_already_enable) { + return ESP_BLUEDROID_STATUS_ENABLED; + } else { + return ESP_BLUEDROID_STATUS_INITIALIZED; + } + } else { + return ESP_BLUEDROID_STATUS_UNINITIALIZED; + } +} + +esp_err_t esp_bluedroid_enable(void) +{ + btc_msg_t msg; + future_t **future_p; + + if (!bd_already_init) { + LOG_ERROR("Bludroid not initialised\n"); + return ESP_ERR_INVALID_STATE; + } + + if (bd_already_enable) { + LOG_ERROR("Bluedroid already enabled\n"); + return ESP_ERR_INVALID_STATE; + } + + future_p = btc_main_get_future_p(BTC_MAIN_ENABLE_FUTURE); + *future_p = future_new(); + if (*future_p == NULL) { + LOG_ERROR("Bluedroid enable failed\n"); + return ESP_ERR_NO_MEM; + } + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_MAIN_INIT; + msg.act = BTC_MAIN_ACT_ENABLE; + + if (btc_transfer_context(&msg, NULL, 0, NULL, NULL) != BT_STATUS_SUCCESS) { + LOG_ERROR("Bluedroid enable failed\n"); + return ESP_FAIL; + } + + if (future_await(*future_p) == FUTURE_FAIL) { + LOG_ERROR("Bluedroid enable failed\n"); + return ESP_FAIL; + } + + bd_already_enable = true; + + return ESP_OK; +} + +esp_err_t esp_bluedroid_disable(void) +{ + btc_msg_t msg; + future_t **future_p; + + if (!bd_already_enable) { + LOG_ERROR("Bluedroid already disabled\n"); + return ESP_ERR_INVALID_STATE; + } + + future_p = btc_main_get_future_p(BTC_MAIN_DISABLE_FUTURE); + *future_p = future_new(); + if (*future_p == NULL) { + LOG_ERROR("Bluedroid disable failed\n"); + return ESP_ERR_NO_MEM; + } + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_MAIN_INIT; + msg.act = BTC_MAIN_ACT_DISABLE; + + if (btc_transfer_context(&msg, NULL, 0, NULL, NULL) != BT_STATUS_SUCCESS) { + LOG_ERROR("Bluedroid disable failed\n"); + return ESP_FAIL; + } + + if (future_await(*future_p) == FUTURE_FAIL) { + LOG_ERROR("Bluedroid disable failed\n"); + return ESP_FAIL; + } + + bd_already_enable = false; + + return ESP_OK; +} + +esp_err_t esp_bluedroid_init(void) +{ + esp_bluedroid_config_t cfg = BT_BLUEDROID_INIT_CONFIG_DEFAULT(); + return esp_bluedroid_init_with_cfg(&cfg); +} + +esp_err_t esp_bluedroid_init_with_cfg(esp_bluedroid_config_t *cfg) +{ + btc_msg_t msg; + future_t **future_p; + bt_status_t ret; + + if (!cfg) { + LOG_ERROR("%s cfg is NULL", __func__); + return ESP_ERR_INVALID_ARG; + } + +#if (BT_CONTROLLER_INCLUDED == TRUE) + if (esp_bt_controller_get_status() != ESP_BT_CONTROLLER_STATUS_ENABLED) { + LOG_ERROR("Controller not initialised\n"); + return ESP_ERR_INVALID_STATE; + } +#endif + + if (bd_already_init) { + LOG_ERROR("Bluedroid already initialised\n"); + return ESP_ERR_INVALID_STATE; + } + +#if HEAP_MEMORY_DEBUG + osi_mem_dbg_init(); +#endif + + ret = bluedriod_config_init(cfg); + if (ret != BT_STATUS_SUCCESS) { + LOG_ERROR("Bluedroid stack initialize fail, ret:%d", ret); + return ESP_FAIL; + } + + /* + * BTC Init + */ + ret = btc_init(); + if (ret != BT_STATUS_SUCCESS) { + LOG_ERROR("Bluedroid Initialize Fail"); + return ESP_FAIL; + } + + future_p = btc_main_get_future_p(BTC_MAIN_INIT_FUTURE); + *future_p = future_new(); + if (*future_p == NULL) { + LOG_ERROR("Bluedroid Initialize Fail!"); + return ESP_ERR_NO_MEM; + } + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_MAIN_INIT; + msg.act = BTC_MAIN_ACT_INIT; + + if (btc_transfer_context(&msg, NULL, 0, NULL, NULL) != BT_STATUS_SUCCESS) { + LOG_ERROR("Bluedroid Initialize Fail"); + return ESP_FAIL; + } + + if (future_await(*future_p) == FUTURE_FAIL) { + LOG_ERROR("Bluedroid Initialize Fail"); + return ESP_FAIL; + } + + bd_already_init = true; + + return ESP_OK; +} + + +esp_err_t esp_bluedroid_deinit(void) +{ + btc_msg_t msg; + future_t **future_p; + + if (!bd_already_init) { + LOG_ERROR("Bluedroid already de-initialised\n"); + return ESP_ERR_INVALID_STATE; + } + + if (bd_already_enable) { + LOG_ERROR("Bludroid already enabled, do disable first\n"); + return ESP_ERR_INVALID_STATE; + } + + future_p = btc_main_get_future_p(BTC_MAIN_DEINIT_FUTURE); + *future_p = future_new(); + if (*future_p == NULL) { + LOG_ERROR("Bluedroid de-initialise failed\n"); + return ESP_ERR_NO_MEM; + } + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_MAIN_INIT; + msg.act = BTC_MAIN_ACT_DEINIT; + + if (btc_transfer_context(&msg, NULL, 0, NULL, NULL) != BT_STATUS_SUCCESS) { + LOG_ERROR("Bluedroid de-initialise failed\n"); + return ESP_FAIL; + } + + if (future_await(*future_p) == FUTURE_FAIL) { + LOG_ERROR("Bluedroid de-initialise failed\n"); + return ESP_FAIL; + } + + btc_deinit(); + + bluedriod_config_deinit(); + + bd_already_init = false; + + return ESP_OK; +} diff --git a/lib/bt/host/bluedroid/api/esp_gap_ble_api.c b/lib/bt/host/bluedroid/api/esp_gap_ble_api.c new file mode 100644 index 00000000..5db7b629 --- /dev/null +++ b/lib/bt/host/bluedroid/api/esp_gap_ble_api.c @@ -0,0 +1,1601 @@ +/* + * SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "esp_bt_device.h" +#include "esp_bt_main.h" +#include "esp_gap_ble_api.h" +#include "bta/bta_api.h" +#include "common/bt_trace.h" +#include "btc/btc_manage.h" +#include "btc_gap_ble.h" +#include "btc/btc_ble_storage.h" + + +esp_err_t esp_ble_gap_register_callback(esp_gap_ble_cb_t callback) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + return (btc_profile_cb_set(BTC_PID_GAP_BLE, callback) == 0 ? ESP_OK : ESP_FAIL); +} + +esp_gap_ble_cb_t esp_ble_gap_get_callback(void) +{ + return (esp_gap_ble_cb_t) btc_profile_cb_get(BTC_PID_GAP_BLE); +} + +#if (BLE_42_FEATURE_SUPPORT == TRUE) +esp_err_t esp_ble_gap_config_adv_data(esp_ble_adv_data_t *adv_data) +{ + btc_msg_t msg = {0}; + btc_ble_gap_args_t arg; + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + if (adv_data == NULL) { + return ESP_ERR_INVALID_ARG; + } + + if (adv_data->service_uuid_len & 0xf) { //not 16*n + return ESP_ERR_INVALID_ARG; + } + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GAP_BLE; + msg.act = BTC_GAP_BLE_ACT_CFG_ADV_DATA; + memcpy(&arg.cfg_adv_data.adv_data, adv_data, sizeof(esp_ble_adv_data_t)); + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gap_args_t), btc_gap_ble_arg_deep_copy, + btc_gap_ble_arg_deep_free)== BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + + +esp_err_t esp_ble_gap_set_scan_params(esp_ble_scan_params_t *scan_params) +{ + btc_msg_t msg = {0}; + btc_ble_gap_args_t arg; + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + if (scan_params == NULL) { + return ESP_ERR_INVALID_ARG; + } + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GAP_BLE; + msg.act = BTC_GAP_BLE_ACT_SET_SCAN_PARAM; + memcpy(&arg.set_scan_param.scan_params, scan_params, sizeof(esp_ble_scan_params_t)); + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gap_args_t), NULL, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_ble_gap_start_scanning(uint32_t duration) +{ + btc_msg_t msg = {0}; + btc_ble_gap_args_t arg; + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GAP_BLE; + msg.act = BTC_GAP_BLE_ACT_START_SCAN; + arg.start_scan.duration = duration; + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gap_args_t), NULL, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + + +esp_err_t esp_ble_gap_stop_scanning(void) +{ + btc_msg_t msg; + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GAP_BLE; + msg.act = BTC_GAP_BLE_ACT_STOP_SCAN; + return (btc_transfer_context(&msg, NULL, 0, NULL, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_ble_gap_start_advertising(esp_ble_adv_params_t *adv_params) +{ + btc_msg_t msg = {0}; + btc_ble_gap_args_t arg; + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GAP_BLE; + msg.act = BTC_GAP_BLE_ACT_START_ADV; + memcpy(&arg.start_adv.adv_params, adv_params, sizeof(esp_ble_adv_params_t)); + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gap_args_t), NULL, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_ble_gap_stop_advertising(void) +{ + btc_msg_t msg; + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GAP_BLE; + msg.act = BTC_GAP_BLE_ACT_STOP_ADV; + + return (btc_transfer_context(&msg, NULL, 0, NULL, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_ble_gap_clear_advertising(void) +{ + btc_msg_t msg; + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GAP_BLE; + msg.act = BTC_GAP_BLE_ACT_CLEAR_ADV; + + return (btc_transfer_context(&msg, NULL, 0, NULL, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} +#endif // #if (BLE_42_FEATURE_SUPPORT == TRUE) + +esp_err_t esp_ble_gap_update_conn_params(esp_ble_conn_update_params_t *params) +{ + btc_msg_t msg = {0}; + btc_ble_gap_args_t arg; + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + if(!params) { + LOG_ERROR("%s,params is NULL", __func__); + return ESP_FAIL; + } + + if (ESP_BLE_IS_VALID_PARAM(params->min_int, ESP_BLE_CONN_INT_MIN, ESP_BLE_CONN_INT_MAX) && + ESP_BLE_IS_VALID_PARAM(params->max_int, ESP_BLE_CONN_INT_MIN, ESP_BLE_CONN_INT_MAX) && + ESP_BLE_IS_VALID_PARAM(params->timeout, ESP_BLE_CONN_SUP_TOUT_MIN, ESP_BLE_CONN_SUP_TOUT_MAX) && + (params->latency <= ESP_BLE_CONN_LATENCY_MAX) && + ((params->timeout * 10) >= ((1 + params->latency) * ((params->max_int * 5) >> 1))) && params->min_int <= params->max_int) { + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GAP_BLE; + msg.act = BTC_GAP_BLE_ACT_UPDATE_CONN_PARAM; + memcpy(&arg.conn_update_params.conn_params, params, sizeof(esp_ble_conn_update_params_t)); + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gap_args_t), NULL, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); + } else { + LOG_ERROR("%s,invalid connection params:min_int = %d, max_int = %d, latency = %d, timeout = %d",\ + __func__, params->min_int, params->max_int, params->latency, params->timeout); + return ESP_FAIL; + } +} + +esp_err_t esp_ble_gap_set_pkt_data_len(esp_bd_addr_t remote_device, uint16_t tx_data_length) +{ + btc_msg_t msg = {0}; + btc_ble_gap_args_t arg; + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GAP_BLE; + msg.act = BTC_GAP_BLE_ACT_SET_PKT_DATA_LEN; + arg.set_pkt_data_len.tx_data_length = tx_data_length; + memcpy(arg.set_pkt_data_len.remote_device, remote_device, ESP_BD_ADDR_LEN); + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gap_args_t), NULL, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + + +esp_err_t esp_ble_gap_set_rand_addr(esp_bd_addr_t rand_addr) +{ + btc_msg_t msg = {0}; + btc_ble_gap_args_t arg; + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GAP_BLE; + msg.act = BTC_GAP_BLE_ACT_SET_RAND_ADDRESS; + memcpy(arg.set_rand_addr.rand_addr, rand_addr, ESP_BD_ADDR_LEN); + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gap_args_t), NULL, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_ble_gap_clear_rand_addr(void) +{ + btc_msg_t msg; + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GAP_BLE; + msg.act = BTC_GAP_BLE_ACT_CLEAR_RAND_ADDRESS; + + return (btc_transfer_context(&msg, NULL, 0, NULL, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_ble_gap_config_local_privacy (bool privacy_enable) +{ + btc_msg_t msg = {0}; + btc_ble_gap_args_t arg; + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GAP_BLE; + msg.act = BTC_GAP_BLE_ACT_CONFIG_LOCAL_PRIVACY; + arg.cfg_local_privacy.privacy_enable = privacy_enable; + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gap_args_t), NULL, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_ble_gap_config_local_icon (uint16_t icon) +{ + esp_err_t ret; + btc_msg_t msg = {0}; + btc_ble_gap_args_t arg; + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + switch (icon) { + case ESP_BLE_APPEARANCE_GENERIC_PHONE: + case ESP_BLE_APPEARANCE_GENERIC_COMPUTER: + case ESP_BLE_APPEARANCE_GENERIC_REMOTE: + case ESP_BLE_APPEARANCE_GENERIC_THERMOMETER: + case ESP_BLE_APPEARANCE_THERMOMETER_EAR: + case ESP_BLE_APPEARANCE_GENERIC_HEART_RATE: + case ESP_BLE_APPEARANCE_HEART_RATE_BELT: + case ESP_BLE_APPEARANCE_GENERIC_BLOOD_PRESSURE: + case ESP_BLE_APPEARANCE_BLOOD_PRESSURE_ARM: + case ESP_BLE_APPEARANCE_BLOOD_PRESSURE_WRIST: + case ESP_BLE_APPEARANCE_GENERIC_PULSE_OXIMETER: + case ESP_BLE_APPEARANCE_PULSE_OXIMETER_FINGERTIP: + case ESP_BLE_APPEARANCE_PULSE_OXIMETER_WRIST: + case ESP_BLE_APPEARANCE_GENERIC_GLUCOSE: + case ESP_BLE_APPEARANCE_GENERIC_WEIGHT: + case ESP_BLE_APPEARANCE_GENERIC_WALKING: + case ESP_BLE_APPEARANCE_WALKING_IN_SHOE: + case ESP_BLE_APPEARANCE_WALKING_ON_SHOE: + case ESP_BLE_APPEARANCE_WALKING_ON_HIP: + case ESP_BLE_APPEARANCE_GENERIC_WATCH: + case ESP_BLE_APPEARANCE_SPORTS_WATCH: + case ESP_BLE_APPEARANCE_GENERIC_EYEGLASSES: + case ESP_BLE_APPEARANCE_GENERIC_DISPLAY: + case ESP_BLE_APPEARANCE_GENERIC_MEDIA_PLAYER: + case ESP_BLE_APPEARANCE_GENERIC_BARCODE_SCANNER: + case ESP_BLE_APPEARANCE_HID_BARCODE_SCANNER: + case ESP_BLE_APPEARANCE_GENERIC_HID: + case ESP_BLE_APPEARANCE_HID_KEYBOARD: + case ESP_BLE_APPEARANCE_HID_MOUSE: + case ESP_BLE_APPEARANCE_HID_JOYSTICK: + case ESP_BLE_APPEARANCE_HID_GAMEPAD: + case ESP_BLE_APPEARANCE_HID_DIGITIZER_TABLET: + case ESP_BLE_APPEARANCE_HID_CARD_READER: + case ESP_BLE_APPEARANCE_HID_DIGITAL_PEN: + case ESP_BLE_APPEARANCE_UNKNOWN: + case ESP_BLE_APPEARANCE_GENERIC_CLOCK: + case ESP_BLE_APPEARANCE_GENERIC_TAG: + case ESP_BLE_APPEARANCE_GENERIC_KEYRING: + case ESP_BLE_APPEARANCE_GENERIC_CYCLING: + case ESP_BLE_APPEARANCE_CYCLING_COMPUTER: + case ESP_BLE_APPEARANCE_CYCLING_SPEED: + case ESP_BLE_APPEARANCE_CYCLING_CADENCE: + case ESP_BLE_APPEARANCE_CYCLING_POWER: + case ESP_BLE_APPEARANCE_CYCLING_SPEED_CADENCE: + case ESP_BLE_APPEARANCE_GENERIC_PERSONAL_MOBILITY_DEVICE: + case ESP_BLE_APPEARANCE_POWERED_WHEELCHAIR: + case ESP_BLE_APPEARANCE_MOBILITY_SCOOTER: + case ESP_BLE_APPEARANCE_GENERIC_CONTINUOUS_GLUCOSE_MONITOR: + case ESP_BLE_APPEARANCE_GENERIC_INSULIN_PUMP: + case ESP_BLE_APPEARANCE_INSULIN_PUMP_DURABLE_PUMP: + case ESP_BLE_APPEARANCE_INSULIN_PUMP_PATCH_PUMP: + case ESP_BLE_APPEARANCE_INSULIN_PEN: + case ESP_BLE_APPEARANCE_GENERIC_MEDICATION_DELIVERY: + case ESP_BLE_APPEARANCE_GENERIC_OUTDOOR_SPORTS: + case ESP_BLE_APPEARANCE_OUTDOOR_SPORTS_LOCATION: + case ESP_BLE_APPEARANCE_OUTDOOR_SPORTS_LOCATION_AND_NAV: + case ESP_BLE_APPEARANCE_OUTDOOR_SPORTS_LOCATION_POD: + case ESP_BLE_APPEARANCE_OUTDOOR_SPORTS_LOCATION_POD_AND_NAV: + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GAP_BLE; + msg.act = BTC_GAP_BLE_ACT_CONFIG_LOCAL_ICON; + arg.cfg_local_icon.icon = icon; + ret = (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gap_args_t), NULL, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); + break; + default: + ret = ESP_ERR_INVALID_ARG; + break; + } + return ret; +} + +esp_err_t esp_ble_gap_update_whitelist(bool add_remove, esp_bd_addr_t remote_bda, esp_ble_wl_addr_type_t wl_addr_type) +{ + btc_msg_t msg = {0}; + btc_ble_gap_args_t arg; + + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + if (!remote_bda){ + return ESP_ERR_INVALID_SIZE; + } + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GAP_BLE; + msg.act = BTC_GAP_BLE_ACT_UPDATE_WHITE_LIST; + arg.update_white_list.add_remove = add_remove; + arg.update_white_list.wl_addr_type = wl_addr_type; + memcpy(arg.update_white_list.remote_bda, remote_bda, sizeof(esp_bd_addr_t)); + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gap_args_t), NULL, NULL) + == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_ble_gap_clear_whitelist(void) +{ + btc_msg_t msg; + + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GAP_BLE; + msg.act = BTC_GAP_BLE_ACT_CLEAR_WHITE_LIST; + + return (btc_transfer_context(&msg, NULL, 0, NULL, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_ble_gap_get_whitelist_size(uint16_t *length) +{ + if (length == NULL) { + return ESP_FAIL; + } + btc_get_whitelist_size(length); + + return ESP_OK; +} +#if (BLE_42_FEATURE_SUPPORT == TRUE) +esp_err_t esp_ble_gap_set_prefer_conn_params(esp_bd_addr_t bd_addr, + uint16_t min_conn_int, uint16_t max_conn_int, + uint16_t slave_latency, uint16_t supervision_tout) +{ + btc_msg_t msg = {0}; + btc_ble_gap_args_t arg; + + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + + if (ESP_BLE_IS_VALID_PARAM(min_conn_int, ESP_BLE_CONN_INT_MIN, ESP_BLE_CONN_INT_MAX) && + ESP_BLE_IS_VALID_PARAM(max_conn_int, ESP_BLE_CONN_INT_MIN, ESP_BLE_CONN_INT_MAX) && + ESP_BLE_IS_VALID_PARAM(supervision_tout, ESP_BLE_CONN_SUP_TOUT_MIN, ESP_BLE_CONN_SUP_TOUT_MAX) && + (slave_latency <= ESP_BLE_CONN_LATENCY_MAX) && + ((supervision_tout * 10) >= ((1 + slave_latency) * ((max_conn_int * 5) >> 1))) && min_conn_int <= max_conn_int) { + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GAP_BLE; + msg.act = BTC_GAP_BLE_ACT_SET_CONN_PARAMS; + arg.set_conn_params.min_conn_int = min_conn_int; + arg.set_conn_params.max_conn_int = max_conn_int; + arg.set_conn_params.slave_latency = slave_latency; + arg.set_conn_params.supervision_tout = supervision_tout; + memcpy(arg.set_conn_params.bd_addr, bd_addr, sizeof(esp_bd_addr_t)); + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gap_args_t), NULL, NULL) + == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); + } else { + LOG_ERROR("%s,invalid connection params:min_int = %d, max_int = %d, latency = %d, timeout = %d",\ + __func__, min_conn_int, max_conn_int, slave_latency, supervision_tout); + return ESP_FAIL; + } +} +#endif // #if (BLE_42_FEATURE_SUPPORT == TRUE) + +esp_err_t esp_ble_gap_set_device_name(const char *name) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + return esp_bt_dev_set_device_name(name); +} + +esp_err_t esp_ble_gap_get_device_name(void) +{ + btc_msg_t msg = {0}; + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GAP_BLE; + msg.act = BTC_GAP_BLE_ACT_GET_DEV_NAME; + + return (btc_transfer_context(&msg, NULL, 0, NULL, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_ble_gap_get_local_used_addr(esp_bd_addr_t local_used_addr, uint8_t * addr_type) +{ + if(esp_bluedroid_get_status() != (ESP_BLUEDROID_STATUS_ENABLED)) { + LOG_ERROR("%s, bluedroid status error", __func__); + return ESP_FAIL; + } + if(!BTM_BleGetCurrentAddress(local_used_addr, addr_type)) { + return ESP_FAIL; + } + return ESP_OK; +} + +uint8_t *esp_ble_resolve_adv_data( uint8_t *adv_data, uint8_t type, uint8_t *length) +{ + if (((type < ESP_BLE_AD_TYPE_FLAG) || (type > ESP_BLE_AD_TYPE_128SERVICE_DATA)) && + (type != ESP_BLE_AD_MANUFACTURER_SPECIFIC_TYPE)) { + LOG_ERROR("the eir type not define, type = %x\n", type); + return NULL; + } + + if (adv_data == NULL) { + LOG_ERROR("Invalid p_eir data.\n"); + return NULL; + } + + return (BTM_CheckAdvData( adv_data, type, length)); +} +#if (BLE_42_FEATURE_SUPPORT == TRUE) +esp_err_t esp_ble_gap_config_adv_data_raw(uint8_t *raw_data, uint32_t raw_data_len) +{ + btc_msg_t msg = {0}; + btc_ble_gap_args_t arg; + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + if ((raw_data_len != 0 && raw_data == NULL) || raw_data_len > ESP_BLE_ADV_DATA_LEN_MAX) { + return ESP_ERR_INVALID_ARG; + } + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GAP_BLE; + msg.act = BTC_GAP_BLE_ACT_CFG_ADV_DATA_RAW; + arg.cfg_adv_data_raw.raw_adv = raw_data; + arg.cfg_adv_data_raw.raw_adv_len = raw_data_len; + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gap_args_t), btc_gap_ble_arg_deep_copy, + btc_gap_ble_arg_deep_free) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); + +} +#endif // #if (BLE_42_FEATURE_SUPPORT == TRUE) +esp_err_t esp_ble_gap_read_rssi(esp_bd_addr_t remote_addr) +{ + btc_msg_t msg = {0}; + btc_ble_gap_args_t arg; + + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GAP_BLE; + msg.act = BTC_GAP_BLE_ACT_READ_RSSI; + memcpy(arg.read_rssi.remote_addr, remote_addr, sizeof(esp_bd_addr_t)); + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gap_args_t), NULL, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} +#if (BLE_42_FEATURE_SUPPORT == TRUE) +esp_err_t esp_ble_gap_config_scan_rsp_data_raw(uint8_t *raw_data, uint32_t raw_data_len) +{ + btc_msg_t msg = {0}; + btc_ble_gap_args_t arg; + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + if ((raw_data_len != 0 && raw_data == NULL) || raw_data_len > ESP_BLE_ADV_DATA_LEN_MAX) { + return ESP_ERR_INVALID_ARG; + } + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GAP_BLE; + msg.act = BTC_GAP_BLE_ACT_CFG_SCAN_RSP_DATA_RAW; + arg.cfg_scan_rsp_data_raw.raw_scan_rsp = raw_data; + arg.cfg_scan_rsp_data_raw.raw_scan_rsp_len = raw_data_len; + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gap_args_t), btc_gap_ble_arg_deep_copy, + btc_gap_ble_arg_deep_free) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); + +} + +esp_err_t esp_ble_gap_add_duplicate_scan_exceptional_device(esp_ble_duplicate_exceptional_info_type_t type, esp_duplicate_info_t device_info) +{ + btc_msg_t msg = {0}; + btc_ble_gap_args_t arg; + + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + if (!device_info && type <= ESP_BLE_DUPLICATE_SCAN_EXCEPTIONAL_INFO_MESH_LINK_ID) { + return ESP_ERR_INVALID_SIZE; + } + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GAP_BLE; + msg.act = BTC_GAP_BLE_UPDATE_DUPLICATE_SCAN_EXCEPTIONAL_LIST; + arg.update_duplicate_exceptional_list.subcode = ESP_BLE_DUPLICATE_EXCEPTIONAL_LIST_ADD; + arg.update_duplicate_exceptional_list.info_type = type; + if (device_info) { + memcpy(arg.update_duplicate_exceptional_list.device_info, device_info, sizeof(esp_bd_addr_t)); + } + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gap_args_t), NULL, NULL) + == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_ble_gap_remove_duplicate_scan_exceptional_device(esp_ble_duplicate_exceptional_info_type_t type, esp_duplicate_info_t device_info) +{ + btc_msg_t msg = {0}; + btc_ble_gap_args_t arg; + + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + if (!device_info && type <= ESP_BLE_DUPLICATE_SCAN_EXCEPTIONAL_INFO_MESH_LINK_ID) { + return ESP_ERR_INVALID_SIZE; + } + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GAP_BLE; + msg.act = BTC_GAP_BLE_UPDATE_DUPLICATE_SCAN_EXCEPTIONAL_LIST; + arg.update_duplicate_exceptional_list.subcode = ESP_BLE_DUPLICATE_EXCEPTIONAL_LIST_REMOVE; + arg.update_duplicate_exceptional_list.info_type = type; + if (device_info) { + memcpy(arg.update_duplicate_exceptional_list.device_info, device_info, sizeof(esp_bd_addr_t)); + } + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gap_args_t), NULL, NULL) + == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_ble_gap_clean_duplicate_scan_exceptional_list(esp_duplicate_scan_exceptional_list_type_t list_type) +{ + btc_msg_t msg = {0}; + btc_ble_gap_args_t arg; + + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GAP_BLE; + msg.act = BTC_GAP_BLE_UPDATE_DUPLICATE_SCAN_EXCEPTIONAL_LIST; + arg.update_duplicate_exceptional_list.subcode = ESP_BLE_DUPLICATE_EXCEPTIONAL_LIST_CLEAN; + arg.update_duplicate_exceptional_list.info_type = list_type; + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gap_args_t), NULL, NULL) + == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} +#endif // #if (BLE_42_FEATURE_SUPPORT == TRUE) +#if (SMP_INCLUDED == TRUE) +esp_err_t esp_ble_gap_set_security_param(esp_ble_sm_param_t param_type, + void *value, uint8_t len) +{ + if(param_type >= ESP_BLE_SM_MAX_PARAM) { + return ESP_ERR_INVALID_ARG; + } + if((param_type != ESP_BLE_SM_CLEAR_STATIC_PASSKEY) && ( value == NULL || len < sizeof(uint8_t) || len > sizeof(uint32_t))) { + return ESP_ERR_INVALID_ARG; + } + if(param_type == ESP_BLE_SM_SET_STATIC_PASSKEY) { + uint32_t passkey = 0; + for(uint8_t i = 0; i < len; i++) + { + passkey += (((uint8_t *)value)[i]<<(8*i)); + } + if(passkey > 999999) { + return ESP_ERR_INVALID_ARG; + } + } + if (param_type == ESP_BLE_APP_ENC_KEY_SIZE) { + LOG_ERROR("ESP_BLE_APP_ENC_KEY_SIZE is deprecated, use ESP_GATT_PERM_ENCRYPT_KEY_SIZE in characteristic definition"); + return ESP_ERR_NOT_SUPPORTED; + } + if (param_type == ESP_BLE_SM_MAX_KEY_SIZE || param_type == ESP_BLE_SM_MIN_KEY_SIZE) { + if (((uint8_t *)value)[0] > 16 || ((uint8_t *)value)[0] < 7) { + return ESP_ERR_INVALID_ARG; + } + } + btc_msg_t msg = {0}; + btc_ble_gap_args_t arg; + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GAP_BLE; + msg.act = BTC_GAP_BLE_SET_SECURITY_PARAM_EVT; + arg.set_security_param.param_type = param_type; + arg.set_security_param.len = len; + arg.set_security_param.value = value; + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gap_args_t), btc_gap_ble_arg_deep_copy, + btc_gap_ble_arg_deep_free) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_ble_set_encryption(esp_bd_addr_t bd_addr, esp_ble_sec_act_t sec_act) +{ + btc_msg_t msg = {0}; + btc_ble_gap_args_t arg; + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GAP_BLE; + msg.act = BTC_GAP_BLE_SET_ENCRYPTION_EVT; + arg.set_encryption.sec_act = sec_act; + memcpy(arg.set_encryption.bd_addr, bd_addr, ESP_BD_ADDR_LEN); + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gap_args_t), NULL, NULL) + == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_ble_gap_security_rsp(esp_bd_addr_t bd_addr, bool accept) +{ + btc_msg_t msg = {0}; + btc_ble_gap_args_t arg; + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GAP_BLE; + msg.act = BTC_GAP_BLE_SECURITY_RSP_EVT; + arg.sec_rsp.accept = accept; + memcpy(arg.sec_rsp.bd_addr, bd_addr, ESP_BD_ADDR_LEN); + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gap_args_t), NULL, NULL) + == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); + +} + +esp_err_t esp_ble_passkey_reply(esp_bd_addr_t bd_addr, bool accept, uint32_t passkey) +{ + btc_msg_t msg = {0}; + btc_ble_gap_args_t arg; + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GAP_BLE; + msg.act = BTC_GAP_BLE_PASSKEY_REPLY_EVT; + arg.enc_passkey_replay.accept = accept; + arg.enc_passkey_replay.passkey = passkey; + memcpy(arg.enc_passkey_replay.bd_addr, bd_addr, ESP_BD_ADDR_LEN); + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gap_args_t), NULL, NULL) + == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_ble_confirm_reply(esp_bd_addr_t bd_addr, bool accept) +{ + btc_msg_t msg = {0}; + btc_ble_gap_args_t arg; + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GAP_BLE; + msg.act = BTC_GAP_BLE_CONFIRM_REPLY_EVT; + arg.enc_comfirm_replay.accept = accept; + memcpy(arg.enc_comfirm_replay.bd_addr, bd_addr, ESP_BD_ADDR_LEN); + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gap_args_t), NULL, NULL) + == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_ble_remove_bond_device(esp_bd_addr_t bd_addr) +{ + btc_msg_t msg = {0}; + btc_ble_gap_args_t arg; + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GAP_BLE; + msg.act = BTC_GAP_BLE_REMOVE_BOND_DEV_EVT; + memcpy(arg.remove_bond_device.bd_addr, bd_addr, ESP_BD_ADDR_LEN); + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gap_args_t), NULL, NULL) + == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +int esp_ble_get_bond_device_num(void) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_FAIL; + } + + return btc_storage_get_num_ble_bond_devices(); +} + +esp_err_t esp_ble_get_bond_device_list(int *dev_num, esp_ble_bond_dev_t *dev_list) +{ + int ret; + int dev_num_total; + + if (dev_num == NULL || dev_list == NULL) { + return ESP_ERR_INVALID_ARG; + } + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + dev_num_total = btc_storage_get_num_ble_bond_devices(); + if (*dev_num > dev_num_total) { + *dev_num = dev_num_total; + } + + ret = btc_storage_get_bonded_ble_devices_list(dev_list, *dev_num); + + return (ret == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_ble_oob_req_reply(esp_bd_addr_t bd_addr, uint8_t *TK, uint8_t len) +{ + if(len != ESP_BT_OCTET16_LEN) { + return ESP_ERR_INVALID_ARG; + } + + btc_msg_t msg = {0}; + btc_ble_gap_args_t arg; + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GAP_BLE; + msg.act = BTC_GAP_BLE_OOB_REQ_REPLY_EVT; + memcpy(arg.oob_req_reply.bd_addr, bd_addr, ESP_BD_ADDR_LEN); + arg.oob_req_reply.len = len; + arg.oob_req_reply.p_value = TK; + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gap_args_t), btc_gap_ble_arg_deep_copy, + btc_gap_ble_arg_deep_free) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_ble_sc_oob_req_reply(esp_bd_addr_t bd_addr, uint8_t p_c[16], uint8_t p_r[16]) +{ + if (!p_c || !p_r) { + return ESP_ERR_INVALID_ARG; + } + + btc_msg_t msg = {0}; + btc_ble_gap_args_t arg; + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GAP_BLE; + msg.act = BTC_GAP_BLE_SC_OOB_REQ_REPLY_EVT; + memcpy(arg.sc_oob_req_reply.bd_addr, bd_addr, ESP_BD_ADDR_LEN); + arg.sc_oob_req_reply.p_c = p_c; + arg.sc_oob_req_reply.p_r = p_r; + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gap_args_t), btc_gap_ble_arg_deep_copy, + btc_gap_ble_arg_deep_free) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_ble_create_sc_oob_data(void) +{ + btc_msg_t msg = {0}; + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GAP_BLE; + msg.act = BTC_GAP_BLE_SC_CR_OOB_DATA_EVT; + + return (btc_transfer_context(&msg, NULL, 0, NULL, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} +#endif /* #if (SMP_INCLUDED == TRUE) */ + +esp_err_t esp_ble_gap_disconnect(esp_bd_addr_t remote_device) +{ + btc_msg_t msg = {0}; + btc_ble_gap_args_t arg; + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GAP_BLE; + msg.act = BTC_GAP_BLE_DISCONNECT_EVT; + memcpy(arg.disconnect.remote_device, remote_device, ESP_BD_ADDR_LEN); + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gap_args_t), NULL, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_ble_get_current_conn_params(esp_bd_addr_t bd_addr, esp_gap_conn_params_t *conn_params) +{ + if(!bd_addr || !conn_params) { + return ESP_ERR_INVALID_ARG; + } + if(BTM_GetCurrentConnParams(bd_addr, &conn_params->interval, &conn_params->latency, &conn_params->timeout)) { + return ESP_OK; + } + return ESP_ERR_NOT_FOUND; +} + +esp_err_t esp_gap_ble_set_channels(esp_gap_ble_channels channels) +{ + btc_msg_t msg = {0}; + btc_ble_gap_args_t arg; + + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GAP_BLE; + msg.act = BTC_GAP_BLE_SET_AFH_CHANNELS; + + memcpy(&arg.set_channels.channels, channels, ESP_GAP_BLE_CHANNELS_LEN); + arg.set_channels.channels[ESP_GAP_BLE_CHANNELS_LEN -1] &= 0x1F; + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gap_args_t), NULL, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_gap_ble_set_authorization(esp_bd_addr_t bd_addr, bool authorize) +{ + if (!bd_addr) { + return ESP_ERR_INVALID_ARG; + } + if (BTM_Ble_Authorization(bd_addr, authorize)) { + return ESP_OK; + } + return ESP_FAIL; +} + +#if (BLE_42_FEATURE_SUPPORT == TRUE) +esp_err_t esp_ble_dtm_tx_start(const esp_ble_dtm_tx_t *tx_params) +{ + btc_msg_t msg = {0}; + btc_ble_gap_args_t arg; + + if (!tx_params) { + return ESP_ERR_INVALID_ARG; + } + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GAP_BLE; + msg.act = BTC_GAP_BLE_DTM_TX_START; + + memcpy(&arg.dtm_tx_start, tx_params, sizeof(esp_ble_dtm_tx_t)); + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gap_args_t), NULL, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_ble_dtm_rx_start(const esp_ble_dtm_rx_t *rx_params) +{ + btc_msg_t msg = {0}; + btc_ble_gap_args_t arg; + + if (!rx_params) { + return ESP_ERR_INVALID_ARG; + } + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GAP_BLE; + msg.act = BTC_GAP_BLE_DTM_RX_START; + + memcpy(&arg.dtm_rx_start, rx_params, sizeof(esp_ble_dtm_rx_t)); + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gap_args_t), NULL, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} +#endif // #if (BLE_42_FEATURE_SUPPORT == TRUE) + +#if (BLE_50_FEATURE_SUPPORT == TRUE) +esp_err_t esp_ble_dtm_enh_tx_start(const esp_ble_dtm_enh_tx_t *tx_params) +{ + btc_msg_t msg = {0}; + btc_ble_5_gap_args_t arg; + + if (!tx_params) { + return ESP_ERR_INVALID_ARG; + } + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GAP_BLE; + msg.act = BTC_GAP_BLE_DTM_ENH_TX_START; + + memcpy(&arg.dtm_enh_tx_start, tx_params, sizeof(esp_ble_dtm_enh_tx_t)); + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_5_gap_args_t), NULL, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_ble_dtm_enh_rx_start(const esp_ble_dtm_enh_rx_t *rx_params) +{ + btc_msg_t msg = {0}; + btc_ble_5_gap_args_t arg; + + if (!rx_params) { + return ESP_ERR_INVALID_ARG; + } + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GAP_BLE; + msg.act = BTC_GAP_BLE_DTM_ENH_RX_START; + + memcpy(&arg.dtm_enh_rx_start, rx_params, sizeof(esp_ble_dtm_enh_rx_t)); + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_5_gap_args_t), NULL, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} +#endif // #if (BLE_50_FEATURE_SUPPORT == TRUE) + +esp_err_t esp_ble_dtm_stop(void) +{ + btc_msg_t msg = {0}; + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GAP_BLE; + msg.act = BTC_GAP_BLE_DTM_STOP; + + return (btc_transfer_context(&msg, NULL, 0, NULL, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +#if (BLE_50_FEATURE_SUPPORT == TRUE) + +esp_err_t esp_ble_gap_read_phy(esp_bd_addr_t bd_addr) +{ + btc_msg_t msg; + btc_ble_5_gap_args_t arg; + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GAP_BLE; + msg.act = BTC_GAP_BLE_READ_PHY; + + memcpy(arg.read_phy.bd_addr, bd_addr, ESP_BD_ADDR_LEN); + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_5_gap_args_t), NULL, NULL) + == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_ble_gap_set_preferred_default_phy(esp_ble_gap_phy_mask_t tx_phy_mask, + esp_ble_gap_phy_mask_t rx_phy_mask) +{ + btc_msg_t msg; + btc_ble_5_gap_args_t arg; + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GAP_BLE; + msg.act = BTC_GAP_BLE_SET_PREFERED_DEF_PHY; + arg.set_perf_def_phy.tx_phy_mask = tx_phy_mask; + arg.set_perf_def_phy.rx_phy_mask = rx_phy_mask; + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_5_gap_args_t), NULL, NULL) + == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_ble_gap_set_preferred_phy(esp_bd_addr_t bd_addr, + esp_ble_gap_all_phys_t all_phys_mask, + esp_ble_gap_phy_mask_t tx_phy_mask, + esp_ble_gap_phy_mask_t rx_phy_mask, + esp_ble_gap_prefer_phy_options_t phy_options) +{ + btc_msg_t msg; + btc_ble_5_gap_args_t arg; + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GAP_BLE; + msg.act = BTC_GAP_BLE_SET_DEF_PHY; + memcpy(arg.set_def_phy.bd_addr, bd_addr, ESP_BD_ADDR_LEN); + arg.set_def_phy.all_phys_mask = all_phys_mask; + arg.set_def_phy.tx_phy_mask = tx_phy_mask; + arg.set_def_phy.rx_phy_mask = rx_phy_mask; + arg.set_def_phy.phy_options = phy_options; + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_5_gap_args_t), NULL, NULL) + == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_ble_gap_ext_adv_set_rand_addr(uint8_t instance, esp_bd_addr_t rand_addr) +{ + btc_msg_t msg; + btc_ble_5_gap_args_t arg; + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GAP_BLE; + msg.act = BTC_GAP_BLE_SET_EXT_ADV_RAND_ADDR; + arg.ext_adv_set_rand_addr.instance = instance; + memcpy(arg.ext_adv_set_rand_addr.rand_addr, rand_addr, BD_ADDR_LEN); + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_5_gap_args_t), NULL, NULL) + == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); + +} + +esp_err_t esp_ble_gap_ext_adv_set_params(uint8_t instance, + const esp_ble_gap_ext_adv_params_t *params) +{ + btc_msg_t msg; + btc_ble_5_gap_args_t arg; + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GAP_BLE; + msg.act = BTC_GAP_BLE_SET_EXT_ADV_PARAMS; + + if (ESP_BLE_IS_VALID_PARAM(params->interval_min, ESP_BLE_PRIM_ADV_INT_MIN, ESP_BLE_PRIM_ADV_INT_MAX) && + ESP_BLE_IS_VALID_PARAM(params->interval_max, ESP_BLE_PRIM_ADV_INT_MIN, ESP_BLE_PRIM_ADV_INT_MAX) && + ESP_BLE_IS_VALID_PARAM(params->peer_addr_type, BLE_ADDR_TYPE_PUBLIC, BLE_ADDR_TYPE_RANDOM) && + ESP_BLE_IS_VALID_PARAM(params->own_addr_type, BLE_ADDR_TYPE_PUBLIC, BLE_ADDR_TYPE_RPA_RANDOM) && + ESP_BLE_IS_VALID_PARAM(params->filter_policy, ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY, ADV_FILTER_ALLOW_SCAN_WLST_CON_WLST) && + (params->interval_min <= params->interval_max) && (params->channel_map > 0) && (params->channel_map <= ADV_CHNL_ALL) && + ((params->primary_phy == ESP_BLE_GAP_PRI_PHY_1M) || (params->primary_phy == ESP_BLE_GAP_PRI_PHY_CODED)) && + ((params->secondary_phy == ESP_BLE_GAP_PHY_1M) || (params->secondary_phy == ESP_BLE_GAP_PHY_2M) || + (params->secondary_phy == ESP_BLE_GAP_PHY_CODED))){ + memcpy(&arg.ext_adv_set_params.params, params, sizeof(esp_ble_gap_ext_adv_params_t)); + } else { + LOG_ERROR("%s,invalid ext adv params", __func__); + return ESP_ERR_INVALID_ARG; + } + + arg.ext_adv_set_params.instance = instance; + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_5_gap_args_t), NULL, NULL) + == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_ble_gap_config_ext_adv_data_raw(uint8_t instance, uint16_t length, + const uint8_t *data) +{ + btc_msg_t msg; + btc_ble_5_gap_args_t arg; + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GAP_BLE; + msg.act = BTC_GAP_BLE_CFG_EXT_ADV_DATA_RAW; + + arg.ext_adv_cfg_data.instance = instance; + arg.ext_adv_cfg_data.length = length; + arg.ext_adv_cfg_data.data = (uint8_t *)data; + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_5_gap_args_t), btc_gap_ble_arg_deep_copy, + btc_gap_ble_arg_deep_free) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_ble_gap_config_ext_scan_rsp_data_raw(uint8_t instance, uint16_t length, + const uint8_t *scan_rsp_data) +{ + btc_msg_t msg; + btc_ble_5_gap_args_t arg; + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GAP_BLE; + msg.act = BTC_GAP_BLE_CFG_EXT_SCAN_RSP_DATA_RAW; + + arg.cfg_scan_rsp.instance = instance; + arg.cfg_scan_rsp.length = length; + arg.cfg_scan_rsp.data = (uint8_t *)scan_rsp_data; + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_5_gap_args_t), btc_gap_ble_arg_deep_copy, + btc_gap_ble_arg_deep_free) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_ble_gap_ext_adv_start(uint8_t num_adv, const esp_ble_gap_ext_adv_t *ext_adv) +{ + btc_msg_t msg; + btc_ble_5_gap_args_t arg; + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GAP_BLE; + msg.act = BTC_GAP_BLE_EXT_ADV_START; + + arg.ext_adv_start.num_adv = num_adv; + arg.ext_adv_start.ext_adv = (esp_ble_gap_ext_adv_t *)ext_adv; + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_5_gap_args_t), btc_gap_ble_arg_deep_copy, + btc_gap_ble_arg_deep_free) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_ble_gap_ext_adv_stop(uint8_t num_adv, const uint8_t *ext_adv_inst) +{ + btc_msg_t msg; + btc_ble_5_gap_args_t arg; + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GAP_BLE; + msg.act = BTC_GAP_BLE_EXT_ADV_STOP; + arg.ext_adv_stop.num_adv = num_adv; + arg.ext_adv_stop.ext_adv_inst = (uint8_t *)ext_adv_inst; + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_5_gap_args_t), btc_gap_ble_arg_deep_copy, + btc_gap_ble_arg_deep_free) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); + +} + +esp_err_t esp_ble_gap_ext_adv_set_remove(uint8_t instance) +{ + btc_msg_t msg; + btc_ble_5_gap_args_t arg; + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GAP_BLE; + msg.act = BTC_GAP_BLE_EXT_ADV_SET_REMOVE; + arg.ext_adv_set_remove.instance = instance; + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_5_gap_args_t), NULL, NULL) + == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_ble_gap_ext_adv_set_clear(void) +{ + btc_msg_t msg; + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GAP_BLE; + msg.act = BTC_GAP_BLE_EXT_ADV_SET_CLEAR; + + return (btc_transfer_context(&msg, NULL, 0, NULL, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_ble_gap_periodic_adv_set_params(uint8_t instance, const esp_ble_gap_periodic_adv_params_t *params) +{ + btc_msg_t msg; + btc_ble_5_gap_args_t arg; + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GAP_BLE; + msg.act = BTC_GAP_BLE_SET_PERIODIC_ADV_PARAMS; + + arg.peridic_adv_set_params.instance = instance; + memcpy(&arg.peridic_adv_set_params.params, params, sizeof(esp_ble_gap_periodic_adv_params_t)); + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_5_gap_args_t), NULL, NULL) + == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); + +} + +#if (CONFIG_BT_BLE_FEAT_PERIODIC_ADV_ENH) +esp_err_t esp_ble_gap_config_periodic_adv_data_raw(uint8_t instance, uint16_t length, + const uint8_t *data, bool only_update_did) +#else +esp_err_t esp_ble_gap_config_periodic_adv_data_raw(uint8_t instance, uint16_t length, + const uint8_t *data) +#endif +{ + btc_msg_t msg; + btc_ble_5_gap_args_t arg; + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GAP_BLE; + msg.act = BTC_GAP_BLE_CFG_PERIODIC_ADV_DATA_RAW; + + arg.periodic_adv_cfg_data.instance = instance; + arg.periodic_adv_cfg_data.len = length; + arg.periodic_adv_cfg_data.data = (uint8_t *)data; +#if (CONFIG_BT_BLE_FEAT_PERIODIC_ADV_ENH) + arg.periodic_adv_cfg_data.only_update_did = only_update_did; +#else + arg.periodic_adv_cfg_data.only_update_did = false; +#endif + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_5_gap_args_t), btc_gap_ble_arg_deep_copy, + btc_gap_ble_arg_deep_free) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); + +} + +#if (CONFIG_BT_BLE_FEAT_PERIODIC_ADV_ENH) +esp_err_t esp_ble_gap_periodic_adv_start(uint8_t instance,bool include_adi) +#else +esp_err_t esp_ble_gap_periodic_adv_start(uint8_t instance) +#endif +{ + btc_msg_t msg; + btc_ble_5_gap_args_t arg; + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GAP_BLE; + msg.act = BTC_GAP_BLE_PERIODIC_ADV_START; + + #if (CONFIG_BT_BLE_FEAT_PERIODIC_ADV_ENH) + arg.periodic_adv_start.include_adi = include_adi; + #else + arg.periodic_adv_start.include_adi = false; + #endif + arg.periodic_adv_start.instance = instance; + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_5_gap_args_t), NULL, NULL) + == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); + +} + +esp_err_t esp_ble_gap_periodic_adv_stop(uint8_t instance) +{ + btc_msg_t msg; + btc_ble_5_gap_args_t arg; + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GAP_BLE; + msg.act = BTC_GAP_BLE_PERIODIC_ADV_STOP; + + arg.periodic_adv_stop.instance = instance; + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_5_gap_args_t), NULL, NULL) + == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); + +} + +esp_err_t esp_ble_gap_periodic_adv_create_sync(const esp_ble_gap_periodic_adv_sync_params_t *params) +{ + btc_msg_t msg; + btc_ble_5_gap_args_t arg; + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GAP_BLE; + msg.act = BTC_GAP_BLE_PERIODIC_ADV_CREATE_SYNC; + + memcpy(&arg.periodic_adv_create_sync.params, params, sizeof(esp_ble_gap_periodic_adv_sync_params_t)); + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_5_gap_args_t), NULL, NULL) + == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); + +} + +esp_err_t esp_ble_gap_periodic_adv_sync_cancel(void) +{ + btc_msg_t msg; + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GAP_BLE; + msg.act = BTC_GAP_BLE_PERIODIC_ADV_SYNC_CANCEL; + + return (btc_transfer_context(&msg, NULL, 0, NULL, NULL) + == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); + +} + +esp_err_t esp_ble_gap_periodic_adv_sync_terminate(uint16_t sync_handle) +{ + btc_msg_t msg; + btc_ble_5_gap_args_t arg; + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GAP_BLE; + msg.act = BTC_GAP_BLE_PERIODIC_ADV_SYNC_TERMINATE; + + arg.periodic_adv_sync_term.sync_handle = sync_handle; + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_5_gap_args_t), NULL, NULL) + == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_ble_gap_periodic_adv_add_dev_to_list(esp_ble_addr_type_t addr_type, + esp_bd_addr_t addr, + uint8_t sid) +{ + btc_msg_t msg; + btc_ble_5_gap_args_t arg; + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GAP_BLE; + msg.act = BTC_GAP_BLE_PERIODIC_ADV_ADD_DEV_TO_LIST; + + arg.periodic_adv_add_dev.addr_type = addr_type; + arg.periodic_adv_add_dev.sid = sid; + + + memcpy(arg.periodic_adv_add_dev.addr, addr, sizeof(esp_bd_addr_t)); + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_5_gap_args_t), NULL, NULL) + == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); + +} + +esp_err_t esp_ble_gap_periodic_adv_remove_dev_from_list(esp_ble_addr_type_t addr_type, + esp_bd_addr_t addr, + uint8_t sid) +{ + btc_msg_t msg; + btc_ble_5_gap_args_t arg; + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GAP_BLE; + msg.act = BTC_GAP_BLE_PERIODIC_REMOVE_ADD_DEV_FROM_LIST; + + arg.periodic_adv_remove_dev.addr_type = addr_type; + arg.periodic_adv_remove_dev.sid = sid; + + + memcpy(arg.periodic_adv_remove_dev.addr, addr, sizeof(esp_bd_addr_t)); + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_5_gap_args_t), NULL, NULL) + == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); + +} + +esp_err_t esp_ble_gap_periodic_adv_clear_dev(void) +{ + btc_msg_t msg; + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GAP_BLE; + msg.act = BTC_GAP_BLE_PERIODIC_CLEAR_DEV; + + return (btc_transfer_context(&msg, NULL, 0, NULL, NULL) + == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); + +} + +esp_err_t esp_ble_gap_set_ext_scan_params(const esp_ble_ext_scan_params_t *params) +{ + btc_msg_t msg; + btc_ble_5_gap_args_t arg; + + if (!params) { + return ESP_ERR_INVALID_ARG; + } + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GAP_BLE; + msg.act = BTC_GAP_BLE_SET_EXT_SCAN_PARAMS; + + memcpy(&arg.set_ext_scan_params.params, params, sizeof(esp_ble_ext_scan_params_t)); + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_5_gap_args_t), NULL, NULL) + == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_ble_gap_start_ext_scan(uint32_t duration, uint16_t period) +{ + btc_msg_t msg; + btc_ble_5_gap_args_t arg; + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GAP_BLE; + msg.act = BTC_GAP_BLE_START_EXT_SCAN; + + arg.start_ext_scan.duration = duration; + arg.start_ext_scan.period = period; + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_5_gap_args_t), NULL, NULL) + == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_ble_gap_stop_ext_scan(void) +{ + btc_msg_t msg; + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GAP_BLE; + msg.act = BTC_GAP_BLE_STOP_EXT_SCAN; + + return (btc_transfer_context(&msg, NULL, 0, NULL, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_ble_gap_prefer_ext_connect_params_set(esp_bd_addr_t addr, + esp_ble_gap_phy_mask_t phy_mask, + const esp_ble_gap_conn_params_t *phy_1m_conn_params, + const esp_ble_gap_conn_params_t *phy_2m_conn_params, + const esp_ble_gap_conn_params_t *phy_coded_conn_params) +{ + btc_msg_t msg; + btc_ble_5_gap_args_t arg; + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GAP_BLE; + msg.act = BTC_GAP_BLE_SET_EXT_PEFER_CONNET_PARAMS; + + arg.set_ext_conn_params.phy_mask = phy_mask; + if (phy_mask & ESP_BLE_GAP_PHY_1M_PREF_MASK) { + if (!phy_1m_conn_params) { + return BT_STATUS_PARM_INVALID; + } + + if (ESP_BLE_IS_VALID_PARAM(phy_1m_conn_params->interval_min, ESP_BLE_CONN_INT_MIN, ESP_BLE_CONN_INT_MAX) && + ESP_BLE_IS_VALID_PARAM(phy_1m_conn_params->interval_max, ESP_BLE_CONN_INT_MIN, ESP_BLE_CONN_INT_MAX) && + ESP_BLE_IS_VALID_PARAM(phy_1m_conn_params->supervision_timeout, ESP_BLE_CONN_SUP_TOUT_MIN, ESP_BLE_CONN_SUP_TOUT_MAX) && + (phy_1m_conn_params->latency <= ESP_BLE_CONN_LATENCY_MAX) && + ((phy_1m_conn_params->supervision_timeout * 10) >= ((1 + phy_1m_conn_params->latency) * ((phy_1m_conn_params->interval_max * 5) >> 1))) && + (phy_1m_conn_params->interval_min <= phy_1m_conn_params->interval_max)) { + + memcpy(&arg.set_ext_conn_params.phy_1m_conn_params, phy_1m_conn_params, sizeof(esp_ble_gap_conn_params_t)); + } else { + LOG_ERROR("%s,invalid connection params:min_int = %d, max_int = %d, latency = %d, timeout = %d", __func__, + phy_1m_conn_params->interval_min, + phy_1m_conn_params->interval_max, + phy_1m_conn_params->latency, + phy_1m_conn_params->supervision_timeout); + + return ESP_ERR_INVALID_ARG; + } + } + + if (phy_mask & ESP_BLE_GAP_PHY_2M_PREF_MASK) { + if (!phy_2m_conn_params) { + return BT_STATUS_PARM_INVALID; + } + + if (ESP_BLE_IS_VALID_PARAM(phy_2m_conn_params->interval_min, ESP_BLE_CONN_INT_MIN, ESP_BLE_CONN_INT_MAX) && + ESP_BLE_IS_VALID_PARAM(phy_2m_conn_params->interval_max, ESP_BLE_CONN_INT_MIN, ESP_BLE_CONN_INT_MAX) && + ESP_BLE_IS_VALID_PARAM(phy_2m_conn_params->supervision_timeout, ESP_BLE_CONN_SUP_TOUT_MIN, ESP_BLE_CONN_SUP_TOUT_MAX) && + (phy_2m_conn_params->latency <= ESP_BLE_CONN_LATENCY_MAX) && + ((phy_2m_conn_params->supervision_timeout * 10) >= ((1 + phy_2m_conn_params->latency) * ((phy_2m_conn_params->interval_max * 5) >> 1))) && + (phy_2m_conn_params->interval_min <= phy_2m_conn_params->interval_max)) { + + memcpy(&arg.set_ext_conn_params.phy_2m_conn_params, phy_2m_conn_params, sizeof(esp_ble_gap_conn_params_t)); + } else { + LOG_ERROR("%s,invalid connection params:min_int = %d, max_int = %d, latency = %d, timeout = %d", __func__, + phy_2m_conn_params->interval_min, + phy_2m_conn_params->interval_max, + phy_2m_conn_params->latency, + phy_2m_conn_params->supervision_timeout); + + return ESP_ERR_INVALID_ARG; + } + } + + if (phy_mask & ESP_BLE_GAP_PHY_CODED_PREF_MASK) { + if (!phy_coded_conn_params) { + return BT_STATUS_PARM_INVALID; + } + + if (ESP_BLE_IS_VALID_PARAM(phy_coded_conn_params->interval_min, ESP_BLE_CONN_INT_MIN, ESP_BLE_CONN_INT_MAX) && + ESP_BLE_IS_VALID_PARAM(phy_coded_conn_params->interval_max, ESP_BLE_CONN_INT_MIN, ESP_BLE_CONN_INT_MAX) && + ESP_BLE_IS_VALID_PARAM(phy_coded_conn_params->supervision_timeout, ESP_BLE_CONN_SUP_TOUT_MIN, ESP_BLE_CONN_SUP_TOUT_MAX) && + (phy_coded_conn_params->latency <= ESP_BLE_CONN_LATENCY_MAX) && + ((phy_coded_conn_params->supervision_timeout * 10) >= ((1 + phy_coded_conn_params->latency) * ((phy_coded_conn_params->interval_max * 5) >> 1))) && + (phy_coded_conn_params->interval_min <= phy_coded_conn_params->interval_max)) { + + memcpy(&arg.set_ext_conn_params.phy_coded_conn_params, phy_coded_conn_params, sizeof(esp_ble_gap_conn_params_t)); + } else { + LOG_ERROR("%s,invalid connection params:min_int = %d, max_int = %d, latency = %d, timeout = %d", __func__, + phy_coded_conn_params->interval_min, + phy_coded_conn_params->interval_max, + phy_coded_conn_params->latency, + phy_coded_conn_params->supervision_timeout); + + return ESP_ERR_INVALID_ARG; + } + } + + memcpy(arg.set_ext_conn_params.addr, addr, sizeof(esp_bd_addr_t)); + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_5_gap_args_t), NULL, NULL) + == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); + +} + +#endif //#if (BLE_50_FEATURE_SUPPORT == TRUE) + +#if (BLE_FEAT_PERIODIC_ADV_SYNC_TRANSFER == TRUE) +esp_err_t esp_ble_gap_periodic_adv_recv_enable(uint16_t sync_handle, uint8_t enable) +{ + btc_msg_t msg; + btc_ble_5_gap_args_t arg; + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GAP_BLE; + msg.act = BTC_GAP_BLE_PERIODIC_ADV_RECV_ENABLE; + + arg.periodic_adv_recv_en.sync_handle = sync_handle; + arg.periodic_adv_recv_en.enable = enable; + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_5_gap_args_t), NULL, NULL) + == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_ble_gap_periodic_adv_sync_trans(esp_bd_addr_t addr, uint16_t service_data, uint16_t sync_handle) +{ + btc_msg_t msg; + btc_ble_5_gap_args_t arg; + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + if (addr == NULL) { + return ESP_ERR_INVALID_ARG; + } + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GAP_BLE; + msg.act = BTC_GAP_BLE_PERIODIC_ADV_SYNC_TRANS; + + memcpy(arg.periodic_adv_sync_trans.addr, addr, sizeof(esp_bd_addr_t)); + arg.periodic_adv_sync_trans.service_data = service_data; + arg.periodic_adv_sync_trans.sync_handle = sync_handle; + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_5_gap_args_t), NULL, NULL) + == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_ble_gap_periodic_adv_set_info_trans(esp_bd_addr_t addr, uint16_t service_data, uint8_t adv_handle) +{ + btc_msg_t msg; + btc_ble_5_gap_args_t arg; + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + if (addr == NULL) { + return ESP_ERR_INVALID_ARG; + } + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GAP_BLE; + msg.act = BTC_GAP_BLE_PERIODIC_ADV_SET_INFO_TRANS; + + memcpy(arg.periodic_adv_set_info_trans.addr, addr, sizeof(esp_bd_addr_t)); + arg.periodic_adv_set_info_trans.service_data = service_data; + arg.periodic_adv_set_info_trans.adv_handle = adv_handle; + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_5_gap_args_t), NULL, NULL) + == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_ble_gap_set_periodic_adv_sync_trans_params(esp_bd_addr_t addr, const esp_ble_gap_past_params_t *params) +{ + btc_msg_t msg; + btc_ble_5_gap_args_t arg; + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + if (params == NULL) { + return ESP_ERR_INVALID_ARG; + } + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GAP_BLE; + msg.act = BTC_GAP_BLE_SET_PERIODIC_ADV_SYNC_TRANS_PARAMS; + + if (addr) { + memcpy(arg.set_periodic_adv_sync_trans_params.addr, addr, sizeof(esp_bd_addr_t)); + } else { + memset(arg.set_periodic_adv_sync_trans_params.addr, 0, sizeof(esp_bd_addr_t)); + } + memcpy(&arg.set_periodic_adv_sync_trans_params.params, params, sizeof(esp_ble_gap_past_params_t)); + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_5_gap_args_t), NULL, NULL) + == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} +#endif //#if (BLE_FEAT_PERIODIC_ADV_SYNC_TRANSFER == TRUE) diff --git a/lib/bt/host/bluedroid/api/esp_gap_bt_api.c b/lib/bt/host/bluedroid/api/esp_gap_bt_api.c new file mode 100644 index 00000000..3e58847d --- /dev/null +++ b/lib/bt/host/bluedroid/api/esp_gap_bt_api.c @@ -0,0 +1,497 @@ +/* + * SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "common/bt_target.h" +#include +#include "esp_bt_main.h" +#include "esp_gap_bt_api.h" +#include "esp_log.h" +#include "common/bt_trace.h" +#include "bta/bta_api.h" +#include "btc/btc_manage.h" +#include "btc_gap_bt.h" +#include "btc/btc_storage.h" +#include "config/stack_config.h" + +#if (BTC_GAP_BT_INCLUDED == TRUE) + +#define TAG "BT_GAP" + +esp_err_t esp_bt_gap_register_callback(esp_bt_gap_cb_t callback) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + + if (callback == NULL) { + return ESP_FAIL; + } + + btc_profile_cb_set(BTC_PID_GAP_BT, callback); + return ESP_OK; +} + +esp_err_t esp_bt_gap_set_scan_mode(esp_bt_connection_mode_t c_mode, esp_bt_discovery_mode_t d_mode) +{ + btc_msg_t msg; + btc_gap_bt_args_t arg; + + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GAP_BT; + msg.act = BTC_GAP_BT_ACT_SET_SCAN_MODE; + arg.set_scan_mode.c_mode = c_mode; + arg.set_scan_mode.d_mode = d_mode; + + return (btc_transfer_context(&msg, &arg, sizeof(btc_gap_bt_args_t), NULL, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_bt_gap_start_discovery(esp_bt_inq_mode_t mode, uint8_t inq_len, uint8_t num_rsps) +{ + btc_msg_t msg; + btc_gap_bt_args_t arg; + + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + + if (mode != ESP_BT_INQ_MODE_GENERAL_INQUIRY && + mode != ESP_BT_INQ_MODE_LIMITED_INQUIRY) { + return ESP_ERR_INVALID_ARG; + } + + if (inq_len < ESP_BT_GAP_MIN_INQ_LEN || + inq_len > ESP_BT_GAP_MAX_INQ_LEN) { + return ESP_ERR_INVALID_ARG; + } + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GAP_BT; + msg.act = BTC_GAP_BT_ACT_START_DISCOVERY; + + arg.start_disc.mode = mode; + arg.start_disc.inq_len = inq_len; + arg.start_disc.num_rsps = num_rsps; + + return (btc_transfer_context(&msg, &arg, sizeof(btc_gap_bt_args_t), NULL, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_bt_gap_cancel_discovery(void) +{ + btc_msg_t msg; + + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GAP_BT; + msg.act = BTC_GAP_BT_ACT_CANCEL_DISCOVERY; + + return (btc_transfer_context(&msg, NULL, 0, NULL, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_bt_gap_get_remote_services(esp_bd_addr_t remote_bda) +{ + btc_msg_t msg; + btc_gap_bt_args_t arg; + + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GAP_BT; + msg.act = BTC_GAP_BT_ACT_GET_REMOTE_SERVICES; + + memcpy(&arg.bda, remote_bda, sizeof(bt_bdaddr_t)); + return (btc_transfer_context(&msg, &arg, sizeof(btc_gap_bt_args_t), NULL, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_bt_gap_get_remote_service_record(esp_bd_addr_t remote_bda, esp_bt_uuid_t *uuid) +{ + btc_msg_t msg; + btc_gap_bt_args_t arg; + + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GAP_BT; + msg.act = BTC_GAP_BT_ACT_GET_REMOTE_SERVICE_RECORD; + + memcpy(&arg.get_rmt_srv_rcd.bda, remote_bda, sizeof(bt_bdaddr_t)); + memcpy(&arg.get_rmt_srv_rcd.uuid, uuid, sizeof(esp_bt_uuid_t)); + return (btc_transfer_context(&msg, &arg, sizeof(btc_gap_bt_args_t), NULL, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +uint8_t *esp_bt_gap_resolve_eir_data(uint8_t *eir, esp_bt_eir_type_t type, uint8_t *length) +{ + if (!eir) { + return NULL; + } + + return BTM_CheckEirData(eir, type, length); +} + +esp_err_t esp_bt_gap_config_eir_data(esp_bt_eir_data_t *eir_data) +{ + btc_msg_t msg; + btc_gap_bt_args_t arg; + + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + + if (eir_data == NULL) { + return ESP_ERR_INVALID_ARG; + } + + if (eir_data->manufacturer_len > ESP_BT_EIR_MAX_LEN + || eir_data->url_len > ESP_BT_EIR_MAX_LEN) { + return ESP_ERR_INVALID_ARG; + } + + if ((eir_data->manufacturer_len > 0 && eir_data->p_manufacturer_data == NULL) + || (eir_data->url_len > 0 && eir_data->p_url == NULL)) { + return ESP_ERR_INVALID_ARG; + } + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GAP_BT; + msg.act = BTC_GAP_BT_ACT_CONFIG_EIR; + + memcpy(&arg.config_eir, eir_data, sizeof(esp_bt_eir_data_t)); + + return (btc_transfer_context(&msg, &arg, sizeof(btc_gap_bt_args_t), btc_gap_bt_arg_deep_copy, + btc_gap_bt_arg_deep_free) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_bt_gap_set_cod(esp_bt_cod_t cod, esp_bt_cod_mode_t mode) +{ + btc_msg_t msg; + btc_gap_bt_args_t arg; + + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + + switch (mode) { + case ESP_BT_SET_COD_MAJOR_MINOR: + case ESP_BT_SET_COD_SERVICE_CLASS: + case ESP_BT_CLR_COD_SERVICE_CLASS: + case ESP_BT_SET_COD_ALL: + case ESP_BT_INIT_COD: + break; + default: + return ESP_ERR_INVALID_ARG; + break; + } + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GAP_BT; + msg.act = BTC_GAP_BT_ACT_SET_COD; + + arg.set_cod.mode = mode; + memcpy(&arg.set_cod.cod, &cod, sizeof(esp_bt_cod_t)); + return (btc_transfer_context(&msg, &arg, sizeof(btc_gap_bt_args_t), NULL, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + + +esp_err_t esp_bt_gap_get_cod(esp_bt_cod_t *cod) +{ + return btc_gap_bt_get_cod(cod); +} + + +esp_err_t esp_bt_gap_read_rssi_delta(esp_bd_addr_t remote_addr) +{ + btc_msg_t msg; + btc_gap_bt_args_t arg; + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GAP_BT; + msg.act = BTC_GAP_BT_ACT_READ_RSSI_DELTA; + memcpy(arg.read_rssi_delta.bda.address, remote_addr, sizeof(esp_bd_addr_t)); + + return (btc_transfer_context(&msg, &arg, sizeof(btc_gap_bt_args_t), NULL, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_bt_gap_remove_bond_device(esp_bd_addr_t bd_addr) +{ + btc_msg_t msg; + btc_gap_bt_args_t arg; + + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GAP_BT; + msg.act = BTC_GAP_BT_ACT_REMOVE_BOND_DEVICE; + + memcpy(arg.rm_bond_device.bda.address, bd_addr, sizeof(esp_bd_addr_t)); + return (btc_transfer_context(&msg, &arg, sizeof(btc_gap_bt_args_t), NULL, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +int esp_bt_gap_get_bond_device_num(void) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + return btc_storage_get_num_bt_bond_devices(); +} + +esp_err_t esp_bt_gap_get_bond_device_list(int *dev_num, esp_bd_addr_t *dev_list) +{ + int ret; + + if (dev_num == NULL || dev_list == NULL) { + return ESP_ERR_INVALID_ARG; + } + + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + + ret = btc_storage_get_bonded_bt_devices_list((bt_bdaddr_t *)dev_list, dev_num); + + return (ret == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_bt_gap_set_pin(esp_bt_pin_type_t pin_type, uint8_t pin_code_len, esp_bt_pin_code_t pin_code) +{ + btc_msg_t msg; + btc_gap_bt_args_t arg; + + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GAP_BT; + msg.act = BTC_GAP_BT_ACT_SET_PIN_TYPE; + arg.set_pin_type.pin_type = pin_type; + if (pin_type == ESP_BT_PIN_TYPE_FIXED){ + arg.set_pin_type.pin_code_len = pin_code_len; + memcpy(arg.set_pin_type.pin_code, pin_code, pin_code_len); + } else { + arg.set_pin_type.pin_code_len = 0; + memset(arg.set_pin_type.pin_code, 0, ESP_BT_PIN_CODE_LEN); + } + + return (btc_transfer_context(&msg, &arg, sizeof(btc_gap_bt_args_t), btc_gap_bt_arg_deep_copy, + btc_gap_bt_arg_deep_free) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_bt_gap_pin_reply(esp_bd_addr_t bd_addr, bool accept, uint8_t pin_code_len, esp_bt_pin_code_t pin_code) +{ + btc_msg_t msg; + btc_gap_bt_args_t arg; + + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GAP_BT; + msg.act = BTC_GAP_BT_ACT_PIN_REPLY; + arg.pin_reply.accept = accept; + arg.pin_reply.pin_code_len = pin_code_len; + memcpy(arg.pin_reply.bda.address, bd_addr, sizeof(esp_bd_addr_t)); + memcpy(arg.pin_reply.pin_code, pin_code, pin_code_len); + + return (btc_transfer_context(&msg, &arg, sizeof(btc_gap_bt_args_t), btc_gap_bt_arg_deep_copy, + btc_gap_bt_arg_deep_free) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_bt_gap_set_security_param(esp_bt_sp_param_t param_type, + void *value, uint8_t len) +{ + btc_msg_t msg; + btc_gap_bt_args_t arg; + + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + + if (!(bluedriod_config_get()->get_ssp_enabled())) { + ESP_LOGE(TAG, "%s is not supported when `ssp_en` in `esp_bluedroid_config_t` is disabled!", __func__); + return ESP_ERR_NOT_SUPPORTED; + } + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GAP_BT; + msg.act = BTC_GAP_BT_ACT_SET_SECURITY_PARAM; + arg.set_security_param.param_type = param_type; + arg.set_security_param.len = len; + arg.set_security_param.value = value; + + return (btc_transfer_context(&msg, &arg, sizeof(btc_gap_bt_args_t), btc_gap_bt_arg_deep_copy, + btc_gap_bt_arg_deep_free) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_bt_gap_ssp_passkey_reply(esp_bd_addr_t bd_addr, bool accept, uint32_t passkey) +{ + btc_msg_t msg; + btc_gap_bt_args_t arg; + + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + + if (!(bluedriod_config_get()->get_ssp_enabled())) { + ESP_LOGE(TAG, "%s is not supported when `ssp_en` in `esp_bluedroid_config_t` is disabled!", __func__); + return ESP_ERR_NOT_SUPPORTED; + } + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GAP_BT; + msg.act = BTC_GAP_BT_ACT_PASSKEY_REPLY; + arg.passkey_reply.accept = accept; + arg.passkey_reply.passkey = passkey; + memcpy(arg.passkey_reply.bda.address, bd_addr, sizeof(esp_bd_addr_t)); + return (btc_transfer_context(&msg, &arg, sizeof(btc_gap_bt_args_t), btc_gap_bt_arg_deep_copy, + btc_gap_bt_arg_deep_free) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_bt_gap_ssp_confirm_reply(esp_bd_addr_t bd_addr, bool accept) +{ + btc_msg_t msg; + btc_gap_bt_args_t arg; + + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + + if (!(bluedriod_config_get()->get_ssp_enabled())) { + ESP_LOGE(TAG, "%s is not supported when `ssp_en` in `esp_bluedroid_config_t` is disabled!", __func__); + return ESP_ERR_NOT_SUPPORTED; + } + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GAP_BT; + msg.act = BTC_GAP_BT_ACT_CONFIRM_REPLY; + arg.confirm_reply.accept = accept; + memcpy(arg.confirm_reply.bda.address, bd_addr, sizeof(esp_bd_addr_t)); + return (btc_transfer_context(&msg, &arg, sizeof(btc_gap_bt_args_t), btc_gap_bt_arg_deep_copy, + btc_gap_bt_arg_deep_free) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_bt_gap_set_afh_channels(esp_bt_gap_afh_channels channels) +{ + btc_msg_t msg; + btc_gap_bt_args_t arg; + + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GAP_BT; + msg.act = BTC_GAP_BT_ACT_SET_AFH_CHANNELS; + + memcpy(&arg.set_afh_channels.channels, channels, ESP_BT_GAP_AFH_CHANNELS_LEN); + arg.set_afh_channels.channels[ESP_BT_GAP_AFH_CHANNELS_LEN -1] &= 0x7F; + return (btc_transfer_context(&msg, &arg, sizeof(btc_gap_bt_args_t), NULL, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_bt_gap_read_remote_name(esp_bd_addr_t remote_bda) +{ + btc_msg_t msg; + btc_gap_bt_args_t arg; + + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GAP_BT; + msg.act = BTC_GAP_BT_ACT_READ_REMOTE_NAME; + + memcpy(&arg.rmt_name_bda, remote_bda, sizeof(bt_bdaddr_t)); + return (btc_transfer_context(&msg, &arg, sizeof(btc_gap_bt_args_t), NULL, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_bt_gap_set_qos(esp_bd_addr_t remote_bda, uint32_t t_poll) +{ + btc_msg_t msg; + btc_gap_bt_args_t arg; + + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + + if (t_poll < ESP_BT_GAP_TPOLL_MIN || t_poll > ESP_BT_GAP_TPOLL_MAX) { + return ESP_ERR_INVALID_ARG; + } + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GAP_BT; + msg.act = BTC_GAP_BT_ACT_SET_QOS; + + memcpy(&arg.set_qos.bda, remote_bda, sizeof(bt_bdaddr_t)); + arg.set_qos.t_poll = t_poll; + return (btc_transfer_context(&msg, &arg, sizeof(btc_gap_bt_args_t), NULL, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_bt_gap_set_page_timeout(uint16_t page_to) +{ + btc_msg_t msg; + btc_gap_bt_args_t arg; + + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + if (page_to < HCI_MIN_PAGE_TOUT) { + return ESP_ERR_INVALID_ARG; + } + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GAP_BT; + msg.act = BTC_GAP_BT_ACT_SET_PAGE_TIMEOUT; + + arg.set_page_to.page_to = page_to; + return (btc_transfer_context(&msg, &arg, sizeof(btc_gap_bt_args_t), NULL, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_bt_gap_get_page_timeout(void) +{ + btc_msg_t msg; + + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GAP_BT; + msg.act = BTC_GAP_BT_ACT_GET_PAGE_TIMEOUT; + + return (btc_transfer_context(&msg, NULL, 0, NULL, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_bt_gap_set_acl_pkt_types(esp_bd_addr_t remote_bda, uint16_t pkt_types) +{ + btc_msg_t msg; + btc_gap_bt_args_t arg; + + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GAP_BT; + msg.act = BTC_GAP_BT_ACT_SET_ACL_PKT_TYPES; + + memcpy(&arg.set_acl_pkt_types.bda, remote_bda, sizeof(bt_bdaddr_t)); + arg.set_acl_pkt_types.pkt_types = pkt_types; + return (btc_transfer_context(&msg, &arg, sizeof(btc_gap_bt_args_t), NULL, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +#endif /* #if BTC_GAP_BT_INCLUDED == TRUE */ diff --git a/lib/bt/host/bluedroid/api/esp_gatt_common_api.c b/lib/bt/host/bluedroid/api/esp_gatt_common_api.c new file mode 100644 index 00000000..d0f3b0e0 --- /dev/null +++ b/lib/bt/host/bluedroid/api/esp_gatt_common_api.c @@ -0,0 +1,75 @@ +/* + * SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "esp_gatt_common_api.h" +#include "esp_bt_main.h" +#include "esp_gatt_defs.h" +#include "btc_gatt_common.h" + +/** + * @brief This function is called to set local MTU, + * the function is called before BLE connection. + * + * @param[in] mtu: the size of MTU. + * + * @return + * - ESP_OK: success + * - other: failed + * + */ +esp_err_t esp_ble_gatt_set_local_mtu (uint16_t mtu) +{ + btc_msg_t msg = {0}; + btc_ble_gatt_com_args_t arg; + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + if ((mtu < ESP_GATT_DEF_BLE_MTU_SIZE) || (mtu > ESP_GATT_MAX_MTU_SIZE)) { + return ESP_ERR_INVALID_SIZE; + } + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GATT_COMMON; + msg.act = BTC_GATT_ACT_SET_LOCAL_MTU; + arg.set_mtu.mtu = mtu; + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gatt_com_args_t), NULL, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +#if (BLE_INCLUDED == TRUE) +extern UINT16 L2CA_GetFreePktBufferNum_LE(void); + +/** + * @brief This function is called to get currently sendable packets number on controller, + * the function is called only in BLE running core and single connection now. + * + * @return + * sendable packets number on controller + * + */ + +uint16_t esp_ble_get_sendable_packets_num (void) +{ + return L2CA_GetFreePktBufferNum_LE(); +} + +/** + * @brief This function is used to query the number of available buffers for the current connection. + * When you need to query the current available buffer number, it is recommended to use this API. + * @param[in] conn_id: current connection id. + * + * @return + * Number of available buffers for the current connection + * + */ + +extern UINT16 L2CA_GetCurFreePktBufferNum_LE(UINT16 conn_id); +uint16_t esp_ble_get_cur_sendable_packets_num (uint16_t connid) +{ + return L2CA_GetCurFreePktBufferNum_LE(connid); +} +#endif diff --git a/lib/bt/host/bluedroid/api/esp_gattc_api.c b/lib/bt/host/bluedroid/api/esp_gattc_api.c new file mode 100644 index 00000000..5f078b28 --- /dev/null +++ b/lib/bt/host/bluedroid/api/esp_gattc_api.c @@ -0,0 +1,786 @@ +/* + * SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include "esp_gattc_api.h" +#include "esp_bt_main.h" +#include "btc/btc_manage.h" +#include "btc_gattc.h" +#include "btc_gatt_util.h" +#include "stack/l2cdefs.h" +#include "stack/l2c_api.h" +#include "gatt_int.h" + + +#if (GATTC_INCLUDED == TRUE) +esp_err_t esp_ble_gattc_register_callback(esp_gattc_cb_t callback) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + if (callback == NULL) { + return ESP_FAIL; + } + + btc_profile_cb_set(BTC_PID_GATTC, callback); + return ESP_OK; +} + +esp_gattc_cb_t esp_ble_gattc_get_callback(void) +{ + return (esp_gattc_cb_t) btc_profile_cb_get(BTC_PID_GATTC); +} + +esp_err_t esp_ble_gattc_app_register(uint16_t app_id) +{ + btc_msg_t msg = {0}; + btc_ble_gattc_args_t arg; + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + if (app_id > ESP_APP_ID_MAX) { + return ESP_ERR_INVALID_ARG; + } + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GATTC; + msg.act = BTC_GATTC_ACT_APP_REGISTER; + arg.app_reg.app_id = app_id; + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gattc_args_t), NULL, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_ble_gattc_app_unregister(esp_gatt_if_t gattc_if) +{ + btc_msg_t msg = {0}; + btc_ble_gattc_args_t arg; + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GATTC; + msg.act = BTC_GATTC_ACT_APP_UNREGISTER; + arg.app_unreg.gattc_if = gattc_if; + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gattc_args_t), NULL, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} +#if (BLE_42_FEATURE_SUPPORT == TRUE) +esp_err_t esp_ble_gattc_open(esp_gatt_if_t gattc_if, esp_bd_addr_t remote_bda, esp_ble_addr_type_t remote_addr_type, bool is_direct) +{ + btc_msg_t msg = {0}; + btc_ble_gattc_args_t arg; + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GATTC; + msg.act = BTC_GATTC_ACT_OPEN; + arg.open.gattc_if = gattc_if; + memcpy(arg.open.remote_bda, remote_bda, ESP_BD_ADDR_LEN); + arg.open.remote_addr_type = remote_addr_type; + arg.open.is_direct = is_direct; + arg.open.is_aux = false; + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gattc_args_t), NULL, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} +#endif // #if (BLE_42_FEATURE_SUPPORT == TRUE) + +#if (BLE_50_FEATURE_SUPPORT == TRUE) +esp_err_t esp_ble_gattc_aux_open(esp_gatt_if_t gattc_if, esp_bd_addr_t remote_bda, esp_ble_addr_type_t remote_addr_type, bool is_direct) +{ + btc_msg_t msg; + btc_ble_gattc_args_t arg; + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GATTC; + msg.act = BTC_GATTC_ACT_AUX_OPEN; + arg.open.gattc_if = gattc_if; + memcpy(arg.open.remote_bda, remote_bda, ESP_BD_ADDR_LEN); + arg.open.remote_addr_type = remote_addr_type; + arg.open.is_direct = is_direct; + arg.open.is_aux = true; + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gattc_args_t), NULL, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); + +} +#endif // #if (BLE_50_FEATURE_SUPPORT == TRUE) + +esp_err_t esp_ble_gattc_close (esp_gatt_if_t gattc_if, uint16_t conn_id) +{ + btc_msg_t msg = {0}; + btc_ble_gattc_args_t arg; + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GATTC; + msg.act = BTC_GATTC_ACT_CLOSE; + arg.close.conn_id = BTC_GATT_CREATE_CONN_ID(gattc_if, conn_id); + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gattc_args_t), NULL, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_ble_gattc_send_mtu_req (esp_gatt_if_t gattc_if, uint16_t conn_id) +{ + btc_msg_t msg = {0}; + btc_ble_gattc_args_t arg; + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GATTC; + msg.act = BTC_GATTC_ACT_CFG_MTU; + arg.cfg_mtu.conn_id = BTC_GATT_CREATE_CONN_ID(gattc_if, conn_id); + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gattc_args_t), NULL, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_ble_gattc_search_service(esp_gatt_if_t gattc_if, uint16_t conn_id, esp_bt_uuid_t *filter_uuid) +{ + btc_msg_t msg = {0}; + btc_ble_gattc_args_t arg; + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GATTC; + msg.act = BTC_GATTC_ACT_SEARCH_SERVICE; + arg.search_srvc.conn_id = BTC_GATT_CREATE_CONN_ID(gattc_if, conn_id); + + if (filter_uuid) { + arg.search_srvc.filter_uuid_enable = true; + memcpy(&arg.search_srvc.filter_uuid, filter_uuid, sizeof(esp_bt_uuid_t)); + } else { + arg.search_srvc.filter_uuid_enable = false; + } + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gattc_args_t), NULL, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_gatt_status_t esp_ble_gattc_get_service(esp_gatt_if_t gattc_if, uint16_t conn_id, esp_bt_uuid_t *svc_uuid, + esp_gattc_service_elem_t *result, uint16_t *count, uint16_t offset) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + if (result == NULL || count == NULL || *count == 0) { + return ESP_GATT_INVALID_PDU; + } + + uint16_t conn_hdl = BTC_GATT_CREATE_CONN_ID(gattc_if, conn_id); + return btc_ble_gattc_get_service(conn_hdl, svc_uuid, result, count, offset); +} + + +esp_gatt_status_t esp_ble_gattc_get_all_char(esp_gatt_if_t gattc_if, + uint16_t conn_id, + uint16_t start_handle, + uint16_t end_handle, + esp_gattc_char_elem_t *result, + uint16_t *count, uint16_t offset) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + if ((start_handle == 0) && (end_handle == 0)) { + *count = 0; + return ESP_GATT_INVALID_HANDLE; + } + + if (result == NULL || count == NULL || *count == 0) { + return ESP_GATT_INVALID_PDU; + } + + uint16_t conn_hdl = BTC_GATT_CREATE_CONN_ID(gattc_if, conn_id); + return btc_ble_gattc_get_all_char(conn_hdl, start_handle, end_handle, result, count, offset); +} + +esp_gatt_status_t esp_ble_gattc_get_all_descr(esp_gatt_if_t gattc_if, + uint16_t conn_id, + uint16_t char_handle, + esp_gattc_descr_elem_t *result, + uint16_t *count, uint16_t offset) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + if (char_handle == 0) { + return ESP_GATT_INVALID_HANDLE; + } + + if (result == NULL || count == NULL || *count == 0) { + return ESP_GATT_INVALID_PDU; + } + + uint16_t conn_hdl = BTC_GATT_CREATE_CONN_ID(gattc_if, conn_id); + return btc_ble_gattc_get_all_descr(conn_hdl, char_handle, result, count, offset); +} + +esp_gatt_status_t esp_ble_gattc_get_char_by_uuid(esp_gatt_if_t gattc_if, + uint16_t conn_id, + uint16_t start_handle, + uint16_t end_handle, + esp_bt_uuid_t char_uuid, + esp_gattc_char_elem_t *result, + uint16_t *count) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + if (start_handle == 0 && end_handle == 0) { + *count = 0; + return ESP_GATT_INVALID_HANDLE; + } + + if (result == NULL || count == NULL || *count == 0) { + return ESP_GATT_INVALID_PDU; + } + + uint16_t conn_hdl = BTC_GATT_CREATE_CONN_ID(gattc_if,conn_id); + return btc_ble_gattc_get_char_by_uuid(conn_hdl, start_handle, end_handle, char_uuid, result, count); +} + + +esp_gatt_status_t esp_ble_gattc_get_descr_by_uuid(esp_gatt_if_t gattc_if, + uint16_t conn_id, + uint16_t start_handle, + uint16_t end_handle, + esp_bt_uuid_t char_uuid, + esp_bt_uuid_t descr_uuid, + esp_gattc_descr_elem_t *result, + uint16_t *count) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + if (result == NULL || count == NULL || *count == 0) { + return ESP_GATT_INVALID_PDU; + } + + uint16_t conn_hdl = BTC_GATT_CREATE_CONN_ID(gattc_if, conn_id); + return btc_ble_gattc_get_descr_by_uuid(conn_hdl, start_handle, end_handle, char_uuid, descr_uuid, result, count); +} + +esp_gatt_status_t esp_ble_gattc_get_descr_by_char_handle(esp_gatt_if_t gattc_if, + uint16_t conn_id, + uint16_t char_handle, + esp_bt_uuid_t descr_uuid, + esp_gattc_descr_elem_t *result, + uint16_t *count) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + if (char_handle == 0) { + *count = 0; + return ESP_GATT_INVALID_HANDLE; + } + + if (result == NULL || count == NULL || *count == 0) { + return ESP_GATT_INVALID_PDU; + } + + uint16_t conn_hdl = BTC_GATT_CREATE_CONN_ID(gattc_if, conn_id); + return btc_ble_gattc_get_descr_by_char_handle(conn_hdl, char_handle, descr_uuid, result, count); +} + +esp_gatt_status_t esp_ble_gattc_get_include_service(esp_gatt_if_t gattc_if, + uint16_t conn_id, + uint16_t start_handle, + uint16_t end_handle, + esp_bt_uuid_t *incl_uuid, + esp_gattc_incl_svc_elem_t *result, + uint16_t *count) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + if (start_handle == 0 && end_handle == 0) { + *count = 0; + return ESP_GATT_INVALID_HANDLE; + } + + if (result == NULL || count == NULL || *count == 0) { + return ESP_GATT_INVALID_PDU; + } + + uint16_t conn_hdl = BTC_GATT_CREATE_CONN_ID(gattc_if, conn_id); + return btc_ble_gattc_get_include_service(conn_hdl, start_handle, end_handle, incl_uuid, result, count); +} + +esp_gatt_status_t esp_ble_gattc_get_attr_count(esp_gatt_if_t gattc_if, + uint16_t conn_id, + esp_gatt_db_attr_type_t type, + uint16_t start_handle, + uint16_t end_handle, + uint16_t char_handle, + uint16_t *count) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + if ((start_handle == 0 && end_handle == 0) && (type != ESP_GATT_DB_DESCRIPTOR)) { + *count = 0; + return ESP_GATT_INVALID_HANDLE; + } + + if (count == NULL) { + return ESP_GATT_INVALID_PDU; + } + + uint16_t conn_hdl = BTC_GATT_CREATE_CONN_ID(gattc_if, conn_id); + return btc_ble_gattc_get_attr_count(conn_hdl, type, start_handle, end_handle, char_handle, count); +} + +esp_gatt_status_t esp_ble_gattc_get_db(esp_gatt_if_t gattc_if, uint16_t conn_id, uint16_t start_handle, uint16_t end_handle, + esp_gattc_db_elem_t *db, uint16_t *count) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + if (start_handle == 0 && end_handle == 0) { + *count = 0; + return ESP_GATT_INVALID_HANDLE; + } + + if (db == NULL || count == NULL || *count == 0) { + return ESP_GATT_INVALID_PDU; + } + + uint16_t conn_hdl = BTC_GATT_CREATE_CONN_ID(gattc_if, conn_id); + return btc_ble_gattc_get_db(conn_hdl, start_handle, end_handle, db, count); +} + + +esp_err_t esp_ble_gattc_read_char (esp_gatt_if_t gattc_if, + uint16_t conn_id, uint16_t handle, + esp_gatt_auth_req_t auth_req) +{ + btc_msg_t msg = {0}; + btc_ble_gattc_args_t arg; + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + tGATT_TCB *p_tcb = gatt_get_tcb_by_idx(conn_id); + if (!gatt_check_connection_state_by_tcb(p_tcb)) { + LOG_WARN("%s, The connection not created.", __func__); + return ESP_ERR_INVALID_STATE; + } + + if (L2CA_CheckIsCongest(L2CAP_ATT_CID, p_tcb->peer_bda)) { + LOG_DEBUG("%s, the l2cap chanel is congest.", __func__); + return ESP_FAIL; + } + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GATTC; + msg.act = BTC_GATTC_ACT_READ_CHAR; + arg.read_char.conn_id = BTC_GATT_CREATE_CONN_ID(gattc_if, conn_id); + arg.read_char.handle = handle; + arg.read_char.auth_req = auth_req; + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gattc_args_t), NULL, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_ble_gattc_read_by_type (esp_gatt_if_t gattc_if, + uint16_t conn_id, + uint16_t start_handle, + uint16_t end_handle, + esp_bt_uuid_t *uuid, + esp_gatt_auth_req_t auth_req) +{ + btc_msg_t msg = {0}; + btc_ble_gattc_args_t arg; + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + if (uuid == NULL) { + return ESP_GATT_ILLEGAL_PARAMETER; + } + + tGATT_TCB *p_tcb = gatt_get_tcb_by_idx(conn_id); + if (!gatt_check_connection_state_by_tcb(p_tcb)) { + LOG_WARN("%s, The connection not created.", __func__); + return ESP_ERR_INVALID_STATE; + } + + if (L2CA_CheckIsCongest(L2CAP_ATT_CID, p_tcb->peer_bda)) { + LOG_DEBUG("%s, the l2cap chanel is congest.", __func__); + return ESP_FAIL; + } + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GATTC; + msg.act = BTC_GATTC_ACT_READ_BY_TYPE; + arg.read_by_type.conn_id = BTC_GATT_CREATE_CONN_ID(gattc_if, conn_id); + arg.read_by_type.s_handle = start_handle; + arg.read_by_type.e_handle = end_handle; + arg.read_by_type.auth_req = auth_req; + memcpy(&(arg.read_by_type.uuid), uuid, sizeof(esp_bt_uuid_t)); + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gattc_args_t), NULL, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_ble_gattc_read_multiple(esp_gatt_if_t gattc_if, + uint16_t conn_id, esp_gattc_multi_t *read_multi, + esp_gatt_auth_req_t auth_req) +{ + btc_msg_t msg = {0}; + btc_ble_gattc_args_t arg; + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + tGATT_TCB *p_tcb = gatt_get_tcb_by_idx(conn_id); + if (!gatt_check_connection_state_by_tcb(p_tcb)) { + LOG_WARN("%s, The connection not created.", __func__); + return ESP_ERR_INVALID_STATE; + } + + if (L2CA_CheckIsCongest(L2CAP_ATT_CID, p_tcb->peer_bda)) { + LOG_DEBUG("%s, the l2cap chanel is congest.", __func__); + return ESP_FAIL; + } + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GATTC; + msg.act = BTC_GATTC_ACT_READ_MULTIPLE_CHAR; + arg.read_multiple.conn_id = BTC_GATT_CREATE_CONN_ID(gattc_if, conn_id); + arg.read_multiple.num_attr = read_multi->num_attr; + arg.read_multiple.auth_req = auth_req; + + if (read_multi->num_attr > 0) { + memcpy(arg.read_multiple.handles, read_multi->handles, sizeof(uint16_t)*read_multi->num_attr); + } else { + LOG_ERROR("%s(), the num_attr should not be 0.", __func__); + return ESP_FAIL; + } + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gattc_args_t), NULL, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_ble_gattc_read_multiple_variable(esp_gatt_if_t gattc_if, + uint16_t conn_id, esp_gattc_multi_t *read_multi, + esp_gatt_auth_req_t auth_req) +{ + btc_msg_t msg = {0}; + btc_ble_gattc_args_t arg; + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + tGATT_TCB *p_tcb = gatt_get_tcb_by_idx(conn_id); + if (!gatt_check_connection_state_by_tcb(p_tcb)) { + LOG_WARN("%s, The connection not created.", __func__); + return ESP_ERR_INVALID_STATE; + } + + if (L2CA_CheckIsCongest(L2CAP_ATT_CID, p_tcb->peer_bda)) { + LOG_DEBUG("%s, the l2cap chanel is congest.", __func__); + return ESP_FAIL; + } + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GATTC; + msg.act = BTC_GATTC_ACT_READ_MULTIPLE_VARIABLE_CHAR; + arg.read_multiple.conn_id = BTC_GATT_CREATE_CONN_ID(gattc_if, conn_id); + arg.read_multiple.num_attr = read_multi->num_attr; + arg.read_multiple.auth_req = auth_req; + + if (read_multi->num_attr > 0) { + memcpy(arg.read_multiple.handles, read_multi->handles, sizeof(uint16_t)*read_multi->num_attr); + } else { + LOG_ERROR("%s(), the num_attr should not be 0.", __func__); + return ESP_FAIL; + } + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gattc_args_t), NULL, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_ble_gattc_read_char_descr (esp_gatt_if_t gattc_if, + uint16_t conn_id, uint16_t handle, + esp_gatt_auth_req_t auth_req) +{ + btc_msg_t msg = {0}; + btc_ble_gattc_args_t arg; + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + tGATT_TCB *p_tcb = gatt_get_tcb_by_idx(conn_id); + if (!gatt_check_connection_state_by_tcb(p_tcb)) { + LOG_WARN("%s, The connection not created.", __func__); + return ESP_ERR_INVALID_STATE; + } + + if (L2CA_CheckIsCongest(L2CAP_ATT_CID, p_tcb->peer_bda)) { + LOG_DEBUG("%s, the l2cap chanel is congest.", __func__); + return ESP_FAIL; + } + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GATTC; + msg.act = BTC_GATTC_ACT_READ_CHAR_DESCR; + arg.read_descr.conn_id = BTC_GATT_CREATE_CONN_ID(gattc_if, conn_id); + arg.read_descr.handle = handle; + arg.read_descr.auth_req = auth_req; + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gattc_args_t), NULL, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_ble_gattc_write_char(esp_gatt_if_t gattc_if, + uint16_t conn_id, uint16_t handle, + uint16_t value_len, + uint8_t *value, + esp_gatt_write_type_t write_type, + esp_gatt_auth_req_t auth_req) +{ + btc_msg_t msg = {0}; + btc_ble_gattc_args_t arg; + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + tGATT_TCB *p_tcb = gatt_get_tcb_by_idx(conn_id); + if (!gatt_check_connection_state_by_tcb(p_tcb)) { + LOG_WARN("%s, The connection not created.", __func__); + return ESP_ERR_INVALID_STATE; + } + + if (L2CA_CheckIsCongest(L2CAP_ATT_CID, p_tcb->peer_bda)) { + LOG_DEBUG("%s, the l2cap chanel is congest.", __func__); + return ESP_FAIL; + } + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GATTC; + msg.act = BTC_GATTC_ACT_WRITE_CHAR; + arg.write_char.conn_id = BTC_GATT_CREATE_CONN_ID(gattc_if, conn_id); + arg.write_char.handle = handle; + arg.write_char.value_len = value_len > ESP_GATT_MAX_ATTR_LEN ? ESP_GATT_MAX_ATTR_LEN : value_len; + arg.write_char.value = value; + arg.write_char.write_type = write_type; + arg.write_char.auth_req = auth_req; + if(write_type == ESP_GATT_WRITE_TYPE_NO_RSP){ + l2ble_update_att_acl_pkt_num(L2CA_ADD_BTC_NUM, NULL); + } + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gattc_args_t), btc_gattc_arg_deep_copy, + btc_gattc_arg_deep_free) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_ble_gattc_write_char_descr (esp_gatt_if_t gattc_if, + uint16_t conn_id, uint16_t handle, + uint16_t value_len, + uint8_t *value, + esp_gatt_write_type_t write_type, + esp_gatt_auth_req_t auth_req) +{ + btc_msg_t msg = {0}; + btc_ble_gattc_args_t arg; + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + tGATT_TCB *p_tcb = gatt_get_tcb_by_idx(conn_id); + if (!gatt_check_connection_state_by_tcb(p_tcb)) { + LOG_WARN("%s, The connection not created.", __func__); + return ESP_ERR_INVALID_STATE; + } + + if (L2CA_CheckIsCongest(L2CAP_ATT_CID, p_tcb->peer_bda)) { + LOG_DEBUG("%s, the l2cap chanel is congest.", __func__); + return ESP_FAIL; + } + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GATTC; + msg.act = BTC_GATTC_ACT_WRITE_CHAR_DESCR; + arg.write_descr.conn_id = BTC_GATT_CREATE_CONN_ID(gattc_if, conn_id); + arg.write_descr.handle = handle; + arg.write_descr.value_len = value_len > ESP_GATT_MAX_ATTR_LEN ? ESP_GATT_MAX_ATTR_LEN : value_len; + arg.write_descr.value = value; + arg.write_descr.write_type = write_type; + arg.write_descr.auth_req = auth_req; + if(write_type == ESP_GATT_WRITE_TYPE_NO_RSP){ + l2ble_update_att_acl_pkt_num(L2CA_ADD_BTC_NUM, NULL); + } + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gattc_args_t), btc_gattc_arg_deep_copy, + btc_gattc_arg_deep_free) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_ble_gattc_prepare_write(esp_gatt_if_t gattc_if, + uint16_t conn_id, uint16_t handle, + uint16_t offset, + uint16_t value_len, + uint8_t *value, + esp_gatt_auth_req_t auth_req) +{ + btc_msg_t msg = {0}; + btc_ble_gattc_args_t arg; + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + tGATT_TCB *p_tcb = gatt_get_tcb_by_idx(conn_id); + if (!gatt_check_connection_state_by_tcb(p_tcb)) { + LOG_WARN("%s, The connection not created.", __func__); + return ESP_ERR_INVALID_STATE; + } + + if (L2CA_CheckIsCongest(L2CAP_ATT_CID, p_tcb->peer_bda)) { + LOG_DEBUG("%s, the l2cap chanel is congest.", __func__); + return ESP_FAIL; + } + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GATTC; + msg.act = BTC_GATTC_ACT_PREPARE_WRITE; + arg.prep_write.conn_id = BTC_GATT_CREATE_CONN_ID(gattc_if, conn_id); + arg.prep_write.handle = handle; + arg.prep_write.offset = offset; + arg.prep_write.value_len = value_len > ESP_GATT_MAX_ATTR_LEN ? ESP_GATT_MAX_ATTR_LEN : value_len; // length check ? + arg.prep_write.value = value; + arg.prep_write.auth_req = auth_req; + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gattc_args_t), btc_gattc_arg_deep_copy, + btc_gattc_arg_deep_free) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_ble_gattc_prepare_write_char_descr(esp_gatt_if_t gattc_if, + uint16_t conn_id, uint16_t handle, + uint16_t offset, + uint16_t value_len, + uint8_t *value, + esp_gatt_auth_req_t auth_req) +{ + btc_msg_t msg = {0}; + btc_ble_gattc_args_t arg; + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + tGATT_TCB *p_tcb = gatt_get_tcb_by_idx(conn_id); + if (!gatt_check_connection_state_by_tcb(p_tcb)) { + LOG_WARN("%s, The connection not created.", __func__); + return ESP_ERR_INVALID_STATE; + } + + if (L2CA_CheckIsCongest(L2CAP_ATT_CID, p_tcb->peer_bda)) { + LOG_DEBUG("%s, the l2cap chanel is congest.", __func__); + return ESP_FAIL; + } + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GATTC; + msg.act = BTC_GATTC_ACT_PREPARE_WRITE_CHAR_DESCR; + arg.prep_write_descr.conn_id = BTC_GATT_CREATE_CONN_ID(gattc_if, conn_id); + arg.prep_write_descr.handle = handle; + arg.prep_write_descr.offset = offset; + arg.prep_write_descr.value_len = value_len > ESP_GATT_MAX_ATTR_LEN ? ESP_GATT_MAX_ATTR_LEN : value_len; // length check ? + arg.prep_write_descr.value = value; + arg.prep_write_descr.auth_req = auth_req; + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gattc_args_t), btc_gattc_arg_deep_copy, + btc_gattc_arg_deep_free) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_ble_gattc_execute_write (esp_gatt_if_t gattc_if, uint16_t conn_id, bool is_execute) +{ + btc_msg_t msg = {0}; + btc_ble_gattc_args_t arg; + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GATTC; + msg.act = BTC_GATTC_ACT_EXECUTE_WRITE; + arg.exec_write.conn_id = BTC_GATT_CREATE_CONN_ID(gattc_if, conn_id); + arg.exec_write.is_execute = is_execute; + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gattc_args_t), NULL, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_ble_gattc_register_for_notify (esp_gatt_if_t gattc_if, + esp_bd_addr_t server_bda, uint16_t handle) +{ + btc_msg_t msg = {0}; + btc_ble_gattc_args_t arg; + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GATTC; + msg.act = BTC_GATTC_ACT_REG_FOR_NOTIFY; + arg.reg_for_notify.gattc_if = gattc_if; + memcpy(arg.reg_for_notify.remote_bda, server_bda, sizeof(esp_bd_addr_t)); + arg.reg_for_notify.handle = handle; + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gattc_args_t), NULL, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_ble_gattc_unregister_for_notify (esp_gatt_if_t gattc_if, + esp_bd_addr_t server_bda, uint16_t handle) +{ + btc_msg_t msg = {0}; + btc_ble_gattc_args_t arg; + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GATTC; + msg.act = BTC_GATTC_ACT_UNREG_FOR_NOTIFY; + arg.unreg_for_notify.gattc_if = gattc_if; + arg.unreg_for_notify.handle = handle; + memcpy(arg.unreg_for_notify.remote_bda, server_bda, sizeof(esp_bd_addr_t)); + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gattc_args_t), NULL, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_ble_gattc_cache_refresh(esp_bd_addr_t remote_bda) +{ + btc_msg_t msg = {0}; + btc_ble_gattc_args_t arg; + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GATTC; + msg.act = BTC_GATTC_ACT_CACHE_REFRESH; + memcpy(arg.cache_refresh.remote_bda, remote_bda, sizeof(esp_bd_addr_t)); + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gattc_args_t), NULL, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_ble_gattc_cache_clean(esp_bd_addr_t remote_bda) +{ + btc_msg_t msg = {0}; + btc_ble_gattc_args_t arg; + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GATTC; + msg.act = BTC_GATTC_ACT_CACHE_CLEAN; + memcpy(arg.cache_clean.remote_bda, remote_bda, sizeof(esp_bd_addr_t)); + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gattc_args_t), NULL, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_ble_gattc_cache_assoc(esp_gatt_if_t gattc_if, esp_bd_addr_t src_addr, esp_bd_addr_t assoc_addr, bool is_assoc) +{ + btc_msg_t msg = {0}; + btc_ble_gattc_args_t arg; + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GATTC; + msg.act = BTC_GATTC_ACT_CACHE_ASSOC; + arg.cache_assoc.is_assoc = is_assoc; + arg.cache_assoc.gattc_if = gattc_if; + memcpy(arg.cache_assoc.src_addr, src_addr, sizeof(esp_bd_addr_t)); + memcpy(arg.cache_assoc.assoc_addr, assoc_addr, sizeof(esp_bd_addr_t)); + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gattc_args_t), NULL, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_ble_gattc_cache_get_addr_list(esp_gatt_if_t gattc_if) +{ + btc_msg_t msg = {0}; + btc_ble_gattc_args_t arg; + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GATTC; + msg.act = BTC_GATTC_ATC_CACHE_GET_ADDR_LIST; + arg.get_addr_list.gattc_if = gattc_if; + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gattc_args_t), NULL, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +#endif ///GATTC_INCLUDED == TRUE diff --git a/lib/bt/host/bluedroid/api/esp_gatts_api.c b/lib/bt/host/bluedroid/api/esp_gatts_api.c new file mode 100644 index 00000000..66fa5c66 --- /dev/null +++ b/lib/bt/host/bluedroid/api/esp_gatts_api.c @@ -0,0 +1,442 @@ +/* + * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "string.h" +#include "esp_gatt_defs.h" +#include "esp_gatts_api.h" +#include "esp_bt_main.h" +#include "btc/btc_manage.h" +#include "btc_gatts.h" +#include "btc_gatt_util.h" +#include "common/bt_target.h" +#include "stack/l2cdefs.h" +#include "stack/l2c_api.h" +#include "gatt_int.h" + +#if (GATTS_INCLUDED == TRUE) +#define COPY_TO_GATTS_ARGS(_gatt_args, _arg, _arg_type) memcpy(_gatt_args, _arg, sizeof(_arg_type)) + +static esp_err_t esp_ble_gatts_add_char_desc_param_check(esp_attr_value_t *char_val, esp_attr_control_t *control); + + +esp_err_t esp_ble_gatts_register_callback(esp_gatts_cb_t callback) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + return (btc_profile_cb_set(BTC_PID_GATTS, callback) == 0 ? ESP_OK : ESP_FAIL); +} + +esp_gatts_cb_t esp_ble_gatts_get_callback(void) +{ + return (esp_gatts_cb_t) btc_profile_cb_get(BTC_PID_GATTS); +} + +esp_err_t esp_ble_gatts_app_register(uint16_t app_id) +{ + btc_msg_t msg = {0}; + btc_ble_gatts_args_t arg; + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + //if (app_id < ESP_APP_ID_MIN || app_id > ESP_APP_ID_MAX) { + if (app_id > ESP_APP_ID_MAX) { + return ESP_ERR_INVALID_ARG; + } + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GATTS; + msg.act = BTC_GATTS_ACT_APP_REGISTER; + arg.app_reg.app_id = app_id; + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gatts_args_t), NULL, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + + +esp_err_t esp_ble_gatts_app_unregister(esp_gatt_if_t gatts_if) +{ + btc_msg_t msg = {0}; + btc_ble_gatts_args_t arg; + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GATTS; + msg.act = BTC_GATTS_ACT_APP_UNREGISTER; + arg.app_unreg.gatts_if = gatts_if; + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gatts_args_t), NULL, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_ble_gatts_create_service(esp_gatt_if_t gatts_if, + esp_gatt_srvc_id_t *service_id, uint16_t num_handle) +{ + btc_msg_t msg = {0}; + btc_ble_gatts_args_t arg; + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GATTS; + msg.act = BTC_GATTS_ACT_CREATE_SERVICE; + arg.create_srvc.gatts_if = gatts_if; + arg.create_srvc.num_handle = num_handle; + memcpy(&arg.create_srvc.service_id, service_id, sizeof(esp_gatt_srvc_id_t)); + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gatts_args_t), NULL, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_ble_gatts_create_attr_tab(const esp_gatts_attr_db_t *gatts_attr_db, + esp_gatt_if_t gatts_if, + uint16_t max_nb_attr, + uint8_t srvc_inst_id) +{ + btc_msg_t msg = {0}; + btc_ble_gatts_args_t arg; + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + if (max_nb_attr > ESP_GATT_ATTR_HANDLE_MAX) { + LOG_ERROR("The number of attribute should not be greater than CONFIG_BT_GATT_MAX_SR_ATTRIBUTES\n"); + return ESP_ERR_INVALID_ARG; + } + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GATTS; + msg.act = BTC_GATTS_ACT_CREATE_ATTR_TAB; + arg.create_attr_tab.gatts_if = gatts_if; + arg.create_attr_tab.max_nb_attr = max_nb_attr; + arg.create_attr_tab.srvc_inst_id = srvc_inst_id; + arg.create_attr_tab.gatts_attr_db = (esp_gatts_attr_db_t *)gatts_attr_db; + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gatts_args_t), btc_gatts_arg_deep_copy, + btc_gatts_arg_deep_free) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + + +esp_err_t esp_ble_gatts_add_included_service(uint16_t service_handle, uint16_t included_service_handle) +{ + btc_msg_t msg = {0}; + btc_ble_gatts_args_t arg; + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GATTS; + msg.act = BTC_GATTS_ACT_ADD_INCLUDE_SERVICE; + arg.add_incl_srvc.service_handle = service_handle; + arg.add_incl_srvc.included_service_handle = included_service_handle; + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gatts_args_t), NULL, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + + +esp_err_t esp_ble_gatts_add_char(uint16_t service_handle, esp_bt_uuid_t *char_uuid, + esp_gatt_perm_t perm, esp_gatt_char_prop_t property, esp_attr_value_t *char_val, + esp_attr_control_t *control) +{ + btc_msg_t msg = {0}; + btc_ble_gatts_args_t arg; + esp_err_t status; + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + /* parameter validation check */ + status = esp_ble_gatts_add_char_desc_param_check(char_val, control); + if (status != ESP_OK){ + return status; + } + + memset(&arg, 0, sizeof(btc_ble_gatts_args_t)); + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GATTS; + msg.act = BTC_GATTS_ACT_ADD_CHAR; + arg.add_char.service_handle = service_handle; + arg.add_char.perm = perm; + arg.add_char.property = property; + if (char_val != NULL) { + arg.add_char.char_val.attr_max_len = char_val->attr_max_len; + arg.add_char.char_val.attr_len = char_val->attr_len; + arg.add_char.char_val.attr_value = char_val->attr_value; + } + + if (control != NULL) { + arg.add_char.attr_control.auto_rsp = control->auto_rsp; + } + memcpy(&arg.add_char.char_uuid, char_uuid, sizeof(esp_bt_uuid_t)); + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gatts_args_t), btc_gatts_arg_deep_copy, + btc_gatts_arg_deep_free) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + + +esp_err_t esp_ble_gatts_add_char_descr (uint16_t service_handle, + esp_bt_uuid_t *descr_uuid, + esp_gatt_perm_t perm, esp_attr_value_t *char_descr_val, + esp_attr_control_t *control) +{ + btc_msg_t msg = {0}; + btc_ble_gatts_args_t arg; + esp_err_t status; + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + /* parameter validation check */ + status = esp_ble_gatts_add_char_desc_param_check(char_descr_val, control); + if (status != ESP_OK){ + return status; + } + + memset(&arg, 0, sizeof(btc_ble_gatts_args_t)); + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GATTS; + msg.act = BTC_GATTS_ACT_ADD_CHAR_DESCR; + arg.add_descr.service_handle = service_handle; + arg.add_descr.perm = perm; + + if (char_descr_val != NULL) { + arg.add_descr.descr_val.attr_max_len = char_descr_val->attr_max_len; + arg.add_descr.descr_val.attr_len = char_descr_val->attr_len; + arg.add_descr.descr_val.attr_value = char_descr_val->attr_value; + } + + if (control != NULL) { + arg.add_descr.attr_control.auto_rsp = control->auto_rsp; + } + memcpy(&arg.add_descr.descr_uuid, descr_uuid, sizeof(esp_bt_uuid_t)); + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gatts_args_t), btc_gatts_arg_deep_copy, + btc_gatts_arg_deep_free) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_ble_gatts_delete_service(uint16_t service_handle) +{ + btc_msg_t msg = {0}; + btc_ble_gatts_args_t arg; + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GATTS; + msg.act = BTC_GATTS_ACT_DELETE_SERVICE; + arg.delete_srvc.service_handle = service_handle; + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gatts_args_t), NULL, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_ble_gatts_start_service(uint16_t service_handle) +{ + btc_msg_t msg = {0}; + btc_ble_gatts_args_t arg; + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GATTS; + msg.act = BTC_GATTS_ACT_START_SERVICE; + arg.start_srvc.service_handle = service_handle; + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gatts_args_t), NULL, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_ble_gatts_stop_service(uint16_t service_handle) +{ + btc_msg_t msg = {0}; + btc_ble_gatts_args_t arg; + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GATTS; + msg.act = BTC_GATTS_ACT_STOP_SERVICE; + arg.stop_srvc.service_handle = service_handle; + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gatts_args_t), NULL, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + + +esp_err_t esp_ble_gatts_send_indicate(esp_gatt_if_t gatts_if, uint16_t conn_id, uint16_t attr_handle, + uint16_t value_len, uint8_t *value, bool need_confirm) +{ + btc_msg_t msg = {0}; + btc_ble_gatts_args_t arg; + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + tGATT_TCB *p_tcb = gatt_get_tcb_by_idx(conn_id); + if (!gatt_check_connection_state_by_tcb(p_tcb)) { + LOG_WARN("%s, The connection not created.", __func__); + return ESP_ERR_INVALID_STATE; + } + + if (L2CA_CheckIsCongest(L2CAP_ATT_CID, p_tcb->peer_bda)) { + LOG_DEBUG("%s, the l2cap chanel is congest.", __func__); + return ESP_FAIL; + } + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GATTS; + msg.act = BTC_GATTS_ACT_SEND_INDICATE; + arg.send_ind.conn_id = BTC_GATT_CREATE_CONN_ID(gatts_if, conn_id); + arg.send_ind.attr_handle = attr_handle; + arg.send_ind.need_confirm = need_confirm; + arg.send_ind.value_len = value_len; + arg.send_ind.value = value; + if(need_confirm == false){ + l2ble_update_att_acl_pkt_num(L2CA_ADD_BTC_NUM, NULL); + } + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gatts_args_t), btc_gatts_arg_deep_copy, + btc_gatts_arg_deep_free) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_ble_gatts_send_response(esp_gatt_if_t gatts_if, uint16_t conn_id, uint32_t trans_id, + esp_gatt_status_t status, esp_gatt_rsp_t *rsp) +{ + btc_msg_t msg = {0}; + btc_ble_gatts_args_t arg; + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + tGATT_TCB *p_tcb = gatt_get_tcb_by_idx(conn_id); + if (!gatt_check_connection_state_by_tcb(p_tcb)) { + LOG_WARN("%s, The connection not created.", __func__); + return ESP_ERR_INVALID_STATE; + } + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GATTS; + msg.act = BTC_GATTS_ACT_SEND_RESPONSE; + arg.send_rsp.conn_id = BTC_GATT_CREATE_CONN_ID(gatts_if, conn_id); + arg.send_rsp.trans_id = trans_id; + arg.send_rsp.status = status; + arg.send_rsp.rsp = rsp; + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gatts_args_t), btc_gatts_arg_deep_copy, + btc_gatts_arg_deep_free) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_ble_gatts_set_attr_value(uint16_t attr_handle, uint16_t length, const uint8_t *value) +{ + btc_msg_t msg = {0}; + btc_ble_gatts_args_t arg; + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GATTS; + msg.act = BTC_GATTS_ACT_SET_ATTR_VALUE; + arg.set_attr_val.handle = attr_handle; + arg.set_attr_val.length = length; + arg.set_attr_val.value = (uint8_t *)value; + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gatts_args_t), btc_gatts_arg_deep_copy, + btc_gatts_arg_deep_free) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_gatt_status_t esp_ble_gatts_get_attr_value(uint16_t attr_handle, uint16_t *length, const uint8_t **value) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + if (attr_handle == ESP_GATT_ILLEGAL_HANDLE) { + *length = 0; + return ESP_GATT_INVALID_HANDLE; + } + + return btc_gatts_get_attr_value(attr_handle, length, (uint8_t **)value); +} + +esp_err_t esp_ble_gatts_open(esp_gatt_if_t gatts_if, esp_bd_addr_t remote_bda, bool is_direct) +{ + btc_msg_t msg = {0}; + btc_ble_gatts_args_t arg; + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GATTS; + msg.act = BTC_GATTS_ACT_OPEN; + arg.open.gatts_if = gatts_if; + arg.open.is_direct = is_direct; + memcpy(&arg.open.remote_bda, remote_bda, sizeof(esp_bd_addr_t)); + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gatts_args_t), NULL, NULL) + == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_ble_gatts_close(esp_gatt_if_t gatts_if, uint16_t conn_id) +{ + btc_msg_t msg = {0}; + btc_ble_gatts_args_t arg; + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GATTS; + msg.act = BTC_GATTS_ACT_CLOSE; + arg.close.conn_id = BTC_GATT_CREATE_CONN_ID(gatts_if, conn_id); + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gatts_args_t), NULL, NULL) + == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_ble_gatts_send_service_change_indication(esp_gatt_if_t gatts_if, esp_bd_addr_t remote_bda) +{ + btc_msg_t msg = {0}; + btc_ble_gatts_args_t arg; + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GATTS; + msg.act = BTC_GATTS_ACT_SEND_SERVICE_CHANGE; + arg.send_service_change.gatts_if = gatts_if; + if(remote_bda) { + memcpy(&arg.send_service_change.remote_bda, remote_bda, sizeof(esp_bd_addr_t)); + } else { + memset(arg.send_service_change.remote_bda, 0, sizeof(esp_bd_addr_t)); + } + + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gatts_args_t), NULL, NULL) + == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +static esp_err_t esp_ble_gatts_add_char_desc_param_check(esp_attr_value_t *char_val, esp_attr_control_t *control) +{ + if ((control != NULL) && ((control->auto_rsp != ESP_GATT_AUTO_RSP) && (control->auto_rsp != ESP_GATT_RSP_BY_APP))){ + LOG_ERROR("Error in %s, line=%d, control->auto_rsp should be set to ESP_GATT_AUTO_RSP or ESP_GATT_RSP_BY_APP\n",\ + __func__, __LINE__); + return ESP_ERR_INVALID_ARG; + } + + if ((control != NULL) && (control->auto_rsp == ESP_GATT_AUTO_RSP)){ + if (char_val == NULL){ + LOG_ERROR("Error in %s, line=%d, for stack respond attribute, char_val should not be NULL here\n",\ + __func__, __LINE__); + return ESP_ERR_INVALID_ARG; + } else if (char_val->attr_max_len == 0){ + LOG_ERROR("Error in %s, line=%d, for stack respond attribute, attribute max length should not be 0\n",\ + __func__, __LINE__); + return ESP_ERR_INVALID_ARG; + } + } + + return ESP_OK; +} + +esp_err_t esp_ble_gatts_show_local_database(void) +{ + btc_msg_t msg = {0}; + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GATTS; + msg.act = BTC_GATTS_ACT_SHOW_LOCAL_DATABASE; + + return (btc_transfer_context(&msg, NULL, 0, NULL, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +#endif ///GATTS_INCLUDED diff --git a/lib/bt/host/bluedroid/api/esp_hf_ag_api.c b/lib/bt/host/bluedroid/api/esp_hf_ag_api.c new file mode 100644 index 00000000..6eb6a6b5 --- /dev/null +++ b/lib/bt/host/bluedroid/api/esp_hf_ag_api.c @@ -0,0 +1,578 @@ +/* + * SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include "bt_common.h" +#include "btc/btc_common.h" +#include "btc/btc_dm.h" +#include "btc_hf_ag.h" +#include "btc/btc_profile_queue.h" +#include "btc/btc_manage.h" +#include "btc/btc_util.h" +#include "bta/bta_ag_api.h" +#include "bta/bta_api.h" +#include "common/bt_target.h" +#include "common/bt_defs.h" +#include "device/bdaddr.h" +#if (BT_CONTROLLER_INCLUDED == TRUE) +#include "esp_bt.h" +#endif +#include "esp_hf_ag_api.h" +#include "esp_err.h" +#include "esp_bt_main.h" +#include "osi/allocator.h" + +#if (BTC_HF_INCLUDED == TRUE) +esp_err_t esp_hf_ag_register_callback(esp_hf_cb_t callback) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + if (callback == NULL) { + return ESP_FAIL; + } + btc_profile_cb_set(BTC_PID_HF, callback); + return ESP_OK; +} + +esp_err_t esp_hf_ag_init(void) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + btc_msg_t msg; + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HF; + msg.act = BTC_HF_INIT_EVT; + + /* Switch to BTC context */ + bt_status_t status = btc_transfer_context(&msg, NULL, 0, NULL, NULL); + return (status == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_hf_ag_deinit(void) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + btc_msg_t msg; + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HF; + msg.act = BTC_HF_DEINIT_EVT; + + /* Switch to BTC context */ + bt_status_t status = btc_transfer_context(&msg, NULL, 0, NULL, NULL); + return (status == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_hf_ag_slc_connect(esp_bd_addr_t remote_addr) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + btc_msg_t msg; + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HF; + msg.act = BTC_HF_CONNECT_EVT; + + btc_hf_args_t arg; + memset(&arg, 0, sizeof(btc_hf_args_t)); + memcpy(&(arg.connect), remote_addr, sizeof(esp_bd_addr_t)); + + /* Switch to BTC context */ + bt_status_t status = btc_transfer_context(&msg, &arg, sizeof(btc_hf_args_t), NULL, NULL); + return (status == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_hf_ag_slc_disconnect(esp_bd_addr_t remote_addr) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + btc_msg_t msg; + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HF; + msg.act = BTC_HF_DISCONNECT_EVT; + + btc_hf_args_t arg; + memset(&arg, 0, sizeof(btc_hf_args_t)); + memcpy(&(arg.disconnect), remote_addr, sizeof(esp_bd_addr_t)); + + /* Switch to BTC context */ + bt_status_t status = btc_transfer_context(&msg, &arg, sizeof(btc_hf_args_t), NULL, NULL); + return (status == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_hf_ag_audio_connect(esp_bd_addr_t remote_addr) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + btc_msg_t msg; + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HF; + msg.act = BTC_HF_CONNECT_AUDIO_EVT; + + btc_hf_args_t arg; + memset(&arg, 0, sizeof(btc_hf_args_t)); + memcpy(&(arg.connect_audio), remote_addr, sizeof(esp_bd_addr_t)); + + /* Switch to BTC context */ + bt_status_t status = btc_transfer_context(&msg, &arg, sizeof(btc_hf_args_t), NULL, NULL); + return (status == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_hf_ag_audio_disconnect(esp_bd_addr_t remote_addr) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + btc_msg_t msg; + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HF; + msg.act = BTC_HF_DISCONNECT_AUDIO_EVT; + + btc_hf_args_t arg; + memset(&arg, 0, sizeof(btc_hf_args_t)); + memcpy(&(arg.disconnect_audio), remote_addr, sizeof(esp_bd_addr_t)); + + /* Switch to BTC context */ + bt_status_t status = btc_transfer_context(&msg, &arg, sizeof(btc_hf_args_t), NULL, NULL); + return (status == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_hf_ag_vra_control(esp_bd_addr_t remote_addr, esp_hf_vr_state_t value) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + btc_msg_t msg; + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HF; + msg.act = BTC_HF_VRA_EVT; + + btc_hf_args_t arg; + memset(&arg, 0, sizeof(btc_hf_args_t)); + arg.vra_rep.value = value; + memcpy(&(arg.volcon.remote_addr), remote_addr, sizeof(esp_bd_addr_t)); + + /* Switch to BTC context */ + bt_status_t status = btc_transfer_context(&msg, &arg, sizeof(btc_hf_args_t), NULL, NULL); + return (status == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_hf_ag_volume_control(esp_bd_addr_t remote_addr, esp_hf_volume_control_target_t type, int volume) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + if (volume < 0 || volume > 15) { + return ESP_ERR_INVALID_ARG; + } + btc_msg_t msg; + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HF; + msg.act = BTC_HF_VOLUME_CONTROL_EVT; + + btc_hf_args_t arg; + memset(&arg, 0, sizeof(btc_hf_args_t)); + arg.volcon.target_type = type; + arg.volcon.volume = volume; + memcpy(&(arg.volcon.remote_addr), remote_addr, sizeof(esp_bd_addr_t)); + + /* Switch to BTC context */ + bt_status_t status = btc_transfer_context(&msg, &arg, sizeof(btc_hf_args_t), NULL, NULL); + return (status == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_hf_ag_unknown_at_send(esp_bd_addr_t remote_addr, char *unat) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + btc_msg_t msg; + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HF; + msg.act = BTC_HF_UNAT_RESPONSE_EVT; + + btc_hf_args_t arg; + memset(&arg, 0, sizeof(btc_hf_args_t)); + arg.unat_rep.unat = unat; + memcpy(&(arg.unat_rep.remote_addr), remote_addr, sizeof(esp_bd_addr_t)); + + /* Switch to BTC context */ + bt_status_t status = btc_transfer_context(&msg, &arg, sizeof(btc_hf_args_t), + btc_hf_arg_deep_copy, btc_hf_arg_deep_free); + return (status == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_hf_ag_cmee_send(esp_bd_addr_t remote_addr, esp_hf_at_response_code_t response_code, esp_hf_cme_err_t error_code) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + btc_msg_t msg; + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HF; + msg.act = BTC_HF_CME_ERR_EVT; + + btc_hf_args_t arg; + memset(&arg, 0, sizeof(btc_hf_args_t)); + arg.ext_at.response_code = response_code; + arg.ext_at.error_code = error_code; + memcpy(&(arg.ext_at.remote_addr), remote_addr, sizeof(esp_bd_addr_t)); + + /* Switch to BTC context */ + bt_status_t status = btc_transfer_context(&msg, &arg, sizeof(btc_hf_args_t), NULL, NULL); + return (status == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_hf_ag_devices_status_indchange(esp_bd_addr_t remote_addr, + esp_hf_call_status_t call_state, + esp_hf_call_setup_status_t call_setup_state, + esp_hf_network_state_t ntk_state, int signal) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + if (signal < 0 || signal > 5) { + return ESP_ERR_INVALID_ARG; + } + btc_msg_t msg; + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HF; + msg.act = BTC_HF_IND_NOTIFICATION_EVT; + + btc_hf_args_t arg; + memset(&arg, 0, sizeof(btc_hf_args_t)); + memcpy(&(arg.ind_change.remote_addr), remote_addr, sizeof(esp_bd_addr_t)); + arg.ind_change.call_state = call_state; + arg.ind_change.call_setup_state = call_setup_state; + arg.ind_change.ntk_state = ntk_state; + arg.ind_change.signal = signal; + + /* Switch to BTC context */ + bt_status_t state = btc_transfer_context(&msg, &arg, sizeof(btc_hf_args_t), NULL, NULL); + return (state == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_hf_ag_ciev_report(esp_bd_addr_t remote_addr, esp_hf_ciev_report_type_t ind_type, int value) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + btc_msg_t msg; + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HF; + msg.act = BTC_HF_CIEV_REPORT_EVT; + + btc_hf_args_t arg; + memset(&arg, 0, sizeof(btc_hf_args_t)); + memcpy(&(arg.ciev_rep.remote_addr), remote_addr, sizeof(esp_bd_addr_t)); + arg.ciev_rep.ind.type = ind_type; + arg.ciev_rep.ind.value = value; + + /* Switch to BTC context */ + bt_status_t state = btc_transfer_context(&msg, &arg, sizeof(btc_hf_args_t), NULL, NULL); + return (state == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_hf_ag_cind_response(esp_bd_addr_t remote_addr, + esp_hf_call_status_t call_state, + esp_hf_call_setup_status_t call_setup_state, + esp_hf_network_state_t ntk_state, int signal, esp_hf_roaming_status_t roam, int batt_lev, + esp_hf_call_held_status_t call_held_status) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + if (signal < 0 || signal > 5 || batt_lev < 0 || batt_lev > 5) { + return ESP_ERR_INVALID_ARG; + } + + btc_msg_t msg; + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HF; + msg.act = BTC_HF_CIND_RESPONSE_EVT; + + btc_hf_args_t arg; + memset(&arg, 0, sizeof(btc_hf_args_t)); + memcpy(&(arg.cind_rep.remote_addr), remote_addr, sizeof(esp_bd_addr_t)); + arg.cind_rep.call_state = call_state; + arg.cind_rep.call_setup_state = call_setup_state; + arg.cind_rep.ntk_state = ntk_state; + arg.cind_rep.signal = signal; + arg.cind_rep.roam = roam; + arg.cind_rep.batt_lev = batt_lev; + arg.cind_rep.call_held_state = call_held_status; + + /* Switch to BTC context */ + bt_status_t stat = btc_transfer_context(&msg, &arg, sizeof(btc_hf_args_t), NULL, NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_hf_ag_cops_response(esp_bd_addr_t remote_addr, char *name) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + btc_msg_t msg; + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HF; + msg.act = BTC_HF_COPS_RESPONSE_EVT; + + btc_hf_args_t arg; + memset(&arg, 0, sizeof(btc_hf_args_t)); + memcpy(&(arg.cops_rep.remote_addr), remote_addr, sizeof(esp_bd_addr_t)); + arg.cops_rep.name = name; //deep_copy + + /* Switch to BTC context */ + bt_status_t stat = btc_transfer_context(&msg, &arg, sizeof(btc_hf_args_t), + btc_hf_arg_deep_copy, btc_hf_arg_deep_free); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_hf_ag_clcc_response(esp_bd_addr_t remote_addr, int index, esp_hf_current_call_direction_t dir, + esp_hf_current_call_status_t current_call_state, esp_hf_current_call_mode_t mode, + esp_hf_current_call_mpty_type_t mpty, char *number, esp_hf_call_addr_type_t type) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + btc_msg_t msg; + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HF; + msg.act = BTC_HF_CLCC_RESPONSE_EVT; + + btc_hf_args_t arg; + memset(&arg, 0, sizeof(btc_hf_args_t)); + memcpy(&(arg.clcc_rep.remote_addr), remote_addr, sizeof(esp_bd_addr_t)); + //mandatory args + arg.clcc_rep.index = index; + arg.clcc_rep.dir = dir; + arg.clcc_rep.current_call_state = current_call_state; + arg.clcc_rep.mode = mode; + arg.clcc_rep.mpty = mpty; + // option args + arg.clcc_rep.number = number; //deep_copy + arg.clcc_rep.type = type; + + /* Switch to BTC context */ + bt_status_t stat = btc_transfer_context(&msg, &arg, sizeof(btc_hf_args_t), + btc_hf_arg_deep_copy, btc_hf_arg_deep_free); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_hf_ag_cnum_response(esp_bd_addr_t remote_addr, char *number, int number_type, esp_hf_subscriber_service_type_t service_type) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + if (number == NULL || number_type < 128 || number_type > 175) { + return ESP_ERR_INVALID_ARG; + } + btc_msg_t msg; + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HF; + msg.act = BTC_HF_CNUM_RESPONSE_EVT; + + btc_hf_args_t arg; + memset(&arg, 0, sizeof(btc_hf_args_t)); + memcpy(&(arg.cnum_rep), remote_addr, sizeof(esp_bd_addr_t)); + arg.cnum_rep.number = number; //deep_copy + arg.cnum_rep.number_type = number_type; + arg.cnum_rep.service_type = service_type; + + /* Switch to BTC context */ + bt_status_t status = btc_transfer_context(&msg, &arg, sizeof(btc_hf_args_t), + btc_hf_arg_deep_copy, btc_hf_arg_deep_free); + return (status == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_hf_ag_bsir(esp_bd_addr_t remote_addr, esp_hf_in_band_ring_state_t state) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + btc_msg_t msg; + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HF; + msg.act = BTC_HF_INBAND_RING_EVT; + + btc_hf_args_t arg; + memset(&arg, 0, sizeof(btc_hf_args_t)); + memcpy(&(arg.bsir.remote_addr), remote_addr, sizeof(esp_bd_addr_t)); + arg.bsir.state = state; + + /* Switch to BTC context */ + bt_status_t status = btc_transfer_context(&msg, &arg, sizeof(btc_hf_args_t), NULL, NULL); + return (status == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_hf_ag_answer_call(esp_bd_addr_t remote_addr, int num_active, int num_held, + esp_hf_call_status_t call_state, esp_hf_call_setup_status_t call_setup_state, + char *number, esp_hf_call_addr_type_t call_addr_type) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + btc_msg_t msg; + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HF; + msg.act = BTC_HF_AC_INCALL_EVT; + + btc_hf_args_t arg; + memset(&arg, 0, sizeof(btc_hf_args_t)); + memcpy(&(arg.phone.remote_addr), remote_addr, sizeof(esp_bd_addr_t)); + arg.phone.num_active = num_active; + arg.phone.num_held = num_held; + arg.phone.call_state = call_state; + arg.phone.call_setup_state = call_setup_state; + arg.phone.number = number; //deep_copy + arg.phone.call_addr_type = call_addr_type; + + /* Switch to BTC context */ + bt_status_t status = btc_transfer_context(&msg, &arg, sizeof(btc_hf_args_t), + btc_hf_arg_deep_copy, btc_hf_arg_deep_free); + return (status == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_hf_ag_reject_call(esp_bd_addr_t remote_addr, int num_active, int num_held, + esp_hf_call_status_t call_state, esp_hf_call_setup_status_t call_setup_state, + char *number, esp_hf_call_addr_type_t call_addr_type) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + btc_msg_t msg; + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HF; + msg.act = BTC_HF_RJ_INCALL_EVT; + + btc_hf_args_t arg; + memset(&arg, 0, sizeof(btc_hf_args_t)); + memcpy(&(arg.phone.remote_addr), remote_addr, sizeof(esp_bd_addr_t)); + arg.phone.num_active = num_active; + arg.phone.num_held = num_held; + arg.phone.call_state = call_state; + arg.phone.call_setup_state = call_setup_state; + arg.phone.number = number; //deep_copy + arg.phone.call_addr_type = call_addr_type; + + /* Switch to BTC context */ + bt_status_t status = btc_transfer_context(&msg, &arg, sizeof(btc_hf_args_t), + btc_hf_arg_deep_copy, btc_hf_arg_deep_free); + return (status == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_hf_ag_end_call(esp_bd_addr_t remote_addr, int num_active, int num_held, + esp_hf_call_status_t call_state, esp_hf_call_setup_status_t call_setup_state, + char *number, esp_hf_call_addr_type_t call_addr_type) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + btc_msg_t msg; + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HF; + msg.act = BTC_HF_END_CALL_EVT; + + btc_hf_args_t arg; + memset(&arg, 0, sizeof(btc_hf_args_t)); + memcpy(&(arg.phone.remote_addr), remote_addr, sizeof(esp_bd_addr_t)); + arg.phone.num_active = num_active; + arg.phone.num_held = num_held; + arg.phone.call_state = call_state; + arg.phone.call_setup_state = call_setup_state; + arg.phone.number = number; //deep_copy + arg.phone.call_addr_type = call_addr_type; + + /* Switch to BTC context */ + bt_status_t status = btc_transfer_context(&msg, &arg, sizeof(btc_hf_args_t), + btc_hf_arg_deep_copy, btc_hf_arg_deep_free); + return (status == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_hf_ag_out_call(esp_bd_addr_t remote_addr, int num_active, int num_held, + esp_hf_call_status_t call_state, esp_hf_call_setup_status_t call_setup_state, + char *number, esp_hf_call_addr_type_t call_addr_type) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + btc_msg_t msg; + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HF; + msg.act = BTC_HF_OUT_CALL_EVT; + + btc_hf_args_t arg; + memset(&arg, 0, sizeof(btc_hf_args_t)); + memcpy(&(arg.phone.remote_addr), remote_addr, sizeof(esp_bd_addr_t)); + arg.phone.num_active = num_active; + arg.phone.num_held = num_held; + arg.phone.call_state = call_state; + arg.phone.call_setup_state = call_setup_state; + arg.phone.number = number; //deep_copy + arg.phone.call_addr_type = call_addr_type; + + /* Switch to BTC context */ + bt_status_t status = btc_transfer_context(&msg, &arg, sizeof(btc_hf_args_t), + btc_hf_arg_deep_copy, btc_hf_arg_deep_free); + return (status == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_hf_ag_register_data_callback(esp_hf_incoming_data_cb_t recv, esp_hf_outgoing_data_cb_t send) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + btc_msg_t msg; + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HF; + msg.act = BTC_HF_REGISTER_DATA_CALLBACK_EVT; + + btc_hf_args_t arg; + memset(&arg, 0, sizeof(btc_hf_args_t)); + arg.reg_data_cb.recv = recv; + arg.reg_data_cb.send = send; + + /* Switch to BTC context */ + bt_status_t status = btc_transfer_context(&msg, &arg, sizeof(btc_hf_args_t), NULL, NULL); + return (status == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +#if (BTM_SCO_HCI_INCLUDED == TRUE) +esp_err_t esp_hf_ag_pkt_stat_nums_get(uint16_t sync_conn_handle) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + + btc_msg_t msg; + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HF; + msg.act = BTC_HF_REQUEST_PKT_STAT_EVT; + + btc_hf_args_t arg; + memset(&arg, 0, sizeof(btc_hf_args_t)); + arg.pkt_sync_hd.sync_conn_handle = sync_conn_handle; + + /* Switch to BTC context */ + bt_status_t status = btc_transfer_context(&msg, &arg, sizeof(btc_hf_args_t), NULL, NULL); + return (status == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +void esp_hf_ag_outgoing_data_ready(void) +{ + btc_hf_ci_sco_data(); +} +#endif /* #if (BTM_SCO_HCI_INCLUDED == TRUE) */ + +#endif // BTC_HF_INCLUDED diff --git a/lib/bt/host/bluedroid/api/esp_hf_client_api.c b/lib/bt/host/bluedroid/api/esp_hf_client_api.c new file mode 100644 index 00000000..9909f442 --- /dev/null +++ b/lib/bt/host/bluedroid/api/esp_hf_client_api.c @@ -0,0 +1,561 @@ +/* + * SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "common/bt_target.h" +#include +#include "esp_err.h" +#include "esp_hf_client_api.h" +#include "esp_bt_main.h" +#include "btc/btc_manage.h" +#include "btc_hf_client.h" +#include "bta/bta_api.h" +#include "bta/bta_hf_client_api.h" + +#if BTC_HF_CLIENT_INCLUDED +esp_err_t esp_hf_client_register_callback(esp_hf_client_cb_t callback) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + + if (callback == NULL) { + return ESP_FAIL; + } + + btc_profile_cb_set(BTC_PID_HF_CLIENT, callback); + return ESP_OK; +} + +esp_err_t esp_hf_client_init(void) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + + btc_msg_t msg; + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HF_CLIENT; + msg.act = BTC_HF_CLIENT_INIT_EVT; + + /* Switch to BTC context */ + bt_status_t stat = btc_transfer_context(&msg, NULL, 0, NULL, NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_hf_client_deinit(void) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + + btc_msg_t msg; + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HF_CLIENT; + msg.act = BTC_HF_CLIENT_DEINIT_EVT; + + /* Switch to BTC context */ + bt_status_t stat = btc_transfer_context(&msg, NULL, 0, NULL, NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_hf_client_connect(esp_bd_addr_t remote_bda) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + + bt_status_t stat; + btc_hf_client_args_t arg; + btc_msg_t msg; + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HF_CLIENT; + msg.act = BTC_HF_CLIENT_CONNECT_EVT; + + memset(&arg, 0, sizeof(btc_hf_client_args_t)); + + /* Switch to BTC context */ + memcpy(&(arg.connect), remote_bda, sizeof(bt_bdaddr_t)); + stat = btc_transfer_context(&msg, &arg, sizeof(btc_hf_client_args_t), NULL, NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_hf_client_disconnect(esp_bd_addr_t remote_bda) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + + bt_status_t stat; + btc_hf_client_args_t arg; + btc_msg_t msg; + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HF_CLIENT; + msg.act = BTC_HF_CLIENT_DISCONNECT_EVT; + + memset(&arg, 0, sizeof(btc_hf_client_args_t)); + + /* Switch to BTC context */ + memcpy(&(arg.disconnect), remote_bda, sizeof(bt_bdaddr_t)); + stat = btc_transfer_context(&msg, &arg, sizeof(btc_hf_client_args_t), NULL, NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_hf_client_connect_audio(esp_bd_addr_t remote_bda) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + + bt_status_t stat; + btc_hf_client_args_t arg; + btc_msg_t msg; + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HF_CLIENT; + msg.act = BTC_HF_CLIENT_CONNECT_AUDIO_EVT; + + memset(&arg, 0, sizeof(btc_hf_client_args_t)); + + /* Switch to BTC context */ + memcpy(&(arg.connect_audio), remote_bda, sizeof(bt_bdaddr_t)); + stat = btc_transfer_context(&msg, &arg, sizeof(btc_hf_client_args_t), NULL, NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_hf_client_disconnect_audio(esp_bd_addr_t remote_bda) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + + bt_status_t stat; + btc_hf_client_args_t arg; + btc_msg_t msg; + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HF_CLIENT; + msg.act = BTC_HF_CLIENT_DISCONNECT_AUDIO_EVT; + + memset(&arg, 0, sizeof(btc_hf_client_args_t)); + + /* Switch to BTC context */ + memcpy(&(arg.disconnect_audio), remote_bda, sizeof(bt_bdaddr_t)); + stat = btc_transfer_context(&msg, &arg, sizeof(btc_hf_client_args_t), NULL, NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + + +esp_err_t esp_hf_client_start_voice_recognition(void) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + + btc_msg_t msg; + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HF_CLIENT; + msg.act = BTC_HF_CLIENT_START_VOICE_RECOGNITION_EVT; + + /* Switch to BTC context */ + bt_status_t stat = btc_transfer_context(&msg, NULL, 0, NULL, NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_hf_client_stop_voice_recognition(void) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + + btc_msg_t msg; + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HF_CLIENT; + msg.act = BTC_HF_CLIENT_STOP_VOICE_RECOGNITION_EVT; + + /* Switch to BTC context */ + bt_status_t stat = btc_transfer_context(&msg, NULL, 0, NULL, NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_hf_client_volume_update(esp_hf_volume_control_target_t type, int volume) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + + btc_msg_t msg; + btc_hf_client_args_t arg; + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HF_CLIENT; + msg.act = BTC_HF_CLIENT_VOLUME_UPDATE_EVT; + + memset(&arg, 0, sizeof(btc_hf_client_args_t)); + arg.volume_update.type = type; + arg.volume_update.volume = volume; + + /* Switch to BTC context */ + bt_status_t stat = btc_transfer_context(&msg, &arg, sizeof(btc_hf_client_args_t), NULL, NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_hf_client_dial(const char *number) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + + btc_msg_t msg; + btc_hf_client_args_t arg; + + if (number != NULL && strlen(number) > ESP_BT_HF_CLIENT_NUMBER_LEN) { + return ESP_ERR_INVALID_ARG; + } + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HF_CLIENT; + msg.act = BTC_HF_CLIENT_DIAL_EVT; + + memset(&arg, 0, sizeof(btc_hf_client_args_t)); + if (number != NULL) { + strcpy(arg.dial.number, number); + } else { + arg.dial.number[0] = '\0'; + } + + /* Switch to BTC context */ + bt_status_t stat = btc_transfer_context(&msg, &arg, sizeof(btc_hf_client_args_t), NULL, NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_hf_client_dial_memory(int location) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + + btc_msg_t msg; + btc_hf_client_args_t arg; + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HF_CLIENT; + msg.act = BTC_HF_CLIENT_DIAL_MEMORY_EVT; + + memset(&arg, 0, sizeof(btc_hf_client_args_t)); + arg.dial_memory.location = location; + + /* Switch to BTC context */ + bt_status_t stat = btc_transfer_context(&msg, &arg, sizeof(btc_hf_client_args_t), NULL, NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_hf_client_send_chld_cmd(esp_hf_chld_type_t chld, int idx) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + + btc_msg_t msg; + btc_hf_client_args_t arg; + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HF_CLIENT; + msg.act = BTC_HF_CLIENT_SEND_CHLD_CMD_EVT; + + memset(&arg, 0, sizeof(btc_hf_client_args_t)); + arg.chld.type = chld; + arg.chld.idx = idx; + + /* Switch to BTC context */ + bt_status_t stat = btc_transfer_context(&msg, &arg, sizeof(btc_hf_client_args_t), NULL, NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_hf_client_send_btrh_cmd(esp_hf_btrh_cmd_t btrh) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + + btc_msg_t msg; + btc_hf_client_args_t arg; + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HF_CLIENT; + msg.act = BTC_HF_CLIENT_SEND_BTRH_CMD_EVT; + + memset(&arg, 0, sizeof(btc_hf_client_args_t)); + arg.btrh.cmd = btrh; + + /* Switch to BTC context */ + bt_status_t stat = btc_transfer_context(&msg, &arg, sizeof(btc_hf_client_args_t), NULL, NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_hf_client_answer_call(void) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + + btc_msg_t msg; + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HF_CLIENT; + msg.act = BTC_HF_CLIENT_ANSWER_CALL_EVT; + + /* Switch to BTC context */ + bt_status_t stat = btc_transfer_context(&msg, NULL, 0, NULL, NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_hf_client_reject_call(void) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + + btc_msg_t msg; + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HF_CLIENT; + msg.act = BTC_HF_CLIENT_REJECT_CALL_EVT; + + /* Switch to BTC context */ + bt_status_t stat = btc_transfer_context(&msg, NULL, 0, NULL, NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_hf_client_query_current_calls(void) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + + btc_msg_t msg; + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HF_CLIENT; + msg.act = BTC_HF_CLIENT_QUERY_CURRENT_CALLS_EVT; + + /* Switch to BTC context */ + bt_status_t stat = btc_transfer_context(&msg, NULL, 0, NULL, NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_hf_client_query_current_operator_name(void) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + + btc_msg_t msg; + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HF_CLIENT; + msg.act = BTC_HF_CLIENT_QUERY_CURRENT_OPERATOR_NAME_EVT; + + /* Switch to BTC context */ + bt_status_t stat = btc_transfer_context(&msg, NULL, 0, NULL, NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_hf_client_retrieve_subscriber_info(void) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + + btc_msg_t msg; + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HF_CLIENT; + msg.act = BTC_HF_CLIENT_RETRIEVE_SUBSCRIBER_INFO_EVT; + + /* Switch to BTC context */ + bt_status_t stat = btc_transfer_context(&msg, NULL, 0, NULL, NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_hf_client_send_dtmf(char code) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + + btc_msg_t msg; + btc_hf_client_args_t arg; + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HF_CLIENT; + msg.act = BTC_HF_CLIENT_SEND_DTMF_EVT; + + memset(&arg, 0, sizeof(btc_hf_client_args_t)); + arg.send_dtmf.code = code; + + /* Switch to BTC context */ + bt_status_t stat = btc_transfer_context(&msg, &arg, sizeof(btc_hf_client_args_t), NULL, NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_hf_client_send_xapl(char *information, uint32_t features) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + + if (information == NULL || strlen(information) != ESP_BT_HF_AT_SEND_XAPL_LEN) { + return ESP_ERR_INVALID_ARG; + } + + btc_msg_t msg; + btc_hf_client_args_t arg; + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HF_CLIENT; + msg.act = BTC_HF_CLIENT_SEND_XAPL_EVT; + + memset(&arg, 0, sizeof(btc_hf_client_args_t)); + strcpy(arg.send_xapl.information, information); + arg.send_xapl.features = features; + + /* Switch to BTC context */ + bt_status_t stat = btc_transfer_context(&msg, &arg, sizeof(btc_hf_client_args_t), NULL, NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_hf_client_send_iphoneaccev(uint32_t bat_level, bool docked) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + + if (bat_level > 9) { + return ESP_ERR_INVALID_ARG; + } + + btc_msg_t msg; + btc_hf_client_args_t arg; + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HF_CLIENT; + msg.act = BTC_HF_CLIENT_SEND_IPHONEACCEV_EVT; + + memset(&arg, 0, sizeof(btc_hf_client_args_t)); + arg.send_iphoneaccev.bat_level = bat_level; + arg.send_iphoneaccev.docked = docked; + + /* Switch to BTC context */ + bt_status_t stat = btc_transfer_context(&msg, &arg, sizeof(btc_hf_client_args_t), NULL, NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + + +esp_err_t esp_hf_client_request_last_voice_tag_number(void) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + + btc_msg_t msg; + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HF_CLIENT; + msg.act = BTC_HF_CLIENT_REQUEST_LAST_VOICE_TAG_NUMBER_EVT; + + /* Switch to BTC context */ + bt_status_t stat = btc_transfer_context(&msg, NULL, 0, NULL, NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_hf_client_send_nrec(void) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + + btc_msg_t msg; + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HF_CLIENT; + msg.act = BTC_HF_CLIENT_SEND_NREC_EVT; + + /* Switch to BTC context */ + bt_status_t stat = btc_transfer_context(&msg, NULL, 0, NULL, NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_hf_client_register_data_callback(esp_hf_client_incoming_data_cb_t recv, + esp_hf_client_outgoing_data_cb_t send) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + + btc_msg_t msg; + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HF_CLIENT; + msg.act = BTC_HF_CLIENT_REGISTER_DATA_CALLBACK_EVT; + + btc_hf_client_args_t arg; + memset(&arg, 0, sizeof(btc_hf_client_args_t)); + arg.reg_data_cb.recv = recv; + arg.reg_data_cb.send = send; + + /* Switch to BTC context */ + bt_status_t stat = btc_transfer_context(&msg, &arg, sizeof(btc_hf_client_args_t), NULL, NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +#if (BTM_SCO_HCI_INCLUDED == TRUE) +esp_err_t esp_hf_client_pkt_stat_nums_get(uint16_t sync_conn_handle) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + + btc_msg_t msg; + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HF_CLIENT; + msg.act = BTC_HF_CLIENT_REQUEST_PKT_STAT_EVT; + + btc_hf_client_args_t arg; + memset(&arg, 0, sizeof(btc_hf_client_args_t)); + arg.pkt_sync_hd.sync_conn_handle = sync_conn_handle; + + /* Switch to BTC context */ + bt_status_t stat = btc_transfer_context(&msg, &arg, sizeof(btc_hf_client_args_t), NULL, NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +void esp_hf_client_outgoing_data_ready(void) +{ + BTA_HfClientCiData(); +} + +void esp_hf_client_pcm_resample_init(uint32_t src_sps, uint32_t bits, uint32_t channels) +{ + BTA_DmPcmInitSamples(src_sps, bits, channels); +} + +void esp_hf_client_pcm_resample_deinit(void) +{ + BTA_DmPcmDeinitSamples(); +} + +int32_t esp_hf_client_pcm_resample(void *src, uint32_t in_bytes, void *dst) +{ + return BTA_DmPcmResample(src, in_bytes, dst); +} + +#endif /* #if (BTM_SCO_HCI_INCLUDED == TRUE) */ + +#endif /* BTC_HF_CLIENT_INCLUDED */ diff --git a/lib/bt/host/bluedroid/api/esp_hidd_api.c b/lib/bt/host/bluedroid/api/esp_hidd_api.c new file mode 100644 index 00000000..2906e7a8 --- /dev/null +++ b/lib/bt/host/bluedroid/api/esp_hidd_api.c @@ -0,0 +1,170 @@ +/* + * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + * + * SPDX-FileContributor: Blake Felt + */ + +#include +#include "esp_err.h" +#include "esp_bt_main.h" +#include "btc/btc_manage.h" +#include "btc_hd.h" +#include "esp_hidd_api.h" + +#if (defined BTC_HD_INCLUDED && BTC_HD_INCLUDED == TRUE) + +esp_err_t esp_bt_hid_device_register_callback(esp_hd_cb_t callback) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + if (callback == NULL) { + return ESP_FAIL; + } + + btc_profile_cb_set(BTC_PID_HD, callback); + return ESP_OK; +} + +esp_err_t esp_bt_hid_device_init(void) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + btc_msg_t msg; + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HD; + msg.act = BTC_HD_INIT_EVT; + + /* Switch to BTC context */ + bt_status_t stat = btc_transfer_context(&msg, NULL, 0, NULL, NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_bt_hid_device_deinit(void) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + btc_msg_t msg; + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HD; + msg.act = BTC_HD_DEINIT_EVT; + + bt_status_t stat = btc_transfer_context(&msg, NULL, 0, NULL, NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_bt_hid_device_register_app(esp_hidd_app_param_t* app_param, esp_hidd_qos_param_t* in_qos, esp_hidd_qos_param_t* out_qos) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + btc_msg_t msg; + btc_hidd_args_t args; + memset(&args, 0, sizeof(btc_hidd_args_t)); + args.register_app.app_param = app_param; + args.register_app.in_qos = in_qos; + args.register_app.out_qos = out_qos; + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HD; + msg.act = BTC_HD_REGISTER_APP_EVT; + + bt_status_t stat = btc_transfer_context(&msg, &args, sizeof(btc_hidd_args_t), NULL, NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_bt_hid_device_unregister_app(void) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + btc_msg_t msg; + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HD; + msg.act = BTC_HD_UNREGISTER_APP_EVT; + + bt_status_t stat = btc_transfer_context(&msg, NULL, 0, NULL, NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_bt_hid_device_connect(esp_bd_addr_t bd_addr) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + btc_msg_t msg; + btc_hidd_args_t args; + memset(&args, 0, sizeof(btc_hidd_args_t)); + memcpy(args.connect.bd_addr, bd_addr, sizeof(esp_bd_addr_t)); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HD; + msg.act = BTC_HD_CONNECT_EVT; + + bt_status_t stat = btc_transfer_context(&msg, &args, sizeof(btc_hidd_args_t), NULL, NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_bt_hid_device_disconnect(void) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + btc_msg_t msg; + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HD; + msg.act = BTC_HD_DISCONNECT_EVT; + + bt_status_t stat = btc_transfer_context(&msg, NULL, 0, NULL, NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_bt_hid_device_send_report(esp_hidd_report_type_t type, uint8_t id, uint16_t len, uint8_t* data) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + btc_msg_t msg; + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HD; + msg.act = BTC_HD_SEND_REPORT_EVT; + + btc_hidd_args_t args; + memset(&args, 0, sizeof(btc_hidd_args_t)); + args.send_report.type = type; + args.send_report.id = id; + args.send_report.len = len; + args.send_report.data = data; + + bt_status_t stat = btc_transfer_context(&msg, &args, sizeof(btc_hidd_args_t), + btc_hd_arg_deep_copy, btc_hd_cb_arg_deep_free); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_bt_hid_device_report_error(esp_hidd_handshake_error_t error) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + btc_msg_t msg; + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HD; + msg.act = BTC_HD_REPORT_ERROR_EVT; + + btc_hidd_args_t args; + memset(&args, 0, sizeof(btc_hidd_args_t)); + args.error = error; + + bt_status_t stat = btc_transfer_context(&msg, &args, sizeof(btc_hidd_args_t), NULL, NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_bt_hid_device_virtual_cable_unplug(void) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + btc_msg_t msg; + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HD; + msg.act = BTC_HD_UNPLUG_EVT; + + bt_status_t stat = btc_transfer_context(&msg, NULL, 0, NULL, NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +#endif /* defined BTC_HD_INCLUDED && BTC_HD_INCLUDED == TRUE */ diff --git a/lib/bt/host/bluedroid/api/esp_hidh_api.c b/lib/bt/host/bluedroid/api/esp_hidh_api.c new file mode 100644 index 00000000..f9959c39 --- /dev/null +++ b/lib/bt/host/bluedroid/api/esp_hidh_api.c @@ -0,0 +1,250 @@ +/* + * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + * + * SPDX-FileContributor: Blake Felt + */ + +#include "btc/btc_manage.h" +#include "btc_hh.h" +#include "esp_bt_main.h" +#include "esp_err.h" +#include "esp_hidh_api.h" +#include + +#if (defined BTC_HH_INCLUDED && BTC_HH_INCLUDED == TRUE) + +esp_err_t esp_bt_hid_host_register_callback(esp_hh_cb_t callback) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + if (callback == NULL) { + return ESP_FAIL; + } + + btc_profile_cb_set(BTC_PID_HH, callback); + return ESP_OK; +} + +esp_err_t esp_bt_hid_host_init(void) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + btc_msg_t msg; + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HH; + msg.act = BTC_HH_INIT_EVT; + + bt_status_t stat = btc_transfer_context(&msg, NULL, 0, NULL, NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_bt_hid_host_deinit(void) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + btc_msg_t msg; + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HH; + msg.act = BTC_HH_DEINIT_EVT; + + bt_status_t stat = btc_transfer_context(&msg, NULL, 0, NULL, NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_bt_hid_host_connect(esp_bd_addr_t bd_addr) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + btc_msg_t msg; + btc_hidh_args_t arg; + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HH; + msg.act = BTC_HH_CONNECT_EVT; + + memcpy(arg.connect.bd_addr, bd_addr, sizeof(esp_bd_addr_t)); + + bt_status_t stat = btc_transfer_context(&msg, &arg, sizeof(btc_hidh_args_t), NULL, NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_bt_hid_host_disconnect(esp_bd_addr_t bd_addr) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + btc_msg_t msg; + btc_hidh_args_t arg; + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HH; + msg.act = BTC_HH_DISCONNECT_EVT; + + memcpy(arg.disconnect.bd_addr, bd_addr, sizeof(esp_bd_addr_t)); + + bt_status_t stat = btc_transfer_context(&msg, &arg, sizeof(btc_hidh_args_t), NULL, NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_bt_hid_host_virtual_cable_unplug(esp_bd_addr_t bd_addr) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + btc_msg_t msg; + btc_hidh_args_t arg; + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HH; + msg.act = BTC_HH_UNPLUG_EVT; + + memcpy(arg.unplug.bd_addr, bd_addr, sizeof(esp_bd_addr_t)); + + bt_status_t stat = btc_transfer_context(&msg, &arg, sizeof(btc_hidh_args_t), NULL, NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_bt_hid_host_set_info(esp_bd_addr_t bd_addr, esp_hidh_hid_info_t *hid_info) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + btc_msg_t msg; + btc_hidh_args_t arg; + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HH; + msg.act = BTC_HH_SET_INFO_EVT; + + memcpy(arg.set_info.bd_addr, bd_addr, sizeof(esp_bd_addr_t)); + arg.set_info.hid_info = hid_info; + + bt_status_t stat = btc_transfer_context(&msg, &arg, sizeof(btc_hidh_args_t), + btc_hh_arg_deep_copy, btc_hh_cb_arg_deep_free); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_bt_hid_host_get_protocol(esp_bd_addr_t bd_addr) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + btc_msg_t msg; + btc_hidh_args_t arg; + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HH; + msg.act = BTC_HH_GET_PROTO_EVT; + + memcpy(arg.get_protocol.bd_addr, bd_addr, sizeof(esp_bd_addr_t)); + + bt_status_t stat = btc_transfer_context(&msg, &arg, sizeof(btc_hidh_args_t), NULL, NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_bt_hid_host_set_protocol(esp_bd_addr_t bd_addr, esp_hidh_protocol_mode_t protocol_mode) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + btc_msg_t msg; + btc_hidh_args_t arg; + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HH; + msg.act = BTC_HH_SET_PROTO_EVT; + + memcpy(arg.set_protocol.bd_addr, bd_addr, sizeof(esp_bd_addr_t)); + arg.set_protocol.protocol_mode = protocol_mode; + + bt_status_t stat = btc_transfer_context(&msg, &arg, sizeof(btc_hidh_args_t), NULL, NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_bt_hid_host_get_idle(esp_bd_addr_t bd_addr) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + btc_msg_t msg; + btc_hidh_args_t arg; + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HH; + msg.act = BTC_HH_GET_IDLE_EVT; + + memcpy(arg.get_idle.bd_addr, bd_addr, sizeof(esp_bd_addr_t)); + + bt_status_t stat = btc_transfer_context(&msg, &arg, sizeof(btc_hidh_args_t), NULL, NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_bt_hid_host_set_idle(esp_bd_addr_t bd_addr, uint16_t idle_time) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + btc_msg_t msg; + btc_hidh_args_t arg; + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HH; + msg.act = BTC_HH_SET_IDLE_EVT; + + memcpy(arg.set_idle.bd_addr, bd_addr, sizeof(esp_bd_addr_t)); + arg.set_idle.idle_time = idle_time; + + bt_status_t stat = btc_transfer_context(&msg, &arg, sizeof(btc_hidh_args_t), NULL, NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_bt_hid_host_get_report(esp_bd_addr_t bd_addr, esp_hidh_report_type_t report_type, uint8_t report_id, + int buffer_size) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + btc_msg_t msg; + btc_hidh_args_t arg; + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HH; + msg.act = BTC_HH_GET_REPORT_EVT; + + memcpy(arg.get_report.bd_addr, bd_addr, sizeof(esp_bd_addr_t)); + arg.get_report.report_type = report_type; + arg.get_report.report_id = report_id; + arg.get_report.buffer_size = buffer_size; + + bt_status_t stat = btc_transfer_context(&msg, &arg, sizeof(btc_hidh_args_t), NULL, NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_bt_hid_host_set_report(esp_bd_addr_t bd_addr, esp_hidh_report_type_t report_type, uint8_t *report, + size_t len) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + btc_msg_t msg; + btc_hidh_args_t arg; + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HH; + msg.act = BTC_HH_SET_REPORT_EVT; + + memcpy(arg.set_report.bd_addr, bd_addr, sizeof(esp_bd_addr_t)); + arg.set_report.report_type = report_type; + arg.set_report.len = len; + arg.set_report.report = report; + + bt_status_t stat = btc_transfer_context(&msg, &arg, sizeof(btc_hidh_args_t), + btc_hh_arg_deep_copy, btc_hh_cb_arg_deep_free); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_bt_hid_host_send_data(esp_bd_addr_t bd_addr, uint8_t *data, size_t len) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + btc_msg_t msg; + btc_hidh_args_t arg; + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HH; + msg.act = BTC_HH_SEND_DATA_EVT; + + memcpy(arg.send_data.bd_addr, bd_addr, sizeof(esp_bd_addr_t)); + arg.send_data.len = len; + arg.send_data.data = data; + + bt_status_t stat = btc_transfer_context(&msg, &arg, sizeof(btc_hidh_args_t), + btc_hh_arg_deep_copy, btc_hh_cb_arg_deep_free); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +#endif /* defined BTC_HH_INCLUDED && BTC_HH_INCLUDED == TRUE */ diff --git a/lib/bt/host/bluedroid/api/esp_l2cap_bt_api.c b/lib/bt/host/bluedroid/api/esp_l2cap_bt_api.c new file mode 100644 index 00000000..c78d6f8d --- /dev/null +++ b/lib/bt/host/bluedroid/api/esp_l2cap_bt_api.c @@ -0,0 +1,131 @@ +/* + * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include "esp_bt_main.h" +#include "btc/btc_manage.h" + +#include "btc_l2cap.h" +#include "esp_l2cap_bt_api.h" +#include "common/bt_target.h" + +#if (defined BTC_L2CAP_INCLUDED && BTC_L2CAP_INCLUDED == TRUE) + +esp_err_t esp_bt_l2cap_register_callback(esp_bt_l2cap_cb_t callback) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + if (callback == NULL) { + return ESP_FAIL; + } + + btc_profile_cb_set(BTC_PID_L2CAP, callback); + return ESP_OK; +} + +esp_err_t esp_bt_l2cap_init(void) +{ + btc_msg_t msg; + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_L2CAP; + msg.act = BTC_L2CAP_ACT_INIT; + + return (btc_transfer_context(&msg, NULL, 0, NULL, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_bt_l2cap_deinit(void) +{ + btc_msg_t msg; + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_L2CAP; + msg.act = BTC_L2CAP_ACT_UNINIT; + + return (btc_transfer_context(&msg, NULL, 0, NULL, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_bt_l2cap_connect(esp_bt_l2cap_cntl_flags_t cntl_flag, uint16_t remote_psm, esp_bd_addr_t peer_bd_addr) +{ + btc_msg_t msg; + btc_l2cap_args_t arg; + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_L2CAP; + msg.act = BTC_L2CAP_ACT_CONNECT; + + arg.connect.sec_mask = (cntl_flag & 0xffff); + arg.connect.remote_psm = remote_psm; + memcpy(arg.connect.peer_bd_addr, peer_bd_addr, ESP_BD_ADDR_LEN); + + return (btc_transfer_context(&msg, &arg, sizeof(btc_l2cap_args_t), NULL, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_bt_l2cap_start_srv(esp_bt_l2cap_cntl_flags_t cntl_flag, uint16_t local_psm) +{ + btc_msg_t msg; + btc_l2cap_args_t arg; + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_L2CAP; + msg.act = BTC_L2CAP_ACT_START_SRV; + + arg.start_srv.sec_mask = (cntl_flag & 0xffff); + arg.start_srv.local_psm = local_psm; + + return (btc_transfer_context(&msg, &arg, sizeof(btc_l2cap_args_t), NULL, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_bt_l2cap_stop_all_srv(void) +{ + btc_msg_t msg; + btc_l2cap_args_t arg; + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_L2CAP; + msg.act = BTC_L2CAP_ACT_STOP_SRV; + + arg.stop_srv.psm = BTC_L2CAP_INVALID_PSM; + + return (btc_transfer_context(&msg, &arg, sizeof(btc_l2cap_args_t), NULL, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_bt_l2cap_stop_srv(uint16_t local_psm) +{ + btc_msg_t msg; + btc_l2cap_args_t arg; + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_L2CAP; + msg.act = BTC_L2CAP_ACT_STOP_SRV; + + arg.stop_srv.psm = local_psm; + + return (btc_transfer_context(&msg, &arg, sizeof(btc_l2cap_args_t), NULL, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_bt_l2cap_vfs_register(void) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + return btc_l2cap_vfs_register(); +} + +esp_err_t esp_bt_l2cap_vfs_unregister(void) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + return btc_l2cap_vfs_unregister(); +} + +#endif ///defined BTC_L2CAP_INCLUDED && BTC_L2CAP_INCLUDED == TRUE diff --git a/lib/bt/host/bluedroid/api/esp_sdp_api.c b/lib/bt/host/bluedroid/api/esp_sdp_api.c new file mode 100644 index 00000000..c47c5ba5 --- /dev/null +++ b/lib/bt/host/bluedroid/api/esp_sdp_api.c @@ -0,0 +1,130 @@ +/* + * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include "esp_bt_main.h" +#include "btc/btc_manage.h" + +#include "btc_sdp.h" +#include "esp_sdp_api.h" +#include "common/bt_target.h" + +#if (defined BTC_SDP_INCLUDED && BTC_SDP_INCLUDED == TRUE) + +esp_err_t esp_sdp_register_callback(esp_sdp_cb_t callback) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + if (callback == NULL) { + return ESP_FAIL; + } + + btc_profile_cb_set(BTC_PID_SDP, callback); + return ESP_OK; +} + +esp_err_t esp_sdp_init(void) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + btc_msg_t msg; + bt_status_t stat; + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_SDP; + msg.act = BTC_SDP_ACT_INIT; + + /* Switch to BTC context */ + stat = btc_transfer_context(&msg, NULL, 0, NULL, NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_sdp_deinit(void) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + btc_msg_t msg; + bt_status_t stat; + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_SDP; + msg.act = BTC_SDP_ACT_DEINIT; + + /* Switch to BTC context */ + stat = btc_transfer_context(&msg, NULL, 0, NULL, NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_sdp_search_record(esp_bd_addr_t bd_addr, esp_bt_uuid_t uuid) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + btc_msg_t msg; + bt_status_t stat; + btc_sdp_args_t arg; + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_SDP; + msg.act = BTC_SDP_ACT_SEARCH; + + memset(&arg, 0, sizeof(btc_sdp_args_t)); + memcpy(arg.search.bd_addr, bd_addr, ESP_BD_ADDR_LEN); + arg.search.sdp_uuid.len = uuid.len; + memcpy(&arg.search.sdp_uuid.uu, &uuid.uuid, sizeof(uuid.uuid)); + + /* Switch to BTC context */ + stat = btc_transfer_context(&msg, &arg, sizeof(btc_sdp_args_t), NULL, NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_sdp_create_record(esp_bluetooth_sdp_record_t *record) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + if (record == NULL || record->hdr.service_name_length > ESP_SDP_SERVER_NAME_MAX + || strlen(record->hdr.service_name)+1 != record->hdr.service_name_length) { + LOG_ERROR("Invalid server name!\n"); + return ESP_ERR_INVALID_ARG; + } + + btc_msg_t msg; + bt_status_t stat; + btc_sdp_args_t arg; + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_SDP; + msg.act = BTC_SDP_ACT_CREATE_RECORD; + + memset(&arg, 0, sizeof(btc_sdp_args_t)); + arg.creat_record.record = (bluetooth_sdp_record *)record; + + /* Switch to BTC context */ + stat = btc_transfer_context(&msg, &arg, sizeof(btc_sdp_args_t), + btc_sdp_arg_deep_copy, btc_sdp_arg_deep_free); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_sdp_remove_record(int record_handle) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + btc_msg_t msg; + bt_status_t stat; + btc_sdp_args_t arg; + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_SDP; + msg.act = BTC_SDP_ACT_REMOVE_RECORD; + + arg.remove_record.record_handle = record_handle; + + /* Switch to BTC context */ + stat = btc_transfer_context(&msg, &arg, sizeof(btc_sdp_args_t), NULL, NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +#endif ///defined BTC_SDP_INCLUDED && BTC_SDP_INCLUDED == TRUE diff --git a/lib/bt/host/bluedroid/api/esp_spp_api.c b/lib/bt/host/bluedroid/api/esp_spp_api.c new file mode 100644 index 00000000..fce84be0 --- /dev/null +++ b/lib/bt/host/bluedroid/api/esp_spp_api.c @@ -0,0 +1,255 @@ +/* + * SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include "esp_bt_main.h" +#include "btc/btc_manage.h" + +#include "btc_spp.h" +#include "esp_spp_api.h" +#include "common/bt_target.h" + +#if (defined BTC_SPP_INCLUDED && BTC_SPP_INCLUDED == TRUE) + +static const uint8_t UUID_SPP[16] = {0x00, 0x00, 0x11, 0x01, 0x00, 0x00, 0x10, 0x00, + 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB + }; +static tSDP_UUID sdp_uuid; + +esp_err_t esp_spp_register_callback(esp_spp_cb_t callback) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + if (callback == NULL) { + return ESP_FAIL; + } + + btc_profile_cb_set(BTC_PID_SPP, callback); + return ESP_OK; +} + + +esp_err_t esp_spp_init(esp_spp_mode_t mode) +{ + esp_spp_cfg_t bt_spp_cfg = { + .mode = mode, + .enable_l2cap_ertm = true, + .tx_buffer_size = ESP_SPP_MAX_TX_BUFFER_SIZE, + }; + + return esp_spp_enhanced_init(&bt_spp_cfg); +} + +esp_err_t esp_spp_enhanced_init(const esp_spp_cfg_t *cfg) +{ + btc_msg_t msg; + btc_spp_args_t arg; + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + if (cfg->mode == ESP_SPP_MODE_VFS && (cfg->tx_buffer_size < ESP_SPP_MIN_TX_BUFFER_SIZE || + cfg->tx_buffer_size > ESP_SPP_MAX_TX_BUFFER_SIZE)) { + LOG_WARN("Invalid tx buffer size"); + return ESP_ERR_INVALID_ARG; + } + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_SPP; + msg.act = BTC_SPP_ACT_INIT; + + arg.init.mode = cfg->mode; + arg.init.enable_l2cap_ertm = cfg->enable_l2cap_ertm; + arg.init.tx_buffer_size = cfg->tx_buffer_size; + + return (btc_transfer_context(&msg, &arg, sizeof(btc_spp_args_t), NULL, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_spp_deinit(void) +{ + btc_msg_t msg; + btc_spp_args_t arg; + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_SPP; + msg.act = BTC_SPP_ACT_UNINIT; + + return (btc_transfer_context(&msg, &arg, sizeof(btc_spp_args_t), NULL, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + + +esp_err_t esp_spp_start_discovery(esp_bd_addr_t bd_addr) +{ + sdp_uuid.len = 16; + memcpy(sdp_uuid.uu.uuid128, UUID_SPP, sizeof(sdp_uuid.uu.uuid128)); + + btc_msg_t msg; + btc_spp_args_t arg; + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_SPP; + msg.act = BTC_SPP_ACT_START_DISCOVERY; + + memcpy(arg.start_discovery.bd_addr, bd_addr, ESP_BD_ADDR_LEN); + arg.start_discovery.num_uuid = 1; + arg.start_discovery.p_uuid_list = &sdp_uuid; + + return (btc_transfer_context(&msg, &arg, sizeof(btc_spp_args_t), + btc_spp_arg_deep_copy, btc_spp_arg_deep_free) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_spp_connect(esp_spp_sec_t sec_mask, + esp_spp_role_t role, uint8_t remote_scn, esp_bd_addr_t peer_bd_addr) +{ + btc_msg_t msg; + btc_spp_args_t arg; + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + if (sec_mask != ESP_SPP_SEC_NONE && + sec_mask != ESP_SPP_SEC_AUTHENTICATE && + sec_mask != (ESP_SPP_SEC_AUTHENTICATE | ESP_SPP_SEC_ENCRYPT)) { + LOG_WARN("Suggest to use ESP_SPP_SEC_NONE, ESP_SPP_SEC_AUTHENTICATE" + "or (ESP_SPP_SEC_AUTHENTICATE | ESP_SPP_SEC_ENCRYPT) only\n"); + } + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_SPP; + msg.act = BTC_SPP_ACT_CONNECT; + + arg.connect.sec_mask = sec_mask; + arg.connect.role = role; + arg.connect.remote_scn = remote_scn; + memcpy(arg.connect.peer_bd_addr, peer_bd_addr, ESP_BD_ADDR_LEN); + + return (btc_transfer_context(&msg, &arg, sizeof(btc_spp_args_t), NULL, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_spp_disconnect(uint32_t handle) +{ + btc_msg_t msg; + btc_spp_args_t arg; + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_SPP; + msg.act = BTC_SPP_ACT_DISCONNECT; + + arg.disconnect.handle = handle; + + return (btc_transfer_context(&msg, &arg, sizeof(btc_spp_args_t), NULL, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_spp_start_srv(esp_spp_sec_t sec_mask, + esp_spp_role_t role, uint8_t local_scn, const char *name) +{ + btc_msg_t msg; + btc_spp_args_t arg; + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + if (name == NULL || strlen(name) > ESP_SPP_SERVER_NAME_MAX) { + LOG_ERROR("Invalid server name!\n"); + return ESP_ERR_INVALID_ARG; + } + + if (sec_mask != ESP_SPP_SEC_NONE && + sec_mask != ESP_SPP_SEC_AUTHENTICATE && + sec_mask != (ESP_SPP_SEC_AUTHENTICATE | ESP_SPP_SEC_ENCRYPT) && + sec_mask != ESP_SPP_SEC_IN_16_DIGITS && + sec_mask != (ESP_SPP_SEC_IN_16_DIGITS | ESP_SPP_SEC_AUTHENTICATE) && + sec_mask != (ESP_SPP_SEC_IN_16_DIGITS | ESP_SPP_SEC_AUTHENTICATE | ESP_SPP_SEC_ENCRYPT)) { + LOG_WARN("Suggest to use ESP_SPP_SEC_NONE, ESP_SPP_SEC_AUTHENTICATE," + "(ESP_SPP_SEC_AUTHENTICATE | ESP_SPP_SEC_ENCRYPT)," + "ESP_SPP_SEC_IN_16_DIGITS, (ESP_SPP_SEC_IN_16_DIGITS | ESP_SPP_SEC_AUTHENTICATE), or" + "(ESP_SPP_SEC_IN_16_DIGITS | ESP_SPP_SEC_AUTHENTICATE | ESP_SPP_SEC_ENCRYPT) only\n"); + } + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_SPP; + msg.act = BTC_SPP_ACT_START_SRV; + + arg.start_srv.sec_mask = sec_mask; + arg.start_srv.role = role; + arg.start_srv.local_scn = local_scn; + arg.start_srv.max_session = ESP_SPP_MAX_SESSION; + strcpy(arg.start_srv.name, name); + + return (btc_transfer_context(&msg, &arg, sizeof(btc_spp_args_t), NULL, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_spp_stop_srv(void) +{ + btc_msg_t msg; + btc_spp_args_t arg; + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_SPP; + msg.act = BTC_SPP_ACT_STOP_SRV; + arg.stop_srv.scn = BTC_SPP_INVALID_SCN; + + return (btc_transfer_context(&msg, &arg, sizeof(btc_spp_args_t), NULL, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_spp_stop_srv_scn(uint8_t scn) +{ + btc_msg_t msg; + btc_spp_args_t arg; + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + if ((scn == 0) || (scn >= PORT_MAX_RFC_PORTS)) { + LOG_ERROR("Invalid SCN!\n"); + return ESP_ERR_INVALID_ARG; + } + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_SPP; + msg.act = BTC_SPP_ACT_STOP_SRV; + arg.stop_srv.scn = scn; + + return (btc_transfer_context(&msg, &arg, sizeof(btc_spp_args_t), NULL, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + + +esp_err_t esp_spp_write(uint32_t handle, int len, uint8_t *p_data) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + if (len <= 0 || p_data == NULL) { + LOG_ERROR("Invalid data or len!\n"); + return ESP_ERR_INVALID_ARG; + } + + return spp_send_data_to_btc(handle, len, p_data, ESP_SPP_MODE_CB); +} + +esp_err_t esp_spp_vfs_register(void) +{ + btc_msg_t msg; + btc_spp_args_t arg; + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_SPP; + msg.act = BTC_SPP_ACT_VFS_REGISTER; + + return (btc_transfer_context(&msg, &arg, sizeof(btc_spp_args_t), NULL, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_spp_vfs_unregister(void) +{ + btc_msg_t msg; + btc_spp_args_t arg; + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_SPP; + msg.act = BTC_SPP_ACT_VFS_UNREGISTER; + + return (btc_transfer_context(&msg, &arg, sizeof(btc_spp_args_t), NULL, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +#endif ///defined BTC_SPP_INCLUDED && BTC_SPP_INCLUDED == TRUE diff --git a/lib/bt/host/bluedroid/api/include/api/esp_a2dp_api.h b/lib/bt/host/bluedroid/api/include/api/esp_a2dp_api.h new file mode 100644 index 00000000..7020750c --- /dev/null +++ b/lib/bt/host/bluedroid/api/include/api/esp_a2dp_api.h @@ -0,0 +1,463 @@ +/* + * SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __ESP_A2DP_API_H__ +#define __ESP_A2DP_API_H__ + +#include "esp_err.h" +#include "esp_bt_defs.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Media codec types supported by A2DP. + */ +#define ESP_A2D_MCT_SBC (0) /*!< SBC */ +#define ESP_A2D_MCT_M12 (0x01) /*!< MPEG-1, 2 Audio */ +#define ESP_A2D_MCT_M24 (0x02) /*!< MPEG-2, 4 AAC */ +#define ESP_A2D_MCT_ATRAC (0x04) /*!< ATRAC family */ +#define ESP_A2D_MCT_NON_A2DP (0xff) /*!< NON-A2DP */ +typedef uint8_t esp_a2d_mct_t; + +/** + * @brief Protocol service capabilities. This value is a mask. + */ +#define ESP_A2D_PSC_DELAY_RPT (1<<0) /*!< Delay Report */ +typedef uint16_t esp_a2d_psc_t; + +/** + * @brief A2DP media codec capabilities union + */ +typedef struct { + esp_a2d_mct_t type; /*!< A2DP media codec type */ +#define ESP_A2D_CIE_LEN_SBC (4) +#define ESP_A2D_CIE_LEN_M12 (4) +#define ESP_A2D_CIE_LEN_M24 (6) +#define ESP_A2D_CIE_LEN_ATRAC (7) + union { + uint8_t sbc[ESP_A2D_CIE_LEN_SBC]; /*!< SBC codec capabilities */ + uint8_t m12[ESP_A2D_CIE_LEN_M12]; /*!< MPEG-1,2 audio codec capabilities */ + uint8_t m24[ESP_A2D_CIE_LEN_M24]; /*!< MPEG-2, 4 AAC audio codec capabilities */ + uint8_t atrac[ESP_A2D_CIE_LEN_ATRAC]; /*!< ATRAC family codec capabilities */ + } cie; /*!< A2DP codec information element */ +} __attribute__((packed)) esp_a2d_mcc_t; + +/** + * @brief Bluetooth A2DP connection states + */ +typedef enum { + ESP_A2D_CONNECTION_STATE_DISCONNECTED = 0, /*!< connection released */ + ESP_A2D_CONNECTION_STATE_CONNECTING, /*!< connecting remote device */ + ESP_A2D_CONNECTION_STATE_CONNECTED, /*!< connection established */ + ESP_A2D_CONNECTION_STATE_DISCONNECTING /*!< disconnecting remote device */ +} esp_a2d_connection_state_t; + +/** + * @brief Bluetooth A2DP disconnection reason + */ +typedef enum { + ESP_A2D_DISC_RSN_NORMAL = 0, /*!< Finished disconnection that is initiated by local or remote device */ + ESP_A2D_DISC_RSN_ABNORMAL /*!< Abnormal disconnection caused by signal loss */ +} esp_a2d_disc_rsn_t; + +/** + * @brief Bluetooth A2DP datapath states + */ +typedef enum { + ESP_A2D_AUDIO_STATE_SUSPEND = 0, /*!< audio stream datapath suspended by remote device */ + ESP_A2D_AUDIO_STATE_STARTED, /*!< audio stream datapath started */ + ESP_A2D_AUDIO_STATE_STOPPED = ESP_A2D_AUDIO_STATE_SUSPEND, /*!< @note Deprecated */ + ESP_A2D_AUDIO_STATE_REMOTE_SUSPEND = ESP_A2D_AUDIO_STATE_SUSPEND, /*!< @note Deprecated */ +} esp_a2d_audio_state_t; + +/** + * @brief A2DP media control command acknowledgement code + */ +typedef enum { + ESP_A2D_MEDIA_CTRL_ACK_SUCCESS = 0, /*!< media control command is acknowledged with success */ + ESP_A2D_MEDIA_CTRL_ACK_FAILURE, /*!< media control command is acknowledged with failure */ + ESP_A2D_MEDIA_CTRL_ACK_BUSY, /*!< media control command is rejected, as previous command is not yet acknowledged */ +} esp_a2d_media_ctrl_ack_t; + +/** + * @brief A2DP media control commands + */ +typedef enum { + ESP_A2D_MEDIA_CTRL_NONE = 0, /*!< Not for application use, use inside stack only. */ + ESP_A2D_MEDIA_CTRL_CHECK_SRC_RDY, /*!< check whether AVDTP is connected, only used in A2DP source */ + ESP_A2D_MEDIA_CTRL_START, /*!< command to set up media transmission channel */ + ESP_A2D_MEDIA_CTRL_SUSPEND, /*!< command to suspend media transmission */ + ESP_A2D_MEDIA_CTRL_STOP, /*!< @note Deprecated, Please use ESP_A2D_MEDIA_CTRL_SUSPEND */ +} esp_a2d_media_ctrl_t; + +/** + * @brief Bluetooth A2DP Initiation states + */ +typedef enum { + ESP_A2D_DEINIT_SUCCESS = 0, /*!< A2DP profile deinit successful event */ + ESP_A2D_INIT_SUCCESS /*!< A2DP profile deinit successful event */ +} esp_a2d_init_state_t; + +/** + * @brief Bluetooth A2DP set delay report value states + */ +typedef enum { + ESP_A2D_SET_SUCCESS = 0, /*!< A2DP profile set delay report value successful */ + ESP_A2D_SET_INVALID_PARAMS /*!< A2DP profile set delay report value is invalid parameter */ +} esp_a2d_set_delay_value_state_t; + +/** + * @brief A2DP callback events + */ +typedef enum { + ESP_A2D_CONNECTION_STATE_EVT = 0, /*!< connection state changed event */ + ESP_A2D_AUDIO_STATE_EVT, /*!< audio stream transmission state changed event */ + ESP_A2D_AUDIO_CFG_EVT, /*!< audio codec is configured, only used for A2DP SINK */ + ESP_A2D_MEDIA_CTRL_ACK_EVT, /*!< acknowledge event in response to media control commands */ + ESP_A2D_PROF_STATE_EVT, /*!< indicate a2dp init&deinit complete */ + ESP_A2D_SNK_PSC_CFG_EVT, /*!< protocol service capabilities configured,only used for A2DP SINK */ + ESP_A2D_SNK_SET_DELAY_VALUE_EVT, /*!< indicate a2dp sink set delay report value complete, only used for A2DP SINK */ + ESP_A2D_SNK_GET_DELAY_VALUE_EVT, /*!< indicate a2dp sink get delay report value complete, only used for A2DP SINK */ + ESP_A2D_REPORT_SNK_DELAY_VALUE_EVT, /*!< report delay value, only used for A2DP SRC */ +} esp_a2d_cb_event_t; + +/** + * @brief A2DP state callback parameters + */ +typedef union { + /** + * @brief ESP_A2D_CONNECTION_STATE_EVT + */ + struct a2d_conn_stat_param { + esp_a2d_connection_state_t state; /*!< one of values from esp_a2d_connection_state_t */ + esp_bd_addr_t remote_bda; /*!< remote bluetooth device address */ + esp_a2d_disc_rsn_t disc_rsn; /*!< reason of disconnection for "DISCONNECTED" */ + } conn_stat; /*!< A2DP connection status */ + + /** + * @brief ESP_A2D_AUDIO_STATE_EVT + */ + struct a2d_audio_stat_param { + esp_a2d_audio_state_t state; /*!< one of the values from esp_a2d_audio_state_t */ + esp_bd_addr_t remote_bda; /*!< remote bluetooth device address */ + } audio_stat; /*!< audio stream playing state */ + + /** + * @brief ESP_A2D_AUDIO_CFG_EVT + */ + struct a2d_audio_cfg_param { + esp_bd_addr_t remote_bda; /*!< remote bluetooth device address */ + esp_a2d_mcc_t mcc; /*!< A2DP media codec capability information */ + } audio_cfg; /*!< media codec configuration information */ + + /** + * @brief ESP_A2D_MEDIA_CTRL_ACK_EVT + */ + struct media_ctrl_stat_param { + esp_a2d_media_ctrl_t cmd; /*!< media control commands to acknowledge */ + esp_a2d_media_ctrl_ack_t status; /*!< acknowledgement to media control commands */ + } media_ctrl_stat; /*!< status in acknowledgement to media control commands */ + + /** + * @brief ESP_A2D_PROF_STATE_EVT + */ + struct a2d_prof_stat_param { + esp_a2d_init_state_t init_state; /*!< a2dp profile state param */ + } a2d_prof_stat; /*!< status to indicate a2d prof init or deinit */ + + /** + * @brief ESP_A2D_SNK_PSC_CFG_EVT + */ + struct a2d_psc_cfg_param { + esp_a2d_psc_t psc_mask; /*!< protocol service capabilities configured */ + } a2d_psc_cfg_stat; /*!< status to indicate protocol service capabilities configured */ + + /** + * @brief ESP_A2D_SNK_SET_DELAY_VALUE_EVT + */ + struct a2d_set_stat_param { + esp_a2d_set_delay_value_state_t set_state; /*!< a2dp profile state param */ + uint16_t delay_value; /*!< delay report value */ + } a2d_set_delay_value_stat; /*!< A2DP sink set delay report value status */ + + /** + * @brief ESP_A2D_SNK_GET_DELAY_VALUE_EVT + */ + struct a2d_get_stat_param { + uint16_t delay_value; /*!< delay report value */ + } a2d_get_delay_value_stat; /*!< A2DP sink get delay report value status */ + + /** + * @brief ESP_A2D_REPORT_SNK_DELAY_VALUE_EVT + */ + struct a2d_report_delay_stat_param { + uint16_t delay_value; /*!< delay report value */ + } a2d_report_delay_value_stat; /*!< A2DP source received sink report value status */ + +} esp_a2d_cb_param_t; + +/** + * @brief A2DP profile callback function type + * + * @param event : Event type + * + * @param param : Pointer to callback parameter + */ +typedef void (* esp_a2d_cb_t)(esp_a2d_cb_event_t event, esp_a2d_cb_param_t *param); + +/** + * @brief A2DP sink data callback function + * + * @param[in] buf : pointer to the data received from A2DP source device and is PCM format decoded from SBC decoder; + * buf references to a static memory block and can be overwritten by upcoming data + * + * @param[in] len : size(in bytes) in buf + */ +typedef void (* esp_a2d_sink_data_cb_t)(const uint8_t *buf, uint32_t len); + +/** + * @brief A2DP source data read callback function + * + * @param[in] buf : buffer to be filled with PCM data stream from higher layer + * + * @param[in] len : size(in bytes) of data block to be copied to buf. -1 is an indication to user + * that data buffer shall be flushed + * + * @return size of bytes read successfully, if the argument len is -1, this value is ignored. + * + */ +typedef int32_t (* esp_a2d_source_data_cb_t)(uint8_t *buf, int32_t len); + +/** + * @brief Register application callback function to A2DP module. This function should be called + * only after esp_bluedroid_enable() completes successfully, used by both A2DP source + * and sink. + * + * @param[in] callback: A2DP event callback function + * + * @return + * - ESP_OK: success + * - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled + * - ESP_FAIL: if callback is a NULL function pointer + * + */ +esp_err_t esp_a2d_register_callback(esp_a2d_cb_t callback); + + +/** + * @brief Register A2DP sink data output function; For now the output is PCM data stream decoded + * from SBC format. This function should be called only after esp_bluedroid_enable() + * completes successfully, used only by A2DP sink. The callback is invoked in the context + * of A2DP sink task whose stack size is configurable through menuconfig. + * + * @param[in] callback: A2DP sink data callback function + * + * @return + * - ESP_OK: success + * - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled + * - ESP_FAIL: if callback is a NULL function pointer + * + */ +esp_err_t esp_a2d_sink_register_data_callback(esp_a2d_sink_data_cb_t callback); + + +/** + * + * @brief Initialize the bluetooth A2DP sink module. This function should be called + * after esp_bluedroid_enable() completes successfully, and ESP_A2D_PROF_STATE_EVT + * with ESP_A2D_INIT_SUCCESS will reported to the APP layer. Note: A2DP can work independently. + * If you want to use AVRC together, you should initiate AVRC first. This + * function should be called after esp_bluedroid_enable() completes successfully. + * + * @return + * - ESP_OK: if the initialization request is sent successfully + * - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled + * - ESP_FAIL: others + * + */ +esp_err_t esp_a2d_sink_init(void); + + +/** + * + * @brief De-initialize for A2DP sink module. This function + * should be called only after esp_bluedroid_enable() completes successfully, + * and ESP_A2D_PROF_STATE_EVT with ESP_A2D_DEINIT_SUCCESS will reported to APP layer. + * + * @return + * - ESP_OK: if the deinitialization request is sent successfully + * - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled + * - ESP_FAIL: others + * + */ +esp_err_t esp_a2d_sink_deinit(void); + + +/** + * + * @brief Connect to remote bluetooth A2DP source device. This API must be called after + * esp_a2d_sink_init() and before esp_a2d_sink_deinit(). + * + * @param[in] remote_bda: remote bluetooth device address + * + * @return + * - ESP_OK: connect request is sent to lower layer successfully + * - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled + * - ESP_FAIL: others + * + */ +esp_err_t esp_a2d_sink_connect(esp_bd_addr_t remote_bda); + + +/** + * + * @brief Disconnect from the remote A2DP source device. This API must be called after + * esp_a2d_sink_init() and before esp_a2d_sink_deinit(). + * + * @param[in] remote_bda: remote bluetooth device address + * + * @return + * - ESP_OK: disconnect request is sent to lower layer successfully + * - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled + * - ESP_FAIL: others + * + */ +esp_err_t esp_a2d_sink_disconnect(esp_bd_addr_t remote_bda); + +/** + * + * @brief Set delay reporting value. The delay value of sink is caused by buffering (including + * protocol stack and application layer), decoding and rendering. The default delay + * value is 120ms, if the set value is less than 120ms, the setting will fail. This API + * must be called after esp_a2d_sink_init() and before esp_a2d_sink_deinit(). + * + * @param[in] delay_value: reporting value is in 1/10 millisecond + * + * @return + * - ESP_OK: delay value is sent to lower layer successfully + * - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled + * - ESP_FAIL: others + * + */ +esp_err_t esp_a2d_sink_set_delay_value(uint16_t delay_value); + +/** + * + * @brief Get delay reporting value. This API must be called after + * esp_a2d_sink_init() and before esp_a2d_sink_deinit(). + * + * @return + * - ESP_OK: if the request is sent successfully + * - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled + * - ESP_FAIL: others + * + */ +esp_err_t esp_a2d_sink_get_delay_value(void); + + +/** + * + * @brief Media control commands. This API can be used for both A2DP sink and source + * and must be called after esp_a2d_sink_init() and before esp_a2d_sink_deinit(). + * + * @param[in] ctrl: control commands for A2DP data channel + * + * @return + * - ESP_OK: control command is sent to lower layer successfully + * - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled + * - ESP_FAIL: others + * + */ +esp_err_t esp_a2d_media_ctrl(esp_a2d_media_ctrl_t ctrl); + + +/** + * + * @brief Initialize the bluetooth A2DP source module. A2DP can work independently. + * If you want to use AVRC together, you should initiate AVRC first. This function should be called + * after esp_bluedroid_enable() completes successfully, and ESP_A2D_PROF_STATE_EVT + * with ESP_A2D_INIT_SUCCESS will reported to the APP layer. Note: A2DP can work independently. + * If you want to use AVRC together, you should initiate AVRC first. + * + * @return + * - ESP_OK: if the initialization request is sent to lower layer successfully + * - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled + * - ESP_FAIL: others + * + */ +esp_err_t esp_a2d_source_init(void); + + +/** + * + * @brief De-initialize for A2DP source module. This function + * should be called only after esp_bluedroid_enable() completes successfully, + * and ESP_A2D_PROF_STATE_EVT with ESP_A2D_DEINIT_SUCCESS will reported to APP layer. + * + * @return + * - ESP_OK: success + * - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled + * - ESP_FAIL: others + * + */ +esp_err_t esp_a2d_source_deinit(void); + + +/** + * @brief Register A2DP source data input function. For now, the input shoule be PCM data stream. + * This function should be called only after esp_bluedroid_enable() completes + * successfully. The callback is invoked in the context of A2DP source task whose + * stack size is configurable through menuconfig. + * + * @param[in] callback: A2DP source data callback function + * + * @return + * - ESP_OK: success + * - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled + * - ESP_FAIL: if callback is a NULL function pointer + * + */ +esp_err_t esp_a2d_source_register_data_callback(esp_a2d_source_data_cb_t callback); + + +/** + * + * @brief Connect to remote A2DP sink device. This API must be called + * after esp_a2d_source_init() and before esp_a2d_source_deinit(). + * + * @param[in] remote_bda: remote bluetooth device address + * + * @return + * - ESP_OK: connect request is sent to lower layer successfully + * - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled + * - ESP_FAIL: others + * + */ +esp_err_t esp_a2d_source_connect(esp_bd_addr_t remote_bda); + + +/** + * + * @brief Disconnect from the remote A2DP sink device. This API must be called + * after esp_a2d_source_init() and before esp_a2d_source_deinit(). + * + * @param[in] remote_bda: remote bluetooth device address + * @return + * - ESP_OK: disconnect request is sent to lower layer + * - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled + * - ESP_FAIL: others + * + */ +esp_err_t esp_a2d_source_disconnect(esp_bd_addr_t remote_bda); + +#ifdef __cplusplus +} +#endif + + +#endif /* __ESP_A2DP_API_H__ */ diff --git a/lib/bt/host/bluedroid/api/include/api/esp_avrc_api.h b/lib/bt/host/bluedroid/api/include/api/esp_avrc_api.h new file mode 100644 index 00000000..f226577e --- /dev/null +++ b/lib/bt/host/bluedroid/api/include/api/esp_avrc_api.h @@ -0,0 +1,736 @@ +/* + * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __ESP_AVRC_API_H__ +#define __ESP_AVRC_API_H__ + +#include +#include +#include "esp_err.h" +#include "esp_bt_defs.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define ESP_AVRC_TRANS_LABEL_MAX 15 /*!< max transaction label */ + +/// AVRC feature bit mask +typedef enum { + ESP_AVRC_FEAT_RCTG = 0x0001, /*!< remote control target */ + ESP_AVRC_FEAT_RCCT = 0x0002, /*!< remote control controller */ + ESP_AVRC_FEAT_VENDOR = 0x0008, /*!< remote control vendor dependent commands */ + ESP_AVRC_FEAT_BROWSE = 0x0010, /*!< use browsing channel */ + ESP_AVRC_FEAT_META_DATA = 0x0040, /*!< remote control metadata transfer command/response */ + ESP_AVRC_FEAT_ADV_CTRL = 0x0200, /*!< remote control advanced control command/response */ +} esp_avrc_features_t; + +/// AVRC supported features flag retrieved in SDP record +typedef enum { + ESP_AVRC_FEAT_FLAG_CAT1 = 0x0001, /*!< category 1 */ + ESP_AVRC_FEAT_FLAG_CAT2 = 0x0002, /*!< category 2 */ + ESP_AVRC_FEAT_FLAG_CAT3 = 0x0004, /*!< category 3 */ + ESP_AVRC_FEAT_FLAG_CAT4 = 0x0008, /*!< category 4 */ + ESP_AVRC_FEAT_FLAG_BROWSING = 0x0040, /*!< browsing */ + ESP_AVRC_FEAT_FLAG_COVER_ART_GET_IMAGE_PROP = 0x0080, /*!< Cover Art GetImageProperties */ + ESP_AVRC_FEAT_FLAG_COVER_ART_GET_IMAGE = 0x0100, /*!< Cover Art GetImage */ + ESP_AVRC_FEAT_FLAG_COVER_ART_GET_LINKED_THUMBNAIL = 0x0200, /*!< Cover Art GetLinkedThumbnail */ +} esp_avrc_feature_flag_t; + +/// AVRC passthrough command code +typedef enum { + ESP_AVRC_PT_CMD_SELECT = 0x00, /*!< select */ + ESP_AVRC_PT_CMD_UP = 0x01, /*!< up */ + ESP_AVRC_PT_CMD_DOWN = 0x02, /*!< down */ + ESP_AVRC_PT_CMD_LEFT = 0x03, /*!< left */ + ESP_AVRC_PT_CMD_RIGHT = 0x04, /*!< right */ + ESP_AVRC_PT_CMD_RIGHT_UP = 0x05, /*!< right-up */ + ESP_AVRC_PT_CMD_RIGHT_DOWN = 0x06, /*!< right-down */ + ESP_AVRC_PT_CMD_LEFT_UP = 0x07, /*!< left-up */ + ESP_AVRC_PT_CMD_LEFT_DOWN = 0x08, /*!< left-down */ + ESP_AVRC_PT_CMD_ROOT_MENU = 0x09, /*!< root menu */ + ESP_AVRC_PT_CMD_SETUP_MENU = 0x0A, /*!< setup menu */ + ESP_AVRC_PT_CMD_CONT_MENU = 0x0B, /*!< contents menu */ + ESP_AVRC_PT_CMD_FAV_MENU = 0x0C, /*!< favorite menu */ + ESP_AVRC_PT_CMD_EXIT = 0x0D, /*!< exit */ + ESP_AVRC_PT_CMD_0 = 0x20, /*!< 0 */ + ESP_AVRC_PT_CMD_1 = 0x21, /*!< 1 */ + ESP_AVRC_PT_CMD_2 = 0x22, /*!< 2 */ + ESP_AVRC_PT_CMD_3 = 0x23, /*!< 3 */ + ESP_AVRC_PT_CMD_4 = 0x24, /*!< 4 */ + ESP_AVRC_PT_CMD_5 = 0x25, /*!< 5 */ + ESP_AVRC_PT_CMD_6 = 0x26, /*!< 6 */ + ESP_AVRC_PT_CMD_7 = 0x27, /*!< 7 */ + ESP_AVRC_PT_CMD_8 = 0x28, /*!< 8 */ + ESP_AVRC_PT_CMD_9 = 0x29, /*!< 9 */ + ESP_AVRC_PT_CMD_DOT = 0x2A, /*!< dot */ + ESP_AVRC_PT_CMD_ENTER = 0x2B, /*!< enter */ + ESP_AVRC_PT_CMD_CLEAR = 0x2C, /*!< clear */ + ESP_AVRC_PT_CMD_CHAN_UP = 0x30, /*!< channel up */ + ESP_AVRC_PT_CMD_CHAN_DOWN = 0x31, /*!< channel down */ + ESP_AVRC_PT_CMD_PREV_CHAN = 0x32, /*!< previous channel */ + ESP_AVRC_PT_CMD_SOUND_SEL = 0x33, /*!< sound select */ + ESP_AVRC_PT_CMD_INPUT_SEL = 0x34, /*!< input select */ + ESP_AVRC_PT_CMD_DISP_INFO = 0x35, /*!< display information */ + ESP_AVRC_PT_CMD_HELP = 0x36, /*!< help */ + ESP_AVRC_PT_CMD_PAGE_UP = 0x37, /*!< page up */ + ESP_AVRC_PT_CMD_PAGE_DOWN = 0x38, /*!< page down */ + ESP_AVRC_PT_CMD_POWER = 0x40, /*!< power */ + ESP_AVRC_PT_CMD_VOL_UP = 0x41, /*!< volume up */ + ESP_AVRC_PT_CMD_VOL_DOWN = 0x42, /*!< volume down */ + ESP_AVRC_PT_CMD_MUTE = 0x43, /*!< mute */ + ESP_AVRC_PT_CMD_PLAY = 0x44, /*!< play */ + ESP_AVRC_PT_CMD_STOP = 0x45, /*!< stop */ + ESP_AVRC_PT_CMD_PAUSE = 0x46, /*!< pause */ + ESP_AVRC_PT_CMD_RECORD = 0x47, /*!< record */ + ESP_AVRC_PT_CMD_REWIND = 0x48, /*!< rewind */ + ESP_AVRC_PT_CMD_FAST_FORWARD = 0x49, /*!< fast forward */ + ESP_AVRC_PT_CMD_EJECT = 0x4A, /*!< eject */ + ESP_AVRC_PT_CMD_FORWARD = 0x4B, /*!< forward */ + ESP_AVRC_PT_CMD_BACKWARD = 0x4C, /*!< backward */ + ESP_AVRC_PT_CMD_ANGLE = 0x50, /*!< angle */ + ESP_AVRC_PT_CMD_SUBPICT = 0x51, /*!< subpicture */ + ESP_AVRC_PT_CMD_F1 = 0x71, /*!< F1 */ + ESP_AVRC_PT_CMD_F2 = 0x72, /*!< F2 */ + ESP_AVRC_PT_CMD_F3 = 0x73, /*!< F3 */ + ESP_AVRC_PT_CMD_F4 = 0x74, /*!< F4 */ + ESP_AVRC_PT_CMD_F5 = 0x75, /*!< F5 */ + ESP_AVRC_PT_CMD_VENDOR = 0x7E, /*!< vendor unique */ +} esp_avrc_pt_cmd_t; + +/// AVRC passthrough command filter +typedef enum { + ESP_AVRC_PSTH_FILTER_ALLOWED_CMD = 0, /*!< all of the PASSTHROUGH commands that can possibly be used, immutable */ + ESP_AVRC_PSTH_FILTER_SUPPORTED_CMD = 1, /*!< PASSTHROUGH commands selectively supported according to the current configuration */ + ESP_AVRC_PSTH_FILTER_SUPPORT_MAX, +} esp_avrc_psth_filter_t; + +/// AVRC passthrough command bit mask +typedef struct { + uint16_t bits[8]; /*!< bit mask representation of PASSTHROUGH commands */ +} esp_avrc_psth_bit_mask_t; + +typedef enum { + ESP_AVRC_BIT_MASK_OP_TEST = 0, /*!< operation code to test a specific bit */ + ESP_AVRC_BIT_MASK_OP_SET = 1, /*!< operation code to set a specific bit */ + ESP_AVRC_BIT_MASK_OP_CLEAR = 2, /*!< operation code to clear a specific bit */ +} esp_avrc_bit_mask_op_t; + +/// AVRC passthrough command state +typedef enum { + ESP_AVRC_PT_CMD_STATE_PRESSED = 0, /*!< key pressed */ + ESP_AVRC_PT_CMD_STATE_RELEASED = 1 /*!< key released */ +} esp_avrc_pt_cmd_state_t; + +/// AVRC Controller callback events +typedef enum { + ESP_AVRC_CT_CONNECTION_STATE_EVT = 0, /*!< connection state changed event */ + ESP_AVRC_CT_PASSTHROUGH_RSP_EVT = 1, /*!< passthrough response event */ + ESP_AVRC_CT_METADATA_RSP_EVT = 2, /*!< metadata response event */ + ESP_AVRC_CT_PLAY_STATUS_RSP_EVT = 3, /*!< play status response event */ + ESP_AVRC_CT_CHANGE_NOTIFY_EVT = 4, /*!< notification event */ + ESP_AVRC_CT_REMOTE_FEATURES_EVT = 5, /*!< feature of remote device indication event */ + ESP_AVRC_CT_GET_RN_CAPABILITIES_RSP_EVT = 6, /*!< supported notification events capability of peer device */ + ESP_AVRC_CT_SET_ABSOLUTE_VOLUME_RSP_EVT = 7, /*!< set absolute volume response event */ +} esp_avrc_ct_cb_event_t; + +/// AVRC Target callback events +typedef enum { + ESP_AVRC_TG_CONNECTION_STATE_EVT = 0, /*!< connection state changed event */ + ESP_AVRC_TG_REMOTE_FEATURES_EVT = 1, /*!< feature of remote device indication event */ + ESP_AVRC_TG_PASSTHROUGH_CMD_EVT = 2, /*!< passthrough command event */ + ESP_AVRC_TG_SET_ABSOLUTE_VOLUME_CMD_EVT = 3, /*!< set absolute volume command from remote device */ + ESP_AVRC_TG_REGISTER_NOTIFICATION_EVT = 4, /*!< register notification event */ + ESP_AVRC_TG_SET_PLAYER_APP_VALUE_EVT = 5, /*!< set application attribute value, attribute refer to esp_avrc_ps_attr_ids_t */ +} esp_avrc_tg_cb_event_t; + +/// AVRC metadata attribute mask +typedef enum { + ESP_AVRC_MD_ATTR_TITLE = 0x1, /*!< title of the playing track */ + ESP_AVRC_MD_ATTR_ARTIST = 0x2, /*!< track artist */ + ESP_AVRC_MD_ATTR_ALBUM = 0x4, /*!< album name */ + ESP_AVRC_MD_ATTR_TRACK_NUM = 0x8, /*!< track position on the album */ + ESP_AVRC_MD_ATTR_NUM_TRACKS = 0x10, /*!< number of tracks on the album */ + ESP_AVRC_MD_ATTR_GENRE = 0x20, /*!< track genre */ + ESP_AVRC_MD_ATTR_PLAYING_TIME = 0x40 /*!< total album playing time in miliseconds */ +} esp_avrc_md_attr_mask_t; + +/// AVRC event notification ids +typedef enum { + ESP_AVRC_RN_PLAY_STATUS_CHANGE = 0x01, /*!< track status change, eg. from playing to paused */ + ESP_AVRC_RN_TRACK_CHANGE = 0x02, /*!< new track is loaded */ + ESP_AVRC_RN_TRACK_REACHED_END = 0x03, /*!< current track reached end */ + ESP_AVRC_RN_TRACK_REACHED_START = 0x04, /*!< current track reached start position */ + ESP_AVRC_RN_PLAY_POS_CHANGED = 0x05, /*!< track playing position changed */ + ESP_AVRC_RN_BATTERY_STATUS_CHANGE = 0x06, /*!< battery status changed */ + ESP_AVRC_RN_SYSTEM_STATUS_CHANGE = 0x07, /*!< system status changed */ + ESP_AVRC_RN_APP_SETTING_CHANGE = 0x08, /*!< application settings changed */ + ESP_AVRC_RN_NOW_PLAYING_CHANGE = 0x09, /*!< now playing content changed */ + ESP_AVRC_RN_AVAILABLE_PLAYERS_CHANGE = 0x0a, /*!< available players changed */ + ESP_AVRC_RN_ADDRESSED_PLAYER_CHANGE = 0x0b, /*!< the addressed player changed */ + ESP_AVRC_RN_UIDS_CHANGE = 0x0c, /*!< UIDs changed */ + ESP_AVRC_RN_VOLUME_CHANGE = 0x0d, /*!< volume changed locally on TG */ + ESP_AVRC_RN_MAX_EVT +} esp_avrc_rn_event_ids_t; + +/// AVRC target notification event notification capability +typedef enum { + ESP_AVRC_RN_CAP_ALLOWED_EVT = 0, /*!< all of the notification events that can possibly be supported, immutable */ + ESP_AVRC_RN_CAP_SUPPORTED_EVT = 1, /*!< notification events selectively supported according to the current configuration */ + ESP_AVRC_RN_CAP_MAX, +} esp_avrc_rn_evt_cap_t; + +/// AVRC target notification event capability bit mask +typedef struct { + uint16_t bits; /*!< bit mask representation of PASSTHROUGH commands */ +} esp_avrc_rn_evt_cap_mask_t; + +/// AVRC notification response type +typedef enum { + ESP_AVRC_RN_RSP_INTERIM = 13, /*!< initial response to RegisterNotification, should be sent T_mtp(1000ms) from receiving the command */ + ESP_AVRC_RN_RSP_CHANGED = 15, /*!< final response to RegisterNotification command */ +} esp_avrc_rn_rsp_t; + +/// AVRC player setting ids +typedef enum { + ESP_AVRC_PS_EQUALIZER = 0x01, /*!< equalizer, on or off */ + ESP_AVRC_PS_REPEAT_MODE = 0x02, /*!< repeat mode */ + ESP_AVRC_PS_SHUFFLE_MODE = 0x03, /*!< shuffle mode */ + ESP_AVRC_PS_SCAN_MODE = 0x04, /*!< scan mode on or off */ + ESP_AVRC_PS_MAX_ATTR +} esp_avrc_ps_attr_ids_t; + +/// AVRC equalizer modes +typedef enum { + ESP_AVRC_PS_EQUALIZER_OFF = 0x1, /*!< equalizer OFF */ + ESP_AVRC_PS_EQUALIZER_ON = 0x2 /*!< equalizer ON */ +} esp_avrc_ps_eq_value_ids_t; + +/// AVRC repeat modes +typedef enum { + ESP_AVRC_PS_REPEAT_OFF = 0x1, /*!< repeat mode off */ + ESP_AVRC_PS_REPEAT_SINGLE = 0x2, /*!< single track repeat */ + ESP_AVRC_PS_REPEAT_GROUP = 0x3 /*!< group repeat */ +} esp_avrc_ps_rpt_value_ids_t; + + +/// AVRC shuffle modes +typedef enum { + ESP_AVRC_PS_SHUFFLE_OFF = 0x1, /* +#include "esp_err.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* HCI driver callbacks */ +typedef struct esp_bluedroid_hci_driver_callbacks { + /** + * @brief callback used to notify that the host can send packet to controller + */ + void (*notify_host_send_available)(void); + + /** + * @brief callback used to notify that the controller has a packet to send to the host + * + * @param[in] data pointer to data buffer + * @param[in] len length of data + * + * @return 0 received successfully, failed otherwise + */ + int (*notify_host_recv)(uint8_t *data, uint16_t len); +} esp_bluedroid_hci_driver_callbacks_t; + +/* HCI driver operations */ +typedef struct esp_bluedroid_hci_driver_operations { + /** + * @brief send data from host to controller + * + * @param[in] data pointer to data buffer + * @param[in] len length of data + */ + void (*send)(uint8_t *data, uint16_t len); + + /** + * @brief host checks whether it can send data to controller + * + * @return true if host can send data, false otherwise + */ + bool (*check_send_available)(void); + + /** + * @brief register host callback + * + * @param[in] callback HCI driver callbacks + */ + esp_err_t (* register_host_callback)(const esp_bluedroid_hci_driver_callbacks_t *callback); +} esp_bluedroid_hci_driver_operations_t; + +/** + * @brief get the operations of HCI transport layer. This API should only be used in + * Bluedroid Host-only mode before Bluedroid initialization. + * + * @param[in] ops struct containing operations of HCI transport layer + * + * @return ESP_OK if get successfully, ESP_FAIL otherwise + */ +esp_err_t esp_bluedroid_attach_hci_driver(const esp_bluedroid_hci_driver_operations_t *ops); + +/** + * @brief remove the operations of HCI transport layer. This API should only be used in + * Bluedroid Host-only mode before Bluedroid initialization. + * + * @param[in] ops struct containing operations of HCI transport layer + * + * @return ESP_OK if get successfully, ESP_FAIL otherwise + */ +esp_err_t esp_bluedroid_detach_hci_driver(void); + +#ifdef __cplusplus +} +#endif + +#endif /* __ESP_BLUEDROID_HCI_H__ */ diff --git a/lib/bt/host/bluedroid/api/include/api/esp_bt_defs.h b/lib/bt/host/bluedroid/api/include/api/esp_bt_defs.h new file mode 100644 index 00000000..19419014 --- /dev/null +++ b/lib/bt/host/bluedroid/api/include/api/esp_bt_defs.h @@ -0,0 +1,205 @@ +/* + * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __ESP_BT_DEFS_H__ +#define __ESP_BT_DEFS_H__ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define ESP_BLUEDROID_STATUS_CHECK(status) \ + if (esp_bluedroid_get_status() != (status)) { \ + return ESP_ERR_INVALID_STATE; \ + } + +#define ESP_BT_STATUS_BASE_FOR_HCI_ERR 0X0100 /* base for coverting HCI error code to ESP status */ + +/* relate to BT_STATUS_xxx in bt_def.h */ +/// Status Return Value +typedef enum { + ESP_BT_STATUS_SUCCESS = 0, /* relate to BT_STATUS_SUCCESS in bt_def.h */ + ESP_BT_STATUS_FAIL, /* relate to BT_STATUS_FAIL in bt_def.h */ + ESP_BT_STATUS_NOT_READY, /* relate to BT_STATUS_NOT_READY in bt_def.h */ + ESP_BT_STATUS_NOMEM, /* relate to BT_STATUS_NOMEM in bt_def.h */ + ESP_BT_STATUS_BUSY, /* relate to BT_STATUS_BUSY in bt_def.h */ + ESP_BT_STATUS_DONE = 5, /* relate to BT_STATUS_DONE in bt_def.h */ + ESP_BT_STATUS_UNSUPPORTED, /* relate to BT_STATUS_UNSUPPORTED in bt_def.h */ + ESP_BT_STATUS_PARM_INVALID, /* relate to BT_STATUS_PARM_INVALID in bt_def.h */ + ESP_BT_STATUS_UNHANDLED, /* relate to BT_STATUS_UNHANDLED in bt_def.h */ + ESP_BT_STATUS_AUTH_FAILURE, /* relate to BT_STATUS_AUTH_FAILURE in bt_def.h */ + ESP_BT_STATUS_RMT_DEV_DOWN = 10, /* relate to BT_STATUS_RMT_DEV_DOWN in bt_def.h */ + ESP_BT_STATUS_AUTH_REJECTED, /* relate to BT_STATUS_AUTH_REJECTED in bt_def.h */ + ESP_BT_STATUS_INVALID_STATIC_RAND_ADDR, /* relate to BT_STATUS_INVALID_STATIC_RAND_ADDR in bt_def.h */ + ESP_BT_STATUS_PENDING, /* relate to BT_STATUS_PENDING in bt_def.h */ + ESP_BT_STATUS_UNACCEPT_CONN_INTERVAL, /* relate to BT_UNACCEPT_CONN_INTERVAL in bt_def.h */ + ESP_BT_STATUS_PARAM_OUT_OF_RANGE, /* relate to BT_PARAM_OUT_OF_RANGE in bt_def.h */ + ESP_BT_STATUS_TIMEOUT, /* relate to BT_STATUS_TIMEOUT in bt_def.h */ + ESP_BT_STATUS_PEER_LE_DATA_LEN_UNSUPPORTED, /* relate to BTM_PEER_LE_DATA_LEN_UNSUPPORTED in stack/btm_api.h */ + ESP_BT_STATUS_CONTROL_LE_DATA_LEN_UNSUPPORTED,/* relate to BTM_CONTROL_LE_DATA_LEN_UNSUPPORTED in stack/btm_api.h */ + ESP_BT_STATUS_ERR_ILLEGAL_PARAMETER_FMT, /* relate to HCI_ERR_ILLEGAL_PARAMETER_FMT in stack/hcidefs.h */ + ESP_BT_STATUS_MEMORY_FULL = 20, /* relate to BT_STATUS_MEMORY_FULL in bt_def.h */ + ESP_BT_STATUS_EIR_TOO_LARGE, /* relate to BT_STATUS_EIR_TOO_LARGE in bt_def.h */ + ESP_BT_STATUS_HCI_SUCCESS = ESP_BT_STATUS_BASE_FOR_HCI_ERR, + ESP_BT_STATUS_HCI_ILLEGAL_COMMAND, + ESP_BT_STATUS_HCI_NO_CONNECTION, + ESP_BT_STATUS_HCI_HW_FAILURE, + ESP_BT_STATUS_HCI_PAGE_TIMEOUT, + ESP_BT_STATUS_HCI_AUTH_FAILURE, + ESP_BT_STATUS_HCI_KEY_MISSING, + ESP_BT_STATUS_HCI_MEMORY_FULL, + ESP_BT_STATUS_HCI_CONNECTION_TOUT, + ESP_BT_STATUS_HCI_MAX_NUM_OF_CONNECTIONS, + ESP_BT_STATUS_HCI_MAX_NUM_OF_SCOS, + ESP_BT_STATUS_HCI_CONNECTION_EXISTS, + ESP_BT_STATUS_HCI_COMMAND_DISALLOWED, + ESP_BT_STATUS_HCI_HOST_REJECT_RESOURCES, + ESP_BT_STATUS_HCI_HOST_REJECT_SECURITY, + ESP_BT_STATUS_HCI_HOST_REJECT_DEVICE, + ESP_BT_STATUS_HCI_HOST_TIMEOUT, + ESP_BT_STATUS_HCI_UNSUPPORTED_VALUE, + ESP_BT_STATUS_HCI_ILLEGAL_PARAMETER_FMT, + ESP_BT_STATUS_HCI_PEER_USER, + ESP_BT_STATUS_HCI_PEER_LOW_RESOURCES, + ESP_BT_STATUS_HCI_PEER_POWER_OFF, + ESP_BT_STATUS_HCI_CONN_CAUSE_LOCAL_HOST, + ESP_BT_STATUS_HCI_REPEATED_ATTEMPTS, + ESP_BT_STATUS_HCI_PAIRING_NOT_ALLOWED, + ESP_BT_STATUS_HCI_UNKNOWN_LMP_PDU, + ESP_BT_STATUS_HCI_UNSUPPORTED_REM_FEATURE, + ESP_BT_STATUS_HCI_SCO_OFFSET_REJECTED, + ESP_BT_STATUS_HCI_SCO_INTERVAL_REJECTED, + ESP_BT_STATUS_HCI_SCO_AIR_MODE, + ESP_BT_STATUS_HCI_INVALID_LMP_PARAM, + ESP_BT_STATUS_HCI_UNSPECIFIED, + ESP_BT_STATUS_HCI_UNSUPPORTED_LMP_PARAMETERS, + ESP_BT_STATUS_HCI_ROLE_CHANGE_NOT_ALLOWED, + ESP_BT_STATUS_HCI_LMP_RESPONSE_TIMEOUT, + ESP_BT_STATUS_HCI_LMP_ERR_TRANS_COLLISION, + ESP_BT_STATUS_HCI_LMP_PDU_NOT_ALLOWED, + ESP_BT_STATUS_HCI_ENCRY_MODE_NOT_ACCEPTABLE, + ESP_BT_STATUS_HCI_UNIT_KEY_USED, + ESP_BT_STATUS_HCI_QOS_NOT_SUPPORTED, + ESP_BT_STATUS_HCI_INSTANT_PASSED, + ESP_BT_STATUS_HCI_PAIRING_WITH_UNIT_KEY_NOT_SUPPORTED, + ESP_BT_STATUS_HCI_DIFF_TRANSACTION_COLLISION, + ESP_BT_STATUS_HCI_UNDEFINED_0x2B, + ESP_BT_STATUS_HCI_QOS_UNACCEPTABLE_PARAM, + ESP_BT_STATUS_HCI_QOS_REJECTED, + ESP_BT_STATUS_HCI_CHAN_CLASSIF_NOT_SUPPORTED, + ESP_BT_STATUS_HCI_INSUFFCIENT_SECURITY, + ESP_BT_STATUS_HCI_PARAM_OUT_OF_RANGE, + ESP_BT_STATUS_HCI_UNDEFINED_0x31, + ESP_BT_STATUS_HCI_ROLE_SWITCH_PENDING, + ESP_BT_STATUS_HCI_UNDEFINED_0x33, + ESP_BT_STATUS_HCI_RESERVED_SLOT_VIOLATION, + ESP_BT_STATUS_HCI_ROLE_SWITCH_FAILED, + ESP_BT_STATUS_HCI_INQ_RSP_DATA_TOO_LARGE, + ESP_BT_STATUS_HCI_SIMPLE_PAIRING_NOT_SUPPORTED, + ESP_BT_STATUS_HCI_HOST_BUSY_PAIRING, + ESP_BT_STATUS_HCI_REJ_NO_SUITABLE_CHANNEL, + ESP_BT_STATUS_HCI_CONTROLLER_BUSY, + ESP_BT_STATUS_HCI_UNACCEPT_CONN_INTERVAL, + ESP_BT_STATUS_HCI_DIRECTED_ADVERTISING_TIMEOUT, + ESP_BT_STATUS_HCI_CONN_TOUT_DUE_TO_MIC_FAILURE, + ESP_BT_STATUS_HCI_CONN_FAILED_ESTABLISHMENT, + ESP_BT_STATUS_HCI_MAC_CONNECTION_FAILED, +} esp_bt_status_t; + + +/*Define the bt octet 16 bit size*/ +#define ESP_BT_OCTET16_LEN 16 +typedef uint8_t esp_bt_octet16_t[ESP_BT_OCTET16_LEN]; /* octet array: size 16 */ + +#define ESP_BT_OCTET8_LEN 8 +typedef uint8_t esp_bt_octet8_t[ESP_BT_OCTET8_LEN]; /* octet array: size 8 */ + +typedef uint8_t esp_link_key[ESP_BT_OCTET16_LEN]; /* Link Key */ + +/// Default GATT interface id +#define ESP_DEFAULT_GATT_IF 0xff + +#if BLE_HIGH_DUTY_ADV_INTERVAL +#define ESP_BLE_PRIM_ADV_INT_MIN 0x000008 /*!< Minimum advertising interval for undirected and low duty cycle directed advertising */ +#else +#define ESP_BLE_PRIM_ADV_INT_MIN 0x000020 /*!< Minimum advertising interval for undirected and low duty cycle directed advertising */ +#endif +#define ESP_BLE_PRIM_ADV_INT_MAX 0xFFFFFF /*!< Maximum advertising interval for undirected and low duty cycle directed advertising */ +#define ESP_BLE_CONN_INT_MIN 0x0006 /*!< relate to BTM_BLE_CONN_INT_MIN in stack/btm_ble_api.h */ +#define ESP_BLE_CONN_INT_MAX 0x0C80 /*!< relate to BTM_BLE_CONN_INT_MAX in stack/btm_ble_api.h */ +#define ESP_BLE_CONN_LATENCY_MAX 499 /*!< relate to ESP_BLE_CONN_LATENCY_MAX in stack/btm_ble_api.h */ +#define ESP_BLE_CONN_SUP_TOUT_MIN 0x000A /*!< relate to BTM_BLE_CONN_SUP_TOUT_MIN in stack/btm_ble_api.h */ +#define ESP_BLE_CONN_SUP_TOUT_MAX 0x0C80 /*!< relate to ESP_BLE_CONN_SUP_TOUT_MAX in stack/btm_ble_api.h */ + +/// Check the param is valid or not +#define ESP_BLE_IS_VALID_PARAM(x, min, max) (((x) >= (min) && (x) <= (max)) ) + +/// UUID type +typedef struct { +#define ESP_UUID_LEN_16 2 +#define ESP_UUID_LEN_32 4 +#define ESP_UUID_LEN_128 16 + uint16_t len; /*!< UUID length, 16bit, 32bit or 128bit */ + union { + uint16_t uuid16; /*!< 16bit UUID */ + uint32_t uuid32; /*!< 32bit UUID */ + uint8_t uuid128[ESP_UUID_LEN_128]; /*!< 128bit UUID */ + } uuid; /*!< UUID */ +} __attribute__((packed)) esp_bt_uuid_t; + +/// Bluetooth device type +typedef enum { + ESP_BT_DEVICE_TYPE_BREDR = 0x01, + ESP_BT_DEVICE_TYPE_BLE = 0x02, + ESP_BT_DEVICE_TYPE_DUMO = 0x03, +} esp_bt_dev_type_t; + +/// Bluetooth address length +#define ESP_BD_ADDR_LEN 6 + +/// Bluetooth device address +typedef uint8_t esp_bd_addr_t[ESP_BD_ADDR_LEN]; + +/// BLE device address type +typedef enum { + BLE_ADDR_TYPE_PUBLIC = 0x00, /*!< Public Device Address */ + BLE_ADDR_TYPE_RANDOM = 0x01, /*!< Random Device Address. To set this address, use the function esp_ble_gap_set_rand_addr(esp_bd_addr_t rand_addr) */ + BLE_ADDR_TYPE_RPA_PUBLIC = 0x02, /*!< Resolvable Private Address (RPA) with public identity address */ + BLE_ADDR_TYPE_RPA_RANDOM = 0x03, /*!< Resolvable Private Address (RPA) with random identity address. To set this address, use the function esp_ble_gap_set_rand_addr(esp_bd_addr_t rand_addr) */ +} esp_ble_addr_type_t; + +/// white list address type +typedef enum { + BLE_WL_ADDR_TYPE_PUBLIC = 0x00, + BLE_WL_ADDR_TYPE_RANDOM = 0x01, +} esp_ble_wl_addr_type_t; + +/// Used to exchange the encryption key in the init key & response key +#define ESP_BLE_ENC_KEY_MASK (1 << 0) /* relate to BTM_BLE_ENC_KEY_MASK in stack/btm_api.h */ +/// Used to exchange the IRK key in the init key & response key +#define ESP_BLE_ID_KEY_MASK (1 << 1) /* relate to BTM_BLE_ID_KEY_MASK in stack/btm_api.h */ +/// Used to exchange the CSRK key in the init key & response key +#define ESP_BLE_CSR_KEY_MASK (1 << 2) /* relate to BTM_BLE_CSR_KEY_MASK in stack/btm_api.h */ +/// Used to exchange the link key(this key just used in the BLE & BR/EDR coexist mode) in the init key & response key +#define ESP_BLE_LINK_KEY_MASK (1 << 3) /* relate to BTM_BLE_LINK_KEY_MASK in stack/btm_api.h */ +typedef uint8_t esp_ble_key_mask_t; /* the key mask type */ + +/// Minimum of the application id +#define ESP_APP_ID_MIN 0x0000 +/// Maximum of the application id +#define ESP_APP_ID_MAX 0x7fff + +#define ESP_BD_ADDR_STR "%02x:%02x:%02x:%02x:%02x:%02x" +#define ESP_BD_ADDR_HEX(addr) addr[0], addr[1], addr[2], addr[3], addr[4], addr[5] + +#ifdef __cplusplus +} +#endif + +#endif /* __ESP_BT_DEFS_H__ */ diff --git a/lib/bt/host/bluedroid/api/include/api/esp_bt_device.h b/lib/bt/host/bluedroid/api/include/api/esp_bt_device.h new file mode 100644 index 00000000..5c00c456 --- /dev/null +++ b/lib/bt/host/bluedroid/api/include/api/esp_bt_device.h @@ -0,0 +1,99 @@ +/* + * SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __ESP_BT_DEVICE_H__ +#define __ESP_BT_DEVICE_H__ + +#include +#include +#include "esp_err.h" +#include "esp_bt_defs.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// coexist status for MESH +#define ESP_BT_DEV_COEX_BLE_ST_MESH_CONFIG 0x08 +#define ESP_BT_DEV_COEX_BLE_ST_MESH_TRAFFIC 0x10 +#define ESP_BT_DEV_COEX_BLE_ST_MESH_STANDBY 0x20 +// coexist status for A2DP +#define ESP_BT_DEV_COEX_BT_ST_A2DP_STREAMING 0x10 +#define ESP_BT_DEV_COEX_BT_ST_A2DP_PAUSED 0x20 + +// coexist operation +#define ESP_BT_DEV_COEX_OP_CLEAR 0x00 +#define ESP_BT_DEV_COEX_OP_SET 0x01 +typedef uint8_t esp_bt_dev_coex_op_t; + +/** + * @brief Bluetooth device coex type + */ +typedef enum { + ESP_BT_DEV_COEX_TYPE_BLE = 1, + ESP_BT_DEV_COEX_TYPE_BT, +} esp_bt_dev_coex_type_t; + +/** + * + * @brief Get bluetooth device address. Must use after "esp_bluedroid_enable". + * + * @return bluetooth device address (six bytes), or NULL if bluetooth stack is not enabled + */ +const uint8_t *esp_bt_dev_get_address(void); + + +/** + * @brief Set bluetooth device name. This function should be called after esp_bluedroid_enable() + * completes successfully. + * + * A BR/EDR/LE device type shall have a single Bluetooth device name which shall be + * identical irrespective of the physical channel used to perform the name discovery procedure. + * + * @param[in] name : device name to be set + * + * @return + * - ESP_OK : Succeed + * - ESP_ERR_INVALID_ARG : if name is NULL pointer or empty, or string length out of limit + * - ESP_ERR_INVALID_STATE : if bluetooth stack is not yet enabled + * - ESP_FAIL : others + */ +esp_err_t esp_bt_dev_set_device_name(const char *name); + +/** + * @brief Config bluetooth device coexis status. This function should be called after esp_bluedroid_enable() + * completes successfully. + * + * @param[in] type : coexist type to operate on + * @param[in] op : clear or set coexist status + * @param[in] status : coexist status to be configured + * + * @return + * - ESP_OK : Succeed + * - ESP_ERR_INVALID_ARG : if name is NULL pointer or empty, or string length out of limit + * - ESP_ERR_INVALID_STATE : if bluetooth stack is not yet enabled + * - ESP_FAIL : others + */ +esp_err_t esp_bt_dev_coex_status_config(esp_bt_dev_coex_type_t type, esp_bt_dev_coex_op_t op, uint8_t status); + +/** + * @brief This function is used to update the path name of bluetooth bond keys saved in the NVS module + * and need to be called before esp_bluedroid_init(). + * @param[in] file_path: the name of config file path, the length of file_path should be less than NVS_NS_NAME_MAX_SIZE + * + * @return + * - ESP_OK: success + * - other: failed + * + */ +esp_err_t esp_bt_config_file_path_update(const char *file_path); + +#ifdef __cplusplus +} +#endif + + +#endif /* __ESP_BT_DEVICE_H__ */ diff --git a/lib/bt/host/bluedroid/api/include/api/esp_bt_main.h b/lib/bt/host/bluedroid/api/include/api/esp_bt_main.h new file mode 100644 index 00000000..db6826e3 --- /dev/null +++ b/lib/bt/host/bluedroid/api/include/api/esp_bt_main.h @@ -0,0 +1,99 @@ +/* + * SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __ESP_BT_MAIN_H__ +#define __ESP_BT_MAIN_H__ + +#include +#include + +#include "esp_err.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Bluetooth stack status type, to indicate whether the bluetooth stack is ready. + */ +typedef enum { + ESP_BLUEDROID_STATUS_UNINITIALIZED = 0, /*!< Bluetooth not initialized */ + ESP_BLUEDROID_STATUS_INITIALIZED, /*!< Bluetooth initialized but not enabled */ + ESP_BLUEDROID_STATUS_ENABLED /*!< Bluetooth initialized and enabled */ +} esp_bluedroid_status_t; + +/** + * @brief Bluetooth stack configuration + */ +typedef struct { + bool ssp_en; /*!< Whether SSP(secure simple pairing) or legacy pairing is used for Classic Bluetooth */ +} esp_bluedroid_config_t; + +#define BT_BLUEDROID_INIT_CONFIG_DEFAULT() \ + { \ + .ssp_en = true, \ + } + +/** + * @brief Get bluetooth stack status + * + * @return Bluetooth stack status + * + */ +esp_bluedroid_status_t esp_bluedroid_get_status(void); + +/** + * @brief Enable bluetooth, must after esp_bluedroid_init()/esp_bluedroid_init_with_cfg(). + * + * @return + * - ESP_OK : Succeed + * - Other : Failed + */ +esp_err_t esp_bluedroid_enable(void); + +/** + * @brief Disable bluetooth, must prior to esp_bluedroid_deinit(). + * + * @return + * - ESP_OK : Succeed + * - Other : Failed + */ +esp_err_t esp_bluedroid_disable(void); + +/** + * @brief Init and alloc the resource for bluetooth, must be prior to every bluetooth stuff. + * + * @return + * - ESP_OK : Succeed + * - Other : Failed + */ +esp_err_t esp_bluedroid_init(void) __attribute__((deprecated("Please use esp_bluedroid_init_with_cfg"))); + +/** + * @brief Init and alloc the resource for bluetooth, must be prior to every bluetooth stuff. + * + * @param cfg Initial configuration of ESP Bluedroid stack. + * + * @return + * - ESP_OK : Succeed + * - Other : Failed + */ +esp_err_t esp_bluedroid_init_with_cfg(esp_bluedroid_config_t *cfg); + +/** + * @brief Deinit and free the resource for bluetooth, must be after every bluetooth stuff. + * + * @return + * - ESP_OK : Succeed + * - Other : Failed + */ +esp_err_t esp_bluedroid_deinit(void); + +#ifdef __cplusplus +} +#endif + +#endif /* __ESP_BT_MAIN_H__ */ diff --git a/lib/bt/host/bluedroid/api/include/api/esp_gap_ble_api.h b/lib/bt/host/bluedroid/api/include/api/esp_gap_ble_api.h new file mode 100644 index 00000000..e3904d6d --- /dev/null +++ b/lib/bt/host/bluedroid/api/include/api/esp_gap_ble_api.h @@ -0,0 +1,2547 @@ +/* + * SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __ESP_GAP_BLE_API_H__ +#define __ESP_GAP_BLE_API_H__ + +#include +#include + +#include "esp_err.h" +#include "esp_bt_defs.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief BLE_ADV_DATA_FLAG data flag bit definition used for advertising data flag + */ +#define ESP_BLE_ADV_FLAG_LIMIT_DISC (0x01 << 0) +#define ESP_BLE_ADV_FLAG_GEN_DISC (0x01 << 1) +#define ESP_BLE_ADV_FLAG_BREDR_NOT_SPT (0x01 << 2) +#define ESP_BLE_ADV_FLAG_DMT_CONTROLLER_SPT (0x01 << 3) +#define ESP_BLE_ADV_FLAG_DMT_HOST_SPT (0x01 << 4) +#define ESP_BLE_ADV_FLAG_NON_LIMIT_DISC (0x00 ) + + +/// relate to BTM_LE_KEY_xxx in stack/btm_api.h +#define ESP_LE_KEY_NONE 0 /*!< No encryption key */ +#define ESP_LE_KEY_PENC (1 << 0) /*!< encryption key, encryption information of peer device */ +#define ESP_LE_KEY_PID (1 << 1) /*!< identity key of the peer device */ +#define ESP_LE_KEY_PCSRK (1 << 2) /*!< peer SRK */ +#define ESP_LE_KEY_PLK (1 << 3) /*!< Link key*/ +#define ESP_LE_KEY_LLK (ESP_LE_KEY_PLK << 4) /*!< peer link key*/ +#define ESP_LE_KEY_LENC (ESP_LE_KEY_PENC << 4) /*!< master role security information:div */ +#define ESP_LE_KEY_LID (ESP_LE_KEY_PID << 4) /*!< master device ID key */ +#define ESP_LE_KEY_LCSRK (ESP_LE_KEY_PCSRK << 4) /*!< local CSRK has been deliver to peer */ +typedef uint8_t esp_ble_key_type_t; + +/// relate to BTM_LE_AUTH_xxx in stack/btm_api.h +#define ESP_LE_AUTH_NO_BOND 0x00 /*!< 0 no bondingv*/ +#define ESP_LE_AUTH_BOND 0x01 /*!< 1 << 0 device in the bonding with peer */ +#define ESP_LE_AUTH_REQ_MITM (1 << 2) /*!< 1 << 2 man in the middle attack */ +#define ESP_LE_AUTH_REQ_BOND_MITM (ESP_LE_AUTH_BOND | ESP_LE_AUTH_REQ_MITM) /*!< 0101 banding with man in the middle attack */ +#define ESP_LE_AUTH_REQ_SC_ONLY (1 << 3) /*!< 1 << 3 secure connection */ +#define ESP_LE_AUTH_REQ_SC_BOND (ESP_LE_AUTH_BOND | ESP_LE_AUTH_REQ_SC_ONLY) /*!< 1001 secure connection with band*/ +#define ESP_LE_AUTH_REQ_SC_MITM (ESP_LE_AUTH_REQ_MITM | ESP_LE_AUTH_REQ_SC_ONLY) /*!< 1100 secure conn with MITM */ +#define ESP_LE_AUTH_REQ_SC_MITM_BOND (ESP_LE_AUTH_REQ_MITM | ESP_LE_AUTH_REQ_SC_ONLY | ESP_LE_AUTH_BOND) /*!< 1101 SC with MITM and Bonding*/ +typedef uint8_t esp_ble_auth_req_t; /*!< combination of the above bit pattern */ + +#define ESP_BLE_ONLY_ACCEPT_SPECIFIED_AUTH_DISABLE 0 /*!< authentication disable*/ +#define ESP_BLE_ONLY_ACCEPT_SPECIFIED_AUTH_ENABLE 1 /*!< authentication enable*/ + +#define ESP_BLE_OOB_DISABLE 0 /*!< disbale the out of bond*/ +#define ESP_BLE_OOB_ENABLE 1 /*!< enable the out of bond*/ + +/// relate to BTM_IO_CAP_xxx in stack/btm_api.h +#define ESP_IO_CAP_OUT 0 /*!< DisplayOnly */ +#define ESP_IO_CAP_IO 1 /*!< DisplayYesNo */ +#define ESP_IO_CAP_IN 2 /*!< KeyboardOnly */ +#define ESP_IO_CAP_NONE 3 /*!< NoInputNoOutput */ +#define ESP_IO_CAP_KBDISP 4 /*!< Keyboard display */ + +#define ESP_BLE_APPEARANCE_UNKNOWN 0x0000 /*!< relate to BTM_BLE_APPEARANCE_UNKNOWN in stack/btm_ble_api.h */ +#define ESP_BLE_APPEARANCE_GENERIC_PHONE 0x0040 /*!< relate to BTM_BLE_APPEARANCE_GENERIC_PHONE in stack/btm_ble_api.h */ +#define ESP_BLE_APPEARANCE_GENERIC_COMPUTER 0x0080 /*!< relate to BTM_BLE_APPEARANCE_GENERIC_COMPUTER in stack/btm_ble_api.h */ +#define ESP_BLE_APPEARANCE_GENERIC_WATCH 0x00C0 /*!< relate to BTM_BLE_APPEARANCE_GENERIC_WATCH in stack/btm_ble_api.h */ +#define ESP_BLE_APPEARANCE_SPORTS_WATCH 0x00C1 /*!< relate to BTM_BLE_APPEARANCE_SPORTS_WATCH in stack/btm_ble_api.h */ +#define ESP_BLE_APPEARANCE_GENERIC_CLOCK 0x0100 /*!< relate to BTM_BLE_APPEARANCE_GENERIC_CLOCK in stack/btm_ble_api.h */ +#define ESP_BLE_APPEARANCE_GENERIC_DISPLAY 0x0140 /*!< relate to BTM_BLE_APPEARANCE_GENERIC_DISPLAY in stack/btm_ble_api.h */ +#define ESP_BLE_APPEARANCE_GENERIC_REMOTE 0x0180 /*!< relate to BTM_BLE_APPEARANCE_GENERIC_REMOTE in stack/btm_ble_api.h */ +#define ESP_BLE_APPEARANCE_GENERIC_EYEGLASSES 0x01C0 /*!< relate to BTM_BLE_APPEARANCE_GENERIC_EYEGLASSES in stack/btm_ble_api.h */ +#define ESP_BLE_APPEARANCE_GENERIC_TAG 0x0200 /*!< relate to BTM_BLE_APPEARANCE_GENERIC_TAG in stack/btm_ble_api.h */ +#define ESP_BLE_APPEARANCE_GENERIC_KEYRING 0x0240 /*!< relate to BTM_BLE_APPEARANCE_GENERIC_KEYRING in stack/btm_ble_api.h */ +#define ESP_BLE_APPEARANCE_GENERIC_MEDIA_PLAYER 0x0280 /*!< relate to BTM_BLE_APPEARANCE_GENERIC_MEDIA_PLAYER in stack/btm_ble_api.h */ +#define ESP_BLE_APPEARANCE_GENERIC_BARCODE_SCANNER 0x02C0 /*!< relate to BTM_BLE_APPEARANCE_GENERIC_BARCODE_SCANNER in stack/btm_ble_api.h */ +#define ESP_BLE_APPEARANCE_GENERIC_THERMOMETER 0x0300 /*!< relate to BTM_BLE_APPEARANCE_GENERIC_THERMOMETER in stack/btm_ble_api.h */ +#define ESP_BLE_APPEARANCE_THERMOMETER_EAR 0x0301 /*!< relate to BTM_BLE_APPEARANCE_THERMOMETER_EAR in stack/btm_ble_api.h */ +#define ESP_BLE_APPEARANCE_GENERIC_HEART_RATE 0x0340 /*!< relate to BTM_BLE_APPEARANCE_GENERIC_HEART_RATE in stack/btm_ble_api.h */ +#define ESP_BLE_APPEARANCE_HEART_RATE_BELT 0x0341 /*!< relate to BTM_BLE_APPEARANCE_HEART_RATE_BELT in stack/btm_ble_api.h */ +#define ESP_BLE_APPEARANCE_GENERIC_BLOOD_PRESSURE 0x0380 /*!< relate to BTM_BLE_APPEARANCE_GENERIC_BLOOD_PRESSURE in stack/btm_ble_api.h */ +#define ESP_BLE_APPEARANCE_BLOOD_PRESSURE_ARM 0x0381 /*!< relate to BTM_BLE_APPEARANCE_BLOOD_PRESSURE_ARM in stack/btm_ble_api.h */ +#define ESP_BLE_APPEARANCE_BLOOD_PRESSURE_WRIST 0x0382 /*!< relate to BTM_BLE_APPEARANCE_BLOOD_PRESSURE_WRIST in stack/btm_ble_api.h */ +#define ESP_BLE_APPEARANCE_GENERIC_HID 0x03C0 /*!< relate to BTM_BLE_APPEARANCE_GENERIC_HID in stack/btm_ble_api.h */ +#define ESP_BLE_APPEARANCE_HID_KEYBOARD 0x03C1 /*!< relate to BTM_BLE_APPEARANCE_HID_KEYBOARD in stack/btm_ble_api.h */ +#define ESP_BLE_APPEARANCE_HID_MOUSE 0x03C2 /*!< relate to BTM_BLE_APPEARANCE_HID_MOUSE in stack/btm_ble_api.h */ +#define ESP_BLE_APPEARANCE_HID_JOYSTICK 0x03C3 /*!< relate to BTM_BLE_APPEARANCE_HID_JOYSTICK in stack/btm_ble_api.h */ +#define ESP_BLE_APPEARANCE_HID_GAMEPAD 0x03C4 /*!< relate to BTM_BLE_APPEARANCE_HID_GAMEPAD in stack/btm_ble_api.h */ +#define ESP_BLE_APPEARANCE_HID_DIGITIZER_TABLET 0x03C5 /*!< relate to BTM_BLE_APPEARANCE_HID_DIGITIZER_TABLET in stack/btm_ble_api.h */ +#define ESP_BLE_APPEARANCE_HID_CARD_READER 0x03C6 /*!< relate to BTM_BLE_APPEARANCE_HID_CARD_READER in stack/btm_ble_api.h */ +#define ESP_BLE_APPEARANCE_HID_DIGITAL_PEN 0x03C7 /*!< relate to BTM_BLE_APPEARANCE_HID_DIGITAL_PEN in stack/btm_ble_api.h */ +#define ESP_BLE_APPEARANCE_HID_BARCODE_SCANNER 0x03C8 /*!< relate to BTM_BLE_APPEARANCE_HID_BARCODE_SCANNER in stack/btm_ble_api.h */ +#define ESP_BLE_APPEARANCE_GENERIC_GLUCOSE 0x0400 /*!< relate to BTM_BLE_APPEARANCE_GENERIC_GLUCOSE in stack/btm_ble_api.h */ +#define ESP_BLE_APPEARANCE_GENERIC_WALKING 0x0440 /*!< relate to BTM_BLE_APPEARANCE_GENERIC_WALKING in stack/btm_ble_api.h */ +#define ESP_BLE_APPEARANCE_WALKING_IN_SHOE 0x0441 /*!< relate to BTM_BLE_APPEARANCE_WALKING_IN_SHOE in stack/btm_ble_api.h */ +#define ESP_BLE_APPEARANCE_WALKING_ON_SHOE 0x0442 /*!< relate to BTM_BLE_APPEARANCE_WALKING_ON_SHOE in stack/btm_ble_api.h */ +#define ESP_BLE_APPEARANCE_WALKING_ON_HIP 0x0443 /*!< relate to BTM_BLE_APPEARANCE_WALKING_ON_HIP in stack/btm_ble_api.h */ +#define ESP_BLE_APPEARANCE_GENERIC_CYCLING 0x0480 /*!< relate to BTM_BLE_APPEARANCE_GENERIC_CYCLING in stack/btm_ble_api.h */ +#define ESP_BLE_APPEARANCE_CYCLING_COMPUTER 0x0481 /*!< relate to BTM_BLE_APPEARANCE_CYCLING_COMPUTER in stack/btm_ble_api.h */ +#define ESP_BLE_APPEARANCE_CYCLING_SPEED 0x0482 /*!< relate to BTM_BLE_APPEARANCE_CYCLING_SPEED in stack/btm_ble_api.h */ +#define ESP_BLE_APPEARANCE_CYCLING_CADENCE 0x0483 /*!< relate to BTM_BLE_APPEARANCE_CYCLING_CADENCE in stack/btm_ble_api.h */ +#define ESP_BLE_APPEARANCE_CYCLING_POWER 0x0484 /*!< relate to BTM_BLE_APPEARANCE_CYCLING_POWER in stack/btm_ble_api.h */ +#define ESP_BLE_APPEARANCE_CYCLING_SPEED_CADENCE 0x0485 /*!< relate to BTM_BLE_APPEARANCE_CYCLING_SPEED_CADENCE in stack/btm_ble_api.h */ +#define ESP_BLE_APPEARANCE_GENERIC_PULSE_OXIMETER 0x0C40 /*!< relate to BTM_BLE_APPEARANCE_GENERIC_PULSE_OXIMETER in stack/btm_ble_api.h */ +#define ESP_BLE_APPEARANCE_PULSE_OXIMETER_FINGERTIP 0x0C41 /*!< relate to BTM_BLE_APPEARANCE_PULSE_OXIMETER_FINGERTIP in stack/btm_ble_api.h */ +#define ESP_BLE_APPEARANCE_PULSE_OXIMETER_WRIST 0x0C42 /*!< relate to BTM_BLE_APPEARANCE_PULSE_OXIMETER_WRIST in stack/btm_ble_api.h */ +#define ESP_BLE_APPEARANCE_GENERIC_WEIGHT 0x0C80 /*!< relate to BTM_BLE_APPEARANCE_GENERIC_WEIGHT in stack/btm_ble_api.h */ +#define ESP_BLE_APPEARANCE_GENERIC_PERSONAL_MOBILITY_DEVICE 0x0CC0 /*!< relate to BTM_BLE_APPEARANCE_GENERIC_PERSONAL_MOBILITY_DEVICE in stack/btm_ble_api.h */ +#define ESP_BLE_APPEARANCE_POWERED_WHEELCHAIR 0x0CC1 /*!< relate to BTM_BLE_APPEARANCE_POWERED_WHEELCHAIR in stack/btm_ble_api.h */ +#define ESP_BLE_APPEARANCE_MOBILITY_SCOOTER 0x0CC2 /*!< relate to BTM_BLE_APPEARANCE_MOBILITY_SCOOTER in stack/btm_ble_api.h */ +#define ESP_BLE_APPEARANCE_GENERIC_CONTINUOUS_GLUCOSE_MONITOR 0x0D00 /*!< relate to BTM_BLE_APPEARANCE_GENERIC_CONTINUOUS_GLUCOSE_MONITOR in stack/btm_ble_api.h */ +#define ESP_BLE_APPEARANCE_GENERIC_INSULIN_PUMP 0x0D40 /*!< relate to BTM_BLE_APPEARANCE_GENERIC_INSULIN_PUMP in stack/btm_ble_api.h */ +#define ESP_BLE_APPEARANCE_INSULIN_PUMP_DURABLE_PUMP 0x0D41 /*!< relate to BTM_BLE_APPEARANCE_INSULIN_PUMP_DURABLE_PUMP in stack/btm_ble_api.h */ +#define ESP_BLE_APPEARANCE_INSULIN_PUMP_PATCH_PUMP 0x0D44 /*!< relate to BTM_BLE_APPEARANCE_INSULIN_PUMP_PATCH_PUMP in stack/btm_ble_api.h */ +#define ESP_BLE_APPEARANCE_INSULIN_PEN 0x0D48 /*!< relate to BTM_BLE_APPEARANCE_INSULIN_PEN in stack/btm_ble_api.h */ +#define ESP_BLE_APPEARANCE_GENERIC_MEDICATION_DELIVERY 0x0D80 /*!< relate to BTM_BLE_APPEARANCE_GENERIC_MEDICATION_DELIVERY in stack/btm_ble_api.h */ +#define ESP_BLE_APPEARANCE_GENERIC_OUTDOOR_SPORTS 0x1440 /*!< relate to BTM_BLE_APPEARANCE_GENERIC_OUTDOOR_SPORTS in stack/btm_ble_api.h */ +#define ESP_BLE_APPEARANCE_OUTDOOR_SPORTS_LOCATION 0x1441 /*!< relate to BTM_BLE_APPEARANCE_OUTDOOR_SPORTS_LOCATION in stack/btm_ble_api.h */ +#define ESP_BLE_APPEARANCE_OUTDOOR_SPORTS_LOCATION_AND_NAV 0x1442 /*!< relate to BTM_BLE_APPEARANCE_OUTDOOR_SPORTS_LOCATION_AND_NAV in stack/btm_ble_api.h */ +#define ESP_BLE_APPEARANCE_OUTDOOR_SPORTS_LOCATION_POD 0x1443 /*!< relate to BTM_BLE_APPEARANCE_OUTDOOR_SPORTS_LOCATION_POD in stack/btm_ble_api.h */ +#define ESP_BLE_APPEARANCE_OUTDOOR_SPORTS_LOCATION_POD_AND_NAV 0x1444 /*!< relate to BTM_BLE_APPEARANCE_OUTDOOR_SPORTS_LOCATION_POD_AND_NAV in stack/btm_ble_api.h */ + +typedef uint8_t esp_ble_io_cap_t; /*!< combination of the io capability */ + +#define BLE_DTM_PKT_PAYLOAD_0x00 0x00 /*!< PRBS9 sequence ‘11111111100000111101...’ (in transmission order) as described in [Vol 6] Part F, Section 4.1.5 */ +#define BLE_DTM_PKT_PAYLOAD_0x01 0x01 /*!< Repeated ‘11110000’ (in transmission order) sequence as described in [Vol 6] Part F, Section 4.1.5 */ +#define BLE_DTM_PKT_PAYLOAD_0x02 0x02 /*!< Repeated ‘10101010’ (in transmission order) sequence as described in [Vol 6] Part F, Section 4.1.5 */ +#define BLE_DTM_PKT_PAYLOAD_0x03 0x03 /*!< PRBS15 sequence as described in [Vol 6] Part F, Section 4.1.5 */ +#define BLE_DTM_PKT_PAYLOAD_0x04 0x04 /*!< Repeated ‘11111111’ (in transmission order) sequence */ +#define BLE_DTM_PKT_PAYLOAD_0x05 0x05 /*!< Repeated ‘00000000’ (in transmission order) sequence */ +#define BLE_DTM_PKT_PAYLOAD_0x06 0x06 /*!< Repeated ‘00001111’ (in transmission order) sequence */ +#define BLE_DTM_PKT_PAYLOAD_0x07 0x07 /*!< Repeated ‘01010101’ (in transmission order) sequence */ +#define BLE_DTM_PKT_PAYLOAD_MAX 0x08 /*!< 0x08 ~ 0xFF, Reserved for future use */ + +typedef uint8_t esp_ble_dtm_pkt_payload_t; + +/// GAP BLE callback event type +typedef enum { + //BLE_42_FEATURE_SUPPORT + ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT = 0, /*!< When advertising data set complete, the event comes */ + ESP_GAP_BLE_SCAN_RSP_DATA_SET_COMPLETE_EVT, /*!< When scan response data set complete, the event comes */ + ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT, /*!< When scan parameters set complete, the event comes */ + ESP_GAP_BLE_SCAN_RESULT_EVT, /*!< When one scan result ready, the event comes each time */ + ESP_GAP_BLE_ADV_DATA_RAW_SET_COMPLETE_EVT, /*!< When raw advertising data set complete, the event comes */ + ESP_GAP_BLE_SCAN_RSP_DATA_RAW_SET_COMPLETE_EVT, /*!< When raw scan response data set complete, the event comes */ + ESP_GAP_BLE_ADV_START_COMPLETE_EVT, /*!< When start advertising complete, the event comes */ + ESP_GAP_BLE_SCAN_START_COMPLETE_EVT, /*!< When start scan complete, the event comes */ + //BLE_INCLUDED + ESP_GAP_BLE_AUTH_CMPL_EVT = 8, /*!< Authentication complete indication. */ + ESP_GAP_BLE_KEY_EVT, /*!< BLE key event for peer device keys */ + ESP_GAP_BLE_SEC_REQ_EVT, /*!< BLE security request */ + ESP_GAP_BLE_PASSKEY_NOTIF_EVT, /*!< passkey notification event */ + ESP_GAP_BLE_PASSKEY_REQ_EVT, /*!< passkey request event */ + ESP_GAP_BLE_OOB_REQ_EVT, /*!< OOB request event */ + ESP_GAP_BLE_LOCAL_IR_EVT, /*!< BLE local IR (identity Root 128-bit random static value used to generate Long Term Key) event */ + ESP_GAP_BLE_LOCAL_ER_EVT, /*!< BLE local ER (Encryption Root vakue used to genrate identity resolving key) event */ + ESP_GAP_BLE_NC_REQ_EVT, /*!< Numeric Comparison request event */ + //BLE_42_FEATURE_SUPPORT + ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT, /*!< When stop adv complete, the event comes */ + ESP_GAP_BLE_SCAN_STOP_COMPLETE_EVT, /*!< When stop scan complete, the event comes */ + //BLE_INCLUDED + ESP_GAP_BLE_SET_STATIC_RAND_ADDR_EVT = 19, /*!< When set the static rand address complete, the event comes */ + ESP_GAP_BLE_UPDATE_CONN_PARAMS_EVT, /*!< When update connection parameters complete, the event comes */ + ESP_GAP_BLE_SET_PKT_LENGTH_COMPLETE_EVT, /*!< When set pkt length complete, the event comes */ + ESP_GAP_BLE_SET_LOCAL_PRIVACY_COMPLETE_EVT, /*!< When Enable/disable privacy on the local device complete, the event comes */ + ESP_GAP_BLE_REMOVE_BOND_DEV_COMPLETE_EVT, /*!< When remove the bond device complete, the event comes */ + ESP_GAP_BLE_CLEAR_BOND_DEV_COMPLETE_EVT, /*!< When clear the bond device clear complete, the event comes */ + ESP_GAP_BLE_GET_BOND_DEV_COMPLETE_EVT, /*!< When get the bond device list complete, the event comes */ + ESP_GAP_BLE_READ_RSSI_COMPLETE_EVT, /*!< When read the rssi complete, the event comes */ + ESP_GAP_BLE_UPDATE_WHITELIST_COMPLETE_EVT, /*!< When add or remove whitelist complete, the event comes */ + //BLE_42_FEATURE_SUPPORT + ESP_GAP_BLE_UPDATE_DUPLICATE_EXCEPTIONAL_LIST_COMPLETE_EVT, /*!< When update duplicate exceptional list complete, the event comes */ + //BLE_INCLUDED + ESP_GAP_BLE_SET_CHANNELS_EVT = 29, /*!< When setting BLE channels complete, the event comes */ + //BLE_50_FEATURE_SUPPORT + ESP_GAP_BLE_READ_PHY_COMPLETE_EVT, /*!< when reading phy complete, this event comes */ + ESP_GAP_BLE_SET_PREFERRED_DEFAULT_PHY_COMPLETE_EVT, /*!< when preferred default phy complete, this event comes */ + ESP_GAP_BLE_SET_PREFERRED_PHY_COMPLETE_EVT, /*!< when preferred phy complete , this event comes */ + ESP_GAP_BLE_EXT_ADV_SET_RAND_ADDR_COMPLETE_EVT, /*!< when extended set random address complete, the event comes */ + ESP_GAP_BLE_EXT_ADV_SET_PARAMS_COMPLETE_EVT, /*!< when extended advertising parameter complete, the event comes */ + ESP_GAP_BLE_EXT_ADV_DATA_SET_COMPLETE_EVT, /*!< when extended advertising data complete, the event comes */ + ESP_GAP_BLE_EXT_SCAN_RSP_DATA_SET_COMPLETE_EVT, /*!< when extended scan response data complete, the event comes */ + ESP_GAP_BLE_EXT_ADV_START_COMPLETE_EVT, /*!< when extended advertising start complete, the event comes */ + ESP_GAP_BLE_EXT_ADV_STOP_COMPLETE_EVT, /*!< when extended advertising stop complete, the event comes */ + ESP_GAP_BLE_EXT_ADV_SET_REMOVE_COMPLETE_EVT, /*!< when extended advertising set remove complete, the event comes */ + ESP_GAP_BLE_EXT_ADV_SET_CLEAR_COMPLETE_EVT, /*!< when extended advertising set clear complete, the event comes */ + ESP_GAP_BLE_PERIODIC_ADV_SET_PARAMS_COMPLETE_EVT, /*!< when periodic advertising parameter complete, the event comes */ + ESP_GAP_BLE_PERIODIC_ADV_DATA_SET_COMPLETE_EVT, /*!< when periodic advertising data complete, the event comes */ + ESP_GAP_BLE_PERIODIC_ADV_START_COMPLETE_EVT, /*!< when periodic advertising start complete, the event comes */ + ESP_GAP_BLE_PERIODIC_ADV_STOP_COMPLETE_EVT, /*!< when periodic advertising stop complete, the event comes */ + ESP_GAP_BLE_PERIODIC_ADV_CREATE_SYNC_COMPLETE_EVT, /*!< when periodic advertising create sync complete, the event comes */ + ESP_GAP_BLE_PERIODIC_ADV_SYNC_CANCEL_COMPLETE_EVT, /*!< when extended advertising sync cancel complete, the event comes */ + ESP_GAP_BLE_PERIODIC_ADV_SYNC_TERMINATE_COMPLETE_EVT, /*!< when extended advertising sync terminate complete, the event comes */ + ESP_GAP_BLE_PERIODIC_ADV_ADD_DEV_COMPLETE_EVT, /*!< when extended advertising add device complete , the event comes */ + ESP_GAP_BLE_PERIODIC_ADV_REMOVE_DEV_COMPLETE_EVT, /*!< when extended advertising remove device complete, the event comes */ + ESP_GAP_BLE_PERIODIC_ADV_CLEAR_DEV_COMPLETE_EVT, /*!< when extended advertising clear device, the event comes */ + ESP_GAP_BLE_SET_EXT_SCAN_PARAMS_COMPLETE_EVT, /*!< when extended scan parameter complete, the event comes */ + ESP_GAP_BLE_EXT_SCAN_START_COMPLETE_EVT, /*!< when extended scan start complete, the event comes */ + ESP_GAP_BLE_EXT_SCAN_STOP_COMPLETE_EVT, /*!< when extended scan stop complete, the event comes */ + ESP_GAP_BLE_PREFER_EXT_CONN_PARAMS_SET_COMPLETE_EVT, /*!< when extended prefer connection parameter set complete, the event comes */ + ESP_GAP_BLE_PHY_UPDATE_COMPLETE_EVT, /*!< when ble phy update complete, the event comes */ + ESP_GAP_BLE_EXT_ADV_REPORT_EVT, /*!< when extended advertising report complete, the event comes */ + ESP_GAP_BLE_SCAN_TIMEOUT_EVT, /*!< when scan timeout complete, the event comes */ + ESP_GAP_BLE_ADV_TERMINATED_EVT, /*!< when advertising terminate data complete, the event comes */ + ESP_GAP_BLE_SCAN_REQ_RECEIVED_EVT, /*!< when scan req received complete, the event comes */ + ESP_GAP_BLE_CHANNEL_SELECT_ALGORITHM_EVT, /*!< when channel select algorithm complete, the event comes */ + ESP_GAP_BLE_PERIODIC_ADV_REPORT_EVT, /*!< when periodic report advertising complete, the event comes */ + ESP_GAP_BLE_PERIODIC_ADV_SYNC_LOST_EVT, /*!< when periodic advertising sync lost complete, the event comes */ + ESP_GAP_BLE_PERIODIC_ADV_SYNC_ESTAB_EVT, /*!< when periodic advertising sync establish complete, the event comes */ + //BLE_INCLUDED + ESP_GAP_BLE_SC_OOB_REQ_EVT, /*!< Secure Connection OOB request event */ + ESP_GAP_BLE_SC_CR_LOC_OOB_EVT, /*!< Secure Connection create OOB data complete event */ + ESP_GAP_BLE_GET_DEV_NAME_COMPLETE_EVT, /*!< When getting BT device name complete, the event comes */ + //BLE_FEAT_PERIODIC_ADV_SYNC_TRANSFER + ESP_GAP_BLE_PERIODIC_ADV_RECV_ENABLE_COMPLETE_EVT, /*!< when set periodic advertising receive enable complete, the event comes */ + ESP_GAP_BLE_PERIODIC_ADV_SYNC_TRANS_COMPLETE_EVT, /*!< when periodic advertising sync transfer complete, the event comes */ + ESP_GAP_BLE_PERIODIC_ADV_SET_INFO_TRANS_COMPLETE_EVT, /*!< when periodic advertising set info transfer complete, the event comes */ + ESP_GAP_BLE_SET_PAST_PARAMS_COMPLETE_EVT, /*!< when set periodic advertising sync transfer params complete, the event comes */ + ESP_GAP_BLE_PERIODIC_ADV_SYNC_TRANS_RECV_EVT, /*!< when periodic advertising sync transfer received, the event comes */ + // DTM + ESP_GAP_BLE_DTM_TEST_UPDATE_EVT, /*!< when direct test mode state changes, the event comes */ + // BLE_INCLUDED + ESP_GAP_BLE_ADV_CLEAR_COMPLETE_EVT, /*!< When clear advertising complete, the event comes */ + ESP_GAP_BLE_EVT_MAX, /*!< when maximum advertising event complete, the event comes */ +} esp_gap_ble_cb_event_t; + +#define ESP_GAP_BLE_CHANNELS_LEN 5 /*!< channel length*/ +typedef uint8_t esp_gap_ble_channels[ESP_GAP_BLE_CHANNELS_LEN]; + +/// This is the old name, just for backwards compatibility +#define ESP_GAP_BLE_ADD_WHITELIST_COMPLETE_EVT ESP_GAP_BLE_UPDATE_WHITELIST_COMPLETE_EVT + +/// Advertising data maximum length +#define ESP_BLE_ADV_DATA_LEN_MAX 31 +/// Scan response data maximum length +#define ESP_BLE_SCAN_RSP_DATA_LEN_MAX 31 + +/* relate to BTM_BLE_AD_TYPE_xxx in stack/btm_ble_api.h */ +/// The type of advertising data(not adv_type) +typedef enum { + ESP_BLE_AD_TYPE_FLAG = 0x01, /* relate to BTM_BLE_AD_TYPE_FLAG in stack/btm_ble_api.h */ + ESP_BLE_AD_TYPE_16SRV_PART = 0x02, /* relate to BTM_BLE_AD_TYPE_16SRV_PART in stack/btm_ble_api.h */ + ESP_BLE_AD_TYPE_16SRV_CMPL = 0x03, /* relate to BTM_BLE_AD_TYPE_16SRV_CMPL in stack/btm_ble_api.h */ + ESP_BLE_AD_TYPE_32SRV_PART = 0x04, /* relate to BTM_BLE_AD_TYPE_32SRV_PART in stack/btm_ble_api.h */ + ESP_BLE_AD_TYPE_32SRV_CMPL = 0x05, /* relate to BTM_BLE_AD_TYPE_32SRV_CMPL in stack/btm_ble_api.h */ + ESP_BLE_AD_TYPE_128SRV_PART = 0x06, /* relate to BTM_BLE_AD_TYPE_128SRV_PART in stack/btm_ble_api.h */ + ESP_BLE_AD_TYPE_128SRV_CMPL = 0x07, /* relate to BTM_BLE_AD_TYPE_128SRV_CMPL in stack/btm_ble_api.h */ + ESP_BLE_AD_TYPE_NAME_SHORT = 0x08, /* relate to BTM_BLE_AD_TYPE_NAME_SHORT in stack/btm_ble_api.h */ + ESP_BLE_AD_TYPE_NAME_CMPL = 0x09, /* relate to BTM_BLE_AD_TYPE_NAME_CMPL in stack/btm_ble_api.h */ + ESP_BLE_AD_TYPE_TX_PWR = 0x0A, /* relate to BTM_BLE_AD_TYPE_TX_PWR in stack/btm_ble_api.h */ + ESP_BLE_AD_TYPE_DEV_CLASS = 0x0D, /* relate to BTM_BLE_AD_TYPE_DEV_CLASS in stack/btm_ble_api.h */ + ESP_BLE_AD_TYPE_SM_TK = 0x10, /* relate to BTM_BLE_AD_TYPE_SM_TK in stack/btm_ble_api.h */ + ESP_BLE_AD_TYPE_SM_OOB_FLAG = 0x11, /* relate to BTM_BLE_AD_TYPE_SM_OOB_FLAG in stack/btm_ble_api.h */ + ESP_BLE_AD_TYPE_INT_RANGE = 0x12, /* relate to BTM_BLE_AD_TYPE_INT_RANGE in stack/btm_ble_api.h */ + ESP_BLE_AD_TYPE_SOL_SRV_UUID = 0x14, /* relate to BTM_BLE_AD_TYPE_SOL_SRV_UUID in stack/btm_ble_api.h */ + ESP_BLE_AD_TYPE_128SOL_SRV_UUID = 0x15, /* relate to BTM_BLE_AD_TYPE_128SOL_SRV_UUID in stack/btm_ble_api.h */ + ESP_BLE_AD_TYPE_SERVICE_DATA = 0x16, /* relate to BTM_BLE_AD_TYPE_SERVICE_DATA in stack/btm_ble_api.h */ + ESP_BLE_AD_TYPE_PUBLIC_TARGET = 0x17, /* relate to BTM_BLE_AD_TYPE_PUBLIC_TARGET in stack/btm_ble_api.h */ + ESP_BLE_AD_TYPE_RANDOM_TARGET = 0x18, /* relate to BTM_BLE_AD_TYPE_RANDOM_TARGET in stack/btm_ble_api.h */ + ESP_BLE_AD_TYPE_APPEARANCE = 0x19, /* relate to BTM_BLE_AD_TYPE_APPEARANCE in stack/btm_ble_api.h */ + ESP_BLE_AD_TYPE_ADV_INT = 0x1A, /* relate to BTM_BLE_AD_TYPE_ADV_INT in stack/btm_ble_api.h */ + ESP_BLE_AD_TYPE_LE_DEV_ADDR = 0x1b, /* relate to BTM_BLE_AD_TYPE_LE_DEV_ADDR in stack/btm_ble_api.h */ + ESP_BLE_AD_TYPE_LE_ROLE = 0x1c, /* relate to BTM_BLE_AD_TYPE_LE_ROLE in stack/btm_ble_api.h */ + ESP_BLE_AD_TYPE_SPAIR_C256 = 0x1d, /* relate to BTM_BLE_AD_TYPE_SPAIR_C256 in stack/btm_ble_api.h */ + ESP_BLE_AD_TYPE_SPAIR_R256 = 0x1e, /* relate to BTM_BLE_AD_TYPE_SPAIR_R256 in stack/btm_ble_api.h */ + ESP_BLE_AD_TYPE_32SOL_SRV_UUID = 0x1f, /* relate to BTM_BLE_AD_TYPE_32SOL_SRV_UUID in stack/btm_ble_api.h */ + ESP_BLE_AD_TYPE_32SERVICE_DATA = 0x20, /* relate to BTM_BLE_AD_TYPE_32SERVICE_DATA in stack/btm_ble_api.h */ + ESP_BLE_AD_TYPE_128SERVICE_DATA = 0x21, /* relate to BTM_BLE_AD_TYPE_128SERVICE_DATA in stack/btm_ble_api.h */ + ESP_BLE_AD_TYPE_LE_SECURE_CONFIRM = 0x22, /* relate to BTM_BLE_AD_TYPE_LE_SECURE_CONFIRM in stack/btm_ble_api.h */ + ESP_BLE_AD_TYPE_LE_SECURE_RANDOM = 0x23, /* relate to BTM_BLE_AD_TYPE_LE_SECURE_RANDOM in stack/btm_ble_api.h */ + ESP_BLE_AD_TYPE_URI = 0x24, /* relate to BTM_BLE_AD_TYPE_URI in stack/btm_ble_api.h */ + ESP_BLE_AD_TYPE_INDOOR_POSITION = 0x25, /* relate to BTM_BLE_AD_TYPE_INDOOR_POSITION in stack/btm_ble_api.h */ + ESP_BLE_AD_TYPE_TRANS_DISC_DATA = 0x26, /* relate to BTM_BLE_AD_TYPE_TRANS_DISC_DATA in stack/btm_ble_api.h */ + ESP_BLE_AD_TYPE_LE_SUPPORT_FEATURE = 0x27, /* relate to BTM_BLE_AD_TYPE_LE_SUPPORT_FEATURE in stack/btm_ble_api.h */ + ESP_BLE_AD_TYPE_CHAN_MAP_UPDATE = 0x28, /* relate to BTM_BLE_AD_TYPE_CHAN_MAP_UPDATE in stack/btm_ble_api.h */ + ESP_BLE_AD_MANUFACTURER_SPECIFIC_TYPE = 0xFF, /* relate to BTM_BLE_AD_MANUFACTURER_SPECIFIC_TYPE in stack/btm_ble_api.h */ +} esp_ble_adv_data_type; + +/// Advertising mode +typedef enum { + ADV_TYPE_IND = 0x00, + ADV_TYPE_DIRECT_IND_HIGH = 0x01, + ADV_TYPE_SCAN_IND = 0x02, + ADV_TYPE_NONCONN_IND = 0x03, + ADV_TYPE_DIRECT_IND_LOW = 0x04, +} esp_ble_adv_type_t; + +/// Advertising channel mask +typedef enum { + ADV_CHNL_37 = 0x01, + ADV_CHNL_38 = 0x02, + ADV_CHNL_39 = 0x04, + ADV_CHNL_ALL = 0x07, +} esp_ble_adv_channel_t; + +typedef enum { + ///Allow both scan and connection requests from anyone + ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY = 0x00, + ///Allow both scan req from White List devices only and connection req from anyone + ADV_FILTER_ALLOW_SCAN_WLST_CON_ANY, + ///Allow both scan req from anyone and connection req from White List devices only + ADV_FILTER_ALLOW_SCAN_ANY_CON_WLST, + ///Allow scan and connection requests from White List devices only + ADV_FILTER_ALLOW_SCAN_WLST_CON_WLST, + ///Enumeration end value for advertising filter policy value check +} esp_ble_adv_filter_t; + + +/* relate to BTA_DM_BLE_SEC_xxx in bta/bta_api.h */ +typedef enum { + ESP_BLE_SEC_ENCRYPT = 1, /*!< relate to BTA_DM_BLE_SEC_ENCRYPT in bta/bta_api.h. If the device has already + bonded, the stack will used Long Term Key (LTK) to encrypt with the remote device directly. + Else if the device hasn't bonded, the stack will used the default authentication request + used the esp_ble_gap_set_security_param function set by the user. */ + ESP_BLE_SEC_ENCRYPT_NO_MITM, /*!< relate to BTA_DM_BLE_SEC_ENCRYPT_NO_MITM in bta/bta_api.h. If the device has been already + bonded, the stack will check the LTK (Long Term Key) Whether the authentication request has been met, and if met, use the LTK + to encrypt with the remote device directly, else re-pair with the remote device. + Else if the device hasn't been bonded, the stack will use NO MITM authentication request in the current link instead of + using the authreq in the esp_ble_gap_set_security_param function set by the user. */ + ESP_BLE_SEC_ENCRYPT_MITM, /*!< relate to BTA_DM_BLE_SEC_ENCRYPT_MITM in bta/bta_api.h. If the device has been already + bonded, the stack will check the LTK (Long Term Key) whether the authentication request has been met, and if met, use the LTK + to encrypt with the remote device directly, else re-pair with the remote device. + Else if the device hasn't been bonded, the stack will use MITM authentication request in the current link instead of + using the authreq in the esp_ble_gap_set_security_param function set by the user. */ +}esp_ble_sec_act_t; + +typedef enum { + ESP_BLE_SM_PASSKEY = 0, + /*!< Authentication requirements of local device */ + ESP_BLE_SM_AUTHEN_REQ_MODE, + /*!< The IO capability of local device */ + ESP_BLE_SM_IOCAP_MODE, + /*!< Initiator Key Distribution/Generation */ + ESP_BLE_SM_SET_INIT_KEY, + /*!< Responder Key Distribution/Generation */ + ESP_BLE_SM_SET_RSP_KEY, + /*!< Maximum Encryption key size to support */ + ESP_BLE_SM_MAX_KEY_SIZE, + /*!< Minimum Encryption key size requirement from Peer */ + ESP_BLE_SM_MIN_KEY_SIZE, + /*!< Set static Passkey */ + ESP_BLE_SM_SET_STATIC_PASSKEY, + /*!< Reset static Passkey */ + ESP_BLE_SM_CLEAR_STATIC_PASSKEY, + /*!< Accept only specified SMP Authentication requirement */ + ESP_BLE_SM_ONLY_ACCEPT_SPECIFIED_SEC_AUTH, + /*!< Enable/Disable OOB support */ + ESP_BLE_SM_OOB_SUPPORT, + /*!< Appl encryption key size */ + ESP_BLE_APP_ENC_KEY_SIZE, + /*!< authentication max param */ + ESP_BLE_SM_MAX_PARAM, +} esp_ble_sm_param_t; + +typedef enum { + /// DTM TX start event + DTM_TX_START_EVT = 0x00, + ///DTM RX start event + DTM_RX_START_EVT, + ///DTM test end event + DTM_TEST_STOP_EVT, +} esp_ble_dtm_update_evt_t; + +#if (BLE_42_FEATURE_SUPPORT == TRUE) +/** +* @brief DTM TX parameters +*/ +typedef struct +{ + uint8_t tx_channel; /*!< channel for sending test data, tx_channel = (Frequency -2402)/2, tx_channel range:0x00-0x27, Frequency range: 2402 MHz to 2480 MHz */ + uint8_t len_of_data; /*!< length in bytes of payload data in each packet */ + esp_ble_dtm_pkt_payload_t pkt_payload; /*!< packet payload type. value range: 0x00-0x07 */ +} esp_ble_dtm_tx_t; +/** +* @brief DTM RX parameters +*/ +typedef struct +{ + uint8_t rx_channel; /*!< channel for test data reception, rx_channel = (Frequency -2402)/2, tx_channel range:0x00-0x27, Frequency range: 2402 MHz to 2480 MHz */ +} esp_ble_dtm_rx_t; + +/// Advertising parameters +typedef struct { + uint16_t adv_int_min; /*!< Minimum advertising interval for + undirected and low duty cycle directed advertising. + Range: 0x0020 to 0x4000 Default: N = 0x0800 (1.28 second) + Time = N * 0.625 msec Time Range: 20 ms to 10.24 sec */ + uint16_t adv_int_max; /*!< Maximum advertising interval for + undirected and low duty cycle directed advertising. + Range: 0x0020 to 0x4000 Default: N = 0x0800 (1.28 second) + Time = N * 0.625 msec Time Range: 20 ms to 10.24 sec Advertising max interval */ + esp_ble_adv_type_t adv_type; /*!< Advertising type */ + esp_ble_addr_type_t own_addr_type; /*!< Owner bluetooth device address type */ + esp_bd_addr_t peer_addr; /*!< Peer device bluetooth device address */ + esp_ble_addr_type_t peer_addr_type; /*!< Peer device bluetooth device address type, only support public address type and random address type */ + esp_ble_adv_channel_t channel_map; /*!< Advertising channel map */ + esp_ble_adv_filter_t adv_filter_policy; /*!< Advertising filter policy */ +} esp_ble_adv_params_t; + +/// Advertising data content, according to "Supplement to the Bluetooth Core Specification" +typedef struct { + bool set_scan_rsp; /*!< Set this advertising data as scan response or not*/ + bool include_name; /*!< Advertising data include device name or not */ + bool include_txpower; /*!< Advertising data include TX power */ + int min_interval; /*!< Advertising data show slave preferred connection min interval. + The connection interval in the following manner: + connIntervalmin = Conn_Interval_Min * 1.25 ms + Conn_Interval_Min range: 0x0006 to 0x0C80 + Value of 0xFFFF indicates no specific minimum. + Values not defined above are reserved for future use.*/ + + int max_interval; /*!< Advertising data show slave preferred connection max interval. + The connection interval in the following manner: + connIntervalmax = Conn_Interval_Max * 1.25 ms + Conn_Interval_Max range: 0x0006 to 0x0C80 + Conn_Interval_Max shall be equal to or greater than the Conn_Interval_Min. + Value of 0xFFFF indicates no specific maximum. + Values not defined above are reserved for future use.*/ + + int appearance; /*!< External appearance of device */ + uint16_t manufacturer_len; /*!< Manufacturer data length */ + uint8_t *p_manufacturer_data; /*!< Manufacturer data point */ + uint16_t service_data_len; /*!< Service data length */ + uint8_t *p_service_data; /*!< Service data point */ + uint16_t service_uuid_len; /*!< Service uuid length */ + uint8_t *p_service_uuid; /*!< Service uuid array point */ + uint8_t flag; /*!< Advertising flag of discovery mode, see BLE_ADV_DATA_FLAG detail */ +} esp_ble_adv_data_t; + +#endif // #if (BLE_42_FEATURE_SUPPORT == TRUE) + +/// Ble scan type +typedef enum { + BLE_SCAN_TYPE_PASSIVE = 0x0, /*!< Passive scan */ + BLE_SCAN_TYPE_ACTIVE = 0x1, /*!< Active scan */ +} esp_ble_scan_type_t; + +/// Ble scan filter type +typedef enum { + BLE_SCAN_FILTER_ALLOW_ALL = 0x0, /*!< Accept all : + 1. advertisement packets except directed advertising packets not addressed to this device (default). */ + BLE_SCAN_FILTER_ALLOW_ONLY_WLST = 0x1, /*!< Accept only : + 1. advertisement packets from devices where the advertiser’s address is in the White list. + 2. Directed advertising packets which are not addressed for this device shall be ignored. */ + BLE_SCAN_FILTER_ALLOW_UND_RPA_DIR = 0x2, /*!< Accept all : + 1. undirected advertisement packets, and + 2. directed advertising packets where the initiator address is a resolvable private address, and + 3. directed advertising packets addressed to this device. */ + BLE_SCAN_FILTER_ALLOW_WLIST_RPA_DIR = 0x3, /*!< Accept all : + 1. advertisement packets from devices where the advertiser’s address is in the White list, and + 2. directed advertising packets where the initiator address is a resolvable private address, and + 3. directed advertising packets addressed to this device.*/ +} esp_ble_scan_filter_t; + +/// Ble scan duplicate type +typedef enum { + BLE_SCAN_DUPLICATE_DISABLE = 0x0, /*!< the Link Layer should generate advertising reports to the host for each packet received */ + BLE_SCAN_DUPLICATE_ENABLE = 0x1, /*!< the Link Layer should filter out duplicate advertising reports to the Host */ + #if (BLE_50_FEATURE_SUPPORT == TRUE) + BLE_SCAN_DUPLICATE_ENABLE_RESET, /*!< Duplicate filtering enabled, reset for each scan period, only supported in BLE 5.0. */ + #endif + BLE_SCAN_DUPLICATE_MAX /*!< Reserved for future use. */ +} esp_ble_scan_duplicate_t; +#if (BLE_42_FEATURE_SUPPORT == TRUE) +/// Ble scan parameters +typedef struct { + esp_ble_scan_type_t scan_type; /*!< Scan type */ + esp_ble_addr_type_t own_addr_type; /*!< Owner address type */ + esp_ble_scan_filter_t scan_filter_policy; /*!< Scan filter policy */ + uint16_t scan_interval; /*!< Scan interval. This is defined as the time interval from + when the Controller started its last LE scan until it begins the subsequent LE scan. + Range: 0x0004 to 0x4000 Default: 0x0010 (10 ms) + Time = N * 0.625 msec + Time Range: 2.5 msec to 10.24 seconds*/ + uint16_t scan_window; /*!< Scan window. The duration of the LE scan. LE_Scan_Window + shall be less than or equal to LE_Scan_Interval + Range: 0x0004 to 0x4000 Default: 0x0010 (10 ms) + Time = N * 0.625 msec + Time Range: 2.5 msec to 10240 msec */ + esp_ble_scan_duplicate_t scan_duplicate; /*!< The Scan_Duplicates parameter controls whether the Link Layer should filter out + duplicate advertising reports (BLE_SCAN_DUPLICATE_ENABLE) to the Host, or if the Link Layer should generate + advertising reports for each packet received */ +} esp_ble_scan_params_t; +#endif // #if (BLE_42_FEATURE_SUPPORT == TRUE) +/// connection parameters information +typedef struct { + uint16_t interval; /*!< connection interval */ + uint16_t latency; /*!< Slave latency for the connection in number of connection events. Range: 0x0000 to 0x01F3 */ + uint16_t timeout; /*!< Supervision timeout for the LE Link. Range: 0x000A to 0x0C80. + Mandatory Range: 0x000A to 0x0C80 Time = N * 10 msec + Time Range: 100 msec to 32 seconds */ +} esp_gap_conn_params_t; + +/// Connection update parameters +typedef struct { + esp_bd_addr_t bda; /*!< Bluetooth device address */ + uint16_t min_int; /*!< Min connection interval */ + uint16_t max_int; /*!< Max connection interval */ + uint16_t latency; /*!< Slave latency for the connection in number of connection events. Range: 0x0000 to 0x01F3 */ + uint16_t timeout; /*!< Supervision timeout for the LE Link. Range: 0x000A to 0x0C80. + Mandatory Range: 0x000A to 0x0C80 Time = N * 10 msec + Time Range: 100 msec to 32 seconds */ +} esp_ble_conn_update_params_t; + +/** +* @brief BLE pkt date length keys +*/ +typedef struct +{ + uint16_t rx_len; /*!< pkt rx data length value */ + uint16_t tx_len; /*!< pkt tx data length value */ +} esp_ble_pkt_data_length_params_t; + +/** +* @brief BLE encryption keys +*/ +typedef struct +{ + esp_bt_octet16_t ltk; /*!< The long term key*/ + esp_bt_octet8_t rand; /*!< The random number*/ + uint16_t ediv; /*!< The ediv value*/ + uint8_t sec_level; /*!< The security level of the security link*/ + uint8_t key_size; /*!< The key size(7~16) of the security link*/ +} esp_ble_penc_keys_t; /*!< The key type*/ + +/** +* @brief BLE CSRK keys +*/ +typedef struct +{ + uint32_t counter; /*!< The counter */ + esp_bt_octet16_t csrk; /*!< The csrk key */ + uint8_t sec_level; /*!< The security level */ +} esp_ble_pcsrk_keys_t; /*!< The pcsrk key type */ + +/** +* @brief BLE pid keys +*/ +typedef struct +{ + esp_bt_octet16_t irk; /*!< The irk value */ + esp_ble_addr_type_t addr_type; /*!< The address type */ + esp_bd_addr_t static_addr; /*!< The static address */ +} esp_ble_pid_keys_t; /*!< The pid key type */ + +/** +* @brief BLE Encryption reproduction keys +*/ +typedef struct +{ + esp_bt_octet16_t ltk; /*!< The long term key */ + uint16_t div; /*!< The div value */ + uint8_t key_size; /*!< The key size of the security link */ + uint8_t sec_level; /*!< The security level of the security link */ +} esp_ble_lenc_keys_t; /*!< The key type */ + +/** +* @brief BLE SRK keys +*/ +typedef struct +{ + uint32_t counter; /*!< The counter value */ + uint16_t div; /*!< The div value */ + uint8_t sec_level; /*!< The security level of the security link */ + esp_bt_octet16_t csrk; /*!< The csrk key value */ +} esp_ble_lcsrk_keys; /*!< The csrk key type */ + +/** +* @brief Structure associated with ESP_KEY_NOTIF_EVT +*/ +typedef struct +{ + esp_bd_addr_t bd_addr; /*!< peer address */ + uint32_t passkey; /*!< the numeric value for comparison. If just_works, do not show this number to UI */ +} esp_ble_sec_key_notif_t; /*!< BLE key notify type*/ + +/** +* @brief Structure of the security request +*/ +typedef struct +{ + esp_bd_addr_t bd_addr; /*!< peer address */ +} esp_ble_sec_req_t; /*!< BLE security request type*/ + +/** +* @brief union type of the security key value +*/ +typedef union +{ + esp_ble_penc_keys_t penc_key; /*!< received peer encryption key */ + esp_ble_pcsrk_keys_t pcsrk_key; /*!< received peer device SRK */ + esp_ble_pid_keys_t pid_key; /*!< peer device ID key */ + esp_ble_lenc_keys_t lenc_key; /*!< local encryption reproduction keys LTK = = d1(ER,DIV,0)*/ + esp_ble_lcsrk_keys lcsrk_key; /*!< local device CSRK = d1(ER,DIV,1)*/ +} esp_ble_key_value_t; /*!< ble key value type*/ + +/** +* @brief struct type of the bond key information value +*/ +typedef struct +{ + esp_ble_key_mask_t key_mask; /*!< the key mask to indicate witch key is present */ + esp_ble_penc_keys_t penc_key; /*!< received peer encryption key */ + esp_ble_pcsrk_keys_t pcsrk_key; /*!< received peer device SRK */ + esp_ble_pid_keys_t pid_key; /*!< peer device ID key */ +} esp_ble_bond_key_info_t; /*!< ble bond key information value type */ + +/** +* @brief struct type of the bond device value +*/ +typedef struct +{ + esp_bd_addr_t bd_addr; /*!< peer address */ + esp_ble_bond_key_info_t bond_key; /*!< the bond key information */ +} esp_ble_bond_dev_t; /*!< the ble bond device type */ + + +/** +* @brief union type of the security key value +*/ +typedef struct +{ + esp_bd_addr_t bd_addr; /*!< peer address */ + esp_ble_key_type_t key_type; /*!< key type of the security link */ + esp_ble_key_value_t p_key_value; /*!< the pointer to the key value */ +} esp_ble_key_t; /*!< the union to the ble key value type*/ + +/** +* @brief structure type of the ble local id keys value +*/ +typedef struct { + esp_bt_octet16_t ir; /*!< the 16 bits of the ir value */ + esp_bt_octet16_t irk; /*!< the 16 bits of the ir key value */ + esp_bt_octet16_t dhk; /*!< the 16 bits of the dh key value */ +} esp_ble_local_id_keys_t; /*!< the structure of the ble local id keys value type*/ + +/** +* @brief structure type of the ble local oob data value +*/ +typedef struct { + esp_bt_octet16_t oob_c; /*!< the 128 bits of confirmation value */ + esp_bt_octet16_t oob_r; /*!< the 128 bits of randomizer value */ +} esp_ble_local_oob_data_t; + +/** + * @brief Structure associated with ESP_AUTH_CMPL_EVT + */ +typedef struct +{ + esp_bd_addr_t bd_addr; /*!< BD address peer device. */ + bool key_present; /*!< Valid link key value in key element */ + esp_link_key key; /*!< Link key associated with peer device. */ + uint8_t key_type; /*!< The type of Link Key */ + bool success; /*!< TRUE of authentication succeeded, FALSE if failed. */ + uint8_t fail_reason; /*!< The HCI reason/error code for when success=FALSE */ + esp_ble_addr_type_t addr_type; /*!< Peer device address type */ + esp_bt_dev_type_t dev_type; /*!< Device type */ + esp_ble_auth_req_t auth_mode; /*!< authentication mode */ +} esp_ble_auth_cmpl_t; /*!< The ble authentication complete cb type */ + +/** + * @brief union associated with ble security + */ +typedef union +{ + esp_ble_sec_key_notif_t key_notif; /*!< passkey notification */ + esp_ble_sec_req_t ble_req; /*!< BLE SMP related request */ + esp_ble_key_t ble_key; /*!< BLE SMP keys used when pairing */ + esp_ble_local_id_keys_t ble_id_keys; /*!< BLE IR event */ + esp_ble_local_oob_data_t oob_data; /*!< BLE SMP secure connection OOB data */ + esp_ble_auth_cmpl_t auth_cmpl; /*!< Authentication complete indication. */ +} esp_ble_sec_t; /*!< BLE security type */ +#if (BLE_42_FEATURE_SUPPORT == TRUE) +/// Sub Event of ESP_GAP_BLE_SCAN_RESULT_EVT +typedef enum { + ESP_GAP_SEARCH_INQ_RES_EVT = 0, /*!< Inquiry result for a peer device. */ + ESP_GAP_SEARCH_INQ_CMPL_EVT = 1, /*!< Inquiry complete. */ + ESP_GAP_SEARCH_DISC_RES_EVT = 2, /*!< Discovery result for a peer device. */ + ESP_GAP_SEARCH_DISC_BLE_RES_EVT = 3, /*!< Discovery result for BLE GATT based service on a peer device. */ + ESP_GAP_SEARCH_DISC_CMPL_EVT = 4, /*!< Discovery complete. */ + ESP_GAP_SEARCH_DI_DISC_CMPL_EVT = 5, /*!< Discovery complete. */ + ESP_GAP_SEARCH_SEARCH_CANCEL_CMPL_EVT = 6, /*!< Search cancelled */ + ESP_GAP_SEARCH_INQ_DISCARD_NUM_EVT = 7, /*!< The number of pkt discarded by flow control */ +} esp_gap_search_evt_t; +#endif // #if (BLE_42_FEATURE_SUPPORT == TRUE) +/** + * @brief Ble scan result event type, to indicate the + * result is scan response or advertising data or other + */ +typedef enum { + ESP_BLE_EVT_CONN_ADV = 0x00, /*!< Connectable undirected advertising (ADV_IND) */ + ESP_BLE_EVT_CONN_DIR_ADV = 0x01, /*!< Connectable directed advertising (ADV_DIRECT_IND) */ + ESP_BLE_EVT_DISC_ADV = 0x02, /*!< Scannable undirected advertising (ADV_SCAN_IND) */ + ESP_BLE_EVT_NON_CONN_ADV = 0x03, /*!< Non connectable undirected advertising (ADV_NONCONN_IND) */ + ESP_BLE_EVT_SCAN_RSP = 0x04, /*!< Scan Response (SCAN_RSP) */ +} esp_ble_evt_type_t; + +typedef enum{ + ESP_BLE_WHITELIST_REMOVE = 0X00, /*!< remove mac from whitelist */ + ESP_BLE_WHITELIST_ADD = 0X01, /*!< add address to whitelist */ + ESP_BLE_WHITELIST_CLEAR = 0x02, /*!< clear all device in whitelist */ +} esp_ble_wl_operation_t; +#if (BLE_42_FEATURE_SUPPORT == TRUE) +typedef enum { + ESP_BLE_DUPLICATE_EXCEPTIONAL_LIST_ADD = 0, /*!< Add device info into duplicate scan exceptional list */ + ESP_BLE_DUPLICATE_EXCEPTIONAL_LIST_REMOVE, /*!< Remove device info from duplicate scan exceptional list */ + ESP_BLE_DUPLICATE_EXCEPTIONAL_LIST_CLEAN, /*!< Clean duplicate scan exceptional list */ +} esp_bt_duplicate_exceptional_subcode_type_t; +#endif //#if (BLE_42_FEATURE_SUPPORT == TRUE) + +#define BLE_BIT(n) (1UL<<(n)) +#if (BLE_42_FEATURE_SUPPORT == TRUE) +typedef enum { + ESP_BLE_DUPLICATE_SCAN_EXCEPTIONAL_INFO_ADV_ADDR = 0, /*!< BLE advertising address , device info will be added into ESP_BLE_DUPLICATE_SCAN_EXCEPTIONAL_ADDR_LIST */ + ESP_BLE_DUPLICATE_SCAN_EXCEPTIONAL_INFO_MESH_LINK_ID, /*!< BLE mesh link ID, it is for BLE mesh, device info will be added into ESP_BLE_DUPLICATE_SCAN_EXCEPTIONAL_MESH_LINK_ID_LIST */ + ESP_BLE_DUPLICATE_SCAN_EXCEPTIONAL_INFO_MESH_BEACON_TYPE, /*!< BLE mesh beacon AD type, the format is | Len | 0x2B | Beacon Type | Beacon Data | */ + ESP_BLE_DUPLICATE_SCAN_EXCEPTIONAL_INFO_MESH_PROV_SRV_ADV, /*!< BLE mesh provisioning service uuid, the format is | 0x02 | 0x01 | flags | 0x03 | 0x03 | 0x1827 | .... |` */ + ESP_BLE_DUPLICATE_SCAN_EXCEPTIONAL_INFO_MESH_PROXY_SRV_ADV, /*!< BLE mesh adv with proxy service uuid, the format is | 0x02 | 0x01 | flags | 0x03 | 0x03 | 0x1828 | .... |` */ + ESP_BLE_DUPLICATE_SCAN_EXCEPTIONAL_INFO_MESH_PROXY_SOLIC_ADV, /*!< BLE mesh adv with proxy service uuid, the format is | 0x02 | 0x01 | flags | 0x03 | 0x03 | 0x1859 | .... |` */ + ESP_BLE_DUPLICATE_SCAN_EXCEPTIONAL_INFO_MESH_URI_ADV, /*!< BLE mesh URI adv, the format is ...| Len | 0x24 | data |... */ +} esp_ble_duplicate_exceptional_info_type_t; + +typedef enum { + ESP_BLE_DUPLICATE_SCAN_EXCEPTIONAL_ADDR_LIST = BLE_BIT(0), /*!< duplicate scan exceptional addr list */ + ESP_BLE_DUPLICATE_SCAN_EXCEPTIONAL_MESH_LINK_ID_LIST = BLE_BIT(1), /*!< duplicate scan exceptional mesh link ID list */ + ESP_BLE_DUPLICATE_SCAN_EXCEPTIONAL_MESH_BEACON_TYPE_LIST = BLE_BIT(2), /*!< duplicate scan exceptional mesh beacon type list */ + ESP_BLE_DUPLICATE_SCAN_EXCEPTIONAL_MESH_PROV_SRV_ADV_LIST = BLE_BIT(3), /*!< duplicate scan exceptional mesh adv with provisioning service uuid */ + ESP_BLE_DUPLICATE_SCAN_EXCEPTIONAL_MESH_PROXY_SRV_ADV_LIST = BLE_BIT(4), /*!< duplicate scan exceptional mesh adv with proxy service uuid */ + ESP_BLE_DUPLICATE_SCAN_EXCEPTIONAL_MESH_PROXY_SOLIC_ADV_LIST = BLE_BIT(5), /*!< duplicate scan exceptional mesh adv with proxy solicitation PDU uuid */ + ESP_BLE_DUPLICATE_SCAN_EXCEPTIONAL_MESH_URI_ADV_LIST = BLE_BIT(6), /*!< duplicate scan exceptional URI list */ + ESP_BLE_DUPLICATE_SCAN_EXCEPTIONAL_ALL_LIST = 0xFFFF, /*!< duplicate scan exceptional all list */ +} esp_duplicate_scan_exceptional_list_type_t; + +typedef uint8_t esp_duplicate_info_t[ESP_BD_ADDR_LEN]; + +#endif //#if (BLE_42_FEATURE_SUPPORT == TRUE) + +#if (BLE_50_FEATURE_SUPPORT == TRUE) +#define ESP_BLE_GAP_SET_EXT_ADV_PROP_NONCONN_NONSCANNABLE_UNDIRECTED (0 << 0) /*!< Non-Connectable and Non-Scannable Undirected advertising */ +#define ESP_BLE_GAP_SET_EXT_ADV_PROP_CONNECTABLE (1 << 0) /*!< Connectable advertising */ +#define ESP_BLE_GAP_SET_EXT_ADV_PROP_SCANNABLE (1 << 1) /*!< Scannable advertising */ +#define ESP_BLE_GAP_SET_EXT_ADV_PROP_DIRECTED (1 << 2) /*!< Directed advertising */ +#define ESP_BLE_GAP_SET_EXT_ADV_PROP_HD_DIRECTED (1 << 3) /*!< High Duty Cycle Directed Connectable advertising (<= 3.75 ms Advertising Interval) */ +#define ESP_BLE_GAP_SET_EXT_ADV_PROP_LEGACY (1 << 4) /*!< Use legacy advertising PDUs */ +#define ESP_BLE_GAP_SET_EXT_ADV_PROP_ANON_ADV (1 << 5) /*!< Omit advertiser's address from all PDUs ("anonymous advertising") */ +#define ESP_BLE_GAP_SET_EXT_ADV_PROP_INCLUDE_TX_PWR (1 << 6) /*!< Include TxPower in the extended header of the advertising PDU */ +#define ESP_BLE_GAP_SET_EXT_ADV_PROP_MASK (0x7F) /*!< Reserved for future use */ + +/*!< If extended advertising PDU types are being used (bit 4 = 0) then: + The advertisement shall not be both connectable and scannable. + High duty cycle directed connectable advertising (<= 3.75 ms advertising interval) shall not be used (bit 3 = 0) +*/ +/*!< ADV_IND */ +#define ESP_BLE_GAP_SET_EXT_ADV_PROP_LEGACY_IND (ESP_BLE_GAP_SET_EXT_ADV_PROP_LEGACY |\ + ESP_BLE_GAP_SET_EXT_ADV_PROP_CONNECTABLE |\ + ESP_BLE_GAP_SET_EXT_ADV_PROP_SCANNABLE) +/*!< ADV_DIRECT_IND (low duty cycle) */ +#define ESP_BLE_GAP_SET_EXT_ADV_PROP_LEGACY_LD_DIR (ESP_BLE_GAP_SET_EXT_ADV_PROP_LEGACY |\ + ESP_BLE_GAP_SET_EXT_ADV_PROP_CONNECTABLE |\ + ESP_BLE_GAP_SET_EXT_ADV_PROP_DIRECTED) +/*!< ADV_DIRECT_IND (high duty cycle) */ +#define ESP_BLE_GAP_SET_EXT_ADV_PROP_LEGACY_HD_DIR (ESP_BLE_GAP_SET_EXT_ADV_PROP_LEGACY |\ + ESP_BLE_GAP_SET_EXT_ADV_PROP_CONNECTABLE |\ + ESP_BLE_GAP_SET_EXT_ADV_PROP_HD_DIRECTED) +/*!< ADV_SCAN_IND */ +#define ESP_BLE_GAP_SET_EXT_ADV_PROP_LEGACY_SCAN (ESP_BLE_GAP_SET_EXT_ADV_PROP_LEGACY |\ + ESP_BLE_GAP_SET_EXT_ADV_PROP_SCANNABLE) +/*!< ADV_NONCONN_IND */ +#define ESP_BLE_GAP_SET_EXT_ADV_PROP_LEGACY_NONCONN (ESP_BLE_GAP_SET_EXT_ADV_PROP_LEGACY) +typedef uint16_t esp_ble_ext_adv_type_mask_t; + +#define ESP_BLE_GAP_PHY_1M 1 /*!< Secondery Advertisement PHY is LE1M */ +#define ESP_BLE_GAP_PHY_2M 2 /*!< Secondery Advertisement PHY is LE2M */ +#define ESP_BLE_GAP_PHY_CODED 3 /*!< Secondery Advertisement PHY is LE Coded */ +typedef uint8_t esp_ble_gap_phy_t; + +#define ESP_BLE_GAP_NO_PREFER_TRANSMIT_PHY (1<<0) /*!< No Prefer TX PHY supported by controller */ +#define ESP_BLE_GAP_NO_PREFER_RECEIVE_PHY (1<<1) /*!< No Prefer RX PHY supported by controller */ +typedef uint8_t esp_ble_gap_all_phys_t; + +/// Primary phy only support 1M and LE coded phy +#define ESP_BLE_GAP_PRI_PHY_1M ESP_BLE_GAP_PHY_1M /*!< Primary Phy is LE1M */ +#define ESP_BLE_GAP_PRI_PHY_CODED ESP_BLE_GAP_PHY_CODED /*!< Primary Phy is LE CODED */ +typedef uint8_t esp_ble_gap_pri_phy_t; // primary phy + +#define ESP_BLE_GAP_PHY_1M_PREF_MASK (1 << 0) /*!< The Host prefers use the LE1M transmitter or reciever PHY */ +#define ESP_BLE_GAP_PHY_2M_PREF_MASK (1 << 1) /*!< The Host prefers use the LE2M transmitter or reciever PHY */ +#define ESP_BLE_GAP_PHY_CODED_PREF_MASK (1 << 2) /*!< The Host prefers use the LE CODED transmitter or reciever PHY */ +typedef uint8_t esp_ble_gap_phy_mask_t; + +#define ESP_BLE_GAP_PHY_OPTIONS_NO_PREF 0 /*!< The Host has no preferred coding when transmitting on the LE Coded PHY */ +#define ESP_BLE_GAP_PHY_OPTIONS_PREF_S2_CODING 1 /*!< The Host prefers that S=2 coding be used when transmitting on the LE Coded PHY */ +#define ESP_BLE_GAP_PHY_OPTIONS_PREF_S8_CODING 2 /*!< The Host prefers that S=8 coding be used when transmitting on the LE Coded PHY */ +typedef uint16_t esp_ble_gap_prefer_phy_options_t; + +#define ESP_BLE_GAP_EXT_SCAN_CFG_UNCODE_MASK 0x01 /*!< Scan Advertisements on the LE1M PHY */ +#define ESP_BLE_GAP_EXT_SCAN_CFG_CODE_MASK 0x02 /*!< Scan advertisements on the LE coded PHY */ +typedef uint8_t esp_ble_ext_scan_cfg_mask_t; + +/// Advertising data +#define ESP_BLE_GAP_EXT_ADV_DATA_COMPLETE 0x00 /*!< extended advertising data compete */ +#define ESP_BLE_GAP_EXT_ADV_DATA_INCOMPLETE 0x01 /*!< extended advertising data incomplete */ +#define ESP_BLE_GAP_EXT_ADV_DATA_TRUNCATED 0x02 /*!< extended advertising data truncated mode */ +typedef uint8_t esp_ble_gap_ext_adv_data_status_t; + +/// Advertising SYNC policy +#define ESP_BLE_GAP_SYNC_POLICY_BY_ADV_INFO 0 /*!< sync policy by advertising info */ +#define ESP_BLE_GAP_SYNC_POLICY_BY_PERIODIC_LIST 1 /*!< periodic advertising sync policy */ +typedef uint8_t esp_ble_gap_sync_t; + +/// Advertising report +#define ESP_BLE_ADV_REPORT_EXT_ADV_IND (1<<0) /*!< advertising report with extended advertising indication type */ +#define ESP_BLE_ADV_REPORT_EXT_SCAN_IND (1<<1) /*!< advertising report with extended scan indication type */ +#define ESP_BLE_ADV_REPORT_EXT_DIRECT_ADV (1<<2) /*!< advertising report with extended direct advertising indication type */ +#define ESP_BLE_ADV_REPORT_EXT_SCAN_RSP (1<<3) /*!< advertising report with extended scan response indication type */ + +/*!< Bluetooth 5.0, Vol 2, Part E, 7.7.65.13 */ +#define ESP_BLE_LEGACY_ADV_TYPE_IND (0x13) /*!< advertising report with legacy advertising indication type */ +#define ESP_BLE_LEGACY_ADV_TYPE_DIRECT_IND (0x15) /*!< advertising report with legacy direct indication type */ +#define ESP_BLE_LEGACY_ADV_TYPE_SCAN_IND (0x12) /*!< advertising report with legacy scan indication type */ +#define ESP_BLE_LEGACY_ADV_TYPE_NONCON_IND (0x10) /*!< advertising report with legacy non connectable indication type */ +#define ESP_BLE_LEGACY_ADV_TYPE_SCAN_RSP_TO_ADV_IND (0x1b) /*!< advertising report with legacy scan response indication type */ +#define ESP_BLE_LEGACY_ADV_TYPE_SCAN_RSP_TO_ADV_SCAN_IND (0x1a) /*!< advertising report with legacy advertising with scan response indication type */ + +typedef uint8_t esp_ble_gap_adv_type_t; + +/// Extend advertising tx power, range: [-127, +126] dBm +#define EXT_ADV_TX_PWR_NO_PREFERENCE (127) /*!< host has no preference for tx power */ + +/** +* @brief ext adv parameters +*/ +typedef struct { + esp_ble_ext_adv_type_mask_t type; /*!< ext adv type */ + uint32_t interval_min; /*!< ext adv minimum interval */ + uint32_t interval_max; /*!< ext adv maximum interval */ + esp_ble_adv_channel_t channel_map; /*!< ext adv channel map */ + esp_ble_addr_type_t own_addr_type; /*!< ext adv own address type */ + esp_ble_addr_type_t peer_addr_type; /*!< ext adv peer address type */ + esp_bd_addr_t peer_addr; /*!< ext adv peer address */ + esp_ble_adv_filter_t filter_policy; /*!< ext adv filter policy */ + int8_t tx_power; /*!< ext adv tx power */ + esp_ble_gap_pri_phy_t primary_phy; /*!< ext adv primary phy */ + uint8_t max_skip; /*!< ext adv maximum skip */ + esp_ble_gap_phy_t secondary_phy; /*!< ext adv secondary phy */ + uint8_t sid; /*!< ext adv sid */ + bool scan_req_notif; /*!< ext adv scan request event notify */ +} esp_ble_gap_ext_adv_params_t; + +/** +* @brief ext scan config +*/ +typedef struct { + esp_ble_scan_type_t scan_type; /*!< ext scan type */ + uint16_t scan_interval; /*!< ext scan interval */ + uint16_t scan_window; /*!< ext scan window */ +} esp_ble_ext_scan_cfg_t; + +/** +* @brief ext scan parameters +*/ +typedef struct { + esp_ble_addr_type_t own_addr_type; /*!< ext scan own address type */ + esp_ble_scan_filter_t filter_policy; /*!< ext scan filter policy */ + esp_ble_scan_duplicate_t scan_duplicate; /*!< ext scan duplicate scan */ + esp_ble_ext_scan_cfg_mask_t cfg_mask; /*!< ext scan config mask */ + esp_ble_ext_scan_cfg_t uncoded_cfg; /*!< ext scan uncoded config parameters */ + esp_ble_ext_scan_cfg_t coded_cfg; /*!< ext scan coded config parameters */ +} esp_ble_ext_scan_params_t; + +/** +* @brief create extend connection parameters +*/ +typedef struct { + uint16_t scan_interval; /*!< init scan interval */ + uint16_t scan_window; /*!< init scan window */ + uint16_t interval_min; /*!< minimum interval */ + uint16_t interval_max; /*!< maximum interval */ + uint16_t latency; /*!< ext scan type */ + uint16_t supervision_timeout; /*!< connection supervision timeout */ + uint16_t min_ce_len; /*!< minimum ce length */ + uint16_t max_ce_len; /*!< maximum ce length */ +} esp_ble_gap_conn_params_t; + +/** +* @brief extend adv enable parameters +*/ +typedef struct { + uint8_t instance; /*!< advertising handle */ + int duration; /*!< advertising duration */ + int max_events; /*!< maximum number of extended advertising events */ +} esp_ble_gap_ext_adv_t; + +/** +* @brief periodic adv parameters +*/ +typedef struct { + uint16_t interval_min; /*!< periodic advertising minimum interval */ + uint16_t interval_max; /*!< periodic advertising maximum interval */ + uint8_t properties; /*!< periodic advertising properties */ +} esp_ble_gap_periodic_adv_params_t; + +/** +* @brief periodic adv sync parameters +*/ +typedef struct { + esp_ble_gap_sync_t filter_policy; /*!< Configures the filter policy for periodic advertising sync: + 0: Use Advertising SID, Advertiser Address Type, and Advertiser Address parameters to determine the advertiser to listen to. + 1: Use the Periodic Advertiser List to determine the advertiser to listen to. */ + #if (CONFIG_BT_BLE_FEAT_CREATE_SYNC_ENH) + esp_ble_gap_sync_t reports_disabled; /*!< Supported only by esp32c2, esp32c6, and esp32h2; can be set by menuconfig: + 0: Reporting initially enabled. + 1: Reporting initially disabled. */ + esp_ble_gap_sync_t filter_duplicates; /*!< Supported only by esp32c2, esp32c6, and esp32h2; can be set by menuconfig: + 0: Duplicate filtering initially disabled. + 1: Duplicate filtering initially enabled. */ + #endif + uint8_t sid; /*!< SID of the periodic advertising */ + esp_ble_addr_type_t addr_type; /*!< Address type of the periodic advertising */ + esp_bd_addr_t addr; /*!< Address of the periodic advertising */ + uint16_t skip; /*!< Maximum number of periodic advertising events that can be skipped */ + uint16_t sync_timeout; /*!< Synchronization timeout */ +} esp_ble_gap_periodic_adv_sync_params_t; + +/** +* @brief extend adv report parameters +*/ +typedef struct { + // uint8_t props; + // uint8_t legacy_event_type; + esp_ble_gap_adv_type_t event_type; /*!< extend advertising type */ + uint8_t addr_type; /*!< extend advertising address type */ + esp_bd_addr_t addr; /*!< extend advertising address */ + esp_ble_gap_pri_phy_t primary_phy; /*!< extend advertising primary phy */ + esp_ble_gap_phy_t secondly_phy; /*!< extend advertising secondary phy */ + uint8_t sid; /*!< extend advertising sid */ + uint8_t tx_power; /*!< extend advertising tx power */ + int8_t rssi; /*!< extend advertising rssi */ + uint16_t per_adv_interval; /*!< periodic advertising interval */ + uint8_t dir_addr_type; /*!< direct address type */ + esp_bd_addr_t dir_addr; /*!< direct address */ + esp_ble_gap_ext_adv_data_status_t data_status; /*!< data type */ + uint8_t adv_data_len; /*!< extend advertising data length */ + uint8_t adv_data[251]; /*!< extend advertising data */ +} esp_ble_gap_ext_adv_reprot_t; + +/** +* @brief periodic adv report parameters +*/ +typedef struct { + uint16_t sync_handle; /*!< periodic advertising train handle */ + uint8_t tx_power; /*!< periodic advertising tx power*/ + int8_t rssi; /*!< periodic advertising rssi */ + esp_ble_gap_ext_adv_data_status_t data_status; /*!< periodic advertising data type*/ + uint8_t data_length; /*!< periodic advertising data length */ + uint8_t data[251]; /*!< periodic advertising data */ +} esp_ble_gap_periodic_adv_report_t; + +/** +* @brief perodic adv sync establish parameters +*/ +typedef struct { + uint8_t status; /*!< periodic advertising sync status */ + uint16_t sync_handle; /*!< periodic advertising train handle */ + uint8_t sid; /*!< periodic advertising sid */ + esp_ble_addr_type_t addr_type; /*!< periodic advertising address type */ + esp_bd_addr_t adv_addr; /*!< periodic advertising address */ + esp_ble_gap_phy_t adv_phy; /*!< periodic advertising adv phy type */ + uint16_t period_adv_interval; /*!< periodic advertising interval */ + uint8_t adv_clk_accuracy; /*!< periodic advertising clock accuracy */ +} esp_ble_gap_periodic_adv_sync_estab_t; + +/** +* @brief DTM TX parameters +*/ +typedef struct +{ + uint8_t tx_channel; /*!< channel for sending test data, tx_channel = (Frequency -2402)/2, tx_channel range:0x00-0x27, Frequency range: 2402 MHz to 2480 MHz */ + uint8_t len_of_data; /*!< length in bytes of payload data in each packet */ + esp_ble_dtm_pkt_payload_t pkt_payload; /*!< packet payload type. value range: 0x00-0x07 */ + esp_ble_gap_phy_t phy; /*!< the phy type used by the transmitter, coded phy with S=2:0x04 */ +} esp_ble_dtm_enh_tx_t; + +/** +* @brief DTM RX parameters +*/ +typedef struct +{ + uint8_t rx_channel; /*!< channel for test data reception, rx_channel = (Frequency -2402)/2, tx_channel range:0x00-0x27, Frequency range: 2402 MHz to 2480 MHz */ + esp_ble_gap_phy_t phy; /*!< the phy type used by the receiver, 1M phy: 0x01, 2M phy:0x02, coded phy:0x03 */ + uint8_t modulation_idx; /*!< modulation index, 0x00:standard modulation index, 0x01:stable modulation index */ +} esp_ble_dtm_enh_rx_t; + +#endif //#if (BLE_50_FEATURE_SUPPORT == TRUE) + +#if (BLE_FEAT_PERIODIC_ADV_SYNC_TRANSFER == TRUE) +/// Periodic advertising sync trans mode +#define ESP_BLE_GAP_PAST_MODE_NO_SYNC_EVT (0x00) /*!< No attempt is made to sync and no periodic adv sync transfer received event */ +#define ESP_BLE_GAP_PAST_MODE_NO_REPORT_EVT (0x01) /*!< An periodic adv sync transfer received event and no periodic adv report events */ +#define ESP_BLE_GAP_PAST_MODE_DUP_FILTER_DISABLED (0x02) /*!< Periodic adv report events will be enabled with duplicate filtering disabled */ +#define ESP_BLE_GAP_PAST_MODE_DUP_FILTER_ENABLED (0x03) /*!< Periodic adv report events will be enabled with duplicate filtering enabled */ +typedef uint8_t esp_ble_gap_past_mode_t; + +/** +* @brief periodic adv sync transfer parameters +*/ +typedef struct { + esp_ble_gap_past_mode_t mode; /*!< periodic advertising sync transfer mode */ + uint16_t skip; /*!< the number of periodic advertising packets that can be skipped */ + uint16_t sync_timeout; /*!< synchronization timeout for the periodic advertising train */ + uint8_t cte_type; /*!< periodic advertising sync transfer CET type */ +} esp_ble_gap_past_params_t; +#endif // #if (BLE_FEAT_PERIODIC_ADV_SYNC_TRANSFER == TRUE) + +/** + * @brief Gap callback parameters union + */ +typedef union { + /** + * @brief ESP_GAP_BLE_GET_DEV_NAME_COMPLETE_EVT + */ + struct ble_get_dev_name_cmpl_evt_param { + esp_bt_status_t status; /*!< Indicate the get device name success status */ + char *name; /*!< Name of bluetooth device */ + } get_dev_name_cmpl; /*!< Event parameter of ESP_GAP_BLE_GET_DEV_NAME_COMPLETE_EVT */ +#if (BLE_42_FEATURE_SUPPORT == TRUE) + /** + * @brief ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT + */ + struct ble_adv_data_cmpl_evt_param { + esp_bt_status_t status; /*!< Indicate the set advertising data operation success status */ + } adv_data_cmpl; /*!< Event parameter of ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT */ + /** + * @brief ESP_GAP_BLE_SCAN_RSP_DATA_SET_COMPLETE_EVT + */ + struct ble_scan_rsp_data_cmpl_evt_param { + esp_bt_status_t status; /*!< Indicate the set scan response data operation success status */ + } scan_rsp_data_cmpl; /*!< Event parameter of ESP_GAP_BLE_SCAN_RSP_DATA_SET_COMPLETE_EVT */ + /** + * @brief ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT + */ + struct ble_scan_param_cmpl_evt_param { + esp_bt_status_t status; /*!< Indicate the set scan param operation success status */ + } scan_param_cmpl; /*!< Event parameter of ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT */ + /** + * @brief ESP_GAP_BLE_SCAN_RESULT_EVT + */ + struct ble_scan_result_evt_param { + esp_gap_search_evt_t search_evt; /*!< Search event type */ + esp_bd_addr_t bda; /*!< Bluetooth device address which has been searched */ + esp_bt_dev_type_t dev_type; /*!< Device type */ + esp_ble_addr_type_t ble_addr_type; /*!< Ble device address type */ + esp_ble_evt_type_t ble_evt_type; /*!< Ble scan result event type */ + int rssi; /*!< Searched device's RSSI */ + uint8_t ble_adv[ESP_BLE_ADV_DATA_LEN_MAX + ESP_BLE_SCAN_RSP_DATA_LEN_MAX]; /*!< Received EIR */ + int flag; /*!< Advertising data flag bit */ + int num_resps; /*!< Scan result number */ + uint8_t adv_data_len; /*!< Adv data length */ + uint8_t scan_rsp_len; /*!< Scan response length */ + uint32_t num_dis; /*!< The number of discard packets */ + } scan_rst; /*!< Event parameter of ESP_GAP_BLE_SCAN_RESULT_EVT */ + /** + * @brief ESP_GAP_BLE_ADV_DATA_RAW_SET_COMPLETE_EVT + */ + struct ble_adv_data_raw_cmpl_evt_param { + esp_bt_status_t status; /*!< Indicate the set raw advertising data operation success status */ + } adv_data_raw_cmpl; /*!< Event parameter of ESP_GAP_BLE_ADV_DATA_RAW_SET_COMPLETE_EVT */ + /** + * @brief ESP_GAP_BLE_SCAN_RSP_DATA_RAW_SET_COMPLETE_EVT + */ + struct ble_scan_rsp_data_raw_cmpl_evt_param { + esp_bt_status_t status; /*!< Indicate the set raw advertising data operation success status */ + } scan_rsp_data_raw_cmpl; /*!< Event parameter of ESP_GAP_BLE_SCAN_RSP_DATA_RAW_SET_COMPLETE_EVT */ + /** + * @brief ESP_GAP_BLE_ADV_START_COMPLETE_EVT + */ + struct ble_adv_start_cmpl_evt_param { + esp_bt_status_t status; /*!< Indicate advertising start operation success status */ + } adv_start_cmpl; /*!< Event parameter of ESP_GAP_BLE_ADV_START_COMPLETE_EVT */ + /** + * @brief ESP_GAP_BLE_SCAN_START_COMPLETE_EVT + */ + struct ble_scan_start_cmpl_evt_param { + esp_bt_status_t status; /*!< Indicate scan start operation success status */ + } scan_start_cmpl; /*!< Event parameter of ESP_GAP_BLE_SCAN_START_COMPLETE_EVT */ +#endif //#if (BLE_42_FEATURE_SUPPORT == TRUE) + esp_ble_sec_t ble_security; /*!< ble gap security union type */ +#if (BLE_42_FEATURE_SUPPORT == TRUE) + /** + * @brief ESP_GAP_BLE_SCAN_STOP_COMPLETE_EVT + */ + struct ble_scan_stop_cmpl_evt_param { + esp_bt_status_t status; /*!< Indicate scan stop operation success status */ + } scan_stop_cmpl; /*!< Event parameter of ESP_GAP_BLE_SCAN_STOP_COMPLETE_EVT */ + /** + * @brief ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT + */ + struct ble_adv_stop_cmpl_evt_param { + esp_bt_status_t status; /*!< Indicate adv stop operation success status */ + } adv_stop_cmpl; /*!< Event parameter of ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT */ + /** + * @brief ESP_GAP_BLE_ADV_CLEAR_COMPLETE_EVT + */ + struct ble_adv_clear_cmpl_evt_param { + esp_bt_status_t status; /*!< Indicate adv clear operation success status */ + } adv_clear_cmpl; /*!< Event parameter of ESP_GAP_BLE_ADV_CLEAR_COMPLETE_EVT */ +#endif // #if (BLE_42_FEATURE_SUPPORT == TRUE) + /** + * @brief ESP_GAP_BLE_SET_STATIC_RAND_ADDR_EVT + */ + struct ble_set_rand_cmpl_evt_param { + esp_bt_status_t status; /*!< Indicate set static rand address operation success status */ + } set_rand_addr_cmpl; /*!< Event parameter of ESP_GAP_BLE_SET_STATIC_RAND_ADDR_EVT */ + /** + * @brief ESP_GAP_BLE_UPDATE_CONN_PARAMS_EVT + */ + struct ble_update_conn_params_evt_param { + esp_bt_status_t status; /*!< Indicate update connection parameters success status */ + esp_bd_addr_t bda; /*!< Bluetooth device address */ + uint16_t min_int; /*!< Min connection interval */ + uint16_t max_int; /*!< Max connection interval */ + uint16_t latency; /*!< Slave latency for the connection in number of connection events. Range: 0x0000 to 0x01F3 */ + uint16_t conn_int; /*!< Current connection interval */ + uint16_t timeout; /*!< Supervision timeout for the LE Link. Range: 0x000A to 0x0C80. + Mandatory Range: 0x000A to 0x0C80 Time = N * 10 msec */ + } update_conn_params; /*!< Event parameter of ESP_GAP_BLE_UPDATE_CONN_PARAMS_EVT */ + /** + * @brief ESP_GAP_BLE_SET_PKT_LENGTH_COMPLETE_EVT + */ + struct ble_pkt_data_length_cmpl_evt_param { + esp_bt_status_t status; /*!< Indicate the set pkt data length operation success status */ + esp_ble_pkt_data_length_params_t params; /*!< pkt data length value */ + } pkt_data_length_cmpl; /*!< Event parameter of ESP_GAP_BLE_SET_PKT_LENGTH_COMPLETE_EVT */ + /** + * @brief ESP_GAP_BLE_SET_LOCAL_PRIVACY_COMPLETE_EVT + */ + struct ble_local_privacy_cmpl_evt_param { + esp_bt_status_t status; /*!< Indicate the set local privacy operation success status */ + } local_privacy_cmpl; /*!< Event parameter of ESP_GAP_BLE_SET_LOCAL_PRIVACY_COMPLETE_EVT */ + /** + * @brief ESP_GAP_BLE_REMOVE_BOND_DEV_COMPLETE_EVT + */ + struct ble_remove_bond_dev_cmpl_evt_param { + esp_bt_status_t status; /*!< Indicate the remove bond device operation success status */ + esp_bd_addr_t bd_addr; /*!< The device address which has been remove from the bond list */ + } remove_bond_dev_cmpl; /*!< Event parameter of ESP_GAP_BLE_REMOVE_BOND_DEV_COMPLETE_EVT */ + /** + * @brief ESP_GAP_BLE_CLEAR_BOND_DEV_COMPLETE_EVT + */ + struct ble_clear_bond_dev_cmpl_evt_param { + esp_bt_status_t status; /*!< Indicate the clear bond device operation success status */ + } clear_bond_dev_cmpl; /*!< Event parameter of ESP_GAP_BLE_CLEAR_BOND_DEV_COMPLETE_EVT */ + /** + * @brief ESP_GAP_BLE_GET_BOND_DEV_COMPLETE_EVT + */ + struct ble_get_bond_dev_cmpl_evt_param { + esp_bt_status_t status; /*!< Indicate the get bond device operation success status */ + uint8_t dev_num; /*!< Indicate the get number device in the bond list */ + esp_ble_bond_dev_t *bond_dev; /*!< the pointer to the bond device Structure */ + } get_bond_dev_cmpl; /*!< Event parameter of ESP_GAP_BLE_GET_BOND_DEV_COMPLETE_EVT */ + /** + * @brief ESP_GAP_BLE_READ_RSSI_COMPLETE_EVT + */ + struct ble_read_rssi_cmpl_evt_param { + esp_bt_status_t status; /*!< Indicate the read adv tx power operation success status */ + int8_t rssi; /*!< The ble remote device rssi value, the range is from -127 to 20, the unit is dbm, + if the RSSI cannot be read, the RSSI metric shall be set to 127. */ + esp_bd_addr_t remote_addr; /*!< The remote device address */ + } read_rssi_cmpl; /*!< Event parameter of ESP_GAP_BLE_READ_RSSI_COMPLETE_EVT */ + /** + * @brief ESP_GAP_BLE_UPDATE_WHITELIST_COMPLETE_EVT + */ + struct ble_update_whitelist_cmpl_evt_param { + esp_bt_status_t status; /*!< Indicate the add or remove whitelist operation success status */ + esp_ble_wl_operation_t wl_operation; /*!< The value is ESP_BLE_WHITELIST_ADD if add address to whitelist operation success, ESP_BLE_WHITELIST_REMOVE if remove address from the whitelist operation success */ + } update_whitelist_cmpl; /*!< Event parameter of ESP_GAP_BLE_UPDATE_WHITELIST_COMPLETE_EVT */ +#if (BLE_42_FEATURE_SUPPORT == TRUE) + /** + * @brief ESP_GAP_BLE_UPDATE_DUPLICATE_EXCEPTIONAL_LIST_COMPLETE_EVT + */ + struct ble_update_duplicate_exceptional_list_cmpl_evt_param { + esp_bt_status_t status; /*!< Indicate update duplicate scan exceptional list operation success status */ + uint8_t subcode; /*!< Define in esp_bt_duplicate_exceptional_subcode_type_t */ + uint16_t length; /*!< The length of device_info */ + esp_duplicate_info_t device_info; /*!< device information, when subcode is ESP_BLE_DUPLICATE_EXCEPTIONAL_LIST_CLEAN, the value is invalid */ + } update_duplicate_exceptional_list_cmpl; /*!< Event parameter of ESP_GAP_BLE_UPDATE_DUPLICATE_EXCEPTIONAL_LIST_COMPLETE_EVT */ +#endif // #if (BLE_42_FEATURE_SUPPORT == TRUE) + /** + * @brief ESP_GAP_BLE_SET_CHANNELS_EVT + */ + struct ble_set_channels_evt_param { + esp_bt_status_t stat; /*!< BLE set channel status */ + } ble_set_channels; /*!< Event parameter of ESP_GAP_BLE_SET_CHANNELS_EVT */ + +#if (BLE_50_FEATURE_SUPPORT == TRUE) + /** + * @brief ESP_GAP_BLE_READ_PHY_COMPLETE_EVT + */ + struct ble_read_phy_cmpl_evt_param { + esp_bt_status_t status; /*!< read phy complete status */ + esp_bd_addr_t bda; /*!< read phy address */ + esp_ble_gap_phy_t tx_phy; /*!< tx phy type */ + esp_ble_gap_phy_t rx_phy; /*!< rx phy type */ + } read_phy; /*!< Event parameter of ESP_GAP_BLE_READ_PHY_COMPLETE_EVT */ + /** + * @brief ESP_GAP_BLE_SET_PREFERRED_DEFAULT_PHY_COMPLETE_EVT + */ + struct ble_set_perf_def_phy_cmpl_evt_param { + esp_bt_status_t status; /*!< Indicate perf default phy set status */ + } set_perf_def_phy; /*!< Event parameter of ESP_GAP_BLE_SET_PREFERRED_DEFAULT_PHY_COMPLETE_EVT */ + /** + * @brief ESP_GAP_BLE_SET_PREFERRED_PHY_COMPLETE_EVT + */ + struct ble_set_perf_phy_cmpl_evt_param { + esp_bt_status_t status; /*!< Indicate perf phy set status */ + } set_perf_phy; /*!< Event parameter of ESP_GAP_BLE_SET_PREFERRED_PHY_COMPLETE_EVT */ + /** + * @brief ESP_GAP_BLE_EXT_ADV_SET_RAND_ADDR_COMPLETE_EVT + */ + struct ble_ext_adv_set_rand_addr_cmpl_evt_param { + esp_bt_status_t status; /*!< Indicate extend advertising random address set status */ + } ext_adv_set_rand_addr; /*!< Event parameter of ESP_GAP_BLE_EXT_ADV_SET_RAND_ADDR_COMPLETE_EVT */ + /** + * @brief ESP_GAP_BLE_EXT_ADV_SET_PARAMS_COMPLETE_EVT + */ + struct ble_ext_adv_set_params_cmpl_evt_param { + esp_bt_status_t status; /*!< Indicate extend advertising parameters set status */ + } ext_adv_set_params; /*!< Event parameter of ESP_GAP_BLE_EXT_ADV_SET_PARAMS_COMPLETE_EVT */ + /** + * @brief ESP_GAP_BLE_EXT_ADV_DATA_SET_COMPLETE_EVT + */ + struct ble_ext_adv_data_set_cmpl_evt_param { + esp_bt_status_t status; /*!< Indicate extend advertising data set status */ + } ext_adv_data_set; /*!< Event parameter of ESP_GAP_BLE_EXT_ADV_DATA_SET_COMPLETE_EVT */ + /** + * @brief ESP_GAP_BLE_EXT_SCAN_RSP_DATA_SET_COMPLETE_EVT + */ + struct ble_ext_adv_scan_rsp_set_cmpl_evt_param { + esp_bt_status_t status; /*!< Indicate extend advertising scan response data set status */ + } scan_rsp_set; /*!< Event parameter of ESP_GAP_BLE_EXT_SCAN_RSP_DATA_SET_COMPLETE_EVT */ + /** + * @brief ESP_GAP_BLE_EXT_ADV_START_COMPLETE_EVT + */ + struct ble_ext_adv_start_cmpl_evt_param { + esp_bt_status_t status; /*!< Indicate advertising start operation success status */ + } ext_adv_start; /*!< Event parameter of ESP_GAP_BLE_EXT_ADV_START_COMPLETE_EVT */ + /** + * @brief ESP_GAP_BLE_EXT_ADV_STOP_COMPLETE_EVT + */ + struct ble_ext_adv_stop_cmpl_evt_param { + esp_bt_status_t status; /*!< Indicate advertising stop operation success status */ + } ext_adv_stop; /*!< Event parameter of ESP_GAP_BLE_EXT_ADV_STOP_COMPLETE_EVT */ + /** + * @brief ESP_GAP_BLE_EXT_ADV_SET_REMOVE_COMPLETE_EVT + */ + struct ble_ext_adv_set_remove_cmpl_evt_param { + esp_bt_status_t status; /*!< Indicate advertising stop operation success status */ + } ext_adv_remove; /*!< Event parameter of ESP_GAP_BLE_EXT_ADV_SET_REMOVE_COMPLETE_EVT */ + /** + * @brief ESP_GAP_BLE_EXT_ADV_SET_CLEAR_COMPLETE_EVT + */ + struct ble_ext_adv_set_clear_cmpl_evt_param { + esp_bt_status_t status; /*!< Indicate advertising stop operation success status */ + } ext_adv_clear; /*!< Event parameter of ESP_GAP_BLE_EXT_ADV_SET_CLEAR_COMPLETE_EVT */ + /** + * @brief ESP_GAP_BLE_PERIODIC_ADV_SET_PARAMS_COMPLETE_EVT + */ + struct ble_periodic_adv_set_params_cmpl_param { + esp_bt_status_t status; /*!< Indicate periodic advertisingparameters set status */ + } peroid_adv_set_params; /*!< Event parameter of ESP_GAP_BLE_PERIODIC_ADV_SET_PARAMS_COMPLETE_EVT */ + /** + * @brief ESP_GAP_BLE_PERIODIC_ADV_DATA_SET_COMPLETE_EVT + */ + struct ble_periodic_adv_data_set_cmpl_param { + esp_bt_status_t status; /*!< Indicate periodic advertising data set status */ + } period_adv_data_set; /*!< Event parameter of ESP_GAP_BLE_PERIODIC_ADV_DATA_SET_COMPLETE_EVT */ + /** + * @brief ESP_GAP_BLE_PERIODIC_ADV_START_COMPLETE_EVT + */ + struct ble_periodic_adv_start_cmpl_param { + esp_bt_status_t status; /*!< Indicate periodic advertising start status */ + } period_adv_start; /*!< Event parameter of ESP_GAP_BLE_PERIODIC_ADV_START_COMPLETE_EVT */ + /** + * @brief ESP_GAP_BLE_PERIODIC_ADV_STOP_COMPLETE_EVT + */ + struct ble_periodic_adv_stop_cmpl_param { + esp_bt_status_t status; /*!< Indicate periodic advertising stop status */ + } period_adv_stop; /*!< Event parameter of ESP_GAP_BLE_PERIODIC_ADV_STOP_COMPLETE_EVT */ + /** + * @brief ESP_GAP_BLE_PERIODIC_ADV_CREATE_SYNC_COMPLETE_EVT + */ + struct ble_period_adv_create_sync_cmpl_param { + esp_bt_status_t status; /*!< Indicate periodic advertising create sync status */ + } period_adv_create_sync; /*!< Event parameter of ESP_GAP_BLE_PERIODIC_ADV_CREATE_SYNC_COMPLETE_EVT */ + /** + * @brief ESP_GAP_BLE_PERIODIC_ADV_SYNC_CANCEL_COMPLETE_EVT + */ + struct ble_period_adv_sync_cancel_cmpl_param { + esp_bt_status_t status; /*!< Indicate periodic advertising sync cancel status */ + } period_adv_sync_cancel; /*!< Event parameter of ESP_GAP_BLE_PERIODIC_ADV_SYNC_CANCEL_COMPLETE_EVT */ + /** + * @brief ESP_GAP_BLE_PERIODIC_ADV_SYNC_TERMINATE_COMPLETE_EVT + */ + struct ble_period_adv_sync_terminate_cmpl_param { + esp_bt_status_t status; /*!< Indicate periodic advertising sync terminate status */ + } period_adv_sync_term; /*!< Event parameter of ESP_GAP_BLE_PERIODIC_ADV_SYNC_TERMINATE_COMPLETE_EVT */ + /** + * @brief ESP_GAP_BLE_PERIODIC_ADV_ADD_DEV_COMPLETE_EVT + */ + struct ble_period_adv_add_dev_cmpl_param { + esp_bt_status_t status; /*!< Indicate periodic advertising device list add status */ + } period_adv_add_dev; /*!< Event parameter of ESP_GAP_BLE_PERIODIC_ADV_ADD_DEV_COMPLETE_EVT */ + /** + * @brief ESP_GAP_BLE_PERIODIC_ADV_REMOVE_DEV_COMPLETE_EVT + */ + struct ble_period_adv_remove_dev_cmpl_param { + esp_bt_status_t status; /*!< Indicate periodic advertising device list remove status */ + } period_adv_remove_dev; /*!< Event parameter of ESP_GAP_BLE_PERIODIC_ADV_REMOVE_DEV_COMPLETE_EVT */ + /** + * @brief ESP_GAP_BLE_PERIODIC_ADV_CLEAR_DEV_COMPLETE_EVT + */ + struct ble_period_adv_clear_dev_cmpl_param { + esp_bt_status_t status; /*!< Indicate periodic advertising device list clean status */ + } period_adv_clear_dev; /*!< Event parameter of ESP_GAP_BLE_PERIODIC_ADV_CLEAR_DEV_COMPLETE_EVT */ + /** + * @brief ESP_GAP_BLE_SET_EXT_SCAN_PARAMS_COMPLETE_EVT + */ + struct ble_set_ext_scan_params_cmpl_param { + esp_bt_status_t status; /*!< Indicate extend advertising parameters set status */ + } set_ext_scan_params; /*!< Event parameter of ESP_GAP_BLE_SET_EXT_SCAN_PARAMS_COMPLETE_EVT */ + /** + * @brief ESP_GAP_BLE_EXT_SCAN_START_COMPLETE_EVT + */ + struct ble_ext_scan_start_cmpl_param { + esp_bt_status_t status; /*!< Indicate extend advertising start status */ + } ext_scan_start; /*!< Event parameter of ESP_GAP_BLE_EXT_SCAN_START_COMPLETE_EVT */ + /** + * @brief ESP_GAP_BLE_EXT_SCAN_STOP_COMPLETE_EVT + */ + struct ble_ext_scan_stop_cmpl_param { + esp_bt_status_t status; /*!< Indicate extend advertising stop status */ + } ext_scan_stop; /*!< Event parameter of ESP_GAP_BLE_EXT_SCAN_STOP_COMPLETE_EVT */ + /** + * @brief ESP_GAP_BLE_PREFER_EXT_CONN_PARAMS_SET_COMPLETE_EVT + */ + struct ble_ext_conn_params_set_cmpl_param { + esp_bt_status_t status; /*!< Indicate extend connection parameters set status */ + } ext_conn_params_set; /*!< Event parameter of ESP_GAP_BLE_PREFER_EXT_CONN_PARAMS_SET_COMPLETE_EVT */ + /** + * @brief ESP_GAP_BLE_ADV_TERMINATED_EVT + */ + struct ble_adv_terminate_param { + uint8_t status; /*!< Indicate adv terminate status */ + /* status 0x3c indicates that advertising for a fixed duration completed or, + for directed advertising, that advertising completed without a connection + being created; + status 0x00 indicates that advertising successfully ended with a connection being created. + */ + uint8_t adv_instance; /*!< extend advertising handle */ + uint16_t conn_idx; /*!< connection index */ + uint8_t completed_event; /*!< the number of completed extend advertising events */ + } adv_terminate; /*!< Event parameter of ESP_GAP_BLE_ADV_TERMINATED_EVT */ + /** + * @brief ESP_GAP_BLE_SCAN_REQ_RECEIVED_EVT + */ + struct ble_scan_req_received_param { + uint8_t adv_instance; /*!< extend advertising handle */ + esp_ble_addr_type_t scan_addr_type; /*!< scanner address type */ + esp_bd_addr_t scan_addr; /*!< scanner address */ + } scan_req_received; /*!< Event parameter of ESP_GAP_BLE_SCAN_REQ_RECEIVED_EVT */ + /** + * @brief ESP_GAP_BLE_CHANNEL_SELECT_ALGORITHM_EVT + */ + struct ble_channel_sel_alg_param { + uint16_t conn_handle; /*!< connection handle */ + uint8_t channel_sel_alg; /*!< channel selection algorithm */ + } channel_sel_alg; /*!< Event parameter of ESP_GAP_BLE_CHANNEL_SELECT_ALGORITHM_EVT */ + /** + * @brief ESP_GAP_BLE_PERIODIC_ADV_SYNC_LOST_EVT + */ + struct ble_periodic_adv_sync_lost_param { + uint16_t sync_handle; /*!< sync handle */ + } periodic_adv_sync_lost; /*!< Event parameter of ESP_GAP_BLE_PERIODIC_ADV_SYNC_LOST_EVT */ + /** + * @brief ESP_GAP_BLE_PERIODIC_ADV_SYNC_ESTAB_EVT + */ + struct ble_periodic_adv_sync_estab_param { + uint8_t status; /*!< periodic advertising sync status */ + uint16_t sync_handle; /*!< periodic advertising sync handle */ + uint8_t sid; /*!< periodic advertising sid */ + esp_ble_addr_type_t adv_addr_type; /*!< periodic advertising address type */ + esp_bd_addr_t adv_addr; /*!< periodic advertising address */ + esp_ble_gap_phy_t adv_phy; /*!< periodic advertising phy type */ + uint16_t period_adv_interval; /*!< periodic advertising interval */ + uint8_t adv_clk_accuracy; /*!< periodic advertising clock accuracy */ + } periodic_adv_sync_estab; /*!< Event parameter of ESP_GAP_BLE_PERIODIC_ADV_SYNC_ESTAB_EVT */ + /** + * @brief ESP_GAP_BLE_PHY_UPDATE_COMPLETE_EVT + */ + struct ble_phy_update_cmpl_param { + esp_bt_status_t status; /*!< phy update status */ + esp_bd_addr_t bda; /*!< address */ + esp_ble_gap_phy_t tx_phy; /*!< tx phy type */ + esp_ble_gap_phy_t rx_phy; /*!< rx phy type */ + } phy_update; /*!< Event parameter of ESP_GAP_BLE_PHY_UPDATE_COMPLETE_EVT */ + /** + * @brief ESP_GAP_BLE_EXT_ADV_REPORT_EVT + */ + struct ble_ext_adv_report_param { + esp_ble_gap_ext_adv_reprot_t params; /*!< extend advertising report parameters */ + } ext_adv_report; /*!< Event parameter of ESP_GAP_BLE_EXT_ADV_REPORT_EVT */ + /** + * @brief ESP_GAP_BLE_PERIODIC_ADV_REPORT_EVT + */ + struct ble_periodic_adv_report_param { + esp_ble_gap_periodic_adv_report_t params; /*!< periodic advertising report parameters */ + } period_adv_report; /*!< Event parameter of ESP_GAP_BLE_PERIODIC_ADV_REPORT_EVT */ +#endif // #if (BLE_50_FEATURE_SUPPORT == TRUE) +#if (BLE_FEAT_PERIODIC_ADV_SYNC_TRANSFER == TRUE) + /** + * @brief ESP_GAP_BLE_PERIODIC_ADV_RECV_ENABLE_COMPLETE_EVT + */ + struct ble_periodic_adv_recv_enable_cmpl_param { + esp_bt_status_t status; /*!< Set periodic advertising receive enable status */ + } period_adv_recv_enable; /*!< Event parameter of ESP_GAP_BLE_PERIODIC_ADV_RECV_ENABLE_COMPLETE_EVT */ + /** + * @brief ESP_GAP_BLE_PERIODIC_ADV_SYNC_TRANS_COMPLETE_EVT + */ + struct ble_periodic_adv_sync_trans_cmpl_param { + esp_bt_status_t status; /*!< Periodic advertising sync transfer status */ + esp_bd_addr_t bda; /*!< The remote device address */ + } period_adv_sync_trans; /*!< Event parameter of ESP_GAP_BLE_PERIODIC_ADV_SYNC_TRANS_COMPLETE_EVT */ + /** + * @brief ESP_GAP_BLE_PERIODIC_ADV_SET_INFO_TRANS_COMPLETE_EVT + */ + struct ble_periodic_adv_set_info_trans_cmpl_param { + esp_bt_status_t status; /*!< Periodic advertising set info transfer status */ + esp_bd_addr_t bda; /*!< The remote device address */ + } period_adv_set_info_trans; /*!< Event parameter of ESP_GAP_BLE_PERIODIC_ADV_SET_INFO_TRANS_COMPLETE_EVT */ + /** + * @brief ESP_GAP_BLE_SET_PAST_PARAMS_COMPLETE_EVT + */ + struct ble_set_past_params_cmpl_param { + esp_bt_status_t status; /*!< Set periodic advertising sync transfer params status */ + esp_bd_addr_t bda; /*!< The remote device address */ + } set_past_params; /*!< Event parameter of ESP_GAP_BLE_SET_PAST_PARAMS_COMPLETE_EVT */ + /** + * @brief ESP_GAP_BLE_PERIODIC_ADV_SYNC_TRANS_RECV_EVT + */ + struct ble_periodic_adv_sync_trans_recv_param { + esp_bt_status_t status; /*!< Periodic advertising sync transfer received status */ + esp_bd_addr_t bda; /*!< The remote device address */ + uint16_t service_data; /*!< The value provided by the peer device */ + uint16_t sync_handle; /*!< Periodic advertising sync handle */ + uint8_t adv_sid; /*!< Periodic advertising set id */ + uint8_t adv_addr_type; /*!< Periodic advertiser address type */ + esp_bd_addr_t adv_addr; /*!< Periodic advertiser address */ + esp_ble_gap_phy_t adv_phy; /*!< Periodic advertising PHY */ + uint16_t adv_interval; /*!< Periodic advertising interval */ + uint8_t adv_clk_accuracy; /*!< Periodic advertising clock accuracy */ + } past_received; /*!< Event parameter of ESP_GAP_BLE_PERIODIC_ADV_SYNC_TRANS_RECV_EVT */ +#endif // #if (BLE_FEAT_PERIODIC_ADV_SYNC_TRANSFER == TRUE) + /** + * @brief ESP_GAP_BLE_DTM_TEST_UPDATE_EVT + */ + struct ble_dtm_state_update_evt_param { + esp_bt_status_t status; /*!< Indicate DTM operation success status */ + esp_ble_dtm_update_evt_t update_evt; /*!< DTM state change event, 0x00: DTM TX start, 0x01: DTM RX start, 0x02:DTM end */ + uint16_t num_of_pkt; /*!< number of packets received, only valid if update_evt is DTM_TEST_STOP_EVT and shall be reported as 0 for a transmitter */ + } dtm_state_update; /*!< Event parameter of ESP_GAP_BLE_DTM_TEST_UPDATE_EVT */ +} esp_ble_gap_cb_param_t; + +/** + * @brief GAP callback function type + * @param event : Event type + * @param param : Point to callback parameter, currently is union type + */ +typedef void (* esp_gap_ble_cb_t)(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param); + +/** + * @brief This function is called to occur gap event, such as scan result + * + * @param[in] callback: callback function + * + * @return + * - ESP_OK : success + * - other : failed + * + */ +esp_err_t esp_ble_gap_register_callback(esp_gap_ble_cb_t callback); + +/** + * @brief This function is called to get the current gap callback + * + * @return + * - esp_gap_ble_cb_t : callback function + * + */ +esp_gap_ble_cb_t esp_ble_gap_get_callback(void); + +#if (BLE_42_FEATURE_SUPPORT == TRUE) +/** + * @brief This function is called to override the BTA default ADV parameters. + * + * @param[in] adv_data: Pointer to User defined ADV data structure. This + * memory space can not be freed until callback of config_adv_data + * is received. + * + * @return + * - ESP_OK : success + * - other : failed + * + */ +esp_err_t esp_ble_gap_config_adv_data (esp_ble_adv_data_t *adv_data); + + + +/** + * @brief This function is called to set scan parameters + * + * @param[in] scan_params: Pointer to User defined scan_params data structure. This + * memory space can not be freed until callback of set_scan_params + * + * @return + * - ESP_OK : success + * - other : failed + * + */ +esp_err_t esp_ble_gap_set_scan_params(esp_ble_scan_params_t *scan_params); + + +/** + * @brief This procedure keep the device scanning the peer device which advertising on the air + * + * @param[in] duration: Keeping the scanning time, the unit is second. + * + * @return + * - ESP_OK : success + * - other : failed + * + */ +esp_err_t esp_ble_gap_start_scanning(uint32_t duration); + + +/** + * @brief This function call to stop the device scanning the peer device which advertising on the air + * @return + * - ESP_OK : success + * - other : failed + * + */ +esp_err_t esp_ble_gap_stop_scanning(void); + +/** + * @brief This function is called to start advertising. + * + * @param[in] adv_params: pointer to User defined adv_params data structure. + + * @return + * - ESP_OK : success + * - other : failed + * + */ +esp_err_t esp_ble_gap_start_advertising (esp_ble_adv_params_t *adv_params); + + + +/** + * @brief This function is called to stop advertising. + * + * @return + * - ESP_OK : success + * - other : failed + * + */ +esp_err_t esp_ble_gap_stop_advertising(void); +#endif // #if (BLE_42_FEATURE_SUPPORT == TRUE) + + +/** + * @brief Update connection parameters, can only be used when connection is up. + * + * @param[in] params - connection update parameters + * + * @return + * - ESP_OK : success + * - other : failed + * + */ +esp_err_t esp_ble_gap_update_conn_params(esp_ble_conn_update_params_t *params); + + +/** + * @brief This function is to set maximum LE data packet size + * + * @return + * - ESP_OK : success + * - other : failed + * + */ +esp_err_t esp_ble_gap_set_pkt_data_len(esp_bd_addr_t remote_device, uint16_t tx_data_length); + +/** + * @brief This function allows configuring either a Non-Resolvable Private Address or a Static Random Address + * + * @param[in] rand_addr: The address to be configured. Refer to the table below for possible address subtypes: + * + * | address [47:46] | Address Type | + * |-----------------|--------------------------| + * | 0b00 | Non-Resolvable Private | + * | | Address | + * |-----------------|--------------------------| + * | 0b11 | Static Random Address | + * |-----------------|--------------------------| + * + * @return + * - ESP_OK : success + * - other : failed + * + */ +esp_err_t esp_ble_gap_set_rand_addr(esp_bd_addr_t rand_addr); + +/** + * @brief This function clears the random address for the application + * + * @return + * - ESP_OK : success + * - other : failed + * + */ +esp_err_t esp_ble_gap_clear_rand_addr(void); + + + +/** + * @brief Enable/disable privacy (including address resolution) on the local device + * + * @param[in] privacy_enable - enable/disable privacy on remote device. + * + * @return + * - ESP_OK : success + * - other : failed + * + */ +esp_err_t esp_ble_gap_config_local_privacy (bool privacy_enable); + +/** + * @brief set local gap appearance icon + * + * + * @param[in] icon - External appearance value, these values are defined by the Bluetooth SIG, please refer to + * https://www.bluetooth.com/specifications/assigned-numbers/ + * + * @return + * - ESP_OK : success + * - other : failed + * + */ +esp_err_t esp_ble_gap_config_local_icon (uint16_t icon); + +/** +* @brief Add or remove device from white list +* +* @param[in] add_remove: the value is true if added the ble device to the white list, and false remove to the white list. +* @param[in] remote_bda: the remote device address add/remove from the white list. +* @param[in] wl_addr_type: whitelist address type +* @return +* - ESP_OK : success +* - other : failed +* +*/ +esp_err_t esp_ble_gap_update_whitelist(bool add_remove, esp_bd_addr_t remote_bda, esp_ble_wl_addr_type_t wl_addr_type); + +/** +* @brief Clear all white list +* +* @return +* - ESP_OK : success +* - other : failed +* +*/ +esp_err_t esp_ble_gap_clear_whitelist(void); + +/** +* @brief Get the whitelist size in the controller +* +* @param[out] length: the white list length. +* @return +* - ESP_OK : success +* - other : failed +* +*/ +esp_err_t esp_ble_gap_get_whitelist_size(uint16_t *length); +#if (BLE_42_FEATURE_SUPPORT == TRUE) +/** +* @brief This function is called to set the preferred connection +* parameters when default connection parameter is not desired before connecting. +* This API can only be used in the master role. +* +* @param[in] bd_addr: BD address of the peripheral +* @param[in] min_conn_int: minimum preferred connection interval +* @param[in] max_conn_int: maximum preferred connection interval +* @param[in] slave_latency: preferred slave latency +* @param[in] supervision_tout: preferred supervision timeout +* +* @return +* - ESP_OK : success +* - other : failed +* +*/ +esp_err_t esp_ble_gap_set_prefer_conn_params(esp_bd_addr_t bd_addr, + uint16_t min_conn_int, uint16_t max_conn_int, + uint16_t slave_latency, uint16_t supervision_tout); +#endif // #if (BLE_42_FEATURE_SUPPORT == TRUE) +/** + * @brief Set device name to the local device + * Note: This API don't affect the advertising data + * + * @param[in] name - device name. + * + * @return + * - ESP_OK : success + * - other : failed + * + */ +esp_err_t esp_ble_gap_set_device_name(const char *name); + +/** + * @brief Get device name of the local device + * + * @return + * - ESP_OK : success + * - other : failed + * + */ +esp_err_t esp_ble_gap_get_device_name(void); + +/** + * @brief This function is called to get local used address and address type. + * uint8_t *esp_bt_dev_get_address(void) get the public address + * + * @param[in] local_used_addr - current local used ble address (six bytes) + * @param[in] addr_type - ble address type + * + * @return - ESP_OK : success + * - other : failed + * + */ +esp_err_t esp_ble_gap_get_local_used_addr(esp_bd_addr_t local_used_addr, uint8_t * addr_type); +/** + * @brief This function is called to get ADV data for a specific type. + * + * @param[in] adv_data - pointer of ADV data which to be resolved + * @param[in] type - finding ADV data type + * @param[out] length - return the length of ADV data not including type + * + * @return pointer of ADV data + * + */ +uint8_t *esp_ble_resolve_adv_data(uint8_t *adv_data, uint8_t type, uint8_t *length); +#if (BLE_42_FEATURE_SUPPORT == TRUE) +/** + * @brief This function is called to set raw advertising data. User need to fill + * ADV data by self. + * + * @param[in] raw_data : raw advertising data with the format: [Length 1][Data Type 1][Data 1][Length 2][Data Type 2][Data 2] ... + * @param[in] raw_data_len : raw advertising data length , less than 31 bytes + * + * @return + * - ESP_OK : success + * - other : failed + * + */ +esp_err_t esp_ble_gap_config_adv_data_raw(uint8_t *raw_data, uint32_t raw_data_len); + +/** + * @brief This function is called to set raw scan response data. User need to fill + * scan response data by self. + * + * @param[in] raw_data : raw scan response data + * @param[in] raw_data_len : raw scan response data length , less than 31 bytes + * + * @return + * - ESP_OK : success + * - other : failed + */ +esp_err_t esp_ble_gap_config_scan_rsp_data_raw(uint8_t *raw_data, uint32_t raw_data_len); +#endif // #if (BLE_42_FEATURE_SUPPORT == TRUE) + +/** + * @brief This function is called to read the RSSI of remote device. + * The address of link policy results are returned in the gap callback function with + * ESP_GAP_BLE_READ_RSSI_COMPLETE_EVT event. + * + * @param[in] remote_addr : The remote connection device address. + * + * @return + * - ESP_OK : success + * - other : failed + */ +esp_err_t esp_ble_gap_read_rssi(esp_bd_addr_t remote_addr); +#if (BLE_42_FEATURE_SUPPORT == TRUE) +/** + * @brief This function is called to add a device info into the duplicate scan exceptional list. + * + * + * @param[in] type: device info type, it is defined in esp_ble_duplicate_exceptional_info_type_t + * when type is MESH_BEACON_TYPE, MESH_PROV_SRV_ADV or MESH_PROXY_SRV_ADV , device_info is invalid. + * @param[in] device_info: the device information. + * @return + * - ESP_OK : success + * - other : failed + */ +esp_err_t esp_ble_gap_add_duplicate_scan_exceptional_device(esp_ble_duplicate_exceptional_info_type_t type, esp_duplicate_info_t device_info); + +/** + * @brief This function is called to remove a device info from the duplicate scan exceptional list. + * + * + * @param[in] type: device info type, it is defined in esp_ble_duplicate_exceptional_info_type_t + * when type is MESH_BEACON_TYPE, MESH_PROV_SRV_ADV or MESH_PROXY_SRV_ADV , device_info is invalid. + * @param[in] device_info: the device information. + * @return + * - ESP_OK : success + * - other : failed + */ +esp_err_t esp_ble_gap_remove_duplicate_scan_exceptional_device(esp_ble_duplicate_exceptional_info_type_t type, esp_duplicate_info_t device_info); + +/** + * @brief This function is called to clean the duplicate scan exceptional list. + * This API will delete all device information in the duplicate scan exceptional list. + * + * + * @param[in] list_type: duplicate scan exceptional list type, the value can be one or more of esp_duplicate_scan_exceptional_list_type_t. + * + * @return + * - ESP_OK : success + * - other : failed + */ +esp_err_t esp_ble_gap_clean_duplicate_scan_exceptional_list(esp_duplicate_scan_exceptional_list_type_t list_type); +#endif // #if (BLE_42_FEATURE_SUPPORT == TRUE) + +#if (SMP_INCLUDED == TRUE) +/** +* @brief Set a GAP security parameter value. Overrides the default value. +* +* Secure connection is highly recommended to avoid some major +* vulnerabilities like 'Impersonation in the Pin Pairing Protocol' +* (CVE-2020-26555) and 'Authentication of the LE Legacy Pairing +* Protocol'. +* +* To accept only `secure connection mode`, it is necessary do as following: +* +* 1. Set bit `ESP_LE_AUTH_REQ_SC_ONLY` (`param_type` is +* `ESP_BLE_SM_AUTHEN_REQ_MODE`), bit `ESP_LE_AUTH_BOND` and bit +* `ESP_LE_AUTH_REQ_MITM` is optional as required. +* +* 2. Set to `ESP_BLE_ONLY_ACCEPT_SPECIFIED_AUTH_ENABLE` (`param_type` is +* `ESP_BLE_SM_ONLY_ACCEPT_SPECIFIED_SEC_AUTH`). +* +* @param[in] param_type : the type of the param which to be set +* @param[in] value : the param value +* @param[in] len : the length of the param value +* +* @return - ESP_OK : success +* - other : failed +* +*/ +esp_err_t esp_ble_gap_set_security_param(esp_ble_sm_param_t param_type, + void *value, uint8_t len); + +/** +* @brief Grant security request access. +* +* @param[in] bd_addr : BD address of the peer +* @param[in] accept : accept the security request or not +* +* @return - ESP_OK : success +* - other : failed +* +*/ +esp_err_t esp_ble_gap_security_rsp(esp_bd_addr_t bd_addr, bool accept); + + +/** +* @brief Set a gap parameter value. Use this function to change +* the default GAP parameter values. +* +* @param[in] bd_addr : the address of the peer device need to encryption +* @param[in] sec_act : This is the security action to indicate +* what kind of BLE security level is required for +* the BLE link if the BLE is supported +* +* @return - ESP_OK : success +* - other : failed +* +*/ +esp_err_t esp_ble_set_encryption(esp_bd_addr_t bd_addr, esp_ble_sec_act_t sec_act); + +/** +* @brief Reply the key value to the peer device in the legacy connection stage. +* +* @param[in] bd_addr : BD address of the peer +* @param[in] accept : passkey entry successful or declined. +* @param[in] passkey : passkey value, must be a 6 digit number, +* can be lead by 0. +* +* @return - ESP_OK : success +* - other : failed +* +*/ +esp_err_t esp_ble_passkey_reply(esp_bd_addr_t bd_addr, bool accept, uint32_t passkey); + + +/** +* @brief Reply the confirm value to the peer device in the secure connection stage. +* +* @param[in] bd_addr : BD address of the peer device +* @param[in] accept : numbers to compare are the same or different. +* +* @return - ESP_OK : success +* - other : failed +* +*/ +esp_err_t esp_ble_confirm_reply(esp_bd_addr_t bd_addr, bool accept); + +/** +* @brief Removes a device from the security database list of +* peer device. It manages unpairing event while connected. +* +* @param[in] bd_addr : BD address of the peer device +* +* @return - ESP_OK : success +* - other : failed +* +*/ +esp_err_t esp_ble_remove_bond_device(esp_bd_addr_t bd_addr); + +/** +* @brief Get the device number from the security database list of peer device. +* It will return the device bonded number immediately. +* +* @return - >= 0 : bonded devices number. +* - ESP_FAIL : failed +* +*/ +int esp_ble_get_bond_device_num(void); + + +/** +* @brief Get the device from the security database list of peer device. +* It will return the device bonded information immediately. +* @param[inout] dev_num: Indicate the dev_list array(buffer) size as input. +* If dev_num is large enough, it means the actual number as output. +* Suggest that dev_num value equal to esp_ble_get_bond_device_num(). +* +* @param[out] dev_list: an array(buffer) of `esp_ble_bond_dev_t` type. Use for storing the bonded devices address. +* The dev_list should be allocated by who call this API. +* @return - ESP_OK : success +* - other : failed +* +*/ +esp_err_t esp_ble_get_bond_device_list(int *dev_num, esp_ble_bond_dev_t *dev_list); + +/** +* @brief This function is called to provide the OOB data for +* SMP in response to ESP_GAP_BLE_OOB_REQ_EVT +* +* @param[in] bd_addr: BD address of the peer device. +* @param[in] TK: Temporary Key value, the TK value shall be a 128-bit random number +* @param[in] len: length of temporary key, should always be 128-bit +* +* @return - ESP_OK : success +* - other : failed +* +*/ +esp_err_t esp_ble_oob_req_reply(esp_bd_addr_t bd_addr, uint8_t *TK, uint8_t len); + +/** +* @brief This function is called to provide the OOB data for +* SMP in response to ESP_GAP_BLE_SC_OOB_REQ_EVT +* +* @param[in] bd_addr: BD address of the peer device. +* @param[in] p_c: Confirmation value, it shall be a 128-bit random number +* @param[in] p_r: Randomizer value, it should be a 128-bit random number +* +* @return - ESP_OK : success +* - other : failed +* +*/ +esp_err_t esp_ble_sc_oob_req_reply(esp_bd_addr_t bd_addr, uint8_t p_c[16], uint8_t p_r[16]); + +/** +* @brief This function is called to create the OOB data for +* SMP when secure connection +* +* @return - ESP_OK : success +* - other : failed +* +*/ +esp_err_t esp_ble_create_sc_oob_data(void); +#endif /* #if (SMP_INCLUDED == TRUE) */ + +/** +* @brief This function is to disconnect the physical connection of the peer device +* gattc may have multiple virtual GATT server connections when multiple app_id registered. +* esp_ble_gattc_close (esp_gatt_if_t gattc_if, uint16_t conn_id) only close one virtual GATT server connection. +* if there exist other virtual GATT server connections, it does not disconnect the physical connection. +* esp_ble_gap_disconnect(esp_bd_addr_t remote_device) disconnect the physical connection directly. +* +* +* +* @param[in] remote_device : BD address of the peer device +* +* @return - ESP_OK : success +* - other : failed +* +*/ +esp_err_t esp_ble_gap_disconnect(esp_bd_addr_t remote_device); + +/** +* @brief This function is called to read the connection +* parameters information of the device +* +* @param[in] bd_addr: BD address of the peer device. +* @param[out] conn_params: the connection parameters information +* +* @return - ESP_OK : success +* - other : failed +* +*/ +esp_err_t esp_ble_get_current_conn_params(esp_bd_addr_t bd_addr, esp_gap_conn_params_t *conn_params); + +/** +* @brief BLE set channels +* +* @param[in] channels : The n th such field (in the range 0 to 36) contains the value for the link layer channel index n. +* 0 means channel n is bad. +* 1 means channel n is unknown. +* The most significant bits are reserved and shall be set to 0. +* At least one channel shall be marked as unknown. +* +* @return - ESP_OK : success +* - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled +* - other : failed +* +*/ +esp_err_t esp_gap_ble_set_channels(esp_gap_ble_channels channels); + +/** +* @brief This function is called to authorized a link after Authentication(MITM protection) +* +* @param[in] bd_addr: BD address of the peer device. +* @param[out] authorize: Authorized the link or not. +* +* @return - ESP_OK : success +* - other : failed +* +*/ +esp_err_t esp_gap_ble_set_authorization(esp_bd_addr_t bd_addr, bool authorize); + +#if (BLE_50_FEATURE_SUPPORT == TRUE) + +/** +* @brief This function is used to read the current transmitter PHY +* and receiver PHY on the connection identified by remote address. +* +* @param[in] bd_addr : BD address of the peer device +* +* @return - ESP_OK : success +* - other : failed +* +*/ +esp_err_t esp_ble_gap_read_phy(esp_bd_addr_t bd_addr); + +/** +* @brief This function is used to allows the Host to specify its preferred values +* for the transmitter PHY and receiver PHY to be used for all subsequent connections +* over the LE transport. +* +* @param[in] tx_phy_mask : indicates the transmitter PHYs that the Host prefers the Controller to use +* @param[in] rx_phy_mask : indicates the receiver PHYs that the Host prefers the Controller to use +* +* @return - ESP_OK : success +* - other : failed +* +*/ +esp_err_t esp_ble_gap_set_preferred_default_phy(esp_ble_gap_phy_mask_t tx_phy_mask, esp_ble_gap_phy_mask_t rx_phy_mask); +/** +* @brief This function is used to set the PHY preferences for the connection identified by the remote address. +* The Controller might not be able to make the change (e.g. because the peer does not support the requested PHY) +* or may decide that the current PHY is preferable. +* +* @param[in] bd_addr : remote address +* @param[in] all_phys_mask : a bit field that allows the Host to specify +* @param[in] tx_phy_mask : a bit field that indicates the transmitter PHYs that the Host prefers the Controller to use +* @param[in] rx_phy_mask : a bit field that indicates the receiver PHYs that the Host prefers the Controller to use +* @param[in] phy_options : a bit field that allows the Host to specify options for PHYs +* +* @return - ESP_OK : success +* - other : failed +* +*/ +esp_err_t esp_ble_gap_set_preferred_phy(esp_bd_addr_t bd_addr, + esp_ble_gap_all_phys_t all_phys_mask, + esp_ble_gap_phy_mask_t tx_phy_mask, + esp_ble_gap_phy_mask_t rx_phy_mask, + esp_ble_gap_prefer_phy_options_t phy_options); + +/** +* @brief This function is used by the Host to set the random device address specified by the Random_Address parameter. +* +* @param[in] instance : Used to identify an advertising set +* @param[in] rand_addr : Random Device Address +* +* @return - ESP_OK : success +* - other : failed +* +*/ +esp_err_t esp_ble_gap_ext_adv_set_rand_addr(uint8_t instance, esp_bd_addr_t rand_addr); + +/** +* @brief This function is used by the Host to set the advertising parameters. +* +* @param[in] instance : identifies the advertising set whose parameters are being configured. +* @param[in] params : advertising parameters +* +* @return - ESP_OK : success +* - other : failed +* +*/ +esp_err_t esp_ble_gap_ext_adv_set_params(uint8_t instance, const esp_ble_gap_ext_adv_params_t *params); + +/** +* @brief This function is used to set the data used in advertising PDUs that have a data field +* +* @param[in] instance : identifies the advertising set whose data are being configured +* @param[in] length : data length +* @param[in] data : data information +* +* @return - ESP_OK : success +* - other : failed +* +*/ +esp_err_t esp_ble_gap_config_ext_adv_data_raw(uint8_t instance, uint16_t length, const uint8_t *data); + +/** +* @brief This function is used to provide scan response data used in scanning response PDUs +* +* @param[in] instance : identifies the advertising set whose response data are being configured. +* @param[in] length : responsedata length +* @param[in] scan_rsp_data : response data information +* +* @return - ESP_OK : success +* - other : failed +* +*/ +esp_err_t esp_ble_gap_config_ext_scan_rsp_data_raw(uint8_t instance, uint16_t length, + const uint8_t *scan_rsp_data); +/** +* @brief This function is used to request the Controller to enable one or more +* advertising sets using the advertising sets identified by the instance parameter. +* +* @param[in] num_adv : Number of advertising sets to enable or disable +* @param[in] ext_adv : adv parameters +* +* @return - ESP_OK : success +* - other : failed +* +*/ +esp_err_t esp_ble_gap_ext_adv_start(uint8_t num_adv, const esp_ble_gap_ext_adv_t *ext_adv); + +/** +* @brief This function is used to request the Controller to disable one or more +* advertising sets using the advertising sets identified by the instance parameter. +* +* @param[in] num_adv : Number of advertising sets to enable or disable +* @param[in] ext_adv_inst : ext adv instance +* +* @return - ESP_OK : success +* - other : failed +* +*/ +esp_err_t esp_ble_gap_ext_adv_stop(uint8_t num_adv, const uint8_t *ext_adv_inst); + +/** +* @brief This function is used to remove an advertising set from the Controller. +* +* @param[in] instance : Used to identify an advertising set +* +* @return - ESP_OK : success +* - other : failed +* +*/ +esp_err_t esp_ble_gap_ext_adv_set_remove(uint8_t instance); + +/** +* @brief This function is used to remove all existing advertising sets from the Controller. +* +* +* @return - ESP_OK : success +* - other : failed +* +*/ +esp_err_t esp_ble_gap_ext_adv_set_clear(void); + +/** +* @brief This function is used by the Host to set the parameters for periodic advertising. +* +* @param[in] instance : identifies the advertising set whose periodic advertising parameters are being configured. +* @param[in] params : periodic adv parameters +* +* @return - ESP_OK : success +* - other : failed +* +*/ +esp_err_t esp_ble_gap_periodic_adv_set_params(uint8_t instance, const esp_ble_gap_periodic_adv_params_t *params); + +#if (CONFIG_BT_BLE_FEAT_PERIODIC_ADV_ENH) +/** +* @brief This function is used to set the data used in periodic advertising PDUs. +* +* @param[in] instance : identifies the advertising set whose periodic advertising parameters are being configured. +* @param[in] length : the length of periodic data +* @param[in] data : periodic data information +* @param[in] only_update_did : If true, only the Advertising DID of the periodic advertising will be updated, and the length and data parameters will be ignored. +* +* @return - ESP_OK : success +* - other : failed +* +*/ +esp_err_t esp_ble_gap_config_periodic_adv_data_raw(uint8_t instance, uint16_t length, + const uint8_t *data, bool only_update_did); +#else +/** +* @brief This function is used to set the data used in periodic advertising PDUs. +* +* @param[in] instance : identifies the advertising set whose periodic advertising parameters are being configured. +* @param[in] length : the length of periodic data +* @param[in] data : periodic data information +* +* @return - ESP_OK : success +* - other : failed +* +*/ +esp_err_t esp_ble_gap_config_periodic_adv_data_raw(uint8_t instance, uint16_t length, + const uint8_t *data); +#endif + +#if (CONFIG_BT_BLE_FEAT_PERIODIC_ADV_ENH) +/** +* @brief This function is used to request the Controller to enable the periodic advertising for the advertising set specified +* +* @param[in] instance : Used to identify an advertising set +* @param[in] include_adi : If true, the ADI (Advertising Data Info) field will be included in AUX_SYNC_IND PDUs +* +* @return - ESP_OK : success +* - other : failed +* +*/ +esp_err_t esp_ble_gap_periodic_adv_start(uint8_t instance,bool include_adi); +#else +/** +* @brief This function is used to request the Controller to enable the periodic advertising for the advertising set specified +* +* @param[in] instance : Used to identify an advertising set +* +* @return - ESP_OK : success +* - other : failed +* +*/ +esp_err_t esp_ble_gap_periodic_adv_start(uint8_t instance); +#endif + +/** +* @brief This function is used to request the Controller to disable the periodic advertising for the advertising set specified +* +* @param[in] instance : Used to identify an advertising set +* +* @return - ESP_OK : success +* - other : failed +* +*/ +esp_err_t esp_ble_gap_periodic_adv_stop(uint8_t instance); + +/** +* @brief This function is used to set the extended scan parameters to be used on the advertising channels. +* +* @param[in] params : scan parameters +* +* @return - ESP_OK : success +* - other : failed +* +*/ +esp_err_t esp_ble_gap_set_ext_scan_params(const esp_ble_ext_scan_params_t *params); + +/** +* @brief This function is used to enable scanning. +* +* @param[in] duration Scan duration time, where Time = N * 10 ms. Range: 0x0001 to 0xFFFF. +* @param[in] period Time interval from when the Controller started its last Scan Duration until it begins the subsequent Scan Duration. +* Time = N * 1.28 sec. Range: 0x0001 to 0xFFFF. +* +* @return - ESP_OK : success +* - other : failed +* +*/ +esp_err_t esp_ble_gap_start_ext_scan(uint32_t duration, uint16_t period); + +/** +* @brief This function is used to disable scanning. +* +* @return - ESP_OK : success +* - other : failed +* +*/ +esp_err_t esp_ble_gap_stop_ext_scan(void); + +/** +* @brief This function is used to synchronize with periodic advertising from an advertiser and begin receiving periodic advertising packets. +* +* @param[in] params : sync parameters +* +* @return - ESP_OK : success +* - other : failed +* +*/ +esp_err_t esp_ble_gap_periodic_adv_create_sync(const esp_ble_gap_periodic_adv_sync_params_t *params); + +/** +* @brief This function is used to cancel the LE_Periodic_Advertising_Create_Sync command while it is pending. +* +* +* @return - ESP_OK : success +* - other : failed +* +*/ +esp_err_t esp_ble_gap_periodic_adv_sync_cancel(void); + +/** +* @brief This function is used to stop reception of the periodic advertising identified by the Sync Handle parameter. +* +* @param[in] sync_handle : identify the periodic advertiser +* +* @return - ESP_OK : success +* - other : failed +* +*/ +esp_err_t esp_ble_gap_periodic_adv_sync_terminate(uint16_t sync_handle); + +/** +* @brief This function is used to add a single device to the Periodic Advertiser list stored in the Controller +* +* @param[in] addr_type : address type +* @param[in] addr : Device Address +* @param[in] sid : Advertising SID subfield in the ADI field used to identify the Periodic Advertising +* +* @return - ESP_OK : success +* - other : failed +* +*/ +esp_err_t esp_ble_gap_periodic_adv_add_dev_to_list(esp_ble_addr_type_t addr_type, + esp_bd_addr_t addr, + uint8_t sid); + +/** +* @brief This function is used to remove one device from the list of Periodic Advertisers stored in the Controller. +* Removals from the Periodic Advertisers List take effect immediately. +* +* @param[in] addr_type : address type +* @param[in] addr : Device Address +* @param[in] sid : Advertising SID subfield in the ADI field used to identify the Periodic Advertising +* +* @return - ESP_OK : success +* - other : failed +* +*/ +esp_err_t esp_ble_gap_periodic_adv_remove_dev_from_list(esp_ble_addr_type_t addr_type, + esp_bd_addr_t addr, + uint8_t sid); +/** +* @brief This function is used to remove all devices from the list of Periodic Advertisers in the Controller. +* +* @return - ESP_OK : success +* - other : failed +* +*/ +esp_err_t esp_ble_gap_periodic_adv_clear_dev(void); + +/** +* @brief This function is used to set aux connection parameters +* +* @param[in] addr : device address +* @param[in] phy_mask : indicates the PHY(s) on which the advertising packets should be received on the primary advertising channel and the PHYs for which connection parameters have been specified. +* @param[in] phy_1m_conn_params : Scan connectable advertisements on the LE 1M PHY. Connection parameters for the LE 1M PHY are provided. +* @param[in] phy_2m_conn_params : Connection parameters for the LE 2M PHY are provided. +* @param[in] phy_coded_conn_params : Scan connectable advertisements on the LE Coded PHY. Connection parameters for the LE Coded PHY are provided. +* +* @return - ESP_OK : success +* - other : failed +* +*/ +esp_err_t esp_ble_gap_prefer_ext_connect_params_set(esp_bd_addr_t addr, + esp_ble_gap_phy_mask_t phy_mask, + const esp_ble_gap_conn_params_t *phy_1m_conn_params, + const esp_ble_gap_conn_params_t *phy_2m_conn_params, + const esp_ble_gap_conn_params_t *phy_coded_conn_params); + +#endif //#if (BLE_50_FEATURE_SUPPORT == TRUE) + +#if (BLE_FEAT_PERIODIC_ADV_SYNC_TRANSFER == TRUE) +/** +* @brief This function is used to set periodic advertising receive enable +* +* @param[in] sync_handle : Handle of periodic advertising sync +* @param[in] enable : Determines whether reporting and duplicate filtering are enabled or disabled +* +* @return - ESP_OK : success +* - other : failed +* +*/ +esp_err_t esp_ble_gap_periodic_adv_recv_enable(uint16_t sync_handle, uint8_t enable); + +/** +* @brief This function is used to transfer periodic advertising sync +* +* @param[in] addr : Peer device address +* @param[in] service_data : Service data used by Host +* @param[in] sync_handle : Handle of periodic advertising sync +* +* @return - ESP_OK : success +* - other : failed +* +*/ +esp_err_t esp_ble_gap_periodic_adv_sync_trans(esp_bd_addr_t addr, + uint16_t service_data, uint16_t sync_handle); + +/** +* @brief This function is used to transfer periodic advertising set info +* +* @param[in] addr : Peer device address +* @param[in] service_data : Service data used by Host +* @param[in] adv_handle : Handle of advertising set +* +* @return - ESP_OK : success +* - other : failed +* +*/ +esp_err_t esp_ble_gap_periodic_adv_set_info_trans(esp_bd_addr_t addr, + uint16_t service_data, uint8_t adv_handle); + +/** +* @brief This function is used to set periodic advertising sync transfer params +* +* @param[in] addr : Peer device address +* @param[in] params : Params of periodic advertising sync transfer +* +* @return - ESP_OK : success +* - other : failed +* +*/ +esp_err_t esp_ble_gap_set_periodic_adv_sync_trans_params(esp_bd_addr_t addr, + const esp_ble_gap_past_params_t *params); +#endif //#if (BLE_FEAT_PERIODIC_ADV_SYNC_TRANSFER == TRUE) + +#if (BLE_42_FEATURE_SUPPORT == TRUE) + +/** +* @brief This function is used to start a test where the DUT generates reference packets +* at a fixed interval. +* +* @param[in] tx_params : DTM Transmitter parameters +* +* @return - ESP_OK : success +* - other : failed +* +*/ +esp_err_t esp_ble_dtm_tx_start(const esp_ble_dtm_tx_t *tx_params); + +/** +* @brief This function is used to start a test where the DUT receives test reference packets +* at a fixed interval. +* +* @param[in] rx_params : DTM Receiver parameters +* +* @return - ESP_OK : success +* - other : failed +* +*/ +esp_err_t esp_ble_dtm_rx_start(const esp_ble_dtm_rx_t *rx_params); +#endif //#if (BLE_42_FEATURE_SUPPORT == TRUE) + +#if (BLE_50_FEATURE_SUPPORT == TRUE) + +/** +* @brief This function is used to start a test where the DUT generates reference packets +* at a fixed interval. +* +* @param[in] tx_params : DTM Transmitter parameters +* +* @return - ESP_OK : success +* - other : failed +* +*/ +esp_err_t esp_ble_dtm_enh_tx_start(const esp_ble_dtm_enh_tx_t *tx_params); + +/** +* @brief This function is used to start a test where the DUT receives test reference packets +* at a fixed interval. +* +* @param[in] rx_params : DTM Receiver parameters +* +* @return - ESP_OK : success +* - other : failed +* +*/ +esp_err_t esp_ble_dtm_enh_rx_start(const esp_ble_dtm_enh_rx_t *rx_params); +#endif // #if (BLE_50_FEATURE_SUPPORT == TRUE) + +/** +* @brief This function is used to stop any test which is in progress +* +* +* @return - ESP_OK : success +* - other : failed +* +*/ +esp_err_t esp_ble_dtm_stop(void); + +/** +* @brief This function is used to clear legacy advertising +* +* +* @return - ESP_OK : success +* - other : failed +* +*/ +esp_err_t esp_ble_gap_clear_advertising(void); + +#ifdef __cplusplus +} +#endif + +#endif /* __ESP_GAP_BLE_API_H__ */ diff --git a/lib/bt/host/bluedroid/api/include/api/esp_gap_bt_api.h b/lib/bt/host/bluedroid/api/include/api/esp_gap_bt_api.h new file mode 100644 index 00000000..6396534f --- /dev/null +++ b/lib/bt/host/bluedroid/api/include/api/esp_gap_bt_api.h @@ -0,0 +1,916 @@ +/* + * SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __ESP_GAP_BT_API_H__ +#define __ESP_GAP_BT_API_H__ + +#include +#include "esp_err.h" +#include "esp_bt_defs.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/// RSSI threshold +#define ESP_BT_GAP_RSSI_HIGH_THRLD -20 /*!< High RSSI threshold */ +#define ESP_BT_GAP_RSSI_LOW_THRLD -45 /*!< Low RSSI threshold */ + +/// Class of device +typedef struct { + uint32_t reserved_2: 2; /*!< undefined */ + uint32_t minor: 6; /*!< minor class */ + uint32_t major: 5; /*!< major class */ + uint32_t service: 11; /*!< service class */ + uint32_t reserved_8: 8; /*!< undefined */ +} esp_bt_cod_t; + +/// class of device settings +typedef enum { + ESP_BT_SET_COD_MAJOR_MINOR = 0x01, /*!< overwrite major, minor class */ + ESP_BT_SET_COD_SERVICE_CLASS = 0x02, /*!< set the bits in the input, the current bit will remain */ + ESP_BT_CLR_COD_SERVICE_CLASS = 0x04, /*!< clear the bits in the input, others will remain */ + ESP_BT_SET_COD_ALL = 0x08, /*!< overwrite major, minor, set the bits in service class */ + ESP_BT_INIT_COD = 0x0a, /*!< overwrite major, minor, and service class */ +} esp_bt_cod_mode_t; + +#define ESP_BT_GAP_AFH_CHANNELS_LEN 10 +typedef uint8_t esp_bt_gap_afh_channels[ESP_BT_GAP_AFH_CHANNELS_LEN]; + + +/// Discoverability and Connectability mode +typedef enum { + ESP_BT_NON_CONNECTABLE, /*!< Non-connectable */ + ESP_BT_CONNECTABLE, /*!< Connectable */ +} esp_bt_connection_mode_t; + +typedef enum { + ESP_BT_NON_DISCOVERABLE, /*!< Non-discoverable */ + ESP_BT_LIMITED_DISCOVERABLE, /*!< Limited Discoverable */ + ESP_BT_GENERAL_DISCOVERABLE, /*!< General Discoverable */ +} esp_bt_discovery_mode_t; + +/// Bluetooth Device Property type +typedef enum { + ESP_BT_GAP_DEV_PROP_BDNAME = 1, /*!< Bluetooth device name, value type is int8_t [] */ + ESP_BT_GAP_DEV_PROP_COD, /*!< Class of Device, value type is uint32_t */ + ESP_BT_GAP_DEV_PROP_RSSI, /*!< Received Signal strength Indication, value type is int8_t, ranging from -128 to 127 */ + ESP_BT_GAP_DEV_PROP_EIR, /*!< Extended Inquiry Response, value type is uint8_t [] */ +} esp_bt_gap_dev_prop_type_t; + +/// Maximum bytes of Bluetooth device name +#define ESP_BT_GAP_MAX_BDNAME_LEN (248) + +/// Maximum size of EIR Significant part +#define ESP_BT_GAP_EIR_DATA_LEN (240) + +/// Bluetooth Device Property Descriptor +typedef struct { + esp_bt_gap_dev_prop_type_t type; /*!< Device property type */ + int len; /*!< Device property value length */ + void *val; /*!< Device property value */ +} esp_bt_gap_dev_prop_t; + +/// Extended Inquiry Response data type +#define ESP_BT_EIR_TYPE_FLAGS 0x01 /*!< Flag with information such as BR/EDR and LE support */ +#define ESP_BT_EIR_TYPE_INCMPL_16BITS_UUID 0x02 /*!< Incomplete list of 16-bit service UUIDs */ +#define ESP_BT_EIR_TYPE_CMPL_16BITS_UUID 0x03 /*!< Complete list of 16-bit service UUIDs */ +#define ESP_BT_EIR_TYPE_INCMPL_32BITS_UUID 0x04 /*!< Incomplete list of 32-bit service UUIDs */ +#define ESP_BT_EIR_TYPE_CMPL_32BITS_UUID 0x05 /*!< Complete list of 32-bit service UUIDs */ +#define ESP_BT_EIR_TYPE_INCMPL_128BITS_UUID 0x06 /*!< Incomplete list of 128-bit service UUIDs */ +#define ESP_BT_EIR_TYPE_CMPL_128BITS_UUID 0x07 /*!< Complete list of 128-bit service UUIDs */ +#define ESP_BT_EIR_TYPE_SHORT_LOCAL_NAME 0x08 /*!< Shortened Local Name */ +#define ESP_BT_EIR_TYPE_CMPL_LOCAL_NAME 0x09 /*!< Complete Local Name */ +#define ESP_BT_EIR_TYPE_TX_POWER_LEVEL 0x0a /*!< Tx power level, value is 1 octet ranging from -127 to 127, unit is dBm*/ +#define ESP_BT_EIR_TYPE_URL 0x24 /*!< Uniform resource identifier */ +#define ESP_BT_EIR_TYPE_MANU_SPECIFIC 0xff /*!< Manufacturer specific data */ +#define ESP_BT_EIR_TYPE_MAX_NUM 12 /*!< MAX number of EIR type */ + +typedef uint8_t esp_bt_eir_type_t; + +/* ACL Packet Types */ +#define ESP_BT_ACL_PKT_TYPES_MASK_DM1 0x0008 +#define ESP_BT_ACL_PKT_TYPES_MASK_DH1 0x0010 +#define ESP_BT_ACL_PKT_TYPES_MASK_DM3 0x0400 +#define ESP_BT_ACL_PKT_TYPES_MASK_DH3 0x0800 +#define ESP_BT_ACL_PKT_TYPES_MASK_DM5 0x4000 +#define ESP_BT_ACL_PKT_TYPES_MASK_DH5 0x8000 +#define ESP_BT_ACL_PKT_TYPES_MASK_NO_2_DH1 0x0002 +#define ESP_BT_ACL_PKT_TYPES_MASK_NO_3_DH1 0x0004 +#define ESP_BT_ACL_PKT_TYPES_MASK_NO_2_DH3 0x0100 +#define ESP_BT_ACL_PKT_TYPES_MASK_NO_3_DH3 0x0200 +#define ESP_BT_ACL_PKT_TYPES_MASK_NO_2_DH5 0x1000 +#define ESP_BT_ACL_PKT_TYPES_MASK_NO_3_DH5 0x2000 + +// DM1 cann not be disabled. All options are mandatory to include DM1. +#define ESP_BT_ACL_DM1_ONLY (ESP_BT_ACL_PKT_TYPES_MASK_DM1 | 0x330e) /* 0x330e */ +#define ESP_BT_ACL_DH1_ONLY (ESP_BT_ACL_PKT_TYPES_MASK_DH1 | 0x330e) /* 0x331e */ +#define ESP_BT_ACL_DM3_ONLY (ESP_BT_ACL_PKT_TYPES_MASK_DM3 | 0x330e) /* 0x370e */ +#define ESP_BT_ACL_DH3_ONLY (ESP_BT_ACL_PKT_TYPES_MASK_DH3 | 0x330e) /* 0x3b0e */ +#define ESP_BT_ACL_DM5_ONLY (ESP_BT_ACL_PKT_TYPES_MASK_DM5 | 0x330e) /* 0x730e */ +#define ESP_BT_ACL_DH5_ONLY (ESP_BT_ACL_PKT_TYPES_MASK_DH5 | 0x330e) /* 0xb30e */ +#define ESP_BT_ACL_2_DH1_ONLY (~ESP_BT_ACL_PKT_TYPES_MASK_NO_2_DH1 & 0x330e) /* 0x330c */ +#define ESP_BT_ACL_3_DH1_ONLY (~ESP_BT_ACL_PKT_TYPES_MASK_NO_3_DH1 & 0x330e) /* 0x330a */ +#define ESP_BT_ACL_2_DH3_ONLY (~ESP_BT_ACL_PKT_TYPES_MASK_NO_2_DH3 & 0x330e) /* 0x320e */ +#define ESP_BT_ACL_3_DH3_ONLY (~ESP_BT_ACL_PKT_TYPES_MASK_NO_3_DH3 & 0x330e) /* 0x310e */ +#define ESP_BT_ACL_2_DH5_ONLY (~ESP_BT_ACL_PKT_TYPES_MASK_NO_2_DH5 & 0x330e) /* 0x230e */ +#define ESP_BT_ACL_3_DH5_ONLY (~ESP_BT_ACL_PKT_TYPES_MASK_NO_3_DH5 & 0x330e) /* 0x130e */ + +typedef uint16_t esp_bt_acl_pkt_type_t; + +/* ESP_BT_EIR_FLAG bit definition */ +#define ESP_BT_EIR_FLAG_LIMIT_DISC (0x01 << 0) +#define ESP_BT_EIR_FLAG_GEN_DISC (0x01 << 1) +#define ESP_BT_EIR_FLAG_BREDR_NOT_SPT (0x01 << 2) +#define ESP_BT_EIR_FLAG_DMT_CONTROLLER_SPT (0x01 << 3) +#define ESP_BT_EIR_FLAG_DMT_HOST_SPT (0x01 << 4) + +#define ESP_BT_EIR_MAX_LEN 240 +/// EIR data content, according to "Supplement to the Bluetooth Core Specification" +typedef struct { + bool fec_required; /*!< FEC is required or not, true by default */ + bool include_txpower; /*!< EIR data include TX power, false by default */ + bool include_uuid; /*!< EIR data include UUID, false by default */ + bool include_name; /*!< EIR data include device name, true by default */ + uint8_t flag; /*!< EIR flags, see ESP_BT_EIR_FLAG for details, EIR will not include flag if it is 0, 0 by default */ + uint16_t manufacturer_len; /*!< Manufacturer data length, 0 by default */ + uint8_t *p_manufacturer_data; /*!< Manufacturer data point */ + uint16_t url_len; /*!< URL length, 0 by default */ + uint8_t *p_url; /*!< URL point */ +} esp_bt_eir_data_t; + +/// Major service class field of Class of Device, mutiple bits can be set +typedef enum { + ESP_BT_COD_SRVC_NONE = 0, /*!< None indicates an invalid value */ + ESP_BT_COD_SRVC_LMTD_DISCOVER = 0x1, /*!< Limited Discoverable Mode */ + ESP_BT_COD_SRVC_POSITIONING = 0x8, /*!< Positioning (Location identification) */ + ESP_BT_COD_SRVC_NETWORKING = 0x10, /*!< Networking, e.g. LAN, Ad hoc */ + ESP_BT_COD_SRVC_RENDERING = 0x20, /*!< Rendering, e.g. Printing, Speakers */ + ESP_BT_COD_SRVC_CAPTURING = 0x40, /*!< Capturing, e.g. Scanner, Microphone */ + ESP_BT_COD_SRVC_OBJ_TRANSFER = 0x80, /*!< Object Transfer, e.g. v-Inbox, v-Folder */ + ESP_BT_COD_SRVC_AUDIO = 0x100, /*!< Audio, e.g. Speaker, Microphone, Headset service */ + ESP_BT_COD_SRVC_TELEPHONY = 0x200, /*!< Telephony, e.g. Cordless telephony, Modem, Headset service */ + ESP_BT_COD_SRVC_INFORMATION = 0x400, /*!< Information, e.g., WEB-server, WAP-server */ +} esp_bt_cod_srvc_t; + +typedef enum{ + ESP_BT_PIN_TYPE_VARIABLE = 0, /*!< Refer to BTM_PIN_TYPE_VARIABLE */ + ESP_BT_PIN_TYPE_FIXED = 1, /*!< Refer to BTM_PIN_TYPE_FIXED */ +} esp_bt_pin_type_t; + +#define ESP_BT_PIN_CODE_LEN 16 /*!< Max pin code length */ +typedef uint8_t esp_bt_pin_code_t[ESP_BT_PIN_CODE_LEN]; /*!< Pin Code (upto 128 bits) MSB is 0 */ + +typedef enum { + ESP_BT_SP_IOCAP_MODE = 0, /*!< Set IO mode */ + //ESP_BT_SP_OOB_DATA, //TODO /*!< Set OOB data */ +} esp_bt_sp_param_t; + +/* relate to BTM_IO_CAP_xxx in stack/btm_api.h */ +#define ESP_BT_IO_CAP_OUT 0 /*!< DisplayOnly */ /* relate to BTM_IO_CAP_OUT in stack/btm_api.h */ +#define ESP_BT_IO_CAP_IO 1 /*!< DisplayYesNo */ /* relate to BTM_IO_CAP_IO in stack/btm_api.h */ +#define ESP_BT_IO_CAP_IN 2 /*!< KeyboardOnly */ /* relate to BTM_IO_CAP_IN in stack/btm_api.h */ +#define ESP_BT_IO_CAP_NONE 3 /*!< NoInputNoOutput */ /* relate to BTM_IO_CAP_NONE in stack/btm_api.h */ +typedef uint8_t esp_bt_io_cap_t; /*!< Combination of the IO Capability */ + + +/* BTM Power manager modes */ +#define ESP_BT_PM_MD_ACTIVE 0x00 /*!< Active mode */ +#define ESP_BT_PM_MD_HOLD 0x01 /*!< Hold mode */ +#define ESP_BT_PM_MD_SNIFF 0x02 /*!< Sniff mode */ +#define ESP_BT_PM_MD_PARK 0x03 /*!< Park state */ +typedef uint8_t esp_bt_pm_mode_t; + + + +/// Bits of major service class field +#define ESP_BT_COD_SRVC_BIT_MASK (0xffe000) /*!< Major service bit mask */ +#define ESP_BT_COD_SRVC_BIT_OFFSET (13) /*!< Major service bit offset */ + +/// Major device class field of Class of Device +typedef enum { + ESP_BT_COD_MAJOR_DEV_MISC = 0, /*!< Miscellaneous */ + ESP_BT_COD_MAJOR_DEV_COMPUTER = 1, /*!< Computer */ + ESP_BT_COD_MAJOR_DEV_PHONE = 2, /*!< Phone(cellular, cordless, pay phone, modem */ + ESP_BT_COD_MAJOR_DEV_LAN_NAP = 3, /*!< LAN, Network Access Point */ + ESP_BT_COD_MAJOR_DEV_AV = 4, /*!< Audio/Video(headset, speaker, stereo, video display, VCR */ + ESP_BT_COD_MAJOR_DEV_PERIPHERAL = 5, /*!< Peripheral(mouse, joystick, keyboard) */ + ESP_BT_COD_MAJOR_DEV_IMAGING = 6, /*!< Imaging(printer, scanner, camera, display */ + ESP_BT_COD_MAJOR_DEV_WEARABLE = 7, /*!< Wearable */ + ESP_BT_COD_MAJOR_DEV_TOY = 8, /*!< Toy */ + ESP_BT_COD_MAJOR_DEV_HEALTH = 9, /*!< Health */ + ESP_BT_COD_MAJOR_DEV_UNCATEGORIZED = 31, /*!< Uncategorized: device not specified */ +} esp_bt_cod_major_dev_t; + +/// Bits of major device class field +#define ESP_BT_COD_MAJOR_DEV_BIT_MASK (0x1f00) /*!< Major device bit mask */ +#define ESP_BT_COD_MAJOR_DEV_BIT_OFFSET (8) /*!< Major device bit offset */ + +/// Bits of minor device class field +#define ESP_BT_COD_MINOR_DEV_BIT_MASK (0xfc) /*!< Minor device bit mask */ +#define ESP_BT_COD_MINOR_DEV_BIT_OFFSET (2) /*!< Minor device bit offset */ + +/// Bits of format type +#define ESP_BT_COD_FORMAT_TYPE_BIT_MASK (0x03) /*!< Format type bit mask */ +#define ESP_BT_COD_FORMAT_TYPE_BIT_OFFSET (0) /*!< Format type bit offset */ + +/// Class of device format type 1 +#define ESP_BT_COD_FORMAT_TYPE_1 (0x00) + +/** Bluetooth Device Discovery state */ +typedef enum { + ESP_BT_GAP_DISCOVERY_STOPPED, /*!< Device discovery stopped */ + ESP_BT_GAP_DISCOVERY_STARTED, /*!< Device discovery started */ +} esp_bt_gap_discovery_state_t; + +/// Type of link key +#define ESP_BT_LINK_KEY_COMB (0x00) /*!< Combination Key */ +#define ESP_BT_LINK_KEY_DBG_COMB (0x03) /*!< Debug Combination Key */ +#define ESP_BT_LINK_KEY_UNAUTHED_COMB_P192 (0x04) /*!< Unauthenticated Combination Key generated from P-192 */ +#define ESP_BT_LINK_KEY_AUTHED_COMB_P192 (0x05) /*!< Authenticated Combination Key generated from P-192 */ +#define ESP_BT_LINK_KEY_CHG_COMB (0x06) /*!< Changed Combination Key */ +#define ESP_BT_LINK_KEY_UNAUTHED_COMB_P256 (0x07) /*!< Unauthenticated Combination Key generated from P-256 */ +#define ESP_BT_LINK_KEY_AUTHED_COMB_P256 (0x08) /*!< Authenticated Combination Key generated from P-256 */ +typedef uint8_t esp_bt_link_key_type_t; + +/// Type of encryption +#define ESP_BT_ENC_MODE_OFF (0x00) /*!< Link Level Encryption is OFF */ +#define ESP_BT_ENC_MODE_E0 (0x01) /*!< Link Level Encryption is ON with E0 */ +#define ESP_BT_ENC_MODE_AES (0x02) /*!< Link Level Encryption is ON with AES-CCM */ +typedef uint8_t esp_bt_enc_mode_t; + +/// BT GAP callback events +typedef enum { + ESP_BT_GAP_DISC_RES_EVT = 0, /*!< Device discovery result event */ + ESP_BT_GAP_DISC_STATE_CHANGED_EVT, /*!< Discovery state changed event */ + ESP_BT_GAP_RMT_SRVCS_EVT, /*!< Get remote services event */ + ESP_BT_GAP_RMT_SRVC_REC_EVT, /*!< Get remote service record event */ + ESP_BT_GAP_AUTH_CMPL_EVT, /*!< Authentication complete event */ + ESP_BT_GAP_PIN_REQ_EVT, /*!< Legacy Pairing Pin code request */ + ESP_BT_GAP_CFM_REQ_EVT, /*!< Security Simple Pairing User Confirmation request. */ + ESP_BT_GAP_KEY_NOTIF_EVT, /*!< Security Simple Pairing Passkey Notification */ + ESP_BT_GAP_KEY_REQ_EVT, /*!< Security Simple Pairing Passkey request */ + ESP_BT_GAP_READ_RSSI_DELTA_EVT, /*!< Read rssi event */ + ESP_BT_GAP_CONFIG_EIR_DATA_EVT, /*!< Config EIR data event */ + ESP_BT_GAP_SET_AFH_CHANNELS_EVT, /*!< Set AFH channels event */ + ESP_BT_GAP_READ_REMOTE_NAME_EVT, /*!< Read Remote Name event */ + ESP_BT_GAP_MODE_CHG_EVT, + ESP_BT_GAP_REMOVE_BOND_DEV_COMPLETE_EVT, /*!< remove bond device complete event */ + ESP_BT_GAP_QOS_CMPL_EVT, /*!< QOS complete event */ + ESP_BT_GAP_ACL_CONN_CMPL_STAT_EVT, /*!< ACL connection complete status event */ + ESP_BT_GAP_ACL_DISCONN_CMPL_STAT_EVT, /*!< ACL disconnection complete status event */ + ESP_BT_GAP_SET_PAGE_TO_EVT, /*!< Set page timeout event */ + ESP_BT_GAP_GET_PAGE_TO_EVT, /*!< Get page timeout event */ + ESP_BT_GAP_ACL_PKT_TYPE_CHANGED_EVT, /*!< Set ACL packet types event */ + ESP_BT_GAP_ENC_CHG_EVT, /*!< Encryption change event */ + ESP_BT_GAP_EVT_MAX, +} esp_bt_gap_cb_event_t; + +/** Inquiry Mode */ +typedef enum { + ESP_BT_INQ_MODE_GENERAL_INQUIRY, /*!< General inquiry mode */ + ESP_BT_INQ_MODE_LIMITED_INQUIRY, /*!< Limited inquiry mode */ +} esp_bt_inq_mode_t; + +/** Minimum and Maximum inquiry length*/ +#define ESP_BT_GAP_MIN_INQ_LEN (0x01) /*!< Minimum inquiry duration, unit is 1.28s */ +#define ESP_BT_GAP_MAX_INQ_LEN (0x30) /*!< Maximum inquiry duration, unit is 1.28s */ + +/** Minimum, Default and Maximum poll interval **/ +#define ESP_BT_GAP_TPOLL_MIN (0x0006) /*!< Minimum poll interval, unit is 625 microseconds */ +#define ESP_BT_GAP_TPOLL_DFT (0x0028) /*!< Default poll interval, unit is 625 microseconds */ +#define ESP_BT_GAP_TPOLL_MAX (0x1000) /*!< Maximum poll interval, unit is 625 microseconds */ + +/// GAP state callback parameters +typedef union { + /** + * @brief ESP_BT_GAP_DISC_RES_EVT + */ + struct disc_res_param { + esp_bd_addr_t bda; /*!< remote bluetooth device address*/ + int num_prop; /*!< number of properties got */ + esp_bt_gap_dev_prop_t *prop; /*!< properties discovered from the new device */ + } disc_res; /*!< discovery result parameter struct */ + + /** + * @brief ESP_BT_GAP_DISC_STATE_CHANGED_EVT + */ + struct disc_state_changed_param { + esp_bt_gap_discovery_state_t state; /*!< discovery state */ + } disc_st_chg; /*!< discovery state changed parameter struct */ + + /** + * @brief ESP_BT_GAP_RMT_SRVCS_EVT + */ + struct rmt_srvcs_param { + esp_bd_addr_t bda; /*!< remote bluetooth device address*/ + esp_bt_status_t stat; /*!< service search status */ + int num_uuids; /*!< number of UUID in uuid_list */ + esp_bt_uuid_t *uuid_list; /*!< list of service UUIDs of remote device */ + } rmt_srvcs; /*!< services of remote device parameter struct */ + + /** + * @brief ESP_BT_GAP_RMT_SRVC_REC_EVT + */ + struct rmt_srvc_rec_param { + esp_bd_addr_t bda; /*!< remote bluetooth device address*/ + esp_bt_status_t stat; /*!< service search status */ + } rmt_srvc_rec; /*!< specific service record from remote device parameter struct */ + + /** + * @brief ESP_BT_GAP_READ_RSSI_DELTA_EVT * + */ + struct read_rssi_delta_param { + esp_bd_addr_t bda; /*!< remote bluetooth device address*/ + esp_bt_status_t stat; /*!< read rssi status */ + int8_t rssi_delta; /*!< rssi delta value range -128 ~127, The value zero indicates that the RSSI is inside the Golden Receive Power Range, the Golden Receive Power Range is from ESP_BT_GAP_RSSI_LOW_THRLD to ESP_BT_GAP_RSSI_HIGH_THRLD */ + } read_rssi_delta; /*!< read rssi parameter struct */ + + /** + * @brief ESP_BT_GAP_CONFIG_EIR_DATA_EVT * + */ + struct config_eir_data_param { + esp_bt_status_t stat; /*!< config EIR status: + ESP_BT_STATUS_SUCCESS: config success + ESP_BT_STATUS_EIR_TOO_LARGE: the EIR data is more than 240B. The EIR may not contain the whole data. + others: failed + */ + uint8_t eir_type_num; /*!< the number of EIR types in EIR type */ + esp_bt_eir_type_t eir_type[ESP_BT_EIR_TYPE_MAX_NUM]; /*!< EIR types in EIR type */ + } config_eir_data; /*!< config EIR data */ + + /** + * @brief ESP_BT_GAP_AUTH_CMPL_EVT + */ + struct auth_cmpl_param { + esp_bd_addr_t bda; /*!< remote bluetooth device address*/ + esp_bt_status_t stat; /*!< authentication complete status */ + esp_bt_link_key_type_t lk_type; /*!< type of link key generated */ + uint8_t device_name[ESP_BT_GAP_MAX_BDNAME_LEN + 1]; /*!< device name */ + } auth_cmpl; /*!< authentication complete parameter struct */ + + /** + * @brief ESP_BT_GAP_ENC_CHG_EVT + */ + struct enc_chg_param { + esp_bd_addr_t bda; /*!< remote bluetooth device address*/ + esp_bt_enc_mode_t enc_mode; /*!< encryption mode */ + } enc_chg; /*!< encryption change parameter struct */ + + /** + * @brief ESP_BT_GAP_PIN_REQ_EVT + */ + struct pin_req_param { + esp_bd_addr_t bda; /*!< remote bluetooth device address*/ + bool min_16_digit; /*!< TRUE if the pin returned must be at least 16 digits */ + } pin_req; /*!< pin request parameter struct */ + + /** + * @brief ESP_BT_GAP_CFM_REQ_EVT + */ + struct cfm_req_param { + esp_bd_addr_t bda; /*!< remote bluetooth device address*/ + uint32_t num_val; /*!< the numeric value for comparison. */ + } cfm_req; /*!< confirm request parameter struct */ + + /** + * @brief ESP_BT_GAP_KEY_NOTIF_EVT + */ + struct key_notif_param { + esp_bd_addr_t bda; /*!< remote bluetooth device address*/ + uint32_t passkey; /*!< the numeric value for passkey entry. */ + } key_notif; /*!< passkey notif parameter struct */ + + /** + * @brief ESP_BT_GAP_KEY_REQ_EVT + */ + struct key_req_param { + esp_bd_addr_t bda; /*!< remote bluetooth device address*/ + } key_req; /*!< passkey request parameter struct */ + + /** + * @brief ESP_BT_GAP_SET_AFH_CHANNELS_EVT + */ + struct set_afh_channels_param { + esp_bt_status_t stat; /*!< set AFH channel status */ + } set_afh_channels; /*!< set AFH channel parameter struct */ + + /** + * @brief ESP_BT_GAP_READ_REMOTE_NAME_EVT + */ + struct read_rmt_name_param { + esp_bd_addr_t bda; /*!< remote bluetooth device address*/ + esp_bt_status_t stat; /*!< read Remote Name status */ + uint8_t rmt_name[ESP_BT_GAP_MAX_BDNAME_LEN + 1]; /*!< Remote device name */ + } read_rmt_name; /*!< read Remote Name parameter struct */ + + /** + * @brief ESP_BT_GAP_MODE_CHG_EVT + */ + struct mode_chg_param { + esp_bd_addr_t bda; /*!< remote bluetooth device address*/ + esp_bt_pm_mode_t mode; /*!< PM mode*/ + } mode_chg; /*!< mode change event parameter struct */ + + /** + * @brief ESP_BT_GAP_REMOVE_BOND_DEV_COMPLETE_EVT + */ + struct bt_remove_bond_dev_cmpl_evt_param { + esp_bd_addr_t bda; /*!< remote bluetooth device address*/ + esp_bt_status_t status; /*!< Indicate the remove bond device operation success status */ + }remove_bond_dev_cmpl; /*!< Event parameter of ESP_BT_GAP_REMOVE_BOND_DEV_COMPLETE_EVT */ + + /** + * @brief ESP_BT_GAP_QOS_CMPL_EVT + */ + struct qos_cmpl_param { + esp_bt_status_t stat; /*!< QoS status */ + esp_bd_addr_t bda; /*!< remote bluetooth device address*/ + uint32_t t_poll; /*!< poll interval, the maximum time between transmissions + which from the master to a particular slave on the ACL + logical transport. unit is 0.625ms. */ + } qos_cmpl; /*!< QoS complete parameter struct */ + + /** + * @brief ESP_BT_GAP_SET_PAGE_TO_EVT + */ + struct page_to_set_param { + esp_bt_status_t stat; /*!< set page timeout status*/ + } set_page_timeout; /*!< set page timeout parameter struct */ + + /** + * @brief ESP_BT_GAP_GET_PAGE_TO_EVT + */ + struct page_to_get_param { + esp_bt_status_t stat; /*!< get page timeout status*/ + uint16_t page_to; /*!< page_timeout value to be set, unit is 0.625ms. */ + } get_page_timeout; /*!< get page timeout parameter struct */ + + /** + * @brief ESP_BT_GAP_ACL_PKT_TYPE_CHANGED_EVT + */ + struct set_acl_pkt_types_param { + esp_bt_status_t status; /*!< set ACL packet types status */ + esp_bd_addr_t bda; /*!< remote bluetooth device address */ + uint16_t pkt_types; /*!< packet types successfully set */ + } set_acl_pkt_types; /*!< set ACL packet types parameter struct */ + + /** + * @brief ESP_BT_GAP_ACL_CONN_CMPL_STAT_EVT + */ + struct acl_conn_cmpl_stat_param { + esp_bt_status_t stat; /*!< ACL connection status */ + uint16_t handle; /*!< ACL connection handle */ + esp_bd_addr_t bda; /*!< remote bluetooth device address */ + } acl_conn_cmpl_stat; /*!< ACL connection complete status parameter struct */ + + /** + * @brief ESP_BT_GAP_ACL_DISCONN_CMPL_STAT_EVT + */ + struct acl_disconn_cmpl_stat_param { + esp_bt_status_t reason; /*!< ACL disconnection reason */ + uint16_t handle; /*!< ACL connection handle */ + esp_bd_addr_t bda; /*!< remote bluetooth device address */ + } acl_disconn_cmpl_stat; /*!< ACL disconnection complete status parameter struct */ +} esp_bt_gap_cb_param_t; + +/** + * @brief bluetooth GAP callback function type + * + * @param event : Event type + * + * @param param : Pointer to callback parameter + */ +typedef void (* esp_bt_gap_cb_t)(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *param); + +/** + * @brief get major service field of COD + * + * @param[in] cod: Class of Device + * + * @return major service bits + */ +static inline uint32_t esp_bt_gap_get_cod_srvc(uint32_t cod) +{ + return (cod & ESP_BT_COD_SRVC_BIT_MASK) >> ESP_BT_COD_SRVC_BIT_OFFSET; +} + +/** + * @brief get major device field of COD + * + * @param[in] cod: Class of Device + * + * @return major device bits + */ +static inline uint32_t esp_bt_gap_get_cod_major_dev(uint32_t cod) +{ + return (cod & ESP_BT_COD_MAJOR_DEV_BIT_MASK) >> ESP_BT_COD_MAJOR_DEV_BIT_OFFSET; +} + +/** + * @brief get minor service field of COD + * + * @param[in] cod: Class of Device + * + * @return minor service bits + */ +static inline uint32_t esp_bt_gap_get_cod_minor_dev(uint32_t cod) +{ + return (cod & ESP_BT_COD_MINOR_DEV_BIT_MASK) >> ESP_BT_COD_MINOR_DEV_BIT_OFFSET; +} + +/** + * @brief get format type of COD + * + * @param[in] cod: Class of Device + * + * @return format type + */ +static inline uint32_t esp_bt_gap_get_cod_format_type(uint32_t cod) +{ + return (cod & ESP_BT_COD_FORMAT_TYPE_BIT_MASK); +} + +/** + * @brief decide the integrity of COD + * + * @param[in] cod: Class of Device + * + * @return + * - true if cod is valid + * - false otherise + */ +static inline bool esp_bt_gap_is_valid_cod(uint32_t cod) +{ + if (esp_bt_gap_get_cod_format_type(cod) == ESP_BT_COD_FORMAT_TYPE_1 && + esp_bt_gap_get_cod_srvc(cod) != ESP_BT_COD_SRVC_NONE) { + return true; + } + + return false; +} + +/** + * @brief register callback function. This function should be called after esp_bluedroid_enable() completes successfully + * + * @return + * - ESP_OK : Succeed + * - ESP_FAIL: others + */ +esp_err_t esp_bt_gap_register_callback(esp_bt_gap_cb_t callback); + +/** + * @brief Set discoverability and connectability mode for legacy bluetooth. This function should + * be called after esp_bluedroid_enable() completes successfully + * + * @param[in] c_mode : one of the enums of esp_bt_connection_mode_t + * + * @param[in] d_mode : one of the enums of esp_bt_discovery_mode_t + * + * @return + * - ESP_OK : Succeed + * - ESP_ERR_INVALID_ARG: if argument invalid + * - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled + * - ESP_FAIL: others + */ +esp_err_t esp_bt_gap_set_scan_mode(esp_bt_connection_mode_t c_mode, esp_bt_discovery_mode_t d_mode); + +/** + * @brief This function starts Inquiry and Name Discovery. This function should be called after esp_bluedroid_enable() completes successfully. + * When Inquiry is halted and cached results do not contain device name, then Name Discovery will connect to the peer target to get the device name. + * esp_bt_gap_cb_t will be called with ESP_BT_GAP_DISC_STATE_CHANGED_EVT when Inquiry is started or Name Discovery is completed. + * esp_bt_gap_cb_t will be called with ESP_BT_GAP_DISC_RES_EVT each time the two types of discovery results are got. + * + * @param[in] mode - Inquiry mode + * + * @param[in] inq_len - Inquiry duration in 1.28 sec units, ranging from 0x01 to 0x30. This parameter only specifies the total duration of the Inquiry process, + * - when this time expires, Inquiry will be halted. + * + * @param[in] num_rsps - Number of responses that can be received before the Inquiry is halted, value 0 indicates an unlimited number of responses. + * + * @return + * - ESP_OK : Succeed + * - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled + * - ESP_ERR_INVALID_ARG: if invalid parameters are provided + * - ESP_FAIL: others + */ +esp_err_t esp_bt_gap_start_discovery(esp_bt_inq_mode_t mode, uint8_t inq_len, uint8_t num_rsps); + +/** + * @brief Cancel Inquiry and Name Discovery. This function should be called after esp_bluedroid_enable() completes successfully. + * esp_bt_gap_cb_t will be called with ESP_BT_GAP_DISC_STATE_CHANGED_EVT if Inquiry or Name Discovery is cancelled by + * calling this function. + * + * @return + * - ESP_OK : Succeed + * - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled + * - ESP_FAIL: others + */ +esp_err_t esp_bt_gap_cancel_discovery(void); + +/** + * @brief Start SDP to get remote services. This function should be called after esp_bluedroid_enable() completes successfully. + * esp_bt_gap_cb_t will be called with ESP_BT_GAP_RMT_SRVCS_EVT after service discovery ends. + * + * @return + * - ESP_OK : Succeed + * - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled + * - ESP_FAIL: others + */ +esp_err_t esp_bt_gap_get_remote_services(esp_bd_addr_t remote_bda); + +/** + * @brief Start SDP to look up the service matching uuid on the remote device. This function should be called after + * esp_bluedroid_enable() completes successfully. + * + * esp_bt_gap_cb_t will be called with ESP_BT_GAP_RMT_SRVC_REC_EVT after service discovery ends + * @return + * - ESP_OK : Succeed + * - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled + * - ESP_FAIL: others + */ +esp_err_t esp_bt_gap_get_remote_service_record(esp_bd_addr_t remote_bda, esp_bt_uuid_t *uuid); + +/** + * @brief This function is called to get EIR data for a specific type. + * + * @param[in] eir - pointer of raw eir data to be resolved + * @param[in] type - specific EIR data type + * @param[out] length - return the length of EIR data excluding fields of length and data type + * + * @return pointer of starting position of eir data excluding eir data type, NULL if not found + * + */ +uint8_t *esp_bt_gap_resolve_eir_data(uint8_t *eir, esp_bt_eir_type_t type, uint8_t *length); + +/** + * @brief This function is called to config EIR data. + * + * esp_bt_gap_cb_t will be called with ESP_BT_GAP_CONFIG_EIR_DATA_EVT after config EIR ends. + * + * @param[in] eir_data - pointer of EIR data content + * @return + * - ESP_OK : Succeed + * - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled + * - ESP_ERR_INVALID_ARG: if param is invalid + * - ESP_FAIL: others + */ +esp_err_t esp_bt_gap_config_eir_data(esp_bt_eir_data_t *eir_data); + +/** + * @brief This function is called to set class of device. + * The structure esp_bt_gap_cb_t will be called with ESP_BT_GAP_SET_COD_EVT after set COD ends. + * This function should be called after Bluetooth profiles are initialized, otherwise the user configured + * class of device can be overwritten. + * Some profiles have special restrictions on class of device, and changes may make these profiles unable to work. + * + * @param[in] cod - class of device + * @param[in] mode - setting mode + * + * @return + * - ESP_OK : Succeed + * - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled + * - ESP_ERR_INVALID_ARG: if param is invalid + * - ESP_FAIL: others + */ +esp_err_t esp_bt_gap_set_cod(esp_bt_cod_t cod, esp_bt_cod_mode_t mode); + +/** + * @brief This function is called to get class of device. + * + * @param[out] cod - class of device + * + * @return + * - ESP_OK : Succeed + * - ESP_FAIL: others + */ +esp_err_t esp_bt_gap_get_cod(esp_bt_cod_t *cod); + +/** + * @brief This function is called to read RSSI delta by address after connected. The RSSI value returned by ESP_BT_GAP_READ_RSSI_DELTA_EVT. + * + * + * @param[in] remote_addr - remote device address, corresponding to a certain connection handle + * @return + * - ESP_OK : Succeed + * - ESP_FAIL: others + * + */ +esp_err_t esp_bt_gap_read_rssi_delta(esp_bd_addr_t remote_addr); + +/** +* @brief Removes a device from the security database list of +* peer device. +* +* @param[in] bd_addr : BD address of the peer device +* +* @return - ESP_OK : success +* - ESP_FAIL : failed +* +*/ +esp_err_t esp_bt_gap_remove_bond_device(esp_bd_addr_t bd_addr); + +/** +* @brief Get the device number from the security database list of peer device. +* It will return the device bonded number immediately. +* +* @return - >= 0 : bonded devices number +* - ESP_FAIL : failed +* +*/ +int esp_bt_gap_get_bond_device_num(void); + +/** +* @brief Get the device from the security database list of peer device. +* It will return the device bonded information immediately. +* +* @param[inout] dev_num: Indicate the dev_list array(buffer) size as input. +* If dev_num is large enough, it means the actual number as output. +* Suggest that dev_num value equal to esp_ble_get_bond_device_num(). +* +* @param[out] dev_list: an array(buffer) of `esp_bd_addr_t` type. Use for storing the bonded devices address. +* The dev_list should be allocated by who call this API. +* +* @return +* - ESP_OK : Succeed +* - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled +* - ESP_FAIL: others +*/ +esp_err_t esp_bt_gap_get_bond_device_list(int *dev_num, esp_bd_addr_t *dev_list); + +/** +* @brief Set pin type and default pin code for legacy pairing. +* +* @param[in] pin_type: Use variable or fixed pin. +* If pin_type is ESP_BT_PIN_TYPE_VARIABLE, pin_code and pin_code_len +* will be ignored, and ESP_BT_GAP_PIN_REQ_EVT will come when control +* requests for pin code. +* Else, will use fixed pin code and not callback to users. +* +* @param[in] pin_code_len: Length of pin_code +* +* @param[in] pin_code: Pin_code +* +* @return - ESP_OK : success +* - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled +* - other : failed +*/ +esp_err_t esp_bt_gap_set_pin(esp_bt_pin_type_t pin_type, uint8_t pin_code_len, esp_bt_pin_code_t pin_code); + +/** +* @brief Reply the pin_code to the peer device for legacy pairing +* when ESP_BT_GAP_PIN_REQ_EVT is coming. +* +* @param[in] bd_addr: BD address of the peer +* +* @param[in] accept: Pin_code reply successful or declined. +* +* @param[in] pin_code_len: Length of pin_code +* +* @param[in] pin_code: Pin_code +* +* @return - ESP_OK : success +* - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled +* - other : failed +*/ +esp_err_t esp_bt_gap_pin_reply(esp_bd_addr_t bd_addr, bool accept, uint8_t pin_code_len, esp_bt_pin_code_t pin_code); + +/** +* @brief Set a GAP security parameter value. Overrides the default value. +* +* @param[in] param_type : the type of the param which is to be set +* +* @param[in] value : the param value +* +* @param[in] len : the length of the param value +* +* @return - ESP_OK : success +* - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled +* - other : failed +* +*/ +esp_err_t esp_bt_gap_set_security_param(esp_bt_sp_param_t param_type, + void *value, uint8_t len); + +/** +* @brief Reply the key value to the peer device in the legacy connection stage. +* +* @param[in] bd_addr : BD address of the peer +* +* @param[in] accept : passkey entry successful or declined. +* +* @param[in] passkey : passkey value, must be a 6 digit number, can be lead by 0. +* +* @return - ESP_OK : success +* - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled +* - other : failed +* +*/ +esp_err_t esp_bt_gap_ssp_passkey_reply(esp_bd_addr_t bd_addr, bool accept, uint32_t passkey); + + +/** +* @brief Reply the confirm value to the peer device in the legacy connection stage. +* +* @param[in] bd_addr : BD address of the peer device +* +* @param[in] accept : numbers to compare are the same or different +* +* @return - ESP_OK : success +* - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled +* - other : failed +* +*/ +esp_err_t esp_bt_gap_ssp_confirm_reply(esp_bd_addr_t bd_addr, bool accept); + +/** +* @brief Set the AFH channels +* +* @param[in] channels : The n th such field (in the range 0 to 78) contains the value for channel n : +* 0 means channel n is bad. +* 1 means channel n is unknown. +* The most significant bit is reserved and shall be set to 0. +* At least 20 channels shall be marked as unknown. +* +* @return - ESP_OK : success +* - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled +* - other : failed +* +*/ +esp_err_t esp_bt_gap_set_afh_channels(esp_bt_gap_afh_channels channels); + +/** +* @brief Read the remote device name +* +* @param[in] remote_bda: The remote device's address +* +* @return - ESP_OK : success +* - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled +* - other : failed +* +*/ +esp_err_t esp_bt_gap_read_remote_name(esp_bd_addr_t remote_bda); + +/** +* @brief Config Quality of service +* +* @param[in] remote_bda: The remote device's address +* @param[in] t_poll: Poll interval, the maximum time between transmissions + which from the master to a particular slave on the ACL + logical transport. unit is 0.625ms +* +* @return - ESP_OK : success +* - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled +* - other : failed +* +*/ +esp_err_t esp_bt_gap_set_qos(esp_bd_addr_t remote_bda, uint32_t t_poll); + +/** + * @brief Set the page timeout + * esp_bt_gap_cb_t will be called with ESP_BT_GAP_SET_PAGE_TO_EVT + * after set page timeout ends. The value to be set will not be effective util the + * next page procedure, it's suggested to set the page timeout before initiating + * a connection. + * + * @param[in] page_to: Page timeout, the maximum time the master will wait for a + Base-band page response from the remote device at a locally + initiated connection attempt. The valid range is 0x0016 ~ 0xffff, + the default value is 0x2000, unit is 0.625ms. + * + * @return - ESP_OK: success + * - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled + * - other: failed + */ +esp_err_t esp_bt_gap_set_page_timeout(uint16_t page_to); + +/** + * @brief Get the page timeout + * esp_bt_gap_cb_t will be called with ESP_BT_GAP_GET_PAGE_TO_EVT + * after get page timeout ends + * + * @return - ESP_OK: success + * - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled + * - other: failed + */ +esp_err_t esp_bt_gap_get_page_timeout(void); + +/** + * @brief Set ACL packet types + * An ESP_BT_GAP_SET_ACL_PPKT_TYPES_EVT event will reported to + * the APP layer. + * + * @return - ESP_OK: success + * - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled + * - other: failed + */ +esp_err_t esp_bt_gap_set_acl_pkt_types(esp_bd_addr_t remote_bda, esp_bt_acl_pkt_type_t pkt_types); + +#ifdef __cplusplus +} +#endif + +#endif /* __ESP_GAP_BT_API_H__ */ diff --git a/lib/bt/host/bluedroid/api/include/api/esp_gatt_common_api.h b/lib/bt/host/bluedroid/api/include/api/esp_gatt_common_api.h new file mode 100644 index 00000000..1a4a59e1 --- /dev/null +++ b/lib/bt/host/bluedroid/api/include/api/esp_gatt_common_api.h @@ -0,0 +1,48 @@ +/* + * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __ESP_GATT_COMMON_API_H__ +#define __ESP_GATT_COMMON_API_H__ + +#include +#include + +#include "esp_err.h" +#include "esp_bt_defs.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// Maximum Transmission Unit used in GATT +#define ESP_GATT_DEF_BLE_MTU_SIZE 23 /* relate to GATT_DEF_BLE_MTU_SIZE in stack/gatt_api.h */ + +// Maximum Transmission Unit allowed in GATT +#define ESP_GATT_MAX_MTU_SIZE 517 /* relate to GATT_MAX_MTU_SIZE in stack/gatt_api.h */ + +/** + * @brief This function is called to set local MTU, + * the function is called before BLE connection. + * + * @param[in] mtu: the size of MTU. + * + * @return + * - ESP_OK: success + * - other: failed + * + */ +extern esp_err_t esp_ble_gatt_set_local_mtu (uint16_t mtu); + +#if (BLE_INCLUDED == TRUE) +extern uint16_t esp_ble_get_sendable_packets_num (void); +extern uint16_t esp_ble_get_cur_sendable_packets_num (uint16_t connid); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* __ESP_GATT_COMMON_API_H__ */ diff --git a/lib/bt/host/bluedroid/api/include/api/esp_gatt_defs.h b/lib/bt/host/bluedroid/api/include/api/esp_gatt_defs.h new file mode 100644 index 00000000..f2d658a2 --- /dev/null +++ b/lib/bt/host/bluedroid/api/include/api/esp_gatt_defs.h @@ -0,0 +1,485 @@ +/* + * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __ESP_GATT_DEFS_H__ +#define __ESP_GATT_DEFS_H__ + +#include "esp_bt_defs.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/// GATT INVALID UUID +#define ESP_GATT_ILLEGAL_UUID 0 +/// GATT INVALID HANDLE +#define ESP_GATT_ILLEGAL_HANDLE 0 +/// GATT attribute max handle +#define ESP_GATT_ATTR_HANDLE_MAX UC_CONFIG_BT_GATT_MAX_SR_ATTRIBUTES +#define ESP_GATT_MAX_READ_MULTI_HANDLES 10 /* Max attributes to read in one request */ + + +/**@{ + * All "ESP_GATT_UUID_xxx" is attribute types + */ +#define ESP_GATT_UUID_IMMEDIATE_ALERT_SVC 0x1802 /* Immediate alert Service*/ +#define ESP_GATT_UUID_LINK_LOSS_SVC 0x1803 /* Link Loss Service*/ +#define ESP_GATT_UUID_TX_POWER_SVC 0x1804 /* TX Power Service*/ +#define ESP_GATT_UUID_CURRENT_TIME_SVC 0x1805 /* Current Time Service Service*/ +#define ESP_GATT_UUID_REF_TIME_UPDATE_SVC 0x1806 /* Reference Time Update Service*/ +#define ESP_GATT_UUID_NEXT_DST_CHANGE_SVC 0x1807 /* Next DST Change Service*/ +#define ESP_GATT_UUID_GLUCOSE_SVC 0x1808 /* Glucose Service*/ +#define ESP_GATT_UUID_HEALTH_THERMOM_SVC 0x1809 /* Health Thermometer Service*/ +#define ESP_GATT_UUID_DEVICE_INFO_SVC 0x180A /* Device Information Service*/ +#define ESP_GATT_UUID_HEART_RATE_SVC 0x180D /* Heart Rate Service*/ +#define ESP_GATT_UUID_PHONE_ALERT_STATUS_SVC 0x180E /* Phone Alert Status Service*/ +#define ESP_GATT_UUID_BATTERY_SERVICE_SVC 0x180F /* Battery Service*/ +#define ESP_GATT_UUID_BLOOD_PRESSURE_SVC 0x1810 /* Blood Pressure Service*/ +#define ESP_GATT_UUID_ALERT_NTF_SVC 0x1811 /* Alert Notification Service*/ +#define ESP_GATT_UUID_HID_SVC 0x1812 /* HID Service*/ +#define ESP_GATT_UUID_SCAN_PARAMETERS_SVC 0x1813 /* Scan Parameters Service*/ +#define ESP_GATT_UUID_RUNNING_SPEED_CADENCE_SVC 0x1814 /* Running Speed and Cadence Service*/ +#define ESP_GATT_UUID_Automation_IO_SVC 0x1815 /* Automation IO Service*/ +#define ESP_GATT_UUID_CYCLING_SPEED_CADENCE_SVC 0x1816 /* Cycling Speed and Cadence Service*/ +#define ESP_GATT_UUID_CYCLING_POWER_SVC 0x1818 /* Cycling Power Service*/ +#define ESP_GATT_UUID_LOCATION_AND_NAVIGATION_SVC 0x1819 /* Location and Navigation Service*/ +#define ESP_GATT_UUID_ENVIRONMENTAL_SENSING_SVC 0x181A /* Environmental Sensing Service*/ +#define ESP_GATT_UUID_BODY_COMPOSITION 0x181B /* Body Composition Service*/ +#define ESP_GATT_UUID_USER_DATA_SVC 0x181C /* User Data Service*/ +#define ESP_GATT_UUID_WEIGHT_SCALE_SVC 0x181D /* Weight Scale Service*/ +#define ESP_GATT_UUID_BOND_MANAGEMENT_SVC 0x181E /* Bond Management Service*/ +#define ESP_GATT_UUID_CONT_GLUCOSE_MONITOR_SVC 0x181F /* Continuous Glucose Monitoring Service*/ + +#define ESP_GATT_UUID_PRI_SERVICE 0x2800 +#define ESP_GATT_UUID_SEC_SERVICE 0x2801 +#define ESP_GATT_UUID_INCLUDE_SERVICE 0x2802 +#define ESP_GATT_UUID_CHAR_DECLARE 0x2803 /* Characteristic Declaration*/ + +#define ESP_GATT_UUID_CHAR_EXT_PROP 0x2900 /* Characteristic Extended Properties */ +#define ESP_GATT_UUID_CHAR_DESCRIPTION 0x2901 /* Characteristic User Description*/ +#define ESP_GATT_UUID_CHAR_CLIENT_CONFIG 0x2902 /* Client Characteristic Configuration */ +#define ESP_GATT_UUID_CHAR_SRVR_CONFIG 0x2903 /* Server Characteristic Configuration */ +#define ESP_GATT_UUID_CHAR_PRESENT_FORMAT 0x2904 /* Characteristic Presentation Format*/ +#define ESP_GATT_UUID_CHAR_AGG_FORMAT 0x2905 /* Characteristic Aggregate Format*/ +#define ESP_GATT_UUID_CHAR_VALID_RANGE 0x2906 /* Characteristic Valid Range */ +#define ESP_GATT_UUID_EXT_RPT_REF_DESCR 0x2907 /* External Report Reference */ +#define ESP_GATT_UUID_RPT_REF_DESCR 0x2908 /* Report Reference */ +#define ESP_GATT_UUID_NUM_DIGITALS_DESCR 0x2909 /* Number of Digitals */ +#define ESP_GATT_UUID_VALUE_TRIGGER_DESCR 0x290A /* Value Trigger Setting */ +#define ESP_GATT_UUID_ENV_SENSING_CONFIG_DESCR 0x290B /* Environmental Sensing Configuration */ +#define ESP_GATT_UUID_ENV_SENSING_MEASUREMENT_DESCR 0x290C /* Environmental Sensing Measurement */ +#define ESP_GATT_UUID_ENV_SENSING_TRIGGER_DESCR 0x290D /* Environmental Sensing Trigger Setting */ +#define ESP_GATT_UUID_TIME_TRIGGER_DESCR 0x290E /* Time Trigger Setting */ + +/* GAP Profile Attributes */ +#define ESP_GATT_UUID_GAP_DEVICE_NAME 0x2A00 +#define ESP_GATT_UUID_GAP_ICON 0x2A01 +#define ESP_GATT_UUID_GAP_PREF_CONN_PARAM 0x2A04 +#define ESP_GATT_UUID_GAP_CENTRAL_ADDR_RESOL 0x2AA6 + +/* Attribute Profile Attribute UUID */ +#define ESP_GATT_UUID_GATT_SRV_CHGD 0x2A05 + +/* Link ESP_Loss Service */ +#define ESP_GATT_UUID_ALERT_LEVEL 0x2A06 /* Alert Level */ +#define ESP_GATT_UUID_TX_POWER_LEVEL 0x2A07 /* TX power level */ + +/* Current Time Service */ +#define ESP_GATT_UUID_CURRENT_TIME 0x2A2B /* Current Time */ +#define ESP_GATT_UUID_LOCAL_TIME_INFO 0x2A0F /* Local time info */ +#define ESP_GATT_UUID_REF_TIME_INFO 0x2A14 /* reference time information */ + +/* Network availability Profile */ +#define ESP_GATT_UUID_NW_STATUS 0x2A18 /* network availability status */ +#define ESP_GATT_UUID_NW_TRIGGER 0x2A1A /* Network availability trigger */ + +/* Phone alert */ +#define ESP_GATT_UUID_ALERT_STATUS 0x2A3F /* alert status */ +#define ESP_GATT_UUID_RINGER_CP 0x2A40 /* ringer control point */ +#define ESP_GATT_UUID_RINGER_SETTING 0x2A41 /* ringer setting */ + +/* Glucose Service */ +#define ESP_GATT_UUID_GM_MEASUREMENT 0x2A18 +#define ESP_GATT_UUID_GM_CONTEXT 0x2A34 +#define ESP_GATT_UUID_GM_CONTROL_POINT 0x2A52 +#define ESP_GATT_UUID_GM_FEATURE 0x2A51 + +/* device information characteristic */ +#define ESP_GATT_UUID_SYSTEM_ID 0x2A23 +#define ESP_GATT_UUID_MODEL_NUMBER_STR 0x2A24 +#define ESP_GATT_UUID_SERIAL_NUMBER_STR 0x2A25 +#define ESP_GATT_UUID_FW_VERSION_STR 0x2A26 +#define ESP_GATT_UUID_HW_VERSION_STR 0x2A27 +#define ESP_GATT_UUID_SW_VERSION_STR 0x2A28 +#define ESP_GATT_UUID_MANU_NAME 0x2A29 +#define ESP_GATT_UUID_IEEE_DATA 0x2A2A +#define ESP_GATT_UUID_PNP_ID 0x2A50 + +/* HID characteristics */ +#define ESP_GATT_UUID_HID_INFORMATION 0x2A4A +#define ESP_GATT_UUID_HID_REPORT_MAP 0x2A4B +#define ESP_GATT_UUID_HID_CONTROL_POINT 0x2A4C +#define ESP_GATT_UUID_HID_REPORT 0x2A4D +#define ESP_GATT_UUID_HID_PROTO_MODE 0x2A4E +#define ESP_GATT_UUID_HID_BT_KB_INPUT 0x2A22 +#define ESP_GATT_UUID_HID_BT_KB_OUTPUT 0x2A32 +#define ESP_GATT_UUID_HID_BT_MOUSE_INPUT 0x2A33 + + /// Heart Rate Measurement +#define ESP_GATT_HEART_RATE_MEAS 0x2A37 +/// Body Sensor Location +#define ESP_GATT_BODY_SENSOR_LOCATION 0x2A38 +/// Heart Rate Control Point +#define ESP_GATT_HEART_RATE_CNTL_POINT 0x2A39 + +/* Battery Service characteristics */ +#define ESP_GATT_UUID_BATTERY_LEVEL 0x2A19 + +/* Sensor Service */ +#define ESP_GATT_UUID_SC_CONTROL_POINT 0x2A55 +#define ESP_GATT_UUID_SENSOR_LOCATION 0x2A5D + +/* Runners speed and cadence service */ +#define ESP_GATT_UUID_RSC_MEASUREMENT 0x2A53 +#define ESP_GATT_UUID_RSC_FEATURE 0x2A54 + +/* Cycling speed and cadence service */ +#define ESP_GATT_UUID_CSC_MEASUREMENT 0x2A5B +#define ESP_GATT_UUID_CSC_FEATURE 0x2A5C + +/* Scan ESP_Parameter characteristics */ +#define ESP_GATT_UUID_SCAN_INT_WINDOW 0x2A4F +#define ESP_GATT_UUID_SCAN_REFRESH 0x2A31 +/** + * @} + */ + +/* relate to BTA_GATT_PREP_WRITE_xxx in bta/bta_gatt_api.h */ +/// Attribute write data type from the client +typedef enum { + ESP_GATT_PREP_WRITE_CANCEL = 0x00, /*!< Prepare write cancel */ /* relate to BTA_GATT_PREP_WRITE_CANCEL in bta/bta_gatt_api.h */ + ESP_GATT_PREP_WRITE_EXEC = 0x01, /*!< Prepare write execute */ /* relate to BTA_GATT_PREP_WRITE_EXEC in bta/bta_gatt_api.h */ +} esp_gatt_prep_write_type; + +/* relate to BTA_GATT_xxx in bta/bta_gatt_api.h */ +/** + * @brief GATT success code and error codes + */ +typedef enum { + ESP_GATT_OK = 0x0, /* relate to BTA_GATT_OK in bta/bta_gatt_api.h */ + ESP_GATT_INVALID_HANDLE = 0x01, /* 0x0001 */ /* relate to BTA_GATT_INVALID_HANDLE in bta/bta_gatt_api.h */ + ESP_GATT_READ_NOT_PERMIT = 0x02, /* 0x0002 */ /* relate to BTA_GATT_READ_NOT_PERMIT in bta/bta_gatt_api.h */ + ESP_GATT_WRITE_NOT_PERMIT = 0x03, /* 0x0003 */ /* relate to BTA_GATT_WRITE_NOT_PERMIT in bta/bta_gatt_api.h */ + ESP_GATT_INVALID_PDU = 0x04, /* 0x0004 */ /* relate to BTA_GATT_INVALID_PDU in bta/bta_gatt_api.h */ + ESP_GATT_INSUF_AUTHENTICATION = 0x05, /* 0x0005 */ /* relate to BTA_GATT_INSUF_AUTHENTICATION in bta/bta_gatt_api.h */ + ESP_GATT_REQ_NOT_SUPPORTED = 0x06, /* 0x0006 */ /* relate to BTA_GATT_REQ_NOT_SUPPORTED in bta/bta_gatt_api.h */ + ESP_GATT_INVALID_OFFSET = 0x07, /* 0x0007 */ /* relate to BTA_GATT_INVALID_OFFSET in bta/bta_gatt_api.h */ + ESP_GATT_INSUF_AUTHORIZATION = 0x08, /* 0x0008 */ /* relate to BTA_GATT_INSUF_AUTHORIZATION in bta/bta_gatt_api.h */ + ESP_GATT_PREPARE_Q_FULL = 0x09, /* 0x0009 */ /* relate to BTA_GATT_PREPARE_Q_FULL in bta/bta_gatt_api.h */ + ESP_GATT_NOT_FOUND = 0x0a, /* 0x000a */ /* relate to BTA_GATT_NOT_FOUND in bta/bta_gatt_api.h */ + ESP_GATT_NOT_LONG = 0x0b, /* 0x000b */ /* relate to BTA_GATT_NOT_LONG in bta/bta_gatt_api.h */ + ESP_GATT_INSUF_KEY_SIZE = 0x0c, /* 0x000c */ /* relate to BTA_GATT_INSUF_KEY_SIZE in bta/bta_gatt_api.h */ + ESP_GATT_INVALID_ATTR_LEN = 0x0d, /* 0x000d */ /* relate to BTA_GATT_INVALID_ATTR_LEN in bta/bta_gatt_api.h */ + ESP_GATT_ERR_UNLIKELY = 0x0e, /* 0x000e */ /* relate to BTA_GATT_ERR_UNLIKELY in bta/bta_gatt_api.h */ + ESP_GATT_INSUF_ENCRYPTION = 0x0f, /* 0x000f */ /* relate to BTA_GATT_INSUF_ENCRYPTION in bta/bta_gatt_api.h */ + ESP_GATT_UNSUPPORT_GRP_TYPE = 0x10, /* 0x0010 */ /* relate to BTA_GATT_UNSUPPORT_GRP_TYPE in bta/bta_gatt_api.h */ + ESP_GATT_INSUF_RESOURCE = 0x11, /* 0x0011 */ /* relate to BTA_GATT_INSUF_RESOURCE in bta/bta_gatt_api.h */ + + ESP_GATT_NO_RESOURCES = 0x80, /* 0x80 */ /* relate to BTA_GATT_NO_RESOURCES in bta/bta_gatt_api.h */ + ESP_GATT_INTERNAL_ERROR = 0x81, /* 0x81 */ /* relate to BTA_GATT_INTERNAL_ERROR in bta/bta_gatt_api.h */ + ESP_GATT_WRONG_STATE = 0x82, /* 0x82 */ /* relate to BTA_GATT_WRONG_STATE in bta/bta_gatt_api.h */ + ESP_GATT_DB_FULL = 0x83, /* 0x83 */ /* relate to BTA_GATT_DB_FULL in bta/bta_gatt_api.h */ + ESP_GATT_BUSY = 0x84, /* 0x84 */ /* relate to BTA_GATT_BUSY in bta/bta_gatt_api.h */ + ESP_GATT_ERROR = 0x85, /* 0x85 */ /* relate to BTA_GATT_ERROR in bta/bta_gatt_api.h */ + ESP_GATT_CMD_STARTED = 0x86, /* 0x86 */ /* relate to BTA_GATT_CMD_STARTED in bta/bta_gatt_api.h */ + ESP_GATT_ILLEGAL_PARAMETER = 0x87, /* 0x87 */ /* relate to BTA_GATT_ILLEGAL_PARAMETER in bta/bta_gatt_api.h */ + ESP_GATT_PENDING = 0x88, /* 0x88 */ /* relate to BTA_GATT_PENDING in bta/bta_gatt_api.h */ + ESP_GATT_AUTH_FAIL = 0x89, /* 0x89 */ /* relate to BTA_GATT_AUTH_FAIL in bta/bta_gatt_api.h */ + ESP_GATT_MORE = 0x8a, /* 0x8a */ /* relate to BTA_GATT_MORE in bta/bta_gatt_api.h */ + ESP_GATT_INVALID_CFG = 0x8b, /* 0x8b */ /* relate to BTA_GATT_INVALID_CFG in bta/bta_gatt_api.h */ + ESP_GATT_SERVICE_STARTED = 0x8c, /* 0x8c */ /* relate to BTA_GATT_SERVICE_STARTED in bta/bta_gatt_api.h */ + ESP_GATT_ENCRYPTED_MITM = ESP_GATT_OK, /* relate to BTA_GATT_ENCRYPED_MITM in bta/bta_gatt_api.h */ + ESP_GATT_ENCRYPTED_NO_MITM = 0x8d, /* 0x8d */ /* relate to BTA_GATT_ENCRYPED_NO_MITM in bta/bta_gatt_api.h */ + ESP_GATT_NOT_ENCRYPTED = 0x8e, /* 0x8e */ /* relate to BTA_GATT_NOT_ENCRYPTED in bta/bta_gatt_api.h */ + ESP_GATT_CONGESTED = 0x8f, /* 0x8f */ /* relate to BTA_GATT_CONGESTED in bta/bta_gatt_api.h */ + ESP_GATT_DUP_REG = 0x90, /* 0x90 */ /* relate to BTA_GATT_DUP_REG in bta/bta_gatt_api.h */ + ESP_GATT_ALREADY_OPEN = 0x91, /* 0x91 */ /* relate to BTA_GATT_ALREADY_OPEN in bta/bta_gatt_api.h */ + ESP_GATT_CANCEL = 0x92, /* 0x92 */ /* relate to BTA_GATT_CANCEL in bta/bta_gatt_api.h */ + /* 0xE0 ~ 0xFC reserved for future use */ + ESP_GATT_STACK_RSP = 0xe0, /* 0xe0 */ /* relate to BTA_GATT_STACK_RSP in bta/bta_gatt_api.h */ + ESP_GATT_APP_RSP = 0xe1, /* 0xe1 */ /* relate to BTA_GATT_APP_RSP in bta/bta_gatt_api.h */ + //Error caused by customer application or stack bug + ESP_GATT_UNKNOWN_ERROR = 0xef, /* 0xef */ /* relate to BTA_GATT_UNKNOWN_ERROR in bta/bta_gatt_api.h */ + ESP_GATT_CCC_CFG_ERR = 0xfd, /* 0xFD Client Characteristic Configuration Descriptor Improperly Configured */ /* relate to BTA_GATT_CCC_CFG_ERR in bta/bta_gatt_api.h */ + ESP_GATT_PRC_IN_PROGRESS = 0xfe, /* 0xFE Procedure Already in progress */ /* relate to BTA_GATT_PRC_IN_PROGRESS in bta/bta_gatt_api.h */ + ESP_GATT_OUT_OF_RANGE = 0xff, /* 0xFF Attribute value out of range */ /* relate to BTA_GATT_OUT_OF_RANGE in bta/bta_gatt_api.h */ +} esp_gatt_status_t; + +/* relate to BTA_GATT_CONN_xxx in bta/bta_gatt_api.h */ +/** + * @brief Gatt Connection reason enum + */ +typedef enum { + ESP_GATT_CONN_UNKNOWN = 0, /*!< Gatt connection unknown */ /* relate to BTA_GATT_CONN_UNKNOWN in bta/bta_gatt_api.h */ + ESP_GATT_CONN_L2C_FAILURE = 1, /*!< General L2cap failure */ /* relate to BTA_GATT_CONN_L2C_FAILURE in bta/bta_gatt_api.h */ + ESP_GATT_CONN_TIMEOUT = 0x08, /*!< Connection timeout */ /* relate to BTA_GATT_CONN_TIMEOUT in bta/bta_gatt_api.h */ + ESP_GATT_CONN_TERMINATE_PEER_USER = 0x13, /*!< Connection terminate by peer user */ /* relate to BTA_GATT_CONN_TERMINATE_PEER_USER in bta/bta_gatt_api.h */ + ESP_GATT_CONN_TERMINATE_LOCAL_HOST = 0x16, /*!< Connection terminated by local host */ /* relate to BTA_GATT_CONN_TERMINATE_LOCAL_HOST in bta/bta_gatt_api.h */ + ESP_GATT_CONN_FAIL_ESTABLISH = 0x3e, /*!< Connection fail to establish */ /* relate to BTA_GATT_CONN_FAIL_ESTABLISH in bta/bta_gatt_api.h */ + ESP_GATT_CONN_LMP_TIMEOUT = 0x22, /*!< Connection fail for LMP response tout */ /* relate to BTA_GATT_CONN_LMP_TIMEOUT in bta/bta_gatt_api.h */ + ESP_GATT_CONN_CONN_CANCEL = 0x0100, /*!< L2CAP connection cancelled */ /* relate to BTA_GATT_CONN_CONN_CANCEL in bta/bta_gatt_api.h */ + ESP_GATT_CONN_NONE = 0x0101 /*!< No connection to cancel */ /* relate to BTA_GATT_CONN_NONE in bta/bta_gatt_api.h */ +} esp_gatt_conn_reason_t; + +/** + * @brief Gatt id, include uuid and instance id + */ +typedef struct { + esp_bt_uuid_t uuid; /*!< UUID */ + uint8_t inst_id; /*!< Instance id */ +} __attribute__((packed)) esp_gatt_id_t; + +/** + * @brief Gatt service id, include id + * (uuid and instance id) and primary flag + */ +typedef struct { + esp_gatt_id_t id; /*!< Gatt id, include uuid and instance */ + bool is_primary; /*!< This service is primary or not */ +} __attribute__((packed)) esp_gatt_srvc_id_t; + +/* relate to BTA_GATT_AUTH_REQ_xxx in bta/bta_gatt_api.h */ +/** + * @brief Gatt authentication request type + */ +typedef enum { + ESP_GATT_AUTH_REQ_NONE = 0, /* relate to BTA_GATT_AUTH_REQ_NONE in bta/bta_gatt_api.h */ + ESP_GATT_AUTH_REQ_NO_MITM = 1, /* unauthenticated encryption */ /* relate to BTA_GATT_AUTH_REQ_NO_MITM in bta/bta_gatt_api.h */ + ESP_GATT_AUTH_REQ_MITM = 2, /* authenticated encryption */ /* relate to BTA_GATT_AUTH_REQ_MITM in bta/bta_gatt_api.h */ + ESP_GATT_AUTH_REQ_SIGNED_NO_MITM = 3, /* relate to BTA_GATT_AUTH_REQ_SIGNED_NO_MITM in bta/bta_gatt_api.h */ + ESP_GATT_AUTH_REQ_SIGNED_MITM = 4, /* relate to BTA_GATT_AUTH_REQ_SIGNED_MITM in bta/bta_gatt_api.h */ +} esp_gatt_auth_req_t; + +/* relate to BTA_GATT_PERM_xxx in bta/bta_gatt_api.h */ +/** + * @brief Attribute permissions + */ +#define ESP_GATT_PERM_READ (1 << 0) /* bit 0 - 0x0001 */ /* relate to BTA_GATT_PERM_READ in bta/bta_gatt_api.h */ +#define ESP_GATT_PERM_READ_ENCRYPTED (1 << 1) /* bit 1 - 0x0002 */ /* relate to BTA_GATT_PERM_READ_ENCRYPTED in bta/bta_gatt_api.h */ +#define ESP_GATT_PERM_READ_ENC_MITM (1 << 2) /* bit 2 - 0x0004 */ /* relate to BTA_GATT_PERM_READ_ENC_MITM in bta/bta_gatt_api.h */ +#define ESP_GATT_PERM_WRITE (1 << 4) /* bit 4 - 0x0010 */ /* relate to BTA_GATT_PERM_WRITE in bta/bta_gatt_api.h */ +#define ESP_GATT_PERM_WRITE_ENCRYPTED (1 << 5) /* bit 5 - 0x0020 */ /* relate to BTA_GATT_PERM_WRITE_ENCRYPTED in bta/bta_gatt_api.h */ +#define ESP_GATT_PERM_WRITE_ENC_MITM (1 << 6) /* bit 6 - 0x0040 */ /* relate to BTA_GATT_PERM_WRITE_ENC_MITM in bta/bta_gatt_api.h */ +#define ESP_GATT_PERM_WRITE_SIGNED (1 << 7) /* bit 7 - 0x0080 */ /* relate to BTA_GATT_PERM_WRITE_SIGNED in bta/bta_gatt_api.h */ +#define ESP_GATT_PERM_WRITE_SIGNED_MITM (1 << 8) /* bit 8 - 0x0100 */ /* relate to BTA_GATT_PERM_WRITE_SIGNED_MITM in bta/bta_gatt_api.h */ +#define ESP_GATT_PERM_READ_AUTHORIZATION (1 << 9) /* bit 9 - 0x0200 */ +#define ESP_GATT_PERM_WRITE_AUTHORIZATION (1 << 10) /* bit 10 - 0x0400 */ +#define ESP_GATT_PERM_ENCRYPT_KEY_SIZE(keysize) (((keysize - 6) & 0xF) << 12) /* bit 12:15 - 0xF000 */ +typedef uint16_t esp_gatt_perm_t; + +/* relate to BTA_GATT_CHAR_PROP_BIT_xxx in bta/bta_gatt_api.h */ +/* definition of characteristic properties */ +#define ESP_GATT_CHAR_PROP_BIT_BROADCAST (1 << 0) /* 0x01 */ /* relate to BTA_GATT_CHAR_PROP_BIT_BROADCAST in bta/bta_gatt_api.h */ +#define ESP_GATT_CHAR_PROP_BIT_READ (1 << 1) /* 0x02 */ /* relate to BTA_GATT_CHAR_PROP_BIT_READ in bta/bta_gatt_api.h */ +#define ESP_GATT_CHAR_PROP_BIT_WRITE_NR (1 << 2) /* 0x04 */ /* relate to BTA_GATT_CHAR_PROP_BIT_WRITE_NR in bta/bta_gatt_api.h */ +#define ESP_GATT_CHAR_PROP_BIT_WRITE (1 << 3) /* 0x08 */ /* relate to BTA_GATT_CHAR_PROP_BIT_WRITE in bta/bta_gatt_api.h */ +#define ESP_GATT_CHAR_PROP_BIT_NOTIFY (1 << 4) /* 0x10 */ /* relate to BTA_GATT_CHAR_PROP_BIT_NOTIFY in bta/bta_gatt_api.h */ +#define ESP_GATT_CHAR_PROP_BIT_INDICATE (1 << 5) /* 0x20 */ /* relate to BTA_GATT_CHAR_PROP_BIT_INDICATE in bta/bta_gatt_api.h */ +#define ESP_GATT_CHAR_PROP_BIT_AUTH (1 << 6) /* 0x40 */ /* relate to BTA_GATT_CHAR_PROP_BIT_AUTH in bta/bta_gatt_api.h */ +#define ESP_GATT_CHAR_PROP_BIT_EXT_PROP (1 << 7) /* 0x80 */ /* relate to BTA_GATT_CHAR_PROP_BIT_EXT_PROP in bta/bta_gatt_api.h */ +typedef uint8_t esp_gatt_char_prop_t; + +/// GATT maximum attribute length +#define ESP_GATT_MAX_ATTR_LEN 600 //as same as GATT_MAX_ATTR_LEN + +typedef enum { + ESP_GATT_SERVICE_FROM_REMOTE_DEVICE = 0, /* relate to BTA_GATTC_SERVICE_INFO_FROM_REMOTE_DEVICE in bta_gattc_int.h */ + ESP_GATT_SERVICE_FROM_NVS_FLASH = 1, /* relate to BTA_GATTC_SERVICE_INFO_FROM_NVS_FLASH in bta_gattc_int.h */ + ESP_GATT_SERVICE_FROM_UNKNOWN = 2, /* relate to BTA_GATTC_SERVICE_INFO_FROM_UNKNOWN in bta_gattc_int.h */ +} esp_service_source_t; + +/** + * @brief Attribute description (used to create database) + */ + typedef struct + { + uint16_t uuid_length; /*!< UUID length */ + uint8_t *uuid_p; /*!< UUID value */ + uint16_t perm; /*!< Attribute permission */ + uint16_t max_length; /*!< Maximum length of the element*/ + uint16_t length; /*!< Current length of the element*/ + uint8_t *value; /*!< Element value array*/ + } esp_attr_desc_t; + + +/** + * @brief attribute auto response flag + */ +typedef struct +{ +#define ESP_GATT_RSP_BY_APP 0 +#define ESP_GATT_AUTO_RSP 1 + /** + * @brief if auto_rsp set to ESP_GATT_RSP_BY_APP, means the response of Write/Read operation will by replied by application. + if auto_rsp set to ESP_GATT_AUTO_RSP, means the response of Write/Read operation will be replied by GATT stack automatically. + */ + uint8_t auto_rsp; +} esp_attr_control_t; + + +/** + * @brief attribute type added to the gatt server database + */ +typedef struct +{ + esp_attr_control_t attr_control; /*!< The attribute control type */ + esp_attr_desc_t att_desc; /*!< The attribute type */ +} esp_gatts_attr_db_t; + + +/** + * @brief set the attribute value type + */ +typedef struct +{ + uint16_t attr_max_len; /*!< attribute max value length */ + uint16_t attr_len; /*!< attribute current value length */ + uint8_t *attr_value; /*!< the pointer to attribute value */ +} esp_attr_value_t; + + +/** + * @brief Gatt include service entry element + */ +typedef struct +{ + uint16_t start_hdl; /*!< Gatt start handle value of included service */ + uint16_t end_hdl; /*!< Gatt end handle value of included service */ + uint16_t uuid; /*!< Gatt attribute value UUID of included service */ +} esp_gatts_incl_svc_desc_t; /*!< Gatt include service entry element */ + +/** + * @brief Gatt include 128 bit service entry element + */ +typedef struct +{ + uint16_t start_hdl; /*!< Gatt start handle value of included 128 bit service */ + uint16_t end_hdl; /*!< Gatt end handle value of included 128 bit service */ +} esp_gatts_incl128_svc_desc_t; /*!< Gatt include 128 bit service entry element */ + +/// Gatt attribute value +typedef struct { + uint8_t value[ESP_GATT_MAX_ATTR_LEN]; /*!< Gatt attribute value */ + uint16_t handle; /*!< Gatt attribute handle */ + uint16_t offset; /*!< Gatt attribute value offset */ + uint16_t len; /*!< Gatt attribute value length */ + uint8_t auth_req; /*!< Gatt authentication request */ +} esp_gatt_value_t; + +/// GATT remote read request response type +typedef union { + esp_gatt_value_t attr_value; /*!< Gatt attribute structure */ + uint16_t handle; /*!< Gatt attribute handle */ +} esp_gatt_rsp_t; + +/** + * @brief Gatt write type + */ +typedef enum { + ESP_GATT_WRITE_TYPE_NO_RSP = 1, /*!< Gatt write attribute need no response */ + ESP_GATT_WRITE_TYPE_RSP, /*!< Gatt write attribute need remote response */ +} esp_gatt_write_type_t; + +/** + * @brief Connection parameters information + */ +typedef struct { + uint16_t interval; /*!< connection interval */ + uint16_t latency; /*!< Slave latency for the connection in number of connection events. Range: 0x0000 to 0x01F3 */ + uint16_t timeout; /*!< Supervision timeout for the LE Link. Range: 0x000A to 0x0C80. + Mandatory Range: 0x000A to 0x0C80 Time = N * 10 msec + Time Range: 100 msec to 32 seconds */ +} esp_gatt_conn_params_t; + +#define ESP_GATT_IF_NONE 0xff /*!< If callback report gattc_if/gatts_if as this macro, means this event is not correspond to any app */ + +typedef uint8_t esp_gatt_if_t; /*!< Gatt interface type, different application on GATT client use different gatt_if */ + +/** + * @brief the type of attribute element + */ +typedef enum { + ESP_GATT_DB_PRIMARY_SERVICE, /*!< Gattc primary service attribute type in the cache */ + ESP_GATT_DB_SECONDARY_SERVICE, /*!< Gattc secondary service attribute type in the cache */ + ESP_GATT_DB_CHARACTERISTIC, /*!< Gattc characteristic attribute type in the cache */ + ESP_GATT_DB_DESCRIPTOR, /*!< Gattc characteristic descriptor attribute type in the cache */ + ESP_GATT_DB_INCLUDED_SERVICE, /*!< Gattc include service attribute type in the cache */ + ESP_GATT_DB_ALL, /*!< Gattc all the attribute (primary service & secondary service & include service & char & descriptor) type in the cache */ +} esp_gatt_db_attr_type_t; /*!< Gattc attribute type element */ + +/** + * @brief read multiple attribute + */ +typedef struct { + uint8_t num_attr; /*!< The number of the attribute */ + uint16_t handles[ESP_GATT_MAX_READ_MULTI_HANDLES]; /*!< The handles list */ +} esp_gattc_multi_t; /*!< The gattc multiple read element */ + +/** + * @brief data base attribute element + */ +typedef struct { + esp_gatt_db_attr_type_t type; /*!< The attribute type */ + uint16_t attribute_handle; /*!< The attribute handle, it's valid for all of the type */ + uint16_t start_handle; /*!< The service start handle, it's valid only when the type = ESP_GATT_DB_PRIMARY_SERVICE or ESP_GATT_DB_SECONDARY_SERVICE */ + uint16_t end_handle; /*!< The service end handle, it's valid only when the type = ESP_GATT_DB_PRIMARY_SERVICE or ESP_GATT_DB_SECONDARY_SERVICE */ + esp_gatt_char_prop_t properties; /*!< The characteristic properties, it's valid only when the type = ESP_GATT_DB_CHARACTERISTIC */ + esp_bt_uuid_t uuid; /*!< The attribute uuid, it's valid for all of the type */ +} esp_gattc_db_elem_t; /*!< The gattc service data base element in the cache */ + +/** + * @brief service element + */ +typedef struct { + bool is_primary; /*!< The service flag, true if the service is primary service, else is secondary service */ + uint16_t start_handle; /*!< The start handle of the service */ + uint16_t end_handle; /*!< The end handle of the service */ + esp_bt_uuid_t uuid; /*!< The uuid of the service */ +} esp_gattc_service_elem_t; /*!< The gattc service element */ + +/** + * @brief characteristic element + */ +typedef struct { + uint16_t char_handle; /*!< The characteristic handle */ + esp_gatt_char_prop_t properties; /*!< The characteristic properties */ + esp_bt_uuid_t uuid; /*!< The characteristic uuid */ +} esp_gattc_char_elem_t; /*!< The gattc characteristic element */ + +/** + * @brief descriptor element + */ +typedef struct { + uint16_t handle; /*!< The characteristic descriptor handle */ + esp_bt_uuid_t uuid; /*!< The characteristic descriptor uuid */ +} esp_gattc_descr_elem_t; /*!< The gattc descriptor type element */ + +/** + * @brief include service element + */ +typedef struct { + uint16_t handle; /*!< The include service current attribute handle */ + uint16_t incl_srvc_s_handle; /*!< The start handle of the service which has been included */ + uint16_t incl_srvc_e_handle; /*!< The end handle of the service which has been included */ + esp_bt_uuid_t uuid; /*!< The include service uuid */ +} esp_gattc_incl_svc_elem_t; /*!< The gattc include service element */ + + +#ifdef __cplusplus +} +#endif + +#endif /* __ESP_GATT_DEFS_H__ */ diff --git a/lib/bt/host/bluedroid/api/include/api/esp_gattc_api.h b/lib/bt/host/bluedroid/api/include/api/esp_gattc_api.h new file mode 100644 index 00000000..13bf16a3 --- /dev/null +++ b/lib/bt/host/bluedroid/api/include/api/esp_gattc_api.h @@ -0,0 +1,913 @@ +/* + * SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __ESP_GATTC_API_H__ +#define __ESP_GATTC_API_H__ + +#include "esp_bt_defs.h" +#include "esp_gatt_defs.h" +#include "esp_err.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/// GATT Client callback function events +typedef enum { + ESP_GATTC_REG_EVT = 0, /*!< When GATT client is registered, the event comes */ + ESP_GATTC_UNREG_EVT = 1, /*!< When GATT client is unregistered, the event comes */ + ESP_GATTC_OPEN_EVT = 2, /*!< When GATT virtual connection is set up, the event comes */ + ESP_GATTC_READ_CHAR_EVT = 3, /*!< When GATT characteristic is read, the event comes */ + ESP_GATTC_WRITE_CHAR_EVT = 4, /*!< When GATT characteristic write operation completes, the event comes */ + ESP_GATTC_CLOSE_EVT = 5, /*!< When GATT virtual connection is closed, the event comes */ + ESP_GATTC_SEARCH_CMPL_EVT = 6, /*!< When GATT service discovery is completed, the event comes */ + ESP_GATTC_SEARCH_RES_EVT = 7, /*!< When GATT service discovery result is got, the event comes */ + ESP_GATTC_READ_DESCR_EVT = 8, /*!< When GATT characteristic descriptor read completes, the event comes */ + ESP_GATTC_WRITE_DESCR_EVT = 9, /*!< When GATT characteristic descriptor write completes, the event comes */ + ESP_GATTC_NOTIFY_EVT = 10, /*!< When GATT notification or indication arrives, the event comes */ + ESP_GATTC_PREP_WRITE_EVT = 11, /*!< When GATT prepare-write operation completes, the event comes */ + ESP_GATTC_EXEC_EVT = 12, /*!< When write execution completes, the event comes */ + ESP_GATTC_ACL_EVT = 13, /*!< When ACL connection is up, the event comes */ + ESP_GATTC_CANCEL_OPEN_EVT = 14, /*!< When GATT client ongoing connection is cancelled, the event comes */ + ESP_GATTC_SRVC_CHG_EVT = 15, /*!< When "service changed" occurs, the event comes */ + ESP_GATTC_ENC_CMPL_CB_EVT = 17, /*!< When encryption procedure completes, the event comes */ + ESP_GATTC_CFG_MTU_EVT = 18, /*!< When configuration of MTU completes, the event comes */ + ESP_GATTC_ADV_DATA_EVT = 19, /*!< When advertising of data, the event comes */ + ESP_GATTC_MULT_ADV_ENB_EVT = 20, /*!< When multi-advertising is enabled, the event comes */ + ESP_GATTC_MULT_ADV_UPD_EVT = 21, /*!< When multi-advertising parameters are updated, the event comes */ + ESP_GATTC_MULT_ADV_DATA_EVT = 22, /*!< When multi-advertising data arrives, the event comes */ + ESP_GATTC_MULT_ADV_DIS_EVT = 23, /*!< When multi-advertising is disabled, the event comes */ + ESP_GATTC_CONGEST_EVT = 24, /*!< When GATT connection congestion comes, the event comes */ + ESP_GATTC_BTH_SCAN_ENB_EVT = 25, /*!< When batch scan is enabled, the event comes */ + ESP_GATTC_BTH_SCAN_CFG_EVT = 26, /*!< When batch scan storage is configured, the event comes */ + ESP_GATTC_BTH_SCAN_RD_EVT = 27, /*!< When Batch scan read event is reported, the event comes */ + ESP_GATTC_BTH_SCAN_THR_EVT = 28, /*!< When Batch scan threshold is set, the event comes */ + ESP_GATTC_BTH_SCAN_PARAM_EVT = 29, /*!< When Batch scan parameters are set, the event comes */ + ESP_GATTC_BTH_SCAN_DIS_EVT = 30, /*!< When Batch scan is disabled, the event comes */ + ESP_GATTC_SCAN_FLT_CFG_EVT = 31, /*!< When Scan filter configuration completes, the event comes */ + ESP_GATTC_SCAN_FLT_PARAM_EVT = 32, /*!< When Scan filter parameters are set, the event comes */ + ESP_GATTC_SCAN_FLT_STATUS_EVT = 33, /*!< When Scan filter status is reported, the event comes */ + ESP_GATTC_ADV_VSC_EVT = 34, /*!< When advertising vendor spec content event is reported, the event comes */ + ESP_GATTC_REG_FOR_NOTIFY_EVT = 38, /*!< When register for notification of a service completes, the event comes */ + ESP_GATTC_UNREG_FOR_NOTIFY_EVT = 39, /*!< When unregister for notification of a service completes, the event comes */ + ESP_GATTC_CONNECT_EVT = 40, /*!< When the ble physical connection is set up, the event comes */ + ESP_GATTC_DISCONNECT_EVT = 41, /*!< When the ble physical connection disconnected, the event comes */ + ESP_GATTC_READ_MULTIPLE_EVT = 42, /*!< When the ble characteristic or descriptor multiple complete, the event comes */ + ESP_GATTC_QUEUE_FULL_EVT = 43, /*!< When the gattc command queue full, the event comes */ + ESP_GATTC_SET_ASSOC_EVT = 44, /*!< When the ble gattc set the associated address complete, the event comes */ + ESP_GATTC_GET_ADDR_LIST_EVT = 45, /*!< When the ble get gattc address list in cache finish, the event comes */ + ESP_GATTC_DIS_SRVC_CMPL_EVT = 46, /*!< When the ble discover service complete, the event comes */ + ESP_GATTC_READ_MULTI_VAR_EVT = 47, /*!< When read multiple variable characteristic complete, the event comes */ +} esp_gattc_cb_event_t; + + +/** + * @brief Gatt client callback parameters union + */ +typedef union { + /** + * @brief ESP_GATTC_REG_EVT + */ + struct gattc_reg_evt_param { + esp_gatt_status_t status; /*!< Operation status */ + uint16_t app_id; /*!< Application id which input in register API */ + } reg; /*!< Gatt client callback param of ESP_GATTC_REG_EVT */ + + /** + * @brief ESP_GATTC_OPEN_EVT + */ + struct gattc_open_evt_param { + esp_gatt_status_t status; /*!< Operation status */ + uint16_t conn_id; /*!< Connection id */ + esp_bd_addr_t remote_bda; /*!< Remote bluetooth device address */ + uint16_t mtu; /*!< MTU size */ + } open; /*!< Gatt client callback param of ESP_GATTC_OPEN_EVT */ + + /** + * @brief ESP_GATTC_CLOSE_EVT + */ + struct gattc_close_evt_param { + esp_gatt_status_t status; /*!< Operation status */ + uint16_t conn_id; /*!< Connection id */ + esp_bd_addr_t remote_bda; /*!< Remote bluetooth device address */ + esp_gatt_conn_reason_t reason; /*!< The reason of gatt connection close */ + } close; /*!< Gatt client callback param of ESP_GATTC_CLOSE_EVT */ + + /** + * @brief ESP_GATTC_CFG_MTU_EVT + */ + struct gattc_cfg_mtu_evt_param { + esp_gatt_status_t status; /*!< Operation status */ + uint16_t conn_id; /*!< Connection id */ + uint16_t mtu; /*!< MTU size */ + } cfg_mtu; /*!< Gatt client callback param of ESP_GATTC_CFG_MTU_EVT */ + + /** + * @brief ESP_GATTC_SEARCH_CMPL_EVT + */ + struct gattc_search_cmpl_evt_param { + esp_gatt_status_t status; /*!< Operation status */ + uint16_t conn_id; /*!< Connection id */ + esp_service_source_t searched_service_source; /*!< The source of the service information */ + } search_cmpl; /*!< Gatt client callback param of ESP_GATTC_SEARCH_CMPL_EVT */ + + /** + * @brief ESP_GATTC_SEARCH_RES_EVT + */ + struct gattc_search_res_evt_param { + uint16_t conn_id; /*!< Connection id */ + uint16_t start_handle; /*!< Service start handle */ + uint16_t end_handle; /*!< Service end handle */ + esp_gatt_id_t srvc_id; /*!< Service id, include service uuid and other information */ + bool is_primary; /*!< True if this is the primary service */ + } search_res; /*!< Gatt client callback param of ESP_GATTC_SEARCH_RES_EVT */ + + /** + * @brief ESP_GATTC_READ_CHAR_EVT, ESP_GATTC_READ_DESCR_EVT, ESP_GATTC_READ_MULTIPLE_EVT, ESP_GATTC_READ_MULTI_VAR_EVT + */ + struct gattc_read_char_evt_param { + + esp_gatt_status_t status; /*!< Operation status */ + uint16_t conn_id; /*!< Connection id */ + uint16_t handle; /*!< Characteristic handle */ + uint8_t *value; /*!< Characteristic value */ + uint16_t value_len; /*!< Characteristic value length */ + } read; /*!< Gatt client callback param of ESP_GATTC_READ_CHAR_EVT */ + + /** + * @brief ESP_GATTC_WRITE_CHAR_EVT, ESP_GATTC_PREP_WRITE_EVT, ESP_GATTC_WRITE_DESCR_EVT + */ + struct gattc_write_evt_param { + esp_gatt_status_t status; /*!< Operation status */ + uint16_t conn_id; /*!< Connection id */ + uint16_t handle; /*!< The Characteristic or descriptor handle */ + uint16_t offset; /*!< The prepare write offset, this value is valid only when prepare write */ + } write; /*!< Gatt client callback param of ESP_GATTC_WRITE_DESCR_EVT */ + + /** + * @brief ESP_GATTC_EXEC_EVT + */ + struct gattc_exec_cmpl_evt_param { + esp_gatt_status_t status; /*!< Operation status */ + uint16_t conn_id; /*!< Connection id */ + } exec_cmpl; /*!< Gatt client callback param of ESP_GATTC_EXEC_EVT */ + + /** + * @brief ESP_GATTC_NOTIFY_EVT + */ + struct gattc_notify_evt_param { + uint16_t conn_id; /*!< Connection id */ + esp_bd_addr_t remote_bda; /*!< Remote bluetooth device address */ + uint16_t handle; /*!< The Characteristic or descriptor handle */ + uint16_t value_len; /*!< Notify attribute value */ + uint8_t *value; /*!< Notify attribute value */ + bool is_notify; /*!< True means notify, false means indicate */ + } notify; /*!< Gatt client callback param of ESP_GATTC_NOTIFY_EVT */ + + /** + * @brief ESP_GATTC_SRVC_CHG_EVT + */ + struct gattc_srvc_chg_evt_param { + esp_bd_addr_t remote_bda; /*!< Remote bluetooth device address */ + } srvc_chg; /*!< Gatt client callback param of ESP_GATTC_SRVC_CHG_EVT */ + + /** + * @brief ESP_GATTC_CONGEST_EVT + */ + struct gattc_congest_evt_param { + uint16_t conn_id; /*!< Connection id */ + bool congested; /*!< Congested or not */ + } congest; /*!< Gatt client callback param of ESP_GATTC_CONGEST_EVT */ + /** + * @brief ESP_GATTC_REG_FOR_NOTIFY_EVT + */ + struct gattc_reg_for_notify_evt_param { + esp_gatt_status_t status; /*!< Operation status */ + uint16_t handle; /*!< The characteristic or descriptor handle */ + } reg_for_notify; /*!< Gatt client callback param of ESP_GATTC_REG_FOR_NOTIFY_EVT */ + + /** + * @brief ESP_GATTC_UNREG_FOR_NOTIFY_EVT + */ + struct gattc_unreg_for_notify_evt_param { + esp_gatt_status_t status; /*!< Operation status */ + uint16_t handle; /*!< The characteristic or descriptor handle */ + } unreg_for_notify; /*!< Gatt client callback param of ESP_GATTC_UNREG_FOR_NOTIFY_EVT */ + + /** + * @brief ESP_GATTC_CONNECT_EVT + */ + struct gattc_connect_evt_param { + uint16_t conn_id; /*!< Connection id */ + uint8_t link_role; /*!< Link role : master role = 0 ; slave role = 1*/ + esp_bd_addr_t remote_bda; /*!< Remote bluetooth device address */ + esp_gatt_conn_params_t conn_params; /*!< current connection parameters */ + esp_ble_addr_type_t ble_addr_type; /*!< Remote BLE device address type */ + uint16_t conn_handle; /*!< HCI connection handle */ + } connect; /*!< Gatt client callback param of ESP_GATTC_CONNECT_EVT */ + + /** + * @brief ESP_GATTC_DISCONNECT_EVT + */ + struct gattc_disconnect_evt_param { + esp_gatt_conn_reason_t reason; /*!< disconnection reason */ + uint16_t conn_id; /*!< Connection id */ + esp_bd_addr_t remote_bda; /*!< Remote bluetooth device address */ + } disconnect; /*!< Gatt client callback param of ESP_GATTC_DISCONNECT_EVT */ + /** + * @brief ESP_GATTC_SET_ASSOC_EVT + */ + struct gattc_set_assoc_addr_cmp_evt_param { + esp_gatt_status_t status; /*!< Operation status */ + } set_assoc_cmp; /*!< Gatt client callback param of ESP_GATTC_SET_ASSOC_EVT */ + /** + * @brief ESP_GATTC_GET_ADDR_LIST_EVT + */ + struct gattc_get_addr_list_evt_param { + esp_gatt_status_t status; /*!< Operation status */ + uint8_t num_addr; /*!< The number of address in the gattc cache address list */ + esp_bd_addr_t *addr_list; /*!< The pointer to the address list which has been get from the gattc cache */ + } get_addr_list; /*!< Gatt client callback param of ESP_GATTC_GET_ADDR_LIST_EVT */ + + /** + * @brief ESP_GATTC_QUEUE_FULL_EVT + */ + struct gattc_queue_full_evt_param { + esp_gatt_status_t status; /*!< Operation status */ + uint16_t conn_id; /*!< Connection id */ + bool is_full; /*!< The gattc command queue is full or not */ + } queue_full; /*!< Gatt client callback param of ESP_GATTC_QUEUE_FULL_EVT */ + + /** + * @brief ESP_GATTC_DIS_SRVC_CMPL_EVT + */ + struct gattc_dis_srvc_cmpl_evt_param { + esp_gatt_status_t status; /*!< Operation status */ + uint16_t conn_id; /*!< Connection id */ + } dis_srvc_cmpl; /*!< Gatt client callback param of ESP_GATTC_DIS_SRVC_CMPL_EVT */ + +} esp_ble_gattc_cb_param_t; /*!< GATT client callback parameter union type */ + +/** + * @brief GATT Client callback function type + * @param event : Event type + * @param gattc_if : GATT client access interface, normally + * different gattc_if correspond to different profile + * @param param : Point to callback parameter, currently is union type + */ +typedef void (* esp_gattc_cb_t)(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param); + +/** + * @brief This function is called to register application callbacks + * with GATTC module. + * + * @param[in] callback : pointer to the application callback function. + * + * @return + * - ESP_OK: success + * - other: failed + * + */ +esp_err_t esp_ble_gattc_register_callback(esp_gattc_cb_t callback); + +/** + * @brief This function is called to get the current application callbacks + * with BTA GATTC module. + * + * @return + * - esp_gattC_cb_t : current callback + * + */ +esp_gattc_cb_t esp_ble_gattc_get_callback(void); + +/** + * @brief This function is called to register application callbacks + * with GATTC module. + * + * @param[in] app_id : Application Identify (UUID), for different application + * + * @return + * - ESP_OK: success + * - other: failed + * + */ +esp_err_t esp_ble_gattc_app_register(uint16_t app_id); + + +/** + * @brief This function is called to unregister an application + * from GATTC module. + * + * @param[in] gattc_if: Gatt client access interface. + * + * @return + * - ESP_OK: success + * - other: failed + * + */ +esp_err_t esp_ble_gattc_app_unregister(esp_gatt_if_t gattc_if); + +#if (BLE_42_FEATURE_SUPPORT == TRUE) +/** + * @brief Open a direct connection or add a background auto connection + * + * @param[in] gattc_if: Gatt client access interface. + * @param[in] remote_bda: remote device bluetooth device address. + * @param[in] remote_addr_type: remote device bluetooth device the address type. + * @param[in] is_direct: direct connection or background auto connection(by now, background auto connection is not supported). + * + * @return + * - ESP_OK: success + * - other: failed + * + */ +esp_err_t esp_ble_gattc_open(esp_gatt_if_t gattc_if, esp_bd_addr_t remote_bda, esp_ble_addr_type_t remote_addr_type, bool is_direct); +#endif // #if (BLE_42_FEATURE_SUPPORT == TRUE) + +#if (BLE_50_FEATURE_SUPPORT == TRUE) +esp_err_t esp_ble_gattc_aux_open(esp_gatt_if_t gattc_if, esp_bd_addr_t remote_bda, esp_ble_addr_type_t remote_addr_type, bool is_direct); +#endif // #if (BLE_50_FEATURE_SUPPORT == TRUE) +/** + * @brief Close the virtual connection to the GATT server. gattc may have multiple virtual GATT server connections when multiple app_id registered, + * this API only close one virtual GATT server connection. if there exist other virtual GATT server connections, + * it does not disconnect the physical connection. + * if you want to disconnect the physical connection directly, you can use esp_ble_gap_disconnect(esp_bd_addr_t remote_device). + * + * @param[in] gattc_if: Gatt client access interface. + * @param[in] conn_id: connection ID to be closed. + * + * @return + * - ESP_OK: success + * - other: failed + * + */ +esp_err_t esp_ble_gattc_close (esp_gatt_if_t gattc_if, uint16_t conn_id); + + +/** + * @brief Configure the MTU size in the GATT channel. This can be done + * only once per connection. Before using, use esp_ble_gatt_set_local_mtu() + * to configure the local MTU size. + * + * + * @param[in] gattc_if: Gatt client access interface. + * @param[in] conn_id: connection ID. + * + * @return + * - ESP_OK: success + * - other: failed + * + */ +esp_err_t esp_ble_gattc_send_mtu_req (esp_gatt_if_t gattc_if, uint16_t conn_id); + + +/** + * @brief This function is called to get service from local cache. + * This function report service search result by a callback + * event, and followed by a service search complete event. + * Note: 128-bit base UUID will automatically be converted to a 16-bit UUID in the search results. Other types of UUID remain unchanged. + * + * @param[in] gattc_if: Gatt client access interface. + * @param[in] conn_id: connection ID. + * @param[in] filter_uuid: a UUID of the service application is interested in. + * If Null, discover for all services. + * + * @return + * - ESP_OK: success + * - other: failed + * + */ +esp_err_t esp_ble_gattc_search_service(esp_gatt_if_t gattc_if, uint16_t conn_id, esp_bt_uuid_t *filter_uuid); + +/** + * @brief Find all the service with the given service uuid in the gattc cache, if the svc_uuid is NULL, find all the service. + * Note: It just get service from local cache, won't get from remote devices. If want to get it from remote device, need + * to used the esp_ble_gattc_cache_refresh, then call esp_ble_gattc_get_service again. + * + * @param[in] gattc_if: Gatt client access interface. + * @param[in] conn_id: connection ID which identify the server. + * @param[in] svc_uuid: the pointer to the service uuid. + * @param[out] result: The pointer to the service which has been found in the gattc cache. + * @param[inout] count: input the number of service want to find, + * it will output the number of service has been found in the gattc cache with the given service uuid. + * @param[in] offset: Offset of the service position to get. + * + * @return + * - ESP_OK: success + * - other: failed + * + */ +esp_gatt_status_t esp_ble_gattc_get_service(esp_gatt_if_t gattc_if, uint16_t conn_id, esp_bt_uuid_t *svc_uuid, + esp_gattc_service_elem_t *result, uint16_t *count, uint16_t offset); + +/** + * @brief Find all the characteristic with the given service in the gattc cache + * Note: It just get characteristic from local cache, won't get from remote devices. + * + * @param[in] gattc_if: Gatt client access interface. + * @param[in] conn_id: connection ID which identify the server. + * @param[in] start_handle: the attribute start handle. + * @param[in] end_handle: the attribute end handle + * @param[out] result: The pointer to the characteristic in the service. + * @param[inout] count: input the number of characteristic want to find, + * it will output the number of characteristic has been found in the gattc cache with the given service. + * @param[in] offset: Offset of the characteristic position to get. + * + * @return + * - ESP_OK: success + * - other: failed + * + */ +esp_gatt_status_t esp_ble_gattc_get_all_char(esp_gatt_if_t gattc_if, + uint16_t conn_id, + uint16_t start_handle, + uint16_t end_handle, + esp_gattc_char_elem_t *result, + uint16_t *count, uint16_t offset); + +/** + * @brief Find all the descriptor with the given characteristic in the gattc cache + * Note: It just get descriptor from local cache, won't get from remote devices. + * + * @param[in] gattc_if: Gatt client access interface. + * @param[in] conn_id: connection ID which identify the server. + * @param[in] char_handle: the given characteristic handle + * @param[out] result: The pointer to the descriptor in the characteristic. + * @param[inout] count: input the number of descriptor want to find, + * it will output the number of descriptor has been found in the gattc cache with the given characteristic. + * @param[in] offset: Offset of the descriptor position to get. + * + * @return + * - ESP_OK: success + * - other: failed + * + */ +esp_gatt_status_t esp_ble_gattc_get_all_descr(esp_gatt_if_t gattc_if, + uint16_t conn_id, + uint16_t char_handle, + esp_gattc_descr_elem_t *result, + uint16_t *count, uint16_t offset); + + +/** + * @brief Find the characteristic with the given characteristic uuid in the gattc cache + * Note: It just get characteristic from local cache, won't get from remote devices. + * + * @param[in] gattc_if: Gatt client access interface. + * @param[in] conn_id: connection ID which identify the server. + * @param[in] start_handle: the attribute start handle + * @param[in] end_handle: the attribute end handle + * @param[in] char_uuid: the characteristic uuid + * @param[out] result: The pointer to the characteristic in the service. + * @param[inout] count: input the number of characteristic want to find, + * it will output the number of characteristic has been found in the gattc cache with the given service. + * + * @return + * - ESP_OK: success + * - other: failed + * + */ +esp_gatt_status_t esp_ble_gattc_get_char_by_uuid(esp_gatt_if_t gattc_if, + uint16_t conn_id, + uint16_t start_handle, + uint16_t end_handle, + esp_bt_uuid_t char_uuid, + esp_gattc_char_elem_t *result, + uint16_t *count); + +/** + * @brief Find the descriptor with the given characteristic uuid in the gattc cache + * Note: It just get descriptor from local cache, won't get from remote devices. + * + * @param[in] gattc_if: Gatt client access interface. + * @param[in] conn_id: connection ID which identify the server. + * @param[in] start_handle: the attribute start handle + * @param[in] end_handle: the attribute end handle + * @param[in] char_uuid: the characteristic uuid. + * @param[in] descr_uuid: the descriptor uuid. + * @param[out] result: The pointer to the descriptor in the given characteristic. + * @param[inout] count: input the number of descriptor want to find, + * it will output the number of descriptor has been found in the gattc cache with the given characteristic. + * + * @return + * - ESP_OK: success + * - other: failed + * + */ +esp_gatt_status_t esp_ble_gattc_get_descr_by_uuid(esp_gatt_if_t gattc_if, + uint16_t conn_id, + uint16_t start_handle, + uint16_t end_handle, + esp_bt_uuid_t char_uuid, + esp_bt_uuid_t descr_uuid, + esp_gattc_descr_elem_t *result, + uint16_t *count); + +/** + * @brief Find the descriptor with the given characteristic handle in the gattc cache + * Note: It just get descriptor from local cache, won't get from remote devices. + * + * @param[in] gattc_if: Gatt client access interface. + * @param[in] conn_id: connection ID which identify the server. + * @param[in] char_handle: the characteristic handle. + * @param[in] descr_uuid: the descriptor uuid. + * @param[out] result: The pointer to the descriptor in the given characteristic. + * @param[inout] count: input the number of descriptor want to find, + * it will output the number of descriptor has been found in the gattc cache with the given characteristic. + * + * @return + * - ESP_OK: success + * - other: failed + * + */ +esp_gatt_status_t esp_ble_gattc_get_descr_by_char_handle(esp_gatt_if_t gattc_if, + uint16_t conn_id, + uint16_t char_handle, + esp_bt_uuid_t descr_uuid, + esp_gattc_descr_elem_t *result, + uint16_t *count); + +/** + * @brief Find the include service with the given service handle in the gattc cache + * Note: It just get include service from local cache, won't get from remote devices. + * + * @param[in] gattc_if: Gatt client access interface. + * @param[in] conn_id: connection ID which identify the server. + * @param[in] start_handle: the attribute start handle + * @param[in] end_handle: the attribute end handle + * @param[in] incl_uuid: the include service uuid + * @param[out] result: The pointer to the include service in the given service. + * @param[inout] count: input the number of include service want to find, + * it will output the number of include service has been found in the gattc cache with the given service. + * + * @return + * - ESP_OK: success + * - other: failed + * + */ +esp_gatt_status_t esp_ble_gattc_get_include_service(esp_gatt_if_t gattc_if, + uint16_t conn_id, + uint16_t start_handle, + uint16_t end_handle, + esp_bt_uuid_t *incl_uuid, + esp_gattc_incl_svc_elem_t *result, + uint16_t *count); + + +/** + * @brief Find the attribute count with the given service or characteristic in the gattc cache + * + * @param[in] gattc_if: Gatt client access interface. + * @param[in] conn_id: connection ID which identify the server. + * @param[in] type: the attribute type. + * @param[in] start_handle: the attribute start handle, if the type is ESP_GATT_DB_DESCRIPTOR, this parameter should be ignore + * @param[in] end_handle: the attribute end handle, if the type is ESP_GATT_DB_DESCRIPTOR, this parameter should be ignore + * @param[in] char_handle: the characteristic handle, this parameter valid when the type is ESP_GATT_DB_DESCRIPTOR. If the type + * isn't ESP_GATT_DB_DESCRIPTOR, this parameter should be ignore. + * @param[out] count: output the number of attribute has been found in the gattc cache with the given attribute type. + * + * @return + * - ESP_OK: success + * - other: failed + * + */ +esp_gatt_status_t esp_ble_gattc_get_attr_count(esp_gatt_if_t gattc_if, + uint16_t conn_id, + esp_gatt_db_attr_type_t type, + uint16_t start_handle, + uint16_t end_handle, + uint16_t char_handle, + uint16_t *count); + +/** + * @brief This function is called to get the GATT database. + * Note: It just get attribute data base from local cache, won't get from remote devices. + * + * @param[in] gattc_if: Gatt client access interface. + * @param[in] start_handle: the attribute start handle + * @param[in] end_handle: the attribute end handle + * @param[in] conn_id: connection ID which identify the server. + * @param[in] db: output parameter which will contain the GATT database copy. + * Caller is responsible for freeing it. + * @param[in] count: number of elements in database. + * + * @return + * - ESP_OK: success + * - other: failed + * + */ +esp_gatt_status_t esp_ble_gattc_get_db(esp_gatt_if_t gattc_if, uint16_t conn_id, uint16_t start_handle, uint16_t end_handle, + esp_gattc_db_elem_t *db, uint16_t *count); + +/** + * @brief This function is called to read a service's characteristics of + * the given characteristic handle + * + * @param[in] gattc_if: Gatt client access interface. + * @param[in] conn_id : connection ID. + * @param[in] handle : characteritic handle to read. + * @param[in] auth_req : authenticate request type + * + * @return + * - ESP_OK: success + * - other: failed + * + */ +esp_err_t esp_ble_gattc_read_char (esp_gatt_if_t gattc_if, + uint16_t conn_id, + uint16_t handle, + esp_gatt_auth_req_t auth_req); + +/** + * @brief This function is called to read a service's characteristics of + * the given characteristic UUID + * + * @param[in] gattc_if: Gatt client access interface. + * @param[in] conn_id : connection ID. + * @param[in] start_handle : the attribute start handle. + * @param[in] end_handle : the attribute end handle + * @param[in] uuid : The UUID of attribute which will be read. + * @param[in] auth_req : authenticate request type + * + * @return + * - ESP_OK: success + * - other: failed + * + */ +esp_err_t esp_ble_gattc_read_by_type (esp_gatt_if_t gattc_if, + uint16_t conn_id, + uint16_t start_handle, + uint16_t end_handle, + esp_bt_uuid_t *uuid, + esp_gatt_auth_req_t auth_req); + +/** + * @brief This function is called to read multiple characteristic or + * characteristic descriptors. + * + * @param[in] gattc_if: Gatt client access interface. + * @param[in] conn_id : connection ID. + * @param[in] read_multi : pointer to the read multiple parameter. + * @param[in] auth_req : authenticate request type + * + * @return + * - ESP_OK: success + * - other: failed + * + */ +esp_err_t esp_ble_gattc_read_multiple(esp_gatt_if_t gattc_if, + uint16_t conn_id, esp_gattc_multi_t *read_multi, + esp_gatt_auth_req_t auth_req); + +/** + * @brief This function is called to read multiple variable length characteristic or + * characteristic descriptors. + * + * @param[in] gattc_if: Gatt client access interface. + * @param[in] conn_id : connection ID. + * @param[in] read_multi : pointer to the read multiple parameter. + * @param[in] auth_req : authenticate request type + * + * @return + * - ESP_OK: success + * - other: failed + * + */ +esp_err_t esp_ble_gattc_read_multiple_variable(esp_gatt_if_t gattc_if, + uint16_t conn_id, esp_gattc_multi_t *read_multi, + esp_gatt_auth_req_t auth_req); + +/** + * @brief This function is called to read a characteristics descriptor. + * + * @param[in] gattc_if: Gatt client access interface. + * @param[in] conn_id : connection ID. + * @param[in] handle : descriptor handle to read. + * @param[in] auth_req : authenticate request type + * + * @return + * - ESP_OK: success + * - other: failed + * + */ +esp_err_t esp_ble_gattc_read_char_descr (esp_gatt_if_t gattc_if, + uint16_t conn_id, + uint16_t handle, + esp_gatt_auth_req_t auth_req); + + +/** + * @brief This function is called to write characteristic value. + * + * @param[in] gattc_if: Gatt client access interface. + * @param[in] conn_id : connection ID. + * @param[in] handle : characteristic handle to write. + * @param[in] value_len: length of the value to be written. + * @param[in] value : the value to be written. + * @param[in] write_type : the type of attribute write operation. + * @param[in] auth_req : authentication request. + * + * @return + * - ESP_OK: success + * - other: failed + * + */ +esp_err_t esp_ble_gattc_write_char( esp_gatt_if_t gattc_if, + uint16_t conn_id, + uint16_t handle, + uint16_t value_len, + uint8_t *value, + esp_gatt_write_type_t write_type, + esp_gatt_auth_req_t auth_req); + + +/** + * @brief This function is called to write characteristic descriptor value. + * + * @param[in] gattc_if: Gatt client access interface. + * @param[in] conn_id : connection ID + * @param[in] handle : descriptor handle to write. + * @param[in] value_len: length of the value to be written. + * @param[in] value : the value to be written. + * @param[in] write_type : the type of attribute write operation. + * @param[in] auth_req : authentication request. + * + * @return + * - ESP_OK: success + * - other: failed + * + */ +esp_err_t esp_ble_gattc_write_char_descr (esp_gatt_if_t gattc_if, + uint16_t conn_id, + uint16_t handle, + uint16_t value_len, + uint8_t *value, + esp_gatt_write_type_t write_type, + esp_gatt_auth_req_t auth_req); + + +/** + * @brief This function is called to prepare write a characteristic value. + * + * @param[in] gattc_if: Gatt client access interface. + * @param[in] conn_id : connection ID. + * @param[in] handle : characteristic handle to prepare write. + * @param[in] offset : offset of the write value. + * @param[in] value_len: length of the value to be written. + * @param[in] value : the value to be written. + * @param[in] auth_req : authentication request. + * + * @return + * - ESP_OK: success + * - other: failed + * + */ +esp_err_t esp_ble_gattc_prepare_write(esp_gatt_if_t gattc_if, + uint16_t conn_id, + uint16_t handle, + uint16_t offset, + uint16_t value_len, + uint8_t *value, + esp_gatt_auth_req_t auth_req); + + +/** + * @brief This function is called to prepare write a characteristic descriptor value. + * + * @param[in] gattc_if: Gatt client access interface. + * @param[in] conn_id : connection ID. + * @param[in] handle : characteristic descriptor handle to prepare write. + * @param[in] offset : offset of the write value. + * @param[in] value_len: length of the value to be written. + * @param[in] value : the value to be written. + * @param[in] auth_req : authentication request. + * + * @return + * - ESP_OK: success + * - other: failed + * + */ +esp_err_t esp_ble_gattc_prepare_write_char_descr(esp_gatt_if_t gattc_if, + uint16_t conn_id, + uint16_t handle, + uint16_t offset, + uint16_t value_len, + uint8_t *value, + esp_gatt_auth_req_t auth_req); + + +/** + * @brief This function is called to execute write a prepare write sequence. + * + * @param[in] gattc_if: Gatt client access interface. + * @param[in] conn_id : connection ID. + * @param[in] is_execute : execute or cancel. + * + * @return + * - ESP_OK: success + * - other: failed + * + */ +esp_err_t esp_ble_gattc_execute_write (esp_gatt_if_t gattc_if, uint16_t conn_id, bool is_execute); + + +/** + * @brief This function is called to register for notification of a service. + * + * @param[in] gattc_if: Gatt client access interface. + * @param[in] server_bda : target GATT server. + * @param[in] handle : GATT characteristic handle. + * + * @return + * - ESP_OK: registration succeeds + * - other: failed + * + */ +esp_err_t esp_ble_gattc_register_for_notify (esp_gatt_if_t gattc_if, + esp_bd_addr_t server_bda, + uint16_t handle); + + +/** + * @brief This function is called to de-register for notification of a service. + * + * @param[in] gattc_if: Gatt client access interface. + * @param[in] server_bda : target GATT server. + * @param[in] handle : GATT characteristic handle. + * + * @return + * - ESP_OK: unregister succeeds + * - other: failed + * + */ +esp_err_t esp_ble_gattc_unregister_for_notify (esp_gatt_if_t gattc_if, + esp_bd_addr_t server_bda, + uint16_t handle); + + +/** +* @brief Refresh the server cache store in the gattc stack of the remote device. If +* the device is connected, this API will restart the discovery of service information of the remote device +* +* @param[in] remote_bda: remote device BD address. +* +* @return +* - ESP_OK: success +* - other: failed +* +*/ +esp_err_t esp_ble_gattc_cache_refresh(esp_bd_addr_t remote_bda); + +/** +* @brief Add or delete the associated address with the source address. +* Note: The role of this API is mainly when the client side has stored a server-side database, +* when it needs to connect another device, but the device's attribute database is the same +* as the server database stored on the client-side, calling this API can use the database +* that the device has stored used as the peer server database to reduce the attribute +* database search and discovery process and speed up the connection time. +* The associated address mains that device want to used the database has stored in the local cache. +* The source address mains that device want to share the database to the associated address device. +* +* @param[in] gattc_if: Gatt client access interface. +* @param[in] src_addr: the source address which provide the attribute table. +* @param[in] assoc_addr: the associated device address which went to share the attribute table with the source address. +* @param[in] is_assoc: true add the associated device address, false remove the associated device address. +* @return +* - ESP_OK: success +* - other: failed +* +*/ +esp_err_t esp_ble_gattc_cache_assoc(esp_gatt_if_t gattc_if, esp_bd_addr_t src_addr, + esp_bd_addr_t assoc_addr, bool is_assoc); +/** +* @brief Get the address list which has store the attribute table in the gattc cache. There will +* callback ESP_GATTC_GET_ADDR_LIST_EVT event when get address list complete. +* +* @param[in] gattc_if: Gatt client access interface. +* @return +* - ESP_OK: success +* - other: failed +* +*/ +esp_err_t esp_ble_gattc_cache_get_addr_list(esp_gatt_if_t gattc_if); + +/** +* @brief Clean the service cache of this device in the gattc stack, +* +* @param[in] remote_bda: remote device BD address. +* +* @return +* - ESP_OK: success +* - other: failed +* +*/ +esp_err_t esp_ble_gattc_cache_clean(esp_bd_addr_t remote_bda); + +#ifdef __cplusplus +} +#endif + +#endif /* __ESP_GATTC_API_H__ */ diff --git a/lib/bt/host/bluedroid/api/include/api/esp_gatts_api.h b/lib/bt/host/bluedroid/api/include/api/esp_gatts_api.h new file mode 100644 index 00000000..0eb7ddd9 --- /dev/null +++ b/lib/bt/host/bluedroid/api/include/api/esp_gatts_api.h @@ -0,0 +1,600 @@ +/* + * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __ESP_GATTS_API_H__ +#define __ESP_GATTS_API_H__ + +#include "esp_bt_defs.h" +#include "esp_gatt_defs.h" +#include "esp_err.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/// GATT Server callback function events +typedef enum { + ESP_GATTS_REG_EVT = 0, /*!< When register application id, the event comes */ + ESP_GATTS_READ_EVT = 1, /*!< When gatt client request read operation, the event comes */ + ESP_GATTS_WRITE_EVT = 2, /*!< When gatt client request write operation, the event comes */ + ESP_GATTS_EXEC_WRITE_EVT = 3, /*!< When gatt client request execute write, the event comes */ + ESP_GATTS_MTU_EVT = 4, /*!< When set mtu complete, the event comes */ + ESP_GATTS_CONF_EVT = 5, /*!< When receive confirm, the event comes */ + ESP_GATTS_UNREG_EVT = 6, /*!< When unregister application id, the event comes */ + ESP_GATTS_CREATE_EVT = 7, /*!< When create service complete, the event comes */ + ESP_GATTS_ADD_INCL_SRVC_EVT = 8, /*!< When add included service complete, the event comes */ + ESP_GATTS_ADD_CHAR_EVT = 9, /*!< When add characteristic complete, the event comes */ + ESP_GATTS_ADD_CHAR_DESCR_EVT = 10, /*!< When add descriptor complete, the event comes */ + ESP_GATTS_DELETE_EVT = 11, /*!< When delete service complete, the event comes */ + ESP_GATTS_START_EVT = 12, /*!< When start service complete, the event comes */ + ESP_GATTS_STOP_EVT = 13, /*!< When stop service complete, the event comes */ + ESP_GATTS_CONNECT_EVT = 14, /*!< When gatt client connect, the event comes */ + ESP_GATTS_DISCONNECT_EVT = 15, /*!< When gatt client disconnect, the event comes */ + ESP_GATTS_OPEN_EVT = 16, /*!< When connect to peer, the event comes */ + ESP_GATTS_CANCEL_OPEN_EVT = 17, /*!< When disconnect from peer, the event comes */ + ESP_GATTS_CLOSE_EVT = 18, /*!< When gatt server close, the event comes */ + ESP_GATTS_LISTEN_EVT = 19, /*!< When gatt listen to be connected the event comes */ + ESP_GATTS_CONGEST_EVT = 20, /*!< When congest happen, the event comes */ + /* following is extra event */ + ESP_GATTS_RESPONSE_EVT = 21, /*!< When gatt send response complete, the event comes */ + ESP_GATTS_CREAT_ATTR_TAB_EVT = 22, /*!< When gatt create table complete, the event comes */ + ESP_GATTS_SET_ATTR_VAL_EVT = 23, /*!< When gatt set attr value complete, the event comes */ + ESP_GATTS_SEND_SERVICE_CHANGE_EVT = 24, /*!< When gatt send service change indication complete, the event comes */ +} esp_gatts_cb_event_t; + +/** + * @brief Gatt server callback parameters union + */ +typedef union { + /** + * @brief ESP_GATTS_REG_EVT + */ + struct gatts_reg_evt_param { + esp_gatt_status_t status; /*!< Operation status */ + uint16_t app_id; /*!< Application id which input in register API */ + } reg; /*!< Gatt server callback param of ESP_GATTS_REG_EVT */ + + /** + * @brief ESP_GATTS_READ_EVT + */ + struct gatts_read_evt_param { + uint16_t conn_id; /*!< Connection id */ + uint32_t trans_id; /*!< Transfer id */ + esp_bd_addr_t bda; /*!< The bluetooth device address which been read */ + uint16_t handle; /*!< The attribute handle */ + uint16_t offset; /*!< Offset of the value, if the value is too long */ + bool is_long; /*!< The value is too long or not */ + bool need_rsp; /*!< The read operation need to do response */ + } read; /*!< Gatt server callback param of ESP_GATTS_READ_EVT */ + + + /** + * @brief ESP_GATTS_WRITE_EVT + */ + struct gatts_write_evt_param { + uint16_t conn_id; /*!< Connection id */ + uint32_t trans_id; /*!< Transfer id */ + esp_bd_addr_t bda; /*!< The bluetooth device address which been written */ + uint16_t handle; /*!< The attribute handle */ + uint16_t offset; /*!< Offset of the value, if the value is too long */ + bool need_rsp; /*!< The write operation need to do response */ + bool is_prep; /*!< This write operation is prepare write */ + uint16_t len; /*!< The write attribute value length */ + uint8_t *value; /*!< The write attribute value */ + } write; /*!< Gatt server callback param of ESP_GATTS_WRITE_EVT */ + + /** + * @brief ESP_GATTS_EXEC_WRITE_EVT + */ + struct gatts_exec_write_evt_param { + uint16_t conn_id; /*!< Connection id */ + uint32_t trans_id; /*!< Transfer id */ + esp_bd_addr_t bda; /*!< The bluetooth device address which been written */ +#define ESP_GATT_PREP_WRITE_CANCEL 0x00 /*!< Prepare write flag to indicate cancel prepare write */ +#define ESP_GATT_PREP_WRITE_EXEC 0x01 /*!< Prepare write flag to indicate execute prepare write */ + uint8_t exec_write_flag; /*!< Execute write flag */ + } exec_write; /*!< Gatt server callback param of ESP_GATTS_EXEC_WRITE_EVT */ + + /** + * @brief ESP_GATTS_MTU_EVT + */ + struct gatts_mtu_evt_param { + uint16_t conn_id; /*!< Connection id */ + uint16_t mtu; /*!< MTU size */ + } mtu; /*!< Gatt server callback param of ESP_GATTS_MTU_EVT */ + + /** + * @brief ESP_GATTS_CONF_EVT + */ + struct gatts_conf_evt_param { + esp_gatt_status_t status; /*!< Operation status */ + uint16_t conn_id; /*!< Connection id */ + uint16_t handle; /*!< attribute handle */ + uint16_t len; /*!< The indication or notification value length, len is valid when send notification or indication failed */ + uint8_t *value; /*!< The indication or notification value , value is valid when send notification or indication failed */ + } conf; /*!< Gatt server callback param of ESP_GATTS_CONF_EVT (confirm) */ + + /** + * @brief ESP_GATTS_UNREG_EVT + */ + + /** + * @brief ESP_GATTS_CREATE_EVT + */ + struct gatts_create_evt_param { + esp_gatt_status_t status; /*!< Operation status */ + uint16_t service_handle; /*!< Service attribute handle */ + esp_gatt_srvc_id_t service_id; /*!< Service id, include service uuid and other information */ + } create; /*!< Gatt server callback param of ESP_GATTS_CREATE_EVT */ + + /** + * @brief ESP_GATTS_ADD_INCL_SRVC_EVT + */ + struct gatts_add_incl_srvc_evt_param { + esp_gatt_status_t status; /*!< Operation status */ + uint16_t attr_handle; /*!< Included service attribute handle */ + uint16_t service_handle; /*!< Service attribute handle */ + } add_incl_srvc; /*!< Gatt server callback param of ESP_GATTS_ADD_INCL_SRVC_EVT */ + + /** + * @brief ESP_GATTS_ADD_CHAR_EVT + */ + struct gatts_add_char_evt_param { + esp_gatt_status_t status; /*!< Operation status */ + uint16_t attr_handle; /*!< Characteristic attribute handle */ + uint16_t service_handle; /*!< Service attribute handle */ + esp_bt_uuid_t char_uuid; /*!< Characteristic uuid */ + } add_char; /*!< Gatt server callback param of ESP_GATTS_ADD_CHAR_EVT */ + + /** + * @brief ESP_GATTS_ADD_CHAR_DESCR_EVT + */ + struct gatts_add_char_descr_evt_param { + esp_gatt_status_t status; /*!< Operation status */ + uint16_t attr_handle; /*!< Descriptor attribute handle */ + uint16_t service_handle; /*!< Service attribute handle */ + esp_bt_uuid_t descr_uuid; /*!< Characteristic descriptor uuid */ + } add_char_descr; /*!< Gatt server callback param of ESP_GATTS_ADD_CHAR_DESCR_EVT */ + + /** + * @brief ESP_GATTS_DELETE_EVT + */ + struct gatts_delete_evt_param { + esp_gatt_status_t status; /*!< Operation status */ + uint16_t service_handle; /*!< Service attribute handle */ + } del; /*!< Gatt server callback param of ESP_GATTS_DELETE_EVT */ + + /** + * @brief ESP_GATTS_START_EVT + */ + struct gatts_start_evt_param { + esp_gatt_status_t status; /*!< Operation status */ + uint16_t service_handle; /*!< Service attribute handle */ + } start; /*!< Gatt server callback param of ESP_GATTS_START_EVT */ + + /** + * @brief ESP_GATTS_STOP_EVT + */ + struct gatts_stop_evt_param { + esp_gatt_status_t status; /*!< Operation status */ + uint16_t service_handle; /*!< Service attribute handle */ + } stop; /*!< Gatt server callback param of ESP_GATTS_STOP_EVT */ + + /** + * @brief ESP_GATTS_CONNECT_EVT + */ + struct gatts_connect_evt_param { + uint16_t conn_id; /*!< Connection id */ + uint8_t link_role; /*!< Link role : master role = 0 ; slave role = 1*/ + esp_bd_addr_t remote_bda; /*!< Remote bluetooth device address */ + esp_gatt_conn_params_t conn_params; /*!< current Connection parameters */ + esp_ble_addr_type_t ble_addr_type; /*!< Remote BLE device address type */ + uint16_t conn_handle; /*!< HCI connection handle */ + } connect; /*!< Gatt server callback param of ESP_GATTS_CONNECT_EVT */ + + /** + * @brief ESP_GATTS_DISCONNECT_EVT + */ + struct gatts_disconnect_evt_param { + uint16_t conn_id; /*!< Connection id */ + esp_bd_addr_t remote_bda; /*!< Remote bluetooth device address */ + esp_gatt_conn_reason_t reason; /*!< Indicate the reason of disconnection */ + } disconnect; /*!< Gatt server callback param of ESP_GATTS_DISCONNECT_EVT */ + + /** + * @brief ESP_GATTS_OPEN_EVT + */ + struct gatts_open_evt_param { + esp_gatt_status_t status; /*!< Operation status */ + } open; /*!< Gatt server callback param of ESP_GATTS_OPEN_EVT */ + + /** + * @brief ESP_GATTS_CANCEL_OPEN_EVT + */ + struct gatts_cancel_open_evt_param { + esp_gatt_status_t status; /*!< Operation status */ + } cancel_open; /*!< Gatt server callback param of ESP_GATTS_CANCEL_OPEN_EVT */ + + /** + * @brief ESP_GATTS_CLOSE_EVT + */ + struct gatts_close_evt_param { + esp_gatt_status_t status; /*!< Operation status */ + uint16_t conn_id; /*!< Connection id */ + } close; /*!< Gatt server callback param of ESP_GATTS_CLOSE_EVT */ + + /** + * @brief ESP_GATTS_LISTEN_EVT + */ + /** + * @brief ESP_GATTS_CONGEST_EVT + */ + struct gatts_congest_evt_param { + uint16_t conn_id; /*!< Connection id */ + bool congested; /*!< Congested or not */ + } congest; /*!< Gatt server callback param of ESP_GATTS_CONGEST_EVT */ + + /** + * @brief ESP_GATTS_RESPONSE_EVT + */ + struct gatts_rsp_evt_param { + esp_gatt_status_t status; /*!< Operation status */ + uint16_t handle; /*!< Attribute handle which send response */ + } rsp; /*!< Gatt server callback param of ESP_GATTS_RESPONSE_EVT */ + + /** + * @brief ESP_GATTS_CREAT_ATTR_TAB_EVT + */ + struct gatts_add_attr_tab_evt_param{ + esp_gatt_status_t status; /*!< Operation status */ + esp_bt_uuid_t svc_uuid; /*!< Service uuid type */ + uint8_t svc_inst_id; /*!< Service id */ + uint16_t num_handle; /*!< The number of the attribute handle to be added to the gatts database */ + uint16_t *handles; /*!< The number to the handles */ + } add_attr_tab; /*!< Gatt server callback param of ESP_GATTS_CREAT_ATTR_TAB_EVT */ + + + /** + * @brief ESP_GATTS_SET_ATTR_VAL_EVT + */ + struct gatts_set_attr_val_evt_param{ + uint16_t srvc_handle; /*!< The service handle */ + uint16_t attr_handle; /*!< The attribute handle */ + esp_gatt_status_t status; /*!< Operation status*/ + } set_attr_val; /*!< Gatt server callback param of ESP_GATTS_SET_ATTR_VAL_EVT */ + + /** + * @brief ESP_GATTS_SEND_SERVICE_CHANGE_EVT + */ + struct gatts_send_service_change_evt_param{ + esp_gatt_status_t status; /*!< Operation status*/ + } service_change; /*!< Gatt server callback param of ESP_GATTS_SEND_SERVICE_CHANGE_EVT */ + +} esp_ble_gatts_cb_param_t; + +/** + * @brief GATT Server callback function type + * @param event : Event type + * @param gatts_if : GATT server access interface, normally + * different gatts_if correspond to different profile + * @param param : Point to callback parameter, currently is union type + */ +typedef void (* esp_gatts_cb_t)(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param); + +/** + * @brief This function is called to register application callbacks + * with BTA GATTS module. + * + * @return + * - ESP_OK : success + * - other : failed + * + */ +esp_err_t esp_ble_gatts_register_callback(esp_gatts_cb_t callback); + +/** + * @brief This function is called to get the current application callbacks + * with BTA GATTS module. + * + * @return + * - esp_gatts_cb_t : current callback + * + */ +esp_gatts_cb_t esp_ble_gatts_get_callback(void); + +/** + * @brief This function is called to register application identifier + * + * @return + * - ESP_OK : success + * - other : failed + * + */ +esp_err_t esp_ble_gatts_app_register(uint16_t app_id); + + + +/** + * @brief unregister with GATT Server. + * + * @param[in] gatts_if: GATT server access interface + * @return + * - ESP_OK : success + * - other : failed + * + */ +esp_err_t esp_ble_gatts_app_unregister(esp_gatt_if_t gatts_if); + + +/** + * @brief Create a service. When service creation is done, a callback + * event ESP_GATTS_CREATE_EVT is called to report status + * and service ID to the profile. The service ID obtained in + * the callback function needs to be used when adding included + * service and characteristics/descriptors into the service. + * + * @param[in] gatts_if: GATT server access interface + * @param[in] service_id: service ID. + * @param[in] num_handle: number of handle requested for this service. + * + * @return + * - ESP_OK : success + * - other : failed + * + */ +esp_err_t esp_ble_gatts_create_service(esp_gatt_if_t gatts_if, + esp_gatt_srvc_id_t *service_id, uint16_t num_handle); + + +/** + * @brief Create a service attribute tab. + * @param[in] gatts_attr_db: the pointer to the service attr tab + * @param[in] gatts_if: GATT server access interface + * @param[in] max_nb_attr: the number of attribute to be added to the service database. + * @param[in] srvc_inst_id: the instance id of the service + * + * @return + * - ESP_OK : success + * - other : failed + * + */ +esp_err_t esp_ble_gatts_create_attr_tab(const esp_gatts_attr_db_t *gatts_attr_db, + esp_gatt_if_t gatts_if, + uint16_t max_nb_attr, + uint8_t srvc_inst_id); +/** + * @brief This function is called to add an included service. This function have to be called between + * 'esp_ble_gatts_create_service' and 'esp_ble_gatts_add_char'. After included + * service is included, a callback event ESP_GATTS_ADD_INCL_SRVC_EVT + * is reported the included service ID. + * + * @param[in] service_handle: service handle to which this included service is to + * be added. + * @param[in] included_service_handle: the service ID to be included. + * + * @return + * - ESP_OK : success + * - other : failed + * + */ +esp_err_t esp_ble_gatts_add_included_service(uint16_t service_handle, uint16_t included_service_handle); + + + +/** + * @brief This function is called to add a characteristic into a service. + * + * @param[in] service_handle: service handle to which this included service is to + * be added. + * @param[in] char_uuid : Characteristic UUID. + * @param[in] perm : Characteristic value declaration attribute permission. + * @param[in] property : Characteristic Properties + * @param[in] char_val : Characteristic value + * @param[in] control : attribute response control byte + * + * @return + * - ESP_OK : success + * - other : failed + * + */ +esp_err_t esp_ble_gatts_add_char(uint16_t service_handle, esp_bt_uuid_t *char_uuid, + esp_gatt_perm_t perm, esp_gatt_char_prop_t property, esp_attr_value_t *char_val, + esp_attr_control_t *control); + + +/** + * @brief This function is called to add characteristic descriptor. When + * it's done, a callback event ESP_GATTS_ADD_DESCR_EVT is called + * to report the status and an ID number for this descriptor. + * + * @param[in] service_handle: service handle to which this characteristic descriptor is to + * be added. + * @param[in] perm: descriptor access permission. + * @param[in] descr_uuid: descriptor UUID. + * @param[in] char_descr_val : Characteristic descriptor value + * @param[in] control : attribute response control byte + * @return + * - ESP_OK : success + * - other : failed + * + */ +esp_err_t esp_ble_gatts_add_char_descr (uint16_t service_handle, + esp_bt_uuid_t *descr_uuid, + esp_gatt_perm_t perm, esp_attr_value_t *char_descr_val, + esp_attr_control_t *control); + + + +/** + * @brief This function is called to delete a service. When this is done, + * a callback event ESP_GATTS_DELETE_EVT is report with the status. + * + * @param[in] service_handle: service_handle to be deleted. + * + * @return + * - ESP_OK : success + * - other : failed + * + */ +esp_err_t esp_ble_gatts_delete_service(uint16_t service_handle); + + + +/** + * @brief This function is called to start a service. + * + * @param[in] service_handle: the service handle to be started. + * + * @return + * - ESP_OK : success + * - other : failed + * + */ +esp_err_t esp_ble_gatts_start_service(uint16_t service_handle); + + + +/** + * @brief This function is called to stop a service. + * + * @param[in] service_handle - service to be topped. + * + * @return + * - ESP_OK : success + * - other : failed + * + */ +esp_err_t esp_ble_gatts_stop_service(uint16_t service_handle); + + + +/** + * @brief Send indicate or notify to GATT client. + * Set param need_confirm as false will send notification, otherwise indication. + * Note: the size of indicate or notify data need less than MTU size,see "esp_ble_gattc_send_mtu_req". + * + * @param[in] gatts_if: GATT server access interface + * @param[in] conn_id - connection id to indicate. + * @param[in] attr_handle - attribute handle to indicate. + * @param[in] value_len - indicate value length. + * @param[in] value: value to indicate. + * @param[in] need_confirm - Whether a confirmation is required. + * false sends a GATT notification, true sends a GATT indication. + * + * @return + * - ESP_OK : success + * - other : failed + * + */ +esp_err_t esp_ble_gatts_send_indicate(esp_gatt_if_t gatts_if, uint16_t conn_id, uint16_t attr_handle, + uint16_t value_len, uint8_t *value, bool need_confirm); + + +/** + * @brief This function is called to send a response to a request. + * + * @param[in] gatts_if: GATT server access interface + * @param[in] conn_id - connection identifier. + * @param[in] trans_id - transfer id + * @param[in] status - response status + * @param[in] rsp - response data. + * + * @return + * - ESP_OK : success + * - other : failed + * + */ +esp_err_t esp_ble_gatts_send_response(esp_gatt_if_t gatts_if, uint16_t conn_id, uint32_t trans_id, + esp_gatt_status_t status, esp_gatt_rsp_t *rsp); + + +/** + * @brief This function is called to set the attribute value by the application + * + * @param[in] attr_handle: the attribute handle which to be set + * @param[in] length: the value length + * @param[in] value: the pointer to the attribute value + * + * @return + * - ESP_OK : success + * - other : failed + * + */ +esp_err_t esp_ble_gatts_set_attr_value(uint16_t attr_handle, uint16_t length, const uint8_t *value); + +/** + * @brief Retrieve attribute value + * + * @param[in] attr_handle: Attribute handle. + * @param[out] length: pointer to the attribute value length + * @param[out] value: Pointer to attribute value payload, the value cannot be modified by user + * + * @return + * - ESP_GATT_OK : success + * - other : failed + * + */ +esp_gatt_status_t esp_ble_gatts_get_attr_value(uint16_t attr_handle, uint16_t *length, const uint8_t **value); + + +/** + * @brief Open a direct open connection or add a background auto connection + * + * @param[in] gatts_if: GATT server access interface + * @param[in] remote_bda: remote device bluetooth device address. + * @param[in] is_direct: direct connection or background auto connection + * + * @return + * - ESP_OK : success + * - other : failed + * + */ +esp_err_t esp_ble_gatts_open(esp_gatt_if_t gatts_if, esp_bd_addr_t remote_bda, bool is_direct); + +/** + * @brief Close a connection a remote device. + * + * @param[in] gatts_if: GATT server access interface + * @param[in] conn_id: connection ID to be closed. + * + * @return + * - ESP_OK : success + * - other : failed + * + */ +esp_err_t esp_ble_gatts_close(esp_gatt_if_t gatts_if, uint16_t conn_id); + +/** + * @brief Send service change indication + * + * @param[in] gatts_if: GATT server access interface + * @param[in] remote_bda: remote device bluetooth device address. + * If remote_bda is NULL then it will send service change + * indication to all the connected devices and if not then + * to a specific device + * + * @return + * - ESP_OK : success + * - other : failed + * + */ +esp_err_t esp_ble_gatts_send_service_change_indication(esp_gatt_if_t gatts_if, esp_bd_addr_t remote_bda); + +/** + * @brief Print local database (GATT service table) + * + * @return + * - ESP_OK : success + * - other : failed + * + */ +esp_err_t esp_ble_gatts_show_local_database(void); + +#ifdef __cplusplus +} +#endif + +#endif /* __ESP_GATTS_API_H__ */ diff --git a/lib/bt/host/bluedroid/api/include/api/esp_hf_ag_api.h b/lib/bt/host/bluedroid/api/include/api/esp_hf_ag_api.h new file mode 100644 index 00000000..84086118 --- /dev/null +++ b/lib/bt/host/bluedroid/api/include/api/esp_hf_ag_api.h @@ -0,0 +1,716 @@ +/* + * SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __ESP_HF_AG_API_H__ +#define __ESP_HF_AG_API_H__ + +#include "esp_err.h" +#include "esp_bt_defs.h" +#include "esp_hf_defs.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* features masks of HF AG */ +#define ESP_HF_PEER_FEAT_3WAY 0x01 /* Three-way calling */ +#define ESP_HF_PEER_FEAT_ECNR 0x02 /* Echo cancellation and/or noise reduction */ +#define ESP_HF_PEER_FEAT_VREC 0x04 /* Voice recognition */ +#define ESP_HF_PEER_FEAT_INBAND 0x08 /* In-band ring tone */ +#define ESP_HF_PEER_FEAT_VTAG 0x10 /* Attach a phone number to a voice tag */ +#define ESP_HF_PEER_FEAT_REJECT 0x20 /* Ability to reject incoming call */ +#define ESP_HF_PEER_FEAT_ECS 0x40 /* Enhanced Call Status */ +#define ESP_HF_PEER_FEAT_ECC 0x80 /* Enhanced Call Control */ +#define ESP_HF_PEER_FEAT_EXTERR 0x100 /* Extended error codes */ +#define ESP_HF_PEER_FEAT_CODEC 0x200 /* Codec Negotiation */ +/* HFP 1.7+ */ +#define ESP_HF_PEER_FEAT_HF_IND 0x400 /* HF Indicators */ +#define ESP_HF_PEER_FEAT_ESCO_S4 0x800 /* eSCO S4 Setting Supported */ + + +/* CHLD feature masks of HF AG */ +#define ESP_HF_CHLD_FEAT_REL 0x01 /* 0 Release waiting call or held calls */ +#define ESP_HF_CHLD_FEAT_REL_ACC 0x02 /* 1 Release active calls and accept other waiting or held call */ +#define ESP_HF_CHLD_FEAT_REL_X 0x04 /* 1x Release specified active call only */ +#define ESP_HF_CHLD_FEAT_HOLD_ACC 0x08 /* 2 Active calls on hold and accept other waiting or held call */ +#define ESP_HF_CHLD_FEAT_PRIV_X 0x10 /* 2x Request private mode with specified call(put the rest on hold) */ +#define ESP_HF_CHLD_FEAT_MERGE 0x20 /* 3 Add held call to multiparty */ +#define ESP_HF_CHLD_FEAT_MERGE_DETACH 0x40 /* 4 Connect two calls and leave(disconnect from multiparty) */ + +/// HF callback events +typedef enum +{ + ESP_HF_CONNECTION_STATE_EVT = 0, /*!< Connection state changed event */ + ESP_HF_AUDIO_STATE_EVT, /*!< Audio connection state change event */ + ESP_HF_BVRA_RESPONSE_EVT, /*!< Voice recognition state change event */ + ESP_HF_VOLUME_CONTROL_EVT, /*!< Audio volume control command from HF Client, provided by +VGM or +VGS message */ + + ESP_HF_UNAT_RESPONSE_EVT, /*!< Unknown AT cmd Response*/ + ESP_HF_IND_UPDATE_EVT, /*!< Indicator Update Event*/ + ESP_HF_CIND_RESPONSE_EVT, /*!< Call And Device Indicator Response*/ + ESP_HF_COPS_RESPONSE_EVT, /*!< Current operator information */ + ESP_HF_CLCC_RESPONSE_EVT, /*!< List of current calls notification */ + ESP_HF_CNUM_RESPONSE_EVT, /*!< Subscriber information response from HF Client */ + ESP_HF_VTS_RESPONSE_EVT, /*!< Enable or not DTMF */ + ESP_HF_NREC_RESPONSE_EVT, /*!< Enable or not NREC */ + + ESP_HF_ATA_RESPONSE_EVT, /*!< Answer an Incoming Call */ + ESP_HF_CHUP_RESPONSE_EVT, /*!< Reject an Incoming Call */ + ESP_HF_DIAL_EVT, /*!< Origin an outgoing call with specific number or the dial the last number */ + ESP_HF_WBS_RESPONSE_EVT, /*!< Codec Status */ + ESP_HF_BCS_RESPONSE_EVT, /*!< Final Codec Choice */ + ESP_HF_PKT_STAT_NUMS_GET_EVT, /*!< Request number of packet different status */ +} esp_hf_cb_event_t; + +/// Dial type of ESP_HF_DIAL_EVT +typedef enum +{ + ESP_HF_DIAL_NUM = 0, /*!< Dial with a phone number */ + ESP_HF_DIAL_VOIP, /*!< Dial with VoIP */ + ESP_HF_DIAL_MEM, /*!< Dial with a memory position */ +} esp_hf_dial_type_t; + +/// HFP AG callback parameters +typedef union +{ + /** + * @brief ESP_HF_CONNECTION_STATE_EVT + */ + struct hf_conn_stat_param { + esp_bd_addr_t remote_bda; /*!< Remote bluetooth device address */ + esp_hf_connection_state_t state; /*!< Connection state */ + uint32_t peer_feat; /*!< HF supported features */ + uint32_t chld_feat; /*!< AG supported features on call hold and multiparty services */ + } conn_stat; /*!< AG callback param of ESP_HF_CONNECTION_STATE_EVT */ + + /** + * @brief ESP_HF_AUDIO_STATE_EVT + */ + struct hf_audio_stat_param { + esp_bd_addr_t remote_addr; /*!< Remote bluetooth device address */ + esp_hf_audio_state_t state; /*!< Audio connection state */ + uint16_t sync_conn_handle; /*!< (e)SCO connection handle */ + } audio_stat; /*!< AG callback param of ESP_HF_AUDIO_STATE_EVT */ + + /** + * @brief ESP_HF_BVRA_RESPONSE_EVT + */ + struct hf_vra_rep_param { + esp_bd_addr_t remote_addr; /*!< Remote bluetooth device address */ + esp_hf_vr_state_t value; /*!< Voice recognition state */ + } vra_rep; /*!< AG callback param of ESP_HF_BVRA_RESPONSE_EVT */ + + /** + * @brief ESP_HF_VOLUME_CONTROL_EVT + */ + struct hf_volume_control_param { + esp_bd_addr_t remote_addr; /*!< Remote bluetooth device address */ + esp_hf_volume_type_t type; /*!< Volume control target, speaker or microphone */ + int volume; /*!< Gain, ranges from 0 to 15 */ + } volume_control; /*!< AG callback param of ESP_HF_VOLUME_CONTROL_EVT */ + + /** + * @brief ESP_HF_UNAT_RESPONSE_EVT + */ + struct hf_unat_rep_param { + esp_bd_addr_t remote_addr; /*!< Remote bluetooth device address */ + char *unat; /*!< Unknown AT command string */ + } unat_rep; /*!< AG callback param of ESP_HF_UNAT_RESPONSE_EVT */ + + /** + * @brief ESP_HF_DIAL_EVT + */ + struct hf_out_call_param { + esp_bd_addr_t remote_addr; /*!< remote bluetooth device address */ + esp_hf_dial_type_t type; /*!< dial type */ + char *num_or_loc; /*!< location in phone memory */ + } out_call; /*!< AG callback param of ESP_HF_DIAL_EVT */ + + /** + * @brief ESP_HF_IND_UPDATE_EVT + */ + struct hf_ind_upd_param { + esp_bd_addr_t remote_addr; /*!< remote bluetooth device address */ + } ind_upd; /*!< AG callback param of ESP_HF_IND_UPDATE_EVT */ + + /** + * @brief ESP_HF_CIND_RESPONSE_EVT + */ + struct hf_cind_rep_param { + esp_bd_addr_t remote_addr; /*!< remote bluetooth device address */ + } cind_rep; /*!< AG callback param of ESP_HF_CIND_RESPONSE_EVT */ + + /** + * @brief ESP_HF_COPS_RESPONSE_EVT + */ + struct hf_cops_rep_param { + esp_bd_addr_t remote_addr; /*!< remote bluetooth device address */ + } cops_rep; /*!< AG callback param of ESP_HF_COPS_RESPONSE_EVT */ + + /** + * @brief ESP_HF_CLCC_RESPONSE_EVT + */ + struct hf_clcc_rep_param { + esp_bd_addr_t remote_addr; /*!< remote bluetooth device address */ + } clcc_rep; /*!< AG callback param of ESP_HF_CLCC_RESPONSE_EVT */ + + /** + * @brief ESP_HF_CNUM_RESPONSE_EVT + */ + struct hf_cnum_rep_param { + esp_bd_addr_t remote_addr; /*!< remote bluetooth device address */ + } cnum_rep; /*!< AG callback param of ESP_HF_CNUM_RESPONSE_EVT */ + + /** + * @brief ESP_HF_VTS_RESPONSE_EVT + */ + struct hf_vts_rep_param { + esp_bd_addr_t remote_addr; /*!< Remote bluetooth device address */ + char *code; /*!< MTF code from HF Client */ + } vts_rep; /*!< AG callback param of ESP_HF_VTS_RESPONSE_EVT */ + + /** + * @brief ESP_HF_NREC_RESPONSE_EVT + */ + struct hf_nrec_param { + esp_bd_addr_t remote_addr; /*!< Remote bluetooth device address */ + esp_hf_nrec_t state; /*!< NREC enabled or disabled */ + } nrec; /*!< AG callback param of ESP_HF_NREC_RESPONSE_EVT */ + + /** + * @brief ESP_HF_ATA_RESPONSE_EVT + */ + struct hf_ata_rep_param { + esp_bd_addr_t remote_addr; /*!< remote bluetooth device address */ + } ata_rep; /*!< AG callback param of ESP_HF_ATA_RESPONSE_EVT */ + + /** + * @brief ESP_HF_CHUP_RESPONSE_EVT + */ + struct hf_chup_rep_param { + esp_bd_addr_t remote_addr; /*!< remote bluetooth device address */ + } chup_rep; /*!< AG callback param of ESP_HF_CHUP_RESPONSE_EVT */ + + /** + * @brief ESP_HF_WBS_RESPONSE_EVT + */ + struct hf_wbs_rep_param { + esp_bd_addr_t remote_addr; /*!< Remote bluetooth device address */ + esp_hf_wbs_config_t codec; /*!< codec mode CVSD or mSBC */ + } wbs_rep; /*!< AG callback param of ESP_HF_WBS_RESPONSE_EVT */ + + /** + * @brief ESP_HF_BCS_RESPONSE_EVT + */ + struct hf_bcs_rep_param { + esp_bd_addr_t remote_addr; /*!< Remote bluetooth device address */ + esp_hf_wbs_config_t mode; /*!< codec mode CVSD or mSBC */ + } bcs_rep; /*!< AG callback param of ESP_HF_BCS_RESPONSE_EVT */ + + /** + * @brief ESP_HF_PKT_STAT_NUMS_GET_EVT + */ + struct ag_pkt_status_nums { + uint32_t rx_total; /*!< the total number of packets received */ + uint32_t rx_correct; /*!< the total number of packets data correctly received */ + uint32_t rx_err; /*!< the total number of packets data with possible invalid */ + uint32_t rx_none; /*!< the total number of packets data no received */ + uint32_t rx_lost; /*!< the total number of packets data partially lost */ + uint32_t tx_total; /*!< the total number of packets send */ + uint32_t tx_discarded; /*!< the total number of packets send lost */ + } pkt_nums; /*!< AG callback param of ESP_HF_PKT_STAT_NUMS_GET_EVT */ + +} esp_hf_cb_param_t; /*!< HFP AG callback param compound*/ + +/** + * @brief AG incoming data callback function, the callback is useful in case of + * Voice Over HCI. + * + * @param[in] buf : pointer to incoming data(payload of HCI synchronous data packet), the + * buffer is allocated inside bluetooth protocol stack and will be released after + * invoke of the callback is finished. + * + * @param[in] len : size(in bytes) in buf + */ +typedef void (* esp_hf_incoming_data_cb_t)(const uint8_t *buf, uint32_t len); + +/** + * @brief AG outgoing data callback function, the callback is useful in case of + * Voice Over HCI. Once audio connection is set up and the application layer has + * prepared data to send, the lower layer will call this function to read data + * and then send. This callback is supposed to be implemented as non-blocking, + * and if data is not enough, return value 0 is supposed. + * + * @param[in] buf : pointer to incoming data(payload of HCI synchronous data packet), the + * buffer is allocated inside bluetooth protocol stack and will be released after + * invoke of the callback is finished. + * + * @param[in] len : size(in bytes) in buf + * + * @return length of data successfully read + */ +typedef uint32_t (* esp_hf_outgoing_data_cb_t) (uint8_t *buf, uint32_t len); + +/** + * @brief HF AG callback function type + * + * @param event : Event type + * + * @param param : Pointer to callback parameter + */ +typedef void (* esp_hf_cb_t) (esp_hf_cb_event_t event, esp_hf_cb_param_t *param); + +/************************************************************************************ +** ESP HF API +************************************************************************************/ +/** + * @brief Register application callback function to HFP AG module. + * This function should be called only after esp_bluedroid_enable() completes successfully. + * + * @param[in] callback: HFP AG event callback function + * + * @return + * - ESP_OK: success + * - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled + * - ESP_FAIL: if callback is a NULL function pointer + * + */ +esp_err_t esp_hf_ag_register_callback(esp_hf_cb_t callback); + +/** + * + * @brief Initialize the bluetooth HF AG module. + * This function should be called after esp_bluedroid_enable() completes successfully. + * + * @return + * - ESP_OK: if the initialization request is sent successfully + * - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled + * - ESP_FAIL: others + * + */ +esp_err_t esp_hf_ag_init(void); + +/** + * + * @brief De-initialize for HF AG module. + * This function should be called only after esp_bluedroid_enable() completes successfully. + * + * @return + * - ESP_OK: success + * - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled + * - ESP_FAIL: others + * + */ +esp_err_t esp_hf_ag_deinit(void); + +/** + * + * @brief To establish a Service Level Connection to remote bluetooth HFP client device. + * This function must be called after esp_hf_ag_init() and before esp_hf_ag_deinit(). + * + * @param[in] remote_bda: remote bluetooth HFP client device address + * + * @return + * - ESP_OK: connect request is sent to lower layer + * - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled + * - ESP_FAIL: others + * + */ +esp_err_t esp_hf_ag_slc_connect(esp_bd_addr_t remote_bda); + +/** + * + * @brief Disconnect from the remote HFP client. This function must be called + * after esp_hf_ag_init() and before esp_hf_ag_deinit(). + * + * @param[in] remote_bda: remote bluetooth device address + * + * @return + * - ESP_OK: disconnect request is sent to lower layer + * - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled + * - ESP_FAIL: others + * + */ +esp_err_t esp_hf_ag_slc_disconnect(esp_bd_addr_t remote_bda); + +/** + * + * @brief Create audio connection with remote HFP client. + * As a precondition to use this API, Service Level Connection shall exist with HFP client. + * + * @param[in] remote_bda: remote bluetooth device address + * + * @return + * - ESP_OK: audio connect request is sent to lower layer + * - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled + * - ESP_FAIL: others + * + */ +esp_err_t esp_hf_ag_audio_connect(esp_bd_addr_t remote_bda); + +/** + * + * @brief Release the established audio connection with remote HFP client. + * As a precondition to use this API, Service Level Connection shall exist with HFP client. + * + * @param[in] remote_bda: remote bluetooth device address + * + * @return + * - ESP_OK: audio disconnect request is sent to lower layer + * - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled + * - ESP_FAIL: others + * + */ +esp_err_t esp_hf_ag_audio_disconnect(esp_bd_addr_t remote_bda); + +/** + * + * @brief Response of Volume Recognition Command(AT+VRA) from HFP client. + * As a precondition to use this API, Service Level Connection shall exist with HFP client. + * + * @param[in] remote_bda: the device address of voice recognition initiator + * + * @param[in] value: 0 - voice recognition disabled, 1- voice recognition enabled + * + * @return + * - ESP_OK: response of volume recognition is sent to lower layer + * - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled + * - ESP_FAIL: others + * + */ +esp_err_t esp_hf_ag_vra_control(esp_bd_addr_t remote_bda, esp_hf_vr_state_t value); + +/** + * + * @brief Volume synchronization with HFP client. + * As a precondition to use this API, Service Level Connection shall exist with HFP client. + * + * @param[in] remote_bda: remote bluetooth device address + * + * @param[in] type: volume control target, speaker or microphone + * + * @param[in] volume: gain of the speaker of microphone, ranges 0 to 15 + * + * @return + * - ESP_OK: volume synchronization control is sent to lower layer + * - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled + * - ESP_ERR_INVALID_ARG: if arguments are invalid + * - ESP_FAIL: others + * + */ +esp_err_t esp_hf_ag_volume_control(esp_bd_addr_t remote_bda, esp_hf_volume_control_target_t type, int volume); + +/** + * + * @brief Handle Unknown AT command from HFP Client. + * As a precondition to use this API, Service Level Connection shall exist with HFP client. + * + * @param[in] remote_addr: remote bluetooth device address + * + * @param[in] unat: User AT command response to HF Client. + * It will response "ERROR" by default if unat is NULL. + * @return + * - ESP_OK: response of unknown AT command is sent to lower layer + * - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled + * - ESP_FAIL: others + * + */ +esp_err_t esp_hf_ag_unknown_at_send(esp_bd_addr_t remote_addr, char *unat); + +/** + * + * @brief Unsolicited send extend AT error code to HFP Client. + * As a precondition to use this API, Service Level Connection shall exist with HFP client. + * + * @param[in] remote_bda: remote bluetooth device address + * @param[in] response_code: AT command response code + * @param[in] error_code: CME error code + * @return + * - ESP_OK: extend error code is sent to lower layer + * - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled + * - ESP_FAIL: others + * + */ +esp_err_t esp_hf_ag_cmee_send(esp_bd_addr_t remote_bda, esp_hf_at_response_code_t response_code, esp_hf_cme_err_t error_code); + +/** + * + * @brief Unsolicited send device status notification to HFP Client. + * As a precondition to use this API, Service Level Connection shall exist with HFP client. + * + * @param[in] remote_addr: remote bluetooth device address + * @param[in] call_state: call state + * @param[in] call_setup_state: call setup state + * @param[in] ntk_state: network service state + * @param[in] signal: signal strength from 0 to 5 + * @return + * - ESP_OK: device status notification is sent to lower layer + * - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled + * - ESP_ERR_INVALID_ARG: if arguments are invalid + * - ESP_FAIL: others + * + */ +esp_err_t esp_hf_ag_devices_status_indchange(esp_bd_addr_t remote_addr, esp_hf_call_status_t call_state, + esp_hf_call_setup_status_t call_setup_state, + esp_hf_network_state_t ntk_state, int signal) __attribute__(( + deprecated("Please use esp_hf_ag_ciev_report") + )); + +/** + * + * @brief Send indicator report "+CIEV: " to HFP Client. "CIEV" means “indicator events reporting", + * and all indicator types can be sent one type at a time. + * As a precondition to use this API, Service Level Connection shall exist with HFP client. + * + * @param[in] remote_addr: remote bluetooth device address + * @param[in] ind_type: indicator type + * @param[in] value: indicator value + * @return + * - ESP_OK: indicator report is sent to lower layer + * - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled + * - ESP_FAIL: others + * + */ +esp_err_t esp_hf_ag_ciev_report(esp_bd_addr_t remote_addr, esp_hf_ciev_report_type_t ind_type, int value); + +/** + * + * @brief Response to device individual indicators to HFP Client. + * As a precondition to use this API, Service Level Connection shall exist with HFP client. + * + * @param[in] remote_addr: remote bluetooth device address + * @param[in] call_state: call state + * @param[in] call_setup_state: call setup state + * @param[in] ntk_state: network service state + * @param[in] signal: signal strength from 0 to 5 + * @param[in] roam: roam state + * @param[in] batt_lev: battery level from 0 to 5 + * @param[in] call_held_status: call held status + * @return + * - ESP_OK: response to device individual indicators is sent to lower layer + * - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled + * - ESP_ERR_INVALID_ARG: if the arguments are invalid + * - ESP_FAIL: others + * + */ +esp_err_t esp_hf_ag_cind_response(esp_bd_addr_t remote_addr, + esp_hf_call_status_t call_state, + esp_hf_call_setup_status_t call_setup_state, + esp_hf_network_state_t ntk_state, int signal, esp_hf_roaming_status_t roam, int batt_lev, + esp_hf_call_held_status_t call_held_status); + +/** + * + * @brief Reponse for AT+COPS command from HF Client. + * As a precondition to use this API, Service Level Connection shall exist with HFP client. + * + * @param[in] remote_addr: remote bluetooth device address + * @param[in] name: current operator name + * @return + * - ESP_OK: reponse for AT+COPS command is sent to lower layer + * - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled + * - ESP_FAIL: others + * + */ +esp_err_t esp_hf_ag_cops_response(esp_bd_addr_t remote_addr, char *name); + +/** + * + * @brief Response to AT+CLCC command from HFP Client. + * As a precondition to use this API, Service Level Connection shall exist with HFP client. + * + * @param[in] remote_addr: remote bluetooth device address + * @param[in] index: the index of current call, starting with 1, finishing response with 0 (send OK) + * @param[in] dir: call direction (incoming/outgoing) + * @param[in] current_call_state: current call state + * @param[in] mode: current call mode (voice/data/fax) + * @param[in] mpty: single or multi type + * @param[in] number: current call number + * @param[in] type: international type or unknow + * @return + * - ESP_OK: response to AT+CLCC command is sent to lower layer + * - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled + * - ESP_FAIL: others + * + */ +esp_err_t esp_hf_ag_clcc_response(esp_bd_addr_t remote_addr, int index, esp_hf_current_call_direction_t dir, + esp_hf_current_call_status_t current_call_state, esp_hf_current_call_mode_t mode, + esp_hf_current_call_mpty_type_t mpty, char *number, esp_hf_call_addr_type_t type); + +/** + * + * @brief Response for AT+CNUM command from HF Client. + * As a precondition to use this API, Service Level Connection shall exist with HFP client. + * + * @param[in] remote_addr: remote bluetooth device address + * @param[in] number: registration number + * @param[in] number_type: value of number type from + * 128-143: national or international, may contain prefix and/or escape digits + * 144-159: international, includes country code prefix, add "+" if needed + * 160-175: national, but no prefix nor escape digits + * @param[in] service_type: service type (unknown/voice/fax) + * @return + * - ESP_OK: response for AT+CNUM command is sent to lower layer + * - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled + * - ESP_FAIL: others + * + */ +esp_err_t esp_hf_ag_cnum_response(esp_bd_addr_t remote_addr, char *number, int number_type, esp_hf_subscriber_service_type_t service_type); + +/** + * + * @brief Inform HF Client that AG Provided in-band ring tone or not. + * As a precondition to use this API, Service Level Connection shall exist with HFP client. + * + * @param[in] remote_addr: remote bluetooth device address + * @param[in] state: in-band ring tone state + * @return + * - ESP_OK: information of in-band ring tone is sent to lower layer + * - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled + * - ESP_ERR_INVALID_ARG: if arguments are invalid + * - ESP_FAIL: others + * + */ +esp_err_t esp_hf_ag_bsir(esp_bd_addr_t remote_addr, esp_hf_in_band_ring_state_t state); + +/** + * + * @brief Answer Incoming Call from AG. + * As a precondition to use this API, Service Level Connection shall exist with HFP client. + * + * @param[in] remote_addr: remote bluetooth device address + * @param[in] num_active: the number of active call + * @param[in] num_held: the number of held call + * @param[in] call_state: call state + * @param[in] call_setup_state: call setup state + * @param[in] number: number of the incoming call + * @param[in] call_addr_type: call address type + * @return + * - ESP_OK: answer incoming call is sent to lower layer + * - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled + * - ESP_FAIL: others + * + */ +esp_err_t esp_hf_ag_answer_call(esp_bd_addr_t remote_addr, int num_active, int num_held, + esp_hf_call_status_t call_state, esp_hf_call_setup_status_t call_setup_state, + char *number, esp_hf_call_addr_type_t call_addr_type); + +/** + * + * @brief Reject Incoming Call from AG. + * As a precondition to use this API, Service Level Connection shall exist with HFP client. + * + * @param[in] remote_addr: remote bluetooth device address + * @param[in] num_active: the number of active call + * @param[in] num_held: the number of held call + * @param[in] call_state: call state + * @param[in] call_setup_state: call setup state + * @param[in] number: number of the incoming call + * @param[in] call_addr_type: call address type + * @return + * - ESP_OK: reject incoming call is sent to lower layer + * - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled + * - ESP_FAIL: others + * + */ +esp_err_t esp_hf_ag_reject_call(esp_bd_addr_t remote_addr, int num_active, int num_held, + esp_hf_call_status_t call_state, esp_hf_call_setup_status_t call_setup_state, + char *number, esp_hf_call_addr_type_t call_addr_type); + +/** + * + * @brief Initiate a call from AG. + * As a precondition to use this API, Service Level Connection shall exist with HFP client. + * If the AG is driven by the HF to call esp_hf_ag_out_call, it needs to response an OK or ERROR + * to HF. But if the AG is actively calling esp_hf_ag_out_call, it does not need to take a response + * to HF. + * + * @param[in] remote_addr: remote bluetooth device address + * @param[in] num_active: the number of active call + * @param[in] num_held: the number of held call + * @param[in] call_state: call state + * @param[in] call_setup_state: call setup state + * @param[in] number: number of the outgoing call + * @param[in] call_addr_type: call address type + * @return + * - ESP_OK: a call initiation is sent to lower layer + * - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled + * - ESP_FAIL: others + * + */ +esp_err_t esp_hf_ag_out_call(esp_bd_addr_t remote_addr, int num_active, int num_held, + esp_hf_call_status_t call_state, esp_hf_call_setup_status_t call_setup_state, + char *number, esp_hf_call_addr_type_t call_addr_type); + +/** + * + * @brief End an ongoing call. + * As a precondition to use this API, Service Level Connection shall exist with HFP client. + * + * @param[in] remote_addr: remote bluetooth device address + * @param[in] num_active: the number of active call + * @param[in] num_held: the number of held call + * @param[in] call_state: call state + * @param[in] call_setup_state: call setup state + * @param[in] number: number of the call + * @param[in] call_addr_type: call address type + * @return + * - ESP_OK: end an ongoing call is sent to lower layer + * - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled + * - ESP_FAIL: others + * + */ +esp_err_t esp_hf_ag_end_call(esp_bd_addr_t remote_addr, int num_active, int num_held, + esp_hf_call_status_t call_state, esp_hf_call_setup_status_t call_setup_state, + char *number, esp_hf_call_addr_type_t call_addr_type); + +/** + * @brief Register AG data output function. + * The callback is only used in the case that Voice Over HCI is enabled. + * + * @param[in] recv: HFP client incoming data callback function + * @param[in] send: HFP client outgoing data callback function + * + * @return + * - ESP_OK: success + * - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled + * - ESP_FAIL: if callback is a NULL function pointer + * + */ +esp_err_t esp_hf_ag_register_data_callback(esp_hf_incoming_data_cb_t recv, esp_hf_outgoing_data_cb_t send); + +/** + * + * @brief Get the number of packets received and sent + * + * This function is only used in the case that Voice Over HCI is enabled and the audio state is connected. + * When the operation is completed, the callback function will be called with ESP_HF_PKT_STAT_NUMS_GET_EVT. + * + * @param[in] sync_conn_handle: the (e)SCO connection handle + * + * @return + * - ESP_OK: if the request is sent successfully + * - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled + * - ESP_FAIL: others + * + */ +esp_err_t esp_hf_ag_pkt_stat_nums_get(uint16_t sync_conn_handle); + +/** + * @brief Trigger the lower-layer to fetch and send audio data. + * + * This function is only used in the case that Voice Over HCI is enabled. + * As a precondition to use this API, Service Level Connection shall exist with HFP client. + * After this function is called, lower layer will invoke esp_hf_client_outgoing_data_cb_t to fetch data + * + */ +void esp_hf_ag_outgoing_data_ready(void); + +#ifdef __cplusplus +} +#endif + +#endif //__ESP_HF_AG_API_H__ diff --git a/lib/bt/host/bluedroid/api/include/api/esp_hf_client_api.h b/lib/bt/host/bluedroid/api/include/api/esp_hf_client_api.h new file mode 100644 index 00000000..9353fc03 --- /dev/null +++ b/lib/bt/host/bluedroid/api/include/api/esp_hf_client_api.h @@ -0,0 +1,736 @@ +/* + * SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __ESP_HF_CLIENT_API_H__ +#define __ESP_HF_CLIENT_API_H__ + +#include "esp_err.h" +#include "esp_bt_defs.h" +#include "esp_hf_defs.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define ESP_BT_HF_CLIENT_NUMBER_LEN (32) +#define ESP_BT_HF_CLIENT_OPERATOR_NAME_LEN (16) +#define ESP_BT_HF_AT_SEND_XAPL_LEN (14) + +/// Bluetooth HFP RFCOMM connection and service level connection status +typedef enum { + ESP_HF_CLIENT_CONNECTION_STATE_DISCONNECTED = 0, /*!< RFCOMM data link channel released */ + ESP_HF_CLIENT_CONNECTION_STATE_CONNECTING, /*!< connecting remote device on the RFCOMM data link*/ + ESP_HF_CLIENT_CONNECTION_STATE_CONNECTED, /*!< RFCOMM connection established */ + ESP_HF_CLIENT_CONNECTION_STATE_SLC_CONNECTED, /*!< service level connection established */ + ESP_HF_CLIENT_CONNECTION_STATE_DISCONNECTING, /*!< disconnecting with remote device on the RFCOMM dat link*/ +} esp_hf_client_connection_state_t; + +/// Bluetooth HFP audio connection status +typedef enum { + ESP_HF_CLIENT_AUDIO_STATE_DISCONNECTED = 0, /*!< audio connection released */ + ESP_HF_CLIENT_AUDIO_STATE_CONNECTING, /*!< audio connection has been initiated */ + ESP_HF_CLIENT_AUDIO_STATE_CONNECTED, /*!< audio connection is established */ + ESP_HF_CLIENT_AUDIO_STATE_CONNECTED_MSBC, /*!< mSBC audio connection is established */ +} esp_hf_client_audio_state_t; + +/// in-band ring tone state +typedef enum { + ESP_HF_CLIENT_IN_BAND_RINGTONE_NOT_PROVIDED = 0, + ESP_HF_CLIENT_IN_BAND_RINGTONE_PROVIDED, +} esp_hf_client_in_band_ring_state_t; + +/* features masks of AG */ +#define ESP_HF_CLIENT_PEER_FEAT_3WAY 0x01 /* Three-way calling */ +#define ESP_HF_CLIENT_PEER_FEAT_ECNR 0x02 /* Echo cancellation and/or noise reduction */ +#define ESP_HF_CLIENT_PEER_FEAT_VREC 0x04 /* Voice recognition */ +#define ESP_HF_CLIENT_PEER_FEAT_INBAND 0x08 /* In-band ring tone */ +#define ESP_HF_CLIENT_PEER_FEAT_VTAG 0x10 /* Attach a phone number to a voice tag */ +#define ESP_HF_CLIENT_PEER_FEAT_REJECT 0x20 /* Ability to reject incoming call */ +#define ESP_HF_CLIENT_PEER_FEAT_ECS 0x40 /* Enhanced Call Status */ +#define ESP_HF_CLIENT_PEER_FEAT_ECC 0x80 /* Enhanced Call Control */ +#define ESP_HF_CLIENT_PEER_FEAT_EXTERR 0x100 /* Extended error codes */ +#define ESP_HF_CLIENT_PEER_FEAT_CODEC 0x200 /* Codec Negotiation */ +/* HFP 1.7+ */ +#define ESP_HF_CLIENT_PEER_FEAT_HF_IND 0x400 /* HF Indicators */ +#define ESP_HF_CLIENT_PEER_FEAT_ESCO_S4 0x800 /* eSCO S4 Setting Supported */ + +/* CHLD feature masks of AG */ +#define ESP_HF_CLIENT_CHLD_FEAT_REL 0x01 /* 0 Release waiting call or held calls */ +#define ESP_HF_CLIENT_CHLD_FEAT_REL_ACC 0x02 /* 1 Release active calls and accept other waiting or held call */ +#define ESP_HF_CLIENT_CHLD_FEAT_REL_X 0x04 /* 1x Release specified active call only */ +#define ESP_HF_CLIENT_CHLD_FEAT_HOLD_ACC 0x08 /* 2 Active calls on hold and accept other waiting or held call */ +#define ESP_HF_CLIENT_CHLD_FEAT_PRIV_X 0x10 /* 2x Request private mode with specified call(put the rest on hold) */ +#define ESP_HF_CLIENT_CHLD_FEAT_MERGE 0x20 /* 3 Add held call to multiparty */ +#define ESP_HF_CLIENT_CHLD_FEAT_MERGE_DETACH 0x40 /* 4 Connect two calls and leave(disconnect from multiparty) */ + +/* XAPL feature masks*/ +#define ESP_HF_CLIENT_XAPL_FEAT_RESERVED 0x01 /* reserved */ +#define ESP_HF_CLIENT_XAPL_FEAT_BATTERY_REPORT 0x02 /* The accessory supports battery reporting (reserved only for battery operated accessories) */ +#define ESP_HF_CLIENT_XAPL_FEAT_DOCKED 0x04 /* The accessory is docked or powered (reserved only for battery operated accessories). */ +#define ESP_HF_CLIENT_XAPL_FEAT_SIRI_STATUS_REPORT 0x08 /* The accessory supports Siri status reporting */ +#define ESP_HF_CLIENT_XAPL_NR_STATUS_REPORT 0x10 /* the accessory supports noise reduction (NR) status reporting */ + +/// HF CLIENT callback events +typedef enum { + ESP_HF_CLIENT_CONNECTION_STATE_EVT = 0, /*!< connection state changed event */ + ESP_HF_CLIENT_AUDIO_STATE_EVT, /*!< audio connection state change event */ + ESP_HF_CLIENT_BVRA_EVT, /*!< voice recognition state change event */ + ESP_HF_CLIENT_CIND_CALL_EVT, /*!< call indication */ + ESP_HF_CLIENT_CIND_CALL_SETUP_EVT, /*!< call setup indication */ + ESP_HF_CLIENT_CIND_CALL_HELD_EVT, /*!< call held indication */ + ESP_HF_CLIENT_CIND_SERVICE_AVAILABILITY_EVT, /*!< network service availability indication */ + ESP_HF_CLIENT_CIND_SIGNAL_STRENGTH_EVT, /*!< signal strength indication */ + ESP_HF_CLIENT_CIND_ROAMING_STATUS_EVT, /*!< roaming status indication */ + ESP_HF_CLIENT_CIND_BATTERY_LEVEL_EVT, /*!< battery level indication */ + ESP_HF_CLIENT_COPS_CURRENT_OPERATOR_EVT, /*!< current operator information */ + ESP_HF_CLIENT_BTRH_EVT, /*!< call response and hold event */ + ESP_HF_CLIENT_CLIP_EVT, /*!< Calling Line Identification notification */ + ESP_HF_CLIENT_CCWA_EVT, /*!< call waiting notification */ + ESP_HF_CLIENT_CLCC_EVT, /*!< list of current calls notification */ + ESP_HF_CLIENT_VOLUME_CONTROL_EVT, /*!< audio volume control command from AG, provided by +VGM or +VGS message */ + ESP_HF_CLIENT_AT_RESPONSE_EVT, /*!< AT command response event */ + ESP_HF_CLIENT_CNUM_EVT, /*!< subscriber information response from AG */ + ESP_HF_CLIENT_BSIR_EVT, /*!< setting of in-band ring tone */ + ESP_HF_CLIENT_BINP_EVT, /*!< requested number of last voice tag from AG */ + ESP_HF_CLIENT_RING_IND_EVT, /*!< ring indication event */ + ESP_HF_CLIENT_PKT_STAT_NUMS_GET_EVT, /*!< requested number of packet different status */ +} esp_hf_client_cb_event_t; + +/// HFP client callback parameters +typedef union { + /** + * @brief ESP_HF_CLIENT_CONNECTION_STATE_EVT + */ + struct hf_client_conn_stat_param { + esp_hf_client_connection_state_t state; /*!< HF connection state */ + uint32_t peer_feat; /*!< AG supported features */ + uint32_t chld_feat; /*!< AG supported features on call hold and multiparty services */ + esp_bd_addr_t remote_bda; /*!< remote bluetooth device address */ + } conn_stat; /*!< HF callback param of ESP_HF_CLIENT_CONNECTION_STATE_EVT */ + + /** + * @brief ESP_HF_CLIENT_AUDIO_STATE_EVT + */ + struct hf_client_audio_stat_param { + esp_hf_client_audio_state_t state; /*!< audio connection state */ + esp_bd_addr_t remote_bda; /*!< remote bluetooth device address */ + uint16_t sync_conn_handle; /*!< (e)SCO connection handle */ + } audio_stat; /*!< HF callback param of ESP_HF_CLIENT_AUDIO_STATE_EVT */ + + /** + * @brief ESP_HF_CLIENT_BVRA_EVT + */ + struct hf_client_bvra_param { + esp_hf_vr_state_t value; /*!< voice recognition state */ + } bvra; /*!< HF callback param of ESP_HF_CLIENT_BVRA_EVT */ + + /** + * @brief ESP_HF_CLIENT_CIND_SERVICE_AVAILABILITY_EVT + */ + struct hf_client_service_availability_param { + esp_hf_network_state_t status; /*!< service availability status */ + } service_availability; /*!< HF callback param of ESP_HF_CLIENT_CIND_SERVICE_AVAILABILITY_EVT */ + + /** + * @brief ESP_HF_CLIENT_CIND_ROAMING_STATUS_EVT + */ + struct hf_client_network_roaming_param { + esp_hf_roaming_status_t status; /*!< roaming status */ + } roaming; /*!< HF callback param of ESP_HF_CLIENT_CIND_ROAMING_STATUS_EVT */ + + /** + * @brief ESP_HF_CLIENT_CIND_SIGNAL_STRENGTH_EVT + */ + struct hf_client_signal_strength_ind_param { + int value; /*!< signal strength value, ranges from 0 to 5 */ + } signal_strength; /*!< HF callback param of ESP_HF_CLIENT_CIND_SIGNAL_STRENGTH_EVT */ + + /** + * @brief ESP_HF_CLIENT_CIND_BATTERY_LEVEL_EVT + */ + struct hf_client_battery_level_ind_param { + int value; /*!< battery charge value, ranges from 0 to 5 */ + } battery_level; /*!< HF callback param of ESP_HF_CLIENT_CIND_BATTERY_LEVEL_EVT */ + + /** + * @brief ESP_HF_CLIENT_COPS_CURRENT_OPERATOR_EVT + */ + struct hf_client_current_operator_param { + const char *name; /*!< name of the network operator */ + } cops; /*!< HF callback param of ESP_HF_CLIENT_COPS_CURRENT_OPERATOR_EVT */ + + /** + * @brief ESP_HF_CLIENT_CIND_CALL_EVT + */ + struct hf_client_call_ind_param { + esp_hf_call_status_t status; /*!< call status indicator */ + } call; /*!< HF callback param of ESP_HF_CLIENT_CIND_CALL_EVT */ + + /** + * @brief ESP_HF_CLIENT_CIND_CALL_SETUP_EVT + */ + struct hf_client_call_setup_ind_param { + esp_hf_call_setup_status_t status; /*!< call setup status indicator */ + } call_setup; /*!< HF callback param of ESP_HF_CLIENT_BVRA_EVT */ + + /** + * @brief ESP_HF_CLIENT_CIND_CALL_HELD_EVT + */ + struct hf_client_call_held_ind_param { + esp_hf_call_held_status_t status; /*!< bluetooth proprietary call hold status indicator */ + } call_held; /*!< HF callback param of ESP_HF_CLIENT_CIND_CALL_HELD_EVT */ + + /** + * @brief ESP_HF_CLIENT_BTRH_EVT + */ + struct hf_client_btrh_param { + esp_hf_btrh_status_t status; /*!< call hold and response status result code */ + } btrh; /*!< HF callback param of ESP_HF_CLIENT_BRTH_EVT */ + + /** + * @brief ESP_HF_CLIENT_CLIP_EVT + */ + struct hf_client_clip_param { + const char *number; /*!< phone number string of call */ + } clip; /*!< HF callback param of ESP_HF_CLIENT_CLIP_EVT */ + + /** + * @brief ESP_HF_CLIENT_CCWA_EVT + */ + struct hf_client_ccwa_param { + const char *number; /*!< phone number string of waiting call */ + } ccwa; /*!< HF callback param of ESP_HF_CLIENT_BVRA_EVT */ + + /** + * @brief ESP_HF_CLIENT_CLCC_EVT + */ + struct hf_client_clcc_param { + int idx; /*!< numbering(starting with 1) of the call */ + esp_hf_current_call_direction_t dir; /*!< direction of the call */ + esp_hf_current_call_status_t status; /*!< status of the call */ + esp_hf_current_call_mpty_type_t mpty; /*!< multi-party flag */ + char *number; /*!< phone number(optional) */ + } clcc; /*!< HF callback param of ESP_HF_CLIENT_CLCC_EVT */ + + /** + * @brief ESP_HF_CLIENT_VOLUME_CONTROL_EVT + */ + struct hf_client_volume_control_param { + esp_hf_volume_control_target_t type; /*!< volume control target, speaker or microphone */ + int volume; /*!< gain, ranges from 0 to 15 */ + } volume_control; /*!< HF callback param of ESP_HF_CLIENT_VOLUME_CONTROL_EVT */ + + /** + * @brief ESP_HF_CLIENT_AT_RESPONSE_EVT + */ + struct hf_client_at_response_param { + esp_hf_at_response_code_t code; /*!< AT response code */ + esp_hf_cme_err_t cme; /*!< Extended Audio Gateway Error Result Code */ + } at_response; /*!< HF callback param of ESP_HF_CLIENT_AT_RESPONSE_EVT */ + + /** + * @brief ESP_HF_CLIENT_CNUM_EVT + */ + struct hf_client_cnum_param { + const char *number; /*!< phone number string */ + esp_hf_subscriber_service_type_t type; /*!< service type that the phone number relates to */ + } cnum; /*!< HF callback param of ESP_HF_CLIENT_CNUM_EVT */ + + /** + * @brief ESP_HF_CLIENT_BSIR_EVT + */ + struct hf_client_bsirparam { + esp_hf_client_in_band_ring_state_t state; /*!< setting state of in-band ring tone */ + } bsir; /*!< HF callback param of ESP_HF_CLIENT_BSIR_EVT */ + + /** + * @brief ESP_HF_CLIENT_BINP_EVT + */ + struct hf_client_binp_param { + const char *number; /*!< phone number corresponding to the last voice tag in the HF */ + } binp; /*!< HF callback param of ESP_HF_CLIENT_BINP_EVT */ + + /** + * @brief ESP_HF_CLIENT_PKT_STAT_NUMS_GET_EVT + */ + struct hf_client_pkt_status_nums { + uint32_t rx_total; /*!< the total number of packets received */ + uint32_t rx_correct; /*!< the total number of packets data correctly received */ + uint32_t rx_err; /*!< the total number of packets data with possible invalid */ + uint32_t rx_none; /*!< the total number of packets data no received */ + uint32_t rx_lost; /*!< the total number of packets data partially lost */ + uint32_t tx_total; /*!< the total number of packets send */ + uint32_t tx_discarded; /*!< the total number of packets send lost */ + } pkt_nums; /*!< HF callback param of ESP_HF_CLIENT_PKT_STAT_NUMS_GET_EVT */ + +} esp_hf_client_cb_param_t; /*!< HFP client callback parameters */ + +/** + * @brief HFP client incoming data callback function, the callback is useful in case of + * Voice Over HCI. + * @param[in] buf : pointer to incoming data(payload of HCI synchronous data packet), the + * buffer is allocated inside bluetooth protocol stack and will be released after + * invoke of the callback is finished. + * @param[in] len : size(in bytes) in buf + */ +typedef void (* esp_hf_client_incoming_data_cb_t)(const uint8_t *buf, uint32_t len); + +/** + * @brief HFP client outgoing data callback function, the callback is useful in case of + * Voice Over HCI. Once audio connection is set up and the application layer has + * prepared data to send, the lower layer will call this function to read data + * and then send. This callback is supposed to be implemented as non-blocking, + * and if data is not enough, return value 0 is supposed. + * + * @param[in] buf : pointer to incoming data(payload of HCI synchronous data packet), the + * buffer is allocated inside bluetooth protocol stack and will be released after + * invoke of the callback is finished. + * + * @param[in] len : size(in bytes) in buf + * + * @return length of data successfully read + * + */ +typedef uint32_t (* esp_hf_client_outgoing_data_cb_t)(uint8_t *buf, uint32_t len); + +/** + * @brief HFP client callback function type + * + * @param event : Event type + * + * @param param : Pointer to callback parameter + */ +typedef void (* esp_hf_client_cb_t)(esp_hf_client_cb_event_t event, esp_hf_client_cb_param_t *param); + +/** + * @brief Register application callback function to HFP client module. + * This function should be called only after esp_bluedroid_enable() completes successfully. + * + * @param[in] callback: HFP client event callback function + * + * @return + * - ESP_OK: success + * - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled + * - ESP_FAIL: if callback is a NULL function pointer + * + */ +esp_err_t esp_hf_client_register_callback(esp_hf_client_cb_t callback); + +/** + * + * @brief Initialize the bluetooth HFP client module. + * This function should be called after esp_bluedroid_enable() completes successfully. + * + * @return + * - ESP_OK: if the initialization request is sent successfully + * - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled + * - ESP_FAIL: others + * + */ +esp_err_t esp_hf_client_init(void); + +/** + * + * @brief De-initialize for HFP client module. + * This function should be called only after esp_bluedroid_enable() completes successfully. + * + * @return + * - ESP_OK: success + * - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled + * - ESP_FAIL: others + * + */ +esp_err_t esp_hf_client_deinit(void); + +/** + * + * @brief Establish a Service Level Connection to remote bluetooth HFP audio gateway(AG) device. + * This function must be called after esp_hf_client_init() and before esp_hf_client_deinit(). + * + * @param[in] remote_bda: remote bluetooth device address + * + * @return + * - ESP_OK: connect request is sent to lower layer + * - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled + * - ESP_FAIL: others + * + */ +esp_err_t esp_hf_client_connect(esp_bd_addr_t remote_bda); + +/** + * + * @brief Disconnect from the remote HFP audio gateway. + * This function must be called after esp_hf_client_init() and before esp_hf_client_deinit(). + * + * @param[in] remote_bda: remote bluetooth device address + * + * @return + * - ESP_OK: disconnect request is sent to lower layer + * - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled + * - ESP_FAIL: others + * + */ +esp_err_t esp_hf_client_disconnect(esp_bd_addr_t remote_bda); + +/** + * + * @brief Create audio connection with remote HFP AG. + * As a precondition to use this API, Service Level Connection shall exist with AG. + * + * @param[in] remote_bda: remote bluetooth device address + * @return + * - ESP_OK: connect audio request is sent to lower layer + * - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled + * - ESP_FAIL: others + * + */ +esp_err_t esp_hf_client_connect_audio(esp_bd_addr_t remote_bda); + +/** + * + * @brief Release the established audio connection with remote HFP AG. + * As a precondition to use this API, Service Level Connection shall exist with AG. + * + * @param[in] remote_bda: remote bluetooth device address + * @return + * - ESP_OK: disconnect audio request is sent to lower layer + * - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled + * - ESP_FAIL: others + * + */ +esp_err_t esp_hf_client_disconnect_audio(esp_bd_addr_t remote_bda); + +/** + * + * @brief Enable voice recognition in the AG. + * As a precondition to use this API, Service Level Connection shall exist with AG. + * + * @return + * - ESP_OK: starting voice recognition is sent to lower layer + * - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled + * - ESP_FAIL: others + * + */ +esp_err_t esp_hf_client_start_voice_recognition(void); + +/** + * + * @brief Disable voice recognition in the AG. + * As a precondition to use this API, Service Level Connection shall exist with AG. + * + * @return + * - ESP_OK: stoping voice recognition is sent to lower layer + * - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled + * - ESP_FAIL: others + * + */ +esp_err_t esp_hf_client_stop_voice_recognition(void); + +/** + * + * @brief Volume synchronization with AG. + * As a precondition to use this API, Service Level Connection shall exist with AG. + * + * @param[in] type: volume control target, speaker or microphone + * @param[in] volume: gain of the speaker of microphone, ranges 0 to 15 + * + * @return + * - ESP_OK: volume update is sent to lower layer + * - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled + * - ESP_FAIL: others + * + */ +esp_err_t esp_hf_client_volume_update(esp_hf_volume_control_target_t type, int volume); + +/** + * + * @brief Place a call with a specified number, if number is NULL, last called number is called. + * As a precondition to use this API, Service Level Connection shall exist with AG. + * + * @param[in] number: number string of the call. If NULL, the last number is called(aka re-dial) + * + * @return + * - ESP_OK: a call placing is sent to lower layer + * - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled + * - ESP_FAIL: others + * + */ +esp_err_t esp_hf_client_dial(const char *number); + +/** + * + * @brief Place a call with number specified by location(speed dial). + * As a precondition to use this API, Service Level Connection shall exist with AG. + * + * @param[in] location: location of the number in the memory + * + * @return + * - ESP_OK: a memory call placing is sent to lower layer + * - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled + * - ESP_FAIL: others + * + */ + +esp_err_t esp_hf_client_dial_memory(int location); + +/** + * + * @brief Send call hold and multiparty commands, or enhanced call control commands(Use AT+CHLD). + * As a precondition to use this API, Service Level Connection shall exist with AG. + * + * @param[in] chld: AT+CHLD call hold and multiparty handling AT command. + * @param[in] idx: used in Enhanced Call Control Mechanisms, used if chld is + * ESP_HF_CHLD_TYPE_REL_X or ESP_HF_CHLD_TYPE_PRIV_X + * + * @return + * - ESP_OK: command AT+CHLD is sent to lower layer + * - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled + * - ESP_FAIL: others + * + */ +esp_err_t esp_hf_client_send_chld_cmd(esp_hf_chld_type_t chld, int idx); + +/** + * + * @brief Send response and hold action command(Send AT+BTRH command) + * As a precondition to use this API, Service Level Connection shall exist with AG. + * + * @param[in] btrh: response and hold action to send + * + * @return + * - ESP_OK: command AT+BTRH is sent to lower layer + * - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled + * - ESP_FAIL: others + * + */ +esp_err_t esp_hf_client_send_btrh_cmd(esp_hf_btrh_cmd_t btrh); + +/** + * + * @brief Answer an incoming call(send ATA command). + * As a precondition to use this API, Service Level Connection shall exist with AG. + * + * @return + * - ESP_OK: a call answering is sent to lower layer + * - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled + * - ESP_FAIL: others + * + */ +esp_err_t esp_hf_client_answer_call(void); + +/** + * + * @brief Reject an incoming call or terminate an ongoing call(send AT+CHUP command). + * As a precondition to use this API, Service Level Connection shall exist with AG. + * + * @return + * - ESP_OK: the call rejecting is sent to lower layer + * - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled + * - ESP_FAIL: others + * + */ +esp_err_t esp_hf_client_reject_call(void); + +/** + * + * @brief Query list of current calls in AG(send AT+CLCC command). + * As a precondition to use this API, Service Level Connection shall exist with AG. + * + * @return + * - ESP_OK: query of current calls is sent to lower layer + * - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled + * - ESP_FAIL: others + * + */ +esp_err_t esp_hf_client_query_current_calls(void); + +/** + * + * @brief Query the name of currently selected network operator in AG(use AT+COPS commands). + * As a precondition to use this API, Service Level Connection shall exist with AG. + * + * @return + * - ESP_OK: query of current operator name is sent to lower layer + * - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled + * - ESP_FAIL: others + * + */ +esp_err_t esp_hf_client_query_current_operator_name(void); + +/** + * + * @brief Get subscriber information number from AG(send AT+CNUM command) + * As a precondition to use this API, Service Level Connection shall exist with AG + * + * @return + * - ESP_OK: the retrieving of subscriber information is sent to lower layer + * - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled + * - ESP_FAIL: others + * + */ +esp_err_t esp_hf_client_retrieve_subscriber_info(void); + +/** + * + * @brief Transmit DTMF codes during an ongoing call(use AT+VTS commands) + * As a precondition to use this API, Service Level Connection shall exist with AG. + * + * @param[in] code: dtmf code, single ascii character in the set 0-9, #, *, A-D + * + * @return + * - ESP_OK: the DTMF codes are sent to lower layer + * - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled + * - ESP_FAIL: others + * + */ +esp_err_t esp_hf_client_send_dtmf(char code); + +/** + * + * @brief Send command to enable Vendor specific feature to indicate battery level + * and docker status + * This is Apple-specific commands, but used by most device, including Android and Windows + * + * @param[in] information: XAPL vendorID-productID-version, such as "0505-1995-0610" + * vendorID: A string representation of the hex value of the vendor ID from the manufacturer, without the 0x prefix. + * productID: A string representation of the hex value of the product ID from the manufacturer, without the 0x prefix. + * version: The revision of the software + * @param[in] features: A base-10 representation of a bit field. such as ESP_HF_CLIENT_XAPL_FEAT_BATTERY_REPORT + * + * @return + * - ESP_OK: Feature enable request is sent to lower layer + * - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled + * - ESP_FAIL: others + * + */ +esp_err_t esp_hf_client_send_xapl(char *information, uint32_t features); + +/** + * + * @brief Send Battery level and docker status + * Enable this feature using XAPL command first + * This is Apple-specific commands, but used by most device, including Android and Windows + * + * + * @param[in] bat_level: Battery Level: value between 0 and 9 + * @param[in] docked: Dock State: false = undocked, true = docked + * + * @return + * - ESP_OK: battery level is sent to lower layer + * - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled + * - ESP_FAIL: others + * + */ +esp_err_t esp_hf_client_send_iphoneaccev(uint32_t bat_level, bool docked); + +/** + * + * @brief Request a phone number from AG corresponding to last voice tag recorded (send AT+BINP command). + * As a precondition to use this API, Service Level Connection shall exist with AG. + * + * @return + * - ESP_OK: the phone number request corresponding to last voice tag recorded is sent to lower layer + * - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled + * - ESP_FAIL: others + * + */ +esp_err_t esp_hf_client_request_last_voice_tag_number(void); + +/** + * + * @brief Disable echo cancellation and noise reduction in the AG (use AT+NREC=0 command). + * As a precondition to use this API, Service Level Connection shall exist with AG + * + * @return + * - ESP_OK: NREC=0 request is sent to lower layer + * - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled + * - ESP_FAIL: others + * + */ +esp_err_t esp_hf_client_send_nrec(void); + + +/** + * @brief Register HFP client data output function; the callback is only used in + * the case that Voice Over HCI is enabled. + * + * @param[in] recv: HFP client incoming data callback function + * + * @param[in] send: HFP client outgoing data callback function + * + * @return + * - ESP_OK: success + * - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled + * - ESP_FAIL: if callback is a NULL function pointer + * + */ +esp_err_t esp_hf_client_register_data_callback(esp_hf_client_incoming_data_cb_t recv, + esp_hf_client_outgoing_data_cb_t send); + +/** + * + * @brief Get the number of packets received and sent + * This function is only used in the case that Voice Over HCI is enabled and the audio state is connected. + * When the operation is completed, the callback function will be called with ESP_HF_CLIENT_PKT_STAT_NUMS_GET_EVT. + * + * @param[in] sync_conn_handle: the (e)SCO connection handle + * + * @return + * - ESP_OK: if the request is sent successfully + * - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled + * - ESP_FAIL: others + * + */ +esp_err_t esp_hf_client_pkt_stat_nums_get(uint16_t sync_conn_handle); + +/** + * @brief Trigger the lower-layer to fetch and send audio data. + * This function is only only used in the case that Voice Over HCI is enabled. After this + * function is called, lower layer will invoke esp_hf_client_outgoing_data_cb_t to fetch data. + * + * As a precondition to use this API, Service Level Connection shall exist with AG. + * + */ +void esp_hf_client_outgoing_data_ready(void); + + +/** + * @brief Initialize the down sampling converter. This is a utility function that can + * only be used in the case that Voice Over HCI is enabled. + * + * @param[in] src_sps: original samples per second(source audio data, i.e. 48000, 32000, + * 16000, 44100, 22050, 11025) + * @param[in] bits: number of bits per pcm sample (16) + * + * @param[in] channels: number of channels (i.e. mono(1), stereo(2)...) + */ +void esp_hf_client_pcm_resample_init(uint32_t src_sps, uint32_t bits, uint32_t channels); + +/** + * @brief Deinitialize the down sampling converter. + */ +void esp_hf_client_pcm_resample_deinit(void); + +/** + * @brief Down sampling utility to convert high sampling rate into 8K/16bits 1-channel mode PCM + * samples. This can only be used in the case that Voice Over HCI is enabled. + * + * @param[in] src: pointer to the buffer where the original sampling PCM are stored + * + * @param[in] in_bytes: length of the input PCM sample buffer in byte + * + * @param[in] dst: pointer to the buffer which is to be used to store the converted PCM samples + * + * @return number of samples converted + */ +int32_t esp_hf_client_pcm_resample(void *src, uint32_t in_bytes, void *dst); + +#ifdef __cplusplus +} +#endif + + +#endif /* __ESP_HF_CLIENT_API_H__ */ diff --git a/lib/bt/host/bluedroid/api/include/api/esp_hf_defs.h b/lib/bt/host/bluedroid/api/include/api/esp_hf_defs.h new file mode 100644 index 00000000..b7671ff1 --- /dev/null +++ b/lib/bt/host/bluedroid/api/include/api/esp_hf_defs.h @@ -0,0 +1,246 @@ +/* + * SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __ESP_HF_DEFS_H__ +#define __ESP_HF_DEFS_H__ + +#include "esp_bt_defs.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/// in-band ring tone state +typedef enum { + ESP_HF_IN_BAND_RINGTONE_NOT_PROVIDED = 0, + ESP_HF_IN_BAND_RINGTONE_PROVIDED, +} esp_hf_in_band_ring_state_t; + +/// voice recognition state +typedef enum { + ESP_HF_VR_STATE_DISABLED = 0, /*!< voice recognition disabled */ + ESP_HF_VR_STATE_ENABLED, /*!< voice recognition enabled */ +} esp_hf_vr_state_t; + +/// Bluetooth HFP audio volume control target +typedef enum { + ESP_HF_VOLUME_CONTROL_TARGET_SPK = 0, /*!< speaker */ + ESP_HF_VOLUME_CONTROL_TARGET_MIC, /*!< microphone */ +} esp_hf_volume_control_target_t; + +/// Bluetooth HFP audio connection status +typedef enum { + ESP_HF_AUDIO_STATE_DISCONNECTED = 0, /*!< audio connection released */ + ESP_HF_AUDIO_STATE_CONNECTING, /*!< audio connection has been initiated */ + ESP_HF_AUDIO_STATE_CONNECTED, /*!< audio connection is established */ + ESP_HF_AUDIO_STATE_CONNECTED_MSBC, /*!< mSBC audio connection is established */ +} esp_hf_audio_state_t; + +typedef enum { + ESP_HF_VOLUME_TYPE_SPK = 0, + ESP_HF_VOLUME_TYPE_MIC +} esp_hf_volume_type_t; + +/// +CIND network service availability status +typedef enum +{ + ESP_HF_NETWORK_STATE_NOT_AVAILABLE = 0, + ESP_HF_NETWORK_STATE_AVAILABLE +} esp_hf_network_state_t; + +/// +CIEV report type +typedef enum { + ESP_HF_IND_TYPE_CALL = 1, /*!< position of call indicator */ + ESP_HF_IND_TYPE_CALLSETUP, /*!< position of callsetup indicator */ + ESP_HF_IND_TYPE_SERVICE, /*!< position of service indicator */ + ESP_HF_IND_TYPE_SIGNAL, /*!< position of signal strength indicator, range: 0-5 */ + ESP_HF_IND_TYPE_ROAM, /*!< position of roaming indicator */ + ESP_HF_IND_TYPE_BATTCHG, /*!< position of battery charge indicator, range: 0-5 */ + ESP_HF_IND_TYPE_CALLHELD /*!< position of callheld indicator */ +} esp_hf_ciev_report_type_t; + +/** +CIEV Service type */ +typedef enum +{ + ESP_HF_SERVICE_TYPE_HOME = 0, + ESP_HF_SERVICE_TYPE_ROAMING +} esp_hf_service_type_t; + +/// +CIND call status indicator values +typedef enum { + ESP_HF_CALL_STATUS_NO_CALLS = 0, /*!< no call in progress */ + ESP_HF_CALL_STATUS_CALL_IN_PROGRESS = 1, /*!< call is present(active or held) */ +} esp_hf_call_status_t; + +/// +CIND call setup status indicator values +typedef enum { + ESP_HF_CALL_SETUP_STATUS_IDLE = 0, /*!< no call setup in progress */ + ESP_HF_CALL_SETUP_STATUS_INCOMING = 1, /*!< incoming call setup in progress */ + ESP_HF_CALL_SETUP_STATUS_OUTGOING_DIALING = 2, /*!< outgoing call setup in dialing state */ + ESP_HF_CALL_SETUP_STATUS_OUTGOING_ALERTING = 3, /*!< outgoing call setup in alerting state */ +} esp_hf_call_setup_status_t; + +/// +CIND roaming status indicator values +typedef enum { + ESP_HF_ROAMING_STATUS_INACTIVE = 0, /*!< roaming is not active */ + ESP_HF_ROAMING_STATUS_ACTIVE, /*!< a roaming is active */ +} esp_hf_roaming_status_t; + +/// +CIND call held indicator values +typedef enum { + ESP_HF_CALL_HELD_STATUS_NONE = 0, /*!< no calls held */ + ESP_HF_CALL_HELD_STATUS_HELD_AND_ACTIVE = 1, /*!< both active and held call */ + ESP_HF_CALL_HELD_STATUS_HELD = 2, /*!< call on hold, no active call*/ +} esp_hf_call_held_status_t; + +/// +CLCC status of the call +typedef enum { + ESP_HF_CURRENT_CALL_STATUS_ACTIVE = 0, /*!< active */ + ESP_HF_CURRENT_CALL_STATUS_HELD = 1, /*!< held */ + ESP_HF_CURRENT_CALL_STATUS_DIALING = 2, /*!< dialing (outgoing calls only) */ + ESP_HF_CURRENT_CALL_STATUS_ALERTING = 3, /*!< alerting (outgoing calls only) */ + ESP_HF_CURRENT_CALL_STATUS_INCOMING = 4, /*!< incoming (incoming calls only) */ + ESP_HF_CURRENT_CALL_STATUS_WAITING = 5, /*!< waiting (incoming calls only) */ + ESP_HF_CURRENT_CALL_STATUS_HELD_BY_RESP_HOLD = 6, /*!< call held by response and hold */ +} esp_hf_current_call_status_t; + +/// +CLCC direction of the call +typedef enum { + ESP_HF_CURRENT_CALL_DIRECTION_OUTGOING = 0, /*!< outgoing */ + ESP_HF_CURRENT_CALL_DIRECTION_INCOMING = 1, /*!< incoming */ +} esp_hf_current_call_direction_t; + +/// +CLCC multi-party call flag +typedef enum { + ESP_HF_CURRENT_CALL_MPTY_TYPE_SINGLE = 0, /*!< not a member of a multi-party call */ + ESP_HF_CURRENT_CALL_MPTY_TYPE_MULTI = 1, /*!< member of a multi-party call */ +} esp_hf_current_call_mpty_type_t; + +/// +CLCC call mode +typedef enum { + ESP_HF_CURRENT_CALL_MODE_VOICE = 0, + ESP_HF_CURRENT_CALL_MODE_DATA = 1, + ESP_HF_CURRENT_CALL_MODE_FAX = 2, +} esp_hf_current_call_mode_t; + +/// +CLCC address type +typedef enum { + ESP_HF_CALL_ADDR_TYPE_UNKNOWN = 0x81, /*!< unkown address type */ + ESP_HF_CALL_ADDR_TYPE_INTERNATIONAL = 0x91, /*!< international address */ +} esp_hf_call_addr_type_t; + +/// +CNUM service type of the phone number +typedef enum { + ESP_HF_SUBSCRIBER_SERVICE_TYPE_UNKNOWN = 0, /*!< unknown */ + ESP_HF_SUBSCRIBER_SERVICE_TYPE_VOICE = 4, /*!< voice service */ + ESP_HF_SUBSCRIBER_SERVICE_TYPE_FAX, /*!< fax service */ +} esp_hf_subscriber_service_type_t; + +/// +BTRH response and hold result code +typedef enum { + ESP_HF_BTRH_STATUS_HELD = 0, /*!< incoming call is put on held in AG */ + ESP_HF_BTRH_STATUS_ACCEPTED, /*!< held incoming call is accepted in AG */ + ESP_HF_BTRH_STATUS_REJECTED, /*!< held incoming call is rejected in AG */ +} esp_hf_btrh_status_t; + +/// AT+BTRH response and hold action code +typedef enum { + ESP_HF_BTRH_CMD_HOLD = 0, /*!< put the incoming call on hold */ + ESP_HF_BTRH_CMD_ACCEPT = 1, /*!< accept a held incoming call */ + ESP_HF_BTRH_CMD_REJECT = 2, /*!< reject a held incoming call */ +} esp_hf_btrh_cmd_t; + +/* +NREC */ +typedef enum +{ + ESP_HF_NREC_STOP = 0, + ESP_HF_NREC_START +} esp_hf_nrec_t; + +///+CCWA resposne status +typedef enum { + ESP_HF_CALL_WAITING_INACTIVE, + ESP_HF_CALL_WAITING_ACTIVE, +} esp_hf_call_waiting_status_t; + +/* WBS codec setting */ +typedef enum +{ + ESP_HF_WBS_NONE, + ESP_HF_WBS_NO, + ESP_HF_WBS_YES +}esp_hf_wbs_config_t; + +/// Bluetooth HFP RFCOMM connection and service level connection status +typedef enum { + ESP_HF_CONNECTION_STATE_DISCONNECTED = 0, /*!< RFCOMM data link channel released */ + ESP_HF_CONNECTION_STATE_CONNECTING, /*!< connecting remote device on the RFCOMM data link*/ + ESP_HF_CONNECTION_STATE_CONNECTED, /*!< RFCOMM connection established */ + ESP_HF_CONNECTION_STATE_SLC_CONNECTED, /*!< service level connection established */ + ESP_HF_CONNECTION_STATE_DISCONNECTING, /*!< disconnecting with remote device on the RFCOMM data link*/ +} esp_hf_connection_state_t; + +/// AT+CHLD command values +typedef enum { + ESP_HF_CHLD_TYPE_REL = 0, /*!< <0>, Terminate all held or set UDUB("busy") to a waiting call */ + ESP_HF_CHLD_TYPE_REL_ACC, /*!< <1>, Terminate all active calls and accepts a waiting/held call */ + ESP_HF_CHLD_TYPE_HOLD_ACC, /*!< <2>, Hold all active calls and accepts a waiting/held call */ + ESP_HF_CHLD_TYPE_MERGE, /*!< <3>, Add all held calls to a conference */ + ESP_HF_CHLD_TYPE_MERGE_DETACH, /*!< <4>, connect the two calls and disconnects the subscriber from both calls */ + ESP_HF_CHLD_TYPE_REL_X, /*!< <1x>, releases specified calls only */ + ESP_HF_CHLD_TYPE_PRIV_X, /*!< <2x>, request private consultation mode with specified call */ +} esp_hf_chld_type_t; + +/* AT response code - OK/Error */ +typedef enum { + ESP_HF_AT_RESPONSE_CODE_OK = 0, /*!< acknowledges execution of a command line */ + ESP_HF_AT_RESPONSE_CODE_ERR, /*!< command not accepted */ + ESP_HF_AT_RESPONSE_CODE_NO_CARRIER, /*!< connection terminated */ + ESP_HF_AT_RESPONSE_CODE_BUSY, /*!< busy signal detected */ + ESP_HF_AT_RESPONSE_CODE_NO_ANSWER, /*!< connection completion timeout */ + ESP_HF_AT_RESPONSE_CODE_DELAYED, /*!< delayed */ + ESP_HF_AT_RESPONSE_CODE_BLACKLISTED, /*!< blacklisted */ + ESP_HF_AT_RESPONSE_CODE_CME, /*!< CME error */ +} esp_hf_at_response_code_t; + +/* AT response code - OK/Error */ +typedef enum { + ESP_HF_AT_RESPONSE_ERROR = 0, + ESP_HF_AT_RESPONSE_OK +} esp_hf_at_response_t; + +/// Extended Audio Gateway Error Result Code Response +typedef enum { + ESP_HF_CME_AG_FAILURE = 0, /*!< ag failure */ + ESP_HF_CME_NO_CONNECTION_TO_PHONE = 1, /*!< no connection to phone */ + ESP_HF_CME_OPERATION_NOT_ALLOWED = 3, /*!< operation not allowed */ + ESP_HF_CME_OPERATION_NOT_SUPPORTED = 4, /*!< operation not supported */ + ESP_HF_CME_PH_SIM_PIN_REQUIRED = 5, /*!< PH-SIM PIN Required */ + ESP_HF_CME_SIM_NOT_INSERTED = 10, /*!< SIM not inserted */ + ESP_HF_CME_SIM_PIN_REQUIRED = 11, /*!< SIM PIN required */ + ESP_HF_CME_SIM_PUK_REQUIRED = 12, /*!< SIM PUK required */ + ESP_HF_CME_SIM_FAILURE = 13, /*!< SIM failure */ + ESP_HF_CME_SIM_BUSY = 14, /*!< SIM busy */ + ESP_HF_CME_INCORRECT_PASSWORD = 16, /*!< incorrect password */ + ESP_HF_CME_SIM_PIN2_REQUIRED = 17, /*!< SIM PIN2 required */ + ESP_HF_CME_SIM_PUK2_REQUIRED = 18, /*!< SIM PUK2 required */ + ESP_HF_CME_MEMORY_FULL = 20, /*!< memory full */ + ESP_HF_CME_INVALID_INDEX = 21, /*!< invalid index */ + ESP_HF_CME_MEMORY_FAILURE = 23, /*!< memory failure */ + ESP_HF_CME_TEXT_STRING_TOO_LONG = 24, /*!< test string too long */ + ESP_HF_CME_INVALID_CHARACTERS_IN_TEXT_STRING = 25, /*!< invalid characters in text string */ + ESP_HF_CME_DIAL_STRING_TOO_LONG = 26, /*!< dial string too long*/ + ESP_HF_CME_INVALID_CHARACTERS_IN_DIAL_STRING = 27, /*!< invalid characters in dial string */ + ESP_HF_CME_NO_NETWORK_SERVICE = 30, /*!< no network service */ + ESP_HF_CME_NETWORK_TIMEOUT = 31, /*!< network timeout */ + ESP_HF_CME_NETWORK_NOT_ALLOWED = 32, /*!< network not allowed --emergency calls only */ +} esp_hf_cme_err_t; + +#ifdef __cplusplus +} +#endif + +#endif /* __ESP_HF_DEFS_H__ */ diff --git a/lib/bt/host/bluedroid/api/include/api/esp_hidd_api.h b/lib/bt/host/bluedroid/api/include/api/esp_hidd_api.h new file mode 100644 index 00000000..a2af1659 --- /dev/null +++ b/lib/bt/host/bluedroid/api/include/api/esp_hidd_api.h @@ -0,0 +1,413 @@ +/* + * SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + * + * SPDX-FileContributor: Blake Felt + */ + +#ifndef __ESP_HIDD_API_H__ +#define __ESP_HIDD_API_H__ + +#include "esp_bt_defs.h" +#include "esp_err.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/// subclass of hid device +#define ESP_HID_CLASS_UNKNOWN (0x00<<2) /*!< unknown HID device subclass */ +#define ESP_HID_CLASS_JOS (0x01<<2) /*!< joystick */ +#define ESP_HID_CLASS_GPD (0x02<<2) /*!< game pad */ +#define ESP_HID_CLASS_RMC (0x03<<2) /*!< remote control */ +#define ESP_HID_CLASS_SED (0x04<<2) /*!< sensing device */ +#define ESP_HID_CLASS_DGT (0x05<<2) /*!< digitizer tablet */ +#define ESP_HID_CLASS_CDR (0x06<<2) /*!< card reader */ +#define ESP_HID_CLASS_KBD (0x10<<2) /*!< keyboard */ +#define ESP_HID_CLASS_MIC (0x20<<2) /*!< pointing device */ +#define ESP_HID_CLASS_COM (0x30<<2) /*!< combo keyboard/pointing */ + +/** + * @brief HIDD handshake result code + */ +typedef enum { + ESP_HID_PAR_HANDSHAKE_RSP_SUCCESS = 0, /*!< successful */ + ESP_HID_PAR_HANDSHAKE_RSP_NOT_READY = 1, /*!< not ready, device is too busy to accept data */ + ESP_HID_PAR_HANDSHAKE_RSP_ERR_INVALID_REP_ID = 2, /*!< invalid report ID */ + ESP_HID_PAR_HANDSHAKE_RSP_ERR_UNSUPPORTED_REQ = 3, /*!< device does not support the request */ + ESP_HID_PAR_HANDSHAKE_RSP_ERR_INVALID_PARAM = 4, /*!< parameter value is out of range or inappropriate */ + ESP_HID_PAR_HANDSHAKE_RSP_ERR_UNKNOWN = 14, /*!< device could not identify the error condition */ + ESP_HID_PAR_HANDSHAKE_RSP_ERR_FATAL = 15, /*!< restart is essential to resume functionality */ +} esp_hidd_handshake_error_t; + +/** + * @brief HIDD report types + */ +typedef enum { + ESP_HIDD_REPORT_TYPE_OTHER = 0, /*!< unknown report type */ + ESP_HIDD_REPORT_TYPE_INPUT, /*!< input report */ + ESP_HIDD_REPORT_TYPE_OUTPUT, /*!< output report */ + ESP_HIDD_REPORT_TYPE_FEATURE, /*!< feature report */ + ESP_HIDD_REPORT_TYPE_INTRDATA, /*!< special value for reports to be sent on interrupt channel, INPUT is assumed */ +} esp_hidd_report_type_t; + +/** + * @brief HIDD connection state + */ +typedef enum { + ESP_HIDD_CONN_STATE_CONNECTED, /*!< HID connection established */ + ESP_HIDD_CONN_STATE_CONNECTING, /*!< connection to remote Bluetooth device */ + ESP_HIDD_CONN_STATE_DISCONNECTED, /*!< connection released */ + ESP_HIDD_CONN_STATE_DISCONNECTING, /*!< disconnecting to remote Bluetooth device*/ + ESP_HIDD_CONN_STATE_UNKNOWN, /*!< unknown connection state */ +} esp_hidd_connection_state_t; + +/** + * @brief HID device protocol modes + */ +typedef enum { + ESP_HIDD_REPORT_MODE = 0x00, /*!< Report Protocol Mode */ + ESP_HIDD_BOOT_MODE = 0x01, /*!< Boot Protocol Mode */ + ESP_HIDD_UNSUPPORTED_MODE = 0xff, /*!< unsupported */ +} esp_hidd_protocol_mode_t; + +/** + * @brief HID Boot Protocol report IDs + */ +typedef enum { + ESP_HIDD_BOOT_REPORT_ID_KEYBOARD = 1, /*!< report ID of Boot Protocol keyboard report */ + ESP_HIDD_BOOT_REPORT_ID_MOUSE = 2, /*!< report ID of Boot Protocol mouse report */ +} esp_hidd_boot_report_id_t; + +/** + * @brief HID Boot Protocol report size including report ID + */ +enum { + ESP_HIDD_BOOT_REPORT_SIZE_KEYBOARD = 9, /*!< report size of Boot Protocol keyboard report */ + ESP_HIDD_BOOT_REPORT_SIZE_MOUSE = 4, /*!< report size of Boot Protocol mouse report */ +}; + +/** + * @brief HID device characteristics for SDP server + */ +typedef struct { + const char *name; /*!< service name */ + const char *description; /*!< service description */ + const char *provider; /*!< provider name */ + uint8_t subclass; /*!< HID device subclass */ + uint8_t *desc_list; /*!< HID descriptor list */ + int desc_list_len; /*!< size in bytes of HID descriptor list */ +} esp_hidd_app_param_t; + +/** + * @brief HIDD Quality of Service parameters negotiated over L2CAP + */ +typedef struct { + uint8_t service_type; /*!< the level of service, 0 indicates no traffic */ + uint32_t token_rate; /*!< token rate in bytes per second, 0 indicates "don't care" */ + uint32_t token_bucket_size; /*!< limit on the burstness of the application data */ + uint32_t peak_bandwidth; /*!< bytes per second, value 0 indicates "don't care" */ + uint32_t access_latency; /*!< maximum acceptable delay in microseconds */ + uint32_t delay_variation; /*!< the difference in microseconds between the max and min delay */ +} esp_hidd_qos_param_t; + +/** + * @brief HID device callback function events + */ +typedef enum { + ESP_HIDD_INIT_EVT = 0, /*!< When HID device is initialized, the event comes */ + ESP_HIDD_DEINIT_EVT, /*!< When HID device is deinitialized, the event comes */ + ESP_HIDD_REGISTER_APP_EVT, /*!< When HID device application registered, the event comes */ + ESP_HIDD_UNREGISTER_APP_EVT, /*!< When HID device application unregistered, the event comes */ + ESP_HIDD_OPEN_EVT, /*!< When HID device connection to host opened, the event comes */ + ESP_HIDD_CLOSE_EVT, /*!< When HID device connection to host closed, the event comes */ + ESP_HIDD_SEND_REPORT_EVT, /*!< When HID device send report to lower layer, the event comes */ + ESP_HIDD_REPORT_ERR_EVT, /*!< When HID device report handshanke error to lower layer, the event comes */ + ESP_HIDD_GET_REPORT_EVT, /*!< When HID device receives GET_REPORT request from host, the event comes */ + ESP_HIDD_SET_REPORT_EVT, /*!< When HID device receives SET_REPORT request from host, the event comes */ + ESP_HIDD_SET_PROTOCOL_EVT, /*!< When HID device receives SET_PROTOCOL request from host, the event comes */ + ESP_HIDD_INTR_DATA_EVT, /*!< When HID device receives DATA from host on intr, the event comes */ + ESP_HIDD_VC_UNPLUG_EVT, /*!< When HID device initiates Virtual Cable Unplug, the event comes */ + ESP_HIDD_API_ERR_EVT /*!< When HID device has API error, the event comes */ +} esp_hidd_cb_event_t; + +typedef enum { + ESP_HIDD_SUCCESS, + ESP_HIDD_ERROR, /*!< general ESP HD error */ + ESP_HIDD_NO_RES, /*!< out of system resources */ + ESP_HIDD_BUSY, /*!< Temporarily can not handle this request. */ + ESP_HIDD_NO_DATA, /*!< No data. */ + ESP_HIDD_NEED_INIT, /*!< HIDD module shall init first */ + ESP_HIDD_NEED_DEINIT, /*!< HIDD module shall deinit first */ + ESP_HIDD_NEED_REG, /*!< HIDD module shall register first */ + ESP_HIDD_NEED_DEREG, /*!< HIDD module shall deregister first */ + ESP_HIDD_NO_CONNECTION, /*!< connection may have been closed */ +} esp_hidd_status_t; + +/** + * @brief HID device callback parameters union + */ +typedef union { + /** + * @brief ESP_HIDD_INIT_EVT + */ + struct hidd_init_evt_param { + esp_hidd_status_t status; /*!< operation status */ + } init; /*!< HIDD callback param of ESP_HIDD_INIT_EVT */ + + /** + * @brief ESP_HIDD_DEINIT_EVT + */ + struct hidd_deinit_evt_param { + esp_hidd_status_t status; /*!< operation status */ + } deinit; /*!< HIDD callback param of ESP_HIDD_DEINIT_EVT */ + + /** + * @brief ESP_HIDD_REGISTER_APP_EVT + */ + struct hidd_register_app_evt_param { + esp_hidd_status_t status; /*!< operation status */ + bool in_use; /*!< indicate whether use virtual cable plug host address */ + esp_bd_addr_t bd_addr; /*!< host address */ + } register_app; /*!< HIDD callback param of ESP_HIDD_REGISTER_APP_EVT */ + + /** + * @brief ESP_HIDD_UNREGISTER_APP_EVT + */ + struct hidd_unregister_app_evt_param { + esp_hidd_status_t status; /*!< operation status */ + } unregister_app; /*!< HIDD callback param of ESP_HIDD_UNREGISTER_APP_EVT */ + + /** + * @brief ESP_HIDD_OPEN_EVT + */ + struct hidd_open_evt_param { + esp_hidd_status_t status; /*!< operation status */ + esp_hidd_connection_state_t conn_status; /*!< connection status */ + esp_bd_addr_t bd_addr; /*!< host address */ + } open; /*!< HIDD callback param of ESP_HIDD_OPEN_EVT */ + + /** + * @brief ESP_HIDD_CLOSE_EVT + */ + struct hidd_close_evt_param { + esp_hidd_status_t status; /*!< operation status */ + esp_hidd_connection_state_t conn_status; /*!< connection status */ + } close; /*!< HIDD callback param of ESP_HIDD_CLOSE_EVT */ + + /** + * @brief ESP_HIDD_SEND_REPORT_EVT + */ + struct hidd_send_report_evt_param { + esp_hidd_status_t status; /*!< operation status */ + uint8_t reason; /*!< lower layer failed reason(ref hiddefs.h) */ + esp_hidd_report_type_t report_type; /*!< report type */ + uint8_t report_id; /*!< report id */ + } send_report; /*!< HIDD callback param of ESP_HIDD_SEND_REPORT_EVT */ + + /** + * @brief ESP_HIDD_REPORT_ERR_EVT + */ + struct hidd_report_err_evt_param { + esp_hidd_status_t status; /*!< operation status */ + uint8_t reason; /*!< lower layer failed reason(ref hiddefs.h) */ + } report_err; /*!< HIDD callback param of ESP_HIDD_REPORT_ERR_EVT */ + + /** + * @brief ESP_HIDD_GET_REPORT_EVT + */ + struct hidd_get_report_evt_param { + esp_hidd_report_type_t report_type; /*!< report type */ + uint8_t report_id; /*!< report id */ + uint16_t buffer_size; /*!< buffer size */ + } get_report; /*!< HIDD callback param of ESP_HIDD_GET_REPORT_EVT */ + + /** + * @brief ESP_HIDD_SET_REPORT_EVT + */ + struct hidd_set_report_evt_param { + esp_hidd_report_type_t report_type; /*!< report type */ + uint8_t report_id; /*!< report id */ + uint16_t len; /*!< set_report data length */ + uint8_t *data; /*!< set_report data pointer */ + } set_report; /*!< HIDD callback param of ESP_HIDD_SET_REPORT_EVT */ + + /** + * @brief ESP_HIDD_SET_PROTOCOL_EVT + */ + struct hidd_set_protocol_evt_param { + esp_hidd_protocol_mode_t protocol_mode; /*!< protocol mode */ + } set_protocol; /*!< HIDD callback param of ESP_HIDD_SET_PROTOCOL_EVT */ + + /** + * @brief ESP_HIDD_INTR_DATA_EVT + */ + struct hidd_intr_data_evt_param { + uint8_t report_id; /*!< interrupt channel report id */ + uint16_t len; /*!< interrupt channel report data length */ + uint8_t *data; /*!< interrupt channel report data pointer */ + } intr_data; /*!< HIDD callback param of ESP_HIDD_INTR_DATA_EVT */ + + /** + * @brief ESP_HIDD_VC_UNPLUG_EVT + */ + struct hidd_vc_unplug_param { + esp_hidd_status_t status; /*!< operation status */ + esp_hidd_connection_state_t conn_status; /*!< connection status */ + } vc_unplug; /*!< HIDD callback param of ESP_HIDD_VC_UNPLUG_EVT */ +} esp_hidd_cb_param_t; + +/** + * @brief HID device callback function type. + * @param event: Event type + * @param param: Point to callback parameter, currently is union type + */ +typedef void (*esp_hd_cb_t)(esp_hidd_cb_event_t event, esp_hidd_cb_param_t *param); + +/** + * @brief This function is called to init callbacks with HID device module. + * + * @param[in] callback: pointer to the init callback function. + * + * @return + * - ESP_OK: success + * - other: failed + */ +esp_err_t esp_bt_hid_device_register_callback(esp_hd_cb_t callback); + +/** + * @brief Initializes HIDD interface. This function should be called after + * esp_bluedroid_init()/esp_bluedroid_init_with_cfg() and esp_bluedroid_enable() success, and should be + * called after esp_bt_hid_device_register_callback. When the operation is complete, the callback + * function will be called with ESP_HIDD_INIT_EVT. + * + * @return + * - ESP_OK: success + * - other: failed + */ +esp_err_t esp_bt_hid_device_init(void); + +/** + * @brief De-initializes HIDD interface. This function should be called after + * esp_bluedroid_init()/esp_bluedroid_init_with_cfg() and esp_bluedroid_enable() success, and should be + * called after esp_bt_hid_device_init(). When the operation is complete, the callback function will be + * called with ESP_HIDD_DEINIT_EVT. + * + * @return + * - ESP_OK: success + * - other: failed + */ +esp_err_t esp_bt_hid_device_deinit(void); + +/** + * @brief Registers HIDD parameters with SDP and sets l2cap Quality of Service. This function should be + * called after esp_bluedroid_init()/esp_bluedroid_init_with_cfg() and esp_bluedroid_enable() success, + * and should be called after esp_bt_hid_device_init(). When the operation is complete, the callback + * function will be called with ESP_HIDD_REGISTER_APP_EVT. + * + * @param[in] app_param: HIDD parameters + * @param[in] in_qos: incoming QoS parameters + * @param[in] out_qos: outgoing QoS parameters + * + * @return + * - ESP_OK: success + * - other: failed + */ +esp_err_t esp_bt_hid_device_register_app(esp_hidd_app_param_t *app_param, esp_hidd_qos_param_t *in_qos, + esp_hidd_qos_param_t *out_qos); + +/** + * @brief Removes HIDD parameters from SDP and resets l2cap Quality of Service. This function should be + * called after esp_bluedroid_init()/esp_bluedroid_init_with_cfg() and esp_bluedroid_enable() success, + * and should be called after esp_bt_hid_device_init(). When the operation is complete, the callback + * function will be called with ESP_HIDD_UNREGISTER_APP_EVT. + * + * @return + * - ESP_OK: success + * - other: failed + */ +esp_err_t esp_bt_hid_device_unregister_app(void); + +/** + * @brief Connects to the peer HID Host with virtual cable. This function should be called after + * esp_bluedroid_init()/esp_bluedroid_init_with_cfg() and esp_bluedroid_enable() success, and should be + * called after esp_bt_hid_device_init(). When the operation is complete, the callback function will + * be called with ESP_HIDD_OPEN_EVT. + * + * @param[in] bd_addr: Remote host bluetooth device address. + * + * @return + * - ESP_OK: success + * - other: failed + */ +esp_err_t esp_bt_hid_device_connect(esp_bd_addr_t bd_addr); + +/** + * @brief Disconnects from the currently connected HID Host. This function should be called after + * esp_bluedroid_init()/esp_bluedroid_init_with_cfg() and esp_bluedroid_enable() success, and should be + * called after esp_bt_hid_device_init(). When the operation is complete, the callback function will + * be called with ESP_HIDD_CLOSE_EVT. + * + * @note The disconnect operation will not remove the virtually cabled device. If the connect request from the + * different HID Host, it will reject the request. + * + * @return + * - ESP_OK: success + * - other: failed + */ +esp_err_t esp_bt_hid_device_disconnect(void); + +/** + * @brief Sends HID report to the currently connected HID Host. This function should be called after + * esp_bluedroid_init()/esp_bluedroid_init_with_cfg() and esp_bluedroid_enable() success, and should be + * called after esp_bt_hid_device_init(). When the operation is complete, the callback function will + * be called with ESP_HIDD_SEND_REPORT_EVT. + * + * @param[in] type: type of report + * @param[in] id: report id as defined by descriptor + * @param[in] len: length of report + * @param[in] data: report data + * + * @return + * - ESP_OK: success + * - other: failed + */ +esp_err_t esp_bt_hid_device_send_report(esp_hidd_report_type_t type, uint8_t id, uint16_t len, uint8_t *data); + +/** + * @brief Sends HID Handshake with error info for invalid set_report to the currently connected HID Host. + * This function should be called after esp_bluedroid_init()/esp_bluedroid_init_with_cfg() and + * esp_bluedroid_enable() success, and should be called after esp_bt_hid_device_init(). When the + * operation is complete, the callback function will be called with ESP_HIDD_REPORT_ERR_EVT. + * + * @param[in] error: type of error + * + * @return + * - ESP_OK: success + * - other: failed + */ +esp_err_t esp_bt_hid_device_report_error(esp_hidd_handshake_error_t error); + +/** + * @brief Remove the virtually cabled device. This function should be called after + * esp_bluedroid_init()/esp_bluedroid_init_with_cfg() and esp_bluedroid_enable() success, and should be + * called after esp_bt_hid_device_init(). When the operation is complete, the callback function will be + * called with ESP_HIDD_VC_UNPLUG_EVT. + * + * @note If the connection exists, then HID Device will send a `VIRTUAL_CABLE_UNPLUG` control command to + * the peer HID Host, and the connection will be destroyed. If the connection does not exist, then HID + * Device will only unplug on it's single side. Once the unplug operation is success, the related + * pairing and bonding information will be removed, then the HID Device can accept connection request + * from the different HID Host, + * + * @return - ESP_OK: success + * - other: failed + */ +esp_err_t esp_bt_hid_device_virtual_cable_unplug(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/bt/host/bluedroid/api/include/api/esp_hidh_api.h b/lib/bt/host/bluedroid/api/include/api/esp_hidh_api.h new file mode 100644 index 00000000..46f8a15b --- /dev/null +++ b/lib/bt/host/bluedroid/api/include/api/esp_hidh_api.h @@ -0,0 +1,482 @@ +/* + * SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + * + * SPDX-FileContributor: Blake Felt + */ + +#ifndef __ESP_HIDH_API_H__ +#define __ESP_HIDH_API_H__ + +#include "esp_bt_defs.h" +#include "esp_err.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/// maximum size of HID Device report descriptor +#define BTHH_MAX_DSC_LEN 884 + +/** + * @brief HID host connection state + */ +typedef enum { + ESP_HIDH_CONN_STATE_CONNECTED = 0, /*!< connected state */ + ESP_HIDH_CONN_STATE_CONNECTING, /*!< connecting state */ + ESP_HIDH_CONN_STATE_DISCONNECTED, /*!< disconnected state */ + ESP_HIDH_CONN_STATE_DISCONNECTING, /*!< disconnecting state */ + ESP_HIDH_CONN_STATE_UNKNOWN /*!< unknown state (initial state) */ +} esp_hidh_connection_state_t; + +/** + * @brief HID handshake error code and vendor-defined result code + */ +typedef enum { + ESP_HIDH_OK, /*!< successful */ + ESP_HIDH_HS_HID_NOT_READY, /*!< handshake error: device not ready */ + ESP_HIDH_HS_INVALID_RPT_ID, /*!< handshake error: invalid report ID */ + ESP_HIDH_HS_TRANS_NOT_SPT, /*!< handshake error: HID device does not support the request */ + ESP_HIDH_HS_INVALID_PARAM, /*!< handshake error: parameter value does not meet the expected criteria of called function or API */ + ESP_HIDH_HS_ERROR, /*!< handshake error: HID device could not identify the error condition */ + ESP_HIDH_ERR, /*!< general ESP HID Host error */ + ESP_HIDH_ERR_SDP, /*!< SDP error */ + ESP_HIDH_ERR_PROTO, /*!< SET_PROTOCOL error, only used in ESP_HIDH_OPEN_EVT callback */ + ESP_HIDH_ERR_DB_FULL, /*!< device database full, used in ESP_HIDH_OPEN_EVT/ESP_HIDH_ADD_DEV_EVT */ + ESP_HIDH_ERR_TOD_UNSPT, /*!< type of device not supported */ + ESP_HIDH_ERR_NO_RES, /*!< out of system resources */ + ESP_HIDH_ERR_AUTH_FAILED, /*!< authentication fail */ + ESP_HIDH_ERR_HDL, /*!< connection handle error */ + ESP_HIDH_ERR_SEC, /*!< encryption error */ + ESP_HIDH_BUSY, /*!< vendor-defined: temporarily can not handle this request */ + ESP_HIDH_NO_DATA, /*!< vendor-defined: no data. */ + ESP_HIDH_NEED_INIT, /*!< vendor-defined: HIDH module shall initialize first */ + ESP_HIDH_NEED_DEINIT, /*!< vendor-defined: HIDH module shall de-deinitialize first */ + ESP_HIDH_NO_CONNECTION, /*!< vendor-defined: connection may have been closed */ +} esp_hidh_status_t; + +/** + * @brief HID host protocol modes + */ +typedef enum { + ESP_HIDH_BOOT_MODE = 0x00, /*!< boot protocol mode */ + ESP_HIDH_REPORT_MODE = 0x01, /*!< report protocol mode */ + ESP_HIDH_UNSUPPORTED_MODE = 0xff /*!< unsupported protocol mode */ +} esp_hidh_protocol_mode_t; + +/** + * @brief HID host report types + */ +typedef enum { + ESP_HIDH_REPORT_TYPE_OTHER = 0, /*!< unsupported report type */ + ESP_HIDH_REPORT_TYPE_INPUT, /*!< input report type */ + ESP_HIDH_REPORT_TYPE_OUTPUT, /*!< output report type */ + ESP_HIDH_REPORT_TYPE_FEATURE, /*!< feature report type */ +} esp_hidh_report_type_t; + +/** + * @brief HID host callback function events + */ +typedef enum { + ESP_HIDH_INIT_EVT = 0, /*!< when HID host is initialized, the event comes */ + ESP_HIDH_DEINIT_EVT, /*!< when HID host is deinitialized, the event comes */ + ESP_HIDH_OPEN_EVT, /*!< when HID host connection opened, the event comes */ + ESP_HIDH_CLOSE_EVT, /*!< when HID host connection closed, the event comes */ + ESP_HIDH_GET_RPT_EVT, /*!< when Get_Report command is called, the event comes */ + ESP_HIDH_SET_RPT_EVT, /*!< when Set_Report command is called, the event comes */ + ESP_HIDH_GET_PROTO_EVT, /*!< when Get_Protocol command is called, the event comes */ + ESP_HIDH_SET_PROTO_EVT, /*!< when Set_Protocol command is called, the event comes */ + ESP_HIDH_GET_IDLE_EVT, /*!< when Get_Idle command is called, the event comes */ + ESP_HIDH_SET_IDLE_EVT, /*!< when Set_Idle command is called, the event comes */ + ESP_HIDH_GET_DSCP_EVT, /*!< when HIDH is initialized, the event comes */ + ESP_HIDH_ADD_DEV_EVT, /*!< when a device is added, the event comes */ + ESP_HIDH_RMV_DEV_EVT, /*!< when a device is removed, the event comes */ + ESP_HIDH_VC_UNPLUG_EVT, /*!< when virtually unplugged, the event comes */ + ESP_HIDH_DATA_EVT, /*!< when send data on interrupt channel, the event comes */ + ESP_HIDH_DATA_IND_EVT, /*!< when receive data on interrupt channel, the event comes */ + ESP_HIDH_SET_INFO_EVT /*!< when set the HID device descriptor, the event comes */ +} esp_hidh_cb_event_t; + +/** + * @brief HID device information from HID Device Service Record and Device ID Service Record + */ +typedef enum { + ESP_HIDH_DEV_ATTR_VIRTUAL_CABLE = 0x0001, /*!< whether Virtual Cables is supported */ + ESP_HIDH_DEV_ATTR_NORMALLY_CONNECTABLE = 0x0002, /*!< whether device is in Page Scan mode when there is no active connection */ + ESP_HIDH_DEV_ATTR_RECONNECT_INITIATE = 0x0004, /*!< whether the HID device inititates the reconnection process */ +} esp_hidh_dev_attr_t; + +/** + * @brief application ID(non-zero) for each type of device + */ +typedef enum { + ESP_HIDH_APP_ID_MOUSE = 1, /*!< pointing device */ + ESP_HIDH_APP_ID_KEYBOARD = 2, /*!< keyboard */ + ESP_HIDH_APP_ID_REMOTE_CONTROL = 3, /*!< remote control */ + ESP_HIDH_APP_ID_JOYSTICK = 5, /*!< joystick */ + ESP_HIDH_APP_ID_GAMEPAD = 6, /*!< gamepad*/ +} esp_hidh_dev_app_id_t; + +/** + * @brief HID device information from HID Device Service Record and Device ID Service Record + */ +typedef struct { + int attr_mask; /*!< device attribute bit mask, refer to esp_hidh_dev_attr_t */ + uint8_t sub_class; /*!< HID device subclass */ + uint8_t app_id; /*!< application ID, refer to esp_hidh_dev_app_id_t */ + int vendor_id; /*!< Device ID information: vendor ID */ + int product_id; /*!< Device ID information: product ID */ + int version; /*!< Device ID information: version */ + uint8_t ctry_code; /*!< SDP attrbutes of HID devices: HID country code (https://www.usb.org/sites/default/files/hid1_11.pdf) */ + int dl_len; /*!< SDP attrbutes of HID devices: HID device descriptor length */ + uint8_t dsc_list[BTHH_MAX_DSC_LEN]; /*!< SDP attrbutes of HID devices: HID device descriptor definition */ +} esp_hidh_hid_info_t; + +/** + * @brief HID host callback parameters union + */ +typedef union { + /** + * @brief ESP_HIDH_INIT_EVT + */ + struct hidh_init_evt_param { + esp_hidh_status_t status; /*!< status */ + } init; /*!< HIDH callback param of ESP_HIDH_INIT_EVT */ + + /** + * @brief ESP_HIDH_DEINIT_EVT + */ + struct hidh_uninit_evt_param { + esp_hidh_status_t status; /*!< status */ + } deinit; /*!< HIDH callback param of ESP_HIDH_DEINIT_EVT */ + + /** + * @brief ESP_HIDH_OPEN_EVT + */ + struct hidh_open_evt_param { + esp_hidh_status_t status; /*!< operation status */ + esp_hidh_connection_state_t conn_status; /*!< connection status */ + bool is_orig; /*!< indicate if host intiate the connection */ + uint8_t handle; /*!< device handle */ + esp_bd_addr_t bd_addr; /*!< device address */ + } open; /*!< HIDH callback param of ESP_HIDH_OPEN_EVT */ + + /** + * @brief ESP_HIDH_CLOSE_EVT + */ + struct hidh_close_evt_param { + esp_hidh_status_t status; /*!< operation status */ + uint8_t reason; /*!< lower layer failed reason(ref hiddefs.h) */ + esp_hidh_connection_state_t conn_status; /*!< connection status */ + uint8_t handle; /*!< device handle */ + } close; /*!< HIDH callback param of ESP_HIDH_CLOSE_EVT */ + + /** + * @brief ESP_HIDH_VC_UNPLUG_EVT + */ + struct hidh_unplug_evt_param { + esp_hidh_status_t status; /*!< operation status */ + esp_hidh_connection_state_t conn_status; /*!< connection status */ + uint8_t handle; /*!< device handle */ + } unplug; /*!< HIDH callback param of ESP_HIDH_VC_UNPLUG_EVT */ + + /** + * @brief ESP_HIDH_GET_PROTO_EVT + */ + struct hidh_get_proto_evt_param { + esp_hidh_status_t status; /*!< operation status */ + uint8_t handle; /*!< device handle */ + esp_hidh_protocol_mode_t proto_mode; /*!< protocol mode */ + } get_proto; /*!< HIDH callback param of ESP_HIDH_GET_PROTO_EVT */ + + /** + * @brief ESP_HIDH_SET_PROTO_EVT + */ + struct hidh_set_proto_evt_param { + esp_hidh_status_t status; /*!< operation status */ + uint8_t handle; /*!< device handle */ + } set_proto; /*!< HIDH callback param of ESP_HIDH_SET_PROTO_EVT */ + + /** + * @brief ESP_HIDH_GET_RPT_EVT + */ + struct hidh_get_rpt_evt_param { + esp_hidh_status_t status; /*!< operation status */ + uint8_t handle; /*!< device handle */ + uint16_t len; /*!< data length */ + uint8_t *data; /*!< data pointer */ + } get_rpt; /*!< HIDH callback param of ESP_HIDH_GET_RPT_EVT */ + + /** + * @brief ESP_HIDH_SET_RPT_EVT + */ + struct hidh_set_rpt_evt_param { + esp_hidh_status_t status; /*!< operation status */ + uint8_t handle; /*!< device handle */ + } set_rpt; /*!< HIDH callback param of ESP_HIDH_SET_RPT_EVT */ + + /** + * @brief ESP_HIDH_DATA_EVT + */ + struct hidh_send_data_evt_param { + esp_hidh_status_t status; /*!< operation status */ + uint8_t handle; /*!< device handle */ + uint8_t reason; /*!< lower layer failed reason(ref hiddefs.h) */ + } send_data; /*!< HIDH callback param of ESP_HIDH_DATA_EVT */ + + /** + * @brief ESP_HIDH_GET_IDLE_EVT + */ + struct hidh_get_idle_evt_param { + esp_hidh_status_t status; /*!< operation status */ + uint8_t handle; /*!< device handle */ + uint8_t idle_rate; /*!< idle rate */ + } get_idle; /*!< HIDH callback param of ESP_HIDH_GET_IDLE_EVT */ + + /** + * @brief ESP_HIDH_SET_IDLE_EVT + */ + struct hidh_set_idle_evt_param { + esp_hidh_status_t status; /*!< operation status */ + uint8_t handle; /*!< device handle */ + } set_idle; /*!< HIDH callback param of ESP_HIDH_SET_IDLE_EVT */ + + /** + * @brief ESP_HIDH_DATA_IND_EVT + */ + struct hidh_data_ind_evt_param { + esp_hidh_status_t status; /*!< operation status */ + uint8_t handle; /*!< device handle */ + esp_hidh_protocol_mode_t proto_mode; /*!< protocol mode */ + uint16_t len; /*!< data length */ + uint8_t *data; /*!< data pointer */ + } data_ind; /*!< HIDH callback param of ESP_HIDH_DATA_IND_EVT */ + + /** + * @brief ESP_HIDH_ADD_DEV_EVT + */ + struct hidh_add_dev_evt_param { + esp_hidh_status_t status; /*!< operation status */ + uint8_t handle; /*!< device handle */ + esp_bd_addr_t bd_addr; /*!< device address */ + } add_dev; /*!< HIDH callback param of ESP_HIDH_ADD_DEV_EVT */ + + /** + * @brief ESP_HIDH_RMV_DEV_EVT + */ + struct hidh_rmv_dev_evt_param { + esp_hidh_status_t status; /*!< operation status */ + uint8_t handle; /*!< device handle */ + esp_bd_addr_t bd_addr; /*!< device address */ + } rmv_dev; /*!< HIDH callback param of ESP_HIDH_RMV_DEV_EVT */ + + /** + * @brief ESP_HIDH_GET_DSCP_EVT + */ + struct hidh_get_dscp_evt_param { + esp_hidh_status_t status; /*!< operation status */ + uint8_t handle; /*!< device handle */ + bool added; /*!< Indicate if added */ + uint16_t vendor_id; /*!< Vendor ID */ + uint16_t product_id; /*!< Product ID */ + uint16_t version; /*!< Version */ + uint16_t ssr_max_latency; /*!< SSR max latency in slots */ + uint16_t ssr_min_tout; /*!< SSR min timeout in slots */ + uint8_t ctry_code; /*!< Country Code */ + uint16_t dl_len; /*!< Device descriptor length */ + uint8_t *dsc_list; /*!< Device descriptor pointer */ + } dscp; /*!< HIDH callback param of ESP_HIDH_GET_DSCP_EVT */ + + /** + * @brief ESP_HIDH_SET_INFO_EVT + */ + struct hidh_set_info_evt_param { + esp_hidh_status_t status; /*!< operation status */ + uint8_t handle; /*!< device handle */ + esp_bd_addr_t bd_addr; /*!< device address */ + } set_info; /*!< HIDH callback param of ESP_HIDH_SET_INFO_EVT */ +} esp_hidh_cb_param_t; + +/** + * @brief HID host callback function type + * @param event: Event type + * @param param: Point to callback parameter, currently is union type + */ +typedef void (*esp_hh_cb_t)(esp_hidh_cb_event_t event, esp_hidh_cb_param_t *param); + +/** + * @brief This function is called to init callbacks with HID host module. + * + * @param[in] callback: pointer to the init callback function. + * + * @return + * - ESP_OK: success + * - other: failed + */ +esp_err_t esp_bt_hid_host_register_callback(esp_hh_cb_t callback); + +/** + * @brief This function initializes HID host. This function should be called after esp_bluedroid_enable() and + * esp_bluedroid_init()/esp_bluedroid_init_with_cfg() success, and should be called after + * esp_bt_hid_host_register_callback(). When the operation is complete the callback function will be called + * with ESP_HIDH_INIT_EVT. + * + * @return + * - ESP_OK: success + * - other: failed + */ +esp_err_t esp_bt_hid_host_init(void); + +/** + * @brief Closes the interface. This function should be called after esp_bluedroid_enable() and + * esp_bluedroid_init()/esp_bluedroid_init_with_cfg() success, and should be called after esp_bt_hid_host_init(). + * When the operation is complete the callback function will be called with ESP_HIDH_DEINIT_EVT. + * + * @return - ESP_OK: success + * - other: failed + */ +esp_err_t esp_bt_hid_host_deinit(void); + +/** + * @brief Connect to HID device. When the operation is complete the callback + * function will be called with ESP_HIDH_OPEN_EVT. + * + * @param[in] bd_addr: Remote device bluetooth device address. + * + * @return - ESP_OK: success + * - other: failed + */ +esp_err_t esp_bt_hid_host_connect(esp_bd_addr_t bd_addr); + +/** + * @brief Disconnect from HID device. When the operation is complete the callback + * function will be called with ESP_HIDH_CLOSE_EVT. + * + * @param[in] bd_addr: Remote device bluetooth device address. + * + * @return - ESP_OK: success + * - other: failed + */ +esp_err_t esp_bt_hid_host_disconnect(esp_bd_addr_t bd_addr); + +/** + * @brief Virtual UnPlug (VUP) the specified HID device. When the operation is complete the callback + * function will be called with ESP_HIDH_VC_UNPLUG_EVT. + * + * @param[in] bd_addr: Remote device bluetooth device address. + * + * @return - ESP_OK: success + * - other: failed + */ +esp_err_t esp_bt_hid_host_virtual_cable_unplug(esp_bd_addr_t bd_addr); + +/** + * @brief Set the HID device descriptor for the specified HID device. When the operation is complete the callback + * function will be called with ESP_HIDH_SET_INFO_EVT. + * + * @param[in] bd_addr: Remote device bluetooth device address. + * @param[in] hid_info: HID device descriptor structure. + * + * @return - ESP_OK: success + * - other: failed + */ +esp_err_t esp_bt_hid_host_set_info(esp_bd_addr_t bd_addr, esp_hidh_hid_info_t *hid_info); + +/** + * @brief Get the HID proto mode. When the operation is complete the callback + * function will be called with ESP_HIDH_GET_PROTO_EVT. + * + * @param[in] bd_addr: Remote device bluetooth device address. + * + * @return + * - ESP_OK: success + * - other: failed + */ +esp_err_t esp_bt_hid_host_get_protocol(esp_bd_addr_t bd_addr); + +/** + * @brief Set the HID proto mode. When the operation is complete the callback + * function will be called with ESP_HIDH_SET_PROTO_EVT. + * + * @param[in] bd_addr: Remote device bluetooth device address. + * @param[in] protocol_mode: Protocol mode type. + * + * @return + * - ESP_OK: success + * - other: failed + */ +esp_err_t esp_bt_hid_host_set_protocol(esp_bd_addr_t bd_addr, esp_hidh_protocol_mode_t protocol_mode); + +/** + * @brief Get the HID Idle Time. When the operation is complete the callback + * function will be called with ESP_HIDH_GET_IDLE_EVT. + * + * @param[in] bd_addr: Remote device bluetooth device address. + * + * @return + * - ESP_OK: success + * - other: failed + */ +esp_err_t esp_bt_hid_host_get_idle(esp_bd_addr_t bd_addr); + +/** + * @brief Set the HID Idle Time. When the operation is complete the callback + * function will be called with ESP_HIDH_SET_IDLE_EVT. + * + * @param[in] bd_addr: Remote device bluetooth device address. + * @param[in] idle_time: Idle time rate + * + * @return - ESP_OK: success + * - other: failed + */ +esp_err_t esp_bt_hid_host_set_idle(esp_bd_addr_t bd_addr, uint16_t idle_time); + +/** + * @brief Send a GET_REPORT to HID device. When the operation is complete the callback + * function will be called with ESP_HIDH_GET_RPT_EVT. + * + * @param[in] bd_addr: Remote device bluetooth device address. + * @param[in] report_type: Report type + * @param[in] report_id: Report id + * @param[in] buffer_size: Buffer size + * + * @return - ESP_OK: success + * - other: failed + */ +esp_err_t esp_bt_hid_host_get_report(esp_bd_addr_t bd_addr, esp_hidh_report_type_t report_type, uint8_t report_id, + int buffer_size); + +/** + * @brief Send a SET_REPORT to HID device. When the operation is complete the callback + * function will be called with ESP_HIDH_SET_RPT_EVT. + * + * @param[in] bd_addr: Remote device bluetooth device address. + * @param[in] report_type: Report type + * @param[in] report: Report data pointer + * @param[in] len: Report data length + * + * @return - ESP_OK: success + * - other: failed + */ +esp_err_t esp_bt_hid_host_set_report(esp_bd_addr_t bd_addr, esp_hidh_report_type_t report_type, uint8_t *report, + size_t len); + +/** + * @brief Send data to HID device. When the operation is complete the callback + * function will be called with ESP_HIDH_DATA_EVT. + * + * @param[in] bd_addr: Remote device bluetooth device address. + * @param[in] data: Data pointer + * @param[in] len: Data length + * + * @return - ESP_OK: success + * - other: failed + */ +esp_err_t esp_bt_hid_host_send_data(esp_bd_addr_t bd_addr, uint8_t *data, size_t len); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/bt/host/bluedroid/api/include/api/esp_l2cap_bt_api.h b/lib/bt/host/bluedroid/api/include/api/esp_l2cap_bt_api.h new file mode 100644 index 00000000..f11c932f --- /dev/null +++ b/lib/bt/host/bluedroid/api/include/api/esp_l2cap_bt_api.h @@ -0,0 +1,251 @@ +/* + * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __ESP_L2CAP_BT_API_H__ +#define __ESP_L2CAP_BT_API_H__ + +#include "esp_err.h" +#include "esp_bt_defs.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief L2CAP operation success and failure codes + */ +typedef enum { + ESP_BT_L2CAP_SUCCESS = 0, /*!< Successful operation. */ + ESP_BT_L2CAP_FAILURE, /*!< Generic failure. */ + ESP_BT_L2CAP_BUSY, /*!< Temporarily can not handle this request. */ + ESP_BT_L2CAP_NO_RESOURCE, /*!< No more resource */ + ESP_BT_L2CAP_NEED_INIT, /*!< L2CAP module shall init first */ + ESP_BT_L2CAP_NEED_DEINIT, /*!< L2CAP module shall deinit first */ + ESP_BT_L2CAP_NO_CONNECTION, /*!< Connection may have been closed */ + ESP_BT_L2CAP_NO_SERVER, /*!< No server */ +} esp_bt_l2cap_status_t; + +/** + * @brief Security Setting Mask. Use these three mask mode: + * 1. ESP_BT_L2CAP_SEC_NONE + * 2. ESP_BT_L2CAP_SEC_AUTHENTICATE + * 3. (ESP_BT_L2CAP_SEC_ENCRYPT|ESP_BT_L2CAP_SEC_AUTHENTICATE) + */ +#define ESP_BT_L2CAP_SEC_NONE 0x0000 /*!< No security */ +#define ESP_BT_L2CAP_SEC_AUTHORIZE 0x0001 /*!< Authorization required */ +#define ESP_BT_L2CAP_SEC_AUTHENTICATE 0x0012 /*!< Authentication required */ +#define ESP_BT_L2CAP_SEC_ENCRYPT 0x0024 /*!< Encryption required */ +typedef uint32_t esp_bt_l2cap_cntl_flags_t; + +/** + * @brief L2CAP callback function events + */ +typedef enum { + ESP_BT_L2CAP_INIT_EVT = 0, /*!< When L2CAP is initialized, the event comes */ + ESP_BT_L2CAP_UNINIT_EVT = 1, /*!< When L2CAP is deinitialized, the event comes */ + ESP_BT_L2CAP_OPEN_EVT = 16, /*!< When L2CAP Client connection open, the event comes */ + ESP_BT_L2CAP_CLOSE_EVT = 17, /*!< When L2CAP connection closed, the event comes */ + ESP_BT_L2CAP_START_EVT = 18, /*!< When L2CAP server started, the event comes */ + ESP_BT_L2CAP_CL_INIT_EVT = 19, /*!< When L2CAP client initiated a connection, the event comes */ + ESP_BT_L2CAP_SRV_STOP_EVT = 36, /*!< When L2CAP server stopped, the event comes */ +} esp_bt_l2cap_cb_event_t; + +/** + * @brief L2CAP callback parameters union + */ +typedef union { + /** + * @brief ESP_BT_L2CAP_INIT_EVT + */ + struct l2cap_init_evt_param { + esp_bt_l2cap_status_t status; /*!< status */ + } init; /*!< L2CAP callback param of ESP_BT_L2CAP_INIT_EVT */ + + /** + * @brief ESP_BT_L2CAP_UNINIT_EVT + */ + struct l2cap_uninit_evt_param { + esp_bt_l2cap_status_t status; /*!< status */ + } uninit; /*!< L2CAP callback param of ESP_BT_L2CAP_UNINIT_EVT */ + + /** + * @brief ESP_BT_L2CAP_OPEN_EVT + */ + struct l2cap_open_evt_param { + esp_bt_l2cap_status_t status; /*!< status */ + uint32_t handle; /*!< The connection handle */ + int fd; /*!< File descriptor */ + esp_bd_addr_t rem_bda; /*!< The peer address */ + int32_t tx_mtu; /*!< The transmit MTU */ + } open; /*!< L2CAP callback param of ESP_BT_L2CAP_OPEN_EVT */ + + /** + * @brief ESP_BT_L2CAP_CLOSE_EVT + */ + struct l2cap_close_evt_param { + esp_bt_l2cap_status_t status; /*!< status */ + uint32_t handle; /*!< The connection handle */ + bool async; /*!< FALSE, if local initiates disconnect */ + } close; /*!< L2CAP callback param of ESP_BT_L2CAP_CLOSE_EVT */ + + /** + * @brief ESP_BT_L2CAP_START_EVT + */ + struct l2cap_start_evt_param { + esp_bt_l2cap_status_t status; /*!< status */ + uint32_t handle; /*!< The connection handle */ + uint8_t sec_id; /*!< security ID used by this server */ + } start; /*!< L2CAP callback param of ESP_BT_L2CAP_START_EVT */ + + /** + * @brief ESP_BT_L2CAP_CL_INIT_EVT + */ + struct l2cap_cl_init_evt_param { + esp_bt_l2cap_status_t status; /*!< status */ + uint32_t handle; /*!< The connection handle */ + uint8_t sec_id; /*!< security ID used by this server */ + } cl_init; /*!< L2CAP callback param of ESP_BT_L2CAP_CL_INIT_EVT */ + + /** + * @brief ESP_BT_L2CAP_SRV_STOP_EVT + */ + struct l2cap_srv_stop_evt_param { + esp_bt_l2cap_status_t status; /*!< status */ + uint8_t psm; /*!< local psm */ + } srv_stop; /*!< L2CAP callback param of ESP_BT_L2CAP_SRV_STOP_EVT */ + +} esp_bt_l2cap_cb_param_t; + +/** + * @brief L2CAP callback function type. + * + * @param event: Event type + * @param param: Point to callback parameter, currently is union type + */ +typedef void (* esp_bt_l2cap_cb_t)(esp_bt_l2cap_cb_event_t event, esp_bt_l2cap_cb_param_t *param); + +/** + * @brief This function is called to init callbacks with L2CAP module. + * + * @param[in] callback: pointer to the init callback function. + * + * @return + * - ESP_OK: success + * - other: failed + */ +esp_err_t esp_bt_l2cap_register_callback(esp_bt_l2cap_cb_t callback); + +/** + * @brief This function is called to init L2CAP module. + * When the operation is completed, the callback function will be called with ESP_BT_L2CAP_INIT_EVT. + * This function should be called after esp_bluedroid_enable() completes successfully. + * + * @return + * - ESP_OK: success + * - other: failed + */ +esp_err_t esp_bt_l2cap_init(void); + +/** + * @brief This function is called to uninit l2cap module. + * The operation will close all active L2CAP connection first, then the callback function will be called + * with ESP_BT_L2CAP_CLOSE_EVT, and the number of ESP_BT_L2CAP_CLOSE_EVT is equal to the number of connection. + * When the operation is completed, the callback function will be called with ESP_BT_L2CAP_UNINIT_EVT. + * This function should be called after esp_bt_l2cap_init() completes successfully. + * + * @return + * - ESP_OK: success + * - other: failed + */ +esp_err_t esp_bt_l2cap_deinit(void); + +/** + * @brief This function makes an L2CAP connection to a remote BD Address. + * When the connection is initiated or failed to initiate, the callback is called with ESP_BT_L2CAP_CL_INIT_EVT. + * When the connection is established or failed, the callback is called with ESP_BT_L2CAP_OPEN_EVT. + * This function must be called after esp_bt_l2cap_init() successful and before esp_bt_l2cap_deinit(). + * + * @param[in] cntl_flag: Lower 16-bit security settings mask. + * @param[in] remote_psm: Remote device bluetooth Profile PSM. + * @param[in] peer_bd_addr: Remote device bluetooth device address. + * + * @return + * - ESP_OK: success + * - other: failed + */ +esp_err_t esp_bt_l2cap_connect(esp_bt_l2cap_cntl_flags_t cntl_flag, uint16_t remote_psm, esp_bd_addr_t peer_bd_addr); + +/** + * @brief This function create a L2CAP server and starts listening for an + * L2CAP connection request from a remote Bluetooth device. + * When the server is started successfully, the callback is called with ESP_BT_L2CAP_START_EVT. + * When the connection is established, the callback is called with ESP_BT_L2CAP_OPEN_EVT. + * This function must be called after esp_bt_l2cap_init() successful and before esp_bt_l2cap_deinit(). + * + * @param[in] cntl_flag: Lower 16-bit security settings mask. + * @param[in] local_psm: Dynamic PSM. + * + * @return + * - ESP_OK: success + * - other: failed + */ +esp_err_t esp_bt_l2cap_start_srv(esp_bt_l2cap_cntl_flags_t cntl_flag, uint16_t local_psm); + +/** + * @brief This function stops all L2CAP servers. + * The operation will close all active L2CAP connection first, then the callback function will be called + * with ESP_BT_L2CAP_CLOSE_EVT, and the number of ESP_BT_L2CAP_CLOSE_EVT is equal to the number of connection. + * When the operation is completed, the callback is called with ESP_BT_L2CAP_SRV_STOP_EVT. + * This function must be called after esp_bt_l2cap_init() successful and before esp_bt_l2cap_deinit(). + * + * @return + * - ESP_OK: success + * - other: failed + */ + +esp_err_t esp_bt_l2cap_stop_all_srv(void); + +/** + * @brief This function stops a specific L2CAP server. + * The operation will close all active L2CAP connection first on the specific L2CAP server, then the callback function will + * be called with ESP_BT_L2CAP_CLOSE_EVT, and the number of ESP_BT_L2CAP_CLOSE_EVT is equal to the number of connection. + * When the operation is completed, the callback is called with ESP_BT_L2CAP_SRV_STOP_EVT. + * This function must be called after esp_bt_l2cap_init() successful and before esp_bt_l2cap_deinit(). + * + * @param[in] local_psm: Dynamic PSM. + * + * @return + * - ESP_OK: success + * - other: failed + */ +esp_err_t esp_bt_l2cap_stop_srv(uint16_t local_psm); + +/** + * @brief This function is used to register VFS. + * Only supports write, read and close. + * This function must be called after esp_bt_l2cap_init() successful and before esp_bt_l2cap_deinit(). + * + * @return + * - ESP_OK: success + * - other: failed + */ +esp_err_t esp_bt_l2cap_vfs_register(void); + +/** + * @brief This function is used to unregister VFS. + * This function must be called after esp_bt_l2cap_init() successful and before esp_bt_l2cap_deinit(). + * + * @return + * - ESP_OK: success + * - other: failed + */ +esp_err_t esp_bt_l2cap_vfs_unregister(void); + +#ifdef __cplusplus +} +#endif + +#endif ///__ESP_L2CAP_BT_API_H__ diff --git a/lib/bt/host/bluedroid/api/include/api/esp_sdp_api.h b/lib/bt/host/bluedroid/api/include/api/esp_sdp_api.h new file mode 100644 index 00000000..14741e74 --- /dev/null +++ b/lib/bt/host/bluedroid/api/include/api/esp_sdp_api.h @@ -0,0 +1,271 @@ +/* + * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __ESP_SDP_API_H__ +#define __ESP_SDP_API_H__ + +#include "esp_err.h" +#include "esp_bt_defs.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define ESP_SDP_SERVER_NAME_MAX 32 /*!< Service name max length */ +#define SDP_OPP_SUPPORTED_FORMATS_MAX_LENGTH 15 /*!< OPP supported format list maximum length */ + +typedef enum { + ESP_SDP_SUCCESS = 0, /*!< Successful operation. */ + ESP_SDP_FAILURE, /*!< Generic failure. */ + ESP_SDP_NO_RESOURCE, /*!< No more resource */ + ESP_SDP_NEED_INIT, /*!< SDP module shall init first */ + ESP_SDP_NEED_DEINIT, /*!< SDP module shall deinit first */ + ESP_SDP_NO_CREATE_RECORD, /*!< No record created */ +} esp_sdp_status_t; + +/** + * @brief SDP callback function events + */ +typedef enum { + ESP_SDP_INIT_EVT = 0, /*!< When SDP is initialized, the event comes */ + ESP_SDP_DEINIT_EVT = 1, /*!< When SDP is deinitialized, the event comes */ + ESP_SDP_SEARCH_COMP_EVT = 2, /*!< When SDP search complete, the event comes */ + ESP_SDP_CREATE_RECORD_COMP_EVT = 3, /*!< When create SDP records complete, the event comes */ + ESP_SDP_REMOVE_RECORD_COMP_EVT = 4, /*!< When remove a SDP record complete, the event comes */ +} esp_sdp_cb_event_t; + +/** + * @brief SDP record type + */ +typedef enum { + ESP_SDP_TYPE_RAW, /*!< Used to carry raw SDP search data for unknown UUIDs */ + ESP_SDP_TYPE_MAP_MAS, /*!< Message Access Profile - Server */ + ESP_SDP_TYPE_MAP_MNS, /*!< Message Access Profile - Client (Notification Server) */ + ESP_SDP_TYPE_PBAP_PSE, /*!< Phone Book Profile - Server */ + ESP_SDP_TYPE_PBAP_PCE, /*!< Phone Book Profile - Client */ + ESP_SDP_TYPE_OPP_SERVER, /*!< Object Push Profile */ + ESP_SDP_TYPE_SAP_SERVER /*!< SIM Access Profile */ +} esp_bluetooth_sdp_types_t; + +/** + * @brief Some signals need additional pointers, hence we introduce a + * generic way to handle these pointers. + */ +typedef struct bluetooth_sdp_hdr_overlay { + esp_bluetooth_sdp_types_t type; /*!< SDP type */ + esp_bt_uuid_t uuid; /*!< UUID type, include uuid and uuid length */ + uint32_t service_name_length; /*!< Service name length */ + char *service_name; /*!< service name */ + int32_t rfcomm_channel_number; /*!< rfcomm channel number, if not used set to -1*/ + int32_t l2cap_psm; /*!< l2cap psm, if not used set to -1 */ + int32_t profile_version; /*!< profile version */ + + // User pointers, only used for some signals - see esp_bluetooth_sdp_ops_record_t + int user1_ptr_len; /*!< see esp_bluetooth_sdp_ops_record_t */ + uint8_t *user1_ptr; /*!< see esp_bluetooth_sdp_ops_record_t */ + int user2_ptr_len; /*!< see esp_bluetooth_sdp_ops_record_t */ + uint8_t *user2_ptr; /*!< see esp_bluetooth_sdp_ops_record_t */ +} esp_bluetooth_sdp_hdr_overlay_t; + +/** + * @brief Message Access Profile - Server parameters + */ +typedef struct bluetooth_sdp_mas_record { + esp_bluetooth_sdp_hdr_overlay_t hdr; /*!< General info */ + uint32_t mas_instance_id; /*!< MAS Instance ID */ + uint32_t supported_features; /*!< Map supported features */ + uint32_t supported_message_types; /*!< Supported message types */ +} esp_bluetooth_sdp_mas_record_t; + +/** + * @brief Message Access Profile - Client (Notification Server) parameters + */ +typedef struct bluetooth_sdp_mns_record { + esp_bluetooth_sdp_hdr_overlay_t hdr; /*!< General info */ + uint32_t supported_features; /*!< Supported features */ +} esp_bluetooth_sdp_mns_record_t; + +/** + * @brief Phone Book Profile - Server parameters + */ +typedef struct bluetooth_sdp_pse_record { + esp_bluetooth_sdp_hdr_overlay_t hdr; /*!< General info */ + uint32_t supported_features; /*!< Pbap Supported Features */ + uint32_t supported_repositories; /*!< Supported Repositories */ +} esp_bluetooth_sdp_pse_record_t; + +/** + * @brief Phone Book Profile - Client parameters + */ +typedef struct bluetooth_sdp_pce_record { + esp_bluetooth_sdp_hdr_overlay_t hdr; /*!< General info */ +} esp_bluetooth_sdp_pce_record_t; + +/** + * @brief Object Push Profile parameters + */ +typedef struct bluetooth_sdp_ops_record { + esp_bluetooth_sdp_hdr_overlay_t hdr; /*!< General info */ + int supported_formats_list_len; /*!< Supported formats list length */ + uint8_t supported_formats_list[SDP_OPP_SUPPORTED_FORMATS_MAX_LENGTH]; /*!< Supported formats list */ +} esp_bluetooth_sdp_ops_record_t; + +/** + * @brief SIM Access Profile parameters + */ +typedef struct bluetooth_sdp_sap_record { + esp_bluetooth_sdp_hdr_overlay_t hdr; /*!< General info */ +} esp_bluetooth_sdp_sap_record_t; + +/** + * @brief SDP record parameters union + */ +typedef union { + esp_bluetooth_sdp_hdr_overlay_t hdr; /*!< General info */ + esp_bluetooth_sdp_mas_record_t mas; /*!< Message Access Profile - Server */ + esp_bluetooth_sdp_mns_record_t mns; /*!< Message Access Profile - Client (Notification Server) */ + esp_bluetooth_sdp_pse_record_t pse; /*!< Phone Book Profile - Server */ + esp_bluetooth_sdp_pce_record_t pce; /*!< Phone Book Profile - Client */ + esp_bluetooth_sdp_ops_record_t ops; /*!< Object Push Profile */ + esp_bluetooth_sdp_sap_record_t sap; /*!< SIM Access Profile */ +} esp_bluetooth_sdp_record_t; + +/** + * @brief SDP callback parameters union + */ +typedef union { + /** + * @brief ESP_SDP_INIT_EVT + */ + struct sdp_init_evt_param { + esp_sdp_status_t status; /*!< status */ + } init; /*!< SDP callback param of ESP_SDP_INIT_EVT */ + + /** + * @brief ESP_SDP_DEINIT_EVT + */ + struct sdp_deinit_evt_param { + esp_sdp_status_t status; /*!< status */ + } deinit; /*!< SDP callback param of ESP_SDP_DEINIT_EVT */ + + /** + * @brief ESP_SDP_SEARCH_COMP_EVT + */ + struct sdp_search_evt_param { + esp_sdp_status_t status; /*!< status */ + esp_bd_addr_t remote_addr; /*!< remote device address */ + esp_bt_uuid_t sdp_uuid; /*!< service uuid */ + int record_count; /*!< Number of SDP records */ + esp_bluetooth_sdp_record_t *records;/*!< SDP records */ + } search; /*!< SDP callback param of ESP_SDP_SEARCH_COMP_EVT */ + + /** + * @brief ESP_SDP_CREATE_RECORD_COMP_EVT + */ + struct sdp_crate_record_evt_param { + esp_sdp_status_t status; /*!< status */ + int record_handle; /*!< SDP record handle */ + } create_record; /*!< SDP callback param of ESP_SDP_CREATE_RECORD_COMP_EVT */ + + /** + * @brief ESP_SDP_REMOVE_RECORD_COMP_EVT + */ + struct sdp_remove_record_evt_param { + esp_sdp_status_t status; /*!< status */ + } remove_record; /*!< SDP callback param of ESP_SDP_REMOVE_RECORD_COMP_EVT */ + +} esp_sdp_cb_param_t; /*!< SDP callback parameter union type */ + + +/** + * @brief SDP callback function type. + * + * @param event: Event type + * @param param: Point to callback parameter, currently is union type + */ +typedef void (* esp_sdp_cb_t)(esp_sdp_cb_event_t event, esp_sdp_cb_param_t *param); + +/** + * @brief This function is called to init callbacks with SDP module. + * + * @param[in] callback: pointer to the init callback function. + * + * @return + * - ESP_OK: success + * - other: failed + */ +esp_err_t esp_sdp_register_callback(esp_sdp_cb_t callback); + +/** + * @brief This function is called to init SDP module. + * When the operation is completed, the callback function will be called with ESP_SDP_INIT_EVT. + * This function should be called after esp_bluedroid_enable() completes successfully. + * + * @return + * - ESP_OK: success + * - other: failed + */ +esp_err_t esp_sdp_init(void); + +/** + * @brief This function is called to de-initialize SDP module. + * The operation will remove all SDP records, then the callback function will be called + * with ESP_SDP_REMOVE_RECORD_COMP_EVT, and the number of ESP_SDP_REMOVE_RECORD_COMP_EVT is + * equal to the number of SDP records.When the operation is completed, the callback function + * will be called with ESP_SDP_DEINIT_EVT. This function should be called after esp_sdp_init() + * completes successfully. + * + * @return + * - ESP_OK: success + * - other: failed + */ +esp_err_t esp_sdp_deinit(void); + +/** + * @brief This function is called to performs service discovery for the services provided by the given peer device. + * When the operation is completed, the callback function will be called with ESP_SDP_SEARCH_COMP_EVT. + * This function must be called after esp_sdp_init() successful and before esp_sdp_deinit(). + * + * @param[in] bd_addr: Remote device bluetooth device address. + * @param[in] uuid: Service UUID of the remote device. + * + * @return + * - ESP_OK: success + * - other: failed + */ +esp_err_t esp_sdp_search_record(esp_bd_addr_t bd_addr, esp_bt_uuid_t uuid); + +/** + * @brief This function is called to create SDP records. + * When the operation is completed, the callback function will be called with ESP_SDP_CREATE_RECORD_COMP_EVT. + * This function must be called after esp_sdp_init() successful and before esp_sdp_deinit(). + * + * @param[in] record: The SDP record to create. + * + * @return + * - ESP_OK: success + * - other: failed + */ +esp_err_t esp_sdp_create_record(esp_bluetooth_sdp_record_t *record); + +/** + * @brief This function is called to remove a SDP record. + * When the operation is completed, the callback function will be called with ESP_SDP_REMOVE_RECORD_COMP_EVT. + * This function must be called after esp_sdp_init() successful and before esp_sdp_deinit(). + * + * @param[in] record_handle: The SDP record handle. + * + * @return + * - ESP_OK: success + * - other: failed + */ +esp_err_t esp_sdp_remove_record(int record_handle); + +#ifdef __cplusplus +} +#endif + +#endif ///__ESP_SDP_API_H__ diff --git a/lib/bt/host/bluedroid/api/include/api/esp_spp_api.h b/lib/bt/host/bluedroid/api/include/api/esp_spp_api.h new file mode 100644 index 00000000..d2a0e090 --- /dev/null +++ b/lib/bt/host/bluedroid/api/include/api/esp_spp_api.h @@ -0,0 +1,439 @@ +/* + * SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __ESP_SPP_API_H__ +#define __ESP_SPP_API_H__ + +#include "esp_err.h" +#include "esp_bt_defs.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define ESP_SPP_MAX_MTU (3*330) /*!< SPP max MTU */ +#define ESP_SPP_MAX_SCN 31 /*!< SPP max SCN */ +#define ESP_SPP_MIN_TX_BUFFER_SIZE 100 /*!< SPP min tx buffer */ +#define ESP_SPP_MAX_TX_BUFFER_SIZE (ESP_SPP_MAX_MTU * 10) /*!< SPP max tx buffer size */ + +/** + * @brief SPP default configuration + */ +#define BT_SPP_DEFAULT_CONFIG() { \ + .mode = ESP_SPP_MODE_VFS, \ + .enable_l2cap_ertm = true, \ + .tx_buffer_size = ESP_SPP_MAX_TX_BUFFER_SIZE, \ +} + +/* Security Setting Mask +Use these three mask modes on both sides: +1. ESP_SPP_SEC_NONE +2. ESP_SPP_SEC_AUTHENTICATE +3. (ESP_SPP_SEC_AUTHENTICATE | ESP_SPP_SEC_ENCRYPT) +Use these three mask modes only on acceptor side: +1. ESP_SPP_SEC_IN_16_DIGITS +2. (ESP_SPP_SEC_IN_16_DIGITS | ESP_SPP_SEC_AUTHENTICATE) +3. (ESP_SPP_SEC_IN_16_DIGITS | ESP_SPP_SEC_AUTHENTICATE | ESP_SPP_SEC_ENCRYPT) +Due to certain limitations, do not use these mask modes: +1. ESP_SPP_SEC_AUTHORIZE +2. ESP_SPP_SEC_MODE4_LEVEL4 +3. ESP_SPP_SEC_MITM +*/ +#define ESP_SPP_SEC_NONE 0x0000 /*!< No security. relate to BTA_SEC_NONE in bta/bta_api.h */ +#define ESP_SPP_SEC_AUTHORIZE 0x0001 /*!< Authorization required (only needed for out going connection ) relate to BTA_SEC_AUTHORIZE in bta/bta_api.h*/ +#define ESP_SPP_SEC_AUTHENTICATE 0x0012 /*!< Authentication required. relate to BTA_SEC_AUTHENTICATE in bta/bta_api.h*/ +#define ESP_SPP_SEC_ENCRYPT 0x0024 /*!< Encryption required. relate to BTA_SEC_ENCRYPT in bta/bta_api.h*/ +#define ESP_SPP_SEC_MODE4_LEVEL4 0x0040 /*!< Mode 4 level 4 service, i.e. incoming/outgoing MITM and P-256 encryption relate to BTA_SEC_MODE4_LEVEL4 in bta/bta_api.h*/ +#define ESP_SPP_SEC_MITM 0x3000 /*!< Man-In-The_Middle protection relate to BTA_SEC_MITM in bta/bta_api.h*/ +#define ESP_SPP_SEC_IN_16_DIGITS 0x4000 /*!< Min 16 digit for pin code relate to BTA_SEC_IN_16_DIGITS in bta/bta_api.h*/ +typedef uint16_t esp_spp_sec_t; + +typedef enum { + ESP_SPP_SUCCESS = 0, /*!< Successful operation. */ + ESP_SPP_FAILURE, /*!< Generic failure. */ + ESP_SPP_BUSY, /*!< Temporarily can not handle this request. */ + ESP_SPP_NO_DATA, /*!< No data */ + ESP_SPP_NO_RESOURCE, /*!< No more resource */ + ESP_SPP_NEED_INIT, /*!< SPP module shall init first */ + ESP_SPP_NEED_DEINIT, /*!< SPP module shall deinit first */ + ESP_SPP_NO_CONNECTION, /*!< Connection may have been closed */ + ESP_SPP_NO_SERVER, /*!< No SPP server */ +} esp_spp_status_t; + +typedef enum { + ESP_SPP_ROLE_MASTER = 0, /*!< Role: master */ + ESP_SPP_ROLE_SLAVE = 1, /*!< Role: slave */ +} esp_spp_role_t; + +typedef enum { + ESP_SPP_MODE_CB = 0, /*!< When data is coming, a callback will come with data */ + ESP_SPP_MODE_VFS = 1, /*!< Use VFS to write/read data */ +} esp_spp_mode_t; + +/** + * @brief SPP configuration parameters + */ +typedef struct { + esp_spp_mode_t mode; /*!< Choose the mode of SPP, ESP_SPP_MODE_CB or ESP_SPP_MODE_VFS. */ + bool enable_l2cap_ertm; /*!< Enable/disable Logical Link Control and Adaptation Layer Protocol enhanced retransmission mode. */ + uint16_t tx_buffer_size; /*!< Tx buffer size for a new SPP channel. A smaller setting can save memory, but may incur a decrease in throughput. Only for ESP_SPP_MODE_VFS mode. */ +} esp_spp_cfg_t; + +/** + * @brief SPP callback function events + */ +typedef enum { + ESP_SPP_INIT_EVT = 0, /*!< When SPP is initialized, the event comes */ + ESP_SPP_UNINIT_EVT = 1, /*!< When SPP is deinitialized, the event comes */ + ESP_SPP_DISCOVERY_COMP_EVT = 8, /*!< When SDP discovery complete, the event comes */ + ESP_SPP_OPEN_EVT = 26, /*!< When SPP Client connection open, the event comes */ + ESP_SPP_CLOSE_EVT = 27, /*!< When SPP connection closed, the event comes */ + ESP_SPP_START_EVT = 28, /*!< When SPP server started, the event comes */ + ESP_SPP_CL_INIT_EVT = 29, /*!< When SPP client initiated a connection, the event comes */ + ESP_SPP_DATA_IND_EVT = 30, /*!< When SPP connection received data, the event comes, only for ESP_SPP_MODE_CB */ + ESP_SPP_CONG_EVT = 31, /*!< When SPP connection congestion status changed, the event comes, only for ESP_SPP_MODE_CB */ + ESP_SPP_WRITE_EVT = 33, /*!< When SPP write operation completes, the event comes, only for ESP_SPP_MODE_CB */ + ESP_SPP_SRV_OPEN_EVT = 34, /*!< When SPP Server connection open, the event comes */ + ESP_SPP_SRV_STOP_EVT = 35, /*!< When SPP server stopped, the event comes */ + ESP_SPP_VFS_REGISTER_EVT = 36, /*!< When SPP VFS register, the event comes */ + ESP_SPP_VFS_UNREGISTER_EVT = 37, /*!< When SPP VFS unregister, the event comes */ +} esp_spp_cb_event_t; + + +/** + * @brief SPP callback parameters union + */ +typedef union { + /** + * @brief SPP_INIT_EVT + */ + struct spp_init_evt_param { + esp_spp_status_t status; /*!< status */ + } init; /*!< SPP callback param of SPP_INIT_EVT */ + + /** + * @brief SPP_UNINIT_EVT + */ + struct spp_uninit_evt_param { + esp_spp_status_t status; /*!< status */ + } uninit; /*!< SPP callback param of SPP_UNINIT_EVT */ + + /** + * @brief SPP_DISCOVERY_COMP_EVT + */ + struct spp_discovery_comp_evt_param { + esp_spp_status_t status; /*!< status */ + uint8_t scn_num; /*!< The num of scn_num */ + uint8_t scn[ESP_SPP_MAX_SCN]; /*!< channel # */ + const char *service_name[ESP_SPP_MAX_SCN]; /*!< service_name */ + } disc_comp; /*!< SPP callback param of SPP_DISCOVERY_COMP_EVT */ + + /** + * @brief ESP_SPP_OPEN_EVT + */ + struct spp_open_evt_param { + esp_spp_status_t status; /*!< status */ + uint32_t handle; /*!< The connection handle */ + int fd; /*!< The file descriptor only for ESP_SPP_MODE_VFS */ + esp_bd_addr_t rem_bda; /*!< The peer address */ + } open; /*!< SPP callback param of ESP_SPP_OPEN_EVT */ + + /** + * @brief ESP_SPP_SRV_OPEN_EVT + */ + struct spp_srv_open_evt_param { + esp_spp_status_t status; /*!< status */ + uint32_t handle; /*!< The connection handle */ + uint32_t new_listen_handle; /*!< The new listen handle */ + int fd; /*!< The file descriptor only for ESP_SPP_MODE_VFS */ + esp_bd_addr_t rem_bda; /*!< The peer address */ + } srv_open; /*!< SPP callback param of ESP_SPP_SRV_OPEN_EVT */ + /** + * @brief ESP_SPP_CLOSE_EVT + */ + struct spp_close_evt_param { + esp_spp_status_t status; /*!< status */ + uint32_t port_status; /*!< PORT status */ + uint32_t handle; /*!< The connection handle */ + bool async; /*!< FALSE, if local initiates disconnect */ + } close; /*!< SPP callback param of ESP_SPP_CLOSE_EVT */ + + /** + * @brief ESP_SPP_START_EVT + */ + struct spp_start_evt_param { + esp_spp_status_t status; /*!< status */ + uint32_t handle; /*!< The connection handle */ + uint8_t sec_id; /*!< security ID used by this server */ + uint8_t scn; /*!< Server channel number */ + bool use_co; /*!< TRUE to use co_rfc_data */ + } start; /*!< SPP callback param of ESP_SPP_START_EVT */ + + /** + * @brief ESP_SPP_SRV_STOP_EVT + */ + struct spp_srv_stop_evt_param { + esp_spp_status_t status; /*!< status */ + uint8_t scn; /*!< Server channel number */ + } srv_stop; /*!< SPP callback param of ESP_SPP_SRV_STOP_EVT */ + + /** + * @brief ESP_SPP_CL_INIT_EVT + */ + struct spp_cl_init_evt_param { + esp_spp_status_t status; /*!< status */ + uint32_t handle; /*!< The connection handle */ + uint8_t sec_id; /*!< security ID used by this server */ + bool use_co; /*!< TRUE to use co_rfc_data */ + } cl_init; /*!< SPP callback param of ESP_SPP_CL_INIT_EVT */ + + /** + * @brief ESP_SPP_WRITE_EVT + */ + struct spp_write_evt_param { + esp_spp_status_t status; /*!< status */ + uint32_t handle; /*!< The connection handle */ + int len; /*!< The length of the data written. */ + bool cong; /*!< congestion status */ + } write; /*!< SPP callback param of ESP_SPP_WRITE_EVT */ + + /** + * @brief ESP_SPP_DATA_IND_EVT + */ + struct spp_data_ind_evt_param { + esp_spp_status_t status; /*!< status */ + uint32_t handle; /*!< The connection handle */ + uint16_t len; /*!< The length of data */ + uint8_t *data; /*!< The data received */ + } data_ind; /*!< SPP callback param of ESP_SPP_DATA_IND_EVT */ + + /** + * @brief ESP_SPP_CONG_EVT + */ + struct spp_cong_evt_param { + esp_spp_status_t status; /*!< status */ + uint32_t handle; /*!< The connection handle */ + bool cong; /*!< TRUE, congested. FALSE, uncongested */ + } cong; /*!< SPP callback param of ESP_SPP_CONG_EVT */ + + /** + * @brief ESP_SPP_VFS_REGISTER_EVT + */ + struct spp_vfs_register_evt_param { + esp_spp_status_t status; /*!< status */ + } vfs_register; /*!< SPP callback param of ESP_SPP_VFS_REGISTER_EVT */ + + /** + * @brief ESP_SPP_VFS_UNREGISTER_EVT + */ + struct spp_vfs_unregister_evt_param { + esp_spp_status_t status; /*!< status */ + } vfs_unregister; /*!< SPP callback param of ESP_SPP_VFS_UNREGISTER_EVT */ +} esp_spp_cb_param_t; /*!< SPP callback parameter union type */ + +/** + * @brief SPP callback function type. + * When handle ESP_SPP_DATA_IND_EVT, it is strongly recommended to cache incoming data, and process them in + * other lower priority application task rather than in this callback directly. + * + * @param event: Event type + * @param param: Point to callback parameter, currently is union type + */ +typedef void (*esp_spp_cb_t)(esp_spp_cb_event_t event, esp_spp_cb_param_t *param); + +/** + * @brief This function is called to init callbacks with SPP module. + * + * @param[in] callback: pointer to the init callback function. + * + * @return + * - ESP_OK: success + * - other: failed + */ +esp_err_t esp_spp_register_callback(esp_spp_cb_t callback); + +/** + * @brief This function is called to init SPP module. + * When the operation is completed, the callback function will be called with ESP_SPP_INIT_EVT. + * This function should be called after esp_bluedroid_enable() completes successfully. + * + * @param[in] mode: Choose the mode of SPP, ESP_SPP_MODE_CB or ESP_SPP_MODE_VFS. + * + * @return + * - ESP_OK: success + * - other: failed + */ +esp_err_t esp_spp_init(esp_spp_mode_t mode) __attribute__((deprecated("Please use esp_spp_enhanced_init"))); + + +/** + * @brief This function is called to init SPP module. + * When the operation is completed, the callback function will be called with ESP_SPP_INIT_EVT. + * This function should be called after esp_bluedroid_enable() completes successfully. + * + * @param[in] cfg: SPP configuration. + * + * @note The member variable enable_l2cap_etrm in esp_spp_cfg_t can affect all L2CAP channel + * configurations of the upper layer RFCOMM protocol. + * + * @return + * - ESP_OK: success + * - other: failed + */ +esp_err_t esp_spp_enhanced_init(const esp_spp_cfg_t *cfg); + +/** + * @brief This function is called to uninit SPP module. + * The operation will close all active SPP connection first, then the callback function will be called + * with ESP_SPP_CLOSE_EVT, and the number of ESP_SPP_CLOSE_EVT is equal to the number of connection. + * When the operation is completed, the callback function will be called with ESP_SPP_UNINIT_EVT. + * This function should be called after esp_spp_init()/esp_spp_enhanced_init() completes successfully. + * + * @return + * - ESP_OK: success + * - other: failed + */ +esp_err_t esp_spp_deinit(void); + + +/** + * @brief This function is called to performs service discovery for the services provided by the given peer device. + * When the operation is completed, the callback function will be called with ESP_SPP_DISCOVERY_COMP_EVT. + * This function must be called after esp_spp_init()/esp_spp_enhanced_init() successful and before esp_spp_deinit(). + * + * @param[in] bd_addr: Remote device bluetooth device address. + * + * @return + * - ESP_OK: success + * - other: failed + */ +esp_err_t esp_spp_start_discovery(esp_bd_addr_t bd_addr); + +/** + * @brief This function makes an SPP connection to a remote BD Address. + * When the connection is initiated or failed to initiate, the callback is called with ESP_SPP_CL_INIT_EVT. + * When the connection is established or failed, the callback is called with ESP_SPP_OPEN_EVT. + * This function must be called after esp_spp_init()/esp_spp_enhanced_init() successful and before esp_spp_deinit(). + * + * @param[in] sec_mask: Security Setting Mask. Suggest to use ESP_SPP_SEC_NONE, ESP_SPP_SEC_AUTHORIZE or ESP_SPP_SEC_AUTHENTICATE only. + * @param[in] role: Master or slave. + * @param[in] remote_scn: Remote device bluetooth device SCN. + * @param[in] peer_bd_addr: Remote device bluetooth device address. + * + * @return + * - ESP_OK: success + * - other: failed + */ +esp_err_t esp_spp_connect(esp_spp_sec_t sec_mask, esp_spp_role_t role, uint8_t remote_scn, esp_bd_addr_t peer_bd_addr); + +/** + * @brief This function closes an SPP connection. + * When the operation is completed, the callback function will be called with ESP_SPP_CLOSE_EVT. + * This function must be called after esp_spp_init()/esp_spp_enhanced_init() successful and before esp_spp_deinit(). + * + * @param[in] handle: The connection handle. + * + * @return + * - ESP_OK: success + * - other: failed + */ +esp_err_t esp_spp_disconnect(uint32_t handle); + +/** + * @brief This function create a SPP server and starts listening for an + * SPP connection request from a remote Bluetooth device. + * When the server is started successfully, the callback is called with ESP_SPP_START_EVT. + * When the connection is established, the callback is called with ESP_SPP_SRV_OPEN_EVT. + * This function must be called after esp_spp_init()/esp_spp_enhanced_init() successful and before esp_spp_deinit(). + * + * @param[in] sec_mask: Security Setting Mask. Suggest to use ESP_SPP_SEC_NONE, ESP_SPP_SEC_AUTHORIZE or ESP_SPP_SEC_AUTHENTICATE only. + * @param[in] role: Master or slave. + * @param[in] local_scn: The specific channel you want to get. + * If channel is 0, means get any channel. + * @param[in] name: Server's name. + * + * @return + * - ESP_OK: success + * - other: failed + */ +esp_err_t esp_spp_start_srv(esp_spp_sec_t sec_mask, esp_spp_role_t role, uint8_t local_scn, const char *name); + +/** + * @brief This function stops all SPP servers. + * The operation will close all active SPP connection first, then the callback function will be called + * with ESP_SPP_CLOSE_EVT, and the number of ESP_SPP_CLOSE_EVT is equal to the number of connection. + * When the operation is completed, the callback is called with ESP_SPP_SRV_STOP_EVT. + * This function must be called after esp_spp_init()/esp_spp_enhanced_init() successful and before esp_spp_deinit(). + * + * @return + * - ESP_OK: success + * - other: failed + */ + +esp_err_t esp_spp_stop_srv(void); + +/** + * @brief This function stops a specific SPP server. + * The operation will close all active SPP connection first on the specific SPP server, then the callback function will be called + * with ESP_SPP_CLOSE_EVT, and the number of ESP_SPP_CLOSE_EVT is equal to the number of connection. + * When the operation is completed, the callback is called with ESP_SPP_SRV_STOP_EVT. + * This function must be called after esp_spp_init()/esp_spp_enhanced_init() successful and before esp_spp_deinit(). + * + * @param[in] scn: Server channel number. + * + * @return + * - ESP_OK: success + * - other: failed + */ +esp_err_t esp_spp_stop_srv_scn(uint8_t scn); + +/** + * @brief This function is used to write data, only for ESP_SPP_MODE_CB. + * When this function need to be called repeatedly, it is strongly recommended to call this function again after + * the previous event ESP_SPP_WRITE_EVT is received and the parameter 'cong' is equal to false. If the previous event + * ESP_SPP_WRITE_EVT with parameter 'cong' is equal to true, the function can only be called again when the event + * ESP_SPP_CONG_EVT with parameter 'cong' equal to false is received. + * This function must be called after an connection between initiator and acceptor has been established. + * + * @param[in] handle: The connection handle. + * @param[in] len: The length of the data written. + * @param[in] p_data: The data written. + * + * @return + * - ESP_OK: success + * - other: failed + */ +esp_err_t esp_spp_write(uint32_t handle, int len, uint8_t *p_data); + + +/** + * @brief This function is used to register VFS. + * For now, SPP only supports write, read and close. + * When the operation is completed, the callback function will be called with ESP_SPP_VFS_REGISTER_EVT. + * This function must be called after esp_spp_init()/esp_spp_enhanced_init() successful and before esp_spp_deinit(). + * + * @return + * - ESP_OK: success + * - other: failed + */ +esp_err_t esp_spp_vfs_register(void); + +/** + * @brief This function is used to unregister VFS. + * When the operation is completed, the callback function will be called with ESP_SPP_VFS_UNREGISTER_EVT. + * This function must be called after esp_spp_vfs_register() successful and before esp_spp_deinit(). + * + * @return + * - ESP_OK: success + * - other: failed + */ +esp_err_t esp_spp_vfs_unregister(void); + +#ifdef __cplusplus +} +#endif + +#endif ///__ESP_SPP_API_H__ diff --git a/lib/bt/host/bluedroid/bta/ar/bta_ar.c b/lib/bt/host/bluedroid/bta/ar/bta_ar.c new file mode 100644 index 00000000..cd70eeec --- /dev/null +++ b/lib/bt/host/bluedroid/bta/ar/bta_ar.c @@ -0,0 +1,318 @@ +/****************************************************************************** + * + * Copyright (C) 2008-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This is the implementation for the audio/video registration module. + * + ******************************************************************************/ + +#include "common/bt_target.h" +#include +#include "bta/bta_ar_api.h" +#include "bta_ar_int.h" + +#if BTA_AR_INCLUDED + +/* AV control block */ +#if BTA_DYNAMIC_MEMORY == FALSE +tBTA_AR_CB bta_ar_cb; +#else +tBTA_AR_CB *bta_ar_cb_ptr; +#endif + +/******************************************************************************* +** +** Function bta_ar_id +** +** Description This function maps sys_id to ar id mask. +** +** Returns void +** +*******************************************************************************/ +static UINT8 bta_ar_id(tBTA_SYS_ID sys_id) +{ + UINT8 mask = 0; + if (sys_id == BTA_ID_AV) { + mask = BTA_AR_AV_MASK; + } else if (sys_id == BTA_ID_AVK) { + mask = BTA_AR_AVK_MASK; + } + + return mask; +} + +/******************************************************************************* +** +** Function bta_ar_init +** +** Description This function is called to register to AVDTP. +** +** Returns void +** +*******************************************************************************/ +void bta_ar_init(void) +{ + /* initialize control block */ + memset(&bta_ar_cb, 0, sizeof(tBTA_AR_CB)); +} + +/******************************************************************************* +** +** Function bta_ar_reg_avdt +** +** Description This function is called to register to AVDTP. +** +** Returns void +** +*******************************************************************************/ +static void bta_ar_avdt_cback(UINT8 handle, BD_ADDR bd_addr, UINT8 event, tAVDT_CTRL *p_data) +{ + /* route the AVDT registration callback to av or avk */ + if (bta_ar_cb.p_av_conn_cback) { + (*bta_ar_cb.p_av_conn_cback)(handle, bd_addr, event, p_data); + } + if (bta_ar_cb.p_avk_conn_cback) { + (*bta_ar_cb.p_avk_conn_cback)(handle, bd_addr, event, p_data); + } +} + +/******************************************************************************* +** +** Function bta_ar_reg_avdt +** +** Description AR module registration to AVDT. +** +** Returns void +** +*******************************************************************************/ +void bta_ar_reg_avdt(tAVDT_REG *p_reg, tAVDT_CTRL_CBACK *p_cback, tBTA_SYS_ID sys_id) +{ + UINT8 mask = 0; + + if (sys_id == BTA_ID_AV) { + bta_ar_cb.p_av_conn_cback = p_cback; + mask = BTA_AR_AV_MASK; + } else if (sys_id == BTA_ID_AVK) { + bta_ar_cb.p_avk_conn_cback = p_cback; + mask = BTA_AR_AVK_MASK; + } +#if (BTA_AR_DEBUG == TRUE) + else { + APPL_TRACE_ERROR("bta_ar_reg_avdt: the registration is from wrong sys_id:%d", sys_id); + } +#endif + + if (mask) { + if (bta_ar_cb.avdt_registered == 0) { + AVDT_Register(p_reg, bta_ar_avdt_cback); + } + bta_ar_cb.avdt_registered |= mask; + } +} + +/******************************************************************************* +** +** Function bta_ar_dereg_avdt +** +** Description This function is called to de-register from AVDTP. +** +** Returns void +** +*******************************************************************************/ +void bta_ar_dereg_avdt(tBTA_SYS_ID sys_id) +{ + UINT8 mask = 0; + + if (sys_id == BTA_ID_AV) { + bta_ar_cb.p_av_conn_cback = NULL; + mask = BTA_AR_AV_MASK; + } else if (sys_id == BTA_ID_AVK) { + bta_ar_cb.p_avk_conn_cback = NULL; + mask = BTA_AR_AVK_MASK; + } + bta_ar_cb.avdt_registered &= ~mask; + + if (bta_ar_cb.avdt_registered == 0) { + AVDT_Deregister(); + } +} + +/******************************************************************************* +** +** Function bta_ar_avdt_conn +** +** Description This function is called to let ar know that some AVDTP profile +** is connected for this sys_id. +** If the other sys modules started a timer for PENDING_EVT, +** the timer can be stopped now. +** +** Returns void +** +*******************************************************************************/ +void bta_ar_avdt_conn(tBTA_SYS_ID sys_id, BD_ADDR bd_addr) +{ + UINT8 event = BTA_AR_AVDT_CONN_EVT; + tAVDT_CTRL data; + + if (sys_id == BTA_ID_AV) { + if (bta_ar_cb.p_avk_conn_cback) { + (*bta_ar_cb.p_avk_conn_cback)(0, bd_addr, event, &data); + } + } else if (sys_id == BTA_ID_AVK) { + if (bta_ar_cb.p_av_conn_cback) { + (*bta_ar_cb.p_av_conn_cback)(0, bd_addr, event, &data); + } + } +} + +/******************************************************************************* +** +** Function bta_ar_reg_avct +** +** Description This function is called to register to AVCTP. +** +** Returns void +** +*******************************************************************************/ +void bta_ar_reg_avct(UINT16 mtu, UINT16 mtu_br, UINT8 sec_mask, tBTA_SYS_ID sys_id) +{ + UINT8 mask = bta_ar_id (sys_id); + + if (mask) { + if (bta_ar_cb.avct_registered == 0) { + AVCT_Register(mtu, mtu_br, sec_mask); + } + bta_ar_cb.avct_registered |= mask; + } +} + +/******************************************************************************* +** +** Function bta_ar_dereg_avct +** +** Description This function is called to deregister from AVCTP. +** +** Returns void +** +*******************************************************************************/ +void bta_ar_dereg_avct(tBTA_SYS_ID sys_id) +{ + UINT8 mask = bta_ar_id (sys_id); + + bta_ar_cb.avct_registered &= ~mask; + + if (bta_ar_cb.avct_registered == 0) { + AVCT_Deregister(); + } +} + +/****************************************************************************** +** +** Function bta_ar_reg_avrc +** +** Description This function is called to register an SDP record for AVRCP. +** +** Returns void +** +******************************************************************************/ +void bta_ar_reg_avrc(UINT16 service_uuid, char *service_name, char *provider_name, + UINT16 categories, tBTA_SYS_ID sys_id, BOOLEAN browsing_en) +{ + UINT8 mask = bta_ar_id (sys_id); + UINT8 temp[8], *p; + + if (!mask || !categories) { + return; + } + + if (service_uuid == UUID_SERVCLASS_AV_REM_CTRL_TARGET) { + if (bta_ar_cb.sdp_tg_handle == 0) { + bta_ar_cb.tg_registered = mask; + bta_ar_cb.sdp_tg_handle = SDP_CreateRecord(); + AVRC_AddRecord(service_uuid, service_name, provider_name, categories, bta_ar_cb.sdp_tg_handle, browsing_en); + bta_sys_add_uuid(service_uuid); + } + /* only one TG is allowed (first-come, first-served). + * If sdp_tg_handle is non-0, ignore this request */ + } else if ((service_uuid == UUID_SERVCLASS_AV_REMOTE_CONTROL) || (service_uuid == UUID_SERVCLASS_AV_REM_CTRL_CONTROL)) { + bta_ar_cb.ct_categories [mask - 1] = categories; + categories = bta_ar_cb.ct_categories[0] | bta_ar_cb.ct_categories[1]; + if (bta_ar_cb.sdp_ct_handle == 0) { + bta_ar_cb.sdp_ct_handle = SDP_CreateRecord(); + AVRC_AddRecord(service_uuid, service_name, provider_name, categories, bta_ar_cb.sdp_ct_handle, browsing_en); + bta_sys_add_uuid(service_uuid); + } else { + /* multiple CTs are allowed. + * Change supported categories on the second one */ + p = temp; + UINT16_TO_BE_STREAM(p, categories); + SDP_AddAttribute(bta_ar_cb.sdp_ct_handle, ATTR_ID_SUPPORTED_FEATURES, UINT_DESC_TYPE, + (UINT32)2, (UINT8 *)temp); + } + } +} + +/****************************************************************************** +** +** Function bta_ar_dereg_avrc +** +** Description This function is called to de-register/delete an SDP record for AVRCP. +** +** Returns void +** +******************************************************************************/ +void bta_ar_dereg_avrc(UINT16 service_uuid, tBTA_SYS_ID sys_id) +{ + UINT8 mask = bta_ar_id (sys_id); + UINT16 categories = 0; + UINT8 temp[8], *p; + + if (!mask) { + return; + } + + if (service_uuid == UUID_SERVCLASS_AV_REM_CTRL_TARGET) { + if (bta_ar_cb.sdp_tg_handle && mask == bta_ar_cb.tg_registered) { + bta_ar_cb.tg_registered = 0; + SDP_DeleteRecord(bta_ar_cb.sdp_tg_handle); + bta_ar_cb.sdp_tg_handle = 0; + bta_sys_remove_uuid(service_uuid); + } + } else if (service_uuid == UUID_SERVCLASS_AV_REMOTE_CONTROL) { + if (bta_ar_cb.sdp_ct_handle) { + bta_ar_cb.ct_categories [mask - 1] = 0; + categories = bta_ar_cb.ct_categories[0] | bta_ar_cb.ct_categories[1]; + if (!categories) { + /* no CT is still registered - cleaup */ + SDP_DeleteRecord(bta_ar_cb.sdp_ct_handle); + bta_ar_cb.sdp_ct_handle = 0; + bta_sys_remove_uuid(service_uuid); + } else { + /* change supported categories to the remaning one */ + p = temp; + UINT16_TO_BE_STREAM(p, categories); + SDP_AddAttribute(bta_ar_cb.sdp_ct_handle, ATTR_ID_SUPPORTED_FEATURES, UINT_DESC_TYPE, + (UINT32)2, (UINT8 *)temp); + } + } + } + +} + +#endif /* #if BTA_AR_INCLUDED */ diff --git a/lib/bt/host/bluedroid/bta/ar/include/bta_ar_int.h b/lib/bt/host/bluedroid/bta/ar/include/bta_ar_int.h new file mode 100644 index 00000000..c4af8269 --- /dev/null +++ b/lib/bt/host/bluedroid/bta/ar/include/bta_ar_int.h @@ -0,0 +1,66 @@ +/****************************************************************************** + * + * Copyright (C) 2008-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This is the private interface file for the BTA audio/video registration + * module. + * + ******************************************************************************/ +#ifndef BTA_AR_INT_H +#define BTA_AR_INT_H + +#include "bta/bta_av_api.h" + +#if (BTA_AR_INCLUDED == TRUE) + +#ifndef BTA_AR_DEBUG +#define BTA_AR_DEBUG FALSE +#endif + +#define BTA_AR_AV_MASK 0x01 +#define BTA_AR_AVK_MASK 0x02 + +/* data associated with BTA_AR */ +typedef struct { + tAVDT_CTRL_CBACK *p_av_conn_cback; /* av connection callback function */ + tAVDT_CTRL_CBACK *p_avk_conn_cback; /* avk connection callback function */ + UINT8 avdt_registered; + UINT8 avct_registered; + UINT32 sdp_tg_handle; + UINT32 sdp_ct_handle; + UINT16 ct_categories[2]; + UINT8 tg_registered; + tBTA_AV_HNDL hndl; /* Handle associated with the stream that rejected the connection. */ +} tBTA_AR_CB; + +/***************************************************************************** +** Global data +*****************************************************************************/ + +/* control block declaration */ +#if BTA_DYNAMIC_MEMORY == FALSE +extern tBTA_AR_CB bta_ar_cb; +#else +extern tBTA_AR_CB *bta_ar_cb_ptr; +#define bta_ar_cb (*bta_ar_cb_ptr) +#endif + +#endif ///BTA_AR_INCLUDED == TRUE + +#endif /* BTA_AR_INT_H */ diff --git a/lib/bt/host/bluedroid/bta/av/bta_av_aact.c b/lib/bt/host/bluedroid/bta/av/bta_av_aact.c new file mode 100644 index 00000000..26a23958 --- /dev/null +++ b/lib/bt/host/bluedroid/bta/av/bta_av_aact.c @@ -0,0 +1,3030 @@ +/****************************************************************************** + * + * Copyright (C) 2004-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains action functions for advanced audio/video stream + * state machine. these functions are shared by both audio and video + * streams. + * + ******************************************************************************/ + +#include "common/bt_target.h" +#if defined(BTA_AV_INCLUDED) && (BTA_AV_INCLUDED == TRUE) + +#include +#include "common/bt_trace.h" +#include + +#include "osi/allocator.h" + +#include "bta_av_int.h" +#include "stack/avdt_api.h" +#include "bta/utl.h" +#include "stack/l2c_api.h" +#include "stack/l2cdefs.h" +#if( defined BTA_AR_INCLUDED ) && (BTA_AR_INCLUDED == TRUE) +#include "bta/bta_ar_api.h" +#endif +#include "bta/bta_api.h" + +/***************************************************************************** +** Constants +*****************************************************************************/ + +/* the delay time in milliseconds to start service discovery on AVRCP */ +#ifndef BTA_AV_RC_DISC_TIME_VAL +#define BTA_AV_RC_DISC_TIME_VAL 3500 +#endif + +/* the timer in milliseconds to guard against link busy and AVDT_CloseReq failed to be sent */ +#ifndef BTA_AV_CLOSE_REQ_TIME_VAL +#define BTA_AV_CLOSE_REQ_TIME_VAL 4000 +#endif + +/* number to retry on reconfigure failure - some headsets requirs this number to be more than 1 */ +#ifndef BTA_AV_RECONFIG_RETRY +#define BTA_AV_RECONFIG_RETRY 6 +#endif + +/* avdt_handle to send abort command for AVDTP BQB test */ +#if A2D_SRC_BQB_INCLUDED +static uint8_t s_avdt_bqb_handle; +#endif /* CONFIG_BT_BQB_ENABLED */ + +static void bta_av_st_rc_timer(tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data); + +/* state machine states */ +enum { + BTA_AV_INIT_SST, + BTA_AV_INCOMING_SST, + BTA_AV_OPENING_SST, + BTA_AV_OPEN_SST, + BTA_AV_RCFG_SST, + BTA_AV_CLOSING_SST +}; + +/* ssm action functions for audio stream */ +const tBTA_AV_SACT bta_av_a2d_action[] = { + bta_av_do_disc_a2d, /* BTA_AV_DO_DISC */ + bta_av_cleanup, /* BTA_AV_CLEANUP */ + bta_av_free_sdb, /* BTA_AV_FREE_SDB */ + bta_av_config_ind, /* BTA_AV_CONFIG_IND */ + bta_av_disconnect_req, /* BTA_AV_DISCONNECT_REQ */ + bta_av_security_req, /* BTA_AV_SECURITY_REQ */ + bta_av_security_rsp, /* BTA_AV_SECURITY_RSP */ + bta_av_setconfig_rsp, /* BTA_AV_SETCONFIG_RSP */ + bta_av_st_rc_timer, /* BTA_AV_ST_RC_TIMER */ + bta_av_str_opened, /* BTA_AV_STR_OPENED */ + bta_av_security_ind, /* BTA_AV_SECURITY_IND */ + bta_av_security_cfm, /* BTA_AV_SECURITY_CFM */ + bta_av_do_close, /* BTA_AV_DO_CLOSE */ + bta_av_connect_req, /* BTA_AV_CONNECT_REQ */ + bta_av_sdp_failed, /* BTA_AV_SDP_FAILED */ + bta_av_disc_results, /* BTA_AV_DISC_RESULTS */ + bta_av_disc_res_as_acp, /* BTA_AV_DISC_RES_AS_ACP */ + bta_av_open_failed, /* BTA_AV_OPEN_FAILED */ + bta_av_getcap_results, /* BTA_AV_GETCAP_RESULTS */ + bta_av_setconfig_rej, /* BTA_AV_SETCONFIG_REJ */ + bta_av_discover_req, /* BTA_AV_DISCOVER_REQ */ + bta_av_conn_failed, /* BTA_AV_CONN_FAILED */ + bta_av_do_start, /* BTA_AV_DO_START */ + bta_av_str_stopped, /* BTA_AV_STR_STOPPED */ + bta_av_reconfig, /* BTA_AV_RECONFIG */ + bta_av_data_path, /* BTA_AV_DATA_PATH */ + bta_av_start_ok, /* BTA_AV_START_OK */ + bta_av_start_failed, /* BTA_AV_START_FAILED */ + bta_av_str_closed, /* BTA_AV_STR_CLOSED */ + bta_av_clr_cong, /* BTA_AV_CLR_CONG */ + bta_av_suspend_cfm, /* BTA_AV_SUSPEND_CFM */ + bta_av_rcfg_str_ok, /* BTA_AV_RCFG_STR_OK */ + bta_av_rcfg_failed, /* BTA_AV_RCFG_FAILED */ + bta_av_rcfg_connect, /* BTA_AV_RCFG_CONNECT */ + bta_av_rcfg_discntd, /* BTA_AV_RCFG_DISCNTD */ + bta_av_suspend_cont, /* BTA_AV_SUSPEND_CONT */ + bta_av_rcfg_cfm, /* BTA_AV_RCFG_CFM */ + bta_av_rcfg_open, /* BTA_AV_RCFG_OPEN */ + bta_av_security_rej, /* BTA_AV_SECURITY_REJ */ + bta_av_open_rc, /* BTA_AV_OPEN_RC */ + bta_av_chk_2nd_start, /* BTA_AV_CHK_2ND_START */ + bta_av_save_caps, /* BTA_AV_SAVE_CAPS */ + bta_av_set_use_rc, /* BTA_AV_SET_USE_RC */ + bta_av_cco_close, /* BTA_AV_CCO_CLOSE */ + bta_av_switch_role, /* BTA_AV_SWITCH_ROLE */ + bta_av_role_res, /* BTA_AV_ROLE_RES */ + bta_av_delay_co, /* BTA_AV_DELAY_CO */ + bta_av_open_at_inc, /* BTA_AV_OPEN_AT_INC */ + bta_av_open_fail_sdp, /* BTA_AV_OPEN_FAIL_SDP */ + bta_av_set_delay_value, /* BTA_AV_SET_DELAY_VALUE */ + NULL +}; + +/* these tables translate AVDT events to SSM events */ +static const UINT16 bta_av_stream_evt_ok[] = { + BTA_AV_STR_DISC_OK_EVT, /* AVDT_DISCOVER_CFM_EVT */ + BTA_AV_STR_GETCAP_OK_EVT, /* AVDT_GETCAP_CFM_EVT */ + BTA_AV_STR_OPEN_OK_EVT, /* AVDT_OPEN_CFM_EVT */ + BTA_AV_STR_OPEN_OK_EVT, /* AVDT_OPEN_IND_EVT */ + BTA_AV_STR_CONFIG_IND_EVT, /* AVDT_CONFIG_IND_EVT */ + BTA_AV_STR_START_OK_EVT, /* AVDT_START_CFM_EVT */ + BTA_AV_STR_START_OK_EVT, /* AVDT_START_IND_EVT */ + BTA_AV_STR_SUSPEND_CFM_EVT, /* AVDT_SUSPEND_CFM_EVT */ + BTA_AV_STR_SUSPEND_CFM_EVT, /* AVDT_SUSPEND_IND_EVT */ + BTA_AV_STR_CLOSE_EVT, /* AVDT_CLOSE_CFM_EVT */ + BTA_AV_STR_CLOSE_EVT, /* AVDT_CLOSE_IND_EVT */ + BTA_AV_STR_RECONFIG_CFM_EVT, /* AVDT_RECONFIG_CFM_EVT */ + 0, /* AVDT_RECONFIG_IND_EVT */ + BTA_AV_STR_SECURITY_CFM_EVT, /* AVDT_SECURITY_CFM_EVT */ + BTA_AV_STR_SECURITY_IND_EVT, /* AVDT_SECURITY_IND_EVT */ + BTA_AV_STR_WRITE_CFM_EVT, /* AVDT_WRITE_CFM_EVT */ + BTA_AV_AVDT_CONNECT_EVT, /* AVDT_CONNECT_IND_EVT */ + BTA_AV_AVDT_DISCONNECT_EVT, /* AVDT_DISCONNECT_IND_EVT */ +#if (AVDT_REPORTING == TRUE) + BTA_AV_AVDT_RPT_CONN_EVT, /* AVDT_REPORT_CONN_EVT */ + BTA_AV_AVDT_RPT_CONN_EVT, /* AVDT_REPORT_DISCONN_EVT */ +#endif + BTA_AV_AVDT_DELAY_RPT_EVT, /* AVDT_DELAY_REPORT_EVT */ + 0 /* AVDT_DELAY_REPORT_CFM_EVT */ +}; + +static const UINT16 bta_av_stream_evt_fail[] = { + BTA_AV_STR_DISC_FAIL_EVT, /* AVDT_DISCOVER_CFM_EVT */ + BTA_AV_STR_GETCAP_FAIL_EVT, /* AVDT_GETCAP_CFM_EVT */ + BTA_AV_STR_OPEN_FAIL_EVT, /* AVDT_OPEN_CFM_EVT */ + BTA_AV_STR_OPEN_OK_EVT, /* AVDT_OPEN_IND_EVT */ + BTA_AV_STR_CONFIG_IND_EVT, /* AVDT_CONFIG_IND_EVT */ + BTA_AV_STR_START_FAIL_EVT, /* AVDT_START_CFM_EVT */ + BTA_AV_STR_START_OK_EVT, /* AVDT_START_IND_EVT */ + BTA_AV_STR_SUSPEND_CFM_EVT, /* AVDT_SUSPEND_CFM_EVT */ + BTA_AV_STR_SUSPEND_CFM_EVT, /* AVDT_SUSPEND_IND_EVT */ + BTA_AV_STR_CLOSE_EVT, /* AVDT_CLOSE_CFM_EVT */ + BTA_AV_STR_CLOSE_EVT, /* AVDT_CLOSE_IND_EVT */ + BTA_AV_STR_RECONFIG_CFM_EVT, /* AVDT_RECONFIG_CFM_EVT */ + 0, /* AVDT_RECONFIG_IND_EVT */ + BTA_AV_STR_SECURITY_CFM_EVT, /* AVDT_SECURITY_CFM_EVT */ + BTA_AV_STR_SECURITY_IND_EVT, /* AVDT_SECURITY_IND_EVT */ + BTA_AV_STR_WRITE_CFM_EVT, /* AVDT_WRITE_CFM_EVT */ + BTA_AV_AVDT_CONNECT_EVT, /* AVDT_CONNECT_IND_EVT */ + BTA_AV_AVDT_DISCONNECT_EVT, /* AVDT_DISCONNECT_IND_EVT */ +#if (AVDT_REPORTING == TRUE) + BTA_AV_AVDT_RPT_CONN_EVT, /* AVDT_REPORT_CONN_EVT */ + BTA_AV_AVDT_RPT_CONN_EVT, /* AVDT_REPORT_DISCONN_EVT */ +#endif + BTA_AV_AVDT_DELAY_RPT_EVT, /* AVDT_DELAY_REPORT_EVT */ + 0 /* AVDT_DELAY_REPORT_CFM_EVT */ +}; + +void bta_av_stream_data_cback(UINT8 handle, BT_HDR *p_pkt, UINT32 time_stamp, UINT8 m_pt); +static void bta_av_stream0_cback(UINT8 handle, BD_ADDR bd_addr, UINT8 event, tAVDT_CTRL *p_data); +static void bta_av_stream1_cback(UINT8 handle, BD_ADDR bd_addr, UINT8 event, tAVDT_CTRL *p_data); +#if BTA_AV_NUM_STRS > 2 +static void bta_av_stream2_cback(UINT8 handle, BD_ADDR bd_addr, UINT8 event, tAVDT_CTRL *p_data); +#endif +#if BTA_AV_NUM_STRS > 3 +static void bta_av_stream3_cback(UINT8 handle, BD_ADDR bd_addr, UINT8 event, tAVDT_CTRL *p_data); +#endif +#if BTA_AV_NUM_STRS > 4 +static void bta_av_stream4_cback(UINT8 handle, BD_ADDR bd_addr, UINT8 event, tAVDT_CTRL *p_data); +#endif +#if BTA_AV_NUM_STRS > 5 +static void bta_av_stream5_cback(UINT8 handle, BD_ADDR bd_addr, UINT8 event, tAVDT_CTRL *p_data); +#endif +/* the array of callback functions to receive events from AVDT control channel */ +tAVDT_CTRL_CBACK *const bta_av_dt_cback[] = { + bta_av_stream0_cback + , bta_av_stream1_cback +#if BTA_AV_NUM_STRS > 2 + , bta_av_stream2_cback +#endif +#if BTA_AV_NUM_STRS > 3 + , bta_av_stream3_cback +#endif +#if BTA_AV_NUM_STRS > 4 + , bta_av_stream4_cback +#endif +#if BTA_AV_NUM_STRS > 5 + , bta_av_stream5_cback +#endif +}; +/*********************************************** +** +** Function bta_get_scb_handle +** +** Description gives the registered AVDT handle.by checking with sep_type. +** +** +** Returns void +***********************************************/ +static UINT8 bta_av_get_scb_handle(tBTA_AV_SCB *p_scb, UINT8 local_sep) +{ + UINT8 xx = 0; + for (xx = 0; xx < BTA_AV_MAX_SEPS; xx++) { + if ((p_scb->seps[xx].tsep == local_sep) && + (p_scb->seps[xx].codec_type == p_scb->codec_type)) { + return (p_scb->seps[xx].av_handle); + } + } + APPL_TRACE_DEBUG(" bta_av_get_scb_handle appropiate sep_type not found") + return 0; /* return invalid handle */ +} + +/*********************************************** +** +** Function bta_av_get_scb_sep_type +** +** Description gives the sep type by cross-checking with AVDT handle +** +** +** Returns void +***********************************************/ +static UINT8 bta_av_get_scb_sep_type(tBTA_AV_SCB *p_scb, UINT8 tavdt_handle) +{ + UINT8 xx = 0; + for (xx = 0; xx < BTA_AV_MAX_SEPS; xx++) { + if (p_scb->seps[xx].av_handle == tavdt_handle) { + return (p_scb->seps[xx].tsep); + } + } + APPL_TRACE_DEBUG(" bta_av_get_scb_sep_type appropiate handle not found") + return 3; /* return invalid sep type */ +} + +/******************************************************************************* +** +** Function bta_av_save_addr +** +** Description copy the bd_addr and maybe reset the supported flags +** +** +** Returns void +** +*******************************************************************************/ +static void bta_av_save_addr(tBTA_AV_SCB *p_scb, const BD_ADDR b) +{ + APPL_TRACE_DEBUG("bta_av_save_addr r:%d, s:%d", + p_scb->recfg_sup, p_scb->suspend_sup); + if (bdcmp(p_scb->peer_addr, b) != 0) { + APPL_TRACE_WARNING("reset flags"); + /* a new addr, reset the supported flags */ + p_scb->recfg_sup = TRUE; + p_scb->suspend_sup = TRUE; + } + + /* do this copy anyway, just in case the first addr matches + * the control block one by accident */ + bdcpy(p_scb->peer_addr, b); +} + +/******************************************************************************* +** +** Function notify_start_failed +** +** Description notify up-layer AV start failed +** +** +** Returns void +** +*******************************************************************************/ +static void notify_start_failed(tBTA_AV_SCB *p_scb) +{ + tBTA_AV_START start; + /* if start failed, clear role */ + p_scb->role &= ~BTA_AV_ROLE_START_INT; + start.chnl = p_scb->chnl; + start.status = BTA_AV_FAIL; + start.initiator = TRUE; + start.hndl = p_scb->hndl; + (*bta_av_cb.p_cback)(BTA_AV_START_EVT, (tBTA_AV *) &start); +} + +/******************************************************************************* +** +** Function bta_av_st_rc_timer +** +** Description start the AVRC timer if no RC connection & CT is supported & +** RC is used or +** as ACP (we do not really know if we want AVRC) +** +** Returns void +** +*******************************************************************************/ +static void bta_av_st_rc_timer(tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data) +{ + UNUSED(p_data); + + APPL_TRACE_DEBUG("bta_av_st_rc_timer rc_handle:%d, use_rc: %d", + p_scb->rc_handle, p_scb->use_rc); + /* for outgoing RC connection as INT/CT */ + if ( (p_scb->rc_handle == BTA_AV_RC_HANDLE_NONE) && + /*(bta_av_cb.features & BTA_AV_FEAT_RCCT) &&*/ + (p_scb->use_rc == TRUE || (p_scb->role & BTA_AV_ROLE_AD_ACP)) ) { + if ((p_scb->wait & BTA_AV_WAIT_ROLE_SW_BITS) == 0) { + bta_sys_start_timer(&p_scb->timer, BTA_AV_AVRC_TIMER_EVT, BTA_AV_RC_DISC_TIME_VAL); + } else { + p_scb->wait |= BTA_AV_WAIT_CHECK_RC; + } + } + +} + +/******************************************************************************* +** +** Function bta_av_next_getcap +** +** Description The function gets the capabilities of the next available +** stream found in the discovery results. +** +** Returns TRUE if we sent request to AVDT, FALSE otherwise. +** +*******************************************************************************/ +static BOOLEAN bta_av_next_getcap(tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data) +{ + int i; + tAVDT_GETCAP_REQ *p_req; + BOOLEAN sent_cmd = FALSE; + UINT16 uuid_int = p_scb->uuid_int; + UINT8 sep_requested = 0; + + if (uuid_int == UUID_SERVCLASS_AUDIO_SOURCE) { + sep_requested = AVDT_TSEP_SNK; + } else if (uuid_int == UUID_SERVCLASS_AUDIO_SINK) { + sep_requested = AVDT_TSEP_SRC; + } + + for (i = p_scb->sep_info_idx; i < p_scb->num_seps; i++) { + /* steam not in use, is a sink, and is the right media type (audio/video) */ + if ((p_scb->sep_info[i].in_use == FALSE) && + (p_scb->sep_info[i].tsep == sep_requested) && + (p_scb->sep_info[i].media_type == p_scb->media_type)) { + p_scb->sep_info_idx = i; + + /* we got a stream; get its capabilities */ + if (p_scb->p_cap == NULL) { + p_scb->p_cap = (tAVDT_CFG *) osi_malloc(sizeof(tAVDT_CFG)); + } + if (p_scb->p_cap == NULL) { + i = p_scb->num_seps; + break; + } + if (p_scb->avdt_version >= AVDT_VERSION_SYNC) { + p_req = AVDT_GetAllCapReq; + } else { + p_req = AVDT_GetCapReq; + } + (*p_req)(p_scb->peer_addr, + p_scb->sep_info[i].seid, + p_scb->p_cap, bta_av_dt_cback[p_scb->hdi]); + sent_cmd = TRUE; + break; + } + } + + /* if no streams available then stream open fails */ + if (!sent_cmd) { + bta_av_ssm_execute(p_scb, BTA_AV_STR_GETCAP_FAIL_EVT, p_data); + } + + return sent_cmd; + +} + +/******************************************************************************* +** +** Function bta_av_proc_stream_evt +** +** Description Utility function to compose stream events. +** +** Returns void +** +*******************************************************************************/ +static void bta_av_proc_stream_evt(UINT8 handle, BD_ADDR bd_addr, UINT8 event, tAVDT_CTRL *p_data, int index) +{ + tBTA_AV_STR_MSG *p_msg; + UINT16 sec_len = 0; + tBTA_AV_SCB *p_scb = bta_av_cb.p_scb[index]; + int xx; + + if (event == AVDT_DELAY_REPORT_CFM_EVT) { + APPL_TRACE_DEBUG("%s: AVDT_DELAY_REPORT_CFM_EVT", __func__); + return; + } + + if (p_data) { + if (event == AVDT_SECURITY_IND_EVT) { + sec_len = (p_data->security_ind.len < BTA_AV_SECURITY_MAX_LEN) ? + p_data->security_ind.len : BTA_AV_SECURITY_MAX_LEN; + } else if (event == AVDT_SECURITY_CFM_EVT && p_data->hdr.err_code == 0) { + sec_len = (p_data->security_cfm.len < BTA_AV_SECURITY_MAX_LEN) ? + p_data->security_cfm.len : BTA_AV_SECURITY_MAX_LEN; + } + } + + if (p_scb && (p_msg = (tBTA_AV_STR_MSG *) osi_malloc((UINT16) (sizeof(tBTA_AV_STR_MSG) + sec_len))) != NULL) { + + /* copy event data, bd addr, and handle to event message buffer */ + p_msg->hdr.offset = 0; + + if (bd_addr != NULL) { + bdcpy(p_msg->bd_addr, bd_addr); + APPL_TRACE_DEBUG(" bd_addr:%02x-%02x-%02x-%02x-%02x-%02x", + bd_addr[0], bd_addr[1], + bd_addr[2], bd_addr[3], + bd_addr[4], bd_addr[5]); + } + + if (p_data != NULL) { + memcpy(&p_msg->msg, p_data, sizeof (tAVDT_CTRL)); + /* copy config params to event message buffer */ + switch (event) { + case AVDT_RECONFIG_CFM_EVT: + APPL_TRACE_DEBUG("reconfig cfm event codec info = 0x%06x-%06x-%06x-%02x", + (p_msg->msg.reconfig_cfm.p_cfg->codec_info[0] << 16) + (p_msg->msg.reconfig_cfm.p_cfg->codec_info[1] << 8) + p_msg->msg.reconfig_cfm.p_cfg->codec_info[2], + (p_msg->msg.reconfig_cfm.p_cfg->codec_info[3] << 16) + (p_msg->msg.reconfig_cfm.p_cfg->codec_info[4] << 8) + p_msg->msg.reconfig_cfm.p_cfg->codec_info[5], + (p_msg->msg.reconfig_cfm.p_cfg->codec_info[6] << 16) + (p_msg->msg.reconfig_cfm.p_cfg->codec_info[7] << 8) + p_msg->msg.reconfig_cfm.p_cfg->codec_info[8], + p_msg->msg.reconfig_cfm.p_cfg->codec_info[9]); + break; + + + + case AVDT_CONFIG_IND_EVT: + /* We might have 2 SEP signallings(A2DP + VDP) with one peer device on one L2CAP. + * If we already have a signalling connection with the bd_addr and the streaming + * SST is at INIT state, change it to INCOMING state to handle the signalling + * from the 2nd SEP. */ + if ((bta_av_find_lcb(bd_addr, BTA_AV_LCB_FIND) != NULL) && (bta_av_is_scb_init(p_scb))) { + bta_av_set_scb_sst_incoming (p_scb); + + /* When ACP_CONNECT_EVT was received, we put first available scb to incoming state. + * Later when we receive AVDT_CONFIG_IND_EVT, we use a new p_scb and set its state to + * incoming which we do it above. + * We also have to set the old p_scb state to init to be used later */ + for (xx = 0; xx < BTA_AV_NUM_STRS; xx++) { + if ((bta_av_cb.p_scb[xx]) && (xx != index)) { + if (bta_av_cb.p_scb[xx]->state == BTA_AV_INCOMING_SST) { + bta_av_cb.p_scb[xx]->state = BTA_AV_INIT_SST; + bta_av_cb.p_scb[xx]->coll_mask = 0; + break; + } + } + } + } + + memcpy(&p_msg->cfg, p_data->config_ind.p_cfg, sizeof(tAVDT_CFG)); + break; + + case AVDT_SECURITY_IND_EVT: + p_msg->msg.security_ind.p_data = (UINT8 *) (p_msg + 1); + memcpy(p_msg->msg.security_ind.p_data, p_data->security_ind.p_data, sec_len); + break; + + case AVDT_SECURITY_CFM_EVT: + p_msg->msg.security_cfm.p_data = (UINT8 *) (p_msg + 1); + if (p_data->hdr.err_code == 0) { + memcpy(p_msg->msg.security_cfm.p_data, p_data->security_cfm.p_data, sec_len); + } + break; + case AVDT_SUSPEND_IND_EVT: + p_msg->msg.hdr.err_code = 0; + break; + /* + case AVDT_CLOSE_CFM_EVT: + case AVDT_CLOSE_IND_EVT: + p_msg->disc_rsn = p_data->hdr.err_param; + break; + */ + case AVDT_DISCONNECT_IND_EVT: + p_msg->hdr.offset = p_data->hdr.err_param; + break; + default: + break; + } + } else { + p_msg->msg.hdr.err_code = 0; + } + + /* look up application event */ + if ((p_data == NULL) || (p_data->hdr.err_code == 0)) { + p_msg->hdr.event = bta_av_stream_evt_ok[event]; + if (p_msg->hdr.event == BTA_AV_STR_START_OK_EVT) { + BTA_DmCoexEventTrigger(BTA_COEX_EVT_STREAMING_STARTED); + } else if (p_msg->hdr.event == BTA_AV_STR_START_FAIL_EVT || + p_msg->hdr.event == BTA_AV_STR_SUSPEND_CFM_EVT || + p_msg->hdr.event == BTA_AV_STR_CLOSE_EVT) { + BTA_DmCoexEventTrigger(BTA_COEX_EVT_STREAMING_STOPPED); + } + } else { + p_msg->hdr.event = bta_av_stream_evt_fail[event]; + if (p_msg->hdr.event == BTA_AV_STR_START_FAIL_EVT || + p_msg->hdr.event == BTA_AV_STR_START_OK_EVT || + p_msg->hdr.event == BTA_AV_STR_SUSPEND_CFM_EVT || + p_msg->hdr.event == BTA_AV_STR_CLOSE_EVT) { + BTA_DmCoexEventTrigger(BTA_COEX_EVT_STREAMING_STOPPED); + } + } + + p_msg->initiator = FALSE; + if (event == AVDT_SUSPEND_CFM_EVT) { + p_msg->initiator = TRUE; + } + + APPL_TRACE_VERBOSE("hndl:x%x", p_scb->hndl); + p_msg->hdr.layer_specific = p_scb->hndl; + p_msg->handle = handle; + p_msg->avdt_event = event; + bta_sys_sendmsg(p_msg); + } + + /* coverity[var_deref_model] */ + /* false-positive: bta_av_conn_cback only processes AVDT_CONNECT_IND_EVT and AVDT_DISCONNECT_IND_EVT event + * these 2 events always have associated p_data */ + if (p_data) { + bta_av_conn_cback(handle, bd_addr, event, p_data); + } else { + APPL_TRACE_EVENT("%s: p_data is null", __func__); + } +} + +/******************************************************************************* +** +** Function bta_av_stream_data_cback +** +** Description This is the AVDTP callback function for stream events. +** +** Returns void +** +*******************************************************************************/ +void bta_av_stream_data_cback(UINT8 handle, BT_HDR *p_pkt, UINT32 time_stamp, UINT8 m_pt) +{ + int index = 0; + tBTA_AV_SCB *p_scb ; + APPL_TRACE_DEBUG("bta_av_stream_data_cback avdt_handle: %d pkt_len=0x%x ofst = 0x%x", handle, p_pkt->len, p_pkt->offset); + APPL_TRACE_DEBUG(" Number of frames 0x%x", *((UINT8 *)(p_pkt + 1) + p_pkt->offset)); + APPL_TRACE_DEBUG("Sequence Number 0x%x", p_pkt->layer_specific); + /* Get SCB and correct sep type*/ + for (index = 0; index < BTA_AV_NUM_STRS; index ++ ) { + p_scb = bta_av_cb.p_scb[index]; + if ((p_scb->avdt_handle == handle) && (p_scb->seps[p_scb->sep_idx].tsep == AVDT_TSEP_SNK)) { + break; + } + } + if (index == BTA_AV_NUM_STRS) { /* cannot find correct handler */ + osi_free(p_pkt); + return; + } + p_pkt->event = BTA_AV_MEDIA_DATA_EVT; + p_scb->seps[p_scb->sep_idx].p_app_data_cback(BTA_AV_MEDIA_DATA_EVT, (tBTA_AV_MEDIA *)p_pkt); + osi_free(p_pkt); /* a copy of packet had been delivered, we free this buffer */ +} + +/******************************************************************************* +** +** Function bta_av_stream0_cback +** +** Description This is the AVDTP callback function for stream events. +** +** Returns void +** +*******************************************************************************/ +static void bta_av_stream0_cback(UINT8 handle, BD_ADDR bd_addr, UINT8 event, tAVDT_CTRL *p_data) +{ + APPL_TRACE_VERBOSE("bta_av_stream0_cback avdt_handle: %d event=0x%x", handle, event); + bta_av_proc_stream_evt(handle, bd_addr, event, p_data, 0); +} + +/******************************************************************************* +** +** Function bta_av_stream1_cback +** +** Description This is the AVDTP callback function for stream events. +** +** Returns void +** +*******************************************************************************/ +static void bta_av_stream1_cback(UINT8 handle, BD_ADDR bd_addr, UINT8 event, tAVDT_CTRL *p_data) +{ + APPL_TRACE_EVENT("bta_av_stream1_cback avdt_handle: %d event=0x%x", handle, event); + bta_av_proc_stream_evt(handle, bd_addr, event, p_data, 1); +} + +#if BTA_AV_NUM_STRS > 2 +/******************************************************************************* +** +** Function bta_av_stream2_cback +** +** Description This is the AVDTP callback function for stream events. +** +** Returns void +** +*******************************************************************************/ +static void bta_av_stream2_cback(UINT8 handle, BD_ADDR bd_addr, UINT8 event, tAVDT_CTRL *p_data) +{ + APPL_TRACE_EVENT("bta_av_stream2_cback avdt_handle: %d event=0x%x", handle, event); + bta_av_proc_stream_evt(handle, bd_addr, event, p_data, 2); +} +#endif + +#if BTA_AV_NUM_STRS > 3 +/******************************************************************************* +** +** Function bta_av_stream3_cback +** +** Description This is the AVDTP callback function for stream events. +** +** Returns void +** +*******************************************************************************/ +static void bta_av_stream3_cback(UINT8 handle, BD_ADDR bd_addr, UINT8 event, tAVDT_CTRL *p_data) +{ + APPL_TRACE_EVENT("bta_av_stream3_cback avdt_handle: %d event=0x%x", handle, event); + bta_av_proc_stream_evt(handle, bd_addr, event, p_data, 3); +} +#endif + +/******************************************************************************* +** +** Function bta_av_stream4_cback +** +** Description This is the AVDTP callback function for stream events. +** +** Returns void +** +*******************************************************************************/ +#if BTA_AV_NUM_STRS > 4 +static void bta_av_stream4_cback(UINT8 handle, BD_ADDR bd_addr, UINT8 event, tAVDT_CTRL *p_data) +{ + APPL_TRACE_EVENT("bta_av_stream4_cback avdt_handle: %d event=0x%x", handle, event); + bta_av_proc_stream_evt(handle, bd_addr, event, p_data, 4); +} +#endif + +/******************************************************************************* +** +** Function bta_av_stream5_cback +** +** Description This is the AVDTP callback function for stream events. +** +** Returns void +** +*******************************************************************************/ +#if BTA_AV_NUM_STRS > 5 +static void bta_av_stream5_cback(UINT8 handle, BD_ADDR bd_addr, UINT8 event, tAVDT_CTRL *p_data) +{ + APPL_TRACE_EVENT("bta_av_stream5_cback avdt_handle: %d event=0x%x", handle, event); + bta_av_proc_stream_evt(handle, bd_addr, event, p_data, 5); +} +#endif + +/******************************************************************************* +** +** Function bta_av_a2d_sdp_cback +** +** Description A2DP service discovery callback. +** +** Returns void +** +*******************************************************************************/ +static void bta_av_a2d_sdp_cback(BOOLEAN found, tA2D_Service *p_service) +{ + tBTA_AV_SDP_RES *p_msg; + tBTA_AV_SCB *p_scb; + + if ((p_msg = (tBTA_AV_SDP_RES *) osi_malloc(sizeof(tBTA_AV_SDP_RES))) != NULL) { + p_msg->hdr.event = (found) ? BTA_AV_SDP_DISC_OK_EVT : BTA_AV_SDP_DISC_FAIL_EVT; + + p_scb = bta_av_hndl_to_scb(bta_av_cb.handle); + if (p_scb) { + if (found && (p_service != NULL)) { + p_scb->avdt_version = p_service->avdt_version; + } else { + p_scb->avdt_version = 0x00; + } + + p_msg->hdr.layer_specific = bta_av_cb.handle; + bta_sys_sendmsg(p_msg); + } else { + APPL_TRACE_ERROR ("bta_av_a2d_sdp_cback, no scb found for handle(0x%x)", bta_av_cb.handle); + } + } +} + +/******************************************************************************* +** +** Function bta_av_adjust_seps_idx +** +** Description adjust the sep_idx +** +** Returns +** +*******************************************************************************/ +static void bta_av_adjust_seps_idx(tBTA_AV_SCB *p_scb, UINT8 avdt_handle) +{ + int xx; + APPL_TRACE_DEBUG("bta_av_adjust_seps_idx codec_type: %d", p_scb->codec_type); + for (xx = 0; xx < BTA_AV_MAX_SEPS; xx++) { + APPL_TRACE_DEBUG("av_handle: %d codec_type: %d", + p_scb->seps[xx].av_handle, p_scb->seps[xx].codec_type); + if ((p_scb->seps[xx].av_handle) && (p_scb->seps[xx].av_handle == avdt_handle)) { + p_scb->sep_idx = xx; + p_scb->avdt_handle = p_scb->seps[xx].av_handle; + break; + } + } +} + +/******************************************************************************* +** +** Function bta_av_switch_role +** +** Description Switch role was not started and a timer was started. +** another attempt to switch role now - still opening. +** +** Returns void +** +*******************************************************************************/ +void bta_av_switch_role (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data) +{ + tBTA_AV_RS_RES switch_res = BTA_AV_RS_NONE; + tBTA_AV_API_OPEN *p_buf = &p_scb->q_info.open; + UNUSED(p_data); + + APPL_TRACE_DEBUG("bta_av_switch_role wait:x%x", p_scb->wait); + if (p_scb->wait & BTA_AV_WAIT_ROLE_SW_RES_START) { + p_scb->wait |= BTA_AV_WAIT_ROLE_SW_RETRY; + } + + /* clear the masks set when the timer is started */ + p_scb->wait &= ~(BTA_AV_WAIT_ROLE_SW_RES_OPEN | BTA_AV_WAIT_ROLE_SW_RES_START); + + if (p_scb->q_tag == BTA_AV_Q_TAG_OPEN) { + if (bta_av_switch_if_needed(p_scb) || !bta_av_link_role_ok(p_scb, A2D_SET_MULTL_BIT)) { + p_scb->wait |= BTA_AV_WAIT_ROLE_SW_RES_OPEN; + } else { + /* this should not happen in theory. Just in case... + * continue to do_disc_a2d */ + switch_res = BTA_AV_RS_DONE; + } + } else { + /* report failure on OPEN */ + switch_res = BTA_AV_RS_FAIL; + } + + if (switch_res != BTA_AV_RS_NONE) { + if (bta_av_cb.rs_idx == (p_scb->hdi + 1)) { + bta_av_cb.rs_idx = 0; + } + p_scb->wait &= ~BTA_AV_WAIT_ROLE_SW_RETRY; + p_scb->q_tag = 0; + p_buf->switch_res = switch_res; + bta_av_do_disc_a2d(p_scb, (tBTA_AV_DATA *)p_buf); + } +} + +/******************************************************************************* +** +** Function bta_av_role_res +** +** Description Handle the role changed event +** +** +** Returns void +** +*******************************************************************************/ +void bta_av_role_res (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data) +{ + BOOLEAN initiator = FALSE; + tBTA_AV_START start; + tBTA_AV_OPEN av_open; + + APPL_TRACE_DEBUG("bta_av_role_res q_tag:%d, wait:x%x, role:x%x", p_scb->q_tag, p_scb->wait, p_scb->role); + if (p_scb->role & BTA_AV_ROLE_START_INT) { + initiator = TRUE; + } + + if (p_scb->q_tag == BTA_AV_Q_TAG_START) { + if (p_scb->wait & BTA_AV_WAIT_ROLE_SW_STARTED) { + p_scb->wait &= ~BTA_AV_WAIT_ROLE_SW_BITS; + if (p_data->role_res.hci_status != HCI_SUCCESS) { + p_scb->role &= ~BTA_AV_ROLE_START_INT; + bta_sys_idle(TSEP_TO_SYS_ID(p_scb->seps[p_scb->sep_idx].tsep), bta_av_cb.audio_open_cnt, p_scb->peer_addr); + /* start failed because of role switch. */ + start.chnl = p_scb->chnl; + start.status = BTA_AV_FAIL_ROLE; + start.hndl = p_scb->hndl; + start.initiator = initiator; + (*bta_av_cb.p_cback)(BTA_AV_START_EVT, (tBTA_AV *) &start); + } else { + bta_av_start_ok(p_scb, p_data); + } + } else if (p_scb->wait & BTA_AV_WAIT_ROLE_SW_RES_START) { + p_scb->wait |= BTA_AV_WAIT_ROLE_SW_FAILED; + } + } else if (p_scb->q_tag == BTA_AV_Q_TAG_OPEN) { + if (p_scb->wait & BTA_AV_WAIT_ROLE_SW_RES_OPEN) { + p_scb->role &= ~BTA_AV_ROLE_START_INT; + p_scb->wait &= ~BTA_AV_WAIT_ROLE_SW_BITS; + + if (p_data->role_res.hci_status != HCI_SUCCESS) { + /* Open failed because of role switch. */ + bdcpy(av_open.bd_addr, p_scb->peer_addr); + av_open.chnl = p_scb->chnl; + av_open.hndl = p_scb->hndl; + start.status = BTA_AV_FAIL_ROLE; + if (p_scb->seps[p_scb->sep_idx].tsep == AVDT_TSEP_SRC ) { + av_open.sep = AVDT_TSEP_SNK; + } else if (p_scb->seps[p_scb->sep_idx].tsep == AVDT_TSEP_SNK ) { + av_open.sep = AVDT_TSEP_SRC; + } + (*bta_av_cb.p_cback)(BTA_AV_OPEN_EVT, (tBTA_AV *)&av_open); + } else { + /* Continue av open process */ + p_scb->q_info.open.switch_res = BTA_AV_RS_DONE; + bta_av_do_disc_a2d (p_scb, (tBTA_AV_DATA *) & (p_scb->q_info.open)); + } + } else { + APPL_TRACE_WARNING ("Unexpected role switch event: q_tag = %d wait = %d", p_scb->q_tag, p_scb->wait); + } + } + + APPL_TRACE_DEBUG("wait:x%x, role:x%x", p_scb->wait, p_scb->role); +} + +/******************************************************************************* +** +** Function bta_av_delay_co +** +** Description Call the delay call-out function to report the delay report +** from SNK +** +** Returns void +** +*******************************************************************************/ +void bta_av_delay_co (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data) +{ + p_scb->p_cos->delay(p_scb->hndl, p_data->str_msg.msg.delay_rpt_cmd.delay); +} + +/******************************************************************************* +** +** Function bta_av_do_disc_a2d +** +** Description Do service discovery for A2DP. +** +** Returns void +** +*******************************************************************************/ +void bta_av_do_disc_a2d (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data) +{ + BOOLEAN ok_continue = FALSE; + tA2D_SDP_DB_PARAMS db_params; + UINT16 attr_list[] = {ATTR_ID_SERVICE_CLASS_ID_LIST, + ATTR_ID_PROTOCOL_DESC_LIST, + ATTR_ID_BT_PROFILE_DESC_LIST + }; + UINT16 sdp_uuid = 0; /* UUID for which SDP has to be done */ + + APPL_TRACE_DEBUG("bta_av_do_disc_a2d use_rc: %d rs:%d, oc:%d", + p_data->api_open.use_rc, p_data->api_open.switch_res, bta_av_cb.audio_open_cnt); + + memcpy (&(p_scb->open_api), &(p_data->api_open), sizeof(tBTA_AV_API_OPEN)); + + switch (p_data->api_open.switch_res) { + case BTA_AV_RS_NONE: + if (bta_av_switch_if_needed(p_scb) || !bta_av_link_role_ok(p_scb, A2D_SET_MULTL_BIT)) { + /* waiting for role switch result. save the api to control block */ + memcpy(&p_scb->q_info.open, &p_data->api_open, sizeof(tBTA_AV_API_OPEN)); + p_scb->wait |= BTA_AV_WAIT_ROLE_SW_RES_OPEN; + p_scb->q_tag = BTA_AV_Q_TAG_OPEN; + } else { + ok_continue = TRUE; + } + break; + + case BTA_AV_RS_FAIL: + /* report a new failure event */ + p_scb->open_status = BTA_AV_FAIL_ROLE; + bta_av_ssm_execute(p_scb, BTA_AV_SDP_DISC_FAIL_EVT, NULL); + break; + + case BTA_AV_RS_OK: + p_data = (tBTA_AV_DATA *)&p_scb->q_info.open; + /* continue to open if link role is ok */ + if (bta_av_link_role_ok(p_scb, A2D_SET_MULTL_BIT)) { + ok_continue = TRUE; + } else { + p_scb->wait |= BTA_AV_WAIT_ROLE_SW_RES_OPEN; + } + break; + + case BTA_AV_RS_DONE: + ok_continue = TRUE; + break; + } + + APPL_TRACE_DEBUG("ok_continue: %d wait:x%x, q_tag: %d", ok_continue, p_scb->wait, p_scb->q_tag); + if (!ok_continue) { + return; + } + + /* clear the role switch bits */ + p_scb->wait &= ~BTA_AV_WAIT_ROLE_SW_BITS; + + if (p_scb->wait & BTA_AV_WAIT_CHECK_RC) { + p_scb->wait &= ~BTA_AV_WAIT_CHECK_RC; + bta_sys_start_timer(&p_scb->timer, BTA_AV_AVRC_TIMER_EVT, BTA_AV_RC_DISC_TIME_VAL); + } + + if (bta_av_cb.features & BTA_AV_FEAT_MASTER) { + L2CA_SetDesireRole(L2CAP_ROLE_DISALLOW_SWITCH); + + if (bta_av_cb.audio_open_cnt == 1) { + /* there's already an A2DP connection. do not allow switch */ + bta_sys_clear_default_policy(BTA_ID_AV, HCI_ENABLE_MASTER_SLAVE_SWITCH); + } + } + /* store peer addr other parameters */ + bta_av_save_addr(p_scb, p_data->api_open.bd_addr); + p_scb->sec_mask = p_data->api_open.sec_mask; + p_scb->use_rc = p_data->api_open.use_rc; + + bta_sys_app_open(TSEP_TO_SYS_ID(p_scb->seps[p_scb->sep_idx].tsep), p_scb->app_id, p_scb->peer_addr); + + /* allocate discovery database */ + if (p_scb->p_disc_db == NULL) { + p_scb->p_disc_db = (tSDP_DISCOVERY_DB *) osi_malloc(BTA_AV_DISC_BUF_SIZE); + } + + /* only one A2D find service is active at a time */ + bta_av_cb.handle = p_scb->hndl; + + if (p_scb->p_disc_db) { + /* set up parameters */ + db_params.db_len = BTA_AV_DISC_BUF_SIZE; + db_params.num_attr = 3; + db_params.p_db = p_scb->p_disc_db; + db_params.p_attrs = attr_list; + p_scb->uuid_int = p_data->api_open.uuid; + if (p_scb->uuid_int == UUID_SERVCLASS_AUDIO_SINK) { + sdp_uuid = UUID_SERVCLASS_AUDIO_SOURCE; + } else if (p_scb->uuid_int == UUID_SERVCLASS_AUDIO_SOURCE) { + sdp_uuid = UUID_SERVCLASS_AUDIO_SINK; + } + + APPL_TRACE_DEBUG("uuid_int 0x%x, Doing SDP For 0x%x", p_scb->uuid_int, sdp_uuid); + if (A2D_FindService(sdp_uuid, p_scb->peer_addr, &db_params, + bta_av_a2d_sdp_cback) == A2D_SUCCESS) { + return; + } + } + + /* when the code reaches here, either the DB is NULL + * or A2D_FindService is not successful */ + bta_av_a2d_sdp_cback(FALSE, NULL); +} + +/******************************************************************************* +** +** Function bta_av_cleanup +** +** Description cleanup AV stream control block. +** +** Returns void +** +*******************************************************************************/ +void bta_av_cleanup(tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data) +{ + tBTA_AV_CONN_CHG msg; + int xx; + UINT8 role = BTA_AV_ROLE_AD_INT; + UNUSED(p_data); + + APPL_TRACE_DEBUG("bta_av_cleanup"); + + /* free any buffers */ + utl_freebuf((void **) &p_scb->p_cap); + utl_freebuf((void **) &p_scb->p_disc_db); + p_scb->avdt_version = 0; + + /* initialize some control block variables */ + p_scb->open_status = BTA_AV_SUCCESS; + + /* if de-registering shut everything down */ + msg.hdr.layer_specific = p_scb->hndl; + p_scb->started = FALSE; + p_scb->cong = FALSE; + p_scb->role = role; + p_scb->cur_psc_mask = 0; + p_scb->wait = 0; + p_scb->num_disc_snks = 0; + p_scb->disc_rsn = 0; + p_scb->avdt_handle = 0; + bta_sys_stop_timer(&p_scb->timer); + if (p_scb->deregistring) { + /* remove stream */ + for (xx = 0; xx < BTA_AV_MAX_SEPS; xx++) { + if (p_scb->seps[xx].av_handle) { + AVDT_RemoveStream(p_scb->seps[xx].av_handle); + } + p_scb->seps[xx].av_handle = 0; + } + + bta_av_dereg_comp((tBTA_AV_DATA *) &msg); + } else { + /* report stream closed to main SM */ + msg.is_up = FALSE; + bdcpy(msg.peer_addr, p_scb->peer_addr); + bta_av_conn_chg((tBTA_AV_DATA *) &msg); + } +} + +/******************************************************************************* +** +** Function bta_av_free_sdb +** +** Description Free service discovery db buffer. +** +** Returns void +** +*******************************************************************************/ +void bta_av_free_sdb(tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data) +{ + UNUSED(p_data); + utl_freebuf((void **) &p_scb->p_disc_db); +} + +/******************************************************************************* +** +** Function bta_av_open_fail_sdp +** +** Description report BTA_AV_OPEN_EVT with service discovery failed status +** +** Returns void +** +*******************************************************************************/ +void bta_av_open_fail_sdp(tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data) +{ + tBTA_AV_OPEN open; + + bdcpy(open.bd_addr, p_scb->peer_addr); + open.chnl = p_scb->chnl; + open.hndl = p_scb->hndl; + open.status = BTA_AV_FAIL_SDP; + + if (p_scb->seps[p_scb->sep_idx].tsep == AVDT_TSEP_SRC ) { + open.sep = AVDT_TSEP_SNK; + } else if (p_scb->seps[p_scb->sep_idx].tsep == AVDT_TSEP_SNK ) { + open.sep = AVDT_TSEP_SRC; + } + + (*bta_av_cb.p_cback)(BTA_AV_OPEN_EVT, (tBTA_AV *) &open); + + UNUSED(p_data); +} + +/******************************************************************************* +** +** Function bta_av_config_ind +** +** Description Handle a stream configuration indication from the peer. +** +** Returns void +** +*******************************************************************************/ +void bta_av_config_ind (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data) +{ + tBTA_AV_CI_SETCONFIG setconfig; + tAVDT_SEP_INFO *p_info; + tAVDT_CFG *p_evt_cfg = &p_data->str_msg.cfg; + UINT8 psc_mask = (p_evt_cfg->psc_mask | p_scb->cfg.psc_mask); + UINT8 local_sep; /* sep type of local handle on which connection was received */ + tBTA_AV_STR_MSG *p_msg = (tBTA_AV_STR_MSG *)p_data; + UNUSED(p_data); + + local_sep = bta_av_get_scb_sep_type(p_scb, p_msg->handle); + p_scb->avdt_label = p_data->str_msg.msg.hdr.label; + memcpy(p_scb->cfg.codec_info, p_evt_cfg->codec_info, AVDT_CODEC_SIZE); + p_scb->codec_type = p_evt_cfg->codec_info[BTA_AV_CODEC_TYPE_IDX]; + bta_av_save_addr(p_scb, p_data->str_msg.bd_addr); + + /* Clear collision mask */ + p_scb->coll_mask = 0; + bta_sys_stop_timer(&bta_av_cb.acp_sig_tmr); + + /* if no codec parameters in configuration, fail */ + if ((p_evt_cfg->num_codec == 0) || + /* or the peer requests for a service we do not support */ + ((psc_mask != p_scb->cfg.psc_mask) && + (psc_mask != (p_scb->cfg.psc_mask & ~AVDT_PSC_DELAY_RPT))) ) { + setconfig.hndl = p_scb->hndl; /* we may not need this */ + setconfig.err_code = AVDT_ERR_UNSUP_CFG; + bta_av_ssm_execute(p_scb, BTA_AV_CI_SETCONFIG_FAIL_EVT, (tBTA_AV_DATA *) &setconfig); + } else { + p_info = &p_scb->sep_info[0]; + p_info->in_use = 0; + p_info->media_type = p_scb->media_type; + p_info->seid = p_data->str_msg.msg.config_ind.int_seid; + + /* Sep type of Peer will be oppsite role to our local sep */ + if (local_sep == AVDT_TSEP_SRC) { + p_info->tsep = AVDT_TSEP_SNK; + } else if (local_sep == AVDT_TSEP_SNK) { + p_info->tsep = AVDT_TSEP_SRC; + } + + p_scb->role |= BTA_AV_ROLE_AD_ACP; + p_scb->cur_psc_mask = p_evt_cfg->psc_mask; + if (bta_av_cb.features & BTA_AV_FEAT_RCTG) { + p_scb->use_rc = TRUE; + } else { + p_scb->use_rc = FALSE; + } + + p_scb->num_seps = 1; + p_scb->sep_info_idx = 0; + APPL_TRACE_DEBUG("bta_av_config_ind: SEID: %d use_rc: %d cur_psc_mask:0x%x", p_info->seid, p_scb->use_rc, p_scb->cur_psc_mask); + /* in case of A2DP SINK this is the first time peer data is being sent to co functions */ + if (local_sep == AVDT_TSEP_SNK) { + p_scb->p_cos->setcfg(p_scb->hndl, p_scb->codec_type, + p_evt_cfg->codec_info, + p_info->seid, + p_scb->peer_addr, + p_evt_cfg->num_protect, + p_evt_cfg->protect_info, + AVDT_TSEP_SNK, + p_msg->handle); + } else { + p_scb->p_cos->setcfg(p_scb->hndl, p_scb->codec_type, + p_evt_cfg->codec_info, + p_info->seid, + p_scb->peer_addr, + p_evt_cfg->num_protect, + p_evt_cfg->protect_info, + AVDT_TSEP_SRC, + p_msg->handle); + } + } +} + +/******************************************************************************* +** +** Function bta_av_disconnect_req +** +** Description Disconnect AVDTP connection. +** +** Returns void +** +*******************************************************************************/ +void bta_av_disconnect_req (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data) +{ + tBTA_AV_RCB *p_rcb; + UNUSED(p_data); + + APPL_TRACE_DEBUG("bta_av_disconnect_req conn_lcb: 0x%x", bta_av_cb.conn_lcb); + + bta_sys_stop_timer(&bta_av_cb.sig_tmr); + bta_sys_stop_timer(&p_scb->timer); + if (bta_av_cb.conn_lcb) { + p_rcb = bta_av_get_rcb_by_shdl((UINT8)(p_scb->hdi + 1)); + if (p_rcb) { + bta_av_del_rc(p_rcb); + } + AVDT_DisconnectReq(p_scb->peer_addr, bta_av_dt_cback[p_scb->hdi]); + } else { + bta_av_ssm_execute(p_scb, BTA_AV_AVDT_DISCONNECT_EVT, NULL); + } +} + +/******************************************************************************* +** +** Function bta_av_security_req +** +** Description Send an AVDTP security request. +** +** Returns void +** +*******************************************************************************/ +void bta_av_security_req (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data) +{ + if (bta_av_cb.features & BTA_AV_FEAT_PROTECT) { + AVDT_SecurityReq(p_scb->avdt_handle, p_data->api_protect_req.p_data, + p_data->api_protect_req.len); + } +} + +/******************************************************************************* +** +** Function bta_av_security_rsp +** +** Description Send an AVDTP security response. +** +** Returns void +** +*******************************************************************************/ +void bta_av_security_rsp (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data) +{ + if (bta_av_cb.features & BTA_AV_FEAT_PROTECT) { + AVDT_SecurityRsp(p_scb->avdt_handle, p_scb->avdt_label, p_data->api_protect_rsp.error_code, + p_data->api_protect_rsp.p_data, p_data->api_protect_rsp.len); + } else { + AVDT_SecurityRsp(p_scb->avdt_handle, p_scb->avdt_label, AVDT_ERR_NSC, + NULL, 0); + } +} + +/******************************************************************************* +** +** Function bta_av_setconfig_rsp +** +** Description setconfig is OK +** +** Returns void +** +*******************************************************************************/ +void bta_av_setconfig_rsp (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data) +{ + UINT8 num = p_data->ci_setconfig.num_seid + 1; + UINT8 avdt_handle = p_data->ci_setconfig.avdt_handle; + UINT8 *p_seid = p_data->ci_setconfig.p_seid; + int i; + UINT8 local_sep; + tBTA_AV_SNK_PSC_CFG psc_cfg = {0}; + + /* we like this codec_type. find the sep_idx */ + local_sep = bta_av_get_scb_sep_type(p_scb, avdt_handle); + bta_av_adjust_seps_idx(p_scb, avdt_handle); + APPL_TRACE_DEBUG("bta_av_setconfig_rsp: sep_idx: %d cur_psc_mask:0x%x", p_scb->sep_idx, p_scb->cur_psc_mask); + + if (AVDT_TSEP_SNK == local_sep) { + if ((p_data->ci_setconfig.err_code == AVDT_SUCCESS) && + (p_scb->seps[p_scb->sep_idx].p_app_data_cback != NULL)) { + p_scb->seps[p_scb->sep_idx].p_app_data_cback(BTA_AV_MEDIA_SINK_CFG_EVT, + (tBTA_AV_MEDIA *)p_scb->cfg.codec_info); + } + if (p_scb->cur_psc_mask & AVDT_PSC_DELAY_RPT) { + psc_cfg.psc_mask |= BTA_AV_PSC_DEALY_RPT; + } + (*bta_av_cb.p_cback)(BTA_AV_SNK_PSC_CFG_EVT, (tBTA_AV *)&psc_cfg); + } + + + AVDT_ConfigRsp(p_scb->avdt_handle, p_scb->avdt_label, p_data->ci_setconfig.err_code, + p_data->ci_setconfig.category); + + bta_sys_stop_timer(&bta_av_cb.sig_tmr); + + if (p_data->ci_setconfig.err_code == AVDT_SUCCESS) { + p_scb->wait = BTA_AV_WAIT_ACP_CAPS_ON; + if (p_data->ci_setconfig.recfg_needed) { + p_scb->role |= BTA_AV_ROLE_SUSPEND_OPT; + } + APPL_TRACE_DEBUG("bta_av_setconfig_rsp recfg_needed:%d role:x%x num:%d", + p_data->ci_setconfig.recfg_needed, p_scb->role, num); + /* callout module tells BTA the number of "good" SEPs and their SEIDs. + * getcap on these SEID */ + p_scb->num_seps = num; + + if (p_scb->cur_psc_mask & AVDT_PSC_DELAY_RPT) { + p_scb->avdt_version = AVDT_VERSION_SYNC; + } + + + if (p_scb->codec_type == BTA_AV_CODEC_SBC || num > 1) { + /* if SBC is used by the SNK as INT, discover req is not sent in bta_av_config_ind. + * call cfg_res now */ + /* this is called in A2DP SRC path only, In case of SINK we don't need it */ + if (local_sep == AVDT_TSEP_SRC) { + p_scb->p_cos->cfg_res(p_scb->hndl, num, num, 0, p_scb->peer_addr, + UUID_SERVCLASS_AUDIO_SOURCE); + } + } else { + /* we do not know the peer device and it is using non-SBC codec + * we need to know all the SEPs on SNK */ + bta_av_discover_req(p_scb, NULL); + return; + } + + for (i = 1; i < num; i++) { + APPL_TRACE_DEBUG("sep_info[%d] SEID: %d", i, p_seid[i - 1]); + /* initialize the sep_info[] to get capabilities */ + p_scb->sep_info[i].in_use = FALSE; + p_scb->sep_info[i].tsep = AVDT_TSEP_SNK; + p_scb->sep_info[i].media_type = p_scb->media_type; + p_scb->sep_info[i].seid = p_seid[i - 1]; + } + + /* only in case of local sep as SRC we need to look for other SEPs, In case of SINK we don't */ + if (local_sep == AVDT_TSEP_SRC) { + /* Make sure UUID has been initialized... */ + if (p_scb->uuid_int == 0) { + p_scb->uuid_int = p_scb->open_api.uuid; + } + bta_av_next_getcap(p_scb, p_data); + } + } +} + +/******************************************************************************* +** +** Function bta_av_str_opened +** +** Description Stream opened OK (incoming/outgoing). +** +** Returns void +** +*******************************************************************************/ +void bta_av_str_opened (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data) +{ + tBTA_AV_CONN_CHG msg; + tBTA_AV_OPEN open; + UINT8 *p; + UINT16 mtu; + + msg.hdr.layer_specific = p_scb->hndl; + msg.is_up = TRUE; + bdcpy(msg.peer_addr, p_scb->peer_addr); + p_scb->l2c_cid = AVDT_GetL2CapChannel(p_scb->avdt_handle); + bta_av_conn_chg((tBTA_AV_DATA *) &msg); + /* set the congestion flag, so AV would not send media packets by accident */ + p_scb->cong = TRUE; + + + p_scb->stream_mtu = p_data->str_msg.msg.open_ind.peer_mtu - AVDT_MEDIA_HDR_SIZE; + mtu = bta_av_chk_mtu(p_scb, p_scb->stream_mtu); + APPL_TRACE_DEBUG("bta_av_str_opened l2c_cid: 0x%x stream_mtu: %d mtu: %d", + p_scb->l2c_cid, p_scb->stream_mtu, mtu); + if (mtu == 0 || mtu > p_scb->stream_mtu) { + mtu = p_scb->stream_mtu; + } + + /* Set the media channel as medium priority */ + L2CA_SetTxPriority(p_scb->l2c_cid, L2CAP_CHNL_PRIORITY_MEDIUM); + L2CA_SetChnlFlushability (p_scb->l2c_cid, TRUE); + + bta_sys_conn_open(TSEP_TO_SYS_ID(p_scb->seps[p_scb->sep_idx].tsep), p_scb->app_id, p_scb->peer_addr); + memset(&p_scb->q_info, 0, sizeof(tBTA_AV_Q_INFO)); + + p_scb->l2c_bufs = 0; + p_scb->p_cos->open(p_scb->hndl, + p_scb->codec_type, p_scb->cfg.codec_info, mtu); + + { + /* TODO check if other audio channel is open. + * If yes, check if reconfig is needed + * Rigt now we do not do this kind of checking. + * BTA-AV is INT for 2nd audio connection. + * The application needs to make sure the current codec_info is proper. + * If one audio connection is open and another SNK attempts to connect to AV, + * the connection will be rejected. + */ + /* check if other audio channel is started. If yes, start */ + bdcpy(open.bd_addr, p_scb->peer_addr); + open.chnl = p_scb->chnl; + open.hndl = p_scb->hndl; + open.status = BTA_AV_SUCCESS; + open.starting = bta_av_chk_start(p_scb); + open.edr = 0; + if ( NULL != (p = BTM_ReadRemoteFeatures(p_scb->peer_addr))) { + if (HCI_EDR_ACL_2MPS_SUPPORTED(p)) { + open.edr |= BTA_AV_EDR_2MBPS; + } + if (HCI_EDR_ACL_3MPS_SUPPORTED(p)) { + open.edr |= BTA_AV_EDR_3MBPS; + } + } +#if( defined BTA_AR_INCLUDED ) && (BTA_AR_INCLUDED == TRUE) + bta_ar_avdt_conn(BTA_ID_AV, open.bd_addr); +#endif + if (p_scb->seps[p_scb->sep_idx].tsep == AVDT_TSEP_SRC ) { + open.sep = AVDT_TSEP_SNK; + } else if (p_scb->seps[p_scb->sep_idx].tsep == AVDT_TSEP_SNK ) { + open.sep = AVDT_TSEP_SRC; + } + + (*bta_av_cb.p_cback)(BTA_AV_OPEN_EVT, (tBTA_AV *) &open); + if (open.starting) { + bta_av_ssm_execute(p_scb, BTA_AV_AP_START_EVT, NULL); + } + } + +#if A2D_SRC_BQB_INCLUDED + s_avdt_bqb_handle = p_scb->avdt_handle; +#endif /* A2D_SRC_BQB_INCLUDED */ + +#if 0 /* TODO: implement the property enable/disable */ + // This code is used to pass PTS TC for AVDTP ABORT + char value[PROPERTY_VALUE_MAX] = {0}; + if ((property_get("bluetooth.pts.force_a2dp_abort", value, "false")) + && (!strcmp(value, "true"))) { + APPL_TRACE_ERROR ("%s: Calling AVDT_AbortReq", __func__); + AVDT_AbortReq(p_scb->avdt_handle); + } +#endif /* #if 0*/ +} + +/******************************************************************************* +** +** Function avdt_bqb_abort +** +** Description Send AVDT abort request for BQB test +** +** Returns void +** +*******************************************************************************/ +#if A2D_SRC_BQB_INCLUDED +void avdt_bqb_abort(void) +{ + AVDT_AbortReq(s_avdt_bqb_handle); +} +#endif /* A2D_SRC_BQB_INCLUDED */ + +/******************************************************************************* +** +** Function bta_av_security_ind +** +** Description Handle an AVDTP security indication. +** +** Returns void +** +*******************************************************************************/ +void bta_av_security_ind (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data) +{ + tBTA_AV_PROTECT_REQ protect_req; + + p_scb->avdt_label = p_data->str_msg.msg.hdr.label; + + if (bta_av_cb.features & BTA_AV_FEAT_PROTECT) { + protect_req.chnl = p_scb->chnl; + protect_req.hndl = p_scb->hndl; + /* + APPL_TRACE_EVENT("sec ind handle: x%x", protect_req.hndl); + */ + protect_req.p_data = p_data->str_msg.msg.security_ind.p_data; + protect_req.len = p_data->str_msg.msg.security_ind.len; + + (*bta_av_cb.p_cback)(BTA_AV_PROTECT_REQ_EVT, (tBTA_AV *) &protect_req); + } + /* app doesn't support security indication; respond with failure */ + else { + AVDT_SecurityRsp(p_scb->avdt_handle, p_scb->avdt_label, AVDT_ERR_NSC, NULL, 0); + } +} + +/******************************************************************************* +** +** Function bta_av_security_cfm +** +** Description Handle an AVDTP security confirm. +** +** Returns void +** +*******************************************************************************/ +void bta_av_security_cfm (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data) +{ + tBTA_AV_PROTECT_RSP protect_rsp; + + if (bta_av_cb.features & BTA_AV_FEAT_PROTECT) { + protect_rsp.chnl = p_scb->chnl; + protect_rsp.hndl = p_scb->hndl; + protect_rsp.p_data = p_data->str_msg.msg.security_cfm.p_data; + protect_rsp.len = p_data->str_msg.msg.security_cfm.len; + protect_rsp.err_code = p_data->str_msg.msg.hdr.err_code; + + (*bta_av_cb.p_cback)(BTA_AV_PROTECT_RSP_EVT, (tBTA_AV *) &protect_rsp); + } +} + +/******************************************************************************* +** +** Function bta_av_do_close +** +** Description Close stream. +** +** Returns void +** +*******************************************************************************/ +void bta_av_do_close (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data) +{ + UNUSED(p_data); + + /* stop stream if started */ + if (p_scb->co_started) { + bta_av_str_stopped(p_scb, NULL); + } + bta_sys_stop_timer(&bta_av_cb.sig_tmr); + + /* close stream */ + p_scb->started = FALSE; + + /* drop the buffers queued in L2CAP */ + L2CA_FlushChannel (p_scb->l2c_cid, L2CAP_FLUSH_CHANS_ALL); + + AVDT_CloseReq(p_scb->avdt_handle); + /* just in case that the link is congested, link is flow controled by peer or + * for whatever reason the the close request can not be sent in time. + * when this timer expires, AVDT_DisconnectReq will be called to disconnect the link + */ + bta_sys_start_timer(&p_scb->timer, + (UINT16)BTA_AV_API_CLOSE_EVT, + BTA_AV_CLOSE_REQ_TIME_VAL); + +} + +/******************************************************************************* +** +** Function bta_av_connect_req +** +** Description Connect AVDTP connection. +** +** Returns void +** +*******************************************************************************/ +void bta_av_connect_req (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data) +{ + UNUSED(p_data); + + utl_freebuf((void **) &p_scb->p_disc_db); + + if (p_scb->coll_mask & BTA_AV_COLL_INC_TMR) { + /* SNK initiated L2C connection while SRC was doing SDP. */ + /* Wait until timeout to check if SNK starts signalling. */ + APPL_TRACE_EVENT("bta_av_connect_req: coll_mask = 0x%2X", p_scb->coll_mask); + return; + } + + AVDT_ConnectReq(p_scb->peer_addr, p_scb->sec_mask, bta_av_dt_cback[p_scb->hdi]); +} + +/******************************************************************************* +** +** Function bta_av_sdp_failed +** +** Description Service discovery failed. +** +** Returns void +** +*******************************************************************************/ +void bta_av_sdp_failed (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data) +{ + if (!p_scb->open_status) { + p_scb->open_status = BTA_AV_FAIL_SDP; + } + + utl_freebuf((void **) &p_scb->p_disc_db); + bta_av_str_closed(p_scb, p_data); +} + +/******************************************************************************* +** +** Function bta_av_disc_results +** +** Description Handle the AVDTP discover results. Search through the +** results and find the first available stream, and get +** its capabilities. +** +** Returns void +** +*******************************************************************************/ +void bta_av_disc_results (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data) +{ + UINT8 num_snks = 0, num_srcs = 0, i; + /* our uuid in case we initiate connection */ + UINT16 uuid_int = p_scb->uuid_int; + + APPL_TRACE_DEBUG(" initiator UUID 0x%x", uuid_int); + /* store number of stream endpoints returned */ + p_scb->num_seps = p_data->str_msg.msg.discover_cfm.num_seps; + + UINT8 num_seps = (p_scb->num_seps < BTA_AV_NUM_SEPS) ? p_scb->num_seps : BTA_AV_NUM_SEPS; + memcpy(p_scb->sep_info, p_data->str_msg.msg.discover_cfm.p_sep_info, sizeof(tAVDT_SEP_INFO) * num_seps); + for (i = 0; i < p_data->str_msg.msg.discover_cfm.num_seps; i++) { + APPL_TRACE_DEBUG("peer sep %d, in use %d, seid %d, media type %d, tsep %d", + i, + p_data->str_msg.msg.discover_cfm.p_sep_info[i].in_use, + p_data->str_msg.msg.discover_cfm.p_sep_info[i].seid, + p_data->str_msg.msg.discover_cfm.p_sep_info[i].media_type, + p_data->str_msg.msg.discover_cfm.p_sep_info[i].tsep + ); + } + for (i = 0; i < p_scb->num_seps; i++) { + /* steam not in use, is a sink, and is audio */ + if ((p_scb->sep_info[i].in_use == FALSE) && + (p_scb->sep_info[i].media_type == p_scb->media_type)) { + if ((p_scb->sep_info[i].tsep == AVDT_TSEP_SNK) && + (uuid_int == UUID_SERVCLASS_AUDIO_SOURCE)) { + num_snks++; + } + + if ((p_scb->sep_info[i].tsep == AVDT_TSEP_SRC) && + (uuid_int == UUID_SERVCLASS_AUDIO_SINK)) { + num_srcs++; + } + APPL_TRACE_DEBUG("num srcs: %d, num_snks: %d\n", num_snks, num_srcs); + } + } + + p_scb->p_cos->disc_res(p_scb->hndl, p_scb->num_seps, num_snks, num_srcs, p_scb->peer_addr, + uuid_int); + p_scb->num_disc_snks = num_snks; + p_scb->num_disc_srcs = num_srcs; + + /* if we got any */ + if (p_scb->num_seps > 0) { + /* initialize index into discovery results */ + p_scb->sep_info_idx = 0; + + /* get the capabilities of the first available stream */ + bta_av_next_getcap(p_scb, p_data); + } + /* else we got discover response but with no streams; we're done */ + else { + bta_av_ssm_execute(p_scb, BTA_AV_STR_DISC_FAIL_EVT, p_data); + } +} + +/******************************************************************************* +** +** Function bta_av_disc_res_as_acp +** +** Description Handle the AVDTP discover results. Search through the +** results and find the first available stream, and get +** its capabilities. +** +** Returns void +** +*******************************************************************************/ +void bta_av_disc_res_as_acp (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data) +{ + UINT8 num_snks = 0, i; + + /* store number of stream endpoints returned */ + p_scb->num_seps = p_data->str_msg.msg.discover_cfm.num_seps; + + + + for (i = 0; i < p_scb->num_seps; i++) { + /* steam is a sink, and is audio */ + if ((p_scb->sep_info[i].tsep == AVDT_TSEP_SNK) && + (p_scb->sep_info[i].media_type == p_scb->media_type)) { + p_scb->sep_info[i].in_use = FALSE; + num_snks++; + } + } + p_scb->p_cos->disc_res(p_scb->hndl, p_scb->num_seps, num_snks, 0, p_scb->peer_addr, + UUID_SERVCLASS_AUDIO_SOURCE); + p_scb->num_disc_snks = num_snks; + p_scb->num_disc_srcs = 0; + + /* if we got any */ + if (p_scb->num_seps > 0) { + /* initialize index into discovery results */ + p_scb->sep_info_idx = 0; + + /* get the capabilities of the first available stream */ + bta_av_next_getcap(p_scb, p_data); + } + /* else we got discover response but with no streams; we're done */ + else { + bta_av_ssm_execute(p_scb, BTA_AV_STR_DISC_FAIL_EVT, p_data); + } +} + +/******************************************************************************* +** +** Function bta_av_save_caps +** +** Description report the SNK SEP capabilities to application +** +** Returns void +** +*******************************************************************************/ +void bta_av_save_caps(tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data) +{ + tAVDT_CFG cfg; + tAVDT_SEP_INFO *p_info = &p_scb->sep_info[p_scb->sep_info_idx]; + UINT8 old_wait = p_scb->wait; + BOOLEAN getcap_done = FALSE; + + APPL_TRACE_DEBUG("bta_av_save_caps num_seps:%d sep_info_idx:%d wait:x%x", + p_scb->num_seps, p_scb->sep_info_idx, p_scb->wait); + memcpy(&cfg, p_scb->p_cap, sizeof(tAVDT_CFG)); + /* let application know the capability of the SNK */ + p_scb->p_cos->getcfg(p_scb->hndl, cfg.codec_info[BTA_AV_CODEC_TYPE_IDX], + cfg.codec_info, &p_scb->sep_info_idx, p_info->seid, + &cfg.num_protect, cfg.protect_info); + + p_scb->sep_info_idx++; + if (p_scb->num_seps > p_scb->sep_info_idx) { + /* Some devices have seps at the end of the discover list, which is not */ + /* matching media type(video not audio). */ + /* In this case, we are done with getcap without sending another */ + /* request to AVDT. */ + if (!bta_av_next_getcap(p_scb, p_data)) { + getcap_done = TRUE; + } + } else { + getcap_done = TRUE; + } + + if (getcap_done) { + /* we are done getting capabilities. restore the p_cb->sep_info_idx */ + p_scb->sep_info_idx = 0; + p_scb->wait &= ~(BTA_AV_WAIT_ACP_CAPS_ON | BTA_AV_WAIT_ACP_CAPS_STARTED); + if (old_wait & BTA_AV_WAIT_ACP_CAPS_STARTED) { + bta_av_start_ok (p_scb, NULL); + } + } +} + +/******************************************************************************* +** +** Function bta_av_set_use_rc +** +** Description set to use AVRC for this stream control block. +** +** Returns void +** +*******************************************************************************/ +void bta_av_set_use_rc (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data) +{ + UNUSED(p_data); + + p_scb->use_rc = TRUE; +} + +/******************************************************************************* +** +** Function bta_av_cco_close +** +** Description call close call-out function. +** +** Returns void +** +*******************************************************************************/ +void bta_av_cco_close (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data) +{ + UINT16 mtu; + UNUSED(p_data); + + mtu = bta_av_chk_mtu(p_scb, BTA_AV_MAX_A2DP_MTU); + + p_scb->p_cos->close(p_scb->hndl, p_scb->codec_type, mtu); +} + +/******************************************************************************* +** +** Function bta_av_open_failed +** +** Description Failed to open an AVDT stream +** +** Returns void +** +*******************************************************************************/ +void bta_av_open_failed (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data) +{ + + BOOLEAN is_av_opened = FALSE; + tBTA_AV_SCB *p_opened_scb = NULL; + UINT8 idx; + tBTA_AV_OPEN open; + + APPL_TRACE_DEBUG("bta_av_open_failed"); + p_scb->open_status = BTA_AV_FAIL_STREAM; + bta_av_cco_close(p_scb, p_data); + + /* check whether there is already an opened audio or video connection with the same device */ + for (idx = 0; (idx < BTA_AV_NUM_STRS) && (is_av_opened == FALSE); idx++ ) { + p_opened_scb = bta_av_cb.p_scb[idx]; + if (p_opened_scb && (p_opened_scb->state == BTA_AV_OPEN_SST) && (!bdcmp(p_opened_scb->peer_addr, p_scb->peer_addr )) ) { + is_av_opened = TRUE; + } + + } + + /* if there is already an active AV connnection with the same bd_addr, + don't send disconnect req, just report the open event with BTA_AV_FAIL_GET_CAP status */ + if (is_av_opened == TRUE) { + bdcpy(open.bd_addr, p_scb->peer_addr); + open.chnl = p_scb->chnl; + open.hndl = p_scb->hndl; + open.status = BTA_AV_FAIL_GET_CAP; + open.starting = bta_av_chk_start(p_scb); + open.edr = 0; + /* set the state back to initial state */ + bta_av_set_scb_sst_init(p_scb); + + if (p_scb->seps[p_scb->sep_idx].tsep == AVDT_TSEP_SRC ) { + open.sep = AVDT_TSEP_SNK; + } else if (p_scb->seps[p_scb->sep_idx].tsep == AVDT_TSEP_SNK ) { + open.sep = AVDT_TSEP_SRC; + } + + (*bta_av_cb.p_cback)(BTA_AV_OPEN_EVT, (tBTA_AV *) &open); + + } else { + AVDT_DisconnectReq(p_scb->peer_addr, bta_av_dt_cback[p_scb->hdi]); + } +} + + +/******************************************************************************* +** +** Function bta_av_getcap_results +** +** Description Handle the AVDTP get capabilities results. Check the codec +** type and see if it matches ours. If it does not, get the +** capabilities of the next stream, if any. +** +** Returns void +** +*******************************************************************************/ +void bta_av_getcap_results (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data) +{ + tAVDT_CFG cfg; + UINT8 media_type; + tAVDT_SEP_INFO *p_info = &p_scb->sep_info[p_scb->sep_info_idx]; + UINT16 uuid_int; /* UUID for which connection was initiatied */ + tBTA_AV_SNK_PSC_CFG psc_cfg = {0}; + + memcpy(&cfg, &p_scb->cfg, sizeof(tAVDT_CFG)); + cfg.num_codec = 1; + cfg.num_protect = p_scb->p_cap->num_protect; + memcpy(cfg.codec_info, p_scb->p_cap->codec_info, AVDT_CODEC_SIZE); + memcpy(cfg.protect_info, p_scb->p_cap->protect_info, AVDT_PROTECT_SIZE); + media_type = p_scb->p_cap->codec_info[BTA_AV_MEDIA_TYPE_IDX] >> 4; + + APPL_TRACE_DEBUG("num_codec %d", p_scb->p_cap->num_codec); + APPL_TRACE_DEBUG("media type x%x, x%x", media_type, p_scb->media_type); +#if AVDT_MULTIPLEXING == TRUE + APPL_TRACE_DEBUG("mux x%x, x%x", cfg.mux_mask, p_scb->p_cap->mux_mask); +#endif + + /* if codec present and we get a codec configuration */ + if ((p_scb->p_cap->num_codec != 0) && + (media_type == p_scb->media_type) && + (p_scb->p_cos->getcfg(p_scb->hndl, p_scb->p_cap->codec_info[BTA_AV_CODEC_TYPE_IDX], + cfg.codec_info, &p_scb->sep_info_idx, p_info->seid, + &cfg.num_protect, cfg.protect_info) == 0)) { +#if AVDT_MULTIPLEXING == TRUE + cfg.mux_mask &= p_scb->p_cap->mux_mask; + APPL_TRACE_DEBUG("mux_mask used x%x", cfg.mux_mask); +#endif + /* save copy of codec type and configuration */ + p_scb->codec_type = cfg.codec_info[BTA_AV_CODEC_TYPE_IDX]; + memcpy(&p_scb->cfg, &cfg, sizeof(tAVDT_CFG)); + + uuid_int = p_scb->uuid_int; + APPL_TRACE_DEBUG(" initiator UUID = 0x%x ", uuid_int); + if (uuid_int == UUID_SERVCLASS_AUDIO_SOURCE) { + bta_av_adjust_seps_idx(p_scb, bta_av_get_scb_handle(p_scb, AVDT_TSEP_SRC)); + } else if (uuid_int == UUID_SERVCLASS_AUDIO_SINK) { + bta_av_adjust_seps_idx(p_scb, bta_av_get_scb_handle(p_scb, AVDT_TSEP_SNK)); + } + + /* use only the services peer supports */ + cfg.psc_mask &= p_scb->p_cap->psc_mask; + p_scb->cur_psc_mask = cfg.psc_mask; + + if (uuid_int == UUID_SERVCLASS_AUDIO_SINK) { + if (p_scb->seps[p_scb->sep_idx].p_app_data_cback != NULL) { + APPL_TRACE_DEBUG(" Configure Deoder for Sink Connection "); + p_scb->seps[p_scb->sep_idx].p_app_data_cback(BTA_AV_MEDIA_SINK_CFG_EVT, + (tBTA_AV_MEDIA *)p_scb->cfg.codec_info); + } + if (p_scb->cur_psc_mask & AVDT_PSC_DELAY_RPT) { + psc_cfg.psc_mask |= BTA_AV_PSC_DEALY_RPT; + } + (*bta_av_cb.p_cback)(BTA_AV_SNK_PSC_CFG_EVT, (tBTA_AV *)&psc_cfg); + } + + /* open the stream */ + AVDT_OpenReq(p_scb->seps[p_scb->sep_idx].av_handle, p_scb->peer_addr, + p_scb->sep_info[p_scb->sep_info_idx].seid, &cfg); + + if (!bta_av_is_rcfg_sst(p_scb)) { + /* free capabilities buffer */ + utl_freebuf((void **) &p_scb->p_cap); + } + } else { + /* try the next stream, if any */ + p_scb->sep_info_idx++; + bta_av_next_getcap(p_scb, p_data); + } +} + +/******************************************************************************* +** +** Function bta_av_setconfig_rej +** +** Description Send AVDTP set config reject. +** +** Returns void +** +*******************************************************************************/ +void bta_av_setconfig_rej (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data) +{ + tBTA_AV_REJECT reject; + UINT8 avdt_handle = p_data->ci_setconfig.avdt_handle; + + bta_av_adjust_seps_idx(p_scb, avdt_handle); + APPL_TRACE_DEBUG("bta_av_setconfig_rej: sep_idx: %d", p_scb->sep_idx); + AVDT_ConfigRsp(p_scb->avdt_handle, p_scb->avdt_label, p_data->ci_setconfig.err_code, 0); + + bdcpy(reject.bd_addr, p_data->str_msg.bd_addr); + reject.hndl = p_scb->hndl; + (*bta_av_cb.p_cback)(BTA_AV_REJECT_EVT, (tBTA_AV *) &reject); +} + +/******************************************************************************* +** +** Function bta_av_discover_req +** +** Description Send an AVDTP discover request to the peer. +** +** Returns void +** +*******************************************************************************/ +void bta_av_discover_req (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data) +{ + UNUSED(p_data); + + /* send avdtp discover request */ + + AVDT_DiscoverReq(p_scb->peer_addr, p_scb->sep_info, BTA_AV_NUM_SEPS, bta_av_dt_cback[p_scb->hdi]); +} + +/******************************************************************************* +** +** Function bta_av_conn_failed +** +** Description AVDTP connection failed. +** +** Returns void +** +*******************************************************************************/ +void bta_av_conn_failed (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data) +{ + p_scb->open_status = BTA_AV_FAIL_STREAM; + bta_av_str_closed(p_scb, p_data); +} + +/******************************************************************************* +** +** Function bta_av_do_start +** +** Description Start stream. +** +** Returns void +** +*******************************************************************************/ +void bta_av_do_start (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data) +{ + UINT8 policy = HCI_ENABLE_SNIFF_MODE; + UINT8 cur_role; + + APPL_TRACE_DEBUG("bta_av_do_start sco_occupied:%d, role:x%x, started:%d", bta_av_cb.sco_occupied, p_scb->role, p_scb->started); + if (bta_av_cb.sco_occupied) { + bta_av_start_failed(p_scb, p_data); + return; + } + + /* disallow role switch during streaming, only if we are the master role + * i.e. allow role switch, if we are slave. + * It would not hurt us, if the peer device wants us to be master */ + if ((BTM_GetRole (p_scb->peer_addr, &cur_role) == BTM_SUCCESS) && + (cur_role == BTM_ROLE_MASTER) ) { + policy |= HCI_ENABLE_MASTER_SLAVE_SWITCH; + } + + bta_sys_clear_policy(BTA_ID_AV, policy, p_scb->peer_addr); + + if ((p_scb->started == FALSE) && ((p_scb->role & BTA_AV_ROLE_START_INT) == 0)) { + p_scb->role |= BTA_AV_ROLE_START_INT; + bta_sys_busy(TSEP_TO_SYS_ID(p_scb->seps[p_scb->sep_idx].tsep), bta_av_cb.audio_open_cnt, p_scb->peer_addr); + AVDT_StartReq(&p_scb->avdt_handle, 1); + } else if (p_scb->started) { + p_scb->role |= BTA_AV_ROLE_START_INT; + if ( p_scb->wait == 0 ) { + if (p_scb->role & BTA_AV_ROLE_SUSPEND) { + notify_start_failed(p_scb); + } else { + bta_av_start_ok(p_scb, NULL); + } + } + } + APPL_TRACE_DEBUG("started %d role:x%x", p_scb->started, p_scb->role); +} + +/******************************************************************************* +** +** Function bta_av_str_stopped +** +** Description Stream stopped. +** +** Returns void +** +*******************************************************************************/ +void bta_av_str_stopped (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data) +{ + tBTA_AV_SUSPEND suspend_rsp; + UINT8 start = p_scb->started; + BOOLEAN sus_evt = TRUE; + BT_HDR *p_buf; + UINT8 policy = HCI_ENABLE_SNIFF_MODE; + + APPL_TRACE_DEBUG("bta_av_str_stopped:audio_open_cnt=%d, p_data %p", + bta_av_cb.audio_open_cnt, p_data); + + bta_sys_idle(TSEP_TO_SYS_ID(p_scb->seps[p_scb->sep_idx].tsep), bta_av_cb.audio_open_cnt, p_scb->peer_addr); + if ((bta_av_cb.features & BTA_AV_FEAT_MASTER) == 0 || bta_av_cb.audio_open_cnt == 1) { + policy |= HCI_ENABLE_MASTER_SLAVE_SWITCH; + } + bta_sys_set_policy(BTA_ID_AV, policy, p_scb->peer_addr); + + if (p_scb->co_started) { + bta_av_stream_chg(p_scb, FALSE); + p_scb->co_started = FALSE; + + p_scb->p_cos->stop(p_scb->hndl, p_scb->codec_type); + L2CA_SetFlushTimeout(p_scb->peer_addr, L2CAP_DEFAULT_FLUSH_TO); + } + + /* if q_info.a2d_list is not empty, drop it now */ + if (BTA_AV_CHNL_AUDIO == p_scb->chnl) { + while (!list_is_empty(p_scb->a2d_list)) { + p_buf = (BT_HDR *)list_front(p_scb->a2d_list); + list_remove(p_scb->a2d_list, p_buf); + osi_free(p_buf); + } + + /* drop the audio buffers queued in L2CAP */ + if (p_data && p_data->api_stop.flush) { + L2CA_FlushChannel (p_scb->l2c_cid, L2CAP_FLUSH_CHANS_ALL); + } + } + + suspend_rsp.chnl = p_scb->chnl; + suspend_rsp.hndl = p_scb->hndl; + + if (p_data && p_data->api_stop.suspend) { + APPL_TRACE_DEBUG("suspending: %d, sup:%d", start, p_scb->suspend_sup); + if ((start) && (p_scb->suspend_sup)) { + sus_evt = FALSE; + p_scb->l2c_bufs = 0; + AVDT_SuspendReq(&p_scb->avdt_handle, 1); + } + + /* send SUSPEND_EVT event only if not in reconfiguring state and sus_evt is TRUE*/ + if ((sus_evt) && (p_scb->state != BTA_AV_RCFG_SST)) { + suspend_rsp.status = BTA_AV_SUCCESS; + suspend_rsp.initiator = TRUE; + (*bta_av_cb.p_cback)(BTA_AV_SUSPEND_EVT, (tBTA_AV *) &suspend_rsp); + } + } else { + suspend_rsp.status = BTA_AV_SUCCESS; + suspend_rsp.initiator = TRUE; + APPL_TRACE_WARNING("bta_av_str_stopped status %d", suspend_rsp.status); + + /* send STOP_EVT event only if not in reconfiguring state */ + if (p_scb->state != BTA_AV_RCFG_SST) { + (*bta_av_cb.p_cback)(BTA_AV_STOP_EVT, (tBTA_AV *) &suspend_rsp); + } + } +} + +/******************************************************************************* +** +** Function bta_av_reconfig +** +** Description process the reconfigure request. +** save the parameter in control block and +** suspend, reconfigure or close the stream +** +** Returns void +** +*******************************************************************************/ +void bta_av_reconfig (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data) +{ + tAVDT_CFG *p_cfg; + tBTA_AV_API_STOP stop; + tBTA_AV_RECONFIG evt; + tBTA_AV_API_RCFG *p_rcfg = &p_data->api_reconfig; + + APPL_TRACE_DEBUG("bta_av_reconfig r:%d, s:%d idx: %d (o:%d)", + p_scb->recfg_sup, p_scb->suspend_sup, + p_scb->rcfg_idx, p_scb->sep_info_idx); + + p_scb->num_recfg = 0; + /* store the new configuration in control block */ + if (p_scb->p_cap == NULL) { + p_scb->p_cap = (tAVDT_CFG *) osi_malloc(sizeof(tAVDT_CFG)); + } + if ((p_cfg = p_scb->p_cap) == NULL) { + /* report failure */ + evt.status = BTA_AV_FAIL_RESOURCES; + evt.chnl = p_scb->chnl; + evt.hndl = p_scb->hndl; + (*bta_av_cb.p_cback)(BTA_AV_RECONFIG_EVT, (tBTA_AV *)&evt); + + /* this event is not possible in this state. + * use it to bring the SSM back to open state */ + bta_av_ssm_execute(p_scb, BTA_AV_SDP_DISC_OK_EVT, NULL); + return; + } + + /*if(bta_av_cb.features & BTA_AV_FEAT_RCCT)*/ + bta_sys_stop_timer(&p_scb->timer); + + memcpy(p_cfg, &p_scb->cfg, sizeof(tAVDT_CFG)); + p_cfg->num_protect = p_rcfg->num_protect; + memcpy(p_cfg->codec_info, p_rcfg->codec_info, AVDT_CODEC_SIZE); + memcpy(p_cfg->protect_info, p_rcfg->p_protect_info, p_rcfg->num_protect); + p_scb->rcfg_idx = p_rcfg->sep_info_idx; + p_scb->p_cap->psc_mask = p_scb->cur_psc_mask; + + /* if the requested index differs from the current one, we can only close/open */ + if ((p_scb->rcfg_idx == p_scb->sep_info_idx) && + (p_rcfg->suspend) && (p_scb->recfg_sup) && (p_scb->suspend_sup)) { + if (p_scb->started) { + stop.flush = FALSE; + stop.suspend = TRUE; + bta_av_str_stopped(p_scb, (tBTA_AV_DATA *)&stop); + } else { + APPL_TRACE_DEBUG("Reconfig"); + AVDT_ReconfigReq(p_scb->avdt_handle, p_scb->p_cap); + p_scb->p_cap->psc_mask = p_scb->cur_psc_mask; + } + } else { + /* close the stream */ + APPL_TRACE_DEBUG("close/open num_protect: %d", p_cfg->num_protect); + if (p_scb->started) { + bta_av_str_stopped(p_scb, NULL); + p_scb->started = FALSE; + + /* drop the buffers queued in L2CAP */ + L2CA_FlushChannel (p_scb->l2c_cid, L2CAP_FLUSH_CHANS_ALL); + + AVDT_CloseReq(p_scb->avdt_handle); + } + } +} + +/******************************************************************************* +** +** Function bta_av_data_path +** +** Description Handle stream data path. +** +** Returns void +** +*******************************************************************************/ +void bta_av_data_path (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data) +{ + BT_HDR *p_buf = NULL; + UINT32 data_len; + UINT32 timestamp; + BOOLEAN new_buf = FALSE; + UINT8 m_pt = 0x60 | p_scb->codec_type; + tAVDT_DATA_OPT_MASK opt; + UNUSED(p_data); + + if (p_scb->cong) { + return; + } + + /* + APPL_TRACE_ERROR("q: %d", p_scb->l2c_bufs); + */ + //Always get the current number of bufs que'd up + p_scb->l2c_bufs = (UINT8)L2CA_FlushChannel (p_scb->l2c_cid, L2CAP_FLUSH_CHANS_GET); + + if (!list_is_empty(p_scb->a2d_list)) { + p_buf = (BT_HDR *)list_front(p_scb->a2d_list); + list_remove(p_scb->a2d_list, p_buf); + /* use q_info.a2d data, read the timestamp */ + timestamp = *(UINT32 *)(p_buf + 1); + } else { + new_buf = TRUE; + /* a2d_list empty, call co_data, dup data to other channels */ + p_buf = (BT_HDR *)p_scb->p_cos->data(p_scb->codec_type, &data_len, + ×tamp); + + if (p_buf) { + /* use the offset area for the time stamp */ + *(UINT32 *)(p_buf + 1) = timestamp; + + /* dup the data to other channels */ + bta_av_dup_audio_buf(p_scb, p_buf); + } + } + + if (p_buf) { + if (p_scb->l2c_bufs < (BTA_AV_QUEUE_DATA_CHK_NUM)) { + /* there's a buffer, just queue it to L2CAP */ + /* There's no need to increment it here, it is always read from L2CAP see above */ + /* p_scb->l2c_bufs++; */ + /* + APPL_TRACE_ERROR("qw: %d", p_scb->l2c_bufs); + */ + + /* opt is a bit mask, it could have several options set */ + opt = AVDT_DATA_OPT_NONE; + if (p_scb->no_rtp_hdr) { + opt |= AVDT_DATA_OPT_NO_RTP; + } + + AVDT_WriteReqOpt(p_scb->avdt_handle, p_buf, timestamp, m_pt, opt); + p_scb->cong = TRUE; + } else { + /* there's a buffer, but L2CAP does not seem to be moving data */ + if (new_buf) { + /* just got this buffer from co_data, + * put it in queue */ + list_append(p_scb->a2d_list, p_buf); + } else { + /* just dequeue it from the a2d_list */ + if (list_length(p_scb->a2d_list) < 3) { + /* put it back to the queue */ + list_prepend(p_scb->a2d_list, p_buf); + } else { + /* too many buffers in a2d_list, drop it. */ + bta_av_co_audio_drop(p_scb->hndl); + osi_free(p_buf); + } + } + } + } +} + +/******************************************************************************* +** +** Function bta_av_start_ok +** +** Description Stream started. +** +** Returns void +** +*******************************************************************************/ +void bta_av_start_ok (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data) +{ + tBTA_AV_START start; + tBTA_AV_API_STOP stop; + BOOLEAN initiator = FALSE; + BOOLEAN suspend = FALSE; + UINT16 flush_to; + UINT8 new_role = p_scb->role; + BT_HDR hdr; + UINT8 policy = HCI_ENABLE_SNIFF_MODE; + UINT8 cur_role; + + APPL_TRACE_DEBUG("bta_av_start_ok wait:x%x, role:x%x", p_scb->wait, p_scb->role); + + p_scb->started = TRUE; + if (p_scb->sco_suspend) { + p_scb->sco_suspend = FALSE; + } + + if (new_role & BTA_AV_ROLE_START_INT) { + initiator = TRUE; + } + + /* for A2DP SINK we do not send get_caps */ + if ((p_scb->avdt_handle == p_scb->seps[p_scb->sep_idx].av_handle) + && (p_scb->seps[p_scb->sep_idx].tsep == AVDT_TSEP_SNK)) { + p_scb->wait &= ~(BTA_AV_WAIT_ACP_CAPS_ON); + APPL_TRACE_DEBUG(" Local SEP type is SNK new wait is 0x%x", p_scb->wait); + } + if (p_scb->wait & BTA_AV_WAIT_ROLE_SW_FAILED) { + /* role switch has failed */ + p_scb->wait &= ~BTA_AV_WAIT_ROLE_SW_FAILED; + p_data = (tBTA_AV_DATA *)&hdr; + hdr.offset = BTA_AV_RS_FAIL; + } + APPL_TRACE_DEBUG("wait:x%x", p_scb->wait); + + if (p_data && (p_data->hdr.offset != BTA_AV_RS_NONE)) { + p_scb->wait &= ~BTA_AV_WAIT_ROLE_SW_BITS; + if (p_data->hdr.offset == BTA_AV_RS_FAIL) { + bta_sys_idle(TSEP_TO_SYS_ID(p_scb->seps[p_scb->sep_idx].tsep), bta_av_cb.audio_open_cnt, p_scb->peer_addr); + start.chnl = p_scb->chnl; + start.status = BTA_AV_FAIL_ROLE; + start.hndl = p_scb->hndl; + start.initiator = initiator; + (*bta_av_cb.p_cback)(BTA_AV_START_EVT, (tBTA_AV *) &start); + return; + } + } + + if (!bta_av_link_role_ok(p_scb, A2D_SET_ONE_BIT)) { + p_scb->q_tag = BTA_AV_Q_TAG_START; + } else { + /* The wait flag may be set here while we are already master on the link */ + /* this could happen if a role switch complete event occurred during reconfig */ + /* if we are now master on the link, there is no need to wait for the role switch, */ + /* complete anymore so we can clear the wait for role switch flag */ + p_scb->wait &= ~BTA_AV_WAIT_ROLE_SW_BITS; + } + + if (p_scb->wait & (BTA_AV_WAIT_ROLE_SW_RES_OPEN | BTA_AV_WAIT_ROLE_SW_RES_START)) { + p_scb->wait |= BTA_AV_WAIT_ROLE_SW_STARTED; + p_scb->q_tag = BTA_AV_Q_TAG_START; + } + + if (p_scb->wait) { + APPL_TRACE_ERROR("wait:x%x q_tag:%d- not started", p_scb->wait, p_scb->q_tag); + /* Clear first bit of p_scb->wait and not to return from this point else + * HAL layer gets blocked. And if there is delay in Get Capability response as + * first bit of p_scb->wait is cleared hence it ensures bt_av_start_ok is not called + * again from bta_av_save_caps. + */ + p_scb->wait &= ~BTA_AV_WAIT_ACP_CAPS_ON; + } + + /* tell role manager to check M/S role */ + bta_sys_conn_open(TSEP_TO_SYS_ID(p_scb->seps[p_scb->sep_idx].tsep), p_scb->app_id, p_scb->peer_addr); + + bta_sys_busy(TSEP_TO_SYS_ID(p_scb->seps[p_scb->sep_idx].tsep), bta_av_cb.audio_open_cnt, p_scb->peer_addr); + + if (p_scb->media_type == AVDT_MEDIA_AUDIO) { + /* in normal logic, conns should be bta_av_cb.audio_count - 1, + * However, bta_av_stream_chg is not called to increase bta_av_cb.audio_count yet. + * If the code were to be re-arranged for some reasons, this number may need to be changed + */ + p_scb->co_started = bta_av_cb.audio_open_cnt; + flush_to = p_bta_av_cfg->p_audio_flush_to[p_scb->co_started - 1]; + } else { + flush_to = p_bta_av_cfg->video_flush_to; + } + L2CA_SetFlushTimeout(p_scb->peer_addr, flush_to ); + + /* clear the congestion flag */ + p_scb->cong = FALSE; + + if (new_role & BTA_AV_ROLE_START_INT) { + new_role &= ~BTA_AV_ROLE_START_INT; + } else if ((new_role & BTA_AV_ROLE_AD_ACP) && (new_role & BTA_AV_ROLE_SUSPEND_OPT)) { + suspend = TRUE; + } + + if (!suspend) { + p_scb->q_tag = BTA_AV_Q_TAG_STREAM; + bta_av_stream_chg(p_scb, TRUE); + } + + { + /* If sink starts stream, disable sniff mode here */ + if (!initiator) { + /* If souce is the master role, disable role switch during streaming. + * Otherwise allow role switch, if source is slave. + * Because it would not hurt source, if the peer device wants source to be master */ + if ((BTM_GetRole (p_scb->peer_addr, &cur_role) == BTM_SUCCESS) && + (cur_role == BTM_ROLE_MASTER) ) { + policy |= HCI_ENABLE_MASTER_SLAVE_SWITCH; + } + + bta_sys_clear_policy(BTA_ID_AV, policy, p_scb->peer_addr); + } + + p_scb->role = new_role; + p_scb->role &= ~BTA_AV_ROLE_AD_ACP; + p_scb->role &= ~BTA_AV_ROLE_SUSPEND_OPT; + + p_scb->no_rtp_hdr = FALSE; + p_scb->p_cos->start(p_scb->hndl, p_scb->codec_type, p_scb->cfg.codec_info, &p_scb->no_rtp_hdr); + p_scb->co_started = TRUE; + + APPL_TRACE_DEBUG("bta_av_start_ok suspending: %d, role:x%x, init %d", + suspend, p_scb->role, initiator); + + start.suspending = suspend; + start.initiator = initiator; + start.chnl = p_scb->chnl; + start.status = BTA_AV_SUCCESS; + start.hndl = p_scb->hndl; + (*bta_av_cb.p_cback)(BTA_AV_START_EVT, (tBTA_AV *) &start); + + if (suspend) { + p_scb->role |= BTA_AV_ROLE_SUSPEND; + p_scb->cong = TRUE; /* do not allow the media data to go through */ + /* do not duplicate the media packets to this channel */ + p_scb->p_cos->stop(p_scb->hndl, p_scb->codec_type); + p_scb->co_started = FALSE; + stop.flush = FALSE; + stop.suspend = TRUE; + bta_av_ssm_execute(p_scb, BTA_AV_AP_STOP_EVT, (tBTA_AV_DATA *)&stop); + } + } +} + +/******************************************************************************* +** +** Function bta_av_start_failed +** +** Description Stream start failed. +** +** Returns void +** +*******************************************************************************/ +void bta_av_start_failed (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data) +{ + UNUSED(p_data); + + if (p_scb->started == FALSE && p_scb->co_started == FALSE) { + bta_sys_idle(TSEP_TO_SYS_ID(p_scb->seps[p_scb->sep_idx].tsep), bta_av_cb.audio_open_cnt, p_scb->peer_addr); + notify_start_failed(p_scb); + } + + bta_sys_set_policy(BTA_ID_AV, (HCI_ENABLE_SNIFF_MODE | HCI_ENABLE_MASTER_SLAVE_SWITCH), p_scb->peer_addr); + p_scb->sco_suspend = FALSE; +} + +/******************************************************************************* +** +** Function bta_av_str_closed +** +** Description Stream closed. +** +** Returns void +** +*******************************************************************************/ +void bta_av_str_closed (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data) +{ + tBTA_AV data; + tBTA_AV_EVT event; + UINT16 mtu; + UINT8 policy = HCI_ENABLE_SNIFF_MODE; + + if ((bta_av_cb.features & BTA_AV_FEAT_MASTER) == 0 || bta_av_cb.audio_open_cnt == 1) { + policy |= HCI_ENABLE_MASTER_SLAVE_SWITCH; + } + bta_sys_set_policy(BTA_ID_AV, policy, p_scb->peer_addr); + if (bta_av_cb.audio_open_cnt <= 1) { + /* last connection - restore the allow switch flag */ + L2CA_SetDesireRole(L2CAP_ROLE_ALLOW_SWITCH); + } + + if (p_scb->open_status) { + /* must be failure when opening the stream */ + bdcpy(data.open.bd_addr, p_scb->peer_addr); + data.open.status = p_scb->open_status; + data.open.chnl = p_scb->chnl; + data.open.hndl = p_scb->hndl; + + if (p_scb->seps[p_scb->sep_idx].tsep == AVDT_TSEP_SRC ) { + data.open.sep = AVDT_TSEP_SNK; + } else if (p_scb->seps[p_scb->sep_idx].tsep == AVDT_TSEP_SNK ) { + data.open.sep = AVDT_TSEP_SRC; + } + + event = BTA_AV_OPEN_EVT; + p_scb->open_status = BTA_AV_SUCCESS; + + bta_sys_conn_close(TSEP_TO_SYS_ID(p_scb->seps[p_scb->sep_idx].tsep), p_scb->app_id, p_scb->peer_addr); + + bta_av_cleanup(p_scb, p_data); + (*bta_av_cb.p_cback)(event, &data); + } else { + /* do stop if we were started */ + if (p_scb->co_started) { + bta_av_str_stopped(p_scb, NULL); + } + + /* Update common mtu shared by remaining connectons */ + mtu = bta_av_chk_mtu(p_scb, BTA_AV_MAX_A2DP_MTU); + + { + p_scb->p_cos->close(p_scb->hndl, p_scb->codec_type, mtu); + data.close.chnl = p_scb->chnl; + data.close.hndl = p_scb->hndl; + data.close.disc_rsn = p_scb->disc_rsn; + event = BTA_AV_CLOSE_EVT; + + bta_sys_conn_close(TSEP_TO_SYS_ID(p_scb->seps[p_scb->sep_idx].tsep), p_scb->app_id, p_scb->peer_addr); + + bta_av_cleanup(p_scb, p_data); + (*bta_av_cb.p_cback)(event, &data); + } + } +} + +/******************************************************************************* +** +** Function bta_av_clr_cong +** +** Description Clear stream congestion flag. +** +** Returns void +** +*******************************************************************************/ +void bta_av_clr_cong (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data) +{ + UNUSED(p_data); + + if (p_scb->co_started) { + p_scb->cong = FALSE; + } +} + +/******************************************************************************* +** +** Function bta_av_suspend_cfm +** +** Description process the suspend response +** +** Returns void +** +*******************************************************************************/ +void bta_av_suspend_cfm (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data) +{ + tBTA_AV_SUSPEND suspend_rsp; + UINT8 err_code = p_data->str_msg.msg.hdr.err_code; + UINT8 policy = HCI_ENABLE_SNIFF_MODE; + + APPL_TRACE_DEBUG ("bta_av_suspend_cfm:audio_open_cnt = %d, err_code = %d", + bta_av_cb.audio_open_cnt, err_code); + + if (p_scb->started == FALSE) { + /* handle the condition where there is a collision of SUSPEND req from either side + ** Second SUSPEND req could be rejected. Do not treat this as a failure + */ + APPL_TRACE_WARNING("bta_av_suspend_cfm: already suspended, ignore, err_code %d", + err_code); + return; + } + + suspend_rsp.status = BTA_AV_SUCCESS; + if (err_code && (err_code != AVDT_ERR_BAD_STATE)) { + /* Disable suspend feature only with explicit rejection(not with timeout) */ + if (err_code != AVDT_ERR_TIMEOUT) { + p_scb->suspend_sup = FALSE; + } + suspend_rsp.status = BTA_AV_FAIL; + + APPL_TRACE_ERROR ("bta_av_suspend_cfm: suspend failed, closing connection"); + + /* SUSPEND failed. Close connection. */ + bta_av_ssm_execute(p_scb, BTA_AV_API_CLOSE_EVT, NULL); + } else { + /* only set started to FALSE when suspend is successful */ + p_scb->started = FALSE; + } + + if (p_scb->role & BTA_AV_ROLE_SUSPEND) { + p_scb->role &= ~BTA_AV_ROLE_SUSPEND; + p_scb->cong = FALSE; + } + + bta_sys_idle(TSEP_TO_SYS_ID(p_scb->seps[p_scb->sep_idx].tsep), bta_av_cb.audio_open_cnt, p_scb->peer_addr); + if ((bta_av_cb.features & BTA_AV_FEAT_MASTER) == 0 || bta_av_cb.audio_open_cnt == 1) { + policy |= HCI_ENABLE_MASTER_SLAVE_SWITCH; + } + bta_sys_set_policy(BTA_ID_AV, policy, p_scb->peer_addr); + + /* in case that we received suspend_ind, we may need to call co_stop here */ + if (p_scb->co_started) { + bta_av_stream_chg(p_scb, FALSE); + + { + p_scb->co_started = FALSE; + p_scb->p_cos->stop(p_scb->hndl, p_scb->codec_type); + } + L2CA_SetFlushTimeout(p_scb->peer_addr, L2CAP_DEFAULT_FLUSH_TO); + } + + { + suspend_rsp.chnl = p_scb->chnl; + suspend_rsp.hndl = p_scb->hndl; + suspend_rsp.initiator = p_data->str_msg.initiator; + (*bta_av_cb.p_cback)(BTA_AV_SUSPEND_EVT, (tBTA_AV *) &suspend_rsp); + } +} + +/******************************************************************************* +** +** Function bta_av_rcfg_str_ok +** +** Description report reconfigure successful +** +** Returns void +** +*******************************************************************************/ +void bta_av_rcfg_str_ok (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data) +{ + tBTA_AV_RECONFIG evt; + UNUSED(p_data); + + p_scb->l2c_cid = AVDT_GetL2CapChannel(p_scb->avdt_handle); + APPL_TRACE_DEBUG("bta_av_rcfg_str_ok: l2c_cid: %d", p_scb->l2c_cid); + + /* rc listen */ + bta_av_st_rc_timer(p_scb, NULL); + utl_freebuf((void **)&p_scb->p_cap); + + /* No need to keep the role bits once reconfig is done. */ + p_scb->role &= ~BTA_AV_ROLE_AD_ACP; + p_scb->role &= ~BTA_AV_ROLE_SUSPEND_OPT; + p_scb->role &= ~BTA_AV_ROLE_START_INT; + + { + /* reconfigure success */ + evt.status = BTA_AV_SUCCESS; + evt.chnl = p_scb->chnl; + evt.hndl = p_scb->hndl; + (*bta_av_cb.p_cback)(BTA_AV_RECONFIG_EVT, (tBTA_AV *)&evt); + } +} + +/******************************************************************************* +** +** Function bta_av_rcfg_failed +** +** Description process reconfigure failed +** +** Returns void +** +*******************************************************************************/ +void bta_av_rcfg_failed (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data) +{ + tBTA_AV_RECONFIG evt; + + APPL_TRACE_DEBUG("bta_av_rcfg_failed num_recfg: %d, conn_lcb:0x%x", + p_scb->num_recfg, bta_av_cb.conn_lcb); + if (p_scb->num_recfg > BTA_AV_RECONFIG_RETRY) { + bta_av_cco_close(p_scb, p_data); + /* report failure */ + evt.status = BTA_AV_FAIL_STREAM; + evt.chnl = p_scb->chnl; + evt.hndl = p_scb->hndl; + (*bta_av_cb.p_cback)(BTA_AV_RECONFIG_EVT, (tBTA_AV *)&evt); + /* go to closing state */ + bta_av_ssm_execute(p_scb, BTA_AV_API_CLOSE_EVT, NULL); + } else { + /* open failed. try again */ + p_scb->num_recfg++; + if (bta_av_cb.conn_lcb) { + AVDT_DisconnectReq(p_scb->peer_addr, bta_av_dt_cback[p_scb->hdi]); + } else { + bta_av_connect_req(p_scb, NULL); + } + } +} + +/******************************************************************************* +** +** Function bta_av_rcfg_connect +** +** Description stream closed. reconnect the stream +** +** Returns void +** +*******************************************************************************/ +void bta_av_rcfg_connect (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data) +{ + UNUSED(p_data); + + p_scb->cong = FALSE; + p_scb->num_recfg++; + APPL_TRACE_DEBUG("bta_av_rcfg_connect num_recfg: %d", p_scb->num_recfg); + if (p_scb->num_recfg > BTA_AV_RECONFIG_RETRY) { + /* let bta_av_rcfg_failed report fail */ + bta_av_rcfg_failed(p_scb, NULL); + } else { + AVDT_ConnectReq(p_scb->peer_addr, p_scb->sec_mask, bta_av_dt_cback[p_scb->hdi]); + } +} + +/******************************************************************************* +** +** Function bta_av_rcfg_discntd +** +** Description AVDT disconnected. reconnect the stream +** +** Returns void +** +*******************************************************************************/ +void bta_av_rcfg_discntd (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data) +{ + tBTA_AV_RECONFIG evt; + UNUSED(p_data); + + APPL_TRACE_DEBUG("bta_av_rcfg_discntd num_recfg: %d", p_scb->num_recfg); + p_scb->num_recfg++; + if (p_scb->num_recfg > BTA_AV_RECONFIG_RETRY) { + /* report failure */ + evt.status = BTA_AV_FAIL_STREAM; + evt.chnl = p_scb->chnl; + evt.hndl = p_scb->hndl; + (*bta_av_cb.p_cback)(BTA_AV_RECONFIG_EVT, (tBTA_AV *)&evt); + /* report close event & go to init state */ + bta_av_ssm_execute(p_scb, BTA_AV_STR_DISC_FAIL_EVT, NULL); + } else { + AVDT_ConnectReq(p_scb->peer_addr, p_scb->sec_mask, bta_av_dt_cback[p_scb->hdi]); + } +} + +/******************************************************************************* +** +** Function bta_av_suspend_cont +** +** Description received the suspend response. +** continue to reconfigure the stream +** +** Returns void +** +*******************************************************************************/ +void bta_av_suspend_cont (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data) +{ + UINT8 err_code = p_data->str_msg.msg.hdr.err_code; + tBTA_AV_RECONFIG evt; + + p_scb->started = FALSE; + p_scb->cong = FALSE; + if (err_code) { + if (AVDT_ERR_CONNECT == err_code) { + /* report failure */ + evt.status = BTA_AV_FAIL; + (*bta_av_cb.p_cback)(BTA_AV_RECONFIG_EVT, (tBTA_AV *)&evt); + bta_av_ssm_execute(p_scb, BTA_AV_STR_DISC_FAIL_EVT, NULL); + } else { + APPL_TRACE_ERROR("suspend rejected, try close"); + /* Disable suspend feature only with explicit rejection(not with timeout) */ + if (err_code != AVDT_ERR_TIMEOUT) { + p_scb->suspend_sup = FALSE; + } + /* drop the buffers queued in L2CAP */ + L2CA_FlushChannel (p_scb->l2c_cid, L2CAP_FLUSH_CHANS_ALL); + + AVDT_CloseReq(p_scb->avdt_handle); + } + } else { + APPL_TRACE_DEBUG("bta_av_suspend_cont calling AVDT_ReconfigReq"); + /* reconfig the stream */ + + AVDT_ReconfigReq(p_scb->avdt_handle, p_scb->p_cap); + p_scb->p_cap->psc_mask = p_scb->cur_psc_mask; + } +} + +/******************************************************************************* +** +** Function bta_av_rcfg_cfm +** +** Description if reconfigure is successful, report the event +** otherwise, close the stream. +** +** Returns void +** +*******************************************************************************/ +void bta_av_rcfg_cfm (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data) +{ + UINT8 err_code = p_data->str_msg.msg.hdr.err_code; + + /* + APPL_TRACE_DEBUG("bta_av_rcfg_cfm"); + */ + if (err_code) { + APPL_TRACE_ERROR("reconfig rejected, try close"); + /* Disable reconfiguration feature only with explicit rejection(not with timeout) */ + if (err_code != AVDT_ERR_TIMEOUT) { + p_scb->recfg_sup = FALSE; + } + /* started flag is FALSE when reconfigure command is sent */ + /* drop the buffers queued in L2CAP */ + L2CA_FlushChannel (p_scb->l2c_cid, L2CAP_FLUSH_CHANS_ALL); + AVDT_CloseReq(p_scb->avdt_handle); + } else { + /* update the codec info after rcfg cfm */ + memcpy(p_scb->cfg.codec_info, p_data->str_msg.msg.reconfig_cfm.p_cfg->codec_info, AVDT_CODEC_SIZE); + /* take the SSM back to OPEN state */ + bta_av_ssm_execute(p_scb, BTA_AV_STR_OPEN_OK_EVT, NULL); + } +} + +/******************************************************************************* +** +** Function bta_av_rcfg_open +** +** Description AVDT is connected. open the stream with the new configuration +** +** Returns void +** +*******************************************************************************/ +void bta_av_rcfg_open (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data) +{ + UNUSED(p_data); + + APPL_TRACE_DEBUG("bta_av_rcfg_open, num_disc_snks = %d", p_scb->num_disc_snks); + + if (p_scb->num_disc_snks == 0) { + /* Need to update call-out module so that it will be ready for discover */ + p_scb->p_cos->stop(p_scb->hndl, p_scb->codec_type); + + /* send avdtp discover request */ + AVDT_DiscoverReq(p_scb->peer_addr, p_scb->sep_info, BTA_AV_NUM_SEPS, bta_av_dt_cback[p_scb->hdi]); + } else { + p_scb->codec_type = p_scb->p_cap->codec_info[BTA_AV_CODEC_TYPE_IDX]; + memcpy(p_scb->cfg.codec_info, p_scb->p_cap->codec_info, AVDT_CODEC_SIZE); + /* we may choose to use a different SEP at reconfig. + * adjust the sep_idx now */ + bta_av_adjust_seps_idx(p_scb, bta_av_get_scb_handle(p_scb, AVDT_TSEP_SRC)); + + /* open the stream with the new config */ + p_scb->sep_info_idx = p_scb->rcfg_idx; + AVDT_OpenReq(p_scb->avdt_handle, p_scb->peer_addr, + p_scb->sep_info[p_scb->sep_info_idx].seid, p_scb->p_cap); + } + +} + +/******************************************************************************* +** +** Function bta_av_security_rej +** +** Description Send an AVDTP security reject. +** +** Returns void +** +*******************************************************************************/ +void bta_av_security_rej (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data) +{ + UNUSED(p_data); + + AVDT_SecurityRsp(p_scb->avdt_handle, p_scb->avdt_label, AVDT_ERR_BAD_STATE, + NULL, 0); +} + +/******************************************************************************* +** +** Function bta_av_chk_2nd_start +** +** Description check if this is 2nd stream and if it needs to be started. +** This function needs to be kept very similar to bta_av_chk_start +** +** Returns void +** +*******************************************************************************/ +void bta_av_chk_2nd_start (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data) +{ + tBTA_AV_SCB *p_scbi; + int i; + BOOLEAN new_started = FALSE; + UNUSED(p_data); + + if ((p_scb->chnl == BTA_AV_CHNL_AUDIO) && (bta_av_cb.audio_open_cnt >= 2)) { + /* more than one audio channel is connected */ + if (!(p_scb->role & BTA_AV_ROLE_SUSPEND_OPT)) { + /* this channel does not need to be reconfigured. + * if there is other channel streaming, start the stream now */ + for (i = 0; i < BTA_AV_NUM_STRS; i++) { + p_scbi = bta_av_cb.p_scb[i]; + if (p_scbi && p_scbi->chnl == BTA_AV_CHNL_AUDIO && p_scbi->co_started) { + if (!new_started) { + /* start the new stream */ + new_started = TRUE; + bta_av_ssm_execute(p_scb, BTA_AV_AP_START_EVT, NULL); + } + /* may need to update the flush timeout of this already started stream */ + if (p_scbi->co_started != bta_av_cb.audio_open_cnt) { + p_scbi->co_started = bta_av_cb.audio_open_cnt; + L2CA_SetFlushTimeout(p_scbi->peer_addr, p_bta_av_cfg->p_audio_flush_to[p_scbi->co_started - 1] ); + } + } + } + } + } +} + +/******************************************************************************* +** +** Function bta_av_open_rc +** +** Description Send a message to main SM to open RC channel. +** +** Returns void +** +*******************************************************************************/ +void bta_av_open_rc (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data) +{ + tBTA_AV_START start; + + APPL_TRACE_DEBUG("bta_av_open_rc use_rc: %d, wait: x%x role:x%x", p_scb->use_rc, p_scb->wait, p_scb->role); + if ((p_scb->wait & BTA_AV_WAIT_ROLE_SW_BITS) && (p_scb->q_tag == BTA_AV_Q_TAG_START)) { + /* waiting for role switch for some reason & the timer expires */ + if (!bta_av_link_role_ok(p_scb, A2D_SET_ONE_BIT)) { + APPL_TRACE_ERROR ("failed to start streaming for role management reasons!!"); + bta_sys_stop_timer(&p_scb->timer); + start.chnl = p_scb->chnl; + start.status = BTA_AV_FAIL_ROLE; + start.initiator = TRUE; + start.hndl = p_scb->hndl; + p_scb->wait &= ~BTA_AV_WAIT_ROLE_SW_BITS; + bta_av_cb.rs_idx = 0; + (*bta_av_cb.p_cback)(BTA_AV_START_EVT, (tBTA_AV *) &start); + } else { + /* role switch is done. continue to start streaming */ + bta_av_cb.rs_idx = 0; + p_data->hdr.offset = BTA_AV_RS_OK; + bta_av_start_ok (p_scb, p_data); + } + return; + } + + if (p_scb->use_rc == TRUE || (p_scb->role & BTA_AV_ROLE_AD_ACP) ) { + if (bta_av_cb.disc) { + /* AVRC discover db is in use */ + if (p_scb->rc_handle == BTA_AV_RC_HANDLE_NONE) { + /* AVRC channel is not connected. delay a little bit */ + if ((p_scb->wait & BTA_AV_WAIT_ROLE_SW_BITS) == 0) { + bta_sys_start_timer(&p_scb->timer, BTA_AV_AVRC_TIMER_EVT, BTA_AV_RC_DISC_TIME_VAL); + } else { + p_scb->wait |= BTA_AV_WAIT_CHECK_RC; + } + } + } else { + /* use main SM for AVRC SDP activities */ + bta_av_rc_disc((UINT8)(p_scb->hdi + 1)); + } + } else { + if (BTA_AV_RC_HANDLE_NONE != p_scb->rc_handle) { + /* the open API said that this handle does not want a RC connection. + * disconnect it now */ + AVRC_Close(p_scb->rc_handle); + } + } +} + +/******************************************************************************* +** +** Function bta_av_open_at_inc +** +** Description This function is called if API open is called by application +** while state-machine is at incoming state. +** +** Returns void +** +*******************************************************************************/ +void bta_av_open_at_inc (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data) +{ + tBTA_AV_API_OPEN *p_buf; + + memcpy (&(p_scb->open_api), &(p_data->api_open), sizeof(tBTA_AV_API_OPEN)); + + if (p_scb->coll_mask & BTA_AV_COLL_INC_TMR) { + p_scb->coll_mask |= BTA_AV_COLL_API_CALLED; + + /* API open will be handled at timeout if SNK did not start signalling. */ + /* API open will be ignored if SNK starts signalling. */ + } else { + /* SNK did not start signalling, API was called N seconds timeout. */ + /* We need to switch to INIT state and start opening connection. */ + p_scb->coll_mask = 0; + bta_av_set_scb_sst_init (p_scb); + + if ((p_buf = (tBTA_AV_API_OPEN *) osi_malloc(sizeof(tBTA_AV_API_OPEN))) != NULL) { + memcpy(p_buf, &(p_scb->open_api), sizeof(tBTA_AV_API_OPEN)); + bta_sys_sendmsg(p_buf); + } + } +} + +/******************************************************************************* +** +** Function bta_av_set_delay_value +** +** Description This function is called if application layer +** call the API set_delay_value +** +** Returns void +** +*******************************************************************************/ +void bta_av_set_delay_value (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data) +{ + tBTA_AV_DELAY delay = {0}; + + AVDT_SetDelayValue(p_data->api_set_delay_vlaue.delay_value); + delay.delay_value = p_data->api_set_delay_vlaue.delay_value; + + if (p_scb->state == BTA_AV_OPEN_SST) { + if (AVDT_DelayReport(p_scb->avdt_handle, 0, delay.delay_value) != AVDT_SUCCESS) { + delay.status = BTA_AV_FAIL; + } + } + + /* call callback with set delay value event */ + (*bta_av_cb.p_cback)(BTA_AV_SET_DELAY_VALUE_EVT, (tBTA_AV *)&delay); +} + +#endif /* BTA_AV_INCLUDED */ diff --git a/lib/bt/host/bluedroid/bta/av/bta_av_act.c b/lib/bt/host/bluedroid/bta/av/bta_av_act.c new file mode 100644 index 00000000..13a57507 --- /dev/null +++ b/lib/bt/host/bluedroid/bta/av/bta_av_act.c @@ -0,0 +1,1900 @@ +/****************************************************************************** + * + * Copyright (C) 2004-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains action functions for advanced audio/video main state + * machine. + * + ******************************************************************************/ + +#include "common/bt_target.h" +#if defined(BTA_AV_INCLUDED) && (BTA_AV_INCLUDED == TRUE) + +#include +#include "bta/bta_av_api.h" +#include "bta_av_int.h" +#include "stack/avdt_api.h" +#include "bta/utl.h" +#include "stack/l2c_api.h" +#include "osi/allocator.h" +#include "osi/list.h" +#if( defined BTA_AR_INCLUDED ) && (BTA_AR_INCLUDED == TRUE) +#include "bta/bta_ar_api.h" +#endif + +#define LOG_TAG "bt_bta_av" +// #include "osi/include/log.h" +#include "common/bt_trace.h" + +/***************************************************************************** +** Constants +*****************************************************************************/ +/* the timer in milliseconds to wait for open req after setconfig for incoming connections */ +#ifndef BTA_AV_SIG_TIME_VAL +#define BTA_AV_SIG_TIME_VAL 8000 +#endif + +/* In millisec to wait for signalling from SNK when it is initiated from SNK. */ +/* If not, we will start signalling from SRC. */ +#ifndef BTA_AV_ACP_SIG_TIME_VAL +#define BTA_AV_ACP_SIG_TIME_VAL 2000 +#endif + +static void bta_av_acp_sig_timer_cback (TIMER_LIST_ENT *p_tle); + +/******************************************************************************* +** +** Function bta_av_get_rcb_by_shdl +** +** Description find the RCB associated with the given SCB handle. +** +** Returns tBTA_AV_RCB +** +*******************************************************************************/ +tBTA_AV_RCB *bta_av_get_rcb_by_shdl(UINT8 shdl) +{ + tBTA_AV_RCB *p_rcb = NULL; + int i; + + for (i = 0; i < BTA_AV_NUM_RCB; i++) { + if (bta_av_cb.rcb[i].shdl == shdl && bta_av_cb.rcb[i].handle != BTA_AV_RC_HANDLE_NONE) { + p_rcb = &bta_av_cb.rcb[i]; + break; + } + } + return p_rcb; +} +#define BTA_AV_STS_NO_RSP 0xFF /* a number not used by tAVRC_STS */ + +/******************************************************************************* +** +** Function bta_av_del_rc +** +** Description delete the given AVRC handle. +** +** Returns void +** +*******************************************************************************/ +void bta_av_del_rc(tBTA_AV_RCB *p_rcb) +{ + tBTA_AV_SCB *p_scb; + UINT8 rc_handle; /* connected AVRCP handle */ + + p_scb = NULL; + if (p_rcb->handle != BTA_AV_RC_HANDLE_NONE) { + if (p_rcb->shdl) { + /* Validate array index*/ + if ((p_rcb->shdl - 1) < BTA_AV_NUM_STRS) { + p_scb = bta_av_cb.p_scb[p_rcb->shdl - 1]; + } + if (p_scb) { + APPL_TRACE_DEBUG("bta_av_del_rc shdl:%d, srch:%d rc_handle:%d", p_rcb->shdl, + p_scb->rc_handle, p_rcb->handle); + if (p_scb->rc_handle == p_rcb->handle) { + p_scb->rc_handle = BTA_AV_RC_HANDLE_NONE; + } + /* just in case the RC timer is active + if(bta_av_cb.features & BTA_AV_FEAT_RCCT && p_scb->chnl == BTA_AV_CHNL_AUDIO) */ + bta_sys_stop_timer(&p_scb->timer); + } + } + + APPL_TRACE_EVENT("bta_av_del_rc handle: %d status=0x%x, rc_acp_handle:%d, idx:%d", + p_rcb->handle, p_rcb->status, bta_av_cb.rc_acp_handle, bta_av_cb.rc_acp_idx); + rc_handle = p_rcb->handle; + if (!(p_rcb->status & BTA_AV_RC_CONN_MASK) || + ((p_rcb->status & BTA_AV_RC_ROLE_MASK) == BTA_AV_RC_ROLE_INT) ) { + p_rcb->status = 0; + p_rcb->handle = BTA_AV_RC_HANDLE_NONE; + p_rcb->shdl = 0; + p_rcb->lidx = 0; + } + /* else ACP && connected. do not clear the handle yet */ + AVRC_Close(rc_handle); + if (rc_handle == bta_av_cb.rc_acp_handle) { + bta_av_cb.rc_acp_handle = BTA_AV_RC_HANDLE_NONE; + } + APPL_TRACE_EVENT("end del_rc handle: %d status=0x%x, rc_acp_handle:%d, lidx:%d", + p_rcb->handle, p_rcb->status, bta_av_cb.rc_acp_handle, p_rcb->lidx); + } +} + + +/******************************************************************************* +** +** Function bta_av_close_all_rc +** +** Description close the all AVRC handle. +** +** Returns void +** +*******************************************************************************/ +static void bta_av_close_all_rc(tBTA_AV_CB *p_cb) +{ + int i; + + for (i = 0; i < BTA_AV_NUM_RCB; i++) { + if ((p_cb->disabling == TRUE) || (bta_av_cb.rcb[i].shdl != 0)) { + bta_av_del_rc(&bta_av_cb.rcb[i]); + } + } +} + +/******************************************************************************* +** +** Function bta_av_del_sdp_rec +** +** Description delete the given SDP record handle. +** +** Returns void +** +*******************************************************************************/ +static void bta_av_del_sdp_rec(UINT32 *p_sdp_handle) +{ + if (*p_sdp_handle != 0) { + SDP_DeleteRecord(*p_sdp_handle); + *p_sdp_handle = 0; + } +} + +/******************************************************************************* +** +** Function bta_av_avrc_sdp_cback +** +** Description AVRCP service discovery callback. +** +** Returns void +** +*******************************************************************************/ +static void bta_av_avrc_sdp_cback(UINT16 status) +{ + BT_HDR *p_msg; + UNUSED(status); + + if ((p_msg = (BT_HDR *) osi_malloc(sizeof(BT_HDR))) != NULL) { + p_msg->event = BTA_AV_SDP_AVRC_DISC_EVT; + bta_sys_sendmsg(p_msg); + } +} + +/******************************************************************************* +** +** Function bta_av_rc_ctrl_cback +** +** Description AVRCP control callback. +** +** Returns void +** +*******************************************************************************/ +static void bta_av_rc_ctrl_cback(UINT8 handle, UINT8 event, UINT16 result, BD_ADDR peer_addr) +{ + tBTA_AV_RC_CONN_CHG *p_msg; + UINT16 msg_event = 0; + UNUSED(result); + + APPL_TRACE_EVENT("%s handle: %d event: 0x%x",__func__, handle, event); + + if (event == AVRC_OPEN_IND_EVT) { + /* save handle of opened connection + bta_av_cb.rc_handle = handle;*/ + + msg_event = BTA_AV_AVRC_OPEN_EVT; + } else if (event == AVRC_CLOSE_IND_EVT) { + msg_event = BTA_AV_AVRC_CLOSE_EVT; + } + + if (msg_event) { + if ((p_msg = (tBTA_AV_RC_CONN_CHG *) osi_malloc(sizeof(tBTA_AV_RC_CONN_CHG))) != NULL) { + p_msg->hdr.event = msg_event; + p_msg->handle = handle; + if (peer_addr) { + bdcpy(p_msg->peer_addr, peer_addr); + } + bta_sys_sendmsg(p_msg); + } + } +} + +/******************************************************************************* +** +** Function bta_av_rc_msg_cback +** +** Description AVRCP message callback. +** +** Returns void +** +*******************************************************************************/ +static void bta_av_rc_msg_cback(UINT8 handle, UINT8 label, UINT8 opcode, tAVRC_MSG *p_msg) +{ + UINT8 *p_data_src = NULL; + UINT16 data_len = 0; + + APPL_TRACE_DEBUG("%s handle: %u opcode=0x%x", __func__, handle, opcode); + + /* Determine the size of the buffer we need */ + if (opcode == AVRC_OP_VENDOR && p_msg->vendor.p_vendor_data != NULL) { + p_data_src = p_msg->vendor.p_vendor_data; + data_len = (UINT16) p_msg->vendor.vendor_len; + } else if (opcode == AVRC_OP_PASS_THRU && p_msg->pass.p_pass_data != NULL) { + p_data_src = p_msg->pass.p_pass_data; + data_len = (UINT16) p_msg->pass.pass_len; + } + + /* Create a copy of the message */ + tBTA_AV_RC_MSG *p_buf = + (tBTA_AV_RC_MSG *)osi_malloc((UINT16)(sizeof(tBTA_AV_RC_MSG) + data_len)); + if (p_buf != NULL) { + p_buf->hdr.event = BTA_AV_AVRC_MSG_EVT; + p_buf->handle = handle; + p_buf->label = label; + p_buf->opcode = opcode; + memcpy(&p_buf->msg, p_msg, sizeof(tAVRC_MSG)); + /* Copy the data payload, and set the pointer to it */ + if (p_data_src != NULL) { + UINT8 *p_data_dst = (UINT8 *)(p_buf + 1); + memcpy(p_data_dst, p_data_src, data_len); + if (opcode == AVRC_OP_VENDOR) { + p_buf->msg.vendor.p_vendor_data = p_data_dst; + } else if (opcode == AVRC_OP_PASS_THRU) { + p_buf->msg.pass.p_pass_data = p_data_dst; + } + } + bta_sys_sendmsg(p_buf); + } +} + +/******************************************************************************* +** +** Function bta_av_rc_create +** +** Description alloc RCB and call AVRC_Open +** +** Returns the created rc handle +** +*******************************************************************************/ +UINT8 bta_av_rc_create(tBTA_AV_CB *p_cb, UINT8 role, UINT8 shdl, UINT8 lidx) +{ + tAVRC_CONN_CB ccb; + BD_ADDR_PTR bda = (BD_ADDR_PTR)bd_addr_any; + UINT8 status = BTA_AV_RC_ROLE_ACP; + tBTA_AV_SCB *p_scb = p_cb->p_scb[shdl - 1]; + int i; + UINT8 rc_handle; + tBTA_AV_RCB *p_rcb; + + if (role == AVCT_INT) { + bda = p_scb->peer_addr; + status = BTA_AV_RC_ROLE_INT; + } else { + if ((p_rcb = bta_av_get_rcb_by_shdl(shdl)) != NULL ) { + APPL_TRACE_ERROR("bta_av_rc_create ACP handle exist for shdl:%d", shdl); + return p_rcb->handle; + } + } + + ccb.p_ctrl_cback = bta_av_rc_ctrl_cback; + ccb.p_msg_cback = bta_av_rc_msg_cback; + ccb.company_id = p_bta_av_cfg->company_id; + ccb.conn = role; + /* note: BTA_AV_FEAT_RCTG = AVRC_CT_TARGET, BTA_AV_FEAT_RCCT = AVRC_CT_CONTROL */ + ccb.control = p_cb->features & (BTA_AV_FEAT_RCTG | BTA_AV_FEAT_RCCT | AVRC_CT_PASSIVE); + + + if (AVRC_Open(&rc_handle, &ccb, bda) != AVRC_SUCCESS) { + return BTA_AV_RC_HANDLE_NONE; + } + + i = rc_handle; + p_rcb = &p_cb->rcb[i]; + + if (p_rcb->handle != BTA_AV_RC_HANDLE_NONE) { + APPL_TRACE_ERROR("bta_av_rc_create found duplicated handle:%d", rc_handle); + } + + p_rcb->handle = rc_handle; + p_rcb->status = status; + p_rcb->shdl = shdl; + p_rcb->lidx = lidx; + p_rcb->peer_features = 0; + p_rcb->peer_ct_features = 0; + p_rcb->peer_tg_features = 0; + if (lidx == (BTA_AV_NUM_LINKS + 1)) { + /* this LIDX is reserved for the AVRCP ACP connection */ + p_cb->rc_acp_handle = p_rcb->handle; + p_cb->rc_acp_idx = (i + 1); + APPL_TRACE_DEBUG("rc_acp_handle:%d idx:%d", p_cb->rc_acp_handle, p_cb->rc_acp_idx); + } + APPL_TRACE_DEBUG("create %d, role: %d, shdl:%d, rc_handle:%d, lidx:%d, status:0x%x", + i, role, shdl, p_rcb->handle, lidx, p_rcb->status); + + return rc_handle; +} + +/******************************************************************************* +** +** Function bta_av_valid_group_navi_msg +** +** Description Check if it is Group Navigation Msg for Metadata +** +** Returns BTA_AV_RSP_ACCEPT or BTA_AV_RSP_NOT_IMPL. +** +*******************************************************************************/ +static tBTA_AV_CODE bta_av_group_navi_supported(UINT8 len, UINT8 *p_data, BOOLEAN is_inquiry) +{ + tBTA_AV_CODE ret = BTA_AV_RSP_NOT_IMPL; + UINT8 *p_ptr = p_data; + UINT16 u16; + UINT32 u32; + + if (p_bta_av_cfg->avrc_group && len == BTA_GROUP_NAVI_MSG_OP_DATA_LEN) { + BTA_AV_BE_STREAM_TO_CO_ID(u32, p_ptr); + BE_STREAM_TO_UINT16(u16, p_ptr); + + if (u32 == AVRC_CO_METADATA) { + if (is_inquiry) { + if (u16 <= AVRC_PDU_PREV_GROUP) { + ret = BTA_AV_RSP_IMPL_STBL; + } + } else { + if (u16 <= AVRC_PDU_PREV_GROUP) { + ret = BTA_AV_RSP_ACCEPT; + } else { + ret = BTA_AV_RSP_REJ; + } + } + } + } + + return ret; +} + +/******************************************************************************* +** +** Function bta_av_op_supported +** +** Description Check if remote control operation is supported. +** +** Returns BTA_AV_RSP_ACCEPT of supported, BTA_AV_RSP_NOT_IMPL if not. +** +*******************************************************************************/ +static tBTA_AV_CODE bta_av_op_supported(tBTA_AV_RC rc_id, BOOLEAN is_inquiry) +{ + tBTA_AV_CODE ret_code = BTA_AV_RSP_NOT_IMPL; + BOOLEAN rc_id_allowed = FALSE; + if (bta_av_cb.p_rc_cos && bta_av_cb.p_rc_cos->rc_cmd) { + rc_id_allowed = bta_av_cb.p_rc_cos->rc_cmd(rc_id); + } + + if (rc_id_allowed == TRUE) { + ret_code = is_inquiry ? BTA_AV_RSP_IMPL_STBL : BTA_AV_RSP_ACCEPT; + } + return ret_code; +} + +/******************************************************************************* +** +** Function bta_av_find_lcb +** +** Description Given BD_addr, find the associated LCB. +** +** Returns NULL, if not found. +** +*******************************************************************************/ +tBTA_AV_LCB *bta_av_find_lcb(BD_ADDR addr, UINT8 op) +{ + tBTA_AV_CB *p_cb = &bta_av_cb; + int xx; + UINT8 mask; + tBTA_AV_LCB *p_lcb = NULL; + + for (xx = 0; xx < BTA_AV_NUM_LINKS; xx++) { + mask = 1 << xx; /* the used mask for this lcb */ + if ((mask & p_cb->conn_lcb) && 0 == ( bdcmp(p_cb->lcb[xx].addr, addr))) { + p_lcb = &p_cb->lcb[xx]; + if (op == BTA_AV_LCB_FREE) { + p_cb->conn_lcb &= ~mask; /* clear the connect mask */ + APPL_TRACE_DEBUG("conn_lcb: 0x%x", p_cb->conn_lcb); + } + break; + } + } + return p_lcb; +} + +/******************************************************************************* +** +** Function bta_av_rc_opened +** +** Description Set AVRCP state to opened. +** +** Returns void +** +*******************************************************************************/ +void bta_av_rc_opened(tBTA_AV_CB *p_cb, tBTA_AV_DATA *p_data) +{ + tBTA_AV_RC_OPEN rc_open; + tBTA_AV_SCB *p_scb; + int i; + UINT8 shdl = 0; + tBTA_AV_LCB *p_lcb; + tBTA_AV_RCB *p_rcb; + UINT8 tmp; + UINT8 disc = 0; + + /* find the SCB & stop the timer */ + for (i = 0; i < BTA_AV_NUM_STRS; i++) { + p_scb = p_cb->p_scb[i]; + if (p_scb && bdcmp(p_scb->peer_addr, p_data->rc_conn_chg.peer_addr) == 0) { + p_scb->rc_handle = p_data->rc_conn_chg.handle; + APPL_TRACE_DEBUG("bta_av_rc_opened shdl:%d, srch %d", i + 1, p_scb->rc_handle); + shdl = i + 1; + APPL_TRACE_EVENT("%s allow incoming AVRCP connections:%d", __func__, p_scb->use_rc); + bta_sys_stop_timer(&p_scb->timer); + disc = p_scb->hndl; + break; + } + } + + i = p_data->rc_conn_chg.handle; + if (p_cb->rcb[i].handle == BTA_AV_RC_HANDLE_NONE) { + APPL_TRACE_ERROR("not a valid handle:%d any more", i); + return; + } + + + if (p_cb->rcb[i].lidx == (BTA_AV_NUM_LINKS + 1) && shdl != 0) { + /* rc is opened on the RC only ACP channel, but is for a specific + * SCB -> need to switch RCBs */ + p_rcb = bta_av_get_rcb_by_shdl(shdl); + if (p_rcb) { + p_rcb->shdl = p_cb->rcb[i].shdl; + tmp = p_rcb->lidx; + p_rcb->lidx = p_cb->rcb[i].lidx; + p_cb->rcb[i].lidx = tmp; + p_cb->rc_acp_handle = p_rcb->handle; + p_cb->rc_acp_idx = (p_rcb - p_cb->rcb) + 1; + APPL_TRACE_DEBUG("switching RCB rc_acp_handle:%d idx:%d", + p_cb->rc_acp_handle, p_cb->rc_acp_idx); + } + } + + p_cb->rcb[i].shdl = shdl; + rc_open.rc_handle = i; + APPL_TRACE_EVENT("bta_av_rc_opened rcb[%d] shdl:%d lidx:%d/%d", + i, shdl, p_cb->rcb[i].lidx, p_cb->lcb[BTA_AV_NUM_LINKS].lidx); + p_cb->rcb[i].status |= BTA_AV_RC_CONN_MASK; + + if (!shdl && 0 == p_cb->lcb[BTA_AV_NUM_LINKS].lidx) { + /* no associated SCB -> connected to an RC only device + * update the index to the extra LCB */ + p_lcb = &p_cb->lcb[BTA_AV_NUM_LINKS]; + bdcpy(p_lcb->addr, p_data->rc_conn_chg.peer_addr); + APPL_TRACE_DEBUG("rc_only bd_addr:%02x-%02x-%02x-%02x-%02x-%02x", + p_lcb->addr[0], p_lcb->addr[1], + p_lcb->addr[2], p_lcb->addr[3], + p_lcb->addr[4], p_lcb->addr[5]); + p_lcb->lidx = BTA_AV_NUM_LINKS + 1; + p_cb->rcb[i].lidx = p_lcb->lidx; + p_lcb->conn_msk = 1; + APPL_TRACE_ERROR("rcb[%d].lidx=%d, lcb.conn_msk=x%x", + i, p_cb->rcb[i].lidx, p_lcb->conn_msk); + disc = p_data->rc_conn_chg.handle | BTA_AV_CHNL_MSK; + } + + bdcpy(rc_open.peer_addr, p_data->rc_conn_chg.peer_addr); + rc_open.peer_features = p_cb->rcb[i].peer_features; + rc_open.peer_ct_features = p_cb->rcb[i].peer_ct_features; + rc_open.peer_tg_features = p_cb->rcb[i].peer_tg_features; + rc_open.sdp_disc_done = TRUE; + rc_open.status = BTA_AV_SUCCESS; + APPL_TRACE_DEBUG("local features:x%x peer_features:x%x", p_cb->features, + rc_open.peer_features); + if (rc_open.peer_features == 0) { + /* we have not done SDP on peer RC capabilities. + * peer must have initiated the RC connection */ + rc_open.peer_features = BTA_AV_FEAT_RCCT; + rc_open.sdp_disc_done = FALSE; + bta_av_rc_disc(disc); + } + (*p_cb->p_cback)(BTA_AV_RC_OPEN_EVT, (tBTA_AV *) &rc_open); + +} + + +/******************************************************************************* +** +** Function bta_av_rc_remote_cmd +** +** Description Send an AVRCP remote control command. +** +** Returns void +** +*******************************************************************************/ +void bta_av_rc_remote_cmd(tBTA_AV_CB *p_cb, tBTA_AV_DATA *p_data) +{ + tBTA_AV_RCB *p_rcb; + if (p_cb->features & BTA_AV_FEAT_RCCT) { + if (p_data->hdr.layer_specific < BTA_AV_NUM_RCB) { + p_rcb = &p_cb->rcb[p_data->hdr.layer_specific]; + if (p_rcb->status & BTA_AV_RC_CONN_MASK) { + AVRC_PassCmd(p_rcb->handle, p_data->api_remote_cmd.label, + &p_data->api_remote_cmd.msg); + } + } + } +} + +/******************************************************************************* +** +** Function bta_av_rc_vendor_cmd +** +** Description Send an AVRCP vendor specific command. +** +** Returns void +** +*******************************************************************************/ +void bta_av_rc_vendor_cmd(tBTA_AV_CB *p_cb, tBTA_AV_DATA *p_data) +{ + tBTA_AV_RCB *p_rcb; + if ( (p_cb->features & (BTA_AV_FEAT_RCCT | BTA_AV_FEAT_VENDOR)) == + (BTA_AV_FEAT_RCCT | BTA_AV_FEAT_VENDOR)) { + if (p_data->hdr.layer_specific < BTA_AV_NUM_RCB) { + p_rcb = &p_cb->rcb[p_data->hdr.layer_specific]; + AVRC_VendorCmd(p_rcb->handle, p_data->api_vendor.label, &p_data->api_vendor.msg); + } + } +} + +/******************************************************************************* +** +** Function bta_av_rc_vendor_rsp +** +** Description Send an AVRCP vendor specific response. +** +** Returns void +** +*******************************************************************************/ +void bta_av_rc_vendor_rsp(tBTA_AV_CB *p_cb, tBTA_AV_DATA *p_data) +{ + tBTA_AV_RCB *p_rcb; + if ( (p_cb->features & (BTA_AV_FEAT_RCTG | BTA_AV_FEAT_VENDOR)) == + (BTA_AV_FEAT_RCTG | BTA_AV_FEAT_VENDOR)) { + if (p_data->hdr.layer_specific < BTA_AV_NUM_RCB) { + p_rcb = &p_cb->rcb[p_data->hdr.layer_specific]; + AVRC_VendorRsp(p_rcb->handle, p_data->api_vendor.label, &p_data->api_vendor.msg); + } + } +} + +/******************************************************************************* +** +** Function bta_av_rc_meta_rsp +** +** Description Send an AVRCP metadata/advanced control command/response. +** +** Returns void +** +*******************************************************************************/ +void bta_av_rc_meta_rsp(tBTA_AV_CB *p_cb, tBTA_AV_DATA *p_data) +{ + tBTA_AV_RCB *p_rcb; + BOOLEAN do_free = TRUE; + + if ((p_cb->features & BTA_AV_FEAT_METADATA) && (p_data->hdr.layer_specific < BTA_AV_NUM_RCB)) { + if ((p_data->api_meta_rsp.is_rsp && (p_cb->features & BTA_AV_FEAT_RCTG)) || + (!p_data->api_meta_rsp.is_rsp && (p_cb->features & BTA_AV_FEAT_RCCT)) ) { + p_rcb = &p_cb->rcb[p_data->hdr.layer_specific]; + if (p_rcb->handle != BTA_AV_RC_HANDLE_NONE) { + AVRC_MsgReq(p_rcb->handle, p_data->api_meta_rsp.label, + p_data->api_meta_rsp.rsp_code, + p_data->api_meta_rsp.p_pkt); + do_free = FALSE; + } + } + } + + if (do_free) { + osi_free (p_data->api_meta_rsp.p_pkt); + } +} + +/******************************************************************************* +** +** Function bta_av_rc_free_rsp +** +** Description free an AVRCP metadata command buffer. +** +** Returns void +** +*******************************************************************************/ +void bta_av_rc_free_rsp (tBTA_AV_CB *p_cb, tBTA_AV_DATA *p_data) +{ + UNUSED(p_cb); + + osi_free (p_data->api_meta_rsp.p_pkt); +} + +/******************************************************************************* +** +** Function bta_av_rc_meta_req +** +** Description Send an AVRCP metadata command. +** +** Returns void +** +*******************************************************************************/ +void bta_av_rc_free_msg (tBTA_AV_CB *p_cb, tBTA_AV_DATA *p_data) +{ + UNUSED(p_cb); + UNUSED(p_data); +} + + + +/******************************************************************************* +** +** Function bta_av_chk_notif_evt_id +** +** Description make sure the requested player id is valid. +** +** Returns BTA_AV_STS_NO_RSP, if no error +** +*******************************************************************************/ +static tAVRC_STS bta_av_chk_notif_evt_id(tAVRC_MSG_VENDOR *p_vendor) +{ + tAVRC_STS status = BTA_AV_STS_NO_RSP; + UINT16 u16; + UINT8 *p = p_vendor->p_vendor_data + 2; + + BE_STREAM_TO_UINT16 (u16, p); + /* double check the fixed length */ + if ((u16 != 5) || (p_vendor->vendor_len != 9)) { + status = AVRC_STS_INTERNAL_ERR; + } else { + /* make sure the event_id is valid */ + status = AVRC_STS_BAD_PARAM; + if (bta_av_cb.p_rc_cos && bta_av_cb.p_rc_cos->rn_evt_supported) { + if (bta_av_cb.p_rc_cos->rn_evt_supported(*p) == TRUE) { + status = BTA_AV_STS_NO_RSP; + } + } + } + + return status; +} + +/******************************************************************************* +** +** Function bta_av_proc_meta_cmd +** +** Description Process an AVRCP metadata command from the peer. +** +** Returns TRUE to respond immediately +** +*******************************************************************************/ +tBTA_AV_EVT bta_av_proc_meta_cmd(tAVRC_RESPONSE *p_rc_rsp, tBTA_AV_RC_MSG *p_msg, UINT8 *p_ctype) +{ + tBTA_AV_EVT evt = BTA_AV_META_MSG_EVT; + UINT8 u8, pdu, *p; + UINT16 u16; + tAVRC_MSG_VENDOR *p_vendor = &p_msg->msg.vendor; + +#if (AVRC_METADATA_INCLUDED == TRUE) + + pdu = *(p_vendor->p_vendor_data); + p_rc_rsp->pdu = pdu; + *p_ctype = AVRC_RSP_REJ; + /* Metadata messages only use PANEL sub-unit type */ + if (p_vendor->hdr.subunit_type != AVRC_SUB_PANEL) { + APPL_TRACE_DEBUG("SUBUNIT must be PANEL"); + /* reject it */ + evt = 0; + p_vendor->hdr.ctype = BTA_AV_RSP_NOT_IMPL; + AVRC_VendorRsp(p_msg->handle, p_msg->label, &p_msg->msg.vendor); + } else if (!AVRC_IsValidAvcType(pdu, p_vendor->hdr.ctype) ) { + APPL_TRACE_DEBUG("Invalid pdu/ctype: 0x%x, %d", pdu, p_vendor->hdr.ctype); + /* reject invalid message without reporting to app */ + evt = 0; + p_rc_rsp->rsp.status = AVRC_STS_BAD_CMD; + } else { + switch (pdu) { + case AVRC_PDU_GET_CAPABILITIES: + /* process GetCapabilities command without reporting the event to app */ + evt = 0; + u8 = *(p_vendor->p_vendor_data + 4); + p = p_vendor->p_vendor_data + 2; + p_rc_rsp->get_caps.capability_id = u8; + BE_STREAM_TO_UINT16 (u16, p); + if ((u16 != 1) || (p_vendor->vendor_len != 5)) { + p_rc_rsp->get_caps.status = AVRC_STS_INTERNAL_ERR; + } else { + p_rc_rsp->get_caps.status = AVRC_STS_NO_ERROR; + if (u8 == AVRC_CAP_COMPANY_ID) { + *p_ctype = AVRC_RSP_IMPL_STBL; + p_rc_rsp->get_caps.count = p_bta_av_cfg->num_co_ids; + memcpy(p_rc_rsp->get_caps.param.company_id, p_bta_av_cfg->p_meta_co_ids, + (p_bta_av_cfg->num_co_ids << 2)); + } else if (u8 == AVRC_CAP_EVENTS_SUPPORTED) { + *p_ctype = AVRC_RSP_IMPL_STBL; + if (bta_av_cb.p_rc_cos && bta_av_cb.p_rc_cos->rn_evt_cap) { + p_rc_rsp->get_caps.count = bta_av_cb.p_rc_cos->rn_evt_cap( + p_rc_rsp->get_caps.param.event_id); + } else { + p_rc_rsp->get_caps.count = 0; + } + } else { + APPL_TRACE_DEBUG("Invalid capability ID: 0x%x", u8); + /* reject - unknown capability ID */ + p_rc_rsp->get_caps.status = AVRC_STS_BAD_PARAM; + } + } + break; + + case AVRC_PDU_REGISTER_NOTIFICATION: + /* make sure the event_id is implemented */ + p_rc_rsp->rsp.status = bta_av_chk_notif_evt_id (p_vendor); + if (p_rc_rsp->rsp.status != BTA_AV_STS_NO_RSP) { + evt = 0; + } + break; + + case AVRC_PDU_SET_ABSOLUTE_VOLUME: + p_rc_rsp->rsp.status = BTA_AV_STS_NO_RSP; + break; + case AVRC_PDU_SET_PLAYER_APP_VALUE: + /* Setting of a value by CT does not implicitly mean that the setting will take effect on TG. */ + /* The setting shall take effect after a play command from CT. */ + break; + default: + APPL_TRACE_WARNING("%s unhandled RC vendor PDU: 0x%x", __FUNCTION__, pdu); + break; + } + } +#else + APPL_TRACE_DEBUG("AVRCP 1.3 Metadata not supporteed. Reject command."); + /* reject invalid message without reporting to app */ + evt = 0; + p_rc_rsp->rsp.status = AVRC_STS_BAD_CMD; +#endif + + return evt; +} + + +/******************************************************************************* +** +** Function bta_av_rc_msg +** +** Description Process an AVRCP message from the peer. +** +** Returns void +** +*******************************************************************************/ +void bta_av_rc_msg(tBTA_AV_CB *p_cb, tBTA_AV_DATA *p_data) +{ + tBTA_AV_EVT evt = 0; + tBTA_AV av; + BT_HDR *p_pkt = NULL; + tAVRC_MSG_VENDOR *p_vendor = &p_data->rc_msg.msg.vendor; + BOOLEAN is_inquiry = ((p_data->rc_msg.msg.hdr.ctype == AVRC_CMD_SPEC_INQ) || p_data->rc_msg.msg.hdr.ctype == AVRC_CMD_GEN_INQ); +#if (AVRC_METADATA_INCLUDED == TRUE) + UINT8 ctype = 0; + tAVRC_RESPONSE rc_rsp; + + rc_rsp.rsp.status = BTA_AV_STS_NO_RSP; +#endif + + if (p_data->rc_msg.opcode == AVRC_OP_PASS_THRU) { + /* if this is a pass thru command */ + if ((p_data->rc_msg.msg.hdr.ctype == AVRC_CMD_CTRL) || + (p_data->rc_msg.msg.hdr.ctype == AVRC_CMD_SPEC_INQ) || + (p_data->rc_msg.msg.hdr.ctype == AVRC_CMD_GEN_INQ) + ) { + /* check if operation is supported */ + if (p_data->rc_msg.msg.pass.op_id == AVRC_ID_VENDOR) { + p_data->rc_msg.msg.hdr.ctype = BTA_AV_RSP_NOT_IMPL; +#if (AVRC_METADATA_INCLUDED == TRUE) + if (p_cb->features & BTA_AV_FEAT_METADATA) { + p_data->rc_msg.msg.hdr.ctype = + bta_av_group_navi_supported(p_data->rc_msg.msg.pass.pass_len, + p_data->rc_msg.msg.pass.p_pass_data, is_inquiry); + } +#endif + } else { + p_data->rc_msg.msg.hdr.ctype = bta_av_op_supported(p_data->rc_msg.msg.pass.op_id, is_inquiry); + } + + APPL_TRACE_DEBUG("ctype %d", p_data->rc_msg.msg.hdr.ctype) + + /* send response */ + if (p_data->rc_msg.msg.hdr.ctype != BTA_AV_RSP_INTERIM) { + AVRC_PassRsp(p_data->rc_msg.handle, p_data->rc_msg.label, &p_data->rc_msg.msg.pass); + } + + /* set up for callback if supported */ + if (p_data->rc_msg.msg.hdr.ctype == BTA_AV_RSP_ACCEPT || p_data->rc_msg.msg.hdr.ctype == BTA_AV_RSP_INTERIM) { + evt = BTA_AV_REMOTE_CMD_EVT; + av.remote_cmd.rc_id = p_data->rc_msg.msg.pass.op_id; + av.remote_cmd.key_state = p_data->rc_msg.msg.pass.state; + av.remote_cmd.p_data = p_data->rc_msg.msg.pass.p_pass_data; + av.remote_cmd.len = p_data->rc_msg.msg.pass.pass_len; + memcpy(&av.remote_cmd.hdr, &p_data->rc_msg.msg.hdr, sizeof (tAVRC_HDR)); + av.remote_cmd.label = p_data->rc_msg.label; + } + } + /* else if this is a pass thru response */ + else if (p_data->rc_msg.msg.hdr.ctype >= AVRC_RSP_NOT_IMPL) { + /* set up for callback */ + evt = BTA_AV_REMOTE_RSP_EVT; + av.remote_rsp.rc_id = p_data->rc_msg.msg.pass.op_id; + av.remote_rsp.key_state = p_data->rc_msg.msg.pass.state; + av.remote_rsp.rsp_code = p_data->rc_msg.msg.hdr.ctype; + av.remote_rsp.label = p_data->rc_msg.label; + } + /* must be a bad ctype -> reject*/ + else { + p_data->rc_msg.msg.hdr.ctype = BTA_AV_RSP_REJ; + AVRC_PassRsp(p_data->rc_msg.handle, p_data->rc_msg.label, &p_data->rc_msg.msg.pass); + } + } + /* else if this is a vendor specific command or response */ + else if (p_data->rc_msg.opcode == AVRC_OP_VENDOR) { + /* set up for callback */ + av.vendor_cmd.code = p_data->rc_msg.msg.hdr.ctype; + av.vendor_cmd.company_id = p_vendor->company_id; + av.vendor_cmd.label = p_data->rc_msg.label; + av.vendor_cmd.p_data = p_vendor->p_vendor_data; + av.vendor_cmd.len = p_vendor->vendor_len; + + /* if configured to support vendor specific and it's a command */ + if ((p_cb->features & BTA_AV_FEAT_VENDOR) && + p_data->rc_msg.msg.hdr.ctype <= AVRC_CMD_GEN_INQ) { +#if (AVRC_METADATA_INCLUDED == TRUE) + if ((p_cb->features & BTA_AV_FEAT_METADATA) && + (p_vendor->company_id == AVRC_CO_METADATA)) { + av.meta_msg.p_msg = &p_data->rc_msg.msg; + evt = bta_av_proc_meta_cmd (&rc_rsp, &p_data->rc_msg, &ctype); + } else +#endif + { + evt = BTA_AV_VENDOR_CMD_EVT; + } + } + /* else if configured to support vendor specific and it's a response */ + else if ((p_cb->features & BTA_AV_FEAT_VENDOR) && + p_data->rc_msg.msg.hdr.ctype >= AVRC_RSP_ACCEPT) { +#if (AVRC_METADATA_INCLUDED == TRUE) + if ((p_cb->features & BTA_AV_FEAT_METADATA) && + (p_vendor->company_id == AVRC_CO_METADATA)) { + av.meta_msg.p_msg = &p_data->rc_msg.msg; + evt = BTA_AV_META_MSG_EVT; + } else +#endif + { + evt = BTA_AV_VENDOR_RSP_EVT; + } + + } + /* else if not configured to support vendor specific and it's a command */ + else if (!(p_cb->features & BTA_AV_FEAT_VENDOR) && + p_data->rc_msg.msg.hdr.ctype <= AVRC_CMD_GEN_INQ) { + if (p_data->rc_msg.msg.vendor.p_vendor_data[0] == AVRC_PDU_INVALID) { + /* reject it */ + p_data->rc_msg.msg.hdr.ctype = BTA_AV_RSP_REJ; + p_data->rc_msg.msg.vendor.p_vendor_data[4] = AVRC_STS_BAD_CMD; + } else { + p_data->rc_msg.msg.hdr.ctype = BTA_AV_RSP_NOT_IMPL; + } + AVRC_VendorRsp(p_data->rc_msg.handle, p_data->rc_msg.label, &p_data->rc_msg.msg.vendor); + } + } +#if (AVRC_METADATA_INCLUDED == TRUE) + if (evt == 0 && rc_rsp.rsp.status != BTA_AV_STS_NO_RSP) { + if (!p_pkt) { + rc_rsp.rsp.opcode = p_data->rc_msg.opcode; + AVRC_BldResponse (0, &rc_rsp, &p_pkt); + } + if (p_pkt) { + AVRC_MsgReq (p_data->rc_msg.handle, p_data->rc_msg.label, ctype, p_pkt); + } + } +#endif + + /* call callback */ + if (evt != 0) { + av.remote_cmd.rc_handle = p_data->rc_msg.handle; + (*p_cb->p_cback)(evt, &av); + } +} + +/******************************************************************************* +** +** Function bta_av_rc_close +** +** Description close the specified AVRC handle. +** +** Returns void +** +*******************************************************************************/ +void bta_av_rc_close (tBTA_AV_CB *p_cb, tBTA_AV_DATA *p_data) +{ + UINT16 handle = p_data->hdr.layer_specific; + tBTA_AV_SCB *p_scb; + tBTA_AV_RCB *p_rcb; + + if (handle < BTA_AV_NUM_RCB) { + p_rcb = &p_cb->rcb[handle]; + + APPL_TRACE_DEBUG("bta_av_rc_close handle: %d, status=0x%x", p_rcb->handle, p_rcb->status); + if (p_rcb->handle != BTA_AV_RC_HANDLE_NONE) { + if (p_rcb->shdl) { + p_scb = bta_av_cb.p_scb[p_rcb->shdl - 1]; + if (p_scb) { + /* just in case the RC timer is active + if(bta_av_cb.features & BTA_AV_FEAT_RCCT && + p_scb->chnl == BTA_AV_CHNL_AUDIO) */ + bta_sys_stop_timer(&p_scb->timer); + } + } + + AVRC_Close(p_rcb->handle); + } + } +} + +/******************************************************************************* +** +** Function bta_av_get_shdl +** +** Returns The index to p_scb[] +** +*******************************************************************************/ +static UINT8 bta_av_get_shdl(tBTA_AV_SCB *p_scb) +{ + int i; + UINT8 shdl = 0; + /* find the SCB & stop the timer */ + for (i = 0; i < BTA_AV_NUM_STRS; i++) { + if (p_scb == bta_av_cb.p_scb[i]) { + shdl = i + 1; + break; + } + } + return shdl; +} + +/******************************************************************************* +** +** Function bta_av_stream_chg +** +** Description audio streaming status changed. +** +** Returns void +** +*******************************************************************************/ +void bta_av_stream_chg(tBTA_AV_SCB *p_scb, BOOLEAN started) +{ + UINT8 started_msk; + int i; + UINT8 *p_streams; + BOOLEAN no_streams = FALSE; + tBTA_AV_SCB *p_scbi; + + started_msk = BTA_AV_HNDL_TO_MSK(p_scb->hdi); + APPL_TRACE_DEBUG ("bta_av_stream_chg started:%d started_msk:x%x chnl:x%x", started, + started_msk, p_scb->chnl); + if (BTA_AV_CHNL_AUDIO == p_scb->chnl) { + p_streams = &bta_av_cb.audio_streams; + } else { + p_streams = &bta_av_cb.video_streams; + } + + if (started) { + /* Let L2CAP know this channel is processed with high priority */ + L2CA_SetAclPriority(p_scb->peer_addr, L2CAP_PRIORITY_HIGH); + (*p_streams) |= started_msk; + } else { + (*p_streams) &= ~started_msk; + } + + if (!started) { + i = 0; + if (BTA_AV_CHNL_AUDIO == p_scb->chnl) { + if (bta_av_cb.video_streams == 0) { + no_streams = TRUE; + } + } else { + no_streams = TRUE; + if ( bta_av_cb.audio_streams ) { + for (; i < BTA_AV_NUM_STRS; i++) { + p_scbi = bta_av_cb.p_scb[i]; + /* scb is used and started */ + if ( p_scbi && (bta_av_cb.audio_streams & BTA_AV_HNDL_TO_MSK(i)) + && bdcmp(p_scbi->peer_addr, p_scb->peer_addr) == 0) { + no_streams = FALSE; + break; + } + } + + } + } + + APPL_TRACE_DEBUG ("no_streams:%d i:%d, audio_streams:x%x, video_streams:x%x", no_streams, i, + bta_av_cb.audio_streams, bta_av_cb.video_streams); + if (no_streams) { + /* Let L2CAP know this channel is processed with low priority */ + L2CA_SetAclPriority(p_scb->peer_addr, L2CAP_PRIORITY_NORMAL); + } + } +} + + +/******************************************************************************* +** +** Function bta_av_conn_chg +** +** Description connection status changed. +** Open an AVRCP acceptor channel, if new conn. +** +** Returns void +** +*******************************************************************************/ +void bta_av_conn_chg(tBTA_AV_DATA *p_data) +{ + tBTA_AV_CB *p_cb = &bta_av_cb; + tBTA_AV_SCB *p_scb = NULL; + tBTA_AV_SCB *p_scbi; + UINT8 mask; + UINT8 conn_msk; + UINT8 old_msk; + int i; + int index = (p_data->hdr.layer_specific & BTA_AV_HNDL_MSK) - 1; + tBTA_AV_LCB *p_lcb; + tBTA_AV_LCB *p_lcb_rc; + tBTA_AV_RCB *p_rcb, *p_rcb2; + BOOLEAN chk_restore = FALSE; + + /* Validate array index*/ + if (index < BTA_AV_NUM_STRS) { + p_scb = p_cb->p_scb[index]; + } + mask = BTA_AV_HNDL_TO_MSK(index); + p_lcb = bta_av_find_lcb(p_data->conn_chg.peer_addr, BTA_AV_LCB_FIND); + conn_msk = 1 << (index + 1); + if (p_data->conn_chg.is_up) { + /* set the conned mask for this channel */ + if (p_scb) { + if (p_lcb) { + p_lcb->conn_msk |= conn_msk; + for (i = 0; i < BTA_AV_NUM_RCB; i++) { + if (bta_av_cb.rcb[i].lidx == p_lcb->lidx) { + bta_av_cb.rcb[i].shdl = index + 1; + APPL_TRACE_DEBUG("conn_chg up[%d]: %d, status=0x%x, shdl:%d, lidx:%d", i, + bta_av_cb.rcb[i].handle, bta_av_cb.rcb[i].status, + bta_av_cb.rcb[i].shdl, bta_av_cb.rcb[i].lidx); + break; + } + } + } + if (p_scb->chnl == BTA_AV_CHNL_AUDIO) { + old_msk = p_cb->conn_audio; + p_cb->conn_audio |= mask; + } else { + old_msk = p_cb->conn_video; + p_cb->conn_video |= mask; + } + + if ((old_msk & mask) == 0) { + /* increase the audio open count, if not set yet */ + bta_av_cb.audio_open_cnt++; + } + + + APPL_TRACE_DEBUG("rc_acp_handle:%d rc_acp_idx:%d", p_cb->rc_acp_handle, p_cb->rc_acp_idx); + /* check if the AVRCP ACP channel is already connected */ + if (p_lcb && p_cb->rc_acp_handle != BTA_AV_RC_HANDLE_NONE && p_cb->rc_acp_idx) { + p_lcb_rc = &p_cb->lcb[BTA_AV_NUM_LINKS]; + APPL_TRACE_DEBUG("rc_acp is connected && conn_chg on same addr p_lcb_rc->conn_msk:x%x", + p_lcb_rc->conn_msk); + /* check if the RC is connected to the scb addr */ + APPL_TRACE_DEBUG ("p_lcb_rc->addr: %02x:%02x:%02x:%02x:%02x:%02x", + p_lcb_rc->addr[0], p_lcb_rc->addr[1], p_lcb_rc->addr[2], p_lcb_rc->addr[3], + p_lcb_rc->addr[4], p_lcb_rc->addr[5]); + APPL_TRACE_DEBUG ("conn_chg.peer_addr: %02x:%02x:%02x:%02x:%02x:%02x", + p_data->conn_chg.peer_addr[0], p_data->conn_chg.peer_addr[1], + p_data->conn_chg.peer_addr[2], + p_data->conn_chg.peer_addr[3], p_data->conn_chg.peer_addr[4], + p_data->conn_chg.peer_addr[5]); + if (p_lcb_rc->conn_msk && bdcmp(p_lcb_rc->addr, p_data->conn_chg.peer_addr) == 0) { + /* AVRCP is already connected. + * need to update the association betwen SCB and RCB */ + p_lcb_rc->conn_msk = 0; /* indicate RC ONLY is not connected */ + p_lcb_rc->lidx = 0; + p_scb->rc_handle = p_cb->rc_acp_handle; + p_rcb = &p_cb->rcb[p_cb->rc_acp_idx - 1]; + p_rcb->shdl = bta_av_get_shdl(p_scb); + APPL_TRACE_DEBUG("update rc_acp shdl:%d/%d srch:%d", index + 1, p_rcb->shdl, + p_scb->rc_handle ); + + p_rcb2 = bta_av_get_rcb_by_shdl(p_rcb->shdl); + if (p_rcb2) { + /* found the RCB that was created to associated with this SCB */ + p_cb->rc_acp_handle = p_rcb2->handle; + p_cb->rc_acp_idx = (p_rcb2 - p_cb->rcb) + 1; + APPL_TRACE_DEBUG("new rc_acp_handle:%d, idx:%d", p_cb->rc_acp_handle, + p_cb->rc_acp_idx); + p_rcb2->lidx = (BTA_AV_NUM_LINKS + 1); + APPL_TRACE_DEBUG("rc2 handle:%d lidx:%d/%d", p_rcb2->handle, p_rcb2->lidx, + p_cb->lcb[p_rcb2->lidx - 1].lidx); + } + p_rcb->lidx = p_lcb->lidx; + APPL_TRACE_DEBUG("rc handle:%d lidx:%d/%d", p_rcb->handle, p_rcb->lidx, + p_cb->lcb[p_rcb->lidx - 1].lidx); + } + } + } + } else { + if ((p_cb->conn_audio & mask) && bta_av_cb.audio_open_cnt) { + bta_sys_conn_close(TSEP_TO_SYS_ID(p_scb->seps[p_scb->sep_idx].tsep), bta_av_cb.audio_open_cnt, p_scb->peer_addr); + /* this channel is still marked as open. decrease the count */ + bta_av_cb.audio_open_cnt--; + } + + /* clear the conned mask for this channel */ + p_cb->conn_audio &= ~mask; + p_cb->conn_video &= ~mask; + if (p_scb) { + /* the stream is closed. + * clear the peer address, so it would not mess up the AVRCP for the next round of operation */ + bdcpy(p_scb->peer_addr, bd_addr_null); + if (p_scb->chnl == BTA_AV_CHNL_AUDIO) { + if (p_lcb) { + p_lcb->conn_msk &= ~conn_msk; + } + /* audio channel is down. make sure the INT channel is down */ + /* just in case the RC timer is active + if(p_cb->features & BTA_AV_FEAT_RCCT) */ + { + bta_sys_stop_timer(&p_scb->timer); + } + /* one audio channel goes down. check if we need to restore high priority */ + chk_restore = TRUE; + } + } + + APPL_TRACE_DEBUG("bta_av_conn_chg shdl:%d", index + 1); + for (i = 0; i < BTA_AV_NUM_RCB; i++) { + APPL_TRACE_DEBUG("conn_chg dn[%d]: %d, status=0x%x, shdl:%d, lidx:%d", i, + bta_av_cb.rcb[i].handle, bta_av_cb.rcb[i].status, + bta_av_cb.rcb[i].shdl, bta_av_cb.rcb[i].lidx); + if (bta_av_cb.rcb[i].shdl == index + 1) { + bta_av_del_rc(&bta_av_cb.rcb[i]); + break; + } + } + + if (p_cb->conn_audio == 0 && p_cb->conn_video == 0) { + /* if both channels are not connected, + * close all RC channels */ + bta_av_close_all_rc(p_cb); + } + + /* if the AVRCP is no longer listening, create the listening channel */ + if (bta_av_cb.rc_acp_handle == BTA_AV_RC_HANDLE_NONE && bta_av_cb.features & BTA_AV_FEAT_RCTG) { + bta_av_rc_create(&bta_av_cb, AVCT_ACP, 0, BTA_AV_NUM_LINKS + 1); + } + } + + APPL_TRACE_DEBUG("bta_av_conn_chg audio:%x video:%x up:%d conn_msk:0x%x chk_restore:%d audio_open_cnt:%d", + p_cb->conn_audio, p_cb->conn_video, p_data->conn_chg.is_up, conn_msk, chk_restore, p_cb->audio_open_cnt); + + if (chk_restore) { + if (p_cb->audio_open_cnt == 1) { + /* one audio channel goes down and there's one audio channel remains open. + * restore the switch role in default link policy */ + bta_sys_set_default_policy(BTA_ID_AV, HCI_ENABLE_MASTER_SLAVE_SWITCH); + /* allow role switch, if this is the last connection */ + bta_av_restore_switch(); + } + if (p_cb->audio_open_cnt) { + /* adjust flush timeout settings to longer period */ + for (i = 0; i < BTA_AV_NUM_STRS; i++) { + p_scbi = bta_av_cb.p_scb[i]; + if (p_scbi && p_scbi->chnl == BTA_AV_CHNL_AUDIO && p_scbi->co_started) { + /* may need to update the flush timeout of this already started stream */ + if (p_scbi->co_started != bta_av_cb.audio_open_cnt) { + p_scbi->co_started = bta_av_cb.audio_open_cnt; + L2CA_SetFlushTimeout(p_scbi->peer_addr, p_bta_av_cfg->p_audio_flush_to[p_scbi->co_started - 1] ); + } + } + } + } + } +} + +/******************************************************************************* +** +** Function bta_av_disable +** +** Description disable AV. +** +** Returns void +** +*******************************************************************************/ +void bta_av_disable(tBTA_AV_CB *p_cb, tBTA_AV_DATA *p_data) +{ + BT_HDR hdr; + UINT16 xx; + UNUSED(p_data); + + p_cb->disabling = TRUE; + + bta_av_close_all_rc(p_cb); + + utl_freebuf((void **) &p_cb->p_disc_db); + + /* disable audio/video - de-register all channels, + * expect BTA_AV_DEREG_COMP_EVT when deregister is complete */ + for (xx = 0; xx < BTA_AV_NUM_STRS; xx++) { + hdr.layer_specific = xx + 1; + bta_av_api_deregister((tBTA_AV_DATA *)&hdr); + } + + bta_sys_free_timer(&p_cb->sig_tmr); + memset(&p_cb->sig_tmr, 0, sizeof(TIMER_LIST_ENT)); + bta_sys_free_timer(&p_cb->acp_sig_tmr); + memset(&p_cb->acp_sig_tmr, 0, sizeof(TIMER_LIST_ENT)); +} + +/******************************************************************************* +** +** Function bta_av_api_disconnect +** +** Description . +** +** Returns void +** +*******************************************************************************/ +void bta_av_api_disconnect(tBTA_AV_DATA *p_data) +{ + AVDT_DisconnectReq(p_data->api_discnt.bd_addr, bta_av_conn_cback); + bta_sys_stop_timer(&bta_av_cb.sig_tmr); +} + +/******************************************************************************* +** +** Function bta_av_sig_chg +** +** Description process AVDT signal channel up/down. +** +** Returns void +** +*******************************************************************************/ +void bta_av_sig_chg(tBTA_AV_DATA *p_data) +{ + UINT16 event = p_data->str_msg.hdr.layer_specific; + tBTA_AV_CB *p_cb = &bta_av_cb; + int xx; + UINT8 mask; + tBTA_AV_LCB *p_lcb = NULL; + + APPL_TRACE_DEBUG("bta_av_sig_chg event: %d", event); + if (event == AVDT_CONNECT_IND_EVT) { + p_lcb = bta_av_find_lcb(p_data->str_msg.bd_addr, BTA_AV_LCB_FIND); + if (!p_lcb) { + /* if the address does not have an LCB yet, alloc one */ + for (xx = 0; xx < BTA_AV_NUM_LINKS; xx++) { + mask = 1 << xx; + APPL_TRACE_DEBUG("conn_lcb: 0x%x", p_cb->conn_lcb); + + /* look for a p_lcb with its p_scb registered */ + if ((!(mask & p_cb->conn_lcb)) && (p_cb->p_scb[xx] != NULL)) { + p_lcb = &p_cb->lcb[xx]; + p_lcb->lidx = xx + 1; + bdcpy(p_lcb->addr, p_data->str_msg.bd_addr); + p_lcb->conn_msk = 0; /* clear the connect mask */ + /* start listening when the signal channel is open */ + if (p_cb->features & BTA_AV_FEAT_RCTG) { + bta_av_rc_create(p_cb, AVCT_ACP, 0, p_lcb->lidx); + } + /* this entry is not used yet. */ + p_cb->conn_lcb |= mask; /* mark it as used */ + APPL_TRACE_DEBUG("start sig timer %d", p_data->hdr.offset); + if (p_data->hdr.offset == AVDT_ACP) { + APPL_TRACE_DEBUG("Incoming L2CAP acquired, set state as incoming"); + bdcpy(p_cb->p_scb[xx]->peer_addr, p_data->str_msg.bd_addr); + p_cb->p_scb[xx]->use_rc = TRUE; /* allowing RC for incoming connection */ + bta_av_ssm_execute(p_cb->p_scb[xx], BTA_AV_ACP_CONNECT_EVT, p_data); + + /* The Pending Event should be sent as soon as the L2CAP signalling channel + * is set up, which is NOW. Earlier this was done only after + * BTA_AV_SIG_TIME_VAL milliseconds. + * The following function shall send the event and start the recurring timer + */ + bta_av_sig_timer(NULL); + + /* Possible collision : need to avoid outgoing processing while the timer is running */ + p_cb->p_scb[xx]->coll_mask = BTA_AV_COLL_INC_TMR; + + p_cb->acp_sig_tmr.param = (UINT32)xx; + p_cb->acp_sig_tmr.p_cback = (TIMER_CBACK *)&bta_av_acp_sig_timer_cback; + bta_sys_start_timer(&p_cb->acp_sig_tmr, 0, BTA_AV_ACP_SIG_TIME_VAL); + } + break; + } + } + + /* check if we found something */ + if (xx == BTA_AV_NUM_LINKS) { + /* We do not have scb for this avdt connection. */ + /* Silently close the connection. */ + APPL_TRACE_ERROR("av scb not available for avdt connection"); + AVDT_DisconnectReq (p_data->str_msg.bd_addr, NULL); + return; + } + } + } +#if( defined BTA_AR_INCLUDED ) && (BTA_AR_INCLUDED == TRUE) + else if (event == BTA_AR_AVDT_CONN_EVT) { + bta_sys_stop_timer(&bta_av_cb.sig_tmr); + } +#endif + else { + /* disconnected. */ + p_lcb = bta_av_find_lcb(p_data->str_msg.bd_addr, BTA_AV_LCB_FREE); + if (p_lcb && p_lcb->conn_msk) { + APPL_TRACE_DEBUG("conn_msk: 0x%x", p_lcb->conn_msk); + /* clean up ssm */ + for (xx = 0; xx < BTA_AV_NUM_STRS; xx++) { + mask = 1 << (xx + 1); + if ((mask & p_lcb->conn_msk) && (p_cb->p_scb[xx]) && + (bdcmp(p_cb->p_scb[xx]->peer_addr, p_data->str_msg.bd_addr) == 0)) { + p_cb->p_scb[xx]->disc_rsn = p_data->str_msg.hdr.offset; + bta_av_ssm_execute(p_cb->p_scb[xx], BTA_AV_AVDT_DISCONNECT_EVT, NULL); + } + } + } + } + APPL_TRACE_DEBUG("conn_lcb: 0x%x", p_cb->conn_lcb); +} + +/******************************************************************************* +** +** Function bta_av_sig_timer +** +** Description process the signal channel timer. This timer is started +** when the AVDTP signal channel is connected. If no profile +** is connected, the timer goes off every BTA_AV_SIG_TIME_VAL +** +** Returns void +** +*******************************************************************************/ +void bta_av_sig_timer(tBTA_AV_DATA *p_data) +{ + tBTA_AV_CB *p_cb = &bta_av_cb; + int xx; + UINT8 mask; + tBTA_AV_LCB *p_lcb = NULL; + tBTA_AV_PEND pend; + UNUSED(p_data); + + APPL_TRACE_DEBUG("bta_av_sig_timer"); + for (xx = 0; xx < BTA_AV_NUM_LINKS; xx++) { + mask = 1 << xx; + if (mask & p_cb->conn_lcb) { + /* this entry is used. check if it is connected */ + p_lcb = &p_cb->lcb[xx]; + if (!p_lcb->conn_msk) { + bta_sys_start_timer(&p_cb->sig_tmr, BTA_AV_SIG_TIMER_EVT, BTA_AV_SIG_TIME_VAL); + bdcpy(pend.bd_addr, p_lcb->addr); + (*p_cb->p_cback)(BTA_AV_PENDING_EVT, (tBTA_AV *) &pend); + } + } + } +} + +/******************************************************************************* +** +** Function bta_av_acp_sig_timer_cback +** +** Description Process the timeout when SRC is accepting connection +** and SNK did not start signalling. +** +** Returns void +** +*******************************************************************************/ +static void bta_av_acp_sig_timer_cback (TIMER_LIST_ENT *p_tle) +{ + UINT8 inx = (UINT8)p_tle->param; + tBTA_AV_CB *p_cb = &bta_av_cb; + tBTA_AV_SCB *p_scb = NULL; + tBTA_AV_API_OPEN *p_buf; + if (inx < BTA_AV_NUM_STRS) { + p_scb = p_cb->p_scb[inx]; + } + if (p_scb) { + APPL_TRACE_DEBUG("bta_av_acp_sig_timer_cback, coll_mask = 0x%02X", p_scb->coll_mask); + + if (p_scb->coll_mask & BTA_AV_COLL_INC_TMR) { + p_scb->coll_mask &= ~BTA_AV_COLL_INC_TMR; + + if (bta_av_is_scb_opening(p_scb)) { + if (p_scb->p_disc_db) { + /* We are still doing SDP. Run the timer again. */ + p_scb->coll_mask |= BTA_AV_COLL_INC_TMR; + + p_cb->acp_sig_tmr.param = (UINT32)inx; + p_cb->acp_sig_tmr.p_cback = (TIMER_CBACK *)&bta_av_acp_sig_timer_cback; + bta_sys_start_timer(&p_cb->acp_sig_tmr, 0, BTA_AV_ACP_SIG_TIME_VAL); + } else { + /* SNK did not start signalling, resume signalling process. */ + bta_av_discover_req (p_scb, NULL); + } + } else if (bta_av_is_scb_incoming(p_scb)) { + /* Stay in incoming state if SNK does not start signalling */ + + /* API open was called right after SNK opened L2C connection. */ + if (p_scb->coll_mask & BTA_AV_COLL_API_CALLED) { + p_scb->coll_mask &= ~BTA_AV_COLL_API_CALLED; + + /* BTA_AV_API_OPEN_EVT */ + if ((p_buf = (tBTA_AV_API_OPEN *) osi_malloc(sizeof(tBTA_AV_API_OPEN))) != NULL) { + memcpy(p_buf, &(p_scb->open_api), sizeof(tBTA_AV_API_OPEN)); + bta_sys_sendmsg(p_buf); + } + } + } + } + } +} + +/******************************************************************************* +** +** Function bta_av_check_peer_rc_features +** +** Description check supported AVRC features on the peer device from the SDP +** record and return the feature mask +** +** Returns tBTA_AV_FEAT peer device feature mask +** +*******************************************************************************/ +tBTA_AV_FEAT bta_av_check_peer_rc_features (UINT16 service_uuid, UINT16 *rc_features) +{ + tBTA_AV_FEAT peer_features = 0; + tBTA_AV_CB *p_cb = &bta_av_cb; + tSDP_DISC_REC *p_rec = NULL; + tSDP_DISC_ATTR *p_attr; + UINT16 peer_rc_version = 0; + UINT16 categories = 0; + + APPL_TRACE_DEBUG("bta_av_check_peer_rc features service_uuid:x%x", service_uuid); + /* loop through all records we found */ + while (TRUE) { + /* get next record; if none found, we're done */ + if ((p_rec = SDP_FindServiceInDb(p_cb->p_disc_db, service_uuid, p_rec)) == NULL) { + break; + } + + if (( SDP_FindAttributeInRec(p_rec, ATTR_ID_SERVICE_CLASS_ID_LIST)) != NULL) { + /* find peer features */ + if (SDP_FindServiceInDb(p_cb->p_disc_db, UUID_SERVCLASS_AV_REMOTE_CONTROL, NULL)) { + peer_features |= BTA_AV_FEAT_RCCT; + } + if (SDP_FindServiceInDb(p_cb->p_disc_db, UUID_SERVCLASS_AV_REM_CTRL_TARGET, NULL)) { + peer_features |= BTA_AV_FEAT_RCTG; + } + } + + if (( SDP_FindAttributeInRec(p_rec, ATTR_ID_BT_PROFILE_DESC_LIST)) != NULL) { + /* get profile version (if failure, version parameter is not updated) */ + SDP_FindProfileVersionInRec(p_rec, UUID_SERVCLASS_AV_REMOTE_CONTROL, &peer_rc_version); + APPL_TRACE_DEBUG("peer_rc_version 0x%x", peer_rc_version); + + if (peer_rc_version >= AVRC_REV_1_3) { + peer_features |= (BTA_AV_FEAT_VENDOR | BTA_AV_FEAT_METADATA); + } + + if (peer_rc_version >= AVRC_REV_1_4) { + peer_features |= (BTA_AV_FEAT_ADV_CTRL); + /* get supported categories */ + if ((p_attr = SDP_FindAttributeInRec(p_rec, + ATTR_ID_SUPPORTED_FEATURES)) != NULL) { + categories = p_attr->attr_value.v.u16; + if (categories & AVRC_SUPF_CT_BROWSE) { + peer_features |= (BTA_AV_FEAT_BROWSE); + } + } + } + } + } + + if (rc_features) { + *rc_features = categories; + } + + APPL_TRACE_DEBUG("peer_features:x%x, rc:x%x", peer_features, categories); + return peer_features; +} + +/******************************************************************************* +** +** Function bta_av_rc_disc_done +** +** Description Handle AVRCP service discovery results. If matching +** service found, open AVRCP connection. +** +** Returns void +** +*******************************************************************************/ +void bta_av_rc_disc_done(tBTA_AV_DATA *p_data) +{ + tBTA_AV_CB *p_cb = &bta_av_cb; + tBTA_AV_SCB *p_scb = NULL; + tBTA_AV_LCB *p_lcb; + tBTA_AV_RC_OPEN rc_open; + tBTA_AV_RC_FEAT rc_feat; + UINT8 rc_handle; + tBTA_AV_FEAT peer_features; /* peer features mask */ + UINT16 peer_ct_features; /* peer features mask as controller */ + UINT16 peer_tg_features; /* peer features mask as target */ + UNUSED(p_data); + + APPL_TRACE_DEBUG("bta_av_rc_disc_done disc:x%x", p_cb->disc); + if (!p_cb->disc) { + return; + } + + if ((p_cb->disc & BTA_AV_CHNL_MSK) == BTA_AV_CHNL_MSK) { + /* this is the rc handle/index to tBTA_AV_RCB */ + rc_handle = p_cb->disc & (~BTA_AV_CHNL_MSK); + } else { + /* Validate array index*/ + if (((p_cb->disc & BTA_AV_HNDL_MSK) - 1) < BTA_AV_NUM_STRS) { + p_scb = p_cb->p_scb[(p_cb->disc & BTA_AV_HNDL_MSK) - 1]; + } + if (p_scb) { + rc_handle = p_scb->rc_handle; + } else { + p_cb->disc = 0; + return; + } + } + + APPL_TRACE_DEBUG("rc_handle %d", rc_handle); + /* check peer version and whether support CT and TG role */ + peer_features = bta_av_check_peer_rc_features (UUID_SERVCLASS_AV_REMOTE_CONTROL, &peer_ct_features); + peer_features |= bta_av_check_peer_rc_features (UUID_SERVCLASS_AV_REM_CTRL_TARGET, &peer_tg_features); + + p_cb->disc = 0; + utl_freebuf((void **) &p_cb->p_disc_db); + + APPL_TRACE_DEBUG("peer_features 0x%x, local features 0x%x", peer_features, p_cb->features); + + /* if we have no rc connection */ + if (rc_handle == BTA_AV_RC_HANDLE_NONE) { + if (p_scb) { + /* if peer remote control service matches ours and USE_RC is TRUE */ + if ((((p_cb->features & BTA_AV_FEAT_RCCT) && (peer_features & BTA_AV_FEAT_RCTG)) || + ((p_cb->features & BTA_AV_FEAT_RCTG) && (peer_features & BTA_AV_FEAT_RCCT))) ) { + p_lcb = bta_av_find_lcb(p_scb->peer_addr, BTA_AV_LCB_FIND); + if (p_lcb) { + rc_handle = bta_av_rc_create(p_cb, AVCT_INT, (UINT8)(p_scb->hdi + 1), p_lcb->lidx); + p_cb->rcb[rc_handle].peer_features = peer_features; + p_cb->rcb[rc_handle].peer_ct_features = peer_ct_features; + p_cb->rcb[rc_handle].peer_tg_features = peer_tg_features; + } +#if (BT_USE_TRACES == TRUE || BT_TRACE_APPL == TRUE) + else { + APPL_TRACE_ERROR("can not find LCB!!"); + } +#endif + } else if (p_scb->use_rc) { + /* can not find AVRC on peer device. report failure */ + p_scb->use_rc = FALSE; + bdcpy(rc_open.peer_addr, p_scb->peer_addr); + rc_open.peer_features = 0; + rc_open.sdp_disc_done = FALSE; + rc_open.status = BTA_AV_FAIL_SDP; + (*p_cb->p_cback)(BTA_AV_RC_OPEN_EVT, (tBTA_AV *) &rc_open); + } + } + } else { + p_cb->rcb[rc_handle].peer_features = peer_features; + rc_feat.rc_handle = rc_handle; + rc_feat.peer_features = peer_features; + rc_feat.peer_ct_features = peer_ct_features; + rc_feat.peer_tg_features = peer_tg_features; + (*p_cb->p_cback)(BTA_AV_RC_FEAT_EVT, (tBTA_AV *) &rc_feat); + } +} + +/******************************************************************************* +** +** Function bta_av_rc_closed +** +** Description Set AVRCP state to closed. +** +** Returns void +** +*******************************************************************************/ +void bta_av_rc_closed(tBTA_AV_DATA *p_data) +{ + tBTA_AV_CB *p_cb = &bta_av_cb; + tBTA_AV_RC_CLOSE rc_close; + tBTA_AV_RC_CONN_CHG *p_msg = (tBTA_AV_RC_CONN_CHG *)p_data; + tBTA_AV_RCB *p_rcb; + tBTA_AV_SCB *p_scb; + int i; + BOOLEAN conn = FALSE; + tBTA_AV_LCB *p_lcb; + + rc_close.rc_handle = BTA_AV_RC_HANDLE_NONE; + p_scb = NULL; + APPL_TRACE_DEBUG("bta_av_rc_closed rc_handle:%d", p_msg->handle); + for (i = 0; i < BTA_AV_NUM_RCB; i++) { + p_rcb = &p_cb->rcb[i]; + APPL_TRACE_DEBUG("bta_av_rc_closed rcb[%d] rc_handle:%d, status=0x%x", i, p_rcb->handle, p_rcb->status); + if (p_rcb->handle == p_msg->handle) { + rc_close.rc_handle = i; + p_rcb->status &= ~BTA_AV_RC_CONN_MASK; + p_rcb->peer_features = 0; + p_rcb->peer_ct_features = 0; + p_rcb->peer_tg_features = 0; + APPL_TRACE_DEBUG(" shdl:%d, lidx:%d", p_rcb->shdl, p_rcb->lidx); + if (p_rcb->shdl) { + if ((p_rcb->shdl - 1) < BTA_AV_NUM_STRS) { + p_scb = bta_av_cb.p_scb[p_rcb->shdl - 1]; + } + if (p_scb) { + bdcpy(rc_close.peer_addr, p_scb->peer_addr); + if (p_scb->rc_handle == p_rcb->handle) { + p_scb->rc_handle = BTA_AV_RC_HANDLE_NONE; + } + APPL_TRACE_DEBUG("shdl:%d, srch:%d", p_rcb->shdl, p_scb->rc_handle); + } + p_rcb->shdl = 0; + } else if (p_rcb->lidx == (BTA_AV_NUM_LINKS + 1) ) { + /* if the RCB uses the extra LCB, use the addr for event and clean it */ + p_lcb = &p_cb->lcb[BTA_AV_NUM_LINKS]; + bdcpy(rc_close.peer_addr, p_msg->peer_addr); + APPL_TRACE_DEBUG("rc_only closed bd_addr:%02x-%02x-%02x-%02x-%02x-%02x", + p_msg->peer_addr[0], p_msg->peer_addr[1], + p_msg->peer_addr[2], p_msg->peer_addr[3], + p_msg->peer_addr[4], p_msg->peer_addr[5]); + p_lcb->conn_msk = 0; + p_lcb->lidx = 0; + } + p_rcb->lidx = 0; + + if ((p_rcb->status & BTA_AV_RC_ROLE_MASK) == BTA_AV_RC_ROLE_INT) { + /* AVCT CCB is deallocated */ + p_rcb->handle = BTA_AV_RC_HANDLE_NONE; + p_rcb->status = 0; + } else { + /* AVCT CCB is still there. dealloc */ + bta_av_del_rc(p_rcb); + + /* if the AVRCP is no longer listening, create the listening channel */ + if (bta_av_cb.rc_acp_handle == BTA_AV_RC_HANDLE_NONE && bta_av_cb.features & BTA_AV_FEAT_RCTG) { + bta_av_rc_create(&bta_av_cb, AVCT_ACP, 0, BTA_AV_NUM_LINKS + 1); + } + } + } else if ((p_rcb->handle != BTA_AV_RC_HANDLE_NONE) && (p_rcb->status & BTA_AV_RC_CONN_MASK)) { + /* at least one channel is still connected */ + conn = TRUE; + } + } + + if (!conn) { + /* no AVRC channels are connected, go back to INIT state */ + bta_av_sm_execute(p_cb, BTA_AV_AVRC_NONE_EVT, NULL); + } + + if (rc_close.rc_handle == BTA_AV_RC_HANDLE_NONE) { + rc_close.rc_handle = p_msg->handle; + bdcpy(rc_close.peer_addr, p_msg->peer_addr); + } + (*p_cb->p_cback)(BTA_AV_RC_CLOSE_EVT, (tBTA_AV *) &rc_close); +} + +/******************************************************************************* +** +** Function bta_av_rc_disc +** +** Description start AVRC SDP discovery. +** +** Returns void +** +*******************************************************************************/ +void bta_av_rc_disc(UINT8 disc) +{ + tBTA_AV_CB *p_cb = &bta_av_cb; + tAVRC_SDP_DB_PARAMS db_params; + UINT16 attr_list[] = {ATTR_ID_SERVICE_CLASS_ID_LIST, + ATTR_ID_BT_PROFILE_DESC_LIST, + ATTR_ID_SUPPORTED_FEATURES + }; + UINT8 hdi; + tBTA_AV_SCB *p_scb; + UINT8 *p_addr = NULL; + UINT8 rc_handle; + + APPL_TRACE_DEBUG("bta_av_rc_disc 0x%x, %d", disc, bta_av_cb.disc); + if ((bta_av_cb.disc != 0) || (disc == 0)) { + return; + } + + if ((disc & BTA_AV_CHNL_MSK) == BTA_AV_CHNL_MSK) { + /* this is the rc handle/index to tBTA_AV_RCB */ + rc_handle = disc & (~BTA_AV_CHNL_MSK); + if (p_cb->rcb[rc_handle].lidx) { + p_addr = p_cb->lcb[p_cb->rcb[rc_handle].lidx - 1].addr; + } + } else { + hdi = (disc & BTA_AV_HNDL_MSK) - 1; + p_scb = p_cb->p_scb[hdi]; + + if (p_scb) { + APPL_TRACE_DEBUG("rc_handle %d", p_scb->rc_handle); + p_addr = p_scb->peer_addr; + } + } + + if (p_addr) { + /* allocate discovery database */ + if (p_cb->p_disc_db == NULL) { + p_cb->p_disc_db = (tSDP_DISCOVERY_DB *) osi_malloc(BTA_AV_DISC_BUF_SIZE); + } + + if (p_cb->p_disc_db) { + /* set up parameters */ + db_params.db_len = BTA_AV_DISC_BUF_SIZE; + db_params.num_attr = 3; + db_params.p_db = p_cb->p_disc_db; + db_params.p_attrs = attr_list; + + /* searching for UUID_SERVCLASS_AV_REMOTE_CONTROL gets both TG and CT */ + if (AVRC_FindService(UUID_SERVCLASS_AV_REMOTE_CONTROL, p_addr, &db_params, + bta_av_avrc_sdp_cback) == AVRC_SUCCESS) { + p_cb->disc = disc; + APPL_TRACE_DEBUG("disc %d", p_cb->disc); + } + } + } +} + +/******************************************************************************* +** +** Function bta_av_dereg_comp +** +** Description deregister complete. free the stream control block. +** +** Returns void +** +*******************************************************************************/ +void bta_av_dereg_comp(tBTA_AV_DATA *p_data) +{ + tBTA_AV_CB *p_cb = &bta_av_cb; + tBTA_AV_SCB *p_scb; + tBTA_UTL_COD cod; + UINT8 mask; + BT_HDR *p_buf; + + /* find the stream control block */ + p_scb = bta_av_hndl_to_scb(p_data->hdr.layer_specific); + + if (p_scb) { + APPL_TRACE_DEBUG("deregistered %d(h%d)", p_scb->chnl, p_scb->hndl); + mask = BTA_AV_HNDL_TO_MSK(p_scb->hdi); + if (p_scb->chnl == BTA_AV_CHNL_AUDIO) { + p_cb->reg_audio &= ~mask; + if ((p_cb->conn_audio & mask) && bta_av_cb.audio_open_cnt) { + /* this channel is still marked as open. decrease the count */ + bta_av_cb.audio_open_cnt--; + } + p_cb->conn_audio &= ~mask; + + if (p_scb->q_tag == BTA_AV_Q_TAG_STREAM && p_scb->a2d_list) { + /* make sure no buffers are in a2d_list */ + while (!list_is_empty(p_scb->a2d_list)) { + p_buf = (BT_HDR *)list_front(p_scb->a2d_list); + list_remove(p_scb->a2d_list, p_buf); + osi_free(p_buf); + } + } + + /* remove the A2DP SDP record, if no more audio stream is left */ + if (!p_cb->reg_audio) { +#if( defined BTA_AR_INCLUDED ) && (BTA_AR_INCLUDED == TRUE) + bta_ar_dereg_avrc (UUID_SERVCLASS_AV_REMOTE_CONTROL, BTA_ID_AV); +#endif + bta_av_del_sdp_rec(&p_cb->sdp_a2d_handle); + bta_sys_remove_uuid(UUID_SERVCLASS_AUDIO_SOURCE); + +#if (BTA_AV_SINK_INCLUDED == TRUE) + bta_av_del_sdp_rec(&p_cb->sdp_a2d_snk_handle); + bta_sys_remove_uuid(UUID_SERVCLASS_AUDIO_SINK); +#endif + } + } else { + p_cb->reg_video &= ~mask; + /* make sure that this channel is not connected */ + p_cb->conn_video &= ~mask; + /* remove the VDP SDP record, (only one video stream at most) */ + bta_av_del_sdp_rec(&p_cb->sdp_vdp_handle); + bta_sys_remove_uuid(UUID_SERVCLASS_VIDEO_SOURCE); + } + + /* free the delay timer for AVRC CT */ + bta_sys_free_timer(&p_scb->timer); + list_free(p_scb->a2d_list); + p_scb->a2d_list = NULL; + utl_freebuf((void **)&p_cb->p_scb[p_scb->hdi]); + } + + APPL_TRACE_DEBUG("audio 0x%x, video: 0x%x, disable:%d", + p_cb->reg_audio, p_cb->reg_video, p_cb->disabling); + /* if no stream control block is active */ + if ((p_cb->reg_audio + p_cb->reg_video) == 0) { +#if( defined BTA_AR_INCLUDED ) && (BTA_AR_INCLUDED == TRUE) + /* deregister from AVDT */ + bta_ar_dereg_avdt(BTA_ID_AV); + + /* deregister from AVCT */ + bta_ar_dereg_avrc (UUID_SERVCLASS_AV_REM_CTRL_TARGET, BTA_ID_AV); + bta_ar_dereg_avct(BTA_ID_AV); +#endif + + if (p_cb->disabling) { + p_cb->disabling = FALSE; + bta_av_cb.features = 0; + } + + /* Clear the Capturing/Rendering service class bit */ + if (p_data->api_reg.tsep == AVDT_TSEP_SRC) { + cod.service = BTM_COD_SERVICE_CAPTURING | BTM_COD_SERVICE_AUDIO; + } else { +#if (BTA_AV_SINK_INCLUDED == TRUE) + cod.service = BTM_COD_SERVICE_RENDERING | BTM_COD_SERVICE_AUDIO; +#endif + } + utl_set_device_class(&cod, BTA_UTL_CLR_COD_SERVICE_CLASS); + } +} +#endif /* BTA_AV_INCLUDED */ diff --git a/lib/bt/host/bluedroid/bta/av/bta_av_api.c b/lib/bt/host/bluedroid/bta/av/bta_av_api.c new file mode 100644 index 00000000..01993eec --- /dev/null +++ b/lib/bt/host/bluedroid/bta/av/bta_av_api.c @@ -0,0 +1,616 @@ +/****************************************************************************** + * + * Copyright (C) 2011-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This is the implementation of the API for the advanced audio/video (AV) + * subsystem of BTA, Broadcom's Bluetooth application layer for mobile + * phones. + * + ******************************************************************************/ + +#include "common/bt_target.h" +#if defined(BTA_AV_INCLUDED) && (BTA_AV_INCLUDED == TRUE) + +#include "osi/allocator.h" +#include "bta/bta_api.h" +#include "bta/bta_sys.h" +#include "bta/bta_av_api.h" +#include "bta_av_int.h" +#include + +/***************************************************************************** +** Constants +*****************************************************************************/ + +static const tBTA_SYS_REG bta_av_reg = { + bta_av_hdl_event, + BTA_AvDisable +}; + +/******************************************************************************* +** +** Function BTA_AvEnable +** +** Description Enable the advanced audio/video service. When the enable +** operation is complete the callback function will be +** called with a BTA_AV_ENABLE_EVT. This function must +** be called before other function in the AV API are +** called. +** +** Returns void +** +*******************************************************************************/ +void BTA_AvEnable(tBTA_SEC sec_mask, tBTA_AV_FEAT features, tBTA_AV_CBACK *p_cback) +{ + tBTA_AV_API_ENABLE *p_buf; + + /* register with BTA system manager */ + bta_sys_register(BTA_ID_AV, &bta_av_reg); + + if ((p_buf = (tBTA_AV_API_ENABLE *) osi_malloc(sizeof(tBTA_AV_API_ENABLE))) != NULL) { + p_buf->hdr.event = BTA_AV_API_ENABLE_EVT; + p_buf->p_cback = p_cback; + p_buf->features = features; + p_buf->sec_mask = sec_mask; + bta_sys_sendmsg(p_buf); + } +} + +/******************************************************************************* +** +** Function BTA_AvDisable +** +** Description Disable the advanced audio/video service. +** +** Returns void +** +*******************************************************************************/ +void BTA_AvDisable(void) +{ + BT_HDR *p_buf; + + bta_sys_deregister(BTA_ID_AV); + if ((p_buf = (BT_HDR *) osi_malloc(sizeof(BT_HDR))) != NULL) { + p_buf->event = BTA_AV_API_DISABLE_EVT; + bta_sys_sendmsg(p_buf); + } +} + +/******************************************************************************* +** +** Function BTA_AvRegister +** +** Description Register the audio or video service to stack. When the +** operation is complete the callback function will be +** called with a BTA_AV_REGISTER_EVT. This function must +** be called before AVDT stream is open. +** +** +** Returns void +** +*******************************************************************************/ +void BTA_AvRegister(tBTA_AV_CHNL chnl, const char *p_service_name, UINT8 app_id, + tBTA_AV_DATA_CBACK *p_data_cback, tBTA_AV_CO_FUNCTS *bta_av_cos, + tBTA_AVRC_CO_FUNCTS *bta_avrc_cos, UINT8 tsep) +{ + tBTA_AV_API_REG *p_buf; + + + if ((p_buf = (tBTA_AV_API_REG *) osi_malloc(sizeof(tBTA_AV_API_REG))) != NULL) { + p_buf->hdr.layer_specific = chnl; + p_buf->hdr.event = BTA_AV_API_REGISTER_EVT; + if (p_service_name) { + BCM_STRNCPY_S(p_buf->p_service_name, p_service_name, BTA_SERVICE_NAME_LEN); + } else { + p_buf->p_service_name[0] = '\0'; + } + p_buf->app_id = app_id; + p_buf->p_app_data_cback = p_data_cback; + p_buf->bta_av_cos = bta_av_cos; + p_buf->bta_avrc_cos = bta_avrc_cos; + p_buf->tsep = tsep; + bta_sys_sendmsg(p_buf); + } +} + +/******************************************************************************* +** +** Function BTA_AvDeregister +** +** Description Deregister the audio or video service +** +** Returns void +** +*******************************************************************************/ +void BTA_AvDeregister(tBTA_AV_HNDL hndl) +{ + BT_HDR *p_buf; + + if ((p_buf = (BT_HDR *) osi_malloc(sizeof(BT_HDR))) != NULL) { + p_buf->layer_specific = hndl; + p_buf->event = BTA_AV_API_DEREGISTER_EVT; + bta_sys_sendmsg(p_buf); + } +} + +/******************************************************************************* +** +** Function BTA_AvOpen +** +** Description Opens an advanced audio/video connection to a peer device. +** When connection is open callback function is called +** with a BTA_AV_OPEN_EVT. +** +** Returns void +** +*******************************************************************************/ +void BTA_AvOpen(BD_ADDR bd_addr, tBTA_AV_HNDL handle, BOOLEAN use_rc, tBTA_SEC sec_mask, + UINT16 uuid) +{ + tBTA_AV_API_OPEN *p_buf; + + if ((p_buf = (tBTA_AV_API_OPEN *) osi_malloc(sizeof(tBTA_AV_API_OPEN))) != NULL) { + p_buf->hdr.event = BTA_AV_API_OPEN_EVT; + p_buf->hdr.layer_specific = handle; + bdcpy(p_buf->bd_addr, bd_addr); + p_buf->use_rc = use_rc; + p_buf->sec_mask = sec_mask; + p_buf->switch_res = BTA_AV_RS_NONE; + p_buf->uuid = uuid; + bta_sys_sendmsg(p_buf); + } +} + +/******************************************************************************* +** +** Function BTA_AvClose +** +** Description Close the current streams. +** +** Returns void +** +*******************************************************************************/ +void BTA_AvClose(tBTA_AV_HNDL handle) +{ + BT_HDR *p_buf; + + if ((p_buf = (BT_HDR *) osi_malloc(sizeof(BT_HDR))) != NULL) { + p_buf->event = BTA_AV_API_CLOSE_EVT; + p_buf->layer_specific = handle; + bta_sys_sendmsg(p_buf); + } +} + +/******************************************************************************* +** +** Function BTA_AvDisconnect +** +** Description Close the connection to the address. +** +** Returns void +** +*******************************************************************************/ +void BTA_AvDisconnect(BD_ADDR bd_addr) +{ + tBTA_AV_API_DISCNT *p_buf; + + if ((p_buf = (tBTA_AV_API_DISCNT *) osi_malloc(sizeof(tBTA_AV_API_DISCNT))) != NULL) { + p_buf->hdr.event = BTA_AV_API_DISCONNECT_EVT; + bdcpy(p_buf->bd_addr, bd_addr); + bta_sys_sendmsg(p_buf); + } +} + +/******************************************************************************* +** +** Function BTA_AvStart +** +** Description Start audio/video stream data transfer. +** +** Returns void +** +*******************************************************************************/ +void BTA_AvStart(void) +{ + BT_HDR *p_buf; + + if ((p_buf = (BT_HDR *) osi_malloc(sizeof(BT_HDR))) != NULL) { + p_buf->event = BTA_AV_API_START_EVT; + bta_sys_sendmsg(p_buf); + } +} + +/******************************************************************************* +** +** Function BTA_AvEnable_Sink +** +** Description Enable/Disable A2DP Sink.. +** +** Returns void +** +*******************************************************************************/ +void BTA_AvEnable_Sink(int enable) +{ +#if (BTA_AV_SINK_INCLUDED == TRUE) + BT_HDR *p_buf; + if ((p_buf = (BT_HDR *) osi_malloc(sizeof(BT_HDR))) != NULL) { + p_buf->event = BTA_AV_API_SINK_ENABLE_EVT; + p_buf->layer_specific = enable; + bta_sys_sendmsg(p_buf); + } +#else + return; +#endif +} + +/******************************************************************************* +** +** Function BTA_AvStop +** +** Description Stop audio/video stream data transfer. +** If suspend is TRUE, this function sends AVDT suspend signal +** to the connected peer(s). +** +** Returns void +** +*******************************************************************************/ +void BTA_AvStop(BOOLEAN suspend) +{ + tBTA_AV_API_STOP *p_buf; + + if ((p_buf = (tBTA_AV_API_STOP *) osi_malloc(sizeof(tBTA_AV_API_STOP))) != NULL) { + p_buf->hdr.event = BTA_AV_API_STOP_EVT; + p_buf->flush = TRUE; + p_buf->suspend = suspend; + bta_sys_sendmsg(p_buf); + } +} + +/******************************************************************************* +** +** Function BTA_AvReconfig +** +** Description Reconfigure the audio/video stream. +** If suspend is TRUE, this function tries the suspend/reconfigure +** procedure first. +** If suspend is FALSE or when suspend/reconfigure fails, +** this function closes and re-opens the AVDT connection. +** +** Returns void +** +*******************************************************************************/ +void BTA_AvReconfig(tBTA_AV_HNDL hndl, BOOLEAN suspend, UINT8 sep_info_idx, + UINT8 *p_codec_info, UINT8 num_protect, UINT8 *p_protect_info) +{ + tBTA_AV_API_RCFG *p_buf; + + if ((p_buf = (tBTA_AV_API_RCFG *) osi_malloc((UINT16) (sizeof(tBTA_AV_API_RCFG) + num_protect))) != NULL) { + p_buf->hdr.layer_specific = hndl; + p_buf->hdr.event = BTA_AV_API_RECONFIG_EVT; + p_buf->num_protect = num_protect; + p_buf->suspend = suspend; + p_buf->sep_info_idx = sep_info_idx; + memcpy(p_buf->codec_info, p_codec_info, AVDT_CODEC_SIZE); + if (p_protect_info && num_protect) { + memcpy(p_buf->p_protect_info, p_protect_info, num_protect); + } + bta_sys_sendmsg(p_buf); + } +} + +/******************************************************************************* +** +** Function BTA_AvProtectReq +** +** Description Send a content protection request. This function can only +** be used if AV is enabled with feature BTA_AV_FEAT_PROTECT. +** +** Returns void +** +*******************************************************************************/ +void BTA_AvProtectReq(tBTA_AV_HNDL hndl, UINT8 *p_data, UINT16 len) +{ + tBTA_AV_API_PROTECT_REQ *p_buf; + + if ((p_buf = (tBTA_AV_API_PROTECT_REQ *) osi_malloc((UINT16) (sizeof(tBTA_AV_API_PROTECT_REQ) + len))) != NULL) { + p_buf->hdr.layer_specific = hndl; + p_buf->hdr.event = BTA_AV_API_PROTECT_REQ_EVT; + p_buf->len = len; + if (p_data == NULL) { + p_buf->p_data = NULL; + } else { + p_buf->p_data = (UINT8 *) (p_buf + 1); + memcpy(p_buf->p_data, p_data, len); + } + bta_sys_sendmsg(p_buf); + } +} + +/******************************************************************************* +** +** Function BTA_AvProtectRsp +** +** Description Send a content protection response. This function must +** be called if a BTA_AV_PROTECT_REQ_EVT is received. +** This function can only be used if AV is enabled with +** feature BTA_AV_FEAT_PROTECT. +** +** Returns void +** +*******************************************************************************/ +void BTA_AvProtectRsp(tBTA_AV_HNDL hndl, UINT8 error_code, UINT8 *p_data, UINT16 len) +{ + tBTA_AV_API_PROTECT_RSP *p_buf; + + if ((p_buf = (tBTA_AV_API_PROTECT_RSP *) osi_malloc((UINT16) (sizeof(tBTA_AV_API_PROTECT_RSP) + len))) != NULL) { + p_buf->hdr.layer_specific = hndl; + p_buf->hdr.event = BTA_AV_API_PROTECT_RSP_EVT; + p_buf->len = len; + p_buf->error_code = error_code; + if (p_data == NULL) { + p_buf->p_data = NULL; + } else { + p_buf->p_data = (UINT8 *) (p_buf + 1); + memcpy(p_buf->p_data, p_data, len); + } + bta_sys_sendmsg(p_buf); + } +} + +/******************************************************************************* +** +** Function BTA_SetDelayValue +** +** Description Set delay report value +** +** Returns void +** +*******************************************************************************/ +void BTA_SetDelayValue(UINT16 delay_value) +{ + tBTA_AV_API_SET_DELAY_VALUE *p_buf; + + if ((p_buf = (tBTA_AV_API_SET_DELAY_VALUE *) osi_malloc(sizeof(tBTA_AV_API_SET_DELAY_VALUE))) != NULL) { + p_buf->hdr.event = BTA_AV_API_SET_DELAY_VALUE_EVT; + p_buf->delay_value = delay_value; + bta_sys_sendmsg(p_buf); + } +} + +/******************************************************************************* +** +** Function BTA_GetDelayValue +** +** Description Get delay report value +** +** Returns void +** +*******************************************************************************/ +void BTA_GetDelayValue(void) +{ + tBTA_AV_API_GET_DELAY_VALUE *p_buf; + + if ((p_buf = (tBTA_AV_API_GET_DELAY_VALUE *) osi_malloc(sizeof(tBTA_AV_API_GET_DELAY_VALUE))) != NULL) { + p_buf->hdr.event = BTA_AV_API_GET_DELAY_VALUE_EVT; + bta_sys_sendmsg(p_buf); + } +} + +/******************************************************************************* +** +** Function BTA_AvRemoteCmd +** +** Description Send a remote control command. This function can only +** be used if AV is enabled with feature BTA_AV_FEAT_RCCT. +** +** Returns void +** +*******************************************************************************/ +void BTA_AvRemoteCmd(UINT8 rc_handle, UINT8 label, tBTA_AV_RC rc_id, tBTA_AV_STATE key_state) +{ + tBTA_AV_API_REMOTE_CMD *p_buf; + + if ((p_buf = (tBTA_AV_API_REMOTE_CMD *) osi_malloc(sizeof(tBTA_AV_API_REMOTE_CMD))) != NULL) { + p_buf->hdr.event = BTA_AV_API_REMOTE_CMD_EVT; + p_buf->hdr.layer_specific = rc_handle; + p_buf->msg.op_id = rc_id; + p_buf->msg.state = key_state; + p_buf->msg.p_pass_data = NULL; + p_buf->msg.pass_len = 0; + p_buf->label = label; + bta_sys_sendmsg(p_buf); + } +} + +/******************************************************************************* +** +** Function BTA_AvVendorCmd +** +** Description Send a vendor dependent remote control command. This +** function can only be used if AV is enabled with feature +** BTA_AV_FEAT_VENDOR. +** +** Returns void +** +*******************************************************************************/ +void BTA_AvVendorCmd(UINT8 rc_handle, UINT8 label, tBTA_AV_CODE cmd_code, UINT8 *p_data, UINT16 len) +{ + tBTA_AV_API_VENDOR *p_buf; + + if ((p_buf = (tBTA_AV_API_VENDOR *) osi_malloc((UINT16) (sizeof(tBTA_AV_API_VENDOR) + len))) != NULL) { + p_buf->hdr.event = BTA_AV_API_VENDOR_CMD_EVT; + p_buf->hdr.layer_specific = rc_handle; + p_buf->msg.hdr.ctype = cmd_code; + p_buf->msg.hdr.subunit_type = AVRC_SUB_PANEL; + p_buf->msg.hdr.subunit_id = 0; + p_buf->msg.company_id = p_bta_av_cfg->company_id; + p_buf->label = label; + p_buf->msg.vendor_len = len; + if (p_data == NULL) { + p_buf->msg.p_vendor_data = NULL; + } else { + p_buf->msg.p_vendor_data = (UINT8 *) (p_buf + 1); + memcpy(p_buf->msg.p_vendor_data, p_data, len); + } + bta_sys_sendmsg(p_buf); + } +} + +/******************************************************************************* +** +** Function BTA_AvVendorRsp +** +** Description Send a vendor dependent remote control response. +** This function must be called if a BTA_AV_VENDOR_CMD_EVT +** is received. This function can only be used if AV is +** enabled with feature BTA_AV_FEAT_VENDOR. +** +** Returns void +** +*******************************************************************************/ +void BTA_AvVendorRsp(UINT8 rc_handle, UINT8 label, tBTA_AV_CODE rsp_code, UINT8 *p_data, UINT16 len, UINT32 company_id) +{ + tBTA_AV_API_VENDOR *p_buf; + + if ((p_buf = (tBTA_AV_API_VENDOR *) osi_malloc((UINT16) (sizeof(tBTA_AV_API_VENDOR) + len))) != NULL) { + p_buf->hdr.event = BTA_AV_API_VENDOR_RSP_EVT; + p_buf->hdr.layer_specific = rc_handle; + p_buf->msg.hdr.ctype = rsp_code; + p_buf->msg.hdr.subunit_type = AVRC_SUB_PANEL; + p_buf->msg.hdr.subunit_id = 0; + if (company_id) { + p_buf->msg.company_id = company_id; + } else { + p_buf->msg.company_id = p_bta_av_cfg->company_id; + } + p_buf->label = label; + p_buf->msg.vendor_len = len; + if (p_data == NULL) { + p_buf->msg.p_vendor_data = NULL; + } else { + p_buf->msg.p_vendor_data = (UINT8 *) (p_buf + 1); + memcpy(p_buf->msg.p_vendor_data, p_data, len); + } + bta_sys_sendmsg(p_buf); + } +} + +/******************************************************************************* +** +** Function BTA_AvOpenRc +** +** Description Open an AVRCP connection toward the device with the +** specified handle +** +** Returns void +** +*******************************************************************************/ +void BTA_AvOpenRc(tBTA_AV_HNDL handle) +{ + tBTA_AV_API_OPEN_RC *p_buf; + + if ((p_buf = (tBTA_AV_API_OPEN_RC *) osi_malloc(sizeof(tBTA_AV_API_OPEN_RC))) != NULL) { + p_buf->hdr.event = BTA_AV_API_RC_OPEN_EVT; + p_buf->hdr.layer_specific = handle; + bta_sys_sendmsg(p_buf); + } +} + +/******************************************************************************* +** +** Function BTA_AvCloseRc +** +** Description Close an AVRCP connection +** +** Returns void +** +*******************************************************************************/ +void BTA_AvCloseRc(UINT8 rc_handle) +{ + tBTA_AV_API_CLOSE_RC *p_buf; + + if ((p_buf = (tBTA_AV_API_CLOSE_RC *) osi_malloc(sizeof(tBTA_AV_API_CLOSE_RC))) != NULL) { + p_buf->hdr.event = BTA_AV_API_RC_CLOSE_EVT; + p_buf->hdr.layer_specific = rc_handle; + bta_sys_sendmsg(p_buf); + } +} + +/******************************************************************************* +** +** Function BTA_AvMetaRsp +** +** Description Send a Metadata/Advanced Control response. The message contained +** in p_pkt can be composed with AVRC utility functions. +** This function can only be used if AV is enabled with feature +** BTA_AV_FEAT_METADATA. +** +** Returns void +** +*******************************************************************************/ +void BTA_AvMetaRsp(UINT8 rc_handle, UINT8 label, tBTA_AV_CODE rsp_code, + BT_HDR *p_pkt) +{ + tBTA_AV_API_META_RSP *p_buf; + + if ((p_buf = (tBTA_AV_API_META_RSP *) osi_malloc((UINT16) (sizeof(tBTA_AV_API_META_RSP)))) != NULL) { + p_buf->hdr.event = BTA_AV_API_META_RSP_EVT; + p_buf->hdr.layer_specific = rc_handle; + p_buf->rsp_code = rsp_code; + p_buf->p_pkt = p_pkt; + p_buf->is_rsp = TRUE; + p_buf->label = label; + + bta_sys_sendmsg(p_buf); + } else if (p_pkt) { + osi_free(p_pkt); + } +} + +/******************************************************************************* +** +** Function BTA_AvMetaCmd +** +** Description Send a Metadata/Advanced Control command. The message contained +** in p_pkt can be composed with AVRC utility functions. +** This function can only be used if AV is enabled with feature +** BTA_AV_FEAT_METADATA. +** This message is sent only when the peer supports the TG role. +*8 The only command makes sense right now is the absolute volume command. +** +** Returns void +** +*******************************************************************************/ +void BTA_AvMetaCmd(UINT8 rc_handle, UINT8 label, tBTA_AV_CMD cmd_code, BT_HDR *p_pkt) +{ + tBTA_AV_API_META_RSP *p_buf; + + if ((p_buf = (tBTA_AV_API_META_RSP *) osi_malloc((UINT16) (sizeof(tBTA_AV_API_META_RSP)))) != NULL) { + p_buf->hdr.event = BTA_AV_API_META_RSP_EVT; + p_buf->hdr.layer_specific = rc_handle; + p_buf->p_pkt = p_pkt; + p_buf->rsp_code = cmd_code; + p_buf->is_rsp = FALSE; + p_buf->label = label; + + bta_sys_sendmsg(p_buf); + } +} + +#endif /* BTA_AV_INCLUDED */ diff --git a/lib/bt/host/bluedroid/bta/av/bta_av_cfg.c b/lib/bt/host/bluedroid/bta/av/bta_av_cfg.c new file mode 100644 index 00000000..ef22d793 --- /dev/null +++ b/lib/bt/host/bluedroid/bta/av/bta_av_cfg.c @@ -0,0 +1,110 @@ +/****************************************************************************** + * + * Copyright (C) 2005-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains compile-time configurable constants for advanced + * audio/video + * + ******************************************************************************/ + +#include + +#include "common/bt_target.h" +#include "bta/bta_api.h" +#include "bta_av_int.h" + +#if defined(BTA_AV_INCLUDED) && (BTA_AV_INCLUDED == TRUE) + +#ifndef BTA_AV_RC_PASS_RSP_CODE +#define BTA_AV_RC_PASS_RSP_CODE BTA_AV_RSP_NOT_IMPL +#endif + +const UINT32 bta_av_meta_caps_co_ids[] = { + AVRC_CO_METADATA, + AVRC_CO_BROADCOM +}; + +/* AVRCP supported categories */ +#define BTA_AV_RC_SNK_SUPF_CT (AVRC_SUPF_CT_CAT1) +#define BTA_AV_RC_SRC_SUPF_CT (AVRC_SUPF_CT_CAT2) + + +/* Added to modify +** 1. flush timeout +** 2. Remove Group navigation support in SupportedFeatures +** 3. GetCapabilities supported event_ids list +** 4. GetCapabilities supported event_ids count +*/ +/* Flushing partial avdtp packets can cause some headsets to disconnect the link + if receiving partial a2dp frames */ +const UINT16 bta_av_audio_flush_to[] = { + 0, /* 1 stream */ + 0, /* 2 streams */ + 0, /* 3 streams */ + 0, /* 4 streams */ + 0 /* 5 streams */ +}; /* AVDTP audio transport channel flush timeout */ + +/* Note: Android doesnt support AVRC_SUPF_TG_GROUP_NAVI */ +/* Note: if AVRC_SUPF_TG_GROUP_NAVI is set, bta_av_cfg.avrc_group should be TRUE */ +#if AVRC_METADATA_INCLUDED == TRUE +#define BTA_AV_RC_SNK_SUPF_TG (AVRC_SUPF_TG_CAT2) /* TODO: | AVRC_SUPF_TG_APP_SETTINGS) */ +#define BTA_AV_RC_SRC_SUPF_TG (AVRC_SUPF_TG_CAT1) /* TODO: | AVRC_SUPF_TG_APP_SETTINGS) */ +#else +#define BTA_AV_RC_SNK_SUPF_TG (AVRC_SUPF_TG_CAT2) +#define BTA_AV_RC_SRC_SUPF_TG (AVRC_SUPF_TG_CAT1) +#endif + +/* the MTU for the AVRCP browsing channel */ +#ifndef BTA_AV_MAX_RC_BR_MTU +#define BTA_AV_MAX_RC_BR_MTU 1008 +#endif + +const tBTA_AV_CFG bta_av_cfg = { + AVRC_CO_BROADCOM, /* AVRCP Company ID */ +#if AVRC_METADATA_INCLUDED == TRUE + 512, /* AVRCP MTU at L2CAP for control channel */ +#else + 48, /* AVRCP MTU at L2CAP for control channel */ +#endif + BTA_AV_MAX_RC_BR_MTU, /* AVRCP MTU at L2CAP for browsing channel */ + BTA_AV_RC_SNK_SUPF_CT, /* AVRCP controller categories as SNK */ + BTA_AV_RC_SNK_SUPF_TG, /* AVRCP target categories as SNK */ + BTA_AV_RC_SRC_SUPF_CT, /* AVRCP controller categories as SRC */ + BTA_AV_RC_SRC_SUPF_TG, /* AVRCP target categories as SRC */ + 672, /* AVDTP signaling channel MTU at L2CAP */ + BTA_AV_MAX_A2DP_MTU, /* AVDTP audio transport channel MTU at L2CAP */ + bta_av_audio_flush_to, /* AVDTP audio transport channel flush timeout */ + 6, /* AVDTP audio channel max data queue size */ + BTA_AV_MAX_VDP_MTU, /* AVDTP video transport channel MTU at L2CAP */ + 600, /* AVDTP video transport channel flush timeout */ + FALSE, /* TRUE, to accept AVRC 1.3 group nevigation command */ + FALSE, /* FALSE, does not support browsing channel */ + 2, /* company id count in p_meta_co_ids */ + BTA_AV_RC_PASS_RSP_CODE,/* the default response code for pass through commands */ + bta_av_meta_caps_co_ids,/* the metadata Get Capabilities response for company id */ + NULL, /* the action function table for VDP stream */ + NULL, /* action function to register VDP */ + {0}, /* Default AVRCP controller name */ + {0}, /* Default AVRCP target name */ +}; + +tBTA_AV_CFG *p_bta_av_cfg = (tBTA_AV_CFG *) &bta_av_cfg; + +#endif /* if defined(BTA_AV_INCLUDED) && (BTA_AV_INCLUDED == TRUE) */ diff --git a/lib/bt/host/bluedroid/bta/av/bta_av_ci.c b/lib/bt/host/bluedroid/bta/av/bta_av_ci.c new file mode 100644 index 00000000..f1d2e9d0 --- /dev/null +++ b/lib/bt/host/bluedroid/bta/av/bta_av_ci.c @@ -0,0 +1,94 @@ +/****************************************************************************** + * + * Copyright (C) 2005-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This is the implementation file for advanced audio/video call-in + * functions. + * + ******************************************************************************/ + +#include "common/bt_target.h" +#include "bta/bta_api.h" +#include "bta/bta_sys.h" +#include "bta_av_int.h" +#include "bta/bta_av_ci.h" +#include "osi/allocator.h" + +#include + +#if defined(BTA_AV_INCLUDED) && (BTA_AV_INCLUDED == TRUE) +/******************************************************************************* +** +** Function bta_av_ci_src_data_ready +** +** Description This function sends an event to the AV indicating that +** the phone has audio stream data ready to send and AV +** should call bta_av_co_audio_src_data_path() or +** bta_av_co_video_src_data_path(). +** +** Returns void +** +*******************************************************************************/ +void bta_av_ci_src_data_ready(tBTA_AV_CHNL chnl) +{ + BT_HDR *p_buf; + + if ((p_buf = (BT_HDR *) osi_malloc(sizeof(BT_HDR))) != NULL) { + p_buf->layer_specific = chnl; + p_buf->event = BTA_AV_CI_SRC_DATA_READY_EVT; + bta_sys_sendmsg(p_buf); + } +} + +/******************************************************************************* +** +** Function bta_av_ci_setconfig +** +** Description This function must be called in response to function +** bta_av_co_audio_setconfig() or bta_av_co_video_setconfig. +** Parameter err_code is set to an AVDTP status value; +** AVDT_SUCCESS if the codec configuration is ok, +** otherwise error. +** +** Returns void +** +*******************************************************************************/ +void bta_av_ci_setconfig(tBTA_AV_HNDL hndl, UINT8 err_code, UINT8 category, + UINT8 num_seid, UINT8 *p_seid, BOOLEAN recfg_needed, UINT8 avdt_handle) +{ + tBTA_AV_CI_SETCONFIG *p_buf; + + if ((p_buf = (tBTA_AV_CI_SETCONFIG *) osi_malloc(sizeof(tBTA_AV_CI_SETCONFIG) + num_seid)) != NULL) { + p_buf->hdr.layer_specific = hndl; + p_buf->hdr.event = (err_code == AVDT_SUCCESS) ? + BTA_AV_CI_SETCONFIG_OK_EVT : BTA_AV_CI_SETCONFIG_FAIL_EVT; + p_buf->err_code = err_code; + p_buf->category = category; + p_buf->recfg_needed = recfg_needed; + p_buf->avdt_handle = avdt_handle; + p_buf->num_seid = num_seid; + if (p_seid && num_seid) { + memcpy(p_buf->p_seid, p_seid, num_seid); + } + + bta_sys_sendmsg(p_buf); + } +} + +#endif /* #if defined(BTA_AV_INCLUDED) && (BTA_AV_INCLUDED == TRUE) */ diff --git a/lib/bt/host/bluedroid/bta/av/bta_av_main.c b/lib/bt/host/bluedroid/bta/av/bta_av_main.c new file mode 100644 index 00000000..2fde06f6 --- /dev/null +++ b/lib/bt/host/bluedroid/bta/av/bta_av_main.c @@ -0,0 +1,1416 @@ +/****************************************************************************** + * + * Copyright (C) 2004-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This is the main implementation file for the BTA advanced audio/video. + * + ******************************************************************************/ + +#include +#include + +#include "common/bt_target.h" +#include "common/bt_trace.h" +#include "osi/allocator.h" + +#if defined(BTA_AV_INCLUDED) && (BTA_AV_INCLUDED == TRUE) +#include "bta_av_int.h" +#include "bta/utl.h" +#include "stack/l2c_api.h" +#include "stack/l2cdefs.h" +#include "bta/bta_av_co.h" +#include "stack/a2d_api.h" +#if( defined BTA_AR_INCLUDED ) && (BTA_AR_INCLUDED == TRUE) +#include "bta/bta_ar_api.h" +#endif +#include "osi/osi.h" + +/***************************************************************************** +** Constants and types +*****************************************************************************/ + +/* AVDTP protocol timeout values */ +#define BTIF_AVK_SERVICE_NAME "Advanced Audio Sink" + +#ifndef BTA_AV_RET_TOUT +#define BTA_AV_RET_TOUT 4 +#endif + +#ifndef BTA_AV_SIG_TOUT +#define BTA_AV_SIG_TOUT 4 +#endif + +#ifndef BTA_AV_IDLE_TOUT +#define BTA_AV_IDLE_TOUT 10 +#endif + +/* the delay time in milliseconds to retry role switch */ +#ifndef BTA_AV_RS_TIME_VAL +#define BTA_AV_RS_TIME_VAL 1000 +#endif + +/* state machine states */ +enum { + BTA_AV_INIT_ST, + BTA_AV_OPEN_ST +}; + +/* state machine action enumeration list */ +enum { + BTA_AV_DISABLE, + BTA_AV_RC_OPENED, + BTA_AV_RC_REMOTE_CMD, + BTA_AV_RC_VENDOR_CMD, + BTA_AV_RC_VENDOR_RSP, + BTA_AV_RC_FREE_RSP, + BTA_AV_RC_FREE_MSG, + BTA_AV_RC_META_RSP, + BTA_AV_RC_MSG, + BTA_AV_RC_CLOSE, + BTA_AV_NUM_ACTIONS +}; + +#define BTA_AV_IGNORE BTA_AV_NUM_ACTIONS + +/* type for action functions */ +typedef void (*tBTA_AV_ACTION)(tBTA_AV_CB *p_cb, tBTA_AV_DATA *p_data); + +/* action functions */ +const tBTA_AV_ACTION bta_av_action[] = { + bta_av_disable, + bta_av_rc_opened, + bta_av_rc_remote_cmd, + bta_av_rc_vendor_cmd, + bta_av_rc_vendor_rsp, + bta_av_rc_free_rsp, + bta_av_rc_free_msg, + bta_av_rc_meta_rsp, + bta_av_rc_msg, + bta_av_rc_close, + NULL +}; + +/* state table information */ +#define BTA_AV_ACTION_COL 0 /* position of actions */ +#define BTA_AV_NEXT_STATE 1 /* position of next state */ +#define BTA_AV_NUM_COLS 2 /* number of columns in state tables */ + +/* state table for init state */ +static const UINT8 bta_av_st_init[][BTA_AV_NUM_COLS] = { + /* Event Action 1 Next state */ + /* API_DISABLE_EVT */ {BTA_AV_DISABLE, BTA_AV_INIT_ST }, + /* API_REMOTE_CMD_EVT */ {BTA_AV_IGNORE, BTA_AV_INIT_ST }, + /* API_VENDOR_CMD_EVT */ {BTA_AV_IGNORE, BTA_AV_INIT_ST }, + /* API_VENDOR_RSP_EVT */ {BTA_AV_IGNORE, BTA_AV_INIT_ST }, + /* API_META_RSP_EVT */ {BTA_AV_RC_FREE_RSP, BTA_AV_INIT_ST }, + /* API_RC_CLOSE_EVT */ {BTA_AV_IGNORE, BTA_AV_INIT_ST }, + /* AVRC_OPEN_EVT */ {BTA_AV_RC_OPENED, BTA_AV_OPEN_ST }, + /* AVRC_MSG_EVT */ {BTA_AV_RC_FREE_MSG, BTA_AV_INIT_ST }, + /* AVRC_NONE_EVT */ {BTA_AV_IGNORE, BTA_AV_INIT_ST }, +}; + +/* state table for open state */ +static const UINT8 bta_av_st_open[][BTA_AV_NUM_COLS] = { + /* Event Action 1 Next state */ + /* API_DISABLE_EVT */ {BTA_AV_DISABLE, BTA_AV_INIT_ST }, + /* API_REMOTE_CMD_EVT */ {BTA_AV_RC_REMOTE_CMD, BTA_AV_OPEN_ST }, + /* API_VENDOR_CMD_EVT */ {BTA_AV_RC_VENDOR_CMD, BTA_AV_OPEN_ST }, + /* API_VENDOR_RSP_EVT */ {BTA_AV_RC_VENDOR_RSP, BTA_AV_OPEN_ST }, + /* API_META_RSP_EVT */ {BTA_AV_RC_META_RSP, BTA_AV_OPEN_ST }, + /* API_RC_CLOSE_EVT */ {BTA_AV_RC_CLOSE, BTA_AV_OPEN_ST }, + /* AVRC_OPEN_EVT */ {BTA_AV_RC_OPENED, BTA_AV_OPEN_ST }, + /* AVRC_MSG_EVT */ {BTA_AV_RC_MSG, BTA_AV_OPEN_ST }, + /* AVRC_NONE_EVT */ {BTA_AV_IGNORE, BTA_AV_INIT_ST }, +}; + +/* type for state table */ +typedef const UINT8 (*tBTA_AV_ST_TBL)[BTA_AV_NUM_COLS]; + +/* state table */ +static const tBTA_AV_ST_TBL bta_av_st_tbl[] = { + bta_av_st_init, + bta_av_st_open +}; + +typedef void (*tBTA_AV_NSM_ACT)(tBTA_AV_DATA *p_data); +static void bta_av_api_enable(tBTA_AV_DATA *p_data); +static void bta_av_api_register(tBTA_AV_DATA *p_data); +#if (BTA_AV_SINK_INCLUDED == TRUE) +static void bta_av_api_sink_enable(tBTA_AV_DATA *p_data); +static void bta_av_api_get_delay_value(tBTA_AV_DATA *p_data); +#endif +static void bta_av_ci_data(tBTA_AV_DATA *p_data); +#if (AVDT_REPORTING == TRUE) +static void bta_av_rpc_conn(tBTA_AV_DATA *p_data); +#endif +static void bta_av_api_to_ssm(tBTA_AV_DATA *p_data); + +static void bta_av_sco_chg_cback(tBTA_SYS_CONN_STATUS status, UINT8 id, UINT8 + app_id, BD_ADDR peer_addr); +static void bta_av_sys_rs_cback (tBTA_SYS_CONN_STATUS status, UINT8 id, UINT8 app_id, BD_ADDR peer_addr); + + +/* action functions */ +const tBTA_AV_NSM_ACT bta_av_nsm_act[] = { + bta_av_api_enable, /* BTA_AV_API_ENABLE_EVT */ + bta_av_api_register, /* BTA_AV_API_REGISTER_EVT */ + bta_av_api_deregister, /* BTA_AV_API_DEREGISTER_EVT */ + bta_av_api_disconnect, /* BTA_AV_API_DISCONNECT_EVT */ + bta_av_ci_data, /* BTA_AV_CI_SRC_DATA_READY_EVT */ + bta_av_sig_chg, /* BTA_AV_SIG_CHG_EVT */ + bta_av_sig_timer, /* BTA_AV_SIG_TIMER_EVT */ + bta_av_rc_disc_done, /* BTA_AV_SDP_AVRC_DISC_EVT */ + bta_av_rc_closed, /* BTA_AV_AVRC_CLOSE_EVT */ + bta_av_conn_chg, /* BTA_AV_CONN_CHG_EVT */ + bta_av_dereg_comp, /* BTA_AV_DEREG_COMP_EVT */ +#if (BTA_AV_SINK_INCLUDED == TRUE) + bta_av_api_sink_enable, /* BTA_AV_API_SINK_ENABLE_EVT */ + bta_av_api_get_delay_value, /* BTA_AV_API_GET_DELAY_VALUE_EVT */ +#endif +#if (AVDT_REPORTING == TRUE) + bta_av_rpc_conn, /* BTA_AV_AVDT_RPT_CONN_EVT */ +#endif + bta_av_api_to_ssm, /* BTA_AV_API_START_EVT */ + bta_av_api_to_ssm, /* BTA_AV_API_STOP_EVT */ + bta_av_api_to_ssm, /* BTA_AV_API_SET_DELAY_VALUE_EVT */ +}; + +/***************************************************************************** +** Global data +*****************************************************************************/ + +/* AV control block */ +#if BTA_DYNAMIC_MEMORY == FALSE +tBTA_AV_CB bta_av_cb; +#else +tBTA_AV_CB *bta_av_cb_ptr; +#endif + +/******************************************************************************* +** +** Function bta_av_timer_cback +** +** Description forward the event to stream state machine +** +** Returns void +** +*******************************************************************************/ +static void bta_av_timer_cback(void *p_tle) +{ + BT_HDR *p_buf; + TIMER_LIST_ENT *p = (TIMER_LIST_ENT *)p_tle; + int xx; + tBTA_AV_SCB *p_scb = NULL; + + /* find the SCB that has the timer */ + for (xx = 0; xx < BTA_AV_NUM_STRS; xx++) { + if (bta_av_cb.p_scb[xx] && &(bta_av_cb.p_scb[xx]->timer) == p) { + p_scb = bta_av_cb.p_scb[xx]; + break; + } + } + + if (p_scb && (p_buf = (BT_HDR *) osi_malloc(sizeof(BT_HDR))) != NULL) { + /* send the event through the audio state machine. + * only when the audio SM is open, the main SM opens the RC connection as INT */ + p_buf->event = p->event; + p_buf->layer_specific = p_scb->hndl; + bta_sys_sendmsg(p_buf); + } +} + +/******************************************************************************* +** +** Function bta_av_api_enable +** +** Description Handle an API enable event. +** +** +** Returns void +** +*******************************************************************************/ +static void bta_av_api_enable(tBTA_AV_DATA *p_data) +{ + int i; + tBTA_AV_ENABLE enable; + + /* initialize control block */ + memset(&bta_av_cb, 0, sizeof(tBTA_AV_CB)); + + for (i = 0; i < BTA_AV_NUM_RCB; i++) { + bta_av_cb.rcb[i].handle = BTA_AV_RC_HANDLE_NONE; + } + + bta_av_cb.rc_acp_handle = BTA_AV_RC_HANDLE_NONE; + + /* store parameters */ + bta_av_cb.p_cback = p_data->api_enable.p_cback; + bta_av_cb.features = p_data->api_enable.features; + bta_av_cb.sec_mask = p_data->api_enable.sec_mask; + + enable.features = bta_av_cb.features; + + /* Register for SCO change event */ + if (!(bta_av_cb.features & BTA_AV_FEAT_NO_SCO_SSPD)) { + bta_sys_sco_register(bta_av_sco_chg_cback); + } + + /* call callback with enable event */ + (*bta_av_cb.p_cback)(BTA_AV_ENABLE_EVT, (tBTA_AV *)&enable); +} + +/******************************************************************************* +** +** Function bta_av_addr_to_scb +** +** Description find the stream control block by the peer addr +** +** Returns void +** +*******************************************************************************/ +static tBTA_AV_SCB *bta_av_addr_to_scb(BD_ADDR bd_addr) +{ + tBTA_AV_SCB *p_scb = NULL; + int xx; + + for (xx = 0; xx < BTA_AV_NUM_STRS; xx++) { + if (bta_av_cb.p_scb[xx]) { + if (!bdcmp(bd_addr, bta_av_cb.p_scb[xx]->peer_addr)) { + p_scb = bta_av_cb.p_scb[xx]; + break; + } + } + } + return p_scb; +} + +/******************************************************************************* +** +** Function bta_av_hndl_to_scb +** +** Description find the stream control block by the handle +** +** Returns void +** +*******************************************************************************/ +tBTA_AV_SCB *bta_av_hndl_to_scb(UINT16 handle) +{ + tBTA_AV_HNDL hndl = (tBTA_AV_HNDL)handle; + tBTA_AV_SCB *p_scb = NULL; + UINT8 idx = (hndl & BTA_AV_HNDL_MSK); + + if (idx && (idx <= BTA_AV_NUM_STRS) ) { + p_scb = bta_av_cb.p_scb[idx - 1]; + } + return p_scb; +} + +/******************************************************************************* +** +** Function bta_av_alloc_scb +** +** Description allocate stream control block, +** register the service to stack +** create SDP record +** +** Returns void +** +*******************************************************************************/ +static tBTA_AV_SCB *bta_av_alloc_scb(tBTA_AV_CHNL chnl) +{ + tBTA_AV_SCB *p_ret = NULL; + int xx; + tBTA_AV_STATUS sts = BTA_AV_SUCCESS; + + if (chnl == BTA_AV_CHNL_VIDEO) { + if (p_bta_av_cfg->p_act_tbl == NULL || p_bta_av_cfg->p_reg == NULL) { + APPL_TRACE_ERROR("Video streaming not supported"); + sts = BTA_AV_FAIL; + } else { + /* allow only one Video channel */ + if (bta_av_cb.reg_video) { + APPL_TRACE_ERROR("Already registered"); + sts = BTA_AV_FAIL; + } + } + } else if (chnl != BTA_AV_CHNL_AUDIO) { + APPL_TRACE_ERROR("bad channel: %d\n", chnl); + sts = BTA_AV_FAIL; + } + + if (sts == BTA_AV_SUCCESS) { + for (xx = 0; xx < BTA_AV_NUM_STRS; xx++) { + if (bta_av_cb.p_scb[xx] == NULL) { + /* found an empty spot */ + p_ret = (tBTA_AV_SCB *)osi_malloc(sizeof(tBTA_AV_SCB)); + if (p_ret) { + memset(p_ret, 0, sizeof(tBTA_AV_SCB)); + p_ret->rc_handle = BTA_AV_RC_HANDLE_NONE; + p_ret->chnl = chnl; + p_ret->hndl = (tBTA_AV_HNDL)((xx + 1) | chnl); + p_ret->hdi = xx; + p_ret->a2d_list = list_new(NULL); + bta_av_cb.p_scb[xx] = p_ret; + } + break; + } + } + } + return p_ret; +} + +/******************************************************************************* +** +** Function bta_av_free_scb +** +** Description free stream control block, +** +** +** Returns void +** +*******************************************************************************/ +UNUSED_ATTR static void bta_av_free_scb(tBTA_AV_SCB *p_scb) +{ + // NOTE(google) This free currently is not called + assert(p_scb != NULL); + + list_free(p_scb->a2d_list); + osi_free(p_scb); +} + +/******************************************************************************* +*******************************************************************************/ +void bta_av_conn_cback(UINT8 handle, BD_ADDR bd_addr, UINT8 event, tAVDT_CTRL *p_data) +{ + tBTA_AV_STR_MSG *p_msg; + UINT16 evt = 0; + tBTA_AV_SCB *p_scb = NULL; + UNUSED(handle); + +#if( defined BTA_AR_INCLUDED ) && (BTA_AR_INCLUDED == TRUE) + if (event == BTA_AR_AVDT_CONN_EVT || + event == AVDT_CONNECT_IND_EVT || event == AVDT_DISCONNECT_IND_EVT) +#else + if (event == AVDT_CONNECT_IND_EVT || event == AVDT_DISCONNECT_IND_EVT) +#endif + { + evt = BTA_AV_SIG_CHG_EVT; + if (AVDT_DISCONNECT_IND_EVT == event) { + p_scb = bta_av_addr_to_scb(bd_addr); + } + else if (AVDT_CONNECT_IND_EVT == event) { + APPL_TRACE_DEBUG("CONN_IND is ACP:%d\n", p_data->hdr.err_param); + } + + if ((p_msg = (tBTA_AV_STR_MSG *) osi_malloc((UINT16) (sizeof(tBTA_AV_STR_MSG)))) != NULL) { + p_msg->hdr.event = evt; + p_msg->hdr.layer_specific = event; + p_msg->hdr.offset = p_data->hdr.err_param; + bdcpy(p_msg->bd_addr, bd_addr); + if (p_scb) { + APPL_TRACE_DEBUG("scb hndl x%x, role x%x\n", p_scb->hndl, p_scb->role); + } + APPL_TRACE_DEBUG("conn_cback bd_addr:%02x-%02x-%02x-%02x-%02x-%02x\n", + bd_addr[0], bd_addr[1], + bd_addr[2], bd_addr[3], + bd_addr[4], bd_addr[5]); + bta_sys_sendmsg(p_msg); + } + } +} + +#if AVDT_REPORTING == TRUE +/******************************************************************************* +** +** Function bta_av_a2dp_report_cback +** +** Description A2DP report callback. +** +** Returns void +** +*******************************************************************************/ +static void bta_av_a2dp_report_cback(UINT8 handle, AVDT_REPORT_TYPE type, + tAVDT_REPORT_DATA *p_data) +{ + UNUSED(handle); + UNUSED(type); + UNUSED(p_data); + /* Do not need to handle report data for now. + * This empty function is here for conformance reasons. */ +} +#endif + +#if (BTA_AV_SINK_INCLUDED == TRUE) +/******************************************************************************* +** +** Function bta_av_api_sink_enable +** +** Description activate, deactive A2DP Sink, +** +** Returns void +** +*******************************************************************************/ + +static void bta_av_api_sink_enable(tBTA_AV_DATA *p_data) +{ + UINT16 activate_sink = 0; + activate_sink = p_data->hdr.layer_specific; + APPL_TRACE_DEBUG("bta_av_api_sink_enable %d \n", activate_sink) + char p_service_name[BTA_SERVICE_NAME_LEN + 1]; + BCM_STRNCPY_S(p_service_name, BTIF_AVK_SERVICE_NAME, BTA_SERVICE_NAME_LEN); + p_service_name[BTA_SERVICE_NAME_LEN] = '\0'; + + if (activate_sink) { + AVDT_SINK_Activate(); + if (bta_av_cb.sdp_a2d_snk_handle == 0) { + bta_av_cb.sdp_a2d_snk_handle = SDP_CreateRecord(); + A2D_AddRecord(UUID_SERVCLASS_AUDIO_SINK, p_service_name, NULL, + A2D_SUPF_PLAYER, bta_av_cb.sdp_a2d_snk_handle); + bta_sys_add_uuid(UUID_SERVCLASS_AUDIO_SINK); + } + } else { + AVDT_SINK_Deactivate(); + if (bta_av_cb.sdp_a2d_snk_handle != 0) { + SDP_DeleteRecord(bta_av_cb.sdp_a2d_snk_handle); + bta_av_cb.sdp_a2d_snk_handle = 0; + bta_sys_remove_uuid(UUID_SERVCLASS_AUDIO_SINK); + } + } +} + +/******************************************************************************* +** +** Function bta_av_api_get_delay_value +** +** Description Get delay reporting value +** +** +** Returns void +** +*******************************************************************************/ +static void bta_av_api_get_delay_value(tBTA_AV_DATA *p_data) +{ + UNUSED(p_data); + tBTA_AV_DELAY delay; + + delay.delay_value = AVDT_GetDelayValue(); + + /* call callback with get delay value event */ + (*bta_av_cb.p_cback)(BTA_AV_GET_DELAY_VALUE_EVT, (tBTA_AV *)&delay); +} +#endif +/******************************************************************************* +** +** Function bta_av_api_register +** +** Description allocate stream control block, +** register the service to stack +** create SDP record +** +** Returns void +** +*******************************************************************************/ +static void bta_av_api_register(tBTA_AV_DATA *p_data) +{ + tBTA_AV_REGISTER registr; + tBTA_AV_SCB *p_scb; /* stream control block */ + tAVDT_REG reg; + tAVDT_CS cs; + char *p_service_name; + tBTA_AV_CODEC codec_type; + tBTA_UTL_COD cod; + UINT8 index = 0; + char p_avk_service_name[BTA_SERVICE_NAME_LEN + 1]; + BCM_STRNCPY_S(p_avk_service_name, BTIF_AVK_SERVICE_NAME, BTA_SERVICE_NAME_LEN); + p_avk_service_name[BTA_SERVICE_NAME_LEN] = '\0'; + + memset(&cs, 0, sizeof(tAVDT_CS)); + + registr.status = BTA_AV_FAIL_RESOURCES; + registr.app_id = p_data->api_reg.app_id; + registr.chnl = (tBTA_AV_CHNL)p_data->hdr.layer_specific; + registr.p_bta_av_cos = p_data->api_reg.bta_av_cos; + registr.p_bta_avrc_cos = p_data->api_reg.bta_avrc_cos; + + // set the avrc call-out functions + bta_av_cb.p_rc_cos = p_data->api_reg.bta_avrc_cos; + + do { + p_scb = bta_av_alloc_scb(registr.chnl); + if (p_scb == NULL) { + APPL_TRACE_ERROR("failed to alloc SCB"); + break; + } + + registr.hndl = p_scb->hndl; + p_scb->app_id = registr.app_id; + + /* initialize the stream control block */ + p_scb->timer.p_cback = (TIMER_CBACK *)&bta_av_timer_cback; + registr.status = BTA_AV_SUCCESS; + + if ((bta_av_cb.reg_audio + bta_av_cb.reg_video) == 0) { + /* the first channel registered. register to AVDTP */ + reg.ctrl_mtu = p_bta_av_cfg->sig_mtu; + reg.ret_tout = BTA_AV_RET_TOUT; + reg.sig_tout = BTA_AV_SIG_TOUT; + reg.idle_tout = BTA_AV_IDLE_TOUT; + reg.sec_mask = bta_av_cb.sec_mask; +#if( defined BTA_AR_INCLUDED ) && (BTA_AR_INCLUDED == TRUE) + bta_ar_reg_avdt(®, bta_av_conn_cback, BTA_ID_AV); +#endif + bta_sys_role_chg_register(&bta_av_sys_rs_cback); + + /* create remote control TG service if required */ + if (bta_av_cb.features & (BTA_AV_FEAT_RCTG)) { + /* register with no authorization; let AVDTP use authorization instead */ +#if( defined BTA_AR_INCLUDED ) && (BTA_AR_INCLUDED == TRUE) + bta_ar_reg_avct(p_bta_av_cfg->avrc_mtu, p_bta_av_cfg->avrc_br_mtu, + (UINT8)(bta_av_cb.sec_mask & (~BTA_SEC_AUTHORIZE)), BTA_ID_AV); + if (p_data->api_reg.tsep == AVDT_TSEP_SRC) { + bta_ar_reg_avrc(UUID_SERVCLASS_AV_REM_CTRL_TARGET, "AV Remote Control Target\n", NULL, + p_bta_av_cfg->avrc_src_tg_cat, BTA_ID_AV, p_bta_av_cfg->avrc_br); + } else { + bta_ar_reg_avrc(UUID_SERVCLASS_AV_REM_CTRL_TARGET, "AV Remote Control Target\n", NULL, + p_bta_av_cfg->avrc_snk_tg_cat, BTA_ID_AV, p_bta_av_cfg->avrc_br); + } +#endif + } + + /* Set the Calss of Device (Audio & Capturing/Rendering service class bit) */ + if (p_data->api_reg.tsep == AVDT_TSEP_SRC) { + cod.service = BTM_COD_SERVICE_CAPTURING | BTM_COD_SERVICE_AUDIO; + cod.major = BTM_COD_MAJOR_AUDIO; + cod.minor = BTM_COD_MINOR_UNCLASSIFIED; + } else { +#if (BTA_AV_SINK_INCLUDED == TRUE) + cod.service = BTM_COD_SERVICE_RENDERING | BTM_COD_SERVICE_AUDIO; + cod.major = BTM_COD_MAJOR_AUDIO; + cod.minor = BTM_COD_MINOR_LOUDSPEAKER; +#endif + } + utl_set_device_class(&cod, BTA_UTL_SET_COD_ALL); + } /* if 1st channel */ + + /* get stream configuration and create stream */ + /* memset(&cs.cfg,0,sizeof(tAVDT_CFG)); */ + cs.cfg.num_codec = 1; + cs.tsep = p_data->api_reg.tsep; + + /* + * memset of cs takes care setting call back pointers to null. + cs.p_data_cback = NULL; + cs.p_report_cback = NULL; + */ + cs.nsc_mask = AVDT_NSC_RECONFIG | + ((bta_av_cb.features & BTA_AV_FEAT_PROTECT) ? 0 : AVDT_NSC_SECURITY); + APPL_TRACE_DEBUG("nsc_mask: 0x%x\n", cs.nsc_mask); + + if (p_data->api_reg.p_service_name[0] == 0) { + p_service_name = NULL; + } else { + p_service_name = p_data->api_reg.p_service_name; + } + + p_scb->suspend_sup = TRUE; + p_scb->recfg_sup = TRUE; + + cs.p_ctrl_cback = bta_av_dt_cback[p_scb->hdi]; + if (registr.chnl == BTA_AV_CHNL_AUDIO) { + /* set up the audio stream control block */ + p_scb->p_act_tbl = (const tBTA_AV_ACT *)bta_av_a2d_action; + p_scb->p_cos = registr.p_bta_av_cos; + p_scb->media_type = AVDT_MEDIA_AUDIO; + cs.cfg.psc_mask = AVDT_PSC_TRANS; + cs.media_type = AVDT_MEDIA_AUDIO; + cs.mtu = p_bta_av_cfg->audio_mtu; + cs.flush_to = L2CAP_DEFAULT_FLUSH_TO; +#if AVDT_REPORTING == TRUE + if (bta_av_cb.features & BTA_AV_FEAT_REPORT) { + cs.cfg.psc_mask |= AVDT_PSC_REPORT; + cs.p_report_cback = bta_av_a2dp_report_cback; +#if AVDT_MULTIPLEXING == TRUE + cs.cfg.mux_tsid_report = 2; +#endif + } +#endif + if (bta_av_cb.features & BTA_AV_FEAT_DELAY_RPT) { + cs.cfg.psc_mask |= AVDT_PSC_DELAY_RPT; + a2d_set_avdt_sdp_ver(AVDT_VERSION_SYNC); + a2d_set_a2dp_sdp_ver(A2D_VERSION_1_4); + } + + /* keep the configuration in the stream control block */ + memcpy(&p_scb->cfg, &cs.cfg, sizeof(tAVDT_CFG)); + while (index < BTA_AV_MAX_SEPS && + (p_scb->p_cos->init)(&codec_type, cs.cfg.codec_info, + &cs.cfg.num_protect, cs.cfg.protect_info, p_data->api_reg.tsep) == TRUE) { + +#if (BTA_AV_SINK_INCLUDED == TRUE) + if (p_data->api_reg.tsep == AVDT_TSEP_SNK) { + cs.p_data_cback = bta_av_stream_data_cback; + } + APPL_TRACE_DEBUG(" SEP Type = %d\n", cs.tsep); +#endif + if (AVDT_CreateStream(&p_scb->seps[index].av_handle, &cs) == AVDT_SUCCESS) { + p_scb->seps[index].codec_type = codec_type; + +#if (BTA_AV_SINK_INCLUDED == TRUE) + p_scb->seps[index].tsep = cs.tsep; + if (cs.tsep == AVDT_TSEP_SNK) { + p_scb->seps[index].p_app_data_cback = p_data->api_reg.p_app_data_cback; + } else { + p_scb->seps[index].p_app_data_cback = NULL; /* In case of A2DP SOURCE we don't need a callback to handle media packets */ + } +#endif + + APPL_TRACE_DEBUG("audio[%d] av_handle: %d codec_type: %d\n", + index, p_scb->seps[index].av_handle, p_scb->seps[index].codec_type); + index++; + } else { + break; + } + } + + if (!bta_av_cb.reg_audio) { + if (p_data->api_reg.tsep == AVDT_TSEP_SRC) { + /* create the SDP records on the 1st audio channel */ + bta_av_cb.sdp_a2d_handle = SDP_CreateRecord(); + A2D_AddRecord(UUID_SERVCLASS_AUDIO_SOURCE, p_service_name, NULL, + A2D_SUPF_PLAYER, bta_av_cb.sdp_a2d_handle); + bta_sys_add_uuid(UUID_SERVCLASS_AUDIO_SOURCE); + } else { +#if (BTA_AV_SINK_INCLUDED == TRUE) + bta_av_cb.sdp_a2d_snk_handle = SDP_CreateRecord(); + A2D_AddRecord(UUID_SERVCLASS_AUDIO_SINK, p_avk_service_name, NULL, + A2D_SUPF_PLAYER, bta_av_cb.sdp_a2d_snk_handle); + bta_sys_add_uuid(UUID_SERVCLASS_AUDIO_SINK); +#endif + } + /* start listening when A2DP is registered */ + if (bta_av_cb.features & BTA_AV_FEAT_RCTG) { + bta_av_rc_create(&bta_av_cb, AVCT_ACP, 0, BTA_AV_NUM_LINKS + 1); + } + + /* if the AV and AVK are both supported, it cannot support the CT role */ + if (bta_av_cb.features & (BTA_AV_FEAT_RCCT)) { + /* if TG is not supported, we need to register to AVCT now */ + if ((bta_av_cb.features & (BTA_AV_FEAT_RCTG)) == 0) { +#if( defined BTA_AR_INCLUDED ) && (BTA_AR_INCLUDED == TRUE) + bta_ar_reg_avct(p_bta_av_cfg->avrc_mtu, p_bta_av_cfg->avrc_br_mtu, + (UINT8)(bta_av_cb.sec_mask & (~BTA_SEC_AUTHORIZE)), BTA_ID_AV); +#endif + } +#if( defined BTA_AR_INCLUDED ) && (BTA_AR_INCLUDED == TRUE) + /* create an SDP record as AVRC CT. */ + if (p_data->api_reg.tsep == AVDT_TSEP_SRC) { + bta_ar_reg_avrc(UUID_SERVCLASS_AV_REMOTE_CONTROL, "AV Remote Control Controller\n", NULL, + p_bta_av_cfg->avrc_src_ct_cat, BTA_ID_AV, p_bta_av_cfg->avrc_br); + } else { + bta_ar_reg_avrc(UUID_SERVCLASS_AV_REMOTE_CONTROL, "AV Remote Control Controller\n", NULL, + p_bta_av_cfg->avrc_snk_ct_cat, BTA_ID_AV, p_bta_av_cfg->avrc_br); + } +#endif + } + } + bta_av_cb.reg_audio |= BTA_AV_HNDL_TO_MSK(p_scb->hdi); + APPL_TRACE_DEBUG("reg_audio: 0x%x\n", bta_av_cb.reg_audio); + } else { + bta_av_cb.reg_video = BTA_AV_HNDL_TO_MSK(p_scb->hdi); + bta_av_cb.sdp_vdp_handle = SDP_CreateRecord(); + /* register the video channel */ + /* no need to verify the function pointer here. it's verified prior */ + (*p_bta_av_cfg->p_reg)(&cs, p_service_name, p_scb); + } + } while (0); + + /* call callback with register event */ + (*bta_av_cb.p_cback)(BTA_AV_REGISTER_EVT, (tBTA_AV *)®istr); +} + +/******************************************************************************* +** +** Function bta_av_api_deregister +** +** Description de-register a channel +** +** +** Returns void +** +*******************************************************************************/ +void bta_av_api_deregister(tBTA_AV_DATA *p_data) +{ + tBTA_AV_SCB *p_scb = bta_av_hndl_to_scb(p_data->hdr.layer_specific); + + if (p_scb) { + p_scb->deregistring = TRUE; + bta_av_ssm_execute(p_scb, BTA_AV_API_CLOSE_EVT, p_data); + } else { + bta_av_dereg_comp(p_data); + } +} + +/******************************************************************************* +** +** Function bta_av_ci_data +** +** Description forward the BTA_AV_CI_SRC_DATA_READY_EVT to stream state machine +** +** +** Returns void +** +*******************************************************************************/ +static void bta_av_ci_data(tBTA_AV_DATA *p_data) +{ + tBTA_AV_SCB *p_scb; + int i; + UINT8 chnl = (UINT8)p_data->hdr.layer_specific; + + for ( i = 0; i < BTA_AV_NUM_STRS; i++ ) { + p_scb = bta_av_cb.p_scb[i]; + + if (p_scb && p_scb->chnl == chnl) { + bta_av_ssm_execute(p_scb, BTA_AV_SRC_DATA_READY_EVT, p_data); + } + } +} + +/******************************************************************************* +** +** Function bta_av_rpc_conn +** +** Description report report channel open +** +** Returns void +** +*******************************************************************************/ +#if (AVDT_REPORTING == TRUE) +static void bta_av_rpc_conn(tBTA_AV_DATA *p_data) +{ + UNUSED(p_data); +} +#endif + +/******************************************************************************* +** +** Function bta_av_api_to_ssm +** +** Description forward the API request to stream state machine +** +** +** Returns void +** +*******************************************************************************/ +static void bta_av_api_to_ssm(tBTA_AV_DATA *p_data) +{ + int xx; + UINT16 event = p_data->hdr.event - BTA_AV_FIRST_A2S_API_EVT + BTA_AV_FIRST_A2S_SSM_EVT; + + for (xx = 0; xx < BTA_AV_NUM_STRS; xx++) { + bta_av_ssm_execute(bta_av_cb.p_scb[xx], event, p_data); + } +} + +/******************************************************************************* +** +** Function bta_av_chk_start +** +** Description if this is audio channel, check if more than one audio +** channel is connected & already started. +** +** Returns TRUE, if need api_start +** +*******************************************************************************/ +BOOLEAN bta_av_chk_start(tBTA_AV_SCB *p_scb) +{ + BOOLEAN start = FALSE; + tBTA_AV_SCB *p_scbi; + int i; + + if (p_scb->chnl == BTA_AV_CHNL_AUDIO) { + if ((bta_av_cb.audio_open_cnt >= 2) && + ((0 == (p_scb->role & BTA_AV_ROLE_AD_ACP)) || /* Outgoing connection or */ + (bta_av_cb.features & BTA_AV_FEAT_ACP_START))) { /* auto-starting option */ + /* more than one audio channel is connected */ + /* if this is the 2nd stream as ACP, give INT a chance to issue the START command */ + for (i = 0; i < BTA_AV_NUM_STRS; i++) { + p_scbi = bta_av_cb.p_scb[i]; + if (p_scbi && p_scbi->chnl == BTA_AV_CHNL_AUDIO && p_scbi->co_started) { + start = TRUE; + /* may need to update the flush timeout of this already started stream */ + if (p_scbi->co_started != bta_av_cb.audio_open_cnt) { + p_scbi->co_started = bta_av_cb.audio_open_cnt; + L2CA_SetFlushTimeout(p_scbi->peer_addr, p_bta_av_cfg->p_audio_flush_to[p_scbi->co_started - 1] ); + } + } + } + } + } + return start; +} + +/******************************************************************************* +** +** Function bta_av_restore_switch +** +** Description assume that the caller of this function already makes +** sure that there's only one ACL connection left +** +** Returns void +** +*******************************************************************************/ +void bta_av_restore_switch (void) +{ + tBTA_AV_CB *p_cb = &bta_av_cb; + int i; + UINT8 mask; + + APPL_TRACE_DEBUG("reg_audio: 0x%x\n", bta_av_cb.reg_audio); + for (i = 0; i < BTA_AV_NUM_STRS; i++) { + mask = BTA_AV_HNDL_TO_MSK(i); + if (p_cb->conn_audio == mask) { + if (p_cb->p_scb[i]) { + bta_sys_set_policy(BTA_ID_AV, HCI_ENABLE_MASTER_SLAVE_SWITCH, p_cb->p_scb[i]->peer_addr); + } + break; + } + } +} + +/******************************************************************************* +** +** Function bta_av_sys_rs_cback +** +** Description Receives the role change event from dm +** +** Returns (BTA_SYS_ROLE_CHANGE, new_role, hci_status, p_bda) +** +*******************************************************************************/ +static void bta_av_sys_rs_cback (tBTA_SYS_CONN_STATUS status, UINT8 id, UINT8 app_id, BD_ADDR peer_addr) +{ + int i; + tBTA_AV_SCB *p_scb = NULL; + tBTA_AV_ROLE_RES *p_buf; + UINT8 cur_role; + UINT8 peer_idx = 0; + UNUSED(status); + + APPL_TRACE_DEBUG("bta_av_sys_rs_cback: %d\n", bta_av_cb.rs_idx); + for (i = 0; i < BTA_AV_NUM_STRS; i++) { + /* loop through all the SCBs to find matching peer addresses and report the role change event */ + /* note that more than one SCB (a2dp & vdp) maybe waiting for this event */ + p_scb = bta_av_cb.p_scb[i]; + if (p_scb && (bdcmp (peer_addr, p_scb->peer_addr) == 0) && + (p_buf = (tBTA_AV_ROLE_RES *) osi_malloc(sizeof(tBTA_AV_ROLE_RES))) != NULL) { + APPL_TRACE_DEBUG("new_role:%d, hci_status:x%x hndl: x%x\n", id, app_id, p_scb->hndl); + /* + if ((id != BTM_ROLE_MASTER) && (app_id != HCI_SUCCESS)) + { + bta_sys_set_policy(BTA_ID_AV, (HCI_ENABLE_MASTER_SLAVE_SWITCH|HCI_ENABLE_SNIFF_MODE), p_scb->peer_addr); + } + */ + p_buf->hdr.event = BTA_AV_ROLE_CHANGE_EVT; + p_buf->hdr.layer_specific = p_scb->hndl; + p_buf->new_role = id; + p_buf->hci_status = app_id; + bta_sys_sendmsg(p_buf); + + peer_idx = p_scb->hdi + 1; /* Handle index for the peer_addr */ + } + } + + /* restore role switch policy, if role switch failed */ + if ((HCI_SUCCESS != app_id) && + (BTM_GetRole (peer_addr, &cur_role) == BTM_SUCCESS) && + (cur_role == BTM_ROLE_SLAVE) ) { + bta_sys_set_policy(BTA_ID_AV, HCI_ENABLE_MASTER_SLAVE_SWITCH, peer_addr); + } + + /* if BTA_AvOpen() was called for other device, which caused the role switch of the peer_addr, */ + /* we need to continue opening process for the BTA_AvOpen(). */ + if ((bta_av_cb.rs_idx != 0) && (bta_av_cb.rs_idx != peer_idx)) { + if ((bta_av_cb.rs_idx - 1) < BTA_AV_NUM_STRS) { + p_scb = bta_av_cb.p_scb[bta_av_cb.rs_idx - 1]; + } + if (p_scb && p_scb->q_tag == BTA_AV_Q_TAG_OPEN) { + APPL_TRACE_DEBUG ("bta_av_sys_rs_cback: rs_idx(%d), hndl:x%x q_tag: %d\n", + bta_av_cb.rs_idx, p_scb->hndl, p_scb->q_tag); + + if (HCI_SUCCESS == app_id || HCI_ERR_NO_CONNECTION == app_id) { + p_scb->q_info.open.switch_res = BTA_AV_RS_OK; + } else { + p_scb->q_info.open.switch_res = BTA_AV_RS_FAIL; + } + + /* Continue av open process */ + bta_av_do_disc_a2d (p_scb, (tBTA_AV_DATA *) & (p_scb->q_info.open)); + } + + bta_av_cb.rs_idx = 0; + } +} + +/******************************************************************************* +** +** Function bta_av_sco_chg_cback +** +** Description receive & process the SCO connection up/down event from sys. +** call setup also triggers this callback, to suspend av before sco +** activity happens, or to resume av once call ends. +** +** Returns void +** +*******************************************************************************/ +static void bta_av_sco_chg_cback(tBTA_SYS_CONN_STATUS status, UINT8 id, UINT8 + app_id, BD_ADDR peer_addr) +{ + tBTA_AV_SCB *p_scb; + int i; + tBTA_AV_API_STOP stop; + UNUSED(app_id); + UNUSED(peer_addr); + + APPL_TRACE_DEBUG("bta_av_sco_chg_cback:%d status:%d\n", id, status); + if (id) { + bta_av_cb.sco_occupied = TRUE; + + /* either BTA_SYS_SCO_OPEN or BTA_SYS_SCO_CLOSE with remaining active SCO */ + for (i = 0; i < BTA_AV_NUM_STRS; i++) { + p_scb = bta_av_cb.p_scb[i]; + + if ( p_scb && p_scb->co_started && (p_scb->sco_suspend == FALSE)) { + APPL_TRACE_DEBUG("suspending scb:%d\n", i); + /* scb is used and started, not suspended automatically */ + p_scb->sco_suspend = TRUE; + stop.flush = FALSE; + stop.suspend = TRUE; + bta_av_ssm_execute(p_scb, BTA_AV_AP_STOP_EVT, (tBTA_AV_DATA *)&stop); + } + } + } else { + bta_av_cb.sco_occupied = FALSE; + + for (i = 0; i < BTA_AV_NUM_STRS; i++) { + p_scb = bta_av_cb.p_scb[i]; + + if ( p_scb && p_scb->sco_suspend ) { /* scb is used and suspended for SCO */ + APPL_TRACE_DEBUG("starting scb:%d\n", i); + bta_av_ssm_execute(p_scb, BTA_AV_AP_START_EVT, NULL); + } + } + } +} + +/******************************************************************************* +** +** Function bta_av_switch_if_needed +** +** Description This function checks if there is another existing AV +** channel that is local as slave role. +** If so, role switch and remove it from link policy. +** +** Returns TRUE, if role switch is done +** +*******************************************************************************/ +BOOLEAN bta_av_switch_if_needed(tBTA_AV_SCB *p_scb) +{ + UINT8 role; + BOOLEAN needed = FALSE; + tBTA_AV_SCB *p_scbi; + int i; + UINT8 mask; + + for (i = 0; i < BTA_AV_NUM_STRS; i++) { + mask = BTA_AV_HNDL_TO_MSK(i); + p_scbi = bta_av_cb.p_scb[i]; + if ( p_scbi && (p_scb->hdi != i) && /* not the original channel */ + ((bta_av_cb.conn_audio & mask) ||/* connected audio */ + (bta_av_cb.conn_video & mask)) ) { /* connected video */ + BTM_GetRole(p_scbi->peer_addr, &role); + /* this channel is open - clear the role switch link policy for this link */ + if (BTM_ROLE_MASTER != role) { + if (bta_av_cb.features & BTA_AV_FEAT_MASTER) { + bta_sys_clear_policy(BTA_ID_AV, HCI_ENABLE_MASTER_SLAVE_SWITCH, p_scbi->peer_addr); + } + if (BTM_CMD_STARTED != BTM_SwitchRole(p_scbi->peer_addr, BTM_ROLE_MASTER, NULL)) { + /* can not switch role on SCBI + * start the timer on SCB - because this function is ONLY called when SCB gets API_OPEN */ + bta_sys_start_timer(&p_scb->timer, BTA_AV_AVRC_TIMER_EVT, BTA_AV_RS_TIME_VAL); + } + needed = TRUE; + /* mark the original channel as waiting for RS result */ + bta_av_cb.rs_idx = p_scb->hdi + 1; + break; + } + } + } + return needed; +} + +/******************************************************************************* +** +** Function bta_av_link_role_ok +** +** Description This function checks if the SCB has existing ACL connection +** If so, check if the link role fits the requirements. +** +** Returns TRUE, if role is ok +** +*******************************************************************************/ +BOOLEAN bta_av_link_role_ok(tBTA_AV_SCB *p_scb, UINT8 bits) +{ + UINT8 role; + BOOLEAN is_ok = TRUE; + + if (BTM_GetRole(p_scb->peer_addr, &role) == BTM_SUCCESS) { + LOG_INFO("%s hndl:x%x role:%d conn_audio:x%x bits:%d features:x%x\n", + __func__, p_scb->hndl, role, bta_av_cb.conn_audio, bits, + bta_av_cb.features); + if (BTM_ROLE_MASTER != role && (A2D_BitsSet(bta_av_cb.conn_audio) > bits || (bta_av_cb.features & BTA_AV_FEAT_MASTER))) { + if (bta_av_cb.features & BTA_AV_FEAT_MASTER) { + bta_sys_clear_policy(BTA_ID_AV, HCI_ENABLE_MASTER_SLAVE_SWITCH, p_scb->peer_addr); + } + + if (BTM_CMD_STARTED != BTM_SwitchRole(p_scb->peer_addr, BTM_ROLE_MASTER, NULL)) { + /* can not switch role on SCB - start the timer on SCB */ + } + is_ok = FALSE; + p_scb->wait |= BTA_AV_WAIT_ROLE_SW_RES_START; + + } + } + + return is_ok; +} + +/******************************************************************************* +** +** Function bta_av_chk_mtu +** +** Description if this is audio channel, check if more than one audio +** channel is connected. +** +** Returns The smallest mtu of the connected audio channels +** +*******************************************************************************/ +UINT16 bta_av_chk_mtu(tBTA_AV_SCB *p_scb, UINT16 mtu) +{ + UINT16 ret_mtu = BTA_AV_MAX_A2DP_MTU; + tBTA_AV_SCB *p_scbi; + int i; + UINT8 mask; + UNUSED(mtu); + + /* TODO_MV mess with the mtu according to the number of EDR/non-EDR headsets */ + if (p_scb->chnl == BTA_AV_CHNL_AUDIO) { + if (bta_av_cb.audio_open_cnt >= 2) { + /* more than one audio channel is connected */ + for (i = 0; i < BTA_AV_NUM_STRS; i++) { + p_scbi = bta_av_cb.p_scb[i]; + if ((p_scb != p_scbi) && p_scbi && (p_scbi->chnl == BTA_AV_CHNL_AUDIO) ) { + mask = BTA_AV_HNDL_TO_MSK(i); + APPL_TRACE_DEBUG("[%d] mtu: %d, mask:0x%x\n", + i, p_scbi->stream_mtu, mask); + if (bta_av_cb.conn_audio & mask) { + if (ret_mtu > p_scbi->stream_mtu) { + ret_mtu = p_scbi->stream_mtu; + } + } + } + } + } + APPL_TRACE_DEBUG("bta_av_chk_mtu audio count:%d, conn_audio:0x%x, ret:%d\n", + bta_av_cb.audio_open_cnt, bta_av_cb.conn_audio, ret_mtu); + } + return ret_mtu; +} + +/******************************************************************************* +** +** Function bta_av_dup_audio_buf +** +** Description dup the audio data to the q_info.a2d of other audio channels +** +** Returns void +** +*******************************************************************************/ +void bta_av_dup_audio_buf(tBTA_AV_SCB *p_scb, BT_HDR *p_buf) +{ + tBTA_AV_SCB *p_scbi; + int i; + UINT16 copy_size; + BT_HDR *p_new; + + if (!p_buf) { + return; + } + + if (bta_av_cb.audio_open_cnt >= 2) { + copy_size = BT_HDR_SIZE + p_buf->len + p_buf->offset; + /* more than one audio channel is connected */ + for (i = 0; i < BTA_AV_NUM_STRS; i++) { + p_scbi = bta_av_cb.p_scb[i]; + if ( (p_scb->hdi != i) && /* not the original channel */ + (bta_av_cb.conn_audio & BTA_AV_HNDL_TO_MSK(i)) && /* connected audio */ + p_scbi && p_scbi->co_started ) { /* scb is used and started */ + /* enqueue the data only when the stream is started */ + p_new = (BT_HDR *)osi_malloc(copy_size); + if (p_new) { + memcpy(p_new, p_buf, copy_size); + list_append(p_scbi->a2d_list, p_new); + if (list_length(p_scbi->a2d_list) > p_bta_av_cfg->audio_mqs) { + // Drop the oldest packet + bta_av_co_audio_drop(p_scbi->hndl); + BT_HDR *p_buf = list_front(p_scbi->a2d_list); + list_remove(p_scbi->a2d_list, p_buf); + osi_free(p_buf); + } + } + } + } + } + +} + +/******************************************************************************* +** +** Function bta_av_sm_execute +** +** Description State machine event handling function for AV +** +** +** Returns void +** +*******************************************************************************/ +void bta_av_sm_execute(tBTA_AV_CB *p_cb, UINT16 event, tBTA_AV_DATA *p_data) +{ + tBTA_AV_ST_TBL state_table; + UINT8 action; + + APPL_TRACE_EVENT("AV event=0x%x state=%d\n", event, p_cb->state); + + /* look up the state table for the current state */ + state_table = bta_av_st_tbl[p_cb->state]; + + event &= 0x00FF; + + /* set next state */ + p_cb->state = state_table[event][BTA_AV_NEXT_STATE]; + APPL_TRACE_EVENT("next state=%d\n", p_cb->state); + + /* execute action functions */ + if ((action = state_table[event][BTA_AV_ACTION_COL]) != BTA_AV_IGNORE) { + (*bta_av_action[action])(p_cb, p_data); + } +} + + +/******************************************************************************* +** +** Function bta_av_hdl_event +** +** Description Advanced audio/video main event handling function. +** +** +** Returns BOOLEAN +** +*******************************************************************************/ +BOOLEAN bta_av_hdl_event(BT_HDR *p_msg) +{ + UINT16 event = p_msg->event; + UINT16 first_event = BTA_AV_FIRST_NSM_EVT; + + if (event > BTA_AV_LAST_EVT) { + return TRUE; /* to free p_msg */ + } + + if (event >= first_event) { + APPL_TRACE_VERBOSE("AV nsm event=0x%x(%s)\n", event, bta_av_evt_code(event)); + /* non state machine events */ + (*bta_av_nsm_act[event - BTA_AV_FIRST_NSM_EVT]) ((tBTA_AV_DATA *) p_msg); + } else if (event >= BTA_AV_FIRST_SM_EVT && event <= BTA_AV_LAST_SM_EVT) { + APPL_TRACE_VERBOSE("AV sm event=0x%x(%s)\n", event, bta_av_evt_code(event)); + /* state machine events */ + bta_av_sm_execute(&bta_av_cb, p_msg->event, (tBTA_AV_DATA *) p_msg); + } else { + APPL_TRACE_VERBOSE("handle=0x%x\n", p_msg->layer_specific); + tBTA_AV_SCB *p_scb = bta_av_hndl_to_scb(p_msg->layer_specific); + if (p_scb) { + p_scb->disc_rsn = p_msg->offset; + /* stream state machine events */ + bta_av_ssm_execute(p_scb, p_msg->event, (tBTA_AV_DATA *) p_msg); + } + } + return TRUE; +} + + +/***************************************************************************** +** Debug Functions +*****************************************************************************/ +/******************************************************************************* +** +** Function bta_av_evt_code +** +** Description +** +** Returns char * +** +*******************************************************************************/ +char *bta_av_evt_code(UINT16 evt_code) +{ + switch (evt_code) { + case BTA_AV_API_DISABLE_EVT: return "API_DISABLE"; + case BTA_AV_API_REMOTE_CMD_EVT: return "API_REMOTE_CMD"; + case BTA_AV_API_VENDOR_CMD_EVT: return "API_VENDOR_CMD"; + case BTA_AV_API_VENDOR_RSP_EVT: return "API_VENDOR_RSP"; + case BTA_AV_API_META_RSP_EVT: return "API_META_RSP_EVT"; + case BTA_AV_API_RC_CLOSE_EVT: return "API_RC_CLOSE"; + case BTA_AV_AVRC_OPEN_EVT: return "AVRC_OPEN"; + case BTA_AV_AVRC_MSG_EVT: return "AVRC_MSG"; + case BTA_AV_AVRC_NONE_EVT: return "AVRC_NONE"; + + case BTA_AV_API_OPEN_EVT: return "API_OPEN"; + case BTA_AV_API_CLOSE_EVT: return "API_CLOSE"; + case BTA_AV_AP_START_EVT: return "AP_START"; + case BTA_AV_AP_STOP_EVT: return "AP_STOP"; + case BTA_AV_AP_SET_DELAY_VALUE_EVT: return "AP_SET_DELAY_VALUE"; + case BTA_AV_API_RECONFIG_EVT: return "API_RECONFIG"; + case BTA_AV_API_PROTECT_REQ_EVT: return "API_PROTECT_REQ"; + case BTA_AV_API_PROTECT_RSP_EVT: return "API_PROTECT_RSP"; + case BTA_AV_API_RC_OPEN_EVT: return "API_RC_OPEN"; + case BTA_AV_SRC_DATA_READY_EVT: return "SRC_DATA_READY"; + case BTA_AV_CI_SETCONFIG_OK_EVT: return "CI_SETCONFIG_OK"; + case BTA_AV_CI_SETCONFIG_FAIL_EVT: return "CI_SETCONFIG_FAIL"; + case BTA_AV_SDP_DISC_OK_EVT: return "SDP_DISC_OK"; + case BTA_AV_SDP_DISC_FAIL_EVT: return "SDP_DISC_FAIL"; + case BTA_AV_STR_DISC_OK_EVT: return "STR_DISC_OK"; + case BTA_AV_STR_DISC_FAIL_EVT: return "STR_DISC_FAIL"; + case BTA_AV_STR_GETCAP_OK_EVT: return "STR_GETCAP_OK"; + case BTA_AV_STR_GETCAP_FAIL_EVT: return "STR_GETCAP_FAIL"; + case BTA_AV_STR_OPEN_OK_EVT: return "STR_OPEN_OK"; + case BTA_AV_STR_OPEN_FAIL_EVT: return "STR_OPEN_FAIL"; + case BTA_AV_STR_START_OK_EVT: return "STR_START_OK"; + case BTA_AV_STR_START_FAIL_EVT: return "STR_START_FAIL"; + case BTA_AV_STR_CLOSE_EVT: return "STR_CLOSE"; + case BTA_AV_STR_CONFIG_IND_EVT: return "STR_CONFIG_IND"; + case BTA_AV_STR_SECURITY_IND_EVT: return "STR_SECURITY_IND"; + case BTA_AV_STR_SECURITY_CFM_EVT: return "STR_SECURITY_CFM"; + case BTA_AV_STR_WRITE_CFM_EVT: return "STR_WRITE_CFM"; + case BTA_AV_STR_SUSPEND_CFM_EVT: return "STR_SUSPEND_CFM"; + case BTA_AV_STR_RECONFIG_CFM_EVT: return "STR_RECONFIG_CFM"; + case BTA_AV_AVRC_TIMER_EVT: return "AVRC_TIMER"; + case BTA_AV_AVDT_CONNECT_EVT: return "AVDT_CONNECT"; + case BTA_AV_AVDT_DISCONNECT_EVT: return "AVDT_DISCONNECT"; + case BTA_AV_ROLE_CHANGE_EVT: return "ROLE_CHANGE"; + case BTA_AV_AVDT_DELAY_RPT_EVT: return "AVDT_DELAY_RPT"; + case BTA_AV_ACP_CONNECT_EVT: return "ACP_CONNECT"; + + case BTA_AV_API_ENABLE_EVT: return "API_ENABLE"; + case BTA_AV_API_REGISTER_EVT: return "API_REG"; + case BTA_AV_API_DEREGISTER_EVT: return "API_DEREG"; + case BTA_AV_API_DISCONNECT_EVT: return "API_DISCNT"; + case BTA_AV_CI_SRC_DATA_READY_EVT: return "CI_DATA_READY"; + case BTA_AV_SIG_CHG_EVT: return "SIG_CHG"; + case BTA_AV_SIG_TIMER_EVT: return "SIG_TMR"; + case BTA_AV_SDP_AVRC_DISC_EVT: return "SDP_AVRC_DISC"; + case BTA_AV_AVRC_CLOSE_EVT: return "AVRC_CLOSE"; + case BTA_AV_CONN_CHG_EVT: return "CONN_CHG"; + case BTA_AV_DEREG_COMP_EVT: return "DEREG_COMP"; +#if (BTA_AV_SINK_INCLUDED == TRUE) + case BTA_AV_API_SINK_ENABLE_EVT: return "SINK_ENABLE"; + case BTA_AV_API_GET_DELAY_VALUE_EVT: return "GET_DELAY_VALUE"; +#endif +#if (AVDT_REPORTING == TRUE) + case BTA_AV_AVDT_RPT_CONN_EVT: return "RPT_CONN"; +#endif + case BTA_AV_API_START_EVT: return "API_START"; + case BTA_AV_API_STOP_EVT: return "API_STOP"; + case BTA_AV_API_SET_DELAY_VALUE_EVT: return "API_SET_DELAY_VALUE"; + default: return "unknown"; + } +} + +/******************************************************************************* +** +** Function bta_av_action_code +** +** Description +** +** Returns char * +** +*******************************************************************************/ +char *bta_av_action_code(UINT16 action_code) +{ + switch (action_code) { + case 0: return "BTA_AV_DO_DISC"; + case 1: return "BTA_AV_CLEANUP"; + case 2: return "BTA_AV_FREE_SDB"; + case 3: return "BTA_AV_CONFIG_IND"; + case 4: return "BTA_AV_DISCONNECT_REQ"; + case 5: return "BTA_AV_SECURITY_REQ"; + case 6: return "BTA_AV_SECURITY_RSP"; + case 7: return "BTA_AV_SETCONFIG_RSP"; + case 8: return "BTA_AV_ST_RC_TIMER"; + case 9: return "BTA_AV_STR_OPENED"; + case 10: return "BTA_AV_SECURITY_IND"; + case 11: return "BTA_AV_SECURITY_CFM"; + case 12: return "BTA_AV_DO_CLOSE"; + case 13: return "BTA_AV_CONNECT_REQ"; + case 14: return "BTA_AV_SDP_FAILED"; + case 15: return "BTA_AV_DISC_RESULTS"; + case 16: return "BTA_AV_DISC_RES_AS_ACP"; + case 17: return "BTA_AV_OPEN_FAILED"; + case 18: return "BTA_AV_GETCAP_RESULTS"; + case 19: return "BTA_AV_SETCONFIG_REJ"; + case 20: return "BTA_AV_DISCOVER_REQ"; + case 21: return "BTA_AV_CONN_FAILED"; + case 22: return "BTA_AV_DO_START"; + case 23: return "BTA_AV_STR_STOPPED"; + case 24: return "BTA_AV_RECONFIG"; + case 25: return "BTA_AV_DATA_PATH"; + case 26: return "BTA_AV_START_OK"; + case 27: return "BTA_AV_START_FAILED"; + case 28: return "BTA_AV_STR_CLOSED"; + case 29: return "BTA_AV_CLR_CONG"; + case 30: return "BTA_AV_SUSPEND_CFM"; + case 31: return "BTA_AV_RCFG_STR_OK"; + case 32: return "BTA_AV_RCFG_FAILED"; + case 33: return "BTA_AV_RCFG_CONNECT"; + case 34: return "BTA_AV_RCFG_DISCNTD"; + case 35: return "BTA_AV_SUSPEND_CONT"; + case 36: return "BTA_AV_RCFG_CFM"; + case 37: return "BTA_AV_RCFG_OPEN"; + case 38: return "BTA_AV_SECURITY_REJ"; + case 39: return "BTA_AV_OPEN_RC"; + case 40: return "BTA_AV_CHK_2ND_START"; + case 41: return "BTA_AV_SAVE_CAPS"; + case 42: return "BTA_AV_SET_USE_RC"; + case 43: return "BTA_AV_CCO_CLOSE"; + case 44: return "BTA_AV_SWITCH_ROLE"; + case 45: return "BTA_AV_ROLE_RES"; + case 46: return "BTA_AV_DELAY_CO"; + case 47: return "BTA_AV_OPEN_AT_INC"; + case 48: return "BTA_AV_OPEN_FAIL_SDP"; + case 49: return "NULL"; + default: return "unknown"; + } +} + +#endif /* BTA_AV_INCLUDED */ diff --git a/lib/bt/host/bluedroid/bta/av/bta_av_sbc.c b/lib/bt/host/bluedroid/bta/av/bta_av_sbc.c new file mode 100644 index 00000000..4e034c42 --- /dev/null +++ b/lib/bt/host/bluedroid/bta/av/bta_av_sbc.c @@ -0,0 +1,603 @@ +/****************************************************************************** + * + * Copyright (C) 2004-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This module contains utility functions for dealing with SBC data frames + * and codec capabilities. + * + ******************************************************************************/ + +#include "common/bt_target.h" +#include "stack/a2d_api.h" +#include "stack/a2d_sbc.h" +#include "bta/bta_av_sbc.h" +#include "bta/utl.h" +#include "common/bt_defs.h" + +#if defined(BTA_AV_INCLUDED) && (BTA_AV_INCLUDED == TRUE) +#include "bta_av_int.h" + +#if BTA_DYNAMIC_MEMORY == FALSE +static tBTA_AV_SBC_UPS_CB bta_av_sbc_ups_cb; +#else +tBTA_AV_SBC_UPS_CB *bta_av_sbc_ups_cb_ptr; +#endif + +/******************************************************************************* +** +** Function bta_av_sbc_init_up_sample +** +** Description initialize the up sample +** +** src_sps: samples per second (source audio data) +** dst_sps: samples per second (converted audio data) +** bits: number of bits per pcm sample +** n_channels: number of channels (i.e. mono(1), stereo(2)...) +** +** Returns none +** +*******************************************************************************/ +void bta_av_sbc_init_up_sample (UINT32 src_sps, UINT32 dst_sps, UINT16 bits, UINT16 n_channels) +{ + bta_av_sbc_ups_cb.cur_pos = -1; + bta_av_sbc_ups_cb.src_sps = src_sps; + bta_av_sbc_ups_cb.dst_sps = dst_sps; + bta_av_sbc_ups_cb.bits = bits; + bta_av_sbc_ups_cb.n_channels = n_channels; + + if (n_channels == 1) { + /* mono */ + if (bits == 8) { + bta_av_sbc_ups_cb.p_act = bta_av_sbc_up_sample_8m; + bta_av_sbc_ups_cb.div = 1; + } else { + bta_av_sbc_ups_cb.p_act = bta_av_sbc_up_sample_16m; + bta_av_sbc_ups_cb.div = 2; + } + } else { + /* stereo */ + if (bits == 8) { + bta_av_sbc_ups_cb.p_act = bta_av_sbc_up_sample_8s; + bta_av_sbc_ups_cb.div = 2; + } else { + bta_av_sbc_ups_cb.p_act = bta_av_sbc_up_sample_16s; + bta_av_sbc_ups_cb.div = 4; + } + } +} + +/******************************************************************************* +** +** Function bta_av_sbc_up_sample +** +** Description Given the source (p_src) audio data and +** source speed (src_sps, samples per second), +** This function converts it to audio data in the desired format +** +** p_src: the data buffer that holds the source audio data +** p_dst: the data buffer to hold the converted audio data +** src_samples: The number of source samples (number of bytes) +** dst_samples: The size of p_dst (number of bytes) +** +** Note: An AE reported an issue with this function. +** When called with bta_av_sbc_up_sample(src, uint8_array_dst..) +** the byte before uint8_array_dst may get overwritten. +** Using uint16_array_dst avoids the problem. +** This issue is related to endian-ness and is hard to resolve +** in a generic manner. +** **************** Please use uint16 array as dst. +** +** Returns The number of bytes used in p_dst +** The number of bytes used in p_src (in *p_ret) +** +*******************************************************************************/ +int bta_av_sbc_up_sample (void *p_src, void *p_dst, + UINT32 src_samples, UINT32 dst_samples, + UINT32 *p_ret) +{ + UINT32 src; + UINT32 dst; + + if (bta_av_sbc_ups_cb.p_act) { + src = src_samples / bta_av_sbc_ups_cb.div; + dst = dst_samples / bta_av_sbc_ups_cb.div; + return (*bta_av_sbc_ups_cb.p_act)(p_src, p_dst, src, dst, p_ret); + } else { + *p_ret = 0; + return 0; + } +} + +/******************************************************************************* +** +** Function bta_av_sbc_up_sample_16s (16bits-stereo) +** +** Description Given the source (p_src) audio data and +** source speed (src_sps, samples per second), +** This function converts it to audio data in the desired format +** +** p_src: the data buffer that holds the source audio data +** p_dst: the data buffer to hold the converted audio data +** src_samples: The number of source samples (in uint of 4 bytes) +** dst_samples: The size of p_dst (in uint of 4 bytes) +** +** Returns The number of bytes used in p_dst +** The number of bytes used in p_src (in *p_ret) +** +*******************************************************************************/ +int bta_av_sbc_up_sample_16s (void *p_src, void *p_dst, + UINT32 src_samples, UINT32 dst_samples, + UINT32 *p_ret) +{ + INT16 *p_src_tmp = (INT16 *)p_src; + INT16 *p_dst_tmp = (INT16 *)p_dst; + INT16 *p_worker1 = &bta_av_sbc_ups_cb.worker1; + INT16 *p_worker2 = &bta_av_sbc_ups_cb.worker2; + UINT32 src_sps = bta_av_sbc_ups_cb.src_sps; + UINT32 dst_sps = bta_av_sbc_ups_cb.dst_sps; + + while (bta_av_sbc_ups_cb.cur_pos > 0 && dst_samples) { + *p_dst_tmp++ = *p_worker1; + *p_dst_tmp++ = *p_worker2; + + bta_av_sbc_ups_cb.cur_pos -= src_sps; + dst_samples--; + } + + bta_av_sbc_ups_cb.cur_pos = dst_sps; + + while (src_samples-- && dst_samples) { + *p_worker1 = *p_src_tmp++; + *p_worker2 = *p_src_tmp++; + + do { + *p_dst_tmp++ = *p_worker1; + *p_dst_tmp++ = *p_worker2; + + bta_av_sbc_ups_cb.cur_pos -= src_sps; + dst_samples--; + } while (bta_av_sbc_ups_cb.cur_pos > 0 && dst_samples); + + bta_av_sbc_ups_cb.cur_pos += dst_sps; + } + + if (bta_av_sbc_ups_cb.cur_pos == (INT32)dst_sps) { + bta_av_sbc_ups_cb.cur_pos = 0; + } + + *p_ret = ((char *)p_src_tmp - (char *)p_src); + return ((char *)p_dst_tmp - (char *)p_dst); +} + +/******************************************************************************* +** +** Function bta_av_sbc_up_sample_16m (16bits-mono) +** +** Description Given the source (p_src) audio data and +** source speed (src_sps, samples per second), +** This function converts it to audio data in the desired format +** +** p_src: the data buffer that holds the source audio data +** p_dst: the data buffer to hold the converted audio data +** src_samples: The number of source samples (in uint of 2 bytes) +** dst_samples: The size of p_dst (in uint of 2 bytes) +** +** Returns The number of bytes used in p_dst +** The number of bytes used in p_src (in *p_ret) +** +*******************************************************************************/ +int bta_av_sbc_up_sample_16m (void *p_src, void *p_dst, + UINT32 src_samples, UINT32 dst_samples, + UINT32 *p_ret) +{ + INT16 *p_src_tmp = (INT16 *)p_src; + INT16 *p_dst_tmp = (INT16 *)p_dst; + INT16 *p_worker = &bta_av_sbc_ups_cb.worker1; + UINT32 src_sps = bta_av_sbc_ups_cb.src_sps; + UINT32 dst_sps = bta_av_sbc_ups_cb.dst_sps; + + while (bta_av_sbc_ups_cb.cur_pos > 0 && dst_samples) { + *p_dst_tmp++ = *p_worker; + *p_dst_tmp++ = *p_worker; + + bta_av_sbc_ups_cb.cur_pos -= src_sps; + dst_samples--; + dst_samples--; + } + + + bta_av_sbc_ups_cb.cur_pos = dst_sps; + + while (src_samples-- && dst_samples) { + *p_worker = *p_src_tmp++; + + do { + *p_dst_tmp++ = *p_worker; + *p_dst_tmp++ = *p_worker; + + bta_av_sbc_ups_cb.cur_pos -= src_sps; + dst_samples--; + dst_samples--; + + } while (bta_av_sbc_ups_cb.cur_pos > 0 && dst_samples); + + bta_av_sbc_ups_cb.cur_pos += dst_sps; + } + + if (bta_av_sbc_ups_cb.cur_pos == (INT32)dst_sps) { + bta_av_sbc_ups_cb.cur_pos = 0; + } + + *p_ret = ((char *)p_src_tmp - (char *)p_src); + return ((char *)p_dst_tmp - (char *)p_dst); +} + +/******************************************************************************* +** +** Function bta_av_sbc_up_sample_8s (8bits-stereo) +** +** Description Given the source (p_src) audio data and +** source speed (src_sps, samples per second), +** This function converts it to audio data in the desired format +** +** p_src: the data buffer that holds the source audio data +** p_dst: the data buffer to hold the converted audio data +** src_samples: The number of source samples (in uint of 2 bytes) +** dst_samples: The size of p_dst (in uint of 2 bytes) +** +** Returns The number of bytes used in p_dst +** The number of bytes used in p_src (in *p_ret) +** +*******************************************************************************/ +int bta_av_sbc_up_sample_8s (void *p_src, void *p_dst, + UINT32 src_samples, UINT32 dst_samples, + UINT32 *p_ret) +{ + UINT8 *p_src_tmp = (UINT8 *)p_src; + INT16 *p_dst_tmp = (INT16 *)p_dst; + INT16 *p_worker1 = &bta_av_sbc_ups_cb.worker1; + INT16 *p_worker2 = &bta_av_sbc_ups_cb.worker2; + UINT32 src_sps = bta_av_sbc_ups_cb.src_sps; + UINT32 dst_sps = bta_av_sbc_ups_cb.dst_sps; + + while (bta_av_sbc_ups_cb.cur_pos > 0 && dst_samples) { + *p_dst_tmp++ = *p_worker1; + *p_dst_tmp++ = *p_worker2; + + bta_av_sbc_ups_cb.cur_pos -= src_sps; + dst_samples--; + dst_samples--; + } + + bta_av_sbc_ups_cb.cur_pos = dst_sps; + + while (src_samples -- && dst_samples) { + *p_worker1 = *(UINT8 *)p_src_tmp++; + *p_worker1 -= 0x80; + *p_worker1 <<= 8; + *p_worker2 = *(UINT8 *)p_src_tmp++; + *p_worker2 -= 0x80; + *p_worker2 <<= 8; + + do { + *p_dst_tmp++ = *p_worker1; + *p_dst_tmp++ = *p_worker2; + + bta_av_sbc_ups_cb.cur_pos -= src_sps; + dst_samples--; + dst_samples--; + } while (bta_av_sbc_ups_cb.cur_pos > 0 && dst_samples); + + bta_av_sbc_ups_cb.cur_pos += dst_sps; + } + + if (bta_av_sbc_ups_cb.cur_pos == (INT32)dst_sps) { + bta_av_sbc_ups_cb.cur_pos = 0; + } + + *p_ret = ((char *)p_src_tmp - (char *)p_src); + return ((char *)p_dst_tmp - (char *)p_dst); +} + +/******************************************************************************* +** +** Function bta_av_sbc_up_sample_8m (8bits-mono) +** +** Description Given the source (p_src) audio data and +** source speed (src_sps, samples per second), +** This function converts it to audio data in the desired format +** +** p_src: the data buffer that holds the source audio data +** p_dst: the data buffer to hold the converted audio data +** src_samples: The number of source samples (number of bytes) +** dst_samples: The size of p_dst (number of bytes) +** +** Returns The number of bytes used in p_dst +** The number of bytes used in p_src (in *p_ret) +** +*******************************************************************************/ +int bta_av_sbc_up_sample_8m (void *p_src, void *p_dst, + UINT32 src_samples, UINT32 dst_samples, + UINT32 *p_ret) +{ + UINT8 *p_src_tmp = (UINT8 *)p_src; + INT16 *p_dst_tmp = (INT16 *)p_dst; + INT16 *p_worker = &bta_av_sbc_ups_cb.worker1; + UINT32 src_sps = bta_av_sbc_ups_cb.src_sps; + UINT32 dst_sps = bta_av_sbc_ups_cb.dst_sps; + + while (bta_av_sbc_ups_cb.cur_pos > 0 && dst_samples) { + *p_dst_tmp++ = *p_worker; + *p_dst_tmp++ = *p_worker; + + bta_av_sbc_ups_cb.cur_pos -= src_sps; + dst_samples -= 4; + } + + + bta_av_sbc_ups_cb.cur_pos = dst_sps; + + while (src_samples-- && dst_samples) { + *p_worker = *(UINT8 *)p_src_tmp++; + *p_worker -= 0x80; + *p_worker <<= 8; + + do { + *p_dst_tmp++ = *p_worker; + *p_dst_tmp++ = *p_worker; + + bta_av_sbc_ups_cb.cur_pos -= src_sps; + dst_samples -= 4; + + } while (bta_av_sbc_ups_cb.cur_pos > 0 && dst_samples); + + bta_av_sbc_ups_cb.cur_pos += dst_sps; + } + + if (bta_av_sbc_ups_cb.cur_pos == (INT32)dst_sps) { + bta_av_sbc_ups_cb.cur_pos = 0; + } + + *p_ret = ((char *)p_src_tmp - (char *)p_src); + return ((char *)p_dst_tmp - (char *)p_dst); +} + +/******************************************************************************* +** +** Function bta_av_sbc_cfg_for_cap +** +** Description Determine the preferred SBC codec configuration for the +** given codec capabilities. The function is passed the +** preferred codec configuration and the peer codec +** capabilities for the stream. The function attempts to +** match the preferred capabilities with the configuration +** as best it can. The resulting codec configuration is +** returned in the same memory used for the capabilities. +** +** Returns 0 if ok, nonzero if error. +** Codec configuration in p_cap. +** +*******************************************************************************/ +UINT8 bta_av_sbc_cfg_for_cap(UINT8 *p_peer, tA2D_SBC_CIE *p_cap, tA2D_SBC_CIE *p_pref) +{ + UINT8 status = A2D_SUCCESS; + tA2D_SBC_CIE peer_cie; + UNUSED(p_cap); + + /* parse peer capabilities */ + if ((status = A2D_ParsSbcInfo(&peer_cie, p_peer, TRUE)) != 0) { + return status; + } + + /* Check if the peer supports our channel mode */ + if (peer_cie.ch_mode & p_pref->ch_mode) { + peer_cie.ch_mode = p_pref->ch_mode; + } else { + APPL_TRACE_ERROR("bta_av_sbc_cfg_for_cap: ch_mode(0x%02X) not supported", p_pref->ch_mode); + return A2D_FAIL; + } + + /* Check if the peer supports our sampling freq */ + if (peer_cie.samp_freq & p_pref->samp_freq) { + peer_cie.samp_freq = p_pref->samp_freq; + } else { + APPL_TRACE_ERROR("bta_av_sbc_cfg_for_cap: samp_freq(0x%02X) not supported", p_pref->samp_freq); + return A2D_FAIL; + } + + /* Check if the peer supports our block len */ + if (peer_cie.block_len & p_pref->block_len) { + peer_cie.block_len = p_pref->block_len; + } else { + APPL_TRACE_ERROR("bta_av_sbc_cfg_for_cap: block_len(0x%02X) not supported", p_pref->block_len); + return A2D_FAIL; + } + + /* Check if the peer supports our num subbands */ + if (peer_cie.num_subbands & p_pref->num_subbands) { + peer_cie.num_subbands = p_pref->num_subbands; + } else { + APPL_TRACE_ERROR("bta_av_sbc_cfg_for_cap: num_subbands(0x%02X) not supported", p_pref->num_subbands); + return A2D_FAIL; + } + + /* Check if the peer supports our alloc method */ + if (peer_cie.alloc_mthd & p_pref->alloc_mthd) { + peer_cie.alloc_mthd = p_pref->alloc_mthd; + } else { + APPL_TRACE_ERROR("bta_av_sbc_cfg_for_cap: alloc_mthd(0x%02X) not supported", p_pref->alloc_mthd); + return A2D_FAIL; + } + + /* max bitpool */ + if (p_pref->max_bitpool != 0 && p_pref->max_bitpool < peer_cie.max_bitpool) { + peer_cie.max_bitpool = p_pref->max_bitpool; + } + + /* min bitpool */ + if (p_pref->min_bitpool != 0 && p_pref->min_bitpool > peer_cie.min_bitpool) { + peer_cie.min_bitpool = p_pref->min_bitpool; + } + + if (status == A2D_SUCCESS) { + /* build configuration */ + A2D_BldSbcInfo(A2D_MEDIA_TYPE_AUDIO, &peer_cie, p_peer); + } + return status; +} + +/******************************************************************************* +** +** Function bta_av_sbc_cfg_matches_cap +** +** Description This function checks whether an SBC codec configuration +** matched with capabilities. Here we check subset. +** +** Returns 0 if ok, nonzero if error. +** +*******************************************************************************/ +UINT8 bta_av_sbc_cfg_matches_cap(UINT8 *p_cfg, tA2D_SBC_CIE *p_cap) +{ + UINT8 status = 0; + tA2D_SBC_CIE cfg_cie; + + /* parse configuration */ + if ((status = A2D_ParsSbcInfo(&cfg_cie, p_cfg, TRUE)) != 0) { + APPL_TRACE_ERROR(" bta_av_sbc_cfg_matches_cap Parsing Failed %d", status); + return status; + } + + /* verify that each parameter is in range */ + + APPL_TRACE_DEBUG(" FREQ peer: 0%x, capability 0%x", cfg_cie.samp_freq, p_cap->samp_freq); + APPL_TRACE_DEBUG(" CH_MODE peer: 0%x, capability 0%x", cfg_cie.ch_mode, p_cap->ch_mode); + APPL_TRACE_DEBUG(" BLOCK_LEN peer: 0%x, capability 0%x", cfg_cie.block_len, p_cap->block_len); + APPL_TRACE_DEBUG(" SUB_BAND peer: 0%x, capability 0%x", cfg_cie.num_subbands, p_cap->num_subbands); + APPL_TRACE_DEBUG(" ALLOC_MTHD peer: 0%x, capability 0%x", cfg_cie.alloc_mthd, p_cap->alloc_mthd); + APPL_TRACE_DEBUG(" MAX_BitPool peer: 0%x, capability 0%x", cfg_cie.max_bitpool, p_cap->max_bitpool); + APPL_TRACE_DEBUG(" Min_bitpool peer: 0%x, capability 0%x", cfg_cie.min_bitpool, p_cap->min_bitpool); + + /* sampling frequency */ + if ((cfg_cie.samp_freq & p_cap->samp_freq) == 0) { + status = A2D_NS_SAMP_FREQ; + } + /* channel mode */ + else if ((cfg_cie.ch_mode & p_cap->ch_mode) == 0) { + status = A2D_NS_CH_MODE; + } + /* block length */ + else if ((cfg_cie.block_len & p_cap->block_len) == 0) { + status = A2D_BAD_BLOCK_LEN; + } + /* subbands */ + else if ((cfg_cie.num_subbands & p_cap->num_subbands) == 0) { + status = A2D_NS_SUBBANDS; + } + /* allocation method */ + else if ((cfg_cie.alloc_mthd & p_cap->alloc_mthd) == 0) { + status = A2D_NS_ALLOC_MTHD; + } + /* max bitpool */ + else if (cfg_cie.max_bitpool > p_cap->max_bitpool) { + status = A2D_NS_MAX_BITPOOL; + } + /* min bitpool */ + else if (cfg_cie.min_bitpool < p_cap->min_bitpool) { + status = A2D_NS_MIN_BITPOOL; + } + + return status; +} + + +/******************************************************************************* +** +** Function bta_av_sbc_cfg_in_cap +** +** Description This function checks whether an SBC codec configuration +** is allowable for the given codec capabilities. +** +** Returns 0 if ok, nonzero if error. +** +*******************************************************************************/ +UINT8 bta_av_sbc_cfg_in_cap(UINT8 *p_cfg, tA2D_SBC_CIE *p_cap) +{ + UINT8 status = 0; + tA2D_SBC_CIE cfg_cie; + + /* parse configuration */ + if ((status = A2D_ParsSbcInfo(&cfg_cie, p_cfg, FALSE)) != 0) { + return status; + } + + /* verify that each parameter is in range */ + + + /* sampling frequency */ + if ((cfg_cie.samp_freq & p_cap->samp_freq) == 0) { + status = A2D_NS_SAMP_FREQ; + } + /* channel mode */ + else if ((cfg_cie.ch_mode & p_cap->ch_mode) == 0) { + status = A2D_NS_CH_MODE; + } + /* block length */ + else if ((cfg_cie.block_len & p_cap->block_len) == 0) { + status = A2D_BAD_BLOCK_LEN; + } + /* subbands */ + else if ((cfg_cie.num_subbands & p_cap->num_subbands) == 0) { + status = A2D_NS_SUBBANDS; + } + /* allocation method */ + else if ((cfg_cie.alloc_mthd & p_cap->alloc_mthd) == 0) { + status = A2D_NS_ALLOC_MTHD; + } + /* max bitpool */ + else if (cfg_cie.max_bitpool > p_cap->max_bitpool) { + status = A2D_NS_MAX_BITPOOL; + } + /* min bitpool */ + else if (cfg_cie.min_bitpool < p_cap->min_bitpool) { + status = A2D_NS_MIN_BITPOOL; + } + + return status; +} + +/******************************************************************************* +** +** Function bta_av_sbc_bld_hdr +** +** Description This function builds the packet header for MPF1. +** +** Returns void +** +*******************************************************************************/ +void bta_av_sbc_bld_hdr(BT_HDR *p_buf, UINT16 fr_per_pkt) +{ + UINT8 *p; + + p_buf->offset -= BTA_AV_SBC_HDR_SIZE; + p = (UINT8 *) (p_buf + 1) + p_buf->offset; + p_buf->len += BTA_AV_SBC_HDR_SIZE; + A2D_BldSbcMplHdr(p, FALSE, FALSE, FALSE, (UINT8) fr_per_pkt); +} + +#endif /* #if defined(BTA_AV_INCLUDED) && (BTA_AV_INCLUDED == TRUE) */ diff --git a/lib/bt/host/bluedroid/bta/av/bta_av_ssm.c b/lib/bt/host/bluedroid/bta/av/bta_av_ssm.c new file mode 100644 index 00000000..15acf513 --- /dev/null +++ b/lib/bt/host/bluedroid/bta/av/bta_av_ssm.c @@ -0,0 +1,580 @@ +/****************************************************************************** + * + * Copyright (C) 2004-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This is the stream state machine for the BTA advanced audio/video. + * + ******************************************************************************/ + +#include "common/bt_target.h" +#if defined(BTA_AV_INCLUDED) && (BTA_AV_INCLUDED == TRUE) + +#include +#include "bta/bta_av_co.h" +#include "bta_av_int.h" +#include "osi/osi.h" + +/***************************************************************************** +** Constants and types +*****************************************************************************/ + +/* state machine states */ +enum { + BTA_AV_INIT_SST, + BTA_AV_INCOMING_SST, + BTA_AV_OPENING_SST, + BTA_AV_OPEN_SST, + BTA_AV_RCFG_SST, + BTA_AV_CLOSING_SST +}; + + +/* state machine action enumeration list */ +enum { + BTA_AV_DO_DISC, + BTA_AV_CLEANUP, + BTA_AV_FREE_SDB, + BTA_AV_CONFIG_IND, + BTA_AV_DISCONNECT_REQ, + BTA_AV_SECURITY_REQ, + BTA_AV_SECURITY_RSP, + BTA_AV_SETCONFIG_RSP, + BTA_AV_ST_RC_TIMER, + BTA_AV_STR_OPENED, + BTA_AV_SECURITY_IND, + BTA_AV_SECURITY_CFM, + BTA_AV_DO_CLOSE, + BTA_AV_CONNECT_REQ, + BTA_AV_SDP_FAILED, + BTA_AV_DISC_RESULTS, + BTA_AV_DISC_RES_AS_ACP, + BTA_AV_OPEN_FAILED, + BTA_AV_GETCAP_RESULTS, + BTA_AV_SETCONFIG_REJ, + BTA_AV_DISCOVER_REQ, + BTA_AV_CONN_FAILED, + BTA_AV_DO_START, + BTA_AV_STR_STOPPED, + BTA_AV_RECONFIG, + BTA_AV_DATA_PATH, + BTA_AV_START_OK, + BTA_AV_START_FAILED, + BTA_AV_STR_CLOSED, + BTA_AV_CLR_CONG, + BTA_AV_SUSPEND_CFM, + BTA_AV_RCFG_STR_OK, + BTA_AV_RCFG_FAILED, + BTA_AV_RCFG_CONNECT, + BTA_AV_RCFG_DISCNTD, + BTA_AV_SUSPEND_CONT, + BTA_AV_RCFG_CFM, + BTA_AV_RCFG_OPEN, + BTA_AV_SECURITY_REJ, + BTA_AV_OPEN_RC, + BTA_AV_CHK_2ND_START, + BTA_AV_SAVE_CAPS, + BTA_AV_SET_USE_RC, + BTA_AV_CCO_CLOSE, + BTA_AV_SWITCH_ROLE, + BTA_AV_ROLE_RES, + BTA_AV_DELAY_CO, + BTA_AV_OPEN_AT_INC, + BTA_AV_OPEN_FAIL_SDP, + BTA_AV_SET_DELAY_VALUE, + BTA_AV_NUM_SACTIONS +}; + +#define BTA_AV_SIGNORE BTA_AV_NUM_SACTIONS + + +/* state table information */ +/* #define BTA_AV_SACTION_COL 0 position of actions */ +#define BTA_AV_SACTIONS 2 /* number of actions */ +#define BTA_AV_SNEXT_STATE 2 /* position of next state */ +#define BTA_AV_NUM_COLS 3 /* number of columns in state tables */ + +/* state table for init state */ +static const UINT8 bta_av_sst_init[][BTA_AV_NUM_COLS] = { + /* Event Action 1 Action 2 Next state */ + /* AP_OPEN_EVT */ {BTA_AV_DO_DISC, BTA_AV_SIGNORE, BTA_AV_OPENING_SST }, + /* AP_CLOSE_EVT */ {BTA_AV_CLEANUP, BTA_AV_SIGNORE, BTA_AV_INIT_SST }, + /* AP_START_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_INIT_SST }, + /* AP_STOP_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_INIT_SST }, + /* AP_SET_DELAY_VALUE_EVT */{BTA_AV_SET_DELAY_VALUE,BTA_AV_SIGNORE, BTA_AV_INIT_SST }, + /* API_RECONFIG_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_INIT_SST }, + /* API_PROTECT_REQ_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_INIT_SST }, + /* API_PROTECT_RSP_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_INIT_SST }, + /* API_RC_OPEN_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_INIT_SST }, + /* SRC_DATA_READY_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_INIT_SST }, + /* CI_SETCONFIG_OK_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_INIT_SST }, + /* CI_SETCONFIG_FAIL_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_INIT_SST }, + /* SDP_DISC_OK_EVT */ {BTA_AV_FREE_SDB, BTA_AV_SIGNORE, BTA_AV_INIT_SST }, + /* SDP_DISC_FAIL_EVT */ {BTA_AV_FREE_SDB, BTA_AV_SIGNORE, BTA_AV_INIT_SST }, + /* STR_DISC_OK_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_INIT_SST }, + /* STR_DISC_FAIL_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_INIT_SST }, + /* STR_GETCAP_OK_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_INIT_SST }, + /* STR_GETCAP_FAIL_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_INIT_SST }, + /* STR_OPEN_OK_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_INIT_SST }, + /* STR_OPEN_FAIL_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_INIT_SST }, + /* STR_START_OK_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_INIT_SST }, + /* STR_START_FAIL_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_INIT_SST }, + /* STR_CLOSE_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_INIT_SST }, + /* STR_CONFIG_IND_EVT */ {BTA_AV_SETCONFIG_REJ, BTA_AV_SIGNORE, BTA_AV_INIT_SST }, + /* STR_SECURITY_IND_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_INIT_SST }, + /* STR_SECURITY_CFM_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_INIT_SST }, + /* STR_WRITE_CFM_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_INIT_SST }, + /* STR_SUSPEND_CFM_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_INIT_SST }, + /* STR_RECONFIG_CFM_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_INIT_SST }, + /* AVRC_TIMER_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_INIT_SST }, + /* AVDT_CONNECT_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_INIT_SST }, + /* AVDT_DISCONNECT_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_INIT_SST }, + /* ROLE_CHANGE_EVT*/ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_INIT_SST }, + /* AVDT_DELAY_RPT_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_INIT_SST }, + /* ACP_CONNECT_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_INCOMING_SST } +}; + +/* state table for incoming state */ +static const UINT8 bta_av_sst_incoming[][BTA_AV_NUM_COLS] = { + /* Event Action 1 Action 2 Next state */ + /* AP_OPEN_EVT */ {BTA_AV_OPEN_AT_INC, BTA_AV_SIGNORE, BTA_AV_INCOMING_SST }, + /* AP_CLOSE_EVT */ {BTA_AV_CCO_CLOSE, BTA_AV_DISCONNECT_REQ, BTA_AV_CLOSING_SST }, + /* AP_START_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_INCOMING_SST }, + /* AP_STOP_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_INCOMING_SST }, + /* AP_SET_DELAY_VALUE_EVT */{BTA_AV_SET_DELAY_VALUE,BTA_AV_SIGNORE, BTA_AV_INCOMING_SST }, + /* API_RECONFIG_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_INCOMING_SST }, + /* API_PROTECT_REQ_EVT */ {BTA_AV_SECURITY_REQ, BTA_AV_SIGNORE, BTA_AV_INCOMING_SST }, + /* API_PROTECT_RSP_EVT */ {BTA_AV_SECURITY_RSP, BTA_AV_SIGNORE, BTA_AV_INCOMING_SST }, + /* API_RC_OPEN_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_INCOMING_SST }, + /* SRC_DATA_READY_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_INCOMING_SST }, + /* CI_SETCONFIG_OK_EVT */ {BTA_AV_SETCONFIG_RSP, BTA_AV_ST_RC_TIMER, BTA_AV_INCOMING_SST }, + /* CI_SETCONFIG_FAIL_EVT */ {BTA_AV_SETCONFIG_REJ, BTA_AV_CLEANUP, BTA_AV_INIT_SST }, + /* SDP_DISC_OK_EVT */ {BTA_AV_FREE_SDB, BTA_AV_SIGNORE, BTA_AV_INCOMING_SST }, + /* SDP_DISC_FAIL_EVT */ {BTA_AV_FREE_SDB, BTA_AV_SIGNORE, BTA_AV_INCOMING_SST }, + /* STR_DISC_OK_EVT */ {BTA_AV_DISC_RES_AS_ACP, BTA_AV_SIGNORE, BTA_AV_INCOMING_SST }, + /* STR_DISC_FAIL_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_INCOMING_SST }, + /* STR_GETCAP_OK_EVT */ {BTA_AV_SAVE_CAPS, BTA_AV_SIGNORE, BTA_AV_INCOMING_SST }, + /* STR_GETCAP_FAIL_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_INCOMING_SST }, + /* STR_OPEN_OK_EVT */ {BTA_AV_STR_OPENED, BTA_AV_SIGNORE, BTA_AV_OPEN_SST }, + /* STR_OPEN_FAIL_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_INCOMING_SST }, + /* STR_START_OK_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_INCOMING_SST }, + /* STR_START_FAIL_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_INCOMING_SST }, + /* STR_CLOSE_EVT */ {BTA_AV_CCO_CLOSE, BTA_AV_CLEANUP, BTA_AV_INIT_SST }, + /* STR_CONFIG_IND_EVT */ {BTA_AV_CONFIG_IND, BTA_AV_SIGNORE, BTA_AV_INCOMING_SST }, + /* STR_SECURITY_IND_EVT */ {BTA_AV_SECURITY_IND, BTA_AV_SIGNORE, BTA_AV_INCOMING_SST }, + /* STR_SECURITY_CFM_EVT */ {BTA_AV_SECURITY_CFM, BTA_AV_SIGNORE, BTA_AV_INCOMING_SST }, + /* STR_WRITE_CFM_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_INCOMING_SST }, + /* STR_SUSPEND_CFM_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_INCOMING_SST }, + /* STR_RECONFIG_CFM_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_INCOMING_SST }, + /* AVRC_TIMER_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_INCOMING_SST }, + /* AVDT_CONNECT_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_INCOMING_SST }, + /* AVDT_DISCONNECT_EVT */ {BTA_AV_CCO_CLOSE, BTA_AV_CLEANUP, BTA_AV_INIT_SST }, + /* ROLE_CHANGE_EVT*/ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_INCOMING_SST }, + /* AVDT_DELAY_RPT_EVT */ {BTA_AV_DELAY_CO, BTA_AV_SIGNORE, BTA_AV_INCOMING_SST }, + /* ACP_CONNECT_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_INCOMING_SST } +}; + +/* state table for opening state */ +static const UINT8 bta_av_sst_opening[][BTA_AV_NUM_COLS] = { + /* Event Action 1 Action 2 Next state */ + /* AP_OPEN_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_OPENING_SST }, + /* AP_CLOSE_EVT */ {BTA_AV_DO_CLOSE, BTA_AV_SIGNORE, BTA_AV_CLOSING_SST }, + /* AP_START_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_OPENING_SST }, + /* AP_STOP_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_OPENING_SST }, + /* AP_SET_DELAY_VALUE_EVT */{BTA_AV_SET_DELAY_VALUE,BTA_AV_SIGNORE, BTA_AV_OPENING_SST }, + /* API_RECONFIG_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_OPENING_SST }, + /* API_PROTECT_REQ_EVT */ {BTA_AV_SECURITY_REQ, BTA_AV_SIGNORE, BTA_AV_OPENING_SST }, + /* API_PROTECT_RSP_EVT */ {BTA_AV_SECURITY_RSP, BTA_AV_SIGNORE, BTA_AV_OPENING_SST }, + /* API_RC_OPEN_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_OPENING_SST }, + /* SRC_DATA_READY_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_OPENING_SST }, + /* CI_SETCONFIG_OK_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_OPENING_SST }, + /* CI_SETCONFIG_FAIL_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_OPENING_SST }, + /* SDP_DISC_OK_EVT */ {BTA_AV_CONNECT_REQ, BTA_AV_SIGNORE, BTA_AV_OPENING_SST }, + /* SDP_DISC_FAIL_EVT */ {BTA_AV_FREE_SDB, BTA_AV_OPEN_FAIL_SDP, BTA_AV_INIT_SST }, + /* STR_DISC_OK_EVT */ {BTA_AV_DISC_RESULTS, BTA_AV_SIGNORE, BTA_AV_OPENING_SST }, + /* STR_DISC_FAIL_EVT */ {BTA_AV_OPEN_FAILED, BTA_AV_SIGNORE, BTA_AV_CLOSING_SST }, + /* STR_GETCAP_OK_EVT */ {BTA_AV_GETCAP_RESULTS, BTA_AV_SIGNORE, BTA_AV_OPENING_SST }, + /* STR_GETCAP_FAIL_EVT */ {BTA_AV_OPEN_FAILED, BTA_AV_SIGNORE, BTA_AV_CLOSING_SST }, + /* STR_OPEN_OK_EVT */ {BTA_AV_ST_RC_TIMER, BTA_AV_STR_OPENED, BTA_AV_OPEN_SST }, + /* STR_OPEN_FAIL_EVT */ {BTA_AV_OPEN_FAILED, BTA_AV_SIGNORE, BTA_AV_CLOSING_SST }, + /* STR_START_OK_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_OPENING_SST }, + /* STR_START_FAIL_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_OPENING_SST }, + /* STR_CLOSE_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_OPENING_SST }, + /* STR_CONFIG_IND_EVT */ {BTA_AV_CONFIG_IND, BTA_AV_SIGNORE, BTA_AV_INCOMING_SST }, + /* STR_SECURITY_IND_EVT */ {BTA_AV_SECURITY_IND, BTA_AV_SIGNORE, BTA_AV_OPENING_SST }, + /* STR_SECURITY_CFM_EVT */ {BTA_AV_SECURITY_CFM, BTA_AV_SIGNORE, BTA_AV_OPENING_SST }, + /* STR_WRITE_CFM_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_OPENING_SST }, + /* STR_SUSPEND_CFM_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_OPENING_SST }, + /* STR_RECONFIG_CFM_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_OPENING_SST }, + /* AVRC_TIMER_EVT */ {BTA_AV_SWITCH_ROLE, BTA_AV_SIGNORE, BTA_AV_OPENING_SST }, + /* AVDT_CONNECT_EVT */ {BTA_AV_DISCOVER_REQ, BTA_AV_SIGNORE, BTA_AV_OPENING_SST }, + /* AVDT_DISCONNECT_EVT */ {BTA_AV_CONN_FAILED, BTA_AV_SIGNORE, BTA_AV_INIT_SST }, + /* ROLE_CHANGE_EVT*/ {BTA_AV_ROLE_RES, BTA_AV_SIGNORE, BTA_AV_OPENING_SST }, + /* AVDT_DELAY_RPT_EVT */ {BTA_AV_DELAY_CO, BTA_AV_SIGNORE, BTA_AV_OPENING_SST }, + /* ACP_CONNECT_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_OPENING_SST } +}; + +/* state table for open state */ +static const UINT8 bta_av_sst_open[][BTA_AV_NUM_COLS] = { + /* Event Action 1 Action 2 Next state */ + /* AP_OPEN_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_OPEN_SST }, + /* AP_CLOSE_EVT */ {BTA_AV_DO_CLOSE, BTA_AV_SIGNORE, BTA_AV_CLOSING_SST }, + /* AP_START_EVT */ {BTA_AV_DO_START, BTA_AV_SIGNORE, BTA_AV_OPEN_SST }, + /* AP_STOP_EVT */ {BTA_AV_STR_STOPPED, BTA_AV_SIGNORE, BTA_AV_OPEN_SST }, + /* AP_SET_DELAY_VALUE_EVT */{BTA_AV_SET_DELAY_VALUE,BTA_AV_SIGNORE, BTA_AV_OPEN_SST }, + /* API_RECONFIG_EVT */ {BTA_AV_RECONFIG, BTA_AV_SIGNORE, BTA_AV_RCFG_SST }, + /* API_PROTECT_REQ_EVT */ {BTA_AV_SECURITY_REQ, BTA_AV_SIGNORE, BTA_AV_OPEN_SST }, + /* API_PROTECT_RSP_EVT */ {BTA_AV_SECURITY_RSP, BTA_AV_SIGNORE, BTA_AV_OPEN_SST }, + /* API_RC_OPEN_EVT */ {BTA_AV_SET_USE_RC, BTA_AV_OPEN_RC, BTA_AV_OPEN_SST }, + /* SRC_DATA_READY_EVT */ {BTA_AV_DATA_PATH, BTA_AV_SIGNORE, BTA_AV_OPEN_SST }, + /* CI_SETCONFIG_OK_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_OPEN_SST }, + /* CI_SETCONFIG_FAIL_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_OPEN_SST }, + /* SDP_DISC_OK_EVT */ {BTA_AV_FREE_SDB, BTA_AV_SIGNORE, BTA_AV_OPEN_SST }, + /* SDP_DISC_FAIL_EVT */ {BTA_AV_FREE_SDB, BTA_AV_SIGNORE, BTA_AV_OPEN_SST }, + /* STR_DISC_OK_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_OPEN_SST }, + /* STR_DISC_FAIL_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_OPEN_SST }, + /* STR_GETCAP_OK_EVT */ {BTA_AV_SAVE_CAPS, BTA_AV_SIGNORE, BTA_AV_OPEN_SST }, + /* STR_GETCAP_FAIL_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_OPEN_SST }, + /* STR_OPEN_OK_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_OPEN_SST }, + /* STR_OPEN_FAIL_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_OPEN_SST }, + /* STR_START_OK_EVT */ {BTA_AV_START_OK, BTA_AV_SIGNORE, BTA_AV_OPEN_SST }, + /* STR_START_FAIL_EVT */ {BTA_AV_START_FAILED, BTA_AV_SIGNORE, BTA_AV_OPEN_SST }, + /* STR_CLOSE_EVT */ {BTA_AV_STR_CLOSED, BTA_AV_SIGNORE, BTA_AV_INIT_SST }, + /* STR_CONFIG_IND_EVT */ {BTA_AV_SETCONFIG_REJ, BTA_AV_SIGNORE, BTA_AV_OPEN_SST }, + /* STR_SECURITY_IND_EVT */ {BTA_AV_SECURITY_IND, BTA_AV_SIGNORE, BTA_AV_OPEN_SST }, + /* STR_SECURITY_CFM_EVT */ {BTA_AV_SECURITY_CFM, BTA_AV_SIGNORE, BTA_AV_OPEN_SST }, + /* STR_WRITE_CFM_EVT */ {BTA_AV_CLR_CONG, BTA_AV_DATA_PATH, BTA_AV_OPEN_SST }, + /* STR_SUSPEND_CFM_EVT */ {BTA_AV_SUSPEND_CFM, BTA_AV_SIGNORE, BTA_AV_OPEN_SST }, + /* STR_RECONFIG_CFM_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_OPEN_SST }, + /* AVRC_TIMER_EVT */ {BTA_AV_OPEN_RC, BTA_AV_CHK_2ND_START, BTA_AV_OPEN_SST }, + /* AVDT_CONNECT_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_OPEN_SST }, + /* AVDT_DISCONNECT_EVT */ {BTA_AV_STR_CLOSED, BTA_AV_SIGNORE, BTA_AV_INIT_SST }, + /* ROLE_CHANGE_EVT*/ {BTA_AV_ROLE_RES, BTA_AV_SIGNORE, BTA_AV_OPEN_SST }, + /* AVDT_DELAY_RPT_EVT */ {BTA_AV_DELAY_CO, BTA_AV_SIGNORE, BTA_AV_OPEN_SST }, + /* ACP_CONNECT_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_OPEN_SST } +}; + +/* state table for reconfig state */ +static const UINT8 bta_av_sst_rcfg[][BTA_AV_NUM_COLS] = { + /* Event Action 1 Action 2 Next state */ + /* AP_OPEN_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_RCFG_SST }, + /* AP_CLOSE_EVT */ {BTA_AV_DISCONNECT_REQ, BTA_AV_SIGNORE, BTA_AV_CLOSING_SST }, + /* AP_START_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_RCFG_SST }, + /* AP_STOP_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_RCFG_SST }, + /* AP_SET_DELAY_VALUE_EVT */{BTA_AV_SET_DELAY_VALUE,BTA_AV_SIGNORE, BTA_AV_RCFG_SST }, + /* API_RECONFIG_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_RCFG_SST }, + /* API_PROTECT_REQ_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_RCFG_SST }, + /* API_PROTECT_RSP_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_RCFG_SST }, + /* API_RC_OPEN_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_RCFG_SST }, + /* SRC_DATA_READY_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_RCFG_SST }, + /* CI_SETCONFIG_OK_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_RCFG_SST }, + /* CI_SETCONFIG_FAIL_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_RCFG_SST }, + /* SDP_DISC_OK_EVT */ {BTA_AV_FREE_SDB, BTA_AV_SIGNORE, BTA_AV_RCFG_SST }, + /* SDP_DISC_FAIL_EVT */ {BTA_AV_FREE_SDB, BTA_AV_SIGNORE, BTA_AV_RCFG_SST }, + /* STR_DISC_OK_EVT */ {BTA_AV_DISC_RESULTS, BTA_AV_SIGNORE, BTA_AV_RCFG_SST }, + /* STR_DISC_FAIL_EVT */ {BTA_AV_STR_CLOSED, BTA_AV_SIGNORE, BTA_AV_INIT_SST }, + /* STR_GETCAP_OK_EVT */ {BTA_AV_GETCAP_RESULTS, BTA_AV_SIGNORE, BTA_AV_RCFG_SST }, + /* STR_GETCAP_FAIL_EVT */ {BTA_AV_STR_CLOSED, BTA_AV_SIGNORE, BTA_AV_INIT_SST }, + /* STR_OPEN_OK_EVT */ {BTA_AV_RCFG_STR_OK, BTA_AV_SIGNORE, BTA_AV_OPEN_SST }, + /* STR_OPEN_FAIL_EVT */ {BTA_AV_RCFG_FAILED, BTA_AV_SIGNORE, BTA_AV_RCFG_SST }, + /* STR_START_OK_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_RCFG_SST }, + /* STR_START_FAIL_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_RCFG_SST }, + /* STR_CLOSE_EVT */ {BTA_AV_RCFG_CONNECT, BTA_AV_SIGNORE, BTA_AV_RCFG_SST }, + /* STR_CONFIG_IND_EVT */ {BTA_AV_SETCONFIG_REJ, BTA_AV_SIGNORE, BTA_AV_RCFG_SST }, + /* STR_SECURITY_IND_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_RCFG_SST }, + /* STR_SECURITY_CFM_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_RCFG_SST }, + /* STR_WRITE_CFM_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_RCFG_SST }, + /* STR_SUSPEND_CFM_EVT */ {BTA_AV_SUSPEND_CONT, BTA_AV_SIGNORE, BTA_AV_RCFG_SST }, + /* STR_RECONFIG_CFM_EVT */ {BTA_AV_RCFG_CFM, BTA_AV_SIGNORE, BTA_AV_RCFG_SST }, + /* AVRC_TIMER_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_RCFG_SST }, + /* AVDT_CONNECT_EVT */ {BTA_AV_RCFG_OPEN, BTA_AV_SIGNORE, BTA_AV_RCFG_SST }, + /* AVDT_DISCONNECT_EVT */ {BTA_AV_RCFG_DISCNTD, BTA_AV_SIGNORE, BTA_AV_RCFG_SST }, + /* ROLE_CHANGE_EVT*/ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_RCFG_SST }, + /* AVDT_DELAY_RPT_EVT */ {BTA_AV_DELAY_CO, BTA_AV_SIGNORE, BTA_AV_RCFG_SST }, + /* ACP_CONNECT_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_RCFG_SST } +}; + +/* state table for closing state */ +static const UINT8 bta_av_sst_closing[][BTA_AV_NUM_COLS] = { + /* Event Action 1 Action 2 Next state */ + /* AP_OPEN_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_CLOSING_SST }, + /* AP_CLOSE_EVT */ {BTA_AV_DISCONNECT_REQ, BTA_AV_SIGNORE, BTA_AV_CLOSING_SST }, + /* AP_START_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_CLOSING_SST }, + /* AP_STOP_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_CLOSING_SST }, + /* AP_SET_DELAY_VALUE_EVT */{BTA_AV_SET_DELAY_VALUE,BTA_AV_SIGNORE, BTA_AV_CLOSING_SST }, + /* API_RECONFIG_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_CLOSING_SST }, + /* API_PROTECT_REQ_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_CLOSING_SST }, + /* API_PROTECT_RSP_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_CLOSING_SST }, + /* API_RC_OPEN_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_CLOSING_SST }, + /* SRC_DATA_READY_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_CLOSING_SST }, + /* CI_SETCONFIG_OK_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_CLOSING_SST }, + /* CI_SETCONFIG_FAIL_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_CLOSING_SST }, + /* SDP_DISC_OK_EVT */ {BTA_AV_SDP_FAILED, BTA_AV_SIGNORE, BTA_AV_INIT_SST }, + /* SDP_DISC_FAIL_EVT */ {BTA_AV_SDP_FAILED, BTA_AV_SIGNORE, BTA_AV_INIT_SST }, + /* STR_DISC_OK_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_CLOSING_SST }, + /* STR_DISC_FAIL_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_CLOSING_SST }, + /* STR_GETCAP_OK_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_CLOSING_SST }, + /* STR_GETCAP_FAIL_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_CLOSING_SST }, + /* STR_OPEN_OK_EVT */ {BTA_AV_DO_CLOSE, BTA_AV_SIGNORE, BTA_AV_CLOSING_SST }, + /* STR_OPEN_FAIL_EVT */ {BTA_AV_DISCONNECT_REQ, BTA_AV_SIGNORE, BTA_AV_CLOSING_SST }, + /* STR_START_OK_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_CLOSING_SST }, + /* STR_START_FAIL_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_CLOSING_SST }, + /* STR_CLOSE_EVT */ {BTA_AV_DISCONNECT_REQ, BTA_AV_SIGNORE, BTA_AV_CLOSING_SST }, + /* STR_CONFIG_IND_EVT */ {BTA_AV_SETCONFIG_REJ, BTA_AV_SIGNORE, BTA_AV_CLOSING_SST }, + /* STR_SECURITY_IND_EVT */ {BTA_AV_SECURITY_REJ, BTA_AV_SIGNORE, BTA_AV_CLOSING_SST }, + /* STR_SECURITY_CFM_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_CLOSING_SST }, + /* STR_WRITE_CFM_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_CLOSING_SST }, + /* STR_SUSPEND_CFM_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_CLOSING_SST }, + /* STR_RECONFIG_CFM_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_CLOSING_SST }, + /* AVRC_TIMER_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_CLOSING_SST }, + /* AVDT_CONNECT_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_CLOSING_SST }, + /* AVDT_DISCONNECT_EVT */ {BTA_AV_STR_CLOSED, BTA_AV_SIGNORE, BTA_AV_INIT_SST }, + /* ROLE_CHANGE_EVT*/ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_CLOSING_SST }, + /* AVDT_DELAY_RPT_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_CLOSING_SST }, + /* ACP_CONNECT_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_CLOSING_SST } +}; + +/* type for state table */ +typedef const UINT8 (*tBTA_AV_SST_TBL)[BTA_AV_NUM_COLS]; + +/* state table */ +static const tBTA_AV_SST_TBL bta_av_sst_tbl[] = { + bta_av_sst_init, + bta_av_sst_incoming, + bta_av_sst_opening, + bta_av_sst_open, + bta_av_sst_rcfg, + bta_av_sst_closing +}; + +static char *bta_av_sst_code(UINT8 state); + +/******************************************************************************* +** +** Function bta_av_is_rcfg_sst +** +** Description Check if stream state machine is in reconfig state. +** +** +** Returns TRUE if stream state machine is in reconfig state. +** +*******************************************************************************/ +BOOLEAN bta_av_is_rcfg_sst (tBTA_AV_SCB *p_scb) +{ + BOOLEAN is_rcfg_sst = FALSE; + + if (p_scb != NULL) { + if (p_scb->state == BTA_AV_RCFG_SST) { + is_rcfg_sst = TRUE; + } + } + + return is_rcfg_sst; +} + +/******************************************************************************* +** +** Function bta_av_ssm_execute +** +** Description Stream state machine event handling function for AV +** +** +** Returns void +** +*******************************************************************************/ +void bta_av_ssm_execute(tBTA_AV_SCB *p_scb, UINT16 event, tBTA_AV_DATA *p_data) +{ + tBTA_AV_SST_TBL state_table; + UINT8 action; + int i, xx; + + if (p_scb == NULL) { + /* this stream is not registered */ + APPL_TRACE_EVENT("AV channel not registered"); + return; + } + + /* In case incoming connection is for VDP, we need to swap scb. */ + /* When ACP_CONNECT_EVT was received, we put first available scb to */ + /* to Incoming state. Later, when STR_CONFIG_IND_EVT is coming, we */ + /* know if it is A2DP or VDP. */ + if ((p_scb->state == BTA_AV_INIT_SST) && (event == BTA_AV_STR_CONFIG_IND_EVT)) { + for (xx = 0; xx < BTA_AV_NUM_STRS; xx++) { + if (bta_av_cb.p_scb[xx]) { + if (bta_av_cb.p_scb[xx]->state == BTA_AV_INCOMING_SST) { + bta_av_cb.p_scb[xx]->state = BTA_AV_INIT_SST; + bta_av_cb.p_scb[xx]->coll_mask = 0; + p_scb->state = BTA_AV_INCOMING_SST; + break; + } + } + } + } + + APPL_TRACE_VERBOSE("AV Sevent(0x%x)=0x%x(%s) state=%d(%s)", + p_scb->hndl, event, bta_av_evt_code(event), p_scb->state, bta_av_sst_code(p_scb->state)); + + /* look up the state table for the current state */ + state_table = bta_av_sst_tbl[p_scb->state]; + + event -= BTA_AV_FIRST_SSM_EVT; + + /* set next state */ + p_scb->state = state_table[event][BTA_AV_SNEXT_STATE]; + + /* execute action functions */ + for (i = 0; i < BTA_AV_SACTIONS; i++) { + if ((action = state_table[event][i]) != BTA_AV_SIGNORE) { + APPL_TRACE_VERBOSE("AV Sevent(0x%x)=%d(%s)", p_scb->hndl, event, bta_av_action_code(action)); + (*p_scb->p_act_tbl[action])(p_scb, p_data); + } else { + break; + } + } + +} + +/******************************************************************************* +** +** Function bta_av_is_scb_opening +** +** Description Returns TRUE is scb is in opening state. +** +** +** Returns TRUE if scb is in opening state. +** +*******************************************************************************/ +BOOLEAN bta_av_is_scb_opening (tBTA_AV_SCB *p_scb) +{ + BOOLEAN is_opening = FALSE; + + if (p_scb) { + if (p_scb->state == BTA_AV_OPENING_SST) { + is_opening = TRUE; + } + } + + return is_opening; +} + +/******************************************************************************* +** +** Function bta_av_is_scb_incoming +** +** Description Returns TRUE is scb is in incoming state. +** +** +** Returns TRUE if scb is in incoming state. +** +*******************************************************************************/ +BOOLEAN bta_av_is_scb_incoming (tBTA_AV_SCB *p_scb) +{ + BOOLEAN is_incoming = FALSE; + + if (p_scb) { + if (p_scb->state == BTA_AV_INCOMING_SST) { + is_incoming = TRUE; + } + } + + return is_incoming; +} + +/******************************************************************************* +** +** Function bta_av_set_scb_sst_init +** +** Description Set SST state to INIT. +** Use this function to change SST outside of state machine. +** +** Returns None +** +*******************************************************************************/ +void bta_av_set_scb_sst_init (tBTA_AV_SCB *p_scb) +{ + if (p_scb) { + p_scb->state = BTA_AV_INIT_SST; + } +} + +/******************************************************************************* +** +** Function bta_av_is_scb_init +** +** Description Returns TRUE is scb is in init state. +** +** +** Returns TRUE if scb is in incoming state. +** +*******************************************************************************/ +BOOLEAN bta_av_is_scb_init (tBTA_AV_SCB *p_scb) +{ + BOOLEAN is_init = FALSE; + + if (p_scb) { + if (p_scb->state == BTA_AV_INIT_SST) { + is_init = TRUE; + } + } + + return is_init; +} + +/******************************************************************************* +** +** Function bta_av_set_scb_sst_incoming +** +** Description Set SST state to incoming. +** Use this function to change SST outside of state machine. +** +** Returns None +** +*******************************************************************************/ +void bta_av_set_scb_sst_incoming (tBTA_AV_SCB *p_scb) +{ + if (p_scb) { + p_scb->state = BTA_AV_INCOMING_SST; + } +} + +/***************************************************************************** +** Debug Functions +*****************************************************************************/ +/******************************************************************************* +** +** Function bta_av_sst_code +** +** Description +** +** Returns char * +** +*******************************************************************************/ +UNUSED_ATTR static char *bta_av_sst_code(UINT8 state) +{ + switch (state) { + case BTA_AV_INIT_SST: return "INIT"; + case BTA_AV_INCOMING_SST: return "INCOMING"; + case BTA_AV_OPENING_SST: return "OPENING"; + case BTA_AV_OPEN_SST: return "OPEN"; + case BTA_AV_RCFG_SST: return "RCFG"; + case BTA_AV_CLOSING_SST: return "CLOSING"; + default: return "unknown"; + } +} + +#endif /* BTA_AV_INCLUDED */ diff --git a/lib/bt/host/bluedroid/bta/av/include/bta_av_int.h b/lib/bt/host/bluedroid/bta/av/include/bta_av_int.h new file mode 100644 index 00000000..8c255456 --- /dev/null +++ b/lib/bt/host/bluedroid/bta/av/include/bta_av_int.h @@ -0,0 +1,710 @@ +/****************************************************************************** + * + * Copyright (C) 2004-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This is the private interface file for the BTA advanced audio/video. + * + ******************************************************************************/ +#ifndef BTA_AV_INT_H +#define BTA_AV_INT_H + +#include "bta/bta_sys.h" +#include "bta/bta_api.h" +#include "bta/bta_av_api.h" +#include "stack/avdt_api.h" +#include "bta/bta_av_co.h" +#include "osi/list.h" + +#if (BTA_AV_INCLUDED == TRUE) + +/***************************************************************************** +** Constants +*****************************************************************************/ + +enum { + /* these events are handled by the AV main state machine */ + BTA_AV_API_DISABLE_EVT = BTA_SYS_EVT_START(BTA_ID_AV), + BTA_AV_API_REMOTE_CMD_EVT, + BTA_AV_API_VENDOR_CMD_EVT, + BTA_AV_API_VENDOR_RSP_EVT, + BTA_AV_API_META_RSP_EVT, + BTA_AV_API_RC_CLOSE_EVT, + BTA_AV_AVRC_OPEN_EVT, + BTA_AV_AVRC_MSG_EVT, + BTA_AV_AVRC_NONE_EVT, + + /* these events are handled by the AV stream state machine */ + BTA_AV_API_OPEN_EVT, + BTA_AV_API_CLOSE_EVT, + BTA_AV_AP_START_EVT, /* the following 3 events must be in the same order as the *API_*EVT */ + BTA_AV_AP_STOP_EVT, + BTA_AV_AP_SET_DELAY_VALUE_EVT, + BTA_AV_API_RECONFIG_EVT, + BTA_AV_API_PROTECT_REQ_EVT, + BTA_AV_API_PROTECT_RSP_EVT, + BTA_AV_API_RC_OPEN_EVT, + BTA_AV_SRC_DATA_READY_EVT, + BTA_AV_CI_SETCONFIG_OK_EVT, + BTA_AV_CI_SETCONFIG_FAIL_EVT, + BTA_AV_SDP_DISC_OK_EVT, + BTA_AV_SDP_DISC_FAIL_EVT, + BTA_AV_STR_DISC_OK_EVT, + BTA_AV_STR_DISC_FAIL_EVT, + BTA_AV_STR_GETCAP_OK_EVT, + BTA_AV_STR_GETCAP_FAIL_EVT, + BTA_AV_STR_OPEN_OK_EVT, + BTA_AV_STR_OPEN_FAIL_EVT, + BTA_AV_STR_START_OK_EVT, + BTA_AV_STR_START_FAIL_EVT, + BTA_AV_STR_CLOSE_EVT, + BTA_AV_STR_CONFIG_IND_EVT, + BTA_AV_STR_SECURITY_IND_EVT, + BTA_AV_STR_SECURITY_CFM_EVT, + BTA_AV_STR_WRITE_CFM_EVT, + BTA_AV_STR_SUSPEND_CFM_EVT, + BTA_AV_STR_RECONFIG_CFM_EVT, + BTA_AV_AVRC_TIMER_EVT, + BTA_AV_AVDT_CONNECT_EVT, + BTA_AV_AVDT_DISCONNECT_EVT, + BTA_AV_ROLE_CHANGE_EVT, + BTA_AV_AVDT_DELAY_RPT_EVT, + BTA_AV_ACP_CONNECT_EVT, + + /* these events are handled outside of the state machine */ + BTA_AV_API_ENABLE_EVT, + BTA_AV_API_REGISTER_EVT, + BTA_AV_API_DEREGISTER_EVT, + BTA_AV_API_DISCONNECT_EVT, + BTA_AV_CI_SRC_DATA_READY_EVT, + BTA_AV_SIG_CHG_EVT, + BTA_AV_SIG_TIMER_EVT, + BTA_AV_SDP_AVRC_DISC_EVT, + BTA_AV_AVRC_CLOSE_EVT, + BTA_AV_CONN_CHG_EVT, + BTA_AV_DEREG_COMP_EVT, +#if (BTA_AV_SINK_INCLUDED == TRUE) + BTA_AV_API_SINK_ENABLE_EVT, + // add + BTA_AV_API_GET_DELAY_VALUE_EVT, +#endif +#if (AVDT_REPORTING == TRUE) + BTA_AV_AVDT_RPT_CONN_EVT, +#endif + BTA_AV_API_START_EVT, /* the following 3 events must be in the same order as the *AP_*EVT */ + BTA_AV_API_STOP_EVT, + BTA_AV_API_SET_DELAY_VALUE_EVT, +}; + +/* events for AV control block state machine */ +#define BTA_AV_FIRST_SM_EVT BTA_AV_API_DISABLE_EVT +#define BTA_AV_LAST_SM_EVT BTA_AV_AVRC_NONE_EVT + +/* events for AV stream control block state machine */ +#define BTA_AV_FIRST_SSM_EVT BTA_AV_API_OPEN_EVT + +/* events that do not go through state machine */ +#define BTA_AV_FIRST_NSM_EVT BTA_AV_API_ENABLE_EVT +#define BTA_AV_LAST_NSM_EVT BTA_AV_API_SET_DELAY_VALUE_EVT + +/* API events passed to both SSMs (by bta_av_api_to_ssm) */ +#define BTA_AV_FIRST_A2S_API_EVT BTA_AV_API_START_EVT +#define BTA_AV_FIRST_A2S_SSM_EVT BTA_AV_AP_START_EVT + +#define BTA_AV_LAST_EVT BTA_AV_API_SET_DELAY_VALUE_EVT + +/* maximum number of SEPS in stream discovery results */ +#define BTA_AV_NUM_SEPS 32 + +/* initialization value for AVRC handle */ +#define BTA_AV_RC_HANDLE_NONE 0xFF + +/* size of database for service discovery */ +#define BTA_AV_DISC_BUF_SIZE 1000 + +/* offset of media type in codec info byte array */ +#define BTA_AV_MEDIA_TYPE_IDX 1 + +/* maximum length of AVDTP security data */ +#define BTA_AV_SECURITY_MAX_LEN 400 + +/* check number of buffers queued at L2CAP when this amount of buffers are queued to L2CAP */ +#define BTA_AV_QUEUE_DATA_CHK_NUM L2CAP_HIGH_PRI_MIN_XMIT_QUOTA + +/* the number of ACL links with AVDT */ +#define BTA_AV_NUM_LINKS AVDT_NUM_LINKS + +#define BTA_AV_CO_ID_TO_BE_STREAM(p, u32) {*(p)++ = (UINT8)((u32) >> 16); *(p)++ = (UINT8)((u32) >> 8); *(p)++ = (UINT8)(u32); } +#define BTA_AV_BE_STREAM_TO_CO_ID(u32, p) {u32 = (((UINT32)(*((p) + 2))) + (((UINT32)(*((p) + 1))) << 8) + (((UINT32)(*(p))) << 16)); (p) += 3;} + +/* these bits are defined for bta_av_cb.multi_av */ +#define BTA_AV_MULTI_AV_SUPPORTED 0x01 +#define BTA_AV_MULTI_AV_IN_USE 0x02 + +#define TSEP_TO_SYS_ID(x) ((x) == AVDT_TSEP_SRC ? BTA_ID_AV : BTA_ID_AVK) + +/***************************************************************************** +** Data types +*****************************************************************************/ +/* data type for BTA_AV_API_ENABLE_EVT */ +typedef struct { + BT_HDR hdr; + tBTA_AV_CBACK *p_cback; + tBTA_AV_FEAT features; + tBTA_SEC sec_mask; +} tBTA_AV_API_ENABLE; + +/* data type for BTA_AV_API_REG_EVT */ +typedef struct { + BT_HDR hdr; + char p_service_name[BTA_SERVICE_NAME_LEN + 1]; + UINT8 app_id; + UINT8 tsep; // local SEP type + tBTA_AV_DATA_CBACK *p_app_data_cback; + tBTA_AV_CO_FUNCTS *bta_av_cos; + tBTA_AVRC_CO_FUNCTS *bta_avrc_cos; +} tBTA_AV_API_REG; + + +enum { + BTA_AV_RS_NONE, /* straight API call */ + BTA_AV_RS_OK, /* the role switch result - successful */ + BTA_AV_RS_FAIL, /* the role switch result - failed */ + BTA_AV_RS_DONE /* the role switch is done - continue */ +}; +typedef UINT8 tBTA_AV_RS_RES; +/* data type for BTA_AV_API_OPEN_EVT */ +typedef struct { + BT_HDR hdr; + BD_ADDR bd_addr; + BOOLEAN use_rc; + tBTA_SEC sec_mask; + tBTA_AV_RS_RES switch_res; + UINT16 uuid; /* uuid of initiator */ +} tBTA_AV_API_OPEN; + +/* data type for BTA_AV_API_STOP_EVT */ +typedef struct { + BT_HDR hdr; + BOOLEAN suspend; + BOOLEAN flush; +} tBTA_AV_API_STOP; + +/* data type for BTA_AV_API_DISCONNECT_EVT */ +typedef struct { + BT_HDR hdr; + BD_ADDR bd_addr; +} tBTA_AV_API_DISCNT; + +/* data type for BTA_AV_API_PROTECT_REQ_EVT */ +typedef struct { + BT_HDR hdr; + UINT8 *p_data; + UINT16 len; +} tBTA_AV_API_PROTECT_REQ; + +/* data type for BTA_AV_API_PROTECT_RSP_EVT */ +typedef struct { + BT_HDR hdr; + UINT8 *p_data; + UINT16 len; + UINT8 error_code; +} tBTA_AV_API_PROTECT_RSP; + +/* data type for BTA_AV_API_REMOTE_CMD_EVT */ +typedef struct { + BT_HDR hdr; + tAVRC_MSG_PASS msg; + UINT8 label; +} tBTA_AV_API_REMOTE_CMD; + +/* data type for BTA_AV_API_VENDOR_CMD_EVT and RSP */ +typedef struct { + BT_HDR hdr; + tAVRC_MSG_VENDOR msg; + UINT8 label; +} tBTA_AV_API_VENDOR; + +/* data type for BTA_AV_API_RC_OPEN_EVT */ +typedef struct { + BT_HDR hdr; +} tBTA_AV_API_OPEN_RC; + +/* data type for BTA_AV_API_RC_CLOSE_EVT */ +typedef struct { + BT_HDR hdr; +} tBTA_AV_API_CLOSE_RC; + +/* data type for BTA_AV_API_META_RSP_EVT */ +typedef struct { + BT_HDR hdr; + BOOLEAN is_rsp; + UINT8 label; + tBTA_AV_CODE rsp_code; + BT_HDR *p_pkt; +} tBTA_AV_API_META_RSP; + + +/* data type for BTA_AV_API_RECONFIG_EVT */ +typedef struct { + BT_HDR hdr; + UINT8 codec_info[AVDT_CODEC_SIZE]; /* codec configuration */ + BOOLEAN suspend; + UINT8 sep_info_idx; + UINT8 num_protect; + UINT8 p_protect_info[0]; +} tBTA_AV_API_RCFG; + +/* data type for BTA_AV_CI_SETCONFIG_OK_EVT and BTA_AV_CI_SETCONFIG_FAIL_EVT */ +typedef struct { + BT_HDR hdr; + tBTA_AV_HNDL hndl; + UINT8 err_code; + UINT8 category; + BOOLEAN recfg_needed; + UINT8 avdt_handle; /* local sep type for which this stream will be set up */ + UINT8 num_seid; + UINT8 p_seid[0]; +} tBTA_AV_CI_SETCONFIG; + +/* data type for all stream events from AVDTP */ +typedef struct { + BT_HDR hdr; + tAVDT_CFG cfg; /* configuration/capabilities parameters */ + tAVDT_CTRL msg; /* AVDTP callback message parameters */ + BD_ADDR bd_addr; /* bd address */ + UINT8 handle; + UINT8 avdt_event; + BOOLEAN initiator; /* TRUE, if local device initiates the SUSPEND */ +} tBTA_AV_STR_MSG; + +/* data type for BTA_AV_AVRC_MSG_EVT */ +typedef struct { + BT_HDR hdr; + tAVRC_MSG msg; + UINT8 handle; + UINT8 label; + UINT8 opcode; +} tBTA_AV_RC_MSG; + +/* data type for BTA_AV_AVRC_OPEN_EVT, BTA_AV_AVRC_CLOSE_EVT */ +typedef struct { + BT_HDR hdr; + BD_ADDR peer_addr; + UINT8 handle; +} tBTA_AV_RC_CONN_CHG; + +/* data type for BTA_AV_CONN_CHG_EVT */ +typedef struct { + BT_HDR hdr; + BD_ADDR peer_addr; + BOOLEAN is_up; +} tBTA_AV_CONN_CHG; + +/* data type for BTA_AV_ROLE_CHANGE_EVT */ +typedef struct { + BT_HDR hdr; + UINT8 new_role; + UINT8 hci_status; +} tBTA_AV_ROLE_RES; + +/* data type for BTA_AV_SDP_DISC_OK_EVT */ +typedef struct { + BT_HDR hdr; + UINT16 avdt_version; /* AVDTP protocol version */ +} tBTA_AV_SDP_RES; + +/* type for SEP control block */ +typedef struct { + UINT8 av_handle; /* AVDTP handle */ + tBTA_AV_CODEC codec_type; /* codec type */ + UINT8 tsep; /* SEP type of local SEP */ + tBTA_AV_DATA_CBACK *p_app_data_cback; /* Application callback for media packets */ +} tBTA_AV_SEP; + +/* data type for BTA_AV_API_SET_DELAY_VALUE_EVT */ +typedef struct { + BT_HDR hdr; + UINT16 delay_value; +} tBTA_AV_API_SET_DELAY_VALUE; + +/* data type for BTA_AV_API_GET_DELAY_VALUE_EVT */ +typedef struct { + BT_HDR hdr; +} tBTA_AV_API_GET_DELAY_VALUE; + +/* initiator/acceptor role for adaption */ +#define BTA_AV_ROLE_AD_INT 0x00 /* initiator */ +#define BTA_AV_ROLE_AD_ACP 0x01 /* acceptor */ + +/* initiator/acceptor signaling roles */ +#define BTA_AV_ROLE_START_ACP 0x00 +#define BTA_AV_ROLE_START_INT 0x10 /* do not change this value */ + +#define BTA_AV_ROLE_SUSPEND 0x20 /* suspending on start */ +#define BTA_AV_ROLE_SUSPEND_OPT 0x40 /* Suspend on Start option is set */ + +/* union of all event datatypes */ +typedef union { + BT_HDR hdr; + tBTA_AV_API_ENABLE api_enable; + tBTA_AV_API_REG api_reg; + tBTA_AV_API_OPEN api_open; + tBTA_AV_API_STOP api_stop; + tBTA_AV_API_DISCNT api_discnt; + tBTA_AV_API_PROTECT_REQ api_protect_req; + tBTA_AV_API_PROTECT_RSP api_protect_rsp; + tBTA_AV_API_REMOTE_CMD api_remote_cmd; + tBTA_AV_API_VENDOR api_vendor; + tBTA_AV_API_RCFG api_reconfig; + tBTA_AV_CI_SETCONFIG ci_setconfig; + tBTA_AV_STR_MSG str_msg; + tBTA_AV_RC_MSG rc_msg; + tBTA_AV_RC_CONN_CHG rc_conn_chg; + tBTA_AV_CONN_CHG conn_chg; + tBTA_AV_ROLE_RES role_res; + tBTA_AV_SDP_RES sdp_res; + tBTA_AV_API_META_RSP api_meta_rsp; + tBTA_AV_API_SET_DELAY_VALUE api_set_delay_vlaue; + tBTA_AV_API_GET_DELAY_VALUE api_get_delay_value; +} tBTA_AV_DATA; + +typedef void (tBTA_AV_VDP_DATA_ACT)(void *p_scb); + +typedef struct { + tBTA_AV_VDP_DATA_ACT *p_act; + UINT8 *p_frame; + UINT16 buf_size; + UINT32 len; + UINT32 offset; + UINT32 timestamp; +} tBTA_AV_VF_INFO; + +typedef union { + tBTA_AV_VF_INFO vdp; /* used for video channels only */ + tBTA_AV_API_OPEN open; /* used only before open and role switch + is needed on another AV channel */ +} tBTA_AV_Q_INFO; + +#define BTA_AV_Q_TAG_OPEN 0x01 /* after API_OPEN, before STR_OPENED */ +#define BTA_AV_Q_TAG_START 0x02 /* before start sending media packets */ +#define BTA_AV_Q_TAG_STREAM 0x03 /* during streaming */ + +#define BTA_AV_WAIT_ACP_CAPS_ON 0x01 /* retriving the peer capabilities */ +#define BTA_AV_WAIT_ACP_CAPS_STARTED 0x02 /* started while retriving peer capabilities */ +#define BTA_AV_WAIT_ROLE_SW_RES_OPEN 0x04 /* waiting for role switch result after API_OPEN, before STR_OPENED */ +#define BTA_AV_WAIT_ROLE_SW_RES_START 0x08 /* waiting for role switch result before streaming */ +#define BTA_AV_WAIT_ROLE_SW_STARTED 0x10 /* started while waiting for role switch result */ +#define BTA_AV_WAIT_ROLE_SW_RETRY 0x20 /* set when retry on timeout */ +#define BTA_AV_WAIT_CHECK_RC 0x40 /* set when the timer is used by role switch */ +#define BTA_AV_WAIT_ROLE_SW_FAILED 0x80 /* role switch failed */ + +#define BTA_AV_WAIT_ROLE_SW_BITS (BTA_AV_WAIT_ROLE_SW_RES_OPEN|BTA_AV_WAIT_ROLE_SW_RES_START|BTA_AV_WAIT_ROLE_SW_STARTED|BTA_AV_WAIT_ROLE_SW_RETRY) + +/* Bitmap for collision, coll_mask */ +#define BTA_AV_COLL_INC_TMR 0x01 /* Timer is running for incoming L2C connection */ +#define BTA_AV_COLL_API_CALLED 0x02 /* API open was called while incoming timer is running */ + +/* type for AV stream control block */ +typedef struct { + const tBTA_AV_ACT *p_act_tbl; /* the action table for stream state machine */ + const tBTA_AV_CO_FUNCTS *p_cos; /* the associated callout functions */ + tSDP_DISCOVERY_DB *p_disc_db; /* pointer to discovery database */ + tBTA_AV_SEP seps[BTA_AV_MAX_SEPS]; + tAVDT_CFG *p_cap; /* buffer used for get capabilities */ + list_t *a2d_list; /* used for audio channels only */ + tBTA_AV_Q_INFO q_info; + tAVDT_SEP_INFO sep_info[BTA_AV_NUM_SEPS]; /* stream discovery results */ + tAVDT_CFG cfg; /* local SEP configuration */ + TIMER_LIST_ENT timer; /* delay timer for AVRC CT */ + BD_ADDR peer_addr; /* peer BD address */ + UINT16 l2c_cid; /* L2CAP channel ID */ + UINT16 stream_mtu; /* MTU of stream */ + UINT16 avdt_version; /* the avdt version of peer device */ + tBTA_SEC sec_mask; /* security mask */ + tBTA_AV_CODEC codec_type; /* codec type */ + UINT8 media_type; /* Media type */ + BOOLEAN cong; /* TRUE if AVDTP congested */ + tBTA_AV_STATUS open_status; /* open failure status */ + tBTA_AV_CHNL chnl; /* the channel: audio/video */ + tBTA_AV_HNDL hndl; /* the handle: ((hdi + 1)|chnl) */ + UINT16 cur_psc_mask; /* Protocol service capabilities mask for current connection */ + UINT8 avdt_handle; /* AVDTP handle */ + UINT8 hdi; /* the index to SCB[] */ + UINT8 num_seps; /* number of seps returned by stream discovery */ + UINT8 num_disc_snks; /* number of discovered snks */ + UINT8 num_disc_srcs; /* number of discovered srcs */ + UINT8 sep_info_idx; /* current index into sep_info */ + UINT8 sep_idx; /* current index into local seps[] */ + UINT8 rcfg_idx; /* reconfig requested index into sep_info */ + UINT8 state; /* state machine state */ + UINT8 avdt_label; /* AVDTP label */ + UINT8 app_id; /* application id */ + UINT8 num_recfg; /* number of reconfigure sent */ + UINT8 role; + UINT8 l2c_bufs; /* the number of buffers queued to L2CAP */ + UINT8 rc_handle; /* connected AVRCP handle */ + BOOLEAN use_rc; /* TRUE if AVRCP is allowed */ + BOOLEAN started; /* TRUE if stream started */ + UINT8 co_started; /* non-zero, if stream started from call-out perspective */ + BOOLEAN recfg_sup; /* TRUE if the first attempt to reconfigure the stream was successfull, else False if command fails */ + BOOLEAN suspend_sup; /* TRUE if Suspend stream is supported, else FALSE if suspend command fails */ + BOOLEAN deregistring; /* TRUE if deregistering */ + BOOLEAN sco_suspend; /* TRUE if SUSPEND is issued automatically for SCO */ + UINT8 coll_mask; /* Mask to check incoming and outgoing collision */ + tBTA_AV_API_OPEN open_api; /* Saved OPEN api message */ + UINT8 wait; /* set 0x1, when getting Caps as ACP, set 0x2, when started */ + UINT8 q_tag; /* identify the associated q_info union member */ + BOOLEAN no_rtp_hdr; /* TRUE if add no RTP header*/ + UINT8 disc_rsn; /* disconenction reason */ + UINT16 uuid_int; /*intended UUID of Initiator to connect to */ +} tBTA_AV_SCB; + +#define BTA_AV_RC_ROLE_MASK 0x10 +#define BTA_AV_RC_ROLE_INT 0x00 +#define BTA_AV_RC_ROLE_ACP 0x10 + +#define BTA_AV_RC_CONN_MASK 0x20 + +/* type for AV RCP control block */ +/* index to this control block is the rc handle */ +typedef struct { + UINT8 status; + UINT8 handle; + UINT8 shdl; /* stream handle (hdi + 1) */ + UINT8 lidx; /* (index+1) to LCB */ + tBTA_AV_FEAT peer_features; /* peer features mask */ + UINT16 peer_ct_features; + UINT16 peer_tg_features; +} tBTA_AV_RCB; +#define BTA_AV_NUM_RCB (BTA_AV_NUM_STRS + 2) + +enum { + BTA_AV_LCB_FREE, + BTA_AV_LCB_FIND +}; + +/* type for AV ACL Link control block */ +typedef struct { + BD_ADDR addr; /* peer BD address */ + UINT8 conn_msk; /* handle mask of connected stream handle */ + UINT8 lidx; /* index + 1 */ +} tBTA_AV_LCB; + +/* type for stream state machine action functions */ +typedef void (*tBTA_AV_SACT)(tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data); + + +/* type for AV control block */ +typedef struct { + tBTA_AV_SCB *p_scb[BTA_AV_NUM_STRS]; /* stream control block */ + tSDP_DISCOVERY_DB *p_disc_db; /* pointer to discovery database */ + tBTA_AV_CBACK *p_cback; /* application callback function */ + tBTA_AV_RCB rcb[BTA_AV_NUM_RCB]; /* RCB control block */ + tBTA_AV_LCB lcb[BTA_AV_NUM_LINKS + 1]; /* link control block */ + TIMER_LIST_ENT sig_tmr; /* link timer */ + TIMER_LIST_ENT acp_sig_tmr; /* timer to monitor signalling when accepting */ + UINT32 sdp_a2d_handle; /* SDP record handle for audio src */ +#if (BTA_AV_SINK_INCLUDED == TRUE) + UINT32 sdp_a2d_snk_handle; /* SDP record handle for audio snk */ +#endif + UINT32 sdp_vdp_handle; /* SDP record handle for video src */ + tBTA_AV_FEAT features; /* features mask */ + tBTA_SEC sec_mask; /* security mask */ + tBTA_AV_HNDL handle; /* the handle for SDP activity */ + tBTA_AVRC_CO_FUNCTS *p_rc_cos; /* AVRCP call-out functions */ + BOOLEAN disabling; /* TRUE if api disabled called */ + UINT8 disc; /* (hdi+1) or (rc_handle|BTA_AV_CHNL_MSK) if p_disc_db is in use */ + UINT8 state; /* state machine state */ + UINT8 conn_rc; /* handle mask of connected RCP channels */ + UINT8 conn_audio; /* handle mask of connected audio channels */ + UINT8 conn_video; /* handle mask of connected video channels */ + UINT8 conn_lcb; /* index mask of used LCBs */ + UINT8 audio_open_cnt; /* number of connected audio channels */ + UINT8 reg_audio; /* handle mask of registered audio channels */ + UINT8 reg_video; /* handle mask of registered video channels */ + UINT8 rc_acp_handle; + UINT8 rc_acp_idx; /* (index + 1) to RCB */ + UINT8 rs_idx; /* (index + 1) to SCB for the one waiting for RS on open */ + BOOLEAN sco_occupied; /* TRUE if SCO is being used or call is in progress */ + UINT8 audio_streams; /* handle mask of streaming audio channels */ + UINT8 video_streams; /* handle mask of streaming video channels */ +} tBTA_AV_CB; + +/* type for dealing with SBC data frames and codec capabilities functions */ +typedef int (tBTA_AV_SBC_ACT)(void *p_src, void *p_dst, + UINT32 src_samples, UINT32 dst_samples, + UINT32 *p_ret); + +/* type for AV up sample control block */ +typedef struct { + INT32 cur_pos; /* current position */ + UINT32 src_sps; /* samples per second (source audio data) */ + UINT32 dst_sps; /* samples per second (converted audio data) */ + tBTA_AV_SBC_ACT *p_act; /* the action function to do the conversion */ + UINT16 bits; /* number of bits per pcm sample */ + UINT16 n_channels; /* number of channels (i.e. mono(1), stereo(2)...) */ + INT16 worker1; + INT16 worker2; + UINT8 div; +} tBTA_AV_SBC_UPS_CB; + +/***************************************************************************** +** Global data +*****************************************************************************/ +/* control block declaration up sample */ +#if BTA_DYNAMIC_MEMORY == TRUE +extern tBTA_AV_SBC_UPS_CB *bta_av_sbc_ups_cb_ptr; +#define bta_av_sbc_ups_cb (*bta_av_sbc_ups_cb_ptr) +#endif + +/* control block declaration */ +#if BTA_DYNAMIC_MEMORY == FALSE +extern tBTA_AV_CB bta_av_cb; +#else +extern tBTA_AV_CB *bta_av_cb_ptr; +#define bta_av_cb (*bta_av_cb_ptr) +#endif + +/* config struct */ +extern tBTA_AV_CFG *p_bta_av_cfg; + +extern const tBTA_AV_SACT bta_av_a2d_action[]; +extern const tBTA_AV_SACT bta_av_vdp_action[]; +extern tAVDT_CTRL_CBACK *const bta_av_dt_cback[]; +extern void bta_av_stream_data_cback(UINT8 handle, BT_HDR *p_pkt, UINT32 time_stamp, UINT8 m_pt); + +/***************************************************************************** +** Function prototypes +*****************************************************************************/ +/* utility functions */ +extern tBTA_AV_SCB *bta_av_hndl_to_scb(UINT16 handle); +extern BOOLEAN bta_av_chk_start(tBTA_AV_SCB *p_scb); +extern void bta_av_restore_switch (void); +extern UINT16 bta_av_chk_mtu(tBTA_AV_SCB *p_scb, UINT16 mtu); +extern void bta_av_conn_cback(UINT8 handle, BD_ADDR bd_addr, UINT8 event, tAVDT_CTRL *p_data); +extern UINT8 bta_av_rc_create(tBTA_AV_CB *p_cb, UINT8 role, UINT8 shdl, UINT8 lidx); +extern void bta_av_stream_chg(tBTA_AV_SCB *p_scb, BOOLEAN started); +extern BOOLEAN bta_av_is_scb_opening (tBTA_AV_SCB *p_scb); +extern BOOLEAN bta_av_is_scb_incoming (tBTA_AV_SCB *p_scb); +extern void bta_av_set_scb_sst_init (tBTA_AV_SCB *p_scb); +extern BOOLEAN bta_av_is_scb_init (tBTA_AV_SCB *p_scb); +extern void bta_av_set_scb_sst_incoming (tBTA_AV_SCB *p_scb); +extern tBTA_AV_LCB *bta_av_find_lcb(BD_ADDR addr, UINT8 op); + + +/* debug functions */ +extern char *bta_av_evt_code(UINT16 evt_code); +extern char *bta_av_action_code(UINT16 action_code); + +/* main functions */ +extern void bta_av_api_deregister(tBTA_AV_DATA *p_data); +extern void bta_av_dup_audio_buf(tBTA_AV_SCB *p_scb, BT_HDR *p_buf); +extern void bta_av_sm_execute(tBTA_AV_CB *p_cb, UINT16 event, tBTA_AV_DATA *p_data); +extern void bta_av_ssm_execute(tBTA_AV_SCB *p_scb, UINT16 event, tBTA_AV_DATA *p_data); +extern BOOLEAN bta_av_hdl_event(BT_HDR *p_msg); +extern BOOLEAN bta_av_switch_if_needed(tBTA_AV_SCB *p_scb); +extern BOOLEAN bta_av_link_role_ok(tBTA_AV_SCB *p_scb, UINT8 bits); +extern BOOLEAN bta_av_is_rcfg_sst(tBTA_AV_SCB *p_scb); + +/* nsm action functions */ +extern void bta_av_api_disconnect(tBTA_AV_DATA *p_data); +extern void bta_av_sig_chg(tBTA_AV_DATA *p_data); +extern void bta_av_sig_timer(tBTA_AV_DATA *p_data); +extern void bta_av_rc_disc_done(tBTA_AV_DATA *p_data); +extern void bta_av_rc_closed(tBTA_AV_DATA *p_data); +extern void bta_av_rc_disc(UINT8 disc); +extern void bta_av_conn_chg(tBTA_AV_DATA *p_data); +extern void bta_av_dereg_comp(tBTA_AV_DATA *p_data); + +/* sm action functions */ +extern void bta_av_disable (tBTA_AV_CB *p_cb, tBTA_AV_DATA *p_data); +extern void bta_av_rc_opened (tBTA_AV_CB *p_cb, tBTA_AV_DATA *p_data); +extern void bta_av_rc_remote_cmd (tBTA_AV_CB *p_cb, tBTA_AV_DATA *p_data); +extern void bta_av_rc_vendor_cmd (tBTA_AV_CB *p_cb, tBTA_AV_DATA *p_data); +extern void bta_av_rc_vendor_rsp (tBTA_AV_CB *p_cb, tBTA_AV_DATA *p_data); +extern void bta_av_rc_msg (tBTA_AV_CB *p_cb, tBTA_AV_DATA *p_data); +extern void bta_av_rc_close (tBTA_AV_CB *p_cb, tBTA_AV_DATA *p_data); +extern void bta_av_rc_meta_rsp (tBTA_AV_CB *p_cb, tBTA_AV_DATA *p_data); +extern void bta_av_rc_free_rsp (tBTA_AV_CB *p_cb, tBTA_AV_DATA *p_data); +extern void bta_av_rc_free_msg (tBTA_AV_CB *p_cb, tBTA_AV_DATA *p_data); + +extern tBTA_AV_RCB *bta_av_get_rcb_by_shdl(UINT8 shdl); +extern void bta_av_del_rc(tBTA_AV_RCB *p_rcb); + +/* ssm action functions */ +extern void bta_av_do_disc_a2d (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data); +extern void bta_av_cleanup (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data); +extern void bta_av_free_sdb (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data); +extern void bta_av_config_ind (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data); +extern void bta_av_disconnect_req (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data); +extern void bta_av_security_req (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data); +extern void bta_av_security_rsp (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data); +extern void bta_av_setconfig_rsp (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data); +extern void bta_av_str_opened (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data); +extern void bta_av_security_ind (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data); +extern void bta_av_security_cfm (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data); +extern void bta_av_do_close (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data); +extern void bta_av_connect_req (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data); +extern void bta_av_sdp_failed (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data); +extern void bta_av_disc_results (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data); +extern void bta_av_disc_res_as_acp (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data); +extern void bta_av_open_failed (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data); +extern void bta_av_getcap_results (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data); +extern void bta_av_setconfig_rej (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data); +extern void bta_av_discover_req (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data); +extern void bta_av_conn_failed (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data); +extern void bta_av_do_start (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data); +extern void bta_av_str_stopped (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data); +extern void bta_av_reconfig (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data); +extern void bta_av_data_path (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data); +extern void bta_av_start_ok (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data); +extern void bta_av_start_failed (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data); +extern void bta_av_str_closed (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data); +extern void bta_av_clr_cong (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data); +extern void bta_av_suspend_cfm (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data); +extern void bta_av_rcfg_str_ok (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data); +extern void bta_av_rcfg_failed (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data); +extern void bta_av_rcfg_connect (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data); +extern void bta_av_rcfg_discntd (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data); +extern void bta_av_suspend_cont (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data); +extern void bta_av_rcfg_cfm (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data); +extern void bta_av_rcfg_open (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data); +extern void bta_av_security_rej (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data); +extern void bta_av_open_rc (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data); +extern void bta_av_chk_2nd_start (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data); +extern void bta_av_save_caps (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data); +extern void bta_av_rej_conn (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data); +extern void bta_av_rej_conn (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data); +extern void bta_av_set_use_rc (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data); +extern void bta_av_cco_close (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data); +extern void bta_av_switch_role (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data); +extern void bta_av_role_res (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data); +extern void bta_av_delay_co (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data); +extern void bta_av_open_at_inc (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data); +extern void bta_av_open_fail_sdp (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data); +extern void bta_av_set_delay_value (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data); + +/* ssm action functions - vdp specific */ +extern void bta_av_do_disc_vdp (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data); +extern void bta_av_vdp_str_opened (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data); +extern void bta_av_reg_vdp (tAVDT_CS *p_cs, char *p_service_name, void *p_data); + +#endif ///BTA_AV_INCLUDED == TRUE + +#endif /* BTA_AV_INT_H */ diff --git a/lib/bt/host/bluedroid/bta/dm/bta_dm_act.c b/lib/bt/host/bluedroid/bta/dm/bta_dm_act.c new file mode 100644 index 00000000..f1eb9a12 --- /dev/null +++ b/lib/bt/host/bluedroid/bta/dm/bta_dm_act.c @@ -0,0 +1,6747 @@ +/****************************************************************************** + * + * Copyright (C) 2003-2014 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains the action functions for device manager state + * machine. + * + ******************************************************************************/ + +#include "common/bt_target.h" +#include "stack/bt_types.h" +#include "bta/bta_sys.h" +#include "bta/bta_api.h" +#include "bta_dm_int.h" +#include "bta/bta_dm_co.h" +#include "bta/bta_gattc_co.h" +#include "stack/btm_api.h" +#include "btm_int.h" +#include "stack/btu.h" +#include "stack/sdp_api.h" +#include "stack/l2c_api.h" +#include "bta/utl.h" +#include "stack/gap_api.h" /* For GAP_BleReadPeerPrefConnParams */ +#include +#include "device/controller.h" + +#define LOG_TAG "bt_bta_dm" +// #include "osi/include/log.h" + +#if (GAP_INCLUDED == TRUE) +#include "stack/gap_api.h" +#endif +#if (BT_CONTROLLER_INCLUDED == TRUE) +#include "esp_bt.h" +#endif + +static void bta_dm_inq_results_cb (tBTM_INQ_RESULTS *p_inq, UINT8 *p_eir); +static void bta_dm_inq_cmpl_cb (void *p_result); +static void bta_dm_service_search_remname_cback (BD_ADDR bd_addr, DEV_CLASS dc, BD_NAME bd_name); +static void bta_dm_remname_cback (tBTM_REMOTE_DEV_NAME *p_remote_name); +#if (SDP_INCLUDED == TRUE) +static void bta_dm_find_services ( BD_ADDR bd_addr); +#endif ///SDP_INCLUDED == TRUE +static void bta_dm_discover_next_device(void); +#if (SDP_INCLUDED == TRUE) +static void bta_dm_sdp_callback (UINT16 sdp_status); +#endif ///SDP_INCLUDED == TRUE +#if (SMP_INCLUDED == TRUE) +static UINT8 bta_dm_authorize_cback (BD_ADDR bd_addr, DEV_CLASS dev_class, BD_NAME bd_name, UINT8 *service_name, UINT8 service_id, BOOLEAN is_originator); +#if (CLASSIC_BT_INCLUDED == TRUE) +static UINT8 bta_dm_pin_cback (BD_ADDR bd_addr, DEV_CLASS dev_class, BD_NAME bd_name, BOOLEAN min_16_digit); +#endif /// CLASSIC_BT_INCLUDED == TRUE +static UINT8 bta_dm_new_link_key_cback(BD_ADDR bd_addr, DEV_CLASS dev_class, BD_NAME bd_name, LINK_KEY key, UINT8 key_type, BOOLEAN sc_support); +static UINT8 bta_dm_authentication_complete_cback(BD_ADDR bd_addr, DEV_CLASS dev_class, BD_NAME bd_name, int result); +#endif ///SMP_INCLUDED == TRUE +static void bta_dm_local_name_cback(const BD_ADDR bd_addr); +static BOOLEAN bta_dm_check_av(UINT16 event); +static void bta_dm_bl_change_cback (tBTM_BL_EVENT_DATA *p_data); + + +static void bta_dm_acl_link_stat_cback(tBTM_ACL_LINK_STAT_EVENT_DATA *p_data); +static void bta_dm_policy_cback(tBTA_SYS_CONN_STATUS status, UINT8 id, UINT8 app_id, BD_ADDR peer_addr); + +#if (CLASSIC_BT_INCLUDED == TRUE) +static void bta_dm_encryption_change_cback(BD_ADDR bd_addr, UINT8 enc_mode); +static UINT8 bta_dm_sp_cback (tBTM_SP_EVT event, tBTM_SP_EVT_DATA *p_data); +/* Extended Inquiry Response */ +static void bta_dm_set_eir (char *local_name); +#endif +#if (SDP_INCLUDED == TRUE) +static void bta_dm_eir_search_services( tBTM_INQ_RESULTS *p_result, + tBTA_SERVICE_MASK *p_services_to_search, + tBTA_SERVICE_MASK *p_services_found); +#endif ///SDP_INCLUDED == TRUE +static void bta_dm_search_timer_cback (TIMER_LIST_ENT *p_tle); +static void bta_dm_disable_conn_down_timer_cback (TIMER_LIST_ENT *p_tle); +static void bta_dm_rm_cback(tBTA_SYS_CONN_STATUS status, UINT8 id, UINT8 app_id, BD_ADDR peer_addr); +static void bta_dm_adjust_roles(BOOLEAN delay_role_switch); +#if (SDP_INCLUDED == TRUE || SMP_INCLUDED == TRUE) +static char *bta_dm_get_remname(void); +#endif ///SDP_INCLUDED == TRUE || SMP_INCLUDED == TRUE +#if (SMP_INCLUDED == TRUE) +static void bta_dm_bond_cancel_complete_cback(tBTM_STATUS result); +#endif ///SMP_INCLUDED == TRUE +#if (SDP_INCLUDED == TRUE) +static BOOLEAN bta_dm_read_remote_device_name (BD_ADDR bd_addr, tBT_TRANSPORT transport); +static void bta_dm_discover_device(BD_ADDR remote_bd_addr); +#endif ///SDP_INCLUDED == TRUE +static void bta_dm_sys_hw_cback( tBTA_SYS_HW_EVT status ); +static void bta_dm_disable_search_and_disc(void); + +#if ((defined BLE_INCLUDED) && (BLE_INCLUDED == TRUE)) +#if ((defined SMP_INCLUDED) && (SMP_INCLUDED == TRUE)) +static UINT8 bta_dm_ble_smp_cback (tBTM_LE_EVT event, BD_ADDR bda, tBTM_LE_EVT_DATA *p_data); +static void bta_dm_ble_id_key_cback (UINT8 key_type, tBTM_BLE_LOCAL_KEYS *p_key); +#endif ///SMP_INCLUDED == TRUE +#if ((defined BTA_GATT_INCLUDED) && (BTA_GATT_INCLUDED == TRUE) && SDP_INCLUDED == TRUE) +#if (GATTC_INCLUDED == TRUE) +static void bta_dm_gattc_register(void); +static void btm_dm_start_gatt_discovery(BD_ADDR bd_addr); +static void bta_dm_cancel_gatt_discovery(BD_ADDR bd_addr); +static void bta_dm_gattc_callback(tBTA_GATTC_EVT event, tBTA_GATTC *p_data); +#endif // (GATTC_INCLUDED == TRUE) +extern tBTA_DM_CONTRL_STATE bta_dm_pm_obtain_controller_state(void); +#endif + +#if BLE_VND_INCLUDED == TRUE +static void bta_dm_ctrl_features_rd_cmpl_cback(tBTM_STATUS result); +#endif + +#ifndef BTA_DM_BLE_ADV_CHNL_MAP +#define BTA_DM_BLE_ADV_CHNL_MAP (BTM_BLE_ADV_CHNL_37|BTM_BLE_ADV_CHNL_38|BTM_BLE_ADV_CHNL_39) +#endif +#endif +#if (SMP_INCLUDED == TRUE) +static BOOLEAN bta_dm_remove_sec_dev_entry(BD_ADDR remote_bd_addr); +#endif ///SMP_INCLUDED == TRUE +#if (BLE_INCLUDED == TRUE) +static void bta_dm_observe_results_cb(tBTM_INQ_RESULTS *p_inq, UINT8 *p_eir); +static void bta_dm_observe_cmpl_cb(void *p_result); +static void bta_dm_observe_discard_cb (uint32_t num_dis); +#endif ///BLE_INCLUDED == TRUE + +static void bta_dm_delay_role_switch_cback(TIMER_LIST_ENT *p_tle); +extern void sdpu_uuid16_to_uuid128(UINT16 uuid16, UINT8 *p_uuid128); +static void bta_dm_disable_timer_cback(TIMER_LIST_ENT *p_tle); + +const UINT16 bta_service_id_to_uuid_lkup_tbl [BTA_MAX_SERVICE_ID] = { + UUID_SERVCLASS_PNP_INFORMATION, /* Reserved */ + UUID_SERVCLASS_SERIAL_PORT, /* BTA_SPP_SERVICE_ID */ + UUID_SERVCLASS_DIALUP_NETWORKING, /* BTA_DUN_SERVICE_ID */ + UUID_SERVCLASS_AUDIO_SOURCE, /* BTA_A2DP_SOURCE_SERVICE_ID */ + UUID_SERVCLASS_LAN_ACCESS_USING_PPP, /* BTA_LAP_SERVICE_ID */ + UUID_SERVCLASS_HEADSET, /* BTA_HSP_HS_SERVICE_ID */ + UUID_SERVCLASS_HF_HANDSFREE, /* BTA_HFP_HS_SERVICE_ID */ + UUID_SERVCLASS_OBEX_OBJECT_PUSH, /* BTA_OPP_SERVICE_ID */ + UUID_SERVCLASS_OBEX_FILE_TRANSFER, /* BTA_FTP_SERVICE_ID */ + UUID_SERVCLASS_CORDLESS_TELEPHONY, /* BTA_CTP_SERVICE_ID */ + UUID_SERVCLASS_INTERCOM, /* BTA_ICP_SERVICE_ID */ + UUID_SERVCLASS_IRMC_SYNC, /* BTA_SYNC_SERVICE_ID */ + UUID_SERVCLASS_DIRECT_PRINTING, /* BTA_BPP_SERVICE_ID */ + UUID_SERVCLASS_IMAGING_RESPONDER, /* BTA_BIP_SERVICE_ID */ + UUID_SERVCLASS_PANU, /* BTA_PANU_SERVICE_ID */ + UUID_SERVCLASS_NAP, /* BTA_NAP_SERVICE_ID */ + UUID_SERVCLASS_GN, /* BTA_GN_SERVICE_ID */ + UUID_SERVCLASS_SAP, /* BTA_SAP_SERVICE_ID */ + UUID_SERVCLASS_AUDIO_SINK, /* BTA_A2DP_SERVICE_ID */ + UUID_SERVCLASS_AV_REMOTE_CONTROL, /* BTA_AVRCP_SERVICE_ID */ + UUID_SERVCLASS_HUMAN_INTERFACE, /* BTA_HID_SERVICE_ID */ + UUID_SERVCLASS_VIDEO_SINK, /* BTA_VDP_SERVICE_ID */ + UUID_SERVCLASS_PBAP_PSE, /* BTA_PBAP_SERVICE_ID */ + UUID_SERVCLASS_HEADSET_AUDIO_GATEWAY, /* BTA_HSP_SERVICE_ID */ + UUID_SERVCLASS_AG_HANDSFREE, /* BTA_HFP_SERVICE_ID */ + UUID_SERVCLASS_MESSAGE_ACCESS, /* BTA_MAP_SERVICE_ID */ + UUID_SERVCLASS_MESSAGE_NOTIFICATION, /* BTA_MN_SERVICE_ID */ + UUID_SERVCLASS_HDP_PROFILE, /* BTA_HDP_SERVICE_ID */ + UUID_SERVCLASS_PBAP_PCE /* BTA_PCE_SERVICE_ID */ +#if BLE_INCLUDED && BTA_GATT_INCLUDED + , UUID_PROTOCOL_ATT /* BTA_GATT_SERVICE_ID */ +#endif +}; + +/* + * NOTE : The number of element in bta_service_id_to_btm_srv_id_lkup_tbl should be matching with + * the value BTA_MAX_SERVICE_ID in bta/bta_api.h + * + * i.e., If you add new Service ID for BTA, the correct security ID of the new service + * from Security service definitions (stack/btm_api.h) should be added to this lookup table. + */ +const UINT32 bta_service_id_to_btm_srv_id_lkup_tbl [BTA_MAX_SERVICE_ID] = { + 0, /* Reserved */ + BTM_SEC_SERVICE_SERIAL_PORT, /* BTA_SPP_SERVICE_ID */ + BTM_SEC_SERVICE_DUN, /* BTA_DUN_SERVICE_ID */ + BTM_SEC_SERVICE_AVDTP, /* BTA_AUDIO_SOURCE_SERVICE_ID */ + BTM_SEC_SERVICE_LAN_ACCESS, /* BTA_LAP_SERVICE_ID */ + BTM_SEC_SERVICE_HEADSET_AG, /* BTA_HSP_SERVICE_ID */ + BTM_SEC_SERVICE_AG_HANDSFREE, /* BTA_HFP_SERVICE_ID */ + BTM_SEC_SERVICE_OBEX, /* BTA_OPP_SERVICE_ID */ + BTM_SEC_SERVICE_OBEX_FTP, /* BTA_FTP_SERVICE_ID */ + BTM_SEC_SERVICE_CORDLESS, /* BTA_CTP_SERVICE_ID */ + BTM_SEC_SERVICE_INTERCOM, /* BTA_ICP_SERVICE_ID */ + BTM_SEC_SERVICE_IRMC_SYNC, /* BTA_SYNC_SERVICE_ID */ + BTM_SEC_SERVICE_BPP_JOB, /* BTA_BPP_SERVICE_ID */ + BTM_SEC_SERVICE_BIP, /* BTA_BIP_SERVICE_ID */ + BTM_SEC_SERVICE_BNEP_PANU, /* BTA_PANU_SERVICE_ID */ + BTM_SEC_SERVICE_BNEP_NAP, /* BTA_NAP_SERVICE_ID */ + BTM_SEC_SERVICE_BNEP_GN, /* BTA_GN_SERVICE_ID */ + BTM_SEC_SERVICE_SAP, /* BTA_SAP_SERVICE_ID */ + BTM_SEC_SERVICE_AVDTP, /* BTA_A2DP_SERVICE_ID */ + BTM_SEC_SERVICE_AVCTP, /* BTA_AVRCP_SERVICE_ID */ + BTM_SEC_SERVICE_HIDH_SEC_CTRL, /* BTA_HID_SERVICE_ID */ + BTM_SEC_SERVICE_AVDTP, /* BTA_VDP_SERVICE_ID */ + BTM_SEC_SERVICE_PBAP, /* BTA_PBAP_SERVICE_ID */ + BTM_SEC_SERVICE_HEADSET, /* BTA_HSP_HS_SERVICE_ID */ + BTM_SEC_SERVICE_HF_HANDSFREE, /* BTA_HFP_HS_SERVICE_ID */ + BTM_SEC_SERVICE_MAP, /* BTA_MAP_SERVICE_ID */ + BTM_SEC_SERVICE_MAP, /* BTA_MN_SERVICE_ID */ + BTM_SEC_SERVICE_HDP_SNK, /* BTA_HDP_SERVICE_ID */ + BTM_SEC_SERVICE_PBAP /* BTA_PCE_SERVICE_ID */ +#if BLE_INCLUDED && BTA_GATT_INCLUDED + , BTM_SEC_SERVICE_ATT /* BTA_GATT_SERVICE_ID */ +#endif + +}; + +/* bta security callback */ +#if (SMP_INCLUDED == TRUE) +const tBTM_APPL_INFO bta_security = { + &bta_dm_authorize_cback, +#if (CLASSIC_BT_INCLUDED == TRUE) + &bta_dm_pin_cback, +#else + NULL, +#endif + &bta_dm_new_link_key_cback, + &bta_dm_authentication_complete_cback, + &bta_dm_bond_cancel_complete_cback, +#if (CLASSIC_BT_INCLUDED == TRUE) + &bta_dm_encryption_change_cback, + &bta_dm_sp_cback, +#else + NULL, + NULL, +#endif +#if BLE_INCLUDED == TRUE + &bta_dm_ble_smp_cback, + &bta_dm_ble_id_key_cback, +#endif ///BLE_INCLUDED == TRUE + +}; +#endif ///SMP_INCLUDED == TRUE + +#if (SDP_INCLUDED == TRUE) +#if BTA_DYNAMIC_MEMORY == FALSE +UINT8 g_disc_raw_data_buf[MAX_DISC_RAW_DATA_BUF]; +#else +UINT8 *g_disc_raw_data_buf; +#endif +#endif ///SDP_INCLUDED == TRUE + +/******************************************************************************* +** +** Function bta_dm_enable +** +** Description Initialises the BT device manager +** +** +** Returns void +** +*******************************************************************************/ +void bta_dm_enable(tBTA_DM_MSG *p_data) +{ + tBTA_SYS_HW_MSG *sys_enable_event; + tBTA_DM_ENABLE enable_event; + + /* if already in use, return an error */ + if ( bta_dm_cb.is_bta_dm_active == TRUE ) { + APPL_TRACE_WARNING("%s Device already started by another application", __func__); + memset(&enable_event, 0, sizeof(tBTA_DM_ENABLE)); + enable_event.status = BTA_FAILURE; + if (p_data->enable.p_sec_cback != NULL) { + p_data->enable.p_sec_cback(BTA_DM_ENABLE_EVT, (tBTA_DM_SEC *)&enable_event); + } + return; + } + + /* first, register our callback to SYS HW manager */ + bta_sys_hw_register( BTA_SYS_HW_BLUETOOTH, bta_dm_sys_hw_cback ); + + /* make sure security callback is saved - if no callback, do not erase the previous one, + it could be an error recovery mechanism */ + if ( p_data->enable.p_sec_cback != NULL ) { + bta_dm_cb.p_sec_cback = p_data->enable.p_sec_cback; + } + /* notify BTA DM is now active */ + bta_dm_cb.is_bta_dm_active = TRUE; + + /* send a message to BTA SYS */ + if ((sys_enable_event = (tBTA_SYS_HW_MSG *) osi_malloc(sizeof(tBTA_SYS_HW_MSG))) != NULL) { + sys_enable_event->hdr.event = BTA_SYS_API_ENABLE_EVT; + sys_enable_event->hw_module = BTA_SYS_HW_BLUETOOTH; + + bta_sys_sendmsg(sys_enable_event); + } +} + +/******************************************************************************* + * + * Function bta_dm_init_cb + * + * Description Initializes the bta_dm_cb control block + * + * + * Returns void + * + ******************************************************************************/ +void bta_dm_init_cb(void) +{ + memset(&bta_dm_cb, 0, sizeof(bta_dm_cb)); +} + +/******************************************************************************* + * + * Function bta_dm_deinit_cb + * + * Description De-initializes the bta_dm_cb control block + * + * + * Returns void + * + ******************************************************************************/ +void bta_dm_deinit_cb(void) +{ + bta_sys_free_timer(&bta_dm_cb.disable_timer); +#if ( BTA_EIR_CANNED_UUID_LIST != TRUE ) + bta_sys_free_timer(&bta_dm_cb.app_ready_timer); +#endif +#if BTM_SSR_INCLUDED == TRUE + for (size_t i = 0; i < BTA_DM_NUM_PM_TIMER; i++) { + for (size_t j = 0; j < BTA_DM_PM_MODE_TIMER_MAX; j++) { + bta_sys_free_timer(&bta_dm_cb.pm_timer[i].timer[j]); + } + } +#endif + memset(&bta_dm_cb, 0, sizeof(bta_dm_cb)); +#if BTA_DYNAMIC_MEMORY + xSemaphoreGive(deinit_semaphore); +#endif /* #if BTA_DYNAMIC_MEMORY */ +} + +/******************************************************************************* + * + * Function bta_dm_eir_cfg_init + * + * Description Initializes the p_bta_dm_eir_cfg + * + * + * Returns void + * + ******************************************************************************/ +static void bta_dm_eir_cfg_init(void) +{ + p_bta_dm_eir_cfg->bta_dm_eir_fec_required = BTM_EIR_DEFAULT_FEC_REQUIRED; + p_bta_dm_eir_cfg->bta_dm_eir_min_name_len = 50; + + p_bta_dm_eir_cfg->bta_dm_eir_included_uuid = TRUE; + p_bta_dm_eir_cfg->bta_dm_eir_included_tx_power = FALSE; + p_bta_dm_eir_cfg->bta_dm_eir_inq_tx_power = 3; + p_bta_dm_eir_cfg->bta_dm_eir_flags = 0; + + p_bta_dm_eir_cfg->bta_dm_eir_manufac_spec_len = 0; + p_bta_dm_eir_cfg->bta_dm_eir_manufac_spec = NULL; + + p_bta_dm_eir_cfg->bta_dm_eir_url_len = 0; + p_bta_dm_eir_cfg->bta_dm_eir_url = NULL; +} + +/******************************************************************************* + * + * Function bta_dm_eir_cfg_deinit + * + * Description De-initializes the p_bta_dm_eir_cfg + * + * + * Returns void + * + ******************************************************************************/ +static void bta_dm_eir_cfg_deinit(void) +{ + p_bta_dm_eir_cfg->bta_dm_eir_manufac_spec_len = 0; + if (p_bta_dm_eir_cfg->bta_dm_eir_manufac_spec) { + osi_free(p_bta_dm_eir_cfg->bta_dm_eir_manufac_spec); + p_bta_dm_eir_cfg->bta_dm_eir_manufac_spec = NULL; + } + + p_bta_dm_eir_cfg->bta_dm_eir_url_len = 0; + if (p_bta_dm_eir_cfg->bta_dm_eir_url) { + osi_free(p_bta_dm_eir_cfg->bta_dm_eir_url); + p_bta_dm_eir_cfg->bta_dm_eir_url = NULL; + } +} + +/******************************************************************************* +** +** Function bta_dm_sys_hw_cback +** +** Description callback register to SYS to get HW status updates +** +** +** Returns void +** +*******************************************************************************/ +static void bta_dm_sys_hw_cback( tBTA_SYS_HW_EVT status ) +{ + DEV_CLASS dev_class; + tBTA_DM_SEC_CBACK *temp_cback; +#if BLE_INCLUDED == TRUE + UINT8 key_mask = 0; + BT_OCTET16 er; + tBTA_BLE_LOCAL_ID_KEYS id_key; +#endif + + APPL_TRACE_DEBUG("%s with event: %i", __func__, status); + + /* On H/W error evt, report to the registered DM application callback */ + if (status == BTA_SYS_HW_ERROR_EVT) { + if ( bta_dm_cb.p_sec_cback != NULL ) { + bta_dm_cb.p_sec_cback(BTA_DM_HW_ERROR_EVT, NULL); + } + return; + } + + if ( status == BTA_SYS_HW_OFF_EVT ) { + if ( bta_dm_cb.p_sec_cback != NULL ) { + bta_dm_cb.p_sec_cback(BTA_DM_DISABLE_EVT, NULL); + } + + /* reinitialize the control block */ + bta_dm_deinit_cb(); + + /* reinitialize the Extended Inquiry Response */ + bta_dm_eir_cfg_deinit(); + + bta_sys_free_timer(&bta_dm_search_cb.search_timer); +#if ((defined BLE_INCLUDED) && (BLE_INCLUDED == TRUE)) +#if ((defined BTA_GATT_INCLUDED) && (BTA_GATT_INCLUDED == TRUE) && SDP_INCLUDED == TRUE) + bta_sys_free_timer(&bta_dm_search_cb.gatt_close_timer); +#endif +#endif + memset(&bta_dm_search_cb, 0x00, sizeof(bta_dm_search_cb)); + + /* unregister from SYS */ + bta_sys_hw_unregister( BTA_SYS_HW_BLUETOOTH ); + /* notify BTA DM is now unactive */ + bta_dm_cb.is_bta_dm_active = FALSE; +#if (defined BLE_INCLUDED && BLE_INCLUDED == TRUE) +#if (GATTC_INCLUDED == TRUE && GATTC_CACHE_NVS == TRUE) + /* clear the gattc cache address list */ + bta_gattc_co_cache_addr_deinit(); +#endif +#endif + } else if ( status == BTA_SYS_HW_ON_EVT ) { + /* FIXME: We should not unregister as the SYS shall invoke this callback on a H/W error. + * We need to revisit when this platform has more than one BLuetooth H/W chip */ + //bta_sys_hw_unregister( BTA_SYS_HW_BLUETOOTH); + + /* save security callback */ + temp_cback = bta_dm_cb.p_sec_cback; + /* make sure the control block is properly initialized */ + bta_dm_init_cb(); + + /* make sure the Extended Inquiry Response is properly initialized */ + bta_dm_eir_cfg_init(); + + /* and retrieve the callback */ + bta_dm_cb.p_sec_cback = temp_cback; + bta_dm_cb.is_bta_dm_active = TRUE; + + bta_sys_free_timer(&bta_dm_search_cb.search_timer); +#if ((defined BLE_INCLUDED) && (BLE_INCLUDED == TRUE)) +#if ((defined BTA_GATT_INCLUDED) && (BTA_GATT_INCLUDED == TRUE) && SDP_INCLUDED == TRUE) + bta_sys_free_timer(&bta_dm_search_cb.gatt_close_timer); +#endif +#endif + /* hw is ready, go on with BTA DM initialization */ + memset(&bta_dm_search_cb, 0x00, sizeof(bta_dm_search_cb)); + memset(&bta_dm_conn_srvcs, 0x00, sizeof(bta_dm_conn_srvcs)); + memset(&bta_dm_di_cb, 0, sizeof(tBTA_DM_DI_CB)); + memcpy(dev_class, p_bta_dm_cfg->dev_class, sizeof(dev_class)); +#if CLASSIC_BT_INCLUDED + BTM_SetDeviceClass (dev_class); +#endif + +#if (defined BLE_INCLUDED && BLE_INCLUDED == TRUE) +#if (GATTC_INCLUDED == TRUE && GATTC_CACHE_NVS == TRUE) + // load the gattc cache address list + bta_gattc_co_cache_addr_init(); +#endif /* #if (GATTC_INCLUDED = TRUE) */ + /* load BLE local information: ID keys, ER if available */ + bta_dm_co_ble_load_local_keys(&key_mask, er, &id_key); + + if (key_mask & BTA_BLE_LOCAL_KEY_TYPE_ER) { + BTM_BleLoadLocalKeys(BTA_BLE_LOCAL_KEY_TYPE_ER, (tBTM_BLE_LOCAL_KEYS *)&er); + } + if (key_mask & BTA_BLE_LOCAL_KEY_TYPE_ID) { + BTM_BleLoadLocalKeys(BTA_BLE_LOCAL_KEY_TYPE_ID, (tBTM_BLE_LOCAL_KEYS *)&id_key); + } +#if ((defined BTA_GATT_INCLUDED) && (BTA_GATT_INCLUDED == TRUE) && SDP_INCLUDED == TRUE) + bta_dm_search_cb.conn_id = BTA_GATT_INVALID_CONN_ID; +#endif +#endif +#if (SMP_INCLUDED == TRUE) + BTM_SecRegister((tBTM_APPL_INFO *)&bta_security); +#endif ///SMP_INCLUDED == TRUE + BTM_SetDefaultLinkSuperTout(p_bta_dm_cfg->link_timeout); +#if CLASSIC_BT_INCLUDED + BTM_WritePageTimeout(p_bta_dm_cfg->page_timeout, NULL); + bta_dm_cb.cur_policy = p_bta_dm_cfg->policy_settings; + BTM_SetDefaultLinkPolicy(bta_dm_cb.cur_policy); +#endif + BTM_RegBusyLevelNotif (bta_dm_bl_change_cback, NULL, BTM_BL_UPDATE_MASK | BTM_BL_ROLE_CHG_MASK); + BTM_RegAclLinkStatNotif (bta_dm_acl_link_stat_cback); + +#if BLE_VND_INCLUDED == TRUE + BTM_BleReadControllerFeatures (bta_dm_ctrl_features_rd_cmpl_cback); +#endif + + /* Earlier, we used to invoke BTM_ReadLocalAddr which was just copying the bd_addr + from the control block and invoking the callback which was sending the DM_ENABLE_EVT. + But then we have a few HCI commands being invoked above which were still in progress + when the ENABLE_EVT was sent. So modified this to fetch the local name which forces + the DM_ENABLE_EVT to be sent only after all the init steps are complete */ + BTM_ReadLocalDeviceNameFromController((tBTM_CMPL_CB *)bta_dm_local_name_cback); + + bta_sys_rm_register((tBTA_SYS_CONN_CBACK *)bta_dm_rm_cback); + +#if (BTA_DM_PM_INCLUDED == TRUE) + /* initialize bluetooth low power manager */ + bta_dm_init_pm(); +#endif /* #if (BTA_DM_PM_INCLUDED == TRUE) */ + + bta_sys_policy_register((tBTA_SYS_CONN_CBACK *)bta_dm_policy_cback); + +#if (BLE_INCLUDED == TRUE && BTA_GATT_INCLUDED == TRUE && SDP_INCLUDED == TRUE) && (GATTC_INCLUDED == TRUE) + bta_dm_gattc_register(); +#endif + + } else { + APPL_TRACE_DEBUG(" --- ignored event"); + } + +} + + +/******************************************************************************* +** +** Function bta_dm_disable +** +** Description Disables the BT device manager +** +** +** Returns void +** +*******************************************************************************/ +void bta_dm_disable (tBTA_DM_MSG *p_data) +{ + UNUSED(p_data); + + /* Set l2cap idle timeout to 0 (so BTE immediately disconnects ACL link after last channel is closed) */ + L2CA_SetIdleTimeoutByBdAddr((UINT8 *)BT_BD_ANY, 0, BT_TRANSPORT_BR_EDR); + L2CA_SetIdleTimeoutByBdAddr((UINT8 *)BT_BD_ANY, 0, BT_TRANSPORT_LE); + + /* disable all active subsystems */ + bta_sys_disable(BTA_SYS_HW_BLUETOOTH); +#if CLASSIC_BT_INCLUDED + BTM_SetDiscoverability(BTM_NON_DISCOVERABLE, 0, 0); + BTM_SetConnectability(BTM_NON_CONNECTABLE, 0, 0); +#endif // #if CLASSIC_BT_INCLUDED + +#if (BTA_DM_PM_INCLUDED == TRUE) + bta_dm_disable_pm(); +#endif /* #if (BTA_DM_PM_INCLUDED == TRUE) */ + + bta_dm_disable_search_and_disc(); + bta_dm_cb.disabling = TRUE; + +#if BLE_INCLUDED == TRUE + /* reset scan activity status*/ + btm_cb.ble_ctr_cb.scan_activity = 0; + + /* reset advertising activity status*/ + btm_cb.ble_ctr_cb.inq_var.state = 0; +#endif + +#if BLE_INCLUDED == TRUE && BTA_GATT_INCLUDED == TRUE + BTM_BleClearBgConnDev(); +#endif + + if (BTM_GetNumAclLinks() == 0) { +#if (defined(BTA_DISABLE_DELAY) && BTA_DISABLE_DELAY > 0) + /* If BTA_DISABLE_DELAY is defined and greater than zero, then delay the shutdown by + * BTA_DISABLE_DELAY milliseconds + */ + APPL_TRACE_WARNING("%s BTA_DISABLE_DELAY set to %d ms", + __FUNCTION__, BTA_DISABLE_DELAY); + bta_sys_stop_timer(&bta_dm_cb.disable_timer); + bta_dm_cb.disable_timer.p_cback = (TIMER_CBACK *)&bta_dm_disable_conn_down_timer_cback; + bta_sys_start_timer(&bta_dm_cb.disable_timer, 0, BTA_DISABLE_DELAY); +#else + bta_dm_disable_conn_down_timer_cback(NULL); +#endif + } else { + bta_dm_cb.disable_timer.p_cback = (TIMER_CBACK *)&bta_dm_disable_timer_cback; + bta_dm_cb.disable_timer.param = 0; + bta_sys_start_timer(&bta_dm_cb.disable_timer, 0, 5000); + } + +#if BLE_INCLUDED == TRUE && BLE_PRIVACY_SPT == TRUE + btm_ble_resolving_list_cleanup (); //by TH, because cmn_ble_vsc_cb.max_filter has something mistake as btm_ble_adv_filter_cleanup +#endif + +} + +/******************************************************************************* +** +** Function bta_dm_disable_timer_cback +** +** Description Called if the disable timer expires +** Used to close ACL connections which are still active +** +** +** +** Returns void +** +*******************************************************************************/ +static void bta_dm_disable_timer_cback (TIMER_LIST_ENT *p_tle) +{ + UNUSED(p_tle); + UINT8 i; + tBT_TRANSPORT transport = BT_TRANSPORT_BR_EDR; + BOOLEAN trigger_disc = FALSE; + + + APPL_TRACE_EVENT(" bta_dm_disable_timer_cback trial %d ", p_tle->param); + + if (BTM_GetNumAclLinks() && p_tle->param == 0) { + for (i = 0; i < bta_dm_cb.device_list.count; i++) { +#if (BLE_INCLUDED == TRUE) + transport = bta_dm_cb.device_list.peer_device[i].transport; +#endif + btm_remove_acl(bta_dm_cb.device_list.peer_device[i].peer_bdaddr, transport); + trigger_disc = TRUE; + } + + /* Retrigger disable timer in case ACL disconnect failed, DISABLE_EVT still need + to be sent out to avoid jave layer disable timeout */ + if (trigger_disc) { + bta_dm_cb.disable_timer.p_cback = (TIMER_CBACK *)&bta_dm_disable_timer_cback; + bta_dm_cb.disable_timer.param = 1; + bta_sys_start_timer(&bta_dm_cb.disable_timer, 0, 1500); + } + } else { + bta_dm_cb.disabling = FALSE; + + bta_sys_remove_uuid(UUID_SERVCLASS_PNP_INFORMATION); + bta_dm_cb.p_sec_cback(BTA_DM_DISABLE_EVT, NULL); + } +} + + + + +/******************************************************************************* +** +** Function bta_dm_set_dev_name +** +** Description Sets local device name +** +** +** Returns void +** +*******************************************************************************/ +void bta_dm_set_dev_name (tBTA_DM_MSG *p_data) +{ + BTM_SetLocalDeviceName((char *)p_data->set_name.name); +#if CLASSIC_BT_INCLUDED + bta_dm_set_eir ((char *)p_data->set_name.name); +#endif /// CLASSIC_BT_INCLUDED +} + +/******************************************************************************* +** +** Function bta_dm_get_dev_name +** +** Description Gets local device name +** +** +** Returns void +** +*******************************************************************************/ +void bta_dm_get_dev_name (tBTA_DM_MSG *p_data) +{ + tBTM_STATUS status; + char *name = NULL; + + status = BTM_ReadLocalDeviceName(&name); + if (p_data->get_name.p_cback) { + (*p_data->get_name.p_cback)(status, name); + } +} + +/******************************************************************************* +** +** Function bta_dm_cfg_coex_status +** +** Description config coexistance status +** +** +** Returns void +** +*******************************************************************************/ +#if (ESP_COEX_VSC_INCLUDED == TRUE) +void bta_dm_cfg_coex_status (tBTA_DM_MSG *p_data) +{ + BTM_ConfigCoexStatus(p_data->cfg_coex_status.op, + p_data->cfg_coex_status.type, + p_data->cfg_coex_status.status); +} +#endif + +/******************************************************************************* +** +** Function bta_dm_set_afh_channels +** +** Description Sets AFH channels +** +** +** Returns void +** +*******************************************************************************/ +void bta_dm_set_afh_channels (tBTA_DM_MSG *p_data) +{ +#if CLASSIC_BT_INCLUDED + BTM_SetAfhChannels (p_data->set_afh_channels.channels, p_data->set_afh_channels.set_afh_cb); +#endif /// CLASSIC_BT_INCLUDED +} + +#if (SDP_INCLUDED == TRUE) +/******************************************************************************* +** +** Function bta_dm_read_remote_device_name +** +** Description Initiate to get remote device name +** +** Returns TRUE if started to get remote name +** +*******************************************************************************/ +static BOOLEAN bta_dm_read_remote_device_name (BD_ADDR bd_addr, tBT_TRANSPORT transport) +{ + tBTM_STATUS btm_status; + + APPL_TRACE_DEBUG("bta_dm_read_remote_device_name"); + + bdcpy(bta_dm_search_cb.peer_bdaddr, bd_addr); + bta_dm_search_cb.peer_name[0] = '\0'; + + btm_status = BTM_ReadRemoteDeviceName (bta_dm_search_cb.peer_bdaddr, + (tBTM_CMPL_CB *) bta_dm_remname_cback, + transport); + + if ( btm_status == BTM_CMD_STARTED ) { + APPL_TRACE_DEBUG("bta_dm_read_remote_device_name: BTM_ReadRemoteDeviceName is started"); + + return (TRUE); + } else if ( btm_status == BTM_BUSY ) { + APPL_TRACE_DEBUG("bta_dm_read_remote_device_name: BTM_ReadRemoteDeviceName is busy"); + + /* Remote name discovery is on going now so BTM cannot notify through "bta_dm_remname_cback" */ + /* adding callback to get notified that current reading remore name done */ + BTM_SecAddRmtNameNotifyCallback(&bta_dm_service_search_remname_cback); + + return (TRUE); + } else { + APPL_TRACE_WARNING("bta_dm_read_remote_device_name: BTM_ReadRemoteDeviceName returns 0x%02X", btm_status); + + return (FALSE); + } +} + +/******************************************************************************* +** +** Function bta_dm_read_rmt_name +** +** Description Initiate to get remote device name +** +** Returns TRUE if started to get remote name +** +*******************************************************************************/ +void bta_dm_read_rmt_name(tBTA_DM_MSG *p_data) +{ + APPL_TRACE_DEBUG("%s",__func__); + bdcpy(bta_dm_search_cb.peer_bdaddr, p_data->get_rmt_name.rmt_addr); + bta_dm_search_cb.peer_name[0] = '\0'; + + tBTM_STATUS btm_status = BTM_ReadRemoteDeviceName(bta_dm_search_cb.peer_bdaddr, + (tBTM_CMPL_CB *) p_data->get_rmt_name.rmt_name_cb, + bta_dm_search_cb.transport); + + if (btm_status == BTM_CMD_STARTED) { + BTM_TRACE_DEBUG("%s: BTM_ReadRemoteDeviceName is started",__func__); + } else if (btm_status == BTM_BUSY) { + BTM_TRACE_DEBUG("%s: BTM_ReadRemoteDeviceName is busy",__func__); + } else { + BTM_TRACE_WARNING("%s: BTM_ReadRemoteDeviceName returns 0x%02X",__func__, btm_status); + } +} +#endif ///SDP_INCLUDED == TRUE + +#if (CLASSIC_BT_INCLUDED == TRUE) +void bta_dm_config_eir (tBTA_DM_MSG *p_data) +{ + tBTA_DM_API_CONFIG_EIR *config_eir = &p_data->config_eir; + + p_bta_dm_eir_cfg->bta_dm_eir_fec_required = config_eir->eir_fec_required; + p_bta_dm_eir_cfg->bta_dm_eir_included_name = config_eir->eir_included_name; + p_bta_dm_eir_cfg->bta_dm_eir_included_uuid = config_eir->eir_included_uuid; + p_bta_dm_eir_cfg->bta_dm_eir_included_tx_power = config_eir->eir_included_tx_power; + p_bta_dm_eir_cfg->bta_dm_eir_flags = config_eir->eir_flags; + p_bta_dm_eir_cfg->bta_dm_eir_manufac_spec_len = config_eir->eir_manufac_spec_len; + p_bta_dm_eir_cfg->bta_dm_eir_url_len = config_eir->eir_url_len; + + if (p_bta_dm_eir_cfg->bta_dm_eir_manufac_spec) { + osi_free(p_bta_dm_eir_cfg->bta_dm_eir_manufac_spec); + p_bta_dm_eir_cfg->bta_dm_eir_manufac_spec = NULL; + } + if (config_eir->eir_manufac_spec_len > 0) { + p_bta_dm_eir_cfg->bta_dm_eir_manufac_spec = osi_malloc(config_eir->eir_manufac_spec_len); + if (p_bta_dm_eir_cfg->bta_dm_eir_manufac_spec) { + memcpy(p_bta_dm_eir_cfg->bta_dm_eir_manufac_spec, config_eir->eir_manufac_spec, config_eir->eir_manufac_spec_len); + } else { + APPL_TRACE_ERROR("%s, malloc failed.", __func__); + p_bta_dm_eir_cfg->bta_dm_eir_manufac_spec_len = 0; + } + } + + if (p_bta_dm_eir_cfg->bta_dm_eir_url) { + osi_free(p_bta_dm_eir_cfg->bta_dm_eir_url); + p_bta_dm_eir_cfg->bta_dm_eir_url = NULL; + } + if (config_eir->eir_url_len > 0) { + p_bta_dm_eir_cfg->bta_dm_eir_url = osi_malloc(config_eir->eir_url_len); + if (p_bta_dm_eir_cfg->bta_dm_eir_url) { + memcpy(p_bta_dm_eir_cfg->bta_dm_eir_url, config_eir->eir_url, config_eir->eir_url_len); + } else { + APPL_TRACE_ERROR("%s, malloc failed.", __func__); + p_bta_dm_eir_cfg->bta_dm_eir_url_len = 0; + } + } + + bta_dm_set_eir(NULL); +} + +/******************************************************************************* +** +** Function bta_dm_set_page_timeout +** +** Description Sets page timeout +** +** +** Returns void +** +*******************************************************************************/ +void bta_dm_set_page_timeout (tBTA_DM_MSG *p_data) +{ + BTM_WritePageTimeout(p_data->set_page_timeout.page_to, p_data->set_page_timeout.set_page_to_cb); +} + +/******************************************************************************* +** +** Function bta_dm_get_page_timeout +** +** Description Gets page timeout +** +** +** Returns void +** +*******************************************************************************/ +void bta_dm_get_page_timeout (tBTA_DM_MSG *p_data) +{ + BTM_ReadPageTimeout(p_data->get_page_timeout.get_page_to_cb); +} + +/******************************************************************************* +** +** Function bta_dm_set_acl_pkt_types +** +** Description Sets ACL packet types +** +** +** Returns void +** +*******************************************************************************/ +void bta_dm_set_acl_pkt_types (tBTA_DM_MSG *p_data) +{ + if (p_data->set_acl_pkt_types.set_acl_pkt_types_cb != NULL) { + BTM_SetAclPktTypes(p_data->set_acl_pkt_types.rmt_addr, + p_data->set_acl_pkt_types.pkt_types, + p_data->set_acl_pkt_types.set_acl_pkt_types_cb); + } else { + APPL_TRACE_ERROR("%s(), the callback function can't be NULL.", __func__); + } +} + +#endif +/******************************************************************************* +** +** Function bta_dm_ble_set_channels +** +** Description Sets AFH channels +** +** +** Returns void +** +*******************************************************************************/ +void bta_dm_ble_set_channels (tBTA_DM_MSG *p_data) +{ +#if (BLE_INCLUDED == TRUE) + BTM_BleSetChannels (p_data->ble_set_channels.channels, p_data->ble_set_channels.set_channels_cb); +#endif /// BLE_INCLUDED == TRUE +} + +void bta_dm_update_white_list(tBTA_DM_MSG *p_data) +{ +#if (BLE_INCLUDED == TRUE) + BTM_BleUpdateAdvWhitelist(p_data->white_list.add_remove, p_data->white_list.remote_addr, p_data->white_list.addr_type, p_data->white_list.update_wl_cb); +#endif ///BLE_INCLUDED == TRUE +} + +void bta_dm_clear_white_list(tBTA_DM_MSG *p_data) +{ +#if (BLE_INCLUDED == TRUE) + BTM_BleClearWhitelist(p_data->white_list.update_wl_cb); +#endif +} + +void bta_dm_ble_read_adv_tx_power(tBTA_DM_MSG *p_data) +{ +#if (BLE_INCLUDED == TRUE) + if (p_data->read_tx_power.read_tx_power_cb != NULL) { + BTM_BleReadAdvTxPower(p_data->read_tx_power.read_tx_power_cb); + } else { + APPL_TRACE_ERROR("%s(), the callback function can't be NULL.", __func__); + } +#endif ///BLE_INCLUDED == TRUE +} + +void bta_dm_read_rssi(tBTA_DM_MSG *p_data) +{ + if (p_data->rssi.read_rssi_cb != NULL) { + BTM_ReadRSSI(p_data->rssi.remote_addr, p_data->rssi.transport, p_data->rssi.read_rssi_cb); + } else { + APPL_TRACE_ERROR("%s(), the callback function can't be NULL.", __func__); + } +} + +/******************************************************************************* +** +** Function bta_dm_set_visibility +** +** Description Sets discoverability, connectability and pairability +** +** +** Returns void +** +*******************************************************************************/ +void bta_dm_set_visibility(tBTA_DM_MSG *p_data) +{ + UINT16 window, interval; + UINT16 disc_mode = BTM_ReadDiscoverability(&window, &interval); + UINT16 conn_mode = BTM_ReadConnectability(&window, &interval); +#if (BLE_INCLUDED == TRUE) + UINT16 le_disc_mode = BTM_BleReadDiscoverability(); + UINT16 le_conn_mode = BTM_BleReadConnectability(); +#endif ///BLE_INCLUDED == TRUE + + /* set modes for Discoverability and connectability if not ignore */ + if (p_data->set_visibility.disc_mode != (BTA_DM_IGNORE | BTA_DM_LE_IGNORE)) { +#if (BLE_INCLUDED == TRUE) + if ((p_data->set_visibility.disc_mode & BTA_DM_LE_IGNORE) == BTA_DM_LE_IGNORE) { + p_data->set_visibility.disc_mode = + ((p_data->set_visibility.disc_mode & ~BTA_DM_LE_IGNORE) | le_disc_mode); + } +#endif ///BLE_INCLUDED == TRUE + + if ((p_data->set_visibility.disc_mode & BTA_DM_IGNORE) == BTA_DM_IGNORE) { + p_data->set_visibility.disc_mode = + ((p_data->set_visibility.disc_mode & ~BTA_DM_IGNORE) | disc_mode); + } + +#if (CLASSIC_BT_INCLUDED == TRUE) + BTM_SetDiscoverability(p_data->set_visibility.disc_mode, + bta_dm_cb.inquiry_scan_window, + bta_dm_cb.inquiry_scan_interval); +#endif + } + + if (p_data->set_visibility.conn_mode != (BTA_DM_IGNORE | BTA_DM_LE_IGNORE)) { +#if (BLE_INCLUDED == TRUE) + if ((p_data->set_visibility.conn_mode & BTA_DM_LE_IGNORE) == BTA_DM_LE_IGNORE) { + p_data->set_visibility.conn_mode = + ((p_data->set_visibility.conn_mode & ~BTA_DM_LE_IGNORE) | le_conn_mode); + } +#endif ///BLE_INCLUDED == TRUE + + if ((p_data->set_visibility.conn_mode & BTA_DM_IGNORE) == BTA_DM_IGNORE) { + p_data->set_visibility.conn_mode = + ((p_data->set_visibility.conn_mode & ~BTA_DM_IGNORE) | conn_mode); + } + +#if (CLASSIC_BT_INCLUDED == TRUE) + BTM_SetConnectability(p_data->set_visibility.conn_mode, + bta_dm_cb.page_scan_window, + bta_dm_cb.page_scan_interval); +#endif + } + + /* Send False or True if not ignore */ + if (p_data->set_visibility.pair_mode != BTA_DM_IGNORE ) { + + if (p_data->set_visibility.pair_mode == BTA_DM_NON_PAIRABLE) { + bta_dm_cb.disable_pair_mode = TRUE; + } else { + bta_dm_cb.disable_pair_mode = FALSE; + } + + } + + /* Send False or True if not ignore */ + if (p_data->set_visibility.conn_paired_only != BTA_DM_IGNORE) { + + if (p_data->set_visibility.conn_paired_only == BTA_DM_CONN_ALL) { + bta_dm_cb.conn_paired_only = FALSE; + } else { + bta_dm_cb.conn_paired_only = TRUE; + } + + } + + /* Change mode if either mode is not ignore */ + if (p_data->set_visibility.pair_mode != BTA_DM_IGNORE || p_data->set_visibility.conn_paired_only != BTA_DM_IGNORE) { + BTM_SetPairableMode((BOOLEAN)(!(bta_dm_cb.disable_pair_mode)), bta_dm_cb.conn_paired_only); + } + +} + +/******************************************************************************* +** +** Function bta_dm_process_remove_device +** +** Description Removes device, Disconnects ACL link if required. +**** +*******************************************************************************/ +static void bta_dm_process_remove_device(BD_ADDR bd_addr, tBT_TRANSPORT transport) +{ +#if (BLE_INCLUDED == TRUE && GATTC_INCLUDED == TRUE) + /* need to remove all pending background connection before unpair */ + BTA_GATTC_CancelOpen(0, bd_addr, FALSE); +#endif + + BTM_SecDeleteDevice(bd_addr, transport); + + if (bta_dm_cb.p_sec_cback) { + tBTA_DM_SEC sec_event; + bdcpy(sec_event.link_down.bd_addr, bd_addr); + sec_event.link_down.status = HCI_SUCCESS; + if (transport == BT_TRANSPORT_LE){ + bta_dm_cb.p_sec_cback(BTA_DM_BLE_DEV_UNPAIRED_EVT, &sec_event); + } else { + bta_dm_cb.p_sec_cback(BTA_DM_DEV_UNPAIRED_EVT, &sec_event); + } + + } +} + +/******************************************************************************* +** +** Function bta_dm_remove_device +** +** Description Removes device, disconnects ACL link if required. +**** +*******************************************************************************/ +void bta_dm_remove_device(tBTA_DM_MSG *p_data) +{ + tBTA_DM_API_REMOVE_DEVICE *p_dev = &p_data->remove_dev; + if (p_dev == NULL) { + return; + } + + /* If ACL exists for the device in the remove_bond message*/ + BOOLEAN continue_delete_dev = FALSE; + UINT8 transport = p_dev->transport; + + if (BTM_IsAclConnectionUp(p_dev->bd_addr, transport)) { + APPL_TRACE_DEBUG("%s: ACL Up count %d", __func__, bta_dm_cb.device_list.count); + continue_delete_dev = FALSE; + + /* Take the link down first, and mark the device for removal when disconnected */ + for (int i = 0; i < bta_dm_cb.device_list.count; i++) { + if (!bdcmp(bta_dm_cb.device_list.peer_device[i].peer_bdaddr, p_dev->bd_addr) +#if BLE_INCLUDED == TRUE + && bta_dm_cb.device_list.peer_device[i].transport == transport +#endif + ) { + + bta_dm_cb.device_list.peer_device[i].conn_state = BTA_DM_UNPAIRING; + btm_remove_acl( p_dev->bd_addr, transport); + APPL_TRACE_DEBUG("%s:transport = %d", __func__, transport); + break; + } + } + } else { + continue_delete_dev = TRUE; + } + + /* Delete the device mentioned in the msg */ + if (continue_delete_dev) { + bta_dm_process_remove_device(p_dev->bd_addr, transport); + } + + BTM_ClearInqDb (p_dev->bd_addr); +} + +/******************************************************************************* +** +** Function bta_dm_add_device +** +** Description This function adds a Link Key to an security database entry. +** It is normally called during host startup to restore all required information +** stored in the NVRAM. +**** +*******************************************************************************/ +void bta_dm_add_device (tBTA_DM_MSG *p_data) +{ + tBTA_DM_API_ADD_DEVICE *p_dev = &p_data->add_dev; + UINT8 *p_dc = NULL; + UINT8 *p_lc = NULL; + UINT32 trusted_services_mask[BTM_SEC_SERVICE_ARRAY_SIZE]; + UINT8 index = 0; + UINT8 btm_mask_index = 0; + + memset (trusted_services_mask, 0, sizeof(trusted_services_mask)); + + /* If not all zeros, the device class has been specified */ + if (p_dev->dc_known) { + p_dc = (UINT8 *)p_dev->dc; + } + + if (p_dev->link_key_known) { + p_lc = (UINT8 *)p_dev->link_key; + } + + if (p_dev->is_trusted) { + /* covert BTA service mask to BTM mask */ + while (p_dev->tm && (index < BTA_MAX_SERVICE_ID)) { + if (p_dev->tm & (UINT32)(1 << index)) { + + btm_mask_index = bta_service_id_to_btm_srv_id_lkup_tbl[index] / BTM_SEC_ARRAY_BITS; + trusted_services_mask[btm_mask_index] |= (UINT32)(1 << (bta_service_id_to_btm_srv_id_lkup_tbl[index] - (UINT32)(btm_mask_index * 32))); + + p_dev->tm &= (UINT32)(~(1 << index)); + + } + index++; + } + } + + if (!BTM_SecAddDevice (p_dev->bd_addr, p_dc, p_dev->bd_name, p_dev->features, + trusted_services_mask, p_lc, p_dev->key_type, p_dev->io_cap, + p_dev->pin_length, p_dev->sc_support)) { + APPL_TRACE_ERROR ("BTA_DM: Error adding device %08x%04x", + (p_dev->bd_addr[0] << 24) + (p_dev->bd_addr[1] << 16) + (p_dev->bd_addr[2] << 8) + p_dev->bd_addr[3], + (p_dev->bd_addr[4] << 8) + p_dev->bd_addr[5]); + } +} + +/******************************************************************************* +** +** Function bta_dm_close_acl +** +** Description This function forces to close the connection to a remote device +** and optionaly remove the device from security database if +** required. +**** +*******************************************************************************/ +void bta_dm_close_acl(tBTA_DM_MSG *p_data) +{ + tBTA_DM_API_REMOVE_ACL *p_remove_acl = &p_data->remove_acl; + UINT8 index; + + APPL_TRACE_DEBUG("bta_dm_close_acl"); + + if (BTM_IsAclConnectionUp(p_remove_acl->bd_addr, p_remove_acl->transport)) { + for (index = 0; index < bta_dm_cb.device_list.count; index ++) { + if (!bdcmp( bta_dm_cb.device_list.peer_device[index].peer_bdaddr, p_remove_acl->bd_addr)) { + break; + } + } + if (index != bta_dm_cb.device_list.count) { + if (p_remove_acl->remove_dev) { + bta_dm_cb.device_list.peer_device[index].remove_dev_pending = TRUE; + } + } else { + APPL_TRACE_ERROR("unknown device, remove ACL failed"); + } + /* Disconnect the ACL link */ + btm_remove_acl(p_remove_acl->bd_addr, p_remove_acl->transport); + } + /* if to remove the device from security database ? do it now */ + else if (p_remove_acl->remove_dev) { + if (!BTM_SecDeleteDevice(p_remove_acl->bd_addr, p_remove_acl->transport)) { + APPL_TRACE_ERROR("delete device from security database failed."); + } +#if (BLE_INCLUDED == TRUE && GATTC_INCLUDED == TRUE) + /* need to remove all pending background connection if any */ + BTA_GATTC_CancelOpen(0, p_remove_acl->bd_addr, FALSE); +#endif + } + /* otherwise, no action needed */ + +} + +/******************************************************************************* +** +** Function bta_dm_remove_all_acl +** +** Description This function forces to close all the ACL links specified by link type +**** +*******************************************************************************/ +void bta_dm_remove_all_acl(tBTA_DM_MSG *p_data) +{ + const tBTA_DM_LINK_TYPE link_type = p_data->remove_all_acl.link_type; + tBT_TRANSPORT transport = BT_TRANSPORT_BR_EDR; + + APPL_TRACE_DEBUG("%s link type = %d", __func__, link_type); + + for (UINT8 i = 0; i < bta_dm_cb.device_list.count; i++) { + BD_ADDR addr = {0}; + bdcpy(addr, bta_dm_cb.device_list.peer_device[i].peer_bdaddr); +#if defined (BLE_INCLUDED) && (BLE_INCLUDED == TRUE) + transport = bta_dm_cb.device_list.peer_device[i].transport; +#endif + if ((link_type == BTA_DM_LINK_TYPE_ALL) || + ((link_type == BTA_DM_LINK_TYPE_LE) && (transport == BT_TRANSPORT_LE)) || + ((link_type == BTA_DM_LINK_TYPE_BR_EDR) && (transport == BT_TRANSPORT_BR_EDR))) { + /* Disconnect the ACL link */ + btm_remove_acl(addr, transport); + } + } +} + + +/******************************************************************************* +** +** Function bta_dm_bond +** +** Description Bonds with peer device +** +** +** Returns void +** +*******************************************************************************/ +#if (SMP_INCLUDED == TRUE) +void bta_dm_bond (tBTA_DM_MSG *p_data) +{ + tBTM_STATUS status; + tBTA_DM_SEC sec_event; + char *p_name; + + if (p_data->bond.transport == BTA_TRANSPORT_UNKNOWN) { + status = BTM_SecBond ( p_data->bond.bd_addr, 0, NULL, 0 ); + } else { + status = BTM_SecBondByTransport ( p_data->bond.bd_addr, p_data->bond.transport, 0, NULL, 0 ); + } + + + if (bta_dm_cb.p_sec_cback && (status != BTM_CMD_STARTED)) { + + memset(&sec_event, 0, sizeof(tBTA_DM_SEC)); + bdcpy(sec_event.auth_cmpl.bd_addr, p_data->bond.bd_addr); + p_name = BTM_SecReadDevName(p_data->bond.bd_addr); + if (p_name != NULL) { + memcpy(sec_event.auth_cmpl.bd_name, p_name, (BD_NAME_LEN - 1)); + sec_event.auth_cmpl.bd_name[BD_NAME_LEN - 1] = 0; + } + + /* taken care of by memset [above] + sec_event.auth_cmpl.key_present = FALSE; + sec_event.auth_cmpl.success = FALSE; + */ + sec_event.auth_cmpl.fail_reason = HCI_ERR_ILLEGAL_COMMAND; + if (status == BTM_SUCCESS) { + sec_event.auth_cmpl.success = TRUE; + } else { + /* delete this device entry from Sec Dev DB */ + bta_dm_remove_sec_dev_entry(p_data->bond.bd_addr); + } + bta_dm_cb.p_sec_cback(BTA_DM_AUTH_CMPL_EVT, &sec_event); + } + +} + +/******************************************************************************* +** +** Function bta_dm_bond_cancel +** +** Description Cancels bonding with a peer device +** +** +** Returns void +** +*******************************************************************************/ +void bta_dm_bond_cancel (tBTA_DM_MSG *p_data) +{ + tBTM_STATUS status; + tBTA_DM_SEC sec_event; + + APPL_TRACE_EVENT(" bta_dm_bond_cancel "); + status = BTM_SecBondCancel ( p_data->bond_cancel.bd_addr ); + + if (bta_dm_cb.p_sec_cback && (status != BTM_CMD_STARTED && status != BTM_SUCCESS)) { + sec_event.bond_cancel_cmpl.result = BTA_FAILURE; + + bta_dm_cb.p_sec_cback(BTA_DM_BOND_CANCEL_CMPL_EVT, &sec_event); + } + +} + +/******************************************************************************* +** +** Function bta_dm_set_pin_type +** +** Description Set the pin type and fixed pin +** +** +** Returns void +** +*******************************************************************************/ +void bta_dm_set_pin_type (tBTA_DM_MSG *p_data) +{ +#if (CLASSIC_BT_INCLUDED == TRUE) + BTM_SetPinType (p_data->set_pin_type.pin_type, p_data->set_pin_type.p_pin, p_data->set_pin_type.pin_len); +#endif ///CLASSIC_BT_INCLUDED == TRUE +} + +/******************************************************************************* +** +** Function bta_dm_pin_reply +** +** Description Send the pin_reply to a request from BTM +** +** +** Returns void +** +*******************************************************************************/ +void bta_dm_pin_reply (tBTA_DM_MSG *p_data) +{ +#if (CLASSIC_BT_INCLUDED == TRUE) + UINT32 trusted_mask[BTM_SEC_SERVICE_ARRAY_SIZE]; + UINT32 *current_trusted_mask; + + current_trusted_mask = BTM_ReadTrustedMask(p_data->pin_reply.bd_addr); + + if (current_trusted_mask) { + memcpy(trusted_mask, current_trusted_mask, sizeof(trusted_mask)); + } else { + memset(trusted_mask, 0, sizeof(trusted_mask)); + } + + if (p_data->pin_reply.accept) { + + BTM_PINCodeReply(p_data->pin_reply.bd_addr, BTM_SUCCESS, p_data->pin_reply.pin_len, p_data->pin_reply.p_pin, trusted_mask ); + } else { + BTM_PINCodeReply(p_data->pin_reply.bd_addr, BTM_NOT_AUTHORIZED, 0, NULL, trusted_mask ); + } +#endif ///CLASSIC_BT_INCLUDED == TRUE +} +#endif ///SMP_INCLUDED == TRUE + +/******************************************************************************* +** +** Function bta_dm_policy_cback +** +** Description process the link policy changes +** +** Returns void +** +*******************************************************************************/ +static void bta_dm_policy_cback(tBTA_SYS_CONN_STATUS status, UINT8 id, UINT8 app_id, BD_ADDR peer_addr) +{ + tBTA_DM_PEER_DEVICE *p_dev = NULL; + UINT16 policy = app_id; + UINT32 mask = (UINT32)(1 << id); + + if (peer_addr) { + p_dev = bta_dm_find_peer_device(peer_addr); + } + + APPL_TRACE_DEBUG(" bta_dm_policy_cback cmd:%d, policy:0x%x", + status, policy); + switch (status) { + case BTA_SYS_PLCY_SET: + if (!p_dev) { + return; + } + /* restore the default link policy */ + p_dev->link_policy |= policy; + BTM_SetLinkPolicy(p_dev->peer_bdaddr, &(p_dev->link_policy)); + break; + + case BTA_SYS_PLCY_CLR: + if (!p_dev) { + return; + } + /* clear the policy from the default link policy */ + p_dev->link_policy &= (~policy); + BTM_SetLinkPolicy(p_dev->peer_bdaddr, &(p_dev->link_policy)); + +#if (BTA_DM_PM_INCLUDED == TRUE) + if (policy & (HCI_ENABLE_SNIFF_MODE | HCI_ENABLE_PARK_MODE)) { + /* if clearing sniff/park, wake the link */ + bta_dm_pm_active(p_dev->peer_bdaddr); + } +#endif /* #if (BTA_DM_PM_INCLUDED == TRUE) */ + break; + + case BTA_SYS_PLCY_DEF_SET: + /* want to restore/set the role switch policy */ + bta_dm_cb.role_policy_mask &= ~mask; + if (0 == bta_dm_cb.role_policy_mask) { + /* if nobody wants to insist on the role */ + bta_dm_cb.cur_policy |= HCI_ENABLE_MASTER_SLAVE_SWITCH; + BTM_SetDefaultLinkPolicy(bta_dm_cb.cur_policy); + } + break; + + case BTA_SYS_PLCY_DEF_CLR: + /* want to remove the role switch policy */ + bta_dm_cb.role_policy_mask |= mask; + bta_dm_cb.cur_policy &= ~HCI_ENABLE_MASTER_SLAVE_SWITCH; + BTM_SetDefaultLinkPolicy(bta_dm_cb.cur_policy); + break; + } +} + +/******************************************************************************* +** +** Function bta_dm_confirm +** +** Description Send the user confirm request reply in response to a +** request from BTM +** +** Returns void +** +*******************************************************************************/ +#if (CLASSIC_BT_INCLUDED == TRUE) +void bta_dm_confirm(tBTA_DM_MSG *p_data) +{ + tBTM_STATUS res = BTM_NOT_AUTHORIZED; + + if (p_data->confirm.accept == TRUE) { + res = BTM_SUCCESS; + } + BTM_ConfirmReqReply(res, p_data->confirm.bd_addr); +} + +/******************************************************************************* +** +** Function bta_dm_key_req +** +** Description Send the user passkey request reply in response to a +** request from BTM +** +** Returns void +** +*******************************************************************************/ +void bta_dm_key_req(tBTA_DM_MSG *p_data) +{ + tBTM_STATUS res = BTM_NOT_AUTHORIZED; + + if (p_data->key_req.accept == TRUE) { + res = BTM_SUCCESS; + } + BTM_PasskeyReqReply(res, p_data->key_req.bd_addr, p_data->key_req.passkey); +} +#endif ///CLASSIC_BT_INCLUDED == TRUE + +/******************************************************************************* +** +** Function bta_dm_loc_oob +** +** Description Retrieve the OOB data from the local LM +** +** Returns void +** +*******************************************************************************/ +#if (BTM_OOB_INCLUDED == TRUE && SMP_INCLUDED == TRUE) +void bta_dm_loc_oob(tBTA_DM_MSG *p_data) +{ + UNUSED(p_data); + BTM_ReadLocalOobData(); +} + +/******************************************************************************* +** +** Function bta_dm_oob_reply +** +** Description This function is called to provide the OOB data for +** SMP in response to BLE OOB request. +** +** Returns void +** +*******************************************************************************/ +void bta_dm_oob_reply(tBTA_DM_MSG *p_data) +{ +#if (BLE_INCLUDED) + BTM_BleOobDataReply(p_data->oob_reply.bd_addr, BTM_SUCCESS, p_data->oob_reply.len, p_data->oob_reply.value); +#endif +} + +/******************************************************************************* +** +** Function bta_dm_sc_oob_reply +** +** Description This function is called to provide the OOB data for +** SMP in response to BLE secure connection OOB request. +** +** Returns void +** +*******************************************************************************/ +void bta_dm_sc_oob_reply(tBTA_DM_MSG *p_data) +{ +#if (BLE_INCLUDED) + BTM_BleSecureConnectionOobDataReply(p_data->sc_oob_reply.bd_addr, p_data->sc_oob_reply.c, p_data->sc_oob_reply.r); +#endif +} + +/******************************************************************************* +** +** Function bta_dm_sc_create_oob_data +** +** Description This function is called to create the OOB data for +** SMP when secure connection. +** +** Returns void +** +*******************************************************************************/ +void bta_dm_sc_create_oob_data(tBTA_DM_MSG *p_data) +{ +#if (BLE_INCLUDED) + BTM_BleSecureConnectionCreateOobData(); +#endif +} + +/******************************************************************************* +** +** Function bta_dm_ci_io_req_act +** +** Description respond to the IO capabilities request from BTM +** +** Returns void +** +*******************************************************************************/ +void bta_dm_ci_io_req_act(tBTA_DM_MSG *p_data) +{ + tBTM_AUTH_REQ auth_req = BTM_AUTH_AP_NO; + if (p_data->ci_io_req.auth_req) { + auth_req = BTM_AUTH_AP_YES; + } + BTM_IoCapRsp(p_data->ci_io_req.bd_addr, p_data->ci_io_req.io_cap, + p_data->ci_io_req.oob_data, auth_req); +} + +/******************************************************************************* +** +** Function bta_dm_ci_rmt_oob_act +** +** Description respond to the OOB data request for the remote device from BTM +** +** +** Returns void +** +*******************************************************************************/ +void bta_dm_ci_rmt_oob_act(tBTA_DM_MSG *p_data) +{ + tBTM_STATUS res = BTM_NOT_AUTHORIZED; + + if (p_data->ci_rmt_oob.accept == TRUE) { + res = BTM_SUCCESS; + } + BTM_RemoteOobDataReply(res, p_data->ci_rmt_oob.bd_addr, + p_data->ci_rmt_oob.c, p_data->ci_rmt_oob.r ); +} +#endif /* BTM_OOB_INCLUDED == TRUE && SMP_INCLUDED == TRUE */ + +/******************************************************************************* +** +** Function bta_dm_search_start +** +** Description Starts an inquiry +** +** +** Returns void +** +*******************************************************************************/ +void bta_dm_search_start (tBTA_DM_MSG *p_data) +{ + tBTM_INQUIRY_CMPL result; + +#if (BLE_INCLUDED == TRUE && BTA_GATT_INCLUDED == TRUE && SDP_INCLUDED == TRUE) && (GATTC_INCLUDED == TRUE) + UINT16 len = (UINT16)(sizeof(tBT_UUID) * p_data->search.num_uuid); + bta_dm_gattc_register(); +#endif + + APPL_TRACE_DEBUG("%s avoid_scatter=%d", __func__, p_bta_dm_cfg->avoid_scatter); + + if (p_bta_dm_cfg->avoid_scatter && + (p_data->search.rs_res == BTA_DM_RS_NONE) && bta_dm_check_av(BTA_DM_API_SEARCH_EVT)) { + memcpy(&bta_dm_cb.search_msg, &p_data->search, sizeof(tBTA_DM_API_SEARCH)); + return; + } + + BTM_ClearInqDb(NULL); + /* save search params */ + bta_dm_search_cb.p_search_cback = p_data->search.p_cback; + bta_dm_search_cb.services = p_data->search.services; + +#if (BLE_INCLUDED == TRUE && BTA_GATT_INCLUDED == TRUE && SDP_INCLUDED == TRUE) && (GATTC_INCLUDED == TRUE) + utl_freebuf((void **)&bta_dm_search_cb.p_srvc_uuid); + + if ((bta_dm_search_cb.num_uuid = p_data->search.num_uuid) != 0 && + p_data->search.p_uuid != NULL) { + if ((bta_dm_search_cb.p_srvc_uuid = (tBT_UUID *)osi_malloc(len)) == NULL) { + APPL_TRACE_ERROR("%s no resources", __func__); + + result.status = BTA_FAILURE; + result.num_resp = 0; + bta_dm_inq_cmpl_cb ((void *)&result); + return; + } + + memcpy(bta_dm_search_cb.p_srvc_uuid, p_data->search.p_uuid, len); + } +#endif + result.status = BTM_StartInquiry( (tBTM_INQ_PARMS *)&p_data->search.inq_params, + bta_dm_inq_results_cb, + (tBTM_CMPL_CB *) bta_dm_inq_cmpl_cb); + + APPL_TRACE_EVENT("%s status=%d", __func__, result.status); + if (result.status != BTM_CMD_STARTED) { + result.num_resp = 0; + bta_dm_inq_cmpl_cb ((void *)&result); + } +} + +/******************************************************************************* +** +** Function bta_dm_search_cancel +** +** Description Cancels an ongoing search for devices +** +** +** Returns void +** +*******************************************************************************/ +void bta_dm_search_cancel (tBTA_DM_MSG *p_data) +{ + UNUSED(p_data); + tBTA_DM_MSG *p_msg; + + if (BTM_IsInquiryActive()) { + if (BTM_CancelInquiry() != BTM_CMD_STARTED) { + bta_dm_search_cancel_notify(NULL); + p_msg = (tBTA_DM_MSG *) osi_malloc(sizeof(tBTA_DM_MSG)); + if (p_msg != NULL) { + p_msg->hdr.event = BTA_DM_SEARCH_CMPL_EVT; + p_msg->hdr.layer_specific = BTA_DM_API_DISCOVER_EVT; + bta_sys_sendmsg(p_msg); + } + } else { + /* flag a search cancel is pending */ + bta_dm_search_cb.cancel_pending = TRUE; + } + } + /* If no Service Search going on then issue cancel remote name in case it is active */ + else if (!bta_dm_search_cb.name_discover_done) { + BTM_CancelRemoteDeviceName(); + + if ((p_msg = (tBTA_DM_MSG *) osi_malloc(sizeof(tBTA_DM_MSG))) != NULL) { + p_msg->hdr.event = BTA_DM_SEARCH_CMPL_EVT; + p_msg->hdr.layer_specific = BTA_DM_API_DISCOVER_EVT; + bta_sys_sendmsg(p_msg); + } + + } else { + if ((p_msg = (tBTA_DM_MSG *) osi_malloc(sizeof(tBTA_DM_MSG))) != NULL) { + p_msg->hdr.event = BTA_DM_INQUIRY_CMPL_EVT; + p_msg->hdr.layer_specific = BTA_DM_API_DISCOVER_EVT; + bta_sys_sendmsg(p_msg); + } + } + +#if BLE_INCLUDED == TRUE && BTA_GATT_INCLUDED == TRUE && SDP_INCLUDED == TRUE && GATTC_INCLUDED == TRUE + if (bta_dm_search_cb.gatt_disc_active) { + bta_dm_cancel_gatt_discovery(bta_dm_search_cb.peer_bdaddr); + } +#endif +} + +/******************************************************************************* +** +** Function bta_dm_discover +** +** Description Discovers services on a remote device +** +** +** Returns void +** +*******************************************************************************/ +#if (SDP_INCLUDED == TRUE) +void bta_dm_discover (tBTA_DM_MSG *p_data) +{ +#if BLE_INCLUDED == TRUE && BTA_GATT_INCLUDED == TRUE && GATTC_INCLUDED == TRUE + UINT16 len = (UINT16)(sizeof(tBT_UUID) * p_data->discover.num_uuid); +#endif + APPL_TRACE_EVENT("%s services_to_search=0x%04X, sdp_search=%d", __func__, + p_data->discover.services, p_data->discover.sdp_search); + + /* save the search condition */ + bta_dm_search_cb.services = p_data->discover.services; + +#if BLE_INCLUDED == TRUE && BTA_GATT_INCLUDED == TRUE && GATTC_INCLUDED == TRUE + bta_dm_gattc_register(); + utl_freebuf((void **)&bta_dm_search_cb.p_srvc_uuid); + if ((bta_dm_search_cb.num_uuid = p_data->discover.num_uuid) != 0 && + p_data->discover.p_uuid != NULL) { + if ((bta_dm_search_cb.p_srvc_uuid = (tBT_UUID *)osi_malloc(len)) == NULL) { + p_data->discover.p_cback(BTA_DM_DISC_CMPL_EVT, NULL); + return; + } + memcpy(bta_dm_search_cb.p_srvc_uuid, p_data->discover.p_uuid, len); + } + bta_dm_search_cb.uuid_to_search = bta_dm_search_cb.num_uuid; +#endif + + bta_dm_search_cb.p_search_cback = p_data->discover.p_cback; + bta_dm_search_cb.sdp_search = p_data->discover.sdp_search; + bta_dm_search_cb.services_to_search = bta_dm_search_cb.services; + bta_dm_search_cb.service_index = 0; + bta_dm_search_cb.services_found = 0; + bta_dm_search_cb.peer_name[0] = '\0'; + bta_dm_search_cb.sdp_search = p_data->discover.sdp_search; + bta_dm_search_cb.p_btm_inq_info = BTM_InqDbRead (p_data->discover.bd_addr); + bta_dm_search_cb.transport = p_data->discover.transport; + + bta_dm_search_cb.name_discover_done = FALSE; + memcpy(&bta_dm_search_cb.uuid, &p_data->discover.uuid, sizeof(tSDP_UUID)); + bta_dm_discover_device(p_data->discover.bd_addr); +} + +/******************************************************************************* +** +** Function bta_dm_di_disc_cmpl +** +** Description Sends event to application when DI discovery complete +** +** Returns void +** +*******************************************************************************/ +void bta_dm_di_disc_cmpl(tBTA_DM_MSG *p_data) +{ + tBTA_DM_DI_DISC_CMPL di_disc; + + memset(&di_disc, 0, sizeof(tBTA_DM_DI_DISC_CMPL)); + bdcpy(di_disc.bd_addr, bta_dm_search_cb.peer_bdaddr); + + if ((p_data->hdr.offset == SDP_SUCCESS) + || (p_data->hdr.offset == SDP_DB_FULL)) { + di_disc.num_record = SDP_GetNumDiRecords(bta_dm_di_cb.p_di_db); + } else { + di_disc.result = BTA_FAILURE; + } + + bta_dm_di_cb.p_di_db = NULL; + bta_dm_search_cb.p_search_cback(BTA_DM_DI_DISC_CMPL_EVT, (tBTA_DM_SEARCH *) &di_disc); +} + +/******************************************************************************* +** +** Function bta_dm_di_disc_callback +** +** Description This function queries a remote device for DI information. +** +** +** Returns void +** +*******************************************************************************/ +static void bta_dm_di_disc_callback(UINT16 result) +{ + tBTA_DM_MSG *p_msg; + + if ((p_msg = (tBTA_DM_MSG *) osi_malloc(sizeof(tBTA_DM_MSG))) != NULL) { + p_msg->hdr.event = BTA_DM_SEARCH_CMPL_EVT; + p_msg->hdr.layer_specific = BTA_DM_API_DI_DISCOVER_EVT; + p_msg->hdr.offset = result; + bta_sys_sendmsg(p_msg); + } +} +#endif ///SDP_INCLUDED == TRUE +/******************************************************************************* +** +** Function bta_dm_disable_search_and_disc +** +** Description Cancels an ongoing search or discovery for devices in case of +** a Bluetooth disable +** +** +** Returns void +** +*******************************************************************************/ +static void bta_dm_disable_search_and_disc (void) +{ +#if (SDP_INCLUDED == TRUE) + tBTA_DM_DI_DISC_CMPL di_disc; +#endif ///SDP_INCLUDED == TRUE + if (bta_dm_search_cb.state != BTA_DM_SEARCH_IDLE) { + bta_dm_search_cancel(NULL); + } +#if (SDP_INCLUDED == TRUE) + if (bta_dm_di_cb.p_di_db != NULL) { + memset(&di_disc, 0, sizeof(tBTA_DM_DI_DISC_CMPL)); + bdcpy(di_disc.bd_addr, bta_dm_search_cb.peer_bdaddr); + di_disc.result = BTA_FAILURE; + + bta_dm_di_cb.p_di_db = NULL; + bta_dm_search_cb.p_search_cback(BTA_DM_DI_DISC_CMPL_EVT, NULL); + } +#endif ///SDP_INCLUDED == TRUE +} + +/******************************************************************************* +** +** Function bta_dm_di_disc +** +** Description This function queries a remote device for DI information. +** +** +** Returns void +** +*******************************************************************************/ +#if (SDP_INCLUDED == TRUE) +void bta_dm_di_disc (tBTA_DM_MSG *p_data) +{ + UINT16 result = BTA_FAILURE; + tBTA_DM_MSG *p_msg; + + bta_dm_search_cb.p_search_cback = p_data->di_disc.p_cback; + bdcpy(bta_dm_search_cb.peer_bdaddr, p_data->di_disc.bd_addr); + bta_dm_di_cb.p_di_db = p_data->di_disc.p_sdp_db; + + if ((bta_dm_search_cb.p_sdp_db = (tSDP_DISCOVERY_DB *)osi_malloc(BTA_DM_SDP_DB_SIZE)) != NULL) { + if ( SDP_DiDiscover(bta_dm_search_cb.peer_bdaddr, p_data->di_disc.p_sdp_db, + p_data->di_disc.len, bta_dm_di_disc_callback) == SDP_SUCCESS) { + result = BTA_SUCCESS; + } + } else { + APPL_TRACE_ERROR("No buffer to start DI discovery"); + } + + if ( result == BTA_FAILURE && + (p_msg = (tBTA_DM_MSG *) osi_malloc(sizeof(tBTA_DM_MSG))) != NULL) { + p_msg->hdr.event = BTA_DM_SEARCH_CMPL_EVT; + p_msg->hdr.layer_specific = BTA_DM_API_DI_DISCOVER_EVT; + p_data->hdr.offset = result; + bta_sys_sendmsg(p_msg); + } +} +#endif ///SDP_INCLUDED == TRUE + +/******************************************************************************* +** +** Function bta_dm_inq_cmpl +** +** Description Process the inquiry complete event from BTM +** +** Returns void +** +*******************************************************************************/ +void bta_dm_inq_cmpl (tBTA_DM_MSG *p_data) +{ + tBTA_DM_MSG *p_msg; + tBTA_DM_SEARCH data; + + APPL_TRACE_DEBUG("bta_dm_inq_cmpl"); + + data.inq_cmpl.num_resps = p_data->inq_cmpl.num; + bta_dm_search_cb.p_search_cback(BTA_DM_INQ_CMPL_EVT, &data); + + if ((bta_dm_search_cb.p_btm_inq_info = BTM_InqDbFirst()) != NULL) { + /* start name and service discovery from the first device on inquiry result */ + bta_dm_search_cb.name_discover_done = FALSE; + bta_dm_search_cb.peer_name[0] = 0; +#if (SDP_INCLUDED == TRUE) + bta_dm_discover_device(bta_dm_search_cb.p_btm_inq_info->results.remote_bd_addr); +#endif ///SDP_INCLUDED == TRUE + } else { + /* no devices, search complete */ + bta_dm_search_cb.services = 0; + + if ((p_msg = (tBTA_DM_MSG *) osi_malloc(sizeof(tBTA_DM_MSG))) != NULL) { + p_msg->hdr.event = BTA_DM_SEARCH_CMPL_EVT; + p_msg->hdr.layer_specific = BTA_DM_API_DISCOVER_EVT; + bta_sys_sendmsg(p_msg); + } + } +} + +/******************************************************************************* +** +** Function bta_dm_rmt_name +** +** Description Process the remote name result from BTM +** +** Returns void +** +*******************************************************************************/ +void bta_dm_rmt_name (tBTA_DM_MSG *p_data) +{ + APPL_TRACE_DEBUG("bta_dm_rmt_name"); + + if ( p_data->rem_name.result.disc_res.bd_name[0] && bta_dm_search_cb.p_btm_inq_info) { + bta_dm_search_cb.p_btm_inq_info->appl_knows_rem_name = TRUE; + } +#if (SDP_INCLUDED == TRUE) + bta_dm_discover_device(bta_dm_search_cb.peer_bdaddr); +#endif ///SDP_INCLUDED == TRUE +} + +/******************************************************************************* +** +** Function bta_dm_disc_rmt_name +** +** Description Process the remote name result from BTM when application +** wants to find the name for a bdaddr +** +** Returns void +** +*******************************************************************************/ +void bta_dm_disc_rmt_name (tBTA_DM_MSG *p_data) +{ + tBTM_INQ_INFO *p_btm_inq_info; + + APPL_TRACE_DEBUG("bta_dm_disc_rmt_name"); + + p_btm_inq_info = BTM_InqDbRead (p_data->rem_name.result.disc_res.bd_addr); + if ( p_btm_inq_info ) { + if ( p_data->rem_name.result.disc_res.bd_name[0] ) { + p_btm_inq_info->appl_knows_rem_name = TRUE; + } + } +#if (SDP_INCLUDED == TRUE) + bta_dm_discover_device(p_data->rem_name.result.disc_res.bd_addr); +#endif ///SDP_INCLUDED == TRUE +} + +/******************************************************************************* +** +** Function bta_dm_sdp_result +** +** Description Process the discovery result from sdp +** +** Returns void +** +*******************************************************************************/ +#if (SDP_INCLUDED == TRUE) +void bta_dm_sdp_result (tBTA_DM_MSG *p_data) +{ + tSDP_DISC_REC *p_sdp_rec = NULL; + tBTA_DM_MSG *p_msg; + BOOLEAN scn_found = FALSE; + UINT16 service = 0xFFFF; + tSDP_PROTOCOL_ELEM pe; + +#if BLE_INCLUDED == TRUE && BTA_GATT_INCLUDED == TRUE + tBT_UUID *p_uuid = bta_dm_search_cb.p_srvc_uuid; + tBTA_DM_SEARCH result; + tBT_UUID service_uuid; +#endif + + UINT32 num_uuids = 0; + UINT8 uuid_list[32][MAX_UUID_SIZE]; // assuming a max of 32 services + + if ((p_data->sdp_event.sdp_result == SDP_SUCCESS) + || (p_data->sdp_event.sdp_result == SDP_NO_RECS_MATCH) + || (p_data->sdp_event.sdp_result == SDP_DB_FULL)) { + APPL_TRACE_DEBUG("sdp_result::0x%x", p_data->sdp_event.sdp_result); + do { + p_sdp_rec = NULL; + if ( bta_dm_search_cb.service_index == (BTA_USER_SERVICE_ID + 1) ) { + p_sdp_rec = SDP_FindServiceUUIDInDb(bta_dm_search_cb.p_sdp_db, &bta_dm_search_cb.uuid, p_sdp_rec); + if (p_sdp_rec && SDP_FindProtocolListElemInRec(p_sdp_rec, UUID_PROTOCOL_RFCOMM, &pe)) { + bta_dm_search_cb.peer_scn = (UINT8) pe.params[0]; + scn_found = TRUE; + } + } else { + service = bta_service_id_to_uuid_lkup_tbl[bta_dm_search_cb.service_index - 1]; + p_sdp_rec = SDP_FindServiceInDb(bta_dm_search_cb.p_sdp_db, service, p_sdp_rec); + } +#if BLE_INCLUDED == TRUE && BTA_GATT_INCLUDED == TRUE + /* finished with BR/EDR services, now we check the result for GATT based service UUID */ + if (bta_dm_search_cb.service_index == BTA_MAX_SERVICE_ID) { + if (bta_dm_search_cb.uuid_to_search != 0 && p_uuid != NULL) { + p_uuid += (bta_dm_search_cb.num_uuid - bta_dm_search_cb.uuid_to_search); + /* only support 16 bits UUID for now */ + service = p_uuid->uu.uuid16; + } + /* all GATT based services */ + do { + /* find a service record, report it */ + p_sdp_rec = SDP_FindServiceInDb(bta_dm_search_cb.p_sdp_db, + 0, p_sdp_rec); + if (p_sdp_rec) { + if (SDP_FindServiceUUIDInRec(p_sdp_rec, &service_uuid)) { + /* send result back to app now, one by one */ + bdcpy (result.disc_ble_res.bd_addr, bta_dm_search_cb.peer_bdaddr); + BCM_STRNCPY_S((char *)result.disc_ble_res.bd_name, bta_dm_get_remname(), BD_NAME_LEN); + result.disc_ble_res.bd_name[BD_NAME_LEN] = '\0'; + result.disc_ble_res.service.len = service_uuid.len; + result.disc_ble_res.service.uu.uuid16 = service_uuid.uu.uuid16; + + bta_dm_search_cb.p_search_cback(BTA_DM_DISC_BLE_RES_EVT, &result); + } + } + + if (bta_dm_search_cb.uuid_to_search > 0) { + break; + } + + } while (p_sdp_rec); + } else +#endif + { + /* SDP_DB_FULL means some records with the + required attributes were received */ + if (((p_data->sdp_event.sdp_result == SDP_DB_FULL) && + bta_dm_search_cb.services != BTA_ALL_SERVICE_MASK) || + (p_sdp_rec != NULL)) { + if (service != UUID_SERVCLASS_PNP_INFORMATION && service != 0) { + UINT16 tmp_svc = 0xFFFF; + bta_dm_search_cb.services_found |= + (tBTA_SERVICE_MASK)(BTA_SERVICE_ID_TO_SERVICE_MASK(bta_dm_search_cb.service_index - 1)); + tmp_svc = bta_service_id_to_uuid_lkup_tbl[bta_dm_search_cb.service_index - 1]; + /* Add to the list of UUIDs */ + sdpu_uuid16_to_uuid128(tmp_svc, uuid_list[num_uuids]); + num_uuids++; + } + } + } + + if (bta_dm_search_cb.services == BTA_ALL_SERVICE_MASK && + bta_dm_search_cb.services_to_search == 0) { +#if BLE_INCLUDED == TRUE && BTA_GATT_INCLUDED == TRUE + if ( bta_dm_search_cb.service_index == BTA_BLE_SERVICE_ID && + bta_dm_search_cb.uuid_to_search > 0) { + bta_dm_search_cb.uuid_to_search --; + } + + if (bta_dm_search_cb.uuid_to_search == 0 || + bta_dm_search_cb.service_index != BTA_BLE_SERVICE_ID) +#endif + bta_dm_search_cb.service_index++; + } else { /* regular one service per search or PNP search */ + break; + } + } while (bta_dm_search_cb.service_index <= BTA_MAX_SERVICE_ID); + + APPL_TRACE_DEBUG("%s services_found = %04x", __FUNCTION__, + bta_dm_search_cb.services_found); + + /* Collect the 128-bit services here and put them into the list */ + if (bta_dm_search_cb.services == BTA_ALL_SERVICE_MASK) { + p_sdp_rec = NULL; + do { + tBT_UUID temp_uuid; + /* find a service record, report it */ + p_sdp_rec = SDP_FindServiceInDb_128bit(bta_dm_search_cb.p_sdp_db, p_sdp_rec); + if (p_sdp_rec) { + if (SDP_FindServiceUUIDInRec_128bit(p_sdp_rec, &temp_uuid)) { + memcpy(uuid_list[num_uuids], temp_uuid.uu.uuid128, MAX_UUID_SIZE); + num_uuids++; + } + } + } while (p_sdp_rec); + } + /* if there are more services to search for */ + if (bta_dm_search_cb.services_to_search) { + /* Free up the p_sdp_db before checking the next one */ + bta_dm_free_sdp_db(NULL); + bta_dm_find_services(bta_dm_search_cb.peer_bdaddr); + } else { + /* callbacks */ + /* start next bd_addr if necessary */ + + BTM_SecDeleteRmtNameNotifyCallback(&bta_dm_service_search_remname_cback); + + + if ((p_msg = (tBTA_DM_MSG *) osi_malloc(sizeof(tBTA_DM_MSG))) != NULL) { + p_msg->hdr.event = BTA_DM_DISCOVERY_RESULT_EVT; + p_msg->disc_result.result.disc_res.result = BTA_SUCCESS; + p_msg->disc_result.result.disc_res.p_raw_data = NULL; + p_msg->disc_result.result.disc_res.raw_data_size = 0; + p_msg->disc_result.result.disc_res.num_uuids = num_uuids; + p_msg->disc_result.result.disc_res.p_uuid_list = NULL; + if (num_uuids > 0) { + p_msg->disc_result.result.disc_res.p_uuid_list = (UINT8 *)osi_malloc(num_uuids * MAX_UUID_SIZE); + if (p_msg->disc_result.result.disc_res.p_uuid_list) { + memcpy(p_msg->disc_result.result.disc_res.p_uuid_list, uuid_list, + num_uuids * MAX_UUID_SIZE); + } else { + p_msg->disc_result.result.disc_res.num_uuids = 0; + APPL_TRACE_ERROR("%s: Unable to allocate memory for uuid_list", __func__); + } + } + //copy the raw_data to the discovery result structure + // + + if ( bta_dm_search_cb.p_sdp_db != NULL && bta_dm_search_cb.p_sdp_db->raw_used != 0 && + bta_dm_search_cb.p_sdp_db->raw_data != NULL) { + APPL_TRACE_DEBUG( + "%s raw_data used = 0x%x raw_data_ptr = %p", __func__, + bta_dm_search_cb.p_sdp_db->raw_used, + bta_dm_search_cb.p_sdp_db->raw_data); + + p_msg->disc_result.result.disc_res.p_raw_data = osi_malloc(bta_dm_search_cb.p_sdp_db->raw_used); + if ( NULL != p_msg->disc_result.result.disc_res.p_raw_data ) { + memcpy( p_msg->disc_result.result.disc_res.p_raw_data, + bta_dm_search_cb.p_sdp_db->raw_data, + bta_dm_search_cb.p_sdp_db->raw_used ); + + p_msg->disc_result.result.disc_res.raw_data_size = + bta_dm_search_cb.p_sdp_db->raw_used; + + } else { + APPL_TRACE_DEBUG("%s Alloc failed to allocate %d bytes !!", __func__, + bta_dm_search_cb.p_sdp_db->raw_used); + } + + bta_dm_search_cb.p_sdp_db->raw_data = NULL; //no need to free this - it is a global assigned. + bta_dm_search_cb.p_sdp_db->raw_used = 0; + bta_dm_search_cb.p_sdp_db->raw_size = 0; + } else { + APPL_TRACE_DEBUG("%s raw data size is 0 or raw_data is null!!", __func__); + } + /* Done with p_sdp_db. Free it */ + bta_dm_free_sdp_db(NULL); + p_msg->disc_result.result.disc_res.services = bta_dm_search_cb.services_found; + + //Piggy back the SCN over result field + if ( scn_found ) { + p_msg->disc_result.result.disc_res.result = (3 + bta_dm_search_cb.peer_scn); + p_msg->disc_result.result.disc_res.services |= BTA_USER_SERVICE_MASK; + + APPL_TRACE_EVENT(" Piggy back the SCN over result field SCN=%d", bta_dm_search_cb.peer_scn); + + } + bdcpy (p_msg->disc_result.result.disc_res.bd_addr, bta_dm_search_cb.peer_bdaddr); + BCM_STRNCPY_S((char *)p_msg->disc_result.result.disc_res.bd_name, bta_dm_get_remname(), BD_NAME_LEN); + p_msg->disc_result.result.disc_res.bd_name[BD_NAME_LEN] = '\0'; + + bta_sys_sendmsg(p_msg); + } + } + } else { + /* conn failed. No need for timer */ + if (p_data->sdp_event.sdp_result == SDP_CONN_FAILED || p_data->sdp_event.sdp_result == SDP_CONN_REJECTED + || p_data->sdp_event.sdp_result == SDP_SECURITY_ERR) { + bta_dm_search_cb.wait_disc = FALSE; + } + + /* not able to connect go to next device */ + osi_free(bta_dm_search_cb.p_sdp_db); + bta_dm_search_cb.p_sdp_db = NULL; + + BTM_SecDeleteRmtNameNotifyCallback(&bta_dm_service_search_remname_cback); + + if ((p_msg = (tBTA_DM_MSG *) osi_malloc(sizeof(tBTA_DM_MSG))) != NULL) { + p_msg->hdr.event = BTA_DM_DISCOVERY_RESULT_EVT; + p_msg->disc_result.result.disc_res.result = BTA_FAILURE; + p_msg->disc_result.result.disc_res.services = bta_dm_search_cb.services_found; + bdcpy (p_msg->disc_result.result.disc_res.bd_addr, bta_dm_search_cb.peer_bdaddr); + BCM_STRNCPY_S((char *)p_msg->disc_result.result.disc_res.bd_name, bta_dm_get_remname(), BD_NAME_LEN); + p_msg->disc_result.result.disc_res.bd_name[BD_NAME_LEN] = '\0'; + + bta_sys_sendmsg(p_msg); + } + } +} +#endif ///SDP_INCLUDE == TRUE + +/******************************************************************************* +** +** Function bta_dm_search_cmpl +** +** Description Sends event to application +** +** Returns void +** +*******************************************************************************/ +void bta_dm_search_cmpl (tBTA_DM_MSG *p_data) +{ + APPL_TRACE_EVENT("%s", __func__); + +#if (BLE_INCLUDED == TRUE && BTA_GATT_INCLUDED == TRUE && SDP_INCLUDED == TRUE) + utl_freebuf((void **)&bta_dm_search_cb.p_srvc_uuid); +#endif + + if (p_data->hdr.layer_specific == BTA_DM_API_DI_DISCOVER_EVT) { + #if (SDP_INCLUDED == TRUE) + bta_dm_di_disc_cmpl(p_data); + #endif ///SDP_INCLUDED == TRUE + } else { + bta_dm_search_cb.p_search_cback(BTA_DM_DISC_CMPL_EVT, NULL); + } +} + +/******************************************************************************* +** +** Function bta_dm_disc_result +** +** Description Service discovery result when discovering services on a device +** +** Returns void +** +*******************************************************************************/ +void bta_dm_disc_result (tBTA_DM_MSG *p_data) +{ + APPL_TRACE_EVENT("%s", __func__); + +#if BLE_INCLUDED == TRUE && BTA_GATT_INCLUDED == TRUE + /* if any BR/EDR service discovery has been done, report the event */ + if ((bta_dm_search_cb.services & ((BTA_ALL_SERVICE_MASK | BTA_USER_SERVICE_MASK ) & ~BTA_BLE_SERVICE_MASK))) { + bta_dm_search_cb.p_search_cback(BTA_DM_DISC_RES_EVT, &p_data->disc_result.result); + } +#else + bta_dm_search_cb.p_search_cback(BTA_DM_DISC_RES_EVT, &p_data->disc_result.result); +#endif + tBTA_DM_MSG *p_msg = (tBTA_DM_MSG *) osi_malloc(sizeof(tBTA_DM_MSG)); + + /* send a message to change state */ + if (p_msg != NULL) { + p_msg->hdr.event = BTA_DM_SEARCH_CMPL_EVT; + p_msg->hdr.layer_specific = BTA_DM_API_DISCOVER_EVT; + bta_sys_sendmsg(p_msg); + } +} + +/******************************************************************************* +** +** Function bta_dm_search_result +** +** Description Service discovery result while searching for devices +** +** Returns void +** +*******************************************************************************/ +void bta_dm_search_result (tBTA_DM_MSG *p_data) +{ + APPL_TRACE_DEBUG("%s searching:0x%04x, result:0x%04x", __func__, + bta_dm_search_cb.services, + p_data->disc_result.result.disc_res.services); + + /* call back if application wants name discovery or found services that application is searching */ + if (( !bta_dm_search_cb.services ) + || (( bta_dm_search_cb.services ) && ( p_data->disc_result.result.disc_res.services ))) { + bta_dm_search_cb.p_search_cback(BTA_DM_DISC_RES_EVT, &p_data->disc_result.result); + } + + /* if searching did not initiate to create link */ + if (!bta_dm_search_cb.wait_disc ) { + /* if service searching is done with EIR, don't search next device */ + if ( bta_dm_search_cb.p_btm_inq_info ) { + bta_dm_discover_next_device(); + } + } else { + /* wait until link is disconnected or timeout */ + bta_dm_search_cb.sdp_results = TRUE; + bta_dm_search_cb.search_timer.p_cback = (TIMER_CBACK *)&bta_dm_search_timer_cback; + bta_sys_start_timer(&bta_dm_search_cb.search_timer, 0, 1000 * (L2CAP_LINK_INACTIVITY_TOUT + 1) ); + } + +} + +/******************************************************************************* +** +** Function bta_dm_search_timer_cback +** +** Description Called when ACL disconnect time is over +** +** +** Returns void +** +*******************************************************************************/ +static void bta_dm_search_timer_cback (TIMER_LIST_ENT *p_tle) +{ + UNUSED(p_tle); + + APPL_TRACE_EVENT("%s", __func__); + bta_dm_search_cb.wait_disc = FALSE; + + /* proceed with next device */ + bta_dm_discover_next_device(); + +} + + +/******************************************************************************* +** +** Function bta_dm_free_sdp_db +** +** Description Frees SDP data base +** +** Returns void +** +*******************************************************************************/ +#if (SDP_INCLUDED == TRUE) +void bta_dm_free_sdp_db (tBTA_DM_MSG *p_data) +{ + UNUSED(p_data); + if (bta_dm_search_cb.p_sdp_db) { + osi_free(bta_dm_search_cb.p_sdp_db); + bta_dm_search_cb.p_sdp_db = NULL; + } +} +#endif ///SDP_INCLUDED == TRUE + +/******************************************************************************* +** +** Function bta_dm_queue_search +** +** Description Queues search command while search is being cancelled +** +** Returns void +** +*******************************************************************************/ +void bta_dm_queue_search (tBTA_DM_MSG *p_data) +{ + if (bta_dm_search_cb.p_search_queue) { + osi_free(bta_dm_search_cb.p_search_queue); + } + + bta_dm_search_cb.p_search_queue = (tBTA_DM_MSG *)osi_malloc(sizeof(tBTA_DM_API_SEARCH)); + memcpy(bta_dm_search_cb.p_search_queue, p_data, sizeof(tBTA_DM_API_SEARCH)); + +} + +/******************************************************************************* +** +** Function bta_dm_queue_disc +** +** Description Queues discovery command while search is being cancelled +** +** Returns void +** +*******************************************************************************/ +#if (SDP_INCLUDED == TRUE) +void bta_dm_queue_disc (tBTA_DM_MSG *p_data) +{ + if (bta_dm_search_cb.p_search_queue) { + osi_free(bta_dm_search_cb.p_search_queue); + } + + bta_dm_search_cb.p_search_queue = (tBTA_DM_MSG *)osi_malloc(sizeof(tBTA_DM_API_DISCOVER)); + memcpy(bta_dm_search_cb.p_search_queue, p_data, sizeof(tBTA_DM_API_DISCOVER)); +} +#endif ///SDP_INCLUDED == TRUE + +/******************************************************************************* +** +** Function bta_dm_search_clear_queue +** +** Description Clears the queue if API search cancel is called +** +** Returns void +** +*******************************************************************************/ +void bta_dm_search_clear_queue (tBTA_DM_MSG *p_data) +{ + UNUSED(p_data); + if (bta_dm_search_cb.p_search_queue) { + osi_free(bta_dm_search_cb.p_search_queue); + bta_dm_search_cb.p_search_queue = NULL; + } +} + +/******************************************************************************* +** +** Function bta_dm_search_cancel_cmpl +** +** Description Search cancel is complete +** +** Returns void +** +*******************************************************************************/ +void bta_dm_search_cancel_cmpl (tBTA_DM_MSG *p_data) +{ + UNUSED(p_data); + if (bta_dm_search_cb.p_search_queue) { + bta_sys_sendmsg(bta_dm_search_cb.p_search_queue); + bta_dm_search_cb.p_search_queue = NULL; + } + +} + +/******************************************************************************* +** +** Function bta_dm_search_cancel_transac_cmpl +** +** Description Current Service Discovery or remote name procedure is +** completed after search cancellation +** +** Returns void +** +*******************************************************************************/ +#if (SDP_INCLUDED == TRUE) +void bta_dm_search_cancel_transac_cmpl(tBTA_DM_MSG *p_data) +{ + UNUSED(p_data); + if (bta_dm_search_cb.p_sdp_db) { + osi_free(bta_dm_search_cb.p_sdp_db); + bta_dm_search_cb.p_sdp_db = NULL; + } + + bta_dm_search_cancel_notify(NULL); +} +#endif ///SDP_INCLUDED == TRUE + + +/******************************************************************************* +** +** Function bta_dm_search_cancel_notify +** +** Description Notify application that search has been cancelled +** +** Returns void +** +*******************************************************************************/ +void bta_dm_search_cancel_notify (tBTA_DM_MSG *p_data) +{ + UNUSED(p_data); + if (bta_dm_search_cb.p_search_cback) { + bta_dm_search_cb.p_search_cback(BTA_DM_SEARCH_CANCEL_CMPL_EVT, NULL); + } + if (!bta_dm_search_cb.name_discover_done) { + BTM_CancelRemoteDeviceName(); + } +#if (BLE_INCLUDED == TRUE) && (BTA_GATT_INCLUDED == TRUE) && (SDP_INCLUDED == TRUE) && (GATTC_INCLUDED) == TRUE + if (bta_dm_search_cb.gatt_disc_active) { + bta_dm_cancel_gatt_discovery(bta_dm_search_cb.peer_bdaddr); + } +#endif + +} + +/******************************************************************************* +** +** Function bta_dm_find_services +** +** Description Starts discovery on a device +** +** Returns void +** +*******************************************************************************/ +#if (SDP_INCLUDED == TRUE) +static void bta_dm_find_services ( BD_ADDR bd_addr) +{ + tSDP_UUID uuid; + tBTA_DM_MSG *p_msg; + + memset (&uuid, 0, sizeof(tSDP_UUID)); + + while (bta_dm_search_cb.service_index < BTA_MAX_SERVICE_ID) { + tBTA_SERVICE_MASK this_service_mask = (tBTA_SERVICE_MASK)(BTA_SERVICE_ID_TO_SERVICE_MASK(bta_dm_search_cb.service_index)); + if ( bta_dm_search_cb.services_to_search & this_service_mask) { + if ((bta_dm_search_cb.p_sdp_db = (tSDP_DISCOVERY_DB *)osi_malloc(BTA_DM_SDP_DB_SIZE)) != NULL) { + APPL_TRACE_DEBUG("bta_dm_search_cb.services = %04x***********", bta_dm_search_cb.services); + /* try to search all services by search based on L2CAP UUID */ + if (bta_dm_search_cb.services == BTA_ALL_SERVICE_MASK ) { + APPL_TRACE_DEBUG("%s services_to_search=%08x", __func__, bta_dm_search_cb.services_to_search); + if (bta_dm_search_cb.services_to_search & BTA_RES_SERVICE_MASK) { + uuid.uu.uuid16 = bta_service_id_to_uuid_lkup_tbl[0]; + bta_dm_search_cb.services_to_search &= ~BTA_RES_SERVICE_MASK; + } else { + uuid.uu.uuid16 = UUID_PROTOCOL_L2CAP; + bta_dm_search_cb.services_to_search = 0; + } + } else { +#if BLE_INCLUDED == TRUE && BTA_GATT_INCLUDED == TRUE + /* for LE only profile */ + if (this_service_mask == BTA_BLE_SERVICE_MASK) { + if (bta_dm_search_cb.uuid_to_search > 0 && bta_dm_search_cb.p_srvc_uuid) { + memcpy(&uuid, + (const void *)(bta_dm_search_cb.p_srvc_uuid + \ + bta_dm_search_cb.num_uuid - bta_dm_search_cb.uuid_to_search), + sizeof(tBT_UUID)); + + bta_dm_search_cb.uuid_to_search -- ; + } else { + uuid.uu.uuid16 = bta_service_id_to_uuid_lkup_tbl[bta_dm_search_cb.service_index]; + } + + /* last one? clear the BLE service bit if all discovery has been done */ + if (bta_dm_search_cb.uuid_to_search == 0) { + bta_dm_search_cb.services_to_search &= + (tBTA_SERVICE_MASK)(~(BTA_SERVICE_ID_TO_SERVICE_MASK(bta_dm_search_cb.service_index))); + } + + } else +#endif + { + /* remove the service from services to be searched */ + bta_dm_search_cb.services_to_search &= + (tBTA_SERVICE_MASK)(~(BTA_SERVICE_ID_TO_SERVICE_MASK(bta_dm_search_cb.service_index))); + uuid.uu.uuid16 = bta_service_id_to_uuid_lkup_tbl[bta_dm_search_cb.service_index]; + } + } + + if (uuid.len == 0) { + uuid.len = LEN_UUID_16; + } + + if (this_service_mask == BTA_USER_SERVICE_MASK) { + memcpy(&uuid, &bta_dm_search_cb.uuid, sizeof(tSDP_UUID)); + } + + APPL_TRACE_DEBUG("%s search UUID = %04x", __func__, uuid.uu.uuid16); + SDP_InitDiscoveryDb (bta_dm_search_cb.p_sdp_db, BTA_DM_SDP_DB_SIZE, 1, &uuid, 0, NULL); + + memset(g_disc_raw_data_buf, 0, MAX_DISC_RAW_DATA_BUF); + bta_dm_search_cb.p_sdp_db->raw_data = g_disc_raw_data_buf; + + bta_dm_search_cb.p_sdp_db->raw_size = MAX_DISC_RAW_DATA_BUF; + + if (!SDP_ServiceSearchAttributeRequest (bd_addr, bta_dm_search_cb.p_sdp_db, &bta_dm_sdp_callback)) { + /* if discovery not successful with this device + proceed to next one */ + osi_free(bta_dm_search_cb.p_sdp_db); + bta_dm_search_cb.p_sdp_db = NULL; + bta_dm_search_cb.service_index = BTA_MAX_SERVICE_ID; + + } else { +#if BLE_INCLUDED == TRUE && BTA_GATT_INCLUDED == TRUE + if ((this_service_mask == BTA_BLE_SERVICE_MASK && + bta_dm_search_cb.uuid_to_search == 0) || + this_service_mask != BTA_BLE_SERVICE_MASK) +#endif + bta_dm_search_cb.service_index++; + return; + } + } else { + APPL_TRACE_ERROR("#### Failed to allocate SDP DB buffer! ####"); + } + } + + bta_dm_search_cb.service_index++; + } + + /* no more services to be discovered */ + if (bta_dm_search_cb.service_index >= BTA_MAX_SERVICE_ID) { + if ((p_msg = (tBTA_DM_MSG *) osi_malloc(sizeof(tBTA_DM_MSG))) != NULL) { + p_msg->hdr.event = BTA_DM_DISCOVERY_RESULT_EVT; + p_msg->disc_result.result.disc_res.services = bta_dm_search_cb.services_found; + bdcpy (p_msg->disc_result.result.disc_res.bd_addr, bta_dm_search_cb.peer_bdaddr); + BCM_STRNCPY_S((char *)p_msg->disc_result.result.disc_res.bd_name,bta_dm_get_remname(), BD_NAME_LEN); + p_msg->disc_result.result.disc_res.bd_name[BD_NAME_LEN] = '\0'; + + bta_sys_sendmsg(p_msg); + } + } +} +#endif ///SDP_INCLUDED == TRUE + +/******************************************************************************* +** +** Function bta_dm_discover_next_device +** +** Description Starts discovery on the next device in Inquiry data base +** +** Returns void +** +*******************************************************************************/ +static void bta_dm_discover_next_device(void) +{ + + tBTA_DM_MSG *p_msg; + + APPL_TRACE_DEBUG("bta_dm_discover_next_device"); + + /* searching next device on inquiry result */ + if ((bta_dm_search_cb.p_btm_inq_info = BTM_InqDbNext(bta_dm_search_cb.p_btm_inq_info)) != NULL) { + bta_dm_search_cb.name_discover_done = FALSE; + bta_dm_search_cb.peer_name[0] = 0; +#if (SDP_INCLUDED == TRUE) + bta_dm_discover_device(bta_dm_search_cb.p_btm_inq_info->results.remote_bd_addr); +#endif ///SDP_INCLUDED == TRUE + } else { + /* no devices, search complete */ + bta_dm_search_cb.services = 0; + + if ((p_msg = (tBTA_DM_MSG *) osi_malloc(sizeof(tBTA_DM_MSG))) != NULL) { + p_msg->hdr.event = BTA_DM_SEARCH_CMPL_EVT; + p_msg->hdr.layer_specific = BTA_DM_API_DISCOVER_EVT; + bta_sys_sendmsg(p_msg); + } + } +} + +/******************************************************************************* +** +** Function bta_dm_discover_device +** +** Description Starts name and service discovery on the device +** +** Returns void +** +*******************************************************************************/ +#if (SDP_INCLUDED == TRUE) +static void bta_dm_discover_device(BD_ADDR remote_bd_addr) +{ + tBTA_DM_MSG *p_msg; + tBT_TRANSPORT transport = BT_TRANSPORT_BR_EDR; + +#if BLE_INCLUDED == TRUE + if (bta_dm_search_cb.transport == BTA_TRANSPORT_UNKNOWN) { + tBT_DEVICE_TYPE dev_type; + tBLE_ADDR_TYPE addr_type; + + BTM_ReadDevInfo(remote_bd_addr, &dev_type, &addr_type); + if (dev_type == BT_DEVICE_TYPE_BLE || addr_type == BLE_ADDR_RANDOM) { + transport = BT_TRANSPORT_LE; + } + } else { + transport = bta_dm_search_cb.transport; + } +#endif + + /* Reset transport state for next discovery */ + bta_dm_search_cb.transport = BTA_TRANSPORT_UNKNOWN; + + APPL_TRACE_DEBUG("%s BDA:0x%02X%02X%02X%02X%02X%02X", __func__, + remote_bd_addr[0], remote_bd_addr[1], + remote_bd_addr[2], remote_bd_addr[3], + remote_bd_addr[4], remote_bd_addr[5]); + + bdcpy(bta_dm_search_cb.peer_bdaddr, remote_bd_addr); + + APPL_TRACE_DEBUG("%s name_discover_done = %d p_btm_inq_info %p state = %d, transport=%d", + __func__, + bta_dm_search_cb.name_discover_done, + bta_dm_search_cb.p_btm_inq_info, + bta_dm_search_cb.state, + transport); + + if (bta_dm_search_cb.p_btm_inq_info) { + APPL_TRACE_DEBUG("%s appl_knows_rem_name %d", __func__, + bta_dm_search_cb.p_btm_inq_info->appl_knows_rem_name); + } + + if ((bta_dm_search_cb.p_btm_inq_info) + && (bta_dm_search_cb.p_btm_inq_info->results.device_type == BT_DEVICE_TYPE_BLE) + && (bta_dm_search_cb.state == BTA_DM_SEARCH_ACTIVE)) { + /* Do not perform RNR for LE devices at inquiry complete*/ + bta_dm_search_cb.name_discover_done = TRUE; + } + + /* if name discovery is not done and application needs remote name */ + if ((!bta_dm_search_cb.name_discover_done) + && (( bta_dm_search_cb.p_btm_inq_info == NULL ) + || (bta_dm_search_cb.p_btm_inq_info && (!bta_dm_search_cb.p_btm_inq_info->appl_knows_rem_name)))) { + if (bta_dm_read_remote_device_name(bta_dm_search_cb.peer_bdaddr, transport) == TRUE) { + return; + } + + /* starting name discovery failed */ + bta_dm_search_cb.name_discover_done = TRUE; + } + + /* if application wants to discover service */ + if ( bta_dm_search_cb.services ) { + /* initialize variables */ + bta_dm_search_cb.service_index = 0; + bta_dm_search_cb.services_found = 0; + bta_dm_search_cb.services_to_search = bta_dm_search_cb.services; +#if BLE_INCLUDED == TRUE && BTA_GATT_INCLUDED == TRUE && SDP_INCLUDED == TRUE + bta_dm_search_cb.uuid_to_search = bta_dm_search_cb.num_uuid; +#endif + if ((bta_dm_search_cb.p_btm_inq_info != NULL) && + bta_dm_search_cb.services != BTA_USER_SERVICE_MASK + && (bta_dm_search_cb.sdp_search == FALSE)) { + /* check if EIR provides the information of supported services */ + bta_dm_eir_search_services( &bta_dm_search_cb.p_btm_inq_info->results, + &bta_dm_search_cb.services_to_search, + &bta_dm_search_cb.services_found ); + } + + /* if seaching with EIR is not completed */ + if (bta_dm_search_cb.services_to_search) { + /* check whether connection already exists to the device + if connection exists, we don't have to wait for ACL + link to go down to start search on next device */ + if (BTM_IsAclConnectionUp(bta_dm_search_cb.peer_bdaddr, BT_TRANSPORT_BR_EDR)) { + bta_dm_search_cb.wait_disc = FALSE; + } else { + bta_dm_search_cb.wait_disc = TRUE; + } + +#if (BLE_INCLUDED == TRUE && (defined BTA_GATT_INCLUDED) && (BTA_GATT_INCLUDED == TRUE) && SDP_INCLUDED == TRUE) && (GATTC_INCLUDED == TRUE) + if ( bta_dm_search_cb.p_btm_inq_info ) { + APPL_TRACE_DEBUG("%s p_btm_inq_info %p results.device_type 0x%x services_to_search 0x%x", + __func__, + bta_dm_search_cb.p_btm_inq_info, + bta_dm_search_cb.p_btm_inq_info->results.device_type, + bta_dm_search_cb.services_to_search); + } + + if (transport == BT_TRANSPORT_LE) { + if (bta_dm_search_cb.services_to_search & BTA_BLE_SERVICE_MASK) { + //set the raw data buffer here + memset(g_disc_raw_data_buf, 0, MAX_DISC_RAW_DATA_BUF); + bta_dm_search_cb.p_ble_rawdata = g_disc_raw_data_buf; + + bta_dm_search_cb.ble_raw_size = MAX_DISC_RAW_DATA_BUF; + bta_dm_search_cb.ble_raw_used = 0; + + /* start GATT for service discovery */ + btm_dm_start_gatt_discovery(bta_dm_search_cb.peer_bdaddr); + return; + } + } else +#endif + { + bta_dm_search_cb.sdp_results = FALSE; + bta_dm_find_services(bta_dm_search_cb.peer_bdaddr); + return; + } + } + } + + /* name discovery and service discovery are done for this device */ + if ((p_msg = (tBTA_DM_MSG *) osi_malloc(sizeof(tBTA_DM_MSG))) != NULL) { + p_msg->hdr.event = BTA_DM_DISCOVERY_RESULT_EVT; + /* initialize the data structure - includes p_raw_data and raw_data_size */ + memset(&(p_msg->disc_result.result), 0, sizeof(tBTA_DM_DISC_RES)); + p_msg->disc_result.result.disc_res.result = BTA_SUCCESS; + p_msg->disc_result.result.disc_res.services = bta_dm_search_cb.services_found; + bdcpy (p_msg->disc_result.result.disc_res.bd_addr, bta_dm_search_cb.peer_bdaddr); + BCM_STRNCPY_S((char *)p_msg->disc_result.result.disc_res.bd_name, bta_dm_get_remname(), BD_NAME_LEN); + p_msg->disc_result.result.disc_res.bd_name[BD_NAME_LEN] = '\0'; + + bta_sys_sendmsg(p_msg); + } +} + +/******************************************************************************* +** +** Function bta_dm_sdp_callback +** +** Description Callback from sdp with discovery status +** +** Returns void +** +*******************************************************************************/ +static void bta_dm_sdp_callback (UINT16 sdp_status) +{ + + tBTA_DM_SDP_RESULT *p_msg; + + if ((p_msg = (tBTA_DM_SDP_RESULT *) osi_malloc(sizeof(tBTA_DM_SDP_RESULT))) != NULL) { + p_msg->hdr.event = BTA_DM_SDP_RESULT_EVT; + p_msg->sdp_result = sdp_status; + bta_sys_sendmsg(p_msg); + + } +} +#endif ///SDP_INCLUDED == TRUE +/******************************************************************************* +** +** Function bta_dm_inq_results_cb +** +** Description Inquiry results callback from BTM +** +** Returns void +** +*******************************************************************************/ +static void bta_dm_inq_results_cb (tBTM_INQ_RESULTS *p_inq, UINT8 *p_eir) +{ + + tBTA_DM_SEARCH result; + tBTM_INQ_INFO *p_inq_info; + UINT16 service_class; + + bdcpy(result.inq_res.bd_addr, p_inq->remote_bd_addr); + memcpy(result.inq_res.dev_class, p_inq->dev_class, DEV_CLASS_LEN); + BTM_COD_SERVICE_CLASS(service_class, p_inq->dev_class); + result.inq_res.is_limited = (service_class & BTM_COD_SERVICE_LMTD_DISCOVER) ? TRUE : FALSE; + result.inq_res.rssi = p_inq->rssi; + +#if (BLE_INCLUDED == TRUE) + result.inq_res.ble_addr_type = p_inq->ble_addr_type; + result.inq_res.inq_result_type = p_inq->inq_result_type; + result.inq_res.device_type = p_inq->device_type; + result.inq_res.flag = p_inq->flag; +#endif + + /* application will parse EIR to find out remote device name */ + result.inq_res.p_eir = p_eir; + + if ((p_inq_info = BTM_InqDbRead(p_inq->remote_bd_addr)) != NULL) { + /* initialize remt_name_not_required to FALSE so that we get the name by default */ + result.inq_res.remt_name_not_required = FALSE; + + } + + if (bta_dm_search_cb.p_search_cback) { + bta_dm_search_cb.p_search_cback(BTA_DM_INQ_RES_EVT, &result); + } + + if (p_inq_info) { + /* application indicates if it knows the remote name, inside the callback + copy that to the inquiry data base*/ + if (result.inq_res.remt_name_not_required) { + p_inq_info->appl_knows_rem_name = TRUE; + } + + } + + +} + + +/******************************************************************************* +** +** Function bta_dm_inq_cmpl_cb +** +** Description Inquiry complete callback from BTM +** +** Returns void +** +*******************************************************************************/ +static void bta_dm_inq_cmpl_cb (void *p_result) +{ + tBTA_DM_MSG *p_msg; + + if (bta_dm_search_cb.cancel_pending == FALSE) { + APPL_TRACE_DEBUG("%s", __FUNCTION__); + p_msg = (tBTA_DM_MSG *) osi_malloc(sizeof(tBTA_DM_MSG)); + if (p_msg != NULL) { + p_msg->inq_cmpl.hdr.event = BTA_DM_INQUIRY_CMPL_EVT; + p_msg->inq_cmpl.num = ((tBTM_INQUIRY_CMPL *)p_result)->num_resp; + bta_sys_sendmsg(p_msg); + } + } else { + bta_dm_search_cb.cancel_pending = FALSE; + bta_dm_search_cancel_notify(NULL); + + p_msg = (tBTA_DM_MSG *) osi_malloc(sizeof(tBTA_DM_MSG)); + if (p_msg != NULL) { + p_msg->hdr.event = BTA_DM_SEARCH_CMPL_EVT; + p_msg->hdr.layer_specific = BTA_DM_API_DISCOVER_EVT; + bta_sys_sendmsg(p_msg); + } + } +} + +/******************************************************************************* +** +** Function bta_dm_service_search_remname_cback +** +** Description Remote name call back from BTM during service discovery +** +** Returns void +** +*******************************************************************************/ +static void bta_dm_service_search_remname_cback (BD_ADDR bd_addr, DEV_CLASS dc, BD_NAME bd_name) +{ + tBTM_REMOTE_DEV_NAME rem_name; + tBTM_STATUS btm_status; + UNUSED(dc); + + APPL_TRACE_DEBUG("bta_dm_service_search_remname_cback name=<%s>", bd_name); + + /* if this is what we are looking for */ + if (!bdcmp( bta_dm_search_cb.peer_bdaddr, bd_addr)) { + rem_name.length = strlen((char *)bd_name); + if (rem_name.length > BD_NAME_LEN) { + rem_name.length = BD_NAME_LEN; + } + BCM_STRNCPY_S((char *)rem_name.remote_bd_name, (char *)bd_name, BD_NAME_LEN); + rem_name.remote_bd_name[BD_NAME_LEN] = '\0'; + rem_name.status = BTM_SUCCESS; + + bta_dm_remname_cback(&rem_name); + } else { + /* get name of device */ + btm_status = BTM_ReadRemoteDeviceName (bta_dm_search_cb.peer_bdaddr, + (tBTM_CMPL_CB *) bta_dm_remname_cback, + BT_TRANSPORT_BR_EDR); + if ( btm_status == BTM_BUSY ) { + /* wait for next chance(notification of remote name discovery done) */ + APPL_TRACE_DEBUG("bta_dm_service_search_remname_cback: BTM_ReadRemoteDeviceName is busy"); + } else if ( btm_status != BTM_CMD_STARTED ) { + /* if failed to start getting remote name then continue */ + APPL_TRACE_WARNING("bta_dm_service_search_remname_cback: BTM_ReadRemoteDeviceName returns 0x%02X", btm_status); + + rem_name.length = 0; + rem_name.remote_bd_name[0] = '\0'; + rem_name.status = btm_status; + bta_dm_remname_cback(&rem_name); + } + } +} + + +/******************************************************************************* +** +** Function bta_dm_remname_cback +** +** Description Remote name complete call back from BTM +** +** Returns void +** +*******************************************************************************/ +static void bta_dm_remname_cback (tBTM_REMOTE_DEV_NAME *p_remote_name) +{ + tBTA_DM_REM_NAME *p_msg; + + APPL_TRACE_DEBUG("bta_dm_remname_cback len = %d name=<%s>", p_remote_name->length, + p_remote_name->remote_bd_name); + /* remote name discovery is done but it could be failed */ + bta_dm_search_cb.name_discover_done = TRUE; + BCM_STRNCPY_S((char *)bta_dm_search_cb.peer_name, (char *)p_remote_name->remote_bd_name, BD_NAME_LEN); + bta_dm_search_cb.peer_name[BD_NAME_LEN] = '\0'; + + BTM_SecDeleteRmtNameNotifyCallback(&bta_dm_service_search_remname_cback); + +#if BLE_INCLUDED == TRUE && GATTS_INCLUDED == TRUE + if (bta_dm_search_cb.transport == BT_TRANSPORT_LE ) { + GAP_BleReadPeerPrefConnParams (bta_dm_search_cb.peer_bdaddr); + } +#endif + + if ((p_msg = (tBTA_DM_REM_NAME *) osi_malloc(sizeof(tBTA_DM_REM_NAME))) != NULL) { + bdcpy (p_msg->result.disc_res.bd_addr, bta_dm_search_cb.peer_bdaddr); + BCM_STRNCPY_S((char *)p_msg->result.disc_res.bd_name, (char *)p_remote_name->remote_bd_name, BD_NAME_LEN); + p_msg->result.disc_res.bd_name[BD_NAME_LEN] = '\0'; + + p_msg->hdr.event = BTA_DM_REMT_NAME_EVT; + bta_sys_sendmsg(p_msg); + } +} + +/******************************************************************************* +** +** Function bta_dm_authorize_cback +** +** Description cback requesting authorization +** +** Returns void +** +*******************************************************************************/ +#if (SMP_INCLUDED == TRUE) +static UINT8 bta_dm_authorize_cback (BD_ADDR bd_addr, DEV_CLASS dev_class, BD_NAME bd_name, + UINT8 *service_name, UINT8 service_id, BOOLEAN is_originator) +{ + tBTA_DM_SEC sec_event; + UINT8 index = 1; + UNUSED(service_name); + UNUSED(is_originator); + + bdcpy(sec_event.authorize.bd_addr, bd_addr); + memcpy(sec_event.authorize.dev_class, dev_class, DEV_CLASS_LEN); + + BCM_STRNCPY_S((char *)sec_event.authorize.bd_name, (char *)bd_name, BD_NAME_LEN); + sec_event.authorize.bd_name[BD_NAME_LEN] = '\0'; + +#if ( defined(BTA_JV_INCLUDED) && BTA_JV_INCLUDED == TRUE ) + sec_event.authorize.service = service_id; +#endif + + while (index < BTA_MAX_SERVICE_ID) { + /* get the BTA service id corresponding to BTM id */ + if (bta_service_id_to_btm_srv_id_lkup_tbl[index] == service_id) { + sec_event.authorize.service = index; + break; + } + index++; + } + + + /* if supported service callback otherwise not authorized */ + if (bta_dm_cb.p_sec_cback && (index < BTA_MAX_SERVICE_ID +#if ( defined(BTA_JV_INCLUDED) && BTA_JV_INCLUDED == TRUE ) + /* pass through JV service ID */ + || (service_id >= BTA_FIRST_JV_SERVICE_ID && service_id <= BTA_LAST_JV_SERVICE_ID) +#endif + )) { + bta_dm_cb.p_sec_cback(BTA_DM_AUTHORIZE_EVT, &sec_event); + return BTM_CMD_STARTED; + } else { + return BTM_NOT_AUTHORIZED; + } +} + + +#if (CLASSIC_BT_INCLUDED == TRUE) +/******************************************************************************* +** +** Function bta_dm_pinname_cback +** +** Description Callback requesting pin_key +** +** Returns void +** +*******************************************************************************/ + static void bta_dm_pinname_cback (void *p_data) +{ + tBTM_REMOTE_DEV_NAME *p_result = (tBTM_REMOTE_DEV_NAME *)p_data; + tBTA_DM_SEC sec_event; + tBTA_DM_SEC_EVT event = bta_dm_cb.pin_evt; + + if (BTA_DM_SP_CFM_REQ_EVT == event) { + /* Retrieved saved device class and bd_addr */ + bdcpy(sec_event.cfm_req.bd_addr, bta_dm_cb.pin_bd_addr); + BTA_COPY_DEVICE_CLASS(sec_event.cfm_req.dev_class, bta_dm_cb.pin_dev_class); + + if (p_result && p_result->status == BTM_SUCCESS) { + BCM_STRNCPY_S((char *)sec_event.cfm_req.bd_name, (char *)p_result->remote_bd_name, BD_NAME_LEN); + sec_event.cfm_req.bd_name[BD_NAME_LEN] = '\0'; + } else { /* No name found */ + sec_event.cfm_req.bd_name[0] = '\0'; + } + + sec_event.cfm_req.num_val = bta_dm_cb.num_val; /* get PIN code numeric number */ + + /* 1 additional event data fields for this event */ + sec_event.cfm_req.just_works = bta_dm_cb.just_works; + } else { + /* Retrieved saved device class and bd_addr */ + bdcpy(sec_event.pin_req.bd_addr, bta_dm_cb.pin_bd_addr); + BTA_COPY_DEVICE_CLASS(sec_event.pin_req.dev_class, bta_dm_cb.pin_dev_class); + + if (p_result && p_result->status == BTM_SUCCESS) { + BCM_STRNCPY_S((char *)sec_event.pin_req.bd_name, (char *)p_result->remote_bd_name, BD_NAME_LEN); + sec_event.pin_req.bd_name[BD_NAME_LEN] = '\0'; + } else { /* No name found */ + sec_event.pin_req.bd_name[0] = '\0'; + } + + event = bta_dm_cb.pin_evt; + sec_event.key_notif.passkey = bta_dm_cb.num_val; /* get PIN code numeric number */ + } + + if ( bta_dm_cb.p_sec_cback ) { + bta_dm_cb.p_sec_cback(event, &sec_event); + } +} + +/******************************************************************************* +** +** Function bta_dm_pin_cback +** +** Description Callback requesting pin_key +** +** Returns void +** +*******************************************************************************/ +static UINT8 bta_dm_pin_cback (BD_ADDR bd_addr, DEV_CLASS dev_class, BD_NAME bd_name, + BOOLEAN min_16_digit) +{ + tBTA_DM_SEC sec_event; + + if (!bta_dm_cb.p_sec_cback) { + return BTM_NOT_AUTHORIZED; + } + + bdcpy(sec_event.pin_req.bd_addr, bd_addr); + BTA_COPY_DEVICE_CLASS(sec_event.pin_req.dev_class, dev_class); + BCM_STRNCPY_S((char *)sec_event.pin_req.bd_name, (char *)bd_name, BD_NAME_LEN); + sec_event.pin_req.bd_name[BD_NAME_LEN] = '\0'; + sec_event.pin_req.min_16_digit = min_16_digit; + + bta_dm_cb.p_sec_cback(BTA_DM_PIN_REQ_EVT, &sec_event); + return BTM_CMD_STARTED; +} + +/******************************************************************************* +** +** Function bta_dm_new_link_key_cback +** +** Description Callback from BTM to notify new link key +** +** Returns void +** +*******************************************************************************/ +static void bta_dm_encryption_change_cback(BD_ADDR bd_addr, UINT8 enc_mode) +{ + if (bta_dm_cb.p_sec_cback) { + tBTA_DM_SEC sec_event; + memset (&sec_event, 0, sizeof(tBTA_DM_SEC)); + bdcpy(sec_event.enc_chg.bd_addr, bd_addr); + sec_event.enc_chg.enc_mode = enc_mode; + + bta_dm_cb.p_sec_cback(BTA_DM_ENC_CHG_EVT, &sec_event); + } +} +#endif ///CLASSIC_BT_INCLUDED == TRUE + +/******************************************************************************* +** +** Function bta_dm_new_link_key_cback +** +** Description Callback from BTM to notify new link key +** +** Returns void +** +*******************************************************************************/ +static UINT8 bta_dm_new_link_key_cback(BD_ADDR bd_addr, DEV_CLASS dev_class, + BD_NAME bd_name, LINK_KEY key, UINT8 key_type, + BOOLEAN sc_support) +{ + tBTA_DM_SEC sec_event; + tBTA_DM_AUTH_CMPL *p_auth_cmpl; + UINT8 event; + UNUSED(dev_class); + + memset (&sec_event, 0, sizeof(tBTA_DM_SEC)); + + /* Not AMP Key type */ + if (key_type != HCI_LKEY_TYPE_AMP_WIFI && key_type != HCI_LKEY_TYPE_AMP_UWB) { + event = BTA_DM_AUTH_CMPL_EVT; + p_auth_cmpl = &sec_event.auth_cmpl; + + bdcpy(p_auth_cmpl->bd_addr, bd_addr); + + memcpy(p_auth_cmpl->bd_name, bd_name, (BD_NAME_LEN - 1)); + p_auth_cmpl->bd_name[BD_NAME_LEN - 1] = 0; + + p_auth_cmpl->key_present = TRUE; + p_auth_cmpl->key_type = key_type; + p_auth_cmpl->success = TRUE; + p_auth_cmpl->sc_support = sc_support; + + memcpy(p_auth_cmpl->key, key, LINK_KEY_LEN); + sec_event.auth_cmpl.fail_reason = HCI_SUCCESS; + +#if BLE_INCLUDED == TRUE + // Report the BR link key based on the BR/EDR address and type + BTM_ReadDevInfo(bd_addr, &sec_event.auth_cmpl.dev_type, &sec_event.auth_cmpl.addr_type); +#endif + if (bta_dm_cb.p_sec_cback) { + bta_dm_cb.p_sec_cback(event, &sec_event); + } + } else { + APPL_TRACE_WARNING("%s() Received AMP Key", __func__); + } + + return BTM_CMD_STARTED; +} + + +/******************************************************************************* +** +** Function bta_dm_authentication_complete_cback +** +** Description Authentication complete callback from BTM +** +** Returns void +** +*******************************************************************************/ +static UINT8 bta_dm_authentication_complete_cback(BD_ADDR bd_addr, DEV_CLASS dev_class, BD_NAME bd_name, int result) +{ + tBTA_DM_SEC sec_event; + UNUSED(dev_class); + + if (result != BTM_SUCCESS) { + memset(&sec_event, 0, sizeof(tBTA_DM_SEC)); + bdcpy(sec_event.auth_cmpl.bd_addr, bd_addr); + + memcpy(sec_event.auth_cmpl.bd_name, bd_name, (BD_NAME_LEN - 1)); + sec_event.auth_cmpl.bd_name[BD_NAME_LEN - 1] = 0; + +#if BLE_INCLUDED == TRUE + // Report the BR link key based on the BR/EDR address and type + BTM_ReadDevInfo(bd_addr, &sec_event.auth_cmpl.dev_type, &sec_event.auth_cmpl.addr_type); +#endif + sec_event.auth_cmpl.fail_reason = (UINT8)result; + + if (bta_dm_cb.p_sec_cback) { + bta_dm_cb.p_sec_cback(BTA_DM_AUTH_CMPL_EVT, &sec_event); + } + + if (bta_dm_remove_sec_dev_entry(bd_addr)) { + return BTM_SEC_DEV_REC_REMOVED; + } + } + + return BTM_SUCCESS; +} + +#if (CLASSIC_BT_INCLUDED == TRUE) +/******************************************************************************* +** +** Function bta_dm_sp_cback +** +** Description simple pairing callback from BTM +** +** Returns void +** +*******************************************************************************/ +static UINT8 bta_dm_sp_cback (tBTM_SP_EVT event, tBTM_SP_EVT_DATA *p_data) +{ + tBTM_STATUS status = BTM_CMD_STARTED; + tBTA_DM_SEC sec_event; + tBTA_DM_SEC_EVT pin_evt = BTA_DM_SP_KEY_NOTIF_EVT; + + APPL_TRACE_EVENT("bta_dm_sp_cback: %d", event); + if (!bta_dm_cb.p_sec_cback) { + return BTM_NOT_AUTHORIZED; + } + + /* TODO_SP */ + switch (event) { + case BTM_SP_IO_REQ_EVT: + /* translate auth_req */ + bta_dm_co_io_req(p_data->io_req.bd_addr, &p_data->io_req.io_cap, + &p_data->io_req.oob_data, &p_data->io_req.auth_req, p_data->io_req.is_orig); +#if BTM_OOB_INCLUDED == FALSE + status = BTM_SUCCESS; +#endif + + APPL_TRACE_EVENT("io mitm: %d oob_data:%d", p_data->io_req.auth_req, p_data->io_req.oob_data); + break; + case BTM_SP_IO_RSP_EVT: + bta_dm_co_io_rsp(p_data->io_rsp.bd_addr, p_data->io_rsp.io_cap, + p_data->io_rsp.oob_data, p_data->io_rsp.auth_req ); + break; + + case BTM_SP_CFM_REQ_EVT: + pin_evt = BTA_DM_SP_CFM_REQ_EVT; + bta_dm_cb.just_works = sec_event.cfm_req.just_works = p_data->cfm_req.just_works; + sec_event.cfm_req.loc_auth_req = p_data->cfm_req.loc_auth_req; + sec_event.cfm_req.rmt_auth_req = p_data->cfm_req.rmt_auth_req; + sec_event.cfm_req.loc_io_caps = p_data->cfm_req.loc_io_caps; + sec_event.cfm_req.rmt_io_caps = p_data->cfm_req.rmt_io_caps; + + /* continue to next case */ + /* Passkey entry mode, mobile device with output capability is very + unlikely to receive key request, so skip this event */ + case BTM_SP_KEY_REQ_EVT: + case BTM_SP_KEY_NOTIF_EVT: + if (BTM_SP_CFM_REQ_EVT == event) { + /* Due to the switch case falling through below to BTM_SP_KEY_NOTIF_EVT, + call remote name request using values from cfm_req */ + if (p_data->cfm_req.bd_name[0] == 0) { + bta_dm_cb.pin_evt = pin_evt; + bdcpy(bta_dm_cb.pin_bd_addr, p_data->cfm_req.bd_addr); + BTA_COPY_DEVICE_CLASS(bta_dm_cb.pin_dev_class, p_data->cfm_req.dev_class); + if ((BTM_ReadRemoteDeviceName(p_data->cfm_req.bd_addr, bta_dm_pinname_cback, + BT_TRANSPORT_BR_EDR)) == BTM_CMD_STARTED) { + return BTM_CMD_STARTED; + } + APPL_TRACE_WARNING(" bta_dm_sp_cback() -> Failed to start Remote Name Request "); + } else { + /* Due to the switch case falling through below to BTM_SP_KEY_NOTIF_EVT, + copy these values into key_notif from cfm_req */ + bdcpy(sec_event.key_notif.bd_addr, p_data->cfm_req.bd_addr); + BTA_COPY_DEVICE_CLASS(sec_event.key_notif.dev_class, p_data->cfm_req.dev_class); + BCM_STRNCPY_S((char *)sec_event.key_notif.bd_name, (char *)p_data->cfm_req.bd_name, BD_NAME_LEN); + sec_event.key_notif.bd_name[BD_NAME_LEN] = '\0'; + } + } + + bta_dm_cb.num_val = sec_event.key_notif.passkey = p_data->key_notif.passkey; + if (BTM_SP_KEY_NOTIF_EVT == event) { + /* If the device name is not known, save bdaddr and devclass + and initiate a name request with values from key_notif */ + if (p_data->key_notif.bd_name[0] == 0) { + bta_dm_cb.pin_evt = pin_evt; + bdcpy(bta_dm_cb.pin_bd_addr, p_data->key_notif.bd_addr); + BTA_COPY_DEVICE_CLASS(bta_dm_cb.pin_dev_class, p_data->key_notif.dev_class); + if ((BTM_ReadRemoteDeviceName(p_data->key_notif.bd_addr, bta_dm_pinname_cback, + BT_TRANSPORT_BR_EDR)) == BTM_CMD_STARTED) { + return BTM_CMD_STARTED; + } + APPL_TRACE_WARNING(" bta_dm_sp_cback() -> Failed to start Remote Name Request "); + } else { + bdcpy(sec_event.key_notif.bd_addr, p_data->key_notif.bd_addr); + BTA_COPY_DEVICE_CLASS(sec_event.key_notif.dev_class, p_data->key_notif.dev_class); + BCM_STRNCPY_S((char *)sec_event.key_notif.bd_name, (char *)p_data->key_notif.bd_name, BD_NAME_LEN); + sec_event.key_notif.bd_name[BD_NAME_LEN] = '\0'; + } + } + + if (BTM_SP_KEY_REQ_EVT == event) { + pin_evt = BTA_DM_SP_KEY_REQ_EVT; + /* If the device name is not known, save bdaddr and devclass + and initiate a name request with values from key_notif */ + if (p_data->key_notif.bd_name[0] == 0) { + bta_dm_cb.pin_evt = pin_evt; + bdcpy(bta_dm_cb.pin_bd_addr, p_data->key_notif.bd_addr); + BTA_COPY_DEVICE_CLASS(bta_dm_cb.pin_dev_class, p_data->key_notif.dev_class); + if ((BTM_ReadRemoteDeviceName(p_data->key_notif.bd_addr, bta_dm_pinname_cback, + BT_TRANSPORT_BR_EDR)) == BTM_CMD_STARTED) { + return BTM_CMD_STARTED; + } + APPL_TRACE_WARNING(" bta_dm_sp_cback() -> Failed to start Remote Name Request "); + } else { + bdcpy(sec_event.key_notif.bd_addr, p_data->key_notif.bd_addr); + BTA_COPY_DEVICE_CLASS(sec_event.key_notif.dev_class, p_data->key_notif.dev_class); + BCM_STRNCPY_S((char *)sec_event.key_notif.bd_name,(char *)p_data->key_notif.bd_name, BD_NAME_LEN); + sec_event.key_notif.bd_name[BD_NAME_LEN] = '\0'; + } + } + bta_dm_cb.p_sec_cback(pin_evt, &sec_event); + + break; + +#if BTM_OOB_INCLUDED == TRUE + case BTM_SP_LOC_OOB_EVT: + bta_dm_co_loc_oob((BOOLEAN)(p_data->loc_oob.status == BTM_SUCCESS), + p_data->loc_oob.c, p_data->loc_oob.r); + break; + + case BTM_SP_RMT_OOB_EVT: + /* If the device name is not known, save bdaddr and devclass and initiate a name request */ + if (p_data->rmt_oob.bd_name[0] == 0) { + bta_dm_cb.pin_evt = BTA_DM_SP_RMT_OOB_EVT; + bdcpy(bta_dm_cb.pin_bd_addr, p_data->rmt_oob.bd_addr); + BTA_COPY_DEVICE_CLASS(bta_dm_cb.pin_dev_class, p_data->rmt_oob.dev_class); + if ((BTM_ReadRemoteDeviceName(p_data->rmt_oob.bd_addr, bta_dm_pinname_cback, + BT_TRANSPORT_BR_EDR)) == BTM_CMD_STARTED) { + return BTM_CMD_STARTED; + } + APPL_TRACE_WARNING(" bta_dm_sp_cback() -> Failed to start Remote Name Request "); + } + + bdcpy(sec_event.rmt_oob.bd_addr, p_data->rmt_oob.bd_addr); + BTA_COPY_DEVICE_CLASS(sec_event.rmt_oob.dev_class, p_data->rmt_oob.dev_class); + BCM_STRNCPY_S((char *)sec_event.rmt_oob.bd_name, (char *)p_data->rmt_oob.bd_name, BD_NAME_LEN); + sec_event.rmt_oob.bd_name[BD_NAME_LEN] = '\0'; + + bta_dm_cb.p_sec_cback(BTA_DM_SP_RMT_OOB_EVT, &sec_event); + + bta_dm_co_rmt_oob(p_data->rmt_oob.bd_addr); + break; +#endif + case BTM_SP_COMPLT_EVT: + /* do not report this event - handled by link_key_callback or auth_complete_callback */ + break; + + case BTM_SP_KEYPRESS_EVT: + memcpy(&sec_event.key_press, &p_data->key_press, sizeof(tBTM_SP_KEYPRESS)); + bta_dm_cb.p_sec_cback(BTA_DM_SP_KEYPRESS_EVT, &sec_event); + break; + + case BTM_SP_UPGRADE_EVT: + bta_dm_co_lk_upgrade(p_data->upgrade.bd_addr, &p_data->upgrade.upgrade ); + break; + + default: + status = BTM_NOT_AUTHORIZED; + break; + } + APPL_TRACE_EVENT("dm status: %d", status); + return status; +} +#endif /* (CLASSIC_BT_INCLUDED == TRUE) */ + +#endif ///SMP_INCLUDED == TRUE + + +/******************************************************************************* +** +** Function bta_dm_local_name_cback +** +** Description Callback from btm after local name is read +** +** +** Returns void +** +*******************************************************************************/ +static void bta_dm_local_name_cback(const BD_ADDR bd_addr) +{ + tBTA_DM_SEC sec_event; + UNUSED(bd_addr); + + sec_event.enable.status = BTA_SUCCESS; + + if (bta_dm_cb.p_sec_cback) { + bta_dm_cb.p_sec_cback(BTA_DM_ENABLE_EVT, &sec_event); + } + +} + +/******************************************************************************* +** +** Function bta_dm_bl_change_cback +** +** Description Callback from btm when acl connection goes up or down +** +** +** Returns void +** +*******************************************************************************/ +static void bta_dm_bl_change_cback (tBTM_BL_EVENT_DATA *p_data) +{ + tBTA_DM_ACL_CHANGE *p_msg; + + if ((p_msg = (tBTA_DM_ACL_CHANGE *) osi_malloc(sizeof(tBTA_DM_ACL_CHANGE))) != NULL) { + p_msg->event = p_data->event; + p_msg->is_new = FALSE; + + switch (p_msg->event) { + case BTM_BL_CONN_EVT: + p_msg->sc_downgrade = p_data->conn.sc_downgrade; + p_msg->is_new = TRUE; + bdcpy(p_msg->bd_addr, p_data->conn.p_bda); +#if BLE_INCLUDED == TRUE + p_msg->transport = p_data->conn.transport; + p_msg->handle = p_data->conn.handle; +#endif + break; + case BTM_BL_DISCN_EVT: + bdcpy(p_msg->bd_addr, p_data->discn.p_bda); +#if BLE_INCLUDED == TRUE + p_msg->transport = p_data->discn.transport; + p_msg->handle = p_data->discn.handle; +#endif + break; + case BTM_BL_UPDATE_EVT: + p_msg->busy_level = p_data->update.busy_level; + p_msg->busy_level_flags = p_data->update.busy_level_flags; + break; + case BTM_BL_ROLE_CHG_EVT: + p_msg->new_role = p_data->role_chg.new_role; + p_msg->hci_status = p_data->role_chg.hci_status; + bdcpy(p_msg->bd_addr, p_data->role_chg.p_bda); + break; + case BTM_BL_COLLISION_EVT: + bdcpy(p_msg->bd_addr, p_data->conn.p_bda); + break; + } + + p_msg->hdr.event = BTA_DM_ACL_CHANGE_EVT; + bta_sys_sendmsg(p_msg); + + } + +} + +/******************************************************************************* +** +** Function bta_dm_acl_link_stat_cback +** +** Description Callback from btm to report acl link status +** +** Returns void +** +*******************************************************************************/ +static void bta_dm_acl_link_stat_cback(tBTM_ACL_LINK_STAT_EVENT_DATA *p_data) +{ + tBTA_DM_SEC sec_event; + memset(&sec_event, 0, sizeof(tBTA_DM_SEC)); + sec_event.acl_link_stat.event = p_data->event; + + switch (sec_event.acl_link_stat.event) { + case BTA_ACL_LINK_STAT_CONN_CMPL: { + sec_event.acl_link_stat.link_act.conn_cmpl.status = p_data->link_act.conn_cmpl.status; + sec_event.acl_link_stat.link_act.conn_cmpl.handle = p_data->link_act.conn_cmpl.handle; + bdcpy(sec_event.acl_link_stat.link_act.conn_cmpl.bd_addr, p_data->link_act.conn_cmpl.bd_addr); + break; + } + case BTA_ACL_LINK_STAT_DISCONN_CMPL: { + sec_event.acl_link_stat.link_act.disconn_cmpl.reason = p_data->link_act.disconn_cmpl.reason; + sec_event.acl_link_stat.link_act.disconn_cmpl.handle = p_data->link_act.disconn_cmpl.handle; + bdcpy(sec_event.acl_link_stat.link_act.disconn_cmpl.bd_addr, p_data->link_act.disconn_cmpl.bd_addr); + break; + } + default: { + APPL_TRACE_WARNING("bta_dm_acl_link_stat: invalid event %d", sec_event.acl_link_stat.event); + return; + } + } + + if (bta_dm_cb.p_sec_cback) { + (*bta_dm_cb.p_sec_cback)(BTA_DM_ACL_LINK_STAT_EVT, &sec_event); + } +} + +/******************************************************************************* +** +** Function bta_dm_rs_cback +** +** Description Receives the role switch complete event +** +** Returns +** +*******************************************************************************/ +static void bta_dm_rs_cback (tBTM_ROLE_SWITCH_CMPL *p1) +{ + UNUSED(p1); + APPL_TRACE_WARNING("bta_dm_rs_cback:%d", bta_dm_cb.rs_event); + if (bta_dm_cb.rs_event == BTA_DM_API_SEARCH_EVT) { + bta_dm_cb.search_msg.rs_res = BTA_DM_RS_OK; /* do not care about the result for now */ + bta_dm_cb.rs_event = 0; + bta_dm_search_start((tBTA_DM_MSG *)&bta_dm_cb.search_msg); + } +} + +/******************************************************************************* +** +** Function bta_dm_check_av +** +** Description This function checks if AV is active +** if yes, make sure the AV link is master +** +** Returns BOOLEAN - TRUE, if switch is in progress +** +*******************************************************************************/ +static BOOLEAN bta_dm_check_av(UINT16 event) +{ + BOOLEAN avoid_roleswitch = FALSE; + BOOLEAN switching = FALSE; + UINT8 i; + tBTA_DM_PEER_DEVICE *p_dev; + +#if defined(BTA_DM_AVOID_A2DP_ROLESWITCH_ON_INQUIRY) && (BTA_DM_AVOID_A2DP_ROLESWITCH_ON_INQUIRY == TRUE) + + /* avoid role switch upon inquiry if a2dp is actively streaming as it + introduces an audioglitch due to FW scheduling delays (unavoidable) */ + if (event == BTA_DM_API_SEARCH_EVT) { + avoid_roleswitch = TRUE; + } +#endif + + APPL_TRACE_EVENT("bta_dm_check_av:%d", bta_dm_cb.cur_av_count); + if (bta_dm_cb.cur_av_count) { + for (i = 0; i < bta_dm_cb.device_list.count; i++) { + p_dev = &bta_dm_cb.device_list.peer_device[i]; + APPL_TRACE_WARNING("[%d]: state:%d, info:x%x, avoid_rs %d", + i, p_dev->conn_state, p_dev->info, avoid_roleswitch); + if ((p_dev->conn_state == BTA_DM_CONNECTED) && (p_dev->info & BTA_DM_DI_AV_ACTIVE) && + (avoid_roleswitch == FALSE)) { + /* make master and take away the role switch policy */ + if (BTM_CMD_STARTED == BTM_SwitchRole (p_dev->peer_bdaddr, HCI_ROLE_MASTER, (tBTM_CMPL_CB *)bta_dm_rs_cback)) { + /* the role switch command is actually sent */ + bta_dm_cb.rs_event = event; + switching = TRUE; + } + /* else either already master or can not switch for some reasons */ + bta_dm_policy_cback(BTA_SYS_PLCY_CLR, 0, HCI_ENABLE_MASTER_SLAVE_SWITCH, p_dev->peer_bdaddr); + break; + } + } + } + return switching; +} + +/******************************************************************************* +** +** Function bta_dm_acl_change +** +** Description Process BTA_DM_ACL_CHANGE_EVT +** +** +** Returns void +** +*******************************************************************************/ +void bta_dm_acl_change(tBTA_DM_MSG *p_data) +{ + + UINT8 i; + UINT8 *p; + tBTA_DM_SEC conn; + BOOLEAN is_new = p_data->acl_change.is_new; + BD_ADDR_PTR p_bda = p_data->acl_change.bd_addr; + BOOLEAN need_policy_change = FALSE; + BOOLEAN issue_unpair_cb = FALSE; + + tBTA_DM_PEER_DEVICE *p_dev; + memset(&conn, 0, sizeof(tBTA_DM_SEC)); + + switch (p_data->acl_change.event) { + case BTM_BL_UPDATE_EVT: /* busy level update */ + if ( bta_dm_cb.p_sec_cback ) { + conn.busy_level.level = p_data->acl_change.busy_level; + conn.busy_level.level_flags = p_data->acl_change.busy_level_flags; + bta_dm_cb.p_sec_cback(BTA_DM_BUSY_LEVEL_EVT, &conn); + } + return; + + case BTM_BL_ROLE_CHG_EVT: /* role change event */ + p_dev = bta_dm_find_peer_device(p_bda); + if (p_dev) { + APPL_TRACE_DEBUG("bta_dm_acl_change role chg info:x%x new_role:%d dev count:%d", + p_dev->info, p_data->acl_change.new_role, bta_dm_cb.device_list.count); + if (p_dev->info & BTA_DM_DI_AV_ACTIVE) { + /* there's AV activity on this link */ + if (p_data->acl_change.new_role == HCI_ROLE_SLAVE && bta_dm_cb.device_list.count > 1 + && p_data->acl_change.hci_status == HCI_SUCCESS) { + /* more than one connections and the AV connection is role switched to slave + * switch it back to master and remove the switch policy */ + BTM_SwitchRole(p_bda, BTM_ROLE_MASTER, NULL); + need_policy_change = TRUE; + } else if (p_bta_dm_cfg->avoid_scatter && (p_data->acl_change.new_role == HCI_ROLE_MASTER)) { + /* if the link updated to be master include AV activities, remove the switch policy */ + need_policy_change = TRUE; + } + + if (need_policy_change) { + bta_dm_policy_cback(BTA_SYS_PLCY_CLR, 0, HCI_ENABLE_MASTER_SLAVE_SWITCH, p_dev->peer_bdaddr); + } + } else { + /* there's AV no activity on this link and role switch happened + * check if AV is active + * if so, make sure the AV link is master */ + bta_dm_check_av(0); + } + bta_sys_notify_role_chg(p_data->acl_change.bd_addr, p_data->acl_change.new_role, p_data->acl_change.hci_status); + bdcpy(conn.role_chg.bd_addr, p_bda); + conn.role_chg.new_role = (UINT8) p_data->acl_change.new_role; + if ( bta_dm_cb.p_sec_cback ) { + bta_dm_cb.p_sec_cback(BTA_DM_ROLE_CHG_EVT, (tBTA_DM_SEC *)&conn); + } + } + return; + } + + /* Collision report from Stack: Notify profiles */ + if (p_data->acl_change.event == BTM_BL_COLLISION_EVT) { + bta_sys_notify_collision (p_bda); + return; + } + + if (is_new) { + for (i = 0; i < bta_dm_cb.device_list.count; i++) { + if (!bdcmp( bta_dm_cb.device_list.peer_device[i].peer_bdaddr, p_bda) +#if BLE_INCLUDED == TRUE + && bta_dm_cb.device_list.peer_device[i].conn_handle == p_data->acl_change.handle +#endif + ) { + break; + } + + } + + if (i == bta_dm_cb.device_list.count) { + if (bta_dm_cb.device_list.count < BTA_DM_NUM_PEER_DEVICE) { + bdcpy(bta_dm_cb.device_list.peer_device[bta_dm_cb.device_list.count].peer_bdaddr, p_bda); + bta_dm_cb.device_list.peer_device[bta_dm_cb.device_list.count].link_policy = bta_dm_cb.cur_policy; + bta_dm_cb.device_list.count++; +#if BLE_INCLUDED == TRUE + bta_dm_cb.device_list.peer_device[i].conn_handle = p_data->acl_change.handle; + if (p_data->acl_change.transport == BT_TRANSPORT_LE) { + bta_dm_cb.device_list.le_count++; + } +#endif + } else { + APPL_TRACE_ERROR("%s max active connection reached, no resources", __func__); + return; + } + } + + bta_dm_cb.device_list.peer_device[i].conn_state = BTA_DM_CONNECTED; + bta_dm_cb.device_list.peer_device[i].pref_role = BTA_ANY_ROLE; + bdcpy(conn.link_up.bd_addr, p_bda); + bta_dm_cb.device_list.peer_device[i].info = BTA_DM_DI_NONE; +#if BLE_INCLUDED == TRUE + conn.link_up.link_type = p_data->acl_change.transport; + bta_dm_cb.device_list.peer_device[i].transport = p_data->acl_change.transport; +#endif + + if (((NULL != (p = BTM_ReadLocalFeatures ())) && HCI_SNIFF_SUB_RATE_SUPPORTED(p)) && + ((NULL != (p = BTM_ReadRemoteFeatures (p_bda))) && HCI_SNIFF_SUB_RATE_SUPPORTED(p))) { + /* both local and remote devices support SSR */ + bta_dm_cb.device_list.peer_device[i].info = BTA_DM_DI_USE_SSR; + } + APPL_TRACE_DEBUG("%s info: 0x%x", __func__, bta_dm_cb.device_list.peer_device[i].info); + + if (bta_dm_cb.p_sec_cback) { + conn.link_up.sc_downgrade = p_data->acl_change.sc_downgrade; + bta_dm_cb.p_sec_cback(BTA_DM_LINK_UP_EVT, (tBTA_DM_SEC *)&conn); + } + } else { + for (i = 0; i < bta_dm_cb.device_list.count; i++) { + if (bdcmp( bta_dm_cb.device_list.peer_device[i].peer_bdaddr, p_bda) +#if BLE_INCLUDED == TRUE + || bta_dm_cb.device_list.peer_device[i].transport != p_data->acl_change.transport +#endif + ) { + continue; + } + + if ( bta_dm_cb.device_list.peer_device[i].conn_state == BTA_DM_UNPAIRING ) { + if (BTM_SecDeleteDevice(bta_dm_cb.device_list.peer_device[i].peer_bdaddr, bta_dm_cb.device_list.peer_device[i].transport)) { + issue_unpair_cb = TRUE; + } + + APPL_TRACE_DEBUG("%s: Unpairing: issue unpair CB = %d ", __FUNCTION__, issue_unpair_cb); + } + + conn.link_down.is_removed = bta_dm_cb.device_list.peer_device[i].remove_dev_pending; + + for (; i < bta_dm_cb.device_list.count ; i++) { + memcpy(&bta_dm_cb.device_list.peer_device[i], &bta_dm_cb.device_list.peer_device[i + 1], sizeof(bta_dm_cb.device_list.peer_device[i])); + } + break; + } + if (bta_dm_cb.device_list.count) { + bta_dm_cb.device_list.count--; + } +#if BLE_INCLUDED == TRUE + if ((p_data->acl_change.transport == BT_TRANSPORT_LE) && + (bta_dm_cb.device_list.le_count)) { + bta_dm_cb.device_list.le_count--; + } + conn.link_down.link_type = p_data->acl_change.transport; +#endif + + if (bta_dm_search_cb.wait_disc && !bdcmp(bta_dm_search_cb.peer_bdaddr, p_bda)) { + bta_dm_search_cb.wait_disc = FALSE; + + if (bta_dm_search_cb.sdp_results) { + APPL_TRACE_EVENT(" timer stopped "); + bta_sys_stop_timer(&bta_dm_search_cb.search_timer); + bta_dm_discover_next_device(); + } + + } + + if (bta_dm_cb.disabling) { + if (!BTM_GetNumAclLinks()) { + bta_sys_stop_timer(&bta_dm_cb.disable_timer); + bta_dm_cb.disable_timer.p_cback = (TIMER_CBACK *)&bta_dm_disable_conn_down_timer_cback; + /* + * Start a timer to make sure that the profiles + * get the disconnect event. + */ + bta_sys_start_timer(&bta_dm_cb.disable_timer, 0, 1000); + } + } + if (conn.link_down.is_removed) { + BTM_SecDeleteDevice(p_bda, p_data->acl_change.transport); +#if (BLE_INCLUDED == TRUE && GATTC_INCLUDED == TRUE) + /* need to remove all pending background connection */ + BTA_GATTC_CancelOpen(0, p_bda, FALSE); +#endif + } + + bdcpy(conn.link_down.bd_addr, p_bda); + conn.link_down.reason = (UINT8) btm_get_acl_disc_reason_code(); + if ( bta_dm_cb.p_sec_cback ) { + bta_dm_cb.p_sec_cback(BTA_DM_LINK_DOWN_EVT, &conn); + if ( issue_unpair_cb ) { + if (p_data->acl_change.transport == BT_TRANSPORT_LE) { + bta_dm_cb.p_sec_cback(BTA_DM_BLE_DEV_UNPAIRED_EVT, &conn); + } else { + bta_dm_cb.p_sec_cback(BTA_DM_DEV_UNPAIRED_EVT, &conn); + } + } + } + } + + bta_dm_adjust_roles(TRUE); +} + +/******************************************************************************* +** +** Function bta_dm_disable_conn_down_timer_cback +** +** Description Sends disable event to application +** +** +** Returns void +** +*******************************************************************************/ +static void bta_dm_disable_conn_down_timer_cback (TIMER_LIST_ENT *p_tle) +{ + UNUSED(p_tle); + tBTA_SYS_HW_MSG *sys_enable_event; + +#if (BTA_DM_PM_INCLUDED == TRUE) + /* disable the power managment module */ + bta_dm_disable_pm(); +#endif /* #if (BTA_DM_PM_INCLUDED == TRUE) */ + + /* register our callback to SYS HW manager */ + bta_sys_hw_register( BTA_SYS_HW_BLUETOOTH, bta_dm_sys_hw_cback ); + + /* send a message to BTA SYS */ + if ((sys_enable_event = (tBTA_SYS_HW_MSG *) osi_malloc(sizeof(tBTA_SYS_HW_MSG))) != NULL) { + sys_enable_event->hdr.event = BTA_SYS_API_DISABLE_EVT; + sys_enable_event->hw_module = BTA_SYS_HW_BLUETOOTH; + bta_sys_sendmsg(sys_enable_event); + } + + bta_dm_cb.disabling = FALSE; + +} + +/******************************************************************************* +** +** Function bta_dm_rm_cback +** +** Description Role management callback from sys +** +** +** Returns void +** +*******************************************************************************/ +static void bta_dm_rm_cback(tBTA_SYS_CONN_STATUS status, UINT8 id, UINT8 app_id, BD_ADDR peer_addr) +{ + UINT8 j; + tBTA_PREF_ROLES role; + tBTA_DM_PEER_DEVICE *p_dev; + + p_dev = bta_dm_find_peer_device(peer_addr); + if ( status == BTA_SYS_CONN_OPEN) { + if (p_dev) { + /* Do not set to connected if we are in the middle of unpairing. When AV stream is + * started it fakes out a SYS_CONN_OPEN to potentially trigger a role switch command. + * But this should not be done if we are in the middle of unpairing. + */ + if (p_dev->conn_state != BTA_DM_UNPAIRING) { + p_dev->conn_state = BTA_DM_CONNECTED; + } + + for (j = 1; j <= p_bta_dm_rm_cfg[0].app_id; j++) { + if (((p_bta_dm_rm_cfg[j].app_id == app_id) || (p_bta_dm_rm_cfg[j].app_id == BTA_ALL_APP_ID)) + && (p_bta_dm_rm_cfg[j].id == id)) { + role = p_bta_dm_rm_cfg[j].cfg; + + if (role > p_dev->pref_role ) { + p_dev->pref_role = role; + } + break; + } + } + } + } + + if ((BTA_ID_AV == id) || (BTA_ID_AVK == id)) { + if ( status == BTA_SYS_CONN_BUSY) { + if (p_dev) { + p_dev->info |= BTA_DM_DI_AV_ACTIVE; + } + /* AV calls bta_sys_conn_open with the A2DP stream count as app_id */ + if (BTA_ID_AV == id) { + bta_dm_cb.cur_av_count = bta_dm_get_av_count(); + } + } else if ( status == BTA_SYS_CONN_IDLE) { + if (p_dev) { + p_dev->info &= ~BTA_DM_DI_AV_ACTIVE; + } + + /* get cur_av_count from connected services */ + if (BTA_ID_AV == id) { + bta_dm_cb.cur_av_count = bta_dm_get_av_count(); + } + } + APPL_TRACE_EVENT("bta_dm_rm_cback:%d, status:%d", bta_dm_cb.cur_av_count, status); + } + + /* Don't adjust roles for each busy/idle state transition to avoid + excessive switch requests when individual profile busy/idle status + changes */ + if ((status != BTA_SYS_CONN_BUSY) && (status != BTA_SYS_CONN_IDLE)) { + bta_dm_adjust_roles(FALSE); + } +} + +/******************************************************************************* +** +** Function bta_dm_delay_role_switch_cback +** +** Description Callback from btm to delay a role switch +** +** Returns void +** +*******************************************************************************/ +static void bta_dm_delay_role_switch_cback(TIMER_LIST_ENT *p_tle) +{ + UNUSED(p_tle); + APPL_TRACE_EVENT("bta_dm_delay_role_switch_cback: initiating Delayed RS"); + bta_dm_adjust_roles (FALSE); +} + +/******************************************************************************* +** +** Function bta_dm_remove_sec_dev_entry +** +** Description Removes device entry from Security device DB if ACL connection with +** remtoe device does not exist, else schedule for dev entry removal upon + ACL close +** +** Returns TRUE if device entry is removed from Security device DB, FALSE otherwise +** +*******************************************************************************/ +#if (SMP_INCLUDED == TRUE) +static BOOLEAN bta_dm_remove_sec_dev_entry(BD_ADDR remote_bd_addr) +{ + BOOLEAN is_device_deleted = FALSE; + UINT16 index = 0; + if ( BTM_IsAclConnectionUp(remote_bd_addr, BT_TRANSPORT_LE) || + BTM_IsAclConnectionUp(remote_bd_addr, BT_TRANSPORT_BR_EDR)) { + APPL_TRACE_DEBUG("%s ACL is not down. Schedule for Dev Removal when ACL closes", + __FUNCTION__); + BTM_SecClearSecurityFlags (remote_bd_addr); + for (index = 0; index < bta_dm_cb.device_list.count; index ++) { + if (!bdcmp( bta_dm_cb.device_list.peer_device[index].peer_bdaddr, remote_bd_addr)) { + break; + } + } + if (index != bta_dm_cb.device_list.count) { + bta_dm_cb.device_list.peer_device[index].remove_dev_pending = TRUE; + } else { + APPL_TRACE_ERROR(" %s Device does not exist in DB", __FUNCTION__); + } + } else { + is_device_deleted = BTM_SecDeleteDevice (remote_bd_addr, bta_dm_cb.device_list.peer_device[index].transport); +#if (BLE_INCLUDED == TRUE && GATTC_INCLUDED == TRUE) + /* need to remove all pending background connection */ + BTA_GATTC_CancelOpen(0, remote_bd_addr, FALSE); +#endif + } + return is_device_deleted; +} +#endif ///SMP_INCLUDED == TRUE + + +/******************************************************************************* +** +** Function bta_dm_adjust_roles +** +** Description Adjust roles +** +** +** Returns void +** +*******************************************************************************/ +static void bta_dm_adjust_roles(BOOLEAN delay_role_switch) +{ + + UINT8 i; + BOOLEAN set_master_role = FALSE; +#if BLE_INCLUDED == TRUE + UINT8 br_count = bta_dm_cb.device_list.count - bta_dm_cb.device_list.le_count; +#else + UINT8 br_count = bta_dm_cb.device_list.count; +#endif + if (br_count) { + + /* the configuration is no scatternet + * or AV connection exists and there are more than one ACL link */ + if ( (p_bta_dm_rm_cfg[0].cfg == BTA_DM_NO_SCATTERNET) || + (bta_dm_cb.cur_av_count && br_count > 1) ) { + + L2CA_SetDesireRole (HCI_ROLE_MASTER); + set_master_role = TRUE; + + } + + for (i = 0; i < bta_dm_cb.device_list.count; i++) { + if (bta_dm_cb.device_list.peer_device[i].conn_state == BTA_DM_CONNECTED +#if BLE_INCLUDED == TRUE + && bta_dm_cb.device_list.peer_device[i].transport == BT_TRANSPORT_BR_EDR +#endif + ) { + if (!set_master_role && (bta_dm_cb.device_list.peer_device[i].pref_role != BTA_ANY_ROLE) + && (p_bta_dm_rm_cfg[0].cfg == BTA_DM_PARTIAL_SCATTERNET)) { + L2CA_SetDesireRole (HCI_ROLE_MASTER); + set_master_role = TRUE; + } + + if ((bta_dm_cb.device_list.peer_device[i].pref_role == BTA_MASTER_ROLE_ONLY) + || (br_count > 1)) { + + /* Initiating immediate role switch with certain remote devices + has caused issues due to role switch colliding with link encryption setup and + causing encryption (and in turn the link) to fail . These device . Firmware + versions are stored in a blacklist and role switch with these devices are + delayed to avoid the collision with link encryption setup */ + + if (bta_dm_cb.device_list.peer_device[i].pref_role != BTA_SLAVE_ROLE_ONLY && + delay_role_switch == FALSE) { + BTM_SwitchRole (bta_dm_cb.device_list.peer_device[i].peer_bdaddr, + HCI_ROLE_MASTER, NULL); + } else { + bta_dm_cb.switch_delay_timer[i].p_cback = + (TIMER_CBACK *)&bta_dm_delay_role_switch_cback; + /* Start the timer if not active */ + if (!bta_sys_timer_is_active(&bta_dm_cb.switch_delay_timer[i])) { + bta_sys_start_timer(&bta_dm_cb.switch_delay_timer[i], 0, 500); + } + } + } + + } + } + + + if (!set_master_role) { + + L2CA_SetDesireRole (L2CAP_DESIRED_LINK_ROLE); + + } + + } else { + L2CA_SetDesireRole (L2CAP_DESIRED_LINK_ROLE); + } + + +} + +/******************************************************************************* +** +** Function bta_dm_get_remname +** +** Description Returns a pointer to the remote name stored in the DM control +** block if it exists, or from the BTM memory. +** +** Returns char * - Pointer to the remote device name +*******************************************************************************/ +#if (SDP_INCLUDED == TRUE || SMP_INCLUDED == TRUE) +static char *bta_dm_get_remname(void) +{ + char *p_name = (char *)bta_dm_search_cb.peer_name; + char *p_temp; + + /* If the name isn't already stored, try retrieving from BTM */ + if (*p_name == '\0') { + if ((p_temp = BTM_SecReadDevName(bta_dm_search_cb.peer_bdaddr)) != NULL) { + p_name = p_temp; + } + } + return p_name; +} +#endif ///SDP_INCLUDED == TRUE || SMP_INCLUDED == TRUE + +/******************************************************************************* +** +** Function bta_dm_bond_cancel_complete_cback +** +** Description Authentication complete callback from BTM +** +** Returns void +** +*******************************************************************************/ +#if (SMP_INCLUDED == TRUE) +static void bta_dm_bond_cancel_complete_cback(tBTM_STATUS result) +{ + + tBTA_DM_SEC sec_event; + + if (result == BTM_SUCCESS) { + sec_event.bond_cancel_cmpl.result = BTA_SUCCESS; + } else { + sec_event.bond_cancel_cmpl.result = BTA_FAILURE; + } + + if (bta_dm_cb.p_sec_cback) { + bta_dm_cb.p_sec_cback(BTA_DM_BOND_CANCEL_CMPL_EVT, &sec_event); + } +} +#endif ///SMP_INCLUDED == TRUE + +/******************************************************************************* +** +** Function bta_dm_set_eir +** +** Description This function creates EIR tagged data and writes it to controller. +** +** Returns None +** +*******************************************************************************/ +#if (CLASSIC_BT_INCLUDED == TRUE) +static void bta_dm_set_eir (char *local_name) +{ + BT_HDR *p_buf; + UINT8 *p; + UINT8 *p_length; +#if (BTA_EIR_CANNED_UUID_LIST != TRUE) + UINT8 *p_type; + UINT8 max_num_uuid; +#if (BTA_EIR_SERVER_NUM_CUSTOM_UUID > 0) + UINT8 custom_uuid_idx; +#endif // BTA_EIR_SERVER_NUM_CUSTOM_UUID +#endif // BTA_EIR_CANNED_UUID_LIST + + UINT8 free_eir_length; + if (p_bta_dm_eir_cfg->bta_dm_eir_fec_required) { + free_eir_length = HCI_DM5_PACKET_SIZE; + } else { + free_eir_length = HCI_EXT_INQ_RESPONSE_LEN; + } + + UINT8 num_uuid; + UINT8 data_type; + UINT8 local_name_len; + + UINT8 eir_type[BTM_EIR_TYPE_MAX_NUM]; + UINT8 eir_type_num = 0; + + tBTA_STATUS status = BTA_SUCCESS; + + /* wait until complete to disable */ + if (bta_dm_cb.disable_timer.in_use) { + if (p_bta_dm_eir_cfg->config_eir_callback) { + p_bta_dm_eir_cfg->config_eir_callback(BTA_WRONG_MODE, eir_type_num , eir_type); + } + return; + } + +#if ( BTA_EIR_CANNED_UUID_LIST != TRUE ) + /* wait until App is ready */ + if (bta_dm_cb.app_ready_timer.in_use) { + if (p_bta_dm_eir_cfg->config_eir_callback) { + p_bta_dm_eir_cfg->config_eir_callback(BTA_WRONG_MODE, eir_type_num , eir_type); + } + return; + } +#endif // BTA_EIR_CANNED_UUID_LIST + + if (p_bta_dm_eir_cfg->bta_dm_eir_included_name) { + /* if local name is not provided, get it from controller */ + if ( local_name == NULL ) { + if ( BTM_ReadLocalDeviceName( &local_name ) != BTM_SUCCESS ) { + APPL_TRACE_ERROR("Fail to read local device name for EIR"); + } + } + } else { + local_name = NULL; + } + + + /* Allocate a buffer to hold HCI command */ + if ((p_buf = (BT_HDR *)osi_malloc(BTM_CMD_BUF_SIZE)) == NULL) { + APPL_TRACE_ERROR("bta_dm_set_eir couldn't allocate buffer"); + if (p_bta_dm_eir_cfg->config_eir_callback) { + p_bta_dm_eir_cfg->config_eir_callback(BTA_NO_RESOURCES, eir_type_num , eir_type); + } + return; + } + p = (UINT8 *)p_buf + BTM_HCI_EIR_OFFSET; + + memset(p, 0x00, HCI_EXT_INQ_RESPONSE_LEN ); + + APPL_TRACE_DEBUG("BTA is generating EIR"); + + if ( local_name ) { + local_name_len = strlen( local_name ); + } else { + local_name_len = 0; + } + + data_type = BTM_EIR_COMPLETE_LOCAL_NAME_TYPE; + /* if local name is longer than minimum length of shortened name */ + /* check whether it needs to be shortened or not */ + if ( local_name_len > p_bta_dm_eir_cfg->bta_dm_eir_min_name_len ) { + /* get number of UUID 16-bit list */ +#if (BTA_EIR_CANNED_UUID_LIST == TRUE) + num_uuid = p_bta_dm_eir_cfg->bta_dm_eir_uuid16_len / LEN_UUID_16; +#else // BTA_EIR_CANNED_UUID_LIST + max_num_uuid = (free_eir_length - 2) / LEN_UUID_16; + data_type = BTM_GetEirSupportedServices( bta_dm_cb.eir_uuid, &p, + max_num_uuid, &num_uuid ); + p = (UINT8 *)p_buf + BTM_HCI_EIR_OFFSET; /* reset p */ +#endif // BTA_EIR_CANNED_UUID_LIST + + /* if UUID doesn't fit remaing space, shorten local name */ + if ( local_name_len > (free_eir_length - 4 - num_uuid * LEN_UUID_16)) { + APPL_TRACE_WARNING("BTA EIR: local name is shortened"); + local_name_len = p_bta_dm_eir_cfg->bta_dm_eir_min_name_len; + data_type = BTM_EIR_SHORTENED_LOCAL_NAME_TYPE; + } else { + data_type = BTM_EIR_COMPLETE_LOCAL_NAME_TYPE; + } + } + + + + if (local_name != NULL) { + UINT8_TO_STREAM(p, local_name_len + 1); + UINT8_TO_STREAM(p, data_type); + eir_type[eir_type_num++] = data_type; + memcpy(p, local_name, local_name_len); + p += local_name_len; + free_eir_length -= local_name_len + 2; + } + + /* if UUIDs are provided in configuration */ + if (p_bta_dm_eir_cfg->bta_dm_eir_included_uuid) { +#if (BTA_EIR_CANNED_UUID_LIST == TRUE) + /* if UUID list is provided as static data in configuration */ + if (( p_bta_dm_eir_cfg->bta_dm_eir_uuid16_len > 0 ) + && (p_bta_dm_eir_cfg->bta_dm_eir_uuid16)) { + if ( free_eir_length > LEN_UUID_16 + 2) { + free_eir_length -= 2; + + if ( free_eir_length >= p_bta_dm_eir_cfg->bta_dm_eir_uuid16_len) { + num_uuid = p_bta_dm_eir_cfg->bta_dm_eir_uuid16_len / LEN_UUID_16; + data_type = BTM_EIR_COMPLETE_16BITS_UUID_TYPE; + } else { /* not enough room for all UUIDs */ + APPL_TRACE_WARNING("BTA EIR: UUID 16-bit list is truncated"); + num_uuid = free_eir_length / LEN_UUID_16; + data_type = BTM_EIR_MORE_16BITS_UUID_TYPE; + } + UINT8_TO_STREAM(p, num_uuid * LEN_UUID_16 + 1); + UINT8_TO_STREAM(p, data_type); + eir_type[eir_type_num++] = data_type; + memcpy(p, p_bta_dm_eir_cfg->bta_dm_eir_uuid16, num_uuid * LEN_UUID_16 ); + p += num_uuid * LEN_UUID_16; + free_eir_length -= num_uuid * LEN_UUID_16; + } else { + status = BTA_EIR_TOO_LARGE; + } + } +#else /* (BTA_EIR_CANNED_UUID_LIST == TRUE) */ + /* if UUID list is dynamic */ + if ( free_eir_length >= 2) { + p_length = p++; + p_type = p++; + num_uuid = 0; + + max_num_uuid = (free_eir_length - 2) / LEN_UUID_16; + data_type = BTM_GetEirSupportedServices( bta_dm_cb.eir_uuid, &p, max_num_uuid, &num_uuid ); + + if ( data_type == BTM_EIR_MORE_16BITS_UUID_TYPE ) { + APPL_TRACE_WARNING("BTA EIR: UUID 16-bit list is truncated"); + } +#if (BTA_EIR_SERVER_NUM_CUSTOM_UUID > 0) + else { + for (custom_uuid_idx = 0; custom_uuid_idx < BTA_EIR_SERVER_NUM_CUSTOM_UUID; custom_uuid_idx++) { + if (bta_dm_cb.custom_uuid[custom_uuid_idx].len == LEN_UUID_16) { + if ( num_uuid < max_num_uuid ) { + UINT16_TO_STREAM(p, bta_dm_cb.custom_uuid[custom_uuid_idx].uu.uuid16); + num_uuid++; + } else { + data_type = BTM_EIR_MORE_16BITS_UUID_TYPE; + APPL_TRACE_WARNING("BTA EIR: UUID 16-bit list is truncated"); + break; + } + } + } + } +#endif /* (BTA_EIR_SERVER_NUM_CUSTOM_UUID > 0) */ + + UINT8_TO_STREAM(p_length, num_uuid * LEN_UUID_16 + 1); + UINT8_TO_STREAM(p_type, data_type); + eir_type[eir_type_num++] = data_type; + free_eir_length -= num_uuid * LEN_UUID_16 + 2; + } else { + status = BTA_EIR_TOO_LARGE; + } +#endif /* (BTA_EIR_CANNED_UUID_LIST == TRUE) */ + +#if ( BTA_EIR_CANNED_UUID_LIST != TRUE )&&(BTA_EIR_SERVER_NUM_CUSTOM_UUID > 0) + /* Adding 32-bit UUID list */ + if ( free_eir_length >= 2) { + p_length = p++; + p_type = p++; + num_uuid = 0; + data_type = BTM_EIR_COMPLETE_32BITS_UUID_TYPE; + + max_num_uuid = (free_eir_length - 2) / LEN_UUID_32; + + for (custom_uuid_idx = 0; custom_uuid_idx < BTA_EIR_SERVER_NUM_CUSTOM_UUID; custom_uuid_idx++) { + if (bta_dm_cb.custom_uuid[custom_uuid_idx].len == LEN_UUID_32) { + if ( num_uuid < max_num_uuid ) { + UINT32_TO_STREAM(p, bta_dm_cb.custom_uuid[custom_uuid_idx].uu.uuid32); + num_uuid++; + } else { + data_type = BTM_EIR_MORE_32BITS_UUID_TYPE; + APPL_TRACE_WARNING("BTA EIR: UUID 32-bit list is truncated"); + break; + } + } + } + + UINT8_TO_STREAM(p_length, num_uuid * LEN_UUID_32 + 1); + UINT8_TO_STREAM(p_type, data_type); + eir_type[eir_type_num++] = data_type; + free_eir_length -= num_uuid * LEN_UUID_32 + 2; + } else { + status = BTA_EIR_TOO_LARGE; + } + + /* Adding 128-bit UUID list */ + if ( free_eir_length >= 2) { + p_length = p++; + p_type = p++; + num_uuid = 0; + data_type = BTM_EIR_COMPLETE_128BITS_UUID_TYPE; + + max_num_uuid = (free_eir_length - 2) / LEN_UUID_128; + + for (custom_uuid_idx = 0; custom_uuid_idx < BTA_EIR_SERVER_NUM_CUSTOM_UUID; custom_uuid_idx++) { + if (bta_dm_cb.custom_uuid[custom_uuid_idx].len == LEN_UUID_128) { + if ( num_uuid < max_num_uuid ) { + ARRAY_TO_STREAM(p, bta_dm_cb.custom_uuid[custom_uuid_idx].uu.uuid128, LEN_UUID_128); + num_uuid++; + } else { + data_type = BTM_EIR_MORE_128BITS_UUID_TYPE; + APPL_TRACE_WARNING("BTA EIR: UUID 128-bit list is truncated"); + break; + } + } + } + + UINT8_TO_STREAM(p_length, num_uuid * LEN_UUID_128 + 1); + UINT8_TO_STREAM(p_type, data_type); + eir_type[eir_type_num++] = data_type; + free_eir_length -= num_uuid * LEN_UUID_128 + 2; + } + else { + status = BTA_EIR_TOO_LARGE; + } +#endif /* ( BTA_EIR_CANNED_UUID_LIST != TRUE )&&(BTA_EIR_SERVER_NUM_CUSTOM_UUID > 0) */ + } + + /* if Flags are provided in configuration */ + if ( p_bta_dm_eir_cfg->bta_dm_eir_flags != 0 ) { + if ( free_eir_length >= 3 ) { + UINT8_TO_STREAM(p, 2); + UINT8_TO_STREAM(p, BTM_EIR_FLAGS_TYPE); + eir_type[eir_type_num++] = BTM_EIR_FLAGS_TYPE; + UINT8_TO_STREAM(p, p_bta_dm_eir_cfg->bta_dm_eir_flags); + free_eir_length -= 3; + } else { + status = BTA_EIR_TOO_LARGE; + } + } + + /* if Manufacturer Specific are provided in configuration */ + if (( p_bta_dm_eir_cfg->bta_dm_eir_manufac_spec_len > 0 ) + && ( p_bta_dm_eir_cfg->bta_dm_eir_manufac_spec )) { + if ( free_eir_length >= p_bta_dm_eir_cfg->bta_dm_eir_manufac_spec_len + 2) { + p_length = p; + + UINT8_TO_STREAM(p, p_bta_dm_eir_cfg->bta_dm_eir_manufac_spec_len + 1); + UINT8_TO_STREAM(p, BTM_EIR_MANUFACTURER_SPECIFIC_TYPE); + eir_type[eir_type_num++] = BTM_EIR_MANUFACTURER_SPECIFIC_TYPE; + memcpy(p, p_bta_dm_eir_cfg->bta_dm_eir_manufac_spec, + p_bta_dm_eir_cfg->bta_dm_eir_manufac_spec_len); + p += p_bta_dm_eir_cfg->bta_dm_eir_manufac_spec_len; + free_eir_length -= p_bta_dm_eir_cfg->bta_dm_eir_manufac_spec_len + 2; + } else { + status = BTA_EIR_TOO_LARGE; + } + } else { + p_length = NULL; + } + + /* if Inquiry Tx Resp Power compiled */ + if (p_bta_dm_eir_cfg->bta_dm_eir_included_tx_power) { + if (free_eir_length >= 3) { + int min_power_level, max_power_level; +#if (BT_CONTROLLER_INCLUDED == TRUE) + if (esp_bredr_tx_power_get((esp_power_level_t *)&min_power_level, (esp_power_level_t *)&max_power_level) == ESP_OK) { +#else + { + min_power_level = 0; + max_power_level = 0; + UNUSED(min_power_level); +#endif + INT8 btm_tx_power[BTM_TX_POWER_LEVEL_MAX + 1] = BTM_TX_POWER; + p_bta_dm_eir_cfg->bta_dm_eir_inq_tx_power = btm_tx_power[max_power_level]; + UINT8_TO_STREAM(p, 2); /* Length field */ + UINT8_TO_STREAM(p, BTM_EIR_TX_POWER_LEVEL_TYPE); + eir_type[eir_type_num++] = BTM_EIR_TX_POWER_LEVEL_TYPE; + UINT8_TO_STREAM(p, p_bta_dm_eir_cfg->bta_dm_eir_inq_tx_power); + free_eir_length -= 3; + } + } else { + status = BTA_EIR_TOO_LARGE; + } + } + + /* if URL are provided in configuration */ + if (( p_bta_dm_eir_cfg->bta_dm_eir_url_len > 0 ) + && ( p_bta_dm_eir_cfg->bta_dm_eir_url )) { + if ( free_eir_length >= p_bta_dm_eir_cfg->bta_dm_eir_url_len + 2 ) { + UINT8_TO_STREAM(p, p_bta_dm_eir_cfg->bta_dm_eir_url_len + 1); + UINT8_TO_STREAM(p, BTM_EIR_URL_TYPE); + eir_type[eir_type_num++] = BTM_EIR_URL_TYPE; + memcpy(p, p_bta_dm_eir_cfg->bta_dm_eir_url, + p_bta_dm_eir_cfg->bta_dm_eir_url_len); + p += p_bta_dm_eir_cfg->bta_dm_eir_url_len; + free_eir_length -= p_bta_dm_eir_cfg->bta_dm_eir_url_len + 2; + } else { + status = BTA_EIR_TOO_LARGE; + } + } + + /* If there is no other data to be sent in the EIR packet, the Host shall + * send a name tag with zero length and the type field set to indicate + * that this is the complete name (i.e., total of 2 octets with length = + * 1). + */ + if (eir_type_num == 0) { + UINT8_TO_STREAM(p, 1); + UINT8_TO_STREAM(p, BTM_EIR_COMPLETE_LOCAL_NAME_TYPE); + free_eir_length -= 2; + } + + if (free_eir_length) { + UINT8_TO_STREAM(p, 0); /* terminator of significant part */ + } + + tBTM_STATUS btm_status = BTM_WriteEIR( p_buf, p_bta_dm_eir_cfg->bta_dm_eir_fec_required ); + + if (btm_status == BTM_MODE_UNSUPPORTED) { + status = BTA_WRONG_MODE; + } else if (btm_status != BTM_SUCCESS) { + status = BTA_FAILURE; + } + + if (p_bta_dm_eir_cfg->config_eir_callback) { + p_bta_dm_eir_cfg->config_eir_callback(status, eir_type_num, eir_type); + } +} +#endif + +/******************************************************************************* +** +** Function bta_dm_eir_search_services +** +** Description This function searches services in received EIR +** +** Returns None +** +*******************************************************************************/ +#if (SDP_INCLUDED == TRUE) +static void bta_dm_eir_search_services( tBTM_INQ_RESULTS *p_result, + tBTA_SERVICE_MASK *p_services_to_search, + tBTA_SERVICE_MASK *p_services_found) +{ + tBTA_SERVICE_MASK service_index = 0; + tBTM_EIR_SEARCH_RESULT result; + + APPL_TRACE_DEBUG("BTA searching services in EIR of BDA:0x%02X%02X%02X%02X%02X%02X", + p_result->remote_bd_addr[0], p_result->remote_bd_addr[1], + p_result->remote_bd_addr[2], p_result->remote_bd_addr[3], + p_result->remote_bd_addr[4], p_result->remote_bd_addr[5]); + + APPL_TRACE_DEBUG(" with services_to_search=0x%08X", *p_services_to_search); + +#if BLE_INCLUDED == TRUE && BTA_GATT_INCLUDED == TRUE + /* always do GATT based service discovery by SDP instead of from EIR */ + /* if GATT based service is also to be put in EIR, need to modify this */ + while (service_index < (BTA_MAX_SERVICE_ID - 1)) +#else + while (service_index < BTA_MAX_SERVICE_ID) +#endif + { + if ( *p_services_to_search + & (tBTA_SERVICE_MASK)(BTA_SERVICE_ID_TO_SERVICE_MASK(service_index))) { + result = BTM_HasInquiryEirService( p_result, + bta_service_id_to_uuid_lkup_tbl[service_index] ); + + /* Searching for HSP v1.2 only device */ + if ((result != BTM_EIR_FOUND) && + (bta_service_id_to_uuid_lkup_tbl[service_index] == UUID_SERVCLASS_HEADSET)) { + result = BTM_HasInquiryEirService (p_result, UUID_SERVCLASS_HEADSET_HS); + } + + if ( result == BTM_EIR_FOUND ) { + /* If Plug and Play service record, need to check to see if Broadcom stack */ + /* However, EIR data doesn't have EXT_BRCM_VERSION so just skip it */ + if ( bta_service_id_to_uuid_lkup_tbl[service_index] + != UUID_SERVCLASS_PNP_INFORMATION ) { + + *p_services_found |= + (tBTA_SERVICE_MASK)(BTA_SERVICE_ID_TO_SERVICE_MASK(service_index)); + /* remove the service from services to be searched */ + *p_services_to_search &= + (tBTA_SERVICE_MASK)(~(BTA_SERVICE_ID_TO_SERVICE_MASK(service_index))); + } + } else if ( result == BTM_EIR_NOT_FOUND ) { + /* remove the service from services to be searched */ + *p_services_to_search &= + (tBTA_SERVICE_MASK)(~(BTA_SERVICE_ID_TO_SERVICE_MASK(service_index))); + } + } + + service_index++; + } + + APPL_TRACE_ERROR("BTA EIR search result, services_to_search=0x%08X, services_found=0x%08X", + *p_services_to_search, *p_services_found); +} +#endif ///SDP_INCLUDED == TRUE + +#if (BTA_EIR_CANNED_UUID_LIST != TRUE) +/******************************************************************************* +** +** Function bta_dm_eir_update_uuid +** +** Description This function adds or removes service UUID in EIR database. +** +** Returns None +** +*******************************************************************************/ +void bta_dm_eir_update_uuid(tBT_UUID uuid, BOOLEAN adding) +{ + /* 32 and 128-bit UUIDs go to the bta_dm_cb.custom_uuid array */ + if ((uuid.len == LEN_UUID_32) || (uuid.len == LEN_UUID_128)) { + if (adding) { + if (BTM_HasCustomEirService(bta_dm_cb.custom_uuid, uuid)) { + APPL_TRACE_EVENT("UUID is already added for EIR"); + return; + } + APPL_TRACE_EVENT("Adding %d-bit UUID into EIR", uuid.len * 8); + + BTM_AddCustomEirService(bta_dm_cb.custom_uuid, uuid); + } else { + APPL_TRACE_EVENT("Removing %d-bit UUID from EIR", uuid.len * 8); + + BTM_RemoveCustomEirService(bta_dm_cb.custom_uuid, uuid); + } + } else { + /* if this UUID is not advertised in EIR */ + if (!BTM_HasEirService(p_bta_dm_eir_cfg->uuid_mask, uuid.uu.uuid16)) { + return; + } + + if (adding) { + APPL_TRACE_EVENT("Adding UUID=0x%04X into EIR", uuid.uu.uuid16); + + BTM_AddEirService(bta_dm_cb.eir_uuid, uuid.uu.uuid16); + } else { + APPL_TRACE_EVENT("Removing UUID=0x%04X from EIR", uuid.uu.uuid16); + + BTM_RemoveEirService(bta_dm_cb.eir_uuid, uuid.uu.uuid16); + } + } +#if CLASSIC_BT_INCLUDED + bta_dm_set_eir (NULL); +#endif + + APPL_TRACE_EVENT("bta_dm_eir_update_uuid UUID bit mask=0x%08X %08X", + bta_dm_cb.eir_uuid[1], bta_dm_cb.eir_uuid[0] ); +} +#endif + +/******************************************************************************* +** +** Function bta_dm_enable_test_mode +** +** Description enable test mode +** +** +** Returns void +** +*******************************************************************************/ +void bta_dm_enable_test_mode(tBTA_DM_MSG *p_data) +{ + UNUSED(p_data); + BTM_EnableTestMode(); +} + +/******************************************************************************* +** +** Function bta_dm_disable_test_mode +** +** Description disable test mode +** +** +** Returns void +** +*******************************************************************************/ +void bta_dm_disable_test_mode(tBTA_DM_MSG *p_data) +{ + UNUSED(p_data); + BTM_DeviceReset(NULL); +} + +/******************************************************************************* +** +** Function bta_dm_execute_callback +** +** Description Just execute a generic call back in the context of the BTU/BTA tack +** +** +** Returns void +** +*******************************************************************************/ +void bta_dm_execute_callback(tBTA_DM_MSG *p_data) +{ + /* sanity check */ + if (p_data->exec_cback.p_exec_cback == NULL) { + return; + } + + p_data->exec_cback.p_exec_cback(p_data->exec_cback.p_param); +} + +/******************************************************************************* +** +** Function bta_dm_encrypt_cback +** +** Description link encryption complete callback. +** +** Returns None +** +*******************************************************************************/ +void bta_dm_encrypt_cback(BD_ADDR bd_addr, tBT_TRANSPORT transport, void *p_ref_data, tBTM_STATUS result) +{ + tBTA_STATUS bta_status = BTA_SUCCESS; + tBTA_DM_ENCRYPT_CBACK *p_callback = NULL; + UINT8 i ; + UNUSED(p_ref_data); + + for (i = 0; i < bta_dm_cb.device_list.count; i++) { + if (bdcmp( bta_dm_cb.device_list.peer_device[i].peer_bdaddr, bd_addr) == 0 && + bta_dm_cb.device_list.peer_device[i].conn_state == BTA_DM_CONNECTED) { + break; + } + } + + if (i < bta_dm_cb.device_list.count) { + p_callback = bta_dm_cb.device_list.peer_device[i].p_encrypt_cback; + bta_dm_cb.device_list.peer_device[i].p_encrypt_cback = NULL; + } + + switch (result) { + case BTM_SUCCESS: + break; + case BTM_WRONG_MODE: + bta_status = BTA_WRONG_MODE; + break; + case BTM_NO_RESOURCES: + bta_status = BTA_NO_RESOURCES; + break; + case BTM_BUSY: + bta_status = BTA_BUSY; + break; + default: + bta_status = BTA_FAILURE; + break; + } + + APPL_TRACE_DEBUG("bta_dm_encrypt_cback status =%d p_callback=%p", bta_status, p_callback); + + if (p_callback) { + (*p_callback)(bd_addr, transport, bta_status); + } +} + +/******************************************************************************* +** +** Function bta_dm_set_encryption +** +** Description This function to encrypt the link +** +** Returns None +** +*******************************************************************************/ +#if (SMP_INCLUDED == TRUE) +void bta_dm_set_encryption (tBTA_DM_MSG *p_data) +{ + UINT8 i ; + + APPL_TRACE_DEBUG("bta_dm_set_encryption\n"); //todo + if (!p_data->set_encryption.p_callback) { + APPL_TRACE_ERROR("bta_dm_set_encryption callback is not provided\n"); + return; + } + for (i = 0; i < bta_dm_cb.device_list.count; i++) { + if (bdcmp( bta_dm_cb.device_list.peer_device[i].peer_bdaddr, p_data->set_encryption.bd_addr) == 0 && + bta_dm_cb.device_list.peer_device[i].conn_state == BTA_DM_CONNECTED) { + break; + } + } + if (i < bta_dm_cb.device_list.count) { + if (bta_dm_cb.device_list.peer_device[i].p_encrypt_cback) { + APPL_TRACE_ERROR("earlier enc was not done for same device\n"); + (*p_data->set_encryption.p_callback)(p_data->set_encryption.bd_addr, + p_data->set_encryption.transport, + BTA_BUSY); + return; + } + + if (BTM_SetEncryption(p_data->set_encryption.bd_addr, p_data->set_encryption.transport, + bta_dm_encrypt_cback, &p_data->set_encryption.sec_act) + == BTM_CMD_STARTED) { + bta_dm_cb.device_list.peer_device[i].p_encrypt_cback = p_data->set_encryption.p_callback; + } + }else{ + APPL_TRACE_ERROR("%s, not find peer_bdaddr or peer_bdaddr connection state error", __func__); + } +} +#endif ///SMP_INCLUDED == TRUE + +#if (BTA_HD_INCLUDED == TRUE) +BOOLEAN bta_dm_check_if_only_hd_connected(BD_ADDR peer_addr) +{ + APPL_TRACE_DEBUG("%s: count(%d)", __func__, bta_dm_conn_srvcs.count); + for (uint8_t j = 0; j < bta_dm_conn_srvcs.count; j++) { + // Check if profiles other than hid are connected + if ((bta_dm_conn_srvcs.conn_srvc[j].id != BTA_ID_HD) && + !bdcmp(bta_dm_conn_srvcs.conn_srvc[j].peer_bdaddr, peer_addr)) { + APPL_TRACE_DEBUG("%s: Another profile (id=%d) is connected", __func__, bta_dm_conn_srvcs.conn_srvc[j].id); + return FALSE; + } + } + return TRUE; +} +#endif /* BTA_HD_INCLUDED == TRUE */ + +#if (BLE_INCLUDED == TRUE) +/******************************************************************************* +** +** Function bta_dm_observe_results_cb +** +** Description Callback for BLE Observe result +** +** +** Returns void +** +*******************************************************************************/ +static void bta_dm_observe_results_cb (tBTM_INQ_RESULTS *p_inq, UINT8 *p_eir) +{ + tBTA_DM_SEARCH result; + tBTM_INQ_INFO *p_inq_info; + APPL_TRACE_DEBUG("bta_dm_observe_results_cb") + + bdcpy(result.inq_res.bd_addr, p_inq->remote_bd_addr); + result.inq_res.rssi = p_inq->rssi; + result.inq_res.ble_addr_type = p_inq->ble_addr_type; + result.inq_res.inq_result_type = p_inq->inq_result_type; + result.inq_res.device_type = p_inq->device_type; + result.inq_res.flag = p_inq->flag; + result.inq_res.adv_data_len = p_inq->adv_data_len; + result.inq_res.scan_rsp_len = p_inq->scan_rsp_len; + memcpy(result.inq_res.dev_class, p_inq->dev_class, sizeof(DEV_CLASS)); + result.inq_res.ble_evt_type = p_inq->ble_evt_type; + + /* application will parse EIR to find out remote device name */ + result.inq_res.p_eir = p_eir; + + if ((p_inq_info = BTM_InqDbRead(p_inq->remote_bd_addr)) != NULL) { + /* initialize remt_name_not_required to FALSE so that we get the name by default */ + result.inq_res.remt_name_not_required = FALSE; + } + + if (bta_dm_search_cb.p_scan_cback) { + bta_dm_search_cb.p_scan_cback(BTA_DM_INQ_RES_EVT, &result); + } + + if (p_inq_info) { + /* application indicates if it knows the remote name, inside the callback + copy that to the inquiry data base*/ + if (result.inq_res.remt_name_not_required) { + p_inq_info->appl_knows_rem_name = TRUE; + } + } +} + +/******************************************************************************* +** +** Function bta_dm_observe_cmpl_cb +** +** Description Callback for BLE Observe complete +** +** +** Returns void +** +*******************************************************************************/ +static void bta_dm_observe_cmpl_cb (void *p_result) +{ + tBTA_DM_SEARCH data; + + APPL_TRACE_DEBUG("bta_dm_observe_cmpl_cb"); + + data.inq_cmpl.num_resps = ((tBTM_INQUIRY_CMPL *)p_result)->num_resp; + if (bta_dm_search_cb.p_scan_cback) { + bta_dm_search_cb.p_scan_cback(BTA_DM_INQ_CMPL_EVT, &data); + } +} + +/******************************************************************************* +** +** Function bta_dm_observe_discard_cb +** +** Description Callback for BLE Observe lost +** +** +** Returns void +** +*******************************************************************************/ +static void bta_dm_observe_discard_cb (uint32_t num_dis) +{ + tBTA_DM_SEARCH data; + + APPL_TRACE_DEBUG("bta_dm_observe_discard_cb"); + + data.inq_dis.num_dis = num_dis; + if (bta_dm_search_cb.p_scan_cback) { + bta_dm_search_cb.p_scan_cback(BTA_DM_INQ_DISCARD_NUM_EVT, &data); + } +} + +#if (SMP_INCLUDED == TRUE) +/******************************************************************************* +** +** Function bta_dm_ble_smp_cback +** +** Description Callback for BLE SMP +** +** +** Returns void +** +*******************************************************************************/ +static UINT8 bta_dm_ble_smp_cback (tBTM_LE_EVT event, BD_ADDR bda, tBTM_LE_EVT_DATA *p_data) +{ + tBTM_STATUS status = BTM_SUCCESS; + tBTA_DM_SEC sec_event; + char *p_name = NULL; + + if (!bta_dm_cb.p_sec_cback) { + return BTM_NOT_AUTHORIZED; + } + + memset(&sec_event, 0, sizeof(tBTA_DM_SEC)); + switch (event) { + case BTM_LE_IO_REQ_EVT: { + bta_dm_co_ble_io_req(bda, + &p_data->io_req.io_cap, + &p_data->io_req.oob_data, + &p_data->io_req.auth_req, + &p_data->io_req.max_key_size, + &p_data->io_req.init_keys, + &p_data->io_req.resp_keys); +#if BTM_OOB_INCLUDED == FALSE + status = BTM_SUCCESS; +#endif + APPL_TRACE_EVENT("io mitm: %d oob_data:%d\n", p_data->io_req.auth_req, p_data->io_req.oob_data); + + break; + } + + case BTM_LE_SEC_REQUEST_EVT: + bdcpy(sec_event.ble_req.bd_addr, bda); + p_name = BTM_SecReadDevName(bda); + if (p_name != NULL) { + BCM_STRNCPY_S((char *)sec_event.ble_req.bd_name, p_name, BD_NAME_LEN); + sec_event.ble_req.bd_name[BD_NAME_LEN] = '\0'; + } else { + sec_event.ble_req.bd_name[0] = '\0'; + } + bta_dm_cb.p_sec_cback(BTA_DM_BLE_SEC_REQ_EVT, &sec_event); + break; + + case BTM_LE_KEY_NOTIF_EVT: + bdcpy(sec_event.key_notif.bd_addr, bda); + p_name = BTM_SecReadDevName(bda); + if (p_name != NULL) { + BCM_STRNCPY_S((char *)sec_event.key_notif.bd_name, p_name, BD_NAME_LEN); + sec_event.key_notif.bd_name[BD_NAME_LEN] = '\0'; + } else { + sec_event.key_notif.bd_name[0] = '\0'; + } + sec_event.key_notif.passkey = p_data->key_notif; + bta_dm_cb.p_sec_cback(BTA_DM_BLE_PASSKEY_NOTIF_EVT, &sec_event); + break; + + case BTM_LE_KEY_REQ_EVT: + bdcpy(sec_event.ble_req.bd_addr, bda); + bta_dm_cb.p_sec_cback(BTA_DM_BLE_PASSKEY_REQ_EVT, &sec_event); + break; + + case BTM_LE_OOB_REQ_EVT: + bdcpy(sec_event.ble_req.bd_addr, bda); + bta_dm_cb.p_sec_cback(BTA_DM_BLE_OOB_REQ_EVT, &sec_event); + break; + + case BTM_LE_SC_OOB_REQ_EVT: + bdcpy(sec_event.ble_req.bd_addr, bda); + bta_dm_cb.p_sec_cback(BTA_DM_BLE_SC_OOB_REQ_EVT, &sec_event); + break; + + case BTM_LE_SC_LOC_OOB_EVT: + memcpy(sec_event.local_oob_data.local_oob_c, p_data->local_oob_data.commitment, BT_OCTET16_LEN); + memcpy(sec_event.local_oob_data.local_oob_r, p_data->local_oob_data.randomizer, BT_OCTET16_LEN); + bta_dm_cb.p_sec_cback(BTA_DM_BLE_SC_CR_LOC_OOB_EVT, &sec_event); + break; + + case BTM_LE_NC_REQ_EVT: + bdcpy(sec_event.key_notif.bd_addr, bda); + BCM_STRNCPY_S((char *)sec_event.key_notif.bd_name,bta_dm_get_remname(), BD_NAME_LEN); + sec_event.key_notif.bd_name[BD_NAME_LEN] = '\0'; + sec_event.key_notif.passkey = p_data->key_notif; + bta_dm_cb.p_sec_cback(BTA_DM_BLE_NC_REQ_EVT, &sec_event); + break; + + case BTM_LE_KEY_EVT: + bdcpy(sec_event.ble_key.bd_addr, bda); + sec_event.ble_key.key_type = p_data->key.key_type; + sec_event.ble_key.p_key_value = p_data->key.p_key_value; + bta_dm_cb.p_sec_cback(BTA_DM_BLE_KEY_EVT, &sec_event); + break; + + case BTM_LE_COMPLT_EVT: + bdcpy(sec_event.auth_cmpl.bd_addr, bda); +#if BLE_INCLUDED == TRUE + BTM_ReadDevInfo(bda, &sec_event.auth_cmpl.dev_type, &sec_event.auth_cmpl.addr_type); +#endif + p_name = BTM_SecReadDevName(bda); + if (p_name != NULL) { + BCM_STRNCPY_S((char *)sec_event.auth_cmpl.bd_name, p_name, BD_NAME_LEN); + sec_event.auth_cmpl.bd_name[BD_NAME_LEN] = '\0'; + } else { + sec_event.auth_cmpl.bd_name[0] = '\0'; + } + if (p_data->complt.reason != 0) { + sec_event.auth_cmpl.fail_reason = BTA_DM_AUTH_CONVERT_SMP_CODE(((UINT8)p_data->complt.reason)); + /* delete this device entry from Sec Dev DB */ + bta_dm_remove_sec_dev_entry (bda); + } else { + sec_event.auth_cmpl.success = TRUE; + if (!p_data->complt.smp_over_br) { + + } + } + sec_event.auth_cmpl.auth_mode = p_data->complt.auth_mode; + if (bta_dm_cb.p_sec_cback) { + //bta_dm_cb.p_sec_cback(BTA_DM_AUTH_CMPL_EVT, &sec_event); + bta_dm_cb.p_sec_cback(BTA_DM_BLE_AUTH_CMPL_EVT, &sec_event); + } + + break; + + default: + status = BTM_NOT_AUTHORIZED; + break; + } + return status; +} + +/******************************************************************************* +** +** Function bta_dm_ble_id_key_cback +** +** Description Callback for BLE local ID keys +** +** +** Returns void +** +*******************************************************************************/ +static void bta_dm_ble_id_key_cback (UINT8 key_type, tBTM_BLE_LOCAL_KEYS *p_key) +{ + UINT8 evt; + tBTA_DM_SEC dm_key; + + switch (key_type) { + case BTM_BLE_KEY_TYPE_ID: + case BTM_BLE_KEY_TYPE_ER: + if (bta_dm_cb.p_sec_cback) { + memcpy(&dm_key.ble_id_keys, p_key, sizeof(tBTM_BLE_LOCAL_KEYS)); + + evt = (key_type == BTM_BLE_KEY_TYPE_ID) ? BTA_DM_BLE_LOCAL_IR_EVT : \ + BTA_DM_BLE_LOCAL_ER_EVT; + bta_dm_cb.p_sec_cback(evt, &dm_key); + } + break; + + default: + APPL_TRACE_DEBUG("Unknown key type %d", key_type); + break; + } + return; + +} + +/******************************************************************************* +** +** Function bta_dm_add_blekey +** +** Description This function adds an BLE Key to an security database entry. +** This function shall only be called AFTER BTA_DmAddBleDevice has been called. +** It is normally called during host startup to restore all required information +** stored in the NVRAM. +** +** Parameters: +** +*******************************************************************************/ +void bta_dm_add_blekey (tBTA_DM_MSG *p_data) +{ + if (!BTM_SecAddBleKey (p_data->add_ble_key.bd_addr, + (tBTM_LE_KEY_VALUE *)&p_data->add_ble_key.blekey, + p_data->add_ble_key.key_type)) { + APPL_TRACE_ERROR ("BTA_DM: Error adding BLE Key for device %08x%04x", + (p_data->add_ble_key.bd_addr[0] << 24) + (p_data->add_ble_key.bd_addr[1] << 16) + \ + (p_data->add_ble_key.bd_addr[2] << 8) + p_data->add_ble_key.bd_addr[3], + (p_data->add_ble_key.bd_addr[4] << 8) + p_data->add_ble_key.bd_addr[5]); + } +} + +/******************************************************************************* +** +** Function bta_dm_add_ble_device +** +** Description This function adds an BLE device to an security database entry. +** It is normally called during host startup to restore all required information +** stored in the NVRAM. +** +** Parameters: +** +*******************************************************************************/ +void bta_dm_add_ble_device (tBTA_DM_MSG *p_data) +{ + if (!BTM_SecAddBleDevice (p_data->add_ble_device.bd_addr, NULL, + p_data->add_ble_device.dev_type , + p_data->add_ble_device.addr_type, + p_data->add_ble_device.auth_mode)) { + APPL_TRACE_ERROR ("BTA_DM: Error adding BLE Device for device %08x%04x", + (p_data->add_ble_device.bd_addr[0] << 24) + (p_data->add_ble_device.bd_addr[1] << 16) + \ + (p_data->add_ble_device.bd_addr[2] << 8) + p_data->add_ble_device.bd_addr[3], + (p_data->add_ble_device.bd_addr[4] << 8) + p_data->add_ble_device.bd_addr[5]); + } +} + +/******************************************************************************* +** +** Function bta_dm_add_ble_device +** +** Description This function adds an BLE device to an security database entry. +** It is normally called during host startup to restore all required information +** stored in the NVRAM. +** +** Parameters: +** +*******************************************************************************/ +void bta_dm_ble_passkey_reply (tBTA_DM_MSG *p_data) +{ + if (p_data->pin_reply.accept) { + BTM_BlePasskeyReply(p_data->ble_passkey_reply.bd_addr, BTM_SUCCESS, p_data->ble_passkey_reply.passkey); + } else { + BTM_BlePasskeyReply(p_data->ble_passkey_reply.bd_addr, BTM_NOT_AUTHORIZED, p_data->ble_passkey_reply.passkey); + } + +} + +void bta_dm_ble_set_static_passkey(tBTA_DM_MSG *p_data) +{ + BTM_BleSetStaticPasskey(p_data->ble_set_static_passkey.add, p_data->ble_set_static_passkey.static_passkey); +} +/******************************************************************************* +** +** Function bta_dm_ble_confirm_reply +** +** Description This is response to SM numeric comparison request submitted +** to application. +** +** Parameters: +** +*******************************************************************************/ +void bta_dm_ble_confirm_reply (tBTA_DM_MSG *p_data) +{ + if (p_data->confirm.accept) { + BTM_BleConfirmReply(p_data->confirm.bd_addr, BTM_SUCCESS); + } else { + BTM_BleConfirmReply(p_data->ble_passkey_reply.bd_addr, BTM_NOT_AUTHORIZED); + } +} + +/******************************************************************************* +** +** Function bta_dm_security_grant +** +** Description This function grant SMP security request access. +** +** Parameters: +** +*******************************************************************************/ +void bta_dm_security_grant (tBTA_DM_MSG *p_data) +{ + BTM_SecurityGrant(p_data->ble_sec_grant.bd_addr, p_data->ble_sec_grant.res); +} +#endif ///SMP_INCLUDED == TRUE + +/******************************************************************************* +** +** Function bta_dm_ble_set_bg_conn_type +** +** Description This function set the BLE background connection type +** +** Parameters: +** +*******************************************************************************/ +void bta_dm_ble_set_bg_conn_type (tBTA_DM_MSG *p_data) +{ + BTM_BleSetBgConnType(p_data->ble_set_bd_conn_type.bg_conn_type, + p_data->ble_set_bd_conn_type.p_select_cback); +} + +/******************************************************************************* +** +** Function bta_dm_ble_set_conn_params +** +** Description This function set the preferred connection parameters. +** +** Parameters: +** +*******************************************************************************/ +void bta_dm_ble_set_conn_params (tBTA_DM_MSG *p_data) +{ + BTM_BleSetPrefConnParams(p_data->ble_set_conn_params.peer_bda, + p_data->ble_set_conn_params.conn_int_min, + p_data->ble_set_conn_params.conn_int_max, + p_data->ble_set_conn_params.slave_latency, + p_data->ble_set_conn_params.supervision_tout); + + BTM_BleConfigConnParams(p_data->ble_set_conn_params.conn_int_min, p_data->ble_set_conn_params.conn_int_max, + p_data->ble_set_conn_params.slave_latency, p_data->ble_set_conn_params.supervision_tout); +} + +/******************************************************************************* +** +** Function bta_dm_ble_set_conn_scan_params +** +** Description This function sets BLE scan parameters. +** +** Parameters: +** +*******************************************************************************/ +void bta_dm_ble_set_scan_params(tBTA_DM_MSG *p_data) +{ + BTM_BleSetScanParams(p_data->ble_set_scan_params.client_if, + p_data->ble_set_scan_params.scan_int, + p_data->ble_set_scan_params.scan_window, + p_data->ble_set_scan_params.scan_mode, + p_data->ble_set_scan_params.scan_param_setup_cback); +} + +/******************************************************************************* +** +** Function bta_dm_ble_set_scan_fil_params +** +** Description This function sets BLE scan filter & parameters. +** +** Parameters: +** +*******************************************************************************/ +void bta_dm_ble_set_scan_fil_params(tBTA_DM_MSG *p_data) +{ + tBTA_STATUS status = BTA_FAILURE; + + if (BTM_BleSetScanFilterParams (p_data->ble_set_scan_fil_params.client_if, + p_data->ble_set_scan_fil_params.scan_int, + p_data->ble_set_scan_fil_params.scan_window, + p_data->ble_set_scan_fil_params.scan_mode, + p_data->ble_set_scan_fil_params.addr_type_own, + p_data->ble_set_scan_fil_params.scan_duplicate_filter, + p_data->ble_set_scan_fil_params.scan_filter_policy, + p_data->ble_set_scan_fil_params.scan_param_setup_cback) == BTM_SUCCESS) { + status = BTA_SUCCESS; + + } else { + APPL_TRACE_ERROR("%s(), fail to set scan params.", __func__); + } + if (p_data->ble_set_scan_fil_params.scan_param_setup_cback != NULL) { + p_data->ble_set_scan_fil_params.scan_param_setup_cback(p_data->ble_set_scan_fil_params.client_if, status); + } + +} + + +/******************************************************************************* +** +** Function bta_dm_ble_set_conn_scan_params +** +** Description This function set the preferred connection scan parameters. +** +** Parameters: +** +*******************************************************************************/ +void bta_dm_ble_set_conn_scan_params (tBTA_DM_MSG *p_data) +{ + BTM_BleSetConnScanParams(p_data->ble_set_conn_scan_params.scan_int, + p_data->ble_set_conn_scan_params.scan_window); +} +/******************************************************************************* +** +** Function bta_dm_ble_update_conn_params +** +** Description This function update LE connection parameters. +** +** Parameters: +** +*******************************************************************************/ +void bta_dm_ble_update_conn_params (tBTA_DM_MSG *p_data) +{ + if (!L2CA_UpdateBleConnParams(p_data->ble_update_conn_params.bd_addr, + p_data->ble_update_conn_params.min_int, + p_data->ble_update_conn_params.max_int, + p_data->ble_update_conn_params.latency, + p_data->ble_update_conn_params.timeout)) { + APPL_TRACE_ERROR("Update connection parameters failed!"); + } +} +/******************************************************************************* +** +** Function bta_dm_ble_disconnect +** +** Description This function disconnect the ble connection. +** +** Parameters: +** +*******************************************************************************/ +void bta_dm_ble_disconnect (tBTA_DM_MSG *p_data) +{ + L2CA_BleDisconnect(p_data->ble_disconnect.remote_bda); +} + +/******************************************************************************* +** +** Function bta_dm_ble_set_rand_address +** +** Description This function set the LE random address for the device. +** +** Parameters: rand_addr:the random address whitch should be setting +** Explanation: This function added by Yulong at 2016/9/9 +*******************************************************************************/ +void bta_dm_ble_set_rand_address(tBTA_DM_MSG *p_data) +{ + tBTM_STATUS status = BTM_SET_STATIC_RAND_ADDR_FAIL; + if (p_data->set_addr.addr_type != BLE_ADDR_RANDOM) { + APPL_TRACE_ERROR("Invalid random adress type = %d\n", p_data->set_addr.addr_type); + if(p_data->set_addr.p_set_rand_addr_cback) { + (*p_data->set_addr.p_set_rand_addr_cback)(status); + } + return; + } + //send the setting random address to BTM layer + status = BTM_BleSetRandAddress(p_data->set_addr.address); + if(p_data->set_addr.p_set_rand_addr_cback) { + (*p_data->set_addr.p_set_rand_addr_cback)(status); + } + +} + +void bta_dm_ble_clear_rand_address(tBTA_DM_MSG *p_data) +{ + UNUSED(p_data); + BTM_BleClearRandAddress(); +} + +/******************************************************************************* +** +** Function bta_dm_ble_stop_advertising +** +** Description This function stop the BLE avdertising for the device. +** +** Parameters: void +** Explanation: This function added by Yulong at 2016/10/19 +*******************************************************************************/ +void bta_dm_ble_stop_advertising(tBTA_DM_MSG *p_data) +{ + if (p_data->hdr.event != BTA_DM_API_BLE_STOP_ADV_EVT) { + APPL_TRACE_ERROR("Invalid BTA event,can't stop the BLE adverting\n"); + } + + btm_ble_stop_adv(); +} + + + +#if BLE_PRIVACY_SPT == TRUE +/******************************************************************************* +** +** Function bta_dm_ble_config_local_privacy +** +** Description This function set the local device LE privacy settings. +** +** Parameters: +** +*******************************************************************************/ +void bta_dm_ble_config_local_privacy (tBTA_DM_MSG *p_data) +{ + BTM_BleConfigPrivacy (p_data->ble_local_privacy.privacy_enable, p_data->ble_local_privacy.set_local_privacy_cback); +} +#endif + +/******************************************************************************* +** +** Function bta_dm_ble_config_local_icon +** +** Description This function sets the local icon value. +** +** +*******************************************************************************/ +void bta_dm_ble_config_local_icon (tBTA_DM_MSG *p_data) +{ + BTM_BleConfigLocalIcon (p_data->ble_local_icon.icon); +} + +/******************************************************************************* +** +** Function bta_dm_ble_observe +** +** Description This function set the preferred connection scan parameters. +** +** Parameters: +** +*******************************************************************************/ +void bta_dm_ble_observe (tBTA_DM_MSG *p_data) +{ + tBTM_STATUS status; + if (p_data->ble_observe.start) { + /*Save the callback to be called when a scan results are available */ + bta_dm_search_cb.p_scan_cback = p_data->ble_observe.p_cback; + + if ((status = BTM_BleObserve(TRUE, p_data->ble_observe.duration, + bta_dm_observe_results_cb, bta_dm_observe_cmpl_cb)) != BTM_CMD_STARTED) { + APPL_TRACE_WARNING(" %s start observe failed. status=0x%x\n", __FUNCTION__, status); + } + + if (p_data->ble_observe.p_start_scan_cback) { + status = (status == BTM_CMD_STARTED ? BTA_SUCCESS : BTA_FAILURE); + p_data->ble_observe.p_start_scan_cback(status); + } + } else { + bta_dm_search_cb.p_scan_cback = NULL; + status = BTM_BleObserve(FALSE, 0, NULL, NULL); + + if (status != BTM_CMD_STARTED){ + APPL_TRACE_WARNING(" %s stop observe failed, status=0x%x\n", __FUNCTION__, status); + } + + if (p_data->ble_observe.p_stop_scan_cback) { + status = (status == BTM_CMD_STARTED ? BTA_SUCCESS : BTA_FAILURE); + p_data->ble_observe.p_stop_scan_cback(status); + } + } +} + +/******************************************************************************* +** +** Function bta_dm_ble_scan +** +** Description This function set the preferred connection scan parameters. +** +** Parameters: +** +*******************************************************************************/ +void bta_dm_ble_scan (tBTA_DM_MSG *p_data) +{ + tBTM_STATUS status; + if (p_data->ble_scan.start) { + /*Save the callback to be called when a scan results are available */ + bta_dm_search_cb.p_scan_cback = p_data->ble_scan.p_cback; + + if ((status = BTM_BleScan(TRUE, p_data->ble_scan.duration, + bta_dm_observe_results_cb, bta_dm_observe_cmpl_cb, bta_dm_observe_discard_cb)) != BTM_CMD_STARTED) { + APPL_TRACE_WARNING(" %s start scan failed. status=0x%x\n", __FUNCTION__, status); + } + + if (p_data->ble_scan.p_start_scan_cback) { + status = (status == BTM_CMD_STARTED ? BTA_SUCCESS : BTA_FAILURE); + p_data->ble_scan.p_start_scan_cback(status); + } + } else { + bta_dm_search_cb.p_scan_cback = NULL; + status = BTM_BleScan(FALSE, 0, NULL, NULL, NULL); + + if (status != BTM_CMD_STARTED){ + APPL_TRACE_WARNING(" %s stop scan failed, status=0x%x\n", __FUNCTION__, status); + } + + if (p_data->ble_scan.p_stop_scan_cback) { + status = (status == BTM_CMD_STARTED ? BTA_SUCCESS : BTA_FAILURE); + p_data->ble_scan.p_stop_scan_cback(status); + } + } +} + +/******************************************************************************* +** +** Function bta_dm_ble_set_adv_params +** +** Description This function set the adv parameters. +** +** Parameters: +** +*******************************************************************************/ +void bta_dm_ble_set_adv_params (tBTA_DM_MSG *p_data) +{ + BTM_BleSetAdvParams(p_data->ble_set_adv_params.adv_int_min, + p_data->ble_set_adv_params.adv_int_max, + p_data->ble_set_adv_params.p_dir_bda, + BTA_DM_BLE_ADV_CHNL_MAP); +} + +/******************************************************************************* +** +** Function bta_dm_ble_set_adv_params_all +** +** Description This function is called to set all of the advertising parameters. +** +** Parameters: None. +** +** Returns void +** +*******************************************************************************/ +void bta_dm_ble_set_adv_params_all (tBTA_DM_MSG *p_data) +{ + tBTA_STATUS status = BTA_FAILURE; + if (BTM_BleSetAdvParamsAll(p_data->ble_set_adv_params_all.adv_int_min, + p_data->ble_set_adv_params_all.adv_int_max, + p_data->ble_set_adv_params_all.adv_type, + p_data->ble_set_adv_params_all.addr_type_own, + p_data->ble_set_adv_params_all.p_dir_bda, + p_data->ble_set_adv_params_all.channel_map, + p_data->ble_set_adv_params_all.adv_filter_policy, + p_data->ble_set_adv_params_all.p_start_adv_cback) == BTM_SUCCESS) { + APPL_TRACE_DEBUG("%s(), success to set ble adv params.", __func__); + } else { + APPL_TRACE_ERROR("%s(), fail to set ble adv params.", __func__); + if(p_data->ble_set_adv_params_all.p_start_adv_cback) { + (*p_data->ble_set_adv_params_all.p_start_adv_cback)(status); + } + return; + } + if(BTM_BleStartAdv() == BTM_SUCCESS) { + status = BTA_SUCCESS; + } + if(p_data->ble_set_adv_params_all.p_start_adv_cback) { + (*p_data->ble_set_adv_params_all.p_start_adv_cback)(status); + } +} + +/******************************************************************************* +** +** Function bta_dm_ble_update_duplicate_exceptional_list +** +** Description This function is to update duplicate scan exceptional list +** +** +*******************************************************************************/ +void bta_dm_ble_update_duplicate_exceptional_list(tBTA_DM_MSG *p_data) +{ + BTM_UpdateBleDuplicateExceptionalList(p_data->ble_duplicate_exceptional_list.subcode, + p_data->ble_duplicate_exceptional_list.type, + p_data->ble_duplicate_exceptional_list.device_info, + p_data->ble_duplicate_exceptional_list.exceptional_list_cb); +} + +/******************************************************************************* +** +** Function bta_dm_ble_set_adv_config +** +** Description This function set the customized ADV data configuration +** +** Parameters: +** +*******************************************************************************/ +void bta_dm_ble_set_adv_config (tBTA_DM_MSG *p_data) +{ + tBTA_STATUS status = BTA_FAILURE; + + if (BTM_BleWriteAdvData(p_data->ble_set_adv_data.data_mask, + (tBTM_BLE_ADV_DATA *)p_data->ble_set_adv_data.p_adv_cfg) == BTM_SUCCESS) { + status = BTA_SUCCESS; + } + + if (p_data->ble_set_adv_data.p_adv_data_cback) { + (*p_data->ble_set_adv_data.p_adv_data_cback)(status); + } +} + +/******************************************************************************* +** +** Function bta_dm_ble_set_long_adv +** +** Description This function set the long ADV data +** +** Parameters: +** +*******************************************************************************/ +void bta_dm_ble_set_long_adv (tBTA_DM_MSG *p_data) +{ + tBTA_STATUS status = BTA_FAILURE; + + if (BTM_BleWriteLongAdvData(p_data->ble_set_long_adv_data.adv_data, + p_data->ble_set_long_adv_data.adv_data_len) == BTM_SUCCESS) { + status = BTA_SUCCESS; + } + + if (p_data->ble_set_adv_data.p_adv_data_cback) { + (*p_data->ble_set_adv_data.p_adv_data_cback)(status); + } +} + +/******************************************************************************* +** +** Function bta_dm_ble_set_adv_config_raw +** +** Description This function set the customized ADV data configuration +** +** Parameters: +** +*******************************************************************************/ +void bta_dm_ble_set_adv_config_raw (tBTA_DM_MSG *p_data) +{ + tBTA_STATUS status = BTA_FAILURE; + + if (BTM_BleWriteAdvDataRaw(p_data->ble_set_adv_data_raw.p_raw_adv, + p_data->ble_set_adv_data_raw.raw_adv_len) == BTM_SUCCESS) { + status = BTA_SUCCESS; + } + + if (p_data->ble_set_adv_data_raw.p_adv_data_cback) { + (*p_data->ble_set_adv_data_raw.p_adv_data_cback)(status); + } +} + + +/******************************************************************************* +** +** Function bta_dm_ble_set_scan_rsp +** +** Description This function set the customized ADV scan resp. configuration +** +** Parameters: +** +*******************************************************************************/ +void bta_dm_ble_set_scan_rsp (tBTA_DM_MSG *p_data) +{ + tBTA_STATUS status = BTA_FAILURE; + + if (BTM_BleWriteScanRsp(p_data->ble_set_adv_data.data_mask, + (tBTM_BLE_ADV_DATA *)p_data->ble_set_adv_data.p_adv_cfg) == BTM_SUCCESS) { + status = BTA_SUCCESS; + } + + if (p_data->ble_set_adv_data.p_adv_data_cback) { + (*p_data->ble_set_adv_data.p_adv_data_cback)(status); + } +} + +/******************************************************************************* +** +** Function bta_dm_ble_set_scan_rsp_raw +** +** Description This function set the raw scan response data +** +** Parameters: +** +*******************************************************************************/ +void bta_dm_ble_set_scan_rsp_raw (tBTA_DM_MSG *p_data) +{ + tBTA_STATUS status = BTA_FAILURE; + + if (BTM_BleWriteScanRspRaw(p_data->ble_set_adv_data_raw.p_raw_adv, + p_data->ble_set_adv_data_raw.raw_adv_len) == BTM_SUCCESS) { + status = BTA_SUCCESS; + } + + if (p_data->ble_set_adv_data_raw.p_adv_data_cback) { + (*p_data->ble_set_adv_data_raw.p_adv_data_cback)(status); + } +} + +/******************************************************************************* +** +** Function bta_dm_ble_set_data_length +** +** Description This function set the maximum transmission packet size +** +** Parameters +** +*******************************************************************************/ +void bta_dm_ble_set_data_length(tBTA_DM_MSG *p_data) +{ + UINT8 status = BTM_SUCCESS; + tACL_CONN *p_acl_cb = btm_bda_to_acl(p_data->ble_set_data_length.remote_bda, BT_TRANSPORT_LE); + if (p_acl_cb == NULL) { + APPL_TRACE_ERROR("%s error: Invalid connection remote_bda.", __func__); + return; + } + + p_acl_cb->p_set_pkt_data_cback = p_data->ble_set_data_length.p_set_pkt_data_cback; + // if the value of the data length is same, triger callback directly + if(p_data->ble_set_data_length.tx_data_length == p_acl_cb->data_length_params.tx_len) { + if(p_data->ble_set_data_length.p_set_pkt_data_cback) { + (*p_data->ble_set_data_length.p_set_pkt_data_cback)(status, &p_acl_cb->data_length_params); + } + return; + } + + if(p_acl_cb->data_len_updating) { + // aleady have one cmd + if(p_acl_cb->data_len_waiting) { + status = BTM_ILLEGAL_ACTION; + } else { + // save the command + p_acl_cb->p_set_data_len_cback_waiting = p_data->ble_set_data_length.p_set_pkt_data_cback; + p_acl_cb->tx_len_waiting = p_data->ble_set_data_length.tx_data_length; + p_acl_cb->data_len_waiting = true; + return; + } + } else { + status = BTM_SetBleDataLength(p_data->ble_set_data_length.remote_bda, + p_data->ble_set_data_length.tx_data_length); + } + if (status != BTM_SUCCESS) { + APPL_TRACE_ERROR("%s failed\n", __FUNCTION__); + } + if (p_data->ble_set_data_length.p_set_pkt_data_cback && status != BTM_SUCCESS) { + if (p_acl_cb->data_length_params.tx_len == 0){ + uint16_t length = controller_get_interface()->get_acl_data_size_ble(); + p_acl_cb->data_length_params.rx_len = length; + p_acl_cb->data_length_params.tx_len = length; + } + (*p_data->ble_set_data_length.p_set_pkt_data_cback)(status, &p_acl_cb->data_length_params); + } + +} + +/******************************************************************************* +** +** Function bta_dm_ble_broadcast +** +** Description Starts or stops LE broadcasts +** +** Parameters: +** +*******************************************************************************/ +void bta_dm_ble_broadcast (tBTA_DM_MSG *p_data) +{ + tBTA_STATUS status = BTA_FAILURE; + BOOLEAN start = p_data->ble_observe.start; + + if (BTM_BleBroadcast(start, p_data->ble_observe.p_stop_adv_cback) == BTM_SUCCESS) { + status = BTA_SUCCESS; + } else { + APPL_TRACE_ERROR("%s failed\n", __FUNCTION__); + } + + if (p_data->ble_observe.p_stop_adv_cback){ + (*p_data->ble_observe.p_stop_adv_cback)(status); + } + +} + +/******************************************************************************* +** +** Function bta_dm_ble_multi_adv_enb +** +** Description This function enables a single advertising instance +** +** Parameters: +** +*******************************************************************************/ +void bta_dm_ble_multi_adv_enb(tBTA_DM_MSG *p_data) +{ + tBTM_STATUS btm_status = 0; + + bta_dm_cb.p_multi_adv_cback = p_data->ble_multi_adv_enb.p_cback; + if (BTM_BleMaxMultiAdvInstanceCount() > 0 && NULL != p_data->ble_multi_adv_enb.p_ref) { + btm_status = BTM_BleEnableAdvInstance((tBTM_BLE_ADV_PARAMS *) + p_data->ble_multi_adv_enb.p_params, + p_data->ble_multi_adv_enb.p_cback, + p_data->ble_multi_adv_enb.p_ref); + } + + if (BTM_CMD_STARTED != btm_status) { + bta_dm_cb.p_multi_adv_cback(BTA_BLE_MULTI_ADV_ENB_EVT, 0xFF, + p_data->ble_multi_adv_enb.p_ref, BTA_FAILURE); + } +} +/******************************************************************************* +** +** Function bta_dm_ble_multi_adv_param_upd +** +** Description This function updates multiple advertising instance parameters +** +** Parameters: +** +*******************************************************************************/ +void bta_dm_ble_multi_adv_upd_param(tBTA_DM_MSG *p_data) +{ + tBTM_STATUS btm_status = 0; + void *p_ref = NULL; + + if (BTM_BleMaxMultiAdvInstanceCount() > 0 && p_data->ble_multi_adv_param.inst_id > 0 + && p_data->ble_multi_adv_param.inst_id < BTM_BleMaxMultiAdvInstanceCount()) { + btm_status = BTM_BleUpdateAdvInstParam(p_data->ble_multi_adv_param.inst_id, + (tBTM_BLE_ADV_PARAMS *)p_data->ble_multi_adv_param.p_params); + } + + if (BTM_CMD_STARTED != btm_status) { + p_ref = btm_ble_multi_adv_get_ref(p_data->ble_multi_adv_param.inst_id); + bta_dm_cb.p_multi_adv_cback(BTA_BLE_MULTI_ADV_PARAM_EVT, + p_data->ble_multi_adv_param.inst_id, p_ref, BTA_FAILURE); + } +} +/******************************************************************************* +** +** Function bta_dm_ble_multi_adv_data +** +** Description This function write multiple advertising instance adv data +** or scan response data +** +** Parameters: +** +*******************************************************************************/ +void bta_dm_ble_multi_adv_data(tBTA_DM_MSG *p_data) +{ + tBTM_STATUS btm_status = 0; + void *p_ref = NULL; + + if (BTM_BleMaxMultiAdvInstanceCount() > 0 && p_data->ble_multi_adv_data.inst_id > 0 + && p_data->ble_multi_adv_data.inst_id < BTM_BleMaxMultiAdvInstanceCount()) { + btm_status = BTM_BleCfgAdvInstData(p_data->ble_multi_adv_data.inst_id, + p_data->ble_multi_adv_data.is_scan_rsp, + p_data->ble_multi_adv_data.data_mask, + (tBTM_BLE_ADV_DATA *)p_data->ble_multi_adv_data.p_data); + } + + if (BTM_CMD_STARTED != btm_status) { + p_ref = btm_ble_multi_adv_get_ref(p_data->ble_multi_adv_data.inst_id); + bta_dm_cb.p_multi_adv_cback(BTA_BLE_MULTI_ADV_DATA_EVT, + p_data->ble_multi_adv_data.inst_id, p_ref, BTA_FAILURE); + } + +} +/******************************************************************************* +** +** Function btm_dm_ble_multi_adv_disable +** +** Description This function disable a single adv instance +** +** Parameters: +** +*******************************************************************************/ +void btm_dm_ble_multi_adv_disable(tBTA_DM_MSG *p_data) +{ + tBTM_STATUS btm_status = 0; + void *p_ref = NULL; + + if (BTM_BleMaxMultiAdvInstanceCount() > 0 && p_data->ble_multi_adv_disable.inst_id > 0 + && p_data->ble_multi_adv_disable.inst_id < BTM_BleMaxMultiAdvInstanceCount()) { + btm_status = BTM_BleDisableAdvInstance(p_data->ble_multi_adv_disable.inst_id); + } + + if (BTM_CMD_STARTED != btm_status) { + p_ref = btm_ble_multi_adv_get_ref(p_data->ble_multi_adv_disable.inst_id); + bta_dm_cb.p_multi_adv_cback(BTA_BLE_MULTI_ADV_DISABLE_EVT, + p_data->ble_multi_adv_disable.inst_id, p_ref, BTA_FAILURE); + } +} + +void bta_dm_ble_gap_dtm_tx_start(tBTA_DM_MSG *p_data) +{ + BTM_BleTransmitterTest(p_data->dtm_tx_start.tx_channel, p_data->dtm_tx_start.len_of_data, p_data->dtm_tx_start.pkt_payload, p_data->dtm_tx_start.p_dtm_cmpl_cback); +} + +void bta_dm_ble_gap_dtm_rx_start(tBTA_DM_MSG *p_data) +{ + BTM_BleReceiverTest(p_data->dtm_rx_start.rx_channel, p_data->dtm_rx_start.p_dtm_cmpl_cback); +} + +void bta_dm_ble_gap_dtm_stop(tBTA_DM_MSG *p_data) +{ + BTM_BleTestEnd(p_data->dtm_stop.p_dtm_cmpl_cback); +} + +void bta_dm_ble_gap_clear_adv(tBTA_DM_MSG *p_data) +{ + if (BTM_BleClearAdv(p_data->ble_clear_adv.p_clear_adv_cback) == FALSE) { + if (p_data->ble_clear_adv.p_clear_adv_cback) { + (*p_data->ble_clear_adv.p_clear_adv_cback)(BTA_FAILURE); + } + } +} + +#if (BLE_50_FEATURE_SUPPORT == TRUE) +void bta_dm_ble_gap_dtm_enhance_tx_start(tBTA_DM_MSG *p_data) +{ + BTM_BleEnhancedTransmitterTest(p_data->dtm_enh_tx_start.tx_channel, p_data->dtm_enh_tx_start.len_of_data, + p_data->dtm_enh_tx_start.pkt_payload, p_data->dtm_enh_tx_start.phy, p_data->dtm_enh_tx_start.p_dtm_cmpl_cback); +} + +void bta_dm_ble_gap_dtm_enhance_rx_start(tBTA_DM_MSG *p_data) +{ + BTM_BleEnhancedReceiverTest(p_data->dtm_enh_rx_start.rx_channel, p_data->dtm_enh_rx_start.phy, + p_data->dtm_enh_rx_start.modulation_index, p_data->dtm_enh_rx_start.p_dtm_cmpl_cback); +} + +void bta_dm_ble_gap_read_phy(tBTA_DM_MSG *p_data) +{ + //tBTM_STATUS btm_status = 0; + UINT8 tx_phy = 0, rx_phy = 0; + + BTM_BleReadPhy(p_data->ble_read_phy.bd_addr, &tx_phy, &rx_phy); + +} + +void bta_dm_ble_gap_set_prefer_default_phy(tBTA_DM_MSG *p_data) +{ + APPL_TRACE_API("%s, tx_phy_mask = %d, rx_phy_mask = %d", __func__, p_data->ble_set_per_def_phy.tx_phy_mask, p_data->ble_set_per_def_phy.rx_phy_mask); + BTM_BleSetPreferDefaultPhy(p_data->ble_set_per_def_phy.tx_phy_mask, p_data->ble_set_per_def_phy.rx_phy_mask); +} + +void bta_dm_ble_gap_set_prefer_phy(tBTA_DM_MSG *p_data) +{ + APPL_TRACE_API("%s, tx_phy_mask = %d, rx_phy_mask = %d, phy_options = %d", + __func__, p_data->ble_set_per_phy.tx_phy_mask, + p_data->ble_set_per_phy.rx_phy_mask, p_data->ble_set_per_phy.phy_options); + BTM_BleSetPreferPhy(p_data->ble_set_per_phy.bd_addr, p_data->ble_set_per_phy.all_phys, + p_data->ble_set_per_phy.tx_phy_mask, p_data->ble_set_per_phy.rx_phy_mask, + p_data->ble_set_per_phy.phy_options); +} + +void bta_dm_ble_gap_ext_adv_set_rand_addr(tBTA_DM_MSG *p_data) +{ + BTM_BleSetExtendedAdvRandaddr(p_data->ble_set_ext_adv_rand_addr.instance, p_data->ble_set_ext_adv_rand_addr.rand_addr); +} +void bta_dm_ble_gap_ext_adv_set_params(tBTA_DM_MSG *p_data) +{ + APPL_TRACE_API("%s, instance = %d", __func__, p_data->ble_set_ext_adv_params.instance); + + BTM_BleSetExtendedAdvParams(p_data->ble_set_ext_adv_params.instance, + (tBTM_BLE_GAP_EXT_ADV_PARAMS *)&p_data->ble_set_ext_adv_params.params); +} + +void bta_dm_ble_gap_config_ext_adv_data_raw(tBTA_DM_MSG *p_data) +{ + APPL_TRACE_API("%s, instance = %d, len = %d", __func__, p_data->ble_cfg_ext_adv_data.instance, + p_data->ble_cfg_ext_adv_data.length); + + BTM_BleConfigExtendedAdvDataRaw(p_data->ble_cfg_ext_adv_data.is_scan_rsp, + p_data->ble_cfg_ext_adv_data.instance, + p_data->ble_cfg_ext_adv_data.length, + p_data->ble_cfg_ext_adv_data.data); +} + +void bta_dm_ble_gap_start_ext_adv(tBTA_DM_MSG *p_data) +{ + APPL_TRACE_API("%s, enable = %d, num = %d", __func__, p_data->ble_start_ext_adv.enable, + p_data->ble_start_ext_adv.num); + BTM_BleStartExtAdv(p_data->ble_start_ext_adv.enable, p_data->ble_start_ext_adv.num, + (tBTM_BLE_EXT_ADV *)p_data->ble_start_ext_adv.ext_adv); +} + +void bta_dm_ble_gap_ext_adv_set_remove(tBTA_DM_MSG *p_data) +{ + BTM_BleExtAdvSetRemove(p_data->ble_ext_adv_set_remove.instance); +} + +void bta_dm_ble_gap_ext_adv_set_clear(tBTA_DM_MSG *p_data) +{ + BTM_BleExtAdvSetClear(); +} + +void bta_dm_ble_gap_periodic_adv_set_params(tBTA_DM_MSG *p_data) +{ + APPL_TRACE_API("%s, instance = %d", __func__, p_data->ble_set_periodic_adv_params.instance); + BTM_BlePeriodicAdvSetParams(p_data->ble_set_periodic_adv_params.instance, + (tBTM_BLE_Periodic_Adv_Params *)&p_data->ble_set_periodic_adv_params.params); +} + +void bta_dm_ble_gap_periodic_adv_cfg_data_raw(tBTA_DM_MSG *p_data) +{ + APPL_TRACE_API("%s, instance = %d, length = %d", __func__, p_data->ble_cfg_periodic_adv_data.instance, + p_data->ble_cfg_periodic_adv_data.length); + + BTM_BlePeriodicAdvCfgDataRaw(p_data->ble_cfg_periodic_adv_data.instance, + p_data->ble_cfg_periodic_adv_data.length, + p_data->ble_cfg_periodic_adv_data.data, + p_data->ble_cfg_periodic_adv_data.only_update_did); +} + +void bta_dm_ble_gap_periodic_adv_enable(tBTA_DM_MSG *p_data) +{ + APPL_TRACE_API("%s, instance = %d, enable = %d", __func__, p_data->ble_enable_periodic_adv.instance, + p_data->ble_enable_periodic_adv.enable); + + BTM_BlePeriodicAdvEnable(p_data->ble_enable_periodic_adv.instance, + p_data->ble_enable_periodic_adv.enable); +} + +void bta_dm_ble_gap_periodic_adv_create_sync(tBTA_DM_MSG *p_data) +{ + APPL_TRACE_API("%s", __func__); + BTM_BlePeriodicAdvCreateSync((tBTM_BLE_Periodic_Sync_Params *)&p_data->ble_periodic_adv_sync.params); +} + +void bta_dm_ble_gap_periodic_adv_sync_cancel(tBTA_DM_MSG *p_data) +{ + APPL_TRACE_API("%s", __func__); + BTM_BlePeriodicAdvSyncCancel(); +} + +void bta_dm_ble_gap_periodic_adv_sync_terminate(tBTA_DM_MSG *p_data) +{ + APPL_TRACE_API("%s", __func__); + BTM_BlePeriodicAdvSyncTerm(p_data->ble_periodic_adv_sync_term.sync_handle); +} + +void bta_dm_ble_gap_periodic_adv_add_dev_to_list(tBTA_DM_MSG *p_data) +{ + APPL_TRACE_API("%s, addr_type = %d, sid = %d", __func__, p_data->ble_periodic_adv_add_dev_to_list.addr_type, + p_data->ble_periodic_adv_add_dev_to_list.sid); + + BTM_BlePeriodicAdvAddDevToList(p_data->ble_periodic_adv_add_dev_to_list.addr_type, + p_data->ble_periodic_adv_add_dev_to_list.addr, + p_data->ble_periodic_adv_add_dev_to_list.sid); +} + +void bta_dm_ble_gap_periodic_adv_remove_dev_from_list(tBTA_DM_MSG *p_data) +{ + APPL_TRACE_API("%s, addr_type = %d, sid = %d", __func__, p_data->ble_periodic_adv_remove_dev_from_list.addr_type, + p_data->ble_periodic_adv_remove_dev_from_list.sid); + + BTM_BlePeriodicAdvRemoveDevFromList(p_data->ble_periodic_adv_remove_dev_from_list.addr_type, + p_data->ble_periodic_adv_remove_dev_from_list.addr, + p_data->ble_periodic_adv_remove_dev_from_list.sid); + +} + +void bta_dm_ble_gap_periodic_adv_clear_dev(tBTA_DM_MSG *p_data) +{ + APPL_TRACE_API("%s", __func__); + BTM_BlePeriodicAdvClearDev(); +} + + +void bta_dm_ble_gap_set_ext_scan_params(tBTA_DM_MSG *p_data) +{ + APPL_TRACE_API("%s", __func__); + BTM_BleSetExtendedScanParams((tBTM_BLE_EXT_SCAN_PARAMS *)&p_data->ble_set_ext_scan_params.params); +} + +void bta_dm_ble_gap_ext_scan(tBTA_DM_MSG *p_data) +{ + APPL_TRACE_API("%s, start = %d, duration = %d, period = %d", __func__, p_data->ble_ext_scan.start, p_data->ble_ext_scan.duration, + p_data->ble_ext_scan.period); + BTM_BleExtendedScan(p_data->ble_ext_scan.start, p_data->ble_ext_scan.duration, + p_data->ble_ext_scan.period); +} + +void bta_dm_ble_gap_set_prefer_ext_conn_params(tBTA_DM_MSG *p_data) +{ + tBTM_EXT_CONN_PARAMS conn_params; + conn_params.phy_mask = p_data->ble_set_per_ext_conn_params.phy_mask; + + APPL_TRACE_API("%s, start = %d, duration = %d, period = %d", __func__, p_data->ble_ext_scan.start, p_data->ble_ext_scan.duration, + p_data->ble_ext_scan.period); + + if (conn_params.phy_mask & BTA_PHY_1M_MASK) { + memcpy(&conn_params.phy_1m_conn_params, &p_data->ble_set_per_ext_conn_params.phy_1m_conn_params, + sizeof(tBTA_DM_BLE_CONN_PARAMS)); + } + + if (conn_params.phy_mask & BTA_PHY_2M_MASK) { + memcpy(&conn_params.phy_2m_conn_params, &p_data->ble_set_per_ext_conn_params.phy_2m_conn_params, + sizeof(tBTA_DM_BLE_CONN_PARAMS)); + } + + if (conn_params.phy_mask & BTAS_PHY_CODED_MASK) { + memcpy(&conn_params.phy_coded_conn_params, &p_data->ble_set_per_ext_conn_params.phy_coded_conn_params, + sizeof(tBTA_DM_BLE_CONN_PARAMS)); + } + + BTM_BleSetPreferExtenedConnParams(p_data->ble_set_per_ext_conn_params.bd_addr, &conn_params); +} + +#endif // #if (BLE_50_FEATURE_SUPPORT == TRUE) + +#if (BLE_FEAT_PERIODIC_ADV_SYNC_TRANSFER == TRUE) +void bta_dm_ble_gap_periodic_adv_recv_enable(tBTA_DM_MSG *p_data) +{ + BTM_BlePeriodicAdvRecvEnable(p_data->ble_periodic_adv_recv_enable.sync_handle, + p_data->ble_periodic_adv_recv_enable.enable); +} + +void bta_dm_ble_gap_periodic_adv_sync_trans(tBTA_DM_MSG *p_data) +{ + BTM_BlePeriodicAdvSyncTrans(p_data->ble_periodic_adv_sync_trans.addr, + p_data->ble_periodic_adv_sync_trans.service_data, + p_data->ble_periodic_adv_sync_trans.sync_handle); +} + +void bta_dm_ble_gap_periodic_adv_set_info_trans(tBTA_DM_MSG *p_data) +{ + BTM_BlePeriodicAdvSetInfoTrans(p_data->ble_periodic_adv_set_info_trans.addr, + p_data->ble_periodic_adv_set_info_trans.service_data, + p_data->ble_periodic_adv_set_info_trans.adv_hanlde); +} + +void bta_dm_ble_gap_set_periodic_adv_sync_trans_params(tBTA_DM_MSG *p_data) +{ + BTM_BleSetPeriodicAdvSyncTransParams(p_data->ble_set_past_params.addr, + p_data->ble_set_past_params.params.mode, + p_data->ble_set_past_params.params.skip, + p_data->ble_set_past_params.params.sync_timeout, + p_data->ble_set_past_params.params.cte_type); +} +#endif // #if (BLE_FEAT_PERIODIC_ADV_SYNC_TRANSFER == TRUE) +/******************************************************************************* +** +** Function bta_dm_ble_setup_storage +** +** Description This function configures up the storage parameters for ADV batch scanning +** +** Parameters: +** +*******************************************************************************/ +void bta_dm_ble_setup_storage (tBTA_DM_MSG *p_data) +{ + tBTM_STATUS btm_status = 0; + tBTM_BLE_VSC_CB cmn_ble_vsc_cb; + + BTM_BleGetVendorCapabilities(&cmn_ble_vsc_cb); + + if (0 != cmn_ble_vsc_cb.tot_scan_results_strg) { + btm_status = BTM_BleSetStorageConfig(p_data->ble_set_storage.batch_scan_full_max, + p_data->ble_set_storage.batch_scan_trunc_max, + p_data->ble_set_storage.batch_scan_notify_threshold, + p_data->ble_set_storage.p_setup_cback, + p_data->ble_set_storage.p_thres_cback, + p_data->ble_set_storage.p_read_rep_cback, + p_data->ble_set_storage.ref_value); + } + + if (BTM_CMD_STARTED != btm_status) { + bta_ble_scan_setup_cb(BTM_BLE_BATCH_SCAN_CFG_STRG_EVT, p_data->ble_set_storage.ref_value, + btm_status); + } +} + +/******************************************************************************* +** +** Function bta_dm_ble_enable_batch_scan +** +** Description This function sets up the parameters and enables batch scan +** +** Parameters: +** +*******************************************************************************/ +void bta_dm_ble_enable_batch_scan (tBTA_DM_MSG *p_data) +{ + tBTM_STATUS btm_status = 0; + tBTM_BLE_VSC_CB cmn_ble_vsc_cb; + + BTM_BleGetVendorCapabilities(&cmn_ble_vsc_cb); + + if (0 != cmn_ble_vsc_cb.tot_scan_results_strg) { + btm_status = BTM_BleEnableBatchScan(p_data->ble_enable_scan.scan_mode, + p_data->ble_enable_scan.scan_int, + p_data->ble_enable_scan.scan_window, + p_data->ble_enable_scan.discard_rule, + p_data->ble_enable_scan.addr_type, + p_data->ble_enable_scan.ref_value); + } + + if (BTM_CMD_STARTED != btm_status) { + bta_ble_scan_setup_cb(BTM_BLE_BATCH_SCAN_ENABLE_EVT, p_data->ble_enable_scan.ref_value, + btm_status); + } +} + +/******************************************************************************* +** +** Function bta_dm_ble_disable_batch_scan +** +** Description This function disables the batch scan +** +** Parameters: +** +*******************************************************************************/ +void bta_dm_ble_disable_batch_scan (tBTA_DM_MSG *p_data) +{ + UNUSED(p_data); + tBTM_STATUS btm_status = 0; + tBTM_BLE_VSC_CB cmn_ble_vsc_cb; + + BTM_BleGetVendorCapabilities(&cmn_ble_vsc_cb); + + if (0 != cmn_ble_vsc_cb.tot_scan_results_strg) { + btm_status = BTM_BleDisableBatchScan(p_data->ble_disable_scan.ref_value); + } + + if (BTM_CMD_STARTED != btm_status) { + bta_ble_scan_setup_cb(BTM_BLE_BATCH_SCAN_DISABLE_EVT, p_data->ble_enable_scan.ref_value, + btm_status); + } +} + +/******************************************************************************* +** +** Function bta_dm_ble_read_scan_reports +** +** Description This function reads the batch scan reports +** +** Parameters: +** +*******************************************************************************/ +void bta_dm_ble_read_scan_reports(tBTA_DM_MSG *p_data) +{ + tBTM_STATUS btm_status = 0; + tBTM_BLE_VSC_CB cmn_ble_vsc_cb; + + BTM_BleGetVendorCapabilities(&cmn_ble_vsc_cb); + + if (0 != cmn_ble_vsc_cb.tot_scan_results_strg) { + btm_status = BTM_BleReadScanReports(p_data->ble_read_reports.scan_type, + p_data->ble_read_reports.ref_value); + } + + if (BTM_CMD_STARTED != btm_status) { + bta_ble_scan_setup_cb(BTM_BLE_BATCH_SCAN_READ_REPTS_EVT, p_data->ble_enable_scan.ref_value, + btm_status); + } +} + +/******************************************************************************* +** +** Function bta_dm_ble_track_advertiser +** +** Description This function tracks the specific advertiser +** +** Parameters: +** +*******************************************************************************/ +void bta_dm_ble_track_advertiser(tBTA_DM_MSG *p_data) +{ + tBTM_STATUS btm_status = 0; + BD_ADDR bda; + memset(&bda, 0 , sizeof(BD_ADDR)); + tBTM_BLE_VSC_CB cmn_ble_vsc_cb; + tBTA_DM_BLE_TRACK_ADV_DATA track_adv_data; + + BTM_BleGetVendorCapabilities(&cmn_ble_vsc_cb); + + if (0 != cmn_ble_vsc_cb.tot_scan_results_strg) { + btm_status = BTM_BleTrackAdvertiser((tBTM_BLE_TRACK_ADV_CBACK *) + p_data->ble_track_advert.p_track_adv_cback, + p_data->ble_track_advert.ref_value); + } + + if (BTM_CMD_STARTED != btm_status) { + memset(&track_adv_data, 0, sizeof(tBTA_DM_BLE_TRACK_ADV_DATA)); + track_adv_data.advertiser_info_present = NO_ADV_INFO_PRESENT; /* Indicates failure */ + track_adv_data.client_if = (UINT8)p_data->ble_track_advert.ref_value; + p_data->ble_track_advert.p_track_adv_cback(&track_adv_data); + } +} + +/******************************************************************************* +** +** Function bta_ble_scan_setup_cb +** +** Description Handle the setup callback from BTM layer and forward it to app layer +** +** Parameters: +** +*******************************************************************************/ +void bta_ble_scan_setup_cb(tBTM_BLE_BATCH_SCAN_EVT evt, tBTM_BLE_REF_VALUE ref_value, + tBTM_STATUS status) +{ + tBTA_BLE_BATCH_SCAN_EVT bta_evt = 0; + + APPL_TRACE_DEBUG("bta_ble_scan_setup_cb : evt: %d, ref_value: %d, status:%d", evt, + ref_value, status); + + switch (evt) { + case BTM_BLE_BATCH_SCAN_ENABLE_EVT: + bta_evt = BTA_BLE_BATCH_SCAN_ENB_EVT; + break; + case BTM_BLE_BATCH_SCAN_CFG_STRG_EVT: + bta_evt = BTA_BLE_BATCH_SCAN_CFG_STRG_EVT; + break; + case BTM_BLE_BATCH_SCAN_DISABLE_EVT: + bta_evt = BTA_BLE_BATCH_SCAN_DIS_EVT; + break; + case BTM_BLE_BATCH_SCAN_PARAM_EVT: + bta_evt = BTA_BLE_BATCH_SCAN_PARAM_EVT; + break; + default: + break; + } + + if (NULL != bta_dm_cb.p_setup_cback) { + bta_dm_cb.p_setup_cback(bta_evt, ref_value, status); + } +} + + +#if BLE_ANDROID_CONTROLLER_SCAN_FILTER == TRUE +/******************************************************************************* +** +** Function bta_ble_scan_pf_cmpl +** +** Description ADV payload filtering operation complete callback +** +** +** Returns TRUE if handled, otherwise FALSE. +** +*******************************************************************************/ +static void bta_ble_scan_cfg_cmpl(tBTM_BLE_PF_ACTION action, tBTM_BLE_SCAN_COND_OP cfg_op, + tBTM_BLE_PF_AVBL_SPACE avbl_space, tBTM_STATUS status, + tBTM_BLE_REF_VALUE ref_value) +{ + tBTA_STATUS st = (status == BTM_SUCCESS) ? BTA_SUCCESS : BTA_FAILURE; + + APPL_TRACE_DEBUG("bta_ble_scan_cfg_cmpl: %d, %d, %d, %d", action, cfg_op, avbl_space, status); + + if (bta_dm_cb.p_scan_filt_cfg_cback) { + bta_dm_cb.p_scan_filt_cfg_cback(action, cfg_op, avbl_space, st, ref_value); + } +} + +/******************************************************************************* +** +** Function bta_dm_cfg_filter_cond +** +** Description This function configure adv payload filtering condition +** +** Parameters: +** +*******************************************************************************/ +void bta_dm_cfg_filter_cond (tBTA_DM_MSG *p_data) +{ + tBTM_STATUS st = BTM_MODE_UNSUPPORTED; + tBTA_STATUS status = BTA_FAILURE; + + tBTM_BLE_VSC_CB cmn_vsc_cb; + + APPL_TRACE_DEBUG("bta_dm_cfg_filter_cond"); + BTM_BleGetVendorCapabilities(&cmn_vsc_cb); + if (0 != cmn_vsc_cb.filter_support) { + if ((st = BTM_BleCfgFilterCondition(p_data->ble_cfg_filter_cond.action, + p_data->ble_cfg_filter_cond.cond_type, + (tBTM_BLE_PF_FILT_INDEX)p_data->ble_cfg_filter_cond.filt_index, + (tBTM_BLE_PF_COND_PARAM *)p_data->ble_cfg_filter_cond.p_cond_param, + bta_ble_scan_cfg_cmpl, p_data->ble_cfg_filter_cond.ref_value)) + == BTM_CMD_STARTED) { + bta_dm_cb.p_scan_filt_cfg_cback = p_data->ble_cfg_filter_cond.p_filt_cfg_cback; + return; + } + } + + if (p_data->ble_cfg_filter_cond.p_filt_cfg_cback) { + p_data->ble_cfg_filter_cond.p_filt_cfg_cback(BTA_DM_BLE_PF_CONFIG_EVT, + p_data->ble_cfg_filter_cond.cond_type, 0, status, + p_data->ble_cfg_filter_cond.ref_value); + } + return; +} + +/******************************************************************************* +** +** Function bta_dm_enable_scan_filter +** +** Description This function enable/disable adv payload filtering condition +** +** Parameters: +** +*******************************************************************************/ +void bta_dm_enable_scan_filter(tBTA_DM_MSG *p_data) +{ + tBTM_STATUS st = BTM_MODE_UNSUPPORTED; + tBTA_STATUS status = BTA_FAILURE; + + tBTM_BLE_VSC_CB cmn_vsc_cb; + APPL_TRACE_DEBUG("bta_dm_enable_scan_filter"); + BTM_BleGetVendorCapabilities(&cmn_vsc_cb); + + if (0 != cmn_vsc_cb.filter_support) { + if ((st = BTM_BleEnableDisableFilterFeature(p_data->ble_enable_scan_filt.action, + p_data->ble_enable_scan_filt.p_filt_status_cback, + (tBTM_BLE_REF_VALUE)p_data->ble_enable_scan_filt.ref_value)) == BTM_CMD_STARTED) { + bta_dm_cb.p_scan_filt_status_cback = p_data->ble_enable_scan_filt.p_filt_status_cback; + } + return; + } + + if (p_data->ble_enable_scan_filt.p_filt_status_cback) { + p_data->ble_enable_scan_filt.p_filt_status_cback (BTA_DM_BLE_PF_ENABLE_EVT, + p_data->ble_enable_scan_filt.ref_value, status); + } + +} + +/******************************************************************************* +** +** Function bta_dm_scan_filter_param_setup +** +** Description This function sets up scan filter params +** +** Parameters: +** +*******************************************************************************/ +void bta_dm_scan_filter_param_setup (tBTA_DM_MSG *p_data) +{ + tBTM_STATUS st = BTM_MODE_UNSUPPORTED; + tBTA_STATUS status = BTA_FAILURE; + + tBTM_BLE_VSC_CB cmn_vsc_cb; + + APPL_TRACE_DEBUG("bta_dm_scan_filter_param_setup"); + BTM_BleGetVendorCapabilities(&cmn_vsc_cb); + if (0 != cmn_vsc_cb.filter_support) { + if ((st = BTM_BleAdvFilterParamSetup(p_data->ble_scan_filt_param_setup.action, + p_data->ble_scan_filt_param_setup.filt_index, + (tBTM_BLE_PF_FILT_PARAMS *)&p_data->ble_scan_filt_param_setup.filt_params, + p_data->ble_scan_filt_param_setup.p_target, + p_data->ble_scan_filt_param_setup.p_filt_param_cback, + p_data->ble_scan_filt_param_setup.ref_value)) == BTM_CMD_STARTED) { + bta_dm_cb.p_scan_filt_param_cback = p_data->ble_scan_filt_param_setup.p_filt_param_cback; + return; + } + } + + if (p_data->ble_scan_filt_param_setup.p_filt_param_cback) { + p_data->ble_scan_filt_param_setup.p_filt_param_cback (BTA_DM_BLE_PF_ENABLE_EVT, 0, + p_data->ble_scan_filt_param_setup.ref_value, status); + } + + return; +} +#endif + +/******************************************************************************* +** +** Function bta_ble_enable_scan_cmpl +** +** Description ADV payload filtering enable / disable complete callback +** +** +** Returns None +** +*******************************************************************************/ +static void bta_ble_energy_info_cmpl(tBTM_BLE_TX_TIME_MS tx_time, + tBTM_BLE_RX_TIME_MS rx_time, + tBTM_BLE_IDLE_TIME_MS idle_time, + tBTM_BLE_ENERGY_USED energy_used, + tBTM_STATUS status) +{ + tBTA_STATUS st = (status == BTM_SUCCESS) ? BTA_SUCCESS : BTA_FAILURE; + tBTA_DM_CONTRL_STATE ctrl_state = 0; +#if ((defined BTA_GATT_INCLUDED) && (BTA_GATT_INCLUDED == TRUE) && SDP_INCLUDED == TRUE) + if (BTA_SUCCESS == st) { + ctrl_state = bta_dm_pm_obtain_controller_state(); + } +#endif + if (bta_dm_cb.p_energy_info_cback) { + bta_dm_cb.p_energy_info_cback(tx_time, rx_time, idle_time, energy_used, ctrl_state, st); + } +} + +/******************************************************************************* +** +** Function bta_dm_ble_get_energy_info +** +** Description This function obtains the energy info +** +** Parameters: +** +*******************************************************************************/ +void bta_dm_ble_get_energy_info(tBTA_DM_MSG *p_data) +{ + tBTM_STATUS btm_status = 0; + + bta_dm_cb.p_energy_info_cback = p_data->ble_energy_info.p_energy_info_cback; + btm_status = BTM_BleGetEnergyInfo(bta_ble_energy_info_cmpl); + if (BTM_CMD_STARTED != btm_status) { + bta_ble_energy_info_cmpl(0, 0, 0, 0, btm_status); + } +} + +#if ((defined BTA_GATT_INCLUDED) && (BTA_GATT_INCLUDED == TRUE) && SDP_INCLUDED == TRUE) +#ifndef BTA_DM_GATT_CLOSE_DELAY_TOUT +#define BTA_DM_GATT_CLOSE_DELAY_TOUT 1000 +#endif + +/******************************************************************************* +** +** Function bta_dm_gattc_register +** +** Description Register with GATTC in DM if BLE is needed. +** +** +** Returns void +** +*******************************************************************************/ +#if (GATTC_INCLUDED == TRUE) +static void bta_dm_gattc_register(void) +{ + tBT_UUID app_uuid = {LEN_UUID_128, {0}}; + + if (bta_dm_search_cb.client_if == BTA_GATTS_INVALID_IF) { + memset (&app_uuid.uu.uuid128, 0x87, LEN_UUID_128); + BTA_GATTC_AppRegister(&app_uuid, bta_dm_gattc_callback); + } +} +#endif /* GATTC_INCLUDED == TRUE */ + +/******************************************************************************* +** +** Function btm_dm_start_disc_gatt_services +** +** Description This function starts a GATT service search request. +** +** Parameters: +** +*******************************************************************************/ +#if (GATTC_INCLUDED == TRUE) +static void btm_dm_start_disc_gatt_services (UINT16 conn_id) +{ + tBT_UUID *p_uuid = bta_dm_search_cb.p_srvc_uuid + + bta_dm_search_cb.num_uuid - bta_dm_search_cb.uuid_to_search; + + p_uuid = bta_dm_search_cb.p_srvc_uuid + + bta_dm_search_cb.num_uuid - bta_dm_search_cb.uuid_to_search; + + /* always search for all services */ + BTA_GATTC_ServiceSearchRequest(conn_id, p_uuid); +} +#endif /* GATTC_INCLUDED == TRUE */ + +/******************************************************************************* +** +** Function bta_dm_gatt_disc_result +** +** Description This function process the GATT service search result. +** +** Parameters: +** +*******************************************************************************/ +#if (GATTC_INCLUDED == TRUE) +static void bta_dm_gatt_disc_result(tBTA_GATT_ID service_id) +{ + tBTA_DM_SEARCH result; + + /* + * This logic will not work for gatt case. We are checking against the bluetooth profiles here + * just copy the GATTID in raw data field and send it across. + */ + + + if ( bta_dm_search_cb.ble_raw_used + sizeof(tBTA_GATT_ID) < bta_dm_search_cb.ble_raw_size ) { + APPL_TRACE_DEBUG("ADDING BLE SERVICE uuid=0x%x, ble_ptr = %p, ble_raw_used = 0x%x", + service_id.uuid.uu.uuid16, bta_dm_search_cb.p_ble_rawdata, bta_dm_search_cb.ble_raw_used); + + if (bta_dm_search_cb.p_ble_rawdata) { + memcpy((bta_dm_search_cb.p_ble_rawdata + bta_dm_search_cb.ble_raw_used), &service_id, + sizeof(service_id) ); + + bta_dm_search_cb.ble_raw_used += sizeof(service_id); + } else { + APPL_TRACE_ERROR("p_ble_rawdata is NULL"); + } + + } else { + APPL_TRACE_ERROR("%s out of room to accomodate more service ids ble_raw_size = %d ble_raw_used = %d", __FUNCTION__, bta_dm_search_cb.ble_raw_size, bta_dm_search_cb.ble_raw_used ); + } + + APPL_TRACE_API("%s service_id_uuid_len=%d ", __func__, service_id.uuid.len); + if ( bta_dm_search_cb.state != BTA_DM_SEARCH_IDLE) { + + /* send result back to app now, one by one */ + bdcpy (result.disc_ble_res.bd_addr, bta_dm_search_cb.peer_bdaddr); + BCM_STRNCPY_S((char *)result.disc_ble_res.bd_name, bta_dm_get_remname(), BD_NAME_LEN); + result.disc_ble_res.bd_name[BD_NAME_LEN] = '\0'; + memcpy(&result.disc_ble_res.service, &service_id.uuid, sizeof(tBT_UUID)); + + bta_dm_search_cb.p_search_cback(BTA_DM_DISC_BLE_RES_EVT, &result); + } +} +#endif /* #if (GATTC_INCLUDED == TRUE) */ +/******************************************************************************* +** +** Function bta_dm_gatt_disc_complete +** +** Description This function process the GATT service search complete. +** +** Parameters: +** +*******************************************************************************/ +#if (GATTC_INCLUDED == TRUE) +static void bta_dm_gatt_disc_complete(UINT16 conn_id, tBTA_GATT_STATUS status) +{ + tBTA_DM_MSG *p_msg; + + APPL_TRACE_DEBUG("bta_dm_gatt_disc_complete conn_id = %d", conn_id); + + if (bta_dm_search_cb.uuid_to_search > 0) { + bta_dm_search_cb.uuid_to_search --; + } + + if (status == BTA_GATT_OK && bta_dm_search_cb.uuid_to_search > 0) { + btm_dm_start_disc_gatt_services(conn_id); + } else { + bta_dm_search_cb.uuid_to_search = 0; + + /* no more services to be discovered */ + if ((p_msg = (tBTA_DM_MSG *) osi_malloc(sizeof(tBTA_DM_MSG))) != NULL) { + p_msg->hdr.event = BTA_DM_DISCOVERY_RESULT_EVT; + p_msg->disc_result.result.disc_res.result = (status == BTA_GATT_OK) ? BTA_SUCCESS : BTA_FAILURE; + APPL_TRACE_DEBUG("%s service found: 0x%08x", __FUNCTION__, + bta_dm_search_cb.services_found); + p_msg->disc_result.result.disc_res.services = bta_dm_search_cb.services_found; + p_msg->disc_result.result.disc_res.num_uuids = 0; + p_msg->disc_result.result.disc_res.p_uuid_list = NULL; + bdcpy (p_msg->disc_result.result.disc_res.bd_addr, bta_dm_search_cb.peer_bdaddr); + BCM_STRNCPY_S((char *)p_msg->disc_result.result.disc_res.bd_name, bta_dm_get_remname(), BD_NAME_LEN); + p_msg->disc_result.result.disc_res.bd_name[BD_NAME_LEN] = '\0'; + + p_msg->disc_result.result.disc_res.device_type |= BT_DEVICE_TYPE_BLE; + if ( bta_dm_search_cb.ble_raw_used > 0 ) { + p_msg->disc_result.result.disc_res.p_raw_data = osi_malloc(bta_dm_search_cb.ble_raw_used); + + memcpy( p_msg->disc_result.result.disc_res.p_raw_data, + bta_dm_search_cb.p_ble_rawdata, + bta_dm_search_cb.ble_raw_used ); + + p_msg->disc_result.result.disc_res.raw_data_size = bta_dm_search_cb.ble_raw_used; + } else { + p_msg->disc_result.result.disc_res.p_raw_data = NULL; + bta_dm_search_cb.p_ble_rawdata = 0; + } + + bta_sys_sendmsg(p_msg); + } + + if (conn_id != BTA_GATT_INVALID_CONN_ID) { + /* start a GATT channel close delay timer */ + bta_sys_start_timer(&bta_dm_search_cb.gatt_close_timer, BTA_DM_DISC_CLOSE_TOUT_EVT, + BTA_DM_GATT_CLOSE_DELAY_TOUT); + bdcpy(bta_dm_search_cb.pending_close_bda, bta_dm_search_cb.peer_bdaddr); + } + bta_dm_search_cb.gatt_disc_active = FALSE; + } +} +#endif /* #if (GATTC_INCLUDED == TRUE) */ + +/******************************************************************************* +** +** Function bta_dm_close_gatt_conn +** +** Description This function close the GATT connection after delay timeout. +** +** Parameters: +** +*******************************************************************************/ +#if (GATTC_INCLUDED == TRUE) +void bta_dm_close_gatt_conn(tBTA_DM_MSG *p_data) +{ + UNUSED(p_data); + + if (bta_dm_search_cb.conn_id != BTA_GATT_INVALID_CONN_ID) { + BTA_GATTC_Close(bta_dm_search_cb.conn_id); + } + + memset(bta_dm_search_cb.pending_close_bda, 0, BD_ADDR_LEN); + bta_dm_search_cb.conn_id = BTA_GATT_INVALID_CONN_ID; +} +#endif /* #if (GATTC_INCLUDED == TRUE) */ +/******************************************************************************* +** +** Function btm_dm_start_gatt_discovery +** +** Description This is GATT initiate the service search by open a GATT connection +** first. +** +** Parameters: +** +*******************************************************************************/ +#if (GATTC_INCLUDED == TRUE) +void btm_dm_start_gatt_discovery (BD_ADDR bd_addr) +{ + bta_dm_search_cb.gatt_disc_active = TRUE; + + /* connection is already open */ + if (bdcmp(bta_dm_search_cb.pending_close_bda, bd_addr) == 0 && + bta_dm_search_cb.conn_id != BTA_GATT_INVALID_CONN_ID) { + memset(bta_dm_search_cb.pending_close_bda, 0, BD_ADDR_LEN); + bta_sys_stop_timer(&bta_dm_search_cb.gatt_close_timer); + btm_dm_start_disc_gatt_services(bta_dm_search_cb.conn_id); + } else { + //TODO need to add addr_type in future + BTA_GATTC_Open(bta_dm_search_cb.client_if, bd_addr, BLE_ADDR_UNKNOWN_TYPE, TRUE, BTA_GATT_TRANSPORT_LE, FALSE); + } +} +#endif /* #if (GATTC_INCLUDED == TRUE) */ +/******************************************************************************* +** +** Function bta_dm_cancel_gatt_discovery +** +** Description This is GATT cancel the GATT service search. +** +** Parameters: +** +*******************************************************************************/ +#if (GATTC_INCLUDED == TRUE) +static void bta_dm_cancel_gatt_discovery(BD_ADDR bd_addr) +{ + if (bta_dm_search_cb.conn_id == BTA_GATT_INVALID_CONN_ID) { + BTA_GATTC_CancelOpen(bta_dm_search_cb.client_if, bd_addr, TRUE); + } + + bta_dm_gatt_disc_complete(bta_dm_search_cb.conn_id, (tBTA_GATT_STATUS) BTA_GATT_ERROR); +} +#endif /* #if (GATTC_INCLUDED == TRUE) */ + +/******************************************************************************* +** +** Function bta_dm_proc_open_evt +** +** Description process BTA_GATTC_OPEN_EVT in DM. +** +** Parameters: +** +*******************************************************************************/ +#if (GATTC_INCLUDED == TRUE) +void bta_dm_proc_open_evt(tBTA_GATTC_OPEN *p_data) +{ + UINT8 *p1; + UINT8 *p2; + + p1 = bta_dm_search_cb.peer_bdaddr; + p2 = p_data->remote_bda; + + APPL_TRACE_DEBUG("DM Search state= %d search_cb.peer_dbaddr: [%08x%04x] connected_bda= [%08x%04x] ", + bta_dm_search_cb.state, + ((p1[0]) << 24) + ((p1[1]) << 16) + ((p1[2]) << 8) + (p1[3]), + ((p1[4]) << 8) + p1[5], + ((p2[0]) << 24) + ((p2[1]) << 16) + ((p2[2]) << 8) + (p2[3]), + ((p2[4]) << 8) + p2[5]); + + UNUSED(p1); + UNUSED(p2); + APPL_TRACE_DEBUG("BTA_GATTC_OPEN_EVT conn_id = %d client_if=%d status = %d" , + p_data->conn_id, + p_data->client_if, + p_data->status); + + bta_dm_search_cb.conn_id = p_data->conn_id; + + if (p_data->status == BTA_GATT_OK) { + btm_dm_start_disc_gatt_services(p_data->conn_id); + } else { + bta_dm_gatt_disc_complete(BTA_GATT_INVALID_CONN_ID, p_data->status); + } +} +#endif /* #if (GATTC_INCLUDED == TRUE) */ + +/******************************************************************************* +** +** Function bta_dm_gattc_callback +** +** Description This is GATT client callback function used in DM. +** +** Parameters: +** +*******************************************************************************/ +#if (GATTC_INCLUDED == TRUE) +static void bta_dm_gattc_callback(tBTA_GATTC_EVT event, tBTA_GATTC *p_data) +{ + APPL_TRACE_DEBUG("bta_dm_gattc_callback event = %d", event); + + switch (event) { + case BTA_GATTC_REG_EVT: + APPL_TRACE_DEBUG("BTA_GATTC_REG_EVT client_if = %d", p_data->reg_oper.client_if); + if (p_data->reg_oper.status == BTA_GATT_OK) { + bta_dm_search_cb.client_if = p_data->reg_oper.client_if; + } else { + bta_dm_search_cb.client_if = BTA_GATTS_INVALID_IF; + } + break; + + case BTA_GATTC_OPEN_EVT: + bta_dm_proc_open_evt(&p_data->open); + break; + + case BTA_GATTC_SEARCH_RES_EVT: + bta_dm_gatt_disc_result(p_data->srvc_res.service_uuid); + break; + + case BTA_GATTC_SEARCH_CMPL_EVT: + if ( bta_dm_search_cb.state != BTA_DM_SEARCH_IDLE) { + bta_dm_gatt_disc_complete(p_data->search_cmpl.conn_id, p_data->search_cmpl.status); + } + break; + + case BTA_GATTC_CLOSE_EVT: + APPL_TRACE_DEBUG("BTA_GATTC_CLOSE_EVT reason = %d", p_data->close.reason); + /* in case of disconnect before search is completed */ + if ( (bta_dm_search_cb.state != BTA_DM_SEARCH_IDLE) && + (bta_dm_search_cb.state != BTA_DM_SEARCH_ACTIVE) && + !memcmp(p_data->close.remote_bda, bta_dm_search_cb.peer_bdaddr, BD_ADDR_LEN)) { + bta_dm_gatt_disc_complete((UINT16)BTA_GATT_INVALID_CONN_ID, (tBTA_GATT_STATUS) BTA_GATT_ERROR); + } + break; + + default: + break; + } +} +#endif /* #if (GATTC_INCLUDED == TRUE) */ +#endif /* BTA_GATT_INCLUDED */ + +#if BLE_VND_INCLUDED == TRUE +/******************************************************************************* +** +** Function bta_dm_ctrl_features_rd_cmpl_cback +** +** Description callback to handle controller feature read complete +** +** Parameters: +** +*******************************************************************************/ +static void bta_dm_ctrl_features_rd_cmpl_cback(tBTM_STATUS result) +{ + APPL_TRACE_DEBUG("%s status = %d ", __FUNCTION__, result); + if (result == BTM_SUCCESS) { + if (bta_dm_cb.p_sec_cback) { + bta_dm_cb.p_sec_cback(BTA_DM_LE_FEATURES_READ, NULL); + } + } else { + APPL_TRACE_ERROR("%s Ctrl BLE feature read failed: status :%d", __FUNCTION__, result); + } + +} +#endif /* BLE_VND_INCLUDED */ + +#endif /* BLE_INCLUDED */ diff --git a/lib/bt/host/bluedroid/bta/dm/bta_dm_api.c b/lib/bt/host/bluedroid/bta/dm/bta_dm_api.c new file mode 100644 index 00000000..ecb6b6ee --- /dev/null +++ b/lib/bt/host/bluedroid/bta/dm/bta_dm_api.c @@ -0,0 +1,3424 @@ +/****************************************************************************** + * + * Copyright (C) 2003-2014 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This is the API implementation file for the BTA device manager. + * + ******************************************************************************/ + +#include "bta/bta_sys.h" +#include "bta/bta_api.h" +#include "bta_dm_int.h" +#include "bta_sys_int.h" +#include "stack/btm_api.h" +#include "btm_int.h" +#include +#include "bta/utl.h" +#include "osi/allocator.h" + +/***************************************************************************** +** Constants +*****************************************************************************/ + +static const tBTA_SYS_REG bta_dm_reg = { + bta_dm_sm_execute, + bta_dm_sm_disable +}; + +static const tBTA_SYS_REG bta_dm_search_reg = { + bta_dm_search_sm_execute, + bta_dm_search_sm_disable +}; + +/******************************************************************************* +** +** Function BTA_EnableBluetooth +** +** Description Enables bluetooth service. This function must be +** called before any other functions in the BTA API are called. +** +** +** Returns tBTA_STATUS +** +*******************************************************************************/ +tBTA_STATUS BTA_EnableBluetooth(tBTA_DM_SEC_CBACK *p_cback) +{ + + tBTA_DM_API_ENABLE *p_msg; + + /* Bluetooth disabling is in progress */ + if (bta_dm_cb.disabling) { + return BTA_FAILURE; + } + + memset(&bta_dm_cb, 0, sizeof(bta_dm_cb)); + + bta_sys_register (BTA_ID_DM, &bta_dm_reg ); + bta_sys_register (BTA_ID_DM_SEARCH, &bta_dm_search_reg ); + + /* if UUID list is not provided as static data */ + bta_sys_eir_register(bta_dm_eir_update_uuid); + + if ((p_msg = (tBTA_DM_API_ENABLE *) osi_malloc(sizeof(tBTA_DM_API_ENABLE))) != NULL) { + p_msg->hdr.event = BTA_DM_API_ENABLE_EVT; + p_msg->p_sec_cback = p_cback; + bta_sys_sendmsg(p_msg); + return BTA_SUCCESS; + } + return BTA_FAILURE; + +} + +/******************************************************************************* +** +** Function BTA_DisableBluetooth +** +** Description Disables bluetooth service. This function is called when +** the application no longer needs bluetooth service +** +** Returns void +** +*******************************************************************************/ +tBTA_STATUS BTA_DisableBluetooth(void) +{ + + BT_HDR *p_msg; + + if ((p_msg = (BT_HDR *) osi_malloc(sizeof(BT_HDR))) != NULL) { + p_msg->event = BTA_DM_API_DISABLE_EVT; + bta_sys_sendmsg(p_msg); + } else { + return BTA_FAILURE; + } + + return BTA_SUCCESS; +} + +/******************************************************************************* +** +** Function BTA_EnableTestMode +** +** Description Enables bluetooth device under test mode +** +** +** Returns tBTA_STATUS +** +*******************************************************************************/ +tBTA_STATUS BTA_EnableTestMode(void) +{ + BT_HDR *p_msg; + + APPL_TRACE_API("BTA_EnableTestMode"); + + if ((p_msg = (BT_HDR *) osi_malloc(sizeof(BT_HDR))) != NULL) { + p_msg->event = BTA_DM_API_ENABLE_TEST_MODE_EVT; + bta_sys_sendmsg(p_msg); + return BTA_SUCCESS; + } + return BTA_FAILURE; +} + +/******************************************************************************* +** +** Function BTA_DisableTestMode +** +** Description Disable bluetooth device under test mode +** +** +** Returns None +** +*******************************************************************************/ +void BTA_DisableTestMode(void) +{ + BT_HDR *p_msg; + + APPL_TRACE_API("BTA_DisableTestMode"); + + if ((p_msg = (BT_HDR *) osi_malloc(sizeof(BT_HDR))) != NULL) { + p_msg->event = BTA_DM_API_DISABLE_TEST_MODE_EVT; + bta_sys_sendmsg(p_msg); + } +} + +/******************************************************************************* +** +** Function BTA_DmSetDeviceName +** +** Description This function sets the Bluetooth name of local device +** +** +** Returns void +** +*******************************************************************************/ +void BTA_DmSetDeviceName(const char *p_name) +{ + + tBTA_DM_API_SET_NAME *p_msg; + + if ((p_msg = (tBTA_DM_API_SET_NAME *) osi_malloc(sizeof(tBTA_DM_API_SET_NAME))) != NULL) { + p_msg->hdr.event = BTA_DM_API_SET_NAME_EVT; + /* truncate the name if needed */ + BCM_STRNCPY_S((char *)p_msg->name, p_name, BD_NAME_LEN); + p_msg->name[BD_NAME_LEN] = '\0'; + + bta_sys_sendmsg(p_msg); + } +} + +/******************************************************************************* +** +** Function BTA_DmGetDeviceName +** +** Description This function gets the Bluetooth name of local device +** +** +** Returns void +** +*******************************************************************************/ +void BTA_DmGetDeviceName(tBTA_GET_DEV_NAME_CBACK *p_cback) +{ + tBTA_DM_API_GET_NAME *p_msg; + + if ((p_msg = (tBTA_DM_API_GET_NAME *) osi_malloc(sizeof(tBTA_DM_API_GET_NAME))) != NULL) { + p_msg->hdr.event = BTA_DM_API_GET_NAME_EVT; + p_msg->p_cback = p_cback; + bta_sys_sendmsg(p_msg); + } +} + +/******************************************************************************* +** +** Function BTA_DmCfgCoexStatus +** +** Description This function configures the coexist status +** +** +** Returns void +** +*******************************************************************************/ +#if (ESP_COEX_VSC_INCLUDED == TRUE) +void BTA_DmCfgCoexStatus(UINT8 op, UINT8 type, UINT8 status) +{ + tBTA_DM_API_CFG_COEX_STATUS *p_msg; + + if ((p_msg = (tBTA_DM_API_CFG_COEX_STATUS *) osi_malloc(sizeof(tBTA_DM_API_CFG_COEX_STATUS))) != NULL) { + p_msg->hdr.event = BTA_DM_API_CFG_COEX_ST_EVT; + p_msg->op = op; + p_msg->type = type; + p_msg->status = status; + bta_sys_sendmsg(p_msg); + } +} +#endif + +#if (CLASSIC_BT_INCLUDED == TRUE) + +void BTA_DmConfigEir(tBTA_DM_EIR_CONF *eir_config) +{ + tBTA_DM_API_CONFIG_EIR *p_msg; + + UINT8 eir_manufac_spec_len = eir_config->bta_dm_eir_manufac_spec_len; + UINT8 eir_url_len = eir_config->bta_dm_eir_url_len; + + if (eir_manufac_spec_len > HCI_EXT_INQ_RESPONSE_LEN) { + APPL_TRACE_WARNING ("%s: Manufacturer data is too long(%d), cut it to %d\n", + __func__, eir_manufac_spec_len, HCI_EXT_INQ_RESPONSE_LEN); + eir_manufac_spec_len = HCI_EXT_INQ_RESPONSE_LEN; + } + if (eir_url_len > HCI_EXT_INQ_RESPONSE_LEN) { + APPL_TRACE_WARNING ("%s: URL is too long(%d), cut it to %d\n", + __func__, eir_url_len, HCI_EXT_INQ_RESPONSE_LEN); + eir_url_len = HCI_EXT_INQ_RESPONSE_LEN; + } + + if ((p_msg = (tBTA_DM_API_CONFIG_EIR *) osi_malloc(sizeof(tBTA_DM_API_CONFIG_EIR) + eir_manufac_spec_len + eir_url_len)) != NULL) { + p_msg->hdr.event = BTA_DM_API_CONFIG_EIR_EVT; + + p_msg->eir_fec_required = eir_config->bta_dm_eir_fec_required; + p_msg->eir_included_name = eir_config->bta_dm_eir_included_name; + p_msg->eir_included_tx_power = eir_config->bta_dm_eir_included_tx_power; + p_msg->eir_included_uuid = eir_config->bta_dm_eir_included_uuid; + p_msg->eir_flags = eir_config->bta_dm_eir_flags; + p_msg->eir_manufac_spec_len = eir_manufac_spec_len; + p_msg->eir_manufac_spec = p_msg->data; + p_msg->eir_url_len = eir_url_len; + p_msg->eir_url = p_msg->data + eir_manufac_spec_len; + + if (eir_manufac_spec_len > 0) { + memcpy(p_msg->eir_manufac_spec, eir_config->bta_dm_eir_manufac_spec, eir_manufac_spec_len); + } + + if (eir_url_len > 0) { + memcpy(p_msg->eir_url, eir_config->bta_dm_eir_url, eir_url_len); + } + + bta_sys_sendmsg(p_msg); + } +} + + +/******************************************************************************* +** +** Function BTA_DmSetAfhChannels +** +** Description This function sets the AFH channels +** +** +** Returns void +** +*******************************************************************************/ +void BTA_DmSetAfhChannels(const uint8_t *channels, tBTA_CMPL_CB *set_afh_cb) +{ + tBTA_DM_API_SET_AFH_CHANNELS *p_msg; + + if ((p_msg = (tBTA_DM_API_SET_AFH_CHANNELS *) osi_malloc(sizeof(tBTA_DM_API_SET_AFH_CHANNELS))) != NULL) { + p_msg->hdr.event = BTA_DM_API_SET_AFH_CHANNELS_EVT; + + p_msg->set_afh_cb = set_afh_cb; + memcpy(p_msg->channels, channels, AFH_CHANNELS_LEN); + + bta_sys_sendmsg(p_msg); + } +} + +/******************************************************************************* +** +** Function BTA_DmSetPageTimeout +** +** Description This function sets the Bluetooth page timeout. +** +** +** Returns void +** +*******************************************************************************/ +void BTA_DmSetPageTimeout(UINT16 page_to, tBTM_CMPL_CB *p_cb) +{ + tBTA_DM_API_PAGE_TO_SET *p_msg; + + if ((p_msg = (tBTA_DM_API_PAGE_TO_SET *) osi_malloc(sizeof(tBTA_DM_API_PAGE_TO_SET))) != NULL) { + p_msg->hdr.event = BTA_DM_API_PAGE_TO_SET_EVT; + p_msg->page_to = page_to; + p_msg->set_page_to_cb = p_cb; + + bta_sys_sendmsg(p_msg); + } +} + +/******************************************************************************* +** +** Function BTA_DmGetPageTimeout +** +** Description This function gets the Bluetooth page timeout. +** +** +** Returns void +** +*******************************************************************************/ +void BTA_DmGetPageTimeout(tBTM_CMPL_CB *p_cb) +{ + tBTA_DM_API_PAGE_TO_GET *p_msg; + + if ((p_msg = (tBTA_DM_API_PAGE_TO_GET *) osi_malloc(sizeof(tBTA_DM_API_PAGE_TO_GET))) != NULL) { + p_msg->hdr.event = BTA_DM_API_PAGE_TO_GET_EVT; + p_msg->get_page_to_cb = p_cb; + + bta_sys_sendmsg(p_msg); + } +} + +/******************************************************************************* +** +** Function BTA_DmSetAclPktTypes +** +** Description This function sets the packet types used for ACL traffic. +** +** +** Returns void +** +*******************************************************************************/ +void BTA_DmSetAclPktTypes(BD_ADDR remote_addr, UINT16 pkt_types, tBTM_CMPL_CB *p_cb) +{ + tBTA_DM_API_SET_ACL_PKT_TYPES *p_msg; + + if ((p_msg = (tBTA_DM_API_SET_ACL_PKT_TYPES *) osi_malloc(sizeof(tBTA_DM_API_SET_ACL_PKT_TYPES))) != NULL) { + p_msg->hdr.event = BTA_DM_API_SET_ACL_PKT_TYPES_EVT; + bdcpy(p_msg->rmt_addr, remote_addr); + p_msg->pkt_types = pkt_types; + p_msg->set_acl_pkt_types_cb = p_cb; + + bta_sys_sendmsg(p_msg); + } +} +#endif /// CLASSIC_BT_INCLUDED == TRUE + +#if (SDP_INCLUDED == TRUE) +/******************************************************************************* +** +** Function BTA_DmGetRemoteName +** +** Description This function gets the peer device's Bluetooth name. +** +** +** Returns void +** +*******************************************************************************/ +void BTA_DmGetRemoteName(BD_ADDR remote_addr, tBTA_CMPL_CB *rmt_name_cb) +{ + tBTA_DM_API_GET_REMOTE_NAME *p_msg; + + if ((p_msg = (tBTA_DM_API_GET_REMOTE_NAME *) osi_malloc(sizeof(tBTA_DM_API_GET_REMOTE_NAME))) != NULL) { + p_msg->hdr.event = BTA_DM_API_GET_REMOTE_NAME_EVT; + p_msg->rmt_name_cb = rmt_name_cb; + bdcpy(p_msg->rmt_addr, remote_addr); + bta_sys_sendmsg(p_msg); + } +} +#endif + +#if (BLE_INCLUDED == TRUE) +/******************************************************************************* +** +** Function BTA_DmBleSetChannels +** +** Description This function sets BLE channels +** +** +** Returns void +** +*******************************************************************************/ +void BTA_DmBleSetChannels(const uint8_t *channels, tBTA_CMPL_CB *set_channels_cb) +{ + + tBTA_DM_API_BLE_SET_CHANNELS *p_msg; + + if ((p_msg = (tBTA_DM_API_BLE_SET_CHANNELS *) osi_malloc(sizeof(tBTA_DM_API_BLE_SET_CHANNELS))) != NULL) { + p_msg->hdr.event = BTA_DM_API_BLE_SET_CHANNELS_EVT; + + p_msg->set_channels_cb = set_channels_cb; + memcpy(p_msg->channels, channels, BLE_CHANNELS_LEN); + + bta_sys_sendmsg(p_msg); + } + + +} + +void BTA_DmUpdateWhiteList(BOOLEAN add_remove, BD_ADDR remote_addr, tBLE_ADDR_TYPE addr_type, tBTA_UPDATE_WHITELIST_CBACK *update_wl_cb) +{ + tBTA_DM_API_UPDATE_WHITE_LIST *p_msg; + if ((p_msg = (tBTA_DM_API_UPDATE_WHITE_LIST *)osi_malloc(sizeof(tBTA_DM_API_UPDATE_WHITE_LIST))) != NULL) { + p_msg->hdr.event = BTA_DM_API_UPDATE_WHITE_LIST_EVT; + p_msg->add_remove = add_remove; + p_msg->addr_type = addr_type; + p_msg->update_wl_cb = update_wl_cb; + memcpy(p_msg->remote_addr, remote_addr, sizeof(BD_ADDR)); + + bta_sys_sendmsg(p_msg); + } +} + +void BTA_DmClearWhiteList(tBTA_UPDATE_WHITELIST_CBACK *update_wl_cb) +{ + tBTA_DM_API_UPDATE_WHITE_LIST *p_msg; + if ((p_msg = (tBTA_DM_API_UPDATE_WHITE_LIST *)osi_malloc(sizeof(tBTA_DM_API_UPDATE_WHITE_LIST))) != NULL) { + p_msg->hdr.event = BTA_DM_API_CLEAR_WHITE_LIST_EVT; + p_msg->update_wl_cb = update_wl_cb; + + bta_sys_sendmsg(p_msg); + } +} + +void BTA_DmBleReadAdvTxPower(tBTA_CMPL_CB *cmpl_cb) +{ + tBTA_DM_API_READ_ADV_TX_POWER *p_msg; + if ((p_msg = (tBTA_DM_API_READ_ADV_TX_POWER *)osi_malloc(sizeof(tBTA_DM_API_READ_ADV_TX_POWER))) != NULL) { + p_msg->hdr.event = BTA_DM_API_BLE_READ_ADV_TX_POWER_EVT; + p_msg->read_tx_power_cb = cmpl_cb; + bta_sys_sendmsg(p_msg); + } +} +#endif ///BLE_INCLUDED == TRUE + +void BTA_DmReadRSSI(BD_ADDR remote_addr, tBTA_TRANSPORT transport, tBTA_CMPL_CB *cmpl_cb) +{ + tBTA_DM_API_READ_RSSI *p_msg; + if ((p_msg = (tBTA_DM_API_READ_RSSI *)osi_malloc(sizeof(tBTA_DM_API_READ_RSSI))) != NULL) { + p_msg->hdr.event = BTA_DM_API_READ_RSSI_EVT; + memcpy(p_msg->remote_addr, remote_addr, sizeof(BD_ADDR)); + p_msg->transport = transport; + p_msg->read_rssi_cb = cmpl_cb; + bta_sys_sendmsg(p_msg); + } +} + +/******************************************************************************* +** +** Function BTA_DmSetVisibility +** +** Description This function sets the Bluetooth connectable, +** discoverable, pairable and conn paired only modes of local device +** +** +** Returns void +** +*******************************************************************************/ +void BTA_DmSetVisibility(tBTA_DM_DISC disc_mode, tBTA_DM_CONN conn_mode, UINT8 pairable_mode, UINT8 conn_filter ) +{ + + tBTA_DM_API_SET_VISIBILITY *p_msg; + + if ((p_msg = (tBTA_DM_API_SET_VISIBILITY *) osi_malloc(sizeof(tBTA_DM_API_SET_VISIBILITY))) != NULL) { + p_msg->hdr.event = BTA_DM_API_SET_VISIBILITY_EVT; + p_msg->disc_mode = disc_mode; + p_msg->conn_mode = conn_mode; + p_msg->pair_mode = pairable_mode; + p_msg->conn_paired_only = conn_filter; + + + bta_sys_sendmsg(p_msg); + } + + +} + +/******************************************************************************* +** +** Function BTA_DmSearch +** +** Description This function searches for peer Bluetooth devices. It performs +** an inquiry and gets the remote name for devices. Service +** discovery is done if services is non zero +** +** +** Returns void +** +*******************************************************************************/ +void BTA_DmSearch(tBTA_DM_INQ *p_dm_inq, tBTA_SERVICE_MASK services, tBTA_DM_SEARCH_CBACK *p_cback) +{ + + tBTA_DM_API_SEARCH *p_msg; + + if ((p_msg = (tBTA_DM_API_SEARCH *) osi_malloc(sizeof(tBTA_DM_API_SEARCH))) != NULL) { + memset(p_msg, 0, sizeof(tBTA_DM_API_SEARCH)); + + p_msg->hdr.event = BTA_DM_API_SEARCH_EVT; + memcpy(&p_msg->inq_params, p_dm_inq, sizeof(tBTA_DM_INQ)); + p_msg->services = services; + p_msg->p_cback = p_cback; + p_msg->rs_res = BTA_DM_RS_NONE; + bta_sys_sendmsg(p_msg); + } + +} + + +/******************************************************************************* +** +** Function BTA_DmSearchCancel +** +** Description This function cancels a search initiated by BTA_DmSearch +** +** +** Returns void +** +*******************************************************************************/ +void BTA_DmSearchCancel(void) +{ + BT_HDR *p_msg; + + if ((p_msg = (BT_HDR *) osi_malloc(sizeof(BT_HDR))) != NULL) { + p_msg->event = BTA_DM_API_SEARCH_CANCEL_EVT; + bta_sys_sendmsg(p_msg); + } + +} + +#if (SDP_INCLUDED == TRUE) +/******************************************************************************* +** +** Function BTA_DmDiscover +** +** Description This function does service discovery for services of a +** peer device +** +** +** Returns void +** +*******************************************************************************/ +void BTA_DmDiscover(BD_ADDR bd_addr, tBTA_SERVICE_MASK services, + tBTA_DM_SEARCH_CBACK *p_cback, BOOLEAN sdp_search) +{ + tBTA_DM_API_DISCOVER *p_msg; + + if ((p_msg = (tBTA_DM_API_DISCOVER *) osi_malloc(sizeof(tBTA_DM_API_DISCOVER))) != NULL) { + memset(p_msg, 0, sizeof(tBTA_DM_API_DISCOVER)); + + p_msg->hdr.event = BTA_DM_API_DISCOVER_EVT; + bdcpy(p_msg->bd_addr, bd_addr); + p_msg->services = services; + p_msg->p_cback = p_cback; + p_msg->sdp_search = sdp_search; + bta_sys_sendmsg(p_msg); + } +} + +/******************************************************************************* +** +** Function BTA_DmDiscoverUUID +** +** Description This function does service discovery for services of a +** peer device +** +** +** Returns void +** +*******************************************************************************/ +void BTA_DmDiscoverUUID(BD_ADDR bd_addr, tSDP_UUID *uuid, + tBTA_DM_SEARCH_CBACK *p_cback, BOOLEAN sdp_search) +{ + tBTA_DM_API_DISCOVER *p_msg; + + if ((p_msg = (tBTA_DM_API_DISCOVER *) osi_malloc(sizeof(tBTA_DM_API_DISCOVER))) != NULL) { + p_msg->hdr.event = BTA_DM_API_DISCOVER_EVT; + bdcpy(p_msg->bd_addr, bd_addr); + p_msg->services = BTA_USER_SERVICE_MASK; //Not exposed at API level + p_msg->p_cback = p_cback; + p_msg->sdp_search = sdp_search; + +#if BLE_INCLUDED == TRUE && BTA_GATT_INCLUDED == TRUE + p_msg->num_uuid = 0; + p_msg->p_uuid = NULL; +#endif + memcpy( &p_msg->uuid, uuid, sizeof(tSDP_UUID) ); + bta_sys_sendmsg(p_msg); + } +} +#endif ///SDP_INCLUDED == TRUE + +/******************************************************************************* +** +** Function BTA_DmBond +** +** Description This function initiates a bonding procedure with a peer +** device +** +** +** Returns void +** +*******************************************************************************/ +#if (SMP_INCLUDED == TRUE) +void BTA_DmBond(BD_ADDR bd_addr) +{ + tBTA_DM_API_BOND *p_msg; + + p_msg = (tBTA_DM_API_BOND *) osi_malloc(sizeof(tBTA_DM_API_BOND)); + if (p_msg != NULL) { + p_msg->hdr.event = BTA_DM_API_BOND_EVT; + bdcpy(p_msg->bd_addr, bd_addr); + p_msg->transport = BTA_TRANSPORT_UNKNOWN; + bta_sys_sendmsg(p_msg); + } +} + +/******************************************************************************* +** +** Function BTA_DmBondByTransports +** +** Description This function initiates a bonding procedure with a peer +** device +** +** +** Returns void +** +*******************************************************************************/ +void BTA_DmBondByTransport(BD_ADDR bd_addr, tBTA_TRANSPORT transport) +{ + tBTA_DM_API_BOND *p_msg; + + if ((p_msg = (tBTA_DM_API_BOND *) osi_malloc(sizeof(tBTA_DM_API_BOND))) != NULL) { + p_msg->hdr.event = BTA_DM_API_BOND_EVT; + bdcpy(p_msg->bd_addr, bd_addr); + p_msg->transport = transport; + bta_sys_sendmsg(p_msg); + } + + +} + +/******************************************************************************* +** +** Function BTA_DmBondCancel +** +** Description This function cancels the bonding procedure with a peer +** device +** +** +** Returns void +** +*******************************************************************************/ +void BTA_DmBondCancel(BD_ADDR bd_addr) +{ + tBTA_DM_API_BOND_CANCEL *p_msg; + + if ((p_msg = (tBTA_DM_API_BOND_CANCEL *) osi_malloc(sizeof(tBTA_DM_API_BOND_CANCEL))) != NULL) { + p_msg->hdr.event = BTA_DM_API_BOND_CANCEL_EVT; + bdcpy(p_msg->bd_addr, bd_addr); + bta_sys_sendmsg(p_msg); + } +} +#endif ///SMP_INCLUDED == TRUE + +#if (CLASSIC_BT_INCLUDED == TRUE) +/******************************************************************************* +** +** Function BTA_DMSetPinType +** +** Description This function set pin type as BTM_PIN_TYPE_FIXED or BTM_PIN_TYPE_VARIABLE +** +** +** Returns void +** +*******************************************************************************/ +void BTA_DMSetPinType (UINT8 pin_type, UINT8 *pin_code, UINT8 pin_code_len) +{ + tBTA_DM_API_SET_PIN_TYPE *p_msg; + + if ((p_msg = (tBTA_DM_API_SET_PIN_TYPE *) osi_malloc(sizeof(tBTA_DM_API_SET_PIN_TYPE))) != NULL) { + p_msg->hdr.event = BTA_DM_API_SET_PIN_TYPE_EVT; + p_msg->pin_type = pin_type; + p_msg->pin_len = pin_code_len; + memcpy(p_msg->p_pin, pin_code, pin_code_len); + bta_sys_sendmsg(p_msg); + } +} + +/******************************************************************************* +** +** Function BTA_DmPinReply +** +** Description This function provides a pincode for a remote device when +** one is requested by DM through BTA_DM_PIN_REQ_EVT +** +** +** Returns void +** +*******************************************************************************/ +void BTA_DmPinReply(BD_ADDR bd_addr, BOOLEAN accept, UINT8 pin_len, UINT8 *p_pin) +{ + tBTA_DM_API_PIN_REPLY *p_msg; + + if ((p_msg = (tBTA_DM_API_PIN_REPLY *) osi_malloc(sizeof(tBTA_DM_API_PIN_REPLY))) != NULL) { + p_msg->hdr.event = BTA_DM_API_PIN_REPLY_EVT; + bdcpy(p_msg->bd_addr, bd_addr); + p_msg->accept = accept; + if (accept) { + p_msg->pin_len = pin_len; + memcpy(p_msg->p_pin, p_pin, pin_len); + } + bta_sys_sendmsg(p_msg); + } + +} +#endif ///CLASSIC_BT_INCLUDED == TRUE + +#if (BTM_OOB_INCLUDED == TRUE && SMP_INCLUDED == TRUE) +/******************************************************************************* +** +** Function BTA_DmLocalOob +** +** Description This function retrieves the OOB data from local controller. +** The result is reported by: +** - bta_dm_co_loc_oob_ext() if device supports secure +** connections (SC) +** - bta_dm_co_loc_oob() if device doesn't support SC +** +** Returns void +** +*******************************************************************************/ +void BTA_DmLocalOob(void) +{ + tBTA_DM_API_LOC_OOB *p_msg; + + if ((p_msg = (tBTA_DM_API_LOC_OOB *) osi_malloc(sizeof(tBTA_DM_API_LOC_OOB))) != NULL) { + p_msg->hdr.event = BTA_DM_API_LOC_OOB_EVT; + bta_sys_sendmsg(p_msg); + } +} + +/******************************************************************************* +** +** Function BTA_DmOobReply +** +** This function is called to provide the OOB data for +** SMP in response to BTA_LE_OOB_REQ_EVT +** +** Parameters: bd_addr - Address of the peer device +** len - length of simple pairing Randomizer C +** p_value - simple pairing Randomizer C. +** +** Returns void +** +*******************************************************************************/ +void BTA_DmOobReply(BD_ADDR bd_addr, UINT8 len, UINT8 *p_value) +{ + tBTA_DM_API_OOB_REPLY *p_msg; + + if ((p_msg = (tBTA_DM_API_OOB_REPLY *) osi_malloc(sizeof(tBTA_DM_API_OOB_REPLY))) != NULL) { + p_msg->hdr.event = BTA_DM_API_OOB_REPLY_EVT; + if(p_value == NULL || len > BT_OCTET16_LEN) { + osi_free(p_msg); + return; + } + memcpy(p_msg->bd_addr, bd_addr, BD_ADDR_LEN); + p_msg->len = len; + memcpy(p_msg->value, p_value, len); + bta_sys_sendmsg(p_msg); + } +} + +/******************************************************************************* +** +** Function BTA_DmSecureConnectionOobReply +** +** This function is called to provide the OOB data for +** SMP in response to BTA_LE_OOB_REQ_EVT +** +** Parameters: bd_addr - Address of the peer device +** p_c - Pointer to Confirmation +** p_r - Pointer to Randomizer +** +** Returns void +** +*******************************************************************************/ +void BTA_DmSecureConnectionOobReply(BD_ADDR bd_addr, UINT8 *p_c, UINT8 *p_r) +{ + tBTA_DM_API_SC_OOB_REPLY *p_msg; + + if ((p_msg = (tBTA_DM_API_SC_OOB_REPLY *) osi_malloc(sizeof(tBTA_DM_API_OOB_REPLY))) != NULL) { + p_msg->hdr.event = BTA_DM_API_SC_OOB_REPLY_EVT; + if((p_c == NULL) || (p_r == NULL)) { + return; + } + memcpy(p_msg->bd_addr, bd_addr, BD_ADDR_LEN); + memcpy(p_msg->c, p_c, BT_OCTET16_LEN); + memcpy(p_msg->r, p_r, BT_OCTET16_LEN); + bta_sys_sendmsg(p_msg); + } +} +/******************************************************************************* +** +** Function BTA_DmSecureConnectionCreateOobData +** +** This function is called to create the OOB data for +** SMP when secure connection +** +** Returns void +** +*******************************************************************************/ +void BTA_DmSecureConnectionCreateOobData(void) +{ + tBTA_DM_API_SC_CR_OOB_DATA *p_msg; + + if ((p_msg = (tBTA_DM_API_SC_CR_OOB_DATA *) osi_malloc(sizeof(tBTA_DM_API_SC_CR_OOB_DATA))) != NULL) { + p_msg->hdr.event = BTA_DM_API_SC_CR_OOB_DATA_EVT; + bta_sys_sendmsg(p_msg); + } +} +#endif /* BTM_OOB_INCLUDED */ +/******************************************************************************* +** +** Function BTA_DmConfirm +** +** Description This function accepts or rejects the numerical value of the +** Simple Pairing process on BTA_DM_SP_CFM_REQ_EVT +** +** Returns void +** +*******************************************************************************/ +#if (CLASSIC_BT_INCLUDED == TRUE) +void BTA_DmConfirm(BD_ADDR bd_addr, BOOLEAN accept) +{ + tBTA_DM_API_CONFIRM *p_msg; + + if ((p_msg = (tBTA_DM_API_CONFIRM *) osi_malloc(sizeof(tBTA_DM_API_CONFIRM))) != NULL) { + p_msg->hdr.event = BTA_DM_API_CONFIRM_EVT; + bdcpy(p_msg->bd_addr, bd_addr); + p_msg->accept = accept; + bta_sys_sendmsg(p_msg); + } +} + +/******************************************************************************* +** +** Function BTA_DmPasskeyReqReply +** +** Description This function is called to provide the passkey for +** Simple Pairing in response to BTA_DM_SP_KEY_REQ_EVT +** +** Returns void +** +*******************************************************************************/ +void BTA_DmPasskeyReqReply(BOOLEAN accept, BD_ADDR bd_addr, UINT32 passkey) +{ + tBTA_DM_API_KEY_REQ *p_msg; + if ((p_msg = (tBTA_DM_API_KEY_REQ *) osi_malloc(sizeof(tBTA_DM_API_KEY_REQ))) != NULL) { + p_msg->hdr.event = BTA_DM_API_KEY_REQ_EVT; + bdcpy(p_msg->bd_addr, bd_addr); + p_msg->accept = accept; + p_msg->passkey = passkey; + bta_sys_sendmsg(p_msg); + } +} +#endif ///CLASSIC_BT_INCLUDED == TRUE +/******************************************************************************* +** +** Function BTA_DmAddDevice +** +** Description This function adds a device to the security database list of +** peer device +** +** +** Returns void +** +*******************************************************************************/ +void BTA_DmAddDevice(BD_ADDR bd_addr, DEV_CLASS dev_class, LINK_KEY link_key, + tBTA_SERVICE_MASK trusted_mask, BOOLEAN is_trusted, + UINT8 key_type, tBTA_IO_CAP io_cap, UINT8 pin_length, + UINT8 sc_support) +{ + + tBTA_DM_API_ADD_DEVICE *p_msg; + + if ((p_msg = (tBTA_DM_API_ADD_DEVICE *) osi_malloc(sizeof(tBTA_DM_API_ADD_DEVICE))) != NULL) { + memset (p_msg, 0, sizeof(tBTA_DM_API_ADD_DEVICE)); + + p_msg->hdr.event = BTA_DM_API_ADD_DEVICE_EVT; + bdcpy(p_msg->bd_addr, bd_addr); + p_msg->tm = trusted_mask; + p_msg->is_trusted = is_trusted; + p_msg->io_cap = io_cap; + p_msg->sc_support = sc_support; + + if (link_key) { + p_msg->link_key_known = TRUE; + p_msg->key_type = key_type; + memcpy(p_msg->link_key, link_key, LINK_KEY_LEN); + } + + /* Load device class if specified */ + if (dev_class) { + p_msg->dc_known = TRUE; + memcpy (p_msg->dc, dev_class, DEV_CLASS_LEN); + } + + memset (p_msg->bd_name, 0, BD_NAME_LEN + 1); + memset (p_msg->features, 0, sizeof (p_msg->features)); + p_msg->pin_length = pin_length; + + bta_sys_sendmsg(p_msg); + } +} + + +/******************************************************************************* +** +** Function BTA_DmRemoveDevice +** +** Description This function removes a device fromthe security database list of +** peer device. It manages unpairing even while connected. +** +** +** Returns void +** +*******************************************************************************/ +tBTA_STATUS BTA_DmRemoveDevice(BD_ADDR bd_addr, tBT_TRANSPORT transport) +{ + tBTA_DM_API_REMOVE_DEVICE *p_msg; + + if ((p_msg = (tBTA_DM_API_REMOVE_DEVICE *) osi_malloc(sizeof(tBTA_DM_API_REMOVE_DEVICE))) != NULL) { + memset (p_msg, 0, sizeof(tBTA_DM_API_REMOVE_DEVICE)); + + p_msg->hdr.event = BTA_DM_API_REMOVE_DEVICE_EVT; + bdcpy(p_msg->bd_addr, bd_addr); + p_msg->transport = transport; + bta_sys_sendmsg(p_msg); + } else { + return BTA_FAILURE; + } + + return BTA_SUCCESS; +} +// #endif ///SMP_INCLUDED == TRUE + +/******************************************************************************* +** +** Function BTA_GetEirService +** +** Description This function is called to get BTA service mask from EIR. +** +** Parameters p_eir - pointer of EIR significant part +** p_services - return the BTA service mask +** +** Returns None +** +*******************************************************************************/ +extern const UINT16 bta_service_id_to_uuid_lkup_tbl []; +void BTA_GetEirService( UINT8 *p_eir, tBTA_SERVICE_MASK *p_services ) +{ + UINT8 xx, yy; + UINT8 num_uuid, max_num_uuid = 32; + UINT8 uuid_list[32 * LEN_UUID_16]; + UINT16 *p_uuid16 = (UINT16 *)uuid_list; + tBTA_SERVICE_MASK mask; + + BTM_GetEirUuidList( p_eir, LEN_UUID_16, &num_uuid, uuid_list, max_num_uuid); + for ( xx = 0; xx < num_uuid; xx++ ) { + mask = 1; + for ( yy = 0; yy < BTA_MAX_SERVICE_ID; yy++ ) { + if ( *(p_uuid16 + xx) == bta_service_id_to_uuid_lkup_tbl[yy] ) { + *p_services |= mask; + break; + } + mask <<= 1; + } + + /* for HSP v1.2 only device */ + if (*(p_uuid16 + xx) == UUID_SERVCLASS_HEADSET_HS) { + *p_services |= BTA_HSP_SERVICE_MASK; + } + + if (*(p_uuid16 + xx) == UUID_SERVCLASS_HDP_SOURCE) { + *p_services |= BTA_HL_SERVICE_MASK; + } + + if (*(p_uuid16 + xx) == UUID_SERVCLASS_HDP_SINK) { + *p_services |= BTA_HL_SERVICE_MASK; + } + } +} + +/******************************************************************************* +** +** Function BTA_DmGetConnectionState +** +** Description Returns whether the remote device is currently connected. +** +** Returns 0 if the device is NOT connected. +** +*******************************************************************************/ +UINT16 BTA_DmGetConnectionState( BD_ADDR bd_addr ) +{ + tBTA_DM_PEER_DEVICE *p_dev = bta_dm_find_peer_device(bd_addr); + return (p_dev && p_dev->conn_state == BTA_DM_CONNECTED); +} + +#if (SDP_INCLUDED == TRUE) +/******************************************************************************* +** Device Identification (DI) Server Functions +*******************************************************************************/ +/******************************************************************************* +** +** Function BTA_DmSetLocalDiRecord +** +** Description This function adds a DI record to the local SDP database. +** +** Returns BTA_SUCCESS if record set successfully, otherwise error code. +** +*******************************************************************************/ +tBTA_STATUS BTA_DmSetLocalDiRecord( tBTA_DI_RECORD *p_device_info, + UINT32 *p_handle ) +{ + tBTA_STATUS status = BTA_FAILURE; + + if (bta_dm_di_cb.di_num < BTA_DI_NUM_MAX) { + if (SDP_SetLocalDiRecord((tSDP_DI_RECORD *)p_device_info, p_handle) == SDP_SUCCESS) { + if (!p_device_info->primary_record) { + bta_dm_di_cb.di_handle[bta_dm_di_cb.di_num] = *p_handle; + bta_dm_di_cb.di_num ++; + } + + bta_sys_add_uuid(UUID_SERVCLASS_PNP_INFORMATION); + status = BTA_SUCCESS; + } + } + + return status; +} +#endif ///SDP_INCLUDED == TRUE +/******************************************************************************* +** +** Function bta_dmexecutecallback +** +** Description This function will request BTA to execute a call back in the context of BTU task +** This API was named in lower case because it is only intended +** for the internal customers(like BTIF). +** +** Returns void +** +*******************************************************************************/ +void bta_dmexecutecallback (tBTA_DM_EXEC_CBACK *p_callback, void *p_param) +{ + tBTA_DM_API_EXECUTE_CBACK *p_msg; + + if ((p_msg = (tBTA_DM_API_EXECUTE_CBACK *) osi_malloc(sizeof(tBTA_DM_API_EXECUTE_CBACK))) != NULL) { + p_msg->hdr.event = BTA_DM_API_EXECUTE_CBACK_EVT; + p_msg->p_param = p_param; + p_msg->p_exec_cback = p_callback; + bta_sys_sendmsg(p_msg); + } +} + +/******************************************************************************* +** +** Function BTA_DmAddBleKey +** +** Description Add/modify LE device information. This function will be +** normally called during host startup to restore all required +** information stored in the NVRAM. +** +** Parameters: bd_addr - BD address of the peer +** p_le_key - LE key values. +** key_type - LE SMP key type. +** +** Returns BTA_SUCCESS if successful +** BTA_FAIL if operation failed. +** +*******************************************************************************/ +#if BLE_INCLUDED == TRUE +#if SMP_INCLUDED == TRUE +void BTA_DmAddBleKey (BD_ADDR bd_addr, tBTA_LE_KEY_VALUE *p_le_key, tBTA_LE_KEY_TYPE key_type) +{ + tBTA_DM_API_ADD_BLEKEY *p_msg; + + if ((p_msg = (tBTA_DM_API_ADD_BLEKEY *) osi_malloc(sizeof(tBTA_DM_API_ADD_BLEKEY))) != NULL) { + memset (p_msg, 0, sizeof(tBTA_DM_API_ADD_BLEKEY)); + + p_msg->hdr.event = BTA_DM_API_ADD_BLEKEY_EVT; + p_msg->key_type = key_type; + bdcpy(p_msg->bd_addr, bd_addr); + memcpy(&p_msg->blekey, p_le_key, sizeof(tBTA_LE_KEY_VALUE)); + + bta_sys_sendmsg(p_msg); + } + +} + +/******************************************************************************* +** +** Function BTA_DmAddBleDevice +** +** Description Add a BLE device. This function will be normally called +** during host startup to restore all required information +** for a LE device stored in the NVRAM. +** +** Parameters: bd_addr - BD address of the peer +** dev_type - Remote device's device type. +** auth_mode - auth mode +** addr_type - LE device address type. +** +** Returns void +** +*******************************************************************************/ +void BTA_DmAddBleDevice(BD_ADDR bd_addr, tBLE_ADDR_TYPE addr_type, int auth_mode, tBT_DEVICE_TYPE dev_type) +{ + tBTA_DM_API_ADD_BLE_DEVICE *p_msg; + + if ((p_msg = (tBTA_DM_API_ADD_BLE_DEVICE *) osi_malloc(sizeof(tBTA_DM_API_ADD_BLE_DEVICE))) != NULL) { + memset (p_msg, 0, sizeof(tBTA_DM_API_ADD_BLE_DEVICE)); + + p_msg->hdr.event = BTA_DM_API_ADD_BLEDEVICE_EVT; + bdcpy(p_msg->bd_addr, bd_addr); + p_msg->addr_type = addr_type; + p_msg->auth_mode = auth_mode; + p_msg->dev_type = dev_type; + + bta_sys_sendmsg(p_msg); + } +} +/******************************************************************************* +** +** Function BTA_DmBlePasskeyReply +** +** Description Send BLE SMP passkey reply. +** +** Parameters: bd_addr - BD address of the peer +** accept - passkey entry sucessful or declined. +** passkey - passkey value, must be a 6 digit number, +** can be lead by 0. +** +** Returns void +** +*******************************************************************************/ +void BTA_DmBlePasskeyReply(BD_ADDR bd_addr, BOOLEAN accept, UINT32 passkey) +{ + tBTA_DM_API_PASSKEY_REPLY *p_msg; + + if ((p_msg = (tBTA_DM_API_PASSKEY_REPLY *) osi_malloc(sizeof(tBTA_DM_API_PASSKEY_REPLY))) != NULL) { + memset(p_msg, 0, sizeof(tBTA_DM_API_PASSKEY_REPLY)); + + p_msg->hdr.event = BTA_DM_API_BLE_PASSKEY_REPLY_EVT; + bdcpy(p_msg->bd_addr, bd_addr); + p_msg->accept = accept; + + if (accept) { + p_msg->passkey = passkey; + } + bta_sys_sendmsg(p_msg); + } +} + +void BTA_DmBleSetStaticPasskey(bool add, uint32_t passkey) +{ + tBTA_DM_API_SET_DEFAULT_PASSKEY *p_msg; + + if ((p_msg = (tBTA_DM_API_SET_DEFAULT_PASSKEY *) osi_malloc(sizeof(tBTA_DM_API_SET_DEFAULT_PASSKEY))) != NULL) { + memset(p_msg, 0, sizeof(tBTA_DM_API_SET_DEFAULT_PASSKEY)); + + p_msg->hdr.event = BTA_DM_API_BLE_SET_STATIC_PASSKEY_EVT; + p_msg->add = add; + p_msg->static_passkey = passkey; + bta_sys_sendmsg(p_msg); + } +} + +/******************************************************************************* +** +** Function BTA_DmBleConfirmReply +** +** Description Send BLE SMP SC user confirmation reply. +** +** Parameters: bd_addr - BD address of the peer +** accept - numbers to compare are the same or different. +** +** Returns void +** +*******************************************************************************/ +void BTA_DmBleConfirmReply(BD_ADDR bd_addr, BOOLEAN accept) +{ + tBTA_DM_API_CONFIRM *p_msg = (tBTA_DM_API_CONFIRM *)osi_malloc(sizeof(tBTA_DM_API_CONFIRM)); + if (p_msg != NULL) { + memset(p_msg, 0, sizeof(tBTA_DM_API_CONFIRM)); + p_msg->hdr.event = BTA_DM_API_BLE_CONFIRM_REPLY_EVT; + bdcpy(p_msg->bd_addr, bd_addr); + p_msg->accept = accept; + bta_sys_sendmsg(p_msg); + } +} + +/******************************************************************************* +** +** Function BTA_DmBleSecurityGrant +** +** Description Grant security request access. +** +** Parameters: bd_addr - BD address of the peer +** res - security grant status. +** +** Returns void +** +*******************************************************************************/ +void BTA_DmBleSecurityGrant(BD_ADDR bd_addr, tBTA_DM_BLE_SEC_GRANT res) +{ + tBTA_DM_API_BLE_SEC_GRANT *p_msg; + + if ((p_msg = (tBTA_DM_API_BLE_SEC_GRANT *) osi_malloc(sizeof(tBTA_DM_API_BLE_SEC_GRANT))) != NULL) { + memset(p_msg, 0, sizeof(tBTA_DM_API_BLE_SEC_GRANT)); + + p_msg->hdr.event = BTA_DM_API_BLE_SEC_GRANT_EVT; + bdcpy(p_msg->bd_addr, bd_addr); + p_msg->res = res; + + bta_sys_sendmsg(p_msg); + } +} +#endif ///SMP_INCLUDED == TRUE + + +/******************************************************************************* +** +** Function BTA_DmSetBlePrefConnParams +** +** Description This function is called to set the preferred connection +** parameters when default connection parameter is not desired. +** +** Parameters: bd_addr - BD address of the peripheral +** scan_interval - scan interval +** scan_window - scan window +** min_conn_int - minimum preferred connection interval +** max_conn_int - maximum preferred connection interval +** slave_latency - preferred slave latency +** supervision_tout - preferred supervision timeout +** +** +** Returns void +** +*******************************************************************************/ +void BTA_DmSetBlePrefConnParams(BD_ADDR bd_addr, + UINT16 min_conn_int, UINT16 max_conn_int, + UINT16 slave_latency, UINT16 supervision_tout ) +{ +#if BLE_INCLUDED == TRUE + tBTA_DM_API_BLE_CONN_PARAMS *p_msg; + + if ((p_msg = (tBTA_DM_API_BLE_CONN_PARAMS *) osi_malloc(sizeof(tBTA_DM_API_BLE_CONN_PARAMS))) != NULL) { + memset(p_msg, 0, sizeof(tBTA_DM_API_BLE_CONN_PARAMS)); + + p_msg->hdr.event = BTA_DM_API_BLE_CONN_PARAM_EVT; + + memcpy(p_msg->peer_bda, bd_addr, BD_ADDR_LEN); + + p_msg->conn_int_max = max_conn_int; + p_msg->conn_int_min = min_conn_int; + p_msg->slave_latency = slave_latency; + p_msg->supervision_tout = supervision_tout; + + bta_sys_sendmsg(p_msg); + } +#endif +} + +/******************************************************************************* +** +** Function BTA_DmSetBleConnScanParams +** +** Description This function is called to set scan parameters used in +** BLE connection request +** +** Parameters: scan_interval - scan interval +** scan_window - scan window +** +** Returns void +** +*******************************************************************************/ +void BTA_DmSetBleConnScanParams(UINT32 scan_interval, UINT32 scan_window) +{ + tBTA_DM_API_BLE_SCAN_PARAMS *p_msg; + if ((p_msg = (tBTA_DM_API_BLE_SCAN_PARAMS *)osi_malloc(sizeof(tBTA_DM_API_BLE_SCAN_PARAMS))) != NULL) { + memset(p_msg, 0, sizeof(tBTA_DM_API_BLE_SCAN_PARAMS)); + p_msg->hdr.event = BTA_DM_API_BLE_CONN_SCAN_PARAM_EVT; + p_msg->scan_int = scan_interval; + p_msg->scan_window = scan_window; + bta_sys_sendmsg(p_msg); + } +} + +/******************************************************************************* +** +** Function BTA_DmSetBleScanParams +** +** Description This function is called to set scan parameters +** +** Parameters: client_if - Client IF +** scan_interval - scan interval +** scan_window - scan window +** scan_mode - scan mode +** scan_param_setup_status_cback - Set scan param status callback +** +** Returns void +** +*******************************************************************************/ +void BTA_DmSetBleScanParams(tGATT_IF client_if, UINT32 scan_interval, + UINT32 scan_window, tBLE_SCAN_MODE scan_mode, + tBLE_SCAN_PARAM_SETUP_CBACK scan_param_setup_cback) +{ + tBTA_DM_API_BLE_SCAN_PARAMS *p_msg; + + if ((p_msg = (tBTA_DM_API_BLE_SCAN_PARAMS *)osi_malloc(sizeof(tBTA_DM_API_BLE_SCAN_PARAMS))) != NULL) { + memset(p_msg, 0, sizeof(tBTA_DM_API_BLE_SCAN_PARAMS)); + p_msg->hdr.event = BTA_DM_API_BLE_SCAN_PARAM_EVT; + p_msg->client_if = client_if; + p_msg->scan_int = scan_interval; + p_msg->scan_window = scan_window; + p_msg->scan_mode = scan_mode; + p_msg->scan_param_setup_cback = scan_param_setup_cback; + + bta_sys_sendmsg(p_msg); + } +} + + +/******************************************************************************* +** +** Function BTA_DmSetBleScanFilterParams +** +** Description This function is called to set scan parameters +** +** Parameters: client_if - Client IF +** scan_interval - scan interval +** scan_window - scan window +** scan_mode - scan mode +** scan_duplicate_filter - scan duplicate filter +** scan_param_setup_status_cback - Set scan param status callback +** +** Returns void +** +*******************************************************************************/ +void BTA_DmSetBleScanFilterParams(tGATT_IF client_if, UINT32 scan_interval, + UINT32 scan_window, tBLE_SCAN_MODE scan_mode, UINT8 scan_fil_poilcy, + UINT8 addr_type_own, UINT8 scan_duplicate_filter, tBLE_SCAN_PARAM_SETUP_CBACK scan_param_setup_cback) +{ + tBTA_DM_API_BLE_SCAN_FILTER_PARAMS *p_msg; + + if ((p_msg = (tBTA_DM_API_BLE_SCAN_FILTER_PARAMS *)osi_malloc(sizeof(tBTA_DM_API_BLE_SCAN_FILTER_PARAMS))) != NULL) { + memset(p_msg, 0, sizeof(tBTA_DM_API_BLE_SCAN_FILTER_PARAMS)); + p_msg->hdr.event = BTA_DM_API_BLE_SCAN_FIL_PARAM_EVT; + p_msg->client_if = client_if; + p_msg->scan_int = scan_interval; + p_msg->scan_window = scan_window; + p_msg->scan_mode = scan_mode; + p_msg->addr_type_own = addr_type_own; + p_msg->scan_duplicate_filter = scan_duplicate_filter; + p_msg->scan_filter_policy = scan_fil_poilcy; + p_msg->scan_param_setup_cback = scan_param_setup_cback; + + bta_sys_sendmsg(p_msg); + } + + +} + +/******************************************************************************* +** +** Function BTA_DmSetBleAdvParams +** +** Description This function sets the advertising parameters BLE functionality. +** It is to be called when device act in peripheral or broadcaster +** role. +** +** +** Returns void +** +*******************************************************************************/ +void BTA_DmSetBleAdvParams (UINT16 adv_int_min, UINT16 adv_int_max, + tBLE_BD_ADDR *p_dir_bda) +{ +#if BLE_INCLUDED == TRUE + tBTA_DM_API_BLE_ADV_PARAMS *p_msg; + + APPL_TRACE_API ("BTA_DmSetBleAdvParam: %d, %d\n", adv_int_min, adv_int_max); + + if ((p_msg = (tBTA_DM_API_BLE_ADV_PARAMS *) osi_malloc(sizeof(tBTA_DM_API_BLE_ADV_PARAMS) + + sizeof(tBLE_BD_ADDR))) != NULL) { + memset(p_msg, 0, sizeof(tBTA_DM_API_BLE_ADV_PARAMS) + sizeof(tBLE_BD_ADDR)); + + p_msg->hdr.event = BTA_DM_API_BLE_ADV_PARAM_EVT; + + p_msg->adv_int_min = adv_int_min; + p_msg->adv_int_max = adv_int_max; + + if (p_dir_bda != NULL) { + p_msg->p_dir_bda = (tBLE_BD_ADDR *)(p_msg + 1); + memcpy(p_msg->p_dir_bda, p_dir_bda, sizeof(tBLE_BD_ADDR)); + } + + bta_sys_sendmsg(p_msg); + } +#endif +} + +void BTA_DmSetBleAdvParamsAll (UINT16 adv_int_min, UINT16 adv_int_max, + UINT8 adv_type, tBLE_ADDR_TYPE addr_type_own, + tBTM_BLE_ADV_CHNL_MAP chnl_map, tBTM_BLE_AFP adv_fil_pol, + tBLE_BD_ADDR *p_dir_bda, tBTA_START_ADV_CMPL_CBACK p_start_adv_cb) +{ +#if BLE_INCLUDED == TRUE + tBTA_DM_API_BLE_ADV_PARAMS_ALL *p_msg; + + APPL_TRACE_API ("BTA_DmSetBleAdvParamsAll: %d, %d\n", adv_int_min, adv_int_max); + APPL_TRACE_API ("adv_type = %d, addr_type_own = %d, chnl_map = %d, adv_fil_pol = %d\n", + adv_type, addr_type_own, chnl_map, adv_fil_pol); + if ((p_msg = (tBTA_DM_API_BLE_ADV_PARAMS_ALL *) osi_malloc(sizeof(tBTA_DM_API_BLE_ADV_PARAMS_ALL) + + sizeof(tBLE_BD_ADDR))) != NULL) { + memset(p_msg, 0, sizeof(tBTA_DM_API_BLE_ADV_PARAMS_ALL)); + + p_msg->hdr.event = BTA_DM_API_BLE_ADV_PARAM_All_EVT; + + p_msg->adv_int_min = adv_int_min; + p_msg->adv_int_max = adv_int_max; + p_msg->adv_type = adv_type; + p_msg->addr_type_own = addr_type_own; + p_msg->channel_map = chnl_map; + p_msg->adv_filter_policy = adv_fil_pol; + p_msg->p_start_adv_cback = p_start_adv_cb; + if (p_dir_bda != NULL) { + p_msg->p_dir_bda = (tBLE_BD_ADDR *)(p_msg + 1); + memcpy(p_msg->p_dir_bda, p_dir_bda, sizeof(tBLE_BD_ADDR)); + } + + bta_sys_sendmsg(p_msg); + } +#endif +} +#endif ///BLE_INCLUDED == TRUE + + +/******************************************************************************* +** BLE ADV data management API +********************************************************************************/ + +#if BLE_INCLUDED == TRUE +/******************************************************************************* +** +** Function BTA_DmBleSetAdvConfig +** +** Description This function is called to override the BTA default ADV parameters. +** +** Parameters data_mask: adv data mask. +** p_adv_cfg: Pointer to User defined ADV data structure. This +** memory space can not be freed until p_adv_data_cback +** is received. +** p_adv_data_cback: set adv data complete callback. +** +** Returns None +** +*******************************************************************************/ +void BTA_DmBleSetAdvConfig (tBTA_BLE_AD_MASK data_mask, tBTA_BLE_ADV_DATA *p_adv_cfg, + tBTA_SET_ADV_DATA_CMPL_CBACK *p_adv_data_cback) +{ + tBTA_DM_API_SET_ADV_CONFIG *p_msg; + + if ((p_msg = (tBTA_DM_API_SET_ADV_CONFIG *) + osi_malloc(sizeof(tBTA_DM_API_SET_ADV_CONFIG))) != NULL) { + p_msg->hdr.event = BTA_DM_API_BLE_SET_ADV_CONFIG_EVT; + p_msg->data_mask = data_mask; + p_msg->p_adv_data_cback = p_adv_data_cback; + p_msg->p_adv_cfg = p_adv_cfg; + + bta_sys_sendmsg(p_msg); + } +} + +/******************************************************************************* +** +** Function BTA_DmBleSetAdvConfigRaw +** +** Description This function is called to set raw Advertising data +** +** Parameters p_raw_adv : raw advertising data. +** raw_adv_len : raw advertising data length. +** p_adv_data_cback : set adv data complete callback. +** +** Returns None +** +*******************************************************************************/ +void BTA_DmBleSetAdvConfigRaw (UINT8 *p_raw_adv, UINT32 raw_adv_len, + tBTA_SET_ADV_DATA_CMPL_CBACK *p_adv_data_cback) +{ + tBTA_DM_API_SET_ADV_CONFIG_RAW *p_msg; + + if ((p_msg = (tBTA_DM_API_SET_ADV_CONFIG_RAW *) + osi_malloc(sizeof(tBTA_DM_API_SET_ADV_CONFIG_RAW) + raw_adv_len)) != NULL) { + p_msg->hdr.event = BTA_DM_API_BLE_SET_ADV_CONFIG_RAW_EVT; + p_msg->p_adv_data_cback = p_adv_data_cback; + p_msg->p_raw_adv = (UINT8 *)(p_msg + 1); + memcpy(p_msg->p_raw_adv, p_raw_adv, raw_adv_len); + p_msg->raw_adv_len = raw_adv_len; + + bta_sys_sendmsg(p_msg); + } +} + +/******************************************************************************* +** +** Function BTA_DmBleSetLongAdv +** +** Description This function is called to set long Advertising data +** +** Parameters adv_data : long advertising data. +** adv_data_len : long advertising data length. +** p_adv_data_cback : set long adv data complete callback. +** +** Returns None +** +*******************************************************************************/ +void BTA_DmBleSetLongAdv (UINT8 *adv_data, UINT32 adv_data_len, + tBTA_SET_ADV_DATA_CMPL_CBACK *p_adv_data_cback) +{ + tBTA_DM_API_SET_LONG_ADV *p_msg; + + if ((p_msg = (tBTA_DM_API_SET_LONG_ADV *) + osi_malloc(sizeof(tBTA_DM_API_SET_LONG_ADV))) != NULL) { + p_msg->hdr.event = BTA_DM_API_BLE_SET_LONG_ADV_EVT; + p_msg->p_adv_data_cback = p_adv_data_cback; + p_msg->adv_data = adv_data; + p_msg->adv_data_len = adv_data_len; + + bta_sys_sendmsg(p_msg); + } +} + +/******************************************************************************* +** +** Function BTA_DmBleSetScanRsp +** +** Description This function is called to override the BTA scan response. +** +** Parameters Pointer to User defined ADV data structure +** +** Returns None +** +*******************************************************************************/ +extern void BTA_DmBleSetScanRsp (tBTA_BLE_AD_MASK data_mask, tBTA_BLE_ADV_DATA *p_adv_cfg, + tBTA_SET_ADV_DATA_CMPL_CBACK *p_adv_data_cback) +{ + tBTA_DM_API_SET_ADV_CONFIG *p_msg; + + if ((p_msg = (tBTA_DM_API_SET_ADV_CONFIG *) + osi_malloc(sizeof(tBTA_DM_API_SET_ADV_CONFIG))) != NULL) { + p_msg->hdr.event = BTA_DM_API_BLE_SET_SCAN_RSP_EVT; + p_msg->data_mask = data_mask; + p_msg->p_adv_data_cback = p_adv_data_cback; + p_msg->p_adv_cfg = p_adv_cfg; + + bta_sys_sendmsg(p_msg); + } +} + +/******************************************************************************* +** +** Function BTA_DmBleSetScanRspRaw +** +** Description This function is called to set raw scan response data +** +** Parameters p_raw_scan_rsp : raw scan_rspertising data. +** raw_scan_rsp_len : raw scan_rspertising data length. +** p_scan_rsp_data_cback : set scan_rsp data complete callback. +** +** Returns None +** +*******************************************************************************/ +void BTA_DmBleSetScanRspRaw (UINT8 *p_raw_scan_rsp, UINT32 raw_scan_rsp_len, + tBTA_SET_ADV_DATA_CMPL_CBACK *p_scan_rsp_data_cback) +{ + tBTA_DM_API_SET_ADV_CONFIG_RAW *p_msg; + + if ((p_msg = (tBTA_DM_API_SET_ADV_CONFIG_RAW *) + osi_malloc(sizeof(tBTA_DM_API_SET_ADV_CONFIG_RAW) + raw_scan_rsp_len)) != NULL) { + p_msg->hdr.event = BTA_DM_API_BLE_SET_SCAN_RSP_RAW_EVT; + p_msg->p_adv_data_cback = p_scan_rsp_data_cback; + p_msg->p_raw_adv = (UINT8 *)(p_msg + 1); + memcpy(p_msg->p_raw_adv, p_raw_scan_rsp, raw_scan_rsp_len); + p_msg->raw_adv_len = raw_scan_rsp_len; + + bta_sys_sendmsg(p_msg); + } +} + +/******************************************************************************* +** +** Function BTA_DmUpdateDuplicateExceptionalList +** +** Description This function is called to update duplicate scan exceptional list +** +** Parameters subcode : add, remove or clean duplicate scan exceptional list. +** type : device info type. +** device_info: device info +** p_update_duplicate_ignore_list_cback : update complete callback. +** +** Returns None +** +*******************************************************************************/ +void BTA_DmUpdateDuplicateExceptionalList(UINT8 subcode, UINT32 type, BD_ADDR device_info, tBTA_UPDATE_DUPLICATE_EXCEPTIONAL_LIST_CMPL_CBACK p_update_duplicate_exceptional_list_cback) +{ + tBTA_DM_API_UPDATE_DUPLICATE_EXCEPTIONAL_LIST *p_msg; + if ((p_msg = (tBTA_DM_API_UPDATE_DUPLICATE_EXCEPTIONAL_LIST *)osi_malloc(sizeof(tBTA_DM_API_UPDATE_DUPLICATE_EXCEPTIONAL_LIST))) != NULL) { + p_msg->hdr.event = BTA_DM_API_UPDATE_DUPLICATE_EXCEPTIONAL_LIST_EVT; + p_msg->subcode = subcode; + p_msg->type = type; + p_msg->exceptional_list_cb = p_update_duplicate_exceptional_list_cback; + memcpy(p_msg->device_info, device_info, sizeof(BD_ADDR)); + + bta_sys_sendmsg(p_msg); + } +} + +/******************************************************************************* +** +** Function BTA_DmBleSetStorageParams +** +** Description This function is called to override the BTA scan response. +** +** Parameters batch_scan_full_max -Max storage space (in %) allocated to full scanning +** batch_scan_trunc_max -Max storage space (in %) allocated to truncated scanning +** batch_scan_notify_threshold -Setup notification level based on total space +** p_setup_cback - Setup callback pointer +** p_thres_cback - Threshold callback pointer +** p_rep_cback - Reports callback pointer +** ref_value - Ref value +** +** Returns None +** +*******************************************************************************/ +extern void BTA_DmBleSetStorageParams(UINT8 batch_scan_full_max, + UINT8 batch_scan_trunc_max, + UINT8 batch_scan_notify_threshold, + tBTA_BLE_SCAN_SETUP_CBACK *p_setup_cback, + tBTA_BLE_SCAN_THRESHOLD_CBACK *p_thres_cback, + tBTA_BLE_SCAN_REP_CBACK *p_rep_cback, + tBTA_DM_BLE_REF_VALUE ref_value) +{ + tBTA_DM_API_SET_STORAGE_CONFIG *p_msg; + bta_dm_cb.p_setup_cback = p_setup_cback; + if ((p_msg = (tBTA_DM_API_SET_STORAGE_CONFIG *) + osi_malloc(sizeof(tBTA_DM_API_SET_STORAGE_CONFIG))) != NULL) { + p_msg->hdr.event = BTA_DM_API_BLE_SETUP_STORAGE_EVT; + p_msg->p_setup_cback = bta_ble_scan_setup_cb; + p_msg->p_thres_cback = p_thres_cback; + p_msg->p_read_rep_cback = p_rep_cback; + p_msg->ref_value = ref_value; + p_msg->batch_scan_full_max = batch_scan_full_max; + p_msg->batch_scan_trunc_max = batch_scan_trunc_max; + p_msg->batch_scan_notify_threshold = batch_scan_notify_threshold; + bta_sys_sendmsg(p_msg); + } +} + +/******************************************************************************* +** +** Function BTA_DmBleEnableBatchScan +** +** Description This function is called to enable the batch scan +** +** Parameters scan_mode -Batch scan mode +** scan_interval - Scan interval +** scan_window - Scan window +** discard_rule -Discard rules +** addr_type - Address type +** ref_value - Reference value +** +** Returns None +** +*******************************************************************************/ +extern void BTA_DmBleEnableBatchScan(tBTA_BLE_BATCH_SCAN_MODE scan_mode, + UINT32 scan_interval, UINT32 scan_window, + tBTA_BLE_DISCARD_RULE discard_rule, + tBLE_ADDR_TYPE addr_type, + tBTA_DM_BLE_REF_VALUE ref_value) +{ + tBTA_DM_API_ENABLE_SCAN *p_msg; + + if ((p_msg = (tBTA_DM_API_ENABLE_SCAN *) osi_malloc(sizeof(tBTA_DM_API_ENABLE_SCAN))) != NULL) { + p_msg->hdr.event = BTA_DM_API_BLE_ENABLE_BATCH_SCAN_EVT; + p_msg->scan_mode = scan_mode; + p_msg->scan_int = scan_interval; + p_msg->scan_window = scan_window; + p_msg->discard_rule = discard_rule; + p_msg->addr_type = addr_type; + p_msg->ref_value = ref_value; + bta_sys_sendmsg(p_msg); + } +} + +/******************************************************************************* +** +** Function BTA_DmBleDisableBatchScan +** +** Description This function is called to disable the batch scan +** +** Parameters ref_value - Reference value +** +** Returns None +** +*******************************************************************************/ +extern void BTA_DmBleDisableBatchScan(tBTA_DM_BLE_REF_VALUE ref_value) +{ + tBTA_DM_API_DISABLE_SCAN *p_msg; + + if ((p_msg = (tBTA_DM_API_DISABLE_SCAN *) + osi_malloc(sizeof(tBTA_DM_API_DISABLE_SCAN))) != NULL) { + p_msg->hdr.event = BTA_DM_API_BLE_DISABLE_BATCH_SCAN_EVT; + p_msg->ref_value = ref_value; + bta_sys_sendmsg(p_msg); + } +} + +/******************************************************************************* +** +** Function BTA_DmBleReadScanReports +** +** Description This function is called to read scan reports +** +** Parameters scan_type -Batch scan mode +** ref_value - Reference value +** +** Returns None +** +*******************************************************************************/ +extern void BTA_DmBleReadScanReports(tBTA_BLE_BATCH_SCAN_MODE scan_type, + tBTA_DM_BLE_REF_VALUE ref_value) +{ + tBTA_DM_API_READ_SCAN_REPORTS *p_msg; + + if ((p_msg = (tBTA_DM_API_READ_SCAN_REPORTS *) + osi_malloc(sizeof(tBTA_DM_API_READ_SCAN_REPORTS))) != NULL) { + p_msg->hdr.event = BTA_DM_API_BLE_READ_SCAN_REPORTS_EVT; + p_msg->scan_type = scan_type; + p_msg->ref_value = ref_value; + bta_sys_sendmsg(p_msg); + } +} + +/******************************************************************************* +** +** Function BTA_DmBleTrackAdvertiser +** +** Description This function is called to track advertiser +** +** Parameters ref_value - Reference value +** p_track_adv_cback - Track ADV callback +** +** Returns None +** +*******************************************************************************/ +extern void BTA_DmBleTrackAdvertiser(tBTA_DM_BLE_REF_VALUE ref_value, + tBTA_BLE_TRACK_ADV_CBACK *p_track_adv_cback) +{ + tBTA_DM_API_TRACK_ADVERTISER *p_msg; + + if ((p_msg = (tBTA_DM_API_TRACK_ADVERTISER *) + osi_malloc(sizeof(tBTA_DM_API_TRACK_ADVERTISER))) != NULL) { + p_msg->hdr.event = BTA_DM_API_BLE_TRACK_ADVERTISER_EVT; + p_msg->p_track_adv_cback = p_track_adv_cback; + p_msg->ref_value = ref_value; + bta_sys_sendmsg(p_msg); + } +} + +#endif + +/******************************************************************************* +** BLE ADV data management API +********************************************************************************/ +#if BLE_INCLUDED == TRUE + +/******************************************************************************* +** +** Function BTA_DmBleBroadcast +** +** Description This function starts or stops LE broadcasting. +** +** Parameters start: start or stop broadcast. +** +** Returns None +** +*******************************************************************************/ +extern void BTA_DmBleBroadcast (BOOLEAN start, tBTA_START_STOP_ADV_CMPL_CBACK *p_start_stop_adv_cb) +{ + tBTA_DM_API_BLE_OBSERVE *p_msg; + + APPL_TRACE_API("BTA_DmBleBroadcast: start = %d \n", start); + + if ((p_msg = (tBTA_DM_API_BLE_OBSERVE *) osi_malloc(sizeof(tBTA_DM_API_BLE_OBSERVE))) != NULL) { + memset(p_msg, 0, sizeof(tBTA_DM_API_BLE_OBSERVE)); + + p_msg->hdr.event = BTA_DM_API_BLE_BROADCAST_EVT; + p_msg->start = start; + if (start == FALSE){ + p_msg->p_stop_adv_cback= p_start_stop_adv_cb; + } + + bta_sys_sendmsg(p_msg); + } +} + +/******************************************************************************* +** +** Function BTA_DmBleClearAdv +** +** Description This function is called to clear Advertising +** +** Parameters p_adv_data_cback : clear adv complete callback. +** +** Returns None +** +*******************************************************************************/ +void BTA_DmBleClearAdv (tBTA_CLEAR_ADV_CMPL_CBACK *p_clear_adv_cback) +{ + tBTA_DM_API_CLEAR_ADV *p_msg; + + if ((p_msg = (tBTA_DM_API_CLEAR_ADV *) + osi_malloc(sizeof(tBTA_DM_API_CLEAR_ADV))) != NULL) { + p_msg->hdr.event = BTA_DM_API_BLE_CLEAR_ADV_EVT; + p_msg->p_clear_adv_cback = p_clear_adv_cback; + + bta_sys_sendmsg(p_msg); + } +} +#endif +/******************************************************************************* +** +** Function BTA_DmBleSetBgConnType +** +** Description This function is called to set BLE connectable mode for a +** peripheral device. +** +** Parameters bg_conn_type: it can be auto connection, or selective connection. +** p_select_cback: callback function when selective connection procedure +** is being used. +** +** Returns void +** +*******************************************************************************/ +void BTA_DmBleSetBgConnType(tBTA_DM_BLE_CONN_TYPE bg_conn_type, tBTA_DM_BLE_SEL_CBACK *p_select_cback) +{ +#if BLE_INCLUDED == TRUE + tBTA_DM_API_BLE_SET_BG_CONN_TYPE *p_msg; + + if ((p_msg = (tBTA_DM_API_BLE_SET_BG_CONN_TYPE *) osi_malloc(sizeof(tBTA_DM_API_BLE_SET_BG_CONN_TYPE))) != NULL) { + memset(p_msg, 0, sizeof(tBTA_DM_API_BLE_SET_BG_CONN_TYPE)); + + p_msg->hdr.event = BTA_DM_API_BLE_SET_BG_CONN_TYPE; + p_msg->bg_conn_type = bg_conn_type; + p_msg->p_select_cback = p_select_cback; + + bta_sys_sendmsg(p_msg); + } +#endif +} + +/******************************************************************************* +** +** Function bta_dm_discover_send_msg +** +** Description This function send discover message to BTA task. +** +** Returns void +** +*******************************************************************************/ +#if BLE_INCLUDED == TRUE && BTA_GATT_INCLUDED == TRUE && SDP_INCLUDED == TRUE +static void bta_dm_discover_send_msg(BD_ADDR bd_addr, tBTA_SERVICE_MASK_EXT *p_services, + tBTA_DM_SEARCH_CBACK *p_cback, BOOLEAN sdp_search, + tBTA_TRANSPORT transport) +{ + tBTA_DM_API_DISCOVER *p_msg; + UINT16 len = p_services ? (sizeof(tBTA_DM_API_DISCOVER) + + sizeof(tBT_UUID) * p_services->num_uuid) : + sizeof(tBTA_DM_API_DISCOVER); + + if ((p_msg = (tBTA_DM_API_DISCOVER *) osi_malloc(len)) != NULL) { + memset(p_msg, 0, len); + + p_msg->hdr.event = BTA_DM_API_DISCOVER_EVT; + bdcpy(p_msg->bd_addr, bd_addr); + p_msg->p_cback = p_cback; + p_msg->sdp_search = sdp_search; + p_msg->transport = transport; + + if (p_services != NULL) { +#if BLE_INCLUDED == TRUE && BTA_GATT_INCLUDED == TRUE + p_msg->services = p_services->srvc_mask; + p_msg->num_uuid = p_services->num_uuid; + if (p_services->num_uuid != 0) { + p_msg->p_uuid = (tBT_UUID *)(p_msg + 1); + memcpy(p_msg->p_uuid, p_services->p_uuid, sizeof(tBT_UUID) * p_services->num_uuid); + } +#endif + } + + bta_sys_sendmsg(p_msg); + } +} +#endif +/******************************************************************************* +** +** Function BTA_DmDiscoverByTransport +** +** Description This function does service discovery on particular transport +** for services of a +** peer device. When services.num_uuid is 0, it indicates all +** GATT based services are to be searched; otherwise a list of +** UUID of interested services should be provided through +** p_services->p_uuid. +** +** +** +** Returns void +** +*******************************************************************************/ +void BTA_DmDiscoverByTransport(BD_ADDR bd_addr, tBTA_SERVICE_MASK_EXT *p_services, + tBTA_DM_SEARCH_CBACK *p_cback, BOOLEAN sdp_search, + tBTA_TRANSPORT transport) +{ +#if BLE_INCLUDED == TRUE && BTA_GATT_INCLUDED == TRUE && SDP_INCLUDED == TRUE + bta_dm_discover_send_msg(bd_addr, p_services, p_cback, sdp_search, transport); +#endif +} + + +/******************************************************************************* +** +** Function BTA_DmDiscoverExt +** +** Description This function does service discovery for services of a +** peer device. When services.num_uuid is 0, it indicates all +** GATT based services are to be searched; other wise a list of +** UUID of interested services should be provided through +** p_services->p_uuid. +** +** +** +** Returns void +** +*******************************************************************************/ +void BTA_DmDiscoverExt(BD_ADDR bd_addr, tBTA_SERVICE_MASK_EXT *p_services, + tBTA_DM_SEARCH_CBACK *p_cback, BOOLEAN sdp_search) +{ +#if BLE_INCLUDED == TRUE && BTA_GATT_INCLUDED == TRUE && SDP_INCLUDED == TRUE + bta_dm_discover_send_msg(bd_addr, p_services, p_cback, sdp_search, BTA_TRANSPORT_UNKNOWN); +#endif + +} + +/******************************************************************************* +** +** Function BTA_DmSearchExt +** +** Description This function searches for peer Bluetooth devices. It performs +** an inquiry and gets the remote name for devices. Service +** discovery is done if services is non zero +** +** Parameters p_dm_inq: inquiry conditions +** p_services: if service is not empty, service discovery will be done. +** for all GATT based service condition, put num_uuid, and +** p_uuid is the pointer to the list of UUID values. +** p_cback: callback functino when search is completed. +** +** +** +** Returns void +** +*******************************************************************************/ +void BTA_DmSearchExt(tBTA_DM_INQ *p_dm_inq, tBTA_SERVICE_MASK_EXT *p_services, tBTA_DM_SEARCH_CBACK *p_cback) +{ +#if BLE_INCLUDED == TRUE && BTA_GATT_INCLUDED == TRUE + tBTA_DM_API_SEARCH *p_msg; + UINT16 len = p_services ? (sizeof(tBTA_DM_API_SEARCH) + sizeof(tBT_UUID) * p_services->num_uuid) : + sizeof(tBTA_DM_API_SEARCH); + + if ((p_msg = (tBTA_DM_API_SEARCH *) osi_malloc(len)) != NULL) { + memset(p_msg, 0, len); + + p_msg->hdr.event = BTA_DM_API_SEARCH_EVT; + memcpy(&p_msg->inq_params, p_dm_inq, sizeof(tBTA_DM_INQ)); + p_msg->p_cback = p_cback; + p_msg->rs_res = BTA_DM_RS_NONE; + + + if (p_services != NULL) { + p_msg->services = p_services->srvc_mask; + p_msg->num_uuid = p_services->num_uuid; + + if (p_services->num_uuid != 0) { + p_msg->p_uuid = (tBT_UUID *)(p_msg + 1); + memcpy(p_msg->p_uuid, p_services->p_uuid, sizeof(tBT_UUID) * p_services->num_uuid); + } else { + p_msg->p_uuid = NULL; + } + } + + bta_sys_sendmsg(p_msg); + } +#else + UNUSED(p_dm_inq); + UNUSED(p_services); + UNUSED(p_cback); +#endif +} +/******************************************************************************* +** +** Function BTA_DmBleUpdateConnectionParam +** +** Description Update connection parameters, can only be used when connection is up. +** +** Parameters: bd_addr - BD address of the peer +** min_int - minimum connection interval, [0x0004~ 0x4000] +** max_int - maximum connection interval, [0x0004~ 0x4000] +** latency - slave latency [0 ~ 500] +** timeout - supervision timeout [0x000a ~ 0xc80] +** +** Returns void +** +*******************************************************************************/ +void BTA_DmBleUpdateConnectionParam(BD_ADDR bd_addr, UINT16 min_int, + UINT16 max_int, UINT16 latency, + UINT16 timeout) +{ +#if BLE_INCLUDED == TRUE + tBTA_DM_API_UPDATE_CONN_PARAM *p_msg; + + p_msg = (tBTA_DM_API_UPDATE_CONN_PARAM *) osi_malloc(sizeof(tBTA_DM_API_UPDATE_CONN_PARAM)); + if (p_msg != NULL) { + memset(p_msg, 0, sizeof(tBTA_DM_API_UPDATE_CONN_PARAM)); + + p_msg->hdr.event = BTA_DM_API_UPDATE_CONN_PARAM_EVT; + bdcpy(p_msg->bd_addr, bd_addr); + p_msg->min_int = min_int; + p_msg->max_int = max_int; + p_msg->latency = latency; + p_msg->timeout = timeout; + + bta_sys_sendmsg(p_msg); + } +#endif +} + +#if BLE_INCLUDED == TRUE +/******************************************************************************* +** +** Function BTA_DmBleConfigLocalPrivacy +** +** Description Enable/disable privacy on the local device +** +** Parameters: privacy_enable - enable/disabe privacy on remote device. +** +** Returns void +** +*******************************************************************************/ +void BTA_DmBleConfigLocalPrivacy(BOOLEAN privacy_enable, tBTA_SET_LOCAL_PRIVACY_CBACK *set_local_privacy_cback) +{ + ///This function used the irk to generate the resolve address +#if BLE_INCLUDED == TRUE && BLE_PRIVACY_SPT == TRUE + tBTA_DM_API_LOCAL_PRIVACY *p_msg; + + if ((p_msg = (tBTA_DM_API_LOCAL_PRIVACY *) osi_malloc(sizeof(tBTA_DM_API_ENABLE_PRIVACY))) != NULL) { + memset (p_msg, 0, sizeof(tBTA_DM_API_LOCAL_PRIVACY)); + + p_msg->hdr.event = BTA_DM_API_LOCAL_PRIVACY_EVT; + p_msg->privacy_enable = privacy_enable; + p_msg->set_local_privacy_cback = set_local_privacy_cback; + bta_sys_sendmsg(p_msg); + } +#else + UNUSED (privacy_enable); +#endif +} + +/******************************************************************************* +** +** Function BTA_DmBleConfigLocalIcon +** +** Description set gap local icon +** +** Parameters: icon - appearance value. +** +** Returns void +** +*******************************************************************************/ +void BTA_DmBleConfigLocalIcon(uint16_t icon) +{ + tBTA_DM_API_LOCAL_ICON *p_msg; + + if ((p_msg = (tBTA_DM_API_LOCAL_ICON *) osi_malloc(sizeof(tBTA_DM_API_LOCAL_ICON))) != NULL) { + memset (p_msg, 0, sizeof(tBTA_DM_API_LOCAL_ICON)); + + p_msg->hdr.event = BTA_DM_API_LOCAL_ICON_EVT; + p_msg->icon = icon; + bta_sys_sendmsg(p_msg); + } +} + +/******************************************************************************* +** +** Function BTA_BleEnableAdvInstance +** +** Description This function enable a Multi-ADV instance with the specififed +** adv parameters +** +** Parameters p_params: pointer to the adv parameter structure. +** p_cback: callback function associated to this adv instance. +** p_ref: reference data pointer to this adv instance. +** +** Returns BTA_SUCCESS if command started successfully; otherwise failure. +** +*******************************************************************************/ +void BTA_BleEnableAdvInstance (tBTA_BLE_ADV_PARAMS *p_params, + tBTA_BLE_MULTI_ADV_CBACK *p_cback, + void *p_ref) +{ + ///This function just used for vendor debug + tBTA_DM_API_BLE_MULTI_ADV_ENB *p_msg; + UINT16 len = sizeof(tBTA_BLE_ADV_PARAMS) + sizeof(tBTA_DM_API_BLE_MULTI_ADV_ENB); + + APPL_TRACE_API ("BTA_BleEnableAdvInstance"); + + if ((p_msg = (tBTA_DM_API_BLE_MULTI_ADV_ENB *) osi_malloc(len)) != NULL) { + memset(p_msg, 0, sizeof(tBTA_DM_API_BLE_MULTI_ADV_ENB)); + + p_msg->hdr.event = BTA_DM_API_BLE_MULTI_ADV_ENB_EVT; + p_msg->p_cback = (void *)p_cback; + if (p_params != NULL) { + p_msg->p_params = (void *)(p_msg + 1); + memcpy(p_msg->p_params, p_params, sizeof(tBTA_BLE_ADV_PARAMS)); + } + p_msg->p_ref = p_ref; + + bta_sys_sendmsg(p_msg); + } +} + +/******************************************************************************* +** +** Function BTA_BleUpdateAdvInstParam +** +** Description This function update a Multi-ADV instance with the specififed +** adv parameters. +** +** Parameters inst_id: Adv instance to update the parameter. +** p_params: pointer to the adv parameter structure. +** +** Returns BTA_SUCCESS if command started successfully; otherwise failure. +** +*******************************************************************************/ +void BTA_BleUpdateAdvInstParam (UINT8 inst_id, tBTA_BLE_ADV_PARAMS *p_params) +{ + ///This function just used for vendor debug + tBTA_DM_API_BLE_MULTI_ADV_PARAM *p_msg; + UINT16 len = sizeof(tBTA_BLE_ADV_PARAMS) + sizeof(tBTA_DM_API_BLE_MULTI_ADV_PARAM); + + APPL_TRACE_API ("BTA_BleUpdateAdvInstParam"); + if ((p_msg = (tBTA_DM_API_BLE_MULTI_ADV_PARAM *) osi_malloc(len)) != NULL) { + memset(p_msg, 0, sizeof(tBTA_DM_API_BLE_MULTI_ADV_PARAM)); + p_msg->hdr.event = BTA_DM_API_BLE_MULTI_ADV_PARAM_UPD_EVT; + p_msg->inst_id = inst_id; + p_msg->p_params = (void *)(p_msg + 1); + memcpy(p_msg->p_params, p_params, sizeof(tBTA_BLE_ADV_PARAMS)); + + bta_sys_sendmsg(p_msg); + } +} + +/******************************************************************************* +** +** Function BTA_BleCfgAdvInstData +** +** Description This function configure a Multi-ADV instance with the specififed +** adv data or scan response data. +** +** Parameter inst_id: Adv instance to configure the adv data or scan response. +** is_scan_rsp: is the data scan response or adv data. +** data_mask: adv data type as bit mask. +** p_data: pointer to the ADV data structure tBTA_BLE_ADV_DATA. This +** memory space can not be freed until BTA_BLE_MULTI_ADV_DATA_EVT +** is sent to application. +** +** Returns BTA_SUCCESS if command started successfully; otherwise failure. +** +*******************************************************************************/ +void BTA_BleCfgAdvInstData (UINT8 inst_id, BOOLEAN is_scan_rsp, + tBTA_BLE_AD_MASK data_mask, + tBTA_BLE_ADV_DATA *p_data) +{ + ///This function just used for vendor debug + tBTA_DM_API_BLE_MULTI_ADV_DATA *p_msg; + UINT16 len = sizeof(tBTA_DM_API_BLE_MULTI_ADV_DATA) ; + + APPL_TRACE_API ("BTA_BleCfgAdvInstData"); + + if ((p_msg = (tBTA_DM_API_BLE_MULTI_ADV_DATA *) osi_malloc(len)) != NULL) { + memset(p_msg, 0, len); + p_msg->hdr.event = BTA_DM_API_BLE_MULTI_ADV_DATA_EVT; + p_msg->inst_id = inst_id; + p_msg->is_scan_rsp = is_scan_rsp; + p_msg->data_mask = data_mask; + p_msg->p_data = p_data; + + bta_sys_sendmsg(p_msg); + } +} + +/******************************************************************************* +** +** Function BTA_BleDisableAdvInstance +** +** Description This function disable a Multi-ADV instance. +** +** Parameter inst_id: instance ID to disable. +** +** Returns BTA_SUCCESS if command started successfully; otherwise failure. +** +*******************************************************************************/ +void BTA_BleDisableAdvInstance (UINT8 inst_id) //this function just used for vendor debug +{ + tBTA_DM_API_BLE_MULTI_ADV_DISABLE *p_msg; + + APPL_TRACE_API ("BTA_BleDisableAdvInstance: %d", inst_id); + if ((p_msg = (tBTA_DM_API_BLE_MULTI_ADV_DISABLE *) + osi_malloc(sizeof(tBTA_DM_API_BLE_MULTI_ADV_DISABLE))) != NULL) { + memset(p_msg, 0, sizeof(tBTA_DM_API_BLE_MULTI_ADV_DISABLE)); + p_msg->hdr.event = BTA_DM_API_BLE_MULTI_ADV_DISABLE_EVT; + p_msg->inst_id = inst_id; + bta_sys_sendmsg(p_msg); + } +} + +/******************************************************************************* +** +** Function BTA_DmBleCfgFilterCondition +** +** Description This function is called to configure the adv data payload filter +** condition. +** +** Parameters action: to read/write/clear +** cond_type: filter condition type +** filt_index - Filter index +** p_cond: filter condition parameter +** p_cmpl_back - Command completed callback +** ref_value - Reference value +** +** Returns void +** +*******************************************************************************/ +void BTA_DmBleCfgFilterCondition(tBTA_DM_BLE_SCAN_COND_OP action, + tBTA_DM_BLE_PF_COND_TYPE cond_type, + tBTA_DM_BLE_PF_FILT_INDEX filt_index, + tBTA_DM_BLE_PF_COND_PARAM *p_cond, + tBTA_DM_BLE_PF_CFG_CBACK *p_cmpl_cback, + tBTA_DM_BLE_REF_VALUE ref_value) +{ +#if BLE_ANDROID_CONTROLLER_SCAN_FILTER == TRUE + tBTA_DM_API_CFG_FILTER_COND *p_msg; + APPL_TRACE_API ("BTA_DmBleCfgFilterCondition: %d, %d", action, cond_type); + + UINT16 len = sizeof(tBTA_DM_API_CFG_FILTER_COND) + + sizeof(tBTA_DM_BLE_PF_COND_PARAM); + UINT8 *p; + + if (NULL != p_cond) { + switch (cond_type) { + case BTA_DM_BLE_PF_SRVC_DATA_PATTERN: + case BTA_DM_BLE_PF_MANU_DATA: + /* Length of pattern and pattern mask and other elements in */ + /* tBTA_DM_BLE_PF_MANU_COND */ + len += ((p_cond->manu_data.data_len) * 2) + + sizeof(UINT16) + sizeof(UINT16) + sizeof(UINT8); + break; + + case BTA_DM_BLE_PF_LOCAL_NAME: + len += ((p_cond->local_name.data_len) + sizeof(UINT8)); + break; + + case BTM_BLE_PF_SRVC_UUID: + case BTM_BLE_PF_SRVC_SOL_UUID: + len += sizeof(tBLE_BD_ADDR) + sizeof(tBTA_DM_BLE_PF_COND_MASK); + break; + + default: + break; + } + } + + if ((p_msg = (tBTA_DM_API_CFG_FILTER_COND *) osi_malloc(len)) != NULL) { + memset (p_msg, 0, len); + + p_msg->hdr.event = BTA_DM_API_CFG_FILTER_COND_EVT; + p_msg->action = action; + p_msg->cond_type = cond_type; + p_msg->filt_index = filt_index; + p_msg->p_filt_cfg_cback = p_cmpl_cback; + p_msg->ref_value = ref_value; + if (p_cond) { + p_msg->p_cond_param = (tBTA_DM_BLE_PF_COND_PARAM *)(p_msg + 1); + memcpy(p_msg->p_cond_param, p_cond, sizeof(tBTA_DM_BLE_PF_COND_PARAM)); + + p = (UINT8 *)(p_msg->p_cond_param + 1); + + if (cond_type == BTA_DM_BLE_PF_SRVC_DATA_PATTERN || + cond_type == BTA_DM_BLE_PF_MANU_DATA) { + p_msg->p_cond_param->manu_data.p_pattern = p; + p_msg->p_cond_param->manu_data.data_len = p_cond->manu_data.data_len; + memcpy(p_msg->p_cond_param->manu_data.p_pattern, p_cond->manu_data.p_pattern, + p_cond->manu_data.data_len); + p += p_cond->manu_data.data_len; + + if (cond_type == BTA_DM_BLE_PF_MANU_DATA) { + p_msg->p_cond_param->manu_data.company_id_mask = + p_cond->manu_data.company_id_mask; + if ( p_cond->manu_data.p_pattern_mask != NULL) { + p_msg->p_cond_param->manu_data.p_pattern_mask = p; + memcpy(p_msg->p_cond_param->manu_data.p_pattern_mask, + p_cond->manu_data.p_pattern_mask, p_cond->manu_data.data_len); + } + } + } else if (cond_type == BTA_DM_BLE_PF_LOCAL_NAME) { + p_msg->p_cond_param->local_name.p_data = p; + p_msg->p_cond_param->local_name.data_len = + p_cond->local_name.data_len; + memcpy(p_msg->p_cond_param->local_name.p_data, + p_cond->local_name.p_data, p_cond->local_name.data_len); + } else if ((cond_type == BTM_BLE_PF_SRVC_UUID + || cond_type == BTM_BLE_PF_SRVC_SOL_UUID)) { + if (p_cond->srvc_uuid.p_target_addr != NULL) { + p_msg->p_cond_param->srvc_uuid.p_target_addr = (tBLE_BD_ADDR *)(p); + p_msg->p_cond_param->srvc_uuid.p_target_addr->type = + p_cond->srvc_uuid.p_target_addr->type; + memcpy(p_msg->p_cond_param->srvc_uuid.p_target_addr->bda, + p_cond->srvc_uuid.p_target_addr->bda, BD_ADDR_LEN); + p = (UINT8 *)( p_msg->p_cond_param->srvc_uuid.p_target_addr + 1); + } + if (p_cond->srvc_uuid.p_uuid_mask) { + p_msg->p_cond_param->srvc_uuid.p_uuid_mask = (tBTA_DM_BLE_PF_COND_MASK *)p; + memcpy(p_msg->p_cond_param->srvc_uuid.p_uuid_mask, + p_cond->srvc_uuid.p_uuid_mask, sizeof(tBTA_DM_BLE_PF_COND_MASK)); + } + } + } + + bta_sys_sendmsg(p_msg); + } +#else + UNUSED(action); + UNUSED(cond_type); + UNUSED(filt_index); + UNUSED(p_cond); + UNUSED(p_cmpl_cback); + UNUSED(ref_value); +#endif +} + +/******************************************************************************* +** +** Function BTA_DmBleScanFilterSetup +** +** Description This function is called to setup the adv data payload filter param +** +** Parameters p_target: enable the filter condition on a target device; if NULL +** filt_index - Filter index +** p_filt_params -Filter parameters +** ref_value - Reference value +** action - Add, delete or clear +** p_cmpl_back - Command completed callback +** +** Returns void +** +*******************************************************************************/ +void BTA_DmBleScanFilterSetup(UINT8 action, tBTA_DM_BLE_PF_FILT_INDEX filt_index, + tBTA_DM_BLE_PF_FILT_PARAMS *p_filt_params, + tBLE_BD_ADDR *p_target, + tBTA_DM_BLE_PF_PARAM_CBACK *p_cmpl_cback, + tBTA_DM_BLE_REF_VALUE ref_value) +{ +#if BLE_ANDROID_CONTROLLER_SCAN_FILTER == TRUE + tBTA_DM_API_SCAN_FILTER_PARAM_SETUP *p_msg; + APPL_TRACE_API ("BTA_DmBleScanFilterSetup: %d", action); + + UINT16 len = sizeof(tBTA_DM_API_SCAN_FILTER_PARAM_SETUP) + sizeof(tBLE_BD_ADDR); + + if ((p_msg = (tBTA_DM_API_SCAN_FILTER_PARAM_SETUP *) osi_malloc(len)) != NULL) { + memset (p_msg, 0, len); + + p_msg->hdr.event = BTA_DM_API_SCAN_FILTER_SETUP_EVT; + p_msg->action = action; + p_msg->filt_index = filt_index; + if (p_filt_params) { + memcpy(&p_msg->filt_params, p_filt_params, sizeof(tBTA_DM_BLE_PF_FILT_PARAMS)); + } + p_msg->p_filt_param_cback = p_cmpl_cback; + p_msg->ref_value = ref_value; + + if (p_target) { + p_msg->p_target = (tBLE_BD_ADDR *)(p_msg + 1); + memcpy(p_msg->p_target, p_target, sizeof(tBLE_BD_ADDR)); + } + + bta_sys_sendmsg(p_msg); + } +#else + UNUSED(action); + UNUSED(filt_index); + UNUSED(p_filt_params); + UNUSED(p_target); + UNUSED(p_cmpl_cback); + UNUSED(ref_value); +#endif +} + +/******************************************************************************* +** +** Function BTA_DmBleGetEnergyInfo +** +** Description This function is called to obtain the energy info +** +** Parameters p_cmpl_cback - Command complete callback +** +** Returns void +** +*******************************************************************************/ +void BTA_DmBleGetEnergyInfo(tBTA_BLE_ENERGY_INFO_CBACK *p_cmpl_cback) +{ + tBTA_DM_API_ENERGY_INFO *p_msg; + APPL_TRACE_API ("BTA_DmBleGetEnergyInfo"); + + UINT16 len = sizeof(tBTA_DM_API_ENERGY_INFO) + sizeof(tBLE_BD_ADDR); + + if ((p_msg = (tBTA_DM_API_ENERGY_INFO *) osi_malloc(len)) != NULL) { + memset (p_msg, 0, len); + p_msg->hdr.event = BTA_DM_API_BLE_ENERGY_INFO_EVT; + p_msg->p_energy_info_cback = p_cmpl_cback; + bta_sys_sendmsg(p_msg); + } +} + +/******************************************************************************* +** +** Function BTA_DmEnableScanFilter +** +** Description This function is called to enable the adv data payload filter +** +** Parameters action - enable or disable the APCF feature +** p_cmpl_cback - Command completed callback +** ref_value - Reference value +** +** Returns void +** +*******************************************************************************/ +void BTA_DmEnableScanFilter(UINT8 action, tBTA_DM_BLE_PF_STATUS_CBACK *p_cmpl_cback, + tBTA_DM_BLE_REF_VALUE ref_value) +{ +#if BLE_ANDROID_CONTROLLER_SCAN_FILTER == TRUE + tBTA_DM_API_ENABLE_SCAN_FILTER *p_msg; + APPL_TRACE_API ("BTA_DmEnableScanFilter: %d\n", action); + + UINT16 len = sizeof(tBTA_DM_API_ENABLE_SCAN_FILTER) + sizeof(tBLE_BD_ADDR); + + if ((p_msg = (tBTA_DM_API_ENABLE_SCAN_FILTER *) osi_malloc(len)) != NULL) { + memset (p_msg, 0, len); + + p_msg->hdr.event = BTA_DM_API_SCAN_FILTER_ENABLE_EVT; + p_msg->action = action; + p_msg->ref_value = ref_value; + p_msg->p_filt_status_cback = p_cmpl_cback; + + bta_sys_sendmsg(p_msg); + } +#else + UNUSED(action); + UNUSED(p_cmpl_cback); + UNUSED(ref_value); +#endif +} + +/******************************************************************************* +** +** Function BTA_DmBleUpdateConnectionParams +** +** Description Update connection parameters, can only be used when connection is up. +** +** Parameters: bd_addr - BD address of the peer +** min_int - minimum connection interval, [0x0004~ 0x4000] +** max_int - maximum connection interval, [0x0004~ 0x4000] +** latency - slave latency [0 ~ 500] +** timeout - supervision timeout [0x000a ~ 0xc80] +** +** Returns void +** +*******************************************************************************/ +void BTA_DmBleUpdateConnectionParams(BD_ADDR bd_addr, UINT16 min_int, UINT16 max_int, + UINT16 latency, UINT16 timeout) +{ + tBTA_DM_API_UPDATE_CONN_PARAM *p_msg; + + if ((p_msg = (tBTA_DM_API_UPDATE_CONN_PARAM *) osi_malloc(sizeof(tBTA_DM_API_UPDATE_CONN_PARAM))) != NULL) { + memset (p_msg, 0, sizeof(tBTA_DM_API_UPDATE_CONN_PARAM)); + + p_msg->hdr.event = BTA_DM_API_UPDATE_CONN_PARAM_EVT; + bdcpy(p_msg->bd_addr, bd_addr); + p_msg->min_int = min_int; + p_msg->max_int = max_int; + p_msg->latency = latency; + p_msg->timeout = timeout; + bta_sys_sendmsg(p_msg); + } +} +/******************************************************************************* +** +** Function BTA_DmBleDisconnect +** +** Description Disconnect the ble connection, can only be used when connection is up. +** +** Parameters: bd_addr - BD address of the peer +** +** Returns void +** +*******************************************************************************/ +void BTA_DmBleDisconnect(BD_ADDR bd_addr) +{ + tBTA_DM_API_BLE_DISCONNECT *p_msg; + + if ((p_msg = (tBTA_DM_API_BLE_DISCONNECT *) osi_malloc(sizeof(tBTA_DM_API_BLE_DISCONNECT))) != NULL) { + memset (p_msg, 0, sizeof(tBTA_DM_API_BLE_DISCONNECT)); + + p_msg->hdr.event = BTA_DM_API_BLE_DISCONNECT_EVT; + bdcpy(p_msg->remote_bda, bd_addr); + + bta_sys_sendmsg(p_msg); + } +} +/******************************************************************************* +** +** Function BTA_DmBleSetDataLength +** +** Description This function is to set maximum LE data packet size +** +** Returns void +** +** +*******************************************************************************/ +void BTA_DmBleSetDataLength(BD_ADDR remote_device, UINT16 tx_data_length, tBTA_SET_PKT_DATA_LENGTH_CBACK *p_set_pkt_data_cback) +{ + tBTA_DM_API_BLE_SET_DATA_LENGTH *p_msg; + + if ((p_msg = (tBTA_DM_API_BLE_SET_DATA_LENGTH *)osi_malloc(sizeof(tBTA_DM_API_BLE_SET_DATA_LENGTH))) + != NULL) { + bdcpy(p_msg->remote_bda, remote_device); + p_msg->hdr.event = BTA_DM_API_SET_DATA_LENGTH_EVT; + p_msg->tx_data_length = tx_data_length; + p_msg->p_set_pkt_data_cback = p_set_pkt_data_cback; + + bta_sys_sendmsg(p_msg); + } +} + +void BTA_DmBleDtmTxStart(uint8_t tx_channel, uint8_t len_of_data, uint8_t pkt_payload, tBTA_DTM_CMD_CMPL_CBACK *p_dtm_cmpl_cback) +{ + tBTA_DM_API_BLE_DTM_TX_START *p_msg; + + if ((p_msg = (tBTA_DM_API_BLE_DTM_TX_START *)osi_malloc(sizeof(tBTA_DM_API_BLE_DTM_TX_START))) + != NULL) { + p_msg->hdr.event = BTA_DM_API_DTM_TX_START_EVT; + p_msg->tx_channel = tx_channel; + p_msg->len_of_data = len_of_data; + p_msg->pkt_payload = pkt_payload; + p_msg->p_dtm_cmpl_cback = p_dtm_cmpl_cback; + + bta_sys_sendmsg(p_msg); + } +} + +void BTA_DmBleDtmRxStart(uint8_t rx_channel, tBTA_DTM_CMD_CMPL_CBACK *p_dtm_cmpl_cback) +{ + tBTA_DM_API_BLE_DTM_RX_START *p_msg; + + if ((p_msg = (tBTA_DM_API_BLE_DTM_RX_START *)osi_malloc(sizeof(tBTA_DM_API_BLE_DTM_RX_START))) + != NULL) { + p_msg->hdr.event = BTA_DM_API_DTM_RX_START_EVT; + p_msg->rx_channel= rx_channel; + p_msg->p_dtm_cmpl_cback = p_dtm_cmpl_cback; + + bta_sys_sendmsg(p_msg); + } +} + +void BTA_DmBleDtmStop(tBTA_DTM_CMD_CMPL_CBACK *p_dtm_cmpl_cback) +{ + tBTA_DM_API_BLE_DTM_STOP *p_msg; + + if ((p_msg = (tBTA_DM_API_BLE_DTM_STOP *)osi_malloc(sizeof(tBTA_DM_API_BLE_DTM_STOP))) + != NULL) { + p_msg->hdr.event = BTA_DM_API_DTM_STOP_EVT; + p_msg->p_dtm_cmpl_cback = p_dtm_cmpl_cback; + + bta_sys_sendmsg(p_msg); + } +} + +#endif + +/******************************************************************************* +** +** Function BTA_DmSetEncryption +** +** Description This function is called to ensure that connection is +** encrypted. Should be called only on an open connection. +** Typically only needed for connections that first want to +** bring up unencrypted links, then later encrypt them. +** +** Parameters: bd_addr - Address of the peer device +** transport - transport of the link to be encruypted +** p_callback - Pointer to callback function to indicat the +** link encryption status +** sec_act - This is the security action to indicate +** what kind of BLE security level is required for +** the BLE link if the BLE is supported +** Note: This parameter is ignored for the BR/EDR link +** or the BLE is not supported +** +** Returns void +** +*******************************************************************************/ +#if (SMP_INCLUDED == TRUE) +void BTA_DmSetEncryption(BD_ADDR bd_addr, tBTA_TRANSPORT transport, tBTA_DM_ENCRYPT_CBACK *p_callback, + tBTA_DM_BLE_SEC_ACT sec_act) +{ + tBTA_DM_API_SET_ENCRYPTION *p_msg; + + APPL_TRACE_API("BTA_DmSetEncryption"); //todo + if ((p_msg = (tBTA_DM_API_SET_ENCRYPTION *) osi_malloc(sizeof(tBTA_DM_API_SET_ENCRYPTION))) != NULL) { + memset(p_msg, 0, sizeof(tBTA_DM_API_SET_ENCRYPTION)); + + p_msg->hdr.event = BTA_DM_API_SET_ENCRYPTION_EVT; + + memcpy(p_msg->bd_addr, bd_addr, BD_ADDR_LEN); + p_msg->transport = transport; + p_msg->p_callback = p_callback; + p_msg->sec_act = sec_act; + + bta_sys_sendmsg(p_msg); + } +} +#endif ///SMP_INCLUDED == TRUE + +/******************************************************************************* +** +** Function BTA_DmCloseACL +** +** Description This function force to close an ACL connection and remove the +** device from the security database list of known devices. +** +** Parameters: bd_addr - Address of the peer device +** remove_dev - remove device or not after link down +** +** Returns void +** +*******************************************************************************/ +void BTA_DmCloseACL(BD_ADDR bd_addr, BOOLEAN remove_dev, tBTA_TRANSPORT transport) +{ + tBTA_DM_API_REMOVE_ACL *p_msg; + + APPL_TRACE_API("BTA_DmCloseACL"); + + if ((p_msg = (tBTA_DM_API_REMOVE_ACL *) osi_malloc(sizeof(tBTA_DM_API_REMOVE_ACL))) != NULL) { + memset(p_msg, 0, sizeof(tBTA_DM_API_REMOVE_ACL)); + + p_msg->hdr.event = BTA_DM_API_REMOVE_ACL_EVT; + + memcpy(p_msg->bd_addr, bd_addr, BD_ADDR_LEN); + p_msg->remove_dev = remove_dev; + p_msg->transport = transport; + + bta_sys_sendmsg(p_msg); + } +} + +#if BLE_INCLUDED == TRUE +/******************************************************************************* +** +** Function BTA_DmBleObserve +** +** Description This procedure keep the device listening for advertising +** events from a broadcast device. +** +** Parameters start: start or stop observe. +** +** Returns void + +** +** Returns void. +** +*******************************************************************************/ +extern void BTA_DmBleObserve(BOOLEAN start, UINT32 duration, + tBTA_DM_SEARCH_CBACK *p_results_cb, + tBTA_START_STOP_SCAN_CMPL_CBACK *p_start_stop_scan_cb) +{ + tBTA_DM_API_BLE_OBSERVE *p_msg; + + APPL_TRACE_API("BTA_DmBleObserve:start = %d ", start); + + if ((p_msg = (tBTA_DM_API_BLE_OBSERVE *) osi_malloc(sizeof(tBTA_DM_API_BLE_OBSERVE))) != NULL) { + memset(p_msg, 0, sizeof(tBTA_DM_API_BLE_OBSERVE)); + + p_msg->hdr.event = BTA_DM_API_BLE_OBSERVE_EVT; + p_msg->start = start; + p_msg->duration = duration; + p_msg->p_cback = p_results_cb; + if (start){ + p_msg->p_start_scan_cback = p_start_stop_scan_cb; + } + else { + p_msg->p_stop_scan_cback = p_start_stop_scan_cb; + } + + bta_sys_sendmsg(p_msg); + } +} + +/******************************************************************************* +** +** Function BTA_DmBleScan +** +** Description This procedure keep the device listening for advertising +** events from a broadcast device. +** +** Parameters start: start or stop scan. +** +** Returns void + +** +** Returns void. +** +*******************************************************************************/ +extern void BTA_DmBleScan(BOOLEAN start, UINT32 duration, + tBTA_DM_SEARCH_CBACK *p_results_cb, + tBTA_START_STOP_SCAN_CMPL_CBACK *p_start_stop_scan_cb) +{ + tBTA_DM_API_BLE_SCAN *p_msg; + + APPL_TRACE_API("BTA_DmBleScan:start = %d ", start); + + if ((p_msg = (tBTA_DM_API_BLE_SCAN *) osi_malloc(sizeof(tBTA_DM_API_BLE_SCAN))) != NULL) { + memset(p_msg, 0, sizeof(tBTA_DM_API_BLE_SCAN)); + + p_msg->hdr.event = BTA_DM_API_BLE_SCAN_EVT; + p_msg->start = start; + p_msg->duration = duration; + p_msg->p_cback = p_results_cb; + if (start){ + p_msg->p_start_scan_cback = p_start_stop_scan_cb; + } + else { + p_msg->p_stop_scan_cback = p_start_stop_scan_cb; + } + + bta_sys_sendmsg(p_msg); + } +} + +/******************************************************************************* +** +** Function BTA_DmBleStopAdvertising +** +** Description This function set the random address for the APP +** +** Parameters void +** +** Returns void +** +** +*******************************************************************************/ +extern void BTA_DmBleStopAdvertising(void) +{ + BT_HDR *p_msg; + + APPL_TRACE_API("BTA_DmBleStopAdvertising\n"); + + if ((p_msg = (BT_HDR *) osi_malloc(sizeof(BT_HDR))) != NULL) { + memset(p_msg, 0, sizeof(BT_HDR)); + p_msg->event = BTA_DM_API_BLE_STOP_ADV_EVT; + bta_sys_sendmsg(p_msg); + } +} + + +/******************************************************************************* +** +** Function BTA_DmSetRandAddress +** +** Description This function set the random address for the APP +** +** Parameters rand_addr: the random address whith should be setting +** p_set_rand_addr_cback: complete callback +** Returns void +** +** +*******************************************************************************/ +extern void BTA_DmSetRandAddress(BD_ADDR rand_addr, tBTA_SET_RAND_ADDR_CBACK *p_set_rand_addr_cback) +{ + tBTA_DM_APT_SET_DEV_ADDR *p_msg; + APPL_TRACE_API("set the random address "); + if ((p_msg = (tBTA_DM_APT_SET_DEV_ADDR *) osi_malloc(sizeof(tBTA_DM_APT_SET_DEV_ADDR))) != NULL) { + memset(p_msg, 0, sizeof(tBTA_DM_APT_SET_DEV_ADDR)); + memcpy(p_msg->address, rand_addr, BD_ADDR_LEN); + p_msg->hdr.event = BTA_DM_API_SET_RAND_ADDR_EVT; + p_msg->addr_type = BLE_ADDR_RANDOM; + p_msg->p_set_rand_addr_cback = p_set_rand_addr_cback; + //start sent the msg to the bta system control moudle + bta_sys_sendmsg(p_msg); + } +} + +void BTA_DmClearRandAddress(void) +{ + tBTA_DM_APT_CLEAR_ADDR *p_msg; + if ((p_msg = (tBTA_DM_APT_CLEAR_ADDR *) osi_malloc(sizeof(tBTA_DM_APT_CLEAR_ADDR))) != NULL) { + memset(p_msg, 0, sizeof(tBTA_DM_APT_CLEAR_ADDR)); + p_msg->hdr.event = BTA_DM_API_CLEAR_RAND_ADDR_EVT; + bta_sys_sendmsg(p_msg); + } +} + +/******************************************************************************* +** +** Function BTA_VendorInit +** +** Description This function initializes vendor specific +** +** Returns void +** +*******************************************************************************/ +void BTA_VendorInit (void) +{ + APPL_TRACE_API("BTA_VendorInit"); +} + +/******************************************************************************* +** +** Function BTA_VendorCleanup +** +** Description This function frees up Broadcom specific VS specific dynamic memory +** +** Returns void +** +*******************************************************************************/ +void BTA_VendorCleanup (void) +{ + tBTM_BLE_VSC_CB cmn_ble_vsc_cb; + BTM_BleGetVendorCapabilities(&cmn_ble_vsc_cb); + +#if (BLE_INCLUDED == TRUE && BLE_ANDROID_CONTROLLER_SCAN_FILTER == TRUE) + btm_ble_adv_filter_cleanup(); // when BLE_VND_INCLUDED is false, this function will be ignore, so move it out of "if" + +#if 0 //by TH, comment out temporarily + if (cmn_ble_vsc_cb.max_filter > 0) { + btm_ble_adv_filter_cleanup(); +#if BLE_PRIVACY_SPT == TRUE + btm_ble_resolving_list_cleanup (); +#endif + } +#endif + + if (cmn_ble_vsc_cb.tot_scan_results_strg > 0) { + btm_ble_batchscan_cleanup(); + } +#endif + + btm_ble_multi_adv_cleanup(); +} +#if (BLE_50_FEATURE_SUPPORT == TRUE) +void BTA_DmBleGapReadPHY(BD_ADDR addr) +{ + tBTA_DM_API_READ_PHY *p_msg; + APPL_TRACE_API("%s, read phy.", __func__); + if ((p_msg = (tBTA_DM_API_READ_PHY *) osi_malloc(sizeof(tBTA_DM_API_READ_PHY))) != NULL) { + memset(p_msg, 0, sizeof(tBTA_DM_API_READ_PHY)); + p_msg->hdr.event = BTA_DM_API_READ_PHY_EVT; + memcpy(p_msg->bd_addr, addr, BD_ADDR_LEN); + //start sent the msg to the bta system control moudle + bta_sys_sendmsg(p_msg); + } else { + APPL_TRACE_ERROR("%s malloc failed", __func__); + } + +} + +void BTA_DmBleGapSetPreferedDefaultPHY(tBTA_DM_BLE_GAP_PHY_MASK tx_phy_mask, + tBTA_DM_BLE_GAP_PHY_MASK rx_phy_mask) +{ + tBTA_DM_API_SET_PER_DEF_PHY *p_msg; + APPL_TRACE_API("%s, Set prefered default phy.", __func__); + if ((p_msg = (tBTA_DM_API_SET_PER_DEF_PHY *) osi_malloc(sizeof(tBTA_DM_API_SET_PER_DEF_PHY))) != NULL) { + memset(p_msg, 0, sizeof(tBTA_DM_API_SET_PER_DEF_PHY)); + p_msg->hdr.event = BTA_DM_API_SET_PER_DEF_PHY_EVT; + p_msg->tx_phy_mask = tx_phy_mask; + p_msg->rx_phy_mask = rx_phy_mask; + //start sent the msg to the bta system control moudle + bta_sys_sendmsg(p_msg); + } else { + APPL_TRACE_ERROR("%s malloc failed", __func__); + } + +} + +void BTA_DmBleGapSetPreferedPHY(BD_ADDR addr, + UINT8 all_phys, + tBTA_DM_BLE_GAP_PHY_MASK tx_phy_mask, + tBTA_DM_BLE_GAP_PHY_MASK rx_phy_mask, + UINT16 phy_options) +{ + tBTA_DM_API_SET_PER_PHY *p_msg; + APPL_TRACE_API("%s, Set prefered phy.", __func__); + if ((p_msg = (tBTA_DM_API_SET_PER_PHY *) osi_malloc(sizeof(tBTA_DM_API_SET_PER_PHY))) != NULL) { + memset(p_msg, 0, sizeof(tBTA_DM_API_SET_PER_PHY)); + p_msg->hdr.event = BTA_DM_API_SET_PER_PHY_EVT; + memcpy(p_msg->bd_addr, addr, BD_ADDR_LEN); + p_msg->all_phys = all_phys; + p_msg->tx_phy_mask = tx_phy_mask; + p_msg->rx_phy_mask = rx_phy_mask; + p_msg->phy_options = phy_options; + //start sent the msg to the bta system control moudle + bta_sys_sendmsg(p_msg); + } else { + APPL_TRACE_ERROR("%s malloc failed", __func__); + } +} + +void BTA_DmBleGapExtAdvSetRandaddr(UINT16 instance, BD_ADDR addr) +{ + tBTA_DM_API_EXT_ADV_SET_RAND_ADDR *p_msg; + APPL_TRACE_API("%s, Set extended ADV parameters.", __func__); + if ((p_msg = (tBTA_DM_API_EXT_ADV_SET_RAND_ADDR *) osi_malloc(sizeof(tBTA_DM_API_EXT_ADV_SET_RAND_ADDR))) != NULL) { + memset(p_msg, 0, sizeof(tBTA_DM_API_EXT_ADV_SET_RAND_ADDR)); + p_msg->hdr.event = BTA_DM_API_SET_EXT_ADV_RAND_ADDR_EVT; + p_msg->instance = instance; + memcpy(&p_msg->rand_addr, addr, BD_ADDR_LEN); + //start sent the msg to the bta system control moudle + bta_sys_sendmsg(p_msg); + } else { + APPL_TRACE_ERROR("%s malloc failed", __func__); + } + +} + +void BTA_DmBleGapExtAdvSetParams(UINT16 instance, + const tBTA_DM_BLE_GAP_EXT_ADV_PARAMS *params) +{ + tBTA_DM_API_EXT_ADV_SET_PARAMS *p_msg; + APPL_TRACE_API("%s, Set extended ADV parameters.", __func__); + if ((p_msg = (tBTA_DM_API_EXT_ADV_SET_PARAMS *) osi_malloc(sizeof(tBTA_DM_API_EXT_ADV_SET_PARAMS))) != NULL) { + memset(p_msg, 0, sizeof(tBTA_DM_API_EXT_ADV_SET_PARAMS)); + p_msg->hdr.event = BTA_DM_API_SET_EXT_ADV_PARAMS_EVT; + p_msg->instance = instance; + memcpy(&p_msg->params, params, sizeof(tBTA_DM_BLE_GAP_EXT_ADV_PARAMS)); + //start sent the msg to the bta system control moudle + bta_sys_sendmsg(p_msg); + } else { + APPL_TRACE_ERROR("%s malloc failed", __func__); + } + +} + +void BTA_DmBleGapConfigExtAdvDataRaw(BOOLEAN is_scan_rsp, UINT8 instance, UINT16 length, + const UINT8 *data) +{ + tBTA_DM_API_CFG_EXT_ADV_DATA *p_msg; + APPL_TRACE_API("%s, Config extended %s data.", __func__, is_scan_rsp ? "Scan rsp" : "Adv"); + if ((p_msg = (tBTA_DM_API_CFG_EXT_ADV_DATA *) osi_malloc(sizeof(tBTA_DM_API_CFG_EXT_ADV_DATA) + length)) != NULL) { + memset(p_msg, 0, sizeof(tBTA_DM_API_CFG_EXT_ADV_DATA) + length); + p_msg->hdr.event = BTA_DM_API_CFG_ADV_DATA_RAW_EVT; + p_msg->is_scan_rsp = is_scan_rsp; + p_msg->instance = instance; + p_msg->length = length; + p_msg->data = length != 0 ? (UINT8 *)(p_msg + 1) : NULL; + if (data) { + memcpy(p_msg->data, data, length); + } + //start sent the msg to the bta system control moudle + bta_sys_sendmsg(p_msg); + } else { + APPL_TRACE_ERROR("%s malloc failed", __func__); + } +} + +void BTA_DmBleGapExtAdvEnable(BOOLEAN enable, UINT8 num, tBTA_DM_BLE_EXT_ADV *ext_adv) +{ + tBTA_DM_API_BLE_EXT_ADV *p_msg; + APPL_TRACE_API("%s, Start extended ADV", __func__); + if ((p_msg = (tBTA_DM_API_BLE_EXT_ADV *) osi_malloc(sizeof(tBTA_DM_API_BLE_EXT_ADV) + sizeof(tBTA_DM_BLE_EXT_ADV)*num)) != NULL) { + memset(p_msg, 0, sizeof(tBTA_DM_API_BLE_EXT_ADV) + sizeof(tBTA_DM_BLE_EXT_ADV)*num); + p_msg->hdr.event = BTA_DM_API_EXT_ADV_ENABLE_EVT; + p_msg->enable = enable; + p_msg->num = num; + p_msg->ext_adv = (tBTA_DM_BLE_EXT_ADV *)(p_msg + 1); + if (ext_adv) { + memcpy(p_msg->ext_adv, ext_adv, sizeof(tBTA_DM_BLE_EXT_ADV)*num); + } + //start sent the msg to the bta system control moudle + bta_sys_sendmsg(p_msg); + } else { + APPL_TRACE_ERROR("%s malloc failed", __func__); + } +} + +void BTA_DmBleGapExtAdvSetRemove(UINT8 instance) +{ + tBTA_DM_API_BLE_EXT_ADV_SET_REMOVE *p_msg; + APPL_TRACE_API("%s, Remove extended ADV", __func__); + if ((p_msg = (tBTA_DM_API_BLE_EXT_ADV_SET_REMOVE *) osi_malloc(sizeof(tBTA_DM_API_BLE_EXT_ADV_SET_REMOVE))) != NULL) { + memset(p_msg, 0, sizeof(tBTA_DM_API_BLE_EXT_ADV_SET_REMOVE)); + p_msg->hdr.event = BTA_DM_API_EXT_ADV_SET_REMOVE_EVT; + p_msg->instance = instance; + //start sent the msg to the bta system control moudle + bta_sys_sendmsg(p_msg); + } else { + APPL_TRACE_ERROR("%s malloc failed", __func__); + } +} + +void BTA_DmBleGapExtAdvSetClear(void) +{ + tBTA_DM_API_BLE_EXT_ADV_SET_CLEAR *p_msg; + APPL_TRACE_API("%s, Clear extended ADV", __func__); + if ((p_msg = (tBTA_DM_API_BLE_EXT_ADV_SET_CLEAR *) osi_malloc(sizeof(tBTA_DM_API_BLE_EXT_ADV_SET_CLEAR))) != NULL) { + memset(p_msg, 0, sizeof(tBTA_DM_API_BLE_EXT_ADV_SET_CLEAR)); + p_msg->hdr.event = BTA_DM_API_EXT_ADV_SET_CLEAR_EVT; + //start sent the msg to the bta system control moudle + bta_sys_sendmsg(p_msg); + } else { + APPL_TRACE_ERROR("%s malloc failed", __func__); + } +} + +void BTA_DmBleGapPeriodicAdvSetParams(UINT8 instance, + tBTA_DM_BLE_Periodic_Adv_Params *params) +{ + tBTA_DM_API_BLE_PERIODIC_ADV_SET_PARAMS *p_msg; + APPL_TRACE_API("%s, Periodic ADV set parameters.", __func__); + if ((p_msg = (tBTA_DM_API_BLE_PERIODIC_ADV_SET_PARAMS *) osi_malloc(sizeof(tBTA_DM_API_BLE_PERIODIC_ADV_SET_PARAMS))) != NULL) { + memset(p_msg, 0, sizeof(tBTA_DM_API_BLE_PERIODIC_ADV_SET_PARAMS)); + p_msg->hdr.event = BTA_DM_API_PERIODIC_ADV_SET_PARAMS_EVT; + p_msg->instance = instance; + memcpy(&p_msg->params, params, sizeof(tBTA_DM_BLE_Periodic_Adv_Params)); + //start sent the msg to the bta system control moudle + bta_sys_sendmsg(p_msg); + } else { + APPL_TRACE_ERROR("%s malloc failed", __func__); + } + +} + +void BTA_DmBleGapPeriodicAdvCfgDataRaw(UINT8 instance, UINT16 length, + const UINT8 *data,bool only_update_did) +{ + tBTA_DM_API_CFG_PERIODIC_ADV_DATA *p_msg; + APPL_TRACE_API("%s, Periodic ADV config data raw.", __func__); + if ((p_msg = (tBTA_DM_API_CFG_PERIODIC_ADV_DATA *) osi_malloc(sizeof(tBTA_DM_API_CFG_PERIODIC_ADV_DATA) + length)) != NULL) { + memset(p_msg, 0, sizeof(tBTA_DM_API_CFG_PERIODIC_ADV_DATA) + length); + p_msg->hdr.event = BTA_DM_API_PERIODIC_ADV_CFG_DATA_EVT; + p_msg->instance = instance; + p_msg->length = length; + p_msg->data = (UINT8 *)(p_msg + 1); + memcpy(p_msg->data, data, length); + p_msg->data = length != 0 ? (UINT8 *)(p_msg + 1) : NULL; + p_msg->only_update_did = only_update_did; + //start sent the msg to the bta system control moudle + bta_sys_sendmsg(p_msg); + } else { + APPL_TRACE_ERROR("%s malloc failed", __func__); + } + +} + +void BTA_DmBleGapPeriodicAdvEnable(UINT8 enable, UINT8 instance) +{ + tBTA_DM_API_ENABLE_PERIODIC_ADV *p_msg; + APPL_TRACE_API("%s, Periodic ADV %s.", __func__, enable ? "start" : "stop"); + if ((p_msg = (tBTA_DM_API_ENABLE_PERIODIC_ADV *) osi_malloc(sizeof(tBTA_DM_API_ENABLE_PERIODIC_ADV))) != NULL) { + memset(p_msg, 0, sizeof(tBTA_DM_API_ENABLE_PERIODIC_ADV)); + p_msg->hdr.event = BTA_DM_API_PERIODIC_ADV_ENABLE_EVT; + p_msg->instance = instance; + p_msg->enable = enable; + //start sent the msg to the bta system control moudle + bta_sys_sendmsg(p_msg); + } else { + APPL_TRACE_ERROR("%s malloc failed", __func__); + } + +} + +void BTA_DmBleGapPeriodicAdvCreateSync(tBTA_DM_BLE_Periodic_Sync_Params *params) +{ + tBTA_DM_API_PERIODIC_ADV_SYNC *p_msg; + APPL_TRACE_API("%s, Periodic ADV create sync.", __func__); + if ((p_msg = (tBTA_DM_API_PERIODIC_ADV_SYNC *) osi_malloc(sizeof(tBTA_DM_API_PERIODIC_ADV_SYNC))) != NULL) { + memset(p_msg, 0, sizeof(tBTA_DM_API_PERIODIC_ADV_SYNC)); + p_msg->hdr.event = BTA_DM_API_PERIODIC_ADV_SYNC_EVT; + memcpy(&p_msg->params, params, sizeof(tBTA_DM_BLE_Periodic_Sync_Params)); + //start sent the msg to the bta system control moudle + bta_sys_sendmsg(p_msg); + } else { + APPL_TRACE_ERROR("%s malloc failed", __func__); + } + +} + +void BTA_DmBleGapPeriodicAdvSyncCancel(void) +{ + tBTA_DM_API_PERIODIC_ADV_SYNC_CANCEL *p_msg; + APPL_TRACE_API("%s, Periodic ADV sync cancel.", __func__); + if ((p_msg = (tBTA_DM_API_PERIODIC_ADV_SYNC_CANCEL *) osi_malloc(sizeof(tBTA_DM_API_PERIODIC_ADV_SYNC_CANCEL))) != NULL) { + memset(p_msg, 0, sizeof(tBTA_DM_API_PERIODIC_ADV_SYNC_CANCEL)); + p_msg->hdr.event = BTA_DM_API_PERIODIC_ADV_SYNC_CANCEL_EVT; + //start sent the msg to the bta system control moudle + bta_sys_sendmsg(p_msg); + } else { + APPL_TRACE_ERROR("%s malloc failed", __func__); + } + +} + +void BTA_DmBleGapPeriodicAdvSyncTerm(UINT16 sync_handle) +{ + tBTA_DM_API_PERIODIC_ADV_SYNC_TERM *p_msg; + APPL_TRACE_API("%s, Periodic ADV sync terminat.", __func__); + if ((p_msg = (tBTA_DM_API_PERIODIC_ADV_SYNC_TERM *) osi_malloc(sizeof(tBTA_DM_API_PERIODIC_ADV_SYNC_TERM))) != NULL) { + memset(p_msg, 0, sizeof(tBTA_DM_API_PERIODIC_ADV_SYNC_TERM)); + p_msg->hdr.event = BTA_DM_API_PERIODIC_ADV_SYNC_TERMINATE_EVT; + p_msg->sync_handle = sync_handle; + //start sent the msg to the bta system control moudle + bta_sys_sendmsg(p_msg); + } else { + APPL_TRACE_ERROR("%s malloc failed", __func__); + } + +} + +void BTA_DmBleGapPeriodicAdvAddDevToList(tBLE_ADDR_TYPE addr_type, + BD_ADDR addr, + UINT16 sid) +{ + tBTA_DM_API_PERIODIC_ADV_ADD_DEV_TO_LIST *p_msg; + APPL_TRACE_API("%s, Periodic ADV add device to list.", __func__); + if ((p_msg = (tBTA_DM_API_PERIODIC_ADV_ADD_DEV_TO_LIST *) osi_malloc(sizeof(tBTA_DM_API_PERIODIC_ADV_ADD_DEV_TO_LIST))) != NULL) { + memset(p_msg, 0, sizeof(tBTA_DM_API_PERIODIC_ADV_ADD_DEV_TO_LIST)); + p_msg->hdr.event = BTA_DM_API_PERIODIC_ADV_ADD_DEV_TO_LSIT_EVT; + p_msg->addr_type = addr_type; + p_msg->sid = sid; + memcpy(p_msg->addr, addr, sizeof(BD_ADDR)); + //start sent the msg to the bta system control moudle + bta_sys_sendmsg(p_msg); + } else { + APPL_TRACE_ERROR("%s malloc failed", __func__); + } + +} + +void BTA_DmBleGapPeriodicAdvRemoveDevFromList(tBLE_ADDR_TYPE addr_type, + BD_ADDR addr, + UINT16 sid) +{ + tBTA_DM_API_PERIODIC_ADV_REMOVE_DEV_FROM_LIST *p_msg; + APPL_TRACE_API("%s, Periodic ADV remove device from list.", __func__); + if ((p_msg = (tBTA_DM_API_PERIODIC_ADV_REMOVE_DEV_FROM_LIST *) osi_malloc(sizeof(tBTA_DM_API_PERIODIC_ADV_REMOVE_DEV_FROM_LIST))) != NULL) { + memset(p_msg, 0, sizeof(tBTA_DM_API_PERIODIC_ADV_REMOVE_DEV_FROM_LIST)); + p_msg->hdr.event = BTA_DM_API_PERIODIC_ADV_REMOVE_DEV_FROM_LSIT_EVT; + p_msg->addr_type = addr_type; + p_msg->sid = sid; + memcpy(p_msg->addr, addr, sizeof(BD_ADDR)); + //start sent the msg to the bta system control moudle + bta_sys_sendmsg(p_msg); + } else { + APPL_TRACE_ERROR("%s malloc failed", __func__); + } + +} + +void BTA_DmBleGapPeriodicAdvClearDev(void) +{ + tBTA_DM_API_PERIODIC_ADV_DEV_CLEAR *p_msg; + APPL_TRACE_API("%s, Periodic ADV clear device from list.", __func__); + if ((p_msg = (tBTA_DM_API_PERIODIC_ADV_DEV_CLEAR *) osi_malloc(sizeof(tBTA_DM_API_PERIODIC_ADV_DEV_CLEAR))) != NULL) { + memset(p_msg, 0, sizeof(tBTA_DM_API_PERIODIC_ADV_DEV_CLEAR)); + p_msg->hdr.event = BTA_DM_API_PERIODIC_ADV_CLEAR_DEV_EVT; + //start sent the msg to the bta system control moudle + bta_sys_sendmsg(p_msg); + } else { + APPL_TRACE_ERROR("%s malloc failed", __func__); + } + +} + +void BTA_DmBleGapSetExtScanParams(tBTA_DM_BLE_EXT_SCAN_PARAMS *params) +{ + tBTA_DM_API_SET_EXT_SCAN_PARAMS *p_msg; + APPL_TRACE_API("%s, Set extended scan parameters.", __func__); + if ((p_msg = (tBTA_DM_API_SET_EXT_SCAN_PARAMS *) osi_malloc(sizeof(tBTA_DM_API_SET_EXT_SCAN_PARAMS))) != NULL) { + memset(p_msg, 0, sizeof(tBTA_DM_API_SET_EXT_SCAN_PARAMS)); + p_msg->hdr.event = BTA_DM_API_SET_EXT_SCAN_PARAMS_EVT; + memcpy(&p_msg->params, params, sizeof(tBTA_DM_BLE_EXT_SCAN_PARAMS)); + //start sent the msg to the bta system control moudle + bta_sys_sendmsg(p_msg); + } else { + APPL_TRACE_ERROR("%s malloc failed", __func__); + } + +} + +void BTA_DmBleGapExtScan(BOOLEAN start, UINT32 duration, UINT16 period) +{ + tBTA_DM_API_EXT_SCAN *p_msg; + APPL_TRACE_API("%s, %s extended scan.", __func__, start ? "Start" : "Stop"); + if ((p_msg = (tBTA_DM_API_EXT_SCAN *) osi_malloc(sizeof(tBTA_DM_API_EXT_SCAN))) != NULL) { + memset(p_msg, 0, sizeof(tBTA_DM_API_EXT_SCAN)); + p_msg->hdr.event = BTA_DM_API_START_EXT_SCAN_EVT; + p_msg->start = start; + p_msg->duration = duration; + p_msg->period = period; + //start sent the msg to the bta system control moudle + bta_sys_sendmsg(p_msg); + } else { + APPL_TRACE_ERROR("%s malloc failed", __func__); + } + +} + +void BTA_DmBleGapPreferExtConnectParamsSet(BD_ADDR bd_addr, + UINT8 phy_mask, + const tBTA_DM_BLE_CONN_PARAMS *phy_1m_conn_params, + const tBTA_DM_BLE_CONN_PARAMS *phy_2m_conn_params, + const tBTA_DM_BLE_CONN_PARAMS *phy_coded_conn_params) +{ + tBTA_DM_API_SET_PER_EXT_CONN_PARAMS *p_msg; + APPL_TRACE_API("%s, Set prefer extended connection parameters.", __func__); + if ((p_msg = (tBTA_DM_API_SET_PER_EXT_CONN_PARAMS *) osi_malloc(sizeof(tBTA_DM_API_SET_PER_EXT_CONN_PARAMS))) != NULL) { + memset(p_msg, 0, sizeof(tBTA_DM_API_SET_PER_EXT_CONN_PARAMS)); + p_msg->hdr.event = BTA_DM_API_SET_PERF_EXT_CONN_PARAMS_EVT; + p_msg->phy_mask = phy_mask; + + memcpy(p_msg->bd_addr, bd_addr, sizeof(BD_ADDR)); + + if (phy_1m_conn_params) { + memcpy(&p_msg->phy_1m_conn_params, phy_1m_conn_params, sizeof(tBTA_DM_BLE_CONN_PARAMS)); + } + + if (phy_2m_conn_params) { + memcpy(&p_msg->phy_2m_conn_params, phy_2m_conn_params, sizeof(tBTA_DM_BLE_CONN_PARAMS)); + } + + if (phy_coded_conn_params) { + memcpy(&p_msg->phy_coded_conn_params, phy_coded_conn_params, sizeof(tBTA_DM_BLE_CONN_PARAMS)); + } + //start sent the msg to the bta system control moudle + bta_sys_sendmsg(p_msg); + } else { + APPL_TRACE_ERROR("%s malloc failed", __func__); + } + + +} + +void BTA_DmBleGapExtConnect(tBLE_ADDR_TYPE own_addr_type, const BD_ADDR peer_addr) +{ + tBTA_DM_API_EXT_CONN *p_msg; + APPL_TRACE_API("%s, Start Extended connect.", __func__); + APPL_TRACE_API("%s, Set prefer extended connection parameters.", __func__); + if ((p_msg = (tBTA_DM_API_EXT_CONN *) osi_malloc(sizeof(tBTA_DM_API_EXT_CONN))) != NULL) { + memset(p_msg, 0, sizeof(tBTA_DM_API_EXT_CONN)); + p_msg->hdr.event = BTA_DM_API_EXT_CONN_EVT; + p_msg->own_addr_type = own_addr_type; + memcpy(p_msg->peer_addr, peer_addr, sizeof(BD_ADDR)); + //start sent the msg to the bta system control moudle + bta_sys_sendmsg(p_msg); + } else { + APPL_TRACE_ERROR("%s malloc failed", __func__); + } + +} + +void BTA_DmBleDtmEnhTxStart(uint8_t tx_channel, uint8_t len_of_data, uint8_t pkt_payload, uint8_t phy, tBTA_DTM_CMD_CMPL_CBACK *p_dtm_cmpl_cback) +{ + tBTA_DM_API_BLE_DTM_ENH_TX_START *p_msg; + + if ((p_msg = (tBTA_DM_API_BLE_DTM_ENH_TX_START *)osi_malloc(sizeof(tBTA_DM_API_BLE_DTM_ENH_TX_START))) + != NULL) { + p_msg->hdr.event = BTA_DM_API_DTM_ENH_TX_START_EVT; + p_msg->tx_channel = tx_channel; + p_msg->len_of_data = len_of_data; + p_msg->pkt_payload = pkt_payload; + p_msg->phy = phy; + p_msg->p_dtm_cmpl_cback = p_dtm_cmpl_cback; + + bta_sys_sendmsg(p_msg); + } +} + +void BTA_DmBleDtmEnhRxStart(uint8_t rx_channel, uint8_t phy, uint8_t modulation_index, tBTA_DTM_CMD_CMPL_CBACK *p_dtm_cmpl_cback) +{ + tBTA_DM_API_BLE_DTM_ENH_RX_START *p_msg; + + if ((p_msg = (tBTA_DM_API_BLE_DTM_ENH_RX_START *)osi_malloc(sizeof(tBTA_DM_API_BLE_DTM_ENH_RX_START))) + != NULL) { + p_msg->hdr.event = BTA_DM_API_DTM_ENH_RX_START_EVT; + p_msg->rx_channel= rx_channel; + p_msg->phy = phy; + p_msg->modulation_index = modulation_index; + p_msg->p_dtm_cmpl_cback = p_dtm_cmpl_cback; + + bta_sys_sendmsg(p_msg); + } +} + +#endif // #if (BLE_50_FEATURE_SUPPORT == TRUE) + +#if (BLE_FEAT_PERIODIC_ADV_SYNC_TRANSFER == TRUE) +void BTA_DmBleGapPeriodicAdvRecvEnable(UINT16 sync_handle, UINT8 enable) +{ + tBTA_DM_API_PERIODIC_ADV_RECV_ENABLE *p_msg; + p_msg = (tBTA_DM_API_PERIODIC_ADV_RECV_ENABLE *) osi_malloc(sizeof(tBTA_DM_API_PERIODIC_ADV_RECV_ENABLE)); + if (p_msg != NULL) { + memset(p_msg, 0, sizeof(tBTA_DM_API_PERIODIC_ADV_RECV_ENABLE)); + p_msg->hdr.event = BTA_DM_API_PERIODIC_ADV_RECV_ENABLE_EVT; + p_msg->sync_handle = sync_handle; + p_msg->enable = enable; + //start sent the msg to the bta system control moudle + bta_sys_sendmsg(p_msg); + } else { + APPL_TRACE_ERROR("%s malloc failed", __func__); + } +} + +void BTA_DmBleGapPeriodicAdvSyncTrans(BD_ADDR peer_addr, UINT16 service_data, UINT16 sync_handle) +{ + tBTA_DM_API_PERIODIC_ADV_SYNC_TRANS *p_msg; + p_msg = (tBTA_DM_API_PERIODIC_ADV_SYNC_TRANS *) osi_malloc(sizeof(tBTA_DM_API_PERIODIC_ADV_SYNC_TRANS)); + if (p_msg != NULL) { + memset(p_msg, 0, sizeof(tBTA_DM_API_PERIODIC_ADV_SYNC_TRANS)); + p_msg->hdr.event = BTA_DM_API_PERIODIC_ADV_SYNC_TRANS_EVT; + memcpy(p_msg->addr, peer_addr, sizeof(BD_ADDR)); + p_msg->service_data = service_data; + p_msg->sync_handle = sync_handle; + //start sent the msg to the bta system control moudle + bta_sys_sendmsg(p_msg); + } else { + APPL_TRACE_ERROR("%s malloc failed", __func__); + } +} + +void BTA_DmBleGapPeriodicAdvSetInfoTrans(BD_ADDR peer_addr, UINT16 service_data, UINT8 adv_handle) +{ + tBTA_DM_API_PERIODIC_ADV_SET_INFO_TRANS *p_msg; + p_msg = (tBTA_DM_API_PERIODIC_ADV_SET_INFO_TRANS *) osi_malloc(sizeof(tBTA_DM_API_PERIODIC_ADV_SET_INFO_TRANS)); + if (p_msg != NULL) { + memset(p_msg, 0, sizeof(tBTA_DM_API_PERIODIC_ADV_SET_INFO_TRANS)); + p_msg->hdr.event = BTA_DM_API_PERIODIC_ADV_SET_INFO_TRANS_EVT; + memcpy(p_msg->addr, peer_addr, sizeof(BD_ADDR)); + p_msg->service_data = service_data; + p_msg->adv_hanlde = adv_handle; + //start sent the msg to the bta system control moudle + bta_sys_sendmsg(p_msg); + } else { + APPL_TRACE_ERROR("%s malloc failed", __func__); + } +} + +void BTA_DmBleGapSetPeriodicAdvSyncTransParams(BD_ADDR peer_addr, tBTA_DM_BLE_PAST_PARAMS *params) +{ + tBTA_DM_API_SET_PAST_PARAMS *p_msg; + p_msg = (tBTA_DM_API_SET_PAST_PARAMS *) osi_malloc(sizeof(tBTA_DM_API_SET_PAST_PARAMS)); + if (p_msg != NULL) { + memset(p_msg, 0, sizeof(tBTA_DM_API_SET_PAST_PARAMS)); + p_msg->hdr.event = BTA_DM_API_SET_PERIODIC_ADV_SYNC_TRANS_PARAMS_EVT; + memcpy(p_msg->addr, peer_addr, sizeof(BD_ADDR)); + memcpy(&p_msg->params, params, sizeof(tBTA_DM_BLE_PAST_PARAMS)); + //start sent the msg to the bta system control moudle + bta_sys_sendmsg(p_msg); + } else { + APPL_TRACE_ERROR("%s malloc failed", __func__); + } +} +#endif // #if (BLE_FEAT_PERIODIC_ADV_SYNC_TRANSFER == TRUE) + +#endif diff --git a/lib/bt/host/bluedroid/bta/dm/bta_dm_cfg.c b/lib/bt/host/bluedroid/bta/dm/bta_dm_cfg.c new file mode 100644 index 00000000..a4792ce8 --- /dev/null +++ b/lib/bt/host/bluedroid/bta/dm/bta_dm_cfg.c @@ -0,0 +1,485 @@ +/****************************************************************************** + * + * Copyright (C) 2003-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains compile-time configurable constants for the device + * manager. + * + ******************************************************************************/ + +#include +#include "common/bt_target.h" +#include "bta/bta_sys.h" +#include "bta/bta_api.h" +#include "bta_dm_int.h" +#include "bta/bta_jv_api.h" +#include "bta/bta_gap_bt_co.h" + +#ifndef BTA_DM_LINK_POLICY_SETTINGS +#define BTA_DM_LINK_POLICY_SETTINGS (HCI_ENABLE_MASTER_SLAVE_SWITCH | HCI_ENABLE_HOLD_MODE | HCI_ENABLE_SNIFF_MODE | HCI_ENABLE_PARK_MODE) +#endif + +/* page timeout in 625uS */ +#ifndef BTA_DM_PAGE_TIMEOUT +#define BTA_DM_PAGE_TIMEOUT 8192 +#endif + +/* link supervision timeout in 625uS (5 secs) */ +#ifndef BTA_DM_LINK_TIMEOUT +#define BTA_DM_LINK_TIMEOUT 8000 +#endif + +/* TRUE to avoid scatternet when av is streaming (be the master) */ +#ifndef BTA_DM_AVOID_SCATTER_A2DP +#define BTA_DM_AVOID_SCATTER_A2DP TRUE +#endif + +/* For Insight, PM cfg lookup tables are runtime configurable (to allow tweaking of params for power consumption measurements) */ +#ifndef BTE_SIM_APP +#define tBTA_DM_PM_TYPE_QUALIFIER const +#else +#define tBTA_DM_PM_TYPE_QUALIFIER +#endif + + +const tBTA_DM_CFG bta_dm_cfg = { + /* mobile phone COD */ + BTA_DM_COD, + /* link policy settings */ + BTA_DM_LINK_POLICY_SETTINGS, + /* page timeout in 625uS */ + BTA_DM_PAGE_TIMEOUT, + /* link supervision timeout in 625uS*/ + BTA_DM_LINK_TIMEOUT, + /* TRUE to avoid scatternet when av is streaming (be the master) */ + BTA_DM_AVOID_SCATTER_A2DP +}; + +#ifndef BTA_DM_SCATTERNET +/* By default, allow partial scatternet */ +#define BTA_DM_SCATTERNET BTA_DM_PARTIAL_SCATTERNET +#endif + +#ifndef BTA_HH_ROLE +/* By default, do not specify HH role (backward compatibility) */ +#define BTA_HH_ROLE BTA_ANY_ROLE +#endif + +#ifndef BTA_AV_ROLE +/* By default, AV role (backward BTA_MASTER_ROLE_PREF) */ +#define BTA_AV_ROLE BTA_MASTER_ROLE_PREF +#endif + +#ifndef BTA_PANU_ROLE +/* By default, AV role (backward BTA_MASTER_ROLE_PREF) */ +#define BTA_PANU_ROLE BTA_SLAVE_ROLE_ONLY +#endif +#define BTA_DM_NUM_RM_ENTRY 6 + +/* appids for PAN used by insight sample application + these have to be same as defined in btui_int.h */ +#define BTUI_PAN_ID_PANU 0 +#define BTUI_PAN_ID_NAP 1 +#define BTUI_PAN_ID_GN 2 + +/* First element is always for SYS: + app_id = # of entries table, cfg is + device scatternet support */ +const tBTA_DM_RM bta_dm_rm_cfg[] = { + {BTA_ID_SYS, BTA_DM_NUM_RM_ENTRY, BTA_DM_SCATTERNET}, + {BTA_ID_PAN, BTUI_PAN_ID_NAP, BTA_ANY_ROLE}, + {BTA_ID_PAN, BTUI_PAN_ID_GN, BTA_ANY_ROLE}, + {BTA_ID_PAN, BTA_APP_ID_PAN_MULTI, BTA_MASTER_ROLE_ONLY}, + {BTA_ID_PAN, BTUI_PAN_ID_PANU, BTA_PANU_ROLE}, + {BTA_ID_HH, BTA_ALL_APP_ID, BTA_HH_ROLE}, + {BTA_ID_AV, BTA_ALL_APP_ID, BTA_AV_ROLE} +}; + + +tBTA_DM_CFG *const p_bta_dm_cfg = (tBTA_DM_CFG *) &bta_dm_cfg; + +tBTA_DM_RM *const p_bta_dm_rm_cfg = (tBTA_DM_RM *) &bta_dm_rm_cfg; + +#if BLE_INCLUDED == TRUE +# define BTA_DM_NUM_PM_ENTRY 10 /* number of entries in bta_dm_pm_cfg except the first */ +# define BTA_DM_NUM_PM_SPEC 10 /* number of entries in bta_dm_pm_spec */ +#else +# define BTA_DM_NUM_PM_ENTRY 8 /* number of entries in bta_dm_pm_cfg except the first */ +# define BTA_DM_NUM_PM_SPEC 8 /* number of entries in bta_dm_pm_spec */ +#endif + +#if (BTA_DM_PM_INCLUDED == TRUE) + +tBTA_DM_PM_TYPE_QUALIFIER tBTA_DM_PM_CFG bta_dm_pm_cfg[BTA_DM_NUM_PM_ENTRY + 1] = { + {BTA_ID_SYS, BTA_DM_NUM_PM_ENTRY, 0}, /* reserved: specifies length of this table. */ + {BTA_ID_AG, BTA_ALL_APP_ID, 0}, /* ag uses first spec table for app id 0 */ + {BTA_ID_AV, BTA_ALL_APP_ID, 1}, /* av spec table */ + {BTA_ID_JV, BTA_APP_ID_1, 2}, /* app BTA_JV_PM_ID_1, reuse ftc spec table */ + {BTA_ID_JV, BTA_ALL_APP_ID, 3}, /* reuse fts spec table */ + {BTA_ID_HS, BTA_ALL_APP_ID, 4}, /* HS spec table */ + {BTA_ID_AVK, BTA_ALL_APP_ID, 5}, /* avk spec table */ + {BTA_ID_HD, BTA_ALL_APP_ID, 6}, /* hd spec table */ + {BTA_ID_HH, BTA_ALL_APP_ID, 7} /* hh spec table */ +#if BLE_INCLUDED == TRUE + , {BTA_ID_GATTC, BTA_ALL_APP_ID, 8} /* gattc spec table */ + , {BTA_ID_GATTS, BTA_ALL_APP_ID, 9} /* gatts spec table */ +#endif +}; + +#define BTA_DM_PM_SPEC_TO_OFFSET (197) /* timeout offset to avoid conflict with other bluedroid host */ +tBTA_DM_PM_TYPE_QUALIFIER tBTA_DM_PM_SPEC bta_dm_pm_spec[BTA_DM_NUM_PM_SPEC] = { + /* AG : 0 */ + { + (BTA_DM_PM_SNIFF | BTA_DM_PM_PARK), /* allow park & sniff */ +#if (BTM_SSR_INCLUDED == TRUE) + (BTA_DM_PM_SSR2), /* the SSR entry */ +#endif + { + {{BTA_DM_PM_SNIFF_A2DP_IDX, 7000 + BTA_DM_PM_SPEC_TO_OFFSET}, {BTA_DM_PM_NO_ACTION, 0}}, /* conn open sniff */ + {{BTA_DM_PM_NO_PREF, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* conn close */ + {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* app open */ + {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* app close */ + {{BTA_DM_PM_SNIFF_SCO_OPEN_IDX, 7000 + BTA_DM_PM_SPEC_TO_OFFSET}, {BTA_DM_PM_NO_ACTION, 0}}, /* sco open, active */ + {{BTA_DM_PM_SNIFF_A2DP_IDX, 7000 + BTA_DM_PM_SPEC_TO_OFFSET}, {BTA_DM_PM_NO_ACTION, 0}}, /* sco close sniff */ + {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* idle */ + {{BTA_DM_PM_ACTIVE, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* busy */ + {{BTA_DM_PM_RETRY, 7000 + BTA_DM_PM_SPEC_TO_OFFSET}, {BTA_DM_PM_NO_ACTION, 0}} /* mode change retry */ + } + }, + + /* AV : 1 */ + { + (BTA_DM_PM_SNIFF), /* allow sniff */ +#if (BTM_SSR_INCLUDED == TRUE) + (BTA_DM_PM_SSR2), /* the SSR entry */ +#endif + { + {{BTA_DM_PM_SNIFF_A2DP_IDX, 7000 + BTA_DM_PM_SPEC_TO_OFFSET}, {BTA_DM_PM_NO_ACTION, 0}}, /* conn open sniff */ + {{BTA_DM_PM_NO_PREF, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* conn close */ + {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* app open */ + {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* app close */ + {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* sco open */ + {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* sco close */ + {{BTA_DM_PM_SNIFF_A2DP_IDX, 7000 + BTA_DM_PM_SPEC_TO_OFFSET}, {BTA_DM_PM_NO_ACTION, 0}}, /* idle */ + {{BTA_DM_PM_ACTIVE, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* busy */ + {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}} /* mode change retry */ + } + }, + + /* FTC, OPC, JV : 2 */ + { + (BTA_DM_PM_SNIFF), /* allow sniff */ + #if (BTM_SSR_INCLUDED == TRUE) + (BTA_DM_PM_SSR2), /* the SSR entry */ + #endif + { + {{BTA_DM_PM_SNIFF_JV_IDX, BTA_FTC_OPS_IDLE_TO_SNIFF_DELAY_MS + BTA_DM_PM_SPEC_TO_OFFSET}, {BTA_DM_PM_NO_ACTION, 0}}, /* conn open active */ + {{BTA_DM_PM_NO_PREF, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* conn close */ + {{BTA_DM_PM_ACTIVE, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* app open */ + {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* app close */ + {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* sco open */ + {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* sco close */ + {{BTA_DM_PM_SNIFF_JV_IDX, BTA_FTC_OPS_IDLE_TO_SNIFF_DELAY_MS + BTA_DM_PM_SPEC_TO_OFFSET}, {BTA_DM_PM_NO_ACTION, 0}}, /* idle */ + {{BTA_DM_PM_ACTIVE, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* busy */ + {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}} /* mode change retry */ + } + }, + + /* FTS, PBS, OPS, MSE, BTA_JV_PM_ID_1 : 3 */ + { + (BTA_DM_PM_SNIFF), /* allow sniff */ + #if (BTM_SSR_INCLUDED == TRUE) + (BTA_DM_PM_SSR2), /* the SSR entry */ + #endif + { + {{BTA_DM_PM_SNIFF_JV_IDX, BTA_FTS_OPS_IDLE_TO_SNIFF_DELAY_MS + BTA_DM_PM_SPEC_TO_OFFSET}, {BTA_DM_PM_NO_ACTION, 0}}, /* conn open active */ + {{BTA_DM_PM_NO_PREF, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* conn close */ + {{BTA_DM_PM_ACTIVE, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* app open */ + {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* app close */ + {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* sco open */ + {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* sco close */ + {{BTA_DM_PM_SNIFF_JV_IDX, BTA_FTS_OPS_IDLE_TO_SNIFF_DELAY_MS + BTA_DM_PM_SPEC_TO_OFFSET}, {BTA_DM_PM_NO_ACTION, 0}}, /* idle */ + {{BTA_DM_PM_ACTIVE, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* busy */ + {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}} /* mode change retry */ + } + }, + + /* HS : 4 */ + { + (BTA_DM_PM_SNIFF | BTA_DM_PM_PARK), /* allow park & sniff */ +#if (BTM_SSR_INCLUDED == TRUE) + (BTA_DM_PM_SSR2), /* the SSR entry */ +#endif + { + {{BTA_DM_PM_SNIFF, 7000 + BTA_DM_PM_SPEC_TO_OFFSET}, {BTA_DM_PM_NO_ACTION, 0}}, /* conn open sniff */ + {{BTA_DM_PM_NO_PREF, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* conn close */ + {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* app open */ + {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* app close */ + {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* sco open, active */ + {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* sco close sniff */ + {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* idle */ + {{BTA_DM_PM_ACTIVE, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* busy */ + {{BTA_DM_PM_RETRY, 7000 + BTA_DM_PM_SPEC_TO_OFFSET}, {BTA_DM_PM_NO_ACTION, 0}} /* mode change retry */ + } + }, + + /* AVK : 5 */ + { + (BTA_DM_PM_SNIFF), /* allow sniff */ +#if (BTM_SSR_INCLUDED == TRUE) + (BTA_DM_PM_SSR2), /* the SSR entry */ +#endif + { + {{BTA_DM_PM_SNIFF, 3000 + BTA_DM_PM_SPEC_TO_OFFSET}, {BTA_DM_PM_NO_ACTION, 0}}, /* conn open sniff */ + {{BTA_DM_PM_NO_PREF, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* conn close */ + {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* app open */ + {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* app close */ + {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* sco open */ + {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* sco close */ + {{BTA_DM_PM_SNIFF_AVK_IDLE_IDX, 3000 + BTA_DM_PM_SPEC_TO_OFFSET}, {BTA_DM_PM_NO_ACTION, 0}}, /* idle */ + {{BTA_DM_PM_ACTIVE, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* busy */ + {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}} /* mode change retry */ + } + }, + + /* HD : 6 */ + { + (BTA_DM_PM_SNIFF | BTA_DM_PM_PARK), /* allow park & sniff */ +#if (BTM_SSR_INCLUDED == TRUE) + (BTA_DM_PM_SSR3), /* the SSR entry */ +#endif + { + {{BTA_DM_PM_SNIFF_HD_ACTIVE_IDX, 5000 + BTA_DM_PM_SPEC_TO_OFFSET}, {BTA_DM_PM_NO_ACTION, 0}}, /* conn open sniff */ + {{BTA_DM_PM_NO_PREF, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* conn close */ + {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* app open */ + {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* app close */ + {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* sco open */ + {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* sco close */ + {{BTA_DM_PM_SNIFF_HD_IDLE_IDX, 5000 + BTA_DM_PM_SPEC_TO_OFFSET}, {BTA_DM_PM_NO_ACTION, 0}}, /* idle */ + {{BTA_DM_PM_SNIFF_HD_ACTIVE_IDX, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* busy */ + {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}} /* mode change retry */ + } + }, + + /* HH : 7 */ + { + (BTA_DM_PM_SNIFF | BTA_DM_PM_PARK), /* allow park & sniff */ +#if (BTM_SSR_INCLUDED == TRUE) + (BTA_DM_PM_SSR1), /* the SSR entry */ +#endif + { + {{BTA_DM_PM_SNIFF_HH_OPEN_IDX, BTA_DM_PM_HH_OPEN_DELAY + BTA_DM_PM_SPEC_TO_OFFSET}, {BTA_DM_PM_NO_ACTION, 0}}, /* conn open sniff */ + {{BTA_DM_PM_NO_PREF, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* conn close */ + {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* app open */ + {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* app close */ + {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* sco open */ + {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* sco close, used for HH suspend */ + {{BTA_DM_PM_SNIFF_HH_IDLE_IDX, BTA_DM_PM_HH_IDLE_DELAY + BTA_DM_PM_SPEC_TO_OFFSET}, {BTA_DM_PM_NO_ACTION, 0}}, /* idle */ + {{BTA_DM_PM_SNIFF_HH_ACTIVE_IDX, BTA_DM_PM_HH_ACTIVE_DELAY + BTA_DM_PM_SPEC_TO_OFFSET}, {BTA_DM_PM_NO_ACTION, 0}}, /* busy */ + {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}} /* mode change retry */ + } + } + +#if BLE_INCLUDED == TRUE + /* GATTC : 8 */ + , { + (BTA_DM_PM_SNIFF | BTA_DM_PM_PARK), /* allow park & sniff */ +#if (BTM_SSR_INCLUDED == TRUE) + (BTA_DM_PM_SSR2), /* the SSR entry */ +#endif + { + {{BTA_DM_PM_SNIFF_A2DP_IDX, 10000 + BTA_DM_PM_SPEC_TO_OFFSET}, {BTA_DM_PM_NO_ACTION, 0}}, /* conn open active */ + {{BTA_DM_PM_NO_PREF, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* conn close */ + {{BTA_DM_PM_ACTIVE, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* app open */ + {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* app close */ + {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* sco open */ + {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* sco close */ + {{BTA_DM_PM_SNIFF_A2DP_IDX, 10000 + BTA_DM_PM_SPEC_TO_OFFSET}, {BTA_DM_PM_NO_ACTION, 0}}, /* idle */ + {{BTA_DM_PM_ACTIVE, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* busy */ +#if defined(AMP_INCLUDED) && (AMP_INCLUDED == TRUE) + {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* amp */ +#endif + {{BTA_DM_PM_RETRY, 5000 + BTA_DM_PM_SPEC_TO_OFFSET}, {BTA_DM_PM_NO_ACTION, 0}} /* mode change retry */ + } + } + /* GATTS : 9 */ + , { + (BTA_DM_PM_SNIFF | BTA_DM_PM_PARK), /* allow park & sniff */ +#if (BTM_SSR_INCLUDED == TRUE) + (BTA_DM_PM_SSR2), /* the SSR entry */ +#endif + { + {{BTA_DM_PM_NO_PREF, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* conn open active */ + {{BTA_DM_PM_NO_PREF, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* conn close */ + {{BTA_DM_PM_NO_PREF, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* app open */ + {{BTA_DM_PM_NO_PREF, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* app close */ + {{BTA_DM_PM_NO_PREF, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* sco open */ + {{BTA_DM_PM_NO_PREF, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* sco close */ + {{BTA_DM_PM_NO_PREF, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* idle */ + {{BTA_DM_PM_NO_PREF, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* busy */ +#if defined(AMP_INCLUDED) && (AMP_INCLUDED == TRUE) + {{BTA_DM_PM_NO_PREF, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* amp */ +#endif + {{BTA_DM_PM_RETRY, 5000 + BTA_DM_PM_SPEC_TO_OFFSET}, {BTA_DM_PM_NO_ACTION, 0}} /* mode change retry */ + } + } + +#endif + +#ifdef BTE_SIM_APP /* For Insight builds only */ + /* Entries at the end of the pm_spec table are user-defined (runtime configurable), + for power consumption experiments. + Insight finds the first user-defined entry by looking for the first BTA_DM_PM_NO_PREF. + The number of user_defined specs is defined by BTA_SWRAP_UD_PM_SPEC_COUNT */ + , + {BTA_DM_PM_NO_PREF}, /* pm_spec USER_DEFINED_0 */ + {BTA_DM_PM_NO_PREF} /* pm_spec USER_DEFINED_1 */ +#endif /* BTE_SIM_APP */ +}; + +/* Please refer to the SNIFF table definitions in bta/bta_api.h. + * + * Adding to or Modifying the Table + * Additional sniff parameter entries can be added for BTA_DM_PM_SNIFF5 - BTA_DM_PM_SNIFF7. + * Overrides of additional table entries can be specified in bdroid_buildcfg.h. If additional + * sniff parameter entries are added or an override of an existing entry is specified in + * bdroid_buildcfg.h then the BTA_DM_PM_*_IDX defines in bta/bta_api.h will need to be match the new + * ordering. + * + * Table Ordering + * Sniff Table entries must be ordered from highest latency (biggest interval) to lowest latency. + * If there is a conflict among the connected services the setting with the lowest latency will + * be selected. + */ +tBTA_DM_PM_TYPE_QUALIFIER tBTM_PM_PWR_MD bta_dm_pm_md[] = { + /* + * More sniff parameter entries can be added for + * BTA_DM_PM_SNIFF3 - BTA_DM_PM_SNIFF7, if needed. When entries are added or + * removed, BTA_DM_PM_PARK_IDX needs to be updated to reflect the actual index + * BTA_DM_PM_PARK_IDX is defined in bta/bta_api.h and can be override by the + * bdroid_buildcfg.h settings. + * The SNIFF table entries must be in the order from highest latency (biggest + * interval) to lowest latency. If there's a conflict among the connected + * services, the setting with lowest latency wins. + */ + /* sniff modes: max interval, min interval, attempt, timeout */ + {BTA_DM_PM_SNIFF_MAX, BTA_DM_PM_SNIFF_MIN, BTA_DM_PM_SNIFF_ATTEMPT, BTA_DM_PM_SNIFF_TIMEOUT, BTM_PM_MD_SNIFF}, /* for BTA_DM_PM_SNIFF - A2DP */ + {BTA_DM_PM_SNIFF1_MAX, BTA_DM_PM_SNIFF1_MIN, BTA_DM_PM_SNIFF1_ATTEMPT, BTA_DM_PM_SNIFF1_TIMEOUT, BTM_PM_MD_SNIFF}, /* for BTA_DM_PM_SNIFF1 */ + {BTA_DM_PM_SNIFF2_MAX, BTA_DM_PM_SNIFF2_MIN, BTA_DM_PM_SNIFF2_ATTEMPT, BTA_DM_PM_SNIFF2_TIMEOUT, BTM_PM_MD_SNIFF}, /* for BTA_DM_PM_SNIFF2 */ + {BTA_DM_PM_SNIFF3_MAX, BTA_DM_PM_SNIFF3_MIN, BTA_DM_PM_SNIFF3_ATTEMPT, BTA_DM_PM_SNIFF3_TIMEOUT, BTM_PM_MD_SNIFF}, /* for BTA_DM_PM_SNIFF3- SCO open */ + {BTA_DM_PM_SNIFF4_MAX, BTA_DM_PM_SNIFF4_MIN, BTA_DM_PM_SNIFF4_ATTEMPT, BTA_DM_PM_SNIFF4_TIMEOUT, BTM_PM_MD_SNIFF}, /* for BTA_DM_PM_SNIFF4 */ + {BTA_DM_PM_SNIFF5_MAX, BTA_DM_PM_SNIFF5_MIN, BTA_DM_PM_SNIFF5_ATTEMPT, BTA_DM_PM_SNIFF5_TIMEOUT, BTM_PM_MD_SNIFF}, /* for BTA_DM_PM_SNIFF5 */ + {BTA_DM_PM_PARK_MAX, BTA_DM_PM_PARK_MIN, BTA_DM_PM_PARK_ATTEMPT, BTA_DM_PM_PARK_TIMEOUT, BTM_PM_MD_PARK} + +#ifdef BTE_SIM_APP /* For Insight builds only */ + /* Entries at the end of the bta_dm_pm_md table are user-defined (runtime configurable), + for power consumption experiments. + Insight finds the first user-defined entry by looking for the first 'max=0'. + The number of user_defined specs is defined by BTA_SWRAP_UD_PM_DM_COUNT */ + , + {0}, /* CONN_OPEN/SCO_CLOSE power mode settings for pm_spec USER_DEFINED_0 */ + {0}, /* SCO_OPEN power mode settings for pm_spec USER_DEFINED_0 */ + + {0}, /* CONN_OPEN/SCO_CLOSE power mode settings for pm_spec USER_DEFINED_1 */ + {0} /* SCO_OPEN power mode settings for pm_spec USER_DEFINED_1 */ +#endif /* BTE_SIM_APP */ +}; + +/* 0=max_lat -> no SSR */ +/* the smaller of the SSR max latency wins. + * the entries in this table must be from highest latency (biggest interval) to lowest latency */ +#if (BTM_SSR_INCLUDED == TRUE) +tBTA_DM_SSR_SPEC bta_dm_ssr_spec[] = { + /*max_lat, min_rmt_to, min_loc_to*/ + {0, 0, 0}, /* BTA_DM_PM_SSR0 - do not use SSR */ + {0, 0, 2}, /* BTA_DM_PM_SSR1 - HH, can NOT share entry with any other profile, + seting default max latency and min remote timeout as 0, + and always read individual device preference from HH module */ + {1200, 2, 2}, /* BTA_DM_PM_SSR2 - others (as long as sniff is allowed)*/ + {360, 160, 1600} /* BTA_DM_PM_SSR3 - HD */ +}; + +tBTA_DM_SSR_SPEC *const p_bta_dm_ssr_spec = (tBTA_DM_SSR_SPEC *) &bta_dm_ssr_spec; +#endif + +tBTA_DM_PM_CFG *const p_bta_dm_pm_cfg = (tBTA_DM_PM_CFG *) &bta_dm_pm_cfg; +tBTA_DM_PM_SPEC *const p_bta_dm_pm_spec = (tBTA_DM_PM_SPEC *) &bta_dm_pm_spec; +tBTM_PM_PWR_MD *const p_bta_dm_pm_md = (tBTM_PM_PWR_MD *) &bta_dm_pm_md; + +#endif /* #if (BTA_DM_PM_INCLUDED == TRUE) */ + +/* The performance impact of EIR packet size +** +** When BTM_EIR_DEFAULT_FEC_REQUIRED is TRUE, +** 1 to 17 bytes, DM1 is used and most robust. +** 18 to 121 bytes, DM3 is used but impacts inquiry scan time with large number +** of devices.(almost double with 150 users) +** 122 to 224 bytes, DM5 is used but cause quite big performance loss even with +** small number of users. so it is not recommended. +** 225 to 240 bytes, DH5 is used without FEC but it not recommended. +** (same reason of DM5) +** +** When BTM_EIR_DEFAULT_FEC_REQUIRED is FALSE, +** 1 to 27 bytes, DH1 is used but only robust at short range. +** 28 to 183 bytes, DH3 is used but only robust at short range and impacts inquiry +** scan time with large number of devices. +** 184 to 240 bytes, DH5 is used but it not recommended. +*/ + +#if (BTA_EIR_CANNED_UUID_LIST == TRUE) +/* for example */ +const UINT8 bta_dm_eir_uuid16_list[] = { 0x08, 0x11, /* Headset */ + 0x1E, 0x11, /* Handsfree */ + 0x0E, 0x11, /* AV Remote Control */ + 0x0B, 0x11, /* Audio Sink */ + }; +#endif // BTA_EIR_CANNED_UUID_LIST + +/* Extended Inquiry Response */ +tBTA_DM_EIR_CONF bta_dm_eir_cfg = { + BTM_EIR_DEFAULT_FEC_REQUIRED, /* FEC required */ + TRUE, /* Included local name */ + 50, /* minimum length of local name when it is shortened */ + /* if length of local name is longer than this and EIR has not enough */ + /* room for all UUID list then local name is shortened to this length */ + TRUE, /* Included UUIDs */ +#if (BTA_EIR_CANNED_UUID_LIST == TRUE) + 8, + (UINT8 *)bta_dm_eir_uuid16_list, +#else // BTA_EIR_CANNED_UUID_LIST + { /* mask of UUID list in EIR */ + 0xFFFFFFFF, /* LSB is the first UUID of the first 32 UUIDs in BTM_EIR_UUID_LKUP_TBL */ + 0xFFFFFFFF /* LSB is the first UUID of the next 32 UUIDs in BTM_EIR_UUID_LKUP_TBL */ + /* BTM_EIR_UUID_LKUP_TBL can be overrided */ + }, +#endif // BTA_EIR_CANNED_UUID_LIST + FALSE, /* Not included TX power*/ + 3, /* Inquiry TX power */ + 0, /* flags for EIR */ + 0, /* length of manufacturer specific in bytes */ + NULL, /* manufacturer specific */ + 0, /* length of URL in bytes */ + NULL, /* URL */ +#if (BTC_GAP_BT_INCLUDED == TRUE) + (tBTA_DM_CONFIG_EIR_CBACK *)btc_gap_bt_config_eir_cmpl_callback /* callback */ +#else + NULL +#endif /* #if (BTC_GAP_BT_INCLUDED == TRUE) */ +}; +tBTA_DM_EIR_CONF *p_bta_dm_eir_cfg = (tBTA_DM_EIR_CONF *) &bta_dm_eir_cfg; diff --git a/lib/bt/host/bluedroid/bta/dm/bta_dm_ci.c b/lib/bt/host/bluedroid/bta/dm/bta_dm_ci.c new file mode 100644 index 00000000..f10787ad --- /dev/null +++ b/lib/bt/host/bluedroid/bta/dm/bta_dm_ci.c @@ -0,0 +1,85 @@ +/****************************************************************************** + * + * Copyright (C) 2003-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This is the API implementation file for the BTA device manager. + * + ******************************************************************************/ + +#include "bta/bta_sys.h" +#include "bta/bta_api.h" +#include "bta_dm_int.h" +#include +#include "bta/bta_dm_ci.h" +#include "osi/allocator.h" + + +#if (BTM_OOB_INCLUDED == TRUE && SMP_INCLUDED == TRUE) +/******************************************************************************* +** +** Function bta_dm_ci_io_req +** +** Description This function must be called in response to function +** bta_dm_co_io_req(), if *p_oob_data to BTA_OOB_UNKNOWN +** by bta_dm_co_io_req(). +** +** Returns void +** +*******************************************************************************/ +void bta_dm_ci_io_req(BD_ADDR bd_addr, tBTA_IO_CAP io_cap, tBTA_OOB_DATA oob_data, + tBTA_AUTH_REQ auth_req) + +{ + tBTA_DM_CI_IO_REQ *p_msg; + + if ((p_msg = (tBTA_DM_CI_IO_REQ *) osi_malloc(sizeof(tBTA_DM_CI_IO_REQ))) != NULL) { + p_msg->hdr.event = BTA_DM_CI_IO_REQ_EVT; + bdcpy(p_msg->bd_addr, bd_addr); + p_msg->io_cap = io_cap; + p_msg->oob_data = oob_data; + p_msg->auth_req = auth_req; + bta_sys_sendmsg(p_msg); + } +} + +/******************************************************************************* +** +** Function bta_dm_ci_rmt_oob +** +** Description This function must be called in response to function +** bta_dm_co_rmt_oob() to provide the OOB data associated +** with the remote device. +** +** Returns void +** +*******************************************************************************/ +void bta_dm_ci_rmt_oob(BOOLEAN accept, BD_ADDR bd_addr, BT_OCTET16 c, BT_OCTET16 r) +{ + tBTA_DM_CI_RMT_OOB *p_msg; + + if ((p_msg = (tBTA_DM_CI_RMT_OOB *) osi_malloc(sizeof(tBTA_DM_CI_RMT_OOB))) != NULL) { + p_msg->hdr.event = BTA_DM_CI_RMT_OOB_EVT; + bdcpy(p_msg->bd_addr, bd_addr); + p_msg->accept = accept; + memcpy(p_msg->c, c, BT_OCTET16_LEN); + memcpy(p_msg->r, r, BT_OCTET16_LEN); + bta_sys_sendmsg(p_msg); + } +} +#endif /* BTM_OOB_INCLUDED */ diff --git a/lib/bt/host/bluedroid/bta/dm/bta_dm_co.c b/lib/bt/host/bluedroid/bta/dm/bta_dm_co.c new file mode 100644 index 00000000..c6b62f3a --- /dev/null +++ b/lib/bt/host/bluedroid/bta/dm/bta_dm_co.c @@ -0,0 +1,458 @@ +/****************************************************************************** + * + * Copyright (C) 2009-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ +#include +#include + +#include "bta/bta_api.h" +#include "bta/bta_sys.h" +#include "bta/bta_dm_co.h" +#include "bta/bta_dm_ci.h" +#include "btc/btc_dm.h" +#if (defined(BTIF_INCLUDED) && BTIF_INCLUDED == TRUE) +#include "common/bt_defs.h" +#if (BTM_OOB_INCLUDED == TRUE) +#include "btif_dm.h" +#endif +#endif /* #if (defined(BTIF_INCLUDED) && BTIF_INCLUDED == TRUE) */ +#if (defined BLE_INCLUDED && BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE) +#include "common/bte_appl.h" + +#define BTM_BLE_ONLY_ACCEPT_SPECIFIED_SEC_AUTH_DISABLE 0 +#define BTM_BLE_ONLY_ACCEPT_SPECIFIED_SEC_AUTH_ENABLE 1 + +#define BTM_BLE_OOB_DISABLE 0 +#define BTM_BLE_OOB_ENABLE 1 + +tBTE_APPL_CFG bte_appl_cfg = { +#if SMP_INCLUDED == TRUE + BTA_LE_AUTH_REQ_SC_MITM_BOND, // Authentication requirements +#else + BTM_AUTH_SPGB_YES, // Authentication requirements +#endif + BTM_LOCAL_IO_CAPS_BLE, + BTM_BLE_INITIATOR_KEY_SIZE, + BTM_BLE_RESPONDER_KEY_SIZE, + BTM_BLE_MAX_KEY_SIZE, + BTM_BLE_MIN_KEY_SIZE, + BTM_BLE_ONLY_ACCEPT_SPECIFIED_SEC_AUTH_DISABLE, + BTM_BLE_OOB_DISABLE, +}; +#endif + +#if (defined CLASSIC_BT_INCLUDED && CLASSIC_BT_INCLUDED == TRUE) +#include "btm_int.h" +#endif + +/******************************************************************************* +** +** Function bta_dm_co_get_compress_memory +** +** Description This callout function is executed by DM to get memory for compression + +** Parameters id - BTA SYS ID +** memory_p - memory return by callout +** memory_size - memory size +** +** Returns TRUE for success, FALSE for fail. +** +*******************************************************************************/ +BOOLEAN bta_dm_co_get_compress_memory(tBTA_SYS_ID id, UINT8 **memory_p, UINT32 *memory_size) +{ + UNUSED(id); + UNUSED(memory_p); + UNUSED(memory_size); + return TRUE; +} + +/******************************************************************************* +** +** Function bta_dm_co_bt_set_io_cap +** +** Description This function is used to set IO capabilities +** +** Parameters bt_io_cap - IO capabilities +** +** @return - ESP_BT_STATUS_SUCCESS : success +** - other : failed +** +*******************************************************************************/ +#if (CLASSIC_BT_INCLUDED == TRUE) +esp_err_t bta_dm_co_bt_set_io_cap(UINT8 bt_io_cap) +{ + esp_err_t ret = ESP_BT_STATUS_SUCCESS; + + if(bt_io_cap < BTM_IO_CAP_MAX ) { + btm_cb.devcb.loc_io_caps = bt_io_cap; + ret = ESP_BT_STATUS_SUCCESS; + } else { + ret = ESP_BT_STATUS_FAIL; + APPL_TRACE_ERROR("%s error:Invalid io cap value.",__func__); + } + + return ret; +} +#endif /* (CLASSIC_BT_INCLUDED == TRUE) */ + +/******************************************************************************* +** +** Function bta_dm_co_io_req +** +** Description This callout function is executed by DM to get IO capabilities +** of the local device for the Simple Pairing process +** +** Parameters bd_addr - The peer device +** *p_io_cap - The local Input/Output capabilities +** *p_oob_data - TRUE, if OOB data is available for the peer device. +** *p_auth_req - TRUE, if MITM protection is required. +** +** Returns void. +** +*******************************************************************************/ +void bta_dm_co_io_req(BD_ADDR bd_addr, tBTA_IO_CAP *p_io_cap, tBTA_OOB_DATA *p_oob_data, + tBTA_AUTH_REQ *p_auth_req, BOOLEAN is_orig) +{ + UNUSED(bd_addr); +#if (defined(BTIF_INCLUDED) && BTIF_INCLUDED == TRUE) +#if (BTM_OOB_INCLUDED == TRUE) + btif_dm_set_oob_for_io_req(p_oob_data); +#endif + btif_dm_proc_io_req(bd_addr, p_io_cap, p_oob_data, p_auth_req, is_orig); +#else + BTIF_TRACE_DEBUG("bta_dm_co_io_req: func not ported\n"); +#endif /* #if (defined(BTIF_INCLUDED) && BTIF_INCLUDED == TRUE) */ + BTIF_TRACE_DEBUG("bta_dm_co_io_req *p_oob_data = %d", *p_oob_data); + BTIF_TRACE_DEBUG("bta_dm_co_io_req *p_io_cap = %d", *p_io_cap); + BTIF_TRACE_DEBUG("bta_dm_co_io_req *p_auth_req = %d", *p_auth_req); + BTIF_TRACE_DEBUG("bta_dm_co_io_req is_orig = %d", is_orig); +} + +/******************************************************************************* +** +** Function bta_dm_co_io_rsp +** +** Description This callout function is executed by DM to report IO capabilities +** of the peer device for the Simple Pairing process +** +** Parameters bd_addr - The peer device +** io_cap - The remote Input/Output capabilities +** oob_data - TRUE, if OOB data is available for the peer device. +** auth_req - TRUE, if MITM protection is required. +** +** Returns void. +** +*******************************************************************************/ +void bta_dm_co_io_rsp(BD_ADDR bd_addr, tBTA_IO_CAP io_cap, + tBTA_OOB_DATA oob_data, tBTA_AUTH_REQ auth_req) +{ +#if (defined(BTIF_INCLUDED) && BTIF_INCLUDED == TRUE) + btif_dm_proc_io_rsp(bd_addr, io_cap, oob_data, auth_req); +#else + BTIF_TRACE_DEBUG("bta_dm_co_io_rsp: func not ported\n"); +#endif /* #if (defined(BTIF_INCLUDED) && BTIF_INCLUDED == TRUE) */ +} + +/******************************************************************************* +** +** Function bta_dm_co_lk_upgrade +** +** Description This callout function is executed by DM to check if the +** platform wants allow link key upgrade +** +** Parameters bd_addr - The peer device +** *p_upgrade - TRUE, if link key upgrade is desired. +** +** Returns void. +** +*******************************************************************************/ +void bta_dm_co_lk_upgrade(BD_ADDR bd_addr, BOOLEAN *p_upgrade ) +{ + UNUSED(bd_addr); + UNUSED(p_upgrade); +} + +#if (BTM_OOB_INCLUDED == TRUE) +/******************************************************************************* +** +** Function bta_dm_co_loc_oob +** +** Description This callout function is executed by DM to report the OOB +** data of the local device for the Simple Pairing process +** +** Parameters valid - TRUE, if the local OOB data is retrieved from LM +** c - Simple Pairing Hash C +** r - Simple Pairing Randomnizer R +** +** Returns void. +** +*******************************************************************************/ +void bta_dm_co_loc_oob(BOOLEAN valid, BT_OCTET16 c, BT_OCTET16 r) +{ +#if (defined(BTIF_INCLUDED) && BTIF_INCLUDED == TRUE) + BTIF_TRACE_DEBUG("bta_dm_co_loc_oob, valid = %d", valid); +#ifdef BTIF_DM_OOB_TEST + btif_dm_proc_loc_oob(valid, c, r); +#endif +#else + BTIF_TRACE_DEBUG("bta_dm_co_loc_oob: func not ported\n"); +#endif /* #if (defined(BTIF_INCLUDED) && BTIF_INCLUDED == TRUE) */ +} + +/******************************************************************************* +** +** Function bta_dm_co_rmt_oob +** +** Description This callout function is executed by DM to request the OOB +** data for the remote device for the Simple Pairing process +** Need to call bta_dm_ci_rmt_oob() in response +** +** Parameters bd_addr - The peer device +** +** Returns void. +** +*******************************************************************************/ +void bta_dm_co_rmt_oob(BD_ADDR bd_addr) +{ + BT_OCTET16 p_c = {0}; + BT_OCTET16 p_r = {0}; + BOOLEAN result = FALSE; + +#ifdef BTIF_DM_OOB_TEST +#if (defined(BTIF_INCLUDED) && BTIF_INCLUDED == TRUE) + result = btif_dm_proc_rmt_oob(bd_addr, p_c, p_r); +#else + BTIF_TRACE_DEBUG("bta_dm_rmt_oob: func not ported\n"); +#endif /* #if (defined(BTIF_INCLUDED) && BTIF_INCLUDED == TRUE) */ +#endif + + BTIF_TRACE_DEBUG("bta_dm_co_rmt_oob: result=%d", result); + bta_dm_ci_rmt_oob(result, bd_addr, p_c, p_r); +} + +#endif /* BTM_OOB_INCLUDED */ + + +// REMOVE FOR BLUEDROID ? + +#if (defined BLE_INCLUDED && BLE_INCLUDED == TRUE) +/******************************************************************************* +** +** Function bta_dm_co_le_io_key_req +** +** Description This callout function is executed by DM to get BLE key information +** before SMP pairing gets going. +** +** Parameters bd_addr - The peer device +** *p_max_key_size - max key size local device supported. +** *p_init_key - initiator keys. +** *p_resp_key - responder keys. +** +** Returns void. +** +*******************************************************************************/ +void bta_dm_co_le_io_key_req(BD_ADDR bd_addr, UINT8 *p_max_key_size, + tBTA_LE_KEY_TYPE *p_init_key, + tBTA_LE_KEY_TYPE *p_resp_key ) +{ + UNUSED(bd_addr); +#if (SMP_INCLUDED == TRUE) + BTIF_TRACE_DEBUG("##################################"); + BTIF_TRACE_DEBUG("bta_dm_co_le_io_key_req: only setting max size to 16"); + BTIF_TRACE_DEBUG("##################################"); + *p_max_key_size = 16; + *p_init_key = *p_resp_key = + (BTA_LE_KEY_PENC | BTA_LE_KEY_PID | BTA_LE_KEY_PCSRK | BTA_LE_KEY_LENC | BTA_LE_KEY_LID | BTA_LE_KEY_LCSRK); +#endif ///SMP_INCLUDED == TRUE +} + + +/******************************************************************************* +** +** Function bta_dm_co_ble_local_key_reload +** +** Description This callout function is to load the local BLE keys if available +** on the device. +** +** Parameters none +** +** Returns void. +** +*******************************************************************************/ +void bta_dm_co_ble_load_local_keys(tBTA_DM_BLE_LOCAL_KEY_MASK *p_key_mask, BT_OCTET16 er, + tBTA_BLE_LOCAL_ID_KEYS *p_id_keys) +{ +#if (defined(BTIF_INCLUDED) && BTIF_INCLUDED == TRUE) + BTIF_TRACE_DEBUG("##################################"); + BTIF_TRACE_DEBUG("bta_dm_co_ble_load_local_keys: Load local keys if any are persisted"); + BTIF_TRACE_DEBUG("##################################"); + btif_dm_get_ble_local_keys( p_key_mask, er, p_id_keys); +#endif ///defined(BTIF_INCLUDED) && BTIF_INCLUDED == TRUE +#if (SMP_INCLUDED == TRUE) + btc_dm_get_ble_local_keys( p_key_mask, er, p_id_keys); +#endif ///SMP_INCLUDED == TRUE +} + +/******************************************************************************* +** +** Function bta_dm_co_ble_io_req +** +** Description This callout function is executed by DM to get BLE IO capabilities +** before SMP pairing gets going. +** +** Parameters bd_addr - The peer device +** *p_io_cap - The local Input/Output capabilities +** *p_oob_data - TRUE, if OOB data is available for the peer device. +** *p_auth_req - Auth request setting (Bonding and MITM required or not) +** *p_max_key_size - max key size local device supported. +** *p_init_key - initiator keys. +** *p_resp_key - responder keys. +** +** Returns void. +** +*******************************************************************************/ +void bta_dm_co_ble_io_req(BD_ADDR bd_addr, tBTA_IO_CAP *p_io_cap, + tBTA_OOB_DATA *p_oob_data, + tBTA_LE_AUTH_REQ *p_auth_req, + UINT8 *p_max_key_size, + tBTA_LE_KEY_TYPE *p_init_key, + tBTA_LE_KEY_TYPE *p_resp_key ) +{ +#if (SMP_INCLUDED == TRUE) + UNUSED(bd_addr); + /* if OOB is not supported, this call-out function does not need to do anything + * otherwise, look for the OOB data associated with the address and set *p_oob_data accordingly + * If the answer can not be obtained right away, + * set *p_oob_data to BTA_OOB_UNKNOWN and call bta_dm_ci_io_req() when the answer is available */ + + *p_oob_data = bte_appl_cfg.oob_support; + + /* *p_auth_req by default is FALSE for devices with NoInputNoOutput; TRUE for other devices. */ + + *p_auth_req = bte_appl_cfg.ble_auth_req | (bte_appl_cfg.ble_auth_req & BTA_LE_AUTH_REQ_MITM) | ((*p_auth_req) & BTA_LE_AUTH_REQ_MITM); + + if (bte_appl_cfg.ble_io_cap <= 4) { + *p_io_cap = bte_appl_cfg.ble_io_cap; + } + + if (bte_appl_cfg.ble_init_key <= BTM_BLE_INITIATOR_KEY_SIZE) { + *p_init_key = bte_appl_cfg.ble_init_key; + } + + if (bte_appl_cfg.ble_resp_key <= BTM_BLE_RESPONDER_KEY_SIZE) { + *p_resp_key = bte_appl_cfg.ble_resp_key; + } + + if (bte_appl_cfg.ble_max_key_size >= 7 && bte_appl_cfg.ble_max_key_size <= 16) { + *p_max_key_size = bte_appl_cfg.ble_max_key_size; + } +#endif ///SMP_INCLUDED == TRUE +} + +void bta_dm_co_ble_set_io_cap(UINT8 ble_io_cap) +{ +#if (SMP_INCLUDED == TRUE) + if(ble_io_cap < BTM_IO_CAP_MAX ) { + bte_appl_cfg.ble_io_cap = ble_io_cap; + } else { + APPL_TRACE_ERROR("%s error:Invalid io cap value.",__func__); + } +#endif ///SMP_INCLUDED == TRUE +} + +void bta_dm_co_ble_set_auth_req(UINT8 ble_auth_req) +{ +#if (SMP_INCLUDED == TRUE) + bte_appl_cfg.ble_auth_req = ble_auth_req; +#endif ///SMP_INCLUDED == TRUE +} + +void bta_dm_co_ble_set_init_key_req(UINT8 init_key) +{ +#if (SMP_INCLUDED == TRUE) + init_key &= 0x0f; // 4~7bit reservd, only used the 0~3bit + bte_appl_cfg.ble_init_key = init_key; +#endif ///SMP_INCLUDED == TRUE +} + +void bta_dm_co_ble_set_rsp_key_req(UINT8 rsp_key) +{ +#if (SMP_INCLUDED == TRUE) + rsp_key &= 0x0f; // 4~7bit reservd, only used the 0~3bit + bte_appl_cfg.ble_resp_key = rsp_key; +#endif ///SMP_INCLUDED == TRUE +} + +void bta_dm_co_ble_set_max_key_size(UINT8 ble_key_size) +{ +#if (SMP_INCLUDED == TRUE) + if(ble_key_size >= bte_appl_cfg.ble_min_key_size && ble_key_size <= BTM_BLE_MAX_KEY_SIZE) { + bte_appl_cfg.ble_max_key_size = ble_key_size; + } else { + APPL_TRACE_ERROR("%s error:Invalid key size value, key_size =%d",__func__, ble_key_size); + } +#endif ///SMP_INCLUDED == TRUE +} + +void bta_dm_co_ble_set_min_key_size(UINT8 ble_key_size) +{ +#if (SMP_INCLUDED == TRUE) + if(ble_key_size >= BTM_BLE_MIN_KEY_SIZE && ble_key_size <= bte_appl_cfg.ble_max_key_size) { + bte_appl_cfg.ble_min_key_size = ble_key_size; + } else { + APPL_TRACE_ERROR("%s error:Invalid key size value, key_size =%d",__func__, ble_key_size); + } +#endif ///SMP_INCLUDED == TRUE +} + +void bta_dm_co_ble_set_accept_auth_enable(UINT8 enable) +{ +#if (SMP_INCLUDED == TRUE) + if (enable) { + enable = BTM_BLE_ONLY_ACCEPT_SPECIFIED_SEC_AUTH_ENABLE; + } + bte_appl_cfg.ble_accept_auth_enable = enable; +#endif ///SMP_INCLUDED == TRUE +} + +UINT8 bta_dm_co_ble_get_accept_auth_enable(void) +{ +#if (SMP_INCLUDED == TRUE) + return bte_appl_cfg.ble_accept_auth_enable; +#endif ///SMP_INCLUDED == TRUE + return 0; +} + +UINT8 bta_dm_co_ble_get_auth_req(void) +{ +#if (SMP_INCLUDED == TRUE) + return bte_appl_cfg.ble_auth_req; +#endif ///SMP_INCLUDED == TRUE + return 0; +} + +void bta_dm_co_ble_oob_support(UINT8 enable) +{ +#if (SMP_INCLUDED == TRUE) + if (enable) { + bte_appl_cfg.oob_support = BTM_BLE_OOB_ENABLE; + } else { + bte_appl_cfg.oob_support = BTM_BLE_OOB_DISABLE; + } +#endif ///SMP_INCLUDED == TRUE +} + +#endif diff --git a/lib/bt/host/bluedroid/bta/dm/bta_dm_main.c b/lib/bt/host/bluedroid/bta/dm/bta_dm_main.c new file mode 100644 index 00000000..42e67d77 --- /dev/null +++ b/lib/bt/host/bluedroid/bta/dm/bta_dm_main.c @@ -0,0 +1,549 @@ +/****************************************************************************** + * + * Copyright (C) 2003-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This is the main implementation file for the BTA device manager. + * + ******************************************************************************/ + +#include "bta/bta_api.h" +#include "bta/bta_sys.h" +#include "bta_dm_int.h" +#if (ESP_COEX_VSC_INCLUDED == TRUE) +#include "stack/btm_api.h" +#endif +#include "osi/allocator.h" +#include + +#include "esp_coexist.h" + +/***************************************************************************** +** Constants and types +*****************************************************************************/ + +#if BTA_DYNAMIC_MEMORY == FALSE +tBTA_DM_CB bta_dm_cb; +tBTA_DM_SEARCH_CB bta_dm_search_cb; +tBTA_DM_DI_CB bta_dm_di_cb; +#else +tBTA_DM_CB *bta_dm_cb_ptr; +tBTA_DM_SEARCH_CB *bta_dm_search_cb_ptr; +tBTA_DM_DI_CB *bta_dm_di_cb_ptr; +SemaphoreHandle_t deinit_semaphore; +#endif + + +#define BTA_DM_NUM_ACTIONS (BTA_DM_MAX_EVT & 0x00ff) + +/* type for action functions */ +typedef void (*tBTA_DM_ACTION)(tBTA_DM_MSG *p_data); + +/* action function list */ +const tBTA_DM_ACTION bta_dm_action[BTA_DM_MAX_EVT] = { + + /* device manager local device API events */ + bta_dm_enable, /* BTA_DM_API_ENABLE_EVT */ + bta_dm_disable, /* BTA_DM_API_DISABLE_EVT */ + bta_dm_set_dev_name, /* BTA_DM_API_SET_NAME_EVT */ + bta_dm_get_dev_name, /* BTA_DM_API_GET_NAME_EVT */ +#if (ESP_COEX_VSC_INCLUDED == TRUE) + bta_dm_cfg_coex_status, /* BTA_DM_API_CFG_COEX_ST_EVT */ +#endif +#if (CLASSIC_BT_INCLUDED == TRUE) + bta_dm_config_eir, /* BTA_DM_API_CONFIG_EIR_EVT */ + bta_dm_set_page_timeout, /* BTA_DM_API_PAGE_TO_SET_EVT */ + bta_dm_get_page_timeout, /* BTA_DM_API_PAGE_TO_GET_EVT */ + bta_dm_set_acl_pkt_types, /* BTA_DM_API_SET_ACL_PKT_TYPES_EVT */ +#endif + bta_dm_set_afh_channels, /* BTA_DM_API_SET_AFH_CHANNELS_EVT */ +#if (SDP_INCLUDED == TRUE) + bta_dm_read_rmt_name, /* BTA_DM_API_GET_REMOTE_NAME_EVT*/ +#endif + bta_dm_set_visibility, /* BTA_DM_API_SET_VISIBILITY_EVT */ + bta_dm_acl_change, /* BTA_DM_ACL_CHANGE_EVT */ + bta_dm_add_device, /* BTA_DM_API_ADD_DEVICE_EVT */ + bta_dm_close_acl, /* BTA_DM_API_REMOVE_ACL_EVT */ +#if (SMP_INCLUDED == TRUE) + /* security API events */ + bta_dm_bond, /* BTA_DM_API_BOND_EVT */ + bta_dm_bond_cancel, /* BTA_DM_API_BOND_CANCEL_EVT */ + bta_dm_set_pin_type, /* BTA_DM_API_SET_PIN_TYPE_EVT */ + bta_dm_pin_reply, /* BTA_DM_API_PIN_REPLY_EVT */ +#endif ///SMP_INCLUDED == TRUE +#if (BTA_DM_PM_INCLUDED == TRUE) + /* power manger events */ + bta_dm_pm_btm_status, /* BTA_DM_PM_BTM_STATUS_EVT */ + bta_dm_pm_timer, /* BTA_DM_PM_TIMER_EVT */ +#endif /* #if (BTA_DM_PM_INCLUDED == TRUE) */ +#if (BTA_DM_QOS_INCLUDED == TRUE) + /* Quality of Service set events */ + bta_dm_set_qos, /* BTA_DM_API_QOS_SET_EVT */ +#endif /* #if (BTA_DM_QOS_INCLUDED == TRUE) */ + /* simple pairing events */ +#if (SMP_INCLUDED == TRUE) +#if (CLASSIC_BT_INCLUDED == TRUE) + bta_dm_confirm, /* BTA_DM_API_CONFIRM_EVT */ + bta_dm_key_req, /* BTA_DM_API_KEY_REQ_EVT */ +#endif /* (CLASSIC_BT_INCLUDED == TRUE) */ + bta_dm_set_encryption, /* BTA_DM_API_SET_ENCRYPTION_EVT */ +#endif /* (SMP_INCLUDED == TRUE) */ +#if (BTM_OOB_INCLUDED == TRUE && SMP_INCLUDED == TRUE) + bta_dm_loc_oob, /* BTA_DM_API_LOC_OOB_EVT */ + bta_dm_oob_reply, /* BTA_DM_API_OOB_REPLY_EVT */ + bta_dm_sc_oob_reply, /* BTA_DM_API_SC_OOB_REPLY_EVT */ + bta_dm_sc_create_oob_data, /* BTA_DM_API_SC_CR_OOB_DATA_EVT */ + bta_dm_ci_io_req_act, /* BTA_DM_CI_IO_REQ_EVT */ + bta_dm_ci_rmt_oob_act, /* BTA_DM_CI_RMT_OOB_EVT */ +#endif /* BTM_OOB_INCLUDED */ + + +#if BLE_INCLUDED == TRUE +#if SMP_INCLUDED == TRUE + bta_dm_add_blekey, /* BTA_DM_API_ADD_BLEKEY_EVT */ + bta_dm_add_ble_device, /* BTA_DM_API_ADD_BLEDEVICE_EVT */ + bta_dm_ble_passkey_reply, /* BTA_DM_API_BLE_PASSKEY_REPLY_EVT */ + bta_dm_ble_set_static_passkey, /* BTA_DM_API_BLE_SET_STATIC_PASSKEY_EVT */ + bta_dm_ble_confirm_reply, /* BTA_DM_API_BLE_CONFIRM_REPLY_EVT */ + bta_dm_security_grant, +#endif ///SMP_INCLUDED == TRUE + bta_dm_ble_set_bg_conn_type, + bta_dm_ble_set_conn_params, /* BTA_DM_API_BLE_CONN_PARAM_EVT */ + bta_dm_ble_set_conn_scan_params, /* BTA_DM_API_BLE_CONN_SCAN_PARAM_EVT */ + bta_dm_ble_set_scan_params, /* BTA_DM_API_BLE_SCAN_PARAM_EVT */ + bta_dm_ble_set_scan_fil_params, /* BTA_DM_API_BLE_SCAN_FIL_PARAM_EVT */ + bta_dm_ble_observe, /* BTA_DM_API_BLE_OBSERVE_EVT */ + bta_dm_ble_scan, /* BTA_DM_API_BLE_SCAN_EVT */ + bta_dm_ble_update_conn_params, /* BTA_DM_API_UPDATE_CONN_PARAM_EVT */ + /* This handler function added by + Yulong at 2016/9/9 to support the + random address setting for the APP */ + bta_dm_ble_set_rand_address, /* BTA_DM_API_SET_RAND_ADDR_EVT*/ + bta_dm_ble_clear_rand_address, /* BTA_DM_API_CLEAR_RAND_ADDR_EVT */ + /* This handler function added by + Yulong at 2016/10/19 to support + stop the ble advertising setting + by the APP */ + bta_dm_ble_stop_advertising, /* BTA_DM_API_BLE_STOP_ADV_EVT */ +#if BLE_PRIVACY_SPT == TRUE + bta_dm_ble_config_local_privacy, /* BTA_DM_API_LOCAL_PRIVACY_EVT */ +#endif + bta_dm_ble_config_local_icon, /* BTA_DM_API_LOCAL_ICON_EVT */ + bta_dm_ble_set_adv_params, /* BTA_DM_API_BLE_ADV_PARAM_EVT */ + bta_dm_ble_set_adv_params_all, /* BTA_DM_API_BLE_ADV_PARAM_All_EVT */ + bta_dm_ble_set_adv_config, /* BTA_DM_API_BLE_SET_ADV_CONFIG_EVT */ + /* New function to allow set raw adv + data to HCI */ + bta_dm_ble_set_adv_config_raw, /* BTA_DM_API_BLE_SET_ADV_CONFIG_RAW_EVT */ + bta_dm_ble_set_scan_rsp, /* BTA_DM_API_BLE_SET_SCAN_RSP_EVT */ + /* New function to allow set raw scan + response data to HCI */ + bta_dm_ble_set_scan_rsp_raw, /* BTA_DM_API_BLE_SET_SCAN_RSP_RAW_EVT */ + bta_dm_ble_broadcast, /* BTA_DM_API_BLE_BROADCAST_EVT */ + bta_dm_ble_set_data_length, /* BTA_DM_API_SET_DATA_LENGTH_EVT */ + bta_dm_ble_set_long_adv, /* BTA_DM_API_BLE_SET_LONG_ADV_EVT */ +#if BLE_ANDROID_CONTROLLER_SCAN_FILTER == TRUE + bta_dm_cfg_filter_cond, /* BTA_DM_API_CFG_FILTER_COND_EVT */ + bta_dm_scan_filter_param_setup, /* BTA_DM_API_SCAN_FILTER_SETUP_EVT */ + bta_dm_enable_scan_filter, /* BTA_DM_API_SCAN_FILTER_ENABLE_EVT */ +#endif + bta_dm_ble_multi_adv_enb, /* BTA_DM_API_BLE_MULTI_ADV_ENB_EVT */ + bta_dm_ble_multi_adv_upd_param, /* BTA_DM_API_BLE_MULTI_ADV_PARAM_UPD_EVT */ + bta_dm_ble_multi_adv_data, /* BTA_DM_API_BLE_MULTI_ADV_DATA_EVT */ + btm_dm_ble_multi_adv_disable, /* BTA_DM_API_BLE_MULTI_ADV_DISABLE_EVT */ + bta_dm_ble_setup_storage, /* BTA_DM_API_BLE_SETUP_STORAGE_EVT */ + bta_dm_ble_enable_batch_scan, /* BTA_DM_API_BLE_ENABLE_BATCH_SCAN_EVT */ + bta_dm_ble_disable_batch_scan, /* BTA_DM_API_BLE_DISABLE_BATCH_SCAN_EVT */ + bta_dm_ble_read_scan_reports, /* BTA_DM_API_BLE_READ_SCAN_REPORTS_EVT */ + bta_dm_ble_track_advertiser, /* BTA_DM_API_BLE_TRACK_ADVERTISER_EVT */ + bta_dm_ble_get_energy_info, /* BTA_DM_API_BLE_ENERGY_INFO_EVT */ + bta_dm_ble_disconnect, /* BTA_DM_API_BLE_DISCONNECT_EVT */ +#endif + + bta_dm_enable_test_mode, /* BTA_DM_API_ENABLE_TEST_MODE_EVT */ + bta_dm_disable_test_mode, /* BTA_DM_API_DISABLE_TEST_MODE_EVT */ + bta_dm_execute_callback, /* BTA_DM_API_EXECUTE_CBACK_EVT */ + + bta_dm_remove_all_acl, /* BTA_DM_API_REMOVE_ALL_ACL_EVT */ + bta_dm_remove_device, /* BTA_DM_API_REMOVE_DEVICE_EVT */ + bta_dm_ble_set_channels, /* BTA_DM_API_BLE_SET_CHANNELS_EVT */ + bta_dm_update_white_list, /* BTA_DM_API_UPDATE_WHITE_LIST_EVT */ + bta_dm_clear_white_list, /* BTA_DM_API_CLEAR_WHITE_LIST_EVT */ + bta_dm_ble_read_adv_tx_power, /* BTA_DM_API_BLE_READ_ADV_TX_POWER_EVT */ + bta_dm_read_rssi, /* BTA_DM_API_READ_RSSI_EVT */ +#if BLE_INCLUDED == TRUE + bta_dm_ble_update_duplicate_exceptional_list,/* BTA_DM_API_UPDATE_DUPLICATE_EXCEPTIONAL_LIST_EVT */ +#endif +#if (BLE_50_FEATURE_SUPPORT == TRUE) + bta_dm_ble_gap_read_phy, /* BTA_DM_API_READ_PHY_EVT */ + bta_dm_ble_gap_set_prefer_default_phy, /* BTA_DM_API_SET_PER_DEF_PHY_EVT */ + bta_dm_ble_gap_set_prefer_phy, /* BTA_DM_API_SET_PER_PHY_EVT */ + bta_dm_ble_gap_ext_adv_set_rand_addr, /* BTA_DM_API_SET_EXT_ADV_RAND_ADDR_EVT */ + bta_dm_ble_gap_ext_adv_set_params, /* BTA_DM_API_SET_EXT_ADV_PARAMS_EVT */ + bta_dm_ble_gap_config_ext_adv_data_raw, /* BTA_DM_API_CFG_ADV_DATA_RAW_EVT */ + bta_dm_ble_gap_start_ext_adv, /* BTA_DM_API_EXT_ADV_ENABLE_EVT */ + bta_dm_ble_gap_ext_adv_set_remove, /* BTA_DM_API_EXT_ADV_SET_REMOVE_EVT */ + bta_dm_ble_gap_ext_adv_set_clear, /* BTA_DM_API_EXT_ADV_SET_CLEAR_EVT */ + bta_dm_ble_gap_periodic_adv_set_params, /* BTA_DM_API_PERIODIC_ADV_SET_PARAMS_EVT */ + bta_dm_ble_gap_periodic_adv_cfg_data_raw, /* BTA_DM_API_PERIODIC_ADV_CFG_DATA_EVT */ + bta_dm_ble_gap_periodic_adv_enable, /* BTA_DM_API_PERIODIC_ADV_ENABLE_EVT */ + bta_dm_ble_gap_periodic_adv_create_sync, /* BTA_DM_API_PERIODIC_ADV_SYNC_EVT */ + bta_dm_ble_gap_periodic_adv_sync_cancel, /* BTA_DM_API_PERIODIC_ADV_SYNC_CANCEL_EVT */ + bta_dm_ble_gap_periodic_adv_sync_terminate, /* BTA_DM_API_PERIODIC_ADV_SYNC_TERMINATE_EVT */ + bta_dm_ble_gap_periodic_adv_add_dev_to_list, /* BTA_DM_API_PERIODIC_ADV_ADD_DEV_TO_LSIT_EVT */ + bta_dm_ble_gap_periodic_adv_remove_dev_from_list, /* BTA_DM_API_PERIODIC_ADV_REMOVE_DEV_FROM_LSIT_EVT */ + bta_dm_ble_gap_periodic_adv_clear_dev, /* BTA_DM_API_PERIODIC_ADV_CLEAR_DEV_EVT */ + bta_dm_ble_gap_set_ext_scan_params, /* BTA_DM_API_SET_EXT_SCAN_PARAMS_EVT */ + bta_dm_ble_gap_ext_scan, /* BTA_DM_API_START_EXT_SCAN_EVT */ + bta_dm_ble_gap_set_prefer_ext_conn_params, /* BTA_DM_API_SET_PERF_EXT_CONN_PARAMS_EVT */ + NULL, /* BTA_DM_API_EXT_CONN_EVT */ + bta_dm_ble_gap_dtm_enhance_tx_start, /* BTA_DM_API_DTM_ENH_TX_START_EVT */ + bta_dm_ble_gap_dtm_enhance_rx_start, /* BTA_DM_API_DTM_ENH_RX_START_EVT */ +#endif // #if (BLE_50_FEATURE_SUPPORT == TRUE) +#if (BLE_FEAT_PERIODIC_ADV_SYNC_TRANSFER == TRUE) + bta_dm_ble_gap_periodic_adv_recv_enable, /* BTA_DM_API_PERIODIC_ADV_RECV_ENABLE_EVT */ + bta_dm_ble_gap_periodic_adv_sync_trans, /* BTA_DM_API_PERIODIC_ADV_SYNC_TRANS_EVT */ + bta_dm_ble_gap_periodic_adv_set_info_trans, /* BTA_DM_API_PERIODIC_ADV_SET_INFO_TRANS_EVT */ + bta_dm_ble_gap_set_periodic_adv_sync_trans_params, /* BTA_DM_API_SET_PERIODIC_ADV_SYNC_TRANS_PARAMS_EVT */ +#endif // #if (BLE_FEAT_PERIODIC_ADV_SYNC_TRANSFER == TRUE) +#if BLE_INCLUDED == TRUE + bta_dm_ble_gap_dtm_tx_start, /* BTA_DM_API_DTM_TX_START_EVT */ + bta_dm_ble_gap_dtm_rx_start, /* BTA_DM_API_DTM_RX_START_EVT */ + bta_dm_ble_gap_dtm_stop, /* BTA_DM_API_DTM_STOP_EVT */ + bta_dm_ble_gap_clear_adv, /* BTA_DM_API_BLE_CLEAR_ADV_EVT */ +#endif +}; + + + +/* state machine action enumeration list */ +enum { + BTA_DM_API_SEARCH, /* 0 bta_dm_search_start */ + BTA_DM_API_SEARCH_CANCEL, /* 1 bta_dm_search_cancel */ +#if (SDP_INCLUDED == TRUE) + BTA_DM_API_DISCOVER, /* 2 bta_dm_discover */ +#endif ///SDP_INCLUDED == TRUE + BTA_DM_INQUIRY_CMPL, /* 3 bta_dm_inq_cmpl */ + BTA_DM_REMT_NAME, /* 4 bta_dm_rmt_name */ +#if (SDP_INCLUDED == TRUE) + BTA_DM_SDP_RESULT, /* 5 bta_dm_sdp_result */ +#endif ///SDP_INCLUDED == TRUE + BTA_DM_SEARCH_CMPL, /* 6 bta_dm_search_cmpl*/ +#if (SDP_INCLUDED == TRUE) + BTA_DM_FREE_SDP_DB, /* 7 bta_dm_free_sdp_db */ +#endif ///SDP_INCLUDED == TRUE + BTA_DM_DISC_RESULT, /* 8 bta_dm_disc_result */ + BTA_DM_SEARCH_RESULT, /* 9 bta_dm_search_result */ + BTA_DM_QUEUE_SEARCH, /* 10 bta_dm_queue_search */ +#if (SDP_INCLUDED == TRUE) + BTA_DM_QUEUE_DISC, /* 11 bta_dm_queue_disc */ +#endif ///SDP_INCLUDED == TRUE + BTA_DM_SEARCH_CLEAR_QUEUE, /* 12 bta_dm_search_clear_queue */ + BTA_DM_SEARCH_CANCEL_CMPL, /* 13 bta_dm_search_cancel_cmpl */ + BTA_DM_SEARCH_CANCEL_NOTIFY, /* 14 bta_dm_search_cancel_notify */ +#if (SDP_INCLUDED == TRUE) + BTA_DM_SEARCH_CANCEL_TRANSAC_CMPL, /* 15 bta_dm_search_cancel_transac_cmpl */ +#endif ///SDP_INCLUDED == TRUE + BTA_DM_DISC_RMT_NAME, /* 16 bta_dm_disc_rmt_name */ +#if (SDP_INCLUDED == TRUE) + BTA_DM_API_DI_DISCOVER, /* 17 bta_dm_di_disc */ +#endif ///SDP_INCLUDED == TRUE +#if BLE_INCLUDED == TRUE && SDP_INCLUDED == TRUE && BTA_GATT_INCLUDED == TRUE && GATTC_INCLUDED == TRUE + BTA_DM_CLOSE_GATT_CONN, /* 18 bta_dm_close_gatt_conn */ +#endif /* BLE_INCLUDED == TRUE && SDP_INCLUDED == TRUE && BTA_GATT_INCLUDED == TRUE && GATTC_INCLUDED == TRUE */ + BTA_DM_SEARCH_NUM_ACTIONS /* 19 */ +}; + + +/* action function list */ +const tBTA_DM_ACTION bta_dm_search_action[] = { + + bta_dm_search_start, /* 0 BTA_DM_API_SEARCH */ + bta_dm_search_cancel, /* 1 BTA_DM_API_SEARCH_CANCEL */ +#if (SDP_INCLUDED == TRUE) + bta_dm_discover, /* 2 BTA_DM_API_DISCOVER */ +#endif ///SDP_INCLUDED == TRUE + bta_dm_inq_cmpl, /* 3 BTA_DM_INQUIRY_CMPL */ + bta_dm_rmt_name, /* 4 BTA_DM_REMT_NAME */ +#if (SDP_INCLUDED == TRUE) + bta_dm_sdp_result, /* 5 BTA_DM_SDP_RESULT */ +#endif ///SDP_INCLUDED == TRUE + bta_dm_search_cmpl, /* 6 BTA_DM_SEARCH_CMPL */ +#if (SDP_INCLUDED == TRUE) + bta_dm_free_sdp_db, /* 7 BTA_DM_FREE_SDP_DB */ +#endif ///SDP_INCLUDED == TRUE + bta_dm_disc_result, /* 8 BTA_DM_DISC_RESULT */ + bta_dm_search_result, /* 9 BTA_DM_SEARCH_RESULT */ + bta_dm_queue_search, /* 10 BTA_DM_QUEUE_SEARCH */ +#if (SDP_INCLUDED == TRUE) + bta_dm_queue_disc, /* 11 BTA_DM_QUEUE_DISC */ +#endif ///SDP_INCLUDED == TRUE + bta_dm_search_clear_queue, /* 12 BTA_DM_SEARCH_CLEAR_QUEUE */ + bta_dm_search_cancel_cmpl, /* 13 BTA_DM_SEARCH_CANCEL_CMPL */ + bta_dm_search_cancel_notify, /* 14 BTA_DM_SEARCH_CANCEL_NOTIFY */ +#if (SDP_INCLUDED == TRUE) + bta_dm_search_cancel_transac_cmpl, /* 15 BTA_DM_SEARCH_CANCEL_TRANSAC_CMPL */ +#endif ///SDP_INCLUDED == TRUE + bta_dm_disc_rmt_name, /* 16 BTA_DM_DISC_RMT_NAME */ +#if (SDP_INCLUDED == TRUE) + bta_dm_di_disc /* 17 BTA_DM_API_DI_DISCOVER */ +#endif ///SDP_INCLUDED == TRUE +#if BLE_INCLUDED == TRUE && SDP_INCLUDED == TRUE && BTA_GATT_INCLUDED == TRUE && GATTC_INCLUDED == TRUE + , bta_dm_close_gatt_conn +#endif +}; + +#define BTA_DM_SEARCH_IGNORE BTA_DM_SEARCH_NUM_ACTIONS +/* state table information */ +#define BTA_DM_SEARCH_ACTIONS 2 /* number of actions */ +#define BTA_DM_SEARCH_NEXT_STATE 2 /* position of next state */ +#define BTA_DM_SEARCH_NUM_COLS 3 /* number of columns in state tables */ + + + +/* state table for listen state */ +const UINT8 bta_dm_search_idle_st_table[][BTA_DM_SEARCH_NUM_COLS] = { + + /* Event Action 1 Action 2 Next State */ + /* API_SEARCH */ {BTA_DM_API_SEARCH, BTA_DM_SEARCH_IGNORE, BTA_DM_SEARCH_ACTIVE}, + /* API_SEARCH_CANCEL */ {BTA_DM_SEARCH_CANCEL_NOTIFY, BTA_DM_SEARCH_IGNORE, BTA_DM_SEARCH_IDLE}, +#if (SDP_INCLUDED == TRUE) + /* API_SEARCH_DISC */ {BTA_DM_API_DISCOVER, BTA_DM_SEARCH_IGNORE, BTA_DM_DISCOVER_ACTIVE}, +#endif ///SDP_INCLUDED == TRUE + /* INQUIRY_CMPL */ {BTA_DM_SEARCH_IGNORE, BTA_DM_SEARCH_IGNORE, BTA_DM_SEARCH_IDLE}, + /* REMT_NAME_EVT */ {BTA_DM_SEARCH_IGNORE, BTA_DM_SEARCH_IGNORE, BTA_DM_SEARCH_IDLE}, +#if (SDP_INCLUDED == TRUE) + /* SDP_RESULT_EVT */ {BTA_DM_FREE_SDP_DB, BTA_DM_SEARCH_IGNORE, BTA_DM_SEARCH_IDLE}, +#endif ///SDP_INCLUDED == TRUE + /* SEARCH_CMPL_EVT */ {BTA_DM_SEARCH_IGNORE, BTA_DM_SEARCH_IGNORE, BTA_DM_SEARCH_IDLE}, + /* DISCV_RES_EVT */ {BTA_DM_SEARCH_IGNORE, BTA_DM_SEARCH_IGNORE, BTA_DM_SEARCH_IDLE}, +#if (SDP_INCLUDED == TRUE) + /* API_DI_DISCOVER_EVT */ {BTA_DM_API_DI_DISCOVER, BTA_DM_SEARCH_IGNORE, BTA_DM_SEARCH_ACTIVE}, +#endif ///SDP_INCLUDED == TRUE +#if BLE_INCLUDED == TRUE && SDP_INCLUDED == TRUE && BTA_GATT_INCLUDED == TRUE && GATTC_INCLUDED == TRUE + // #if BLE_INCLUDED == TRUE + /* DISC_CLOSE_TOUT_EVT */ {BTA_DM_CLOSE_GATT_CONN, BTA_DM_SEARCH_IGNORE, BTA_DM_SEARCH_IDLE}, +#endif +}; +const UINT8 bta_dm_search_search_active_st_table[][BTA_DM_SEARCH_NUM_COLS] = { + + /* Event Action 1 Action 2 Next State */ + /* API_SEARCH */ {BTA_DM_SEARCH_IGNORE, BTA_DM_SEARCH_IGNORE, BTA_DM_SEARCH_ACTIVE}, + /* API_SEARCH_CANCEL */ {BTA_DM_API_SEARCH_CANCEL, BTA_DM_SEARCH_IGNORE, BTA_DM_SEARCH_CANCELLING}, + /* API_SEARCH_DISC */ {BTA_DM_SEARCH_IGNORE, BTA_DM_SEARCH_IGNORE, BTA_DM_SEARCH_ACTIVE}, + /* INQUIRY_CMPL */ {BTA_DM_INQUIRY_CMPL, BTA_DM_SEARCH_IGNORE, BTA_DM_SEARCH_ACTIVE}, + /* REMT_NAME_EVT */ {BTA_DM_REMT_NAME, BTA_DM_SEARCH_IGNORE, BTA_DM_SEARCH_ACTIVE}, +#if (SDP_INCLUDED == TRUE) + /* SDP_RESULT_EVT */ {BTA_DM_SDP_RESULT, BTA_DM_SEARCH_IGNORE, BTA_DM_SEARCH_ACTIVE}, +#endif ///SDP_INCLUDED == TRUE + /* SEARCH_CMPL_EVT */ {BTA_DM_SEARCH_CMPL, BTA_DM_SEARCH_IGNORE, BTA_DM_SEARCH_IDLE}, + /* DISCV_RES_EVT */ {BTA_DM_SEARCH_RESULT, BTA_DM_SEARCH_IGNORE, BTA_DM_SEARCH_ACTIVE}, + /* API_DI_DISCOVER_EVT */ {BTA_DM_SEARCH_IGNORE, BTA_DM_SEARCH_IGNORE, BTA_DM_SEARCH_ACTIVE} +#if BLE_INCLUDED == TRUE && SDP_INCLUDED == TRUE && BTA_GATT_INCLUDED == TRUE && GATTC_INCLUDED == TRUE + // #if BLE_INCLUDED == TRUE + /* DISC_CLOSE_TOUT_EVT */ , {BTA_DM_CLOSE_GATT_CONN, BTA_DM_SEARCH_IGNORE, BTA_DM_SEARCH_ACTIVE} +#endif + +}; + +const UINT8 bta_dm_search_search_cancelling_st_table[][BTA_DM_SEARCH_NUM_COLS] = { + + /* Event Action 1 Action 2 Next State */ + /* API_SEARCH */ {BTA_DM_QUEUE_SEARCH, BTA_DM_SEARCH_IGNORE, BTA_DM_SEARCH_CANCELLING}, + /* API_SEARCH_CANCEL */ {BTA_DM_SEARCH_CLEAR_QUEUE, BTA_DM_SEARCH_CANCEL_NOTIFY, BTA_DM_SEARCH_CANCELLING}, +#if (SDP_INCLUDED == TRUE) + /* API_SEARCH_DISC */ {BTA_DM_QUEUE_DISC, BTA_DM_SEARCH_IGNORE, BTA_DM_SEARCH_CANCELLING}, +#endif ///SDP_INCLUDED == TRUE + /* INQUIRY_CMPL */ {BTA_DM_SEARCH_CANCEL_CMPL, BTA_DM_SEARCH_IGNORE, BTA_DM_SEARCH_IDLE}, +#if (SDP_INCLUDED == TRUE) + /* REMT_NAME_EVT */ {BTA_DM_SEARCH_CANCEL_TRANSAC_CMPL, BTA_DM_SEARCH_CANCEL_CMPL, BTA_DM_SEARCH_IDLE}, + /* SDP_RESULT_EVT */ {BTA_DM_SEARCH_CANCEL_TRANSAC_CMPL, BTA_DM_SEARCH_CANCEL_CMPL, BTA_DM_SEARCH_IDLE}, + /* SEARCH_CMPL_EVT */ {BTA_DM_SEARCH_CANCEL_TRANSAC_CMPL, BTA_DM_SEARCH_CANCEL_CMPL, BTA_DM_SEARCH_IDLE}, + /* DISCV_RES_EVT */ {BTA_DM_SEARCH_CANCEL_TRANSAC_CMPL, BTA_DM_SEARCH_CANCEL_CMPL, BTA_DM_SEARCH_IDLE}, +#endif ///SDP_INCLUDED == TRUE + /* API_DI_DISCOVER_EVT */ {BTA_DM_SEARCH_IGNORE, BTA_DM_SEARCH_IGNORE, BTA_DM_SEARCH_CANCELLING} +#if BLE_INCLUDED == TRUE + /* DISC_CLOSE_TOUT_EVT */ , {BTA_DM_SEARCH_IGNORE, BTA_DM_SEARCH_IGNORE, BTA_DM_SEARCH_CANCELLING} +#endif + +}; + +const UINT8 bta_dm_search_disc_active_st_table[][BTA_DM_SEARCH_NUM_COLS] = { + + /* Event Action 1 Action 2 Next State */ + /* API_SEARCH */ {BTA_DM_SEARCH_IGNORE, BTA_DM_SEARCH_IGNORE, BTA_DM_DISCOVER_ACTIVE}, + /* API_SEARCH_CANCEL */ {BTA_DM_SEARCH_CANCEL_NOTIFY, BTA_DM_SEARCH_IGNORE, BTA_DM_SEARCH_CANCELLING}, + /* API_SEARCH_DISC */ {BTA_DM_SEARCH_IGNORE, BTA_DM_SEARCH_IGNORE, BTA_DM_DISCOVER_ACTIVE}, + /* INQUIRY_CMPL */ {BTA_DM_SEARCH_IGNORE, BTA_DM_SEARCH_IGNORE, BTA_DM_DISCOVER_ACTIVE}, + /* REMT_NAME_EVT */ {BTA_DM_DISC_RMT_NAME, BTA_DM_SEARCH_IGNORE, BTA_DM_DISCOVER_ACTIVE}, +#if (SDP_INCLUDED == TRUE) + /* SDP_RESULT_EVT */ {BTA_DM_SDP_RESULT, BTA_DM_SEARCH_IGNORE, BTA_DM_DISCOVER_ACTIVE}, +#endif ///SDP_INCLUDED == TRUE + /* SEARCH_CMPL_EVT */ {BTA_DM_SEARCH_CMPL, BTA_DM_SEARCH_IGNORE, BTA_DM_SEARCH_IDLE}, + /* DISCV_RES_EVT */ {BTA_DM_DISC_RESULT, BTA_DM_SEARCH_IGNORE, BTA_DM_DISCOVER_ACTIVE}, + /* API_DI_DISCOVER_EVT */ {BTA_DM_SEARCH_IGNORE, BTA_DM_SEARCH_IGNORE, BTA_DM_DISCOVER_ACTIVE} + +#if BLE_INCLUDED == TRUE + /* DISC_CLOSE_TOUT_EVT */ , {BTA_DM_SEARCH_IGNORE, BTA_DM_SEARCH_IGNORE, BTA_DM_DISCOVER_ACTIVE} +#endif +}; + +typedef const UINT8 (*tBTA_DM_ST_TBL)[BTA_DM_SEARCH_NUM_COLS]; + +/* state table */ +const tBTA_DM_ST_TBL bta_dm_search_st_tbl[] = { + bta_dm_search_idle_st_table, + bta_dm_search_search_active_st_table, + bta_dm_search_search_cancelling_st_table, + bta_dm_search_disc_active_st_table +}; + + +/******************************************************************************* +** +** Function bta_dm_sm_disable +** +** Description unregister BTA DM +** +** +** Returns void +** +*******************************************************************************/ +void bta_dm_sm_disable(void) +{ + bta_sys_deregister( BTA_ID_DM ); +} + +void bta_dm_sm_deinit(void) +{ + memset(&bta_dm_cb, 0, sizeof(tBTA_DM_CB)); + memset(&bta_dm_search_cb, 0, sizeof(tBTA_DM_SEARCH_CB)); + memset(&bta_dm_di_cb, 0, sizeof(tBTA_DM_DI_CB)); +#if BTA_DYNAMIC_MEMORY + FREE_AND_RESET(bta_dm_cb_ptr); + FREE_AND_RESET(bta_dm_search_cb_ptr); + FREE_AND_RESET(bta_dm_di_cb_ptr); +#endif /* #if BTA_DYNAMIC_MEMORY */ +} + + +/******************************************************************************* +** +** Function bta_dm_sm_execute +** +** Description State machine event handling function for DM +** +** +** Returns void +** +*******************************************************************************/ +BOOLEAN bta_dm_sm_execute(BT_HDR *p_msg) +{ + UINT16 event = p_msg->event & 0x00ff; + + APPL_TRACE_EVENT("bta_dm_sm_execute event:0x%x", event); + + /* execute action functions */ + if (event < BTA_DM_NUM_ACTIONS) { + (*bta_dm_action[event])( (tBTA_DM_MSG *) p_msg); + } + + return TRUE; +} + +void BTA_DmCoexEventTrigger(uint32_t event) +{ + switch(event) { + case BTA_COEX_EVT_SCAN_STARTED: + case BTA_COEX_EVT_SCAN_STOPPED: + case BTA_COEX_EVT_SNIFF_ENTER: + case BTA_COEX_EVT_SNIFF_EXIT: + case BTA_COEX_EVT_A2DP_PAUSED_ENTER: + case BTA_COEX_EVT_A2DP_PAUSED_EXIT: + case BTA_COEX_EVT_ACL_CONNECTED: + case BTA_COEX_EVT_ACL_DISCONNECTED: + break; + case BTA_COEX_EVT_STREAMING_STARTED: +#if (ESP_COEX_VSC_INCLUDED == TRUE) + BTM_ConfigCoexStatus(BTM_COEX_OP_SET, BTM_COEX_TYPE_BT, BTM_COEX_BT_ST_A2DP_STREAMING); + BTM_ConfigCoexStatus(BTM_COEX_OP_CLEAR, BTM_COEX_TYPE_BT, BTM_COEX_BT_ST_A2DP_PAUSED); +#endif + break; + case BTA_COEX_EVT_STREAMING_STOPPED: +#if (ESP_COEX_VSC_INCLUDED == TRUE) + BTM_ConfigCoexStatus(BTM_COEX_OP_CLEAR, BTM_COEX_TYPE_BT, BTM_COEX_BT_ST_A2DP_STREAMING); + BTM_ConfigCoexStatus(BTM_COEX_OP_CLEAR, BTM_COEX_TYPE_BT, BTM_COEX_BT_ST_A2DP_PAUSED); +#endif + break; + default: + break; + } +} + +/******************************************************************************* +** +** Function bta_dm_sm_search_disable +** +** Description unregister BTA SEARCH DM +** +** +** Returns void +** +*******************************************************************************/ +void bta_dm_search_sm_disable(void) +{ + bta_sys_deregister( BTA_ID_DM_SEARCH ); + +} + + +/******************************************************************************* +** +** Function bta_dm_search_sm_execute +** +** Description State machine event handling function for DM +** +** +** Returns void +** +*******************************************************************************/ +BOOLEAN bta_dm_search_sm_execute(BT_HDR *p_msg) +{ + tBTA_DM_ST_TBL state_table; + UINT8 action; + int i; + + APPL_TRACE_EVENT("bta_dm_search_sm_execute state:%d, event:0x%x", + bta_dm_search_cb.state, p_msg->event); + + /* look up the state table for the current state */ + state_table = bta_dm_search_st_tbl[bta_dm_search_cb.state]; + + bta_dm_search_cb.state = state_table[p_msg->event & 0x00ff][BTA_DM_SEARCH_NEXT_STATE]; + + + /* execute action functions */ + for (i = 0; i < BTA_DM_SEARCH_ACTIONS; i++) { + if ((action = state_table[p_msg->event & 0x00ff][i]) != BTA_DM_SEARCH_IGNORE) { + (*bta_dm_search_action[action])( (tBTA_DM_MSG *) p_msg); + } else { + break; + } + } + return TRUE; +} diff --git a/lib/bt/host/bluedroid/bta/dm/bta_dm_pm.c b/lib/bt/host/bluedroid/bta/dm/bta_dm_pm.c new file mode 100644 index 00000000..149b4fac --- /dev/null +++ b/lib/bt/host/bluedroid/bta/dm/bta_dm_pm.c @@ -0,0 +1,1172 @@ +/****************************************************************************** + * + * Copyright (C) 2003-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains the action functions for device manager state + * machine. + * + ******************************************************************************/ + +#include +#include + +#include "bta/bta_sys.h" +#include "bta/bta_api.h" +#include "bta_dm_int.h" +#include "stack/btm_api.h" +#include "osi/allocator.h" + +#if BTA_DYNAMIC_MEMORY == FALSE +tBTA_DM_CONNECTED_SRVCS bta_dm_conn_srvcs; +#else +tBTA_DM_CONNECTED_SRVCS *bta_dm_conn_srvcs_ptr; +#endif + +#if (BTA_DM_PM_INCLUDED == TRUE) +static void bta_dm_pm_cback(tBTA_SYS_CONN_STATUS status, UINT8 id, UINT8 app_id, BD_ADDR peer_addr); +static void bta_dm_pm_set_mode(BD_ADDR peer_addr, tBTA_DM_PM_ACTION pm_mode, + tBTA_DM_PM_REQ pm_req); +static void bta_dm_pm_timer_cback(void *p_tle); +static void bta_dm_pm_btm_cback(BD_ADDR bd_addr, tBTM_PM_STATUS status, UINT16 value, UINT8 hci_status); +static BOOLEAN bta_dm_pm_park(BD_ADDR peer_addr); +static BOOLEAN bta_dm_pm_sniff(tBTA_DM_PEER_DEVICE *p_peer_dev, UINT8 index); +static BOOLEAN bta_dm_pm_is_sco_active (void); +static void bta_dm_pm_hid_check(BOOLEAN bScoActive); +static void bta_dm_pm_set_sniff_policy(tBTA_DM_PEER_DEVICE *p_dev, BOOLEAN bDisable); +static void bta_dm_pm_stop_timer_by_index(tBTA_PM_TIMER *p_timer, + UINT8 timer_idx); + +#if (BTM_SSR_INCLUDED == TRUE) +#if (defined BTA_HH_INCLUDED && BTA_HH_INCLUDED == TRUE) +#include "bta_hh_int.h" +/* BTA_DM_PM_SSR1 will be dedicated for HH SSR setting entry, no other profile can use it */ +#define BTA_DM_PM_SSR_HH BTA_DM_PM_SSR1 +#endif /* (defined BTA_HH_INCLUDED && BTA_HH_INCLUDED == TRUE) */ +static void bta_dm_pm_ssr(BD_ADDR peer_addr); +#endif /* (BTM_SSR_INCLUDED == TRUE) */ + +/******************************************************************************* +** +** Function bta_dm_init_pm +** +** Description Initializes the BT low power manager +** +** +** Returns void +** +*******************************************************************************/ +void bta_dm_init_pm(void) +{ + memset(&bta_dm_conn_srvcs, 0x00, sizeof(bta_dm_conn_srvcs)); + + /* if there are no power manger entries, so not register */ + if (p_bta_dm_pm_cfg[0].app_id != 0) { + bta_sys_pm_register((tBTA_SYS_CONN_CBACK *)bta_dm_pm_cback); + + BTM_PmRegister((BTM_PM_REG_SET | BTM_PM_REG_NOTIF), &bta_dm_cb.pm_id, + bta_dm_pm_btm_cback); + } + + /* Need to initialize all PM timer service IDs */ + for (int i = 0; i < BTA_DM_NUM_PM_TIMER; i++) { + for (int j = 0; j < BTA_DM_PM_MODE_TIMER_MAX; j++) { + bta_dm_cb.pm_timer[i].srvc_id[j] = BTA_ID_MAX; + } + } +} + + +/******************************************************************************* +** +** Function bta_dm_disable_pm +** +** Description Disable PM +** +** +** Returns void +** +*******************************************************************************/ +void bta_dm_disable_pm(void) +{ + BTM_PmRegister( BTM_PM_DEREG, &bta_dm_cb.pm_id, NULL); + + /* + * Deregister the PM callback from the system handling to prevent + * re-enabling the PM timers after this call if the callback is invoked. + */ + bta_sys_pm_register((tBTA_SYS_CONN_CBACK *)NULL); + + /* Need to stop all active timers. */ + for (int i = 0; i < BTA_DM_NUM_PM_TIMER; i++) { + for (int j = 0; j < BTA_DM_PM_MODE_TIMER_MAX; j++) { + bta_dm_pm_stop_timer_by_index(&bta_dm_cb.pm_timer[i], j); + bta_dm_cb.pm_timer[i].pm_action[j] = BTA_DM_PM_NO_ACTION; + } + } +} + +/******************************************************************************* +** +** Function bta_dm_pm_stop_timer +** +** Description stop a PM timer +** +** +** Returns void +** +*******************************************************************************/ +static void bta_dm_pm_stop_timer(BD_ADDR peer_addr) +{ + APPL_TRACE_DEBUG("%s: ", __func__); + + for (int i = 0; i < BTA_DM_NUM_PM_TIMER; i++) { + if (bta_dm_cb.pm_timer[i].in_use && !bdcmp(bta_dm_cb.pm_timer[i].peer_bdaddr, peer_addr)) { + for (int j = 0; j < BTA_DM_PM_MODE_TIMER_MAX; j++) { + bta_dm_pm_stop_timer_by_index(&bta_dm_cb.pm_timer[i], j); + /* + * TODO: For now, stopping the timer does not reset + * pm_action[j]. + * The reason is because some of the internal logic that + * (re)assigns the pm_action[] values is taking into account + * the older value; e.g., see the pm_action[] assignment in + * function bta_dm_pm_start_timer(). + * Such subtlety in the execution logic is error prone, and + * should be eliminiated in the future. + */ + } + break; + } + } +} + +/******************************************************************************* +** +** Function bta_pm_action_to_timer_idx +** +** Description convert power mode into timer index for each connected device +** +** +** Returns index of the power mode delay timer +** +*******************************************************************************/ +static UINT8 bta_pm_action_to_timer_idx(UINT8 pm_action) +{ + if (pm_action == BTA_DM_PM_SUSPEND) { + return BTA_DM_PM_SUSPEND_TIMER_IDX; + } else if (pm_action == BTA_DM_PM_PARK) { + return BTA_DM_PM_PARK_TIMER_IDX; + } else if ((pm_action & BTA_DM_PM_SNIFF) == BTA_DM_PM_SNIFF) { + return BTA_DM_PM_SNIFF_TIMER_IDX; + } + + /* Active, no preference, no action and retry */ + return BTA_DM_PM_MODE_TIMER_MAX; +} + +/******************************************************************************* +** +** Function bta_dm_pm_stop_timer_by_mode +** +** Description stop a PM timer +** +** +** Returns void +** +*******************************************************************************/ +static void bta_dm_pm_stop_timer_by_mode(BD_ADDR peer_addr, UINT8 power_mode) +{ + const UINT8 timer_idx = bta_pm_action_to_timer_idx(power_mode); + if (timer_idx == BTA_DM_PM_MODE_TIMER_MAX) { + return; + } + + for (int i = 0; i < BTA_DM_NUM_PM_TIMER; i++) { + if (bta_dm_cb.pm_timer[i].in_use && !bdcmp(bta_dm_cb.pm_timer[i].peer_bdaddr, peer_addr)) { + if (bta_dm_cb.pm_timer[i].srvc_id[timer_idx] != BTA_ID_MAX) { + bta_dm_pm_stop_timer_by_index(&bta_dm_cb.pm_timer[i], timer_idx); + /* + * TODO: Intentionally setting pm_action[timer_idx]. + * This assignment should be eliminated in the future - see the + * pm_action[] related comment inside function + * bta_dm_pm_stop_timer(). + */ + bta_dm_cb.pm_timer[i].pm_action[timer_idx] = power_mode; + } + break; + } + } +} + +/******************************************************************************* +** +** Function bta_dm_pm_stop_timer_by_srvc_id +** +** Description stop all timer started by the service ID. +** +** +** Returns index of the power mode delay timer +** +*******************************************************************************/ +static void bta_dm_pm_stop_timer_by_srvc_id(BD_ADDR peer_addr, UINT8 srvc_id) +{ + for (int i = 0; i < BTA_DM_NUM_PM_TIMER; i++) { + if (bta_dm_cb.pm_timer[i].in_use && !bdcmp(bta_dm_cb.pm_timer[i].peer_bdaddr, peer_addr)) { + for (int j = 0; j < BTA_DM_PM_MODE_TIMER_MAX; j++) { + if (bta_dm_cb.pm_timer[i].srvc_id[j] == srvc_id) { + bta_dm_pm_stop_timer_by_index(&bta_dm_cb.pm_timer[i], j); + bta_dm_cb.pm_timer[i].pm_action[j] = BTA_DM_PM_NO_ACTION; + break; + } + } + } + } +} + +/******************************************************************************* +** +** Function bta_dm_pm_start_timer +** +** Description start a PM timer +** +** +** Returns void +** +*******************************************************************************/ +static void bta_dm_pm_start_timer(tBTA_PM_TIMER *p_timer, UINT8 timer_idx, + INT32 timeout, UINT8 srvc_id, UINT8 pm_action) +{ + p_timer->in_use = TRUE; + p_timer->timer[timer_idx].p_cback = bta_dm_pm_timer_cback; + + if (p_timer->srvc_id[timer_idx] == BTA_ID_MAX) { + p_timer->active++; + } + + if (p_timer->pm_action[timer_idx] < pm_action) { + p_timer->pm_action[timer_idx] = pm_action; + } + + p_timer->srvc_id[timer_idx] = srvc_id; + + bta_sys_start_timer(&p_timer->timer[timer_idx], 0, timeout); +} + +/******************************************************************************* +** +** Function bta_dm_pm_stop_timer_by_index +** +** Description stop a PM timer +** +** +** Returns void +** +*******************************************************************************/ +static void bta_dm_pm_stop_timer_by_index(tBTA_PM_TIMER *p_timer, + UINT8 timer_idx) +{ + if ((p_timer == NULL) || (timer_idx >= BTA_DM_PM_MODE_TIMER_MAX)) { + return; + } + + if (p_timer->srvc_id[timer_idx] == BTA_ID_MAX) { + return; /* The timer was not scheduled */ + } + + assert(p_timer->in_use && (p_timer->active > 0)); + + bta_sys_stop_timer(&p_timer->timer[timer_idx]); + p_timer->srvc_id[timer_idx] = BTA_ID_MAX; + /* NOTE: pm_action[timer_idx] intentionally not reset */ + + p_timer->active--; + if (p_timer->active == 0) { + p_timer->in_use = FALSE; + } +} + +UINT32 bta_dm_pm_get_remaining_ticks (TIMER_LIST_ENT *p_target_tle) +{ + return bta_sys_get_remaining_ticks(p_target_tle); +} + +/******************************************************************************* +** +** Function bta_dm_pm_cback +** +** Description Conn change callback from sys for low power management +** +** +** Returns void +** +*******************************************************************************/ +static void bta_dm_pm_cback(tBTA_SYS_CONN_STATUS status, UINT8 id, UINT8 app_id, BD_ADDR peer_addr) +{ + + UINT8 i, j; + tBTA_DM_PEER_DEVICE *p_dev; + +#if (BTM_SSR_INCLUDED == TRUE) + UINT8 *p = NULL; + int index = BTA_DM_PM_SSR0; +#endif + + APPL_TRACE_DEBUG("bta_dm_pm_cback: st(%d), id(%d), app(%d)", status, id, app_id); + + p_dev = bta_dm_find_peer_device(peer_addr); + + /* find if there is an power mode entry for the service */ + for (i = 1; i <= p_bta_dm_pm_cfg[0].app_id; i++) { + + if ((p_bta_dm_pm_cfg[i].id == id) + && ((p_bta_dm_pm_cfg[i].app_id == BTA_ALL_APP_ID ) || (p_bta_dm_pm_cfg[i].app_id == app_id ))) { + break; + } + + } + + /* if no entries are there for the app_id and subsystem in p_bta_dm_pm_spec*/ + if (i > p_bta_dm_pm_cfg[0].app_id) { + return; + } + + bta_dm_pm_stop_timer_by_srvc_id(peer_addr, id); + /*p_dev = bta_dm_find_peer_device(peer_addr);*/ + +#if (BTM_SSR_INCLUDED == TRUE) + /* set SSR parameters on SYS CONN OPEN */ + if ((BTA_SYS_CONN_OPEN == status) && p_dev && (p_dev->info & BTA_DM_DI_USE_SSR)) { + index = p_bta_dm_pm_spec[p_bta_dm_pm_cfg[i].spec_idx].ssr; + } +#endif + + /* if no action for the event */ + if (p_bta_dm_pm_spec[p_bta_dm_pm_cfg[i].spec_idx].actn_tbl[status][0].power_mode == BTA_DM_PM_NO_ACTION) { +#if (BTM_SSR_INCLUDED == TRUE) + if (BTA_DM_PM_SSR0 == index) /* and do not need to set SSR, return. */ +#endif + return; + } + + for (j = 0; j < bta_dm_conn_srvcs.count ; j++) { + /* check if an entry already present */ + if ((bta_dm_conn_srvcs.conn_srvc[j].id == id) + && (bta_dm_conn_srvcs.conn_srvc[j].app_id == app_id ) + && !bdcmp(bta_dm_conn_srvcs.conn_srvc[j].peer_bdaddr, peer_addr)) { + bta_dm_conn_srvcs.conn_srvc[j].new_request = TRUE; + break; + } + + } + + /* if subsystem has no more preference on the power mode remove + the cb */ + if (p_bta_dm_pm_spec[p_bta_dm_pm_cfg[i].spec_idx].actn_tbl[status][0].power_mode == BTA_DM_PM_NO_PREF) { + if (j != bta_dm_conn_srvcs.count) { + bta_dm_conn_srvcs.count--; + + for (; j < bta_dm_conn_srvcs.count ; j++) { + + memcpy(&bta_dm_conn_srvcs.conn_srvc[j], &bta_dm_conn_srvcs.conn_srvc[j + 1], sizeof(bta_dm_conn_srvcs.conn_srvc[j])); + + } + } else { + APPL_TRACE_WARNING("bta_dm_act no entry for connected service cbs"); + return; + } + } else if (j == bta_dm_conn_srvcs.count ) { + /* check if we have more connected service that cbs */ + if (bta_dm_conn_srvcs.count == BTA_DM_NUM_CONN_SRVS) { + APPL_TRACE_WARNING("bta_dm_act no more connected service cbs"); + return; + } + + /* fill in a new cb */ + bta_dm_conn_srvcs.conn_srvc[j].id = id; + bta_dm_conn_srvcs.conn_srvc[j].app_id = app_id; + bta_dm_conn_srvcs.conn_srvc[j].new_request = TRUE; + bdcpy(bta_dm_conn_srvcs.conn_srvc[j].peer_bdaddr, peer_addr); + + APPL_TRACE_WARNING("new conn_srvc id:%d, app_id:%d", id, app_id); + + bta_dm_conn_srvcs.count++; + bta_dm_conn_srvcs.conn_srvc[j].state = status; + } else { + /* no service is added or removed. only updating status. */ + bta_dm_conn_srvcs.conn_srvc[j].state = status; + } + + /* stop timer */ + bta_dm_pm_stop_timer(peer_addr); + + if (p_dev) { + p_dev->pm_mode_attempted = 0; + p_dev->pm_mode_failed = 0; + } + +#if (BTM_SSR_INCLUDED == TRUE) + if (p_bta_dm_ssr_spec[index].max_lat +#if (defined BTA_HH_INCLUDED && BTA_HH_INCLUDED == TRUE) + || index == BTA_DM_PM_SSR_HH +#endif + ) { + bta_dm_pm_ssr(peer_addr); + } else { + if ( ((NULL != (p = BTM_ReadLocalFeatures ())) && HCI_SNIFF_SUB_RATE_SUPPORTED(p)) && + ((NULL != (p = BTM_ReadRemoteFeatures (peer_addr))) && HCI_SNIFF_SUB_RATE_SUPPORTED(p)) && + (index == BTA_DM_PM_SSR0)) { + if (status == BTA_SYS_SCO_OPEN) { + APPL_TRACE_DEBUG("%s: SCO inactive, reset SSR to zero", __func__); + BTM_SetSsrParams (peer_addr, 0, 0, 0 ); + } else if (status == BTA_SYS_SCO_CLOSE) { + APPL_TRACE_DEBUG("%s: SCO active, back to old SSR", __func__); + bta_dm_pm_ssr(peer_addr); + } + } + } +#endif + + bta_dm_pm_set_mode(peer_addr, BTA_DM_PM_NO_ACTION, BTA_DM_PM_NEW_REQ); + + /* perform the HID link workaround if needed + ** 1. If SCO up/down event is received OR + ** 2. If HID connection open is received and SCO is already active. + ** This will handle the case where HID connects when SCO already active + */ + if ( BTM_IsDeviceUp() && + ( ((status == BTA_SYS_SCO_OPEN) || (status == BTA_SYS_SCO_CLOSE)) || + ((status == BTA_SYS_CONN_OPEN) && (id == BTA_ID_HH) && bta_dm_pm_is_sco_active()) ) ) { + BOOLEAN bScoActive; + if (status == BTA_SYS_CONN_OPEN) { + bScoActive = TRUE; + } else { + bScoActive = (status == BTA_SYS_SCO_OPEN); + } + + bta_dm_pm_hid_check(bScoActive); + } + +} + + +/******************************************************************************* +** +** Function bta_dm_pm_set_mode +** +** Description Set the power mode for the device +** +** +** Returns void +** +*******************************************************************************/ +static void bta_dm_pm_set_mode(BD_ADDR peer_addr, tBTA_DM_PM_ACTION pm_request, + tBTA_DM_PM_REQ pm_req ) +{ + + tBTA_DM_PM_ACTION pm_action = BTA_DM_PM_NO_ACTION; + UINT16 timeout = 0; + UINT8 i, j; + tBTA_DM_PM_ACTION failed_pm = 0; + tBTA_DM_PEER_DEVICE *p_peer_device = NULL; + tBTA_DM_PM_ACTION allowed_modes = 0; + tBTA_DM_PM_ACTION pref_modes = 0; + tBTA_DM_PM_CFG *p_pm_cfg; + tBTA_DM_PM_SPEC *p_pm_spec; + tBTA_DM_PM_ACTN *p_act0, *p_act1; + tBTA_DM_SRVCS *p_srvcs = NULL; + BOOLEAN timer_started = FALSE; + UINT8 timer_idx, available_timer = BTA_DM_PM_MODE_TIMER_MAX; + UINT32 remaining_ticks = 0; + + if (!bta_dm_cb.device_list.count) { + return; + } + + /* see if any attempt to put device in low power mode failed */ + p_peer_device = bta_dm_find_peer_device(peer_addr); + /* if no peer device found return */ + if (p_peer_device == NULL) { + return; + } + + failed_pm = p_peer_device->pm_mode_failed; + + for (i = 0; i < bta_dm_conn_srvcs.count ; i++) { + + p_srvcs = &bta_dm_conn_srvcs.conn_srvc[i]; + if (!bdcmp(p_srvcs->peer_bdaddr, peer_addr)) { + + /* p_bta_dm_pm_cfg[0].app_id is the number of entries */ + for (j = 1; j <= p_bta_dm_pm_cfg[0].app_id; j++) { + if ((p_bta_dm_pm_cfg[j].id == p_srvcs->id) + && ((p_bta_dm_pm_cfg[j].app_id == BTA_ALL_APP_ID ) || + (p_bta_dm_pm_cfg[j].app_id == p_srvcs->app_id))) { + break; + } + } + + p_pm_cfg = &p_bta_dm_pm_cfg[j]; + p_pm_spec = &p_bta_dm_pm_spec[p_pm_cfg->spec_idx]; + p_act0 = &p_pm_spec->actn_tbl[p_srvcs->state][0]; + p_act1 = &p_pm_spec->actn_tbl[p_srvcs->state][1]; + + APPL_TRACE_DEBUG("bta_dm_pm_set_mode: srvcsid: %d, state: %d, j: %d", p_srvcs->id, p_srvcs->state, j); + allowed_modes |= p_pm_spec->allow_mask; + + /* PM actions are in the order of strictness */ + + /* first check if the first preference is ok */ + if (!(failed_pm & p_act0->power_mode)) { + pref_modes |= p_act0->power_mode; + + if (p_act0->power_mode >= pm_action) { + pm_action = p_act0->power_mode; + + if (pm_req != BTA_DM_PM_NEW_REQ || p_srvcs->new_request) { + p_srvcs->new_request = FALSE; + timeout = p_act0->timeout; + } + } + } + /* if first preference has already failed, try second preference */ + else if (!(failed_pm & p_act1->power_mode)) { + pref_modes |= p_act1->power_mode; + + if (p_act1->power_mode > pm_action) { + pm_action = p_act1->power_mode; + timeout = p_act1->timeout; + } + } + } + } + + if (pm_action & (BTA_DM_PM_PARK | BTA_DM_PM_SNIFF)) { + /* some service don't like the mode */ + if (!(allowed_modes & pm_action)) { + + /* select the other mode if its allowed and preferred, otherwise 0 which is BTA_DM_PM_NO_ACTION */ + pm_action = (allowed_modes & (BTA_DM_PM_PARK | BTA_DM_PM_SNIFF) & pref_modes); + + /* no timeout needed if no action is required */ + if (pm_action == BTA_DM_PM_NO_ACTION) { + timeout = 0; + } + + } + } + /* if need to start a timer */ + if ((pm_req != BTA_DM_PM_EXECUTE) && timeout) { + for (i = 0; i < BTA_DM_NUM_PM_TIMER; i++) { + if (bta_dm_cb.pm_timer[i].in_use && bdcmp(bta_dm_cb.pm_timer[i].peer_bdaddr, peer_addr) == 0) { + if ((timer_idx = bta_pm_action_to_timer_idx(pm_action)) != BTA_DM_PM_MODE_TIMER_MAX) { + remaining_ticks = bta_dm_pm_get_remaining_ticks(&bta_dm_cb.pm_timer[i].timer[timer_idx]); + if (remaining_ticks < timeout) { + APPL_TRACE_DEBUG("%s remain 0\n", __func__); + /* Cancel and restart the timer */ + /* + * TODO: The value of pm_action[timer_idx] is + * conditionally updated between the two function + * calls below when the timer is restarted. + * This logic is error-prone and should be eliminated + * in the future. + */ + bta_dm_pm_stop_timer_by_index(&bta_dm_cb.pm_timer[i], + timer_idx); + bta_dm_pm_start_timer(&bta_dm_cb.pm_timer[i], timer_idx, timeout, p_srvcs->id, pm_action); + } + timer_started = TRUE; + } + break; + } else if (!bta_dm_cb.pm_timer[i].in_use) { + APPL_TRACE_DEBUG("%s dm_pm_timer:%d, %d", __func__, i, timeout); + if (available_timer == BTA_DM_PM_MODE_TIMER_MAX) { + available_timer = i; + } + } + } + /* new power mode for a new active connection */ + if (!timer_started) { + if ( available_timer != BTA_DM_PM_MODE_TIMER_MAX) { + bdcpy(bta_dm_cb.pm_timer[available_timer].peer_bdaddr, peer_addr); + if ((timer_idx = bta_pm_action_to_timer_idx(pm_action)) != BTA_DM_PM_MODE_TIMER_MAX) { + bta_dm_pm_start_timer(&bta_dm_cb.pm_timer[available_timer], timer_idx, timeout, p_srvcs->id, pm_action); + timer_started = TRUE; + } + } + /* no more timers */ + else { + APPL_TRACE_WARNING("bta_dm_act dm_pm_timer no more"); + } + } + return; + } + /* if pending power mode timer expires, and currecnt link is in a + lower power mode than current profile requirement, igonre it */ + if (pm_req == BTA_DM_PM_EXECUTE && pm_request < pm_action) { + APPL_TRACE_ERROR("Ignore the power mode request: %d", pm_request) + return; + } + if (pm_action == BTA_DM_PM_PARK) { + p_peer_device->pm_mode_attempted = BTA_DM_PM_PARK; + bta_dm_pm_park(peer_addr); + } else if (pm_action & BTA_DM_PM_SNIFF) { + /* dont initiate SNIFF, if link_policy has it disabled */ + if (p_peer_device->link_policy & HCI_ENABLE_SNIFF_MODE) { + p_peer_device->pm_mode_attempted = BTA_DM_PM_SNIFF; + bta_dm_pm_sniff(p_peer_device, (UINT8)(pm_action & 0x0F) ); + } else { + APPL_TRACE_DEBUG("bta_dm_pm_set_mode: Link policy disallows SNIFF, ignore request"); + } + } else if (pm_action == BTA_DM_PM_ACTIVE) { + bta_dm_pm_active(peer_addr); + } +} +/******************************************************************************* +** +** Function bta_ag_pm_park +** +** Description Switch to park mode. +** +** +** Returns TRUE if park attempted, FALSE otherwise. +** +*******************************************************************************/ +static BOOLEAN bta_dm_pm_park(BD_ADDR peer_addr) +{ + + tBTM_PM_MODE mode = BTM_PM_STS_ACTIVE; + + /* if not in park mode, switch to park */ + BTM_ReadPowerMode(peer_addr, &mode); + + if (mode != BTM_PM_MD_PARK) { + BTM_SetPowerMode (bta_dm_cb.pm_id, peer_addr, &p_bta_dm_pm_md[BTA_DM_PM_PARK_IDX]); + } + return TRUE; + +} + +/******************************************************************************* +** +** Function bta_ag_pm_sniff +** +** Description Switch to sniff mode. +** +** +** Returns TRUE if sniff attempted, FALSE otherwise. +** +*******************************************************************************/ +static BOOLEAN bta_dm_pm_sniff(tBTA_DM_PEER_DEVICE *p_peer_dev, UINT8 index) +{ + tBTM_PM_MODE mode = BTM_PM_STS_ACTIVE; + tBTM_PM_PWR_MD pwr_md; + tBTM_STATUS status; +#if (BTM_SSR_INCLUDED == TRUE) + UINT8 *p_rem_feat = NULL; +#endif + + BTM_ReadPowerMode(p_peer_dev->peer_bdaddr, &mode); +#if (BTM_SSR_INCLUDED == TRUE) + p_rem_feat = BTM_ReadRemoteFeatures (p_peer_dev->peer_bdaddr); + APPL_TRACE_DEBUG("bta_dm_pm_sniff cur:%d, idx:%d, info:x%x", mode, index, p_peer_dev->info); + if (mode != BTM_PM_MD_SNIFF || + (HCI_SNIFF_SUB_RATE_SUPPORTED(BTM_ReadLocalFeatures ()) && p_rem_feat && + HCI_SNIFF_SUB_RATE_SUPPORTED(p_rem_feat) && + !(p_peer_dev->info & BTA_DM_DI_USE_SSR))) +#else + APPL_TRACE_DEBUG("bta_dm_pm_sniff cur:%d, idx:%d", mode, index); + if (mode != BTM_PM_MD_SNIFF) +#endif + { +#if (BTM_SSR_INCLUDED == TRUE) + /* Dont initiate Sniff if controller has alreay accepted + * remote sniff params. This avoid sniff loop issue with + * some agrresive headsets who use sniff latencies more than + * DUT supported range of Sniff intervals.*/ + if ((mode == BTM_PM_MD_SNIFF) && (p_peer_dev->info & BTA_DM_DI_ACP_SNIFF)) { + APPL_TRACE_DEBUG("%s: already in remote initiate sniff", __func__); + return TRUE; + } +#endif + /* if the current mode is not sniff, issue the sniff command. + * If sniff, but SSR is not used in this link, still issue the command */ + memcpy(&pwr_md, &p_bta_dm_pm_md[index], sizeof (tBTM_PM_PWR_MD)); + if (p_peer_dev->info & BTA_DM_DI_INT_SNIFF) { + pwr_md.mode |= BTM_PM_MD_FORCE; + } + status = BTM_SetPowerMode (bta_dm_cb.pm_id, p_peer_dev->peer_bdaddr, &pwr_md); + if (status == BTM_CMD_STORED || status == BTM_CMD_STARTED) { + p_peer_dev->info &= ~(BTA_DM_DI_INT_SNIFF | BTA_DM_DI_ACP_SNIFF); + p_peer_dev->info |= BTA_DM_DI_SET_SNIFF; + } else if (status == BTM_SUCCESS) { + APPL_TRACE_DEBUG("bta_dm_pm_sniff BTM_SetPowerMode() returns BTM_SUCCESS"); + p_peer_dev->info &= ~(BTA_DM_DI_INT_SNIFF | BTA_DM_DI_ACP_SNIFF | BTA_DM_DI_SET_SNIFF); + } else { /* error */ + APPL_TRACE_ERROR("bta_dm_pm_sniff BTM_SetPowerMode() returns ERROR status=%d", status); + p_peer_dev->info &= ~(BTA_DM_DI_INT_SNIFF | BTA_DM_DI_ACP_SNIFF | BTA_DM_DI_SET_SNIFF); + } + } + return TRUE; + +} +/******************************************************************************* +** +** Function bta_dm_pm_ssr +** +** Description checks and sends SSR parameters +** +** Returns void +** +*******************************************************************************/ +#if (BTM_SSR_INCLUDED == TRUE) +static void bta_dm_pm_ssr(BD_ADDR peer_addr) +{ + tBTA_DM_SSR_SPEC *p_spec, *p_spec_cur; + UINT8 i, j; + int ssr = BTA_DM_PM_SSR0; + + /* go through the connected services */ + for (i = 0; i < bta_dm_conn_srvcs.count ; i++) { + if (!bdcmp(bta_dm_conn_srvcs.conn_srvc[i].peer_bdaddr, peer_addr)) { + /* p_bta_dm_pm_cfg[0].app_id is the number of entries */ + for (j = 1; j <= p_bta_dm_pm_cfg[0].app_id; j++) { + /* find the associated p_bta_dm_pm_cfg */ + if ((p_bta_dm_pm_cfg[j].id == bta_dm_conn_srvcs.conn_srvc[i].id) + && ((p_bta_dm_pm_cfg[j].app_id == BTA_ALL_APP_ID ) + || (p_bta_dm_pm_cfg[j].app_id == bta_dm_conn_srvcs.conn_srvc[i].app_id))) { + APPL_TRACE_WARNING("bta_dm_pm_ssr conn_srvc id:%d, app_id:%d", + bta_dm_conn_srvcs.conn_srvc[i].id, bta_dm_conn_srvcs.conn_srvc[i].app_id); + break; + } + } + + /* find the ssr index with the smallest max latency. */ + p_spec_cur = &p_bta_dm_ssr_spec[p_bta_dm_pm_spec[p_bta_dm_pm_cfg[j].spec_idx].ssr]; + p_spec = &p_bta_dm_ssr_spec[ssr]; + +#if (defined BTA_HH_INCLUDED && BTA_HH_INCLUDED == TRUE) + /* HH has the per connection SSR preference, already read the SSR params from BTA HH */ + if (p_bta_dm_pm_spec[p_bta_dm_pm_cfg[j].spec_idx].ssr == BTA_DM_PM_SSR_HH) { + if (bta_hh_read_ssr_param(peer_addr, &p_spec_cur->max_lat, &p_spec_cur->min_rmt_to) == BTA_HH_ERR) { + continue; + } + } +#endif + if (p_spec_cur->max_lat < p_spec->max_lat || + (ssr == BTA_DM_PM_SSR0 && p_bta_dm_pm_spec[p_bta_dm_pm_cfg[j].spec_idx].ssr != BTA_DM_PM_SSR0)) { + ssr = p_bta_dm_pm_spec[p_bta_dm_pm_cfg[j].spec_idx].ssr; + } + + } + } + + p_spec = &p_bta_dm_ssr_spec[ssr]; + APPL_TRACE_WARNING("bta_dm_pm_ssr:%d, lat:%d", ssr, p_spec->max_lat); + if (p_spec->max_lat) { + /* set the SSR parameters. */ + BTM_SetSsrParams (peer_addr, p_spec->max_lat, + p_spec->min_rmt_to, p_spec->min_loc_to); + } +} +#endif + +/******************************************************************************* +** +** Function bta_dm_pm_active +** +** Description Brings connection to active mode +** +** +** Returns void +** +*******************************************************************************/ +void bta_dm_pm_active(BD_ADDR peer_addr) +{ + tBTM_PM_PWR_MD pm; + + memset( (void *)&pm, 0, sizeof(pm)); + + /* switch to active mode */ + pm.mode = BTM_PM_MD_ACTIVE; + BTM_SetPowerMode (bta_dm_cb.pm_id, peer_addr, &pm); +} + +/******************************************************************************* +** +** Function bta_dm_pm_btm_cback +** +** Description BTM power manager callback. +** +** +** Returns void +** +*******************************************************************************/ +static void bta_dm_pm_btm_cback(BD_ADDR bd_addr, tBTM_PM_STATUS status, UINT16 value, UINT8 hci_status) +{ + tBTA_DM_PM_BTM_STATUS *p_buf; + + if ((p_buf = (tBTA_DM_PM_BTM_STATUS *) osi_malloc(sizeof(tBTA_DM_PM_BTM_STATUS))) != NULL) { + p_buf->hdr.event = BTA_DM_PM_BTM_STATUS_EVT; + p_buf->status = status; + p_buf->value = value; + p_buf->hci_status = hci_status; + bdcpy(p_buf->bd_addr, bd_addr); + bta_sys_sendmsg(p_buf); + } +} + +/******************************************************************************* +** +** Function bta_dm_pm_timer_cback +** +** Description Power management timer callback. +** +** +** Returns void +** +*******************************************************************************/ +static void bta_dm_pm_timer_cback(void *p_tle) +{ + UINT8 i, j; + + for (i = 0; i < BTA_DM_NUM_PM_TIMER; i++) { + APPL_TRACE_DEBUG("dm_pm_timer[%d] in use? %d", i, bta_dm_cb.pm_timer[i].in_use); + if (bta_dm_cb.pm_timer[i].in_use) { + for (j = 0; j < BTA_DM_PM_MODE_TIMER_MAX; j++) { + if (&bta_dm_cb.pm_timer[i].timer[j] == (TIMER_LIST_ENT *) p_tle) { + bta_dm_cb.pm_timer[i].active --; + bta_dm_cb.pm_timer[i].srvc_id[j] = BTA_ID_MAX; + APPL_TRACE_DEBUG("dm_pm_timer[%d] expires, timer_idx=%d", i, j); + break; + } + } + if (bta_dm_cb.pm_timer[i].active == 0) { + bta_dm_cb.pm_timer[i].in_use = FALSE; + } + if (j < BTA_DM_PM_MODE_TIMER_MAX) { + break; + } + } + } + + /* no more timers */ + if (i == BTA_DM_NUM_PM_TIMER) { + return; + } + + tBTA_DM_PM_TIMER *p_buf = (tBTA_DM_PM_TIMER *) osi_malloc(sizeof(tBTA_DM_PM_TIMER)); + if (p_buf != NULL) { + p_buf->hdr.event = BTA_DM_PM_TIMER_EVT; + p_buf->pm_request = bta_dm_cb.pm_timer[i].pm_action[j]; + bdcpy(p_buf->bd_addr, bta_dm_cb.pm_timer[i].peer_bdaddr); + bta_sys_sendmsg(p_buf); + } +} + + +/******************************************************************************* +** +** Function bta_dm_pm_btm_status +** +** Description Process pm status event from btm +** +** +** Returns void +** +*******************************************************************************/ +void bta_dm_pm_btm_status(tBTA_DM_MSG *p_data) +{ + APPL_TRACE_DEBUG("%s status: %d", __func__, p_data->pm_status.status); + + tBTA_DM_PEER_DEVICE *p_dev = bta_dm_find_peer_device(p_data->pm_status.bd_addr); + if (NULL == p_dev) { + return; + } + + tBTA_DM_DEV_INFO info = p_dev->info; + /* check new mode */ + switch (p_data->pm_status.status) { + case BTM_PM_STS_ACTIVE: + /* if our sniff or park attempt failed + we should not try it again*/ + if (p_data->pm_status.hci_status != 0) { + APPL_TRACE_ERROR("%s hci_status=%d", __func__, p_data->pm_status.hci_status); + p_dev->info &= ~(BTA_DM_DI_INT_SNIFF | BTA_DM_DI_ACP_SNIFF | BTA_DM_DI_SET_SNIFF); + + if (p_dev->pm_mode_attempted & (BTA_DM_PM_PARK | BTA_DM_PM_SNIFF)) { + p_dev->pm_mode_failed + |= ((BTA_DM_PM_PARK | BTA_DM_PM_SNIFF) & p_dev->pm_mode_attempted); + bta_dm_pm_stop_timer_by_mode(p_data->pm_status.bd_addr, p_dev->pm_mode_attempted); + bta_dm_pm_set_mode(p_data->pm_status.bd_addr, BTA_DM_PM_NO_ACTION, BTA_DM_PM_RESTART); + } + } else { +#if (BTM_SSR_INCLUDED == TRUE) + if (p_dev->prev_low) { + /* need to send the SSR paramaters to controller again */ + bta_dm_pm_ssr(p_dev->peer_bdaddr); + } + p_dev->prev_low = BTM_PM_STS_ACTIVE; +#endif + /* link to active mode, need to restart the timer for next low power mode if needed */ + bta_dm_pm_stop_timer(p_data->pm_status.bd_addr); + bta_dm_pm_set_mode(p_data->pm_status.bd_addr, BTA_DM_PM_NO_ACTION, BTA_DM_PM_RESTART); + } + break; + +#if (BTM_SSR_INCLUDED == TRUE) + case BTM_PM_STS_PARK: + case BTM_PM_STS_HOLD: + /* save the previous low power mode - for SSR. + * SSR parameters are sent to controller on "conn open". + * the numbers stay good until park/hold/detach */ + if (p_dev->info & BTA_DM_DI_USE_SSR) { + p_dev->prev_low = p_data->pm_status.status; + } + break; + + case BTM_PM_STS_SSR: + if (p_data->pm_status.value) { + p_dev->info |= BTA_DM_DI_USE_SSR; + } else { + p_dev->info &= ~BTA_DM_DI_USE_SSR; + } + break; +#endif + case BTM_PM_STS_SNIFF: + if (p_data->pm_status.hci_status == 0) { + /* Stop PM timer now if already active for + * particular device since link is already + * put in sniff mode by remote device, and + * PM timer sole purpose is to put the link + * in sniff mode from host side. + */ + bta_dm_pm_stop_timer(p_data->pm_status.bd_addr); + } else { + p_dev->info &= ~(BTA_DM_DI_SET_SNIFF | BTA_DM_DI_INT_SNIFF | BTA_DM_DI_ACP_SNIFF); + if (info & BTA_DM_DI_SET_SNIFF) { + p_dev->info |= BTA_DM_DI_INT_SNIFF; + } else { + p_dev->info |= BTA_DM_DI_ACP_SNIFF; + } + } + break; + + case BTM_PM_STS_ERROR: + p_dev->info &= ~BTA_DM_DI_SET_SNIFF; + break; + + default: + break; + } + + if ( bta_dm_cb.p_sec_cback ) { + if (p_data->pm_status.status <= BTM_PM_STS_PARK + /*&& p_data->pm_status.status >= BTM_PM_STS_ACTIVE*/ // comparison is always true due to limited range of data type + ) { + tBTA_DM_SEC conn; + conn.mode_chg.mode = p_data->pm_status.status; + bdcpy(conn.mode_chg.bd_addr, p_data->pm_status.bd_addr); + bta_dm_cb.p_sec_cback(BTA_DM_PM_MODE_CHG_EVT, (tBTA_DM_SEC *)&conn); + } + } +} + + +/******************************************************************************* +** +** Function bta_dm_pm_timer +** +** Description Process pm timer event from btm +** +** +** Returns void +** +*******************************************************************************/ +void bta_dm_pm_timer(tBTA_DM_MSG *p_data) +{ + APPL_TRACE_EVENT("%s", __func__); + bta_dm_pm_set_mode(p_data->pm_timer.bd_addr, p_data->pm_timer.pm_request, BTA_DM_PM_EXECUTE); +} + +/******************************************************************************* +** +** Function bta_dm_is_sco_active +** +** Description Loop through connected services for HFP+State=SCO +** +** Returns BOOLEAN. TRUE if SCO active, else FALSE +** +*******************************************************************************/ +static BOOLEAN bta_dm_pm_is_sco_active (void) +{ + int j; + BOOLEAN bScoActive = FALSE; + + for (j = 0; j < bta_dm_conn_srvcs.count ; j++) { + /* check if an entry already present */ + if ( (bta_dm_conn_srvcs.conn_srvc[j].id == BTA_ID_AG ) && (bta_dm_conn_srvcs.conn_srvc[j].state == BTA_SYS_SCO_OPEN) ) { + bScoActive = TRUE; + break; + } + } + + APPL_TRACE_DEBUG("bta_dm_is_sco_active: SCO active: %d", bScoActive); + return bScoActive; +} + + +/******************************************************************************* +** +** Function bta_dm_pm_hid_check +** +** Description Disables/Enables sniff in link policy based on SCO Up/Down +** +** Returns None +** +*******************************************************************************/ + +static void bta_dm_pm_hid_check(BOOLEAN bScoActive) +{ + int j; + + /* if HID is active, disable the link policy */ + for (j = 0; j < bta_dm_conn_srvcs.count ; j++) { + /* check if an entry already present */ + if (bta_dm_conn_srvcs.conn_srvc[j].id == BTA_ID_HH ) { + APPL_TRACE_DEBUG ("SCO status change(Active: %d), modify HID link policy. state: %d", + bScoActive, bta_dm_conn_srvcs.conn_srvc[j].state); + bta_dm_pm_set_sniff_policy( bta_dm_find_peer_device(bta_dm_conn_srvcs.conn_srvc[j].peer_bdaddr), bScoActive); + + /* if we had disabled link policy, seems like the hid device stop retrying SNIFF after a few tries. force sniff if needed */ + if (!bScoActive) { + bta_dm_pm_set_mode(bta_dm_conn_srvcs.conn_srvc[j].peer_bdaddr, BTA_DM_PM_NO_ACTION, + BTA_DM_PM_RESTART); + } + } + } + +} + +/******************************************************************************* +** +** Function bta_dm_pm_set_sniff_policy +** +** Description Disables/Enables sniff in link policy for the give device +** +** Returns None +** +*******************************************************************************/ +static void bta_dm_pm_set_sniff_policy(tBTA_DM_PEER_DEVICE *p_dev, BOOLEAN bDisable) +{ + UINT16 policy_setting; + + if (!p_dev) { + return; + } + + if (bDisable) { + policy_setting = bta_dm_cb.cur_policy & + (HCI_ENABLE_MASTER_SLAVE_SWITCH | + HCI_ENABLE_HOLD_MODE | + HCI_ENABLE_PARK_MODE); + + } else { + /* allow sniff after sco is closed */ + policy_setting = bta_dm_cb.cur_policy; + } + + /* if disabling SNIFF, make sure link is Active */ + if (bDisable) { + bta_dm_pm_active(p_dev->peer_bdaddr); + } + + /* update device record and set link policy */ + p_dev->link_policy = policy_setting; + BTM_SetLinkPolicy(p_dev->peer_bdaddr, &policy_setting); + +} +#endif /* #if (BTA_DM_PM_INCLUDED == TRUE) */ + +/******************************************************************************* +** +** Function bta_dm_get_av_count +** +** Description Get the number of connected AV +** +** +** Returns number of av connections +** +*******************************************************************************/ +UINT8 bta_dm_get_av_count(void) +{ + UINT8 count = 0; + for (int i = 0; i < bta_dm_conn_srvcs.count; i++) { + if (bta_dm_conn_srvcs.conn_srvc[i].id == BTA_ID_AV) { + ++count; + } + } + return count; +} + +/******************************************************************************* +** +** Function bta_dm_find_peer_device +** +** Description Given an address, find the associated control block. +** +** Returns tBTA_DM_PEER_DEVICE +** +*******************************************************************************/ +tBTA_DM_PEER_DEVICE *bta_dm_find_peer_device(BD_ADDR peer_addr) +{ + tBTA_DM_PEER_DEVICE *p_dev = NULL; + + for (int i = 0; i < bta_dm_cb.device_list.count; i++) { + if (!bdcmp( bta_dm_cb.device_list.peer_device[i].peer_bdaddr, peer_addr)) { + p_dev = &bta_dm_cb.device_list.peer_device[i]; + break; + } + + } + return p_dev; +} + + +#if ((defined BLE_INCLUDED) && (BLE_INCLUDED == TRUE) && SDP_INCLUDED == TRUE) +/******************************************************************************* +** +** Function bta_dm_pm_obtain_controller_state +** +** Description This function obtains the consolidated controller power state +** +** Parameters: +** +*******************************************************************************/ +tBTA_DM_CONTRL_STATE bta_dm_pm_obtain_controller_state(void) +{ + /* Did not use counts as it is not sure, how accurate the count values are in + ** bta_dm_cb.device_list.count > 0 || bta_dm_cb.device_list.le_count > 0 */ + + tBTA_DM_CONTRL_STATE cur_state = BTA_DM_CONTRL_UNKNOWN; + cur_state = BTM_PM_ReadControllerState(); + + APPL_TRACE_DEBUG("bta_dm_pm_obtain_controller_state: %d", cur_state); + return cur_state; +} + +#endif diff --git a/lib/bt/host/bluedroid/bta/dm/bta_dm_qos.c b/lib/bt/host/bluedroid/bta/dm/bta_dm_qos.c new file mode 100644 index 00000000..19a31b94 --- /dev/null +++ b/lib/bt/host/bluedroid/bta/dm/bta_dm_qos.c @@ -0,0 +1,68 @@ +/****************************************************************************** + * + * Copyright (C) 2003-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains the action functions for QoS state + * machine. + * + ******************************************************************************/ + +#include "bta/bta_sys.h" +#include "bta/bta_api.h" +#include "bta_dm_int.h" +#include "stack/btm_api.h" +#include "osi/allocator.h" + +#if (BTA_DM_QOS_INCLUDED == TRUE) + +void bta_dm_set_qos(tBTA_DM_MSG *p_data) +{ + FLOW_SPEC p_flow = { + .qos_flags = 0, /* TBD */ + .service_type = NO_TRAFFIC, /* service_type */ + .token_rate = 0, /* bytes/second */ + .token_bucket_size = 0, /* bytes */ + .peak_bandwidth = 0, /* bytes/second */ + .latency = 625 * p_data->qos_set.t_poll, /* microseconds */ + .delay_variation = 0xFFFFFFFF /* microseconds */ + }; + + tBTM_STATUS status = BTM_SetQoS (p_data->qos_set.bd_addr, &p_flow, p_data->qos_set.p_cb); + + if(status != BTM_CMD_STARTED) { + APPL_TRACE_ERROR("%s ERROR: 0x%x\n", __func__, status); + } +} + + +void BTA_DmSetQos(BD_ADDR bd_addr, UINT32 t_poll, tBTM_CMPL_CB *p_cb) +{ + tBTA_DM_API_QOS_SET *p_msg; + + if ((p_msg = (tBTA_DM_API_QOS_SET *) osi_malloc(sizeof(tBTA_DM_API_QOS_SET))) != NULL) { + p_msg->hdr.event = BTA_DM_API_QOS_SET_EVT; + + bdcpy(p_msg->bd_addr, bd_addr); + p_msg->t_poll = t_poll; + p_msg->p_cb = p_cb; + + bta_sys_sendmsg(p_msg); + } +} +#endif /// (BTA_DM_QOS_INCLUDED == TRUE) diff --git a/lib/bt/host/bluedroid/bta/dm/bta_dm_sco.c b/lib/bt/host/bluedroid/bta/dm/bta_dm_sco.c new file mode 100644 index 00000000..df91799a --- /dev/null +++ b/lib/bt/host/bluedroid/bta/dm/bta_dm_sco.c @@ -0,0 +1,674 @@ +/****************************************************************************** + * + * Copyright (C) 2003-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains the down sampling utility to convert PCM samples in + * 16k/32k/48k/44.1k/22050/11025 sampling rate into 8K/16bits samples + * required for SCO channel format. One API function isprovided and only + * possible to be used when transmitting SCO data is sent via HCI + * interface. + * + ******************************************************************************/ +#include +#include "bta/bta_api.h" +#include "bta/bta_sys.h" +#include "osi/allocator.h" + +#if (BTM_SCO_HCI_INCLUDED == TRUE) + +#ifndef BTA_DM_SCO_DEBUG +#define BTA_DM_SCO_DEBUG FALSE +#endif +/***************************************************************************** +** Constants +*****************************************************************************/ + +#define BTA_DM_PCM_OVERLAP_SIZE 48 + +#define BTA_DM_PCM_SMPL_RATE_44100 44100 +#define BTA_DM_PCM_SMPL_RATE_22050 22050 +#define BTA_DM_PCM_SMPL_RATE_11025 11025 + +/***************************************************************************** +** Data types for PCM Resampling utility +*****************************************************************************/ + +typedef INT32 (*PCONVERT_TO_BT_FILTERED) (UINT8 *pSrc, void *pDst, UINT32 dwSrcSamples, + UINT32 dwSrcSps, INT32 *pLastCurPos, UINT8 *pOverlapArea); +typedef INT32 (*PCONVERT_TO_BT_NOFILTER) (void *pSrc, void *pDst, UINT32 dwSrcSamples, + UINT32 dwSrcSps); +typedef struct { + UINT8 overlap_area[BTA_DM_PCM_OVERLAP_SIZE * 4]; + UINT32 cur_pos; /* current position */ + UINT32 src_sps; /* samples per second (source audio data) */ + PCONVERT_TO_BT_FILTERED filter; /* the action function to do the + conversion 44100, 22050, 11025*/ + PCONVERT_TO_BT_NOFILTER nofilter; /* the action function to do + the conversion 48000, 32000, 16000*/ + UINT32 bits; /* number of bits per pcm sample */ + UINT32 n_channels; /* number of channels (i.e. mono(1), stereo(2)...) */ + UINT32 sample_size; + UINT32 can_be_filtered; + UINT32 divisor; +} tBTA_DM_PCM_RESAMPLE_CB; + +static tBTA_DM_PCM_RESAMPLE_CB* p_bta_dm_pcm_cb; + +/***************************************************************************** +** Macro Definition +*****************************************************************************/ + + +#define CHECK_SATURATION16(x) \ + if (x > 32767) \ + x = 32767; \ + else if (x < -32768) \ + x = -32768; + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +#define CONVERT_44100_TO_BLUETOOTH(pStart, pEnd) \ + { \ + INT32 out1, out2, out3, out4, out5; \ + SRC_TYPE *pS = (SRC_TYPE *)pStart; \ + SRC_TYPE *pSEnd = (SRC_TYPE *)pEnd; \ + \ + while (pS < pSEnd) \ + { \ + CurrentPos -= 8000; \ + \ + if (CurrentPos >= 0) \ + { \ + pS += SRC_CHANNELS; \ + continue; \ + } \ + CurrentPos += dwSrcSps; \ + \ + out1 = (SRC_SAMPLE(0) * 1587) \ + + ((SRC_SAMPLE(1) + SRC_SAMPLE(-1)) * 1522) \ + + ((SRC_SAMPLE(2) + SRC_SAMPLE(-2)) * 1337) \ + + ((SRC_SAMPLE(3) + SRC_SAMPLE(-3)) * 1058); \ + \ + out1 = out1 / 30000; \ + \ + out2 = ((SRC_SAMPLE(4) + SRC_SAMPLE(-4)) * 725) \ + + ((SRC_SAMPLE(5) + SRC_SAMPLE(-5)) * 384) \ + + ((SRC_SAMPLE(6) + SRC_SAMPLE(-6)) * 79); \ + \ + out2 = out2 / 30000; \ + \ + out3 = ((SRC_SAMPLE(7) + SRC_SAMPLE(-7)) * 156) \ + + ((SRC_SAMPLE(8) + SRC_SAMPLE(-8)) * 298) \ + + ((SRC_SAMPLE(9) + SRC_SAMPLE(-9)) * 345); \ + \ + out3 = out3 / 30000; \ + \ + out4 = ((SRC_SAMPLE(10) + SRC_SAMPLE(-10)) * 306) \ + + ((SRC_SAMPLE(11) + SRC_SAMPLE(-11)) * 207) \ + + ((SRC_SAMPLE(12) + SRC_SAMPLE(-12)) * 78); \ + \ + out4 = out4 / 30000; \ + \ + out5 = out1 + out2 - out3 - out4; \ + \ + CHECK_SATURATION16(out5); \ + *psBtOut++ = (INT16)out5; \ + \ + pS += SRC_CHANNELS; \ + } \ + } + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +#define CONVERT_22050_TO_BLUETOOTH(pStart, pEnd) \ + { \ + INT32 out1, out2, out3, out4, out5; \ + SRC_TYPE *pS = (SRC_TYPE *)pStart; \ + SRC_TYPE *pSEnd = (SRC_TYPE *)pEnd; \ + \ + while (pS < pSEnd) \ + { \ + CurrentPos -= 8000; \ + \ + if (CurrentPos >= 0) \ + { \ + pS += SRC_CHANNELS; \ + continue; \ + } \ + CurrentPos += dwSrcSps; \ + \ + out1 = (SRC_SAMPLE(0) * 2993) \ + + ((SRC_SAMPLE(1) + SRC_SAMPLE(-1)) * 2568) \ + + ((SRC_SAMPLE(2) + SRC_SAMPLE(-2)) * 1509) \ + + ((SRC_SAMPLE(3) + SRC_SAMPLE(-3)) * 331); \ + \ + out1 = out1 / 30000; \ + \ + out2 = ((SRC_SAMPLE(4) + SRC_SAMPLE(-4)) * 454) \ + + ((SRC_SAMPLE(5) + SRC_SAMPLE(-5)) * 620) \ + + ((SRC_SAMPLE(6) + SRC_SAMPLE(-6)) * 305); \ + \ + out2 = out2 / 30000; \ + \ + out3 = ((SRC_SAMPLE(7) + SRC_SAMPLE(-7)) * 127) \ + + ((SRC_SAMPLE(8) + SRC_SAMPLE(-8)) * 350) \ + + ((SRC_SAMPLE(9) + SRC_SAMPLE(-9)) * 265) \ + + ((SRC_SAMPLE(10) + SRC_SAMPLE(-10)) * 6); \ + \ + out3 = out3 / 30000; \ + \ + out4 = ((SRC_SAMPLE(11) + SRC_SAMPLE(-11)) * 201); \ + \ + out4 = out4 / 30000; \ + \ + out5 = out1 - out2 + out3 - out4; \ + \ + CHECK_SATURATION16(out5); \ + *psBtOut++ = (INT16)out5; \ + \ + pS += SRC_CHANNELS; \ + } \ + } + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +#define CONVERT_11025_TO_BLUETOOTH(pStart, pEnd) \ + { \ + INT32 out1; \ + SRC_TYPE *pS = (SRC_TYPE *)pStart; \ + SRC_TYPE *pSEnd = (SRC_TYPE *)pEnd; \ + \ + while (pS < pSEnd) \ + { \ + CurrentPos -= 8000; \ + \ + if (CurrentPos >= 0) \ + { \ + pS += SRC_CHANNELS; \ + continue; \ + } \ + CurrentPos += dwSrcSps; \ + \ + out1 = (SRC_SAMPLE(0) * 6349) \ + + ((SRC_SAMPLE(1) + SRC_SAMPLE(-1)) * 2874) \ + - ((SRC_SAMPLE(2) + SRC_SAMPLE(-2)) * 1148) \ + - ((SRC_SAMPLE(3) + SRC_SAMPLE(-3)) * 287) \ + + ((SRC_SAMPLE(4) + SRC_SAMPLE(-4)) * 675) \ + - ((SRC_SAMPLE(5) + SRC_SAMPLE(-5)) * 258) \ + - ((SRC_SAMPLE(6) + SRC_SAMPLE(-6)) * 206) \ + + ((SRC_SAMPLE(7) + SRC_SAMPLE(-7)) * 266); \ + \ + out1 = out1 / 30000; \ + \ + CHECK_SATURATION16(out1); \ + *psBtOut++ = (INT16)out1; \ + \ + pS += SRC_CHANNELS; \ + } \ + } + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +#undef SRC_CHANNELS +#undef SRC_SAMPLE +#undef SRC_TYPE + +#define SRC_TYPE UINT8 +#define SRC_CHANNELS 1 +#define SRC_SAMPLE(x) ((pS[x] - 0x80) << 8) + +/***************************************************************************** +** Local Function +*****************************************************************************/ +INT32 Convert_8M_ToBT_Filtered (UINT8 *pSrc, void *pDst, UINT32 dwSrcSamples, + UINT32 dwSrcSps, INT32 *pLastCurPos, UINT8 *pOverlapArea) +{ + INT32 CurrentPos = *pLastCurPos; + SRC_TYPE *pIn, *pInEnd; + SRC_TYPE *pOv, *pOvEnd; + INT16 *psBtOut = (INT16 *)pDst; +#if BTA_DM_SCO_DEBUG + APPL_TRACE_DEBUG("Convert_8M_ToBT_Filtered, CurrentPos %d\n", CurrentPos); +#endif + memcpy (pOverlapArea + (BTA_DM_PCM_OVERLAP_SIZE * 2), pSrc, BTA_DM_PCM_OVERLAP_SIZE * 2); + + pOv = (SRC_TYPE *)(pOverlapArea + BTA_DM_PCM_OVERLAP_SIZE); + pOvEnd = (SRC_TYPE *)(pOverlapArea + (BTA_DM_PCM_OVERLAP_SIZE * 3)); + + pIn = (SRC_TYPE *)(pSrc + BTA_DM_PCM_OVERLAP_SIZE); + pInEnd = (SRC_TYPE *)(pSrc + (dwSrcSamples * SRC_CHANNELS * sizeof (SRC_TYPE)) - \ + BTA_DM_PCM_OVERLAP_SIZE); + + if (dwSrcSps == BTA_DM_PCM_SMPL_RATE_44100) { + CONVERT_44100_TO_BLUETOOTH(pOv, pOvEnd); + CONVERT_44100_TO_BLUETOOTH(pIn, pInEnd); + } else if (dwSrcSps == BTA_DM_PCM_SMPL_RATE_22050) { + CONVERT_22050_TO_BLUETOOTH(pOv, pOvEnd); + CONVERT_22050_TO_BLUETOOTH(pIn, pInEnd); + } else if (dwSrcSps == BTA_DM_PCM_SMPL_RATE_11025) { + CONVERT_11025_TO_BLUETOOTH(pOv, pOvEnd); + CONVERT_11025_TO_BLUETOOTH(pIn, pInEnd); + } + + memcpy (pOverlapArea, pSrc + (dwSrcSamples * SRC_CHANNELS * sizeof (SRC_TYPE)) - \ + (BTA_DM_PCM_OVERLAP_SIZE * 2), BTA_DM_PCM_OVERLAP_SIZE * 2); + + *pLastCurPos = CurrentPos; + + return (psBtOut - (INT16 *)pDst); +} + +INT32 Convert_8M_ToBT_NoFilter (void *pSrc, void *pDst, UINT32 dwSrcSamples, UINT32 dwSrcSps) +{ + INT32 CurrentPos; + UINT8 *pbSrc = (UINT8 *)pSrc; + INT16 *psDst = (INT16 *)pDst; + INT16 sWorker; + + // start at dwSpsSrc / 2, decrement by 8000 + // + CurrentPos = (dwSrcSps >> 1); + + while (dwSrcSamples--) { + CurrentPos -= 8000; + + if (CurrentPos >= 0) { + pbSrc++; + } else { + sWorker = *pbSrc++; + sWorker -= 0x80; + sWorker <<= 8; + + *psDst++ = sWorker; + + CurrentPos += dwSrcSps; + } + } + + return (psDst - (INT16 *)pDst); +} + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +#undef SRC_CHANNELS +#undef SRC_SAMPLE +#undef SRC_TYPE + +#define SRC_TYPE INT16 +#define SRC_CHANNELS 1 +#define SRC_SAMPLE(x) pS[x] + +INT32 Convert_16M_ToBT_Filtered (UINT8 *pSrc, void *pDst, UINT32 dwSrcSamples, + UINT32 dwSrcSps, INT32 *pLastCurPos, UINT8 *pOverlapArea) +{ + INT32 CurrentPos = *pLastCurPos; + SRC_TYPE *pIn, *pInEnd; + SRC_TYPE *pOv, *pOvEnd; + INT16 *psBtOut = (INT16 *)pDst; + + memcpy (pOverlapArea + (BTA_DM_PCM_OVERLAP_SIZE * 2), pSrc, BTA_DM_PCM_OVERLAP_SIZE * 2); + + pOv = (SRC_TYPE *)(pOverlapArea + BTA_DM_PCM_OVERLAP_SIZE); + pOvEnd = (SRC_TYPE *)(pOverlapArea + (BTA_DM_PCM_OVERLAP_SIZE * 3)); + + pIn = (SRC_TYPE *)(pSrc + BTA_DM_PCM_OVERLAP_SIZE); + pInEnd = (SRC_TYPE *)(pSrc + (dwSrcSamples * SRC_CHANNELS * sizeof (SRC_TYPE)) - BTA_DM_PCM_OVERLAP_SIZE); + + if (dwSrcSps == BTA_DM_PCM_SMPL_RATE_44100) { + CONVERT_44100_TO_BLUETOOTH(pOv, pOvEnd); + CONVERT_44100_TO_BLUETOOTH(pIn, pInEnd); + } else if (dwSrcSps == BTA_DM_PCM_SMPL_RATE_22050) { + CONVERT_22050_TO_BLUETOOTH(pOv, pOvEnd); + CONVERT_22050_TO_BLUETOOTH(pIn, pInEnd); + } else if (dwSrcSps == BTA_DM_PCM_SMPL_RATE_11025) { + CONVERT_11025_TO_BLUETOOTH(pOv, pOvEnd); + CONVERT_11025_TO_BLUETOOTH(pIn, pInEnd); + } + + memcpy (pOverlapArea, pSrc + (dwSrcSamples * SRC_CHANNELS * sizeof (SRC_TYPE)) - \ + (BTA_DM_PCM_OVERLAP_SIZE * 2), BTA_DM_PCM_OVERLAP_SIZE * 2); + + *pLastCurPos = CurrentPos; + + return (psBtOut - (INT16 *)pDst); +} + +INT32 Convert_16M_ToBT_NoFilter (void *pSrc, void *pDst, UINT32 dwSrcSamples, UINT32 dwSrcSps) +{ + INT32 CurrentPos; + INT16 *psSrc = (INT16 *)pSrc; + INT16 *psDst = (INT16 *)pDst; + + // start at dwSpsSrc / 2, decrement by 8000 + // + CurrentPos = (dwSrcSps >> 1); + + while (dwSrcSamples--) { + CurrentPos -= 8000; + + if (CurrentPos >= 0) { + psSrc++; + } else { + *psDst++ = *psSrc++; + + CurrentPos += dwSrcSps; + } + } + + return (psDst - (INT16 *)pDst); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +#undef SRC_CHANNELS +#undef SRC_SAMPLE +#undef SRC_TYPE + +#define SRC_TYPE UINT8 +#define SRC_CHANNELS 2 +#define SRC_SAMPLE(x) ((((pS[x * 2] - 0x80) << 8) + ((pS[(x * 2) + 1] - 0x80) << 8)) >> 1) + +INT32 Convert_8S_ToBT_Filtered (UINT8 *pSrc, void *pDst, UINT32 dwSrcSamples, + UINT32 dwSrcSps, INT32 *pLastCurPos, UINT8 *pOverlapArea) +{ + INT32 CurrentPos = *pLastCurPos; + SRC_TYPE *pIn, *pInEnd; + SRC_TYPE *pOv, *pOvEnd; + INT16 *psBtOut = (INT16 *)pDst; + +#if BTA_DM_SCO_DEBUG + APPL_TRACE_DEBUG("Convert_8S_ToBT_Filtered CurrentPos %d, SRC_TYPE %d, SRC_CHANNELS %d, \ + dwSrcSamples %d, dwSrcSps %d", CurrentPos, sizeof (SRC_TYPE), SRC_CHANNELS, \ + dwSrcSamples, dwSrcSps); +#endif + memcpy (pOverlapArea + (BTA_DM_PCM_OVERLAP_SIZE * 2), pSrc, BTA_DM_PCM_OVERLAP_SIZE * 2); + + pOv = (SRC_TYPE *)(pOverlapArea + BTA_DM_PCM_OVERLAP_SIZE); + pOvEnd = (SRC_TYPE *)(pOverlapArea + (BTA_DM_PCM_OVERLAP_SIZE * 3)); + + pIn = (SRC_TYPE *)(pSrc + BTA_DM_PCM_OVERLAP_SIZE); + pInEnd = (SRC_TYPE *)(pSrc + (dwSrcSamples * SRC_CHANNELS * sizeof (SRC_TYPE)) - BTA_DM_PCM_OVERLAP_SIZE); + + if (dwSrcSps == BTA_DM_PCM_SMPL_RATE_44100) { + CONVERT_44100_TO_BLUETOOTH(pOv, pOvEnd); + CONVERT_44100_TO_BLUETOOTH(pIn, pInEnd); + } else if (dwSrcSps == BTA_DM_PCM_SMPL_RATE_22050) { + CONVERT_22050_TO_BLUETOOTH(pOv, pOvEnd); + CONVERT_22050_TO_BLUETOOTH(pIn, pInEnd); + } else if (dwSrcSps == BTA_DM_PCM_SMPL_RATE_11025) { + CONVERT_11025_TO_BLUETOOTH(pOv, pOvEnd); + CONVERT_11025_TO_BLUETOOTH(pIn, pInEnd); + } + + memcpy (pOverlapArea, pSrc + (dwSrcSamples * SRC_CHANNELS * sizeof (SRC_TYPE)) - \ + (BTA_DM_PCM_OVERLAP_SIZE * 2), BTA_DM_PCM_OVERLAP_SIZE * 2); + + *pLastCurPos = CurrentPos; + + return (psBtOut - (INT16 *)pDst); +} + +INT32 Convert_8S_ToBT_NoFilter (void *pSrc, void *pDst, UINT32 dwSrcSamples, UINT32 dwSrcSps) +{ + INT32 CurrentPos; + UINT8 *pbSrc = (UINT8 *)pSrc; + INT16 *psDst = (INT16 *)pDst; + INT16 sWorker, sWorker2; + + // start at dwSpsSrc / 2, decrement by 8000 + // + CurrentPos = (dwSrcSps >> 1); + + while (dwSrcSamples--) { + CurrentPos -= 8000; + + if (CurrentPos >= 0) { + pbSrc += 2; + } else { + sWorker = *(unsigned char *)pbSrc; + sWorker -= 0x80; + sWorker <<= 8; + pbSrc++; + + sWorker2 = *(unsigned char *)pbSrc; + sWorker2 -= 0x80; + sWorker2 <<= 8; + pbSrc++; + + sWorker += sWorker2; + sWorker >>= 1; + + *psDst++ = sWorker; + + CurrentPos += dwSrcSps; + } + } + + return (psDst - (INT16 *)pDst); +} + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +#undef SRC_CHANNELS +#undef SRC_SAMPLE +#undef SRC_TYPE + +#define SRC_TYPE INT16 +#define SRC_CHANNELS 2 +#define SRC_SAMPLE(x) ((pS[x * 2] + pS[(x * 2) + 1]) >> 1) + +INT32 Convert_16S_ToBT_Filtered (UINT8 *pSrc, void *pDst, UINT32 dwSrcSamples, + UINT32 dwSrcSps, INT32 *pLastCurPos, UINT8 *pOverlapArea) +{ + INT32 CurrentPos = *pLastCurPos; + SRC_TYPE *pIn, *pInEnd; + SRC_TYPE *pOv, *pOvEnd; + INT16 *psBtOut = (INT16 *)pDst; + + memcpy (pOverlapArea + (BTA_DM_PCM_OVERLAP_SIZE * 2), pSrc, BTA_DM_PCM_OVERLAP_SIZE * 2); + + pOv = (SRC_TYPE *)(pOverlapArea + BTA_DM_PCM_OVERLAP_SIZE); + pOvEnd = (SRC_TYPE *)(pOverlapArea + (BTA_DM_PCM_OVERLAP_SIZE * 3)); + + pIn = (SRC_TYPE *)(pSrc + BTA_DM_PCM_OVERLAP_SIZE); + pInEnd = (SRC_TYPE *)(pSrc + (dwSrcSamples * SRC_CHANNELS * sizeof (SRC_TYPE)) - BTA_DM_PCM_OVERLAP_SIZE); + + if (dwSrcSps == BTA_DM_PCM_SMPL_RATE_44100) { + CONVERT_44100_TO_BLUETOOTH(pOv, pOvEnd); + CONVERT_44100_TO_BLUETOOTH(pIn, pInEnd); + } else if (dwSrcSps == BTA_DM_PCM_SMPL_RATE_22050) { + CONVERT_22050_TO_BLUETOOTH(pOv, pOvEnd); + CONVERT_22050_TO_BLUETOOTH(pIn, pInEnd); + } else if (dwSrcSps == BTA_DM_PCM_SMPL_RATE_11025) { + CONVERT_11025_TO_BLUETOOTH(pOv, pOvEnd); + CONVERT_11025_TO_BLUETOOTH(pIn, pInEnd); + } + + memcpy (pOverlapArea, pSrc + (dwSrcSamples * SRC_CHANNELS * sizeof (SRC_TYPE)) - \ + (BTA_DM_PCM_OVERLAP_SIZE * 2), BTA_DM_PCM_OVERLAP_SIZE * 2); + + *pLastCurPos = CurrentPos; + + return (psBtOut - (INT16 *)pDst); +} + +INT32 Convert_16S_ToBT_NoFilter (void *pSrc, void *pDst, UINT32 dwSrcSamples, UINT32 dwSrcSps) +{ + INT32 CurrentPos; + INT16 *psSrc = (INT16 *)pSrc; + INT16 *psDst = (INT16 *)pDst; + INT16 sWorker; + + // start at dwSpsSrc / 2, decrement by 8000 + // + CurrentPos = (dwSrcSps >> 1); + + while (dwSrcSamples--) { + CurrentPos -= 8000; + + if (CurrentPos >= 0) { + psSrc += 2; + } else { + /* CR 82894, to avoid overflow, divide before add */ + sWorker = ((*psSrc) >> 1 ); + psSrc++; + sWorker += ((*psSrc) >> 1 ); + psSrc++; + + *psDst++ = sWorker; + + CurrentPos += dwSrcSps; + } + } + + return (psDst - (INT16 *)pDst); +} + +/******************************************************************************* +** +** Function BTA_DmPcmInitSamples +** +** Description initialize the down sample converter. +** +** src_sps: original samples per second (source audio data) +** (ex. 44100, 48000) +** bits: number of bits per pcm sample (16) +** n_channels: number of channels (i.e. mono(1), stereo(2)...) +** +** Returns none +** +*******************************************************************************/ +void BTA_DmPcmInitSamples (UINT32 src_sps, UINT32 bits, UINT32 n_channels) +{ + if ((p_bta_dm_pcm_cb = (tBTA_DM_PCM_RESAMPLE_CB *)osi_malloc(sizeof(tBTA_DM_PCM_RESAMPLE_CB))) == NULL) { + APPL_TRACE_ERROR("%s malloc failed!", __func__); + return; + } + tBTA_DM_PCM_RESAMPLE_CB *p_cb = p_bta_dm_pcm_cb; + + p_cb->cur_pos = src_sps / 2; + p_cb->src_sps = src_sps; + p_cb->bits = bits; + p_cb->n_channels = n_channels; + p_cb->sample_size = 2; + p_cb->divisor = 2; + + memset(p_cb->overlap_area, 0, sizeof(p_cb->overlap_area) ); + + if ((src_sps == BTA_DM_PCM_SMPL_RATE_44100) || + (src_sps == BTA_DM_PCM_SMPL_RATE_22050) || + (src_sps == BTA_DM_PCM_SMPL_RATE_11025)) { + p_cb->can_be_filtered = 1; + } else { + p_cb->can_be_filtered = 0; + } + +#if BTA_DM_SCO_DEBUG + APPL_TRACE_DEBUG("bta_dm_pcm_init_samples: n_channels = %d bits = %d", n_channels, bits); +#endif + if (n_channels == 1) { + /* mono */ + if (bits == 8) { + p_cb->filter = (PCONVERT_TO_BT_FILTERED) Convert_8M_ToBT_Filtered; + p_cb->nofilter = (PCONVERT_TO_BT_NOFILTER) Convert_8M_ToBT_NoFilter; + p_cb->divisor = 1; + } else { + p_cb->filter = (PCONVERT_TO_BT_FILTERED) Convert_16M_ToBT_Filtered; + p_cb->nofilter = (PCONVERT_TO_BT_NOFILTER) Convert_16M_ToBT_NoFilter; + } + } else { + /* stereo */ + if (bits == 8) { + p_cb->filter = (PCONVERT_TO_BT_FILTERED) Convert_8S_ToBT_Filtered; + p_cb->nofilter = (PCONVERT_TO_BT_NOFILTER) Convert_8S_ToBT_NoFilter; + } else { + p_cb->filter = (PCONVERT_TO_BT_FILTERED) Convert_16S_ToBT_Filtered; + p_cb->nofilter = (PCONVERT_TO_BT_NOFILTER) Convert_16S_ToBT_NoFilter; + p_cb->divisor = 4; + } + } + +#if BTA_DM_SCO_DEBUG + APPL_TRACE_DEBUG("bta_pcm_init_dwn_sample: cur_pos %d, src_sps %d", \ + p_cb->cur_pos, p_cb->src_sps); + APPL_TRACE_DEBUG("bta_pcm_init_dwn_sample: bits %d, n_channels %d, sample_size %d, ", \ + p_cb->bits, p_cb->n_channels, p_cb->sample_size); + APPL_TRACE_DEBUG("bta_pcm_init_dwn_sample: can_be_filtered %d, n_channels: %d, \ + divisor %d", p_cb->can_be_filtered, p_cb->n_channels, p_cb->divisor); +#endif + +} + +/******************************************************************************* +** +** Function BTA_DmPcmDeinitSamples +** +** Description Deinitialize the down sample converter. +** +** Returns none +** +*******************************************************************************/ +void BTA_DmPcmDeinitSamples(void) { + osi_free(p_bta_dm_pcm_cb); + p_bta_dm_pcm_cb = NULL; +} + +/************************************************************************************** +** Function BTA_DmPcmResample +** +** Description Down sampling utility to convert higher sampling rate into 8K/16bits +** PCM samples. +** +** Parameters p_src: pointer to the buffer where the original sampling PCM +** are stored. +** in_bytes: Length of the input PCM sample buffer in byte. +** p_dst: pointer to the buffer which is to be used to store +** the converted PCM samples. +** +** +** Returns INT32: number of samples converted. +** +**************************************************************************************/ +INT32 BTA_DmPcmResample (void *p_src, UINT32 in_bytes, void *p_dst) +{ + UINT32 out_sample; + +#if BTA_DM_SCO_DEBUG + APPL_TRACE_DEBUG("bta_pcm_resample : insamples %d", (in_bytes / p_bta_dm_pcm_cb->divisor)); +#endif + if (p_bta_dm_pcm_cb->can_be_filtered) { + out_sample = (*p_bta_dm_pcm_cb->filter) (p_src, p_dst, (in_bytes / p_bta_dm_pcm_cb->divisor), + p_bta_dm_pcm_cb->src_sps, (INT32 *) &(p_bta_dm_pcm_cb->cur_pos), p_bta_dm_pcm_cb->overlap_area); + } else { + out_sample = (*p_bta_dm_pcm_cb->nofilter) (p_src, p_dst, + (in_bytes / p_bta_dm_pcm_cb->divisor), p_bta_dm_pcm_cb->src_sps); + } + +#if BTA_DM_SCO_DEBUG + APPL_TRACE_DEBUG("bta_pcm_resample : outsamples %d", out_sample); +#endif + + return (out_sample); +} +#endif diff --git a/lib/bt/host/bluedroid/bta/dm/include/bta_dm_int.h b/lib/bt/host/bluedroid/bta/dm/include/bta_dm_int.h new file mode 100644 index 00000000..a46651d3 --- /dev/null +++ b/lib/bt/host/bluedroid/bta/dm/include/bta_dm_int.h @@ -0,0 +1,1892 @@ +/****************************************************************************** + * + * Copyright (C) 2003-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This is the private interface file for the BTA device manager. + * + ******************************************************************************/ +#ifndef BTA_DM_INT_H +#define BTA_DM_INT_H + +#include "common/bt_target.h" +#include "freertos/semphr.h" +#include "bta/bta_sys.h" +#if (BLE_INCLUDED == TRUE && (defined BTA_GATT_INCLUDED) && (BTA_GATT_INCLUDED == TRUE)) +#include "bta/bta_gatt_api.h" +#endif + + + +/***************************************************************************** +** Constants and data types +*****************************************************************************/ + + +#define BTA_COPY_DEVICE_CLASS(coddst, codsrc) {((UINT8 *)(coddst))[0] = ((UINT8 *)(codsrc))[0]; \ + ((UINT8 *)(coddst))[1] = ((UINT8 *)(codsrc))[1]; \ + ((UINT8 *)(coddst))[2] = ((UINT8 *)(codsrc))[2];} + + +#define BTA_DM_MSG_LEN 50 + +#define BTA_SERVICE_ID_TO_SERVICE_MASK(id) (1 << (id)) + +/* DM events */ +enum { + /* device manager local device API events */ + BTA_DM_API_ENABLE_EVT = BTA_SYS_EVT_START(BTA_ID_DM), + BTA_DM_API_DISABLE_EVT, + BTA_DM_API_SET_NAME_EVT, + BTA_DM_API_GET_NAME_EVT, +#if (ESP_COEX_VSC_INCLUDED == TRUE) + BTA_DM_API_CFG_COEX_ST_EVT, +#endif +#if (CLASSIC_BT_INCLUDED == TRUE) + BTA_DM_API_CONFIG_EIR_EVT, + BTA_DM_API_PAGE_TO_SET_EVT, + BTA_DM_API_PAGE_TO_GET_EVT, + BTA_DM_API_SET_ACL_PKT_TYPES_EVT, +#endif + BTA_DM_API_SET_AFH_CHANNELS_EVT, +#if (SDP_INCLUDED == TRUE) + BTA_DM_API_GET_REMOTE_NAME_EVT, +#endif + BTA_DM_API_SET_VISIBILITY_EVT, + + BTA_DM_ACL_CHANGE_EVT, + BTA_DM_API_ADD_DEVICE_EVT, + BTA_DM_API_REMOVE_ACL_EVT, +#if (SMP_INCLUDED == TRUE) + /* security API events */ + BTA_DM_API_BOND_EVT, + BTA_DM_API_BOND_CANCEL_EVT, + BTA_DM_API_SET_PIN_TYPE_EVT, + BTA_DM_API_PIN_REPLY_EVT, +#endif ///SMP_INCLUDED == TRUE +#if (BTA_DM_PM_INCLUDED == TRUE) + /* power manger events */ + BTA_DM_PM_BTM_STATUS_EVT, + BTA_DM_PM_TIMER_EVT, +#endif /* #if (BTA_DM_PM_INCLUDED == TRUE) */ +#if (BTA_DM_QOS_INCLUDED == TRUE) + /* Quality of Service set events */ + BTA_DM_API_QOS_SET_EVT, +#endif /* #if (BTA_DM_QOS_INCLUDED == TRUE) */ +#if (SMP_INCLUDED == TRUE) +#if (CLASSIC_BT_INCLUDED == TRUE) + /* simple pairing events */ + BTA_DM_API_CONFIRM_EVT, + BTA_DM_API_KEY_REQ_EVT, +#endif /* (CLASSIC_BT_INCLUDED == TRUE) */ + BTA_DM_API_SET_ENCRYPTION_EVT, +#endif /* (SMP_INCLUDED == TRUE) */ +#if (BTM_OOB_INCLUDED == TRUE && SMP_INCLUDED == TRUE) + BTA_DM_API_LOC_OOB_EVT, + BTA_DM_API_OOB_REPLY_EVT, + BTA_DM_API_SC_OOB_REPLY_EVT, + BTA_DM_API_SC_CR_OOB_DATA_EVT, + BTA_DM_CI_IO_REQ_EVT, + BTA_DM_CI_RMT_OOB_EVT, +#endif /* BTM_OOB_INCLUDED */ + + +#if BLE_INCLUDED == TRUE +#if SMP_INCLUDED == TRUE + BTA_DM_API_ADD_BLEKEY_EVT, + BTA_DM_API_ADD_BLEDEVICE_EVT, + BTA_DM_API_BLE_PASSKEY_REPLY_EVT, + BTA_DM_API_BLE_SET_STATIC_PASSKEY_EVT, + BTA_DM_API_BLE_CONFIRM_REPLY_EVT, + BTA_DM_API_BLE_SEC_GRANT_EVT, +#endif ///SMP_INCLUDED == TRUE + BTA_DM_API_BLE_SET_BG_CONN_TYPE, + BTA_DM_API_BLE_CONN_PARAM_EVT, + BTA_DM_API_BLE_CONN_SCAN_PARAM_EVT, + BTA_DM_API_BLE_SCAN_PARAM_EVT, + /*******This event added by Yulong at 2016/10/25 to + support the scan filter setting for the APP******/ + BTA_DM_API_BLE_SCAN_FIL_PARAM_EVT, + BTA_DM_API_BLE_OBSERVE_EVT, + BTA_DM_API_BLE_SCAN_EVT, + BTA_DM_API_UPDATE_CONN_PARAM_EVT, + /*******This event added by Yulong at 2016/9/9 to + support the random address setting for the APP******/ + BTA_DM_API_SET_RAND_ADDR_EVT, + BTA_DM_API_CLEAR_RAND_ADDR_EVT, + /*******This event added by Yulong at 2016/10/19 to + support stop the ble advertising setting by the APP******/ + BTA_DM_API_BLE_STOP_ADV_EVT, +#if BLE_PRIVACY_SPT == TRUE + BTA_DM_API_LOCAL_PRIVACY_EVT, +#endif + BTA_DM_API_LOCAL_ICON_EVT, + BTA_DM_API_BLE_ADV_PARAM_EVT, + + /*******This event added by Yulong at 2016/10/20 to + support setting the ble advertising param by the APP******/ + BTA_DM_API_BLE_ADV_PARAM_All_EVT, + BTA_DM_API_BLE_SET_ADV_CONFIG_EVT, + /* Add for set raw advertising data */ + BTA_DM_API_BLE_SET_ADV_CONFIG_RAW_EVT, + BTA_DM_API_BLE_SET_SCAN_RSP_EVT, + /* Add for set raw scan response data */ + BTA_DM_API_BLE_SET_SCAN_RSP_RAW_EVT, + BTA_DM_API_BLE_BROADCAST_EVT, + BTA_DM_API_SET_DATA_LENGTH_EVT, + BTA_DM_API_BLE_SET_LONG_ADV_EVT, +#if BLE_ANDROID_CONTROLLER_SCAN_FILTER == TRUE + BTA_DM_API_CFG_FILTER_COND_EVT, + BTA_DM_API_SCAN_FILTER_SETUP_EVT, + BTA_DM_API_SCAN_FILTER_ENABLE_EVT, +#endif + BTA_DM_API_BLE_MULTI_ADV_ENB_EVT, + BTA_DM_API_BLE_MULTI_ADV_PARAM_UPD_EVT, + BTA_DM_API_BLE_MULTI_ADV_DATA_EVT, + BTA_DM_API_BLE_MULTI_ADV_DISABLE_EVT, + BTA_DM_API_BLE_SETUP_STORAGE_EVT, + BTA_DM_API_BLE_ENABLE_BATCH_SCAN_EVT, + BTA_DM_API_BLE_DISABLE_BATCH_SCAN_EVT, + BTA_DM_API_BLE_READ_SCAN_REPORTS_EVT, + BTA_DM_API_BLE_TRACK_ADVERTISER_EVT, + BTA_DM_API_BLE_ENERGY_INFO_EVT, + BTA_DM_API_BLE_DISCONNECT_EVT, + +#endif + + BTA_DM_API_ENABLE_TEST_MODE_EVT, + BTA_DM_API_DISABLE_TEST_MODE_EVT, + BTA_DM_API_EXECUTE_CBACK_EVT, + BTA_DM_API_REMOVE_ALL_ACL_EVT, + BTA_DM_API_REMOVE_DEVICE_EVT, + BTA_DM_API_BLE_SET_CHANNELS_EVT, + BTA_DM_API_UPDATE_WHITE_LIST_EVT, + BTA_DM_API_CLEAR_WHITE_LIST_EVT, + BTA_DM_API_BLE_READ_ADV_TX_POWER_EVT, + BTA_DM_API_READ_RSSI_EVT, +#if BLE_INCLUDED == TRUE + BTA_DM_API_UPDATE_DUPLICATE_EXCEPTIONAL_LIST_EVT, +#endif +#if (BLE_50_FEATURE_SUPPORT == TRUE) + BTA_DM_API_READ_PHY_EVT, + BTA_DM_API_SET_PER_DEF_PHY_EVT, + BTA_DM_API_SET_PER_PHY_EVT, + BTA_DM_API_SET_EXT_ADV_RAND_ADDR_EVT, + BTA_DM_API_SET_EXT_ADV_PARAMS_EVT, + BTA_DM_API_CFG_ADV_DATA_RAW_EVT, + BTA_DM_API_EXT_ADV_ENABLE_EVT, + BTA_DM_API_EXT_ADV_SET_REMOVE_EVT, + BTA_DM_API_EXT_ADV_SET_CLEAR_EVT, + BTA_DM_API_PERIODIC_ADV_SET_PARAMS_EVT, + BTA_DM_API_PERIODIC_ADV_CFG_DATA_EVT, + BTA_DM_API_PERIODIC_ADV_ENABLE_EVT, + BTA_DM_API_PERIODIC_ADV_SYNC_EVT, + BTA_DM_API_PERIODIC_ADV_SYNC_CANCEL_EVT, + BTA_DM_API_PERIODIC_ADV_SYNC_TERMINATE_EVT, + BTA_DM_API_PERIODIC_ADV_ADD_DEV_TO_LSIT_EVT, + BTA_DM_API_PERIODIC_ADV_REMOVE_DEV_FROM_LSIT_EVT, + BTA_DM_API_PERIODIC_ADV_CLEAR_DEV_EVT, + BTA_DM_API_SET_EXT_SCAN_PARAMS_EVT, + BTA_DM_API_START_EXT_SCAN_EVT, + BTA_DM_API_SET_PERF_EXT_CONN_PARAMS_EVT, + BTA_DM_API_EXT_CONN_EVT, + BTA_DM_API_DTM_ENH_TX_START_EVT, + BTA_DM_API_DTM_ENH_RX_START_EVT, +#endif // #if (BLE_50_FEATURE_SUPPORT == TRUE) +#if (BLE_FEAT_PERIODIC_ADV_SYNC_TRANSFER == TRUE) + BTA_DM_API_PERIODIC_ADV_RECV_ENABLE_EVT, + BTA_DM_API_PERIODIC_ADV_SYNC_TRANS_EVT, + BTA_DM_API_PERIODIC_ADV_SET_INFO_TRANS_EVT, + BTA_DM_API_SET_PERIODIC_ADV_SYNC_TRANS_PARAMS_EVT, +#endif // #if (BLE_FEAT_PERIODIC_ADV_SYNC_TRANSFER == TRUE) +#if BLE_INCLUDED == TRUE + BTA_DM_API_DTM_TX_START_EVT, + BTA_DM_API_DTM_RX_START_EVT, + BTA_DM_API_DTM_STOP_EVT, + BTA_DM_API_BLE_CLEAR_ADV_EVT, +#endif + BTA_DM_MAX_EVT +}; + + +/* DM search events */ +enum { + /* DM search API events */ + BTA_DM_API_SEARCH_EVT = BTA_SYS_EVT_START(BTA_ID_DM_SEARCH), + BTA_DM_API_SEARCH_CANCEL_EVT, + BTA_DM_API_DISCOVER_EVT, + BTA_DM_INQUIRY_CMPL_EVT, + BTA_DM_REMT_NAME_EVT, + BTA_DM_SDP_RESULT_EVT, + BTA_DM_SEARCH_CMPL_EVT, + BTA_DM_DISCOVERY_RESULT_EVT, + BTA_DM_API_DI_DISCOVER_EVT, + BTA_DM_DISC_CLOSE_TOUT_EVT, +}; + +/* data type for BTA_DM_API_ENABLE_EVT */ +typedef struct { + BT_HDR hdr; + tBTA_DM_SEC_CBACK *p_sec_cback; +} tBTA_DM_API_ENABLE; + +/* data type for BTA_DM_API_SET_NAME_EVT */ +typedef struct { + BT_HDR hdr; + BD_NAME name; /* max 248 bytes name, plus must be Null terminated */ +} tBTA_DM_API_SET_NAME; + +typedef struct { + BT_HDR hdr; + tBTA_GET_DEV_NAME_CBACK *p_cback; +} tBTA_DM_API_GET_NAME; + +#if (ESP_COEX_VSC_INCLUDED == TRUE) +typedef struct { + BT_HDR hdr; + UINT8 op; + UINT8 type; + UINT8 status; +} tBTA_DM_API_CFG_COEX_STATUS; +#endif + +/* data type for BTA_DM_API_CONFIG_EIR_EVT */ +typedef struct { + BT_HDR hdr; + BOOLEAN eir_fec_required; + BOOLEAN eir_included_name; + BOOLEAN eir_included_tx_power; + BOOLEAN eir_included_uuid; + UINT8 eir_flags; + UINT8 eir_manufac_spec_len; + UINT8 *eir_manufac_spec; + UINT8 eir_url_len; + UINT8 *eir_url; + UINT8 data[]; +}tBTA_DM_API_CONFIG_EIR; + +/* data type for BTA_DM_API_SET_AFH_CHANNELS_EVT */ +typedef struct { + BT_HDR hdr; + AFH_CHANNELS channels; + tBTA_CMPL_CB *set_afh_cb; +}tBTA_DM_API_SET_AFH_CHANNELS; + +/* data type for BTA_DM_API_PAGE_TO_SET_EVT */ +typedef struct { + BT_HDR hdr; + UINT16 page_to; + tBTM_CMPL_CB *set_page_to_cb; +} tBTA_DM_API_PAGE_TO_SET; + +/* data type for BTA_DM_API_PAGE_TO_GET_EVT */ +typedef struct { + BT_HDR hdr; + tBTM_CMPL_CB *get_page_to_cb; +} tBTA_DM_API_PAGE_TO_GET; + +/* data type for BTA_DM_API_SET_ACL_PKT_TYPES_EVT */ +typedef struct { + BT_HDR hdr; + BD_ADDR rmt_addr; + UINT16 pkt_types; + tBTM_CMPL_CB *set_acl_pkt_types_cb; +} tBTA_DM_API_SET_ACL_PKT_TYPES; + +/* data type for BTA_DM_API_GET_REMOTE_NAME_EVT */ +typedef struct { + BT_HDR hdr; + BD_ADDR rmt_addr; + BD_NAME rmt_name; + tBTA_TRANSPORT transport; + tBTA_CMPL_CB *rmt_name_cb; +} tBTA_DM_API_GET_REMOTE_NAME; + +#if (BLE_INCLUDED == TRUE) +/* data type for BTA_DM_API_BLE_SET_CHANNELS_EVT */ +typedef struct { + BT_HDR hdr; + AFH_CHANNELS channels; + tBTA_CMPL_CB *set_channels_cb; +}tBTA_DM_API_BLE_SET_CHANNELS; + +typedef struct { + BT_HDR hdr; + BOOLEAN add_remove; + BD_ADDR remote_addr; + tBLE_ADDR_TYPE addr_type; + tBTA_UPDATE_WHITELIST_CBACK *update_wl_cb; +}tBTA_DM_API_UPDATE_WHITE_LIST; + +typedef struct { + BT_HDR hdr; + UINT8 subcode; + UINT32 type; + BD_ADDR device_info; + tBTA_UPDATE_DUPLICATE_EXCEPTIONAL_LIST_CMPL_CBACK *exceptional_list_cb; +}tBTA_DM_API_UPDATE_DUPLICATE_EXCEPTIONAL_LIST; + +typedef struct { + BT_HDR hdr; + tBTA_CMPL_CB *read_tx_power_cb; +}tBTA_DM_API_READ_ADV_TX_POWER; +#endif ///BLE_INCLUDED == TRUE + +typedef struct { + BT_HDR hdr; + BD_ADDR remote_addr; + tBTA_TRANSPORT transport; + tBTA_CMPL_CB *read_rssi_cb; +}tBTA_DM_API_READ_RSSI; + +/* data type for BTA_DM_API_SET_VISIBILITY_EVT */ +typedef struct { + BT_HDR hdr; + tBTA_DM_DISC disc_mode; + tBTA_DM_CONN conn_mode; + UINT8 pair_mode; + UINT8 conn_paired_only; +} tBTA_DM_API_SET_VISIBILITY; + +enum { + BTA_DM_RS_NONE, /* straight API call */ + BTA_DM_RS_OK, /* the role switch result - successful */ + BTA_DM_RS_FAIL /* the role switch result - failed */ +}; +typedef UINT8 tBTA_DM_RS_RES; + +/* data type for BTA_DM_API_SEARCH_EVT */ +typedef struct { + BT_HDR hdr; + tBTA_DM_INQ inq_params; + tBTA_SERVICE_MASK services; + tBTA_DM_SEARCH_CBACK *p_cback; + tBTA_DM_RS_RES rs_res; +#if BLE_INCLUDED == TRUE && BTA_GATT_INCLUDED == TRUE + UINT8 num_uuid; + tBT_UUID *p_uuid; +#endif +} tBTA_DM_API_SEARCH; + +#if (SDP_INCLUDED == TRUE) +/* data type for BTA_DM_API_DISCOVER_EVT */ +typedef struct { + BT_HDR hdr; + BD_ADDR bd_addr; + tBTA_SERVICE_MASK services; + tBTA_DM_SEARCH_CBACK *p_cback; + BOOLEAN sdp_search; + tBTA_TRANSPORT transport; +#if BLE_INCLUDED == TRUE && BTA_GATT_INCLUDED == TRUE + UINT8 num_uuid; + tBT_UUID *p_uuid; +#endif + tSDP_UUID uuid; +} tBTA_DM_API_DISCOVER; +#endif ///SDP_INCLUDED == TRUE + +/* data type for BTA_DM_API_DI_DISC_EVT */ +typedef struct { + BT_HDR hdr; + BD_ADDR bd_addr; +#if (SDP_INCLUDED == TRUE) + tBTA_DISCOVERY_DB *p_sdp_db; +#endif ///SDP_INCLUDED == TRUE + UINT32 len; + tBTA_DM_SEARCH_CBACK *p_cback; +} tBTA_DM_API_DI_DISC; + +/* data type for BTA_DM_API_BOND_EVT */ +typedef struct { + BT_HDR hdr; + BD_ADDR bd_addr; + tBTA_TRANSPORT transport; +} tBTA_DM_API_BOND; + +/* data type for BTA_DM_API_BOND_CANCEL_EVT */ +typedef struct { + BT_HDR hdr; + BD_ADDR bd_addr; + tBTA_TRANSPORT transport; +} tBTA_DM_API_BOND_CANCEL; + +/* data type for BTA_DM_API_SET_PIN_TYPE_EVT */ +typedef struct { + BT_HDR hdr; + UINT8 pin_type; + UINT8 pin_len; + UINT8 p_pin[PIN_CODE_LEN]; +} tBTA_DM_API_SET_PIN_TYPE; + +/* data type for BTA_DM_API_PIN_REPLY_EVT */ +typedef struct { + BT_HDR hdr; + BD_ADDR bd_addr; + BOOLEAN accept; + UINT8 pin_len; + UINT8 p_pin[PIN_CODE_LEN]; +} tBTA_DM_API_PIN_REPLY; + +/* data type for BTA_DM_API_LOC_OOB_EVT */ +typedef struct { + BT_HDR hdr; +} tBTA_DM_API_LOC_OOB; + +/* data type for BTA_DM_API_OOB_REPLY_EVT */ +typedef struct { + BT_HDR hdr; + BD_ADDR bd_addr; + UINT8 len; + UINT8 value[BT_OCTET16_LEN]; + UINT8 c[BT_OCTET16_LEN]; + UINT8 r[BT_OCTET16_LEN]; +} tBTA_DM_API_OOB_REPLY; + +/* data type for BTA_DM_API_SC_OOB_REPLY_EVT */ +typedef struct { + BT_HDR hdr; + BD_ADDR bd_addr; + UINT8 c[BT_OCTET16_LEN]; + UINT8 r[BT_OCTET16_LEN]; +} tBTA_DM_API_SC_OOB_REPLY; + +/* data type for BTA_DM_API_SC_CR_OOB_DATA_EVT */ +typedef struct { + BT_HDR hdr; +} tBTA_DM_API_SC_CR_OOB_DATA; + +/* data type for BTA_DM_API_CONFIRM_EVT */ +typedef struct { + BT_HDR hdr; + BD_ADDR bd_addr; + BOOLEAN accept; +} tBTA_DM_API_CONFIRM; + +/* data type for BTA_DM_API_KEY_REQ_EVT */ +typedef struct { + BT_HDR hdr; + BD_ADDR bd_addr; + BOOLEAN accept; + UINT32 passkey; +} tBTA_DM_API_KEY_REQ; + +/* data type for BTA_DM_CI_IO_REQ_EVT */ +typedef struct { + BT_HDR hdr; + BD_ADDR bd_addr; + tBTA_IO_CAP io_cap; + tBTA_OOB_DATA oob_data; + tBTA_AUTH_REQ auth_req; +} tBTA_DM_CI_IO_REQ; + +/* data type for BTA_DM_CI_RMT_OOB_EVT */ +typedef struct { + BT_HDR hdr; + BD_ADDR bd_addr; + BT_OCTET16 c; + BT_OCTET16 r; + BOOLEAN accept; +} tBTA_DM_CI_RMT_OOB; + +/* data type for BTA_DM_REMT_NAME_EVT */ +typedef struct { + BT_HDR hdr; + tBTA_DM_SEARCH result; +} tBTA_DM_REM_NAME; + +/* data type for tBTA_DM_DISC_RESULT */ +typedef struct { + BT_HDR hdr; + tBTA_DM_SEARCH result; +} tBTA_DM_DISC_RESULT; + + +/* data type for BTA_DM_INQUIRY_CMPL_EVT */ +typedef struct { + BT_HDR hdr; + UINT8 num; +} tBTA_DM_INQUIRY_CMPL; + +/* data type for BTA_DM_SDP_RESULT_EVT */ +typedef struct { + BT_HDR hdr; + UINT16 sdp_result; +} tBTA_DM_SDP_RESULT; + +/* data type for BTA_DM_ACL_CHANGE_EVT */ +typedef struct { + BT_HDR hdr; + tBTM_BL_EVENT event; + UINT8 busy_level; + UINT8 busy_level_flags; + BOOLEAN is_new; + UINT8 new_role; + BD_ADDR bd_addr; + UINT8 hci_status; + BOOLEAN sc_downgrade; +#if BLE_INCLUDED == TRUE + UINT16 handle; +#endif + tBT_TRANSPORT transport; +} tBTA_DM_ACL_CHANGE; + +#if (BTA_DM_PM_INCLUDED == TRUE) +/* data type for BTA_DM_PM_BTM_STATUS_EVT */ +typedef struct { + + BT_HDR hdr; + BD_ADDR bd_addr; + tBTM_PM_STATUS status; + UINT16 value; + UINT8 hci_status; + +} tBTA_DM_PM_BTM_STATUS; + +/* data type for BTA_DM_PM_TIMER_EVT */ +typedef struct { + BT_HDR hdr; + BD_ADDR bd_addr; + tBTA_DM_PM_ACTION pm_request; +} tBTA_DM_PM_TIMER; +#endif /* #if (BTA_DM_PM_INCLUDED == TRUE) */ + +#if (BTA_DM_QOS_INCLUDED == TRUE) +/* data type for BTA_DM_API_QOS_SET_EVT */ +typedef struct { + BT_HDR hdr; + BD_ADDR bd_addr; + UINT32 t_poll; + tBTM_CMPL_CB *p_cb; +} tBTA_DM_API_QOS_SET; +#endif /* #if (BTA_DM_QOS_INCLUDED == TRUE) */ + +/* data type for BTA_DM_API_ADD_DEVICE_EVT */ +typedef struct { + BT_HDR hdr; + BD_ADDR bd_addr; + DEV_CLASS dc; + LINK_KEY link_key; + tBTA_SERVICE_MASK tm; + BOOLEAN is_trusted; + UINT8 key_type; + tBTA_IO_CAP io_cap; + BOOLEAN link_key_known; + BOOLEAN dc_known; + BD_NAME bd_name; + UINT8 features[BTA_FEATURE_BYTES_PER_PAGE * (BTA_EXT_FEATURES_PAGE_MAX + 1)]; + UINT8 pin_length; + UINT8 sc_support; +} tBTA_DM_API_ADD_DEVICE; + +/* data type for BTA_DM_API_REMOVE_ACL_EVT */ +typedef struct { + BT_HDR hdr; + BD_ADDR bd_addr; + UINT8 transport; +} tBTA_DM_API_REMOVE_DEVICE; + +/* data type for BTA_DM_API_EXECUTE_CBACK_EVT */ +typedef struct { + BT_HDR hdr; + void *p_param; + tBTA_DM_EXEC_CBACK *p_exec_cback; +} tBTA_DM_API_EXECUTE_CBACK; + +/* data type for tBTA_DM_API_SET_ENCRYPTION */ +typedef struct { + BT_HDR hdr; + tBTA_TRANSPORT transport; + tBTA_DM_ENCRYPT_CBACK *p_callback; + tBTA_DM_BLE_SEC_ACT sec_act; + BD_ADDR bd_addr; +} tBTA_DM_API_SET_ENCRYPTION; + +#if BLE_INCLUDED == TRUE +typedef struct { + BT_HDR hdr; + BD_ADDR bd_addr; + tBTA_LE_KEY_VALUE blekey; + tBTA_LE_KEY_TYPE key_type; + +} tBTA_DM_API_ADD_BLEKEY; + +typedef struct { + BT_HDR hdr; + BD_ADDR bd_addr; + tBT_DEVICE_TYPE dev_type ; + UINT32 auth_mode; + tBLE_ADDR_TYPE addr_type; + +} tBTA_DM_API_ADD_BLE_DEVICE; + +typedef struct { + BT_HDR hdr; + BD_ADDR bd_addr; + BOOLEAN accept; + UINT32 passkey; +} tBTA_DM_API_PASSKEY_REPLY; + +typedef struct { + BT_HDR hdr; + BOOLEAN add; + UINT32 static_passkey; +} tBTA_DM_API_SET_DEFAULT_PASSKEY; + +typedef struct { + BT_HDR hdr; + BD_ADDR bd_addr; + tBTA_DM_BLE_SEC_GRANT res; +} tBTA_DM_API_BLE_SEC_GRANT; + + +typedef struct { + BT_HDR hdr; + tBTA_DM_BLE_CONN_TYPE bg_conn_type; + tBTA_DM_BLE_SEL_CBACK *p_select_cback; +} tBTA_DM_API_BLE_SET_BG_CONN_TYPE; + +/* set prefered BLE connection parameters for a device */ +typedef struct { + BT_HDR hdr; + BD_ADDR peer_bda; + UINT16 conn_int_min; + UINT16 conn_int_max; + UINT16 supervision_tout; + UINT16 slave_latency; + +} tBTA_DM_API_BLE_CONN_PARAMS; + +typedef struct { + BT_HDR hdr; + BD_ADDR peer_bda; + BOOLEAN privacy_enable; + +} tBTA_DM_API_ENABLE_PRIVACY; + +typedef struct { + BT_HDR hdr; + BOOLEAN privacy_enable; + tBTA_SET_LOCAL_PRIVACY_CBACK *set_local_privacy_cback; +} tBTA_DM_API_LOCAL_PRIVACY; + +typedef struct { + BT_HDR hdr; + uint16_t icon; +} tBTA_DM_API_LOCAL_ICON; + +/* set scan parameter for BLE connections */ +typedef struct { + BT_HDR hdr; + tBTA_GATTC_IF client_if; + UINT32 scan_int; + UINT32 scan_window; + tBLE_SCAN_MODE scan_mode; + tBLE_SCAN_PARAM_SETUP_CBACK scan_param_setup_cback; +} tBTA_DM_API_BLE_SCAN_PARAMS; + +typedef struct { + BT_HDR hdr; + tBTA_GATTC_IF client_if; + UINT32 scan_int; + UINT32 scan_window; + tBLE_SCAN_MODE scan_mode; + UINT8 addr_type_own; + UINT8 scan_duplicate_filter; + UINT8 scan_filter_policy; + tBLE_SCAN_PARAM_SETUP_CBACK scan_param_setup_cback; +} tBTA_DM_API_BLE_SCAN_FILTER_PARAMS; + + +/* set scan parameter for BLE connections */ +typedef struct { + BT_HDR hdr; + UINT16 scan_int; + UINT16 scan_window; +} tBTA_DM_API_BLE_CONN_SCAN_PARAMS; + +/* Data type for start/stop observe */ +typedef struct { + BT_HDR hdr; + BOOLEAN start; + UINT32 duration; + tBTA_DM_SEARCH_CBACK *p_cback; + tBTA_START_STOP_SCAN_CMPL_CBACK *p_start_scan_cback; + tBTA_START_STOP_SCAN_CMPL_CBACK *p_stop_scan_cback; + tBTA_START_STOP_ADV_CMPL_CBACK *p_stop_adv_cback; +} tBTA_DM_API_BLE_OBSERVE; + +/* Data type for start/stop scan */ +typedef struct { + BT_HDR hdr; + BOOLEAN start; + UINT32 duration; + tBTA_DM_SEARCH_CBACK *p_cback; + tBTA_START_STOP_SCAN_CMPL_CBACK *p_start_scan_cback; + tBTA_START_STOP_SCAN_CMPL_CBACK *p_stop_scan_cback; + tBTA_START_STOP_ADV_CMPL_CBACK *p_stop_adv_cback; +} tBTA_DM_API_BLE_SCAN; + +typedef struct { + BT_HDR hdr; + BD_ADDR remote_bda; + UINT16 tx_data_length; + tBTA_SET_PKT_DATA_LENGTH_CBACK *p_set_pkt_data_cback; +} tBTA_DM_API_BLE_SET_DATA_LENGTH; + +/* set the address for BLE device + this type added by Yulong at 2016/9/9*/ +typedef struct { + BT_HDR hdr; + tBLE_ADDR_TYPE addr_type; + BD_ADDR address; + tBTA_SET_RAND_ADDR_CBACK *p_set_rand_addr_cback; +} tBTA_DM_APT_SET_DEV_ADDR; + +typedef struct { + BT_HDR hdr; +} tBTA_DM_APT_CLEAR_ADDR; + +/* set adv parameter for BLE advertising */ +typedef struct { + BT_HDR hdr; + UINT16 adv_int_min; + UINT16 adv_int_max; + tBLE_BD_ADDR *p_dir_bda; +} tBTA_DM_API_BLE_ADV_PARAMS; + +/* set adv parameter for BLE advertising */ +typedef struct { + BT_HDR hdr; + UINT16 adv_int_min; + UINT16 adv_int_max; + UINT8 adv_type; + tBLE_ADDR_TYPE addr_type_own; + tBTM_BLE_ADV_CHNL_MAP channel_map; + tBTM_BLE_AFP adv_filter_policy; + tBLE_BD_ADDR *p_dir_bda; + tBTA_START_ADV_CMPL_CBACK *p_start_adv_cback; +} tBTA_DM_API_BLE_ADV_PARAMS_ALL; + + +typedef struct { + BT_HDR hdr; + BOOLEAN enable; + +} tBTA_DM_API_BLE_FEATURE; + +/* multi adv data structure */ +typedef struct { + BT_HDR hdr; + tBTA_BLE_MULTI_ADV_CBACK *p_cback; + void *p_ref; + tBTA_BLE_ADV_PARAMS *p_params; +} tBTA_DM_API_BLE_MULTI_ADV_ENB; + +typedef struct { + BT_HDR hdr; + UINT8 inst_id; + tBTA_BLE_ADV_PARAMS *p_params; +} tBTA_DM_API_BLE_MULTI_ADV_PARAM; + +typedef struct { + BT_HDR hdr; + UINT8 inst_id; + BOOLEAN is_scan_rsp; + tBTA_BLE_AD_MASK data_mask; + tBTA_BLE_ADV_DATA *p_data; +} tBTA_DM_API_BLE_MULTI_ADV_DATA; + +typedef struct { + BT_HDR hdr; + UINT8 inst_id; +} tBTA_DM_API_BLE_MULTI_ADV_DISABLE; + +typedef struct { + BT_HDR hdr; + UINT32 data_mask; + tBTA_BLE_ADV_DATA *p_adv_cfg; + tBTA_SET_ADV_DATA_CMPL_CBACK *p_adv_data_cback; +} tBTA_DM_API_SET_ADV_CONFIG; + +/* raw scan response and raw advertising data use + the same structure */ +typedef struct { + BT_HDR hdr; + UINT8 *p_raw_adv; + UINT32 raw_adv_len; + tBTA_SET_ADV_DATA_CMPL_CBACK *p_adv_data_cback; +} tBTA_DM_API_SET_ADV_CONFIG_RAW; + +typedef struct { + BT_HDR hdr; + UINT8 *adv_data; + UINT8 adv_data_len; + tBTA_SET_ADV_DATA_CMPL_CBACK *p_adv_data_cback; +} tBTA_DM_API_SET_LONG_ADV; + +typedef struct { + BT_HDR hdr; + UINT8 batch_scan_full_max; + UINT8 batch_scan_trunc_max; + UINT8 batch_scan_notify_threshold; + tBTA_BLE_SCAN_SETUP_CBACK *p_setup_cback; + tBTA_BLE_SCAN_THRESHOLD_CBACK *p_thres_cback; + tBTA_BLE_SCAN_REP_CBACK *p_read_rep_cback; + tBTA_DM_BLE_REF_VALUE ref_value; +} tBTA_DM_API_SET_STORAGE_CONFIG; + +typedef struct { + BT_HDR hdr; + tBTA_BLE_BATCH_SCAN_MODE scan_mode; + UINT32 scan_int; + UINT32 scan_window; + tBTA_BLE_DISCARD_RULE discard_rule; + tBLE_ADDR_TYPE addr_type; + tBTA_DM_BLE_REF_VALUE ref_value; +} tBTA_DM_API_ENABLE_SCAN; + +typedef struct { + BT_HDR hdr; + tBTA_DM_BLE_REF_VALUE ref_value; +} tBTA_DM_API_DISABLE_SCAN; + +typedef struct { + BT_HDR hdr; + tBTA_BLE_BATCH_SCAN_MODE scan_type; + tBTA_DM_BLE_REF_VALUE ref_value; +} tBTA_DM_API_READ_SCAN_REPORTS; + +typedef struct { + BT_HDR hdr; + tBTA_DM_BLE_REF_VALUE ref_value; + tBTA_BLE_TRACK_ADV_CBACK *p_track_adv_cback; +} tBTA_DM_API_TRACK_ADVERTISER; + +typedef struct { + BT_HDR hdr; + tBTA_BLE_ENERGY_INFO_CBACK *p_energy_info_cback; +} tBTA_DM_API_ENERGY_INFO; + +typedef struct { + BT_HDR hdr; + BD_ADDR remote_bda; +} tBTA_DM_API_BLE_DISCONNECT; + +typedef struct { + BT_HDR hdr; + UINT8 tx_channel; + UINT8 len_of_data; + UINT8 pkt_payload; + tBTA_DTM_CMD_CMPL_CBACK *p_dtm_cmpl_cback; +} tBTA_DM_API_BLE_DTM_TX_START; + +typedef struct { + BT_HDR hdr; + UINT8 rx_channel; + tBTA_DTM_CMD_CMPL_CBACK *p_dtm_cmpl_cback; +} tBTA_DM_API_BLE_DTM_RX_START; + +typedef struct { + BT_HDR hdr; + tBTA_DTM_CMD_CMPL_CBACK *p_dtm_cmpl_cback; +} tBTA_DM_API_BLE_DTM_STOP; + +typedef struct { + BT_HDR hdr; + tBTA_CLEAR_ADV_CMPL_CBACK *p_clear_adv_cback; +} tBTA_DM_API_CLEAR_ADV; + +#endif /* BLE_INCLUDED */ + +/* data type for BTA_DM_API_REMOVE_ACL_EVT */ +typedef struct { + BT_HDR hdr; + BD_ADDR bd_addr; + BOOLEAN remove_dev; + tBTA_TRANSPORT transport; + +} tBTA_DM_API_REMOVE_ACL; + +/* data type for BTA_DM_API_REMOVE_ALL_ACL_EVT */ +typedef struct { + BT_HDR hdr; + tBTA_DM_LINK_TYPE link_type; + +} tBTA_DM_API_REMOVE_ALL_ACL; +typedef struct { + BT_HDR hdr; + BD_ADDR bd_addr; + UINT16 min_int; + UINT16 max_int; + UINT16 latency; + UINT16 timeout; +} tBTA_DM_API_UPDATE_CONN_PARAM; + +#if BLE_ANDROID_CONTROLLER_SCAN_FILTER == TRUE +typedef struct { + BT_HDR hdr; + tBTA_DM_BLE_SCAN_COND_OP action; + tBTA_DM_BLE_PF_COND_TYPE cond_type; + tBTA_DM_BLE_PF_FILT_INDEX filt_index; + tBTA_DM_BLE_PF_COND_PARAM *p_cond_param; + tBTA_DM_BLE_PF_CFG_CBACK *p_filt_cfg_cback; + tBTA_DM_BLE_REF_VALUE ref_value; +} tBTA_DM_API_CFG_FILTER_COND; + +typedef struct { + BT_HDR hdr; + UINT8 action; + tBTA_DM_BLE_PF_STATUS_CBACK *p_filt_status_cback; + tBTA_DM_BLE_REF_VALUE ref_value; +} tBTA_DM_API_ENABLE_SCAN_FILTER; + +typedef struct { + BT_HDR hdr; + UINT8 action; + tBTA_DM_BLE_PF_FILT_INDEX filt_index; + tBTA_DM_BLE_PF_FILT_PARAMS filt_params; + tBLE_BD_ADDR *p_target; + tBTA_DM_BLE_PF_PARAM_CBACK *p_filt_param_cback; + tBTA_DM_BLE_REF_VALUE ref_value; +} tBTA_DM_API_SCAN_FILTER_PARAM_SETUP; +#endif +#if (BLE_50_FEATURE_SUPPORT == TRUE) + +typedef struct { + BT_HDR hdr; + UINT8 tx_channel; + UINT8 len_of_data; + UINT8 pkt_payload; + UINT8 phy; + tBTA_DTM_CMD_CMPL_CBACK *p_dtm_cmpl_cback; +} tBTA_DM_API_BLE_DTM_ENH_TX_START; +typedef struct { + BT_HDR hdr; + UINT8 rx_channel; + UINT8 phy; + UINT8 modulation_index; + tBTA_DTM_CMD_CMPL_CBACK *p_dtm_cmpl_cback; +} tBTA_DM_API_BLE_DTM_ENH_RX_START; + +#define BTA_PHY_1M_MASK (1 << 0) +#define BTA_PHY_2M_MASK (1 << 1) +#define BTAS_PHY_CODED_MASK (1 << 2) +typedef struct { + BT_HDR hdr; + BD_ADDR bd_addr; +} tBTA_DM_API_READ_PHY; + +typedef struct { + BT_HDR hdr; + tBTA_DM_BLE_GAP_PHY_MASK tx_phy_mask; + tBTA_DM_BLE_GAP_PHY_MASK rx_phy_mask; +} tBTA_DM_API_SET_PER_DEF_PHY; + +typedef struct { + BT_HDR hdr; + BD_ADDR bd_addr; + UINT8 all_phys; + tBTA_DM_BLE_GAP_PHY_MASK tx_phy_mask; + tBTA_DM_BLE_GAP_PHY_MASK rx_phy_mask; + UINT16 phy_options; +} tBTA_DM_API_SET_PER_PHY; + +typedef struct { + BT_HDR hdr; + UINT16 instance; + BD_ADDR rand_addr; +} tBTA_DM_API_EXT_ADV_SET_RAND_ADDR; + +typedef struct { + BT_HDR hdr; + UINT16 instance; + tBTA_DM_BLE_GAP_EXT_ADV_PARAMS params; +} tBTA_DM_API_EXT_ADV_SET_PARAMS; + +typedef struct { + BT_HDR hdr; + BOOLEAN is_scan_rsp; + UINT8 instance; + UINT16 length; + UINT8 *data; +} tBTA_DM_API_CFG_EXT_ADV_DATA; + +typedef struct { + BT_HDR hdr; + BOOLEAN enable; + UINT8 num; + tBTA_DM_BLE_EXT_ADV *ext_adv; +} tBTA_DM_API_BLE_EXT_ADV; + +typedef struct { + BT_HDR hdr; + UINT16 instance; +} tBTA_DM_API_BLE_EXT_ADV_SET_REMOVE; + +typedef struct { + BT_HDR hdr; +} tBTA_DM_API_BLE_EXT_ADV_SET_CLEAR; + +typedef struct { + BT_HDR hdr; + UINT8 instance; + tBTA_DM_BLE_Periodic_Adv_Params params; +} tBTA_DM_API_BLE_PERIODIC_ADV_SET_PARAMS; + +typedef struct { + BT_HDR hdr; + UINT8 instance; + UINT16 length; + UINT8 *data; + BOOLEAN only_update_did; +} tBTA_DM_API_CFG_PERIODIC_ADV_DATA; + +typedef struct { + BT_HDR hdr; + UINT8 instance; + UINT8 enable; +} tBTA_DM_API_ENABLE_PERIODIC_ADV; + +typedef struct { + BT_HDR hdr; + tBTA_DM_BLE_Periodic_Sync_Params params; +} tBTA_DM_API_PERIODIC_ADV_SYNC; + +typedef struct { + BT_HDR hdr; +} tBTA_DM_API_PERIODIC_ADV_SYNC_CANCEL; + +typedef struct { + BT_HDR hdr; + UINT16 sync_handle; +} tBTA_DM_API_PERIODIC_ADV_SYNC_TERM; + +typedef struct { + BT_HDR hdr; + tBLE_ADDR_TYPE addr_type; + BD_ADDR addr; + UINT16 sid; +} tBTA_DM_API_PERIODIC_ADV_ADD_DEV_TO_LIST; + +typedef struct { + BT_HDR hdr; + tBLE_ADDR_TYPE addr_type; + BD_ADDR addr; + UINT16 sid; +} tBTA_DM_API_PERIODIC_ADV_REMOVE_DEV_FROM_LIST; + +typedef struct { + BT_HDR hdr; +} tBTA_DM_API_PERIODIC_ADV_DEV_CLEAR; + + +typedef struct { + BT_HDR hdr; + tBTA_DM_BLE_EXT_SCAN_PARAMS params; +} tBTA_DM_API_SET_EXT_SCAN_PARAMS; + +typedef struct { + BT_HDR hdr; + BOOLEAN start; + UINT32 duration; + UINT16 period; +} tBTA_DM_API_EXT_SCAN; + +typedef struct { + BT_HDR hdr; + BD_ADDR bd_addr; + UINT8 phy_mask; + tBTA_DM_BLE_CONN_PARAMS phy_1m_conn_params; + tBTA_DM_BLE_CONN_PARAMS phy_2m_conn_params; + tBTA_DM_BLE_CONN_PARAMS phy_coded_conn_params; +} tBTA_DM_API_SET_PER_EXT_CONN_PARAMS; + +typedef struct { + BT_HDR hdr; + tBLE_ADDR_TYPE own_addr_type; + BD_ADDR peer_addr; +} tBTA_DM_API_EXT_CONN; +#endif //#if (BLE_50_FEATURE_SUPPORT == TRUE) + +#if (BLE_FEAT_PERIODIC_ADV_SYNC_TRANSFER == TRUE) +typedef struct { + BT_HDR hdr; + UINT16 sync_handle; + UINT8 enable; +} tBTA_DM_API_PERIODIC_ADV_RECV_ENABLE; + +typedef struct { + BT_HDR hdr; + BD_ADDR addr; + UINT16 service_data; + UINT16 sync_handle; +} tBTA_DM_API_PERIODIC_ADV_SYNC_TRANS; + +typedef struct { + BT_HDR hdr; + BD_ADDR addr; + UINT16 service_data; + UINT8 adv_hanlde; +} tBTA_DM_API_PERIODIC_ADV_SET_INFO_TRANS; + +typedef struct { + BT_HDR hdr; + BD_ADDR addr; + tBTA_DM_BLE_PAST_PARAMS params; +} tBTA_DM_API_SET_PAST_PARAMS; +#endif // #if (BLE_FEAT_PERIODIC_ADV_SYNC_TRANSFER == TRUE) + +/* union of all data types */ +typedef union { + /* event buffer header */ + BT_HDR hdr; + tBTA_DM_API_ENABLE enable; + + tBTA_DM_API_SET_NAME set_name; + tBTA_DM_API_GET_NAME get_name; +#if (ESP_COEX_VSC_INCLUDED == TRUE) + tBTA_DM_API_CFG_COEX_STATUS cfg_coex_status; +#endif + tBTA_DM_API_CONFIG_EIR config_eir; + + tBTA_DM_API_SET_AFH_CHANNELS set_afh_channels; + tBTA_DM_API_PAGE_TO_SET set_page_timeout; + tBTA_DM_API_PAGE_TO_GET get_page_timeout; + tBTA_DM_API_SET_ACL_PKT_TYPES set_acl_pkt_types; +#if (SDP_INCLUDED == TRUE) + tBTA_DM_API_GET_REMOTE_NAME get_rmt_name; +#endif + +#if (BLE_INCLUDED == TRUE) + tBTA_DM_API_BLE_SET_CHANNELS ble_set_channels; + tBTA_DM_API_UPDATE_WHITE_LIST white_list; + tBTA_DM_API_READ_ADV_TX_POWER read_tx_power; +#endif ///BLE_INCLUDED == TRUE + tBTA_DM_API_READ_RSSI rssi; + + tBTA_DM_API_SET_VISIBILITY set_visibility; + + tBTA_DM_API_ADD_DEVICE add_dev; + + tBTA_DM_API_REMOVE_DEVICE remove_dev; + + tBTA_DM_API_SEARCH search; +#if (SDP_INCLUDED == TRUE) + tBTA_DM_API_DISCOVER discover; +#endif ///SDP_INCLUDED == TRUE + tBTA_DM_API_BOND bond; + + tBTA_DM_API_BOND_CANCEL bond_cancel; + + tBTA_DM_API_SET_PIN_TYPE set_pin_type; + tBTA_DM_API_PIN_REPLY pin_reply; + + tBTA_DM_API_LOC_OOB loc_oob; + tBTA_DM_API_OOB_REPLY oob_reply; + tBTA_DM_API_SC_OOB_REPLY sc_oob_reply; + tBTA_DM_API_CONFIRM confirm; + tBTA_DM_API_KEY_REQ key_req; + tBTA_DM_CI_IO_REQ ci_io_req; + tBTA_DM_CI_RMT_OOB ci_rmt_oob; + + tBTA_DM_REM_NAME rem_name; + + tBTA_DM_DISC_RESULT disc_result; + + tBTA_DM_INQUIRY_CMPL inq_cmpl; + + tBTA_DM_SDP_RESULT sdp_event; + + tBTA_DM_ACL_CHANGE acl_change; + +#if (BTA_DM_PM_INCLUDED == TRUE) + tBTA_DM_PM_BTM_STATUS pm_status; + + tBTA_DM_PM_TIMER pm_timer; +#endif /* #if (BTA_DM_PM_INCLUDED == TRUE) */ + +#if (BTA_DM_QOS_INCLUDED == TRUE) + /* Quality of Service set events */ + tBTA_DM_API_QOS_SET qos_set; +#endif /* #if (BTA_DM_QOS_INCLUDED == TRUE) */ + + tBTA_DM_API_DI_DISC di_disc; + + tBTA_DM_API_EXECUTE_CBACK exec_cback; + + tBTA_DM_API_SET_ENCRYPTION set_encryption; + +#if BLE_INCLUDED == TRUE + tBTA_DM_API_ADD_BLEKEY add_ble_key; + tBTA_DM_API_ADD_BLE_DEVICE add_ble_device; + tBTA_DM_API_PASSKEY_REPLY ble_passkey_reply; + tBTA_DM_API_SET_DEFAULT_PASSKEY ble_set_static_passkey; + tBTA_DM_API_BLE_SEC_GRANT ble_sec_grant; + tBTA_DM_API_BLE_SET_BG_CONN_TYPE ble_set_bd_conn_type; + tBTA_DM_API_BLE_CONN_PARAMS ble_set_conn_params; + tBTA_DM_API_BLE_CONN_SCAN_PARAMS ble_set_conn_scan_params; + tBTA_DM_API_BLE_SCAN_PARAMS ble_set_scan_params; + tBTA_DM_API_BLE_SCAN_FILTER_PARAMS ble_set_scan_fil_params; + tBTA_DM_API_BLE_OBSERVE ble_observe; + tBTA_DM_API_BLE_SCAN ble_scan; + tBTA_DM_API_ENABLE_PRIVACY ble_remote_privacy; + tBTA_DM_API_LOCAL_PRIVACY ble_local_privacy; + tBTA_DM_API_LOCAL_ICON ble_local_icon; + tBTA_DM_API_BLE_ADV_PARAMS ble_set_adv_params; + tBTA_DM_API_BLE_ADV_PARAMS_ALL ble_set_adv_params_all; + tBTA_DM_API_SET_ADV_CONFIG ble_set_adv_data; + tBTA_DM_API_SET_ADV_CONFIG_RAW ble_set_adv_data_raw; + tBTA_DM_API_SET_LONG_ADV ble_set_long_adv_data; +#if BLE_ANDROID_CONTROLLER_SCAN_FILTER == TRUE + tBTA_DM_API_SCAN_FILTER_PARAM_SETUP ble_scan_filt_param_setup; + tBTA_DM_API_CFG_FILTER_COND ble_cfg_filter_cond; + tBTA_DM_API_ENABLE_SCAN_FILTER ble_enable_scan_filt; +#endif + tBTA_DM_API_UPDATE_CONN_PARAM ble_update_conn_params; + tBTA_DM_API_BLE_SET_DATA_LENGTH ble_set_data_length; + tBTA_DM_APT_SET_DEV_ADDR set_addr; + tBTA_DM_APT_CLEAR_ADDR clear_addr; + tBTA_DM_API_BLE_MULTI_ADV_ENB ble_multi_adv_enb; + tBTA_DM_API_BLE_MULTI_ADV_PARAM ble_multi_adv_param; + tBTA_DM_API_BLE_MULTI_ADV_DATA ble_multi_adv_data; + tBTA_DM_API_BLE_MULTI_ADV_DISABLE ble_multi_adv_disable; + + tBTA_DM_API_SET_STORAGE_CONFIG ble_set_storage; + tBTA_DM_API_ENABLE_SCAN ble_enable_scan; + tBTA_DM_API_READ_SCAN_REPORTS ble_read_reports; + tBTA_DM_API_DISABLE_SCAN ble_disable_scan; + tBTA_DM_API_TRACK_ADVERTISER ble_track_advert; + tBTA_DM_API_ENERGY_INFO ble_energy_info; + tBTA_DM_API_BLE_DISCONNECT ble_disconnect; + tBTA_DM_API_UPDATE_DUPLICATE_EXCEPTIONAL_LIST ble_duplicate_exceptional_list; +#if (BLE_50_FEATURE_SUPPORT == TRUE) + tBTA_DM_API_READ_PHY ble_read_phy; + tBTA_DM_API_SET_PER_DEF_PHY ble_set_per_def_phy; + tBTA_DM_API_SET_PER_PHY ble_set_per_phy; + tBTA_DM_API_EXT_ADV_SET_RAND_ADDR ble_set_ext_adv_rand_addr; + tBTA_DM_API_EXT_ADV_SET_PARAMS ble_set_ext_adv_params; + tBTA_DM_API_CFG_EXT_ADV_DATA ble_cfg_ext_adv_data; + tBTA_DM_API_BLE_EXT_ADV ble_start_ext_adv; + tBTA_DM_API_BLE_EXT_ADV_SET_REMOVE ble_ext_adv_set_remove; + tBTA_DM_API_BLE_EXT_ADV_SET_CLEAR ble_ext_adv_set_clear; + tBTA_DM_API_BLE_PERIODIC_ADV_SET_PARAMS ble_set_periodic_adv_params; + tBTA_DM_API_CFG_PERIODIC_ADV_DATA ble_cfg_periodic_adv_data; + tBTA_DM_API_ENABLE_PERIODIC_ADV ble_enable_periodic_adv; + tBTA_DM_API_PERIODIC_ADV_SYNC ble_periodic_adv_sync; + tBTA_DM_API_PERIODIC_ADV_SYNC_CANCEL ble_periodic_adv_sync_cancel; + tBTA_DM_API_PERIODIC_ADV_SYNC_TERM ble_periodic_adv_sync_term; + tBTA_DM_API_PERIODIC_ADV_ADD_DEV_TO_LIST ble_periodic_adv_add_dev_to_list; + tBTA_DM_API_PERIODIC_ADV_REMOVE_DEV_FROM_LIST ble_periodic_adv_remove_dev_from_list; + tBTA_DM_API_PERIODIC_ADV_DEV_CLEAR ble_periodic_adv_clear_dev; + tBTA_DM_API_SET_EXT_SCAN_PARAMS ble_set_ext_scan_params; + tBTA_DM_API_EXT_SCAN ble_ext_scan; + tBTA_DM_API_SET_PER_EXT_CONN_PARAMS ble_set_per_ext_conn_params; + tBTA_DM_API_BLE_DTM_ENH_TX_START dtm_enh_tx_start; + tBTA_DM_API_BLE_DTM_ENH_RX_START dtm_enh_rx_start; +#endif // #if (BLE_50_FEATURE_SUPPORT == TRUE) +#if (BLE_FEAT_PERIODIC_ADV_SYNC_TRANSFER == TRUE) + tBTA_DM_API_PERIODIC_ADV_RECV_ENABLE ble_periodic_adv_recv_enable; + tBTA_DM_API_PERIODIC_ADV_SYNC_TRANS ble_periodic_adv_sync_trans; + tBTA_DM_API_PERIODIC_ADV_SET_INFO_TRANS ble_periodic_adv_set_info_trans; + tBTA_DM_API_SET_PAST_PARAMS ble_set_past_params; +#endif // #if (BLE_FEAT_PERIODIC_ADV_SYNC_TRANSFER == TRUE) + + tBTA_DM_API_BLE_DTM_TX_START dtm_tx_start; + tBTA_DM_API_BLE_DTM_RX_START dtm_rx_start; + tBTA_DM_API_BLE_DTM_STOP dtm_stop; + tBTA_DM_API_CLEAR_ADV ble_clear_adv; +#endif + + tBTA_DM_API_REMOVE_ACL remove_acl; + tBTA_DM_API_REMOVE_ALL_ACL remove_all_acl; + +} tBTA_DM_MSG; + + +#define BTA_DM_NUM_PEER_DEVICE MAX_ACL_CONNECTIONS + +#define BTA_DM_NOT_CONNECTED 0 +#define BTA_DM_CONNECTED 1 +#define BTA_DM_UNPAIRING 2 +typedef UINT8 tBTA_DM_CONN_STATE; + + +#define BTA_DM_DI_NONE 0x00 /* nothing special */ +#define BTA_DM_DI_USE_SSR 0x10 /* set this bit if ssr is supported for this link */ +#define BTA_DM_DI_AV_ACTIVE 0x20 /* set this bit if AV is active for this link */ +#define BTA_DM_DI_SET_SNIFF 0x01 /* set this bit if call BTM_SetPowerMode(sniff) */ +#define BTA_DM_DI_INT_SNIFF 0x02 /* set this bit if call BTM_SetPowerMode(sniff) & enter sniff mode */ +#define BTA_DM_DI_ACP_SNIFF 0x04 /* set this bit if peer init sniff */ +typedef UINT8 tBTA_DM_DEV_INFO; + +/* set power mode request type */ +#define BTA_DM_PM_RESTART 1 +#define BTA_DM_PM_NEW_REQ 2 +#define BTA_DM_PM_EXECUTE 3 +typedef UINT8 tBTA_DM_PM_REQ; + +typedef struct { + BD_ADDR peer_bdaddr; + UINT16 link_policy; + tBTA_DM_CONN_STATE conn_state; + tBTA_PREF_ROLES pref_role; + BOOLEAN in_use; + tBTA_DM_DEV_INFO info; + tBTA_DM_ENCRYPT_CBACK *p_encrypt_cback; +#if (BTM_SSR_INCLUDED == TRUE) + tBTM_PM_STATUS prev_low; /* previous low power mode used */ +#endif + tBTA_DM_PM_ACTION pm_mode_attempted; + tBTA_DM_PM_ACTION pm_mode_failed; + BOOLEAN remove_dev_pending; +#if BLE_INCLUDED == TRUE + UINT16 conn_handle; +#endif + tBT_TRANSPORT transport; +} tBTA_DM_PEER_DEVICE; + + + +/* structure to store list of + active connections */ +typedef struct { + tBTA_DM_PEER_DEVICE peer_device[BTA_DM_NUM_PEER_DEVICE]; + UINT8 count; +#if BLE_INCLUDED == TRUE + UINT8 le_count; +#endif +} tBTA_DM_ACTIVE_LINK; + + +typedef struct { + BD_ADDR peer_bdaddr; + tBTA_SYS_ID id; + UINT8 app_id; + tBTA_SYS_CONN_STATUS state; + BOOLEAN new_request; + +} tBTA_DM_SRVCS; + +#ifndef BTA_DM_NUM_CONN_SRVS +#define BTA_DM_NUM_CONN_SRVS 10 +#endif + +typedef struct { + + UINT8 count; + tBTA_DM_SRVCS conn_srvc[BTA_DM_NUM_CONN_SRVS]; + +} tBTA_DM_CONNECTED_SRVCS; + + +#if (BTA_DM_PM_INCLUDED == TRUE) + +typedef struct { +#define BTA_DM_PM_SNIFF_TIMER_IDX 0 +#define BTA_DM_PM_PARK_TIMER_IDX 1 +#define BTA_DM_PM_SUSPEND_TIMER_IDX 2 +#define BTA_DM_PM_MODE_TIMER_MAX 3 + /* + * Keep three different timers for PARK, SNIFF and SUSPEND if TBFC is + * supported. + */ + TIMER_LIST_ENT timer[BTA_DM_PM_MODE_TIMER_MAX]; + + UINT8 srvc_id[BTA_DM_PM_MODE_TIMER_MAX]; + UINT8 pm_action[BTA_DM_PM_MODE_TIMER_MAX]; + UINT8 active; /* number of active timer */ + + BD_ADDR peer_bdaddr; + BOOLEAN in_use; +} tBTA_PM_TIMER; + +#define BTA_DM_NUM_PM_TIMER 7 +#endif /* #if (BTA_DM_PM_INCLUDED == TRUE) */ + +/* DM control block */ +typedef struct { + BOOLEAN is_bta_dm_active; + tBTA_DM_ACTIVE_LINK device_list; + tBTA_DM_SEC_CBACK *p_sec_cback; +#if ((defined BLE_INCLUDED) && (BLE_INCLUDED == TRUE)) + tBTA_BLE_SCAN_SETUP_CBACK *p_setup_cback; + tBTA_DM_BLE_PF_CFG_CBACK *p_scan_filt_cfg_cback; + tBTA_DM_BLE_PF_STATUS_CBACK *p_scan_filt_status_cback; + tBTA_DM_BLE_PF_PARAM_CBACK *p_scan_filt_param_cback; + tBTA_BLE_MULTI_ADV_CBACK *p_multi_adv_cback; + tBTA_BLE_ENERGY_INFO_CBACK *p_energy_info_cback; +#endif + UINT16 state; + BOOLEAN disabling; + TIMER_LIST_ENT disable_timer; + UINT32 wbt_sdp_handle; /* WIDCOMM Extensions SDP record handle */ + UINT8 wbt_scn; /* WIDCOMM Extensions SCN */ + UINT8 num_master_only; +#if (BTA_DM_PM_INCLUDED == TRUE) + UINT8 pm_id; + tBTA_PM_TIMER pm_timer[BTA_DM_NUM_PM_TIMER]; +#endif /* #if (BTA_DM_PM_INCLUDED == TRUE) */ + UINT32 role_policy_mask; /* the bits set indicates the modules that wants to remove role switch from the default link policy */ + UINT16 cur_policy; /* current default link policy */ + UINT16 rs_event; /* the event waiting for role switch */ + UINT8 cur_av_count; /* current AV connecions */ + BOOLEAN disable_pair_mode; /* disable pair mode or not */ + BOOLEAN conn_paired_only; /* allow connectable to paired device only or not */ + tBTA_DM_API_SEARCH search_msg; + +#if (CLASSIC_BT_INCLUDED == TRUE) + UINT16 page_scan_interval; + UINT16 page_scan_window; + UINT16 inquiry_scan_interval; + UINT16 inquiry_scan_window; + + /* Storage for pin code request parameters */ + BD_ADDR pin_bd_addr; + DEV_CLASS pin_dev_class; + tBTA_DM_SEC_EVT pin_evt; + UINT32 num_val; /* the numeric value for comparison. If just_works, do not show this number to UI */ + BOOLEAN just_works; /* TRUE, if "Just Works" association model */ +#endif + +#if ( BTA_EIR_CANNED_UUID_LIST != TRUE ) + /* store UUID list for EIR */ + TIMER_LIST_ENT app_ready_timer; + UINT32 eir_uuid[BTM_EIR_SERVICE_ARRAY_SIZE]; +#if (BTA_EIR_SERVER_NUM_CUSTOM_UUID > 0) + tBT_UUID custom_uuid[BTA_EIR_SERVER_NUM_CUSTOM_UUID]; +#endif + +#endif + + + tBTA_DM_ENCRYPT_CBACK *p_encrypt_cback; + TIMER_LIST_ENT switch_delay_timer[BTA_DM_NUM_PEER_DEVICE]; + +} tBTA_DM_CB; + +#ifndef BTA_DM_SDP_DB_SIZE +#define BTA_DM_SDP_DB_SIZE 250 +#endif + +/* DM search control block */ +typedef struct { + + tBTA_DM_SEARCH_CBACK *p_search_cback; + tBTM_INQ_INFO *p_btm_inq_info; + tBTA_SERVICE_MASK services; + tBTA_SERVICE_MASK services_to_search; + tBTA_SERVICE_MASK services_found; +#if (SDP_INCLUDED == TRUE) + tSDP_DISCOVERY_DB *p_sdp_db; +#endif ///SDP_INCLUDED == TRUE + UINT16 state; + BD_ADDR peer_bdaddr; + BOOLEAN name_discover_done; + BD_NAME peer_name; + TIMER_LIST_ENT search_timer; + UINT8 service_index; + tBTA_DM_MSG *p_search_queue; /* search or discover commands during search cancel stored here */ + BOOLEAN wait_disc; + BOOLEAN sdp_results; +#if (SDP_INCLUDED == TRUE) + tSDP_UUID uuid; +#endif ///SDP_INCLUDED == TRUE + UINT8 peer_scn; + BOOLEAN sdp_search; + BOOLEAN cancel_pending; /* inquiry cancel is pending */ + tBTA_TRANSPORT transport; +#if ((defined BLE_INCLUDED) && (BLE_INCLUDED == TRUE)) + tBTA_DM_SEARCH_CBACK *p_scan_cback; +#if ((defined BTA_GATT_INCLUDED) && (BTA_GATT_INCLUDED == TRUE) && SDP_INCLUDED == TRUE) + tBTA_GATTC_IF client_if; + UINT8 num_uuid; + tBT_UUID *p_srvc_uuid; + UINT8 uuid_to_search; + BOOLEAN gatt_disc_active; + UINT16 conn_id; + UINT8 *p_ble_rawdata; + UINT32 ble_raw_size; + UINT32 ble_raw_used; + TIMER_LIST_ENT gatt_close_timer; /* GATT channel close delay timer */ + BD_ADDR pending_close_bda; /* pending GATT channel remote device address */ +#endif +#endif + + +} tBTA_DM_SEARCH_CB; + +/* DI control block */ +typedef struct { +#if (SDP_INCLUDED == TRUE) + tSDP_DISCOVERY_DB *p_di_db; /* pointer to the DI discovery database */ +#endif ///SDP_INCLUDED == TRUE + UINT8 di_num; /* total local DI record number */ + UINT32 di_handle[BTA_DI_NUM_MAX]; /* local DI record handle, the first one is primary record */ +} tBTA_DM_DI_CB; + +/* DM search state */ +enum { + + BTA_DM_SEARCH_IDLE, + BTA_DM_SEARCH_ACTIVE, + BTA_DM_SEARCH_CANCELLING, + BTA_DM_DISCOVER_ACTIVE + +}; + + + +typedef struct { + DEV_CLASS dev_class; /* local device class */ + UINT16 policy_settings; /* link policy setting hold, sniff, park, MS switch */ + UINT16 page_timeout; /* timeout for page in slots */ + UINT16 link_timeout; /* link supervision timeout in slots */ + BOOLEAN avoid_scatter; /* TRUE to avoid scatternet when av is streaming (be the master) */ + +} tBTA_DM_CFG; + +extern const UINT32 bta_service_id_to_btm_srv_id_lkup_tbl[]; + + +typedef struct { + UINT8 id; + UINT8 app_id; + UINT8 cfg; + +} tBTA_DM_RM ; + +extern tBTA_DM_CFG *const p_bta_dm_cfg; +extern tBTA_DM_RM *const p_bta_dm_rm_cfg; + +typedef struct { + + UINT8 id; + UINT8 app_id; + UINT8 spec_idx; /* index of spec table to use */ + +} tBTA_DM_PM_CFG; + + +typedef struct { + + tBTA_DM_PM_ACTION power_mode; + UINT16 timeout; + +} tBTA_DM_PM_ACTN; + +typedef struct { + + UINT8 allow_mask; /* mask of sniff/hold/park modes to allow */ +#if (BTM_SSR_INCLUDED == TRUE) + UINT8 ssr; /* set SSR on conn open/unpark */ +#endif + tBTA_DM_PM_ACTN actn_tbl [BTA_DM_PM_NUM_EVTS][2]; + +} tBTA_DM_PM_SPEC; + +typedef struct { + UINT16 max_lat; + UINT16 min_rmt_to; + UINT16 min_loc_to; +} tBTA_DM_SSR_SPEC; + +typedef struct { + UINT16 manufacturer; + UINT16 lmp_sub_version; + UINT8 lmp_version; +} tBTA_DM_LMP_VER_INFO; + +#if (BTA_DM_PM_INCLUDED == TRUE) +extern tBTA_DM_PM_CFG *const p_bta_dm_pm_cfg; +extern tBTA_DM_PM_SPEC *const p_bta_dm_pm_spec; +extern tBTM_PM_PWR_MD *const p_bta_dm_pm_md; +#if (BTM_SSR_INCLUDED == TRUE) +extern tBTA_DM_SSR_SPEC *const p_bta_dm_ssr_spec; +#endif +#endif /* #if (BTA_DM_PM_INCLUDED == TRUE) */ + +/* update dynamic BRCM Aware EIR data */ +extern tBTA_DM_EIR_CONF bta_dm_eir_cfg; +extern tBTA_DM_EIR_CONF *p_bta_dm_eir_cfg; + +/* DM control block */ +#if BTA_DYNAMIC_MEMORY == FALSE +extern tBTA_DM_CB bta_dm_cb; +#else +extern tBTA_DM_CB *bta_dm_cb_ptr; +#define bta_dm_cb (*bta_dm_cb_ptr) +#endif + +/* DM search control block */ +#if BTA_DYNAMIC_MEMORY == FALSE +extern tBTA_DM_SEARCH_CB bta_dm_search_cb; +#else +extern tBTA_DM_SEARCH_CB *bta_dm_search_cb_ptr; +#define bta_dm_search_cb (*bta_dm_search_cb_ptr) +#endif + +/* DI control block */ +#if BTA_DYNAMIC_MEMORY == FALSE +extern tBTA_DM_DI_CB bta_dm_di_cb; +#else +extern tBTA_DM_DI_CB *bta_dm_di_cb_ptr; +#define bta_dm_di_cb (*bta_dm_di_cb_ptr) +extern SemaphoreHandle_t deinit_semaphore; +#endif + +#if BTA_DYNAMIC_MEMORY == FALSE +extern tBTA_DM_CONNECTED_SRVCS bta_dm_conn_srvcs; +#else +extern tBTA_DM_CONNECTED_SRVCS *bta_dm_conn_srvcs_ptr; +#define bta_dm_conn_srvcs (*bta_dm_conn_srvcs_ptr) +#endif + +/* Discovery raw data buffer */ +#define MAX_DISC_RAW_DATA_BUF (1024) +#if BTA_DYNAMIC_MEMORY == TRUE +extern UINT8 *g_disc_raw_data_buf; +#endif + +extern BOOLEAN bta_dm_sm_execute(BT_HDR *p_msg); +extern void bta_dm_sm_disable( void ); +extern void bta_dm_sm_deinit(void); +extern BOOLEAN bta_dm_search_sm_execute(BT_HDR *p_msg); +extern void bta_dm_search_sm_disable( void ); + + +extern void bta_dm_enable (tBTA_DM_MSG *p_data); +extern void bta_dm_disable (tBTA_DM_MSG *p_data); +extern void bta_dm_set_dev_name (tBTA_DM_MSG *p_data); +extern void bta_dm_get_dev_name (tBTA_DM_MSG *p_data); +#if (ESP_COEX_VSC_INCLUDED == TRUE) +extern void bta_dm_cfg_coex_status(tBTA_DM_MSG *p_data); +#endif +#if (CLASSIC_BT_INCLUDED == TRUE) +extern void bta_dm_config_eir (tBTA_DM_MSG *p_data); +extern void bta_dm_set_page_timeout (tBTA_DM_MSG *p_data); +extern void bta_dm_get_page_timeout (tBTA_DM_MSG *p_data); +extern void bta_dm_set_acl_pkt_types (tBTA_DM_MSG *p_data); +#endif +extern void bta_dm_set_afh_channels (tBTA_DM_MSG *p_data); +extern void bta_dm_read_rmt_name(tBTA_DM_MSG *p_data); +extern void bta_dm_ble_set_channels (tBTA_DM_MSG *p_data); +extern void bta_dm_update_white_list(tBTA_DM_MSG *p_data); +extern void bta_dm_clear_white_list(tBTA_DM_MSG *p_data); +extern void bta_dm_ble_read_adv_tx_power(tBTA_DM_MSG *p_data); +extern void bta_dm_read_rssi(tBTA_DM_MSG *p_data); +extern void bta_dm_set_visibility (tBTA_DM_MSG *p_data); + +extern void bta_dm_set_scan_config(tBTA_DM_MSG *p_data); +extern void bta_dm_vendor_spec_command(tBTA_DM_MSG *p_data); +extern void bta_dm_bond (tBTA_DM_MSG *p_data); +extern void bta_dm_bond_cancel (tBTA_DM_MSG *p_data); +extern void bta_dm_set_pin_type (tBTA_DM_MSG *p_data); +extern void bta_dm_pin_reply (tBTA_DM_MSG *p_data); +extern void bta_dm_acl_change(tBTA_DM_MSG *p_data); +extern void bta_dm_add_device (tBTA_DM_MSG *p_data); +extern void bta_dm_remove_device (tBTA_DM_MSG *p_data); +extern void bta_dm_close_acl(tBTA_DM_MSG *p_data); + +extern void bta_dm_add_ampkey (tBTA_DM_MSG *p_data); + +#if BLE_INCLUDED == TRUE +extern void bta_dm_add_blekey (tBTA_DM_MSG *p_data); +extern void bta_dm_add_ble_device (tBTA_DM_MSG *p_data); +extern void bta_dm_ble_passkey_reply (tBTA_DM_MSG *p_data); +extern void bta_dm_ble_set_static_passkey(tBTA_DM_MSG *p_data); +extern void bta_dm_ble_confirm_reply (tBTA_DM_MSG *p_data); +extern void bta_dm_security_grant (tBTA_DM_MSG *p_data); +extern void bta_dm_ble_set_bg_conn_type (tBTA_DM_MSG *p_data); +extern void bta_dm_ble_set_conn_params (tBTA_DM_MSG *p_data); +extern void bta_dm_ble_set_scan_params(tBTA_DM_MSG *p_data); +extern void bta_dm_ble_set_scan_fil_params(tBTA_DM_MSG *p_data); +extern void bta_dm_ble_set_conn_scan_params (tBTA_DM_MSG *p_data); +#if ((defined BTA_GATT_INCLUDED) && (BTA_GATT_INCLUDED == TRUE) && SDP_INCLUDED == TRUE) && (GATTC_INCLUDED == TRUE) +extern void bta_dm_close_gatt_conn(tBTA_DM_MSG *p_data); +#endif /* ((defined BTA_GATT_INCLUDED) && (BTA_GATT_INCLUDED == TRUE) && SDP_INCLUDED == TRUE) && (GATTC_INCLUDED == TRUE) */ +extern void bta_dm_ble_observe (tBTA_DM_MSG *p_data); +extern void bta_dm_ble_scan (tBTA_DM_MSG *p_data); +extern void bta_dm_ble_update_conn_params (tBTA_DM_MSG *p_data); +extern void bta_dm_ble_disconnect (tBTA_DM_MSG *p_data); +extern void bta_dm_ble_set_rand_address(tBTA_DM_MSG *p_data); +extern void bta_dm_ble_clear_rand_address(tBTA_DM_MSG *p_data); +extern void bta_dm_ble_stop_advertising(tBTA_DM_MSG *p_data); +extern void bta_dm_ble_config_local_privacy (tBTA_DM_MSG *p_data); +extern void bta_dm_ble_config_local_icon (tBTA_DM_MSG *p_data); +extern void bta_dm_ble_set_adv_params (tBTA_DM_MSG *p_data); +extern void bta_dm_ble_set_adv_params_all(tBTA_DM_MSG *p_data); +extern void bta_dm_ble_set_adv_config (tBTA_DM_MSG *p_data); +extern void bta_dm_ble_set_long_adv (tBTA_DM_MSG *p_data); +extern void bta_dm_ble_set_adv_config_raw (tBTA_DM_MSG *p_data); +extern void bta_dm_ble_set_scan_rsp (tBTA_DM_MSG *p_data); +extern void bta_dm_ble_set_scan_rsp_raw (tBTA_DM_MSG *p_data); +extern void bta_dm_ble_broadcast (tBTA_DM_MSG *p_data); +extern void bta_dm_ble_set_data_length(tBTA_DM_MSG *p_data); +extern void bta_dm_ble_update_duplicate_exceptional_list(tBTA_DM_MSG *p_data); +#if BLE_ANDROID_CONTROLLER_SCAN_FILTER == TRUE +extern void bta_dm_cfg_filter_cond (tBTA_DM_MSG *p_data); +extern void bta_dm_scan_filter_param_setup (tBTA_DM_MSG *p_data); +extern void bta_dm_enable_scan_filter(tBTA_DM_MSG *p_data); +#endif +extern void btm_dm_ble_multi_adv_disable(tBTA_DM_MSG *p_data); +extern void bta_dm_ble_multi_adv_data(tBTA_DM_MSG *p_data); +extern void bta_dm_ble_multi_adv_upd_param(tBTA_DM_MSG *p_data); +extern void bta_dm_ble_multi_adv_enb(tBTA_DM_MSG *p_data); +extern void bta_dm_ble_gap_dtm_tx_start(tBTA_DM_MSG *p_data); +extern void bta_dm_ble_gap_dtm_rx_start(tBTA_DM_MSG *p_data); +extern void bta_dm_ble_gap_dtm_stop(tBTA_DM_MSG *p_data); +extern void bta_dm_ble_gap_clear_adv(tBTA_DM_MSG *p_data); + +#if (BLE_50_FEATURE_SUPPORT == TRUE) +extern void bta_dm_ble_gap_dtm_enhance_tx_start(tBTA_DM_MSG *p_data); +extern void bta_dm_ble_gap_dtm_enhance_rx_start(tBTA_DM_MSG *p_data); +extern void bta_dm_ble_gap_read_phy(tBTA_DM_MSG *p_data); +extern void bta_dm_ble_gap_set_prefer_default_phy(tBTA_DM_MSG *p_data); +extern void bta_dm_ble_gap_set_prefer_phy(tBTA_DM_MSG *p_data); +extern void bta_dm_ble_gap_ext_adv_set_rand_addr(tBTA_DM_MSG *p_data); +extern void bta_dm_ble_gap_ext_adv_set_params(tBTA_DM_MSG *p_data); +extern void bta_dm_ble_gap_config_ext_adv_data_raw(tBTA_DM_MSG *p_data); +extern void bta_dm_ble_gap_set_ext_scan_params(tBTA_DM_MSG *p_data); +extern void bta_dm_ble_gap_ext_scan(tBTA_DM_MSG *p_data); +extern void bta_dm_ble_gap_set_prefer_ext_conn_params(tBTA_DM_MSG *p_data); +#endif // #if (BLE_50_FEATURE_SUPPORT == TRUE) +extern void bta_dm_ble_setup_storage(tBTA_DM_MSG *p_data); +extern void bta_dm_ble_enable_batch_scan(tBTA_DM_MSG *p_data); +extern void bta_dm_ble_disable_batch_scan(tBTA_DM_MSG *p_data); +extern void bta_dm_ble_read_scan_reports(tBTA_DM_MSG *p_data); +extern void bta_dm_ble_track_advertiser(tBTA_DM_MSG *p_data); +extern void bta_dm_ble_get_energy_info(tBTA_DM_MSG *p_data); + +#endif +extern void bta_dm_set_encryption(tBTA_DM_MSG *p_data); +extern void bta_dm_confirm(tBTA_DM_MSG *p_data); +extern void bta_dm_key_req(tBTA_DM_MSG *p_data); + +#if (BTA_HD_INCLUDED == TRUE) +extern BOOLEAN bta_dm_check_if_only_hd_connected(BD_ADDR peer_addr); +#endif /* BTA_HD_INCLUDED */ + +#if (BTM_OOB_INCLUDED == TRUE) +extern void bta_dm_loc_oob(tBTA_DM_MSG *p_data); +extern void bta_dm_oob_reply(tBTA_DM_MSG *p_data); +extern void bta_dm_sc_oob_reply(tBTA_DM_MSG *p_data); +extern void bta_dm_sc_create_oob_data(tBTA_DM_MSG *p_data); +extern void bta_dm_ci_io_req_act(tBTA_DM_MSG *p_data); +extern void bta_dm_ci_rmt_oob_act(tBTA_DM_MSG *p_data); +#endif /* BTM_OOB_INCLUDED */ + +#if (BTA_DM_PM_INCLUDED == TRUE) +extern void bta_dm_init_pm(void); +extern void bta_dm_disable_pm(void); +extern void bta_dm_pm_active(BD_ADDR peer_addr); +extern void bta_dm_pm_btm_status(tBTA_DM_MSG *p_data); +extern void bta_dm_pm_timer(tBTA_DM_MSG *p_data); +#endif /* #if (BTA_DM_PM_INCLUDED == TRUE) */ + +#if (BTA_DM_QOS_INCLUDED == TRUE) +extern void bta_dm_set_qos(tBTA_DM_MSG *p_data); +#endif /* #if (BTA_DM_QOS_INCLUDED == TRUE) */ + +extern UINT8 bta_dm_get_av_count(void); +extern void bta_dm_search_start (tBTA_DM_MSG *p_data); +extern void bta_dm_search_cancel (tBTA_DM_MSG *p_data); +extern void bta_dm_discover (tBTA_DM_MSG *p_data); +#if (SDP_INCLUDED == TRUE) +extern void bta_dm_di_disc (tBTA_DM_MSG *p_data); +#endif ///SDP_INCLUDED == TRUE +extern void bta_dm_inq_cmpl (tBTA_DM_MSG *p_data); +extern void bta_dm_rmt_name (tBTA_DM_MSG *p_data); +#if (SDP_INCLUDED == TRUE) +extern void bta_dm_sdp_result (tBTA_DM_MSG *p_data); +#endif ///SDP_INCLUDED == TRUE +extern void bta_dm_search_cmpl (tBTA_DM_MSG *p_data); +extern void bta_dm_free_sdp_db (tBTA_DM_MSG *p_data); +extern void bta_dm_disc_result (tBTA_DM_MSG *p_data); +extern void bta_dm_search_result (tBTA_DM_MSG *p_data); +extern void bta_dm_discovery_cmpl (tBTA_DM_MSG *p_data); +extern void bta_dm_queue_search (tBTA_DM_MSG *p_data); +extern void bta_dm_queue_disc (tBTA_DM_MSG *p_data); +extern void bta_dm_search_clear_queue (tBTA_DM_MSG *p_data); +extern void bta_dm_search_cancel_cmpl (tBTA_DM_MSG *p_data); +extern void bta_dm_search_cancel_notify (tBTA_DM_MSG *p_data); +extern void bta_dm_search_cancel_transac_cmpl(tBTA_DM_MSG *p_data); +extern void bta_dm_disc_rmt_name (tBTA_DM_MSG *p_data); +extern tBTA_DM_PEER_DEVICE *bta_dm_find_peer_device(BD_ADDR peer_addr); +void bta_dm_eir_update_uuid(tBT_UUID uuid, BOOLEAN adding); + +extern void bta_dm_enable_test_mode(tBTA_DM_MSG *p_data); +extern void bta_dm_disable_test_mode(tBTA_DM_MSG *p_data); +extern void bta_dm_execute_callback(tBTA_DM_MSG *p_data); + + +extern void bta_dm_remove_all_acl(tBTA_DM_MSG *p_data); +#if (BLE_50_FEATURE_SUPPORT == TRUE) +extern void bta_dm_ble_gap_read_phy(tBTA_DM_MSG *p_data); + +extern void bta_dm_ble_gap_set_prefer_default_phy(tBTA_DM_MSG *p_data); + +extern void bta_dm_ble_gap_set_prefer_phy(tBTA_DM_MSG *p_data); + +extern void bta_dm_ble_gap_ext_adv_set_rand_addr(tBTA_DM_MSG *p_data); + +extern void bta_dm_ble_gap_ext_adv_set_params(tBTA_DM_MSG *p_data); + +extern void bta_dm_ble_gap_config_ext_adv_data_raw(tBTA_DM_MSG *p_data); + +extern void bta_dm_ble_gap_start_ext_adv(tBTA_DM_MSG *p_data); + +extern void bta_dm_ble_gap_ext_adv_set_remove(tBTA_DM_MSG *p_data); + +extern void bta_dm_ble_gap_ext_adv_set_clear(tBTA_DM_MSG *p_data); + +extern void bta_dm_ble_gap_periodic_adv_set_params(tBTA_DM_MSG *p_data); + +extern void bta_dm_ble_gap_periodic_adv_cfg_data_raw(tBTA_DM_MSG *p_data); + +extern void bta_dm_ble_gap_periodic_adv_enable(tBTA_DM_MSG *p_data); + +extern void bta_dm_ble_gap_periodic_adv_create_sync(tBTA_DM_MSG *p_data); + +extern void bta_dm_ble_gap_periodic_adv_sync_cancel(tBTA_DM_MSG *p_data); + +extern void bta_dm_ble_gap_periodic_adv_sync_terminate(tBTA_DM_MSG *p_data); + +extern void bta_dm_ble_gap_periodic_adv_add_dev_to_list(tBTA_DM_MSG *p_data); + +extern void bta_dm_ble_gap_periodic_adv_remove_dev_from_list(tBTA_DM_MSG *p_data); + +extern void bta_dm_ble_gap_periodic_adv_clear_dev(tBTA_DM_MSG *p_data); + +extern void bta_dm_ble_gap_set_ext_scan_params(tBTA_DM_MSG *p_data); + +extern void bta_dm_ble_gap_ext_scan(tBTA_DM_MSG *p_data); + +extern void bta_dm_ble_gap_set_prefer_ext_conn_params(tBTA_DM_MSG *p_data); +#endif // #if (BLE_50_FEATURE_SUPPORT == TRUE) + +#if (BLE_FEAT_PERIODIC_ADV_SYNC_TRANSFER == TRUE) +extern void bta_dm_ble_gap_periodic_adv_recv_enable(tBTA_DM_MSG *p_data); + +extern void bta_dm_ble_gap_periodic_adv_sync_trans(tBTA_DM_MSG *p_data); + +extern void bta_dm_ble_gap_periodic_adv_set_info_trans(tBTA_DM_MSG *p_data); + +extern void bta_dm_ble_gap_set_periodic_adv_sync_trans_params(tBTA_DM_MSG *p_data); +#endif // #if (BLE_FEAT_PERIODIC_ADV_SYNC_TRANSFER == TRUE) + +#endif /* BTA_DM_INT_H */ diff --git a/lib/bt/host/bluedroid/bta/gatt/bta_gatt_common.c b/lib/bt/host/bluedroid/bta/gatt/bta_gatt_common.c new file mode 100644 index 00000000..81cee877 --- /dev/null +++ b/lib/bt/host/bluedroid/bta/gatt/bta_gatt_common.c @@ -0,0 +1,25 @@ +/* + * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/****************************************************************************** + * + * This file contains the action functions for gatts and gattc. + * + * + ******************************************************************************/ + +#include "bta/bta_gatt_common.h" +#include "gatt_int.h" + +void BTA_GATT_SetLocalMTU(uint16_t mtu) +{ + gatt_set_local_mtu(mtu); +} + +uint16_t BTA_GATT_GetLocalMTU(void) +{ + return gatt_get_local_mtu(); +} diff --git a/lib/bt/host/bluedroid/bta/gatt/bta_gattc_act.c b/lib/bt/host/bluedroid/bta/gatt/bta_gattc_act.c new file mode 100644 index 00000000..b2302fa4 --- /dev/null +++ b/lib/bt/host/bluedroid/bta/gatt/bta_gattc_act.c @@ -0,0 +1,2498 @@ +/****************************************************************************** + * + * Copyright (C) 2003-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains the GATT client action functions for the state + * machine. + * + ******************************************************************************/ +#define LOG_TAG "bt_bta_gattc" + +#include "common/bt_target.h" + +#include "bta/utl.h" +#include "bta/bta_sys.h" + +#include "bta_gattc_int.h" +#include "stack/l2c_api.h" +#include "l2c_int.h" +#include "gatt_int.h" +#include "osi/allocator.h" +#include "osi/mutex.h" +#include "bta_hh_int.h" + +#if (defined BTA_HH_LE_INCLUDED && BTA_HH_LE_INCLUDED == TRUE) +#include "bta_hh_int.h" +#endif + +// #include "btif/include/btif_debug_conn.h" + +#include + +// #include "osi/include/log.h" + +#if GATTC_INCLUDED == TRUE && BLE_INCLUDED == TRUE + +/***************************************************************************** +** Constants +*****************************************************************************/ +static void bta_gattc_conn_cback(tGATT_IF gattc_if, BD_ADDR bda, UINT16 conn_id, + BOOLEAN connected, tGATT_DISCONN_REASON reason, + tBT_TRANSPORT transport); + +static void bta_gattc_cmpl_cback(UINT16 conn_id, tGATTC_OPTYPE op, tGATT_STATUS status, + tGATT_CL_COMPLETE *p_data); +static void bta_gattc_cmpl_sendmsg(UINT16 conn_id, tGATTC_OPTYPE op, + tBTA_GATT_STATUS status, + tGATT_CL_COMPLETE *p_data); +static void bta_gattc_pop_command_to_send(tBTA_GATTC_CLCB *p_clcb); + +static void bta_gattc_deregister_cmpl(tBTA_GATTC_RCB *p_clreg); +static void bta_gattc_enc_cmpl_cback(tGATT_IF gattc_if, BD_ADDR bda); +static void bta_gattc_cong_cback (UINT16 conn_id, BOOLEAN congested); +static void bta_gattc_req_cback (UINT16 conn_id, UINT32 trans_id, tGATTS_REQ_TYPE type, tGATTS_DATA *p_data); +static tBTA_GATTC_FIND_SERVICE_CB bta_gattc_register_service_change_notify(UINT16 conn_id, BD_ADDR remote_bda); + +extern void btc_gattc_congest_callback(tBTA_GATTC *param); + +static const tGATT_CBACK bta_gattc_cl_cback = { + bta_gattc_conn_cback, + bta_gattc_cmpl_cback, + bta_gattc_disc_res_cback, + bta_gattc_disc_cmpl_cback, + bta_gattc_req_cback, + bta_gattc_enc_cmpl_cback, + bta_gattc_cong_cback +}; + +/* opcode(tGATTC_OPTYPE) order has to be comply with internal event order */ +static const UINT16 bta_gattc_opcode_to_int_evt[] = { + BTA_GATTC_API_READ_EVT, + BTA_GATTC_API_WRITE_EVT, + BTA_GATTC_API_EXEC_EVT, + BTA_GATTC_API_CFG_MTU_EVT, + BTA_GATTC_API_READ_MULTI_EVT +}; + +#if (BT_TRACE_VERBOSE == TRUE) +static const char *bta_gattc_op_code_name[] = { + "Unknown", + "Discovery", + "Read", + "Write", + "Exec", + "Config", + "Notification", + "Indication" +}; +#endif +/***************************************************************************** +** Action Functions +*****************************************************************************/ + + +void bta_gattc_reset_discover_st(tBTA_GATTC_SERV *p_srcb, tBTA_GATT_STATUS status); + +/******************************************************************************* +** +** Function bta_gattc_enable +** +** Description Enables GATTC module +** +** +** Returns void +** +*******************************************************************************/ +static void bta_gattc_enable(tBTA_GATTC_CB *p_cb) +{ + APPL_TRACE_DEBUG("bta_gattc_enable"); + /* This is a workaround because the task priority of btc (BTA_GATTC_CLOSE_EVT + in that task) is lower than the priority of the btu task. + Consequently, the p_cb->state fails to be restored to BTA_GATTC_STATE_DISABLED + and remains in the BTA_GATTC_STATE_DISABLING state. */ + if (p_cb->state == BTA_GATTC_STATE_DISABLED || p_cb->state == BTA_GATTC_STATE_DISABLING) { + /* initialize control block */ + memset(&bta_gattc_cb, 0, sizeof(tBTA_GATTC_CB)); + bta_gattc_cb.auto_disc = true; + p_cb->state = BTA_GATTC_STATE_ENABLED; + } else { + APPL_TRACE_DEBUG("GATTC is already enabled"); + } +} + + +/******************************************************************************* +** +** Function bta_gattc_disable +** +** Description Disable GATTC module by cleaning up all active connections +** and deregister all application. +** +** Returns void +** +*******************************************************************************/ +void bta_gattc_disable(tBTA_GATTC_CB *p_cb) +{ + UINT8 i; + + APPL_TRACE_DEBUG("bta_gattc_disable"); + + if (p_cb->state != BTA_GATTC_STATE_ENABLED) { + APPL_TRACE_ERROR("not enabled or disable in pogress"); + return; + } + + for (i = 0; i < BTA_GATTC_CL_MAX; i ++) { + if (p_cb->cl_rcb[i].in_use) { + p_cb->state = BTA_GATTC_STATE_DISABLING; + /* don't deregister HH GATT IF */ + /* HH GATT IF will be deregistered by bta_hh_le_deregister when disable HH */ +#if (defined BTA_HH_LE_INCLUDED && BTA_HH_LE_INCLUDED == TRUE) + if (!bta_hh_le_is_hh_gatt_if(p_cb->cl_rcb[i].client_if)) { +#endif + bta_gattc_deregister(p_cb, &p_cb->cl_rcb[i]); +#if (defined BTA_HH_LE_INCLUDED && BTA_HH_LE_INCLUDED == TRUE) + } +#endif + } + } + + /* no registered apps, indicate disable completed */ + if (p_cb->state != BTA_GATTC_STATE_DISABLING) { + p_cb->state = BTA_GATTC_STATE_DISABLED; + memset(p_cb, 0, sizeof(tBTA_GATTC_CB)); + } +} + +/******************************************************************************* +** +** Function bta_gattc_register +** +** Description Register a GATT client application with BTA. +** +** Returns void +** +*******************************************************************************/ +void bta_gattc_register(tBTA_GATTC_CB *p_cb, tBTA_GATTC_DATA *p_data) +{ + tBTA_GATTC cb_data; + UINT8 i; + tBT_UUID *p_app_uuid = &p_data->api_reg.app_uuid; + tBTA_GATTC_INT_START_IF *p_buf; + tBTA_GATT_STATUS status = BTA_GATT_NO_RESOURCES; + + + APPL_TRACE_DEBUG("bta_gattc_register state %d\n", p_cb->state); + memset(&cb_data, 0, sizeof(cb_data)); + cb_data.reg_oper.status = BTA_GATT_NO_RESOURCES; + + /* check if GATTC module is already enabled . Else enable */ + /* This is a workaround because the task priority of btc (BTA_GATTC_CLOSE_EVT + in that task) is lower than the priority of the btu task. + Consequently, the p_cb->state fails to be restored to BTA_GATTC_STATE_DISABLED + and remains in the BTA_GATTC_STATE_DISABLING state. */ + if (p_cb->state == BTA_GATTC_STATE_DISABLED || p_cb->state == BTA_GATTC_STATE_DISABLING) { + bta_gattc_enable (p_cb); + } + /* todo need to check duplicate uuid */ + for (i = 0; i < BTA_GATTC_CL_MAX; i ++) { + if (!p_cb->cl_rcb[i].in_use) { + if ((p_app_uuid == NULL) || (p_cb->cl_rcb[i].client_if = GATT_Register(p_app_uuid, &bta_gattc_cl_cback)) == 0) { + APPL_TRACE_ERROR("Register with GATT stack failed.\n"); + status = BTA_GATT_ERROR; + } else { + p_cb->cl_rcb[i].in_use = TRUE; + p_cb->cl_rcb[i].p_cback = p_data->api_reg.p_cback; + memcpy(&p_cb->cl_rcb[i].app_uuid, p_app_uuid, sizeof(tBT_UUID)); + + /* BTA use the same client interface as BTE GATT statck */ + cb_data.reg_oper.client_if = p_cb->cl_rcb[i].client_if; + + if ((p_buf = (tBTA_GATTC_INT_START_IF *) osi_malloc(sizeof(tBTA_GATTC_INT_START_IF))) != NULL) { + p_buf->hdr.event = BTA_GATTC_INT_START_IF_EVT; + p_buf->client_if = p_cb->cl_rcb[i].client_if; + APPL_TRACE_DEBUG("GATTC getbuf sucess.\n"); + bta_sys_sendmsg(p_buf); + status = BTA_GATT_OK; + } else { + GATT_Deregister(p_cb->cl_rcb[i].client_if); + + status = BTA_GATT_NO_RESOURCES; + memset( &p_cb->cl_rcb[i], 0 , sizeof(tBTA_GATTC_RCB)); + } + break; + } + } + } + + /* callback with register event */ + if (p_data->api_reg.p_cback) { + if (p_app_uuid != NULL) { + memcpy(&(cb_data.reg_oper.app_uuid), p_app_uuid, sizeof(tBT_UUID)); + } + cb_data.reg_oper.status = status; + (*p_data->api_reg.p_cback)(BTA_GATTC_REG_EVT, (tBTA_GATTC *)&cb_data); + } +} +/******************************************************************************* +** +** Function bta_gattc_start_if +** +** Description start an application interface. +** +** Returns none. +** +*******************************************************************************/ +void bta_gattc_start_if(tBTA_GATTC_CB *p_cb, tBTA_GATTC_DATA *p_msg) +{ + UNUSED(p_cb); + + if (bta_gattc_cl_get_regcb(p_msg->int_start_if.client_if) != NULL ) { + GATT_StartIf(p_msg->int_start_if.client_if); + } else { + APPL_TRACE_ERROR("Unable to start app.: Unknown interface =%d", p_msg->int_start_if.client_if ); + } +} + +/******************************************************************************* +** +** Function bta_gattc_deregister +** +** Description De-Register a GATT client application with BTA. +** +** Returns void +** +*******************************************************************************/ +void bta_gattc_deregister(tBTA_GATTC_CB *p_cb, tBTA_GATTC_RCB *p_clreg) +{ + UINT8 i; + BT_HDR buf; + + if (p_clreg != NULL) { + /* remove bg connection associated with this rcb */ + for (i = 0; i < BTA_GATTC_KNOWN_SR_MAX; i ++) { + if (p_cb->bg_track[i].in_use) { + if (p_cb->bg_track[i].cif_mask & (1 << (p_clreg->client_if - 1))) { + bta_gattc_mark_bg_conn(p_clreg->client_if, p_cb->bg_track[i].remote_bda, FALSE, FALSE); + GATT_CancelConnect(p_clreg->client_if, p_cb->bg_track[i].remote_bda, FALSE); + } + if (p_cb->bg_track[i].cif_adv_mask & (1 << (p_clreg->client_if - 1))) { + bta_gattc_mark_bg_conn(p_clreg->client_if, p_cb->bg_track[i].remote_bda, FALSE, TRUE); + } + } + } + + if (p_clreg->num_clcb > 0) { + /* close all CLCB related to this app */ + for (i = 0; i < BTA_GATTC_CLCB_MAX; i ++) { + if (p_cb->clcb[i].in_use && (p_cb->clcb[i].p_rcb == p_clreg)) { + p_clreg->dereg_pending = TRUE; + + buf.event = BTA_GATTC_API_CLOSE_EVT; + buf.layer_specific = p_cb->clcb[i].bta_conn_id; + bta_gattc_close(&p_cb->clcb[i], (tBTA_GATTC_DATA *)&buf) ; + } + } + } else { + bta_gattc_deregister_cmpl(p_clreg); + } + } else { + APPL_TRACE_ERROR("Deregister Failed unknown client cif"); +#if defined(BTA_HH_INCLUDED) && (BTA_HH_INCLUDED == TRUE) + bta_hh_cleanup_disable(BTA_HH_OK); +#endif + } +} +/******************************************************************************* +** +** Function bta_gattc_process_api_open +** +** Description process connect API request. +** +** Returns void +** +*******************************************************************************/ +void bta_gattc_process_api_open (tBTA_GATTC_CB *p_cb, tBTA_GATTC_DATA *p_msg) +{ + UINT16 event = ((BT_HDR *)p_msg)->event; + tBTA_GATTC_CLCB *p_clcb = NULL; + tBTA_GATTC_RCB *p_clreg = bta_gattc_cl_get_regcb(p_msg->api_conn.client_if); + UNUSED(p_cb); + + if (p_clreg != NULL) { + if (p_msg->api_conn.is_direct) { + if ((p_clcb = bta_gattc_find_alloc_clcb(p_msg->api_conn.client_if, + p_msg->api_conn.remote_bda, + p_msg->api_conn.transport)) != NULL) { + bta_gattc_sm_execute(p_clcb, event, p_msg); + } else { + APPL_TRACE_ERROR("No resources to open a new connection."); + + bta_gattc_send_open_cback(p_clreg, + BTA_GATT_NO_RESOURCES, + p_msg->api_conn.remote_bda, + BTA_GATT_INVALID_CONN_ID, + p_msg->api_conn.transport, 0); + } + } else { + bta_gattc_init_bk_conn(&p_msg->api_conn, p_clreg); + } + } else { + APPL_TRACE_ERROR("bta_gattc_process_api_open Failed, unknown client_if: %d", + p_msg->api_conn.client_if); + } +} +/******************************************************************************* +** +** Function bta_gattc_process_api_open_cancel +** +** Description process connect API request. +** +** Returns void +** +*******************************************************************************/ +void bta_gattc_process_api_open_cancel (tBTA_GATTC_CB *p_cb, tBTA_GATTC_DATA *p_msg) +{ + UINT16 event = ((BT_HDR *)p_msg)->event; + tBTA_GATTC_CLCB *p_clcb = NULL; + tBTA_GATTC_RCB *p_clreg; + tBTA_GATTC cb_data; + UNUSED(p_cb); + + if (p_msg->api_cancel_conn.is_direct) { + if ((p_clcb = bta_gattc_find_clcb_by_cif(p_msg->api_cancel_conn.client_if, + p_msg->api_cancel_conn.remote_bda, + BTA_GATT_TRANSPORT_LE)) != NULL) { + bta_gattc_sm_execute(p_clcb, event, p_msg); + } else { + APPL_TRACE_ERROR("No such connection need to be cancelled"); + + p_clreg = bta_gattc_cl_get_regcb(p_msg->api_cancel_conn.client_if); + + if (p_clreg && p_clreg->p_cback) { + cb_data.status = BTA_GATT_ERROR; + (*p_clreg->p_cback)(BTA_GATTC_CANCEL_OPEN_EVT, &cb_data); + } + } + } else { + bta_gattc_cancel_bk_conn(&p_msg->api_cancel_conn); + + } +} + +/******************************************************************************* +** +** Function bta_gattc_process_enc_cmpl +** +** Description process encryption complete message. +** +** Returns void +** +*******************************************************************************/ +void bta_gattc_process_enc_cmpl(tBTA_GATTC_CB *p_cb, tBTA_GATTC_DATA *p_msg) +{ + tBTA_GATTC_RCB *p_clreg; + tBTA_GATTC cb_data; + UNUSED(p_cb); + + p_clreg = bta_gattc_cl_get_regcb(p_msg->enc_cmpl.client_if); + + if (p_clreg && p_clreg->p_cback) { + memset(&cb_data, 0, sizeof(tBTA_GATTC)); + + cb_data.enc_cmpl.client_if = p_msg->enc_cmpl.client_if; + bdcpy(cb_data.enc_cmpl.remote_bda, p_msg->enc_cmpl.remote_bda); + + (*p_clreg->p_cback)(BTA_GATTC_ENC_CMPL_CB_EVT, &cb_data); + } +} + +/******************************************************************************* +** +** Function bta_gattc_cancel_open_error +** +** Description +** +** Returns void +** +*******************************************************************************/ +void bta_gattc_cancel_open_error(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data) +{ + tBTA_GATTC cb_data; + UNUSED(p_data); + + cb_data.status = BTA_GATT_ERROR; + + if ( p_clcb && p_clcb->p_rcb && p_clcb->p_rcb->p_cback ) { + (*p_clcb->p_rcb->p_cback)(BTA_GATTC_CANCEL_OPEN_EVT, &cb_data); + } +} + +/******************************************************************************* +** +** Function bta_gattc_open_error +** +** Description +** +** Returns void +** +*******************************************************************************/ +void bta_gattc_open_error(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data) +{ + UNUSED(p_data); + + APPL_TRACE_ERROR("Connection already opened. wrong state"); + + bta_gattc_send_open_cback(p_clcb->p_rcb, + BTA_GATT_OK, + p_clcb->bda, + p_clcb->bta_conn_id, + p_clcb->transport, + 0); +} +/******************************************************************************* +** +** Function bta_gattc_open_fail +** +** Description +** +** Returns void +** +*******************************************************************************/ +void bta_gattc_open_fail(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data) +{ + UNUSED(p_data); + + bta_gattc_send_open_cback(p_clcb->p_rcb, + BTA_GATT_ERROR, + p_clcb->bda, + p_clcb->bta_conn_id, + p_clcb->transport, + 0); + /* open failure, remove clcb */ + bta_gattc_clcb_dealloc(p_clcb); +} + +/******************************************************************************* +** +** Function bta_gattc_open +** +** Description Process API connection function. +** +** Returns void +** +*******************************************************************************/ +void bta_gattc_open(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data) +{ + tBTA_GATTC_DATA gattc_data; + BOOLEAN found_app = FALSE; + tGATT_TCB *p_tcb; + + if (!p_clcb || !p_data) { + return; + } + + p_tcb = gatt_find_tcb_by_addr(p_data->api_conn.remote_bda, BT_TRANSPORT_LE); + if(p_tcb) { + found_app = gatt_find_specific_app_in_hold_link(p_tcb, p_clcb->p_rcb->client_if); + } + /* open/hold a connection */ + if (!GATT_Connect(p_clcb->p_rcb->client_if, p_data->api_conn.remote_bda, p_data->api_conn.remote_addr_type, + TRUE, p_data->api_conn.transport, p_data->api_conn.is_aux)) { + APPL_TRACE_ERROR("Connection open failure"); + + bta_gattc_sm_execute(p_clcb, BTA_GATTC_INT_OPEN_FAIL_EVT, p_data); + } else { + /* a connected remote device */ + if (GATT_GetConnIdIfConnected(p_clcb->p_rcb->client_if, + p_data->api_conn.remote_bda, + &p_clcb->bta_conn_id, + p_data->api_conn.transport)) { + gattc_data.int_conn.hdr.layer_specific = p_clcb->bta_conn_id; + gattc_data.int_conn.already_connect = found_app; + bta_gattc_sm_execute(p_clcb, BTA_GATTC_INT_CONN_EVT, &gattc_data); + } + /* else wait for the callback event */ + } +} +/******************************************************************************* +** +** Function bta_gattc_init_bk_conn +** +** Description Process API Open for a background connection +** +** Returns void +** +*******************************************************************************/ +void bta_gattc_init_bk_conn(tBTA_GATTC_API_OPEN *p_data, tBTA_GATTC_RCB *p_clreg) +{ + tBTA_GATT_STATUS status = BTA_GATT_NO_RESOURCES; + UINT16 conn_id; + tBTA_GATTC_CLCB *p_clcb; + tBTA_GATTC_DATA gattc_data; + + if (bta_gattc_mark_bg_conn(p_data->client_if, p_data->remote_bda, TRUE, FALSE)) { + /* always call open to hold a connection */ + if (!GATT_Connect(p_data->client_if, p_data->remote_bda, + p_data->remote_addr_type, FALSE, + p_data->transport, p_data->is_aux)) { +#if (!CONFIG_BT_STACK_NO_LOG) + uint8_t *bda = (uint8_t *)p_data->remote_bda; +#endif + status = BTA_GATT_ERROR; + APPL_TRACE_ERROR("%s unable to connect to remote bd_addr:%02x:%02x:%02x:%02x:%02x:%02x", + __func__, bda[0], bda[1], bda[2], bda[3], bda[4], bda[5]); + + } else { + status = BTA_GATT_OK; + + /* if is a connected remote device */ + if (GATT_GetConnIdIfConnected(p_data->client_if, + p_data->remote_bda, + &conn_id, + p_data->transport)) { + if ((p_clcb = bta_gattc_find_alloc_clcb(p_data->client_if, p_data->remote_bda, + BTA_GATT_TRANSPORT_LE)) != NULL) { + gattc_data.hdr.layer_specific = p_clcb->bta_conn_id = conn_id; + + /* open connection */ + bta_gattc_sm_execute(p_clcb, BTA_GATTC_INT_CONN_EVT, &gattc_data); + status = BTA_GATT_OK; + } + } + } + } + + /* open failure, report OPEN_EVT */ + if (status != BTA_GATT_OK) { + bta_gattc_send_open_cback(p_clreg, status, p_data->remote_bda, + BTA_GATT_INVALID_CONN_ID, BTA_GATT_TRANSPORT_LE, 0); + } +} +/******************************************************************************* +** +** Function bta_gattc_cancel_bk_conn +** +** Description Process API Cancel Open for a background connection +** +** Returns void +** +*******************************************************************************/ +void bta_gattc_cancel_bk_conn(tBTA_GATTC_API_CANCEL_OPEN *p_data) +{ + tBTA_GATTC_RCB *p_clreg; + tBTA_GATTC cb_data; + cb_data.status = BTA_GATT_ERROR; + + /* remove the device from the bg connection mask */ + if (bta_gattc_mark_bg_conn(p_data->client_if, p_data->remote_bda, FALSE, FALSE)) { + if (GATT_CancelConnect(p_data->client_if, p_data->remote_bda, FALSE)) { + cb_data.status = BTA_GATT_OK; + } else { + APPL_TRACE_ERROR("bta_gattc_cancel_bk_conn failed"); + } + } + p_clreg = bta_gattc_cl_get_regcb(p_data->client_if); + + if (p_clreg && p_clreg->p_cback) { + (*p_clreg->p_cback)(BTA_GATTC_CANCEL_OPEN_EVT, &cb_data); + } + +} +/******************************************************************************* +** +** Function bta_gattc_int_cancel_open_ok +** +** Description +** +** Returns void +** +*******************************************************************************/ +void bta_gattc_cancel_open_ok(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data) +{ + tBTA_GATTC cb_data; + UNUSED(p_data); + + if ( p_clcb->p_rcb->p_cback ) { + cb_data.status = BTA_GATT_OK; + (*p_clcb->p_rcb->p_cback)(BTA_GATTC_CANCEL_OPEN_EVT, &cb_data); + } + + bta_gattc_clcb_dealloc(p_clcb); +} +/******************************************************************************* +** +** Function bta_gattc_cancel_open +** +** Description +** +** Returns void +** +*******************************************************************************/ +void bta_gattc_cancel_open(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data) +{ + tBTA_GATTC cb_data; + + if (GATT_CancelConnect(p_clcb->p_rcb->client_if, p_data->api_cancel_conn.remote_bda, TRUE)) { + bta_gattc_sm_execute(p_clcb, BTA_GATTC_INT_CANCEL_OPEN_OK_EVT, p_data); + } else { + if ( p_clcb->p_rcb->p_cback ) { + cb_data.status = BTA_GATT_ERROR; + (*p_clcb->p_rcb->p_cback)(BTA_GATTC_CANCEL_OPEN_EVT, &cb_data); + } + } +} +/******************************************************************************* +** +** Function bta_gattc_conn +** +** Description receive connection callback from stack +** +** Returns void +** +*******************************************************************************/ +void bta_gattc_conn(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data) +{ + tBTA_GATTC_IF gatt_if; + APPL_TRACE_DEBUG("bta_gattc_conn server cache state=%d", p_clcb->p_srcb->state); + + if (p_data != NULL) { + APPL_TRACE_DEBUG("bta_gattc_conn conn_id=%d", p_data->hdr.layer_specific); + p_clcb->bta_conn_id = p_data->int_conn.hdr.layer_specific; + + GATT_GetConnectionInfor(p_data->hdr.layer_specific, + &gatt_if, p_clcb->bda, &p_clcb->transport); + } + + p_clcb->p_srcb->connected = TRUE; + + if (p_clcb->p_srcb->mtu == 0) { + p_clcb->p_srcb->mtu = GATT_DEF_BLE_MTU_SIZE; + } + + /* start database cache if needed */ + if (p_clcb->p_srcb->p_srvc_cache == NULL || + p_clcb->p_srcb->state != BTA_GATTC_SERV_IDLE) { + if (p_clcb->p_srcb->state == BTA_GATTC_SERV_IDLE) { +#if (GATTC_CACHE_NVS == TRUE) + p_clcb->p_srcb->state = BTA_GATTC_SERV_LOAD; + if (bta_gattc_cache_load(p_clcb)) { + p_clcb->p_srcb->state = BTA_GATTC_SERV_IDLE; + bta_gattc_reset_discover_st(p_clcb->p_srcb, BTA_GATT_OK); + //register service change + bta_gattc_register_service_change_notify(p_clcb->bta_conn_id, p_clcb->bda); + } else +#endif + { /* cache is building */ + if (bta_gattc_cb.auto_disc) { + p_clcb->p_srcb->state = BTA_GATTC_SERV_DISC; + /* cache load failure, start discovery */ + bta_gattc_start_discover(p_clcb, NULL); + } + } + } else { /* cache is building */ + p_clcb->state = BTA_GATTC_DISCOVER_ST; + } + } else { + /* a pending service handle change indication */ + if (p_clcb->p_srcb->srvc_hdl_chg) { + p_clcb->p_srcb->srvc_hdl_chg = FALSE; + /* start discovery */ + bta_gattc_sm_execute(p_clcb, BTA_GATTC_INT_DISCOVER_EVT, NULL); + } + } + + if (p_clcb->p_rcb) { + /* there is no RM for GATT */ + if (p_clcb->transport == BTA_TRANSPORT_BR_EDR) { + bta_sys_conn_open(BTA_ID_GATTC, BTA_ALL_APP_ID, p_clcb->bda); + } + tBTA_GATT_STATUS status = BTA_GATT_OK; + if (p_data && p_data->int_conn.already_connect) { + //clear already_connect + p_data->int_conn.already_connect = FALSE; + status = BTA_GATT_ALREADY_OPEN; + } + bta_gattc_send_open_cback(p_clcb->p_rcb, + status, + p_clcb->bda, + p_clcb->bta_conn_id, + p_clcb->transport, + p_clcb->p_srcb->mtu); + + } +} +/******************************************************************************* +** +** Function bta_gattc_conncback +** +** Description receive connection callback from stack +** +** Returns void +** +*******************************************************************************/ +void bta_gattc_conncback(tBTA_GATTC_RCB *p_rcb, tBTA_GATTC_DATA *p_data) +{ + if (p_rcb) { + bta_gattc_send_connect_cback(p_rcb, + p_data->int_conn.remote_bda, + p_data->int_conn.hdr.layer_specific, p_data->int_conn.conn_params, p_data->int_conn.role, + p_data->int_conn.ble_addr_type, p_data->int_conn.conn_handle); + + } +} +/******************************************************************************* +** +** Function bta_gattc_disconncback +** +** Description receive disconnection callback from stack +** +** Returns void +** +*******************************************************************************/ +void bta_gattc_disconncback(tBTA_GATTC_RCB *p_rcb, tBTA_GATTC_DATA *p_data) +{ + if (p_rcb) { + // Clear up the notification registration information by BD_ADDR + bta_gattc_clear_notif_registration_by_bda(p_rcb, p_data->int_conn.remote_bda); + bta_gattc_send_disconnect_cback(p_rcb, + p_data->int_conn.reason, + p_data->int_conn.remote_bda, + p_data->int_conn.hdr.layer_specific); + + } +} +/******************************************************************************* +** +** Function bta_gattc_close_fail +** +** Description close a connection. +** +** Returns void +** +*******************************************************************************/ +void bta_gattc_close_fail(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data) +{ + tBTA_GATTC cb_data; + + if ( p_clcb->p_rcb->p_cback ) { + memset(&cb_data, 0, sizeof(tBTA_GATTC)); + cb_data.close.client_if = p_clcb->p_rcb->client_if; + cb_data.close.conn_id = p_data->hdr.layer_specific; + bdcpy(cb_data.close.remote_bda, p_clcb->bda); + cb_data.close.status = BTA_GATT_ERROR; + cb_data.close.reason = BTA_GATT_CONN_NONE; + + + (*p_clcb->p_rcb->p_cback)(BTA_GATTC_CLOSE_EVT, &cb_data); + } +} +/******************************************************************************* +** +** Function bta_gattc_api_close +** +** Description close a GATTC connection. +** +** Returns void +** +*******************************************************************************/ +void bta_gattc_close(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data) +{ + tBTA_GATTC_CBACK *p_cback = p_clcb->p_rcb->p_cback; + tBTA_GATTC_RCB *p_clreg = p_clcb->p_rcb; + tBTA_GATTC cb_data; + + APPL_TRACE_DEBUG("bta_gattc_close conn_id=%d", p_clcb->bta_conn_id); + + cb_data.close.client_if = p_clcb->p_rcb->client_if; + cb_data.close.conn_id = p_clcb->bta_conn_id; + cb_data.close.reason = p_clcb->reason; + cb_data.close.status = p_clcb->status; + bdcpy(cb_data.close.remote_bda, p_clcb->bda); + + if (p_clcb->transport == BTA_TRANSPORT_BR_EDR) { + bta_sys_conn_close( BTA_ID_GATTC , BTA_ALL_APP_ID, p_clcb->bda); + } + + if (p_data->hdr.event == BTA_GATTC_API_CLOSE_EVT) { + cb_data.close.status = GATT_Disconnect(p_data->hdr.layer_specific); + } else if (p_data->hdr.event == BTA_GATTC_INT_DISCONN_EVT) { + cb_data.close.status = BTA_GATT_OK; + cb_data.close.reason = p_data->int_conn.reason; + } + + if (p_cback) { + (* p_cback)(BTA_GATTC_CLOSE_EVT, (tBTA_GATTC *)&cb_data); + } + + if (p_clreg->num_clcb == 0 && p_clreg->dereg_pending) { + bta_gattc_deregister_cmpl(p_clreg); + } +} +/******************************************************************************* +** +** Function bta_gattc_reset_discover_st +** +** Description when a SRCB finished discovery, tell all related clcb. +** +** Returns None. +** +*******************************************************************************/ +void bta_gattc_reset_discover_st(tBTA_GATTC_SERV *p_srcb, tBTA_GATT_STATUS status) +{ + tBTA_GATTC_CB *p_cb = &bta_gattc_cb; + UINT8 i; + + for (i = 0; i < BTA_GATTC_CLCB_MAX; i ++) { + if (p_cb->clcb[i].p_srcb == p_srcb) { + p_cb->clcb[i].status = status; + bta_gattc_sm_execute(&p_cb->clcb[i], BTA_GATTC_DISCOVER_CMPL_EVT, NULL); + } + } +} +/******************************************************************************* +** +** Function bta_gattc_disc_close +** +** Description close a GATTC connection while in discovery state. +** +** Returns void +** +*******************************************************************************/ +void bta_gattc_disc_close(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data) +{ + APPL_TRACE_DEBUG("%s: Discovery cancel conn_id=%d", __func__, + p_clcb->bta_conn_id); + + if (p_clcb->disc_active) { + bta_gattc_reset_discover_st(p_clcb->p_srcb, BTA_GATT_ERROR); + } else { + p_clcb->state = BTA_GATTC_CONN_ST; + } + + // This function only gets called as the result of a BTA_GATTC_API_CLOSE_EVT + // while in the BTA_GATTC_DISCOVER_ST state. Once the state changes, the + // connection itself still needs to be closed to resolve the original event. + if (p_clcb->state == BTA_GATTC_CONN_ST) { + APPL_TRACE_DEBUG("State is back to BTA_GATTC_CONN_ST. " + "Trigger connection close"); + bta_gattc_close(p_clcb, p_data); + } +} +/******************************************************************************* +** +** Function bta_gattc_set_discover_st +** +** Description when a SRCB start discovery, tell all related clcb and set +** the state. +** +** Returns None. +** +*******************************************************************************/ +void bta_gattc_set_discover_st(tBTA_GATTC_SERV *p_srcb) +{ + tBTA_GATTC_CB *p_cb = &bta_gattc_cb; + UINT8 i; + + for (i = 0; i < BTA_GATTC_CLCB_MAX; i ++) { + if (p_cb->clcb[i].p_srcb == p_srcb) { + p_cb->clcb[i].status = BTA_GATT_OK; + p_cb->clcb[i].state = BTA_GATTC_DISCOVER_ST; + } + } +} +/******************************************************************************* +** +** Function bta_gattc_restart_discover +** +** Description process service change in discovery state, mark up the auto +** update flag and set status to be discovery cancel for current +** discovery. +** +** Returns None. +** +*******************************************************************************/ +void bta_gattc_restart_discover(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data) +{ + UNUSED(p_data); + + p_clcb->status = BTA_GATT_CANCEL; + p_clcb->auto_update = BTA_GATTC_DISC_WAITING; +} + +/******************************************************************************* +** +** Function bta_gattc_cfg_mtu +** +** Description Configure MTU size on the GATT connection. +** +** Returns None. +** +*******************************************************************************/ +void bta_gattc_cfg_mtu(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data) +{ + tBTA_GATT_STATUS status; + + if (bta_gattc_enqueue(p_clcb, p_data)) { + status = GATTC_ConfigureMTU (p_clcb->bta_conn_id); + + /* if failed, return callback here */ + if (status != GATT_SUCCESS && status != GATT_CMD_STARTED) { + /* Dequeue the data, if it was enqueued */ + if (p_clcb->p_q_cmd == p_data) { + p_clcb->p_q_cmd = NULL; + bta_gattc_pop_command_to_send(p_clcb); + } + + bta_gattc_cmpl_sendmsg(p_clcb->bta_conn_id, GATTC_OPTYPE_CONFIG, status, NULL); + } + } +} +/******************************************************************************* +** +** Function bta_gattc_start_discover +** +** Description Start a discovery send to server. +** +** Returns None. +** +*******************************************************************************/ +void bta_gattc_start_discover(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data) +{ + UNUSED(p_data); + + APPL_TRACE_DEBUG("bta_gattc_start_discover conn_id=%d p_clcb->p_srcb->state = %d ", + p_clcb->bta_conn_id, p_clcb->p_srcb->state); + + if (((p_clcb->p_q_cmd == NULL || p_clcb->auto_update == BTA_GATTC_REQ_WAITING) && + p_clcb->p_srcb->state == BTA_GATTC_SERV_IDLE) || + p_clcb->p_srcb->state == BTA_GATTC_SERV_DISC) { + /* no pending operation, start discovery right away */ + p_clcb->auto_update = BTA_GATTC_NO_SCHEDULE; + + if (p_clcb->p_srcb != NULL) { + /* clear the service change mask */ + p_clcb->p_srcb->srvc_hdl_chg = FALSE; + p_clcb->p_srcb->update_count = 0; + p_clcb->p_srcb->state = BTA_GATTC_SERV_DISC_ACT; + #if (BT_MULTI_CONNECTION_ENBALE == FALSE) + if (p_clcb->transport == BTA_TRANSPORT_LE) { + L2CA_EnableUpdateBleConnParams(p_clcb->p_srcb->server_bda, FALSE); + } + #endif + + /* set all srcb related clcb into discovery ST */ + bta_gattc_set_discover_st(p_clcb->p_srcb); + + if ((p_clcb->status = bta_gattc_init_cache(p_clcb->p_srcb)) == BTA_GATT_OK) { + p_clcb->status = bta_gattc_discover_pri_service(p_clcb->bta_conn_id, + p_clcb->p_srcb, GATT_DISC_SRVC_ALL); + } + if (p_clcb->status != BTA_GATT_OK) { + APPL_TRACE_ERROR("discovery on server failed"); + bta_gattc_reset_discover_st(p_clcb->p_srcb, p_clcb->status); + //discover service complete, trigger callback + tBTA_GATTC cb_data; + cb_data.dis_cmpl.status = p_clcb->status; + cb_data.dis_cmpl.conn_id = p_clcb->bta_conn_id; + ( *p_clcb->p_rcb->p_cback)(BTA_GATTC_DIS_SRVC_CMPL_EVT, &cb_data); + } else { + p_clcb->disc_active = TRUE; + } + p_clcb->searched_service_source = BTA_GATTC_SERVICE_INFO_FROM_REMOTE_DEVICE; + } else { + APPL_TRACE_ERROR("unknown device, can not start discovery"); + } + } + /* pending operation, wait until it finishes */ + else { + p_clcb->auto_update = BTA_GATTC_DISC_WAITING; + + if (p_clcb->p_srcb->state == BTA_GATTC_SERV_IDLE) { + p_clcb->state = BTA_GATTC_CONN_ST; /* set clcb state */ + } + } + +} +/******************************************************************************* +** +** Function bta_gattc_disc_cmpl +** +** Description discovery on server is finished +** +** Returns None. +** +*******************************************************************************/ +void bta_gattc_disc_cmpl(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data) +{ + tBTA_GATTC_DATA *p_q_cmd = p_clcb->p_q_cmd; + UNUSED(p_data); + + APPL_TRACE_DEBUG("bta_gattc_disc_cmpl conn_id=%d, status = %d", p_clcb->bta_conn_id, p_clcb->status); + + p_clcb->p_srcb->state = BTA_GATTC_SERV_IDLE; + p_clcb->disc_active = FALSE; + + if (p_clcb->status != GATT_SUCCESS) { + /* clean up cache */ + if (p_clcb->p_srcb && p_clcb->p_srcb->p_srvc_cache) { + list_free(p_clcb->p_srcb->p_srvc_cache); + p_clcb->p_srcb->p_srvc_cache = NULL; + } +#if(GATTC_CACHE_NVS == TRUE) + /* used to reset cache in application */ + bta_gattc_cache_reset(p_clcb->p_srcb->server_bda); +#endif + } + if (p_clcb->p_srcb && p_clcb->p_srcb->p_srvc_list) { + /* release pending attribute list buffer */ + osi_free(p_clcb->p_srcb->p_srvc_list); + p_clcb->p_srcb->p_srvc_list = NULL; + //osi_free_and_reset((void **)&p_clcb->p_srcb->p_srvc_list); + } + + if (p_clcb->auto_update == BTA_GATTC_DISC_WAITING) { + /* start discovery again */ + bta_gattc_sm_execute(p_clcb, BTA_GATTC_INT_DISCOVER_EVT, NULL); + } + /* get any queued command to proceed */ + else if (p_q_cmd != NULL) { + p_clcb->p_q_cmd = NULL; + /* execute pending operation of link block still present */ + if (l2cu_find_lcb_by_bd_addr(p_clcb->p_srcb->server_bda, BT_TRANSPORT_LE) != NULL) { + bta_gattc_sm_execute(p_clcb, p_q_cmd->hdr.event, p_q_cmd); + } + /* if the command executed requeued the cmd, we don't + * want to free the underlying buffer that's being + * referenced by p_clcb->p_q_cmd + */ + if (p_q_cmd != p_clcb->p_q_cmd) { + osi_free(p_q_cmd); + p_q_cmd = NULL; + } + } + + //register service change + bta_gattc_register_service_change_notify(p_clcb->bta_conn_id, p_clcb->bda); + +} +/******************************************************************************* +** +** Function bta_gattc_read +** +** Description Read an attribute +** +** Returns None. +** +*******************************************************************************/ +void bta_gattc_read(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data) +{ + if (!bta_gattc_enqueue(p_clcb, p_data)) { + return; + } + + tGATT_READ_PARAM read_param; + memset (&read_param, 0 ,sizeof(tGATT_READ_PARAM)); + read_param.by_handle.handle = p_data->api_read.handle; + read_param.by_handle.auth_req = p_data->api_read.auth_req; + + tBTA_GATT_STATUS status = GATTC_Read(p_clcb->bta_conn_id, GATT_READ_BY_HANDLE, &read_param); + + /* read fail */ + if (status != BTA_GATT_OK) { + bta_gattc_cmpl_sendmsg(p_clcb->bta_conn_id, GATTC_OPTYPE_READ, status, NULL); + } +} +/******************************************************************************* +** +** Function bta_gattc_read_by_type +** +** Description Read an attribute +** +** Returns None. +** +*******************************************************************************/ +void bta_gattc_read_by_type(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data) +{ + if (!bta_gattc_enqueue(p_clcb, p_data)) { + return; + } + + tGATT_READ_PARAM read_param; + memset (&read_param, 0 ,sizeof(tGATT_READ_PARAM)); + read_param.service.auth_req = p_data->api_read.auth_req; + read_param.service.s_handle = p_data->api_read.s_handle; + read_param.service.e_handle = p_data->api_read.e_handle; + memcpy(&(read_param.service.uuid), &(p_data->api_read.uuid), sizeof(tBT_UUID)); + + tBTA_GATT_STATUS status = GATTC_Read(p_clcb->bta_conn_id, GATT_READ_BY_TYPE, &read_param); + + /* read fail */ + if (status != BTA_GATT_OK) { + bta_gattc_cmpl_sendmsg(p_clcb->bta_conn_id, GATTC_OPTYPE_READ, status, NULL); + } +} +/******************************************************************************* +** +** Function bta_gattc_read_multi +** +** Description read multiple +** +** Returns None. +*********************************************************************************/ +void bta_gattc_read_multi(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data) +{ + tBTA_GATT_STATUS status = BTA_GATT_OK; + tGATT_READ_PARAM read_param; + + if (bta_gattc_enqueue(p_clcb, p_data)) { + memset(&read_param, 0, sizeof(tGATT_READ_PARAM)); + + if (status == BTA_GATT_OK) { + read_param.read_multiple.num_handles = p_data->api_read_multi.num_attr; + read_param.read_multiple.auth_req = p_data->api_read_multi.auth_req; + memcpy(&read_param.read_multiple.handles, p_data->api_read_multi.handles, + sizeof(UINT16) * p_data->api_read_multi.num_attr); + + status = GATTC_Read(p_clcb->bta_conn_id, GATT_READ_MULTIPLE, &read_param); + } + + /* read fail */ + if (status != BTA_GATT_OK) { + bta_gattc_cmpl_sendmsg(p_clcb->bta_conn_id, GATTC_OPTYPE_READ, status, NULL); + } + } +} +/******************************************************************************* +** +** Function bta_gattc_read_multi_var +** +** Description read multiple variable +** +** Returns None. +*********************************************************************************/ +void bta_gattc_read_multi_var(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data) +{ + tBTA_GATT_STATUS status = BTA_GATT_OK; + tGATT_READ_PARAM read_param; + + if (bta_gattc_enqueue(p_clcb, p_data)) { + memset(&read_param, 0, sizeof(tGATT_READ_PARAM)); + + if (status == BTA_GATT_OK) { + read_param.read_multiple.num_handles = p_data->api_read_multi.num_attr; + read_param.read_multiple.auth_req = p_data->api_read_multi.auth_req; + memcpy(&read_param.read_multiple.handles, p_data->api_read_multi.handles, + sizeof(UINT16) * p_data->api_read_multi.num_attr); + + status = GATTC_Read(p_clcb->bta_conn_id, GATT_READ_MULTIPLE_VAR, &read_param); + } + + /* read fail */ + if (status != BTA_GATT_OK) { + bta_gattc_cmpl_sendmsg(p_clcb->bta_conn_id, GATTC_OPTYPE_READ, status, NULL); + } + } +} +/******************************************************************************* +** +** Function bta_gattc_write +** +** Description Write an attribute +** +** Returns None. +** +*******************************************************************************/ +void bta_gattc_write(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data) +{ + if (!bta_gattc_enqueue(p_clcb, p_data)) { + return; + } + + tBTA_GATT_STATUS status = BTA_GATT_OK; + + tGATT_CL_COMPLETE cl_data = {0}; + + cl_data.att_value.conn_id = p_clcb->bta_conn_id; + cl_data.att_value.handle = p_data->api_write.handle; + cl_data.att_value.offset = p_data->api_write.offset; + cl_data.att_value.len = p_data->api_write.len; + cl_data.att_value.auth_req = p_data->api_write.auth_req; + + if (p_data->api_write.p_value) { + memcpy(cl_data.att_value.value, p_data->api_write.p_value, p_data->api_write.len); + } + + status = GATTC_Write(p_clcb->bta_conn_id, p_data->api_write.write_type, &cl_data.att_value); + + /* write fail */ + if (status != BTA_GATT_OK) { + cl_data.handle = p_data->api_write.handle; + bta_gattc_cmpl_sendmsg(p_clcb->bta_conn_id, GATTC_OPTYPE_WRITE, status, &cl_data); + } +} +/******************************************************************************* +** +** Function bta_gattc_execute +** +** Description send execute write +** +** Returns None. +*********************************************************************************/ +void bta_gattc_execute(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data) +{ + tBTA_GATT_STATUS status; + + if (bta_gattc_enqueue(p_clcb, p_data)) { + status = GATTC_ExecuteWrite(p_clcb->bta_conn_id, p_data->api_exec.is_execute); + + if (status != BTA_GATT_OK) { + /* Dequeue the data, if it was enqueued */ + if (p_clcb->p_q_cmd == p_data) { + p_clcb->p_q_cmd = NULL; + bta_gattc_pop_command_to_send(p_clcb); + } + + bta_gattc_cmpl_sendmsg(p_clcb->bta_conn_id, GATTC_OPTYPE_EXE_WRITE, status, NULL); + } + } +} +/******************************************************************************* +** +** Function bta_gattc_confirm +** +** Description send handle value confirmation +** +** Returns None. +** +*******************************************************************************/ +void bta_gattc_confirm(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data) +{ + UINT16 handle = p_data->api_confirm.handle; + + if (GATTC_SendHandleValueConfirm(p_data->api_confirm.hdr.layer_specific, handle) + != GATT_SUCCESS) { + APPL_TRACE_ERROR("bta_gattc_confirm to handle [0x%04x] failed", handle); + } else { + /* if over BR_EDR, inform PM for mode change */ + if (p_clcb->transport == BTA_TRANSPORT_BR_EDR) { + bta_sys_busy(BTA_ID_GATTC, BTA_ALL_APP_ID, p_clcb->bda); + bta_sys_idle(BTA_ID_GATTC, BTA_ALL_APP_ID, p_clcb->bda); + } + } +} +/******************************************************************************* +** +** Function bta_gattc_read_cmpl +** +** Description read complete +** +** Returns None. +** +*******************************************************************************/ +void bta_gattc_read_cmpl(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_OP_CMPL *p_data) +{ + UINT8 event; + tBTA_GATTC cb_data; + tBTA_GATT_UNFMT read_value; + + memset(&cb_data, 0, sizeof(tBTA_GATTC)); + memset(&read_value, 0, sizeof(tBTA_GATT_UNFMT)); + + cb_data.read.status = p_data->status; + + if (p_data->p_cmpl != NULL && p_data->status == BTA_GATT_OK) { + cb_data.read.handle = p_data->p_cmpl->att_value.handle; + + read_value.len = p_data->p_cmpl->att_value.len; + read_value.p_value = p_data->p_cmpl->att_value.value; + cb_data.read.p_value = &read_value; + } else { + cb_data.read.handle = p_clcb->p_q_cmd->api_read.handle; + } + + if (p_clcb->p_q_cmd->hdr.event != BTA_GATTC_API_READ_MULTI_EVT && + p_clcb->p_q_cmd->hdr.event != BTA_GATTC_API_READ_MULTI_VAR_EVT) { + event = p_clcb->p_q_cmd->api_read.cmpl_evt; + } else { + event = p_clcb->p_q_cmd->api_read_multi.cmpl_evt; + } + cb_data.read.conn_id = p_clcb->bta_conn_id; + //free the command data store in the queue. + bta_gattc_free_command_data(p_clcb); + bta_gattc_pop_command_to_send(p_clcb); + /* read complete, callback */ + ( *p_clcb->p_rcb->p_cback)(event, (tBTA_GATTC *)&cb_data); + +} +/******************************************************************************* +** +** Function bta_gattc_write_cmpl +** +** Description write complete +** +** Returns None. +** +*******************************************************************************/ +void bta_gattc_write_cmpl(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_OP_CMPL *p_data) +{ + tBTA_GATTC cb_data = {0}; + UINT8 event; + tBTA_GATTC_CONN *p_conn = bta_gattc_conn_find(p_clcb->bda); + + memset(&cb_data, 0, sizeof(tBTA_GATTC)); + + cb_data.write.status = p_data->status; + cb_data.write.handle = p_data->p_cmpl->att_value.handle; + if (p_clcb->p_q_cmd->api_write.hdr.event == BTA_GATTC_API_WRITE_EVT && + p_clcb->p_q_cmd->api_write.write_type == BTA_GATTC_WRITE_PREPARE) { + // Should check the value received from the peer device is correct or not. + if (memcmp(p_clcb->p_q_cmd->api_write.p_value, p_data->p_cmpl->att_value.value, + p_data->p_cmpl->att_value.len) != 0) { + cb_data.write.status = BTA_GATT_INVALID_PDU; + } + + event = BTA_GATTC_PREP_WRITE_EVT; + } else { + event = p_clcb->p_q_cmd->api_write.cmpl_evt; + } + //free the command data store in the queue. + bta_gattc_free_command_data(p_clcb); + bta_gattc_pop_command_to_send(p_clcb); + cb_data.write.conn_id = p_clcb->bta_conn_id; + if (p_conn && p_conn->svc_change_descr_handle == cb_data.write.handle) { + if(cb_data.write.status != BTA_GATT_OK) { + p_conn->write_remote_svc_change_ccc_done = FALSE; + APPL_TRACE_ERROR("service change write ccc failed"); + } + return; + } + /* write complete, callback */ + ( *p_clcb->p_rcb->p_cback)(event, (tBTA_GATTC *)&cb_data); + +} +/******************************************************************************* +** +** Function bta_gattc_exec_cmpl +** +** Description execute write complete +** +** Returns None. +** +*******************************************************************************/ +void bta_gattc_exec_cmpl(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_OP_CMPL *p_data) +{ + tBTA_GATTC cb_data; + //free the command data store in the queue. + bta_gattc_free_command_data(p_clcb); + bta_gattc_pop_command_to_send(p_clcb); + p_clcb->status = BTA_GATT_OK; + + /* execute complete, callback */ + cb_data.exec_cmpl.conn_id = p_clcb->bta_conn_id; + cb_data.exec_cmpl.status = p_data->status; + + ( *p_clcb->p_rcb->p_cback)(BTA_GATTC_EXEC_EVT, &cb_data); + +} + +/******************************************************************************* +** +** Function bta_gattc_cfg_mtu_cmpl +** +** Description configure MTU operation complete +** +** Returns None. +** +*******************************************************************************/ +void bta_gattc_cfg_mtu_cmpl(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_OP_CMPL *p_data) +{ + tBTA_GATTC cb_data; + //free the command data store in the queue. + bta_gattc_free_command_data(p_clcb); + bta_gattc_pop_command_to_send(p_clcb); + + if (p_data->p_cmpl && p_data->status == BTA_GATT_OK) { + p_clcb->p_srcb->mtu = p_data->p_cmpl->mtu; + } + + /* configure MTU complete, callback */ + p_clcb->status = p_data->status; + cb_data.cfg_mtu.conn_id = p_clcb->bta_conn_id; + cb_data.cfg_mtu.status = p_data->status; + cb_data.cfg_mtu.mtu = p_clcb->p_srcb->mtu; + + (*p_clcb->p_rcb->p_cback) (BTA_GATTC_CFG_MTU_EVT, &cb_data); + +} +/******************************************************************************* +** +** Function bta_gattc_op_cmpl +** +** Description operation completed. +** +** Returns None. +** +*******************************************************************************/ +void bta_gattc_op_cmpl(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data) +{ + UINT8 op = (UINT8)p_data->op_cmpl.op_code; + UINT8 mapped_op = 0; + + APPL_TRACE_DEBUG("bta_gattc_op_cmpl op = %d", op); + + if (op == GATTC_OPTYPE_INDICATION || op == GATTC_OPTYPE_NOTIFICATION) { + APPL_TRACE_ERROR("unexpected operation, ignored"); + } else if (op >= GATTC_OPTYPE_READ) { + if (p_clcb->p_q_cmd == NULL) { + APPL_TRACE_ERROR("No pending command"); + return; + } + if (p_clcb->p_q_cmd->hdr.event != bta_gattc_opcode_to_int_evt[op - GATTC_OPTYPE_READ]) { + if ((p_clcb->p_q_cmd->hdr.event != BTA_GATTC_API_READ_MULTI_EVT) && + (p_clcb->p_q_cmd->hdr.event != BTA_GATTC_API_READ_BY_TYPE_EVT) && + (p_clcb->p_q_cmd->hdr.event != BTA_GATTC_API_READ_MULTI_VAR_EVT)) { + mapped_op = p_clcb->p_q_cmd->hdr.event - BTA_GATTC_API_READ_EVT + GATTC_OPTYPE_READ; + if ( mapped_op > GATTC_OPTYPE_INDICATION) { + mapped_op = 0; + } + +#if (BT_TRACE_VERBOSE == TRUE) + APPL_TRACE_ERROR("expect op:(%s :0x%04x), receive unexpected operation (%s).", + bta_gattc_op_code_name[mapped_op] , p_clcb->p_q_cmd->hdr.event, + bta_gattc_op_code_name[op]); +#else + APPL_TRACE_ERROR("expect op:(%u :0x%04x), receive unexpected operation (%u).", + mapped_op , p_clcb->p_q_cmd->hdr.event, op); +#endif + return; + } + } + + /* discard responses if service change indication is received before operation completed */ + if (p_clcb->auto_update == BTA_GATTC_DISC_WAITING && p_clcb->p_srcb->srvc_hdl_chg) { + APPL_TRACE_DEBUG("Discard all responses when service change indication is received."); + p_data->op_cmpl.status = GATT_ERROR; + } + + /* service handle change void the response, discard it */ + if (op == GATTC_OPTYPE_READ) { + bta_gattc_read_cmpl(p_clcb, &p_data->op_cmpl); + } + + else if (op == GATTC_OPTYPE_WRITE) { + bta_gattc_write_cmpl(p_clcb, &p_data->op_cmpl); + } + + else if (op == GATTC_OPTYPE_EXE_WRITE) { + bta_gattc_exec_cmpl(p_clcb, &p_data->op_cmpl); + } + + else if (op == GATTC_OPTYPE_CONFIG) { + bta_gattc_cfg_mtu_cmpl(p_clcb, &p_data->op_cmpl); + } + + if (p_clcb->auto_update == BTA_GATTC_DISC_WAITING) { + p_clcb->auto_update = BTA_GATTC_REQ_WAITING; + bta_gattc_sm_execute(p_clcb, BTA_GATTC_INT_DISCOVER_EVT, NULL); + } + } +} +/******************************************************************************* +** +** Function bta_gattc_op_cmpl +** +** Description operation completed. +** +** Returns None. +** +*******************************************************************************/ +void bta_gattc_ignore_op_cmpl(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data) +{ + UNUSED(p_clcb); + + /* receive op complete when discovery is started, ignore the response, + and wait for discovery finish and resent */ + APPL_TRACE_DEBUG("bta_gattc_ignore_op_cmpl op = %d", p_data->hdr.layer_specific); + +} +/******************************************************************************* +** +** Function bta_gattc_search +** +** Description start a search in the local server cache +** +** Returns None. +** +*******************************************************************************/ +void bta_gattc_search(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data) +{ + tBTA_GATT_STATUS status = GATT_INTERNAL_ERROR; + tBTA_GATTC cb_data; + APPL_TRACE_DEBUG("bta_gattc_search conn_id=%d", p_clcb->bta_conn_id); + if (p_clcb->p_srcb && p_clcb->p_srcb->p_srvc_cache) { + status = BTA_GATT_OK; + /* search the local cache of a server device */ + bta_gattc_search_service(p_clcb, p_data->api_search.p_srvc_uuid); + } + cb_data.search_cmpl.status = status; + cb_data.search_cmpl.conn_id = p_clcb->bta_conn_id; + cb_data.search_cmpl.searched_service_source = p_clcb->searched_service_source; + + /* end of search or no server cache available */ + ( *p_clcb->p_rcb->p_cback)(BTA_GATTC_SEARCH_CMPL_EVT, &cb_data); +} +/******************************************************************************* +** +** Function bta_gattc_q_cmd +** +** Description enqueue a command into control block, usually because discovery +** operation is busy. +** +** Returns None. +** +*******************************************************************************/ +void bta_gattc_q_cmd(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data) +{ + bta_gattc_enqueue(p_clcb, p_data); +} +/******************************************************************************* +** +** Function bta_gattc_pop_command_to_send +** +** Description dequeue a command into control block. +** Check if there has command pending in the command queue or not, +** if there has command pending in the command queue, sent it to the state machine to decision +** should be sent it to the remote device or not. +** +** Returns None. +** +*******************************************************************************/ +static void bta_gattc_pop_command_to_send(tBTA_GATTC_CLCB *p_clcb) +{ + if (!list_is_empty(p_clcb->p_cmd_list)) { + list_node_t *node = list_begin(p_clcb->p_cmd_list); + tBTA_GATTC_DATA *p_data = (tBTA_GATTC_DATA *)list_node(node); + if (p_data != NULL) { + /* execute pending operation of link block still present */ + if (l2cu_find_lcb_by_bd_addr(p_clcb->p_srcb->server_bda, BT_TRANSPORT_LE) != NULL) { + // The data to be sent to the gattc state machine for processing + if(bta_gattc_sm_execute(p_clcb, p_data->hdr.event, p_data)) { + list_remove(p_clcb->p_cmd_list, (void *)p_data); + } + + if (p_clcb->is_full) { + tBTA_GATTC cb_data = {0}; + p_clcb->is_full = FALSE; + cb_data.status = GATT_SUCCESS; + cb_data.queue_full.conn_id = p_clcb->bta_conn_id; + cb_data.queue_full.is_full = FALSE; + if (p_clcb->p_rcb->p_cback != NULL) { + ( *p_clcb->p_rcb->p_cback)(BTA_GATTC_QUEUE_FULL_EVT, (tBTA_GATTC *)&cb_data); + } + } + } + } + } +} +/******************************************************************************* +** +** Function bta_gattc_free_command_data +** +** Description free the command data into control block. +** +** Returns None. +** +*******************************************************************************/ +void bta_gattc_free_command_data(tBTA_GATTC_CLCB *p_clcb) +{ + assert(p_clcb->p_cmd_list); + //Check the list is empty or not. + if (!list_is_empty(p_clcb->p_cmd_list)) { + /* Traversal the command queue, check the p_q_cmd is point to the queue data or not, if the p_q_cmd point to the + command queue,should remove it from the list */ + for (list_node_t *node = list_begin(p_clcb->p_cmd_list); node != list_end(p_clcb->p_cmd_list); + node = list_next(node)) { + tBTA_GATTC_DATA *p_data = (tBTA_GATTC_DATA *)list_node(node); + if (p_data == p_clcb->p_q_cmd) { + list_remove(p_clcb->p_cmd_list, (void *)p_data); + p_clcb->p_q_cmd = NULL; + return; + } + } + + osi_free(p_clcb->p_q_cmd); + p_clcb->p_q_cmd = NULL; + } else { + osi_free(p_clcb->p_q_cmd); + p_clcb->p_q_cmd = NULL; + } +} + +/******************************************************************************* +** +** Function bta_gattc_fail +** +** Description report API call failure back to apps +** +** Returns None. +** +*******************************************************************************/ +void bta_gattc_fail(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data) +{ + UNUSED(p_data); + + if (p_clcb->status == BTA_GATT_OK) { + APPL_TRACE_ERROR("operation not supported at current state [%d]", p_clcb->state); + } +} + +/******************************************************************************* +** +** Function bta_gattc_deregister_cmpl +** +** Description De-Register a GATT client application with BTA completed. +** +** Returns void +** +*******************************************************************************/ +static void bta_gattc_deregister_cmpl(tBTA_GATTC_RCB *p_clreg) +{ + tBTA_GATTC_CB *p_cb = &bta_gattc_cb; + tBTA_GATTC_IF client_if = p_clreg->client_if; + tBTA_GATTC cb_data; + tBTA_GATTC_CBACK *p_cback = p_clreg->p_cback; + + memset(&cb_data, 0, sizeof(tBTA_GATTC)); + + GATT_Deregister(p_clreg->client_if); + memset(p_clreg, 0, sizeof(tBTA_GATTC_RCB)); + + cb_data.reg_oper.client_if = client_if; + cb_data.reg_oper.status = BTA_GATT_OK; + + if (p_cback) + /* callback with de-register event */ + { + (*p_cback)(BTA_GATTC_DEREG_EVT, (tBTA_GATTC *)&cb_data); + } + + if (bta_gattc_num_reg_app() == 0 && p_cb->state == BTA_GATTC_STATE_DISABLING) { + p_cb->state = BTA_GATTC_STATE_DISABLED; + } +} + +/******************************************************************************* +** +** Function bta_gattc_conn_cback +** +** Description callback functions to GATT client stack. +** +** Returns void +** +*******************************************************************************/ +static void bta_gattc_conn_cback(tGATT_IF gattc_if, BD_ADDR bda, UINT16 conn_id, + BOOLEAN connected, tGATT_DISCONN_REASON reason, + tBT_TRANSPORT transport) +{ + tBTA_GATTC_DATA *p_buf; + + if (reason != 0) { + APPL_TRACE_WARNING("gattc_conn_cb: if=%d st=%d id=%d rsn=0x%x", gattc_if, connected, conn_id, reason); + } + + bt_bdaddr_t bdaddr; + bdcpy(bdaddr.address, bda); + + if ((p_buf = (tBTA_GATTC_DATA *) osi_malloc(sizeof(tBTA_GATTC_DATA))) != NULL) { + memset(p_buf, 0, sizeof(tBTA_GATTC_DATA)); + + p_buf->int_conn.hdr.event = connected ? BTA_GATTC_INT_CONN_EVT : + BTA_GATTC_INT_DISCONN_EVT; + if(p_buf->int_conn.hdr.event == BTA_GATTC_INT_CONN_EVT) { + tL2C_LCB *p_lcb = l2cu_find_lcb_by_bd_addr(bda, BT_TRANSPORT_LE); + if(p_lcb != NULL) { + p_buf->int_conn.conn_params.interval = p_lcb->current_used_conn_interval; + p_buf->int_conn.conn_params.latency = p_lcb->current_used_conn_latency; + p_buf->int_conn.conn_params.timeout = p_lcb->current_used_conn_timeout; + #if (BLE_INCLUDED == TRUE) + p_buf->int_conn.ble_addr_type = p_lcb->ble_addr_type; + #endif + p_buf->int_conn.conn_handle = p_lcb->handle; + + } else { + APPL_TRACE_WARNING("gattc_conn_cb: conn params not found"); + } + } + p_buf->int_conn.hdr.layer_specific = conn_id; + p_buf->int_conn.client_if = gattc_if; + p_buf->int_conn.role = L2CA_GetBleConnRole(bda); + p_buf->int_conn.reason = reason; + p_buf->int_conn.transport = transport; + bdcpy(p_buf->int_conn.remote_bda, bda); + + bta_sys_sendmsg(p_buf); + } +} + +/******************************************************************************* +** +** Function bta_gattc_enc_cmpl_cback +** +** Description encryption complete callback function to GATT client stack. +** +** Returns void +** +*******************************************************************************/ +static void bta_gattc_enc_cmpl_cback(tGATT_IF gattc_if, BD_ADDR bda) +{ + tBTA_GATTC_DATA *p_buf; + tBTA_GATTC_CLCB *p_clcb = NULL; + + if ((p_clcb = bta_gattc_find_clcb_by_cif(gattc_if, bda, BTA_GATT_TRANSPORT_LE)) == NULL) { + return; + } + +#if (defined BTA_HH_LE_INCLUDED && BTA_HH_LE_INCLUDED == TRUE) + /* filter this event just for BTA HH LE GATT client, + In the future, if we want to enable encryption complete event + for all GATT clients, we can remove this code */ + if (!bta_hh_le_is_hh_gatt_if(gattc_if)) { + return; + } +#endif + + APPL_TRACE_DEBUG("bta_gattc_enc_cmpl_cback: cif = %d", gattc_if); + + if ((p_buf = (tBTA_GATTC_DATA *) osi_calloc(sizeof(tBTA_GATTC_DATA))) != NULL) { + memset(p_buf, 0, sizeof(tBTA_GATTC_DATA)); + + p_buf->enc_cmpl.hdr.event = BTA_GATTC_ENC_CMPL_EVT; + p_buf->enc_cmpl.hdr.layer_specific = p_clcb->bta_conn_id; + p_buf->enc_cmpl.client_if = gattc_if; + bdcpy(p_buf->enc_cmpl.remote_bda, bda); + + bta_sys_sendmsg(p_buf); + } +} + +/******************************************************************************* +** +** Function bta_gattc_process_api_refresh +** +** Description process refresh API to delete cache and start a new discovery +** if currently connected. +** +** Returns None. +** +*******************************************************************************/ +void bta_gattc_process_api_refresh(tBTA_GATTC_CB *p_cb, tBTA_GATTC_DATA *p_msg) +{ + tBTA_GATTC_SERV *p_srvc_cb = bta_gattc_find_srvr_cache(p_msg->api_conn.remote_bda); + tBTA_GATTC_CLCB *p_clcb = &bta_gattc_cb.clcb[0]; + BOOLEAN found = FALSE; + UINT8 i; + UNUSED(p_cb); + + if (p_srvc_cb != NULL) { + /* try to find a CLCB */ + if (p_srvc_cb->connected && p_srvc_cb->num_clcb != 0) { + for (i = 0; i < BTA_GATTC_CLCB_MAX; i ++, p_clcb ++) { + if (p_clcb->in_use && p_clcb->p_srcb == p_srvc_cb) { + found = TRUE; + break; + } + } + if (found) { + // If the device is discovering services, return + if(p_clcb->state == BTA_GATTC_CONN_ST) { + bta_gattc_sm_execute(p_clcb, BTA_GATTC_INT_DISCOVER_EVT, NULL); + } + return; + } + } + /* in all other cases, mark it and delete the cache */ + if (p_srvc_cb->p_srvc_cache != NULL) { + list_free(p_srvc_cb->p_srvc_cache); + p_srvc_cb->p_srvc_cache = NULL; + } + } +} + +void bta_gattc_process_api_cache_assoc(tBTA_GATTC_CB *p_cb, tBTA_GATTC_DATA *p_msg) +{ + tBTA_GATTC gattc_cb = {0}; + gattc_cb.set_assoc.client_if = p_msg->api_assoc.client_if; + BOOLEAN state = FALSE; + tBTA_GATTC_CLCB *p_assoc_clcb = bta_gattc_find_clcb_by_cif(p_msg->api_assoc.client_if, + p_msg->api_assoc.assoc_addr, BTA_TRANSPORT_LE); + tBTA_GATTC_RCB *p_clrcb = bta_gattc_cl_get_regcb(p_msg->api_assoc.client_if); + if (p_assoc_clcb != NULL) { + if (p_assoc_clcb->state == BTA_GATTC_CONN_ST || p_assoc_clcb->state == BTA_GATTC_DISCOVER_ST) { + gattc_cb.set_assoc.status = BTA_GATT_BUSY; + if (p_clrcb != NULL) { + (*p_clrcb->p_cback)(BTA_GATTC_ASSOC_EVT, &gattc_cb); + return; + } + } + } + + if (p_msg->api_assoc.is_assoc) { + if ((state = bta_gattc_co_cache_append_assoc_addr(p_msg->api_assoc.src_addr, p_msg->api_assoc.assoc_addr)) == TRUE) { + gattc_cb.set_assoc.status = BTA_GATT_OK; + + } else { + gattc_cb.set_assoc.status = BTA_GATT_ERROR; + if (p_clrcb != NULL) { + (*p_clrcb->p_cback)(BTA_GATTC_ASSOC_EVT, &gattc_cb); + return; + } + } + } else { + if (( state = bta_gattc_co_cache_remove_assoc_addr(p_msg->api_assoc.src_addr, p_msg->api_assoc.assoc_addr)) == TRUE) { + gattc_cb.set_assoc.status = BTA_GATT_OK; + } else { + gattc_cb.set_assoc.status = BTA_GATT_ERROR; + if (p_clrcb != NULL) { + (*p_clrcb->p_cback)(BTA_GATTC_ASSOC_EVT, &gattc_cb); + return; + } + } + } + + if (p_clrcb != NULL) { + (*p_clrcb->p_cback)(BTA_GATTC_ASSOC_EVT, &gattc_cb); + } + + return; + +} +void bta_gattc_process_api_cache_get_addr_list(tBTA_GATTC_CB *p_cb, tBTA_GATTC_DATA *p_msg) +{ + tBTA_GATTC gattc_cb = {0}; + tBTA_GATTC_RCB *p_clrcb = bta_gattc_cl_get_regcb(p_msg->api_get_addr.client_if); + UINT8 num_addr = bta_gattc_co_get_addr_num(); + gattc_cb.get_addr_list.client_if = p_msg->api_get_addr.client_if; + + if (num_addr != 0) { + gattc_cb.get_addr_list.num_addr = num_addr; + gattc_cb.get_addr_list.bda_list = (BD_ADDR *)osi_malloc(sizeof(BD_ADDR)*num_addr); + if (gattc_cb.get_addr_list.bda_list != NULL) { + bta_gattc_co_get_addr_list(gattc_cb.get_addr_list.bda_list); + gattc_cb.get_addr_list.status = BTA_GATT_OK; + } else { + gattc_cb.get_addr_list.status = BTA_GATT_ERROR; + } + } else { + gattc_cb.get_addr_list.status = BTA_GATT_NOT_FOUND; + } + + if (p_clrcb != NULL) { + (* p_clrcb->p_cback)(BTA_GATTC_GET_ADDR_LIST_EVT, &gattc_cb); + } + + //release the address list buffer after used. + if (gattc_cb.get_addr_list.bda_list != NULL) { + osi_free((void *)gattc_cb.get_addr_list.bda_list); + } + +} + +/******************************************************************************* +** +** Function bta_gattc_process_api_cache_clean +** +** Description process cache clean API to delete cache +** +** Returns None. +** +*******************************************************************************/ +void bta_gattc_process_api_cache_clean(tBTA_GATTC_CB *p_cb, tBTA_GATTC_DATA *p_msg) +{ + tBTA_GATTC_SERV *p_srvc_cb = bta_gattc_find_srvr_cache(p_msg->api_conn.remote_bda); + UNUSED(p_cb); + + if (p_srvc_cb != NULL && p_srvc_cb->p_srvc_cache != NULL) { + //mark it and delete the cache */ + list_free(p_srvc_cb->p_srvc_cache); + p_srvc_cb->p_srvc_cache = NULL; + } +} + +/******************************************************************************* +** +** Function bta_gattc_process_srvc_chg_ind +** +** Description process service change indication. +** +** Returns None. +** +*******************************************************************************/ +BOOLEAN bta_gattc_process_srvc_chg_ind(UINT16 conn_id, + tBTA_GATTC_RCB *p_clrcb, + tBTA_GATTC_SERV *p_srcb, + tBTA_GATTC_CLCB *p_clcb, + tBTA_GATTC_NOTIFY *p_notify, + tGATT_VALUE *att_value) +{ + tBT_UUID gattp_uuid, srvc_chg_uuid; + BOOLEAN processed = FALSE; + UINT8 i; + + gattp_uuid.len = 2; + gattp_uuid.uu.uuid16 = UUID_SERVCLASS_GATT_SERVER; + + srvc_chg_uuid.len = 2; + srvc_chg_uuid.uu.uuid16 = GATT_UUID_GATT_SRV_CHGD; + + const tBTA_GATTC_CHARACTERISTIC *p_char = bta_gattc_get_characteristic_srcb(p_srcb, p_notify->handle); + if (p_char && bta_gattc_uuid_compare(&p_char->service->uuid, &gattp_uuid, TRUE) && + bta_gattc_uuid_compare(&p_char->uuid, &srvc_chg_uuid, TRUE)) { + if (att_value->len != BTA_GATTC_SERVICE_CHANGED_LEN) { + APPL_TRACE_ERROR("%s: received malformed service changed indication, skipping", __func__); + return FALSE; + } + + UINT8 *p = att_value->value; + UINT16 s_handle = ((UINT16)(*(p )) + (((UINT16)(*(p + 1))) << 8)); + UINT16 e_handle = ((UINT16)(*(p + 2)) + (((UINT16)(*(p + 3))) << 8)); + + APPL_TRACE_DEBUG("%s: service changed s_handle:0x%04x e_handle:0x%04x", + __func__, s_handle, e_handle); + + processed = TRUE; + /* mark service handle change pending */ + p_srcb->srvc_hdl_chg = TRUE; + /* clear up all notification/indication registration */ + bta_gattc_clear_notif_registration(p_srcb, conn_id, s_handle, e_handle); + /* service change indication all received, do discovery update */ + if ( ++ p_srcb->update_count == bta_gattc_num_reg_app()) { + /* not an opened connection; or connection busy */ + /* search for first available clcb and start discovery */ + if (p_clcb == NULL || (p_clcb && p_clcb->p_q_cmd != NULL)) { + for (i = 0 ; i < BTA_GATTC_CLCB_MAX; i ++) { + if (bta_gattc_cb.clcb[i].in_use && + bta_gattc_cb.clcb[i].p_srcb == p_srcb && + bta_gattc_cb.clcb[i].p_q_cmd == NULL) { + p_clcb = &bta_gattc_cb.clcb[i]; + break; + } + } + } + /* send confirmation here if this is an indication, it should always be */ + GATTC_SendHandleValueConfirm(conn_id, att_value->handle); + + /* if connection available, refresh cache by doing discovery now */ + if (p_clcb != NULL) { + tBTA_GATTC_CONN *p_conn = bta_gattc_conn_find(p_clcb->bda); + if(p_conn) { + p_conn->write_remote_svc_change_ccc_done = FALSE; + } + bta_gattc_sm_execute(p_clcb, BTA_GATTC_INT_DISCOVER_EVT, NULL); + } + } + /* notify applicationf or service change */ + if (p_clrcb->p_cback != NULL) { + tBTA_GATTC_SERVICE_CHANGE srvc_chg= {0}; + memcpy(srvc_chg.remote_bda, p_srcb->server_bda, sizeof(BD_ADDR)); + srvc_chg.conn_id = conn_id; + (* p_clrcb->p_cback)(BTA_GATTC_SRVC_CHG_EVT, (tBTA_GATTC *)&srvc_chg); + } + + } + + return processed; + +} +/******************************************************************************* +** +** Function bta_gattc_proc_other_indication +** +** Description process all non-service change indication/notification. +** +** Returns None. +** +*******************************************************************************/ +void bta_gattc_proc_other_indication(tBTA_GATTC_CLCB *p_clcb, UINT8 op, + tGATT_CL_COMPLETE *p_data, + tBTA_GATTC_NOTIFY *p_notify) +{ + APPL_TRACE_DEBUG("bta_gattc_proc_other_indication check p_data->att_value.handle=%d p_data->handle=%d", + p_data->att_value.handle, p_data->handle); + APPL_TRACE_DEBUG("is_notify %d", p_notify->is_notify); + + p_notify->is_notify = (op == GATTC_OPTYPE_INDICATION) ? FALSE : TRUE; + p_notify->len = p_data->att_value.len; + bdcpy(p_notify->bda, p_clcb->bda); + memcpy(p_notify->value, p_data->att_value.value, p_data->att_value.len); + p_notify->conn_id = p_clcb->bta_conn_id; + + if (p_clcb->p_rcb->p_cback) { + (*p_clcb->p_rcb->p_cback)(BTA_GATTC_NOTIF_EVT, (tBTA_GATTC *)p_notify); + } + +} +/******************************************************************************* +** +** Function bta_gattc_process_indicate +** +** Description process indication/notification. +** +** Returns None. +** +*******************************************************************************/ +void bta_gattc_process_indicate(UINT16 conn_id, tGATTC_OPTYPE op, tGATT_CL_COMPLETE *p_data) +{ + UINT16 handle = p_data->att_value.handle; + tBTA_GATTC_CLCB *p_clcb ; + tBTA_GATTC_RCB *p_clrcb = NULL; + tBTA_GATTC_SERV *p_srcb = NULL; + tBTA_GATTC_NOTIFY notify; + BD_ADDR remote_bda; + tBTA_GATTC_IF gatt_if; + tBTA_TRANSPORT transport; + + if (!GATT_GetConnectionInfor(conn_id, &gatt_if, remote_bda, &transport)) { + APPL_TRACE_ERROR("%s indication/notif for unknown app", __func__); + if (op == GATTC_OPTYPE_INDICATION) { + GATTC_SendHandleValueConfirm(conn_id, handle); + } + return; + } + + if ((p_clrcb = bta_gattc_cl_get_regcb(gatt_if)) == NULL) { + APPL_TRACE_ERROR("%s indication/notif for unregistered app", __func__); + if (op == GATTC_OPTYPE_INDICATION) { + GATTC_SendHandleValueConfirm(conn_id, handle); + } + return; + } + + if ((p_srcb = bta_gattc_find_srcb(remote_bda)) == NULL) { + APPL_TRACE_ERROR("%s indication/notif for unknown device, ignore", __func__); + if (op == GATTC_OPTYPE_INDICATION) { + GATTC_SendHandleValueConfirm(conn_id, handle); + } + return; + } + + p_clcb = bta_gattc_find_clcb_by_conn_id(conn_id); + + notify.handle = handle; + /* if non-service change indication/notification, forward to application */ + if (!bta_gattc_process_srvc_chg_ind(conn_id, p_clrcb, p_srcb, p_clcb, ¬ify, &p_data->att_value)) { + /* if app registered for the notification */ + if (bta_gattc_check_notif_registry(p_clrcb, p_srcb, ¬ify)) { + /* connection not open yet */ + if (p_clcb == NULL) { + p_clcb = bta_gattc_clcb_alloc(gatt_if, remote_bda, transport); + + if (p_clcb == NULL) { + APPL_TRACE_ERROR("No resources"); + return; + } + + p_clcb->bta_conn_id = conn_id; + p_clcb->transport = transport; + + bta_gattc_sm_execute(p_clcb, BTA_GATTC_INT_CONN_EVT, NULL); + } + + if (p_clcb != NULL) { + bta_gattc_proc_other_indication(p_clcb, op, p_data, ¬ify); + } + } else if (op == GATTC_OPTYPE_INDICATION) { + /* no one intersted and need ack? */ + APPL_TRACE_DEBUG("%s no one interested, ack now", __func__); + GATTC_SendHandleValueConfirm(conn_id, handle); + } + } +} +/******************************************************************************* +** +** Function bta_gattc_cmpl_cback +** +** Description client operation complete callback register with BTE GATT. +** +** Returns None. +** +*******************************************************************************/ +static void bta_gattc_cmpl_cback(UINT16 conn_id, tGATTC_OPTYPE op, tGATT_STATUS status, + tGATT_CL_COMPLETE *p_data) +{ + tBTA_GATTC_CLCB *p_clcb; + APPL_TRACE_DEBUG("bta_gattc_cmpl_cback: conn_id = %d op = %d status = %d", + conn_id, op, status); + + /* notification and indication processed right away */ + if (op == GATTC_OPTYPE_NOTIFICATION || op == GATTC_OPTYPE_INDICATION) { + bta_gattc_process_indicate(conn_id, op, p_data); + return; + } + /* for all other operation, not expected if w/o connection */ + else if ((p_clcb = bta_gattc_find_clcb_by_conn_id(conn_id)) == NULL) { + APPL_TRACE_ERROR("bta_gattc_cmpl_cback unknown conn_id = %d, ignore data", conn_id); + return; + } + + /* if over BR_EDR, inform PM for mode change */ + if (p_clcb->transport == BTA_TRANSPORT_BR_EDR) { + bta_sys_busy(BTA_ID_GATTC, BTA_ALL_APP_ID, p_clcb->bda); + bta_sys_idle(BTA_ID_GATTC, BTA_ALL_APP_ID, p_clcb->bda); + } + + bta_gattc_cmpl_sendmsg(conn_id, op, status, p_data); +} + +/******************************************************************************* +** +** Function bta_gattc_cmpl_sendmsg +** +** Description client operation complete send message +** +** Returns None. +** +*******************************************************************************/ +static void bta_gattc_cmpl_sendmsg(UINT16 conn_id, tGATTC_OPTYPE op, + tBTA_GATT_STATUS status, + tGATT_CL_COMPLETE *p_data) +{ + const UINT16 len = sizeof(tBTA_GATTC_OP_CMPL) + sizeof(tGATT_CL_COMPLETE); + tBTA_GATTC_OP_CMPL *p_buf = (tBTA_GATTC_OP_CMPL *) osi_malloc(len); + + if (p_buf != NULL) { + memset(p_buf, 0, len); + p_buf->hdr.event = BTA_GATTC_OP_CMPL_EVT; + p_buf->hdr.layer_specific = conn_id; + p_buf->status = status; + p_buf->op_code = op; + + if (p_data != NULL) { + p_buf->p_cmpl = (tGATT_CL_COMPLETE *)(p_buf + 1); + memcpy(p_buf->p_cmpl, p_data, sizeof(tGATT_CL_COMPLETE)); + } + + bta_sys_sendmsg(p_buf); + } +} + +/******************************************************************************* +** +** Function bta_gattc_cong_cback +** +** Description congestion callback for BTA GATT client. +** +** Returns void +** +********************************************************************************/ +static void bta_gattc_cong_cback (UINT16 conn_id, BOOLEAN congested) +{ + tBTA_GATTC cb_data; + cb_data.congest.conn_id = conn_id; + cb_data.congest.congested = congested; + btc_gattc_congest_callback(&cb_data); +} + +/******************************************************************************* +** +** Function bta_gattc_req_cback +** +** Description GATT request command callback for BTA GATT client. +** +** Returns void +** +********************************************************************************/ +static void bta_gattc_req_cback (UINT16 conn_id, UINT32 trans_id, tGATTS_REQ_TYPE type, tGATTS_DATA *p_data) +{ + /* GATTC doesn't need to process the GATT request commands. + * Add this callback here to avoid the warning "Call back not found for application" + * printed in the function gatt_sr_send_req_callback + * */ + UNUSED (conn_id); + UNUSED (trans_id) ; + UNUSED (type); + UNUSED (p_data); +} + +#if BLE_INCLUDED == TRUE +/******************************************************************************* +** +** Function bta_gattc_init_clcb_conn +** +** Description Initaite a BTA CLCB connection +** +** Returns void +** +********************************************************************************/ +void bta_gattc_init_clcb_conn(UINT8 cif, BD_ADDR remote_bda) +{ + tBTA_GATTC_CLCB *p_clcb = NULL; + tBTA_GATTC_DATA gattc_data; + UINT16 conn_id; + + /* should always get the connection ID */ + if (GATT_GetConnIdIfConnected(cif, remote_bda, &conn_id, BTA_GATT_TRANSPORT_LE) == FALSE) { + APPL_TRACE_ERROR("bta_gattc_init_clcb_conn ERROR: not a connected device"); + return; + } + + /* initaite a new connection here */ + if ((p_clcb = bta_gattc_clcb_alloc(cif, remote_bda, BTA_GATT_TRANSPORT_LE)) != NULL) { + gattc_data.hdr.layer_specific = p_clcb->bta_conn_id = conn_id; + + gattc_data.api_conn.client_if = cif; + memcpy(gattc_data.api_conn.remote_bda, remote_bda, BD_ADDR_LEN); + gattc_data.api_conn.is_direct = TRUE; + + bta_gattc_sm_execute(p_clcb, BTA_GATTC_API_OPEN_EVT, &gattc_data); + } else { + APPL_TRACE_ERROR("No resources"); + } +} +/******************************************************************************* +** +** Function bta_gattc_process_listen_all +** +** Description process listen all, send open callback to application for all +** connected slave LE link. +** +** Returns void +** +********************************************************************************/ +void bta_gattc_process_listen_all(UINT8 cif) +{ + UINT8 i_conn = 0; + tBTA_GATTC_CONN *p_conn = &bta_gattc_cb.conn_track[0]; + + for (i_conn = 0; i_conn < BTA_GATTC_CONN_MAX; i_conn++, p_conn ++) { + if (p_conn->in_use ) { + if (bta_gattc_find_clcb_by_cif(cif, p_conn->remote_bda, BTA_GATT_TRANSPORT_LE) == NULL) { + bta_gattc_init_clcb_conn(cif, p_conn->remote_bda); + } + /* else already connected */ + } + } +} +/******************************************************************************* +** +** Function bta_gattc_listen +** +** Description Start or stop a listen for connection +** +** Returns void +** +********************************************************************************/ +void bta_gattc_listen(tBTA_GATTC_CB *p_cb, tBTA_GATTC_DATA *p_msg) +{ + tBTA_GATTC_RCB *p_clreg = bta_gattc_cl_get_regcb(p_msg->api_listen.client_if); + tBTA_GATTC cb_data; + UNUSED(p_cb); + + cb_data.reg_oper.status = BTA_GATT_ERROR; + cb_data.reg_oper.client_if = p_msg->api_listen.client_if; + + if (p_clreg == NULL) { + APPL_TRACE_ERROR("bta_gattc_listen failed, unknown client_if: %d", + p_msg->api_listen.client_if); + return; + } + /* mark bg conn record */ + if (bta_gattc_mark_bg_conn(p_msg->api_listen.client_if, + (BD_ADDR_PTR) p_msg->api_listen.remote_bda, + p_msg->api_listen.start, + TRUE)) { + if (!GATT_Listen(p_msg->api_listen.client_if, + p_msg->api_listen.start, + p_msg->api_listen.remote_bda)) { + APPL_TRACE_ERROR("Listen failure"); + (*p_clreg->p_cback)(BTA_GATTC_LISTEN_EVT, &cb_data); + } else { + cb_data.status = BTA_GATT_OK; + + (*p_clreg->p_cback)(BTA_GATTC_LISTEN_EVT, &cb_data); + + if (p_msg->api_listen.start) { + /* if listen to a specific target */ + if (p_msg->api_listen.remote_bda != NULL) { + + /* if is a connected remote device */ + if (L2CA_GetBleConnRole(p_msg->api_listen.remote_bda) == HCI_ROLE_SLAVE && + bta_gattc_find_clcb_by_cif(p_msg->api_listen.client_if, + p_msg->api_listen.remote_bda, + BTA_GATT_TRANSPORT_LE) == NULL) { + + bta_gattc_init_clcb_conn(p_msg->api_listen.client_if, + p_msg->api_listen.remote_bda); + } + } + /* if listen to all */ + else { + APPL_TRACE_DEBUG("Listen For All now"); + /* go through all connected device and send + callback for all connected slave connection */ + bta_gattc_process_listen_all(p_msg->api_listen.client_if); + } + } + } + } +} + +/******************************************************************************* +** +** Function bta_gattc_broadcast +** +** Description Start or stop broadcasting +** +** Returns void +** +********************************************************************************/ +void bta_gattc_broadcast(tBTA_GATTC_CB *p_cb, tBTA_GATTC_DATA *p_msg) +{ + tBTA_GATTC_RCB *p_clreg = bta_gattc_cl_get_regcb(p_msg->api_listen.client_if); + tBTA_GATTC cb_data; + UNUSED(p_cb); + + cb_data.reg_oper.client_if = p_msg->api_listen.client_if; + cb_data.reg_oper.status = BTM_BleBroadcast(p_msg->api_listen.start, NULL); + //TODO need modify callback if used + if (p_clreg && p_clreg->p_cback) { + (*p_clreg->p_cback)(BTA_GATTC_LISTEN_EVT, &cb_data); + } +} + +/******************************************************************************* +** +** Function bta_gattc_register_service_change_notify +** +** Description Find remote device's gatt service change characteristic ccc's handle and write 2 to this +** this ccc. +** +** Returns Return result of service change ccc service discovery result +** +*******************************************************************************/ +tBTA_GATTC_FIND_SERVICE_CB bta_gattc_register_service_change_notify(UINT16 conn_id, BD_ADDR remote_bda) +{ + tBTA_GATTC_SERV *p_srcb = NULL; + list_t *p_cache = NULL; + tBTA_GATTC_SERVICE *p_service = NULL; + tBTA_GATTC_CHARACTERISTIC *p_char = NULL; + tBTA_GATTC_DESCRIPTOR *p_desc = NULL; + tBTA_GATTC_FIND_SERVICE_CB result; + BOOLEAN gatt_cache_found = FALSE; + BOOLEAN gatt_service_found = FALSE; + BOOLEAN gatt_service_change_found = FALSE; + BOOLEAN gatt_ccc_found = FALSE; + + tBT_UUID gatt_service_uuid = {LEN_UUID_16, {UUID_SERVCLASS_GATT_SERVER}}; + tBT_UUID gatt_service_change_uuid = {LEN_UUID_16, {GATT_UUID_GATT_SRV_CHGD}}; + tBT_UUID gatt_ccc_uuid = {LEN_UUID_16, {GATT_UUID_CHAR_CLIENT_CONFIG}}; + tBTA_GATTC_CONN *p_conn = bta_gattc_conn_find_alloc(remote_bda); + if(p_conn && p_conn->write_remote_svc_change_ccc_done) { + return SERVICE_CHANGE_CCC_WRITTEN_SUCCESS; + } + + p_srcb = bta_gattc_find_srcb(remote_bda); + if ((p_srcb != NULL) && (p_srcb->p_srvc_cache != NULL)) { + p_cache = p_srcb->p_srvc_cache; + gatt_cache_found = TRUE; + } + else { + result = SERVICE_CHANGE_CACHE_NOT_FOUND; + } + /* start to find gatt service */ + if (gatt_cache_found == TRUE) { + for (list_node_t *sn = list_begin(p_cache); + sn != list_end(p_cache); sn = list_next(sn)) { + p_service = list_node(sn); + if (bta_gattc_uuid_compare(&gatt_service_uuid, &p_service->uuid, TRUE)) { + gatt_service_found = TRUE; + break; + } + } + } + else { + result = SERVICE_CHANGE_CACHE_NOT_FOUND; + } + + /* start to find gatt service change characteristic */ + if (gatt_service_found == TRUE) { + if (p_service->characteristics) { + for (list_node_t *cn = list_begin(p_service->characteristics); + cn != list_end(p_service->characteristics); cn = list_next(cn)) { + p_char = list_node(cn); + if (bta_gattc_uuid_compare(&gatt_service_change_uuid, &p_char->uuid, TRUE)) { + gatt_service_change_found = TRUE; + break; + } + } + } + } + else if (gatt_cache_found == TRUE) { + /* Gatt service not found, start a timer to wait for service discovery */ + result = SERVICE_CHANGE_SERVICE_NOT_FOUND; + } + /* start to find gatt service change characteristic ccc */ + if (gatt_service_change_found == TRUE) { + if (p_char->descriptors) { + for (list_node_t *dn = list_begin(p_char->descriptors); + dn != list_end(p_char->descriptors); dn = list_next(dn)) { + p_desc = list_node(dn); + if (bta_gattc_uuid_compare(&gatt_ccc_uuid, &p_desc->uuid, TRUE)) { + gatt_ccc_found = TRUE; + break; + } + } + } + } + else if (gatt_service_found ==TRUE) { + /* Gatt service found, but service change char not found, + * Case1: remote device doesn't have service change char, we don't need to start a timer here to + * wait for service discovery + * Case2: remote device exist service change char, we have found gatt service, but have not found + * service change char, we need to start a timer here*/ + result = SERVICE_CHANGE_CHAR_NOT_FOUND; + } + + if (gatt_ccc_found == TRUE){ + if (p_conn) { + p_conn->svc_change_descr_handle = p_desc->handle; + p_conn->write_remote_svc_change_ccc_done = TRUE; + } + result = SERVICE_CHANGE_CCC_WRITTEN_SUCCESS; + uint16_t indicate_value = GATT_CLT_CONFIG_INDICATION; + tBTA_GATT_UNFMT indicate_v; + indicate_v.len = 2; + indicate_v.p_value = (uint8_t *)&indicate_value; + BTA_GATTC_WriteCharDescr (conn_id, p_desc->handle, BTA_GATTC_TYPE_WRITE, &indicate_v, BTA_GATT_AUTH_REQ_NONE); + + } + else if (gatt_service_change_found == TRUE) { + /* Gatt service char found, but service change char ccc not found, + * Case1: remote device doesn't have service change char ccc, we don't need to start a timer here to + * wait for service discovery + * Case2: remote device exist service change char ccc, we have found gatt service change char, but have not found + * service change char ccc, we need to start a timer here */ + result = SERVICE_CHANGE_CCC_NOT_FOUND; + } + + return result; +} + +#endif +#endif ///GATTC_INCLUDED == TRUE && BLE_INCLUDED == TRUE diff --git a/lib/bt/host/bluedroid/bta/gatt/bta_gattc_api.c b/lib/bt/host/bluedroid/bta/gatt/bta_gattc_api.c new file mode 100644 index 00000000..6e2586d6 --- /dev/null +++ b/lib/bt/host/bluedroid/bta/gatt/bta_gattc_api.c @@ -0,0 +1,1221 @@ +/****************************************************************************** + * + * Copyright (C) 2010-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This is the implementation of the API for GATT module of BTA. + * + ******************************************************************************/ + +#include "common/bt_target.h" +#include "osi/allocator.h" + +#if defined(GATTC_INCLUDED) && (GATTC_INCLUDED == TRUE) + +#include +#include "bta/bta_sys.h" +#include "bta/bta_gatt_api.h" +#include "bta_gattc_int.h" +#include "stack/l2c_api.h" +/***************************************************************************** +** Constants +*****************************************************************************/ + +static const tBTA_SYS_REG bta_gattc_reg = { + bta_gattc_hdl_event, + BTA_GATTC_Disable +}; + + +/******************************************************************************* +** +** Function BTA_GATTC_Disable +** +** Description This function is called to disable GATTC module +** +** Parameters None. +** +** Returns None +** +*******************************************************************************/ +void BTA_GATTC_Disable(void) +{ + BT_HDR *p_buf; + + if (bta_sys_is_register(BTA_ID_GATTC) == FALSE) { + APPL_TRACE_WARNING("GATTC Module not enabled/already disabled\n"); + return; + } + if ((p_buf = (BT_HDR *) osi_malloc(sizeof(BT_HDR))) != NULL) { + p_buf->event = BTA_GATTC_API_DISABLE_EVT; + bta_sys_sendmsg(p_buf); + } + bta_sys_deregister(BTA_ID_GATTC); + +} + +/******************************************************************************* +** +** Function BTA_GATTC_AppRegister +** +** Description This function is called to register application callbacks +** with BTA GATTC module. +** +** Parameters p_app_uuid - application UUID +** p_client_cb - pointer to the application callback function. +** +** Returns None +** +*******************************************************************************/ +void BTA_GATTC_AppRegister(tBT_UUID *p_app_uuid, tBTA_GATTC_CBACK *p_client_cb) +{ + tBTA_GATTC_API_REG *p_buf; + + if (bta_sys_is_register(BTA_ID_GATTC) == FALSE) { + bta_sys_register(BTA_ID_GATTC, &bta_gattc_reg); + } + + if ((p_buf = (tBTA_GATTC_API_REG *) osi_malloc(sizeof(tBTA_GATTC_API_REG))) != NULL) { + p_buf->hdr.event = BTA_GATTC_API_REG_EVT; + if (p_app_uuid != NULL) { + memcpy(&p_buf->app_uuid, p_app_uuid, sizeof(tBT_UUID)); + } + p_buf->p_cback = p_client_cb; + + bta_sys_sendmsg(p_buf); + } + return; +} + +/******************************************************************************* +** +** Function BTA_GATTC_AppDeregister +** +** Description This function is called to deregister an application +** from BTA GATTC module. +** +** Parameters client_if - client interface identifier. +** +** Returns None +** +*******************************************************************************/ +void BTA_GATTC_AppDeregister(tBTA_GATTC_IF client_if) +{ + tBTA_GATTC_API_DEREG *p_buf; + + if ((p_buf = (tBTA_GATTC_API_DEREG *) osi_malloc(sizeof(tBTA_GATTC_API_DEREG))) != NULL) { + p_buf->hdr.event = BTA_GATTC_API_DEREG_EVT; + p_buf->client_if = client_if; + bta_sys_sendmsg(p_buf); + } + return; +} + +/******************************************************************************* +** +** Function BTA_GATTC_Open +** +** Description Open a direct connection or add a background auto connection +** bd address +** +** Parameters client_if: server interface. +** remote_bda: remote device BD address. +** remote_addr_type: remote device BD address type. +** is_direct: direct connection or background auto connection +** transport: Transport to be used for GATT connection (BREDR/LE) +** +** Returns void +** +*******************************************************************************/ +void BTA_GATTC_Open(tBTA_GATTC_IF client_if, BD_ADDR remote_bda, tBTA_ADDR_TYPE remote_addr_type, + BOOLEAN is_direct, tBTA_GATT_TRANSPORT transport, BOOLEAN is_aux) +{ + tBTA_GATTC_API_OPEN *p_buf; + + if ((p_buf = (tBTA_GATTC_API_OPEN *) osi_malloc(sizeof(tBTA_GATTC_API_OPEN))) != NULL) { + p_buf->hdr.event = BTA_GATTC_API_OPEN_EVT; + + p_buf->client_if = client_if; + p_buf->is_direct = is_direct; + p_buf->transport = transport; + p_buf->is_aux = is_aux; + p_buf->remote_addr_type = remote_addr_type; + memcpy(p_buf->remote_bda, remote_bda, BD_ADDR_LEN); + + + bta_sys_sendmsg(p_buf); + } + return; +} + +/******************************************************************************* +** +** Function BTA_GATTC_CancelOpen +** +** Description Cancel a direct open connection or remove a background auto connection +** bd address +** +** Parameters client_if: server interface. +** remote_bda: remote device BD address. +** is_direct: direct connection or background auto connection +** +** Returns void +** +*******************************************************************************/ +void BTA_GATTC_CancelOpen(tBTA_GATTC_IF client_if, BD_ADDR remote_bda, BOOLEAN is_direct) +{ + //If the registration callback is NULL, return + if(bta_sys_is_register(BTA_ID_GATTC) == FALSE) { + return; + } + + tBTA_GATTC_API_CANCEL_OPEN *p_buf; + + if ((p_buf = (tBTA_GATTC_API_CANCEL_OPEN *) osi_malloc(sizeof(tBTA_GATTC_API_CANCEL_OPEN))) != NULL) { + p_buf->hdr.event = BTA_GATTC_API_CANCEL_OPEN_EVT; + + p_buf->client_if = client_if; + p_buf->is_direct = is_direct; + memcpy(p_buf->remote_bda, remote_bda, BD_ADDR_LEN); + + bta_sys_sendmsg(p_buf); + } + return; +} + +/******************************************************************************* +** +** Function BTA_GATTC_Close +** +** Description Close a connection to a GATT server. +** +** Parameters conn_id: connection ID to be closed. +** +** Returns void +** +*******************************************************************************/ +void BTA_GATTC_Close(UINT16 conn_id) +{ + BT_HDR *p_buf; + + if ((p_buf = (BT_HDR *) osi_malloc(sizeof(BT_HDR))) != NULL) { + p_buf->event = BTA_GATTC_API_CLOSE_EVT; + + p_buf->layer_specific = conn_id; + + bta_sys_sendmsg(p_buf); + } + return; + +} +/******************************************************************************* +** +** Function BTA_GATTC_ConfigureMTU +** +** Description Configure the MTU size in the GATT channel. This can be done +** only once per connection. +** +** Parameters conn_id: connection ID. +** mtu: desired MTU size to use. +** +** Returns void +** +*******************************************************************************/ +void BTA_GATTC_ConfigureMTU (UINT16 conn_id) +{ + tBTA_GATTC_API_CFG_MTU *p_buf; + + if ((p_buf = (tBTA_GATTC_API_CFG_MTU *) osi_malloc(sizeof(tBTA_GATTC_API_CFG_MTU))) != NULL) { + p_buf->hdr.event = BTA_GATTC_API_CFG_MTU_EVT; + p_buf->hdr.layer_specific = conn_id; + + bta_sys_sendmsg(p_buf); + } + return; +} +/******************************************************************************* +** +** Function BTA_GATTC_ServiceSearchRequest +** +** Description This function is called to request a GATT service discovery +** on a GATT server. This function report service search result +** by a callback event, and followed by a service search complete +** event. +** +** Parameters conn_id: connection ID. +** p_srvc_uuid: a UUID of the service application is interested in. +** If Null, discover for all services. +** +** Returns None +** +*******************************************************************************/ +void BTA_GATTC_ServiceSearchRequest (UINT16 conn_id, tBT_UUID *p_srvc_uuid) +{ + tBTA_GATTC_API_SEARCH *p_buf; + UINT16 len = sizeof(tBTA_GATTC_API_SEARCH) + sizeof(tBT_UUID); + + if ((p_buf = (tBTA_GATTC_API_SEARCH *) osi_malloc(len)) != NULL) { + memset(p_buf, 0, len); + + p_buf->hdr.event = BTA_GATTC_API_SEARCH_EVT; + p_buf->hdr.layer_specific = conn_id; + + if (p_srvc_uuid) { + p_buf->p_srvc_uuid = (tBT_UUID *)(p_buf + 1); + memcpy(p_buf->p_srvc_uuid, p_srvc_uuid, sizeof(tBT_UUID)); + } else { + p_buf->p_srvc_uuid = NULL; + } + + bta_sys_sendmsg(p_buf); + } + return; +} + + +/******************************************************************************* +** +** Function BTA_GATTC_GetServices +** +** Description This function is called to find the services on the given server. +** +** Parameters conn_id: connection ID which identify the server. +** +** Returns returns list_t of tBTA_GATTC_SERVICE or NULL. +** +*******************************************************************************/ +const list_t* BTA_GATTC_GetServices(UINT16 conn_id) +{ + return bta_gattc_get_services(conn_id); +} + +/******************************************************************************* +** +** Function BTA_GATTC_GetCharacteristic +** +** Description This function is called to find the characteristic on the given server. +** +** Parameters conn_id - connection ID which identify the server. +** handle - characteristic handle +** +** Returns returns pointer to tBTA_GATTC_CHARACTERISTIC or NULL. +** +*******************************************************************************/ +const tBTA_GATTC_CHARACTERISTIC* BTA_GATTC_GetCharacteristic(UINT16 conn_id, UINT16 handle) +{ + return bta_gattc_get_characteristic(conn_id, handle); +} + +/******************************************************************************* +** +** Function BTA_GATTC_GetDescriptor +** +** Description This function is called to find the characteristic on the given server. +** +** Parameters conn_id - connection ID which identify the server. +** handle - descriptor handle +** +** Returns returns pointer to tBTA_GATTC_DESCRIPTOR or NULL. +** +*******************************************************************************/ +const tBTA_GATTC_DESCRIPTOR* BTA_GATTC_GetDescriptor(UINT16 conn_id, UINT16 handle) +{ + return bta_gattc_get_descriptor(conn_id, handle); +} + +void BTA_GATTC_GetServiceWithUUID(UINT16 conn_id, tBT_UUID *svc_uuid, + btgatt_db_element_t **db, UINT16 *count) +{ + bta_gattc_get_service_with_uuid(conn_id, svc_uuid, db, count); +} + +void BTA_GATTC_GetAllChar(UINT16 conn_id, UINT16 start_handle, UINT16 end_handle, + btgatt_db_element_t **db, UINT16 *count) +{ + bta_gattc_get_db_with_opration(conn_id, + GATT_OP_GET_ALL_CHAR, + 0, + NULL, + NULL, + NULL, + start_handle, + end_handle, + db, + count); +} + +void BTA_GATTC_GetAllDescriptor(UINT16 conn_id, UINT16 char_handle, + btgatt_db_element_t **db, UINT16 *count) +{ + bta_gattc_get_db_with_opration(conn_id, + GATT_OP_GET_ALL_DESCRI, + char_handle, + NULL, + NULL, + NULL, + 0, + 0xFFFF, + db, + count); +} + +void BTA_GATTC_GetCharByUUID(UINT16 conn_id, UINT16 start_handle, UINT16 end_handle, tBT_UUID char_uuid, + btgatt_db_element_t **db, UINT16 *count) +{ + bta_gattc_get_db_with_opration(conn_id, + GATT_OP_GET_CHAR_BY_UUID, + 0, + NULL, + &char_uuid, + NULL, + start_handle, + end_handle, + db, + count); +} + +void BTA_GATTC_GetDescrByUUID(UINT16 conn_id, uint16_t start_handle, uint16_t end_handle, + tBT_UUID char_uuid, tBT_UUID descr_uuid, + btgatt_db_element_t **db, UINT16 *count) +{ + bta_gattc_get_db_with_opration(conn_id, + GATT_OP_GET_DESCRI_BY_UUID, + 0, + NULL, + &char_uuid, + &descr_uuid, + start_handle, + end_handle, + db, + count); +} + +void BTA_GATTC_GetDescrByCharHandle(UINT16 conn_id, UINT16 char_handle, tBT_UUID descr_uuid, + btgatt_db_element_t **db, UINT16 *count) +{ + bta_gattc_get_db_with_opration(conn_id, + GATT_OP_GET_DESCRI_BY_HANDLE, + char_handle, + NULL, + NULL, + &descr_uuid, + 0, + 0xFFFF, + db, + count); +} + +void BTA_GATTC_GetIncludeService(UINT16 conn_id, UINT16 start_handle, UINT16 end_handle, + tBT_UUID *incl_uuid, btgatt_db_element_t **db, UINT16 *count) +{ + bta_gattc_get_db_with_opration(conn_id, + GATT_OP_GET_INCLUDE_SVC, + 0, + incl_uuid, + NULL, + NULL, + start_handle, + end_handle, + db, + count); +} + +void BTA_GATTC_GetDBSize(UINT16 conn_id, UINT16 start_handle, UINT16 end_handle, UINT16 *count) +{ + bta_gattc_get_db_size_handle(conn_id, start_handle, end_handle, count); +} + +void BTA_GATTC_GetDBSizeByType(UINT16 conn_id, bt_gatt_db_attribute_type_t type, + UINT16 start_handle, UINT16 end_handle, UINT16 char_handle, UINT16 *count) +{ + bta_gattc_get_db_size_with_type_handle(conn_id, type, start_handle, end_handle, char_handle, count); +} + + +/******************************************************************************* +** +** Function BTA_GATTC_GetGattDb +** +** Description This function is called to get the GATT database. +** +** Parameters conn_id: connection ID which identify the server. +** db: output parameter which will contain the GATT database copy. +** Caller is responsible for freeing it. +** count: number of elements in database. +** +*******************************************************************************/ +void BTA_GATTC_GetGattDb(UINT16 conn_id, UINT16 start_handle, UINT16 end_handle, + btgatt_db_element_t **db, UINT16 *count) +{ + bta_gattc_get_gatt_db(conn_id, start_handle, end_handle, db, count); +} + +/******************************************************************************* +** +** Function BTA_GATTC_ReadCharacteristic +** +** Description This function is called to read a characteristics value +** +** Parameters conn_id - connection ID. +** handle - characteritic handle to read. +** +** Returns None +** +*******************************************************************************/ +void BTA_GATTC_ReadCharacteristic(UINT16 conn_id, UINT16 handle, tBTA_GATT_AUTH_REQ auth_req) +{ + tBTA_GATTC_API_READ *p_buf; + + if ((p_buf = (tBTA_GATTC_API_READ *) osi_malloc(sizeof(tBTA_GATTC_API_READ))) != NULL) { + memset(p_buf, 0, sizeof(tBTA_GATTC_API_READ)); + + p_buf->hdr.event = BTA_GATTC_API_READ_EVT; + p_buf->hdr.layer_specific = conn_id; + p_buf->auth_req = auth_req; + p_buf->handle = handle; + p_buf->cmpl_evt = BTA_GATTC_READ_CHAR_EVT; + + bta_sys_sendmsg(p_buf); + } + return; +} + +/******************************************************************************* +** +** Function BTA_GATTC_ReadCharDescr +** +** Description This function is called to read a descriptor value. +** +** Parameters conn_id - connection ID. +** handle - descriptor handle to read. +** +** Returns None +** +*******************************************************************************/ +void BTA_GATTC_ReadCharDescr (UINT16 conn_id, UINT16 handle, tBTA_GATT_AUTH_REQ auth_req) +{ + tBTA_GATTC_API_READ *p_buf; + UINT16 len = (UINT16)(sizeof(tBTA_GATT_ID) + sizeof(tBTA_GATTC_API_READ)); + + if ((p_buf = (tBTA_GATTC_API_READ *) osi_malloc(len)) != NULL) { + memset(p_buf, 0, sizeof(tBTA_GATTC_API_READ)); + + p_buf->hdr.event = BTA_GATTC_API_READ_EVT; + p_buf->hdr.layer_specific = conn_id; + p_buf->auth_req = auth_req; + p_buf->handle = handle; + p_buf->cmpl_evt = BTA_GATTC_READ_DESCR_EVT; + + bta_sys_sendmsg(p_buf); + } + return; + +} +/******************************************************************************* +** +** Function BTA_GATTC_ReadMultiple +** +** Description This function is called to read multiple characteristic or +** characteristic descriptors. +** +** Parameters conn_id - connection ID. +** p_read_multi - pointer to the read multiple parameter. +** +** Returns None +** +*******************************************************************************/ +void BTA_GATTC_ReadMultiple(UINT16 conn_id, tBTA_GATTC_MULTI *p_read_multi, + tBTA_GATT_AUTH_REQ auth_req) +{ + tBTA_GATTC_API_READ_MULTI *p_buf; + //tBTA_GATTC_API_READ_MULTI *p_value; + UINT16 len = (UINT16)(sizeof(tBTA_GATTC_API_READ_MULTI)); + + if ((p_buf = (tBTA_GATTC_API_READ_MULTI *) osi_malloc(len)) != NULL) { + memset(p_buf, 0, len); + + p_buf->hdr.event = BTA_GATTC_API_READ_MULTI_EVT; + p_buf->hdr.layer_specific = conn_id; + p_buf->auth_req = auth_req; + p_buf->num_attr = p_read_multi->num_attr; + p_buf->cmpl_evt = BTA_GATTC_READ_MULTIPLE_EVT; + if (p_buf->num_attr > 0) { + memcpy(p_buf->handles, p_read_multi->handles, sizeof(UINT16) * p_read_multi->num_attr); + } + + bta_sys_sendmsg(p_buf); + } + return; +} + +/******************************************************************************* +** +** Function BTA_GATTC_ReadMultipleVariable +** +** Description This function is called to read multiple variable length characteristic or +** characteristic descriptors. +** +** Parameters conn_id - connection ID. +** p_read_multi - pointer to the read multiple parameter. +** +** Returns None +** +*******************************************************************************/ +void BTA_GATTC_ReadMultipleVariable(UINT16 conn_id, tBTA_GATTC_MULTI *p_read_multi, + tBTA_GATT_AUTH_REQ auth_req) +{ + tBTA_GATTC_API_READ_MULTI *p_buf; + UINT16 len = (UINT16)(sizeof(tBTA_GATTC_API_READ_MULTI)); + + if ((p_buf = (tBTA_GATTC_API_READ_MULTI *) osi_malloc(len)) != NULL) { + memset(p_buf, 0, len); + + p_buf->hdr.event = BTA_GATTC_API_READ_MULTI_VAR_EVT; + p_buf->hdr.layer_specific = conn_id; + p_buf->auth_req = auth_req; + p_buf->num_attr = p_read_multi->num_attr; + p_buf->cmpl_evt = BTA_GATTC_READ_MULTI_VAR_EVT; + if (p_buf->num_attr > 0) { + memcpy(p_buf->handles, p_read_multi->handles, sizeof(UINT16) * p_read_multi->num_attr); + } + + bta_sys_sendmsg(p_buf); + } + return; +} + +/******************************************************************************* +** +** Function BTA_GATTC_Read_by_type +** +** Description This function is called to read a attribute value by uuid +** +** Parameters conn_id - connection ID. +** s_handle - start handle. +** e_handle - end hanle +** uuid - The attribute UUID. +** +** Returns None +** +*******************************************************************************/ +void BTA_GATTC_Read_by_type(UINT16 conn_id, UINT16 s_handle,UINT16 e_handle, tBT_UUID *uuid, tBTA_GATT_AUTH_REQ auth_req) +{ + tBTA_GATTC_API_READ *p_buf; + + if ((p_buf = (tBTA_GATTC_API_READ *) osi_malloc(sizeof(tBTA_GATTC_API_READ))) != NULL) { + memset(p_buf, 0, sizeof(tBTA_GATTC_API_READ)); + + p_buf->hdr.event = BTA_GATTC_API_READ_BY_TYPE_EVT; + p_buf->hdr.layer_specific = conn_id; + p_buf->auth_req = auth_req; + p_buf->s_handle = s_handle; + p_buf->e_handle = e_handle; + memcpy(&(p_buf->uuid), uuid, sizeof(tBT_UUID)); + p_buf->cmpl_evt = BTA_GATTC_READ_CHAR_EVT; + + bta_sys_sendmsg(p_buf); + } + return; +} + +/******************************************************************************* +** +** Function BTA_GATTC_WriteCharValue +** +** Description This function is called to write characteristic value. +** +** Parameters conn_id - connection ID. +** handle - characteristic handle to write. +** write_type - type of write. +** len: length of the data to be written. +** p_value - the value to be written. +** +** Returns None +** +*******************************************************************************/ +void BTA_GATTC_WriteCharValue ( UINT16 conn_id, + UINT16 handle, + tBTA_GATTC_WRITE_TYPE write_type, + UINT16 len, + UINT8 *p_value, + tBTA_GATT_AUTH_REQ auth_req) +{ + tBTA_GATTC_API_WRITE *p_buf; + + if ((p_buf = (tBTA_GATTC_API_WRITE *) osi_malloc((UINT16)(sizeof(tBTA_GATTC_API_WRITE) + len))) != NULL) { + memset(p_buf, 0, sizeof(tBTA_GATTC_API_WRITE) + len); + + p_buf->hdr.event = BTA_GATTC_API_WRITE_EVT; + p_buf->hdr.layer_specific = conn_id; + p_buf->auth_req = auth_req; + p_buf->handle = handle; + p_buf->cmpl_evt = BTA_GATTC_WRITE_CHAR_EVT; + p_buf->write_type = write_type; + p_buf->len = len; + + if (p_value && len > 0) { + p_buf->p_value = (UINT8 *)(p_buf + 1); + memcpy(p_buf->p_value, p_value, len); + } + if(write_type == BTA_GATTC_TYPE_WRITE_NO_RSP){ + l2ble_update_att_acl_pkt_num(L2CA_DECREASE_BTC_NUM, NULL); + l2ble_update_att_acl_pkt_num(L2CA_ADD_BTU_NUM, NULL); + } + bta_sys_sendmsg(p_buf); + } + return; +} +/******************************************************************************* +** +** Function BTA_GATTC_WriteCharDescr +** +** Description This function is called to write descriptor value. +** +** Parameters conn_id - connection ID +** handle - descriptor hadle to write. +** write_type - write type. +** p_value - the value to be written. +** +** Returns None +** +*******************************************************************************/ +void BTA_GATTC_WriteCharDescr (UINT16 conn_id, + UINT16 handle, + tBTA_GATTC_WRITE_TYPE write_type, + tBTA_GATT_UNFMT *p_data, + tBTA_GATT_AUTH_REQ auth_req) +{ + size_t len = sizeof(tBTA_GATTC_API_WRITE); + tBTA_GATTC_API_WRITE *p_buf; + if (p_data != NULL) { + len += p_data->len; + } + + if ((p_buf = (tBTA_GATTC_API_WRITE *) osi_malloc(len)) != NULL) { + memset(p_buf, 0, len); + + p_buf->hdr.event = BTA_GATTC_API_WRITE_EVT; + p_buf->hdr.layer_specific = conn_id; + p_buf->auth_req = auth_req; + p_buf->handle = handle; + p_buf->cmpl_evt = BTA_GATTC_WRITE_DESCR_EVT; + p_buf->write_type = write_type; + + if (p_data && p_data->len != 0) { + p_buf->p_value = (UINT8 *)(p_buf + 1); + p_buf->len = p_data->len; + /* pack the descr data */ + memcpy(p_buf->p_value, p_data->p_value, p_data->len); + } + if(write_type == BTA_GATTC_TYPE_WRITE_NO_RSP){ + l2ble_update_att_acl_pkt_num(L2CA_DECREASE_BTC_NUM, NULL); + l2ble_update_att_acl_pkt_num(L2CA_ADD_BTU_NUM, NULL); + } + bta_sys_sendmsg(p_buf); + } + return; + +} +/******************************************************************************* +** +** Function BTA_GATTC_PrepareWrite +** +** Description This function is called to prepare write a characteristic value. +** +** Parameters conn_id - connection ID. +** p_char_id - GATT characteritic ID of the service. +** offset - offset of the write value. +** len: length of the data to be written. +** p_value - the value to be written. +** +** Returns None +** +*******************************************************************************/ +void BTA_GATTC_PrepareWrite (UINT16 conn_id, UINT16 handle, + UINT16 offset, UINT16 len, UINT8 *p_value, + tBTA_GATT_AUTH_REQ auth_req) +{ + tBTA_GATTC_API_WRITE *p_buf; + + if ((p_buf = (tBTA_GATTC_API_WRITE *) osi_malloc((UINT16)(sizeof(tBTA_GATTC_API_WRITE) + len))) != NULL) { + memset(p_buf, 0, sizeof(tBTA_GATTC_API_WRITE) + len); + + p_buf->hdr.event = BTA_GATTC_API_WRITE_EVT; + p_buf->hdr.layer_specific = conn_id; + p_buf->auth_req = auth_req; + p_buf->handle = handle; + + p_buf->write_type = BTA_GATTC_WRITE_PREPARE; + p_buf->offset = offset; + p_buf->len = len; + + if (p_value && len > 0) { + p_buf->p_value = (UINT8 *)(p_buf + 1); + memcpy(p_buf->p_value, p_value, len); + } + + bta_sys_sendmsg(p_buf); + } + return; + +} +/******************************************************************************* +** +** Function BTA_GATTC_PrepareWriteCharDescr +** +** Description This function is called to prepare write a characteristic descriptor value. +** +** Parameters conn_id - connection ID. +** p_char_descr_id - GATT characteritic descriptor ID of the service. +** offset - offset of the write value. +** len: length of the data to be written. +** p_value - the value to be written. +** +** Returns None +** +*******************************************************************************/ +void BTA_GATTC_PrepareWriteCharDescr (UINT16 conn_id, UINT16 handle, + UINT16 offset,tBTA_GATT_UNFMT *p_data, + tBTA_GATT_AUTH_REQ auth_req) +{ + tBTA_GATTC_API_WRITE *p_buf; + UINT16 len = sizeof(tBTA_GATTC_API_WRITE); + + if (p_data != NULL) { + len += p_data->len; + } + + if ((p_buf = (tBTA_GATTC_API_WRITE *) osi_malloc(len)) != NULL) { + memset(p_buf, 0, len); + + p_buf->hdr.event = BTA_GATTC_API_WRITE_EVT; + p_buf->hdr.layer_specific = conn_id; + p_buf->auth_req = auth_req; + p_buf->handle = handle; + p_buf->write_type = BTA_GATTC_WRITE_PREPARE; + p_buf->offset = offset; + + if (p_data && p_data->len != 0) { + p_buf->len = p_data->len; + p_buf->p_value = (UINT8 *)(p_buf + 1); + /* pack the descr data */ + memcpy(p_buf->p_value, p_data->p_value, p_data->len); + } + + bta_sys_sendmsg(p_buf); + } + return; + +} +/******************************************************************************* +** +** Function BTA_GATTC_ExecuteWrite +** +** Description This function is called to execute write a prepare write sequence. +** +** Parameters conn_id - connection ID. +** is_execute - execute or cancel. +** +** Returns None +** +*******************************************************************************/ +void BTA_GATTC_ExecuteWrite (UINT16 conn_id, BOOLEAN is_execute) +{ + tBTA_GATTC_API_EXEC *p_buf; + + if ((p_buf = (tBTA_GATTC_API_EXEC *) osi_malloc((UINT16)sizeof(tBTA_GATTC_API_EXEC))) != NULL) { + memset(p_buf, 0, sizeof(tBTA_GATTC_API_EXEC)); + p_buf->hdr.event = BTA_GATTC_API_EXEC_EVT; + p_buf->hdr.layer_specific = conn_id; + + p_buf->is_execute = is_execute; + + bta_sys_sendmsg(p_buf); + } + return; + +} +/******************************************************************************* +** +** Function BTA_GATTC_SendIndConfirm +** +** Description This function is called to send handle value confirmation. +** +** Parameters conn_id - connection ID. +** p_char_id - characteristic ID to confirm. +** +** Returns None +** +*******************************************************************************/ +void BTA_GATTC_SendIndConfirm (UINT16 conn_id, UINT16 handle) +{ + tBTA_GATTC_API_CONFIRM *p_buf; + + APPL_TRACE_API("BTA_GATTC_SendIndConfirm conn_id=%d handle =0x%x", + conn_id, handle); + + if ((p_buf = (tBTA_GATTC_API_CONFIRM *) osi_malloc(sizeof(tBTA_GATTC_API_CONFIRM))) != NULL) { + memset(p_buf, 0, sizeof(tBTA_GATTC_API_CONFIRM)); + p_buf->hdr.event = BTA_GATTC_API_CONFIRM_EVT; + p_buf->hdr.layer_specific = conn_id; + p_buf->handle = handle; + + bta_sys_sendmsg(p_buf); + } + return; + +} + +/******************************************************************************* +** +** Function BTA_GATTC_RegisterForNotifications +** +** Description This function is called to register for notification of a service. +** +** Parameters client_if - client interface. +** bda - target GATT server. +** handle - GATT characteristic handle. +** +** Returns OK if registration succeed, otherwise failed. +** +*******************************************************************************/ +tBTA_GATT_STATUS BTA_GATTC_RegisterForNotifications (tBTA_GATTC_IF client_if, + BD_ADDR bda, UINT16 handle) +{ + tBTA_GATTC_RCB *p_clreg; + tBTA_GATT_STATUS status = BTA_GATT_ILLEGAL_PARAMETER; + UINT8 i; + + if (!handle) + { + APPL_TRACE_ERROR("deregistration failed, handle is 0"); + return status; + } + + if ((p_clreg = bta_gattc_cl_get_regcb(client_if)) != NULL) { + for (i = 0; i < BTA_GATTC_NOTIF_REG_MAX; i ++) { + if ( p_clreg->notif_reg[i].in_use && + !memcmp(p_clreg->notif_reg[i].remote_bda, bda, BD_ADDR_LEN) && + p_clreg->notif_reg[i].handle == handle) { + APPL_TRACE_DEBUG("notification already registered"); + status = BTA_GATT_OK; + break; + } + } + if (status != BTA_GATT_OK) { + for (i = 0; i < BTA_GATTC_NOTIF_REG_MAX; i ++) { + if (!p_clreg->notif_reg[i].in_use) { + memset((void *)&p_clreg->notif_reg[i], 0, sizeof(tBTA_GATTC_NOTIF_REG)); + + p_clreg->notif_reg[i].in_use = TRUE; + memcpy(p_clreg->notif_reg[i].remote_bda, bda, BD_ADDR_LEN); + + p_clreg->notif_reg[i].handle = handle; + status = BTA_GATT_OK; + break; + } + } + if (i == BTA_GATTC_NOTIF_REG_MAX) { + status = BTA_GATT_NO_RESOURCES; + APPL_TRACE_ERROR("Max Notification Reached, registration failed,see CONFIG_BT_GATTC_NOTIF_REG_MAX in menuconfig"); + } + } + } else { + APPL_TRACE_ERROR("Client_if: %d Not Registered", client_if); + } + + return status; +} + +/******************************************************************************* +** +** Function BTA_GATTC_DeregisterForNotifications +** +** Description This function is called to de-register for notification of a service. +** +** Parameters client_if - client interface. +** remote_bda - target GATT server. +** handle - GATT characteristic handle. +** +** Returns OK if deregistration succeed, otherwise failed. +** +*******************************************************************************/ +tBTA_GATT_STATUS BTA_GATTC_DeregisterForNotifications (tBTA_GATTC_IF client_if, + BD_ADDR bda, UINT16 handle) +{ + if (!handle) { + APPL_TRACE_ERROR("%s: deregistration failed, handle is 0", __func__); + return BTA_GATT_ILLEGAL_PARAMETER; + } + + tBTA_GATTC_RCB *p_clreg = bta_gattc_cl_get_regcb(client_if); + if (p_clreg == NULL) { + APPL_TRACE_ERROR("%s client_if: %d not registered bd_addr:%02x:%02x:%02x:%02x:%02x:%02x", + __func__, client_if, bda[0], bda[1], bda[2], bda[3], bda[4], bda[5]); + return BTA_GATT_ILLEGAL_PARAMETER; + } + + for (int i = 0; i < BTA_GATTC_NOTIF_REG_MAX; i ++) { + if (p_clreg->notif_reg[i].in_use && + !memcmp(p_clreg->notif_reg[i].remote_bda, bda, BD_ADDR_LEN) && + p_clreg->notif_reg[i].handle == handle) { + APPL_TRACE_DEBUG("%s deregistered bd_addr:%02x:%02x:%02x:%02x:%02x:%02x", + __func__, bda[0], bda[1], bda[2], bda[3], bda[4], bda[5]); + memset(&p_clreg->notif_reg[i], 0, sizeof(tBTA_GATTC_NOTIF_REG)); + return BTA_GATT_OK; + } + } + + APPL_TRACE_ERROR("%s registration not found bd_addr:%02x:%02x:%02x:%02x:%02x:%02x", + __func__, bda[0], bda[1], bda[2], bda[3], bda[4], bda[5]); + return BTA_GATT_ERROR; +} + +/******************************************************************************* +** +** Function BTA_GATTC_Refresh +** +** Description Refresh the server cache of the remote device +** +** Parameters remote_bda: remote device BD address. +** erase_flash: delete cache from nvs flash +** +** Returns void +** +*******************************************************************************/ +void BTA_GATTC_Refresh(BD_ADDR remote_bda, bool erase_flash) +{ +#if(GATTC_CACHE_NVS == TRUE) + if(erase_flash) { + /* used to reset cache in application */ + bta_gattc_cache_reset(remote_bda); + } +#endif + //If the registration callback is NULL, return + if(bta_sys_is_register(BTA_ID_GATTC) == FALSE) { + return; + } + tBTA_GATTC_API_OPEN *p_buf; + + if ((p_buf = (tBTA_GATTC_API_OPEN *) osi_malloc(sizeof(tBTA_GATTC_API_OPEN))) != NULL) { + p_buf->hdr.event = BTA_GATTC_API_REFRESH_EVT; + memcpy(p_buf->remote_bda, remote_bda, BD_ADDR_LEN); + + bta_sys_sendmsg(p_buf); + } + return; +} + +void BTA_GATTC_CacheAssoc(tBTA_GATTC_IF client_if, BD_ADDR src_addr, BD_ADDR assoc_addr, BOOLEAN is_assoc) +{ + tBTA_GATTC_API_CACHE_ASSOC *p_buf; + + if ((p_buf = (tBTA_GATTC_API_CACHE_ASSOC *)osi_malloc(sizeof(tBTA_GATTC_API_CACHE_ASSOC))) != NULL) { + p_buf->hdr.event = BTA_GATTC_API_CACHE_ASSOC_EVT; + p_buf->is_assoc = is_assoc; + p_buf->client_if = client_if; + memcpy(p_buf->src_addr, src_addr, sizeof(BD_ADDR)); + memcpy(p_buf->assoc_addr, assoc_addr, sizeof(BD_ADDR)); + + bta_sys_sendmsg(p_buf); + + } + return; +} + +void BTA_GATTC_CacheGetAddrList(tBTA_GATTC_IF client_if) +{ + tBTA_GATTC_API_GET_ADDR *p_buf; + if ((p_buf = (tBTA_GATTC_API_GET_ADDR *)osi_malloc(sizeof(tBTA_GATTC_API_GET_ADDR))) != NULL) { + p_buf->hdr.event = BTA_GATTC_API_CACHE_GET_ADDR_LIST_EVT; + p_buf->client_if = client_if; + + bta_sys_sendmsg(p_buf); + } + return; +} + +/******************************************************************************* +** +** Function BTA_GATTC_Clean +** +** Description Clean the server cache of the remote device +** +** Parameters remote_bda: remote device BD address. +** +** Returns void +** +*******************************************************************************/ +void BTA_GATTC_Clean(BD_ADDR remote_bda) +{ +#if(GATTC_CACHE_NVS == TRUE) + /* used to reset cache in application */ + bta_gattc_cache_reset(remote_bda); +#endif + + tBTA_GATTC_API_OPEN *p_buf; + + if ((p_buf = (tBTA_GATTC_API_OPEN *) osi_malloc(sizeof(tBTA_GATTC_API_OPEN))) != NULL) { + p_buf->hdr.event = BTA_GATTC_API_CACHE_CLEAN_EVT; + memcpy(p_buf->remote_bda, remote_bda, BD_ADDR_LEN); + + bta_sys_sendmsg(p_buf); + } + return; +} +/******************************************************************************* +** +** Function BTA_GATTC_Listen +** +** Description Start advertisement to listen for connection request for a GATT +** client application. +** +** Parameters client_if: server interface. +** start: to start or stop listening for connection +** remote_bda: remote device BD address, if listen to all device +** use NULL. +** +** Returns void +** +*******************************************************************************/ +void BTA_GATTC_Listen(tBTA_GATTC_IF client_if, BOOLEAN start, BD_ADDR_PTR target_bda) +{ + tBTA_GATTC_API_LISTEN *p_buf; + + if ((p_buf = (tBTA_GATTC_API_LISTEN *) osi_malloc((UINT16)(sizeof(tBTA_GATTC_API_LISTEN) + BD_ADDR_LEN))) != NULL) { + p_buf->hdr.event = BTA_GATTC_API_LISTEN_EVT; + + p_buf->client_if = client_if; + p_buf->start = start; + if (target_bda) { + p_buf->remote_bda = (UINT8 *)(p_buf + 1); + memcpy(p_buf->remote_bda, target_bda, BD_ADDR_LEN); + } else { + p_buf->remote_bda = NULL; + } + + bta_sys_sendmsg(p_buf); + } + return; +} + +/******************************************************************************* +** +** Function BTA_GATTC_Broadcast +** +** Description Start broadcasting (non-connectable advertisements) +** +** Parameters client_if: client interface. +** start: to start or stop listening for connection +** +** Returns void +** +*******************************************************************************/ +void BTA_GATTC_Broadcast(tBTA_GATTC_IF client_if, BOOLEAN start) +{ + tBTA_GATTC_API_LISTEN *p_buf; + + if ((p_buf = (tBTA_GATTC_API_LISTEN *) osi_malloc((UINT16)(sizeof(tBTA_GATTC_API_LISTEN) + BD_ADDR_LEN))) != NULL) { + p_buf->hdr.event = BTA_GATTC_API_BROADCAST_EVT; + p_buf->client_if = client_if; + p_buf->start = start; + bta_sys_sendmsg(p_buf); + } + return; +} + +/* Add For BLE PTS */ +uint8_t BTA_GATTC_AutoDiscoverEnable(uint8_t enable) +{ + APPL_TRACE_DEBUG("%s enable %d", __func__, enable); + + bta_gattc_cb.auto_disc = ((enable > 0) ? true : false); + GATTC_AutoDiscoverEnable(enable); + + return 0; +} + +typedef struct { + UINT16 len; + union { + UINT16 uuid16; + UINT32 uuid32; + UINT8 uuid128[LEN_UUID_128]; + } uuid; +} __attribute__((packed)) tAPP_UUID; + +uint8_t BTA_GATTC_Discover(uint8_t gatt_if, uint16_t conn_id, void *uuid, uint8_t disc_type, uint16_t s_handle, uint16_t e_handle) +{ + tGATT_STATUS status; + tGATT_DISC_PARAM param; + tAPP_UUID *app_uuid = (tAPP_UUID *)uuid; + + conn_id = (UINT16)((((UINT8)conn_id) << 8) | gatt_if); + memset(¶m, 0, sizeof(tGATT_DISC_PARAM)); + + if (disc_type == GATT_DISC_SRVC_ALL || disc_type == GATT_DISC_SRVC_BY_UUID) { + param.s_handle = 1; + param.e_handle = 0xFFFF; + } else { + param.s_handle = s_handle; + param.e_handle = e_handle; + } + + if (app_uuid) { + param.service.len = app_uuid->len; + if (app_uuid->len == LEN_UUID_16) { + param.service.uu.uuid16 = app_uuid->uuid.uuid16; + } else if (app_uuid->len == LEN_UUID_32) { + param.service.uu.uuid32 = app_uuid->uuid.uuid32; + } else if (app_uuid->len == LEN_UUID_128) { + memcpy(param.service.uu.uuid128, app_uuid->uuid.uuid128, LEN_UUID_128); + } else { + APPL_TRACE_ERROR("%s invalid uuid len %u", __func__, app_uuid->len); + } + } + + status = GATTC_Discover (conn_id, disc_type, ¶m); + if (status != GATT_SUCCESS) { + APPL_TRACE_ERROR("%s status %x", __func__, status); + return -1; + } + + return 0; +} + +uint8_t BTA_GATTC_ReadLongChar(uint8_t gatt_if, uint16_t conn_id, uint16_t handle, uint16_t offset, uint8_t auth_req) +{ + tGATT_STATUS status; + tGATT_READ_PARAM read_param; + + conn_id = (UINT16)((((UINT8)conn_id) << 8) | gatt_if); + memset (&read_param, 0, sizeof(tGATT_READ_PARAM)); + read_param.partial.handle = handle; + read_param.partial.offset = offset; + read_param.partial.auth_req = auth_req; + + status = GATTC_Read(conn_id, GATT_READ_PARTIAL, &read_param); + if (status != GATT_SUCCESS) { + APPL_TRACE_ERROR("%s status %x", __func__, status); + return -1; + } + + return 0; +} +/* End BLE PTS */ +#endif /* defined(GATTC_INCLUDED) && (GATTC_INCLUDED == TRUE) */ diff --git a/lib/bt/host/bluedroid/bta/gatt/bta_gattc_cache.c b/lib/bt/host/bluedroid/bta/gatt/bta_gattc_cache.c new file mode 100644 index 00000000..7af5e6be --- /dev/null +++ b/lib/bt/host/bluedroid/bta/gatt/bta_gattc_cache.c @@ -0,0 +1,2216 @@ +/****************************************************************************** + * + * Copyright (C) 2003-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains the GATT client discovery procedures and cache + * related functions. + * + ******************************************************************************/ + +#include "common/bt_target.h" + +#if defined(GATTC_INCLUDED) && (GATTC_INCLUDED == TRUE) +//#if( defined GATTC_CACHE_NVS ) && (GATTC_CACHE_NVS == TRUE) + +#include +#include "bta/utl.h" +#include "bta/bta_sys.h" +#include "stack/sdp_api.h" +#include "stack/sdpdefs.h" +#include "bta_gattc_int.h" +#include "stack/btm_api.h" +#include "stack/btm_ble_api.h" +#include "osi/allocator.h" +#include "stack/l2c_api.h" +#include "btm_int.h" +#include "errno.h" + +// #include "osi/include/log.h" + +static void bta_gattc_char_dscpt_disc_cmpl(UINT16 conn_id, tBTA_GATTC_SERV *p_srvc_cb); +extern void bta_to_btif_uuid(bt_uuid_t *p_dest, tBT_UUID *p_src); +static size_t bta_gattc_get_db_size_with_type(list_t *services, + bt_gatt_db_attribute_type_t type, + tBT_UUID *char_uuid, + UINT16 start_handle, UINT16 end_handle); +static void bta_gattc_cache_write(BD_ADDR server_bda, UINT16 num_attr, + tBTA_GATTC_NV_ATTR *attr); +tBTA_GATTC_SERVICE* bta_gattc_find_matching_service(const list_t *services, UINT16 handle); +tBTA_GATTC_DESCRIPTOR* bta_gattc_get_descriptor_srcb(tBTA_GATTC_SERV *p_srcb, UINT16 handle); +tBTA_GATTC_CHARACTERISTIC* bta_gattc_get_characteristic_srcb(tBTA_GATTC_SERV *p_srcb, UINT16 handle); +void bta_gattc_fill_gatt_db_el(btgatt_db_element_t *p_attr, + bt_gatt_db_attribute_type_t type, + UINT16 att_handle, + UINT16 s_handle, UINT16 e_handle, + UINT16 id, tBT_UUID uuid, UINT8 prop); + + +#if (SDP_INCLUDED == TRUE) +static tBTA_GATT_STATUS bta_gattc_sdp_service_disc(UINT16 conn_id, tBTA_GATTC_SERV *p_server_cb); +#define BTA_GATT_SDP_DB_SIZE 4096 +#endif ///SDP_INCLUDED == TRUE +#define GATT_CACHE_PREFIX "/data/misc/bluetooth/gatt_cache_" +#define GATT_CACHE_VERSION 2 + +static void bta_gattc_generate_cache_file_name(char *buffer, BD_ADDR bda) +{ + sprintf(buffer, "%s%02x%02x%02x%02x%02x%02x", GATT_CACHE_PREFIX, + bda[0], bda[1], bda[2], bda[3], bda[4], bda[5]); +} + +/***************************************************************************** +** Constants and data types +*****************************************************************************/ +#if (SDP_INCLUDED == TRUE) +typedef struct +{ + tSDP_DISCOVERY_DB *p_sdp_db; + UINT16 sdp_conn_id; +} tBTA_GATTC_CB_DATA; +#endif ///SDP_INCLUDED == TRUE + + +#if (defined BTA_GATT_DEBUG && BTA_GATT_DEBUG == TRUE) +static char *bta_gattc_attr_type[] = { + "I", /* Included Service */ + "C", /* Characteristic */ + "D" /* Characteristic Descriptor */ +}; +/* utility functions */ + +bool display_cache_attribute(void *data, void *context) +{ + //tBTA_GATTC_CACHE_ATTR *p_attr = data; + //APPL_TRACE_ERROR("\t Attr handle[%d] uuid[0x%04x] type[%s] prop[0x%1x]", + // p_attr->handle, p_attr->uuid.uu.uuid16, + // bta_gattc_attr_type[p_attr->attr_type], p_attr->property); + return true; +} + +bool display_cache_service(void *data, void *context) +{ + tBTA_GATTC_SERVICE *p_cur_srvc = data; + APPL_TRACE_API("Service: handle[%d ~ %d] %s[0x%04x] inst[%d]", + p_cur_srvc->s_handle, p_cur_srvc->e_handle, + ((p_cur_srvc->uuid.len == 2) ? "uuid16" : "uuid128"), + p_cur_srvc->uuid.uu.uuid16, + p_cur_srvc->handle); + + if (p_cur_srvc->characteristics != NULL) { + list_foreach(p_cur_srvc->characteristics, display_cache_attribute, NULL); + } + + return true; +} + +/******************************************************************************* +** +** Function bta_gattc_display_cache_server +** +** Description debug function to display the server cache. +** +** Returns none. +** +*******************************************************************************/ +static void bta_gattc_display_cache_server(list_t *p_cache) +{ + APPL_TRACE_ERROR("<================Start Server Cache =============>"); + list_foreach(p_cache, display_cache_service, NULL); + APPL_TRACE_ERROR("<================End Server Cache =============>"); + APPL_TRACE_ERROR(" "); +} + +/******************************************************************************* +** +** Function bta_gattc_display_explore_record +** +** Description debug function to display the exploration list +** +** Returns none. +** +*******************************************************************************/ +static void bta_gattc_display_explore_record(tBTA_GATTC_ATTR_REC *p_rec, UINT8 num_rec) +{ + UINT8 i; + tBTA_GATTC_ATTR_REC *pp = p_rec; + + APPL_TRACE_ERROR("<================Start Explore Queue =============>"); + for (i = 0; i < num_rec; i ++, pp ++) { + APPL_TRACE_ERROR("\t rec[%d] uuid[0x%04x] s_handle[%d] e_handle[%d] is_primary[%d]", + i + 1, pp->uuid.uu.uuid16, pp->s_handle, pp->e_handle, pp->is_primary); + } + APPL_TRACE_ERROR("<================ End Explore Queue =============>"); + APPL_TRACE_ERROR(" "); + +} +#endif /* BTA_GATT_DEBUG == TRUE */ + + +/******************************************************************************* +** +** Function bta_gattc_init_cache +** +** Description Initialize the database cache and discovery related resources. +** +** Returns status +** +*******************************************************************************/ +tBTA_GATT_STATUS bta_gattc_init_cache(tBTA_GATTC_SERV *p_srvc_cb) +{ + if (p_srvc_cb->p_srvc_cache != NULL) { + list_free(p_srvc_cb->p_srvc_cache); + p_srvc_cb->p_srvc_cache = NULL; + } + + osi_free(p_srvc_cb->p_srvc_list); + + if ((p_srvc_cb->p_srvc_list = (tBTA_GATTC_ATTR_REC *)osi_malloc(BTA_GATTC_ATTR_LIST_SIZE)) == NULL) { + APPL_TRACE_DEBUG("No resources: GKI buffer allocation failed."); + return BTA_GATT_NO_RESOURCES; + } else { + p_srvc_cb->total_srvc = 0; + p_srvc_cb->cur_srvc_idx = 0; + p_srvc_cb->cur_char_idx = 0; + p_srvc_cb->next_avail_idx = 0; + p_srvc_cb->total_attr = 0; + } + + return BTA_GATT_OK; +} + +static void characteristic_free(void *ptr) +{ + tBTA_GATTC_CHARACTERISTIC *p_char = ptr; + list_free(p_char->descriptors); + osi_free(p_char); +} + +static void service_free(void *ptr) +{ + tBTA_GATTC_SERVICE *srvc = ptr; + list_free(srvc->characteristics); + list_free(srvc->included_svc); + osi_free(srvc); +} + +static void bta_gattc_free(void *ptr) +{ + osi_free(ptr); +} + +void bta_gattc_insert_sec_service_to_cache(list_t *services, tBTA_GATTC_SERVICE *p_new_srvc) +{ + // services/p_new_srvc is NULL + if (!services || !p_new_srvc) { + APPL_TRACE_ERROR("%s services/p_new_srvc is NULL", __func__); + return; + } + //list is empty + if (list_is_empty(services)) { + list_append(services, p_new_srvc); + } else { + //check the first service + list_node_t *sn = list_begin(services); + tBTA_GATTC_SERVICE *service = list_node(sn); + if(service && p_new_srvc->e_handle < service->s_handle) { + list_prepend(services, p_new_srvc); + } else { + for (list_node_t *sn = list_begin(services); sn != list_end(services); sn = list_next(sn)) { + list_node_t *next_sn = list_next(sn); + if(next_sn == list_end(services)) { + list_append(services, p_new_srvc); + return; + } + tBTA_GATTC_SERVICE *service = list_node(sn); + tBTA_GATTC_SERVICE *next_service = list_node(next_sn); + if (p_new_srvc->s_handle > service->e_handle && p_new_srvc->e_handle < next_service->s_handle) { + list_insert_after(services, sn, p_new_srvc); + return; + } + } + } + } +} + +/******************************************************************************* +** +** Function bta_gattc_add_srvc_to_cache +** +** Description Add a service into database cache. +** +** Returns status +** +*******************************************************************************/ +static tBTA_GATT_STATUS bta_gattc_add_srvc_to_cache(tBTA_GATTC_SERV *p_srvc_cb, + UINT16 s_handle, UINT16 e_handle, + tBT_UUID *p_uuid, + BOOLEAN is_primary) +{ +#if (defined BTA_GATT_DEBUG && BTA_GATT_DEBUG == TRUE) + APPL_TRACE_DEBUG("Add a service into Service"); +#endif + + tBTA_GATTC_SERVICE *p_new_srvc = osi_malloc(sizeof(tBTA_GATTC_SERVICE)); + if (!p_new_srvc) { + APPL_TRACE_WARNING("%s(), no resource.", __func__); + return BTA_GATT_NO_RESOURCES; + } + + /* update service information */ + p_new_srvc->s_handle = s_handle; + p_new_srvc->e_handle = e_handle; + p_new_srvc->is_primary = is_primary; + memcpy(&p_new_srvc->uuid, p_uuid, sizeof(tBT_UUID)); + p_new_srvc->handle = s_handle; + p_new_srvc->characteristics = list_new(characteristic_free); + p_new_srvc->included_svc = list_new(bta_gattc_free); + + if (p_srvc_cb->p_srvc_cache == NULL) { + p_srvc_cb->p_srvc_cache = list_new(service_free); + } + + if(!p_srvc_cb->p_srvc_cache) { + APPL_TRACE_WARNING("%s(), no resource.", __func__); + osi_free(p_new_srvc); + return BTA_GATT_NO_RESOURCES; + } + + if(is_primary) { + list_append(p_srvc_cb->p_srvc_cache, p_new_srvc); + } else { + //add secondary service into list + bta_gattc_insert_sec_service_to_cache(p_srvc_cb->p_srvc_cache, p_new_srvc); + } + return BTA_GATT_OK; +} + +static tBTA_GATT_STATUS bta_gattc_add_char_to_cache(tBTA_GATTC_SERV *p_srvc_cb, + UINT16 attr_handle, + UINT16 value_handle, + tBT_UUID *p_uuid, + UINT8 property) +{ +#if (defined BTA_GATT_DEBUG && BTA_GATT_DEBUG == TRUE) + APPL_TRACE_DEBUG("%s: Add a characteristic into Service", __func__); + APPL_TRACE_DEBUG("handle=%d uuid16=0x%x property=0x%x", + value_handle, p_uuid->uu.uuid16, property); +#endif + + tBTA_GATTC_SERVICE *service = bta_gattc_find_matching_service(p_srvc_cb->p_srvc_cache, attr_handle); + if (!service) { + APPL_TRACE_ERROR("Illegal action to add char/descr/incl srvc for non-existing service!"); + return GATT_WRONG_STATE; + } + + /* TODO(jpawlowski): We should use attribute handle, not value handle to refer to characteristic. + This is just a temporary workaround. + */ + if (service->e_handle < value_handle) { + service->e_handle = value_handle; + } + + tBTA_GATTC_CHARACTERISTIC *characteristic = osi_malloc(sizeof(tBTA_GATTC_CHARACTERISTIC)); + if (!characteristic) { + APPL_TRACE_WARNING("%s(), no resource.", __func__); + return BTA_GATT_NO_RESOURCES; + } + characteristic->handle = value_handle; + characteristic->properties = property; + characteristic->descriptors = list_new(bta_gattc_free); + memcpy(&characteristic->uuid, p_uuid, sizeof(tBT_UUID)); + + characteristic->service = service; + list_append(service->characteristics, characteristic); + + return BTA_GATT_OK; +} +/******************************************************************************* +** +** Function bta_gattc_add_attr_to_cache +** +** Description Add an attribute into database cache buffer. +** +** Returns status +** +*******************************************************************************/ +static tBTA_GATT_STATUS bta_gattc_add_attr_to_cache(tBTA_GATTC_SERV *p_srvc_cb, + UINT16 handle, + tBT_UUID *p_uuid, + UINT8 property, + UINT16 incl_srvc_s_handle, + UINT16 incl_srvc_e_handle, + tBTA_GATTC_ATTR_TYPE type) +{ +#if (defined BTA_GATT_DEBUG && BTA_GATT_DEBUG == TRUE) + APPL_TRACE_DEBUG("%s: Add a [%s] into Service", __func__, bta_gattc_attr_type[type]); + APPL_TRACE_DEBUG("handle=%d uuid16=0x%x property=0x%x type=%d", + handle, p_uuid->uu.uuid16, property, type); +#endif + + tBTA_GATTC_SERVICE *service = bta_gattc_find_matching_service(p_srvc_cb->p_srvc_cache, handle); + if (!service) { + APPL_TRACE_ERROR("Illegal action to add char/descr/incl srvc for non-existing service!"); + return GATT_WRONG_STATE; + } + + if (type == BTA_GATTC_ATTR_TYPE_INCL_SRVC) { + tBTA_GATTC_INCLUDED_SVC *isvc = + osi_malloc(sizeof(tBTA_GATTC_INCLUDED_SVC)); + if (!isvc) { + APPL_TRACE_WARNING("%s(), no resource.", __func__); + return BTA_GATT_NO_RESOURCES; + } + isvc->handle = handle; + memcpy(&isvc->uuid, p_uuid, sizeof(tBT_UUID)); + isvc->incl_srvc_s_handle = incl_srvc_s_handle; + isvc->incl_srvc_e_handle = incl_srvc_e_handle; + isvc->owning_service = service; + isvc->included_service = bta_gattc_find_matching_service( + p_srvc_cb->p_srvc_cache, incl_srvc_s_handle); + if (!isvc->included_service) { + // if can't find included service, wait to update later + p_srvc_cb->update_incl_srvc = true; + } + + list_append(service->included_svc, isvc); + } else if (type == BTA_GATTC_ATTR_TYPE_CHAR_DESCR) { + tBTA_GATTC_DESCRIPTOR *descriptor = + osi_malloc(sizeof(tBTA_GATTC_DESCRIPTOR)); + if (!descriptor) { + APPL_TRACE_WARNING("%s(), no resource.", __func__); + return BTA_GATT_NO_RESOURCES; + } + descriptor->handle = handle; + memcpy(&descriptor->uuid, p_uuid, sizeof(tBT_UUID)); + + if (service->characteristics == NULL || + list_is_empty(service->characteristics)) { + APPL_TRACE_ERROR("%s: Illegal action to add descriptor before adding a characteristic!", + __func__); + osi_free(descriptor); + return GATT_WRONG_STATE; + } + + tBTA_GATTC_CHARACTERISTIC *char_node = list_back(service->characteristics); + + descriptor->characteristic = char_node; + list_append(char_node->descriptors, descriptor); + } + return BTA_GATT_OK; +} + +/******************************************************************************* +** +** Function bta_gattc_get_disc_range +** +** Description get discovery stating and ending handle range. +** +** Returns None. +** +*******************************************************************************/ +void bta_gattc_get_disc_range(tBTA_GATTC_SERV *p_srvc_cb, UINT16 *p_s_hdl, UINT16 *p_e_hdl, BOOLEAN is_srvc) +{ + tBTA_GATTC_ATTR_REC *p_rec = NULL; + + if (is_srvc) { + p_rec = p_srvc_cb->p_srvc_list + p_srvc_cb->cur_srvc_idx; + *p_s_hdl = p_rec->s_handle; + } else { + p_rec = p_srvc_cb->p_srvc_list + p_srvc_cb->cur_char_idx; + *p_s_hdl = p_rec->s_handle + 1; + } + + *p_e_hdl = p_rec->e_handle; +#if (defined BTA_GATT_DEBUG && BTA_GATT_DEBUG == TRUE) + APPL_TRACE_DEBUG("discover range [%d ~ %d]", p_rec->s_handle, p_rec->e_handle); +#endif + return; +} +/******************************************************************************* +** +** Function bta_gattc_discover_pri_service +** +** Description Start primary service discovery +** +** Returns status of the operation. +** +*******************************************************************************/ +tBTA_GATT_STATUS bta_gattc_discover_pri_service(UINT16 conn_id, tBTA_GATTC_SERV *p_server_cb, + UINT8 disc_type) +{ + tBTA_GATTC_CLCB *p_clcb = bta_gattc_find_clcb_by_conn_id(conn_id); + tBTA_GATT_STATUS status = BTA_GATT_ERROR; + + if (p_clcb) { + if (p_clcb->transport == BTA_TRANSPORT_LE) { + status = bta_gattc_discover_procedure(conn_id, p_server_cb, disc_type); + } else { + #if (SDP_INCLUDED == TRUE) + status = bta_gattc_sdp_service_disc(conn_id, p_server_cb); + #endif ///SDP_INCLUDED == TRUE + } + } + + return status; +} +/******************************************************************************* +** +** Function bta_gattc_discover_procedure +** +** Description Start a particular type of discovery procedure on server. +** +** Returns status of the operation. +** +*******************************************************************************/ +tBTA_GATT_STATUS bta_gattc_discover_procedure(UINT16 conn_id, tBTA_GATTC_SERV *p_server_cb, + UINT8 disc_type) +{ + tGATT_DISC_PARAM param; + BOOLEAN is_service = TRUE; + + memset(¶m, 0, sizeof(tGATT_DISC_PARAM)); + + if (disc_type == GATT_DISC_SRVC_ALL || disc_type == GATT_DISC_SRVC_BY_UUID) { + param.s_handle = 1; + param.e_handle = 0xFFFF; + } else { + if (disc_type == GATT_DISC_CHAR_DSCPT) { + is_service = FALSE; + } + + bta_gattc_get_disc_range(p_server_cb, ¶m.s_handle, ¶m.e_handle, is_service); + + if (param.s_handle > param.e_handle) { + return GATT_ERROR; + } + } + return GATTC_Discover (conn_id, disc_type, ¶m); + +} +/******************************************************************************* +** +** Function bta_gattc_start_disc_include_srvc +** +** Description Start discovery for included service +** +** Returns status of the operation. +** +*******************************************************************************/ +tBTA_GATT_STATUS bta_gattc_start_disc_include_srvc(UINT16 conn_id, tBTA_GATTC_SERV *p_srvc_cb) +{ + return bta_gattc_discover_procedure(conn_id, p_srvc_cb, GATT_DISC_INC_SRVC); +} +/******************************************************************************* +** +** Function bta_gattc_start_disc_char +** +** Description Start discovery for characteristic +** +** Returns status of the operation. +** +*******************************************************************************/ +tBTA_GATT_STATUS bta_gattc_start_disc_char(UINT16 conn_id, tBTA_GATTC_SERV *p_srvc_cb) +{ + p_srvc_cb->total_char = 0; + + return bta_gattc_discover_procedure(conn_id, p_srvc_cb, GATT_DISC_CHAR); +} +/******************************************************************************* +** +** Function bta_gattc_start_disc_char_dscp +** +** Description Start discovery for characteristic descriptor +** +** Returns none. +** +*******************************************************************************/ +void bta_gattc_start_disc_char_dscp(UINT16 conn_id, tBTA_GATTC_SERV *p_srvc_cb) +{ + APPL_TRACE_DEBUG("starting discover characteristics descriptor"); + + if (bta_gattc_discover_procedure(conn_id, p_srvc_cb, GATT_DISC_CHAR_DSCPT) != 0) { + bta_gattc_char_dscpt_disc_cmpl(conn_id, p_srvc_cb); + } +} + +void bta_gattc_update_include_service(const list_t *services) { + if (!services || list_is_empty(services)) { + return; + } + for (list_node_t *sn = list_begin(services); sn != list_end(services); sn = list_next(sn)) { + tBTA_GATTC_SERVICE *service = list_node(sn); + if(!service || !service->included_svc || list_is_empty(service->included_svc)) break; + for (list_node_t *sn = list_begin(service->included_svc); sn != list_end(service->included_svc); sn = list_next(sn)) { + tBTA_GATTC_INCLUDED_SVC *include_service = list_node(sn); + if(include_service && !include_service->included_service) { + //update + include_service->included_service = bta_gattc_find_matching_service(services, include_service->incl_srvc_s_handle); + if(!include_service->included_service) { + //not match, free it + list_remove(service->included_svc, include_service); + osi_free(include_service); + } + } + } + + } +} + +/******************************************************************************* +** +** Function bta_gattc_explore_srvc +** +** Description process the service discovery complete event +** +** Returns status +** +*******************************************************************************/ +static void bta_gattc_explore_srvc(UINT16 conn_id, tBTA_GATTC_SERV *p_srvc_cb) +{ + tBTA_GATTC_ATTR_REC *p_rec = p_srvc_cb->p_srvc_list + p_srvc_cb->cur_srvc_idx; + tBTA_GATTC_CLCB *p_clcb = bta_gattc_find_clcb_by_conn_id(conn_id); + + APPL_TRACE_DEBUG("Start service discovery: srvc_idx = %d", p_srvc_cb->cur_srvc_idx); + + p_srvc_cb->cur_char_idx = p_srvc_cb->next_avail_idx = p_srvc_cb->total_srvc; + + if (p_clcb == NULL) { + APPL_TRACE_ERROR("unknown connection ID"); + return; + } + /* start expore a service if there is service not been explored */ + if (p_srvc_cb->cur_srvc_idx < p_srvc_cb->total_srvc) { + /* add the first service into cache */ + if (bta_gattc_add_srvc_to_cache (p_srvc_cb, + p_rec->s_handle, + p_rec->e_handle, + &p_rec->uuid, + p_rec->is_primary) == 0) { + /* start discovering included services */ + bta_gattc_start_disc_include_srvc(conn_id, p_srvc_cb); + return; + } + } + // if update_incl_srvc is true, update include service + if(p_srvc_cb->update_incl_srvc) { + bta_gattc_update_include_service(p_srvc_cb->p_srvc_cache); + p_srvc_cb->update_incl_srvc = false; + } + /* no service found at all, the end of server discovery*/ + APPL_TRACE_DEBUG("%s no more services found", __func__); + +#if (defined BTA_GATT_DEBUG && BTA_GATT_DEBUG == TRUE) + bta_gattc_display_cache_server(p_srvc_cb->p_srvc_cache); +#endif + + //server discover end, update connection parameters +#if BLE_INCLUDED == TRUE + #if (BT_MULTI_CONNECTION_ENBALE == FALSE) + if (p_clcb->transport == BTA_TRANSPORT_LE) { + L2CA_EnableUpdateBleConnParams(p_clcb->p_srcb->server_bda, TRUE); + } + #endif + //discover service complete, trigger callback + tBTA_GATTC cb_data; + cb_data.dis_cmpl.status = p_clcb->status; + cb_data.dis_cmpl.conn_id = conn_id; + ( *p_clcb->p_rcb->p_cback)(BTA_GATTC_DIS_SRVC_CMPL_EVT, &cb_data); +#endif +#if(GATTC_CACHE_NVS == TRUE) + /* save cache to NV */ + p_clcb->p_srcb->state = BTA_GATTC_SERV_SAVE; + bta_gattc_cache_save(p_clcb->p_srcb, p_clcb->bta_conn_id); +#endif + bta_gattc_reset_discover_st(p_clcb->p_srcb, BTA_GATT_OK); +} +/******************************************************************************* +** +** Function bta_gattc_incl_srvc_disc_cmpl +** +** Description process the relationship discovery complete event +** +** Returns status +** +*******************************************************************************/ +static void bta_gattc_incl_srvc_disc_cmpl(UINT16 conn_id, tBTA_GATTC_SERV *p_srvc_cb) +{ + p_srvc_cb->cur_char_idx = p_srvc_cb->total_srvc; + + /* start discoverying characteristic */ + bta_gattc_start_disc_char(conn_id, p_srvc_cb); +} +/******************************************************************************* +** +** Function bta_gattc_char_disc_cmpl +** +** Description process the characteristic discovery complete event +** +** Returns status +** +*******************************************************************************/ +static void bta_gattc_char_disc_cmpl(UINT16 conn_id, tBTA_GATTC_SERV *p_srvc_cb) +{ + tBTA_GATTC_ATTR_REC *p_rec = p_srvc_cb->p_srvc_list + p_srvc_cb->cur_char_idx; + + /* if there are characteristic needs to be explored */ + if (p_srvc_cb->total_char > 0) { + /* add the first characteristic into cache */ + bta_gattc_add_char_to_cache (p_srvc_cb, + p_rec->char_decl_handle, + p_rec->s_handle, + &p_rec->uuid, + p_rec->property); + + /* start discoverying characteristic descriptor , if failed, disc for next char */ + bta_gattc_start_disc_char_dscp(conn_id, p_srvc_cb); + } else { /* otherwise start with next service */ + p_srvc_cb->cur_srvc_idx ++; + + bta_gattc_explore_srvc (conn_id, p_srvc_cb); + } +} +/******************************************************************************* +** +** Function bta_gattc_char_dscpt_disc_cmpl +** +** Description process the char descriptor discovery complete event +** +** Returns status +** +*******************************************************************************/ +static void bta_gattc_char_dscpt_disc_cmpl(UINT16 conn_id, tBTA_GATTC_SERV *p_srvc_cb) +{ + tBTA_GATTC_ATTR_REC *p_rec = NULL; + + /* Recursive function will cause BTU stack overflow when there are a large number of characteristic + * without descriptor to discover. So replace it with while function */ + while (--p_srvc_cb->total_char > 0) { + p_rec = p_srvc_cb->p_srvc_list + (++ p_srvc_cb->cur_char_idx); + /* add the next characteristic into cache */ + bta_gattc_add_char_to_cache (p_srvc_cb, + p_rec->char_decl_handle, + p_rec->s_handle, + &p_rec->uuid, + p_rec->property); + /* start to discover next characteristic for descriptor */ + if (bta_gattc_discover_procedure(conn_id, p_srvc_cb, GATT_DISC_CHAR_DSCPT) == 0) { + /* send att req and wait for att rsp */ + break; + } + } + + if (p_srvc_cb->total_char == 0) /* all characteristic has been explored, start with next service if any */ + { +#if (defined BTA_GATT_DEBUG && BTA_GATT_DEBUG == TRUE) + APPL_TRACE_ERROR("all char has been explored"); +#endif + p_srvc_cb->cur_srvc_idx ++; + bta_gattc_explore_srvc (conn_id, p_srvc_cb); + } + +} +static BOOLEAN bta_gattc_srvc_in_list(tBTA_GATTC_SERV *p_srvc_cb, UINT16 s_handle, + UINT16 e_handle, tBT_UUID uuid) +{ + tBTA_GATTC_ATTR_REC *p_rec = NULL; + UINT8 i; + BOOLEAN exist_srvc = FALSE; + UNUSED(uuid); + + if (!GATT_HANDLE_IS_VALID(s_handle) || !GATT_HANDLE_IS_VALID(e_handle)) { + APPL_TRACE_ERROR("invalid included service handle: [0x%04x ~ 0x%04x]", s_handle, e_handle); + exist_srvc = TRUE; + } else { + for (i = 0; i < p_srvc_cb->next_avail_idx; i ++) { + p_rec = p_srvc_cb->p_srvc_list + i; + + /* a new service should not have any overlap with other service handle range */ + if (p_rec->s_handle == s_handle || p_rec->e_handle == e_handle) { + exist_srvc = TRUE; + break; + } + } + } + return exist_srvc; +} +/******************************************************************************* +** +** Function bta_gattc_add_srvc_to_list +** +** Description Add a service into explore pending list +** +** Returns status +** +*******************************************************************************/ +static tBTA_GATT_STATUS bta_gattc_add_srvc_to_list(tBTA_GATTC_SERV *p_srvc_cb, + UINT16 s_handle, UINT16 e_handle, + tBT_UUID uuid, BOOLEAN is_primary) +{ + tBTA_GATTC_ATTR_REC *p_rec = NULL; + tBTA_GATT_STATUS status = BTA_GATT_OK; + + if (p_srvc_cb->p_srvc_list && p_srvc_cb->next_avail_idx < BTA_GATTC_MAX_CACHE_CHAR) { + p_rec = p_srvc_cb->p_srvc_list + p_srvc_cb->next_avail_idx; + + APPL_TRACE_DEBUG("%s handle=%d, service type=0x%04x", + __func__, s_handle, uuid.uu.uuid16); + + p_rec->s_handle = s_handle; + p_rec->e_handle = e_handle; + p_rec->is_primary = is_primary; + memcpy(&p_rec->uuid, &uuid, sizeof(tBT_UUID)); + + p_srvc_cb->total_srvc ++; + p_srvc_cb->next_avail_idx ++; + } else { + /* allocate bigger buffer ?? */ + status = GATT_DB_FULL; + + APPL_TRACE_ERROR("service not added, no resources or wrong state, see CONFIG_BT_GATTC_MAX_CACHE_CHAR"); + } + return status; +} +/******************************************************************************* +** +** Function bta_gattc_add_char_to_list +** +** Description Add a characteristic into explore pending list +** +** Returns status +** +*******************************************************************************/ +static tBTA_GATT_STATUS bta_gattc_add_char_to_list(tBTA_GATTC_SERV *p_srvc_cb, + UINT16 decl_handle, UINT16 value_handle, + tBT_UUID uuid, UINT8 property) +{ + tBTA_GATTC_ATTR_REC *p_rec = NULL; + tBTA_GATT_STATUS status = BTA_GATT_OK; + + if (p_srvc_cb->p_srvc_list == NULL) { + APPL_TRACE_ERROR("No service available, unexpected char discovery result"); + status = BTA_GATT_INTERNAL_ERROR; + } else if (p_srvc_cb->next_avail_idx < BTA_GATTC_MAX_CACHE_CHAR) { + + p_rec = p_srvc_cb->p_srvc_list + p_srvc_cb->next_avail_idx; + + p_srvc_cb->total_char ++; + + p_rec->s_handle = value_handle; + p_rec->char_decl_handle = decl_handle; + p_rec->property = property; + p_rec->e_handle = (p_srvc_cb->p_srvc_list + p_srvc_cb->cur_srvc_idx)->e_handle; + memcpy(&p_rec->uuid, &uuid, sizeof(tBT_UUID)); + + /* update the endind handle of pervious characteristic if available */ + if (p_srvc_cb->total_char > 1) { + p_rec -= 1; + p_rec->e_handle = decl_handle - 1; + } + p_srvc_cb->next_avail_idx ++; + } else { + APPL_TRACE_ERROR("char not added, no resources, see CONFIG_BT_GATTC_MAX_CACHE_CHAR"); + /* allocate bigger buffer ?? */ + status = BTA_GATT_DB_FULL; + } + return status; + +} +/******************************************************************************* +** +** Function bta_gattc_sdp_callback +** +** Description Process the discovery result from sdp +** +** Returns void +** +*******************************************************************************/ +#if (SDP_INCLUDED == TRUE) +void bta_gattc_sdp_callback(UINT16 sdp_status, void* user_data) +{ + tSDP_DISC_REC *p_sdp_rec = NULL; + tBT_UUID service_uuid; + tSDP_PROTOCOL_ELEM pe; + UINT16 start_handle = 0, end_handle = 0; + tBTA_GATTC_CB_DATA *cb_data = user_data; + tBTA_GATTC_SERV *p_srvc_cb = bta_gattc_find_scb_by_cid(cb_data->sdp_conn_id); + + if (((sdp_status == SDP_SUCCESS) || (sdp_status == SDP_DB_FULL)) && p_srvc_cb != NULL) { + do { + /* find a service record, report it */ + p_sdp_rec = SDP_FindServiceInDb(cb_data->p_sdp_db, 0, p_sdp_rec); + if (p_sdp_rec) { + if (SDP_FindServiceUUIDInRec(p_sdp_rec, &service_uuid)) { + + if (SDP_FindProtocolListElemInRec(p_sdp_rec, UUID_PROTOCOL_ATT, &pe)) { + start_handle = (UINT16) pe.params[0]; + end_handle = (UINT16) pe.params[1]; + +#if (defined BTA_GATT_DEBUG && BTA_GATT_DEBUG == TRUE) + APPL_TRACE_EVENT("Found ATT service [0x%04x] handle[0x%04x ~ 0x%04x]", + service_uuid.uu.uuid16, start_handle, end_handle); +#endif + + if (GATT_HANDLE_IS_VALID(start_handle) && GATT_HANDLE_IS_VALID(end_handle) && + p_srvc_cb != NULL) { + /* discover services result, add services into a service list */ + bta_gattc_add_srvc_to_list(p_srvc_cb, + start_handle, + end_handle, + service_uuid, + TRUE); + } else { + APPL_TRACE_ERROR("invalid start_handle = %d end_handle = %d", + start_handle, end_handle); + } + } + + + } + } + } while (p_sdp_rec); + } + + if ( p_srvc_cb != NULL) + { + /* start discover primary service */ + bta_gattc_explore_srvc(cb_data->sdp_conn_id, p_srvc_cb); + } + else + { + APPL_TRACE_ERROR("GATT service discovery is done on unknown connection"); + } + + /* both were allocated in bta_gattc_sdp_service_disc */ + osi_free(cb_data->p_sdp_db); + osi_free(cb_data); +} +#endif ///SDP_INCLUDED == TRUE + +/******************************************************************************* +** +** Function bta_gattc_sdp_service_disc +** +** Description Start DSP Service Discovert +** +** Returns void +** +*******************************************************************************/ +#if (SDP_INCLUDED == TRUE) +static tBTA_GATT_STATUS bta_gattc_sdp_service_disc(UINT16 conn_id, tBTA_GATTC_SERV *p_server_cb) +{ + tSDP_UUID uuid; + UINT16 num_attrs = 2; + UINT16 attr_list[2]; + + memset (&uuid, 0, sizeof(tSDP_UUID)); + + uuid.len = LEN_UUID_16; + uuid.uu.uuid16 = UUID_PROTOCOL_ATT; + + /* + * On success, cb_data will be freed inside bta_gattc_sdp_callback, + * otherwise it will be freed within this function. + */ + tBTA_GATTC_CB_DATA *cb_data = + (tBTA_GATTC_CB_DATA *)osi_malloc(sizeof(tBTA_GATTC_CB_DATA)); + cb_data->p_sdp_db = (tSDP_DISCOVERY_DB *)osi_malloc(BTA_GATT_SDP_DB_SIZE); + if (!cb_data || cb_data->p_sdp_db) { + APPL_TRACE_WARNING("%s(), no resource.", __func__); + return BTA_GATT_NO_RESOURCES; + } + attr_list[0] = ATTR_ID_SERVICE_CLASS_ID_LIST; + attr_list[1] = ATTR_ID_PROTOCOL_DESC_LIST; + + SDP_InitDiscoveryDb(cb_data->p_sdp_db, BTA_GATT_SDP_DB_SIZE, 1, + &uuid, num_attrs, attr_list); + + if (!SDP_ServiceSearchAttributeRequest2(p_server_cb->server_bda, + cb_data->p_sdp_db, &bta_gattc_sdp_callback, cb_data)) + { + osi_free(cb_data->p_sdp_db); + osi_free(cb_data); + return BTA_GATT_ERROR; + } + + cb_data->sdp_conn_id = conn_id; + return BTA_GATT_OK; +} +#endif ///SDP_INCLUDED == TRUE + +/******************************************************************************* +** +** Function bta_gattc_disc_res_cback +** bta_gattc_disc_cmpl_cback +** +** Description callback functions to GATT client stack. +** +** Returns void +** +*******************************************************************************/ +void bta_gattc_disc_res_cback (UINT16 conn_id, tGATT_DISC_TYPE disc_type, tGATT_DISC_RES *p_data) +{ + tBTA_GATTC_SERV *p_srvc_cb = NULL; + BOOLEAN pri_srvc; + tBTA_GATTC_CLCB *p_clcb = bta_gattc_find_clcb_by_conn_id(conn_id); + + if (bta_gattc_cb.auto_disc == FALSE) { + return; + } + + p_srvc_cb = bta_gattc_find_scb_by_cid(conn_id); + + if (p_srvc_cb != NULL && p_clcb != NULL && p_clcb->state == BTA_GATTC_DISCOVER_ST) { + p_srvc_cb->total_attr++; + switch (disc_type) { + case GATT_DISC_SRVC_ALL: + /* discover services result, add services into a service list */ + bta_gattc_add_srvc_to_list(p_srvc_cb, + p_data->handle, + p_data->value.group_value.e_handle, + p_data->value.group_value.service_type, + TRUE); + + break; + case GATT_DISC_SRVC_BY_UUID: + bta_gattc_add_srvc_to_list(p_srvc_cb, + p_data->handle, + p_data->value.group_value.e_handle, + p_data->value.group_value.service_type, + TRUE); + break; + + case GATT_DISC_INC_SRVC: + /* add included service into service list if it's secondary or it never showed up + in the primary service search */ + pri_srvc = bta_gattc_srvc_in_list(p_srvc_cb, + p_data->value.incl_service.s_handle, + p_data->value.incl_service.e_handle, + p_data->value.incl_service.service_type); + + if (!pri_srvc) { + bta_gattc_add_srvc_to_list(p_srvc_cb, + p_data->value.incl_service.s_handle, + p_data->value.incl_service.e_handle, + p_data->value.incl_service.service_type, + FALSE); + } + /* add into database */ + bta_gattc_add_attr_to_cache(p_srvc_cb, + p_data->handle, + &p_data->value.incl_service.service_type, + pri_srvc, + p_data->value.incl_service.s_handle, + p_data->value.incl_service.e_handle, + BTA_GATTC_ATTR_TYPE_INCL_SRVC); + break; + + case GATT_DISC_CHAR: + /* add char value into database */ + bta_gattc_add_char_to_list(p_srvc_cb, + p_data->handle, + p_data->value.dclr_value.val_handle, + p_data->value.dclr_value.char_uuid, + p_data->value.dclr_value.char_prop); + break; + + case GATT_DISC_CHAR_DSCPT: + bta_gattc_add_attr_to_cache(p_srvc_cb, + p_data->handle, + &p_data->type, + 0, + 0 /* incl_srvc_s_handle */, + 0 /* incl_srvc_e_handle */, + BTA_GATTC_ATTR_TYPE_CHAR_DESCR); + break; + } + } +} +void bta_gattc_disc_cmpl_cback (UINT16 conn_id, tGATT_DISC_TYPE disc_type, tGATT_STATUS status) +{ + tBTA_GATTC_SERV *p_srvc_cb; + tBTA_GATTC_CLCB *p_clcb = bta_gattc_find_clcb_by_conn_id(conn_id); + + if (bta_gattc_cb.auto_disc == FALSE) { + return; + } + + if ( p_clcb && (status != GATT_SUCCESS || p_clcb->status != GATT_SUCCESS) ) { + if (status == GATT_SUCCESS) { + p_clcb->status = status; + } + bta_gattc_sm_execute(p_clcb, BTA_GATTC_DISCOVER_CMPL_EVT, NULL); + return; + } + p_srvc_cb = bta_gattc_find_scb_by_cid(conn_id); + + if (p_srvc_cb != NULL) { + switch (disc_type) { + case GATT_DISC_SRVC_ALL: + case GATT_DISC_SRVC_BY_UUID: +#if (defined BTA_GATT_DEBUG && BTA_GATT_DEBUG == TRUE) + bta_gattc_display_explore_record(p_srvc_cb->p_srvc_list, p_srvc_cb->next_avail_idx); +#endif + bta_gattc_explore_srvc(conn_id, p_srvc_cb); + break; + + case GATT_DISC_INC_SRVC: + bta_gattc_incl_srvc_disc_cmpl(conn_id, p_srvc_cb); + + break; + + case GATT_DISC_CHAR: +#if (defined BTA_GATT_DEBUG && BTA_GATT_DEBUG == TRUE) + bta_gattc_display_explore_record(p_srvc_cb->p_srvc_list, p_srvc_cb->next_avail_idx); +#endif + bta_gattc_char_disc_cmpl(conn_id, p_srvc_cb); + break; + + case GATT_DISC_CHAR_DSCPT: + bta_gattc_char_dscpt_disc_cmpl(conn_id, p_srvc_cb); + break; + } + } +} +/******************************************************************************* +** +** Function bta_gattc_search_service +** +** Description search local cache for matching service record. +** +** Returns FALSE if map can not be found. +** +*******************************************************************************/ +void bta_gattc_search_service(tBTA_GATTC_CLCB *p_clcb, tBT_UUID *p_uuid) +{ + tBTA_GATTC cb_data; + + if (!p_clcb->p_srcb->p_srvc_cache || list_is_empty(p_clcb->p_srcb->p_srvc_cache)) { + return; + } + + for (list_node_t *sn = list_begin(p_clcb->p_srcb->p_srvc_cache); + sn != list_end(p_clcb->p_srcb->p_srvc_cache); sn = list_next(sn)) { + tBTA_GATTC_SERVICE *p_cache = list_node(sn); + + if (!bta_gattc_uuid_compare(p_uuid, &p_cache->uuid, FALSE)) { + continue; + } + +#if (defined BTA_GATT_DEBUG && BTA_GATT_DEBUG == TRUE) + APPL_TRACE_DEBUG("found service [0x%04x], inst[%d] handle [%d]", + p_cache->uuid.uu.uuid16, + p_cache->handle, + p_cache->s_handle); +#endif + if (!p_clcb->p_rcb->p_cback) { + continue; + } + + memset(&cb_data, 0, sizeof(tBTA_GATTC)); + + cb_data.srvc_res.conn_id = p_clcb->bta_conn_id; + cb_data.srvc_res.service_uuid.inst_id = p_cache->handle; + cb_data.srvc_res.start_handle = p_cache->s_handle; + cb_data.srvc_res.end_handle = p_cache->e_handle; + cb_data.srvc_res.is_primary = p_cache->is_primary; + memcpy(&cb_data.srvc_res.service_uuid.uuid, &p_cache->uuid, sizeof(tBT_UUID)); + (* p_clcb->p_rcb->p_cback)(BTA_GATTC_SEARCH_RES_EVT, &cb_data); + } +} + +list_t* bta_gattc_get_services_srcb(tBTA_GATTC_SERV *p_srcb) { + if (!p_srcb || !p_srcb->p_srvc_cache || list_is_empty(p_srcb->p_srvc_cache)) { + return NULL; + } + + return p_srcb->p_srvc_cache; +} + +const list_t* bta_gattc_get_services(UINT16 conn_id) { + tBTA_GATTC_CLCB *p_clcb = bta_gattc_find_clcb_by_conn_id(conn_id); + + if (p_clcb == NULL ) { + return NULL; + } + + tBTA_GATTC_SERV *p_srcb = p_clcb->p_srcb; + + return bta_gattc_get_services_srcb(p_srcb); +} + +tBTA_GATTC_SERVICE* bta_gattc_find_matching_service(const list_t *services, UINT16 handle) { + if (!services || list_is_empty(services)) { + return NULL; + } + + for (list_node_t *sn = list_begin(services); + sn != list_end(services); sn = list_next(sn)) { + tBTA_GATTC_SERVICE *service = list_node(sn); + + if (handle >= service->s_handle && handle <= service->e_handle) { + return service; + } + } + + return NULL; +} + +const tBTA_GATTC_SERVICE* bta_gattc_get_service_for_handle_srcb(tBTA_GATTC_SERV *p_srcb, UINT16 handle) +{ + const list_t *services = bta_gattc_get_services_srcb(p_srcb); + + return bta_gattc_find_matching_service(services, handle); +} + +const tBTA_GATTC_SERVICE* bta_gattc_get_service_for_handle(UINT16 conn_id, UINT16 handle) +{ + const list_t *services = bta_gattc_get_services(conn_id); + + return bta_gattc_find_matching_service(services, handle); +} + +tBTA_GATTC_CHARACTERISTIC* bta_gattc_get_characteristic_srcb(tBTA_GATTC_SERV *p_srcb, UINT16 handle) +{ + const tBTA_GATTC_SERVICE* service = bta_gattc_get_service_for_handle_srcb(p_srcb, handle); + + if (!service) { + return NULL; + } + + for (list_node_t *cn = list_begin(service->characteristics); + cn != list_end(service->characteristics); cn = list_next(cn)) { + tBTA_GATTC_CHARACTERISTIC *p_char = list_node(cn); + if (handle == p_char->handle) { + return p_char; + } + } + + return NULL; +} + +tBTA_GATTC_CHARACTERISTIC* bta_gattc_get_characteristic(UINT16 conn_id, UINT16 handle) +{ + tBTA_GATTC_CLCB *p_clcb = bta_gattc_find_clcb_by_conn_id(conn_id); + + if (p_clcb == NULL ) { + return NULL; + } + + tBTA_GATTC_SERV *p_srcb = p_clcb->p_srcb; + return bta_gattc_get_characteristic_srcb(p_srcb, handle); +} + +tBTA_GATTC_DESCRIPTOR* bta_gattc_get_descriptor_srcb(tBTA_GATTC_SERV *p_srcb, UINT16 handle) +{ + const tBTA_GATTC_SERVICE* service = bta_gattc_get_service_for_handle_srcb(p_srcb, handle); + + if (!service) { + return NULL; + } + + for (list_node_t *cn = list_begin(service->characteristics); + cn != list_end(service->characteristics); cn = list_next(cn)) { + tBTA_GATTC_CHARACTERISTIC *p_char = list_node(cn); + for (list_node_t *dn = list_begin(p_char->descriptors); + dn != list_end(p_char->descriptors); dn = list_next(dn)) { + tBTA_GATTC_DESCRIPTOR *p_desc = list_node(dn); + if (handle == p_desc->handle) { + return p_desc; + } + } + } + + return NULL; +} + +tBTA_GATTC_DESCRIPTOR* bta_gattc_get_descriptor(UINT16 conn_id, UINT16 handle) +{ + tBTA_GATTC_CLCB *p_clcb = bta_gattc_find_clcb_by_conn_id(conn_id); + + if (p_clcb == NULL ) { + return NULL; + } + + tBTA_GATTC_SERV *p_srcb = p_clcb->p_srcb; + return bta_gattc_get_descriptor_srcb(p_srcb, handle); +} + +void bta_gattc_get_service_with_uuid(UINT16 conn_id, tBT_UUID *svc_uuid, + btgatt_db_element_t **svc_db, + UINT16 *count) +{ + const list_t* svc = bta_gattc_get_services(conn_id); + if(!svc) { + APPL_TRACE_WARNING("%s(), no service.", __func__); + *svc_db = NULL; + *count = 0; + return; + } + size_t db_size = list_length(svc); + void *buffer = osi_malloc(db_size*sizeof(btgatt_db_element_t)); + if (!buffer) { + APPL_TRACE_WARNING("%s(), no resource.", __func__); + *svc_db = NULL; + *count = 0; + return; + } + btgatt_db_element_t *curr_db_attr = buffer; + db_size = 0; + for (list_node_t *sn = list_begin(svc); + sn != list_end(svc); sn = list_next(sn)) { + tBTA_GATTC_SERVICE *p_cur_srvc = list_node(sn); + if (svc_uuid == NULL || bta_gattc_uuid_compare(svc_uuid, &p_cur_srvc->uuid, TRUE)) { + bta_gattc_fill_gatt_db_el(curr_db_attr, + p_cur_srvc->is_primary ? + BTGATT_DB_PRIMARY_SERVICE : + BTGATT_DB_SECONDARY_SERVICE, + 0 /* att_handle */, + p_cur_srvc->s_handle /* s_handle */, + p_cur_srvc->e_handle /* e_handle */, + p_cur_srvc->s_handle, + p_cur_srvc->uuid, + 0 /* prop */); + db_size++; + curr_db_attr++; + } + } + + *svc_db = buffer; + *count = db_size; +} + +/******************************************************************************* +** +** Function bta_gattc_fill_gatt_db_el +** +** Description fill a btgatt_db_element_t value +** +** Returns None. +** +*******************************************************************************/ +void bta_gattc_fill_gatt_db_el(btgatt_db_element_t *p_attr, + bt_gatt_db_attribute_type_t type, + UINT16 att_handle, + UINT16 s_handle, UINT16 e_handle, + UINT16 id, tBT_UUID uuid, UINT8 prop) +{ + p_attr->type = type; + p_attr->attribute_handle = att_handle; + p_attr->start_handle = s_handle; + p_attr->end_handle = e_handle; + p_attr->id = id; + p_attr->properties = prop; + bta_to_btif_uuid(&p_attr->uuid, &uuid); +} + +void bta_gattc_get_db_with_opration(UINT16 conn_id, + bt_gatt_get_db_op_t op, + UINT16 char_handle, + tBT_UUID *incl_uuid, + tBT_UUID *char_uuid, + tBT_UUID *descr_uuid, + UINT16 start_handle, UINT16 end_handle, + btgatt_db_element_t **char_db, + UINT16 *count) +{ + tBTA_GATTC_CLCB *p_clcb = bta_gattc_find_clcb_by_conn_id(conn_id); + + if (p_clcb == NULL) { + *count = 0; + *char_db = NULL; + return; + } + + tBTA_GATTC_SERV *p_srcb = p_clcb->p_srcb; + if (!p_srcb->p_srvc_cache || list_is_empty(p_srcb->p_srvc_cache)) { + APPL_TRACE_DEBUG("the service cache is empty."); + *count = 0; + *char_db = NULL; + return; + } + + size_t db_size = ((end_handle - start_handle + 1) < p_srcb->total_attr) ? (end_handle - start_handle + 1) : p_srcb->total_attr; + if (!db_size) { + APPL_TRACE_DEBUG("the db size is 0."); + *count = 0; + *char_db = NULL; + return; + } + + void *buffer = osi_malloc(db_size*sizeof(btgatt_db_element_t)); + + if (!buffer) { + APPL_TRACE_DEBUG("the buffer is NULL."); + *count = 0; + *char_db = NULL; + return; + } + btgatt_db_element_t *curr_db_attr = buffer; + db_size = 0; + for (list_node_t *sn = list_begin(p_srcb->p_srvc_cache); + sn != list_end(p_srcb->p_srvc_cache); sn = list_next(sn)) { + tBTA_GATTC_SERVICE *p_cur_srvc = list_node(sn); + + if (p_cur_srvc->e_handle < start_handle) { + continue; + } + + if (p_cur_srvc->s_handle > end_handle) { + break; + } + + if (op == GATT_OP_GET_INCLUDE_SVC) { + if (!p_cur_srvc->included_svc || list_is_empty(p_cur_srvc->included_svc)) + continue; + + for (list_node_t *isn = list_begin(p_cur_srvc->included_svc); + isn != list_end(p_cur_srvc->included_svc); isn = list_next(isn)) { + tBTA_GATTC_INCLUDED_SVC *p_isvc = list_node(isn); + + if (p_isvc->handle < start_handle) { + continue; + } + + if (p_isvc->handle > end_handle) { + *char_db = buffer; + *count = db_size; + return; + } + if (!incl_uuid || bta_gattc_uuid_compare(&p_isvc->uuid, incl_uuid, TRUE)) { + bta_gattc_fill_gatt_db_el(curr_db_attr, + BTGATT_DB_INCLUDED_SERVICE, + p_isvc->handle, + p_isvc->incl_srvc_s_handle /* s_handle */, + p_isvc->incl_srvc_e_handle /* e_handle */, + p_isvc->handle, + p_isvc->uuid, + 0 /* property */); + curr_db_attr++; + db_size++; + } + } + continue; + } + + if (!p_cur_srvc->characteristics || list_is_empty(p_cur_srvc->characteristics)) { + continue; + } + + for (list_node_t *cn = list_begin(p_cur_srvc->characteristics); + cn != list_end(p_cur_srvc->characteristics); cn = list_next(cn)) { + tBTA_GATTC_CHARACTERISTIC *p_char = list_node(cn); + if(op == GATT_OP_GET_ALL_CHAR || op == GATT_OP_GET_CHAR_BY_UUID) { + if (p_char->handle < start_handle) { + continue; + } + } + + if (p_char->handle > end_handle) { + *char_db = buffer; + *count = db_size; + return; + } + if ((op == GATT_OP_GET_ALL_CHAR || op == GATT_OP_GET_CHAR_BY_UUID) && + (char_uuid == NULL || bta_gattc_uuid_compare(&p_char->uuid, char_uuid, TRUE))) { + APPL_TRACE_DEBUG("%s(), uuid match.", __func__); + bta_gattc_fill_gatt_db_el(curr_db_attr, + BTGATT_DB_CHARACTERISTIC, + p_char->handle, + 0 /* s_handle */, + 0 /* e_handle */, + p_char->handle, + p_char->uuid, + p_char->properties); + curr_db_attr++; + db_size++; + continue; + } + + if (!p_char->descriptors || list_is_empty(p_char->descriptors)) { + continue; + } + + if ((op == GATT_OP_GET_DESCRI_BY_HANDLE || op == GATT_OP_GET_ALL_DESCRI) && (p_char->handle != char_handle)) { + continue; + } + + if ((op == GATT_OP_GET_DESCRI_BY_UUID) && + !bta_gattc_uuid_compare(&p_char->uuid, char_uuid, TRUE)) { + continue; + } + + if (op == GATT_OP_GET_ALL_DESCRI || op == GATT_OP_GET_DESCRI_BY_UUID || op == GATT_OP_GET_DESCRI_BY_HANDLE) { + for (list_node_t *dn = list_begin(p_char->descriptors); + dn != list_end(p_char->descriptors); dn = list_next(dn)) { + tBTA_GATTC_DESCRIPTOR *p_desc = list_node(dn); + + if (p_desc->handle < start_handle) { + continue; + } + if (p_desc->handle > end_handle) { + *char_db = buffer; + *count = db_size; + return; + } + if (((op == GATT_OP_GET_ALL_DESCRI || op == GATT_OP_GET_DESCRI_BY_UUID) && + (descr_uuid == NULL || bta_gattc_uuid_compare(&p_desc->uuid, descr_uuid, TRUE))) || + (op == GATT_OP_GET_DESCRI_BY_HANDLE && bta_gattc_uuid_compare(&p_desc->uuid, descr_uuid, TRUE))) { + bta_gattc_fill_gatt_db_el(curr_db_attr, + BTGATT_DB_DESCRIPTOR, + p_desc->handle, + 0 /* s_handle */, + 0 /* e_handle */, + p_desc->handle, + p_desc->uuid, + 0 /* property */); + curr_db_attr++; + db_size++; + } + } + } + + } + } + + *char_db = buffer; + *count = db_size; +} + +static size_t bta_gattc_get_db_size_with_type(list_t *services, + bt_gatt_db_attribute_type_t type, + tBT_UUID *char_uuid, + UINT16 start_handle, UINT16 end_handle) +{ + if (!services || list_is_empty(services)) { + return 0; + } + + size_t db_size = 0; + UINT16 svc_length = list_length(services) - 1; + + for (list_node_t *sn = list_begin(services); + sn != list_end(services); sn = list_next(sn), svc_length--) { + tBTA_GATTC_SERVICE *p_cur_srvc = list_node(sn); + + if (p_cur_srvc->e_handle < start_handle) { + continue; + } + + if (p_cur_srvc->s_handle > end_handle) { + break; + } + + if (type == BTGATT_DB_PRIMARY_SERVICE || type == BTGATT_DB_SECONDARY_SERVICE) { + if ((type == BTGATT_DB_PRIMARY_SERVICE && p_cur_srvc->is_primary) || + (type == BTGATT_DB_SECONDARY_SERVICE && !p_cur_srvc->is_primary)) { + // if the current service is the last service in the db, need to ensure the current service start handle is not less than the start_handle. + if (!svc_length) { + if (p_cur_srvc->s_handle >= start_handle) { + db_size++; + } + } else { + db_size++; + } + } + continue; + } + + if (p_cur_srvc->included_svc && (type == BTGATT_DB_INCLUDED_SERVICE)) { + for (list_node_t *isn = list_begin(p_cur_srvc->included_svc); + isn != list_end(p_cur_srvc->included_svc); isn = list_next(isn)) { + tBTA_GATTC_INCLUDED_SVC *p_isvc = list_node(isn); + + if (p_isvc->handle < start_handle) { + continue; + } + + if (p_isvc->handle > end_handle) { + return db_size; + } + db_size++; + } + continue; + } + + if (!p_cur_srvc->characteristics || list_is_empty(p_cur_srvc->characteristics)) { + continue; + } + + if (char_uuid != NULL) { + for (list_node_t *cn = list_begin(p_cur_srvc->characteristics); + cn != list_end(p_cur_srvc->characteristics); cn = list_next(cn)) { + tBTA_GATTC_CHARACTERISTIC *p_char = list_node(cn); + + if (p_char->handle < start_handle) { + continue; + } + + if (p_char->handle > end_handle) { + return db_size; + } + + if ((type == BTGATT_DB_CHARACTERISTIC) && bta_gattc_uuid_compare(&p_char->uuid, char_uuid, TRUE)) { + db_size++; + continue; + } + + if (p_char->descriptors && (type == BTGATT_DB_DESCRIPTOR) && bta_gattc_uuid_compare(&p_char->uuid, char_uuid, TRUE)) { + for (list_node_t *dn = list_begin(p_char->descriptors); + dn != list_end(p_char->descriptors); dn = list_next(dn)) { + tBTA_GATTC_DESCRIPTOR *p_desc = list_node(dn); + + if (p_desc->handle < start_handle) { + continue; + } + + if (p_desc->handle > end_handle) { + return db_size; + } + db_size++; + } + } + } + } else { + if (type == BTGATT_DB_CHARACTERISTIC) { + for (list_node_t *cn = list_begin(p_cur_srvc->characteristics); + cn != list_end(p_cur_srvc->characteristics); cn = list_next(cn)) { + tBTA_GATTC_CHARACTERISTIC *p_char = list_node(cn); + + if (p_char->handle < start_handle) { + continue; + } + + if (p_char->handle > end_handle) { + return db_size; + } + db_size++; + } + } + } + + } + + return db_size; +} + +/******************************************************************************* +** Returns number of elements inside db from start_handle to end_handle +*******************************************************************************/ +static size_t bta_gattc_get_db_size(list_t *services, + UINT16 start_handle, UINT16 end_handle) +{ + if (!services || list_is_empty(services)) { + return 0; + } + + size_t db_size = 0; + UINT16 svc_length = list_length(services) - 1; + for (list_node_t *sn = list_begin(services); + sn != list_end(services); sn = list_next(sn), svc_length--) { + tBTA_GATTC_SERVICE *p_cur_srvc = list_node(sn); + + if (p_cur_srvc->e_handle < start_handle) { + continue; + } + + if (p_cur_srvc->s_handle > end_handle) { + break; + } + + if (!svc_length) { + if (p_cur_srvc->s_handle >= start_handle) { + db_size++; + } + } else { + db_size++; + } + + if (!p_cur_srvc->characteristics || list_is_empty(p_cur_srvc->characteristics)) { + continue; + } + + for (list_node_t *cn = list_begin(p_cur_srvc->characteristics); + cn != list_end(p_cur_srvc->characteristics); cn = list_next(cn)) { + tBTA_GATTC_CHARACTERISTIC *p_char = list_node(cn); + + if (p_char->handle < start_handle) { + continue; + } + if (p_char->handle > end_handle) { + return db_size; + } + db_size++; + + if (p_char->descriptors) { + for (list_node_t *dn = list_begin(p_char->descriptors); + dn != list_end(p_char->descriptors); dn = list_next(dn)) { + tBTA_GATTC_DESCRIPTOR *p_desc = list_node(dn); + if (p_desc->handle < start_handle) { + continue; + } + if (p_desc->handle > end_handle) { + return db_size; + } + db_size++; + } + } + } + + if (p_cur_srvc->included_svc) { + for (list_node_t *isn = list_begin(p_cur_srvc->included_svc); + isn != list_end(p_cur_srvc->included_svc); isn = list_next(isn)) { + tBTA_GATTC_INCLUDED_SVC *p_isvc = list_node(isn); + + if (p_isvc->handle < start_handle) { + continue; + } + + if (p_isvc->handle > end_handle) { + return db_size; + } + db_size++; + } + } + } + + return db_size; +} + +void bta_gattc_get_db_size_handle(UINT16 conn_id, UINT16 start_handle, UINT16 end_handle, UINT16 *count) +{ + tBTA_GATTC_CLCB *p_clcb = bta_gattc_find_clcb_by_conn_id(conn_id); + + if (p_clcb == NULL) { + *count = 0; + return; + } + + tBTA_GATTC_SERV *p_srcb = p_clcb->p_srcb; + if (!p_srcb->p_srvc_cache || list_is_empty(p_srcb->p_srvc_cache)) { + *count = 0; + return; + } + + *count = bta_gattc_get_db_size(p_srcb->p_srvc_cache, start_handle, end_handle); +} + +void bta_gattc_get_db_size_with_type_handle(UINT16 conn_id, bt_gatt_db_attribute_type_t type, + UINT16 start_handle, UINT16 end_handle, UINT16 char_handle, UINT16 *count) +{ + tBTA_GATTC_CLCB *p_clcb = bta_gattc_find_clcb_by_conn_id(conn_id); + + if (p_clcb == NULL) { + *count = 0; + return; + } + + tBTA_GATTC_SERV *p_srcb = p_clcb->p_srcb; + if (!p_srcb->p_srvc_cache || list_is_empty(p_srcb->p_srvc_cache)) { + *count = 0; + return; + } + + if (type == BTGATT_DB_DESCRIPTOR) { + if (char_handle == BTA_GATTC_INVALID_HANDLE) { + *count = 0; + return; + } else { + tBTA_GATTC_CHARACTERISTIC *char_db = bta_gattc_get_characteristic(conn_id, char_handle); + *count = char_db ? list_length(char_db->descriptors) : 0; + return; + } + } + *count = bta_gattc_get_db_size_with_type(p_srcb->p_srvc_cache, type, NULL, start_handle, end_handle); + +} + +/******************************************************************************* +** +** Function bta_gattc_get_gatt_db_impl +** +** Description copy the server GATT database into db parameter. +** +** Parameters p_srvc_cb: server. +** db: output parameter which will contain GATT database copy. +** Caller is responsible for freeing it. +** count: output parameter which will contain number of +** elements in database. +** +** Returns None. +** +*******************************************************************************/ +static void bta_gattc_get_gatt_db_impl(tBTA_GATTC_SERV *p_srvc_cb, + UINT16 start_handle, UINT16 end_handle, + btgatt_db_element_t **db, + UINT16 *count) +{ + APPL_TRACE_DEBUG("%s: start_handle 0x%04x, end_handle 0x%04x", + __func__, start_handle, end_handle); + + if (!p_srvc_cb->p_srvc_cache || list_is_empty(p_srvc_cb->p_srvc_cache)) { + *count = 0; + *db = NULL; + return; + } + + size_t db_size = bta_gattc_get_db_size(p_srvc_cb->p_srvc_cache, start_handle, end_handle); + + void* buffer = osi_malloc(db_size * sizeof(btgatt_db_element_t)); + if (!buffer) { + APPL_TRACE_WARNING("%s(), no resource.", __func__); + *db = NULL; + *count = 0; + return; + } + btgatt_db_element_t *curr_db_attr = buffer; + + for (list_node_t *sn = list_begin(p_srvc_cb->p_srvc_cache); + sn != list_end(p_srvc_cb->p_srvc_cache); sn = list_next(sn)) { + tBTA_GATTC_SERVICE *p_cur_srvc = list_node(sn); + + if (p_cur_srvc->e_handle < start_handle) { + continue; + } + + if (p_cur_srvc->s_handle > end_handle) { + break; + } + + bta_gattc_fill_gatt_db_el(curr_db_attr, + p_cur_srvc->is_primary ? + BTGATT_DB_PRIMARY_SERVICE : + BTGATT_DB_SECONDARY_SERVICE, + 0 /* att_handle */, + p_cur_srvc->s_handle, + p_cur_srvc->e_handle, + p_cur_srvc->s_handle, + p_cur_srvc->uuid, + 0 /* prop */); + curr_db_attr++; + + if (p_cur_srvc->characteristics && !list_is_empty(p_cur_srvc->characteristics)) { + + for (list_node_t *cn = list_begin(p_cur_srvc->characteristics); + cn != list_end(p_cur_srvc->characteristics); cn = list_next(cn)) { + tBTA_GATTC_CHARACTERISTIC *p_char = list_node(cn); + + if (p_char->handle < start_handle) { + continue; + } + + if (p_char->handle > end_handle) { + *db = buffer; + *count = db_size; + return; + } + bta_gattc_fill_gatt_db_el(curr_db_attr, + BTGATT_DB_CHARACTERISTIC, + p_char->handle, + 0 /* s_handle */, + 0 /* e_handle */, + p_char->handle, + p_char->uuid, + p_char->properties); + curr_db_attr++; + + if (!p_char->descriptors || list_is_empty(p_char->descriptors)) { + continue; + } + + for (list_node_t *dn = list_begin(p_char->descriptors); + dn != list_end(p_char->descriptors); dn = list_next(dn)) { + tBTA_GATTC_DESCRIPTOR *p_desc = list_node(dn); + + if (p_desc->handle < start_handle) { + continue; + } + + if (p_desc->handle > end_handle) { + *db = buffer; + *count = db_size; + return; + } + bta_gattc_fill_gatt_db_el(curr_db_attr, + BTGATT_DB_DESCRIPTOR, + p_desc->handle, + 0 /* s_handle */, + 0 /* e_handle */, + p_desc->handle, + p_desc->uuid, + 0 /* property */); + curr_db_attr++; + } + } + } + + if (!p_cur_srvc->included_svc || list_is_empty(p_cur_srvc->included_svc)) { + continue; + } + + for (list_node_t *isn = list_begin(p_cur_srvc->included_svc); + isn != list_end(p_cur_srvc->included_svc); isn = list_next(isn)) { + tBTA_GATTC_INCLUDED_SVC *p_isvc = list_node(isn); + + if (p_isvc->handle < start_handle) { + continue; + } + + if (p_isvc->handle > end_handle) { + *db = buffer; + *count = db_size; + return; + } + bta_gattc_fill_gatt_db_el(curr_db_attr, + BTGATT_DB_INCLUDED_SERVICE, + p_isvc->handle, + 0 /* s_handle */, + 0 /* e_handle */, + p_isvc->handle, + p_isvc->uuid, + 0 /* property */); + curr_db_attr++; + } + } + + *db = buffer; + *count = db_size; +} + +/******************************************************************************* +** +** Function bta_gattc_get_gatt_db +** +** Description copy the server GATT database into db parameter. +** +** Parameters conn_id: connection ID which identify the server. +** db: output parameter which will contain GATT database copy. +** Caller is responsible for freeing it. +** count: number of elements in database. +** +** Returns None. +** +*******************************************************************************/ +void bta_gattc_get_gatt_db(UINT16 conn_id, UINT16 start_handle, UINT16 end_handle, btgatt_db_element_t **db, UINT16 *count) +{ + tBTA_GATTC_CLCB *p_clcb = bta_gattc_find_clcb_by_conn_id(conn_id); + + if (p_clcb == NULL) { + APPL_TRACE_ERROR("Unknown conn ID: %d", conn_id); + return; + } + + if (p_clcb->state != BTA_GATTC_CONN_ST) { + APPL_TRACE_ERROR("server cache not available, CLCB state = %d", + p_clcb->state); + return; + } + + if (!p_clcb->p_srcb || p_clcb->p_srcb->p_srvc_list || /* no active discovery */ + !p_clcb->p_srcb->p_srvc_cache) { + APPL_TRACE_ERROR("No server cache available"); + return; + } + + bta_gattc_get_gatt_db_impl(p_clcb->p_srcb, start_handle, end_handle, db, count); +} + +/******************************************************************************* +** +** Function bta_gattc_rebuild_cache +** +** Description rebuild server cache from NV cache. +** +** Parameters +** +** Returns None. +** +*******************************************************************************/ +void bta_gattc_rebuild_cache(tBTA_GATTC_SERV *p_srvc_cb, UINT16 num_attr, + tBTA_GATTC_NV_ATTR *p_attr) +{ + /* first attribute loading, initialize buffer */ + APPL_TRACE_DEBUG("%s: bta_gattc_rebuild_cache, num_attr = %d", __func__, num_attr); + + list_free(p_srvc_cb->p_srvc_cache); + p_srvc_cb->p_srvc_cache = NULL; + + while (num_attr > 0 && p_attr != NULL) { + switch (p_attr->attr_type) { + case BTA_GATTC_ATTR_TYPE_SRVC: + bta_gattc_add_srvc_to_cache(p_srvc_cb, + p_attr->s_handle, + p_attr->e_handle, + &p_attr->uuid, + p_attr->is_primary); + break; + + case BTA_GATTC_ATTR_TYPE_CHAR: + //TODO(jpawlowski): store decl_handle properly. + bta_gattc_add_char_to_cache(p_srvc_cb, + p_attr->s_handle, + p_attr->s_handle, + &p_attr->uuid, + p_attr->prop); + break; + + case BTA_GATTC_ATTR_TYPE_CHAR_DESCR: + bta_gattc_add_attr_to_cache(p_srvc_cb, + p_attr->s_handle, + &p_attr->uuid, + p_attr->prop, + p_attr->incl_srvc_s_handle, + p_attr->incl_srvc_e_handle, + p_attr->attr_type); + break; + case BTA_GATTC_ATTR_TYPE_INCL_SRVC: + bta_gattc_add_attr_to_cache(p_srvc_cb, + p_attr->s_handle, + &p_attr->uuid, + p_attr->prop, + p_attr->incl_srvc_s_handle, + p_attr->incl_srvc_e_handle, + p_attr->attr_type); + break; + } + p_attr ++; + num_attr --; + } +} + +/******************************************************************************* +** +** Function bta_gattc_fill_nv_attr +** +** Description fill a NV attribute entry value +** +** Returns None. +** +*******************************************************************************/ +void bta_gattc_fill_nv_attr(tBTA_GATTC_NV_ATTR *p_attr, UINT8 type, UINT16 s_handle, + UINT16 e_handle, tBT_UUID uuid, UINT8 prop, UINT16 incl_srvc_s_handle, + UINT16 incl_srvc_e_handle, BOOLEAN is_primary) +{ + p_attr->s_handle = s_handle; + p_attr->e_handle = e_handle; + p_attr->attr_type = type; + p_attr->is_primary = is_primary; + p_attr->id = 0; + p_attr->prop = prop; + p_attr->incl_srvc_s_handle = incl_srvc_s_handle; + p_attr->incl_srvc_e_handle = incl_srvc_e_handle; + + memcpy(&p_attr->uuid, &uuid, sizeof(tBT_UUID)); +} +/******************************************************************************* +** +** Function bta_gattc_cache_save +** +** Description save the server cache into NV +** +** Returns None. +** +*******************************************************************************/ +void bta_gattc_cache_save(tBTA_GATTC_SERV *p_srvc_cb, UINT16 conn_id) +{ + if (!p_srvc_cb->p_srvc_cache || list_is_empty(p_srvc_cb->p_srvc_cache)) { + return; + } + + int i = 0; + size_t db_size = bta_gattc_get_db_size(p_srvc_cb->p_srvc_cache, 0x0000, 0xFFFF); + tBTA_GATTC_NV_ATTR *nv_attr = osi_malloc(db_size * sizeof(tBTA_GATTC_NV_ATTR)); + // This step is very importent, if not clear the memory, the hasy key base on the attribute case will be not corret. + if (nv_attr != NULL) { + memset(nv_attr, 0, db_size * sizeof(tBTA_GATTC_NV_ATTR)); + } + + if (!nv_attr) { + APPL_TRACE_WARNING("%s(), no resource.", __func__); + return; + } + for (list_node_t *sn = list_begin(p_srvc_cb->p_srvc_cache); + sn != list_end(p_srvc_cb->p_srvc_cache); sn = list_next(sn)) { + tBTA_GATTC_SERVICE *p_cur_srvc = list_node(sn); + + bta_gattc_fill_nv_attr(&nv_attr[i++], + BTA_GATTC_ATTR_TYPE_SRVC, + p_cur_srvc->s_handle, + p_cur_srvc->e_handle, + p_cur_srvc->uuid, + 0 /* properties */, + 0 /* incl_srvc_s_handle */, + 0 /* incl_srvc_e_handle */, + p_cur_srvc->is_primary); + } + + for (list_node_t *sn = list_begin(p_srvc_cb->p_srvc_cache); + sn != list_end(p_srvc_cb->p_srvc_cache); sn = list_next(sn)) { + tBTA_GATTC_SERVICE *p_cur_srvc = list_node(sn); + + if (!p_cur_srvc->characteristics || list_is_empty(p_cur_srvc->characteristics)) { + continue; + } + + for (list_node_t *cn = list_begin(p_cur_srvc->characteristics); + cn != list_end(p_cur_srvc->characteristics); cn = list_next(cn)) { + tBTA_GATTC_CHARACTERISTIC *p_char = list_node(cn); + + bta_gattc_fill_nv_attr(&nv_attr[i++], + BTA_GATTC_ATTR_TYPE_CHAR, + p_char->handle, + 0, + p_char->uuid, + p_char->properties, + 0 /* incl_srvc_s_handle */, + 0 /* incl_srvc_e_handle */, + FALSE); + + if (!p_char->descriptors || list_is_empty(p_char->descriptors)) { + continue; + } + + for (list_node_t *dn = list_begin(p_char->descriptors); + dn != list_end(p_char->descriptors); dn = list_next(dn)) { + tBTA_GATTC_DESCRIPTOR *p_desc = list_node(dn); + + bta_gattc_fill_nv_attr(&nv_attr[i++], + BTA_GATTC_ATTR_TYPE_CHAR_DESCR, + p_desc->handle, + 0, + p_desc->uuid, + 0 /* properties */, + 0 /* incl_srvc_s_handle */, + 0 /* incl_srvc_e_handle */, + FALSE); + } + } + + if (!p_cur_srvc->included_svc || list_is_empty(p_cur_srvc->included_svc)) { + continue; + } + + for (list_node_t *an = list_begin(p_cur_srvc->included_svc); + an != list_end(p_cur_srvc->included_svc); an = list_next(an)) { + tBTA_GATTC_INCLUDED_SVC *p_isvc = list_node(an); + + bta_gattc_fill_nv_attr(&nv_attr[i++], + BTA_GATTC_ATTR_TYPE_INCL_SRVC, + p_isvc->handle, + 0, + p_isvc->uuid, + 0 /* properties */, + p_isvc->included_service->s_handle, + p_isvc->included_service->e_handle, + FALSE); + } + } + + /* TODO: Gattc cache write/read need to be added in IDF 3.1*/ + bta_gattc_cache_write(p_srvc_cb->server_bda, db_size, nv_attr); + osi_free(nv_attr); +} + +/******************************************************************************* +** +** Function bta_gattc_cache_load +** +** Description Load GATT cache from storage for server. +** +** Parameter p_clcb: pointer to server clcb, that will +** be filled from storage +** Returns true on success, false otherwise +** +*******************************************************************************/ +bool bta_gattc_cache_load(tBTA_GATTC_CLCB *p_clcb) +{ + /* open NV cache and send call in */ + tBTA_GATT_STATUS status = BTA_GATT_OK; + UINT8 index = 0; + tBTA_GATTC_NV_ATTR *attr = NULL; + + if ((status = bta_gattc_co_cache_open(p_clcb->p_srcb->server_bda, true, &index)) != BTA_GATT_OK) { + APPL_TRACE_DEBUG("%s(), gattc cache open fail, index = %x", __func__, index); + return false; + } + + size_t num_attr = bta_gattc_get_cache_attr_length(index) / sizeof(tBTA_GATTC_NV_ATTR); + + if (!num_attr) { + return false; + } + //don't forget to set the total attribute number. + p_clcb->p_srcb->total_attr = num_attr; + APPL_TRACE_DEBUG("%s(), index = %x, num_attr = %d", __func__, index, num_attr); + if ((attr = osi_malloc(sizeof(tBTA_GATTC_NV_ATTR) * num_attr)) == NULL) { + APPL_TRACE_ERROR("%s, No Memory.", __func__); + return false; + } + if ((status = bta_gattc_co_cache_load(attr, index)) != BTA_GATT_OK) { + APPL_TRACE_DEBUG("%s(), gattc cache load fail, status = %x", __func__, status); + return false; + } + p_clcb->searched_service_source = BTA_GATTC_SERVICE_INFO_FROM_NVS_FLASH; + bta_gattc_rebuild_cache(p_clcb->p_srcb, num_attr, attr); + //free the attr buffer after used. + osi_free(attr); + return true; +} + +/******************************************************************************* +** +** Function bta_gattc_cache_write +** +** Description This callout function is executed by GATT when a server cache +** is available to save. +** +** Parameter server_bda: server bd address of this cache belongs to +** num_attr: number of attribute to be save. +** attr: pointer to the list of attributes to save. +** Returns +** +*******************************************************************************/ +static void bta_gattc_cache_write(BD_ADDR server_bda, UINT16 num_attr, + tBTA_GATTC_NV_ATTR *attr) +{ + bta_gattc_co_cache_save(server_bda, num_attr, attr); +} + +/******************************************************************************* +** +** Function bta_gattc_cache_reset +** +** Description This callout function is executed by GATTC to reset cache in +** application +** +** Parameter server_bda: server bd address of this cache belongs to +** +** Returns void. +** +*******************************************************************************/ +void bta_gattc_cache_reset(BD_ADDR server_bda) +{ + BTIF_TRACE_DEBUG("%s", __func__); + char fname[255] = {0}; + bta_gattc_generate_cache_file_name(fname, server_bda); + bta_gattc_co_cache_reset(server_bda); + //unlink(fname); +} + +//#endif /* GATTC_CACHE_NVS */ +#endif /* BTA_GATT_INCLUDED */ diff --git a/lib/bt/host/bluedroid/bta/gatt/bta_gattc_ci.c b/lib/bt/host/bluedroid/bta/gatt/bta_gattc_ci.c new file mode 100644 index 00000000..cfe08560 --- /dev/null +++ b/lib/bt/host/bluedroid/bta/gatt/bta_gattc_ci.c @@ -0,0 +1,137 @@ +/****************************************************************************** + * + * Copyright (C) 2010-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This is the implementation file for the GATT call-in functions. + * + ******************************************************************************/ + +#include "common/bt_target.h" + +#if defined(GATTC_INCLUDED) && (GATTC_INCLUDED == TRUE) + +#include + +#include "bta/bta_api.h" +#include "bta/bta_sys.h" +#include "bta/bta_gattc_ci.h" +#include "bta/utl.h" +#include "osi/allocator.h" + + +/******************************************************************************* +** +** Function bta_gattc_ci_cache_open +** +** Description This function sends an event to indicate server cache open +** completed. +** +** Parameters server_bda - server BDA of this cache. +** status - BTA_GATT_OK if full buffer of data, +** BTA_GATT_FAIL if an error has occurred. +** +** Returns void +** +*******************************************************************************/ +void bta_gattc_ci_cache_open(BD_ADDR server_bda, UINT16 evt, tBTA_GATT_STATUS status, + UINT16 conn_id) +{ + tBTA_GATTC_CI_EVT *p_evt; + UNUSED(server_bda); + + if ((p_evt = (tBTA_GATTC_CI_EVT *) osi_malloc(sizeof(tBTA_GATTC_CI_EVT))) != NULL) { + p_evt->hdr.event = evt; + p_evt->hdr.layer_specific = conn_id; + + p_evt->status = status; + bta_sys_sendmsg(p_evt); + } +} + +/******************************************************************************* +** +** Function bta_gattc_ci_cache_load +** +** Description This function sends an event to BTA indicating the phone has +** load the servere cache and ready to send it to the stack. +** +** Parameters server_bda - server BDA of this cache. +** num_bytes_read - number of bytes read into the buffer +** specified in the read callout-function. +** status - BTA_GATT_OK if full buffer of data, +** BTA_GATT_FAIL if an error has occurred. +** +** Returns void +** +*******************************************************************************/ +void bta_gattc_ci_cache_load(BD_ADDR server_bda, UINT16 evt, UINT16 num_attr, + tBTA_GATTC_NV_ATTR *p_attr, tBTA_GATT_STATUS status, + UINT16 conn_id) +{ + tBTA_GATTC_CI_LOAD *p_evt; + UNUSED(server_bda); + + if ((p_evt = (tBTA_GATTC_CI_LOAD *) osi_malloc(sizeof(tBTA_GATTC_CI_LOAD))) != NULL) { + memset(p_evt, 0, sizeof(tBTA_GATTC_CI_LOAD)); + + p_evt->hdr.event = evt; + p_evt->hdr.layer_specific = conn_id; + + p_evt->status = status; + p_evt->num_attr = (num_attr > BTA_GATTC_NV_LOAD_MAX) ? BTA_GATTC_NV_LOAD_MAX : num_attr; + + if (p_evt->num_attr > 0 && p_attr != NULL) { + memcpy(p_evt->attr, p_attr, p_evt->num_attr * sizeof(tBTA_GATTC_NV_ATTR)); + } + + bta_sys_sendmsg(p_evt); + } +} + +/******************************************************************************* +** +** Function bta_gattc_ci_cache_save +** +** Description This function sends an event to BTA indicating the phone has +** save the servere cache. +** +** Parameters server_bda - server BDA of this cache. +** evt - callin event code. +** status - BTA_GATT_OK if full buffer of data, +** BTA_GATT_ERROR if an error has occurred. +*8 conn_id - for this NV operation for. +** +** Returns void +** +*******************************************************************************/ +void bta_gattc_ci_cache_save(BD_ADDR server_bda, UINT16 evt, tBTA_GATT_STATUS status, + UINT16 conn_id) +{ + tBTA_GATTC_CI_EVT *p_evt; + UNUSED(server_bda); + + if ((p_evt = (tBTA_GATTC_CI_EVT *) osi_malloc(sizeof(tBTA_GATTC_CI_EVT))) != NULL) { + p_evt->hdr.event = evt; + p_evt->hdr.layer_specific = conn_id; + + p_evt->status = status; + bta_sys_sendmsg(p_evt); + } +} +#endif /* GATTC_INCLUDED */ diff --git a/lib/bt/host/bluedroid/bta/gatt/bta_gattc_co.c b/lib/bt/host/bluedroid/bta/gatt/bta_gattc_co.c new file mode 100644 index 00000000..8ec3a55d --- /dev/null +++ b/lib/bt/host/bluedroid/bta/gatt/bta_gattc_co.c @@ -0,0 +1,693 @@ +/****************************************************************************** + * + * Copyright (C) 2009-2013 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ +#ifdef BT_SUPPORT_NVM +#include +#endif /* BT_SUPPORT_NVM */ +#include +#include +#include "bta/bta_gattc_co.h" +#include "bta/bta_gattc_ci.h" + +// #include "btif_util.h" +#include "btm_int.h" +#include "nvs.h" +#include "nvs_flash.h" +#include "osi/list.h" +#include "esp_err.h" +#include "osi/allocator.h" + +#if( defined BLE_INCLUDED ) && (BLE_INCLUDED == TRUE) +#if( defined BTA_GATT_INCLUDED ) && (GATTC_INCLUDED == TRUE) +// #if( defined GATTC_CACHE_NVS ) && (GATTC_CACHE_NVS == TRUE) + +#define GATT_CACHE_PREFIX "gatt_" +#define INVALID_ADDR_NUM 0xff +#define MAX_DEVICE_IN_CACHE 50 +#define MAX_ADDR_LIST_CACHE_BUF 2048 + +#ifdef BT_SUPPORT_NVM +static FILE *sCacheFD = 0; +static void getFilename(char *buffer, BD_ADDR bda) +{ + sprintf(buffer, "%s%02x%02x%02x%02x%02x%02x", GATT_CACHE_PREFIX + , bda[0], bda[1], bda[2], bda[3], bda[4], bda[5]); +} + +static void cacheClose(void) +{ + if (sCacheFD != 0) { + fclose(sCacheFD); + sCacheFD = 0; + } +} + +static bool cacheOpen(BD_ADDR bda, bool to_save) +{ + char fname[255] = {0}; + getFilename(fname, bda); + + cacheClose(); + sCacheFD = fopen(fname, to_save ? "w" : "r"); + + return (sCacheFD != 0); +} + +static void cacheReset(BD_ADDR bda) +{ + char fname[255] = {0}; + getFilename(fname, bda); + unlink(fname); +} + +#else + +static const char *cache_key = "gattc_cache_key"; +static const char *cache_addr = "cache_addr_tab"; + +typedef struct { + //save the service data in the list according to the address + nvs_handle_t cache_fp; + BOOLEAN is_open; + BD_ADDR addr; + hash_key_t hash_key; + list_t *assoc_addr; +}cache_addr_info_t; + +typedef struct { + //save the address list in the cache + nvs_handle_t addr_fp; + BOOLEAN is_open; + UINT8 num_addr; + cache_addr_info_t cache_addr[MAX_DEVICE_IN_CACHE]; +}cache_env_t; + +static cache_env_t *cache_env = NULL; + +static void getFilename(char *buffer, hash_key_t hash) +{ + sprintf(buffer, "%s%02x%02x%02x%02x", GATT_CACHE_PREFIX, + hash[0], hash[1], hash[2], hash[3]); +} + +static void cacheClose(BD_ADDR bda) +{ + UINT8 index = 0; + if ((index = bta_gattc_co_find_addr_in_cache(bda)) != INVALID_ADDR_NUM) { + if (cache_env->cache_addr[index].is_open) { + nvs_close(cache_env->cache_addr[index].cache_fp); + cache_env->cache_addr[index].is_open = FALSE; + } + } +} + +static bool cacheOpen(BD_ADDR bda, bool to_save, UINT8 *index) +{ + UNUSED(to_save); + char fname[255] = {0}; + UINT8 *assoc_addr = NULL; + esp_err_t status = ESP_FAIL; + hash_key_t hash_key = {0}; + if (((*index = bta_gattc_co_find_addr_in_cache(bda)) != INVALID_ADDR_NUM) || + ((assoc_addr = bta_gattc_co_cache_find_src_addr(bda, index)) != NULL)) { + if (cache_env->cache_addr[*index].is_open) { + return TRUE; + } else { + memcpy(hash_key, cache_env->cache_addr[*index].hash_key, sizeof(hash_key_t)); + getFilename(fname, hash_key); + if ((status = nvs_open(fname, NVS_READWRITE, &cache_env->cache_addr[*index].cache_fp)) == ESP_OK) { + // Set the open flag to TRUE when success to open the hash file. + cache_env->cache_addr[*index].is_open = TRUE; + } + } + } + + return ((status == ESP_OK) ? true : false); +} + +static void cacheReset(BD_ADDR bda, BOOLEAN update) +{ + char fname[255] = {0}; + getFilename(fname, bda); + UINT8 index = 0; + //cache_env->cache_addr + if ((index = bta_gattc_co_find_addr_in_cache(bda)) != INVALID_ADDR_NUM) { + //clear the association address pending in the source address. + bta_gattc_co_cache_clear_assoc_addr(bda); + if (cache_env->cache_addr[index].is_open) { + nvs_erase_all(cache_env->cache_addr[index].cache_fp); + nvs_close(cache_env->cache_addr[index].cache_fp); + cache_env->cache_addr[index].is_open = FALSE; + } else { + cacheOpen(bda, false, &index); + if (index == INVALID_ADDR_NUM) { + APPL_TRACE_ERROR("%s INVALID ADDR NUM", __func__); + return; + } + if (cache_env->cache_addr[index].is_open) { + nvs_erase_all(cache_env->cache_addr[index].cache_fp); + nvs_close(cache_env->cache_addr[index].cache_fp); + cache_env->cache_addr[index].is_open = FALSE; + } else { + APPL_TRACE_ERROR("%s cacheOpen failed", __func__); + return; + } + } + if(cache_env->num_addr == 0) { + APPL_TRACE_ERROR("%s cache addr list error", __func__); + return; + } + + UINT8 num = cache_env->num_addr; + //delete the server_bda in the addr_info list. + for(UINT8 i = index; i < (num - 1); i++) { + memcpy(&cache_env->cache_addr[i], &cache_env->cache_addr[i+1], sizeof(cache_addr_info_t)); + } + //clear the last cache when delete a addr + memset(&cache_env->cache_addr[num-1], 0, sizeof(cache_addr_info_t)); + //reduced the number address counter also + cache_env->num_addr--; + + //don't need to update addr list to nvs flash + if (!update) { + return; + } + + //update addr list to nvs flash + if(cache_env->num_addr > 0) { + //update + UINT8 *p_buf = osi_malloc(MAX_ADDR_LIST_CACHE_BUF); + if(!p_buf) { + APPL_TRACE_ERROR("%s malloc error", __func__); + return; + } + UINT16 length = cache_env->num_addr*(sizeof(BD_ADDR) + sizeof(hash_key_t)); + for (UINT8 i = 0; i < cache_env->num_addr; i++) { + //copy the address to the buffer. + memcpy(p_buf + i*(sizeof(BD_ADDR) + sizeof(hash_key_t)), cache_env->cache_addr[i].addr, sizeof(BD_ADDR)); + //copy the hash key to the buffer. + memcpy(p_buf + i*(sizeof(BD_ADDR) + sizeof(hash_key_t)) + sizeof(BD_ADDR), + cache_env->cache_addr[i].hash_key, sizeof(hash_key_t)); + } + if (cache_env->is_open) { + if (nvs_set_blob(cache_env->addr_fp, cache_key, p_buf, length) != ESP_OK) { + APPL_TRACE_WARNING("%s, nvs set blob failed", __func__); + } + } + osi_free(p_buf); + + } else { + //erase + if (cache_env->is_open) { + nvs_erase_all(cache_env->addr_fp); + nvs_close(cache_env->addr_fp); + cache_env->is_open = FALSE; + } else { + APPL_TRACE_WARNING("cache_env status is error"); + } + } + } +} + +#endif /* BT_SUPPORT_NVM */ +/***************************************************************************** +** Function Declarations +*****************************************************************************/ + +/******************************************************************************* +** +** Function bta_gattc_co_cache_open +** +** Description This callout function is executed by GATTC when a GATT server +** cache is ready to be sent. +** +** Parameter server_bda: server bd address of this cache belongs to +** evt: call in event to be passed in when cache open is done. +** conn_id: connection ID of this cache operation attach to. +** to_save: open cache to save or to load. +** +** Returns void. +** +*******************************************************************************/ +tBTA_GATT_STATUS bta_gattc_co_cache_open(BD_ADDR server_bda, BOOLEAN to_save, UINT8 *index) +{ + /* open NV cache and send call in */ + tBTA_GATT_STATUS status = BTA_GATT_OK; + if (!cacheOpen(server_bda, to_save, index)) { + status = BTA_GATT_ERROR; + } + + APPL_TRACE_DEBUG("%s() - status=%d", __func__, status); + return status; +} + +/******************************************************************************* +** +** Function bta_gattc_co_cache_load +** +** Description This callout function is executed by GATT when server cache +** is required to load. +** +** Parameter server_bda: server bd address of this cache belongs to +** evt: call in event to be passed in when cache save is done. +** num_attr: number of attribute to be save. +** attr_index: starting attribute index of the save operation. +** conn_id: connection ID of this cache operation attach to. +** Returns +** +*******************************************************************************/ +tBTA_GATT_STATUS bta_gattc_co_cache_load(tBTA_GATTC_NV_ATTR *attr, UINT8 index) +{ +#if (!CONFIG_BT_STACK_NO_LOG) + UINT16 num_attr = 0; +#endif + tBTA_GATT_STATUS status = BTA_GATT_ERROR; + size_t length = 0; + // Read the size of memory space required for blob + nvs_get_blob(cache_env->cache_addr[index].cache_fp, cache_key, NULL, &length); + // Read previously saved blob if available + esp_err_t err_code = nvs_get_blob(cache_env->cache_addr[index].cache_fp, cache_key, attr, &length); +#if (!CONFIG_BT_STACK_NO_LOG) + num_attr = length / sizeof(tBTA_GATTC_NV_ATTR); +#endif + status = (err_code == ESP_OK && length != 0) ? BTA_GATT_OK : BTA_GATT_ERROR; + APPL_TRACE_DEBUG("%s() - read=%d, status=%d, err_code = %d", + __func__, num_attr, status, err_code); + + return status; + } + +size_t bta_gattc_get_cache_attr_length(UINT8 index) +{ + size_t length = 0; + if (index == INVALID_ADDR_NUM) { + return 0; + } + + // Read the size of memory space required for blob + nvs_get_blob(cache_env->cache_addr[index].cache_fp, cache_key, NULL, &length); + return length; +} + +/******************************************************************************* +** +** Function bta_gattc_co_cache_save +** +** Description This callout function is executed by GATT when a server cache +** is available to save. +** +** Parameter server_bda: server bd address of this cache belongs to +** evt: call in event to be passed in when cache save is done. +** num_attr: number of attribute to be save. +** p_attr: pointer to the list of attributes to save. +** attr_index: starting attribute index of the save operation. +** conn_id: connection ID of this cache operation attach to. +** Returns +** +*******************************************************************************/ +void bta_gattc_co_cache_save (BD_ADDR server_bda, UINT16 num_attr, + tBTA_GATTC_NV_ATTR *p_attr_list) +{ + tBTA_GATT_STATUS status = BTA_GATT_OK; + hash_key_t hash_key = {0}; + UINT8 index = INVALID_ADDR_NUM; + //calculate the hash value of the attribute table which should be added to the nvs flash. + hash_function_blob((unsigned char *)p_attr_list, sizeof(tBTA_GATTC_NV_ATTR)*num_attr, hash_key); + //save the address list to the nvs flash + bta_gattc_co_cache_addr_save(server_bda, hash_key); + + if (cacheOpen(server_bda, TRUE, &index)) { + esp_err_t err_code = nvs_set_blob(cache_env->cache_addr[index].cache_fp, cache_key, + p_attr_list, sizeof(tBTA_GATTC_NV_ATTR)*num_attr); + status = (err_code == ESP_OK) ? BTA_GATT_OK : BTA_GATT_ERROR; + } else { + status = BTA_GATT_ERROR; + } + +#if CONFIG_BT_STACK_NO_LOG + (void) status; +#endif + APPL_TRACE_DEBUG("%s() wrote hash_key = %x%x%x%x, num_attr = %d, status = %d.", __func__, hash_key[0], hash_key[1], hash_key[2], hash_key[3], num_attr, status); +} + +/******************************************************************************* +** +** Function bta_gattc_co_cache_close +** +** Description This callout function is executed by GATTC when a GATT server +** cache is written completely. +** +** Parameter server_bda: server bd address of this cache belongs to +** conn_id: connection ID of this cache operation attach to. +** +** Returns void. +** +*******************************************************************************/ +void bta_gattc_co_cache_close(BD_ADDR server_bda, UINT16 conn_id) +{ + UNUSED(conn_id); +//#ifdef BT_SUPPORT_NVM + cacheClose(server_bda); +//#endif /* BT_SUPPORT_NVM */ + /* close NV when server cache is done saving or loading, + does not need to do anything for now on Insight */ + + BTIF_TRACE_DEBUG("%s()", __FUNCTION__); +} + +/******************************************************************************* +** +** Function bta_gattc_co_cache_reset +** +** Description This callout function is executed by GATTC to reset cache in +** application +** +** Parameter server_bda: server bd address of this cache belongs to +** +** Returns void. +** +*******************************************************************************/ +void bta_gattc_co_cache_reset(BD_ADDR server_bda) +{ + cacheReset(server_bda, TRUE); +} + +void bta_gattc_co_cache_addr_init(void) +{ + nvs_handle_t fp; + esp_err_t err_code; + UINT8 num_addr; + size_t length = MAX_ADDR_LIST_CACHE_BUF; + UINT8 *p_buf = osi_malloc(MAX_ADDR_LIST_CACHE_BUF); + if (p_buf == NULL) { + APPL_TRACE_ERROR("%s malloc failed!", __func__); + return; + } + + cache_env = (cache_env_t *)osi_malloc(sizeof(cache_env_t)); + if (cache_env == NULL) { + APPL_TRACE_ERROR("%s malloc failed!", __func__); + osi_free(p_buf); + return; + } + + memset(cache_env, 0x0, sizeof(cache_env_t)); + + if ((err_code = nvs_open(cache_addr, NVS_READWRITE, &fp)) == ESP_OK) { + cache_env->addr_fp = fp; + cache_env->is_open = TRUE; + // Read previously saved blob if available + if ((err_code = nvs_get_blob(fp, cache_key, p_buf, &length)) != ESP_OK) { + if(err_code != ESP_ERR_NVS_NOT_FOUND) { + APPL_TRACE_ERROR("%s, Line = %d, nvs flash get blob data fail, err_code = 0x%x", __func__, __LINE__, err_code); + } + osi_free(p_buf); + return; + } + num_addr = length / (sizeof(BD_ADDR) + sizeof(hash_key_t)); + cache_env->num_addr = num_addr; + //read the address from nvs flash to cache address list. + for (UINT8 i = 0; i < num_addr; i++) { + memcpy(cache_env->cache_addr[i].addr, p_buf + i*(sizeof(BD_ADDR) + sizeof(hash_key_t)), sizeof(BD_ADDR)); + memcpy(cache_env->cache_addr[i].hash_key, + p_buf + i*(sizeof(BD_ADDR) + sizeof(hash_key_t)) + sizeof(BD_ADDR), sizeof(hash_key_t)); + + APPL_TRACE_DEBUG("cache_addr[%x] = %x:%x:%x:%x:%x:%x", i, cache_env->cache_addr[i].addr[0], cache_env->cache_addr[i].addr[1], cache_env->cache_addr[i].addr[2], + cache_env->cache_addr[i].addr[3], cache_env->cache_addr[i].addr[4], cache_env->cache_addr[i].addr[5]); + APPL_TRACE_DEBUG("hash_key[%x] = %x%x%x%x", i, cache_env->cache_addr[i].hash_key[0], cache_env->cache_addr[i].hash_key[1], + cache_env->cache_addr[i].hash_key[2], cache_env->cache_addr[i].hash_key[3]); + bta_gattc_co_cache_new_assoc_list(cache_env->cache_addr[i].addr, i); + } + } else { + APPL_TRACE_ERROR("%s, Line = %d, nvs flash open fail, err_code = %x", __func__, __LINE__, err_code); + osi_free(p_buf); + return; + } + + osi_free(p_buf); + return; +} + +void bta_gattc_co_cache_addr_deinit(void) +{ + if(!cache_env->is_open) { + return; + } + nvs_close(cache_env->addr_fp); + cache_env->is_open = false; + + for(UINT8 i = 0; i< cache_env->num_addr; i++) { + cache_addr_info_t *addr_info = &cache_env->cache_addr[i]; + if(addr_info) { + nvs_close(addr_info->cache_fp); + addr_info->is_open = false; + if(addr_info->assoc_addr) { + list_free(addr_info->assoc_addr); + } + } + } + + osi_free(cache_env); + cache_env = NULL; +} + +BOOLEAN bta_gattc_co_addr_in_cache(BD_ADDR bda) +{ + UINT8 addr_index = 0; + UINT8 num = cache_env->num_addr; + cache_addr_info_t *addr_info = &cache_env->cache_addr[0]; + for (addr_index = 0; addr_index < num; addr_index++) { + if (!memcmp(addr_info->addr, bda, sizeof(BD_ADDR))) { + return TRUE; + } + } + + return FALSE; +} + +UINT8 bta_gattc_co_find_addr_in_cache(BD_ADDR bda) +{ + UINT8 addr_index = 0; + UINT8 num = cache_env->num_addr; + cache_addr_info_t *addr_info = &cache_env->cache_addr[0]; + + for (addr_index = 0; addr_index < num; addr_index++, addr_info++) { + if (!memcmp(addr_info->addr, bda, sizeof(BD_ADDR))) { + return addr_index; + } + } + + return INVALID_ADDR_NUM; +} + +UINT8 bta_gattc_co_find_hash_in_cache(hash_key_t hash_key) +{ + UINT8 index = 0; + UINT8 num = cache_env->num_addr; + cache_addr_info_t *addr_info = &cache_env->cache_addr[0]; + for (index = 0; index < num; index++) { + if (!memcmp(addr_info->hash_key, hash_key, sizeof(hash_key_t))) { + return index; + } + } + + return INVALID_ADDR_NUM; +} + +UINT8 bta_gattc_co_get_addr_num(void) +{ + if (cache_env == NULL) { + return 0; + } + + return cache_env->num_addr; +} + +void bta_gattc_co_get_addr_list(BD_ADDR *addr_list) +{ + UINT8 num = cache_env->num_addr; + for (UINT8 i = 0; i < num; i++) { + memcpy(addr_list[i], cache_env->cache_addr[i].addr, sizeof(BD_ADDR)); + } +} + +void bta_gattc_co_cache_addr_save(BD_ADDR bd_addr, hash_key_t hash_key) +{ + esp_err_t err_code; + UINT8 index = 0; + UINT8 new_index = cache_env->num_addr; + UINT8 *p_buf = osi_malloc(MAX_ADDR_LIST_CACHE_BUF); + if (p_buf == NULL) { + APPL_TRACE_ERROR("%s malloc failed!", __func__); + return; + } + + // check the address list has the same address or not + // for the same address, it's hash key may be change due to service change + if ((index = bta_gattc_co_find_addr_in_cache(bd_addr)) != INVALID_ADDR_NUM) { + APPL_TRACE_DEBUG("%s the bd_addr already in the cache list, index = %x", __func__, index); + //if the bd_addr already in the address list, update the hash key in it. + memcpy(cache_env->cache_addr[index].addr, bd_addr, sizeof(BD_ADDR)); + memcpy(cache_env->cache_addr[index].hash_key, hash_key, sizeof(hash_key_t)); + } else { + if (cache_env->num_addr >= MAX_DEVICE_IN_CACHE) { + APPL_TRACE_WARNING("%s cache list full and remove the oldest addr info", __func__); + cacheReset(cache_env->cache_addr[0].addr, FALSE); + } + new_index = cache_env->num_addr; + assert(new_index < MAX_DEVICE_IN_CACHE); + memcpy(cache_env->cache_addr[new_index].addr, bd_addr, sizeof(BD_ADDR)); + memcpy(cache_env->cache_addr[new_index].hash_key, hash_key, sizeof(hash_key_t)); + cache_env->num_addr++; + APPL_TRACE_DEBUG("%s(), num = %d", __func__, cache_env->num_addr); + } + + nvs_handle_t *fp = &cache_env->addr_fp; + UINT16 length = cache_env->num_addr * (sizeof(BD_ADDR) + sizeof(hash_key_t)); + + for (UINT8 i = 0; i < cache_env->num_addr; i++) { + //copy the address to the buffer. + memcpy(p_buf + i*(sizeof(BD_ADDR) + sizeof(hash_key_t)), cache_env->cache_addr[i].addr, sizeof(BD_ADDR)); + //copy the hash key to the buffer. + memcpy(p_buf + i*(sizeof(BD_ADDR) + sizeof(hash_key_t)) + sizeof(BD_ADDR), + cache_env->cache_addr[i].hash_key, sizeof(hash_key_t)); + } + + if (cache_env->is_open) { + if ((err_code = nvs_set_blob(cache_env->addr_fp, cache_key, p_buf, length)) != ESP_OK) { + APPL_TRACE_WARNING("%s(), nvs set blob fail, err %d", __func__, err_code); + } + } else { + if ((err_code = nvs_open(cache_addr, NVS_READWRITE , fp)) == ESP_OK) { + cache_env->is_open = true; + if (( err_code = nvs_set_blob(cache_env->addr_fp, cache_key, p_buf, length)) != ESP_OK) { + APPL_TRACE_WARNING("%s(), nvs set blob fail, err %d", __func__, err_code); + } + } else { + APPL_TRACE_ERROR("%s, Line = %d, nvs flash open fail, err_code = %x", __func__, __LINE__, err_code); + } + } + + //free the buffer after used. + osi_free(p_buf); + return; +} + +BOOLEAN bta_gattc_co_cache_new_assoc_list(BD_ADDR src_addr, UINT8 index) +{ + cache_addr_info_t *addr_info = &cache_env->cache_addr[index]; + addr_info->assoc_addr = list_new(osi_free_func); + return (addr_info->assoc_addr != NULL ? TRUE : FALSE); +} + +BOOLEAN bta_gattc_co_cache_append_assoc_addr(BD_ADDR src_addr, BD_ADDR assoc_addr) +{ + UINT8 addr_index = 0; + cache_addr_info_t *addr_info; + UINT8 *p_assoc_buf = osi_malloc(sizeof(BD_ADDR)); + if(!p_assoc_buf) { + return FALSE; + } + memcpy(p_assoc_buf, assoc_addr, sizeof(BD_ADDR)); + if ((addr_index = bta_gattc_co_find_addr_in_cache(src_addr)) != INVALID_ADDR_NUM) { + addr_info = &cache_env->cache_addr[addr_index]; + if (addr_info->assoc_addr == NULL) { + addr_info->assoc_addr =list_new(NULL); + } + return list_append(addr_info->assoc_addr, p_assoc_buf); + } else { + osi_free(p_assoc_buf); + } + + return FALSE; +} + +BOOLEAN bta_gattc_co_cache_remove_assoc_addr(BD_ADDR src_addr, BD_ADDR assoc_addr) +{ + UINT8 addr_index = 0; + cache_addr_info_t *addr_info; + if ((addr_index = bta_gattc_co_find_addr_in_cache(src_addr)) != INVALID_ADDR_NUM) { + addr_info = &cache_env->cache_addr[addr_index]; + if (addr_info->assoc_addr != NULL) { + for (list_node_t *sn = list_begin(addr_info->assoc_addr); + sn != list_end(addr_info->assoc_addr); sn = list_next(sn)) { + void *addr = list_node(sn); + if (!memcmp(addr, assoc_addr, sizeof(BD_ADDR))) { + return list_remove(addr_info->assoc_addr, addr); + } + } + //return list_remove(addr_info->assoc_addr, assoc_addr); + } else { + return FALSE; + } + } + + return FALSE; +} + +UINT8* bta_gattc_co_cache_find_src_addr(BD_ADDR assoc_addr, UINT8 *index) +{ + UINT8 num = cache_env->num_addr; + cache_addr_info_t *addr_info = &cache_env->cache_addr[0]; + UINT8 *addr_data; + //Check the assoc_addr list is NULL or not + if (addr_info->assoc_addr == NULL) { + *index = INVALID_ADDR_NUM; + return NULL; + } + + for (int i = 0; i < num; i++) { + for (const list_node_t *node = list_begin(addr_info->assoc_addr); node != list_end(addr_info->assoc_addr); + node = list_next(node)) { + addr_data = (UINT8 *)list_node(node); + if (!memcmp(addr_data, assoc_addr, sizeof(BD_ADDR))) { + *index = i; + return (UINT8 *)addr_info->addr; + } + } + addr_info++; + + if (addr_info->assoc_addr == NULL) { + *index = INVALID_ADDR_NUM; + return NULL; + } + } + + *index = INVALID_ADDR_NUM; + return NULL; +} + +BOOLEAN bta_gattc_co_cache_clear_assoc_addr(BD_ADDR src_addr) +{ + UINT8 addr_index = 0; + cache_addr_info_t *addr_info; + if ((addr_index = bta_gattc_co_find_addr_in_cache(src_addr)) != INVALID_ADDR_NUM) { + addr_info = &cache_env->cache_addr[addr_index]; + if (addr_info->assoc_addr != NULL) { + list_clear(addr_info->assoc_addr); + } else { + return FALSE; + } + return TRUE; + } + + return FALSE; +} + +// #endif /* #if( defined GATTC_CACHE_NVS ) && (GATTC_CACHE_NVS == TRUE) */ +#endif /* #if( defined BLE_INCLUDED ) && (BLE_INCLUDED == TRUE) */ +#endif /* #if( defined BTA_GATT_INCLUDED ) && (BTA_GATT_INCLUDED == TRUE) */ diff --git a/lib/bt/host/bluedroid/bta/gatt/bta_gattc_main.c b/lib/bt/host/bluedroid/bta/gatt/bta_gattc_main.c new file mode 100644 index 00000000..0cc55995 --- /dev/null +++ b/lib/bt/host/bluedroid/bta/gatt/bta_gattc_main.c @@ -0,0 +1,550 @@ +/****************************************************************************** + * + * Copyright (C) 2003-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains the GATT client main functions and state machine. + * + ******************************************************************************/ + +#include "common/bt_target.h" + +#if (GATTC_INCLUDED == TRUE && BLE_INCLUDED == TRUE) + +#include + +#include "bta_gattc_int.h" +#include "osi/allocator.h" + + +/***************************************************************************** +** Constants and types +*****************************************************************************/ + + +/* state machine action enumeration list */ +enum { + BTA_GATTC_OPEN, + BTA_GATTC_OPEN_FAIL, + BTA_GATTC_OPEN_ERROR, + BTA_GATTC_CANCEL_OPEN, + BTA_GATTC_CANCEL_OPEN_OK, + BTA_GATTC_CANCEL_OPEN_ERROR, + BTA_GATTC_CONN, + BTA_GATTC_START_DISCOVER, + BTA_GATTC_DISC_CMPL, + + BTA_GATTC_Q_CMD, + BTA_GATTC_CLOSE, + BTA_GATTC_CLOSE_FAIL, + BTA_GATTC_READ, + BTA_GATTC_WRITE, + + BTA_GATTC_OP_CMPL, + BTA_GATTC_SEARCH, + BTA_GATTC_FAIL, + BTA_GATTC_CONFIRM, + BTA_GATTC_EXEC, + BTA_GATTC_READ_MULTI, + BTA_GATTC_IGNORE_OP_CMPL, + BTA_GATTC_DISC_CLOSE, + BTA_GATTC_RESTART_DISCOVER, + BTA_GATTC_CFG_MTU, + BTA_GATTC_READ_BY_TYPE, + BTA_GATTC_READ_MULTI_VAR, + + BTA_GATTC_IGNORE +}; +/* type for action functions */ +typedef void (*tBTA_GATTC_ACTION)(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data); + +/* action function list */ +const tBTA_GATTC_ACTION bta_gattc_action[] = { + bta_gattc_open, + bta_gattc_open_fail, + bta_gattc_open_error, + bta_gattc_cancel_open, + bta_gattc_cancel_open_ok, + bta_gattc_cancel_open_error, + bta_gattc_conn, + bta_gattc_start_discover, + bta_gattc_disc_cmpl, + + bta_gattc_q_cmd, + bta_gattc_close, + bta_gattc_close_fail, + bta_gattc_read, + bta_gattc_write, + + bta_gattc_op_cmpl, + bta_gattc_search, + bta_gattc_fail, + bta_gattc_confirm, + bta_gattc_execute, + bta_gattc_read_multi, + bta_gattc_ignore_op_cmpl, + bta_gattc_disc_close, + bta_gattc_restart_discover, + bta_gattc_cfg_mtu, + bta_gattc_read_by_type, + bta_gattc_read_multi_var, +}; + + +/* state table information */ +#define BTA_GATTC_ACTIONS 1 /* number of actions */ +#define BTA_GATTC_NEXT_STATE 1 /* position of next state */ +#define BTA_GATTC_NUM_COLS 2 /* number of columns in state tables */ + +/* state table for idle state */ +static const UINT8 bta_gattc_st_idle[][BTA_GATTC_NUM_COLS] = { + /* Event Action 1 Next state */ + /* BTA_GATTC_API_OPEN_EVT */ {BTA_GATTC_OPEN, BTA_GATTC_W4_CONN_ST}, + /* BTA_GATTC_INT_OPEN_FAIL_EVT */ {BTA_GATTC_IGNORE, BTA_GATTC_IDLE_ST}, + /* BTA_GATTC_API_CANCEL_OPEN_EVT */ {BTA_GATTC_IGNORE, BTA_GATTC_IDLE_ST}, + /* BTA_GATTC_INT_CANCEL_OPEN_OK_EVT */ {BTA_GATTC_IGNORE, BTA_GATTC_IDLE_ST}, + + /* BTA_GATTC_API_READ_EVT */ {BTA_GATTC_FAIL, BTA_GATTC_IDLE_ST}, + /* BTA_GATTC_API_WRITE_EVT */ {BTA_GATTC_FAIL, BTA_GATTC_IDLE_ST}, + /* BTA_GATTC_API_EXEC_EVT */ {BTA_GATTC_FAIL, BTA_GATTC_IDLE_ST}, + /* BTA_GATTC_API_CFG_MTU_EVT */ {BTA_GATTC_IGNORE, BTA_GATTC_IDLE_ST}, + + /* BTA_GATTC_API_CLOSE_EVT */ {BTA_GATTC_CLOSE_FAIL, BTA_GATTC_IDLE_ST}, + + /* BTA_GATTC_API_SEARCH_EVT */ {BTA_GATTC_FAIL, BTA_GATTC_IDLE_ST}, + /* BTA_GATTC_API_CONFIRM_EVT */ {BTA_GATTC_FAIL, BTA_GATTC_IDLE_ST}, + /* BTA_GATTC_API_READ_MULTI_EVT */ {BTA_GATTC_FAIL, BTA_GATTC_IDLE_ST}, + /* BTA_GATTC_API_REFRESH_EVT */ {BTA_GATTC_IGNORE, BTA_GATTC_IDLE_ST}, + /* BTA_GATTC_API_CACHE_CLEAN_EVT */ {BTA_GATTC_IGNORE, BTA_GATTC_IDLE_ST}, + + /* BTA_GATTC_INT_CONN_EVT */ {BTA_GATTC_CONN, BTA_GATTC_CONN_ST}, + /* BTA_GATTC_INT_DISCOVER_EVT */ {BTA_GATTC_IGNORE, BTA_GATTC_IDLE_ST}, + /* BTA_GATTC_DISCOVER_CMPL_EVT */ {BTA_GATTC_IGNORE, BTA_GATTC_IDLE_ST}, + /* BTA_GATTC_OP_CMPL_EVT */ {BTA_GATTC_IGNORE, BTA_GATTC_IDLE_ST}, + /* BTA_GATTC_INT_DISCONN_EVT */ {BTA_GATTC_IGNORE, BTA_GATTC_IDLE_ST}, + + /* BTA_GATTC_API_READ_BY_TYPE_EVT */ {BTA_GATTC_FAIL, BTA_GATTC_IDLE_ST}, + /* BTA_GATTC_API_READ_MULTI_VAR_EVT */ {BTA_GATTC_FAIL, BTA_GATTC_IDLE_ST}, +}; + +/* state table for wait for open state */ +static const UINT8 bta_gattc_st_w4_conn[][BTA_GATTC_NUM_COLS] = { + /* Event Action 1 Next state */ + /* BTA_GATTC_API_OPEN_EVT */ {BTA_GATTC_OPEN, BTA_GATTC_W4_CONN_ST}, + /* BTA_GATTC_INT_OPEN_FAIL_EVT */ {BTA_GATTC_OPEN_FAIL, BTA_GATTC_IDLE_ST}, + /* BTA_GATTC_API_CANCEL_OPEN_EVT */ {BTA_GATTC_CANCEL_OPEN, BTA_GATTC_W4_CONN_ST}, + /* BTA_GATTC_INT_CANCEL_OPEN_OK_EVT */ {BTA_GATTC_CANCEL_OPEN_OK, BTA_GATTC_IDLE_ST}, + + /* BTA_GATTC_API_READ_EVT */ {BTA_GATTC_FAIL, BTA_GATTC_W4_CONN_ST}, + /* BTA_GATTC_API_WRITE_EVT */ {BTA_GATTC_FAIL, BTA_GATTC_W4_CONN_ST}, + /* BTA_GATTC_API_EXEC_EVT */ {BTA_GATTC_FAIL, BTA_GATTC_W4_CONN_ST}, + /* BTA_GATTC_API_CFG_MTU_EVT */ {BTA_GATTC_IGNORE, BTA_GATTC_W4_CONN_ST}, + + /* BTA_GATTC_API_CLOSE_EVT */ {BTA_GATTC_CANCEL_OPEN, BTA_GATTC_W4_CONN_ST}, + + /* BTA_GATTC_API_SEARCH_EVT */ {BTA_GATTC_FAIL, BTA_GATTC_W4_CONN_ST}, + /* BTA_GATTC_API_CONFIRM_EVT */ {BTA_GATTC_FAIL, BTA_GATTC_W4_CONN_ST}, + /* BTA_GATTC_API_READ_MULTI_EVT */ {BTA_GATTC_FAIL, BTA_GATTC_W4_CONN_ST}, + /* BTA_GATTC_API_REFRESH_EVT */ {BTA_GATTC_IGNORE, BTA_GATTC_W4_CONN_ST}, + /* BTA_GATTC_API_CACHE_CLEAN_EVT */ {BTA_GATTC_IGNORE, BTA_GATTC_W4_CONN_ST}, + + /* BTA_GATTC_INT_CONN_EVT */ {BTA_GATTC_CONN, BTA_GATTC_CONN_ST}, + /* BTA_GATTC_INT_DISCOVER_EVT */ {BTA_GATTC_IGNORE, BTA_GATTC_W4_CONN_ST}, + /* BTA_GATTC_DISCOVER_CMPL_EVT */ {BTA_GATTC_IGNORE, BTA_GATTC_W4_CONN_ST}, + /* BTA_GATTC_OP_CMPL_EVT */ {BTA_GATTC_IGNORE, BTA_GATTC_W4_CONN_ST}, + /* BTA_GATTC_INT_DISCONN_EVT */ {BTA_GATTC_OPEN_FAIL, BTA_GATTC_IDLE_ST}, + + /* BTA_GATTC_API_READ_BY_TYPE_EVT */ {BTA_GATTC_FAIL, BTA_GATTC_W4_CONN_ST}, + /* BTA_GATTC_API_READ_MULTI_VAR_EVT */ {BTA_GATTC_FAIL, BTA_GATTC_W4_CONN_ST}, +}; + +/* state table for open state */ +static const UINT8 bta_gattc_st_connected[][BTA_GATTC_NUM_COLS] = { + /* Event Action 1 Next state */ + /* BTA_GATTC_API_OPEN_EVT */ {BTA_GATTC_OPEN, BTA_GATTC_CONN_ST}, + /* BTA_GATTC_INT_OPEN_FAIL_EVT */ {BTA_GATTC_IGNORE, BTA_GATTC_CONN_ST}, + /* BTA_GATTC_API_CANCEL_OPEN_EVT */ {BTA_GATTC_CANCEL_OPEN_ERROR, BTA_GATTC_CONN_ST}, + /* BTA_GATTC_INT_CANCEL_OPEN_OK_EVT */ {BTA_GATTC_IGNORE, BTA_GATTC_CONN_ST}, + + /* BTA_GATTC_API_READ_EVT */ {BTA_GATTC_READ, BTA_GATTC_CONN_ST}, + /* BTA_GATTC_API_WRITE_EVT */ {BTA_GATTC_WRITE, BTA_GATTC_CONN_ST}, + /* BTA_GATTC_API_EXEC_EVT */ {BTA_GATTC_EXEC, BTA_GATTC_CONN_ST}, + /* BTA_GATTC_API_CFG_MTU_EVT */ {BTA_GATTC_CFG_MTU, BTA_GATTC_CONN_ST}, + + /* BTA_GATTC_API_CLOSE_EVT */ {BTA_GATTC_CLOSE, BTA_GATTC_IDLE_ST}, + + /* BTA_GATTC_API_SEARCH_EVT */ {BTA_GATTC_SEARCH, BTA_GATTC_CONN_ST}, + /* BTA_GATTC_API_CONFIRM_EVT */ {BTA_GATTC_CONFIRM, BTA_GATTC_CONN_ST}, + /* BTA_GATTC_API_READ_MULTI_EVT */ {BTA_GATTC_READ_MULTI, BTA_GATTC_CONN_ST}, + /* BTA_GATTC_API_REFRESH_EVT */ {BTA_GATTC_IGNORE, BTA_GATTC_CONN_ST}, + /* BTA_GATTC_API_CACHE_CLEAN_EVT */ {BTA_GATTC_IGNORE, BTA_GATTC_CONN_ST}, + + /* BTA_GATTC_INT_CONN_EVT */ {BTA_GATTC_CONN, BTA_GATTC_CONN_ST}, + /* BTA_GATTC_INT_DISCOVER_EVT */ {BTA_GATTC_START_DISCOVER, BTA_GATTC_DISCOVER_ST}, + /* BTA_GATTC_DISCOVER_CMPL_EVT */ {BTA_GATTC_IGNORE, BTA_GATTC_CONN_ST}, + /* BTA_GATTC_OP_CMPL_EVT */ {BTA_GATTC_OP_CMPL, BTA_GATTC_CONN_ST}, + + /* BTA_GATTC_INT_DISCONN_EVT */ {BTA_GATTC_CLOSE, BTA_GATTC_IDLE_ST}, + + /* BTA_GATTC_API_READ_BY_TYPE_EVT */ {BTA_GATTC_READ_BY_TYPE, BTA_GATTC_CONN_ST}, + /* BTA_GATTC_API_READ_MULTI_VAR_EVT */ {BTA_GATTC_READ_MULTI_VAR, BTA_GATTC_CONN_ST}, +}; + +/* state table for discover state */ +static const UINT8 bta_gattc_st_discover[][BTA_GATTC_NUM_COLS] = { + /* Event Action 1 Next state */ + /* BTA_GATTC_API_OPEN_EVT */ {BTA_GATTC_OPEN, BTA_GATTC_DISCOVER_ST}, + /* BTA_GATTC_INT_OPEN_FAIL_EVT */ {BTA_GATTC_IGNORE, BTA_GATTC_DISCOVER_ST}, + /* BTA_GATTC_API_CANCEL_OPEN_EVT */ {BTA_GATTC_CANCEL_OPEN_ERROR, BTA_GATTC_DISCOVER_ST}, + /* BTA_GATTC_INT_CANCEL_OPEN_OK_EVT */ {BTA_GATTC_FAIL, BTA_GATTC_DISCOVER_ST}, + + /* BTA_GATTC_API_READ_EVT */ {BTA_GATTC_Q_CMD, BTA_GATTC_DISCOVER_ST}, + /* BTA_GATTC_API_WRITE_EVT */ {BTA_GATTC_Q_CMD, BTA_GATTC_DISCOVER_ST}, + /* BTA_GATTC_API_EXEC_EVT */ {BTA_GATTC_Q_CMD, BTA_GATTC_DISCOVER_ST}, + /* BTA_GATTC_API_CFG_MTU_EVT */ {BTA_GATTC_Q_CMD, BTA_GATTC_DISCOVER_ST}, + + /* BTA_GATTC_API_CLOSE_EVT */ {BTA_GATTC_DISC_CLOSE, BTA_GATTC_DISCOVER_ST}, + + /* BTA_GATTC_API_SEARCH_EVT */ {BTA_GATTC_Q_CMD, BTA_GATTC_DISCOVER_ST}, + /* BTA_GATTC_API_CONFIRM_EVT */ {BTA_GATTC_CONFIRM, BTA_GATTC_DISCOVER_ST}, + /* BTA_GATTC_API_READ_MULTI_EVT */ {BTA_GATTC_Q_CMD, BTA_GATTC_DISCOVER_ST}, + /* BTA_GATTC_API_REFRESH_EVT */ {BTA_GATTC_IGNORE, BTA_GATTC_DISCOVER_ST}, + /* BTA_GATTC_API_CACHE_CLEAN_EVT */ {BTA_GATTC_IGNORE, BTA_GATTC_DISCOVER_ST}, + + /* BTA_GATTC_INT_CONN_EVT */ {BTA_GATTC_CONN, BTA_GATTC_DISCOVER_ST}, + /* BTA_GATTC_INT_DISCOVER_EVT */ {BTA_GATTC_RESTART_DISCOVER, BTA_GATTC_DISCOVER_ST}, + /* BTA_GATTC_DISCOVER_CMPL_EVT */ {BTA_GATTC_DISC_CMPL, BTA_GATTC_CONN_ST}, + /* BTA_GATTC_OP_CMPL_EVT */ {BTA_GATTC_IGNORE_OP_CMPL, BTA_GATTC_DISCOVER_ST}, + /* BTA_GATTC_INT_DISCONN_EVT */ {BTA_GATTC_CLOSE, BTA_GATTC_IDLE_ST}, + + /* BTA_GATTC_API_READ_BY_TYPE_EVT */ {BTA_GATTC_Q_CMD, BTA_GATTC_DISCOVER_ST}, + /* BTA_GATTC_API_READ_MULTI_VAR_EVT */ {BTA_GATTC_Q_CMD, BTA_GATTC_DISCOVER_ST}, +}; + +/* type for state table */ +typedef const UINT8 (*tBTA_GATTC_ST_TBL)[BTA_GATTC_NUM_COLS]; + +/* state table */ +const tBTA_GATTC_ST_TBL bta_gattc_st_tbl[] = { + bta_gattc_st_idle, + bta_gattc_st_w4_conn, + bta_gattc_st_connected, + bta_gattc_st_discover +}; + +/***************************************************************************** +** Global data +*****************************************************************************/ + +/* GATTC control block */ +#if BTA_DYNAMIC_MEMORY == FALSE +tBTA_GATTC_CB bta_gattc_cb; +#else +tBTA_GATTC_CB *bta_gattc_cb_ptr; +#endif + +#if BTA_GATT_DEBUG == TRUE +static char *gattc_evt_code(tBTA_GATTC_INT_EVT evt_code); +static char *gattc_state_code(tBTA_GATTC_STATE state_code); +#endif + +/******************************************************************************* +** +** Function bta_gattc_sm_execute +** +** Description State machine event handling function for GATTC +** +** +** Returns BOOLEAN : TRUE if queued client request buffer can be immediately released +** else FALSE +** +*******************************************************************************/ +BOOLEAN bta_gattc_sm_execute(tBTA_GATTC_CLCB *p_clcb, UINT16 event, tBTA_GATTC_DATA *p_data) +{ + tBTA_GATTC_ST_TBL state_table; + UINT8 action; + int i; + BOOLEAN rt = TRUE; +#if BTA_GATT_DEBUG == TRUE + tBTA_GATTC_STATE in_state = p_clcb->state; + UINT16 in_event = event; + APPL_TRACE_DEBUG("bta_gattc_sm_execute: State 0x%02x [%s], Event 0x%x[%s]", in_state, + gattc_state_code(in_state), + in_event, + gattc_evt_code(in_event)); +#endif + + + /* look up the state table for the current state */ + state_table = bta_gattc_st_tbl[p_clcb->state]; + + event &= 0x00FF; + + /* set next state */ + p_clcb->state = state_table[event][BTA_GATTC_NEXT_STATE]; + + /* execute action functions */ + for (i = 0; i < BTA_GATTC_ACTIONS; i++) { + if ((action = state_table[event][i]) != BTA_GATTC_IGNORE) { + (*bta_gattc_action[action])(p_clcb, p_data); + if (p_clcb->p_q_cmd == p_data) { + /* buffer is queued, don't free in the bta dispatcher. + * we free it ourselves when a completion event is received. + */ + rt = FALSE; + } + } else { + break; + } + } + +#if BTA_GATT_DEBUG == TRUE + if (in_state != p_clcb->state) { + APPL_TRACE_DEBUG("GATTC State Change: [%s] -> [%s] after Event [%s]", + gattc_state_code(in_state), + gattc_state_code(p_clcb->state), + gattc_evt_code(in_event)); + } +#endif + return rt; +} + +/******************************************************************************* +** +** Function bta_gattc_hdl_event +** +** Description GATT client main event handling function. +** +** +** Returns BOOLEAN +** +*******************************************************************************/ +BOOLEAN bta_gattc_hdl_event(BT_HDR *p_msg) +{ + tBTA_GATTC_CB *p_cb = &bta_gattc_cb; + tBTA_GATTC_CLCB *p_clcb = NULL; + tBTA_GATTC_RCB *p_clreg; + BOOLEAN rt = TRUE; +#if BTA_GATT_DEBUG == TRUE + APPL_TRACE_DEBUG("bta_gattc_hdl_event: Event [%s]\n", gattc_evt_code(p_msg->event)); +#endif + switch (p_msg->event) { + case BTA_GATTC_API_DISABLE_EVT: + bta_gattc_disable(p_cb); + break; + + case BTA_GATTC_API_REG_EVT: + bta_gattc_register(p_cb, (tBTA_GATTC_DATA *) p_msg); + break; + + case BTA_GATTC_INT_START_IF_EVT: + bta_gattc_start_if(p_cb, (tBTA_GATTC_DATA *) p_msg); + break; + + case BTA_GATTC_API_DEREG_EVT: + p_clreg = bta_gattc_cl_get_regcb(((tBTA_GATTC_DATA *)p_msg)->api_dereg.client_if); + bta_gattc_deregister(p_cb, p_clreg); + break; + + case BTA_GATTC_API_OPEN_EVT: + bta_gattc_process_api_open(p_cb, (tBTA_GATTC_DATA *) p_msg); + break; + + case BTA_GATTC_API_CANCEL_OPEN_EVT: + bta_gattc_process_api_open_cancel(p_cb, (tBTA_GATTC_DATA *) p_msg); + break; + + case BTA_GATTC_API_REFRESH_EVT: + bta_gattc_process_api_refresh(p_cb, (tBTA_GATTC_DATA *) p_msg); + break; + case BTA_GATTC_API_CACHE_ASSOC_EVT: + bta_gattc_process_api_cache_assoc(p_cb, (tBTA_GATTC_DATA *)p_msg); + break; + case BTA_GATTC_API_CACHE_GET_ADDR_LIST_EVT: + bta_gattc_process_api_cache_get_addr_list(p_cb, (tBTA_GATTC_DATA *)p_msg); + break; + case BTA_GATTC_API_CACHE_CLEAN_EVT: + bta_gattc_process_api_cache_clean(p_cb, (tBTA_GATTC_DATA *) p_msg); + break; +#if BLE_INCLUDED == TRUE + case BTA_GATTC_API_LISTEN_EVT: + bta_gattc_listen(p_cb, (tBTA_GATTC_DATA *) p_msg); + break; + case BTA_GATTC_API_BROADCAST_EVT: + bta_gattc_broadcast(p_cb, (tBTA_GATTC_DATA *) p_msg); + break; +#endif + + case BTA_GATTC_ENC_CMPL_EVT: + bta_gattc_process_enc_cmpl(p_cb, (tBTA_GATTC_DATA *) p_msg); + break; + + default: + if (p_msg->event == BTA_GATTC_INT_CONN_EVT) { + p_clcb = bta_gattc_find_int_conn_clcb((tBTA_GATTC_DATA *) p_msg); + p_clreg = bta_gattc_cl_get_regcb(((tBTA_GATTC_DATA *)p_msg)->int_conn.client_if); + if (p_clreg != NULL){ + bta_gattc_conncback(p_clreg, (tBTA_GATTC_DATA *) p_msg); + } + + } else if (p_msg->event == BTA_GATTC_INT_DISCONN_EVT) { + p_clreg = bta_gattc_cl_get_regcb(((tBTA_GATTC_DATA *)p_msg)->int_conn.client_if); + if (p_clreg != NULL){ + bta_gattc_disconncback(p_clreg, (tBTA_GATTC_DATA *) p_msg); + } + p_clcb = bta_gattc_find_int_disconn_clcb((tBTA_GATTC_DATA *) p_msg); + } else { + p_clcb = bta_gattc_find_clcb_by_conn_id(p_msg->layer_specific); + } + + if (p_clcb != NULL) { + rt = bta_gattc_sm_execute(p_clcb, p_msg->event, (tBTA_GATTC_DATA *) p_msg); + } else { + APPL_TRACE_DEBUG("Ignore unknown conn ID: %d\n", p_msg->layer_specific); + } + + break; + } + + + return rt; +} + + +/***************************************************************************** +** Debug Functions +*****************************************************************************/ +#if BTA_GATT_DEBUG == TRUE + +/******************************************************************************* +** +** Function gattc_evt_code +** +** Description +** +** Returns void +** +*******************************************************************************/ +static char *gattc_evt_code(tBTA_GATTC_INT_EVT evt_code) +{ + switch (evt_code) { + case BTA_GATTC_API_OPEN_EVT: + return "BTA_GATTC_API_OPEN_EVT"; + case BTA_GATTC_INT_OPEN_FAIL_EVT: + return "BTA_GATTC_INT_OPEN_FAIL_EVT"; + case BTA_GATTC_API_CANCEL_OPEN_EVT: + return "BTA_GATTC_API_CANCEL_OPEN_EVT"; + case BTA_GATTC_INT_CANCEL_OPEN_OK_EVT: + return "BTA_GATTC_INT_CANCEL_OPEN_OK_EVT"; + case BTA_GATTC_API_READ_EVT: + return "BTA_GATTC_API_READ_EVT"; + case BTA_GATTC_API_WRITE_EVT: + return "BTA_GATTC_API_WRITE_EVT"; + case BTA_GATTC_API_EXEC_EVT: + return "BTA_GATTC_API_EXEC_EVT"; + case BTA_GATTC_API_CLOSE_EVT: + return "BTA_GATTC_API_CLOSE_EVT"; + case BTA_GATTC_API_SEARCH_EVT: + return "BTA_GATTC_API_SEARCH_EVT"; + case BTA_GATTC_API_CONFIRM_EVT: + return "BTA_GATTC_API_CONFIRM_EVT"; + case BTA_GATTC_API_READ_MULTI_EVT: + return "BTA_GATTC_API_READ_MULTI_EVT"; + case BTA_GATTC_INT_CONN_EVT: + return "BTA_GATTC_INT_CONN_EVT"; + case BTA_GATTC_INT_DISCOVER_EVT: + return "BTA_GATTC_INT_DISCOVER_EVT"; + case BTA_GATTC_DISCOVER_CMPL_EVT: + return "BTA_GATTC_DISCOVER_CMPL_EVT"; + case BTA_GATTC_OP_CMPL_EVT: + return "BTA_GATTC_OP_CMPL_EVT"; + case BTA_GATTC_INT_DISCONN_EVT: + return "BTA_GATTC_INT_DISCONN_EVT"; + case BTA_GATTC_INT_START_IF_EVT: + return "BTA_GATTC_INT_START_IF_EVT"; + case BTA_GATTC_API_REG_EVT: + return "BTA_GATTC_API_REG_EVT"; + case BTA_GATTC_API_DEREG_EVT: + return "BTA_GATTC_API_DEREG_EVT"; + case BTA_GATTC_API_REFRESH_EVT: + return "BTA_GATTC_API_REFRESH_EVT"; + case BTA_GATTC_API_CACHE_CLEAN_EVT: + return "BTA_GATTC_API_CACHE_CLEAN_EVT"; + case BTA_GATTC_API_LISTEN_EVT: + return "BTA_GATTC_API_LISTEN_EVT"; + case BTA_GATTC_API_DISABLE_EVT: + return "BTA_GATTC_API_DISABLE_EVT"; + case BTA_GATTC_API_CFG_MTU_EVT: + return "BTA_GATTC_API_CFG_MTU_EVT"; + case BTA_GATTC_API_READ_BY_TYPE_EVT: + return "BTA_GATTC_API_READ_BY_TYPE_EVT"; + case BTA_GATTC_API_READ_MULTI_VAR_EVT: + return "BTA_GATTC_API_READ_MULTI_VAR_EVT"; + default: + return "unknown GATTC event code"; + } +} + +/******************************************************************************* +** +** Function gattc_state_code +** +** Description +** +** Returns void +** +*******************************************************************************/ +static char *gattc_state_code(tBTA_GATTC_STATE state_code) +{ + switch (state_code) { + case BTA_GATTC_IDLE_ST: + return "GATTC_IDLE_ST"; + case BTA_GATTC_W4_CONN_ST: + return "GATTC_W4_CONN_ST"; + case BTA_GATTC_CONN_ST: + return "GATTC_CONN_ST"; + case BTA_GATTC_DISCOVER_ST: + return "GATTC_DISCOVER_ST"; + default: + return "unknown GATTC state code"; + } +} + +#endif /* Debug Functions */ + +void bta_gattc_deinit(void) +{ +#if BTA_DYNAMIC_MEMORY + memset(bta_gattc_cb_ptr, 0, sizeof(tBTA_GATTC_CB)); + FREE_AND_RESET(bta_gattc_cb_ptr); +#endif /* #if BTA_DYNAMIC_MEMORY */ +} + +uint8_t bta_gattc_cl_rcb_active_count(void) +{ + uint8_t count = 0; + + for (uint8_t i = 0; i < BTA_GATTC_CL_MAX; i ++) { + if (bta_gattc_cb.cl_rcb[i].in_use) { + count++; + } + } + + return count; +} +#endif /* GATTC_INCLUDED == TRUE && BLE_INCLUDED == TRUE */ diff --git a/lib/bt/host/bluedroid/bta/gatt/bta_gattc_utils.c b/lib/bt/host/bluedroid/bta/gatt/bta_gattc_utils.c new file mode 100644 index 00000000..2265e282 --- /dev/null +++ b/lib/bt/host/bluedroid/bta/gatt/bta_gattc_utils.c @@ -0,0 +1,1020 @@ +/****************************************************************************** + * + * Copyright (C) 2003-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains the GATT client utility function. + * + ******************************************************************************/ + +#include "common/bt_target.h" + +#if defined(GATTC_INCLUDED) && (GATTC_INCLUDED == TRUE) + +#include + +#include "device/bdaddr.h" +// #include "btif/include/btif_util.h" +#include "bta/utl.h" +#include "bta/bta_sys.h" +#include "bta_gattc_int.h" +#include "stack/l2c_api.h" +#include "osi/allocator.h" + +/***************************************************************************** +** Constants +*****************************************************************************/ + + +static const UINT8 base_uuid[LEN_UUID_128] = {0xFB, 0x34, 0x9B, 0x5F, 0x80, 0x00, 0x00, 0x80, + 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + +static const BD_ADDR dummy_bda = {0, 0, 0, 0, 0, 0}; + +#define GATTC_COMMAND_QUEUE_SIZE_MAX 30 + +/******************************************************************************* +** +** Function bta_gatt_convert_uuid16_to_uuid128 +** +** Description Convert a 16 bits UUID to be an standard 128 bits one. +** +** Returns TRUE if two uuid match; FALSE otherwise. +** +*******************************************************************************/ +void bta_gatt_convert_uuid16_to_uuid128(UINT8 uuid_128[LEN_UUID_128], UINT16 uuid_16) +{ + UINT8 *p = &uuid_128[LEN_UUID_128 - 4]; + + memcpy (uuid_128, base_uuid, LEN_UUID_128); + + UINT16_TO_STREAM(p, uuid_16); +} +/******************************************************************************* +** +** Function bta_gattc_uuid_compare +** +** Description Compare two UUID to see if they are the same. +** +** Returns TRUE if two uuid match; FALSE otherwise. +** +*******************************************************************************/ +BOOLEAN bta_gattc_uuid_compare (const tBT_UUID *p_src, const tBT_UUID *p_tar, BOOLEAN is_precise) +{ + UINT8 su[LEN_UUID_128], tu[LEN_UUID_128]; + const UINT8 *ps, *pt; + + /* any of the UUID is unspecified */ + if (p_src == 0 || p_tar == 0) { + if (is_precise) { + return FALSE; + } else { + return TRUE; + } + } + + /* If both are 16-bit, we can do a simple compare */ + if (p_src->len == 2 && p_tar->len == 2) { + return p_src->uu.uuid16 == p_tar->uu.uuid16; + } + + /* One or both of the UUIDs is 128-bit */ + if (p_src->len == LEN_UUID_16) { + /* convert a 16 bits UUID to 128 bits value */ + bta_gatt_convert_uuid16_to_uuid128(su, p_src->uu.uuid16); + ps = su; + } else { + ps = p_src->uu.uuid128; + } + + if (p_tar->len == LEN_UUID_16) { + /* convert a 16 bits UUID to 128 bits value */ + bta_gatt_convert_uuid16_to_uuid128(tu, p_tar->uu.uuid16); + pt = tu; + } else { + pt = p_tar->uu.uuid128; + } + + return (memcmp(ps, pt, LEN_UUID_128) == 0); +} + +/******************************************************************************* +** +** Function bta_gattc_cl_get_regcb +** +** Description get registration control block by client interface. +** +** Returns pointer to the regcb +** +*******************************************************************************/ +tBTA_GATTC_RCB *bta_gattc_cl_get_regcb(UINT8 client_if) +{ + UINT8 i = 0; + tBTA_GATTC_RCB *p_clrcb = &bta_gattc_cb.cl_rcb[0]; + + for (i = 0; i < BTA_GATTC_CL_MAX; i ++, p_clrcb ++) { + if (p_clrcb->in_use && + p_clrcb->client_if == client_if) { + return p_clrcb; + } + } + return NULL; +} +/******************************************************************************* +** +** Function bta_gattc_num_reg_app +** +** Description find the number of registered application. +** +** Returns pointer to the regcb +** +*******************************************************************************/ +UINT8 bta_gattc_num_reg_app(void) +{ + UINT8 i = 0, j = 0; + + for (i = 0; i < BTA_GATTC_CL_MAX; i ++) { + if (bta_gattc_cb.cl_rcb[i].in_use) { + j ++; + } + } + return j; +} +/******************************************************************************* +** +** Function bta_gattc_find_clcb_by_cif +** +** Description get clcb by client interface and remote bd adddress +** +** Returns pointer to the clcb +** +*******************************************************************************/ +tBTA_GATTC_CLCB *bta_gattc_find_clcb_by_cif (UINT8 client_if, BD_ADDR remote_bda, + tBTA_TRANSPORT transport) +{ + tBTA_GATTC_CLCB *p_clcb = &bta_gattc_cb.clcb[0]; + UINT8 i; + + for (i = 0; i < BTA_GATTC_CLCB_MAX; i ++, p_clcb ++) { + if (p_clcb->in_use && + p_clcb->p_rcb->client_if == client_if && + p_clcb->transport == transport && + bdcmp(p_clcb->bda, remote_bda) == 0) { + return p_clcb; + } + } + return NULL; +} +/******************************************************************************* +** +** Function bta_gattc_find_clcb_by_conn_id +** +** Description get clcb by connection ID +** +** Returns pointer to the clcb +** +*******************************************************************************/ +tBTA_GATTC_CLCB *bta_gattc_find_clcb_by_conn_id (UINT16 conn_id) +{ + tBTA_GATTC_CLCB *p_clcb = &bta_gattc_cb.clcb[0]; + UINT8 i; + + for (i = 0; i < BTA_GATTC_CLCB_MAX; i ++, p_clcb ++) { + if (p_clcb->in_use && + p_clcb->bta_conn_id == conn_id) { + return p_clcb; + } + } + return NULL; +} + +/******************************************************************************* +** +** Function bta_gattc_clcb_alloc +** +** Description allocate CLCB +** +** Returns pointer to the clcb +** +*******************************************************************************/ +tBTA_GATTC_CLCB *bta_gattc_clcb_alloc(tBTA_GATTC_IF client_if, BD_ADDR remote_bda, + tBTA_TRANSPORT transport) +{ + UINT8 i_clcb = 0; + tBTA_GATTC_CLCB *p_clcb = NULL; + + for (i_clcb = 0; i_clcb < BTA_GATTC_CLCB_MAX; i_clcb++) { + if (!bta_gattc_cb.clcb[i_clcb].in_use) { +#if BTA_GATT_DEBUG == TRUE + APPL_TRACE_DEBUG("bta_gattc_clcb_alloc: found clcb[%d] available", i_clcb); +#endif + p_clcb = &bta_gattc_cb.clcb[i_clcb]; + p_clcb->in_use = TRUE; + p_clcb->status = BTA_GATT_OK; + p_clcb->transport = transport; + bdcpy(p_clcb->bda, remote_bda); + p_clcb->searched_service_source = BTA_GATTC_SERVICE_INFO_FROM_UNKNOWN; + p_clcb->p_rcb = bta_gattc_cl_get_regcb(client_if); + if (p_clcb->p_cmd_list == NULL) { + p_clcb->p_cmd_list = list_new(osi_free_func); + } + if ((p_clcb->p_srcb = bta_gattc_find_srcb(remote_bda)) == NULL) { + p_clcb->p_srcb = bta_gattc_srcb_alloc(remote_bda); + } + + if (p_clcb->p_rcb != NULL && p_clcb->p_srcb != NULL) { + p_clcb->p_srcb->num_clcb ++; + p_clcb->p_rcb->num_clcb ++; + } else { + /* release this clcb if clcb or srcb allocation failed */ + p_clcb->in_use = FALSE; + p_clcb = NULL; + } + break; + } + } + return p_clcb; +} +/******************************************************************************* +** +** Function bta_gattc_find_alloc_clcb +** +** Description find or allocate CLCB if not found. +** +** Returns pointer to the clcb +** +*******************************************************************************/ +tBTA_GATTC_CLCB *bta_gattc_find_alloc_clcb(tBTA_GATTC_IF client_if, BD_ADDR remote_bda, + tBTA_TRANSPORT transport) +{ + tBTA_GATTC_CLCB *p_clcb ; + + if ((p_clcb = bta_gattc_find_clcb_by_cif(client_if, remote_bda, transport)) == NULL) { + p_clcb = bta_gattc_clcb_alloc(client_if, remote_bda, transport); + } + return p_clcb; +} + +/******************************************************************************* +** +** Function bta_gattc_clcb_dealloc +** +** Description Deallocte a clcb +** +** Returns pointer to the clcb +** +*******************************************************************************/ +void bta_gattc_clcb_dealloc(tBTA_GATTC_CLCB *p_clcb) +{ + tBTA_GATTC_SERV *p_srcb = NULL; + + if (p_clcb) { + p_srcb = p_clcb->p_srcb; + if (p_srcb->num_clcb) { + p_srcb->num_clcb --; + } + + if (p_clcb->p_rcb->num_clcb) { + p_clcb->p_rcb->num_clcb --; + } + + /* if the srcb is no longer needed, reset the state */ + if ( p_srcb->num_clcb == 0) { + p_srcb->connected = FALSE; + p_srcb->state = BTA_GATTC_SERV_IDLE; + p_srcb->mtu = 0; + + /* clean up cache */ + if (p_srcb->p_srvc_cache) { + list_free(p_srcb->p_srvc_cache); + p_srcb->p_srvc_cache = NULL; + } + } + + if ( p_clcb->p_q_cmd != NULL && !list_contains(p_clcb->p_cmd_list, p_clcb->p_q_cmd)){ + osi_free(p_clcb->p_q_cmd); + p_clcb->p_q_cmd = NULL; + } + // don't forget to clear the command queue before dealloc the clcb. + list_clear(p_clcb->p_cmd_list); + osi_free((void *)p_clcb->p_cmd_list); + p_clcb->p_cmd_list = NULL; + //osi_free_and_reset((void **)&p_clcb->p_q_cmd); + memset(p_clcb, 0, sizeof(tBTA_GATTC_CLCB)); + } else { + APPL_TRACE_ERROR("bta_gattc_clcb_dealloc p_clcb=NULL"); + } +} + +void bta_gattc_clcb_dealloc_by_conn_id(UINT16 conn_id) +{ + tBTA_GATTC_CLCB *p_clcb = bta_gattc_find_clcb_by_conn_id(conn_id); + + if (p_clcb) { + bta_gattc_clcb_dealloc(p_clcb); + } +} + +/******************************************************************************* +** +** Function bta_gattc_find_srcb +** +** Description find server cache by remote bd address currently in use +** +** Returns pointer to the server cache. +** +*******************************************************************************/ +tBTA_GATTC_SERV *bta_gattc_find_srcb(BD_ADDR bda) +{ + tBTA_GATTC_SERV *p_srcb = &bta_gattc_cb.known_server[0]; + UINT8 i; + + for (i = 0; i < BTA_GATTC_KNOWN_SR_MAX; i ++, p_srcb ++) { + if (p_srcb->in_use && bdcmp(p_srcb->server_bda, bda) == 0) { + return p_srcb; + } + } + return NULL; +} + +/******************************************************************************* +** +** Function bta_gattc_find_srvr_cache +** +** Description find server cache by remote bd address +** +** Returns pointer to the server cache. +** +*******************************************************************************/ +tBTA_GATTC_SERV *bta_gattc_find_srvr_cache(BD_ADDR bda) +{ + tBTA_GATTC_SERV *p_srcb = &bta_gattc_cb.known_server[0]; + UINT8 i; + + for (i = 0; i < BTA_GATTC_KNOWN_SR_MAX; i ++, p_srcb ++) { + if (bdcmp(p_srcb->server_bda, bda) == 0) { + return p_srcb; + } + } + return NULL; +} +/******************************************************************************* +** +** Function bta_gattc_find_scb_by_cid +** +** Description find server control block by connection ID +** +** Returns pointer to the server cache. +** +*******************************************************************************/ +tBTA_GATTC_SERV *bta_gattc_find_scb_by_cid (UINT16 conn_id) +{ + tBTA_GATTC_CLCB *p_clcb = bta_gattc_find_clcb_by_conn_id(conn_id); + + if (p_clcb) { + return p_clcb->p_srcb; + } else { + return NULL; + } +} +/******************************************************************************* +** +** Function bta_gattc_srcb_alloc +** +** Description allocate server cache control block +** +** Returns pointer to the server cache. +** +*******************************************************************************/ +tBTA_GATTC_SERV *bta_gattc_srcb_alloc(BD_ADDR bda) +{ + tBTA_GATTC_SERV *p_tcb = &bta_gattc_cb.known_server[0], + *p_recycle = NULL; + BOOLEAN found = FALSE; + UINT8 i; + + for (i = 0; i < BTA_GATTC_KNOWN_SR_MAX; i ++, p_tcb ++) { + if (!p_tcb->in_use) { + found = TRUE; + break; + } else if (!p_tcb->connected) { + p_recycle = p_tcb; + } + } + + /* if not found, try to recycle one known device */ + if (!found && !p_recycle) { + p_tcb = NULL; + } + else if (!found && p_recycle) { + p_tcb = p_recycle; + } + + if (p_tcb != NULL) + { + if (p_tcb->p_srvc_cache != NULL) { + list_free(p_tcb->p_srvc_cache); + p_tcb->p_srvc_cache = NULL; + } + osi_free(p_tcb->p_srvc_list); + p_tcb->p_srvc_list = NULL; + //osi_free_and_reset((void **)&p_tcb->p_srvc_list); + memset(p_tcb, 0 , sizeof(tBTA_GATTC_SERV)); + + p_tcb->in_use = TRUE; + bdcpy(p_tcb->server_bda, bda); + } + return p_tcb; +} + +static BOOLEAN bta_gattc_has_prepare_command_in_queue(tBTA_GATTC_CLCB *p_clcb) +{ + assert(p_clcb != NULL); + + for(list_node_t *sn = list_begin(p_clcb->p_cmd_list); + sn != list_end(p_clcb->p_cmd_list); sn = list_next(sn)) { + + tBTA_GATTC_DATA *cmd_data = (tBTA_GATTC_DATA *)list_node(sn); + if (cmd_data != NULL && ((cmd_data->hdr.event == BTA_GATTC_API_WRITE_EVT && + cmd_data->api_write.write_type == BTA_GATTC_WRITE_PREPARE) || + cmd_data->hdr.event == BTA_GATTC_API_EXEC_EVT)) { + return TRUE; + } + } + + return FALSE; +} +/******************************************************************************* +** +** Function bta_gattc_enqueue +** +** Description enqueue a client request in clcb. +** +** Returns success or failure. +** +*******************************************************************************/ +BOOLEAN bta_gattc_enqueue(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data) +{ + tBTA_GATTC cb_data = {0}; + + if (p_clcb->p_q_cmd == NULL) { + p_clcb->p_q_cmd = p_data; + return TRUE; + } else if ((p_data->hdr.event == BTA_GATTC_API_WRITE_EVT && + p_data->api_write.write_type == BTA_GATTC_WRITE_PREPARE) && + ((p_clcb->p_q_cmd->hdr.event == BTA_GATTC_API_WRITE_EVT && + p_clcb->p_q_cmd->api_write.write_type == BTA_GATTC_WRITE_PREPARE) || + bta_gattc_has_prepare_command_in_queue(p_clcb))) { + APPL_TRACE_DEBUG("%s(), prepare offset = %d", __func__, p_data->api_write.offset); + cb_data.write.status = BTA_GATT_CONGESTED; + cb_data.write.handle = p_data->api_write.handle; + cb_data.write.conn_id = p_clcb->bta_conn_id; + cb_data.write.offset = p_data->api_write.offset; + /* write complete, callback */ + if (p_clcb->p_rcb->p_cback != NULL) { + ( *p_clcb->p_rcb->p_cback)(BTA_GATTC_PREP_WRITE_EVT, (tBTA_GATTC *)&cb_data); + } + return FALSE; + } + else if (p_clcb->p_cmd_list) { + UINT16 len = 0; + tBTA_GATTC_DATA *cmd_data = NULL; + + if (list_length(p_clcb->p_cmd_list) >= GATTC_COMMAND_QUEUE_SIZE_MAX) { + + APPL_TRACE_ERROR("%s(), the gattc command queue is full.", __func__); + cb_data.status = GATT_BUSY; + cb_data.queue_full.conn_id = p_clcb->bta_conn_id; + cb_data.queue_full.is_full = TRUE; + p_clcb->is_full = TRUE; + if (p_clcb->p_rcb->p_cback != NULL) { + ( *p_clcb->p_rcb->p_cback)(BTA_GATTC_QUEUE_FULL_EVT, (tBTA_GATTC *)&cb_data); + } + return FALSE; + } + + if (p_data->hdr.event == BTA_GATTC_API_WRITE_EVT) { + len = p_data->api_write.len; + if ((cmd_data = (tBTA_GATTC_DATA *)osi_malloc(sizeof(tBTA_GATTC_DATA) + len)) != NULL) { + memset(cmd_data, 0, sizeof(tBTA_GATTC_DATA) + len); + memcpy(cmd_data, p_data, sizeof(tBTA_GATTC_DATA)); + cmd_data->api_write.p_value = (UINT8 *)(cmd_data + 1); + memcpy(cmd_data->api_write.p_value, p_data->api_write.p_value, len); + } else { + APPL_TRACE_ERROR("%s(), line = %d, alloc fail, no memery.", __func__, __LINE__); + return FALSE; + } + } else { + if ((cmd_data = (tBTA_GATTC_DATA *)osi_malloc(sizeof(tBTA_GATTC_DATA))) != NULL) { + memset(cmd_data, 0, sizeof(tBTA_GATTC_DATA)); + memcpy(cmd_data, p_data, sizeof(tBTA_GATTC_DATA)); + } else { + APPL_TRACE_ERROR("%s(), line = %d, alloc fail, no memery.", __func__, __LINE__); + return FALSE; + } + } + + //store the command to the command list. + list_append(p_clcb->p_cmd_list, (void *)cmd_data); + return FALSE; + } + + return FALSE; +} + +/******************************************************************************* +** +** Function bta_gattc_check_notif_registry +** +** Description check if the service notificaition has been registered. +** +** Returns +** +*******************************************************************************/ +BOOLEAN bta_gattc_check_notif_registry(tBTA_GATTC_RCB *p_clreg, tBTA_GATTC_SERV *p_srcb, + tBTA_GATTC_NOTIFY *p_notify) +{ + UINT8 i; + + for (i = 0 ; i < BTA_GATTC_NOTIF_REG_MAX; i ++) + { + if (p_clreg->notif_reg[i].in_use && + bdcmp(p_clreg->notif_reg[i].remote_bda, p_srcb->server_bda) == 0 && + p_clreg->notif_reg[i].handle == p_notify->handle) + { + APPL_TRACE_DEBUG("Notification registered!"); + return TRUE; + } + } + return FALSE; + +} +/******************************************************************************* +** +** Function bta_gattc_clear_notif_registration +** +** Description Clear up the notification registration information by BD_ADDR. +** Where handle is between start_handle and end_handle, and +** start_handle and end_handle are boundaries of service +** containing characteristic. +** +** Returns None. +** +*******************************************************************************/ +void bta_gattc_clear_notif_registration(tBTA_GATTC_SERV *p_srcb, UINT16 conn_id, + UINT16 start_handle, UINT16 end_handle) +{ + BD_ADDR remote_bda; + tBTA_GATTC_IF gatt_if; + tBTA_GATTC_RCB *p_clrcb ; + UINT8 i; + tGATT_TRANSPORT transport; + UINT16 handle = 0; + + if (GATT_GetConnectionInfor(conn_id, &gatt_if, remote_bda, &transport)) { + if ((p_clrcb = bta_gattc_cl_get_regcb(gatt_if)) != NULL) { + for (i = 0 ; i < BTA_GATTC_NOTIF_REG_MAX; i ++) { + if (p_clrcb->notif_reg[i].in_use && + !bdcmp(p_clrcb->notif_reg[i].remote_bda, remote_bda)) + { + /* It's enough to get service or characteristic handle, as + * clear boundaries are always around service. + */ + handle = p_clrcb->notif_reg[i].handle; + if (handle >= start_handle && handle <= end_handle) { + memset(&p_clrcb->notif_reg[i], 0, sizeof(tBTA_GATTC_NOTIF_REG)); + } + } + } + } + } else { + APPL_TRACE_ERROR("can not clear indication/notif registration for unknown app"); + } + return; +} + +/******************************************************************************* +** +** Function bta_gattc_clear_notif_registration_by_bda +** +** Description Clear up the notification registration information by BD_ADDR. +** +** +** Returns None. +** +*******************************************************************************/ +void bta_gattc_clear_notif_registration_by_bda(tBTA_GATTC_RCB *p_clrcb, BD_ADDR remote_bda) +{ + if(p_clrcb == NULL) { + return; + } + for (uint8_t i = 0 ; i < BTA_GATTC_NOTIF_REG_MAX; i ++) { + if (p_clrcb->notif_reg[i].in_use && + !bdcmp(p_clrcb->notif_reg[i].remote_bda, remote_bda)) + { + memset(&p_clrcb->notif_reg[i], 0, sizeof(tBTA_GATTC_NOTIF_REG)); + } + } +} + +/******************************************************************************* +** +** Function bta_gattc_mark_bg_conn +** +** Description mark background connection status when a bg connection is initiated +** or terminated. +** +** Returns TRUE if success; FALSE otherwise. +** +*******************************************************************************/ +BOOLEAN bta_gattc_mark_bg_conn (tBTA_GATTC_IF client_if, BD_ADDR_PTR remote_bda_ptr, + BOOLEAN add, BOOLEAN is_listen) +{ + tBTA_GATTC_BG_TCK *p_bg_tck = &bta_gattc_cb.bg_track[0]; + UINT8 i = 0; + tBTA_GATTC_CIF_MASK *p_cif_mask; + + for (i = 0; i < BTA_GATTC_KNOWN_SR_MAX; i ++, p_bg_tck ++) { + if (p_bg_tck->in_use && + ((remote_bda_ptr != NULL && bdcmp(p_bg_tck->remote_bda, remote_bda_ptr) == 0) || + (remote_bda_ptr == NULL && bdcmp(p_bg_tck->remote_bda, dummy_bda) == 0))) { + p_cif_mask = is_listen ? &p_bg_tck->cif_adv_mask : &p_bg_tck->cif_mask; + + if (add) + /* mask on the cif bit */ + { + *p_cif_mask |= (1 << (client_if - 1)); + } else { + if (client_if != 0) { + *p_cif_mask &= (~(1 << (client_if - 1))); + } else { + *p_cif_mask = 0; + } + } + /* no BG connection for this device, make it available */ + if (p_bg_tck->cif_mask == 0 && p_bg_tck->cif_adv_mask == 0) { + memset(p_bg_tck, 0, sizeof(tBTA_GATTC_BG_TCK)); + } + return TRUE; + } + } + if (!add) { + if (remote_bda_ptr) { +#if (!CONFIG_BT_STACK_NO_LOG) + char bdstr[18] = {0}; +#endif + APPL_TRACE_WARNING("%s unable to find the bg connection mask for: %s", __func__, + bdaddr_to_string((bt_bdaddr_t *)remote_bda_ptr, bdstr, sizeof(bdstr))); + } + return FALSE; + } else { /* adding a new device mask */ + for (i = 0, p_bg_tck = &bta_gattc_cb.bg_track[0]; + i < BTA_GATTC_KNOWN_SR_MAX; i ++, p_bg_tck ++) { + if (!p_bg_tck->in_use) { + p_bg_tck->in_use = TRUE; + if (remote_bda_ptr) { + bdcpy(p_bg_tck->remote_bda, remote_bda_ptr); + } else { + bdcpy(p_bg_tck->remote_bda, dummy_bda); + } + + p_cif_mask = is_listen ? &p_bg_tck->cif_adv_mask : &p_bg_tck->cif_mask; + + *p_cif_mask = (1 << (client_if - 1)); + return TRUE; + } + } + APPL_TRACE_ERROR("no available space to mark the bg connection status"); + return FALSE; + } +} +/******************************************************************************* +** +** Function bta_gattc_check_bg_conn +** +** Description check if this is a background connection background connection. +** +** Returns TRUE if success; FALSE otherwise. +** +*******************************************************************************/ +BOOLEAN bta_gattc_check_bg_conn (tBTA_GATTC_IF client_if, BD_ADDR remote_bda, UINT8 role) +{ + tBTA_GATTC_BG_TCK *p_bg_tck = &bta_gattc_cb.bg_track[0]; + UINT8 i = 0; + BOOLEAN is_bg_conn = FALSE; + + for (i = 0; i < BTA_GATTC_KNOWN_SR_MAX && !is_bg_conn; i ++, p_bg_tck ++) { + if (p_bg_tck->in_use && + (bdcmp(p_bg_tck->remote_bda, remote_bda) == 0 || + bdcmp(p_bg_tck->remote_bda, dummy_bda) == 0)) { + if (((p_bg_tck->cif_mask & (1 << (client_if - 1))) != 0) && + role == HCI_ROLE_MASTER) { + is_bg_conn = TRUE; + } + + if (((p_bg_tck->cif_adv_mask & (1 << (client_if - 1))) != 0) && + role == HCI_ROLE_SLAVE) { + is_bg_conn = TRUE; + } + } + } + return is_bg_conn; +} +/******************************************************************************* +** +** Function bta_gattc_send_open_cback +** +** Description send open callback +** +** Returns +** +*******************************************************************************/ +void bta_gattc_send_open_cback( tBTA_GATTC_RCB *p_clreg, tBTA_GATT_STATUS status, + BD_ADDR remote_bda, UINT16 conn_id, + tBTA_TRANSPORT transport, UINT16 mtu) +{ + + tBTA_GATTC cb_data; + + if (p_clreg->p_cback) { + memset(&cb_data, 0, sizeof(tBTA_GATTC)); + + cb_data.open.status = status; + cb_data.open.client_if = p_clreg->client_if; + cb_data.open.conn_id = conn_id; + cb_data.open.mtu = mtu; + cb_data.open.transport = transport; + bdcpy(cb_data.open.remote_bda, remote_bda); + + (*p_clreg->p_cback)(BTA_GATTC_OPEN_EVT, &cb_data); + } +} + +/******************************************************************************* +** +** Function bta_gattc_send_connect_cback +** +** Description send connect callback +** +** Returns +** +*******************************************************************************/ +void bta_gattc_send_connect_cback( tBTA_GATTC_RCB *p_clreg, BD_ADDR remote_bda, UINT16 conn_id, + tBTA_GATT_CONN_PARAMS conn_params, UINT8 link_role, UINT8 ble_addr_type, UINT16 conn_handle) +{ + tBTA_GATTC cb_data; + + if (p_clreg->p_cback) { + memset(&cb_data, 0, sizeof(tBTA_GATTC)); + + cb_data.connect.client_if = p_clreg->client_if; + cb_data.connect.conn_id = conn_id; + cb_data.connect.link_role = link_role; + cb_data.connect.conn_params.interval = conn_params.interval; + cb_data.connect.conn_params.latency = conn_params.latency; + cb_data.connect.conn_params.timeout = conn_params.timeout; + bdcpy(cb_data.connect.remote_bda, remote_bda); + cb_data.connect.ble_addr_type = ble_addr_type; + cb_data.connect.conn_handle = conn_handle; + + (*p_clreg->p_cback)(BTA_GATTC_CONNECT_EVT, &cb_data); + } +} + +/******************************************************************************* +** +** Function bta_gattc_send_disconnect_cback +** +** Description send disconnect callback +** +** Returns +** +*******************************************************************************/ +void bta_gattc_send_disconnect_cback( tBTA_GATTC_RCB *p_clreg, tGATT_DISCONN_REASON reason, + BD_ADDR remote_bda, UINT16 conn_id) +{ + tBTA_GATTC cb_data; + + if (p_clreg->p_cback) { + memset(&cb_data, 0, sizeof(tBTA_GATTC)); + + cb_data.disconnect.reason = reason; + cb_data.disconnect.client_if = p_clreg->client_if; + cb_data.disconnect.conn_id = conn_id; + bdcpy(cb_data.disconnect.remote_bda, remote_bda); + + (*p_clreg->p_cback)(BTA_GATTC_DISCONNECT_EVT, &cb_data); + } +} +/******************************************************************************* +** +** Function bta_gattc_conn_alloc +** +** Description allocate connection tracking spot +** +** Returns pointer to the clcb +** +*******************************************************************************/ +tBTA_GATTC_CONN *bta_gattc_conn_alloc(BD_ADDR remote_bda) +{ + UINT8 i_conn = 0; + tBTA_GATTC_CONN *p_conn = &bta_gattc_cb.conn_track[0]; + + for (i_conn = 0; i_conn < BTA_GATTC_CONN_MAX; i_conn++, p_conn ++) { + if (!p_conn->in_use) { +#if BTA_GATT_DEBUG == TRUE + APPL_TRACE_DEBUG("bta_gattc_conn_alloc: found conn_track[%d] available", i_conn); +#endif + p_conn->in_use = TRUE; + bdcpy(p_conn->remote_bda, remote_bda); + return p_conn; + } + } + return NULL; +} + +/******************************************************************************* +** +** Function bta_gattc_conn_find +** +** Description allocate connection tracking spot +** +** Returns pointer to the clcb +** +*******************************************************************************/ +tBTA_GATTC_CONN *bta_gattc_conn_find(BD_ADDR remote_bda) +{ + UINT8 i_conn = 0; + tBTA_GATTC_CONN *p_conn = &bta_gattc_cb.conn_track[0]; + + for (i_conn = 0; i_conn < BTA_GATTC_CONN_MAX; i_conn++, p_conn ++) { + if (p_conn->in_use && bdcmp(remote_bda, p_conn->remote_bda) == 0) { +#if BTA_GATT_DEBUG == TRUE + APPL_TRACE_DEBUG("bta_gattc_conn_find: found conn_track[%d] matched", i_conn); +#endif + return p_conn; + } + } + return NULL; +} + + +/******************************************************************************* +** +** Function bta_gattc_conn_find_alloc +** +** Description find or allocate connection tracking spot +** +** Returns pointer to the clcb +** +*******************************************************************************/ +tBTA_GATTC_CONN *bta_gattc_conn_find_alloc(BD_ADDR remote_bda) +{ + tBTA_GATTC_CONN *p_conn = bta_gattc_conn_find (remote_bda); + + if (p_conn == NULL) { + p_conn = bta_gattc_conn_alloc(remote_bda); + } + return p_conn; +} + +/******************************************************************************* +** +** Function bta_gattc_conn_dealloc +** +** Description de-allocate connection tracking spot +** +** Returns pointer to the clcb +** +*******************************************************************************/ +BOOLEAN bta_gattc_conn_dealloc(BD_ADDR remote_bda) +{ + tBTA_GATTC_CONN *p_conn = bta_gattc_conn_find (remote_bda); + + if (p_conn != NULL) { + p_conn->in_use = FALSE; + memset(p_conn->remote_bda, 0, BD_ADDR_LEN); + return TRUE; + } + return FALSE; +} + +/******************************************************************************* +** +** Function bta_gattc_find_int_conn_clcb +** +** Description try to locate a clcb when an internal connecion event arrives. +** +** Returns pointer to the clcb +** +*******************************************************************************/ +tBTA_GATTC_CLCB *bta_gattc_find_int_conn_clcb(tBTA_GATTC_DATA *p_msg) +{ + tBTA_GATTC_CLCB *p_clcb = NULL; + + if (p_msg->int_conn.role == HCI_ROLE_SLAVE) { + bta_gattc_conn_find_alloc(p_msg->int_conn.remote_bda); + } + + /* try to locate a logic channel */ + if ((p_clcb = bta_gattc_find_clcb_by_cif(p_msg->int_conn.client_if, + p_msg->int_conn.remote_bda, + p_msg->int_conn.transport)) == NULL) { + /* for a background connection or listening connection */ + if (/*p_msg->int_conn.role == HCI_ROLE_SLAVE || */ + bta_gattc_check_bg_conn(p_msg->int_conn.client_if, + p_msg->int_conn.remote_bda, + p_msg->int_conn.role)) { + /* allocate a new channel */ + p_clcb = bta_gattc_clcb_alloc(p_msg->int_conn.client_if, + p_msg->int_conn.remote_bda, + p_msg->int_conn.transport); + } + } + return p_clcb; +} + +/******************************************************************************* +** +** Function bta_gattc_find_int_disconn_clcb +** +** Description try to locate a clcb when an internal disconnect callback arrives. +** +** Returns pointer to the clcb +** +*******************************************************************************/ +tBTA_GATTC_CLCB *bta_gattc_find_int_disconn_clcb(tBTA_GATTC_DATA *p_msg) +{ + tBTA_GATTC_CLCB *p_clcb = NULL; + + bta_gattc_conn_dealloc(p_msg->int_conn.remote_bda); + if ((p_clcb = bta_gattc_find_clcb_by_conn_id(p_msg->int_conn.hdr.layer_specific)) == NULL) { + /* connection attempt failed, send connection callback event */ + p_clcb = bta_gattc_find_clcb_by_cif(p_msg->int_conn.client_if, + p_msg->int_conn.remote_bda, + p_msg->int_conn.transport); + } + if (p_clcb == NULL) { + APPL_TRACE_DEBUG(" disconnection ID: [%d] not used by BTA", + p_msg->int_conn.hdr.layer_specific); + } + return p_clcb; +} + +void bta_to_btif_uuid(bt_uuid_t *p_dest, tBT_UUID *p_src) +{ + int i = 0; + + if (p_src->len == LEN_UUID_16 || p_src->len == LEN_UUID_32) + { + for(i=0; i != 16; ++i) { + p_dest->uu[i] = base_uuid[i]; + } + } + + switch (p_src->len) + { + case 0: + break; + + case LEN_UUID_16: + p_dest->uu[12] = p_src->uu.uuid16 & 0xff; + p_dest->uu[13] = (p_src->uu.uuid16 >> 8) & 0xff; + break; + + case LEN_UUID_32: + p_dest->uu[12] = p_src->uu.uuid16 & 0xff; + p_dest->uu[13] = (p_src->uu.uuid16 >> 8) & 0xff; + p_dest->uu[14] = (p_src->uu.uuid32 >> 16) & 0xff; + p_dest->uu[15] = (p_src->uu.uuid32 >> 24) & 0xff; + break; + + case LEN_UUID_128: + for(i=0; i != 16; ++i) + p_dest->uu[i] = p_src->uu.uuid128[i]; + break; + + default: + APPL_TRACE_ERROR("%s: Unknown UUID length %d!", __FUNCTION__, p_src->len); + break; + } +} + + +#endif /* BTA_GATT_INCLUDED */ diff --git a/lib/bt/host/bluedroid/bta/gatt/bta_gatts_act.c b/lib/bt/host/bluedroid/bta/gatt/bta_gatts_act.c new file mode 100644 index 00000000..d09eaa96 --- /dev/null +++ b/lib/bt/host/bluedroid/bta/gatt/bta_gatts_act.c @@ -0,0 +1,1070 @@ +/****************************************************************************** + * + * Copyright (C) 2003-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains the GATT Server action functions for the state + * machine. + * + ******************************************************************************/ + + +#include "common/bt_target.h" + +#if defined(GATTS_INCLUDED) && (GATTS_INCLUDED == TRUE) + +#include "bta/utl.h" +#include "bta/bta_sys.h" +#include "bta_gatts_int.h" +#include "bta/bta_gatts_co.h" +#include "stack/btm_ble_api.h" +#include +#include "osi/allocator.h" +#include "l2c_int.h" + +static void bta_gatts_nv_save_cback(BOOLEAN is_saved, tGATTS_HNDL_RANGE *p_hndl_range); +static BOOLEAN bta_gatts_nv_srv_chg_cback(tGATTS_SRV_CHG_CMD cmd, tGATTS_SRV_CHG_REQ *p_req, + tGATTS_SRV_CHG_RSP *p_rsp); + +static void bta_gatts_conn_cback (tGATT_IF gatt_if, BD_ADDR bda, UINT16 conn_id, + BOOLEAN connected, tGATT_DISCONN_REASON reason, + tGATT_TRANSPORT transport); +static void bta_gatts_send_request_cback (UINT16 conn_id, + UINT32 trans_id, + tGATTS_REQ_TYPE req_type, tGATTS_DATA *p_data); +static void bta_gatts_cong_cback (UINT16 conn_id, BOOLEAN congested); +extern void btc_congest_callback(tBTA_GATTS *param); + +static const tGATT_CBACK bta_gatts_cback = { + bta_gatts_conn_cback, + NULL, + NULL, + NULL, + bta_gatts_send_request_cback, + NULL, + bta_gatts_cong_cback +}; + +const tGATT_APPL_INFO bta_gatts_nv_cback = { + bta_gatts_nv_save_cback, + bta_gatts_nv_srv_chg_cback +}; + +/******************************************************************************* +** +** Function bta_gatts_nv_save_cback +** +** Description NV save callback function. +** +** Parameter is_add: true is to add a handle range; otherwise is to delete. +** Returns none. +** +*******************************************************************************/ +static void bta_gatts_nv_save_cback(BOOLEAN is_add, tGATTS_HNDL_RANGE *p_hndl_range) +{ + bta_gatts_co_update_handle_range(is_add, (tBTA_GATTS_HNDL_RANGE *)p_hndl_range); +} + + +/******************************************************************************* +** +** Function bta_gatts_nv_srv_chg_cback +** +** Description NV save callback function. +** +** Parameter is_add: true is to add a handle range; otherwise is to delete. +** Returns none. +** +*******************************************************************************/ +static BOOLEAN bta_gatts_nv_srv_chg_cback(tGATTS_SRV_CHG_CMD cmd, + tGATTS_SRV_CHG_REQ *p_req, tGATTS_SRV_CHG_RSP *p_rsp) +{ + return bta_gatts_co_srv_chg((tBTA_GATTS_SRV_CHG_CMD) cmd, + (tBTA_GATTS_SRV_CHG_REQ *) p_req, + (tBTA_GATTS_SRV_CHG_RSP *) p_rsp); +} + + +/******************************************************************************* +** +** Function bta_gatts_enable +** +** Description enable BTA GATTS module. +** +** Returns none. +** +*******************************************************************************/ +void bta_gatts_enable(tBTA_GATTS_CB *p_cb) +{ + UINT8 index = 0; + tBTA_GATTS_HNDL_RANGE handle_range; + + if (p_cb->enabled) { + APPL_TRACE_DEBUG("GATTS already enabled."); + } else { + memset(p_cb, 0, sizeof(tBTA_GATTS_CB)); + + p_cb->enabled = TRUE; + + while ( bta_gatts_co_load_handle_range(index, &handle_range)) { + GATTS_AddHandleRange((tGATTS_HNDL_RANGE *)&handle_range); + memset(&handle_range, 0, sizeof(tGATTS_HNDL_RANGE)); + index++; + } + + APPL_TRACE_DEBUG("bta_gatts_enable: num of handle range added=%d", index); + + if (!GATTS_NVRegister(&bta_gatts_nv_cback)) { + APPL_TRACE_ERROR("BTA GATTS NV register failed."); + } + } +} + +/******************************************************************************* +** +** Function bta_gatts_api_disable +** +** Description disable BTA GATTS module. +** +** Returns none. +** +*******************************************************************************/ +void bta_gatts_api_disable(tBTA_GATTS_CB *p_cb) +{ + UINT8 i; + + if (p_cb->enabled) { + for (i = 0; i < BTA_GATTS_MAX_APP_NUM; i ++) { + if (p_cb->rcb[i].in_use) { + GATT_Deregister(p_cb->rcb[i].gatt_if); + } + } + memset(p_cb, 0, sizeof(tBTA_GATTS_CB)); + } else { + APPL_TRACE_ERROR("GATTS not enabled"); + } +} + +/******************************************************************************* +** +** Function bta_gatts_register +** +** Description register an application. +** +** Returns none. +** +*******************************************************************************/ +void bta_gatts_register(tBTA_GATTS_CB *p_cb, tBTA_GATTS_DATA *p_msg) +{ + tBTA_GATTS_INT_START_IF *p_buf; + tBTA_GATTS cb_data; + tBTA_GATT_STATUS status = BTA_GATT_OK; + UINT8 i, first_unuse = 0xff; + + if (p_cb->enabled == FALSE) { + bta_gatts_enable(p_cb); + } + + for (i = 0; i < BTA_GATTS_MAX_APP_NUM; i ++) { + if (p_cb->rcb[i].in_use) { + if (bta_gatts_uuid_compare(p_cb->rcb[i].app_uuid, p_msg->api_reg.app_uuid)) { + APPL_TRACE_ERROR("application already registered.\n"); + status = BTA_GATT_DUP_REG; + break; + } + } + } + + if (status == BTA_GATT_OK) { + for (i = 0; i < BTA_GATTS_MAX_APP_NUM; i ++) { + if (first_unuse == 0xff && !p_cb->rcb[i].in_use) { + first_unuse = i; + break; + } + } + + cb_data.reg_oper.server_if = BTA_GATTS_INVALID_IF; +// btla-specific ++ + memcpy(&cb_data.reg_oper.uuid, &p_msg->api_reg.app_uuid, sizeof(tBT_UUID)); +// btla-specific -- + if (first_unuse != 0xff) { + APPL_TRACE_VERBOSE("register application first_unuse rcb_idx = %d", first_unuse); + + p_cb->rcb[first_unuse].in_use = TRUE; + p_cb->rcb[first_unuse].p_cback = p_msg->api_reg.p_cback; + memcpy(&p_cb->rcb[first_unuse].app_uuid, &p_msg->api_reg.app_uuid, sizeof(tBT_UUID)); + cb_data.reg_oper.server_if = + p_cb->rcb[first_unuse].gatt_if = + GATT_Register(&p_msg->api_reg.app_uuid, &bta_gatts_cback); + if ( !p_cb->rcb[first_unuse].gatt_if) { + status = BTA_GATT_NO_RESOURCES; + } else { + if ((p_buf = + (tBTA_GATTS_INT_START_IF *) osi_malloc(sizeof(tBTA_GATTS_INT_START_IF))) != NULL) { + p_buf->hdr.event = BTA_GATTS_INT_START_IF_EVT; + p_buf->server_if = p_cb->rcb[first_unuse].gatt_if; + + bta_sys_sendmsg(p_buf); + } else { + status = BTA_GATT_NO_RESOURCES; + memset( &p_cb->rcb[first_unuse], 0 , sizeof(tBTA_GATTS_RCB)); + } + } + } else { + status = BTA_GATT_NO_RESOURCES; + } + + } + cb_data.reg_oper.status = status; + if (p_msg->api_reg.p_cback) { + (*p_msg->api_reg.p_cback)(BTA_GATTS_REG_EVT, &cb_data); + } +} + + +/******************************************************************************* +** +** Function bta_gatts_start_if +** +** Description start an application interface. +** +** Returns none. +** +*******************************************************************************/ +void bta_gatts_start_if(tBTA_GATTS_CB *p_cb, tBTA_GATTS_DATA *p_msg) +{ + UNUSED(p_cb); + + if (bta_gatts_find_app_rcb_by_app_if(p_msg->int_start_if.server_if)) { + GATT_StartIf(p_msg->int_start_if.server_if); + } else { + APPL_TRACE_ERROR("Unable to start app.: Unknown interface =%d", + p_msg->int_start_if.server_if ); + } +} +/******************************************************************************* +** +** Function bta_gatts_deregister +** +** Description deregister an application. +** +** Returns none. +** +*******************************************************************************/ +void bta_gatts_deregister(tBTA_GATTS_CB *p_cb, tBTA_GATTS_DATA *p_msg) +{ + tBTA_GATT_STATUS status = BTA_GATT_ERROR; + tBTA_GATTS_CBACK *p_cback = NULL; + UINT8 i; + tBTA_GATTS cb_data; + + cb_data.reg_oper.server_if = p_msg->api_dereg.server_if; + cb_data.reg_oper.status = status; + + for (i = 0; i < BTA_GATTS_MAX_APP_NUM; i ++) { + if (p_cb->rcb[i].in_use && p_cb->rcb[i].gatt_if == p_msg->api_dereg.server_if) { + p_cback = p_cb->rcb[i].p_cback; + status = BTA_GATT_OK; + + /* deregister the app */ + GATT_Deregister(p_cb->rcb[i].gatt_if); + + /* reset cb */ + memset(&p_cb->rcb[i], 0, sizeof(tBTA_GATTS_RCB)); + cb_data.reg_oper.status = status; + break; + } + } + + if (p_cback) { + (*p_cback)(BTA_GATTS_DEREG_EVT, &cb_data); + } else { + APPL_TRACE_ERROR("application not registered."); + } +} +/******************************************************************************* +** +** Function bta_gatts_create_srvc +** +** Description action function to create a service. +** +** Returns none. +** +*******************************************************************************/ +void bta_gatts_create_srvc(tBTA_GATTS_CB *p_cb, tBTA_GATTS_DATA *p_msg) +{ + UINT8 rcb_idx; + tBTA_GATTS cb_data; + UINT8 srvc_idx; + UINT16 service_id = 0; + + cb_data.create.status = BTA_GATT_ERROR; + + rcb_idx = bta_gatts_find_app_rcb_idx_by_app_if(p_cb, p_msg->api_create_svc.server_if); + + APPL_TRACE_DEBUG("create service rcb_idx = %d", rcb_idx); + + if (rcb_idx != BTA_GATTS_INVALID_APP) { + if ((srvc_idx = bta_gatts_alloc_srvc_cb(p_cb, rcb_idx)) != BTA_GATTS_INVALID_APP) { + /* create the service now */ + service_id = GATTS_CreateService (p_cb->rcb[rcb_idx].gatt_if, + &p_msg->api_create_svc.service_uuid, + p_msg->api_create_svc.inst, + p_msg->api_create_svc.num_handle, + p_msg->api_create_svc.is_pri); + + if (service_id != 0) { + memcpy(&p_cb->srvc_cb[srvc_idx].service_uuid, + &p_msg->api_create_svc.service_uuid, sizeof(tBT_UUID)); + p_cb->srvc_cb[srvc_idx].service_id = service_id; + p_cb->srvc_cb[srvc_idx].inst_num = p_msg->api_create_svc.inst; + p_cb->srvc_cb[srvc_idx].idx = srvc_idx; + + cb_data.create.status = BTA_GATT_OK; + cb_data.create.service_id = service_id; + + cb_data.create.is_primary = p_msg->api_create_svc.is_pri; + + cb_data.create.server_if = p_cb->rcb[rcb_idx].gatt_if; + } else { + cb_data.status = BTA_GATT_ERROR; + memset(&p_cb->srvc_cb[srvc_idx], 0, sizeof(tBTA_GATTS_SRVC_CB)); + APPL_TRACE_ERROR("service creation failed."); + } + + memcpy(&cb_data.create.uuid, &p_msg->api_create_svc.service_uuid, sizeof(tBT_UUID)); + cb_data.create.svc_instance = p_msg->api_create_svc.inst; + + } + if (p_cb->rcb[rcb_idx].p_cback) { + (* p_cb->rcb[rcb_idx].p_cback)(BTA_GATTS_CREATE_EVT, &cb_data); + } + } else { /* application not registered */ + APPL_TRACE_ERROR("Application not registered"); + } +} +/******************************************************************************* +** +** Function bta_gatts_add_include_srvc +** +** Description action function to add an included service. +** +** Returns none. +** +*******************************************************************************/ +void bta_gatts_add_include_srvc(tBTA_GATTS_SRVC_CB *p_srvc_cb, tBTA_GATTS_DATA *p_msg) +{ + tBTA_GATTS_RCB *p_rcb = &bta_gatts_cb.rcb[p_srvc_cb->rcb_idx]; + UINT16 attr_id = 0; + tBTA_GATTS cb_data; + + attr_id = GATTS_AddIncludeService(p_msg->api_add_incl_srvc.hdr.layer_specific, + p_msg->api_add_incl_srvc.included_service_id); + + cb_data.add_result.server_if = p_rcb->gatt_if; + cb_data.add_result.service_id = p_msg->api_add_incl_srvc.hdr.layer_specific; + cb_data.add_result.attr_id = attr_id; + + if (attr_id) { + cb_data.add_result.status = BTA_GATT_OK; + } else { + cb_data.add_result.status = BTA_GATT_ERROR; + } + + if (p_rcb->p_cback) { + (*p_rcb->p_cback)(BTA_GATTS_ADD_INCL_SRVC_EVT, &cb_data); + } +} +/******************************************************************************* +** +** Function bta_gatts_add_char +** +** Description action function to add characteristic. +** +** Returns none. +** +*******************************************************************************/ +void bta_gatts_add_char(tBTA_GATTS_SRVC_CB *p_srvc_cb, tBTA_GATTS_DATA *p_msg) +{ + tBTA_GATTS_RCB *p_rcb = &bta_gatts_cb.rcb[p_srvc_cb->rcb_idx]; + UINT16 attr_id = 0; + tBTA_GATTS cb_data; + + tGATT_ATTR_VAL *p_attr_val = NULL; + tGATTS_ATTR_CONTROL *p_control = NULL; + + if(p_msg->api_add_char.attr_val.attr_max_len != 0){ + p_attr_val = &p_msg->api_add_char.attr_val; + } + + if(p_msg->api_add_char.control.auto_rsp != 0){ + p_control = &p_msg->api_add_char.control; + } + + + attr_id = GATTS_AddCharacteristic(p_msg->api_add_char.hdr.layer_specific, + &p_msg->api_add_char.char_uuid, + p_msg->api_add_char.perm, + p_msg->api_add_char.property, p_attr_val, p_control); + cb_data.add_result.server_if = p_rcb->gatt_if; + cb_data.add_result.service_id = p_msg->api_add_char.hdr.layer_specific; + cb_data.add_result.attr_id = attr_id; +// btla-specific ++ + memcpy(&cb_data.add_result.char_uuid, &p_msg->api_add_char.char_uuid, sizeof(tBT_UUID)); +// btla-specific -- + + if (attr_id) { + cb_data.add_result.status = BTA_GATT_OK; + } else { + cb_data.add_result.status = BTA_GATT_ERROR; + } + if((p_attr_val != NULL) && (p_attr_val->attr_val != NULL)){ + osi_free(p_attr_val->attr_val); + } + + if (p_rcb->p_cback) { + (*p_rcb->p_cback)(BTA_GATTS_ADD_CHAR_EVT, &cb_data); + } +} + +/******************************************************************************* +** +** Function bta_gatts_add_char_descr +** +** Description action function to add characteristic descriptor. +** +** Returns none. +** +*******************************************************************************/ +void bta_gatts_add_char_descr(tBTA_GATTS_SRVC_CB *p_srvc_cb, tBTA_GATTS_DATA *p_msg) +{ + tBTA_GATTS_RCB *p_rcb = &bta_gatts_cb.rcb[p_srvc_cb->rcb_idx]; + UINT16 attr_id = 0; + tBTA_GATTS cb_data; + tGATT_ATTR_VAL *p_attr_val = NULL; + tGATTS_ATTR_CONTROL *p_control = NULL; + + if (p_msg->api_add_char_descr.attr_val.attr_max_len != 0) { + p_attr_val = &p_msg->api_add_char_descr.attr_val; + } + + if (p_msg->api_add_char_descr.control.auto_rsp != 0) { + p_control = &p_msg->api_add_char_descr.control; + } + attr_id = GATTS_AddCharDescriptor(p_msg->api_add_char_descr.hdr.layer_specific, + p_msg->api_add_char_descr.perm, + &p_msg->api_add_char_descr.descr_uuid, p_attr_val, + p_control); + + cb_data.add_result.server_if = p_rcb->gatt_if; + cb_data.add_result.service_id = p_msg->api_add_char_descr.hdr.layer_specific; + cb_data.add_result.attr_id = attr_id; +// btla-specific ++ + memcpy(&cb_data.add_result.char_uuid, &p_msg->api_add_char_descr.descr_uuid, sizeof(tBT_UUID)); +// btla-specific -- + + if (attr_id) { + cb_data.add_result.status = BTA_GATT_OK; + } else { + cb_data.add_result.status = BTA_GATT_ERROR; + } + if((p_attr_val != NULL) && (p_attr_val->attr_val != NULL)){ + osi_free(p_attr_val->attr_val); + } + + if (p_rcb->p_cback) { + (*p_rcb->p_cback)(BTA_GATTS_ADD_CHAR_DESCR_EVT, &cb_data); + } + +} + +/******************************************************************************* +** +** Function bta_gatts_set_attr_value +** +** Description This function is used to set the attribute value. +** +** Returns None. +** +*******************************************************************************/ +void bta_gatts_set_attr_value(tBTA_GATTS_SRVC_CB *p_srvc_cb, tBTA_GATTS_DATA *p_msg) +{ + tBTA_GATTS_RCB *p_rcb = &bta_gatts_cb.rcb[p_srvc_cb->rcb_idx]; + UINT16 service_id = p_srvc_cb->service_id; + tBTA_GATTS cb_data; + tBTA_GATT_STATUS gatts_status; + gatts_status = GATTS_SetAttributeValue(p_msg->api_set_val.hdr.layer_specific, + p_msg->api_set_val.length, + p_msg->api_set_val.value); + + cb_data.attr_val.server_if = p_rcb->gatt_if; + cb_data.attr_val.service_id = service_id; + cb_data.attr_val.attr_id = p_msg->api_set_val.hdr.layer_specific; + cb_data.attr_val.status = gatts_status; + + if (p_msg->api_set_val.value != NULL){ + osi_free(p_msg->api_set_val.value); + } + + if (p_rcb->p_cback) { + (*p_rcb->p_cback)(BTA_GATTS_SET_ATTR_VAL_EVT, &cb_data); + } +} + +/******************************************************************************* +** +** Function bta_gatts_get_attr_value +** +** Description This function retrieves the attribute value associated with +** the given attribute handle. +** +** Returns tGATT_STATUS - GATT status indicating success or failure in +** retrieving the attribute value. +** +*******************************************************************************/ + +tGATT_STATUS bta_gatts_get_attr_value(UINT16 attr_handle, UINT16 *length, UINT8 **value) +{ + if (GATTS_GetAttributeValueInternal(attr_handle, length, value) == 0) { + return 0; + } + + return GATTS_GetAttributeValue(attr_handle, length, value); +} + +/******************************************************************************* +** +** Function bta_gatts_delete_service +** +** Description action function to delete a service. +** +** Returns none. +** +*******************************************************************************/ +void bta_gatts_delete_service(tBTA_GATTS_SRVC_CB *p_srvc_cb, tBTA_GATTS_DATA *p_msg) +{ + tBTA_GATTS_RCB *p_rcb = &bta_gatts_cb.rcb[p_srvc_cb->rcb_idx]; + tBTA_GATTS cb_data; + + cb_data.srvc_oper.server_if = p_rcb->gatt_if; + cb_data.srvc_oper.service_id = p_msg->api_add_incl_srvc.hdr.layer_specific; + + if (GATTS_DeleteService(p_rcb->gatt_if, + &p_srvc_cb->service_uuid, + p_srvc_cb->inst_num)) { + cb_data.srvc_oper.status = BTA_GATT_OK; + memset(p_srvc_cb, 0, sizeof(tBTA_GATTS_SRVC_CB)); + } else { + cb_data.srvc_oper.status = BTA_GATT_ERROR; + } + + if (p_rcb->p_cback) { + (*p_rcb->p_cback)(BTA_GATTS_DELELTE_EVT, &cb_data); + } + +} +/******************************************************************************* +** +** Function bta_gatts_start_service +** +** Description action function to start a service. +** +** Returns none. +** +*******************************************************************************/ +void bta_gatts_start_service(tBTA_GATTS_SRVC_CB *p_srvc_cb, tBTA_GATTS_DATA *p_msg) +{ + tBTA_GATTS_RCB *p_rcb = &bta_gatts_cb.rcb[p_srvc_cb->rcb_idx]; + tBTA_GATTS cb_data; + + cb_data.srvc_oper.server_if = p_rcb->gatt_if; + cb_data.srvc_oper.service_id = p_msg->api_add_incl_srvc.hdr.layer_specific; + + if (GATTS_StartService(p_rcb->gatt_if, + p_srvc_cb->service_id, + p_msg->api_start.transport) == GATT_SUCCESS) { + APPL_TRACE_DEBUG("bta_gatts_start_service service_id= %d", p_srvc_cb->service_id); + cb_data.srvc_oper.status = BTA_GATT_OK; + } else { + cb_data.srvc_oper.status = BTA_GATT_ERROR; + } + + if (p_rcb->p_cback) { + (*p_rcb->p_cback)(BTA_GATTS_START_EVT, &cb_data); + } + +} +/******************************************************************************* +** +** Function bta_gatts_stop_service +** +** Description action function to stop a service. +** +** Returns none. +** +*******************************************************************************/ +void bta_gatts_stop_service(tBTA_GATTS_SRVC_CB *p_srvc_cb, tBTA_GATTS_DATA *p_msg) +{ + tBTA_GATTS_RCB *p_rcb = &bta_gatts_cb.rcb[p_srvc_cb->rcb_idx]; + tBTA_GATTS cb_data; + UNUSED(p_msg); + + GATTS_StopService(p_srvc_cb->service_id); + cb_data.srvc_oper.server_if = p_rcb->gatt_if; + cb_data.srvc_oper.service_id = p_srvc_cb->service_id; + cb_data.srvc_oper.status = BTA_GATT_OK; + APPL_TRACE_DEBUG("bta_gatts_stop_service service_id= %d", p_srvc_cb->service_id); + + if (p_rcb->p_cback) { + (*p_rcb->p_cback)(BTA_GATTS_STOP_EVT, &cb_data); + } + +} +/******************************************************************************* +** +** Function bta_gatts_send_rsp +** +** Description GATTS send response. +** +** Returns none. +** +*******************************************************************************/ +void bta_gatts_send_rsp (tBTA_GATTS_CB *p_cb, tBTA_GATTS_DATA *p_msg) +{ + UNUSED(p_cb); + + if (GATTS_SendRsp (p_msg->api_rsp.hdr.layer_specific, + p_msg->api_rsp.trans_id, + p_msg->api_rsp.status, + (tGATTS_RSP *)p_msg->api_rsp.p_rsp) != GATT_SUCCESS) { + APPL_TRACE_ERROR("Sending response failed\n"); + } + +} +/******************************************************************************* +** +** Function bta_gatts_indicate_handle +** +** Description GATTS send handle value indication or notification. +** +** Returns none. +** +*******************************************************************************/ +void bta_gatts_indicate_handle (tBTA_GATTS_CB *p_cb, tBTA_GATTS_DATA *p_msg) +{ + tBTA_GATTS_SRVC_CB *p_srvc_cb; + tBTA_GATTS_RCB *p_rcb = NULL; + tBTA_GATT_STATUS status = BTA_GATT_ILLEGAL_PARAMETER; + tGATT_IF gatt_if; + BD_ADDR remote_bda; + tBTA_TRANSPORT transport; + tBTA_GATTS cb_data; + + p_srvc_cb = bta_gatts_find_srvc_cb_by_attr_id (p_cb, p_msg->api_indicate.attr_id); + + if (p_srvc_cb ) { + if (GATT_GetConnectionInfor(p_msg->api_indicate.hdr.layer_specific, + &gatt_if, remote_bda, &transport)) { + p_rcb = bta_gatts_find_app_rcb_by_app_if(gatt_if); + + if (p_msg->api_indicate.need_confirm) { + + status = GATTS_HandleValueIndication (p_msg->api_indicate.hdr.layer_specific, + p_msg->api_indicate.attr_id, + p_msg->api_indicate.len, + p_msg->api_indicate.value); + } else { + l2ble_update_att_acl_pkt_num(L2CA_DECREASE_BTU_NUM, NULL); + status = GATTS_HandleValueNotification (p_msg->api_indicate.hdr.layer_specific, + p_msg->api_indicate.attr_id, + p_msg->api_indicate.len, + p_msg->api_indicate.value); + } + + /* if over BR_EDR, inform PM for mode change */ + if (transport == BTA_TRANSPORT_BR_EDR) { + bta_sys_busy(BTA_ID_GATTS, BTA_ALL_APP_ID, remote_bda); + bta_sys_idle(BTA_ID_GATTS, BTA_ALL_APP_ID, remote_bda); + } + } else { + APPL_TRACE_ERROR("Unknown connection ID: %d fail sending notification", + p_msg->api_indicate.hdr.layer_specific); + } + + if ((status != GATT_SUCCESS || !p_msg->api_indicate.need_confirm) && + p_rcb && p_cb->rcb[p_srvc_cb->rcb_idx].p_cback) { + cb_data.req_data.status = status; + cb_data.req_data.conn_id = p_msg->api_indicate.hdr.layer_specific; + cb_data.req_data.value = NULL; + cb_data.req_data.data_len = 0; + cb_data.req_data.handle = p_msg->api_indicate.attr_id; + + if (p_msg->api_indicate.len > 0) { + cb_data.req_data.value = (uint8_t *) osi_malloc(p_msg->api_indicate.len); + if (cb_data.req_data.value != NULL) { + memset(cb_data.req_data.value, 0, p_msg->api_indicate.len); + cb_data.req_data.data_len = p_msg->api_indicate.len; + memcpy(cb_data.req_data.value, p_msg->api_indicate.value, p_msg->api_indicate.len); + } else { + APPL_TRACE_ERROR("%s, malloc failed", __func__); + } + } else { + APPL_TRACE_ERROR("%s, incorrect length", __func__); + } + (*p_rcb->p_cback)(BTA_GATTS_CONF_EVT, &cb_data); + if (cb_data.req_data.value != NULL) { + osi_free(cb_data.req_data.value); + cb_data.req_data.value = NULL; + } + } + } else { + APPL_TRACE_ERROR("Not an registered servce attribute ID: 0x%04x", + p_msg->api_indicate.attr_id); + } +} + + +/******************************************************************************* +** +** Function bta_gatts_open +** +** Description +** +** Returns none. +** +*******************************************************************************/ +void bta_gatts_open (tBTA_GATTS_CB *p_cb, tBTA_GATTS_DATA *p_msg) +{ + tBTA_GATTS_RCB *p_rcb = NULL; + tBTA_GATT_STATUS status = BTA_GATT_ERROR; + UINT16 conn_id; + tBTA_GATTS_OPEN open; + UNUSED(p_cb); + + if ((p_rcb = bta_gatts_find_app_rcb_by_app_if(p_msg->api_open.server_if)) != NULL) { + /* should always get the connection ID */ + if (GATT_Connect(p_rcb->gatt_if, p_msg->api_open.remote_bda, BLE_ADDR_UNKNOWN_TYPE, + p_msg->api_open.is_direct, p_msg->api_open.transport, FALSE)) { + status = BTA_GATT_OK; + + if (GATT_GetConnIdIfConnected(p_rcb->gatt_if, p_msg->api_open.remote_bda, + &conn_id, p_msg->api_open.transport)) { + status = BTA_GATT_ALREADY_OPEN; + } + } + } else { + APPL_TRACE_ERROR("Inavlide server_if=%d", p_msg->api_open.server_if); + } + + if (p_rcb && p_rcb->p_cback) { + open.status = status; + open.server_if = p_msg->api_open.server_if; + (*p_rcb->p_cback)(BTA_GATTS_OPEN_EVT, (tBTA_GATTS *)&open); + } + +} +/******************************************************************************* +** +** Function bta_gatts_cancel_open +** +** Description +** +** Returns none. +** +*******************************************************************************/ +void bta_gatts_cancel_open (tBTA_GATTS_CB *p_cb, tBTA_GATTS_DATA *p_msg) +{ + tBTA_GATTS_RCB *p_rcb; + tBTA_GATT_STATUS status = BTA_GATT_ERROR; + tBTA_GATTS_CANCEL_OPEN cancel_open; + UNUSED(p_cb); + + if ((p_rcb = bta_gatts_find_app_rcb_by_app_if(p_msg->api_cancel_open.server_if)) != NULL) { + if (!GATT_CancelConnect(p_rcb->gatt_if, p_msg->api_cancel_open.remote_bda, + p_msg->api_cancel_open.is_direct)) { + APPL_TRACE_ERROR("bta_gatts_cancel_open failed for open request"); + } else { + status = BTA_GATT_OK; + } + } else { + APPL_TRACE_ERROR("Inavlide server_if=%d", p_msg->api_cancel_open.server_if); + } + + if (p_rcb && p_rcb->p_cback) { + cancel_open.status = status; + cancel_open.server_if = p_msg->api_cancel_open.server_if; + (*p_rcb->p_cback)(BTA_GATTS_CANCEL_OPEN_EVT, (tBTA_GATTS *)&cancel_open); + + } +} +/******************************************************************************* +** +** Function bta_gatts_close +** +** Description +** +** Returns none. +** +*******************************************************************************/ +void bta_gatts_close (tBTA_GATTS_CB *p_cb, tBTA_GATTS_DATA *p_msg) +{ + tBTA_GATTS_RCB *p_rcb; + tBTA_GATT_STATUS status = BTA_GATT_ERROR; + tGATT_IF gatt_if; + BD_ADDR remote_bda; + tBTA_GATT_TRANSPORT transport; + tBTA_GATTS_CLOSE close; + UNUSED(p_cb); + + if (GATT_GetConnectionInfor(p_msg->hdr.layer_specific, &gatt_if, remote_bda, &transport)) { + if (GATT_Disconnect(p_msg->hdr.layer_specific) != GATT_SUCCESS) { + APPL_TRACE_ERROR("bta_gatts_close fail conn_id=%d", p_msg->hdr.layer_specific); + } else { + status = BTA_GATT_OK; + } + + p_rcb = bta_gatts_find_app_rcb_by_app_if(gatt_if); + + if (p_rcb && p_rcb->p_cback) { + if (transport == BTA_TRANSPORT_BR_EDR) { + bta_sys_conn_close( BTA_ID_GATTS , BTA_ALL_APP_ID, remote_bda); + } + + close.status = status; + close.conn_id = p_msg->hdr.layer_specific; + (*p_rcb->p_cback)(BTA_GATTS_CLOSE_EVT, (tBTA_GATTS *)&close); + } + } else { + APPL_TRACE_ERROR("Unknown connection ID: %d", p_msg->hdr.layer_specific); + } + +} + +/******************************************************************************* +** +** Function bta_gatts_send_service_change_indication +** +** Description gatts send service change indication +** +** Returns none. +** +*******************************************************************************/ +void bta_gatts_send_service_change_indication (tBTA_GATTS_DATA *p_msg) +{ + tBTA_GATTS_RCB *p_rcb = bta_gatts_find_app_rcb_by_app_if(p_msg->api_send_service_change.server_if); + tBTA_GATTS_SERVICE_CHANGE service_change; + tBTA_GATT_STATUS status = BTA_GATT_OK; + UINT16 addr[BD_ADDR_LEN] = {0}; + if(memcmp(p_msg->api_send_service_change.remote_bda, addr, BD_ADDR_LEN) != 0) { + BD_ADDR bd_addr; + memcpy(bd_addr, p_msg->api_send_service_change.remote_bda, BD_ADDR_LEN); + status = GATT_SendServiceChangeIndication(bd_addr); + } else { + status = GATT_SendServiceChangeIndication(NULL); + } + if (p_rcb && p_rcb->p_cback) { + service_change.status = status; + service_change.server_if = p_msg->api_send_service_change.server_if; + (*p_rcb->p_cback)(BTA_GATTS_SEND_SERVICE_CHANGE_EVT, (tBTA_GATTS *)&service_change); + } +} + +/******************************************************************************* +** +** Function bta_gatts_listen +** +** Description Start or stop listening for LE connection on a GATT server +** +** Returns none. +** +*******************************************************************************/ +void bta_gatts_listen(tBTA_GATTS_CB *p_cb, tBTA_GATTS_DATA *p_msg) +{ + tBTA_GATTS_RCB *p_rcb = bta_gatts_find_app_rcb_by_app_if(p_msg->api_listen.server_if); + tBTA_GATTS cb_data; + UNUSED(p_cb); + + cb_data.reg_oper.status = BTA_GATT_OK; + cb_data.reg_oper.server_if = p_msg->api_listen.server_if; + + if (p_rcb == NULL) { + APPL_TRACE_ERROR("Unknown GATTS application"); + return; + } + + if (!GATT_Listen(p_msg->api_listen.server_if, + p_msg->api_listen.start, + p_msg->api_listen.remote_bda)) { + cb_data.status = BTA_GATT_ERROR; + APPL_TRACE_ERROR("bta_gatts_listen Listen failed"); + } + + if (p_rcb->p_cback) { + (*p_rcb->p_cback)(BTA_GATTS_LISTEN_EVT, &cb_data); + } +} + +/******************************************************************************* +** +** Function bta_gatts_show_local_database +** +** Description print loacl service database +** +** Returns none. +** +*******************************************************************************/ +void bta_gatts_show_local_database (void) +{ + if (GATTS_ShowLocalDatabase()) { + APPL_TRACE_ERROR("%s failed", __func__); + } +} + +/******************************************************************************* +** +** Function bta_gatts_request_cback +** +** Description GATTS attribute request callback. +** +** Returns none. +** +*******************************************************************************/ +static void bta_gatts_send_request_cback (UINT16 conn_id, + UINT32 trans_id, + tGATTS_REQ_TYPE req_type, tGATTS_DATA *p_data) +{ + tBTA_GATTS cb_data; + tBTA_GATTS_RCB *p_rcb; + tGATT_IF gatt_if; + tBTA_GATT_TRANSPORT transport; + + memset(&cb_data, 0 , sizeof(tBTA_GATTS)); + + if (GATT_GetConnectionInfor(conn_id, &gatt_if, cb_data.req_data.remote_bda, &transport)) { + p_rcb = bta_gatts_find_app_rcb_by_app_if(gatt_if); + + APPL_TRACE_DEBUG ("bta_gatts_send_request_cback conn_id=%d trans_id=%d req_type=%d", + conn_id, trans_id, req_type); + + if (p_rcb && p_rcb->p_cback) { + /* if over BR_EDR, inform PM for mode change */ + if (transport == BTA_TRANSPORT_BR_EDR) { + bta_sys_busy(BTA_ID_GATTS, BTA_ALL_APP_ID, cb_data.req_data.remote_bda); + bta_sys_idle(BTA_ID_GATTS, BTA_ALL_APP_ID, cb_data.req_data.remote_bda); + } + + cb_data.req_data.conn_id = conn_id; + cb_data.req_data.trans_id = trans_id; + cb_data.req_data.p_data = (tBTA_GATTS_REQ_DATA *)p_data; + + if(req_type == BTA_GATTS_CONF_EVT) { + cb_data.req_data.handle = p_data->handle; + } + (*p_rcb->p_cback)(req_type, &cb_data); + } else { + APPL_TRACE_ERROR("connection request on gatt_if[%d] is not interested", gatt_if); + } + } else { + APPL_TRACE_ERROR("request received on unknown connection ID: %d", conn_id); + } +} + +/******************************************************************************* +** +** Function bta_gatts_conn_cback +** +** Description connection callback. +** +** Returns none. +** +*******************************************************************************/ +static void bta_gatts_conn_cback (tGATT_IF gatt_if, BD_ADDR bda, UINT16 conn_id, + BOOLEAN connected, tGATT_DISCONN_REASON reason, + tGATT_TRANSPORT transport) +{ + tBTA_GATTS cb_data = {0}; + UINT8 evt = connected ? BTA_GATTS_CONNECT_EVT : BTA_GATTS_DISCONNECT_EVT; + tBTA_GATTS_RCB *p_reg; + + APPL_TRACE_DEBUG ("bta_gatts_conn_cback gatt_if=%d conn_id=%d connected=%d reason = 0x%04d", + gatt_if, conn_id, connected, reason); + APPL_TRACE_DEBUG("bta_gatts_conn_cback bda :%02x-%02x-%02x-%02x-%02x-%02x ", + bda[0], bda[1], bda[2], bda[3], bda[4], bda[5]); + + bt_bdaddr_t bdaddr; + bdcpy(bdaddr.address, bda); + /* + if (connected) + btif_debug_conn_state(bdaddr, BTIF_DEBUG_CONNECTED, GATT_CONN_UNKNOWN); + else + btif_debug_conn_state(bdaddr, BTIF_DEBUG_DISCONNECTED, reason); + */ + p_reg = bta_gatts_find_app_rcb_by_app_if(gatt_if); + + if (p_reg && p_reg->p_cback) { + /* there is no RM for GATT */ + if (transport == BTA_TRANSPORT_BR_EDR) { + if (connected) { + bta_sys_conn_open(BTA_ID_GATTS, BTA_ALL_APP_ID, bda); + } else { + bta_sys_conn_close( BTA_ID_GATTS , BTA_ALL_APP_ID, bda); + } + } + if(evt == BTA_GATTS_CONNECT_EVT) { + tL2C_LCB *p_lcb = l2cu_find_lcb_by_bd_addr(bda, BT_TRANSPORT_LE); + if(p_lcb != NULL) { + cb_data.conn.conn_params.interval = p_lcb->current_used_conn_interval; + cb_data.conn.conn_params.latency = p_lcb->current_used_conn_latency; + cb_data.conn.conn_params.timeout = p_lcb->current_used_conn_timeout; + cb_data.conn.link_role = p_lcb->link_role; + #if (BLE_INCLUDED == TRUE) + cb_data.conn.ble_addr_type = p_lcb->ble_addr_type; + #endif + cb_data.conn.conn_handle = p_lcb->handle; + }else { + APPL_TRACE_WARNING("%s not found connection parameters of the device ", __func__); + } + } + cb_data.conn.conn_id = conn_id; + cb_data.conn.server_if = gatt_if; + cb_data.conn.reason = reason; + cb_data.conn.transport = transport; + memcpy(cb_data.conn.remote_bda, bda, BD_ADDR_LEN); + (*p_reg->p_cback)(evt, &cb_data); + } else { + APPL_TRACE_ERROR("bta_gatts_conn_cback server_if=%d not found", gatt_if); + } +} + +/******************************************************************************* +** +** Function bta_gatts_cong_cback +** +** Description congestion callback. +** +** Returns none. +** +*******************************************************************************/ +static void bta_gatts_cong_cback (UINT16 conn_id, BOOLEAN congested) +{ + tBTA_GATTS cb_data; + cb_data.congest.conn_id = conn_id; + cb_data.congest.congested = congested; + btc_congest_callback(&cb_data); +} +#endif /* GATTS_INCLUDED */ diff --git a/lib/bt/host/bluedroid/bta/gatt/bta_gatts_api.c b/lib/bt/host/bluedroid/bta/gatt/bta_gatts_api.c new file mode 100644 index 00000000..ffe3abec --- /dev/null +++ b/lib/bt/host/bluedroid/bta/gatt/bta_gatts_api.c @@ -0,0 +1,680 @@ +/****************************************************************************** + * + * Copyright (C) 2010-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This is the implementation of the API for GATT server of BTA. + * + ******************************************************************************/ + +#include "common/bt_target.h" + +#if defined(GATTS_INCLUDED) && (GATTS_INCLUDED == TRUE) + +#include +#include "bta/bta_sys.h" +#include "bta/bta_gatt_api.h" +#include "bta_gatts_int.h" +#include "osi/allocator.h" +#include "stack/l2c_api.h" + +/***************************************************************************** +** Constants +*****************************************************************************/ + +static const tBTA_SYS_REG bta_gatts_reg = { + bta_gatts_hdl_event, + BTA_GATTS_Disable +}; + +/******************************************************************************* +** +** Function BTA_GATTS_Disable +** +** Description This function is called to disable GATTS module +** +** Parameters None. +** +** Returns None +** +*******************************************************************************/ +void BTA_GATTS_Disable(void) +{ + BT_HDR *p_buf; + + if (bta_sys_is_register(BTA_ID_GATTS) == FALSE) { + APPL_TRACE_WARNING("GATTS Module not enabled/already disabled"); + return; + } + + if ((p_buf = (BT_HDR *) osi_malloc(sizeof(BT_HDR))) != NULL) { + p_buf->event = BTA_GATTS_API_DISABLE_EVT; + bta_sys_sendmsg(p_buf); + } + bta_sys_deregister(BTA_ID_GATTS); + +} + +/******************************************************************************* +** +** Function BTA_GATTS_AppRegister +** +** Description This function is called to register application callbacks +** with BTA GATTS module. +** +** Parameters p_app_uuid - application UUID +** p_cback - pointer to the application callback function. +** +** Returns None +** +*******************************************************************************/ +void BTA_GATTS_AppRegister(const tBT_UUID * p_app_uuid, tBTA_GATTS_CBACK *p_cback) +{ + tBTA_GATTS_API_REG *p_buf; + + /* register with BTA system manager */ + if (bta_sys_is_register(BTA_ID_GATTS) == FALSE) { + bta_sys_register(BTA_ID_GATTS, &bta_gatts_reg); + } + + if ((p_buf = (tBTA_GATTS_API_REG *) osi_malloc(sizeof(tBTA_GATTS_API_REG))) != NULL) { + p_buf->hdr.event = BTA_GATTS_API_REG_EVT; + + if (p_app_uuid != NULL) { + memcpy(&p_buf->app_uuid, p_app_uuid, sizeof(tBT_UUID)); + } + p_buf->p_cback = p_cback; + + bta_sys_sendmsg(p_buf); + } + return; +} + + + +/******************************************************************************* +** +** Function BTA_GATTS_AppDeregister +** +** Description De-register with GATT Server. +** +** Parameters app_id: applicatino ID. +** +** Returns void +** +*******************************************************************************/ +void BTA_GATTS_AppDeregister(tBTA_GATTS_IF server_if) +{ + tBTA_GATTS_API_DEREG *p_buf; + + if ((p_buf = (tBTA_GATTS_API_DEREG *) osi_malloc(sizeof(tBTA_GATTS_API_DEREG))) != NULL) { + p_buf->hdr.event = BTA_GATTS_API_DEREG_EVT; + p_buf->server_if = server_if; + + bta_sys_sendmsg(p_buf); + } + return; +} + +/******************************************************************************* +** +** Function BTA_GATTS_CreateService +** +** Description Create a service. When service creation is done, a callback +** event BTA_GATTS_CREATE_EVT is called to report status +** and service ID to the profile. The service ID obtained in +** the callback function needs to be used when adding included +** service and characteristics/descriptors into the service. +** +** Parameters app_id: Profile ID this service is belonged to. +** p_service_uuid: service UUID. +** inst: instance ID number of this service. +** num_handle: numble of handle requessted for this service. +** is_primary: is this service a primary one or not. +** +** Returns void +** +*******************************************************************************/ +void BTA_GATTS_CreateService(tBTA_GATTS_IF server_if, const tBT_UUID * p_service_uuid, UINT8 inst, + UINT16 num_handle, BOOLEAN is_primary) +{ + tBTA_GATTS_API_CREATE_SRVC *p_buf; + + if ((p_buf = (tBTA_GATTS_API_CREATE_SRVC *) osi_malloc(sizeof(tBTA_GATTS_API_CREATE_SRVC))) != NULL) { + p_buf->hdr.event = BTA_GATTS_API_CREATE_SRVC_EVT; + + p_buf->server_if = server_if; + p_buf->inst = inst; + memcpy(&p_buf->service_uuid, p_service_uuid, sizeof(tBT_UUID)); + p_buf->num_handle = num_handle; + p_buf->is_pri = is_primary; + + bta_sys_sendmsg(p_buf); + } + return; +} +/******************************************************************************* +** +** Function BTA_GATTS_AddIncludeService +** +** Description This function is called to add an included service. After included +** service is included, a callback event BTA_GATTS_ADD_INCL_SRVC_EVT +** is reported the included service ID. +** +** Parameters service_id: service ID to which this included service is to +** be added. +** included_service_id: the service ID to be included. +** +** Returns void +** +*******************************************************************************/ +void BTA_GATTS_AddIncludeService(UINT16 service_id, UINT16 included_service_id) +{ + tBTA_GATTS_API_ADD_INCL_SRVC *p_buf; + + if ((p_buf = + (tBTA_GATTS_API_ADD_INCL_SRVC *) osi_malloc(sizeof(tBTA_GATTS_API_ADD_INCL_SRVC))) + != NULL) { + p_buf->hdr.event = BTA_GATTS_API_ADD_INCL_SRVC_EVT; + + p_buf->hdr.layer_specific = service_id; + p_buf->included_service_id = included_service_id; + + bta_sys_sendmsg(p_buf); + } + return; + +} +/******************************************************************************* +** +** Function BTA_GATTS_AddCharacteristic +** +** Description This function is called to add a characteristic into a service. +** +** Parameters service_id: service ID to which this included service is to +** be added. +** p_char_uuid : Characteristic UUID. +** perm : Characteristic value declaration attribute permission. +** property : Characteristic Properties +** +** Returns None +** +*******************************************************************************/ +void BTA_GATTS_AddCharacteristic (UINT16 service_id, const tBT_UUID * p_char_uuid, + tBTA_GATT_PERM perm, tBTA_GATT_CHAR_PROP property, tGATT_ATTR_VAL *attr_val, + tBTA_GATTS_ATTR_CONTROL *control) +{ + tBTA_GATTS_API_ADD_CHAR *p_buf; + UINT16 len = 0; + if(attr_val != NULL){ + len = attr_val->attr_len; + } + if ((p_buf = (tBTA_GATTS_API_ADD_CHAR *) osi_malloc(sizeof(tBTA_GATTS_API_ADD_CHAR))) != NULL) { + memset(p_buf, 0, sizeof(tBTA_GATTS_API_ADD_CHAR)); + + p_buf->hdr.event = BTA_GATTS_API_ADD_CHAR_EVT; + p_buf->hdr.layer_specific = service_id; + p_buf->perm = perm; + p_buf->property = property; + if(control !=NULL){ + p_buf->control.auto_rsp = control->auto_rsp; + } + if(attr_val != NULL){ + APPL_TRACE_DEBUG("!!!!!!attr_val->attr_len = %x\n",attr_val->attr_len); + APPL_TRACE_DEBUG("!!!!!!!attr_val->attr_max_len = %x\n",attr_val->attr_max_len); + p_buf->attr_val.attr_len = attr_val->attr_len; + p_buf->attr_val.attr_max_len = attr_val->attr_max_len; + p_buf->attr_val.attr_val = (uint8_t *)osi_malloc(len); + if(p_buf->attr_val.attr_val != NULL){ + memcpy(p_buf->attr_val.attr_val, attr_val->attr_val, attr_val->attr_len); + } + } + + if (p_char_uuid) { + memcpy(&p_buf->char_uuid, p_char_uuid, sizeof(tBT_UUID)); + } + bta_sys_sendmsg(p_buf); + } + return; +} + +/******************************************************************************* +** +** Function BTA_GATTS_AddCharDescriptor +** +** Description This function is called to add characteristic descriptor. When +** it's done, a callback event BTA_GATTS_ADD_DESCR_EVT is called +** to report the status and an ID number for this descriptor. +** +** Parameters service_id: service ID to which this charatceristic descriptor is to +** be added. +** perm: descriptor access permission. +** p_descr_uuid: descriptor UUID. +** +** Returns returns status. +** +*******************************************************************************/ +void BTA_GATTS_AddCharDescriptor (UINT16 service_id, + tBTA_GATT_PERM perm, + const tBT_UUID * p_descr_uuid, tBTA_GATT_ATTR_VAL *attr_val, + tBTA_GATTS_ATTR_CONTROL *control) +{ + tBTA_GATTS_API_ADD_DESCR *p_buf; + UINT16 value_len = 0; + + if ((p_buf = (tBTA_GATTS_API_ADD_DESCR *) osi_malloc(sizeof(tBTA_GATTS_API_ADD_DESCR))) != NULL) { + memset(p_buf, 0, sizeof(tBTA_GATTS_API_ADD_DESCR)); + + p_buf->hdr.event = BTA_GATTS_API_ADD_DESCR_EVT; + p_buf->hdr.layer_specific = service_id; + p_buf->perm = perm; + + if(control != NULL){ + p_buf->control.auto_rsp = control->auto_rsp; + } + + if (p_descr_uuid) { + memcpy(&p_buf->descr_uuid, p_descr_uuid, sizeof(tBT_UUID)); + } + + if(attr_val != NULL){ + p_buf->attr_val.attr_len = attr_val->attr_len; + p_buf->attr_val.attr_max_len = attr_val->attr_max_len; + value_len = attr_val->attr_len; + if (value_len != 0){ + p_buf->attr_val.attr_val = (uint8_t*)osi_malloc(value_len); + if(p_buf->attr_val.attr_val != NULL){ + memcpy(p_buf->attr_val.attr_val, attr_val->attr_val, value_len); + } + else{ + APPL_TRACE_ERROR("Allocate fail for %s\n", __func__); + + } + } + } + + bta_sys_sendmsg(p_buf); + } + return; + +} + +/******************************************************************************* + ** + ** Function BTA_GATTS_DeleteService + ** + ** Description This function is called to delete a service. When this is done, + ** a callback event BTA_GATTS_DELETE_EVT is report with the status. + ** + ** Parameters service_id: service_id to be deleted. + ** +** Returns returns none. +** +*******************************************************************************/ +void BTA_GATTS_DeleteService(UINT16 service_id) +{ + BT_HDR *p_buf; + + if ((p_buf = (BT_HDR *) osi_malloc(sizeof(BT_HDR))) != NULL) { + p_buf->event = BTA_GATTS_API_DEL_SRVC_EVT; + + p_buf->layer_specific = service_id; + + bta_sys_sendmsg(p_buf); + } + return; + +} + +/******************************************************************************* +** +** Function BTA_GATTS_StartService +** +** Description This function is called to start a service. +** +** Parameters service_id: the service ID to be started. +** sup_transport: supported transport. +** +** Returns None. +** +*******************************************************************************/ +void BTA_GATTS_StartService(UINT16 service_id, tBTA_GATT_TRANSPORT sup_transport) +{ + tBTA_GATTS_API_START *p_buf; + + if ((p_buf = (tBTA_GATTS_API_START *) osi_malloc(sizeof(tBTA_GATTS_API_START))) != NULL) { + p_buf->hdr.event = BTA_GATTS_API_START_SRVC_EVT; + + p_buf->hdr.layer_specific = service_id; + p_buf->transport = sup_transport; + + bta_sys_sendmsg(p_buf); + } + return; +} + +/******************************************************************************* +** +** Function BTA_GATTS_StopService +** +** Description This function is called to stop a service. +** +** Parameters service_id - service to be topped. +** +** Returns None +** +*******************************************************************************/ +void BTA_GATTS_StopService(UINT16 service_id) +{ + BT_HDR *p_buf; + + if ((p_buf = (BT_HDR *) osi_malloc(sizeof(BT_HDR))) != NULL) { + p_buf->event = BTA_GATTS_API_STOP_SRVC_EVT; + + p_buf->layer_specific = service_id; + + bta_sys_sendmsg(p_buf); + } + return; +} + +/******************************************************************************* +** +** Function BTA_GATTS_HandleValueIndication +** +** Description This function is called to read a characteristics descriptor. +** +** Parameters bda - remote device bd address to indicate. +** attr_id - attribute ID to indicate. +** data_len - indicate data length. +** p_data: data to indicate. +** need_confirm - if this indication expects a confirmation or not. +** +** Returns None +** +*******************************************************************************/ +void BTA_GATTS_HandleValueIndication (UINT16 conn_id, UINT16 attr_id, UINT16 data_len, + UINT8 *p_data, BOOLEAN need_confirm) +{ + tBTA_GATTS_API_INDICATION *p_buf; + UINT16 len = sizeof(tBTA_GATTS_API_INDICATION); + + if ((p_buf = (tBTA_GATTS_API_INDICATION *) osi_malloc(len)) != NULL) { + memset(p_buf, 0, len); + + p_buf->hdr.event = BTA_GATTS_API_INDICATION_EVT; + p_buf->hdr.layer_specific = conn_id; + p_buf->attr_id = attr_id; + p_buf->need_confirm = need_confirm; + + if (data_len > 0 && p_data != NULL) { + p_buf->len = data_len; + memcpy(p_buf->value, p_data, data_len); + + } + if(need_confirm == false){ + l2ble_update_att_acl_pkt_num(L2CA_DECREASE_BTC_NUM, NULL); + l2ble_update_att_acl_pkt_num(L2CA_ADD_BTU_NUM, NULL); + } + bta_sys_sendmsg(p_buf); + } + return; + +} +/******************************************************************************* +** +** Function BTA_GATTS_SendRsp +** +** Description This function is called to send a response to a request. +** +** Parameters conn_id - connection identifier. +** trans_id - transaction ID. +** status - response status +** p_msg - response data. +** +** Returns None +** +*******************************************************************************/ +void BTA_GATTS_SendRsp (UINT16 conn_id, UINT32 trans_id, + tBTA_GATT_STATUS status, tBTA_GATTS_RSP *p_msg) +{ + tBTA_GATTS_API_RSP *p_buf; + UINT16 len = sizeof(tBTA_GATTS_API_RSP) + sizeof(tBTA_GATTS_RSP); + + if ((p_buf = (tBTA_GATTS_API_RSP *) osi_malloc(len)) != NULL) { + memset(p_buf, 0, len); + + p_buf->hdr.event = BTA_GATTS_API_RSP_EVT; + p_buf->hdr.layer_specific = conn_id; + p_buf->trans_id = trans_id; + p_buf->status = status; + + if (p_msg != NULL) { + p_buf->p_rsp = (tBTA_GATTS_RSP *)(p_buf + 1); + memcpy(p_buf->p_rsp, p_msg, sizeof(tBTA_GATTS_RSP)); + } + + bta_sys_sendmsg(p_buf); + } + return; + +} + + +void BTA_SetAttributeValue(UINT16 attr_handle, UINT16 length, UINT8 *value) +{ + tBTA_GATTS_API_SET_ATTR_VAL *p_buf; + UINT16 len = sizeof(tBTA_GATTS_API_SET_ATTR_VAL); + if((p_buf = (tBTA_GATTS_API_SET_ATTR_VAL *)osi_malloc( + sizeof(tBTA_GATTS_API_SET_ATTR_VAL))) != NULL){ + memset(p_buf, 0, len); + p_buf->hdr.event = BTA_GATTS_API_SET_ATTR_VAL_EVT; + p_buf->hdr.layer_specific = attr_handle; + p_buf->length = length; + if(value != NULL){ + if((p_buf->value = (UINT8 *)osi_malloc(length)) != NULL){ + memcpy(p_buf->value, value, length); + } + } + + bta_sys_sendmsg(p_buf); + } + +} + +tBTA_GATT_STATUS BTA_GetAttributeValue(UINT16 attr_handle, UINT16 *length, UINT8 **value) +{ + return bta_gatts_get_attr_value(attr_handle, length, value); +} + +/******************************************************************************* +** +** Function BTA_GATTS_Open +** +** Description Open a direct open connection or add a background auto connection +** bd address +** +** Parameters server_if: server interface. +** remote_bda: remote device BD address. +** is_direct: direct connection or background auto connection +** transport : Transport on which GATT connection to be opened (BR/EDR or LE) +** +** Returns void +** +*******************************************************************************/ +void BTA_GATTS_Open(tBTA_GATTS_IF server_if, BD_ADDR remote_bda, BOOLEAN is_direct, + tBTA_GATT_TRANSPORT transport) +{ + tBTA_GATTS_API_OPEN *p_buf; + + if ((p_buf = (tBTA_GATTS_API_OPEN *) osi_malloc(sizeof(tBTA_GATTS_API_OPEN))) != NULL) { + p_buf->hdr.event = BTA_GATTS_API_OPEN_EVT; + p_buf->server_if = server_if; + p_buf->is_direct = is_direct; + p_buf->transport = transport; + memcpy(p_buf->remote_bda, remote_bda, BD_ADDR_LEN); + + bta_sys_sendmsg(p_buf); + } + return; +} + + +/******************************************************************************* +** +** Function BTA_GATTS_CancelOpen +** +** Description Cancel a direct open connection or remove a background auto connection +** bd address +** +** Parameters server_if: server interface. +** remote_bda: remote device BD address. +** is_direct: direct connection or background auto connection +** +** Returns void +** +*******************************************************************************/ +void BTA_GATTS_CancelOpen(tBTA_GATTS_IF server_if, BD_ADDR remote_bda, BOOLEAN is_direct) +{ + tBTA_GATTS_API_CANCEL_OPEN *p_buf; + + if ((p_buf = (tBTA_GATTS_API_CANCEL_OPEN *) osi_malloc(sizeof(tBTA_GATTS_API_CANCEL_OPEN))) != NULL) { + p_buf->hdr.event = BTA_GATTS_API_CANCEL_OPEN_EVT; + p_buf->server_if = server_if; + p_buf->is_direct = is_direct; + memcpy(p_buf->remote_bda, remote_bda, BD_ADDR_LEN); + bta_sys_sendmsg(p_buf); + } + return; +} + +/******************************************************************************* +** +** Function BTA_GATTS_Close +** +** Description Close a connection a remote device. +** +** Parameters conn_id: connection ID to be closed. +** +** Returns void +** +*******************************************************************************/ +void BTA_GATTS_Close(UINT16 conn_id) +{ + BT_HDR *p_buf; + + if ((p_buf = (BT_HDR *) osi_malloc(sizeof(BT_HDR))) != NULL) { + p_buf->event = BTA_GATTS_API_CLOSE_EVT; + p_buf->layer_specific = conn_id; + bta_sys_sendmsg(p_buf); + } + return; + +} + +void BTA_GATTS_SendServiceChangeIndication(tBTA_GATTS_IF server_if, BD_ADDR remote_bda) +{ + tBTA_GATTS_API_SEND_SERVICE_CHANGE *p_buf; + + if ((p_buf = (tBTA_GATTS_API_SEND_SERVICE_CHANGE *) osi_malloc(sizeof(tBTA_GATTS_API_SEND_SERVICE_CHANGE))) != NULL) { + memset(p_buf, 0, sizeof(tBTA_GATTS_API_SEND_SERVICE_CHANGE)); + p_buf->hdr.event = BTA_GATTS_API_SEND_SERVICE_CHANGE_EVT; + p_buf->server_if = server_if; + memcpy(p_buf->remote_bda, remote_bda, BD_ADDR_LEN); + + bta_sys_sendmsg(p_buf); + } + return; + +} + +/******************************************************************************* +** +** Function BTA_GATTS_Listen +** +** Description Start advertisement to listen for connection request for a +** GATT server +** +** Parameters server_if: server interface. +** start: to start or stop listening for connection +** remote_bda: remote device BD address, if listen to all device +** use NULL. +** +** Returns void +** +*******************************************************************************/ +void BTA_GATTS_Listen(tBTA_GATTS_IF server_if, BOOLEAN start, BD_ADDR_PTR target_bda) +{ + tBTA_GATTS_API_LISTEN *p_buf; + + if ((p_buf = (tBTA_GATTS_API_LISTEN *) osi_malloc((UINT16)(sizeof(tBTA_GATTS_API_LISTEN) + BD_ADDR_LEN))) != NULL) { + p_buf->hdr.event = BTA_GATTS_API_LISTEN_EVT; + + p_buf->server_if = server_if; + p_buf->start = start; + + if (target_bda) { + p_buf->remote_bda = (UINT8 *)(p_buf + 1); + memcpy(p_buf->remote_bda, target_bda, BD_ADDR_LEN); + } else { + p_buf->remote_bda = NULL; + } + + bta_sys_sendmsg(p_buf); + } + return; +} + +uint8_t BTA_GATTS_SetServiceChangeMode(uint8_t mode) +{ + tGATT_STATUS status; + APPL_TRACE_DEBUG("%s mode %u", __func__, mode); + + status = GATTS_SetServiceChangeMode(mode); + if (status != GATT_SUCCESS) { + APPL_TRACE_ERROR("%s status %x", __func__, status); + return -1; + } + + return 0; +} + +uint8_t BTA_GATTS_SendMultiNotification(uint8_t gatt_if, uint16_t conn_id, void *tuples, uint16_t num_tuples) +{ + tGATT_STATUS status; + conn_id = (UINT16)((((UINT8)conn_id) << 8) | gatt_if); + + status = GATTS_HandleMultiValueNotification(conn_id, (tGATT_HLV *)tuples, num_tuples); + if (status != GATT_SUCCESS) { + APPL_TRACE_ERROR("%s status %x", __func__, status); + return -1; + } + + return 0; +} + +void BTA_GATTS_ShowLocalDatabase(void) +{ + BT_HDR *p_buf; + + if ((p_buf = (BT_HDR *) osi_malloc(sizeof(BT_HDR))) != NULL) { + p_buf->event = BTA_GATTS_API_SHOW_LOCAL_DATABASE_EVT; + bta_sys_sendmsg(p_buf); + } +} +#endif /* BTA_GATT_INCLUDED */ diff --git a/lib/bt/host/bluedroid/bta/gatt/bta_gatts_co.c b/lib/bt/host/bluedroid/bta/gatt/bta_gatts_co.c new file mode 100644 index 00000000..a5a73b52 --- /dev/null +++ b/lib/bt/host/bluedroid/bta/gatt/bta_gatts_co.c @@ -0,0 +1,251 @@ +/****************************************************************************** + * + * Copyright (C) 2009-2013 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +#include "bta/bta_api.h" + +#if( defined BLE_INCLUDED ) && (BLE_INCLUDED == TRUE) +#if( defined GATTS_INCLUDED ) && (GATTS_INCLUDED == TRUE) + +#include +#include +#include "bta/bta_gatts_co.h" +#include "btc/btc_storage.h" +#include "btc/btc_ble_storage.h" +// #include "btif_util.h" + +#if (defined(BTIF_INCLUDED) && BTIF_INCLUDED == TRUE) +/***************************************************************************** +** Local type definitions +*****************************************************************************/ + +#define BTIF_GATTS_MAX_SRV_CHG_CLT_SIZE 50 + +typedef struct { + BOOLEAN enable; + UINT8 num_clients; + tBTA_GATTS_SRV_CHG srv_chg[BTIF_GATTS_MAX_SRV_CHG_CLT_SIZE]; +} __attribute__((packed)) btif_gatts_srv_chg_cb_t; + +/***************************************************************************** +** Static variables +*****************************************************************************/ + +static btif_gatts_srv_chg_cb_t btif_gatts_srv_chg_cb; + +/***************************************************************************** +** Static functions +*****************************************************************************/ + +static void btif_gatts_check_init(void) +{ + btif_gatts_srv_chg_cb_t *p_cb = &btif_gatts_srv_chg_cb; + + if (!p_cb->enable) { + memset(p_cb, 0, sizeof(btif_gatts_srv_chg_cb_t)); + p_cb->enable = TRUE; + } +} + +/***************************************************************************** +** Externally called functions +*****************************************************************************/ + +void btif_gatts_add_bonded_dev_from_nv(BD_ADDR bda) +{ + btif_gatts_srv_chg_cb_t *p_cb = &btif_gatts_srv_chg_cb; + BOOLEAN found = FALSE; + UINT8 i; + + btif_gatts_check_init(); + + for (i = 0; i != p_cb->num_clients; ++i) { + if (!memcmp(p_cb->srv_chg[i].bda, bda, sizeof(BD_ADDR))) { + found = TRUE; + break; + } + } + + if (!found) { + if (p_cb->num_clients < BTIF_GATTS_MAX_SRV_CHG_CLT_SIZE) { + bdcpy(p_cb->srv_chg[p_cb->num_clients].bda, bda); + p_cb->srv_chg[p_cb->num_clients].srv_changed = FALSE; + p_cb->num_clients++; + } + } +} + +#endif /* #if (defined(BTIF_INCLUDED) && BTIF_INCLUDED == TRUE) */ +/***************************************************************************** +** Call-out functions +*****************************************************************************/ + +/******************************************************************************* +** +** Function bta_gatts_co_update_handle_range +** +** Description This callout function is executed by GATTS when a GATT server +** handle range ios to be added or removed. +** +** Parameter is_add: true is to add a handle range; otherwise is to delete. +** p_hndl_range: handle range. +** +** Returns void. +** +*******************************************************************************/ +void bta_gatts_co_update_handle_range(BOOLEAN is_add, tBTA_GATTS_HNDL_RANGE *p_hndl_range) +{ + UNUSED(is_add); + UNUSED(p_hndl_range); +} + +/******************************************************************************* +** +** Function bta_gatts_co_srv_chg +** +** Description This call-out is to read/write/remove service change related +** informaiton. The request consists of the cmd and p_req and the +** response is returned in p_rsp +** +** Parameter cmd - request command +** p_req - request paramters +** p_rsp - response data for the request +** +** Returns TRUE - if the request is processed successfully and +** the response is returned in p_rsp. +** FALSE - if the request can not be processed +** +*******************************************************************************/ +BOOLEAN bta_gatts_co_srv_chg(tBTA_GATTS_SRV_CHG_CMD cmd, + tBTA_GATTS_SRV_CHG_REQ *p_req, + tBTA_GATTS_SRV_CHG_RSP *p_rsp) +{ + UNUSED(cmd); + UNUSED(p_req); + UNUSED(p_rsp); + + return FALSE; +} + +/******************************************************************************* +** +** Function bta_gatts_co_load_handle_range +** +** Description This callout function is executed by GATTS when a GATT server +** handle range is requested to be loaded from NV. +** +** Parameter +** +** Returns void. +** +*******************************************************************************/ +BOOLEAN bta_gatts_co_load_handle_range(UINT8 index, + tBTA_GATTS_HNDL_RANGE *p_handle_range) +{ + UNUSED(index); + UNUSED(p_handle_range); + + return FALSE; +} + +#if (SMP_INCLUDED == TRUE) +/******************************************************************************* +** +** Function bta_gatts_co_cl_feat_save +** +** Description This callout function is executed by GATTS when GATT server +** client support feature is requested to write to NV. +** +** Parameter remote_addr - remote device address +** feature - pointer of client support feature +** +** Returns void. +** +*******************************************************************************/ +void bta_gatts_co_cl_feat_save(BD_ADDR remote_addr, UINT8 *feature) +{ + bt_bdaddr_t bd_addr; + + memcpy(bd_addr.address, remote_addr, BD_ADDR_LEN); + btc_storage_set_gatt_cl_supp_feat(&bd_addr, feature, 1); +} + +/******************************************************************************* +** +** Function bta_gatts_co_db_hash_save +** +** Description This callout function is executed by GATTS when GATT server +** client status is requested to write to NV. +** +** Parameter remote_addr - remote device address +** db_hash - pointer of GATT service datebase hash +** +** Returns void. +** +*******************************************************************************/ +void bta_gatts_co_db_hash_save(BD_ADDR remote_addr, BT_OCTET16 db_hash) +{ + bt_bdaddr_t bd_addr; + + memcpy(bd_addr.address, remote_addr, BD_ADDR_LEN); + btc_storage_set_gatt_db_hash(&bd_addr, db_hash, BT_OCTET16_LEN); +} + +/******************************************************************************* +** +** Function bta_gatts_co_cl_feat_load +** +** Description This callout function is executed by GATTS when GATT server +** client status is requested to load from NV. +** +** Parameter remote_addr - remote device address +** feature - pointer of GATT service datebase hash +** +** Returns void. +** +*******************************************************************************/ +void bta_gatts_co_cl_feat_load(BD_ADDR remote_addr, UINT8 *feature) +{ + bt_bdaddr_t bd_addr; + + memcpy(bd_addr.address, remote_addr, BD_ADDR_LEN); + btc_storage_get_gatt_cl_supp_feat(&bd_addr, feature, 1); +} + +/******************************************************************************* +** +** Function bta_gatts_co_db_hash_load +** +** Description This callout function is executed by GATTS when GATT server +** client status is requested to load from NV. +** +** Parameter remote_addr - remote device address +** db_hash - pointer of GATT service datebase hash +** +** Returns void. +** +*******************************************************************************/ +void bta_gatts_co_db_hash_load(BD_ADDR remote_addr, BT_OCTET16 db_hash) +{ + bt_bdaddr_t bd_addr; + + memcpy(bd_addr.address, remote_addr, BD_ADDR_LEN); + btc_storage_get_gatt_db_hash(&bd_addr, db_hash, BT_OCTET16_LEN); +} +#endif // #if (SMP_INCLUDED == TRUE) +#endif // #if (GATTS_INCLUDED == TRUE) +#endif // #if (BLE_INCLUDED == TRUE) diff --git a/lib/bt/host/bluedroid/bta/gatt/bta_gatts_main.c b/lib/bt/host/bluedroid/bta/gatt/bta_gatts_main.c new file mode 100644 index 00000000..fe83d151 --- /dev/null +++ b/lib/bt/host/bluedroid/bta/gatt/bta_gatts_main.c @@ -0,0 +1,168 @@ +/****************************************************************************** + * + * Copyright (C) 2003-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + *****************************************************************************/ + +/****************************************************************************** + * + * This file contains the GATT server main functions and state machine. + * + ******************************************************************************/ + +#include "common/bt_target.h" + +#if defined(GATTS_INCLUDED) && (GATTS_INCLUDED == TRUE) + +#include + +#include "bta_gatts_int.h" +#include "osi/allocator.h" + +/* type for service building action functions */ +typedef void (*tBTA_GATTS_SRVC_ACT)(tBTA_GATTS_SRVC_CB *p_rcb, tBTA_GATTS_DATA *p_data); + +/* service building action function list */ +const tBTA_GATTS_SRVC_ACT bta_gatts_srvc_build_act[] = { + bta_gatts_add_include_srvc, + bta_gatts_add_char, + bta_gatts_add_char_descr, + bta_gatts_delete_service, + bta_gatts_start_service, + bta_gatts_stop_service, +}; + +/* GATTS control block */ +#if BTA_DYNAMIC_MEMORY == FALSE +tBTA_GATTS_CB bta_gatts_cb; +#else +tBTA_GATTS_CB *bta_gatts_cb_ptr; +#endif + +/******************************************************************************* +** +** Function bta_gatts_hdl_event +** +** Description BTA GATT server main event handling function. +** +** +** Returns void +** +*******************************************************************************/ +BOOLEAN bta_gatts_hdl_event(BT_HDR *p_msg) +{ + tBTA_GATTS_CB *p_cb = &bta_gatts_cb; + tBTA_GATTS_SRVC_CB *p_srvc_cb = NULL; + + switch (p_msg->event) { + case BTA_GATTS_API_DISABLE_EVT: + bta_gatts_api_disable(p_cb); + break; + + case BTA_GATTS_API_REG_EVT: + bta_gatts_register(p_cb, (tBTA_GATTS_DATA *) p_msg); + break; + + case BTA_GATTS_INT_START_IF_EVT: + bta_gatts_start_if(p_cb, (tBTA_GATTS_DATA *) p_msg); + break; + + case BTA_GATTS_API_DEREG_EVT: + bta_gatts_deregister(p_cb, (tBTA_GATTS_DATA *) p_msg); + break; + + case BTA_GATTS_API_CREATE_SRVC_EVT: + bta_gatts_create_srvc(p_cb, (tBTA_GATTS_DATA *) p_msg); + break; + + case BTA_GATTS_API_INDICATION_EVT: + bta_gatts_indicate_handle(p_cb, (tBTA_GATTS_DATA *) p_msg); + break; + + case BTA_GATTS_API_OPEN_EVT: + bta_gatts_open(p_cb, (tBTA_GATTS_DATA *) p_msg); + break; + + case BTA_GATTS_API_CANCEL_OPEN_EVT: + bta_gatts_cancel_open(p_cb, (tBTA_GATTS_DATA *) p_msg); + break; + + case BTA_GATTS_API_CLOSE_EVT: + bta_gatts_close(p_cb, (tBTA_GATTS_DATA *) p_msg); + break; + + case BTA_GATTS_API_RSP_EVT: + bta_gatts_send_rsp(p_cb, (tBTA_GATTS_DATA *) p_msg); + break; + case BTA_GATTS_API_SET_ATTR_VAL_EVT:{ + UINT16 attr_id = ((tBTA_GATTS_DATA *) p_msg)->api_set_val.hdr.layer_specific; + p_srvc_cb = bta_gatts_find_srvc_cb_by_attr_id(p_cb, attr_id); + bta_gatts_set_attr_value(p_srvc_cb, (tBTA_GATTS_DATA *) p_msg); + break; + } + case BTA_GATTS_API_LISTEN_EVT: + bta_gatts_listen(p_cb, (tBTA_GATTS_DATA *) p_msg); + break; + case BTA_GATTS_API_ADD_INCL_SRVC_EVT: + case BTA_GATTS_API_ADD_CHAR_EVT: + case BTA_GATTS_API_ADD_DESCR_EVT: + case BTA_GATTS_API_DEL_SRVC_EVT: + case BTA_GATTS_API_START_SRVC_EVT: + case BTA_GATTS_API_STOP_SRVC_EVT: + p_srvc_cb = bta_gatts_find_srvc_cb_by_srvc_id(p_cb, + ((tBTA_GATTS_DATA *)p_msg)->api_add_incl_srvc.hdr.layer_specific); + + if (p_srvc_cb != NULL) { + bta_gatts_srvc_build_act[p_msg->event - BTA_GATTS_API_ADD_INCL_SRVC_EVT](p_srvc_cb, (tBTA_GATTS_DATA *) p_msg); + } else { + APPL_TRACE_ERROR("service not created\n"); + } + break; + case BTA_GATTS_API_SEND_SERVICE_CHANGE_EVT: + bta_gatts_send_service_change_indication((tBTA_GATTS_DATA *) p_msg); + break; + case BTA_GATTS_API_SHOW_LOCAL_DATABASE_EVT: + bta_gatts_show_local_database(); + break; + default: + break; + } + + + return (TRUE); +} + +void bta_gatts_deinit(void) +{ + memset(&bta_gatts_cb, 0, sizeof(tBTA_GATTS_CB)); +#if BTA_DYNAMIC_MEMORY + FREE_AND_RESET(bta_gatts_cb_ptr); +#endif /* #if BTA_DYNAMIC_MEMORY */ +} + +uint8_t bta_gatts_srvc_active_count(void) +{ + uint8_t count = 0; + + for (uint8_t i = 0; i < BTA_GATTS_MAX_SRVC_NUM; i ++) { + if (bta_gatts_cb.srvc_cb[i].in_use) { + count++; + } + } + + return count; +} + +#endif /* GATTS_INCLUDED */ diff --git a/lib/bt/host/bluedroid/bta/gatt/bta_gatts_utils.c b/lib/bt/host/bluedroid/bta/gatt/bta_gatts_utils.c new file mode 100644 index 00000000..224e8873 --- /dev/null +++ b/lib/bt/host/bluedroid/bta/gatt/bta_gatts_utils.c @@ -0,0 +1,224 @@ +/****************************************************************************** + * + * Copyright (C) 2003-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains the GATT client utility function. + * + ******************************************************************************/ + +#include "common/bt_target.h" + +#if defined(GATTS_INCLUDED) && (GATTS_INCLUDED == TRUE) + +#include +#include "bta/utl.h" +#include "bta/bta_sys.h" +#include "bta_gatts_int.h" + +static const UINT8 base_uuid[LEN_UUID_128] = {0xFB, 0x34, 0x9B, 0x5F, 0x80, 0x00, 0x00, 0x80, + 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + +/******************************************************************************* +** +** Function bta_gatt_convert_uuid16_to_uuid128 +** +** Description Convert a 16 bits UUID to be an standard 128 bits one. +** +** Returns TRUE if two uuid match; FALSE otherwise. +** +*******************************************************************************/ +static void bta_gatt_convert_uuid16_to_uuid128(UINT8 uuid_128[LEN_UUID_128], UINT16 uuid_16) +{ + UINT8 *p = &uuid_128[LEN_UUID_128 - 4]; + + memcpy (uuid_128, base_uuid, LEN_UUID_128); + + UINT16_TO_STREAM(p, uuid_16); +} +/******************************************************************************* +** +** Function bta_gatts_alloc_srvc_cb +** +** Description allocate a service control block. +** +** Returns pointer to the control block, or otherwise NULL when failed. +** +*******************************************************************************/ +UINT8 bta_gatts_alloc_srvc_cb(tBTA_GATTS_CB *p_cb, UINT8 rcb_idx) +{ + UINT8 i; + + for (i = 0; i < BTA_GATTS_MAX_SRVC_NUM; i ++) { + if (!p_cb->srvc_cb[i].in_use) { + p_cb->srvc_cb[i].in_use = TRUE; + p_cb->srvc_cb[i].rcb_idx = rcb_idx; + return i; + } + } + return BTA_GATTS_INVALID_APP; +} + +/******************************************************************************* +** +** Function bta_gatts_find_app_rcb_by_app_if +** +** Description find the index of the application control block by app ID. +** +** Returns pointer to the control block if success, otherwise NULL +** +*******************************************************************************/ +tBTA_GATTS_RCB *bta_gatts_find_app_rcb_by_app_if(tBTA_GATTS_IF server_if) +{ + UINT8 i; + tBTA_GATTS_RCB *p_reg; + + for (i = 0, p_reg = bta_gatts_cb.rcb; i < BTA_GATTS_MAX_APP_NUM; i ++, p_reg++) { + if (p_reg->in_use && p_reg->gatt_if == server_if) { + return p_reg; + } + } + return NULL; +} + +/******************************************************************************* +** +** Function bta_gatts_find_app_rcb_idx_by_app_if +** +** Description find the index of the application control block by app ID. +** +** Returns index of the control block, or BTA_GATTS_INVALID_APP if failed. +** +*******************************************************************************/ + +UINT8 bta_gatts_find_app_rcb_idx_by_app_if(tBTA_GATTS_CB *p_cb, tBTA_GATTS_IF server_if) +{ + UINT8 i; + + for (i = 0; i < BTA_GATTS_MAX_APP_NUM; i ++) { + if (p_cb->rcb[i].in_use && p_cb->rcb[i].gatt_if == server_if) { + return i; + } + } + return BTA_GATTS_INVALID_APP; +} +/******************************************************************************* +** +** Function bta_gatts_find_srvc_cb_by_srvc_id +** +** Description find the service control block by service ID. +** +** Returns pointer to the rcb. +** +*******************************************************************************/ +tBTA_GATTS_SRVC_CB *bta_gatts_find_srvc_cb_by_srvc_id(tBTA_GATTS_CB *p_cb, UINT16 service_id) +{ + UINT8 i; + APPL_TRACE_DEBUG("bta_gatts_find_srvc_cb_by_srvc_id service_id=%d", service_id); + for (i = 0; i < BTA_GATTS_MAX_SRVC_NUM; i ++) { + if (p_cb->srvc_cb[i].in_use && + p_cb->srvc_cb[i].service_id == service_id) { + APPL_TRACE_DEBUG("bta_gatts_find_srvc_cb_by_srvc_id found service cb index =%d", i); + return &p_cb->srvc_cb[i]; + } + } + return NULL; +} +/******************************************************************************* +** +** Function bta_gatts_find_srvc_cb_by_attr_id +** +** Description find the service control block by attribute ID. +** +** Returns pointer to the rcb. +** +*******************************************************************************/ +tBTA_GATTS_SRVC_CB *bta_gatts_find_srvc_cb_by_attr_id(tBTA_GATTS_CB *p_cb, UINT16 attr_id) +{ + UINT8 i; + + for (i = 0; i < (BTA_GATTS_MAX_SRVC_NUM); i ++) { + if (/* middle service */ + (i < (BTA_GATTS_MAX_SRVC_NUM - 1) && + p_cb->srvc_cb[i].in_use && + p_cb->srvc_cb[i + 1].in_use && + attr_id >= p_cb->srvc_cb[i].service_id && + attr_id < p_cb->srvc_cb[i + 1].service_id) || + /* last active service */ + (i < (BTA_GATTS_MAX_SRVC_NUM - 1) && + p_cb->srvc_cb[i].in_use && + !p_cb->srvc_cb[i + 1].in_use && + attr_id >= p_cb->srvc_cb[i].service_id) || + /* last service incb */ + (i == (BTA_GATTS_MAX_SRVC_NUM - 1) && + attr_id >= p_cb->srvc_cb[i].service_id) + ) { + return &p_cb->srvc_cb[i]; + } + } + return NULL; +} +/******************************************************************************* +** +** Function bta_gatts_uuid_compare +** +** Description Compare two UUID to see if they are the same. +** +** Returns TRUE if two uuid match; FALSE otherwise. +** +*******************************************************************************/ +BOOLEAN bta_gatts_uuid_compare(tBT_UUID tar, tBT_UUID src) +{ + UINT8 su[LEN_UUID_128], tu[LEN_UUID_128]; + UINT8 *ps, *pt; + + /* any of the UUID is unspecified */ + if (src.len == 0 || tar.len == 0) { + return TRUE; + } + + /* If both are 16-bit, we can do a simple compare */ + if (src.len == 2 && tar.len == 2) { + return src.uu.uuid16 == tar.uu.uuid16; + } + + /* One or both of the UUIDs is 128-bit */ + if (src.len == LEN_UUID_16) { + /* convert a 16 bits UUID to 128 bits value */ + bta_gatt_convert_uuid16_to_uuid128(su, src.uu.uuid16); + ps = su; + } else { + ps = src.uu.uuid128; + } + + if (tar.len == LEN_UUID_16) { + /* convert a 16 bits UUID to 128 bits value */ + bta_gatt_convert_uuid16_to_uuid128(tu, tar.uu.uuid16); + pt = tu; + } else { + pt = tar.uu.uuid128; + } + + return (memcmp(ps, pt, LEN_UUID_128) == 0); +} + + + + +#endif /* GATTS_INCLUDED */ diff --git a/lib/bt/host/bluedroid/bta/gatt/include/bta_gattc_int.h b/lib/bt/host/bluedroid/bta/gatt/include/bta_gattc_int.h new file mode 100644 index 00000000..108358ca --- /dev/null +++ b/lib/bt/host/bluedroid/bta/gatt/include/bta_gattc_int.h @@ -0,0 +1,559 @@ +/****************************************************************************** + * + * Copyright (C) 2003-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This is the private file for the file transfer client (FTC). + * + ******************************************************************************/ +#ifndef BTA_GATTC_INT_H +#define BTA_GATTC_INT_H + +#include "common/bt_target.h" +#include "bta/bta_sys.h" +#include "bta/bta_gatt_api.h" +#include "bta/bta_gattc_ci.h" +#include "bta/bta_gattc_co.h" +#include "osi/fixed_queue.h" +#include "osi/mutex.h" + +/***************************************************************************** +** Constants and data types +*****************************************************************************/ +enum { + BTA_GATTC_API_OPEN_EVT = BTA_SYS_EVT_START(BTA_ID_GATTC), + BTA_GATTC_INT_OPEN_FAIL_EVT, + BTA_GATTC_API_CANCEL_OPEN_EVT, + BTA_GATTC_INT_CANCEL_OPEN_OK_EVT, + + BTA_GATTC_API_READ_EVT, + BTA_GATTC_API_WRITE_EVT, + BTA_GATTC_API_EXEC_EVT, + BTA_GATTC_API_CFG_MTU_EVT, + + BTA_GATTC_API_CLOSE_EVT, + + BTA_GATTC_API_SEARCH_EVT, + BTA_GATTC_API_CONFIRM_EVT, + BTA_GATTC_API_READ_MULTI_EVT, + BTA_GATTC_API_REFRESH_EVT, + BTA_GATTC_API_CACHE_CLEAN_EVT, + + BTA_GATTC_INT_CONN_EVT, + BTA_GATTC_INT_DISCOVER_EVT, + BTA_GATTC_DISCOVER_CMPL_EVT, + BTA_GATTC_OP_CMPL_EVT, + BTA_GATTC_INT_DISCONN_EVT, + + BTA_GATTC_API_READ_BY_TYPE_EVT, + BTA_GATTC_API_READ_MULTI_VAR_EVT, + + BTA_GATTC_INT_START_IF_EVT, + BTA_GATTC_API_REG_EVT, + BTA_GATTC_API_DEREG_EVT, + BTA_GATTC_API_LISTEN_EVT, + BTA_GATTC_API_BROADCAST_EVT, + BTA_GATTC_API_DISABLE_EVT, + BTA_GATTC_ENC_CMPL_EVT, + BTA_GATTC_API_CACHE_ASSOC_EVT, + BTA_GATTC_API_CACHE_GET_ADDR_LIST_EVT, +}; +typedef UINT16 tBTA_GATTC_INT_EVT; + +#define BTA_GATTC_SERVICE_CHANGED_LEN 4 + +typedef enum { + BTA_GATTC_SERVICE_INFO_FROM_REMOTE_DEVICE = 0, + BTA_GATTC_SERVICE_INFO_FROM_NVS_FLASH = 1, + BTA_GATTC_SERVICE_INFO_FROM_UNKNOWN = 2, +} tBTA_SERVICE_SOURCE_t; + +/* max client application GATTC can support */ +#ifndef BTA_GATTC_CL_MAX +#if (GATT_MAX_PHY_CHANNEL > 3) + #define BTA_GATTC_CL_MAX GATT_MAX_PHY_CHANNEL +#else + #define BTA_GATTC_CL_MAX 3 // The origin value is 10 +#endif +#endif + +/* max known devices GATTC can support */ +#ifndef BTA_GATTC_KNOWN_SR_MAX +#if (GATT_MAX_PHY_CHANNEL > 3) + #define BTA_GATTC_KNOWN_SR_MAX GATT_MAX_PHY_CHANNEL +#else + #define BTA_GATTC_KNOWN_SR_MAX 3 // The origin value is 10 +#endif +#endif + +#define BTA_GATTC_CONN_MAX GATT_MAX_PHY_CHANNEL + +#ifndef BTA_GATTC_CLCB_MAX +#define BTA_GATTC_CLCB_MAX GATT_CL_MAX_LCB +#endif + +#define BTA_GATTC_WRITE_PREPARE GATT_WRITE_PREPARE +#define BTA_GATTC_INVALID_HANDLE 0 + +/* internal strucutre for GATTC register API */ +typedef struct { + BT_HDR hdr; + tBT_UUID app_uuid; + tBTA_GATTC_CBACK *p_cback; +} tBTA_GATTC_API_REG; + +typedef struct { + BT_HDR hdr; + tBTA_GATTC_IF client_if; +} tBTA_GATTC_INT_START_IF; + +typedef tBTA_GATTC_INT_START_IF tBTA_GATTC_API_DEREG; +typedef tBTA_GATTC_INT_START_IF tBTA_GATTC_INT_DEREG; + +typedef struct { + BT_HDR hdr; + BD_ADDR remote_bda; + tBTA_ADDR_TYPE remote_addr_type; + tBTA_GATTC_IF client_if; + BOOLEAN is_direct; + BOOLEAN is_aux; + tBTA_TRANSPORT transport; +} tBTA_GATTC_API_OPEN; + +typedef tBTA_GATTC_API_OPEN tBTA_GATTC_API_CANCEL_OPEN; + +typedef struct { + BT_HDR hdr; + tBTA_GATT_AUTH_REQ auth_req; + UINT16 handle; + UINT16 s_handle; + UINT16 e_handle; + tBT_UUID uuid; + tBTA_GATTC_EVT cmpl_evt; +} tBTA_GATTC_API_READ; + +typedef struct { + BT_HDR hdr; + tBTA_GATT_AUTH_REQ auth_req; + UINT16 handle; + tBTA_GATTC_EVT cmpl_evt; + tBTA_GATTC_WRITE_TYPE write_type; + UINT16 offset; + UINT16 len; + UINT8 *p_value; +} tBTA_GATTC_API_WRITE; + +typedef struct { + BT_HDR hdr; + BOOLEAN is_execute; +} tBTA_GATTC_API_EXEC; + +typedef struct { + BT_HDR hdr; + UINT16 handle; +} tBTA_GATTC_API_CONFIRM; + +typedef tGATT_CL_COMPLETE tBTA_GATTC_CMPL; + +typedef struct { + BT_HDR hdr; + UINT8 op_code; + tGATT_STATUS status; + tBTA_GATTC_CMPL *p_cmpl; +} tBTA_GATTC_OP_CMPL; + +typedef struct { + BT_HDR hdr; + tBT_UUID *p_srvc_uuid; +} tBTA_GATTC_API_SEARCH; + +typedef struct { + BT_HDR hdr; + tBTA_GATT_AUTH_REQ auth_req; + UINT8 num_attr; + UINT16 handles[GATT_MAX_READ_MULTI_HANDLES]; + tBTA_GATTC_EVT cmpl_evt; +}tBTA_GATTC_API_READ_MULTI; + +typedef struct { + BT_HDR hdr; + BD_ADDR_PTR remote_bda; + tBTA_GATTC_IF client_if; + BOOLEAN start; +} tBTA_GATTC_API_LISTEN; + + +typedef struct { + BT_HDR hdr; +} tBTA_GATTC_API_CFG_MTU; + +typedef struct { + BT_HDR hdr; + tBTA_GATTC_IF client_if; + BD_ADDR src_addr; + BD_ADDR assoc_addr; + BOOLEAN is_assoc; +} tBTA_GATTC_API_CACHE_ASSOC; + +typedef struct { + BT_HDR hdr; + tBTA_GATTC_IF client_if; +} tBTA_GATTC_API_GET_ADDR; + +typedef struct { + BT_HDR hdr; + BD_ADDR remote_bda; + tBTA_GATTC_IF client_if; + UINT8 role; + tBT_TRANSPORT transport; + tGATT_DISCONN_REASON reason; + BOOLEAN already_connect; + tBTA_GATT_CONN_PARAMS conn_params; + UINT8 ble_addr_type; + UINT16 conn_handle; +} tBTA_GATTC_INT_CONN; + +typedef struct { + BT_HDR hdr; + BD_ADDR remote_bda; + tBTA_GATTC_IF client_if; +} tBTA_GATTC_ENC_CMPL; + +typedef union { + BT_HDR hdr; + tBTA_GATTC_API_REG api_reg; + tBTA_GATTC_API_DEREG api_dereg; + tBTA_GATTC_API_OPEN api_conn; + tBTA_GATTC_API_CANCEL_OPEN api_cancel_conn; + tBTA_GATTC_API_READ api_read; + tBTA_GATTC_API_SEARCH api_search; + tBTA_GATTC_API_WRITE api_write; + tBTA_GATTC_API_CONFIRM api_confirm; + tBTA_GATTC_API_EXEC api_exec; + tBTA_GATTC_API_READ_MULTI api_read_multi; + tBTA_GATTC_API_CFG_MTU api_mtu; + tBTA_GATTC_API_CACHE_ASSOC api_assoc; + tBTA_GATTC_API_GET_ADDR api_get_addr; + tBTA_GATTC_OP_CMPL op_cmpl; + tBTA_GATTC_INT_CONN int_conn; + tBTA_GATTC_ENC_CMPL enc_cmpl; + + tBTA_GATTC_INT_START_IF int_start_if; + tBTA_GATTC_INT_DEREG int_dereg; + /* if peripheral role is supported */ + tBTA_GATTC_API_LISTEN api_listen; + +} tBTA_GATTC_DATA; + + +/* GATT server cache on the client */ +typedef struct { + tBT_UUID uuid; + UINT16 s_handle; + UINT16 e_handle; + // this field is set only for characteristic + UINT16 char_decl_handle; + BOOLEAN is_primary; + tBTA_GATT_CHAR_PROP property; +} tBTA_GATTC_ATTR_REC; + + +#define BTA_GATTC_ATTR_LIST_SIZE (BTA_GATTC_MAX_CACHE_CHAR * sizeof(tBTA_GATTC_ATTR_REC)) + +#ifndef BTA_GATTC_CACHE_SRVR_SIZE +#define BTA_GATTC_CACHE_SRVR_SIZE 600 +#endif + +enum { + BTA_GATTC_IDLE_ST = 0, /* Idle */ + BTA_GATTC_W4_CONN_ST, /* Wait for connection - (optional) */ + BTA_GATTC_CONN_ST, /* connected state */ + BTA_GATTC_DISCOVER_ST /* discover is in progress */ +}; +typedef UINT8 tBTA_GATTC_STATE; + +typedef struct { + BOOLEAN in_use; + BD_ADDR server_bda; + BOOLEAN connected; + +#define BTA_GATTC_SERV_IDLE 0 +#define BTA_GATTC_SERV_LOAD 1 +#define BTA_GATTC_SERV_SAVE 2 +#define BTA_GATTC_SERV_DISC 3 +#define BTA_GATTC_SERV_DISC_ACT 4 + + UINT8 state; + + list_t *p_srvc_cache; /* list of tBTA_GATTC_SERVICE */ + UINT8 update_count; /* indication received */ + UINT8 num_clcb; /* number of associated CLCB */ + + + tBTA_GATTC_ATTR_REC *p_srvc_list; + UINT8 cur_srvc_idx; + UINT16 cur_char_idx; + UINT16 next_avail_idx; + UINT8 total_srvc; + UINT16 total_char; + UINT16 total_attr; + UINT8 srvc_hdl_chg; /* service handle change indication pending */ + UINT16 attr_index; /* cahce NV saving/loading attribute index */ + + UINT16 mtu; + bool update_incl_srvc; +} tBTA_GATTC_SERV; + +typedef struct { + BOOLEAN in_use; + BD_ADDR remote_bda; + UINT16 handle; +}tBTA_GATTC_NOTIF_REG; + +typedef struct { + tBTA_GATTC_CBACK *p_cback; + BOOLEAN in_use; + tBTA_GATTC_IF client_if; /* client interface with BTE stack for this application */ + UINT8 num_clcb; /* number of associated CLCB */ + BOOLEAN dereg_pending; + tBT_UUID app_uuid; + tBTA_GATTC_NOTIF_REG notif_reg[BTA_GATTC_NOTIF_REG_MAX]; +} tBTA_GATTC_RCB; + +/* client channel is a mapping between a BTA client(cl_id) and a remote BD address */ +typedef struct { + UINT16 bta_conn_id; /* client channel ID, unique for clcb */ + BD_ADDR bda; + tBTA_TRANSPORT transport; /* channel transport */ + tBTA_GATTC_RCB *p_rcb; /* pointer to the registration CB */ + tBTA_GATTC_SERV *p_srcb; /* server cache CB */ + tBTA_GATTC_DATA *p_q_cmd; /* command in queue waiting for execution */ + list_t *p_cmd_list; /* The list to store the command to be sent */ + BOOLEAN is_full; /* The gattc command queue is full or not */ +#define BTA_GATTC_NO_SCHEDULE 0 +#define BTA_GATTC_DISC_WAITING 0x01 +#define BTA_GATTC_REQ_WAITING 0x10 + + UINT8 auto_update; /* auto update is waiting */ + BOOLEAN disc_active; + BOOLEAN in_use; + tBTA_GATTC_STATE state; + tBTA_GATT_STATUS status; + UINT16 reason; + UINT8 searched_service_source; +} tBTA_GATTC_CLCB; + +/* background connection tracking information */ +#if GATT_MAX_APPS <= 8 +typedef UINT8 tBTA_GATTC_CIF_MASK ; +#elif GATT_MAX_APPS <= 16 +typedef UINT16 tBTA_GATTC_CIF_MASK; +#elif GATT_MAX_APPS <= 32 +typedef UINT32 tBTA_GATTC_CIF_MASK; +#endif + +typedef struct { + BOOLEAN in_use; + BD_ADDR remote_bda; + tBTA_GATTC_CIF_MASK cif_mask; + tBTA_GATTC_CIF_MASK cif_adv_mask; + +} tBTA_GATTC_BG_TCK; + +typedef struct { + BOOLEAN in_use; + BD_ADDR remote_bda; + UINT16 svc_change_descr_handle; + BOOLEAN write_remote_svc_change_ccc_done; +} tBTA_GATTC_CONN; + +enum { + BTA_GATTC_STATE_DISABLED, + BTA_GATTC_STATE_ENABLING, + BTA_GATTC_STATE_ENABLED, + BTA_GATTC_STATE_DISABLING +}; + +typedef struct { + UINT8 state; + BOOLEAN auto_disc; /* internal use: true for auto discovering after connected */ + tBTA_GATTC_CONN conn_track[BTA_GATTC_CONN_MAX]; + tBTA_GATTC_BG_TCK bg_track[BTA_GATTC_KNOWN_SR_MAX]; + tBTA_GATTC_RCB cl_rcb[BTA_GATTC_CL_MAX]; + + tBTA_GATTC_CLCB clcb[BTA_GATTC_CLCB_MAX]; + tBTA_GATTC_SERV known_server[BTA_GATTC_KNOWN_SR_MAX]; +}tBTA_GATTC_CB; + +typedef enum { + SERVICE_CHANGE_CCC_WRITTEN_SUCCESS = 0, + SERVICE_CHANGE_CACHE_NOT_FOUND, + SERVICE_CHANGE_SERVICE_NOT_FOUND, + SERVICE_CHANGE_CHAR_NOT_FOUND, + SERVICE_CHANGE_CCC_NOT_FOUND, + SERVICE_CHANGE_WRITE_CCC_FAILED +}tBTA_GATTC_FIND_SERVICE_CB; + + +/***************************************************************************** +** Global data +*****************************************************************************/ + +/* GATTC control block */ +#if BTA_DYNAMIC_MEMORY == FALSE +extern tBTA_GATTC_CB bta_gattc_cb; +#else +extern tBTA_GATTC_CB *bta_gattc_cb_ptr; +#define bta_gattc_cb (*bta_gattc_cb_ptr) +#endif + +/***************************************************************************** +** Function prototypes +*****************************************************************************/ +extern BOOLEAN bta_gattc_hdl_event(BT_HDR *p_msg); +extern BOOLEAN bta_gattc_sm_execute(tBTA_GATTC_CLCB *p_clcb, UINT16 event, tBTA_GATTC_DATA *p_data); + +/* function processed outside SM */ +extern void bta_gattc_disable(tBTA_GATTC_CB *p_cb); +extern void bta_gattc_register(tBTA_GATTC_CB *p_cb, tBTA_GATTC_DATA *p_data); +extern void bta_gattc_start_if(tBTA_GATTC_CB *p_cb, tBTA_GATTC_DATA *p_data); +extern void bta_gattc_process_api_open (tBTA_GATTC_CB *p_cb, tBTA_GATTC_DATA *p_msg); +extern void bta_gattc_process_api_open_cancel (tBTA_GATTC_CB *p_cb, tBTA_GATTC_DATA *p_msg); +extern void bta_gattc_deregister(tBTA_GATTC_CB *p_cb, tBTA_GATTC_RCB *p_clreg); +extern void bta_gattc_process_enc_cmpl(tBTA_GATTC_CB *p_cb, tBTA_GATTC_DATA *p_msg); + +/* function within state machine */ +extern void bta_gattc_open(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data); +extern void bta_gattc_open_fail(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data); +extern void bta_gattc_open_error(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data); + +extern void bta_gattc_cancel_open(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data); +extern void bta_gattc_cancel_open_ok(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data); +extern void bta_gattc_cancel_open_error(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data); + +extern void bta_gattc_conn(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data); +extern void bta_gattc_conncback(tBTA_GATTC_RCB *p_rcb, tBTA_GATTC_DATA *p_data); +extern void bta_gattc_disconncback(tBTA_GATTC_RCB *p_rcb, tBTA_GATTC_DATA *p_data); +extern void bta_gattc_close(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data); +extern void bta_gattc_close_fail(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data); +extern void bta_gattc_disc_close(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data); + +extern void bta_gattc_start_discover(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data); +extern void bta_gattc_disc_cmpl(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data); +extern void bta_gattc_read(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data); +extern void bta_gattc_read_by_type(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data); +extern void bta_gattc_write(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data); +extern void bta_gattc_op_cmpl(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data); +extern void bta_gattc_q_cmd(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data); +extern void bta_gattc_free_command_data(tBTA_GATTC_CLCB *p_clcb); +extern void bta_gattc_search(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data); +extern void bta_gattc_fail(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data); +extern void bta_gattc_confirm(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data); +extern void bta_gattc_execute(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data); +extern void bta_gattc_read_multi(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data); +extern void bta_gattc_read_multi_var(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data); +extern void bta_gattc_ci_open(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data); +extern void bta_gattc_ci_close(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data); +extern void bta_gattc_ignore_op_cmpl(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data); +extern void bta_gattc_restart_discover(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_msg); +extern void bta_gattc_init_bk_conn(tBTA_GATTC_API_OPEN *p_data, tBTA_GATTC_RCB *p_clreg); +extern void bta_gattc_cancel_bk_conn(tBTA_GATTC_API_CANCEL_OPEN *p_data); +extern void bta_gattc_send_open_cback( tBTA_GATTC_RCB *p_clreg, tBTA_GATT_STATUS status, + BD_ADDR remote_bda, UINT16 conn_id, tBTA_TRANSPORT transport, UINT16 mtu); +extern void bta_gattc_send_connect_cback( tBTA_GATTC_RCB *p_clreg, BD_ADDR remote_bda, UINT16 conn_id, + tBTA_GATT_CONN_PARAMS conn_params, UINT8 link_role, UINT8 ble_addr_type, UINT16 conn_handle); +extern void bta_gattc_send_disconnect_cback( tBTA_GATTC_RCB *p_clreg, tGATT_DISCONN_REASON reason, + BD_ADDR remote_bda, UINT16 conn_id); +extern void bta_gattc_process_api_refresh(tBTA_GATTC_CB *p_cb, tBTA_GATTC_DATA *p_msg); +extern void bta_gattc_process_api_cache_clean(tBTA_GATTC_CB *p_cb, tBTA_GATTC_DATA *p_msg); +extern void bta_gattc_process_api_cache_assoc(tBTA_GATTC_CB *p_cb, tBTA_GATTC_DATA *p_msg); +extern void bta_gattc_process_api_cache_get_addr_list(tBTA_GATTC_CB *p_cb, tBTA_GATTC_DATA *p_msg); +extern void bta_gattc_cfg_mtu(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data); +#if BLE_INCLUDED == TRUE +extern void bta_gattc_listen(tBTA_GATTC_CB *p_cb, tBTA_GATTC_DATA *p_msg); +extern void bta_gattc_broadcast(tBTA_GATTC_CB *p_cb, tBTA_GATTC_DATA *p_msg); +#endif +/* utility functions */ +extern tBTA_GATTC_CLCB *bta_gattc_find_clcb_by_cif (UINT8 client_if, BD_ADDR remote_bda, tBTA_TRANSPORT transport); +extern tBTA_GATTC_CLCB *bta_gattc_find_clcb_by_conn_id (UINT16 conn_id); +extern tBTA_GATTC_CLCB *bta_gattc_clcb_alloc(tBTA_GATTC_IF client_if, BD_ADDR remote_bda, tBTA_TRANSPORT transport); +extern void bta_gattc_clcb_dealloc(tBTA_GATTC_CLCB *p_clcb); +extern tBTA_GATTC_CLCB *bta_gattc_find_alloc_clcb(tBTA_GATTC_IF client_if, BD_ADDR remote_bda, tBTA_TRANSPORT transport); +extern tBTA_GATTC_RCB *bta_gattc_cl_get_regcb(UINT8 client_if); +extern tBTA_GATTC_SERV *bta_gattc_find_srcb(BD_ADDR bda); +extern tBTA_GATTC_SERV *bta_gattc_srcb_alloc(BD_ADDR bda); +extern tBTA_GATTC_SERV *bta_gattc_find_scb_by_cid (UINT16 conn_id); +extern tBTA_GATTC_CLCB *bta_gattc_find_int_conn_clcb(tBTA_GATTC_DATA *p_msg); +extern tBTA_GATTC_CLCB *bta_gattc_find_int_disconn_clcb(tBTA_GATTC_DATA *p_msg); + +extern BOOLEAN bta_gattc_enqueue(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data); + +extern BOOLEAN bta_gattc_uuid_compare (const tBT_UUID *p_src, const tBT_UUID *p_tar, BOOLEAN is_precise); +extern BOOLEAN bta_gattc_check_notif_registry(tBTA_GATTC_RCB *p_clreg, tBTA_GATTC_SERV *p_srcb, tBTA_GATTC_NOTIFY *p_notify); +extern BOOLEAN bta_gattc_mark_bg_conn (tBTA_GATTC_IF client_if, BD_ADDR_PTR remote_bda, BOOLEAN add, BOOLEAN is_listen); +extern BOOLEAN bta_gattc_check_bg_conn (tBTA_GATTC_IF client_if, BD_ADDR remote_bda, UINT8 role); +extern UINT8 bta_gattc_num_reg_app(void); +extern void bta_gattc_clear_notif_registration(tBTA_GATTC_SERV *p_srcb, UINT16 conn_id, UINT16 start_handle, UINT16 end_handle); +extern void bta_gattc_clear_notif_registration_by_bda(tBTA_GATTC_RCB *p_clrcb, BD_ADDR remote_bda); +extern tBTA_GATTC_SERV * bta_gattc_find_srvr_cache(BD_ADDR bda); + +/* discovery functions */ +extern void bta_gattc_disc_res_cback (UINT16 conn_id, tGATT_DISC_TYPE disc_type, tGATT_DISC_RES *p_data); +extern void bta_gattc_disc_cmpl_cback (UINT16 conn_id, tGATT_DISC_TYPE disc_type, tGATT_STATUS status); +extern tBTA_GATT_STATUS bta_gattc_discover_procedure(UINT16 conn_id, tBTA_GATTC_SERV *p_server_cb, UINT8 disc_type); +extern tBTA_GATT_STATUS bta_gattc_discover_pri_service(UINT16 conn_id, tBTA_GATTC_SERV *p_server_cb, UINT8 disc_type); +extern void bta_gattc_search_service(tBTA_GATTC_CLCB *p_clcb, tBT_UUID *p_uuid); +extern const list_t* bta_gattc_get_services(UINT16 conn_id); +extern const tBTA_GATTC_SERVICE* bta_gattc_get_service_for_handle(UINT16 conn_id, UINT16 handle); +tBTA_GATTC_CHARACTERISTIC* bta_gattc_get_characteristic_srcb(tBTA_GATTC_SERV *p_srcb, UINT16 handle); +extern tBTA_GATTC_CHARACTERISTIC* bta_gattc_get_characteristic(UINT16 conn_id, UINT16 handle); +extern tBTA_GATTC_DESCRIPTOR* bta_gattc_get_descriptor(UINT16 conn_id, UINT16 handle); +extern void bta_gattc_get_db_size_handle(UINT16 conn_id, UINT16 start_handle, UINT16 end_handle, UINT16 *count); +extern void bta_gattc_get_db_size_with_type_handle(UINT16 conn_id, bt_gatt_db_attribute_type_t type, + UINT16 start_handle, UINT16 end_handle, UINT16 char_handle, UINT16 *count); +extern void bta_gattc_get_service_with_uuid(UINT16 conn_id, tBT_UUID *svc_uuid, + btgatt_db_element_t **svc_db, + UINT16 *count); + +extern void bta_gattc_get_db_with_opration(UINT16 conn_id, + bt_gatt_get_db_op_t op, + UINT16 char_handle, + tBT_UUID *incl_uuid, + tBT_UUID *char_uuid, + tBT_UUID *descr_uuid, + UINT16 start_handle, UINT16 end_handle, + btgatt_db_element_t **char_db, + UINT16 *count); + +extern void bta_gattc_get_gatt_db(UINT16 conn_id, UINT16 start_handle, UINT16 end_handle, btgatt_db_element_t **db, UINT16 *count); + +extern tBTA_GATT_STATUS bta_gattc_init_cache(tBTA_GATTC_SERV *p_srvc_cb); +extern void bta_gattc_rebuild_cache(tBTA_GATTC_SERV *p_srcv, UINT16 num_attr, tBTA_GATTC_NV_ATTR *attr); +extern void bta_gattc_cache_save(tBTA_GATTC_SERV *p_srvc_cb, UINT16 conn_id); +extern void bta_gattc_reset_discover_st(tBTA_GATTC_SERV *p_srcb, tBTA_GATT_STATUS status); + +extern tBTA_GATTC_CONN *bta_gattc_conn_alloc(BD_ADDR remote_bda); +extern tBTA_GATTC_CONN *bta_gattc_conn_find(BD_ADDR remote_bda); +extern tBTA_GATTC_CONN *bta_gattc_conn_find_alloc(BD_ADDR remote_bda); +extern BOOLEAN bta_gattc_conn_dealloc(BD_ADDR remote_bda); + +extern bool bta_gattc_cache_load(tBTA_GATTC_CLCB *p_clcb); +extern void bta_gattc_cache_reset(BD_ADDR server_bda); +extern void bta_gattc_deinit(void); + +#endif /* BTA_GATTC_INT_H */ diff --git a/lib/bt/host/bluedroid/bta/gatt/include/bta_gatts_int.h b/lib/bt/host/bluedroid/bta/gatt/include/bta_gatts_int.h new file mode 100644 index 00000000..51d6fd4e --- /dev/null +++ b/lib/bt/host/bluedroid/bta/gatt/include/bta_gatts_int.h @@ -0,0 +1,265 @@ +/****************************************************************************** + * + * Copyright (C) 2003-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This is the private file for the BTA GATT server. + * + ******************************************************************************/ +#ifndef BTA_GATTS_INT_H +#define BTA_GATTS_INT_H + +#include "common/bt_target.h" +#include "bta/bta_sys.h" +#include "bta/bta_gatt_api.h" +#include "stack/gatt_api.h" + + +/***************************************************************************** +** Constants and data types +*****************************************************************************/ +enum { + BTA_GATTS_API_REG_EVT = BTA_SYS_EVT_START(BTA_ID_GATTS), + BTA_GATTS_INT_START_IF_EVT, + BTA_GATTS_API_DEREG_EVT, + BTA_GATTS_API_CREATE_SRVC_EVT, + BTA_GATTS_API_INDICATION_EVT, + + BTA_GATTS_API_ADD_INCL_SRVC_EVT, + BTA_GATTS_API_ADD_CHAR_EVT, + BTA_GATTS_API_ADD_DESCR_EVT, + BTA_GATTS_API_DEL_SRVC_EVT, + BTA_GATTS_API_START_SRVC_EVT, + BTA_GATTS_API_STOP_SRVC_EVT, + BTA_GATTS_API_RSP_EVT, + BTA_GATTS_API_SET_ATTR_VAL_EVT, + BTA_GATTS_API_OPEN_EVT, + BTA_GATTS_API_CANCEL_OPEN_EVT, + BTA_GATTS_API_CLOSE_EVT, + BTA_GATTS_API_LISTEN_EVT, + BTA_GATTS_API_DISABLE_EVT, + BTA_GATTS_API_SEND_SERVICE_CHANGE_EVT, + BTA_GATTS_API_SHOW_LOCAL_DATABASE_EVT +}; +typedef UINT16 tBTA_GATTS_INT_EVT; + +/* max number of application allowed on device */ +#define BTA_GATTS_MAX_APP_NUM GATT_MAX_SR_PROFILES + +/* max number of services allowed in the device */ +#define BTA_GATTS_MAX_SRVC_NUM GATT_MAX_SR_PROFILES + +/* internal strucutre for GATTC register API */ +typedef struct { + BT_HDR hdr; + tBT_UUID app_uuid; + tBTA_GATTS_CBACK *p_cback; +} tBTA_GATTS_API_REG; + +typedef struct { + BT_HDR hdr; + tBTA_GATTS_IF server_if; +} tBTA_GATTS_INT_START_IF; + +typedef tBTA_GATTS_INT_START_IF tBTA_GATTS_API_DEREG; + +typedef struct { + BT_HDR hdr; + tBTA_GATTS_IF server_if; + tBT_UUID service_uuid; + UINT16 num_handle; + UINT8 inst; + BOOLEAN is_pri; + +} tBTA_GATTS_API_CREATE_SRVC; + +typedef struct { + BT_HDR hdr; + tBT_UUID char_uuid; + tBTA_GATT_PERM perm; + tBTA_GATT_CHAR_PROP property; + tBTA_GATTS_ATTR_CONTROL control; + tBTA_GATT_ATTR_VAL attr_val; +} tBTA_GATTS_API_ADD_CHAR; + +typedef struct { + BT_HDR hdr; + UINT16 included_service_id; +} tBTA_GATTS_API_ADD_INCL_SRVC; + +typedef struct { + BT_HDR hdr; + tBT_UUID descr_uuid; + tBTA_GATT_PERM perm; + tBTA_GATTS_ATTR_CONTROL control; + tBTA_GATT_ATTR_VAL attr_val; +} tBTA_GATTS_API_ADD_DESCR; + +typedef struct { + BT_HDR hdr; + UINT16 attr_id; + UINT16 len; + BOOLEAN need_confirm; + UINT8 value[BTA_GATT_MAX_ATTR_LEN]; +} tBTA_GATTS_API_INDICATION; + +typedef struct { + BT_HDR hdr; + UINT32 trans_id; + tBTA_GATT_STATUS status; + tBTA_GATTS_RSP *p_rsp; +} tBTA_GATTS_API_RSP; + +typedef struct{ + BT_HDR hdr; + UINT16 length; + UINT8 *value; +}tBTA_GATTS_API_SET_ATTR_VAL; + +typedef struct { + BT_HDR hdr; + tBTA_GATT_TRANSPORT transport; +} tBTA_GATTS_API_START; + + +typedef struct { + BT_HDR hdr; + BD_ADDR remote_bda; + tBTA_GATTS_IF server_if; + BOOLEAN is_direct; + tBTA_GATT_TRANSPORT transport; + +} tBTA_GATTS_API_OPEN; + +typedef tBTA_GATTS_API_OPEN tBTA_GATTS_API_CANCEL_OPEN; + +typedef struct { + BT_HDR hdr; + BD_ADDR_PTR remote_bda; + tBTA_GATTS_IF server_if; + BOOLEAN start; +} tBTA_GATTS_API_LISTEN; + +typedef struct { + BT_HDR hdr; + tBTA_GATTS_IF server_if; + BD_ADDR remote_bda; +} tBTA_GATTS_API_SEND_SERVICE_CHANGE; + +typedef union { + BT_HDR hdr; + tBTA_GATTS_API_REG api_reg; + tBTA_GATTS_API_DEREG api_dereg; + tBTA_GATTS_API_CREATE_SRVC api_create_svc; + tBTA_GATTS_API_ADD_INCL_SRVC api_add_incl_srvc; + tBTA_GATTS_API_ADD_CHAR api_add_char; + tBTA_GATTS_API_ADD_DESCR api_add_char_descr; + tBTA_GATTS_API_START api_start; + tBTA_GATTS_API_INDICATION api_indicate; + tBTA_GATTS_API_RSP api_rsp; + tBTA_GATTS_API_SET_ATTR_VAL api_set_val; + tBTA_GATTS_API_OPEN api_open; + tBTA_GATTS_API_CANCEL_OPEN api_cancel_open; + + tBTA_GATTS_INT_START_IF int_start_if; + /* if peripheral role is supported */ + tBTA_GATTS_API_LISTEN api_listen; + tBTA_GATTS_API_SEND_SERVICE_CHANGE api_send_service_change; +} tBTA_GATTS_DATA; + +/* application registration control block */ +typedef struct { + BOOLEAN in_use; + tBT_UUID app_uuid; + tBTA_GATTS_CBACK *p_cback; + tBTA_GATTS_IF gatt_if; +} tBTA_GATTS_RCB; + +/* service registration control block */ +typedef struct { + tBT_UUID service_uuid; /* service UUID */ + UINT16 service_id; /* service handle */ + UINT8 inst_num; /* instance ID */ + UINT8 rcb_idx; + UINT8 idx; /* self index of serviec CB */ + BOOLEAN in_use; + +} tBTA_GATTS_SRVC_CB; + + +/* GATT server control block */ +typedef struct { + BOOLEAN enabled; + tBTA_GATTS_RCB rcb[BTA_GATTS_MAX_APP_NUM]; + tBTA_GATTS_SRVC_CB srvc_cb[BTA_GATTS_MAX_SRVC_NUM]; +} tBTA_GATTS_CB; + + + +/***************************************************************************** +** Global data +*****************************************************************************/ + +/* GATTC control block */ +#if BTA_DYNAMIC_MEMORY == FALSE +extern tBTA_GATTS_CB bta_gatts_cb; +#else +extern tBTA_GATTS_CB *bta_gatts_cb_ptr; +#define bta_gatts_cb (*bta_gatts_cb_ptr) +#endif + +/***************************************************************************** +** Function prototypes +*****************************************************************************/ +extern BOOLEAN bta_gatts_hdl_event(BT_HDR *p_msg); + +extern void bta_gatts_api_disable(tBTA_GATTS_CB *p_cb); +extern void bta_gatts_api_enable(tBTA_GATTS_CB *p_cb, tBTA_GATTS_DATA *p_data); +extern void bta_gatts_register(tBTA_GATTS_CB *p_cb, tBTA_GATTS_DATA *p_msg); +extern void bta_gatts_start_if(tBTA_GATTS_CB *p_cb, tBTA_GATTS_DATA *p_msg); +extern void bta_gatts_deregister(tBTA_GATTS_CB *p_cb, tBTA_GATTS_DATA *p_msg); +extern void bta_gatts_create_srvc(tBTA_GATTS_CB *p_cb, tBTA_GATTS_DATA *p_msg); +extern void bta_gatts_add_include_srvc(tBTA_GATTS_SRVC_CB *p_srvc_cb, tBTA_GATTS_DATA *p_msg); +extern void bta_gatts_add_char(tBTA_GATTS_SRVC_CB *p_srvc_cb, tBTA_GATTS_DATA *p_msg); +extern void bta_gatts_add_char_descr(tBTA_GATTS_SRVC_CB *p_srvc_cb, tBTA_GATTS_DATA *p_msg); +extern void bta_gatts_set_attr_value(tBTA_GATTS_SRVC_CB *p_srvc_cb, tBTA_GATTS_DATA *p_msg); +extern tGATT_STATUS bta_gatts_get_attr_value(UINT16 attr_handle, UINT16 *length, UINT8 **value); +extern void bta_gatts_delete_service(tBTA_GATTS_SRVC_CB *p_srvc_cb, tBTA_GATTS_DATA *p_msg); +extern void bta_gatts_start_service(tBTA_GATTS_SRVC_CB *p_srvc_cb, tBTA_GATTS_DATA *p_msg); +extern void bta_gatts_stop_service(tBTA_GATTS_SRVC_CB *p_srvc_cb, tBTA_GATTS_DATA *p_msg); + +extern void bta_gatts_send_rsp(tBTA_GATTS_CB *p_cb, tBTA_GATTS_DATA *p_msg); +extern void bta_gatts_indicate_handle (tBTA_GATTS_CB *p_cb, tBTA_GATTS_DATA *p_msg); + + +extern void bta_gatts_open (tBTA_GATTS_CB *p_cb, tBTA_GATTS_DATA *p_msg); +extern void bta_gatts_cancel_open (tBTA_GATTS_CB *p_cb, tBTA_GATTS_DATA *p_msg); +extern void bta_gatts_close (tBTA_GATTS_CB *p_cb, tBTA_GATTS_DATA *p_msg); +extern void bta_gatts_listen(tBTA_GATTS_CB *p_cb, tBTA_GATTS_DATA *p_msg); +extern void bta_gatts_send_service_change_indication (tBTA_GATTS_DATA *p_msg); +extern void bta_gatts_show_local_database (void); + +extern BOOLEAN bta_gatts_uuid_compare(tBT_UUID tar, tBT_UUID src); +extern tBTA_GATTS_RCB *bta_gatts_find_app_rcb_by_app_if(tBTA_GATTS_IF server_if); +extern UINT8 bta_gatts_find_app_rcb_idx_by_app_if(tBTA_GATTS_CB *p_cb, tBTA_GATTS_IF server_if); +extern UINT8 bta_gatts_alloc_srvc_cb(tBTA_GATTS_CB *p_cb, UINT8 rcb_idx); +extern tBTA_GATTS_SRVC_CB *bta_gatts_find_srvc_cb_by_srvc_id(tBTA_GATTS_CB *p_cb, UINT16 service_id); +extern tBTA_GATTS_SRVC_CB *bta_gatts_find_srvc_cb_by_attr_id(tBTA_GATTS_CB *p_cb, UINT16 attr_id); +extern void bta_gatts_deinit(void); + +#endif /* BTA_GATTS_INT_H */ diff --git a/lib/bt/host/bluedroid/bta/hd/bta_hd_act.c b/lib/bt/host/bluedroid/bta/hd/bta_hd_act.c new file mode 100644 index 00000000..8f655d73 --- /dev/null +++ b/lib/bt/host/bluedroid/bta/hd/bta_hd_act.c @@ -0,0 +1,789 @@ +/****************************************************************************** + * + * Copyright (C) 2016 The Android Open Source Project + * Copyright (C) 2005-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ +/****************************************************************************** + * + * This file contains the HID device action functions. + * + ******************************************************************************/ +#include "common/bt_target.h" + +#if defined(BTA_HD_INCLUDED) && (BTA_HD_INCLUDED == TRUE) + +#include "bta/bta_sys.h" +#include "bta_hd_int.h" +#include "osi/allocator.h" +#include "osi/osi.h" +#include "stack/btm_api.h" +#include + +static void bta_hd_cback(BD_ADDR bd_addr, uint8_t event, uint32_t data, BT_HDR *pdata); + +static bool check_descriptor(uint8_t *data, uint16_t length, bool *has_report_id) +{ + uint8_t *ptr = data; + *has_report_id = FALSE; + while (ptr < data + length) { + uint8_t item = *ptr++; + switch (item) { + case 0xfe: // long item indicator + if (ptr < data + length) { + ptr += ((*ptr) + 2); + } else { + return false; + } + break; + case 0x85: // Report ID + *has_report_id = TRUE; + default: + ptr += (item & 0x03); + break; + } + } + return (ptr == data + length); +} + +/******************************************************************************* + * + * Function bta_hd_api_enable + * + * Description Enables HID device + * + * Returns void + * + ******************************************************************************/ +void bta_hd_api_enable(tBTA_HD_DATA *p_data) +{ + tBTA_HD_STATUS status = BTA_HD_ERROR; + tHID_STATUS ret; + + APPL_TRACE_API("%s", __func__); + + HID_DevInit(); + + memset(&bta_hd_cb, 0, sizeof(tBTA_HD_CB)); + + HID_DevSetSecurityLevel(BTA_SEC_AUTHENTICATE | BTA_SEC_ENCRYPT); + /* store parameters */ + bta_hd_cb.p_cback = p_data->api_enable.p_cback; + + ret = HID_DevRegister(bta_hd_cback); + if (ret == HID_SUCCESS) { + status = BTA_HD_OK; + } else { + APPL_TRACE_ERROR("%s: Failed to register HID device (%d)", __func__, ret); + } + + /* signal BTA call back event */ + (*bta_hd_cb.p_cback)(BTA_HD_ENABLE_EVT, (tBTA_HD *)&status); +} + +/******************************************************************************* + * + * Function bta_hd_api_disable + * + * Description Disables HID device + * + * Returns void + * + ******************************************************************************/ +void bta_hd_api_disable(void) +{ + tBTA_HD_STATUS status = BTA_HD_ERROR; + tHID_STATUS ret; + + APPL_TRACE_API("%s", __func__); + + /* service is not enabled */ + if (bta_hd_cb.p_cback == NULL) + return; + + /* Remove service record */ + if (bta_hd_cb.sdp_handle != 0) { + SDP_DeleteRecord(bta_hd_cb.sdp_handle); + bta_sys_remove_uuid(UUID_SERVCLASS_HUMAN_INTERFACE); + } + + /* Deregister with lower layer */ + ret = HID_DevDeregister(); + if (ret == HID_SUCCESS) { + status = BTA_HD_OK; + } else { + APPL_TRACE_ERROR("%s: Failed to deregister HID device (%d)", __func__, ret); + } + + (*bta_hd_cb.p_cback)(BTA_HD_DISABLE_EVT, (tBTA_HD *)&status); + + memset(&bta_hd_cb, 0, sizeof(tBTA_HD_CB)); +} + +/******************************************************************************* + * + * Function bta_hd_register_act + * + * Description Registers SDP record + * + * Returns void + * + ******************************************************************************/ +void bta_hd_register_act(tBTA_HD_DATA *p_data) +{ + tBTA_HD ret; + tBTA_HD_REGISTER_APP *p_app_data = (tBTA_HD_REGISTER_APP *)p_data; + bool use_report_id = FALSE; + + APPL_TRACE_API("%s", __func__); + + ret.reg_status.in_use = FALSE; + + /* Check if len doesn't exceed BTA_HD_APP_DESCRIPTOR_LEN and descriptor + * itself is well-formed. Also check if descriptor has Report Id item so we + * know if report will have prefix or not. */ + if (p_app_data->d_len > BTA_HD_APP_DESCRIPTOR_LEN || + !check_descriptor(p_app_data->d_data, p_app_data->d_len, &use_report_id)) { + APPL_TRACE_ERROR("%s: Descriptor is too long or malformed", __func__); + ret.reg_status.status = BTA_HD_ERROR; + (*bta_hd_cb.p_cback)(BTA_HD_REGISTER_APP_EVT, &ret); + return; + } + + ret.reg_status.status = BTA_HD_OK; + + /* Remove old record if for some reason it's already registered */ + if (bta_hd_cb.sdp_handle != 0) { + SDP_DeleteRecord(bta_hd_cb.sdp_handle); + } + + bta_hd_cb.use_report_id = use_report_id; + bta_hd_cb.sdp_handle = SDP_CreateRecord(); + HID_DevAddRecord(bta_hd_cb.sdp_handle, p_app_data->name, p_app_data->description, p_app_data->provider, + p_app_data->subclass, p_app_data->d_len, p_app_data->d_data); + bta_sys_add_uuid(UUID_SERVCLASS_HUMAN_INTERFACE); + + HID_DevSetIncomingQos(p_app_data->in_qos.service_type, p_app_data->in_qos.token_rate, + p_app_data->in_qos.token_bucket_size, p_app_data->in_qos.peak_bandwidth, + p_app_data->in_qos.access_latency, p_app_data->in_qos.delay_variation); + + HID_DevSetOutgoingQos(p_app_data->out_qos.service_type, p_app_data->out_qos.token_rate, + p_app_data->out_qos.token_bucket_size, p_app_data->out_qos.peak_bandwidth, + p_app_data->out_qos.access_latency, p_app_data->out_qos.delay_variation); + + // application is registered so we can accept incoming connections + HID_DevSetIncomingPolicy(TRUE); + + if (HID_DevGetDevice(&ret.reg_status.bda) == HID_SUCCESS) { + ret.reg_status.in_use = TRUE; + } + + (*bta_hd_cb.p_cback)(BTA_HD_REGISTER_APP_EVT, &ret); +} + +/******************************************************************************* + * + * Function bta_hd_unregister_act + * + * Description Unregisters SDP record + * + * Returns void + * + ******************************************************************************/ +void bta_hd_unregister_act(UNUSED_ATTR tBTA_HD_DATA *p_data) +{ + tBTA_HD_STATUS status = BTA_HD_OK; + + APPL_TRACE_API("%s", __func__); + + // application is no longer registered so we do not want incoming connections + HID_DevSetIncomingPolicy(FALSE); + + if (bta_hd_cb.sdp_handle != 0) { + SDP_DeleteRecord(bta_hd_cb.sdp_handle); + } + + bta_hd_cb.sdp_handle = 0; + bta_sys_remove_uuid(UUID_SERVCLASS_HUMAN_INTERFACE); + + (*bta_hd_cb.p_cback)(BTA_HD_UNREGISTER_APP_EVT, (tBTA_HD *)&status); +} + +/******************************************************************************* + * + * Function bta_hd_unregister2_act + * + * Description + * + * Returns void + * + ******************************************************************************/ +void bta_hd_unregister2_act(tBTA_HD_DATA *p_data) +{ + APPL_TRACE_API("%s", __func__); + + // close first + bta_hd_close_act(p_data); + + // then unregister + bta_hd_unregister_act(p_data); + + if (bta_hd_cb.disable_w4_close) { + bta_hd_api_disable(); + } +} + +/******************************************************************************* + * + * Function bta_hd_connect_act + * + * Description Connect to device (must be virtually plugged) + * + * Returns void + * + ******************************************************************************/ +extern void bta_hd_connect_act(tBTA_HD_DATA *p_data) +{ + tHID_STATUS ret; + tBTA_HD_DEVICE_CTRL *p_ctrl = (tBTA_HD_DEVICE_CTRL *)p_data; + tBTA_HD cback_data; + + APPL_TRACE_API("%s", __func__); + do { + ret = HID_DevPlugDevice(p_ctrl->addr); + if (ret != HID_SUCCESS) { + APPL_TRACE_WARNING("%s: HID_DevPlugDevice returned %d", __func__, ret); + return; + } + + ret = HID_DevConnect(); + if (ret != HID_SUCCESS) { + APPL_TRACE_WARNING("%s: HID_DevConnect returned %d", __func__, ret); + return; + } + } while (0); + + bdcpy(cback_data.conn.bda, p_ctrl->addr); + cback_data.conn.status = (ret == HID_SUCCESS ? BTA_HD_OK : BTA_HD_ERROR); + cback_data.conn.conn_status = BTA_HD_CONN_STATE_CONNECTING; + bta_hd_cb.p_cback(BTA_HD_OPEN_EVT, &cback_data); +} + +/******************************************************************************* + * + * Function bta_hd_disconnect_act + * + * Description Disconnect from device + * + * Returns void + * + ******************************************************************************/ +extern void bta_hd_disconnect_act(UNUSED_ATTR tBTA_HD_DATA *p_data) +{ + tHID_STATUS ret; + tBTA_HD cback_data; + + APPL_TRACE_API("%s", __func__); + + ret = HID_DevDisconnect(); + + if (ret != HID_SUCCESS) { + APPL_TRACE_WARNING("%s: HID_DevDisconnect returned %d", __func__, ret); + return; + } + + if (HID_DevGetDevice(&cback_data.conn.bda) == HID_SUCCESS) { + cback_data.conn.status = BTA_HD_OK; + cback_data.conn.conn_status = BTA_HD_CONN_STATE_DISCONNECTING; + bta_hd_cb.p_cback(BTA_HD_CLOSE_EVT, &cback_data); + } +} + +/******************************************************************************* + * + * Function bta_hd_add_device_act + * + * Description + * + * Returns void + * + ******************************************************************************/ +extern void bta_hd_add_device_act(tBTA_HD_DATA *p_data) +{ + tBTA_HD_DEVICE_CTRL *p_ctrl = (tBTA_HD_DEVICE_CTRL *)p_data; + + APPL_TRACE_API("%s", __func__); + + HID_DevPlugDevice(p_ctrl->addr); +} + +/******************************************************************************* + * + * Function bta_hd_remove_device_act + * + * Description + * + * Returns void + * + ******************************************************************************/ +extern void bta_hd_remove_device_act(tBTA_HD_DATA *p_data) +{ + tBTA_HD_DEVICE_CTRL *p_ctrl = (tBTA_HD_DEVICE_CTRL *)p_data; + + APPL_TRACE_API("%s", __func__); + + HID_DevUnplugDevice(p_ctrl->addr); +} + +/******************************************************************************* + * + * Function bta_hd_send_report_act + * + * Description Sends report + * + * Returns void + * + ******************************************************************************/ +extern void bta_hd_send_report_act(tBTA_HD_DATA *p_data) +{ + tBTA_HD_SEND_REPORT *p_report = (tBTA_HD_SEND_REPORT *)p_data; + uint8_t channel; + uint8_t report_id; + tBTA_HD cback_data; + tHID_STATUS ret; + + APPL_TRACE_VERBOSE("%s", __func__); + + channel = p_report->use_intr ? HID_CHANNEL_INTR : HID_CHANNEL_CTRL; + report_id = (bta_hd_cb.use_report_id || bta_hd_cb.boot_mode) ? p_report->id : 0x00; + + ret = HID_DevSendReport(channel, p_report->type, report_id, p_report->len, p_report->data); + + /* trigger PM */ + bta_sys_busy(BTA_ID_HD, 1, bta_hd_cb.bd_addr); + bta_sys_idle(BTA_ID_HD, 1, bta_hd_cb.bd_addr); + + cback_data.send_report.status = (ret == HID_SUCCESS ? BTA_HD_OK : BTA_HD_ERROR); + cback_data.send_report.reason = ret; + cback_data.send_report.report_id = report_id; + cback_data.send_report.report_type = p_report->type; + bta_hd_cb.p_cback(BTA_HD_SEND_REPORT_EVT, &cback_data); +} + +/******************************************************************************* + * + * Function bta_hd_report_error_act + * + * Description + * + * Returns void + * + ******************************************************************************/ +extern void bta_hd_report_error_act(tBTA_HD_DATA *p_data) +{ + tBTA_HD_REPORT_ERR *p_report = (tBTA_HD_REPORT_ERR *)p_data; + tHID_STATUS ret; + tBTA_HD cback_data; + + APPL_TRACE_API("%s: error = %d", __func__, p_report->error); + + ret = HID_DevReportError(p_report->error); + + if (ret != HID_SUCCESS) { + APPL_TRACE_WARNING("%s: HID_DevReportError returned %d", __func__, ret); + } + + cback_data.report_err.status = (ret == HID_SUCCESS ? BTA_HD_OK : BTA_HD_ERROR); + cback_data.report_err.reason = ret; + bta_hd_cb.p_cback(BTA_HD_REPORT_ERR_EVT, &cback_data); +} + +/******************************************************************************* + * + * Function bta_hd_vc_unplug_act + * + * Description Sends Virtual Cable Unplug + * + * Returns void + * + ******************************************************************************/ +extern void bta_hd_vc_unplug_act(UNUSED_ATTR tBTA_HD_DATA *p_data) +{ + tHID_STATUS ret; + tBTA_HD cback_data = {0}; + BD_ADDR plugged_addr = {0}; + + APPL_TRACE_API("%s", __func__); + + bta_hd_cb.vc_unplug = TRUE; + ret = HID_DevVirtualCableUnplug(); + + if (ret == HID_ERR_NO_CONNECTION) { + /* This is a local VUP without connection, set the vc_unplug to FALSE */ + bta_hd_cb.vc_unplug = FALSE; + APPL_TRACE_WARNING("%s: HID_DevVirtualCableUnplug returned %d", __func__, ret); + if (HID_DevGetDevice(&plugged_addr) == HID_SUCCESS) { + HID_DevUnplugDevice(plugged_addr); + } + APPL_TRACE_DEBUG("%s local VUP, remove bda: %02x:%02x:%02x:%02x:%02x:%02x", __func__, plugged_addr[0], + plugged_addr[1], plugged_addr[2], plugged_addr[3], plugged_addr[4], plugged_addr[5]); + cback_data.conn.status = BTA_HD_OK; + cback_data.conn.conn_status = BTA_HD_CONN_STATE_DISCONNECTED; + bta_hd_cb.p_cback(BTA_HD_VC_UNPLUG_EVT, &cback_data); + return; + } else if (ret != HID_SUCCESS) { + APPL_TRACE_WARNING("%s: HID_DevVirtualCableUnplug returned %d", __func__, ret); + } + + /* trigger PM */ + bta_sys_busy(BTA_ID_HD, 1, bta_hd_cb.bd_addr); + bta_sys_idle(BTA_ID_HD, 1, bta_hd_cb.bd_addr); +} + +/******************************************************************************* + * + * Function bta_hd_open_act + * + * Description + * + * Returns void + * + ******************************************************************************/ +extern void bta_hd_open_act(tBTA_HD_DATA *p_data) +{ + tBTA_HD_CBACK_DATA *p_cback = (tBTA_HD_CBACK_DATA *)p_data; + tBTA_HD cback_data; + + APPL_TRACE_API("%s", __func__); + + HID_DevPlugDevice(p_cback->addr); + bta_sys_conn_open(BTA_ID_HD, 1, p_cback->addr); + + bdcpy(cback_data.conn.bda, p_cback->addr); + bdcpy(bta_hd_cb.bd_addr, p_cback->addr); + cback_data.conn.status = BTA_HD_OK; + cback_data.conn.conn_status = BTA_HD_CONN_STATE_CONNECTED; + bta_hd_cb.p_cback(BTA_HD_OPEN_EVT, &cback_data); +} + +/******************************************************************************* + * + * Function bta_hd_close_act + * + * Description + * + * Returns void + * + ******************************************************************************/ +extern void bta_hd_close_act(tBTA_HD_DATA *p_data) +{ + tBTA_HD_CBACK_DATA *p_cback = (tBTA_HD_CBACK_DATA *)p_data; + tBTA_HD cback_data; + tBTA_HD_EVT cback_event = BTA_HD_CLOSE_EVT; + + APPL_TRACE_API("%s", __func__); + + bta_sys_conn_close(BTA_ID_HD, 1, p_cback->addr); + + if (bta_hd_cb.vc_unplug) { + bta_hd_cb.vc_unplug = FALSE; + HID_DevUnplugDevice(p_cback->addr); + cback_event = BTA_HD_VC_UNPLUG_EVT; + } + + bdcpy(cback_data.conn.bda, p_cback->addr); + memset(bta_hd_cb.bd_addr, 0, sizeof(BD_ADDR)); + cback_data.conn.status = BTA_HD_OK; + cback_data.conn.conn_status = BTA_HD_CONN_STATE_DISCONNECTED; + bta_hd_cb.p_cback(cback_event, &cback_data); +} + +/******************************************************************************* + * + * Function bta_hd_intr_data_act + * + * Description Handles incoming DATA request on intr + * + * Returns void + * + ******************************************************************************/ +extern void bta_hd_intr_data_act(tBTA_HD_DATA *p_data) +{ + tBTA_HD_CBACK_DATA *p_cback = (tBTA_HD_CBACK_DATA *)p_data; + BT_HDR *p_msg = p_cback->p_data; + uint16_t len = p_msg->len; + uint8_t *p_buf = (uint8_t *)(p_msg + 1) + p_msg->offset; + tBTA_HD_INTR_DATA ret; + + APPL_TRACE_API("%s", __func__); + + if (bta_hd_cb.use_report_id || bta_hd_cb.boot_mode) { + ret.report_id = *p_buf; + len--; + p_buf++; + } else { + ret.report_id = 0; + } + + ret.len = len; + ret.p_data = p_buf; + (*bta_hd_cb.p_cback)(BTA_HD_INTR_DATA_EVT, (tBTA_HD *)&ret); + if (p_msg) { + osi_free(p_msg); + } +} + +/******************************************************************************* + * + * Function bta_hd_get_report_act + * + * Description Handles incoming GET_REPORT request + * + * Returns void + * + ******************************************************************************/ +extern void bta_hd_get_report_act(tBTA_HD_DATA *p_data) +{ + tBTA_HD_CBACK_DATA *p_cback = (tBTA_HD_CBACK_DATA *)p_data; + bool rep_size_follows = p_cback->data; + BT_HDR *p_msg = p_cback->p_data; + uint8_t *p_buf = (uint8_t *)(p_msg + 1) + p_msg->offset; + tBTA_HD_GET_REPORT ret = {0, 0, 0}; + uint16_t remaining_len = p_msg->len; + + APPL_TRACE_API("%s", __func__); + if (remaining_len < 1) { + APPL_TRACE_ERROR("%s invalid data, remaining_len:%d", __func__, remaining_len); + return; + } + + ret.report_type = *p_buf & HID_PAR_REP_TYPE_MASK; + p_buf++; + remaining_len--; + + if (bta_hd_cb.use_report_id) { + if (remaining_len < 1) { + APPL_TRACE_ERROR("%s invalid data, remaining_len:%d", __func__, remaining_len); + return; + } + ret.report_id = *p_buf; + p_buf++; + remaining_len--; + } + + if (rep_size_follows) { + if (remaining_len < 2) { + APPL_TRACE_ERROR("%s invalid data, remaining_len:%d", __func__, remaining_len); + return; + } + ret.buffer_size = *p_buf | (*(p_buf + 1) << 8); + } + + (*bta_hd_cb.p_cback)(BTA_HD_GET_REPORT_EVT, (tBTA_HD *)&ret); + if (p_msg) { + osi_free(p_msg); + } +} + +/******************************************************************************* + * + * Function bta_hd_set_report_act + * + * Description Handles incoming SET_REPORT request + * + * Returns void + * + ******************************************************************************/ +extern void bta_hd_set_report_act(tBTA_HD_DATA *p_data) +{ + tBTA_HD_CBACK_DATA *p_cback = (tBTA_HD_CBACK_DATA *)p_data; + BT_HDR *p_msg = p_cback->p_data; + uint16_t len = p_msg->len; + uint8_t *p_buf = (uint8_t *)(p_msg + 1) + p_msg->offset; + tBTA_HD_SET_REPORT ret = {0, 0, 0, NULL}; + + APPL_TRACE_API("%s", __func__); + + ret.report_type = *p_buf & HID_PAR_REP_TYPE_MASK; + p_buf++; + len--; + + if (bta_hd_cb.use_report_id || bta_hd_cb.boot_mode) { + ret.report_id = *p_buf; + len--; + p_buf++; + } else { + ret.report_id = 0; + } + + ret.len = len; + ret.p_data = p_buf; + (*bta_hd_cb.p_cback)(BTA_HD_SET_REPORT_EVT, (tBTA_HD *)&ret); + if (p_msg) { + osi_free(p_msg); + } +} + +/******************************************************************************* + * + * Function bta_hd_set_protocol_act + * + * Description + * + * Returns void + * + ******************************************************************************/ +extern void bta_hd_set_protocol_act(tBTA_HD_DATA *p_data) +{ + tBTA_HD_CBACK_DATA *p_cback = (tBTA_HD_CBACK_DATA *)p_data; + tBTA_HD cback_data; + + APPL_TRACE_API("%s", __func__); + + bta_hd_cb.boot_mode = (p_cback->data == HID_PAR_PROTOCOL_BOOT_MODE); + cback_data.set_protocol = p_cback->data; + + (*bta_hd_cb.p_cback)(BTA_HD_SET_PROTOCOL_EVT, &cback_data); +} + +/******************************************************************************* + * + * Function bta_hd_vc_unplug_done_act + * + * Description + * + * Returns void + * + ******************************************************************************/ +extern void bta_hd_vc_unplug_done_act(tBTA_HD_DATA *p_data) +{ + tBTA_HD_CBACK_DATA *p_cback = (tBTA_HD_CBACK_DATA *)p_data; + tBTA_HD cback_data; + + APPL_TRACE_API("%s", __func__); + + bta_sys_conn_close(BTA_ID_HD, 1, p_cback->addr); + + HID_DevUnplugDevice(p_cback->addr); + + bdcpy(cback_data.conn.bda, p_cback->addr); + bdcpy(bta_hd_cb.bd_addr, p_cback->addr); + cback_data.conn.status = BTA_HD_OK; + cback_data.conn.conn_status = BTA_HD_CONN_STATE_DISCONNECTED; + (*bta_hd_cb.p_cback)(BTA_HD_VC_UNPLUG_EVT, &cback_data); +} + +/******************************************************************************* + * + * Function bta_hd_suspend_act + * + * Description + * + * Returns void + * + ******************************************************************************/ +extern void bta_hd_suspend_act(tBTA_HD_DATA *p_data) +{ + tBTA_HD_CBACK_DATA *p_cback = (tBTA_HD_CBACK_DATA *)p_data; + + APPL_TRACE_API("%s", __func__); + + bta_sys_idle(BTA_ID_HD, 1, p_cback->addr); +} + +/******************************************************************************* + * + * Function bta_hd_exit_suspend_act + * + * Description + * + * Returns void + * + ******************************************************************************/ +extern void bta_hd_exit_suspend_act(tBTA_HD_DATA *p_data) +{ + tBTA_HD_CBACK_DATA *p_cback = (tBTA_HD_CBACK_DATA *)p_data; + + APPL_TRACE_API("%s", __func__); + + bta_sys_busy(BTA_ID_HD, 1, p_cback->addr); + bta_sys_idle(BTA_ID_HD, 1, p_cback->addr); +} + +/******************************************************************************* + * + * Function bta_hd_cback + * + * Description BTA HD callback function + * + * Returns void + * + ******************************************************************************/ +static void bta_hd_cback(BD_ADDR bd_addr, uint8_t event, uint32_t data, BT_HDR *pdata) +{ + tBTA_HD_CBACK_DATA *p_buf = NULL; + uint16_t sm_event = BTA_HD_INVALID_EVT; + + APPL_TRACE_API("%s: event=%d", __func__, event); + + switch (event) { + case HID_DHOST_EVT_OPEN: + sm_event = BTA_HD_INT_OPEN_EVT; + break; + + case HID_DHOST_EVT_CLOSE: + sm_event = BTA_HD_INT_CLOSE_EVT; + break; + + case HID_DHOST_EVT_GET_REPORT: + sm_event = BTA_HD_INT_GET_REPORT_EVT; + break; + + case HID_DHOST_EVT_SET_REPORT: + sm_event = BTA_HD_INT_SET_REPORT_EVT; + break; + + case HID_DHOST_EVT_SET_PROTOCOL: + sm_event = BTA_HD_INT_SET_PROTOCOL_EVT; + break; + + case HID_DHOST_EVT_INTR_DATA: + sm_event = BTA_HD_INT_INTR_DATA_EVT; + break; + + case HID_DHOST_EVT_VC_UNPLUG: + sm_event = BTA_HD_INT_VC_UNPLUG_EVT; + break; + + case HID_DHOST_EVT_SUSPEND: + sm_event = BTA_HD_INT_SUSPEND_EVT; + break; + + case HID_DHOST_EVT_EXIT_SUSPEND: + sm_event = BTA_HD_INT_EXIT_SUSPEND_EVT; + break; + } + + if (sm_event != BTA_HD_INVALID_EVT && + (p_buf = (tBTA_HD_CBACK_DATA *)osi_malloc(sizeof(tBTA_HD_CBACK_DATA) + sizeof(BT_HDR))) != NULL) { + p_buf->hdr.event = sm_event; + bdcpy(p_buf->addr, bd_addr); + p_buf->data = data; + p_buf->p_data = pdata; + + bta_sys_sendmsg(p_buf); + } +} +#endif /* BTA_HD_INCLUDED */ diff --git a/lib/bt/host/bluedroid/bta/hd/bta_hd_api.c b/lib/bt/host/bluedroid/bta/hd/bta_hd_api.c new file mode 100644 index 00000000..33875b95 --- /dev/null +++ b/lib/bt/host/bluedroid/bta/hd/bta_hd_api.c @@ -0,0 +1,287 @@ +/****************************************************************************** + * + * Copyright (C) 2016 The Android Open Source Project + * Copyright (C) 2005-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ +/****************************************************************************** + * + * This file contains the HID DEVICE API in the subsystem of BTA. + * + ******************************************************************************/ +#include "common/bt_target.h" + +#if defined(BTA_HD_INCLUDED) && (BTA_HD_INCLUDED == TRUE) + +#include "bta/bta_hd_api.h" +#include "bta_hd_int.h" +#include "osi/allocator.h" +#include +#include +#include + +/***************************************************************************** + * Constants + ****************************************************************************/ +static const tBTA_SYS_REG bta_hd_reg = {bta_hd_hdl_event, BTA_HdDisable}; +/******************************************************************************* + * + * Function BTA_HdEnable + * + * Description Enables HID device + * + * Returns void + * + ******************************************************************************/ +void BTA_HdEnable(tBTA_HD_CBACK *p_cback) +{ + tBTA_HD_API_ENABLE *p_buf; + APPL_TRACE_API("%s", __func__); + bta_sys_register(BTA_ID_HD, &bta_hd_reg); + p_buf = (tBTA_HD_API_ENABLE *)osi_malloc((uint16_t)sizeof(tBTA_HD_API_ENABLE)); + if (p_buf != NULL) { + memset(p_buf, 0, sizeof(tBTA_HD_API_ENABLE)); + p_buf->hdr.event = BTA_HD_API_ENABLE_EVT; + p_buf->p_cback = p_cback; + bta_sys_sendmsg(p_buf); + } +} +/******************************************************************************* + * + * Function BTA_HdDisable + * + * Description Disables HID device. + * + * Returns void + * + ******************************************************************************/ +void BTA_HdDisable(void) +{ + BT_HDR *p_buf; + APPL_TRACE_API("%s", __func__); + bta_sys_deregister(BTA_ID_HD); + if ((p_buf = (BT_HDR *)osi_malloc(sizeof(BT_HDR))) != NULL) { + p_buf->event = BTA_HD_API_DISABLE_EVT; + bta_sys_sendmsg(p_buf); + } +} +/******************************************************************************* + * + * Function BTA_HdRegisterApp + * + * Description This function is called when application should be + *registered + * + * Returns void + * + ******************************************************************************/ +extern void BTA_HdRegisterApp(tBTA_HD_APP_INFO *p_app_info, tBTA_HD_QOS_INFO *p_in_qos, tBTA_HD_QOS_INFO *p_out_qos) +{ + tBTA_HD_REGISTER_APP *p_buf; + APPL_TRACE_API("%s", __func__); + if ((p_buf = (tBTA_HD_REGISTER_APP *)osi_malloc(sizeof(tBTA_HD_REGISTER_APP))) != NULL) { + p_buf->hdr.event = BTA_HD_API_REGISTER_APP_EVT; + if (p_app_info->p_name) { + strncpy(p_buf->name, p_app_info->p_name, BTA_HD_APP_NAME_LEN); + p_buf->name[BTA_HD_APP_NAME_LEN] = '\0'; + } else { + p_buf->name[0] = '\0'; + } + if (p_app_info->p_description) { + strncpy(p_buf->description, p_app_info->p_description, BTA_HD_APP_DESCRIPTION_LEN); + p_buf->description[BTA_HD_APP_DESCRIPTION_LEN] = '\0'; + } else { + p_buf->description[0] = '\0'; + } + if (p_app_info->p_provider) { + strncpy(p_buf->provider, p_app_info->p_provider, BTA_HD_APP_PROVIDER_LEN); + p_buf->provider[BTA_HD_APP_PROVIDER_LEN] = '\0'; + } else { + p_buf->provider[0] = '\0'; + } + p_buf->subclass = p_app_info->subclass; + p_buf->d_len = p_app_info->descriptor.dl_len; + memcpy(p_buf->d_data, p_app_info->descriptor.dsc_list, p_app_info->descriptor.dl_len); + // copy qos data as-is + memcpy(&p_buf->in_qos, p_in_qos, sizeof(tBTA_HD_QOS_INFO)); + memcpy(&p_buf->out_qos, p_out_qos, sizeof(tBTA_HD_QOS_INFO)); + bta_sys_sendmsg(p_buf); + } +} +/******************************************************************************* + * + * Function BTA_HdUnregisterApp + * + * Description This function is called when application should be + *unregistered + * + * Returns void + * + ******************************************************************************/ +extern void BTA_HdUnregisterApp(void) +{ + BT_HDR *p_buf; + APPL_TRACE_API("%s", __func__); + if ((p_buf = (BT_HDR *)osi_malloc(sizeof(BT_HDR))) != NULL) { + p_buf->event = BTA_HD_API_UNREGISTER_APP_EVT; + bta_sys_sendmsg(p_buf); + } +} +/******************************************************************************* + * + * Function BTA_HdSendReport + * + * Description This function is called when report is to be sent + * + * Returns void + * + ******************************************************************************/ +extern void BTA_HdSendReport(tBTA_HD_REPORT *p_report) +{ + tBTA_HD_SEND_REPORT *p_buf; + APPL_TRACE_VERBOSE("%s", __func__); + if (p_report->len > BTA_HD_REPORT_LEN) { + APPL_TRACE_WARNING("%s, report len (%d) > MTU len (%d), can't send report." + " Increase value of HID_DEV_MTU_SIZE to send larger reports", + __func__, p_report->len, BTA_HD_REPORT_LEN); + return; + } + if ((p_buf = (tBTA_HD_SEND_REPORT *)osi_malloc(sizeof(tBTA_HD_SEND_REPORT))) != NULL) { + p_buf->hdr.event = BTA_HD_API_SEND_REPORT_EVT; + p_buf->use_intr = p_report->use_intr; + p_buf->type = p_report->type; + p_buf->id = p_report->id; + p_buf->len = p_report->len; + memcpy(p_buf->data, p_report->p_data, p_report->len); + bta_sys_sendmsg(p_buf); + } +} +/******************************************************************************* + * + * Function BTA_HdVirtualCableUnplug + * + * Description This function is called when VCU shall be sent + * + * Returns void + * + ******************************************************************************/ +extern void BTA_HdVirtualCableUnplug(void) +{ + BT_HDR *p_buf; + APPL_TRACE_API("%s", __func__); + if ((p_buf = (BT_HDR *)osi_malloc(sizeof(BT_HDR))) != NULL) { + p_buf->event = BTA_HD_API_VC_UNPLUG_EVT; + bta_sys_sendmsg(p_buf); + } +} + +/******************************************************************************* + * + * Function BTA_HdConnect + * + * Description This function is called when connection to host shall be + * made + * + * Returns void + * + ******************************************************************************/ +extern void BTA_HdConnect(BD_ADDR addr) +{ + tBTA_HD_DEVICE_CTRL *p_buf; + APPL_TRACE_API("%s", __func__); + + if ((p_buf = (tBTA_HD_DEVICE_CTRL *)osi_malloc(sizeof(tBTA_HD_DEVICE_CTRL))) != NULL) { + p_buf->hdr.event = BTA_HD_API_CONNECT_EVT; + memcpy(p_buf->addr, addr, sizeof(BD_ADDR)); + bta_sys_sendmsg(p_buf); + } +} + +/******************************************************************************* + * + * Function BTA_HdDisconnect + * + * Description This function is called when host shall be disconnected + * + * Returns void + * + ******************************************************************************/ +extern void BTA_HdDisconnect(void) +{ + BT_HDR *p_buf; + APPL_TRACE_API("%s", __func__); + if ((p_buf = (BT_HDR *)osi_malloc(sizeof(BT_HDR))) != NULL) { + p_buf->event = BTA_HD_API_DISCONNECT_EVT; + bta_sys_sendmsg(p_buf); + } +} +/******************************************************************************* + * + * Function BTA_HdAddDevice + * + * Description This function is called when a device is virtually cabled + * + * Returns void + * + ******************************************************************************/ +extern void BTA_HdAddDevice(BD_ADDR addr) +{ + tBTA_HD_DEVICE_CTRL *p_buf; + APPL_TRACE_API("%s", __func__); + if ((p_buf = (tBTA_HD_DEVICE_CTRL *)osi_malloc(sizeof(tBTA_HD_DEVICE_CTRL))) != NULL) { + p_buf->hdr.event = BTA_HD_API_ADD_DEVICE_EVT; + memcpy(p_buf->addr, addr, sizeof(BD_ADDR)); + bta_sys_sendmsg(p_buf); + } +} +/******************************************************************************* + * + * Function BTA_HdRemoveDevice + * + * Description This function is called when a device is virtually uncabled + * + * Returns void + * + ******************************************************************************/ +extern void BTA_HdRemoveDevice(BD_ADDR addr) +{ + tBTA_HD_DEVICE_CTRL *p_buf; + APPL_TRACE_API("%s", __func__); + if ((p_buf = (tBTA_HD_DEVICE_CTRL *)osi_malloc(sizeof(tBTA_HD_DEVICE_CTRL))) != NULL) { + p_buf->hdr.event = BTA_HD_API_REMOVE_DEVICE_EVT; + memcpy(p_buf->addr, addr, sizeof(BD_ADDR)); + bta_sys_sendmsg(p_buf); + } +} +/******************************************************************************* + * + * Function BTA_HdReportError + * + * Description This function is called when reporting error for set report + * + * Returns void + * + ******************************************************************************/ +extern void BTA_HdReportError(uint8_t error) +{ + tBTA_HD_REPORT_ERR *p_buf; + APPL_TRACE_API("%s", __func__); + if ((p_buf = (tBTA_HD_REPORT_ERR *)osi_malloc(sizeof(tBTA_HD_REPORT_ERR))) != NULL) { + p_buf->hdr.event = BTA_HD_API_REPORT_ERROR_EVT; + p_buf->error = error; + bta_sys_sendmsg(p_buf); + } +} +#endif /* BTA_HD_INCLUDED */ diff --git a/lib/bt/host/bluedroid/bta/hd/bta_hd_main.c b/lib/bt/host/bluedroid/bta/hd/bta_hd_main.c new file mode 100644 index 00000000..a6a3b634 --- /dev/null +++ b/lib/bt/host/bluedroid/bta/hd/bta_hd_main.c @@ -0,0 +1,337 @@ +/****************************************************************************** + * + * Copyright (C) 2016 The Android Open Source Project + * Copyright (C) 2005-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ +/****************************************************************************** + * + * This file contains the HID host main functions and state machine. + * + ******************************************************************************/ +#include "common/bt_target.h" + +#if defined(BTA_HD_INCLUDED) && (BTA_HD_INCLUDED == TRUE) + +#include "bta/bta_hd_api.h" +#include "bta_hd_int.h" +#include + +/***************************************************************************** + * Constants and types + ****************************************************************************/ +/* state machine states */ +enum { + BTA_HD_INIT_ST, + BTA_HD_IDLE_ST, /* not connected, waiting for connection */ + BTA_HD_CONN_ST, /* host connected */ + BTA_HD_TRANSIENT_TO_INIT_ST, /* transient state: going back from CONN to INIT */ +}; +typedef uint8_t tBTA_HD_STATE; + +/* state machine actions */ +enum { + BTA_HD_REGISTER_ACT, + BTA_HD_UNREGISTER_ACT, + BTA_HD_UNREGISTER2_ACT, + BTA_HD_CONNECT_ACT, + BTA_HD_DISCONNECT_ACT, + BTA_HD_ADD_DEVICE_ACT, + BTA_HD_REMOVE_DEVICE_ACT, + BTA_HD_SEND_REPORT_ACT, + BTA_HD_REPORT_ERROR_ACT, + BTA_HD_VC_UNPLUG_ACT, + BTA_HD_OPEN_ACT, + BTA_HD_CLOSE_ACT, + BTA_HD_INTR_DATA_ACT, + BTA_HD_GET_REPORT_ACT, + BTA_HD_SET_REPORT_ACT, + BTA_HD_SET_PROTOCOL_ACT, + BTA_HD_VC_UNPLUG_DONE_ACT, + BTA_HD_SUSPEND_ACT, + BTA_HD_EXIT_SUSPEND_ACT, + BTA_HD_NUM_ACTIONS +}; + +#define BTA_HD_IGNORE BTA_HD_NUM_ACTIONS + +typedef void (*tBTA_HD_ACTION)(tBTA_HD_DATA *p_data); +/* action functions */ +const tBTA_HD_ACTION bta_hd_action[] = { + bta_hd_register_act, bta_hd_unregister_act, bta_hd_unregister2_act, bta_hd_connect_act, + bta_hd_disconnect_act, bta_hd_add_device_act, bta_hd_remove_device_act, bta_hd_send_report_act, + bta_hd_report_error_act, bta_hd_vc_unplug_act, bta_hd_open_act, bta_hd_close_act, + bta_hd_intr_data_act, bta_hd_get_report_act, bta_hd_set_report_act, bta_hd_set_protocol_act, + bta_hd_vc_unplug_done_act, bta_hd_suspend_act, bta_hd_exit_suspend_act, +}; + +/* state table information */ +#define BTA_HD_ACTION 0 /* position of action */ +#define BTA_HD_NEXT_STATE 1 /* position of next state */ +#define BTA_HD_NUM_COLS 2 /* number of columns */ + +const uint8_t bta_hd_st_init[][BTA_HD_NUM_COLS] = { + /* Event Action Next state + */ + /* BTA_HD_API_REGISTER_APP_EVT */ {BTA_HD_REGISTER_ACT, BTA_HD_IDLE_ST}, + /* BTA_HD_API_UNREGISTER_APP_EVT */ {BTA_HD_IGNORE, BTA_HD_INIT_ST}, + /* BTA_HD_API_CONNECT_EVT */ {BTA_HD_IGNORE, BTA_HD_INIT_ST}, + /* BTA_HD_API_DISCONNECT_EVT */ {BTA_HD_IGNORE, BTA_HD_INIT_ST}, + /* BTA_HD_API_ADD_DEVICE_EVT */ {BTA_HD_ADD_DEVICE_ACT, BTA_HD_INIT_ST}, + /* BTA_HD_API_REMOVE_DEVICE_EVT */ {BTA_HD_REMOVE_DEVICE_ACT, BTA_HD_INIT_ST}, + /* BTA_HD_API_SEND_REPORT_EVT */ {BTA_HD_IGNORE, BTA_HD_INIT_ST}, + /* BTA_HD_API_REPORT_ERROR_EVT */ {BTA_HD_IGNORE, BTA_HD_INIT_ST}, + /* BTA_HD_API_VC_UNPLUG_EVT */ {BTA_HD_IGNORE, BTA_HD_INIT_ST}, + /* BTA_HD_INT_OPEN_EVT */ {BTA_HD_IGNORE, BTA_HD_INIT_ST}, + /* BTA_HD_INT_CLOSE_EVT */ {BTA_HD_IGNORE, BTA_HD_INIT_ST}, + /* BTA_HD_INT_INTR_DATA_EVT */ {BTA_HD_IGNORE, BTA_HD_INIT_ST}, + /* BTA_HD_INT_GET_REPORT_EVT */ {BTA_HD_IGNORE, BTA_HD_INIT_ST}, + /* BTA_HD_INT_SET_REPORT_EVT */ {BTA_HD_IGNORE, BTA_HD_INIT_ST}, + /* BTA_HD_INT_SET_PROTOCOL_EVT */ {BTA_HD_IGNORE, BTA_HD_INIT_ST}, + /* BTA_HD_INT_VC_UNPLUG_EVT */ {BTA_HD_IGNORE, BTA_HD_INIT_ST}, + /* BTA_HD_INT_SUSPEND_EVT */ {BTA_HD_IGNORE, BTA_HD_INIT_ST}, + /* BTA_HD_INT_EXIT_SUSPEND_EVT */ {BTA_HD_IGNORE, BTA_HD_INIT_ST}, +}; + +const uint8_t bta_hd_st_idle[][BTA_HD_NUM_COLS] = { + /* Event Action Next state + */ + /* BTA_HD_API_REGISTER_APP_EVT */ {BTA_HD_IGNORE, BTA_HD_IDLE_ST}, + /* BTA_HD_API_UNREGISTER_APP_EVT */ {BTA_HD_UNREGISTER_ACT, BTA_HD_INIT_ST}, + /* BTA_HD_API_CONNECT_EVT */ {BTA_HD_CONNECT_ACT, BTA_HD_IDLE_ST}, + /* BTA_HD_API_DISCONNECT_EVT */ {BTA_HD_IGNORE, BTA_HD_IDLE_ST}, + /* BTA_HD_API_ADD_DEVICE_EVT */ {BTA_HD_ADD_DEVICE_ACT, BTA_HD_IDLE_ST}, + /* BTA_HD_API_REMOVE_DEVICE_EVT */ {BTA_HD_REMOVE_DEVICE_ACT, BTA_HD_IDLE_ST}, + /* BTA_HD_API_SEND_REPORT_EVT */ {BTA_HD_SEND_REPORT_ACT, BTA_HD_IDLE_ST}, + /* BTA_HD_API_REPORT_ERROR_EVT */ {BTA_HD_IGNORE, BTA_HD_IDLE_ST}, + /* BTA_HD_API_VC_UNPLUG_EVT */ {BTA_HD_VC_UNPLUG_ACT, BTA_HD_IDLE_ST}, + /* BTA_HD_INT_OPEN_EVT */ {BTA_HD_OPEN_ACT, BTA_HD_CONN_ST}, + /* BTA_HD_INT_CLOSE_EVT */ {BTA_HD_IGNORE, BTA_HD_IDLE_ST}, + /* BTA_HD_INT_INTR_DATA_EVT */ {BTA_HD_IGNORE, BTA_HD_IDLE_ST}, + /* BTA_HD_INT_GET_REPORT_EVT */ {BTA_HD_IGNORE, BTA_HD_IDLE_ST}, + /* BTA_HD_INT_SET_REPORT_EVT */ {BTA_HD_IGNORE, BTA_HD_IDLE_ST}, + /* BTA_HD_INT_SET_PROTOCOL_EVT */ {BTA_HD_IGNORE, BTA_HD_IDLE_ST}, + /* BTA_HD_INT_VC_UNPLUG_EVT */ {BTA_HD_IGNORE, BTA_HD_IDLE_ST}, + /* BTA_HD_INT_SUSPEND_EVT */ {BTA_HD_IGNORE, BTA_HD_IDLE_ST}, + /* BTA_HD_INT_EXIT_SUSPEND_EVT */ {BTA_HD_IGNORE, BTA_HD_IDLE_ST}, +}; + +const uint8_t bta_hd_st_conn[][BTA_HD_NUM_COLS] = { + /* Event Action Next state */ + /* BTA_HD_API_REGISTER_APP_EVT */ {BTA_HD_IGNORE, BTA_HD_CONN_ST}, + /* BTA_HD_API_UNREGISTER_APP_EVT */ {BTA_HD_DISCONNECT_ACT, BTA_HD_TRANSIENT_TO_INIT_ST}, + /* BTA_HD_API_CONNECT_EVT */ {BTA_HD_IGNORE, BTA_HD_CONN_ST}, + /* BTA_HD_API_DISCONNECT_EVT */ {BTA_HD_DISCONNECT_ACT, BTA_HD_CONN_ST}, + /* BTA_HD_API_ADD_DEVICE_EVT */ {BTA_HD_ADD_DEVICE_ACT, BTA_HD_CONN_ST}, + /* BTA_HD_API_REMOVE_DEVICE_EVT */ {BTA_HD_REMOVE_DEVICE_ACT, BTA_HD_CONN_ST}, + /* BTA_HD_API_SEND_REPORT_EVT */ {BTA_HD_SEND_REPORT_ACT, BTA_HD_CONN_ST}, + /* BTA_HD_API_REPORT_ERROR_EVT */ {BTA_HD_REPORT_ERROR_ACT, BTA_HD_CONN_ST}, + /* BTA_HD_API_VC_UNPLUG_EVT */ {BTA_HD_VC_UNPLUG_ACT, BTA_HD_CONN_ST}, + /* BTA_HD_INT_OPEN_EVT */ {BTA_HD_IGNORE, BTA_HD_CONN_ST}, + /* BTA_HD_INT_CLOSE_EVT */ {BTA_HD_CLOSE_ACT, BTA_HD_IDLE_ST}, + /* BTA_HD_INT_INTR_DATA_EVT */ {BTA_HD_INTR_DATA_ACT, BTA_HD_CONN_ST}, + /* BTA_HD_INT_GET_REPORT_EVT */ {BTA_HD_GET_REPORT_ACT, BTA_HD_CONN_ST}, + /* BTA_HD_INT_SET_REPORT_EVT */ {BTA_HD_SET_REPORT_ACT, BTA_HD_CONN_ST}, + /* BTA_HD_INT_SET_PROTOCOL_EVT */ {BTA_HD_SET_PROTOCOL_ACT, BTA_HD_CONN_ST}, + /* BTA_HD_INT_VC_UNPLUG_EVT */ {BTA_HD_VC_UNPLUG_DONE_ACT, BTA_HD_IDLE_ST}, + /* BTA_HD_INT_SUSPEND_EVT */ {BTA_HD_SUSPEND_ACT, BTA_HD_CONN_ST}, + /* BTA_HD_INT_EXIT_SUSPEND_EVT */ {BTA_HD_EXIT_SUSPEND_ACT, BTA_HD_CONN_ST}, +}; + +const uint8_t bta_hd_st_transient_to_init[][BTA_HD_NUM_COLS] = { + /* Event Action Next state */ + /* BTA_HD_API_REGISTER_APP_EVT */ {BTA_HD_IGNORE, BTA_HD_TRANSIENT_TO_INIT_ST}, + /* BTA_HD_API_UNREGISTER_APP_EVT */ {BTA_HD_IGNORE, BTA_HD_TRANSIENT_TO_INIT_ST}, + /* BTA_HD_API_CONNECT_EVT */ {BTA_HD_IGNORE, BTA_HD_TRANSIENT_TO_INIT_ST}, + /* BTA_HD_API_DISCONNECT_EVT */ {BTA_HD_IGNORE, BTA_HD_TRANSIENT_TO_INIT_ST}, + /* BTA_HD_API_ADD_DEVICE_EVT */ {BTA_HD_IGNORE, BTA_HD_TRANSIENT_TO_INIT_ST}, + /* BTA_HD_API_REMOVE_DEVICE_EVT */ {BTA_HD_IGNORE, BTA_HD_TRANSIENT_TO_INIT_ST}, + /* BTA_HD_API_SEND_REPORT_EVT */ {BTA_HD_IGNORE, BTA_HD_TRANSIENT_TO_INIT_ST}, + /* BTA_HD_API_REPORT_ERROR_EVT */ {BTA_HD_IGNORE, BTA_HD_TRANSIENT_TO_INIT_ST}, + /* BTA_HD_API_VC_UNPLUG_EVT */ {BTA_HD_IGNORE, BTA_HD_TRANSIENT_TO_INIT_ST}, + /* BTA_HD_INT_OPEN_EVT */ {BTA_HD_IGNORE, BTA_HD_TRANSIENT_TO_INIT_ST}, + /* BTA_HD_INT_CLOSE_EVT */ {BTA_HD_UNREGISTER2_ACT, BTA_HD_INIT_ST}, + /* BTA_HD_INT_INTR_DATA_EVT */ {BTA_HD_IGNORE, BTA_HD_TRANSIENT_TO_INIT_ST}, + /* BTA_HD_INT_GET_REPORT_EVT */ {BTA_HD_IGNORE, BTA_HD_TRANSIENT_TO_INIT_ST}, + /* BTA_HD_INT_SET_REPORT_EVT */ {BTA_HD_IGNORE, BTA_HD_TRANSIENT_TO_INIT_ST}, + /* BTA_HD_INT_SET_PROTOCOL_EVT */ {BTA_HD_IGNORE, BTA_HD_TRANSIENT_TO_INIT_ST}, + /* BTA_HD_INT_VC_UNPLUG_EVT */ {BTA_HD_UNREGISTER2_ACT, BTA_HD_INIT_ST}, + /* BTA_HD_INT_SUSPEND_EVT */ {BTA_HD_IGNORE, BTA_HD_TRANSIENT_TO_INIT_ST}, + /* BTA_HD_INT_EXIT_SUSPEND_EVT */ {BTA_HD_IGNORE, BTA_HD_TRANSIENT_TO_INIT_ST}, +}; + +/* type for state table */ +typedef const uint8_t (*tBTA_HD_ST_TBL)[BTA_HD_NUM_COLS]; +/* state table */ +const tBTA_HD_ST_TBL bta_hd_st_tbl[] = {bta_hd_st_init, bta_hd_st_idle, bta_hd_st_conn, bta_hd_st_transient_to_init}; + +/***************************************************************************** + * Global data + ****************************************************************************/ +#if BTA_DYNAMIC_MEMORY == FALSE +tBTA_HD_CB bta_hd_cb; +#else +tBTA_HD_CB *bta_hd_cb_ptr; +#endif + +static const char *bta_hd_evt_code(tBTA_HD_INT_EVT evt_code); +static const char *bta_hd_state_code(tBTA_HD_STATE state_code); + +/******************************************************************************* + * + * Function bta_hd_sm_execute + * + * Description State machine event handling function for HID Device + * + * Returns void + * + ******************************************************************************/ +void bta_hd_sm_execute(uint16_t event, tBTA_HD_DATA *p_data) +{ + tBTA_HD_ST_TBL state_table; + tBTA_HD_STATE prev_state; + uint8_t action; + tBTA_HD cback_data; + + APPL_TRACE_EVENT("%s: state=%s (%d) event=%s (%d)", __func__, bta_hd_state_code(bta_hd_cb.state), bta_hd_cb.state, + bta_hd_evt_code(event), event); + + prev_state = bta_hd_cb.state; + memset(&cback_data, 0, sizeof(tBTA_HD)); + state_table = bta_hd_st_tbl[bta_hd_cb.state]; + event &= 0xff; + + if ((action = state_table[event][BTA_HD_ACTION]) < BTA_HD_IGNORE) { + (*bta_hd_action[action])(p_data); + } + + bta_hd_cb.state = state_table[event][BTA_HD_NEXT_STATE]; + + if (bta_hd_cb.state != prev_state) { + APPL_TRACE_EVENT("%s: [new] state=%s (%d)", __func__, bta_hd_state_code(bta_hd_cb.state), bta_hd_cb.state); + } + return; +} + +/******************************************************************************* + * + * Function bta_hd_hdl_event + * + * Description HID device main event handling function. + * + * Returns void + * + ******************************************************************************/ +bool bta_hd_hdl_event(BT_HDR *p_msg) +{ + APPL_TRACE_API("%s: p_msg->event=%d", __func__, p_msg->event); + + switch (p_msg->event) { + case BTA_HD_API_ENABLE_EVT: + bta_hd_api_enable((tBTA_HD_DATA *)p_msg); + break; + case BTA_HD_API_DISABLE_EVT: + if (bta_hd_cb.state == BTA_HD_CONN_ST) { + APPL_TRACE_WARNING("%s: host connected, disconnect before disabling", __func__); + // unregister (and disconnect) + bta_hd_cb.disable_w4_close = TRUE; + bta_hd_sm_execute(BTA_HD_API_UNREGISTER_APP_EVT, (tBTA_HD_DATA *)p_msg); + } else { + bta_hd_api_disable(); + } + break; + default: + bta_hd_sm_execute(p_msg->event, (tBTA_HD_DATA *)p_msg); + } + return (TRUE); +} + +static const char *bta_hd_evt_code(tBTA_HD_INT_EVT evt_code) +{ + switch (evt_code) { + case BTA_HD_API_REGISTER_APP_EVT: + return "BTA_HD_API_REGISTER_APP_EVT"; + case BTA_HD_API_UNREGISTER_APP_EVT: + return "BTA_HD_API_UNREGISTER_APP_EVT"; + case BTA_HD_API_CONNECT_EVT: + return "BTA_HD_API_CONNECT_EVT"; + case BTA_HD_API_DISCONNECT_EVT: + return "BTA_HD_API_DISCONNECT_EVT"; + case BTA_HD_API_ADD_DEVICE_EVT: + return "BTA_HD_API_ADD_DEVICE_EVT"; + case BTA_HD_API_REMOVE_DEVICE_EVT: + return "BTA_HD_API_REMOVE_DEVICE_EVT"; + case BTA_HD_API_SEND_REPORT_EVT: + return "BTA_HD_API_SEND_REPORT_EVT"; + case BTA_HD_API_REPORT_ERROR_EVT: + return "BTA_HD_API_REPORT_ERROR_EVT"; + case BTA_HD_API_VC_UNPLUG_EVT: + return "BTA_HD_API_VC_UNPLUG_EVT"; + case BTA_HD_INT_OPEN_EVT: + return "BTA_HD_INT_OPEN_EVT"; + case BTA_HD_INT_CLOSE_EVT: + return "BTA_HD_INT_CLOSE_EVT"; + case BTA_HD_INT_INTR_DATA_EVT: + return "BTA_HD_INT_INTR_DATA_EVT"; + case BTA_HD_INT_GET_REPORT_EVT: + return "BTA_HD_INT_GET_REPORT_EVT"; + case BTA_HD_INT_SET_REPORT_EVT: + return "BTA_HD_INT_SET_REPORT_EVT"; + case BTA_HD_INT_SET_PROTOCOL_EVT: + return "BTA_HD_INT_SET_PROTOCOL_EVT"; + case BTA_HD_INT_VC_UNPLUG_EVT: + return "BTA_HD_INT_VC_UNPLUG_EVT"; + case BTA_HD_INT_SUSPEND_EVT: + return "BTA_HD_INT_SUSPEND_EVT"; + case BTA_HD_INT_EXIT_SUSPEND_EVT: + return "BTA_HD_INT_EXIT_SUSPEND_EVT"; + default: + return ""; + } +} + +static const char *bta_hd_state_code(tBTA_HD_STATE state_code) +{ + switch (state_code) { + case BTA_HD_INIT_ST: + return "BTA_HD_INIT_ST"; + case BTA_HD_IDLE_ST: + return "BTA_HD_IDLE_ST"; + case BTA_HD_CONN_ST: + return "BTA_HD_CONN_ST"; + case BTA_HD_TRANSIENT_TO_INIT_ST: + return "BTA_HD_TRANSIENT_TO_INIT_ST"; + default: + return ""; + } +} + +#if BT_HID_DEVICE_BQB_INCLUDED +tBTA_STATUS bta_hd_bqb_set_local_di_record(void) +{ + tBTA_STATUS status = BTA_FAILURE; + + tBTA_DI_RECORD bqb_device_info; + bqb_device_info.vendor = 0; + bqb_device_info.vendor_id_source = 0xff; // BTA_HH_VENDOR_ID_INVALID + bqb_device_info.product = 1; + bqb_device_info.version = 0; + bqb_device_info.primary_record = TRUE; + + return BTA_DmSetLocalDiRecord(&bqb_device_info, &bta_hd_cb.sdp_handle); +} +#endif /* BT_HID_DEVICE_BQB_INCLUDED */ + +#endif /* BTA_HD_INCLUDED */ diff --git a/lib/bt/host/bluedroid/bta/hd/include/bta_hd_int.h b/lib/bt/host/bluedroid/bta/hd/include/bta_hd_int.h new file mode 100644 index 00000000..7a515970 --- /dev/null +++ b/lib/bt/host/bluedroid/bta/hd/include/bta_hd_int.h @@ -0,0 +1,168 @@ +/****************************************************************************** + * + * Copyright (C) 2016 The Android Open Source Project + * Copyright (C) 2005-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ +/****************************************************************************** + * + * This file contains BTA HID Device internal definitions + * + ******************************************************************************/ +#ifndef BTA_HD_INT_H +#define BTA_HD_INT_H + +#include "bta/bta_hd_api.h" +#include "bta/bta_sys.h" +#include "stack/hiddefs.h" + +enum { + BTA_HD_API_REGISTER_APP_EVT = BTA_SYS_EVT_START(BTA_ID_HD), + BTA_HD_API_UNREGISTER_APP_EVT, + BTA_HD_API_CONNECT_EVT, + BTA_HD_API_DISCONNECT_EVT, + BTA_HD_API_ADD_DEVICE_EVT, + BTA_HD_API_REMOVE_DEVICE_EVT, + BTA_HD_API_SEND_REPORT_EVT, + BTA_HD_API_REPORT_ERROR_EVT, + BTA_HD_API_VC_UNPLUG_EVT, + BTA_HD_INT_OPEN_EVT, + BTA_HD_INT_CLOSE_EVT, + BTA_HD_INT_INTR_DATA_EVT, + BTA_HD_INT_GET_REPORT_EVT, + BTA_HD_INT_SET_REPORT_EVT, + BTA_HD_INT_SET_PROTOCOL_EVT, + BTA_HD_INT_VC_UNPLUG_EVT, + BTA_HD_INT_SUSPEND_EVT, + BTA_HD_INT_EXIT_SUSPEND_EVT, + /* handled outside state machine */ + BTA_HD_API_ENABLE_EVT, + BTA_HD_API_DISABLE_EVT +}; +typedef uint16_t tBTA_HD_INT_EVT; +#define BTA_HD_INVALID_EVT (BTA_HD_API_DISABLE_EVT + 1) +typedef struct { + BT_HDR hdr; + tBTA_HD_CBACK *p_cback; +} tBTA_HD_API_ENABLE; +#define BTA_HD_APP_NAME_LEN 50 +#define BTA_HD_APP_DESCRIPTION_LEN 50 +#define BTA_HD_APP_PROVIDER_LEN 50 +#define BTA_HD_APP_DESCRIPTOR_LEN 2048 +#define BTA_HD_STATE_DISABLED 0x00 +#define BTA_HD_STATE_ENABLED 0x01 +#define BTA_HD_STATE_IDLE 0x02 +#define BTA_HD_STATE_CONNECTED 0x03 +#define BTA_HD_STATE_DISABLING 0x04 +#define BTA_HD_STATE_REMOVING 0x05 +typedef struct { + BT_HDR hdr; + char name[BTA_HD_APP_NAME_LEN + 1]; + char description[BTA_HD_APP_DESCRIPTION_LEN + 1]; + char provider[BTA_HD_APP_PROVIDER_LEN + 1]; + uint8_t subclass; + uint16_t d_len; + uint8_t d_data[BTA_HD_APP_DESCRIPTOR_LEN]; + tBTA_HD_QOS_INFO in_qos; + tBTA_HD_QOS_INFO out_qos; +} tBTA_HD_REGISTER_APP; + +#define BTA_HD_REPORT_LEN HID_DEV_MTU_SIZE + +typedef struct { + BT_HDR hdr; + bool use_intr; + uint8_t type; + uint8_t id; + uint16_t len; + uint8_t data[BTA_HD_REPORT_LEN]; +} tBTA_HD_SEND_REPORT; + +typedef struct { + BT_HDR hdr; + BD_ADDR addr; +} tBTA_HD_DEVICE_CTRL; + +typedef struct { + BT_HDR hdr; + uint8_t error; +} tBTA_HD_REPORT_ERR; + +/* union of all event data types */ +typedef union { + BT_HDR hdr; + tBTA_HD_API_ENABLE api_enable; + tBTA_HD_REGISTER_APP register_app; + tBTA_HD_SEND_REPORT send_report; + tBTA_HD_DEVICE_CTRL device_ctrl; + tBTA_HD_REPORT_ERR report_err; +} tBTA_HD_DATA; + +typedef struct { + BT_HDR hdr; + BD_ADDR addr; + uint32_t data; + BT_HDR *p_data; +} tBTA_HD_CBACK_DATA; + +/****************************************************************************** + * Main Control Block + ******************************************************************************/ +typedef struct { + tBTA_HD_CBACK *p_cback; + uint32_t sdp_handle; + uint8_t trace_level; + uint8_t state; + BD_ADDR bd_addr; + bool use_report_id; + bool boot_mode; + bool vc_unplug; + bool disable_w4_close; +} tBTA_HD_CB; + +#if BTA_DYNAMIC_MEMORY == FALSE +extern tBTA_HD_CB bta_hd_cb; +#else +extern tBTA_HD_CB *bta_hd_cb_ptr; +#define bta_hd_cb (*bta_hd_cb_ptr) +#endif + +/***************************************************************************** + * Function prototypes + ****************************************************************************/ +extern bool bta_hd_hdl_event(BT_HDR *p_msg); +extern void bta_hd_api_enable(tBTA_HD_DATA *p_data); +extern void bta_hd_api_disable(void); +extern void bta_hd_register_act(tBTA_HD_DATA *p_data); +extern void bta_hd_unregister_act(tBTA_HD_DATA *p_data); +extern void bta_hd_unregister2_act(tBTA_HD_DATA *p_data); +extern void bta_hd_connect_act(tBTA_HD_DATA *p_data); +extern void bta_hd_disconnect_act(tBTA_HD_DATA *p_data); +extern void bta_hd_add_device_act(tBTA_HD_DATA *p_data); +extern void bta_hd_remove_device_act(tBTA_HD_DATA *p_data); +extern void bta_hd_send_report_act(tBTA_HD_DATA *p_data); +extern void bta_hd_report_error_act(tBTA_HD_DATA *p_data); +extern void bta_hd_vc_unplug_act(tBTA_HD_DATA *p_data); +extern void bta_hd_open_act(tBTA_HD_DATA *p_data); +extern void bta_hd_close_act(tBTA_HD_DATA *p_data); +extern void bta_hd_intr_data_act(tBTA_HD_DATA *p_data); +extern void bta_hd_get_report_act(tBTA_HD_DATA *p_data); +extern void bta_hd_set_report_act(tBTA_HD_DATA *p_data); +extern void bta_hd_set_protocol_act(tBTA_HD_DATA *p_data); +extern void bta_hd_vc_unplug_done_act(tBTA_HD_DATA *p_data); +extern void bta_hd_suspend_act(tBTA_HD_DATA *p_data); +extern void bta_hd_exit_suspend_act(tBTA_HD_DATA *p_data); + +#endif diff --git a/lib/bt/host/bluedroid/bta/hf_ag/bta_ag_act.c b/lib/bt/host/bluedroid/bta/hf_ag/bta_ag_act.c new file mode 100644 index 00000000..c2e27737 --- /dev/null +++ b/lib/bt/host/bluedroid/bta/hf_ag/bta_ag_act.c @@ -0,0 +1,816 @@ +/****************************************************************************** + * + * Copyright (C) 2003-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains action functions for the audio gateway. + * + ******************************************************************************/ +#include +#include "bta_ag_int.h" +#include "bta/bta_sys.h" +#include "bta/bta_ag_api.h" +#include "bta/bta_ag_co.h" +#include "stack/port_api.h" +#include "stack/l2c_api.h" +#include "bta_dm_int.h" +#include "bta/bta_sdp_api.h" +#include "bta/utl.h" +#include "common/bt_trace.h" +#include "osi/allocator.h" + +#if (BTA_AG_INCLUDED == TRUE) +/***************************************************************************** +** Constants +*****************************************************************************/ + +/* maximum length of data to read from RFCOMM */ +#define BTA_AG_RFC_READ_MAX 512 + +/* maximum AT command length */ +#define BTA_AG_CMD_MAX 512 + +const UINT16 bta_ag_uuid[BTA_AG_NUM_IDX] = +{ + UUID_SERVCLASS_HEADSET_AUDIO_GATEWAY, + UUID_SERVCLASS_AG_HANDSFREE +}; + +const UINT8 bta_ag_sec_id[BTA_AG_NUM_IDX] = +{ + BTM_SEC_SERVICE_HEADSET_AG, + BTM_SEC_SERVICE_AG_HANDSFREE +}; + +const tBTA_SERVICE_ID bta_ag_svc_id[BTA_AG_NUM_IDX] = +{ + BTA_HSP_SERVICE_ID, + BTA_HFP_SERVICE_ID +}; + +const tBTA_SERVICE_MASK bta_ag_svc_mask[BTA_AG_NUM_IDX] = +{ + BTA_HSP_SERVICE_MASK, + BTA_HFP_SERVICE_MASK +}; + +typedef void (*tBTA_AG_ATCMD_CBACK)(tBTA_AG_SCB *p_scb, UINT16 cmd, UINT8 arg_type, + char *p_arg, INT16 int_arg); + +const tBTA_AG_ATCMD_CBACK bta_ag_at_cback_tbl[BTA_AG_NUM_IDX] = +{ + bta_ag_at_hsp_cback, + bta_ag_at_hfp_cback +}; + +/******************************************************************************* +** +** Function bta_ag_cback_open +** +** Description Send open callback event to application. +** +** +** Returns void +** +*******************************************************************************/ +static void bta_ag_cback_open(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data, tBTA_AG_STATUS status) +{ + tBTA_AG_OPEN open; + /* call app callback with open event */ + open.hdr.handle = bta_ag_scb_to_idx(p_scb); + open.hdr.app_id = p_scb->app_id; + open.hdr.status = status; + open.service_id = bta_ag_svc_id[p_scb->conn_service]; + if (p_data) { + /* if p_data is provided then we need to pick the bd address from the open api structure */ + bdcpy(open.bd_addr, p_data->api_open.bd_addr); + } else { + bdcpy(open.bd_addr, p_scb->peer_addr); + } + (*bta_ag_cb.p_cback)(BTA_AG_OPEN_EVT, (tBTA_AG *) &open); +} + +/******************************************************************************* +** +** Function bta_ag_register +** +** Description This function initializes values of the AG cb and sets up +** the SDP record for the services. +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_register(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data) +{ + tBTA_AG_REGISTER reg; + /* initialize control block */ + p_scb->reg_services = p_data->api_register.services; + p_scb->serv_sec_mask = p_data->api_register.sec_mask; + p_scb->features = p_data->api_register.features; + p_scb->app_id = p_data->api_register.app_id; + /* create SDP records */ + bta_ag_create_records(p_scb, p_data); + /* start RFCOMM servers */ + bta_ag_start_servers(p_scb, p_scb->reg_services); + /* call app callback with register event */ + reg.hdr.handle = bta_ag_scb_to_idx(p_scb); + reg.hdr.app_id = p_scb->app_id; + reg.hdr.status = BTA_AG_SUCCESS; + (*bta_ag_cb.p_cback)(BTA_AG_REGISTER_EVT, (tBTA_AG *) ®); +} + +/******************************************************************************* +** +** Function bta_ag_deregister +** +** Description This function removes the sdp records, closes the RFCOMM +** servers, and deallocates the service control block. +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_deregister(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data) +{ + /* set dealloc */ + p_scb->dealloc = TRUE; + /* remove sdp records */ + bta_ag_del_records(p_scb, p_data); + /* remove rfcomm servers */ + bta_ag_close_servers(p_scb, p_scb->reg_services); + /* dealloc */ + bta_ag_scb_dealloc(p_scb); +} + +/******************************************************************************* +** +** Function bta_ag_start_dereg +** +** Description Start a deregister event. +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_start_dereg(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data) +{ + /* set dealloc */ + p_scb->dealloc = TRUE; + /* remove sdp records */ + bta_ag_del_records(p_scb, p_data); +} + +/******************************************************************************* +** +** Function bta_ag_start_open +** +** Description This starts an AG open. +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_start_open(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data) +{ + BD_ADDR pending_bd_addr; + /* store parameters */ + if (p_data) { + bdcpy(p_scb->peer_addr, p_data->api_open.bd_addr); + p_scb->open_services = p_data->api_open.services; + p_scb->cli_sec_mask = p_data->api_open.sec_mask; + } + /* Check if RFCOMM has any incoming connection to avoid collision. */ + if (PORT_IsOpening (pending_bd_addr)) { + /* Let the incoming connection goes through. */ + /* Issue collision for this scb for now. */ + /* We will decide what to do when we find incoming connetion later. */ + bta_ag_collision_cback (0, BTA_ID_AG, 0, p_scb->peer_addr); + return; + } + /* close servers */ + bta_ag_close_servers(p_scb, p_scb->reg_services); + /* set role */ + p_scb->role = BTA_AG_INT; + /* do service search */ + bta_ag_do_disc(p_scb, p_scb->open_services); +} + +/******************************************************************************* +** +** Function bta_ag_disc_int_res +** +** Description This function handles a discovery result when initiator. +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_disc_int_res(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data) +{ + UINT16 event = BTA_AG_DISC_FAIL_EVT; + APPL_TRACE_DEBUG ("bta_ag_disc_int_res: Status: %d", p_data->disc_result.status); + + /* if found service */ + if (p_data->disc_result.status == SDP_SUCCESS || + p_data->disc_result.status == SDP_DB_FULL) { + /* get attributes */ + if (bta_ag_sdp_find_attr(p_scb, p_scb->open_services)) { + /* set connected service */ + p_scb->conn_service = bta_ag_service_to_idx(p_scb->open_services); + /* send ourselves sdp ok event */ + event = BTA_AG_DISC_OK_EVT; + } + } + /* free discovery db */ + bta_ag_free_db(p_scb, p_data); + /* if service not found check if we should search for other service */ + if ((event == BTA_AG_DISC_FAIL_EVT) && + (p_data->disc_result.status == SDP_SUCCESS || + p_data->disc_result.status == SDP_DB_FULL || + p_data->disc_result.status == SDP_NO_RECS_MATCH)) { + if ((p_scb->open_services & BTA_HFP_SERVICE_MASK) && + (p_scb->open_services & BTA_HSP_SERVICE_MASK)) { + /* search for HSP */ + p_scb->open_services &= ~BTA_HFP_SERVICE_MASK; + bta_ag_do_disc(p_scb, p_scb->open_services); + } else if ((p_scb->open_services & BTA_HSP_SERVICE_MASK) && + (p_scb->hsp_version == HSP_VERSION_1_2)) { + /* search for UUID_SERVCLASS_HEADSET for HSP 1.0 device */ + p_scb->hsp_version = HSP_VERSION_1_0; + bta_ag_do_disc(p_scb, p_scb->open_services); + } else { + /* send ourselves sdp ok/fail event */ + bta_ag_sm_execute(p_scb, event, p_data); + } + } else { + /* send ourselves sdp ok/fail event */ + bta_ag_sm_execute(p_scb, event, p_data); + } +} + +/******************************************************************************* +** +** Function bta_ag_disc_acp_res +** +** Description This function handles a discovery result when acceptor. +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_disc_acp_res(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data) +{ + /* if found service */ + if (p_data->disc_result.status == SDP_SUCCESS || + p_data->disc_result.status == SDP_DB_FULL) { + /* get attributes */ + bta_ag_sdp_find_attr(p_scb, bta_ag_svc_mask[p_scb->conn_service]); + } + /* free discovery db */ + bta_ag_free_db(p_scb, p_data); +} + +/******************************************************************************* +** +** Function bta_ag_disc_fail +** +** Description This function handles a discovery failure. +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_disc_fail(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data) +{ + UNUSED(p_data); + /* reopen registered servers */ + bta_ag_start_servers(p_scb, p_scb->reg_services); + /* reinitialize stuff */ + /* call open cback w. failure */ + bta_ag_cback_open(p_scb, NULL, BTA_AG_FAIL_SDP); +} + +/******************************************************************************* +** +** Function bta_ag_open_fail +** +** Description open connection failed. +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_open_fail(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data) +{ + /* call open cback w. failure */ + bta_ag_cback_open(p_scb, p_data, BTA_AG_FAIL_RESOURCES); +} + +/******************************************************************************* +** +** Function bta_ag_rfc_fail +** +** Description RFCOMM connection failed. +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_rfc_fail(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data) +{ + UNUSED(p_data); + /* reinitialize stuff */ + p_scb->conn_handle = 0; + p_scb->conn_service = 0; + p_scb->peer_features = 0; +#if (BTM_WBS_INCLUDED == TRUE) + p_scb->peer_codecs = BTA_AG_CODEC_NONE; + p_scb->sco_codec = BTA_AG_CODEC_NONE; +#endif + p_scb->role = 0; + p_scb->svc_conn = FALSE; + p_scb->hsp_version = HSP_VERSION_1_2; + /*Clear the BD address*/ + bdcpy(p_scb->peer_addr, bd_addr_null); + /* reopen registered servers */ + bta_ag_start_servers(p_scb, p_scb->reg_services); + /* call open cback w. failure */ + bta_ag_cback_open(p_scb, NULL, BTA_AG_FAIL_RFCOMM); +} + +/******************************************************************************* +** +** Function bta_ag_rfc_close +** +** Description RFCOMM connection closed. +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_rfc_close(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data) +{ + tBTA_AG_CLOSE close; + tBTA_SERVICE_MASK services; + int i, num_active_conn = 0; + UNUSED(p_data); + /* reinitialize stuff */ + p_scb->conn_service = 0; + p_scb->peer_features = 0; +#if (BTM_WBS_INCLUDED == TRUE) + p_scb->peer_codecs = BTA_AG_CODEC_NONE; + p_scb->sco_codec = BTA_AG_CODEC_NONE; + /* Clear these flags upon SLC teardown */ + p_scb->codec_updated = FALSE; + p_scb->codec_fallback = FALSE; + p_scb->codec_msbc_settings = BTA_AG_SCO_MSBC_SETTINGS_T2; +#endif + p_scb->role = 0; + p_scb->post_sco = BTA_AG_POST_SCO_NONE; + p_scb->svc_conn = FALSE; + p_scb->hsp_version = HSP_VERSION_1_2; + bta_ag_at_reinit(&p_scb->at_cb); + /* stop timers */ + bta_sys_stop_timer(&p_scb->act_timer); +#if (BTM_WBS_INCLUDED == TRUE) + bta_sys_stop_timer(&p_scb->cn_timer); +#endif + close.hdr.handle = bta_ag_scb_to_idx(p_scb); + close.hdr.app_id = p_scb->app_id; + bdcpy(close.bd_addr, p_scb->peer_addr); + + bta_sys_conn_close(BTA_ID_AG, p_scb->app_id, p_scb->peer_addr); + + /* call close cback */ + (*bta_ag_cb.p_cback)(BTA_AG_CLOSE_EVT, (tBTA_AG *) &close); + + /* if not deregistering (deallocating) reopen registered servers */ + if (p_scb->dealloc == FALSE) { + /* Clear peer bd_addr so instance can be reused */ + bdcpy(p_scb->peer_addr, bd_addr_null); + + /* start only unopened server */ + services = p_scb->reg_services; + for (i = 0; i < BTA_AG_NUM_IDX && services != 0; i++) { + if (p_scb->serv_handle[i]) { + services &= ~((tBTA_SERVICE_MASK)1 << (BTA_HSP_SERVICE_ID + i)); + } + } + bta_ag_start_servers(p_scb, services); + p_scb->conn_handle = 0; + /* Make sure SCO state is BTA_AG_SCO_SHUTDOWN_ST */ + bta_ag_sco_shutdown(p_scb, NULL); + /* Check if all the SLCs are down */ + for (i = 0; i < BTA_AG_NUM_SCB; i++) { + if (bta_ag_cb.scb[i].in_use && bta_ag_cb.scb[i].svc_conn) { + num_active_conn++; + } + } + if (!num_active_conn) { + bta_sys_sco_unuse(BTA_ID_AG, p_scb->app_id, p_scb->peer_addr); + } + } else { + /* else close port and deallocate scb */ + RFCOMM_RemoveServer(p_scb->conn_handle); + bta_ag_scb_dealloc(p_scb); + } +} + +/******************************************************************************* +** +** Function bta_ag_rfc_open +** +** Description Handle RFCOMM channel open. +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_rfc_open(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data) +{ + /* initialize AT feature variables */ + p_scb->clip_enabled = FALSE; + p_scb->ccwa_enabled = FALSE; + p_scb->cmer_enabled = FALSE; + p_scb->cmee_enabled = FALSE; + p_scb->inband_enabled = ((p_scb->features & BTA_AG_FEAT_INBAND) == BTA_AG_FEAT_INBAND); + + /* set up AT command interpreter */ + p_scb->at_cb.p_at_tbl = (tBTA_AG_AT_CMD *) bta_ag_at_tbl[p_scb->conn_service]; + p_scb->at_cb.p_cmd_cback = (tBTA_AG_AT_CMD_CBACK *) bta_ag_at_cback_tbl[p_scb->conn_service]; + p_scb->at_cb.p_err_cback = (tBTA_AG_AT_ERR_CBACK *) bta_ag_at_err_cback; + p_scb->at_cb.p_user = p_scb; + p_scb->at_cb.cmd_max_len = BTA_AG_CMD_MAX; + bta_ag_at_init(&p_scb->at_cb); + + /* call app open call-out */ + bta_sys_conn_open(BTA_ID_AG, p_scb->app_id, p_scb->peer_addr); + bta_ag_cback_open(p_scb, NULL, BTA_AG_SUCCESS); + + if (p_scb->conn_service == BTA_AG_HFP) { + /* if hfp start timer for service level conn */ + bta_sys_start_timer(&p_scb->act_timer, BTA_AG_SVC_TOUT_EVT, p_bta_ag_cfg->conn_tout); + } else { + /* else service level conn is open */ + bta_ag_svc_conn_open(p_scb, p_data); + } +} + +/******************************************************************************* +** +** Function bta_ag_rfc_acp_open +** +** Description Handle RFCOMM channel open when accepting connection. +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_rfc_acp_open(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data) +{ + UINT16 lcid; + int i; + tBTA_AG_SCB *ag_scb, *other_scb; + BD_ADDR dev_addr = {0}; + int status; + /* set role */ + p_scb->role = BTA_AG_ACP; + APPL_TRACE_DEBUG ("bta_ag_rfc_acp_open: serv_handle0 = %d serv_handle1 = %d", + p_scb->serv_handle[0], p_scb->serv_handle[1]); + /* get bd addr of peer */ + if (PORT_SUCCESS != (status = PORT_CheckConnection(p_data->rfc.port_handle, FALSE, dev_addr, &lcid))) { + APPL_TRACE_DEBUG ("bta_ag_rfc_acp_open error PORT_CheckConnection returned status %d", status); + } + /* Collision Handling */ + for (i = 0, ag_scb = &bta_ag_cb.scb[0]; i < BTA_AG_NUM_SCB; i++, ag_scb++) { + if ((ag_scb->in_use) && (ag_scb->colli_tmr_on)) { + /* stop collision timer */ + ag_scb->colli_tmr_on = FALSE; + bta_sys_stop_timer (&ag_scb->colli_timer); + + if (bdcmp (dev_addr, ag_scb->peer_addr) == 0) { + /* If incoming and outgoing device are same, nothing more to do. */ + /* Outgoing conn will be aborted because we have successful incoming conn. */ + } else { + /* Resume outgoing connection. */ + other_scb = bta_ag_get_other_idle_scb (p_scb); + if (other_scb) { + bdcpy(other_scb->peer_addr, ag_scb->peer_addr); + other_scb->open_services = ag_scb->open_services; + other_scb->cli_sec_mask = ag_scb->cli_sec_mask; + bta_ag_resume_open (other_scb); + } + } + break; + } + } + bdcpy (p_scb->peer_addr, dev_addr); + /* determine connected service from port handle */ + for (i = 0; i < BTA_AG_NUM_IDX; i++) { + APPL_TRACE_DEBUG ("bta_ag_rfc_acp_open: i = %d serv_handle = %d port_handle = %d", + i, p_scb->serv_handle[i], p_data->rfc.port_handle); + if (p_scb->serv_handle[i] == p_data->rfc.port_handle) { + p_scb->conn_service = i; + p_scb->conn_handle = p_data->rfc.port_handle; + break; + } + } + APPL_TRACE_DEBUG ("bta_ag_rfc_acp_open: conn_service = %d conn_handle = %d", + p_scb->conn_service, p_scb->conn_handle); + /* close any unopened server */ + bta_ag_close_servers(p_scb, (p_scb->reg_services & ~bta_ag_svc_mask[p_scb->conn_service])); + /* do service discovery to get features */ + bta_ag_do_disc(p_scb, bta_ag_svc_mask[p_scb->conn_service]); + /* continue with common open processing */ + bta_ag_rfc_open(p_scb, p_data); +} + +/******************************************************************************* +** +** Function bta_ag_rfc_data +** +** Description Read and process data from RFCOMM. +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_rfc_data(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data) +{ + UINT16 len; + char buf[BTA_AG_RFC_READ_MAX]; + UNUSED(p_data); + memset(buf, 0, BTA_AG_RFC_READ_MAX); + + APPL_TRACE_DEBUG("bta_ag_rfc_data"); + /* do the following */ + for (;;) { + /* read data from rfcomm; if bad status, we're done */ + if (PORT_ReadData(p_scb->conn_handle, buf, BTA_AG_RFC_READ_MAX, &len) != PORT_SUCCESS) { + break; + } + /* if no data, we're done */ + if (len == 0) { + break; + } + /* run AT command interpreter on data */ + bta_sys_busy(BTA_ID_AG, p_scb->app_id, p_scb->peer_addr); + bta_ag_at_parse(&p_scb->at_cb, buf, len); + bta_sys_idle(BTA_ID_AG, p_scb->app_id, p_scb->peer_addr); + /* no more data to read, we're done */ + if (len < BTA_AG_RFC_READ_MAX) { + break; + } + } +} + +/******************************************************************************* +** +** Function bta_ag_start_close +** +** Description Start the process of closing SCO and RFCOMM connection. +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_start_close(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data) +{ + /* Take the link out of sniff and set L2C idle time to 0 */ + bta_dm_pm_active(p_scb->peer_addr); + L2CA_SetIdleTimeoutByBdAddr(p_scb->peer_addr, 0, BT_TRANSPORT_BR_EDR); + /* if SCO is open close SCO and wait on RFCOMM close */ + if (bta_ag_sco_is_open(p_scb)) { + p_scb->post_sco = BTA_AG_POST_SCO_CLOSE_RFC; + } else { + p_scb->post_sco = BTA_AG_POST_SCO_NONE; + bta_ag_rfc_do_close(p_scb, p_data); + } + /* Always do SCO shutdown to handle all SCO corner cases */ + bta_ag_sco_shutdown(p_scb, p_data); +} + +/******************************************************************************* +** +** Function bta_ag_post_sco_open +** +** Description Perform post-SCO open action, if any +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_post_sco_open(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data) +{ + switch (p_scb->post_sco) { + case BTA_AG_POST_SCO_RING: + bta_ag_send_ring(p_scb, p_data); + p_scb->post_sco = BTA_AG_POST_SCO_NONE; + break; + + case BTA_AG_POST_SCO_CALL_CONN: + bta_ag_send_call_inds(p_scb, BTA_AG_IN_CALL_CONN_RES); + p_scb->post_sco = BTA_AG_POST_SCO_NONE; + break; + + default: + break; + } +} + +/******************************************************************************* +** +** Function bta_ag_post_sco_close +** +** Description Perform post-SCO close action, if any +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_post_sco_close(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data) +{ + switch (p_scb->post_sco) { + case BTA_AG_POST_SCO_CLOSE_RFC: + bta_ag_rfc_do_close(p_scb, p_data); + p_scb->post_sco = BTA_AG_POST_SCO_NONE; + break; + + case BTA_AG_POST_SCO_CALL_CONN: + bta_ag_send_call_inds(p_scb, BTA_AG_IN_CALL_CONN_RES); + p_scb->post_sco = BTA_AG_POST_SCO_NONE; + break; + + case BTA_AG_POST_SCO_CALL_ORIG: + bta_ag_send_call_inds(p_scb, BTA_AG_OUT_CALL_ORIG_RES); + p_scb->post_sco = BTA_AG_POST_SCO_NONE; + break; + + case BTA_AG_POST_SCO_CALL_END: + bta_ag_send_call_inds(p_scb, BTA_AG_END_CALL_RES); + p_scb->post_sco = BTA_AG_POST_SCO_NONE; + break; + + case BTA_AG_POST_SCO_CALL_END_INCALL: + { + bta_ag_send_call_inds(p_scb, BTA_AG_END_CALL_RES); + /* Sending callsetup IND and Ring were defered to after SCO close. */ + bta_ag_send_call_inds(p_scb, BTA_AG_IN_CALL_RES); + + if (bta_ag_inband_enabled(p_scb) && !(p_scb->features & BTA_AG_FEAT_NOSCO)) { + p_scb->post_sco = BTA_AG_POST_SCO_RING; + bta_ag_sco_open(p_scb, p_data); + } else { + p_scb->post_sco = BTA_AG_POST_SCO_NONE; + bta_ag_send_ring(p_scb, p_data); + } + break; + } + + default: + break; + } +} + +/******************************************************************************* +** +** Function bta_ag_svc_conn_open +** +** Description Service level connection opened +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_svc_conn_open(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data) +{ + tBTA_AG_CONN evt; + UNUSED(p_data); + + if (!p_scb->svc_conn) { + /* set state variable */ + p_scb->svc_conn = TRUE; + /* Clear AT+BIA mask from previous SLC if any. */ + p_scb->bia_masked_out = 0; + /* stop timer */ + bta_sys_stop_timer(&p_scb->act_timer); + /* call callback */ + evt.hdr.handle = bta_ag_scb_to_idx(p_scb); + evt.hdr.app_id = p_scb->app_id; + evt.peer_feat = p_scb->peer_features; + bdcpy(evt.bd_addr, p_scb->peer_addr); +#if (BTM_WBS_INCLUDED == TRUE) + evt.peer_codec = p_scb->peer_codecs; +#endif + if ((p_scb->call_ind != BTA_AG_CALL_INACTIVE) || + (p_scb->callsetup_ind != BTA_AG_CALLSETUP_NONE)) { + bta_sys_sco_use(BTA_ID_AG, p_scb->app_id, p_scb->peer_addr); + } + (*bta_ag_cb.p_cback)(BTA_AG_CONN_EVT, (tBTA_AG *) &evt); + } +} + +/******************************************************************************* +** +** Function bta_ag_ci_rx_data +** +** Description Send result code to RFCOMM +** +** Returns void +** +*******************************************************************************/ +void bta_ag_ci_rx_data(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data) +{ + UINT16 len; + tBTA_AG_CI_RX_WRITE *p_rx_write_msg = (tBTA_AG_CI_RX_WRITE *)p_data; + char *p_data_area = (char *)(p_rx_write_msg+1); /* Point to data area after header */ + + APPL_TRACE_DEBUG("bta_ag_ci_rx_data:"); + /* send to RFCOMM */ + bta_sys_busy(BTA_ID_AG, p_scb->app_id, p_scb->peer_addr); + PORT_WriteData(p_scb->conn_handle, p_data_area, strlen(p_data_area), &len); + bta_sys_idle(BTA_ID_AG, p_scb->app_id, p_scb->peer_addr); +} + +/******************************************************************************* +** +** Function bta_ag_rcvd_slc_ready +** +** Description Handles SLC ready call-in in case of pass-through mode. +** +** Returns void +** +*******************************************************************************/ +void bta_ag_rcvd_slc_ready(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data) +{ + UNUSED(p_data); + APPL_TRACE_DEBUG("bta_ag_rcvd_slc_ready: handle = %d", bta_ag_scb_to_idx(p_scb)); + + if (bta_ag_cb.parse_mode == BTA_AG_PASS_THROUGH) { + /* In pass-through mode, BTA knows that SLC is ready only through call-in. */ + bta_ag_svc_conn_open(p_scb, NULL); + } +} + +/******************************************************************************* +** +** Function bta_ag_setcodec +** +** Description Handle API SetCodec +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_setcodec(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data) +{ +#if (BTM_WBS_INCLUDED == TRUE) + UINT16 handle = p_data->api_setcodec.hdr.layer_specific; + tBTA_AG_PEER_CODEC codec_type = p_data->api_setcodec.codec; + tBTA_AG_VAL val; + + val.hdr.handle = handle; + val.num = codec_type; + + /* Check if the requested codec type is valid */ + if((codec_type != BTA_AG_CODEC_NONE) && + (codec_type != BTA_AG_CODEC_CVSD) && + (codec_type != BTA_AG_CODEC_MSBC)) { + val.hdr.status = BTA_AG_FAIL_RESOURCES; + APPL_TRACE_ERROR("%s error: unsupported codec type %d", __func__, codec_type); + (*bta_ag_cb.p_cback)(BTA_AG_WBS_EVT, (tBTA_AG *) &val); + return; + } + + if ((p_scb->peer_codecs & codec_type) || + (codec_type == BTA_AG_CODEC_NONE) || + (codec_type == BTA_AG_CODEC_CVSD)) { + p_scb->sco_codec = codec_type; + p_scb->codec_updated = TRUE; + val.hdr.status = BTA_AG_SUCCESS; + APPL_TRACE_DEBUG("%s: Updated codec type %d", __func__, codec_type); + } else { + val.hdr.status = BTA_AG_FAIL_RESOURCES; + APPL_TRACE_ERROR("%s error: unsupported codec type %d",__func__, codec_type); + } + (*bta_ag_cb.p_cback)(BTA_AG_WBS_EVT, (tBTA_AG *) &val); +#endif +} + +#endif /* #if (BTA_AG_INCLUDED == TRUE) */ diff --git a/lib/bt/host/bluedroid/bta/hf_ag/bta_ag_api.c b/lib/bt/host/bluedroid/bta/hf_ag/bta_ag_api.c new file mode 100644 index 00000000..4b1d91ee --- /dev/null +++ b/lib/bt/host/bluedroid/bta/hf_ag/bta_ag_api.c @@ -0,0 +1,344 @@ +/****************************************************************************** + * + * Copyright (C) 2003-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This is the implementation of the API for the audio gateway (AG) + * subsystem of BTA, Broadcom's Bluetooth application layer for mobile + * phones. + * + ******************************************************************************/ + +#include +#include "bta/bta_api.h" +#include "bta/bta_sys.h" +#include "bta/bta_ag_api.h" +#include "bta_ag_int.h" +#include "osi/allocator.h" + +#if (BTA_AG_INCLUDED == TRUE) +/***************************************************************************** +** Constants +*****************************************************************************/ + +static const tBTA_SYS_REG bta_ag_reg = +{ + bta_ag_hdl_event, + BTA_AgDisable +}; + +/******************************************************************************* +** +** Function BTA_AgEnable +** +** Description Enable the audio gateway service. When the enable +** operation is complete the callback function will be +** called with a BTA_AG_ENABLE_EVT. This function must +** be called before other function in the AG API are +** called. +** +** Returns BTA_SUCCESS if OK, BTA_FAILURE otherwise. +** +*******************************************************************************/ +tBTA_STATUS BTA_AgEnable(tBTA_AG_PARSE_MODE parse_mode, tBTA_AG_CBACK *p_cback) +{ + tBTA_AG_API_ENABLE *p_buf; + UINT8 idx; + + /* Error if AG is already enabled, or AG is in the middle of disabling. */ + for (idx = 0; idx < BTA_AG_NUM_SCB; idx++) { + if (bta_ag_cb.scb[idx].in_use) { + APPL_TRACE_ERROR ("BTA_AgEnable: FAILED, AG already enabled."); + return BTA_FAILURE; + } + } + /* register with BTA system manager */ + bta_sys_register(BTA_ID_AG, &bta_ag_reg); + + if ((p_buf = (tBTA_AG_API_ENABLE *) osi_malloc(sizeof(tBTA_AG_API_ENABLE))) != NULL) { + p_buf->hdr.event = BTA_AG_API_ENABLE_EVT; + p_buf->parse_mode = parse_mode; + p_buf->p_cback = p_cback; + bta_sys_sendmsg(p_buf); + } + return BTA_SUCCESS; +} + +/******************************************************************************* +** +** Function BTA_AgDisable +** +** Description Disable the audio gateway service +** +** +** Returns void +** +*******************************************************************************/ +void BTA_AgDisable(void) +{ + BT_HDR *p_buf; + if ((p_buf = (BT_HDR *) osi_malloc(sizeof(BT_HDR))) != NULL) { + p_buf->event = BTA_AG_API_DISABLE_EVT; + bta_sys_sendmsg(p_buf); + } +} + +/******************************************************************************* +** +** Function BTA_AgRegister +** +** Description Register an Audio Gateway service. +** +** +** Returns void +** +*******************************************************************************/ +void BTA_AgRegister(tBTA_SERVICE_MASK services, tBTA_SEC sec_mask,tBTA_AG_FEAT features, + char * p_service_names[], UINT8 app_id) +{ + tBTA_AG_API_REGISTER *p_buf; + int i; + + if ((p_buf = (tBTA_AG_API_REGISTER *) osi_malloc(sizeof(tBTA_AG_API_REGISTER))) != NULL) { + p_buf->hdr.event = BTA_AG_API_REGISTER_EVT; + p_buf->features = features; + p_buf->sec_mask = sec_mask; + p_buf->services = services; + p_buf->app_id = app_id; + for (i = 0; i < BTA_AG_NUM_IDX; i++) { + if(p_service_names[i]) { + BCM_STRNCPY_S(p_buf->p_name[i], p_service_names[i], BTA_SERVICE_NAME_LEN); + p_buf->p_name[i][BTA_SERVICE_NAME_LEN] = '\0'; + } else { + p_buf->p_name[i][0] = '\0'; + } + } + bta_sys_sendmsg(p_buf); + } +} + +/******************************************************************************* +** +** Function BTA_AgDeregister +** +** Description Deregister an audio gateway service. +** +** +** Returns void +** +*******************************************************************************/ +void BTA_AgDeregister(UINT16 handle) +{ + BT_HDR *p_buf; + if ((p_buf = (BT_HDR *) osi_malloc(sizeof(BT_HDR))) != NULL) { + p_buf->event = BTA_AG_API_DEREGISTER_EVT; + p_buf->layer_specific = handle; + bta_sys_sendmsg(p_buf); + } +} + +/******************************************************************************* +** +** Function BTA_AgOpen +** +** Description Opens a connection to a headset or hands-free device. +** When connection is open callback function is called +** with a BTA_AG_OPEN_EVT. Only the data connection is +** opened. The audio connection is not opened. +** +** +** Returns void +** +*******************************************************************************/ +void BTA_AgOpen(UINT16 handle, BD_ADDR bd_addr, tBTA_SEC sec_mask, tBTA_SERVICE_MASK services) +{ + tBTA_AG_API_OPEN *p_buf; + + if ((p_buf = (tBTA_AG_API_OPEN *) osi_malloc(sizeof(tBTA_AG_API_OPEN))) != NULL) { + p_buf->hdr.event = BTA_AG_API_OPEN_EVT; + p_buf->hdr.layer_specific = handle; + bdcpy(p_buf->bd_addr, bd_addr); + p_buf->services = services; + p_buf->sec_mask = sec_mask; + bta_sys_sendmsg(p_buf); + } +} + +/******************************************************************************* +** +** Function BTA_AgClose +** +** Description Close the current connection to a headset or a handsfree +** Any current audio connection will also be closed. +** +** +** Returns void +** +*******************************************************************************/ +void BTA_AgClose(UINT16 handle) +{ + BT_HDR *p_buf; + + if ((p_buf = (BT_HDR *) osi_malloc(sizeof(BT_HDR))) != NULL) { + p_buf->event = BTA_AG_API_CLOSE_EVT; + p_buf->layer_specific = handle; + bta_sys_sendmsg(p_buf); + } +} + +/******************************************************************************* +** +** Function BTA_AgAudioOpen +** +** Description Opens an audio connection to the currently connected +** headset or handsfree. +** +** +** Returns void +** +*******************************************************************************/ +void BTA_AgAudioOpen(UINT16 handle) +{ + BT_HDR *p_buf; + + if ((p_buf = (BT_HDR *) osi_malloc(sizeof(BT_HDR))) != NULL) { + p_buf->event = BTA_AG_API_AUDIO_OPEN_EVT; + p_buf->layer_specific = handle; + bta_sys_sendmsg(p_buf); + } +} + +/******************************************************************************* +** +** Function BTA_AgAudioClose +** +** Description Close the currently active audio connection to a headset +** or handsfree. The data connection remains open +** +** +** Returns void +** +*******************************************************************************/ +void BTA_AgAudioClose(UINT16 handle) +{ + BT_HDR *p_buf; + + if ((p_buf = (BT_HDR *) osi_malloc(sizeof(BT_HDR))) != NULL) { + p_buf->event = BTA_AG_API_AUDIO_CLOSE_EVT; + p_buf->layer_specific = handle; + bta_sys_sendmsg(p_buf); + } +} + +/******************************************************************************* +** +** Function BTA_AgResult +** +** Description Send an AT result code to a headset or hands-free device. +** This function is only used when the AG parse mode is set +** to BTA_AG_PARSE. +** +** +** Returns void +** +*******************************************************************************/ +void BTA_AgResult(UINT16 handle, tBTA_AG_RES result, tBTA_AG_RES_DATA *p_data) +{ + tBTA_AG_API_RESULT *p_buf; + + if ((p_buf = (tBTA_AG_API_RESULT *) osi_malloc(sizeof(tBTA_AG_API_RESULT))) != NULL) { + p_buf->hdr.event = BTA_AG_API_RESULT_EVT; + p_buf->hdr.layer_specific = handle; + p_buf->result = result; + if(p_data) { + memcpy(&p_buf->data, p_data, sizeof(p_buf->data)); + } + bta_sys_sendmsg(p_buf); + } +} + +/******************************************************************************* +** +** Function BTA_AgSetCodec +** +** Description Specify the codec type to be used for the subsequent +** audio connection. +** +** +** +** Returns void +** +*******************************************************************************/ +void BTA_AgSetCodec(UINT16 handle, tBTA_AG_PEER_CODEC codec) +{ + tBTA_AG_API_SETCODEC *p_buf; + + if ((p_buf = (tBTA_AG_API_SETCODEC *) osi_malloc(sizeof(tBTA_AG_API_SETCODEC))) != NULL) { + p_buf->hdr.event = BTA_AG_API_SETCODEC_EVT; + p_buf->hdr.layer_specific = handle; + p_buf->codec = codec; + bta_sys_sendmsg(p_buf); + } +} + +#if (BTM_SCO_HCI_INCLUDED == TRUE) +/******************************************************************************* +** +** Function BTA_AgPktStatsNumsGet +** +** Description Specify the sync_conn_handle to be used for the packet numbers +** received or send. +** +** +** Returns void +** +*******************************************************************************/ +void BTA_AgPktStatsNumsGet(UINT16 handle, UINT16 sync_conn_handle) +{ + tBTA_AG_PKT_STAT_GET *p_buf; + + if ((p_buf = (tBTA_AG_PKT_STAT_GET *) osi_malloc(sizeof(tBTA_AG_PKT_STAT_GET))) != NULL) { + p_buf->hdr.event = BTA_AG_PKT_STAT_NUMS_GET_EVT; + p_buf->hdr.layer_specific = handle; + p_buf->sync_conn_handle = sync_conn_handle; + bta_sys_sendmsg(p_buf); + } +} + +/************************************************************************************************ + * Function BTA_AgCiData + * + * Description Trigger the lower-layer to fetch and send audio data. This function is only + * only used in the case that Voice Over HCI is enabled. Precondition is that + * the HFP audio connection is connected. After this function is called, lower + * layer will invoke esp_hf_client_outgoing_data_cb_t to fetch data + * + ***********************************************************************************************/ +void BTA_AgCiData(UINT16 handle) +{ + BT_HDR *p_buf; + tBTA_AG_SCB *p_scb; + if ((p_scb = bta_ag_scb_by_idx(handle)) != NULL && (p_buf = (BT_HDR *)osi_malloc(sizeof(BT_HDR))) != NULL) { + p_buf->event = BTA_AG_CI_SCO_DATA_EVT; + p_buf->layer_specific = handle; + bta_sys_sendmsg(p_buf); + } +} +#endif /* #if (BTM_SCO_HCI_INCLUDED == TRUE) */ + +#endif /* #if (BTA_AG_INCLUDED == TRUE)*/ diff --git a/lib/bt/host/bluedroid/bta/hf_ag/bta_ag_at.c b/lib/bt/host/bluedroid/bta/hf_ag/bta_ag_at.c new file mode 100644 index 00000000..82066d8f --- /dev/null +++ b/lib/bt/host/bluedroid/bta/hf_ag/bta_ag_at.c @@ -0,0 +1,201 @@ +/****************************************************************************** + * + * Copyright (C) 2004-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * BTA AG AT command interpreter. + * + ******************************************************************************/ + +#include +#include "osi/allocator.h" +#include "bta_ag_at.h" +#include "bta/utl.h" + +#if (BTA_AG_INCLUDED == TRUE) +/****************************************************************************** +** +** Function bta_ag_at_init +** +** Description Initialize the AT command parser control block. +** +** +** Returns void +** +******************************************************************************/ +void bta_ag_at_init(tBTA_AG_AT_CB *p_cb) +{ + p_cb->p_cmd_buf = NULL; + p_cb->cmd_pos = 0; +} + +/****************************************************************************** +** +** Function bta_ag_at_reinit +** +** Description Re-initialize the AT command parser control block. This +** function resets the AT command parser state and frees +** any GKI buffer. +** +** +** Returns void +** +******************************************************************************/ +void bta_ag_at_reinit(tBTA_AG_AT_CB *p_cb) +{ + if (p_cb->p_cmd_buf != NULL) { + osi_free(p_cb->p_cmd_buf); + p_cb->p_cmd_buf = NULL; + } + p_cb->cmd_pos = 0; +} +/****************************************************************************** +** +** Function bta_ag_process_at +** +** Description Parse AT commands. This function will take the input +** character string and parse it for AT commands according to +** the AT command table passed in the control block. +** +** +** Returns void +** +******************************************************************************/ +void bta_ag_process_at(tBTA_AG_AT_CB *p_cb) +{ + UINT16 idx; + UINT8 arg_type; + char *p_arg; + INT16 int_arg = 0; + /* loop through at command table looking for match */ + for (idx = 0; p_cb->p_at_tbl[idx].p_cmd[0] != 0; idx++) { + if (!utl_strucmp(p_cb->p_at_tbl[idx].p_cmd, p_cb->p_cmd_buf)) { + break; + } + } + + /* if there is a match; verify argument type */ + if (p_cb->p_at_tbl[idx].p_cmd[0] != 0) { + /* start of argument is p + strlen matching command */ + p_arg = p_cb->p_cmd_buf + strlen(p_cb->p_at_tbl[idx].p_cmd); + /* if no argument */ + if (p_arg[0] == 0) { + arg_type = BTA_AG_AT_NONE; + } + /* else if arg is '?' and it is last character */ + else if (p_arg[0] == '?' && p_arg[1] == 0) { + arg_type = BTA_AG_AT_READ; /* we have a read */ + } + /* else if arg is '=' */ + else if (p_arg[0] == '=' && p_arg[1] != 0) { + if (p_arg[1] == '?' && p_arg[2] == 0) { + arg_type = BTA_AG_AT_TEST; /* we have a test */ + } else { + arg_type = BTA_AG_AT_SET; /* we have a set */ + p_arg++; /* skip past '=' */ + } + } + /* else it is freeform argument */ + else { + arg_type = BTA_AG_AT_FREE; + } + /* if arguments match command capabilities */ + if ((arg_type & p_cb->p_at_tbl[idx].arg_type) != 0) { + /* if it's a set integer check max, min range */ + if (arg_type == BTA_AG_AT_SET && p_cb->p_at_tbl[idx].fmt == BTA_AG_AT_INT) { + int_arg = utl_str2int(p_arg); + if (int_arg < (INT16) p_cb->p_at_tbl[idx].min || + int_arg > (INT16) p_cb->p_at_tbl[idx].max) { + /* arg out of range; error */ + (*p_cb->p_err_cback)(p_cb->p_user, FALSE, NULL); + } else { + (*p_cb->p_cmd_cback)(p_cb->p_user, idx, arg_type, p_arg, int_arg); + } + } else { + (*p_cb->p_cmd_cback)(p_cb->p_user, idx, arg_type, p_arg, int_arg); + } + } else { + /* else error */ + (*p_cb->p_err_cback)(p_cb->p_user, FALSE, NULL); + } + } else { + /* else no match call error callback */ + (*p_cb->p_err_cback)(p_cb->p_user, TRUE, p_cb->p_cmd_buf); + } +} + +/****************************************************************************** +** +** Function bta_ag_at_parse +** +** Description Parse AT commands. This function will take the input +** character string and parse it for AT commands according to +** the AT command table passed in the control block. +** +** +** Returns void +** +******************************************************************************/ +void bta_ag_at_parse(tBTA_AG_AT_CB *p_cb, char *p_buf, UINT16 len) +{ + int i = 0; + char* p_save; + + if (p_cb->p_cmd_buf == NULL) { + if ((p_cb->p_cmd_buf = (char *) osi_malloc(p_cb->cmd_max_len)) == NULL) { + APPL_TRACE_ERROR("%s: osi_malloc() failed allocation", __func__); + return; + } + p_cb->cmd_pos = 0; + } + + for (i = 0; i < len;) { + while (p_cb->cmd_pos < p_cb->cmd_max_len-1 && i < len) { + /* Skip null characters between AT commands. */ + if ((p_cb->cmd_pos == 0) && (p_buf[i] == 0)) { + i++; + continue; + } + p_cb->p_cmd_buf[p_cb->cmd_pos] = p_buf[i++]; + if ( p_cb->p_cmd_buf[p_cb->cmd_pos] == '\r' || p_cb->p_cmd_buf[p_cb->cmd_pos] == '\n') { + p_cb->p_cmd_buf[p_cb->cmd_pos] = 0; + if ((p_cb->cmd_pos > 2) && + (p_cb->p_cmd_buf[0] == 'A' || p_cb->p_cmd_buf[0] == 'a') && + (p_cb->p_cmd_buf[1] == 'T' || p_cb->p_cmd_buf[1] == 't')) { + p_save = p_cb->p_cmd_buf; + p_cb->p_cmd_buf += 2; + bta_ag_process_at(p_cb); + p_cb->p_cmd_buf = p_save; + } + p_cb->cmd_pos = 0; + } else if( p_cb->p_cmd_buf[p_cb->cmd_pos] == 0x1A || p_cb->p_cmd_buf[p_cb->cmd_pos] == 0x1B) { + p_cb->p_cmd_buf[++p_cb->cmd_pos] = 0; + (*p_cb->p_err_cback)(p_cb->p_user, TRUE, p_cb->p_cmd_buf); + p_cb->cmd_pos = 0; + } else { + ++p_cb->cmd_pos; + } + } + + if (i < len) { + p_cb->cmd_pos = 0; + } + } +} + +#endif /* #if (BTA_AG_INCLUDED == TRUE) */ diff --git a/lib/bt/host/bluedroid/bta/hf_ag/bta_ag_cfg.c b/lib/bt/host/bluedroid/bta/hf_ag/bta_ag_cfg.c new file mode 100644 index 00000000..3c059cea --- /dev/null +++ b/lib/bt/host/bluedroid/bta/hf_ag/bta_ag_cfg.c @@ -0,0 +1,60 @@ +/****************************************************************************** + * + * Copyright (C) 2003-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains compile-time configurable constants for the audio + * gateway. + * + ******************************************************************************/ + +#include "osi/allocator.h" +#include "bta/bta_api.h" +#include "bta/bta_ag_api.h" +#include "common/bt_target.h" + +#if (BTA_AG_INCLUDED == TRUE) + +#ifndef BTA_AG_CIND_INFO +#define BTA_AG_CIND_INFO "(\"call\",(0,1)),(\"callsetup\",(0-3)),(\"service\",(0-3)),(\"signal\",(0-6)),(\"roam\",(0,1)),(\"battchg\",(0-5)),(\"callheld\",(0-2))" +#endif + +#ifndef BTA_AG_CONN_TIMEOUT +#define BTA_AG_CONN_TIMEOUT 5000 +#endif + +#ifndef BTA_AG_SCO_PKT_TYPES +/* S1 packet type setting from HFP 1.5 spec */ +#define BTA_AG_SCO_PKT_TYPES /* BTM_SCO_LINK_ALL_PKT_MASK */ (BTM_SCO_LINK_ONLY_MASK | \ + BTM_SCO_PKT_TYPES_MASK_EV3 | \ + BTM_SCO_PKT_TYPES_MASK_NO_3_EV3 | \ + BTM_SCO_PKT_TYPES_MASK_NO_2_EV5 | \ + BTM_SCO_PKT_TYPES_MASK_NO_3_EV5) +#endif /* BTA_AG_SCO_PKT_TYPES */ + +const tBTA_AG_CFG bta_ag_cfg = +{ + BTA_AG_CIND_INFO, + BTA_AG_CONN_TIMEOUT, + BTA_AG_SCO_PKT_TYPES, + BTA_AG_CHLD_VAL_ECC, + BTA_AG_CHLD_VAL +}; +tBTA_AG_CFG *p_bta_ag_cfg = (tBTA_AG_CFG *) &bta_ag_cfg; + +#endif /* #if (BTA_AG_INCLUDED == TRUE) */ diff --git a/lib/bt/host/bluedroid/bta/hf_ag/bta_ag_cmd.c b/lib/bt/host/bluedroid/bta/hf_ag/bta_ag_cmd.c new file mode 100644 index 00000000..6bccfe1b --- /dev/null +++ b/lib/bt/host/bluedroid/bta/hf_ag/bta_ag_cmd.c @@ -0,0 +1,1719 @@ +/****************************************************************************** + * + * Copyright (C) 2004-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains functions for processing AT commands and results. + * + ******************************************************************************/ +#include +#include +#include +#include "common/bt_target.h" +#include "common/bt_trace.h" +#include "stack/bt_types.h" +#include "bta/bta_ag_api.h" +#include "bta_ag_at.h" +#include "bta_ag_int.h" +#include "bta/bta_api.h" +#include "bta/bta_sys.h" +#include "osi/allocator.h" +#include "stack/port_api.h" +#include "bta/utl.h" + +#if BT_HF_AG_BQB_INCLUDED +static BOOLEAN s_bta_hf_ag_bqb_brsf_flag = false; +#endif /* BT_HF_AG_BQB_INCLUDED */ + +#if (BTA_AG_INCLUDED == TRUE) +/***************************************************************************** +** Constants +*****************************************************************************/ + +/* ring timeout */ +#define BTA_AG_RING_TOUT 5000 +#define BTA_AG_CMD_MAX_VAL 32767 /* Maximum value is signed 16-bit value */ + +/* Invalid Chld command */ +#define BTA_AG_INVALID_CHLD 255 + +/* clip type constants */ +#define BTA_AG_CLIP_TYPE_MIN 128 +#define BTA_AG_CLIP_TYPE_MAX 175 +#define BTA_AG_CLIP_TYPE_DEFAULT 129 +#define BTA_AG_CLIP_TYPE_VOIP 255 + +/******************************************* +* HSP callback +********************************************/ +/* callback event lookup table for HSP */ +const tBTA_AG_EVT bta_ag_hsp_cb_evt[] = +{ + BTA_AG_AT_CKPD_EVT, /* BTA_AG_HS_CMD_CKPD */ + BTA_AG_SPK_EVT, /* BTA_AG_HS_CMD_VGS */ + BTA_AG_MIC_EVT /* BTA_AG_HS_CMD_VGM */ +}; +/* HSP AT commands matches bta_ag_hsp_cmd[] */ +enum +{ + BTA_AG_HS_CMD_CKPD, + BTA_AG_HS_CMD_VGS, + BTA_AG_HS_CMD_VGM +}; +/* HSP AT command interpreter table */ +const tBTA_AG_AT_CMD bta_ag_hsp_cmd[] = +{ + {"+CKPD", BTA_AG_AT_SET, BTA_AG_AT_INT, 200, 200}, + {"+VGS", BTA_AG_AT_SET, BTA_AG_AT_INT, 0, 15}, + {"+VGM", BTA_AG_AT_SET, BTA_AG_AT_INT, 0, 15}, + {"", BTA_AG_AT_NONE, BTA_AG_AT_STR, 0, 0} +}; + +/******************************************* +* HFP callback +********************************************/ +/* callback event lookup table for HFP (Indexed by command) */ +const tBTA_AG_EVT bta_ag_hfp_cb_evt[] = +{ + BTA_AG_AT_A_EVT, /* BTA_AG_HF_CMD_A */ + BTA_AG_AT_D_EVT, /* BTA_AG_HF_CMD_D */ + BTA_AG_SPK_EVT, /* BTA_AG_HF_CMD_VGS */ + BTA_AG_MIC_EVT, /* BTA_AG_HF_CMD_VGM */ + 0, /* BTA_AG_HF_CMD_CCWA */ + BTA_AG_AT_CHLD_EVT, /* BTA_AG_HF_CMD_CHLD */ + BTA_AG_AT_CHUP_EVT, /* BTA_AG_HF_CMD_CHUP */ + BTA_AG_AT_CIND_EVT, /* BTA_AG_HF_CMD_CIND */ + 0, /* BTA_AG_HF_CMD_CLIP */ + 0, /* BTA_AG_HF_CMD_CMER */ + BTA_AG_AT_VTS_EVT, /* BTA_AG_HF_CMD_VTS */ + BTA_AG_AT_BINP_EVT, /* BTA_AG_HF_CMD_BINP */ + BTA_AG_AT_BLDN_EVT, /* BTA_AG_HF_CMD_BLDN */ + BTA_AG_AT_BVRA_EVT, /* BTA_AG_HF_CMD_BVRA */ + 0, /* BTA_AG_HF_CMD_BRSF */ + BTA_AG_AT_NREC_EVT, /* BTA_AG_HF_CMD_NREC */ + BTA_AG_AT_CNUM_EVT, /* BTA_AG_HF_CMD_CNUM */ + BTA_AG_AT_BTRH_EVT, /* BTA_AG_HF_CMD_BTRH */ + BTA_AG_AT_CLCC_EVT, /* BTA_AG_HF_CMD_CLCC */ + BTA_AG_AT_COPS_EVT, /* BTA_AG_HF_CMD_COPS */ + 0, /* BTA_AG_HF_CMD_CMEE */ + 0, /* BTA_AG_HF_CMD_BIA */ + BTA_AG_AT_CBC_EVT, /* BTA_AG_HF_CMD_CBC */ + 0, /* BTA_AG_HF_CMD_BCC */ + BTA_AG_AT_BCS_EVT, /* BTA_AG_HF_CMD_BCS */ + BTA_AG_AT_BAC_EVT /* BTA_AG_HF_CMD_BAC */ +}; + +/* HFP AT commands matches bta_ag_hfp_cmd[] */ +enum +{ + BTA_AG_HF_CMD_A, + BTA_AG_HF_CMD_D, + BTA_AG_HF_CMD_VGS, + BTA_AG_HF_CMD_VGM, + BTA_AG_HF_CMD_CCWA, + BTA_AG_HF_CMD_CHLD, + BTA_AG_HF_CMD_CHUP, + BTA_AG_HF_CMD_CIND, + BTA_AG_HF_CMD_CLIP, + BTA_AG_HF_CMD_CMER, + BTA_AG_HF_CMD_VTS, + BTA_AG_HF_CMD_BINP, + BTA_AG_HF_CMD_BLDN, + BTA_AG_HF_CMD_BVRA, + BTA_AG_HF_CMD_BRSF, + BTA_AG_HF_CMD_NREC, + BTA_AG_HF_CMD_CNUM, + BTA_AG_HF_CMD_BTRH, + BTA_AG_HF_CMD_CLCC, + BTA_AG_HF_CMD_COPS, + BTA_AG_HF_CMD_CMEE, + BTA_AG_HF_CMD_BIA, + BTA_AG_HF_CMD_CBC, + BTA_AG_HF_CMD_BCC, + BTA_AG_HF_CMD_BCS, + BTA_AG_HF_CMD_BAC +}; + +/* dialing type of BTA_AG_HF_CMD_D */ +enum +{ + BTA_AG_HF_DIAL_NUM = 0, + BTA_AG_HF_DIAL_VOIP, + BTA_AG_HF_DIAL_MEM, +}; + +/* HFP AT command interpreter table */ +const tBTA_AG_AT_CMD bta_ag_hfp_cmd[] = +{ + {"A", BTA_AG_AT_NONE, BTA_AG_AT_STR, 0, 0}, + {"D", (BTA_AG_AT_NONE | BTA_AG_AT_FREE), BTA_AG_AT_STR, 0, 0}, + {"+VGS", BTA_AG_AT_SET, BTA_AG_AT_INT, 0, 15}, + {"+VGM", BTA_AG_AT_SET, BTA_AG_AT_INT, 0, 15}, + {"+CCWA", BTA_AG_AT_SET, BTA_AG_AT_INT, 0, 1}, + /* Consider CHLD as str to take care of indexes for ECC */ + {"+CHLD", (BTA_AG_AT_SET | BTA_AG_AT_TEST), BTA_AG_AT_STR, 0, 4}, + {"+CHUP", BTA_AG_AT_NONE, BTA_AG_AT_STR, 0, 0}, + {"+CIND", (BTA_AG_AT_READ | BTA_AG_AT_TEST), BTA_AG_AT_STR, 0, 0}, + {"+CLIP", BTA_AG_AT_SET, BTA_AG_AT_INT, 0, 1}, + {"+CMER", BTA_AG_AT_SET, BTA_AG_AT_STR, 0, 0}, + {"+VTS", BTA_AG_AT_SET, BTA_AG_AT_STR, 0, 0}, + {"+BINP", BTA_AG_AT_SET, BTA_AG_AT_INT, 1, 1}, + {"+BLDN", BTA_AG_AT_NONE, BTA_AG_AT_STR, 0, 0}, + {"+BVRA", BTA_AG_AT_SET, BTA_AG_AT_INT, 0, 1}, + {"+BRSF", BTA_AG_AT_SET, BTA_AG_AT_INT, 0, BTA_AG_CMD_MAX_VAL}, + {"+NREC", BTA_AG_AT_SET, BTA_AG_AT_INT, 0, 0}, + {"+CNUM", BTA_AG_AT_NONE, BTA_AG_AT_STR, 0, 0}, + {"+BTRH", (BTA_AG_AT_READ | BTA_AG_AT_SET), BTA_AG_AT_INT, 0, 2}, + {"+CLCC", BTA_AG_AT_NONE, BTA_AG_AT_STR, 0, 0}, + {"+COPS", (BTA_AG_AT_READ | BTA_AG_AT_SET), BTA_AG_AT_STR, 0, 0}, + {"+CMEE", BTA_AG_AT_SET, BTA_AG_AT_INT, 0, 1}, + {"+BIA", BTA_AG_AT_SET, BTA_AG_AT_STR, 0, 20}, + {"+CBC", BTA_AG_AT_SET, BTA_AG_AT_INT, 0, 100}, + {"+BCC", BTA_AG_AT_NONE, BTA_AG_AT_STR, 0, 0}, + {"+BCS", BTA_AG_AT_SET, BTA_AG_AT_INT, 0, BTA_AG_CMD_MAX_VAL}, + {"+BAC", BTA_AG_AT_SET, BTA_AG_AT_STR, 0, 0}, + {"", BTA_AG_AT_NONE, BTA_AG_AT_STR, 0, 0} +}; + +/******************************************* +* AT Result +********************************************/ +const tBTA_AG_AT_CMD *bta_ag_at_tbl[BTA_AG_NUM_IDX] = +{ + bta_ag_hsp_cmd, + bta_ag_hfp_cmd +}; + +/* AT result code argument types */ +enum +{ + BTA_AG_RES_FMT_NONE, /* no argument */ + BTA_AG_RES_FMT_INT, /* integer argument */ + BTA_AG_RES_FMT_STR /* string argument */ +}; + +#if defined(BTA_AG_MULTI_RESULT_INCLUDED) && (BTA_AG_MULTI_RESULT_INCLUDED == TRUE) +#define BTA_AG_AT_MULTI_LEN 2 +#define AT_SET_RES_CB(res_cb, c, p, i) {res_cb.code = c; res_cb.p_arg = p; res_cb.int_arg = i;} + +/* type for AT result code block */ +typedef struct +{ + UINT8 code; + char *p_arg; + INT16 int_arg; +} tBTA_AG_RESULT_CB; + +/* type for multiple AT result codes block */ +typedef struct +{ + UINT8 num_result; + tBTA_AG_RESULT_CB res_cb[BTA_AG_AT_MULTI_LEN]; +} tBTA_AG_MULTI_RESULT_CB; +#endif + +/* AT result code table element */ +typedef struct +{ + const char *p_res; /* AT result string */ + UINT8 fmt; /* whether argument is int or string */ +} tBTA_AG_RESULT; + +/* AT result code constant table (Indexed by result code) */ +const tBTA_AG_RESULT bta_ag_result_tbl[] = +{ + {"OK", BTA_AG_RES_FMT_NONE}, + {"ERROR", BTA_AG_RES_FMT_NONE}, + {"RING", BTA_AG_RES_FMT_NONE}, + {"+VGS: ", BTA_AG_RES_FMT_INT}, + {"+VGM: ", BTA_AG_RES_FMT_INT}, + {"+CCWA: ", BTA_AG_RES_FMT_STR}, + {"+CHLD: ", BTA_AG_RES_FMT_STR}, + {"+CIND: ", BTA_AG_RES_FMT_STR}, + {"+CLIP: ", BTA_AG_RES_FMT_STR}, + {"+CIEV: ", BTA_AG_RES_FMT_STR}, + {"+BINP: ", BTA_AG_RES_FMT_STR}, + {"+BVRA: ", BTA_AG_RES_FMT_INT}, + {"+BRSF: ", BTA_AG_RES_FMT_INT}, + {"+BSIR: ", BTA_AG_RES_FMT_INT}, + {"+CNUM: ", BTA_AG_RES_FMT_STR}, + {"+BTRH: ", BTA_AG_RES_FMT_INT}, + {"+CLCC: ", BTA_AG_RES_FMT_STR}, + {"+COPS: ", BTA_AG_RES_FMT_STR}, + {"+CME ERROR: ", BTA_AG_RES_FMT_INT}, + {"+BCS: ", BTA_AG_RES_FMT_INT}, + {"", BTA_AG_RES_FMT_STR} +}; + +/* AT result codes, matches bta_ag_result_tbl[] */ +enum +{ + BTA_AG_RES_OK, + BTA_AG_RES_ERROR, + BTA_AG_RES_RING, + BTA_AG_RES_VGS, + BTA_AG_RES_VGM, + BTA_AG_RES_CCWA, + BTA_AG_RES_CHLD, + BTA_AG_RES_CIND, + BTA_AG_RES_CLIP, + BTA_AG_RES_CIEV, + BTA_AG_RES_BINP, + BTA_AG_RES_BVRA, + BTA_AG_RES_BRSF, + BTA_AG_RES_BSIR, + BTA_AG_RES_CNUM, + BTA_AG_RES_BTRH, + BTA_AG_RES_CLCC, + BTA_AG_RES_COPS, + BTA_AG_RES_CMEE, + BTA_AG_RES_BCS, + BTA_AG_RES_UNAT +}; + +/* translation of API result code values to internal values */ +const UINT8 bta_ag_trans_result[] = +{ + BTA_AG_RES_VGS, /* BTA_AG_SPK_RES */ + BTA_AG_RES_VGM, /* BTA_AG_MIC_RES */ + BTA_AG_RES_BSIR, /* BTA_AG_INBAND_RING_RES */ + BTA_AG_RES_CIND, /* BTA_AG_CIND_RES */ + BTA_AG_RES_BINP, /* BTA_AG_BINP_RES */ + BTA_AG_RES_CIEV, /* BTA_AG_IND_RES */ + BTA_AG_RES_BVRA, /* BTA_AG_BVRA_RES */ + BTA_AG_RES_CNUM, /* BTA_AG_CNUM_RES */ + BTA_AG_RES_BTRH, /* BTA_AG_BTRH_RES */ + BTA_AG_RES_CLCC, /* BTA_AG_CLCC_RES */ + BTA_AG_RES_COPS, /* BTA_AG_COPS_RES */ + 0, /* BTA_AG_IN_CALL_RES */ + 0, /* BTA_AG_IN_CALL_CONN_RES */ + BTA_AG_RES_CCWA, /* BTA_AG_CALL_WAIT_RES */ + 0, /* BTA_AG_OUT_CALL_ORIG_RES */ + 0, /* BTA_AG_OUT_CALL_ALERT_RES */ + 0, /* BTA_AG_OUT_CALL_CONN_RES */ + 0, /* BTA_AG_CALL_CANCEL_RES */ + 0, /* BTA_AG_END_CALL_RES */ + 0, /* BTA_AG_IN_CALL_HELD_RES */ + BTA_AG_RES_UNAT /* BTA_AG_UNAT_RES */ +}; + +/* callsetup indicator value lookup table */ +const UINT8 bta_ag_callsetup_ind_tbl[] = +{ + 0, /* BTA_AG_SPK_RES */ + 0, /* BTA_AG_MIC_RES */ + 0, /* BTA_AG_INBAND_RING_RES */ + 0, /* BTA_AG_CIND_RES */ + 0, /* BTA_AG_BINP_RES */ + 0, /* BTA_AG_IND_RES */ + 0, /* BTA_AG_BVRA_RES */ + 0, /* BTA_AG_CNUM_RES */ + 0, /* BTA_AG_BTRH_RES */ + 0, /* BTA_AG_CLCC_RES */ + 0, /* BTA_AG_COPS_RES */ + BTA_AG_CALLSETUP_INCOMING, /* BTA_AG_IN_CALL_RES */ + BTA_AG_CALLSETUP_NONE, /* BTA_AG_IN_CALL_CONN_RES */ + BTA_AG_CALLSETUP_INCOMING, /* BTA_AG_CALL_WAIT_RES */ + BTA_AG_CALLSETUP_OUTGOING, /* BTA_AG_OUT_CALL_ORIG_RES */ + BTA_AG_CALLSETUP_ALERTING, /* BTA_AG_OUT_CALL_ALERT_RES */ + BTA_AG_CALLSETUP_NONE, /* BTA_AG_OUT_CALL_CONN_RES */ + BTA_AG_CALLSETUP_NONE, /* BTA_AG_CALL_CANCEL_RES */ + BTA_AG_CALLSETUP_NONE, /* BTA_AG_END_CALL_RES */ + BTA_AG_CALLSETUP_NONE /* BTA_AG_IN_CALL_HELD_RES */ +}; + +#if defined(BTA_HSP_RESULT_REPLACE_COLON) && (BTA_HSP_RESULT_REPLACE_COLON == TRUE) +#define COLON_IDX_4_VGSVGM 4 +#endif + +/******************************************************************************* +** +** Function bta_hf_ag_bqb_brsf_ctrl +** +** Description Control the usage of BTA_AG_BQB_BRSF_FEAT_SPEC for BQB test +** +** Returns void +** +*******************************************************************************/ +#if BT_HF_AG_BQB_INCLUDED +void bta_hf_ag_bqb_brsf_ctrl(BOOLEAN enable) +{ + s_bta_hf_ag_bqb_brsf_flag = enable; +} +#endif /* BT_HF_AG_BQB_INCLUDED */ + +/******************************************* +* Funcitons Result +********************************************/ +/******************************************************************************* +** +** Function bta_ag_send_result +** +** Description Send an AT result code. +** +** +** Returns void +** +*******************************************************************************/ +static void bta_ag_send_result(tBTA_AG_SCB *p_scb, UINT8 code, char *p_arg, INT16 int_arg) +{ + char buf[BTA_AG_AT_MAX_LEN + 16]; + char *p = buf; + UINT16 len; +#if (BTIF_TRACE_DEBUG == TRUE) + memset(buf, NULL, sizeof(buf)); +#endif + /* init with \r\n */ + *p++ = '\r'; + *p++ = '\n'; + + /* copy result code string */ + BCM_STRCPY_S(p, bta_ag_result_tbl[code].p_res); +#if defined(BTA_HSP_RESULT_REPLACE_COLON) && (BTA_HSP_RESULT_REPLACE_COLON == TRUE) + if(p_scb->conn_service == BTA_AG_HSP) { + /* If HSP then ":"symbol should be changed as "=" for HSP compatibility */ + switch(code) { + case BTA_AG_RES_VGS: + case BTA_AG_RES_VGM: + { + if(*(p+COLON_IDX_4_VGSVGM) == ':') + { + #if defined(BTA_AG_RESULT_DEBUG) && (BTA_AG_RESULT_DEBUG == TRUE) + APPL_TRACE_DEBUG("[HSP] ':'symbol is changed as '=' for HSP compatibility"); + #endif + *(p+COLON_IDX_4_VGSVGM) = '='; + } + break; + } + } + } +#endif + p += strlen(bta_ag_result_tbl[code].p_res); + + /* copy argument if any */ + if (bta_ag_result_tbl[code].fmt == BTA_AG_RES_FMT_INT) { + p += utl_itoa((UINT16) int_arg, p); + } else if (bta_ag_result_tbl[code].fmt == BTA_AG_RES_FMT_STR) { + BCM_STRCPY_S(p, p_arg); + p += strlen(p_arg); + } + /* finish with \r\n */ + *p++ = '\r'; + *p++ = '\n'; + APPL_TRACE_DEBUG("bta_ag_send_result: %s", buf); + /* send to RFCOMM */ + PORT_WriteData(p_scb->conn_handle, buf, (UINT16) (p - buf), &len); +} + +#if defined(BTA_AG_MULTI_RESULT_INCLUDED) && (BTA_AG_MULTI_RESULT_INCLUDED == TRUE) +/******************************************************************************* +** +** Function bta_ag_send_multi_result +** +** Description Send multiple AT result codes. +** +** +** Returns void +** +*******************************************************************************/ +static void bta_ag_send_multi_result(tBTA_AG_SCB *p_scb, tBTA_AG_MULTI_RESULT_CB *m_res_cb) +{ + char buf[BTA_AG_AT_MAX_LEN * BTA_AG_AT_MULTI_LEN + 16]; + char *p = buf; + UINT16 len; + UINT8 res_idx = 0; + + if((!m_res_cb) || (m_res_cb->num_result == 0) || (m_res_cb->num_result > BTA_AG_AT_MULTI_LEN)) { + APPL_TRACE_DEBUG("m_res_cb is NULL or num_result is out of range."); + return; + } + +#if defined(BTA_AG_RESULT_DEBUG) && (BTA_AG_RESULT_DEBUG == TRUE) + memset(buf, NULL, sizeof(buf)); +#endif + + while(res_idx < m_res_cb->num_result) { + /* init with \r\n */ + *p++ = '\r'; + *p++ = '\n'; + + /* copy result code string */ + BCM_STRCPY_S(p, bta_ag_result_tbl[m_res_cb->res_cb[res_idx].code].p_res); + p += strlen(bta_ag_result_tbl[m_res_cb->res_cb[res_idx].code].p_res); + + /* copy argument if any */ + if (bta_ag_result_tbl[m_res_cb->res_cb[res_idx].code].fmt == BTA_AG_RES_FMT_INT) { + p += utl_itoa((UINT16) m_res_cb->res_cb[res_idx].int_arg, p); + } else if (bta_ag_result_tbl[m_res_cb->res_cb[res_idx].code].fmt == BTA_AG_RES_FMT_STR) { + BCM_STRCPY_S(p, m_res_cb->res_cb[res_idx].p_arg); + p += strlen(m_res_cb->res_cb[res_idx].p_arg); + } + /* finish with \r\n */ + *p++ = '\r'; + *p++ = '\n'; + res_idx++; + } +#if defined(BTA_AG_RESULT_DEBUG) && (BTA_AG_RESULT_DEBUG == TRUE) + APPL_TRACE_DEBUG("send_result: %s", buf); +#endif + /* send to RFCOMM */ + PORT_WriteData(p_scb->conn_handle, buf, (UINT16) (p - buf), &len); +} +#endif /* #if defined(BTA_AG_MULTI_RESULT_INCLUDED) && (BTA_AG_MULTI_RESULT_INCLUDED == TRUE) */ + +/******************************************************************************* +** +** Function bta_ag_send_ok +** +** Description Send an OK result code. +** +** +** Returns void +** +*******************************************************************************/ +static void bta_ag_send_ok(tBTA_AG_SCB *p_scb) +{ + bta_ag_send_result(p_scb, BTA_AG_RES_OK, NULL, 0); +} + +/******************************************************************************* +** +** Function bta_ag_send_error +** +** Description Send an ERROR result code. +** errcode - used to send verbose errocode +** +** +** Returns void +** +*******************************************************************************/ +static void bta_ag_send_error(tBTA_AG_SCB *p_scb, INT16 errcode) +{ + /* If HFP and extended audio gateway error codes are enabled */ + if (p_scb->conn_service == BTA_AG_HFP && p_scb->cmee_enabled) { + bta_ag_send_result(p_scb, BTA_AG_RES_CMEE, NULL, errcode); + } else { + bta_ag_send_result(p_scb, BTA_AG_RES_ERROR, NULL, 0); + } +} + +/******************************************************************************* +** +** Function bta_ag_send_ind +** +** Description Send an indicator CIEV result code. +** +** +** Returns void +** +*******************************************************************************/ +static void bta_ag_send_ind(tBTA_AG_SCB *p_scb, UINT16 id, UINT16 value, BOOLEAN on_demand) +{ + char str[12]; + char *p = str; + /* If the indicator is masked out, just return */ + /* Mandatory indicators can not be masked out. */ + if ((p_scb->bia_masked_out & ((UINT32)1 << id)) && + ((id != BTA_AG_IND_CALL) && (id != BTA_AG_IND_CALLSETUP) && (id != BTA_AG_IND_CALLHELD))) + return; + + /* Ensure we do not send duplicate indicators if not requested by app */ + /* If it was requested by app, transmit CIEV even if it is duplicate. */ + if (id == BTA_AG_IND_CALL) { + if ((value == p_scb->call_ind) && (on_demand == FALSE)) { + return; + } + p_scb->call_ind = (UINT8)value; + } + if ((id == BTA_AG_IND_CALLSETUP) && (on_demand == FALSE)) { + if (value == p_scb->callsetup_ind) { + return; + } + p_scb->callsetup_ind = (UINT8)value; + } + if ((id == BTA_AG_IND_SERVICE) && (on_demand == FALSE)) { + if (value == p_scb->service_ind) { + return; + } + p_scb->service_ind = (UINT8)value; + } + if ((id == BTA_AG_IND_SIGNAL) && (on_demand == FALSE)) { + if (value == p_scb->signal_ind) { + return; + } + p_scb->signal_ind = (UINT8)value; + } + if ((id == BTA_AG_IND_ROAM) && (on_demand == FALSE)) { + if (value == p_scb->roam_ind) { + return; + } + p_scb->roam_ind = (UINT8)value; + } + if ((id == BTA_AG_IND_BATTCHG) && (on_demand == FALSE)) { + if (value == p_scb->battchg_ind) { + return; + } + p_scb->battchg_ind = (UINT8)value; + } + if ((id == BTA_AG_IND_CALLHELD) && (on_demand == FALSE)) { + /* call swap could result in sending callheld=1 multiple times */ + if ((value != 1) && (value == p_scb->callheld_ind)) { + return; + } + p_scb->callheld_ind = (UINT8)value; + } + if (p_scb->cmer_enabled) { + p += utl_itoa(id, p); + *p++ = ','; + utl_itoa(value, p); + bta_ag_send_result(p_scb, BTA_AG_RES_CIEV, str, 0); + } +} + +/******************************************************************************* +** +** Function bta_ag_parse_cmer +** +** Description Parse AT+CMER parameter string. +** +** +** Returns TRUE if parsed ok, FALSE otherwise. +** +*******************************************************************************/ +static BOOLEAN bta_ag_parse_cmer(char *p_s, BOOLEAN *p_enabled) +{ + INT16 n[4] = {-1, -1, -1, -1}; + int i; + char *p; + + for (i = 0; i < 4; i++) { + /* skip to comma delimiter */ + for (p = p_s; *p != ',' && *p != 0; p++); + /* get integer value */ + *p = 0; + n[i] = utl_str2int(p_s); + p_s = p + 1; + if (p_s == 0) { + break; + } + } + /* process values */ + if (n[0] < 0 || n[3] < 0) { + return FALSE; + } + if ((n[0] == 3) && ((n[3] == 1) || (n[3] == 0))) { + *p_enabled = (BOOLEAN) n[3]; + } + return TRUE; +} + +/******************************************************************************* +** +** Function bta_ag_parse_chld +** +** Description Parse AT+CHLD parameter string. +** +** +** Returns Returns idx (1-7), 0 if ECC not enabled or BTA_AG_INVALID_CHLD + if idx doesn't exist/1st character of argument is not a digit +** +*******************************************************************************/ +static UINT8 bta_ag_parse_chld(tBTA_AG_SCB *p_scb, char *p_s) +{ + UINT8 retval = 0; + INT16 idx = -1; + UNUSED(p_scb); + + if (!isdigit((unsigned char)p_s[0])) { + return BTA_AG_INVALID_CHLD; + } + + if (p_s[1] != 0) { + /* p_idxstr++; point to beginning of call number */ + idx = utl_str2int(&p_s[1]); + if (idx != -1 && idx < 255) { + retval = (UINT8)idx; + } else { + retval = BTA_AG_INVALID_CHLD; + } + } + return(retval); +} + +#if (BTM_WBS_INCLUDED == TRUE) +/******************************************************************************* +** +** Function bta_ag_parse_bac +** +** Description Parse AT+BAC parameter string. +** +** Returns Returns bitmap of supported codecs. +** +*******************************************************************************/ +static tBTA_AG_PEER_CODEC bta_ag_parse_bac(tBTA_AG_SCB *p_scb, char *p_s) +{ + tBTA_AG_PEER_CODEC retval = BTA_AG_CODEC_NONE; + UINT16 uuid_codec; + BOOLEAN cont = FALSE; /* Continue processing */ + char *p; + + while (p_s) { + /* skip to comma delimiter */ + for (p = p_s; *p != ',' && *p != 0; p++); + + /* get integre value */ + if (*p != 0) { + *p = 0; + cont = TRUE; + } else { + cont = FALSE; + } + uuid_codec = utl_str2int(p_s); + switch(uuid_codec) { + case UUID_CODEC_CVSD: + retval |= BTA_AG_CODEC_CVSD; + break; + + case UUID_CODEC_MSBC: + retval |= BTA_AG_CODEC_MSBC; + break; + + default: + APPL_TRACE_ERROR("Unknown Codec UUID(%d) received", uuid_codec); + return BTA_AG_CODEC_NONE; + } + if (cont) { + p_s = p + 1; + } + else { + break; + } + } + return (retval); +} +#endif /* #if (BTM_WBS_INCLUDED == TRUE ) */ + +/******************************************************************************* +** +** Function bta_ag_process_unat_res +** +** Description Process the unat response data and remove extra carriage return +** and line feed +** +** +** Returns void +** +*******************************************************************************/ +static void bta_ag_process_unat_res(char *unat_result) +{ + UINT8 str_leng; + UINT8 i = 0; + UINT8 j = 0; + UINT8 pairs_of_nl_cr; + char trim_data[BTA_AG_AT_MAX_LEN]; + + str_leng = strlen(unat_result); + /* If no extra CR and LF, just return */ + if(str_leng < 4) { + return; + } + + /* Remove the carriage return and left feed */ + while(unat_result[0] =='\r' && unat_result[1] =='\n' + && unat_result[str_leng-2] =='\r' && unat_result[str_leng-1] =='\n') { + pairs_of_nl_cr = 1; + for (i=0;i<(str_leng-4*pairs_of_nl_cr);i++) { + trim_data[j++] = unat_result[i+pairs_of_nl_cr*2]; + } + /* Add EOF */ + trim_data[j] = '\0'; + str_leng = str_leng - 4; + BCM_STRNCPY_S(unat_result, trim_data, BTA_AG_AT_MAX_LEN); + i=0; + j=0; + if(str_leng <4) { + return; + } + } + return; +} + +/******************************************************************************* +** +** Function bta_ag_inband_enabled +** +** Description Determine whether in-band ring can be used. +** +** +** Returns void +** +*******************************************************************************/ +BOOLEAN bta_ag_inband_enabled(tBTA_AG_SCB *p_scb) +{ + /* if feature is enabled and no other scbs connected */ + if (p_scb->inband_enabled && !bta_ag_other_scb_open(p_scb)) { + return TRUE; + } else { + return FALSE; + } +} + +/******************************************************************************* +** +** Function bta_ag_send_call_inds +** +** Description Send call and callsetup indicators. +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_send_call_inds(tBTA_AG_SCB *p_scb, tBTA_AG_RES result) +{ + UINT8 call = p_scb->call_ind; + UINT8 callsetup; + /* set new call and callsetup values based on BTA_AgResult */ + callsetup = bta_ag_callsetup_ind_tbl[result]; + + if (result == BTA_AG_END_CALL_RES) { + call = BTA_AG_CALL_INACTIVE; + } else if (result == BTA_AG_IN_CALL_CONN_RES || result == BTA_AG_OUT_CALL_CONN_RES + || result == BTA_AG_IN_CALL_HELD_RES) { + call = BTA_AG_CALL_ACTIVE; + } else { + call = p_scb->call_ind; + } + /* Send indicator function tracks if the values have actually changed */ + bta_ag_send_ind(p_scb, BTA_AG_IND_CALL, call, FALSE); + bta_ag_send_ind(p_scb, BTA_AG_IND_CALLSETUP, callsetup, FALSE); +} + +/******************************************************************************* +** +** Function bta_ag_at_hsp_cback +** +** Description AT command processing callback for HSP. +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_at_hsp_cback(tBTA_AG_SCB *p_scb, UINT16 cmd, UINT8 arg_type, + char *p_arg, INT16 int_arg) +{ + tBTA_AG_VAL val; + APPL_TRACE_DEBUG("AT cmd:%d arg_type:%d arg:%d arg:%s", cmd, arg_type, int_arg, p_arg); + + /* send OK */ + bta_ag_send_ok(p_scb); + val.hdr.handle = bta_ag_scb_to_idx(p_scb); + val.hdr.app_id = p_scb->app_id; + val.num = (UINT16) int_arg; + BCM_STRNCPY_S(val.str, p_arg, BTA_AG_AT_MAX_LEN); + val.str[BTA_AG_AT_MAX_LEN] = '\0'; + /* call callback with event */ + (*bta_ag_cb.p_cback)(bta_ag_hsp_cb_evt[cmd], (tBTA_AG *) &val); +} + +/******************************************************************************* +** +** Function bta_ag_at_hfp_cback +** +** Description AT command processing callback for HFP. +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_at_hfp_cback(tBTA_AG_SCB *p_scb, UINT16 cmd, UINT8 arg_type, + char *p_arg, INT16 int_arg) +{ + tBTA_AG_VAL val; + tBTA_AG_EVT event; + tBTA_AG_SCB *ag_scb; + UINT32 i, ind_id; + UINT32 bia_masked_out; +#if (BTM_WBS_INCLUDED == TRUE) + tBTA_AG_PEER_CODEC codec_type, codec_sent; +#endif + if (p_arg == NULL) { + APPL_TRACE_ERROR("%s: p_arg is null, send error and return", __func__); + bta_ag_send_error(p_scb, BTA_AG_ERR_INV_CHAR_IN_TSTR); + return; + } + APPL_TRACE_DEBUG("HFP AT cmd:%d arg_type:%d arg:%d arg:%s", cmd, arg_type, int_arg, p_arg); + + val.hdr.handle = bta_ag_scb_to_idx(p_scb); + val.hdr.app_id = p_scb->app_id; + val.num = int_arg; + bdcpy(val.bd_addr, p_scb->peer_addr); + BCM_STRNCPY_S(val.str, p_arg, BTA_AG_AT_MAX_LEN); + val.str[BTA_AG_AT_MAX_LEN] = '\0'; + event = bta_ag_hfp_cb_evt[cmd]; + + switch (cmd) + { + case BTA_AG_HF_CMD_A: + case BTA_AG_HF_CMD_VGS: + case BTA_AG_HF_CMD_VGM: + case BTA_AG_HF_CMD_CHUP: + case BTA_AG_HF_CMD_CBC: + /* send OK */ + bta_ag_send_ok(p_scb); + break; + + case BTA_AG_HF_CMD_BLDN: + /* Do not send OK, App will send error or OK depending on last-dial-umber enabled or not */ + break; + + case BTA_AG_HF_CMD_D: + { + UINT16 src = 0; + UINT16 dst = 0; + /* Do not send OK for Dial cmds Let application decide whether to send OK or ERROR*/ + /* if mem dial cmd, make sure string contains only digits */ + if(p_arg[0] == '>') { + if(!utl_isintstr(p_arg+1)) { + event = 0; + bta_ag_send_error(p_scb, BTA_AG_ERR_INV_CHAR_IN_DSTR); + } + val.value = BTA_AG_HF_DIAL_MEM; + src = 1; + } else if (p_arg[0] == 'V') { + /* ATDV : Dial VoIP Call */ + /* We do not check string. Code will be added later if needed. */ + if(!((p_scb->peer_features & BTA_AG_PEER_FEAT_VOIP) && (p_scb->features & BTA_AG_FEAT_VOIP))) { + event = 0; + bta_ag_send_error(p_scb, BTA_AG_ERR_OP_NOT_SUPPORTED); + } + val.value = BTA_AG_HF_DIAL_VOIP; + } else { + /* If dial cmd, make sure string contains only dial digits + ** Dial digits are 0-9, A-C, *, #, + */ + if(!utl_isdialstr(p_arg)) { + event = 0; + bta_ag_send_error(p_scb, BTA_AG_ERR_INV_CHAR_IN_DSTR); + } + val.value = BTA_AG_HF_DIAL_NUM; + } + if (event != 0) { + while ((val.str[dst] = p_arg[src]) != '\0') { + if (val.str[dst] == ';') { + val.str[dst] = '\0'; + break; + } + src++; + dst++; + } + } + break; + } + + case BTA_AG_HF_CMD_CCWA: + p_scb->ccwa_enabled = (BOOLEAN) int_arg; /* store setting */ + bta_ag_send_ok(p_scb); /* send OK */ + break; + + case BTA_AG_HF_CMD_CHLD: + { + if (arg_type == BTA_AG_AT_TEST) { + /* don't call callback */ + event = 0; + /* send CHLD string */ + /* Form string based on supported 1.5 feature */ + if ((p_scb->peer_version >= HFP_VERSION_1_5) && + (p_scb->features & BTA_AG_FEAT_ECC) && + (p_scb->peer_features & BTA_AG_PEER_FEAT_ECC)) { + bta_ag_send_result(p_scb, BTA_AG_RES_CHLD, p_bta_ag_cfg->chld_val_ecc, 0); + } else { + bta_ag_send_result(p_scb, BTA_AG_RES_CHLD, p_bta_ag_cfg->chld_val, 0); + } + /* send OK */ + bta_ag_send_ok(p_scb); + /* if service level conn. not already open, now it's open */ + bta_ag_svc_conn_open(p_scb, NULL); + } else { + val.idx = bta_ag_parse_chld(p_scb, val.str); + if (val.idx == BTA_AG_INVALID_CHLD) { + event = 0; + bta_ag_send_error(p_scb, BTA_AG_ERR_OP_NOT_SUPPORTED); + break; + } + if(val.idx && !((p_scb->features & BTA_AG_FEAT_ECC) && (p_scb->peer_features & BTA_AG_PEER_FEAT_ECC))) { + /* we do not support ECC, but HF is sending us a CHLD with call index*/ + event = 0; + bta_ag_send_error(p_scb, BTA_AG_ERR_OP_NOT_SUPPORTED); + + } else { + /* If it is swap between calls, set call held indicator to 3(out of valid 0-2) + ** Application will set it back to 1 + ** callheld indicator will be sent across to the peer. */ + if(val.str[0] == '2') { + for (i = 0, ag_scb = &bta_ag_cb.scb[0]; i < BTA_AG_NUM_SCB; i++, ag_scb++) { + if (ag_scb->in_use) { + if((ag_scb->call_ind == BTA_AG_CALL_ACTIVE) + && (ag_scb->callsetup_ind == BTA_AG_CALLSETUP_NONE)) { + ag_scb->callheld_ind = BTA_AG_CALLHELD_NOACTIVE + 1; + } + } + } + } + } + /* Do not send OK. Let app decide after parsing the val str */ + /* bta_ag_send_ok(p_scb); */ + } + break; + } + + case BTA_AG_HF_CMD_CIND: + if (arg_type == BTA_AG_AT_TEST) { + /* don't call callback */ + event = 0; + /* send CIND string, send OK */ + bta_ag_send_result(p_scb, BTA_AG_RES_CIND, p_bta_ag_cfg->cind_info, 0); + bta_ag_send_ok(p_scb); + } + break; + + case BTA_AG_HF_CMD_CLIP: + /* store setting, send OK */ + p_scb->clip_enabled = (BOOLEAN) int_arg; + bta_ag_send_ok(p_scb); + break; + + case BTA_AG_HF_CMD_CMER: + /* if parsed ok store setting, send OK */ + if (bta_ag_parse_cmer(p_arg, &p_scb->cmer_enabled)) { + bta_ag_send_ok(p_scb); + /* if service level conn. not already open and our features and + ** peer features do not have 3-way, service level conn. now open + */ + if (!p_scb->svc_conn && !((p_scb->features & BTA_AG_FEAT_3WAY) && (p_scb->peer_features & BTA_AG_PEER_FEAT_3WAY))) { + bta_ag_svc_conn_open(p_scb, NULL); + } + } else { + bta_ag_send_error(p_scb, BTA_AG_ERR_INV_CHAR_IN_TSTR); + } + break; + + case BTA_AG_HF_CMD_VTS: + /* check argument */ + if (strlen(p_arg) == 1) { + bta_ag_send_ok(p_scb); + } else { + event = 0; + bta_ag_send_error(p_scb, BTA_AG_ERR_INV_CHAR_IN_TSTR); + } + break; + + case BTA_AG_HF_CMD_BINP: + /* if feature not set don't call callback, send ERROR */ + if (!(p_scb->features & BTA_AG_FEAT_VTAG)) { + event = 0; + bta_ag_send_error(p_scb, BTA_AG_ERR_OP_NOT_SUPPORTED); + } + break; + + case BTA_AG_HF_CMD_BVRA: + /* if feature not supported don't call callback, send ERROR. App will send OK */ + if (!(p_scb->features & BTA_AG_FEAT_VREC)) { + event = 0; + bta_ag_send_error(p_scb, BTA_AG_ERR_OP_NOT_SUPPORTED); + } else { + bta_ag_send_ok(p_scb); + } + break; + + case BTA_AG_HF_CMD_BRSF: + { + /* store peer features */ + p_scb->peer_features = (UINT16) int_arg; + /* send BRSF, send OK */ +#if BT_HF_AG_BQB_INCLUDED + if (s_bta_hf_ag_bqb_brsf_flag == true) { + bta_ag_send_result(p_scb, BTA_AG_RES_BRSF, NULL, (INT16) (p_scb->features & BTA_AG_BQB_BRSF_FEAT_SPEC)); + } else { + bta_ag_send_result(p_scb, BTA_AG_RES_BRSF, NULL, (INT16) (p_scb->features & BTA_AG_BRSF_FEAT_SPEC)); + } +#else + bta_ag_send_result(p_scb, BTA_AG_RES_BRSF, NULL, (INT16) (p_scb->features & BTA_AG_BRSF_FEAT_SPEC)); +#endif /* BT_HF_AG_BQB_INCLUDED */ + bta_ag_send_ok(p_scb); + break; + } + + case BTA_AG_HF_CMD_NREC: + /* if feature send OK, else don't call callback, send ERROR */ + if (p_scb->features & BTA_AG_FEAT_ECNR) { + bta_ag_send_ok(p_scb); + } else { + event = 0; + bta_ag_send_error(p_scb, BTA_AG_ERR_OP_NOT_SUPPORTED); + } + break; + + case BTA_AG_HF_CMD_BTRH: + /* if feature send BTRH, send OK:, else don't call callback, send ERROR */ + if (p_scb->features & BTA_AG_FEAT_BTRH) { + /* If set command; send response and notify app */ + if (arg_type == BTA_AG_AT_SET) { + for (i = 0, ag_scb = &bta_ag_cb.scb[0]; i < BTA_AG_NUM_SCB; i++, ag_scb++) { + if (ag_scb->in_use) { + bta_ag_send_result(ag_scb, BTA_AG_RES_BTRH, NULL, int_arg); + } + } + bta_ag_send_ok(p_scb); + } else { + /* Read Command */ + val.num = BTA_AG_BTRH_READ; + } + } else { + event = 0; + bta_ag_send_error(p_scb, BTA_AG_ERR_OP_NOT_SUPPORTED); + } + break; + + case BTA_AG_HF_CMD_COPS: + if (arg_type == BTA_AG_AT_SET) { + /* don't call callback */ + event = 0; + /* send OK */ + bta_ag_send_ok(p_scb); + } + break; + + case BTA_AG_HF_CMD_CMEE: + if (p_scb->features & BTA_AG_FEAT_EXTERR) { + /* store setting */ + p_scb->cmee_enabled = (BOOLEAN) int_arg; + /* send OK */ + bta_ag_send_ok(p_scb); + } else { + bta_ag_send_error(p_scb, BTA_AG_ERR_OP_NOT_SUPPORTED); + } + /* don't call callback */ + event = 0; + break; + + case BTA_AG_HF_CMD_BIA: + { + /* don't call callback */ + event = 0; + bia_masked_out = p_scb->bia_masked_out; + /* Parse the indicator mask */ + for (i = 0, ind_id = 1; (val.str[i] != 0) && (ind_id <= 20); i++, ind_id++) { + if (val.str[i] == ',') + continue; + if (val.str[i] == '0') + bia_masked_out |= ((UINT32)1 << ind_id); + else if (val.str[i] == '1') + bia_masked_out &= ~((UINT32)1 << ind_id); + else + break; + i++; + if ( (val.str[i] != 0) && (val.str[i] != ',') ) + break; + } + if (val.str[i] == 0) { + p_scb->bia_masked_out = bia_masked_out; + bta_ag_send_ok (p_scb); + } else { + bta_ag_send_error (p_scb, BTA_AG_ERR_INVALID_INDEX); + } + break; + } + + case BTA_AG_HF_CMD_CNUM: + if(!(p_scb->features & BTA_AG_FEAT_ECS)) { + event = 0; + bta_ag_send_error(p_scb, BTA_AG_ERR_OP_NOT_SUPPORTED); + } + break; + + case BTA_AG_HF_CMD_CLCC: + if(!(p_scb->features & BTA_AG_FEAT_ECS)) { + event = 0; + bta_ag_send_error(p_scb, BTA_AG_ERR_OP_NOT_SUPPORTED); + } + break; + +#if (BTM_WBS_INCLUDED == TRUE) + case BTA_AG_HF_CMD_BAC: + { + bta_ag_send_ok(p_scb); + /* store available codecs from the peer */ + if((p_scb->peer_features & BTA_AG_PEER_FEAT_CODEC) && (p_scb->features & BTA_AG_FEAT_CODEC)) { + p_scb->peer_codecs = bta_ag_parse_bac(p_scb, p_arg); + p_scb->codec_updated = TRUE; + + if (p_scb->peer_codecs & BTA_AG_CODEC_MSBC) { + p_scb->sco_codec = UUID_CODEC_MSBC; + APPL_TRACE_DEBUG("Received AT+BAC, updating sco codec to MSBC"); + } else { + p_scb->sco_codec = UUID_CODEC_CVSD; + APPL_TRACE_DEBUG("Received AT+BAC, updating sco codec to CVSD"); + } + /* The above logic sets the stack preferred codec based on local and peer codec + capabilities. This can be overridden by the application depending on its preference + using the bta_ag_setcodec API. We send the peer_codecs to the application. */ + val.num = p_scb->peer_codecs; + /* Received BAC while in codec negotiation. */ + if ((bta_ag_cb.sco.state == BTA_AG_SCO_CODEC_ST) && (bta_ag_cb.sco.p_curr_scb == p_scb)) { + bta_ag_codec_negotiate (p_scb); + } + } else { + p_scb->peer_codecs = BTA_AG_CODEC_NONE; + APPL_TRACE_ERROR("Unexpected CMD:AT+BAC, Codec Negotiation is not supported"); + } + break; + } + + case BTA_AG_HF_CMD_BCS: + { + bta_ag_send_ok(p_scb); + /* stop cn timer */ + bta_sys_stop_timer(&p_scb->cn_timer); + + switch(int_arg) { + case UUID_CODEC_CVSD: + codec_type = BTA_AG_CODEC_CVSD; + break; + + case UUID_CODEC_MSBC: + codec_type = BTA_AG_CODEC_MSBC; + break; + + default: + APPL_TRACE_ERROR("Unknown codec_uuid %d", int_arg); + codec_type = 0xFFFF; + break; + } + + if (p_scb->codec_fallback) { + codec_sent = BTA_AG_CODEC_CVSD; + } else { + codec_sent = p_scb->sco_codec; + } + + if(codec_type == codec_sent) { + bta_ag_sco_codec_nego(p_scb, TRUE); + } else { + bta_ag_sco_codec_nego(p_scb, FALSE); + } + /* send final codec info to callback */ + val.num = codec_sent; + break; + } + + case BTA_AG_HF_CMD_BCC: + { + bta_ag_send_ok(p_scb); + bta_ag_sco_open(p_scb, NULL); + break; + } +#endif + default: + bta_ag_send_error(p_scb, BTA_AG_ERR_OP_NOT_SUPPORTED); + break; + } + /* call callback */ + if (event != 0) { + (*bta_ag_cb.p_cback)(event, (tBTA_AG *) &val); + } +} + +/******************************************************************************* +** +** Function bta_ag_at_err_cback +** +** Description AT command parser error callback. +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_at_err_cback(tBTA_AG_SCB *p_scb, BOOLEAN unknown, char *p_arg) +{ + tBTA_AG_VAL val; + + if(unknown && (!strlen(p_arg))) { + APPL_TRACE_DEBUG("Empty AT cmd string received"); + bta_ag_send_ok(p_scb); + return; + } + + /* if unknown AT command and configured to pass these to app */ + if (unknown && (p_scb->features & BTA_AG_FEAT_UNAT)) { + val.hdr.handle = bta_ag_scb_to_idx(p_scb); + val.hdr.app_id = p_scb->app_id; + val.num = 0; + BCM_STRNCPY_S(val.str, p_arg, BTA_AG_AT_MAX_LEN); + val.str[BTA_AG_AT_MAX_LEN] = '\0'; + (*bta_ag_cb.p_cback)(BTA_AG_AT_UNAT_EVT, (tBTA_AG *) &val); + } else { + bta_ag_send_error(p_scb, BTA_AG_ERR_OP_NOT_SUPPORTED); + } +} + +/******************************************************************************* +** +** Function bta_ag_hsp_result +** +** Description Handle API result for HSP connections. +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_hsp_result(tBTA_AG_SCB *p_scb, tBTA_AG_API_RESULT *p_result) +{ + UINT8 code = bta_ag_trans_result[p_result->result]; + + APPL_TRACE_DEBUG("bta_ag_hsp_result : res = %d", p_result->result); + + switch(p_result->result) { + case BTA_AG_SPK_RES: + case BTA_AG_MIC_RES: + bta_ag_send_result(p_scb, code, NULL, p_result->data.num); + break; + + case BTA_AG_IN_CALL_RES: + { + /* tell sys to stop av if any */ + bta_sys_sco_use(BTA_ID_AG, p_scb->app_id, p_scb->peer_addr); + /* if sco already opened or no inband ring send ring now */ + if (bta_ag_sco_is_open(p_scb) || !bta_ag_inband_enabled(p_scb) || + (p_scb->features & BTA_AG_FEAT_NOSCO)) { + bta_ag_send_ring(p_scb, (tBTA_AG_DATA *) p_result); + } else { + /* else open sco, send ring after sco opened */ + /* HSPv1.2: AG shall not send RING if using in-band ring tone. */ + if (p_scb->hsp_version >= HSP_VERSION_1_2) { + p_scb->post_sco = BTA_AG_POST_SCO_NONE; + } else { + p_scb->post_sco = BTA_AG_POST_SCO_RING; + } + bta_ag_sco_open(p_scb, (tBTA_AG_DATA *) p_result); + } + break; + } + + case BTA_AG_IN_CALL_CONN_RES: + case BTA_AG_OUT_CALL_ORIG_RES: + { + /* if incoming call connected stop ring timer */ + if (p_result->result == BTA_AG_IN_CALL_CONN_RES) { + bta_sys_stop_timer(&p_scb->act_timer); + } + + if (!(p_scb->features & BTA_AG_FEAT_NOSCO)) { + /* if audio connected to this scb open sco */ + if (p_result->data.audio_handle == bta_ag_scb_to_idx(p_scb)) { + bta_ag_sco_open(p_scb, (tBTA_AG_DATA *) p_result); + } else if (p_result->data.audio_handle == BTA_AG_HANDLE_NONE) { + /* else if no audio at call close sco */ + bta_ag_sco_close(p_scb, (tBTA_AG_DATA *) p_result); + } + } + break; + } + + case BTA_AG_END_CALL_RES: + { + /* stop ring timer */ + bta_sys_stop_timer(&p_scb->act_timer); + /* close sco */ + if ((bta_ag_sco_is_open(p_scb) || bta_ag_sco_is_opening(p_scb)) && !(p_scb->features & BTA_AG_FEAT_NOSCO)) { + bta_ag_sco_close(p_scb, (tBTA_AG_DATA *) p_result); + } else { + /* if av got suspended by this call, let it resume. */ + bta_sys_sco_unuse(BTA_ID_AG, p_scb->app_id, p_scb->peer_addr); + } + break; + } + + case BTA_AG_INBAND_RING_RES: + p_scb->inband_enabled = p_result->data.state; + APPL_TRACE_DEBUG("inband_enabled set to %d", p_scb->inband_enabled); + break; + + case BTA_AG_UNAT_RES: + { + if (p_result->data.ok_flag != BTA_AG_OK_ERROR) { + if (p_result->data.str[0] != 0) { + bta_ag_send_result(p_scb, code, p_result->data.str, 0); + } + if (p_result->data.ok_flag == BTA_AG_OK_DONE) { + bta_ag_send_ok(p_scb); + } + } else { + bta_ag_send_error(p_scb, BTA_AG_ERR_INV_CHAR_IN_TSTR); + } + break; + } + default: + break; /* ignore all others */ + } +} + +/******************************************************************************* +** +** Function bta_ag_hfp_result +** +** Description Handle API result for HFP connections. +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_hfp_result(tBTA_AG_SCB *p_scb, tBTA_AG_API_RESULT *p_result) +{ + UINT8 code = bta_ag_trans_result[p_result->result]; + + APPL_TRACE_DEBUG("bta_ag_hfp_result : res = %d", p_result->result); + + switch(p_result->result) + { + case BTA_AG_SPK_RES: + case BTA_AG_MIC_RES: + bta_ag_send_result(p_scb, code, NULL, p_result->data.num); + break; + + case BTA_AG_IN_CALL_RES: + { + /* tell sys to stop av if any */ + bta_sys_sco_use(BTA_ID_AG, p_scb->app_id, p_scb->peer_addr); + /* store caller id string. + * append type info at the end. + * make sure a valid type info is passed. + * otherwise add 129 as default type */ + if ((p_result->data.num < BTA_AG_CLIP_TYPE_MIN) || (p_result->data.num > BTA_AG_CLIP_TYPE_MAX)) { + if (p_result->data.num != BTA_AG_CLIP_TYPE_VOIP) { + p_result->data.num = BTA_AG_CLIP_TYPE_DEFAULT; + } + } + APPL_TRACE_DEBUG("CLIP type :%d", p_result->data.num); + p_scb->clip[0] = '\0'; + if (p_result->data.str[0] != 0) { + snprintf(p_scb->clip, sizeof(p_scb->clip), "%s,%d", p_result->data.str, p_result->data.num); + } + /* send callsetup indicator */ + if (p_scb->post_sco == BTA_AG_POST_SCO_CALL_END) { + /* Need to sent 2 callsetup IND's(Call End and Incoming call) after SCO close. */ + p_scb->post_sco = BTA_AG_POST_SCO_CALL_END_INCALL; + } else { + bta_ag_send_call_inds(p_scb, p_result->result); + /* if sco already opened or no inband ring send ring now */ + if (bta_ag_sco_is_open(p_scb) || !bta_ag_inband_enabled(p_scb) || + (p_scb->features & BTA_AG_FEAT_NOSCO)) { + bta_ag_send_ring(p_scb, (tBTA_AG_DATA *) p_result); + } else { + /* else open sco, send ring after sco opened */ + p_scb->post_sco = BTA_AG_POST_SCO_RING; + bta_ag_sco_open(p_scb, (tBTA_AG_DATA *) p_result); + } + } + break; + } + + case BTA_AG_IN_CALL_CONN_RES: + { + /* stop ring timer */ + bta_sys_stop_timer(&p_scb->act_timer); + /* if sco not opened and we need to open it, send indicators first + ** then open sco. */ + bta_ag_send_call_inds(p_scb, p_result->result); + + if (!(p_scb->features & BTA_AG_FEAT_NOSCO)) { + if (p_result->data.audio_handle == bta_ag_scb_to_idx(p_scb)) { + bta_ag_sco_open(p_scb, (tBTA_AG_DATA *) p_result); + } else if ((p_result->data.audio_handle == BTA_AG_HANDLE_NONE) && bta_ag_sco_is_open(p_scb)) { + bta_ag_sco_close(p_scb, (tBTA_AG_DATA *) p_result); + } + } + break; + } + + case BTA_AG_IN_CALL_HELD_RES: + /* stop ring timer */ + bta_sys_stop_timer(&p_scb->act_timer); + bta_ag_send_call_inds(p_scb, p_result->result); + break; + + case BTA_AG_OUT_CALL_ORIG_RES: + bta_ag_send_call_inds(p_scb, p_result->result); + if (p_result->data.audio_handle == bta_ag_scb_to_idx(p_scb) && !(p_scb->features & BTA_AG_FEAT_NOSCO)) { + bta_ag_sco_open(p_scb, (tBTA_AG_DATA *) p_result); + } + break; + + case BTA_AG_OUT_CALL_ALERT_RES: + /* send indicators */ + bta_ag_send_call_inds(p_scb, p_result->result); + if (p_result->data.audio_handle == bta_ag_scb_to_idx(p_scb) && !(p_scb->features & BTA_AG_FEAT_NOSCO)) { + bta_ag_sco_open(p_scb, (tBTA_AG_DATA *) p_result); + } + break; + + case BTA_AG_MULTI_CALL_RES: + /* open SCO at SLC for this three way call */ + APPL_TRACE_DEBUG("Headset Connected in three way call"); + if (!(p_scb->features & BTA_AG_FEAT_NOSCO)) { + if (p_result->data.audio_handle == bta_ag_scb_to_idx(p_scb)) { + bta_ag_sco_open(p_scb, (tBTA_AG_DATA *) p_result); + } else if (p_result->data.audio_handle == BTA_AG_HANDLE_NONE) { + bta_ag_sco_close(p_scb, (tBTA_AG_DATA *) p_result); + } + } + break; + + case BTA_AG_OUT_CALL_CONN_RES: + /* send indicators */ + bta_ag_send_call_inds(p_scb, p_result->result); + /* open or close sco */ + if (!(p_scb->features & BTA_AG_FEAT_NOSCO)) { + if (p_result->data.audio_handle == bta_ag_scb_to_idx(p_scb)) { + bta_ag_sco_open(p_scb, (tBTA_AG_DATA *) p_result); + } else if (p_result->data.audio_handle == BTA_AG_HANDLE_NONE) { + bta_ag_sco_close(p_scb, (tBTA_AG_DATA *) p_result); + } + } + break; + + case BTA_AG_CALL_CANCEL_RES: + /* send indicators */ + bta_ag_send_call_inds(p_scb, p_result->result); + break; + + case BTA_AG_END_CALL_RES: + /* stop ring timer */ + bta_sys_stop_timer(&p_scb->act_timer); + /* if sco open, close sco then send indicator values */ + if ((bta_ag_sco_is_open(p_scb) || bta_ag_sco_is_opening(p_scb)) && !(p_scb->features & BTA_AG_FEAT_NOSCO)) { + p_scb->post_sco = BTA_AG_POST_SCO_CALL_END; + bta_ag_sco_close(p_scb, (tBTA_AG_DATA *) p_result); + } else if (p_scb->post_sco == BTA_AG_POST_SCO_CALL_END_INCALL) { + /* sco closing for outgoing call because of incoming call */ + /* Send only callsetup end indicator after sco close */ + p_scb->post_sco = BTA_AG_POST_SCO_CALL_END; + } else { + bta_ag_send_call_inds(p_scb, p_result->result); + /* if av got suspended by this call, let it resume. */ + bta_sys_sco_unuse(BTA_ID_AG, p_scb->app_id, p_scb->peer_addr); + } + break; + + case BTA_AG_INBAND_RING_RES: + p_scb->inband_enabled = p_result->data.state; + APPL_TRACE_DEBUG("inband_enabled set to %d", p_scb->inband_enabled); + bta_ag_send_result(p_scb, code, NULL, p_result->data.state); + break; + + case BTA_AG_CIND_RES: + { + /* store local values */ + p_scb->call_ind = p_result->data.str[0] - '0'; + p_scb->callsetup_ind = p_result->data.str[2] - '0'; + p_scb->service_ind = p_result->data.str[4] - '0'; + p_scb->signal_ind = p_result->data.str[6] - '0'; + p_scb->roam_ind = p_result->data.str[8] - '0'; + p_scb->battchg_ind = p_result->data.str[10] - '0'; + p_scb->callheld_ind = p_result->data.str[12] - '0'; + + APPL_TRACE_DEBUG("cind call:%d callsetup:%d", p_scb->call_ind, p_scb->callsetup_ind); + bta_ag_send_result(p_scb, code, p_result->data.str, 0); + bta_ag_send_ok(p_scb); + break; + } + + case BTA_AG_BINP_RES: // Not supported yet + case BTA_AG_CNUM_RES: + case BTA_AG_CLCC_RES: + case BTA_AG_COPS_RES: + { + if (p_result->data.ok_flag != BTA_AG_OK_ERROR) { + if (p_result->data.str[0] != 0) { + bta_ag_send_result(p_scb, code, p_result->data.str, 0); + } + if (p_result->data.ok_flag == BTA_AG_OK_DONE) { + bta_ag_send_ok(p_scb); + } + } else { + bta_ag_send_error(p_scb, p_result->data.errcode); + } + break; + } + + case BTA_AG_UNAT_RES: + if (p_result->data.ok_flag != BTA_AG_OK_ERROR) { + if (p_result->data.str[0] != 0) { + bta_ag_process_unat_res(p_result->data.str); + APPL_TRACE_DEBUG("BTA_AG_RES :%s",p_result->data.str); + bta_ag_send_result(p_scb, code, p_result->data.str, 0); + } + + if (p_result->data.ok_flag == BTA_AG_OK_DONE) { + bta_ag_send_ok(p_scb); + } + } else { + bta_ag_send_error(p_scb, p_result->data.errcode); + } + break; + + case BTA_AG_CALL_WAIT_RES: + if (p_scb->ccwa_enabled) { + bta_ag_send_result(p_scb, code, p_result->data.str, 0); + } + bta_ag_send_call_inds(p_scb, p_result->result); + break; + + case BTA_AG_IND_RES: + bta_ag_send_ind(p_scb, p_result->data.ind.type, p_result->data.ind.value, FALSE); + break; + + case BTA_AG_BVRA_RES: + bta_ag_send_result(p_scb, code, NULL, p_result->data.state); + if (p_result->data.ok_flag!= BTA_AG_OK_ERROR) + { + bta_ag_send_ok(p_scb); + } else { + bta_ag_send_error(p_scb, p_result->data.errcode); + } + break; + + case BTA_AG_BTRH_RES: // Not supported yet + if (p_result->data.ok_flag != BTA_AG_OK_ERROR) { + /* Don't respond to read if not in response & hold state */ + if (p_result->data.num != BTA_AG_BTRH_NO_RESP) { + bta_ag_send_result(p_scb, code, NULL, p_result->data.num); + } + /* In case of a response to a read request we need to send OK */ + if (p_result->data.ok_flag == BTA_AG_OK_DONE) { + bta_ag_send_ok(p_scb); + } + } else { + bta_ag_send_error(p_scb, p_result->data.errcode); + } + break; + + default: + break; + } +} + + +/******************************************************************************* +** +** Function bta_ag_result +** +** Description Handle API result. +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_result(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data) +{ + if (p_scb->conn_service == BTA_AG_HSP) { + bta_ag_hsp_result(p_scb, &p_data->api_result); + } else { + bta_ag_hfp_result(p_scb, &p_data->api_result); + } +} + +/******************************************************************************* +** +** Function bta_ag_send_bcs +** +** Description Send +BCS AT command to peer. +** +** Returns void +** +*******************************************************************************/ +#if (BTM_WBS_INCLUDED == TRUE ) +void bta_ag_send_bcs(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data) +{ + UINT16 codec_uuid; + + if (p_scb->codec_fallback) { + codec_uuid = UUID_CODEC_CVSD; + } else { + switch(p_scb->sco_codec) { + case BTA_AG_CODEC_NONE: + codec_uuid = UUID_CODEC_CVSD; + break; + + case BTA_AG_CODEC_CVSD: + codec_uuid = UUID_CODEC_CVSD; + break; + + case BTA_AG_CODEC_MSBC: + codec_uuid = UUID_CODEC_MSBC; + break; + + default: + APPL_TRACE_ERROR("bta_ag_send_bcs: unknown codec %d, use CVSD", p_scb->sco_codec); + codec_uuid = UUID_CODEC_CVSD; + break; + } + } + /* send +BCS */ + APPL_TRACE_DEBUG("send +BCS codec is %d", codec_uuid); + bta_ag_send_result(p_scb, BTA_AG_RES_BCS, NULL, codec_uuid); +} +#endif + +/******************************************************************************* +** +** Function bta_ag_send_ring +** +** Description Send RING result code to peer. +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_send_ring(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data) +{ + UNUSED(p_data); + +#if defined(BTA_AG_MULTI_RESULT_INCLUDED) && (BTA_AG_MULTI_RESULT_INCLUDED == TRUE) + tBTA_AG_MULTI_RESULT_CB m_res_cb; + if (p_scb->conn_service == BTA_AG_HFP && p_scb->clip_enabled && p_scb->clip[0] != 0) { + memset(&m_res_cb, NULL, sizeof(tBTA_AG_MULTI_RESULT_CB)); + m_res_cb.num_result = 2; + AT_SET_RES_CB(m_res_cb.res_cb[0], BTA_AG_RES_RING, NULL, 0) + AT_SET_RES_CB(m_res_cb.res_cb[1], BTA_AG_RES_CLIP, p_scb->clip, 0) + bta_ag_send_multi_result(p_scb, &m_res_cb); + } else { + /* send RING ONLY */ + bta_ag_send_result(p_scb, BTA_AG_RES_RING, NULL, 0); + } +#else + /* send RING */ + bta_ag_send_result(p_scb, BTA_AG_RES_RING, NULL, 0); + /* if HFP and clip enabled and clip data send CLIP */ + if (p_scb->conn_service == BTA_AG_HFP && p_scb->clip_enabled && p_scb->clip[0] != 0) { + bta_ag_send_result(p_scb, BTA_AG_RES_CLIP, p_scb->clip, 0); + } +#endif + /* restart ring timer */ + bta_sys_start_timer(&p_scb->act_timer, BTA_AG_RING_TOUT_EVT, BTA_AG_RING_TOUT); +} + +#endif /* #if (BTA_AG_INCLUDED == TRUE) */ diff --git a/lib/bt/host/bluedroid/bta/hf_ag/bta_ag_main.c b/lib/bt/host/bluedroid/bta/hf_ag/bta_ag_main.c new file mode 100644 index 00000000..bcf3cc6e --- /dev/null +++ b/lib/bt/host/bluedroid/bta/hf_ag/bta_ag_main.c @@ -0,0 +1,980 @@ +/****************************************************************************** + * + * Copyright (C) 2003-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This is the main implementation file for the BTA audio gateway. + * + ******************************************************************************/ + +#include +#include +#include "bta_ag_int.h" +#include "bta/bta_api.h" +#include "bta/bta_sys.h" +#include "bta/bta_ag_api.h" +#include "bta/bta_ag_co.h" +#include "bta/utl.h" +#include "common/bt_defs.h" +#include "common/bt_trace.h" +#include "osi/allocator.h" + +#if (BTA_AG_INCLUDED == TRUE) +/***************************************************************************** +** Constants and types +*****************************************************************************/ +#ifndef BTA_AG_DEBUG +#define BTA_AG_DEBUG TRUE +#endif + +#if BTA_AG_DEBUG == TRUE +static char *bta_ag_evt_str(UINT16 event, tBTA_AG_RES result); +static char *bta_ag_state_str(UINT8 state); +#endif + +/* state machine states */ +enum +{ + BTA_AG_INIT_ST, + BTA_AG_OPENING_ST, + BTA_AG_OPEN_ST, + BTA_AG_CLOSING_ST +}; + +/* state machine action enumeration list */ +enum +{ + BTA_AG_REGISTER, + BTA_AG_DEREGISTER, + BTA_AG_START_OPEN, + BTA_AG_RFC_DO_OPEN, + BTA_AG_RFC_DO_CLOSE, + BTA_AG_START_DEREG, + BTA_AG_START_CLOSE, + BTA_AG_RFC_OPEN, + BTA_AG_OPEN_FAIL, + BTA_AG_RFC_ACP_OPEN, + BTA_AG_RFC_CLOSE, + BTA_AG_RFC_FAIL, + BTA_AG_RFC_DATA, + BTA_AG_DISC_INT_RES, + BTA_AG_DISC_FAIL, + BTA_AG_DISC_ACP_RES, + BTA_AG_FREE_DB, + BTA_AG_SCO_CONN_OPEN, + BTA_AG_SCO_CONN_CLOSE, + BTA_AG_SCO_LISTEN, + BTA_AG_SCO_OPEN, + BTA_AG_SCO_CLOSE, + BTA_AG_SCO_SHUTDOWN, + BTA_AG_POST_SCO_OPEN, + BTA_AG_POST_SCO_CLOSE, + BTA_AG_SVC_CONN_OPEN, + BTA_AG_RESULT, + BTA_AG_SETCODEC, + BTA_AG_SEND_RING, + BTA_AG_CI_SCO_DATA, + BTA_AG_CI_RX_DATA, + BTA_AG_RCVD_SLC_READY, + BTA_AG_PKT_STAT_NUMS, + BTA_AG_NUM_ACTIONS +}; + +#define BTA_AG_IGNORE BTA_AG_NUM_ACTIONS + +/* type for action functions */ +typedef void (*tBTA_AG_ACTION) (tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data); + +/* action functions */ +const tBTA_AG_ACTION bta_ag_action[] = +{ + bta_ag_register, + bta_ag_deregister, + bta_ag_start_open, + bta_ag_rfc_do_open, + bta_ag_rfc_do_close, + bta_ag_start_dereg, + bta_ag_start_close, + bta_ag_rfc_open, + bta_ag_open_fail, + bta_ag_rfc_acp_open, + bta_ag_rfc_close, + bta_ag_rfc_fail, + bta_ag_rfc_data, + bta_ag_disc_int_res, + bta_ag_disc_fail, + bta_ag_disc_acp_res, + bta_ag_free_db, + bta_ag_sco_conn_open, + bta_ag_sco_conn_close, + bta_ag_sco_listen, + bta_ag_sco_open, + bta_ag_sco_close, + bta_ag_sco_shutdown, + bta_ag_post_sco_open, + bta_ag_post_sco_close, + bta_ag_svc_conn_open, + bta_ag_result, + bta_ag_setcodec, + bta_ag_send_ring, + bta_ag_ci_sco_data, + bta_ag_ci_rx_data, + bta_ag_rcvd_slc_ready, + bta_ag_pkt_stat_nums +}; + +/* state table information */ +#define BTA_AG_ACTIONS 2 /* number of actions */ +#define BTA_AG_NEXT_STATE 2 /* position of next state */ +#define BTA_AG_NUM_COLS 3 /* number of columns in state tables */ + +/* state table for init state */ +const UINT8 bta_ag_st_init[][BTA_AG_NUM_COLS] = +{ +/* Event Action 1 Action 2 Next state */ +/* API_REGISTER_EVT */ {BTA_AG_REGISTER, BTA_AG_IGNORE, BTA_AG_INIT_ST}, +/* API_DEREGISTER_EVT */ {BTA_AG_DEREGISTER, BTA_AG_IGNORE, BTA_AG_INIT_ST}, +/* API_OPEN_EVT */ {BTA_AG_START_OPEN, BTA_AG_IGNORE, BTA_AG_OPENING_ST}, +/* API_CLOSE_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_INIT_ST}, +/* API_AUDIO_OPEN_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_INIT_ST}, +/* API_AUDIO_CLOSE_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_INIT_ST}, +/* API_RESULT_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_INIT_ST}, +/* API_SETCODEC_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_INIT_ST}, +/* RFC_OPEN_EVT */ {BTA_AG_RFC_ACP_OPEN, BTA_AG_SCO_LISTEN, BTA_AG_OPEN_ST}, +/* RFC_CLOSE_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_INIT_ST}, +/* RFC_SRV_CLOSE_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_INIT_ST}, +/* RFC_DATA_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_INIT_ST}, +/* SCO_OPEN_EVT */ {BTA_AG_SCO_CONN_OPEN, BTA_AG_IGNORE, BTA_AG_INIT_ST}, +/* SCO_CLOSE_EVT */ {BTA_AG_SCO_CONN_CLOSE, BTA_AG_IGNORE, BTA_AG_INIT_ST}, +/* DISC_ACP_RES_EVT */ {BTA_AG_FREE_DB, BTA_AG_IGNORE, BTA_AG_INIT_ST}, +/* DISC_INT_RES_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_INIT_ST}, +/* DISC_OK_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_INIT_ST}, +/* DISC_FAIL_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_INIT_ST}, +/* CI_RX_WRITE_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_INIT_ST}, +/* RING_TOUT_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_INIT_ST}, +/* SVC_TOUT_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_INIT_ST}, +/* CI_SCO_DATA_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_INIT_ST}, +/* CI_SLC_READY_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_INIT_ST}, +/* PKT_STAT_NUMS_GET_EVT */ {BTA_AG_PKT_STAT_NUMS, BTA_AG_IGNORE, BTA_AG_INIT_ST} +}; + +/* state table for opening state */ +const UINT8 bta_ag_st_opening[][BTA_AG_NUM_COLS] = +{ +/* Event Action 1 Action 2 Next state */ +/* API_REGISTER_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_OPENING_ST}, +/* API_DEREGISTER_EVT */ {BTA_AG_RFC_DO_CLOSE, BTA_AG_START_DEREG, BTA_AG_CLOSING_ST}, +/* API_OPEN_EVT */ {BTA_AG_OPEN_FAIL, BTA_AG_IGNORE, BTA_AG_OPENING_ST}, +/* API_CLOSE_EVT */ {BTA_AG_RFC_DO_CLOSE, BTA_AG_IGNORE, BTA_AG_CLOSING_ST}, +/* API_AUDIO_OPEN_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_OPENING_ST}, +/* API_AUDIO_CLOSE_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_OPENING_ST}, +/* API_RESULT_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_OPENING_ST}, +/* API_SETCODEC_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_OPENING_ST}, +/* RFC_OPEN_EVT */ {BTA_AG_RFC_OPEN, BTA_AG_SCO_LISTEN, BTA_AG_OPEN_ST}, +/* RFC_CLOSE_EVT */ {BTA_AG_RFC_FAIL, BTA_AG_IGNORE, BTA_AG_INIT_ST}, +/* RFC_SRV_CLOSE_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_OPENING_ST}, +/* RFC_DATA_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_OPENING_ST}, +/* SCO_OPEN_EVT */ {BTA_AG_SCO_CONN_OPEN, BTA_AG_IGNORE, BTA_AG_OPENING_ST}, +/* SCO_CLOSE_EVT */ {BTA_AG_SCO_CONN_CLOSE, BTA_AG_IGNORE, BTA_AG_OPENING_ST}, +/* DISC_ACP_RES_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_OPENING_ST}, +/* DISC_INT_RES_EVT */ {BTA_AG_DISC_INT_RES, BTA_AG_IGNORE, BTA_AG_OPENING_ST}, +/* DISC_OK_EVT */ {BTA_AG_RFC_DO_OPEN, BTA_AG_IGNORE, BTA_AG_OPENING_ST}, +/* DISC_FAIL_EVT */ {BTA_AG_DISC_FAIL, BTA_AG_IGNORE, BTA_AG_INIT_ST}, +/* CI_RX_WRITE_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_OPENING_ST}, +/* RING_TOUT_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_OPENING_ST}, +/* SVC_TOUT_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_OPENING_ST}, +/* CI_SCO_DATA_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_OPENING_ST}, +/* CI_SLC_READY_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_OPENING_ST}, +/* PKT_STAT_NUMS_GET_EVT */ {BTA_AG_PKT_STAT_NUMS, BTA_AG_IGNORE, BTA_AG_OPENING_ST} +}; + +/* state table for open state */ +const UINT8 bta_ag_st_open[][BTA_AG_NUM_COLS] = +{ +/* Event Action 1 Action 2 Next state */ +/* API_REGISTER_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_OPEN_ST}, +/* API_DEREGISTER_EVT */ {BTA_AG_START_CLOSE, BTA_AG_START_DEREG, BTA_AG_CLOSING_ST}, +/* API_OPEN_EVT */ {BTA_AG_OPEN_FAIL, BTA_AG_IGNORE, BTA_AG_OPEN_ST}, +/* API_CLOSE_EVT */ {BTA_AG_START_CLOSE, BTA_AG_IGNORE, BTA_AG_CLOSING_ST}, +/* API_AUDIO_OPEN_EVT */ {BTA_AG_SCO_OPEN, BTA_AG_IGNORE, BTA_AG_OPEN_ST}, +/* API_AUDIO_CLOSE_EVT */ {BTA_AG_SCO_CLOSE, BTA_AG_IGNORE, BTA_AG_OPEN_ST}, +/* API_RESULT_EVT */ {BTA_AG_RESULT, BTA_AG_IGNORE, BTA_AG_OPEN_ST}, +/* API_SETCODEC_EVT */ {BTA_AG_SETCODEC, BTA_AG_IGNORE, BTA_AG_OPEN_ST}, +/* RFC_OPEN_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_OPEN_ST}, +/* RFC_CLOSE_EVT */ {BTA_AG_RFC_CLOSE, BTA_AG_IGNORE, BTA_AG_INIT_ST}, +/* RFC_SRV_CLOSE_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_OPEN_ST}, +/* RFC_DATA_EVT */ {BTA_AG_RFC_DATA, BTA_AG_IGNORE, BTA_AG_OPEN_ST}, +/* SCO_OPEN_EVT */ {BTA_AG_SCO_CONN_OPEN, BTA_AG_POST_SCO_OPEN, BTA_AG_OPEN_ST}, +/* SCO_CLOSE_EVT */ {BTA_AG_SCO_CONN_CLOSE, BTA_AG_POST_SCO_CLOSE, BTA_AG_OPEN_ST}, +/* DISC_ACP_RES_EVT */ {BTA_AG_DISC_ACP_RES, BTA_AG_IGNORE, BTA_AG_OPEN_ST}, +/* DISC_INT_RES_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_OPEN_ST}, +/* DISC_OK_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_OPEN_ST}, +/* DISC_FAIL_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_OPEN_ST}, +/* CI_RX_WRITE_EVT */ {BTA_AG_CI_RX_DATA, BTA_AG_IGNORE, BTA_AG_OPEN_ST}, +/* RING_TOUT_EVT */ {BTA_AG_SEND_RING, BTA_AG_IGNORE, BTA_AG_OPEN_ST}, +/* SVC_TOUT_EVT */ {BTA_AG_START_CLOSE, BTA_AG_IGNORE, BTA_AG_CLOSING_ST}, +/* CI_SCO_DATA_EVT */ {BTA_AG_CI_SCO_DATA, BTA_AG_IGNORE, BTA_AG_OPEN_ST}, +/* CI_SLC_READY_EVT */ {BTA_AG_RCVD_SLC_READY, BTA_AG_IGNORE, BTA_AG_OPEN_ST}, +/* PKT_STAT_NUMS_GET_EVT */ {BTA_AG_PKT_STAT_NUMS, BTA_AG_IGNORE, BTA_AG_OPEN_ST} +}; + +/* state table for closing state */ +const UINT8 bta_ag_st_closing[][BTA_AG_NUM_COLS] = +{ +/* Event Action 1 Action 2 Next state */ +/* API_REGISTER_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_CLOSING_ST}, +/* API_DEREGISTER_EVT */ {BTA_AG_START_DEREG, BTA_AG_IGNORE, BTA_AG_CLOSING_ST}, +/* API_OPEN_EVT */ {BTA_AG_OPEN_FAIL, BTA_AG_IGNORE, BTA_AG_CLOSING_ST}, +/* API_CLOSE_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_CLOSING_ST}, +/* API_AUDIO_OPEN_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_CLOSING_ST}, +/* API_AUDIO_CLOSE_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_CLOSING_ST}, +/* API_RESULT_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_CLOSING_ST}, +/* API_SETCODEC_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_CLOSING_ST}, +/* RFC_OPEN_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_CLOSING_ST}, +/* RFC_CLOSE_EVT */ {BTA_AG_RFC_CLOSE, BTA_AG_IGNORE, BTA_AG_INIT_ST}, +/* RFC_SRV_CLOSE_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_CLOSING_ST}, +/* RFC_DATA_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_CLOSING_ST}, +/* SCO_OPEN_EVT */ {BTA_AG_SCO_CONN_OPEN, BTA_AG_IGNORE, BTA_AG_CLOSING_ST}, +/* SCO_CLOSE_EVT */ {BTA_AG_SCO_CONN_CLOSE, BTA_AG_POST_SCO_CLOSE, BTA_AG_CLOSING_ST}, +/* DISC_ACP_RES_EVT */ {BTA_AG_FREE_DB, BTA_AG_IGNORE, BTA_AG_CLOSING_ST}, +/* DISC_INT_RES_EVT */ {BTA_AG_FREE_DB, BTA_AG_IGNORE, BTA_AG_INIT_ST}, +/* DISC_OK_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_CLOSING_ST}, +/* DISC_FAIL_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_CLOSING_ST}, +/* CI_RX_WRITE_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_CLOSING_ST}, +/* RING_TOUT_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_CLOSING_ST}, +/* SVC_TOUT_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_CLOSING_ST}, +/* CI_SCO_DATA_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_CLOSING_ST}, +/* CI_SLC_READY_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_CLOSING_ST}, +/* PKT_STAT_NUMS_GET_EVT */ {BTA_AG_PKT_STAT_NUMS, BTA_AG_IGNORE, BTA_AG_CLOSING_ST} +}; + +/* type for state table */ +typedef const UINT8 (*tBTA_AG_ST_TBL)[BTA_AG_NUM_COLS]; + +/* state table */ +const tBTA_AG_ST_TBL bta_ag_st_tbl[] = +{ + bta_ag_st_init, + bta_ag_st_opening, + bta_ag_st_open, + bta_ag_st_closing +}; + +/***************************************************************************** +** Global data +*****************************************************************************/ +const uint16_t bta_ag_version = HFP_VERSION_1_8; +/* AG control block */ +#if BTA_DYNAMIC_MEMORY == FALSE +tBTA_AG_CB bta_ag_cb; +#else +tBTA_AG_CB *bta_ag_cb_ptr; +#endif + +#if BTA_AG_DEBUG == TRUE +static char *bta_ag_evt_str(UINT16 event, tBTA_AG_RES result) +{ + switch (event) + { + case BTA_AG_API_REGISTER_EVT: + return "Register Request"; + case BTA_AG_API_DEREGISTER_EVT: + return "Deregister Request"; + case BTA_AG_API_OPEN_EVT: + return "Open SLC Request"; + case BTA_AG_API_CLOSE_EVT: + return "Close SLC Request"; + case BTA_AG_API_AUDIO_OPEN_EVT: + return "Open Audio Request"; + case BTA_AG_API_AUDIO_CLOSE_EVT: + return "Close Audio Request"; + case BTA_AG_API_RESULT_EVT: + switch (result) { + case BTA_AG_SPK_RES: return ("AT Result BTA_AG_SPK_RES"); + case BTA_AG_MIC_RES: return ("AT Result BTA_AG_MIC_RES"); + case BTA_AG_INBAND_RING_RES: return ("AT Result BTA_AG_INBAND_RING_RES"); + case BTA_AG_CIND_RES: return ("AT Result BTA_AG_CIND_RES"); + case BTA_AG_BINP_RES: return ("AT Result BTA_AG_BINP_RES"); + case BTA_AG_IND_RES: return ("AT Result BTA_AG_IND_RES"); + case BTA_AG_BVRA_RES: return ("AT Result BTA_AG_BVRA_RES"); + case BTA_AG_CNUM_RES: return ("AT Result BTA_AG_CNUM_RES"); + case BTA_AG_BTRH_RES: return ("AT Result BTA_AG_BTRH_RES"); + case BTA_AG_CLCC_RES: return ("AT Result BTA_AG_CLCC_RES"); + case BTA_AG_COPS_RES: return ("AT Result BTA_AG_COPS_RES"); + case BTA_AG_IN_CALL_RES: return ("AT Result BTA_AG_IN_CALL_RES"); + case BTA_AG_IN_CALL_CONN_RES: return ("AT Result BTA_AG_IN_CALL_CONN_RES"); + case BTA_AG_CALL_WAIT_RES: return ("AT Result BTA_AG_CALL_WAIT_RES"); + case BTA_AG_OUT_CALL_ORIG_RES: return ("AT Result BTA_AG_OUT_CALL_ORIG_RES"); + case BTA_AG_OUT_CALL_ALERT_RES: return ("AT Result BTA_AG_OUT_CALL_ALERT_RES"); + case BTA_AG_OUT_CALL_CONN_RES: return ("AT Result BTA_AG_OUT_CALL_CONN_RES"); + case BTA_AG_CALL_CANCEL_RES: return ("AT Result BTA_AG_CALL_CANCEL_RES"); + case BTA_AG_END_CALL_RES: return ("AT Result BTA_AG_END_CALL_RES"); + case BTA_AG_UNAT_RES: return ("AT Result BTA_AG_UNAT_RES"); + default: return ("Unknown AG Result"); + } + case BTA_AG_API_SETCODEC_EVT: + return "Set Codec Request"; + case BTA_AG_RFC_OPEN_EVT: + return "RFC Opened"; + case BTA_AG_RFC_CLOSE_EVT: + return "RFC Closed"; + case BTA_AG_RFC_SRV_CLOSE_EVT: + return "RFC SRV Closed"; + case BTA_AG_RFC_DATA_EVT: + return "RFC Data"; + case BTA_AG_SCO_OPEN_EVT: + return "Audio Opened"; + case BTA_AG_SCO_CLOSE_EVT: + return "Audio Closed"; + case BTA_AG_DISC_ACP_RES_EVT: + return "Discovery ACP Result"; + case BTA_AG_DISC_INT_RES_EVT: + return "Discovery INT Result"; + case BTA_AG_DISC_OK_EVT: + return "Discovery OK"; + case BTA_AG_DISC_FAIL_EVT: + return "Discovery Failed"; + case BTA_AG_CI_RX_WRITE_EVT: + return "CI RX Write"; + case BTA_AG_RING_TOUT_EVT: + return "Ring Timeout"; + case BTA_AG_SVC_TOUT_EVT: + return "Service Timeout"; + case BTA_AG_API_ENABLE_EVT: + return "Enable AG"; + case BTA_AG_API_DISABLE_EVT: + return "Disable AG"; + case BTA_AG_CI_SCO_DATA_EVT: + return "SCO data Callin"; + case BTA_AG_CI_SLC_READY_EVT: + return "SLC Ready Callin"; + case BTA_AG_PKT_STAT_NUMS_GET_EVT: + return "Get Packet Nums"; + default: + return "Unknown AG Event"; + } +} + +static char *bta_ag_state_str(UINT8 state) +{ + switch (state) { + case BTA_AG_INIT_ST: + return "Initial"; + case BTA_AG_OPENING_ST: + return "Opening"; + case BTA_AG_OPEN_ST: + return "Open"; + case BTA_AG_CLOSING_ST: + return "Closing"; + default: + return "Unknown AG State"; + } +} + +#endif + +/******************************************************************************* +** +** Function bta_ag_timer_cback +** +** Description AG timer callback. +** +** +** Returns void +** +*******************************************************************************/ +static void bta_ag_timer_cback(void *p) +{ + BT_HDR *p_buf; + TIMER_LIST_ENT *p_tle = (TIMER_LIST_ENT *) p; + + if ((p_buf = (BT_HDR *) osi_malloc(sizeof(BT_HDR))) != NULL) { + p_buf->event = p_tle->event; + p_buf->layer_specific = bta_ag_scb_to_idx((tBTA_AG_SCB *) p_tle->param); + bta_sys_sendmsg(p_buf); + } +} + +/******************************************************************************* +** +** Function bta_ag_scb_alloc +** +** Description Allocate an AG service control block. +** +** +** Returns pointer to the scb, or NULL if none could be allocated. +** +*******************************************************************************/ +static tBTA_AG_SCB *bta_ag_scb_alloc(void) +{ + tBTA_AG_SCB *p_scb = &bta_ag_cb.scb[0]; + int i; + + for (i = 0; i < BTA_AG_NUM_SCB; i++, p_scb++) { + if (!p_scb->in_use) { + /* initialize variables */ + p_scb->in_use = TRUE; + p_scb->sco_idx = BTM_INVALID_SCO_INDEX; +#if (BTM_WBS_INCLUDED == TRUE) + p_scb->codec_updated = FALSE; +#endif + /* set up timers */ + p_scb->act_timer.param = (UINT32) p_scb; + p_scb->act_timer.p_cback = bta_ag_timer_cback; +#if (BTM_WBS_INCLUDED == TRUE) + /* set eSCO mSBC setting to T2 as the preferred */ + p_scb->codec_msbc_settings = BTA_AG_SCO_MSBC_SETTINGS_T2; +#endif + APPL_TRACE_DEBUG("bta_ag_scb_alloc %d", bta_ag_scb_to_idx(p_scb)); + break; + } + } + + if (i == BTA_AG_NUM_SCB) { + p_scb = NULL; /* out of scbs */ + APPL_TRACE_WARNING("Out of ag scbs"); + } + return p_scb; +} + +/******************************************************************************* +** +** Function bta_ag_scb_dealloc +** +** Description Deallocate a service control block. +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_scb_dealloc(tBTA_AG_SCB *p_scb) +{ + UINT8 idx; + BOOLEAN allocated = FALSE; + + APPL_TRACE_DEBUG("bta_ag_scb_dealloc %d", bta_ag_scb_to_idx(p_scb)); + /* stop timers */ + bta_sys_free_timer(&p_scb->act_timer); +#if (BTM_WBS_INCLUDED == TRUE) + bta_sys_free_timer(&p_scb->cn_timer); +#endif + bta_sys_free_timer(&p_scb->colli_timer); + + /* initialize control block */ + memset(p_scb, 0, sizeof(tBTA_AG_SCB)); + p_scb->sco_idx = BTM_INVALID_SCO_INDEX; + /* If all scbs are deallocated, callback with disable event */ + if (!bta_sys_is_register (BTA_ID_AG)) { + for (idx = 0; idx < BTA_AG_NUM_SCB; idx++) { + if (bta_ag_cb.scb[idx].in_use) { + allocated = TRUE; + break; + } + } + if (!allocated) { + (*bta_ag_cb.p_cback)(BTA_AG_DISABLE_EVT, NULL); + } + } +} + +/******************************************************************************* +** +** Function bta_ag_scb_to_idx +** +** Description Given a pointer to an scb, return its index. +** +** +** Returns Index of scb. +** +*******************************************************************************/ +UINT16 bta_ag_scb_to_idx(tBTA_AG_SCB *p_scb) +{ + /* use array arithmetic to determine index */ + return ((UINT16) (p_scb - bta_ag_cb.scb)) + 1; +} + +/******************************************************************************* +** +** Function bta_ag_scb_by_idx +** +** Description Given an scb index return pointer to scb. +** +** +** Returns Pointer to scb or NULL if not allocated. +** +*******************************************************************************/ +tBTA_AG_SCB *bta_ag_scb_by_idx(UINT16 idx) +{ + tBTA_AG_SCB *p_scb; + /* verify index */ + if (idx > 0 && idx <= BTA_AG_NUM_SCB) { + p_scb = &bta_ag_cb.scb[idx - 1]; + if (!p_scb->in_use) { + p_scb = NULL; + APPL_TRACE_WARNING("ag scb idx %d not allocated", idx); + } + } else { + p_scb = NULL; + APPL_TRACE_DEBUG("ag scb idx %d out of range", idx); + } + return p_scb; +} + +/******************************************************************************* +** +** Function bta_ag_service_to_idx +** +** Description Given a BTA service mask convert to profile index. +** +** +** Returns Profile ndex of scb. +** +*******************************************************************************/ +UINT8 bta_ag_service_to_idx(tBTA_SERVICE_MASK services) +{ + if (services & BTA_HFP_SERVICE_MASK) { + return BTA_AG_HFP; + } else { + return BTA_AG_HSP; + } +} + +/******************************************************************************* +** +** Function bta_ag_idx_by_bdaddr +** +** Description Find SCB associated with peer BD address. +** +** +** Returns Index of SCB or zero if none found. +** +*******************************************************************************/ +UINT16 bta_ag_idx_by_bdaddr(BD_ADDR peer_addr) +{ + tBTA_AG_SCB *p_scb = &bta_ag_cb.scb[0]; + UINT16 i; + + if (peer_addr != NULL) { + for (i = 0; i < BTA_AG_NUM_SCB; i++, p_scb++) { + if (p_scb->in_use && !bdcmp(peer_addr, p_scb->peer_addr)) { + return (i + 1); + } + } + } + /* no scb found */ + APPL_TRACE_WARNING("No ag scb for peer addr"); + return 0; +} + +/******************************************************************************* +** +** Function bta_ag_other_scb_open +** +** Description Check whether any other scb is in open state. +** +** +** Returns TRUE if another scb is in open state, FALSE otherwise. +** +*******************************************************************************/ +BOOLEAN bta_ag_other_scb_open(tBTA_AG_SCB *p_curr_scb) +{ + tBTA_AG_SCB *p_scb = &bta_ag_cb.scb[0]; + + for (int i = 0; i < BTA_AG_NUM_SCB; i++, p_scb++) { + if (p_scb->in_use && p_scb != p_curr_scb && p_scb->state == BTA_AG_OPEN_ST) { + return TRUE; + } + } + /* no other scb found */ + APPL_TRACE_DEBUG("No other ag scb open"); + return FALSE; +} + +/******************************************************************************* +** +** Function bta_ag_scb_open +** +** Description Check whether given scb is in open state. +** +** +** Returns TRUE if scb is in open state, FALSE otherwise. +** +*******************************************************************************/ +BOOLEAN bta_ag_scb_open(tBTA_AG_SCB *p_curr_scb) +{ + if (p_curr_scb && p_curr_scb->in_use && p_curr_scb->state == BTA_AG_OPEN_ST) { + return TRUE; + } + return FALSE; +} + +/******************************************************************************* +** +** Function bta_ag_get_other_idle_scb +** +** Description Return other scb if it is in INIT st. +** +** +** Returns Pointer to other scb if INIT st, NULL otherwise. +** +*******************************************************************************/ +tBTA_AG_SCB *bta_ag_get_other_idle_scb (tBTA_AG_SCB *p_curr_scb) +{ + tBTA_AG_SCB *p_scb = &bta_ag_cb.scb[0]; + UINT8 xx; + + for (xx = 0; xx < BTA_AG_NUM_SCB; xx++, p_scb++) { + if (p_scb->in_use && (p_scb != p_curr_scb) && (p_scb->state == BTA_AG_INIT_ST)) { + return p_scb; + } + } + /* no other scb found */ + APPL_TRACE_DEBUG("bta_ag_get_other_idle_scb: No idle AG scb"); + return NULL; +} + +/******************************************************************************* +** +** Function bta_ag_colli_timer_cback +** +** Description AG connection collision timer callback +** +** +** Returns void +** +*******************************************************************************/ +static void bta_ag_colli_timer_cback (TIMER_LIST_ENT *p_tle) +{ + tBTA_AG_SCB *p_scb; + + APPL_TRACE_DEBUG ("bta_ag_colli_timer_cback"); + if (p_tle) { + p_scb = (tBTA_AG_SCB *)p_tle->param; + if (p_scb) { + p_scb->colli_tmr_on = FALSE; + /* If the peer haven't opened AG connection */ + /* we will restart opening process. */ + bta_ag_resume_open (p_scb); + } + } +} + +/******************************************************************************* +** +** Function bta_ag_collision_cback +** +** Description Get notified about collision. +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_collision_cback (tBTA_SYS_CONN_STATUS status, UINT8 id, UINT8 app_id, BD_ADDR peer_addr) +{ + UINT16 handle; + tBTA_AG_SCB *p_scb; + UNUSED(status); + UNUSED(app_id); + + /* Check if we have opening scb for the peer device. */ + handle = bta_ag_idx_by_bdaddr (peer_addr); + p_scb = bta_ag_scb_by_idx (handle); + + if (p_scb && (p_scb->state == BTA_AG_OPENING_ST)) { + if (id == BTA_ID_SYS) { + /* ACL collision */ + APPL_TRACE_WARNING ("AG found collision (ACL) ..."); + } else if (id == BTA_ID_AG) { + /* RFCOMM collision */ + APPL_TRACE_WARNING ("AG found collision (RFCOMM) ..."); + } else { + APPL_TRACE_WARNING ("AG found collision (\?\?\?) ..."); + } + p_scb->state = BTA_AG_INIT_ST; + /* Cancel SDP if it had been started. */ + if(p_scb->p_disc_db) { + (void)SDP_CancelServiceSearch (p_scb->p_disc_db); + bta_ag_free_db(p_scb, NULL); + } + /* reopen registered servers */ + /* Collision may be detected before or after we close servers. */ + if (bta_ag_is_server_closed (p_scb)) { + bta_ag_start_servers(p_scb, p_scb->reg_services); + } + /* Start timer to han */ + p_scb->colli_timer.p_cback = (TIMER_CBACK*)&bta_ag_colli_timer_cback; + p_scb->colli_timer.param = (INT32)p_scb; + bta_sys_start_timer(&p_scb->colli_timer, 0, BTA_AG_COLLISION_TIMER); + p_scb->colli_tmr_on = TRUE; + } +} + +/******************************************************************************* +** +** Function bta_ag_resume_open +** +** Description Resume opening process. +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_resume_open (tBTA_AG_SCB *p_scb) +{ + if (p_scb) { + APPL_TRACE_DEBUG ("bta_ag_resume_open, Handle(%d)", bta_ag_scb_to_idx(p_scb)); + /* resume opening process. */ + if (p_scb->state == BTA_AG_INIT_ST) { + p_scb->state = BTA_AG_OPENING_ST; + bta_ag_start_open (p_scb, NULL); + } + } else { + APPL_TRACE_ERROR ("bta_ag_resume_open, Null p_scb"); + } +} + +/******************************************************************************* +** +** Function bta_ag_api_enable +** +** Description Handle an API enable event. +** +** +** Returns void +** +*******************************************************************************/ +static void bta_ag_api_enable(tBTA_AG_DATA *p_data) +{ + /* initialize control block */ + memset(&bta_ag_cb, 0, sizeof(tBTA_AG_CB)); + /* store callback function */ + bta_ag_cb.p_cback = p_data->api_enable.p_cback; + bta_ag_cb.parse_mode = p_data->api_enable.parse_mode; + /* check if mSBC support enabled */ + if (bta_ag_version >= HFP_VERSION_1_6) { + bta_ag_cb.msbc_enabled = TRUE; + bta_ag_cb.scb->negotiated_codec = BTM_SCO_CODEC_MSBC; + } else{ + bta_ag_cb.msbc_enabled = FALSE; + bta_ag_cb.scb->negotiated_codec = BTM_SCO_CODEC_CVSD; + } + + /* set deault setting for eSCO/SCO */ + BTM_WriteVoiceSettings(AG_VOICE_SETTINGS); + bta_sys_collision_register (BTA_ID_AG, bta_ag_collision_cback); + /* call callback with enable event */ + (*bta_ag_cb.p_cback)(BTA_AG_ENABLE_EVT, NULL); +} + +/******************************************************************************* +** +** Function bta_ag_api_disable +** +** Description Handle an API disable event. +** +** +** Returns void +** +*******************************************************************************/ +static void bta_ag_api_disable(tBTA_AG_DATA *p_data) +{ + /* deregister all scbs in use */ + tBTA_AG_SCB *p_scb = &bta_ag_cb.scb[0]; + BOOLEAN do_dereg = FALSE; + int i; + + if (!bta_sys_is_register (BTA_ID_AG)) { + APPL_TRACE_ERROR("BTA AG is already disabled, ignoring ..."); + return; + } + /* De-register with BTA system manager */ + bta_sys_deregister(BTA_ID_AG); + + for (i = 0; i < BTA_AG_NUM_SCB; i++, p_scb++) { + if (p_scb->in_use) { + bta_ag_sm_execute(p_scb, BTA_AG_API_DEREGISTER_EVT, p_data); + do_dereg = TRUE; + } + } + + if (!do_dereg) { + /* Done, send callback evt to app */ + (*bta_ag_cb.p_cback)(BTA_AG_DISABLE_EVT, NULL); + } + bta_sys_collision_register (BTA_ID_AG, NULL); +} + +/******************************************************************************* +** +** Function bta_ag_api_register +** +** Description Handle an API event registers a new service. +** +** +** Returns void +** +*******************************************************************************/ +static void bta_ag_api_register(tBTA_AG_DATA *p_data) +{ + tBTA_AG_SCB *p_scb; + tBTA_AG_REGISTER reg; + + /* allocate an scb */ + if ((p_scb = bta_ag_scb_alloc()) != NULL) { + APPL_TRACE_DEBUG("bta_ag_api_register: p_scb 0x%08x ", (unsigned int)p_scb); + bta_ag_sm_execute(p_scb, p_data->hdr.event, p_data); + } else { + reg.hdr.status = BTA_AG_FAIL_RESOURCES; + (*bta_ag_cb.p_cback)(BTA_AG_REGISTER_EVT, (tBTA_AG *) ®); + } +} + +/******************************************************************************* +** +** Function bta_ag_api_result +** +** Description Handle an API result event. +** +** +** Returns void +** +*******************************************************************************/ +static void bta_ag_api_result(tBTA_AG_DATA *p_data) +{ + tBTA_AG_SCB *p_scb; + int i; + + if (p_data->hdr.layer_specific != BTA_AG_HANDLE_ALL) { + if ((p_scb = bta_ag_scb_by_idx(p_data->hdr.layer_specific)) != NULL) { + APPL_TRACE_DEBUG("bta_ag_api_result: p_scb 0x%08x ", (unsigned int)p_scb); + bta_ag_sm_execute(p_scb, BTA_AG_API_RESULT_EVT, p_data); + } + } else { + for (i = 0, p_scb = &bta_ag_cb.scb[0]; i < BTA_AG_NUM_SCB; i++, p_scb++) { + if (p_scb->in_use && p_scb->svc_conn) { + APPL_TRACE_DEBUG("bta_ag_api_result p_scb 0x%08x ", (unsigned int)p_scb); + bta_ag_sm_execute(p_scb, BTA_AG_API_RESULT_EVT, p_data); + } + } + } +} + +/******************************************************************************* +** +** Function bta_ag_sm_execute +** +** Description State machine event handling function for AG +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_sm_execute(tBTA_AG_SCB *p_scb, UINT16 event, tBTA_AG_DATA *p_data) +{ + tBTA_AG_ST_TBL state_table; + UINT8 action; + +#if BTA_AG_DEBUG == TRUE + UINT16 in_event = event; + UINT8 in_state = p_scb->state; + /* Ignore displaying of AT results when not connected (Ignored in state machine) */ + if (in_event != BTA_AG_API_RESULT_EVT || p_scb->state == BTA_AG_OPEN_ST) { + APPL_TRACE_EVENT("AG evt (hdl 0x%04x): State %d (%s), Event 0x%04x (%s)", + bta_ag_scb_to_idx(p_scb), + p_scb->state, bta_ag_state_str(p_scb->state), + event, bta_ag_evt_str(event, p_data->api_result.result)); + } +#else + APPL_TRACE_EVENT("AG evt (hdl 0x%04x): State %d, Event 0x%04x", + bta_ag_scb_to_idx(p_scb), p_scb->state, event); +#endif + event &= 0x00FF; + if (event >= (BTA_AG_MAX_EVT & 0x00FF)) { + APPL_TRACE_ERROR("AG evt out of range, ignoring..."); + return; + } + /* look up the state table for the current state */ + state_table = bta_ag_st_tbl[p_scb->state]; + /* set next state */ + p_scb->state = state_table[event][BTA_AG_NEXT_STATE]; + /* execute action functions */ + for (int i = 0; i < BTA_AG_ACTIONS; i++) { + if ((action = state_table[event][i]) != BTA_AG_IGNORE) { + (*bta_ag_action[action])(p_scb, p_data); + } else { + break; + } + } +#if BTA_AG_DEBUG == TRUE + if (p_scb->state != in_state) { + APPL_TRACE_EVENT("BTA AG State Change: [%s] -> [%s] after Event [%s]", + bta_ag_state_str(in_state), + bta_ag_state_str(p_scb->state), + bta_ag_evt_str(in_event, p_data->api_result.result)); + } +#endif +} + +/******************************************************************************* +** +** Function bta_ag_hdl_event +** +** Description Data gateway main event handling function. +** +** +** Returns BOOLEAN +** +*******************************************************************************/ +BOOLEAN bta_ag_hdl_event(BT_HDR *p_msg) +{ + tBTA_AG_SCB *p_scb; + APPL_TRACE_DEBUG("bta_ag_hdl_event: Event 0x%04x ", p_msg->event); + + switch (p_msg->event) { + /* handle enable event */ + case BTA_AG_API_ENABLE_EVT: + bta_ag_api_enable((tBTA_AG_DATA *) p_msg); + break; + + /* handle disable event */ + case BTA_AG_API_DISABLE_EVT: + bta_ag_api_disable((tBTA_AG_DATA *) p_msg); + break; + + /* handle register event */ + case BTA_AG_API_REGISTER_EVT: + bta_ag_api_register((tBTA_AG_DATA *) p_msg); + break; + + /* handle result event */ + case BTA_AG_API_RESULT_EVT: + bta_ag_api_result((tBTA_AG_DATA *) p_msg); + break; + + /* all others reference scb by handle */ + default: + if ((p_scb = bta_ag_scb_by_idx(p_msg->layer_specific)) != NULL) { + APPL_TRACE_DEBUG("bta_ag_hdl_event: p_scb 0x%08x ", (unsigned int)p_scb); + bta_ag_sm_execute(p_scb, p_msg->event, (tBTA_AG_DATA *) p_msg); + } + break; + } + return TRUE; +} + +#endif /* #if (BTA_AG_INCLUDED == TRUE) */ diff --git a/lib/bt/host/bluedroid/bta/hf_ag/bta_ag_rfc.c b/lib/bt/host/bluedroid/bta/hf_ag/bta_ag_rfc.c new file mode 100644 index 00000000..49ca54af --- /dev/null +++ b/lib/bt/host/bluedroid/bta/hf_ag/bta_ag_rfc.c @@ -0,0 +1,406 @@ +/****************************************************************************** + * + * Copyright (C) 2004-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains the audio gateway functions controlling the RFCOMM + * connections. + * + ******************************************************************************/ + + +#include +#include "bta_ag_int.h" +#include "bta/bta_api.h" +#include "bta/bta_sys.h" +#include "bta/bta_ag_api.h" +#include "bta/bta_ag_co.h" +#include "bta/utl.h" +#include "stack/btm_api.h" +#include "stack/port_api.h" +#include "stack/rfcdefs.h" +#include "common/bt_trace.h" +#include "osi/allocator.h" + + +#if (BTA_AG_INCLUDED == TRUE) +/* Event mask for RfCOMM port callback */ +#define BTA_AG_PORT_EV_MASK PORT_EV_RXCHAR + +/* each scb has its own rfcomm callbacks */ +void bta_ag_port_cback_1(UINT32 code, UINT16 port_handle); +void bta_ag_port_cback_2(UINT32 code, UINT16 port_handle); +void bta_ag_port_cback_3(UINT32 code, UINT16 port_handle); + +void bta_ag_mgmt_cback_1(UINT32 code, UINT16 port_handle, void* data); +void bta_ag_mgmt_cback_2(UINT32 code, UINT16 port_handle, void* data); +void bta_ag_mgmt_cback_3(UINT32 code, UINT16 port_handle, void* data); + +int bta_ag_data_cback_1(UINT16 port_handle, void *p_data, UINT16 len); +int bta_ag_data_cback_2(UINT16 port_handle, void *p_data, UINT16 len); +int bta_ag_data_cback_3(UINT16 port_handle, void *p_data, UINT16 len); + +/* rfcomm callback function tables */ +typedef tPORT_CALLBACK *tBTA_AG_PORT_CBACK; +const tBTA_AG_PORT_CBACK bta_ag_port_cback_tbl[] = +{ + bta_ag_port_cback_1, + bta_ag_port_cback_2, + bta_ag_port_cback_3 +}; + +typedef tPORT_MGMT_CALLBACK *tBTA_AG_MGMT_CBACK; +const tBTA_AG_MGMT_CBACK bta_ag_mgmt_cback_tbl[] = +{ + bta_ag_mgmt_cback_1, + bta_ag_mgmt_cback_2, + bta_ag_mgmt_cback_3 +}; + +typedef tPORT_DATA_CALLBACK *tBTA_AG_DATA_CBACK; +const tBTA_AG_DATA_CBACK bta_ag_data_cback_tbl[] = +{ + bta_ag_data_cback_1, + bta_ag_data_cback_2, + bta_ag_data_cback_3 +}; + +/******************************************************************************* +** +** Function bta_ag_port_cback +** +** Description RFCOMM Port callback +** +** +** Returns void +** +*******************************************************************************/ +static void bta_ag_port_cback(UINT32 code, UINT16 port_handle, UINT16 handle) +{ + BT_HDR *p_buf; + tBTA_AG_SCB *p_scb; + UNUSED(code); + + if ((p_scb = bta_ag_scb_by_idx(handle)) != NULL) { + /* ignore port events for port handles other than connected handle */ + if (port_handle != p_scb->conn_handle) { + APPL_TRACE_DEBUG("ag_port_cback ignoring handle:%d conn_handle = %d other handle = %d", + port_handle, p_scb->conn_handle, handle); + return; + } + + if ((p_buf = (BT_HDR *) osi_malloc(sizeof(BT_HDR))) != NULL) { + p_buf->event = BTA_AG_RFC_DATA_EVT; + p_buf->layer_specific = handle; + bta_sys_sendmsg(p_buf); + } + } +} + +/******************************************************************************* +** +** Function bta_ag_mgmt_cback +** +** Description RFCOMM management callback +** +** +** Returns void +** +*******************************************************************************/ +static void bta_ag_mgmt_cback(UINT32 code, UINT16 port_handle, UINT16 handle) +{ + tBTA_AG_RFC *p_buf; + tBTA_AG_SCB *p_scb; + UINT16 event; + UINT8 i; + BOOLEAN found_handle = FALSE; + + APPL_TRACE_DEBUG("ag_mgmt_cback : code = %d, port_handle = %d, handle = %d", + code, port_handle, handle); + + if ((p_scb = bta_ag_scb_by_idx(handle)) != NULL) { + /* ignore close event for port handles other than connected handle */ + if ((code != PORT_SUCCESS) && (port_handle != p_scb->conn_handle)) { + APPL_TRACE_DEBUG("ag_mgmt_cback ignoring handle:%d", port_handle); + return; + } + + if (code == PORT_SUCCESS) { + if (p_scb->conn_handle) { + /* Outgoing connection */ + if (port_handle == p_scb->conn_handle) + found_handle = TRUE; + } else { + /* Incoming connection */ + for (i = 0; i < BTA_AG_NUM_IDX; i++) { + if (port_handle == p_scb->serv_handle[i]) + found_handle = TRUE; + } + } + + if (!found_handle) { + APPL_TRACE_ERROR ("bta_ag_mgmt_cback: PORT_SUCCESS, ignoring handle = %d", port_handle); + return; + } + event = BTA_AG_RFC_OPEN_EVT; + } else if (port_handle == p_scb->conn_handle) { + /* distinguish server close events */ + event = BTA_AG_RFC_CLOSE_EVT; + } else { + event = BTA_AG_RFC_SRV_CLOSE_EVT; + } + + if ((p_buf = (tBTA_AG_RFC *) osi_malloc(sizeof(tBTA_AG_RFC))) != NULL) { + p_buf->hdr.event = event; + p_buf->hdr.layer_specific = handle; + p_buf->port_handle = port_handle; + bta_sys_sendmsg(p_buf); + } + } +} + +/******************************************************************************* +** +** Function bta_ag_data_cback +** +** Description RFCOMM data callback +** +** +** Returns void +** +*******************************************************************************/ +static int bta_ag_data_cback(UINT16 port_handle, void *p_data, UINT16 len, UINT16 handle) +{ + UNUSED(port_handle); + + /* call data call-out directly */ +#if (BTM_SCO_HCI_INCLUDED == TRUE) + bta_ag_co_tx_write(handle, (UINT8 *) p_data, len); +#endif + return 0; +} + +/******************************************************************************* +** +** Function bta_ag_port_cback_1 to 3 +** bta_ag_mgmt_cback_1 to 3 +** +** Description RFCOMM callback functions. This is an easy way to +** distinguish scb from the callback. +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_mgmt_cback_1(UINT32 code, UINT16 handle, void* data) {bta_ag_mgmt_cback(code, handle, 1);} +void bta_ag_mgmt_cback_2(UINT32 code, UINT16 handle, void* data) {bta_ag_mgmt_cback(code, handle, 2);} +void bta_ag_mgmt_cback_3(UINT32 code, UINT16 handle, void* data) {bta_ag_mgmt_cback(code, handle, 3);} +void bta_ag_port_cback_1(UINT32 code, UINT16 handle) {bta_ag_port_cback(code, handle, 1);} +void bta_ag_port_cback_2(UINT32 code, UINT16 handle) {bta_ag_port_cback(code, handle, 2);} +void bta_ag_port_cback_3(UINT32 code, UINT16 handle) {bta_ag_port_cback(code, handle, 3);} + +/******************************************************************************* +** +** Function bta_ag_data_cback_1 to 3 +** +** Description RFCOMM data callback functions. This is an easy way to +** distinguish scb from the callback. +** +** +** Returns void +** +*******************************************************************************/ +int bta_ag_data_cback_1(UINT16 port_handle, void *p_data, UINT16 len) +{ + return bta_ag_data_cback(port_handle, p_data, len, 1); +} +int bta_ag_data_cback_2(UINT16 port_handle, void *p_data, UINT16 len) +{ + return bta_ag_data_cback(port_handle, p_data, len, 2); +} +int bta_ag_data_cback_3(UINT16 port_handle, void *p_data, UINT16 len) +{ + return bta_ag_data_cback(port_handle, p_data, len, 3); +} + +/******************************************************************************* +** +** Function bta_ag_setup_port +** +** Description Setup RFCOMM port for use by AG. +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_setup_port(tBTA_AG_SCB *p_scb, UINT16 handle) +{ + UINT16 i = bta_ag_scb_to_idx(p_scb) - 1; + + /* set up data callback if using pass through mode */ + if (bta_ag_cb.parse_mode == BTA_AG_PASS_THROUGH) { + PORT_SetDataCallback(handle, bta_ag_data_cback_tbl[i]); + } + PORT_SetEventMask(handle, BTA_AG_PORT_EV_MASK); + PORT_SetEventCallback(handle, bta_ag_port_cback_tbl[i]); +} + +/******************************************************************************* +** +** Function bta_ag_start_servers +** +** Description Setup RFCOMM servers for use by AG. +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_start_servers(tBTA_AG_SCB *p_scb, tBTA_SERVICE_MASK services) +{ + int bta_ag_port_status; + + services >>= BTA_HSP_SERVICE_ID; + for (int i = 0; i < BTA_AG_NUM_IDX && services != 0; i++, services >>= 1) { + /* if service is set in mask */ + if (services & 1) { + BTM_SetSecurityLevel(FALSE, "", bta_ag_sec_id[i], p_scb->serv_sec_mask, + BT_PSM_RFCOMM, BTM_SEC_PROTO_RFCOMM, bta_ag_cb.profile[i].scn); + + bta_ag_port_status = RFCOMM_CreateConnection(bta_ag_uuid[i], bta_ag_cb.profile[i].scn, + TRUE, BTA_AG_MTU, (UINT8 *) bd_addr_any, &(p_scb->serv_handle[i]), + bta_ag_mgmt_cback_tbl[bta_ag_scb_to_idx(p_scb) - 1]); + + if( bta_ag_port_status == PORT_SUCCESS ) { + bta_ag_setup_port(p_scb, p_scb->serv_handle[i]); + } else { + /* TODO: CR#137125 to handle to error properly */ + APPL_TRACE_DEBUG("bta_ag_start_servers: RFCOMM_CreateConnection returned error:%d", bta_ag_port_status); + } + } + } +} + +/******************************************************************************* +** +** Function bta_ag_close_servers +** +** Description Close RFCOMM servers port for use by AG. +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_close_servers(tBTA_AG_SCB *p_scb, tBTA_SERVICE_MASK services) +{ + int i; + + services >>= BTA_HSP_SERVICE_ID; + for (i = 0; i < BTA_AG_NUM_IDX && services != 0; i++, services >>= 1) { + /* if service is set in mask */ + if (services & 1) { + RFCOMM_RemoveServer(p_scb->serv_handle[i]); + p_scb->serv_handle[i] = 0; + } + } +} + +/******************************************************************************* +** +** Function bta_ag_is_server_closed +** +** Description Returns TRUE if all servers are closed. +** +** +** Returns TRUE if all servers are closed, FALSE otherwise +** +*******************************************************************************/ +BOOLEAN bta_ag_is_server_closed (tBTA_AG_SCB *p_scb) +{ + UINT8 i; + BOOLEAN is_closed = TRUE; + + for (i = 0; i < BTA_AG_NUM_IDX; i++) { + if (p_scb->serv_handle[i] != 0) + is_closed = FALSE; + } + return is_closed; +} + +/******************************************************************************* +** +** Function bta_ag_rfc_do_open +** +** Description Open an RFCOMM connection to the peer device. +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_rfc_do_open(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data) +{ + BTM_SetSecurityLevel(TRUE, "", bta_ag_sec_id[p_scb->conn_service], + p_scb->cli_sec_mask, BT_PSM_RFCOMM, BTM_SEC_PROTO_RFCOMM, p_scb->peer_scn); + + if (RFCOMM_CreateConnection(bta_ag_uuid[p_scb->conn_service], p_scb->peer_scn, + FALSE, BTA_AG_MTU, p_scb->peer_addr, &(p_scb->conn_handle), + bta_ag_mgmt_cback_tbl[bta_ag_scb_to_idx(p_scb) - 1]) == PORT_SUCCESS) { + bta_ag_setup_port(p_scb, p_scb->conn_handle); + APPL_TRACE_DEBUG("bta_ag_rfc_do_open : conn_handle = %d", p_scb->conn_handle); + } else { + /* RFCOMM create connection failed; send ourselves RFCOMM close event */ + bta_ag_sm_execute(p_scb, BTA_AG_RFC_CLOSE_EVT, p_data); + } +} + +/******************************************************************************* +** +** Function bta_ag_rfc_do_close +** +** Description Close RFCOMM connection. +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_rfc_do_close(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data) +{ + tBTA_AG_RFC *p_buf; + UNUSED(p_data); + + if (p_scb->conn_handle) { + RFCOMM_RemoveConnection(p_scb->conn_handle); + } else { + /* Close API was called while AG is in Opening state. */ + /* Need to trigger the state machine to send callback to the app */ + /* and move back to INIT state. */ + if ((p_buf = (tBTA_AG_RFC *) osi_malloc(sizeof(tBTA_AG_RFC))) != NULL) { + p_buf->hdr.event = BTA_AG_RFC_CLOSE_EVT; + p_buf->hdr.layer_specific = bta_ag_scb_to_idx(p_scb); + bta_sys_sendmsg(p_buf); + } + /* Cancel SDP if it had been started. */ + /* + if(p_scb->p_disc_db) + { + (void)SDP_CancelServiceSearch (p_scb->p_disc_db); + } + */ + } +} + +#endif /* #if (BTA_AG_INCLUDED == TRUE) */ diff --git a/lib/bt/host/bluedroid/bta/hf_ag/bta_ag_sco.c b/lib/bt/host/bluedroid/bta/hf_ag/bta_ag_sco.c new file mode 100644 index 00000000..661d2372 --- /dev/null +++ b/lib/bt/host/bluedroid/bta/hf_ag/bta_ag_sco.c @@ -0,0 +1,1847 @@ +/****************************************************************************** + * + * Copyright (C) 2004-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains functions for managing the SCO connection used in AG. + * + ******************************************************************************/ +#include +#include "bta_ag_int.h" +#include "bta/bta_api.h" +#include "bta/bta_ag_api.h" +#include "bta/bta_ag_co.h" +#include "bta/bta_hfp_defs.h" + +#if (BTM_SCO_HCI_INCLUDED == TRUE ) +#include "bta/bta_dm_co.h" +#include "hci/hci_audio.h" +#endif + +#include "bta/utl.h" +#include "stack/btm_api.h" +#include "common/bt_trace.h" +#include "osi/allocator.h" + +#if (BTA_AG_INCLUDED == TRUE) + +#ifndef BTA_AG_CODEC_NEGO_TIMEOUT +#define BTA_AG_CODEC_NEGO_TIMEOUT 3000 +#endif + +static char *bta_ag_sco_evt_str(UINT8 event); +static char *bta_ag_sco_state_str(UINT8 state); + +#define BTA_AG_NO_EDR_ESCO (BTM_SCO_PKT_TYPES_MASK_NO_2_EV3 | \ + BTM_SCO_PKT_TYPES_MASK_NO_3_EV3 | \ + BTM_SCO_PKT_TYPES_MASK_NO_2_EV5 | \ + BTM_SCO_PKT_TYPES_MASK_NO_3_EV5) + +/* sco events */ +enum +{ + BTA_AG_SCO_LISTEN_E, /* listen request */ + BTA_AG_SCO_OPEN_E, /* open request */ + BTA_AG_SCO_XFER_E, /* transfer request */ +#if (BTM_WBS_INCLUDED == TRUE) + BTA_AG_SCO_CN_DONE_E, /* codec negotiation done */ + BTA_AG_SCO_REOPEN_E, /* Retry with other codec when failed */ +#endif + BTA_AG_SCO_CLOSE_E, /* close request */ + BTA_AG_SCO_SHUTDOWN_E, /* shutdown request */ + BTA_AG_SCO_CONN_OPEN_E, /* sco open */ + BTA_AG_SCO_CONN_CLOSE_E, /* sco closed */ + BTA_AG_SCO_CI_DATA_E /* SCO data ready */ +}; + +#if (BTM_WBS_INCLUDED == TRUE) +#define BTA_AG_NUM_CODECS 4 +#define BTA_AG_ESCO_SETTING_IDX_CVSD 0 /* eSCO setting for CVSD */ +#define BTA_AG_ESCO_SETTING_IDX_T1 1 /* eSCO setting for mSBC T1 */ +#define BTA_AG_ESCO_SETTING_IDX_T2 2 /* eSCO setting for mSBC T2 */ +#define BTA_AG_ESCO_SETTING_IDX_S4 3 /* eSCO setting for CVSD S4 */ + +static const tBTM_ESCO_PARAMS bta_ag_esco_params[BTA_AG_NUM_CODECS] = +{ + /* CVSD */ + { + BTM_64KBITS_RATE, /* TX Bandwidth (64 kbits/sec) */ + BTM_64KBITS_RATE, /* RX Bandwidth (64 kbits/sec) */ + 10, /* 10 ms (HS/HF can use EV3, 2-EV3) */ + BTM_VOICE_SETTING_CVSD, /* Inp Linear, Air CVSD, 2s Comp, 16bit */ + (BTM_SCO_PKT_TYPES_MASK_HV1 | /* Packet Types */ + BTM_SCO_PKT_TYPES_MASK_HV3 | + BTM_SCO_PKT_TYPES_MASK_EV3 | + BTM_SCO_PKT_TYPES_MASK_NO_2_EV5 | + BTM_SCO_PKT_TYPES_MASK_NO_3_EV3 | + BTM_SCO_PKT_TYPES_MASK_NO_3_EV5), + BTM_ESCO_RETRANS_POWER /* Retransmission effort */ + }, + /* mSBC T1 */ + { + BTM_64KBITS_RATE, /* TX Bandwidth (64 kbits/sec), 8000 */ + BTM_64KBITS_RATE, /* RX Bandwidth (64 kbits/sec), 8000 */ + 8, /* 8 ms */ + BTM_VOICE_SETTING_TRANS, /* Inp Linear, Transparent, 2s Comp, 16bit */ + (BTM_SCO_PKT_TYPES_MASK_EV3 | /* Packet Types : EV3 */ + BTM_SCO_PKT_TYPES_MASK_NO_3_EV3 | + BTM_SCO_PKT_TYPES_MASK_NO_2_EV5 | + BTM_SCO_PKT_TYPES_MASK_NO_3_EV5 | + BTM_SCO_PKT_TYPES_MASK_NO_2_EV3 ), + BTM_ESCO_RETRANS_QUALITY /* Retransmission effort */ + }, + /* mSBC T2*/ + { + BTM_64KBITS_RATE, /* TX Bandwidth (64 kbits/sec), 8000 */ + BTM_64KBITS_RATE, /* RX Bandwidth (64 kbits/sec), 8000 */ + 13, /* 13 ms */ + BTM_VOICE_SETTING_TRANS, /* Inp Linear, Transparent, 2s Comp, 16bit */ + (BTM_SCO_PKT_TYPES_MASK_NO_3_EV3 | /* Packet Types : 2-EV3 */ + BTM_SCO_PKT_TYPES_MASK_NO_2_EV5 | + BTM_SCO_PKT_TYPES_MASK_NO_3_EV5), + BTM_ESCO_RETRANS_QUALITY /* Retransmission effort */ + }, + /* HFP 1.7+ */ + /* eSCO CVSD S4 */ + { + BTM_64KBITS_RATE, /* TX Bandwidth (64 kbits/sec) */ + BTM_64KBITS_RATE, /* RX Bandwidth (64 kbits/sec) */ + 12, /* 12 ms (HS/HF can use 2-EV3) */ + BTM_VOICE_SETTING_CVSD, /* Inp Linear, Air CVSD, 2s Comp, 16bit */ + (BTM_SCO_PKT_TYPES_MASK_NO_2_EV5 | /* Packet Types : 2-EV3 */ + BTM_SCO_PKT_TYPES_MASK_NO_3_EV3 | + BTM_SCO_PKT_TYPES_MASK_NO_3_EV5), + BTM_ESCO_RETRANS_QUALITY /* Retransmission effort */ + } +}; +#else +#define BTA_AG_NUM_CODECS 2 +#define BTA_AG_ESCO_SETTING_IDX_CVSD 0 /* eSCO setting for CVSD S3 */ +#define BTA_AG_ESCO_SETTING_IDX_S4 1 /* eSCO setting for CVSD S4 */ + +/* WBS not included, CVSD by default */ +static const tBTM_ESCO_PARAMS bta_ag_esco_params[] = +{ + { + BTM_64KBITS_RATE, /* TX Bandwidth (64 kbits/sec) */ + BTM_64KBITS_RATE, /* RX Bandwidth (64 kbits/sec) */ + 10, /* 10 ms (HS/HF can use EV3, 2-EV3) */ + BTM_VOICE_SETTING_CVSD, /* Inp Linear, Air CVSD, 2s Comp, 16bit */ + (BTM_SCO_PKT_TYPES_MASK_HV1 | /* Packet Types */ + BTM_SCO_PKT_TYPES_MASK_HV3 | + BTM_SCO_PKT_TYPES_MASK_EV3 | + BTM_SCO_PKT_TYPES_MASK_NO_2_EV5 | + BTM_SCO_PKT_TYPES_MASK_NO_3_EV3 | + BTM_SCO_PKT_TYPES_MASK_NO_3_EV5), + BTM_ESCO_RETRANS_POWER /* Retransmission effort */ + }, + /* HFP 1.7+ */ + /* eSCO CVSD S4 */ + { + BTM_64KBITS_RATE, /* TX Bandwidth (64 kbits/sec) */ + BTM_64KBITS_RATE, /* RX Bandwidth (64 kbits/sec) */ + 12, /* 12 ms (HS/HF can use 2-EV3) */ + BTM_VOICE_SETTING_CVSD, /* Inp Linear, Air CVSD, 2s Comp, 16bit */ + (BTM_SCO_PKT_TYPES_MASK_NO_2_EV5 | + BTM_SCO_PKT_TYPES_MASK_NO_3_EV3 | + BTM_SCO_PKT_TYPES_MASK_NO_3_EV5), + BTM_ESCO_RETRANS_QUALITY /* Retransmission effort */ + } +}; +#endif + +/******************************************************************************* +** +** Function bta_ag_sco_conn_cback +** +** Description BTM SCO connection callback. +** +** +** Returns void +** +*******************************************************************************/ +static void bta_ag_sco_conn_cback(UINT16 sco_idx) +{ + UINT16 handle; + BT_HDR *p_buf; + tBTA_AG_SCB *p_scb = &bta_ag_cb.scb[0]; + tBTM_ESCO_DATA sco_data; + + APPL_TRACE_DEBUG("%s %d", __FUNCTION__, sco_idx); + + /* match callback to scb; first check current sco scb */ + if (bta_ag_cb.sco.p_curr_scb != NULL && bta_ag_cb.sco.p_curr_scb->in_use) + { + handle = bta_ag_scb_to_idx(bta_ag_cb.sco.p_curr_scb); + } + /* then check for scb connected to this peer */ + else + { + /* Check if SLC is up */ + handle = bta_ag_idx_by_bdaddr(BTM_ReadScoBdAddr(sco_idx)); + p_scb = bta_ag_scb_by_idx(handle); + if(p_scb && !p_scb->svc_conn) + handle = 0; + } + + if (handle != 0) + { + BTM_ReadEScoLinkParms(sco_idx, &sco_data); + + p_scb->link_type = sco_data.link_type; + p_scb->tx_interval = sco_data.tx_interval; + p_scb->retrans_window = sco_data.retrans_window; + p_scb->air_mode = sco_data.air_mode; + + if (sco_data.air_mode == BTM_SCO_AIR_MODE_CVSD) + { + p_scb->out_pkt_len = sco_data.tx_pkt_len * 2; + p_scb->in_pkt_len = sco_data.rx_pkt_len * 2; + } + else { + p_scb->out_pkt_len = sco_data.tx_pkt_len; + p_scb->in_pkt_len = sco_data.rx_pkt_len; + } + + if ((p_buf = (BT_HDR *) osi_malloc(sizeof(BT_HDR))) != NULL) + { + p_buf->event = BTA_AG_SCO_OPEN_EVT; + p_buf->layer_specific = handle; + bta_sys_sendmsg(p_buf); + } + } + /* no match found; disconnect sco, init sco variables */ + else + { + bta_ag_cb.sco.p_curr_scb = NULL; + bta_ag_cb.sco.state = BTA_AG_SCO_SHUTDOWN_ST; + BTM_RemoveSco(sco_idx); + } +} + +/******************************************************************************* +** +** Function bta_ag_sco_disc_cback +** +** Description BTM SCO disconnection callback. +** +** +** Returns void +** +*******************************************************************************/ +static void bta_ag_sco_disc_cback(UINT16 sco_idx) +{ + BT_HDR *p_buf; + UINT16 handle = 0; + + APPL_TRACE_DEBUG ("bta_ag_sco_disc_cback(): sco_idx: 0x%x p_cur_scb: 0x%08x sco.state: %d", (unsigned int)sco_idx, (unsigned int)bta_ag_cb.sco.p_curr_scb, (unsigned int)bta_ag_cb.sco.state); + + APPL_TRACE_DEBUG ("bta_ag_sco_disc_cback(): scb[0] addr: 0x%08x in_use: %u sco_idx: 0x%x sco state: %u", + (unsigned int) &bta_ag_cb.scb[0], (unsigned int)bta_ag_cb.scb[0].in_use, (unsigned int)bta_ag_cb.scb[0].sco_idx, (unsigned int)bta_ag_cb.scb[0].state); + APPL_TRACE_DEBUG ("bta_ag_sco_disc_cback(): scb[1] addr: 0x%08x in_use: %u sco_idx: 0x%x sco state: %u", + (unsigned int) &bta_ag_cb.scb[1], (unsigned int) bta_ag_cb.scb[1].in_use, (unsigned int) bta_ag_cb.scb[1].sco_idx, (unsigned int) bta_ag_cb.scb[1].state); + + /* match callback to scb */ + if (bta_ag_cb.sco.p_curr_scb != NULL && bta_ag_cb.sco.p_curr_scb->in_use) + { + /* We only care about callbacks for the active SCO */ + if (bta_ag_cb.sco.p_curr_scb->sco_idx != sco_idx) + { + if (bta_ag_cb.sco.p_curr_scb->sco_idx != 0xFFFF) + return; + } + handle = bta_ag_scb_to_idx(bta_ag_cb.sco.p_curr_scb); + } + + if (handle != 0) + { +#if (BTM_SCO_HCI_INCLUDED == TRUE ) + tBTM_STATUS status = BTM_ConfigScoPath(BTM_SCO_ROUTE_PCM, NULL, NULL, TRUE); + APPL_TRACE_DEBUG("bta_ag_sco_disc_cback sco close config status = %d", status); + /* SCO clean up here */ + bta_ag_sco_co_close(); +#endif + +#if (BTM_WBS_INCLUDED == TRUE ) + /* Restore settings */ + if(bta_ag_cb.sco.p_curr_scb->inuse_codec == BTA_AG_CODEC_MSBC) + { + /* set_sco_codec(BTM_SCO_CODEC_NONE); we should get a close */ + BTM_WriteVoiceSettings (BTM_VOICE_SETTING_CVSD); + + /* If SCO open was initiated by AG and failed for mSBC, then attempt + mSBC with T1 settings i.e. 'Safe Settings'. If this fails, then switch to CVSD */ + if (bta_ag_sco_is_opening (bta_ag_cb.sco.p_curr_scb)) + { + if (bta_ag_cb.sco.p_curr_scb->codec_msbc_settings == BTA_AG_SCO_MSBC_SETTINGS_T2) + { + APPL_TRACE_DEBUG("Fallback to mSBC T1 settings"); + bta_ag_cb.sco.p_curr_scb->codec_msbc_settings = BTA_AG_SCO_MSBC_SETTINGS_T1; + } + else + { + APPL_TRACE_DEBUG("Fallback to CVSD settings"); + bta_ag_cb.sco.p_curr_scb->codec_fallback = TRUE; + } + } + } + + bta_ag_cb.sco.p_curr_scb->inuse_codec = BTA_AG_CODEC_NONE; +#endif + + if ((p_buf = (BT_HDR *) osi_malloc(sizeof(BT_HDR))) != NULL) + { + p_buf->event = BTA_AG_SCO_CLOSE_EVT; + p_buf->layer_specific = handle; + bta_sys_sendmsg(p_buf); + } + } + /* no match found */ + else + { + APPL_TRACE_DEBUG("no scb for ag_sco_disc_cback"); + + /* sco could be closed after scb dealloc'ed */ + if (bta_ag_cb.sco.p_curr_scb != NULL) + { + bta_ag_cb.sco.p_curr_scb->sco_idx = BTM_INVALID_SCO_INDEX; + bta_ag_cb.sco.p_curr_scb = NULL; + bta_ag_cb.sco.state = BTA_AG_SCO_SHUTDOWN_ST; + } + } +} + + +#if (BTM_SCO_HCI_INCLUDED == TRUE ) +/******************************************************************************* +** +** Function bta_ag_sco_read_cback +** +** Description Callback function is the callback function for incoming +** SCO data over HCI. +** +** Returns void +** +*******************************************************************************/ +static void bta_ag_sco_read_cback(UINT16 sco_inx, BT_HDR *p_data, tBTM_SCO_DATA_FLAG status) +{ + if (status != BTM_SCO_DATA_CORRECT) + { + APPL_TRACE_DEBUG("bta_ag_sco_read_cback: status(%d)", status); + } + + /* Callout function must free the data. */ + bta_ag_sco_co_in_data(p_data, status); + osi_free(p_data); +} +#endif +/******************************************************************************* +** +** Function bta_ag_remove_sco +** +** Description Removes the specified SCO from the system. +** If only_active is TRUE, then SCO is only removed if connected +** +** Returns BOOLEAN - TRUE if Sco removal was started +** +*******************************************************************************/ +static BOOLEAN bta_ag_remove_sco(tBTA_AG_SCB *p_scb, BOOLEAN only_active) +{ + BOOLEAN removed_started = FALSE; + tBTM_STATUS status; + + if (p_scb->sco_idx != BTM_INVALID_SCO_INDEX) + { + if (!only_active || p_scb->sco_idx == bta_ag_cb.sco.cur_idx) + { + status = BTM_RemoveSco(p_scb->sco_idx); + + APPL_TRACE_DEBUG("ag remove sco: inx 0x%04x, status:0x%x", p_scb->sco_idx, status); + + if (status == BTM_CMD_STARTED) + { + /* Sco is connected; set current control block */ + bta_ag_cb.sco.p_curr_scb = p_scb; + + removed_started = TRUE; + } + /* If no connection reset the sco handle */ + else if ( (status == BTM_SUCCESS) || (status == BTM_UNKNOWN_ADDR) ) + { + p_scb->sco_idx = BTM_INVALID_SCO_INDEX; + } + } + } + return removed_started; +} + + +/******************************************************************************* +** +** Function bta_ag_esco_connreq_cback +** +** Description BTM eSCO connection requests and eSCO change requests +** Only the connection requests are processed by BTA. +** +** Returns void +** +*******************************************************************************/ +static void bta_ag_esco_connreq_cback(tBTM_ESCO_EVT event, tBTM_ESCO_EVT_DATA *p_data) +{ + tBTA_AG_SCB *p_scb; + UINT16 handle; + UINT16 sco_inx = p_data->conn_evt.sco_inx; + + /* Only process connection requests */ + if (event == BTM_ESCO_CONN_REQ_EVT) + { + if ((handle = bta_ag_idx_by_bdaddr(BTM_ReadScoBdAddr(sco_inx))) != 0 && + ((p_scb = bta_ag_scb_by_idx(handle)) != NULL) && p_scb->svc_conn) + { + p_scb->sco_idx = sco_inx; + + /* If no other SCO active, allow this one */ + if (!bta_ag_cb.sco.p_curr_scb) + { + APPL_TRACE_EVENT("bta_ag_esco_connreq_cback: Accept Conn Request (sco_inx 0x%04x)", sco_inx); + bta_ag_sco_conn_rsp(p_scb, &p_data->conn_evt); + + bta_ag_cb.sco.state = BTA_AG_SCO_OPENING_ST; + bta_ag_cb.sco.p_curr_scb = p_scb; + bta_ag_cb.sco.cur_idx = p_scb->sco_idx; + } + else /* Begin a transfer: Close current SCO before responding */ + { + APPL_TRACE_DEBUG("bta_ag_esco_connreq_cback: Begin XFER"); + bta_ag_cb.sco.p_xfer_scb = p_scb; + bta_ag_cb.sco.conn_data = p_data->conn_evt; + bta_ag_cb.sco.state = BTA_AG_SCO_OPEN_XFER_ST; + + if (!bta_ag_remove_sco(bta_ag_cb.sco.p_curr_scb, TRUE)) + { + APPL_TRACE_ERROR("bta_ag_esco_connreq_cback: Nothing to remove so accept Conn Request (sco_inx 0x%04x)", sco_inx); + bta_ag_cb.sco.p_xfer_scb = NULL; + bta_ag_cb.sco.state = BTA_AG_SCO_LISTEN_ST; + + bta_ag_sco_conn_rsp(p_scb, &p_data->conn_evt); + } + } + } + /* If error occurred send reject response immediately */ + else + { + APPL_TRACE_WARNING("no scb for bta_ag_esco_connreq_cback or no resources"); + BTM_EScoConnRsp(p_data->conn_evt.sco_inx, HCI_ERR_HOST_REJECT_RESOURCES, NULL); + } + } + /* Received a change in the esco link */ + else if (event == BTM_ESCO_CHG_EVT) + { + APPL_TRACE_EVENT("eSCO change event (inx %d): rtrans %d, rxlen %d, txlen %d, txint %d", + p_data->chg_evt.sco_inx, + p_data->chg_evt.retrans_window, p_data->chg_evt.rx_pkt_len, + p_data->chg_evt.tx_pkt_len, p_data->chg_evt.tx_interval); + } +} + +/******************************************************************************* +** +** Function bta_ag_cback_sco +** +** Description Call application callback function with SCO event. +** +** +** Returns void +** +*******************************************************************************/ +static void bta_ag_cback_sco(tBTA_AG_SCB *p_scb, UINT8 event) +{ + tBTA_AG_HDR sco; + + sco.handle = bta_ag_scb_to_idx(p_scb); + sco.app_id = p_scb->app_id; + sco.sync_conn_handle = BTM_ReadScoHandle(p_scb->sco_idx); + + /* call close cback */ + (*bta_ag_cb.p_cback)(event, (tBTA_AG *) &sco); +} + +/******************************************************************************* +** +** Function bta_ag_create_sco +** +** Description Create a sco connection and is is_orig is TRUE means AG originate +** this connection, if FALSE it's peer device originate the connection. +** +** +** Returns void +** +*******************************************************************************/ +static void bta_ag_create_sco(tBTA_AG_SCB *p_scb, BOOLEAN is_orig) +{ + tBTM_STATUS status; + UINT8 *p_bd_addr = NULL; + tBTM_ESCO_PARAMS params; + UINT8 codec_index = BTA_AG_ESCO_SETTING_IDX_CVSD; +#if (BTM_WBS_INCLUDED == TRUE) + tBTA_AG_PEER_CODEC esco_codec = BTM_SCO_CODEC_CVSD; +#endif +#if (BTM_SCO_HCI_INCLUDED == TRUE) + tBTM_SCO_ROUTE_TYPE sco_route; + tBTA_HFP_CODEC_INFO codec_info = {BTA_HFP_SCO_CODEC_PCM}; + UINT32 pcm_sample_rate; +#endif + + /* Make sure this sco handle is not already in use */ + if (p_scb->sco_idx != BTM_INVALID_SCO_INDEX) + { + APPL_TRACE_WARNING("bta_ag_create_sco: Index 0x%04x Already In Use!", + p_scb->sco_idx); + return; + } + +#if (BTM_WBS_INCLUDED == TRUE) + + if ((p_scb->sco_codec == BTM_SCO_CODEC_MSBC) && !p_scb->codec_fallback && !p_scb->retry_with_sco_only) + { + esco_codec = BTM_SCO_CODEC_MSBC; + } + if (p_scb->codec_fallback) + { + p_scb->codec_fallback = FALSE; + /* Force AG to send +BCS for the next audio connection. */ + p_scb->codec_updated = TRUE; + } + /* If WBS included, use CVSD by default, index is 0 for CVSD by initialization */ + /* If eSCO codec is mSBC, index is T2 or T1 */ + if (esco_codec == BTM_SCO_CODEC_MSBC) + { + if (p_scb->codec_msbc_settings == BTA_AG_SCO_MSBC_SETTINGS_T2) + { + codec_index = BTA_AG_ESCO_SETTING_IDX_T2; + } + else + { + codec_index = BTA_AG_ESCO_SETTING_IDX_T1; + } + } + /* If eSCO codec is CVSD and eSC0 S4 is supported, index is S4 */ + else if ((esco_codec == BTM_SCO_CODEC_CVSD) && (p_scb->features & BTA_AG_FEAT_ESCO_S4) + && (p_scb->peer_features & BTA_AG_PEER_FEAT_ESCO_S4)) + { + codec_index = BTA_AG_ESCO_SETTING_IDX_S4; + } + +#else + if ((p_scb->features & BTA_AG_FEAT_ESCO_S4) && (p_scb->peer_features & BTA_AG_PEER_FEAT_ESCO_S4)) + { + codec_index = BTA_AG_ESCO_SETTING_IDX_S4; + } +#endif + params = bta_ag_esco_params[codec_index]; + + if(bta_ag_cb.sco.param_updated) /* If we do not use the default parameters */ + params = bta_ag_cb.sco.params; + + if(!bta_ag_cb.sco.param_updated) + { +#if (BTM_WBS_INCLUDED == TRUE) + if (esco_codec == BTM_SCO_CODEC_CVSD) /* For CVSD */ +#endif + { + if (codec_index == BTA_AG_ESCO_SETTING_IDX_CVSD) + { + /* Use the application packet types (5 slot EV packets not allowed) */ + params.packet_types = p_bta_ag_cfg->sco_pkt_types | + BTM_SCO_PKT_TYPES_MASK_NO_2_EV5 | + BTM_SCO_PKT_TYPES_MASK_NO_3_EV5; + } + } + } + + /* if initiating, set current scb and peer bd addr */ + if (is_orig) + { + /* Attempt to use eSCO if remote host supports HFP >= 1.5 */ + /* Need to find out from SIG if HSP can use eSCO; for now use SCO */ + if (p_scb->conn_service == BTA_AG_HFP && p_scb->peer_version >= HFP_VERSION_1_5 && !p_scb->retry_with_sco_only) + { + BTM_SetEScoMode(BTM_LINK_TYPE_ESCO, ¶ms); + /* If ESCO or EDR ESCO, retry with SCO only in case of failure */ + if((params.packet_types & BTM_ESCO_LINK_ONLY_MASK) + ||!((params.packet_types & ~(BTM_ESCO_LINK_ONLY_MASK | BTM_SCO_LINK_ONLY_MASK)) ^ BTA_AG_NO_EDR_ESCO)) + { +#if (BTM_WBS_INCLUDED == TRUE) + if (esco_codec != BTA_AG_CODEC_MSBC) + { + p_scb->retry_with_sco_only = TRUE; + APPL_TRACE_API("Setting retry_with_sco_only to TRUE"); + } + else /* Do not use SCO when using mSBC */ + { + p_scb->retry_with_sco_only = FALSE; + APPL_TRACE_API("Setting retry_with_sco_only to FALSE"); + } +#else + p_scb->retry_with_sco_only = TRUE; + APPL_TRACE_API("Setting retry_with_sco_only to TRUE"); +#endif + } + } + else + { + if(p_scb->retry_with_sco_only){ + APPL_TRACE_API("retrying with SCO only"); + } + p_scb->retry_with_sco_only = FALSE; + BTM_SetEScoMode(BTM_LINK_TYPE_SCO, ¶ms); + } + + bta_ag_cb.sco.p_curr_scb = p_scb; + /* tell sys to stop av if any */ + bta_sys_sco_use(BTA_ID_AG, p_scb->app_id, p_scb->peer_addr); + +#if (BTM_SCO_HCI_INCLUDED == TRUE) +#if (BTM_WBS_INCLUDED == TRUE) + /* Allow any platform specific pre-SCO set up to take place */ + bta_ag_sco_audio_state(bta_ag_scb_to_idx(p_scb), p_scb->app_id, SCO_STATE_SETUP, esco_codec); + + /* This setting may not be necessary */ + /* To be verified with stable 2049 boards */ + if (esco_codec == BTA_AG_CODEC_MSBC) + BTM_WriteVoiceSettings(BTM_VOICE_SETTING_TRANS); + else + BTM_WriteVoiceSettings(BTM_VOICE_SETTING_CVSD); + /* save the current codec because sco_codec can be updated while SCO is open. */ + p_scb->inuse_codec = esco_codec; +#else + /* Allow any platform specific pre-SCO set up to take place */ + bta_ag_sco_audio_state(bta_ag_scb_to_idx(p_scb), p_scb->app_id, SCO_STATE_SETUP); +#endif +#endif + +#if (BTM_SCO_HCI_INCLUDED == TRUE) +#if (BTM_WBS_INCLUDED == TRUE) + if (esco_codec == BTA_AG_CODEC_MSBC) + pcm_sample_rate = BTA_HFP_SCO_SAMP_RATE_16K; +#endif + pcm_sample_rate = BTA_HFP_SCO_SAMP_RATE_8K; + sco_route = bta_ag_sco_co_init(pcm_sample_rate, pcm_sample_rate, &codec_info, p_scb->app_id); +#endif + + +#if (BTM_SCO_HCI_INCLUDED == TRUE) + /* initialize SCO setup, no voice setting for AG, data rate <==> sample rate */ + BTM_ConfigScoPath(sco_route, bta_ag_sco_read_cback, NULL, TRUE); +#endif + bta_ag_cb.sco.cur_idx = p_scb->sco_idx; + } + else{ + p_scb->retry_with_sco_only = FALSE; + } + + p_bd_addr = p_scb->peer_addr; + + status = BTM_CreateSco(p_bd_addr, is_orig, params.packet_types, &p_scb->sco_idx, bta_ag_sco_conn_cback, + bta_ag_sco_disc_cback); + + if (status == BTM_CMD_STARTED) + { + if (!is_orig) + { + BTM_RegForEScoEvts(p_scb->sco_idx, bta_ag_esco_connreq_cback); + } + else /* Initiating the connection, set the current sco handle */ + { + bta_ag_cb.sco.cur_idx = p_scb->sco_idx; + } + } + + APPL_TRACE_API("ag create sco: orig %d, inx 0x%04x, status 0x%x, pkt types 0x%04x", + is_orig, p_scb->sco_idx, status, params.packet_types); +} + +#if (BTM_WBS_INCLUDED == TRUE ) +/******************************************************************************* +** +** Function bta_ag_attempt_msbc_safe_settings +** +** Description Checks if ESCO connection needs to be attempted using mSBC T1(safe) settings +** +** +** Returns TRUE if T1 settings has to be used, FALSE otherwise +** +*******************************************************************************/ +BOOLEAN bta_ag_attempt_msbc_safe_settings(tBTA_AG_SCB *p_scb) +{ + if (p_scb->svc_conn && p_scb->sco_codec == BTM_SCO_CODEC_MSBC && + p_scb->codec_msbc_settings == BTA_AG_SCO_MSBC_SETTINGS_T1) + return TRUE; + else + return FALSE; +} + +/******************************************************************************* +** +** Function bta_ag_cn_timer_cback +** +** Description +** +** +** Returns void +** +*******************************************************************************/ +static void bta_ag_cn_timer_cback (TIMER_LIST_ENT *p_tle) +{ + tBTA_AG_SCB *p_scb; + + if (p_tle) + { + p_scb = (tBTA_AG_SCB *)p_tle->param; + + if (p_scb) + { + /* Announce that codec negotiation failed. */ + bta_ag_sco_codec_nego(p_scb, FALSE); + + /* call app callback */ + bta_ag_cback_sco(p_scb, BTA_AG_AUDIO_CLOSE_EVT); + } + } +} + +/******************************************************************************* +** +** Function bta_ag_codec_negotiate +** +** Description Initiate codec negotiation by sending AT command. +** If not necessary, skip negotiation. +** +** Returns void +** +*******************************************************************************/ +void bta_ag_codec_negotiate(tBTA_AG_SCB *p_scb) +{ + bta_ag_cb.sco.p_curr_scb = p_scb; + + if ((p_scb->codec_updated || p_scb->codec_fallback || + bta_ag_attempt_msbc_safe_settings(p_scb)) && + (p_scb->peer_features & BTA_AG_PEER_FEAT_CODEC)) + { + /* Change the power mode to Active until sco open is completed. */ + bta_sys_busy(BTA_ID_AG, p_scb->app_id, p_scb->peer_addr); + + /* Send +BCS to the peer */ + bta_ag_send_bcs(p_scb, NULL); + + /* Start timer to handle timeout */ + p_scb->cn_timer.p_cback = (TIMER_CBACK*)&bta_ag_cn_timer_cback; + p_scb->cn_timer.param = (INT32)p_scb; + bta_sys_start_timer(&p_scb->cn_timer, 0, BTA_AG_CODEC_NEGO_TIMEOUT); + } + else + { + /* use same codec type as previous SCO connection, skip codec negotiation */ + APPL_TRACE_DEBUG("use same codec type as previous SCO connection,skip codec negotiation"); + bta_ag_sco_codec_nego(p_scb, TRUE); + } +} +#endif + +/******************************************************************************* +** +** Function bta_ag_sco_event +** +** Description AG Sco State Machine +** +** +** Returns void +** +*******************************************************************************/ +static void bta_ag_sco_event(tBTA_AG_SCB *p_scb, UINT8 event) +{ + tBTA_AG_SCO_CB *p_sco = &bta_ag_cb.sco; +#if (BTM_WBS_INCLUDED == TRUE) + tBTA_AG_SCB *p_cn_scb = NULL; /* For codec negotiation */ +#endif + UINT8 in_state = p_sco->state; + APPL_TRACE_EVENT("BTA ag sco evt (hdl 0x%04x): State %d (%s), Event %d (%s)", + p_scb->sco_idx, p_sco->state, + bta_ag_sco_state_str(p_sco->state), event, bta_ag_sco_evt_str(event)); + +#if (BTM_SCO_HCI_INCLUDED == TRUE) + BT_HDR *p_buf; + if (event == BTA_AG_SCO_CI_DATA_E) + { + UINT16 pkt_offset = 1 + HCI_SCO_PREAMBLE_SIZE; + UINT16 len_to_send = 0; + while (TRUE) + { + p_buf = osi_calloc(sizeof(BT_HDR) + pkt_offset + p_scb->out_pkt_len); + if (!p_buf) { + APPL_TRACE_WARNING("%s, no mem", __FUNCTION__); + break; + } + p_buf->offset = pkt_offset; + len_to_send = bta_ag_sco_co_out_data(p_buf->data + pkt_offset); + p_buf->len = len_to_send; + if (len_to_send == p_scb->out_pkt_len) { + if (p_sco->state == BTA_AG_SCO_OPEN_ST) { + tBTM_STATUS write_stat = BTM_WriteScoData(p_sco->p_curr_scb->sco_idx, p_buf); + if (write_stat != BTM_SUCCESS) { + break; + } + } else { + osi_free(p_buf); + } + } else { + osi_free(p_buf); + break; + } + } + return; + } +#endif + + /* State Machine Start */ + switch (p_sco->state) + { + case BTA_AG_SCO_SHUTDOWN_ST: + switch (event) + { + case BTA_AG_SCO_LISTEN_E: + /* create sco listen connection */ + bta_ag_create_sco(p_scb, FALSE); + p_sco->state = BTA_AG_SCO_LISTEN_ST; + break; + + default: + APPL_TRACE_WARNING("BTA_AG_SCO_SHUTDOWN_ST: Ignoring event %d", event); + break; + } + break; + + case BTA_AG_SCO_LISTEN_ST: + switch (event) + { + case BTA_AG_SCO_LISTEN_E: + /* create sco listen connection (Additional channel) */ + bta_ag_create_sco(p_scb, FALSE); + break; + + case BTA_AG_SCO_OPEN_E: + /* remove listening connection */ + bta_ag_remove_sco(p_scb, FALSE); +#if (BTM_WBS_INCLUDED == TRUE ) + /* start codec negotiation */ + p_sco->state = BTA_AG_SCO_CODEC_ST; + p_cn_scb = p_scb; +#else + /* create sco connection to peer */ + bta_ag_create_sco(p_scb, TRUE); + p_sco->state = BTA_AG_SCO_OPENING_ST; +#endif + break; + + case BTA_AG_SCO_SHUTDOWN_E: + /* remove listening connection */ + bta_ag_remove_sco(p_scb, FALSE); + + if (p_scb == p_sco->p_curr_scb) + p_sco->p_curr_scb = NULL; + + /* If last SCO instance then finish shutting down */ + if (!bta_ag_other_scb_open(p_scb)) + { + p_sco->state = BTA_AG_SCO_SHUTDOWN_ST; + } + break; + + case BTA_AG_SCO_CLOSE_E: + /* remove listening connection */ + /* Ignore the event. We need to keep listening SCO for the active SLC */ + APPL_TRACE_WARNING("BTA_AG_SCO_LISTEN_ST: Ignoring event %d", event); + break; + + case BTA_AG_SCO_CONN_CLOSE_E: + /* sco failed; create sco listen connection */ + bta_ag_create_sco(p_scb, FALSE); + p_sco->state = BTA_AG_SCO_LISTEN_ST; + break; + + default: + APPL_TRACE_WARNING("BTA_AG_SCO_LISTEN_ST: Ignoring event %d", event); + break; + } + break; + +#if (BTM_WBS_INCLUDED == TRUE ) + case BTA_AG_SCO_CODEC_ST: + switch (event) + { + case BTA_AG_SCO_LISTEN_E: + /* create sco listen connection (Additional channel) */ + bta_ag_create_sco(p_scb, FALSE); + break; + + case BTA_AG_SCO_CN_DONE_E: + /* create sco connection to peer */ + bta_ag_create_sco(p_scb, TRUE); + p_sco->state = BTA_AG_SCO_OPENING_ST; + break; + + case BTA_AG_SCO_XFER_E: + /* save xfer scb */ + p_sco->p_xfer_scb = p_scb; + p_sco->state = BTA_AG_SCO_CLOSE_XFER_ST; + break; + + case BTA_AG_SCO_SHUTDOWN_E: + /* remove listening connection */ + bta_ag_remove_sco(p_scb, FALSE); + + if (p_scb == p_sco->p_curr_scb) + p_sco->p_curr_scb = NULL; + + /* If last SCO instance then finish shutting down */ + if (!bta_ag_other_scb_open(p_scb)) + { + p_sco->state = BTA_AG_SCO_SHUTDOWN_ST; + } + break; + + case BTA_AG_SCO_CLOSE_E: + /* sco open is not started yet. just go back to listening */ + p_sco->state = BTA_AG_SCO_LISTEN_ST; + break; + + case BTA_AG_SCO_CONN_CLOSE_E: + /* sco failed; create sco listen connection */ + bta_ag_create_sco(p_scb, FALSE); + p_sco->state = BTA_AG_SCO_LISTEN_ST; + break; + + default: + APPL_TRACE_WARNING("BTA_AG_SCO_CODEC_ST: Ignoring event %d", event); + break; + } + break; +#endif + + case BTA_AG_SCO_OPENING_ST: + switch (event) + { + case BTA_AG_SCO_LISTEN_E: + /* second headset has now joined */ + /* create sco listen connection (Additional channel) */ + if (p_scb != p_sco->p_curr_scb) + { + bta_ag_create_sco(p_scb, FALSE); + } + break; + +#if (BTM_WBS_INCLUDED == TRUE) + case BTA_AG_SCO_REOPEN_E: + /* start codec negotiation */ + p_sco->state = BTA_AG_SCO_CODEC_ST; + p_cn_scb = p_scb; + break; +#endif + + case BTA_AG_SCO_XFER_E: + /* save xfer scb */ + p_sco->p_xfer_scb = p_scb; + p_sco->state = BTA_AG_SCO_CLOSE_XFER_ST; + break; + + case BTA_AG_SCO_CLOSE_E: + p_sco->state = BTA_AG_SCO_OPEN_CL_ST; + break; + + case BTA_AG_SCO_SHUTDOWN_E: + /* If not opening scb, just close it */ + if (p_scb != p_sco->p_curr_scb) + { + /* remove listening connection */ + bta_ag_remove_sco(p_scb, FALSE); + } + else + p_sco->state = BTA_AG_SCO_SHUTTING_ST; + + break; + + case BTA_AG_SCO_CONN_OPEN_E: + p_sco->state = BTA_AG_SCO_OPEN_ST; + break; + + case BTA_AG_SCO_CONN_CLOSE_E: + /* sco failed; create sco listen connection */ + bta_ag_create_sco(p_scb, FALSE); + p_sco->state = BTA_AG_SCO_LISTEN_ST; + break; + + default: + APPL_TRACE_WARNING("BTA_AG_SCO_OPENING_ST: Ignoring event %d", event); + break; + } + break; + + case BTA_AG_SCO_OPEN_CL_ST: + switch (event) + { + case BTA_AG_SCO_XFER_E: + /* save xfer scb */ + p_sco->p_xfer_scb = p_scb; + + p_sco->state = BTA_AG_SCO_CLOSE_XFER_ST; + break; + + case BTA_AG_SCO_OPEN_E: + p_sco->state = BTA_AG_SCO_OPENING_ST; + break; + + case BTA_AG_SCO_SHUTDOWN_E: + /* If not opening scb, just close it */ + if (p_scb != p_sco->p_curr_scb) + { + /* remove listening connection */ + bta_ag_remove_sco(p_scb, FALSE); + } + else + p_sco->state = BTA_AG_SCO_SHUTTING_ST; + + break; + + case BTA_AG_SCO_CONN_OPEN_E: + /* close sco connection */ + bta_ag_remove_sco(p_scb, TRUE); + + p_sco->state = BTA_AG_SCO_CLOSING_ST; + break; + + case BTA_AG_SCO_CONN_CLOSE_E: + /* sco failed; create sco listen connection */ + + p_sco->state = BTA_AG_SCO_LISTEN_ST; + break; + + default: + APPL_TRACE_WARNING("BTA_AG_SCO_OPEN_CL_ST: Ignoring event %d", event); + break; + } + break; + + case BTA_AG_SCO_OPEN_XFER_ST: + switch (event) + { + case BTA_AG_SCO_CLOSE_E: + /* close sco connection */ + bta_ag_remove_sco(p_scb, TRUE); + p_sco->state = BTA_AG_SCO_CLOSING_ST; + break; + + case BTA_AG_SCO_SHUTDOWN_E: + /* remove all connection */ + bta_ag_remove_sco(p_scb, FALSE); + p_sco->state = BTA_AG_SCO_SHUTTING_ST; + break; + + case BTA_AG_SCO_CONN_CLOSE_E: + /* closed sco; place in listen mode and + accept the transferred connection */ + bta_ag_create_sco(p_scb, FALSE); /* Back into listen mode */ + /* Accept sco connection with xfer scb */ + bta_ag_sco_conn_rsp(p_sco->p_xfer_scb, &p_sco->conn_data); + p_sco->state = BTA_AG_SCO_OPENING_ST; + p_sco->p_curr_scb = p_sco->p_xfer_scb; + p_sco->cur_idx = p_sco->p_xfer_scb->sco_idx; + p_sco->p_xfer_scb = NULL; + break; + + default: + APPL_TRACE_WARNING("BTA_AG_SCO_OPEN_XFER_ST: Ignoring event %d", event); + break; + } + break; + + case BTA_AG_SCO_OPEN_ST: + switch (event) + { + case BTA_AG_SCO_LISTEN_E: + /* second headset has now joined */ + /* create sco listen connection (Additional channel) */ + if (p_scb != p_sco->p_curr_scb) + { + bta_ag_create_sco(p_scb, FALSE); + } + break; + + case BTA_AG_SCO_XFER_E: + /* close current sco connection */ + bta_ag_remove_sco(p_sco->p_curr_scb, TRUE); + /* save xfer scb */ + p_sco->p_xfer_scb = p_scb; + p_sco->state = BTA_AG_SCO_CLOSE_XFER_ST; + break; + + case BTA_AG_SCO_CLOSE_E: + /* close sco connection if active */ + if (bta_ag_remove_sco(p_scb, TRUE)) + { + p_sco->state = BTA_AG_SCO_CLOSING_ST; + } + break; + + case BTA_AG_SCO_SHUTDOWN_E: + /* remove all listening connections */ + bta_ag_remove_sco(p_scb, FALSE); + /* If SCO was active on this scb, close it */ + if (p_scb == p_sco->p_curr_scb) + { + p_sco->state = BTA_AG_SCO_SHUTTING_ST; + } + break; + + case BTA_AG_SCO_CONN_CLOSE_E: + /* peer closed sco; create sco listen connection */ + bta_ag_create_sco(p_scb, FALSE); + p_sco->state = BTA_AG_SCO_LISTEN_ST; + break; + + default: + APPL_TRACE_WARNING("BTA_AG_SCO_OPEN_ST: Ignoring event %d", event); + break; + } + break; + + case BTA_AG_SCO_CLOSING_ST: + switch (event) + { + case BTA_AG_SCO_LISTEN_E: + /* create sco listen connection (Additional channel) */ + if (p_scb != p_sco->p_curr_scb) + { + bta_ag_create_sco(p_scb, FALSE); + } + break; + + case BTA_AG_SCO_OPEN_E: + p_sco->state = BTA_AG_SCO_CLOSE_OP_ST; + break; + + case BTA_AG_SCO_XFER_E: + /* save xfer scb */ + p_sco->p_xfer_scb = p_scb; + p_sco->state = BTA_AG_SCO_CLOSE_XFER_ST; + break; + + case BTA_AG_SCO_SHUTDOWN_E: + /* If not closing scb, just close it */ + if (p_scb != p_sco->p_curr_scb) + { + /* remove listening connection */ + bta_ag_remove_sco(p_scb, FALSE); + } + else + p_sco->state = BTA_AG_SCO_SHUTTING_ST; + break; + + case BTA_AG_SCO_CONN_CLOSE_E: + /* peer closed sco; create sco listen connection */ + bta_ag_create_sco(p_scb, FALSE); + p_sco->state = BTA_AG_SCO_LISTEN_ST; + break; + + default: + APPL_TRACE_WARNING("BTA_AG_SCO_CLOSING_ST: Ignoring event %d", event); + break; + } + break; + + case BTA_AG_SCO_CLOSE_OP_ST: + switch (event) + { + case BTA_AG_SCO_CLOSE_E: + p_sco->state = BTA_AG_SCO_CLOSING_ST; + break; + + case BTA_AG_SCO_SHUTDOWN_E: + p_sco->state = BTA_AG_SCO_SHUTTING_ST; + break; + + case BTA_AG_SCO_CONN_CLOSE_E: +#if (BTM_WBS_INCLUDED == TRUE ) + /* start codec negotiation */ + p_sco->state = BTA_AG_SCO_CODEC_ST; + p_cn_scb = p_scb; +#else + /* open sco connection */ + bta_ag_create_sco(p_scb, TRUE); + p_sco->state = BTA_AG_SCO_OPENING_ST; +#endif + break; + + case BTA_AG_SCO_LISTEN_E: + /* create sco listen connection (Additional channel) */ + if (p_scb != p_sco->p_curr_scb) + { + bta_ag_create_sco(p_scb, FALSE); + } + break; + + default: + APPL_TRACE_WARNING("BTA_AG_SCO_CLOSE_OP_ST: Ignoring event %d", event); + break; + } + break; + + case BTA_AG_SCO_CLOSE_XFER_ST: + switch (event) + { + case BTA_AG_SCO_CONN_OPEN_E: + /* close sco connection so headset can be transferred + Probably entered this state from "opening state" */ + bta_ag_remove_sco(p_scb, TRUE); + break; + + case BTA_AG_SCO_CLOSE_E: + /* clear xfer scb */ + p_sco->p_xfer_scb = NULL; + p_sco->state = BTA_AG_SCO_CLOSING_ST; + break; + + case BTA_AG_SCO_SHUTDOWN_E: + /* clear xfer scb */ + p_sco->p_xfer_scb = NULL; + p_sco->state = BTA_AG_SCO_SHUTTING_ST; + break; + + case BTA_AG_SCO_CONN_CLOSE_E: + /* closed sco; place old sco in listen mode, + take current sco out of listen, and + create originating sco for current */ + bta_ag_create_sco(p_scb, FALSE); + bta_ag_remove_sco(p_sco->p_xfer_scb, FALSE); + +#if (BTM_WBS_INCLUDED == TRUE) + /* start codec negotiation */ + p_sco->state = BTA_AG_SCO_CODEC_ST; + p_cn_scb = p_sco->p_xfer_scb; + p_sco->p_xfer_scb = NULL; +#else + /* create sco connection to peer */ + bta_ag_create_sco(p_sco->p_xfer_scb, TRUE); + p_sco->p_xfer_scb = NULL; + p_sco->state = BTA_AG_SCO_OPENING_ST; +#endif + break; + + default: + APPL_TRACE_WARNING("BTA_AG_SCO_CLOSE_XFER_ST: Ignoring event %d", event); + break; + } + break; + + case BTA_AG_SCO_SHUTTING_ST: + switch (event) + { + case BTA_AG_SCO_CONN_OPEN_E: + /* close sco connection; wait for conn close event */ + bta_ag_remove_sco(p_scb, TRUE); + break; + + case BTA_AG_SCO_CONN_CLOSE_E: + /* If last SCO instance then finish shutting down */ + if (!bta_ag_other_scb_open(p_scb)) + { + p_sco->state = BTA_AG_SCO_SHUTDOWN_ST; + } + else /* Other instance is still listening */ + { + p_sco->state = BTA_AG_SCO_LISTEN_ST; + } + + /* If SCO closed for other HS which is not being disconnected, + then create listen sco connection for it as scb still open */ + if (bta_ag_scb_open(p_scb)) + { + bta_ag_create_sco(p_scb, FALSE); + p_sco->state = BTA_AG_SCO_LISTEN_ST; + } + + if (p_scb == p_sco->p_curr_scb) + { + p_sco->p_curr_scb->sco_idx = BTM_INVALID_SCO_INDEX; + p_sco->p_curr_scb = NULL; + } + break; + + case BTA_AG_SCO_LISTEN_E: + /* create sco listen connection (Additional channel) */ + if (p_scb != p_sco->p_curr_scb) + { + bta_ag_create_sco(p_scb, FALSE); + } + break; + + case BTA_AG_SCO_SHUTDOWN_E: + if (!bta_ag_other_scb_open(p_scb)) + { + p_sco->state = BTA_AG_SCO_SHUTDOWN_ST; + } + else /* Other instance is still listening */ + { + p_sco->state = BTA_AG_SCO_LISTEN_ST; + } + + if (p_scb == p_sco->p_curr_scb) + { + p_sco->p_curr_scb->sco_idx = BTM_INVALID_SCO_INDEX; + p_sco->p_curr_scb = NULL; + } + break; + + default: + APPL_TRACE_WARNING("BTA_AG_SCO_SHUTTING_ST: Ignoring event %d", event); + break; + } + break; + + default: + break; + } + + if (p_sco->state != in_state) + { + APPL_TRACE_EVENT("BTA AG SCO State Change: [%s] -> [%s] after Event [%s]", + bta_ag_sco_state_str(in_state), + bta_ag_sco_state_str(p_sco->state), + bta_ag_sco_evt_str(event)); + } + +#if (BTM_WBS_INCLUDED == TRUE) + if (p_cn_scb) + { + bta_ag_codec_negotiate(p_cn_scb); + } +#endif +} + +/******************************************************************************* +** +** Function bta_ag_sco_is_open +** +** Description Check if sco is open for this scb. +** +** +** Returns TRUE if sco open for this scb, FALSE otherwise. +** +*******************************************************************************/ +BOOLEAN bta_ag_sco_is_open(tBTA_AG_SCB *p_scb) +{ + return ((bta_ag_cb.sco.state == BTA_AG_SCO_OPEN_ST) && + (bta_ag_cb.sco.p_curr_scb == p_scb)); +} + +/******************************************************************************* +** +** Function bta_ag_sco_is_opening +** +** Description Check if sco is in Opening state. +** +** +** Returns TRUE if sco is in Opening state for this scb, FALSE otherwise. +** +*******************************************************************************/ +BOOLEAN bta_ag_sco_is_opening(tBTA_AG_SCB *p_scb) +{ +#if (BTM_WBS_INCLUDED == TRUE ) + return (((bta_ag_cb.sco.state == BTA_AG_SCO_OPENING_ST) || + (bta_ag_cb.sco.state == BTA_AG_SCO_OPENING_ST)) && + (bta_ag_cb.sco.p_curr_scb == p_scb)); +#else + return ((bta_ag_cb.sco.state == BTA_AG_SCO_OPENING_ST) && + (bta_ag_cb.sco.p_curr_scb == p_scb)); +#endif +} + +/******************************************************************************* +** +** Function bta_ag_sco_listen +** +** Description +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_sco_listen(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data) +{ + UNUSED(p_data); + bta_ag_sco_event(p_scb, BTA_AG_SCO_LISTEN_E); +} + +/******************************************************************************* +** +** Function bta_ag_sco_open +** +** Description +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_sco_open(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data) +{ + UINT8 event; + UNUSED(p_data); + + /* if another scb using sco, this is a transfer */ + if (bta_ag_cb.sco.p_curr_scb != NULL && bta_ag_cb.sco.p_curr_scb != p_scb) + { + event = BTA_AG_SCO_XFER_E; + } + /* else it is an open */ + else + { + event = BTA_AG_SCO_OPEN_E; + } + + bta_ag_sco_event(p_scb, event); +} + +/******************************************************************************* +** +** Function bta_ag_sco_close +** +** Description +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_sco_close(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data) +{ + UNUSED(p_data); + + /* if scb is in use */ +#if (BTM_WBS_INCLUDED == TRUE ) + /* sco_idx is not allocated in SCO_CODEC_ST, we still need to move to listening state. */ + if ((p_scb->sco_idx != BTM_INVALID_SCO_INDEX) || (bta_ag_cb.sco.state == BTA_AG_SCO_CODEC_ST)) +#else + if (p_scb->sco_idx != BTM_INVALID_SCO_INDEX) +#endif + { + APPL_TRACE_DEBUG("bta_ag_sco_close: sco_inx = %d", p_scb->sco_idx); + bta_ag_sco_event(p_scb, BTA_AG_SCO_CLOSE_E); + } +} + +#if (BTM_WBS_INCLUDED == TRUE ) + +/******************************************************************************* +** +** Function bta_ag_sco_codec_nego +** +** Description +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_sco_codec_nego(tBTA_AG_SCB *p_scb, BOOLEAN result) +{ + if(result == TRUE) + { + /* Subsequent sco connection will skip codec negotiation */ + p_scb->codec_updated = FALSE; + + bta_ag_sco_event(p_scb, BTA_AG_SCO_CN_DONE_E); + } + else /* codec negotiation failed */ + bta_ag_sco_event(p_scb, BTA_AG_SCO_CLOSE_E); +} +#endif + +/******************************************************************************* +** +** Function bta_ag_pkt_stat_nums +** +** Description Get the number of packet states +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_pkt_stat_nums(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data) +{ + UNUSED(p_scb); + +#if (BTM_SCO_HCI_INCLUDED == TRUE) + tBTA_AG_PKT_STAT_NUMS pkt_stat_nums; + uint16_t sync_conn_handle = p_data->pkt_stat.sync_conn_handle; + BTM_PktStatNumsGet(sync_conn_handle, (tBTM_SCO_PKT_STAT_NUMS *) &pkt_stat_nums); + + /* call app cback */ + if (bta_ag_cb.p_cback) { + (*bta_ag_cb.p_cback)(BTA_AG_PKT_NUMS_GET_EVT, (tBTA_AG*) &pkt_stat_nums); + } +#endif +} + +/******************************************************************************* +** +** Function bta_ag_sco_shutdown +** +** Description +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_sco_shutdown(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data) +{ + UNUSED(p_data); + + bta_ag_sco_event(p_scb, BTA_AG_SCO_SHUTDOWN_E); +} + +/******************************************************************************* +** +** Function bta_ag_sco_conn_open +** +** Description +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_sco_conn_open(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data) +{ + UNUSED(p_data); + + bta_ag_sco_event(p_scb, BTA_AG_SCO_CONN_OPEN_E); + + bta_sys_sco_open(BTA_ID_AG, p_scb->app_id, p_scb->peer_addr); + +#if (BTM_SCO_HCI_INCLUDED == TRUE) + +#if (BTM_WBS_INCLUDED == TRUE) + bta_ag_sco_audio_state(bta_ag_scb_to_idx(p_scb), p_scb->app_id, SCO_STATE_ON, p_scb->inuse_codec); +#else + bta_ag_sco_audio_state(bta_ag_scb_to_idx(p_scb), p_scb->app_id, SCO_STATE_ON); +#endif + /* open SCO codec if SCO is routed through transport */ + bta_ag_sco_co_open(bta_ag_scb_to_idx(p_scb), p_scb->air_mode, p_scb->out_pkt_len, BTA_AG_CI_SCO_DATA_EVT); +#endif + +#if (BTM_WBS_INCLUDED == TRUE) + /* call app callback */ + if (p_scb->sco_codec == BTA_AG_CODEC_MSBC) { + bta_ag_cback_sco(p_scb, BTA_AG_AUDIO_MSBC_OPEN_EVT); + } else { + bta_ag_cback_sco(p_scb, BTA_AG_AUDIO_OPEN_EVT); + } +#else + bta_ag_cback_sco(p_scb, BTA_AG_AUDIO_OPEN_EVT); +#endif + p_scb->retry_with_sco_only = FALSE; +#if (BTM_WBS_INCLUDED == TRUE) + /* reset to mSBC T2 settings as the preferred */ + p_scb->codec_msbc_settings = BTA_AG_SCO_MSBC_SETTINGS_T2; +#endif +} + +/******************************************************************************* +** +** Function bta_ag_sco_conn_close +** +** Description This function is called when a SCO connection is closed +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_sco_conn_close(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data) +{ +#if (BTM_SCO_HCI_INCLUDED == TRUE) + UINT16 handle = bta_ag_scb_to_idx(p_scb); +#endif + UNUSED(p_data); + + /* clear current scb */ + bta_ag_cb.sco.p_curr_scb = NULL; + p_scb->sco_idx = BTM_INVALID_SCO_INDEX; + +#if (BTM_WBS_INCLUDED == TRUE) + /* codec_fallback is set when AG is initiator and connection failed for mSBC. */ + /* OR if codec is msbc and T2 settings failed, then retry Safe T1 settings */ + if ((p_scb->codec_fallback && p_scb->svc_conn) || + bta_ag_attempt_msbc_safe_settings(p_scb)) + { + bta_ag_sco_event(p_scb, BTA_AG_SCO_REOPEN_E); + } + else if (p_scb->retry_with_sco_only && p_scb->svc_conn) + { + /* retry_with_sco_only is set when AG is initiator and connection failed for eSCO */ + bta_ag_create_sco(p_scb, TRUE); + } +#else + /* retry_with_sco_only, will be set only when AG is initiator + ** and AG is first trying to establish an eSCO connection */ + if (p_scb->retry_with_sco_only && p_scb->svc_conn) + { + bta_ag_create_sco(p_scb, TRUE); + } +#endif + else + { +#if (BTM_SCO_HCI_INCLUDED == TRUE) + sco_state_t sco_state = bta_ag_cb.sco.p_xfer_scb ? SCO_STATE_OFF_TRANSFER : SCO_STATE_OFF; +#if (BTM_WBS_INCLUDED == TRUE) + /* Indicate if the closing of audio is because of transfer */ + bta_ag_sco_audio_state(handle, p_scb->app_id, sco_state, p_scb->inuse_codec); +#else + /* Indicate if the closing of audio is because of transfer */ + bta_ag_sco_audio_state(handle, p_scb->app_id, sco_state); +#endif +#endif + bta_ag_sco_event(p_scb, BTA_AG_SCO_CONN_CLOSE_E); + + bta_sys_sco_close(BTA_ID_AG, p_scb->app_id, p_scb->peer_addr); + + /* if av got suspended by this call, let it resume. */ + /* In case call stays alive regardless of sco, av should not be affected. */ + if(((p_scb->call_ind == BTA_AG_CALL_INACTIVE) && (p_scb->callsetup_ind == BTA_AG_CALLSETUP_NONE)) + || (p_scb->post_sco == BTA_AG_POST_SCO_CALL_END)) + { + bta_sys_sco_unuse(BTA_ID_AG, p_scb->app_id, p_scb->peer_addr); + } + + /* call app callback */ + bta_ag_cback_sco(p_scb, BTA_AG_AUDIO_CLOSE_EVT); +#if (BTM_WBS_INCLUDED == TRUE) + p_scb->codec_msbc_settings = BTA_AG_SCO_MSBC_SETTINGS_T2; +#endif + } + p_scb->retry_with_sco_only = FALSE; +} + +/******************************************************************************* +** +** Function bta_ag_sco_conn_rsp +** +** Description Process the SCO connection request +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_sco_conn_rsp(tBTA_AG_SCB *p_scb, tBTM_ESCO_CONN_REQ_EVT_DATA *p_data) +{ + tBTM_ESCO_PARAMS resp; + UINT8 hci_status = HCI_SUCCESS; +#if (BTM_SCO_HCI_INCLUDED == TRUE) + tBTA_HFP_CODEC_INFO codec_info = {BTA_HFP_SCO_CODEC_PCM}; + UINT32 pcm_sample_rate; +#endif + + if (bta_ag_cb.sco.state == BTA_AG_SCO_LISTEN_ST || + bta_ag_cb.sco.state == BTA_AG_SCO_CLOSE_XFER_ST || + bta_ag_cb.sco.state == BTA_AG_SCO_OPEN_XFER_ST) + { + /* If script overrided sco parameter by BTA_CMD_SET_ESCO_PARAM */ + if (bta_ag_cb.sco.param_updated) + { + resp = bta_ag_cb.sco.params; + } + else + { + resp.rx_bw = BTM_64KBITS_RATE; + resp.tx_bw = BTM_64KBITS_RATE; + resp.max_latency = 10; + resp.voice_contfmt = 0x60; + resp.retrans_effort = BTM_ESCO_RETRANS_POWER; + + if (p_data->link_type == BTM_LINK_TYPE_SCO) + { + resp.retrans_effort = BTM_ESCO_RETRANS_OFF; + resp.packet_types = (BTM_SCO_PKT_TYPES_MASK_HV1 | + BTM_SCO_PKT_TYPES_MASK_HV3 | + BTM_SCO_PKT_TYPES_MASK_NO_2_EV3 | + BTM_SCO_PKT_TYPES_MASK_NO_3_EV3 | + BTM_SCO_PKT_TYPES_MASK_NO_2_EV5 | + BTM_SCO_PKT_TYPES_MASK_NO_3_EV5); + } + else /* Allow controller to use all types available except 5-slot EDR */ + { + if ((p_scb->features & BTA_AG_FEAT_ESCO_S4) && + (p_scb->peer_features & BTA_AG_PEER_FEAT_ESCO_S4)) + { + resp.max_latency = 12; + resp.retrans_effort = BTM_ESCO_RETRANS_QUALITY; + } + + resp.packet_types = (BTM_SCO_PKT_TYPES_MASK_EV3 | + BTM_SCO_PKT_TYPES_MASK_NO_2_EV5 | + BTM_SCO_PKT_TYPES_MASK_NO_3_EV3 | + BTM_SCO_PKT_TYPES_MASK_NO_3_EV5); + } + } + + /* tell sys to stop av if any */ + bta_sys_sco_use(BTA_ID_AG, p_scb->app_id, p_scb->peer_addr); + +#if (BTM_SCO_HCI_INCLUDED == TRUE) +#if (BTM_WBS_INCLUDED == TRUE) + /* When HS initiated SCO, it cannot be WBS. */ + /* Allow any platform specific pre-SCO set up to take place */ + bta_ag_sco_audio_state(bta_ag_scb_to_idx(p_scb), p_scb->app_id, SCO_STATE_SETUP, BTA_AG_CODEC_CVSD); +#else + /* Allow any platform specific pre-SCO set up to take place */ + bta_ag_sco_audio_state(bta_ag_scb_to_idx(p_scb), p_scb->app_id, SCO_STATE_SETUP); +#endif + pcm_sample_rate = BTA_HFP_SCO_SAMP_RATE_8K; + /* initialize SCO setup, no voice setting for AG, data rate <==> sample rate */ + BTM_ConfigScoPath(bta_ag_sco_co_init(pcm_sample_rate, pcm_sample_rate, &codec_info, p_scb->app_id), + bta_ag_sco_read_cback, NULL, TRUE); +#endif + } + else + hci_status = HCI_ERR_HOST_REJECT_DEVICE; + +#if (BTM_WBS_INCLUDED == TRUE) + /* If SCO open was initiated from HS, it must be CVSD */ + p_scb->inuse_codec = BTA_AG_CODEC_NONE; +#endif + BTM_EScoConnRsp(p_data->sco_inx, hci_status, &resp); +} + +/******************************************************************************* +** +** Function bta_ag_ci_sco_data +** +** Description Process the SCO data ready callin event +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_ci_sco_data(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data) +{ + UNUSED(p_scb); + UNUSED(p_data); + +#if (BTM_SCO_HCI_INCLUDED == TRUE ) + bta_ag_sco_event(p_scb, BTA_AG_SCO_CI_DATA_E); +#endif +} + +/******************************************************************************* +** +** Function bta_ag_set_esco_param +** +** Description Update esco parameters from script wrapper. +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_set_esco_param(BOOLEAN set_reset, tBTM_ESCO_PARAMS *param) +{ + if(set_reset == FALSE) /* reset the parameters to default */ + { + bta_ag_cb.sco.param_updated = FALSE; + APPL_TRACE_DEBUG("bta_ag_set_esco_param : Resetting ESCO parameters to default"); + } + else + { + bta_ag_cb.sco.param_updated = TRUE; + bta_ag_cb.sco.params = *param; + APPL_TRACE_DEBUG("bta_ag_set_esco_param : Setting ESCO parameters"); + } +} + +/******************************************************************************* +** Debugging functions +*******************************************************************************/ +static char *bta_ag_sco_evt_str(UINT8 event) +{ + switch (event) + { + case BTA_AG_SCO_LISTEN_E: + return "Listen Request"; + case BTA_AG_SCO_OPEN_E: + return "Open Request"; + case BTA_AG_SCO_XFER_E: + return "Transfer Request"; +#if (BTM_WBS_INCLUDED == TRUE ) + case BTA_AG_SCO_CN_DONE_E: + return "Codec Negotiation Done"; + case BTA_AG_SCO_REOPEN_E: + return "Reopen Request"; +#endif + case BTA_AG_SCO_CLOSE_E: + return "Close Request"; + case BTA_AG_SCO_SHUTDOWN_E: + return "Shutdown Request"; + case BTA_AG_SCO_CONN_OPEN_E: + return "Opened"; + case BTA_AG_SCO_CONN_CLOSE_E: + return "Closed"; + case BTA_AG_SCO_CI_DATA_E : + return "Sco Data"; + default: + return "Unknown SCO Event"; + } +} + +static char *bta_ag_sco_state_str(UINT8 state) +{ + switch (state) + { + case BTA_AG_SCO_SHUTDOWN_ST: + return "Shutdown"; + case BTA_AG_SCO_LISTEN_ST: + return "Listening"; +#if (BTM_WBS_INCLUDED == TRUE ) + case BTA_AG_SCO_CODEC_ST: + return "Codec Negotiation"; +#endif + case BTA_AG_SCO_OPENING_ST: + return "Opening"; + case BTA_AG_SCO_OPEN_CL_ST: + return "Open while closing"; + case BTA_AG_SCO_OPEN_XFER_ST: + return "Opening while Transferring"; + case BTA_AG_SCO_OPEN_ST: + return "Open"; + case BTA_AG_SCO_CLOSING_ST: + return "Closing"; + case BTA_AG_SCO_CLOSE_OP_ST: + return "Close while Opening"; + case BTA_AG_SCO_CLOSE_XFER_ST: + return "Close while Transferring"; + case BTA_AG_SCO_SHUTTING_ST: + return "Shutting Down"; + default: + return "Unknown SCO State"; + } +} + +#endif //#if (BTA_AG_INCLUDED == TRUE) diff --git a/lib/bt/host/bluedroid/bta/hf_ag/bta_ag_sdp.c b/lib/bt/host/bluedroid/bta/hf_ag/bta_ag_sdp.c new file mode 100644 index 00000000..d35ad4f7 --- /dev/null +++ b/lib/bt/host/bluedroid/bta/hf_ag/bta_ag_sdp.c @@ -0,0 +1,449 @@ +/****************************************************************************** + * + * Copyright (C) 2003-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains the audio gateway functions performing SDP + * operations. + * + ******************************************************************************/ + +#include +#include "bta_ag_int.h" +#include "bta/bta_ag_api.h" +#include "bta/bta_sys.h" +#include "bta/bta_ag_api.h" +#include "bta/utl.h" +#include "stack/sdp_api.h" +#include "stack/btm_api.h" +#include "common/bt_trace.h" +#include "osi/allocator.h" + +#if (BTA_AG_INCLUDED == TRUE) + +/* Number of protocol elements in protocol element list. */ +#define BTA_AG_NUM_PROTO_ELEMS 2 + +/* Number of elements in service class id list. */ +#define BTA_AG_NUM_SVC_ELEMS 2 + +/* size of database for service discovery */ +#ifndef BTA_AG_DISC_BUF_SIZE +#define BTA_AG_DISC_BUF_SIZE (4096+16) +#endif + +/* declare sdp callback functions */ +void bta_ag_sdp_cback_1(UINT16 status); +void bta_ag_sdp_cback_2(UINT16 status); +void bta_ag_sdp_cback_3(UINT16 status); + +/* SDP callback function table */ +typedef tSDP_DISC_CMPL_CB *tBTA_AG_SDP_CBACK; +const tBTA_AG_SDP_CBACK bta_ag_sdp_cback_tbl[] = +{ + bta_ag_sdp_cback_1, + bta_ag_sdp_cback_2, + bta_ag_sdp_cback_3 +}; + +/******************************************************************************* +** +** Function bta_ag_sdp_cback +** +** Description SDP callback function. +** +** +** Returns void +** +*******************************************************************************/ +static void bta_ag_sdp_cback(UINT16 status, UINT8 idx) +{ + tBTA_AG_DISC_RESULT *p_buf; + UINT16 event; + tBTA_AG_SCB *p_scb; + + APPL_TRACE_DEBUG("bta_ag_sdp_cback status:0x%x", status); + + if ((p_scb = bta_ag_scb_by_idx(idx)) != NULL) { + /* set event according to int/acp */ + if (p_scb->role == BTA_AG_ACP) { + event = BTA_AG_DISC_ACP_RES_EVT; + } else { + event = BTA_AG_DISC_INT_RES_EVT; + } + + if ((p_buf = (tBTA_AG_DISC_RESULT *) osi_malloc(sizeof(tBTA_AG_DISC_RESULT))) != NULL) { + p_buf->hdr.event = event; + p_buf->hdr.layer_specific = idx; + p_buf->status = status; + bta_sys_sendmsg(p_buf); + } + } +} + +/******************************************************************************* +** +** Function bta_ag_sdp_cback_1 to 3 +** +** Description SDP callback functions. Since there is no way to +** distinguish scb from the callback we need separate +** callbacks for each scb. +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_sdp_cback_1(UINT16 status) {bta_ag_sdp_cback(status, 1);} +void bta_ag_sdp_cback_2(UINT16 status) {bta_ag_sdp_cback(status, 2);} +void bta_ag_sdp_cback_3(UINT16 status) {bta_ag_sdp_cback(status, 3);} + +/****************************************************************************** +** +** Function bta_ag_add_record +** +** Description This function is called by a server application to add +** HSP or HFP information to an SDP record. Prior to +** calling this function the application must call +** SDP_CreateRecord() to create an SDP record. +** +** Returns TRUE if function execution succeeded, +** FALSE if function execution failed. +** +******************************************************************************/ +BOOLEAN bta_ag_add_record(UINT16 service_uuid, char *p_service_name, UINT8 scn, + tBTA_AG_FEAT features, UINT32 sdp_handle) +{ + tSDP_PROTOCOL_ELEM proto_elem_list[BTA_AG_NUM_PROTO_ELEMS]; + UINT16 svc_class_id_list[BTA_AG_NUM_SVC_ELEMS]; + UINT16 browse_list[] = {UUID_SERVCLASS_PUBLIC_BROWSE_GROUP}; + UINT16 version; + UINT16 profile_uuid; + UINT8 network; + BOOLEAN result = TRUE; + BOOLEAN codec_supported = FALSE; + UINT8 buf[2]; + + APPL_TRACE_DEBUG("bta_ag_add_record uuid: %x", service_uuid); + + memset( proto_elem_list, 0 , BTA_AG_NUM_PROTO_ELEMS*sizeof(tSDP_PROTOCOL_ELEM)); + /* add the protocol element sequence */ + proto_elem_list[0].protocol_uuid = UUID_PROTOCOL_L2CAP; + proto_elem_list[0].num_params = 0; + proto_elem_list[1].protocol_uuid = UUID_PROTOCOL_RFCOMM; + proto_elem_list[1].num_params = 1; + proto_elem_list[1].params[0] = scn; + result &= SDP_AddProtocolList(sdp_handle, BTA_AG_NUM_PROTO_ELEMS, proto_elem_list); + + /* add service class id list */ + svc_class_id_list[0] = service_uuid; + svc_class_id_list[1] = UUID_SERVCLASS_GENERIC_AUDIO; + result &= SDP_AddServiceClassIdList(sdp_handle, BTA_AG_NUM_SVC_ELEMS, svc_class_id_list); + + /* add profile descriptor list */ + if (service_uuid == UUID_SERVCLASS_AG_HANDSFREE) { + profile_uuid = UUID_SERVCLASS_HF_HANDSFREE; + version = HFP_VERSION_1_8; + } else { + profile_uuid = UUID_SERVCLASS_HEADSET; + version = HSP_VERSION_1_2; + } + result &= SDP_AddProfileDescriptorList(sdp_handle, profile_uuid, version); + + /* add service name */ + if (p_service_name != NULL && p_service_name[0] != 0) { + result &= SDP_AddAttribute(sdp_handle, ATTR_ID_SERVICE_NAME, TEXT_STR_DESC_TYPE, + (UINT32)(strlen(p_service_name)+1), (UINT8 *) p_service_name); + } + + /* add features and network */ + if (service_uuid == UUID_SERVCLASS_AG_HANDSFREE) { + network = (features & BTA_AG_FEAT_REJECT) ? 1 : 0; + result &= SDP_AddAttribute(sdp_handle, ATTR_ID_DATA_STORES_OR_NETWORK, + UINT_DESC_TYPE, 1, &network); + + if (features & BTA_AG_FEAT_CODEC) { + codec_supported = TRUE; + } + features &= BTA_AG_SDP_FEAT_SPEC; + /* Codec bit position is different in SDP and in BRSF */ + if (codec_supported) { + features |= 0x0020; + } + UINT16_TO_BE_FIELD(buf, features); + result &= SDP_AddAttribute(sdp_handle, ATTR_ID_SUPPORTED_FEATURES, UINT_DESC_TYPE, 2, buf); + } + /* add browse group list */ + result &= SDP_AddUuidSequence(sdp_handle, ATTR_ID_BROWSE_GROUP_LIST, 1, browse_list); + return result; +} + +/******************************************************************************* +** +** Function bta_ag_create_records +** +** Description Create SDP records for registered services. +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_create_records(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data) +{ + tBTA_SERVICE_MASK services; + services = p_scb->reg_services >> BTA_HSP_SERVICE_ID; + + for (int i = 0; i < BTA_AG_NUM_IDX && services != 0; i++, services >>= 1) { + /* if service is set in mask */ + if (services & 1) { + /* add sdp record if not already registered */ + if (bta_ag_cb.profile[i].sdp_handle == 0) { + bta_ag_cb.profile[i].sdp_handle = SDP_CreateRecord(); + bta_ag_cb.profile[i].scn = BTM_AllocateSCN(); + bta_ag_add_record(bta_ag_uuid[i], p_data->api_register.p_name[i], + bta_ag_cb.profile[i].scn, p_data->api_register.features, + bta_ag_cb.profile[i].sdp_handle); + bta_sys_add_uuid(bta_ag_uuid[i]); + } + } + } + p_scb->hsp_version = HSP_VERSION_1_2; +} + +/******************************************************************************* +** +** Function bta_ag_del_records +** +** Description Delete SDP records for any registered services. +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_del_records(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data) +{ + tBTA_AG_SCB *p = &bta_ag_cb.scb[0]; + tBTA_SERVICE_MASK services; + tBTA_SERVICE_MASK others = 0; + int i; + UNUSED(p_data); + + /* get services of all other registered servers */ + for (i = 0; i < BTA_AG_NUM_IDX; i++) { + if (p_scb == p) { + continue; + } + if (p->in_use && p->dealloc == FALSE) { + others |= p->reg_services; + } + if (i < BTA_AG_NUM_SCB) { + p++; + } + } + others >>= BTA_HSP_SERVICE_ID; + services = p_scb->reg_services >> BTA_HSP_SERVICE_ID; + for (i = 0; i < BTA_AG_NUM_IDX && services != 0; i++, services >>= 1, others >>= 1) + { + /* if service registered for this scb and not registered for any other scb */ + if (((services & 1) == 1) && ((others & 1) == 0)) { + APPL_TRACE_DEBUG("bta_ag_del_records %d", i); + if (bta_ag_cb.profile[i].sdp_handle != 0) { + SDP_DeleteRecord(bta_ag_cb.profile[i].sdp_handle); + bta_ag_cb.profile[i].sdp_handle = 0; + } + BTM_FreeSCN(bta_ag_cb.profile[i].scn); + BTM_SecClrService(bta_ag_sec_id[i]); + bta_sys_remove_uuid(bta_ag_uuid[i]); + } + } +} + +/******************************************************************************* +** +** Function bta_ag_sdp_find_attr +** +** Description Process SDP discovery results to find requested attributes +** for requested service. +** +** +** Returns TRUE if results found, FALSE otherwise. +** +*******************************************************************************/ +BOOLEAN bta_ag_sdp_find_attr(tBTA_AG_SCB *p_scb, tBTA_SERVICE_MASK service) +{ + tSDP_DISC_REC *p_rec = NULL; + tSDP_DISC_ATTR *p_attr; + tSDP_PROTOCOL_ELEM pe; + UINT16 uuid; + BOOLEAN result = FALSE; + + if (service & BTA_HFP_SERVICE_MASK) { + uuid = UUID_SERVCLASS_HF_HANDSFREE; + p_scb->peer_version = HFP_VERSION_1_1; /* Default version */ + } else if (service & BTA_HSP_SERVICE_MASK && p_scb->role == BTA_AG_INT) { + uuid = UUID_SERVCLASS_HEADSET_HS; + p_scb->peer_version = 0x0100; /* Default version */ + } else { + return result; + } + + /* loop through all records we found */ + while (TRUE) + { + /* get next record; if none found, we're done */ + if ((p_rec = SDP_FindServiceInDb(p_scb->p_disc_db, uuid, p_rec)) == NULL) { + if (uuid == UUID_SERVCLASS_HEADSET_HS) { + /* Search again in case the peer device is HSP v1.0 */ + uuid = UUID_SERVCLASS_HEADSET; + if ((p_rec = SDP_FindServiceInDb(p_scb->p_disc_db, uuid, p_rec)) == NULL) { + break; + } + } else { + break; + } + } + + /* get scn from proto desc list if initiator */ + if (p_scb->role == BTA_AG_INT) { + if (SDP_FindProtocolListElemInRec(p_rec, UUID_PROTOCOL_RFCOMM, &pe)) { + p_scb->peer_scn = (UINT8) pe.params[0]; + } else { + continue; + } + } + /* get profile version (if failure, version parameter is not updated) */ + SDP_FindProfileVersionInRec(p_rec, uuid, &p_scb->peer_version); + /* get features if HFP */ + if (service & BTA_HFP_SERVICE_MASK) { + if ((p_attr = SDP_FindAttributeInRec(p_rec, ATTR_ID_SUPPORTED_FEATURES)) != NULL) { + /* Found attribute. Get value. */ + /* There might be race condition between SDP and BRSF. */ + /* Do not update if we already received BRSF. */ + if (p_scb->peer_features == 0) + p_scb->peer_features = p_attr->attr_value.v.u16; + } + } else { + /* HSP */ + if ((p_attr = SDP_FindAttributeInRec(p_rec, ATTR_ID_REMOTE_AUDIO_VOLUME_CONTROL)) != NULL) { + /* Remote volume control of HSP */ + if (p_attr->attr_value.v.u8) { + p_scb->peer_features |= BTA_AG_PEER_FEAT_VOL; + } else { + p_scb->peer_features &= ~BTA_AG_PEER_FEAT_VOL; + } + } + } + /* found what we needed */ + result = TRUE; + break; + } + return result; +} + +/******************************************************************************* +** +** Function bta_ag_do_disc +** +** Description Do service discovery. +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_do_disc(tBTA_AG_SCB *p_scb, tBTA_SERVICE_MASK service) +{ + tSDP_UUID uuid_list[2]; + UINT16 num_uuid = 1; + UINT16 attr_list[4]; + UINT8 num_attr; + BOOLEAN db_inited = FALSE; + + /* HFP initiator; get proto list and features */ + if (service & BTA_HFP_SERVICE_MASK && p_scb->role == BTA_AG_INT) { + attr_list[0] = ATTR_ID_SERVICE_CLASS_ID_LIST; + attr_list[1] = ATTR_ID_PROTOCOL_DESC_LIST; + attr_list[2] = ATTR_ID_BT_PROFILE_DESC_LIST; + attr_list[3] = ATTR_ID_SUPPORTED_FEATURES; + num_attr = 4; + uuid_list[0].uu.uuid16 = UUID_SERVCLASS_HF_HANDSFREE; + } else if (service & BTA_HFP_SERVICE_MASK && p_scb->role == BTA_AG_ACP) { + /* HFP acceptor; get features */ + attr_list[0] = ATTR_ID_SERVICE_CLASS_ID_LIST; + attr_list[1] = ATTR_ID_BT_PROFILE_DESC_LIST; + attr_list[2] = ATTR_ID_SUPPORTED_FEATURES; + num_attr = 3; + uuid_list[0].uu.uuid16 = UUID_SERVCLASS_HF_HANDSFREE; + } else if (service & BTA_HSP_SERVICE_MASK && p_scb->role == BTA_AG_INT) { + /* HSP initiator; get proto list */ + attr_list[0] = ATTR_ID_SERVICE_CLASS_ID_LIST; + attr_list[1] = ATTR_ID_PROTOCOL_DESC_LIST; + attr_list[2] = ATTR_ID_BT_PROFILE_DESC_LIST; + attr_list[3] = ATTR_ID_REMOTE_AUDIO_VOLUME_CONTROL; + num_attr = 4; + uuid_list[0].uu.uuid16 = UUID_SERVCLASS_HEADSET; /* Legacy from HSP v1.0 */ + if (p_scb->hsp_version >= HSP_VERSION_1_2) { + uuid_list[1].uu.uuid16 = UUID_SERVCLASS_HEADSET_HS; + num_uuid = 2; + } + } else { + /* HSP acceptor; no discovery */ + return; + } + + /* allocate buffer for sdp database */ + p_scb->p_disc_db = (tSDP_DISCOVERY_DB *) osi_malloc(BTA_AG_DISC_BUF_SIZE); + if(p_scb->p_disc_db) { + /* set up service discovery database; attr happens to be attr_list len */ + uuid_list[0].len = LEN_UUID_16; + uuid_list[1].len = LEN_UUID_16; + db_inited = SDP_InitDiscoveryDb(p_scb->p_disc_db, BTA_AG_DISC_BUF_SIZE, num_uuid, + uuid_list, num_attr, attr_list); + } + + if(db_inited) { + /*Service discovery not initiated */ + db_inited = SDP_ServiceSearchAttributeRequest(p_scb->peer_addr, p_scb->p_disc_db, + bta_ag_sdp_cback_tbl[bta_ag_scb_to_idx(p_scb) - 1]); + } + if(!db_inited) { + /*free discover db */ + bta_ag_free_db(p_scb, NULL); + /* sent failed event */ + bta_ag_sm_execute(p_scb, BTA_AG_DISC_FAIL_EVT, NULL); + } +} + +/******************************************************************************* +** +** Function bta_ag_free_db +** +** Description Free discovery database. +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_free_db(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data) +{ + UNUSED(p_data); + if (p_scb->p_disc_db != NULL) { + osi_free(p_scb->p_disc_db); + p_scb->p_disc_db = NULL; + } +} + +#endif /* #if (BTA_AG_INCLUDED == TRUE) */ diff --git a/lib/bt/host/bluedroid/bta/hf_ag/include/bta_ag_at.h b/lib/bt/host/bluedroid/bta/hf_ag/include/bta_ag_at.h new file mode 100644 index 00000000..867df146 --- /dev/null +++ b/lib/bt/host/bluedroid/bta/hf_ag/include/bta_ag_at.h @@ -0,0 +1,126 @@ +/****************************************************************************** + * + * Copyright (C) 2004-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * Interface file for BTA AG AT command interpreter. + * + ******************************************************************************/ +#ifndef BTA_AG_AT_H +#define BTA_AG_AT_H + +#include "stack/bt_types.h" +#include "common/bt_target.h" + +/***************************************************************************** +** Constants +*****************************************************************************/ +#if (BTA_AG_INCLUDED == TRUE) + +/* AT command argument capabilities */ +#define BTA_AG_AT_NONE 0x01 /* no argument */ +#define BTA_AG_AT_SET 0x02 /* set value */ +#define BTA_AG_AT_READ 0x04 /* read value */ +#define BTA_AG_AT_TEST 0x08 /* test value range */ +#define BTA_AG_AT_FREE 0x10 /* freeform argument */ + +/* AT command argument format */ +#define BTA_AG_AT_STR 0 /* string */ +#define BTA_AG_AT_INT 1 /* integer */ + +/***************************************************************************** +** Data types +*****************************************************************************/ + +/* AT command table element */ +typedef struct +{ + const char *p_cmd; /* AT command string */ + UINT8 arg_type; /* allowable argument type syntax */ + UINT8 fmt; /* whether arg is int or string */ + UINT8 min; /* minimum value for int arg */ + INT16 max; /* maximum value for int arg */ +} tBTA_AG_AT_CMD; + +/* callback function executed when command is parsed */ +typedef void (tBTA_AG_AT_CMD_CBACK)(void *p_user, UINT16 cmd, UINT8 arg_type, + char *p_arg, INT16 int_arg); + +/* callback function executed to send "ERROR" result code */ +typedef void (tBTA_AG_AT_ERR_CBACK)(void *p_user, BOOLEAN unknown, char *p_arg); + +/* AT command parsing control block */ +typedef struct +{ + tBTA_AG_AT_CMD *p_at_tbl; /* AT command table */ + tBTA_AG_AT_CMD_CBACK *p_cmd_cback; /* command callback */ + tBTA_AG_AT_ERR_CBACK *p_err_cback; /* error callback */ + void *p_user; /* user-defined data */ + char *p_cmd_buf; /* temp parsing buffer */ + UINT16 cmd_pos; /* position in temp buffer */ + UINT16 cmd_max_len; /* length of temp buffer to allocate */ + UINT8 state; /* parsing state */ +} tBTA_AG_AT_CB; + +/***************************************************************************** +** Function prototypes +*****************************************************************************/ + +/***************************************************************************** +** +** Function bta_ag_at_init +** +** Description Initialize the AT command parser control block. +** +** +** Returns void +** +*****************************************************************************/ +extern void bta_ag_at_init(tBTA_AG_AT_CB *p_cb); + +/***************************************************************************** +** +** Function bta_ag_at_reinit +** +** Description Re-initialize the AT command parser control block. This +** function resets the AT command parser state and frees +** any GKI buffer. +** +** +** Returns void +** +*****************************************************************************/ +extern void bta_ag_at_reinit(tBTA_AG_AT_CB *p_cb); + +/***************************************************************************** +** +** Function bta_ag_at_parse +** +** Description Parse AT commands. This function will take the input +** character string and parse it for AT commands according to +** the AT command table passed in the control block. +** +** +** Returns void +** +*****************************************************************************/ +extern void bta_ag_at_parse(tBTA_AG_AT_CB *p_cb, char *p_buf, UINT16 len); + +#endif /* #if (BTA_AG_INCLUDED == TRUE) */ + +#endif /* BTA_AG_AT_H */ diff --git a/lib/bt/host/bluedroid/bta/hf_ag/include/bta_ag_int.h b/lib/bt/host/bluedroid/bta/hf_ag/include/bta_ag_int.h new file mode 100644 index 00000000..9ced8b36 --- /dev/null +++ b/lib/bt/host/bluedroid/bta/hf_ag/include/bta_ag_int.h @@ -0,0 +1,462 @@ +/****************************************************************************** + * + * Copyright (C) 2003-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This is the private interface file for the BTA audio gateway. + * + ******************************************************************************/ +#ifndef BTA_AG_INT_H +#define BTA_AG_INT_H + +#include "bta_ag_at.h" +#include "bta/bta_sys.h" +#include "bta/bta_api.h" +#include "bta/bta_ag_api.h" +#include "stack/sdp_api.h" + +#if (BTA_AG_INCLUDED == TRUE) + +/* Send RING & CLIP in one AT cmd */ +#ifndef BTA_AG_MULTI_RESULT_INCLUDED +#define BTA_AG_MULTI_RESULT_INCLUDED FALSE +#endif + +/* Replace : in VGS and VGM for HSP */ +#ifndef BTA_HSP_RESULT_REPLACE_COLON +#define BTA_HSP_RESULT_REPLACE_COLON TRUE +#endif + +/***************************************************************************** +** Constants +*****************************************************************************/ +#define HFP_VERSION_1_1 0x0101 +#define HFP_VERSION_1_5 0x0105 +#define HFP_VERSION_1_6 0x0106 +#define HFP_VERSION_1_7 0x0107 +#define HFP_VERSION_1_8 0x0108 + +#define HSP_VERSION_1_0 0x0100 +#define HSP_VERSION_1_2 0x0102 + +/* Number of SCBs (AG service instances that can be registered) */ +#ifndef BTA_AG_NUM_SCB +#define BTA_AG_NUM_SCB 1 +#endif + +/* Timer to wait for retry in case of collision */ +#ifndef BTA_AG_COLLISION_TIMER +#define BTA_AG_COLLISION_TIMER 2000 +#endif + +/* RFCOMM MTU SIZE */ +#define BTA_AG_MTU 256 + +/* Internal profile indexes */ +#define BTA_AG_HSP 0 /* index for HSP */ +#define BTA_AG_HFP 1 /* index for HFP */ +#define BTA_AG_NUM_IDX 2 /* number of profile indexes */ + +/* profile role for connection */ +#define BTA_AG_ACP 0 /* accepted connection */ +#define BTA_AG_INT 1 /* initiating connection */ + +#if BT_HF_AG_BQB_INCLUDED +/* feature mask that matches spec for BQB test */ +#define BTA_AG_BQB_BRSF_FEAT_SPEC (BTA_AG_FEAT_VOIP | \ + BTA_AG_FEAT_VTAG | BTA_AG_FEAT_CODEC | \ + BTA_AG_FEAT_ECS | BTA_AG_FEAT_ECC | \ + BTA_AG_FEAT_ESCO_S4 | BTA_AG_FEAT_EXTERR) +#endif /* BT_HF_AG_BQB_INCLUDED */ + +/* feature mask that matches spec */ +#define BTA_AG_BRSF_FEAT_SPEC (BTA_AG_FEAT_3WAY | BTA_AG_FEAT_ECNR | \ + BTA_AG_FEAT_VREC | BTA_AG_FEAT_INBAND | \ + BTA_AG_FEAT_VTAG | BTA_AG_FEAT_REJECT | \ + BTA_AG_FEAT_ECS | BTA_AG_FEAT_ECC | \ + BTA_AG_FEAT_EXTERR | BTA_AG_FEAT_CODEC | \ + BTA_AG_FEAT_ESCO_S4 | BTA_AG_FEAT_VOIP) + +#define BTA_AG_SDP_FEAT_SPEC (BTA_AG_FEAT_3WAY | BTA_AG_FEAT_ECNR | \ + BTA_AG_FEAT_VREC | BTA_AG_FEAT_INBAND | \ + BTA_AG_FEAT_VTAG) + +enum +{ + /* these events are handled by the state machine */ + BTA_AG_API_REGISTER_EVT = BTA_SYS_EVT_START(BTA_ID_AG), + BTA_AG_API_DEREGISTER_EVT, + BTA_AG_API_OPEN_EVT, + BTA_AG_API_CLOSE_EVT, + BTA_AG_API_AUDIO_OPEN_EVT, + BTA_AG_API_AUDIO_CLOSE_EVT, + BTA_AG_API_RESULT_EVT, + BTA_AG_API_SETCODEC_EVT, + BTA_AG_RFC_OPEN_EVT, + BTA_AG_RFC_CLOSE_EVT, + BTA_AG_RFC_SRV_CLOSE_EVT, + BTA_AG_RFC_DATA_EVT, + BTA_AG_SCO_OPEN_EVT, + BTA_AG_SCO_CLOSE_EVT, + BTA_AG_DISC_ACP_RES_EVT, + BTA_AG_DISC_INT_RES_EVT, + BTA_AG_DISC_OK_EVT, + BTA_AG_DISC_FAIL_EVT, + BTA_AG_CI_RX_WRITE_EVT, + BTA_AG_RING_TOUT_EVT, + BTA_AG_SVC_TOUT_EVT, + BTA_AG_CI_SCO_DATA_EVT, + BTA_AG_CI_SLC_READY_EVT, + BTA_AG_PKT_STAT_NUMS_GET_EVT, + BTA_AG_MAX_EVT, + + /* these events are handled outside of the state machine */ + BTA_AG_API_ENABLE_EVT, + BTA_AG_API_DISABLE_EVT +}; + +/* Actions to perform after a SCO event */ +enum +{ + BTA_AG_POST_SCO_NONE, /* no action */ + BTA_AG_POST_SCO_CLOSE_RFC, /* close RFCOMM channel after SCO closes */ + BTA_AG_POST_SCO_RING, /* send RING result code after SCO opens */ + BTA_AG_POST_SCO_CALL_CONN, /* send call indicators after SCO opens/closes */ + BTA_AG_POST_SCO_CALL_ORIG, /* send call indicators after SCO closes */ + BTA_AG_POST_SCO_CALL_END, /* send call indicators after SCO closes */ + BTA_AG_POST_SCO_CALL_END_INCALL /* send call indicators for end call & incoming call after SCO closes */ +}; + +/* sco states */ +enum +{ + BTA_AG_SCO_SHUTDOWN_ST, /* no sco listening, all sco connections closed */ + BTA_AG_SCO_LISTEN_ST, /* sco listening */ +#if (BTM_WBS_INCLUDED == TRUE) + BTA_AG_SCO_CODEC_ST, /* sco codec negotiation */ +#endif + BTA_AG_SCO_OPENING_ST, /* sco connection opening */ + BTA_AG_SCO_OPEN_CL_ST, /* opening sco connection being closed */ + BTA_AG_SCO_OPEN_XFER_ST, /* opening sco connection being transferred */ + BTA_AG_SCO_OPEN_ST, /* sco open */ + BTA_AG_SCO_CLOSING_ST, /* sco closing */ + BTA_AG_SCO_CLOSE_OP_ST, /* closing sco being opened */ + BTA_AG_SCO_CLOSE_XFER_ST, /* closing sco being transferred */ + BTA_AG_SCO_SHUTTING_ST /* sco shutting down */ +}; + +/***************************************************************************** +** Data types +*****************************************************************************/ + +/* data type for BTA_AG_API_ENABLE_EVT */ +typedef struct +{ + BT_HDR hdr; + tBTA_AG_PARSE_MODE parse_mode; + tBTA_AG_CBACK *p_cback; +} tBTA_AG_API_ENABLE; + +/* data type for BTA_AG_API_REGISTER_EVT */ +typedef struct +{ + BT_HDR hdr; + char p_name[2][BTA_SERVICE_NAME_LEN+1]; + tBTA_SERVICE_MASK services; + tBTA_SEC sec_mask; + tBTA_AG_FEAT features; + UINT8 app_id; +} tBTA_AG_API_REGISTER; + +/* data type for BTA_AG_API_OPEN_EVT */ +typedef struct +{ + BT_HDR hdr; + BD_ADDR bd_addr; + tBTA_SERVICE_MASK services; + tBTA_SEC sec_mask; +} tBTA_AG_API_OPEN; + +/* data type for BTA_AG_API_RESULT_EVT */ +typedef struct +{ + BT_HDR hdr; + tBTA_AG_RES result; + tBTA_AG_RES_DATA data; +} tBTA_AG_API_RESULT; + +/* data type for BTA_AG_API_SETCODEC_EVT */ +typedef struct +{ + BT_HDR hdr; + tBTA_AG_PEER_CODEC codec; +} tBTA_AG_API_SETCODEC; + +/* data type for BTA_AG_DISC_RESULT_EVT */ +typedef struct +{ + BT_HDR hdr; + UINT16 status; +} tBTA_AG_DISC_RESULT; + +/* data type for RFCOMM events */ +typedef struct +{ + BT_HDR hdr; + UINT16 port_handle; +} tBTA_AG_RFC; + +/* data type for BTA_AG_CI_RX_WRITE_EVT */ +typedef struct +{ + BT_HDR hdr; + char p_data[BTA_AG_MTU+1]; +} tBTA_AG_CI_RX_WRITE; + +/* data type for BTA_AG_PKT_STAT_NUMS_GET_EVT */ +typedef struct +{ + BT_HDR hdr; + UINT16 sync_conn_handle; +} tBTA_AG_PKT_STAT_GET; + +/* union of all event datatypes */ +typedef union +{ + BT_HDR hdr; + tBTA_AG_API_ENABLE api_enable; + tBTA_AG_API_REGISTER api_register; + tBTA_AG_API_OPEN api_open; + tBTA_AG_API_RESULT api_result; +#if (BTM_WBS_INCLUDED == TRUE) + tBTA_AG_API_SETCODEC api_setcodec; +#endif + tBTA_AG_DISC_RESULT disc_result; + tBTA_AG_RFC rfc; + tBTA_AG_CI_RX_WRITE ci_rx_write; + tBTA_AG_PKT_STAT_GET pkt_stat; +} tBTA_AG_DATA; + +/* type for each profile */ +typedef struct +{ + UINT32 sdp_handle; + UINT8 scn; +} tBTA_AG_PROFILE; + +#if (BTM_WBS_INCLUDED == TRUE) +typedef enum +{ + BTA_AG_SCO_MSBC_SETTINGS_T2 = 0, /* preferred/default when codec is mSBC */ + BTA_AG_SCO_MSBC_SETTINGS_T1, +} tBTA_AG_SCO_MSBC_SETTINGS; +#endif + +/* type for each service control block */ +typedef struct +{ + char clip[BTA_AG_AT_MAX_LEN+10]; /* number string used for CLIP */ + UINT16 serv_handle[BTA_AG_NUM_IDX]; /* RFCOMM server handles */ + tBTA_AG_AT_CB at_cb; /* AT command interpreter */ + TIMER_LIST_ENT act_timer; /* ring timer */ + BD_ADDR peer_addr; /* peer bd address */ + tSDP_DISCOVERY_DB *p_disc_db; /* pointer to discovery database */ + tBTA_SERVICE_MASK reg_services; /* services specified in register API */ + tBTA_SERVICE_MASK open_services; /* services specified in open API */ + UINT16 conn_handle; /* RFCOMM handle of connected service */ + tBTA_SEC serv_sec_mask; /* server security mask */ + tBTA_SEC cli_sec_mask; /* client security mask */ + tBTA_AG_FEAT features; /* features registered by application */ + tBTA_AG_PEER_FEAT peer_features; /* peer device features */ + UINT16 peer_version; /* profile version of peer device */ + UINT16 hsp_version; /* HSP profile version */ +#if (BTM_WBS_INCLUDED == TRUE) + tBTA_AG_PEER_CODEC peer_codecs; /* codecs for eSCO supported by the peer */ + tBTA_AG_PEER_CODEC sco_codec; /* codec to be used for eSCO connection */ + tBTA_AG_PEER_CODEC inuse_codec; /* codec being used for the current SCO connection */ + BOOLEAN codec_updated; /* set to TRUE whenever the app updates codec type */ + BOOLEAN codec_fallback; /* If sco nego fails for mSBC, fallback to CVSD */ + tBTA_AG_SCO_MSBC_SETTINGS codec_msbc_settings; /* settings to be used for the impending eSCO */ + TIMER_LIST_ENT cn_timer; /* codec negotiation timer */ +#endif + UINT16 sco_idx; /* SCO connection index */ + BOOLEAN in_use; /* scb in use */ + BOOLEAN dealloc; /* TRUE if service shutting down */ + BOOLEAN clip_enabled; /* set to TRUE if HF enables CLIP reporting */ + BOOLEAN ccwa_enabled; /* set to TRUE if HF enables CCWA reporting */ + BOOLEAN cmer_enabled; /* set to TRUE if HF enables CMER reporting */ + BOOLEAN cmee_enabled; /* set to TRUE if HF enables CME ERROR reporting */ + BOOLEAN inband_enabled; /* set to TRUE if inband ring enabled */ + BOOLEAN svc_conn; /* set to TRUE when service level connection up */ + TIMER_LIST_ENT colli_timer; /* Collision timer */ + BOOLEAN colli_tmr_on; /* TRUE if collision timer is active */ + UINT8 state; /* state machine state */ + UINT8 conn_service; /* connected service */ + UINT8 peer_scn; /* peer scn */ + UINT8 app_id; /* application id */ + UINT8 role; /* initiator/acceptor role */ + tBTM_SCO_CODEC_TYPE negotiated_codec; /* negotiated codec */ + UINT8 post_sco; /* action to perform after sco event */ + UINT8 call_ind; /* CIEV call indicator value */ + UINT8 callsetup_ind; /* CIEV callsetup indicator value */ + UINT8 service_ind; /* CIEV service indicator value */ + UINT8 signal_ind; /* CIEV signal indicator value */ + UINT8 roam_ind; /* CIEV roam indicator value */ + UINT8 battchg_ind; /* CIEV battery charge indicator value */ + UINT8 callheld_ind; /* CIEV call held indicator value */ + BOOLEAN retry_with_sco_only; /* indicator to try with SCO only when eSCO fails */ + UINT32 bia_masked_out; /* indicators HF does not want us to send */ + /* add */ + UINT16 in_pkt_len; + UINT16 out_pkt_len; + UINT8 link_type; /* BTM_LINK_TYPE_SCO or BTM_LINK_TYPE_ESCO */ + UINT8 tx_interval; + UINT8 retrans_window; + UINT8 air_mode; +} tBTA_AG_SCB; + +/* type for sco data */ +typedef struct +{ + tBTM_ESCO_CONN_REQ_EVT_DATA conn_data; /* CO data for pending conn requestS */ + tBTA_AG_SCB *p_curr_scb; /* SCB associated with SCO connection */ + tBTA_AG_SCB *p_xfer_scb; /* SCB associated with SCO transfer */ + UINT16 cur_idx; /* SCO handle */ + UINT8 state; /* SCO state variable */ + BOOLEAN param_updated; /* if params were updated to non-default */ + tBTM_ESCO_PARAMS params; /* ESCO parameters */ + tBTA_AG_DATA *p_data; +} tBTA_AG_SCO_CB; + +/* type for AG control block */ +typedef struct +{ + tBTA_AG_SCB scb[BTA_AG_NUM_SCB]; /* service control blocks */ + tBTA_AG_PROFILE profile[BTA_AG_NUM_IDX]; /* profile-specific data */ + tBTA_AG_SCO_CB sco; /* SCO data */ + tBTA_AG_CBACK *p_cback; /* application callback */ + tBTA_AG_PARSE_MODE parse_mode; /* parse/pass-through mode */ + BOOLEAN msbc_enabled; +} tBTA_AG_CB; + +/***************************************************************************** +** Global data +*****************************************************************************/ + +/* constant lookup tables */ +extern const UINT16 bta_ag_uuid[BTA_AG_NUM_IDX]; +extern const UINT8 bta_ag_sec_id[BTA_AG_NUM_IDX]; +extern const tBTA_AG_AT_CMD *bta_ag_at_tbl[BTA_AG_NUM_IDX]; + +/* control block declaration */ +#if BTA_DYNAMIC_MEMORY == FALSE +extern tBTA_AG_CB bta_ag_cb; +#else +extern tBTA_AG_CB *bta_ag_cb_ptr; +#define bta_ag_cb (*bta_ag_cb_ptr) +#endif + +/* config struct */ +extern tBTA_AG_CFG *p_bta_ag_cfg; + +/***************************************************************************** +** Function prototypes +*****************************************************************************/ + +/* main functions */ +extern void bta_ag_scb_dealloc(tBTA_AG_SCB *p_scb); +extern UINT16 bta_ag_scb_to_idx(tBTA_AG_SCB *p_scb); +extern tBTA_AG_SCB *bta_ag_scb_by_idx(UINT16 idx); +extern UINT8 bta_ag_service_to_idx(tBTA_SERVICE_MASK services); +extern UINT16 bta_ag_idx_by_bdaddr(BD_ADDR peer_addr); +extern BOOLEAN bta_ag_other_scb_open(tBTA_AG_SCB *p_curr_scb); +extern BOOLEAN bta_ag_scb_open(tBTA_AG_SCB *p_curr_scb); +extern tBTA_AG_SCB *bta_ag_get_other_idle_scb (tBTA_AG_SCB *p_curr_scb); +extern void bta_ag_sm_execute(tBTA_AG_SCB *p_scb, UINT16 event, tBTA_AG_DATA *p_data); +extern BOOLEAN bta_ag_hdl_event(BT_HDR *p_msg); +extern void bta_ag_collision_cback(tBTA_SYS_CONN_STATUS status, UINT8 id, UINT8 app_id, BD_ADDR peer_addr); +extern void bta_ag_resume_open(tBTA_AG_SCB *p_scb); + +/* SDP functions */ +extern BOOLEAN bta_ag_add_record(UINT16 service_uuid, char *p_service_name, UINT8 scn, tBTA_AG_FEAT features, UINT32 sdp_handle); +extern void bta_ag_create_records(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data); +extern void bta_ag_del_records(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data); +extern BOOLEAN bta_ag_sdp_find_attr(tBTA_AG_SCB *p_scb, tBTA_SERVICE_MASK service); +extern void bta_ag_do_disc(tBTA_AG_SCB *p_scb, tBTA_SERVICE_MASK service); +extern void bta_ag_free_db(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data); + +/* RFCOMM functions */ +extern void bta_ag_start_servers(tBTA_AG_SCB *p_scb, tBTA_SERVICE_MASK services); +extern void bta_ag_close_servers(tBTA_AG_SCB *p_scb, tBTA_SERVICE_MASK services); +extern BOOLEAN bta_ag_is_server_closed (tBTA_AG_SCB *p_scb); +extern void bta_ag_rfc_do_close(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data); +extern void bta_ag_rfc_do_open(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data); + +/* SCO functions */ +extern BOOLEAN bta_ag_sco_is_open(tBTA_AG_SCB *p_scb); +extern BOOLEAN bta_ag_sco_is_opening(tBTA_AG_SCB *p_scb); +extern void bta_ag_sco_conn_rsp(tBTA_AG_SCB *p_scb, tBTM_ESCO_CONN_REQ_EVT_DATA *p_data); + +/* AT command functions */ +extern void bta_ag_at_hsp_cback(tBTA_AG_SCB *p_scb, UINT16 cmd, UINT8 arg_type, char *p_arg, INT16 int_arg); +extern void bta_ag_at_hfp_cback(tBTA_AG_SCB *p_scb, UINT16 cmd, UINT8 arg_type, char *p_arg, INT16 int_arg); +extern void bta_ag_at_err_cback(tBTA_AG_SCB *p_scb, BOOLEAN unknown, char *p_arg); +extern BOOLEAN bta_ag_inband_enabled(tBTA_AG_SCB *p_scb); +extern void bta_ag_send_call_inds(tBTA_AG_SCB *p_scb, tBTA_AG_RES result); + +/* Action functions */ +extern void bta_ag_register(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data); +extern void bta_ag_deregister(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data); +extern void bta_ag_start_dereg(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data); +extern void bta_ag_start_close(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data); +extern void bta_ag_start_open(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data); +extern void bta_ag_disc_int_res(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data); +extern void bta_ag_disc_acp_res(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data); +extern void bta_ag_disc_fail(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data); +extern void bta_ag_open_fail(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data); +extern void bta_ag_rfc_fail(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data); +extern void bta_ag_rfc_close(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data); +extern void bta_ag_rfc_open(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data); +extern void bta_ag_rfc_acp_open(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data); +extern void bta_ag_rfc_data(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data); +extern void bta_ag_sco_listen(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data); +extern void bta_ag_sco_open(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data); +extern void bta_ag_sco_close(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data); +#if (BTM_WBS_INCLUDED == TRUE) +extern void bta_ag_sco_codec_nego(tBTA_AG_SCB *p_scb, BOOLEAN result); +extern void bta_ag_codec_negotiate (tBTA_AG_SCB *p_scb); +extern void bta_ag_send_bcs(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data); +#endif +extern void bta_ag_sco_shutdown(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data); +extern void bta_ag_sco_conn_open(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data); +extern void bta_ag_sco_conn_close(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data); +extern void bta_ag_post_sco_open(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data); +extern void bta_ag_post_sco_close(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data); +extern void bta_ag_svc_conn_open(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data); +extern void bta_ag_result(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data); +extern void bta_ag_setcodec(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data); +extern void bta_ag_send_ring(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data); +extern void bta_ag_ci_sco_data(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data); +extern void bta_ag_set_esco_param(BOOLEAN set_reset, tBTM_ESCO_PARAMS *param); +extern void bta_ag_ci_rx_data(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data); +extern void bta_ag_rcvd_slc_ready(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data); +extern void bta_ag_pkt_stat_nums(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data); + +#endif /* #if (BTA_AG_INCLUDED == TRUE) */ + +#endif /* BTA_AG_INT_H */ diff --git a/lib/bt/host/bluedroid/bta/hf_client/bta_hf_client_act.c b/lib/bt/host/bluedroid/bta/hf_client/bta_hf_client_act.c new file mode 100644 index 00000000..0e0c43ad --- /dev/null +++ b/lib/bt/host/bluedroid/bta/hf_client/bta_hf_client_act.c @@ -0,0 +1,780 @@ +/****************************************************************************** + * + * Copyright (c) 2014 The Android Open Source Project + * Copyright (C) 2003-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains action functions for the handsfree client. + * + ******************************************************************************/ + +#include "bta/bta_api.h" +#include "bta/bta_hf_client_api.h" +#include "bta_hf_client_int.h" +#include "bta_dm_int.h" +#include "stack/l2c_api.h" +#include "stack/port_api.h" +#include "bta/bta_sys.h" +#include "bta/utl.h" +#include "common/bt_defs.h" +#include +#include "osi/allocator.h" + +#if (BTA_HF_INCLUDED == TRUE) +/***************************************************************************** +** Constants +*****************************************************************************/ + +/* maximum length of data to read from RFCOMM */ +#define BTA_HF_CLIENT_RFC_READ_MAX 512 + +/******************************************************************************* +** +** Function bta_hf_client_register +** +** Description This function initializes values of the scb and sets up +** the SDP record for the services. +** +** +** Returns void +** +*******************************************************************************/ +void bta_hf_client_register(tBTA_HF_CLIENT_DATA *p_data) +{ + tBTA_HF_CLIENT_REGISTER evt; + tBTA_UTL_COD cod; + + memset(&evt, 0, sizeof(evt)); + + /* initialize control block */ + bta_hf_client_scb_init(); + + bta_hf_client_cb.scb.serv_sec_mask = p_data->api_register.sec_mask; + bta_hf_client_cb.scb.features = p_data->api_register.features; + + /* initialize AT control block */ + bta_hf_client_at_init(); + + /* create SDP records */ + bta_hf_client_create_record(p_data); + + /* Set the Class of Device (Audio service class bit) */ + cod.service = BTM_COD_SERVICE_AUDIO; + cod.major = BTM_COD_MAJOR_AUDIO; + cod.minor = BTM_COD_MINOR_CONFM_HANDSFREE; + utl_set_device_class(&cod, BTA_UTL_SET_COD_ALL); + + /* start RFCOMM server */ + bta_hf_client_start_server(); + + /* call app callback with register event */ + evt.status = BTA_HF_CLIENT_SUCCESS; + (*bta_hf_client_cb.p_cback)(BTA_HF_CLIENT_REGISTER_EVT, &evt); +} + +/******************************************************************************* +** +** Function bta_hf_client_deregister +** +** Description This function removes the sdp records, closes the RFCOMM +** servers, and deallocates the service control block. +** +** +** Returns void +** +*******************************************************************************/ +void bta_hf_client_deregister(tBTA_HF_CLIENT_DATA *p_data) +{ + tBTA_UTL_COD cod; + + bta_hf_client_cb.scb.deregister = TRUE; + + /* Clear the Audio service class bit */ + cod.service = BTM_COD_SERVICE_AUDIO; + utl_set_device_class(&cod, BTA_UTL_CLR_COD_SERVICE_CLASS); + + /* remove sdp record */ + bta_hf_client_del_record(p_data); + + /* remove rfcomm server */ + bta_hf_client_close_server(); + + /* disable */ + bta_hf_client_scb_disable(); +} + +/******************************************************************************* +** +** Function bta_hf_client_start_dereg +** +** Description Start a deregister event. +** +** +** Returns void +** +*******************************************************************************/ +void bta_hf_client_start_dereg(tBTA_HF_CLIENT_DATA *p_data) +{ + bta_hf_client_cb.scb.deregister = TRUE; + + /* remove sdp record */ + bta_hf_client_del_record(p_data); +} + +/******************************************************************************* +** +** Function bta_hf_client_start_close +** +** Description Start the process of closing SCO and RFCOMM connection. +** +** +** Returns void +** +*******************************************************************************/ +void bta_hf_client_start_close(tBTA_HF_CLIENT_DATA *p_data) +{ + /* Take the link out of sniff and set L2C idle time to 0 */ +#if (BTA_DM_PM_INCLUDED == TRUE) + bta_dm_pm_active(bta_hf_client_cb.scb.peer_addr); +#endif /* (BTA_DM_PM_INCLUDED == TRUE) */ + L2CA_SetIdleTimeoutByBdAddr(bta_hf_client_cb.scb.peer_addr, 0, BT_TRANSPORT_BR_EDR); + + /* if SCO is open close SCO and wait on RFCOMM close */ + if (bta_hf_client_cb.scb.sco_state == BTA_HF_CLIENT_SCO_OPEN_ST) { + bta_hf_client_cb.scb.sco_close_rfc = TRUE; + } else { + bta_hf_client_rfc_do_close(p_data); + } + + /* always do SCO shutdown to handle all SCO corner cases */ + bta_hf_client_sco_shutdown(NULL); +} + +/******************************************************************************* +** +** Function bta_hf_client_start_open +** +** Description This starts an HF Client open. +** +** +** Returns void +** +*******************************************************************************/ +void bta_hf_client_start_open(tBTA_HF_CLIENT_DATA *p_data) +{ + BD_ADDR pending_bd_addr; + + /* store parameters */ + if (p_data) { + bdcpy(bta_hf_client_cb.scb.peer_addr, p_data->api_open.bd_addr); + bta_hf_client_cb.scb.cli_sec_mask = p_data->api_open.sec_mask; + } + + /* Check if RFCOMM has any incoming connection to avoid collision. */ + if (PORT_IsOpening (pending_bd_addr)) { + /* Let the incoming connection goes through. */ + /* Issue collision for now. */ + /* We will decide what to do when we find incoming connection later.*/ + bta_hf_client_collision_cback (0, BTA_ID_HS, 0, bta_hf_client_cb.scb.peer_addr); + return; + } + + /* close server */ + bta_hf_client_close_server(); + + /* set role */ + bta_hf_client_cb.scb.role = BTA_HF_CLIENT_INT; + + /* do service search */ + bta_hf_client_do_disc(); +} + +/******************************************************************************* +** +** Function bta_hf_client_cback_open +** +** Description Send open callback event to application. +** +** +** Returns void +** +*******************************************************************************/ +static void bta_hf_client_cback_open(tBTA_HF_CLIENT_DATA *p_data, tBTA_HF_CLIENT_STATUS status) +{ + tBTA_HF_CLIENT_OPEN evt; + + memset(&evt, 0, sizeof(evt)); + + /* call app callback with open event */ + evt.status = status; + if (p_data) { + /* if p_data is provided then we need to pick the bd address from the open api structure */ + bdcpy(evt.bd_addr, p_data->api_open.bd_addr); + } else { + bdcpy(evt.bd_addr, bta_hf_client_cb.scb.peer_addr); + } + + (*bta_hf_client_cb.p_cback)(BTA_HF_CLIENT_OPEN_EVT, &evt); +} + +/******************************************************************************* +** +** Function bta_hf_client_rfc_open +** +** Description Handle RFCOMM channel open. +** +** +** Returns void +** +*******************************************************************************/ +void bta_hf_client_rfc_open(tBTA_HF_CLIENT_DATA *p_data) +{ + UNUSED(p_data); + + bta_sys_conn_open(BTA_ID_HS, 1, bta_hf_client_cb.scb.peer_addr); + + bta_hf_client_cback_open(NULL, BTA_HF_CLIENT_SUCCESS); + + /* start SLC procedure */ + bta_hf_client_slc_seq(FALSE); +} + +/******************************************************************************* +** +** Function bta_hf_client_rfc_acp_open +** +** Description Handle RFCOMM channel open when accepting connection. +** +** +** Returns void +** +*******************************************************************************/ +void bta_hf_client_rfc_acp_open(tBTA_HF_CLIENT_DATA *p_data) +{ + UINT16 lcid; + BD_ADDR dev_addr = {0}; + int status; + + /* set role */ + bta_hf_client_cb.scb.role = BTA_HF_CLIENT_ACP; + + APPL_TRACE_DEBUG ("bta_hf_client_rfc_acp_open: serv_handle = %d rfc.port_handle = %d", + bta_hf_client_cb.scb.serv_handle, p_data->rfc.port_handle); + + /* get bd addr of peer */ + if (PORT_SUCCESS != (status = PORT_CheckConnection(p_data->rfc.port_handle, FALSE, dev_addr, &lcid))) { + APPL_TRACE_DEBUG ("bta_hf_client_rfc_acp_open error PORT_CheckConnection returned status %d", status); + } + + /* Collision Handling */ + if (bta_hf_client_cb.scb.colli_tmr_on) { + /* stop collision timer */ + bta_hf_client_cb.scb.colli_tmr_on = FALSE; + bta_sys_free_timer (&bta_hf_client_cb.scb.colli_timer); + + if (bdcmp (dev_addr, bta_hf_client_cb.scb.peer_addr) == 0) { + /* If incoming and outgoing device are same, nothing more to do. */ + /* Outgoing conn will be aborted because we have successful incoming conn. */ + } else { + /* Resume outgoing connection. */ + bta_hf_client_resume_open (); + } + } + + bdcpy (bta_hf_client_cb.scb.peer_addr, dev_addr); + bta_hf_client_cb.scb.conn_handle = p_data->rfc.port_handle; + + /* do service discovery to get features */ + bta_hf_client_do_disc(); + + /* continue with open processing */ + bta_hf_client_rfc_open(p_data); +} + +/******************************************************************************* +** +** Function bta_hf_client_rfc_fail +** +** Description RFCOMM connection failed. +** +** +** Returns void +** +*******************************************************************************/ +void bta_hf_client_rfc_fail(tBTA_HF_CLIENT_DATA *p_data) +{ + UNUSED(p_data); + + /* reinitialize stuff */ + bta_hf_client_cb.scb.conn_handle = 0; + bta_hf_client_cb.scb.peer_features = 0; + bta_hf_client_cb.scb.chld_features = 0; + bta_hf_client_cb.scb.role = BTA_HF_CLIENT_ACP; + bta_hf_client_cb.scb.svc_conn = FALSE; + bta_hf_client_cb.scb.send_at_reply = FALSE; + bta_hf_client_cb.scb.negotiated_codec = BTM_SCO_CODEC_CVSD; + + bta_hf_client_at_reset(); + + /* reopen server */ + bta_hf_client_start_server(); + + /* call open cback w. failure */ + bta_hf_client_cback_open(NULL, BTA_HF_CLIENT_FAIL_RFCOMM); +} + +/******************************************************************************* +** +** Function bta_hf_client_disc_fail +** +** Description This function handles a discovery failure. +** +** +** Returns void +** +*******************************************************************************/ +void bta_hf_client_disc_fail(tBTA_HF_CLIENT_DATA *p_data) +{ + UNUSED(p_data); + + /* reopen server */ + bta_hf_client_start_server(); + + /* reinitialize stuff */ + + /* call open cback w. failure */ + bta_hf_client_cback_open(NULL, BTA_HF_CLIENT_FAIL_SDP); +} + +/******************************************************************************* +** +** Function bta_hf_client_open_fail +** +** Description open connection failed. +** +** +** Returns void +** +*******************************************************************************/ +void bta_hf_client_open_fail(tBTA_HF_CLIENT_DATA *p_data) +{ + /* call open cback w. failure */ + bta_hf_client_cback_open(p_data, BTA_HF_CLIENT_FAIL_RESOURCES); +} + +/******************************************************************************* +** +** Function bta_hf_client_rfc_close +** +** Description RFCOMM connection closed. +** +** +** Returns void +** +*******************************************************************************/ +void bta_hf_client_rfc_close(tBTA_HF_CLIENT_DATA *p_data) +{ + UNUSED(p_data); + + /* reinitialize stuff */ + bta_hf_client_cb.scb.peer_features = 0; + bta_hf_client_cb.scb.chld_features = 0; + bta_hf_client_cb.scb.role = BTA_HF_CLIENT_ACP; + bta_hf_client_cb.scb.svc_conn = FALSE; + bta_hf_client_cb.scb.send_at_reply = FALSE; + bta_hf_client_cb.scb.negotiated_codec = BTM_SCO_CODEC_CVSD; + + bta_hf_client_at_reset(); + + bta_sys_conn_close(BTA_ID_HS, 1, bta_hf_client_cb.scb.peer_addr); + + /* call close cback */ + (*bta_hf_client_cb.p_cback)(BTA_HF_CLIENT_CLOSE_EVT, NULL); + + /* if not deregistering reopen server */ + if (bta_hf_client_cb.scb.deregister == FALSE) { + /* Clear peer bd_addr so instance can be reused */ + bdcpy(bta_hf_client_cb.scb.peer_addr, bd_addr_null); + + /* start server as it might got closed on open*/ + bta_hf_client_start_server(); + + bta_hf_client_cb.scb.conn_handle = 0; + + /* Make sure SCO is shutdown */ + bta_hf_client_sco_shutdown(NULL); + + bta_sys_sco_unuse(BTA_ID_HS, 1, bta_hf_client_cb.scb.peer_addr); + } + /* else close port and deallocate scb */ + else { + bta_hf_client_close_server(); + bta_hf_client_scb_disable(); + } +} + +/******************************************************************************* +** +** Function bta_hf_client_disc_int_res +** +** Description This function handles a discovery result when initiator. +** +** +** Returns void +** +*******************************************************************************/ +void bta_hf_client_disc_int_res(tBTA_HF_CLIENT_DATA *p_data) +{ + UINT16 event = BTA_HF_CLIENT_DISC_FAIL_EVT; + + APPL_TRACE_DEBUG ("bta_hf_client_disc_int_res: Status: %d", p_data->disc_result.status); + + /* if found service */ + if (p_data->disc_result.status == SDP_SUCCESS || + p_data->disc_result.status == SDP_DB_FULL) { + /* get attributes */ + if (bta_hf_client_sdp_find_attr()) { + event = BTA_HF_CLIENT_DISC_OK_EVT; + } + } + + /* free discovery db */ + bta_hf_client_free_db(p_data); + + /* send ourselves sdp ok/fail event */ + bta_hf_client_sm_execute(event, p_data); +} + +/******************************************************************************* +** +** Function bta_hf_client_disc_acp_res +** +** Description This function handles a discovery result when acceptor. +** +** +** Returns void +** +*******************************************************************************/ +void bta_hf_client_disc_acp_res(tBTA_HF_CLIENT_DATA *p_data) +{ + /* if found service */ + if (p_data->disc_result.status == SDP_SUCCESS || + p_data->disc_result.status == SDP_DB_FULL) { + /* get attributes */ + bta_hf_client_sdp_find_attr(); + } + + /* free discovery db */ + bta_hf_client_free_db(p_data); +} + +/******************************************************************************* +** +** Function bta_hf_client_rfc_data +** +** Description Read and process data from RFCOMM. +** +** +** Returns void +** +*******************************************************************************/ +void bta_hf_client_rfc_data(tBTA_HF_CLIENT_DATA *p_data) +{ + UINT16 len; + char *buf = osi_calloc(BTA_HF_CLIENT_RFC_READ_MAX); + if (buf == NULL) { + APPL_TRACE_ERROR("No mem %s", __FUNCTION__); + return; + } + + UNUSED(p_data); + + /* read data from rfcomm; if bad status, we're done */ + while (PORT_ReadData(bta_hf_client_cb.scb.conn_handle, buf, BTA_HF_CLIENT_RFC_READ_MAX, &len) == PORT_SUCCESS) { + /* if no data, we're done */ + if (len == 0) { + break; + } + + bta_hf_client_at_parse(buf, len); + + /* no more data to read, we're done */ + if (len < BTA_HF_CLIENT_RFC_READ_MAX) { + break; + } + } + osi_free(buf); +} + +/******************************************************************************* +** +** Function bta_hf_client_svc_conn_open +** +** Description Service level connection opened +** +** +** Returns void +** +*******************************************************************************/ +void bta_hf_client_svc_conn_open(tBTA_HF_CLIENT_DATA *p_data) +{ + tBTA_HF_CLIENT_CONN evt; + UNUSED(p_data); + + memset(&evt, 0, sizeof(evt)); + + if (!bta_hf_client_cb.scb.svc_conn) { + /* set state variable */ + bta_hf_client_cb.scb.svc_conn = TRUE; + + /* call callback */ + evt.peer_feat = bta_hf_client_cb.scb.peer_features; + evt.chld_feat = bta_hf_client_cb.scb.chld_features; + + (*bta_hf_client_cb.p_cback)(BTA_HF_CLIENT_CONN_EVT, &evt); + } +} + +/******************************************************************************* +** +** Function bta_hf_client_cback_ind +** +** Description Send indicator callback event to application. +** +** Returns void +** +*******************************************************************************/ +void bta_hf_client_ind(tBTA_HF_CLIENT_IND_TYPE type, UINT16 value) +{ + tBTA_HF_CLIENT_IND evt; + + memset(&evt, 0, sizeof(evt)); + + evt.type = type; + evt.value = value; + + (*bta_hf_client_cb.p_cback)(BTA_HF_CLIENT_IND_EVT, &evt); +} + +/******************************************************************************* +** +** Function bta_hf_client_evt_val +** +** Description Send event to application. +** This is a generic helper for events with common data. +** +** +** Returns void +** +*******************************************************************************/ +void bta_hf_client_evt_val(tBTA_HF_CLIENT_EVT type, UINT16 value) +{ + tBTA_HF_CLIENT_VAL evt; + + memset(&evt, 0, sizeof(evt)); + + evt.value = value; + + (*bta_hf_client_cb.p_cback)(type, &evt); +} + +/******************************************************************************* +** +** Function bta_hf_client_operator_name +** +** Description Send operator name event to application. +** +** +** Returns void +** +*******************************************************************************/ +void bta_hf_client_operator_name(char *name) +{ + tBTA_HF_CLIENT_OPERATOR_NAME *evt; + + if ((evt = osi_calloc(sizeof(tBTA_HF_CLIENT_OPERATOR_NAME))) != NULL) { + strlcpy(evt->name, name, BTA_HF_CLIENT_OPERATOR_NAME_LEN + 1); + evt->name[BTA_HF_CLIENT_OPERATOR_NAME_LEN] = '\0'; + + (*bta_hf_client_cb.p_cback)(BTA_HF_CLIENT_OPERATOR_NAME_EVT, evt); + osi_free(evt); + } else { + APPL_TRACE_ERROR("No mem: %s", __func__); + } +} + + +/******************************************************************************* +** +** Function bta_hf_client_clip +** +** Description Send CLIP event to application. +** +** +** Returns void +** +*******************************************************************************/ +void bta_hf_client_clip(char *number) +{ + tBTA_HF_CLIENT_NUMBER *evt; + + if ((evt = osi_calloc(sizeof(tBTA_HF_CLIENT_NUMBER))) != NULL) { + strlcpy(evt->number, number, BTA_HF_CLIENT_NUMBER_LEN + 1); + evt->number[BTA_HF_CLIENT_NUMBER_LEN] = '\0'; + + (*bta_hf_client_cb.p_cback)(BTA_HF_CLIENT_CLIP_EVT, evt); + osi_free(evt); + } else { + APPL_TRACE_ERROR("No mem: %s", __func__); + } +} + +/******************************************************************************* +** +** Function bta_hf_client_ccwa +** +** Description Send CLIP event to application. +** +** +** Returns void +** +*******************************************************************************/ +void bta_hf_client_ccwa(char *number) +{ + tBTA_HF_CLIENT_NUMBER *evt; + + if ((evt = osi_calloc(sizeof(tBTA_HF_CLIENT_NUMBER))) != NULL) { + strlcpy(evt->number, number, BTA_HF_CLIENT_NUMBER_LEN + 1); + evt->number[BTA_HF_CLIENT_NUMBER_LEN] = '\0'; + + + (*bta_hf_client_cb.p_cback)(BTA_HF_CLIENT_CCWA_EVT, evt); + osi_free(evt); + } else { + APPL_TRACE_ERROR("No mem: %s", __func__); + } +} + +/******************************************************************************* +** +** Function bta_hf_client_at_result +** +** Description Send AT result event to application. +** +** +** Returns void +** +*******************************************************************************/ +void bta_hf_client_at_result(tBTA_HF_CLIENT_AT_RESULT_TYPE type, UINT16 cme) +{ + tBTA_HF_CLIENT_AT_RESULT evt; + + memset(&evt, 0, sizeof(evt)); + + evt.type = type; + evt.cme = cme; + + (*bta_hf_client_cb.p_cback)(BTA_HF_CLIENT_AT_RESULT_EVT, &evt); +} + +/******************************************************************************* +** +** Function bta_hf_client_clcc +** +** Description Send clcc event to application. +** +** +** Returns void +** +*******************************************************************************/ +void bta_hf_client_clcc(UINT32 idx, BOOLEAN incoming, UINT8 status, BOOLEAN mpty, char *number) +{ + tBTA_HF_CLIENT_CLCC *evt; + + if ((evt = osi_calloc(sizeof(tBTA_HF_CLIENT_CLCC))) != NULL) { + evt->idx = idx; + evt->inc = incoming; + evt->status = status; + evt->mpty = mpty; + + if (number) { + evt->number_present = TRUE; + strlcpy(evt->number, number, BTA_HF_CLIENT_NUMBER_LEN + 1); + evt->number[BTA_HF_CLIENT_NUMBER_LEN] = '\0'; + } + + (*bta_hf_client_cb.p_cback)(BTA_HF_CLIENT_CLCC_EVT, evt); + osi_free(evt); + } else { + APPL_TRACE_ERROR("No mem, %s\n", __func__); + } +} + +/******************************************************************************* +** +** Function bta_hf_client_cnum +** +** Description Send cnum event to application. +** +** +** Returns void +** +*******************************************************************************/ +void bta_hf_client_cnum(char *number, UINT16 service) +{ + tBTA_HF_CLIENT_CNUM *evt; + + if ((evt = osi_calloc(sizeof(tBTA_HF_CLIENT_CNUM))) != NULL) { + + evt->service = service; + strlcpy(evt->number, number, BTA_HF_CLIENT_NUMBER_LEN + 1); + evt->number[BTA_HF_CLIENT_NUMBER_LEN] = '\0'; + + (*bta_hf_client_cb.p_cback)(BTA_HF_CLIENT_CNUM_EVT, evt); + osi_free(evt); + } else { + APPL_TRACE_ERROR("No mem, %s", __func__); + } +} + +/******************************************************************************* +** +** Function bta_hf_client_binp +** +** Description Send BINP event to application. +** +** +** Returns void +** +*******************************************************************************/ +void bta_hf_client_binp(char *number) +{ + tBTA_HF_CLIENT_NUMBER *evt; + + if ((evt = osi_calloc(sizeof(tBTA_HF_CLIENT_NUMBER))) != NULL) { + strlcpy(evt->number, number, BTA_HF_CLIENT_NUMBER_LEN + 1); + evt->number[BTA_HF_CLIENT_NUMBER_LEN] = '\0'; + + (*bta_hf_client_cb.p_cback)(BTA_HF_CLIENT_BINP_EVT, evt); + osi_free(evt); + } else { + APPL_TRACE_ERROR("No mem: %s", __func__); + } +} + +#endif /* #if (BTA_HF_INCLUDED == TRUE) */ diff --git a/lib/bt/host/bluedroid/bta/hf_client/bta_hf_client_api.c b/lib/bt/host/bluedroid/bta/hf_client/bta_hf_client_api.c new file mode 100644 index 00000000..6149d8c4 --- /dev/null +++ b/lib/bt/host/bluedroid/bta/hf_client/bta_hf_client_api.c @@ -0,0 +1,339 @@ +/****************************************************************************** + * + * Copyright (c) 2014 The Android Open Source Project + * Copyright (C) 2003-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This is the implementation of the API for the handsfree (HF role) + * subsystem of BTA + * + ******************************************************************************/ + +#include +#include "bta/bta_hf_client_api.h" +#include "bta_hf_client_int.h" +#include "osi/allocator.h" + +#if (BTA_HF_INCLUDED == TRUE) +/***************************************************************************** +** Constants and data types +*****************************************************************************/ +static const tBTA_SYS_REG bta_hf_client_reg = { + bta_hf_client_hdl_event, + BTA_HfClientDisable +}; + +static const uint8_t bta_hf_client_cb_data_size[] = { + 0, // #define BTA_HF_CLIENT_ENABLE_EVT 0 + sizeof(tBTA_HF_CLIENT_REGISTER), // #define BTA_HF_CLIENT_REGISTER_EVT 1 + sizeof(tBTA_HF_CLIENT_OPEN), // #define BTA_HF_CLIENT_OPEN_EVT 2 + 0, // #define BTA_HF_CLIENT_CLOSE_EVT 3 + sizeof(tBTA_HF_CLIENT_CONN), // #define BTA_HF_CLIENT_CONN_EVT 4 + sizeof(tBTA_HF_CLIENT_HDR), // #define BTA_HF_CLIENT_AUDIO_OPEN_EVT 5 + sizeof(tBTA_HF_CLIENT_HDR), //#define BTA_HF_CLIENT_AUDIO_MSBC_OPEN_EVT 6 + sizeof(tBTA_HF_CLIENT_HDR), // #define BTA_HF_CLIENT_AUDIO_CLOSE_EVT 7 + sizeof(tBTA_HF_CLIENT_VAL), // #define BTA_HF_CLIENT_SPK_EVT 8 + sizeof(tBTA_HF_CLIENT_VAL), // #define BTA_HF_CLIENT_MIC_EVT 9 + sizeof(tBTA_HF_CLIENT_IND), //#define BTA_HF_CLIENT_IND_EVT 10 + sizeof(tBTA_HF_CLIENT_VAL), // #define BTA_HF_CLIENT_VOICE_REC_EVT 11 + sizeof(tBTA_HF_CLIENT_OPERATOR_NAME), // #define BTA_HF_CLIENT_OPERATOR_NAME_EVT 12 + sizeof(tBTA_HF_CLIENT_NUMBER), // #define BTA_HF_CLIENT_CLIP_EVT 13 + sizeof(tBTA_HF_CLIENT_NUMBER), // #define BTA_HF_CLIENT_CCWA_EVT 14 + sizeof(tBTA_HF_CLIENT_AT_RESULT), // #define BTA_HF_CLIENT_AT_RESULT_EVT 15 + sizeof(tBTA_HF_CLIENT_CLCC), // #define BTA_HF_CLIENT_CLCC_EVT 16 + sizeof(tBTA_HF_CLIENT_CNUM), //#define BTA_HF_CLIENT_CNUM_EVT 17 + sizeof(tBTA_HF_CLIENT_VAL), // #define BTA_HF_CLIENT_BTRH_EVT 18 + sizeof(tBTA_HF_CLIENT_VAL), // #define BTA_HF_CLIENT_BSIR_EVT 19 + sizeof(tBTA_HF_CLIENT_NUMBER), // #define BTA_HF_CLIENT_BINP_EVT 20 + sizeof(tBTA_HF_CLIENT_VAL), // #define BTA_HF_CLIENT_RING_INDICATION 21 + 0, // #define BTA_HF_CLIENT_DISABLE_EVT 22 + sizeof(tBTA_SCO_PKT_STAT_NUMS), // #define BTA_HF_CLIENT_PKT_STAT_NUMS_GET_EVT 23 +}; +/***************************************************************************** +** External Function Declarations +*****************************************************************************/ + +/******************************************************************************* +** +** Function BTA_HfClientEnable +** +** Description Enable the HF CLient service. When the enable +** operation is complete the callback function will be +** called with a BTA_HF_CLIENT_ENABLE_EVT. This function must +** be called before other function in the HF CLient API are +** called. +** +** Returns BTA_SUCCESS if OK, BTA_FAILURE otherwise. +** +*******************************************************************************/ +tBTA_STATUS BTA_HfClientEnable(tBTA_HF_CLIENT_CBACK *p_cback) +{ + tBTA_HF_CLIENT_API_ENABLE *p_buf; + + if (bta_sys_is_register (BTA_ID_HS)) { + APPL_TRACE_ERROR("BTA HF Client is already enabled, ignoring ..."); + return BTA_FAILURE; + } + + /* register with BTA system manager */ + bta_sys_register(BTA_ID_HS, &bta_hf_client_reg); + + if ((p_buf = (tBTA_HF_CLIENT_API_ENABLE *) osi_malloc(sizeof(tBTA_HF_CLIENT_API_ENABLE))) != NULL) { + p_buf->hdr.event = BTA_HF_CLIENT_API_ENABLE_EVT; + p_buf->p_cback = p_cback; + bta_sys_sendmsg(p_buf); + } + + return BTA_SUCCESS; +} + +/******************************************************************************* +** +** Function BTA_HfClientDisable +** +** Description Disable the HF Client service +** +** +** Returns void +** +*******************************************************************************/ +void BTA_HfClientDisable(void) +{ + BT_HDR *p_buf; + + if ((p_buf = (BT_HDR *) osi_malloc(sizeof(BT_HDR))) != NULL) { + p_buf->event = BTA_HF_CLIENT_API_DISABLE_EVT; + bta_sys_sendmsg(p_buf); + } +} + +/******************************************************************************* +** +** Function BTA_HfClientRegister +** +** Description Register an HF Client service. +** +** +** Returns void +** +*******************************************************************************/ +void BTA_HfClientRegister(tBTA_SEC sec_mask, tBTA_HF_CLIENT_FEAT features, + char *p_service_name) +{ + tBTA_HF_CLIENT_API_REGISTER *p_buf; + + if ((p_buf = (tBTA_HF_CLIENT_API_REGISTER *) osi_malloc(sizeof(tBTA_HF_CLIENT_API_REGISTER))) != NULL) { + p_buf->hdr.event = BTA_HF_CLIENT_API_REGISTER_EVT; + p_buf->features = features; + p_buf->sec_mask = sec_mask; + if (p_service_name) { + BCM_STRNCPY_S(p_buf->name, p_service_name, BTA_SERVICE_NAME_LEN); + p_buf->name[BTA_SERVICE_NAME_LEN] = '\0'; + } else { + p_buf->name[0] = '\0'; + } + bta_sys_sendmsg(p_buf); + } +} + +/******************************************************************************* +** +** Function BTA_HfClientDeregister +** +** Description Deregister an HF Client service. +** +** +** Returns void +** +*******************************************************************************/ +void BTA_HfClientDeregister(UINT16 handle) +{ + BT_HDR *p_buf; + + if ((p_buf = (BT_HDR *) osi_malloc(sizeof(BT_HDR))) != NULL) { + p_buf->event = BTA_HF_CLIENT_API_DEREGISTER_EVT; + p_buf->layer_specific = handle; + bta_sys_sendmsg(p_buf); + } +} + +/******************************************************************************* +** +** Function BTA_HfClientOpen +** +** Description Opens a connection to an audio gateway. +** When connection is open callback function is called +** with a BTA_AG_OPEN_EVT. Only the data connection is +** opened. The audio connection is not opened. +** +** +** Returns void +** +*******************************************************************************/ +void BTA_HfClientOpen(UINT16 handle, BD_ADDR bd_addr, tBTA_SEC sec_mask) +{ + tBTA_HF_CLIENT_API_OPEN *p_buf; + + if ((p_buf = (tBTA_HF_CLIENT_API_OPEN *) osi_malloc(sizeof(tBTA_HF_CLIENT_API_OPEN))) != NULL) { + p_buf->hdr.event = BTA_HF_CLIENT_API_OPEN_EVT; + p_buf->hdr.layer_specific = handle; + bdcpy(p_buf->bd_addr, bd_addr); + p_buf->sec_mask = sec_mask; + bta_sys_sendmsg(p_buf); + } +} + +/******************************************************************************* +** +** Function BTA_HfClientClose +** +** Description Close the current connection to an audio gateway. +** Any current audio connection will also be closed +** +** +** Returns void +** +*******************************************************************************/ +void BTA_HfClientClose(UINT16 handle) +{ + BT_HDR *p_buf; + + if ((p_buf = (BT_HDR *) osi_malloc(sizeof(BT_HDR))) != NULL) { + p_buf->event = BTA_HF_CLIENT_API_CLOSE_EVT; + p_buf->layer_specific = handle; + bta_sys_sendmsg(p_buf); + } +} + +/******************************************************************************* +** +** Function BTA_HfCllientAudioOpen +** +** Description Opens an audio connection to the currently connected +** audio gateway +** +** +** Returns void +** +*******************************************************************************/ +void BTA_HfClientAudioOpen(UINT16 handle) +{ + BT_HDR *p_buf; + + if ((p_buf = (BT_HDR *) osi_malloc(sizeof(BT_HDR))) != NULL) { + p_buf->event = BTA_HF_CLIENT_API_AUDIO_OPEN_EVT; + p_buf->layer_specific = handle; + bta_sys_sendmsg(p_buf); + } +} + +/******************************************************************************* +** +** Function BTA_HfClientAudioClose +** +** Description Close the currently active audio connection to an audio +** gateway. The data connection remains open +** +** +** Returns void +** +*******************************************************************************/ +void BTA_HfClientAudioClose(UINT16 handle) +{ + BT_HDR *p_buf; + + if ((p_buf = (BT_HDR *) osi_malloc(sizeof(BT_HDR))) != NULL) { + p_buf->event = BTA_HF_CLIENT_API_AUDIO_CLOSE_EVT; + p_buf->layer_specific = handle; + bta_sys_sendmsg(p_buf); + } +} + +/******************************************************************************* +** +** Function BTA_HfClientSendAT +** +** Description send AT command +** +** +** Returns void +** +*******************************************************************************/ +void BTA_HfClientSendAT(UINT16 handle, tBTA_HF_CLIENT_AT_CMD_TYPE at, UINT32 val1, UINT32 val2, const char *str) +{ + tBTA_HF_CLIENT_DATA_VAL *p_buf; + + if ((p_buf = (tBTA_HF_CLIENT_DATA_VAL *) osi_malloc(sizeof(tBTA_HF_CLIENT_DATA_VAL))) != NULL) { + p_buf->hdr.event = BTA_HF_CLIENT_SEND_AT_CMD_EVT; + p_buf->uint8_val = at; + p_buf->uint32_val1 = val1; + p_buf->uint32_val2 = val2; + + if (str) { + UINT32 str_len = strlen(str); + if (str_len > BTA_HF_CLIENT_MAX_LEN) { + APPL_TRACE_WARNING("%s, str length(%d) is more than %d, truncate it.", __FUNCTION__, str_len, BTA_HF_CLIENT_MAX_LEN); + str_len = BTA_HF_CLIENT_MAX_LEN; + } + + strlcpy(p_buf->str, str, str_len + 1); + } else { + p_buf->str[0] = '\0'; + } + + p_buf->hdr.layer_specific = handle; + bta_sys_sendmsg(p_buf); + } +} + +#if (BTM_SCO_HCI_INCLUDED == TRUE) +/******************************************************************************* +** +** Function BTA_HfClientPktStatsNumsGet +** +** Description Get the packet ststus numbers received and send for a specific (e)SCO connection handle. +** +** +** Returns void +** +*******************************************************************************/ +void BTA_HfClientPktStatsNumsGet(UINT16 sync_conn_handle) +{ + tBTA_HF_CLIENT_PKT_STAT_GET *p_buf; + if ((p_buf = (tBTA_HF_CLIENT_PKT_STAT_GET *) osi_malloc(sizeof(tBTA_HF_CLIENT_PKT_STAT_GET))) != NULL) { + p_buf->hdr.event = BTA_HF_CLIENT_PKT_NUMS_GET_EVT; + p_buf->sync_conn_handle = sync_conn_handle; + bta_sys_sendmsg(p_buf); + } +} + +void BTA_HfClientCiData(void) +{ + BT_HDR *p_buf; + if ((p_buf = (BT_HDR *) osi_malloc(sizeof(BT_HDR))) != NULL) { + p_buf->event = BTA_HF_CLIENT_CI_SCO_DATA_EVT; + bta_sys_sendmsg(p_buf); + } +} +#endif /* #if (BTM_SCO_HCI_INCLUDED == TRUE ) */ + +int BTA_HfClientGetCbDataSize(tBTA_HF_CLIENT_EVT event) +{ + return bta_hf_client_cb_data_size[event]; +} +#endif /* #if (BTA_HF_INCLUDED == TRUE) */ diff --git a/lib/bt/host/bluedroid/bta/hf_client/bta_hf_client_at.c b/lib/bt/host/bluedroid/bta/hf_client/bta_hf_client_at.c new file mode 100644 index 00000000..adf3d609 --- /dev/null +++ b/lib/bt/host/bluedroid/bta/hf_client/bta_hf_client_at.c @@ -0,0 +1,1862 @@ +/****************************************************************************** + * + * Copyright (c) 2014 The Android Open Source Project + * Copyright (C) 2003-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ +// #include +#include +#include + +#include "bta/bta_hf_client_api.h" +#include "bta_hf_client_int.h" +#include "stack/port_api.h" +#include "osi/allocator.h" + +#if (BTA_HF_INCLUDED == TRUE) +/* Uncomment to enable AT traffic dumping */ +/* #define BTA_HF_CLIENT_AT_DUMP 1 */ + +/* minimum length of AT event */ +#define BTA_HF_CLIENT_AT_EVENT_MIN_LEN 3 + +/* timeout for AT response */ +#define BTA_HF_CLIENT_AT_TIMEOUT 29989 + +/* timeout for AT hold timer */ +#define BTA_HF_CLIENT_AT_HOLD_TIMEOUT 41 + +/****************************************************************************** +** +** DATA TYPES AND CONTAINERS +** +*******************************************************************************/ +/* BRSF: store received values here */ +extern tBTA_HF_CLIENT_CB bta_hf_client_cb; + +/****************************************************************************** +** SUPPORTED EVENT MESSAGES +*******************************************************************************/ + +/* CIND: supported indicator names */ +#define BTA_HF_CLIENT_INDICATOR_BATTERYCHG "battchg" +#define BTA_HF_CLIENT_INDICATOR_SIGNAL "signal" +#define BTA_HF_CLIENT_INDICATOR_SERVICE "service" +#define BTA_HF_CLIENT_INDICATOR_CALL "call" +#define BTA_HF_CLIENT_INDICATOR_ROAM "roam" +#define BTA_HF_CLIENT_INDICATOR_CALLSETUP "callsetup" +#define BTA_HF_CLIENT_INDICATOR_CALLHELD "callheld" + +#define MIN(a, b) \ + ({ __typeof__(a) _a = (a); __typeof__(b) _b = (b); (_a < _b) ? _a : _b; }) + +/* CIND: represents each indicators boundaries */ +typedef struct { + char *name; + UINT8 min; + UINT8 max; + UINT8 namelen; +} tBTA_HF_CLIENT_INDICATOR; + +#define BTA_HF_CLIENT_AT_SUPPORTED_INDICATOR_COUNT 7 + +/* CIND: storage room for indicators value range and their statuses */ +static const tBTA_HF_CLIENT_INDICATOR bta_hf_client_indicators[BTA_HF_CLIENT_AT_SUPPORTED_INDICATOR_COUNT] = { + /* name | min | max | name length - used by parser */ + {BTA_HF_CLIENT_INDICATOR_CALL, 0, 1, sizeof(BTA_HF_CLIENT_INDICATOR_CALL)}, + {BTA_HF_CLIENT_INDICATOR_CALLSETUP, 0, 3, sizeof(BTA_HF_CLIENT_INDICATOR_CALLSETUP)}, + {BTA_HF_CLIENT_INDICATOR_SERVICE, 0, 1, sizeof(BTA_HF_CLIENT_INDICATOR_SERVICE)}, + {BTA_HF_CLIENT_INDICATOR_SIGNAL, 0, 5, sizeof(BTA_HF_CLIENT_INDICATOR_SIGNAL)}, + {BTA_HF_CLIENT_INDICATOR_ROAM, 0, 1, sizeof(BTA_HF_CLIENT_INDICATOR_ROAM)}, + {BTA_HF_CLIENT_INDICATOR_BATTERYCHG, 0, 5, sizeof(BTA_HF_CLIENT_INDICATOR_BATTERYCHG)}, + {BTA_HF_CLIENT_INDICATOR_CALLHELD, 0, 2, sizeof(BTA_HF_CLIENT_INDICATOR_CALLHELD)} +}; + +/* +VGM/+VGS - gain min/max values */ +#define BTA_HF_CLIENT_VGS_MIN 0 +#define BTA_HF_CLIENT_VGS_MAX 15 +#define BTA_HF_CLIENT_VGM_MIN 0 +#define BTA_HF_CLIENT_VGM_MAX 15 + +UINT32 service_index = 0; +BOOLEAN service_availability = TRUE; +/* helper functions for handling AT commands queueing */ + +static void bta_hf_client_handle_ok(void); + +static void bta_hf_client_clear_queued_at(void) +{ + tBTA_HF_CLIENT_AT_QCMD *cur = bta_hf_client_cb.scb.at_cb.queued_cmd; + tBTA_HF_CLIENT_AT_QCMD *next; + + while (cur != NULL) { + next = cur->next; + osi_free(cur); + cur = next; + } + + bta_hf_client_cb.scb.at_cb.queued_cmd = NULL; +} + +static void bta_hf_client_queue_at(tBTA_HF_CLIENT_AT_CMD cmd, const char *buf, UINT16 buf_len) +{ + tBTA_HF_CLIENT_AT_QCMD *new_cmd; + + APPL_TRACE_DEBUG("%s", __FUNCTION__); + + if ((new_cmd = (tBTA_HF_CLIENT_AT_QCMD *) osi_malloc(sizeof(tBTA_HF_CLIENT_AT_QCMD))) != NULL) { + new_cmd->cmd = cmd; + new_cmd->buf_len = buf_len; + new_cmd->next = NULL; + memcpy(new_cmd->buf, buf, buf_len); + + if (bta_hf_client_cb.scb.at_cb.queued_cmd != NULL) { + tBTA_HF_CLIENT_AT_QCMD *qcmd = bta_hf_client_cb.scb.at_cb.queued_cmd; + + while (qcmd->next != NULL) { + qcmd = qcmd->next; + } + + qcmd->next = new_cmd; + } else { + bta_hf_client_cb.scb.at_cb.queued_cmd = new_cmd; + } + } +} + +static void bta_hf_client_at_resp_timer_cback (TIMER_LIST_ENT *p_tle) +{ + if (p_tle) { + bta_hf_client_cb.scb.at_cb.resp_timer_on = FALSE; + + APPL_TRACE_ERROR("HFPClient: AT response timeout, disconnecting"); + + bta_hf_client_sm_execute(BTA_HF_CLIENT_API_CLOSE_EVT, NULL); + } +} + +static void bta_hf_client_stop_at_resp_timer(void) +{ + if (bta_hf_client_cb.scb.at_cb.resp_timer_on) { + bta_hf_client_cb.scb.at_cb.resp_timer_on = FALSE; + bta_sys_stop_timer (&bta_hf_client_cb.scb.at_cb.resp_timer); + } +} + +static void bta_hf_client_free_at_resp_timer(void) +{ + bta_hf_client_cb.scb.at_cb.resp_timer_on = FALSE; + bta_sys_free_timer (&bta_hf_client_cb.scb.at_cb.resp_timer); +} + +static void bta_hf_client_start_at_resp_timer(void) +{ + if (bta_hf_client_cb.scb.at_cb.resp_timer_on) { + bta_sys_stop_timer (&bta_hf_client_cb.scb.at_cb.resp_timer); + } + + bta_hf_client_cb.scb.at_cb.resp_timer.p_cback = (TIMER_CBACK *)&bta_hf_client_at_resp_timer_cback; + bta_sys_start_timer(&bta_hf_client_cb.scb.at_cb.resp_timer, 0, BTA_HF_CLIENT_AT_TIMEOUT); + bta_hf_client_cb.scb.at_cb.resp_timer_on = TRUE; +} + +static void bta_hf_client_send_at(tBTA_HF_CLIENT_AT_CMD cmd, char *buf, UINT16 buf_len) +{ + if ((bta_hf_client_cb.scb.at_cb.current_cmd == BTA_HF_CLIENT_AT_NONE || + bta_hf_client_cb.scb.svc_conn == FALSE) && + bta_hf_client_cb.scb.at_cb.hold_timer_on == FALSE) { + UINT16 len; + +#ifdef BTA_HF_CLIENT_AT_DUMP + APPL_TRACE_DEBUG("%s %.*s", __FUNCTION__, buf_len - 1, buf); +#endif + + bta_hf_client_cb.scb.at_cb.current_cmd = cmd; + /* Generate fake responses for these because they won't reliably work */ + if (!service_availability && + (cmd == BTA_HF_CLIENT_AT_CNUM || cmd == BTA_HF_CLIENT_AT_COPS)) { + APPL_TRACE_WARNING("%s: No service, skipping %d command", __FUNCTION__, cmd); + bta_hf_client_handle_ok(); + return; + } + + PORT_WriteData(bta_hf_client_cb.scb.conn_handle, buf, buf_len, &len); + + bta_hf_client_start_at_resp_timer(); + + return; + } + + bta_hf_client_queue_at(cmd, buf, buf_len); +} + +static void bta_hf_client_send_queued_at(void) +{ + tBTA_HF_CLIENT_AT_QCMD *cur = bta_hf_client_cb.scb.at_cb.queued_cmd; + + APPL_TRACE_DEBUG("%s", __FUNCTION__); + + if (cur != NULL) { + bta_hf_client_cb.scb.at_cb.queued_cmd = cur->next; + + bta_hf_client_send_at(cur->cmd, cur->buf, cur->buf_len); + + osi_free(cur); + } +} + +static void bta_hf_client_at_hold_timer_cback(TIMER_LIST_ENT *p_tle) +{ + APPL_TRACE_DEBUG("%s", __FUNCTION__); + + if (p_tle) { + bta_hf_client_cb.scb.at_cb.hold_timer_on = FALSE; + bta_hf_client_send_queued_at(); + } +} + +static void bta_hf_client_stop_at_hold_timer(void) +{ + APPL_TRACE_DEBUG("%s", __FUNCTION__); + + if (bta_hf_client_cb.scb.at_cb.hold_timer_on) { + bta_hf_client_cb.scb.at_cb.hold_timer_on = FALSE; + bta_sys_stop_timer (&bta_hf_client_cb.scb.at_cb.hold_timer); + } +} + +static void bta_hf_client_free_at_hold_timer(void) +{ + APPL_TRACE_DEBUG("%s", __FUNCTION__); + + bta_hf_client_cb.scb.at_cb.hold_timer_on = FALSE; + bta_sys_free_timer(&bta_hf_client_cb.scb.at_cb.hold_timer); +} + +static void bta_hf_client_start_at_hold_timer(void) +{ + TIMER_LIST_ENT *timer = &bta_hf_client_cb.scb.at_cb.hold_timer; + + APPL_TRACE_DEBUG("%s", __FUNCTION__); + + if (bta_hf_client_cb.scb.at_cb.hold_timer_on) { + bta_sys_stop_timer (timer); + } + + timer->p_cback = (TIMER_CBACK *)&bta_hf_client_at_hold_timer_cback; + bta_sys_start_timer(timer, 0, BTA_HF_CLIENT_AT_HOLD_TIMEOUT); + bta_hf_client_cb.scb.at_cb.hold_timer_on = TRUE; +} + +/****************************************************************************** +** +** COMMON AT EVENT HANDLING FUNCTIONS +** +** Receives data (strings, ints, etc.) from the parser and processes this data. +** No buffer parsing is being done here. +*******************************************************************************/ + +static void bta_hf_client_handle_ok(void) +{ + APPL_TRACE_DEBUG("%s", __FUNCTION__); + + bta_hf_client_stop_at_resp_timer(); + + if (!bta_hf_client_cb.scb.svc_conn) { + bta_hf_client_slc_seq(FALSE); + return; + } + + switch (bta_hf_client_cb.scb.at_cb.current_cmd) { + case BTA_HF_CLIENT_AT_BIA: + case BTA_HF_CLIENT_AT_BCC: + break; + case BTA_HF_CLIENT_AT_BCS: + bta_hf_client_start_at_hold_timer(); + bta_hf_client_cb.scb.at_cb.current_cmd = BTA_HF_CLIENT_AT_NONE; + return; + case BTA_HF_CLIENT_AT_CLIP: //last cmd is post slc seq + if (bta_hf_client_cb.scb.send_at_reply == FALSE) { + bta_hf_client_cb.scb.send_at_reply = TRUE; + } + break; + case BTA_HF_CLIENT_AT_NONE: + bta_hf_client_stop_at_hold_timer(); + break; + default: + if (bta_hf_client_cb.scb.send_at_reply) { + bta_hf_client_at_result(BTA_HF_CLIENT_AT_RESULT_OK, 0); + } + break; + } + + bta_hf_client_cb.scb.at_cb.current_cmd = BTA_HF_CLIENT_AT_NONE; + + bta_hf_client_send_queued_at(); +} + +static void bta_hf_client_handle_error(tBTA_HF_CLIENT_AT_RESULT_TYPE type, UINT16 cme) +{ + APPL_TRACE_DEBUG("%s %u %u", __FUNCTION__, type, cme); + + bta_hf_client_stop_at_resp_timer(); + + if (!bta_hf_client_cb.scb.svc_conn) { + bta_hf_client_slc_seq(TRUE); + return; + } + + switch (bta_hf_client_cb.scb.at_cb.current_cmd) { + case BTA_HF_CLIENT_AT_BIA: + break; + case BTA_HF_CLIENT_AT_BCC: + case BTA_HF_CLIENT_AT_BCS: + bta_hf_client_cback_sco(BTA_HF_CLIENT_AUDIO_CLOSE_EVT); + break; + case BTA_HF_CLIENT_AT_CLIP: //last cmd is post slc seq + if (bta_hf_client_cb.scb.send_at_reply == FALSE) { + bta_hf_client_cb.scb.send_at_reply = TRUE; + } + break; + default: + if (bta_hf_client_cb.scb.send_at_reply) { + bta_hf_client_at_result(type, cme); + } + break; + } + + bta_hf_client_cb.scb.at_cb.current_cmd = BTA_HF_CLIENT_AT_NONE; + + bta_hf_client_send_queued_at(); +} + +static void bta_hf_client_handle_ring(void) +{ + APPL_TRACE_DEBUG("%s", __FUNCTION__); + bta_hf_client_evt_val(BTA_HF_CLIENT_RING_INDICATION, 0); +} + +static void bta_hf_client_handle_brsf(UINT32 value) +{ + APPL_TRACE_DEBUG("%s 0x%x", __FUNCTION__, value); + bta_hf_client_cb.scb.peer_features = value; +} + +/* handles a single indicator descriptor - registers it for value changing events */ +static void bta_hf_client_handle_cind_list_item(char *name, UINT32 min, UINT32 max, UINT32 index) +{ + + UINT8 i = 0; + + APPL_TRACE_DEBUG("%s %u.%s <%u:%u>", __FUNCTION__, index, name, min, max); + + /* look for a matching indicator on list of supported ones */ + for (i = 0; i < BTA_HF_CLIENT_AT_SUPPORTED_INDICATOR_COUNT; i++) { + if (strcmp(name, BTA_HF_CLIENT_INDICATOR_SERVICE) == 0) { + service_index = index; + } + /* look for a match - search one sign further than indicators name to check for string end */ + /* It will distinguish 'callheld' which could be matched by strncmp as 'call'. */ + if (strncmp(name, bta_hf_client_indicators[i].name, bta_hf_client_indicators[i].namelen) != 0) { + continue; + } + + /* index - enumerates value position in the incoming sequence */ + /* if name matches one of the known indicators, add its incoming position */ + /* to lookup table for easy value->indicator matching later, when only values come */ + bta_hf_client_cb.scb.at_cb.indicator_lookup[index] = i; + + return; + } +} + +static void bta_hf_client_handle_cind_value(UINT32 index, UINT32 value) +{ + APPL_TRACE_DEBUG("%s index: %u value: %u", __FUNCTION__, index, value); + + if (index >= BTA_HF_CLIENT_AT_INDICATOR_COUNT) { + return; + } + + if (service_index == index) { + if (value == 0) { + service_availability = FALSE; + } else { + service_availability = TRUE; + } + } + if (bta_hf_client_cb.scb.at_cb.indicator_lookup[index] == -1) { + return; + } + + /* get the real array index from lookup table */ + index = bta_hf_client_cb.scb.at_cb.indicator_lookup[index]; + + /* Ignore out of range values */ + if (value > bta_hf_client_indicators[index].max || + value < bta_hf_client_indicators[index].min) { + return; + } + + /* tBTA_HF_CLIENT_IND_TYPE match index in bta_hf_client_indicators */ + bta_hf_client_ind(index, value); +} + +static void bta_hf_client_handle_chld(UINT32 mask) +{ + APPL_TRACE_DEBUG("%s 0x%x", __FUNCTION__, mask); + + bta_hf_client_cb.scb.chld_features |= mask; +} + +static void bta_hf_client_handle_ciev(UINT32 index, UINT32 value) +{ + INT8 realind = -1; + + APPL_TRACE_DEBUG("%s index: %u value: %u", __FUNCTION__, index, value); + + if (index == 0 || index > BTA_HF_CLIENT_AT_INDICATOR_COUNT) { + APPL_TRACE_WARNING("%s: Invalid index %d", __FUNCTION__, index); + return; + } + + if (service_index == index - 1) { + service_availability = value == 0 ? FALSE : TRUE; + } + + realind = bta_hf_client_cb.scb.at_cb.indicator_lookup[index - 1]; + + if (realind >= 0 && realind < BTA_HF_CLIENT_AT_SUPPORTED_INDICATOR_COUNT) { + /* get the real in-array index from lookup table by index it comes at */ + /* if there is no bug it should automatically be correctly calculated */ + if (value > bta_hf_client_indicators[realind].max || value < bta_hf_client_indicators[realind].min) { + return; + } + + /* update service availability on +ciev from AG. */ + if (service_index == (index - 1)) { + if (value == 1) { + service_availability = TRUE; + } else { + service_availability = FALSE; + } + } + + /* tBTA_HF_CLIENT_IND_TYPE match index in bta_hf_client_indicators */ + bta_hf_client_ind(realind, value); + } +} + +static void bta_hf_client_handle_bcs(UINT32 codec) +{ + APPL_TRACE_DEBUG("%s %u", __FUNCTION__, codec); + + if (codec == BTM_SCO_CODEC_CVSD || + (codec == BTM_SCO_CODEC_MSBC && bta_hf_client_cb.msbc_enabled == TRUE)) { + bta_hf_client_cb.scb.negotiated_codec = codec; + bta_hf_client_send_at_bcs(codec); + } else { + bta_hf_client_cb.scb.negotiated_codec = BTM_SCO_CODEC_CVSD; + bta_hf_client_send_at_bac(); + } +} + +static void bta_hf_client_handle_bsir(UINT32 provided) +{ + APPL_TRACE_DEBUG("%s %u", __FUNCTION__, provided); + + bta_hf_client_evt_val(BTA_HF_CLIENT_BSIR_EVT, provided); +} + +static void bta_hf_client_handle_cmeerror(UINT32 code) +{ + bta_hf_client_handle_error(BTA_HF_CLIENT_AT_RESULT_CME, code); +} + +static void bta_hf_client_handle_vgm(UINT32 value) +{ + APPL_TRACE_DEBUG("%s %u", __FUNCTION__, value); + + if (value <= BTA_HF_CLIENT_VGM_MAX) { + bta_hf_client_evt_val(BTA_HF_CLIENT_MIC_EVT, value); + } +} + +static void bta_hf_client_handle_vgs(UINT32 value) +{ + APPL_TRACE_DEBUG("%s %u", __FUNCTION__, value); + + if (value <= BTA_HF_CLIENT_VGS_MAX) { + bta_hf_client_evt_val(BTA_HF_CLIENT_SPK_EVT, value); + } +} + +static void bta_hf_client_handle_bvra(UINT32 value) +{ + APPL_TRACE_DEBUG("%s %u", __FUNCTION__, value); + + if (value > 1) { + return; + } + + bta_hf_client_evt_val(BTA_HF_CLIENT_VOICE_REC_EVT, value); +} + +static void bta_hf_client_handle_clip(char *numstr, UINT32 type) +{ + APPL_TRACE_DEBUG("%s %u %s", __FUNCTION__, type, numstr); + + bta_hf_client_clip(numstr); +} + +static void bta_hf_client_handle_ccwa(char *numstr, UINT32 type) +{ + APPL_TRACE_DEBUG("%s %u %s", __FUNCTION__, type, numstr); + + bta_hf_client_ccwa(numstr); +} + +static void bta_hf_client_handle_cops(char *opstr, UINT32 mode) +{ + APPL_TRACE_DEBUG("%s %u %s", __FUNCTION__, mode, opstr); + + bta_hf_client_operator_name(opstr); +} + +static void bta_hf_client_handle_binp(char *numstr) +{ + APPL_TRACE_DEBUG("%s %s", __FUNCTION__, numstr); + + bta_hf_client_binp(numstr); +} + +static void bta_hf_client_handle_clcc(UINT16 idx, UINT16 dir, UINT16 status, UINT16 mode, UINT16 mpty, char *numstr, UINT16 type) +{ + APPL_TRACE_DEBUG("%s idx: %u dir: %u status: %u mode: %u mpty: %u", + __FUNCTION__, idx, dir, status, mode, mpty); + + if (numstr) { + APPL_TRACE_DEBUG("%s number: %s type: %u", __FUNCTION__, numstr, type); + } + + bta_hf_client_clcc(idx, dir, status, mpty, numstr); +} + +static void bta_hf_client_handle_cnum( char *numstr, UINT16 type, UINT16 service) +{ + APPL_TRACE_DEBUG("%s number: %s type: %u service: %u", __FUNCTION__, numstr, type, service); + + /* TODO: should number be modified according to type? */ + bta_hf_client_cnum(numstr, service); +} + +static void bta_hf_client_handle_btrh( UINT16 code) +{ + APPL_TRACE_DEBUG("%s %u", __FUNCTION__, code); + + bta_hf_client_evt_val(BTA_HF_CLIENT_BTRH_EVT, code); +} + +/****************************************************************************** +** +** COMMON AT EVENTS PARSING FUNCTIONS +** +*******************************************************************************/ + +/* Check if prefix match and skip spaces if any */ +#define AT_CHECK_EVENT(buf, event) \ +if (strncmp("\r\n"event,buf,sizeof("\r\n"event) - 1) != 0) \ + return buf; \ +buf += sizeof("\r\n"event) - 1; \ +while (*buf == ' ') buf++; + +/* check for and forward buffer if match */ +#define AT_CHECK_RN(buf) \ + if (strncmp("\r\n", buf, sizeof("\r\n") - 1) != 0) { \ + APPL_TRACE_ERROR("%s missing end ", __FUNCTION__); \ + return NULL;\ + } \ + buf += sizeof("\r\n") - 1; + +/* skip rest of AT string up to */ +#define AT_SKIP_REST(buf) while(*buf != '\r') buf++; + +static char *bta_hf_client_parse_ok(char *buffer) +{ + AT_CHECK_EVENT(buffer, "OK"); + AT_CHECK_RN(buffer); + + bta_hf_client_handle_ok(); + + return buffer; +} + +static char *bta_hf_client_parse_error(char *buffer) +{ + AT_CHECK_EVENT(buffer, "ERROR"); + AT_CHECK_RN(buffer); + + bta_hf_client_handle_error(BTA_HF_CLIENT_AT_RESULT_ERROR, 0); + + return buffer; +} + +static char *bta_hf_client_parse_ring(char *buffer) +{ + AT_CHECK_EVENT(buffer, "RING"); + AT_CHECK_RN(buffer); + + bta_hf_client_handle_ring(); + + return buffer; +} + +/* generic uint32 parser */ +static char *bta_hf_client_parse_uint32(char *buffer, void (*handler_callback)(UINT32)) +{ + UINT32 value; + int res; + int offset; + + res = sscanf(buffer, "%u%n", &value, &offset); + if (res < 1) { + return NULL; + } + + buffer += offset; + + AT_CHECK_RN(buffer); + + handler_callback(value); + return buffer; +} + +static char *bta_hf_client_parse_brsf(char *buffer) +{ + AT_CHECK_EVENT(buffer, "+BRSF:"); + + return bta_hf_client_parse_uint32(buffer, bta_hf_client_handle_brsf); +} + +static char *bta_hf_client_parse_cind_values(char *buffer) +{ + /* value and its position */ + UINT16 index = 0; + UINT32 value = 0; + + int offset; + int res; + + while ((res = sscanf(buffer, "%u%n", &value, &offset)) > 0) { + /* decides if its valid index and value, if yes stores it */ + bta_hf_client_handle_cind_value(index, value); + + buffer += offset; + + /* check if more values are present */ + if (*buffer != ',') { + break; + } + + index++; + buffer++; + } + + if (res > 0) { + AT_CHECK_RN(buffer); + return buffer; + } + + return NULL; +} + +static char *bta_hf_client_parse_cind_list(char *buffer) +{ + int offset; + char *name = osi_malloc(129); + UINT32 min, max; + UINT32 index = 0; + int res; + + if (name == NULL) { + APPL_TRACE_ERROR("No mem %s", __FUNCTION__); + return NULL; + } + + while ((res = sscanf(buffer, "(\"%128[^\"]\",(%u%*[-,]%u))%n", name, &min, &max, &offset)) > 2) { + bta_hf_client_handle_cind_list_item(name, min, max, index); + buffer += offset; + index++; + + if (*buffer != ',') { + break; + } + + buffer++; + } + + osi_free(name); + + if (res > 2) { + AT_CHECK_RN(buffer); + return buffer; + } + + return NULL; +} + +static char *bta_hf_client_parse_cind(char *buffer) +{ + AT_CHECK_EVENT(buffer, "+CIND:"); + + if (*buffer == '(') { + return bta_hf_client_parse_cind_list(buffer); + } + + return bta_hf_client_parse_cind_values(buffer); +} + +static char *bta_hf_client_parse_chld(char *buffer) +{ + AT_CHECK_EVENT(buffer, "+CHLD:"); + + if (*buffer != '(') { + return NULL; + } + + buffer++; + + while (*buffer != '\0') { + if (strncmp("0", buffer, 1) == 0) { + bta_hf_client_handle_chld(BTA_HF_CLIENT_CHLD_REL); + buffer++; + } else if (strncmp("1x", buffer, 2) == 0) { + bta_hf_client_handle_chld(BTA_HF_CLIENT_CHLD_REL_X); + buffer += 2; + } else if (strncmp("1", buffer, 1) == 0) { + bta_hf_client_handle_chld(BTA_HF_CLIENT_CHLD_REL_ACC); + buffer++; + } else if (strncmp("2x", buffer, 2) == 0) { + bta_hf_client_handle_chld(BTA_HF_CLIENT_CHLD_PRIV_X); + buffer += 2; + } else if (strncmp("2", buffer, 1) == 0) { + bta_hf_client_handle_chld(BTA_HF_CLIENT_CHLD_HOLD_ACC); + buffer++; + } else if (strncmp("3", buffer, 1) == 0) { + bta_hf_client_handle_chld(BTA_HF_CLIENT_CHLD_MERGE); + buffer++; + } else if (strncmp("4", buffer, 1) == 0) { + bta_hf_client_handle_chld(BTA_HF_CLIENT_CHLD_MERGE_DETACH); + buffer++; + } else { + return NULL; + } + + if (*buffer == ',') { + buffer++; + continue; + } + + if (*buffer == ')') { + buffer++; + break; + } + + return NULL; + } + + AT_CHECK_RN(buffer); + + return buffer; +} + +static char *bta_hf_client_parse_ciev(char *buffer) +{ + UINT32 index, value; + int res; + int offset; + + AT_CHECK_EVENT(buffer, "+CIEV:"); + + res = sscanf(buffer, "%u,%u%n", &index, &value, &offset); + if (res < 2) { + return NULL; + } + + buffer += offset; + + AT_CHECK_RN(buffer); + + bta_hf_client_handle_ciev(index, value); + return buffer; +} + +static char *bta_hf_client_parse_bcs(char *buffer) +{ + AT_CHECK_EVENT(buffer, "+BCS:"); + + return bta_hf_client_parse_uint32(buffer, bta_hf_client_handle_bcs); +} + +static char *bta_hf_client_parse_bsir(char *buffer) +{ + AT_CHECK_EVENT(buffer, "+BSIR:"); + + return bta_hf_client_parse_uint32(buffer, bta_hf_client_handle_bsir); +} + +static char *bta_hf_client_parse_cmeerror(char *buffer) +{ + AT_CHECK_EVENT(buffer, "+CME ERROR:"); + + return bta_hf_client_parse_uint32(buffer, bta_hf_client_handle_cmeerror); +} + +static char *bta_hf_client_parse_vgm(char *buffer) +{ + AT_CHECK_EVENT(buffer, "+VGM:"); + + return bta_hf_client_parse_uint32(buffer, bta_hf_client_handle_vgm); +} + +static char *bta_hf_client_parse_vgme(char *buffer) +{ + AT_CHECK_EVENT(buffer, "+VGM="); + + return bta_hf_client_parse_uint32(buffer, bta_hf_client_handle_vgm); +} + +static char *bta_hf_client_parse_vgs(char *buffer) +{ + AT_CHECK_EVENT(buffer, "+VGS:"); + + return bta_hf_client_parse_uint32(buffer, bta_hf_client_handle_vgs); +} + +static char *bta_hf_client_parse_vgse(char *buffer) +{ + AT_CHECK_EVENT(buffer, "+VGS="); + + return bta_hf_client_parse_uint32(buffer, bta_hf_client_handle_vgs); +} + +static char *bta_hf_client_parse_bvra(char *buffer) +{ + AT_CHECK_EVENT(buffer, "+BVRA:"); + + return bta_hf_client_parse_uint32(buffer, bta_hf_client_handle_bvra); +} + +static char *bta_hf_client_parse_clip(char *buffer) +{ + /* spec forces 32 chars, plus \0 here */ + char number[33]; + UINT32 type = 0; + int res; + int offset; + + AT_CHECK_EVENT(buffer, "+CLIP:"); + + /* there might be something more after %lu but HFP doesn't care */ + res = sscanf(buffer, "\"%32[^\"]\",%u%n", number, &type, &offset); + if (res < 2) { + return NULL; + } + + buffer += offset; + + AT_SKIP_REST(buffer); + + AT_CHECK_RN(buffer); + + bta_hf_client_handle_clip(number, type); + return buffer; +} + +/* in HFP context there is no difference between ccwa and clip */ +static char *bta_hf_client_parse_ccwa(char *buffer) +{ + /* ac to spec 32 chars max, plus \0 here */ + char number[33]; + UINT32 type = 0; + int res ; + int offset; + + AT_CHECK_EVENT(buffer, "+CCWA:"); + + /* there might be something more after %lu but HFP doesn't care */ + res = sscanf(buffer, "\"%32[^\"]\",%u%n", number, &type, &offset); + if (res < 2) { + return NULL; + } + + buffer += offset; + + AT_SKIP_REST(buffer); + + AT_CHECK_RN(buffer); + + bta_hf_client_handle_ccwa(number, type); + return buffer; +} + +static char *bta_hf_client_parse_cops(char *buffer) +{ + UINT8 mode; + /* spec forces 16 chars max, plus \0 here */ + char opstr[17]; + int res; + int offset; + + AT_CHECK_EVENT(buffer, "+COPS:"); + + /* TODO: Not sure if operator string actually can contain escaped " char inside */ + res = sscanf(buffer, "%hhi,0,\"%16[^\"]\"%n", &mode, opstr, &offset); + if (res < 2) { + return NULL; + } + + buffer += offset; + + AT_SKIP_REST(buffer); + + AT_CHECK_RN(buffer); + + bta_hf_client_handle_cops(opstr, mode); + return buffer; +} + +static char *bta_hf_client_parse_binp(char *buffer) +{ + /* HFP only supports phone number as BINP data */ + /* phone number is 32 chars plus one for \0*/ + char numstr[33]; + int res; + int offset; + + AT_CHECK_EVENT(buffer, "+BINP:"); + + res = sscanf(buffer, "\"%32[^\"]\"\r\n%n", numstr, &offset); + if (res < 1) { + return NULL; + } + + buffer += offset; + + /* some phones might sent type as well, just skip it */ + AT_SKIP_REST(buffer); + + AT_CHECK_RN(buffer); + + bta_hf_client_handle_binp(numstr); + return buffer; +} + +static char *bta_hf_client_parse_clcc(char *buffer) +{ + UINT16 idx, dir, status, mode, mpty; + char numstr[33]; /* spec forces 32 chars, plus one for \0*/ + UINT16 type; + int res; + int offset; + AT_CHECK_EVENT(buffer, "+CLCC:"); + + res = sscanf(buffer, "%hu,%hu,%hu,%hu,%hu%n", + &idx, &dir, &status, &mode, &mpty, &offset); + if (res < 5) { + return NULL; + } + + /* Abort in case offset not set because of format error */ + if (offset == 0) { + APPL_TRACE_ERROR("%s: Format Error %s", __func__, buffer); + return NULL; + } + + buffer += offset; + offset = 0; + + /* check optional part */ + if (*buffer == ',') { + int res2 = sscanf(buffer, ",\"%32[^\"]\",%hu%n", numstr, &type, &offset); + if (res2 < 0) { + return NULL; + } + + if (res2 == 0) { + res2 = sscanf(buffer, ",\"\",%hu%n", &type, &offset); + if (res2 < 0) { + return NULL; + } + + /* numstr is not matched in second attempt, correct this */ + res2++; + numstr[0] = '\0'; + } + + if (res2 >= 2) { + res += res2; + /* Abort in case offset not set because of format error */ + if (offset == 0) { + APPL_TRACE_ERROR("%s: Format Error %s", __func__, buffer); + return NULL; + } + + buffer += offset; + } + } + + /* Skip any remaing param,as they are not defined by BT HFP spec */ + AT_SKIP_REST(buffer); + AT_CHECK_RN(buffer); + + if (res > 6) { + /* we also have last two optional parameters */ + bta_hf_client_handle_clcc(idx, dir, status, mode, mpty, numstr, type); + } else { + /* we didn't get the last two parameters */ + bta_hf_client_handle_clcc(idx, dir, status, mode, mpty, NULL, 0); + } + + return buffer; +} + +static char *bta_hf_client_parse_cnum(char *buffer) +{ + char numstr[33]; /* spec forces 32 chars, plus one for \0*/ + UINT16 type; + UINT16 service = 0; /* 0 in case this optional parameter is not being sent */ + int res; + int offset; + + AT_CHECK_EVENT(buffer, "+CNUM:"); + + res = sscanf(buffer, ",\"%32[^\"]\",%hu%n,,%hu%n", numstr, &type, &offset, &service, &offset); + if (res < 0) { + return NULL; + } + + if (res == 0) { + res = sscanf(buffer, ",\"\",%hu%n,,%hu%n", &type, &offset, &service, &offset); + if (res < 0) { + return NULL; + } + + /* numstr is not matched in second attempt, correct this */ + res++; + numstr[0] = '\0'; + } + + if (res < 2) { + return NULL; + } + + buffer += offset; + + AT_CHECK_RN(buffer); + + /* service is optional */ + if (res == 2) { + bta_hf_client_handle_cnum(numstr, type, service); + return buffer; + } + + if (service != 4 && service != 5) { + return NULL; + } + + bta_hf_client_handle_cnum(numstr, type, service); + return buffer; +} + +static char *bta_hf_client_parse_btrh(char *buffer) +{ + UINT16 code = 0; + int res; + int offset; + + AT_CHECK_EVENT(buffer, "+BTRH:"); + + res = sscanf(buffer, "%hu%n", &code, &offset); + if (res < 1) { + return NULL; + } + + buffer += offset; + + AT_CHECK_RN(buffer); + + bta_hf_client_handle_btrh(code); + return buffer; +} + +static char *bta_hf_client_parse_busy(char *buffer) +{ + AT_CHECK_EVENT(buffer, "BUSY"); + AT_CHECK_RN(buffer); + + bta_hf_client_handle_error(BTA_HF_CLIENT_AT_RESULT_BUSY, 0); + + return buffer; +} + +static char *bta_hf_client_parse_delayed(char *buffer) +{ + AT_CHECK_EVENT(buffer, "DELAYED"); + AT_CHECK_RN(buffer); + + bta_hf_client_handle_error(BTA_HF_CLIENT_AT_RESULT_DELAY, 0); + + return buffer; +} + +static char *bta_hf_client_parse_no_carrier(char *buffer) +{ + AT_CHECK_EVENT(buffer, "NO CARRIER"); + AT_CHECK_RN(buffer); + + bta_hf_client_handle_error(BTA_HF_CLIENT_AT_RESULT_NO_CARRIER, 0); + + return buffer; +} + +static char *bta_hf_client_parse_no_answer(char *buffer) +{ + AT_CHECK_EVENT(buffer, "NO ANSWER"); + AT_CHECK_RN(buffer); + + bta_hf_client_handle_error(BTA_HF_CLIENT_AT_RESULT_NO_ANSWER, 0); + + return buffer; +} + +static char *bta_hf_client_parse_blacklisted(char *buffer) +{ + AT_CHECK_EVENT(buffer, "BLACKLISTED"); + AT_CHECK_RN(buffer); + + bta_hf_client_handle_error(BTA_HF_CLIENT_AT_RESULT_BLACKLISTED, 0); + + return buffer; +} + +static char *bta_hf_client_skip_unknown(char *buffer) +{ + char *start; + char *tmp; + + tmp = strstr(buffer, "\r\n"); + if (tmp == NULL) { + return NULL; + } + + buffer += 2; + start = buffer; + + tmp = strstr(buffer, "\r\n"); + if (tmp == NULL) { + return NULL; + } + + buffer = tmp + 2; + + APPL_TRACE_DEBUG("%s %.*s", __FUNCTION__, buffer - start - 2, start); + UNUSED(start); + + return buffer; +} + + +/****************************************************************************** +** SUPPORTED EVENT MESSAGES +*******************************************************************************/ + +/* returned values are as follow: + * != NULL && != buf : match and parsed ok + * == NULL : match but parse failed + * != NULL && == buf : no match + */ +typedef char *(*tBTA_HF_CLIENT_PARSER_CALLBACK)(char *); + +static const tBTA_HF_CLIENT_PARSER_CALLBACK bta_hf_client_parser_cb[] = { + bta_hf_client_parse_ok, + bta_hf_client_parse_error, + bta_hf_client_parse_ring, + bta_hf_client_parse_brsf, + bta_hf_client_parse_cind, + bta_hf_client_parse_ciev, + bta_hf_client_parse_chld, + bta_hf_client_parse_bcs, + bta_hf_client_parse_bsir, + bta_hf_client_parse_cmeerror, + bta_hf_client_parse_vgm, + bta_hf_client_parse_vgme, + bta_hf_client_parse_vgs, + bta_hf_client_parse_vgse, + bta_hf_client_parse_bvra, + bta_hf_client_parse_clip, + bta_hf_client_parse_ccwa, + bta_hf_client_parse_cops, + bta_hf_client_parse_binp, + bta_hf_client_parse_clcc, + bta_hf_client_parse_cnum, + bta_hf_client_parse_btrh, + bta_hf_client_parse_busy, + bta_hf_client_parse_delayed, + bta_hf_client_parse_no_carrier, + bta_hf_client_parse_no_answer, + bta_hf_client_parse_blacklisted, + bta_hf_client_skip_unknown +}; + +/* calculate supported event list length */ +static const UINT16 bta_hf_client_psraser_cb_count = + sizeof(bta_hf_client_parser_cb) / sizeof(bta_hf_client_parser_cb[0]); + +#ifdef BTA_HF_CLIENT_AT_DUMP +static void bta_hf_client_dump_at(void) +{ + char dump[(4 * BTA_HF_CLIENT_AT_PARSER_MAX_LEN) + 1]; + char *p1, *p2; + + p1 = bta_hf_client_cb.scb.at_cb.buf; + p2 = dump; + + while (*p1 != '\0') { + if (*p1 == '\r') { + strlcpy(p2, "", 4); + p2 += 4; + } else if (*p1 == '\n') { + strlcpy(p2, "", 4); + p2 += 4; + } else { + *p2 = *p1; + p2++; + } + p1++; + } + + *p2 = '\0'; + + APPL_TRACE_DEBUG("%s %s", __FUNCTION__, dump); +} +#endif + +static void bta_hf_client_at_parse_start(void) +{ + char *buf = bta_hf_client_cb.scb.at_cb.buf; + + APPL_TRACE_DEBUG("%s", __FUNCTION__); + +#ifdef BTA_HF_CLIENT_AT_DUMP + bta_hf_client_dump_at(); +#endif + + while (*buf != '\0') { + int i; + char *tmp = NULL; + + for (i = 0; i < bta_hf_client_psraser_cb_count; i++) { + tmp = bta_hf_client_parser_cb[i](buf); + if (tmp == NULL) { + APPL_TRACE_ERROR("HFPCient: AT event/reply parsing failed, skipping %d", i); + tmp = bta_hf_client_skip_unknown(buf); + break; + } + + /* matched or unknown skipped, if unknown failed tmp is NULL so + this is also handled */ + if (tmp != buf) { + buf = tmp; + break; + } + } + + /* could not skip unknown (received garbage?)... disconnect */ + if (tmp == NULL) { + APPL_TRACE_ERROR("HFPCient: could not skip unknown AT event, disconnecting"); + bta_hf_client_at_reset(); + bta_hf_client_sm_execute(BTA_HF_CLIENT_API_CLOSE_EVT, NULL); + return; + } + + buf = tmp; + } +} + +static BOOLEAN bta_hf_client_check_at_complete(void) +{ + BOOLEAN ret = FALSE; + tBTA_HF_CLIENT_AT_CB *at_cb = &bta_hf_client_cb.scb.at_cb; + + if (at_cb->offset >= BTA_HF_CLIENT_AT_EVENT_MIN_LEN) { + if (at_cb->buf[at_cb->offset - 2] == '\r' && at_cb->buf[at_cb->offset - 1] == '\n') { + ret = TRUE; + } + } + + APPL_TRACE_DEBUG("%s %d", __FUNCTION__, ret); + + return ret; +} + +static void bta_hf_client_at_clear_buf(void) +{ + memset(bta_hf_client_cb.scb.at_cb.buf, 0, sizeof(bta_hf_client_cb.scb.at_cb.buf)); + bta_hf_client_cb.scb.at_cb.offset = 0; +} + +/****************************************************************************** +** +** MAIN PARSING FUNCTION +** +** +*******************************************************************************/ +void bta_hf_client_at_parse(char *buf, unsigned int len) +{ + APPL_TRACE_DEBUG("%s offset: %u len: %u", __FUNCTION__, bta_hf_client_cb.scb.at_cb.offset, len); + + if (len + bta_hf_client_cb.scb.at_cb.offset > BTA_HF_CLIENT_AT_PARSER_MAX_LEN) { + unsigned int tmp = bta_hf_client_cb.scb.at_cb.offset; + unsigned int space_left = BTA_HF_CLIENT_AT_PARSER_MAX_LEN - bta_hf_client_cb.scb.at_cb.offset; + char *tmp_buff = osi_malloc(BTA_HF_CLIENT_AT_PARSER_MAX_LEN); + if (tmp_buff == NULL) { + APPL_TRACE_ERROR("No mem %s", __FUNCTION__); + return; + } + APPL_TRACE_DEBUG("%s overrun, trying to recover", __FUNCTION__); + + /* fill up parser buffer */ + memcpy(bta_hf_client_cb.scb.at_cb.buf + bta_hf_client_cb.scb.at_cb.offset, buf, space_left); + len -= space_left; + buf += space_left; + bta_hf_client_cb.scb.at_cb.offset += space_left; + + /* find end of last complete command before proceeding */ + while (bta_hf_client_check_at_complete() == FALSE) { + if (bta_hf_client_cb.scb.at_cb.offset == 0) { + APPL_TRACE_ERROR("HFPClient: AT parser buffer overrun, disconnecting"); + + bta_hf_client_at_reset(); + bta_hf_client_sm_execute(BTA_HF_CLIENT_API_CLOSE_EVT, NULL); + osi_free(tmp_buff); + return; + } + + bta_hf_client_cb.scb.at_cb.offset--; + } + + /* cut buffer to complete AT event and keep cut data */ + tmp += space_left - bta_hf_client_cb.scb.at_cb.offset; + memcpy(tmp_buff, bta_hf_client_cb.scb.at_cb.buf + bta_hf_client_cb.scb.at_cb.offset, tmp); + bta_hf_client_cb.scb.at_cb.buf[bta_hf_client_cb.scb.at_cb.offset] = '\0'; + + /* parse */ + bta_hf_client_at_parse_start(); + bta_hf_client_at_clear_buf(); + + /* recover cut data */ + memcpy(bta_hf_client_cb.scb.at_cb.buf, tmp_buff, tmp); + bta_hf_client_cb.scb.at_cb.offset += tmp; + + osi_free(tmp_buff); + } + + memcpy(bta_hf_client_cb.scb.at_cb.buf + bta_hf_client_cb.scb.at_cb.offset, buf, len); + bta_hf_client_cb.scb.at_cb.offset += len; + + /* If last event is complete, parsing can be started */ + if (bta_hf_client_check_at_complete() == TRUE) { + bta_hf_client_at_parse_start(); + bta_hf_client_at_clear_buf(); + } +} + +void bta_hf_client_send_at_brsf(void) +{ + char *buf = osi_malloc(BTA_HF_CLIENT_AT_MAX_LEN); + int at_len; + + if (buf == NULL) { + APPL_TRACE_ERROR("No mem %s", __FUNCTION__); + return; + } + + APPL_TRACE_DEBUG("%s", __FUNCTION__); + + at_len = snprintf(buf, BTA_HF_CLIENT_AT_MAX_LEN, "AT+BRSF=%u\r", bta_hf_client_cb.scb.features); + + bta_hf_client_send_at(BTA_HF_CLIENT_AT_BRSF , buf, at_len); + osi_free(buf); +} + +void bta_hf_client_send_at_bac(void) +{ + char *buf; + + APPL_TRACE_DEBUG("%s", __FUNCTION__); + + if (bta_hf_client_cb.msbc_enabled) { + buf = "AT+BAC=1,2\r"; + } else { + buf = "AT+BAC=1\r"; + } + + bta_hf_client_send_at(BTA_HF_CLIENT_AT_BAC, buf, strlen(buf)); +} + +void bta_hf_client_send_at_bcs(UINT32 codec) +{ + char * buf = osi_malloc(BTA_HF_CLIENT_AT_MAX_LEN); + int at_len; + + if (buf == NULL) { + APPL_TRACE_ERROR("No mem %s", __FUNCTION__); + return; + } + + at_len = snprintf(buf, BTA_HF_CLIENT_AT_MAX_LEN, "AT+BCS=%u\r", codec); + + bta_hf_client_send_at(BTA_HF_CLIENT_AT_BCS, buf, at_len); + osi_free(buf); +} + +void bta_hf_client_send_at_cind(BOOLEAN status) +{ + char *buf; + tBTA_HF_CLIENT_AT_CMD cmd; + + APPL_TRACE_DEBUG("%s", __FUNCTION__); + + if (status) { + buf = "AT+CIND?\r"; + cmd = BTA_HF_CLIENT_AT_CIND_STATUS; + } else { + buf = "AT+CIND=?\r"; + cmd = BTA_HF_CLIENT_AT_CIND; + } + + bta_hf_client_send_at(cmd, buf, strlen(buf)); +} + +void bta_hf_client_send_at_cmer(BOOLEAN activate) +{ + char *buf; + + APPL_TRACE_DEBUG("%s", __FUNCTION__); + + if (activate) { + buf = "AT+CMER=3,0,0,1\r"; + } else { + buf = "AT+CMER=3,0,0,0\r"; + } + + bta_hf_client_send_at(BTA_HF_CLIENT_AT_CMER, buf, strlen(buf)); +} + +void bta_hf_client_send_at_chld(char cmd, UINT32 idx) +{ + char *buf = osi_malloc(BTA_HF_CLIENT_AT_MAX_LEN); + int at_len; + + if (buf == NULL) { + APPL_TRACE_ERROR("No mem %s", __FUNCTION__); + return; + } + + if (idx > 0) { + at_len = snprintf(buf, BTA_HF_CLIENT_AT_MAX_LEN, "AT+CHLD=%c%u\r", cmd, idx); + } else { + at_len = snprintf(buf, BTA_HF_CLIENT_AT_MAX_LEN, "AT+CHLD=%c\r", cmd); + } + + bta_hf_client_send_at(BTA_HF_CLIENT_AT_CHLD, buf, at_len); + osi_free(buf); +} + +void bta_hf_client_send_at_clip(BOOLEAN activate) +{ + char *buf; + + APPL_TRACE_DEBUG("%s", __FUNCTION__); + + if (activate) { + buf = "AT+CLIP=1\r"; + } else { + buf = "AT+CLIP=0\r"; + } + + bta_hf_client_send_at(BTA_HF_CLIENT_AT_CLIP, buf, strlen(buf)); +} + +void bta_hf_client_send_at_ccwa(BOOLEAN activate) +{ + char *buf; + + APPL_TRACE_DEBUG("%s", __FUNCTION__); + + if (activate) { + buf = "AT+CCWA=1\r"; + } else { + buf = "AT+CCWA=0\r"; + } + + bta_hf_client_send_at(BTA_HF_CLIENT_AT_CCWA, buf, strlen(buf)); +} + + +void bta_hf_client_send_at_cmee(BOOLEAN activate) +{ + char *buf; + + APPL_TRACE_DEBUG("%s", __FUNCTION__); + + if (activate) { + buf = "AT+CMEE=1\r"; + } else { + buf = "AT+CMEE=0\r"; + } + + bta_hf_client_send_at(BTA_HF_CLIENT_AT_CMEE, buf, strlen(buf)); +} + +void bta_hf_client_send_at_cops(BOOLEAN query) +{ + char *buf; + + APPL_TRACE_DEBUG("%s", __FUNCTION__); + + if (query) { + buf = "AT+COPS?\r"; + } else { + buf = "AT+COPS=3,0\r"; + } + + bta_hf_client_send_at(BTA_HF_CLIENT_AT_COPS, buf, strlen(buf)); +} + +void bta_hf_client_send_at_clcc(void) +{ + char *buf; + + APPL_TRACE_DEBUG("%s", __FUNCTION__); + + buf = "AT+CLCC\r"; + + bta_hf_client_send_at(BTA_HF_CLIENT_AT_CLCC, buf, strlen(buf)); +} + + +void bta_hf_client_send_at_xapl(char *information, UINT32 features) +{ + APPL_TRACE_DEBUG("%s(%s, %u)", __FUNCTION__, information, features); + + char *buf = osi_malloc(BTA_HF_CLIENT_AT_MAX_LEN); + + /* + Format: AT+XAPL=vendorID-productID-version,features + Parameters: + *vendorID: A string representation of the hex value of the vendor ID from the manufacturer, without the 0x prefix. + *productID: A string representation of the hex value of the product ID from the manufacturer, without the 0x prefix. + *version: The revision of the software. + *Fatures: A base-10 representation of a bit field. Available features are: + *Bit 0 = reserved + *Bit 1 = The accessory supports battery reporting (reserved only for battery operated accessories). + *Bit 2 = The accessory is docked or powered (reserved only for battery operated accessories). + *Bit 3 = The accessory supports Siri status reporting. + *Bit 4 = the accessory supports noise reduction (NR) status reporting. + *All other values are reserved. + */ + + snprintf(buf, BTA_HF_CLIENT_AT_MAX_LEN, "AT+XAPL=%s,%u\r", information, features); + + bta_hf_client_send_at(BTA_HF_CLIENT_AT_XAPL, buf, strlen(buf)); + osi_free(buf); +} + +void bta_hf_client_send_at_iphoneaccev(UINT32 bat_level, BOOLEAN docked) +{ + APPL_TRACE_DEBUG("%s(%u, %s)", __FUNCTION__, bat_level, docked ? "docked" : "undocked"); + + char *buf = osi_malloc(BTA_HF_CLIENT_AT_MAX_LEN); + + /* + Format: AT+IPHONEACCEV=Number of key/value pairs,key1,val1,key2,val2,... + Parameters: + * Number of key/value pairs: The number of parameters coming next. + * key: the type of change being reported: + * 1 = Battery Level + * 2 = Dock State + * val: the value of the change: + * Battery Level: string value between '0' and '9' + * Dock State: 0 = undocked, 1 = docked + */ + + snprintf(buf, BTA_HF_CLIENT_AT_MAX_LEN, "AT+IPHONEACCEV=2,1,%u,2,%u\r", bat_level, docked ? 1 : 0); + + bta_hf_client_send_at(BTA_HF_CLIENT_AT_IPHONEACCEV, buf, strlen(buf)); + osi_free(buf); +} + +void bta_hf_client_send_at_bvra(BOOLEAN enable) +{ + char *buf; + + APPL_TRACE_DEBUG("%s", __FUNCTION__); + + if (enable) { + buf = "AT+BVRA=1\r"; + } else { + buf = "AT+BVRA=0\r"; + } + + bta_hf_client_send_at(BTA_HF_CLIENT_AT_BVRA, buf, strlen(buf)); +} + +void bta_hf_client_send_at_vgs(UINT32 volume) +{ + char *buf = osi_malloc(BTA_HF_CLIENT_AT_MAX_LEN); + int at_len; + if (buf == NULL) { + APPL_TRACE_ERROR("No mem %s", __FUNCTION__); + return; + } + APPL_TRACE_DEBUG("%s", __FUNCTION__); + + at_len = snprintf(buf, BTA_HF_CLIENT_AT_MAX_LEN, "AT+VGS=%u\r", volume); + + bta_hf_client_send_at(BTA_HF_CLIENT_AT_VGS, buf, at_len); + osi_free(buf); +} + +void bta_hf_client_send_at_vgm(UINT32 volume) +{ + char *buf = osi_malloc(BTA_HF_CLIENT_AT_MAX_LEN); + int at_len; + + if (buf == NULL) { + APPL_TRACE_ERROR("No mem %s", __FUNCTION__); + return; + } + APPL_TRACE_DEBUG("%s", __FUNCTION__); + + at_len = snprintf(buf, BTA_HF_CLIENT_AT_MAX_LEN, "AT+VGM=%u\r", volume); + + bta_hf_client_send_at(BTA_HF_CLIENT_AT_VGM, buf, at_len); + osi_free(buf); +} + +void bta_hf_client_send_at_atd(char *number, UINT32 memory) +{ + char *buf = osi_malloc(BTA_HF_CLIENT_AT_MAX_LEN); + int at_len; + + if (buf == NULL) { + APPL_TRACE_ERROR("No mem %s", __FUNCTION__); + return; + } + APPL_TRACE_DEBUG("%s", __FUNCTION__); + + if (number[0] != '\0') { + at_len = snprintf(buf, BTA_HF_CLIENT_AT_MAX_LEN, "ATD%s;\r", number); + } else { + at_len = snprintf(buf, BTA_HF_CLIENT_AT_MAX_LEN, "ATD>%u;\r", memory); + } + + at_len = MIN(at_len, BTA_HF_CLIENT_AT_MAX_LEN); + + bta_hf_client_send_at(BTA_HF_CLIENT_AT_ATD, buf, at_len); + osi_free(buf); +} + +void bta_hf_client_send_at_bldn(void) +{ + char *buf; + + APPL_TRACE_DEBUG("%s", __FUNCTION__); + + buf = "AT+BLDN\r"; + + bta_hf_client_send_at(BTA_HF_CLIENT_AT_BLDN, buf, strlen(buf)); +} + +void bta_hf_client_send_at_ata(void) +{ + char *buf; + + APPL_TRACE_DEBUG("%s", __FUNCTION__); + + buf = "ATA\r"; + + bta_hf_client_send_at(BTA_HF_CLIENT_AT_ATA, buf, strlen(buf)); +} + +void bta_hf_client_send_at_chup(void) +{ + char *buf; + + APPL_TRACE_DEBUG("%s", __FUNCTION__); + + buf = "AT+CHUP\r"; + + bta_hf_client_send_at(BTA_HF_CLIENT_AT_CHUP, buf, strlen(buf)); +} + +void bta_hf_client_send_at_btrh(BOOLEAN query, UINT32 val) +{ + char *buf = osi_malloc(BTA_HF_CLIENT_AT_MAX_LEN); + int at_len; + + if (buf == NULL) { + APPL_TRACE_ERROR("No mem %s", __FUNCTION__); + return; + } + APPL_TRACE_DEBUG("%s", __FUNCTION__); + + if (query == TRUE) { + at_len = snprintf(buf, BTA_HF_CLIENT_AT_MAX_LEN, "AT+BTRH?\r"); + } else { + at_len = snprintf(buf, BTA_HF_CLIENT_AT_MAX_LEN, "AT+BTRH=%u\r", val); + } + + bta_hf_client_send_at(BTA_HF_CLIENT_AT_BTRH, buf, at_len); + osi_free(buf); +} + +void bta_hf_client_send_at_vts(char code) +{ + char *buf = osi_malloc(BTA_HF_CLIENT_AT_MAX_LEN); + int at_len; + if (buf == NULL) { + APPL_TRACE_ERROR("No mem %s", __FUNCTION__); + return; + } + APPL_TRACE_DEBUG("%s", __FUNCTION__); + + at_len = snprintf(buf, BTA_HF_CLIENT_AT_MAX_LEN, "AT+VTS=%c\r", code); + + bta_hf_client_send_at(BTA_HF_CLIENT_AT_VTS, buf, at_len); + osi_free(buf); +} + +void bta_hf_client_send_at_bcc(void) +{ + char *buf; + + APPL_TRACE_DEBUG("%s", __FUNCTION__); + + buf = "AT+BCC\r"; + + bta_hf_client_send_at(BTA_HF_CLIENT_AT_BCC, buf, strlen(buf)); +} + +void bta_hf_client_send_at_cnum(void) +{ + char *buf; + + APPL_TRACE_DEBUG("%s", __FUNCTION__); + + buf = "AT+CNUM\r"; + + bta_hf_client_send_at(BTA_HF_CLIENT_AT_CNUM, buf, strlen(buf)); +} + +void bta_hf_client_send_at_nrec(void) +{ + char *buf; + + APPL_TRACE_DEBUG("%s", __FUNCTION__); + + if (!(bta_hf_client_cb.scb.peer_features & BTA_HF_CLIENT_PEER_FEAT_ECNR)) { + APPL_TRACE_ERROR("%s: Remote does not support NREC.", __FUNCTION__); + return; + } + + buf = "AT+NREC=0\r"; + + bta_hf_client_send_at(BTA_HF_CLIENT_AT_NREC, buf, strlen(buf)); +} + +void bta_hf_client_send_at_binp(UINT32 action) +{ + char *buf = osi_malloc(BTA_HF_CLIENT_AT_MAX_LEN); + int at_len; + + if (buf == NULL) { + APPL_TRACE_ERROR("No mem %s", __FUNCTION__); + return; + } + + at_len = snprintf(buf, BTA_HF_CLIENT_AT_MAX_LEN, "AT+BINP=%u\r", action); + + bta_hf_client_send_at(BTA_HF_CLIENT_AT_BINP, buf, at_len); + osi_free(buf); +} + +void bta_hf_client_send_at_bia(void) +{ + char *buf; + int at_len; + int i; + + APPL_TRACE_DEBUG("%s", __FUNCTION__); + if (bta_hf_client_cb.scb.peer_version < HFP_VERSION_1_6) { + APPL_TRACE_DEBUG("Remote does not Support AT+BIA"); + return; + } + + buf = osi_malloc(BTA_HF_CLIENT_AT_MAX_LEN); + if (buf == NULL) { + APPL_TRACE_ERROR("No mem %s", __FUNCTION__); + return; + } + at_len = snprintf(buf, BTA_HF_CLIENT_AT_MAX_LEN, "AT+BIA="); + + for (i = 0; i < BTA_HF_CLIENT_AT_INDICATOR_COUNT; i++) { + int sup = bta_hf_client_cb.scb.at_cb.indicator_lookup[i] == -1 ? 0 : 1; + + at_len += snprintf(buf + at_len, BTA_HF_CLIENT_AT_MAX_LEN - at_len, "%u,", sup); + } + + buf[at_len - 1] = '\r'; + + bta_hf_client_send_at(BTA_HF_CLIENT_AT_BIA, buf, at_len); + osi_free(buf); +} + +void bta_hf_client_at_init(void) +{ + memset(&bta_hf_client_cb.scb.at_cb, 0, sizeof(tBTA_HF_CLIENT_AT_CB)); + bta_hf_client_at_reset(); +} + +void bta_hf_client_at_reset(void) +{ + int i; + + bta_hf_client_free_at_resp_timer(); + bta_hf_client_free_at_hold_timer(); + + bta_hf_client_clear_queued_at(); + + bta_hf_client_at_clear_buf(); + + for (i = 0; i < BTA_HF_CLIENT_AT_INDICATOR_COUNT; i++) { + bta_hf_client_cb.scb.at_cb.indicator_lookup[i] = -1; + } + + bta_hf_client_cb.scb.at_cb.current_cmd = BTA_HF_CLIENT_AT_NONE; +} +#endif /* #if (BTA_HF_INCLUDED == TRUE) */ diff --git a/lib/bt/host/bluedroid/bta/hf_client/bta_hf_client_cmd.c b/lib/bt/host/bluedroid/bta/hf_client/bta_hf_client_cmd.c new file mode 100644 index 00000000..08e321d3 --- /dev/null +++ b/lib/bt/host/bluedroid/bta/hf_client/bta_hf_client_cmd.c @@ -0,0 +1,91 @@ +/****************************************************************************** + * + * Copyright (c) 2014 The Android Open Source Project + * Copyright (C) 2003-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +#include "bta_hf_client_int.h" +#include "stdio.h" +#include "common/bt_target.h" + +#if (BTA_HF_INCLUDED == TRUE) + +void bta_hf_client_send_at_cmd(tBTA_HF_CLIENT_DATA *p_data) +{ + tBTA_HF_CLIENT_DATA_VAL *p_val = (tBTA_HF_CLIENT_DATA_VAL *)p_data; + + switch (p_val->uint8_val) { + case BTA_HF_CLIENT_AT_CMD_VTS: + bta_hf_client_send_at_vts((char)p_val->uint32_val1); + break; + case BTA_HF_CLIENT_AT_CMD_BTRH: + bta_hf_client_send_at_btrh(FALSE, p_val->uint32_val1); + break; + case BTA_HF_CLIENT_AT_CMD_CHUP: + bta_hf_client_send_at_chup(); + break; + case BTA_HF_CLIENT_AT_CMD_CHLD: + /* expects ascii code for command */ + bta_hf_client_send_at_chld('0' + p_val->uint32_val1, p_val->uint32_val2); + break; + case BTA_HF_CLIENT_AT_CMD_BCC: + bta_hf_client_send_at_bcc(); + break; + case BTA_HF_CLIENT_AT_CMD_CNUM: + bta_hf_client_send_at_cnum(); + break; + case BTA_HF_CLIENT_AT_CMD_ATA: + bta_hf_client_send_at_ata(); + break; + case BTA_HF_CLIENT_AT_CMD_COPS: + bta_hf_client_send_at_cops(TRUE); + break; + case BTA_HF_CLIENT_AT_CMD_ATD: + bta_hf_client_send_at_atd(p_val->str, p_val->uint32_val1); + break; + case BTA_HF_CLIENT_AT_CMD_VGM: + bta_hf_client_send_at_vgm(p_val->uint32_val1); + break; + case BTA_HF_CLIENT_AT_CMD_VGS: + bta_hf_client_send_at_vgs(p_val->uint32_val1); + break; + case BTA_HF_CLIENT_AT_CMD_BVRA: + bta_hf_client_send_at_bvra(p_val->uint32_val1 == 0 ? FALSE : TRUE); + break; + case BTA_HF_CLIENT_AT_CMD_CLCC: + bta_hf_client_send_at_clcc(); + break; + case BTA_HF_CLIENT_AT_CMD_BINP: + bta_hf_client_send_at_binp(p_val->uint32_val1); + break; + case BTA_HF_CLIENT_AT_CMD_BLDN: + bta_hf_client_send_at_bldn(); + break; + case BTA_HF_CLIENT_AT_CMD_NREC: + bta_hf_client_send_at_nrec(); + break; + case BTA_HF_CLIENT_AT_CMD_XAPL: + bta_hf_client_send_at_xapl(p_val->str, p_val->uint32_val1); + break; + case BTA_HF_CLIENT_AT_CMD_IPHONEACCEV: + bta_hf_client_send_at_iphoneaccev(p_val->uint32_val1, p_val->uint32_val1 == 0 ? FALSE : TRUE); + break; + default: + APPL_TRACE_ERROR("Default case, %s", __FUNCTION__); + break; + } +} +#endif /* #if (BTA_HF_INCLUDED == TRUE) */ diff --git a/lib/bt/host/bluedroid/bta/hf_client/bta_hf_client_main.c b/lib/bt/host/bluedroid/bta/hf_client/bta_hf_client_main.c new file mode 100644 index 00000000..4124a10f --- /dev/null +++ b/lib/bt/host/bluedroid/bta/hf_client/bta_hf_client_main.c @@ -0,0 +1,693 @@ +/****************************************************************************** + * + * Copyright (c) 2014 The Android Open Source Project + * Copyright (C) 2003-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +#include +#include +#include "common/bt_defs.h" +#include "bta/bta_api.h" +#include "bta/bta_sys.h" +#include "bta/bta_hf_client_api.h" +#include "bta_hf_client_int.h" + +#if BT_HF_CLIENT_BQB_INCLUDED +static BOOLEAN s_bta_hf_client_bqb_clip_flag = TRUE; +#endif /* BT_HF_CLIENT_BQB_INCLUDED */ + +#if (BTA_HF_INCLUDED == TRUE) +/* uncomment to enable extra debug */ +/* #define BTA_HF_CLIENT_DEBUG TRUE */ + +#ifndef BTA_HF_CLIENT_DEBUG +#define BTA_HF_CLIENT_DEBUG FALSE +#endif + +#if BTA_HF_CLIENT_DEBUG == TRUE +static char *bta_hf_client_evt_str(UINT16 event); +static char *bta_hf_client_state_str(UINT8 state); +#endif + +/* state machine states */ +enum { + BTA_HF_CLIENT_INIT_ST, + BTA_HF_CLIENT_OPENING_ST, + BTA_HF_CLIENT_OPEN_ST, + BTA_HF_CLIENT_CLOSING_ST +}; + +/* state machine action enumeration list */ +enum { + BTA_HF_CLIENT_REGISTER, + BTA_HF_CLIENT_DEREGISTER, + BTA_HF_CLIENT_START_DEREG, + BTA_HF_CLIENT_RFC_DO_CLOSE, + BTA_HF_CLIENT_START_CLOSE, + BTA_HF_CLIENT_START_OPEN, + BTA_HF_CLIENT_RFC_ACP_OPEN, + BTA_HF_CLIENT_SCO_LISTEN, + BTA_HF_CLIENT_SCO_CONN_OPEN, + BTA_HF_CLIENT_SCO_CONN_CLOSE, + BTA_HF_CLIENT_SCO_OPEN, + BTA_HF_CLIENT_SCO_CLOSE, + BTA_HF_CLIENT_SCO_SHUTDOWN, + BTA_HF_CLIENT_FREE_DB, + BTA_HF_CLIENT_OPEN_FAIL, + BTA_HF_CLIENT_RFC_OPEN, + BTA_HF_CLIENT_RFC_FAIL, + BTA_HF_CLIENT_DISC_INT_RES, + BTA_HF_CLIENT_RFC_DO_OPEN, + BTA_HF_CLIENT_DISC_FAIL, + BTA_HF_CLIENT_RFC_CLOSE, + BTA_HF_CLIENT_RFC_DATA, + BTA_HF_CLIENT_DISC_ACP_RES, + BTA_HF_CLIENT_SVC_CONN_OPEN, + BTA_HF_CLIENT_SEND_AT_CMD, +#if (BTM_SCO_HCI_INCLUDED == TRUE) + BTA_HF_CLIENT_CI_SCO_DATA, + BTA_HF_CLIENT_PKT_STAT_NUMS, +#endif + BTA_HF_CLIENT_NUM_ACTIONS, +}; + +#define BTA_HF_CLIENT_IGNORE BTA_HF_CLIENT_NUM_ACTIONS + +/* type for action functions */ +typedef void (*tBTA_HF_CLIENT_ACTION)(tBTA_HF_CLIENT_DATA *p_data); + +/* action functions table, indexed with action enum */ +const tBTA_HF_CLIENT_ACTION bta_hf_client_action[] = { + /* BTA_HF_CLIENT_REGISTER */ bta_hf_client_register, + /* BTA_HF_CLIENT_DEREGISTER */ bta_hf_client_deregister, + /* BTA_HF_CLIENT_START_DEREG */ bta_hf_client_start_dereg, + /* BTA_HF_CLIENT_RFC_DO_CLOSE */ bta_hf_client_rfc_do_close, + /* BTA_HF_CLIENT_START_CLOSE */ bta_hf_client_start_close, + /* BTA_HF_CLIENT_START_OPEN */ bta_hf_client_start_open, + /* BTA_HF_CLIENT_RFC_ACP_OPEN */ bta_hf_client_rfc_acp_open, + /* BTA_HF_CLIENT_SCO_LISTEN */ bta_hf_client_sco_listen, + /* BTA_HF_CLIENT_SCO_CONN_OPEN */ bta_hf_client_sco_conn_open, + /* BTA_HF_CLIENT_SCO_CONN_CLOSE*/ bta_hf_client_sco_conn_close, + /* BTA_HF_CLIENT_SCO_OPEN */ bta_hf_client_sco_open, + /* BTA_HF_CLIENT_SCO_CLOSE */ bta_hf_client_sco_close, + /* BTA_HF_CLIENT_SCO_SHUTDOWN */ bta_hf_client_sco_shutdown, + /* BTA_HF_CLIENT_FREE_DB */ bta_hf_client_free_db, + /* BTA_HF_CLIENT_OPEN_FAIL */ bta_hf_client_open_fail, + /* BTA_HF_CLIENT_RFC_OPEN */ bta_hf_client_rfc_open, + /* BTA_HF_CLIENT_RFC_FAIL */ bta_hf_client_rfc_fail, + /* BTA_HF_CLIENT_DISC_INT_RES */ bta_hf_client_disc_int_res, + /* BTA_HF_CLIENT_RFC_DO_OPEN */ bta_hf_client_rfc_do_open, + /* BTA_HF_CLIENT_DISC_FAIL */ bta_hf_client_disc_fail, + /* BTA_HF_CLIENT_RFC_CLOSE */ bta_hf_client_rfc_close, + /* BTA_HF_CLIENT_RFC_DATA */ bta_hf_client_rfc_data, + /* BTA_HF_CLIENT_DISC_ACP_RES */ bta_hf_client_disc_acp_res, + /* BTA_HF_CLIENT_SVC_CONN_OPEN */ bta_hf_client_svc_conn_open, + /* BTA_HF_CLIENT_SEND_AT_CMD */ bta_hf_client_send_at_cmd, +#if (BTM_SCO_HCI_INCLUDED == TRUE) + /* BTA_HF_CLIENT_CI_SCO_DATA */ bta_hf_client_ci_sco_data, + /* BTA_HF_CLIENT_PKT_STAT_NUMS */ bta_hf_client_pkt_stat_nums, +#endif /* (BTM_SCO_HCI_INCLUDED == TRUE ) */ +}; + +/* state table information */ +#define BTA_HF_CLIENT_ACTIONS 2 /* number of actions */ +#define BTA_HF_CLIENT_NEXT_STATE 2 /* position of next state */ +#define BTA_HF_CLIENT_NUM_COLS 3 /* number of columns in state tables */ + +/* state table for init state */ +const UINT8 bta_hf_client_st_init[][BTA_HF_CLIENT_NUM_COLS] = { + /* Event Action 1 Action 2 Next state */ + /* API_REGISTER_EVT */ {BTA_HF_CLIENT_REGISTER, BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_INIT_ST}, + /* API_DEREGISTER_EVT */ {BTA_HF_CLIENT_DEREGISTER, BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_INIT_ST}, + /* API_OPEN_EVT */ {BTA_HF_CLIENT_START_OPEN, BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_OPENING_ST}, + /* API_CLOSE_EVT */ {BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_INIT_ST}, + /* API_AUDIO_OPEN_EVT */ {BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_INIT_ST}, + /* API_AUDIO_CLOSE_EVT */ {BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_INIT_ST}, + /* RFC_OPEN_EVT */ {BTA_HF_CLIENT_RFC_ACP_OPEN, BTA_HF_CLIENT_SCO_LISTEN, BTA_HF_CLIENT_OPEN_ST}, + /* RFC_CLOSE_EVT */ {BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_INIT_ST}, + /* RFC_SRV_CLOSE_EVT */ {BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_INIT_ST}, + /* RFC_DATA_EVT */ {BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_INIT_ST}, + /* DISC_ACP_RES_EVT */ {BTA_HF_CLIENT_FREE_DB, BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_INIT_ST}, + /* DISC_INT_RES_EVT */ {BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_INIT_ST}, + /* DISC_OK_EVT */ {BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_INIT_ST}, + /* DISC_FAIL_EVT */ {BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_INIT_ST}, + /* SCO_OPEN_EVT */ {BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_INIT_ST}, + /* SCO_CLOSE_EVT */ {BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_INIT_ST}, + /* SEND_AT_CMD_EVT */ {BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_INIT_ST}, +#if (BTM_SCO_HCI_INCLUDED == TRUE ) + /* CI_SCO_DATA_EVT */ {BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_INIT_ST}, + /* PKT_STAT_NUMS_GET_EVT */ {BTA_HF_CLIENT_PKT_STAT_NUMS, BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_INIT_ST}, +#endif /* (BTM_SCO_HCI_INCLUDED == TRUE ) */ +}; + +/* state table for opening state */ +const UINT8 bta_hf_client_st_opening[][BTA_HF_CLIENT_NUM_COLS] = { + /* Event Action 1 Action 2 Next state */ + /* API_REGISTER_EVT */ {BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_OPENING_ST}, + /* API_DEREGISTER_EVT */ {BTA_HF_CLIENT_RFC_DO_CLOSE, BTA_HF_CLIENT_START_DEREG, BTA_HF_CLIENT_CLOSING_ST}, + /* API_OPEN_EVT */ {BTA_HF_CLIENT_OPEN_FAIL, BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_OPENING_ST}, + /* API_CLOSE_EVT */ {BTA_HF_CLIENT_RFC_DO_CLOSE, BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_CLOSING_ST}, + /* API_AUDIO_OPEN_EVT */ {BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_OPENING_ST}, + /* API_AUDIO_CLOSE_EVT */ {BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_OPENING_ST}, + /* RFC_OPEN_EVT */ {BTA_HF_CLIENT_RFC_OPEN, BTA_HF_CLIENT_SCO_LISTEN, BTA_HF_CLIENT_OPEN_ST}, + /* RFC_CLOSE_EVT */ {BTA_HF_CLIENT_RFC_FAIL, BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_INIT_ST}, + /* RFC_SRV_CLOSE_EVT */ {BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_OPENING_ST}, + /* RFC_DATA_EVT */ {BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_OPENING_ST}, + /* DISC_ACP_RES_EVT */ {BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_OPENING_ST}, + /* DISC_INT_RES_EVT */ {BTA_HF_CLIENT_DISC_INT_RES, BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_OPENING_ST}, + /* DISC_OK_EVT */ {BTA_HF_CLIENT_RFC_DO_OPEN, BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_OPENING_ST}, + /* DISC_FAIL_EVT */ {BTA_HF_CLIENT_DISC_FAIL, BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_INIT_ST}, + /* SCO_OPEN_EVT */ {BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_OPENING_ST}, + /* SCO_CLOSE_EVT */ {BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_OPENING_ST}, + /* SEND_AT_CMD_EVT */ {BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_OPENING_ST}, +#if (BTM_SCO_HCI_INCLUDED == TRUE) + /* CI_SCO_DATA_EVT */ {BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_OPENING_ST}, + /* PKT_STAT_NUMS_GET_EVT */ {BTA_HF_CLIENT_PKT_STAT_NUMS, BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_OPENING_ST}, +#endif /* (BTM_SCO_HCI_INCLUDED == TRUE ) */ +}; + +/* state table for open state */ +const UINT8 bta_hf_client_st_open[][BTA_HF_CLIENT_NUM_COLS] = { + /* Event Action 1 Action 2 Next state */ + /* API_REGISTER_EVT */ {BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_OPEN_ST}, + /* API_DEREGISTER_EVT */ {BTA_HF_CLIENT_START_CLOSE, BTA_HF_CLIENT_START_DEREG, BTA_HF_CLIENT_CLOSING_ST}, + /* API_OPEN_EVT */ {BTA_HF_CLIENT_OPEN_FAIL, BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_OPEN_ST}, + /* API_CLOSE_EVT */ {BTA_HF_CLIENT_START_CLOSE, BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_CLOSING_ST}, + /* API_AUDIO_OPEN_EVT */ {BTA_HF_CLIENT_SCO_OPEN, BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_OPEN_ST}, + /* API_AUDIO_CLOSE_EVT */ {BTA_HF_CLIENT_SCO_CLOSE, BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_OPEN_ST}, + /* RFC_OPEN_EVT */ {BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_OPEN_ST}, + /* RFC_CLOSE_EVT */ {BTA_HF_CLIENT_RFC_CLOSE, BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_INIT_ST}, + /* RFC_SRV_CLOSE_EVT */ {BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_OPEN_ST}, + /* RFC_DATA_EVT */ {BTA_HF_CLIENT_RFC_DATA, BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_OPEN_ST}, + /* DISC_ACP_RES_EVT */ {BTA_HF_CLIENT_DISC_ACP_RES, BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_OPEN_ST}, + /* DISC_INT_RES_EVT */ {BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_OPEN_ST}, + /* DISC_OK_EVT */ {BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_OPEN_ST}, + /* DISC_FAIL_EVT */ {BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_OPEN_ST}, + /* SCO_OPEN_EVT */ {BTA_HF_CLIENT_SCO_CONN_OPEN, BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_OPEN_ST}, + /* SCO_CLOSE_EVT */ {BTA_HF_CLIENT_SCO_CONN_CLOSE, BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_OPEN_ST}, + /* SEND_AT_CMD_EVT */ {BTA_HF_CLIENT_SEND_AT_CMD, BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_OPEN_ST}, +#if (BTM_SCO_HCI_INCLUDED == TRUE ) + /* CI_SCO_DATA_EVT */ {BTA_HF_CLIENT_CI_SCO_DATA, BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_OPEN_ST}, + /* PKT_STAT_NUMS_GET_EVT */ {BTA_HF_CLIENT_PKT_STAT_NUMS, BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_OPEN_ST}, +#endif /* (BTM_SCO_HCI_INCLUDED == TRUE ) */ +}; + +/* state table for closing state */ +const UINT8 bta_hf_client_st_closing[][BTA_HF_CLIENT_NUM_COLS] = { + /* Event Action 1 Action 2 Next state */ + /* API_REGISTER_EVT */ {BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_CLOSING_ST}, + /* API_DEREGISTER_EVT */ {BTA_HF_CLIENT_START_DEREG, BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_CLOSING_ST}, + /* API_OPEN_EVT */ {BTA_HF_CLIENT_OPEN_FAIL, BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_CLOSING_ST}, + /* API_CLOSE_EVT */ {BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_CLOSING_ST}, + /* API_AUDIO_OPEN_EVT */ {BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_CLOSING_ST}, + /* API_AUDIO_CLOSE_EVT */ {BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_CLOSING_ST}, + /* RFC_OPEN_EVT */ {BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_CLOSING_ST}, + /* RFC_CLOSE_EVT */ {BTA_HF_CLIENT_RFC_CLOSE, BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_INIT_ST}, + /* RFC_SRV_CLOSE_EVT */ {BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_CLOSING_ST}, + /* RFC_DATA_EVT */ {BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_CLOSING_ST}, + /* DISC_ACP_RES_EVT */ {BTA_HF_CLIENT_FREE_DB, BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_CLOSING_ST}, + /* DISC_INT_RES_EVT */ {BTA_HF_CLIENT_FREE_DB, BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_INIT_ST}, + /* DISC_OK_EVT */ {BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_CLOSING_ST}, + /* DISC_FAIL_EVT */ {BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_CLOSING_ST}, + /* SCO_OPEN_EVT */ {BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_CLOSING_ST}, + /* SCO_CLOSE_EVT */ {BTA_HF_CLIENT_SCO_CONN_CLOSE, BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_CLOSING_ST}, + /* SEND_AT_CMD_EVT */ {BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_CLOSING_ST}, +#if (BTM_SCO_HCI_INCLUDED == TRUE ) + /* CI_SCO_DATA_EVT */ {BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_CLOSING_ST}, + /* PKT_STAT_NUMS_GET_EVT */ {BTA_HF_CLIENT_PKT_STAT_NUMS, BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_CLOSING_ST}, +#endif /* (BTM_SCO_HCI_INCLUDED == TRUE ) */ +}; + +/* type for state table */ +typedef const UINT8 (*tBTA_HF_CLIENT_ST_TBL)[BTA_HF_CLIENT_NUM_COLS]; + +/* state table */ +const tBTA_HF_CLIENT_ST_TBL bta_hf_client_st_tbl[] = { + bta_hf_client_st_init, + bta_hf_client_st_opening, + bta_hf_client_st_open, + bta_hf_client_st_closing +}; + +const int bta_hf_client_version = HFP_HF_VERSION_1_7; + +/* HF Client control block */ +#if BTA_DYNAMIC_MEMORY == FALSE +tBTA_HF_CLIENT_CB bta_hf_client_cb; +#else +tBTA_HF_CLIENT_CB *bta_hf_client_cb_ptr; +#endif + +/******************************************************************************* +** +** Function bta_hf_client_bqb_clip_ctrl +** +** Description Control if send the command AT+CLIP for BQB test +** +** Returns void +** +*******************************************************************************/ +#if BT_HF_CLIENT_BQB_INCLUDED +void bta_hf_client_bqb_clip_ctrl(BOOLEAN enable) +{ + s_bta_hf_client_bqb_clip_flag = enable; +} +#endif /* BT_HF_CLIENT_BQB_INCLUDED */ + +/******************************************************************************* +** +** Function bta_hf_client_scb_init +** +** Description Initialize an HF_Client service control block. +** +** +** Returns void +** +*******************************************************************************/ +void bta_hf_client_scb_init(void) +{ + APPL_TRACE_DEBUG("%s", __FUNCTION__); + + memset(&bta_hf_client_cb.scb, 0, sizeof(tBTA_HF_CLIENT_SCB)); + bta_hf_client_cb.scb.sco_idx = BTM_INVALID_SCO_INDEX; + bta_hf_client_cb.scb.negotiated_codec = BTM_SCO_CODEC_CVSD; +} + +/******************************************************************************* +** +** Function bta_hf_client_scb_disable +** +** Description Disable a service control block. +** +** +** Returns void +** +*******************************************************************************/ +void bta_hf_client_scb_disable(void) +{ + APPL_TRACE_DEBUG("%s", __FUNCTION__); + + bta_hf_client_scb_init(); + + (*bta_hf_client_cb.p_cback)(BTA_HF_CLIENT_DISABLE_EVT, NULL); +} + +/******************************************************************************* +** +** Function bta_hf_client_resume_open +** +** Description Resume opening process. +** +** +** Returns void +** +*******************************************************************************/ +void bta_hf_client_resume_open (void) +{ + APPL_TRACE_DEBUG ("%s", __FUNCTION__); + + /* resume opening process. */ + if (bta_hf_client_cb.scb.state == BTA_HF_CLIENT_INIT_ST) { + bta_hf_client_cb.scb.state = BTA_HF_CLIENT_OPENING_ST; + bta_hf_client_start_open (NULL); + } +} + +/******************************************************************************* +** +** Function bta_hf_client_colli_timer_cback +** +** Description HF Client connection collision timer callback +** +** +** Returns void +** +*******************************************************************************/ +static void bta_hf_client_colli_timer_cback (TIMER_LIST_ENT *p_tle) +{ + APPL_TRACE_DEBUG("%s", __FUNCTION__); + + if (p_tle) { + bta_hf_client_cb.scb.colli_tmr_on = FALSE; + + /* If the peer haven't opened connection, restart opening process */ + bta_hf_client_resume_open (); + } +} + +/******************************************************************************* +** +** Function bta_hf_client_collision_cback +** +** Description Get notified about collision. +** +** +** Returns void +** +*******************************************************************************/ +void bta_hf_client_collision_cback (tBTA_SYS_CONN_STATUS status, UINT8 id, + UINT8 app_id, BD_ADDR peer_addr) +{ + UNUSED(status); + UNUSED(app_id); + UNUSED(peer_addr); + + if (bta_hf_client_cb.scb.state == BTA_HF_CLIENT_OPENING_ST) { + if (id == BTA_ID_SYS) { /* ACL collision */ + APPL_TRACE_WARNING ("HF Client found collision (ACL) ..."); + } else if (id == BTA_ID_HS) { /* RFCOMM collision */ + APPL_TRACE_WARNING ("HF Client found collision (RFCOMM) ..."); + } else { + APPL_TRACE_WARNING ("HF Client found collision (\?\?\?) ..."); + } + + bta_hf_client_cb.scb.state = BTA_HF_CLIENT_INIT_ST; + + /* Cancel SDP if it had been started. */ + if (bta_hf_client_cb.scb.p_disc_db) { + (void)SDP_CancelServiceSearch (bta_hf_client_cb.scb.p_disc_db); + bta_hf_client_free_db(NULL); + } + + /* reopen registered server */ + /* Collision may be detected before or after we close servers. */ + bta_hf_client_start_server(); + + /* Start timer to handle connection opening restart */ + bta_hf_client_cb.scb.colli_timer.p_cback = (TIMER_CBACK *)&bta_hf_client_colli_timer_cback; + bta_sys_start_timer(&bta_hf_client_cb.scb.colli_timer, 0, BTA_HF_CLIENT_COLLISION_TIMER); + bta_hf_client_cb.scb.colli_tmr_on = TRUE; + } +} + +/******************************************************************************* +** +** Function bta_hf_client_api_enable +** +** Description Handle an API enable event. +** +** +** Returns void +** +*******************************************************************************/ +static void bta_hf_client_api_enable(tBTA_HF_CLIENT_DATA *p_data) +{ + /* initialize control block */ + memset(&bta_hf_client_cb, 0, sizeof(tBTA_HF_CLIENT_CB)); + + /* store callback function */ + bta_hf_client_cb.p_cback = p_data->api_enable.p_cback; + + /* check if mSBC support enabled */ + if (bta_hf_client_version >= HFP_HF_VERSION_1_6) { + bta_hf_client_cb.msbc_enabled = TRUE; + } else{ + bta_hf_client_cb.msbc_enabled = FALSE; + } + + bta_hf_client_cb.scb.negotiated_codec = BTM_SCO_CODEC_CVSD; + + /* set same setting as AG does */ + BTM_WriteVoiceSettings(AG_VOICE_SETTINGS); + + bta_sys_collision_register (BTA_ID_HS, bta_hf_client_collision_cback); + + /* call callback with enable event */ + (*bta_hf_client_cb.p_cback)(BTA_HF_CLIENT_ENABLE_EVT, NULL); +} + +/******************************************************************************* +** +** Function bta_hf_client_api_disable +** +** Description Handle an API disable event. +** +** +** Returns void +** +*******************************************************************************/ +static void bta_hf_client_api_disable(tBTA_HF_CLIENT_DATA *p_data) +{ + if (!bta_sys_is_register (BTA_ID_HS)) { + APPL_TRACE_ERROR("BTA HF Client is already disabled, ignoring ..."); + return; + } + + /* De-register with BTA system manager */ + bta_sys_deregister(BTA_ID_HS); + + bta_hf_client_sm_execute(BTA_HF_CLIENT_API_DEREGISTER_EVT, p_data); + + bta_sys_collision_register (BTA_ID_HS, NULL); +} + +/******************************************************************************* +** +** Function bta_hf_client_hdl_event +** +** Description Data HF Client main event handling function. +** +** +** Returns BOOLEAN +** +*******************************************************************************/ +BOOLEAN bta_hf_client_hdl_event(BT_HDR *p_msg) +{ +#if BTA_HF_CLIENT_DEBUG == TRUE + APPL_TRACE_DEBUG("bta_hf_client_hdl_event %s (0x%x)", bta_hf_client_evt_str(p_msg->event), p_msg->event); +#endif + + switch (p_msg->event) { + /* handle enable event */ + case BTA_HF_CLIENT_API_ENABLE_EVT: + bta_hf_client_api_enable((tBTA_HF_CLIENT_DATA *) p_msg); + break; + + /* handle disable event */ + case BTA_HF_CLIENT_API_DISABLE_EVT: + bta_hf_client_api_disable((tBTA_HF_CLIENT_DATA *) p_msg); + break; + + default: + bta_hf_client_sm_execute(p_msg->event, (tBTA_HF_CLIENT_DATA *) p_msg); + break; + } + return TRUE; +} + +/******************************************************************************* +** +** Function bta_hf_client_sm_execute +** +** Description State machine event handling function for HF Client +** +** +** Returns void +** +*******************************************************************************/ +void bta_hf_client_sm_execute(UINT16 event, tBTA_HF_CLIENT_DATA *p_data) +{ + tBTA_HF_CLIENT_ST_TBL state_table; + UINT8 action; + int i; + +#if BTA_HF_CLIENT_DEBUG == TRUE + UINT16 in_event = event; + UINT8 in_state = bta_hf_client_cb.scb.state; + + /* Ignore displaying of AT results when not connected (Ignored in state machine) */ + if (bta_hf_client_cb.scb.state == BTA_HF_CLIENT_OPEN_ST) { + APPL_TRACE_EVENT("HF Client evt : State %d (%s), Event 0x%04x (%s)", + bta_hf_client_cb.scb.state, + bta_hf_client_state_str(bta_hf_client_cb.scb.state), + event, bta_hf_client_evt_str(event)); + } +#endif + + event &= 0x00FF; + if (event >= (BTA_HF_CLIENT_MAX_EVT & 0x00FF)) { + APPL_TRACE_ERROR("HF Client evt out of range, ignoring..."); + return; + } + + /* look up the state table for the current state */ + state_table = bta_hf_client_st_tbl[bta_hf_client_cb.scb.state]; + + /* set next state */ + bta_hf_client_cb.scb.state = state_table[event][BTA_HF_CLIENT_NEXT_STATE]; + + /* execute action functions */ + for (i = 0; i < BTA_HF_CLIENT_ACTIONS; i++) { + if ((action = state_table[event][i]) != BTA_HF_CLIENT_IGNORE) { + (*bta_hf_client_action[action])(p_data); + } else { + break; + } + } + +#if BTA_HF_CLIENT_DEBUG == TRUE + if (bta_hf_client_cb.scb.state != in_state) { + APPL_TRACE_EVENT("BTA HF Client State Change: [%s] -> [%s] after Event [%s]", + bta_hf_client_state_str(in_state), + bta_hf_client_state_str(bta_hf_client_cb.scb.state), + bta_hf_client_evt_str(in_event)); + } +#endif +} + +static void send_post_slc_cmd(void) +{ + bta_hf_client_cb.scb.at_cb.current_cmd = BTA_HF_CLIENT_AT_NONE; + + bta_hf_client_send_at_bia(); + bta_hf_client_send_at_ccwa(TRUE); + bta_hf_client_send_at_cmee(TRUE); + bta_hf_client_send_at_cops(FALSE); + bta_hf_client_send_at_btrh(TRUE, 0); + +#if BT_HF_CLIENT_BQB_INCLUDED + if (s_bta_hf_client_bqb_clip_flag == TRUE) { + bta_hf_client_send_at_clip(TRUE); + } +#else + bta_hf_client_send_at_clip(TRUE); +#endif /* BT_HF_CLIENT_BQB_INCLUDED */ +} + +/******************************************************************************* +** +** Function bta_hf_client_slc_seq +** +** Description Handles AT commands sequence required for SLC creation +** +** +** Returns void +** +*******************************************************************************/ +void bta_hf_client_slc_seq(BOOLEAN error) +{ + APPL_TRACE_DEBUG("bta_hf_client_slc_seq cmd: %u", bta_hf_client_cb.scb.at_cb.current_cmd); + + if (error) { + /* SLC establishment error, sent close rfcomm event */ + APPL_TRACE_ERROR("HFPClient: Failed to create SLC due to AT error, disconnecting (%u)", + bta_hf_client_cb.scb.at_cb.current_cmd); + + bta_hf_client_sm_execute(BTA_HF_CLIENT_API_CLOSE_EVT, NULL); + return; + } + + if (bta_hf_client_cb.scb.svc_conn) { + return; + } + + switch (bta_hf_client_cb.scb.at_cb.current_cmd) { + case BTA_HF_CLIENT_AT_NONE: + bta_hf_client_send_at_brsf(); + break; + + case BTA_HF_CLIENT_AT_BRSF: + if (bta_hf_client_cb.scb.peer_features & BTA_HF_CLIENT_PEER_CODEC) { + bta_hf_client_send_at_bac(); + break; + } + + bta_hf_client_send_at_cind(FALSE); + break; + + case BTA_HF_CLIENT_AT_BAC: + bta_hf_client_send_at_cind(FALSE); + break; + + case BTA_HF_CLIENT_AT_CIND: + bta_hf_client_send_at_cind(TRUE); + break; + + case BTA_HF_CLIENT_AT_CIND_STATUS: + bta_hf_client_send_at_cmer(TRUE); + break; + + case BTA_HF_CLIENT_AT_CMER: + if (bta_hf_client_cb.scb.peer_features & BTA_HF_CLIENT_PEER_FEAT_3WAY) { + bta_hf_client_send_at_chld('?', 0); + } else { + bta_hf_client_svc_conn_open(NULL); + send_post_slc_cmd(); + } + break; + + case BTA_HF_CLIENT_AT_CHLD: + bta_hf_client_svc_conn_open(NULL); + send_post_slc_cmd(); + break; + + default: + /* If happen there is a bug in SLC creation procedure... */ + APPL_TRACE_ERROR("HFPClient: Failed to create SLCdue to unexpected AT command, disconnecting (%u)", + bta_hf_client_cb.scb.at_cb.current_cmd); + + bta_hf_client_sm_execute(BTA_HF_CLIENT_API_CLOSE_EVT, NULL); + break; + } +} + +#if BTA_HF_CLIENT_DEBUG == TRUE + +#ifndef CASE_RETURN_STR +#define CASE_RETURN_STR(const) case const: return #const; +#endif + +static char *bta_hf_client_evt_str(UINT16 event) +{ + switch (event) { + CASE_RETURN_STR(BTA_HF_CLIENT_API_REGISTER_EVT) + CASE_RETURN_STR(BTA_HF_CLIENT_API_DEREGISTER_EVT) + CASE_RETURN_STR(BTA_HF_CLIENT_API_OPEN_EVT) + CASE_RETURN_STR(BTA_HF_CLIENT_API_CLOSE_EVT) + CASE_RETURN_STR(BTA_HF_CLIENT_API_AUDIO_OPEN_EVT) + CASE_RETURN_STR(BTA_HF_CLIENT_API_AUDIO_CLOSE_EVT) + CASE_RETURN_STR(BTA_HF_CLIENT_RFC_OPEN_EVT) + CASE_RETURN_STR(BTA_HF_CLIENT_RFC_CLOSE_EVT) + CASE_RETURN_STR(BTA_HF_CLIENT_RFC_SRV_CLOSE_EVT) + CASE_RETURN_STR(BTA_HF_CLIENT_RFC_DATA_EVT) + CASE_RETURN_STR(BTA_HF_CLIENT_DISC_ACP_RES_EVT) + CASE_RETURN_STR(BTA_HF_CLIENT_DISC_INT_RES_EVT) + CASE_RETURN_STR(BTA_HF_CLIENT_DISC_OK_EVT) + CASE_RETURN_STR(BTA_HF_CLIENT_DISC_FAIL_EVT) + CASE_RETURN_STR(BTA_HF_CLIENT_API_ENABLE_EVT) + CASE_RETURN_STR(BTA_HF_CLIENT_API_DISABLE_EVT) + CASE_RETURN_STR(BTA_HF_CLIENT_SCO_OPEN_EVT) + CASE_RETURN_STR(BTA_HF_CLIENT_SCO_CLOSE_EVT) + CASE_RETURN_STR(BTA_HF_CLIENT_SEND_AT_CMD_EVT) + CASE_RETURN_STR(BTA_HF_CLIENT_PKT_NUMS_GET_EVT) + default: + return "Unknown HF Client Event"; + } +} + +static char *bta_hf_client_state_str(UINT8 state) +{ + switch (state) { + CASE_RETURN_STR(BTA_HF_CLIENT_INIT_ST) + CASE_RETURN_STR(BTA_HF_CLIENT_OPENING_ST) + CASE_RETURN_STR(BTA_HF_CLIENT_OPEN_ST) + CASE_RETURN_STR(BTA_HF_CLIENT_CLOSING_ST) + default: + return "Unknown HF Client State"; + } +} +#endif +#endif /* #if (BTA_HF_INCLUDED == TRUE) */ diff --git a/lib/bt/host/bluedroid/bta/hf_client/bta_hf_client_rfc.c b/lib/bt/host/bluedroid/bta/hf_client/bta_hf_client_rfc.c new file mode 100644 index 00000000..b64b6237 --- /dev/null +++ b/lib/bt/host/bluedroid/bta/hf_client/bta_hf_client_rfc.c @@ -0,0 +1,246 @@ +/****************************************************************************** + * + * Copyright (c) 2014 The Android Open Source Project + * Copyright (C) 2004-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains the audio gateway functions controlling the RFCOMM + * connections. + * + ******************************************************************************/ + +#include +#include "common/bt_defs.h" +#include "bta/bta_api.h" +#include "bta_hf_client_int.h" +#include "stack/port_api.h" +#include "osi/allocator.h" + +#if (BTA_HF_INCLUDED == TRUE) +/******************************************************************************* +** +** Function bta_hf_client_port_cback +** +** Description RFCOMM Port callback +** +** +** Returns void +** +*******************************************************************************/ +static void bta_hf_client_port_cback(UINT32 code, UINT16 port_handle) +{ + BT_HDR *p_buf; + UNUSED(code); + + /* ignore port events for port handles other than connected handle */ + if (port_handle != bta_hf_client_cb.scb.conn_handle) { + APPL_TRACE_DEBUG("bta_hf_client_port_cback ignoring handle:%d conn_handle = %d", + port_handle, bta_hf_client_cb.scb.conn_handle); + return; + } + + if ((p_buf = (BT_HDR *) osi_malloc(sizeof(BT_HDR))) != NULL) { + p_buf->event = BTA_HF_CLIENT_RFC_DATA_EVT; + bta_sys_sendmsg(p_buf); + } +} + +/******************************************************************************* +** +** Function bta_hf_client_mgmt_cback +** +** Description RFCOMM management callback +** +** +** Returns void +** +*******************************************************************************/ +static void bta_hf_client_mgmt_cback(UINT32 code, UINT16 port_handle, void* data) +{ + tBTA_HF_CLIENT_RFC *p_buf; + UINT16 event; + + APPL_TRACE_DEBUG("bta_hf_client_mgmt_cback : code = %d, port_handle = %d, conn_handle = %d, serv_handle = %d", + code, port_handle, bta_hf_client_cb.scb.conn_handle, bta_hf_client_cb.scb.serv_handle); + + /* ignore close event for port handles other than connected handle */ + if ((code != PORT_SUCCESS) && (port_handle != bta_hf_client_cb.scb.conn_handle)) { + APPL_TRACE_DEBUG("bta_hf_client_mgmt_cback ignoring handle:%d", port_handle); + return; + } + + if (code == PORT_SUCCESS) { + if ((bta_hf_client_cb.scb.conn_handle && (port_handle == bta_hf_client_cb.scb.conn_handle)) || /* outgoing connection */ + (port_handle == bta_hf_client_cb.scb.serv_handle)) { /* incoming connection */ + event = BTA_HF_CLIENT_RFC_OPEN_EVT; + } else { + APPL_TRACE_ERROR ("bta_hf_client_mgmt_cback: PORT_SUCCESS, ignoring handle = %d", port_handle); + return; + } + } + /* distinguish server close events */ + else if (port_handle == bta_hf_client_cb.scb.conn_handle) { + event = BTA_HF_CLIENT_RFC_CLOSE_EVT; + } else { + event = BTA_HF_CLIENT_RFC_SRV_CLOSE_EVT; + } + + if ((p_buf = (tBTA_HF_CLIENT_RFC *) osi_malloc(sizeof(tBTA_HF_CLIENT_RFC))) != NULL) { + p_buf->hdr.event = event; + p_buf->port_handle = port_handle; + bta_sys_sendmsg(p_buf); + } +} + +/******************************************************************************* +** +** Function bta_hf_client_setup_port +** +** Description Setup RFCOMM port for use by HF Client. +** +** +** Returns void +** +*******************************************************************************/ +void bta_hf_client_setup_port(UINT16 handle) +{ + PORT_SetEventMask(handle, PORT_EV_RXCHAR); + PORT_SetEventCallback(handle, bta_hf_client_port_cback); +} + +/******************************************************************************* +** +** Function bta_hf_client_start_server +** +** Description Setup RFCOMM server for use by HF Client. +** +** +** Returns void +** +*******************************************************************************/ +void bta_hf_client_start_server(void) +{ + int port_status; + + if (bta_hf_client_cb.scb.serv_handle > 0) { + APPL_TRACE_DEBUG("%s already started, handle: %d", __FUNCTION__, bta_hf_client_cb.scb.serv_handle); + return; + } + + BTM_SetSecurityLevel(FALSE, "", BTM_SEC_SERVICE_HF_HANDSFREE, bta_hf_client_cb.scb.serv_sec_mask, + BT_PSM_RFCOMM, BTM_SEC_PROTO_RFCOMM, bta_hf_client_cb.scn); + + port_status = RFCOMM_CreateConnection(UUID_SERVCLASS_HF_HANDSFREE, bta_hf_client_cb.scn, + TRUE, BTA_HF_CLIENT_MTU, (UINT8 *) bd_addr_any, &(bta_hf_client_cb.scb.serv_handle), + bta_hf_client_mgmt_cback); + + if (port_status == PORT_SUCCESS) { + bta_hf_client_setup_port(bta_hf_client_cb.scb.serv_handle); + } else { + /* TODO: can we handle this better? */ + APPL_TRACE_DEBUG("bta_hf_client_start_server: RFCOMM_CreateConnection returned error:%d", port_status); + } + + APPL_TRACE_DEBUG("bta_hf_client_start_server handle: %d", bta_hf_client_cb.scb.serv_handle); +} + +/******************************************************************************* +** +** Function bta_hf_client_close_server +** +** Description Close RFCOMM server port for use by HF Client. +** +** +** Returns void +** +*******************************************************************************/ +void bta_hf_client_close_server(void) +{ + APPL_TRACE_DEBUG("%s %d", __FUNCTION__, bta_hf_client_cb.scb.serv_handle); + + if (bta_hf_client_cb.scb.serv_handle == 0) { + APPL_TRACE_DEBUG("%s already stopped", __FUNCTION__); + return; + } + + RFCOMM_RemoveServer(bta_hf_client_cb.scb.serv_handle); + bta_hf_client_cb.scb.serv_handle = 0; +} + +/******************************************************************************* +** +** Function bta_hf_client_rfc_do_open +** +** Description Open an RFCOMM connection to the peer device. +** +** +** Returns void +** +*******************************************************************************/ +void bta_hf_client_rfc_do_open(tBTA_HF_CLIENT_DATA *p_data) +{ + BTM_SetSecurityLevel(TRUE, "", BTM_SEC_SERVICE_HF_HANDSFREE, + bta_hf_client_cb.scb.cli_sec_mask, BT_PSM_RFCOMM, + BTM_SEC_PROTO_RFCOMM, bta_hf_client_cb.scb.peer_scn); + + if (RFCOMM_CreateConnection(UUID_SERVCLASS_HF_HANDSFREE, bta_hf_client_cb.scb.peer_scn, + FALSE, BTA_HF_CLIENT_MTU, bta_hf_client_cb.scb.peer_addr, &(bta_hf_client_cb.scb.conn_handle), + bta_hf_client_mgmt_cback) == PORT_SUCCESS) { + bta_hf_client_setup_port(bta_hf_client_cb.scb.conn_handle); + APPL_TRACE_DEBUG("bta_hf_client_rfc_do_open : conn_handle = %d", bta_hf_client_cb.scb.conn_handle); + } + /* RFCOMM create connection failed; send ourselves RFCOMM close event */ + else { + bta_hf_client_sm_execute(BTA_HF_CLIENT_RFC_CLOSE_EVT, p_data); + } +} + +/******************************************************************************* +** +** Function bta_hf_client_rfc_do_close +** +** Description Close RFCOMM connection. +** +** +** Returns void +** +*******************************************************************************/ +void bta_hf_client_rfc_do_close(tBTA_HF_CLIENT_DATA *p_data) +{ + tBTA_HF_CLIENT_RFC *p_buf; + UNUSED(p_data); + + if (bta_hf_client_cb.scb.conn_handle) { + RFCOMM_RemoveConnection(bta_hf_client_cb.scb.conn_handle); + } else { + /* Close API was called while HF Client is in Opening state. */ + /* Need to trigger the state machine to send callback to the app */ + /* and move back to INIT state. */ + if ((p_buf = (tBTA_HF_CLIENT_RFC *) osi_malloc(sizeof(tBTA_HF_CLIENT_RFC))) != NULL) { + p_buf->hdr.event = BTA_HF_CLIENT_RFC_CLOSE_EVT; + bta_sys_sendmsg(p_buf); + } + + /* Cancel SDP if it had been started. */ + if (bta_hf_client_cb.scb.p_disc_db) { + (void)SDP_CancelServiceSearch (bta_hf_client_cb.scb.p_disc_db); + bta_hf_client_free_db(NULL); + } + } +} +#endif /* #if (BTA_HF_INCLUDED == TRUE) */ diff --git a/lib/bt/host/bluedroid/bta/hf_client/bta_hf_client_sco.c b/lib/bt/host/bluedroid/bta/hf_client/bta_hf_client_sco.c new file mode 100644 index 00000000..d464a889 --- /dev/null +++ b/lib/bt/host/bluedroid/bta/hf_client/bta_hf_client_sco.c @@ -0,0 +1,944 @@ +/****************************************************************************** + * + * Copyright (c) 2014 The Android Open Source Project + * Copyright (C) 2004-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +#include "bta_hf_client_int.h" +#include "bta/bta_hf_client_api.h" +#include "common/bt_trace.h" +#include +#include "common/bt_defs.h" +#include "common/bt_target.h" +#include "osi/allocator.h" +#if (BTM_SCO_HCI_INCLUDED == TRUE ) +#include "bta/bta_hf_client_co.h" +#include "hci/hci_audio.h" +#endif + +#if BT_HF_CLIENT_BQB_INCLUDED +static BOOLEAN s_bta_hf_client_bqb_esco_s1_flag = false; +#endif /* BT_HF_CLIENT_BQB_INCLUDED */ + +#if (BTA_HF_INCLUDED == TRUE) +#define BTA_HF_CLIENT_NO_EDR_ESCO (BTM_SCO_PKT_TYPES_MASK_NO_2_EV3 | \ + BTM_SCO_PKT_TYPES_MASK_NO_3_EV3 | \ + BTM_SCO_PKT_TYPES_MASK_NO_2_EV5 | \ + BTM_SCO_PKT_TYPES_MASK_NO_3_EV5) + +#define BTA_HF_CLIENT_SCO_PARAM_IDX_CVSD 0 /* SCO setting for CVSD */ +#define BTA_HF_CLIENT_ESCO_PARAM_IDX_CVSD_S3 1 /* eSCO setting for CVSD S3 */ +#define BTA_HF_CLIENT_ESCO_PARAM_IDX_MSBC_T2 2 /* eSCO setting for mSBC T2 */ +#define BTA_HF_CLIENT_ESCO_PARAM_IDX_CVSD_S4 3 /* eSCO setting for CVSD S4 */ +#define BTA_HF_CLIENT_ESCO_PARAM_IDX_CVSD_S1 4 /* eSCO setting for CVSD S1 */ + +static const tBTM_ESCO_PARAMS bta_hf_client_esco_params[] = { + /* SCO CVSD */ + { + .rx_bw = BTM_64KBITS_RATE, + .tx_bw = BTM_64KBITS_RATE, + .max_latency = 10, + .voice_contfmt = BTM_VOICE_SETTING_CVSD, + .packet_types = (BTM_SCO_PKT_TYPES_MASK_HV1 | + BTM_SCO_PKT_TYPES_MASK_HV3 | + BTM_SCO_PKT_TYPES_MASK_NO_2_EV3 | + BTM_SCO_PKT_TYPES_MASK_NO_3_EV3 | + BTM_SCO_PKT_TYPES_MASK_NO_2_EV5 | + BTM_SCO_PKT_TYPES_MASK_NO_3_EV5), + .retrans_effort = BTM_ESCO_RETRANS_OFF, + }, + /* ESCO CVSD */ + { + .rx_bw = BTM_64KBITS_RATE, + .tx_bw = BTM_64KBITS_RATE, + .max_latency = 10, + .voice_contfmt = BTM_VOICE_SETTING_CVSD, + /* Packet Types : 2-EV3 */ + .packet_types = (BTM_SCO_PKT_TYPES_MASK_NO_2_EV5 | + BTM_SCO_PKT_TYPES_MASK_NO_3_EV3 | + BTM_SCO_PKT_TYPES_MASK_NO_3_EV5), + .retrans_effort = BTM_ESCO_RETRANS_POWER, + }, + /* ESCO mSBC */ + { + .rx_bw = BTM_64KBITS_RATE, + .tx_bw = BTM_64KBITS_RATE, + .max_latency = 13, + .voice_contfmt = BTM_VOICE_SETTING_TRANS, + /* Packet Types : 2-EV3 */ + .packet_types = (BTM_SCO_PKT_TYPES_MASK_NO_2_EV5 | + BTM_SCO_PKT_TYPES_MASK_NO_3_EV3 | + BTM_SCO_PKT_TYPES_MASK_NO_3_EV5), + .retrans_effort = BTM_ESCO_RETRANS_QUALITY, + }, + /* HFP 1.7+ */ + /* ESCO CVSD S4 */ + { + .rx_bw = BTM_64KBITS_RATE, + .tx_bw = BTM_64KBITS_RATE, + .max_latency = 12, + .voice_contfmt = BTM_VOICE_SETTING_CVSD, + /* Packet Types : 2-EV3 */ + .packet_types = (BTM_SCO_PKT_TYPES_MASK_NO_2_EV5 | + BTM_SCO_PKT_TYPES_MASK_NO_3_EV3 | + BTM_SCO_PKT_TYPES_MASK_NO_3_EV5), + .retrans_effort = BTM_ESCO_RETRANS_QUALITY, + }, + /* ESCO CVSD S1 */ +#if BT_HF_CLIENT_BQB_INCLUDED + { + .rx_bw = BTM_64KBITS_RATE, + .tx_bw = BTM_64KBITS_RATE, + .max_latency = 7, + .voice_contfmt = BTM_VOICE_SETTING_CVSD, + /* Packet Types : EV3 */ + .packet_types = (HCI_ESCO_PKT_TYPES_MASK_EV3 | + BTM_SCO_PKT_TYPES_MASK_NO_2_EV5 | + BTM_SCO_PKT_TYPES_MASK_NO_2_EV3 | + BTM_SCO_PKT_TYPES_MASK_NO_3_EV3 | + BTM_SCO_PKT_TYPES_MASK_NO_3_EV5), + .retrans_effort = BTM_ESCO_RETRANS_POWER, + } +#endif /* BT_HF_CLIENT_BQB_INCLUDED */ +}; + +enum { + BTA_HF_CLIENT_SCO_LISTEN_E, + BTA_HF_CLIENT_SCO_OPEN_E, /* open request */ + BTA_HF_CLIENT_SCO_CLOSE_E, /* close request */ + BTA_HF_CLIENT_SCO_SHUTDOWN_E, /* shutdown request */ + BTA_HF_CLIENT_SCO_CONN_OPEN_E, /* sco opened */ + BTA_HF_CLIENT_SCO_CONN_CLOSE_E, /* sco closed */ +#if (BTM_SCO_HCI_INCLUDED == TRUE ) + BTA_HF_CLIENT_SCO_CI_DATA_E, /* sco data ready */ +#endif /* #if (BTM_SCO_HCI_INCLUDED == TRUE ) */ +}; + +/******************************************************************************* +** +** Function bta_hf_client_bqb_esco_s1_ctrl +** +** Description Control the usage of CVSD eSCO S1 parameter for BQB test +** +** Returns void +** +*******************************************************************************/ +#if BT_HF_CLIENT_BQB_INCLUDED +void bta_hf_client_bqb_esco_s1_ctrl(BOOLEAN enable) +{ + s_bta_hf_client_bqb_esco_s1_flag = enable; +} +#endif /* BT_HF_CLIENT_BQB_INCLUDED */ + +static void bta_hf_client_sco_event(UINT8 event); +/******************************************************************************* +** +** Function bta_hf_client_remove_sco +** +** Description Removes the specified SCO from the system. +** If only_active is TRUE, then SCO is only removed if connected +** +** Returns BOOLEAN - TRUE if Sco removal was started +** +*******************************************************************************/ +static BOOLEAN bta_hf_client_sco_remove(BOOLEAN only_active) +{ + BOOLEAN removed_started = FALSE; + tBTM_STATUS status; + + APPL_TRACE_DEBUG("%s %d", __FUNCTION__, only_active); + + if (bta_hf_client_cb.scb.sco_idx != BTM_INVALID_SCO_INDEX) { + status = BTM_RemoveSco(bta_hf_client_cb.scb.sco_idx); + + APPL_TRACE_DEBUG("%s idx 0x%04x, status:0x%x", __FUNCTION__, bta_hf_client_cb.scb.sco_idx, status); + + if (status == BTM_CMD_STARTED) { + removed_started = TRUE; + } + /* If no connection reset the sco handle */ + else if ( (status == BTM_SUCCESS) || (status == BTM_UNKNOWN_ADDR) ) { + bta_hf_client_cb.scb.sco_idx = BTM_INVALID_SCO_INDEX; + } + } + return removed_started; +} + +/******************************************************************************* +** +** Function bta_hf_client_cback_sco +** +** Description Call application callback function with SCO event. +** +** +** Returns void +** +*******************************************************************************/ +void bta_hf_client_cback_sco(UINT8 event) +{ + tBTA_HF_CLIENT_HDR evt; + + memset(&evt, 0, sizeof(evt)); + evt.sync_conn_handle = BTM_ReadScoHandle(bta_hf_client_cb.scb.sco_idx); + + /* call app cback */ + (*bta_hf_client_cb.p_cback)(event, (tBTA_HF_CLIENT_HDR *) &evt); +} + +#if (BTM_SCO_HCI_INCLUDED == TRUE ) +/******************************************************************************* +** +** Function bta_hf_client_sco_read_cback +** +** Description Callback function is the callback function for incoming +** SCO data over HCI. +** +** Returns void +** +*******************************************************************************/ +static void bta_hf_client_sco_read_cback (UINT16 sco_idx, BT_HDR *p_data, tBTM_SCO_DATA_FLAG status) +{ + if (status != BTM_SCO_DATA_CORRECT) + { + APPL_TRACE_DEBUG("%s: status(%d)", __FUNCTION__, status); + } + + bta_hf_client_sco_co_in_data (p_data, status); + osi_free(p_data); +} +#endif /* BTM_SCO_HCI_INCLUDED */ + +/******************************************************************************* +** +** Function bta_hf_client_sco_conn_rsp +** +** Description Process the SCO connection request +** +** +** Returns void +** +*******************************************************************************/ +static void bta_hf_client_sco_conn_rsp(tBTM_ESCO_CONN_REQ_EVT_DATA *p_data) +{ + tBTM_ESCO_PARAMS resp; + UINT8 hci_status = HCI_SUCCESS; + UINT8 index = BTA_HF_CLIENT_ESCO_PARAM_IDX_CVSD_S3; +#if (BTM_SCO_HCI_INCLUDED == TRUE ) + tBTA_HFP_CODEC_INFO codec_info = {BTA_HFP_SCO_CODEC_PCM}; + UINT32 pcm_sample_rate; +#endif + + APPL_TRACE_DEBUG("%s: negotiated codec = %d", __FUNCTION__, bta_hf_client_cb.scb.negotiated_codec); + + if (bta_hf_client_cb.scb.sco_state == BTA_HF_CLIENT_SCO_LISTEN_ST) { + if (p_data->link_type == BTM_LINK_TYPE_SCO) { + index = BTA_HF_CLIENT_SCO_PARAM_IDX_CVSD; + } else { + if ((bta_hf_client_cb.scb.negotiated_codec == BTM_SCO_CODEC_CVSD) && + (bta_hf_client_cb.scb.features && BTA_HF_CLIENT_FEAT_ESCO_S4) && + (bta_hf_client_cb.scb.peer_features && BTA_HF_CLIENT_PEER_ESCO_S4)) { + index = BTA_HF_CLIENT_ESCO_PARAM_IDX_CVSD_S4; +#if BT_HF_CLIENT_BQB_INCLUDED + if (s_bta_hf_client_bqb_esco_s1_flag == true) { + index = BTA_HF_CLIENT_ESCO_PARAM_IDX_CVSD_S1; + } +#endif /* BT_HF_CLIENT_BQB_INCLUDED */ + } else if (bta_hf_client_cb.scb.negotiated_codec == BTM_SCO_CODEC_MSBC) { + index = BTA_HF_CLIENT_ESCO_PARAM_IDX_MSBC_T2; + } + } + resp = bta_hf_client_esco_params[index]; + + /* tell sys to stop av if any */ + bta_sys_sco_use(BTA_ID_HS, 1, bta_hf_client_cb.scb.peer_addr); + +#if (BTM_SCO_HCI_INCLUDED == TRUE ) + bta_hf_client_co_audio_state(bta_hf_client_cb.scb.sco_idx, SCO_STATE_SETUP, 0); + pcm_sample_rate = BTA_HFP_SCO_SAMP_RATE_8K; + + /* initialize SCO setup, no voice setting for AG, data rate <==> sample rate */ + BTM_ConfigScoPath(bta_hf_client_sco_co_init(pcm_sample_rate, pcm_sample_rate, &codec_info, 0), + bta_hf_client_sco_read_cback, NULL, TRUE); +#endif + } else { + hci_status = HCI_ERR_HOST_REJECT_DEVICE; + } + + BTM_EScoConnRsp(p_data->sco_inx, hci_status, &resp); +} + +#if (BTM_SCO_HCI_INCLUDED == TRUE) +/******************************************************************************* +** +** Function bta_hf_client_pkt_stat_nums +** +** Description Get the packet status number +** +** +** Returns void +** +*******************************************************************************/ +void bta_hf_client_pkt_stat_nums(tBTA_HF_CLIENT_DATA *p_data) +{ + tBTA_SCO_PKT_STAT_NUMS pkt_stat_nums; + uint16_t sync_conn_handle = p_data->pkt_stat.sync_conn_handle; + BTM_PktStatNumsGet(sync_conn_handle, (tBTM_SCO_PKT_STAT_NUMS *) &pkt_stat_nums); + + /* call app cback */ + if (bta_hf_client_cb.p_cback) { + (*bta_hf_client_cb.p_cback)(BTA_HF_CLIENT_PKT_STAT_NUMS_GET_EVT, (void*) &pkt_stat_nums); + } +} + +/******************************************************************************* +** +** Function bta_hf_client_ci_sco_data +** +** Description Process the SCO data ready callin event +** +** +** Returns void +** +*******************************************************************************/ +void bta_hf_client_ci_sco_data(tBTA_HF_CLIENT_DATA *p_data) +{ + UNUSED(p_data); + bta_hf_client_sco_event(BTA_HF_CLIENT_SCO_CI_DATA_E); +} +#endif + +/******************************************************************************* +** +** Function bta_hf_client_sco_connreq_cback +** +** Description BTM eSCO connection requests and eSCO change requests +** Only the connection requests are processed by BTA. +** +** Returns void +** +*******************************************************************************/ +static void bta_hf_client_esco_connreq_cback(tBTM_ESCO_EVT event, tBTM_ESCO_EVT_DATA *p_data) +{ + APPL_TRACE_DEBUG("%s %d", __FUNCTION__, event); + + if (event != BTM_ESCO_CONN_REQ_EVT) { + return; + } + + /* TODO check remote bdaddr, should allow connect only from device with + * active SLC */ + + bta_hf_client_cb.scb.sco_idx = p_data->conn_evt.sco_inx; + + bta_hf_client_sco_conn_rsp(&p_data->conn_evt); + + bta_hf_client_cb.scb.sco_state = BTA_HF_CLIENT_SCO_OPENING_ST; +} + +/******************************************************************************* +** +** Function bta_hf_client_sco_conn_cback +** +** Description BTM SCO connection callback. +** +** +** Returns void +** +*******************************************************************************/ +static void bta_hf_client_sco_conn_cback(UINT16 sco_idx) +{ + BT_HDR *p_buf; + UINT8 *rem_bd; + tBTM_ESCO_DATA sco_data; + + APPL_TRACE_DEBUG("%s %d", __FUNCTION__, sco_idx); + + rem_bd = BTM_ReadScoBdAddr(sco_idx); + BTM_ReadEScoLinkParms (sco_idx, &sco_data); + + if (rem_bd && bdcmp(bta_hf_client_cb.scb.peer_addr, rem_bd) == 0 && + bta_hf_client_cb.scb.svc_conn && bta_hf_client_cb.scb.sco_idx == sco_idx) { + + bta_hf_client_cb.scb.link_type = sco_data.link_type; + bta_hf_client_cb.scb.tx_interval = sco_data.tx_interval; + bta_hf_client_cb.scb.retrans_window = sco_data.retrans_window; + bta_hf_client_cb.scb.air_mode = sco_data.air_mode; + if (sco_data.air_mode == BTM_SCO_AIR_MODE_CVSD) { + bta_hf_client_cb.scb.out_pkt_len = sco_data.tx_pkt_len * 2; + bta_hf_client_cb.scb.in_pkt_len = sco_data.rx_pkt_len * 2; + } else { + bta_hf_client_cb.scb.out_pkt_len = sco_data.tx_pkt_len; + bta_hf_client_cb.scb.in_pkt_len = sco_data.rx_pkt_len; + } + + if ((p_buf = (BT_HDR *) osi_malloc(sizeof(BT_HDR))) != NULL) { + p_buf->event = BTA_HF_CLIENT_SCO_OPEN_EVT; + p_buf->layer_specific = bta_hf_client_cb.scb.conn_handle; + bta_sys_sendmsg(p_buf); + } + } + /* no match found; disconnect sco, init sco variables */ + else { + bta_hf_client_cb.scb.sco_state = BTA_HF_CLIENT_SCO_SHUTDOWN_ST; + BTM_RemoveSco(sco_idx); + } +} + +/******************************************************************************* +** +** Function bta_hf_client_sco_disc_cback +** +** Description BTM SCO disconnection callback. +** +** +** Returns void +** +*******************************************************************************/ +static void bta_hf_client_sco_disc_cback(UINT16 sco_idx) +{ + BT_HDR *p_buf; + + APPL_TRACE_DEBUG("%s %d", __FUNCTION__, sco_idx); + + if (bta_hf_client_cb.scb.sco_idx == sco_idx) { +#if (BTM_SCO_HCI_INCLUDED == TRUE ) + tBTM_STATUS status = BTM_ConfigScoPath(BTM_SCO_ROUTE_PCM, NULL, NULL, TRUE); + APPL_TRACE_DEBUG("%s close config status = %d", __FUNCTION__, status); + UNUSED(status); + /* SCO clean up here */ + bta_hf_client_sco_co_close(); +#endif + if ((p_buf = (BT_HDR *) osi_malloc(sizeof(BT_HDR))) != NULL) { + p_buf->event = BTA_HF_CLIENT_SCO_CLOSE_EVT; + p_buf->layer_specific = bta_hf_client_cb.scb.conn_handle;; + bta_sys_sendmsg(p_buf); + } + } +} + +/******************************************************************************* +** +** Function bta_hf_client_sco_create +** +** Description +** +** +** Returns void +** +*******************************************************************************/ +static void bta_hf_client_sco_create(BOOLEAN is_orig) +{ + tBTM_STATUS status; + UINT8 *p_bd_addr = NULL; + tBTM_ESCO_PARAMS params; + UINT8 index = BTA_HF_CLIENT_ESCO_PARAM_IDX_CVSD_S3; +#if (BTM_SCO_HCI_INCLUDED == TRUE ) + tBTM_SCO_ROUTE_TYPE sco_route; + tBTA_HFP_CODEC_INFO codec_info = {BTA_HFP_SCO_CODEC_PCM}; + UINT32 pcm_sample_rate; +#endif + APPL_TRACE_DEBUG("%s %d", __FUNCTION__, is_orig); + + /* Make sure this sco handle is not already in use */ + if (bta_hf_client_cb.scb.sco_idx != BTM_INVALID_SCO_INDEX) { + APPL_TRACE_WARNING("%s: Index 0x%04x already in use", __FUNCTION__, + bta_hf_client_cb.scb.sco_idx); + return; + } + + if (bta_hf_client_cb.scb.negotiated_codec == BTM_SCO_CODEC_CVSD) { + if ((bta_hf_client_cb.scb.features && BTA_HF_CLIENT_FEAT_ESCO_S4) && + (bta_hf_client_cb.scb.peer_features && BTA_HF_CLIENT_PEER_ESCO_S4)) { + index = BTA_HF_CLIENT_ESCO_PARAM_IDX_CVSD_S4; + } + } else if (bta_hf_client_cb.scb.negotiated_codec == BTM_SCO_CODEC_MSBC) { + index = BTA_HF_CLIENT_ESCO_PARAM_IDX_MSBC_T2; + } + params = bta_hf_client_esco_params[index]; + + /* if initiating set current scb and peer bd addr */ + if (is_orig) { + /* Attempt to use eSCO if remote host supports HFP >= 1.5 */ + if (bta_hf_client_cb.scb.peer_version >= HFP_VERSION_1_5 && !bta_hf_client_cb.scb.retry_with_sco_only) { + BTM_SetEScoMode(BTM_LINK_TYPE_ESCO, ¶ms); + /* If ESCO or EDR ESCO, retry with SCO only in case of failure */ + if ((params.packet_types & BTM_ESCO_LINK_ONLY_MASK) + || !((params.packet_types & ~(BTM_ESCO_LINK_ONLY_MASK | BTM_SCO_LINK_ONLY_MASK)) ^ BTA_HF_CLIENT_NO_EDR_ESCO)) { + bta_hf_client_cb.scb.retry_with_sco_only = TRUE; + APPL_TRACE_API("Setting retry_with_sco_only to TRUE"); + } + } else { + if (bta_hf_client_cb.scb.retry_with_sco_only) { + APPL_TRACE_API("retrying with SCO only"); + } + bta_hf_client_cb.scb.retry_with_sco_only = FALSE; + + BTM_SetEScoMode(BTM_LINK_TYPE_SCO, ¶ms); + } + + /* tell sys to stop av if any */ + bta_sys_sco_use(BTA_ID_HS, 1, bta_hf_client_cb.scb.peer_addr); + +#if (BTM_SCO_HCI_INCLUDED == TRUE ) + /* Allow any platform specific pre-SCO set up to take place */ + bta_hf_client_co_audio_state(bta_hf_client_cb.scb.sco_idx, SCO_STATE_SETUP, 0); + + pcm_sample_rate = BTA_HFP_SCO_SAMP_RATE_8K; + sco_route = bta_hf_client_sco_co_init(pcm_sample_rate, pcm_sample_rate, &codec_info, 0); + + /* initialize SCO setup, no voice setting for AG, data rate <==> sample rate */ + BTM_ConfigScoPath(sco_route, bta_hf_client_sco_read_cback, NULL, TRUE); +#endif + + } else { + bta_hf_client_cb.scb.retry_with_sco_only = FALSE; + } + + p_bd_addr = bta_hf_client_cb.scb.peer_addr; + + status = BTM_CreateSco(p_bd_addr, is_orig, params.packet_types, + &bta_hf_client_cb.scb.sco_idx, bta_hf_client_sco_conn_cback, + bta_hf_client_sco_disc_cback); + if (status == BTM_CMD_STARTED && !is_orig) { + if (!BTM_RegForEScoEvts(bta_hf_client_cb.scb.sco_idx, bta_hf_client_esco_connreq_cback)) { + APPL_TRACE_DEBUG("%s SCO registration success", __FUNCTION__); + } + } + + APPL_TRACE_API("%s: orig %d, inx 0x%04x, status 0x%x, pkt types 0x%04x", + __FUNCTION__, is_orig, bta_hf_client_cb.scb.sco_idx, + status, params.packet_types); +} + + +/******************************************************************************* +** +** Function bta_hf_client_sco_event +** +** Description Handle SCO events +** +** +** Returns void +** +*******************************************************************************/ +static void bta_hf_client_sco_event(UINT8 event) +{ + APPL_TRACE_DEBUG("%s state: %d event: %d", __FUNCTION__, + bta_hf_client_cb.scb.sco_state, event); + +#if (BTM_SCO_HCI_INCLUDED == TRUE ) + tBTA_HF_CLIENT_SCB *p_scb = &bta_hf_client_cb.scb; + BT_HDR *p_buf; +#endif + +#if (BTM_SCO_HCI_INCLUDED == TRUE ) + if (event == BTA_HF_CLIENT_SCO_CI_DATA_E) { + UINT16 pkt_offset = 1 + HCI_SCO_PREAMBLE_SIZE; + UINT16 len_to_send = 0; + while (true) + { + p_buf = osi_calloc(sizeof(BT_HDR) + pkt_offset + p_scb->out_pkt_len); + if (!p_buf) { + APPL_TRACE_WARNING("%s, no mem", __FUNCTION__); + break; + } + + p_buf->offset = pkt_offset; + len_to_send = bta_hf_client_sco_co_out_data(p_buf->data + pkt_offset); + p_buf->len = len_to_send; + if (len_to_send == p_scb->out_pkt_len) { + // expect to get the exact size of data from upper layer + if (bta_hf_client_cb.scb.sco_state == BTA_HF_CLIENT_SCO_OPEN_ST) { + tBTM_STATUS write_stat = BTM_WriteScoData(p_scb->sco_idx, p_buf); + if (write_stat != BTM_SUCCESS) { + break; + } + } else { + osi_free(p_buf); + } + } else { + osi_free(p_buf); + break; + } + } + + return; + } +#endif + + switch (bta_hf_client_cb.scb.sco_state) { + case BTA_HF_CLIENT_SCO_SHUTDOWN_ST: + switch (event) { + case BTA_HF_CLIENT_SCO_LISTEN_E: + /* create sco listen connection */ + bta_hf_client_sco_create(FALSE); + bta_hf_client_cb.scb.sco_state = BTA_HF_CLIENT_SCO_LISTEN_ST; + break; + + default: + APPL_TRACE_WARNING("BTA_HF_CLIENT_SCO_SHUTDOWN_ST: Ignoring event %d", event); + break; + } + break; + + case BTA_HF_CLIENT_SCO_LISTEN_ST: + switch (event) { + case BTA_HF_CLIENT_SCO_LISTEN_E: + /* create sco listen connection (Additional channel) */ + bta_hf_client_sco_create(FALSE); + break; + + case BTA_HF_CLIENT_SCO_OPEN_E: + /* remove listening connection */ + bta_hf_client_sco_remove(FALSE); + + /* create sco connection to peer */ + bta_hf_client_sco_create(TRUE); + bta_hf_client_cb.scb.sco_state = BTA_HF_CLIENT_SCO_OPENING_ST; + break; + + case BTA_HF_CLIENT_SCO_SHUTDOWN_E: + /* remove listening connection */ + bta_hf_client_sco_remove(FALSE); + + bta_hf_client_cb.scb.sco_state = BTA_HF_CLIENT_SCO_SHUTDOWN_ST; + break; + + case BTA_HF_CLIENT_SCO_CLOSE_E: + /* remove listening connection */ + /* Ignore the event. We need to keep listening SCO for the active SLC */ + APPL_TRACE_WARNING("BTA_HF_CLIENT_SCO_LISTEN_ST: Ignoring event %d", event); + break; + + case BTA_HF_CLIENT_SCO_CONN_CLOSE_E: + /* sco failed; create sco listen connection */ + bta_hf_client_sco_create(FALSE); + bta_hf_client_cb.scb.sco_state = BTA_HF_CLIENT_SCO_LISTEN_ST; + break; + + default: + APPL_TRACE_WARNING("BTA_HF_CLIENT_SCO_LISTEN_ST: Ignoring event %d", event); + break; + } + break; + + case BTA_HF_CLIENT_SCO_OPENING_ST: + switch (event) { + case BTA_HF_CLIENT_SCO_CLOSE_E: + bta_hf_client_cb.scb.sco_state = BTA_HF_CLIENT_SCO_OPEN_CL_ST; + break; + + case BTA_HF_CLIENT_SCO_SHUTDOWN_E: + bta_hf_client_cb.scb.sco_state = BTA_HF_CLIENT_SCO_SHUTTING_ST; + break; + + case BTA_HF_CLIENT_SCO_CONN_OPEN_E: + bta_hf_client_cb.scb.sco_state = BTA_HF_CLIENT_SCO_OPEN_ST; + break; + + case BTA_HF_CLIENT_SCO_CONN_CLOSE_E: + /* sco failed; create sco listen connection */ + bta_hf_client_sco_create(FALSE); + bta_hf_client_cb.scb.sco_state = BTA_HF_CLIENT_SCO_LISTEN_ST; + break; + + default: + APPL_TRACE_WARNING("BTA_HF_CLIENT_SCO_OPENING_ST: Ignoring event %d", event); + break; + } + break; + + case BTA_HF_CLIENT_SCO_OPEN_CL_ST: + switch (event) { + case BTA_HF_CLIENT_SCO_OPEN_E: + bta_hf_client_cb.scb.sco_state = BTA_HF_CLIENT_SCO_OPENING_ST; + break; + + case BTA_HF_CLIENT_SCO_SHUTDOWN_E: + bta_hf_client_cb.scb.sco_state = BTA_HF_CLIENT_SCO_SHUTTING_ST; + break; + + case BTA_HF_CLIENT_SCO_CONN_OPEN_E: + /* close sco connection */ + bta_hf_client_sco_remove(TRUE); + + bta_hf_client_cb.scb.sco_state = BTA_HF_CLIENT_SCO_CLOSING_ST; + break; + + case BTA_HF_CLIENT_SCO_CONN_CLOSE_E: + /* sco failed; create sco listen connection */ + + bta_hf_client_cb.scb.sco_state = BTA_HF_CLIENT_SCO_LISTEN_ST; + break; + + default: + APPL_TRACE_WARNING("BTA_HF_CLIENT_SCO_OPEN_CL_ST: Ignoring event %d", event); + break; + } + break; + + case BTA_HF_CLIENT_SCO_OPEN_ST: + switch (event) { + case BTA_HF_CLIENT_SCO_CLOSE_E: + /* close sco connection if active */ + if (bta_hf_client_sco_remove(TRUE)) { + bta_hf_client_cb.scb.sco_state = BTA_HF_CLIENT_SCO_CLOSING_ST; + } + break; + + case BTA_HF_CLIENT_SCO_SHUTDOWN_E: + /* remove all listening connections */ + bta_hf_client_sco_remove(FALSE); + + bta_hf_client_cb.scb.sco_state = BTA_HF_CLIENT_SCO_SHUTTING_ST; + break; + + case BTA_HF_CLIENT_SCO_CONN_CLOSE_E: + /* peer closed sco; create sco listen connection */ + bta_hf_client_sco_create(FALSE); + bta_hf_client_cb.scb.sco_state = BTA_HF_CLIENT_SCO_LISTEN_ST; + break; + + default: + APPL_TRACE_WARNING("BTA_HF_CLIENT_SCO_OPEN_ST: Ignoring event %d", event); + break; + } + break; + + case BTA_HF_CLIENT_SCO_CLOSING_ST: + switch (event) { + case BTA_HF_CLIENT_SCO_OPEN_E: + bta_hf_client_cb.scb.sco_state = BTA_HF_CLIENT_SCO_CLOSE_OP_ST; + break; + + case BTA_HF_CLIENT_SCO_SHUTDOWN_E: + bta_hf_client_cb.scb.sco_state = BTA_HF_CLIENT_SCO_SHUTTING_ST; + break; + + case BTA_HF_CLIENT_SCO_CONN_CLOSE_E: + /* peer closed sco; create sco listen connection */ + bta_hf_client_sco_create(FALSE); + + bta_hf_client_cb.scb.sco_state = BTA_HF_CLIENT_SCO_LISTEN_ST; + break; + + default: + APPL_TRACE_WARNING("BTA_HF_CLIENT_SCO_CLOSING_ST: Ignoring event %d", event); + break; + } + break; + + case BTA_HF_CLIENT_SCO_CLOSE_OP_ST: + switch (event) { + case BTA_HF_CLIENT_SCO_CLOSE_E: + bta_hf_client_cb.scb.sco_state = BTA_HF_CLIENT_SCO_CLOSING_ST; + break; + + case BTA_HF_CLIENT_SCO_SHUTDOWN_E: + bta_hf_client_cb.scb.sco_state = BTA_HF_CLIENT_SCO_SHUTTING_ST; + break; + + case BTA_HF_CLIENT_SCO_CONN_CLOSE_E: + /* open sco connection */ + bta_hf_client_sco_create(TRUE); + bta_hf_client_cb.scb.sco_state = BTA_HF_CLIENT_SCO_OPENING_ST; + break; + + default: + APPL_TRACE_WARNING("BTA_HF_CLIENT_SCO_CLOSE_OP_ST: Ignoring event %d", event); + break; + } + break; + + case BTA_HF_CLIENT_SCO_SHUTTING_ST: + switch (event) { + case BTA_HF_CLIENT_SCO_CONN_OPEN_E: + /* close sco connection; wait for conn close event */ + bta_hf_client_sco_remove(TRUE); + break; + + case BTA_HF_CLIENT_SCO_CONN_CLOSE_E: + bta_hf_client_cb.scb.sco_state = BTA_HF_CLIENT_SCO_SHUTDOWN_ST; + break; + + case BTA_HF_CLIENT_SCO_SHUTDOWN_E: + bta_hf_client_cb.scb.sco_state = BTA_HF_CLIENT_SCO_SHUTDOWN_ST; + break; + + default: + APPL_TRACE_WARNING("BTA_HF_CLIENT_SCO_SHUTTING_ST: Ignoring event %d", event); + break; + } + break; + + default: + break; + } +} + +/******************************************************************************* +** +** Function bta_hf_client_sco_listen +** +** Description Initialize SCO listener +** +** +** Returns void +** +*******************************************************************************/ +void bta_hf_client_sco_listen(tBTA_HF_CLIENT_DATA *p_data) +{ + UNUSED(p_data); + + APPL_TRACE_DEBUG("%s", __FUNCTION__); + + bta_hf_client_sco_event(BTA_HF_CLIENT_SCO_LISTEN_E); +} + +/******************************************************************************* +** +** Function bta_hf_client_sco_shutdown +** +** Description +** +** +** Returns void +** +*******************************************************************************/ +void bta_hf_client_sco_shutdown(tBTA_HF_CLIENT_DATA *p_data) +{ + UNUSED(p_data); + + APPL_TRACE_DEBUG("%s", __FUNCTION__); + + bta_hf_client_sco_event(BTA_HF_CLIENT_SCO_SHUTDOWN_E); +} + +/******************************************************************************* +** +** Function bta_hf_client_sco_conn_open +** +** Description +** +** +** Returns void +** +*******************************************************************************/ +void bta_hf_client_sco_conn_open(tBTA_HF_CLIENT_DATA *p_data) +{ + UNUSED(p_data); + + APPL_TRACE_DEBUG("%s", __FUNCTION__); + + bta_hf_client_sco_event(BTA_HF_CLIENT_SCO_CONN_OPEN_E); + + bta_sys_sco_open(BTA_ID_HS, 1, bta_hf_client_cb.scb.peer_addr); +#if (BTM_SCO_HCI_INCLUDED == TRUE) + bta_hf_client_co_audio_state(bta_hf_client_cb.scb.sco_idx, SCO_STATE_ON, 0); + /* open SCO codec if SCO is routed through transport */ + bta_hf_client_sco_co_open(bta_hf_client_cb.scb.sco_idx, bta_hf_client_cb.scb.air_mode, + bta_hf_client_cb.scb.out_pkt_len, BTA_HF_CLIENT_CI_SCO_DATA_EVT); +#endif + + if (bta_hf_client_cb.scb.negotiated_codec == BTM_SCO_CODEC_MSBC) { + bta_hf_client_cback_sco(BTA_HF_CLIENT_AUDIO_MSBC_OPEN_EVT); + } else { + bta_hf_client_cback_sco(BTA_HF_CLIENT_AUDIO_OPEN_EVT); + } + + bta_hf_client_cb.scb.retry_with_sco_only = FALSE; +} + +/******************************************************************************* +** +** Function bta_hf_client_sco_conn_close +** +** Description +** +** +** Returns void +** +*******************************************************************************/ +void bta_hf_client_sco_conn_close(tBTA_HF_CLIENT_DATA *p_data) +{ + APPL_TRACE_DEBUG("%s", __FUNCTION__); + + /* clear current scb */ + bta_hf_client_cb.scb.sco_idx = BTM_INVALID_SCO_INDEX; + + /* retry_with_sco_only, will be set only when initiator + ** and HFClient is first trying to establish an eSCO connection */ + if (bta_hf_client_cb.scb.retry_with_sco_only && bta_hf_client_cb.scb.svc_conn) { + bta_hf_client_sco_create(TRUE); + } else { + bta_hf_client_sco_event(BTA_HF_CLIENT_SCO_CONN_CLOSE_E); + + bta_sys_sco_close(BTA_ID_HS, 1, bta_hf_client_cb.scb.peer_addr); + + bta_sys_sco_unuse(BTA_ID_HS, 1, bta_hf_client_cb.scb.peer_addr); + + /* call app callback */ + bta_hf_client_cback_sco(BTA_HF_CLIENT_AUDIO_CLOSE_EVT); + + if (bta_hf_client_cb.scb.sco_close_rfc == TRUE) { + bta_hf_client_cb.scb.sco_close_rfc = FALSE; + bta_hf_client_rfc_do_close(p_data); + } + } + bta_hf_client_cb.scb.retry_with_sco_only = FALSE; +} + +/******************************************************************************* +** +** Function bta_hf_client_sco_open +** +** Description +** +** +** Returns void +** +*******************************************************************************/ +void bta_hf_client_sco_open(tBTA_HF_CLIENT_DATA *p_data) +{ + UNUSED(p_data); + + APPL_TRACE_DEBUG("%s", __FUNCTION__); + + bta_hf_client_sco_event(BTA_HF_CLIENT_SCO_OPEN_E); +} + +/******************************************************************************* +** +** Function bta_hf_client_sco_close +** +** Description +** +** +** Returns void +** +*******************************************************************************/ +void bta_hf_client_sco_close(tBTA_HF_CLIENT_DATA *p_data) +{ + UNUSED(p_data); + + APPL_TRACE_DEBUG("%s 0x%x", __FUNCTION__, bta_hf_client_cb.scb.sco_idx); + + if (bta_hf_client_cb.scb.sco_idx != BTM_INVALID_SCO_INDEX) { + bta_hf_client_sco_event(BTA_HF_CLIENT_SCO_CLOSE_E); + } +} + +#endif /* #if (BTA_HF_INCLUDED == TRUE) */ diff --git a/lib/bt/host/bluedroid/bta/hf_client/bta_hf_client_sdp.c b/lib/bt/host/bluedroid/bta/hf_client/bta_hf_client_sdp.c new file mode 100644 index 00000000..5ed7e443 --- /dev/null +++ b/lib/bt/host/bluedroid/bta/hf_client/bta_hf_client_sdp.c @@ -0,0 +1,367 @@ +/****************************************************************************** + * + * Copyright (c) 2014 The Android Open Source Project + * Copyright (C) 2003-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains the audio gateway functions performing SDP + * operations. + * + ******************************************************************************/ + +#include +#include "bta/bta_api.h" +#include "bta/bta_sys.h" +#include "common/bt_defs.h" +#include "bta/bta_hf_client_api.h" +#include "bta_hf_client_int.h" +#include "osi/allocator.h" + +#if (BTA_HF_INCLUDED == TRUE) +/* Number of protocol elements in protocol element list. */ +#define BTA_HF_CLIENT_NUM_PROTO_ELEMS 2 + +/* Number of elements in service class id list. */ +#define BTA_HF_CLIENT_NUM_SVC_ELEMS 2 + +/******************************************************************************* +** +** Function bta_hf_client_sdp_cback +** +** Description SDP callback function. +** +** +** Returns void +** +*******************************************************************************/ +static void bta_hf_client_sdp_cback(UINT16 status) +{ + tBTA_HF_CLIENT_DISC_RESULT *p_buf; + UINT16 event; + + APPL_TRACE_DEBUG("bta_hf_client_sdp_cback status:0x%x", status); + + /* set event according to int/acp */ + if (bta_hf_client_cb.scb.role == BTA_HF_CLIENT_ACP) { + event = BTA_HF_CLIENT_DISC_ACP_RES_EVT; + } else { + event = BTA_HF_CLIENT_DISC_INT_RES_EVT; + } + + if ((p_buf = (tBTA_HF_CLIENT_DISC_RESULT *) osi_malloc(sizeof(tBTA_HF_CLIENT_DISC_RESULT))) != NULL) { + p_buf->hdr.event = event; + p_buf->status = status; + bta_sys_sendmsg(p_buf); + } +} + +/****************************************************************************** +** +** Function bta_hf_client_add_record +** +** Description This function is called by a server application to add +** HFP Client information to an SDP record. Prior to +** calling this function the application must call +** SDP_CreateRecord() to create an SDP record. +** +** Returns TRUE if function execution succeeded, +** FALSE if function execution failed. +** +******************************************************************************/ +BOOLEAN bta_hf_client_add_record(char *p_service_name, UINT8 scn, + tBTA_HF_CLIENT_FEAT features, UINT32 sdp_handle) +{ + tSDP_PROTOCOL_ELEM proto_elem_list[BTA_HF_CLIENT_NUM_PROTO_ELEMS]; + UINT16 svc_class_id_list[BTA_HF_CLIENT_NUM_SVC_ELEMS]; + UINT16 browse_list[] = {UUID_SERVCLASS_PUBLIC_BROWSE_GROUP}; + UINT16 version; + UINT16 profile_uuid; + BOOLEAN result = TRUE; + UINT8 buf[2]; + UINT16 sdp_features = 0; + + APPL_TRACE_DEBUG("bta_hf_client_add_record"); + + memset( proto_elem_list, 0 , BTA_HF_CLIENT_NUM_PROTO_ELEMS * sizeof(tSDP_PROTOCOL_ELEM)); + + /* add the protocol element sequence */ + proto_elem_list[0].protocol_uuid = UUID_PROTOCOL_L2CAP; + proto_elem_list[0].num_params = 0; + proto_elem_list[1].protocol_uuid = UUID_PROTOCOL_RFCOMM; + proto_elem_list[1].num_params = 1; + proto_elem_list[1].params[0] = scn; + result &= SDP_AddProtocolList(sdp_handle, BTA_HF_CLIENT_NUM_PROTO_ELEMS, proto_elem_list); + + /* add service class id list */ + svc_class_id_list[0] = UUID_SERVCLASS_HF_HANDSFREE; + svc_class_id_list[1] = UUID_SERVCLASS_GENERIC_AUDIO; + result &= SDP_AddServiceClassIdList(sdp_handle, BTA_HF_CLIENT_NUM_SVC_ELEMS, svc_class_id_list); + + /* add profile descriptor list */ + profile_uuid = UUID_SERVCLASS_HF_HANDSFREE; + version = HFP_VERSION_1_8; + + result &= SDP_AddProfileDescriptorList(sdp_handle, profile_uuid, version); + + /* add service name */ + if (p_service_name != NULL && p_service_name[0] != 0) { + result &= SDP_AddAttribute(sdp_handle, ATTR_ID_SERVICE_NAME, TEXT_STR_DESC_TYPE, + (UINT32)(strlen(p_service_name) + 1), (UINT8 *) p_service_name); + } + + /* add features */ + if (features & BTA_HF_CLIENT_FEAT_ECNR) { + sdp_features |= BTA_HF_CLIENT_FEAT_ECNR; + } + + if (features & BTA_HF_CLIENT_FEAT_3WAY) { + sdp_features |= BTA_HF_CLIENT_FEAT_3WAY; + } + + if (features & BTA_HF_CLIENT_FEAT_CLI) { + sdp_features |= BTA_HF_CLIENT_FEAT_CLI; + } + + if (features & BTA_HF_CLIENT_FEAT_VREC) { + sdp_features |= BTA_HF_CLIENT_FEAT_VREC; + } + + if (features & BTA_HF_CLIENT_FEAT_VOL) { + sdp_features |= BTA_HF_CLIENT_FEAT_VOL; + } + + /* Codec bit position is different in SDP (bit 5) and in BRSF (bit 7) */ + if (features & BTA_HF_CLIENT_FEAT_CODEC) { + sdp_features |= 0x0020; + } + + UINT16_TO_BE_FIELD(buf, sdp_features); + result &= SDP_AddAttribute(sdp_handle, ATTR_ID_SUPPORTED_FEATURES, UINT_DESC_TYPE, 2, buf); + + /* add browse group list */ + result &= SDP_AddUuidSequence(sdp_handle, ATTR_ID_BROWSE_GROUP_LIST, 1, browse_list); + + return result; +} + +/******************************************************************************* +** +** Function bta_hf_client_create_record +** +** Description Create SDP record for registered service. +** +** +** Returns void +** +*******************************************************************************/ +void bta_hf_client_create_record(tBTA_HF_CLIENT_DATA *p_data) +{ + /* add sdp record if not already registered */ + if (bta_hf_client_cb.sdp_handle == 0) { + bta_hf_client_cb.sdp_handle = SDP_CreateRecord(); + bta_hf_client_cb.scn = BTM_AllocateSCN(); + bta_hf_client_add_record(p_data->api_register.name, + bta_hf_client_cb.scn, + p_data->api_register.features, + bta_hf_client_cb.sdp_handle); + + bta_sys_add_uuid(UUID_SERVCLASS_HF_HANDSFREE); + } + +} + +/******************************************************************************* +** +** Function bta_hf_client_del_record +** +** Description Delete SDP record for registered service. +** +** +** Returns void +** +*******************************************************************************/ +void bta_hf_client_del_record(tBTA_HF_CLIENT_DATA *p_data) +{ + UNUSED(p_data); + + APPL_TRACE_DEBUG("bta_hf_client_del_record"); + + if (bta_hf_client_cb.sdp_handle != 0) { + SDP_DeleteRecord(bta_hf_client_cb.sdp_handle); + bta_hf_client_cb.sdp_handle = 0; + BTM_FreeSCN(bta_hf_client_cb.scn); + BTM_SecClrService(BTM_SEC_SERVICE_HF_HANDSFREE); + bta_sys_remove_uuid(UUID_SERVCLASS_HF_HANDSFREE); + } +} + +/******************************************************************************* +** +** Function bta_hf_client_sdp_find_attr +** +** Description Process SDP discovery results to find requested attribute +** +** +** Returns TRUE if results found, FALSE otherwise. +** +*******************************************************************************/ +BOOLEAN bta_hf_client_sdp_find_attr(void) +{ + tSDP_DISC_REC *p_rec = NULL; + tSDP_DISC_ATTR *p_attr; + tSDP_PROTOCOL_ELEM pe; + BOOLEAN result = FALSE; + + bta_hf_client_cb.scb.peer_version = HFP_VERSION_1_1; /* Default version */ + + /* loop through all records we found */ + while (TRUE) { + /* get next record; if none found, we're done */ + if ((p_rec = SDP_FindServiceInDb(bta_hf_client_cb.scb.p_disc_db, UUID_SERVCLASS_AG_HANDSFREE, p_rec)) == NULL) { + break; + } + + /* get scn from proto desc list if initiator */ + if (bta_hf_client_cb.scb.role == BTA_HF_CLIENT_INT) { + if (SDP_FindProtocolListElemInRec(p_rec, UUID_PROTOCOL_RFCOMM, &pe)) { + bta_hf_client_cb.scb.peer_scn = (UINT8) pe.params[0]; + } else { + continue; + } + } + + /* get profile version (if failure, version parameter is not updated) */ + SDP_FindProfileVersionInRec(p_rec, UUID_SERVCLASS_HF_HANDSFREE, &bta_hf_client_cb.scb.peer_version); + + /* get features */ + if ((p_attr = SDP_FindAttributeInRec(p_rec, ATTR_ID_SUPPORTED_FEATURES)) != NULL) { + /* Found attribute. Get value. */ + /* There might be race condition between SDP and BRSF. */ + /* Do not update if we already received BRSF. */ + if (bta_hf_client_cb.scb.peer_features == 0) { + bta_hf_client_cb.scb.peer_features = p_attr->attr_value.v.u16; + + /* SDP and BRSF WBS bit are different, correct it if set */ + if (bta_hf_client_cb.scb.peer_features & 0x0020) { + bta_hf_client_cb.scb.peer_features &= ~0x0020; + bta_hf_client_cb.scb.peer_features |= BTA_HF_CLIENT_PEER_CODEC; + } + + /* get network for ability to reject calls */ + if ((p_attr = SDP_FindAttributeInRec(p_rec, ATTR_ID_NETWORK)) != NULL) { + if (p_attr->attr_value.v.u16 == 0x01) { + bta_hf_client_cb.scb.peer_features |= BTA_HF_CLIENT_PEER_REJECT; + } + } + } + } + + /* found what we needed */ + result = TRUE; + break; + } + + APPL_TRACE_DEBUG("%s peer_version=0x%x peer_features=0x%x", + __FUNCTION__, bta_hf_client_cb.scb.peer_version, + bta_hf_client_cb.scb.peer_features); + + return result; +} + +/******************************************************************************* +** +** Function bta_hf_client_do_disc +** +** Description Do service discovery. +** +** +** Returns void +** +*******************************************************************************/ +void bta_hf_client_do_disc(void) +{ + tSDP_UUID uuid_list[2]; + UINT16 num_uuid = 1; + UINT16 attr_list[4]; + UINT8 num_attr; + BOOLEAN db_inited = FALSE; + + /* initiator; get proto list and features */ + if (bta_hf_client_cb.scb.role == BTA_HF_CLIENT_INT) { + attr_list[0] = ATTR_ID_SERVICE_CLASS_ID_LIST; + attr_list[1] = ATTR_ID_PROTOCOL_DESC_LIST; + attr_list[2] = ATTR_ID_BT_PROFILE_DESC_LIST; + attr_list[3] = ATTR_ID_SUPPORTED_FEATURES; + num_attr = 4; + uuid_list[0].uu.uuid16 = UUID_SERVCLASS_AG_HANDSFREE; + } + /* acceptor; get features */ + else { + attr_list[0] = ATTR_ID_SERVICE_CLASS_ID_LIST; + attr_list[1] = ATTR_ID_BT_PROFILE_DESC_LIST; + attr_list[2] = ATTR_ID_SUPPORTED_FEATURES; + num_attr = 3; + uuid_list[0].uu.uuid16 = UUID_SERVCLASS_AG_HANDSFREE; + } + + /* allocate buffer for sdp database */ + bta_hf_client_cb.scb.p_disc_db = (tSDP_DISCOVERY_DB *) osi_malloc(BT_DEFAULT_BUFFER_SIZE); + + if (bta_hf_client_cb.scb.p_disc_db) { + /* set up service discovery database; attr happens to be attr_list len */ + uuid_list[0].len = LEN_UUID_16; + uuid_list[1].len = LEN_UUID_16; + db_inited = SDP_InitDiscoveryDb(bta_hf_client_cb.scb.p_disc_db, BT_DEFAULT_BUFFER_SIZE, num_uuid, + uuid_list, num_attr, attr_list); + } + + if (db_inited) { + /*Service discovery not initiated */ + db_inited = SDP_ServiceSearchAttributeRequest(bta_hf_client_cb.scb.peer_addr, + bta_hf_client_cb.scb.p_disc_db, bta_hf_client_sdp_cback); + } + + if (!db_inited) { + /*free discover db */ + bta_hf_client_free_db(NULL); + /* sent failed event */ + bta_hf_client_sm_execute(BTA_HF_CLIENT_DISC_FAIL_EVT, NULL); + } + +} + +/******************************************************************************* +** +** Function bta_hf_client_free_db +** +** Description Free discovery database. +** +** +** Returns void +** +*******************************************************************************/ +void bta_hf_client_free_db(tBTA_HF_CLIENT_DATA *p_data) +{ + UNUSED(p_data); + + if (bta_hf_client_cb.scb.p_disc_db != NULL) { + osi_free(bta_hf_client_cb.scb.p_disc_db); + bta_hf_client_cb.scb.p_disc_db = NULL; + } +} +#endif /* #if (BTA_HF_INCLUDED == TRUE) */ diff --git a/lib/bt/host/bluedroid/bta/hf_client/include/bta_hf_client_at.h b/lib/bt/host/bluedroid/bta/hf_client/include/bta_hf_client_at.h new file mode 100644 index 00000000..2cf38acc --- /dev/null +++ b/lib/bt/host/bluedroid/bta/hf_client/include/bta_hf_client_at.h @@ -0,0 +1,110 @@ +/****************************************************************************** + * + * Copyright (c) 2014 The Android Open Source Project + * Copyright (C) 2003-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/***************************************************************************** +** Data types +*****************************************************************************/ +#include "common/bt_target.h" +#if (BTA_HF_INCLUDED == TRUE) + +/* ASCII character string of arguments to the AT command */ +#define BTA_HF_CLIENT_AT_MAX_LEN 512 + +/* callback function executed when command is parsed */ +typedef void (tBTA_AG_AT_CMD_CBACK)(void *p_user, UINT16 cmd, UINT8 arg_type, + char *p_arg, INT16 int_arg); + +/* callback function executed to send "ERROR" result code */ +typedef void (tBTA_AG_AT_ERR_CBACK)(void *p_user, BOOLEAN unknown, char *p_arg); + +enum { + BTA_HF_CLIENT_AT_NONE, + BTA_HF_CLIENT_AT_BRSF, + BTA_HF_CLIENT_AT_BAC, + BTA_HF_CLIENT_AT_CIND, + BTA_HF_CLIENT_AT_CIND_STATUS, + BTA_HF_CLIENT_AT_CMER, + BTA_HF_CLIENT_AT_CHLD, + BTA_HF_CLIENT_AT_CMEE, + BTA_HF_CLIENT_AT_BIA, + BTA_HF_CLIENT_AT_CLIP, + BTA_HF_CLIENT_AT_CCWA, + BTA_HF_CLIENT_AT_COPS, + BTA_HF_CLIENT_AT_CLCC, + BTA_HF_CLIENT_AT_BVRA, + BTA_HF_CLIENT_AT_VGS, + BTA_HF_CLIENT_AT_VGM, + BTA_HF_CLIENT_AT_ATD, + BTA_HF_CLIENT_AT_BLDN, + BTA_HF_CLIENT_AT_ATA, + BTA_HF_CLIENT_AT_CHUP, + BTA_HF_CLIENT_AT_BTRH, + BTA_HF_CLIENT_AT_VTS, + BTA_HF_CLIENT_AT_BCC, + BTA_HF_CLIENT_AT_BCS, + BTA_HF_CLIENT_AT_CNUM, + BTA_HF_CLIENT_AT_NREC, + BTA_HF_CLIENT_AT_BINP, + BTA_HF_CLIENT_AT_XAPL, + BTA_HF_CLIENT_AT_IPHONEACCEV, +}; + +typedef UINT8 tBTA_HF_CLIENT_AT_CMD; + +/* Maximum combined buffer for received AT events string */ +#define BTA_HF_CLIENT_AT_PARSER_MAX_LEN 4096 + +/* This structure holds prepared AT command queued for sending */ +struct queued_at_cmd { + tBTA_HF_CLIENT_AT_CMD cmd; + char buf[BTA_HF_CLIENT_AT_MAX_LEN]; + UINT16 buf_len; + struct queued_at_cmd *next; +}; +typedef struct queued_at_cmd tBTA_HF_CLIENT_AT_QCMD; + +/* Maximum number of indicators */ +#define BTA_HF_CLIENT_AT_INDICATOR_COUNT 20 + +/* AT command parsing control block */ +typedef struct { + char buf[BTA_HF_CLIENT_AT_PARSER_MAX_LEN + 1]; /* extra byte to always have \0 at the end */ + unsigned int offset; + tBTA_HF_CLIENT_AT_CMD current_cmd; + tBTA_HF_CLIENT_AT_QCMD *queued_cmd; + + TIMER_LIST_ENT resp_timer; /* AT response timer */ + BOOLEAN resp_timer_on; /* TRUE if AT response timer is active */ + + TIMER_LIST_ENT hold_timer; /* AT hold timer */ + BOOLEAN hold_timer_on; /* TRUE if AT hold timer is active */ + + /* CIND: lookup table to store the sequence of incoming indicators and their values + so when their values come later, we know which value in sequence match certain indicator */ + int indicator_lookup[BTA_HF_CLIENT_AT_INDICATOR_COUNT]; + +} tBTA_HF_CLIENT_AT_CB; + +/***************************************************************************** +** Functions +*****************************************************************************/ + +void bta_hf_client_at_init(void); +void bta_hf_client_at_reset(void); +#endif /* #if (BTA_HF_INCLUDED == TRUE) */ diff --git a/lib/bt/host/bluedroid/bta/hf_client/include/bta_hf_client_int.h b/lib/bt/host/bluedroid/bta/hf_client/include/bta_hf_client_int.h new file mode 100644 index 00000000..0732a96c --- /dev/null +++ b/lib/bt/host/bluedroid/bta/hf_client/include/bta_hf_client_int.h @@ -0,0 +1,316 @@ +/****************************************************************************** + * + * Copyright (c) 2014 The Android Open Source Project + * Copyright (C) 2003-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +#include "bta/bta_sys.h" +#include "bta/bta_api.h" +#include "bta/bta_hf_client_api.h" +#include "bta_hf_client_at.h" + +#if (BTA_HF_INCLUDED == TRUE) +/***************************************************************************** +** Constants +*****************************************************************************/ +#define HFP_VERSION_1_1 0x0101 +#define HFP_VERSION_1_5 0x0105 +#define HFP_VERSION_1_6 0x0106 +#define HFP_VERSION_1_7 0x0107 +#define HFP_VERSION_1_8 0x0108 + +/* RFCOMM MTU SIZE */ +#define BTA_HF_CLIENT_MTU 256 + +/* profile role for connection */ +#define BTA_HF_CLIENT_ACP 0 /* accepted connection */ +#define BTA_HF_CLIENT_INT 1 /* initiating connection */ + +/* Timer to wait for retry in case of collision */ +#ifndef BTA_HF_CLIENT_COLLISION_TIMER +#define BTA_HF_CLIENT_COLLISION_TIMER 2411 +#endif + +enum { + /* these events are handled by the state machine */ + BTA_HF_CLIENT_API_REGISTER_EVT = BTA_SYS_EVT_START(BTA_ID_HS), + BTA_HF_CLIENT_API_DEREGISTER_EVT, + BTA_HF_CLIENT_API_OPEN_EVT, + BTA_HF_CLIENT_API_CLOSE_EVT, + BTA_HF_CLIENT_API_AUDIO_OPEN_EVT, + BTA_HF_CLIENT_API_AUDIO_CLOSE_EVT, + BTA_HF_CLIENT_RFC_OPEN_EVT, + BTA_HF_CLIENT_RFC_CLOSE_EVT, + BTA_HF_CLIENT_RFC_SRV_CLOSE_EVT, + BTA_HF_CLIENT_RFC_DATA_EVT, + BTA_HF_CLIENT_DISC_ACP_RES_EVT, + BTA_HF_CLIENT_DISC_INT_RES_EVT, + BTA_HF_CLIENT_DISC_OK_EVT, + BTA_HF_CLIENT_DISC_FAIL_EVT, + BTA_HF_CLIENT_SCO_OPEN_EVT, + BTA_HF_CLIENT_SCO_CLOSE_EVT, + BTA_HF_CLIENT_SEND_AT_CMD_EVT, +#if (BTM_SCO_HCI_INCLUDED == TRUE ) + BTA_HF_CLIENT_CI_SCO_DATA_EVT, + BTA_HF_CLIENT_PKT_NUMS_GET_EVT, +#endif /* (BTM_SCO_HCI_INCLUDED == TRUE ) */ + BTA_HF_CLIENT_MAX_EVT, + + /* these events are handled outside of the state machine */ + BTA_HF_CLIENT_API_ENABLE_EVT, + BTA_HF_CLIENT_API_DISABLE_EVT +}; + +/***************************************************************************** +** Data types +*****************************************************************************/ + +/* data type for BTA_HF_CLIENT_API_ENABLE_EVT */ +typedef struct { + BT_HDR hdr; + tBTA_HF_CLIENT_CBACK *p_cback; +} tBTA_HF_CLIENT_API_ENABLE; + +/* data type for BTA_HF_CLIENT_API_REGISTER_EVT */ +typedef struct { + BT_HDR hdr; + tBTA_HF_CLIENT_CBACK *p_cback; + tBTA_SEC sec_mask; + tBTA_HF_CLIENT_FEAT features; + char name[BTA_SERVICE_NAME_LEN + 1]; +} tBTA_HF_CLIENT_API_REGISTER; + +/* data type for BTA_HF_CLIENT_API_OPEN_EVT */ +typedef struct { + BT_HDR hdr; + BD_ADDR bd_addr; + tBTA_SEC sec_mask; +} tBTA_HF_CLIENT_API_OPEN; + +/* data type for BTA_HF_CLIENT_DISC_RESULT_EVT */ +typedef struct { + BT_HDR hdr; + UINT16 status; +} tBTA_HF_CLIENT_DISC_RESULT; + +/* data type for RFCOMM events */ +typedef struct { + BT_HDR hdr; + UINT16 port_handle; +} tBTA_HF_CLIENT_RFC; + +/* generic purpose data type for other events */ +typedef struct { + BT_HDR hdr; + BOOLEAN bool_val; + UINT8 uint8_val; + UINT32 uint32_val1; + UINT32 uint32_val2; + char str[BTA_HF_CLIENT_MAX_LEN + 1]; +} tBTA_HF_CLIENT_DATA_VAL; + +/* data type for BTA_HF_CLIENT_PKT_NUMS_GET_EVT */ +typedef struct { + BT_HDR hdr; + UINT16 sync_conn_handle; +} tBTA_HF_CLIENT_PKT_STAT_GET; + +/* union of all event datatypes */ +typedef union { + BT_HDR hdr; + tBTA_HF_CLIENT_API_ENABLE api_enable; + tBTA_HF_CLIENT_API_REGISTER api_register; + tBTA_HF_CLIENT_API_OPEN api_open; + tBTA_HF_CLIENT_DISC_RESULT disc_result; + tBTA_HF_CLIENT_RFC rfc; + tBTA_HF_CLIENT_DATA_VAL val; + tBTA_HF_CLIENT_PKT_STAT_GET pkt_stat; + +} tBTA_HF_CLIENT_DATA; + +/* type for each service control block */ +typedef struct { + UINT16 serv_handle; /* RFCOMM server handle */ + BD_ADDR peer_addr; /* peer bd address */ + tSDP_DISCOVERY_DB *p_disc_db; /* pointer to discovery database */ + UINT16 conn_handle; /* RFCOMM handle of connected service */ + tBTA_SEC serv_sec_mask; /* server security mask */ + tBTA_SEC cli_sec_mask; /* client security mask */ + tBTA_HF_CLIENT_FEAT features; /* features registered by application */ + tBTA_HF_CLIENT_PEER_FEAT peer_features; /* peer device features */ + tBTA_HF_CLIENT_CHLD_FEAT chld_features; /* call handling features */ + UINT16 peer_version; /* profile version of peer device */ + UINT8 peer_scn; /* peer scn */ + UINT8 role; /* initiator/acceptor role */ + UINT16 sco_idx; /* SCO handle */ + UINT8 sco_state; /* SCO state variable */ + BOOLEAN sco_close_rfc; /* TRUE if also close RFCOMM after SCO */ + BOOLEAN retry_with_sco_only; + BOOLEAN deregister; /* TRUE if service shutting down */ + BOOLEAN svc_conn; /* set to TRUE when service level connection is up */ + BOOLEAN send_at_reply; /* set to TRUE to notify framework about AT results */ + tBTA_HF_CLIENT_AT_CB at_cb; /* AT Parser control block */ + UINT8 state; /* state machine state */ + tBTM_SCO_CODEC_TYPE negotiated_codec; /* negotiated codec */ + TIMER_LIST_ENT colli_timer; /* Collision timer */ + BOOLEAN colli_tmr_on; /* TRUE if collision timer is active */ + + UINT16 in_pkt_len; + UINT16 out_pkt_len; + UINT8 link_type; /* BTM_LINK_TYPE_SCO or BTM_LINK_TYPE_ESCO */ + UINT8 tx_interval; + UINT8 retrans_window; + UINT8 air_mode; +} tBTA_HF_CLIENT_SCB; + +/* sco states */ +enum { + BTA_HF_CLIENT_SCO_SHUTDOWN_ST, /* no listening, no connection */ + BTA_HF_CLIENT_SCO_LISTEN_ST, /* listening */ + BTA_HF_CLIENT_SCO_OPENING_ST, /* connection opening */ + BTA_HF_CLIENT_SCO_OPEN_CL_ST, /* opening connection being closed */ + BTA_HF_CLIENT_SCO_OPEN_ST, /* open */ + BTA_HF_CLIENT_SCO_CLOSING_ST, /* closing */ + BTA_HF_CLIENT_SCO_CLOSE_OP_ST, /* closing sco being opened */ + BTA_HF_CLIENT_SCO_SHUTTING_ST /* sco shutting down */ +}; + +/* type for AG control block */ +typedef struct { + tBTA_HF_CLIENT_SCB scb; /* service control block */ + UINT32 sdp_handle; + UINT8 scn; + tBTA_HF_CLIENT_CBACK *p_cback; /* application callback */ + BOOLEAN msbc_enabled; +} tBTA_HF_CLIENT_CB; + +/***************************************************************************** +** Global data +*****************************************************************************/ + +/* control block declaration */ +#if BTA_DYNAMIC_MEMORY == FALSE +extern tBTA_HF_CLIENT_CB bta_hf_client_cb; +#else +extern tBTA_HF_CLIENT_CB *bta_hf_client_cb_ptr; +#define bta_hf_client_cb (*bta_hf_client_cb_ptr) +#endif + +/***************************************************************************** +** Function prototypes +*****************************************************************************/ + +/* main functions */ +extern void bta_hf_client_scb_init(void); +extern void bta_hf_client_scb_disable(void); +extern BOOLEAN bta_hf_client_hdl_event(BT_HDR *p_msg); +extern void bta_hf_client_sm_execute(UINT16 event, + tBTA_HF_CLIENT_DATA *p_data); +extern void bta_hf_client_slc_seq(BOOLEAN error); +extern void bta_hf_client_collision_cback (tBTA_SYS_CONN_STATUS status, UINT8 id, + UINT8 app_id, BD_ADDR peer_addr); +extern void bta_hf_client_resume_open (void); + +/* SDP functions */ +extern BOOLEAN bta_hf_client_add_record(char *p_service_name, + UINT8 scn, tBTA_HF_CLIENT_FEAT features, + UINT32 sdp_handle); +extern void bta_hf_client_create_record(tBTA_HF_CLIENT_DATA *p_data); +extern void bta_hf_client_del_record(tBTA_HF_CLIENT_DATA *p_data); +extern BOOLEAN bta_hf_client_sdp_find_attr(void); +extern void bta_hf_client_do_disc(void); +extern void bta_hf_client_free_db(tBTA_HF_CLIENT_DATA *p_data); + +/* RFCOMM functions */ +extern void bta_hf_client_setup_port(UINT16 handle); +extern void bta_hf_client_start_server(void); +extern void bta_hf_client_close_server(void); +extern void bta_hf_client_rfc_do_open(tBTA_HF_CLIENT_DATA *p_data); +extern void bta_hf_client_rfc_do_close(tBTA_HF_CLIENT_DATA *p_data); + +/* SCO functions */ +extern void bta_hf_client_sco_listen(tBTA_HF_CLIENT_DATA *p_data); +extern void bta_hf_client_sco_shutdown(tBTA_HF_CLIENT_DATA *p_data); +extern void bta_hf_client_sco_conn_open(tBTA_HF_CLIENT_DATA *p_data); +extern void bta_hf_client_sco_conn_close(tBTA_HF_CLIENT_DATA *p_data); +extern void bta_hf_client_sco_open(tBTA_HF_CLIENT_DATA *p_data); +extern void bta_hf_client_sco_close(tBTA_HF_CLIENT_DATA *p_data); +extern void bta_hf_client_cback_sco(UINT8 event); + +/* AT command functions */ +extern void bta_hf_client_at_parse(char *buf, unsigned int len); +extern void bta_hf_client_send_at_brsf(void); +extern void bta_hf_client_send_at_bac(void); +extern void bta_hf_client_send_at_cind(BOOLEAN status); +extern void bta_hf_client_send_at_cmer(BOOLEAN activate); +extern void bta_hf_client_send_at_chld(char cmd, UINT32 idx); +extern void bta_hf_client_send_at_clip(BOOLEAN activate); +extern void bta_hf_client_send_at_ccwa(BOOLEAN activate); +extern void bta_hf_client_send_at_cmee(BOOLEAN activate); +extern void bta_hf_client_send_at_cops(BOOLEAN query); +extern void bta_hf_client_send_at_clcc(void); +extern void bta_hf_client_send_at_bvra(BOOLEAN enable); +extern void bta_hf_client_send_at_vgs(UINT32 volume); +extern void bta_hf_client_send_at_vgm(UINT32 volume); +extern void bta_hf_client_send_at_atd(char *number, UINT32 memory); +extern void bta_hf_client_send_at_bldn(void); +extern void bta_hf_client_send_at_ata(void); +extern void bta_hf_client_send_at_chup(void); +extern void bta_hf_client_send_at_btrh(BOOLEAN query, UINT32 val); +extern void bta_hf_client_send_at_vts(char code); +extern void bta_hf_client_send_at_bcc(void); +extern void bta_hf_client_send_at_bcs(UINT32 codec); +extern void bta_hf_client_send_at_cnum(void); +extern void bta_hf_client_send_at_nrec(void); +extern void bta_hf_client_send_at_binp(UINT32 action); +extern void bta_hf_client_send_at_bia(void); +extern void bta_hf_client_send_at_xapl(char *information, UINT32 features); +extern void bta_hf_client_send_at_iphoneaccev(UINT32 bat_level, BOOLEAN docked); + +/* Action functions */ +extern void bta_hf_client_register(tBTA_HF_CLIENT_DATA *p_data); +extern void bta_hf_client_deregister(tBTA_HF_CLIENT_DATA *p_data); +extern void bta_hf_client_start_dereg(tBTA_HF_CLIENT_DATA *p_data); +extern void bta_hf_client_start_close(tBTA_HF_CLIENT_DATA *p_data); +extern void bta_hf_client_start_open(tBTA_HF_CLIENT_DATA *p_data); +extern void bta_hf_client_rfc_acp_open(tBTA_HF_CLIENT_DATA *p_data); +extern void bta_hf_client_rfc_open(tBTA_HF_CLIENT_DATA *p_data); +extern void bta_hf_client_rfc_fail(tBTA_HF_CLIENT_DATA *p_data); +extern void bta_hf_client_disc_fail(tBTA_HF_CLIENT_DATA *p_data); +extern void bta_hf_client_open_fail(tBTA_HF_CLIENT_DATA *p_data); +extern void bta_hf_client_rfc_close(tBTA_HF_CLIENT_DATA *p_data); +extern void bta_hf_client_disc_acp_res(tBTA_HF_CLIENT_DATA *p_data); +extern void bta_hf_client_rfc_data(tBTA_HF_CLIENT_DATA *p_data); +extern void bta_hf_client_disc_int_res(tBTA_HF_CLIENT_DATA *p_data); +extern void bta_hf_client_svc_conn_open(tBTA_HF_CLIENT_DATA *p_data); +extern void bta_hf_client_ind(tBTA_HF_CLIENT_IND_TYPE type, UINT16 value); +extern void bta_hf_client_evt_val(tBTA_HF_CLIENT_EVT type, UINT16 value); +extern void bta_hf_client_operator_name(char *name); +extern void bta_hf_client_clip(char *number); +extern void bta_hf_client_ccwa(char *number); +extern void bta_hf_client_at_result(tBTA_HF_CLIENT_AT_RESULT_TYPE type, UINT16 cme); +extern void bta_hf_client_clcc(UINT32 idx, BOOLEAN incoming, UINT8 status, BOOLEAN mpty, char *number); +extern void bta_hf_client_cnum(char *number, UINT16 service); +extern void bta_hf_client_binp(char *number); + +/* Commands handling functions */ +extern void bta_hf_client_dial(tBTA_HF_CLIENT_DATA *p_data); +extern void bta_hf_client_send_at_cmd(tBTA_HF_CLIENT_DATA *p_data); +#if (BTM_SCO_HCI_INCLUDED == TRUE ) +extern void bta_hf_client_pkt_stat_nums(tBTA_HF_CLIENT_DATA *p_data); +extern void bta_hf_client_ci_sco_data(tBTA_HF_CLIENT_DATA *p_data); +#endif /* (BTM_SCO_HCI_INCLUDED == TRUE ) */ +#endif /* #if (BTA_HF_INCLUDED == TRUE) */ diff --git a/lib/bt/host/bluedroid/bta/hh/bta_hh_act.c b/lib/bt/host/bluedroid/bta/hh/bta_hh_act.c new file mode 100644 index 00000000..604b5e95 --- /dev/null +++ b/lib/bt/host/bluedroid/bta/hh/bta_hh_act.c @@ -0,0 +1,1280 @@ +/****************************************************************************** + * + * Copyright (C) 2005-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains the HID host action functions. + * + ******************************************************************************/ + +#include "common/bt_target.h" + +#if defined(BTA_HH_INCLUDED) && (BTA_HH_INCLUDED == TRUE) + +#include + +#include "bta/bta_sys.h" +#include "stack/btm_api.h" +#include "stack/l2c_api.h" +#include "bta_hh_int.h" +#include "bta/bta_hh_co.h" +#include "bta/utl.h" +#include "osi/allocator.h" + +/***************************************************************************** +** Constants +*****************************************************************************/ + + +/***************************************************************************** +** Local Function prototypes +*****************************************************************************/ +static void bta_hh_cback (UINT8 dev_handle, BD_ADDR addr, UINT8 event, + UINT32 data, BT_HDR *pdata); +static tBTA_HH_STATUS bta_hh_get_trans_status(UINT32 result); + +#if BTA_HH_DEBUG +static char *bta_hh_get_w4_event(UINT16 event); +static char *bta_hh_hid_event_name(UINT16 event); +#endif + +/***************************************************************************** +** Action Functions +*****************************************************************************/ +/******************************************************************************* +** +** Function bta_hh_api_enable +** +** Description Perform necessary operations to enable HID host. +** +** +** Returns void +** +*******************************************************************************/ +void bta_hh_api_enable(tBTA_HH_DATA *p_data) +{ + tBTA_HH_STATUS status = BTA_HH_ERR; + UINT8 xx; + + /* initialize BTE HID */ + HID_HostInit(); + + memset(&bta_hh_cb, 0, sizeof(tBTA_HH_CB)); + + HID_HostSetSecurityLevel("", p_data->api_enable.sec_mask); + + /* Register with L2CAP */ + if ( HID_HostRegister (bta_hh_cback) == HID_SUCCESS) { + /* store parameters */ + bta_hh_cb.p_cback = p_data->api_enable.p_cback; + + status = BTA_HH_OK; + /* initialize device CB */ + for (xx = 0; xx < BTA_HH_MAX_DEVICE; xx ++) { + bta_hh_cb.kdev[xx].state = BTA_HH_IDLE_ST; + bta_hh_cb.kdev[xx].hid_handle = BTA_HH_INVALID_HANDLE; + bta_hh_cb.kdev[xx].index = xx; + } + + /* initialize control block map */ + for (xx = 0; xx < BTA_HH_MAX_KNOWN; xx ++) { + bta_hh_cb.cb_index[xx] = BTA_HH_IDX_INVALID; + } + } + +#if (BTA_HH_LE_INCLUDED == TRUE) + if (status == BTA_HH_OK) { + bta_hh_le_enable(); + } else +#endif + { + /* signal BTA call back event */ + (* bta_hh_cb.p_cback)(BTA_HH_ENABLE_EVT, (tBTA_HH *)&status); + } +} +/******************************************************************************* +** +** Function bta_hh_api_disable +** +** Description Perform necessary operations to disable HID host. +** +** +** Returns void +** +*******************************************************************************/ +void bta_hh_api_disable(void) +{ + UINT8 xx; + + /* service is not enabled */ + if (bta_hh_cb.p_cback == NULL) { + return; + } + + /* no live connection, signal DISC_CMPL_EVT directly */ + if (!bta_hh_cb.cnt_num) { + bta_hh_disc_cmpl(); + } else { /* otherwise, disconnect all live connections */ + bta_hh_cb.w4_disable = TRUE; + + for (xx = 0; xx < BTA_HH_MAX_DEVICE; xx ++) { + /* send API_CLOSE event to every connected device */ + if ( bta_hh_cb.kdev[xx].state == BTA_HH_CONN_ST ) { + /* disconnect all connected devices */ + bta_hh_sm_execute(&bta_hh_cb.kdev[xx], + BTA_HH_API_CLOSE_EVT, + NULL); + } + } + } + + return; +} + +/******************************************************************************* +** +** Function bta_hh_disc_cmpl +** +** Description All connections have been closed, disable service. +** +** +** Returns void +** +*******************************************************************************/ +void bta_hh_disc_cmpl(void) +{ + tBTA_HH_STATUS status = BTA_HH_OK; + + /* Deregister with lower layer */ + if (HID_HostDeregister() != HID_SUCCESS) { + status = BTA_HH_ERR; + } + +#if (BTA_HH_LE_INCLUDED == TRUE) + bta_hh_le_deregister(); + UNUSED(status); +#else + bta_hh_cleanup_disable(status); +#endif +} + +/******************************************************************************* +** +** Function bta_hh_sdp_cback +** +** Description SDP callback function. +** +** Returns void +** +*******************************************************************************/ +static void bta_hh_sdp_cback(UINT16 result, UINT16 attr_mask, + tHID_DEV_SDP_INFO *sdp_rec ) +{ + tBTA_HH_DEV_CB *p_cb = bta_hh_cb.p_cur; + UINT8 hdl = 0; + tBTA_HH_STATUS status = BTA_HH_ERR_SDP; + + /* make sure sdp succeeded and hh has not been disabled */ + if ((result == SDP_SUCCESS) && (p_cb != NULL)) { + /* security is required for the connection, add attr_mask bit*/ + if (p_cb->sec_mask) { + attr_mask |= HID_SEC_REQUIRED; + } + +#if BTA_HH_DEBUG + APPL_TRACE_EVENT("bta_hh_sdp_cback: p_cb: %p result 0x%02x, \ + attr_mask 0x%02x, handle %x", \ + p_cb, result, attr_mask, p_cb->hid_handle); +#endif + + /* check to see type of device is supported , and should not been added before */ + if (bta_hh_tod_spt(p_cb, sdp_rec->sub_class)) { + /* if not added before */ + if (p_cb->hid_handle == BTA_HH_INVALID_HANDLE) { + /* add device/update attr_mask information */ + if (HID_HostAddDev (p_cb->addr, attr_mask, &hdl) == HID_SUCCESS) { + status = BTA_HH_OK; + /* update cb_index[] map */ + bta_hh_cb.cb_index[hdl] = p_cb->index; + } else { + p_cb->app_id = 0; + } + } else { + hdl = p_cb->hid_handle; + } + /* else : incoming connection after SDP should update the SDP information as well */ + + if (p_cb->app_id != 0) { + /* update cb information with attr_mask, dscp_info etc. */ + bta_hh_add_device_to_list(p_cb, hdl, attr_mask, + &sdp_rec->dscp_info, + sdp_rec->sub_class, + sdp_rec->ssr_max_latency, + sdp_rec->ssr_min_tout, + p_cb->app_id); + + p_cb->dscp_info.ctry_code = sdp_rec->ctry_code; + + status = BTA_HH_OK; + } + + } else { /* type of device is not supported */ + status = BTA_HH_ERR_TOD_UNSPT; + } + } + + /* free disc_db when SDP is completed */ + utl_freebuf((void **)&bta_hh_cb.p_disc_db); + + /* send SDP_CMPL_EVT into state machine */ + bta_hh_sm_execute(p_cb, BTA_HH_SDP_CMPL_EVT, (tBTA_HH_DATA *)&status); + + return; +} +/******************************************************************************* +** +** Function bta_hh_di_sdp_cback +** +** Description SDP DI callback function. +** +** Returns void +** +*******************************************************************************/ +static void bta_hh_di_sdp_cback(UINT16 result) +{ + tBTA_HH_DEV_CB *p_cb = bta_hh_cb.p_cur; + tBTA_HH_STATUS status = BTA_HH_ERR_SDP; + tSDP_DI_GET_RECORD di_rec; + tHID_STATUS ret; +#if BTA_HH_DEBUG + APPL_TRACE_EVENT("bta_hh_di_sdp_cback: p_cb: %p result 0x%02x", p_cb, result); +#endif + + /* if DI record does not exist on remote device, vendor_id in tBTA_HH_DEV_DSCP_INFO will be + * set to 0xffff and we will allow the connection to go through. Spec mandates that DI + * record be set, but many HID devices do not set this. So for IOP purposes, we allow the + * connection to go through and update the DI record to invalid DI entry.*/ + if (((result == SDP_SUCCESS) || (result == SDP_NO_RECS_MATCH)) && (p_cb != NULL)) { + if (result == SDP_SUCCESS && SDP_GetNumDiRecords(bta_hh_cb.p_disc_db) != 0) { + /* always update information with primary DI record */ + if (SDP_GetDiRecord(1, &di_rec, bta_hh_cb.p_disc_db) == SDP_SUCCESS) { + bta_hh_update_di_info(p_cb, di_rec.rec.vendor, di_rec.rec.product, di_rec.rec.version, 0); + } + + } else { /* no DI recrod available */ + bta_hh_update_di_info(p_cb, BTA_HH_VENDOR_ID_INVALID, 0, 0, 0); + } + + if ((ret = HID_HostGetSDPRecord(p_cb->addr, + bta_hh_cb.p_disc_db, + p_bta_hh_cfg->sdp_db_size, + bta_hh_sdp_cback)) == HID_SUCCESS) { + status = BTA_HH_OK; + } else { +#if BTA_HH_DEBUG + APPL_TRACE_DEBUG ("bta_hh_di_sdp_cback: HID_HostGetSDPRecord failed: Status 0x%2x", + ret); +#endif + } + } + + + if (status != BTA_HH_OK) { + utl_freebuf((void **)&bta_hh_cb.p_disc_db); + /* send SDP_CMPL_EVT into state machine */ + bta_hh_sm_execute(p_cb, BTA_HH_SDP_CMPL_EVT, (tBTA_HH_DATA *)&status); + } + return; + +} + + +/******************************************************************************* +** +** Function bta_hh_start_sdp +** +** Description Start SDP service search, and obtain necessary SDP records. +** Only one SDP service search request is allowed at the same +** time. For every BTA_HhOpen API call, do SDP first unless SDP +** has been done previously. +** +** Returns void +** +*******************************************************************************/ +void bta_hh_start_sdp(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data) +{ + tBTA_HH_STATUS status = BTA_HH_ERR_SDP; + UINT8 hdl; + + p_cb->sec_mask = p_data->api_conn.sec_mask; + p_cb->mode = p_data->api_conn.mode; + p_cb->new_mode = p_data->api_conn.mode; + bta_hh_cb.p_cur = p_cb; + +#if (BTA_HH_LE_INCLUDED == TRUE) + if (bta_hh_is_le_device(p_cb, p_data->api_conn.bd_addr)) { + bta_hh_le_open_conn(p_cb, p_data->api_conn.bd_addr); + return; + } +#endif + + /* if previously virtually cabled device, skip SDP */ + if (p_cb->app_id) { + status = BTA_HH_OK; +#if BTA_HH_DEBUG + APPL_TRACE_DEBUG("bta_hh_start_sdp:: skip SDP for known devices"); +#endif + if (p_cb->hid_handle == BTA_HH_INVALID_HANDLE) { + if (HID_HostAddDev (p_cb->addr, p_cb->attr_mask, &hdl) \ + == HID_SUCCESS) { + /* update device CB with newly register device handle */ + bta_hh_add_device_to_list(p_cb, hdl, p_cb->attr_mask, NULL, + p_cb->sub_class, + p_cb->dscp_info.ssr_max_latency, + p_cb->dscp_info.ssr_min_tout, + p_cb->app_id); + /* update cb_index[] map */ + bta_hh_cb.cb_index[hdl] = p_cb->index; + } else { + status = BTA_HH_ERR_NO_RES; + } + } + bta_hh_sm_execute(p_cb, BTA_HH_SDP_CMPL_EVT, (tBTA_HH_DATA *)&status); + + return; + } + /* GetSDPRecord. at one time only one SDP precedure can be active */ + else if (!bta_hh_cb.p_disc_db) { + bta_hh_cb.p_disc_db = (tSDP_DISCOVERY_DB *) osi_malloc(p_bta_hh_cfg->sdp_db_size); + + if (bta_hh_cb.p_disc_db == NULL) { + status = BTA_HH_ERR_NO_RES; + } else { + bta_hh_cb.p_cur = p_cb; + /* do DI discovery first */ + if (SDP_DiDiscover(p_data->api_conn.bd_addr, + bta_hh_cb.p_disc_db, + p_bta_hh_cfg->sdp_db_size, + bta_hh_di_sdp_cback) != SDP_SUCCESS) { +#if BTA_HH_DEBUG + APPL_TRACE_DEBUG ("bta_hh_start_sdp: SDP_DiDiscover failed: \ + Status 0x%2X", status); +#endif + status = BTA_HH_ERR_SDP; + utl_freebuf((void **)&bta_hh_cb.p_disc_db); + } else { + status = BTA_HH_OK; + } + } + } + + if (status != BTA_HH_OK) { + bta_hh_sm_execute(p_cb, BTA_HH_SDP_CMPL_EVT, (tBTA_HH_DATA *)&status); + } + + return; + +} +/******************************************************************************* +** +** Function bta_hh_sdp_cmpl +** +** Description When SDP completed, initiate a connection or report error depend +** on SDP result. +** +** +** Returns void +** +*******************************************************************************/ +void bta_hh_sdp_cmpl(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data) +{ + tBTA_HH_CONN conn_dat; + tBTA_HH_STATUS status = p_data->status; + +#if BTA_HH_DEBUG + APPL_TRACE_DEBUG ("bta_hh_sdp_cmpl: status 0x%2X", p_data->status); +#endif + + /* initialize call back data */ + memset((void *)&conn_dat, 0, sizeof(tBTA_HH_CONN)); + conn_dat.handle = p_cb->hid_handle; + bdcpy(conn_dat.bda, p_cb->addr); + + /* if SDP compl success */ + if ( status == BTA_HH_OK) { + /* not incoming connection doing SDP, initiate a HID connection */ + if (!p_cb->incoming_conn) { + tHID_STATUS ret; + /* set security level */ + HID_HostSetSecurityLevel("", p_cb->sec_mask); + + /* open HID connection */ + if ((ret = HID_HostOpenDev (p_cb->hid_handle)) != HID_SUCCESS) { +#if BTA_HH_DEBUG + APPL_TRACE_DEBUG ("bta_hh_sdp_cmpl: HID_HostOpenDev failed: \ + Status 0x%2X", ret); +#endif + /* open fail, remove device from management device list */ + HID_HostRemoveDev( p_cb->hid_handle); + status = BTA_HH_ERR; + } else { + status = BTA_HH_OK; + } + } else { /* incoming connection SDP finish */ + bta_hh_sm_execute(p_cb, BTA_HH_OPEN_CMPL_EVT, NULL); + } + } + + if (status != BTA_HH_OK) { + /* Check if this was incoming connection request from an unknown device + **and connection failed due to missing HID Device SDP UUID + **In above condition, disconnect the link as well as remove the + **device from list of HID devices*/ + if ((status == BTA_HH_ERR_SDP) && + (p_cb->incoming_conn) && (p_cb->app_id == 0)) { + APPL_TRACE_DEBUG ("bta_hh_sdp_cmpl:SDP failed for incoming conn :hndl %d", + p_cb->incoming_hid_handle); + HID_HostRemoveDev( p_cb->incoming_hid_handle); + } + conn_dat.status = status; + /* check if host initiate the connection*/ + conn_dat.is_orig = !p_cb->incoming_conn; + (* bta_hh_cb.p_cback)(BTA_HH_OPEN_EVT, (tBTA_HH *)&conn_dat); + + /* move state machine W4_CONN ->IDLE */ + bta_hh_sm_execute(p_cb, BTA_HH_API_CLOSE_EVT, NULL); + + if (p_cb->app_id == 0) { + /* clean up device control block */ + bta_hh_clean_up_kdev(p_cb); + } +#if BTA_HH_DEBUG + bta_hh_trace_dev_db(); +#endif + } + return; +} + +/******************************************************************************* +** +** Function bta_hh_api_disc_act +** +** Description HID Host initiate a disconnection. +** +** +** Returns void +** +*******************************************************************************/ +void bta_hh_api_disc_act(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data) +{ + tBTA_HH_CBDATA disc_dat; + tHID_STATUS status; + +#if BTA_HH_LE_INCLUDED == TRUE + if (p_cb->is_le_device) { + bta_hh_le_api_disc_act(p_cb); + } else +#endif + { + /* found an active connection */ + disc_dat.handle = p_data ? (UINT8)p_data->hdr.layer_specific : p_cb->hid_handle; + disc_dat.status = BTA_HH_ERR; + + status = HID_HostCloseDev(disc_dat.handle); + + if (status) { + (* bta_hh_cb.p_cback)(BTA_HH_CLOSE_EVT, (tBTA_HH *)&disc_dat); + } + } + + return; + +} +/******************************************************************************* +** +** Function bta_hh_open_cmpl_act +** +** Description HID host connection completed +** +** +** Returns void +** +*******************************************************************************/ +void bta_hh_open_cmpl_act(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data) +{ + tBTA_HH_CONN conn ; + UINT8 dev_handle = p_data ? (UINT8)p_data->hid_cback.hdr.layer_specific : \ + p_cb->hid_handle; + + memset((void *)&conn, 0, sizeof (tBTA_HH_CONN)); + conn.handle = dev_handle; + /* check if host initiate the connection*/ + conn.is_orig = !p_cb->incoming_conn; + bdcpy(conn.bda, p_cb->addr); + + /* increase connection number */ + bta_hh_cb.cnt_num ++; + + /* initialize device driver */ + bta_hh_co_open(p_cb->hid_handle, p_cb->sub_class, + p_cb->attr_mask, p_cb->app_id); + +#if (BTA_HH_LE_INCLUDED == TRUE) + conn.status = p_cb->status; + conn.le_hid = p_cb->is_le_device; + conn.scps_supported = p_cb->scps_supported; + + if (!p_cb->is_le_device) +#endif + { + /* inform role manager */ + bta_sys_conn_open( BTA_ID_HH , p_cb->app_id, p_cb->addr); + } + /* set protocol mode when not default report mode */ + if ( p_cb->mode != BTA_HH_PROTO_RPT_MODE +#if (BTA_HH_LE_INCLUDED == TRUE) + && !p_cb->is_le_device +#endif + ) { + if ((HID_HostWriteDev(dev_handle, + HID_TRANS_SET_PROTOCOL, HID_PAR_PROTOCOL_BOOT_MODE, + 0, + 0, NULL)) != HID_SUCCESS) { + /* HID connection is up, while SET_PROTO fail */ + conn.status = BTA_HH_ERR_PROTO; + (* bta_hh_cb.p_cback)(BTA_HH_OPEN_EVT, (tBTA_HH *)&conn); + } else { + conn.status = BTA_HH_OK; + p_cb->w4_evt = BTA_HH_OPEN_EVT; + } + } else { + (* bta_hh_cb.p_cback)(BTA_HH_OPEN_EVT, (tBTA_HH *)&conn); + } + + p_cb->incoming_conn = FALSE; + p_cb->incoming_hid_handle = BTA_HH_INVALID_HANDLE; + +} +/******************************************************************************* +** +** Function bta_hh_open_act +** +** Description HID host receive HID_OPEN_EVT . +** +** +** Returns void +** +*******************************************************************************/ +void bta_hh_open_act(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data) +{ + tBTA_HH_API_CONN conn_data; + + UINT8 dev_handle = p_data ? (UINT8)p_data->hid_cback.hdr.layer_specific : \ + p_cb->hid_handle; + +#if BTA_HH_DEBUG + APPL_TRACE_EVENT ("bta_hh_open_act: Device[%d] connected", dev_handle); +#endif + + p_cb->incoming_conn = TRUE; + /* SDP has been done */ + if (p_cb->app_id != 0) { + bta_hh_sm_execute(p_cb, BTA_HH_OPEN_CMPL_EVT, p_data); + } else + /* app_id == 0 indicates an incoming conenction request arrives without SDP + performed, do it first */ + { + /* store the handle here in case sdp fails - need to disconnect */ + p_cb->incoming_hid_handle = dev_handle; + + memset(&conn_data, 0, sizeof(tBTA_HH_API_CONN)); + bdcpy(conn_data.bd_addr, p_cb->addr); + bta_hh_start_sdp(p_cb, (tBTA_HH_DATA *)&conn_data); + } + + return; +} + + +/******************************************************************************* +** +** Function bta_hh_data_act +** +** Description HID Host process a data report +** +** +** Returns void +** +*******************************************************************************/ +void bta_hh_data_act(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data) +{ + BT_HDR *pdata = p_data->hid_cback.p_data; + UINT8 *p_rpt = (UINT8 *)(pdata + 1) + pdata->offset; + + bta_hh_co_data((UINT8)p_data->hid_cback.hdr.layer_specific, p_rpt, pdata->len, + p_cb->mode, p_cb->sub_class, p_cb->dscp_info.ctry_code, p_cb->addr, p_cb->app_id); + + utl_freebuf((void **)&pdata); +} + + +/******************************************************************************* +** +** Function bta_hh_handsk_act +** +** Description HID Host process a handshake acknoledgement. +** +** +** Returns void +** +*******************************************************************************/ +void bta_hh_handsk_act(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data) +{ + tBTA_HH_CBDATA cback_data ; + tBTA_HH_HSDATA hs_data; + tBTA_HH_CONN conn ; + +#if BTA_HH_DEBUG + APPL_TRACE_DEBUG("HANDSHAKE received for: event = %s data= %d", + bta_hh_get_w4_event(p_cb->w4_evt), p_data->hid_cback.data); +#endif + + memset(&hs_data, 0, sizeof(tBTA_HH_HSDATA)); + memset(&cback_data, 0, sizeof(tBTA_HH_CBDATA)); + + switch (p_cb->w4_evt) { + /* GET_ transsaction, handshake indicate unsupported request */ + case BTA_HH_GET_PROTO_EVT: + hs_data.rsp_data.proto_mode = BTA_HH_PROTO_UNKNOWN; + /* fall through */ + case BTA_HH_GET_RPT_EVT: + case BTA_HH_GET_IDLE_EVT : + hs_data.handle = p_cb->hid_handle; + /* if handshake gives an OK code for these transaction, fill in UNSUPT */ + if ((hs_data.status = bta_hh_get_trans_status(p_data->hid_cback.data)) == BTA_HH_OK) { + hs_data.status = BTA_HH_HS_TRANS_NOT_SPT; + } + + (* bta_hh_cb.p_cback)(p_cb->w4_evt, (tBTA_HH *)&hs_data); + p_cb->w4_evt = 0; + break; + + /* acknoledgement from HID device for SET_ transaction */ + case BTA_HH_SET_RPT_EVT: + case BTA_HH_SET_PROTO_EVT: + case BTA_HH_SET_IDLE_EVT : + cback_data.handle = p_cb->hid_handle; + cback_data.status = bta_hh_get_trans_status(p_data->hid_cback.data); + if (cback_data.status == BTA_HH_OK) { + p_cb->mode = p_cb->new_mode; + } else { + p_cb->new_mode = p_cb->mode; + } + (* bta_hh_cb.p_cback)(p_cb->w4_evt, (tBTA_HH *)&cback_data); + p_cb->w4_evt = 0; + break; + + /* SET_PROTOCOL when open connection */ + case BTA_HH_OPEN_EVT: + conn.status = p_data->hid_cback.data ? BTA_HH_ERR_PROTO : BTA_HH_OK; + conn.handle = p_cb->hid_handle; + /* check if host initiate the connection*/ + conn.is_orig = !p_cb->incoming_conn; + bdcpy(conn.bda, p_cb->addr); + (* bta_hh_cb.p_cback)(p_cb->w4_evt, (tBTA_HH *)&conn); +#if BTA_HH_DEBUG + bta_hh_trace_dev_db(); +#endif + p_cb->w4_evt = 0; + break; + + default: + /* unknow transaction handshake response */ + APPL_TRACE_DEBUG("unknown transaction type"); + break; + } + + /* transaction achknoledgement received, inform PM for mode change */ + bta_sys_idle(BTA_ID_HH, p_cb->app_id, p_cb->addr); + return; +} +/******************************************************************************* +** +** Function bta_hh_ctrl_dat_act +** +** Description HID Host process a data report from control channel. +** +** +** Returns void +** +*******************************************************************************/ +void bta_hh_ctrl_dat_act(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data) +{ + BT_HDR *pdata = p_data->hid_cback.p_data; + UINT8 *data = (UINT8 *)(pdata + 1) + pdata->offset; + tBTA_HH_HSDATA hs_data; + +#if BTA_HH_DEBUG + APPL_TRACE_DEBUG("Ctrl DATA received w4: event[%s]", + bta_hh_get_w4_event(p_cb->w4_evt)); +#endif + hs_data.status = BTA_HH_OK; + hs_data.handle = p_cb->hid_handle; + + switch (p_cb->w4_evt) { + case BTA_HH_GET_IDLE_EVT: + hs_data.rsp_data.idle_rate = *data; + break; + case BTA_HH_GET_RPT_EVT: + hs_data.rsp_data.p_rpt_data = pdata; + break; + case BTA_HH_GET_PROTO_EVT: + /* match up BTE/BTA report/boot mode def*/ + hs_data.rsp_data.proto_mode = ((*data) == HID_PAR_PROTOCOL_REPORT) ? \ + BTA_HH_PROTO_RPT_MODE : BTA_HH_PROTO_BOOT_MODE; +#if BTA_HH_DEBUG + APPL_TRACE_DEBUG("GET_PROTOCOL Mode = [%s]", + (hs_data.rsp_data.proto_mode == BTA_HH_PROTO_RPT_MODE) ? "Report" : "Boot"); +#endif + break; + /* should not expect control DATA for SET_ transaction */ + case BTA_HH_SET_PROTO_EVT: + /* fall through */ + case BTA_HH_SET_RPT_EVT: + /* fall through */ + case BTA_HH_SET_IDLE_EVT : + /* fall through */ + default: +#if BTA_HH_DEBUG + APPL_TRACE_DEBUG("invalid transaction type for DATA payload: 4_evt[%s]", + bta_hh_get_w4_event(p_cb->w4_evt)); +#endif + break; + } + + /* inform PM for mode change */ + bta_sys_busy(BTA_ID_HH, p_cb->app_id, p_cb->addr); + bta_sys_idle(BTA_ID_HH, p_cb->app_id, p_cb->addr); + + (* bta_hh_cb.p_cback)(p_cb->w4_evt, (tBTA_HH *)&hs_data); + + p_cb->w4_evt = 0; + utl_freebuf((void **)&pdata); + +} + +/******************************************************************************* +** +** Function bta_hh_open_failure +** +** Description report HID open failure when at wait for connection state and receive +** device close event. +** +** +** Returns void +** +*******************************************************************************/ +void bta_hh_open_failure(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data) +{ + tBTA_HH_CONN conn_dat ; + UINT32 reason = p_data->hid_cback.data; /* Reason for closing (32-bit) */ + + memset(&conn_dat, 0, sizeof(tBTA_HH_CONN)); + conn_dat.handle = p_cb->hid_handle; + conn_dat.status = (reason == HID_ERR_AUTH_FAILED) ? + BTA_HH_ERR_AUTH_FAILED : BTA_HH_ERR; + /* check if host initiate the connection*/ + conn_dat.is_orig = !p_cb->incoming_conn; + bdcpy(conn_dat.bda, p_cb->addr); + HID_HostCloseDev(p_cb->hid_handle); + + /* Report OPEN fail event */ + (*bta_hh_cb.p_cback)(BTA_HH_OPEN_EVT, (tBTA_HH *)&conn_dat); + +#if BTA_HH_DEBUG + bta_hh_trace_dev_db(); +#endif + /* clean up control block, but retain SDP info and device handle */ + p_cb->vp = FALSE; + p_cb->w4_evt = 0; + + /* if no connection is active and HH disable is signaled, disable service */ + if (bta_hh_cb.cnt_num == 0 && bta_hh_cb.w4_disable) { + bta_hh_disc_cmpl(); + } + +} + +/******************************************************************************* +** +** Function bta_hh_close_act +** +** Description HID Host process a close event +** +** +** Returns void +** +*******************************************************************************/ +void bta_hh_close_act (tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data) +{ + tBTA_HH_CONN conn_dat ; + tBTA_HH_CBDATA disc_dat = {BTA_HH_OK, 0}; + UINT32 reason = p_data->hid_cback.data; /* Reason for closing (32-bit) */ + + /* if HID_HDEV_EVT_VC_UNPLUG was received, report BTA_HH_VC_UNPLUG_EVT */ + UINT16 event = p_cb->vp ? BTA_HH_VC_UNPLUG_EVT : BTA_HH_CLOSE_EVT; + + disc_dat.handle = p_cb->hid_handle; + disc_dat.status = p_data->hid_cback.data; + + /* Check reason for closing */ + if ((reason & (HID_L2CAP_CONN_FAIL | HID_L2CAP_REQ_FAIL)) || /* Failure to initialize connection (page timeout or l2cap error) */ + (reason == HID_ERR_AUTH_FAILED) || /* Authenication error (while initiating) */ + (reason == HID_ERR_L2CAP_FAILED)) { /* Failure creating l2cap connection */ + /* Failure in opening connection */ + conn_dat.handle = p_cb->hid_handle; + conn_dat.status = (reason == HID_ERR_AUTH_FAILED) ? BTA_HH_ERR_AUTH_FAILED : BTA_HH_ERR; + /* check if host initiate the connection*/ + conn_dat.is_orig = !p_cb->incoming_conn; + bdcpy(conn_dat.bda, p_cb->addr); + HID_HostCloseDev(p_cb->hid_handle); + + /* Report OPEN fail event */ + (*bta_hh_cb.p_cback)(BTA_HH_OPEN_EVT, (tBTA_HH *)&conn_dat); + +#if BTA_HH_DEBUG + bta_hh_trace_dev_db(); +#endif + return; + } + /* otherwise report CLOSE/VC_UNPLUG event */ + else { + /* finaliza device driver */ + bta_hh_co_close(p_cb->hid_handle, p_cb->app_id); + /* inform role manager */ + bta_sys_conn_close( BTA_ID_HH , p_cb->app_id, p_cb->addr); + /* update total conn number */ + bta_hh_cb.cnt_num --; + + if (disc_dat.status) { + disc_dat.status = BTA_HH_ERR; + } + + (*bta_hh_cb.p_cback)(event, (tBTA_HH *)&disc_dat); + + /* if virtually unplug, remove device */ + if (p_cb->vp ) { + HID_HostRemoveDev( p_cb->hid_handle); + bta_hh_clean_up_kdev(p_cb); + } + +#if BTA_HH_DEBUG + bta_hh_trace_dev_db(); +#endif + } + + /* clean up control block, but retain SDP info and device handle */ + p_cb->vp = FALSE; + p_cb->w4_evt = 0; + + /* if no connection is active and HH disable is signaled, disable service */ + if (bta_hh_cb.cnt_num == 0 && bta_hh_cb.w4_disable) { + bta_hh_disc_cmpl(); + } + + return; +} + +/******************************************************************************* +** +** Function bta_hh_get_dscp_act +** +** Description Get device report descriptor +** +** +** Returns void +** +*******************************************************************************/ +void bta_hh_get_dscp_act(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data) +{ + UNUSED(p_data); + +#if (BTA_HH_LE_INCLUDED == TRUE) + if (p_cb->is_le_device) { + bta_hh_le_get_dscp_act(p_cb); + } else +#endif + { + (*bta_hh_cb.p_cback)(BTA_HH_GET_DSCP_EVT, (tBTA_HH *)&p_cb->dscp_info); + } +} + +/******************************************************************************* +** +** Function bta_hh_maint_dev_act +** +** Description HID Host maintain device list. +** +** +** Returns void +** +*******************************************************************************/ +void bta_hh_maint_dev_act(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data) +{ + tBTA_HH_MAINT_DEV *p_dev_info = &p_data->api_maintdev; + tBTA_HH_DEV_INFO dev_info ; + UINT8 dev_handle; + + dev_info.status = BTA_HH_ERR; + dev_info.handle = BTA_HH_INVALID_HANDLE; + + switch (p_dev_info->sub_event) { + case BTA_HH_ADD_DEV_EVT: /* add a device */ + bdcpy(dev_info.bda, p_dev_info->bda); + /* initialize callback data */ + if (p_cb->hid_handle == BTA_HH_INVALID_HANDLE) { +#if (BTA_HH_LE_INCLUDED == TRUE) + if (bta_hh_is_le_device(p_cb, p_data->api_conn.bd_addr)) { + dev_info.handle = bta_hh_le_add_device(p_cb, p_dev_info); + dev_info.status = BTA_HH_OK; + } else +#endif + { + if (HID_HostAddDev(p_dev_info->bda, p_dev_info->attr_mask, &dev_handle)\ + == HID_SUCCESS) { + dev_info.handle = dev_handle; + +#if (defined BTA_HH_LE_INCLUDED && BTA_HH_LE_INCLUDED == TRUE) + /* update DI information */ + bta_hh_update_di_info(p_cb, + p_dev_info->dscp_info.vendor_id, + p_dev_info->dscp_info.product_id, + p_dev_info->dscp_info.version, + p_dev_info->dscp_info.flag); +#else + bta_hh_update_di_info(p_cb, + p_dev_info->dscp_info.vendor_id, + p_dev_info->dscp_info.product_id, + p_dev_info->dscp_info.version, + 0); + +#endif + /* add to BTA device list */ + bta_hh_add_device_to_list(p_cb, dev_handle, + p_dev_info->attr_mask, + &p_dev_info->dscp_info.descriptor, + p_dev_info->sub_class, + p_dev_info->dscp_info.ssr_max_latency, + p_dev_info->dscp_info.ssr_min_tout, + p_dev_info->app_id); + /* update cb_index[] map */ + bta_hh_cb.cb_index[dev_handle] = p_cb->index; + } + } + } else { /* device already been added */ + dev_info.handle = p_cb->hid_handle; + dev_info.status = BTA_HH_OK; + } +#if BTA_HH_DEBUG + bta_hh_trace_dev_db(); +#endif + + break; + case BTA_HH_RMV_DEV_EVT: /* remove device */ + dev_info.handle = (UINT8)p_dev_info->hdr.layer_specific; + bdcpy(dev_info.bda, p_cb->addr); + +#if BTA_HH_LE_INCLUDED == TRUE + if (p_cb->is_le_device) { + bta_hh_le_remove_dev_bg_conn(p_cb); + bta_hh_sm_execute(p_cb, BTA_HH_API_CLOSE_EVT, NULL); + bta_hh_clean_up_kdev(p_cb); + } else +#endif + { + if (HID_HostRemoveDev( dev_info.handle ) == HID_SUCCESS) { + dev_info.status = BTA_HH_OK; + + /* remove from known device list in BTA */ + bta_hh_clean_up_kdev(p_cb); + } + } + break; + + default: + APPL_TRACE_DEBUG("invalid command"); + break; + } + + (* bta_hh_cb.p_cback)(p_dev_info->sub_event, (tBTA_HH *)&dev_info); +} +/******************************************************************************* +** +** Function bta_hh_write_dev_act +** +** Description Write device action. can be SET/GET/DATA transaction. +** +** Returns void +** +*******************************************************************************/ +void bta_hh_write_dev_act(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data) +{ + tHID_STATUS status; + tBTA_HH_CBDATA cbdata = {BTA_HH_OK, 0}; + tBTA_HH_API_SENDDATA send_data = {BTA_HH_OK, 0, 0}; + UINT16 event = (p_data->api_sndcmd.t_type - BTA_HH_FST_BTE_TRANS_EVT) + + BTA_HH_FST_TRANS_CB_EVT; + +#if BTA_HH_LE_INCLUDED == TRUE + if (p_cb->is_le_device) { + bta_hh_le_write_dev_act(p_cb, p_data); + } else +#endif + { + + cbdata.handle = p_cb->hid_handle; + send_data.handle = p_cb->hid_handle; + + /* match up BTE/BTA report/boot mode def */ + if (p_data->api_sndcmd.t_type == HID_TRANS_SET_PROTOCOL) { + p_cb->new_mode = p_data->api_sndcmd.param; + p_data->api_sndcmd.param = ( p_data->api_sndcmd.param == BTA_HH_PROTO_RPT_MODE) ? \ + HID_PAR_PROTOCOL_REPORT : HID_PAR_PROTOCOL_BOOT_MODE; + } + + status = HID_HostWriteDev(p_cb->hid_handle, p_data->api_sndcmd.t_type, p_data->api_sndcmd.param, + p_data->api_sndcmd.data, p_data->api_sndcmd.rpt_id, p_data->api_sndcmd.p_data); + if (status != HID_SUCCESS) { + APPL_TRACE_ERROR("HID_HostWriteDev status:%d", status); + cbdata.status = BTA_HH_ERR; + send_data.status = BTA_HH_ERR; + + if (p_data->api_sndcmd.t_type != HID_TRANS_CONTROL) { + switch (p_data->api_sndcmd.t_type) { + case HID_TRANS_DATA: + event = BTA_HH_DATA_EVT; + send_data.reason = status; + (*bta_hh_cb.p_cback)(event, (tBTA_HH *)&send_data); + break; + default: + (*bta_hh_cb.p_cback)(event, (tBTA_HH *)&cbdata); + break; + } + } else if (p_data->api_sndcmd.param == BTA_HH_CTRL_VIRTUAL_CABLE_UNPLUG) { + (* bta_hh_cb.p_cback)(BTA_HH_VC_UNPLUG_EVT, (tBTA_HH *)&cbdata); + } + } else { + + switch (p_data->api_sndcmd.t_type) { + case HID_TRANS_SET_PROTOCOL: + /* fall through */ + case HID_TRANS_GET_REPORT: + /* fall through */ + case HID_TRANS_SET_REPORT: + /* fall through */ + case HID_TRANS_GET_PROTOCOL: + /* fall through */ + case HID_TRANS_GET_IDLE: + /* fall through */ + case HID_TRANS_SET_IDLE:/* set w4_handsk event name for callback function use */ + p_cb->w4_evt = event; + break; + case HID_TRANS_DATA: /* output report */ + (*bta_hh_cb.p_cback)(BTA_HH_DATA_EVT, (tBTA_HH *)&send_data); + /* fall through */ + case HID_TRANS_CONTROL: + /* no handshake event will be generated */ + /* if VC_UNPLUG is issued, set flag */ + if (p_data->api_sndcmd.param == BTA_HH_CTRL_VIRTUAL_CABLE_UNPLUG) { + p_cb->vp = TRUE; + } + + break; + /* currently not expected */ + case HID_TRANS_DATAC: + default: + APPL_TRACE_DEBUG("bta_hh_write_dev_act:: cmd type = %d", + p_data->api_sndcmd.t_type); + break; + } + + /* if not control type transaction, notify PM for energy control */ + if (p_data->api_sndcmd.t_type != HID_TRANS_CONTROL) { + /* inform PM for mode change */ + bta_sys_busy(BTA_ID_HH, p_cb->app_id, p_cb->addr); + bta_sys_idle(BTA_ID_HH, p_cb->app_id, p_cb->addr); + } else if (p_data->api_sndcmd.param == BTA_HH_CTRL_SUSPEND) { + bta_sys_sco_close(BTA_ID_HH, p_cb->app_id, p_cb->addr); + } else if (p_data->api_sndcmd.param == BTA_HH_CTRL_EXIT_SUSPEND) { + bta_sys_busy(BTA_ID_HH, p_cb->app_id, p_cb->addr); + } + } + } + return; +} + +/***************************************************************************** +** Static Function +*****************************************************************************/ +/******************************************************************************* +** +** Function bta_hh_cback +** +** Description BTA HH callback function. +** +** +** Returns void +** +*******************************************************************************/ +static void bta_hh_cback (UINT8 dev_handle, BD_ADDR addr, UINT8 event, + UINT32 data, BT_HDR *pdata) +{ + tBTA_HH_CBACK_DATA *p_buf = NULL; + UINT16 sm_event = BTA_HH_INVALID_EVT; + UINT8 xx = 0; + +#if BTA_HH_DEBUG + APPL_TRACE_DEBUG("bta_hh_cback::HID_event [%s]", bta_hh_hid_event_name(event)); +#endif + + switch (event) { + case HID_HDEV_EVT_OPEN: + sm_event = BTA_HH_INT_OPEN_EVT; + break; + case HID_HDEV_EVT_CLOSE: + sm_event = BTA_HH_INT_CLOSE_EVT; + break; + case HID_HDEV_EVT_INTR_DATA: + sm_event = BTA_HH_INT_DATA_EVT; + break; + case HID_HDEV_EVT_HANDSHAKE: + sm_event = BTA_HH_INT_HANDSK_EVT; + break; + case HID_HDEV_EVT_CTRL_DATA: + sm_event = BTA_HH_INT_CTRL_DATA; + break; + case HID_HDEV_EVT_RETRYING: + break; + case HID_HDEV_EVT_INTR_DATC: + case HID_HDEV_EVT_CTRL_DATC: + /* Unhandled events: Free buffer for DATAC */ + utl_freebuf((void **)&pdata); + break; + case HID_HDEV_EVT_VC_UNPLUG: + for (xx = 0; xx < BTA_HH_MAX_DEVICE; xx++) { + if (bta_hh_cb.kdev[xx].hid_handle == dev_handle) { + bta_hh_cb.kdev[xx].vp = TRUE; + break; + } + } + break; + } + + if (sm_event != BTA_HH_INVALID_EVT && + (p_buf = (tBTA_HH_CBACK_DATA *)osi_malloc(sizeof(tBTA_HH_CBACK_DATA) + + sizeof(BT_HDR))) != NULL) { + p_buf->hdr.event = sm_event; + p_buf->hdr.layer_specific = (UINT16)dev_handle; + p_buf->data = data; + bdcpy(p_buf->addr, addr); + p_buf->p_data = pdata; + + bta_sys_sendmsg(p_buf); + } + +} +/******************************************************************************* +** +** Function bta_hh_get_trans_status +** +** Description translate a handshake result code into BTA HH +** status code +** +*******************************************************************************/ +static tBTA_HH_STATUS bta_hh_get_trans_status(UINT32 result) +{ + switch (result) { + case HID_PAR_HANDSHAKE_RSP_SUCCESS : /* (0) */ + return BTA_HH_OK; + case HID_PAR_HANDSHAKE_RSP_NOT_READY : /* (1) */ + case HID_PAR_HANDSHAKE_RSP_ERR_INVALID_REP_ID: /* (2) */ + case HID_PAR_HANDSHAKE_RSP_ERR_UNSUPPORTED_REQ : /* (3) */ + case HID_PAR_HANDSHAKE_RSP_ERR_INVALID_PARAM : /* (4) */ + return (tBTA_HH_STATUS)result; + case HID_PAR_HANDSHAKE_RSP_ERR_UNKNOWN : /* (14) */ + case HID_PAR_HANDSHAKE_RSP_ERR_FATAL : /* (15) */ + default: + return BTA_HH_HS_ERROR; + break; + } +} +/***************************************************************************** +** Debug Functions +*****************************************************************************/ + +#if (defined BTA_HH_DEBUG && BTA_HH_DEBUG == TRUE) +static char *bta_hh_get_w4_event(UINT16 event) +{ + switch (event) { + case BTA_HH_GET_RPT_EVT: + return "BTA_HH_GET_RPT_EVT"; + case BTA_HH_SET_RPT_EVT: + return "BTA_HH_SET_RPT_EVT"; + case BTA_HH_GET_PROTO_EVT: + return "BTA_HH_GET_PROTO_EVT"; + case BTA_HH_SET_PROTO_EVT: + return "BTA_HH_SET_PROTO_EVT"; + case BTA_HH_GET_IDLE_EVT: + return "BTA_HH_GET_IDLE_EVT"; + case BTA_HH_SET_IDLE_EVT: + return "BTA_HH_SET_IDLE_EVT"; + case BTA_HH_OPEN_EVT: + return "BTA_HH_OPEN_EVT"; + default: + return "Unknown event"; + } + +} + +static char *bta_hh_hid_event_name(UINT16 event) +{ + switch (event) { + case HID_HDEV_EVT_OPEN: + return "HID_HDEV_EVT_OPEN"; + case HID_HDEV_EVT_CLOSE: + return "HID_HDEV_EVT_CLOSE"; + case HID_HDEV_EVT_RETRYING: + return "HID_HDEV_EVT_RETRYING"; + case HID_HDEV_EVT_INTR_DATA: + return "HID_HDEV_EVT_INTR_DATA"; + case HID_HDEV_EVT_INTR_DATC: + return "HID_HDEV_EVT_INTR_DATC"; + case HID_HDEV_EVT_CTRL_DATA: + return "HID_HDEV_EVT_CTRL_DATA"; + case HID_HDEV_EVT_CTRL_DATC: + return "HID_HDEV_EVT_CTRL_DATC"; + case HID_HDEV_EVT_HANDSHAKE: + return "HID_HDEV_EVT_HANDSHAKE"; + case HID_HDEV_EVT_VC_UNPLUG: + return "HID_HDEV_EVT_VC_UNPLUG"; + default: + return "Unknown HID event"; + } +} +#endif +#endif /* BTA_HH_INCLUDED */ diff --git a/lib/bt/host/bluedroid/bta/hh/bta_hh_api.c b/lib/bt/host/bluedroid/bta/hh/bta_hh_api.c new file mode 100644 index 00000000..157e4b6a --- /dev/null +++ b/lib/bt/host/bluedroid/bta/hh/bta_hh_api.c @@ -0,0 +1,477 @@ +/****************************************************************************** + * + * Copyright (C) 2005-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains the HID HOST API in the subsystem of BTA. + * + ******************************************************************************/ + +#include "common/bt_target.h" + +#if defined(BTA_HH_INCLUDED) && (BTA_HH_INCLUDED == TRUE) + +#include +#include +#include + +#include "bta/bta_hh_api.h" +#include "bta_hh_int.h" +#include "stack/l2c_api.h" +#include "bta/utl.h" + +#include "osi/allocator.h" + +/***************************************************************************** +** Constants +*****************************************************************************/ + +static const tBTA_SYS_REG bta_hh_reg = { + bta_hh_hdl_event, + BTA_HhDisable +}; + +/******************************************************************************* +** +** Function BTA_HhEnable +** +** Description Enable the HID host. This function must be called before +** any other functions in the HID host API are called. When the +** enable operation is complete the callback function will be +** called with BTA_HH_ENABLE_EVT. +** +** +** Returns void +** +*******************************************************************************/ +void BTA_HhEnable(tBTA_SEC sec_mask, tBTA_HH_CBACK *p_cback) +{ + tBTA_HH_API_ENABLE *p_buf; + + /* register with BTA system manager */ + bta_sys_register(BTA_ID_HH, &bta_hh_reg); + + APPL_TRACE_API("%s sec_mask:0x%x p_cback:%p", __func__, sec_mask, p_cback); + p_buf = (tBTA_HH_API_ENABLE *)osi_malloc((UINT16)sizeof(tBTA_HH_API_ENABLE)); + + if (p_buf != NULL) { + memset(p_buf, 0, sizeof(tBTA_HH_API_ENABLE)); + + p_buf->hdr.event = BTA_HH_API_ENABLE_EVT; + p_buf->p_cback = p_cback; + p_buf->sec_mask = sec_mask; + + bta_sys_sendmsg(p_buf); + } +} + +/******************************************************************************* +** +** Function BTA_HhDisable +** +** Description Disable the HID host. If the server is currently +** connected, the connection will be closed. +** +** Returns void +** +*******************************************************************************/ +void BTA_HhDisable(void) +{ + BT_HDR *p_buf; + + bta_sys_deregister(BTA_ID_HH); + if ((p_buf = (BT_HDR *)osi_malloc(sizeof(BT_HDR))) != NULL) { + p_buf->event = BTA_HH_API_DISABLE_EVT; + bta_sys_sendmsg(p_buf); + } +} + +/******************************************************************************* +** +** Function BTA_HhClose +** +** Description Disconnect a connection. +** +** Returns void +** +*******************************************************************************/ +void BTA_HhClose(UINT8 dev_handle) +{ + BT_HDR *p_buf; + + if ((p_buf = (BT_HDR *)osi_malloc((UINT16)sizeof(BT_HDR))) != NULL) { + memset(p_buf, 0, sizeof(BT_HDR)); + p_buf->event = BTA_HH_API_CLOSE_EVT; + p_buf->layer_specific = (UINT16) dev_handle; + + bta_sys_sendmsg(p_buf); + } +} + +/******************************************************************************* +** +** Function BTA_HhOpen +** +** Description Connect to a device of specified BD address in specified +** protocol mode and security level. +** +** Returns void +** +*******************************************************************************/ +void BTA_HhOpen(BD_ADDR dev_bda, tBTA_HH_PROTO_MODE mode, tBTA_SEC sec_mask) +{ + tBTA_HH_API_CONN *p_buf; + + p_buf = (tBTA_HH_API_CONN *)osi_malloc((UINT16)sizeof(tBTA_HH_API_CONN)); + + if (p_buf != NULL) { + memset((void *)p_buf, 0, sizeof(tBTA_HH_API_CONN)); + + p_buf->hdr.event = BTA_HH_API_OPEN_EVT; + p_buf->hdr.layer_specific = BTA_HH_INVALID_HANDLE; + p_buf->sec_mask = sec_mask; + p_buf->mode = mode; + bdcpy(p_buf->bd_addr, dev_bda); + + bta_sys_sendmsg((void *)p_buf); + } else { + APPL_TRACE_ERROR("No resource to send HID host Connect request."); + } +} + +/******************************************************************************* +** +** Function bta_hh_snd_write_dev +** +*******************************************************************************/ +static void bta_hh_snd_write_dev(UINT8 dev_handle, UINT8 t_type, UINT8 param, + UINT16 data, UINT8 rpt_id, BT_HDR *p_data) +{ + tBTA_HH_CMD_DATA *p_buf; + UINT16 len = (UINT16) (sizeof(tBTA_HH_CMD_DATA) ); + + if ((p_buf = (tBTA_HH_CMD_DATA *)osi_malloc(len)) != NULL) { + memset(p_buf, 0, sizeof(tBTA_HH_CMD_DATA)); + + p_buf->hdr.event = BTA_HH_API_WRITE_DEV_EVT; + p_buf->hdr.layer_specific = (UINT16) dev_handle; + p_buf->t_type = t_type; + p_buf->data = data; + p_buf->param = param; + p_buf->p_data = p_data; + p_buf->rpt_id = rpt_id; + + bta_sys_sendmsg(p_buf); + } +} +/******************************************************************************* +** +** Function BTA_HhSetReport +** +** Description send SET_REPORT to device. +** +** Parameter dev_handle: device handle +** r_type: report type, could be BTA_HH_RPTT_OUTPUT or +** BTA_HH_RPTT_FEATURE. +** Returns void +** +*******************************************************************************/ +void BTA_HhSetReport(UINT8 dev_handle, tBTA_HH_RPT_TYPE r_type, BT_HDR *p_data) +{ + bta_hh_snd_write_dev(dev_handle, HID_TRANS_SET_REPORT, r_type, 0, 0, p_data); +} +/******************************************************************************* +** +** Function BTA_HhGetReport +** +** Description Send a GET_REPORT to HID device. +** +** Returns void +** +*******************************************************************************/ +void BTA_HhGetReport(UINT8 dev_handle, tBTA_HH_RPT_TYPE r_type, UINT8 rpt_id, UINT16 buf_size) +{ + UINT8 param = (buf_size) ? (r_type | 0x08) : r_type; + + bta_hh_snd_write_dev(dev_handle, HID_TRANS_GET_REPORT, param, + buf_size, rpt_id, NULL); +} +/******************************************************************************* +** +** Function BTA_HhSetProtoMode +** +** Description This function set the protocol mode at specified HID handle +** +** Returns void +** +*******************************************************************************/ +void BTA_HhSetProtoMode(UINT8 dev_handle, tBTA_HH_PROTO_MODE p_type) +{ + bta_hh_snd_write_dev(dev_handle, HID_TRANS_SET_PROTOCOL, (UINT8)p_type, + 0, 0, NULL); +} +/******************************************************************************* +** +** Function BTA_HhGetProtoMode +** +** Description This function get protocol mode information. +** +** Returns void +** +*******************************************************************************/ +void BTA_HhGetProtoMode(UINT8 dev_handle) +{ + bta_hh_snd_write_dev(dev_handle, HID_TRANS_GET_PROTOCOL, 0, 0, 0, NULL); +} +/******************************************************************************* +** +** Function BTA_HhSetIdle +** +** Description send SET_IDLE to device. +** +** Returns void +** +*******************************************************************************/ +void BTA_HhSetIdle(UINT8 dev_handle, UINT16 idle_rate) +{ + bta_hh_snd_write_dev(dev_handle, HID_TRANS_SET_IDLE, 0, idle_rate, 0, NULL); +} + +/******************************************************************************* +** +** Function BTA_HhGetIdle +** +** Description Send a GET_IDLE from HID device. +** +** Returns void +** +*******************************************************************************/ +void BTA_HhGetIdle(UINT8 dev_handle) +{ + bta_hh_snd_write_dev(dev_handle, HID_TRANS_GET_IDLE, 0, 0, 0, NULL); +} +/******************************************************************************* +** +** Function BTA_HhSendCtrl +** +** Description Send a control command to HID device. +** +** Returns void +** +*******************************************************************************/ +void BTA_HhSendCtrl(UINT8 dev_handle, tBTA_HH_TRANS_CTRL_TYPE c_type) +{ + bta_hh_snd_write_dev(dev_handle, HID_TRANS_CONTROL, (UINT8)c_type, 0, 0, NULL); +} +/******************************************************************************* +** +** Function BTA_HhSendData +** +** Description This function send DATA transaction to HID device. +** +** Parameter dev_handle: device handle +** dev_bda: remote device address +** p_data: data to be sent in the DATA transaction; or +** the data to be write into the Output Report of a LE HID +** device. The report is identified the report ID which is +** the value of the byte (UINT8 *)(p_buf + 1) + p_buf->offset. +** p_data->layer_specific needs to be set to the report type, +** it can be OUTPUT report, or FEATURE report. +** +** Returns void +** +*******************************************************************************/ +void BTA_HhSendData(UINT8 dev_handle, BD_ADDR dev_bda, BT_HDR *p_data) +{ + UNUSED(dev_bda); +#if (defined BTA_HH_LE_INCLUDED && BTA_HH_LE_INCLUDED == TRUE) + if (p_data->layer_specific != BTA_HH_RPTT_OUTPUT) { + APPL_TRACE_ERROR("ERROR! Wrong report type! Write Command only valid for output report!"); + return; + } +#endif + bta_hh_snd_write_dev(dev_handle, HID_TRANS_DATA, (UINT8)p_data->layer_specific, 0, 0, p_data); +} + +/******************************************************************************* +** +** Function BTA_HhGetDscpInfo +** +** Description Get HID device report descriptor +** +** Returns void +** +*******************************************************************************/ +void BTA_HhGetDscpInfo(UINT8 dev_handle) +{ + BT_HDR *p_buf; + + if ((p_buf = (BT_HDR *)osi_malloc((UINT16)sizeof(BT_HDR))) != NULL) { + memset(p_buf, 0, sizeof(BT_HDR)); + p_buf->event = BTA_HH_API_GET_DSCP_EVT; + p_buf->layer_specific = (UINT16) dev_handle; + + bta_sys_sendmsg(p_buf); + } +} + +/******************************************************************************* +** +** Function BTA_HhAddDev +** +** Description Add a virtually cabled device into HID-Host device list +** to manage and assign a device handle for future API call, +** host applciation call this API at start-up to initialize its +** virtually cabled devices. +** +** Returns void +** +*******************************************************************************/ +void BTA_HhAddDev(BD_ADDR bda, tBTA_HH_ATTR_MASK attr_mask, UINT8 sub_class, + UINT8 app_id, tBTA_HH_DEV_DSCP_INFO dscp_info) +{ + tBTA_HH_MAINT_DEV *p_buf; + UINT16 len = sizeof(tBTA_HH_MAINT_DEV) + dscp_info.descriptor.dl_len; + + p_buf = (tBTA_HH_MAINT_DEV *)osi_malloc(len); + + if (p_buf != NULL) { + memset(p_buf, 0, sizeof(tBTA_HH_MAINT_DEV)); + + p_buf->hdr.event = BTA_HH_API_MAINT_DEV_EVT; + p_buf->sub_event = BTA_HH_ADD_DEV_EVT; + p_buf->hdr.layer_specific = BTA_HH_INVALID_HANDLE; + + p_buf->attr_mask = (UINT16) attr_mask; + p_buf->sub_class = sub_class; + p_buf->app_id = app_id; + bdcpy(p_buf->bda, bda); + + memcpy(&p_buf->dscp_info, &dscp_info, sizeof(tBTA_HH_DEV_DSCP_INFO)); + if ( dscp_info.descriptor.dl_len != 0 && dscp_info.descriptor.dsc_list) { + p_buf->dscp_info.descriptor.dl_len = dscp_info.descriptor.dl_len; + p_buf->dscp_info.descriptor.dsc_list = (UINT8 *)(p_buf + 1); + memcpy(p_buf->dscp_info.descriptor.dsc_list, dscp_info.descriptor.dsc_list, dscp_info.descriptor.dl_len); + } else { + p_buf->dscp_info.descriptor.dsc_list = NULL; + p_buf->dscp_info.descriptor.dl_len = 0; + } + + bta_sys_sendmsg(p_buf); + } +} +/******************************************************************************* +** +** Function BTA_HhRemoveDev +** +** Description Remove a device from the HID host devices list. +** +** Returns void +** +*******************************************************************************/ +void BTA_HhRemoveDev(UINT8 dev_handle ) +{ + tBTA_HH_MAINT_DEV *p_buf; + + p_buf = (tBTA_HH_MAINT_DEV *)osi_malloc((UINT16)sizeof(tBTA_HH_MAINT_DEV)); + + if (p_buf != NULL) { + memset(p_buf, 0, sizeof(tBTA_HH_MAINT_DEV)); + + p_buf->hdr.event = BTA_HH_API_MAINT_DEV_EVT; + p_buf->sub_event = BTA_HH_RMV_DEV_EVT; + p_buf->hdr.layer_specific = (UINT16) dev_handle; + + bta_sys_sendmsg(p_buf); + } +} +#if BTA_HH_LE_INCLUDED == TRUE + +/******************************************************************************* +** +** Function BTA_HhUpdateLeScanParam +** +** Description Update the scan paramteters if connected to a LE hid device as +** report host. +** +** Returns void +** +*******************************************************************************/ +void BTA_HhUpdateLeScanParam(UINT8 dev_handle, UINT16 scan_int, UINT16 scan_win) +{ + tBTA_HH_SCPP_UPDATE *p_buf; + + p_buf = (tBTA_HH_SCPP_UPDATE *)osi_malloc((UINT16)sizeof(tBTA_HH_SCPP_UPDATE)); + + if (p_buf != NULL) { + memset(p_buf, 0, sizeof(tBTA_HH_SCPP_UPDATE)); + + p_buf->hdr.event = BTA_HH_API_SCPP_UPDATE_EVT; + p_buf->hdr.layer_specific = (UINT16) dev_handle; + p_buf->scan_int = scan_int; + p_buf->scan_win = scan_win; + + bta_sys_sendmsg(p_buf); + } +} +#endif +/*******************************************************************************/ +/* Utility Function */ +/*******************************************************************************/ + +/******************************************************************************* +** +** Function BTA_HhParseBootRpt +** +** Description This utility function parse a boot mode report. +** For keyboard report, report data will carry the keycode max +** up to 6 key press in one report. Application need to convert +** the keycode into keypress character according to keyboard +** language. +** +** Returns void +** +*******************************************************************************/ +void BTA_HhParseBootRpt(tBTA_HH_BOOT_RPT *p_data, UINT8 *p_report, + UINT16 report_len) +{ + p_data->dev_type = BTA_HH_DEVT_UNKNOWN; + + if (p_report) { + /* first byte is report ID */ + switch (p_report[0]) { + case BTA_HH_KEYBD_RPT_ID: /* key board report ID */ + p_data->dev_type = p_report[0]; + bta_hh_parse_keybd_rpt(p_data, p_report + 1, (UINT16)(report_len - 1)); + break; + + case BTA_HH_MOUSE_RPT_ID: /* mouse report ID */ + p_data->dev_type = p_report[0]; + bta_hh_parse_mice_rpt(p_data, p_report + 1, (UINT16)(report_len - 1)); + break; + + default: + APPL_TRACE_DEBUG("Unknown boot report: %d", p_report[0]);; + break; + } + } + + return; +} + +#endif /* BTA_HH_INCLUDED */ diff --git a/lib/bt/host/bluedroid/bta/hh/bta_hh_cfg.c b/lib/bt/host/bluedroid/bta/hh/bta_hh_cfg.c new file mode 100644 index 00000000..383f2326 --- /dev/null +++ b/lib/bt/host/bluedroid/bta/hh/bta_hh_cfg.c @@ -0,0 +1,64 @@ +/****************************************************************************** + * + * Copyright (C) 2005-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains compile-time configurable constants for the BTA Hid + * Host. + * + ******************************************************************************/ + +#include "common/bt_target.h" + +#if defined(BTA_HH_INCLUDED) && (BTA_HH_INCLUDED == TRUE) +#include "bta/bta_hh_api.h" + + +/* max number of device types supported by BTA */ +#define BTA_HH_MAX_DEVT_SPT 9 + +/* size of database for service discovery */ +#ifndef BTA_HH_DISC_BUF_SIZE +#define BTA_HH_DISC_BUF_SIZE BT_DEFAULT_BUFFER_SIZE +#endif + + + +/* The type of devices supported by BTA HH and corresponding application ID */ +tBTA_HH_SPT_TOD p_devt_list[BTA_HH_MAX_DEVT_SPT] = { + {BTA_HH_DEVT_MIC, BTA_HH_APP_ID_MI}, + {BTA_HH_DEVT_KBD, BTA_HH_APP_ID_KB}, + {BTA_HH_DEVT_KBD | BTA_HH_DEVT_MIC, BTA_HH_APP_ID_KB}, + {BTA_HH_DEVT_RMC, BTA_HH_APP_ID_RMC}, + {BTA_HH_DEVT_RMC | BTA_HH_DEVT_KBD, BTA_HH_APP_ID_RMC}, + {BTA_HH_DEVT_MIC | BTA_HH_DEVT_DGT, BTA_HH_APP_ID_MI}, + {BTA_HH_DEVT_JOS, BTA_HH_APP_ID_JOY}, + {BTA_HH_DEVT_GPD, BTA_HH_APP_ID_GPAD}, + {BTA_HH_DEVT_UNKNOWN, BTA_HH_APP_ID_3DSG} +}; + + +const tBTA_HH_CFG bta_hh_cfg = { + BTA_HH_MAX_DEVT_SPT, /* number of supported type of devices */ + p_devt_list, /* ToD & AppID list */ + BTA_HH_DISC_BUF_SIZE /* HH SDP discovery database size */ +}; + + +tBTA_HH_CFG *p_bta_hh_cfg = (tBTA_HH_CFG *) &bta_hh_cfg; +#endif ///defined(BTA_HH_INCLUDED) && (BTA_HH_INCLUDED == TRUE) diff --git a/lib/bt/host/bluedroid/bta/hh/bta_hh_le.c b/lib/bt/host/bluedroid/bta/hh/bta_hh_le.c new file mode 100644 index 00000000..a80314ef --- /dev/null +++ b/lib/bt/host/bluedroid/bta/hh/bta_hh_le.c @@ -0,0 +1,3021 @@ +/****************************************************************************** + * + * Copyright (C) 2009-2013 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +#include "bta/bta_api.h" +#include "bta_hh_int.h" + +#if (defined BTA_HH_LE_INCLUDED && BTA_HH_LE_INCLUDED == TRUE) + +#include "bta/bta_api.h" +#include +#include "stack/btm_api.h" +#include "stack/btm_ble_api.h" +#include "bta/bta_hh_co.h" +#include "bta/bta_gatt_api.h" +#include "srvc_api.h" +#include "btm_int.h" +#include "bta/utl.h" + +#define LOG_TAG "bt_bta_hh" +// #include "osi/include/log.h" + +#ifndef BTA_HH_LE_RECONN +#define BTA_HH_LE_RECONN TRUE +#endif + +#define BTA_HH_APP_ID_LE 0xff + +#define BTA_HH_LE_RPT_TYPE_VALID(x) ((x) <= BTA_LE_HID_RPT_FEATURE && (x)>=BTA_LE_HID_RPT_INPUT) + +#define BTA_HH_LE_RPT_INST_ID_MAP(s,c) (UINT8)(((s)<<4)|(c)) +#define BTA_HH_LE_RPT_GET_SRVC_INST_ID(x) (UINT8)(x >> 4) +#define BTA_HH_LE_RPT_GET_RPT_INST_ID(x) (UINT8)(x & 0x0f) + + +#define BTA_HH_LE_PROTO_BOOT_MODE 0x00 +#define BTA_HH_LE_PROTO_REPORT_MODE 0x01 + +#define BTA_HH_SCPP_INST_DEF 0 + +#define BTA_HH_LE_DISC_CHAR_NUM 8 +static const UINT16 bta_hh_le_disc_char_uuid[BTA_HH_LE_DISC_CHAR_NUM] = { + GATT_UUID_HID_INFORMATION, + GATT_UUID_HID_REPORT_MAP, + GATT_UUID_HID_CONTROL_POINT, + GATT_UUID_HID_REPORT, + GATT_UUID_HID_BT_KB_INPUT, + GATT_UUID_HID_BT_KB_OUTPUT, + GATT_UUID_HID_BT_MOUSE_INPUT, + GATT_UUID_HID_PROTO_MODE /* always make sure this is the last attribute to discover */ +}; + +#define BTA_LE_HID_RTP_UUID_MAX 5 +static const UINT16 bta_hh_uuid_to_rtp_type[BTA_LE_HID_RTP_UUID_MAX][2] = { + {GATT_UUID_HID_REPORT, BTA_HH_RPTT_INPUT}, + {GATT_UUID_HID_BT_KB_INPUT, BTA_HH_RPTT_INPUT}, + {GATT_UUID_HID_BT_KB_OUTPUT, BTA_HH_RPTT_OUTPUT}, + {GATT_UUID_HID_BT_MOUSE_INPUT, BTA_HH_RPTT_INPUT}, + {GATT_UUID_BATTERY_LEVEL, BTA_HH_RPTT_INPUT} +}; + + +static void bta_hh_gattc_callback(tBTA_GATTC_EVT event, tBTA_GATTC *p_data); +static void bta_hh_le_search_hid_chars(tBTA_HH_DEV_CB *p_dev_cb); +static void bta_hh_le_search_hid_included(tBTA_HH_DEV_CB *p_dev_cb); +static void bta_hh_le_search_scps(tBTA_HH_DEV_CB *p_cb); +static void bta_hh_le_search_scps_chars(tBTA_HH_DEV_CB *p_cb); +static void bta_hh_le_register_scpp_notif(tBTA_HH_DEV_CB *p_dev_cb, tBTA_GATT_STATUS status); +static void bta_hh_le_register_scpp_notif_cmpl(tBTA_HH_DEV_CB *p_dev_cb, tBTA_GATT_STATUS status); +static void bta_hh_le_add_dev_bg_conn(tBTA_HH_DEV_CB *p_cb, BOOLEAN check_bond); +static void bta_hh_process_cache_rpt (tBTA_HH_DEV_CB *p_cb, + tBTA_HH_RPT_CACHE_ENTRY *p_rpt_cache, + UINT8 num_rpt); + +#define BTA_HH_LE_SRVC_DEF 0 + +#if BTA_HH_DEBUG == TRUE +static const char *bta_hh_le_rpt_name[4] = { + "UNKNOWN", + "INPUT", + "OUTPUT", + "FEATURE" +}; + +/******************************************************************************* +** +** Function bta_hh_le_hid_report_dbg +** +** Description debug function to print out all HID report available on remote +** device. +** +** Returns void +** +*******************************************************************************/ +static void bta_hh_le_hid_report_dbg(tBTA_HH_DEV_CB *p_cb) +{ + UINT8 i , j; + tBTA_HH_LE_RPT *p_rpt; + char *rpt_name; + + APPL_TRACE_DEBUG("HID Report DB"); + for (i = 0; i < BTA_HH_LE_HID_SRVC_MAX; i ++) { + if (p_cb->hid_srvc[i].in_use) { + p_rpt = &p_cb->hid_srvc[i].report[0]; + + APPL_TRACE_DEBUG("\t HID serivce inst: %d", i); + + for (j = 0; j < BTA_HH_LE_RPT_MAX; j ++, p_rpt++) { + rpt_name = "Unknown"; + if (p_rpt->in_use) { + if (p_rpt->uuid == GATT_UUID_HID_REPORT) { + rpt_name = "Report"; + } + if (p_rpt->uuid == GATT_UUID_HID_BT_KB_INPUT) { + rpt_name = "Boot KB Input"; + } + if (p_rpt->uuid == GATT_UUID_HID_BT_KB_OUTPUT) { + rpt_name = "Boot KB Output"; + } + if (p_rpt->uuid == GATT_UUID_HID_BT_MOUSE_INPUT) { + rpt_name = "Boot MI Input"; + } + + + APPL_TRACE_DEBUG("\t\t [%s- 0x%04x] [Type: %s], [ReportID: %d] [inst_id: %d] [Clt_cfg: %d]", + rpt_name, + p_rpt->uuid , + ((p_rpt->rpt_type < 4) ? bta_hh_le_rpt_name[p_rpt->rpt_type] : "UNKNOWN"), + p_rpt->rpt_id, + p_rpt->inst_id, + p_rpt->client_cfg_value); + } else { + break; + } + } + } else { + break; + } + } +} + +/******************************************************************************* +** +** Function bta_hh_uuid_to_str +** +** Description +** +** Returns void +** +*******************************************************************************/ +static char *bta_hh_uuid_to_str(UINT16 uuid) +{ + switch (uuid) { + case GATT_UUID_HID_INFORMATION: + return "GATT_UUID_HID_INFORMATION"; + case GATT_UUID_HID_REPORT_MAP: + return "GATT_UUID_HID_REPORT_MAP"; + case GATT_UUID_HID_CONTROL_POINT: + return "GATT_UUID_HID_CONTROL_POINT"; + case GATT_UUID_HID_REPORT: + return "GATT_UUID_HID_REPORT"; + case GATT_UUID_HID_PROTO_MODE: + return "GATT_UUID_HID_PROTO_MODE"; + case GATT_UUID_HID_BT_KB_INPUT: + return "GATT_UUID_HID_BT_KB_INPUT"; + case GATT_UUID_HID_BT_KB_OUTPUT: + return "GATT_UUID_HID_BT_KB_OUTPUT"; + case GATT_UUID_HID_BT_MOUSE_INPUT: + return "GATT_UUID_HID_BT_MOUSE_INPUT"; + case GATT_UUID_CHAR_CLIENT_CONFIG: + return "GATT_UUID_CHAR_CLIENT_CONFIG"; + case GATT_UUID_EXT_RPT_REF_DESCR: + return "GATT_UUID_EXT_RPT_REF_DESCR"; + case GATT_UUID_RPT_REF_DESCR: + return "GATT_UUID_RPT_REF_DESCR"; + default: + return "Unknown UUID"; + } +} + +#endif +/******************************************************************************* +** +** Function bta_hh_le_enable +** +** Description initialize LE HID related functionality +** +** +** Returns void +** +*******************************************************************************/ +void bta_hh_le_enable(void) +{ + char app_name[LEN_UUID_128 + 1]; + tBT_UUID app_uuid = {LEN_UUID_128, {0}}; + UINT8 xx; + + bta_hh_cb.gatt_if = BTA_GATTS_INVALID_IF; + + for (xx = 0; xx < BTA_HH_MAX_DEVICE; xx ++) { + bta_hh_cb.le_cb_index[xx] = BTA_HH_IDX_INVALID; + } + + memset (app_name, 0, LEN_UUID_128 + 1); + strncpy(app_name, "BTA HH OVER LE", LEN_UUID_128); + + memcpy((void *)app_uuid.uu.uuid128, (void *)app_name, LEN_UUID_128); + + BTA_GATTC_AppRegister(&app_uuid, bta_hh_gattc_callback); + + return; +} + +/******************************************************************************* +** +** Function bta_hh_le_register_cmpl +** +** Description BTA HH register with BTA GATTC completed +** +** Parameters: +** +*******************************************************************************/ +void bta_hh_le_register_cmpl(tBTA_GATTC_REG *p_reg) +{ + tBTA_HH_STATUS status = BTA_HH_ERR; + + if (p_reg->status == BTA_GATT_OK) { + bta_hh_cb.gatt_if = p_reg->client_if; + status = BTA_HH_OK; + } else { + bta_hh_cb.gatt_if = BTA_GATTS_INVALID_IF; + } + + /* signal BTA call back event */ + (* bta_hh_cb.p_cback)(BTA_HH_ENABLE_EVT, (tBTA_HH *)&status); +} + +/******************************************************************************* +** +** Function bta_hh_le_is_hh_gatt_if +** +** Description Check to see if client_if is BTA HH LE GATT interface +** +** +** Returns whether it is HH GATT IF +** +*******************************************************************************/ +BOOLEAN bta_hh_le_is_hh_gatt_if(tBTA_GATTC_IF client_if) +{ + return (bta_hh_cb.gatt_if == client_if); +} + +/******************************************************************************* +** +** Function bta_hh_le_deregister +** +** Description De-register BTA HH from BTA GATTC +** +** +** Returns void +** +*******************************************************************************/ +void bta_hh_le_deregister(void) +{ + BTA_GATTC_AppDeregister(bta_hh_cb.gatt_if); +} + +/******************************************************************************* +** +** Function bta_hh_is_le_device +** +** Description Check to see if the remote device is a LE only device +** +** Parameters: +** +*******************************************************************************/ +BOOLEAN bta_hh_is_le_device(tBTA_HH_DEV_CB *p_cb, BD_ADDR remote_bda) +{ + p_cb->is_le_device = BTM_UseLeLink (remote_bda); + + return p_cb->is_le_device; +} + +/******************************************************************************* +** +** Function bta_hh_le_add_hid_srvc_entry +** +** Description Add a HID service entry in the HID device control block +** +** Parameters: +** +*******************************************************************************/ +BOOLEAN bta_hh_le_add_hid_srvc_entry(tBTA_HH_DEV_CB *p_dev_cb, UINT8 idx) +{ + BOOLEAN added = FALSE; + + if (idx < BTA_HH_LE_HID_SRVC_MAX) { + p_dev_cb->hid_srvc[idx].in_use = TRUE; + added = TRUE; + } else { + APPL_TRACE_ERROR("DB full,max HID service entry!"); + } + return added; +} + +/******************************************************************************* +** +** Function bta_hh_le_open_conn +** +** Description open a GATT connection first. +** +** Parameters: +** +*******************************************************************************/ +void bta_hh_le_open_conn(tBTA_HH_DEV_CB *p_cb, BD_ADDR remote_bda) +{ + /* update cb_index[] map */ + p_cb->hid_handle = BTA_HH_GET_LE_DEV_HDL(p_cb->index); + memcpy(p_cb->addr, remote_bda, BD_ADDR_LEN); + bta_hh_cb.le_cb_index[BTA_HH_GET_LE_CB_IDX(p_cb->hid_handle)] = p_cb->index; + p_cb->in_use = TRUE; + + BTA_GATTC_Open(bta_hh_cb.gatt_if, remote_bda, BLE_ADDR_UNKNOWN_TYPE, TRUE, BTA_GATT_TRANSPORT_LE, FALSE); +} + +/******************************************************************************* +** +** Function bta_hh_le_fill_16bits_gatt_id +** +** Description Utility function to fill a GATT ID strucure +** +*******************************************************************************/ +void bta_hh_le_fill_16bits_gatt_id(UINT8 inst_id, UINT16 uuid, tBTA_GATT_ID *p_output) +{ + p_output->inst_id = inst_id; + p_output->uuid.len = LEN_UUID_16; + p_output->uuid.uu.uuid16 = uuid; +} + +/******************************************************************************* +** +** Function bta_hh_le_fill_16bits_srvc_id +** +** Description Utility function to fill a service ID strucure with a 16 bits +** service UUID. +** +*******************************************************************************/ +void bta_hh_le_fill_16bits_srvc_id(BOOLEAN is_pri, UINT8 inst_id, UINT16 srvc_uuid, + tBTA_GATT_SRVC_ID *p_output) +{ + memset((void *)p_output, 0, sizeof(tBTA_GATT_SRVC_ID)); + p_output->is_primary = is_pri; + bta_hh_le_fill_16bits_gatt_id(inst_id, srvc_uuid, &p_output->id); + +} + +/******************************************************************************* +** +** Function bta_hh_le_fill_16bits_char_id +** +** Description Utility function to fill a char ID strucure with a 16 bits +** char UUID. +** +*******************************************************************************/ +void bta_hh_le_fill_16bits_char_id(UINT8 inst_id, UINT16 char_uuid, + tBTA_GATT_ID *p_output) +{ + memset((void *)p_output, 0, sizeof(tBTA_GATT_ID)); + bta_hh_le_fill_16bits_gatt_id(inst_id, char_uuid, p_output); +} + +/******************************************************************************* +** +** Function bta_hh_le_find_dev_cb_by_conn_id +** +** Description Utility function find a device control block by connection ID. +** +*******************************************************************************/ +tBTA_HH_DEV_CB *bta_hh_le_find_dev_cb_by_conn_id(UINT16 conn_id) +{ + UINT8 i; + tBTA_HH_DEV_CB *p_dev_cb = &bta_hh_cb.kdev[0]; + + for (i = 0; i < BTA_HH_MAX_DEVICE; i ++, p_dev_cb ++) { + if (p_dev_cb->in_use && p_dev_cb->conn_id == conn_id) { + return p_dev_cb; + } + } + return NULL; +} + +/******************************************************************************* +** +** Function bta_hh_le_find_dev_cb_by_bda +** +** Description Utility function find a device control block by BD address. +** +*******************************************************************************/ +tBTA_HH_DEV_CB *bta_hh_le_find_dev_cb_by_bda(BD_ADDR bda) +{ + UINT8 i; + tBTA_HH_DEV_CB *p_dev_cb = &bta_hh_cb.kdev[0]; + + for (i = 0; i < BTA_HH_MAX_DEVICE; i ++, p_dev_cb ++) { + if (p_dev_cb->in_use && + memcmp(p_dev_cb->addr, bda, BD_ADDR_LEN) == 0) { + return p_dev_cb; + } + } + return NULL; +} + +/******************************************************************************* +** +** Function bta_hh_le_find_service_inst_by_battery_inst_id +** +** Description find HID service instance ID by battery service instance ID +** +*******************************************************************************/ +UINT8 bta_hh_le_find_service_inst_by_battery_inst_id(tBTA_HH_DEV_CB *p_cb, UINT8 ba_inst_id) +{ + UINT8 i; + + for (i = 0; i < BTA_HH_LE_HID_SRVC_MAX; i ++) { + if (p_cb->hid_srvc[i].in_use && + p_cb->hid_srvc[i].incl_srvc_inst == ba_inst_id) { + return i; + } + } + return BTA_HH_IDX_INVALID; +} + +/******************************************************************************* +** +** Function bta_hh_le_find_report_entry +** +** Description find the report entry by service instance and report UUID and +** instance ID +** +*******************************************************************************/ +tBTA_HH_LE_RPT *bta_hh_le_find_report_entry(tBTA_HH_DEV_CB *p_cb, + UINT8 srvc_inst_id, /* service instance ID */ + UINT16 rpt_uuid, + UINT8 char_inst_id) +{ + UINT8 i; + UINT8 hid_inst_id = srvc_inst_id; + tBTA_HH_LE_RPT *p_rpt; + + if (rpt_uuid == GATT_UUID_BATTERY_LEVEL) { + hid_inst_id = bta_hh_le_find_service_inst_by_battery_inst_id(p_cb, srvc_inst_id); + + if (hid_inst_id == BTA_HH_IDX_INVALID) { + return NULL; + } + } + + p_rpt = &p_cb->hid_srvc[hid_inst_id].report[0]; + + for (i = 0; i < BTA_HH_LE_RPT_MAX; i ++, p_rpt ++) { + if (p_rpt->uuid == rpt_uuid && + p_rpt->inst_id == BTA_HH_LE_RPT_INST_ID_MAP(srvc_inst_id, char_inst_id)) { + + return p_rpt; + } + } + return NULL; + +} + +/******************************************************************************* +** +** Function bta_hh_le_find_rpt_by_idtype +** +** Description find a report entry by report ID and protocol mode +** +** Returns void +** +*******************************************************************************/ +tBTA_HH_LE_RPT *bta_hh_le_find_rpt_by_idtype(tBTA_HH_LE_RPT *p_head, UINT8 mode, + tBTA_HH_RPT_TYPE r_type, UINT8 rpt_id) +{ + tBTA_HH_LE_RPT *p_rpt = p_head; + UINT8 i; + +#if BTA_HH_DEBUG == TRUE + APPL_TRACE_DEBUG("bta_hh_le_find_rpt_by_idtype: r_type: %d rpt_id: %d", r_type, rpt_id); +#endif + + for (i = 0 ; i < BTA_HH_LE_RPT_MAX; i ++, p_rpt++) { + if (p_rpt->in_use && p_rpt->rpt_id == rpt_id && r_type == p_rpt->rpt_type) { + /* return battery report w/o condition */ + if (p_rpt->uuid == GATT_UUID_BATTERY_LEVEL) { + return p_rpt; + } + + if (mode == BTA_HH_PROTO_RPT_MODE && p_rpt->uuid == GATT_UUID_HID_REPORT) { + return p_rpt; + } + + if ( mode == BTA_HH_PROTO_BOOT_MODE && + (p_rpt->uuid >= GATT_UUID_HID_BT_KB_INPUT && p_rpt->uuid <= GATT_UUID_HID_BT_MOUSE_INPUT)) { + return p_rpt; + } + } + } + return NULL; +} + +/******************************************************************************* +** +** Function bta_hh_le_find_alloc_report_entry +** +** Description find or allocate a report entry in the HID service report list. +** +*******************************************************************************/ +tBTA_HH_LE_RPT *bta_hh_le_find_alloc_report_entry(tBTA_HH_DEV_CB *p_cb, + UINT8 srvc_inst_id, + UINT16 rpt_uuid, + UINT8 inst_id, + UINT8 prop) +{ + UINT8 i, hid_inst_id = srvc_inst_id; + tBTA_HH_LE_RPT *p_rpt; + + if (rpt_uuid == GATT_UUID_BATTERY_LEVEL) { + hid_inst_id = bta_hh_le_find_service_inst_by_battery_inst_id(p_cb, srvc_inst_id); + + if (hid_inst_id == BTA_HH_IDX_INVALID) { + return NULL; + } + } + p_rpt = &p_cb->hid_srvc[hid_inst_id].report[0]; + + for (i = 0; i < BTA_HH_LE_RPT_MAX; i ++, p_rpt ++) { + if (!p_rpt->in_use || + (p_rpt->uuid == rpt_uuid && + p_rpt->inst_id == BTA_HH_LE_RPT_INST_ID_MAP(srvc_inst_id, inst_id))) { + if (!p_rpt->in_use) { + p_rpt->in_use = TRUE; + p_rpt->index = i; + p_rpt->inst_id = BTA_HH_LE_RPT_INST_ID_MAP(srvc_inst_id, inst_id); + p_rpt->prop = prop; + p_rpt->uuid = rpt_uuid; + + /* assign report type */ + for (i = 0; i < BTA_LE_HID_RTP_UUID_MAX; i ++) { + if (bta_hh_uuid_to_rtp_type[i][0] == rpt_uuid) { + p_rpt->rpt_type = (tBTA_HH_RPT_TYPE)bta_hh_uuid_to_rtp_type[i][1]; + + if (rpt_uuid == GATT_UUID_HID_BT_KB_INPUT || rpt_uuid == GATT_UUID_HID_BT_KB_OUTPUT) { + p_rpt->rpt_id = BTA_HH_KEYBD_RPT_ID; + } + + if (rpt_uuid == GATT_UUID_HID_BT_MOUSE_INPUT) { + p_rpt->rpt_id = BTA_HH_MOUSE_RPT_ID; + } + + break; + } + } + } + return p_rpt; + } + } + return NULL; +} + +/******************************************************************************* +** +** Function bta_hh_le_read_char_dscrpt +** +** Description read characteristic descriptor +** +*******************************************************************************/ +tBTA_HH_STATUS bta_hh_le_read_char_dscrpt(tBTA_HH_DEV_CB *p_cb, UINT16 srvc_uuid, UINT8 srvc_inst_id, + UINT16 char_uuid, UINT8 char_inst_id, UINT16 char_descp_uuid) +{ + tBTA_GATTC_CHAR_ID char_id; + tBT_UUID descr_uuid; + tBTA_GATTC_CHAR_DESCR_ID descr_id; + tBTA_HH_STATUS status = BTA_HH_ERR; + + bta_hh_le_fill_16bits_srvc_id(TRUE, srvc_inst_id, srvc_uuid, &char_id.srvc_id); + bta_hh_le_fill_16bits_char_id(char_inst_id, char_uuid, &char_id.char_id); + + descr_uuid.len = LEN_UUID_16; + descr_uuid.uu.uuid16 = char_descp_uuid; + + /* find the report reference descriptor */ + if (BTA_GATTC_GetFirstCharDescr(p_cb->conn_id, + &char_id, + &descr_uuid, + &descr_id) == BTA_GATT_OK) { + BTA_GATTC_ReadCharDescr(p_cb->conn_id, + &descr_id, + BTA_GATT_AUTH_REQ_NONE); + + status = BTA_HH_OK; + } else { +#if BTA_HH_DEBUG == TRUE + APPL_TRACE_WARNING("%s No descriptor exists: %s(0x%04x)", __func__, + bta_hh_uuid_to_str(char_descp_uuid), char_descp_uuid); +#endif + } + return status; +} + +/******************************************************************************* +** +** Function bta_hh_le_read_rpt_ref_descr +** +** Description read report refernece descriptors in service discovery process +** +*******************************************************************************/ +void bta_hh_le_read_rpt_ref_descr(tBTA_HH_DEV_CB *p_dev_cb, tBTA_HH_LE_RPT *p_rpt) +{ + BOOLEAN started = FALSE; + UINT16 srvc_uuid, char_uuid; + + while (p_rpt != NULL) { + if (!p_rpt->in_use) { + break; + } + + if (p_rpt->rpt_type == BTA_HH_RPTT_INPUT) { + /* is battery report */ + if (p_rpt->uuid == GATT_UUID_BATTERY_LEVEL) { +#if BTA_HH_DEBUG == TRUE + APPL_TRACE_DEBUG("read battery level report reference descriptor"); +#endif + srvc_uuid = UUID_SERVCLASS_BATTERY; + char_uuid = GATT_UUID_BATTERY_LEVEL; + } else { +#if BTA_HH_DEBUG == TRUE + APPL_TRACE_DEBUG("read HID report reference descriptor"); +#endif + srvc_uuid = UUID_SERVCLASS_LE_HID; + char_uuid = GATT_UUID_HID_REPORT; + } + + if (bta_hh_le_read_char_dscrpt(p_dev_cb, + srvc_uuid, + BTA_HH_LE_RPT_GET_SRVC_INST_ID(p_rpt->inst_id), + char_uuid, + BTA_HH_LE_RPT_GET_RPT_INST_ID(p_rpt->inst_id), + GATT_UUID_RPT_REF_DESCR) + == BTA_HH_OK) { + started = TRUE; + break; + } + } + + if (p_rpt->index == BTA_HH_LE_RPT_MAX - 1) { + break; + } + + p_rpt ++; + } + + + /* if no report reference descriptor */ + if (!started) { + /* explore next char */ + bta_hh_le_search_hid_chars(p_dev_cb); + } +} + +/******************************************************************************* +** +** Function bta_hh_le_save_rpt_ref +** +** Description save report reference information and move to next one. +** +** Parameters: +** +*******************************************************************************/ +void bta_hh_le_save_rpt_ref(tBTA_HH_DEV_CB *p_dev_cb, tBTA_HH_LE_RPT *p_rpt, + tBTA_GATTC_READ *p_data) +{ + UINT8 *pp; + tBTA_HH_RPT_CACHE_ENTRY rpt_entry; + + /* if the length of the descriptor value is right, parse it */ + if (p_data->status == BTA_GATT_OK && + p_data->p_value && p_data->p_value->unformat.len == 2) { + pp = p_data->p_value->unformat.p_value; + + STREAM_TO_UINT8(p_rpt->rpt_id, pp); + STREAM_TO_UINT8(p_rpt->rpt_type, pp); + + if (p_rpt->rpt_type > BTA_HH_RPTT_FEATURE) { /* invalid report type */ + p_rpt->rpt_type = BTA_HH_RPTT_RESRV; + } + +#if BTA_HH_DEBUG == TRUE + APPL_TRACE_DEBUG("report ID: %d", p_rpt->rpt_id); +#endif + rpt_entry.rpt_id = p_rpt->rpt_id; + rpt_entry.rpt_type = p_rpt->rpt_type; + rpt_entry.rpt_uuid = p_rpt->uuid; + rpt_entry.prop = p_rpt->prop; + rpt_entry.inst_id = p_rpt->inst_id; + + bta_hh_le_co_rpt_info(p_dev_cb->addr, + &rpt_entry, + p_dev_cb->app_id); + } else if (p_data->status == BTA_GATT_INSUF_AUTHENTICATION) { + /* close connection right away */ + p_dev_cb->status = BTA_HH_ERR_AUTH_FAILED; + /* close the connection and report service discovery complete with error */ + bta_hh_le_api_disc_act(p_dev_cb); + return; + } + + if (p_rpt->index < BTA_HH_LE_RPT_MAX - 1) { + p_rpt ++; + } else { + p_rpt = NULL; + } + + /* read next report reference descriptor */ + bta_hh_le_read_rpt_ref_descr(p_dev_cb, p_rpt); + +} + +/******************************************************************************* +** +** Function bta_hh_le_save_rpt_ref +** +** Description save report reference information and move to next one. +** +** Parameters: +** +*******************************************************************************/ +void bta_hh_le_save_ext_rpt_ref(tBTA_HH_DEV_CB *p_dev_cb, + tBTA_GATTC_READ *p_data) +{ + UINT8 *pp; + + /* if the length of the descriptor value is right, parse it + assume it's a 16 bits UUID */ + if (p_data->status == BTA_GATT_OK && + p_data->p_value && p_data->p_value->unformat.len == 2) { + pp = p_data->p_value->unformat.p_value; + STREAM_TO_UINT16(p_dev_cb->hid_srvc[p_dev_cb->cur_srvc_index].ext_rpt_ref, pp); + +#if BTA_HH_DEBUG == TRUE + APPL_TRACE_DEBUG("External Report Reference UUID 0x%04x", + p_dev_cb->hid_srvc[p_dev_cb->cur_srvc_index].ext_rpt_ref); +#endif + } + bta_hh_le_search_hid_chars(p_dev_cb); + +} + +/******************************************************************************* +** +** Function bta_hh_le_register_input_notif +** +** Description Register for all notifications for the report applicable +** for the protocol mode. +** +** Parameters: +** +*******************************************************************************/ +void bta_hh_le_register_input_notif(tBTA_HH_DEV_CB *p_dev_cb, UINT8 srvc_inst, + UINT8 proto_mode, BOOLEAN register_ba) +{ + tBTA_HH_LE_RPT *p_rpt = &p_dev_cb->hid_srvc[srvc_inst].report[0]; + tBTA_GATTC_CHAR_ID char_id; + UINT8 i; + UINT16 srvc_uuid; + +#if BTA_HH_DEBUG == TRUE + APPL_TRACE_DEBUG("bta_hh_le_register_input_notif mode: %d", proto_mode); +#endif + + for (i = 0; i < BTA_HH_LE_RPT_MAX; i ++, p_rpt ++) { + if (p_rpt->rpt_type == BTA_HH_RPTT_INPUT) { + if (p_rpt->uuid == GATT_UUID_BATTERY_LEVEL) { + srvc_uuid = UUID_SERVCLASS_BATTERY; + } else { + srvc_uuid = UUID_SERVCLASS_LE_HID; + } + + bta_hh_le_fill_16bits_srvc_id(TRUE, BTA_HH_LE_RPT_GET_SRVC_INST_ID(p_rpt->inst_id), srvc_uuid, &char_id.srvc_id); + bta_hh_le_fill_16bits_char_id(BTA_HH_LE_RPT_GET_RPT_INST_ID(p_rpt->inst_id), p_rpt->uuid, &char_id.char_id); + + if (register_ba && p_rpt->uuid == GATT_UUID_BATTERY_LEVEL) { + BTA_GATTC_RegisterForNotifications(bta_hh_cb.gatt_if, + p_dev_cb->addr, + &char_id); + } + /* boot mode, deregister report input notification */ + else if (proto_mode == BTA_HH_PROTO_BOOT_MODE) { + if (p_rpt->uuid == GATT_UUID_HID_REPORT && + p_rpt->client_cfg_value == BTA_GATT_CLT_CONFIG_NOTIFICATION) { + APPL_TRACE_DEBUG("---> Deregister Report ID: %d", p_rpt->rpt_id); + BTA_GATTC_DeregisterForNotifications(bta_hh_cb.gatt_if, + p_dev_cb->addr, + &char_id); + } + /* register boot reports notification */ + else if (p_rpt->uuid == GATT_UUID_HID_BT_KB_INPUT || + p_rpt->uuid == GATT_UUID_HID_BT_MOUSE_INPUT) { + APPL_TRACE_DEBUG("<--- Register Boot Report ID: %d", p_rpt->rpt_id); + BTA_GATTC_RegisterForNotifications(bta_hh_cb.gatt_if, + p_dev_cb->addr, + &char_id); + } + } else if (proto_mode == BTA_HH_PROTO_RPT_MODE) { + if ((p_rpt->uuid == GATT_UUID_HID_BT_KB_INPUT || + p_rpt->uuid == GATT_UUID_HID_BT_MOUSE_INPUT) && + p_rpt->client_cfg_value == BTA_GATT_CLT_CONFIG_NOTIFICATION) { + + APPL_TRACE_DEBUG("---> Deregister Boot Report ID: %d", p_rpt->rpt_id); + BTA_GATTC_DeregisterForNotifications(bta_hh_cb.gatt_if, + p_dev_cb->addr, + &char_id); + } else if (p_rpt->uuid == GATT_UUID_HID_REPORT && + p_rpt->client_cfg_value == BTA_GATT_CLT_CONFIG_NOTIFICATION) { + APPL_TRACE_DEBUG("<--- Register Report ID: %d", p_rpt->rpt_id); + BTA_GATTC_RegisterForNotifications(bta_hh_cb.gatt_if, + p_dev_cb->addr, + &char_id); + } + } + /* + else unknow protocol mode */ + } + } +} + +/******************************************************************************* +** +** Function bta_hh_le_open_cmpl +** +** Description HID over GATT connection successfully opened +** +*******************************************************************************/ +void bta_hh_le_open_cmpl(tBTA_HH_DEV_CB *p_cb) +{ + if ( p_cb->disc_active == BTA_HH_LE_DISC_NONE) { +#if BTA_HH_DEBUG + bta_hh_le_hid_report_dbg(p_cb); +#endif + bta_hh_le_register_input_notif(p_cb, 0, p_cb->mode, TRUE); + bta_hh_sm_execute(p_cb, BTA_HH_OPEN_CMPL_EVT, NULL); + +#if (BTA_HH_LE_RECONN == TRUE) + if (p_cb->status == BTA_HH_OK) { + bta_hh_le_add_dev_bg_conn(p_cb, TRUE); + } +#endif + } +} + +/******************************************************************************* +** +** Function bta_hh_le_write_char_clt_cfg +** +** Description Utility function to find and write client configuration of +** a characteristic +** +*******************************************************************************/ +BOOLEAN bta_hh_le_write_char_clt_cfg(tBTA_HH_DEV_CB *p_cb, + UINT8 srvc_inst_id, UINT16 srvc_uuid16, + UINT8 char_inst_id, UINT16 char_uuid16, + UINT16 clt_cfg_value) +{ + tBTA_GATTC_CHAR_ID char_id; + tBT_UUID descr_cond; + tBTA_GATTC_CHAR_DESCR_ID descr_id; + tBTA_GATT_UNFMT value; + UINT8 buf[2], *pp = buf; + + bta_hh_le_fill_16bits_srvc_id(TRUE, srvc_inst_id, srvc_uuid16, &char_id.srvc_id); + bta_hh_le_fill_16bits_char_id(char_inst_id, char_uuid16, &char_id.char_id); + + descr_cond.len = LEN_UUID_16; + descr_cond.uu.uuid16 = GATT_UUID_CHAR_CLIENT_CONFIG; + + value.len = 2; + value.p_value = buf; + + UINT16_TO_STREAM(pp, clt_cfg_value); + + if (BTA_GATTC_GetFirstCharDescr(p_cb->conn_id, + &char_id, + &descr_cond, + &descr_id) == BTA_GATT_OK) { + BTA_GATTC_WriteCharDescr(p_cb->conn_id, + &descr_id, + BTA_GATTC_TYPE_WRITE, + &value, + BTA_GATT_AUTH_REQ_NONE); + + return TRUE; + } + return FALSE; +} + +/******************************************************************************* +** +** Function bta_hh_le_write_rpt_clt_cfg +** +** Description write client configuration. This is only for input report +** enable all input notification upon connection open. +** +*******************************************************************************/ +BOOLEAN bta_hh_le_write_rpt_clt_cfg(tBTA_HH_DEV_CB *p_cb, UINT8 srvc_inst_id) +{ + UINT8 i; + tBTA_HH_LE_RPT *p_rpt = &p_cb->hid_srvc[srvc_inst_id].report[p_cb->clt_cfg_idx]; + UINT16 srvc_uuid; + + for (i = p_cb->clt_cfg_idx; i < BTA_HH_LE_RPT_MAX && p_rpt->in_use; i ++, p_rpt ++) { + /* enable notification for all input report, regardless mode */ + if (p_rpt->rpt_type == BTA_HH_RPTT_INPUT) + + { + if (p_rpt->uuid == GATT_UUID_BATTERY_LEVEL) { + srvc_uuid = UUID_SERVCLASS_BATTERY; + } else { + srvc_uuid = UUID_SERVCLASS_LE_HID; + } + + if (bta_hh_le_write_char_clt_cfg(p_cb, + BTA_HH_LE_RPT_GET_SRVC_INST_ID(p_rpt->inst_id), + srvc_uuid, + BTA_HH_LE_RPT_GET_RPT_INST_ID(p_rpt->inst_id), + p_rpt->uuid, + BTA_GATT_CLT_CONFIG_NOTIFICATION)) { + p_cb->clt_cfg_idx = i; + return TRUE; + } + } + + } + p_cb->clt_cfg_idx = 0; + + /* client configuration is completed, send open callback */ + if (p_cb->state == BTA_HH_W4_CONN_ST) { + p_cb->disc_active &= ~BTA_HH_LE_DISC_HIDS; + + /* discover scan parameter profile is act as report host */ + bta_hh_le_search_scps(p_cb); + } + return FALSE; +} + +/******************************************************************************* +** +** Function bta_hh_le_set_protocol_mode +** +** Description Set remote device protocol mode. +** +*******************************************************************************/ +BOOLEAN bta_hh_le_set_protocol_mode(tBTA_HH_DEV_CB *p_cb, tBTA_HH_PROTO_MODE mode) +{ + tBTA_GATTC_CHAR_ID char_id; + tBTA_HH_CBDATA cback_data ; + BOOLEAN exec = FALSE; + + APPL_TRACE_DEBUG("bta_hh_le_set_protocol_mode attempt mode: %s", + (mode == BTA_HH_PROTO_RPT_MODE) ? "Report" : "Boot"); + + cback_data.handle = p_cb->hid_handle; + /* boot mode is not supported in the remote device */ + if ((p_cb->hid_srvc[BTA_HH_LE_SRVC_DEF].option_char & BTA_HH_LE_PROTO_MODE_BIT) == 0) { + p_cb->mode = BTA_HH_PROTO_RPT_MODE; + + if (mode == BTA_HH_PROTO_BOOT_MODE) { + APPL_TRACE_ERROR("Set Boot Mode failed!! No PROTO_MODE Char!"); + cback_data.status = BTA_HH_ERR; + } else { + /* if set to report mode, need to de-register all input report notification */ + bta_hh_le_register_input_notif(p_cb, 0, p_cb->mode, FALSE); + cback_data.status = BTA_HH_OK; + } + if (p_cb->state == BTA_HH_W4_CONN_ST) { + p_cb->status = (cback_data.status == BTA_HH_OK) ? BTA_HH_OK : BTA_HH_ERR_PROTO; + } else { + (* bta_hh_cb.p_cback)(BTA_HH_SET_PROTO_EVT, (tBTA_HH *)&cback_data); + } + } else if (p_cb->mode != mode) { + bta_hh_le_fill_16bits_srvc_id(TRUE, 0, UUID_SERVCLASS_LE_HID, &char_id.srvc_id); + bta_hh_le_fill_16bits_char_id(0, GATT_UUID_HID_PROTO_MODE, &char_id.char_id); + + p_cb->mode = mode; + mode = (mode == BTA_HH_PROTO_BOOT_MODE) ? BTA_HH_LE_PROTO_BOOT_MODE : BTA_HH_LE_PROTO_REPORT_MODE; + + BTA_GATTC_WriteCharValue(p_cb->conn_id, + &char_id, + BTA_GATTC_TYPE_WRITE_NO_RSP, + 1, + &mode, + BTA_GATT_AUTH_REQ_NONE); + exec = TRUE; + } + + return exec; +} + +/******************************************************************************* +** +** Function bta_hh_le_get_protocol_mode +** +** Description Get remote device protocol mode. +** +*******************************************************************************/ +void bta_hh_le_get_protocol_mode(tBTA_HH_DEV_CB *p_cb) +{ + tBTA_GATTC_CHAR_ID char_id; + tBTA_HH_HSDATA hs_data; + UINT8 i; + + p_cb->w4_evt = BTA_HH_GET_PROTO_EVT; + + for (i = 0; i < BTA_HH_LE_HID_SRVC_MAX; i ++) { + if (p_cb->hid_srvc[i].in_use && + p_cb->hid_srvc[i].option_char & BTA_HH_LE_PROTO_MODE_BIT) { + bta_hh_le_fill_16bits_srvc_id(TRUE, 0, UUID_SERVCLASS_LE_HID, &char_id.srvc_id); + bta_hh_le_fill_16bits_char_id(0, GATT_UUID_HID_PROTO_MODE, &char_id.char_id); + + BTA_GATTC_ReadCharacteristic(p_cb->conn_id, + &char_id, + BTA_GATT_AUTH_REQ_NONE); + break; + } + } + /* no service support protocol_mode, by default report mode */ + if (i == BTA_HH_LE_HID_SRVC_MAX) { + hs_data.status = BTA_HH_OK; + hs_data.handle = p_cb->hid_handle; + hs_data.rsp_data.proto_mode = BTA_HH_PROTO_RPT_MODE; + p_cb->w4_evt = 0; + (* bta_hh_cb.p_cback)(BTA_HH_GET_PROTO_EVT, (tBTA_HH *)&hs_data); + } + +} + +/******************************************************************************* +** +** Function bta_hh_le_expl_rpt +** +** Description explore all report characteristic +** +*******************************************************************************/ +void bta_hh_le_expl_rpt(tBTA_HH_DEV_CB *p_dev_cb, + tBTA_GATTC_CHAR_ID *p_char_id, + tBT_UUID *p_char_cond, + tBTA_GATT_CHAR_PROP prop) +{ + tBTA_GATTC_CHAR_ID char_result; + + do { + if (bta_hh_le_find_alloc_report_entry(p_dev_cb, + p_dev_cb->cur_srvc_index, + GATT_UUID_HID_REPORT, + p_char_id->char_id.inst_id, + prop) == NULL) { + APPL_TRACE_ERROR("Add report entry failed !!!"); + break; + } + + APPL_TRACE_DEBUG("Find more REPORT"); + + if (BTA_GATTC_GetNextChar(p_dev_cb->conn_id, + p_char_id, + p_char_cond, + &char_result, + &prop) != BTA_GATT_OK) { + break; + } + + p_char_id = &char_result; + } while (1); + + APPL_TRACE_API("%s all BLE reports searched", __func__); + bta_hh_le_read_rpt_ref_descr(p_dev_cb, + &p_dev_cb->hid_srvc[p_dev_cb->cur_srvc_index].report[0]); + + + return ; +} + +/******************************************************************************* +** +** Function bta_hh_le_expl_boot_rpt +** +** Description explore boot report +** +*******************************************************************************/ +void bta_hh_le_expl_boot_rpt(tBTA_HH_DEV_CB *p_dev_cb, UINT16 char_uuid, + tBTA_GATT_CHAR_PROP prop) +{ + if (bta_hh_le_find_alloc_report_entry(p_dev_cb, + p_dev_cb->cur_srvc_index, + char_uuid, + 0, + prop) == NULL) + + { + APPL_TRACE_ERROR("Add report entry failed !!!"); + } + + return; +} + +/******************************************************************************* +** +** Function bta_hh_le_dis_cback +** +** Description DIS read complete callback +** +** Parameters: +** +*******************************************************************************/ +void bta_hh_le_dis_cback(BD_ADDR addr, tDIS_VALUE *p_dis_value) +{ + tBTA_HH_DEV_CB *p_cb = bta_hh_le_find_dev_cb_by_bda(addr); + + + if (p_cb == NULL || p_dis_value == NULL) { + APPL_TRACE_ERROR("received unexpected/error DIS callback"); + return; + } + + p_cb->disc_active &= ~BTA_HH_LE_DISC_DIS; + /* plug in the PnP info for this device */ + if (p_dis_value->attr_mask & DIS_ATTR_PNP_ID_BIT) { +#if BTA_HH_DEBUG == TRUE + APPL_TRACE_DEBUG("Plug in PnP info: product_id = %02x, vendor_id = %04x, version = %04x", + p_dis_value->pnp_id.product_id, + p_dis_value->pnp_id.vendor_id, + p_dis_value->pnp_id.product_version); +#endif + p_cb->dscp_info.product_id = p_dis_value->pnp_id.product_id; + p_cb->dscp_info.vendor_id = p_dis_value->pnp_id.vendor_id; + p_cb->dscp_info.version = p_dis_value->pnp_id.product_version; + } + bta_hh_le_open_cmpl(p_cb); +} + +/******************************************************************************* +** +** Function bta_hh_le_pri_service_discovery +** +** Description Initialize GATT discovery on the remote LE HID device by opening +** a GATT connection first. +** +** Parameters: +** +*******************************************************************************/ +void bta_hh_le_pri_service_discovery(tBTA_HH_DEV_CB *p_cb) +{ + tBT_UUID pri_srvc; + + bta_hh_le_co_reset_rpt_cache(p_cb->addr, p_cb->app_id); + + p_cb->disc_active |= (BTA_HH_LE_DISC_HIDS | BTA_HH_LE_DISC_DIS); + + /* read DIS info */ + if (!DIS_ReadDISInfo(p_cb->addr, bta_hh_le_dis_cback, DIS_ATTR_PNP_ID_BIT)) { + APPL_TRACE_ERROR("read DIS failed"); + p_cb->disc_active &= ~BTA_HH_LE_DISC_DIS; + } + + /* in parallel */ + /* start primary service discovery for HID service */ + pri_srvc.len = LEN_UUID_16; + pri_srvc.uu.uuid16 = UUID_SERVCLASS_LE_HID; + BTA_GATTC_ServiceSearchRequest(p_cb->conn_id, &pri_srvc); + return; +} + +/******************************************************************************* +** +** Function bta_hh_le_encrypt_cback +** +** Description link encryption complete callback for bond verification. +** +** Returns None +** +*******************************************************************************/ +void bta_hh_le_encrypt_cback(BD_ADDR bd_addr, tBTA_GATT_TRANSPORT transport, + void *p_ref_data, tBTM_STATUS result) +{ + UINT8 idx = bta_hh_find_cb(bd_addr); + tBTA_HH_DEV_CB *p_dev_cb; + UNUSED(p_ref_data); + UNUSED (transport); + + if (idx != BTA_HH_IDX_INVALID) { + p_dev_cb = &bta_hh_cb.kdev[idx]; + } else { + APPL_TRACE_ERROR("unexpected encryption callback, ignore"); + return; + } + p_dev_cb->status = (result == BTM_SUCCESS) ? BTA_HH_OK : BTA_HH_ERR_SEC; + p_dev_cb->reason = result; + + bta_hh_sm_execute(p_dev_cb, BTA_HH_ENC_CMPL_EVT, NULL); +} + +/******************************************************************************* +** +** Function bta_hh_security_cmpl +** +** Description Security check completed, start the service discovery +** if no cache available, otherwise report connection open completed +** +** Parameters: +** +*******************************************************************************/ +void bta_hh_security_cmpl(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_buf) +{ + tBTA_HH_RPT_CACHE_ENTRY *p_rpt_cache; + UINT8 num_rpt = 0; + UNUSED(p_buf); + + if (p_cb->status == BTA_HH_OK) { + APPL_TRACE_DEBUG("bta_hh_security_cmpl OK"); + if (!p_cb->hid_srvc[BTA_HH_LE_SRVC_DEF].in_use) { + APPL_TRACE_DEBUG("bta_hh_security_cmpl no reports loaded, try to load"); + /* start loading the cache if not in stack */ + if ((p_rpt_cache = bta_hh_le_co_cache_load(p_cb->addr, &num_rpt, p_cb->app_id)) != NULL) { + bta_hh_process_cache_rpt(p_cb, p_rpt_cache, num_rpt); + } + } + /* discovery has been done for HID service */ + if (p_cb->app_id != 0 && p_cb->hid_srvc[BTA_HH_LE_SRVC_DEF].in_use) { + /* configure protocol mode */ + if (bta_hh_le_set_protocol_mode(p_cb, p_cb->mode) == FALSE) { + APPL_TRACE_ERROR("bta_hh_security_cmpl"); + bta_hh_le_open_cmpl(p_cb); + } + } + /* start primary service discovery for HID service */ + else { + bta_hh_le_pri_service_discovery(p_cb); + } + } else { + APPL_TRACE_ERROR("%s() - encryption failed; status=0x%04x, reason=0x%04x", + __FUNCTION__, p_cb->status, p_cb->reason); + if (!(p_cb->status == BTA_HH_ERR_SEC && p_cb->reason == BTM_ERR_PROCESSING)) { + bta_hh_le_api_disc_act(p_cb); + } + } +} + +/******************************************************************************* +** +** Function bta_hh_le_notify_enc_cmpl +** +** Description process GATT encryption complete event +** +** Returns +** +*******************************************************************************/ +void bta_hh_le_notify_enc_cmpl(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_buf) +{ + if (p_cb == NULL || p_cb->security_pending == FALSE || + p_buf == NULL || p_buf->le_enc_cmpl.client_if != bta_hh_cb.gatt_if) { + return; + } + + p_cb->security_pending = FALSE; + bta_hh_start_security(p_cb, NULL); +} + +/******************************************************************************* +** +** Function bta_hh_clear_service_cache +** +** Description clear the service cache +** +** Parameters: +** +*******************************************************************************/ +void bta_hh_clear_service_cache(tBTA_HH_DEV_CB *p_cb) +{ + UINT8 i; + tBTA_HH_LE_HID_SRVC *p_hid_srvc = &p_cb->hid_srvc[0]; + + p_cb->app_id = 0; + p_cb->total_srvc = 0; + p_cb->dscp_info.descriptor.dsc_list = NULL; + + for (i = 0; i < BTA_HH_LE_HID_SRVC_MAX; i ++, p_hid_srvc ++) { + utl_freebuf((void **)&p_hid_srvc->rpt_map); + memset(p_hid_srvc, 0, sizeof(tBTA_HH_LE_HID_SRVC)); + } +} + +/******************************************************************************* +** +** Function bta_hh_start_security +** +** Description start the security check of the established connection +** +** Parameters: +** +*******************************************************************************/ +void bta_hh_start_security(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_buf) +{ + UINT8 sec_flag = 0; + tBTM_SEC_DEV_REC *p_dev_rec; + UNUSED(p_buf); + + p_dev_rec = btm_find_dev(p_cb->addr); + if (p_dev_rec) { + if (p_dev_rec->sec_state == BTM_SEC_STATE_ENCRYPTING || + p_dev_rec->sec_state == BTM_SEC_STATE_AUTHENTICATING) { + /* if security collision happened, wait for encryption done */ + p_cb->security_pending = TRUE; + return; + } + } + + /* verify bond */ + BTM_GetSecurityFlagsByTransport(p_cb->addr, &sec_flag, BT_TRANSPORT_LE); + + /* if link has been encrypted */ + if (sec_flag & BTM_SEC_FLAG_ENCRYPTED) { + bta_hh_sm_execute(p_cb, BTA_HH_ENC_CMPL_EVT, NULL); + } + /* if bonded and link not encrypted */ + else if (sec_flag & BTM_SEC_FLAG_LKEY_KNOWN) { + sec_flag = BTM_BLE_SEC_ENCRYPT; + p_cb->status = BTA_HH_ERR_AUTH_FAILED; + BTM_SetEncryption(p_cb->addr, BTA_TRANSPORT_LE, bta_hh_le_encrypt_cback, &sec_flag); + } + /* unbonded device, report security error here */ + else if (p_cb->sec_mask != BTA_SEC_NONE) { + sec_flag = BTM_BLE_SEC_ENCRYPT_NO_MITM; + p_cb->status = BTA_HH_ERR_AUTH_FAILED; + bta_hh_clear_service_cache(p_cb); + BTM_SetEncryption(p_cb->addr, BTA_TRANSPORT_LE, bta_hh_le_encrypt_cback, &sec_flag); + } + /* otherwise let it go through */ + else { + bta_hh_sm_execute(p_cb, BTA_HH_ENC_CMPL_EVT, NULL); + } + + +} + +/******************************************************************************* +** +** Function bta_hh_gatt_open +** +** Description process GATT open event. +** +** Parameters: +** +*******************************************************************************/ +void bta_hh_gatt_open(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_buf) +{ + tBTA_GATTC_OPEN *p_data = &p_buf->le_open; + UINT8 *p2; + tHID_STATUS status = BTA_HH_ERR; + + /* if received invalid callback data , ignore it */ + if (p_cb == NULL || p_data == NULL) { + return; + } + + p2 = p_data->remote_bda; + + APPL_TRACE_DEBUG("bta_hh_gatt_open BTA_GATTC_OPEN_EVT bda= [%08x%04x] status =%d", + ((p2[0]) << 24) + ((p2[1]) << 16) + ((p2[2]) << 8) + (p2[3]), + ((p2[4]) << 8) + p2[5], p_data->status); + + if (p_data->status == BTA_GATT_OK) { + p_cb->is_le_device = TRUE; + p_cb->in_use = TRUE; + p_cb->conn_id = p_data->conn_id; + p_cb->hid_handle = BTA_HH_GET_LE_DEV_HDL(p_cb->index); + + bta_hh_cb.le_cb_index[BTA_HH_GET_LE_CB_IDX(p_cb->hid_handle)] = p_cb->index; + +#if BTA_HH_DEBUG == TRUE + APPL_TRACE_DEBUG("hid_handle = %2x conn_id = %04x cb_index = %d", p_cb->hid_handle, p_cb->conn_id, p_cb->index); +#endif + + bta_hh_sm_execute(p_cb, BTA_HH_START_ENC_EVT, NULL); + + } else { /* open failure */ + bta_hh_sm_execute(p_cb, BTA_HH_SDP_CMPL_EVT, (tBTA_HH_DATA *)&status); + } + +} + +/******************************************************************************* +** +** Function bta_hh_le_close +** +** Description This function process the GATT close event and post it as a +** BTA HH internal event +** +** Parameters: +** +*******************************************************************************/ +void bta_hh_le_close(tBTA_GATTC_CLOSE *p_data) +{ + tBTA_HH_DEV_CB *p_dev_cb = bta_hh_le_find_dev_cb_by_bda(p_data->remote_bda); + tBTA_HH_LE_CLOSE *p_buf = NULL; + UINT16 sm_event = BTA_HH_GATT_CLOSE_EVT; + + if (p_dev_cb != NULL && + (p_buf = (tBTA_HH_LE_CLOSE *)osi_malloc(sizeof(tBTA_HH_LE_CLOSE))) != NULL) { + p_buf->hdr.event = sm_event; + p_buf->hdr.layer_specific = (UINT16)p_dev_cb->hid_handle; + p_buf->conn_id = p_data->conn_id; + p_buf->reason = p_data->reason; + + p_dev_cb->conn_id = BTA_GATT_INVALID_CONN_ID; + p_dev_cb->security_pending = FALSE; + bta_sys_sendmsg(p_buf); + } +} + +/******************************************************************************* +** +** Function bta_hh_le_search_result +** +** Description This function process the GATT service search result. +** +** Parameters: +** +*******************************************************************************/ +void bta_hh_le_search_result(tBTA_GATTC_SRVC_RES *p_srvc_result) +{ + tBTA_HH_DEV_CB *p_dev_cb = bta_hh_le_find_dev_cb_by_conn_id(p_srvc_result->conn_id); + + if (p_dev_cb != NULL) { + switch (p_srvc_result->service_uuid.id.uuid.uu.uuid16) { + case UUID_SERVCLASS_LE_HID: + if (p_srvc_result->service_uuid.is_primary) { + /* found HID primamry service */ + /* TODO: proceed to find battery and device info */ + if (bta_hh_le_add_hid_srvc_entry(p_dev_cb, p_dev_cb->total_srvc)) { + p_dev_cb->total_srvc ++; + } + APPL_TRACE_DEBUG("num of hid service: %d", p_dev_cb->total_srvc); + } + break; + + case UUID_SERVCLASS_SCAN_PARAM : /* scan parameter service */ + bta_hh_le_search_scps_chars(p_dev_cb); + break; + } + + } + +} + + +/******************************************************************************* +** +** Function bta_hh_le_gatt_disc_cmpl +** +** Description Check to see if the remote device is a LE only device +** +** Parameters: +** +*******************************************************************************/ +void bta_hh_le_gatt_disc_cmpl(tBTA_HH_DEV_CB *p_cb, tBTA_HH_STATUS status) +{ + APPL_TRACE_DEBUG("bta_hh_le_gatt_disc_cmpl "); + + /* if open sucessful or protocol mode not desired, keep the connection open but inform app */ + if (status == BTA_HH_OK || status == BTA_HH_ERR_PROTO) { + /* assign a special APP ID temp, since device type unknown */ + p_cb->app_id = BTA_HH_APP_ID_LE; + + /* set report notification configuration */ + p_cb->clt_cfg_idx = 0; + bta_hh_le_write_rpt_clt_cfg(p_cb, BTA_HH_LE_SRVC_DEF); + } else { /* error, close the GATT connection */ + /* close GATT connection if it's on */ + bta_hh_le_api_disc_act(p_cb); + } +} + +/******************************************************************************* +** +** Function bta_hh_le_srvc_expl_srvc +** +** Description This function discover the next avaible HID service. +** +** Parameters: +** +*******************************************************************************/ +void bta_hh_le_srvc_expl_srvc(tBTA_HH_DEV_CB *p_dev_cb) +{ +#if BTA_HH_DEBUG == TRUE + APPL_TRACE_DEBUG("bta_hh_le_srvc_expl_srvc cur_srvc_index = %d in_use = %d", + p_dev_cb->cur_srvc_index, + p_dev_cb->hid_srvc[p_dev_cb->cur_srvc_index].in_use); +#endif + + if (p_dev_cb->cur_srvc_index < BTA_HH_LE_HID_SRVC_MAX && + p_dev_cb->hid_srvc[p_dev_cb->cur_srvc_index].in_use) { + if (!p_dev_cb->hid_srvc[p_dev_cb->cur_srvc_index].expl_incl_srvc) + /* explore included service first */ + { + bta_hh_le_search_hid_included(p_dev_cb); + } else { + /* explore characterisc */ + p_dev_cb->hid_srvc[p_dev_cb->cur_srvc_index].cur_expl_char_idx = 0; + bta_hh_le_search_hid_chars(p_dev_cb); + } + } else { /* all service discvery finished */ + bta_hh_le_gatt_disc_cmpl(p_dev_cb, p_dev_cb->status); + } +} + +/******************************************************************************* +** +** Function bta_hh_le_srvc_search_cmpl +** +** Description This function process the GATT service search complete. +** +** Parameters: +** +*******************************************************************************/ +void bta_hh_le_srvc_search_cmpl(tBTA_GATTC_SEARCH_CMPL *p_data) +{ + tBTA_HH_DEV_CB *p_dev_cb = bta_hh_le_find_dev_cb_by_conn_id(p_data->conn_id); + + /* service search exception or no HID service is supported on remote */ + if (p_dev_cb == NULL) { + return; + } + + if (p_data->status != BTA_GATT_OK || p_dev_cb->total_srvc == 0) { + p_dev_cb->status = BTA_HH_ERR_SDP; + /* close the connection and report service discovery complete with error */ + bta_hh_le_api_disc_act(p_dev_cb); + } + /* GATT service discovery successfully finished */ + else { + if (p_dev_cb->disc_active & BTA_HH_LE_DISC_SCPS) { + p_dev_cb->disc_active &= ~BTA_HH_LE_DISC_SCPS; + bta_hh_le_open_cmpl(p_dev_cb); + } else { /* discover HID service */ + p_dev_cb->cur_srvc_index = 0; + bta_hh_le_srvc_expl_srvc(p_dev_cb); + } + } +} + +/******************************************************************************* +** +** Function bta_hh_le_search_hid_included +** +** Description This function search the included service within the HID service. +** +** Parameters: +** +*******************************************************************************/ +static void bta_hh_le_search_hid_included(tBTA_HH_DEV_CB *p_dev_cb) +{ + tBT_UUID srvc_cond, char_cond; + tBTA_GATTC_INCL_SVC_ID inc_srvc_result; + tBTA_GATT_SRVC_ID srvc_id; + tBTA_GATTC_CHAR_ID char_result; + tBTA_GATT_CHAR_PROP prop = 0; + + bta_hh_le_fill_16bits_srvc_id(TRUE, p_dev_cb->cur_srvc_index, UUID_SERVCLASS_LE_HID, &srvc_id); + + srvc_cond.len = LEN_UUID_16; + srvc_cond.uu.uuid16 = UUID_SERVCLASS_BATTERY; + + if (BTA_GATTC_GetFirstIncludedService(p_dev_cb->conn_id, + &srvc_id, + &srvc_cond, + &inc_srvc_result) == BTA_GATT_OK) { + /* read include service UUID */ + p_dev_cb->hid_srvc[p_dev_cb->cur_srvc_index].incl_srvc_inst = inc_srvc_result.incl_svc_id.id.inst_id; + + char_cond.len = LEN_UUID_16; + char_cond.uu.uuid16 = GATT_UUID_BATTERY_LEVEL; + + /* find the battery characteristic */ + if (BTA_GATTC_GetFirstChar( p_dev_cb->conn_id, + &inc_srvc_result.incl_svc_id, + &char_cond, + &char_result, + &prop) == BTA_GATT_OK) { + + if (bta_hh_le_find_alloc_report_entry(p_dev_cb, + char_result.srvc_id.id.inst_id, + GATT_UUID_BATTERY_LEVEL, + char_result.char_id.inst_id, + prop) == NULL) { + APPL_TRACE_ERROR("Add battery report entry failed !!!") + } + + /* read the battery characteristic */ + BTA_GATTC_ReadCharacteristic(p_dev_cb->conn_id, + &char_result, + BTA_GATT_AUTH_REQ_NONE); + + return; + + } else { + APPL_TRACE_ERROR("Remote device does not have battery level"); + } + } + + p_dev_cb->hid_srvc[p_dev_cb->cur_srvc_index].expl_incl_srvc = TRUE; + + bta_hh_le_srvc_expl_srvc(p_dev_cb); + +} + +/******************************************************************************* +** +** Function bta_hh_read_battery_level_cmpl +** +** Description This function process the battery level read +** +** Parameters: +** +*******************************************************************************/ +void bta_hh_read_battery_level_cmpl(UINT8 status, tBTA_HH_DEV_CB *p_dev_cb, tBTA_GATTC_READ *p_data) +{ + UNUSED(status); + UNUSED(p_data); + + p_dev_cb->hid_srvc[p_dev_cb->cur_srvc_index].expl_incl_srvc = TRUE; + bta_hh_le_srvc_expl_srvc(p_dev_cb); +} +/******************************************************************************* +** +** Function bta_hh_le_search_hid_chars +** +** Description This function discover all characteristics a service and +** all descriptors available. +** +** Parameters: +** +*******************************************************************************/ +static void bta_hh_le_search_hid_chars(tBTA_HH_DEV_CB *p_dev_cb) +{ + tBT_UUID char_cond; + tBTA_GATTC_CHAR_ID char_result; + tBTA_GATT_CHAR_PROP prop; + BOOLEAN next = TRUE; + UINT16 char_uuid = 0; + tBTA_GATT_SRVC_ID srvc_id; + + if (p_dev_cb->hid_srvc[p_dev_cb->cur_srvc_index].cur_expl_char_idx == BTA_HH_LE_DISC_CHAR_NUM || + (p_dev_cb->status != BTA_HH_OK && p_dev_cb->status != BTA_HH_ERR_PROTO)) { + p_dev_cb->hid_srvc[p_dev_cb->cur_srvc_index].cur_expl_char_idx = 0; + /* explore next service */ + p_dev_cb->cur_srvc_index ++; + bta_hh_le_srvc_expl_srvc(p_dev_cb); + return; + } + + p_dev_cb->hid_srvc[ p_dev_cb->cur_srvc_index].cur_expl_char_idx ++; + char_uuid = bta_hh_le_disc_char_uuid[p_dev_cb->hid_srvc[p_dev_cb->cur_srvc_index].cur_expl_char_idx - 1]; + + char_cond.len = LEN_UUID_16; + char_cond.uu.uuid16 = char_uuid; + + bta_hh_le_fill_16bits_srvc_id(TRUE, p_dev_cb->cur_srvc_index, UUID_SERVCLASS_LE_HID, &srvc_id); + +#if BTA_HH_DEBUG == TRUE + APPL_TRACE_DEBUG("bta_hh_le_search_hid_chars: looking for %s(0x%04x)", + bta_hh_uuid_to_str(char_uuid), char_uuid); +#endif + + if (BTA_GATTC_GetFirstChar( p_dev_cb->conn_id, + &srvc_id, + &char_cond, + &char_result, + &prop) == BTA_GATT_OK) { + switch (char_uuid) { + case GATT_UUID_HID_CONTROL_POINT: + p_dev_cb->hid_srvc[char_result.srvc_id.id.inst_id].option_char |= BTA_HH_LE_CP_BIT; + next = TRUE; + break; + case GATT_UUID_HID_INFORMATION: + case GATT_UUID_HID_REPORT_MAP: + /* read the char value */ + BTA_GATTC_ReadCharacteristic(p_dev_cb->conn_id, + &char_result, + BTA_GATT_AUTH_REQ_NONE); + next = FALSE; + break; + + case GATT_UUID_HID_PROTO_MODE: + p_dev_cb->hid_srvc[char_result.srvc_id.id.inst_id].option_char |= BTA_HH_LE_PROTO_MODE_BIT; + next = !bta_hh_le_set_protocol_mode(p_dev_cb, p_dev_cb->mode); + break; + + case GATT_UUID_HID_REPORT: + bta_hh_le_expl_rpt(p_dev_cb, &char_result, &char_cond, prop); + next = FALSE; + break; + + /* found boot mode report types */ + case GATT_UUID_HID_BT_KB_OUTPUT: + case GATT_UUID_HID_BT_MOUSE_INPUT: + case GATT_UUID_HID_BT_KB_INPUT: + bta_hh_le_expl_boot_rpt(p_dev_cb, char_uuid, prop); + break; + } + } else { + if (char_uuid == GATT_UUID_HID_PROTO_MODE) { + next = !bta_hh_le_set_protocol_mode(p_dev_cb, p_dev_cb->mode); + } + + } + + if (next == TRUE) { + bta_hh_le_search_hid_chars(p_dev_cb); + } +} + +/******************************************************************************* +** +** Function bta_hh_le_save_rpt_map +** +** Description save the report map into the control block. +** +** Parameters: +** +*******************************************************************************/ +void bta_hh_le_save_rpt_map(tBTA_HH_DEV_CB *p_dev_cb, tBTA_GATTC_READ *p_data) +{ + UINT8 *pp ; + tBTA_HH_LE_HID_SRVC *p_srvc = &p_dev_cb->hid_srvc[p_data->srvc_id.id.inst_id]; + + pp = p_data->p_value->unformat.p_value; + + /* save report descriptor */ + if (p_srvc->rpt_map != NULL) { + osi_free((void *)p_srvc->rpt_map); + } + + if (p_data->p_value->unformat.len > 0) { + p_srvc->rpt_map = (UINT8 *)osi_malloc(p_data->p_value->unformat.len); + } + + if (p_srvc->rpt_map != NULL) { + STREAM_TO_ARRAY(p_srvc->rpt_map, pp, p_data->p_value->unformat.len); + p_srvc->descriptor.dl_len = p_data->p_value->unformat.len; + p_srvc->descriptor.dsc_list = p_dev_cb->hid_srvc[p_data->srvc_id.id.inst_id].rpt_map; + } + + if (bta_hh_le_read_char_dscrpt(p_dev_cb, + UUID_SERVCLASS_LE_HID, + p_data->srvc_id.id.inst_id, + GATT_UUID_HID_REPORT_MAP, + p_data->char_id.inst_id, + GATT_UUID_EXT_RPT_REF_DESCR) != BTA_HH_OK) { + bta_hh_le_search_hid_chars(p_dev_cb); + } +} + +/******************************************************************************* +** +** Function bta_hh_le_proc_get_rpt_cmpl +** +** Description Process the Read report complete, send GET_REPORT_EVT to application +** with the report data. +** +** Parameters: +** +*******************************************************************************/ +void bta_hh_le_proc_get_rpt_cmpl(tBTA_HH_DEV_CB *p_dev_cb, tBTA_GATTC_READ *p_data) +{ + BT_HDR *p_buf = NULL; + tBTA_HH_LE_RPT *p_rpt; + tBTA_HH_HSDATA hs_data; + UINT8 *pp ; + + if (p_dev_cb->w4_evt != BTA_HH_GET_RPT_EVT) { + APPL_TRACE_ERROR("Unexpected READ cmpl, w4_evt = %d", p_dev_cb->w4_evt); + return; + } + + memset(&hs_data, 0, sizeof(hs_data)); + hs_data.status = BTA_HH_ERR; + hs_data.handle = p_dev_cb->hid_handle; + + if (p_data->status == BTA_GATT_OK) { + p_rpt = bta_hh_le_find_report_entry(p_dev_cb, + p_data->srvc_id.id.inst_id,//BTA_HH_LE_SRVC_DEF, + p_data->char_id.uuid.uu.uuid16, + p_data->char_id.inst_id); + + if (p_rpt != NULL && + p_data->p_value != NULL && + (p_buf = (BT_HDR *)osi_malloc((UINT16)(sizeof(BT_HDR) + p_data->p_value->unformat.len + 1))) != NULL) { + /* pack data send to app */ + hs_data.status = BTA_HH_OK; + p_buf->len = p_data->p_value->unformat.len + 1; + p_buf->layer_specific = 0; + p_buf->offset = 0; + + /* attach report ID as the first byte of the report before sending it to USB HID driver */ + pp = (UINT8 *)(p_buf + 1); + UINT8_TO_STREAM(pp, p_rpt->rpt_id); + memcpy(pp, p_data->p_value->unformat.p_value, p_data->p_value->unformat.len); + + hs_data.rsp_data.p_rpt_data = p_buf; + } + } + + p_dev_cb->w4_evt = 0; + (* bta_hh_cb.p_cback)(BTA_HH_GET_RPT_EVT, (tBTA_HH *)&hs_data); + + utl_freebuf((void **)&p_buf); +} + +/******************************************************************************* +** +** Function bta_hh_le_proc_read_proto_mode +** +** Description Process the Read protocol mode, send GET_PROTO_EVT to application +** with the protocol mode. +** +*******************************************************************************/ +void bta_hh_le_proc_read_proto_mode(tBTA_HH_DEV_CB *p_dev_cb, tBTA_GATTC_READ *p_data) +{ + tBTA_HH_HSDATA hs_data; + + hs_data.status = BTA_HH_ERR; + hs_data.handle = p_dev_cb->hid_handle; + hs_data.rsp_data.proto_mode = p_dev_cb->mode; + + if (p_data->status == BTA_GATT_OK && p_data->p_value) { + hs_data.status = BTA_HH_OK; + /* match up BTE/BTA report/boot mode def*/ + hs_data.rsp_data.proto_mode = *(p_data->p_value->unformat.p_value); + /* LE repot mode is the opposite value of BR/EDR report mode, flip it here */ + if (hs_data.rsp_data.proto_mode == 0) { + hs_data.rsp_data.proto_mode = BTA_HH_PROTO_BOOT_MODE; + } else { + hs_data.rsp_data.proto_mode = BTA_HH_PROTO_RPT_MODE; + } + + p_dev_cb->mode = hs_data.rsp_data.proto_mode; + } +#if BTA_HH_DEBUG + APPL_TRACE_DEBUG("LE GET_PROTOCOL Mode = [%s]", + (hs_data.rsp_data.proto_mode == BTA_HH_PROTO_RPT_MODE) ? "Report" : "Boot"); +#endif + + p_dev_cb->w4_evt = 0; + (* bta_hh_cb.p_cback)(BTA_HH_GET_PROTO_EVT, (tBTA_HH *)&hs_data); + +} + +/******************************************************************************* +** +** Function bta_hh_w4_le_read_char_cmpl +** +** Description process the GATT read complete in W4_CONN state. +** +** Parameters: +** +*******************************************************************************/ +void bta_hh_w4_le_read_char_cmpl(tBTA_HH_DEV_CB *p_dev_cb, tBTA_HH_DATA *p_buf) +{ + tBTA_GATTC_READ *p_data = (tBTA_GATTC_READ *)p_buf; + UINT8 *pp ; + + if (p_data->char_id.uuid.uu.uuid16 == GATT_UUID_BATTERY_LEVEL) { + bta_hh_read_battery_level_cmpl(p_data->status, p_dev_cb, p_data); + } else { + if (p_data->status == BTA_GATT_OK && p_data->p_value) { + pp = p_data->p_value->unformat.p_value; + + switch (p_data->char_id.uuid.uu.uuid16) { + /* save device information */ + case GATT_UUID_HID_INFORMATION: + STREAM_TO_UINT16(p_dev_cb->dscp_info.version, pp); + STREAM_TO_UINT8(p_dev_cb->dscp_info.ctry_code, pp); + STREAM_TO_UINT8(p_dev_cb->dscp_info.flag, pp); + break; + + case GATT_UUID_HID_REPORT_MAP: + bta_hh_le_save_rpt_map(p_dev_cb, p_data); + return; + + default: +#if BTA_HH_DEBUG == TRUE + APPL_TRACE_ERROR("Unexpected read %s(0x%04x)", + bta_hh_uuid_to_str(p_data->char_id.uuid.uu.uuid16), + p_data->char_id.uuid.uu.uuid16); +#endif + break; + } + } else { +#if BTA_HH_DEBUG == TRUE + APPL_TRACE_ERROR("read uuid %s[0x%04x] error: %d", + bta_hh_uuid_to_str(p_data->char_id.uuid.uu.uuid16), + p_data->char_id.uuid.uu.uuid16, + p_data->status); +#else + APPL_TRACE_ERROR("read uuid [0x%04x] error: %d", p_data->char_id.uuid.uu.uuid16, p_data->status); +#endif + } + bta_hh_le_search_hid_chars(p_dev_cb); + } + +} + +/******************************************************************************* +** +** Function bta_hh_le_read_char_cmpl +** +** Description a characteristic value is received. +** +** Parameters: +** +*******************************************************************************/ +void bta_hh_le_read_char_cmpl (tBTA_HH_DEV_CB *p_dev_cb, tBTA_HH_DATA *p_buf) +{ + tBTA_GATTC_READ *p_data = (tBTA_GATTC_READ *)p_buf; + + switch (p_data->char_id.uuid.uu.uuid16) { + /* GET_REPORT */ + case GATT_UUID_HID_REPORT: + case GATT_UUID_HID_BT_KB_INPUT: + case GATT_UUID_HID_BT_KB_OUTPUT: + case GATT_UUID_HID_BT_MOUSE_INPUT: + case GATT_UUID_BATTERY_LEVEL: /* read battery level */ + bta_hh_le_proc_get_rpt_cmpl(p_dev_cb, p_data); + break; + + case GATT_UUID_HID_PROTO_MODE: + bta_hh_le_proc_read_proto_mode(p_dev_cb, p_data); + break; + + default: + APPL_TRACE_ERROR("Unexpected Read UUID: 0x%04x", p_data->char_id.uuid.uu.uuid16); + break; + } + +} + +/******************************************************************************* +** +** Function bta_hh_le_read_descr_cmpl +** +** Description read characteristic descriptor is completed in CONN st. +** +** Parameters: +** +*******************************************************************************/ +void bta_hh_le_read_descr_cmpl(tBTA_HH_DEV_CB *p_dev_cb, tBTA_HH_DATA *p_buf) +{ + tBTA_HH_LE_RPT *p_rpt; + tBTA_GATTC_READ *p_data = (tBTA_GATTC_READ *)p_buf; + UINT8 *pp; + + /* if a report client configuration */ + if (p_data->descr_type.uuid.uu.uuid16 == GATT_UUID_CHAR_CLIENT_CONFIG) { + if ((p_rpt = bta_hh_le_find_report_entry(p_dev_cb, + BTA_HH_LE_SRVC_DEF, + p_data->char_id.uuid.uu.uuid16, + p_data->char_id.inst_id)) != NULL) { + pp = p_data->p_value->unformat.p_value; + STREAM_TO_UINT16(p_rpt->client_cfg_value, pp); + + APPL_TRACE_DEBUG("Read Client Configuration: 0x%04x", p_rpt->client_cfg_value); + } + } +} + +/******************************************************************************* +** +** Function bta_hh_le_read_battery_level_descr_cmpl +** +** Description Process report reference descriptor for battery level is completed +** +** Parameters: +** +*******************************************************************************/ +void bta_hh_le_read_battery_level_descr_cmpl(tBTA_HH_DEV_CB *p_dev_cb, tBTA_GATTC_READ *p_data) +{ + tBTA_HH_LE_RPT *p_rpt; + UINT16 descr_uuid = p_data->descr_type.uuid.uu.uuid16; + + /* read report reference descriptor for battery level is completed */ + if (descr_uuid == GATT_UUID_RPT_REF_DESCR) { + if ((p_rpt = bta_hh_le_find_report_entry(p_dev_cb, + p_data->srvc_id.id.inst_id, + GATT_UUID_BATTERY_LEVEL, + p_data->char_id.inst_id)) == NULL) { + bta_hh_le_search_hid_chars(p_dev_cb); + } else { + bta_hh_le_save_rpt_ref(p_dev_cb, p_rpt, p_data); + } + } +} + +/******************************************************************************* +** +** Function bta_hh_w4_le_read_descr_cmpl +** +** Description read characteristic descriptor is completed in W4_CONN st. +** +** Parameters: +** +*******************************************************************************/ +void bta_hh_w4_le_read_descr_cmpl(tBTA_HH_DEV_CB *p_dev_cb, tBTA_HH_DATA *p_buf) +{ + tBTA_HH_LE_RPT *p_rpt; + tBTA_GATTC_READ *p_data = (tBTA_GATTC_READ *)p_buf; + UINT16 char_uuid16; + + if (p_data == NULL) { + return; + } + + char_uuid16 = p_data->char_id.uuid.uu.uuid16; + +#if BTA_HH_DEBUG == TRUE + APPL_TRACE_DEBUG("bta_hh_w4_le_read_descr_cmpl uuid: %s(0x%04x)", + bta_hh_uuid_to_str(p_data->descr_type.uuid.uu.uuid16), + p_data->descr_type.uuid.uu.uuid16); +#endif + switch (char_uuid16) { + case GATT_UUID_HID_REPORT: + if ((p_rpt = bta_hh_le_find_report_entry(p_dev_cb, + p_data->srvc_id.id.inst_id, + GATT_UUID_HID_REPORT, + p_data->char_id.inst_id)) == NULL) { + bta_hh_le_search_hid_chars(p_dev_cb); + } else { + bta_hh_le_save_rpt_ref(p_dev_cb, p_rpt, p_data); + } + break; + + case GATT_UUID_HID_REPORT_MAP: + bta_hh_le_save_ext_rpt_ref(p_dev_cb, p_data); + break; + + case GATT_UUID_BATTERY_LEVEL: + bta_hh_le_read_battery_level_descr_cmpl(p_dev_cb, p_data); + break; + + default: + APPL_TRACE_ERROR("unknown descriptor read complete for uuid: 0x%04x", char_uuid16); + break; + } +} + +/******************************************************************************* +** +** Function bta_hh_w4_le_write_cmpl +** +** Description Write charactersitic complete event at W4_CONN st. +** +** Parameters: +** +*******************************************************************************/ +void bta_hh_w4_le_write_cmpl(tBTA_HH_DEV_CB *p_dev_cb, tBTA_HH_DATA *p_buf) +{ + tBTA_GATTC_WRITE *p_data = (tBTA_GATTC_WRITE *)p_buf; + + if (p_data == NULL) { + return; + } + + if (p_data->char_id.uuid.uu.uuid16 == GATT_UUID_HID_PROTO_MODE) { + p_dev_cb->status = (p_data->status == BTA_GATT_OK) ? BTA_HH_OK : BTA_HH_ERR_PROTO; + + if ((p_dev_cb->disc_active & BTA_HH_LE_DISC_HIDS) != 0) { + bta_hh_le_search_hid_chars(p_dev_cb); + } else { + bta_hh_le_open_cmpl(p_dev_cb); + } + } +} + +/******************************************************************************* +** +** Function bta_hh_le_write_cmpl +** +** Description Write charactersitic complete event at CONN st. +** +** Parameters: +** +*******************************************************************************/ +void bta_hh_le_write_cmpl(tBTA_HH_DEV_CB *p_dev_cb, tBTA_HH_DATA *p_buf) +{ + tBTA_GATTC_WRITE *p_data = (tBTA_GATTC_WRITE *)p_buf; + tBTA_HH_CBDATA cback_data ; + UINT16 cb_evt = p_dev_cb->w4_evt; + + if (p_data == NULL || cb_evt == 0) { + return; + } + +#if BTA_HH_DEBUG + APPL_TRACE_DEBUG("bta_hh_le_write_cmpl w4_evt: %d", p_dev_cb->w4_evt); +#endif + switch (p_data->char_id.uuid.uu.uuid16) { + /* Set protocol finished */ + case GATT_UUID_HID_PROTO_MODE: + cback_data.handle = p_dev_cb->hid_handle; + if (p_data->status == BTA_GATT_OK) { + bta_hh_le_register_input_notif(p_dev_cb, p_data->srvc_id.id.inst_id, p_dev_cb->mode, FALSE); + cback_data.status = BTA_HH_OK; + } else { + cback_data.status = BTA_HH_ERR; + } + p_dev_cb->w4_evt = 0; + (* bta_hh_cb.p_cback)(cb_evt, (tBTA_HH *)&cback_data); + break; + + /* Set Report finished */ + case GATT_UUID_HID_REPORT: + case GATT_UUID_HID_BT_KB_INPUT: + case GATT_UUID_HID_BT_MOUSE_INPUT: + case GATT_UUID_HID_BT_KB_OUTPUT: + cback_data.handle = p_dev_cb->hid_handle; + cback_data.status = (p_data->status == BTA_GATT_OK) ? BTA_HH_OK : BTA_HH_ERR; + p_dev_cb->w4_evt = 0; + (* bta_hh_cb.p_cback)(cb_evt, (tBTA_HH *)&cback_data); + break; + + case GATT_UUID_SCAN_INT_WINDOW: + bta_hh_le_register_scpp_notif(p_dev_cb, p_data->status); + break; + + + default: + break; + } + +} + +/******************************************************************************* +** +** Function bta_hh_le_write_char_descr_cmpl +** +** Description Write charactersitic descriptor complete event +** +** Parameters: +** +*******************************************************************************/ +void bta_hh_le_write_char_descr_cmpl(tBTA_HH_DEV_CB *p_dev_cb, tBTA_HH_DATA *p_buf) +{ + tBTA_GATTC_WRITE *p_data = (tBTA_GATTC_WRITE *)p_buf; + UINT8 srvc_inst_id, hid_inst_id; + + /* only write client configuration possible */ + if (p_data->descr_type.uuid.uu.uuid16 == GATT_UUID_CHAR_CLIENT_CONFIG) { + srvc_inst_id = p_data->srvc_id.id.inst_id; + hid_inst_id = srvc_inst_id; + switch (p_data->char_id.uuid.uu.uuid16) { + case GATT_UUID_BATTERY_LEVEL: /* battery level clt cfg registered */ + hid_inst_id = bta_hh_le_find_service_inst_by_battery_inst_id(p_dev_cb, srvc_inst_id); + /* fall through */ + case GATT_UUID_HID_BT_KB_INPUT: + case GATT_UUID_HID_BT_MOUSE_INPUT: + case GATT_UUID_HID_REPORT: + if (p_data->status == BTA_GATT_OK) { + p_dev_cb->hid_srvc[hid_inst_id].report[p_dev_cb->clt_cfg_idx].client_cfg_value = + BTA_GATT_CLT_CONFIG_NOTIFICATION; + } + p_dev_cb->clt_cfg_idx ++; + bta_hh_le_write_rpt_clt_cfg(p_dev_cb, hid_inst_id); + + break; + + case GATT_UUID_SCAN_REFRESH: + bta_hh_le_register_scpp_notif_cmpl(p_dev_cb, p_data->status); + break; + + default: + APPL_TRACE_ERROR("Unknown char ID clt cfg: 0x%04x", p_data->char_id.uuid.uu.uuid16); + } + } else { +#if BTA_HH_DEBUG == TRUE + APPL_TRACE_ERROR("Unexpected write to %s(0x%04x)", + bta_hh_uuid_to_str(p_data->descr_type.uuid.uu.uuid16), + p_data->descr_type.uuid.uu.uuid16); +#else + APPL_TRACE_ERROR("Unexpected write to (0x%04x)", + p_data->descr_type.uuid.uu.uuid16); +#endif + } + +} + +/******************************************************************************* +** +** Function bta_hh_le_input_rpt_notify +** +** Description process the notificaton event, most likely for input report. +** +** Parameters: +** +*******************************************************************************/ +void bta_hh_le_input_rpt_notify(tBTA_GATTC_NOTIFY *p_data) +{ + tBTA_HH_DEV_CB *p_dev_cb = bta_hh_le_find_dev_cb_by_conn_id(p_data->conn_id); + UINT8 app_id; + UINT8 *p_buf; + tBTA_HH_LE_RPT *p_rpt; + + if (p_dev_cb == NULL) { + APPL_TRACE_ERROR("notification received from Unknown device"); + return; + } + app_id = p_dev_cb->app_id; + + p_rpt = bta_hh_le_find_report_entry(p_dev_cb, + BTA_HH_LE_SRVC_DEF, + p_data->char_id.char_id.uuid.uu.uuid16, + p_data->char_id.char_id.inst_id); + if (p_rpt == NULL) { + APPL_TRACE_ERROR("notification received for Unknown Report"); + return; + } + + if (p_data->char_id.char_id.uuid.uu.uuid16 == GATT_UUID_HID_BT_MOUSE_INPUT) { + app_id = BTA_HH_APP_ID_MI; + } else if (p_data->char_id.char_id.uuid.uu.uuid16 == GATT_UUID_HID_BT_KB_INPUT) { + app_id = BTA_HH_APP_ID_KB; + } + + APPL_TRACE_DEBUG("Notification received on report ID: %d", p_rpt->rpt_id); + + /* need to append report ID to the head of data */ + if (p_rpt->rpt_id != 0) { + if ((p_buf = (UINT8 *)osi_malloc((UINT16)(p_data->len + 1))) == NULL) { + APPL_TRACE_ERROR("No resources to send report data"); + return; + } + + p_buf[0] = p_rpt->rpt_id; + memcpy(&p_buf[1], p_data->value, p_data->len); + ++p_data->len; + } else { + p_buf = p_data->value; + } + + bta_hh_co_data((UINT8)p_dev_cb->hid_handle, + p_buf, + p_data->len, + p_dev_cb->mode, + 0 , /* no sub class*/ + p_dev_cb->dscp_info.ctry_code, + p_dev_cb->addr, + app_id); + + if (p_buf != p_data->value) { + osi_free(p_buf); + } +} + +/******************************************************************************* +** +** Function bta_hh_gatt_open_fail +** +** Description action function to process the open fail +** +** Returns void +** +*******************************************************************************/ +void bta_hh_le_open_fail(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data) +{ + tBTA_HH_CONN conn_dat ; + + /* open failure in the middle of service discovery, clear all services */ + if (p_cb->disc_active & BTA_HH_LE_DISC_HIDS) { + bta_hh_clear_service_cache(p_cb); + } + + p_cb->disc_active = BTA_HH_LE_DISC_NONE; + /* Failure in opening connection or GATT discovery failure */ + conn_dat.handle = p_cb->hid_handle; + memcpy(conn_dat.bda, p_cb->addr, BD_ADDR_LEN); + conn_dat.le_hid = TRUE; + conn_dat.scps_supported = p_cb->scps_supported; + + if (p_cb->status == BTA_HH_OK) { + conn_dat.status = (p_data->le_close.reason == BTA_GATT_CONN_UNKNOWN) ? p_cb->status : BTA_HH_ERR; + } else { + conn_dat.status = p_cb->status; + } + + /* Report OPEN fail event */ + (*bta_hh_cb.p_cback)(BTA_HH_OPEN_EVT, (tBTA_HH *)&conn_dat); + +} + +/******************************************************************************* +** +** Function bta_hh_gatt_close +** +** Description action function to process the GATT close int he state machine. +** +** Returns void +** +*******************************************************************************/ +void bta_hh_gatt_close(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data) +{ + tBTA_HH_CBDATA disc_dat = {BTA_HH_OK, 0}; + + /* finaliza device driver */ + bta_hh_co_close(p_cb->hid_handle, p_cb->app_id); + /* update total conn number */ + bta_hh_cb.cnt_num --; + + disc_dat.handle = p_cb->hid_handle; + disc_dat.status = p_cb->status; + + (*bta_hh_cb.p_cback)(BTA_HH_CLOSE_EVT, (tBTA_HH *)&disc_dat); + + /* if no connection is active and HH disable is signaled, disable service */ + if (bta_hh_cb.cnt_num == 0 && bta_hh_cb.w4_disable) { + bta_hh_disc_cmpl(); + } else { +#if (BTA_HH_LE_RECONN == TRUE) + if (p_data->le_close.reason == BTA_GATT_CONN_TIMEOUT) { + bta_hh_le_add_dev_bg_conn(p_cb, FALSE); + } +#endif + } + + return; + +} + +/******************************************************************************* +** +** Function bta_hh_le_api_disc_act +** +** Description initaite a Close API to a remote HID device +** +** Returns void +** +*******************************************************************************/ +void bta_hh_le_api_disc_act(tBTA_HH_DEV_CB *p_cb) +{ + if (p_cb->conn_id != BTA_GATT_INVALID_CONN_ID) { + BTA_GATTC_Close(p_cb->conn_id); + /* remove device from background connection if intended to disconnect, + do not allow reconnection */ + bta_hh_le_remove_dev_bg_conn(p_cb); + } +} + +/******************************************************************************* +** +** Function bta_hh_le_get_rpt +** +** Description GET_REPORT on a LE HID Report +** +** Returns void +** +*******************************************************************************/ +void bta_hh_le_get_rpt(tBTA_HH_DEV_CB *p_cb, UINT8 srvc_inst, tBTA_HH_RPT_TYPE r_type, UINT8 rpt_id) +{ + tBTA_HH_LE_RPT *p_rpt = bta_hh_le_find_rpt_by_idtype(p_cb->hid_srvc[srvc_inst].report, p_cb->mode, r_type, rpt_id); + tBTA_GATTC_CHAR_ID char_id; + UINT16 srvc_uuid = UUID_SERVCLASS_LE_HID; + + if (p_rpt == NULL) { + APPL_TRACE_ERROR("bta_hh_le_get_rpt: no matching report"); + return; + } + if (p_rpt->uuid == GATT_UUID_BATTERY_LEVEL) { + srvc_uuid = UUID_SERVCLASS_BATTERY; + } + + p_cb->w4_evt = BTA_HH_GET_RPT_EVT; + + bta_hh_le_fill_16bits_srvc_id(TRUE, srvc_inst, srvc_uuid, &char_id.srvc_id); + bta_hh_le_fill_16bits_char_id(p_rpt->inst_id, p_rpt->uuid, &char_id.char_id); + + BTA_GATTC_ReadCharacteristic(p_cb->conn_id, + &char_id, + BTA_GATT_AUTH_REQ_NONE); +} + +/******************************************************************************* +** +** Function bta_hh_le_write_rpt +** +** Description SET_REPORT/or DATA output on a LE HID Report +** +** Returns void +** +*******************************************************************************/ +void bta_hh_le_write_rpt(tBTA_HH_DEV_CB *p_cb, UINT8 srvc_inst, + tBTA_GATTC_WRITE_TYPE write_type, + tBTA_HH_RPT_TYPE r_type, + BT_HDR *p_buf, UINT16 w4_evt ) +{ + tBTA_HH_LE_RPT *p_rpt; + tBTA_GATTC_CHAR_ID char_id; + UINT8 *p_value, rpt_id; + + if (p_buf == NULL || p_buf->len == 0) { + APPL_TRACE_ERROR("bta_hh_le_write_rpt: Illegal data"); + return; + } + + /* strip report ID from the data */ + p_value = (UINT8 *)(p_buf + 1) + p_buf->offset; + STREAM_TO_UINT8(rpt_id, p_value); + p_buf->len -= 1; + + p_rpt = bta_hh_le_find_rpt_by_idtype(p_cb->hid_srvc[srvc_inst].report, p_cb->mode, r_type, rpt_id); + + if (p_rpt == NULL) { + APPL_TRACE_ERROR("bta_hh_le_write_rpt: no matching report"); + osi_free(p_buf); + return; + } + + APPL_TRACE_ERROR("bta_hh_le_write_rpt: ReportID: 0x%02x Data Len: %d", rpt_id, p_buf->len); + + p_cb->w4_evt = w4_evt; + + bta_hh_le_fill_16bits_srvc_id(TRUE, srvc_inst, UUID_SERVCLASS_LE_HID, &char_id.srvc_id); + bta_hh_le_fill_16bits_char_id(p_rpt->inst_id, p_rpt->uuid, &char_id.char_id); + + BTA_GATTC_WriteCharValue(p_cb->conn_id, + &char_id, + write_type, /* default to use write request */ + p_buf->len, + p_value, + BTA_GATT_AUTH_REQ_NONE); + +} + +/******************************************************************************* +** +** Function bta_hh_le_suspend +** +** Description send LE suspend or exit suspend mode to remote device. +** +** Returns void +** +*******************************************************************************/ +void bta_hh_le_suspend(tBTA_HH_DEV_CB *p_cb, tBTA_HH_TRANS_CTRL_TYPE ctrl_type) +{ + UINT8 i; + tBTA_GATTC_CHAR_ID char_id; + + ctrl_type -= BTA_HH_CTRL_SUSPEND; + + for (i = 0; i < BTA_HH_LE_HID_SRVC_MAX; i ++) { + bta_hh_le_fill_16bits_srvc_id(TRUE, i, UUID_SERVCLASS_LE_HID, &char_id.srvc_id); + bta_hh_le_fill_16bits_char_id(0, GATT_UUID_HID_CONTROL_POINT, &char_id.char_id); + + BTA_GATTC_WriteCharValue(p_cb->conn_id, + &char_id, + BTA_GATTC_TYPE_WRITE_NO_RSP, /* default to use write request */ + 1, + &ctrl_type, + BTA_GATT_AUTH_REQ_NONE); + } +} + +/******************************************************************************* +** +** Function bta_hh_le_write_dev_act +** +** Description Write LE device action. can be SET/GET/DATA transaction. +** +** Returns void +** +*******************************************************************************/ +void bta_hh_le_write_dev_act(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data) +{ + switch (p_data->api_sndcmd.t_type) { + case HID_TRANS_SET_PROTOCOL: + p_cb->w4_evt = BTA_HH_SET_PROTO_EVT; + bta_hh_le_set_protocol_mode(p_cb, p_data->api_sndcmd.param); + break; + + case HID_TRANS_GET_PROTOCOL: + bta_hh_le_get_protocol_mode(p_cb); + break; + + case HID_TRANS_GET_REPORT: + bta_hh_le_get_rpt(p_cb, + BTA_HH_LE_SRVC_DEF, + p_data->api_sndcmd.param, + p_data->api_sndcmd.rpt_id); + break; + + case HID_TRANS_SET_REPORT: + bta_hh_le_write_rpt(p_cb, + BTA_HH_LE_SRVC_DEF, + BTA_GATTC_TYPE_WRITE, + p_data->api_sndcmd.param, + p_data->api_sndcmd.p_data, + BTA_HH_SET_RPT_EVT); + break; + + case HID_TRANS_DATA: /* output report */ + + bta_hh_le_write_rpt(p_cb, + BTA_HH_LE_SRVC_DEF, + BTA_GATTC_TYPE_WRITE_NO_RSP, + p_data->api_sndcmd.param, + p_data->api_sndcmd.p_data, + BTA_HH_DATA_EVT); + break; + + case HID_TRANS_CONTROL: + /* no handshake event will be generated */ + /* if VC_UNPLUG is issued, set flag */ + if (p_data->api_sndcmd.param == BTA_HH_CTRL_SUSPEND || + p_data->api_sndcmd.param == BTA_HH_CTRL_EXIT_SUSPEND) { + bta_hh_le_suspend(p_cb, p_data->api_sndcmd.param); + } + break; + + default: + APPL_TRACE_ERROR("%s unsupported transaction for BLE HID device: %d", + __func__, p_data->api_sndcmd.t_type); + break; + } +} + +/******************************************************************************* +** +** Function bta_hh_le_get_dscp_act +** +** Description Send ReportDescriptor to application for all HID services. +** +** Returns void +** +*******************************************************************************/ +void bta_hh_le_get_dscp_act(tBTA_HH_DEV_CB *p_cb) +{ + UINT8 i; + + for (i = 0 ; i < BTA_HH_LE_HID_SRVC_MAX; i ++) { + if (p_cb->hid_srvc[i].in_use) { + p_cb->dscp_info.descriptor.dl_len = p_cb->hid_srvc[i].descriptor.dl_len; + p_cb->dscp_info.descriptor.dsc_list = p_cb->hid_srvc[i].descriptor.dsc_list; + + (*bta_hh_cb.p_cback)(BTA_HH_GET_DSCP_EVT, (tBTA_HH *)&p_cb->dscp_info); + } else { + break; + } + } +} + +/******************************************************************************* +** +** Function bta_hh_le_add_dev_bg_conn +** +** Description Remove a LE HID device from background connection procedure. +** +** Returns void +** +*******************************************************************************/ +static void bta_hh_le_add_dev_bg_conn(tBTA_HH_DEV_CB *p_cb, BOOLEAN check_bond) +{ + UINT8 sec_flag = 0; + BOOLEAN to_add = TRUE; + + if (check_bond) { + /* start reconnection if remote is a bonded device */ + /* verify bond */ + BTM_GetSecurityFlagsByTransport(p_cb->addr, &sec_flag, BT_TRANSPORT_LE); + + if ((sec_flag & BTM_SEC_FLAG_LKEY_KNOWN) == 0) { + to_add = FALSE; + } + } + + if (/*p_cb->dscp_info.flag & BTA_HH_LE_NORMAL_CONN &&*/ + !p_cb->in_bg_conn && to_add) { + /* add device into BG connection to accept remote initiated connection */ + BTA_GATTC_Open(bta_hh_cb.gatt_if, p_cb->addr, BLE_ADDR_UNKNOWN_TYPE, FALSE, BTA_GATT_TRANSPORT_LE, FALSE); + p_cb->in_bg_conn = TRUE; + + BTA_DmBleSetBgConnType(BTA_DM_BLE_CONN_AUTO, NULL); + } + return; +} + +/******************************************************************************* +** +** Function bta_hh_le_add_device +** +** Description Add a LE HID device as a known device, and also add the address +** into background connection WL for incoming connection. +** +** Returns void +** +*******************************************************************************/ +UINT8 bta_hh_le_add_device(tBTA_HH_DEV_CB *p_cb, tBTA_HH_MAINT_DEV *p_dev_info) +{ + p_cb->hid_handle = BTA_HH_GET_LE_DEV_HDL(p_cb->index); + bta_hh_cb.le_cb_index[BTA_HH_GET_LE_CB_IDX(p_cb->hid_handle)] = p_cb->index; + + /* update DI information */ + bta_hh_update_di_info(p_cb, + p_dev_info->dscp_info.vendor_id, + p_dev_info->dscp_info.product_id, + p_dev_info->dscp_info.version, + p_dev_info->dscp_info.flag); + + /* add to BTA device list */ + bta_hh_add_device_to_list(p_cb, p_cb->hid_handle, + p_dev_info->attr_mask, + &p_dev_info->dscp_info.descriptor, + p_dev_info->sub_class, + p_dev_info->dscp_info.ssr_max_latency, + p_dev_info->dscp_info.ssr_min_tout, + p_dev_info->app_id); + + bta_hh_le_add_dev_bg_conn(p_cb, FALSE); + + return p_cb->hid_handle; +} + +/******************************************************************************* +** +** Function bta_hh_le_remove_dev_bg_conn +** +** Description Remove a LE HID device from background connection procedure. +** +** Returns void +** +*******************************************************************************/ +void bta_hh_le_remove_dev_bg_conn(tBTA_HH_DEV_CB *p_dev_cb) +{ + if (p_dev_cb->in_bg_conn) { + p_dev_cb->in_bg_conn = FALSE; + + BTA_GATTC_CancelOpen(bta_hh_cb.gatt_if, p_dev_cb->addr, FALSE); + } +} + +/******************************************************************************* +** +** Function bta_hh_le_update_scpp +** +** Description action function to update the scan parameters on remote HID +** device +** +** Parameters: +** +*******************************************************************************/ +void bta_hh_le_update_scpp(tBTA_HH_DEV_CB *p_dev_cb, tBTA_HH_DATA *p_buf) +{ + tBTA_GATTC_CHAR_ID char_id; + UINT8 value[4], *p = value; + tBTA_HH_CBDATA cback_data ; + + if (!p_dev_cb->is_le_device || + p_dev_cb->mode != BTA_HH_PROTO_RPT_MODE || + p_dev_cb->scps_supported == FALSE) { + APPL_TRACE_ERROR("Can not set ScPP scan paramter as boot host, or remote does not support ScPP "); + + cback_data.handle = p_dev_cb->hid_handle; + cback_data.status = BTA_HH_ERR; + (* bta_hh_cb.p_cback)(BTA_HH_UPDATE_SCPP_EVT, (tBTA_HH *)&cback_data); + + return; + } + + p_dev_cb->w4_evt = BTA_HH_UPDATE_SCPP_EVT; + + UINT16_TO_STREAM(p, p_buf->le_scpp_update.scan_int); + UINT16_TO_STREAM(p, p_buf->le_scpp_update.scan_win); + + bta_hh_le_fill_16bits_srvc_id(TRUE, BTA_HH_SCPP_INST_DEF, UUID_SERVCLASS_SCAN_PARAM, &char_id.srvc_id); + bta_hh_le_fill_16bits_char_id(BTA_HH_SCPP_INST_DEF, GATT_UUID_SCAN_INT_WINDOW, &char_id.char_id); + + BTA_GATTC_WriteCharValue(p_dev_cb->conn_id, + &char_id, + BTA_GATTC_TYPE_WRITE_NO_RSP, + 2, + value, + BTA_GATT_AUTH_REQ_NONE); + +} + +/******************************************************************************* +** +** Function bta_hh_gattc_callback +** +** Description This is GATT client callback function used in BTA HH. +** +** Parameters: +** +*******************************************************************************/ +static void bta_hh_gattc_callback(tBTA_GATTC_EVT event, tBTA_GATTC *p_data) +{ + tBTA_HH_DEV_CB *p_dev_cb; + UINT16 evt; +#if BTA_HH_DEBUG + APPL_TRACE_DEBUG("bta_hh_gattc_callback event = %d", event); +#endif + if (p_data == NULL) { + return; + } + + switch (event) { + case BTA_GATTC_REG_EVT: /* 0 */ + bta_hh_le_register_cmpl(&p_data->reg_oper); + break; + + case BTA_GATTC_DEREG_EVT: /* 1 */ + bta_hh_cleanup_disable(p_data->reg_oper.status); + break; + + case BTA_GATTC_OPEN_EVT: /* 2 */ + p_dev_cb = bta_hh_le_find_dev_cb_by_bda(p_data->open.remote_bda); + if (p_dev_cb) { + bta_hh_sm_execute(p_dev_cb, BTA_HH_GATT_OPEN_EVT, (tBTA_HH_DATA *)&p_data->open); + } + break; + + case BTA_GATTC_READ_CHAR_EVT: /* 3 */ + case BTA_GATTC_READ_DESCR_EVT: /* 8 */ + p_dev_cb = bta_hh_le_find_dev_cb_by_conn_id(p_data->read.conn_id); + if (event == BTA_GATTC_READ_CHAR_EVT) { + evt = BTA_HH_GATT_READ_CHAR_CMPL_EVT; + } else { + evt = BTA_HH_GATT_READ_DESCR_CMPL_EVT; + } + + bta_hh_sm_execute(p_dev_cb, evt, (tBTA_HH_DATA *)&p_data->read); + break; + + case BTA_GATTC_WRITE_DESCR_EVT: /* 9 */ + case BTA_GATTC_WRITE_CHAR_EVT: /* 4 */ + p_dev_cb = bta_hh_le_find_dev_cb_by_conn_id(p_data->write.conn_id); + if (event == BTA_GATTC_WRITE_CHAR_EVT) { + evt = BTA_HH_GATT_WRITE_CHAR_CMPL_EVT; + } else { + evt = BTA_HH_GATT_WRITE_DESCR_CMPL_EVT; + } + + bta_hh_sm_execute(p_dev_cb, evt, (tBTA_HH_DATA *)&p_data->write); + break; + + case BTA_GATTC_CLOSE_EVT: /* 5 */ + bta_hh_le_close(&p_data->close); + break; + + case BTA_GATTC_SEARCH_CMPL_EVT: /* 6 */ + bta_hh_le_srvc_search_cmpl(&p_data->search_cmpl); + break; + + case BTA_GATTC_SEARCH_RES_EVT: /* 7 */ + bta_hh_le_search_result(&p_data->srvc_res); + break; + + + + case BTA_GATTC_NOTIF_EVT: /* 10 */ + bta_hh_le_input_rpt_notify(&p_data->notify); + break; + + case BTA_GATTC_ENC_CMPL_CB_EVT: /* 17 */ + p_dev_cb = bta_hh_le_find_dev_cb_by_bda(p_data->enc_cmpl.remote_bda); + if (p_dev_cb) { + bta_hh_sm_execute(p_dev_cb, BTA_HH_GATT_ENC_CMPL_EVT, + (tBTA_HH_DATA *)&p_data->enc_cmpl); + } + break; + + default: + break; + } +} + +/******************************************************************************* +** +** Function bta_hh_le_hid_read_rpt_clt_cfg +** +** Description a test command to read report descriptor client configuration +** +** Returns void +** +*******************************************************************************/ +void bta_hh_le_hid_read_rpt_clt_cfg(BD_ADDR bd_addr, UINT8 rpt_id) +{ + tBTA_HH_DEV_CB *p_cb = NULL; + tBTA_HH_LE_RPT *p_rpt ; + UINT8 index = BTA_HH_IDX_INVALID; + + index = bta_hh_find_cb(bd_addr); + if ((index = bta_hh_find_cb(bd_addr)) == BTA_HH_IDX_INVALID) { + APPL_TRACE_ERROR("unknown device"); + return; + } + + p_cb = &bta_hh_cb.kdev[index]; + + p_rpt = bta_hh_le_find_rpt_by_idtype(p_cb->hid_srvc[BTA_HH_LE_SRVC_DEF].report, p_cb->mode, BTA_HH_RPTT_INPUT, rpt_id); + + if (p_rpt == NULL) { + APPL_TRACE_ERROR("bta_hh_le_write_rpt: no matching report"); + return; + } + + bta_hh_le_read_char_dscrpt(p_cb, + UUID_SERVCLASS_LE_HID, + BTA_HH_LE_SRVC_DEF, + p_rpt->uuid, + p_rpt->inst_id, + GATT_UUID_CHAR_CLIENT_CONFIG); + + + + return; +} + +/******************************************************************************* +** +** Function bta_hh_le_search_scps +** +** Description discovery scan parameter service if act as report host, otherwise +** finish LE connection. +** +** Parameters: +** +*******************************************************************************/ +static void bta_hh_le_search_scps(tBTA_HH_DEV_CB *p_cb) +{ + tBT_UUID pri_srvc; + + if ( p_cb->mode == BTA_HH_PROTO_RPT_MODE) { + p_cb->disc_active |= BTA_HH_LE_DISC_SCPS; + /* start service discovery for Scan Parameter service */ + pri_srvc.len = LEN_UUID_16; + pri_srvc.uu.uuid16 = UUID_SERVCLASS_SCAN_PARAM; + + BTA_GATTC_ServiceSearchRequest(p_cb->conn_id, &pri_srvc); + } else { + bta_hh_le_open_cmpl(p_cb); + } +} + +/******************************************************************************* +** +** Function bta_hh_le_search_scps_chars +** +** Description find ScPS optional characteristics scan refresh +** +** Parameters: +** +*******************************************************************************/ +static void bta_hh_le_search_scps_chars(tBTA_HH_DEV_CB *p_cb) +{ + tBTA_GATT_SRVC_ID srvc_id; + tBT_UUID char_cond; + tBTA_GATTC_CHAR_ID char_result; + tBTA_GATT_CHAR_PROP prop; + + p_cb->scps_supported = TRUE; + bta_hh_le_fill_16bits_srvc_id(TRUE, 0, UUID_SERVCLASS_SCAN_PARAM, &srvc_id); + + char_cond.len = LEN_UUID_16; + char_cond.uu.uuid16 = GATT_UUID_SCAN_REFRESH; + + /* look for scan refresh */ + if (BTA_GATTC_GetFirstChar( p_cb->conn_id, + &srvc_id, + &char_cond, + &char_result, + &prop) == BTA_GATT_OK) { + if (prop & BTA_GATT_CHAR_PROP_BIT_NOTIFY) { + p_cb->scps_notify |= BTA_HH_LE_SCPS_NOTIFY_SPT; + } else { + p_cb->scps_notify = BTA_HH_LE_SCPS_NOTIFY_NONE; + } + + } +} + +/******************************************************************************* +** +** Function bta_hh_le_register_scpp_notif +** +** Description register scan parameter refresh notitication complete +** +** +** Parameters: +** +*******************************************************************************/ +static void bta_hh_le_register_scpp_notif(tBTA_HH_DEV_CB *p_dev_cb, tBTA_GATT_STATUS status) +{ + UINT8 sec_flag = 0; + tBTA_GATTC_CHAR_ID char_id; + + /* if write scan parameter sucessful */ + /* if bonded and notification is not enabled, configure the client configuration */ + if (status == BTA_GATT_OK && + (p_dev_cb->scps_notify & BTA_HH_LE_SCPS_NOTIFY_SPT) != 0 && + (p_dev_cb->scps_notify & BTA_HH_LE_SCPS_NOTIFY_ENB) == 0) { + BTM_GetSecurityFlagsByTransport(p_dev_cb->addr, &sec_flag, BT_TRANSPORT_LE); + if ((sec_flag & BTM_SEC_FLAG_LKEY_KNOWN)) { + if (bta_hh_le_write_char_clt_cfg (p_dev_cb, + BTA_HH_SCPP_INST_DEF, + UUID_SERVCLASS_SCAN_PARAM, + BTA_HH_SCPP_INST_DEF, + GATT_UUID_SCAN_REFRESH, + BTA_GATT_CLT_CONFIG_NOTIFICATION)) { + bta_hh_le_fill_16bits_srvc_id(TRUE, BTA_HH_SCPP_INST_DEF, UUID_SERVCLASS_SCAN_PARAM, &char_id.srvc_id); + bta_hh_le_fill_16bits_char_id(BTA_HH_SCPP_INST_DEF, GATT_UUID_SCAN_REFRESH, &char_id.char_id); + + BTA_GATTC_RegisterForNotifications(bta_hh_cb.gatt_if, + p_dev_cb->addr, + &char_id); + return; + } + } + } + bta_hh_le_register_scpp_notif_cmpl(p_dev_cb, status); +} + +/******************************************************************************* +** +** Function bta_hh_le_register_scpp_notif_cmpl +** +** Description action function to register scan parameter refresh notitication +** +** Parameters: +** +*******************************************************************************/ +static void bta_hh_le_register_scpp_notif_cmpl(tBTA_HH_DEV_CB *p_dev_cb, tBTA_GATT_STATUS status) +{ + tBTA_HH_CBDATA cback_data ; + UINT16 cb_evt = p_dev_cb->w4_evt; + + if (status == BTA_GATT_OK) { + p_dev_cb->scps_notify = (BTA_HH_LE_SCPS_NOTIFY_ENB | BTA_HH_LE_SCPS_NOTIFY_SPT); + } + + cback_data.handle = p_dev_cb->hid_handle; + cback_data.status = (status == BTA_GATT_OK) ? BTA_HH_OK : BTA_HH_ERR; + p_dev_cb->w4_evt = 0; + (* bta_hh_cb.p_cback)(cb_evt, (tBTA_HH *)&cback_data); + + +} + +/******************************************************************************* +** +** Function bta_hh_process_cache_rpt +** +** Description Process the cached reports +** +** Parameters: +** +*******************************************************************************/ +static void bta_hh_process_cache_rpt (tBTA_HH_DEV_CB *p_cb, + tBTA_HH_RPT_CACHE_ENTRY *p_rpt_cache, + UINT8 num_rpt) +{ + UINT8 i = 0; + tBTA_HH_LE_RPT *p_rpt; + + if (num_rpt != 0) { /* no cache is found */ + p_cb->hid_srvc[BTA_HH_LE_RPT_GET_SRVC_INST_ID(p_rpt_cache->inst_id)].in_use = TRUE; + + /* set the descriptor info */ + p_cb->hid_srvc[BTA_HH_LE_RPT_GET_SRVC_INST_ID(p_rpt_cache->inst_id)].descriptor.dl_len = + p_cb->dscp_info.descriptor.dl_len; + p_cb->hid_srvc[BTA_HH_LE_RPT_GET_SRVC_INST_ID(p_rpt_cache->inst_id)].descriptor.dsc_list = + p_cb->dscp_info.descriptor.dsc_list; + + for (; i < num_rpt; i ++, p_rpt_cache ++) { + if ((p_rpt = bta_hh_le_find_alloc_report_entry (p_cb, + BTA_HH_LE_RPT_GET_SRVC_INST_ID(p_rpt_cache->inst_id), + p_rpt_cache->rpt_uuid, + BTA_HH_LE_RPT_GET_RPT_INST_ID(p_rpt_cache->inst_id), + p_rpt_cache->prop)) == NULL) { + APPL_TRACE_ERROR("bta_hh_process_cache_rpt: allocation report entry failure"); + break; + } else { + p_rpt->rpt_type = p_rpt_cache->rpt_type; + p_rpt->rpt_id = p_rpt_cache->rpt_id; + + if (p_rpt->uuid == GATT_UUID_HID_BT_KB_INPUT || + p_rpt->uuid == GATT_UUID_HID_BT_MOUSE_INPUT || + (p_rpt->uuid == GATT_UUID_HID_REPORT && p_rpt->rpt_type == BTA_HH_RPTT_INPUT)) { + p_rpt->client_cfg_value = BTA_GATT_CLT_CONFIG_NOTIFICATION; + } + } + } + } +} + +#endif diff --git a/lib/bt/host/bluedroid/bta/hh/bta_hh_main.c b/lib/bt/host/bluedroid/bta/hh/bta_hh_main.c new file mode 100644 index 00000000..663d28e9 --- /dev/null +++ b/lib/bt/host/bluedroid/bta/hh/bta_hh_main.c @@ -0,0 +1,568 @@ +/****************************************************************************** + * + * Copyright (C) 2005-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains the HID host main functions and state machine. + * + ******************************************************************************/ + +#include "common/bt_target.h" + +#if defined(BTA_HH_INCLUDED) && (BTA_HH_INCLUDED == TRUE) + +#include + +#include "bta/bta_hh_api.h" +#include "bta_hh_int.h" +#include "osi/allocator.h" + +/***************************************************************************** +** Constants and types +*****************************************************************************/ + +/* state machine action enumeration list */ +enum { + BTA_HH_API_DISC_ACT, /* HID host process API close action */ + BTA_HH_OPEN_ACT, /* HID host process BTA_HH_EVT_OPEN */ + BTA_HH_CLOSE_ACT, /* HID host process BTA_HH_EVT_CLOSE */ + BTA_HH_DATA_ACT, /* HID host receive data report */ + BTA_HH_CTRL_DAT_ACT, + BTA_HH_HANDSK_ACT, + BTA_HH_START_SDP, /* HID host inquery */ + BTA_HH_SDP_CMPL, + BTA_HH_WRITE_DEV_ACT, + BTA_HH_GET_DSCP_ACT, + BTA_HH_MAINT_DEV_ACT, + BTA_HH_OPEN_CMPL_ACT, + BTA_HH_OPEN_FAILURE, +#if (defined BTA_HH_LE_INCLUDED && BTA_HH_LE_INCLUDED == TRUE) + BTA_HH_GATT_CLOSE, + BTA_HH_LE_OPEN_FAIL, + BTA_HH_GATT_OPEN, + BTA_HH_W4_LE_READ_CHAR, + BTA_HH_LE_READ_CHAR, + BTA_HH_W4_LE_READ_DESCR, + BTA_HH_LE_READ_DESCR, + BTA_HH_W4_LE_WRITE, + BTA_HH_LE_WRITE, + BTA_HH_WRITE_DESCR, + BTA_HH_START_SEC, + BTA_HH_SEC_CMPL, + BTA_HH_LE_UPDATE_SCPP, + BTA_HH_GATT_ENC_CMPL, +#endif + BTA_HH_NUM_ACTIONS +}; + +#define BTA_HH_IGNORE BTA_HH_NUM_ACTIONS + +/* type for action functions */ +typedef void (*tBTA_HH_ACTION)(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data); + +/* action functions */ +const tBTA_HH_ACTION bta_hh_action[] = { + bta_hh_api_disc_act, + bta_hh_open_act, + bta_hh_close_act, + bta_hh_data_act, + bta_hh_ctrl_dat_act, + bta_hh_handsk_act, + bta_hh_start_sdp, + bta_hh_sdp_cmpl, + bta_hh_write_dev_act, + bta_hh_get_dscp_act, + bta_hh_maint_dev_act, + bta_hh_open_cmpl_act, + bta_hh_open_failure +#if (defined BTA_HH_LE_INCLUDED && BTA_HH_LE_INCLUDED == TRUE) + , bta_hh_gatt_close + , bta_hh_le_open_fail + , bta_hh_gatt_open + , bta_hh_w4_le_read_char_cmpl + , bta_hh_le_read_char_cmpl + , bta_hh_w4_le_read_descr_cmpl + , bta_hh_le_read_descr_cmpl + , bta_hh_w4_le_write_cmpl + , bta_hh_le_write_cmpl + , bta_hh_le_write_char_descr_cmpl + , bta_hh_start_security + , bta_hh_security_cmpl + , bta_hh_le_update_scpp + , bta_hh_le_notify_enc_cmpl +#endif +}; + +/* state table information */ +#define BTA_HH_ACTION 0 /* position of action */ +#define BTA_HH_NEXT_STATE 1 /* position of next state */ +#define BTA_HH_NUM_COLS 2 /* number of columns */ + +/* state table for idle state */ +const UINT8 bta_hh_st_idle[][BTA_HH_NUM_COLS] = { + /* Event Action Next state */ + /* BTA_HH_API_OPEN_EVT */ {BTA_HH_START_SDP, BTA_HH_W4_CONN_ST }, + /* BTA_HH_API_CLOSE_EVT */ {BTA_HH_IGNORE, BTA_HH_IDLE_ST }, + /* BTA_HH_INT_OPEN_EVT */ {BTA_HH_OPEN_ACT, BTA_HH_W4_CONN_ST }, + /* BTA_HH_INT_CLOSE_EVT */ {BTA_HH_CLOSE_ACT, BTA_HH_IDLE_ST }, + /* BTA_HH_INT_DATA_EVT */ {BTA_HH_IGNORE, BTA_HH_IDLE_ST }, + /* BTA_HH_INT_CTRL_DATA */ {BTA_HH_IGNORE, BTA_HH_IDLE_ST }, + /* BTA_HH_INT_HANDSK_EVT */ {BTA_HH_IGNORE, BTA_HH_IDLE_ST }, + /* BTA_HH_SDP_CMPL_EVT */ {BTA_HH_IGNORE, BTA_HH_IDLE_ST }, + /* BTA_HH_API_WRITE_DEV_EVT */ {BTA_HH_IGNORE, BTA_HH_IDLE_ST }, + /* BTA_HH_API_GET_DSCP_EVT */ {BTA_HH_IGNORE, BTA_HH_IDLE_ST }, + /* BTA_HH_API_MAINT_DEV_EVT */ {BTA_HH_MAINT_DEV_ACT, BTA_HH_IDLE_ST }, + /* BTA_HH_OPEN_CMPL_EVT */ {BTA_HH_OPEN_CMPL_ACT, BTA_HH_CONN_ST } +#if (defined BTA_HH_LE_INCLUDED && BTA_HH_LE_INCLUDED == TRUE) + /* BTA_HH_GATT_CLOSE_EVT */ , {BTA_HH_IGNORE, BTA_HH_IDLE_ST } + /* BTA_HH_GATT_OPEN_EVT */ , {BTA_HH_GATT_OPEN, BTA_HH_W4_CONN_ST } + /* BTA_HH_START_ENC_EVT */ , {BTA_HH_IGNORE, BTA_HH_IDLE_ST } + /* BTA_HH_ENC_CMPL_EVT */ , {BTA_HH_IGNORE, BTA_HH_IDLE_ST } + /* READ_CHAR_CMPL_EVT */ , {BTA_HH_IGNORE, BTA_HH_IDLE_ST } + /* BTA_HH_GATT_WRITE_CMPL_EVT*/ , {BTA_HH_IGNORE, BTA_HH_IDLE_ST } + /* READ_DESCR_CMPL_EVT */ , {BTA_HH_IGNORE, BTA_HH_IDLE_ST } + /* WRITE_DESCR_CMPL_EVT */ , {BTA_HH_IGNORE, BTA_HH_IDLE_ST } + /* SCPP_UPDATE_EVT */ , {BTA_HH_IGNORE, BTA_HH_IDLE_ST } + /* BTA_HH_GATT_ENC_CMPL_EVT */ , {BTA_HH_IGNORE, BTA_HH_IDLE_ST } +#endif + +}; + + +const UINT8 bta_hh_st_w4_conn[][BTA_HH_NUM_COLS] = { + /* Event Action Next state */ + /* BTA_HH_API_OPEN_EVT */ {BTA_HH_IGNORE, BTA_HH_W4_CONN_ST }, + /* BTA_HH_API_CLOSE_EVT */ {BTA_HH_IGNORE, BTA_HH_IDLE_ST }, + /* BTA_HH_INT_OPEN_EVT */ {BTA_HH_OPEN_ACT, BTA_HH_W4_CONN_ST }, + /* BTA_HH_INT_CLOSE_EVT */ {BTA_HH_OPEN_FAILURE, BTA_HH_IDLE_ST }, + /* BTA_HH_INT_DATA_EVT */ {BTA_HH_IGNORE, BTA_HH_W4_CONN_ST }, + /* BTA_HH_INT_CTRL_DATA */ {BTA_HH_IGNORE, BTA_HH_W4_CONN_ST }, + /* BTA_HH_INT_HANDSK_EVT */ {BTA_HH_IGNORE, BTA_HH_W4_CONN_ST }, + /* BTA_HH_SDP_CMPL_EVT */ {BTA_HH_SDP_CMPL, BTA_HH_W4_CONN_ST }, + /* BTA_HH_API_WRITE_DEV_EVT */ {BTA_HH_WRITE_DEV_ACT, BTA_HH_W4_CONN_ST }, + /* BTA_HH_API_GET_DSCP_EVT */ {BTA_HH_IGNORE, BTA_HH_W4_CONN_ST }, + /* BTA_HH_API_MAINT_DEV_EVT */ {BTA_HH_MAINT_DEV_ACT, BTA_HH_IDLE_ST }, + /* BTA_HH_OPEN_CMPL_EVT */ {BTA_HH_OPEN_CMPL_ACT, BTA_HH_CONN_ST } +#if (defined BTA_HH_LE_INCLUDED && BTA_HH_LE_INCLUDED == TRUE) + /* BTA_HH_GATT_CLOSE_EVT */ , {BTA_HH_LE_OPEN_FAIL, BTA_HH_IDLE_ST } + /* BTA_HH_GATT_OPEN_EVT */ , {BTA_HH_GATT_OPEN, BTA_HH_W4_CONN_ST } + /* BTA_HH_START_ENC_EVT */ , {BTA_HH_START_SEC, BTA_HH_W4_SEC } + /* BTA_HH_ENC_CMPL_EVT */ , {BTA_HH_IGNORE, BTA_HH_W4_CONN_ST } + /* READ_CHAR_CMPL_EVT */ , {BTA_HH_W4_LE_READ_CHAR, BTA_HH_W4_CONN_ST } + /* BTA_HH_GATT_WRITE_CMPL_EVT*/ , {BTA_HH_W4_LE_WRITE, BTA_HH_W4_CONN_ST } + /* READ_DESCR_CMPL_EVT */ , {BTA_HH_W4_LE_READ_DESCR, BTA_HH_W4_CONN_ST } + /* WRITE_DESCR_CMPL_EVT */ , {BTA_HH_WRITE_DESCR, BTA_HH_W4_CONN_ST } + /* SCPP_UPDATE_EVT */ , {BTA_HH_IGNORE, BTA_HH_W4_CONN_ST } + /* BTA_HH_GATT_ENC_CMPL_EVT */ , {BTA_HH_IGNORE, BTA_HH_W4_CONN_ST } +#endif +}; + + +const UINT8 bta_hh_st_connected[][BTA_HH_NUM_COLS] = { + /* Event Action Next state */ + /* BTA_HH_API_OPEN_EVT */ {BTA_HH_IGNORE, BTA_HH_CONN_ST }, + /* BTA_HH_API_CLOSE_EVT */ {BTA_HH_API_DISC_ACT, BTA_HH_CONN_ST }, + /* BTA_HH_INT_OPEN_EVT */ {BTA_HH_OPEN_ACT, BTA_HH_CONN_ST }, + /* BTA_HH_INT_CLOSE_EVT */ {BTA_HH_CLOSE_ACT, BTA_HH_IDLE_ST }, + /* BTA_HH_INT_DATA_EVT */ {BTA_HH_DATA_ACT, BTA_HH_CONN_ST }, + /* BTA_HH_INT_CTRL_DATA */ {BTA_HH_CTRL_DAT_ACT, BTA_HH_CONN_ST }, + /* BTA_HH_INT_HANDSK_EVT */ {BTA_HH_HANDSK_ACT, BTA_HH_CONN_ST }, + /* BTA_HH_SDP_CMPL_EVT */ {BTA_HH_IGNORE, BTA_HH_CONN_ST }, + /* BTA_HH_API_WRITE_DEV_EVT */ {BTA_HH_WRITE_DEV_ACT, BTA_HH_CONN_ST }, + /* BTA_HH_API_GET_DSCP_EVT */ {BTA_HH_GET_DSCP_ACT, BTA_HH_CONN_ST }, + /* BTA_HH_API_MAINT_DEV_EVT */ {BTA_HH_MAINT_DEV_ACT, BTA_HH_CONN_ST }, + /* BTA_HH_OPEN_CMPL_EVT */ {BTA_HH_IGNORE, BTA_HH_CONN_ST } +#if (defined BTA_HH_LE_INCLUDED && BTA_HH_LE_INCLUDED == TRUE) + /* BTA_HH_GATT_CLOSE_EVT */ , {BTA_HH_GATT_CLOSE, BTA_HH_IDLE_ST } + /* BTA_HH_GATT_OPEN_EVT */ , {BTA_HH_IGNORE, BTA_HH_CONN_ST } + /* BTA_HH_START_ENC_EVT */ , {BTA_HH_IGNORE, BTA_HH_CONN_ST } + /* BTA_HH_ENC_CMPL_EVT */ , {BTA_HH_IGNORE, BTA_HH_CONN_ST } + /* READ_CHAR_CMPL_EVT */ , {BTA_HH_LE_READ_CHAR, BTA_HH_CONN_ST } + /* WRITE_CHAR_CMPL_EVT*/ , {BTA_HH_LE_WRITE, BTA_HH_CONN_ST } + /* READ_DESCR_CMPL_EVT */ , {BTA_HH_LE_READ_DESCR, BTA_HH_CONN_ST } /* do not currently read any descr when connection up */ + /* WRITE_DESCR_CMPL_EVT */ , {BTA_HH_WRITE_DESCR, BTA_HH_CONN_ST } /* do not currently write any descr when connection up */ + /* SCPP_UPDATE_EVT */ , {BTA_HH_LE_UPDATE_SCPP, BTA_HH_CONN_ST } + /* BTA_HH_GATT_ENC_CMPL_EVT */ , {BTA_HH_IGNORE, BTA_HH_CONN_ST } +#endif +}; +#if (defined BTA_HH_LE_INCLUDED && BTA_HH_LE_INCLUDED == TRUE) +const UINT8 bta_hh_st_w4_sec[][BTA_HH_NUM_COLS] = { + /* Event Action Next state */ + /* BTA_HH_API_OPEN_EVT */ {BTA_HH_IGNORE, BTA_HH_W4_SEC }, + /* BTA_HH_API_CLOSE_EVT */ {BTA_HH_API_DISC_ACT, BTA_HH_W4_SEC }, + /* BTA_HH_INT_OPEN_EVT */ {BTA_HH_IGNORE, BTA_HH_W4_SEC }, + /* BTA_HH_INT_CLOSE_EVT */ {BTA_HH_OPEN_FAILURE, BTA_HH_IDLE_ST }, + /* BTA_HH_INT_DATA_EVT */ {BTA_HH_IGNORE, BTA_HH_W4_SEC }, + /* BTA_HH_INT_CTRL_DATA */ {BTA_HH_IGNORE, BTA_HH_W4_SEC }, + /* BTA_HH_INT_HANDSK_EVT */ {BTA_HH_IGNORE, BTA_HH_W4_SEC }, + /* BTA_HH_SDP_CMPL_EVT */ {BTA_HH_IGNORE, BTA_HH_W4_SEC }, + /* BTA_HH_API_WRITE_DEV_EVT */ {BTA_HH_IGNORE , BTA_HH_W4_SEC }, + /* BTA_HH_API_GET_DSCP_EVT */ {BTA_HH_IGNORE, BTA_HH_W4_SEC }, + /* BTA_HH_API_MAINT_DEV_EVT */ {BTA_HH_MAINT_DEV_ACT, BTA_HH_W4_SEC }, + /* BTA_HH_OPEN_CMPL_EVT */ {BTA_HH_IGNORE, BTA_HH_W4_SEC }, + /* BTA_HH_GATT_CLOSE_EVT */ {BTA_HH_LE_OPEN_FAIL, BTA_HH_IDLE_ST }, + /* BTA_HH_GATT_OPEN_EVT */ {BTA_HH_IGNORE, BTA_HH_W4_SEC }, + /* BTA_HH_START_ENC_EVT */ {BTA_HH_IGNORE, BTA_HH_W4_SEC }, + /* BTA_HH_ENC_CMPL_EVT */ {BTA_HH_SEC_CMPL, BTA_HH_W4_CONN_ST }, + /* READ_CHAR_CMPL_EVT */ {BTA_HH_IGNORE, BTA_HH_W4_SEC }, + /* BTA_HH_GATT_WRITE_CMPL_EVT*/ {BTA_HH_IGNORE, BTA_HH_W4_SEC }, + /* READ_DESCR_CMPL_EVT */ {BTA_HH_IGNORE, BTA_HH_W4_SEC }, + /* WRITE_DESCR_CMPL_EVT */ {BTA_HH_IGNORE, BTA_HH_W4_SEC } + /* SCPP_UPDATE_EVT */ , {BTA_HH_IGNORE, BTA_HH_W4_SEC } + /* BTA_HH_GATT_ENC_CMPL_EVT */ , {BTA_HH_GATT_ENC_CMPL, BTA_HH_W4_SEC } +}; +#endif + +/* type for state table */ +typedef const UINT8 (*tBTA_HH_ST_TBL)[BTA_HH_NUM_COLS]; + +/* state table */ +const tBTA_HH_ST_TBL bta_hh_st_tbl[] = { + bta_hh_st_idle, + bta_hh_st_w4_conn, + bta_hh_st_connected +#if (defined BTA_HH_LE_INCLUDED && BTA_HH_LE_INCLUDED == TRUE) + , bta_hh_st_w4_sec +#endif +}; + +/***************************************************************************** +** Global data +*****************************************************************************/ +#if BTA_DYNAMIC_MEMORY == FALSE +tBTA_HH_CB bta_hh_cb; +#else +tBTA_HH_CB *bta_hh_cb_ptr; +#endif +/***************************************************************************** +** Static functions +*****************************************************************************/ +#if BTA_HH_DEBUG == TRUE +static char *bta_hh_evt_code(tBTA_HH_INT_EVT evt_code); +static char *bta_hh_state_code(tBTA_HH_STATE state_code); +#endif + +/******************************************************************************* +** +** Function bta_hh_sm_execute +** +** Description State machine event handling function for HID Host +** +** +** Returns void +** +*******************************************************************************/ +void bta_hh_sm_execute(tBTA_HH_DEV_CB *p_cb, UINT16 event, tBTA_HH_DATA *p_data) +{ + tBTA_HH_ST_TBL state_table; + UINT8 action; + tBTA_HH cback_data; + tBTA_HH_EVT cback_event = 0; +#if BTA_HH_DEBUG == TRUE + tBTA_HH_STATE in_state ; + UINT16 debug_event = event; +#endif + + memset(&cback_data, 0, sizeof(tBTA_HH)); + + /* handle exception, no valid control block was found */ + if (!p_cb) { + /* BTA HH enabled already? otherwise ignore the event although it's bad*/ + if (bta_hh_cb.p_cback != NULL) { + switch (event) { + /* no control block available for new connection */ + case BTA_HH_API_OPEN_EVT: + cback_event = BTA_HH_OPEN_EVT; + /* build cback data */ + bdcpy(cback_data.conn.bda, ((tBTA_HH_API_CONN *)p_data)->bd_addr); + cback_data.conn.status = BTA_HH_ERR_DB_FULL; + cback_data.conn.handle = BTA_HH_INVALID_HANDLE; + /* check if host initiate the connection*/ + cback_data.conn.is_orig = !p_cb->incoming_conn; + break; + /* DB full, BTA_HhAddDev */ + case BTA_HH_API_MAINT_DEV_EVT: + cback_event = p_data->api_maintdev.sub_event; + + if (p_data->api_maintdev.sub_event == BTA_HH_ADD_DEV_EVT) { + bdcpy(cback_data.dev_info.bda, p_data->api_maintdev.bda); + cback_data.dev_info.status = BTA_HH_ERR_DB_FULL; + cback_data.dev_info.handle = BTA_HH_INVALID_HANDLE; + } else { + cback_data.dev_info.status = BTA_HH_ERR_HDL; + cback_data.dev_info.handle = (UINT8)p_data->api_maintdev.hdr.layer_specific; + } + break; + case BTA_HH_API_WRITE_DEV_EVT: + cback_event = (p_data->api_sndcmd.t_type - BTA_HH_FST_BTE_TRANS_EVT) + + BTA_HH_FST_TRANS_CB_EVT; + if (p_data->api_sndcmd.p_data != NULL) { + osi_free(p_data->api_sndcmd.p_data); + } + if (p_data->api_sndcmd.t_type == HID_TRANS_SET_PROTOCOL || + p_data->api_sndcmd.t_type == HID_TRANS_SET_REPORT || + p_data->api_sndcmd.t_type == HID_TRANS_SET_IDLE) { + cback_data.dev_status.status = BTA_HH_ERR_HDL; + cback_data.dev_status.handle = (UINT8)p_data->api_sndcmd.hdr.layer_specific; + } else if (p_data->api_sndcmd.t_type != HID_TRANS_DATA && + p_data->api_sndcmd.t_type != HID_TRANS_CONTROL) { + cback_data.hs_data.handle = (UINT8)p_data->api_sndcmd.hdr.layer_specific; + cback_data.hs_data.status = BTA_HH_ERR_HDL; + /* hs_data.rsp_data will be all zero, which is not valid value */ + } else if (p_data->api_sndcmd.t_type == HID_TRANS_CONTROL && + p_data->api_sndcmd.param == BTA_HH_CTRL_VIRTUAL_CABLE_UNPLUG) { + cback_data.status = BTA_HH_ERR_HDL; + cback_event = BTA_HH_VC_UNPLUG_EVT; + } else { + cback_event = 0; + } + break; + + case BTA_HH_API_CLOSE_EVT: + cback_event = BTA_HH_CLOSE_EVT; + + cback_data.dev_status.status = BTA_HH_ERR_HDL; + cback_data.dev_status.handle = (UINT8)p_data->api_sndcmd.hdr.layer_specific; + break; + + default: + /* invalid handle, call bad API event */ + APPL_TRACE_ERROR("wrong device handle: [%d], event:%d", p_data->hdr.layer_specific, event - BTA_HH_API_OPEN_EVT); + /* Free the callback buffer now */ + if (p_data != NULL && p_data->hid_cback.p_data != NULL) { + osi_free(p_data->hid_cback.p_data); + p_data->hid_cback.p_data = NULL; + } + break; + } + if (cback_event) { + (* bta_hh_cb.p_cback)(cback_event, &cback_data); + } + } + } + /* corresponding CB is found, go to state machine */ + else { +#if BTA_HH_DEBUG == TRUE + in_state = p_cb->state; + APPL_TRACE_EVENT("bta_hh_sm_execute: State 0x%02x [%s], Event [%s]", + in_state, bta_hh_state_code(in_state), + bta_hh_evt_code(debug_event)); +#endif + + if ((p_cb->state == BTA_HH_NULL_ST) || (p_cb->state >= BTA_HH_INVALID_ST)) { + APPL_TRACE_ERROR("bta_hh_sm_execute: Invalid state State = 0x%x, Event = %d", + p_cb->state, event); + return; + } + state_table = bta_hh_st_tbl[p_cb->state - 1]; + + event &= 0xff; + + p_cb->state = state_table[event][BTA_HH_NEXT_STATE] ; + + if ((action = state_table[event][BTA_HH_ACTION]) != BTA_HH_IGNORE) { + (*bta_hh_action[action])(p_cb, p_data); + } + +#if BTA_HH_DEBUG == TRUE + if (in_state != p_cb->state) { + APPL_TRACE_DEBUG("HH State Change: [%s] -> [%s] after Event [%s]", + bta_hh_state_code(in_state), + bta_hh_state_code(p_cb->state), + bta_hh_evt_code(debug_event)); + } +#endif + } + + return; +} +/******************************************************************************* +** +** Function bta_hh_hdl_event +** +** Description HID host main event handling function. +** +** +** Returns void +** +*******************************************************************************/ +BOOLEAN bta_hh_hdl_event(BT_HDR *p_msg) +{ + UINT8 index = BTA_HH_IDX_INVALID; + tBTA_HH_DEV_CB *p_cb = NULL; + + switch (p_msg->event) { + case BTA_HH_API_ENABLE_EVT: + bta_hh_api_enable((tBTA_HH_DATA *) p_msg); + break; + + case BTA_HH_API_DISABLE_EVT: + bta_hh_api_disable(); + break; + + case BTA_HH_DISC_CMPL_EVT: /* disable complete */ + bta_hh_disc_cmpl(); + break; + + default: + /* all events processed in state machine need to find corresponding + CB before proceed */ + if (p_msg->event == BTA_HH_API_OPEN_EVT) { + index = bta_hh_find_cb(((tBTA_HH_API_CONN *)p_msg)->bd_addr); + } else if (p_msg->event == BTA_HH_API_MAINT_DEV_EVT) { + /* if add device */ + if (((tBTA_HH_MAINT_DEV *)p_msg)->sub_event == BTA_HH_ADD_DEV_EVT) { + index = bta_hh_find_cb(((tBTA_HH_MAINT_DEV *)p_msg)->bda); + } else { /* else remove device by handle */ + index = bta_hh_dev_handle_to_cb_idx((UINT8)p_msg->layer_specific); +// btla-specific ++ + /* If BT disable is done while the HID device is connected and Link_Key uses unauthenticated combination + * then we can get into a situation where remove_bonding is called with the index set to 0 (without getting + * cleaned up). Only when VIRTUAL_UNPLUG is called do we cleanup the index and make it MAX_KNOWN. + * So if REMOVE_DEVICE is called and in_use is FALSE then we should treat this as a NULL p_cb. Hence we + * force the index to be IDX_INVALID + */ + if ((index != BTA_HH_IDX_INVALID) && + (bta_hh_cb.kdev[index].in_use == FALSE)) { + index = BTA_HH_IDX_INVALID; + } +// btla-specific -- + } + } else if (p_msg->event == BTA_HH_INT_OPEN_EVT) { + index = bta_hh_find_cb(((tBTA_HH_CBACK_DATA *)p_msg)->addr); + uint8_t hdl = BTA_HH_IDX_INVALID; + if (HID_HostGetDev(((tBTA_HH_CBACK_DATA *)p_msg)->addr, &hdl) == HID_SUCCESS && hdl != BTA_HH_IDX_INVALID) { + bta_hh_cb.cb_index[hdl] = bta_hh_cb.kdev[index].index; + } + } else { + index = bta_hh_dev_handle_to_cb_idx((UINT8)p_msg->layer_specific); + } + + if (index != BTA_HH_IDX_INVALID) { + p_cb = &bta_hh_cb.kdev[index]; + } + +#if BTA_HH_DEBUG + APPL_TRACE_DEBUG("bta_hh_hdl_event:: handle = %d dev_cb[%d] ", p_msg->layer_specific, index); +#endif + bta_hh_sm_execute(p_cb, p_msg->event, (tBTA_HH_DATA *) p_msg); + } + return (TRUE); +} + +/***************************************************************************** +** Debug Functions +*****************************************************************************/ +#if BTA_HH_DEBUG +/******************************************************************************* +** +** Function bta_hh_evt_code +** +** Description +** +** Returns void +** +*******************************************************************************/ +static char *bta_hh_evt_code(tBTA_HH_INT_EVT evt_code) +{ + switch (evt_code) { + case BTA_HH_API_DISABLE_EVT: + return "BTA_HH_API_DISABLE_EVT"; + case BTA_HH_API_ENABLE_EVT: + return "BTA_HH_API_ENABLE_EVT"; + case BTA_HH_API_OPEN_EVT: + return "BTA_HH_API_OPEN_EVT"; + case BTA_HH_API_CLOSE_EVT: + return "BTA_HH_API_CLOSE_EVT"; + case BTA_HH_INT_OPEN_EVT: + return "BTA_HH_INT_OPEN_EVT"; + case BTA_HH_INT_CLOSE_EVT: + return "BTA_HH_INT_CLOSE_EVT"; + case BTA_HH_INT_HANDSK_EVT: + return "BTA_HH_INT_HANDSK_EVT"; + case BTA_HH_INT_DATA_EVT: + return "BTA_HH_INT_DATA_EVT"; + case BTA_HH_INT_CTRL_DATA: + return "BTA_HH_INT_CTRL_DATA"; + case BTA_HH_API_WRITE_DEV_EVT: + return "BTA_HH_API_WRITE_DEV_EVT"; + case BTA_HH_SDP_CMPL_EVT: + return "BTA_HH_SDP_CMPL_EVT"; + case BTA_HH_DISC_CMPL_EVT: + return "BTA_HH_DISC_CMPL_EVT"; + case BTA_HH_API_MAINT_DEV_EVT: + return "BTA_HH_API_MAINT_DEV_EVT"; + case BTA_HH_API_GET_DSCP_EVT: + return "BTA_HH_API_GET_DSCP_EVT"; + case BTA_HH_OPEN_CMPL_EVT: + return "BTA_HH_OPEN_CMPL_EVT"; +#if (defined BTA_HH_LE_INCLUDED && BTA_HH_LE_INCLUDED == TRUE) + case BTA_HH_GATT_CLOSE_EVT: + return "BTA_HH_GATT_CLOSE_EVT"; + case BTA_HH_GATT_OPEN_EVT: + return "BTA_HH_GATT_OPEN_EVT"; + case BTA_HH_START_ENC_EVT: + return "BTA_HH_START_ENC_EVT"; + case BTA_HH_ENC_CMPL_EVT: + return "BTA_HH_ENC_CMPL_EVT"; + case BTA_HH_GATT_READ_CHAR_CMPL_EVT: + return "BTA_HH_GATT_READ_CHAR_CMPL_EVT"; + case BTA_HH_GATT_WRITE_CHAR_CMPL_EVT: + return "BTA_HH_GATT_WRITE_CHAR_CMPL_EVT"; + case BTA_HH_GATT_READ_DESCR_CMPL_EVT: + return "BTA_HH_GATT_READ_DESCR_CMPL_EVT"; + case BTA_HH_GATT_WRITE_DESCR_CMPL_EVT: + return "BTA_HH_GATT_WRITE_DESCR_CMPL_EVT"; +#endif + default: + return "unknown HID Host event code"; + } +} + +/******************************************************************************* +** +** Function bta_hh_state_code +** +** Description get string representation of HID host state code. +** +** Returns void +** +*******************************************************************************/ +static char *bta_hh_state_code(tBTA_HH_STATE state_code) +{ + switch (state_code) { + case BTA_HH_NULL_ST: + return"BTA_HH_NULL_ST"; + case BTA_HH_IDLE_ST: + return "BTA_HH_IDLE_ST"; + case BTA_HH_W4_CONN_ST: + return "BTA_HH_W4_CONN_ST"; + case BTA_HH_CONN_ST: + return "BTA_HH_CONN_ST"; +#if (defined BTA_HH_LE_INCLUDED && BTA_HH_LE_INCLUDED == TRUE) + case BTA_HH_W4_SEC: + return "BTA_HH_W4_SEC"; +#endif + default: + return "unknown HID Host state"; + } +} + +#endif /* Debug Functions */ + +#endif /* BTA_HH_INCLUDED */ diff --git a/lib/bt/host/bluedroid/bta/hh/bta_hh_utils.c b/lib/bt/host/bluedroid/bta/hh/bta_hh_utils.c new file mode 100644 index 00000000..6dc3ec02 --- /dev/null +++ b/lib/bt/host/bluedroid/bta/hh/bta_hh_utils.c @@ -0,0 +1,531 @@ +/****************************************************************************** + * + * Copyright (C) 2005-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ +#include + +#include "common/bt_target.h" +#if defined(BTA_HH_INCLUDED) && (BTA_HH_INCLUDED == TRUE) + +#include "osi/allocator.h" +#include "bta_hh_int.h" + +/* if SSR max latency is not defined by remote device, set the default value + as half of the link supervision timeout */ +#define BTA_HH_GET_DEF_SSR_MAX_LAT(x) ((x)>> 1) + +/***************************************************************************** +** Constants +*****************************************************************************/ +#define BTA_HH_KB_CTRL_MASK 0x11 +#define BTA_HH_KB_SHIFT_MASK 0x22 +#define BTA_HH_KB_ALT_MASK 0x44 +#define BTA_HH_KB_GUI_MASK 0x88 + +#define BTA_HH_KB_CAPS_LOCK 0x39 /* caps lock */ +#define BTA_HH_KB_NUM_LOCK 0x53 /* num lock */ + + +#define BTA_HH_MAX_RPT_CHARS 8 + +static const UINT8 bta_hh_mod_key_mask[BTA_HH_MOD_MAX_KEY] = { + BTA_HH_KB_CTRL_MASK, + BTA_HH_KB_SHIFT_MASK, + BTA_HH_KB_ALT_MASK, + BTA_HH_KB_GUI_MASK +}; + + +/******************************************************************************* +** +** Function bta_hh_find_cb +** +** Description Find best available control block according to BD address. +** +** +** Returns void +** +*******************************************************************************/ +UINT8 bta_hh_find_cb(BD_ADDR bda) +{ + UINT8 xx; + + /* See how many active devices there are. */ + for (xx = 0; xx < BTA_HH_MAX_DEVICE; xx++) { + /* check if any active/known devices is a match */ + if ((!bdcmp (bda, bta_hh_cb.kdev[xx].addr) && + bdcmp(bda, bd_addr_null) != 0) ) { +#if BTA_HH_DEBUG + APPL_TRACE_DEBUG("found kdev_cb[%d] hid_handle = %d ", xx, + bta_hh_cb.kdev[xx].hid_handle) +#endif + return xx; + } +#if BTA_HH_DEBUG + else { + APPL_TRACE_DEBUG("in_use ? [%d] kdev[%d].hid_handle = %d state = [%d]", + bta_hh_cb.kdev[xx].in_use, xx, + bta_hh_cb.kdev[xx].hid_handle, + bta_hh_cb.kdev[xx].state); + } +#endif + } + + /* if no active device match, find a spot for it */ + for (xx = 0; xx < BTA_HH_MAX_DEVICE; xx++) { + if (!bta_hh_cb.kdev[xx].in_use) { + bdcpy(bta_hh_cb.kdev[xx].addr, bda); + bta_hh_cb.kdev[xx].in_use = TRUE; + break; + } + } + /* If device list full, report BTA_HH_IDX_INVALID */ +#if BTA_HH_DEBUG + APPL_TRACE_DEBUG("bta_hh_find_cb:: index = %d while max = %d", + xx, BTA_HH_MAX_DEVICE); +#endif + + if (xx == BTA_HH_MAX_DEVICE) { + xx = BTA_HH_IDX_INVALID; + } + + return xx; +} + +/******************************************************************************* +** +** Function bta_hh_clean_up_kdev +** +** Description Clean up device control block when device is removed from +** manitainace list, and update control block index map. +** +** Returns void +** +*******************************************************************************/ +void bta_hh_clean_up_kdev(tBTA_HH_DEV_CB *p_cb) +{ + UINT8 index; + + if (p_cb->hid_handle != BTA_HH_INVALID_HANDLE ) { +#if BTA_HH_LE_INCLUDED == TRUE + if (p_cb->is_le_device) { + bta_hh_cb.le_cb_index[BTA_HH_GET_LE_CB_IDX(p_cb->hid_handle)] = BTA_HH_IDX_INVALID; + } else +#endif + { + bta_hh_cb.cb_index[p_cb->hid_handle] = BTA_HH_IDX_INVALID; + } + } + + /* reset device control block */ + index = p_cb->index; /* Preserve index for this control block */ + + /* Free buffer for report descriptor info */ + utl_freebuf((void **)&p_cb->dscp_info.descriptor.dsc_list); + + memset(p_cb, 0, sizeof (tBTA_HH_DEV_CB)); /* Reset control block */ + + p_cb->index = index; /* Restore index for this control block */ + p_cb->state = BTA_HH_IDLE_ST; + p_cb->hid_handle = BTA_HH_INVALID_HANDLE; + +} +/******************************************************************************* +** +** Function bta_hh_update_di_info +** +** Description Maintain a known device list for BTA HH. +** +** Returns void +** +*******************************************************************************/ +void bta_hh_update_di_info(tBTA_HH_DEV_CB *p_cb, UINT16 vendor_id, UINT16 product_id, + UINT16 version, UINT8 flag) +{ +#if BTA_HH_DEBUG + APPL_TRACE_DEBUG("vendor_id = 0x%2x product_id = 0x%2x version = 0x%2x", + vendor_id, product_id, version); +#endif + p_cb->dscp_info.vendor_id = vendor_id; + p_cb->dscp_info.product_id = product_id; + p_cb->dscp_info.version = version; +#if (defined BTA_HH_LE_INCLUDED && BTA_HH_LE_INCLUDED == TRUE) + p_cb->dscp_info.flag = flag; +#else + UNUSED(flag); +#endif +} +/******************************************************************************* +** +** Function bta_hh_add_device_to_list +** +** Description Maintain a known device list for BTA HH. +** +** Returns void +** +*******************************************************************************/ +void bta_hh_add_device_to_list(tBTA_HH_DEV_CB *p_cb, UINT8 handle, + UINT16 attr_mask, + tHID_DEV_DSCP_INFO *p_dscp_info, + UINT8 sub_class, + UINT16 ssr_max_latency, + UINT16 ssr_min_tout, + UINT8 app_id) +{ +#if BTA_HH_DEBUG + APPL_TRACE_DEBUG("subclass = 0x%2x", sub_class); +#endif + + p_cb->hid_handle = handle; + p_cb->in_use = TRUE; + p_cb->attr_mask = attr_mask; + + p_cb->sub_class = sub_class; + p_cb->app_id = app_id; + + p_cb->dscp_info.ssr_max_latency = ssr_max_latency; + p_cb->dscp_info.ssr_min_tout = ssr_min_tout; + + /* store report descriptor info */ + if ( p_dscp_info) { + utl_freebuf((void **)&p_cb->dscp_info.descriptor.dsc_list); + + if (p_dscp_info->dl_len && + (p_cb->dscp_info.descriptor.dsc_list = + (UINT8 *)osi_malloc(p_dscp_info->dl_len)) != NULL) { + p_cb->dscp_info.descriptor.dl_len = p_dscp_info->dl_len; + memcpy(p_cb->dscp_info.descriptor.dsc_list, p_dscp_info->dsc_list, + p_dscp_info->dl_len); + } + } + return; +} + +/******************************************************************************* +** +** Function bta_hh_tod_spt +** +** Description Check to see if this type of device is supported +** +** Returns +** +*******************************************************************************/ +BOOLEAN bta_hh_tod_spt(tBTA_HH_DEV_CB *p_cb, UINT8 sub_class) +{ + UINT8 xx; + UINT8 cod = (sub_class >> 2); /* lower two bits are reserved */ + + for (xx = 0 ; xx < p_bta_hh_cfg->max_devt_spt; xx ++) { + if (cod == (UINT8) p_bta_hh_cfg->p_devt_list[xx].tod) { + p_cb->app_id = p_bta_hh_cfg->p_devt_list[xx].app_id; +#if BTA_HH_DEBUG + APPL_TRACE_EVENT("bta_hh_tod_spt sub_class:0x%x supported", sub_class); +#endif + return TRUE; + } + } +#if BTA_HH_DEBUG + APPL_TRACE_ERROR("bta_hh_tod_spt sub_class:0x%x NOT supported", sub_class); +#endif + return FALSE; +} + + +/******************************************************************************* +** +** Function bta_hh_parse_keybd_rpt +** +** Description This utility function parse a boot mode keyboard report. +** +** Returns void +** +*******************************************************************************/ +void bta_hh_parse_keybd_rpt(tBTA_HH_BOOT_RPT *p_kb_data, UINT8 *p_report, + UINT16 report_len) +{ + tBTA_HH_KB_CB *p_kb = &bta_hh_cb.kb_cb; + tBTA_HH_KEYBD_RPT *p_data = &p_kb_data->data_rpt.keybd_rpt; + + UINT8 this_char, ctl_shift; + UINT16 xx, yy, key_idx = 0; + UINT8 this_report[BTA_HH_MAX_RPT_CHARS]; + +#if BTA_HH_DEBUG + APPL_TRACE_DEBUG("bta_hh_parse_keybd_rpt: (report=%p, report_len=%d) called", + p_report, report_len); +#endif + + if (report_len < 2) { + return; + } + + ctl_shift = *p_report++; + report_len--; + + if (report_len > BTA_HH_MAX_RPT_CHARS) { + report_len = BTA_HH_MAX_RPT_CHARS; + } + + memset (this_report, 0, BTA_HH_MAX_RPT_CHARS); + memset (p_data, 0, sizeof(tBTA_HH_KEYBD_RPT)); + memcpy (this_report, p_report, report_len); + + /* Take care of shift, control, GUI and alt, modifier keys */ + for (xx = 0; xx < BTA_HH_MOD_MAX_KEY; xx ++ ) { + if (ctl_shift & bta_hh_mod_key_mask[xx]) { + APPL_TRACE_DEBUG("Mod Key[%02x] pressed", bta_hh_mod_key_mask[xx] ); + p_kb->mod_key[xx] = TRUE; + } else if (p_kb->mod_key[xx]) { + p_kb->mod_key[xx] = FALSE; + } + /* control key flag is set */ + p_data->mod_key[xx] = p_kb->mod_key[xx]; + } + + /***************************************************************************/ + /* First step is to remove all characters we saw in the last report */ + /***************************************************************************/ + for (xx = 0; xx < report_len; xx++) { + for (yy = 0; yy < BTA_HH_MAX_RPT_CHARS; yy++) { + if (this_report[xx] == p_kb->last_report[yy]) { + this_report[xx] = 0; + } + } + } + /***************************************************************************/ + /* Now, process all the characters in the report, up to 6 keycodes */ + /***************************************************************************/ + for (xx = 0; xx < report_len; xx++) { +#if BTA_HH_DEBUG + APPL_TRACE_DEBUG("this_char = %02x", this_report[xx]); +#endif + if ((this_char = this_report[xx]) == 0) { + continue; + } + /* take the key code as the report data */ + if (this_report[xx] == BTA_HH_KB_CAPS_LOCK) { + p_kb->caps_lock = p_kb->caps_lock ? FALSE : TRUE; + } else if (this_report[xx] == BTA_HH_KB_NUM_LOCK) { + p_kb->num_lock = p_kb->num_lock ? FALSE : TRUE; + } else { + p_data->this_char[key_idx ++] = this_char; + } + +#if BTA_HH_DEBUG + APPL_TRACE_DEBUG("found keycode %02x ", this_report[xx]); +#endif + p_data->caps_lock = p_kb->caps_lock; + p_data->num_lock = p_kb->num_lock; + } + + memset (p_kb->last_report, 0, BTA_HH_MAX_RPT_CHARS); + memcpy (p_kb->last_report, p_report, report_len); + + return; +} + +/******************************************************************************* +** +** Function bta_hh_parse_mice_rpt +** +** Description This utility function parse a boot mode mouse report. +** +** Returns void +** +*******************************************************************************/ +void bta_hh_parse_mice_rpt(tBTA_HH_BOOT_RPT *p_mice_data, UINT8 *p_report, + UINT16 report_len) +{ + tBTA_HH_MICE_RPT *p_data = &p_mice_data->data_rpt.mice_rpt; +#if BTA_HH_DEBUG + UINT8 xx; + + APPL_TRACE_DEBUG("bta_hh_parse_mice_rpt: bta_keybd_rpt_rcvd(report=%p, \ + report_len=%d) called", p_report, report_len); +#endif + + if (report_len < 3) { + return; + } + + if (report_len > BTA_HH_MAX_RPT_CHARS) { + report_len = BTA_HH_MAX_RPT_CHARS; + } + +#if BTA_HH_DEBUG + for (xx = 0; xx < report_len; xx++) { + APPL_TRACE_DEBUG("this_char = %02x", p_report[xx]); + } +#endif + + /* only first bytes lower 3 bits valid */ + p_data->mouse_button = (p_report[0] & 0x07); + + /* x displacement */ + p_data->delta_x = p_report[1]; + + /* y displacement */ + p_data->delta_y = p_report[2]; + +#if BTA_HH_DEBUG + APPL_TRACE_DEBUG("mice button: 0x%2x", p_data->mouse_button); + APPL_TRACE_DEBUG("mice move: x = %d y = %d", p_data->delta_x, + p_data->delta_y ); +#endif + + return; + +} + +/******************************************************************************* +** +** Function bta_hh_read_ssr_param +** +** Description Read the SSR Parameter for the remote device +** +** Returns tBTA_HH_STATUS operation status +** +*******************************************************************************/ +tBTA_HH_STATUS bta_hh_read_ssr_param(BD_ADDR bd_addr, UINT16 *p_max_ssr_lat, UINT16 *p_min_ssr_tout) +{ + tBTA_HH_STATUS status = BTA_HH_ERR; + tBTA_HH_CB *p_cb = &bta_hh_cb; + UINT8 i; + UINT16 ssr_max_latency; + for (i = 0; i < BTA_HH_MAX_KNOWN; i ++) { + if (memcmp(p_cb->kdev[i].addr, bd_addr, BD_ADDR_LEN) == 0) { + + /* if remote device does not have HIDSSRHostMaxLatency attribute in SDP, + set SSR max latency default value here. */ + if (p_cb->kdev[i].dscp_info.ssr_max_latency == HID_SSR_PARAM_INVALID) { + /* The default is calculated as half of link supervision timeout.*/ + + BTM_GetLinkSuperTout(p_cb->kdev[i].addr, &ssr_max_latency) ; + ssr_max_latency = BTA_HH_GET_DEF_SSR_MAX_LAT(ssr_max_latency); + + /* per 1.1 spec, if the newly calculated max latency is greater than + BTA_HH_SSR_MAX_LATENCY_DEF which is 500ms, use BTA_HH_SSR_MAX_LATENCY_DEF */ + if (ssr_max_latency > BTA_HH_SSR_MAX_LATENCY_DEF) { + ssr_max_latency = BTA_HH_SSR_MAX_LATENCY_DEF; + } + + * p_max_ssr_lat = ssr_max_latency; + } else { + * p_max_ssr_lat = p_cb->kdev[i].dscp_info.ssr_max_latency; + } + + if (p_cb->kdev[i].dscp_info.ssr_min_tout == HID_SSR_PARAM_INVALID) { + * p_min_ssr_tout = BTA_HH_SSR_MIN_TOUT_DEF; + } else { + * p_min_ssr_tout = p_cb->kdev[i].dscp_info.ssr_min_tout; + } + + status = BTA_HH_OK; + + break; + } + } + + return status; +} + +/******************************************************************************* +** +** Function bta_hh_cleanup_disable +** +** Description when disable finished, cleanup control block and send callback +** +** +** Returns void +** +*******************************************************************************/ +void bta_hh_cleanup_disable(tBTA_HH_STATUS status) +{ + UINT8 xx; + /* free buffer in CB holding report descriptors */ + for (xx = 0; xx < BTA_HH_MAX_DEVICE; xx ++) { + utl_freebuf((void **)&bta_hh_cb.kdev[xx].dscp_info.descriptor.dsc_list); + } + utl_freebuf((void **)&bta_hh_cb.p_disc_db); + + if (bta_hh_cb.p_cback) { + (*bta_hh_cb.p_cback)(BTA_HH_DISABLE_EVT, (tBTA_HH*)&status); + /* all connections are down, no waiting for diconnect */ + memset(&bta_hh_cb, 0, sizeof(tBTA_HH_CB)); + } +} + +/******************************************************************************* +** +** Function bta_hh_dev_handle_to_cb_idx +** +** Description convert a HID device handle to the device control block index. +** +** +** Returns UINT8: index of the device control block. +** +*******************************************************************************/ +UINT8 bta_hh_dev_handle_to_cb_idx(UINT8 dev_handle) +{ + UINT8 index = BTA_HH_IDX_INVALID; + +#if BTA_HH_LE_INCLUDED == TRUE + if (BTA_HH_IS_LE_DEV_HDL(dev_handle)) { + if (BTA_HH_IS_LE_DEV_HDL_VALID(dev_handle)) { + index = bta_hh_cb.le_cb_index[BTA_HH_GET_LE_CB_IDX(dev_handle)]; + } +#if BTA_HH_DEBUG == TRUE + APPL_TRACE_DEBUG("bta_hh_dev_handle_to_cb_idx dev_handle = %d index = %d", dev_handle, index); +#endif + } else +#endif + { + /* regular HID device checking */ + if (dev_handle < BTA_HH_MAX_KNOWN ) { + index = bta_hh_cb.cb_index[dev_handle]; + } + } + return index; + +} +#if BTA_HH_DEBUG +/******************************************************************************* +** +** Function bta_hh_trace_dev_db +** +** Description Check to see if this type of device is supported +** +** Returns +** +*******************************************************************************/ +void bta_hh_trace_dev_db(void) +{ + UINT8 xx; + + APPL_TRACE_DEBUG("bta_hh_trace_dev_db:: Device DB list********************"); + + for (xx = 0; xx < BTA_HH_MAX_DEVICE; xx++) { + APPL_TRACE_DEBUG("kdev[%d] in_use[%d] handle[%d] ", xx, + bta_hh_cb.kdev[xx].in_use, bta_hh_cb.kdev[xx].hid_handle); + + APPL_TRACE_DEBUG("\t\t\t attr_mask[%04x] state [%d] sub_class[%02x] index = %d", + bta_hh_cb.kdev[xx].attr_mask, bta_hh_cb.kdev[xx].state, + bta_hh_cb.kdev[xx].sub_class, bta_hh_cb.kdev[xx].index); + } + APPL_TRACE_DEBUG("*********************************************************"); +} +#endif +#endif /* HL_INCLUDED */ diff --git a/lib/bt/host/bluedroid/bta/hh/include/bta_hh_int.h b/lib/bt/host/bluedroid/bta/hh/include/bta_hh_int.h new file mode 100644 index 00000000..0efb2fca --- /dev/null +++ b/lib/bt/host/bluedroid/bta/hh/include/bta_hh_int.h @@ -0,0 +1,403 @@ +/****************************************************************************** + * + * Copyright (C) 2005-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains BTA HID Host internal definitions + * + ******************************************************************************/ + +#ifndef BTA_HH_INT_H +#define BTA_HH_INT_H + +#include "bta/bta_sys.h" +#include "bta/utl.h" +#include "bta/bta_hh_api.h" + +//#if BTA_HH_LE_INCLUDED == TRUE +#include "bta/bta_gatt_api.h" +//#endif +#if defined(BTA_HH_INCLUDED) && (BTA_HH_INCLUDED == TRUE) + +/* can be moved to bta/bta_api.h */ +#define BTA_HH_MAX_RPT_CHARS 8 + +#if (BTA_GATT_INCLUDED == FALSE || BLE_INCLUDED == FALSE) +#undef BTA_HH_LE_INCLUDED +#define BTA_HH_LE_INCLUDED FALSE +#endif + +/* state machine events, these events are handled by the state machine */ +enum { + BTA_HH_API_OPEN_EVT = BTA_SYS_EVT_START(BTA_ID_HH), + BTA_HH_API_CLOSE_EVT, + BTA_HH_INT_OPEN_EVT, + BTA_HH_INT_CLOSE_EVT, + BTA_HH_INT_DATA_EVT, + BTA_HH_INT_CTRL_DATA, + BTA_HH_INT_HANDSK_EVT, + BTA_HH_SDP_CMPL_EVT, + BTA_HH_API_WRITE_DEV_EVT, + BTA_HH_API_GET_DSCP_EVT, + BTA_HH_API_MAINT_DEV_EVT, + BTA_HH_OPEN_CMPL_EVT, +#if (defined BTA_HH_LE_INCLUDED && BTA_HH_LE_INCLUDED == TRUE) + BTA_HH_GATT_CLOSE_EVT, + BTA_HH_GATT_OPEN_EVT, + BTA_HH_START_ENC_EVT, + BTA_HH_ENC_CMPL_EVT, + BTA_HH_GATT_READ_CHAR_CMPL_EVT, + BTA_HH_GATT_WRITE_CHAR_CMPL_EVT, + BTA_HH_GATT_READ_DESCR_CMPL_EVT, + BTA_HH_GATT_WRITE_DESCR_CMPL_EVT, + BTA_HH_API_SCPP_UPDATE_EVT, + BTA_HH_GATT_ENC_CMPL_EVT, +#endif + + /* not handled by execute state machine */ + BTA_HH_API_ENABLE_EVT, + BTA_HH_API_DISABLE_EVT, + BTA_HH_DISC_CMPL_EVT +}; +typedef UINT16 tBTA_HH_INT_EVT; /* HID host internal events */ + +#define BTA_HH_INVALID_EVT (BTA_HH_DISC_CMPL_EVT + 1) + +/* event used to map between BTE event and BTA event */ +#define BTA_HH_FST_TRANS_CB_EVT BTA_HH_GET_RPT_EVT +#define BTA_HH_FST_BTE_TRANS_EVT HID_TRANS_GET_REPORT + +/* sub event code used for device maintainence API call */ +#define BTA_HH_ADD_DEV 0 +#define BTA_HH_REMOVE_DEV 1 + +/* state machine states */ +enum { + BTA_HH_NULL_ST, + BTA_HH_IDLE_ST, + BTA_HH_W4_CONN_ST, + BTA_HH_CONN_ST +#if (defined BTA_HH_LE_INCLUDED && BTA_HH_LE_INCLUDED == TRUE) + , BTA_HH_W4_SEC +#endif + , BTA_HH_INVALID_ST /* Used to check invalid states before executing SM function */ + +}; +typedef UINT8 tBTA_HH_STATE; + +/* data structure used to send a command/data to HID device */ +typedef struct { + BT_HDR hdr; + UINT8 t_type; + UINT8 param; + UINT8 rpt_id; +#if (defined BTA_HH_LE_INCLUDED && BTA_HH_LE_INCLUDED == TRUE) + UINT8 srvc_id; +#endif + UINT16 data; + BT_HDR *p_data; +} tBTA_HH_CMD_DATA; + +/* data type for BTA_HH_API_ENABLE_EVT */ +typedef struct { + BT_HDR hdr; + UINT8 sec_mask; + UINT8 service_name[BTA_SERVICE_NAME_LEN + 1]; + tBTA_HH_CBACK *p_cback; +} tBTA_HH_API_ENABLE; + +typedef struct { + BT_HDR hdr; + BD_ADDR bd_addr; + UINT8 sec_mask; + tBTA_HH_PROTO_MODE mode; +} tBTA_HH_API_CONN; + +/* internal event data from BTE HID callback */ +typedef struct { + BT_HDR hdr; + BD_ADDR addr; + UINT32 data; + BT_HDR *p_data; +} tBTA_HH_CBACK_DATA; + +typedef struct { + BT_HDR hdr; + BD_ADDR bda; + UINT16 attr_mask; + UINT16 sub_event; + UINT8 sub_class; + UINT8 app_id; + tBTA_HH_DEV_DSCP_INFO dscp_info; +} tBTA_HH_MAINT_DEV; + +#if BTA_HH_LE_INCLUDED == TRUE +typedef struct { + BT_HDR hdr; + UINT16 conn_id; + tBTA_GATT_REASON reason; /* disconnect reason code, not useful when connect event is reported */ + +} tBTA_HH_LE_CLOSE; + +typedef struct { + BT_HDR hdr; + UINT16 scan_int; + UINT16 scan_win; +} tBTA_HH_SCPP_UPDATE; +#endif +/* union of all event data types */ +typedef union { + BT_HDR hdr; + tBTA_HH_API_ENABLE api_enable; + tBTA_HH_API_CONN api_conn; + tBTA_HH_CMD_DATA api_sndcmd; + tBTA_HH_CBACK_DATA hid_cback; + tBTA_HH_STATUS status; + tBTA_HH_MAINT_DEV api_maintdev; +#if BTA_HH_LE_INCLUDED == TRUE + tBTA_HH_LE_CLOSE le_close; + tBTA_GATTC_OPEN le_open; + tBTA_HH_SCPP_UPDATE le_scpp_update; + tBTA_GATTC_ENC_CMPL_CB le_enc_cmpl; +#endif +} tBTA_HH_DATA; + +#if (defined BTA_HH_LE_INCLUDED && BTA_HH_LE_INCLUDED == TRUE) +typedef struct { + UINT8 index; + BOOLEAN in_use; + UINT8 inst_id; /* share service instance ID and report instance ID, as + hi 4 for service instance ID, low 4 as charatceristic instance ID */ + tBTA_HH_RPT_TYPE rpt_type; + UINT16 uuid; + UINT8 prop; + UINT8 rpt_id; + BOOLEAN client_cfg_exist; + UINT16 client_cfg_value; +} tBTA_HH_LE_RPT; + +#ifndef BTA_HH_LE_RPT_MAX +#define BTA_HH_LE_RPT_MAX 20 +#endif + +typedef struct { + BOOLEAN in_use; + tBTA_HH_LE_RPT report[BTA_HH_LE_RPT_MAX]; + +#define BTA_HH_LE_PROTO_MODE_BIT 0x01 +#define BTA_HH_LE_CP_BIT 0x02 + UINT8 option_char; /* control point char exisit or not */ + + BOOLEAN expl_incl_srvc; + UINT8 incl_srvc_inst; /* assuming only one included service : battery service */ + UINT8 cur_expl_char_idx; /* currently discovering service index */ + UINT8 *rpt_map; + UINT16 ext_rpt_ref; + tBTA_HH_DEV_DESCR descriptor; + +} tBTA_HH_LE_HID_SRVC; + +#ifndef BTA_HH_LE_HID_SRVC_MAX +#define BTA_HH_LE_HID_SRVC_MAX 1 +#endif + +/* convert a HID handle to the LE CB index */ +#define BTA_HH_GET_LE_CB_IDX(x) (((x) >> 4) - 1) +/* convert a GATT connection ID to HID device handle, it is the hi 4 bits of a UINT8 */ +#define BTA_HH_GET_LE_DEV_HDL(x) (UINT8)(((x) + 1) << 4) +/* check to see if th edevice handle is a LE device handle */ +#define BTA_HH_IS_LE_DEV_HDL(x) ((x) & 0xf0) +#define BTA_HH_IS_LE_DEV_HDL_VALID(x) (((x)>>4) <= BTA_HH_LE_MAX_KNOWN) +#endif + +/* device control block */ +typedef struct { + tBTA_HH_DEV_DSCP_INFO dscp_info; /* report descriptor and DI information */ + BD_ADDR addr; /* BD-Addr of the HID device */ + UINT16 attr_mask; /* attribute mask */ + UINT16 w4_evt; /* W4_handshake event name */ + UINT8 index; /* index number referenced to handle index */ + UINT8 sub_class; /* Cod sub class */ + UINT8 sec_mask; /* security mask */ + UINT8 app_id; /* application ID for this connection */ + UINT8 hid_handle; /* device handle : low 4 bits for regular HID: HID_HOST_MAX_DEVICES can not exceed 15; + high 4 bits for LE HID: GATT_MAX_PHY_CHANNEL can not exceed 15 */ + BOOLEAN vp; /* virtually unplug flag */ + BOOLEAN in_use; /* control block currently in use */ + BOOLEAN incoming_conn; /* is incoming connection? */ + UINT8 incoming_hid_handle; /* temporary handle for incoming connection? */ + BOOLEAN opened; /* TRUE if device successfully opened HID connection */ + tBTA_HH_PROTO_MODE mode; /* protocol mode */ + tBTA_HH_PROTO_MODE new_mode; /* protocol mode */ + tBTA_HH_STATE state; /* CB state */ + +#if (BTA_HH_LE_INCLUDED == TRUE) +#define BTA_HH_LE_DISC_NONE 0x00 +#define BTA_HH_LE_DISC_HIDS 0x01 +#define BTA_HH_LE_DISC_DIS 0x02 +#define BTA_HH_LE_DISC_SCPS 0x04 + + UINT8 disc_active; + tBTA_HH_STATUS status; + tBTA_GATT_REASON reason; + BOOLEAN is_le_device; + tBTA_HH_LE_HID_SRVC hid_srvc[BTA_HH_LE_HID_SRVC_MAX]; + UINT16 conn_id; + BOOLEAN in_bg_conn; + UINT8 total_srvc; + UINT8 clt_cfg_idx; + UINT8 cur_srvc_index; /* currently discovering service index */ + BOOLEAN scps_supported; + +#define BTA_HH_LE_SCPS_NOTIFY_NONE 0 +#define BTA_HH_LE_SCPS_NOTIFY_SPT 0x01 +#define BTA_HH_LE_SCPS_NOTIFY_ENB 0x02 + UINT8 scps_notify; /* scan refresh supported/notification enabled */ +#endif + + BOOLEAN security_pending; +} tBTA_HH_DEV_CB; + +/* key board parsing control block */ +typedef struct { + BOOLEAN mod_key[4]; /* ctrl, shift(upper), Alt, GUI */ + BOOLEAN num_lock; + BOOLEAN caps_lock; + UINT8 last_report[BTA_HH_MAX_RPT_CHARS]; +} tBTA_HH_KB_CB; + +/****************************************************************************** +** Main Control Block +*******************************************************************************/ +typedef struct { + tBTA_HH_KB_CB kb_cb; /* key board control block, + suppose BTA will connect + to only one keyboard at + the same time */ + tBTA_HH_DEV_CB kdev[BTA_HH_MAX_DEVICE]; /* device control block */ + tBTA_HH_DEV_CB *p_cur; /* current device control + block idx, used in sdp */ + UINT8 cb_index[BTA_HH_MAX_KNOWN]; /* maintain a CB index + map to dev handle */ +#if (defined BTA_HH_LE_INCLUDED && BTA_HH_LE_INCLUDED == TRUE) + UINT8 le_cb_index[BTA_HH_MAX_DEVICE]; /* maintain a CB index map to LE dev handle */ + tBTA_GATTC_IF gatt_if; +#endif + tBTA_HH_CBACK *p_cback; /* Application callbacks */ + tSDP_DISCOVERY_DB *p_disc_db; + UINT8 trace_level; /* tracing level */ + UINT8 cnt_num; /* connected device number */ + BOOLEAN w4_disable; /* w4 disable flag */ +} +tBTA_HH_CB; + +#if BTA_DYNAMIC_MEMORY == FALSE +extern tBTA_HH_CB bta_hh_cb; +#else +extern tBTA_HH_CB *bta_hh_cb_ptr; +#define bta_hh_cb (*bta_hh_cb_ptr) +#endif + +/* from bta_hh_cfg.c */ +extern tBTA_HH_CFG *p_bta_hh_cfg; + +/***************************************************************************** +** Function prototypes +*****************************************************************************/ +extern BOOLEAN bta_hh_hdl_event(BT_HDR *p_msg); +extern void bta_hh_sm_execute(tBTA_HH_DEV_CB *p_cb, UINT16 event, + tBTA_HH_DATA *p_data); + +/* action functions */ +extern void bta_hh_api_disc_act(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data); +extern void bta_hh_open_act(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data); +extern void bta_hh_close_act(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data); +extern void bta_hh_data_act(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data); +extern void bta_hh_ctrl_dat_act(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data); +extern void bta_hh_start_sdp(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data); +extern void bta_hh_sdp_cmpl(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data); +extern void bta_hh_write_dev_act(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data); +extern void bta_hh_get_dscp_act(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data); +extern void bta_hh_handsk_act(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data); +extern void bta_hh_maint_dev_act(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data); +extern void bta_hh_open_cmpl_act(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data); +extern void bta_hh_open_failure(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data); + +/* utility functions */ +extern UINT8 bta_hh_find_cb(BD_ADDR bda); +extern void bta_hh_parse_keybd_rpt(tBTA_HH_BOOT_RPT *p_kb_data, + UINT8 *p_report, UINT16 report_len); +extern void bta_hh_parse_mice_rpt(tBTA_HH_BOOT_RPT *p_kb_data, + UINT8 *p_report, UINT16 report_len); +extern BOOLEAN bta_hh_tod_spt(tBTA_HH_DEV_CB *p_cb, UINT8 sub_class); +extern void bta_hh_clean_up_kdev(tBTA_HH_DEV_CB *p_cb); + +extern void bta_hh_add_device_to_list(tBTA_HH_DEV_CB *p_cb, UINT8 handle, + UINT16 attr_mask, + tHID_DEV_DSCP_INFO *p_dscp_info, + UINT8 sub_class, UINT16 max_latency, UINT16 min_tout, UINT8 app_id); +extern void bta_hh_update_di_info(tBTA_HH_DEV_CB *p_cb, UINT16 vendor_id, UINT16 product_id, + UINT16 version, UINT8 flag); +extern void bta_hh_cleanup_disable(tBTA_HH_STATUS status); + +extern UINT8 bta_hh_dev_handle_to_cb_idx(UINT8 dev_handle); + +/* action functions used outside state machine */ +extern void bta_hh_api_enable(tBTA_HH_DATA *p_data); +extern void bta_hh_api_disable(void); +extern void bta_hh_disc_cmpl(void); + +extern tBTA_HH_STATUS bta_hh_read_ssr_param(BD_ADDR bd_addr, UINT16 *p_max_ssr_lat, UINT16 *p_min_ssr_tout); + +/* functions for LE HID */ +#if (BTA_HH_LE_INCLUDED == TRUE) +extern void bta_hh_le_enable(void); +extern BOOLEAN bta_hh_le_is_hh_gatt_if(tBTA_GATTC_IF client_if); +extern void bta_hh_le_deregister(void); +extern BOOLEAN bta_hh_is_le_device(tBTA_HH_DEV_CB *p_cb, BD_ADDR remote_bda); +extern void bta_hh_le_open_conn(tBTA_HH_DEV_CB *p_cb, BD_ADDR remote_bda); +extern void bta_hh_le_api_disc_act(tBTA_HH_DEV_CB *p_cb); +extern void bta_hh_le_get_dscp_act(tBTA_HH_DEV_CB *p_cb); +extern void bta_hh_le_write_dev_act(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data); +extern UINT8 bta_hh_le_add_device(tBTA_HH_DEV_CB *p_cb, tBTA_HH_MAINT_DEV *p_dev_info); +extern void bta_hh_le_remove_dev_bg_conn(tBTA_HH_DEV_CB *p_cb); +extern void bta_hh_le_open_fail(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data); +extern void bta_hh_gatt_open(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data); +extern void bta_hh_gatt_close(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data); +extern void bta_hh_start_security(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_buf); +extern void bta_hh_start_srvc_discovery(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_buf); +extern void bta_hh_w4_le_read_char_cmpl(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_buf); +extern void bta_hh_le_read_char_cmpl(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_buf); +extern void bta_hh_w4_le_read_descr_cmpl(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_buf); +extern void bta_hh_le_read_descr_cmpl(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_buf); +extern void bta_hh_w4_le_write_cmpl(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_buf); +extern void bta_hh_le_write_cmpl(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_buf); +extern void bta_hh_le_write_char_descr_cmpl(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_buf); +extern void bta_hh_start_security(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_buf); +extern void bta_hh_security_cmpl(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_buf); +extern void bta_hh_le_update_scpp(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_buf); +extern void bta_hh_le_notify_enc_cmpl(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data); +extern void bta_hh_ci_load_rpt (tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_buf); +#endif + +#if BTA_HH_DEBUG +extern void bta_hh_trace_dev_db(void); +#endif + +#endif ///defined(BTA_HH_INCLUDED) && (BTA_HH_INCLUDED == TRUE) +#endif diff --git a/lib/bt/host/bluedroid/bta/include/bta/bta_ag_api.h b/lib/bt/host/bluedroid/bta/include/bta/bta_ag_api.h new file mode 100644 index 00000000..35bb6ab5 --- /dev/null +++ b/lib/bt/host/bluedroid/bta/include/bta/bta_ag_api.h @@ -0,0 +1,630 @@ +/****************************************************************************** + * + * Copyright (C) 2003-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This is the public interface file for the audio gateway (AG) subsystem + * of BTA, Broadcom's Bluetooth application layer for mobile phones. + * + ******************************************************************************/ +#ifndef BTA_AG_API_H +#define BTA_AG_API_H + +#include "bta_api.h" +#include "bta_hfp_defs.h" +#include "esp_hf_defs.h" + +#if (BTA_AG_INCLUDED == TRUE) +/***************************************************************************** +** Constants and data types +*****************************************************************************/ + +/* AG feature masks */ +#define BTA_AG_FEAT_3WAY 0x00000001 /* Three-way calling */ +#define BTA_AG_FEAT_ECNR 0x00000002 /* Echo cancellation and/or noise reduction */ +#define BTA_AG_FEAT_VREC 0x00000004 /* Voice recognition */ +#define BTA_AG_FEAT_INBAND 0x00000008 /* In-band ring tone */ +#define BTA_AG_FEAT_VTAG 0x00000010 /* Attach a phone number to a voice tag */ +#define BTA_AG_FEAT_REJECT 0x00000020 /* Ability to reject incoming call */ +#define BTA_AG_FEAT_ECS 0x00000040 /* Enhanced Call Status */ +#define BTA_AG_FEAT_ECC 0x00000080 /* Enhanced Call Control */ +#define BTA_AG_FEAT_EXTERR 0x00000100 /* Extended error codes */ +#define BTA_AG_FEAT_CODEC 0x00000200 /* Codec Negotiation */ +/* HFP 1.7+ */ +#define BTA_AG_FEAT_HF_IND 0x00000400 /* HF Indicators */ +#define BTA_AG_FEAT_ESCO_S4 0x00000800 /* eSCO S4 Setting Supported */ + +/* Proprietary features: using 31 ~ 16 bits */ +#define BTA_AG_FEAT_BTRH 0x00010000 /* CCAP incoming call hold */ +#define BTA_AG_FEAT_UNAT 0x00020000 /* Pass unknown AT commands to application */ +#define BTA_AG_FEAT_NOSCO 0x00040000 /* No SCO control performed by BTA AG */ +#define BTA_AG_FEAT_NO_ESCO 0x00080000 /* Do not allow or use eSCO */ +#define BTA_AG_FEAT_VOIP 0x00100000 /* VoIP call */ +typedef UINT32 tBTA_AG_FEAT; + +/* HFP peer features */ +#define BTA_AG_PEER_FEAT_ECNR 0x0001 /* Echo cancellation and/or noise reduction */ +#define BTA_AG_PEER_FEAT_3WAY 0x0002 /* Call waiting and three-way calling */ +#define BTA_AG_PEER_FEAT_CLI 0x0004 /* Caller ID presentation capability */ +#define BTA_AG_PEER_FEAT_VREC 0x0008 /* Voice recognition activation */ +#define BTA_AG_PEER_FEAT_VOL 0x0010 /* Remote volume control */ +#define BTA_AG_PEER_FEAT_ECS 0x0020 /* Enhanced Call Status */ +#define BTA_AG_PEER_FEAT_ECC 0x0040 /* Enhanced Call Control */ +#define BTA_AG_PEER_FEAT_CODEC 0x0080 /* Codec Negotiation */ +/* HFP 1.7+ */ +#define BTA_AG_PEER_FEAT_HF_IND 0x0100 /* HF Indicators */ +#define BTA_AG_PEER_FEAT_ESCO_S4 0x0200 /* eSCO S4 Setting Supported */ +typedef UINT16 tBTA_AG_PEER_FEAT; + +/* Proprietary features: using bits after 12 */ +/* Pass unknown AT command responses to application */ +#define BTA_AG_PEER_FEAT_UNAT 0x1000 +#define BTA_AG_PEER_FEAT_VOIP 0x2000 /* VoIP call */ + +/* AG extended call handling - masks not related to any spec */ +#define BTA_AG_CLIENT_CHLD_REL 0x00000001 /* 0 Release waiting call or held calls */ +#define BTA_AG_CLIENT_CHLD_REL_ACC 0x00000002 /* 1 Release active calls and accept other (waiting or held) cal */ +#define BTA_AG_CLIENT_CHLD_REL_X 0x00000004 /* 1x Release x call*/ +#define BTA_AG_CLIENT_CHLD_HOLD_ACC 0x00000008 /* 2 Active calls on hold and accept other call */ +#define BTA_AG_CLIENT_CHLD_PRIV_X 0x00000010 /* 2x Active multiparty call on hold except call x */ +#define BTA_AG_CLIENT_CHLD_MERGE 0x00000020 /* 3 Add held call to multiparty */ +#define BTA_AG_CLIENT_CHLD_MERGE_DETACH 0x00000040 /* 4 Add held call to multiparty */ +typedef UINT16 tBTA_AG_CHLD_FEAT; + +/* HFP peer supported codec masks */ +// TODO(google) This should use common definitions +// in hci/include/hci_audio.h +#define BTA_AG_CODEC_NONE BTM_SCO_CODEC_NONE +#define BTA_AG_CODEC_CVSD BTM_SCO_CODEC_CVSD /* CVSD */ +#define BTA_AG_CODEC_MSBC BTM_SCO_CODEC_MSBC /* mSBC */ +typedef UINT16 tBTA_AG_PEER_CODEC; + +/* AG parse mode */ +#define BTA_AG_PARSE 0 /* Perform AT command parsing in AG */ +#define BTA_AG_PASS_THROUGH 1 /* Pass data directly to phones AT command interpreter */ +typedef UINT8 tBTA_AG_PARSE_MODE; + +/* AG open status */ +#define BTA_AG_SUCCESS 0 /* Connection successfully opened */ +#define BTA_AG_FAIL_SDP 1 /* Open failed due to SDP */ +#define BTA_AG_FAIL_RFCOMM 2 /* Open failed due to RFCOMM */ +#define BTA_AG_FAIL_RESOURCES 3 /* out of resources failure */ +typedef UINT8 tBTA_AG_STATUS; + +/* handle values used with BTA_AgResult */ +#define BTA_AG_HANDLE_NONE 0 +#define BTA_AG_HANDLE_ALL 0xFFFF +/* It is safe to use the same value as BTA_AG_HANDLE_ALL + * HANDLE_ALL is used for delivering indication + * SCO_NO_CHANGE is used for changing sco behavior + * They donot interfere with each other + */ +#define BTA_AG_HANDLE_SCO_NO_CHANGE 0xFFFF + +/* AG result codes used with BTA_AgResult */ +#define BTA_AG_SPK_RES 0 /* Update speaker volume */ +#define BTA_AG_MIC_RES 1 /* Update microphone volume */ +#define BTA_AG_INBAND_RING_RES 2 /* Update inband ring state AT+BSIR */ +#define BTA_AG_CIND_RES 3 /* Send indicator response for AT+CIND */ +#define BTA_AG_BINP_RES 4 /* Send phone number for voice tag for AT+BINP */ +#define BTA_AG_IND_RES 5 /* Update an indicator value +CIEV<...> */ +#define BTA_AG_BVRA_RES 6 /* Update voice recognition state for AT+BVRA */ +#define BTA_AG_CNUM_RES 7 /* Send subscriber number response for AT+CNUM */ +#define BTA_AG_BTRH_RES 8 /* Send CCAP incoming call hold */ +#define BTA_AG_CLCC_RES 9 /* Query list of calls AT+CLCC */ +#define BTA_AG_COPS_RES 10 /* Read network operator for AT+COPS */ +#define BTA_AG_IN_CALL_RES 11 /* Indicate incoming phone call */ +#define BTA_AG_IN_CALL_CONN_RES 12 /* Incoming phone call connected*/ +#define BTA_AG_CALL_WAIT_RES 13 /* Call waiting notification for AT+CCWA */ +#define BTA_AG_OUT_CALL_ORIG_RES 14 /* Outgoing phone call origination AT+ATD*/ +#define BTA_AG_OUT_CALL_ALERT_RES 15 /* Outgoing phone call alerting remote party */ +#define BTA_AG_OUT_CALL_CONN_RES 16 /* Outgoing phone call connected */ +#define BTA_AG_CALL_CANCEL_RES 17 /* Incoming/outgoing 3-way canceled before connected */ +#define BTA_AG_END_CALL_RES 18 /* End call AT+CHUP */ +#define BTA_AG_IN_CALL_HELD_RES 19 /* Incoming call held AT+CHLD */ +#define BTA_AG_UNAT_RES 20 /* Response to unknown AT command event AT+UNAT */ +#define BTA_AG_MULTI_CALL_RES 21 /* SLC at three way call */ +typedef UINT8 tBTA_AG_RES; + +/* AG callback events */ +#define BTA_AG_ENABLE_EVT 0 /* AG enabled */ +#define BTA_AG_REGISTER_EVT 1 /* AG registered */ +#define BTA_AG_OPEN_EVT 2 /* AG connection open */ +#define BTA_AG_CLOSE_EVT 3 /* AG connection closed */ +#define BTA_AG_CONN_EVT 4 /* Service level connection opened */ +#define BTA_AG_AUDIO_OPEN_EVT 5 /* Audio connection open */ +#define BTA_AG_AUDIO_CLOSE_EVT 6 /* Audio connection closed */ +#define BTA_AG_SPK_EVT 7 /* Speaker volume changed */ +#define BTA_AG_MIC_EVT 8 /* Microphone volume changed */ +#define BTA_AG_AT_CKPD_EVT 9 /* CKPD from the HS */ +#define BTA_AG_DISABLE_EVT 30 /* AG disabled */ +#if (BTM_WBS_INCLUDED == TRUE ) +#define BTA_AG_WBS_EVT 31 /* SCO codec nego */ +#endif +#define BTA_AG_AUDIO_MSBC_OPEN_EVT 32 /* Audio connection with mSBC codec open */ + +#define BTA_AG_PKT_NUMS_GET_EVT 33 /* AG packet status nums */ + +/* Values below are for HFP only */ +#define BTA_AG_AT_A_EVT 10 /* Answer a incoming call */ +#define BTA_AG_AT_D_EVT 11 /* Place a call using number or memory dial */ +#define BTA_AG_AT_CHLD_EVT 12 /* Call hold */ +#define BTA_AG_AT_CHUP_EVT 13 /* Hang up a call */ +#define BTA_AG_AT_CIND_EVT 14 /* Read indicator settings */ +#define BTA_AG_AT_VTS_EVT 15 /* Transmit DTMF tone */ +#define BTA_AG_AT_BINP_EVT 16 /* Retrieve number from voice tag */ +#define BTA_AG_AT_BLDN_EVT 17 /* Place call to last dialed number */ +#define BTA_AG_AT_BVRA_EVT 18 /* Enable/disable voice recognition */ +#define BTA_AG_AT_NREC_EVT 19 /* Disable echo canceling */ +#define BTA_AG_AT_CNUM_EVT 20 /* Retrieve subscriber number */ +#define BTA_AG_AT_BTRH_EVT 21 /* CCAP-style incoming call hold */ +#define BTA_AG_AT_CLCC_EVT 22 /* Query list of current calls */ +#define BTA_AG_AT_COPS_EVT 23 /* Query Current Operator Name on AG */ +#define BTA_AG_AT_UNAT_EVT 24 /* Unknown AT command */ +#define BTA_AG_AT_CBC_EVT 25 /* Indicator Update */ +#define BTA_AG_AT_BAC_EVT 26 /* avablable codec */ +#define BTA_AG_AT_BCS_EVT 27 /* Codec select */ +typedef UINT8 tBTA_AG_EVT; + +/* HFP errcode - Set when BTA_AG_OK_ERROR is returned in 'ok_flag' */ +#define BTA_AG_ERR_PHONE_FAILURE 0 /* Phone Failure */ +#define BTA_AG_ERR_NO_CONN_PHONE 1 /* No connection to phone */ +#define BTA_AG_ERR_OP_NOT_ALLOWED 3 /* Operation not allowed */ +#define BTA_AG_ERR_OP_NOT_SUPPORTED 4 /* Operation not supported */ +#define BTA_AG_ERR_PHSIM_PIN_REQ 5 /* PH-SIM PIN required */ +#define BTA_AG_ERR_SIM_NOT_INSERTED 10 /* SIM not inserted */ +#define BTA_AG_ERR_SIM_PIN_REQ 11 /* SIM PIN required */ +#define BTA_AG_ERR_SIM_PUK_REQ 12 /* SIM PUK required */ +#define BTA_AG_ERR_SIM_FAILURE 13 /* SIM failure */ +#define BTA_AG_ERR_SIM_BUSY 14 /* SIM busy */ +#define BTA_AG_ERR_INCORRECT_PWD 16 /* Incorrect password */ +#define BTA_AG_ERR_SIM_PIN2_REQ 17 /* SIM PIN2 required */ +#define BTA_AG_ERR_SIM_PUK2_REQ 18 /* SIM PUK2 required */ +#define BTA_AG_ERR_MEMORY_FULL 20 /* Memory full */ +#define BTA_AG_ERR_INVALID_INDEX 21 /* Invalid index */ +#define BTA_AG_ERR_MEMORY_FAILURE 23 /* Memory failure */ +#define BTA_AG_ERR_TEXT_TOO_LONG 24 /* Text string too long */ +#define BTA_AG_ERR_INV_CHAR_IN_TSTR 25 /* Invalid characters in text string */ +#define BTA_AG_ERR_DSTR_TOO_LONG 26 /* Dial string too long */ +#define BTA_AG_ERR_INV_CHAR_IN_DSTR 27 /* Invalid characters in dial string */ +#define BTA_AG_ERR_NO_NETWORK_SERV 30 /* No network service */ +#define BTA_AG_ERR_NETWORK_TIME_OUT 31 /* Network timeout */ +#define BTA_AG_ERR_NO_NET_EMG_ONLY 32 /* Network not allowed - emergency service only */ +#define BTA_AG_ERR_VOIP_CS_CALLS 33 /* AG cannot create simultaneous VoIP and CS calls */ +#define BTA_AG_ERR_NOT_FOR_VOIP 34 /* Not supported on this call type(VoIP) */ +#define BTA_AG_ERR_SIP_RESP_CODE 35 /* SIP 3 digit response code */ +typedef UINT8 tBTA_AG_ERR_TYPE; + +#if 0 /* Not Used in Bluetooth HFP 1.5 Specification */ +#define BTA_AG_ERR_PHADAP_LNK_RES 2 /* Phone-adapter link reserved */ +#define BTA_AG_ERR_PHFSIM_PIN_REQ 6 /* PH-FSIM PIN required */ +#define BTA_AG_ERR_PHFSIM_PUK_REQ 7 /* PH-FSIM PUK required */ +#define BTA_AG_ERR_SIM_WRONG 15 /* SIM wrong */ +#define BTA_AG_ERR_NOT_FOUND 22 /* Not found */ +#define BTA_AG_ERR_NETWORK_TIMEOUT 31 /* Network timeout */ +#define BTA_AG_ERR_NET_PIN_REQ 40 /* Network personalization PIN required */ +#define BTA_AG_ERR_NET_PUK_REQ 41 /* Network personalization PUK required */ +#define BTA_AG_ERR_SUBSET_PIN_REQ 42 /* Network subset personalization PIN required */ +#define BTA_AG_ERR_SUBSET_PUK_REQ 43 /* Network subset personalization PUK required */ +#define BTA_AG_ERR_SERVPRO_PIN_REQ 44 /* Service provider personalization PIN required */ +#define BTA_AG_ERR_SERVPRO_PUK_REQ 45 /* Service provider personalization PUK required */ +#define BTA_AG_ERR_CORP_PIN_REQ 46 /* Corporate personalization PIN required */ +#define BTA_AG_ERR_CORP_PUK_REQ 47 /* Corporate personalization PUK required */ +#define BTA_AG_ERR_UNKNOWN 100 /* Unknown error */ +/* GPRS-related errors */ +#define BTA_AG_ERR_ILL_MS 103 /* Illegal MS (#3) */ +#define BTA_AG_ERR_ILL_ME 106 /* Illegal ME (#6) */ +#define BTA_AG_ERR_GPRS_NOT_ALLOWED 107 /* GPRS services not allowed (#7) */ +#define BTA_AG_ERR_PLMN_NOT_ALLOWED 111 /* PLMN services not allowed (#11) */ +#define BTA_AG_ERR_LOC_NOT_ALLOWED 112 /* Location area not allowed (#12) */ +#define BTA_AG_ERR_ROAM_NOT_ALLOWED 113 /* Roaming not allowed in this location area (#13) */ +/* Errors related to a failure to Activate a Context */ +#define BTA_AG_ERR_OPT_NOT_SUPP 132 /* Service option not supported (#32) */ +#define BTA_AG_ERR_OPT_NOT_SUBSCR 133 /* Requested service option not subscribed (#33) */ +#define BTA_AG_ERR_OPT_OUT_OF_ORDER 134 /* Service option temporarily out of order (#34) */ +#define BTA_AG_ERR_PDP_AUTH_FAILURE 149 /* PDP authentication failure */ +/* Other GPRS errors */ +#define BTA_AG_ERR_INV_MOBILE_CLASS 150 /* Invalid mobile class */ +#define BTA_AG_ERR_UNSPEC_GPRS_ERR 148 /* Unspecified GPRS error */ +#endif /* Unused error codes */ + +/* HFP result data 'ok_flag' */ +#define BTA_AG_OK_CONTINUE 0 /* Send out response (more responses coming) */ +#define BTA_AG_OK_DONE 1 /* Send out response followed by OK (finished) */ +#define BTA_AG_OK_ERROR 2 /* Error response */ +typedef UINT8 tBTA_AG_AT_RESULT_TYPE; + +/* BTRH values */ +#define BTA_AG_BTRH_SET_HOLD 0 /* Put incoming call on hold */ +#define BTA_AG_BTRH_SET_ACC 1 /* Accept incoming call on hold */ +#define BTA_AG_BTRH_SET_REJ 2 /* Reject incoming call on hold */ +#define BTA_AG_BTRH_READ 3 /* Read the current value */ +#define BTA_AG_BTRH_NO_RESP 4 /* Not in RH States (reply to read) */ +typedef UINT8 tBTA_AG_BTRH_TYPE; + +/* ASCII character string of arguments to the AT command or result */ +#ifndef BTA_AG_AT_MAX_LEN +#define BTA_AG_AT_MAX_LEN 256 +#endif + +/* indicator constants HFP 1.1 and later */ +#define BTA_AG_IND_CALL 1 /* position of call indicator */ +#define BTA_AG_IND_CALLSETUP 2 /* position of callsetup indicator */ +#define BTA_AG_IND_SERVICE 3 /* position of service indicator */ +/* indicator constants HFP 1.5 and later */ +#define BTA_AG_IND_SIGNAL 4 /* position of signal strength indicator */ +#define BTA_AG_IND_ROAM 5 /* position of roaming indicator */ +#define BTA_AG_IND_BATTCHG 6 /* position of battery charge indicator */ +#define BTA_AG_IND_CALLHELD 7 /* position of callheld indicator */ +#define BTA_AG_IND_BEARER 8 /* position of bearer indicator */ +typedef UINT16 tBTA_AG_IND_TYPE; + +/* call indicator values */ +#define BTA_AG_CALL_INACTIVE 0 /* Phone call inactive */ +#define BTA_AG_CALL_ACTIVE 1 /* Phone call active */ +/* callsetup indicator values */ +#define BTA_AG_CALLSETUP_NONE 0 /* Not currently in call set up */ +#define BTA_AG_CALLSETUP_INCOMING 1 /* Incoming call process ongoing */ +#define BTA_AG_CALLSETUP_OUTGOING 2 /* Outgoing call set up is ongoing */ +#define BTA_AG_CALLSETUP_ALERTING 3 /* Remote party being alerted in an outgoing call */ +/* service indicator values */ +#define BTA_AG_SERVICE_NONE 0 /* Neither CS nor VoIP service is available */ +#define BTA_AG_SERVICE_CS 1 /* Only CS service is available */ +#define BTA_AG_SERVICE_VOIP 2 /* Only VoIP service is available */ +#define BTA_AG_SERVICE_CS_VOIP 3 /* Both CS and VoIP services available */ +/* callheld indicator values */ +#define BTA_AG_CALLHELD_INACTIVE 0 /* No held calls */ +#define BTA_AG_CALLHELD_ACTIVE 1 /* Call held and call active */ +#define BTA_AG_CALLHELD_NOACTIVE 2 /* Call held and no call active */ +/* signal strength indicator values */ +#define BTA_AG_ROAMING_INACTIVE 0 /* Phone call inactive */ +#define BTA_AG_ROAMING_ACTIVE 1 /* Phone call active */ +/* bearer indicator values */ +#define BTA_AG_BEARER_WLAN 0 /* WLAN */ +#define BTA_AG_BEARER_BLUETOOTH 1 /* Bluetooth */ +#define BTA_AG_BEARER_WIRED 2 /* Wired */ +#define BTA_AG_BEARER_2G3G 3 /* 2G 3G */ +#define BTA_AG_BEARER_WIMAX 4 /* WIMAX */ +#define BTA_AG_BEARER_RES1 5 /* Reserved */ +#define BTA_AG_BEARER_RES2 6 /* Reserved */ +#define BTA_AG_BEARER_RES3 7 /* Reserved */ + +/* data associated with BTA_AG_IND_RES */ +typedef struct +{ + tBTA_AG_IND_TYPE type; + UINT16 value; +} tBTA_AG_IND; + +/* data type for BTA_AgResult() */ +typedef struct +{ + char str[BTA_AG_AT_MAX_LEN+1]; /* used for cops,clcc,cnum... */ + tBTA_AG_IND ind; /* used for indicator type */ + UINT16 num; /* used for codec state */ + UINT16 audio_handle; /* used for audio path */ + UINT16 errcode; /* Valid only if 'ok_flag' is set to BTA_AG_OK_ERROR */ + UINT8 ok_flag; /* Indicates if response is finished, and if error occurred */ + BOOLEAN state; +} tBTA_AG_RES_DATA; + +/* data associated with most non-AT events */ +typedef struct +{ + UINT16 handle; + UINT8 app_id; + tBTA_AG_STATUS status; + UINT16 sync_conn_handle; +} tBTA_AG_HDR; + +/* data associated with BTA_AG_REGISTER_EVT */ +typedef struct +{ + tBTA_AG_HDR hdr; +} tBTA_AG_REGISTER; + +/* data associated with BTA_AG_OPEN_EVT */ +typedef struct +{ + tBTA_AG_HDR hdr; + BD_ADDR bd_addr; + tBTA_SERVICE_ID service_id; +} tBTA_AG_OPEN; + +/* data associated with BTA_AG_CLOSE_EVT */ +typedef struct +{ + tBTA_AG_HDR hdr; + BD_ADDR bd_addr; +} tBTA_AG_CLOSE; + +/* data associated with BTA_AG_CONN_EVT */ +typedef struct +{ + tBTA_AG_HDR hdr; + tBTA_AG_PEER_FEAT peer_feat; + BD_ADDR bd_addr; + tBTA_AG_PEER_CODEC peer_codec; + tBTA_AG_CHLD_FEAT chld_feat; +} tBTA_AG_CONN; + +/* data associated with AT command event */ +typedef struct +{ + tBTA_AG_HDR hdr; + BD_ADDR bd_addr; + char str[BTA_AG_AT_MAX_LEN+1]; + UINT16 num; /* voice recognition state*/ + UINT8 idx; /* call number used by CLCC and CHLD */ + UINT16 value; +} tBTA_AG_VAL; + +/* data associated with BTA_AG_CLIP_EVT and BTA_AG_CCWA_EVT*/ +#define BTA_AG_NUMBER_LEN 32 +typedef struct { + char number[BTA_AG_NUMBER_LEN + 1]; +} tBTA_AG_NUMBER; + +/* data associated with BTA_HF_CLIENT_OPERATOR_NAME_EVT */ +#define BTA_AG_COPS_LEN 16 +typedef struct { + char name[BTA_AG_COPS_LEN + 1]; +} tBTA_AG_COPS; + +/* data associated with BTA_AG_AT_RESULT_EVT event */ +typedef struct { + tBTA_AG_AT_RESULT_TYPE type; + UINT16 cme; +} tBTA_AG_AT_RESULT; + +/* data associated with BTA_AG_CLCC_EVT event */ +typedef struct { + UINT32 idx; + BOOLEAN inc; + UINT8 status; + BOOLEAN mpty; + BOOLEAN number_present; + char number[BTA_AG_NUMBER_LEN + 1]; +} tBTA_AG_CLCC; + +/* data associated with BTA_AG_CNUM_EVT event */ +typedef struct { + UINT16 service; + char number[BTA_AG_NUMBER_LEN + 1]; +} tBTA_AG_CNUM; + +/* data associated with BTA_HF_CLIENT_PKT_STAT_NUMS_GET_EVT */ +typedef struct { + UINT32 rx_total; + UINT32 rx_correct; + UINT32 rx_err; + UINT32 rx_none; + UINT32 rx_lost; + UINT32 tx_total; + UINT32 tx_discarded; +} tBTA_AG_PKT_STAT_NUMS; + +/* union of data associated with AG callback */ +typedef union +{ + tBTA_AG_HDR hdr; + tBTA_AG_REGISTER reg; + tBTA_AG_OPEN open; + tBTA_AG_CLOSE close; + tBTA_AG_CONN conn; + tBTA_AG_IND ind; + tBTA_AG_VAL val; + //add + tBTA_AG_COPS operator; + tBTA_AG_NUMBER number; + tBTA_AG_AT_RESULT result; + tBTA_AG_CLCC clcc; + tBTA_AG_CNUM cnum; + tBTA_AG_PKT_STAT_NUMS pkt_num; +} tBTA_AG; + +/* AG callback */ +typedef void (tBTA_AG_CBACK)(tBTA_AG_EVT event, tBTA_AG *p_data); + +/* AG configuration structure */ +typedef struct +{ + char *cind_info; + INT32 conn_tout; + UINT16 sco_pkt_types; + char *chld_val_ecc; + char *chld_val; +} tBTA_AG_CFG; + +#ifdef __cplusplus +extern "C" +{ +#endif + +/***************************************************************************** +** External Function Declarations +*****************************************************************************/ + +/******************************************************************************* +** +** Function BTA_AgEnable +** +** Description Enable the audio gateway service. When the enable +** operation is complete the callback function will be +** called with a BTA_AG_ENABLE_EVT. This function must +** be called before other function in the AG API are +** called. +** +** Returns BTA_SUCCESS if OK, BTA_FAILURE otherwise. +** +*******************************************************************************/ +tBTA_STATUS BTA_AgEnable(tBTA_AG_PARSE_MODE parse_mode, tBTA_AG_CBACK *p_cback); + +/******************************************************************************* +** +** Function BTA_AgDisable +** +** Description Disable the audio gateway service +** +** +** Returns void +** +*******************************************************************************/ +void BTA_AgDisable(void); + +/******************************************************************************* +** +** Function BTA_AgRegister +** +** Description Register an Audio Gateway service. +** +** +** Returns void +** +*******************************************************************************/ +void BTA_AgRegister(tBTA_SERVICE_MASK services, tBTA_SEC sec_mask, + tBTA_AG_FEAT features, char *p_service_names[], UINT8 app_id); + +/******************************************************************************* +** +** Function BTA_AgDeregister +** +** Description Deregister an audio gateway service. +** +** +** Returns void +** +*******************************************************************************/ +void BTA_AgDeregister(UINT16 handle); + +/******************************************************************************* +** +** Function BTA_AgOpen +** +** Description Opens a connection to a headset or hands-free device. +** When connection is open callback function is called +** with a BTA_AG_OPEN_EVT. Only the data connection is +** opened. The audio connection is not opened. +** +** +** Returns void +** +*******************************************************************************/ +void BTA_AgOpen(UINT16 handle, BD_ADDR bd_addr, tBTA_SEC sec_mask, tBTA_SERVICE_MASK services); + +/******************************************************************************* +** +** Function BTA_AgClose +** +** Description Close the current connection to a headset or a handsfree +** Any current audio connection will also be closed +** +** +** Returns void +** +*******************************************************************************/ +void BTA_AgClose(UINT16 handle); + +/******************************************************************************* +** +** Function BTA_AgAudioOpen +** +** Description Opens an audio connection to the currently connected +** headset or hnadsfree +** +** +** Returns void +** +*******************************************************************************/ +void BTA_AgAudioOpen(UINT16 handle); + +/******************************************************************************* +** +** Function BTA_AgAudioClose +** +** Description Close the currently active audio connection to a headset +** or hnadsfree. The data connection remains open +** +** +** Returns void +** +*******************************************************************************/ +void BTA_AgAudioClose(UINT16 handle); + +/******************************************************************************* +** +** Function BTA_AgResult +** +** Description Send an AT result code to a headset or hands-free device. +** This function is only used when the AG parse mode is set +** to BTA_AG_PARSE. +** +** +** Returns void +** +*******************************************************************************/ +void BTA_AgResult(UINT16 handle, tBTA_AG_RES result, tBTA_AG_RES_DATA *p_data); + +/******************************************************************************* +** +** Function BTA_AgSetCodec +** +** Description Specify the codec type to be used for the subsequent +** audio connection. +** +** +** +** Returns void +** +*******************************************************************************/ +void BTA_AgSetCodec(UINT16 handle, tBTA_AG_PEER_CODEC codec); + + +#if (BTM_SCO_HCI_INCLUDED == TRUE ) +/******************************************************************************* +** +** Function BTA_AgPktStatsNumsGet +** +** Description Get the Number of packets status received +** +** +** Returns void +** +*******************************************************************************/ +void BTA_AgPktStatsNumsGet(UINT16 handle, UINT16 sync_conn_handle); + +/******************************************************************************* +** +** Function BTA_AgCiData +** +** Description Give an EVT to BTA that tell outgoing data is ready. +** +** +** Returns void +** +*******************************************************************************/ +void BTA_AgCiData(UINT16 handle); +#endif /*#if (BTM_SCO_HCI_INCLUDED == TRUE ) */ + +#ifdef __cplusplus +} +#endif + +#endif /* #if (BTA_AG_INCLUDED == TRUE) */ + +#endif /* BTA_HF_API_H */ diff --git a/lib/bt/host/bluedroid/bta/include/bta/bta_ag_co.h b/lib/bt/host/bluedroid/bta/include/bta/bta_ag_co.h new file mode 100644 index 00000000..932ecddd --- /dev/null +++ b/lib/bt/host/bluedroid/bta/include/bta/bta_ag_co.h @@ -0,0 +1,163 @@ +/****************************************************************************** + * + * Copyright (C) 2003-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This is the interface file for audio gateway call-out and call-in functions. + * + ******************************************************************************/ +#ifndef BTA_AG_CO_H +#define BTA_AG_CO_H + +#include "bta/bta_ag_api.h" +#include "hci/hci_audio.h" + +#if (BTA_AG_INCLUDED == TRUE) +#if (BTM_SCO_HCI_INCLUDED == TRUE) +/******************************************************************************* +** +** Function bta_ag_sco_audio_state +** +** Description This function is called by the AG before the audio connection +** is brought up, after it comes up, and after it goes down. +** +** Parameters handle - handle of the AG instance +** state - Audio state +** codec - if WBS support is compiled in, codec to going to be used is provided +** and when in SCO_STATE_SETUP, BTM_I2SPCMConfig() must be called with +** the correct platform parameters. +** in the other states codec type should not be ignored +** +** Returns void +** +*******************************************************************************/ +#if (BTM_WBS_INCLUDED == TRUE) +void bta_ag_sco_audio_state(UINT16 handle, UINT8 app_id, UINT8 state, tBTA_AG_PEER_CODEC codec); +#else +void bta_ag_sco_audio_state(UINT16 handle, UINT8 app_id, UINT8 state); +#endif + +/******************************************************************************* +** +** Function bta_ag_sco_co_init +** +** Description Set default data path for SCO/eSCO. +** This callout function is executed by AG when it is +** started by calling BTA_AgEnable(). This function can be +** used by the phone to initialize audio paths or for other +** initialization purposes. +** +** +** Returns Void. +** +*******************************************************************************/ +tBTA_HFP_SCO_ROUTE_TYPE bta_ag_sco_co_init(UINT32 rx_bw, UINT32 tx_bw, tBTA_HFP_CODEC_INFO *p_codec_info, UINT8 app_id); + +/******************************************************************************* +** +** Function bta_ag_sco_co_open +** +** Description This function is executed by AG when a service level connection +** is opened. +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_sco_co_open(UINT16 handle, tBTM_SCO_AIR_MODE_TYPE air_mode, UINT8 inout_pkt_size, UINT16 event); + +/******************************************************************************* +** +** Function bta_ag_sco_co_close +** +** Description This function is called by AG when a service level +** connection is closed. +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_sco_co_close(void); + +/******************************************************************************* +** +** Function bta_ag_sco_co_out_data +** +** Description This function is called to send SCO data over HCI. +** +** Returns number of bytes got from application +** +*******************************************************************************/ +uint32_t bta_ag_sco_co_out_data(UINT8 *p_buf); + +/******************************************************************************* +** +** Function bta_hf_client_sco_co_in_data +** +** Description This function is called to send incoming SCO data to application. +** +** Returns void +** +*******************************************************************************/ +void bta_ag_sco_co_in_data(BT_HDR *p_buf, tBTM_SCO_DATA_FLAG status); + +/******************************************************************************* +** +** Function bta_ag_co_tx_write +** +** Description This function is called by the AG to send data to the +** phone when the AG is configured for AT command pass-through. +** The implementation of this function must copy the data to +** the phones memory. +** +** Returns void +** +*******************************************************************************/ +void bta_ag_co_tx_write(UINT16 handle, UINT8 *p_data, UINT16 len); + +/******************************************************************************* +** +** Function bta_ag_ci_rx_write +** +** Description This function is called to send data to the AG when the AG +** is configured for AT command pass-through. The function +** copies data to an event buffer and sends it. +** +** Returns void +** +*******************************************************************************/ +extern void bta_ag_ci_rx_write(UINT16 handle, char *p_data, UINT16 len); + +/****************************************************************************** +** +** Function bta_ag_ci_slc_ready +** +** Description This function is called to notify AG that SLC is up at +** the application. This funcion is only used when the app +** is running in pass-through mode. +** +** Returns void +** +******************************************************************************/ +extern void bta_ag_ci_slc_ready(UINT16 handle); + +#endif /* #if (BTM_SCO_HCI_INCLUDED == TRUE) */ + +#endif /* #if (BTA_AG_INCLUDED == TRUE) */ + +#endif /* BTA_AG_CO_H */ diff --git a/lib/bt/host/bluedroid/bta/include/bta/bta_api.h b/lib/bt/host/bluedroid/bta/include/bta/bta_api.h new file mode 100644 index 00000000..8ba75884 --- /dev/null +++ b/lib/bt/host/bluedroid/bta/include/bta/bta_api.h @@ -0,0 +1,3155 @@ +/****************************************************************************** + * + * Copyright (C) 2003-2014 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This is the public interface file for BTA, Broadcom's Bluetooth + * application layer for mobile phones. + * + ******************************************************************************/ +#ifndef BTA_API_H +#define BTA_API_H + +#include "common/bt_target.h" +#include "stack/bt_types.h" +#include "stack/btm_api.h" +// #include "uipc_msg.h" +#include "stack/sdp_api.h" + +// #if BLE_INCLUDED == TRUE +#include "stack/btm_ble_api.h" +// #endif + +/***************************************************************************** +** Constants and data types +*****************************************************************************/ + +/* Status Return Value */ +#define BTA_SUCCESS 0 /* Successful operation. */ +#define BTA_FAILURE 1 /* Generic failure. */ +#define BTA_PENDING 2 /* API cannot be completed right now */ +#define BTA_BUSY 3 +#define BTA_NO_RESOURCES 4 +#define BTA_WRONG_MODE 5 +#define BTA_EIR_TOO_LARGE 6 + +typedef UINT8 tBTA_STATUS; + +/* + * Service ID + * + * NOTES: When you add a new Service ID for BTA AND require to change the value of BTA_MAX_SERVICE_ID, + * make sure that the correct security ID of the new service from Security service definitions (stack/btm_api.h) + * should be added to bta_service_id_to_btm_srv_id_lkup_tbl table in bta_dm_act.c. + */ + +#define BTA_RES_SERVICE_ID 0 /* Reserved */ +#define BTA_SPP_SERVICE_ID 1 /* Serial port profile. */ +#define BTA_DUN_SERVICE_ID 2 /* Dial-up networking profile. */ +#define BTA_A2DP_SOURCE_SERVICE_ID 3 /* A2DP Source profile. */ +#define BTA_LAP_SERVICE_ID 4 /* LAN access profile. */ +#define BTA_HSP_SERVICE_ID 5 /* Headset profile. */ +#define BTA_HFP_SERVICE_ID 6 /* Hands-free profile. */ +#define BTA_OPP_SERVICE_ID 7 /* Object push */ +#define BTA_FTP_SERVICE_ID 8 /* File transfer */ +#define BTA_CTP_SERVICE_ID 9 /* Cordless Terminal */ +#define BTA_ICP_SERVICE_ID 10 /* Intercom Terminal */ +#define BTA_SYNC_SERVICE_ID 11 /* Synchronization */ +#define BTA_BPP_SERVICE_ID 12 /* Basic printing profile */ +#define BTA_BIP_SERVICE_ID 13 /* Basic Imaging profile */ +#define BTA_PANU_SERVICE_ID 14 /* PAN User */ +#define BTA_NAP_SERVICE_ID 15 /* PAN Network access point */ +#define BTA_GN_SERVICE_ID 16 /* PAN Group Ad-hoc networks */ +#define BTA_SAP_SERVICE_ID 17 /* SIM Access profile */ +#define BTA_A2DP_SINK_SERVICE_ID 18 /* A2DP Sink */ +#define BTA_AVRCP_SERVICE_ID 19 /* A/V remote control */ +#define BTA_HID_SERVICE_ID 20 /* HID Host*/ +#define BTA_VDP_SERVICE_ID 21 /* Video distribution */ +#define BTA_PBAP_SERVICE_ID 22 /* PhoneBook Access Server*/ +#define BTA_HSP_HS_SERVICE_ID 23 /* HFP HS role */ +#define BTA_HFP_HS_SERVICE_ID 24 /* HSP HS role */ +#define BTA_MAP_SERVICE_ID 25 /* Message Access Profile */ +#define BTA_MN_SERVICE_ID 26 /* Message Notification Service */ +#define BTA_HDP_SERVICE_ID 27 /* Health Device Profile */ +#define BTA_PCE_SERVICE_ID 28 /* PhoneBook Access Client*/ +#define BTA_SDP_SERVICE_ID 29 /* SDP Search*/ +#if BLE_INCLUDED == TRUE && BTA_GATT_INCLUDED == TRUE +/* BLE profile service ID */ +#define BTA_BLE_SERVICE_ID 30 /* GATT profile */ + +// btla-specific ++ +#define BTA_USER_SERVICE_ID 31 /* User requested UUID */ + +#define BTA_MAX_SERVICE_ID 32 +// btla-specific -- +#else +#define BTA_USER_SERVICE_ID 30 /* User requested UUID */ +#define BTA_MAX_SERVICE_ID 31 +#endif +/* service IDs (BTM_SEC_SERVICE_FIRST_EMPTY + 1) to (BTM_SEC_MAX_SERVICES - 1) + * are used by BTA JV */ +#define BTA_FIRST_JV_SERVICE_ID (BTM_SEC_SERVICE_FIRST_EMPTY + 1) +#define BTA_LAST_JV_SERVICE_ID (BTM_SEC_MAX_SERVICES - 1) + +typedef UINT8 tBTA_SERVICE_ID; + +/* Service ID Mask */ +#define BTA_RES_SERVICE_MASK 0x00000001 /* Reserved */ +#define BTA_SPP_SERVICE_MASK 0x00000002 /* Serial port profile. */ +#define BTA_DUN_SERVICE_MASK 0x00000004 /* Dial-up networking profile. */ +#define BTA_FAX_SERVICE_MASK 0x00000008 /* Fax profile. */ +#define BTA_LAP_SERVICE_MASK 0x00000010 /* LAN access profile. */ +#define BTA_HSP_SERVICE_MASK 0x00000020 /* HSP AG role. */ +#define BTA_HFP_SERVICE_MASK 0x00000040 /* HFP AG role */ +#define BTA_OPP_SERVICE_MASK 0x00000080 /* Object push */ +#define BTA_FTP_SERVICE_MASK 0x00000100 /* File transfer */ +#define BTA_CTP_SERVICE_MASK 0x00000200 /* Cordless Terminal */ +#define BTA_ICP_SERVICE_MASK 0x00000400 /* Intercom Terminal */ +#define BTA_SYNC_SERVICE_MASK 0x00000800 /* Synchronization */ +#define BTA_BPP_SERVICE_MASK 0x00001000 /* Print server */ +#define BTA_BIP_SERVICE_MASK 0x00002000 /* Basic Imaging */ +#define BTA_PANU_SERVICE_MASK 0x00004000 /* PAN User */ +#define BTA_NAP_SERVICE_MASK 0x00008000 /* PAN Network access point */ +#define BTA_GN_SERVICE_MASK 0x00010000 /* PAN Group Ad-hoc networks */ +#define BTA_SAP_SERVICE_MASK 0x00020000 /* PAN Group Ad-hoc networks */ +#define BTA_A2DP_SERVICE_MASK 0x00040000 /* Advanced audio distribution */ +#define BTA_AVRCP_SERVICE_MASK 0x00080000 /* A/V remote control */ +#define BTA_HID_SERVICE_MASK 0x00100000 /* HID */ +#define BTA_VDP_SERVICE_MASK 0x00200000 /* Video distribution */ +#define BTA_PBAP_SERVICE_MASK 0x00400000 /* Phone Book Server */ +#define BTA_HSP_HS_SERVICE_MASK 0x00800000 /* HFP HS role */ +#define BTA_HFP_HS_SERVICE_MASK 0x01000000 /* HSP HS role */ +#define BTA_MAS_SERVICE_MASK 0x02000000 /* Message Access Profile */ +#define BTA_MN_SERVICE_MASK 0x04000000 /* Message Notification Profile */ +#define BTA_HL_SERVICE_MASK 0x08000000 /* Health Device Profile */ +#define BTA_PCE_SERVICE_MASK 0x10000000 /* Phone Book Client */ + +#if BLE_INCLUDED == TRUE && BTA_GATT_INCLUDED == TRUE +#define BTA_BLE_SERVICE_MASK 0x20000000 /* GATT based service */ +// btla-specific ++ +#define BTA_USER_SERVICE_MASK 0x40000000 /* Message Notification Profile */ +// btla-specific -- +#else +// btla-specific ++ +#define BTA_USER_SERVICE_MASK 0x20000000 /* Message Notification Profile */ +// btla-specific -- +#endif + +#if BLE_INCLUDED == TRUE && BTA_GATT_INCLUDED == TRUE +#define BTA_ALL_SERVICE_MASK 0x3FFFFFFF /* All services supported by BTA. */ +#else +#define BTA_ALL_SERVICE_MASK 0x1FFFFFFF /* All services supported by BTA. */ +#endif + +typedef UINT32 tBTA_SERVICE_MASK; + +/* extended service mask, including mask with one or more GATT UUID */ +typedef struct { + tBTA_SERVICE_MASK srvc_mask; + UINT8 num_uuid; + tBT_UUID *p_uuid; +} tBTA_SERVICE_MASK_EXT; + +/* Security Setting Mask */ +#define BTA_SEC_NONE BTM_SEC_NONE /* No security. */ +#define BTA_SEC_AUTHORIZE (BTM_SEC_IN_AUTHORIZE ) /* Authorization required (only needed for out going connection )*/ +#define BTA_SEC_AUTHENTICATE (BTM_SEC_IN_AUTHENTICATE | BTM_SEC_OUT_AUTHENTICATE) /* Authentication required. */ +#define BTA_SEC_ENCRYPT (BTM_SEC_IN_ENCRYPT | BTM_SEC_OUT_ENCRYPT) /* Encryption required. */ +#define BTA_SEC_MODE4_LEVEL4 (BTM_SEC_MODE4_LEVEL4) /* Mode 4 level 4 service, i.e. incoming/outgoing MITM and P-256 encryption */ +#define BTA_SEC_MITM (BTM_SEC_IN_MITM | BTM_SEC_OUT_MITM) /* Man-In-The_Middle protection */ +#define BTA_SEC_IN_16_DIGITS (BTM_SEC_IN_MIN_16_DIGIT_PIN) /* Min 16 digit for pin code */ + +typedef UINT16 tBTA_SEC; + +typedef tBTM_GET_DEV_NAME_CBACK tBTA_GET_DEV_NAME_CBACK; + +/* Ignore for Discoverable, Connectable, Pairable and Connectable Paired only device modes */ +#define BTA_DM_IGNORE 0x00FF + +/* Ignore for Discoverable, Connectable only for LE modes */ +#define BTA_DM_LE_IGNORE 0xFF00 + +#define BTA_APP_ID_1 1 /* PM example profile 1 */ +#define BTA_APP_ID_PAN_MULTI 0xFE /* app id for pan multiple connection */ +#define BTA_ALL_APP_ID 0xFF + +/* Discoverable Modes */ +#define BTA_DM_NON_DISC BTM_NON_DISCOVERABLE /* Device is not discoverable. */ +#define BTA_DM_GENERAL_DISC BTM_GENERAL_DISCOVERABLE /* General discoverable. */ +#define BTA_DM_LIMITED_DISC BTM_LIMITED_DISCOVERABLE /* Limited discoverable. */ +#if ((defined BLE_INCLUDED) && (BLE_INCLUDED == TRUE)) +#define BTA_DM_BLE_NON_DISCOVERABLE BTM_BLE_NON_DISCOVERABLE /* Device is not LE discoverable */ +#define BTA_DM_BLE_GENERAL_DISCOVERABLE BTM_BLE_GENERAL_DISCOVERABLE /* Device is LE General discoverable */ +#define BTA_DM_BLE_LIMITED_DISCOVERABLE BTM_BLE_LIMITED_DISCOVERABLE /* Device is LE Limited discoverable */ +#endif +typedef UINT16 tBTA_DM_DISC; /* this discoverability mode is a bit mask among BR mode and LE mode */ + +/* Connectable Modes */ +#define BTA_DM_NON_CONN BTM_NON_CONNECTABLE /* Device is not connectable. */ +#define BTA_DM_CONN BTM_CONNECTABLE /* Device is connectable. */ +#if ((defined BLE_INCLUDED) && (BLE_INCLUDED == TRUE)) +#define BTA_DM_BLE_NON_CONNECTABLE BTM_BLE_NON_CONNECTABLE /* Device is LE non-connectable. */ +#define BTA_DM_BLE_CONNECTABLE BTM_BLE_CONNECTABLE /* Device is LE connectable. */ +#endif + +// btla-specific ++ +typedef UINT16 tBTA_DM_CONN; + +#define BTA_TRANSPORT_UNKNOWN 0 +#define BTA_TRANSPORT_BR_EDR BT_TRANSPORT_BR_EDR +#define BTA_TRANSPORT_LE BT_TRANSPORT_LE +typedef tBT_TRANSPORT tBTA_TRANSPORT; + +/* Pairable Modes */ +#define BTA_DM_PAIRABLE 1 +#define BTA_DM_NON_PAIRABLE 0 + +/* Connectable Paired Only Mode */ +#define BTA_DM_CONN_ALL 0 +#define BTA_DM_CONN_PAIRED 1 + +/* Inquiry Modes */ +#define BTA_DM_INQUIRY_NONE BTM_INQUIRY_NONE /*No BR inquiry. */ +#define BTA_DM_GENERAL_INQUIRY BTM_GENERAL_INQUIRY /* Perform general inquiry. */ +#define BTA_DM_LIMITED_INQUIRY BTM_LIMITED_INQUIRY /* Perform limited inquiry. */ + +#if ((defined BLE_INCLUDED) && (BLE_INCLUDED == TRUE)) +#define BTA_BLE_INQUIRY_NONE BTM_BLE_INQUIRY_NONE +#define BTA_BLE_GENERAL_INQUIRY BTM_BLE_GENERAL_INQUIRY /* Perform LE general inquiry. */ +#define BTA_BLE_LIMITED_INQUIRY BTM_BLE_LIMITED_INQUIRY /* Perform LE limited inquiry. */ +#endif +typedef UINT8 tBTA_DM_INQ_MODE; + +/* Inquiry Filter Type */ +#define BTA_DM_INQ_CLR BTM_CLR_INQUIRY_FILTER /* Clear inquiry filter. */ +#define BTA_DM_INQ_DEV_CLASS BTM_FILTER_COND_DEVICE_CLASS /* Filter on device class. */ +#define BTA_DM_INQ_BD_ADDR BTM_FILTER_COND_BD_ADDR /* Filter on a specific BD address. */ + +typedef UINT8 tBTA_DM_INQ_FILT; + +/* Authorize Response */ +#define BTA_DM_AUTH_PERM 0 /* Authorized for future connections to the service */ +#define BTA_DM_AUTH_TEMP 1 /* Authorized for current connection only */ +#define BTA_DM_NOT_AUTH 2 /* Not authorized for the service */ + +typedef UINT8 tBTA_AUTH_RESP; + +/* M/S preferred roles */ +#define BTA_ANY_ROLE 0x00 +#define BTA_MASTER_ROLE_PREF 0x01 +#define BTA_MASTER_ROLE_ONLY 0x02 +#define BTA_SLAVE_ROLE_ONLY 0x03 /* Used for PANU only, skip role switch to master */ + +typedef UINT8 tBTA_PREF_ROLES; + +enum { + + BTA_DM_NO_SCATTERNET, /* Device doesn't support scatternet, it might + support "role switch during connection" for + an incoming connection, when it already has + another connection in master role */ + BTA_DM_PARTIAL_SCATTERNET, /* Device supports partial scatternet. It can have + simulateous connection in Master and Slave roles + for short period of time */ + BTA_DM_FULL_SCATTERNET /* Device can have simultaneous connection in master + and slave roles */ + +}; + + +/* Inquiry filter device class condition */ +typedef struct { + DEV_CLASS dev_class; /* device class of interest */ + DEV_CLASS dev_class_mask; /* mask to determine the bits of device class of interest */ +} tBTA_DM_COD_COND; + + +/* Inquiry Filter Condition */ +typedef union { + BD_ADDR bd_addr; /* BD address of device to filter. */ + tBTA_DM_COD_COND dev_class_cond; /* Device class filter condition */ +} tBTA_DM_INQ_COND; + +/* Inquiry Parameters */ +typedef struct { + tBTA_DM_INQ_MODE mode; /* Inquiry mode, limited or general. */ + UINT8 duration; /* Inquiry duration in 1.28 sec units. */ + UINT8 max_resps; /* Maximum inquiry responses. Set to zero for unlimited responses. */ + BOOLEAN report_dup; /* report duplicated inquiry response with higher RSSI value */ + tBTA_DM_INQ_FILT filter_type; /* Filter condition type. */ + tBTA_DM_INQ_COND filter_cond; /* Filter condition data. */ +#if (defined(BTA_HOST_INTERLEAVE_SEARCH) && BTA_HOST_INTERLEAVE_SEARCH == TRUE) + UINT8 intl_duration[4];/*duration array storing the interleave scan's time portions*/ +#endif +} tBTA_DM_INQ; + +/* Config EIR callback */ +typedef void (tBTA_DM_CONFIG_EIR_CBACK) (tBTA_STATUS status, UINT8 eir_type_num, UINT8 *eir_type); + +typedef struct { + BOOLEAN bta_dm_eir_fec_required; /* FEC required */ + BOOLEAN bta_dm_eir_included_name; /* Included device name or not */ + UINT8 bta_dm_eir_min_name_len; /* minimum length of local name when it is shortened */ + + BOOLEAN bta_dm_eir_included_uuid; /* Included UUIDs or not */ +#if (BTA_EIR_CANNED_UUID_LIST == TRUE) + UINT8 bta_dm_eir_uuid16_len; /* length of 16-bit UUIDs */ + UINT8 *bta_dm_eir_uuid16; /* 16-bit UUIDs */ +#else + UINT32 uuid_mask[BTM_EIR_SERVICE_ARRAY_SIZE]; /* mask of UUID list in EIR */ +#endif + + BOOLEAN bta_dm_eir_included_tx_power; /* Included inquiry TX power or not */ + INT8 bta_dm_eir_inq_tx_power; /* Inquiry TX power */ + + UINT8 bta_dm_eir_flags; /* flags for EIR */ + UINT8 bta_dm_eir_manufac_spec_len; /* length of manufacturer specific in bytes */ + UINT8 *bta_dm_eir_manufac_spec; /* manufacturer specific */ + UINT8 bta_dm_eir_url_len; /* length of URL in bytes */ + UINT8 *bta_dm_eir_url; /* URL data */ + + tBTA_DM_CONFIG_EIR_CBACK *config_eir_callback; /* callback */ +} tBTA_DM_EIR_CONF; + +// #if BLE_INCLUDED == TRUE +/* ADV data flag bit definition used for BTM_BLE_AD_TYPE_FLAG */ +#define BTA_BLE_LIMIT_DISC_FLAG BTM_BLE_LIMIT_DISC_FLAG +#define BTA_BLE_GEN_DISC_FLAG BTM_BLE_GEN_DISC_FLAG +#define BTA_BLE_BREDR_NOT_SPT BTM_BLE_BREDR_NOT_SPT +#define BTA_BLE_DMT_CONTROLLER_SPT BTM_BLE_DMT_CONTROLLER_SPT +#define BTA_BLE_DMT_HOST_SPT BTM_BLE_DMT_HOST_SPT +#define BTA_BLE_NON_LIMIT_DISC_FLAG BTM_BLE_NON_LIMIT_DISC_FLAG +#define BTA_BLE_ADV_FLAG_MASK BTM_BLE_ADV_FLAG_MASK +#define BTA_BLE_LIMIT_DISC_MASK BTM_BLE_LIMIT_DISC_MASK + +/* ADV data bit mask */ +#define BTA_BLE_AD_BIT_DEV_NAME BTM_BLE_AD_BIT_DEV_NAME +#define BTA_BLE_AD_BIT_FLAGS BTM_BLE_AD_BIT_FLAGS +#define BTA_BLE_AD_BIT_MANU BTM_BLE_AD_BIT_MANU +#define BTA_BLE_AD_BIT_TX_PWR BTM_BLE_AD_BIT_TX_PWR +#define BTA_BLE_AD_BIT_INT_RANGE BTM_BLE_AD_BIT_INT_RANGE +#define BTA_BLE_AD_BIT_SERVICE BTM_BLE_AD_BIT_SERVICE +#define BTA_BLE_AD_BIT_APPEARANCE BTM_BLE_AD_BIT_APPEARANCE +#define BTA_BLE_AD_BIT_PROPRIETARY BTM_BLE_AD_BIT_PROPRIETARY +#define BTA_DM_BLE_AD_BIT_SERVICE_SOL BTM_BLE_AD_BIT_SERVICE_SOL +#define BTA_DM_BLE_AD_BIT_SERVICE_DATA BTM_BLE_AD_BIT_SERVICE_DATA +#define BTA_DM_BLE_AD_BIT_SIGN_DATA BTM_BLE_AD_BIT_SIGN_DATA +#define BTA_DM_BLE_AD_BIT_SERVICE_128SOL BTM_BLE_AD_BIT_SERVICE_128SOL +#define BTA_DM_BLE_AD_BIT_PUBLIC_ADDR BTM_BLE_AD_BIT_PUBLIC_ADDR +#define BTA_DM_BLE_AD_BIT_RANDOM_ADDR BTM_BLE_AD_BIT_RANDOM_ADDR +#define BTA_DM_BLE_AD_BIT_SERVICE_128 BTM_BLE_AD_BIT_SERVICE_128 /*128-bit Service UUIDs*/ + +typedef tBTM_BLE_AD_MASK tBTA_BLE_AD_MASK; + +/* slave preferred connection interval range */ +typedef struct { + UINT16 low; + UINT16 hi; + +} tBTA_BLE_INT_RANGE; + +/* Service tag supported in the device */ +typedef struct { + UINT8 num_service; + BOOLEAN list_cmpl; + UINT16 *p_uuid; +} tBTA_BLE_SERVICE; + + +typedef struct { + UINT8 len; + UINT8 *p_val; +} tBTA_BLE_MANU; + +typedef struct { + UINT8 adv_type; + UINT8 len; + UINT8 *p_val; /* number of len byte */ +} tBTA_BLE_PROP_ELEM; + +/* vendor proprietary adv type */ +typedef struct { + UINT8 num_elem; + tBTA_BLE_PROP_ELEM *p_elem; +} tBTA_BLE_PROPRIETARY; + +typedef struct { + tBT_UUID service_uuid; + UINT8 len; + UINT8 *p_val; +} tBTA_BLE_SERVICE_DATA; + +typedef tBTM_BLE_128SERVICE tBTA_BLE_128SERVICE; +typedef tBTM_BLE_32SERVICE tBTA_BLE_32SERVICE; + +typedef struct { + tBTA_BLE_INT_RANGE int_range; /* slave prefered conn interval range */ + tBTA_BLE_MANU *p_manu; /* manufacturer data */ + tBTA_BLE_SERVICE *p_services; /* 16 bits services */ + tBTA_BLE_128SERVICE *p_services_128b; /* 128 bits service */ + tBTA_BLE_32SERVICE *p_service_32b; /* 32 bits Service UUID */ + tBTA_BLE_SERVICE *p_sol_services; /* 16 bits services Solicitation UUIDs */ + tBTA_BLE_32SERVICE *p_sol_service_32b; /* List of 32 bit Service Solicitation UUIDs */ + tBTA_BLE_128SERVICE *p_sol_service_128b;/* List of 128 bit Service Solicitation UUIDs */ + tBTA_BLE_PROPRIETARY *p_proprietary; /* proprietary data */ + tBTA_BLE_SERVICE_DATA *p_service_data; /* service data */ + UINT16 appearance; /* appearance data */ + UINT8 flag; + UINT8 tx_power; +} tBTA_BLE_ADV_DATA; + +typedef void (tBTA_UPDATE_DUPLICATE_EXCEPTIONAL_LIST_CMPL_CBACK) (tBTA_STATUS status, uint8_t subcode, uint32_t length, uint8_t *device_info); + +typedef void (tBTA_SET_ADV_DATA_CMPL_CBACK) (tBTA_STATUS status); + +typedef tBTM_START_ADV_CMPL_CBACK tBTA_START_ADV_CMPL_CBACK; + +typedef tBTM_START_STOP_ADV_CMPL_CBACK tBTA_START_STOP_ADV_CMPL_CBACK; + +typedef tBTM_UPDATE_WHITELIST_CBACK tBTA_UPDATE_WHITELIST_CBACK; + +typedef tBTM_SET_PKT_DATA_LENGTH_CBACK tBTA_SET_PKT_DATA_LENGTH_CBACK; + +typedef tBTM_DTM_CMD_CMPL_CBACK tBTA_DTM_CMD_CMPL_CBACK; + +typedef tBTM_SET_RAND_ADDR_CBACK tBTA_SET_RAND_ADDR_CBACK; + +typedef tBTM_SET_LOCAL_PRIVACY_CBACK tBTA_SET_LOCAL_PRIVACY_CBACK; + +typedef tBTM_CMPL_CB tBTA_CMPL_CB; + +typedef tBTM_TX_POWER_RESULTS tBTA_TX_POWER_RESULTS; + +typedef tBTM_RSSI_RESULTS tBTA_RSSI_RESULTS; + +typedef tBTM_SET_AFH_CHANNELS_RESULTS tBTA_SET_AFH_CHANNELS_RESULTS; +typedef tBTM_BLE_SET_CHANNELS_RESULTS tBTA_BLE_SET_CHANNELS_RESULTS; + +typedef tBTM_SET_PAGE_TIMEOUT_RESULTS tBTA_SET_PAGE_TIMEOUT_RESULTS; +typedef tBTM_GET_PAGE_TIMEOUT_RESULTS tBTA_GET_PAGE_TIMEOUT_RESULTS; + +typedef tBTM_SET_ACL_PKT_TYPES_RESULTS tBTA_SET_ACL_PKT_TYPES_RESULTS; + +typedef tBTM_REMOTE_DEV_NAME tBTA_REMOTE_DEV_NAME; + +/* advertising channel map */ +#define BTA_BLE_ADV_CHNL_37 BTM_BLE_ADV_CHNL_37 +#define BTA_BLE_ADV_CHNL_38 BTM_BLE_ADV_CHNL_38 +#define BTA_BLE_ADV_CHNL_39 BTM_BLE_ADV_CHNL_39 +typedef tBTM_BLE_ADV_CHNL_MAP tBTA_BLE_ADV_CHNL_MAP; /* use as a bit mask */ + +/* advertising filter policy */ +typedef tBTM_BLE_AFP tBTA_BLE_AFP; + +/* adv event type */ +#define BTA_BLE_CONNECT_EVT BTM_BLE_CONNECT_EVT /* Connectable undirected advertising */ +#define BTA_BLE_CONNECT_DIR_EVT BTM_BLE_CONNECT_DIR_EVT /* Connectable directed advertising */ +#define BTA_BLE_DISCOVER_EVT BTM_BLE_DISCOVER_EVT /* Scannable undirected advertising */ +#define BTA_BLE_NON_CONNECT_EVT BTM_BLE_NON_CONNECT_EVT /* Non connectable undirected advertising */ +typedef UINT8 tBTA_BLE_ADV_EVT; + +/* adv tx power level */ +#define BTA_BLE_ADV_TX_POWER_MIN 0 /* minimum tx power */ +#define BTA_BLE_ADV_TX_POWER_LOW 1 /* low tx power */ +#define BTA_BLE_ADV_TX_POWER_MID 2 /* middle tx power */ +#define BTA_BLE_ADV_TX_POWER_UPPER 3 /* upper tx power */ +#define BTA_BLE_ADV_TX_POWER_MAX 4 /* maximum tx power */ +typedef UINT8 tBTA_BLE_ADV_TX_POWER; + +/* advertising instance parameters */ +typedef struct { + UINT16 adv_int_min; /* minimum adv interval */ + UINT16 adv_int_max; /* maximum adv interval */ + tBTA_BLE_ADV_EVT adv_type; /* adv event type */ + tBTA_BLE_ADV_CHNL_MAP channel_map; /* adv channel map */ + tBTA_BLE_AFP adv_filter_policy; /* advertising filter policy */ + tBTA_BLE_ADV_TX_POWER tx_power; /* adv tx power */ +} tBTA_BLE_ADV_PARAMS; + +/* These are the fields returned in each device adv packet. It +** is returned in the results callback if registered. +*/ +typedef struct { + UINT8 conn_mode; + tBTA_BLE_AD_MASK ad_mask; /* mask of the valid adv data field */ + UINT8 flag; + UINT8 tx_power_level; + UINT8 remote_name_len; + UINT8 *p_remote_name; + tBTA_BLE_SERVICE service; +} tBTA_BLE_INQ_DATA; + +enum { + BTA_BLE_BATCH_SCAN_MODE_PASS = 1, + BTA_BLE_BATCH_SCAN_MODE_ACTI = 2, + BTA_BLE_BATCH_SCAN_MODE_PASS_ACTI = 3 +}; +typedef UINT8 tBTA_BLE_BATCH_SCAN_MODE; + +enum { + BTA_BLE_DISCARD_OLD_ITEMS = 0, + BTA_BLE_DISCARD_LOWER_RSSI_ITEMS = 1 +}; +typedef UINT8 tBTA_BLE_DISCARD_RULE; + +enum { + BTA_BLE_ADV_SEEN_FIRST_TIME = 0, + BTA_BLE_ADV_TRACKING_TIMEOUT = 1 +}; +typedef UINT8 tBTA_BLE_ADV_CHANGE_REASON; + +enum { + BTA_BLE_BATCH_SCAN_ENB_EVT = 1, + BTA_BLE_BATCH_SCAN_CFG_STRG_EVT = 2, + BTA_BLE_BATCH_SCAN_DATA_EVT = 3, + BTA_BLE_BATCH_SCAN_THRES_EVT = 4, + BTA_BLE_BATCH_SCAN_PARAM_EVT = 5, + BTA_BLE_BATCH_SCAN_DIS_EVT = 6 +}; +typedef tBTM_BLE_BATCH_SCAN_EVT tBTA_BLE_BATCH_SCAN_EVT; + +typedef tBTM_BLE_TRACK_ADV_ACTION tBTA_BLE_TRACK_ADV_ACTION; +// #endif + +/* BLE customer specific feature function type definitions */ +/* data type used on customer specific feature for RSSI monitoring */ +#define BTA_BLE_RSSI_ALERT_HI 0 +#define BTA_BLE_RSSI_ALERT_RANGE 1 +#define BTA_BLE_RSSI_ALERT_LO 2 +typedef UINT8 tBTA_DM_BLE_RSSI_ALERT_TYPE; + +#define BTA_BLE_RSSI_ALERT_NONE BTM_BLE_RSSI_ALERT_NONE /* (0) */ +#define BTA_BLE_RSSI_ALERT_HI_BIT BTM_BLE_RSSI_ALERT_HI_BIT /* (1) */ +#define BTA_BLE_RSSI_ALERT_RANGE_BIT BTM_BLE_RSSI_ALERT_RANGE_BIT /* (1 << 1) */ +#define BTA_BLE_RSSI_ALERT_LO_BIT BTM_BLE_RSSI_ALERT_LO_BIT /* (1 << 2) */ +typedef UINT8 tBTA_DM_BLE_RSSI_ALERT_MASK; + + +typedef void (tBTA_DM_BLE_RSSI_CBACK) (BD_ADDR bd_addr, tBTA_DM_BLE_RSSI_ALERT_TYPE alert_type, INT8 rssi); + +/* max number of filter spot for different filter type */ +#define BTA_DM_BLE_MAX_UUID_FILTER BTM_BLE_MAX_UUID_FILTER /* 8 */ +#define BTA_DM_BLE_MAX_ADDR_FILTER BTM_BLE_MAX_ADDR_FILTER /* 8 */ +#define BTA_DM_BLE_PF_STR_COND_MAX BTM_BLE_PF_STR_COND_MAX /* 4 apply to manu data , or local name */ +#define BTA_DM_BLE_PF_STR_LEN_MAX BTM_BLE_PF_STR_LEN_MAX /* match for first 20 bytes */ + +#define BTA_DM_BLE_PF_LOGIC_OR 0 +#define BTA_DM_BLE_PF_LOGIC_AND 1 +typedef UINT8 tBTA_DM_BLE_PF_LOGIC_TYPE; + +enum { + BTA_DM_BLE_SCAN_COND_ADD, + BTA_DM_BLE_SCAN_COND_DELETE, + BTA_DM_BLE_SCAN_COND_CLEAR = 2 +}; +typedef UINT8 tBTA_DM_BLE_SCAN_COND_OP; + +/* ADV payload filtering vendor specific call event */ +enum { + BTA_BLE_SCAN_PF_ENABLE_EVT = 7, + BTA_BLE_SCAN_PF_COND_EVT +}; + +/* filter selection bit index */ +#define BTA_DM_BLE_PF_ADDR_FILTER BTM_BLE_PF_ADDR_FILTER +#define BTA_DM_BLE_PF_SRVC_DATA BTM_BLE_PF_SRVC_DATA +#define BTA_DM_BLE_PF_SRVC_UUID BTM_BLE_PF_SRVC_UUID +#define BTA_DM_BLE_PF_SRVC_SOL_UUID BTM_BLE_PF_SRVC_SOL_UUID +#define BTA_DM_BLE_PF_LOCAL_NAME BTM_BLE_PF_LOCAL_NAME +#define BTA_DM_BLE_PF_MANU_DATA BTM_BLE_PF_MANU_DATA +#define BTA_DM_BLE_PF_SRVC_DATA_PATTERN BTM_BLE_PF_SRVC_DATA_PATTERN +#define BTA_DM_BLE_PF_TYPE_ALL BTM_BLE_PF_TYPE_ALL +#define BTA_DM_BLE_PF_TYPE_MAX BTM_BLE_PF_TYPE_MAX +typedef UINT8 tBTA_DM_BLE_PF_COND_TYPE; + +typedef union { + UINT16 uuid16_mask; + UINT32 uuid32_mask; + UINT8 uuid128_mask[LEN_UUID_128]; +} tBTA_DM_BLE_PF_COND_MASK; + +typedef struct { + tBLE_BD_ADDR *p_target_addr; /* target address, if NULL, generic UUID filter */ + tBT_UUID uuid; /* UUID condition */ + tBTA_DM_BLE_PF_LOGIC_TYPE cond_logic; /* AND/OR */ + tBTA_DM_BLE_PF_COND_MASK *p_uuid_mask; /* UUID condition mask, if NULL, match exact as UUID condition */ +} tBTA_DM_BLE_PF_UUID_COND; + +typedef struct { + UINT8 data_len; /* <= 20 bytes */ + UINT8 *p_data; +} tBTA_DM_BLE_PF_LOCAL_NAME_COND; + +typedef struct { + UINT16 company_id; /* company ID */ + UINT8 data_len; /* <= 20 bytes */ + UINT8 *p_pattern; + UINT16 company_id_mask; /* UUID value mask */ + UINT8 *p_pattern_mask; /* Manufacturer data matching mask, same length + as data pattern, set to all 0xff, match exact data */ +} tBTA_DM_BLE_PF_MANU_COND; + +typedef struct { + UINT16 uuid; /* service ID */ + UINT8 data_len; /* <= 20 bytes */ + UINT8 *p_pattern; + UINT8 *p_pattern_mask; /* Service data matching mask, same length + as data pattern, set to all 0xff, match exact data */ +} tBTA_DM_BLE_PF_SRVC_PATTERN_COND; + +typedef union { + tBLE_BD_ADDR target_addr; + tBTA_DM_BLE_PF_LOCAL_NAME_COND local_name; /* local name filtering */ + tBTA_DM_BLE_PF_MANU_COND manu_data; /* manufacturer data filtering */ + tBTA_DM_BLE_PF_UUID_COND srvc_uuid; /* service UUID filtering */ + tBTA_DM_BLE_PF_UUID_COND solicitate_uuid; /* solicited service UUID filtering */ + tBTA_DM_BLE_PF_SRVC_PATTERN_COND srvc_data; /* service data pattern */ +} tBTA_DM_BLE_PF_COND_PARAM; + +typedef UINT8 tBTA_DM_BLE_PF_FILT_INDEX; +typedef UINT8 tBTA_DM_BLE_PF_AVBL_SPACE; + +typedef INT8 tBTA_DM_RSSI_VALUE; +typedef UINT8 tBTA_DM_LINK_QUALITY_VALUE; + + +typedef UINT8 tBTA_SIG_STRENGTH_MASK; + + +/* Security Callback Events */ +#define BTA_DM_ENABLE_EVT 0 /* Enable Event */ +#define BTA_DM_DISABLE_EVT 1 /* Disable Event */ +#define BTA_DM_PIN_REQ_EVT 2 /* PIN request. */ +#define BTA_DM_AUTH_CMPL_EVT 3 /* Authentication complete indication. */ +#define BTA_DM_AUTHORIZE_EVT 4 /* Authorization request. */ +#define BTA_DM_LINK_UP_EVT 5 /* Connection UP event */ +#define BTA_DM_LINK_DOWN_EVT 6 /* Connection DOWN event */ +#define BTA_DM_SIG_STRENGTH_EVT 7 /* Signal strength for bluetooth connection */ +#define BTA_DM_BUSY_LEVEL_EVT 8 /* System busy level */ +#define BTA_DM_BOND_CANCEL_CMPL_EVT 9 /* Bond cancel complete indication */ +#define BTA_DM_SP_CFM_REQ_EVT 10 /* Simple Pairing User Confirmation request. */ +#define BTA_DM_SP_KEY_NOTIF_EVT 11 /* Simple Pairing Passkey Notification */ +#define BTA_DM_SP_RMT_OOB_EVT 12 /* Simple Pairing Remote OOB Data request. */ +#define BTA_DM_SP_KEYPRESS_EVT 13 /* Key press notification event. */ +#define BTA_DM_ROLE_CHG_EVT 14 /* Role Change event. */ +#define BTA_DM_BLE_KEY_EVT 15 /* BLE SMP key event for peer device keys */ +#define BTA_DM_BLE_SEC_REQ_EVT 16 /* BLE SMP security request */ +#define BTA_DM_BLE_PASSKEY_NOTIF_EVT 17 /* SMP passkey notification event */ +#define BTA_DM_BLE_PASSKEY_REQ_EVT 18 /* SMP passkey request event */ +#define BTA_DM_BLE_OOB_REQ_EVT 19 /* SMP OOB request event */ +#define BTA_DM_BLE_LOCAL_IR_EVT 20 /* BLE local IR event */ +#define BTA_DM_BLE_LOCAL_ER_EVT 21 /* BLE local ER event */ +#define BTA_DM_BLE_NC_REQ_EVT 22 /* SMP Numeric Comparison request event */ +// btla-specific ++ +#define BTA_DM_SP_RMT_OOB_EXT_EVT 23 /* Simple Pairing Remote OOB Extended Data request. */ +#define BTA_DM_BLE_AUTH_CMPL_EVT 24 /* BLE Auth complete */ +// btla-specific -- +#define BTA_DM_DEV_UNPAIRED_EVT 25 /* BT unpair event */ +#define BTA_DM_HW_ERROR_EVT 26 /* BT Chip H/W error */ +#define BTA_DM_LE_FEATURES_READ 27 /* Cotroller specific LE features are read */ +#define BTA_DM_ENER_INFO_READ 28 /* Energy info read */ +#define BTA_DM_BLE_DEV_UNPAIRED_EVT 29 /* BLE unpair event */ +#define BTA_DM_SP_KEY_REQ_EVT 30 /* Simple Pairing Passkey request */ +#define BTA_DM_PM_MODE_CHG_EVT 31 /* Mode changed event */ +#define BTA_DM_ACL_LINK_STAT_EVT 32 /* ACL connection status report event */ +#define BTA_DM_BLE_SC_OOB_REQ_EVT 33 /* BLE SMP SC OOB request event */ +#define BTA_DM_BLE_SC_CR_LOC_OOB_EVT 34 /* BLE SMP SC Create Local OOB request event */ +#define BTA_DM_ENC_CHG_EVT 35 /* Encryption change event */ + +typedef UINT8 tBTA_DM_SEC_EVT; + +/* Structure associated with BTA_DM_ENABLE_EVT */ +typedef struct { + tBTA_STATUS status; +} tBTA_DM_ENABLE; + +/* Structure associated with BTA_DM_PIN_REQ_EVT */ +typedef struct { + /* Note: First 3 data members must be, bd_addr, dev_class, and bd_name in order */ + BD_ADDR bd_addr; /* BD address peer device. */ + DEV_CLASS dev_class; /* Class of Device */ + BD_NAME bd_name; /* Name of peer device. */ + BOOLEAN min_16_digit; /* TRUE if the pin returned must be at least 16 digits */ +} tBTA_DM_PIN_REQ; + +/* BLE related definition */ +#if (SMP_INCLUDED == TRUE) +#define BTA_DM_AUTH_FAIL_BASE (HCI_ERR_MAX_ERR + 10) +#define BTA_DM_AUTH_CONVERT_SMP_CODE(x) (BTA_DM_AUTH_FAIL_BASE + (x)) +#define BTA_DM_AUTH_SMP_PASSKEY_FAIL BTA_DM_AUTH_CONVERT_SMP_CODE (SMP_PASSKEY_ENTRY_FAIL) +#define BTA_DM_AUTH_SMP_OOB_FAIL (BTA_DM_AUTH_FAIL_BASE + SMP_OOB_FAIL) +#define BTA_DM_AUTH_SMP_PAIR_AUTH_FAIL (BTA_DM_AUTH_FAIL_BASE + SMP_PAIR_AUTH_FAIL) +#define BTA_DM_AUTH_SMP_CONFIRM_VALUE_FAIL (BTA_DM_AUTH_FAIL_BASE + SMP_CONFIRM_VALUE_ERR) +#define BTA_DM_AUTH_SMP_PAIR_NOT_SUPPORT (BTA_DM_AUTH_FAIL_BASE + SMP_PAIR_NOT_SUPPORT) +#define BTA_DM_AUTH_SMP_ENC_KEY_SIZE (BTA_DM_AUTH_FAIL_BASE + SMP_ENC_KEY_SIZE) +#define BTA_DM_AUTH_SMP_INVALID_CMD (BTA_DM_AUTH_FAIL_BASE + SMP_INVALID_CMD) +#define BTA_DM_AUTH_SMP_UNKNOWN_ERR (BTA_DM_AUTH_FAIL_BASE + SMP_PAIR_FAIL_UNKNOWN) +#define BTA_DM_AUTH_SMP_REPEATED_ATTEMPT (BTA_DM_AUTH_FAIL_BASE + SMP_REPEATED_ATTEMPTS) +#define BTA_DM_AUTH_SMP_INVALID_PARAMETERS (BTA_DM_AUTH_FAIL_BASE + SMP_INVALID_PARAMETERS) +#define BTA_DM_AUTH_SMP_INTERNAL_ERR (BTA_DM_AUTH_FAIL_BASE + SMP_PAIR_INTERNAL_ERR) +#define BTA_DM_AUTH_SMP_UNKNOWN_IO (BTA_DM_AUTH_FAIL_BASE + SMP_UNKNOWN_IO_CAP) +#define BTA_DM_AUTH_SMP_INIT_FAIL (BTA_DM_AUTH_FAIL_BASE + SMP_INIT_FAIL) +#define BTA_DM_AUTH_SMP_CONFIRM_FAIL (BTA_DM_AUTH_FAIL_BASE + SMP_CONFIRM_FAIL) +#define BTA_DM_AUTH_SMP_BUSY (BTA_DM_AUTH_FAIL_BASE + SMP_BUSY) +#define BTA_DM_AUTH_SMP_ENC_FAIL (BTA_DM_AUTH_FAIL_BASE + SMP_ENC_FAIL) +#define BTA_DM_AUTH_SMP_RSP_TIMEOUT (BTA_DM_AUTH_FAIL_BASE + SMP_RSP_TIMEOUT) +#endif ///SMP_INCLUDED == TRUE +/* connection parameter boundary value and dummy value */ +#define BTA_DM_BLE_SCAN_INT_MIN BTM_BLE_SCAN_INT_MIN +#define BTA_DM_BLE_SCAN_INT_MAX BTM_BLE_SCAN_INT_MAX +#define BTA_DM_BLE_SCAN_WIN_MIN BTM_BLE_SCAN_WIN_MIN +#define BTA_DM_BLE_SCAN_WIN_MAX BTM_BLE_SCAN_WIN_MAX +#define BTA_DM_BLE_CONN_INT_MIN BTM_BLE_CONN_INT_MIN +#define BTA_DM_BLE_CONN_INT_MAX BTM_BLE_CONN_INT_MAX +#define BTA_DM_BLE_CONN_LATENCY_MAX BTM_BLE_CONN_LATENCY_MAX +#define BTA_DM_BLE_CONN_SUP_TOUT_MIN BTM_BLE_CONN_SUP_TOUT_MIN +#define BTA_DM_BLE_CONN_SUP_TOUT_MAX BTM_BLE_CONN_SUP_TOUT_MAX +#define BTA_DM_BLE_CONN_PARAM_UNDEF BTM_BLE_CONN_PARAM_UNDEF /* use this value when a specific value not to be overwritten */ + +#if (SMP_INCLUDED == TRUE) +#define BTA_LE_KEY_PENC BTM_LE_KEY_PENC /* encryption information of peer device */ +#define BTA_LE_KEY_PID BTM_LE_KEY_PID /* identity key of the peer device */ +#define BTA_LE_KEY_PCSRK BTM_LE_KEY_PCSRK /* peer SRK */ +#define BTA_LE_KEY_LENC BTM_LE_KEY_LENC /* master role security information:div */ +#define BTA_LE_KEY_LID BTM_LE_KEY_LID /* master device ID key */ +#define BTA_LE_KEY_LCSRK BTM_LE_KEY_LCSRK /* local CSRK has been deliver to peer */ +#endif ///SMP_INCLUDED == TRUE +typedef UINT8 tBTA_LE_KEY_TYPE; /* can be used as a bit mask */ + + +typedef tBTM_LE_PENC_KEYS tBTA_LE_PENC_KEYS ; +typedef tBTM_LE_PCSRK_KEYS tBTA_LE_PCSRK_KEYS; +typedef tBTM_LE_LENC_KEYS tBTA_LE_LENC_KEYS ; +typedef tBTM_LE_LCSRK_KEYS tBTA_LE_LCSRK_KEYS ; +typedef tBTM_LE_PID_KEYS tBTA_LE_PID_KEYS ; + +typedef union { + tBTA_LE_PENC_KEYS penc_key; /* received peer encryption key */ + tBTA_LE_PCSRK_KEYS psrk_key; /* received peer device SRK */ + tBTA_LE_PID_KEYS pid_key; /* peer device ID key */ + tBTA_LE_LENC_KEYS lenc_key; /* local encryption reproduction keys LTK = = d1(ER,DIV,0)*/ + tBTA_LE_LCSRK_KEYS lcsrk_key; /* local device CSRK = d1(ER,DIV,1)*/ + tBTA_LE_PID_KEYS lid_key; /* local device ID key for the particular remote */ +} tBTA_LE_KEY_VALUE; + +#define BTA_BLE_LOCAL_KEY_TYPE_ID 1 +#define BTA_BLE_LOCAL_KEY_TYPE_ER 2 +typedef UINT8 tBTA_DM_BLE_LOCAL_KEY_MASK; + +typedef struct { + BT_OCTET16 ir; + BT_OCTET16 irk; + BT_OCTET16 dhk; +} tBTA_BLE_LOCAL_ID_KEYS; +#if (SMP_INCLUDED == TRUE) +#define BTA_DM_SEC_GRANTED BTA_SUCCESS +#define BTA_DM_SEC_PAIR_NOT_SPT BTA_DM_AUTH_SMP_PAIR_NOT_SUPPORT +#define BTA_DM_SEC_REP_ATTEMPTS BTA_DM_AUTH_SMP_REPEATED_ATTEMPT +#endif ///SMP_INCLUDED == TRUE +typedef UINT8 tBTA_DM_BLE_SEC_GRANT; + + +#define BTA_DM_BLE_ONN_NONE BTM_BLE_CONN_NONE +#define BTA_DM_BLE_CONN_AUTO BTM_BLE_CONN_AUTO +#define BTA_DM_BLE_CONN_SELECTIVE BTM_BLE_CONN_SELECTIVE +typedef UINT8 tBTA_DM_BLE_CONN_TYPE; + +typedef BOOLEAN (tBTA_DM_BLE_SEL_CBACK)(BD_ADDR random_bda, UINT8 *p_remote_name); + +typedef tBTM_LE_UPDATE_CONN_PRAMS tBTA_LE_UPDATE_CONN_PRAMS; +typedef tBTM_UPDATE_CONN_PARAM_CBACK tBTA_UPDATE_CONN_PARAM_CBACK; + + +/* Structure associated with BTA_DM_BLE_SEC_REQ_EVT */ +typedef struct { + BD_ADDR bd_addr; /* peer address */ + BD_NAME bd_name; /* peer device name */ +} tBTA_DM_BLE_SEC_REQ; + +typedef struct { + BD_ADDR bd_addr; /* peer address */ + tBTM_LE_KEY_TYPE key_type; + tBTM_LE_KEY_VALUE *p_key_value; +} tBTA_DM_BLE_KEY; + +/* Structure associated with BTA_DM_AUTH_CMPL_EVT */ +typedef struct { + BD_ADDR bd_addr; /* BD address peer device. */ + BD_NAME bd_name; /* Name of peer device. */ + BOOLEAN key_present; /* Valid link key value in key element */ + LINK_KEY key; /* Link key associated with peer device. */ + UINT8 key_type; /* The type of Link Key */ + BOOLEAN success; /* TRUE of authentication succeeded, FALSE if failed. */ + UINT8 fail_reason; /* The HCI reason/error code for when success=FALSE */ + tBLE_ADDR_TYPE addr_type; /* Peer device address type */ + tBT_DEVICE_TYPE dev_type; + UINT8 auth_mode; + BOOLEAN sc_support; /* Denotes if peer device supported secure connection while bonding. */ +} tBTA_DM_AUTH_CMPL; + + +/* Structure associated with BTA_DM_AUTHORIZE_EVT */ +typedef struct { + BD_ADDR bd_addr; /* BD address peer device. */ + BD_NAME bd_name; /* Name of peer device. */ + tBTA_SERVICE_ID service; /* Service ID to authorize. */ +// btla-specific ++ + DEV_CLASS dev_class; +// btla-specific -- +} tBTA_DM_AUTHORIZE; + +/* Structure associated with BTA_DM_LINK_UP_EVT */ +typedef struct { + BOOLEAN sc_downgrade; /* Security downgrade state. */ + BD_ADDR bd_addr; /* BD address peer device. */ +#if BLE_INCLUDED == TRUE + tBTA_TRANSPORT link_type; +#endif +} tBTA_DM_LINK_UP; + +/* Structure associated with BTA_DM_LINK_DOWN_EVT */ +typedef struct { + BD_ADDR bd_addr; /* BD address peer device. */ + UINT8 status; /* connection open/closed */ + UINT8 reason; /* link down reason */ + BOOLEAN is_removed; /* TRUE if device is removed when link is down */ +#if BLE_INCLUDED == TRUE + tBTA_TRANSPORT link_type; +#endif +} tBTA_DM_LINK_DOWN; + +enum { + BTA_ACL_LINK_STAT_CONN_CMPL, + BTA_ACL_LINK_STAT_DISCONN_CMPL +}; +typedef UINT8 tBTA_ACL_LINK_STAT_EVT; + +typedef struct { + UINT8 status; /* ACL link connection status */ + UINT16 handle; /* ACL connection handle */ + BD_ADDR bd_addr; /* peer bluetooth address */ +} tBTA_DM_ACL_CONN_CMPL_STAT; + +typedef struct { + UINT8 reason; /* ACL link disconnection reason */ + UINT16 handle; /* ACL connection handle */ + BD_ADDR bd_addr; /* peer bluetooth address */ +} tBTA_DM_ACL_DISCONN_CMPL_STAT; + +/* Structure associated with BTA_DM_ACL_LINK_STAT_EVT */ +typedef struct { + tBTA_ACL_LINK_STAT_EVT event; /* ACL link event */ + union { + tBTA_DM_ACL_CONN_CMPL_STAT conn_cmpl; + tBTA_DM_ACL_DISCONN_CMPL_STAT disconn_cmpl; + } link_act; +} tBTA_DM_ACL_LINK_STAT; + +/* Structure associated with BTA_DM_ROLE_CHG_EVT */ +typedef struct { + BD_ADDR bd_addr; /* BD address peer device. */ + UINT8 new_role; /* the new connection role */ +} tBTA_DM_ROLE_CHG; + +/* Structure associated with BTA_DM_BUSY_LEVEL_EVT */ +typedef struct { + UINT8 level; /* when paging or inquiring, level is 10. + Otherwise, the number of ACL links */ + UINT8 level_flags; /* indicates individual flags */ +} tBTA_DM_BUSY_LEVEL; + +#define BTA_IO_CAP_OUT BTM_IO_CAP_OUT /* 0 DisplayOnly */ +#define BTA_IO_CAP_IO BTM_IO_CAP_IO /* 1 DisplayYesNo */ +#define BTA_IO_CAP_IN BTM_IO_CAP_IN /* 2 KeyboardOnly */ +#define BTA_IO_CAP_NONE BTM_IO_CAP_NONE /* 3 NoInputNoOutput */ +#if BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE +#define BTA_IO_CAP_KBDISP BTM_IO_CAP_KBDISP /* 4 Keyboard display */ +#endif +typedef tBTM_IO_CAP tBTA_IO_CAP; + +#define BTA_AUTH_SP_NO BTM_AUTH_SP_NO /* 0 MITM Protection Not Required - Single Profile/non-bonding + Numeric comparison with automatic accept allowed */ +#define BTA_AUTH_SP_YES BTM_AUTH_SP_YES /* 1 MITM Protection Required - Single Profile/non-bonding + Use IO Capabilities to determine authentication procedure */ +#define BTA_AUTH_AP_NO BTM_AUTH_AP_NO /* 2 MITM Protection Not Required - All Profiles/dedicated bonding + Numeric comparison with automatic accept allowed */ +#define BTA_AUTH_AP_YES BTM_AUTH_AP_YES /* 3 MITM Protection Required - All Profiles/dedicated bonding + Use IO Capabilities to determine authentication procedure */ +#define BTA_AUTH_SPGB_NO BTM_AUTH_SPGB_NO /* 4 MITM Protection Not Required - Single Profiles/general bonding + Numeric comparison with automatic accept allowed */ +#define BTA_AUTH_SPGB_YES BTM_AUTH_SPGB_YES /* 5 MITM Protection Required - Single Profiles/general bonding + Use IO Capabilities to determine authentication procedure */ +typedef tBTM_AUTH_REQ tBTA_AUTH_REQ; + +#define BTA_AUTH_DD_BOND BTM_AUTH_DD_BOND /* 2 this bit is set for dedicated bonding */ +#define BTA_AUTH_GEN_BOND BTM_AUTH_SPGB_NO /* 4 this bit is set for general bonding */ +#define BTA_AUTH_BONDS BTM_AUTH_BONDS /* 6 the general/dedicated bonding bits */ + +#if (SMP_INCLUDED == TRUE) +#define BTA_LE_AUTH_NO_BOND BTM_LE_AUTH_REQ_NO_BOND /* 0*/ +#define BTA_LE_AUTH_BOND BTM_LE_AUTH_REQ_BOND /* 1 << 0 */ +#define BTA_LE_AUTH_REQ_MITM BTM_LE_AUTH_REQ_MITM /* 1 << 2 */ + +#define BTA_LE_AUTH_REQ_SC_ONLY BTM_LE_AUTH_REQ_SC_ONLY /* 1 << 3 */ +#define BTA_LE_AUTH_REQ_SC_BOND BTM_LE_AUTH_REQ_SC_BOND /* 1001 */ +#define BTA_LE_AUTH_REQ_SC_MITM BTM_LE_AUTH_REQ_SC_MITM /* 1100 */ +#define BTA_LE_AUTH_REQ_SC_MITM_BOND BTM_LE_AUTH_REQ_SC_MITM_BOND /* 1101 */ +#endif ///SMP_INCLUDED == TRUE +typedef tBTM_LE_AUTH_REQ tBTA_LE_AUTH_REQ; /* combination of the above bit pattern */ + +#define BTA_OOB_NONE BTM_OOB_NONE +#define BTA_OOB_PRESENT BTM_OOB_PRESENT +#if BTM_OOB_INCLUDED == TRUE +#define BTA_OOB_UNKNOWN BTM_OOB_UNKNOWN +#endif +typedef tBTM_OOB_DATA tBTA_OOB_DATA; + +#define BTA_PM_MD_ACTIVE BTM_PM_MD_ACTIVE /* 0 Active mode */ +#define BTA_PM_MD_HOLD BTM_PM_MD_HOLD /* 1 Hold mode */ +#define BTA_PM_MD_SNIFF BTM_PM_MD_SNIFF /* 2 Sniff mode */ +#define BTA_PM_MD_PARK BTM_PM_MD_PARK /* 3 Park state */ +typedef tBTM_PM_MODE tBTA_PM_MODE; + +/* Structure associated with BTA_DM_SP_CFM_REQ_EVT */ +typedef struct { + /* Note: First 3 data members must be, bd_addr, dev_class, and bd_name in order */ + BD_ADDR bd_addr; /* peer address */ + DEV_CLASS dev_class; /* peer CoD */ + BD_NAME bd_name; /* peer device name */ + UINT32 num_val; /* the numeric value for comparison. If just_works, do not show this number to UI */ + BOOLEAN just_works; /* TRUE, if "Just Works" association model */ + tBTA_AUTH_REQ loc_auth_req; /* Authentication required for local device */ + tBTA_AUTH_REQ rmt_auth_req; /* Authentication required for peer device */ + tBTA_IO_CAP loc_io_caps; /* IO Capabilities of local device */ + tBTA_AUTH_REQ rmt_io_caps; /* IO Capabilities of remote device */ +} tBTA_DM_SP_CFM_REQ; + +/* Structure associated with tBTA_DM_SP_KEY_REQ */ +typedef struct { + BD_ADDR bd_addr; /* peer address */ + DEV_CLASS dev_class; /* peer CoD */ + BD_NAME bd_name; /* peer device name */ +} tBTA_DM_SP_KEY_REQ; + +enum { + BTA_SP_KEY_STARTED, /* passkey entry started */ + BTA_SP_KEY_ENTERED, /* passkey digit entered */ + BTA_SP_KEY_ERASED, /* passkey digit erased */ + BTA_SP_KEY_CLEARED, /* passkey cleared */ + BTA_SP_KEY_COMPLT /* passkey entry completed */ +}; +typedef UINT8 tBTA_SP_KEY_TYPE; + +/* Structure associated with BTA_DM_SP_KEYPRESS_EVT */ +typedef struct { + BD_ADDR bd_addr; /* peer address */ + tBTA_SP_KEY_TYPE notif_type; +} tBTA_DM_SP_KEY_PRESS; + +/* Structure associated with BTA_DM_SP_KEY_NOTIF_EVT */ +typedef struct { + /* Note: First 3 data members must be, bd_addr, dev_class, and bd_name in order */ + BD_ADDR bd_addr; /* peer address */ + DEV_CLASS dev_class; /* peer CoD */ + BD_NAME bd_name; /* peer device name */ + UINT32 passkey; /* the numeric value for comparison. If just_works, do not show this number to UI */ +} tBTA_DM_SP_KEY_NOTIF; + +/* Structure associated with BTA_DM_SP_RMT_OOB_EVT */ +typedef struct { + /* Note: First 3 data members must be, bd_addr, dev_class, and bd_name in order */ + BD_ADDR bd_addr; /* peer address */ + DEV_CLASS dev_class; /* peer CoD */ + BD_NAME bd_name; /* peer device name */ +} tBTA_DM_SP_RMT_OOB; + +/* Structure associated with BTA_DM_BOND_CANCEL_CMPL_EVT */ +typedef struct { + tBTA_STATUS result; /* TRUE of bond cancel succeeded, FALSE if failed. */ +} tBTA_DM_BOND_CANCEL_CMPL; + +/* Structure associated with BTA_DM_PM_MODE_CHG_EVT */ +typedef struct { + BD_ADDR bd_addr; /* BD address peer device. */ + tBTA_PM_MODE mode; /* the new connection role */ +} tBTA_DM_MODE_CHG; + +typedef struct { + BT_OCTET16 local_oob_c; /* Local OOB Data Confirmation/Commitment */ + BT_OCTET16 local_oob_r; /* Local OOB Data Randomizer */ +} tBTA_DM_LOC_OOB_DATA; + +typedef struct { + BD_ADDR bd_addr; /* BD address peer device */ + UINT8 enc_mode; /* Encryption mode */ +} tBTA_DM_ENC_CHG; + +/* Union of all security callback structures */ +typedef union { + tBTA_DM_ENABLE enable; /* BTA enabled */ + tBTA_DM_PIN_REQ pin_req; /* PIN request. */ + tBTA_DM_AUTH_CMPL auth_cmpl; /* Authentication complete indication. */ + tBTA_DM_AUTHORIZE authorize; /* Authorization request. */ + tBTA_DM_LINK_UP link_up; /* ACL connection up event */ + tBTA_DM_LINK_DOWN link_down; /* ACL connection down event */ + tBTA_DM_ACL_LINK_STAT acl_link_stat; /* ACL link status event */ + tBTA_DM_BUSY_LEVEL busy_level; /* System busy level */ + tBTA_DM_SP_CFM_REQ cfm_req; /* user confirm request */ + tBTA_DM_SP_KEY_REQ key_req; /* user passkey request */ + tBTA_DM_SP_KEY_NOTIF key_notif; /* passkey notification */ + tBTA_DM_SP_RMT_OOB rmt_oob; /* remote oob */ + tBTA_DM_BOND_CANCEL_CMPL bond_cancel_cmpl; /* Bond Cancel Complete indication */ + tBTA_DM_SP_KEY_PRESS key_press; /* key press notification event */ + tBTA_DM_ROLE_CHG role_chg; /* role change event */ + tBTA_DM_BLE_SEC_REQ ble_req; /* BLE SMP related request */ + tBTA_DM_BLE_KEY ble_key; /* BLE SMP keys used when pairing */ + tBTA_BLE_LOCAL_ID_KEYS ble_id_keys; /* IR event */ + BT_OCTET16 ble_er; /* ER event data */ +#if BTA_DM_PM_INCLUDED + tBTA_DM_MODE_CHG mode_chg; /* mode change event */ +#endif ///BTA_DM_PM_INCLUDED + tBTA_DM_LOC_OOB_DATA local_oob_data; /* Local OOB data generated by us */ + tBTA_DM_ENC_CHG enc_chg; /* Encryption change event */ +} tBTA_DM_SEC; + +/* Security callback */ +typedef void (tBTA_DM_SEC_CBACK)(tBTA_DM_SEC_EVT event, tBTA_DM_SEC *p_data); + +#define BTA_BLE_MULTI_ADV_ILLEGAL 0 + +/* multi adv callback event */ +#define BTA_BLE_MULTI_ADV_ENB_EVT 1 +#define BTA_BLE_MULTI_ADV_DISABLE_EVT 2 +#define BTA_BLE_MULTI_ADV_PARAM_EVT 3 +#define BTA_BLE_MULTI_ADV_DATA_EVT 4 + +typedef UINT8 tBTA_BLE_MULTI_ADV_EVT; + +/* multi adv callback */ +typedef void (tBTA_BLE_MULTI_ADV_CBACK)(tBTA_BLE_MULTI_ADV_EVT event, + UINT8 inst_id, void *p_ref, tBTA_STATUS status); +typedef UINT32 tBTA_DM_BLE_REF_VALUE; + +#define BTA_DM_BLE_PF_ENABLE_EVT BTM_BLE_PF_ENABLE +#define BTA_DM_BLE_PF_CONFIG_EVT BTM_BLE_PF_CONFIG +typedef UINT8 tBTA_DM_BLE_PF_EVT; + +#define BTA_DM_BLE_PF_ENABLE 1 +#define BTA_DM_BLE_PF_CONFIG 2 +typedef UINT8 tBTA_DM_BLE_PF_ACTION; + +/* Config callback */ +typedef void (tBTA_DM_BLE_PF_CFG_CBACK) (tBTA_DM_BLE_PF_ACTION action, + tBTA_DM_BLE_PF_COND_TYPE cfg_cond, + tBTA_DM_BLE_PF_AVBL_SPACE avbl_space, tBTA_STATUS status, + tBTA_DM_BLE_REF_VALUE ref_value); +/* Param callback */ +typedef void (tBTA_DM_BLE_PF_PARAM_CBACK) (UINT8 action_type, tBTA_DM_BLE_PF_AVBL_SPACE avbl_space, + tBTA_DM_BLE_REF_VALUE ref_value, tBTA_STATUS status); + +/* Status callback */ +typedef void (tBTA_DM_BLE_PF_STATUS_CBACK) (UINT8 action, tBTA_STATUS status, + tBTA_DM_BLE_REF_VALUE ref_value); + + +#define BTA_DM_BLE_PF_BRDCAST_ADDR_FILT 1 +#define BTA_DM_BLE_PF_SERV_DATA_CHG_FILT 2 +#define BTA_DM_BLE_PF_SERV_UUID 4 +#define BTA_DM_BLE_PF_SERV_SOLC_UUID 8 +#define BTA_DM_BLE_PF_LOC_NAME_CHECK 16 +#define BTA_DM_BLE_PF_MANUF_NAME_CHECK 32 +#define BTA_DM_BLE_PF_SERV_DATA_CHECK 64 +typedef UINT16 tBTA_DM_BLE_PF_FEAT_SEL; + +#define BTA_DM_BLE_PF_LIST_LOGIC_OR 1 +#define BTA_DM_BLE_PF_LIST_LOGIC_AND 2 +typedef UINT16 tBTA_DM_BLE_PF_LIST_LOGIC_TYPE; + +#define BTA_DM_BLE_PF_FILT_LOGIC_OR 0 +#define BTA_DM_BLE_PF_FILT_LOGIC_AND 1 +typedef UINT16 tBTA_DM_BLE_PF_FILT_LOGIC_TYPE; + +typedef UINT8 tBTA_DM_BLE_PF_RSSI_THRESHOLD; +typedef UINT8 tBTA_DM_BLE_PF_DELIVERY_MODE; +typedef UINT16 tBTA_DM_BLE_PF_TIMEOUT; +typedef UINT8 tBTA_DM_BLE_PF_TIMEOUT_CNT; +typedef UINT16 tBTA_DM_BLE_PF_ADV_TRACK_ENTRIES; + +typedef struct { + tBTA_DM_BLE_PF_FEAT_SEL feat_seln; + tBTA_DM_BLE_PF_LIST_LOGIC_TYPE list_logic_type; + tBTA_DM_BLE_PF_FILT_LOGIC_TYPE filt_logic_type; + tBTA_DM_BLE_PF_RSSI_THRESHOLD rssi_high_thres; + tBTA_DM_BLE_PF_RSSI_THRESHOLD rssi_low_thres; + tBTA_DM_BLE_PF_DELIVERY_MODE dely_mode; + tBTA_DM_BLE_PF_TIMEOUT found_timeout; + tBTA_DM_BLE_PF_TIMEOUT lost_timeout; + tBTA_DM_BLE_PF_TIMEOUT_CNT found_timeout_cnt; + tBTA_DM_BLE_PF_ADV_TRACK_ENTRIES num_of_tracking_entries; +} tBTA_DM_BLE_PF_FILT_PARAMS; + +/* Search callback events */ +#define BTA_DM_INQ_RES_EVT 0 /* Inquiry result for a peer device. */ +#define BTA_DM_INQ_CMPL_EVT 1 /* Inquiry complete. */ +#define BTA_DM_DISC_RES_EVT 2 /* Discovery result for a peer device. */ +#define BTA_DM_DISC_BLE_RES_EVT 3 /* Discovery result for BLE GATT based servoce on a peer device. */ +#define BTA_DM_DISC_CMPL_EVT 4 /* Discovery complete. */ +#define BTA_DM_DI_DISC_CMPL_EVT 5 /* Discovery complete. */ +#define BTA_DM_SEARCH_CANCEL_CMPL_EVT 6 /* Search cancelled */ +#define BTA_DM_INQ_DISCARD_NUM_EVT 7 /* The number of inquiry discarded packets */ + +typedef UINT8 tBTA_DM_SEARCH_EVT; + +#define BTA_DM_INQ_RES_IGNORE_RSSI BTM_INQ_RES_IGNORE_RSSI /* 0x7f RSSI value not supplied (ignore it) */ + +/* Structure associated with BTA_DM_INQ_RES_EVT */ +typedef struct { + BD_ADDR bd_addr; /* BD address peer device. */ + DEV_CLASS dev_class; /* Device class of peer device. */ + BOOLEAN remt_name_not_required; /* Application sets this flag if it already knows the name of the device */ + /* If the device name is known to application BTA skips the remote name request */ + BOOLEAN is_limited; /* TRUE, if the limited inquiry bit is set in the CoD */ + INT8 rssi; /* The rssi value */ + UINT8 *p_eir; /* Received EIR */ +#if (BLE_INCLUDED == TRUE) + UINT8 inq_result_type; + UINT8 ble_addr_type; + tBTM_BLE_EVT_TYPE ble_evt_type; + tBT_DEVICE_TYPE device_type; + UINT8 flag; + UINT8 adv_data_len; + UINT8 scan_rsp_len; +#endif + +} tBTA_DM_INQ_RES; + +/* Structure associated with BTA_DM_INQ_CMPL_EVT */ +typedef struct { + UINT8 num_resps; /* Number of inquiry responses. */ +} tBTA_DM_INQ_CMPL; + +/* Structure associated with BTA_DM_INQ_DISCARD_NUM_EVT */ +typedef struct { + UINT32 num_dis; /* The number of inquiry discarded packets. */ +} tBTA_DM_INQ_DISCARD; + +/* Structure associated with BTA_DM_DI_DISC_CMPL_EVT */ +typedef struct { + BD_ADDR bd_addr; /* BD address peer device. */ + UINT8 num_record; /* Number of DI record */ + tBTA_STATUS result; +} tBTA_DM_DI_DISC_CMPL; + +/* Structure associated with BTA_DM_DISC_RES_EVT */ +typedef struct { + BD_ADDR bd_addr; /* BD address peer device. */ + BD_NAME bd_name; /* Name of peer device. */ + tBTA_SERVICE_MASK services; /* Services found on peer device. */ +// btla-specific ++ + UINT8 *p_raw_data; /* Raw data for discovery DB */ + UINT32 raw_data_size; /* Size of raw data */ + tBT_DEVICE_TYPE device_type; /* device type in case it is BLE device */ + UINT32 num_uuids; + UINT8 *p_uuid_list; +// btla-specific -- + tBTA_STATUS result; +} tBTA_DM_DISC_RES; + +/* Structure associated with tBTA_DM_DISC_BLE_RES */ +typedef struct { + BD_ADDR bd_addr; /* BD address peer device. */ + BD_NAME bd_name; /* Name of peer device. */ + tBT_UUID service; /* GATT based Services UUID found on peer device. */ +} tBTA_DM_DISC_BLE_RES; + +/* Structure associated with tBTA_DM_RMTNAME_CMPL */ +typedef struct { + BD_ADDR bd_addr; + BD_NAME bd_name; + tBTA_CMPL_CB *read_rmtname_cb; +} tBTA_DM_RMTNAME_CMPL; + +/* Union of all search callback structures */ +typedef union { + tBTA_DM_INQ_RES inq_res; /* Inquiry result for a peer device. */ + tBTA_DM_INQ_CMPL inq_cmpl; /* Inquiry complete. */ + tBTA_DM_DISC_RES disc_res; /* Discovery result for a peer device. */ + tBTA_DM_DISC_BLE_RES disc_ble_res; /* Discovery result for GATT based service */ + tBTA_DM_DI_DISC_CMPL di_disc; /* DI discovery result for a peer device */ + tBTA_DM_INQ_DISCARD inq_dis; /* the discarded packets information of inquiry */ + tBTA_DM_RMTNAME_CMPL rmt_name; /* the remote name information */ +} tBTA_DM_SEARCH; + +/* Structure of search callback event and structures */ +typedef struct { + tBTA_DM_SEARCH_EVT event; /* Search callback events */ + UINT16 len; /* Length of p_data */ + tBTA_DM_SEARCH *p_data; /* Union of all search callback structures */ +} tBTA_DM_SEARCH_PARAM; + +/* Search callback */ +typedef void (tBTA_DM_SEARCH_CBACK)(tBTA_DM_SEARCH_EVT event, tBTA_DM_SEARCH *p_data); + +/* Execute call back */ +typedef void (tBTA_DM_EXEC_CBACK) (void *p_param); + +/* Encryption callback*/ +typedef void (tBTA_DM_ENCRYPT_CBACK) (BD_ADDR bd_addr, tBTA_TRANSPORT transport, tBTA_STATUS result); + +/* Relate to ESP_BLE_SEC_xxx in esp_gatt_defs.h */ +#if BLE_INCLUDED == TRUE +#define BTA_DM_BLE_SEC_NONE BTM_BLE_SEC_NONE +#define BTA_DM_BLE_SEC_ENCRYPT BTM_BLE_SEC_ENCRYPT +#define BTA_DM_BLE_SEC_NO_MITM BTM_BLE_SEC_ENCRYPT_NO_MITM +#define BTA_DM_BLE_SEC_MITM BTM_BLE_SEC_ENCRYPT_MITM +typedef tBTM_BLE_SEC_ACT tBTA_DM_BLE_SEC_ACT; + +typedef tBTM_BLE_TX_TIME_MS tBTA_DM_BLE_TX_TIME_MS; +typedef tBTM_BLE_RX_TIME_MS tBTA_DM_BLE_RX_TIME_MS; +typedef tBTM_BLE_IDLE_TIME_MS tBTA_DM_BLE_IDLE_TIME_MS; +typedef tBTM_BLE_ENERGY_USED tBTA_DM_BLE_ENERGY_USED; + +#define BTA_DM_CONTRL_UNKNOWN 0 /* Unknown state */ +#define BTA_DM_CONTRL_ACTIVE 1 /* ACL link on, SCO link ongoing, sniff mode */ +#define BTA_DM_CONTRL_SCAN 2 /* Scan state - paging/inquiry/trying to connect*/ +#define BTA_DM_CONTRL_IDLE 3 /* Idle state - page scan, LE advt, inquiry scan */ + +typedef UINT8 tBTA_DM_CONTRL_STATE; + +typedef UINT8 tBTA_DM_BLE_ADV_STATE; +typedef UINT8 tBTA_DM_BLE_ADV_INFO_PRESENT; +typedef UINT8 tBTA_DM_BLE_RSSI_VALUE; +typedef UINT16 tBTA_DM_BLE_ADV_INFO_TIMESTAMP; + +typedef tBTM_BLE_TRACK_ADV_DATA tBTA_DM_BLE_TRACK_ADV_DATA; + +typedef void (tBTA_BLE_SCAN_THRESHOLD_CBACK)(tBTA_DM_BLE_REF_VALUE ref_value); + +typedef void (tBTA_BLE_SCAN_REP_CBACK) (tBTA_DM_BLE_REF_VALUE ref_value, UINT8 report_format, + UINT8 num_records, UINT16 data_len, + UINT8 *p_rep_data, tBTA_STATUS status); + +typedef void (tBTA_BLE_SCAN_SETUP_CBACK) (tBTA_BLE_BATCH_SCAN_EVT evt, + tBTA_DM_BLE_REF_VALUE ref_value, + tBTA_STATUS status); + +typedef void (tBTA_START_STOP_SCAN_CMPL_CBACK) (tBTA_STATUS status); + +typedef void (tBTA_START_STOP_ADV_CMPL_CBACK) (tBTA_STATUS status); + +typedef void (tBTA_CLEAR_ADV_CMPL_CBACK) (tBTA_STATUS status); + +typedef void (tBTA_BLE_TRACK_ADV_CMPL_CBACK)(int action, tBTA_STATUS status, + tBTA_DM_BLE_PF_AVBL_SPACE avbl_space, + tBTA_DM_BLE_REF_VALUE ref_value); + +typedef void (tBTA_BLE_TRACK_ADV_CBACK)(tBTA_DM_BLE_TRACK_ADV_DATA *p_adv_data); + +typedef void (tBTA_BLE_ENERGY_INFO_CBACK)(tBTA_DM_BLE_TX_TIME_MS tx_time, + tBTA_DM_BLE_RX_TIME_MS rx_time, + tBTA_DM_BLE_IDLE_TIME_MS idle_time, + tBTA_DM_BLE_ENERGY_USED energy_used, + tBTA_DM_CONTRL_STATE ctrl_state, + tBTA_STATUS status); + +#else +typedef UINT8 tBTA_DM_BLE_SEC_ACT; +#endif + +/* Maximum service name length */ +#define BTA_SERVICE_NAME_LEN 35 +#define BTA_SERVICE_DESP_LEN BTA_SERVICE_NAME_LEN +#define BTA_PROVIDER_NAME_LEN BTA_SERVICE_NAME_LEN + + +/* link policy masks */ +#define BTA_DM_LP_SWITCH HCI_ENABLE_MASTER_SLAVE_SWITCH +#define BTA_DM_LP_HOLD HCI_ENABLE_HOLD_MODE +#define BTA_DM_LP_SNIFF HCI_ENABLE_SNIFF_MODE +#define BTA_DM_LP_PARK HCI_ENABLE_PARK_MODE +typedef UINT16 tBTA_DM_LP_MASK; + +/* power mode actions */ +#define BTA_DM_PM_NO_ACTION 0x00 /* no change to the current pm setting */ +#define BTA_DM_PM_PARK 0x10 /* prefers park mode */ +#define BTA_DM_PM_SNIFF 0x20 /* prefers sniff mode */ +#define BTA_DM_PM_SNIFF1 0x21 /* prefers sniff1 mode */ +#define BTA_DM_PM_SNIFF2 0x22 /* prefers sniff2 mode */ +#define BTA_DM_PM_SNIFF3 0x23 /* prefers sniff3 mode */ +#define BTA_DM_PM_SNIFF4 0x24 /* prefers sniff4 mode */ +#define BTA_DM_PM_SNIFF5 0x25 /* prefers sniff5 mode */ +#define BTA_DM_PM_SNIFF6 0x26 /* prefers sniff6 mode */ +#define BTA_DM_PM_SNIFF7 0x27 /* prefers sniff7 mode */ +#define BTA_DM_PM_SNIFF_USER0 0x28 /* prefers user-defined sniff0 mode (testtool only) */ +#define BTA_DM_PM_SNIFF_USER1 0x29 /* prefers user-defined sniff1 mode (testtool only) */ +#define BTA_DM_PM_ACTIVE 0x40 /* prefers active mode */ +#define BTA_DM_PM_RETRY 0x80 /* retry power mode based on current settings */ +#define BTA_DM_PM_SUSPEND 0x04 /* prefers suspend mode */ +#define BTA_DM_PM_NO_PREF 0x01 /* service has no preference on power mode setting. eg. connection to service got closed */ + +typedef UINT8 tBTA_DM_PM_ACTION; + +/* index to bta_dm_ssr_spec */ +#define BTA_DM_PM_SSR0 0 +#define BTA_DM_PM_SSR1 1 /* BTA_DM_PM_SSR1 will be dedicated for + HH SSR setting entry, no other profile can use it */ +#define BTA_DM_PM_SSR2 2 +#define BTA_DM_PM_SSR3 3 +#define BTA_DM_PM_SSR4 4 +#define BTA_DM_PM_SSR5 5 +#define BTA_DM_PM_SSR6 6 + +#define BTA_DM_PM_NUM_EVTS 9 + +#ifndef BTA_DM_PM_PARK_IDX +#define BTA_DM_PM_PARK_IDX 5 /* the actual index to bta_dm_pm_md[] for PARK mode */ +#endif + +#ifndef BTA_DM_PM_SNIFF_A2DP_IDX +#define BTA_DM_PM_SNIFF_A2DP_IDX BTA_DM_PM_SNIFF +#endif + +#ifndef BTA_DM_PM_SNIFF_AVK_IDLE_IDX +#define BTA_DM_PM_SNIFF_AVK_IDLE_IDX BTA_DM_PM_SNIFF4 +#endif + +#ifndef BTA_DM_PM_SNIFF_JV_IDX +#define BTA_DM_PM_SNIFF_JV_IDX BTA_DM_PM_SNIFF +#endif + +#ifndef BTA_DM_PM_SNIFF_HD_IDLE_IDX +#define BTA_DM_PM_SNIFF_HD_IDLE_IDX BTA_DM_PM_SNIFF4 +#endif + +#ifndef BTA_DM_PM_SNIFF_SCO_OPEN_IDX +#define BTA_DM_PM_SNIFF_SCO_OPEN_IDX BTA_DM_PM_SNIFF3 +#endif + +#ifndef BTA_DM_PM_SNIFF_HD_ACTIVE_IDX +#define BTA_DM_PM_SNIFF_HD_ACTIVE_IDX BTA_DM_PM_SNIFF5 +#endif + +#ifndef BTA_DM_PM_SNIFF_HH_OPEN_IDX +#define BTA_DM_PM_SNIFF_HH_OPEN_IDX BTA_DM_PM_SNIFF4 +#endif + +#ifndef BTA_DM_PM_SNIFF_HH_ACTIVE_IDX +#define BTA_DM_PM_SNIFF_HH_ACTIVE_IDX BTA_DM_PM_SNIFF4 +#endif + +#ifndef BTA_DM_PM_SNIFF_HH_IDLE_IDX +#define BTA_DM_PM_SNIFF_HH_IDLE_IDX BTA_DM_PM_SNIFF4 +#endif + +#ifndef BTA_DM_PM_HH_OPEN_DELAY +#define BTA_DM_PM_HH_OPEN_DELAY 30000 +#endif + +#ifndef BTA_DM_PM_HH_ACTIVE_DELAY +#define BTA_DM_PM_HH_ACTIVE_DELAY 30000 +#endif + +#ifndef BTA_DM_PM_HH_IDLE_DELAY +#define BTA_DM_PM_HH_IDLE_DELAY 30000 +#endif + +/* The Sniff Parameters defined below must be ordered from highest + * latency (biggest interval) to lowest latency. If there is a conflict + * among the connected services the setting with the lowest latency will + * be selected. If a device should override a sniff parameter then it + * must insure that order is maintained. + */ +#ifndef BTA_DM_PM_SNIFF_MAX +#define BTA_DM_PM_SNIFF_MAX 800 +#define BTA_DM_PM_SNIFF_MIN 400 +#define BTA_DM_PM_SNIFF_ATTEMPT 4 +#define BTA_DM_PM_SNIFF_TIMEOUT 1 +#endif + +#ifndef BTA_DM_PM_SNIFF1_MAX +#define BTA_DM_PM_SNIFF1_MAX 400 +#define BTA_DM_PM_SNIFF1_MIN 200 +#define BTA_DM_PM_SNIFF1_ATTEMPT 4 +#define BTA_DM_PM_SNIFF1_TIMEOUT 1 +#endif + +#ifndef BTA_DM_PM_SNIFF2_MAX +#define BTA_DM_PM_SNIFF2_MAX 180 //54 +#define BTA_DM_PM_SNIFF2_MIN 150 //30 +#define BTA_DM_PM_SNIFF2_ATTEMPT 4 +#define BTA_DM_PM_SNIFF2_TIMEOUT 1 +#endif + +#ifndef BTA_DM_PM_SNIFF3_MAX +#define BTA_DM_PM_SNIFF3_MAX 150 +#define BTA_DM_PM_SNIFF3_MIN 50 +#define BTA_DM_PM_SNIFF3_ATTEMPT 4 +#define BTA_DM_PM_SNIFF3_TIMEOUT 1 +#endif + +#ifndef BTA_DM_PM_SNIFF4_MAX +#define BTA_DM_PM_SNIFF4_MAX 54 //18 +#define BTA_DM_PM_SNIFF4_MIN 30 //10 +#define BTA_DM_PM_SNIFF4_ATTEMPT 4 +#define BTA_DM_PM_SNIFF4_TIMEOUT 1 +#endif + +#ifndef BTA_DM_PM_SNIFF5_MAX +#define BTA_DM_PM_SNIFF5_MAX 18 +#define BTA_DM_PM_SNIFF5_MIN 10 +#define BTA_DM_PM_SNIFF5_ATTEMPT 4 +#define BTA_DM_PM_SNIFF5_TIMEOUT 1 +#endif + +#ifndef BTA_DM_PM_PARK_MAX +#define BTA_DM_PM_PARK_MAX 800 +#define BTA_DM_PM_PARK_MIN 400 +#define BTA_DM_PM_PARK_ATTEMPT 0 +#define BTA_DM_PM_PARK_TIMEOUT 0 +#endif + + +/* Switch callback events */ +#define BTA_DM_SWITCH_CMPL_EVT 0 /* Completion of the Switch API */ + +typedef UINT8 tBTA_DM_SWITCH_EVT; +typedef void (tBTA_DM_SWITCH_CBACK)(tBTA_DM_SWITCH_EVT event, tBTA_STATUS status); + +/* Audio routing out configuration */ +#define BTA_DM_ROUTE_NONE 0x00 /* No Audio output */ +#define BTA_DM_ROUTE_DAC 0x01 /* routing over analog output */ +#define BTA_DM_ROUTE_I2S 0x02 /* routing over digital (I2S) output */ +#define BTA_DM_ROUTE_BT_MONO 0x04 /* routing over SCO */ +#define BTA_DM_ROUTE_BT_STEREO 0x08 /* routing over BT Stereo */ +#define BTA_DM_ROUTE_HOST 0x10 /* routing over Host */ +#define BTA_DM_ROUTE_FMTX 0x20 /* routing over FMTX */ +#define BTA_DM_ROUTE_FMRX 0x40 /* routing over FMRX */ +#define BTA_DM_ROUTE_BTSNK 0x80 /* routing over BT SNK */ + +typedef UINT8 tBTA_DM_ROUTE_PATH; + +#if (SDP_INCLUDED == TRUE) +/* Device Identification (DI) data structure +*/ +/* Used to set the DI record */ +typedef tSDP_DI_RECORD tBTA_DI_RECORD; +/* Used to get the DI record */ +typedef tSDP_DI_GET_RECORD tBTA_DI_GET_RECORD; +/* SDP discovery database */ +typedef tSDP_DISCOVERY_DB tBTA_DISCOVERY_DB; +#endif ///SDP_INCLUDED == TRUE + +#ifndef BTA_DI_NUM_MAX +#define BTA_DI_NUM_MAX 3 +#endif + +/* Device features mask definitions */ +#define BTA_FEATURE_BYTES_PER_PAGE BTM_FEATURE_BYTES_PER_PAGE +#define BTA_EXT_FEATURES_PAGE_MAX BTM_EXT_FEATURES_PAGE_MAX +/* ACL type +*/ +#define BTA_DM_LINK_TYPE_BR_EDR 0x01 +#define BTA_DM_LINK_TYPE_LE 0x02 +#define BTA_DM_LINK_TYPE_ALL 0xFF +typedef UINT8 tBTA_DM_LINK_TYPE; + +#define IMMEDIATE_DELY_MODE 0x00 +#define ONFOUND_DELY_MODE 0x01 +#define BATCH_DELY_MODE 0x02 +#define ALLOW_ALL_FILTER 0x00 +#define LOWEST_RSSI_VALUE 129 +#if (BLE_50_FEATURE_SUPPORT == TRUE) +#define BTA_DM_BLE_GAP_SET_EXT_ADV_PROP_CONNECTABLE (1 << 0) +#define BTA_DM_BLE_GAP_SET_EXT_ADV_PROP_SCANNABLE (1 << 1) +#define BTA_DM_BLE_GAP_SET_EXT_ADV_PROP_DIRECTED (1 << 2) +#define BTA_DM_BLE_GAP_SET_EXT_ADV_PROP_HD_DIRECTED (1 << 3) +#define BTA_DM_BLE_GAP_SET_EXT_ADV_PROP_LEGACY (1 << 4) +#define BTA_DM_BLE_GAP_SET_EXT_ADV_PROP_ANON_ADV (1 << 5) +#define BTA_DM_BLE_GAP_SET_EXT_ADV_PROP_INCLUDE_TX_PWR (1 << 6) +#define BTA_DM_BLE_GAP_SET_EXT_ADV_PROP_MASK (0x7F) + +#define BTA_DM_BLE_GAP_SET_EXT_ADV_PROP_LEGACY_IND (ESP_BLE_GAP_SET_EXT_ADV_PROP_LEGACY |\ + ESP_BLE_GAP_SET_EXT_ADV_PROP_CONNECTABLE |\ + ESP_BLE_GAP_SET_EXT_ADV_PROP_SCANNABLE) +#define BTA_DM_BLE_GAP_SET_EXT_ADV_PROP_LEGACY_LD_DIR (ESP_BLE_GAP_SET_EXT_ADV_PROP_LEGACY |\ + ESP_BLE_GAP_SET_EXT_ADV_PROP_CONNECTABLE |\ + ESP_BLE_GAP_SET_EXT_ADV_PROP_DIRECTED) +#define BTA_DM_BLE_GAP_SET_EXT_ADV_PROP_LEGACY_HD_DIR (ESP_BLE_GAP_SET_EXT_ADV_PROP_LEGACY |\ + ESP_BLE_GAP_SET_EXT_ADV_PROP_CONNECTABLE |\ + ESP_BLE_GAP_SET_EXT_ADV_PROP_HD_DIRECTED) +#define BTA_DM_BLE_GAP_SET_EXT_ADV_PROP_LEGACY_SCAN (ESP_BLE_GAP_SET_EXT_ADV_PROP_LEGACY |\ + ESP_BLE_GAP_SET_EXT_ADV_PROP_SCANNABLE) +#define BTA_DM_BLE_GAP_SET_EXT_ADV_PROP_LEGACY_NONCONN (ESP_BLE_GAP_SET_EXT_ADV_PROP_LEGACY) +typedef UINT16 tBTA_DM_BLE_EXT_ADV_TYPE_MASK; + + +#define BTA_DM_BLE_GAP_PHY_1M 1 +#define BTA_DM_BLE_GAP_PHY_2M 2 +#define BTA_DM_BLE_GAP_PHY_CODED 3 +typedef UINT8 tBTA_DM_BLE_GAP_PHY; + +#define BTA_DM_BLE_GAP_PHY_NO_TX_PREF_MASK (0x01) +#define BTA_DM_BLE_GAP_PHY_NO_RX_PREF_MASK (0x02) +#define BTA_DM_BLE_GAP_PHY_1M_PREF_MASK (0x03) +#define BTA_DM_BLE_GAP_PHY_2M_PREF_MASK (0x04) +#define BTA_DM_BLE_GAP_PHY_CODED_PREF_MASK (0x05) +typedef UINT8 tBTA_DM_BLE_GAP_PHY_MASK; + +#define BTA_DM_BLE_GAP_EXT_SCAN_UNCODE_MASK 0x01 +#define BTA_DM_BLE_GAP_EXT_SCAN_CODE_MASK 0x02 +typedef UINT8 tBTA_DM_BLE_EXT_SCAN_CFG_MASK; + + +typedef struct { + tBTA_DM_BLE_EXT_ADV_TYPE_MASK type; + UINT32 interval_min; + UINT32 interval_max; + tBTA_BLE_ADV_CHNL_MAP channel_map; + tBLE_ADDR_TYPE own_addr_type; + tBLE_ADDR_TYPE peer_addr_type; + BD_ADDR peer_addr; + tBTA_BLE_AFP filter_policy; + INT8 tx_power; + tBTA_DM_BLE_GAP_PHY primary_phy; + UINT8 max_skip; + tBTA_DM_BLE_GAP_PHY secondary_phy; + UINT8 sid; + BOOLEAN scan_req_notif; +} tBTA_DM_BLE_GAP_EXT_ADV_PARAMS; + +typedef struct { + UINT8 instance; + int duration; + int max_events; +} tBTA_DM_BLE_EXT_ADV; + +typedef struct { + UINT16 interval_min; + UINT16 interval_max; + UINT8 properties; +} tBTA_DM_BLE_Periodic_Adv_Params; + +typedef struct { + UINT8 filter_policy; + #if (CONFIG_BT_BLE_FEAT_CREATE_SYNC_ENH) + UINT8 reports_disabled; + UINT8 filter_duplicates; + #endif + UINT8 sid; + tBLE_ADDR_TYPE addr_type; + BD_ADDR addr; + UINT16 skip; + UINT16 sync_timeout; +} tBTA_DM_BLE_Periodic_Sync_Params; + +typedef struct { + tBLE_SCAN_MODE scan_type; + UINT16 scan_interval; + UINT16 scan_window; +} tBTA_DM_BLE_EXT_SCAN_CFG; + +typedef struct { + tBLE_ADDR_TYPE own_addr_type; + UINT8 filter_policy; + UINT8 scan_duplicate; + tBTA_DM_BLE_EXT_SCAN_CFG_MASK cfg_mask; + tBTA_DM_BLE_EXT_SCAN_CFG uncoded_cfg; + tBTA_DM_BLE_EXT_SCAN_CFG coded_cfg; +} tBTA_DM_BLE_EXT_SCAN_PARAMS; + +typedef struct { + UINT16 scan_interval; + UINT16 scan_window; + UINT16 interval_min; + UINT16 interval_max; + UINT16 latency; + UINT16 supervision_timeout; + UINT16 min_ce_len; + UINT16 max_ce_len; +} tBTA_DM_BLE_CONN_PARAMS; + +#define BTA_DM_BLE_5_GAP_READ_PHY_COMPLETE_EVT BTM_BLE_5_GAP_READ_PHY_COMPLETE_EVT +#define BTA_DM_BLE_5_GAP_SET_PREFERED_DEFAULT_PHY_COMPLETE_EVT BTM_BLE_5_GAP_SET_PREFERED_DEFAULT_PHY_COMPLETE_EVT +#define BTA_DM_BLE_5_GAP_SET_PREFERED_PHY_COMPLETE_EVT BTM_BLE_5_GAP_SET_PREFERED_PHY_COMPLETE_EVT +#define BTA_DM_BLE_5_GAP_EXT_ADV_SET_RAND_ADDR_COMPLETE_EVT BTM_BLE_5_GAP_EXT_ADV_SET_RAND_ADDR_COMPLETE_EVT +#define BTA_DM_BLE_5_GAP_EXT_ADV_SET_PARAMS_COMPLETE_EVT BTM_BLE_5_GAP_EXT_ADV_SET_PARAMS_COMPLETE_EVT +#define BTA_DM_BLE_5_GAP_EXT_ADV_DATA_SET_COMPLETE_EVT BTM_BLE_5_GAP_EXT_ADV_DATA_SET_COMPLETE_EVT +#define BTA_DM_BLE_5_GAP_EXT_SCAN_RSP_DATA_SET_COMPLETE_EVT BTM_BLE_5_GAP_EXT_SCAN_RSP_DATA_SET_COMPLETE_EVT +#define BTA_DM_BLE_5_GAP_EXT_ADV_START_COMPLETE_EVT BTM_BLE_5_GAP_EXT_ADV_START_COMPLETE_EVT +#define BTA_DM_BLE_5_GAP_EXT_ADV_STOP_COMPLETE_EVT BTM_BLE_5_GAP_EXT_ADV_STOP_COMPLETE_EVT +#define BTA_DM_BLE_5_GAP_EXT_ADV_SET_REMOVE_COMPLETE_EVT BTM_BLE_5_GAP_EXT_ADV_SET_REMOVE_COMPLETE_EVT +#define BTA_DM_BLE_5_GAP_EXT_ADV_SET_CLEAR_COMPLETE_EVT BTM_BLE_5_GAP_EXT_ADV_SET_CLEAR_COMPLETE_EVT +#define BTA_DM_BLE_5_GAP_PERIODIC_ADV_SET_PARAMS_COMPLETE_EVT BTM_BLE_5_GAP_PERIODIC_ADV_SET_PARAMS_COMPLETE_EVT +#define BTA_DM_BLE_5_GAP_PERIODIC_ADV_DATA_SET_COMPLETE_EVT BTM_BLE_5_GAP_PERIODIC_ADV_DATA_SET_COMPLETE_EVT +#define BTA_DM_BLE_5_GAP_PERIODIC_ADV_START_COMPLETE_EVT BTM_BLE_5_GAP_PERIODIC_ADV_START_COMPLETE_EVT +#define BTA_DM_BLE_5_GAP_PERIODIC_ADV_STOP_COMPLETE_EVT BTM_BLE_5_GAP_PERIODIC_ADV_STOP_COMPLETE_EVT +#define BTA_DM_BLE_5_GAP_PERIODIC_ADV_CREATE_SYNC_COMPLETE_EVT BTM_BLE_5_GAP_PERIODIC_ADV_CREATE_SYNC_COMPLETE_EVT +#define BTA_DM_BLE_5_GAP_PERIODIC_ADV_SYNC_CANCEL_COMPLETE_EVT BTM_BLE_5_GAP_PERIODIC_ADV_SYNC_CANCEL_COMPLETE_EVT +#define BTA_DM_BLE_5_GAP_PERIODIC_ADV_SYNC_TERMINATE_COMPLETE_EVT BTM_BLE_5_GAP_PERIODIC_ADV_SYNC_TERMINATE_COMPLETE_EVT +#define BTA_DM_BLE_5_GAP_PERIODIC_ADV_ADD_DEV_COMPLETE_EVT BTM_BLE_5_GAP_PERIODIC_ADV_ADD_DEV_COMPLETE_EVT +#define BTA_DM_BLE_5_GAP_PERIODIC_ADV_REMOVE_DEV_COMPLETE_EVT BTM_BLE_5_GAP_PERIODIC_ADV_REMOVE_DEV_COMPLETE_EVT +#define BTA_DM_BLE_5_GAP_PERIODIC_ADV_CLEAR_DEV_COMPLETE_EVT BTM_BLE_5_GAP_PERIODIC_ADV_CLEAR_DEV_COMPLETE_EVT +#define BTA_DM_BLE_5_GAP_SET_EXT_SCAN_PARAMS_COMPLETE_EVT BTM_BLE_5_GAP_SET_EXT_SCAN_PARAMS_COMPLETE_EVT +#define BTA_DM_BLE_5_GAP_EXT_SCAN_START_COMPLETE_EVT BTM_BLE_5_GAP_EXT_SCAN_START_COMPLETE_EVT +#define BTA_DM_BLE_5_GAP_EXT_SCAN_STOP_COMPLETE_EVT BTM_BLE_5_GAP_EXT_SCAN_STOP_COMPLETE_EVT +#define BTA_DM_BLE_5_GAP_PREFER_EXT_CONN_PARAMS_SET_COMPLETE_EVT BTM_BLE_5_GAP_PREFER_EXT_CONN_PARAMS_SET_COMPLETE_EVT +#define BTA_DM_BLE_5_GAP_PHY_UPDATE_COMPLETE_EVT BTM_BLE_5_GAP_PHY_UPDATE_COMPLETE_EVT +#define BTA_DM_BLE_5_GAP_EXT_ADV_REPORT_EVT BTM_BLE_5_GAP_EXT_ADV_REPORT_EVT +#define BTA_DM_BLE_5_GAP_SCAN_TIMEOUT_EVT BTM_BLE_5_GAP_SCAN_TIMEOUT_EVT +#define BTA_DM_BLE_5_GAP_ADV_TERMINATED_EVT BTM_BLE_5_GAP_ADV_TERMINATED_EVT +#define BTA_DM_BLE_5_GAP_SCAN_REQ_RECEIVED_EVT BTM_BLE_5_GAP_SCAN_REQ_RECEIVED_EVT +#define BTA_DM_BLE_5_GAP_CHANNEL_SELETE_ALGORITHM_EVT BTM_BLE_5_GAP_CHANNEL_SELETE_ALGORITHM_EVT +#define BTA_DM_BLE_5_GAP_PERIODIC_ADV_REPORT_EVT BTM_BLE_5_GAP_PERIODIC_ADV_REPORT_EVT +#define BTA_DM_BLE_5_GAP_PERIODIC_ADV_SYNC_LOST_EVT BTM_BLE_5_GAP_PERIODIC_ADV_SYNC_LOST_EVT +#define BTA_DM_BLE_5_GAP_PERIODIC_ADV_SYNC_ESTAB_EVT BTM_BLE_5_GAP_PERIODIC_ADV_SYNC_ESTAB_EVT +#if (BLE_FEAT_PERIODIC_ADV_SYNC_TRANSFER == TRUE) +#define BTA_BLE_GAP_PERIODIC_ADV_RECV_ENABLE_COMPLETE_EVT BTM_BLE_GAP_PERIODIC_ADV_RECV_ENABLE_COMPLETE_EVT +#define BTA_BLE_GAP_PERIODIC_ADV_SYNC_TRANS_COMPLETE_EVT BTM_BLE_GAP_PERIODIC_ADV_SYNC_TRANS_COMPLETE_EVT +#define BTA_BLE_GAP_PERIODIC_ADV_SET_INFO_TRANS_COMPLETE_EVT BTM_BLE_GAP_PERIODIC_ADV_SET_INFO_TRANS_COMPLETE_EVT +#define BTA_BLE_GAP_SET_PAST_PARAMS_COMPLETE_EVT BTM_BLE_GAP_SET_PAST_PARAMS_COMPLETE_EVT +#define BTA_BLE_GAP_PERIODIC_ADV_SYNC_TRANS_RECV_EVT BTM_BLE_GAP_PERIODIC_ADV_SYNC_TRANS_RECV_EVT +#endif // #if (BLE_FEAT_PERIODIC_ADV_SYNC_TRANSFER == TRUE) +#define BTA_DM_BLE_5_GAP_UNKNOWN_EVT BTM_BLE_5_GAP_UNKNOWN_EVT +typedef tBTM_BLE_5_GAP_EVENT tBTA_DM_BLE_5_GAP_EVENT; + +typedef tBTM_BLE_5_GAP_CB_PARAMS tBTA_DM_BLE_5_GAP_CB_PARAMS; +typedef tBTM_BLE_5_HCI_CBACK tBTA_DM_BLE_5_HCI_CBCAK; + +extern tBTM_BLE_5_HCI_CBACK ble_5_hci_cb; + +#endif // #if (BLE_50_FEATURE_SUPPORT == TRUE) + +#if (BLE_FEAT_PERIODIC_ADV_SYNC_TRANSFER == TRUE) +typedef struct { + UINT8 mode; + UINT16 skip; + UINT16 sync_timeout; + UINT8 cte_type; +} tBTA_DM_BLE_PAST_PARAMS; +#endif // #if (BLE_FEAT_PERIODIC_ADV_SYNC_TRANSFER == TRUE) + +/***************************************************************************** +** External Function Declarations +*****************************************************************************/ +#ifdef __cplusplus +extern "C" +{ +#endif + +/******************************************************************************* +** +** Function BTA_EnableBluetooth +** +** Description This function initializes BTA and prepares BTA and the +** Bluetooth protocol stack for use. This function is +** typically called at startup or when Bluetooth services +** are required by the phone. This function must be called +** before calling any other API function. +** +** +** Returns BTA_SUCCESS if successful. +** BTA_FAIL if internal failure. +** +*******************************************************************************/ +extern tBTA_STATUS BTA_EnableBluetooth(tBTA_DM_SEC_CBACK *p_cback); + +/******************************************************************************* +** +** Function BTA_DisableBluetooth +** +** Description This function disables BTA and the Bluetooth protocol +** stack. It is called when BTA is no longer being used +** by any application in the system. +** +** +** Returns void +** +*******************************************************************************/ +extern tBTA_STATUS BTA_DisableBluetooth(void); + +/******************************************************************************* +** +** Function BTA_EnableTestMode +** +** Description Enables bluetooth device under test mode +** +** +** Returns tBTA_STATUS +** +*******************************************************************************/ +extern tBTA_STATUS BTA_EnableTestMode(void); + +/******************************************************************************* +** +** Function BTA_DisableTestMode +** +** Description Disable bluetooth device under test mode +** +** +** Returns None +** +*******************************************************************************/ +extern void BTA_DisableTestMode(void); + +/******************************************************************************* +** +** Function BTA_DmSetDeviceName +** +** Description This function sets the Bluetooth name of the local device. +** +** +** Returns void +** +*******************************************************************************/ +extern void BTA_DmSetDeviceName(const char *p_name); + +/******************************************************************************* +** +** Function BTA_DmGetDeviceName +** +** Description This function gets the Bluetooth name of the local device. +** +** +** Returns void +** +*******************************************************************************/ +extern void BTA_DmGetDeviceName(tBTA_GET_DEV_NAME_CBACK *p_cback); + +/******************************************************************************* +** +** Function BTA_DmCfgCoexStatus +** +** Description This function configures coexist status. +** +** +** Returns void +** +*******************************************************************************/ +#if (ESP_COEX_VSC_INCLUDED == TRUE) +extern void BTA_DmCfgCoexStatus(UINT8 op, UINT8 type, UINT8 status); +#endif + +/******************************************************************************* +** +** Function BTA_DmGetRemoteName +** +** Description This function gets the peer device's Bluetooth name. +** +** +** Returns void +** +*******************************************************************************/ +extern void BTA_DmGetRemoteName(BD_ADDR remote_addr, tBTA_CMPL_CB *read_remote_name_cb); + +/******************************************************************************* +** +** Function BTA_DmConfigEir +** +** Description This function config EIR data of the local device. +** +** +** Returns void +** +*******************************************************************************/ +extern void BTA_DmConfigEir(tBTA_DM_EIR_CONF *eir_config); + +/******************************************************************************* +** +** Function BTA_DmSetAfhChannels +** +** Description This function sets the AFH channels +** +** +** Returns void +** +*******************************************************************************/ +void BTA_DmSetAfhChannels(const uint8_t *channels, tBTA_CMPL_CB *set_afh_cb); + +#if (BTA_DM_QOS_INCLUDED == TRUE) +/******************************************************************************* +** +** Function BTA_DmSetQos +** +** Description This function sets the QOS +** +** +** Returns void +** +*******************************************************************************/ +void BTA_DmSetQos(BD_ADDR bd_addr, UINT32 t_poll, tBTM_CMPL_CB *p_cb); +#endif /// (BTA_DM_QOS_INCLUDED == TRUE) + +/******************************************************************************* +** +** Function BTA_DmSetPageTimeout +** +** Description This function sets the Bluetooth page timeout. +** +** +** Returns void +** +*******************************************************************************/ +void BTA_DmSetPageTimeout(UINT16 page_to, tBTM_CMPL_CB *p_cb); +/******************************************************************************* +** +** Function BTA_DmGetPageTimeout +** +** Description This function gets the Bluetooth page timeout. +** +** +** Returns void +** +*******************************************************************************/ +void BTA_DmGetPageTimeout(tBTM_CMPL_CB *p_cb); + +/******************************************************************************* +** +** Function BTA_DmSetAclPktTypes +** +** Description This function sets the packet types used for ACL traffic. +** +** +** Returns void +** +*******************************************************************************/ +void BTA_DmSetAclPktTypes(BD_ADDR remote_addr, UINT16 pkt_types, tBTM_CMPL_CB *p_cb); + +#if (BLE_INCLUDED == TRUE) +/******************************************************************************* +** +** Function BTA_DmBleSetChannels +** +** Description This function sets BLE channels +** +** +** Returns void +** +*******************************************************************************/ +void BTA_DmBleSetChannels(const uint8_t *channels, tBTA_CMPL_CB *set_channels_cb); + +extern void BTA_DmUpdateWhiteList(BOOLEAN add_remove, BD_ADDR remote_addr, tBLE_ADDR_TYPE addr_type, tBTA_UPDATE_WHITELIST_CBACK *update_wl_cb); + +extern void BTA_DmClearWhiteList(tBTA_UPDATE_WHITELIST_CBACK *update_wl_cb); + +extern void BTA_DmBleReadAdvTxPower(tBTA_CMPL_CB *cmpl_cb); +#endif ///BLE_INCLUDED == TRUE + +extern void BTA_DmReadRSSI(BD_ADDR remote_addr, tBTA_TRANSPORT transport, tBTA_CMPL_CB *cmpl_cb); + +/******************************************************************************* +** +** Function BTA_DmSetVisibility +** +** Description This function sets the Bluetooth connectable,discoverable, +** pairable and conn paired only modesmodes of the local device. +** This controls whether other Bluetooth devices can find and connect to +** the local device. +** +** +** Returns void +** +*******************************************************************************/ +extern void BTA_DmSetVisibility(tBTA_DM_DISC disc_mode, tBTA_DM_CONN conn_mode, UINT8 pairable_mode, UINT8 conn_filter); + +/******************************************************************************* +** +** Function BTA_DmSearch +** +** Description This function searches for peer Bluetooth devices. It +** first performs an inquiry; for each device found from the +** inquiry it gets the remote name of the device. If +** parameter services is nonzero, service discovery will be +** performed on each device for the services specified. +** +** +** Returns void +** +*******************************************************************************/ +extern void BTA_DmSearch(tBTA_DM_INQ *p_dm_inq, tBTA_SERVICE_MASK services, + tBTA_DM_SEARCH_CBACK *p_cback); + +/******************************************************************************* +** +** Function BTA_DmSearchCancel +** +** Description This function cancels a search that has been initiated +** by calling BTA_DmSearch(). +** +** +** Returns void +** +*******************************************************************************/ +extern void BTA_DmSearchCancel(void); + +/******************************************************************************* +** +** Function BTA_DmDiscover +** +** Description This function performs service discovery for the services +** of a particular peer device. +** +** +** Returns void +** +*******************************************************************************/ +#if (SDP_INCLUDED == TRUE) +extern void BTA_DmDiscover(BD_ADDR bd_addr, tBTA_SERVICE_MASK services, + tBTA_DM_SEARCH_CBACK *p_cback, BOOLEAN sdp_search); +// btla-specific ++ +/******************************************************************************* +** +** Function BTA_DmDiscoverUUID +** +** Description This function performs service discovery for the services +** of a particular peer device. +** +** +** Returns void +** +*******************************************************************************/ +extern void BTA_DmDiscoverUUID(BD_ADDR bd_addr, tSDP_UUID *uuid, + tBTA_DM_SEARCH_CBACK *p_cback, BOOLEAN sdp_search); +#endif ///SDP_INCLUDED == TRUE +/******************************************************************************* +** +** Function BTA_DmGetCachedRemoteName +** +** Description Retieve cached remote name if available +** +** Returns BTA_SUCCESS if cached name was retrieved +** BTA_FAILURE if cached name is not available +** +*******************************************************************************/ +tBTA_STATUS BTA_DmGetCachedRemoteName(BD_ADDR remote_device, UINT8 **pp_cached_name); +// btla-specific -- + +/******************************************************************************* +** +** Function BTA_DmBond +** +** Description This function initiates a bonding procedure with a peer +** device. The bonding procedure enables authentication +** and optionally encryption on the Bluetooth link. +** +** +** Returns void +** +*******************************************************************************/ +extern void BTA_DmBond(BD_ADDR bd_addr); + +/******************************************************************************* +** +** Function BTA_DmBondByTransport +** +** Description This function initiates a bonding procedure with a peer +** device by designated transport. The bonding procedure enables +** authentication and optionally encryption on the Bluetooth link. +** +** +** Returns void +** +*******************************************************************************/ +extern void BTA_DmBondByTransport(BD_ADDR bd_addr, tBTA_TRANSPORT transport); + + +/******************************************************************************* +** +** Function BTA_DmBondCancel +** +** Description This function cancels a bonding procedure with a peer +** device. +** +** +** Returns void +** +*******************************************************************************/ +extern void BTA_DmBondCancel(BD_ADDR bd_addr); + +/******************************************************************************* +** +** Function BTA_DMSetPinType +** +** Description This function sets pin type as BTM_PIN_TYPE_FIXED or BTM_PIN_TYPE_VARIABLE +** +** +** Returns void +** +*******************************************************************************/ +extern void BTA_DMSetPinType (UINT8 pin_type, UINT8 *pin_code, UINT8 pin_code_len); + +/******************************************************************************* +** +** Function BTA_DmPinReply +** +** Description This function provides a PIN when one is requested by DM +** during a bonding procedure. The application should call +** this function after the security callback is called with +** a BTA_DM_PIN_REQ_EVT. +** +** +** Returns void +** +*******************************************************************************/ +extern void BTA_DmPinReply(BD_ADDR bd_addr, BOOLEAN accept, UINT8 pin_len, + UINT8 *p_pin); + +#if (BTM_OOB_INCLUDED == TRUE) +/******************************************************************************* +** +** Function BTA_DmLocalOob +** +** Description This function retrieves the OOB data from local controller. +** The result is reported by bta_dm_co_loc_oob(). +** +** Returns void +** +*******************************************************************************/ +extern void BTA_DmLocalOob(void); + +/******************************************************************************* +** +** Function BTA_DmOobReply +** +** This function is called to provide the OOB data for +** SMP in response to BTA_LE_OOB_REQ_EVT +** +** Parameters: bd_addr - Address of the peer device +** len - length of simple pairing Randomizer C +** p_value - simple pairing Randomizer C. +** +** Returns void +** +*******************************************************************************/ +extern void BTA_DmOobReply(BD_ADDR bd_addr, UINT8 len, UINT8 *p_value); + +/******************************************************************************* +** +** Function BTA_DmSecureConnectionOobReply +** +** This function is called to provide the OOB data for +** SMP in response to BTA_LE_OOB_REQ_EVT when secure connection +** +** Parameters: bd_addr - Address of the peer device +** p_c - Pointer to Confirmation +** p_r - Pointer to Randomizer +** +** Returns void +** +*******************************************************************************/ +extern void BTA_DmSecureConnectionOobReply(BD_ADDR bd_addr, UINT8 *p_c, UINT8 *p_r); +/******************************************************************************* +** +** Function BTA_DmSecureConnectionCreateOobData +** +** This function is called to create the OOB data for +** SMP when secure connection +** +** Returns void +** +*******************************************************************************/ +extern void BTA_DmSecureConnectionCreateOobData(void); +#endif /* BTM_OOB_INCLUDED */ + +/******************************************************************************* +** +** Function BTA_DmConfirm +** +** Description This function accepts or rejects the numerical value of the +** Simple Pairing process on BTA_DM_SP_CFM_REQ_EVT +** +** Returns void +** +*******************************************************************************/ +extern void BTA_DmConfirm(BD_ADDR bd_addr, BOOLEAN accept); + +/******************************************************************************* +** +** Function BTA_DmPasskeyReqReply +** +** Description This function is called to provide the passkey for +** Simple Pairing in response to BTA_DM_SP_KEY_REQ_EVT +** +** Returns void +** +*******************************************************************************/ +extern void BTA_DmPasskeyReqReply(BOOLEAN accept, BD_ADDR bd_addr, UINT32 passkey); + +/******************************************************************************* +** +** Function BTA_DmAddDevice +** +** Description This function adds a device to the security database list +** of peer devices. This function would typically be called +** at system startup to initialize the security database with +** known peer devices. This is a direct execution function +** that may lock task scheduling on some platforms. +** +** Returns void +** +*******************************************************************************/ +extern void BTA_DmAddDevice(BD_ADDR bd_addr, DEV_CLASS dev_class, + LINK_KEY link_key, tBTA_SERVICE_MASK trusted_mask, + BOOLEAN is_trusted, UINT8 key_type, + tBTA_IO_CAP io_cap, UINT8 pin_length, + UINT8 sc_support); + +/******************************************************************************* +** +** Function BTA_DmRemoveDevice +** +** Description This function removes a device from the security database. +** This is a direct execution function that may lock task +** scheduling on some platforms. +** +** +** Returns BTA_SUCCESS if successful. +** BTA_FAIL if operation failed. +** +*******************************************************************************/ +extern tBTA_STATUS BTA_DmRemoveDevice(BD_ADDR bd_addr, tBT_TRANSPORT transport); + +/******************************************************************************* +** +** Function BTA_GetEirService +** +** Description This function is called to get BTA service mask from EIR. +** +** Parameters p_eir - pointer of EIR significant part +** p_services - return the BTA service mask +** +** Returns None +** +*******************************************************************************/ +extern void BTA_GetEirService( UINT8 *p_eir, tBTA_SERVICE_MASK *p_services ); + +/******************************************************************************* +** +** Function BTA_DmGetConnectionState +** +** Description Returns whether the remote device is currently connected. +** +** Returns 0 if the device is NOT connected. +** +*******************************************************************************/ +extern UINT16 BTA_DmGetConnectionState( BD_ADDR bd_addr ); + +#if (SDP_INCLUDED == TRUE) +/******************************************************************************* +** +** Function BTA_DmSetLocalDiRecord +** +** Description This function adds a DI record to the local SDP database. +** +** Returns BTA_SUCCESS if record set successfully, otherwise error code. +** +*******************************************************************************/ +extern tBTA_STATUS BTA_DmSetLocalDiRecord( tBTA_DI_RECORD *p_device_info, + UINT32 *p_handle ); +#endif ///SDP_INCLUDED == TRUE +/******************************************************************************* +** +** +** Function BTA_DmCloseACL +** +** Description This function force to close an ACL connection and remove the +** device from the security database list of known devices. +** +** Parameters: bd_addr - Address of the peer device +** remove_dev - remove device or not after link down +** transport - which transport to close + +** +** Returns void. +** +*******************************************************************************/ +extern void BTA_DmCloseACL(BD_ADDR bd_addr, BOOLEAN remove_dev, tBTA_TRANSPORT transport); + +/******************************************************************************* +** +** Function bta_dmexecutecallback +** +** Description This function will request BTA to execute a call back in the context of BTU task +** This API was named in lower case because it is only intended +** for the internal customers(like BTIF). +** +** Returns void +** +*******************************************************************************/ +extern void bta_dmexecutecallback (tBTA_DM_EXEC_CBACK *p_callback, void *p_param); + +#if (BTM_SCO_HCI_INCLUDED == TRUE) +/******************************************************************************* +** +** Function BTA_DmPcmInitSamples +** +** Description initialize the down sample converter. +** +** src_sps: original samples per second (source audio data) +** (ex. 44100, 48000) +** bits: number of bits per pcm sample (16) +** n_channels: number of channels (i.e. mono(1), stereo(2)...) +** +** Returns none +** +*******************************************************************************/ +extern void BTA_DmPcmInitSamples (UINT32 src_sps, UINT32 bits, UINT32 n_channels); + +/******************************************************************************* +** +** Function BTA_DmPcmDeinitSamples +** +** Description Deinitialize the down sample converter. +** +** Returns none +** +*******************************************************************************/ +extern void BTA_DmPcmDeinitSamples(void); + +/************************************************************************************** +** Function BTA_DmPcmResample +** +** Description Down sampling utility to convert higher sampling rate into 8K/16bits +** PCM samples. +** +** Parameters p_src: pointer to the buffer where the original sampling PCM +** are stored. +** in_bytes: Length of the input PCM sample buffer in byte. +** p_dst: pointer to the buffer which is to be used to store +** the converted PCM samples. +** +** +** Returns INT32: number of samples converted. +** +**************************************************************************************/ +extern INT32 BTA_DmPcmResample (void *p_src, UINT32 in_bytes, void *p_dst); +#endif + +#if ((defined BLE_INCLUDED) && (BLE_INCLUDED == TRUE)) +/* BLE related API functions */ +/******************************************************************************* +** +** Function BTA_DmBleSecurityGrant +** +** Description Grant security request access. +** +** Parameters: bd_addr - BD address of the peer +** res - security grant status. +** +** Returns void +** +*******************************************************************************/ +extern void BTA_DmBleSecurityGrant(BD_ADDR bd_addr, tBTA_DM_BLE_SEC_GRANT res); + + + +/******************************************************************************* +** +** Function BTA_DmBleSetBgConnType +** +** Description This function is called to set BLE connectable mode for a +** peripheral device. +** +** Parameters bg_conn_type: it can be auto connection, or selective connection. +** p_select_cback: callback function when selective connection procedure +** is being used. +** +** Returns void +** +*******************************************************************************/ +extern void BTA_DmBleSetBgConnType(tBTA_DM_BLE_CONN_TYPE bg_conn_type, tBTA_DM_BLE_SEL_CBACK *p_select_cback); + +/******************************************************************************* +** +** Function BTA_DmBlePasskeyReply +** +** Description Send BLE SMP passkey reply. +** +** Parameters: bd_addr - BD address of the peer +** accept - passkey entry sucessful or declined. +** passkey - passkey value, must be a 6 digit number, +** can be lead by 0. +** +** Returns void +** +*******************************************************************************/ +extern void BTA_DmBlePasskeyReply(BD_ADDR bd_addr, BOOLEAN accept, UINT32 passkey); + +/******************************************************************************* +** +** Function BTA_DmBleSetStaticPasskey +** +** Description Set BLE SMP static passkey. +** +** Parameters: add - add static passkey when add is true +** clear static passkey when add is false +** passkey - static passkey value +** +** +** Returns void +** +*******************************************************************************/ +extern void BTA_DmBleSetStaticPasskey(bool add, uint32_t passkey); + +/******************************************************************************* +** +** Function BTA_DmBleConfirmReply +** +** Description Send BLE SMP SC user confirmation reply. +** +** Parameters: bd_addr - BD address of the peer +** accept - numbers to compare are the same or different. +** +** Returns void +** +*******************************************************************************/ +extern void BTA_DmBleConfirmReply(BD_ADDR bd_addr, BOOLEAN accept); + +/******************************************************************************* +** +** Function BTA_DmAddBleDevice +** +** Description Add a BLE device. This function will be normally called +** during host startup to restore all required information +** for a LE device stored in the NVRAM. +** +** Parameters: bd_addr - BD address of the peer +** dev_type - Remote device's device type. +** auth_mode - auth mode +** addr_type - LE device address type. +** +** Returns void +** +*******************************************************************************/ +extern void BTA_DmAddBleDevice(BD_ADDR bd_addr, tBLE_ADDR_TYPE addr_type, int auth_mode, + tBT_DEVICE_TYPE dev_type); + + +/******************************************************************************* +** +** Function BTA_DmAddBleKey +** +** Description Add/modify LE device information. This function will be +** normally called during host startup to restore all required +** information stored in the NVRAM. +** +** Parameters: bd_addr - BD address of the peer +** p_le_key - LE key values. +** key_type - LE SMP key type. +** +** Returns void +** +*******************************************************************************/ +extern void BTA_DmAddBleKey (BD_ADDR bd_addr, + tBTA_LE_KEY_VALUE *p_le_key, + tBTA_LE_KEY_TYPE key_type); + +/******************************************************************************* +** +** Function BTA_DmSetBlePrefConnParams +** +** Description This function is called to set the preferred connection +** parameters when default connection parameter is not desired. +** +** Parameters: bd_addr - BD address of the peripheral +** min_conn_int - minimum preferred connection interval +** max_conn_int - maximum preferred connection interval +** slave_latency - preferred slave latency +** supervision_tout - preferred supervision timeout +** +** +** Returns void +** +*******************************************************************************/ +extern void BTA_DmSetBlePrefConnParams(BD_ADDR bd_addr, + UINT16 min_conn_int, UINT16 max_conn_int, + UINT16 slave_latency, UINT16 supervision_tout ); + +/******************************************************************************* +** +** Function BTA_DmSetBleConnScanParams +** +** Description This function is called to set scan parameters used in +** BLE connection request +** +** Parameters: scan_interval - scan interval +** scan_window - scan window +** +** Returns void +** +*******************************************************************************/ +extern void BTA_DmSetBleConnScanParams(UINT32 scan_interval, + UINT32 scan_window); + +/******************************************************************************* +** +** Function BTA_DmSetBleScanParams +** +** Description This function is called to set scan parameters +** +** Parameters: client_if - Client IF +** scan_interval - scan interval +** scan_window - scan window +** scan_mode - scan mode +** scan_param_setup_status_cback - Set scan param status callback +** +** Returns void +** +*******************************************************************************/ +extern void BTA_DmSetBleScanParams(tGATT_IF client_if, UINT32 scan_interval, + UINT32 scan_window, tBLE_SCAN_MODE scan_mode, + tBLE_SCAN_PARAM_SETUP_CBACK scan_param_setup_status_cback); + + +/******************************************************************************* +** +** Function BTA_DmSetBleScanFilterParams +** +** Description This function is called to set scan parameters +** +** Parameters: client_if - Client IF +** scan_interval - scan interval +** scan_window - scan window +** scan_mode - scan mode +** scan_duplicate_filter - scan duplicate filter +** scan_param_setup_status_cback - Set scan param status callback +** +** Returns void +** +*******************************************************************************/ +extern void BTA_DmSetBleScanFilterParams(tGATT_IF client_if, UINT32 scan_interval, + UINT32 scan_window, tBLE_SCAN_MODE scan_mode, UINT8 scan_fil_poilcy, + UINT8 addr_type_own, UINT8 scan_duplicate_filter, tBLE_SCAN_PARAM_SETUP_CBACK scan_param_setup_cback); + + +/******************************************************************************* +** +** Function BTA_DmSetBleAdvParams +** +** Description This function sets the advertising parameters BLE functionality. +** It is to be called when device act in peripheral or broadcaster +** role. +** +** Parameters: adv_int_min - adv interval minimum +** adv_int_max - adv interval max +** p_dir_bda - directed adv initator address +** +** Returns void +** +*******************************************************************************/ +extern void BTA_DmSetBleAdvParams (UINT16 adv_int_min, UINT16 adv_int_max, + tBLE_BD_ADDR *p_dir_bda); + +extern void BTA_DmSetBleAdvParamsAll (UINT16 adv_int_min, UINT16 adv_int_max, + UINT8 adv_type, tBLE_ADDR_TYPE addr_type_own, + tBTM_BLE_ADV_CHNL_MAP chnl_map, tBTM_BLE_AFP adv_fil_pol, + tBLE_BD_ADDR *p_dir_bda, tBTA_START_ADV_CMPL_CBACK p_start_adv_cb); + + +/******************************************************************************* +** +** Function BTA_DmSearchExt +** +** Description This function searches for peer Bluetooth devices. It performs +** an inquiry and gets the remote name for devices. Service +** discovery is done if services is non zero +** +** Parameters p_dm_inq: inquiry conditions +** services: if service is not empty, service discovery will be done. +** for all GATT based service condition, put num_uuid, and +** p_uuid is the pointer to the list of UUID values. +** p_cback: callback functino when search is completed. +** +** +** +** Returns void +** +*******************************************************************************/ +extern void BTA_DmSearchExt(tBTA_DM_INQ *p_dm_inq, tBTA_SERVICE_MASK_EXT *p_services, + tBTA_DM_SEARCH_CBACK *p_cback); + +/******************************************************************************* +** +** Function BTA_DmDiscoverExt +** +** Description This function does service discovery for services of a +** peer device. When services.num_uuid is 0, it indicates all +** GATT based services are to be searched; other wise a list of +** UUID of interested services should be provided through +** services.p_uuid. +** +** +** +** Returns void +** +*******************************************************************************/ +extern void BTA_DmDiscoverExt(BD_ADDR bd_addr, tBTA_SERVICE_MASK_EXT *p_services, + tBTA_DM_SEARCH_CBACK *p_cback, BOOLEAN sdp_search); + +/******************************************************************************* +** +** Function BTA_DmDiscoverByTransport +** +** Description This function does service discovery on particular transport +** for services of a +** peer device. When services.num_uuid is 0, it indicates all +** GATT based services are to be searched; other wise a list of +** UUID of interested services should be provided through +** p_services->p_uuid. +** +** +** +** Returns void +** +*******************************************************************************/ +extern void BTA_DmDiscoverByTransport(BD_ADDR bd_addr, tBTA_SERVICE_MASK_EXT *p_services, + tBTA_DM_SEARCH_CBACK *p_cback, BOOLEAN sdp_search, + tBTA_TRANSPORT transport); + +/******************************************************************************* +** +** Function BTA_DmSetEncryption +** +** Description This function is called to ensure that connection is +** encrypted. Should be called only on an open connection. +** Typically only needed for connections that first want to +** bring up unencrypted links, then later encrypt them. +** +** Parameters: bd_addr - Address of the peer device +** transport - transport of the link to be encruypted +** p_callback - Pointer to callback function to indicat the +** link encryption status +** sec_act - This is the security action to indicate +** what knid of BLE security level is required for +** the BLE link if the BLE is supported +** Note: This parameter is ignored for the BR/EDR link +** or the BLE is not supported +** +** Returns void +** +** +*******************************************************************************/ +extern void BTA_DmSetEncryption(BD_ADDR bd_addr, tBTA_TRANSPORT transport, + tBTA_DM_ENCRYPT_CBACK *p_callback, + tBTA_DM_BLE_SEC_ACT sec_act); + + +/******************************************************************************* +** +** Function BTA_DmBleObserve +** +** Description This procedure keep the device listening for advertising +** events from a broadcast device. +** +** Parameters start: start or stop observe. +** duration : Duration of the scan. Continuous scan if 0 is passed +** p_results_cb: Callback to be called with scan results +** +** Returns void +** +*******************************************************************************/ +extern void BTA_DmBleObserve(BOOLEAN start, UINT32 duration, + tBTA_DM_SEARCH_CBACK *p_results_cb, + tBTA_START_STOP_SCAN_CMPL_CBACK *p_start_stop_scan_cb); + +/******************************************************************************* +** +** Function BTA_DmBleScan +** +** Description This procedure keep the device listening for advertising +** events from a broadcast device. +** +** Parameters start: start or stop observe. +** duration : Duration of the scan. Continuous scan if 0 is passed +** p_results_cb: Callback to be called with scan results +** +** Returns void +** +*******************************************************************************/ +extern void BTA_DmBleScan(BOOLEAN start, UINT32 duration, + tBTA_DM_SEARCH_CBACK *p_results_cb, + tBTA_START_STOP_SCAN_CMPL_CBACK *p_start_stop_scan_cb); + +extern void BTA_DmBleStopAdvertising(void); + +extern void BTA_DmSetRandAddress(BD_ADDR rand_addr, tBTA_SET_RAND_ADDR_CBACK *p_set_rand_addr_cback); +extern void BTA_DmClearRandAddress(void); + +#endif + +#if BLE_INCLUDED == TRUE +// btla-specific -- +/******************************************************************************* +** +** Function BTA_DmBleConfigLocalPrivacy +** +** Description Enable/disable privacy on the local device +** +** Parameters: privacy_enable - enable/disabe privacy on remote device. +** set_local_privacy_cback -callback to be called with result +** Returns void +** +*******************************************************************************/ +extern void BTA_DmBleConfigLocalPrivacy(BOOLEAN privacy_enable, tBTA_SET_LOCAL_PRIVACY_CBACK *set_local_privacy_cback); + +/******************************************************************************* +** +** Function BTA_DmBleConfigLocalIcon +** +** Description set gap local icon +** +** Parameters: icon - appearance value. +** +** Returns void +** +*******************************************************************************/ +extern void BTA_DmBleConfigLocalIcon(uint16_t icon); + +/******************************************************************************* +** +** Function BTA_DmBleEnableRemotePrivacy +** +** Description Enable/disable privacy on a remote device +** +** Parameters: bd_addr - BD address of the peer +** privacy_enable - enable/disabe privacy on remote device. +** +** Returns void +** +*******************************************************************************/ +extern void BTA_DmBleEnableRemotePrivacy(BD_ADDR bd_addr, BOOLEAN privacy_enable); + + +/******************************************************************************* +** +** Function BTA_DmBleSetAdvConfig +** +** Description This function is called to override the BTA default ADV parameters. +** +** Parameters Pointer to User defined ADV data structure +** +** Returns None +** +*******************************************************************************/ +extern void BTA_DmBleSetAdvConfig (tBTA_BLE_AD_MASK data_mask, + tBTA_BLE_ADV_DATA *p_adv_cfg, + tBTA_SET_ADV_DATA_CMPL_CBACK *p_adv_data_cback); + +/******************************************************************************* +** +** Function BTA_DmBleSetAdvConfigRaw +** +** Description This function is called to set raw Advertising data +** +** Parameters p_raw_adv : raw advertising data. +** raw_adv_len : raw advertising data length. +** p_adv_data_cback : set adv data complete callback. +** +** Returns None +** +*******************************************************************************/ +extern void BTA_DmBleSetAdvConfigRaw (UINT8 *p_raw_adv, UINT32 raw_adv_len, + tBTA_SET_ADV_DATA_CMPL_CBACK *p_adv_data_cback); + +/******************************************************************************* +** +** Function BTA_DmBleSetLongAdv +** +** Description This function is called to set long Advertising data +** +** Parameters adv_data : long advertising data. +** adv_data_len : long advertising data length. +** p_adv_data_cback : set long adv data complete callback. +** +** Returns None +** +*******************************************************************************/ +void BTA_DmBleSetLongAdv (UINT8 *adv_data, UINT32 adv_data_len, + tBTA_SET_ADV_DATA_CMPL_CBACK *p_adv_data_cback); + +/******************************************************************************* +** +** Function BTA_DmBleClearAdv +** +** Description This function is called to clear Advertising +** +** Parameters p_adv_data_cback : clear adv complete callback. +** +** Returns None +** +*******************************************************************************/ +void BTA_DmBleClearAdv (tBTA_CLEAR_ADV_CMPL_CBACK *p_clear_adv_cback); + +/******************************************************************************* +** +** Function BTA_DmBleSetScanRsp +** +** Description This function is called to override the BTA scan response. +** +** Parameters Pointer to User defined ADV data structure +** +** Returns None +** +*******************************************************************************/ +extern void BTA_DmBleSetScanRsp (tBTA_BLE_AD_MASK data_mask, + tBTA_BLE_ADV_DATA *p_adv_cfg, + tBTA_SET_ADV_DATA_CMPL_CBACK *p_adv_data_cback); + +/******************************************************************************* +** +** Function BTA_DmBleSetScanRspRaw +** +** Description This function is called to set raw scan response data +** +** Parameters p_raw_scan_rsp : raw scan_rspertising data. +** raw_scan_rsp_len : raw scan_rspertising data length. +** p_scan_rsp_data_cback : set scan_rsp data complete callback. +** +** Returns None +** +*******************************************************************************/ +extern void BTA_DmBleSetScanRspRaw (UINT8 *p_raw_scan_rsp, UINT32 raw_scan_rsp_len, + tBTA_SET_ADV_DATA_CMPL_CBACK *p_scan_rsp_data_cback); + +/******************************************************************************* +** +** Function BTA_DmUpdateDuplicateExceptionalList +** +** Description This function is called to update duplicate scan exceptional list +** +** Parameters subcode : add, remove or clean duplicate scan exceptional list. +** type : device info type. +** device_info: device info +** p_update_duplicate_ignore_list_cback : update complete callback. +** +** Returns None +** +*******************************************************************************/ +extern void BTA_DmUpdateDuplicateExceptionalList(UINT8 subcode, UINT32 type, + BD_ADDR device_info, + tBTA_UPDATE_DUPLICATE_EXCEPTIONAL_LIST_CMPL_CBACK p_update_duplicate_exceptional_list_cback); + +/******************************************************************************* +** +** Function BTA_DmBleBroadcast +** +** Description This function starts or stops LE broadcasting. +** +** Parameters start: start or stop broadcast. +** p_start_stop_adv_cb: stop broadcast completed event +** +** Returns None +** +*******************************************************************************/ +extern void BTA_DmBleBroadcast (BOOLEAN start, tBTA_START_STOP_ADV_CMPL_CBACK *p_start_stop_adv_cb); + + +/******************************************************************************* +** +** Function BTA_BleEnableAdvInstance +** +** Description This function enables the Multi ADV instance feature +** +** Parameters p_params Pointer to ADV param user defined structure +** p_cback Pointer to Multi ADV callback structure +** p_ref - Reference pointer +** +** Returns None +** +*******************************************************************************/ +extern void BTA_BleEnableAdvInstance (tBTA_BLE_ADV_PARAMS *p_params, + tBTA_BLE_MULTI_ADV_CBACK *p_cback, void *p_ref); + +/******************************************************************************* +** +** Function BTA_BleUpdateAdvInstParam +** +** Description This function updates the Multi ADV instance params +** +** Parameters inst_id Instance ID +** p_params Pointer to ADV param user defined structure +** +** Returns None +** +*******************************************************************************/ +extern void BTA_BleUpdateAdvInstParam (UINT8 inst_id, + tBTA_BLE_ADV_PARAMS *p_params); + +/******************************************************************************* +** +** Function BTA_BleCfgAdvInstData +** +** Description This function is called to configure the ADV instance data +** +** Parameters inst_id - Instance ID +** is_scan_rsp - Boolean value Scan response +** Pointer to User defined ADV data structure +** Returns None +** +*******************************************************************************/ +extern void BTA_BleCfgAdvInstData (UINT8 inst_id, BOOLEAN is_scan_rsp, + tBTA_BLE_AD_MASK data_mask, tBTA_BLE_ADV_DATA *p_data); + +/******************************************************************************* +** +** Function BTA_BleDisableAdvInstance +** +** Description This function is called to disable the ADV instance +** +** Parameters inst_id - Instance ID to be disabled +** +** Returns None +** +*******************************************************************************/ +extern void BTA_BleDisableAdvInstance(UINT8 inst_id); + +/******************************************************************************* +** +** Function BTA_DmBleUpdateConnectionParams +** +** Description Update connection parameters, can only be used when connection is up. +** +** Parameters: bd_addr - BD address of the peer +** min_int - minimum connection interval, [0x0004~ 0x4000] +** max_int - maximum connection interval, [0x0004~ 0x4000] +** latency - slave latency [0 ~ 500] +** timeout - supervision timeout [0x000a ~ 0xc80] +** +** Returns void +** +*******************************************************************************/ +extern void BTA_DmBleUpdateConnectionParams(BD_ADDR bd_addr, UINT16 min_int, + UINT16 max_int, UINT16 latency, UINT16 timeout); + +/******************************************************************************* +** +** Function BTA_DmBleDisconnect +** +** Description This function is to disconnect the ble connection +** +** Returns void +** +*******************************************************************************/ +extern void BTA_DmBleDisconnect(BD_ADDR bd_addr); + +/******************************************************************************* +** +** Function BTA_DmBleSetDataLength +** +** Description This function is to set maximum LE data packet size +** +** Returns void +** +*******************************************************************************/ +extern void BTA_DmBleSetDataLength(BD_ADDR remote_device, UINT16 tx_data_length, tBTA_SET_PKT_DATA_LENGTH_CBACK *p_set_pkt_data_cback); + +extern void BTA_DmBleDtmTxStart(uint8_t tx_channel, uint8_t len_of_data, uint8_t pkt_payload, tBTA_DTM_CMD_CMPL_CBACK *p_dtm_cmpl_cback); +extern void BTA_DmBleDtmRxStart(uint8_t rx_channel, tBTA_DTM_CMD_CMPL_CBACK *p_dtm_cmpl_cback); +extern void BTA_DmBleDtmStop(tBTA_DTM_CMD_CMPL_CBACK *p_dtm_cmpl_cback); + +/******************************************************************************* +** +** Function BTA_DmBleSetStorageParams +** +** Description This function is called to set the storage parameters +** +** Parameters batch_scan_full_max -Max storage space (in %) allocated to full scanning +** batch_scan_trunc_max -Max storage space (in %) allocated to truncated scanning +** batch_scan_notify_threshold - Setup notification level based on total space +** consumed by both pools. Setting it to 0 will disable threshold notification +** p_setup_cback - Setup callback +** p_thres_cback - Threshold callback +** p_rep_cback - Reports callback +** ref_value - Reference value +** +** Returns None +** +*******************************************************************************/ +extern void BTA_DmBleSetStorageParams(UINT8 batch_scan_full_max, + UINT8 batch_scan_trunc_max, + UINT8 batch_scan_notify_threshold, + tBTA_BLE_SCAN_SETUP_CBACK *p_setup_cback, + tBTA_BLE_SCAN_THRESHOLD_CBACK *p_thres_cback, + tBTA_BLE_SCAN_REP_CBACK *p_rep_cback, + tBTA_DM_BLE_REF_VALUE ref_value); + +/******************************************************************************* +** +** Function BTA_DmBleEnableBatchScan +** +** Description This function is called to enable the batch scan +** +** Parameters scan_mode -Batch scan mode +** scan_interval - Scan interval +** scan_window - Scan window +** discard_rule -Discard rules +** addr_type - Address type +** ref_value - Reference value +** +** Returns None +** +*******************************************************************************/ +extern void BTA_DmBleEnableBatchScan(tBTA_BLE_BATCH_SCAN_MODE scan_mode, + UINT32 scan_interval, UINT32 scan_window, + tBTA_BLE_DISCARD_RULE discard_rule, + tBLE_ADDR_TYPE addr_type, + tBTA_DM_BLE_REF_VALUE ref_value); + +/******************************************************************************* +** +** Function BTA_DmBleReadScanReports +** +** Description This function is called to read the batch scan reports +** +** Parameters scan_mode -Batch scan mode +** ref_value - Reference value +** +** Returns None +** +*******************************************************************************/ +extern void BTA_DmBleReadScanReports(tBTA_BLE_BATCH_SCAN_MODE scan_type, + tBTA_DM_BLE_REF_VALUE ref_value); + +/******************************************************************************* +** +** Function BTA_DmBleDisableBatchScan +** +** Description This function is called to disable the batch scanning +** +** Parameters ref_value - Reference value +** +** Returns None +** +*******************************************************************************/ +extern void BTA_DmBleDisableBatchScan(tBTA_DM_BLE_REF_VALUE ref_value); + +/******************************************************************************* +** +** Function BTA_DmEnableScanFilter +** +** Description This function is called to enable the adv data payload filter +** +** Parameters action - enable or disable the APCF feature +** p_cmpl_cback - Command completed callback +** ref_value - Reference value +** +** Returns void +** +*******************************************************************************/ +extern void BTA_DmEnableScanFilter(UINT8 action, + tBTA_DM_BLE_PF_STATUS_CBACK *p_cmpl_cback, + tBTA_DM_BLE_REF_VALUE ref_value); + +/******************************************************************************* +** +** Function BTA_DmBleScanFilterSetup +** +** Description This function is called to setup the filter params +** +** Parameters p_target: enable the filter condition on a target device; if NULL +** filt_index - Filter index +** p_filt_params -Filter parameters +** ref_value - Reference value +** action - Add, delete or clear +** p_cmpl_back - Command completed callback +** +** Returns void +** +*******************************************************************************/ +extern void BTA_DmBleScanFilterSetup(UINT8 action, + tBTA_DM_BLE_PF_FILT_INDEX filt_index, + tBTA_DM_BLE_PF_FILT_PARAMS *p_filt_params, + tBLE_BD_ADDR *p_target, + tBTA_DM_BLE_PF_PARAM_CBACK *p_cmpl_cback, + tBTA_DM_BLE_REF_VALUE ref_value); + +/******************************************************************************* +** +** Function BTA_DmBleCfgFilterCondition +** +** Description This function is called to configure the adv data payload filter +** condition. +** +** Parameters action: to read/write/clear +** cond_type: filter condition type +** filt_index - Filter index +** p_cond: filter condition parameter +** p_cmpl_back - Command completed callback +** ref_value - Reference value +** +** Returns void +** +*******************************************************************************/ +extern void BTA_DmBleCfgFilterCondition(tBTA_DM_BLE_SCAN_COND_OP action, + tBTA_DM_BLE_PF_COND_TYPE cond_type, + tBTA_DM_BLE_PF_FILT_INDEX filt_index, + tBTA_DM_BLE_PF_COND_PARAM *p_cond, + tBTA_DM_BLE_PF_CFG_CBACK *p_cmpl_cback, + tBTA_DM_BLE_REF_VALUE ref_value); + + +/******************************************************************************* +** +** Function BTA_DmBleTrackAdvertiser +** +** Description This function is called to track the advertiser +** +** Parameters ref_value - Reference value +** p_track_adv_cback - ADV callback +** +** Returns None +** +*******************************************************************************/ +extern void BTA_DmBleTrackAdvertiser(tBTA_DM_BLE_REF_VALUE ref_value, + tBTA_BLE_TRACK_ADV_CBACK *p_track_adv_cback); + +/******************************************************************************* +** +** Function BTA_DmBleGetEnergyInfo +** +** Description This function is called to obtain the energy info +** +** Parameters p_cmpl_cback - Command complete callback +** +** Returns void +** +*******************************************************************************/ +extern void BTA_DmBleGetEnergyInfo(tBTA_BLE_ENERGY_INFO_CBACK *p_cmpl_cback); + +/******************************************************************************* +** +** Function BTA_BrcmInit +** +** Description This function initializes Broadcom specific VS handler in BTA +** +** Returns void +** +*******************************************************************************/ +extern void BTA_VendorInit (void); + +/******************************************************************************* +** +** Function BTA_BrcmCleanup +** +** Description This function frees up Broadcom specific VS specific dynamic memory +** +** Returns void +** +*******************************************************************************/ +extern void BTA_VendorCleanup (void); + +#if (BLE_50_FEATURE_SUPPORT == TRUE) +extern void BTA_DmBleGapReadPHY(BD_ADDR addr); + +extern void BTA_DmBleGapSetPreferedDefaultPHY(tBTA_DM_BLE_GAP_PHY_MASK tx_phy_mask, + tBTA_DM_BLE_GAP_PHY_MASK rx_phy_mask); + +extern void BTA_DmBleGapSetPreferedPHY(BD_ADDR addr, + UINT8 all_phys, + tBTA_DM_BLE_GAP_PHY_MASK tx_phy_mask, + tBTA_DM_BLE_GAP_PHY_MASK rx_phy_mask, + UINT16 phy_options); + +extern void BTA_DmBleGapExtAdvSetRandaddr(UINT16 instance, BD_ADDR addr); + +extern void BTA_DmBleGapExtAdvSetParams(UINT16 instance, + const tBTA_DM_BLE_GAP_EXT_ADV_PARAMS *params); + +extern void BTA_DmBleGapConfigExtAdvDataRaw(BOOLEAN is_scan_rsp, UINT8 instance, UINT16 length, + const UINT8 *data); + +extern void BTA_DmBleGapStartExtAdv(UINT8 num, tBTA_DM_BLE_EXT_ADV *ext_adv); + +extern void BTA_DmBleGapExtAdvEnable(BOOLEAN enable, UINT8 num, tBTA_DM_BLE_EXT_ADV *ext_adv); + +extern void BTA_DmBleGapExtAdvSetRemove(UINT8 instance); + +extern void BTA_DmBleGapExtAdvSetClear(void); + +extern void BTA_DmBleGapPeriodicAdvSetParams(UINT8 instance, + tBTA_DM_BLE_Periodic_Adv_Params *params); + +extern void BTA_DmBleGapPeriodicAdvCfgDataRaw(UINT8 instance, UINT16 length, + const UINT8 *data,BOOLEAN only_update_did); + +extern void BTA_DmBleGapPeriodicAdvEnable(UINT8 enable, UINT8 instance); + +extern void BTA_DmBleGapPeriodicAdvCreateSync(tBTA_DM_BLE_Periodic_Sync_Params *params); + +extern void BTA_DmBleGapPeriodicAdvSyncCancel(void); + +extern void BTA_DmBleGapPeriodicAdvSyncTerm(UINT16 sync_handle); + +extern void BTA_DmBleGapPeriodicAdvAddDevToList(tBLE_ADDR_TYPE addr_type, + BD_ADDR addr, + UINT16 sid); + +extern void BTA_DmBleGapPeriodicAdvRemoveDevFromList(tBLE_ADDR_TYPE addr_type, + BD_ADDR addr, + UINT16 sid); + +extern void BTA_DmBleGapPeriodicAdvClearDev(void); + +extern void BTA_DmBleGapSetExtScanParams(tBTA_DM_BLE_EXT_SCAN_PARAMS *params); + +extern void BTA_DmBleGapExtScan(BOOLEAN start, UINT32 duration, UINT16 period); + +extern void BTA_DmBleGapPreferExtConnectParamsSet(BD_ADDR bd_addr, + UINT8 phy_mask, + const tBTA_DM_BLE_CONN_PARAMS *phy_1m_conn_params, + const tBTA_DM_BLE_CONN_PARAMS *phy_2m_conn_params, + const tBTA_DM_BLE_CONN_PARAMS *phy_coded_conn_params); + +extern void BTA_DmBleGapExtConnect(tBLE_ADDR_TYPE own_addr_type, const BD_ADDR peer_addr); + +extern void BTA_DmBleDtmEnhTxStart(uint8_t tx_channel, uint8_t len_of_data, uint8_t pkt_payload, uint8_t phy, tBTA_DTM_CMD_CMPL_CBACK *p_dtm_cmpl_cback); + +extern void BTA_DmBleDtmEnhRxStart(uint8_t rx_channel, uint8_t phy, uint8_t modulation_index, tBTA_DTM_CMD_CMPL_CBACK *p_dtm_cmpl_cback); +#endif // #if (BLE_50_FEATURE_SUPPORT == TRUE) + +#if (BLE_FEAT_PERIODIC_ADV_SYNC_TRANSFER == TRUE) +extern void BTA_DmBleGapPeriodicAdvRecvEnable(UINT16 sync_handle, UINT8 enable); + +extern void BTA_DmBleGapPeriodicAdvSyncTrans(BD_ADDR peer_addr, UINT16 service_data, UINT16 sync_handle); + +extern void BTA_DmBleGapPeriodicAdvSetInfoTrans(BD_ADDR peer_addr, UINT16 service_data, UINT8 adv_handle); + +extern void BTA_DmBleGapSetPeriodicAdvSyncTransParams(BD_ADDR peer_addr, tBTA_DM_BLE_PAST_PARAMS *params); +#endif // #if (BLE_FEAT_PERIODIC_ADV_SYNC_TRANSFER == TRUE) + +#endif + +enum { + BTA_COEX_EVT_SCAN_STARTED = 1, + BTA_COEX_EVT_SCAN_STOPPED, + BTA_COEX_EVT_ACL_CONNECTED, + BTA_COEX_EVT_ACL_DISCONNECTED, + BTA_COEX_EVT_STREAMING_STARTED, + BTA_COEX_EVT_STREAMING_STOPPED, + BTA_COEX_EVT_SNIFF_ENTER, + BTA_COEX_EVT_SNIFF_EXIT, + BTA_COEX_EVT_A2DP_PAUSED_ENTER, + BTA_COEX_EVT_A2DP_PAUSED_EXIT, +}; + +extern void BTA_DmCoexEventTrigger(uint32_t event); + +#ifdef __cplusplus +} +#endif + +#endif /* BTA_API_H */ diff --git a/lib/bt/host/bluedroid/bta/include/bta/bta_ar_api.h b/lib/bt/host/bluedroid/bta/include/bta/bta_ar_api.h new file mode 100644 index 00000000..2823d674 --- /dev/null +++ b/lib/bt/host/bluedroid/bta/include/bta/bta_ar_api.h @@ -0,0 +1,144 @@ +/****************************************************************************** + * + * Copyright (C) 2004-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This is the public interface file for the simulatenous advanced + * audio/video streaming (AV) source and sink of BTA, Broadcom's Bluetooth + * application layer for mobile phones. + * + ******************************************************************************/ +#ifndef BTA_AR_API_H +#define BTA_AR_API_H + +#include "stack/avdt_api.h" +#include "stack/avct_api.h" +#include "stack/avrc_api.h" +#include "stack/sdp_api.h" +#include "bta/bta_av_api.h" +#include "bta/bta_sys.h" + +#if (BTA_AR_INCLUDED == TRUE) + +/***************************************************************************** +** Constants and data types +*****************************************************************************/ +/* This event signal to AR user that other profile is connected */ +#define BTA_AR_AVDT_CONN_EVT (AVDT_MAX_EVT + 1) + +/******************************************************************************* +** +** Function bta_ar_init +** +** Description This function is called from bta_sys_init(). +** to initialize the control block +** +** Returns void +** +*******************************************************************************/ +extern void bta_ar_init(void); + +/******************************************************************************* +** +** Function bta_ar_reg_avdt +** +** Description This function is called to register to AVDTP. +** +** Returns void +** +*******************************************************************************/ +extern void bta_ar_reg_avdt(tAVDT_REG *p_reg, tAVDT_CTRL_CBACK *p_cback, tBTA_SYS_ID sys_id); + +/******************************************************************************* +** +** Function bta_ar_dereg_avdt +** +** Description This function is called to de-register from AVDTP. +** +** Returns void +** +*******************************************************************************/ +extern void bta_ar_dereg_avdt(tBTA_SYS_ID sys_id); + +/******************************************************************************* +** +** Function bta_ar_avdt_conn +** +** Description This function is called to let ar know that some AVDTP profile +** is connected for this sys_id. +** If the other sys modules started a timer for PENDING_EVT, +** the timer can be stopped now. +** +** Returns void +** +*******************************************************************************/ +extern void bta_ar_avdt_conn(tBTA_SYS_ID sys_id, BD_ADDR bd_addr); + +/******************************************************************************* +** +** Function bta_ar_reg_avct +** +** Description This function is called to register to AVCTP. +** +** Returns void +** +*******************************************************************************/ +extern void bta_ar_reg_avct(UINT16 mtu, UINT16 mtu_br, UINT8 sec_mask, tBTA_SYS_ID sys_id); + +/******************************************************************************* +** +** Function bta_ar_dereg_avct +** +** Description This function is called to deregister from AVCTP. +** +** Returns void +** +*******************************************************************************/ +extern void bta_ar_dereg_avct(tBTA_SYS_ID sys_id); + +/****************************************************************************** +** +** Function bta_ar_reg_avrc +** +** Description This function is called to register an SDP record for AVRCP. +** +** Returns void +** +******************************************************************************/ +extern void bta_ar_reg_avrc(UINT16 service_uuid, char *p_service_name, char *p_provider_name, + UINT16 categories, tBTA_SYS_ID sys_id, BOOLEAN browsing_en); + +/****************************************************************************** +** +** Function bta_ar_dereg_avrc +** +** Description This function is called to de-register/delete an SDP record for AVRCP. +** +** Returns void +** +******************************************************************************/ +extern void bta_ar_dereg_avrc(UINT16 service_uuid, tBTA_SYS_ID sys_id); + + +#ifdef __cplusplus +} +#endif + +#endif ///BTA_AR_INCLUDED == TRUE + +#endif /* BTA_AR_API_H */ diff --git a/lib/bt/host/bluedroid/bta/include/bta/bta_av_api.h b/lib/bt/host/bluedroid/bta/include/bta/bta_av_api.h new file mode 100644 index 00000000..ddf72b96 --- /dev/null +++ b/lib/bt/host/bluedroid/bta/include/bta/bta_av_api.h @@ -0,0 +1,874 @@ +/****************************************************************************** + * + * Copyright (C) 2004-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This is the public interface file for the advanced audio/video streaming + * (AV) subsystem of BTA, Broadcom's Bluetooth application layer for mobile + * phones. + * + ******************************************************************************/ +#ifndef BTA_AV_API_H +#define BTA_AV_API_H + +#include "stack/avrc_api.h" +#include "stack/avdt_api.h" +#include "stack/a2d_api.h" +#include "bta/bta_api.h" + +#if (BTA_AV_INCLUDED == TRUE) + +/***************************************************************************** +** Constants and data types +*****************************************************************************/ +/* AV status values */ +#define BTA_AV_SUCCESS 0 /* successful operation */ +#define BTA_AV_FAIL 1 /* generic failure */ +#define BTA_AV_FAIL_SDP 2 /* service not found */ +#define BTA_AV_FAIL_STREAM 3 /* stream connection failed */ +#define BTA_AV_FAIL_RESOURCES 4 /* no resources */ +#define BTA_AV_FAIL_ROLE 5 /* failed due to role management related issues */ +#define BTA_AV_FAIL_GET_CAP 6 /* get capability failed due to no SEP availale on the peer */ + +typedef UINT8 tBTA_AV_STATUS; + +/* AV features masks */ +#define BTA_AV_FEAT_RCTG 0x0001 /* remote control target */ +#define BTA_AV_FEAT_RCCT 0x0002 /* remote control controller */ +#define BTA_AV_FEAT_PROTECT 0x0004 /* streaming media contect protection */ +#define BTA_AV_FEAT_VENDOR 0x0008 /* remote control vendor dependent commands */ +#define BTA_AV_FEAT_REPORT 0x0020 /* use reporting service for VDP */ +#define BTA_AV_FEAT_METADATA 0x0040 /* remote control Metadata Transfer command/response */ +#define BTA_AV_FEAT_MULTI_AV 0x0080 /* use multi-av, if controller supports it */ +#define BTA_AV_FEAT_BROWSE 0x0010 /* use browsing channel */ +#define BTA_AV_FEAT_MASTER 0x0100 /* stream only as master role */ +#define BTA_AV_FEAT_ADV_CTRL 0x0200 /* remote control Advanced Control command/response */ +#define BTA_AV_FEAT_DELAY_RPT 0x0400 /* allow delay reporting */ +#define BTA_AV_FEAT_ACP_START 0x0800 /* start stream when 2nd SNK was accepted */ + +/* Internal features */ +#define BTA_AV_FEAT_NO_SCO_SSPD 0x8000 /* Do not suspend av streaming as to AG events(SCO or Call) */ + +/* Protocol service capabilities mask*/ +#define BTA_AV_PSC_DEALY_RPT (1<<0) /* Delay Report */ + +typedef UINT16 tBTA_AV_FEAT; + +/* AV channel values */ +#define BTA_AV_CHNL_MSK 0xC0 +#define BTA_AV_CHNL_AUDIO 0x40 /* audio channel */ +#define BTA_AV_CHNL_VIDEO 0x80 /* video channel */ +typedef UINT8 tBTA_AV_CHNL; + + +#define BTA_AV_HNDL_MSK 0x3F +typedef UINT8 tBTA_AV_HNDL; +/* handle index to mask */ +#define BTA_AV_HNDL_TO_MSK(h) ((UINT8)(1 << (h))) + +/* tBTA_AV_HNDL to mask */ +#define BTA_AV_HNDL_TYPE_TO_MSK(h) ((UINT8)(1 << (h&BTA_AV_HNDL_MSK))) + +/* offset of codec type in codec info byte array */ +#define BTA_AV_CODEC_TYPE_IDX AVDT_CODEC_TYPE_INDEX /* 2 */ + + + +/* maximum number of streams created: 1 for audio, 1 for video */ +#ifndef BTA_AV_NUM_STRS +#define BTA_AV_NUM_STRS 2 +#endif + +#ifndef BTA_AV_MAX_SEPS +#define BTA_AV_MAX_SEPS 1 +#endif + +#ifndef BTA_AV_MAX_A2DP_MTU +/*#define BTA_AV_MAX_A2DP_MTU 668 //224 (DM5) * 3 - 4(L2CAP header) */ +#define BTA_AV_MAX_A2DP_MTU 1008 +#endif + +#ifndef BTA_AV_MAX_VDP_MTU +#define BTA_AV_MAX_VDP_MTU 1008 +#endif + + +/* codec type */ +#define BTA_AV_CODEC_SBC A2D_MEDIA_CT_SBC /* SBC media codec type */ +#define BTA_AV_CODEC_M12 A2D_MEDIA_CT_M12 /* MPEG-1, 2 Audio media codec type */ +#define BTA_AV_CODEC_M24 A2D_MEDIA_CT_M24 /* MPEG-2, 4 AAC media codec type */ +#define BTA_AV_CODEC_ATRAC A2D_MEDIA_CT_ATRAC /* ATRAC family media codec type */ +#define BTA_AV_CODEC_H263_P0 VDP_MEDIA_CT_H263_P0 /* H.263 baseline (profile 0) */ +#define BTA_AV_CODEC_MPEG4 VDP_MEDIA_CT_MPEG4 /* MPEG-4 Visual Simple Profile */ +#define BTA_AV_CODEC_H263_P3 VDP_MEDIA_CT_H263_P3 /* H.263 profile 3 */ +#define BTA_AV_CODEC_H263_P8 VDP_MEDIA_CT_H263_P8 /* H.263 profile 8 */ +#define BTA_AV_CODEC_VEND VDP_MEDIA_CT_VEND /* Non-VDP */ + +typedef UINT8 tBTA_AV_CODEC; + +/* Company ID in BT assigned numbers */ +#define BTA_AV_BT_VENDOR_ID VDP_BT_VENDOR_ID /* Broadcom Corporation */ + +/* vendor specific codec ID */ +#define BTA_AV_CODEC_ID_H264 VDP_CODEC_ID_H264 /* Non-VDP codec ID - H.264 */ +#define BTA_AV_CODEC_ID_IMG VDP_CODEC_ID_IMG /* Non-VDP codec ID - images/slideshow */ + +/* operation id list for BTA_AvRemoteCmd */ +#define BTA_AV_RC_SELECT AVRC_ID_SELECT /* select */ +#define BTA_AV_RC_UP AVRC_ID_UP /* up */ +#define BTA_AV_RC_DOWN AVRC_ID_DOWN /* down */ +#define BTA_AV_RC_LEFT AVRC_ID_LEFT /* left */ +#define BTA_AV_RC_RIGHT AVRC_ID_RIGHT /* right */ +#define BTA_AV_RC_RIGHT_UP AVRC_ID_RIGHT_UP /* right-up */ +#define BTA_AV_RC_RIGHT_DOWN AVRC_ID_RIGHT_DOWN /* right-down */ +#define BTA_AV_RC_LEFT_UP AVRC_ID_LEFT_UP /* left-up */ +#define BTA_AV_RC_LEFT_DOWN AVRC_ID_LEFT_DOWN /* left-down */ +#define BTA_AV_RC_ROOT_MENU AVRC_ID_ROOT_MENU /* root menu */ +#define BTA_AV_RC_SETUP_MENU AVRC_ID_SETUP_MENU /* setup menu */ +#define BTA_AV_RC_CONT_MENU AVRC_ID_CONT_MENU /* contents menu */ +#define BTA_AV_RC_FAV_MENU AVRC_ID_FAV_MENU /* favorite menu */ +#define BTA_AV_RC_EXIT AVRC_ID_EXIT /* exit */ +#define BTA_AV_RC_0 AVRC_ID_0 /* 0 */ +#define BTA_AV_RC_1 AVRC_ID_1 /* 1 */ +#define BTA_AV_RC_2 AVRC_ID_2 /* 2 */ +#define BTA_AV_RC_3 AVRC_ID_3 /* 3 */ +#define BTA_AV_RC_4 AVRC_ID_4 /* 4 */ +#define BTA_AV_RC_5 AVRC_ID_5 /* 5 */ +#define BTA_AV_RC_6 AVRC_ID_6 /* 6 */ +#define BTA_AV_RC_7 AVRC_ID_7 /* 7 */ +#define BTA_AV_RC_8 AVRC_ID_8 /* 8 */ +#define BTA_AV_RC_9 AVRC_ID_9 /* 9 */ +#define BTA_AV_RC_DOT AVRC_ID_DOT /* dot */ +#define BTA_AV_RC_ENTER AVRC_ID_ENTER /* enter */ +#define BTA_AV_RC_CLEAR AVRC_ID_CLEAR /* clear */ +#define BTA_AV_RC_CHAN_UP AVRC_ID_CHAN_UP /* channel up */ +#define BTA_AV_RC_CHAN_DOWN AVRC_ID_CHAN_DOWN /* channel down */ +#define BTA_AV_RC_PREV_CHAN AVRC_ID_PREV_CHAN /* previous channel */ +#define BTA_AV_RC_SOUND_SEL AVRC_ID_SOUND_SEL /* sound select */ +#define BTA_AV_RC_INPUT_SEL AVRC_ID_INPUT_SEL /* input select */ +#define BTA_AV_RC_DISP_INFO AVRC_ID_DISP_INFO /* display information */ +#define BTA_AV_RC_HELP AVRC_ID_HELP /* help */ +#define BTA_AV_RC_PAGE_UP AVRC_ID_PAGE_UP /* page up */ +#define BTA_AV_RC_PAGE_DOWN AVRC_ID_PAGE_DOWN /* page down */ +#define BTA_AV_RC_POWER AVRC_ID_POWER /* power */ +#define BTA_AV_RC_VOL_UP AVRC_ID_VOL_UP /* volume up */ +#define BTA_AV_RC_VOL_DOWN AVRC_ID_VOL_DOWN /* volume down */ +#define BTA_AV_RC_MUTE AVRC_ID_MUTE /* mute */ +#define BTA_AV_RC_PLAY AVRC_ID_PLAY /* play */ +#define BTA_AV_RC_STOP AVRC_ID_STOP /* stop */ +#define BTA_AV_RC_PAUSE AVRC_ID_PAUSE /* pause */ +#define BTA_AV_RC_RECORD AVRC_ID_RECORD /* record */ +#define BTA_AV_RC_REWIND AVRC_ID_REWIND /* rewind */ +#define BTA_AV_RC_FAST_FOR AVRC_ID_FAST_FOR /* fast forward */ +#define BTA_AV_RC_EJECT AVRC_ID_EJECT /* eject */ +#define BTA_AV_RC_FORWARD AVRC_ID_FORWARD /* forward */ +#define BTA_AV_RC_BACKWARD AVRC_ID_BACKWARD /* backward */ +#define BTA_AV_RC_ANGLE AVRC_ID_ANGLE /* angle */ +#define BTA_AV_RC_SUBPICT AVRC_ID_SUBPICT /* subpicture */ +#define BTA_AV_RC_F1 AVRC_ID_F1 /* F1 */ +#define BTA_AV_RC_F2 AVRC_ID_F2 /* F2 */ +#define BTA_AV_RC_F3 AVRC_ID_F3 /* F3 */ +#define BTA_AV_RC_F4 AVRC_ID_F4 /* F4 */ +#define BTA_AV_RC_F5 AVRC_ID_F5 /* F5 */ +#define BTA_AV_VENDOR AVRC_ID_VENDOR /* vendor unique */ + +typedef UINT8 tBTA_AV_RC; + +/* state flag for pass through command */ +#define BTA_AV_STATE_PRESS AVRC_STATE_PRESS /* key pressed */ +#define BTA_AV_STATE_RELEASE AVRC_STATE_RELEASE /* key released */ + +typedef UINT8 tBTA_AV_STATE; + +/* command codes for BTA_AvVendorCmd */ +#define BTA_AV_CMD_CTRL AVRC_CMD_CTRL +#define BTA_AV_CMD_STATUS AVRC_CMD_STATUS +#define BTA_AV_CMD_SPEC_INQ AVRC_CMD_SPEC_INQ +#define BTA_AV_CMD_NOTIF AVRC_CMD_NOTIF +#define BTA_AV_CMD_GEN_INQ AVRC_CMD_GEN_INQ + +typedef UINT8 tBTA_AV_CMD; + +/* response codes for BTA_AvVendorRsp */ +#define BTA_AV_RSP_NOT_IMPL AVRC_RSP_NOT_IMPL +#define BTA_AV_RSP_ACCEPT AVRC_RSP_ACCEPT +#define BTA_AV_RSP_REJ AVRC_RSP_REJ +#define BTA_AV_RSP_IN_TRANS AVRC_RSP_IN_TRANS +#define BTA_AV_RSP_IMPL_STBL AVRC_RSP_IMPL_STBL +#define BTA_AV_RSP_CHANGED AVRC_RSP_CHANGED +#define BTA_AV_RSP_INTERIM AVRC_RSP_INTERIM + +typedef UINT8 tBTA_AV_CODE; + +/* error codes for BTA_AvProtectRsp */ +#define BTA_AV_ERR_NONE A2D_SUCCESS /* Success, no error */ +#define BTA_AV_ERR_BAD_STATE AVDT_ERR_BAD_STATE /* Message cannot be processed in this state */ +#define BTA_AV_ERR_RESOURCE AVDT_ERR_RESOURCE /* Insufficient resources */ +#define BTA_AV_ERR_BAD_CP_TYPE A2D_BAD_CP_TYPE /* The requested Content Protection Type is not supported */ +#define BTA_AV_ERR_BAD_CP_FORMAT A2D_BAD_CP_FORMAT /* The format of Content Protection Data is not correct */ + +typedef UINT8 tBTA_AV_ERR; + + +/* AV callback events */ +#define BTA_AV_ENABLE_EVT 0 /* AV enabled */ +#define BTA_AV_REGISTER_EVT 1 /* registered to AVDT */ +#define BTA_AV_OPEN_EVT 2 /* connection opened */ +#define BTA_AV_CLOSE_EVT 3 /* connection closed */ +#define BTA_AV_START_EVT 4 /* stream data transfer started */ +#define BTA_AV_STOP_EVT 5 /* stream data transfer stopped */ +#define BTA_AV_PROTECT_REQ_EVT 6 /* content protection request */ +#define BTA_AV_PROTECT_RSP_EVT 7 /* content protection response */ +#define BTA_AV_RC_OPEN_EVT 8 /* remote control channel open */ +#define BTA_AV_RC_CLOSE_EVT 9 /* remote control channel closed */ +#define BTA_AV_REMOTE_CMD_EVT 10 /* remote control command */ +#define BTA_AV_REMOTE_RSP_EVT 11 /* remote control response */ +#define BTA_AV_VENDOR_CMD_EVT 12 /* vendor dependent remote control command */ +#define BTA_AV_VENDOR_RSP_EVT 13 /* vendor dependent remote control response */ +#define BTA_AV_RECONFIG_EVT 14 /* reconfigure response */ +#define BTA_AV_SUSPEND_EVT 15 /* suspend response */ +#define BTA_AV_PENDING_EVT 16 /* incoming connection pending: + * signal channel is open and stream is not open + * after BTA_AV_SIG_TIME_VAL ms */ +#define BTA_AV_META_MSG_EVT 17 /* metadata messages */ +#define BTA_AV_REJECT_EVT 18 /* incoming connection rejected */ +#define BTA_AV_RC_FEAT_EVT 19 /* remote control channel peer supported features update */ +#define BTA_AV_MEDIA_SINK_CFG_EVT 20 /* command to configure codec */ +#define BTA_AV_MEDIA_DATA_EVT 21 /* sending data to Media Task */ +#define BTA_AV_SET_DELAY_VALUE_EVT 22 /* set delay reporting value */ +#define BTA_AV_GET_DELAY_VALUE_EVT 23 /* get delay reporting value */ +#define BTA_AV_SNK_PSC_CFG_EVT 24 /* Protocol service capabilities. */ +/* Max BTA event */ +#define BTA_AV_MAX_EVT 25 + + +/* function types for call-out functions */ +typedef BOOLEAN (*tBTA_AV_CO_INIT) (UINT8 *p_codec_type, UINT8 *p_codec_info, + UINT8 *p_num_protect, UINT8 *p_protect_info, UINT8 tsep); +typedef void (*tBTA_AV_CO_DISC_RES) (tBTA_AV_HNDL hndl, UINT8 num_seps, + UINT8 num_snk, UINT8 num_src, BD_ADDR addr, UINT16 uuid_local); +typedef void (*tBTA_AV_CO_CFG_RES) (tBTA_AV_HNDL hndl, UINT8 num_seps, + UINT8 num_snk, UINT8 num_src, BD_ADDR addr, UINT16 uuid_local); +typedef UINT8 (*tBTA_AV_CO_GETCFG) (tBTA_AV_HNDL hndl, tBTA_AV_CODEC codec_type, + UINT8 *p_codec_info, UINT8 *p_sep_info_idx, UINT8 seid, + UINT8 *p_num_protect, UINT8 *p_protect_info); +typedef void (*tBTA_AV_CO_SETCFG) (tBTA_AV_HNDL hndl, tBTA_AV_CODEC codec_type, + UINT8 *p_codec_info, UINT8 seid, BD_ADDR addr, + UINT8 num_protect, UINT8 *p_protect_info, + UINT8 t_local_sep, UINT8 avdt_handle); +typedef void (*tBTA_AV_CO_OPEN) (tBTA_AV_HNDL hndl, + tBTA_AV_CODEC codec_type, UINT8 *p_codec_info, + UINT16 mtu); +typedef void (*tBTA_AV_CO_CLOSE) (tBTA_AV_HNDL hndl, tBTA_AV_CODEC codec_type, UINT16 mtu); +typedef void (*tBTA_AV_CO_START) (tBTA_AV_HNDL hndl, tBTA_AV_CODEC codec_type, UINT8 *p_codec_info, BOOLEAN *p_no_rtp_hdr); +typedef void (*tBTA_AV_CO_STOP) (tBTA_AV_HNDL hndl, tBTA_AV_CODEC codec_type); +typedef void *(*tBTA_AV_CO_DATAPATH) (tBTA_AV_CODEC codec_type, + UINT32 *p_len, UINT32 *p_timestamp); +typedef void (*tBTA_AV_CO_DELAY) (tBTA_AV_HNDL hndl, UINT16 delay); + +/// AVRCP call-out function +// check whether PASSTHROUGH command is supported +typedef BOOLEAN (*tBTA_AVRC_CO_CMD_ALLOWED) (tBTA_AV_RC rc_id); +// get the event notification capabilities +typedef UINT8 (*tBTA_AVRC_CO_RN_EVT_CAP) (UINT8 *event_ids); +// check whether the event_id is supported +typedef BOOLEAN (*tBTA_AVRC_CO_RN_EVT_SUPPORTED) (UINT8 event_id); + +/* the call-out functions for one stream */ +typedef struct { + tBTA_AV_CO_INIT init; + tBTA_AV_CO_DISC_RES disc_res; + tBTA_AV_CO_CFG_RES cfg_res; + tBTA_AV_CO_GETCFG getcfg; + tBTA_AV_CO_SETCFG setcfg; + tBTA_AV_CO_OPEN open; + tBTA_AV_CO_CLOSE close; + tBTA_AV_CO_START start; + tBTA_AV_CO_STOP stop; + tBTA_AV_CO_DATAPATH data; + tBTA_AV_CO_DELAY delay; +} tBTA_AV_CO_FUNCTS; + +/* the call-out functions for AVRCP */ +typedef struct { + tBTA_AVRC_CO_CMD_ALLOWED rc_cmd; + tBTA_AVRC_CO_RN_EVT_CAP rn_evt_cap; + tBTA_AVRC_CO_RN_EVT_SUPPORTED rn_evt_supported; +} tBTA_AVRC_CO_FUNCTS; + +typedef UINT8 tBTA_AV_EVT; + +/* Event associated with BTA_AV_ENABLE_EVT */ +typedef struct { + tBTA_AV_FEAT features; +} tBTA_AV_ENABLE; + +/* Event associated with BTA_AV_REGISTER_EVT */ +typedef struct { + tBTA_AV_CHNL chnl; /* audio/video */ + tBTA_AV_HNDL hndl; /* Handle associated with the stream. */ + UINT8 app_id; /* ID associated with call to BTA_AvRegister() */ + tBTA_AV_STATUS status; + tBTA_AV_CO_FUNCTS *p_bta_av_cos; + tBTA_AVRC_CO_FUNCTS *p_bta_avrc_cos; +} tBTA_AV_REGISTER; + +/* data associated with BTA_AV_OPEN_EVT */ +#define BTA_AV_EDR_2MBPS 0x01 +#define BTA_AV_EDR_3MBPS 0x02 +typedef UINT8 tBTA_AV_EDR; + +typedef struct { + tBTA_AV_CHNL chnl; + tBTA_AV_HNDL hndl; + BD_ADDR bd_addr; + tBTA_AV_STATUS status; + BOOLEAN starting; + tBTA_AV_EDR edr; /* 0, if peer device does not support EDR */ + UINT8 sep; /* sep type of peer device */ +} tBTA_AV_OPEN; + +/* data associated with BTA_AV_CLOSE_EVT */ +typedef struct { + tBTA_AV_CHNL chnl; + tBTA_AV_HNDL hndl; + UINT8 disc_rsn; /* disconnection reason */ +} tBTA_AV_CLOSE; + +/* data associated with BTA_AV_START_EVT */ +typedef struct { + tBTA_AV_CHNL chnl; + tBTA_AV_HNDL hndl; + tBTA_AV_STATUS status; + BOOLEAN initiator; /* TRUE, if local device initiates the START */ + BOOLEAN suspending; +} tBTA_AV_START; + +/* data associated with BTA_AV_SUSPEND_EVT */ +typedef struct { + tBTA_AV_CHNL chnl; + tBTA_AV_HNDL hndl; + BOOLEAN initiator; /* TRUE, if local device initiates the SUSPEND */ + tBTA_AV_STATUS status; +} tBTA_AV_SUSPEND; + +/* data associated with BTA_AV_RECONFIG_EVT */ +typedef struct { + tBTA_AV_CHNL chnl; + tBTA_AV_HNDL hndl; + tBTA_AV_STATUS status; +} tBTA_AV_RECONFIG; + +/* data associated with BTA_AV_PROTECT_REQ_EVT */ +typedef struct { + tBTA_AV_CHNL chnl; + tBTA_AV_HNDL hndl; + UINT8 *p_data; + UINT16 len; +} tBTA_AV_PROTECT_REQ; + +/* data associated with BTA_AV_PROTECT_RSP_EVT */ +typedef struct { + tBTA_AV_CHNL chnl; + tBTA_AV_HNDL hndl; + UINT8 *p_data; + UINT16 len; + tBTA_AV_ERR err_code; +} tBTA_AV_PROTECT_RSP; + +/* data associated with BTA_AV_RC_OPEN_EVT */ +typedef struct { + UINT8 rc_handle; + BOOLEAN sdp_disc_done; + tBTA_AV_FEAT peer_features; + UINT16 peer_ct_features; + UINT16 peer_tg_features; + BD_ADDR peer_addr; + tBTA_AV_STATUS status; +} tBTA_AV_RC_OPEN; + +/* data associated with BTA_AV_RC_CLOSE_EVT */ +typedef struct { + UINT8 rc_handle; + BD_ADDR peer_addr; +} tBTA_AV_RC_CLOSE; + +/* data associated with BTA_AV_RC_FEAT_EVT */ +typedef struct { + UINT8 rc_handle; + tBTA_AV_FEAT peer_features; + UINT16 peer_ct_features; + UINT16 peer_tg_features; +} tBTA_AV_RC_FEAT; + +/* data associated with BTA_AV_REMOTE_CMD_EVT */ +typedef struct { + UINT8 rc_handle; + tBTA_AV_RC rc_id; + tBTA_AV_STATE key_state; + UINT8 len; + UINT8 *p_data; + tAVRC_HDR hdr; /* Message header. */ + UINT8 label; +} tBTA_AV_REMOTE_CMD; + +/* data associated with BTA_AV_REMOTE_RSP_EVT */ +typedef struct { + UINT8 rc_handle; + tBTA_AV_RC rc_id; + tBTA_AV_STATE key_state; + UINT8 len; + UINT8 *p_data; + tBTA_AV_CODE rsp_code; + UINT8 label; +} tBTA_AV_REMOTE_RSP; + +/* data associated with BTA_AV_VENDOR_CMD_EVT, BTA_AV_VENDOR_RSP_EVT */ +typedef struct { + UINT8 rc_handle; + UINT16 len; /* Max vendor dependent message is 512 */ + UINT8 label; + tBTA_AV_CODE code; + UINT32 company_id; + UINT8 *p_data; +} tBTA_AV_VENDOR; + +/* data associated with BTA_AV_META_MSG_EVT */ +typedef struct { + UINT8 rc_handle; + UINT16 len; + UINT8 label; + tBTA_AV_CODE code; + UINT32 company_id; + UINT8 *p_data; + tAVRC_MSG *p_msg; +} tBTA_AV_META_MSG; + +/* data associated with BTA_AV_PENDING_EVT */ +typedef struct { + BD_ADDR bd_addr; +} tBTA_AV_PEND; + +/* data associated with BTA_AV_REJECT_EVT */ +typedef struct { + BD_ADDR bd_addr; + tBTA_AV_HNDL hndl; /* Handle associated with the stream that rejected the connection. */ +} tBTA_AV_REJECT; + +/* data associated with BTA_AV_SET_DELAY_VALUE_EVT/BTA_AV_GET_DELAY_VALUE_EVT */ +typedef struct { + tBTA_AV_STATUS status; + UINT16 delay_value; +} tBTA_AV_DELAY; + +/* data associated with BTA_AV_SNK_PSC_CFG_EVT */ +typedef struct { + UINT16 psc_mask; +} tBTA_AV_SNK_PSC_CFG; + + +/* union of data associated with AV callback */ +typedef union { + tBTA_AV_CHNL chnl; + tBTA_AV_ENABLE enable; + tBTA_AV_REGISTER registr; + tBTA_AV_OPEN open; + tBTA_AV_CLOSE close; + tBTA_AV_START start; + tBTA_AV_PROTECT_REQ protect_req; + tBTA_AV_PROTECT_RSP protect_rsp; + tBTA_AV_RC_OPEN rc_open; + tBTA_AV_RC_CLOSE rc_close; + tBTA_AV_REMOTE_CMD remote_cmd; + tBTA_AV_REMOTE_RSP remote_rsp; + tBTA_AV_VENDOR vendor_cmd; + tBTA_AV_VENDOR vendor_rsp; + tBTA_AV_RECONFIG reconfig; + tBTA_AV_SUSPEND suspend; + tBTA_AV_PEND pend; + tBTA_AV_META_MSG meta_msg; + tBTA_AV_REJECT reject; + tBTA_AV_RC_FEAT rc_feat; + tBTA_AV_DELAY delay; + tBTA_AV_SNK_PSC_CFG psc; +} tBTA_AV; + +/* union of data associated with AV Media callback */ +typedef union { + BT_HDR *p_data; + UINT8 *codec_info; +} tBTA_AV_MEDIA; + + +#define BTA_AVC_PACKET_LEN AVRC_PACKET_LEN +#define BTA_VENDOR_DATA_OFFSET 6 +#define BTA_VENDOR_HEADER_LEN 4 +#define BTA_MAX_VENDOR_DEPENDENT_DATA_LEN (BTA_AVC_PACKET_LEN-BTA_VENDOR_DATA_OFFSET-BTA_VENDOR_HEADER_LEN) +#define BTA_GROUP_NAVI_MSG_OP_DATA_LEN 5 + +#define BTA_ERROR_INVALID_CMD AVRC_STS_BAD_CMD +#define BTA_ERROR_INVALID_PARAM AVRC_STS_BAD_PARAM +#define BTA_ERROR_BAD_CONTENTS AVRC_STS_NOT_FOUND +#define BTA_ERROR_INTERNAL AVRC_STS_INTERNAL_ERR + +#define BTA_AV_META_SINGLE_PACKET AVRC_PKT_SINGLE + +#define BTA_AV_CO_METADATA AVRC_CO_METADATA + +/* AV callback */ +typedef void (tBTA_AV_CBACK)(tBTA_AV_EVT event, tBTA_AV *p_data); +typedef void (tBTA_AV_DATA_CBACK)(tBTA_AV_EVT event, tBTA_AV_MEDIA *p_data); + +/* type for stream state machine action functions */ +typedef void (*tBTA_AV_ACT)(void *p_cb, void *p_data); + +/* type for registering VDP */ +typedef void (tBTA_AV_REG) (tAVDT_CS *p_cs, char *p_service_name, void *p_data); + +/* AV configuration structure */ +typedef struct { + UINT32 company_id; /* AVRCP Company ID */ + UINT16 avrc_mtu; /* AVRCP MTU at L2CAP for control channel */ + UINT16 avrc_br_mtu; /* AVRCP MTU at L2CAP for browsing channel */ + UINT16 avrc_snk_ct_cat; /* AVRCP controller categories as SNK */ + UINT16 avrc_snk_tg_cat; /* AVRCP target categories SNK */ + UINT16 avrc_src_ct_cat; /* AVRCP controller categories as SRC */ + UINT16 avrc_src_tg_cat; /* AVRCP target categories as SRC */ + UINT16 sig_mtu; /* AVDTP signaling channel MTU at L2CAP */ + UINT16 audio_mtu; /* AVDTP audio transport channel MTU at L2CAP */ + const UINT16 *p_audio_flush_to;/* AVDTP audio transport channel flush timeout */ + UINT16 audio_mqs; /* AVDTP audio channel max data queue size */ + UINT16 video_mtu; /* AVDTP video transport channel MTU at L2CAP */ + UINT16 video_flush_to; /* AVDTP video transport channel flush timeout */ + BOOLEAN avrc_group; /* TRUE, to accept AVRC 1.3 group nevigation command */ + BOOLEAN avrc_br; /* FALSE, does not support browsing channel */ + UINT8 num_co_ids; /* company id count in p_meta_co_ids */ + tBTA_AV_CODE rc_pass_rsp; /* the default response code for pass through commands */ + const UINT32 *p_meta_co_ids;/* the metadata Get Capabilities response for company id */ + const tBTA_AV_ACT *p_act_tbl;/* the action function table for VDP stream */ + tBTA_AV_REG *p_reg; /* action function to register VDP */ + char avrc_controller_name[BTA_SERVICE_NAME_LEN]; /* Default AVRCP controller name */ + char avrc_target_name[BTA_SERVICE_NAME_LEN]; /* Default AVRCP target name*/ +} tBTA_AV_CFG; + +#ifdef __cplusplus +extern "C" +{ +#endif + +/***************************************************************************** +** External Function Declarations +*****************************************************************************/ + +/******************************************************************************* +** +** Function BTA_AvEnable +** +** Description Enable the advanced audio/video service. When the enable +** operation is complete the callback function will be +** called with a BTA_AV_ENABLE_EVT. This function must +** be called before other function in the AV API are +** called. +** +** Returns void +** +*******************************************************************************/ +void BTA_AvEnable(tBTA_SEC sec_mask, tBTA_AV_FEAT features, + tBTA_AV_CBACK *p_cback); + +/******************************************************************************* +** +** Function BTA_AvDisable +** +** Description Disable the advanced audio/video service. +** +** +** Returns void +** +*******************************************************************************/ +void BTA_AvDisable(void); + +/******************************************************************************* +** +** Function BTA_AvRegister +** +** Description Register the audio or video service to stack. When the +** operation is complete the callback function will be +** called with a BTA_AV_REGISTER_EVT. This function must +** be called before AVDT stream is open. +** +** +** Returns void +** +*******************************************************************************/ +void BTA_AvRegister(tBTA_AV_CHNL chnl, const char *p_service_name, + UINT8 app_id, tBTA_AV_DATA_CBACK *p_data_cback, + tBTA_AV_CO_FUNCTS *bta_av_cos, tBTA_AVRC_CO_FUNCTS *bta_avrc_cos, + UINT8 tsep); + +/******************************************************************************* +** +** Function BTA_AvDeregister +** +** Description Deregister the audio or video service +** +** Returns void +** +*******************************************************************************/ +void BTA_AvDeregister(tBTA_AV_HNDL hndl); + +/******************************************************************************* +** +** Function BTA_AvOpen +** +** Description Opens an advanced audio/video connection to a peer device. +** When connection is open callback function is called +** with a BTA_AV_OPEN_EVT. +** +** Returns void +** +*******************************************************************************/ +void BTA_AvOpen(BD_ADDR bd_addr, tBTA_AV_HNDL handle, + BOOLEAN use_rc, tBTA_SEC sec_mask, UINT16 uuid); + +/******************************************************************************* +** +** Function BTA_AvClose +** +** Description Close the current streams. +** +** Returns void +** +*******************************************************************************/ +void BTA_AvClose(tBTA_AV_HNDL handle); + +/******************************************************************************* +** +** Function BTA_AvDisconnect +** +** Description Close the connection to the address. +** +** Returns void +** +*******************************************************************************/ +void BTA_AvDisconnect(BD_ADDR bd_addr); + +/******************************************************************************* +** +** Function BTA_AvEnable_Sink +** +** Description Enable/Disable A2DP Sink. +** +** Returns void +** +*******************************************************************************/ +void BTA_AvEnable_Sink(int enable); + +/******************************************************************************* +** +** Function BTA_AvStart +** +** Description Start audio/video stream data transfer. +** +** Returns void +** +*******************************************************************************/ +void BTA_AvStart(void); + +/******************************************************************************* +** +** Function BTA_AvStop +** +** Description Stop audio/video stream data transfer. +** If suspend is TRUE, this function sends AVDT suspend signal +** to the connected peer(s). +** +** Returns void +** +*******************************************************************************/ +void BTA_AvStop(BOOLEAN suspend); + +/******************************************************************************* +** +** Function BTA_AvReconfig +** +** Description Reconfigure the audio/video stream. +** If suspend is TRUE, this function tries the suspend/reconfigure +** procedure first. +** If suspend is FALSE or when suspend/reconfigure fails, +** this function closes and re-opens the AVDT connection. +** +** Returns void +** +*******************************************************************************/ +void BTA_AvReconfig(tBTA_AV_HNDL hndl, BOOLEAN suspend, UINT8 sep_info_idx, + UINT8 *p_codec_info, UINT8 num_protect, UINT8 *p_protect_info); + +/******************************************************************************* +** +** Function BTA_AvProtectReq +** +** Description Send a content protection request. This function can only +** be used if AV is enabled with feature BTA_AV_FEAT_PROTECT. +** +** Returns void +** +*******************************************************************************/ +void BTA_AvProtectReq(tBTA_AV_HNDL hndl, UINT8 *p_data, UINT16 len); + +/******************************************************************************* +** +** Function BTA_AvProtectRsp +** +** Description Send a content protection response. This function must +** be called if a BTA_AV_PROTECT_REQ_EVT is received. +** This function can only be used if AV is enabled with +** feature BTA_AV_FEAT_PROTECT. +** +** Returns void +** +*******************************************************************************/ +void BTA_AvProtectRsp(tBTA_AV_HNDL hndl, UINT8 error_code, UINT8 *p_data, + UINT16 len); + +/******************************************************************************* +** +** Function BTA_SetDelayValue +** +** Description Set delay report value +** +** Returns void +** +*******************************************************************************/ +void BTA_SetDelayValue(UINT16 delay_value); + +/******************************************************************************* +** +** Function BTA_GetDelayValue +** +** Description Get delay report value +** +** Returns void +** +*******************************************************************************/ +void BTA_GetDelayValue(void); + +/******************************************************************************* +** +** Function BTA_AvRemoteCmd +** +** Description Send a remote control command. This function can only +** be used if AV is enabled with feature BTA_AV_FEAT_RCCT. +** +** Returns void +** +*******************************************************************************/ +void BTA_AvRemoteCmd(UINT8 rc_handle, UINT8 label, tBTA_AV_RC rc_id, + tBTA_AV_STATE key_state); + +/******************************************************************************* +** +** Function BTA_AvVendorCmd +** +** Description Send a vendor dependent remote control command. This +** function can only be used if AV is enabled with feature +** BTA_AV_FEAT_VENDOR. +** +** Returns void +** +*******************************************************************************/ +void BTA_AvVendorCmd(UINT8 rc_handle, UINT8 label, tBTA_AV_CODE cmd_code, + UINT8 *p_data, UINT16 len); + +/******************************************************************************* +** +** Function BTA_AvVendorRsp +** +** Description Send a vendor dependent remote control response. +** This function must be called if a BTA_AV_VENDOR_CMD_EVT +** is received. This function can only be used if AV is +** enabled with feature BTA_AV_FEAT_VENDOR. +** +** Returns void +** +*******************************************************************************/ +void BTA_AvVendorRsp(UINT8 rc_handle, UINT8 label, tBTA_AV_CODE rsp_code, + UINT8 *p_data, UINT16 len, UINT32 company_id); + + +/******************************************************************************* +** +** Function BTA_AvOpenRc +** +** Description Open an AVRCP connection toward the device with the +** specified handle +** +** Returns void +** +*******************************************************************************/ +void BTA_AvOpenRc(tBTA_AV_HNDL handle); + +/******************************************************************************* +** +** Function BTA_AvCloseRc +** +** Description Close an AVRCP connection +** +** Returns void +** +*******************************************************************************/ +void BTA_AvCloseRc(UINT8 rc_handle); + +/******************************************************************************* +** +** Function BTA_AvMetaRsp +** +** Description Send a Metadata command/response. The message contained +** in p_pkt can be composed with AVRC utility functions. +** This function can only be used if AV is enabled with feature +** BTA_AV_FEAT_METADATA. +** +** Returns void +** +*******************************************************************************/ +void BTA_AvMetaRsp(UINT8 rc_handle, UINT8 label, tBTA_AV_CODE rsp_code, + BT_HDR *p_pkt); + +/******************************************************************************* +** +** Function BTA_AvMetaCmd +** +** Description Send a Metadata/Advanced Control command. The message contained +** in p_pkt can be composed with AVRC utility functions. +** This function can only be used if AV is enabled with feature +** BTA_AV_FEAT_METADATA. +** This message is sent only when the peer supports the TG role. +*8 The only command makes sense right now is the absolute volume command. +** +** Returns void +** +*******************************************************************************/ +void BTA_AvMetaCmd(UINT8 rc_handle, UINT8 label, tBTA_AV_CMD cmd_code, BT_HDR *p_pkt); + +#ifdef __cplusplus +} +#endif + +#endif ///BTA_AV_INCLUDED == TRUE + +#endif /* BTA_AV_API_H */ diff --git a/lib/bt/host/bluedroid/bta/include/bta/bta_av_ci.h b/lib/bt/host/bluedroid/bta/include/bta/bta_av_ci.h new file mode 100644 index 00000000..b39acace --- /dev/null +++ b/lib/bt/host/bluedroid/bta/include/bta/bta_av_ci.h @@ -0,0 +1,77 @@ +/****************************************************************************** + * + * Copyright (C) 2005-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This is the interface file for advanced audio/video call-in functions. + * + ******************************************************************************/ +#ifndef BTA_AV_CI_H +#define BTA_AV_CI_H + +#include "bta/bta_av_api.h" + +#if (BTA_AV_INCLUDED == TRUE) + +/***************************************************************************** +** Function Declarations +*****************************************************************************/ +#ifdef __cplusplus +extern "C" +{ +#endif + +/******************************************************************************* +** +** Function bta_av_ci_src_data_ready +** +** Description This function sends an event to the AV indicating that +** the phone has audio stream data ready to send and AV +** should call bta_av_co_audio_src_data_path() or +** bta_av_co_video_src_data_path(). +** +** Returns void +** +*******************************************************************************/ +extern void bta_av_ci_src_data_ready(tBTA_AV_CHNL chnl); + +/******************************************************************************* +** +** Function bta_av_ci_setconfig +** +** Description This function must be called in response to function +** bta_av_co_audio_setconfig() or bta_av_co_video_setconfig. +** Parameter err_code is set to an AVDTP status value; +** AVDT_SUCCESS if the codec configuration is ok, +** otherwise error. +** +** Returns void +** +*******************************************************************************/ +extern void bta_av_ci_setconfig(tBTA_AV_HNDL hndl, UINT8 err_code, + UINT8 category, UINT8 num_seid, UINT8 *p_seid, + BOOLEAN recfg_needed, UINT8 avdt_handle); + + +#ifdef __cplusplus +} +#endif + +#endif ///BTA_AV_INCLUDED == TRUE + +#endif /* BTA_AV_CI_H */ diff --git a/lib/bt/host/bluedroid/bta/include/bta/bta_av_co.h b/lib/bt/host/bluedroid/bta/include/bta/bta_av_co.h new file mode 100644 index 00000000..03c07c33 --- /dev/null +++ b/lib/bt/host/bluedroid/bta/include/bta/bta_av_co.h @@ -0,0 +1,393 @@ +/****************************************************************************** + * + * Copyright (C) 2003-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This is the interface file for advanced audio/video call-out functions. + * + ******************************************************************************/ +#ifndef BTA_AV_CO_H +#define BTA_AV_CO_H + +#include "stack/l2c_api.h" +#include "bta/bta_av_api.h" + +#if (BTA_AV_INCLUDED == TRUE) + +/***************************************************************************** +** Constants and data types +*****************************************************************************/ + +/* TRUE to use SCMS-T content protection */ +#ifndef BTA_AV_CO_CP_SCMS_T +#define BTA_AV_CO_CP_SCMS_T FALSE +#endif + +/* the content protection IDs assigned by BT SIG */ +#define BTA_AV_CP_SCMS_T_ID 0x0002 +#define BTA_AV_CP_DTCP_ID 0x0001 + +#define BTA_AV_CP_LOSC 2 +#define BTA_AV_CP_INFO_LEN 3 + +#define BTA_AV_CP_SCMS_COPY_MASK 3 +#define BTA_AV_CP_SCMS_COPY_FREE 2 +#define BTA_AV_CP_SCMS_COPY_ONCE 1 +#define BTA_AV_CP_SCMS_COPY_NEVER 0 + +#define BTA_AV_CO_DEFAULT_AUDIO_OFFSET AVDT_MEDIA_OFFSET + +enum { + BTA_AV_CO_ST_INIT, + BTA_AV_CO_ST_IN, + BTA_AV_CO_ST_OUT, + BTA_AV_CO_ST_OPEN, + BTA_AV_CO_ST_STREAM +}; + + +/* data type for the Audio Codec Information*/ +typedef struct { + UINT16 bit_rate; /* SBC encoder bit rate in kbps */ + UINT16 bit_rate_busy; /* SBC encoder bit rate in kbps */ + UINT16 bit_rate_swampd;/* SBC encoder bit rate in kbps */ + UINT8 busy_level; /* Busy level indicating the bit-rate to be used */ + UINT8 codec_info[AVDT_CODEC_SIZE]; + UINT8 codec_type; /* Codec type */ +} tBTA_AV_AUDIO_CODEC_INFO; + +/******************************************************************************* +** +** Function bta_av_co_audio_init +** +** Description This callout function is executed by AV when it is +** started by calling BTA_AvEnable(). This function can be +** used by the phone to initialize audio paths or for other +** initialization purposes. +** +** +** Returns Stream codec and content protection capabilities info. +** +*******************************************************************************/ +extern BOOLEAN bta_av_co_audio_init(UINT8 *p_codec_type, UINT8 *p_codec_info, + UINT8 *p_num_protect, UINT8 *p_protect_info, UINT8 tsep); + +/******************************************************************************* +** +** Function bta_av_co_audio_disc_res +** +** Description This callout function is executed by AV to report the +** number of stream end points (SEP) were found during the +** AVDT stream discovery process. +** +** +** Returns void. +** +*******************************************************************************/ +extern void bta_av_co_audio_disc_res(tBTA_AV_HNDL hndl, UINT8 num_seps, + UINT8 num_snk, UINT8 num_src, BD_ADDR addr, UINT16 uuid_local); + +/******************************************************************************* +** +** Function bta_av_co_video_disc_res +** +** Description This callout function is executed by AV to report the +** number of stream end points (SEP) were found during the +** AVDT stream discovery process. +** +** +** Returns void. +** +*******************************************************************************/ +extern void bta_av_co_video_disc_res(tBTA_AV_HNDL hndl, UINT8 num_seps, + UINT8 num_snk, BD_ADDR addr); + +/******************************************************************************* +** +** Function bta_av_co_audio_getconfig +** +** Description This callout function is executed by AV to retrieve the +** desired codec and content protection configuration for the +** audio stream. +** +** +** Returns Stream codec and content protection configuration info. +** +*******************************************************************************/ +extern UINT8 bta_av_co_audio_getconfig(tBTA_AV_HNDL hndl, tBTA_AV_CODEC codec_type, + UINT8 *p_codec_info, UINT8 *p_sep_info_idx, UINT8 seid, + UINT8 *p_num_protect, UINT8 *p_protect_info); + +/******************************************************************************* +** +** Function bta_av_co_video_getconfig +** +** Description This callout function is executed by AV to retrieve the +** desired codec and content protection configuration for the +** video stream. +** +** +** Returns Stream codec and content protection configuration info. +** +*******************************************************************************/ +extern UINT8 bta_av_co_video_getconfig(tBTA_AV_HNDL hndl, tBTA_AV_CODEC codec_type, + UINT8 *p_codec_info, UINT8 *p_sep_info_idx, UINT8 seid, + UINT8 *p_num_protect, UINT8 *p_protect_info); + +/******************************************************************************* +** +** Function bta_av_co_audio_setconfig +** +** Description This callout function is executed by AV to set the +** codec and content protection configuration of the audio stream. +** +** +** Returns void +** +*******************************************************************************/ +extern void bta_av_co_audio_setconfig(tBTA_AV_HNDL hndl, tBTA_AV_CODEC codec_type, + UINT8 *p_codec_info, UINT8 seid, BD_ADDR addr, + UINT8 num_protect, UINT8 *p_protect_info, UINT8 t_local_sep, UINT8 avdt_handle); + +/******************************************************************************* +** +** Function bta_av_co_video_setconfig +** +** Description This callout function is executed by AV to set the +** codec and content protection configuration of the video stream. +** +** +** Returns void +** +*******************************************************************************/ +extern void bta_av_co_video_setconfig(tBTA_AV_HNDL hndl, tBTA_AV_CODEC codec_type, + UINT8 *p_codec_info, UINT8 seid, BD_ADDR addr, + UINT8 num_protect, UINT8 *p_protect_info); + +/******************************************************************************* +** +** Function bta_av_co_audio_open +** +** Description This function is called by AV when the audio stream connection +** is opened. +** BTA-AV maintains the MTU of A2DP streams. +** If this is the 2nd audio stream, mtu is the smaller of the 2 +** streams. +** +** Returns void +** +*******************************************************************************/ +extern void bta_av_co_audio_open(tBTA_AV_HNDL hndl, + tBTA_AV_CODEC codec_type, UINT8 *p_codec_info, + UINT16 mtu); + +/******************************************************************************* +** +** Function bta_av_co_video_open +** +** Description This function is called by AV when the video stream connection +** is opened. +** +** +** Returns void +** +*******************************************************************************/ +extern void bta_av_co_video_open(tBTA_AV_HNDL hndl, + tBTA_AV_CODEC codec_type, UINT8 *p_codec_info, + UINT16 mtu); + +/******************************************************************************* +** +** Function bta_av_co_audio_close +** +** Description This function is called by AV when the audio stream connection +** is closed. +** BTA-AV maintains the MTU of A2DP streams. +** When one stream is closed and no other audio stream is open, +** mtu is reported as 0. +** Otherwise, the MTU remains open is reported. +** +** Returns void +** +*******************************************************************************/ +extern void bta_av_co_audio_close(tBTA_AV_HNDL hndl, tBTA_AV_CODEC codec_type, + UINT16 mtu); + +/******************************************************************************* +** +** Function bta_av_co_video_close +** +** Description This function is called by AV when the video stream connection +** is closed. +** +** +** Returns void +** +*******************************************************************************/ +extern void bta_av_co_video_close(tBTA_AV_HNDL hndl, tBTA_AV_CODEC codec_type, + UINT16 mtu); + +/******************************************************************************* +** +** Function bta_av_co_audio_start +** +** Description This function is called by AV when the audio streaming data +** transfer is started. +** +** +** Returns void +** +*******************************************************************************/ +extern void bta_av_co_audio_start(tBTA_AV_HNDL hndl, tBTA_AV_CODEC codec_type, + UINT8 *p_codec_info, BOOLEAN *p_no_rtp_hdr); + +/******************************************************************************* +** +** Function bta_av_co_video_start +** +** Description This function is called by AV when the video streaming data +** transfer is started. +** +** +** Returns void +** +*******************************************************************************/ +extern void bta_av_co_video_start(tBTA_AV_HNDL hndl, tBTA_AV_CODEC codec_type, + UINT8 *p_codec_info, BOOLEAN *p_no_rtp_hdr); + +/******************************************************************************* +** +** Function bta_av_co_audio_stop +** +** Description This function is called by AV when the audio streaming data +** transfer is stopped. +** +** +** Returns void +** +*******************************************************************************/ +extern void bta_av_co_audio_stop(tBTA_AV_HNDL hndl, tBTA_AV_CODEC codec_type); + +/******************************************************************************* +** +** Function bta_av_co_video_stop +** +** Description This function is called by AV when the video streaming data +** transfer is stopped. +** +** +** Returns void +** +*******************************************************************************/ +extern void bta_av_co_video_stop(tBTA_AV_HNDL hndl, tBTA_AV_CODEC codec_type); + +/******************************************************************************* +** +** Function bta_av_co_audio_src_data_path +** +** Description This function is called to get the next data buffer from +** the audio codec +** +** Returns NULL if data is not ready. +** Otherwise, a buffer (BT_HDR*) containing the audio data. +** +*******************************************************************************/ +extern void *bta_av_co_audio_src_data_path(tBTA_AV_CODEC codec_type, + UINT32 *p_len, UINT32 *p_timestamp); + +/******************************************************************************* +** +** Function bta_av_co_video_src_data_path +** +** Description This function is called to get the next data buffer from +** the video codec. +** +** Returns NULL if data is not ready. +** Otherwise, a video data buffer (UINT8*). +** +*******************************************************************************/ +extern void *bta_av_co_video_src_data_path(tBTA_AV_CODEC codec_type, + UINT32 *p_len, UINT32 *p_timestamp); + +/******************************************************************************* +** +** Function bta_av_co_audio_drop +** +** Description An Audio packet is dropped. . +** It's very likely that the connected headset with this handle +** is moved far away. The implementation may want to reduce +** the encoder bit rate setting to reduce the packet size. +** +** Returns void +** +*******************************************************************************/ +extern void bta_av_co_audio_drop(tBTA_AV_HNDL hndl); + +/******************************************************************************* +** +** Function bta_av_co_video_report_conn +** +** Description This function is called by AV when the reporting channel is +** opened (open=TRUE) or closed (open=FALSE). +** +** Returns void +** +*******************************************************************************/ +extern void bta_av_co_video_report_conn (BOOLEAN open, UINT8 avdt_handle); + +/******************************************************************************* +** +** Function bta_av_co_video_report_rr +** +** Description This function is called by AV when a Receiver Report is +** received +** +** Returns void +** +*******************************************************************************/ +extern void bta_av_co_video_report_rr (UINT32 packet_lost); + +/******************************************************************************* +** +** Function bta_av_co_audio_delay +** +** Description This function is called by AV when the audio stream connection +** needs to send the initial delay report to the connected SRC. +** +** +** Returns void +** +*******************************************************************************/ +extern void bta_av_co_audio_delay(tBTA_AV_HNDL hndl, UINT16 delay); + +/******************************************************************************* +** +** Function bta_av_co_video_delay +** +** Description This function is called by AV when the video stream connection +** needs to send the initial delay report to the connected SRC. +** +** +** Returns void +** +*******************************************************************************/ +extern void bta_av_co_video_delay(tBTA_AV_HNDL hndl, UINT16 delay); + +#endif ///BTA_AV_INCLUDED == TRUE + +#endif /* BTA_AV_CO_H */ diff --git a/lib/bt/host/bluedroid/bta/include/bta/bta_av_sbc.h b/lib/bt/host/bluedroid/bta/include/bta/bta_av_sbc.h new file mode 100644 index 00000000..fccc02fa --- /dev/null +++ b/lib/bt/host/bluedroid/bta/include/bta/bta_av_sbc.h @@ -0,0 +1,222 @@ +/****************************************************************************** + * + * Copyright (C) 2004-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This is the interface to utility functions for dealing with SBC data + * frames and codec capabilities. + * + ******************************************************************************/ +#ifndef BTA_AV_SBC_H +#define BTA_AV_SBC_H + +#if (BTA_AV_INCLUDED == TRUE) + +/***************************************************************************** +** constants +*****************************************************************************/ + +/* SBC packet header size */ +#define BTA_AV_SBC_HDR_SIZE A2D_SBC_MPL_HDR_LEN + +/******************************************************************************* +** +** Function bta_av_sbc_init_up_sample +** +** Description initialize the up sample +** +** src_sps: samples per second (source audio data) +** dst_sps: samples per second (converted audio data) +** bits: number of bits per pcm sample +** n_channels: number of channels (i.e. mono(1), stereo(2)...) +** +** Returns none +** +*******************************************************************************/ +extern void bta_av_sbc_init_up_sample (UINT32 src_sps, UINT32 dst_sps, + UINT16 bits, UINT16 n_channels); + +/******************************************************************************* +** +** Function bta_av_sbc_up_sample +** +** Description Given the source (p_src) audio data and +** source speed (src_sps, samples per second), +** This function converts it to audio data in the desired format +** +** p_src: the data buffer that holds the source audio data +** p_dst: the data buffer to hold the converted audio data +** src_samples: The number of source samples (number of bytes) +** dst_samples: The size of p_dst (number of bytes) +** +** Note: An AE reported an issue with this function. +** When called with bta_av_sbc_up_sample(src, uint8_array_dst..) +** the byte before uint8_array_dst may get overwritten. +** Using uint16_array_dst avoids the problem. +** This issue is related to endian-ness and is hard to resolve +** in a generic manner. +** **************** Please use uint16 array as dst. +** +** Returns The number of bytes used in p_dst +** The number of bytes used in p_src (in *p_ret) +** +*******************************************************************************/ +extern int bta_av_sbc_up_sample (void *p_src, void *p_dst, + UINT32 src_samples, UINT32 dst_samples, + UINT32 *p_ret); + +/******************************************************************************* +** +** Function bta_av_sbc_up_sample_16s (16bits-stereo) +** +** Description Given the source (p_src) audio data and +** source speed (src_sps, samples per second), +** This function converts it to audio data in the desired format +** +** p_src: the data buffer that holds the source audio data +** p_dst: the data buffer to hold the converted audio data +** src_samples: The number of source samples (in uint of 4 bytes) +** dst_samples: The size of p_dst (in uint of 4 bytes) +** +** Returns The number of bytes used in p_dst +** The number of bytes used in p_src (in *p_ret) +** +*******************************************************************************/ +extern int bta_av_sbc_up_sample_16s (void *p_src, void *p_dst, + UINT32 src_samples, UINT32 dst_samples, + UINT32 *p_ret); + +/******************************************************************************* +** +** Function bta_av_sbc_up_sample_16m (16bits-mono) +** +** Description Given the source (p_src) audio data and +** source speed (src_sps, samples per second), +** This function converts it to audio data in the desired format +** +** p_src: the data buffer that holds the source audio data +** p_dst: the data buffer to hold the converted audio data +** src_samples: The number of source samples (in uint of 2 bytes) +** dst_samples: The size of p_dst (in uint of 2 bytes) +** +** Returns The number of bytes used in p_dst +** The number of bytes used in p_src (in *p_ret) +** +*******************************************************************************/ +extern int bta_av_sbc_up_sample_16m (void *p_src, void *p_dst, + UINT32 src_samples, UINT32 dst_samples, + UINT32 *p_ret); + +/******************************************************************************* +** +** Function bta_av_sbc_up_sample_8s (8bits-stereo) +** +** Description Given the source (p_src) audio data and +** source speed (src_sps, samples per second), +** This function converts it to audio data in the desired format +** +** p_src: the data buffer that holds the source audio data +** p_dst: the data buffer to hold the converted audio data +** src_samples: The number of source samples (in uint of 2 bytes) +** dst_samples: The size of p_dst (in uint of 2 bytes) +** +** Returns The number of bytes used in p_dst +** The number of bytes used in p_src (in *p_ret) +** +*******************************************************************************/ +extern int bta_av_sbc_up_sample_8s (void *p_src, void *p_dst, + UINT32 src_samples, UINT32 dst_samples, + UINT32 *p_ret); + +/******************************************************************************* +** +** Function bta_av_sbc_up_sample_8m (8bits-mono) +** +** Description Given the source (p_src) audio data and +** source speed (src_sps, samples per second), +** This function converts it to audio data in the desired format +** +** p_src: the data buffer that holds the source audio data +** p_dst: the data buffer to hold the converted audio data +** src_samples: The number of source samples (number of bytes) +** dst_samples: The size of p_dst (number of bytes) +** +** Returns The number of bytes used in p_dst +** The number of bytes used in p_src (in *p_ret) +** +*******************************************************************************/ +extern int bta_av_sbc_up_sample_8m (void *p_src, void *p_dst, + UINT32 src_samples, UINT32 dst_samples, + UINT32 *p_ret); + +/******************************************************************************* +** +** Function bta_av_sbc_cfg_for_cap +** +** Description Determine the preferred SBC codec configuration for the +** given codec capabilities. The function is passed the +** preferred codec configuration and the peer codec +** capabilities for the stream. The function attempts to +** match the preferred capabilities with the configuration +** as best it can. The resulting codec configuration is +** returned in the same memory used for the capabilities. +** +** Returns 0 if ok, nonzero if error. +** Codec configuration in p_cap. +** +*******************************************************************************/ +extern UINT8 bta_av_sbc_cfg_for_cap(UINT8 *p_peer, tA2D_SBC_CIE *p_cap, tA2D_SBC_CIE *p_pref); + +/******************************************************************************* +** +** Function bta_av_sbc_cfg_in_cap +** +** Description This function checks whether an SBC codec configuration +** is allowable for the given codec capabilities. +** +** Returns 0 if ok, nonzero if error. +** +*******************************************************************************/ +extern UINT8 bta_av_sbc_cfg_in_cap(UINT8 *p_cfg, tA2D_SBC_CIE *p_cap); + +/******************************************************************************* +** +** Function bta_av_sbc_cfg_matches_cap +** +** Description This function checks whether an SBC codec configuration +** matched with capabilities. Here we check subset. +** +** Returns 0 if ok, nonzero if error. +** +*******************************************************************************/ +extern UINT8 bta_av_sbc_cfg_matches_cap(UINT8 *p_cfg, tA2D_SBC_CIE *p_cap); + +/******************************************************************************* +** +** Function bta_av_sbc_bld_hdr +** +** Description This function builds the packet header for MPF1. +** +** Returns void +** +*******************************************************************************/ +extern void bta_av_sbc_bld_hdr(BT_HDR *p_buf, UINT16 fr_per_pkt); + +#endif ///BTA_AV_INCLUDED == TRUE + +#endif /* BTA_AV_SBC_H */ diff --git a/lib/bt/host/bluedroid/bta/include/bta/bta_dm_ci.h b/lib/bt/host/bluedroid/bta/include/bta/bta_dm_ci.h new file mode 100644 index 00000000..cd15c86c --- /dev/null +++ b/lib/bt/host/bluedroid/bta/include/bta/bta_dm_ci.h @@ -0,0 +1,68 @@ +/****************************************************************************** + * + * Copyright (C) 2006-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This is the interface file for device mananger call-in functions. + * + ******************************************************************************/ +#ifndef BTA_DM_CI_H +#define BTA_DM_CI_H + +#include "bta/bta_api.h" + +/***************************************************************************** +** Function Declarations +*****************************************************************************/ +#ifdef __cplusplus +extern "C" +{ +#endif + +/******************************************************************************* +** +** Function bta_dm_ci_io_req +** +** Description This function must be called in response to function +** bta_dm_co_io_req(), if *p_oob_data is set to BTA_OOB_UNKNOWN +** by bta_dm_co_io_req(). +** +** Returns void +** +*******************************************************************************/ +extern void bta_dm_ci_io_req(BD_ADDR bd_addr, tBTA_IO_CAP io_cap, + tBTA_OOB_DATA oob_data, tBTA_AUTH_REQ auth_req); + +/******************************************************************************* +** +** Function bta_dm_ci_rmt_oob +** +** Description This function must be called in response to function +** bta_dm_co_rmt_oob() to provide the OOB data associated +** with the remote device. +** +** Returns void +** +*******************************************************************************/ +extern void bta_dm_ci_rmt_oob(BOOLEAN accept, BD_ADDR bd_addr, + BT_OCTET16 c, BT_OCTET16 r); +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/bt/host/bluedroid/bta/include/bta/bta_dm_co.h b/lib/bt/host/bluedroid/bta/include/bta/bta_dm_co.h new file mode 100644 index 00000000..124711eb --- /dev/null +++ b/lib/bt/host/bluedroid/bta/include/bta/bta_dm_co.h @@ -0,0 +1,218 @@ +/****************************************************************************** + * + * Copyright (C) 2006-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This is the interface file for device mananger callout functions. + * + ******************************************************************************/ +#ifndef BTA_DM_CO_H +#define BTA_DM_CO_H + +#include "bta/bta_sys.h" +#include "esp_err.h" + +/***************************************************************************** +** Function Declarations +*****************************************************************************/ + +/******************************************************************************* +** +** Function bta_dm_co_bt_set_io_cap +** +** Description This function is used to set IO capabilities +** +** Parameters bt_io_cap - IO capabilities +** +** @return - ESP_BT_STATUS_SUCCESS : success +** - other : failed +** +*******************************************************************************/ +extern esp_err_t bta_dm_co_bt_set_io_cap(UINT8 bt_io_cap); + +/******************************************************************************* +** +** Function bta_dm_co_io_req +** +** Description This callout function is executed by DM to get IO capabilities +** of the local device for the Simple Pairing process +** +** Parameters bd_addr - The peer device +** *p_io_cap - The local Input/Output capabilities +** *p_oob_data - TRUE, if OOB data is available for the peer device. +** *p_auth_req - TRUE, if MITM protection is required. +** +** Returns void. +** +*******************************************************************************/ +extern void bta_dm_co_io_req(BD_ADDR bd_addr, tBTA_IO_CAP *p_io_cap, + tBTA_OOB_DATA *p_oob_data, tBTA_AUTH_REQ *p_auth_req, + BOOLEAN is_orig); + +/******************************************************************************* +** +** Function bta_dm_co_io_rsp +** +** Description This callout function is executed by DM to report IO capabilities +** of the peer device for the Simple Pairing process +** +** Parameters bd_addr - The peer device +** io_cap - The remote Input/Output capabilities +** oob_data - TRUE, if OOB data is available for the peer device. +** auth_req - TRUE, if MITM protection is required. +** +** Returns void. +** +*******************************************************************************/ +extern void bta_dm_co_io_rsp(BD_ADDR bd_addr, tBTA_IO_CAP io_cap, + tBTA_OOB_DATA oob_data, tBTA_AUTH_REQ auth_req); + +/******************************************************************************* +** +** Function bta_dm_co_lk_upgrade +** +** Description This callout function is executed by DM to check if the +** platform wants allow link key upgrade +** +** Parameters bd_addr - The peer device +** *p_upgrade - TRUE, if link key upgrade is desired. +** +** Returns void. +** +*******************************************************************************/ +extern void bta_dm_co_lk_upgrade(BD_ADDR bd_addr, BOOLEAN *p_upgrade ); + +/******************************************************************************* +** +** Function bta_dm_co_loc_oob +** +** Description This callout function is executed by DM to report the OOB +** data of the local device for the Simple Pairing process +** +** Parameters valid - TRUE, if the local OOB data is retrieved from LM +** c - Simple Pairing Hash C +** r - Simple Pairing Randomnizer R +** +** Returns void. +** +*******************************************************************************/ +extern void bta_dm_co_loc_oob(BOOLEAN valid, BT_OCTET16 c, BT_OCTET16 r); + +/******************************************************************************* +** +** Function bta_dm_co_rmt_oob +** +** Description This callout function is executed by DM to request the OOB +** data for the remote device for the Simple Pairing process +** +** Parameters bd_addr - The peer device +** +** Returns void. +** +*******************************************************************************/ +extern void bta_dm_co_rmt_oob(BD_ADDR bd_addr); + + +/******************************************************************************* +** +** Function bta_dm_co_ble_io_req +** +** Description This callout function is executed by DM to get BLE IO capabilities +** before SMP pairing gets going. +** +** Parameters bd_addr - The peer device +** *p_io_cap - The local Input/Output capabilities +** *p_oob_data - TRUE, if OOB data is available for the peer device. +** *p_auth_req - Auth request setting (Bonding and MITM required or not) +** *p_max_key_size - max key size local device supported. +** *p_init_key - initiator keys. +** *p_resp_key - responder keys. +** +** Returns void. +** +*******************************************************************************/ +extern void bta_dm_co_ble_io_req(BD_ADDR bd_addr, tBTA_IO_CAP *p_io_cap, + tBTA_OOB_DATA *p_oob_data, + tBTA_LE_AUTH_REQ *p_auth_req, + UINT8 *p_max_key_size, + tBTA_LE_KEY_TYPE *p_init_key, + tBTA_LE_KEY_TYPE *p_resp_key ); + + +/******************************************************************************* +** +** Function bta_dm_co_ble_local_key_reload +** +** Description This callout function is to load the local BLE keys if available +** on the device. +** +** Parameters none +** +** Returns void. +** +*******************************************************************************/ +extern void bta_dm_co_ble_load_local_keys (tBTA_DM_BLE_LOCAL_KEY_MASK *p_key_mask, BT_OCTET16 er, + tBTA_BLE_LOCAL_ID_KEYS *p_id_keys); + +// btla-specific ++ +/******************************************************************************* +** +** Function bta_dm_co_ble_io_req +** +** Description This callout function is executed by DM to get BLE IO capabilities +** before SMP pairing gets going. +** +** Parameters bd_addr - The peer device +** *p_io_cap - The local Input/Output capabilities +** *p_oob_data - TRUE, if OOB data is available for the peer device. +** *p_auth_req - Auth request setting (Bonding and MITM required or not) +** *p_max_key_size - max key size local device supported. +** *p_init_key - initiator keys. +** *p_resp_key - responder keys. +** +** Returns void. +** +*******************************************************************************/ +extern void bta_dm_co_ble_io_req(BD_ADDR bd_addr, tBTA_IO_CAP *p_io_cap, + tBTA_OOB_DATA *p_oob_data, + tBTA_LE_AUTH_REQ *p_auth_req, + UINT8 *p_max_key_size, + tBTA_LE_KEY_TYPE *p_init_key, + tBTA_LE_KEY_TYPE *p_resp_key ); +// btla-specific -- + +extern void bta_dm_co_ble_set_io_cap(UINT8 ble_io_cap); + +extern void bta_dm_co_ble_set_auth_req(UINT8 ble_auth_req); + +extern void bta_dm_co_ble_set_init_key_req(UINT8 init_key); + +extern void bta_dm_co_ble_set_rsp_key_req(UINT8 rsp_key); + +extern void bta_dm_co_ble_set_max_key_size(UINT8 ble_key_size); + +extern void bta_dm_co_ble_set_min_key_size(UINT8 ble_key_size); + +extern void bta_dm_co_ble_set_accept_auth_enable(UINT8 enable); + +extern UINT8 bta_dm_co_ble_get_accept_auth_enable(void); + +extern UINT8 bta_dm_co_ble_get_auth_req(void); + +extern void bta_dm_co_ble_oob_support(UINT8 enable); +#endif diff --git a/lib/bt/host/bluedroid/bta/include/bta/bta_gap_bt_co.h b/lib/bt/host/bluedroid/bta/include/bta/bta_gap_bt_co.h new file mode 100644 index 00000000..35aeb933 --- /dev/null +++ b/lib/bt/host/bluedroid/bta/include/bta/bta_gap_bt_co.h @@ -0,0 +1,21 @@ +/* + * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + + +/****************************************************************************** + * + * This is the interface file for BT GAP call-out functions. + * + ******************************************************************************/ +#ifndef BTA_GAP_BT_CO_H +#define BTA_GAP_BT_CO_H + +#if (BTC_GAP_BT_INCLUDED == TRUE) + +extern void btc_gap_bt_config_eir_cmpl_callback (uint8_t status, uint8_t eir_type_num, uint8_t *eir_type); + +#endif /// (BTC_GAP_BT_INCLUDED == TRUE) +#endif /// BTA_GAP_BT_CO_H diff --git a/lib/bt/host/bluedroid/bta/include/bta/bta_gatt_api.h b/lib/bt/host/bluedroid/bta/include/bta/bta_gatt_api.h new file mode 100644 index 00000000..183e3acc --- /dev/null +++ b/lib/bt/host/bluedroid/bta/include/bta/bta_gatt_api.h @@ -0,0 +1,1580 @@ +/****************************************************************************** + * + * Copyright (C) 2003-2013 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This is the public interface file for BTA GATT. + * + ******************************************************************************/ + +#ifndef BTA_GATT_API_H +#define BTA_GATT_API_H + +#include "bta/bta_api.h" +#include "stack/gatt_api.h" +#include "osi/list.h" + +#ifndef BTA_GATT_INCLUDED +#define BTA_GATT_INCLUDED FALSE +#endif + +#if ((BLE_INCLUDED == FALSE) && (BTA_GATT_INCLUDED == TRUE)) +#undef BTA_GATT_INCLUDED +#define BTA_GATT_INCLUDED FALSE +#endif + + +#ifndef BTA_GATT_DEBUG +#define BTA_GATT_DEBUG FALSE +#endif + +typedef enum { + BTGATT_DB_PRIMARY_SERVICE, + BTGATT_DB_SECONDARY_SERVICE, + BTGATT_DB_CHARACTERISTIC, + BTGATT_DB_DESCRIPTOR, + BTGATT_DB_INCLUDED_SERVICE, +}bt_gatt_db_attribute_type_t; + +typedef enum { + GATT_OP_GET_SVC_BY_UUID, + GATT_OP_GET_ALL_CHAR, + GATT_OP_GET_ALL_DESCRI, + GATT_OP_GET_CHAR_BY_UUID, + GATT_OP_GET_DESCRI_BY_UUID, + GATT_OP_GET_DESCRI_BY_HANDLE, + GATT_OP_GET_INCLUDE_SVC, +}bt_gatt_get_db_op_t; + +typedef struct { + bt_gatt_db_attribute_type_t type; + UINT16 attribute_handle; + UINT16 start_handle; + UINT16 end_handle; + UINT16 id; + UINT8 properties; + bt_uuid_t uuid; +}btgatt_db_element_t; + +/***************************************************************************** +** Constants and data types +*****************************************************************************/ +/************************** +** Common Definitions +***************************/ +/* GATT ID */ +typedef struct { + tBT_UUID uuid; /* uuid of the attribute */ + UINT8 inst_id; /* instance ID */ +} __attribute__((packed)) tBTA_GATT_ID; + +/* relate to ESP_GATT_xxx in esp_gatt_def.h */ +/* Success code and error codes */ +#define BTA_GATT_OK GATT_SUCCESS +#define BTA_GATT_INVALID_HANDLE GATT_INVALID_HANDLE /* 0x0001 */ +#define BTA_GATT_READ_NOT_PERMIT GATT_READ_NOT_PERMIT /* 0x0002 */ +#define BTA_GATT_WRITE_NOT_PERMIT GATT_WRITE_NOT_PERMIT /* 0x0003 */ +#define BTA_GATT_INVALID_PDU GATT_INVALID_PDU /* 0x0004 */ +#define BTA_GATT_INSUF_AUTHENTICATION GATT_INSUF_AUTHENTICATION /* 0x0005 */ +#define BTA_GATT_REQ_NOT_SUPPORTED GATT_REQ_NOT_SUPPORTED /* 0x0006 */ +#define BTA_GATT_INVALID_OFFSET GATT_INVALID_OFFSET /* 0x0007 */ +#define BTA_GATT_INSUF_AUTHORIZATION GATT_INSUF_AUTHORIZATION /* 0x0008 */ +#define BTA_GATT_PREPARE_Q_FULL GATT_PREPARE_Q_FULL /* 0x0009 */ +#define BTA_GATT_NOT_FOUND GATT_NOT_FOUND /* 0x000a */ +#define BTA_GATT_NOT_LONG GATT_NOT_LONG /* 0x000b */ +#define BTA_GATT_INSUF_KEY_SIZE GATT_INSUF_KEY_SIZE /* 0x000c */ +#define BTA_GATT_INVALID_ATTR_LEN GATT_INVALID_ATTR_LEN /* 0x000d */ +#define BTA_GATT_ERR_UNLIKELY GATT_ERR_UNLIKELY /* 0x000e */ +#define BTA_GATT_INSUF_ENCRYPTION GATT_INSUF_ENCRYPTION /* 0x000f */ +#define BTA_GATT_UNSUPPORT_GRP_TYPE GATT_UNSUPPORT_GRP_TYPE /* 0x0010 */ +#define BTA_GATT_INSUF_RESOURCE GATT_INSUF_RESOURCE /* 0x0011 */ + + +#define BTA_GATT_NO_RESOURCES GATT_NO_RESOURCES /* 0x80 */ +#define BTA_GATT_INTERNAL_ERROR GATT_INTERNAL_ERROR /* 0x81 */ +#define BTA_GATT_WRONG_STATE GATT_WRONG_STATE /* 0x82 */ +#define BTA_GATT_DB_FULL GATT_DB_FULL /* 0x83 */ +#define BTA_GATT_BUSY GATT_BUSY /* 0x84 */ +#define BTA_GATT_ERROR GATT_ERROR /* 0x85 */ +#define BTA_GATT_CMD_STARTED GATT_CMD_STARTED /* 0x86 */ +#define BTA_GATT_ILLEGAL_PARAMETER GATT_ILLEGAL_PARAMETER /* 0x87 */ +#define BTA_GATT_PENDING GATT_PENDING /* 0x88 */ +#define BTA_GATT_AUTH_FAIL GATT_AUTH_FAIL /* 0x89 */ +#define BTA_GATT_MORE GATT_MORE /* 0x8a */ +#define BTA_GATT_INVALID_CFG GATT_INVALID_CFG /* 0x8b */ +#define BTA_GATT_SERVICE_STARTED GATT_SERVICE_STARTED /* 0x8c */ +#define BTA_GATT_ENCRYPED_MITM GATT_ENCRYPED_MITM /* GATT_SUCCESS */ +#define BTA_GATT_ENCRYPED_NO_MITM GATT_ENCRYPED_NO_MITM /* 0x8d */ +#define BTA_GATT_NOT_ENCRYPTED GATT_NOT_ENCRYPTED /* 0x8e */ +#define BTA_GATT_CONGESTED GATT_CONGESTED /* 0x8f */ + +#define BTA_GATT_DUP_REG GATT_DUP_REG /* 0x90 */ +#define BTA_GATT_ALREADY_OPEN GATT_ALREADY_OPEN /* 0x91 */ +#define BTA_GATT_CANCEL GATT_CANCEL /* 0x92 */ + +/* 0xE0 ~ 0xFC reserved for future use */ +#define BTA_GATT_STACK_RSP GATT_STACK_RSP /* 0xE0 */ +#define BTA_GATT_APP_RSP GATT_APP_RSP /* 0xE1 */ +//Error caused by customer application or stack bug +#define BTA_GATT_UNKNOWN_ERROR GATT_UNKNOWN_ERROR /* 0XEF */ + /* 0xE0 ~ 0xFC reserved for future use */ +#define BTA_GATT_CCC_CFG_ERR GATT_CCC_CFG_ERR /* 0xFD Client Characteristic Configuration Descriptor Improperly Configured */ +#define BTA_GATT_PRC_IN_PROGRESS GATT_PRC_IN_PROGRESS /* 0xFE Procedure Already in progress */ +#define BTA_GATT_OUT_OF_RANGE GATT_OUT_OF_RANGE /* 0xFFAttribute value out of range */ + +typedef UINT8 tBTA_GATT_STATUS; + +#define BTA_GATT_INVALID_CONN_ID GATT_INVALID_CONN_ID + + +/* Client callback function events */ +#define BTA_GATTC_REG_EVT 0 /* GATT client is registered. */ +#define BTA_GATTC_DEREG_EVT 1 /* GATT client deregistered event */ +#define BTA_GATTC_OPEN_EVT 2 /* GATTC open request status event */ +#define BTA_GATTC_READ_CHAR_EVT 3 /* GATT read characteristic event */ +#define BTA_GATTC_WRITE_CHAR_EVT 4 /* GATT write characteristic or char descriptor event */ +#define BTA_GATTC_CLOSE_EVT 5 /* GATTC close request status event */ +#define BTA_GATTC_SEARCH_CMPL_EVT 6 /* GATT discovery complete event */ +#define BTA_GATTC_SEARCH_RES_EVT 7 /* GATT discovery result event */ +#define BTA_GATTC_READ_DESCR_EVT 8 /* GATT read characteristic descriptor event */ +#define BTA_GATTC_WRITE_DESCR_EVT 9 /* GATT write characteristic descriptor event */ +#define BTA_GATTC_NOTIF_EVT 10 /* GATT attribute notification event */ +#define BTA_GATTC_PREP_WRITE_EVT 11 /* GATT prepare write event */ +#define BTA_GATTC_EXEC_EVT 12 /* execute write complete event */ +#define BTA_GATTC_ACL_EVT 13 /* ACL up event */ +#define BTA_GATTC_CANCEL_OPEN_EVT 14 /* cancel open event */ +#define BTA_GATTC_SRVC_CHG_EVT 15 /* service change event */ +#define BTA_GATTC_LISTEN_EVT 16 /* listen event */ +#define BTA_GATTC_ENC_CMPL_CB_EVT 17 /* encryption complete callback event */ +#define BTA_GATTC_CFG_MTU_EVT 18 /* configure MTU complete event */ +#define BTA_GATTC_ADV_DATA_EVT 19 /* ADV data event */ +#define BTA_GATTC_MULT_ADV_ENB_EVT 20 /* Enable Multi ADV event */ +#define BTA_GATTC_MULT_ADV_UPD_EVT 21 /* Update parameter event */ +#define BTA_GATTC_MULT_ADV_DATA_EVT 22 /* Multi ADV data event */ +#define BTA_GATTC_MULT_ADV_DIS_EVT 23 /* Disable Multi ADV event */ +#define BTA_GATTC_CONGEST_EVT 24 /* Congestion event */ +#define BTA_GATTC_BTH_SCAN_ENB_EVT 25 /* Enable batch scan event */ +#define BTA_GATTC_BTH_SCAN_CFG_EVT 26 /* Config storage event */ +#define BTA_GATTC_BTH_SCAN_RD_EVT 27 /* Batch scan reports read event */ +#define BTA_GATTC_BTH_SCAN_THR_EVT 28 /* Batch scan threshold event */ +#define BTA_GATTC_BTH_SCAN_PARAM_EVT 29 /* Batch scan param event */ +#define BTA_GATTC_BTH_SCAN_DIS_EVT 30 /* Disable batch scan event */ +#define BTA_GATTC_SCAN_FLT_CFG_EVT 31 /* Scan filter config event */ +#define BTA_GATTC_SCAN_FLT_PARAM_EVT 32 /* Param filter event */ +#define BTA_GATTC_SCAN_FLT_STATUS_EVT 33 /* Filter status event */ +#define BTA_GATTC_ADV_VSC_EVT 34 /* ADV VSC event */ +#define BTA_GATTC_CONNECT_EVT 35 /* GATTC CONNECT event */ +#define BTA_GATTC_DISCONNECT_EVT 36 /* GATTC DISCONNECT event */ +#define BTA_GATTC_READ_MULTIPLE_EVT 37 /* GATTC Read mutiple event */ +#define BTA_GATTC_QUEUE_FULL_EVT 38 /* GATTC queue full event */ +#define BTA_GATTC_ASSOC_EVT 39 /* GATTC association address event */ +#define BTA_GATTC_GET_ADDR_LIST_EVT 40 /* GATTC get address list in the cache event */ +#define BTA_GATTC_DIS_SRVC_CMPL_EVT 41 /* GATTC discover service complete */ +#define BTA_GATTC_READ_MULTI_VAR_EVT 42 /* GATTC read multiple variable event */ + +typedef UINT8 tBTA_GATTC_EVT; + +typedef tGATT_IF tBTA_GATTC_IF; + +typedef UINT8 tBTA_ADDR_TYPE; + +typedef struct { + UINT16 unit; /* as UUIUD defined by SIG */ + UINT16 descr; /* as UUID as defined by SIG */ + tGATT_FORMAT format; + INT8 exp; + UINT8 name_spc; /* The name space of the description */ +} tBTA_GATT_CHAR_PRES; + +typedef struct { + UINT16 interval; + UINT16 latency; + UINT16 timeout; +} tBTA_GATT_CONN_PARAMS; + +#define BTA_GATT_CLT_CONFIG_NONE GATT_CLT_CONFIG_NONE /* 0x0000 */ +#define BTA_GATT_CLT_CONFIG_NOTIFICATION GATT_CLT_CONFIG_NOTIFICATION /* 0x0001 */ +#define BTA_GATT_CLT_CONFIG_INDICATION GATT_CLT_CONFIG_INDICATION /* 0x0002 */ +typedef UINT16 tBTA_GATT_CLT_CHAR_CONFIG; + +/* characteristic descriptor: server configuration value +*/ +#define BTA_GATT_SVR_CONFIG_NONE GATT_SVR_CONFIG_NONE /* 0x0000 */ +#define BTA_GATT_SVR_CONFIG_BROADCAST GATT_SVR_CONFIG_BROADCAST /* 0x0001 */ +typedef UINT16 tBTA_GATT_SVR_CHAR_CONFIG; + +/* Characteristic Aggregate Format attribute value +*/ +#define BTA_GATT_AGGR_HANDLE_NUM_MAX 10 +typedef struct { + UINT8 num_handle; + UINT16 handle_list[BTA_GATT_AGGR_HANDLE_NUM_MAX]; +} tBTA_GATT_CHAR_AGGRE; +typedef tGATT_VALID_RANGE tBTA_GATT_VALID_RANGE; + +typedef struct { + UINT16 len; + UINT8 *p_value; +} tBTA_GATT_UNFMT; + +#define BTA_GATT_MAX_ATTR_LEN GATT_MAX_ATTR_LEN + +#define BTA_GATTC_TYPE_WRITE GATT_WRITE +#define BTA_GATTC_TYPE_WRITE_NO_RSP GATT_WRITE_NO_RSP +typedef UINT8 tBTA_GATTC_WRITE_TYPE; + +/* relate to ESP_GATT_CONN_xxx in esp_gatt_defs.h */ +#define BTA_GATT_CONN_UNKNOWN 0 +#define BTA_GATT_CONN_L2C_FAILURE GATT_CONN_L2C_FAILURE /* general l2cap resource failure */ +#define BTA_GATT_CONN_TIMEOUT GATT_CONN_TIMEOUT /* 0x08 connection timeout */ +#define BTA_GATT_CONN_TERMINATE_PEER_USER GATT_CONN_TERMINATE_PEER_USER /* 0x13 connection terminate by peer user */ +#define BTA_GATT_CONN_TERMINATE_LOCAL_HOST GATT_CONN_TERMINATE_LOCAL_HOST/* 0x16 connectionterminated by local host */ +#define BTA_GATT_CONN_FAIL_ESTABLISH GATT_CONN_FAIL_ESTABLISH /* 0x03E connection fail to establish */ +#define BTA_GATT_CONN_LMP_TIMEOUT GATT_CONN_LMP_TIMEOUT /* 0x22 connection fail for LMP response tout */ +#define BTA_GATT_CONN_CANCEL GATT_CONN_CANCEL /* 0x0100 L2CAP connection cancelled */ +#define BTA_GATT_CONN_NONE 0x0101 /* 0x0101 no connection to cancel */ +typedef UINT16 tBTA_GATT_REASON; + +typedef struct { + tBTA_GATT_ID id; + BOOLEAN is_primary; +} tBTA_GATT_SRVC_ID; + + +#define BTA_GATTC_MULTI_MAX GATT_MAX_READ_MULTI_HANDLES + +typedef struct { + UINT8 num_attr; + UINT16 handles[BTA_GATTC_MULTI_MAX]; +}tBTA_GATTC_MULTI; + +/* relate to ESP_GATT_xxx in esp_gatt_def.h */ +#define BTA_GATT_AUTH_REQ_NONE GATT_AUTH_REQ_NONE +#define BTA_GATT_AUTH_REQ_NO_MITM GATT_AUTH_REQ_NO_MITM /* unauthenticated encryption */ +#define BTA_GATT_AUTH_REQ_MITM GATT_AUTH_REQ_MITM /* authenticated encryption */ +#define BTA_GATT_AUTH_REQ_SIGNED_NO_MITM GATT_AUTH_REQ_SIGNED_NO_MITM +#define BTA_GATT_AUTH_REQ_SIGNED_MITM GATT_AUTH_REQ_SIGNED_MITM + +typedef tGATT_AUTH_REQ tBTA_GATT_AUTH_REQ; + +enum { + BTA_GATTC_ATTR_TYPE_INCL_SRVC, + BTA_GATTC_ATTR_TYPE_CHAR, + BTA_GATTC_ATTR_TYPE_CHAR_DESCR, + BTA_GATTC_ATTR_TYPE_SRVC +}; +typedef UINT8 tBTA_GATTC_ATTR_TYPE; + + +typedef struct { + tBT_UUID uuid; + UINT16 s_handle; + UINT16 e_handle; /* used for service only */ + UINT8 attr_type; + UINT8 id; + UINT8 prop; /* used when attribute type is characteristic */ + BOOLEAN is_primary; /* used when attribute type is service */ + UINT16 incl_srvc_s_handle; /* used when attribute type is included service */ + UINT16 incl_srvc_e_handle; /* used when attribute type is included service */ +}tBTA_GATTC_NV_ATTR; + +/* callback data structure */ +typedef struct { + tBTA_GATT_STATUS status; + tBTA_GATTC_IF client_if; + tBT_UUID app_uuid; +}tBTA_GATTC_REG; + +typedef struct { + UINT16 conn_id; + tBTA_GATT_STATUS status; + UINT16 handle; + tBTA_GATT_UNFMT *p_value; +}tBTA_GATTC_READ; + +typedef struct { + UINT16 conn_id; + tBTA_GATT_STATUS status; + UINT16 handle; + UINT16 offset; +}tBTA_GATTC_WRITE; + +typedef struct { + UINT16 conn_id; + tBTA_GATT_STATUS status; +} tBTA_GATTC_EXEC_CMPL; + +typedef struct { + UINT16 conn_id; + tBTA_GATT_STATUS status; + UINT8 searched_service_source; +} tBTA_GATTC_SEARCH_CMPL; + +typedef struct { + UINT16 conn_id; + tBTA_GATT_STATUS status; +}tBTA_GATTC_DIS_CMPL; + +typedef struct { + UINT16 conn_id; + UINT16 start_handle; + UINT16 end_handle; + tBTA_GATT_ID service_uuid; + bool is_primary; +}tBTA_GATTC_SRVC_RES; + +typedef struct { + UINT16 conn_id; + tBTA_GATT_STATUS status; + UINT16 mtu; +} tBTA_GATTC_CFG_MTU; + +typedef struct { + tBTA_GATT_STATUS status; + UINT16 conn_id; + tBTA_GATTC_IF client_if; + BD_ADDR remote_bda; + tBTA_TRANSPORT transport; + UINT16 mtu; +} tBTA_GATTC_OPEN; + +typedef struct { + tBTA_GATT_STATUS status; + UINT16 conn_id; + tBTA_GATTC_IF client_if; + BD_ADDR remote_bda; + tBTA_GATT_REASON reason; /* disconnect reason code, not useful when connect event is reported */ +} tBTA_GATTC_CLOSE; + +typedef struct { + UINT16 conn_id; + BD_ADDR bda; + UINT16 handle; + UINT16 len; + UINT8 value[BTA_GATT_MAX_ATTR_LEN]; + BOOLEAN is_notify; +} tBTA_GATTC_NOTIFY; + +typedef struct { + UINT16 conn_id; + BOOLEAN congested; /* congestion indicator */ +} tBTA_GATTC_CONGEST; + +typedef struct { + tBTA_GATT_STATUS status; + UINT16 conn_id; + BOOLEAN is_full; +} tBTA_GATTC_QUEUE_FULL; + +typedef struct { + tBTA_GATT_STATUS status; + tBTA_GATTC_IF client_if; +} tBTA_GATTC_SET_ASSOC; + +typedef struct { + tBTA_GATT_STATUS status; + tBTA_GATTC_IF client_if; + UINT8 num_addr; + BD_ADDR *bda_list; +} tBTA_GATTC_GET_ADDR_LIST; + +typedef struct { + tBTA_GATT_STATUS status; + tBTA_GATTC_IF client_if; + UINT16 conn_id; + BD_ADDR remote_bda; +} tBTA_GATTC_OPEN_CLOSE; + +typedef struct { + tBTA_GATTC_IF client_if; + BD_ADDR remote_bda; +} tBTA_GATTC_ENC_CMPL_CB; + +typedef struct { + UINT16 conn_id; + UINT8 link_role; + tBTA_GATTC_IF client_if; + BD_ADDR remote_bda; + tBTA_GATT_CONN_PARAMS conn_params; + UINT8 ble_addr_type; + UINT16 conn_handle; +} tBTA_GATTC_CONNECT; + +typedef struct { + tGATT_DISCONN_REASON reason; + UINT16 conn_id; + tBTA_GATTC_IF client_if; + BD_ADDR remote_bda; +} tBTA_GATTC_DISCONNECT; + +typedef struct { + UINT16 conn_id; + BD_ADDR remote_bda; +} tBTA_GATTC_SERVICE_CHANGE; + +typedef union { + tBTA_GATT_STATUS status; + tBTA_GATTC_DIS_CMPL dis_cmpl; /* discovery complete */ + tBTA_GATTC_SEARCH_CMPL search_cmpl; /* search complete */ + tBTA_GATTC_SRVC_RES srvc_res; /* discovery result */ + tBTA_GATTC_REG reg_oper; /* registration data */ + tBTA_GATTC_OPEN open; + tBTA_GATTC_CONNECT connect; + tBTA_GATTC_CLOSE close; + tBTA_GATTC_DISCONNECT disconnect; + tBTA_GATTC_READ read; /* read attribute/descriptor data */ + tBTA_GATTC_WRITE write; /* write complete data */ + tBTA_GATTC_EXEC_CMPL exec_cmpl; /* execute complete */ + tBTA_GATTC_NOTIFY notify; /* notification/indication event data */ + tBTA_GATTC_ENC_CMPL_CB enc_cmpl; + tBTA_GATTC_CFG_MTU cfg_mtu; /* configure MTU operation */ + tBTA_GATTC_CONGEST congest; + tBTA_GATTC_QUEUE_FULL queue_full; + tBTA_GATTC_SERVICE_CHANGE srvc_chg; /* service change event */ + tBTA_GATTC_SET_ASSOC set_assoc; + tBTA_GATTC_GET_ADDR_LIST get_addr_list; +} tBTA_GATTC; + +/* GATTC enable callback function */ +typedef void (tBTA_GATTC_ENB_CBACK)(tBTA_GATT_STATUS status); + +/* Client callback function */ +typedef void (tBTA_GATTC_CBACK)(tBTA_GATTC_EVT event, tBTA_GATTC *p_data); + +/* GATT Server Data Structure */ +/* Server callback function events */ +#define BTA_GATTS_REG_EVT 0 +#define BTA_GATTS_READ_EVT GATTS_REQ_TYPE_READ /* 1 */ +#define BTA_GATTS_WRITE_EVT GATTS_REQ_TYPE_WRITE /* 2 */ +#define BTA_GATTS_EXEC_WRITE_EVT GATTS_REQ_TYPE_WRITE_EXEC /* 3 */ +#define BTA_GATTS_MTU_EVT GATTS_REQ_TYPE_MTU /* 4 */ +#define BTA_GATTS_CONF_EVT GATTS_REQ_TYPE_CONF /* 5 */ +#define BTA_GATTS_DEREG_EVT 6 +#define BTA_GATTS_CREATE_EVT 7 +#define BTA_GATTS_ADD_INCL_SRVC_EVT 8 +#define BTA_GATTS_ADD_CHAR_EVT 9 +#define BTA_GATTS_ADD_CHAR_DESCR_EVT 10 +#define BTA_GATTS_DELELTE_EVT 11 +#define BTA_GATTS_START_EVT 12 +#define BTA_GATTS_STOP_EVT 13 +#define BTA_GATTS_CONNECT_EVT 14 +#define BTA_GATTS_DISCONNECT_EVT 15 +#define BTA_GATTS_OPEN_EVT 16 +#define BTA_GATTS_CANCEL_OPEN_EVT 17 +#define BTA_GATTS_CLOSE_EVT 18 +#define BTA_GATTS_LISTEN_EVT 19 +#define BTA_GATTS_CONGEST_EVT 20 +#define BTA_GATTS_SET_ATTR_VAL_EVT 23 +#define BTA_GATTS_SEND_SERVICE_CHANGE_EVT 24 + +typedef UINT8 tBTA_GATTS_EVT; +typedef tGATT_IF tBTA_GATTS_IF; + +/* Attribute permissions +*/ +#define BTA_GATT_PERM_READ GATT_PERM_READ /* bit 0 - 0x0001 */ +#define BTA_GATT_PERM_READ_ENCRYPTED GATT_PERM_READ_ENCRYPTED /* bit 1 - 0x0002 */ +#define BTA_GATT_PERM_READ_ENC_MITM GATT_PERM_READ_ENC_MITM /* bit 2 - 0x0004 */ +#define BTA_GATT_PERM_WRITE GATT_PERM_WRITE /* bit 4 - 0x0010 */ +#define BTA_GATT_PERM_WRITE_ENCRYPTED GATT_PERM_WRITE_ENCRYPTED /* bit 5 - 0x0020 */ +#define BTA_GATT_PERM_WRITE_ENC_MITM GATT_PERM_WRITE_ENC_MITM /* bit 6 - 0x0040 */ +#define BTA_GATT_PERM_WRITE_SIGNED GATT_PERM_WRITE_SIGNED /* bit 7 - 0x0080 */ +#define BTA_GATT_PERM_WRITE_SIGNED_MITM GATT_PERM_WRITE_SIGNED_MITM /* bit 8 - 0x0100 */ +#define BTA_GATT_PERM_READ_AUTHORIZATION GATT_PERM_READ_AUTHORIZATION /* bit 9 - 0x0200 */ +#define BTA_GATT_PERM_WRITE_AUTHORIZATION GATT_PERM_WRITE_AUTHORIZATION /* bit 10 - 0x0400 */ +typedef UINT16 tBTA_GATT_PERM; +typedef tGATT_ATTR_VAL tBTA_GATT_ATTR_VAL; +typedef tGATTS_ATTR_CONTROL tBTA_GATTS_ATTR_CONTROL; + +#define BTA_GATTS_INVALID_APP 0xff + +#define BTA_GATTS_INVALID_IF 0 + +/* definition of characteristic properties */ +#define BTA_GATT_CHAR_PROP_BIT_BROADCAST GATT_CHAR_PROP_BIT_BROADCAST /* 0x01 */ +#define BTA_GATT_CHAR_PROP_BIT_READ GATT_CHAR_PROP_BIT_READ /* 0x02 */ +#define BTA_GATT_CHAR_PROP_BIT_WRITE_NR GATT_CHAR_PROP_BIT_WRITE_NR /* 0x04 */ +#define BTA_GATT_CHAR_PROP_BIT_WRITE GATT_CHAR_PROP_BIT_WRITE /* 0x08 */ +#define BTA_GATT_CHAR_PROP_BIT_NOTIFY GATT_CHAR_PROP_BIT_NOTIFY /* 0x10 */ +#define BTA_GATT_CHAR_PROP_BIT_INDICATE GATT_CHAR_PROP_BIT_INDICATE /* 0x20 */ +#define BTA_GATT_CHAR_PROP_BIT_AUTH GATT_CHAR_PROP_BIT_AUTH /* 0x40 */ +#define BTA_GATT_CHAR_PROP_BIT_EXT_PROP GATT_CHAR_PROP_BIT_EXT_PROP /* 0x80 */ +typedef UINT8 tBTA_GATT_CHAR_PROP; + +#ifndef BTA_GATTC_CHAR_DESCR_MAX +#define BTA_GATTC_CHAR_DESCR_MAX 7 +#endif + +/*********************** NV callback Data Definitions ********************** +*/ +typedef struct { + tBT_UUID app_uuid128; + tBT_UUID svc_uuid; + UINT16 svc_inst; + UINT16 s_handle; + UINT16 e_handle; + BOOLEAN is_primary; /* primary service or secondary */ +} tBTA_GATTS_HNDL_RANGE; + +#define BTA_GATTS_SRV_CHG_CMD_ADD_CLIENT GATTS_SRV_CHG_CMD_ADD_CLIENT +#define BTA_GATTS_SRV_CHG_CMD_UPDATE_CLIENT GATTS_SRV_CHG_CMD_UPDATE_CLIENT +#define BTA_GATTS_SRV_CHG_CMD_REMOVE_CLIENT GATTS_SRV_CHG_CMD_REMOVE_CLIENT +#define BTA_GATTS_SRV_CHG_CMD_READ_NUM_CLENTS GATTS_SRV_CHG_CMD_READ_NUM_CLENTS +#define BTA_GATTS_SRV_CHG_CMD_READ_CLENT GATTS_SRV_CHG_CMD_READ_CLENT +typedef tGATTS_SRV_CHG_CMD tBTA_GATTS_SRV_CHG_CMD; + +typedef tGATTS_SRV_CHG tBTA_GATTS_SRV_CHG; +typedef tGATTS_SRV_CHG_REQ tBTA_GATTS_SRV_CHG_REQ; +typedef tGATTS_SRV_CHG_RSP tBTA_GATTS_SRV_CHG_RSP; + +#define BTA_GATT_TRANSPORT_LE GATT_TRANSPORT_LE +#define BTA_GATT_TRANSPORT_BR_EDR GATT_TRANSPORT_BR_EDR +#define BTA_GATT_TRANSPORT_LE_BR_EDR GATT_TRANSPORT_LE_BR_EDR +typedef UINT8 tBTA_GATT_TRANSPORT; + +/* attribute value */ +typedef tGATT_VALUE tBTA_GATT_VALUE; + +/* attribute response data */ +typedef tGATTS_RSP tBTA_GATTS_RSP; + +/* relate to ESP_GATT_PREP_WRITE_xxx in esp_gatt_defs.h */ +/* attribute request data from the client */ +#define BTA_GATT_PREP_WRITE_CANCEL 0x00 +#define BTA_GATT_PREP_WRITE_EXEC 0x01 +typedef tGATT_EXEC_FLAG tBTA_GATT_EXEC_FLAG; + +/* read request always based on UUID */ +typedef tGATT_READ_REQ tBTA_GATT_READ_REQ; + +/* write request data */ +typedef tGATT_WRITE_REQ tBTA_GATT_WRITE_REQ; + +/* callback data for server access request from client */ +typedef tGATTS_DATA tBTA_GATTS_REQ_DATA; + +typedef struct { + tBTA_GATT_STATUS status; + BD_ADDR remote_bda; + UINT32 trans_id; + UINT16 conn_id; + UINT16 handle; + tBTA_GATTS_REQ_DATA *p_data; + UINT16 data_len; + UINT8 *value; +} tBTA_GATTS_REQ; + +typedef struct { + tBTA_GATTS_IF server_if; + tBTA_GATT_STATUS status; + tBT_UUID uuid; +}tBTA_GATTS_REG_OPER; + + +typedef struct { + tBTA_GATTS_IF server_if; + UINT16 service_id; + UINT16 svc_instance; + BOOLEAN is_primary; + tBTA_GATT_STATUS status; + tBT_UUID uuid; +}tBTA_GATTS_CREATE; + +typedef struct { + tBTA_GATTS_IF server_if; + UINT16 service_id; + UINT16 attr_id; + tBTA_GATT_STATUS status; + tBT_UUID char_uuid; +}tBTA_GATTS_ADD_RESULT; +typedef struct{ + tBTA_GATTS_IF server_if; + UINT16 service_id; + UINT16 attr_id; + tBTA_GATT_STATUS status; +}tBAT_GATTS_ATTR_VAL_RESULT; + +typedef struct { + tBTA_GATTS_IF server_if; + UINT16 service_id; + tBTA_GATT_STATUS status; +} tBTA_GATTS_SRVC_OPER; + + +typedef struct { + tBTA_GATTS_IF server_if; + BD_ADDR remote_bda; + UINT16 conn_id; + UINT8 link_role; + tBTA_GATT_REASON reason; /* report disconnect reason */ + tBTA_GATT_TRANSPORT transport; + tBTA_GATT_CONN_PARAMS conn_params; + UINT8 ble_addr_type; + UINT16 conn_handle; +} tBTA_GATTS_CONN; + +typedef struct { + UINT16 conn_id; + BOOLEAN congested; /* report channel congestion indicator */ +} tBTA_GATTS_CONGEST; + +typedef struct { + UINT16 conn_id; /* connection ID */ + tBTA_GATT_STATUS status; /* notification/indication status */ +} tBTA_GATTS_CONF; + +typedef struct { + tBTA_GATT_STATUS status; + UINT16 conn_id; /* connection ID */ +} tBTA_GATTS_CLOSE; + +typedef struct { + tBTA_GATT_STATUS status; + tBTA_GATTS_IF server_if; +} tBTA_GATTS_SERVICE_CHANGE; + +typedef struct { + tBTA_GATT_STATUS status; + tBTA_GATTS_IF server_if; +} tBTA_GATTS_OPEN; + +typedef struct { + tBTA_GATT_STATUS status; + tBTA_GATTS_IF server_if; +} tBTA_GATTS_CANCEL_OPEN; +/* GATTS callback data */ +typedef union { + tBTA_GATTS_REG_OPER reg_oper; + tBTA_GATTS_CREATE create; + tBTA_GATTS_SRVC_OPER srvc_oper; + tBTA_GATT_STATUS status; /* BTA_GATTS_LISTEN_EVT */ + tBTA_GATTS_ADD_RESULT add_result; /* add included service: BTA_GATTS_ADD_INCL_SRVC_EVT + add char : BTA_GATTS_ADD_CHAR_EVT + add char descriptor: BTA_GATTS_ADD_CHAR_DESCR_EVT */ + tBAT_GATTS_ATTR_VAL_RESULT attr_val; + tBTA_GATTS_REQ req_data; + tBTA_GATTS_CONN conn; /* BTA_GATTS_CONN_EVT */ + tBTA_GATTS_CONGEST congest; /* BTA_GATTS_CONGEST_EVT callback data */ + tBTA_GATTS_CONF confirm; /* BTA_GATTS_CONF_EVT callback data */ + tBTA_GATTS_CLOSE close; /* BTA_GATTS_CLOSE_EVT callback data */ + tBTA_GATTS_OPEN open; /* BTA_GATTS_OPEN_EVT callback data */ + tBTA_GATTS_CANCEL_OPEN cancel_open; /* tBTA_GATTS_CANCEL_OPEN callback data */ + tBTA_GATTS_SERVICE_CHANGE service_change; + +} tBTA_GATTS; + +/* GATTC wait for service change ccc timer callback data */ +typedef struct { + UINT16 conn_id; + BD_ADDR remote_bda; + UINT8 count; + UINT8 last_status; +}tBTA_GATTC_WAIT_CCC_TIMER; + +/* GATTS enable callback function */ +typedef void (tBTA_GATTS_ENB_CBACK)(tBTA_GATT_STATUS status); + +/* Server callback function */ +typedef void (tBTA_GATTS_CBACK)(tBTA_GATTS_EVT event, tBTA_GATTS *p_data); +typedef struct +{ + tBT_UUID uuid; + BOOLEAN is_primary; + UINT16 handle; + UINT16 s_handle; + UINT16 e_handle; + list_t *characteristics; /* list of tBTA_GATTC_CHARACTERISTIC */ + list_t *included_svc; /* list of tBTA_GATTC_INCLUDED_SVC */ +} __attribute__((packed)) tBTA_GATTC_SERVICE; + +typedef struct +{ + tBT_UUID uuid; + UINT16 handle; + tBTA_GATT_CHAR_PROP properties; + tBTA_GATTC_SERVICE *service; /* owning service*/ + list_t *descriptors; /* list of tBTA_GATTC_DESCRIPTOR */ +} __attribute__((packed)) tBTA_GATTC_CHARACTERISTIC; + +typedef struct +{ + tBT_UUID uuid; + UINT16 handle; + tBTA_GATTC_CHARACTERISTIC *characteristic; /* owning characteristic */ +} __attribute__((packed)) tBTA_GATTC_DESCRIPTOR; + +typedef struct +{ + tBT_UUID uuid; + UINT16 handle; + UINT16 incl_srvc_s_handle; + UINT16 incl_srvc_e_handle; + tBTA_GATTC_SERVICE *owning_service; /* owning service*/ + tBTA_GATTC_SERVICE *included_service; +} __attribute__((packed)) tBTA_GATTC_INCLUDED_SVC; + +/***************************************************************************** +** External Function Declarations +*****************************************************************************/ + +#ifdef __cplusplus +extern "C" +{ +#endif + +/************************** +** Client Functions +***************************/ + +/******************************************************************************* +** +** Function BTA_GATTC_Disable +** +** Description This function is called to disable the GATTC module +** +** Parameters None. +** +** Returns None +** +*******************************************************************************/ +extern void BTA_GATTC_Disable(void); + +/******************************************************************************* +** +** Function BTA_GATTC_AppRegister +** +** Description This function is called to register application callbacks +** with BTA GATTC module. +** +** Parameters p_app_uuid - application UUID +** p_client_cb - pointer to the application callback function. +** +** Returns None +** +*******************************************************************************/ +extern void BTA_GATTC_AppRegister(tBT_UUID *p_app_uuid, tBTA_GATTC_CBACK *p_client_cb); + +/******************************************************************************* +** +** Function BTA_GATTC_AppDeregister +** +** Description This function is called to deregister an application +** from BTA GATTC module. +** +** Parameters client_if - client interface identifier. +** +** Returns None +** +*******************************************************************************/ +extern void BTA_GATTC_AppDeregister (tBTA_GATTC_IF client_if); + +/******************************************************************************* +** +** Function BTA_GATTC_Open +** +** Description Open a direct connection or add a background auto connection +** bd address +** +** Parameters client_if: server interface. +** remote_bda: remote device BD address. +** remote_addr_type: remote device BD address type. +** is_direct: direct connection or background auto connection +** +** Returns void +** +*******************************************************************************/ +extern void BTA_GATTC_Open(tBTA_GATTC_IF client_if, BD_ADDR remote_bda, tBTA_ADDR_TYPE remote_addr_type, + BOOLEAN is_direct, tBTA_GATT_TRANSPORT transport, BOOLEAN is_aux); + +/******************************************************************************* +** +** Function BTA_GATTC_CancelOpen +** +** Description Open a direct connection or add a background auto connection +** bd address +** +** Parameters client_if: server interface. +** remote_bda: remote device BD address. +** is_direct: direct connection or background auto connection +** +** Returns void +** +*******************************************************************************/ +extern void BTA_GATTC_CancelOpen(tBTA_GATTC_IF client_if, BD_ADDR remote_bda, BOOLEAN is_direct); + +/******************************************************************************* +** +** Function BTA_GATTC_Close +** +** Description Close a connection to a GATT server. +** +** Parameters conn_id: connection ID to be closed. +** +** Returns void +** +*******************************************************************************/ +extern void BTA_GATTC_Close(UINT16 conn_id); + +/******************************************************************************* +** +** Function BTA_GATTC_ServiceSearchRequest +** +** Description This function is called to request a GATT service discovery +** on a GATT server. This function report service search result +** by a callback event, and followed by a service search complete +** event. +** +** Parameters conn_id: connection ID. +** p_srvc_uuid: a UUID of the service application is interested in. +** If Null, discover for all services. +** +** Returns None +** +*******************************************************************************/ +extern void BTA_GATTC_ServiceSearchRequest(UINT16 conn_id, tBT_UUID *p_srvc_uuid); + +/******************************************************************************* +** +** Function BTA_GATTC_GetServices +** +** Description This function is called to find the services on the given server. +** +** Parameters conn_id: connection ID which identify the server. +** +** Returns returns list_t of tBTA_GATTC_SERVICE or NULL. +** +*******************************************************************************/ +extern const list_t* BTA_GATTC_GetServices(UINT16 conn_id); + +/******************************************************************************* +** +** Function BTA_GATTC_GetCharacteristic +** +** Description This function is called to find the characteristic on the given server. +** +** Parameters conn_id: connection ID which identify the server. +** handle: characteristic handle +** +** Returns returns pointer to tBTA_GATTC_CHARACTERISTIC or NULL. +** +*******************************************************************************/ +extern const tBTA_GATTC_CHARACTERISTIC* BTA_GATTC_GetCharacteristic(UINT16 conn_id, UINT16 handle); + +/******************************************************************************* +** +** Function BTA_GATTC_GetDescriptor +** +** Description This function is called to find the characteristic on the given server. +** +** Parameters conn_id: connection ID which identify the server. +** handle: descriptor handle +** +** Returns returns pointer to tBTA_GATTC_DESCRIPTOR or NULL. +** +*******************************************************************************/ +extern const tBTA_GATTC_DESCRIPTOR* BTA_GATTC_GetDescriptor(UINT16 conn_id, UINT16 handle); + +extern void BTA_GATTC_GetServiceWithUUID(UINT16 conn_id, tBT_UUID *svc_uuid, + btgatt_db_element_t **db, UINT16 *count); + +extern void BTA_GATTC_GetAllChar(UINT16 conn_id, UINT16 start_handle, UINT16 end_handle, + btgatt_db_element_t **db, UINT16 *count); + +extern void BTA_GATTC_GetAllDescriptor(UINT16 conn_id, UINT16 char_handle, + btgatt_db_element_t **db, UINT16 *count); + +extern void BTA_GATTC_GetCharByUUID(UINT16 conn_id, UINT16 start_handle, UINT16 end_handle, tBT_UUID char_uuid, + btgatt_db_element_t **db, UINT16 *count); + +extern void BTA_GATTC_GetDescrByUUID(UINT16 conn_id, uint16_t start_handle, uint16_t end_handle, + tBT_UUID char_uuid, tBT_UUID descr_uuid, + btgatt_db_element_t **db, UINT16 *count); + +extern void BTA_GATTC_GetDescrByCharHandle(UINT16 conn_id, UINT16 char_handle, tBT_UUID descr_uuid, + btgatt_db_element_t **db, UINT16 *count); + +extern void BTA_GATTC_GetIncludeService(UINT16 conn_id, UINT16 start_handle, UINT16 end_handle, + tBT_UUID *incl_uuid, btgatt_db_element_t **db, UINT16 *count); + +extern void BTA_GATTC_GetDBSize(UINT16 conn_id, UINT16 start_handle, UINT16 end_handle, UINT16 *count); + +extern void BTA_GATTC_GetDBSizeByType(UINT16 conn_id, bt_gatt_db_attribute_type_t type, + UINT16 start_handle, UINT16 end_handle, UINT16 char_handle, UINT16 *count); + +/******************************************************************************* +** +** Function BTA_GATTC_GetGattDb +** +** Description This function is called to get gatt db. +** +** Parameters conn_id: connection ID which identify the server. +** db: output parameter which will contain gatt db copy. +** Caller is responsible for freeing it. +** count: number of elements in db. +** +*******************************************************************************/ +extern void BTA_GATTC_GetGattDb(UINT16 conn_id, UINT16 start_handle, UINT16 end_handle, + btgatt_db_element_t **db, UINT16 *count); + +/******************************************************************************* +** +** Function BTA_GATTC_ReadCharacteristic +** +** Description This function is called to read a characteristics value +** +** Parameters conn_id - connectino ID. +** handle - characteritic handle to read. +** +** Returns None +** +*******************************************************************************/ +void BTA_GATTC_ReadCharacteristic(UINT16 conn_id, UINT16 handle, tBTA_GATT_AUTH_REQ auth_req); + +/******************************************************************************* +** +** Function BTA_GATTC_Read_by_type +** +** Description This function is called to read a attribute value by uuid +** +** Parameters conn_id - connection ID. +** s_handle - start handle. +** e_handle - end hanle +** uuid - The attribute UUID. +** +** Returns None +** +*******************************************************************************/ +void BTA_GATTC_Read_by_type(UINT16 conn_id, UINT16 s_handle,UINT16 e_handle, tBT_UUID *uuid, tBTA_GATT_AUTH_REQ auth_req); + +/******************************************************************************* +** +** Function BTA_GATTC_ReadCharDescr +** +** Description This function is called to read a descriptor value. +** +** Parameters conn_id - connection ID. +** handle - descriptor handle to read. +** +** Returns None +** +*******************************************************************************/ +void BTA_GATTC_ReadCharDescr (UINT16 conn_id, UINT16 handle, tBTA_GATT_AUTH_REQ auth_req); + +/******************************************************************************* +** +** Function BTA_GATTC_WriteCharValue +** +** Description This function is called to write characteristic value. +** +** Parameters conn_id - connection ID. +** handle - characteristic handle to write. +** write_type - type of write. +** len: length of the data to be written. +** p_value - the value to be written. +** +** Returns None +** +*******************************************************************************/ +void BTA_GATTC_WriteCharValue ( UINT16 conn_id, + UINT16 handle, + tBTA_GATTC_WRITE_TYPE write_type, + UINT16 len, + UINT8 *p_value, + tBTA_GATT_AUTH_REQ auth_req); + +/******************************************************************************* +** +** Function BTA_GATTC_WriteCharDescr +** +** Description This function is called to write descriptor value. +** +** Parameters conn_id - connection ID +** handle - descriptor handle to write. +** write_type - type of write. +** p_value - the value to be written. +** +** Returns None +** +*******************************************************************************/ +void BTA_GATTC_WriteCharDescr (UINT16 conn_id, + UINT16 handle, + tBTA_GATTC_WRITE_TYPE write_type, + tBTA_GATT_UNFMT *p_data, + tBTA_GATT_AUTH_REQ auth_req); + +/******************************************************************************* +** +** Function BTA_GATTC_SendIndConfirm +** +** Description This function is called to send handle value confirmation. +** +** Parameters conn_id - connection ID. +** handle - characteristic handle to confirm. +** +** Returns None +** +*******************************************************************************/ +extern void BTA_GATTC_SendIndConfirm (UINT16 conn_id, UINT16 handle); + +/******************************************************************************* +** +** Function BTA_GATTC_RegisterForNotifications +** +** Description This function is called to register for notification of a service. +** +** Parameters client_if - client interface. +** remote_bda - target GATT server. +** handle - GATT characteristic handle. +** +** Returns OK if registration succeed, otherwise failed. +** +*******************************************************************************/ +extern tBTA_GATT_STATUS BTA_GATTC_RegisterForNotifications (tBTA_GATTC_IF client_if, + BD_ADDR remote_bda, + UINT16 handle); + +/******************************************************************************* +** +** Function BTA_GATTC_DeregisterForNotifications +** +** Description This function is called to de-register for notification of a servbice. +** +** Parameters client_if - client interface. +** remote_bda - target GATT server. +** handle - GATT characteristic handle. +** +** Returns OK if deregistration succeed, otherwise failed. +** +*******************************************************************************/ +extern tBTA_GATT_STATUS BTA_GATTC_DeregisterForNotifications (tBTA_GATTC_IF client_if, + BD_ADDR remote_bda, + UINT16 handle); + +/******************************************************************************* +** +** Function BTA_GATTC_PrepareWrite +** +** Description This function is called to prepare write a characteristic value. +** +** Parameters conn_id - connection ID. +** handle - GATT characteritic handle. +** offset - offset of the write value. +** len - length of the data to be written. +** p_value - the value to be written. +** +** Returns None +** +*******************************************************************************/ +extern void BTA_GATTC_PrepareWrite (UINT16 conn_id, + UINT16 handle, + UINT16 offset, + UINT16 len, + UINT8 *p_value, + tBTA_GATT_AUTH_REQ auth_req); + +/******************************************************************************* +** +** Function BTA_GATTC_PrepareWriteCharDescr +** +** Description This function is called to prepare write a characteristic descriptor value. +** +** Parameters conn_id - connection ID. +** p_char_descr_id - GATT characteritic descriptor ID of the service. +** offset - offset of the write value. +** len: length of the data to be written. +** p_value - the value to be written. +** +** Returns None +** +*******************************************************************************/ +extern void BTA_GATTC_PrepareWriteCharDescr (UINT16 conn_id, + UINT16 handle, + UINT16 offset, + tBTA_GATT_UNFMT *p_data, + tBTA_GATT_AUTH_REQ auth_req); +/******************************************************************************* +** +** Function BTA_GATTC_ExecuteWrite +** +** Description This function is called to execute write a prepare write sequence. +** +** Parameters conn_id - connection ID. +** is_execute - execute or cancel. +** +** Returns None +** +*******************************************************************************/ +extern void BTA_GATTC_ExecuteWrite (UINT16 conn_id, BOOLEAN is_execute); + +/******************************************************************************* +** +** Function BTA_GATTC_ReadMultiple +** +** Description This function is called to read multiple characteristic or +** characteristic descriptors. +** +** Parameters conn_id - connection ID. +** p_read_multi - read multiple parameters. +** +** Returns None +** +*******************************************************************************/ +extern void BTA_GATTC_ReadMultiple(UINT16 conn_id, tBTA_GATTC_MULTI *p_read_multi, + tBTA_GATT_AUTH_REQ auth_req); + +/******************************************************************************* +** +** Function BTA_GATTC_ReadMultiple +** +** Description This function is called to read multiple variable length characteristic or +** characteristic descriptors. +** +** Parameters conn_id - connection ID. +** p_read_multi - read multiple parameters. +** +** Returns None +** +*******************************************************************************/ +extern void BTA_GATTC_ReadMultipleVariable(UINT16 conn_id, tBTA_GATTC_MULTI *p_read_multi, + tBTA_GATT_AUTH_REQ auth_req); + +/******************************************************************************* +** +** Function BTA_GATTC_Refresh +** +** Description Refresh the server cache of the remote device +** +** Parameters remote_bda: remote device BD address. +** erase_flash: delete cache from nvs flash +** +** Returns void +** +*******************************************************************************/ +extern void BTA_GATTC_Refresh(BD_ADDR remote_bda, bool erase_flash); + +extern void BTA_GATTC_CacheAssoc(tBTA_GATTC_IF client_if, BD_ADDR src_addr, BD_ADDR assoc_addr, BOOLEAN is_assoc); + +extern void BTA_GATTC_CacheGetAddrList(tBTA_GATTC_IF client_if); + +/******************************************************************************* +** +** Function BTA_GATTC_Clean +** +** Description Clean the server cache of the remote device +** +** Parameters remote_bda: remote device BD address. +** +** Returns void +** +*******************************************************************************/ +extern void BTA_GATTC_Clean(BD_ADDR remote_bda); + +/******************************************************************************* +** +** Function BTA_GATTC_Listen +** +** Description Start advertisement to listen for connection request. +** +** Parameters client_if: server interface. +** start: to start or stop listening for connection +** remote_bda: remote device BD address, if listen to all device +** use NULL. +** +** Returns void +** +*******************************************************************************/ +extern void BTA_GATTC_Listen(tBTA_GATTC_IF client_if, BOOLEAN start, BD_ADDR_PTR target_bda); + +/******************************************************************************* +** +** Function BTA_GATTC_Broadcast +** +** Description Start broadcasting (non-connectable advertisements) +** +** Parameters client_if: client interface. +** start: to start or stop listening for connection +** +** Returns void +** +*******************************************************************************/ +extern void BTA_GATTC_Broadcast(tBTA_GATTC_IF client_if, BOOLEAN start); + + +/******************************************************************************* +** +** Function BTA_GATTC_ConfigureMTU +** +** Description Configure the MTU size in the GATT channel. This can be done +** only once per connection. +** +** Parameters conn_id: connection ID. +** +** +** Returns void +** +*******************************************************************************/ +extern void BTA_GATTC_ConfigureMTU (UINT16 conn_id); + +/******************************************************************************* +** BTA GATT Server API +********************************************************************************/ + +/******************************************************************************* +** +** Function BTA_GATTS_Init +** +** Description This function is called to initalize GATTS module +** +** Parameters None +** +** Returns None +** +*******************************************************************************/ +extern void BTA_GATTS_Init(void); + +/******************************************************************************* +** +** Function BTA_GATTS_Disable +** +** Description This function is called to disable GATTS module +** +** Parameters None. +** +** Returns None +** +*******************************************************************************/ +extern void BTA_GATTS_Disable(void); + +/******************************************************************************* +** +** Function BTA_GATTS_AppRegister +** +** Description This function is called to register application callbacks +** with BTA GATTS module. +** +** Parameters p_app_uuid - application UUID +** p_cback - pointer to the application callback function. +** +** Returns None +** +*******************************************************************************/ +extern void BTA_GATTS_AppRegister(const tBT_UUID * p_app_uuid, tBTA_GATTS_CBACK *p_cback); + + +/******************************************************************************* +** +** Function BTA_GATTS_AppDeregister +** +** Description De-register with BTA GATT Server. +** +** Parameters server_if: server interface +** +** Returns void +** +*******************************************************************************/ +extern void BTA_GATTS_AppDeregister(tBTA_GATTS_IF server_if); + +/******************************************************************************* +** +** Function BTA_GATTS_CreateService +** +** Description Create a service. When service creation is done, a callback +** event BTA_GATTS_CREATE_EVT is called to report status +** and service ID to the profile. The service ID obtained in +** the callback function needs to be used when adding included +** service and characteristics/descriptors into the service. +** +** Parameters server_if: server interface. +** p_service_uuid: service UUID. +** inst: instance ID number of this service. +** num_handle: numble of handle requessted for this service. +** is_primary: is this service a primary one or not. +** +** Returns void +** +*******************************************************************************/ +extern void BTA_GATTS_CreateService(tBTA_GATTS_IF server_if, const tBT_UUID * p_service_uuid, + UINT8 inst, UINT16 num_handle, BOOLEAN is_primary); + +/******************************************************************************* +** +** Function BTA_GATTS_AddIncludeService +** +** Description This function is called to add an included service. After included +** service is included, a callback event BTA_GATTS_ADD_INCL_SRVC_EVT +** is reported the included service ID. +** +** Parameters service_id: service ID to which this included service is to +** be added. +** included_service_id: the service ID to be included. +** +** Returns void +** +*******************************************************************************/ +extern void BTA_GATTS_AddIncludeService(UINT16 service_id, UINT16 included_service_id); + +/******************************************************************************* +** +** Function BTA_GATTS_AddCharacteristic +** +** Description This function is called to add a characteristic into a service. +** +** Parameters service_id: service ID to which this included service is to +** be added. +** p_char_uuid : Characteristic UUID. +** perm : Characteristic value declaration attribute permission. +** property : Characteristic Properties +** +** Returns None +** +*******************************************************************************/ +extern void BTA_GATTS_AddCharacteristic (UINT16 service_id, const tBT_UUID * p_char_uuid, + tBTA_GATT_PERM perm, tBTA_GATT_CHAR_PROP property, tGATT_ATTR_VAL *attr_val, + tBTA_GATTS_ATTR_CONTROL *control); + +/******************************************************************************* +** +** Function BTA_GATTS_AddCharDescriptor +** +** Description This function is called to add characteristic descriptor. When +** it's done, a callback event BTA_GATTS_ADD_DESCR_EVT is called +** to report the status and an ID number for this descriptor. +** +** Parameters service_id: service ID to which this charatceristic descriptor is to +** be added. +** perm: descriptor access permission. +** p_descr_uuid: descriptor UUID. +** p_descr_params: descriptor value if it's read only descriptor. +** +** Returns returns status. +** +*******************************************************************************/ +extern void BTA_GATTS_AddCharDescriptor (UINT16 service_id, + tBTA_GATT_PERM perm, + const tBT_UUID * p_descr_uuid, tBTA_GATT_ATTR_VAL *attr_val, + tBTA_GATTS_ATTR_CONTROL *control); + +/******************************************************************************* +** +** Function BTA_GATTS_DeleteService +** +** Description This function is called to delete a service. When this is done, +** a callback event BTA_GATTS_DELETE_EVT is report with the status. +** +** Parameters service_id: service_id to be deleted. +** +** Returns returns none. +** +*******************************************************************************/ +extern void BTA_GATTS_DeleteService(UINT16 service_id); + +/******************************************************************************* +** +** Function BTA_GATTS_StartService +** +** Description This function is called to start a service. +** +** Parameters service_id: the service ID to be started. +** sup_transport: supported transport. +** +** Returns None. +** +*******************************************************************************/ +extern void BTA_GATTS_StartService(UINT16 service_id, tBTA_GATT_TRANSPORT sup_transport); + +/******************************************************************************* +** +** Function BTA_GATTS_StopService +** +** Description This function is called to stop a service. +** +** Parameters service_id - service to be topped. +** +** Returns None +** +*******************************************************************************/ +extern void BTA_GATTS_StopService(UINT16 service_id); + +/******************************************************************************* +** +** Function BTA_GATTS_HandleValueIndication +** +** Description This function is called to read a characteristics descriptor. +** +** Parameters conn_id - connection identifier. +** attr_id - attribute ID to indicate. +** data_len - indicate data length. +** p_data: data to indicate. +** need_confirm - if this indication expects a confirmation or not. +** +** Returns None +** +*******************************************************************************/ +extern void BTA_GATTS_HandleValueIndication (UINT16 conn_id, UINT16 attr_id, + UINT16 data_len, + UINT8 *p_data, + BOOLEAN need_confirm); + +/******************************************************************************* +** +** Function BTA_GATTS_SendRsp +** +** Description This function is called to send a response to a request. +** +** Parameters conn_id - connection identifier. +** trans_id - transaction ID. +** status - response status +** p_msg - response data. +** +** Returns None +** +*******************************************************************************/ +extern void BTA_GATTS_SendRsp (UINT16 conn_id, UINT32 trans_id, + tBTA_GATT_STATUS status, tBTA_GATTS_RSP *p_msg); + + + +/******************************************************************************* +** +** Function BTA_SetAttributeValue +** +** Description This function is called to set the attribute value in the gatt database +** +** Parameters attr_handle - the attribute value handle. +** length - the value length which has been set to the attribute. +** value - the pointer to the value +** +** Returns None +** +*******************************************************************************/ +extern void BTA_SetAttributeValue(UINT16 attr_handle, UINT16 length, UINT8 *value); + + +/******************************************************************************* +** +** Function BTA_GetAttributeValue +** +** Description This function is called to get the attribute value in the gatt database +** +** Parameters attr_handle - the attribute value handle. +** length - the value length which has been set to the attribute. +** value - the pointer to the value +** +** Returns tBTA_GATT_STATUS +** +*******************************************************************************/ +extern tBTA_GATT_STATUS BTA_GetAttributeValue(UINT16 attr_handle, UINT16 *length, UINT8 **value); + +/******************************************************************************* +** +** Function BTA_GATTS_Open +** +** Description Open a direct open connection or add a background auto connection +** bd address +** +** Parameters server_if: server interface. +** remote_bda: remote device BD address. +** is_direct: direct connection or background auto connection +** +** Returns void +** +*******************************************************************************/ +extern void BTA_GATTS_Open(tBTA_GATTS_IF server_if, BD_ADDR remote_bda, + BOOLEAN is_direct, tBTA_GATT_TRANSPORT transport); + + +/******************************************************************************* +** +** Function BTA_GATTS_CancelOpen +** +** Description Cancel a direct open connection or remove a background auto connection +** bd address +** +** Parameters server_if: server interface. +** remote_bda: remote device BD address. +** is_direct: direct connection or background auto connection +** +** Returns void +** +*******************************************************************************/ +extern void BTA_GATTS_CancelOpen(tBTA_GATTS_IF server_if, BD_ADDR remote_bda, BOOLEAN is_direct); + + +/******************************************************************************* +** +** Function BTA_GATTS_Close +** +** Description Close a connection a remote device. +** +** Parameters conn_id: connection ID to be closed. +** +** Returns void +** +*******************************************************************************/ +extern void BTA_GATTS_Close(UINT16 conn_id); + +/******************************************************************************* +** +** Function BTA_GATTS_SendServiceChangeIndication +** +** Description send a service change indication. +** +** Returns void +** +*******************************************************************************/ + +void BTA_GATTS_SendServiceChangeIndication(tBTA_GATTS_IF server_if, BD_ADDR remote_bda); + +/******************************************************************************* +** +** Function BTA_GATTS_Listen +** +** Description Start advertisement to listen for connection request for a +** GATT server +** +** Parameters server_if: server interface. +** start: to start or stop listening for connection +** remote_bda: remote device BD address, if listen to all device +** use NULL. +** +** Returns void +** +*******************************************************************************/ +extern void BTA_GATTS_Listen(tBTA_GATTS_IF server_if, BOOLEAN start, + BD_ADDR_PTR target_bda); + +/******************************************************************************* +** +** Function BTA_GATTS_ShowLocalDatabase +** +** Description print local service database. +** +** Returns void +** +*******************************************************************************/ +extern void BTA_GATTS_ShowLocalDatabase(void); + +extern void bta_gattc_clcb_dealloc_by_conn_id(UINT16 conn_id); + +#ifdef __cplusplus + +} +#endif + + +#endif /* BTA_GATT_API_H */ diff --git a/lib/bt/host/bluedroid/bta/include/bta/bta_gatt_common.h b/lib/bt/host/bluedroid/bta/include/bta/bta_gatt_common.h new file mode 100644 index 00000000..2d696729 --- /dev/null +++ b/lib/bt/host/bluedroid/bta/include/bta/bta_gatt_common.h @@ -0,0 +1,28 @@ +/* + * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/****************************************************************************** + * + * This file contains the action functions for gatts and gattc. + * + * + ******************************************************************************/ + +#include "stack/bt_types.h" + + +#ifdef __cplusplus +extern "C" +{ +#endif + +extern void BTA_GATT_SetLocalMTU(uint16_t mtu); + +extern uint16_t BTA_GATT_GetLocalMTU(void); + +#ifdef __cplusplus +} +#endif diff --git a/lib/bt/host/bluedroid/bta/include/bta/bta_gattc_ci.h b/lib/bt/host/bluedroid/bta/include/bta/bta_gattc_ci.h new file mode 100644 index 00000000..d523e87b --- /dev/null +++ b/lib/bt/host/bluedroid/bta/include/bta/bta_gattc_ci.h @@ -0,0 +1,117 @@ +/****************************************************************************** + * + * Copyright (C) 2003-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This is the interface file for GATT call-in functions. + * + ******************************************************************************/ +#ifndef BTA_GATTC_CI_H +#define BTA_GATTC_CI_H + +#include "bta/bta_gatt_api.h" + +/***************************************************************************** +** Constants and data types +*****************************************************************************/ + +/* Open Complete Event */ +typedef struct { + BT_HDR hdr; + tBTA_GATT_STATUS status; +} tBTA_GATTC_CI_EVT; + +#define BTA_GATTC_NV_LOAD_MAX 100 + +/* Read Ready Event */ +typedef struct { + BT_HDR hdr; + tBTA_GATT_STATUS status; + UINT16 num_attr; + tBTA_GATTC_NV_ATTR attr[BTA_GATTC_NV_LOAD_MAX]; +} tBTA_GATTC_CI_LOAD; + + +/***************************************************************************** +** Function Declarations +*****************************************************************************/ +#ifdef __cplusplus +extern "C" +{ +#endif + +/******************************************************************************* +** +** Function bta_gattc_ci_cache_open +** +** Description This function sends an event to indicate server cache open +** completed. +** +** Parameters server_bda - server BDA of this cache. +** status - BTA_GATT_OK if full buffer of data, +** BTA_GATT_FAIL if an error has occurred. +** +** Returns void +** +*******************************************************************************/ +extern void bta_gattc_ci_cache_open(BD_ADDR server_bda, UINT16 evt, + tBTA_GATT_STATUS status, UINT16 conn_id); + +/******************************************************************************* +** +** Function bta_gattc_ci_cache_load +** +** Description This function sends an event to BTA indicating the phone has +** load the servere cache and ready to send it to the stack. +** +** Parameters server_bda - server BDA of this cache. +** num_bytes_read - number of bytes read into the buffer +** specified in the read callout-function. +** status - BTA_GATT_OK if full buffer of data, +** BTA_GATT_FAIL if an error has occurred. +** +** Returns void +** +*******************************************************************************/ +extern void bta_gattc_ci_cache_load(BD_ADDR server_bda, UINT16 evt, + UINT16 num_attr, tBTA_GATTC_NV_ATTR *p_atrr, + tBTA_GATT_STATUS status, UINT16 conn_id); + +/******************************************************************************* +** +** Function bta_gattc_ci_save +** +** Description This function sends an event to BTA indicating the phone has +** save the server cache. +** +** Parameters server_bda - server BDA of this cache. +** status - BTA_GATT_OK if full buffer of data, +** BTA_GATT_FAIL if an error has occurred. +** +** Returns void +** +*******************************************************************************/ +extern void bta_gattc_ci_cache_save(BD_ADDR server_bda, UINT16 evt, + tBTA_GATT_STATUS status, UINT16 conn_id); + + +#ifdef __cplusplus +} +#endif + +#endif /* BTA_GATTC_CI_H */ diff --git a/lib/bt/host/bluedroid/bta/include/bta/bta_gattc_co.h b/lib/bt/host/bluedroid/bta/include/bta/bta_gattc_co.h new file mode 100644 index 00000000..44a0e18e --- /dev/null +++ b/lib/bt/host/bluedroid/bta/include/bta/bta_gattc_co.h @@ -0,0 +1,140 @@ +/****************************************************************************** + * + * Copyright (C) 2009-2013 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This is the interface file for BTA GATT client call-out functions. + * + ******************************************************************************/ +#ifndef BTA_GATTC_CO_H +#define BTA_GATTC_CO_H + +#include "bta/bta_gatt_api.h" +#include "osi/hash_functions.h" + +/******************************************************************************* +** +** Function bta_gattc_co_cache_open +** +** Description This callout function is executed by GATTC when a GATT server +** cache is ready to be sent. +** +** Parameter server_bda: server bd address of this cache belongs to +** evt: call in event to be passed in when cache open is done. +** conn_id: connection ID of this cache operation attach to. +** to_save: open cache to save or to load. +** +** Returns void. +** +*******************************************************************************/ +extern tBTA_GATT_STATUS bta_gattc_co_cache_open(BD_ADDR server_bda, BOOLEAN to_save, UINT8 *index); + +/******************************************************************************* +** +** Function bta_gattc_co_cache_close +** +** Description This callout function is executed by GATTC when a GATT server +** cache is written completely. +** +** Parameter server_bda: server bd address of this cache belongs to +** conn_id: connection ID of this cache operation attach to. +** +** Returns void. +** +*******************************************************************************/ +extern void bta_gattc_co_cache_close(BD_ADDR server_bda, UINT16 conn_id); + +/******************************************************************************* +** +** Function bta_gattc_co_cache_save +** +** Description This callout function is executed by GATT when a server cache +** is available to save. +** +** Parameter server_bda: server bd address of this cache belongs to +** evt: call in event to be passed in when cache save is done. +** num_attr: number of attribute to be save. +** p_attr: pointer to the list of attributes to save. +** attr_index: starting attribute index of the save operation. +** conn_id: connection ID of this cache operation attach to. +** Returns +** +*******************************************************************************/ +extern void bta_gattc_co_cache_save (BD_ADDR server_bda, UINT16 num_attr, + tBTA_GATTC_NV_ATTR *p_attr_list); + +/******************************************************************************* +** +** Function bta_gattc_co_cache_load +** +** Description This callout function is executed by GATT when server cache +** is required to load. +** +** Parameter server_bda: server bd address of this cache belongs to +** evt: call in event to be passed in when cache save is done. +** num_attr: number of attribute to be save. +** attr_index: starting attribute index of the save operation. +** conn_id: connection ID of this cache operation attach to. +** Returns +** +*******************************************************************************/ +extern tBTA_GATT_STATUS bta_gattc_co_cache_load(tBTA_GATTC_NV_ATTR *attr, UINT8 index); + +/******************************************************************************* +** +** Function bta_gattc_co_cache_reset +** +** Description This callout function is executed by GATTC to reset cache in +** application +** +** Parameter server_bda: server bd address of this cache belongs to +** +** Returns void. +** +*******************************************************************************/ +extern void bta_gattc_co_cache_reset(BD_ADDR server_bda); + +extern size_t bta_gattc_get_cache_attr_length(UINT8 index); + +extern void bta_gattc_co_cache_addr_init(void); + +extern void bta_gattc_co_cache_addr_deinit(void); + +extern BOOLEAN bta_gattc_co_addr_in_cache(BD_ADDR bda); + +extern uint8_t bta_gattc_co_find_addr_in_cache(BD_ADDR bda); + +extern uint8_t bta_gattc_co_find_hash_in_cache(hash_key_t hash_key); + +extern UINT8 bta_gattc_co_get_addr_num(void); + +extern void bta_gattc_co_get_addr_list(BD_ADDR *addr_list); + +extern void bta_gattc_co_cache_addr_save(BD_ADDR bd_addr, hash_key_t hash_key); + +extern BOOLEAN bta_gattc_co_cache_new_assoc_list(BD_ADDR src_addr, uint8_t index); + +extern BOOLEAN bta_gattc_co_cache_append_assoc_addr(BD_ADDR src_addr, BD_ADDR assoc_addr); + +extern BOOLEAN bta_gattc_co_cache_remove_assoc_addr(BD_ADDR src_addr, BD_ADDR assoc_addr); + +uint8_t* bta_gattc_co_cache_find_src_addr(BD_ADDR assoc_addr, uint8_t *index); + +extern BOOLEAN bta_gattc_co_cache_clear_assoc_addr(BD_ADDR src_addr); + +#endif /* BTA_GATT_CO_H */ diff --git a/lib/bt/host/bluedroid/bta/include/bta/bta_gatts_co.h b/lib/bt/host/bluedroid/bta/include/bta/bta_gatts_co.h new file mode 100644 index 00000000..66178c76 --- /dev/null +++ b/lib/bt/host/bluedroid/bta/include/bta/bta_gatts_co.h @@ -0,0 +1,88 @@ +/****************************************************************************** + * + * Copyright (C) 2010-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This is the interface file for BTA GATT server call-out functions. + * + ******************************************************************************/ +#ifndef BTA_GATTS_CO_H +#define BTA_GATTS_CO_H + +#include "bta/bta_gatt_api.h" + +/******************************************************************************* +** +** Function bta_gatts_co_update_handle_range +** +** Description This callout function is executed by GATTS when a GATT server +** handle range ios to be added or removed. +** +** Parameter is_add: true is to add a handle range; otherwise is to delete. +** p_hndl_range: handle range. +** +** Returns void. +** +*******************************************************************************/ +extern void bta_gatts_co_update_handle_range(BOOLEAN is_add, tBTA_GATTS_HNDL_RANGE *p_hndl_range); + +/******************************************************************************* +** +** Function bta_gatts_co_srv_chg +** +** Description This call-out is to read/write/remove service change related +** informaiton. The request consists of the cmd and p_req and the +** response is returned in p_rsp +** +** Parameter cmd - request command +** p_req - request paramters +** p_rsp - response data for the request +** +** Returns TRUE - if the request is processed successfully and +** the response is returned in p_rsp. +** FALSE - if the request can not be processed +** +*******************************************************************************/ +extern BOOLEAN bta_gatts_co_srv_chg(tBTA_GATTS_SRV_CHG_CMD cmd, + tBTA_GATTS_SRV_CHG_REQ *p_req, + tBTA_GATTS_SRV_CHG_RSP *p_rsp); + +/******************************************************************************* +** +** Function bta_gatts_co_load_handle_range +** +** Description This callout function is executed by GATTS when a GATT server +** handle range is requested to be loaded from NV. +** +** Parameter +** +** Returns void. +** +*******************************************************************************/ +extern BOOLEAN bta_gatts_co_load_handle_range(UINT8 index, + tBTA_GATTS_HNDL_RANGE *p_handle); + +extern void bta_gatts_co_cl_feat_save(BD_ADDR remote_addr, UINT8 *feature); + +extern void bta_gatts_co_db_hash_save(BD_ADDR remote_addr, BT_OCTET16 db_hash); + +extern void bta_gatts_co_cl_feat_load(BD_ADDR remote_addr, UINT8 *feature); + +extern void bta_gatts_co_db_hash_load(BD_ADDR remote_addr, BT_OCTET16 db_hash); + +#endif /* BTA_GATTS_CO_H */ diff --git a/lib/bt/host/bluedroid/bta/include/bta/bta_hd_api.h b/lib/bt/host/bluedroid/bta/include/bta/bta_hd_api.h new file mode 100644 index 00000000..1c7e708e --- /dev/null +++ b/lib/bt/host/bluedroid/bta/include/bta/bta_hd_api.h @@ -0,0 +1,295 @@ +/****************************************************************************** + * + * Copyright (C) 2016 The Android Open Source Project + * Copyright (C) 2002-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ +#ifndef BTA_HD_API_H +#define BTA_HD_API_H + +#include "bta_api.h" +#include "stack/hidd_api.h" + +#if BTA_HD_INCLUDED == TRUE + +/***************************************************************************** + * Constants and Type Definitions + ****************************************************************************/ + +#ifndef BTA_HD_DEBUG +#define BTA_HD_DEBUG TRUE +#endif + +/* BTA HID Device callback events */ +#define BTA_HD_ENABLE_EVT 0 /* BT-HD enabled */ +#define BTA_HD_DISABLE_EVT 1 /* BT-HD disabled */ +#define BTA_HD_REGISTER_APP_EVT 2 /* application registered */ +#define BTA_HD_UNREGISTER_APP_EVT 3 /* application unregistered */ +#define BTA_HD_OPEN_EVT 4 /* connection to host opened */ +#define BTA_HD_CLOSE_EVT 5 /* connection to host closed */ +#define BTA_HD_GET_REPORT_EVT 6 /* GET_REPORT request from host */ +#define BTA_HD_SET_REPORT_EVT 7 /* SET_REPORT request from host */ +#define BTA_HD_SET_PROTOCOL_EVT 8 /* SET_PROTOCOL request from host */ +#define BTA_HD_INTR_DATA_EVT 9 /* DATA received from host on intr */ +#define BTA_HD_VC_UNPLUG_EVT 10 /* Virtual Cable Unplug */ +// #define BTA_HD_CONN_STATE_EVT 11 /* Report connection state change */ +#define BTA_HD_SEND_REPORT_EVT 12 /* Send report finish */ +#define BTA_HD_REPORT_ERR_EVT 13 /* Report Handshake finish */ +#define BTA_HD_API_ERR_EVT 99 /* BT-HD API error */ + +typedef uint16_t tBTA_HD_EVT; + +enum { BTA_HD_OK, BTA_HD_ERROR }; + +typedef enum { + BTA_HD_CONN_STATE_CONNECTED, + BTA_HD_CONN_STATE_CONNECTING, + BTA_HD_CONN_STATE_DISCONNECTED, + BTA_HD_CONN_STATE_DISCONNECTING, + BTA_HD_CONN_STATE_UNKNOWN +} tBTA_HD_CONN_STAT; + +typedef uint8_t tBTA_HD_STATUS; +typedef tHID_DEV_DSCP_INFO tBTA_HD_DEV_DESCR; + +typedef struct { + char *p_name; + char *p_description; + char *p_provider; + uint8_t subclass; + tBTA_HD_DEV_DESCR descriptor; +} tBTA_HD_APP_INFO; + +typedef struct { + uint8_t service_type; + uint32_t token_rate; + uint32_t token_bucket_size; + uint32_t peak_bandwidth; + uint32_t access_latency; + uint32_t delay_variation; +} tBTA_HD_QOS_INFO; + +typedef struct { + bool use_intr; + uint8_t type; + uint8_t id; + uint16_t len; + uint8_t *p_data; +} tBTA_HD_REPORT; + +typedef struct { + tBTA_HD_STATUS status; + bool in_use; + BD_ADDR bda; +} tBTA_HD_REG_STATUS; + +typedef struct { + BD_ADDR bda; + tBTA_HD_STATUS status; + tBTA_HD_CONN_STAT conn_status; +} tBTA_HD_CONN; + +typedef struct { + uint8_t report_type; + uint8_t report_id; + uint16_t buffer_size; +} tBTA_HD_GET_REPORT; + +typedef struct { + uint8_t report_type; + uint8_t report_id; + uint16_t len; + uint8_t *p_data; +} tBTA_HD_SET_REPORT; + +typedef uint8_t tBTA_HD_SET_PROTOCOL; + +typedef struct { + uint8_t report_id; + uint16_t len; + uint8_t *p_data; +} tBTA_HD_INTR_DATA; + +typedef struct { + tBTA_HD_STATUS status; + uint8_t reason; + uint8_t report_type; + uint8_t report_id; +} tBTA_HD_API_SEND_REPORT; + +typedef struct { + tBTA_HD_STATUS status; + uint8_t reason; +} tBTA_HD_API_REPORT_ERR; + +/* union of data associated with HD callback */ +typedef union { + tBTA_HD_STATUS status; /* BTA_HD_ENABLE_EVT + BTA_HD_DISABLE_EVT + BTA_HD_UNREGISTER_APP_EVT */ + tBTA_HD_REG_STATUS reg_status; /* BTA_HD_REGISTER_APP_EVT */ + tBTA_HD_CONN conn; /* BTA_HD_OPEN_EVT + BTA_HD_CLOSE_EVT + BTA_HD_VC_UNPLUG_EVT + BTA_HD_OWN_VC_UNPLUG_EVT */ + tBTA_HD_GET_REPORT get_report; /* BTA_HD_GET_REPORT */ + tBTA_HD_SET_REPORT set_report; /* BTA_HD_SET_REPORT */ + tBTA_HD_SET_PROTOCOL set_protocol; /* BTA_HD_SETPROTOCOL */ + tBTA_HD_INTR_DATA intr_data; /* BTA_HD_INTR_DATA_EVT */ + tBTA_HD_API_SEND_REPORT send_report; /* BTA_HD_API_SEND_REPORT_EVT */ + tBTA_HD_API_REPORT_ERR report_err; /* BTA_HD_API_REPORT_ERR_EVT */ +} tBTA_HD; + +/* BTA HD callback function */ +typedef void (tBTA_HD_CBACK)(tBTA_HD_EVT event, tBTA_HD *p_data); + +/***************************************************************************** + * External Function Declarations + ****************************************************************************/ +#ifdef __cplusplus +extern "C" { +#endif + +/******************************************************************************* + * + * Function BTA_HhRegister + * + * Description This function enable HID host and registers HID-Host with + * lower layers. + * + * Returns void + * + ******************************************************************************/ +extern void BTA_HdEnable(tBTA_HD_CBACK *p_cback); + +/******************************************************************************* + * + * Function BTA_HhDeregister + * + * Description This function is called when the host is about power down. + * + * Returns void + * + ******************************************************************************/ +extern void BTA_HdDisable(void); + +/******************************************************************************* + * + * Function BTA_HdRegisterApp + * + * Description This function is called when application should be + * registered + * + * Returns void + * + ******************************************************************************/ +extern void BTA_HdRegisterApp(tBTA_HD_APP_INFO *p_app_info, tBTA_HD_QOS_INFO *p_in_qos, tBTA_HD_QOS_INFO *p_out_qos); + +/******************************************************************************* + * + * Function BTA_HdUnregisterApp + * + * Description This function is called when application should be + * unregistered + * + * Returns void + * + ******************************************************************************/ +extern void BTA_HdUnregisterApp(void); + +/******************************************************************************* + * + * Function BTA_HdSendReport + * + * Description This function is called when report is to be sent + * + * Returns void + * + ******************************************************************************/ +extern void BTA_HdSendReport(tBTA_HD_REPORT *p_report); + +/******************************************************************************* + * + * Function BTA_HdVirtualCableUnplug + * + * Description This function is called when VCU shall be sent + * + * Returns void + * + ******************************************************************************/ +extern void BTA_HdVirtualCableUnplug(void); + +/******************************************************************************* + * + * Function BTA_HdConnect + * + * Description This function is called when connection to host shall be + * made. + * + * Returns void + * + ******************************************************************************/ +extern void BTA_HdConnect(BD_ADDR addr); + +/******************************************************************************* + * + * Function BTA_HdDisconnect + * + * Description This function is called when host shall be disconnected + * + * Returns void + * + ******************************************************************************/ +extern void BTA_HdDisconnect(void); + +/******************************************************************************* + * + * Function BTA_HdAddDevice + * + * Description This function is called when a device is virtually cabled + * + * Returns void + * + ******************************************************************************/ +extern void BTA_HdAddDevice(BD_ADDR addr); + +/******************************************************************************* + * + * Function BTA_HdRemoveDevice + * + * Description This function is called when a device is virtually uncabled + * + * Returns void + * + ******************************************************************************/ +extern void BTA_HdRemoveDevice(BD_ADDR addr); + +/******************************************************************************* + * + * Function BTA_HdReportError + * + * Description This function is called when reporting error for set report + * + * Returns void + * + ******************************************************************************/ +extern void BTA_HdReportError(uint8_t error); + +#ifdef __cplusplus +} +#endif + +#endif /* BTA_HD_INCLUDED */ +#endif /* BTA_HD_API_H */ diff --git a/lib/bt/host/bluedroid/bta/include/bta/bta_hf_client_api.h b/lib/bt/host/bluedroid/bta/include/bta/bta_hf_client_api.h new file mode 100644 index 00000000..ac834c7b --- /dev/null +++ b/lib/bt/host/bluedroid/bta/include/bta/bta_hf_client_api.h @@ -0,0 +1,418 @@ +/****************************************************************************** + * + * Copyright (c) 2014 The Android Open Source Project + * Copyright (C) 2003-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This is the public interface file for the handsfree (HF role) subsystem + * + ******************************************************************************/ +#ifndef BTA_HF_CLIENT_API_H +#define BTA_HF_CLIENT_API_H + +#include "bta_api.h" +#include "bta_hfp_defs.h" + +#if (BTA_HF_INCLUDED == TRUE) +/***************************************************************************** +** Constants and data types +*****************************************************************************/ + +/* Hands-Free unit(HF) version */ +#define HFP_HF_VERSION_1_6 0x0106 /* v1.6 */ +#define HFP_HF_VERSION_1_7 0x0107 /* v1.7 */ + +/* HFP peer (AG) features*/ +#define BTA_HF_CLIENT_PEER_FEAT_3WAY 0x00000001 /* Three-way calling */ +#define BTA_HF_CLIENT_PEER_FEAT_ECNR 0x00000002 /* Echo cancellation and/or noise reduction */ +#define BTA_HF_CLIENT_PEER_FEAT_VREC 0x00000004 /* Voice recognition */ +#define BTA_HF_CLIENT_PEER_INBAND 0x00000008 /* In-band ring tone */ +#define BTA_HF_CLIENT_PEER_VTAG 0x00000010 /* Attach a phone number to a voice tag */ +#define BTA_HF_CLIENT_PEER_REJECT 0x00000020 /* Ability to reject incoming call */ +#define BTA_HF_CLIENT_PEER_ECS 0x00000040 /* Enhanced Call Status */ +#define BTA_HF_CLIENT_PEER_ECC 0x00000080 /* Enhanced Call Control */ +#define BTA_HF_CLIENT_PEER_EXTERR 0x00000100 /* Extended error codes */ +#define BTA_HF_CLIENT_PEER_CODEC 0x00000200 /* Codec Negotiation */ +/* HFP 1.7+ */ +#define BTA_HF_CLIENT_PEER_HF_IND 0x00000400 /* HF Indicators */ +#define BTA_HF_CLIENT_PEER_ESCO_S4 0x00000800 /* eSCO S4 Setting Supported */ + +typedef UINT16 tBTA_HF_CLIENT_PEER_FEAT; + +/* HFP HF features */ +#define BTA_HF_CLIENT_FEAT_ECNR 0x00000001 /* Echo cancellation and/or noise reduction */ +#define BTA_HF_CLIENT_FEAT_3WAY 0x00000002 /* Call waiting and three-way calling */ +#define BTA_HF_CLIENT_FEAT_CLI 0x00000004 /* Caller ID presentation capability */ +#define BTA_HF_CLIENT_FEAT_VREC 0x00000008 /* Voice recognition activation */ +#define BTA_HF_CLIENT_FEAT_VOL 0x00000010 /* Remote volume control */ +#define BTA_HF_CLIENT_FEAT_ECS 0x00000020 /* Enhanced Call Status */ +#define BTA_HF_CLIENT_FEAT_ECC 0x00000040 /* Enhanced Call Control */ +#define BTA_HF_CLIENT_FEAT_CODEC 0x00000080 /* Codec Negotiation */ +#define BTA_HF_CLIENT_FEAT_HF_IND 0x00000100 /* HF indicators */ +#define BTA_HF_CLIENT_FEAT_ESCO_S4 0x00000200 /* eSCO S4 Setting Supported */ + +/* HFP HF extended call handling - masks not related to any spec */ +#define BTA_HF_CLIENT_CHLD_REL 0x00000001 /* 0 Release waiting call or held calls */ +#define BTA_HF_CLIENT_CHLD_REL_ACC 0x00000002 /* 1 Release active calls and accept other (waiting or held) cal */ +#define BTA_HF_CLIENT_CHLD_REL_X 0x00000004 /* 1x Release x call*/ +#define BTA_HF_CLIENT_CHLD_HOLD_ACC 0x00000008 /* 2 Active calls on hold and accept other call */ +#define BTA_HF_CLIENT_CHLD_PRIV_X 0x00000010 /* 2x Active multiparty call on hold except call x */ +#define BTA_HF_CLIENT_CHLD_MERGE 0x00000020 /* 3 Add held call to multiparty */ +#define BTA_HF_CLIENT_CHLD_MERGE_DETACH 0x00000040 /* 4 Add held call to multiparty */ + +typedef UINT16 tBTA_HF_CLIENT_CHLD_FEAT; + +/* HFP AG errors ot OK sent to HF Unit */ +#define BTA_HF_CLIENT_AT_RESULT_OK 0 +#define BTA_HF_CLIENT_AT_RESULT_ERROR 1 +#define BTA_HF_CLIENT_AT_RESULT_NO_CARRIER 2 +#define BTA_HF_CLIENT_AT_RESULT_BUSY 3 +#define BTA_HF_CLIENT_AT_RESULT_NO_ANSWER 4 +#define BTA_HF_CLIENT_AT_RESULT_DELAY 5 +#define BTA_HF_CLIENT_AT_RESULT_BLACKLISTED 6 +#define BTA_HF_CLIENT_AT_RESULT_CME 7 + +typedef UINT8 tBTA_HF_CLIENT_AT_RESULT_TYPE; + +/* HF Client callback events */ +#define BTA_HF_CLIENT_ENABLE_EVT 0 /* HF Client enabled */ +#define BTA_HF_CLIENT_REGISTER_EVT 1 /* HF Client registered */ +#define BTA_HF_CLIENT_OPEN_EVT 2 /* HF Client connection open */ +#define BTA_HF_CLIENT_CLOSE_EVT 3 /* HF Client connection closed */ +#define BTA_HF_CLIENT_CONN_EVT 4 /* Service level connection opened */ +#define BTA_HF_CLIENT_AUDIO_OPEN_EVT 5 /* Audio connection open */ +#define BTA_HF_CLIENT_AUDIO_MSBC_OPEN_EVT 6 /* Audio connection with mSBC codec open */ +#define BTA_HF_CLIENT_AUDIO_CLOSE_EVT 7 /* Audio connection closed */ +#define BTA_HF_CLIENT_SPK_EVT 8 /* Speaker volume changed */ +#define BTA_HF_CLIENT_MIC_EVT 9 /* Microphone volume changed */ +#define BTA_HF_CLIENT_IND_EVT 10 /* Indicator */ +#define BTA_HF_CLIENT_VOICE_REC_EVT 11 /* AG changed voice recognition setting */ +#define BTA_HF_CLIENT_OPERATOR_NAME_EVT 12 /* Operator name acquired */ +#define BTA_HF_CLIENT_CLIP_EVT 13 /* Calling line identification event */ +#define BTA_HF_CLIENT_CCWA_EVT 14 /* Call waiting notification */ +#define BTA_HF_CLIENT_AT_RESULT_EVT 15 /* Call waiting notification */ +#define BTA_HF_CLIENT_CLCC_EVT 16 /* current call event */ +#define BTA_HF_CLIENT_CNUM_EVT 17 /* subscriber information event */ +#define BTA_HF_CLIENT_BTRH_EVT 18 /* bluetooth response and hold event */ +#define BTA_HF_CLIENT_BSIR_EVT 19 /* in-band ring tone setting changed event */ +#define BTA_HF_CLIENT_BINP_EVT 20 /* binp number event */ +#define BTA_HF_CLIENT_RING_INDICATION 21 /* HF Client ring indication */ +#define BTA_HF_CLIENT_DISABLE_EVT 22 /* HF Client disabled */ +#define BTA_HF_CLIENT_PKT_STAT_NUMS_GET_EVT 23 /* HF Client packet status nums */ + +typedef UINT8 tBTA_HF_CLIENT_EVT; + +/* HF Client open status */ +#define BTA_HF_CLIENT_SUCCESS 0 /* Connection successfully opened */ +#define BTA_HF_CLIENT_FAIL_SDP 1 /* Open failed due to SDP */ +#define BTA_HF_CLIENT_FAIL_RFCOMM 2 /* Open failed due to RFCOMM */ +#define BTA_HF_CLIENT_FAIL_RESOURCES 3 /* out of resources failure */ + +typedef UINT8 tBTA_HF_CLIENT_STATUS; + +/* indicator constants HFP 1.1 and later */ +#define BTA_HF_CLIENT_IND_CALL 0 /* position of call indicator */ +#define BTA_HF_CLIENT_IND_CALLSETUP 1 /* position of callsetup indicator */ +#define BTA_HF_CLIENT_IND_SERVICE 2 /* position of service indicator */ +/* indicator constants HFP 1.5 and later */ +#define BTA_HF_CLIENT_IND_SIGNAL 3 /* position of signal strength indicator */ +#define BTA_HF_CLIENT_IND_ROAM 4 /* position of roaming indicator */ +#define BTA_HF_CLIENT_IND_BATTCH 5 /* position of battery charge indicator */ +#define BTA_HF_CLIENT_IND_CALLHELD 6 /* position of callheld indicator */ +#define BTA_HF_CLIENT_IND_BEARER 7 /* position of bearer indicator */ +typedef UINT8 tBTA_HF_CLIENT_IND_TYPE; + +/* AT commands */ +#define BTA_HF_CLIENT_AT_CMD_VTS 0 +#define BTA_HF_CLIENT_AT_CMD_BTRH 1 +#define BTA_HF_CLIENT_AT_CMD_CHUP 2 +#define BTA_HF_CLIENT_AT_CMD_CHLD 3 +#define BTA_HF_CLIENT_AT_CMD_BCC 4 +#define BTA_HF_CLIENT_AT_CMD_CNUM 5 +#define BTA_HF_CLIENT_AT_CMD_ATA 6 +#define BTA_HF_CLIENT_AT_CMD_COPS 7 +#define BTA_HF_CLIENT_AT_CMD_ATD 8 +#define BTA_HF_CLIENT_AT_CMD_VGM 9 +#define BTA_HF_CLIENT_AT_CMD_VGS 10 +#define BTA_HF_CLIENT_AT_CMD_BVRA 11 +#define BTA_HF_CLIENT_AT_CMD_CLCC 12 +#define BTA_HF_CLIENT_AT_CMD_BINP 13 +#define BTA_HF_CLIENT_AT_CMD_BLDN 14 +#define BTA_HF_CLIENT_AT_CMD_NREC 15 +#define BTA_HF_CLIENT_AT_CMD_XAPL 16 +#define BTA_HF_CLIENT_AT_CMD_IPHONEACCEV 17 + +typedef UINT8 tBTA_HF_CLIENT_AT_CMD_TYPE; + +#define BTA_HF_CLIENT_MAX_LEN 32 + +/* data associated with most non-AT events */ +/* placeholder, if not needed should be removed*/ +typedef struct { + UINT16 sync_conn_handle; +} tBTA_HF_CLIENT_HDR; + +/* data associated with BTA_HF_CLIENT_REGISTER_EVT */ +typedef struct { + tBTA_HF_CLIENT_HDR hdr; + UINT16 handle; + tBTA_HF_CLIENT_STATUS status; +} tBTA_HF_CLIENT_REGISTER; + +/* data associated with BTA_HF_CLIENT_OPEN_EVT */ +typedef struct { + tBTA_HF_CLIENT_HDR hdr; + BD_ADDR bd_addr; + tBTA_HF_CLIENT_STATUS status; +} tBTA_HF_CLIENT_OPEN; + +/* data associated with BTA_HF_CLIENT_CONN_EVT */ +typedef struct { + tBTA_HF_CLIENT_HDR hdr; + tBTA_HF_CLIENT_PEER_FEAT peer_feat; + tBTA_HF_CLIENT_CHLD_FEAT chld_feat; +} tBTA_HF_CLIENT_CONN; + +/* data associated with BTA_HF_CLIENT_IND_EVT event */ +typedef struct { + tBTA_HF_CLIENT_HDR hdr; + tBTA_HF_CLIENT_IND_TYPE type; + UINT16 value; +} tBTA_HF_CLIENT_IND; + +/* data associated with BTA_HF_CLIENT_OPERATOR_NAME_EVT */ +#define BTA_HF_CLIENT_OPERATOR_NAME_LEN 16 +typedef struct { + char name[BTA_HF_CLIENT_OPERATOR_NAME_LEN + 1]; +} tBTA_HF_CLIENT_OPERATOR_NAME; + +/* data associated with BTA_HF_CLIENT_CLIP_EVT and BTA_HF_CLIENT_CCWA_EVT*/ +#define BTA_HF_CLIENT_NUMBER_LEN 32 +typedef struct { + char number[BTA_HF_CLIENT_NUMBER_LEN + 1]; +} tBTA_HF_CLIENT_NUMBER; + +/* data associated with BTA_HF_CLIENT_AT_RESULT_EVT event */ +typedef struct { + tBTA_HF_CLIENT_AT_RESULT_TYPE type; + UINT16 cme; +} tBTA_HF_CLIENT_AT_RESULT; + +/* data associated with BTA_HF_CLIENT_CLCC_EVT event */ +typedef struct { + UINT32 idx; + BOOLEAN inc; + UINT8 status; + BOOLEAN mpty; + BOOLEAN number_present; + char number[BTA_HF_CLIENT_NUMBER_LEN + 1]; +} tBTA_HF_CLIENT_CLCC; + +/* data associated with BTA_HF_CLIENT_CNUM_EVT event */ +typedef struct { + UINT16 service; + char number[BTA_HF_CLIENT_NUMBER_LEN + 1]; +} tBTA_HF_CLIENT_CNUM; + +/* data associated with other events */ +typedef struct { + UINT16 value; +} tBTA_HF_CLIENT_VAL; + +/* data associated with BTA_HF_CLIENT_PKT_STAT_NUMS_GET_EVT */ +typedef struct { + UINT32 rx_total; + UINT32 rx_correct; + UINT32 rx_err; + UINT32 rx_none; + UINT32 rx_lost; + UINT32 tx_total; + UINT32 tx_discarded; +} tBTA_SCO_PKT_STAT_NUMS; + +/* union of data associated with AG callback */ +typedef union { + tBTA_HF_CLIENT_HDR hdr; + tBTA_HF_CLIENT_REGISTER reg; + tBTA_HF_CLIENT_OPEN open; + tBTA_HF_CLIENT_CONN conn; + tBTA_HF_CLIENT_IND ind; + tBTA_HF_CLIENT_VAL val; + tBTA_HF_CLIENT_OPERATOR_NAME operator; + tBTA_HF_CLIENT_NUMBER number; + tBTA_HF_CLIENT_AT_RESULT result; + tBTA_HF_CLIENT_CLCC clcc; + tBTA_HF_CLIENT_CNUM cnum; + tBTA_SCO_PKT_STAT_NUMS pkt_num; +} tBTA_HF_CLIENT; + +typedef UINT32 tBTA_HF_CLIENT_FEAT; + +/* HF Client callback */ +typedef void (tBTA_HF_CLIENT_CBACK)(tBTA_HF_CLIENT_EVT event, void *p_data); + +#ifdef __cplusplus +extern "C" +{ +#endif + +/***************************************************************************** +** External Function Declarations +*****************************************************************************/ + +/******************************************************************************* +** +** Function BTA_HfClientEnable +** +** Description Enable the HF CLient service. When the enable +** operation is complete the callback function will be +** called with a BTA_HF_CLIENT_ENABLE_EVT. This function must +** be called before other function in the HF CLient API are +** called. +** +** Returns BTA_SUCCESS if OK, BTA_FAILURE otherwise. +** +*******************************************************************************/ +tBTA_STATUS BTA_HfClientEnable(tBTA_HF_CLIENT_CBACK *p_cback); + +/******************************************************************************* +** +** Function BTA_HfClientDisable +** +** Description Disable the HF Client service +** +** +** Returns void +** +*******************************************************************************/ +void BTA_HfClientDisable(void); + +/******************************************************************************* +** +** Function BTA_HfClientRegister +** +** Description Register an HF Client service. +** +** +** Returns void +** +*******************************************************************************/ +void BTA_HfClientRegister(tBTA_SEC sec_mask, tBTA_HF_CLIENT_FEAT features, + char *p_service_name); + +/******************************************************************************* +** +** Function BTA_HfClientDeregister +** +** Description Deregister an HF Client service. +** +** +** Returns void +** +*******************************************************************************/ +void BTA_HfClientDeregister(UINT16 handle); + +/******************************************************************************* +** +** Function BTA_HfClientOpen +** +** Description Opens a connection to an audio gateway. +** When connection is open callback function is called +** with a BTA_HF_CLIENT_OPEN_EVT. Only the data connection is +** opened. The audio connection is not opened. +** +** +** Returns void +** +*******************************************************************************/ +void BTA_HfClientOpen(UINT16 handle, BD_ADDR bd_addr, tBTA_SEC sec_mask); + +/******************************************************************************* +** +** Function BTA_HfClientClose +** +** Description Close the current connection to an audio gateway. +** Any current audio connection will also be closed +** +** +** Returns void +** +*******************************************************************************/ +void BTA_HfClientClose(UINT16 handle); + +/******************************************************************************* +** +** Function BTA_HfCllientAudioOpen +** +** Description Opens an audio connection to the currently connected +** audio gateway +** +** +** Returns void +** +*******************************************************************************/ +void BTA_HfClientAudioOpen(UINT16 handle); + +/******************************************************************************* +** +** Function BTA_HfClientAudioClose +** +** Description Close the currently active audio connection to an audio +** gateway. The data connection remains open +** +** +** Returns void +** +*******************************************************************************/ +void BTA_HfClientAudioClose(UINT16 handle); + +/******************************************************************************* +** +** Function BTA_HfClientSendAT +** +** Description send AT command +** +** +** Returns void +** +*******************************************************************************/ +void BTA_HfClientSendAT(UINT16 handle, tBTA_HF_CLIENT_AT_CMD_TYPE at, UINT32 val1, UINT32 val2, const char *str); + +#if (BTM_SCO_HCI_INCLUDED == TRUE ) +/******************************************************************************* +** +** Function BTA_HfClientPktStatsNumsGet +** +** Description Get the Number of packets status received and send +** +** +** Returns void +** +*******************************************************************************/ +void BTA_HfClientPktStatsNumsGet(UINT16 sync_conn_handle); + +void BTA_HfClientCiData(void); +#endif /*#if (BTM_SCO_HCI_INCLUDED == TRUE ) */ + +int BTA_HfClientGetCbDataSize(tBTA_HF_CLIENT_EVT event); + +#ifdef __cplusplus +} +#endif +#endif /* #if (BTA_HF_INCLUDED == TRUE) */ +#endif /* BTA_HF_CLIENT_API_H */ diff --git a/lib/bt/host/bluedroid/bta/include/bta/bta_hf_client_co.h b/lib/bt/host/bluedroid/bta/include/bta/bta_hf_client_co.h new file mode 100644 index 00000000..42299723 --- /dev/null +++ b/lib/bt/host/bluedroid/bta/include/bta/bta_hf_client_co.h @@ -0,0 +1,107 @@ +/* + * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + + +/****************************************************************************** + * + * This is the interface file for hf client call-out functions. + * + ******************************************************************************/ +#ifndef BTA_HF_CLIENT_CO_H +#define BTA_HF_CLIENT_CO_H + +#include "common/bt_target.h" +#include "bta/bta_hf_client_api.h" + +#if (BTA_HF_INCLUDED == TRUE) + +#if (BTM_SCO_HCI_INCLUDED == TRUE) +/******************************************************************************* +** +** Function bta_hf_client_co_audio_state +** +** Description This function is called by the HF CLIENT before the audio connection +** is brought up, after it comes up, and after it goes down. +** +** Parameters handle - handle of the AG instance +** state - Audio state +** codec - if WBS support is compiled in, codec to going to be used is provided +** and when in SCO_STATE_SETUP, BTM_I2SPCMConfig() must be called with +** the correct platform parameters. +** in the other states codec type should not be ignored +** +** Returns void +** +*******************************************************************************/ +void bta_hf_client_co_audio_state(UINT16 handle, UINT8 state, tBTA_HFP_PEER_CODEC codec); + + +/******************************************************************************* +** +** Function bta_hf_client_sco_co_init +** +** Description This function can be used by the phone to initialize audio +** codec or for other initialization purposes before SCO connection +** is opened. +** +** +** Returns Void. +** +*******************************************************************************/ +tBTA_HFP_SCO_ROUTE_TYPE bta_hf_client_sco_co_init(UINT32 rx_bw, UINT32 tx_bw, + tBTA_HFP_CODEC_INFO *p_codec_info, UINT8 app_id); + + +/******************************************************************************* +** +** Function bta_hf_client_sco_co_open +** +** Description This function is executed when a SCO connection is open. +** +** +** Returns void +** +*******************************************************************************/ +void bta_hf_client_sco_co_open(UINT16 handle, UINT8 air_mode, UINT8 inout_pkt_size, UINT16 event); + +/******************************************************************************* +** +** Function bta_hf_client_sco_co_close +** +** Description This function is called when a SCO connection is closed +** +** +** Returns void +** +*******************************************************************************/ +void bta_hf_client_sco_co_close(void); + +/******************************************************************************* +** +** Function bta_hf_client_sco_co_out_data +** +** Description This function is called to send SCO data over HCI. +** +** Returns number of bytes got from application +** +*******************************************************************************/ +uint32_t bta_hf_client_sco_co_out_data(UINT8 *p_buf); + +/******************************************************************************* +** +** Function bta_hf_client_sco_co_in_data +** +** Description This function is called to send incoming SCO data to application. +** +** Returns void +** +*******************************************************************************/ +extern void bta_hf_client_sco_co_in_data(BT_HDR *p_buf, tBTM_SCO_DATA_FLAG status); + +#endif /* #if (BTM_SCO_HCI_INCLUDED == TRUE) */ + +#endif /* #if (BTA_HF_INCLUDED == TRUE) */ +#endif /* BTA_HF_CLIENT_CO_H */ diff --git a/lib/bt/host/bluedroid/bta/include/bta/bta_hfp_defs.h b/lib/bt/host/bluedroid/bta/include/bta/bta_hfp_defs.h new file mode 100644 index 00000000..752b2be3 --- /dev/null +++ b/lib/bt/host/bluedroid/bta/include/bta/bta_hfp_defs.h @@ -0,0 +1,39 @@ +/* + * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __BTA_HFP_DEFS_H__ +#define __BTA_HFP_DEFS_H__ + +#include "stack/btm_api.h" + +#define BTA_HFP_CODEC_NONE BTM_SCO_CODEC_NONE +#define BTA_HFP_CODEC_CVSD BTM_SCO_CODEC_CVSD /* CVSD */ +#define BTA_HFP_CODEC_MSBC BTM_SCO_CODEC_MSBC /* mSBC */ + +typedef UINT16 tBTA_HFP_PEER_CODEC; + +#ifndef BTA_HFP_SCO_OUT_PKT_SIZE +#define BTA_HFP_SCO_OUT_PKT_SIZE BTM_SCO_DATA_SIZE_MAX +#endif + +#define BTA_HFP_SCO_CODEC_PCM 0 /* used for regular SCO */ +#define BTA_HFP_SCO_CODEC_SBC 1 /* used for WBS */ +typedef UINT8 tBTA_HFP_SCO_CODEC_TYPE; + +#define BTA_HFP_SCO_SAMP_RATE_8K 8000 +#define BTA_HFP_SCO_SAMP_RATE_16K 16000 + +/* SCO codec information */ +typedef struct { + tBTA_HFP_SCO_CODEC_TYPE codec_type; +} tBTA_HFP_CODEC_INFO; + +#define BTA_HFP_SCO_ROUTE_PCM BTM_SCO_ROUTE_PCM +#define BTA_HFP_SCO_ROUTE_HCI BTM_SCO_ROUTE_HCI + +typedef tBTM_SCO_ROUTE_TYPE tBTA_HFP_SCO_ROUTE_TYPE; + +#endif /* __BTA_HFP_DEFS_H__ */ diff --git a/lib/bt/host/bluedroid/bta/include/bta/bta_hh_api.h b/lib/bt/host/bluedroid/bta/include/bta/bta_hh_api.h new file mode 100644 index 00000000..88cd1926 --- /dev/null +++ b/lib/bt/host/bluedroid/bta/include/bta/bta_hh_api.h @@ -0,0 +1,565 @@ +/****************************************************************************** + * + * Copyright (C) 2002-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ +#ifndef BTA_HH_API_H +#define BTA_HH_API_H + +#include "bta/bta_api.h" +#include "stack/hidh_api.h" +#if defined(BTA_HH_INCLUDED) && (BTA_HH_INCLUDED == TRUE) + +#if (defined BTA_HH_LE_INCLUDED && BTA_HH_LE_INCLUDED == TRUE) +#include "stack/gatt_api.h" +#endif + +/***************************************************************************** +** Constants and Type Definitions +*****************************************************************************/ +#ifndef BTA_HH_DEBUG +#define BTA_HH_DEBUG TRUE +#endif + +#ifndef BTA_HH_SSR_MAX_LATENCY_DEF +#define BTA_HH_SSR_MAX_LATENCY_DEF 800 /* 500 ms*/ +#endif + +#ifndef BTA_HH_SSR_MIN_TOUT_DEF +#define BTA_HH_SSR_MIN_TOUT_DEF 2 +#endif + +/* BTA HID Host callback events */ +#define BTA_HH_ENABLE_EVT 0 /* HH enabled */ +#define BTA_HH_DISABLE_EVT 1 /* HH disabled */ +#define BTA_HH_OPEN_EVT 2 /* connection opened */ +#define BTA_HH_CLOSE_EVT 3 /* connection closed */ +#define BTA_HH_GET_RPT_EVT 4 /* BTA_HhGetReport callback */ +#define BTA_HH_SET_RPT_EVT 5 /* BTA_HhSetReport callback */ +#define BTA_HH_GET_PROTO_EVT 6 /* BTA_GetProtoMode callback */ +#define BTA_HH_SET_PROTO_EVT 7 /* BTA_HhSetProtoMode callback */ +#define BTA_HH_GET_IDLE_EVT 8 /* BTA_HhGetIdle comes callback */ +#define BTA_HH_SET_IDLE_EVT 9 /* BTA_HhSetIdle finish callback */ +#define BTA_HH_GET_DSCP_EVT 10 /* Get report descriptor */ +#define BTA_HH_ADD_DEV_EVT 11 /* Add Device callback */ +#define BTA_HH_RMV_DEV_EVT 12 /* remove device finished */ +#define BTA_HH_VC_UNPLUG_EVT 13 /* virtually unplugged */ +#define BTA_HH_DATA_EVT 15 +#define BTA_HH_API_ERR_EVT 16 /* API error is caught */ +#define BTA_HH_UPDATE_SCPP_EVT 17 /* update scan paramter complete */ +#define BTA_HH_DATA_IND_EVT 18 /* Data on interrupt channel */ + +typedef UINT16 tBTA_HH_EVT; + +/* application ID(none-zero) for each type of device */ +#define BTA_HH_APP_ID_MI 1 +#define BTA_HH_APP_ID_KB 2 +#define BTA_HH_APP_ID_RMC 3 +#define BTA_HH_APP_ID_3DSG 4 +#define BTA_HH_APP_ID_JOY 5 +#define BTA_HH_APP_ID_GPAD 6 +#define BTA_HH_APP_ID_LE 0xff + +/* defined the minimum offset */ +#define BTA_HH_MIN_OFFSET L2CAP_MIN_OFFSET+1 + +/* HID_HOST_MAX_DEVICES can not exceed 15 for th design of BTA HH */ +#define BTA_HH_IDX_INVALID 0xff +#define BTA_HH_MAX_KNOWN HID_HOST_MAX_DEVICES + +#if (defined BTA_HH_LE_INCLUDED && BTA_HH_LE_INCLUDED == TRUE) +/* GATT_MAX_PHY_CHANNEL can not exceed 14 for the design of BTA HH */ +#define BTA_HH_LE_MAX_KNOWN GATT_MAX_PHY_CHANNEL +#define BTA_HH_MAX_DEVICE (HID_HOST_MAX_DEVICES + GATT_MAX_PHY_CHANNEL) +#else +#define BTA_HH_MAX_DEVICE HID_HOST_MAX_DEVICES +#endif +/* invalid device handle */ +#define BTA_HH_INVALID_HANDLE 0xff + +/* type of protocol mode */ +#define BTA_HH_PROTO_RPT_MODE (0x00) +#define BTA_HH_PROTO_BOOT_MODE (0x01) +#define BTA_HH_PROTO_UNKNOWN (0xff) +typedef UINT8 tBTA_HH_PROTO_MODE; + +enum { + BTA_HH_KEYBD_RPT_ID = 1, + BTA_HH_MOUSE_RPT_ID +}; +typedef UINT8 tBTA_HH_BOOT_RPT_ID; + +/* type of devices, bit mask */ +#define BTA_HH_DEVT_UNKNOWN 0x00 +#define BTA_HH_DEVT_JOS 0x01 /* joy stick */ +#define BTA_HH_DEVT_GPD 0x02 /* game pad */ +#define BTA_HH_DEVT_RMC 0x03 /* remote control */ +#define BTA_HH_DEVT_SED 0x04 /* sensing device */ +#define BTA_HH_DEVT_DGT 0x05 /* Digitizer tablet */ +#define BTA_HH_DEVT_CDR 0x06 /* card reader */ +#define BTA_HH_DEVT_KBD 0x10 /* keyboard */ +#define BTA_HH_DEVT_MIC 0x20 /* pointing device */ +#define BTA_HH_DEVT_COM 0x30 /* Combo keyboard/pointing */ +#define BTA_HH_DEVT_OTHER 0x80 +typedef UINT8 tBTA_HH_DEVT; + +enum { + BTA_HH_OK, + BTA_HH_HS_HID_NOT_READY, /* handshake error : device not ready */ + BTA_HH_HS_INVALID_RPT_ID, /* handshake error : invalid report ID */ + BTA_HH_HS_TRANS_NOT_SPT, /* handshake error : transaction not spt */ + BTA_HH_HS_INVALID_PARAM, /* handshake error : invalid paremter */ + BTA_HH_HS_ERROR, /* handshake error : unspecified HS error */ + BTA_HH_ERR, /* general BTA HH error */ + BTA_HH_ERR_SDP, /* SDP error */ + BTA_HH_ERR_PROTO, /* SET_Protocol error, + only used in BTA_HH_OPEN_EVT callback */ + + BTA_HH_ERR_DB_FULL, /* device database full error, used in + BTA_HH_OPEN_EVT/BTA_HH_ADD_DEV_EVT */ + BTA_HH_ERR_TOD_UNSPT, /* type of device not supported */ + BTA_HH_ERR_NO_RES, /* out of system resources */ + BTA_HH_ERR_AUTH_FAILED, /* authentication fail */ + BTA_HH_ERR_HDL, /* connection handle error */ + BTA_HH_ERR_SEC, /* encryption error */ +}; +typedef UINT8 tBTA_HH_STATUS; + + +#define BTA_HH_VIRTUAL_CABLE HID_VIRTUAL_CABLE +#define BTA_HH_NORMALLY_CONNECTABLE HID_NORMALLY_CONNECTABLE +#define BTA_HH_RECONN_INIT HID_RECONN_INIT +#define BTA_HH_SDP_DISABLE HID_SDP_DISABLE +#define BTA_HH_BATTERY_POWER HID_BATTERY_POWER +#define BTA_HH_REMOTE_WAKE HID_REMOTE_WAKE +#define BTA_HH_SUP_TOUT_AVLBL HID_SUP_TOUT_AVLBL +#define BTA_HH_SEC_REQUIRED HID_SEC_REQUIRED +typedef UINT16 tBTA_HH_ATTR_MASK; + +/* supported type of device and corresponding application ID */ +typedef struct { + tBTA_HH_DEVT tod; /* type of device */ + UINT8 app_id; /* corresponding application ID */ +} tBTA_HH_SPT_TOD; + +/* configuration struct */ +typedef struct { + UINT8 max_devt_spt; /* max number of types of devices spt */ + tBTA_HH_SPT_TOD *p_devt_list; /* supported types of device list */ + UINT16 sdp_db_size; +} tBTA_HH_CFG; + +enum { + BTA_HH_RPTT_RESRV, /* reserved */ + BTA_HH_RPTT_INPUT, /* input report */ + BTA_HH_RPTT_OUTPUT, /* output report */ + BTA_HH_RPTT_FEATURE /* feature report */ +}; +typedef UINT8 tBTA_HH_RPT_TYPE; + +/* HID_CONTROL operation code used in BTA_HhSendCtrl() +*/ +enum { + BTA_HH_CTRL_NOP = 0 + HID_PAR_CONTROL_NOP ,/* mapping from BTE */ + BTA_HH_CTRL_HARD_RESET, /* hard reset */ + BTA_HH_CTRL_SOFT_RESET, /* soft reset */ + BTA_HH_CTRL_SUSPEND, /* enter suspend */ + BTA_HH_CTRL_EXIT_SUSPEND, /* exit suspend */ + BTA_HH_CTRL_VIRTUAL_CABLE_UNPLUG /* virtual unplug */ +}; +typedef UINT8 tBTA_HH_TRANS_CTRL_TYPE; + +typedef tHID_DEV_DSCP_INFO tBTA_HH_DEV_DESCR; + +#define BTA_HH_SSR_PARAM_INVALID HID_SSR_PARAM_INVALID + +/* id DI is not existing in remote device, vendor_id in tBTA_HH_DEV_DSCP_INFO will be set to 0xffff */ +#define BTA_HH_VENDOR_ID_INVALID 0xffff + + +/* report descriptor information */ +typedef struct { + UINT16 vendor_id; /* vendor ID */ + UINT16 product_id; /* product ID */ + UINT16 version; /* version */ + UINT16 ssr_max_latency; /* SSR max latency, BTA_HH_SSR_PARAM_INVALID if unknown */ + UINT16 ssr_min_tout; /* SSR min timeout, BTA_HH_SSR_PARAM_INVALID if unknown */ + UINT8 ctry_code; /*Country Code.*/ +#if (defined BTA_HH_LE_INCLUDED && BTA_HH_LE_INCLUDED == TRUE) +#define BTA_HH_LE_REMOTE_WAKE 0x01 +#define BTA_HH_LE_NORMAL_CONN 0x02 + + UINT8 flag; +#endif + tBTA_HH_DEV_DESCR descriptor; +} tBTA_HH_DEV_DSCP_INFO; + +/* callback event data for BTA_HH_OPEN_EVT */ +typedef struct { + BD_ADDR bda; /* HID device bd address */ + tBTA_HH_STATUS status; /* operation status */ + UINT8 handle; /* device handle */ + BOOLEAN is_orig; /* indicate if host initiate connection */ +#if (defined BTA_HH_LE_INCLUDED && BTA_HH_LE_INCLUDED == TRUE) + BOOLEAN le_hid; /* is LE devices? */ + BOOLEAN scps_supported; /* scan parameter service supported */ +#endif + +} tBTA_HH_CONN; + +typedef tBTA_HH_CONN tBTA_HH_DEV_INFO; + +/* callback event data */ +typedef struct { + tBTA_HH_STATUS status; /* operation status */ + UINT8 handle; /* device handle */ +} tBTA_HH_CBDATA; + +enum { + BTA_HH_MOD_CTRL_KEY, + BTA_HH_MOD_SHFT_KEY, + BTA_HH_MOD_ALT_KEY, + BTA_HH_MOD_GUI_KEY, + BTA_HH_MOD_MAX_KEY +}; + +/* parsed boot mode keyboard report */ +typedef struct { + UINT8 this_char[6]; /* virtual key code */ + BOOLEAN mod_key[BTA_HH_MOD_MAX_KEY]; + /* ctrl, shift, Alt, GUI */ + /* modifier key: is Shift key pressed */ + /* modifier key: is Ctrl key pressed */ + /* modifier key: is Alt key pressed */ + /* modifier key: GUI up/down */ + BOOLEAN caps_lock; /* is caps locked */ + BOOLEAN num_lock; /* is Num key pressed */ +} tBTA_HH_KEYBD_RPT; + +/* parsed boot mode mouse report */ +typedef struct { + UINT8 mouse_button; /* mouse button is clicked */ + INT8 delta_x; /* displacement x */ + INT8 delta_y; /* displacement y */ +} tBTA_HH_MICE_RPT; + +/* parsed Boot report */ +typedef struct { + tBTA_HH_BOOT_RPT_ID dev_type; /* type of device report */ + union { + tBTA_HH_KEYBD_RPT keybd_rpt; /* keyboard report */ + tBTA_HH_MICE_RPT mice_rpt; /* mouse report */ + } data_rpt; +} tBTA_HH_BOOT_RPT; + +/* handshake data */ +typedef struct { + tBTA_HH_STATUS status; /* handshake status */ + UINT8 handle; /* device handle */ + union { + tBTA_HH_PROTO_MODE proto_mode; /* GET_PROTO_EVT :protocol mode */ + BT_HDR *p_rpt_data; /* GET_RPT_EVT : report data */ + UINT8 idle_rate; /* GET_IDLE_EVT : idle rate */ + } rsp_data; + +} tBTA_HH_HSDATA; + + +/* upper layer send data */ +typedef struct { + tBTA_HH_STATUS status; /* handshake status */ + UINT8 handle; /* device handle */ + UINT8 reason; /* send data failed reason */ +} tBTA_HH_API_SENDDATA; + +/* interrupt channel data */ +typedef struct { + tBTA_HH_STATUS status; /* handshake status */ + UINT8 handle; /* device handle */ + tBTA_HH_PROTO_MODE proto_mode; /* protocol mode */ + BT_HDR *p_data; /* DATA_EVT : feature report data */ +} tBTA_HH_INTDATA; + +/* union of data associated with HD callback */ +typedef union { + tBTA_HH_DEV_INFO dev_info; /* BTA_HH_ADD_DEV_EVT, BTA_HH_RMV_DEV_EVT */ + tBTA_HH_CONN conn; /* BTA_HH_OPEN_EVT */ + tBTA_HH_CBDATA dev_status; /* BTA_HH_CLOSE_EVT, + BTA_HH_SET_PROTO_EVT + BTA_HH_SET_RPT_EVT + BTA_HH_SET_IDLE_EVT + BTA_HH_UPDATE_SCPP_EVT */ + + tBTA_HH_STATUS status; /* BTA_HH_ENABLE_EVT */ + tBTA_HH_DEV_DSCP_INFO dscp_info; /* BTA_HH_GET_DSCP_EVT */ + tBTA_HH_HSDATA hs_data; /* GET_ transaction callback + BTA_HH_GET_RPT_EVT + BTA_HH_GET_PROTO_EVT + BTA_HH_GET_IDLE_EVT */ + tBTA_HH_API_SENDDATA send_data; /* BTA_HH_DATA_EVT */ + tBTA_HH_INTDATA int_data; /* BTA_HH_DATA_IND_EVT */ +} tBTA_HH; + +/* BTA HH callback function */ +typedef void (tBTA_HH_CBACK)(tBTA_HH_EVT event, tBTA_HH *p_data); + + +/***************************************************************************** +** External Function Declarations +*****************************************************************************/ +#ifdef __cplusplus +extern "C" +{ +#endif + +/******************************************************************************* +** +** Function BTA_HhRegister +** +** Description This function enable HID host and registers HID-Host with +** lower layers. +** +** Returns void +** +*******************************************************************************/ +extern void BTA_HhEnable(tBTA_SEC sec_mask, tBTA_HH_CBACK *p_cback); + +/******************************************************************************* +** +** Function BTA_HhDeregister +** +** Description This function is called when the host is about power down. +** +** Returns void +** +*******************************************************************************/ +extern void BTA_HhDisable(void); + +/******************************************************************************* +** +** Function BTA_HhOpen +** +** Description This function is called to start an inquiry and read SDP +** record of responding devices; connect to a device if only +** one active HID device is found. +** +** Returns void +** +*******************************************************************************/ +extern void BTA_HhOpen (BD_ADDR dev_bda, tBTA_HH_PROTO_MODE mode, + tBTA_SEC sec_mask); + +/******************************************************************************* +** +** Function BTA_HhClose +** +** Description This function disconnects the device. +** +** Returns void +** +*******************************************************************************/ +extern void BTA_HhClose(UINT8 dev_handle); + +/******************************************************************************* +** +** Function BTA_HhSetProtoMode +** +** Description This function set the protocol mode at specified HID handle +** +** Returns void +** +*******************************************************************************/ +extern void BTA_HhSetProtoMode(UINT8 handle, tBTA_HH_PROTO_MODE t_type); + +/******************************************************************************* +** +** Function BTA_HhGetProtoMode +** +** Description This function get the protocol mode of a specified HID device. +** +** Returns void +** +*******************************************************************************/ +extern void BTA_HhGetProtoMode(UINT8 dev_handle); +/******************************************************************************* +** +** Function BTA_HhSetReport +** +** Description send SET_REPORT to device. +** +** Returns void +** +*******************************************************************************/ +extern void BTA_HhSetReport(UINT8 dev_handle, tBTA_HH_RPT_TYPE r_type, + BT_HDR *p_data); + +/******************************************************************************* +** +** Function BTA_HhGetReport +** +** Description Send a GET_REPORT to HID device. +** +** Returns void +** +*******************************************************************************/ +extern void BTA_HhGetReport(UINT8 dev_handle, tBTA_HH_RPT_TYPE r_type, + UINT8 rpt_id, UINT16 buf_size); +/******************************************************************************* +** +** Function BTA_HhSetIdle +** +** Description send SET_IDLE to device. +** +** Returns void +** +*******************************************************************************/ +extern void BTA_HhSetIdle(UINT8 dev_handle, UINT16 idle_rate); + +/******************************************************************************* +** +** Function BTA_HhGetIdle +** +** Description Send a GET_IDLE to HID device. +** +** Returns void +** +*******************************************************************************/ +extern void BTA_HhGetIdle(UINT8 dev_handle); + +/******************************************************************************* +** +** Function BTA_HhSendCtrl +** +** Description Send HID_CONTROL request to a HID device. +** +** Returns void +** +*******************************************************************************/ +extern void BTA_HhSendCtrl(UINT8 dev_handle, + tBTA_HH_TRANS_CTRL_TYPE c_type); + +/******************************************************************************* +** +** Function BTA_HhSetIdle +** +** Description send SET_IDLE to device. +** +** Returns void +** +*******************************************************************************/ +extern void BTA_HhSetIdle(UINT8 dev_handle, UINT16 idle_rate); + + +/******************************************************************************* +** +** Function BTA_HhGetIdle +** +** Description Send a GET_IDLE from HID device. +** +** Returns void +** +*******************************************************************************/ +extern void BTA_HhGetIdle(UINT8 dev_handle); + +/******************************************************************************* +** +** Function BTA_HhSendData +** +** Description Send DATA transaction to a HID device. +** +** Returns void +** +*******************************************************************************/ +extern void BTA_HhSendData(UINT8 dev_handle, BD_ADDR dev_bda, BT_HDR *p_buf); + +/******************************************************************************* +** +** Function BTA_HhGetDscpInfo +** +** Description Get report descriptor of the device +** +** Returns void +** +*******************************************************************************/ +extern void BTA_HhGetDscpInfo(UINT8 dev_handle); + +/******************************************************************************* +** Function BTA_HhAddDev +** +** Description Add a virtually cabled device into HID-Host device list +** to manage and assign a device handle for future API call, +** host applciation call this API at start-up to initialize its +** virtually cabled devices. +** +** Returns void +** +*******************************************************************************/ +extern void BTA_HhAddDev(BD_ADDR bda, tBTA_HH_ATTR_MASK attr_mask, + UINT8 sub_class, UINT8 app_id, + tBTA_HH_DEV_DSCP_INFO dscp_info); +/******************************************************************************* +** +** Function BTA_HhRemoveDev +** +** Description Remove a device from the HID host devices list. +** +** Returns void +** +*******************************************************************************/ +extern void BTA_HhRemoveDev(UINT8 dev_handle ); + +/******************************************************************************* +** +** Parsing Utility Functions +** +*******************************************************************************/ +/******************************************************************************* +** +** Function BTA_HhParseBootRpt +** +** Description This utility function parse a boot mode report. +** +** Returns void +** +*******************************************************************************/ +extern void BTA_HhParseBootRpt(tBTA_HH_BOOT_RPT *p_data, UINT8 *p_report, + UINT16 report_len); + +#if BTA_HH_LE_INCLUDED == TRUE +/******************************************************************************* +** +** Function BTA_HhUpdateLeScanParam +** +** Description Update the scan paramteters if connected to a LE hid device as +** report host. +** +** Returns void +** +*******************************************************************************/ +extern void BTA_HhUpdateLeScanParam(UINT8 dev_handle, UINT16 scan_int, UINT16 scan_win); +#endif +/* test commands */ +extern void bta_hh_le_hid_read_rpt_clt_cfg(BD_ADDR bd_addr, UINT8 rpt_id); + + + +#ifdef __cplusplus +} +#endif + +#endif ///defined(BTA_HH_INCLUDED) && (BTA_HH_INCLUDED == TRUE) + + +#endif /* BTA_HH_API_H */ diff --git a/lib/bt/host/bluedroid/bta/include/bta/bta_hh_co.h b/lib/bt/host/bluedroid/bta/include/bta/bta_hh_co.h new file mode 100644 index 00000000..f0fef370 --- /dev/null +++ b/lib/bt/host/bluedroid/bta/include/bta/bta_hh_co.h @@ -0,0 +1,132 @@ +/****************************************************************************** + * + * Copyright (C) 2005-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This is the interface file for hid host call-out functions. + * + ******************************************************************************/ +#ifndef BTA_HH_CO_H +#define BTA_HH_CO_H + +#include "bta/bta_hh_api.h" + +typedef struct { + UINT16 rpt_uuid; + UINT8 rpt_id; + tBTA_HH_RPT_TYPE rpt_type; + UINT8 inst_id; + UINT8 prop; +} tBTA_HH_RPT_CACHE_ENTRY; + +/******************************************************************************* +** +** Function bta_hh_co_data +** +** Description This callout function is executed by HH when data is received +** in interupt channel. +** +** +** Returns void. +** +*******************************************************************************/ +extern void bta_hh_co_data(UINT8 dev_handle, UINT8 *p_rpt, UINT16 len, + tBTA_HH_PROTO_MODE mode, UINT8 sub_class, + UINT8 ctry_code, BD_ADDR peer_addr, UINT8 app_id); + +/******************************************************************************* +** +** Function bta_hh_co_open +** +** Description This callout function is executed by HH when connection is +** opened, and application may do some device specific +** initialization. +** +** Returns void. +** +*******************************************************************************/ +extern void bta_hh_co_open(UINT8 dev_handle, UINT8 sub_class, + UINT16 attr_mask, UINT8 app_id); + +/******************************************************************************* +** +** Function bta_hh_co_close +** +** Description This callout function is executed by HH when connection is +** closed, and device specific finalizatio nmay be needed. +** +** Returns void. +** +*******************************************************************************/ +extern void bta_hh_co_close(UINT8 dev_handle, UINT8 app_id); + +#if (BLE_INCLUDED == TRUE && BTA_HH_LE_INCLUDED == TRUE) +/******************************************************************************* +** +** Function bta_hh_le_co_rpt_info +** +** Description This callout function is to convey the report information on +** a HOGP device to the application. Application can save this +** information in NV if device is bonded and load it back when +** stack reboot. +** +** Parameters remote_bda - remote device address +** p_entry - report entry pointer +** app_id - application id +** +** Returns void. +** +*******************************************************************************/ +extern void bta_hh_le_co_rpt_info(BD_ADDR remote_bda, + tBTA_HH_RPT_CACHE_ENTRY *p_entry, + UINT8 app_id); + +/******************************************************************************* +** +** Function bta_hh_le_co_cache_load +** +** Description This callout function is to request the application to load the +** cached HOGP report if there is any. When cache reading is completed, +** bta_hh_le_ci_cache_load() is called by the application. +** +** Parameters remote_bda - remote device address +** p_num_rpt: number of cached report +** app_id - application id +** +** Returns the acched report array +** +*******************************************************************************/ +extern tBTA_HH_RPT_CACHE_ENTRY *bta_hh_le_co_cache_load (BD_ADDR remote_bda, + UINT8 *p_num_rpt, + UINT8 app_id); + +/******************************************************************************* +** +** Function bta_hh_le_co_reset_rpt_cache +** +** Description This callout function is to reset the HOGP device cache. +** +** Parameters remote_bda - remote device address +** +** Returns none +** +*******************************************************************************/ +extern void bta_hh_le_co_reset_rpt_cache (BD_ADDR remote_bda, UINT8 app_id); + +#endif /* #if (BLE_INCLUDED == TRUE && BTA_HH_LE_INCLUDED == TRUE) */ +#endif /* BTA_HH_CO_H */ diff --git a/lib/bt/host/bluedroid/bta/include/bta/bta_jv_api.h b/lib/bt/host/bluedroid/bta/include/bta/bta_jv_api.h new file mode 100644 index 00000000..660ce5cf --- /dev/null +++ b/lib/bt/host/bluedroid/bta/include/bta/bta_jv_api.h @@ -0,0 +1,972 @@ +/****************************************************************************** + * + * Copyright (C) 2006-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This is the public interface file the BTA Java I/F + * + ******************************************************************************/ +#ifndef BTA_JV_API_H +#define BTA_JV_API_H + +#include "common/bt_target.h" +#include "stack/bt_types.h" +#include "bta/bta_api.h" +#include "stack/btm_api.h" +#include "stack/l2c_api.h" +#include "stack/rfcdefs.h" +#include "stack/sdp_api.h" + +#if (defined BTA_JV_INCLUDED && BTA_JV_INCLUDED == TRUE) +/***************************************************************************** +** Constants and data types +*****************************************************************************/ +/* status values */ +#define BTA_JV_SUCCESS 0 /* Successful operation. */ +#define BTA_JV_FAILURE 1 /* Generic failure. */ +#define BTA_JV_BUSY 2 /* Temporarily can not handle this request. */ +#define BTA_JV_NO_DATA 3 /* no data. */ +#define BTA_JV_NO_RESOURCE 4 /* No more set pm control block */ +#define BTA_JV_ALREADY_DONE 5 /* already done, repeat the operation */ + +typedef UINT8 tBTA_JV_STATUS; +#define BTA_JV_INTERNAL_ERR (-1) /* internal error. */ + +#define BTA_JV_MAX_UUIDS SDP_MAX_UUID_FILTERS +#define BTA_JV_MAX_ATTRS SDP_MAX_ATTR_FILTERS +#define BTA_JV_MAX_SDP_REC SDP_MAX_RECORDS +#define BTA_JV_MAX_L2C_CONN GAP_MAX_CONNECTIONS /* GAP handle is used as index, hence do not change this value */ +#define BTA_JV_MAX_SCN PORT_MAX_RFC_PORTS /* same as BTM_MAX_SCN (in btm_int.h) */ +#define BTA_JV_MAX_RFC_CONN MAX_RFC_PORTS +#define BTA_JV_MAX_CREDIT_NUM PORT_RX_BUF_HIGH_WM + +#ifndef BTA_JV_DEF_RFC_MTU +#define BTA_JV_DEF_RFC_MTU (3*330) +#endif + +#ifndef BTA_JV_MAX_RFC_SR_SESSION +#define BTA_JV_MAX_RFC_SR_SESSION MAX_BD_CONNECTIONS +#endif + +/* BTA_JV_MAX_RFC_SR_SESSION can not be bigger than MAX_BD_CONNECTIONS */ +#if (BTA_JV_MAX_RFC_SR_SESSION > MAX_BD_CONNECTIONS) +#undef BTA_JV_MAX_RFC_SR_SESSION +#define BTA_JV_MAX_RFC_SR_SESSION MAX_BD_CONNECTIONS +#endif + +#define BTA_JV_FIRST_SERVICE_ID BTA_FIRST_JV_SERVICE_ID +#define BTA_JV_LAST_SERVICE_ID BTA_LAST_JV_SERVICE_ID +#define BTA_JV_NUM_SERVICE_ID (BTA_LAST_JV_SERVICE_ID - BTA_FIRST_JV_SERVICE_ID + 1) + +/* Discoverable modes */ +enum { + BTA_JV_DISC_NONE, + BTA_JV_DISC_LIMITED, + BTA_JV_DISC_GENERAL +}; +typedef UINT16 tBTA_JV_DISC; + +#define BTA_JV_ROLE_SLAVE BTM_ROLE_SLAVE +#define BTA_JV_ROLE_MASTER BTM_ROLE_MASTER +typedef UINT32 tBTA_JV_ROLE; + +#define BTA_JV_SERVICE_LMTD_DISCOVER BTM_COD_SERVICE_LMTD_DISCOVER /* 0x0020 */ +#define BTA_JV_SERVICE_POSITIONING BTM_COD_SERVICE_POSITIONING /* 0x0100 */ +#define BTA_JV_SERVICE_NETWORKING BTM_COD_SERVICE_NETWORKING /* 0x0200 */ +#define BTA_JV_SERVICE_RENDERING BTM_COD_SERVICE_RENDERING /* 0x0400 */ +#define BTA_JV_SERVICE_CAPTURING BTM_COD_SERVICE_CAPTURING /* 0x0800 */ +#define BTA_JV_SERVICE_OBJ_TRANSFER BTM_COD_SERVICE_OBJ_TRANSFER /* 0x1000 */ +#define BTA_JV_SERVICE_AUDIO BTM_COD_SERVICE_AUDIO /* 0x2000 */ +#define BTA_JV_SERVICE_TELEPHONY BTM_COD_SERVICE_TELEPHONY /* 0x4000 */ +#define BTA_JV_SERVICE_INFORMATION BTM_COD_SERVICE_INFORMATION /* 0x8000 */ + +/* JV ID type */ +#define BTA_JV_PM_ID_1 1 /* PM example profile 1 */ +#define BTA_JV_PM_ID_2 2 /* PM example profile 2 */ +#define BTA_JV_PM_ID_CLEAR 0 /* Special JV ID used to clear PM profile */ +#define BTA_JV_PM_ALL 0xFF /* Generic match all id, see bta_dm_cfg.c */ +typedef UINT8 tBTA_JV_PM_ID; + +#define BTA_JV_PM_HANDLE_CLEAR 0xFF /* Special JV ID used to clear PM profile */ + +/* define maximum number of registered PM entities. should be in sync with bta pm! */ +#ifndef BTA_JV_PM_MAX_NUM +#define BTA_JV_PM_MAX_NUM 5 +#endif + +/* JV pm connection states */ +enum { + BTA_JV_CONN_OPEN = 0, /* Connection opened state */ + BTA_JV_CONN_CLOSE, /* Connection closed state */ + BTA_JV_APP_OPEN, /* JV Application opened state */ + BTA_JV_APP_CLOSE, /* JV Application closed state */ + BTA_JV_SCO_OPEN, /* SCO connection opened state */ + BTA_JV_SCO_CLOSE, /* SCO connection opened state */ + BTA_JV_CONN_IDLE, /* Connection idle state */ + BTA_JV_CONN_BUSY, /* Connection busy state */ + BTA_JV_MAX_CONN_STATE /* Max number of connection state */ +}; +typedef UINT8 tBTA_JV_CONN_STATE; + +/* JV Connection types */ +#define BTA_JV_CONN_TYPE_RFCOMM 0 +#define BTA_JV_CONN_TYPE_L2CAP 1 +#define BTA_JV_CONN_TYPE_L2CAP_LE 2 + +/* Java I/F callback events */ +/* events received by tBTA_JV_DM_CBACK */ +#define BTA_JV_ENABLE_EVT 0 /* JV enabled */ +#define BTA_JV_DISABLE_EVT 1 /* JV disabled */ +#define BTA_JV_GET_SCN_EVT 6 /* Reserved an SCN */ +#define BTA_JV_GET_PSM_EVT 7 /* Reserved a PSM */ +#define BTA_JV_DISCOVERY_COMP_EVT 8 /* SDP discovery complete */ +#define BTA_JV_CREATE_RECORD_EVT 11 /* the result for BTA_JvCreateRecord */ + +/* events received by tBTA_JV_L2CAP_CBACK */ +#if BTA_JV_L2CAP_INCLUDED +#define BTA_JV_L2CAP_OPEN_EVT 16 /* open status of L2CAP connection */ +#define BTA_JV_L2CAP_CLOSE_EVT 17 /* L2CAP connection closed */ +#define BTA_JV_L2CAP_START_EVT 18 /* L2CAP server started */ +#define BTA_JV_L2CAP_CL_INIT_EVT 19 /* L2CAP client initiated a connection */ +#define BTA_JV_L2CAP_DATA_IND_EVT 20 /* L2CAP connection received data */ +#define BTA_JV_L2CAP_CONG_EVT 21 /* L2CAP connection congestion status changed */ +#define BTA_JV_L2CAP_READ_EVT 22 /* the result for BTA_JvL2capRead */ +#define BTA_JV_L2CAP_RECEIVE_EVT 23 /* the result for BTA_JvL2capReceive*/ +#define BTA_JV_L2CAP_WRITE_EVT 24 /* the result for BTA_JvL2capWrite*/ +#define BTA_JV_L2CAP_WRITE_FIXED_EVT 25 /* the result for BTA_JvL2capWriteFixed */ +#endif /* BTA_JV_L2CAP_INCLUDED */ + +/* events received by tBTA_JV_RFCOMM_CBACK */ +#if BTA_JV_RFCOMM_INCLUDED +#define BTA_JV_RFCOMM_OPEN_EVT 26 /* open status of RFCOMM Client connection */ +#define BTA_JV_RFCOMM_CLOSE_EVT 27 /* RFCOMM connection closed */ +#define BTA_JV_RFCOMM_START_EVT 28 /* RFCOMM server started */ +#define BTA_JV_RFCOMM_CL_INIT_EVT 29 /* RFCOMM client initiated a connection */ +#define BTA_JV_RFCOMM_DATA_IND_EVT 30 /* RFCOMM connection received data */ +#define BTA_JV_RFCOMM_CONG_EVT 31 /* RFCOMM connection congestion status changed */ +#define BTA_JV_RFCOMM_READ_EVT 32 /* the result for BTA_JvRfcommRead */ +#define BTA_JV_RFCOMM_WRITE_EVT 33 /* the result for BTA_JvRfcommWrite*/ +#define BTA_JV_RFCOMM_SRV_OPEN_EVT 34 /* open status of Server RFCOMM connection */ +#endif /* BTA_JV_RFCOMM_INCLUDED */ +#define BTA_JV_FREE_SCN_EVT 35 /* FREE an SCN */ +#define BTA_JV_MAX_EVT 36 /* max number of JV events */ + +typedef UINT16 tBTA_JV_EVT; + +/* data associated with BTA_JV_SET_DISCOVER_EVT */ +typedef struct { + tBTA_JV_STATUS status; /* Whether the operation succeeded or failed. */ + tBTA_JV_DISC disc_mode; /* The current discoverable mode */ +} tBTA_JV_SET_DISCOVER; + +/* data associated with BTA_JV_DISCOVERY_COMP_EVT_ */ +typedef struct { + tBTA_JV_STATUS status; /* Whether the operation succeeded or failed. */ + UINT8 scn_num; /* num of channel */ + UINT8 scn[BTA_JV_MAX_SCN]; /* channel # */ + const char *service_name[BTA_JV_MAX_SCN]; /* service_name */ +} tBTA_JV_DISCOVERY_COMP; + +/* data associated with BTA_JV_CREATE_RECORD_EVT */ +typedef struct { + tBTA_JV_STATUS status; /* Whether the operation succeeded or failed. */ + UINT32 handle; /* The SDP handle */ +} tBTA_JV_CREATE_RECORD; + +#if BTA_JV_L2CAP_INCLUDED +/* data associated with BTA_JV_L2CAP_OPEN_EVT */ +typedef struct { + tBTA_JV_STATUS status; /* Whether the operation succeeded or failed. */ + UINT32 handle; /* The connection handle */ + BD_ADDR rem_bda; /* The peer address */ + INT32 tx_mtu; /* The transmit MTU */ +} tBTA_JV_L2CAP_OPEN; + +/* data associated with BTA_JV_L2CAP_CLOSE_EVT */ +typedef struct { + tBTA_JV_STATUS status; /* Whether the operation succeeded or failed. */ + UINT32 handle; /* The connection handle */ + BOOLEAN async; /* FALSE, if local initiates disconnect */ + void * user_data; /* piggyback caller's private data */ +} tBTA_JV_L2CAP_CLOSE; + +/* data associated with BTA_JV_L2CAP_START_EVT */ +typedef struct { + tBTA_JV_STATUS status; /* Whether the operation succeeded or failed. */ + UINT32 handle; /* The connection handle */ + UINT8 sec_id; /* security ID used by this server */ +} tBTA_JV_L2CAP_START; + +/* data associated with BTA_JV_L2CAP_CL_INIT_EVT */ +typedef struct { + tBTA_JV_STATUS status; /* Whether the operation succeeded or failed. */ + UINT32 handle; /* The connection handle */ + UINT8 sec_id; /* security ID used by this client */ +} tBTA_JV_L2CAP_CL_INIT; + +/* data associated with BTA_JV_L2CAP_CONG_EVT */ +typedef struct { + tBTA_JV_STATUS status; /* Whether the operation succeeded or failed. */ + UINT32 handle; /* The connection handle */ + BOOLEAN cong; /* TRUE, congested. FALSE, uncongested */ +} tBTA_JV_L2CAP_CONG; + +/* data associated with BTA_JV_L2CAP_READ_EVT */ +typedef struct { + tBTA_JV_STATUS status; /* Whether the operation succeeded or failed. */ + UINT32 handle; /* The connection handle */ + UINT32 req_id; /* The req_id in the associated BTA_JvL2capRead() */ + UINT8 *p_data; /* This points the same location as the p_data + * parameter in BTA_JvL2capRead () */ + UINT16 len; /* The length of the data read. */ +} tBTA_JV_L2CAP_READ; + +/* data associated with BTA_JV_L2CAP_RECEIVE_EVT */ +typedef struct { + tBTA_JV_STATUS status; /* Whether the operation succeeded or failed. */ + UINT32 handle; /* The connection handle */ + UINT32 req_id; /* The req_id in the associated BTA_JvL2capReceive() */ + UINT8 *p_data; /* This points the same location as the p_data + * parameter in BTA_JvL2capReceive () */ + UINT16 len; /* The length of the data read. */ +} tBTA_JV_L2CAP_RECEIVE; + +/* data associated with BTA_JV_L2CAP_WRITE_EVT */ +typedef struct { + tBTA_JV_STATUS status; /* Whether the operation succeeded or failed. */ + UINT32 handle; /* The connection handle */ + UINT32 req_id; /* The req_id in the associated BTA_JvL2capWrite() */ + UINT16 len; /* The length of the data written. */ + BOOLEAN cong; /* congestion status */ +} tBTA_JV_L2CAP_WRITE; + +/* data associated with BTA_JV_L2CAP_OPEN_EVT for LE sockets */ +typedef struct { + tBTA_JV_STATUS status; /* Whether the operation succeeded or failed. */ + UINT32 handle; /* The connection handle */ + BD_ADDR rem_bda; /* The peer address */ + INT32 tx_mtu; /* The transmit MTU */ + void **p_p_cback; /* set them for new socket */ + void **p_user_data;/* set them for new socket */ + +} tBTA_JV_L2CAP_LE_OPEN; + +/*data associated with BTA_JV_L2CAP_DATA_IND_EVT if used for LE */ +typedef struct { + UINT32 handle; /* The connection handle */ + BT_HDR *p_buf; /* The incoming data */ +} tBTA_JV_LE_DATA_IND; + +/* data associated with BTA_JV_L2CAP_WRITE_FIXED_EVT */ +typedef struct { + tBTA_JV_STATUS status; /* Whether the operation succeeded or failed. */ + UINT16 channel; /* The connection channel */ + BD_ADDR addr; /* The peer address */ + UINT32 req_id; /* The req_id in the associated BTA_JvL2capWrite() */ + UINT16 len; /* The length of the data written. */ + BOOLEAN cong; /* congestion status */ +} tBTA_JV_L2CAP_WRITE_FIXED; +#endif /* BTA_JV_L2CAP_INCLUDED */ + +#if BTA_JV_RFCOMM_INCLUDED +/* data associated with BTA_JV_RFCOMM_OPEN_EVT */ +typedef struct { + tBTA_JV_STATUS status; /* Whether the operation succeeded or failed. */ + UINT16 peer_mtu; /* Max MTU that port can send */ + UINT32 handle; /* The connection handle */ + BD_ADDR rem_bda; /* The peer address */ +} tBTA_JV_RFCOMM_OPEN; + +/* data associated with BTA_JV_RFCOMM_SRV_OPEN_EVT */ +typedef struct { + tBTA_JV_STATUS status; /* Whether the operation succeeded or failed. */ + UINT16 peer_mtu; /* Max MTU that port can send */ + UINT32 handle; /* The connection handle */ + UINT32 new_listen_handle; /* The new listen handle */ + BD_ADDR rem_bda; /* The peer address */ +} tBTA_JV_RFCOMM_SRV_OPEN; + +/* data associated with BTA_JV_RFCOMM_CLOSE_EVT */ +typedef struct { + tBTA_JV_STATUS status; /* Whether the operation succeeded or failed. */ + UINT32 port_status; /* PORT status */ + UINT32 handle; /* The connection handle */ + BOOLEAN async; /* FALSE, if local initiates disconnect */ + void * user_data; /* piggyback caller's private data */ +} tBTA_JV_RFCOMM_CLOSE; + +/* data associated with BTA_JV_RFCOMM_START_EVT */ +typedef struct { + tBTA_JV_STATUS status; /* Whether the operation succeeded or failed. */ + UINT32 handle; /* The connection handle */ + UINT8 sec_id; /* security ID used by this server */ + UINT8 scn; /* Server channe number */ + BOOLEAN use_co; /* TRUE to use co_rfc_data */ +} tBTA_JV_RFCOMM_START; + +/* data associated with BTA_JV_RFCOMM_CL_INIT_EVT */ +typedef struct { + tBTA_JV_STATUS status; /* Whether the operation succeeded or failed. */ + UINT32 handle; /* The connection handle */ + UINT8 sec_id; /* security ID used by this client */ + BOOLEAN use_co; /* TRUE to use co_rfc_data */ +} tBTA_JV_RFCOMM_CL_INIT; + +/* data associated with BTA_JV_RFCOMM_CONG_EVT */ +typedef struct { + tBTA_JV_STATUS status; /* Whether the operation succeeded or failed. */ + UINT32 handle; /* The connection handle */ + BOOLEAN cong; /* TRUE, congested. FALSE, uncongested */ +} tBTA_JV_RFCOMM_CONG; + +/* data associated with BTA_JV_RFCOMM_READ_EVT */ +typedef struct { + tBTA_JV_STATUS status; /* Whether the operation succeeded or failed. */ + UINT32 handle; /* The connection handle */ + UINT32 req_id; /* The req_id in the associated BTA_JvRfcommRead() */ + UINT8 *p_data; /* This points the same location as the p_data + * parameter in BTA_JvRfcommRead () */ + UINT16 len; /* The length of the data read. */ +} tBTA_JV_RFCOMM_READ; + +/* data associated with BTA_JV_RFCOMM_WRITE_EVT */ +typedef struct { + tBTA_JV_STATUS status; /* Whether the operation succeeded or failed. */ + UINT32 handle; /* The connection handle */ + UINT32 req_id; /* The req_id in the associated BTA_JvRfcommWrite() */ + int len; /* The length of the data written. */ + BOOLEAN cong; /* congestion status */ + BOOLEAN old_cong; /* congestion status */ +} tBTA_JV_RFCOMM_WRITE; +#endif /* BTA_JV_RFCOMM_INCLUDED */ + +/*data associated with BTA_JV_L2CAP_DATA_IND_EVT & BTA_JV_RFCOMM_DATA_IND_EVT */ +typedef struct { + UINT32 handle; /* The connection handle */ + BT_HDR *p_buf; /* The incoming data */ +} tBTA_JV_DATA_IND; + +/* data associated with BTA_JV_API_SET_PM_PROFILE_EVT */ +typedef struct { + tBTA_JV_STATUS status; /* Status of the operation */ + UINT32 handle; /* Connection handle */ + tBTA_JV_PM_ID app_id; /* JV app ID */ +} tBTA_JV_SET_PM_PROFILE; + +/* data associated with BTA_JV_API_NOTIFY_PM_STATE_CHANGE_EVT */ +typedef struct { + UINT32 handle; /* Connection handle */ + tBTA_JV_CONN_STATE state; /* JV connection stata */ +} tBTA_JV_NOTIFY_PM_STATE_CHANGE; + +/* indicate server at which status */ +typedef enum { + BTA_JV_SERVER_START_FAILED, + BTA_JV_SERVER_RUNNING, + BTA_JV_SERVER_STATUS_MAX, +} tBTA_JV_SERVER_STATUS; + +typedef struct { + tBTA_JV_SERVER_STATUS server_status; + UINT32 slot_id; +}tBTA_JV_FREE_SCN_USER_DATA; + +/* data associated with BTA_JV_FREE_SCN_EVT */ +typedef struct { + tBTA_JV_STATUS status; /* Status of the operation */ + tBTA_JV_SERVER_STATUS server_status; /* Server status */ + UINT8 scn; /* Server channe number */ +} tBTA_JV_FREE_SCN; + + +/* union of data associated with JV callback */ +typedef union { + tBTA_JV_STATUS status; /* BTA_JV_ENABLE_EVT */ + tBTA_JV_DISCOVERY_COMP disc_comp; /* BTA_JV_DISCOVERY_COMP_EVT */ + tBTA_JV_SET_DISCOVER set_discover; /* BTA_JV_SET_DISCOVER_EVT */ + UINT8 scn; /* BTA_JV_GET_SCN_EVT */ + UINT16 psm; /* BTA_JV_GET_PSM_EVT */ + tBTA_JV_CREATE_RECORD create_rec; /* BTA_JV_CREATE_RECORD_EVT */ +#if BTA_JV_L2CAP_INCLUDED + tBTA_JV_L2CAP_OPEN l2c_open; /* BTA_JV_L2CAP_OPEN_EVT */ + tBTA_JV_L2CAP_CLOSE l2c_close; /* BTA_JV_L2CAP_CLOSE_EVT */ + tBTA_JV_L2CAP_START l2c_start; /* BTA_JV_L2CAP_START_EVT */ + tBTA_JV_L2CAP_CL_INIT l2c_cl_init; /* BTA_JV_L2CAP_CL_INIT_EVT */ + tBTA_JV_L2CAP_CONG l2c_cong; /* BTA_JV_L2CAP_CONG_EVT */ + tBTA_JV_L2CAP_READ l2c_read; /* BTA_JV_L2CAP_READ_EVT */ + tBTA_JV_L2CAP_WRITE l2c_write; /* BTA_JV_L2CAP_WRITE_EVT */ +#endif /* BTA_JV_L2CAP_INCLUDED */ +#if BTA_JV_RFCOMM_INCLUDED + tBTA_JV_RFCOMM_OPEN rfc_open; /* BTA_JV_RFCOMM_OPEN_EVT */ + tBTA_JV_RFCOMM_SRV_OPEN rfc_srv_open; /* BTA_JV_RFCOMM_SRV_OPEN_EVT */ + tBTA_JV_RFCOMM_CLOSE rfc_close; /* BTA_JV_RFCOMM_CLOSE_EVT */ + tBTA_JV_RFCOMM_START rfc_start; /* BTA_JV_RFCOMM_START_EVT */ + tBTA_JV_RFCOMM_CL_INIT rfc_cl_init; /* BTA_JV_RFCOMM_CL_INIT_EVT */ + tBTA_JV_RFCOMM_CONG rfc_cong; /* BTA_JV_RFCOMM_CONG_EVT */ + tBTA_JV_RFCOMM_READ rfc_read; /* BTA_JV_RFCOMM_READ_EVT */ + tBTA_JV_RFCOMM_WRITE rfc_write; /* BTA_JV_RFCOMM_WRITE_EVT */ +#endif /* BTA_JV_RFCOMM_INCLUDED */ + tBTA_JV_DATA_IND data_ind; /* BTA_JV_L2CAP_DATA_IND_EVT + BTA_JV_RFCOMM_DATA_IND_EVT */ + tBTA_JV_FREE_SCN free_scn; /* BTA_JV_FREE_SCN_EVT */ +#if BTA_JV_L2CAP_INCLUDED + tBTA_JV_L2CAP_LE_OPEN l2c_le_open; /* BTA_JV_L2CAP_OPEN_EVT */ + tBTA_JV_LE_DATA_IND le_data_ind; /* BTA_JV_L2CAP_LE_DATA_IND_EVT */ + tBTA_JV_L2CAP_WRITE_FIXED l2c_write_fixed; /* BTA_JV_L2CAP_WRITE_FIXED_EVT */ +#endif /* BTA_JV_L2CAP_INCLUDED */ +} tBTA_JV; + +/* JAVA DM Interface callback */ +typedef void (tBTA_JV_DM_CBACK)(tBTA_JV_EVT event, tBTA_JV *p_data, void *user_data); + +/* JAVA RFCOMM interface callback */ +typedef void *(tBTA_JV_RFCOMM_CBACK)(tBTA_JV_EVT event, tBTA_JV *p_data, void *user_data); + +#if BTA_JV_L2CAP_INCLUDED +/* JAVA L2CAP interface callback */ +typedef void *(tBTA_JV_L2CAP_CBACK)(tBTA_JV_EVT event, tBTA_JV *p_data, void *user_Data); +#endif /* BTA_JV_L2CAP_INCLUDED */ + +/* JV configuration structure */ +typedef struct { + UINT16 sdp_raw_size; /* The size of p_sdp_raw_data */ + UINT16 sdp_db_size; /* The size of p_sdp_db */ + UINT8 *p_sdp_raw_data; /* The data buffer to keep raw data */ + tSDP_DISCOVERY_DB *p_sdp_db; /* The data buffer to keep SDP database */ +} tBTA_JV_CFG; + +/******************************************************************************* +** +** Function BTA_JvEnable +** +** Description Enable the Java I/F service. When the enable +** operation is complete the callback function will be +** called with a BTA_JV_ENABLE_EVT. This function must +** be called before other functions in the JV API are +** called. +** +** Returns BTA_JV_SUCCESS if successful. +** BTA_JV_FAIL if internal failure. +** +*******************************************************************************/ +extern tBTA_JV_STATUS BTA_JvEnable(tBTA_JV_DM_CBACK *p_cback); + +/******************************************************************************* +** +** Function BTA_JvDisable +** +** Description Disable the Java I/F. When the enable +** operation is complete the callback function will be +** called with a BTA_JV_DISABLE_EVT. +** +** Returns void +** +*******************************************************************************/ +extern void BTA_JvDisable(tBTA_JV_RFCOMM_CBACK *p_cback); + +/******************************************************************************* +** +** Function BTA_JvFree +** +** Description Free JV configuration +** +** Returns void +** +*******************************************************************************/ +extern void BTA_JvFree(void); + +/******************************************************************************* +** +** Function BTA_JvIsEnable +** +** Description Get the JV registration status. +** +** Returns TRUE, if registered +** +*******************************************************************************/ +extern BOOLEAN BTA_JvIsEnable(void); + +/******************************************************************************* +** +** Function BTA_JvIsEncrypted +** +** Description This function checks if the link to peer device is encrypted +** +** Returns TRUE if encrypted. +** FALSE if not. +** +*******************************************************************************/ +extern BOOLEAN BTA_JvIsEncrypted(BD_ADDR bd_addr); + +/******************************************************************************* +** +** Function BTA_JvGetChannelId +** +** Description This function reserves a SCN/PSM for applications running +** over RFCOMM or L2CAP. It is primarily called by +** server profiles/applications to register their SCN/PSM into the +** SDP database. The SCN is reported by the tBTA_JV_DM_CBACK +** callback with a BTA_JV_GET_SCN_EVT. +** If the SCN/PSM reported is 0, that means all SCN resources are +** exhausted. +** The channel parameter can be used to request a specific +** channel. If the request on the specific channel fails, the +** SCN/PSM returned in the EVT will be 0 - no attempt to request +** a new channel will be made. set channel to <= 0 to automatically +** assign an channel ID. +** +** Returns BTA_JV_SUCCESS, if the request is being processed. +** BTA_JV_FAILURE, otherwise. +** +*******************************************************************************/ +extern tBTA_JV_STATUS BTA_JvGetChannelId(int conn_type, void *user_data, + INT32 channel); + +/******************************************************************************* +** +** Function BTA_JvFreeChannel +** +** Description This function frees a SCN/PSM that was used +** by an application running over RFCOMM or L2CAP. +** Parameters +** channel The channel to free +** conn_type one of BTA_JV_CONN_TYPE_ +** p_cback tBTA_JV_RFCOMM_CBACK is called with BTA_JV_FREE_SCN_EVT when server frees a SCN/PSM +** user_data indicate the RFCOMM server status +** +** Returns BTA_JV_SUCCESS, if the request is being processed. +** BTA_JV_FAILURE, otherwise. +** +*******************************************************************************/ +extern tBTA_JV_STATUS BTA_JvFreeChannel(UINT16 channel, int conn_type, tBTA_JV_RFCOMM_CBACK *p_cback, void *user_data); + +/******************************************************************************* +** +** Function BTA_JvStartDiscovery +** +** Description This function performs service discovery for the services +** provided by the given peer device. When the operation is +** complete the tBTA_JV_DM_CBACK callback function will be +** called with a BTA_JV_DISCOVERY_COMP_EVT. +** +** Returns BTA_JV_SUCCESS, if the request is being processed. +** BTA_JV_FAILURE, otherwise. +** +*******************************************************************************/ +extern tBTA_JV_STATUS BTA_JvStartDiscovery(BD_ADDR bd_addr, UINT16 num_uuid, + tSDP_UUID *p_uuid_list, void *user_data); + +/******************************************************************************* +** +** Function BTA_JvCreateRecordByUser +** +** Description Create a service record in the local SDP database by user in +** tBTA_JV_DM_CBACK callback with a BTA_JV_CREATE_RECORD_EVT. +** +** Returns BTA_JV_SUCCESS, if the request is being processed. +** BTA_JV_FAILURE, otherwise. +** +*******************************************************************************/ +extern tBTA_JV_STATUS BTA_JvCreateRecordByUser(const char *name, UINT32 channel, void *user_data); + +/******************************************************************************* +** +** Function BTA_JvDeleteRecord +** +** Description Delete a service record in the local SDP database. +** +** Returns BTA_JV_SUCCESS, if the request is being processed. +** BTA_JV_FAILURE, otherwise. +** +*******************************************************************************/ +extern tBTA_JV_STATUS BTA_JvDeleteRecord(UINT32 handle); + +#if BTA_JV_L2CAP_INCLUDED +/******************************************************************************* +** +** Function BTA_JvL2capConnectLE +** +** Description Initiate a connection as an LE L2CAP client to the given BD +** Address. +** When the connection is initiated or failed to initiate, +** tBTA_JV_L2CAP_CBACK is called with BTA_JV_L2CAP_CL_INIT_EVT +** When the connection is established or failed, +** tBTA_JV_L2CAP_CBACK is called with BTA_JV_L2CAP_OPEN_EVT +** +** Returns BTA_JV_SUCCESS, if the request is being processed. +** BTA_JV_FAILURE, otherwise. +** +*******************************************************************************/ +extern tBTA_JV_STATUS BTA_JvL2capConnectLE(tBTA_SEC sec_mask, tBTA_JV_ROLE role, + const tL2CAP_ERTM_INFO *ertm_info, UINT16 remote_chan, + UINT16 rx_mtu, tL2CAP_CFG_INFO *cfg, + BD_ADDR peer_bd_addr, tBTA_JV_L2CAP_CBACK *p_cback, void *user_data); + +/******************************************************************************* +** +** Function BTA_JvL2capConnect +** +** Description Initiate a connection as a L2CAP client to the given BD +** Address. +** When the connection is initiated or failed to initiate, +** tBTA_JV_L2CAP_CBACK is called with BTA_JV_L2CAP_CL_INIT_EVT +** When the connection is established or failed, +** tBTA_JV_L2CAP_CBACK is called with BTA_JV_L2CAP_OPEN_EVT +** +** Returns BTA_JV_SUCCESS, if the request is being processed. +** BTA_JV_FAILURE, otherwise. +** +*******************************************************************************/ +extern tBTA_JV_STATUS BTA_JvL2capConnect(tBTA_SEC sec_mask, tBTA_JV_ROLE role, + const tL2CAP_ERTM_INFO *ertm_info, UINT16 remote_psm, + UINT16 rx_mtu, tL2CAP_CFG_INFO *cfg, + BD_ADDR peer_bd_addr, tBTA_JV_L2CAP_CBACK *p_cback, void *user_data); + +/******************************************************************************* +** +** Function BTA_JvL2capClose +** +** Description This function closes an L2CAP client connection +** +** Returns BTA_JV_SUCCESS, if the request is being processed. +** BTA_JV_FAILURE, otherwise. +** +*******************************************************************************/ +extern tBTA_JV_STATUS BTA_JvL2capClose(UINT32 handle, tBTA_JV_L2CAP_CBACK *p_cback, void *user_data); + +/******************************************************************************* +** +** Function BTA_JvL2capCloseLE +** +** Description This function closes an L2CAP client connection for Fixed Channels +** Function is idempotent and no callbacks are called! +** +** Returns BTA_JV_SUCCESS, if the request is being processed. +** BTA_JV_FAILURE, otherwise. +** +*******************************************************************************/ +extern tBTA_JV_STATUS BTA_JvL2capCloseLE(UINT32 handle); + +/******************************************************************************* +** +** Function BTA_JvL2capStartServer +** +** Description This function starts an L2CAP server and listens for an L2CAP +** connection from a remote Bluetooth device. When the server +** is started successfully, tBTA_JV_L2CAP_CBACK is called with +** BTA_JV_L2CAP_START_EVT. When the connection is established, +** tBTA_JV_L2CAP_CBACK is called with BTA_JV_L2CAP_OPEN_EVT. +** +** Returns BTA_JV_SUCCESS, if the request is being processed. +** BTA_JV_FAILURE, otherwise. +** +*******************************************************************************/ +extern tBTA_JV_STATUS BTA_JvL2capStartServer(tBTA_SEC sec_mask, tBTA_JV_ROLE role, + const tL2CAP_ERTM_INFO *ertm_info, + UINT16 local_psm, UINT16 rx_mtu, tL2CAP_CFG_INFO *cfg, + tBTA_JV_L2CAP_CBACK *p_cback, void *user_data); + +/******************************************************************************* +** +** Function BTA_JvL2capStartServerLE +** +** Description This function starts an LE L2CAP server and listens for an L2CAP +** connection from a remote Bluetooth device on a fixed channel +** over an LE link. When the server +** is started successfully, tBTA_JV_L2CAP_CBACK is called with +** BTA_JV_L2CAP_START_EVT. When the connection is established, +** tBTA_JV_L2CAP_CBACK is called with BTA_JV_L2CAP_OPEN_EVT. +** +** Returns BTA_JV_SUCCESS, if the request is being processed. +** BTA_JV_FAILURE, otherwise. +** +*******************************************************************************/ +extern tBTA_JV_STATUS BTA_JvL2capStartServerLE(tBTA_SEC sec_mask, tBTA_JV_ROLE role, + const tL2CAP_ERTM_INFO *ertm_info, + UINT16 local_chan, UINT16 rx_mtu, tL2CAP_CFG_INFO *cfg, + tBTA_JV_L2CAP_CBACK *p_cback, void *user_data); + +/******************************************************************************* +** +** Function BTA_JvL2capStopServerLE +** +** Description This function stops the LE L2CAP server. If the server has an +** active connection, it would be closed. +** +** Returns BTA_JV_SUCCESS, if the request is being processed. +** BTA_JV_FAILURE, otherwise. +** +*******************************************************************************/ +extern tBTA_JV_STATUS BTA_JvL2capStopServerLE(UINT16 local_chan, void *user_data); + +/******************************************************************************* +** +** Function BTA_JvL2capStopServerLE +** +** Description This function stops the LE L2CAP server. If the server has an +** active connection, it would be closed. +** +** Returns BTA_JV_SUCCESS, if the request is being processed. +** BTA_JV_FAILURE, otherwise. +** +*******************************************************************************/ +extern tBTA_JV_STATUS BTA_JvL2capStopServer(UINT16 local_psm, void *user_data); + +/******************************************************************************* +** +** Function BTA_JvL2capRead +** +** Description This function reads data from an L2CAP connection +** When the operation is complete, tBTA_JV_L2CAP_CBACK is +** called with BTA_JV_L2CAP_READ_EVT. +** +** Returns Length of read data. +** +*******************************************************************************/ +extern int BTA_JvL2capRead(UINT32 handle, UINT32 req_id, UINT8 *p_data, UINT16 len); + +/******************************************************************************* +** +** Function BTA_JvL2capReceive +** +** Description This function reads data from an L2CAP connection +** When the operation is complete, tBTA_JV_L2CAP_CBACK is +** called with BTA_JV_L2CAP_RECEIVE_EVT. +** If there are more data queued in L2CAP than len, the extra data will be discarded. +** +** Returns BTA_JV_SUCCESS, if the request is being processed. +** BTA_JV_FAILURE, otherwise. +** +*******************************************************************************/ +extern tBTA_JV_STATUS BTA_JvL2capReceive(UINT32 handle, UINT32 req_id, + UINT8 *p_data, UINT16 len); + +/******************************************************************************* +** +** Function BTA_JvL2capReady +** +** Description This function determined if there is data to read from +** an L2CAP connection +** +** Returns BTA_JV_SUCCESS, if data queue size is in *p_data_size. +** BTA_JV_FAILURE, if error. +** +*******************************************************************************/ +extern tBTA_JV_STATUS BTA_JvL2capReady(UINT32 handle, UINT32 *p_data_size); + +/******************************************************************************* +** +** Function BTA_JvL2capWrite +** +** Description This function writes data to an L2CAP connection +** When the operation is complete, tBTA_JV_L2CAP_CBACK is +** called with BTA_JV_L2CAP_WRITE_EVT. Works for +** PSM-based connections +** +** Returns BTA_JV_SUCCESS, if the request is being processed. +** BTA_JV_FAILURE, otherwise. +** +*******************************************************************************/ +extern tBTA_JV_STATUS BTA_JvL2capWrite(UINT32 handle, UINT32 req_id, + UINT8 *p_data, UINT16 len, void *user_data); + + +/******************************************************************************* +** +** Function BTA_JvL2capWriteFixed +** +** Description This function writes data to an L2CAP connection +** When the operation is complete, tBTA_JV_L2CAP_CBACK is +** called with BTA_JV_L2CAP_WRITE_FIXED_EVT. Works for +** fixed-channel connections +** +** Returns BTA_JV_SUCCESS, if the request is being processed. +** BTA_JV_FAILURE, otherwise. +** +*******************************************************************************/ +extern tBTA_JV_STATUS BTA_JvL2capWriteFixed(UINT16 channel, BD_ADDR *addr, UINT32 req_id, + tBTA_JV_L2CAP_CBACK *p_cback, + UINT8 *p_data, UINT16 len, void *user_data); +#endif /* BTA_JV_L2CAP_INCLUDED */ + +#if BTA_JV_RFCOMM_INCLUDED +/******************************************************************************* +** +** Function BTA_JvRfcommConfig +** +** Description This function is to configure RFCOMM. +** +** +** Returns BTA_JV_SUCCESS, if the request is being processed. +** BTA_JV_FAILURE, otherwise. +** +*******************************************************************************/ +extern tBTA_JV_STATUS BTA_JvRfcommConfig(BOOLEAN enable_l2cap_ertm); + +/******************************************************************************* +** +** Function BTA_JvRfcommConnect +** +** Description This function makes an RFCOMM conection to a remote BD +** Address. +** When the connection is initiated or failed to initiate, +** tBTA_JV_RFCOMM_CBACK is called with BTA_JV_RFCOMM_CL_INIT_EVT +** When the connection is established or failed, +** tBTA_JV_RFCOMM_CBACK is called with BTA_JV_RFCOMM_OPEN_EVT +** +** Returns BTA_JV_SUCCESS, if the request is being processed. +** BTA_JV_FAILURE, otherwise. +** +*******************************************************************************/ +extern tBTA_JV_STATUS BTA_JvRfcommConnect(tBTA_SEC sec_mask, + tBTA_JV_ROLE role, UINT8 remote_scn, BD_ADDR peer_bd_addr, + tBTA_JV_RFCOMM_CBACK *p_cback, void *user_data); + +/******************************************************************************* +** +** Function BTA_JvRfcommClose +** +** Description This function closes an RFCOMM connection +** +** Returns BTA_JV_SUCCESS, if the request is being processed. +** BTA_JV_FAILURE, otherwise. +** +*******************************************************************************/ +extern tBTA_JV_STATUS BTA_JvRfcommClose(UINT32 handle, void *user_data); + +/******************************************************************************* +** +** Function BTA_JvRfcommStartServer +** +** Description This function starts listening for an RFCOMM connection +** request from a remote Bluetooth device. When the server is +** started successfully, tBTA_JV_RFCOMM_CBACK is called +** with BTA_JV_RFCOMM_START_EVT. +** When the connection is established, tBTA_JV_RFCOMM_CBACK +** is called with BTA_JV_RFCOMM_OPEN_EVT. +** +** Returns BTA_JV_SUCCESS, if the request is being processed. +** BTA_JV_FAILURE, otherwise. +** +*******************************************************************************/ +extern tBTA_JV_STATUS BTA_JvRfcommStartServer(tBTA_SEC sec_mask, tBTA_JV_ROLE role, + UINT8 local_scn, UINT8 max_session, + tBTA_JV_RFCOMM_CBACK *p_cback, void *user_data); + +/******************************************************************************* +** +** Function BTA_JvRfcommStopServer +** +** Description This function stops the RFCOMM server. If the server has an +** active connection, it would be closed. +** +** Returns BTA_JV_SUCCESS, if the request is being processed. +** BTA_JV_FAILURE, otherwise. +** +*******************************************************************************/ +extern tBTA_JV_STATUS BTA_JvRfcommStopServer(UINT32 handle, void *user_data); + +/******************************************************************************* +** +** Function BTA_JvRfcommRead +** +** Description This function reads data from an RFCOMM connection +** When the operation is complete, tBTA_JV_RFCOMM_CBACK is +** called with BTA_JV_RFCOMM_READ_EVT. +** +** Returns BTA_JV_SUCCESS, if the request is being processed. +** BTA_JV_FAILURE, otherwise. +** +*******************************************************************************/ +extern tBTA_JV_STATUS BTA_JvRfcommRead(UINT32 handle, UINT32 req_id, UINT8 *p_data, UINT16 len); + +/******************************************************************************* +** +** Function BTA_JvRfcommReady +** +** Description This function determined if there is data to read from +** an RFCOMM connection +** +** Returns BTA_JV_SUCCESS, if data queue size is in *p_data_size. +** BTA_JV_FAILURE, if error. +** +*******************************************************************************/ +extern tBTA_JV_STATUS BTA_JvRfcommReady(UINT32 handle, UINT32 *p_data_size); + +/******************************************************************************* +** +** Function BTA_JvRfcommWrite +** +** Description This function writes data to an RFCOMM connection +** When the operation is complete, tBTA_JV_RFCOMM_CBACK is +** called with BTA_JV_RFCOMM_WRITE_EVT. +** +** Returns BTA_JV_SUCCESS, if the request is being processed. +** BTA_JV_FAILURE, otherwise. +** +*******************************************************************************/ +extern tBTA_JV_STATUS BTA_JvRfcommWrite(UINT32 handle, UINT32 req_id, int len, UINT8 *p_data); + +/******************************************************************************* +** +** Function BTA_JvRfcommFlowControl +** +** Description This function gives the credit to the peer +** +** Returns BTA_JV_SUCCESS, if the request is being processed. +** BTA_JV_FAILURE, otherwise. +** +*******************************************************************************/ +extern tBTA_JV_STATUS BTA_JvRfcommFlowControl(UINT32 handle, UINT16 credits_given); + +/******************************************************************************* +** +** Function BTA_JvRfcommGetPortHdl +** +** Description This function fetches the rfcomm port handle +** +** Returns BTA_JV_SUCCESS, if the request is being processed. +** BTA_JV_FAILURE, otherwise. +** +*******************************************************************************/ +UINT16 BTA_JvRfcommGetPortHdl(UINT32 handle); +#endif /* BTA_JV_RFCOMM_INCLUDED */ + +/******************************************************************************* + ** + ** Function BTA_JVSetPmProfile + ** + ** Description This function set or free power mode profile for different JV application + ** + ** Parameters: handle, JV handle from RFCOMM or L2CAP + ** app_id: app specific pm ID, can be BTA_JV_PM_ALL, see bta_dm_cfg.c for details + ** BTA_JV_PM_ID_CLEAR: removes pm management on the handle. init_st is ignored and + ** BTA_JV_CONN_CLOSE is called implicitely + ** init_st: state after calling this API. typically it should be BTA_JV_CONN_OPEN + ** + ** Returns BTA_JV_SUCCESS, if the request is being processed. + ** BTA_JV_FAILURE, otherwise. + ** + ** NOTE: BTA_JV_PM_ID_CLEAR: In general no need to be called as jv pm calls automatically + ** BTA_JV_CONN_CLOSE to remove in case of connection close! + ** + *******************************************************************************/ +extern tBTA_JV_STATUS BTA_JvSetPmProfile(UINT32 handle, tBTA_JV_PM_ID app_id, tBTA_JV_CONN_STATE init_st); + +#endif ///defined BTA_JV_INCLUDED && BTA_JV_INCLUDED == TRUE +#endif /* BTA_JV_API_H */ diff --git a/lib/bt/host/bluedroid/bta/include/bta/bta_jv_co.h b/lib/bt/host/bluedroid/bta/include/bta/bta_jv_co.h new file mode 100644 index 00000000..b37625cf --- /dev/null +++ b/lib/bt/host/bluedroid/bta/include/bta/bta_jv_co.h @@ -0,0 +1,55 @@ +/****************************************************************************** + * + * Copyright (C) 2007-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This is the interface file for java interface call-out functions. + * + ******************************************************************************/ +#ifndef BTA_JV_CO_H +#define BTA_JV_CO_H + +#include "bta/bta_jv_api.h" + +#if (defined BTA_JV_INCLUDED && BTA_JV_INCLUDED == TRUE) +/***************************************************************************** +** Function Declarations +*****************************************************************************/ + + +/******************************************************************************* +** +** Function bta_jv_co_rfc_data +** +** Description This function is called by JV to send data to the java glue +** code when the RX data path is configured to use a call-out +** +** Returns void +** +*******************************************************************************/ + +extern int bta_co_rfc_data_incoming(void *user_data, BT_HDR *p_buf); +extern int bta_co_rfc_data_outgoing_size(void *user_data, int *size); +extern int bta_co_rfc_data_outgoing(void *user_data, UINT8 *buf, UINT16 size); + +extern int bta_co_l2cap_data_incoming(void *user_data, BT_HDR *p_buf); +extern int bta_co_l2cap_data_outgoing_size(void *user_data, int *size); +extern int bta_co_l2cap_data_outgoing(void *user_data, UINT8 *buf, UINT16 size); + +#endif ///defined BTA_JV_INCLUDED && BTA_JV_INCLUDED == TRUE +#endif /* BTA_DG_CO_H */ diff --git a/lib/bt/host/bluedroid/bta/include/bta/bta_sdp_api.h b/lib/bt/host/bluedroid/bta/include/bta/bta_sdp_api.h new file mode 100644 index 00000000..62b5228a --- /dev/null +++ b/lib/bt/host/bluedroid/bta/include/bta/bta_sdp_api.h @@ -0,0 +1,168 @@ +/****************************************************************************** + * + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This is the public interface file for the BTA SDP I/F + * + ******************************************************************************/ +#ifndef BTA_SDP_API_H +#define BTA_SDP_API_H + +#include "bt_sdp.h" +#include "common/bt_target.h" +#include "stack/bt_types.h" +#include "bta/bta_api.h" +#include "stack/btm_api.h" + +#if (SDP_INCLUDED == TRUE) +/* status values */ +#define BTA_SDP_SUCCESS 0 /* Successful operation. */ +#define BTA_SDP_FAILURE 1 /* Generic failure. */ +#define BTA_SDP_BUSY 2 /* Temporarily can not handle this request. */ + +typedef UINT8 tBTA_SDP_STATUS; + +/* SDP I/F callback events */ +/* events received by tBTA_SDP_DM_CBACK */ +#define BTA_SDP_ENABLE_EVT 0 /* SDP service enabled */ +#define BTA_SDP_DISENABLE_EVT 1 /* SDP service disenabled */ +#define BTA_SDP_SEARCH_EVT 2 /* SDP search started */ +#define BTA_SDP_SEARCH_COMP_EVT 3 /* SDP search complete */ +#define BTA_SDP_CREATE_RECORD_USER_EVT 4 /* SDP create record complete */ +#define BTA_SDP_REMOVE_RECORD_USER_EVT 5 /* SDP remove record complete */ +#define BTA_SDP_MAX_EVT 6 /* max number of SDP events */ + +#define BTA_SDP_MAX_RECORDS 15 + +typedef UINT16 tBTA_SDP_EVT; + +/* data associated with BTA_SDP_DISCOVERY_COMP_EVT */ +typedef struct { + tBTA_SDP_STATUS status; + BD_ADDR remote_addr; + tBT_UUID uuid; + int record_count; + bluetooth_sdp_record records[BTA_SDP_MAX_RECORDS]; +} tBTA_SDP_SEARCH_COMP; + +/* data associated with BTA_SDP_CREATE_RECORD_USER_EVT */ +typedef struct { + tBTA_SDP_STATUS status; + int handle; +} tBTA_SDP_CREATE_RECORD_USER; + +typedef union { + tBTA_SDP_STATUS status; /* BTA_SDP_SEARCH_EVT */ + tBTA_SDP_SEARCH_COMP sdp_search_comp; /* BTA_SDP_SEARCH_COMP_EVT */ + tBTA_SDP_CREATE_RECORD_USER sdp_create_record; /* BTA_SDP_CREATE_RECORD_USER_EVT */ +} tBTA_SDP; + +/* SDP DM Interface callback */ +typedef void (tBTA_SDP_DM_CBACK)(tBTA_SDP_EVT event, tBTA_SDP *p_data, void *user_data); + +/* MCE configuration structure */ +typedef struct { + UINT16 sdp_db_size; /* The size of p_sdp_db */ +#if (SDP_INCLUDED == TRUE) + tSDP_DISCOVERY_DB *p_sdp_db; /* The data buffer to keep SDP database */ +#endif ///SDP_INCLUDED == TRUE +} tBTA_SDP_CFG; + +#ifdef __cplusplus +extern "C" +{ +#endif +/******************************************************************************* +** +** Function BTA_SdpEnable +** +** Description Enable the SDP I/F service. When the enable +** operation is complete the callback function will be +** called with a BTA_SDP_ENABLE_EVT. This function must +** be called before other functions in the MCE API are +** called. +** +** Returns BTA_SDP_SUCCESS if successful. +** BTA_SDP_FAIL if internal failure. +** +*******************************************************************************/ +extern tBTA_SDP_STATUS BTA_SdpEnable(tBTA_SDP_DM_CBACK *p_cback); + +/******************************************************************************* +** +** Function BTA_SdpDisable +** +** Description Disable the SDP search I/F service. +** Free buffer for SDP configuration structure. +** +** Returns BTA_SDP_SUCCESS if successful. +** BTA_SDP_FAIL if internal failure. +** +*******************************************************************************/ +extern tBTA_SDP_STATUS BTA_SdpDisable(void); + +/******************************************************************************* +** +** Function BTA_SdpSearch +** +** Description Start a search for sdp records for a specific BD_ADDR with a +** specific profile uuid. +** When the search operation is completed, the callback function +** will be called with a BTA_SDP_SEARCH_EVT. +** Returns BTA_SDP_SUCCESS if successful. +** BTA_SDP_FAIL if internal failure. +** +*******************************************************************************/ +extern tBTA_SDP_STATUS BTA_SdpSearch(BD_ADDR bd_addr, tSDP_UUID *uuid); + +/******************************************************************************* +** +** Function BTA_SdpCreateRecordByUser +** +** Description This function is used to request a callback to create a SDP +** record. The registered callback will be called with event +** BTA_SDP_CREATE_RECORD_USER_EVT. +** +** Returns BTA_SDP_SUCCESS, if the request is being processed. +** BTA_SDP_FAILURE, otherwise. +** +*******************************************************************************/ +extern tBTA_SDP_STATUS BTA_SdpCreateRecordByUser(void *user_data); + +/******************************************************************************* +** +** Function BTA_SdpRemoveRecordByUser +** +** Description This function is used to request a callback to remove a SDP +** record. The registered callback will be called with event +** BTA_SDP_REMOVE_RECORD_USER_EVT. +** +** Returns BTA_SDP_SUCCESS, if the request is being processed. +** BTA_SDP_FAILURE, otherwise. +** +*******************************************************************************/ +extern tBTA_SDP_STATUS BTA_SdpRemoveRecordByUser(void *user_data); + +#ifdef __cplusplus +} +#endif + +#endif ///SDP_INCLUDED == TRUE + +#endif /* BTA_SDP_API_H */ diff --git a/lib/bt/host/bluedroid/bta/include/bta/bta_sys.h b/lib/bt/host/bluedroid/bta/include/bta/bta_sys.h new file mode 100644 index 00000000..54376de6 --- /dev/null +++ b/lib/bt/host/bluedroid/bta/include/bta/bta_sys.h @@ -0,0 +1,292 @@ +/****************************************************************************** + * + * Copyright (C) 2003-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This is the public interface file for the BTA system manager. + * + ******************************************************************************/ +#ifndef BTA_SYS_H +#define BTA_SYS_H + +#include "common/bt_target.h" +#include "common/bt_defs.h" + +/***************************************************************************** +** Constants and data types +*****************************************************************************/ + +/* vendor specific event handler function type */ +typedef BOOLEAN (tBTA_SYS_VS_EVT_HDLR)(UINT16 evt, void *p); + +/* event handler function type */ +typedef BOOLEAN (tBTA_SYS_EVT_HDLR)(BT_HDR *p_msg); + +/* disable function type */ +typedef void (tBTA_SYS_DISABLE)(void); + + +/* HW modules */ +enum { + BTA_SYS_HW_BLUETOOTH, + BTA_SYS_HW_RT, + + BTA_SYS_MAX_HW_MODULES +}; + +typedef UINT16 tBTA_SYS_HW_MODULE; + +#ifndef BTA_DM_NUM_JV_ID +#define BTA_DM_NUM_JV_ID 2 +#endif + +/* SW sub-systems */ +#define BTA_ID_SYS 0 /* system manager */ +/* BLUETOOTH PART - from 0 to BTA_ID_BLUETOOTH_MAX */ +#define BTA_ID_DM 1 /* device manager */ +#define BTA_ID_DM_SEARCH 2 /* device manager search */ +#define BTA_ID_DM_SEC 3 /* device manager security */ +#define BTA_ID_DG 4 /* data gateway */ +#define BTA_ID_AG 5 /* audio gateway */ +#define BTA_ID_OPC 6 /* object push client */ +#define BTA_ID_OPS 7 /* object push server */ +#define BTA_ID_FTS 8 /* file transfer server */ +#define BTA_ID_CT 9 /* cordless telephony terminal */ +#define BTA_ID_FTC 10 /* file transfer client */ +#define BTA_ID_SS 11 /* synchronization server */ +#define BTA_ID_PR 12 /* Printer client */ +#define BTA_ID_BIC 13 /* Basic Imaging Client */ +#define BTA_ID_PAN 14 /* Personal Area Networking */ +#define BTA_ID_BIS 15 /* Basic Imaging Server */ +#define BTA_ID_ACC 16 /* Advanced Camera Client */ +#define BTA_ID_SC 17 /* SIM Card Access server */ +#define BTA_ID_AV 18 /* Advanced audio/video */ +#define BTA_ID_AVK 19 /* Audio/video sink */ +#define BTA_ID_HD 20 /* HID Device */ +#define BTA_ID_CG 21 /* Cordless Gateway */ +#define BTA_ID_BP 22 /* Basic Printing Client */ +#define BTA_ID_HH 23 /* Human Interface Device Host */ +#define BTA_ID_PBS 24 /* Phone Book Access Server */ +#define BTA_ID_PBC 25 /* Phone Book Access Client */ +#define BTA_ID_JV 26 /* Java */ +#define BTA_ID_HS 27 /* Headset */ +#define BTA_ID_MSE 28 /* Message Server Equipment */ +#define BTA_ID_MCE 29 /* Message Client Equipment */ +#define BTA_ID_HL 30 /* Health Device Profile*/ +#define BTA_ID_GATTC 31 /* GATT Client */ +#define BTA_ID_GATTS 32 /* GATT Client */ +#define BTA_ID_SDP 33 /* SDP Client */ +#define BTA_ID_BLUETOOTH_MAX 34 /* last BT profile */ + +/* GENERIC */ +#define BTA_ID_PRM 38 +#define BTA_ID_SYSTEM 39 /* platform-specific */ +#define BTA_ID_SWRAP 40 /* Insight script wrapper */ +#define BTA_ID_MIP 41 /* Multicase Individual Polling */ +#define BTA_ID_RT 42 /* Audio Routing module: This module is always on. */ + + +/* JV */ +#define BTA_ID_JV1 44 /* JV1 */ +#define BTA_ID_JV2 45 /* JV2 */ + +#define BTA_ID_MAX (44 + BTA_DM_NUM_JV_ID) + +typedef UINT8 tBTA_SYS_ID; + + +#define BTA_SYS_CONN_OPEN 0x00 +#define BTA_SYS_CONN_CLOSE 0x01 +#define BTA_SYS_APP_OPEN 0x02 +#define BTA_SYS_APP_CLOSE 0x03 +#define BTA_SYS_SCO_OPEN 0x04 +#define BTA_SYS_SCO_CLOSE 0x05 +#define BTA_SYS_CONN_IDLE 0x06 +#define BTA_SYS_CONN_BUSY 0x07 + +/* for link policy */ +#define BTA_SYS_PLCY_SET 0x10 /* set the link policy to the given addr */ +#define BTA_SYS_PLCY_CLR 0x11 /* clear the link policy to the given addr */ +#define BTA_SYS_PLCY_DEF_SET 0x12 /* set the default link policy */ +#define BTA_SYS_PLCY_DEF_CLR 0x13 /* clear the default link policy */ +#define BTA_SYS_ROLE_CHANGE 0x14 /* role change */ + +typedef UINT8 tBTA_SYS_CONN_STATUS; + +/* Bitmask of sys features */ +#define BTA_SYS_FEAT_PCM2 0x0001 +#define BTA_SYS_FEAT_PCM2_MASTER 0x0002 + +/* tBTA_PREF_ROLES */ +typedef UINT8 tBTA_SYS_PREF_ROLES; + +/* conn callback for role / low power manager*/ +typedef void (tBTA_SYS_CONN_CBACK)(tBTA_SYS_CONN_STATUS status, UINT8 id, UINT8 app_id, BD_ADDR peer_addr); + +/* conn callback for role / low power manager*/ +typedef void (tBTA_SYS_SSR_CFG_CBACK)(UINT8 id, UINT8 app_id, UINT16 latency, UINT16 tout); + +#if (BTA_EIR_CANNED_UUID_LIST != TRUE) +/* eir callback for adding/removeing UUID */ +typedef void (tBTA_SYS_EIR_CBACK)(tBT_UUID uuid, BOOLEAN adding); +#endif + +/* registration structure */ +typedef struct { + tBTA_SYS_EVT_HDLR *evt_hdlr; + tBTA_SYS_DISABLE *disable; +} tBTA_SYS_REG; + +/* data type to send events to BTA SYS HW manager */ +typedef struct { + BT_HDR hdr; + tBTA_SYS_HW_MODULE hw_module; +} tBTA_SYS_HW_MSG; + +/***************************************************************************** +** Global data +*****************************************************************************/ + +/* trace level */ +extern UINT8 appl_trace_level; + +/***************************************************************************** +** Macros +*****************************************************************************/ + +/* Calculate start of event enumeration; id is top 8 bits of event */ +#define BTA_SYS_EVT_START(id) ((id) << 8) + +/***************************************************************************** +** events for BTA SYS HW manager +*****************************************************************************/ + +/* events sent to SYS HW manager - must be kept synchronized with tables in bta_sys_main.c */ +enum { + /* device manager local device API events */ + BTA_SYS_API_ENABLE_EVT = BTA_SYS_EVT_START(BTA_ID_SYS), + BTA_SYS_EVT_ENABLED_EVT, + BTA_SYS_EVT_STACK_ENABLED_EVT, + BTA_SYS_API_DISABLE_EVT, + BTA_SYS_EVT_DISABLED_EVT, + BTA_SYS_ERROR_EVT, + + BTA_SYS_MAX_EVT +}; + + + +/* SYS HW status events - returned by SYS HW manager to other modules. */ +enum { + BTA_SYS_HW_OFF_EVT, + BTA_SYS_HW_ON_EVT, + BTA_SYS_HW_STARTING_EVT, + BTA_SYS_HW_STOPPING_EVT, + BTA_SYS_HW_ERROR_EVT + +}; +typedef UINT8 tBTA_SYS_HW_EVT; + +/* HW enable callback type */ +typedef void (tBTA_SYS_HW_CBACK)(tBTA_SYS_HW_EVT status); + +/***************************************************************************** +** Function declarations +*****************************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +extern void bta_sys_init(void); +extern void bta_sys_free(void); +extern void bta_sys_event(void * param); +extern void bta_sys_set_trace_level(UINT8 level); +extern void bta_sys_register(UINT8 id, const tBTA_SYS_REG *p_reg); +extern void bta_sys_deregister(UINT8 id); +extern BOOLEAN bta_sys_is_register(UINT8 id); +extern UINT16 bta_sys_get_sys_features(void); +extern void bta_sys_sendmsg(void *p_msg); +extern void bta_sys_start_timer(TIMER_LIST_ENT *p_tle, UINT16 type, INT32 timeout_ms); +extern void bta_sys_stop_timer(TIMER_LIST_ENT *p_tle); +extern void bta_sys_free_timer(TIMER_LIST_ENT *p_tle); +extern BOOLEAN bta_sys_timer_is_active(TIMER_LIST_ENT *p_tle); +extern void bta_sys_disable(tBTA_SYS_HW_MODULE module); +extern UINT32 bta_sys_get_remaining_ticks(TIMER_LIST_ENT *p_target_tle); + +extern void bta_sys_hw_register( tBTA_SYS_HW_MODULE module, tBTA_SYS_HW_CBACK *cback); +extern void bta_sys_hw_unregister( tBTA_SYS_HW_MODULE module ); + + +extern void bta_sys_rm_register(tBTA_SYS_CONN_CBACK *p_cback); +extern void bta_sys_pm_register(tBTA_SYS_CONN_CBACK *p_cback); + +extern void bta_sys_policy_register(tBTA_SYS_CONN_CBACK *p_cback); +extern void bta_sys_sco_register(tBTA_SYS_CONN_CBACK *p_cback); + + +extern void bta_sys_conn_open(UINT8 id, UINT8 app_id, BD_ADDR peer_addr); +extern void bta_sys_conn_close(UINT8 id, UINT8 app_id, BD_ADDR peer_addr); +extern void bta_sys_app_open(UINT8 id, UINT8 app_id, BD_ADDR peer_addr); +extern void bta_sys_app_close(UINT8 id, UINT8 app_id, BD_ADDR peer_addr); +extern void bta_sys_sco_open(UINT8 id, UINT8 app_id, BD_ADDR peer_addr); +extern void bta_sys_sco_close(UINT8 id, UINT8 app_id, BD_ADDR peer_addr); +extern void bta_sys_sco_use(UINT8 id, UINT8 app_id, BD_ADDR peer_addr); +extern void bta_sys_sco_unuse(UINT8 id, UINT8 app_id, BD_ADDR peer_addr); +extern void bta_sys_idle(UINT8 id, UINT8 app_id, BD_ADDR peer_addr); +extern void bta_sys_busy(UINT8 id, UINT8 app_id, BD_ADDR peer_addr); + +#if (BTM_SSR_INCLUDED == TRUE) +extern void bta_sys_ssr_cfg_register(tBTA_SYS_SSR_CFG_CBACK *p_cback); +extern void bta_sys_chg_ssr_config (UINT8 id, UINT8 app_id, UINT16 max_latency, UINT16 min_tout); +#endif + +extern void bta_sys_role_chg_register(tBTA_SYS_CONN_CBACK *p_cback); +extern void bta_sys_notify_role_chg(BD_ADDR_PTR p_bda, UINT8 new_role, UINT8 hci_status); +extern void bta_sys_collision_register(UINT8 bta_id, tBTA_SYS_CONN_CBACK *p_cback); +extern void bta_sys_notify_collision (BD_ADDR_PTR p_bda); + +#if (BTA_EIR_CANNED_UUID_LIST != TRUE) +extern void bta_sys_eir_register(tBTA_SYS_EIR_CBACK *p_cback); +extern void bta_sys_add_uuid(UINT16 uuid); +extern void bta_sys_add_uuid_32(UINT32 uuid32); +extern void bta_sys_add_uuid_128(UINT8 *uuid128); +extern void bta_sys_remove_uuid(UINT16 uuid16); +extern void bta_sys_remove_uuid_32(UINT32 uuid32); +extern void bta_sys_remove_uuid_128(UINT8 *uuid128); +#else +#define bta_sys_eir_register(ut) +#define bta_sys_add_uuid(ut) +#define bta_sys_add_uuid_32(ut) +#define bta_sys_add_uuid_128(ut) +#define bta_sys_remove_uuid(ut) +#define bta_sys_remove_uuid_32(ut) +#define bta_sys_remove_uuid_128(ut) +#endif + +extern void bta_sys_set_policy (UINT8 id, UINT8 policy, BD_ADDR peer_addr); +extern void bta_sys_clear_policy (UINT8 id, UINT8 policy, BD_ADDR peer_addr); +extern void bta_sys_set_default_policy (UINT8 id, UINT8 policy); +extern void bta_sys_clear_default_policy (UINT8 id, UINT8 policy); +extern BOOLEAN bta_sys_vs_hdl(UINT16 evt, void *p); + +#ifdef __cplusplus +} +#endif + +#endif /* BTA_SYS_H */ diff --git a/lib/bt/host/bluedroid/bta/include/bta/utl.h b/lib/bt/host/bluedroid/bta/include/bta/utl.h new file mode 100644 index 00000000..a140832f --- /dev/null +++ b/lib/bt/host/bluedroid/bta/include/bta/utl.h @@ -0,0 +1,184 @@ +/****************************************************************************** + * + * Copyright (C) 2003-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * Basic utility functions. + * + ******************************************************************************/ +#ifndef UTL_H +#define UTL_H + +#include "stack/bt_types.h" +// #include "bt_utils.h" + +/***************************************************************************** +** Constants +*****************************************************************************/ +/*** class of device settings ***/ +#define BTA_UTL_SET_COD_MAJOR_MINOR 0x01 +#define BTA_UTL_SET_COD_SERVICE_CLASS 0x02 /* only set the bits in the input */ +#define BTA_UTL_CLR_COD_SERVICE_CLASS 0x04 +#define BTA_UTL_SET_COD_ALL 0x08 /* take service class as the input (may clear some set bits!!) */ +#define BTA_UTL_INIT_COD 0x0a + +/***************************************************************************** +** Type Definitions +*****************************************************************************/ + +/** for utl_set_device_class() **/ +typedef struct { + UINT8 minor; + UINT8 major; + UINT16 service; +} tBTA_UTL_COD; + + +#ifdef __cplusplus +extern "C" +{ +#endif + +/***************************************************************************** +** External Function Declarations +*****************************************************************************/ + +/******************************************************************************* +** +** Function utl_str2int +** +** Description This utility function converts a character string to an +** integer. Acceptable values in string are 0-9. If invalid +** string or string value too large, -1 is returned. +** +** +** Returns Integer value or -1 on error. +** +*******************************************************************************/ +extern INT16 utl_str2int(const char *p_s); + +/******************************************************************************* +** +** Function utl_strucmp +** +** Description This utility function compares two strings in uppercase. +** String p_s must be uppercase. String p_t is converted to +** uppercase if lowercase. If p_s ends first, the substring +** match is counted as a match. +** +** +** Returns 0 if strings match, nonzero otherwise. +** +*******************************************************************************/ +extern int utl_strucmp(const char *p_s, const char *p_t); + +/******************************************************************************* +** +** Function utl_itoa +** +** Description This utility function converts a UINT16 to a string. The +** string is NULL-terminated. The length of the string is +** returned. +** +** +** Returns Length of string. +** +*******************************************************************************/ +extern UINT8 utl_itoa(UINT16 i, char *p_s); + +/******************************************************************************* +** +** Function utl_freebuf +** +** Description This function calls osi_free to free the buffer passed +** in, if buffer pointer is not NULL, and also initializes +** buffer pointer to NULL. +** +** +** Returns Nothing. +** +*******************************************************************************/ +extern void utl_freebuf(void **p); + +/******************************************************************************* +** +** Function utl_set_device_class +** +** Description This function updates the local Device Class. +** +** Parameters: +** p_cod - Pointer to the device class to set to +** +** cmd - the fields of the device class to update. +** BTA_UTL_SET_COD_MAJOR_MINOR, - overwrite major, minor class +** BTA_UTL_SET_COD_SERVICE_CLASS - set the bits in the input +** BTA_UTL_CLR_COD_SERVICE_CLASS - clear the bits in the input +** BTA_UTL_SET_COD_ALL - overwrite major, minor, set the bits in service class +** BTA_UTL_INIT_COD - overwrite major, minor, and service class +** +** Returns TRUE if successful, Otherwise FALSE +** +*******************************************************************************/ +extern BOOLEAN utl_set_device_class(tBTA_UTL_COD *p_cod, UINT8 cmd); + +/******************************************************************************* +** +** Function utl_get_device_class +** +** Description This function get the local Device Class. +** +** Parameters: +** p_cod - Pointer to the device class to get to +** +** +** Returns TRUE if successful, Otherwise FALSE +** +*******************************************************************************/ +extern BOOLEAN utl_get_device_class(tBTA_UTL_COD *p_cod); + +/******************************************************************************* +** +** Function utl_isintstr +** +** Description This utility function checks if the given string is an +** integer string or not +** +** +** Returns TRUE if successful, Otherwise FALSE +** +*******************************************************************************/ +extern BOOLEAN utl_isintstr(const char *p_s); + +/******************************************************************************* +** +** Function utl_isdialstr +** +** Description This utility function checks if the given string contains +** only dial digits or not +** +** +** Returns TRUE if successful, Otherwise FALSE +** +*******************************************************************************/ +extern BOOLEAN utl_isdialstr(const char *p_s); + +#ifdef __cplusplus +} +#endif + +#endif /* UTL_H */ diff --git a/lib/bt/host/bluedroid/bta/jv/bta_jv_act.c b/lib/bt/host/bluedroid/bta/jv/bta_jv_act.c new file mode 100644 index 00000000..214ef746 --- /dev/null +++ b/lib/bt/host/bluedroid/bta/jv/bta_jv_act.c @@ -0,0 +1,3049 @@ +/****************************************************************************** + * + * Copyright (C) 2006-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains action functions for BTA JV APIs. + * + ******************************************************************************/ + +#include +#include + +#include "osi/allocator.h" +#include "osi/osi.h" +#include "stack/bt_types.h" +#include "bta/utl.h" +#include "bta/bta_sys.h" +#include "bta/bta_api.h" +#include "bta/bta_jv_api.h" +#include "bta_jv_int.h" +#include "bta/bta_jv_co.h" +#include "stack/btm_api.h" +#include "btm_int.h" +#include "stack/sdp_api.h" +#include "stack/l2c_api.h" +#include "stack/port_api.h" +#include +#include "stack/rfcdefs.h" +#include "stack/avct_api.h" +#include "stack/avdt_api.h" +#include "stack/gap_api.h" +#include "stack/l2c_api.h" + + +#if (defined BTA_JV_INCLUDED && BTA_JV_INCLUDED == TRUE) +#if BTA_JV_L2CAP_INCLUDED +/* one of these exists for each client */ +struct fc_client { + struct fc_client *next_all_list; + struct fc_client *next_chan_list; + BD_ADDR remote_addr; + uint32_t id; + tBTA_JV_L2CAP_CBACK *p_cback; + void *user_data; + uint16_t handle; + uint16_t chan; + uint8_t sec_id; + unsigned server : 1; + unsigned init_called : 1; +}; + +/* one of these exists for each channel we're dealing with */ +struct fc_channel { + struct fc_channel *next; + struct fc_client *clients; + uint8_t has_server : 1; + uint16_t chan; +}; + + +static struct fc_client *fc_clients; +static struct fc_channel *fc_channels; +static uint32_t fc_next_id; +static pthread_once_t fc_init_once = PTHREAD_ONCE_INIT; + + +static void fc_init_work(void) +{ + fc_clients = NULL; + fc_channels = NULL; + fc_next_id = 0; + + //more init here if needed... +} + +static void __attribute__((unused)) fc_init(void) +{ + pthread_once(&fc_init_once, fc_init_work); +} + +static void fcchan_conn_chng_cbk(UINT16 chan, BD_ADDR bd_addr, BOOLEAN connected, + UINT16 reason, tBT_TRANSPORT ); +static void fcchan_data_cbk(UINT16 chan, BD_ADDR bd_addr, BT_HDR *p_buf); +#endif /* BTA_JV_L2CAP_INCLUDED */ + +extern void uuid_to_string_legacy(bt_uuid_t *p_uuid, char *str); +static inline void logu(const char *title, const uint8_t *p_uuid) +{ + char uuids[128]; + uuid_to_string_legacy((bt_uuid_t *)p_uuid, uuids); + APPL_TRACE_DEBUG("%s: %s", title, uuids); +} + + +static tBTA_JV_STATUS bta_jv_free_set_pm_profile_cb(UINT32 jv_handle); +static void bta_jv_pm_conn_busy(tBTA_JV_PM_CB *p_cb); +static void bta_jv_pm_conn_idle(tBTA_JV_PM_CB *p_cb); +static void bta_jv_pm_state_change(tBTA_JV_PM_CB *p_cb, const tBTA_JV_CONN_STATE state); +tBTA_JV_STATUS bta_jv_set_pm_conn_state(tBTA_JV_PM_CB *p_cb, const tBTA_JV_CONN_STATE new_st); + +#if BT_SDP_BQB_INCLUDED +static BOOLEAN s_sdp_bqb_add_language_attr_flag = FALSE; +#endif /* BT_SDP_BQB_INCLUDED */ + +#if BTA_JV_RFCOMM_INCLUDED +static tBTA_JV_PCB *bta_jv_add_rfc_port(tBTA_JV_RFC_CB *p_cb, tBTA_JV_PCB *p_pcb_open); +static int find_rfc_pcb(void *user_data, tBTA_JV_RFC_CB **cb, tBTA_JV_PCB **pcb); +static void bta_jv_port_mgmt_sr_cback(UINT32 code, UINT16 port_handle, void* data); +static void bta_jv_port_event_sr_cback(UINT32 code, UINT16 port_handle); +static int bta_jv_port_data_co_cback(UINT16 port_handle, UINT8 *buf, UINT16 len, int type); +#endif /* BTA_JV_RFCOMM_INCLUDED */ +/******************************************************************************* +** +** Function bta_jv_alloc_sec_id +** +** Description allocate a security id +** +** Returns +** +*******************************************************************************/ +UINT8 bta_jv_alloc_sec_id(void) +{ + UINT8 ret = 0; + int i; + for (i = 0; i < BTA_JV_NUM_SERVICE_ID; i++) { + if (0 == bta_jv_cb.sec_id[i]) { + bta_jv_cb.sec_id[i] = BTA_JV_FIRST_SERVICE_ID + i; + ret = bta_jv_cb.sec_id[i]; + break; + } + } + return ret; + +} +UNUSED_ATTR static int get_sec_id_used(void) +{ + int i; + int used = 0; + for (i = 0; i < BTA_JV_NUM_SERVICE_ID; i++) { + if (bta_jv_cb.sec_id[i]) { + used++; + } + } + if (used == BTA_JV_NUM_SERVICE_ID) { + APPL_TRACE_ERROR("get_sec_id_used, sec id exceeds the limit:%d", + BTA_JV_NUM_SERVICE_ID); + } + return used; +} +#if BTA_JV_RFCOMM_INCLUDED +UNUSED_ATTR static int get_rfc_cb_used(void) +{ + int i; + int used = 0; + for (i = 0; i < BTA_JV_MAX_RFC_CONN; i++) { + if (bta_jv_cb.rfc_cb[i].handle ) { + used++; + } + } + if (used == BTA_JV_MAX_RFC_CONN) { + APPL_TRACE_ERROR("get_sec_id_used, rfc ctrl block exceeds the limit:%d", + BTA_JV_MAX_RFC_CONN); + } + return used; +} +#endif /* BTA_JV_RFCOMM_INCLUDED */ + +/******************************************************************************* +** +** Function bta_jv_free_sec_id +** +** Description free the given security id +** +** Returns +** +*******************************************************************************/ +static void bta_jv_free_sec_id(UINT8 *p_sec_id) +{ + UINT8 sec_id = *p_sec_id; + *p_sec_id = 0; + if (sec_id >= BTA_JV_FIRST_SERVICE_ID && sec_id <= BTA_JV_LAST_SERVICE_ID) { + BTM_SecClrService(sec_id); + bta_jv_cb.sec_id[sec_id - BTA_JV_FIRST_SERVICE_ID] = 0; + } +} + +#if BTA_JV_RFCOMM_INCLUDED +/******************************************************************************* +** +** Function bta_jv_alloc_rfc_cb +** +** Description allocate a control block for the given port handle +** +** Returns +** +*******************************************************************************/ +tBTA_JV_RFC_CB *bta_jv_alloc_rfc_cb(UINT16 port_handle, tBTA_JV_PCB **pp_pcb) +{ + tBTA_JV_RFC_CB *p_cb = NULL; + tBTA_JV_PCB *p_pcb; + int i, j; + for (i = 0; i < BTA_JV_MAX_RFC_CONN; i++) { + if (0 == bta_jv_cb.rfc_cb[i].handle ) { + p_cb = &bta_jv_cb.rfc_cb[i]; + /* mask handle to distinguish it with L2CAP handle */ + p_cb->handle = (i + 1) | BTA_JV_RFCOMM_MASK; + + p_cb->max_sess = 1; + p_cb->curr_sess = 1; + for (j = 0; j < BTA_JV_MAX_RFC_SR_SESSION; j++) { + p_cb->rfc_hdl[j] = 0; + } + p_cb->rfc_hdl[0] = port_handle; + APPL_TRACE_DEBUG( "bta_jv_alloc_rfc_cb port_handle:%d handle:0x%2x", + port_handle, p_cb->handle); + + p_pcb = &bta_jv_cb.port_cb[port_handle - 1]; + p_pcb->handle = p_cb->handle; + p_pcb->port_handle = port_handle; + p_pcb->p_pm_cb = NULL; + *pp_pcb = p_pcb; + break; + } + } + if (p_cb == NULL) { + APPL_TRACE_ERROR( "bta_jv_alloc_rfc_cb: port_handle:%d, ctrl block exceeds " + "limit:%d", port_handle, BTA_JV_MAX_RFC_CONN); + } + return p_cb; +} + +/******************************************************************************* +** +** Function bta_jv_rfc_port_to_pcb +** +** Description find the port control block associated with the given port handle +** +** Returns +** +*******************************************************************************/ +tBTA_JV_PCB *bta_jv_rfc_port_to_pcb(UINT16 port_handle) +{ + tBTA_JV_PCB *p_pcb = NULL; + + if ((port_handle > 0) && (port_handle <= MAX_RFC_PORTS) + && bta_jv_cb.port_cb[port_handle - 1].handle) { + p_pcb = &bta_jv_cb.port_cb[port_handle - 1]; + } + + return p_pcb; +} + +/******************************************************************************* +** +** Function bta_jv_rfc_port_to_cb +** +** Description find the RFCOMM control block associated with the given port handle +** +** Returns +** +*******************************************************************************/ +tBTA_JV_RFC_CB *bta_jv_rfc_port_to_cb(UINT16 port_handle) +{ + tBTA_JV_RFC_CB *p_cb = NULL; + UINT32 handle; + + if ((port_handle > 0) && (port_handle <= MAX_RFC_PORTS) + && bta_jv_cb.port_cb[port_handle - 1].handle) { + handle = bta_jv_cb.port_cb[port_handle - 1].handle; + handle &= BTA_JV_RFC_HDL_MASK; + handle &= ~BTA_JV_RFCOMM_MASK; + if (handle) { + p_cb = &bta_jv_cb.rfc_cb[handle - 1]; + } + } else { + APPL_TRACE_WARNING("bta_jv_rfc_port_to_cb(port_handle:0x%x):jv handle:0x%x not" + " FOUND", port_handle, bta_jv_cb.port_cb[port_handle - 1].handle); + } + return p_cb; +} + +static tBTA_JV_STATUS bta_jv_free_rfc_cb(tBTA_JV_RFC_CB *p_cb, tBTA_JV_PCB *p_pcb, BOOLEAN close_server, BOOLEAN close_pending) +{ + tBTA_JV_STATUS status = BTA_JV_SUCCESS; + BOOLEAN remove_server = FALSE; + + UINT8 used = 0, i, listen = 0; + tPORT_STATE port_state; + UINT32 event_mask = BTA_JV_RFC_EV_MASK; + UINT32 scn_num = (UINT32)p_cb->scn; + tBTA_JV evt_data = {0}; + + if (!p_cb || !p_pcb) { + APPL_TRACE_ERROR("bta_jv_free_sr_rfc_cb, p_cb or p_pcb cannot be null"); + return BTA_JV_FAILURE; + } + APPL_TRACE_DEBUG("bta_jv_free_sr_rfc_cb: max_sess:%d, curr_sess:%d, p_pcb:%p, user:" + "%p, state:%d, jv handle: 0x%x" , p_cb->max_sess, p_cb->curr_sess, p_pcb, + p_pcb->user_data, p_pcb->state, p_pcb->handle); + + if (p_cb->curr_sess <= 0) { + return BTA_JV_SUCCESS; + } + + switch (p_pcb->state) { + case BTA_JV_ST_CL_CLOSING: + case BTA_JV_ST_SR_CLOSING: + APPL_TRACE_DEBUG("bta_jv_free_sr_rfc_cb: return on closing, port state:%d, " + "scn:%d, p_pcb:%p, user_data:%p", p_pcb->state, p_cb->scn, p_pcb, + p_pcb->user_data); + status = BTA_JV_FAILURE; + break; + case BTA_JV_ST_CL_OPEN: + case BTA_JV_ST_CL_OPENING: + APPL_TRACE_DEBUG("bta_jv_free_sr_rfc_cb: state: %d, scn:%d," + " user_data:%p", p_pcb->state, p_cb->scn, p_pcb->user_data); + p_pcb->state = BTA_JV_ST_CL_CLOSING; + break; + case BTA_JV_ST_SR_LISTEN: + p_pcb->state = BTA_JV_ST_SR_CLOSING; + remove_server = TRUE; + APPL_TRACE_DEBUG("bta_jv_free_sr_rfc_cb: state: BTA_JV_ST_SR_LISTEN, scn:%d," + " user_data:%p", p_cb->scn, p_pcb->user_data); + break; + case BTA_JV_ST_SR_OPEN: + p_pcb->state = BTA_JV_ST_SR_CLOSING; + APPL_TRACE_DEBUG("bta_jv_free_sr_rfc_cb: state: BTA_JV_ST_SR_OPEN, scn:%d," + " user_data:%p", p_cb->scn, p_pcb->user_data); + break; + default: + APPL_TRACE_WARNING("bta_jv_free_sr_rfc_cb():failed, ignore port state:%d, scn:" + "%d, p_pcb:%p, jv handle: 0x%x, port_handle: %d, user_data:%p", + p_pcb->state, p_cb->scn, p_pcb, p_pcb->handle, p_pcb->port_handle, + p_pcb->user_data); + status = BTA_JV_FAILURE; + break; + } + if (BTA_JV_SUCCESS == status) { + int port_status; + + if (!remove_server) { + port_status = RFCOMM_RemoveConnection(p_pcb->port_handle); + } else { + port_status = RFCOMM_RemoveServer(p_pcb->port_handle); + } + if (port_status != PORT_SUCCESS) { + status = BTA_JV_FAILURE; + APPL_TRACE_WARNING("bta_jv_free_rfc_cb(jv handle: 0x%x, state %d)::" + "port_status: %d, port_handle: %d, close_pending: %d:Remove", + p_pcb->handle, p_pcb->state, port_status, p_pcb->port_handle, + close_pending); + } + } + if (!close_pending) { + p_pcb->port_handle = 0; + p_pcb->state = BTA_JV_ST_NONE; + bta_jv_free_set_pm_profile_cb(p_pcb->handle); + + //Initialize congestion flags + p_pcb->cong = FALSE; + p_pcb->user_data = 0; + int si = BTA_JV_RFC_HDL_TO_SIDX(p_pcb->handle); + if (0 <= si && si < BTA_JV_MAX_RFC_SR_SESSION) { + p_cb->rfc_hdl[si] = 0; + } + p_pcb->handle = 0; + p_cb->curr_sess--; + + /* only needs a listening port when has a server */ + if (!close_server && (p_cb->max_sess > 1) && (p_cb->scn != 0)) { + for (i = 0; i < p_cb->max_sess; i++) { + if (p_cb->rfc_hdl[i] != 0) { + p_pcb = &bta_jv_cb.port_cb[p_cb->rfc_hdl[i] - 1]; + if (p_pcb->state == BTA_JV_ST_SR_LISTEN) { + listen++; + } + used++; + } + } + APPL_TRACE_DEBUG("%s max_sess=%d used:%d curr_sess:%d, listen:%d si:%d", __func__, p_cb->max_sess, used, + p_cb->curr_sess, listen, si); + if (used < p_cb->max_sess && + listen == 0 && + 0 <= si && + si < BTA_JV_MAX_RFC_SR_SESSION) { + /* make sure the server has a listen port */ + if ((RFCOMM_CreateConnection(p_cb->sec_id, p_cb->scn, TRUE, + BTA_JV_DEF_RFC_MTU, (UINT8 *)bd_addr_any, &(p_cb->rfc_hdl[si]), bta_jv_port_mgmt_sr_cback) == PORT_SUCCESS) && + (p_cb->rfc_hdl[si] != 0)) { + p_cb->curr_sess++; + p_pcb = &bta_jv_cb.port_cb[p_cb->rfc_hdl[si] - 1]; + p_pcb->state = BTA_JV_ST_SR_LISTEN; + p_pcb->port_handle = p_cb->rfc_hdl[si]; + // p_pcb->user_data = p_pcb_open->user_data; + + PORT_ClearKeepHandleFlag(p_pcb->port_handle); + PORT_SetEventCallback(p_pcb->port_handle, bta_jv_port_event_sr_cback); + PORT_SetDataCOCallback(p_pcb->port_handle, bta_jv_port_data_co_cback); + PORT_SetEventMask(p_pcb->port_handle, event_mask); + PORT_GetState(p_pcb->port_handle, &port_state); + + port_state.fc_type = (PORT_FC_CTS_ON_INPUT | PORT_FC_CTS_ON_OUTPUT); + + PORT_SetState(p_pcb->port_handle, &port_state); + p_pcb->handle = BTA_JV_RFC_H_S_TO_HDL(p_cb->handle, si); + APPL_TRACE_DEBUG("%s: p_pcb->handle:0x%x, curr_sess:%d", __func__, + p_pcb->handle, p_cb->curr_sess); + + evt_data.rfc_srv_open.handle = 0; + evt_data.rfc_srv_open.new_listen_handle = p_pcb->handle; + evt_data.rfc_srv_open.status = BTA_JV_SUCCESS; + p_pcb->user_data = p_cb->p_cback(BTA_JV_RFCOMM_SRV_OPEN_EVT, &evt_data, (void *)scn_num); + } + } + } + + if (p_cb->curr_sess == 0) { + p_cb->scn = 0; + bta_jv_free_sec_id(&p_cb->sec_id); + p_cb->p_cback = NULL; + p_cb->handle = 0; + p_cb->curr_sess = -1; + } + } + return status; +} +#endif /* BTA_JV_RFCOMM_INCLUDED */ + +#if BTA_JV_L2CAP_INCLUDED +/******************************************************************************* +** +** Function bta_jv_free_l2c_cb +** +** Description free the given L2CAP control block +** +** Returns +** +*******************************************************************************/ +tBTA_JV_STATUS bta_jv_free_l2c_cb(tBTA_JV_L2C_CB *p_cb) +{ + tBTA_JV_STATUS status = BTA_JV_SUCCESS; + + if (BTA_JV_ST_NONE != p_cb->state) { + bta_jv_free_set_pm_profile_cb((UINT32)p_cb->handle); + if (GAP_ConnClose(p_cb->handle) != BT_PASS) { + status = BTA_JV_FAILURE; + } + } + p_cb->psm = 0; + p_cb->state = BTA_JV_ST_NONE; + bta_jv_free_sec_id(&p_cb->sec_id); + p_cb->p_cback = NULL; + return status; +} +#endif /* BTA_JV_L2CAP_INCLUDED */ + +/******************************************************************************* +** +** +** Function bta_jv_clear_pm_cb +** +** Description clears jv pm control block and optionally calls bta_sys_conn_close() +** In general close_conn should be set to TRUE to remove registering with +** dm pm! +** +** WARNING: Make sure to clear pointer form port or l2c to this control block too! +** +*******************************************************************************/ +static void bta_jv_clear_pm_cb(tBTA_JV_PM_CB *p_pm_cb, BOOLEAN close_conn) +{ + /* needs to be called if registered with bta pm, otherwise we may run out of dm pm slots! */ + if (close_conn) { + bta_sys_conn_close(BTA_ID_JV, p_pm_cb->app_id, p_pm_cb->peer_bd_addr); + } + p_pm_cb->state = BTA_JV_PM_FREE_ST; + p_pm_cb->app_id = BTA_JV_PM_ALL; + p_pm_cb->handle = BTA_JV_PM_HANDLE_CLEAR; + bdcpy(p_pm_cb->peer_bd_addr, bd_addr_null); +} + +/******************************************************************************* + ** + ** Function bta_jv_free_set_pm_profile_cb + ** + ** Description free pm profile control block + ** + ** Returns BTA_JV_SUCCESS if cb has been freed correctly, + ** BTA_JV_FAILURE in case of no profile has been registered or already freed + ** + *******************************************************************************/ +static tBTA_JV_STATUS bta_jv_free_set_pm_profile_cb(UINT32 jv_handle) +{ + tBTA_JV_STATUS status = BTA_JV_FAILURE; + tBTA_JV_PM_CB **p_cb; + int i, j, bd_counter = 0, appid_counter = 0; + + for (i = 0; i < BTA_JV_PM_MAX_NUM; i++) { + p_cb = NULL; + if ((bta_jv_cb.pm_cb[i].state != BTA_JV_PM_FREE_ST) && + (jv_handle == bta_jv_cb.pm_cb[i].handle)) { + for (j = 0; j < BTA_JV_PM_MAX_NUM; j++) { + if (bdcmp(bta_jv_cb.pm_cb[j].peer_bd_addr, bta_jv_cb.pm_cb[i].peer_bd_addr) == 0) { + bd_counter++; + } + if (bta_jv_cb.pm_cb[j].app_id == bta_jv_cb.pm_cb[i].app_id) { + appid_counter++; + } + } + + APPL_TRACE_API("%s(jv_handle: 0x%2x), idx: %d, app_id: 0x%x", __func__, jv_handle, i, bta_jv_cb.pm_cb[i].app_id); + APPL_TRACE_API("%s, bd_counter = %d, appid_counter = %d", __func__, bd_counter, appid_counter); + if (bd_counter > 1) { + bta_jv_pm_conn_idle(&bta_jv_cb.pm_cb[i]); + } + + if (bd_counter <= 1 || (appid_counter <= 1)) { + bta_jv_clear_pm_cb(&bta_jv_cb.pm_cb[i], TRUE); + } else { + bta_jv_clear_pm_cb(&bta_jv_cb.pm_cb[i], FALSE); + } + + if (BTA_JV_RFCOMM_MASK & jv_handle) { +#if BTA_JV_RFCOMM_INCLUDED + UINT32 hi = ((jv_handle & BTA_JV_RFC_HDL_MASK) & ~BTA_JV_RFCOMM_MASK) - 1; + UINT32 si = BTA_JV_RFC_HDL_TO_SIDX(jv_handle); + if (hi < BTA_JV_MAX_RFC_CONN && bta_jv_cb.rfc_cb[hi].p_cback && si + < BTA_JV_MAX_RFC_SR_SESSION && bta_jv_cb.rfc_cb[hi].rfc_hdl[si]) { + tBTA_JV_PCB *p_pcb = bta_jv_rfc_port_to_pcb(bta_jv_cb.rfc_cb[hi].rfc_hdl[si]); + if (p_pcb) { + if (NULL == p_pcb->p_pm_cb) { + APPL_TRACE_WARNING("%s(jv_handle: 0x%x):port_handle: 0x%x, p_pm_cb: %d: no link to pm_cb?", + __func__, jv_handle, p_pcb->port_handle, i); + } + p_cb = &p_pcb->p_pm_cb; + } + } +#endif /* BTA_JV_RFCOMM_INCLUDED */ + } +#if BTA_JV_L2CAP_INCLUDED + else { + if (jv_handle < BTA_JV_MAX_L2C_CONN) { + tBTA_JV_L2C_CB *p_l2c_cb = &bta_jv_cb.l2c_cb[jv_handle]; + if (NULL == p_l2c_cb->p_pm_cb) { + APPL_TRACE_WARNING("%s(jv_handle: " + "0x%x): p_pm_cb: %d: no link to pm_cb?", __func__, jv_handle, i); + } + p_cb = &p_l2c_cb->p_pm_cb; + } + } +#endif /* BTA_JV_L2CAP_INCLUDED */ + + if (p_cb) { + *p_cb = NULL; + status = BTA_JV_SUCCESS; + } + } + } + return status; +} + +/******************************************************************************* + ** + ** Function bta_jv_alloc_set_pm_profile_cb + ** + ** Description set PM profile control block + ** + ** Returns pointer to allocated cb or NULL in case of failure + ** + *******************************************************************************/ +static tBTA_JV_PM_CB *bta_jv_alloc_set_pm_profile_cb(UINT32 jv_handle, tBTA_JV_PM_ID app_id) +{ + BOOLEAN bRfcHandle = (jv_handle & BTA_JV_RFCOMM_MASK) != 0; + BD_ADDR peer_bd_addr; + int i, j; + tBTA_JV_PM_CB **pp_cb; + + for (i = 0; i < BTA_JV_PM_MAX_NUM; i++) { + pp_cb = NULL; + if (bta_jv_cb.pm_cb[i].state == BTA_JV_PM_FREE_ST) { + /* rfc handle bd addr retrieval requires core stack handle */ + if (bRfcHandle) { +#if BTA_JV_RFCOMM_INCLUDED + // UINT32 hi = ((jv_handle & BTA_JV_RFC_HDL_MASK) & ~BTA_JV_RFCOMM_MASK) - 1; + // UINT32 si = BTA_JV_RFC_HDL_TO_SIDX(jv_handle); + for (j = 0; j < BTA_JV_MAX_RFC_CONN; j++) { + if (jv_handle == bta_jv_cb.port_cb[j].handle) { + pp_cb = &bta_jv_cb.port_cb[j].p_pm_cb; + if (PORT_SUCCESS != + PORT_CheckConnection(bta_jv_cb.port_cb[j].port_handle, FALSE, peer_bd_addr, NULL)) { + i = BTA_JV_PM_MAX_NUM; + } + break; + } + } +#endif /* BTA_JV_RFCOMM_INCLUDED */ + } +#if BTA_JV_L2CAP_INCLUDED + else { + /* use jv handle for l2cap bd address retrieval */ + for (j = 0; j < BTA_JV_MAX_L2C_CONN; j++) { + if (jv_handle == bta_jv_cb.l2c_cb[j].handle) { + pp_cb = &bta_jv_cb.l2c_cb[j].p_pm_cb; + UINT8 *p_bd_addr = GAP_ConnGetRemoteAddr((UINT16)jv_handle); + if (NULL != p_bd_addr) { + bdcpy(peer_bd_addr, p_bd_addr); + } else { + i = BTA_JV_PM_MAX_NUM; + } + break; + } + } + } +#endif /* BTA_JV_L2CAP_INCLUDED */ + APPL_TRACE_API("bta_jv_alloc_set_pm_profile_cb(handle 0x%2x, app_id %d): " + "idx: %d, (BTA_JV_PM_MAX_NUM: %d), pp_cb: %p", jv_handle, app_id, + i, BTA_JV_PM_MAX_NUM, (void *)pp_cb); + break; + } + } + + if ((i != BTA_JV_PM_MAX_NUM) && (NULL != pp_cb)) { + *pp_cb = &bta_jv_cb.pm_cb[i]; + bta_jv_cb.pm_cb[i].handle = jv_handle; + bta_jv_cb.pm_cb[i].app_id = app_id; + bdcpy(bta_jv_cb.pm_cb[i].peer_bd_addr, peer_bd_addr); + bta_jv_cb.pm_cb[i].state = BTA_JV_PM_IDLE_ST; + return &bta_jv_cb.pm_cb[i]; + } + APPL_TRACE_WARNING("bta_jv_alloc_set_pm_profile_cb(jv_handle: 0x%x, app_id: %d) " + "return NULL", jv_handle, app_id); + return (tBTA_JV_PM_CB *)NULL; +} + +/******************************************************************************* +** +** Function bta_jv_check_psm +** +** Description for now use only the legal PSM per JSR82 spec +** +** Returns TRUE, if allowed +** +*******************************************************************************/ +BOOLEAN bta_jv_check_psm(UINT16 psm) +{ + BOOLEAN ret = FALSE; + + if (L2C_IS_VALID_PSM(psm)) { + if (psm < 0x1001) { + /* see if this is defined by spec */ + switch (psm) { + case SDP_PSM: /* 1 */ + case BT_PSM_RFCOMM: /* 3 */ + /* do not allow java app to use these 2 PSMs */ + break; + + case TCS_PSM_INTERCOM: /* 5 */ + case TCS_PSM_CORDLESS: /* 7 */ + if ( FALSE == bta_sys_is_register(BTA_ID_CT) && + FALSE == bta_sys_is_register(BTA_ID_CG) ) { + ret = TRUE; + } + break; + + case BT_PSM_BNEP: /* F */ + if (FALSE == bta_sys_is_register(BTA_ID_PAN)) { + ret = TRUE; + } + break; + + case HID_PSM_CONTROL: /* 0x11 */ + case HID_PSM_INTERRUPT: /* 0x13 */ + //FIX: allow HID Device and HID Host to coexist + if ( FALSE == bta_sys_is_register(BTA_ID_HD) || + FALSE == bta_sys_is_register(BTA_ID_HH) ) { + ret = TRUE; + } + break; + + case AVCT_PSM: /* 0x17 */ + case AVDT_PSM: /* 0x19 */ + if ((FALSE == bta_sys_is_register(BTA_ID_AV)) && + (FALSE == bta_sys_is_register(BTA_ID_AVK))) { + ret = TRUE; + } + break; + + default: + ret = TRUE; + break; + } + } else { + ret = TRUE; + } + } + return ret; +} + +/******************************************************************************* +** +** Function bta_jv_enable +** +** Description Initialises the JAVA I/F +** +** Returns void +** +*******************************************************************************/ +void bta_jv_enable(tBTA_JV_MSG *p_data) +{ + tBTA_UTL_COD cod; + + tBTA_JV_STATUS status = BTA_JV_SUCCESS; + bta_jv_cb.p_dm_cback = p_data->enable.p_cback; + bta_jv_cb.p_dm_cback(BTA_JV_ENABLE_EVT, (tBTA_JV *)&status, 0); + memset(bta_jv_cb.free_psm_list, 0, sizeof(bta_jv_cb.free_psm_list)); + + /* Set the Class of Device */ + cod.major = BTM_COD_MAJOR_UNCLASSIFIED; + cod.minor = BTM_COD_MINOR_UNCLASSIFIED; + utl_set_device_class(&cod, BTA_UTL_SET_COD_MAJOR_MINOR); +} + +/******************************************************************************* +** +** Function bta_jv_disable +** +** Description Disables the BT device manager +** free the resources used by java +** +** Returns void +** +*******************************************************************************/ +void bta_jv_disable (tBTA_JV_MSG *p_data) +{ + tBTA_JV_STATUS evt_data; + evt_data = BTA_JV_SUCCESS; + // UNUSED(p_data); + if (p_data->disable.p_cback) { + p_data->disable.p_cback(BTA_JV_DISABLE_EVT, (tBTA_JV *)&evt_data, NULL); + } +} + + +/** + * We keep a list of PSM's that have been freed from JAVA, for reuse. + * This function will return a free PSM, and delete it from the free + * list. + * If no free PSMs exist, 0 will be returned. + */ +static UINT16 bta_jv_get_free_psm(void) +{ + const int cnt = sizeof(bta_jv_cb.free_psm_list) / sizeof(bta_jv_cb.free_psm_list[0]); + for (int i = 0; i < cnt; i++) { + UINT16 psm = bta_jv_cb.free_psm_list[i]; + if (psm != 0) { + APPL_TRACE_DEBUG("%s(): Reusing PSM: 0x%04d", __func__, psm) + bta_jv_cb.free_psm_list[i] = 0; + return psm; + } + } + return 0; +} + +static void bta_jv_set_free_psm(UINT16 psm) +{ + int free_index = -1; + const int cnt = sizeof(bta_jv_cb.free_psm_list) / sizeof(bta_jv_cb.free_psm_list[0]); + for (int i = 0; i < cnt; i++) { + if (bta_jv_cb.free_psm_list[i] == 0) { + free_index = i; + } else if (psm == bta_jv_cb.free_psm_list[i]) { + return; // PSM already freed? + } + } + if (free_index != -1) { + bta_jv_cb.free_psm_list[free_index] = psm; + APPL_TRACE_DEBUG("%s(): Recycling PSM: 0x%04d", __func__, psm) + } else { + APPL_TRACE_ERROR("%s unable to free psm 0x%x no more free slots", __func__, psm); + } +} + +/******************************************************************************* +** +** Function bta_jv_get_channel_id +** +** Description Obtain a free SCN (Server Channel Number) +** (RFCOMM channel or L2CAP PSM) +** +** Returns void +** +*******************************************************************************/ +void bta_jv_get_channel_id(tBTA_JV_MSG *p_data) +{ + UINT16 psm = 0; + + switch (p_data->alloc_channel.type) { + case BTA_JV_CONN_TYPE_RFCOMM: { + INT32 channel = p_data->alloc_channel.channel; + UINT8 scn = 0; + if (channel > 0) { + if (BTM_TryAllocateSCN(channel) == FALSE) { + APPL_TRACE_ERROR("rfc channel:%d already in use or invalid", channel); + channel = 0; + } + } else if ((channel = BTM_AllocateSCN()) == 0) { + APPL_TRACE_ERROR("run out of rfc channels"); + channel = 0; + } + if (channel != 0) { + bta_jv_cb.scn[channel - 1] = TRUE; + scn = (UINT8) channel; + } + if (bta_jv_cb.p_dm_cback) { + bta_jv_cb.p_dm_cback(BTA_JV_GET_SCN_EVT, (tBTA_JV *)&scn, + p_data->alloc_channel.user_data); + } + return; + } + case BTA_JV_CONN_TYPE_L2CAP: + psm = bta_jv_get_free_psm(); + if (psm == 0) { + psm = L2CA_AllocatePSM(); + APPL_TRACE_DEBUG("%s() returned PSM: 0x%04x", __func__, psm); + } + break; + case BTA_JV_CONN_TYPE_L2CAP_LE: + break; + default: + break; + } + + if (bta_jv_cb.p_dm_cback) { + bta_jv_cb.p_dm_cback(BTA_JV_GET_PSM_EVT, (tBTA_JV *)&psm, p_data->alloc_channel.user_data); + } +} + +/******************************************************************************* +** +** Function bta_jv_free_scn +** +** Description free a SCN +** +** Returns void +** +*******************************************************************************/ +void bta_jv_free_scn(tBTA_JV_MSG *p_data) +{ + tBTA_JV_API_FREE_CHANNEL *fc = &(p_data->free_channel); + UINT16 scn = fc->scn; + tBTA_JV_FREE_SCN evt_data = { + .status = BTA_JV_SUCCESS, + .server_status = BTA_JV_SERVER_STATUS_MAX, + .scn = scn + }; + + tBTA_JV_FREE_SCN_USER_DATA *user_data = NULL; +#if BTA_JV_RFCOMM_INCLUDED + tBTA_JV_RFC_CB *p_cb = NULL; + tBTA_JV_PCB *p_pcb = NULL; +#endif /* BTA_JV_RFCOMM_INCLUDED */ + + switch (p_data->free_channel.type) { + case BTA_JV_CONN_TYPE_RFCOMM: { +#if BTA_JV_RFCOMM_INCLUDED + if (scn > 0 && scn <= BTA_JV_MAX_SCN && bta_jv_cb.scn[scn - 1]) { + /* this scn is used by JV */ + bta_jv_cb.scn[scn - 1] = FALSE; + BTM_FreeSCN(scn); + } + if (fc->user_data) { + user_data = (tBTA_JV_FREE_SCN_USER_DATA *)fc->user_data; + evt_data.server_status = user_data->server_status; + if (user_data->server_status == BTA_JV_SERVER_RUNNING && find_rfc_pcb((void *)user_data->slot_id, &p_cb, &p_pcb)) { + /* if call bta_jv_rfcomm_stop_server successfully, find_rfc_pcb shall return false */ + evt_data.status = BTA_JV_FAILURE; + } + + if (fc->p_cback) { + fc->p_cback(BTA_JV_FREE_SCN_EVT, (tBTA_JV *)&evt_data, (void *)user_data); + } + } +#endif /* BTA_JV_RFCOMM_INCLUDED */ + break; + } + case BTA_JV_CONN_TYPE_L2CAP: + bta_jv_set_free_psm(scn); + if (fc->p_cback) { + fc->p_cback(BTA_JV_FREE_SCN_EVT, (tBTA_JV *)&evt_data, (void *)user_data); + } + break; + case BTA_JV_CONN_TYPE_L2CAP_LE: + // TODO: Not yet implemented... + break; + default: + break; + } +} +static inline tBT_UUID shorten_sdp_uuid(const tBT_UUID *u) +{ + static uint8_t bt_base_uuid[] = + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB }; + + logu("in, uuid:", u->uu.uuid128); + APPL_TRACE_DEBUG("uuid len:%d", u->len); + if (u->len == 16) { + if (memcmp(&u->uu.uuid128[4], &bt_base_uuid[4], 12) == 0) { + tBT_UUID su; + memset(&su, 0, sizeof(su)); + if (u->uu.uuid128[0] == 0 && u->uu.uuid128[1] == 0) { + su.len = 2; + uint16_t u16; + memcpy(&u16, &u->uu.uuid128[2], sizeof(u16)); + su.uu.uuid16 = ntohs(u16); + APPL_TRACE_DEBUG("shorten to 16 bits uuid: %x", su.uu.uuid16); + } else { + su.len = 4; + uint32_t u32; + memcpy(&u32, &u->uu.uuid128[0], sizeof(u32)); + su.uu.uuid32 = ntohl(u32); + APPL_TRACE_DEBUG("shorten to 32 bits uuid: %x", su.uu.uuid32); + } + return su; + } + } + APPL_TRACE_DEBUG("cannot shorten none-reserved 128 bits uuid"); + return *u; +} + +/******************************************************************************* +** +** Function bta_jv_start_discovery_cback +** +** Description Callback for Start Discovery +** +** Returns void +** +*******************************************************************************/ +static void bta_jv_start_discovery_cback(UINT16 result, void *user_data) +{ + tBTA_JV_STATUS status; + // UINT8 old_sdp_act = bta_jv_cb.sdp_active; + + APPL_TRACE_DEBUG("bta_jv_start_discovery_cback res: 0x%x", result); + + bta_jv_cb.sdp_active = BTA_JV_SDP_ACT_NONE; + if (bta_jv_cb.p_dm_cback) { + tBTA_JV_DISCOVERY_COMP dcomp; + dcomp.scn_num = 0; + status = BTA_JV_FAILURE; + if (result == SDP_SUCCESS || result == SDP_DB_FULL) { + tSDP_DISC_REC *p_sdp_rec = NULL; + tSDP_DISC_ATTR *p_attr = NULL; + tSDP_PROTOCOL_ELEM pe; + logu("bta_jv_cb.uuid", bta_jv_cb.uuid.uu.uuid128); + tBT_UUID su = shorten_sdp_uuid(&bta_jv_cb.uuid); + logu("shorten uuid:", su.uu.uuid128); + do{ + p_sdp_rec = SDP_FindServiceUUIDInDb(p_bta_jv_cfg->p_sdp_db, &su, p_sdp_rec); + APPL_TRACE_DEBUG("p_sdp_rec:%p", p_sdp_rec); + if (p_sdp_rec && SDP_FindProtocolListElemInRec(p_sdp_rec, UUID_PROTOCOL_RFCOMM, &pe)){ + dcomp.scn[dcomp.scn_num] = (UINT8) pe.params[0]; + if ((p_attr = SDP_FindAttributeInRec(p_sdp_rec, ATTR_ID_SERVICE_NAME)) != NULL) { + dcomp.service_name[dcomp.scn_num] = (char *)p_attr->attr_value.v.array; + } else { + dcomp.service_name[dcomp.scn_num] = NULL; + } + dcomp.scn_num++; + status = BTA_JV_SUCCESS; + } + } while (p_sdp_rec); + } + + dcomp.status = status; + bta_jv_cb.p_dm_cback(BTA_JV_DISCOVERY_COMP_EVT, (tBTA_JV *)&dcomp, user_data); + } +} + +/******************************************************************************* +** +** Function bta_jv_start_discovery +** +** Description Discovers services on a remote device +** +** Returns void +** +*******************************************************************************/ +void bta_jv_start_discovery(tBTA_JV_MSG *p_data) +{ + tBTA_JV_STATUS status = BTA_JV_FAILURE; + APPL_TRACE_DEBUG("bta_jv_start_discovery in, sdp_active:%d", bta_jv_cb.sdp_active); + if (bta_jv_cb.sdp_active != BTA_JV_SDP_ACT_NONE) { + /* SDP is still in progress */ + status = BTA_JV_BUSY; + if (bta_jv_cb.p_dm_cback) { + bta_jv_cb.p_dm_cback(BTA_JV_DISCOVERY_COMP_EVT, (tBTA_JV *)&status, p_data->start_discovery.user_data); + } + return; + } + + /* init the database/set up the filter */ + APPL_TRACE_DEBUG("call SDP_InitDiscoveryDb, p_data->start_discovery.num_uuid:%d", + p_data->start_discovery.num_uuid); + SDP_InitDiscoveryDb (p_bta_jv_cfg->p_sdp_db, p_bta_jv_cfg->sdp_db_size, + p_data->start_discovery.num_uuid, p_data->start_discovery.uuid_list, 0, NULL); + + /* tell SDP to keep the raw data */ + p_bta_jv_cfg->p_sdp_db->raw_data = p_bta_jv_cfg->p_sdp_raw_data; + p_bta_jv_cfg->p_sdp_db->raw_size = p_bta_jv_cfg->sdp_raw_size; + + bta_jv_cb.p_sel_raw_data = 0; + bta_jv_cb.uuid = p_data->start_discovery.uuid_list[0]; + + bta_jv_cb.sdp_active = BTA_JV_SDP_ACT_YES; + if (!SDP_ServiceSearchAttributeRequest2(p_data->start_discovery.bd_addr, + p_bta_jv_cfg->p_sdp_db, + bta_jv_start_discovery_cback, p_data->start_discovery.user_data)) { + bta_jv_cb.sdp_active = BTA_JV_SDP_ACT_NONE; + /* failed to start SDP. report the failure right away */ + if (bta_jv_cb.p_dm_cback) { + bta_jv_cb.p_dm_cback(BTA_JV_DISCOVERY_COMP_EVT, (tBTA_JV *)&status, p_data->start_discovery.user_data); + } + } + /* + else report the result when the cback is called + */ +} + +/******************************************************************************* +** +** Function sdp_bqb_add_language_attr_ctrl +** +** Description Control adding of the language attribute for SDP BQB test +** +** Returns void +** +*******************************************************************************/ +#if BT_SDP_BQB_INCLUDED +void sdp_bqb_add_language_attr_ctrl(BOOLEAN enable) +{ + s_sdp_bqb_add_language_attr_flag = enable; +} +#endif /* BT_SDP_BQB_INCLUDED */ + +/** + * @brief Adds a protocol list and service name (if provided) to an SDP record given by + * sdp_handle, and marks it as browseable. This is a shortcut for defining a + * set of protocols that includes L2CAP, RFCOMM, and optionally OBEX. + * + * @param[in] sdp_handle: SDP handle + * @param[in] name: service name + * @param[in] channel: channel + * @param[in] with_obex: if TRUE, then an additional OBEX protocol UUID will be included + * at the end of the protocol list. + * @return + * - TRUE : success + * - other : failed + */ +static bool create_base_record(const uint32_t sdp_handle, const char *name, const uint16_t channel, const bool with_obex){ + APPL_TRACE_DEBUG("create_base_record: scn: %d, name: %s, with_obex: %d", + channel, name, with_obex); + + // Setup the protocol list and add it. + tSDP_PROTOCOL_ELEM proto_list[SDP_MAX_LIST_ELEMS]; + int num_proto_elements = with_obex ? 3 : 2; + + memset(proto_list, 0, num_proto_elements * sizeof(tSDP_PROTOCOL_ELEM)); + + proto_list[0].protocol_uuid = UUID_PROTOCOL_L2CAP; + proto_list[0].num_params = 0; + proto_list[1].protocol_uuid = UUID_PROTOCOL_RFCOMM; + proto_list[1].num_params = 1; + proto_list[1].params[0] = channel; + + if (with_obex == TRUE) { + proto_list[2].protocol_uuid = UUID_PROTOCOL_OBEX; + proto_list[2].num_params = 0; + } + + const char *stage = "protocol_list"; + if (!SDP_AddProtocolList(sdp_handle, num_proto_elements, proto_list)){ + APPL_TRACE_ERROR("create_base_record: failed to create base service " + "record, stage: %s, scn: %d, name: %s, with_obex: %d", + stage, channel, name, with_obex); + return FALSE; + } + + stage = "profile_descriptor_list"; + if (!SDP_AddProfileDescriptorList(sdp_handle, UUID_SERVCLASS_SERIAL_PORT, SPP_VERSION)){ + APPL_TRACE_ERROR("create_base_record: failed to create base service " + "record, stage: %s, scn: %d, name: %s, with_obex: %d", + stage, channel, name, with_obex); + return FALSE; + } + +#if BT_SDP_BQB_INCLUDED + // SDP/SR/SA/BV-09-C,SDP/SR/SSA/BV-13-C + if (s_sdp_bqb_add_language_attr_flag == TRUE) { + stage = "language_base"; + if (!SDP_AddLanguageBaseAttrIDList(sdp_handle, LANG_ID_CODE_ENGLISH, LANG_ID_CHAR_ENCODE_UTF8, LANGUAGE_BASE_ID)) { + APPL_TRACE_ERROR("create_base_record: failed to create base service " + "record, stage: %s, scn: %d, name: %s, with_obex: %d", + stage, channel, name, with_obex); + return FALSE; + } + } +#endif /* BT_SDP_BQB_INCLUDED */ + + // Add the name to the SDP record. + if (name[0] != '\0') { + stage = "service_name"; + if (!SDP_AddAttribute(sdp_handle, ATTR_ID_SERVICE_NAME, + TEXT_STR_DESC_TYPE, (uint32_t)(strlen(name) + 1), + (uint8_t *)name)){ + APPL_TRACE_ERROR("create_base_record: failed to create base service " + "record, stage: %s, scn: %d, name: %s, with_obex: %d", + stage, channel, name, with_obex); + return FALSE; + } + } + + // Mark the service as browseable. + uint16_t list = UUID_SERVCLASS_PUBLIC_BROWSE_GROUP; + stage = "browseable"; + if (!SDP_AddUuidSequence(sdp_handle, ATTR_ID_BROWSE_GROUP_LIST, 1, &list)){ + APPL_TRACE_ERROR("create_base_record: failed to create base service " + "record, stage: %s, scn: %d, name: %s, with_obex: %d", + stage, channel, name, with_obex); + return FALSE; + } + + + APPL_TRACE_DEBUG("create_base_record: successfully created base service " + "record, handle: 0x%08x, scn: %d, name: %s, with_obex: %d", + sdp_handle, channel, name, with_obex); + + UNUSED(stage); + + return TRUE; +} + +static int add_spp_sdp(const char *name, const int channel) { + APPL_TRACE_DEBUG("add_spp_sdp: scn %d, service_name %s", channel, name); + + int handle = SDP_CreateRecord(); + if (handle == 0) { + APPL_TRACE_ERROR("add_spp_sdp: failed to create sdp record, " + "service_name: %s", name); + return 0; + } + + // Create the base SDP record. + const char *stage = "create_base_record"; + if (!create_base_record(handle, name, channel, FALSE /* with_obex */)){ + SDP_DeleteRecord(handle); + APPL_TRACE_ERROR("add_spp_sdp: failed to register SPP service, " + "stage: %s, service_name: %s", stage, name); + return 0; + } + + uint16_t service = UUID_SERVCLASS_SERIAL_PORT; + stage = "service_class"; + if (!SDP_AddServiceClassIdList(handle, 1, &service)){ + SDP_DeleteRecord(handle); + APPL_TRACE_ERROR("add_spp_sdp: failed to register SPP service, " + "stage: %s, service_name: %s", stage, name); + return 0; + } + + APPL_TRACE_DEBUG("add_spp_sdp: service registered successfully, " + "service_name: %s, handle 0x%08x)", name, handle); + UNUSED(stage); + + return handle; +} + +/******************************************************************************* +** +** Function bta_jv_create_record +** +** Description Create an SDP record with the given attributes +** +** Returns void +** +*******************************************************************************/ +void bta_jv_create_record(tBTA_JV_MSG *p_data) +{ + tBTA_JV_API_CREATE_RECORD *cr = &(p_data->create_record); + tBTA_JV_CREATE_RECORD evt_data; + + int handle = add_spp_sdp(cr->name, cr->channel); + evt_data.handle = handle; + if (handle) { + evt_data.status = BTA_JV_SUCCESS; + } else { + evt_data.status = BTA_JV_FAILURE; + } + + if(bta_jv_cb.p_dm_cback) { + //callback user immediately to create his own sdp record in stack thread context + bta_jv_cb.p_dm_cback(BTA_JV_CREATE_RECORD_EVT, (tBTA_JV *)&evt_data, cr->user_data); + } +} + +/******************************************************************************* +** +** Function bta_jv_delete_record +** +** Description Delete an SDP record +** +** +** Returns void +** +*******************************************************************************/ +void bta_jv_delete_record(tBTA_JV_MSG *p_data) +{ + tBTA_JV_API_ADD_ATTRIBUTE *dr = &(p_data->add_attr); + if (dr->handle) { + /* this is a record created by btif layer*/ + SDP_DeleteRecord(dr->handle); + } +} + +#if BTA_JV_L2CAP_INCLUDED +/******************************************************************************* +** +** Function bta_jv_l2cap_client_cback +** +** Description handles the l2cap client events +** +** Returns void +** +*******************************************************************************/ +static void bta_jv_l2cap_client_cback(UINT16 gap_handle, UINT16 event) +{ + if (gap_handle >= BTA_JV_MAX_L2C_CONN) { + APPL_TRACE_WARNING("Invalid gap_handle: %u", gap_handle); + return; + } + + tBTA_JV_L2C_CB *p_cb = &bta_jv_cb.l2c_cb[gap_handle]; + tBTA_JV evt_data = {0}; + + if (!p_cb->p_cback) { + return; + } + + APPL_TRACE_DEBUG( "%s: %d evt:x%x", __func__, gap_handle, event); + evt_data.l2c_open.status = BTA_JV_SUCCESS; + evt_data.l2c_open.handle = gap_handle; + + switch (event) { + case GAP_EVT_CONN_OPENED: + bdcpy(evt_data.l2c_open.rem_bda, GAP_ConnGetRemoteAddr(gap_handle)); + evt_data.l2c_open.tx_mtu = GAP_ConnGetRemMtuSize(gap_handle); + p_cb->state = BTA_JV_ST_CL_OPEN; + p_cb->p_cback(BTA_JV_L2CAP_OPEN_EVT, &evt_data, p_cb->user_data); + break; + + case GAP_EVT_CONN_CLOSED: + p_cb->state = BTA_JV_ST_NONE; + bta_jv_free_sec_id(&p_cb->sec_id); + evt_data.l2c_close.async = TRUE; + p_cb->p_cback(BTA_JV_L2CAP_CLOSE_EVT, &evt_data, p_cb->user_data); + p_cb->p_cback = NULL; + break; + + case GAP_EVT_CONN_DATA_AVAIL: + evt_data.data_ind.handle = gap_handle; + /* Reset idle timer to avoid requesting sniff mode while receiving data */ + bta_jv_pm_conn_busy(p_cb->p_pm_cb); + p_cb->p_cback(BTA_JV_L2CAP_DATA_IND_EVT, &evt_data, p_cb->user_data); + bta_jv_pm_conn_idle(p_cb->p_pm_cb); + break; + + case GAP_EVT_CONN_CONGESTED: + case GAP_EVT_CONN_UNCONGESTED: + p_cb->cong = (event == GAP_EVT_CONN_CONGESTED) ? TRUE : FALSE; + evt_data.l2c_cong.cong = p_cb->cong; + p_cb->p_cback(BTA_JV_L2CAP_CONG_EVT, &evt_data, p_cb->user_data); + break; + + default: + break; + } +} + +/******************************************************************************* +** +** Function bta_jv_l2cap_connect +** +** Description makes an l2cap client connection +** +** Returns void +** +*******************************************************************************/ +void bta_jv_l2cap_connect(tBTA_JV_MSG *p_data) +{ + tBTA_JV_L2C_CB *p_cb; + tBTA_JV_L2CAP_CL_INIT evt_data; + UINT16 handle = GAP_INVALID_HANDLE; + UINT8 sec_id; + tL2CAP_CFG_INFO cfg; + tBTA_JV_API_L2CAP_CONNECT *cc = &(p_data->l2cap_connect); + UINT8 chan_mode_mask = GAP_FCR_CHAN_OPT_BASIC; + tL2CAP_ERTM_INFO *ertm_info = NULL; + + memset(&cfg, 0, sizeof(tL2CAP_CFG_INFO)); + + if (cc->has_cfg == TRUE) { + cfg = cc->cfg; + if (cfg.fcr_present && cfg.fcr.mode == L2CAP_FCR_ERTM_MODE) { + chan_mode_mask |= GAP_FCR_CHAN_OPT_ERTM; + } + } + + if (cc->has_ertm_info == TRUE) { + ertm_info = &(cc->ertm_info); + } + + /* We need to use this value for MTU to be able to handle cases where cfg is not set in req. */ + cfg.mtu_present = TRUE; + cfg.mtu = cc->rx_mtu; + + /* TODO: DM role manager + L2CA_SetDesireRole(cc->role); + */ + + sec_id = bta_jv_alloc_sec_id(); + evt_data.sec_id = sec_id; + evt_data.status = BTA_JV_FAILURE; + + if (sec_id) { + if (bta_jv_check_psm(cc->remote_psm)) { /* allowed */ + if ((handle = GAP_ConnOpen("", sec_id, 0, cc->peer_bd_addr, cc->remote_psm, + &cfg, ertm_info, cc->sec_mask, chan_mode_mask, + bta_jv_l2cap_client_cback)) != GAP_INVALID_HANDLE ) { + evt_data.status = BTA_JV_SUCCESS; + } + } + } + + if (evt_data.status == BTA_JV_SUCCESS) { + p_cb = &bta_jv_cb.l2c_cb[handle]; + p_cb->handle = handle; + p_cb->p_cback = cc->p_cback; + p_cb->user_data = cc->user_data; + p_cb->psm = 0; /* not a server */ + p_cb->sec_id = sec_id; + p_cb->state = BTA_JV_ST_CL_OPENING; + } else { + bta_jv_free_sec_id(&sec_id); + } + + evt_data.handle = handle; + cc->p_cback(BTA_JV_L2CAP_CL_INIT_EVT, (tBTA_JV *)&evt_data, cc->user_data); +} + + +/******************************************************************************* +** +** Function bta_jv_l2cap_close +** +** Description Close an L2CAP client connection +** +** Returns void +** +*******************************************************************************/ +void bta_jv_l2cap_close(tBTA_JV_MSG *p_data) +{ + tBTA_JV_L2CAP_CLOSE evt_data; + tBTA_JV_API_L2CAP_CLOSE *cc = &(p_data->l2cap_close); + void *user_data = cc->user_data; + + evt_data.handle = cc->handle; + evt_data.status = bta_jv_free_l2c_cb(cc->p_cb); + evt_data.async = FALSE; + + if (cc->p_cback) { + cc->p_cback(BTA_JV_L2CAP_CLOSE_EVT, (tBTA_JV *)&evt_data, user_data); + } +} + +/******************************************************************************* +** +** Function bta_jv_l2cap_server_cback +** +** Description handles the l2cap server callback +** +** Returns void +** +*******************************************************************************/ +static void bta_jv_l2cap_server_cback(UINT16 gap_handle, UINT16 event) +{ + if (gap_handle >= BTA_JV_MAX_L2C_CONN) { + APPL_TRACE_WARNING("Invalid gap_handle: %u", gap_handle); + return; + } + + tBTA_JV_L2C_CB *p_cb = &bta_jv_cb.l2c_cb[gap_handle]; + tBTA_JV evt_data = {0}; + tBTA_JV_L2CAP_CBACK *p_cback; + void *user_data; + + if (!p_cb->p_cback) { + return; + } + + APPL_TRACE_DEBUG( "%s: %d evt:x%x", __func__, gap_handle, event); + evt_data.l2c_open.status = BTA_JV_SUCCESS; + evt_data.l2c_open.handle = gap_handle; + + switch (event) { + case GAP_EVT_CONN_OPENED: + bdcpy(evt_data.l2c_open.rem_bda, GAP_ConnGetRemoteAddr(gap_handle)); + evt_data.l2c_open.tx_mtu = GAP_ConnGetRemMtuSize(gap_handle); + p_cb->state = BTA_JV_ST_SR_OPEN; + p_cb->p_cback(BTA_JV_L2CAP_OPEN_EVT, &evt_data, p_cb->user_data); + break; + + case GAP_EVT_CONN_CLOSED: + evt_data.l2c_close.async = TRUE; + evt_data.l2c_close.handle = p_cb->handle; + p_cback = p_cb->p_cback; + user_data = p_cb->user_data; + evt_data.l2c_close.status = bta_jv_free_l2c_cb(p_cb); + p_cback(BTA_JV_L2CAP_CLOSE_EVT, &evt_data, user_data); + break; + + case GAP_EVT_CONN_DATA_AVAIL: + evt_data.data_ind.handle = gap_handle; + /* Reset idle timer to avoid requesting sniff mode while receiving data */ + bta_jv_pm_conn_busy(p_cb->p_pm_cb); + p_cb->p_cback(BTA_JV_L2CAP_DATA_IND_EVT, &evt_data, p_cb->user_data); + bta_jv_pm_conn_idle(p_cb->p_pm_cb); + break; + + case GAP_EVT_CONN_CONGESTED: + case GAP_EVT_CONN_UNCONGESTED: + p_cb->cong = (event == GAP_EVT_CONN_CONGESTED) ? TRUE : FALSE; + evt_data.l2c_cong.cong = p_cb->cong; + p_cb->p_cback(BTA_JV_L2CAP_CONG_EVT, &evt_data, p_cb->user_data); + break; + + default: + break; + } +} + +/******************************************************************************* +** +** Function bta_jv_l2cap_start_server +** +** Description starts an L2CAP server +** +** Returns void +** +*******************************************************************************/ +void bta_jv_l2cap_start_server(tBTA_JV_MSG *p_data) +{ + tBTA_JV_L2C_CB *p_cb; + UINT8 sec_id; + UINT16 handle; + tL2CAP_CFG_INFO cfg; + tBTA_JV_L2CAP_START evt_data; + tBTA_JV_API_L2CAP_SERVER *ls = &(p_data->l2cap_server); + // INT32 use_etm = FALSE; + UINT8 chan_mode_mask = GAP_FCR_CHAN_OPT_BASIC; + tL2CAP_ERTM_INFO *ertm_info = NULL; + + memset(&cfg, 0, sizeof(tL2CAP_CFG_INFO)); + + if (ls->has_cfg == TRUE) { + cfg = ls->cfg; + if (cfg.fcr_present && cfg.fcr.mode == L2CAP_FCR_ERTM_MODE) { + chan_mode_mask |= GAP_FCR_CHAN_OPT_ERTM; + } + } + + if (ls->has_ertm_info == TRUE) { + ertm_info = &(ls->ertm_info); + } + + //FIX: MTU=0 means not present + if (ls->rx_mtu > 0) { + cfg.mtu_present = TRUE; + cfg.mtu = ls->rx_mtu; + } else { + cfg.mtu_present = FALSE; + cfg.mtu = 0; + } + + /* TODO DM role manager + L2CA_SetDesireRole(ls->role); + */ + + sec_id = bta_jv_alloc_sec_id(); + if (0 == sec_id || (FALSE == bta_jv_check_psm(ls->local_psm)) || + (handle = GAP_ConnOpen("JV L2CAP", sec_id, 1, 0, ls->local_psm, &cfg, ertm_info, + ls->sec_mask, chan_mode_mask, bta_jv_l2cap_server_cback)) == GAP_INVALID_HANDLE) { + bta_jv_free_sec_id(&sec_id); + evt_data.status = BTA_JV_FAILURE; + } else { + p_cb = &bta_jv_cb.l2c_cb[handle]; + evt_data.status = BTA_JV_SUCCESS; + evt_data.handle = handle; + evt_data.sec_id = sec_id; + p_cb->p_cback = ls->p_cback; + p_cb->user_data = ls->user_data; + p_cb->handle = handle; + p_cb->sec_id = sec_id; + p_cb->state = BTA_JV_ST_SR_LISTEN; + p_cb->psm = ls->local_psm; + } + + ls->p_cback(BTA_JV_L2CAP_START_EVT, (tBTA_JV *)&evt_data, ls->user_data); +} + +/******************************************************************************* +** +** Function bta_jv_l2cap_stop_server +** +** Description stops an L2CAP server +** +** Returns void +** +*******************************************************************************/ +void bta_jv_l2cap_stop_server(tBTA_JV_MSG *p_data) +{ + tBTA_JV_L2C_CB *p_cb; + tBTA_JV_L2CAP_CLOSE evt_data; + tBTA_JV_API_L2CAP_SERVER *ls = &(p_data->l2cap_server); + tBTA_JV_L2CAP_CBACK *p_cback; + void *user_data; + for (int i = 0; i < BTA_JV_MAX_L2C_CONN; i++) { + if (bta_jv_cb.l2c_cb[i].psm == ls->local_psm) { + p_cb = &bta_jv_cb.l2c_cb[i]; + p_cback = p_cb->p_cback; + user_data = p_cb->user_data; + evt_data.handle = p_cb->handle; + evt_data.status = bta_jv_free_l2c_cb(p_cb); + evt_data.async = FALSE; + p_cback(BTA_JV_L2CAP_CLOSE_EVT, (tBTA_JV *)&evt_data, user_data); + break; + } + } +} + + + +/******************************************************************************* +** +** Function bta_jv_l2cap_read +** +** Description Read data from an L2CAP connection +** +** Returns void +** +*******************************************************************************/ +void bta_jv_l2cap_read(tBTA_JV_MSG *p_data) +{ + tBTA_JV_L2CAP_READ evt_data; + tBTA_JV_API_L2CAP_READ *rc = &(p_data->l2cap_read); + + evt_data.status = BTA_JV_FAILURE; + evt_data.handle = rc->handle; + evt_data.req_id = rc->req_id; + evt_data.p_data = rc->p_data; + evt_data.len = 0; + + if (BT_PASS == GAP_ConnReadData(rc->handle, rc->p_data, rc->len, &evt_data.len)) { + evt_data.status = BTA_JV_SUCCESS; + } + + rc->p_cback(BTA_JV_L2CAP_READ_EVT, (tBTA_JV *)&evt_data, rc->user_data); +} + + +/******************************************************************************* +** +** Function bta_jv_l2cap_write +** +** Description Write data to an L2CAP connection +** +** Returns void +** +*******************************************************************************/ +void bta_jv_l2cap_write(tBTA_JV_MSG *p_data) +{ + tBTA_JV_L2CAP_WRITE evt_data; + tBTA_JV_API_L2CAP_WRITE *ls = &(p_data->l2cap_write); + + /* As we check this callback exists before the tBTA_JV_API_L2CAP_WRITE can be send through the + * API this check should not be needed. + * But the API is not designed to be used (safely at least) in a multi-threaded scheduler, hence + * if the peer device disconnects the l2cap link after the API is called, but before this + * message is handled, the ->p_cback will be cleared at this point. At first glanch this seems + * highly unlikely, but for all obex-profiles with two channels connected - e.g. MAP, this + * happens around 1 of 4 disconnects, as a disconnect on the server channel causes a disconnect + * to be send on the client (notification) channel, but at the peer typically disconnects both + * the OBEX disconnect request crosses the incoming l2cap disconnect. + * If p_cback is cleared, we simply discard the data. + * RISK: The caller must handle any cleanup based on another signal than BTA_JV_L2CAP_WRITE_EVT, + * which is typically not possible, as the pointer to the allocated buffer is stored + * in this message, and can therefore not be freed, hence we have a mem-leak-by-design.*/ + if (ls->p_cb->p_cback != NULL) { + evt_data.status = BTA_JV_FAILURE; + evt_data.handle = ls->handle; + evt_data.req_id = ls->req_id; + evt_data.cong = ls->p_cb->cong; + evt_data.len = 0; + bta_jv_pm_conn_busy(ls->p_cb->p_pm_cb); + if (!evt_data.cong && + BT_PASS == GAP_ConnWriteData(ls->handle, ls->p_data, ls->len, &evt_data.len)) { + evt_data.status = BTA_JV_SUCCESS; + } + ls->p_cb->p_cback(BTA_JV_L2CAP_WRITE_EVT, (tBTA_JV *)&evt_data, ls->user_data); + bta_jv_set_pm_conn_state(ls->p_cb->p_pm_cb, BTA_JV_CONN_IDLE); + } else { + /* As this pointer is checked in the API function, this occurs only when the channel is + * disconnected after the API function is called, but before the message is handled. */ + APPL_TRACE_ERROR("%s() ls->p_cb->p_cback == NULL", __func__); + } +} + +/******************************************************************************* +** +** Function bta_jv_l2cap_write_fixed +** +** Description Write data to an L2CAP connection using Fixed channels +** +** Returns void +** +*******************************************************************************/ +void bta_jv_l2cap_write_fixed(tBTA_JV_MSG *p_data) +{ + tBTA_JV_L2CAP_WRITE_FIXED evt_data; + tBTA_JV_API_L2CAP_WRITE_FIXED *ls = &(p_data->l2cap_write_fixed); + BT_HDR *msg = (BT_HDR *)osi_malloc(sizeof(BT_HDR) + ls->len + L2CAP_MIN_OFFSET); + if (!msg) { + APPL_TRACE_ERROR("%s() could not allocate msg buffer", __func__); + return; + } + evt_data.status = BTA_JV_FAILURE; + evt_data.channel = ls->channel; + memcpy(evt_data.addr, ls->addr, sizeof(evt_data.addr)); + evt_data.req_id = ls->req_id; + evt_data.len = 0; + + + memcpy(((uint8_t *)(msg + 1)) + L2CAP_MIN_OFFSET, ls->p_data, ls->len); + msg->len = ls->len; + msg->offset = L2CAP_MIN_OFFSET; + + L2CA_SendFixedChnlData(ls->channel, ls->addr, msg); + + ls->p_cback(BTA_JV_L2CAP_WRITE_FIXED_EVT, (tBTA_JV *)&evt_data, ls->user_data); +} +#endif /* BTA_JV_L2CAP_INCLUDED */ + +#if BTA_JV_RFCOMM_INCLUDED +/******************************************************************************* +** +** Function bta_jv_port_data_co_cback +** +** Description port data callback function of rfcomm +** connections +** +** Returns void +** +*******************************************************************************/ +static int bta_jv_port_data_co_cback(UINT16 port_handle, UINT8 *buf, UINT16 len, int type) +{ + tBTA_JV_RFC_CB *p_cb = bta_jv_rfc_port_to_cb(port_handle); + tBTA_JV_PCB *p_pcb = bta_jv_rfc_port_to_pcb(port_handle); + int ret = 0; + APPL_TRACE_DEBUG("%s, p_cb:%p, p_pcb:%p, len:%d, type:%d", __func__, p_cb, p_pcb, len, type); + UNUSED(p_cb); + if (p_pcb != NULL) { + switch (type) { + case DATA_CO_CALLBACK_TYPE_INCOMING: + bta_jv_pm_conn_busy(p_pcb->p_pm_cb); + ret = bta_co_rfc_data_incoming(p_pcb->user_data, (BT_HDR *)buf); + bta_jv_pm_conn_idle(p_pcb->p_pm_cb); + return ret; + case DATA_CO_CALLBACK_TYPE_OUTGOING_SIZE: + return bta_co_rfc_data_outgoing_size(p_pcb->user_data, (int *)buf); + case DATA_CO_CALLBACK_TYPE_OUTGOING: + return bta_co_rfc_data_outgoing(p_pcb->user_data, buf, len); + default: + APPL_TRACE_ERROR("unknown callout type:%d", type); + break; + } + } + return 0; +} + +/******************************************************************************* +** +** Function bta_jv_port_mgmt_cl_cback +** +** Description callback for port mamangement function of rfcomm +** client connections +** +** Returns void +** +*******************************************************************************/ +static void bta_jv_port_mgmt_cl_cback(UINT32 code, UINT16 port_handle, void* data) +{ + tBTA_JV_RFC_CB *p_cb = bta_jv_rfc_port_to_cb(port_handle); + tBTA_JV_PCB *p_pcb = bta_jv_rfc_port_to_pcb(port_handle); + tBTA_JV evt_data = {0}; + BD_ADDR rem_bda = {0}; + UINT16 lcid; + tBTA_JV_RFCOMM_CBACK *p_cback; /* the callback function */ + tPORT_MGMT_CL_CALLBACK_ARG *p_mgmt_cb_arg = (tPORT_MGMT_CL_CALLBACK_ARG *)data; + void *user_data = NULL; + + APPL_TRACE_DEBUG( "bta_jv_port_mgmt_cl_cback:code:%d, port_handle%d", code, port_handle); + if (NULL == p_cb || NULL == p_cb->p_cback) { + return; + } + + APPL_TRACE_DEBUG( "bta_jv_port_mgmt_cl_cback code=%d port_handle:%d handle:%d", + code, port_handle, p_cb->handle); + + PORT_CheckConnection(port_handle, FALSE, rem_bda, &lcid); + + if (code == PORT_SUCCESS) { + evt_data.rfc_open.handle = p_pcb->handle; + evt_data.rfc_open.status = BTA_JV_SUCCESS; + bdcpy(evt_data.rfc_open.rem_bda, rem_bda); + p_pcb->state = BTA_JV_ST_CL_OPEN; + if (p_mgmt_cb_arg) { + evt_data.rfc_open.peer_mtu = p_mgmt_cb_arg->peer_mtu; + } + p_cb->p_cback(BTA_JV_RFCOMM_OPEN_EVT, &evt_data, p_pcb->user_data); + } else { + evt_data.rfc_close.handle = p_pcb->handle; + evt_data.rfc_close.status = BTA_JV_FAILURE; + evt_data.rfc_close.port_status = code; + evt_data.rfc_close.async = TRUE; + if (p_pcb->state == BTA_JV_ST_CL_CLOSING) { + evt_data.rfc_close.async = FALSE; + evt_data.rfc_close.status = BTA_JV_SUCCESS; + } + p_cback = p_cb->p_cback; + user_data = p_pcb->user_data; + + // To free up resources. + p_pcb->state = BTA_JV_ST_CL_CLOSING; + bta_jv_free_rfc_cb(p_cb, p_pcb, FALSE, FALSE); + p_cback(BTA_JV_RFCOMM_CLOSE_EVT, &evt_data, user_data); + } +} + +/******************************************************************************* +** +** Function bta_jv_port_event_cl_cback +** +** Description Callback for RFCOMM client port events +** +** Returns void +** +*******************************************************************************/ +static void bta_jv_port_event_cl_cback(UINT32 code, UINT16 port_handle) +{ + tBTA_JV_RFC_CB *p_cb = bta_jv_rfc_port_to_cb(port_handle); + tBTA_JV_PCB *p_pcb = bta_jv_rfc_port_to_pcb(port_handle); + tBTA_JV evt_data = {0}; + + APPL_TRACE_DEBUG( "bta_jv_port_event_cl_cback:%d", port_handle); + if (NULL == p_cb || NULL == p_cb->p_cback) { + return; + } + + APPL_TRACE_DEBUG( "bta_jv_port_event_cl_cback code=x%x port_handle:%d handle:%d", + code, port_handle, p_cb->handle); + if (code & PORT_EV_RXCHAR) { + evt_data.data_ind.handle = p_pcb->handle; + p_cb->p_cback(BTA_JV_RFCOMM_DATA_IND_EVT, &evt_data, p_pcb->user_data); + } + + if (code & PORT_EV_FC) { + p_pcb->cong = (code & PORT_EV_FCS) ? FALSE : TRUE; + evt_data.rfc_cong.cong = p_pcb->cong; + evt_data.rfc_cong.handle = p_pcb->handle; + evt_data.rfc_cong.status = BTA_JV_SUCCESS; + p_cb->p_cback(BTA_JV_RFCOMM_CONG_EVT, &evt_data, p_pcb->user_data); + } + + if (code & PORT_EV_TXEMPTY) { + bta_jv_pm_conn_idle(p_pcb->p_pm_cb); + } +} + +/******************************************************************************* +** +** Function bta_jv_rfcomm_config +** +** Description Configure RFCOMM +** +** Returns void +** +*******************************************************************************/ +void bta_jv_rfcomm_config(tBTA_JV_MSG *p_data) +{ + APPL_TRACE_DEBUG("%s enable_l2cap_ertm:%d", __func__, p_data->rfcomm_config.enable_l2cap_ertm); + + PORT_SetL2capErtm(p_data->rfcomm_config.enable_l2cap_ertm); +} + +/******************************************************************************* +** +** Function bta_jv_rfcomm_connect +** +** Description Client initiates an RFCOMM connection +** +** Returns void +** +*******************************************************************************/ +void bta_jv_rfcomm_connect(tBTA_JV_MSG *p_data) +{ + UINT16 handle = 0; + UINT32 event_mask = BTA_JV_RFC_EV_MASK; + tPORT_STATE port_state; + UINT8 sec_id = 0; + tBTA_JV_RFC_CB *p_cb = NULL; + tBTA_JV_PCB *p_pcb; + tBTA_JV_API_RFCOMM_CONNECT *cc = &(p_data->rfcomm_connect); + tBTA_JV_RFCOMM_CL_INIT evt_data = {0}; + + /* TODO DM role manager + L2CA_SetDesireRole(cc->role); + */ + + sec_id = bta_jv_alloc_sec_id(); + evt_data.sec_id = sec_id; + evt_data.status = BTA_JV_SUCCESS; + if (0 == sec_id || + BTM_SetSecurityLevel(TRUE, "", sec_id, cc->sec_mask, BT_PSM_RFCOMM, + BTM_SEC_PROTO_RFCOMM, cc->remote_scn) == FALSE) { + evt_data.status = BTA_JV_FAILURE; + APPL_TRACE_ERROR("sec_id:%d is zero or BTM_SetSecurityLevel failed, remote_scn:%d", sec_id, cc->remote_scn); + } + + if (evt_data.status == BTA_JV_SUCCESS && + RFCOMM_CreateConnection(UUID_SERVCLASS_SERIAL_PORT, cc->remote_scn, FALSE, + BTA_JV_DEF_RFC_MTU, cc->peer_bd_addr, &handle, bta_jv_port_mgmt_cl_cback) != PORT_SUCCESS) { + APPL_TRACE_ERROR("bta_jv_rfcomm_connect, RFCOMM_CreateConnection failed"); + evt_data.status = BTA_JV_FAILURE; + } + if (evt_data.status == BTA_JV_SUCCESS) { + p_cb = bta_jv_alloc_rfc_cb(handle, &p_pcb); + if (p_cb) { + p_cb->p_cback = cc->p_cback; + p_cb->sec_id = sec_id; + p_cb->scn = 0; + p_pcb->state = BTA_JV_ST_CL_OPENING; + p_pcb->user_data = cc->user_data; + evt_data.use_co = TRUE; + + PORT_SetEventCallback(handle, bta_jv_port_event_cl_cback); + PORT_SetEventMask(handle, event_mask); + PORT_SetDataCOCallback (handle, bta_jv_port_data_co_cback); + + PORT_GetState(handle, &port_state); + + port_state.fc_type = (PORT_FC_CTS_ON_INPUT | PORT_FC_CTS_ON_OUTPUT); + + /* coverity[uninit_use_in_call] + FALSE-POSITIVE: port_state is initialized at PORT_GetState() */ + PORT_SetState(handle, &port_state); + + evt_data.handle = p_pcb->handle; + } else { + evt_data.status = BTA_JV_FAILURE; + APPL_TRACE_ERROR("run out of rfc control block"); + } + } + cc->p_cback(BTA_JV_RFCOMM_CL_INIT_EVT, (tBTA_JV *)&evt_data, cc->user_data); + if (evt_data.status == BTA_JV_FAILURE) { + if (sec_id) { + bta_jv_free_sec_id(&sec_id); + } + if (handle) { + RFCOMM_RemoveConnection(handle); + } + } +} + +static int find_rfc_pcb(void *user_data, tBTA_JV_RFC_CB **cb, tBTA_JV_PCB **pcb) +{ + *cb = NULL; + *pcb = NULL; + int i; + for (i = 0; i < MAX_RFC_PORTS; i++) { + UINT32 rfc_handle = bta_jv_cb.port_cb[i].handle & BTA_JV_RFC_HDL_MASK; + rfc_handle &= ~BTA_JV_RFCOMM_MASK; + if (rfc_handle && bta_jv_cb.port_cb[i].user_data == user_data) { + *pcb = &bta_jv_cb.port_cb[i]; + *cb = &bta_jv_cb.rfc_cb[rfc_handle - 1]; + APPL_TRACE_DEBUG("find_rfc_pcb(): FOUND rfc_cb_handle 0x%x, port.jv_handle:" + " 0x%x, state: %d, rfc_cb->handle: 0x%x", rfc_handle, (*pcb)->handle, + (*pcb)->state, (*cb)->handle); + return 1; + } + } + APPL_TRACE_DEBUG("find_rfc_pcb: cannot find rfc_cb from user data:%d", (UINT32)user_data); + return 0; +} + +/******************************************************************************* +** +** Function bta_jv_rfcomm_close +** +** Description Close an RFCOMM connection +** +** Returns void +** +*******************************************************************************/ +void bta_jv_rfcomm_close(tBTA_JV_MSG *p_data) +{ + tBTA_JV_API_RFCOMM_CLOSE *cc = &(p_data->rfcomm_close); + tBTA_JV_RFC_CB *p_cb = NULL; + tBTA_JV_PCB *p_pcb = NULL; + APPL_TRACE_DEBUG("%s, rfc handle:%d",__func__, cc->handle); + if (!cc->handle) { + APPL_TRACE_ERROR("%s, rfc handle is null", __func__); + return; + } + + void *user_data = cc->user_data; + if (!find_rfc_pcb(user_data, &p_cb, &p_pcb)) { + return; + } + + bta_jv_free_rfc_cb(p_cb, p_pcb, FALSE, TRUE); + + APPL_TRACE_DEBUG("%s: sec id in use:%d, rfc_cb in use:%d",__func__, + get_sec_id_used(), get_rfc_cb_used()); +} + +/******************************************************************************* +** +** Function bta_jv_get_num_rfc_listen +** +** Description when a RFCOMM connection goes down, make sure that there's only +** one port stays listening on this scn. +** +** Returns +** +*******************************************************************************/ +static UINT8 __attribute__((unused)) bta_jv_get_num_rfc_listen(tBTA_JV_RFC_CB *p_cb) +{ + UINT8 listen = 1; + + if (p_cb->max_sess > 1) { + listen = 0; + for (UINT8 i = 0; i < p_cb->max_sess; i++) { + if (p_cb->rfc_hdl[i] != 0) { + const tBTA_JV_PCB *p_pcb = &bta_jv_cb.port_cb[p_cb->rfc_hdl[i] - 1]; + if (BTA_JV_ST_SR_LISTEN == p_pcb->state) { + listen++; + } + } + } + } + return listen; +} + +/******************************************************************************* +** +** Function bta_jv_port_mgmt_sr_cback +** +** Description callback for port mamangement function of rfcomm +** server connections +** +** Returns void +** +*******************************************************************************/ +static void bta_jv_port_mgmt_sr_cback(UINT32 code, UINT16 port_handle, void *data) +{ + tBTA_JV_PCB *p_pcb = bta_jv_rfc_port_to_pcb(port_handle); + tBTA_JV_RFC_CB *p_cb = bta_jv_rfc_port_to_cb(port_handle); + tBTA_JV evt_data = {0}; + BD_ADDR rem_bda = {0}; + UINT16 lcid; + tPORT_MGMT_SR_CALLBACK_ARG *p_mgmt_cb_arg = (tPORT_MGMT_SR_CALLBACK_ARG *)data; + void *user_data = NULL; + void *new_user_data = NULL; + int status; + int failed = TRUE; + + // APPL_TRACE_DEBUG("bta_jv_port_mgmt_sr_cback, code:0x%x, port_handle:%d", code, (uint16_t)port_handle); + if (NULL == p_cb || NULL == p_cb->p_cback) { + // APPL_TRACE_ERROR("bta_jv_port_mgmt_sr_cback, p_cb:%p, p_cb->p_cback%p", + // p_cb, p_cb ? p_cb->p_cback : NULL); + return; + } + user_data = p_pcb->user_data; + // APPL_TRACE_DEBUG( "bta_jv_port_mgmt_sr_cback code=%p port_handle:0x%x handle:0x%x, p_pcb:%p, user:%p", + // code, port_handle, p_cb->handle, p_pcb, p_pcb->user_data); + + if (p_mgmt_cb_arg) { + if ((status = PORT_CheckConnection(port_handle, p_mgmt_cb_arg->ignore_rfc_state, rem_bda, &lcid)) != + PORT_SUCCESS) { + APPL_TRACE_WARNING("PORT_CheckConnection status:%d", status); + } + } else { + PORT_CheckConnection(port_handle, FALSE, rem_bda, &lcid); + } + + if (code == PORT_SUCCESS) { + failed = FALSE; + /* accept the connection defaulted */ + if (p_mgmt_cb_arg) { + p_mgmt_cb_arg->accept = TRUE; + } + evt_data.rfc_srv_open.handle = p_pcb->handle; + evt_data.rfc_srv_open.status = BTA_JV_SUCCESS; + evt_data.rfc_srv_open.peer_mtu = p_mgmt_cb_arg->peer_mtu; + bdcpy(evt_data.rfc_srv_open.rem_bda, rem_bda); + tBTA_JV_PCB *p_pcb_new_listen = bta_jv_add_rfc_port(p_cb, p_pcb); + if (p_pcb_new_listen) { + evt_data.rfc_srv_open.new_listen_handle = p_pcb_new_listen->handle; + new_user_data = p_cb->p_cback(BTA_JV_RFCOMM_SRV_OPEN_EVT, &evt_data, user_data); + if (new_user_data) { + p_pcb_new_listen->user_data = new_user_data; + APPL_TRACE_DEBUG("PORT_SUCCESS: curr_sess:%d, max_sess:%d", p_cb->curr_sess, + p_cb->max_sess); + } else { + /** + * new_user_data is NULL, which means the upper layer runs out of slot source. + * Tells the caller to reject this connection. + */ + APPL_TRACE_ERROR("create new listen port, but upper layer reject connection"); + p_pcb_new_listen->user_data = NULL; + p_pcb->state = BTA_JV_ST_SR_LISTEN; + bta_jv_free_rfc_cb(p_cb, p_pcb_new_listen, FALSE, FALSE); + if (p_mgmt_cb_arg) { + p_mgmt_cb_arg->accept = FALSE; + } + } + } else { + evt_data.rfc_srv_open.new_listen_handle = 0; + new_user_data = p_cb->p_cback(BTA_JV_RFCOMM_SRV_OPEN_EVT, &evt_data, user_data); + if (new_user_data) { + APPL_TRACE_DEBUG("PORT_SUCCESS: curr_sess:%d, max_sess:%d", p_cb->curr_sess, + p_cb->max_sess); + } else { + /** + * new_user_data is NULL, which means the upper layer runs out of slot source. + * Tells the caller to reject this connection. + */ + APPL_TRACE_ERROR("no listen port, and upper layer reject connection"); + p_pcb->state = BTA_JV_ST_SR_LISTEN; + if (p_mgmt_cb_arg) { + p_mgmt_cb_arg->accept = FALSE; + } + } + APPL_TRACE_ERROR("bta_jv_add_rfc_port failed to create new listen port"); + } + } + + if (failed) { + evt_data.rfc_close.handle = p_pcb->handle; + evt_data.rfc_close.status = BTA_JV_FAILURE; + evt_data.rfc_close.async = TRUE; + evt_data.rfc_close.port_status = code; + p_pcb->cong = FALSE; + + tBTA_JV_RFCOMM_CBACK *p_cback = p_cb->p_cback; + APPL_TRACE_DEBUG("PORT_CLOSED before BTA_JV_RFCOMM_CLOSE_EVT: curr_sess:%d, max_sess:%d", + p_cb->curr_sess, p_cb->max_sess); + if (BTA_JV_ST_SR_CLOSING == p_pcb->state) { + evt_data.rfc_close.async = FALSE; + evt_data.rfc_close.status = BTA_JV_SUCCESS; + } + + // To free up resources. + p_pcb->state = BTA_JV_ST_SR_CLOSING; + bta_jv_free_rfc_cb(p_cb, p_pcb, FALSE, FALSE); + p_cback(BTA_JV_RFCOMM_CLOSE_EVT, &evt_data, user_data); + APPL_TRACE_DEBUG("PORT_CLOSED after BTA_JV_RFCOMM_CLOSE_EVT: curr_sess:%d, max_sess:%d", + p_cb->curr_sess, p_cb->max_sess); + } +} + +/******************************************************************************* +** +** Function bta_jv_port_event_sr_cback +** +** Description Callback for RFCOMM server port events +** +** Returns void +** +*******************************************************************************/ +static void bta_jv_port_event_sr_cback(UINT32 code, UINT16 port_handle) +{ + tBTA_JV_PCB *p_pcb = bta_jv_rfc_port_to_pcb(port_handle); + tBTA_JV_RFC_CB *p_cb = bta_jv_rfc_port_to_cb(port_handle); + tBTA_JV evt_data = {0}; + + if (NULL == p_cb || NULL == p_cb->p_cback) { + return; + } + + APPL_TRACE_DEBUG( "bta_jv_port_event_sr_cback code=x%x port_handle:%d handle:%d", + code, port_handle, p_cb->handle); + + void *user_data = p_pcb->user_data; + if (code & PORT_EV_RXCHAR) { + evt_data.data_ind.handle = p_pcb->handle; + p_cb->p_cback(BTA_JV_RFCOMM_DATA_IND_EVT, &evt_data, user_data); + } + + if (code & PORT_EV_FC) { + p_pcb->cong = (code & PORT_EV_FCS) ? FALSE : TRUE; + evt_data.rfc_cong.cong = p_pcb->cong; + evt_data.rfc_cong.handle = p_pcb->handle; + evt_data.rfc_cong.status = BTA_JV_SUCCESS; + p_cb->p_cback(BTA_JV_RFCOMM_CONG_EVT, &evt_data, user_data); + } + + if (code & PORT_EV_TXEMPTY) { + bta_jv_pm_conn_idle(p_pcb->p_pm_cb); + } +} + +/******************************************************************************* +** +** Function bta_jv_add_rfc_port +** +** Description add a port for server when the existing posts is open +** +** Returns return a pointer to tBTA_JV_PCB just added +** +*******************************************************************************/ +static tBTA_JV_PCB *bta_jv_add_rfc_port(tBTA_JV_RFC_CB *p_cb, tBTA_JV_PCB *p_pcb_open) +{ + UINT8 used = 0, i, listen = 0; + UINT32 si = 0; + tPORT_STATE port_state; + UINT32 event_mask = BTA_JV_RFC_EV_MASK; + tBTA_JV_PCB *p_pcb = NULL; + if (p_cb->max_sess > 1) { + for (i = 0; i < p_cb->max_sess; i++) { + if (p_cb->rfc_hdl[i] != 0) { + p_pcb = &bta_jv_cb.port_cb[p_cb->rfc_hdl[i] - 1]; + if (p_pcb->state == BTA_JV_ST_SR_LISTEN) { + listen++; + if (p_pcb_open == p_pcb) { + APPL_TRACE_DEBUG("bta_jv_add_rfc_port, port_handle:%d, change the listen port to open state", + p_pcb->port_handle); + p_pcb->state = BTA_JV_ST_SR_OPEN; + + } else { + APPL_TRACE_ERROR("bta_jv_add_rfc_port, open pcb not matching listen one," + "listen count:%d, listen pcb handle:%d, open pcb:%d", + listen, p_pcb->port_handle, p_pcb_open->handle); + return NULL; + } + } + used++; + } else if (si == 0) { + si = i + 1; + } + } + + p_pcb = NULL; + APPL_TRACE_DEBUG("bta_jv_add_rfc_port max_sess=%d used:%d curr_sess:%d, listen:%d si:%d", + p_cb->max_sess, used, p_cb->curr_sess, listen, si); + if (used < p_cb->max_sess && listen == 1 && si) { + si--; + if (RFCOMM_CreateConnection(p_cb->sec_id, p_cb->scn, TRUE, + BTA_JV_DEF_RFC_MTU, (UINT8 *) bd_addr_any, &(p_cb->rfc_hdl[si]), bta_jv_port_mgmt_sr_cback) == PORT_SUCCESS) { + p_cb->curr_sess++; + p_pcb = &bta_jv_cb.port_cb[p_cb->rfc_hdl[si] - 1]; + p_pcb->state = BTA_JV_ST_SR_LISTEN; + p_pcb->port_handle = p_cb->rfc_hdl[si]; + p_pcb->user_data = p_pcb_open->user_data; + + PORT_ClearKeepHandleFlag(p_pcb->port_handle); + PORT_SetEventCallback(p_pcb->port_handle, bta_jv_port_event_sr_cback); + PORT_SetDataCOCallback (p_pcb->port_handle, bta_jv_port_data_co_cback); + PORT_SetEventMask(p_pcb->port_handle, event_mask); + PORT_GetState(p_pcb->port_handle, &port_state); + + port_state.fc_type = (PORT_FC_CTS_ON_INPUT | PORT_FC_CTS_ON_OUTPUT); + + PORT_SetState(p_pcb->port_handle, &port_state); + p_pcb->handle = BTA_JV_RFC_H_S_TO_HDL(p_cb->handle, si); + APPL_TRACE_DEBUG("bta_jv_add_rfc_port: p_pcb->handle:0x%x, curr_sess:%d", + p_pcb->handle, p_cb->curr_sess); + } + } else { + /* avoid p_pcb always points to the last element of rfc_hdl */ + APPL_TRACE_ERROR("bta_jv_add_rfc_port, cannot create new rfc listen port"); + } + } + APPL_TRACE_DEBUG("bta_jv_add_rfc_port: sec id in use:%d, rfc_cb in use:%d", + get_sec_id_used(), get_rfc_cb_used()); + return p_pcb; +} + +/******************************************************************************* +** +** Function bta_jv_rfcomm_start_server +** +** Description waits for an RFCOMM client to connect +** +** +** Returns void +** +*******************************************************************************/ +void bta_jv_rfcomm_start_server(tBTA_JV_MSG *p_data) +{ + UINT16 handle = 0; + UINT32 event_mask = BTA_JV_RFC_EV_MASK; + tPORT_STATE port_state; + UINT8 sec_id = 0; + tBTA_JV_RFC_CB *p_cb = NULL; + tBTA_JV_PCB *p_pcb; + tBTA_JV_API_RFCOMM_SERVER *rs = &(p_data->rfcomm_server); + tBTA_JV_RFCOMM_START evt_data = {0}; + /* TODO DM role manager + L2CA_SetDesireRole(rs->role); + */ + evt_data.status = BTA_JV_FAILURE; + APPL_TRACE_DEBUG("bta_jv_rfcomm_start_server: sec id in use:%d, rfc_cb in use:%d", + get_sec_id_used(), get_rfc_cb_used()); + + do { + sec_id = bta_jv_alloc_sec_id(); + + if (0 == sec_id || + BTM_SetSecurityLevel(FALSE, "JV PORT", sec_id, rs->sec_mask, + BT_PSM_RFCOMM, BTM_SEC_PROTO_RFCOMM, rs->local_scn) == FALSE) { + APPL_TRACE_ERROR("bta_jv_rfcomm_start_server, run out of sec_id"); + break; + } + + if (RFCOMM_CreateConnection(sec_id, rs->local_scn, TRUE, + BTA_JV_DEF_RFC_MTU, (UINT8 *) bd_addr_any, &handle, bta_jv_port_mgmt_sr_cback) != PORT_SUCCESS) { + APPL_TRACE_ERROR("bta_jv_rfcomm_start_server, RFCOMM_CreateConnection failed"); + break; + } + + + p_cb = bta_jv_alloc_rfc_cb(handle, &p_pcb); + if (!p_cb) { + APPL_TRACE_ERROR("bta_jv_rfcomm_start_server, run out of rfc control block"); + break; + } + + p_cb->max_sess = rs->max_session; + p_cb->p_cback = rs->p_cback; + p_cb->sec_id = sec_id; + p_cb->scn = rs->local_scn; + p_pcb->state = BTA_JV_ST_SR_LISTEN; + p_pcb->user_data = rs->user_data; + evt_data.status = BTA_JV_SUCCESS; + evt_data.handle = p_pcb->handle; + evt_data.sec_id = sec_id; + evt_data.scn = rs->local_scn; + evt_data.use_co = TRUE; + + PORT_ClearKeepHandleFlag(handle); + PORT_SetEventCallback(handle, bta_jv_port_event_sr_cback); + PORT_SetEventMask(handle, event_mask); + PORT_GetState(handle, &port_state); + + port_state.fc_type = (PORT_FC_CTS_ON_INPUT | PORT_FC_CTS_ON_OUTPUT); + + PORT_SetState(handle, &port_state); + } while (0); + + rs->p_cback(BTA_JV_RFCOMM_START_EVT, (tBTA_JV *)&evt_data, rs->user_data); + if (evt_data.status == BTA_JV_SUCCESS) { + PORT_SetDataCOCallback (handle, bta_jv_port_data_co_cback); + } else { + if (sec_id) { + bta_jv_free_sec_id(&sec_id); + } + if (handle) { + RFCOMM_RemoveConnection(handle); + } + } +} + +/******************************************************************************* +** +** Function bta_jv_rfcomm_stop_server +** +** Description stops an RFCOMM server +** +** Returns void +** +*******************************************************************************/ + +void bta_jv_rfcomm_stop_server(tBTA_JV_MSG *p_data) +{ + tBTA_JV_API_RFCOMM_SERVER *ls = &(p_data->rfcomm_server); + tBTA_JV_RFC_CB *p_cb = NULL; + tBTA_JV_PCB *p_pcb = NULL; + APPL_TRACE_DEBUG("bta_jv_rfcomm_stop_server"); + if (!ls->handle) { + APPL_TRACE_ERROR("bta_jv_rfcomm_stop_server, jv handle is null"); + return; + } + void *user_data = ls->user_data; + if (!find_rfc_pcb(user_data, &p_cb, &p_pcb)) { + return; + } + APPL_TRACE_DEBUG("bta_jv_rfcomm_stop_server: p_pcb:%p, p_pcb->port_handle:%d", + p_pcb, p_pcb->port_handle); + bta_jv_free_rfc_cb(p_cb, p_pcb, TRUE, FALSE); + APPL_TRACE_DEBUG("bta_jv_rfcomm_stop_server: sec id in use:%d, rfc_cb in use:%d", + get_sec_id_used(), get_rfc_cb_used()); +} + +/******************************************************************************* +** +** Function bta_jv_rfcomm_read +** +** Description Read data from an RFCOMM connection +** +** Returns void +** +*******************************************************************************/ +void bta_jv_rfcomm_read(tBTA_JV_MSG *p_data) +{ + tBTA_JV_API_RFCOMM_READ *rc = &(p_data->rfcomm_read); + tBTA_JV_RFC_CB *p_cb = rc->p_cb; + tBTA_JV_PCB *p_pcb = rc->p_pcb; + tBTA_JV_RFCOMM_READ evt_data; + + evt_data.status = BTA_JV_FAILURE; + evt_data.handle = p_pcb->handle; + evt_data.req_id = rc->req_id; + evt_data.p_data = rc->p_data; + if (PORT_ReadData(rc->p_pcb->port_handle, (char *)rc->p_data, rc->len, &evt_data.len) == + PORT_SUCCESS) { + evt_data.status = BTA_JV_SUCCESS; + } + + p_cb->p_cback(BTA_JV_RFCOMM_READ_EVT, (tBTA_JV *)&evt_data, p_pcb->user_data); +} + +/******************************************************************************* +** +** Function bta_jv_rfcomm_write +** +** Description write data to an RFCOMM connection +** +** Returns void +** +*******************************************************************************/ +void bta_jv_rfcomm_write(tBTA_JV_MSG *p_data) +{ + tBTA_JV_API_RFCOMM_WRITE *wc = &(p_data->rfcomm_write); + tBTA_JV_RFC_CB *p_cb = wc->p_cb; + tBTA_JV_PCB *p_pcb = wc->p_pcb; + tBTA_JV_RFCOMM_WRITE evt_data; + + evt_data.status = BTA_JV_FAILURE; + evt_data.handle = p_pcb->handle; + evt_data.req_id = wc->req_id; + evt_data.old_cong = p_pcb->cong; + bta_jv_pm_conn_busy(p_pcb->p_pm_cb); + evt_data.len = -1; + if (!evt_data.old_cong && + PORT_WriteDataCO(p_pcb->port_handle, &evt_data.len, wc->len, wc->p_data) == + PORT_SUCCESS) { + evt_data.status = BTA_JV_SUCCESS; + } + // update congestion flag + evt_data.cong = p_pcb->cong; + if (p_cb->p_cback) { + p_cb->p_cback(BTA_JV_RFCOMM_WRITE_EVT, (tBTA_JV *)&evt_data, p_pcb->user_data); + } else { + APPL_TRACE_ERROR("bta_jv_rfcomm_write :: WARNING ! No JV callback set"); + } + +} + +/******************************************************************************* +** +** Function bta_jv_rfcomm_flow_control +** +** Description give credits to the peer +** +** Returns void +** +*******************************************************************************/ +void bta_jv_rfcomm_flow_control(tBTA_JV_MSG *p_data) +{ + tBTA_JV_API_RFCOMM_FLOW_CONTROL *fc = &(p_data->rfcomm_fc); + + tBTA_JV_PCB *p_pcb = fc->p_pcb; + PORT_FlowControl_GiveCredit(p_pcb->port_handle, TRUE, fc->credits_given); +} +#endif /* BTA_JV_RFCOMM_INCLUDED */ + +/******************************************************************************* + ** + ** Function bta_jv_set_pm_profile + ** + ** Description Set or free power mode profile for a JV application + ** + ** Returns void + ** + *******************************************************************************/ +void bta_jv_set_pm_profile(tBTA_JV_MSG *p_data) +{ + tBTA_JV_STATUS status; + tBTA_JV_PM_CB *p_cb; + + APPL_TRACE_API("bta_jv_set_pm_profile(handle: 0x%x, app_id: %d, init_st: %d)", + p_data->set_pm.handle, p_data->set_pm.app_id, p_data->set_pm.init_st); + + /* clear PM control block */ + if (p_data->set_pm.app_id == BTA_JV_PM_ID_CLEAR) { + status = bta_jv_free_set_pm_profile_cb(p_data->set_pm.handle); + + if (status != BTA_JV_SUCCESS) { + APPL_TRACE_WARNING("bta_jv_set_pm_profile() free pm cb failed: reason %d", + status); + } + } else { /* set PM control block */ + p_cb = bta_jv_alloc_set_pm_profile_cb(p_data->set_pm.handle, + p_data->set_pm.app_id); + + if (NULL != p_cb) { + bta_jv_pm_state_change(p_cb, p_data->set_pm.init_st); + } else { + APPL_TRACE_WARNING("bta_jv_alloc_set_pm_profile_cb() failed"); + } + } +} + +/******************************************************************************* + ** + ** Function bta_jv_change_pm_state + ** + ** Description change jv pm connect state, used internally + ** + ** Returns void + ** + *******************************************************************************/ +void bta_jv_change_pm_state(tBTA_JV_MSG *p_data) +{ + tBTA_JV_API_PM_STATE_CHANGE *p_msg = (tBTA_JV_API_PM_STATE_CHANGE *)p_data; + + if (p_msg->p_cb) { + bta_jv_pm_state_change(p_msg->p_cb, p_msg->state); + } +} + + +/******************************************************************************* + ** + ** Function bta_jv_set_pm_conn_state + ** + ** Description Send pm event state change to jv state machine to serialize jv pm changes + ** in relation to other jv messages. internal API use mainly. + ** + ** Params: p_cb: jv pm control block, NULL pointer returns failure + ** new_state: new PM connections state, setting is forced by action function + ** + ** Returns BTA_JV_SUCCESS, BTA_JV_FAILURE (buffer allocation, or NULL ptr!) + ** + *******************************************************************************/ +tBTA_JV_STATUS bta_jv_set_pm_conn_state(tBTA_JV_PM_CB *p_cb, const tBTA_JV_CONN_STATE + new_st) +{ + tBTA_JV_STATUS status = BTA_JV_FAILURE; + tBTA_JV_API_PM_STATE_CHANGE *p_msg; + + if (NULL == p_cb) { + return status; + } + + APPL_TRACE_API("bta_jv_set_pm_conn_state(handle:0x%x, state: %d)", p_cb->handle, + new_st); + if ((p_msg = (tBTA_JV_API_PM_STATE_CHANGE *)osi_malloc( + sizeof(tBTA_JV_API_PM_STATE_CHANGE))) != NULL) { + p_msg->hdr.event = BTA_JV_API_PM_STATE_CHANGE_EVT; + p_msg->p_cb = p_cb; + p_msg->state = new_st; + bta_sys_sendmsg(p_msg); + status = BTA_JV_SUCCESS; + } + return (status); +} + +/******************************************************************************* + ** + ** Function bta_jv_pm_conn_busy + ** + ** Description set pm connection busy state (input param safe) + ** + ** Params p_cb: pm control block of jv connection + ** + ** Returns void + ** + *******************************************************************************/ +static void bta_jv_pm_conn_busy(tBTA_JV_PM_CB *p_cb) +{ + if ((NULL != p_cb) && (BTA_JV_PM_IDLE_ST == p_cb->state)) { + bta_jv_pm_state_change(p_cb, BTA_JV_CONN_BUSY); + } +} + +/******************************************************************************* + ** + ** Function bta_jv_pm_conn_busy + ** + ** Description set pm connection busy state (input param safe) + ** + ** Params p_cb: pm control block of jv connection + ** + ** Returns void + ** + *******************************************************************************/ +static void bta_jv_pm_conn_idle(tBTA_JV_PM_CB *p_cb) +{ + if ((NULL != p_cb) && (BTA_JV_PM_IDLE_ST != p_cb->state)) { + bta_jv_pm_state_change(p_cb, BTA_JV_CONN_IDLE); + } +} + +/******************************************************************************* + ** + ** Function bta_jv_pm_state_change + ** + ** Description Notify power manager there is state change + ** + ** Params p_cb: must be NONE NULL + ** + ** Returns void + ** + *******************************************************************************/ +static void bta_jv_pm_state_change(tBTA_JV_PM_CB *p_cb, const tBTA_JV_CONN_STATE state) +{ + APPL_TRACE_API("bta_jv_pm_state_change(p_cb: 0x%x, handle: 0x%x, busy/idle_state: %d" + ", app_id: %d, conn_state: %d)", (uint32_t)p_cb, p_cb->handle, p_cb->state, + p_cb->app_id, state); + + switch (state) { + case BTA_JV_CONN_OPEN: + bta_sys_conn_open(BTA_ID_JV, p_cb->app_id, p_cb->peer_bd_addr); + break; + + case BTA_JV_CONN_CLOSE: + bta_sys_conn_close(BTA_ID_JV, p_cb->app_id, p_cb->peer_bd_addr); + break; + + case BTA_JV_APP_OPEN: + bta_sys_app_open(BTA_ID_JV, p_cb->app_id, p_cb->peer_bd_addr); + break; + + case BTA_JV_APP_CLOSE: + bta_sys_app_close(BTA_ID_JV, p_cb->app_id, p_cb->peer_bd_addr); + break; + + case BTA_JV_SCO_OPEN: + bta_sys_sco_open(BTA_ID_JV, p_cb->app_id, p_cb->peer_bd_addr); + break; + + case BTA_JV_SCO_CLOSE: + bta_sys_sco_close(BTA_ID_JV, p_cb->app_id, p_cb->peer_bd_addr); + break; + + case BTA_JV_CONN_IDLE: + p_cb->state = BTA_JV_PM_IDLE_ST; + bta_sys_idle(BTA_ID_JV, p_cb->app_id, p_cb->peer_bd_addr); + break; + + case BTA_JV_CONN_BUSY: + p_cb->state = BTA_JV_PM_BUSY_ST; + bta_sys_busy(BTA_ID_JV, p_cb->app_id, p_cb->peer_bd_addr); + break; + + default: + APPL_TRACE_WARNING("bta_jv_pm_state_change(state: %d): Invalid state", state); + break; + } +} +/**********************************************************************************************/ + +#if BTA_JV_L2CAP_INCLUDED +static struct fc_channel *fcchan_get(uint16_t chan, char create) +{ + struct fc_channel *t = fc_channels; + static tL2CAP_FIXED_CHNL_REG fcr = { + .pL2CA_FixedConn_Cb = fcchan_conn_chng_cbk, + .pL2CA_FixedData_Cb = fcchan_data_cbk, + .default_idle_tout = 0xffff, + .fixed_chnl_opts = { + .mode = L2CAP_FCR_BASIC_MODE, + .max_transmit = 0xFF, + .rtrans_tout = 2000, + .mon_tout = 12000, + .mps = 670, + .tx_win_sz = 1, + }, + }; + + while (t && t->chan != chan) { + t = t->next; + } + + if (t) { + return t; + } else if (!create) { + return NULL; /* we cannot alloc a struct if not asked to */ + } + + t = osi_calloc(sizeof(*t)); + if (!t) { + return NULL; + } + + t->chan = chan; + + if (!L2CA_RegisterFixedChannel(chan, &fcr)) { + osi_free(t); + return NULL; + } + + //link it in + t->next = fc_channels; + fc_channels = t; + + return t; +} + +/* pass NULL to find servers */ +static struct fc_client *fcclient_find_by_addr(struct fc_client *start, BD_ADDR addr) +{ + struct fc_client *t = start; + + while (t) { + + /* match client if have addr */ + if (addr && !memcmp(addr, &t->remote_addr, sizeof(t->remote_addr))) { + break; + } + + /* match server if do not have addr */ + if (!addr && t->server) { + break; + } + + t = t->next_all_list; + } + + return t; +} + +static struct fc_client *fcclient_find_by_id(uint32_t id) +{ + struct fc_client *t = fc_clients; + + while (t && t->id != id) { + t = t->next_all_list; + } + + return t; +} + +static struct fc_client *fcclient_alloc(uint16_t chan, char server, const uint8_t *sec_id_to_use) +{ + struct fc_channel *fc = fcchan_get(chan, TRUE); + struct fc_client *t; + uint8_t sec_id; + + if (!fc) { + return NULL; + } + + if (fc->has_server && server) { + return NULL; /* no way to have multiple servers on same channel */ + } + + if (sec_id_to_use) { + sec_id = *sec_id_to_use; + } else { + sec_id = bta_jv_alloc_sec_id(); + } + + t = osi_calloc(sizeof(*t)); + if (t) { + //allocate it a unique ID + do { + t->id = ++fc_next_id; + } while (!t->id || fcclient_find_by_id(t->id)); + + //populate some params + t->chan = chan; + t->server = server; + + //get a security id + t->sec_id = sec_id; + + //link it in to global list + t->next_all_list = fc_clients; + fc_clients = t; + + //link it in to channel list + t->next_chan_list = fc->clients; + fc->clients = t; + + //update channel if needed + if (server) { + fc->has_server = TRUE; + } + } else if (!sec_id_to_use) { + bta_jv_free_sec_id(&sec_id); + } + + return t; +} + +static void fcclient_free(struct fc_client *fc) +{ + struct fc_client *t = fc_clients; + struct fc_channel *tc = fcchan_get(fc->chan, FALSE); + + //remove from global list + while (t && t->next_all_list != fc) { + t = t->next_all_list; + } + + if (!t && fc != fc_clients) { + return; /* prevent double-free */ + } + + if (t) { + t->next_all_list = fc->next_all_list; + } else { + fc_clients = fc->next_all_list; + } + + //remove from channel list + if (tc) { + t = tc->clients; + + while (t && t->next_chan_list != fc) { + t = t->next_chan_list; + } + + if (t) { + t->next_chan_list = fc->next_chan_list; + } else { + tc->clients = fc->next_chan_list; + } + + //if was server then channel no longer has a server + if (fc->server) { + tc->has_server = FALSE; + } + } + + //free security id + bta_jv_free_sec_id(&fc->sec_id); + + osi_free(fc); +} + +static void fcchan_conn_chng_cbk(UINT16 chan, BD_ADDR bd_addr, BOOLEAN connected, UINT16 reason, tBT_TRANSPORT transport) +{ + tBTA_JV init_evt; + tBTA_JV open_evt; + struct fc_channel *tc; + struct fc_client *t = NULL, *new_conn; + tBTA_JV_L2CAP_CBACK *p_cback = NULL; + char call_init = FALSE; + void *user_data = NULL; + + + tc = fcchan_get(chan, FALSE); + if (tc) { + t = fcclient_find_by_addr(tc->clients, bd_addr); // try to find an open socked for that addr + if (t) { + p_cback = t->p_cback; + user_data = t->user_data; + } else { + t = fcclient_find_by_addr(tc->clients, NULL); // try to find a listening socked for that channel + if (t) { + //found: create a normal connection socket and assign the connection to it + new_conn = fcclient_alloc(chan, FALSE, &t->sec_id); + if (new_conn) { + + memcpy(&new_conn->remote_addr, bd_addr, sizeof(new_conn->remote_addr)); + new_conn->p_cback = NULL; //for now + new_conn->init_called = TRUE; /*nop need to do it again */ + + p_cback = t->p_cback; + user_data = t->user_data; + + t = new_conn; + } + } else { + //drop it + return; + } + } + } + + if (t) { + + if (!t->init_called) { + + call_init = TRUE; + t->init_called = TRUE; + + init_evt.l2c_cl_init.handle = t->id; + init_evt.l2c_cl_init.status = BTA_JV_SUCCESS; + init_evt.l2c_cl_init.sec_id = t->sec_id; + } + + open_evt.l2c_open.handle = t->id; + open_evt.l2c_open.tx_mtu = 23; /* 23, why not ?*/ + memcpy(&open_evt.l2c_le_open.rem_bda, &t->remote_addr, sizeof(open_evt.l2c_le_open.rem_bda)); + open_evt.l2c_le_open.p_p_cback = (void **)&t->p_cback; + open_evt.l2c_le_open.p_user_data = &t->user_data; + open_evt.l2c_le_open.status = BTA_JV_SUCCESS; + + if (connected) { + open_evt.l2c_open.status = BTA_JV_SUCCESS; + } else { + fcclient_free(t); + open_evt.l2c_open.status = BTA_JV_FAILURE; + } + } + + if (call_init) { + p_cback(BTA_JV_L2CAP_CL_INIT_EVT, &init_evt, user_data); + } + + //call this with lock taken so socket does not disappear from under us */ + if (p_cback) { + p_cback(BTA_JV_L2CAP_OPEN_EVT, &open_evt, user_data); + if (!t->p_cback) { /* no callback set, means they do not want this one... */ + fcclient_free(t); + } + } +} + +static void fcchan_data_cbk(UINT16 chan, BD_ADDR bd_addr, BT_HDR *p_buf) +{ + tBTA_JV evt_data = {0}; + // tBTA_JV evt_open; + struct fc_channel *tc; + struct fc_client *t = NULL; + tBTA_JV_L2CAP_CBACK *sock_cback = NULL; + void *sock_user_data; + + tc = fcchan_get(chan, FALSE); + if (tc) { + t = fcclient_find_by_addr(tc->clients, bd_addr); // try to find an open socked for that addr and channel + if (!t) { + //no socket -> drop it + return; + } + } + + sock_cback = t->p_cback; + sock_user_data = t->user_data; + evt_data.le_data_ind.handle = t->id; + evt_data.le_data_ind.p_buf = p_buf; + + if (sock_cback) { + sock_cback(BTA_JV_L2CAP_DATA_IND_EVT, &evt_data, sock_user_data); + } +} + + +/******************************************************************************* +** +** Function bta_jv_l2cap_connect_le +** +** Description makes an le l2cap client connection +** +** Returns void +** +*******************************************************************************/ +void bta_jv_l2cap_connect_le(tBTA_JV_MSG *p_data) +{ + tBTA_JV_API_L2CAP_CONNECT *cc = &(p_data->l2cap_connect); + tBTA_JV evt; + uint32_t id; + char call_init_f = TRUE; + struct fc_client *t; + + evt.l2c_cl_init.handle = GAP_INVALID_HANDLE; + evt.l2c_cl_init.status = BTA_JV_FAILURE; + + t = fcclient_alloc(cc->remote_chan, FALSE, NULL); + if (!t) { + cc->p_cback(BTA_JV_L2CAP_CL_INIT_EVT, &evt, cc->user_data); + return; + } + + t->p_cback = cc->p_cback; + t->user_data = cc->user_data; + memcpy(&t->remote_addr, &cc->peer_bd_addr, sizeof(t->remote_addr)); + id = t->id; + t->init_called = FALSE; + + if (L2CA_ConnectFixedChnl(t->chan, t->remote_addr, BLE_ADDR_UNKNOWN_TYPE, FALSE)) { + + evt.l2c_cl_init.status = BTA_JV_SUCCESS; + evt.l2c_cl_init.handle = id; + } + + //it could have been deleted/moved from under us, so re-find it */ + t = fcclient_find_by_id(id); + if (t) { + if (evt.l2c_cl_init.status == BTA_JV_SUCCESS) { + call_init_f = !t->init_called; + } else { + fcclient_free(t); + } + } + if (call_init_f) { + cc->p_cback(BTA_JV_L2CAP_CL_INIT_EVT, &evt, cc->user_data); + } + t->init_called = TRUE; +} + + +/******************************************************************************* +** +** Function bta_jv_l2cap_stop_server_le +** +** Description stops an LE L2CAP server +** +** Returns void +** +*******************************************************************************/ +void bta_jv_l2cap_stop_server_le(tBTA_JV_MSG *p_data) +{ + tBTA_JV evt; + tBTA_JV_API_L2CAP_SERVER *ls = &(p_data->l2cap_server); + tBTA_JV_L2CAP_CBACK *p_cback = NULL; + struct fc_channel *fcchan; + struct fc_client *fcclient; + void *user_data; + + evt.l2c_close.status = BTA_JV_FAILURE; + evt.l2c_close.async = FALSE; + evt.l2c_close.handle = GAP_INVALID_HANDLE; + + fcchan = fcchan_get(ls->local_chan, FALSE); + if (fcchan) { + while ((fcclient = fcchan->clients)) { + p_cback = fcclient->p_cback; + user_data = fcclient->user_data; + + evt.l2c_close.handle = fcclient->id; + evt.l2c_close.status = BTA_JV_SUCCESS; + evt.l2c_close.async = FALSE; + + fcclient_free(fcclient); + + if (p_cback) { + p_cback(BTA_JV_L2CAP_CLOSE_EVT, &evt, user_data); + } + } + } +} + +/******************************************************************************* +** +** Function bta_jv_l2cap_start_server_le +** +** Description starts an LE L2CAP server +** +** Returns void +** +*******************************************************************************/ +void bta_jv_l2cap_start_server_le(tBTA_JV_MSG *p_data) +{ + tBTA_JV_API_L2CAP_SERVER *ss = &(p_data->l2cap_server); + tBTA_JV_L2CAP_START evt_data; + struct fc_client *t; + // uint16_t handle; + + evt_data.handle = GAP_INVALID_HANDLE; + evt_data.status = BTA_JV_FAILURE; + + + t = fcclient_alloc(ss->local_chan, TRUE, NULL); + if (!t) { + goto out; + } + + t->p_cback = ss->p_cback; + t->user_data = ss->user_data; + + //if we got here, we're registered... + evt_data.status = BTA_JV_SUCCESS; + evt_data.handle = t->id; + evt_data.sec_id = t->sec_id; + +out: + ss->p_cback(BTA_JV_L2CAP_START_EVT, (tBTA_JV *)&evt_data, ss->user_data); +} + +/******************************************************************************* +** +** Function bta_jv_l2cap_close_fixed +** +** Description close a fixed channel connection. calls no callbacks. idempotent +** +** Returns void +** +*******************************************************************************/ +extern void bta_jv_l2cap_close_fixed (tBTA_JV_MSG *p_data) +{ + tBTA_JV_API_L2CAP_CLOSE *cc = &(p_data->l2cap_close); + struct fc_client *t; + + t = fcclient_find_by_id(cc->handle); + if (t) { + fcclient_free(t); + } +} +#endif /* BTA_JV_L2CAP_INCLUDED */ + +#endif ///defined BTA_JV_INCLUDED && BTA_JV_INCLUDED == TRUE diff --git a/lib/bt/host/bluedroid/bta/jv/bta_jv_api.c b/lib/bt/host/bluedroid/bta/jv/bta_jv_api.c new file mode 100644 index 00000000..c0bd066d --- /dev/null +++ b/lib/bt/host/bluedroid/bta/jv/bta_jv_api.c @@ -0,0 +1,1236 @@ +/****************************************************************************** + * + * Copyright (C) 2006-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This is the implementation of the JAVA API for Bluetooth Wireless + * Technology (JABWT) as specified by the JSR82 specificiation + * + ******************************************************************************/ + +#include "bta/bta_api.h" +#include "bta/bta_sys.h" +#include "bta/bta_jv_api.h" +#include "bta_jv_int.h" +#include "osi/allocator.h" +#include +#include "stack/port_api.h" +#include "stack/sdp_api.h" +#include "bta/utl.h" +#include "stack/gap_api.h" + +#include "common/bt_target.h" +#include "stack/sdp_api.h" + + +#if (defined BTA_JV_INCLUDED && BTA_JV_INCLUDED == TRUE) +/***************************************************************************** +** Constants +*****************************************************************************/ + +static const tBTA_SYS_REG bta_jv_reg = { + bta_jv_sm_execute, + NULL +}; + +/******************************************************************************* +** +** Function BTA_JvEnable +** +** Description Enable the Java I/F service. When the enable +** operation is complete the callback function will be +** called with a BTA_JV_ENABLE_EVT. This function must +** be called before other function in the JV API are +** called. +** +** Returns BTA_JV_SUCCESS if successful. +** BTA_JV_FAIL if internal failure. +** +*******************************************************************************/ +tBTA_JV_STATUS BTA_JvEnable(tBTA_JV_DM_CBACK *p_cback) +{ + tBTA_JV_STATUS status = BTA_JV_FAILURE; + tBTA_JV_API_ENABLE *p_buf; + int i; + APPL_TRACE_API( "BTA_JvEnable"); + +#if BTA_DYNAMIC_MEMORY == TRUE + /* Malloc buffer for JV configuration structure */ + p_bta_jv_cfg->p_sdp_raw_data = (UINT8 *)osi_malloc(p_bta_jv_cfg->sdp_raw_size); + p_bta_jv_cfg->p_sdp_db = (tSDP_DISCOVERY_DB *)osi_malloc(p_bta_jv_cfg->sdp_db_size); + if (p_bta_jv_cfg->p_sdp_raw_data == NULL || p_bta_jv_cfg->p_sdp_db == NULL) { + return BTA_JV_NO_DATA; + } +#endif + + if (p_cback && FALSE == bta_sys_is_register(BTA_ID_JV)) { + memset(&bta_jv_cb, 0, sizeof(tBTA_JV_CB)); + /* set handle to invalid value by default */ + for (i = 0; i < BTA_JV_PM_MAX_NUM; i++) { + bta_jv_cb.pm_cb[i].handle = BTA_JV_PM_HANDLE_CLEAR; + } + + /* register with BTA system manager */ + bta_sys_register(BTA_ID_JV, &bta_jv_reg); + + if (p_cback && (p_buf = (tBTA_JV_API_ENABLE *) osi_malloc(sizeof(tBTA_JV_API_ENABLE))) != NULL) { + p_buf->hdr.event = BTA_JV_API_ENABLE_EVT; + p_buf->p_cback = p_cback; + bta_sys_sendmsg(p_buf); + status = BTA_JV_SUCCESS; + } + } else if (p_cback == NULL) { + APPL_TRACE_ERROR("No p_cback."); + } else { + APPL_TRACE_WARNING("No need to Init again."); + status = BTA_JV_ALREADY_DONE; + } + return (status); +} + +/******************************************************************************* +** +** Function BTA_JvDisable +** +** Description Disable the Java I/F +** +** Returns void +** +*******************************************************************************/ +void BTA_JvDisable(tBTA_JV_RFCOMM_CBACK *p_cback) +{ + tBTA_JV_API_DISABLE *p_buf; + + APPL_TRACE_API( "BTA_JvDisable"); + bta_sys_deregister(BTA_ID_JV); + if ((p_buf = (tBTA_JV_API_DISABLE *) osi_malloc(sizeof(tBTA_JV_API_DISABLE))) != NULL) { + p_buf->hdr.event = BTA_JV_API_DISABLE_EVT; + p_buf->p_cback = p_cback; + bta_sys_sendmsg(p_buf); + } +} + +/******************************************************************************* +** +** Function BTA_JvFree +** +** Description Free JV configuration +** +** Returns void +** +*******************************************************************************/ +void BTA_JvFree(void) +{ +#if BTA_DYNAMIC_MEMORY == TRUE + /* Free buffer for JV configuration structure */ + osi_free(p_bta_jv_cfg->p_sdp_raw_data); + osi_free(p_bta_jv_cfg->p_sdp_db); + p_bta_jv_cfg->p_sdp_raw_data = NULL; + p_bta_jv_cfg->p_sdp_db = NULL; +#endif +} + +/******************************************************************************* +** +** Function BTA_JvIsEnable +** +** Description Get the JV registration status. +** +** Returns TRUE, if registered +** +*******************************************************************************/ +BOOLEAN BTA_JvIsEnable(void) +{ + return bta_sys_is_register(BTA_ID_JV); +} + +/******************************************************************************* +** +** Function BTA_JvIsEncrypted +** +** Description This function checks if the link to peer device is encrypted +** +** Returns TRUE if encrypted. +** FALSE if not. +** +*******************************************************************************/ +BOOLEAN BTA_JvIsEncrypted(BD_ADDR bd_addr) +{ + BOOLEAN is_encrypted = FALSE; + UINT8 sec_flags, le_flags; + + if (BTM_GetSecurityFlags(bd_addr, &sec_flags) && + BTM_GetSecurityFlagsByTransport(bd_addr, &le_flags, BT_TRANSPORT_LE)) { + if (sec_flags & BTM_SEC_FLAG_ENCRYPTED || + le_flags & BTM_SEC_FLAG_ENCRYPTED) { + is_encrypted = TRUE; + } + } + return is_encrypted; +} +/******************************************************************************* +** +** Function BTA_JvGetChannelId +** +** Description This function reserves a SCN (server channel number) for +** applications running over RFCOMM, L2CAP of L2CAP_LE. +** It is primarily called by server profiles/applications to +** register their SCN into the SDP database. The SCN is reported +** by the tBTA_JV_DM_CBACK callback with a BTA_JV_GET_SCN_EVT +** for RFCOMM channels and BTA_JV_GET_PSM_EVT for L2CAP and LE. +** If the SCN/PSM reported is 0, that means all resources are +** exhausted. +** Parameters +** conn_type one of BTA_JV_CONN_TYPE_ +** user_data Any uservalue - will be returned in the resulting event. +** channel Only used for RFCOMM - to try to allocate a specific RFCOMM +** channel. +** +** Returns BTA_JV_SUCCESS, if the request is being processed. +** BTA_JV_FAILURE, otherwise. +** +*******************************************************************************/ +tBTA_JV_STATUS BTA_JvGetChannelId(int conn_type, void *user_data, INT32 channel) +{ + tBTA_JV_STATUS status = BTA_JV_FAILURE; + tBTA_JV_API_ALLOC_CHANNEL *p_msg; + + APPL_TRACE_API( "%s", __func__); + if ((p_msg = (tBTA_JV_API_ALLOC_CHANNEL *)osi_malloc(sizeof(tBTA_JV_API_ALLOC_CHANNEL))) != NULL) { + p_msg->hdr.event = BTA_JV_API_GET_CHANNEL_EVT; + p_msg->type = conn_type; + p_msg->channel = channel; + p_msg->user_data = user_data; + bta_sys_sendmsg(p_msg); + status = BTA_JV_SUCCESS; + } + + return (status); +} + +/******************************************************************************* +** +** Function BTA_JvFreeChannel +** +** Description This function frees a server channel number that was used +** by an application running over RFCOMM. +** Parameters +** channel The channel to free +** conn_type one of BTA_JV_CONN_TYPE_ +** p_cback tBTA_JV_RFCOMM_CBACK is called with when server +** user_data indicate the RFCOMM server status +** +** Returns BTA_JV_SUCCESS, if the request is being processed. +** BTA_JV_FAILURE, otherwise. +** +*******************************************************************************/ +tBTA_JV_STATUS BTA_JvFreeChannel(UINT16 channel, int conn_type, tBTA_JV_RFCOMM_CBACK *p_cback, void *user_data) +{ + tBTA_JV_STATUS status = BTA_JV_FAILURE; + tBTA_JV_API_FREE_CHANNEL *p_msg; + + APPL_TRACE_API( "%s", __func__); + if ((p_msg = (tBTA_JV_API_FREE_CHANNEL *)osi_malloc(sizeof(tBTA_JV_API_FREE_CHANNEL))) != NULL) { + p_msg->hdr.event = BTA_JV_API_FREE_SCN_EVT; + p_msg->scn = channel; + p_msg->type = conn_type; + p_msg->p_cback = p_cback; + p_msg->user_data = user_data; + bta_sys_sendmsg(p_msg); + status = BTA_JV_SUCCESS; + } + + return (status); +} + +/******************************************************************************* +** +** Function BTA_JvStartDiscovery +** +** Description This function performs service discovery for the services +** provided by the given peer device. When the operation is +** complete the tBTA_JV_DM_CBACK callback function will be +** called with a BTA_JV_DISCOVERY_COMP_EVT. +** +** Returns BTA_JV_SUCCESS, if the request is being processed. +** BTA_JV_FAILURE, otherwise. +** +*******************************************************************************/ +tBTA_JV_STATUS BTA_JvStartDiscovery(BD_ADDR bd_addr, UINT16 num_uuid, + tSDP_UUID *p_uuid_list, void *user_data) +{ + tBTA_JV_STATUS status = BTA_JV_FAILURE; + tBTA_JV_API_START_DISCOVERY *p_msg; + + APPL_TRACE_API( "BTA_JvStartDiscovery"); + if ((p_msg = (tBTA_JV_API_START_DISCOVERY *)osi_malloc(sizeof(tBTA_JV_API_START_DISCOVERY))) != NULL) { + p_msg->hdr.event = BTA_JV_API_START_DISCOVERY_EVT; + bdcpy(p_msg->bd_addr, bd_addr); + p_msg->num_uuid = num_uuid; + memcpy(p_msg->uuid_list, p_uuid_list, num_uuid * sizeof(tSDP_UUID)); + p_msg->num_attr = 0; + p_msg->user_data = user_data; + bta_sys_sendmsg(p_msg); + status = BTA_JV_SUCCESS; + } + + return (status); +} + +/******************************************************************************* +** +** Function BTA_JvCreateRecord +** +** Description Create a service record in the local SDP database. +** When the operation is complete the tBTA_JV_DM_CBACK callback +** function will be called with a BTA_JV_CREATE_RECORD_EVT. +** +** Returns BTA_JV_SUCCESS, if the request is being processed. +** BTA_JV_FAILURE, otherwise. +** +*******************************************************************************/ +tBTA_JV_STATUS BTA_JvCreateRecordByUser(const char *name, UINT32 channel, void *user_data) +{ + tBTA_JV_STATUS status = BTA_JV_FAILURE; + tBTA_JV_API_CREATE_RECORD *p_msg; + + APPL_TRACE_API( "BTA_JvCreateRecordByUser"); + if ((p_msg = (tBTA_JV_API_CREATE_RECORD *)osi_malloc(sizeof(tBTA_JV_API_CREATE_RECORD))) != NULL) { + p_msg->hdr.event = BTA_JV_API_CREATE_RECORD_EVT; + p_msg->user_data = user_data; + strcpy(p_msg->name, name); + p_msg->channel = channel; + bta_sys_sendmsg(p_msg); + status = BTA_JV_SUCCESS; + } + + return (status); +} + +/******************************************************************************* +** +** Function BTA_JvDeleteRecord +** +** Description Delete a service record in the local SDP database. +** +** Returns BTA_JV_SUCCESS, if the request is being processed. +** BTA_JV_FAILURE, otherwise. +** +*******************************************************************************/ +tBTA_JV_STATUS BTA_JvDeleteRecord(UINT32 handle) +{ + tBTA_JV_STATUS status = BTA_JV_FAILURE; + tBTA_JV_API_ADD_ATTRIBUTE *p_msg; + + APPL_TRACE_API( "BTA_JvDeleteRecord"); + if ((p_msg = (tBTA_JV_API_ADD_ATTRIBUTE *)osi_malloc(sizeof(tBTA_JV_API_ADD_ATTRIBUTE))) != NULL) { + p_msg->hdr.event = BTA_JV_API_DELETE_RECORD_EVT; + p_msg->handle = handle; + bta_sys_sendmsg(p_msg); + status = BTA_JV_SUCCESS; + } + return (status); +} + +#if BTA_JV_L2CAP_INCLUDED +/******************************************************************************* +** +** Function BTA_JvL2capConnectLE +** +** Description Initiate an LE connection as a L2CAP client to the given BD +** Address. +** When the connection is initiated or failed to initiate, +** tBTA_JV_L2CAP_CBACK is called with BTA_JV_L2CAP_CL_INIT_EVT +** When the connection is established or failed, +** tBTA_JV_L2CAP_CBACK is called with BTA_JV_L2CAP_OPEN_EVT +** +** Returns BTA_JV_SUCCESS, if the request is being processed. +** BTA_JV_FAILURE, otherwise. +** +*******************************************************************************/ +tBTA_JV_STATUS BTA_JvL2capConnectLE(tBTA_SEC sec_mask, tBTA_JV_ROLE role, + const tL2CAP_ERTM_INFO *ertm_info, UINT16 remote_chan, + UINT16 rx_mtu, tL2CAP_CFG_INFO *cfg, + BD_ADDR peer_bd_addr, tBTA_JV_L2CAP_CBACK *p_cback, void *user_data) +{ + tBTA_JV_STATUS status = BTA_JV_FAILURE; + tBTA_JV_API_L2CAP_CONNECT *p_msg; + + APPL_TRACE_API( "%s", __func__); + + if (p_cback && + (p_msg = + (tBTA_JV_API_L2CAP_CONNECT *)osi_malloc(sizeof(tBTA_JV_API_L2CAP_CONNECT))) != NULL) { + p_msg->hdr.event = BTA_JV_API_L2CAP_CONNECT_LE_EVT; + p_msg->sec_mask = sec_mask; + p_msg->role = role; + p_msg->remote_chan = remote_chan; + p_msg->rx_mtu = rx_mtu; + if (cfg != NULL) { + p_msg->has_cfg = TRUE; + p_msg->cfg = *cfg; + } else { + p_msg->has_cfg = FALSE; + } + if (ertm_info != NULL) { + p_msg->has_ertm_info = TRUE; + p_msg->ertm_info = *ertm_info; + } else { + p_msg->has_ertm_info = FALSE; + } + memcpy(p_msg->peer_bd_addr, peer_bd_addr, sizeof(BD_ADDR)); + p_msg->p_cback = p_cback; + p_msg->user_data = user_data; + bta_sys_sendmsg(p_msg); + status = BTA_JV_SUCCESS; + } + + return (status); +} + +/******************************************************************************* +** +** Function BTA_JvL2capConnect +** +** Description Initiate a connection as a L2CAP client to the given BD +** Address. +** When the connection is initiated or failed to initiate, +** tBTA_JV_L2CAP_CBACK is called with BTA_JV_L2CAP_CL_INIT_EVT +** When the connection is established or failed, +** tBTA_JV_L2CAP_CBACK is called with BTA_JV_L2CAP_OPEN_EVT +** +** Returns BTA_JV_SUCCESS, if the request is being processed. +** BTA_JV_FAILURE, otherwise. +** +*******************************************************************************/ +tBTA_JV_STATUS BTA_JvL2capConnect(tBTA_SEC sec_mask, tBTA_JV_ROLE role, + const tL2CAP_ERTM_INFO *ertm_info, UINT16 remote_psm, + UINT16 rx_mtu, tL2CAP_CFG_INFO *cfg, + BD_ADDR peer_bd_addr, tBTA_JV_L2CAP_CBACK *p_cback, void *user_data) +{ + tBTA_JV_STATUS status = BTA_JV_FAILURE; + tBTA_JV_API_L2CAP_CONNECT *p_msg; + + APPL_TRACE_API( "%s", __func__); + + if (p_cback && + (p_msg = (tBTA_JV_API_L2CAP_CONNECT *)osi_malloc(sizeof(tBTA_JV_API_L2CAP_CONNECT))) != NULL) { + p_msg->hdr.event = BTA_JV_API_L2CAP_CONNECT_EVT; + p_msg->sec_mask = sec_mask; + p_msg->role = role; + p_msg->remote_psm = remote_psm; + p_msg->rx_mtu = rx_mtu; + if (cfg != NULL) { + p_msg->has_cfg = TRUE; + p_msg->cfg = *cfg; + } else { + p_msg->has_cfg = FALSE; + } + if (ertm_info != NULL) { + p_msg->has_ertm_info = TRUE; + p_msg->ertm_info = *ertm_info; + } else { + p_msg->has_ertm_info = FALSE; + } + memcpy(p_msg->peer_bd_addr, peer_bd_addr, sizeof(BD_ADDR)); + p_msg->p_cback = p_cback; + p_msg->user_data = user_data; + bta_sys_sendmsg(p_msg); + status = BTA_JV_SUCCESS; + } + + return (status); +} + +/******************************************************************************* +** +** Function BTA_JvL2capClose +** +** Description This function closes an L2CAP client connection +** +** Returns BTA_JV_SUCCESS, if the request is being processed. +** BTA_JV_FAILURE, otherwise. +** +*******************************************************************************/ +tBTA_JV_STATUS BTA_JvL2capClose(UINT32 handle, tBTA_JV_L2CAP_CBACK *p_cback, void *user_data) +{ + tBTA_JV_STATUS status = BTA_JV_FAILURE; + tBTA_JV_API_L2CAP_CLOSE *p_msg; + + APPL_TRACE_API( "%s", __func__); + + if (handle < BTA_JV_MAX_L2C_CONN && bta_jv_cb.l2c_cb[handle].p_cback && + (p_msg = (tBTA_JV_API_L2CAP_CLOSE *)osi_malloc(sizeof(tBTA_JV_API_L2CAP_CLOSE))) != NULL) { + p_msg->hdr.event = BTA_JV_API_L2CAP_CLOSE_EVT; + p_msg->handle = handle; + p_msg->p_cb = &bta_jv_cb.l2c_cb[handle]; + p_msg->p_cback = p_cback; + p_msg->user_data = user_data; + bta_sys_sendmsg(p_msg); + status = BTA_JV_SUCCESS; + } + + return (status); +} + +/******************************************************************************* +** +** Function BTA_JvL2capCloseLE +** +** Description This function closes an L2CAP client connection for Fixed Channels +** Function is idempotent and no callbacks are called! +** +** Returns BTA_JV_SUCCESS, if the request is being processed. +** BTA_JV_FAILURE, otherwise. +** +*******************************************************************************/ +tBTA_JV_STATUS BTA_JvL2capCloseLE(UINT32 handle) +{ + tBTA_JV_STATUS status = BTA_JV_FAILURE; + tBTA_JV_API_L2CAP_CLOSE *p_msg; + + APPL_TRACE_API( "%s", __func__); + + if ((p_msg = (tBTA_JV_API_L2CAP_CLOSE *)osi_malloc(sizeof(tBTA_JV_API_L2CAP_CLOSE))) != NULL) { + p_msg->hdr.event = BTA_JV_API_L2CAP_CLOSE_FIXED_EVT; + p_msg->handle = handle; + bta_sys_sendmsg(p_msg); + status = BTA_JV_SUCCESS; + } + + return (status); +} + +/******************************************************************************* +** +** Function BTA_JvL2capStartServer +** +** Description This function starts an L2CAP server and listens for an L2CAP +** connection from a remote Bluetooth device. When the server +** is started successfully, tBTA_JV_L2CAP_CBACK is called with +** BTA_JV_L2CAP_START_EVT. When the connection is established, +** tBTA_JV_L2CAP_CBACK is called with BTA_JV_L2CAP_OPEN_EVT. +** +** Returns BTA_JV_SUCCESS, if the request is being processed. +** BTA_JV_FAILURE, otherwise. +** +*******************************************************************************/ +tBTA_JV_STATUS BTA_JvL2capStartServer(tBTA_SEC sec_mask, tBTA_JV_ROLE role, + const tL2CAP_ERTM_INFO *ertm_info, UINT16 local_psm, UINT16 rx_mtu, tL2CAP_CFG_INFO *cfg, + tBTA_JV_L2CAP_CBACK *p_cback, void *user_data) +{ + tBTA_JV_STATUS status = BTA_JV_FAILURE; + tBTA_JV_API_L2CAP_SERVER *p_msg; + + APPL_TRACE_API( "%s", __func__); + + if (p_cback && + (p_msg = (tBTA_JV_API_L2CAP_SERVER *)osi_malloc(sizeof(tBTA_JV_API_L2CAP_SERVER))) != NULL) { + p_msg->hdr.event = BTA_JV_API_L2CAP_START_SERVER_EVT; + p_msg->sec_mask = sec_mask; + p_msg->role = role; + p_msg->local_psm = local_psm; + p_msg->rx_mtu = rx_mtu; + if (cfg != NULL) { + p_msg->has_cfg = TRUE; + p_msg->cfg = *cfg; + } else { + p_msg->has_cfg = FALSE; + } + if (ertm_info != NULL) { + p_msg->has_ertm_info = TRUE; + p_msg->ertm_info = *ertm_info; + } else { + p_msg->has_ertm_info = FALSE; + } + p_msg->p_cback = p_cback; + p_msg->user_data = user_data; + bta_sys_sendmsg(p_msg); + status = BTA_JV_SUCCESS; + } + + return (status); +} + +/******************************************************************************* +** +** Function BTA_JvL2capStartServerLE +** +** Description This function starts an LE L2CAP server and listens for an L2CAP +** connection from a remote Bluetooth device. When the server +** is started successfully, tBTA_JV_L2CAP_CBACK is called with +** BTA_JV_L2CAP_START_EVT. When the connection is established, +** tBTA_JV_L2CAP_CBACK is called with BTA_JV_L2CAP_OPEN_EVT. +** +** Returns BTA_JV_SUCCESS, if the request is being processed. +** BTA_JV_FAILURE, otherwise. +** +*******************************************************************************/ +tBTA_JV_STATUS BTA_JvL2capStartServerLE(tBTA_SEC sec_mask, tBTA_JV_ROLE role, + const tL2CAP_ERTM_INFO *ertm_info, UINT16 local_chan, UINT16 rx_mtu, tL2CAP_CFG_INFO *cfg, + tBTA_JV_L2CAP_CBACK *p_cback, void *user_data) +{ + tBTA_JV_STATUS status = BTA_JV_FAILURE; + tBTA_JV_API_L2CAP_SERVER *p_msg; + + APPL_TRACE_API( "%s", __func__); + + if (p_cback && + (p_msg = (tBTA_JV_API_L2CAP_SERVER *)osi_malloc(sizeof(tBTA_JV_API_L2CAP_SERVER))) != NULL) { + p_msg->hdr.event = BTA_JV_API_L2CAP_START_SERVER_LE_EVT; + p_msg->sec_mask = sec_mask; + p_msg->role = role; + p_msg->local_chan = local_chan; + p_msg->rx_mtu = rx_mtu; + if (cfg != NULL) { + p_msg->has_cfg = TRUE; + p_msg->cfg = *cfg; + } else { + p_msg->has_cfg = FALSE; + } + if (ertm_info != NULL) { + p_msg->has_ertm_info = TRUE; + p_msg->ertm_info = *ertm_info; + } else { + p_msg->has_ertm_info = FALSE; + } + p_msg->p_cback = p_cback; + p_msg->user_data = user_data; + bta_sys_sendmsg(p_msg); + status = BTA_JV_SUCCESS; + } + + return (status); +} + +/******************************************************************************* +** +** Function BTA_JvL2capStopServer +** +** Description This function stops the L2CAP server. If the server has an +** active connection, it would be closed. +** +** Returns BTA_JV_SUCCESS, if the request is being processed. +** BTA_JV_FAILURE, otherwise. +** +*******************************************************************************/ +tBTA_JV_STATUS BTA_JvL2capStopServer(UINT16 local_psm, void *user_data) +{ + tBTA_JV_STATUS status = BTA_JV_FAILURE; + tBTA_JV_API_L2CAP_SERVER *p_msg; + + APPL_TRACE_API( "%s", __func__); + + if ((p_msg = (tBTA_JV_API_L2CAP_SERVER *)osi_malloc(sizeof(tBTA_JV_API_L2CAP_SERVER))) != NULL) { + p_msg->hdr.event = BTA_JV_API_L2CAP_STOP_SERVER_EVT; + p_msg->local_psm = local_psm; + p_msg->user_data = user_data; + bta_sys_sendmsg(p_msg); + status = BTA_JV_SUCCESS; + } + + return (status); +} + +/******************************************************************************* +** +** Function BTA_JvL2capStopServerLE +** +** Description This function stops the LE L2CAP server. If the server has an +** active connection, it would be closed. +** +** Returns BTA_JV_SUCCESS, if the request is being processed. +** BTA_JV_FAILURE, otherwise. +** +*******************************************************************************/ +tBTA_JV_STATUS BTA_JvL2capStopServerLE(UINT16 local_chan, void *user_data) +{ + tBTA_JV_STATUS status = BTA_JV_FAILURE; + tBTA_JV_API_L2CAP_SERVER *p_msg; + + APPL_TRACE_API( "%s", __func__); + + if ((p_msg = (tBTA_JV_API_L2CAP_SERVER *)osi_malloc(sizeof(tBTA_JV_API_L2CAP_SERVER))) != NULL) { + p_msg->hdr.event = BTA_JV_API_L2CAP_STOP_SERVER_LE_EVT; + p_msg->local_chan = local_chan; + p_msg->user_data = user_data; + bta_sys_sendmsg(p_msg); + status = BTA_JV_SUCCESS; + } + + return (status); +} + +/******************************************************************************* +** +** Function BTA_JvL2capRead +** +** Description This function reads data from an L2CAP connecti; + tBTA_JV_RFC_CB *p_cb = rc->p_cb; +on +** When the operation is complete, tBTA_JV_L2CAP_CBACK is +** called with BTA_JV_L2CAP_READ_EVT. +** +** Returns Length of read data. +** +*******************************************************************************/ +int BTA_JvL2capRead(UINT32 handle, UINT32 req_id, UINT8 *p_data, UINT16 len) +{ + tBTA_JV_L2CAP_READ evt_data; + + APPL_TRACE_API( "%s", __func__); + + if (handle < BTA_JV_MAX_L2C_CONN && bta_jv_cb.l2c_cb[handle].p_cback) { + evt_data.status = BTA_JV_FAILURE; + evt_data.handle = handle; + evt_data.req_id = req_id; + evt_data.p_data = p_data; + evt_data.len = 0; + + if (BT_PASS == GAP_ConnReadData((UINT16)handle, p_data, len, &evt_data.len)) { + evt_data.status = BTA_JV_SUCCESS; + } + bta_jv_cb.l2c_cb[handle].p_cback( + BTA_JV_L2CAP_READ_EVT, (tBTA_JV *)&evt_data, bta_jv_cb.l2c_cb[handle].user_data); + } + + return (evt_data.len); +} + +/******************************************************************************* +** +** Function BTA_JvL2capReceive +** +** Description This function reads data from an L2CAP connection +** When the operation is complete, tBTA_JV_L2CAP_CBACK is +** called with BTA_JV_L2CAP_RECEIVE_EVT. +** If there are more data queued in L2CAP than len, the extra data will be discarded. +** +** Returns BTA_JV_SUCCESS, if the request is being processed. +** BTA_JV_FAILURE, otherwise. +** +*******************************************************************************/ +tBTA_JV_STATUS BTA_JvL2capReceive(UINT32 handle, UINT32 req_id, UINT8 *p_data, UINT16 len) +{ + tBTA_JV_STATUS status = BTA_JV_FAILURE; + tBTA_JV_L2CAP_RECEIVE evt_data; + UINT32 left_over = 0; + UINT16 max_len, read_len; + + APPL_TRACE_API( "%s", __func__); + + + if (handle < BTA_JV_MAX_L2C_CONN && bta_jv_cb.l2c_cb[handle].p_cback) { + status = BTA_JV_SUCCESS; + evt_data.status = BTA_JV_FAILURE; + evt_data.handle = handle; + evt_data.req_id = req_id; + evt_data.p_data = p_data; + evt_data.len = 0; + + if (BT_PASS == GAP_ConnReadData((UINT16)handle, p_data, len, &evt_data.len)) { + evt_data.status = BTA_JV_SUCCESS; + GAP_GetRxQueueCnt ((UINT16)handle, &left_over); + while (left_over) { + max_len = (left_over > 0xFFFF) ? 0xFFFF : left_over; + GAP_ConnReadData ((UINT16)handle, NULL, max_len, &read_len); + left_over -= read_len; + } + } + bta_jv_cb.l2c_cb[handle].p_cback( + BTA_JV_L2CAP_RECEIVE_EVT, (tBTA_JV *)&evt_data, bta_jv_cb.l2c_cb[handle].user_data); + } + + return (status); +} +/******************************************************************************* +** +** Function BTA_JvL2capReady +** +** Description This function determined if there is data to read from +** an L2CAP connection +** +** Returns BTA_JV_SUCCESS, if data queue size is in *p_data_size. +** BTA_JV_FAILURE, if error. +** +*******************************************************************************/ +tBTA_JV_STATUS BTA_JvL2capReady(UINT32 handle, UINT32 *p_data_size) +{ + tBTA_JV_STATUS status = BTA_JV_FAILURE; + + APPL_TRACE_API( "%s: %d", __func__, handle); + if (p_data_size && handle < BTA_JV_MAX_L2C_CONN && bta_jv_cb.l2c_cb[handle].p_cback) { + *p_data_size = 0; + if (BT_PASS == GAP_GetRxQueueCnt((UINT16)handle, p_data_size) ) { + status = BTA_JV_SUCCESS; + } + } + + return (status); +} + + +/******************************************************************************* +** +** Function BTA_JvL2capWrite +** +** Description This function writes data to an L2CAP connection +** When the operation is complete, tBTA_JV_L2CAP_CBACK is +** called with BTA_JV_L2CAP_WRITE_EVT. Works for +** PSM-based connections +** +** Returns BTA_JV_SUCCESS, if the request is being processed. +** BTA_JV_FAILURE, otherwise. +** +*******************************************************************************/ +tBTA_JV_STATUS BTA_JvL2capWrite(UINT32 handle, UINT32 req_id, UINT8 *p_data, + UINT16 len, void *user_data) +{ + tBTA_JV_STATUS status = BTA_JV_FAILURE; + tBTA_JV_API_L2CAP_WRITE *p_msg; + + APPL_TRACE_API( "%s", __func__); + + if (handle < BTA_JV_MAX_L2C_CONN && bta_jv_cb.l2c_cb[handle].p_cback && + (p_msg = (tBTA_JV_API_L2CAP_WRITE *)osi_malloc(sizeof(tBTA_JV_API_L2CAP_WRITE))) != NULL) { + p_msg->hdr.event = BTA_JV_API_L2CAP_WRITE_EVT; + p_msg->handle = handle; + p_msg->req_id = req_id; + p_msg->p_data = p_data; + p_msg->p_cb = &bta_jv_cb.l2c_cb[handle]; + p_msg->len = len; + p_msg->user_data = user_data; + bta_sys_sendmsg(p_msg); + status = BTA_JV_SUCCESS; + } + + return (status); +} + +/******************************************************************************* +** +** Function BTA_JvL2capWriteFixed +** +** Description This function writes data to an L2CAP connection +** When the operation is complete, tBTA_JV_L2CAP_CBACK is +** called with BTA_JV_L2CAP_WRITE_EVT. Works for +** fixed-channel connections +** +** Returns BTA_JV_SUCCESS, if the request is being processed. +** BTA_JV_FAILURE, otherwise. +** +*******************************************************************************/ +tBTA_JV_STATUS BTA_JvL2capWriteFixed(UINT16 channel, BD_ADDR *addr, UINT32 req_id, + tBTA_JV_L2CAP_CBACK *p_cback, UINT8 *p_data, UINT16 len, void *user_data) +{ + tBTA_JV_STATUS status = BTA_JV_FAILURE; + tBTA_JV_API_L2CAP_WRITE_FIXED *p_msg; + + APPL_TRACE_API( "%s", __func__); + + if ((p_msg = + (tBTA_JV_API_L2CAP_WRITE_FIXED *)osi_malloc(sizeof(tBTA_JV_API_L2CAP_WRITE_FIXED))) != NULL) { + p_msg->hdr.event = BTA_JV_API_L2CAP_WRITE_FIXED_EVT; + p_msg->channel = channel; + memcpy(p_msg->addr, addr, sizeof(p_msg->addr)); + p_msg->req_id = req_id; + p_msg->p_data = p_data; + p_msg->p_cback = p_cback; + p_msg->len = len; + p_msg->user_data = user_data; + bta_sys_sendmsg(p_msg); + status = BTA_JV_SUCCESS; + } + + return (status); +} +#endif /* BTA_JV_L2CAP_INCLUDED */ + +#if BTA_JV_RFCOMM_INCLUDED +/******************************************************************************* +** +** Function BTA_JvRfcommConfig +** +** Description This function is to configure RFCOMM. +** +** Returns BTA_JV_SUCCESS, if the request is being processed. +** BTA_JV_FAILURE, otherwise. +** +*******************************************************************************/ +tBTA_JV_STATUS BTA_JvRfcommConfig(BOOLEAN enable_l2cap_ertm) +{ + tBTA_JV_STATUS status = BTA_JV_FAILURE; + tBTA_JV_API_RFCOMM_CONFIG *p_msg; + + APPL_TRACE_API( "%s", __func__); + + if ((p_msg = (tBTA_JV_API_RFCOMM_CONFIG *)osi_malloc(sizeof(tBTA_JV_API_RFCOMM_CONFIG))) != NULL) { + p_msg->hdr.event = BTA_JV_API_RFCOMM_CONFIG_EVT; + p_msg->enable_l2cap_ertm = enable_l2cap_ertm; + bta_sys_sendmsg(p_msg); + status = BTA_JV_SUCCESS; + } + + return (status); +} + +/******************************************************************************* +** +** Function BTA_JvRfcommConnect +** +** Description This function makes an RFCOMM conection to a remote BD +** Address. +** When the connection is initiated or failed to initiate, +** tBTA_JV_RFCOMM_CBACK is called with BTA_JV_RFCOMM_CL_INIT_EVT +** When the connection is established or failed, +** tBTA_JV_RFCOMM_CBACK is called with BTA_JV_RFCOMM_OPEN_EVT +** +** Returns BTA_JV_SUCCESS, if the request is being processed. +** BTA_JV_FAILURE, otherwise. +** +*******************************************************************************/ +tBTA_JV_STATUS BTA_JvRfcommConnect(tBTA_SEC sec_mask, + tBTA_JV_ROLE role, UINT8 remote_scn, BD_ADDR peer_bd_addr, + tBTA_JV_RFCOMM_CBACK *p_cback, void *user_data) +{ + tBTA_JV_STATUS status = BTA_JV_FAILURE; + tBTA_JV_API_RFCOMM_CONNECT *p_msg; + + APPL_TRACE_API( "BTA_JvRfcommConnect"); + if (p_cback && + (p_msg = (tBTA_JV_API_RFCOMM_CONNECT *)osi_malloc(sizeof(tBTA_JV_API_RFCOMM_CONNECT))) != NULL) { + p_msg->hdr.event = BTA_JV_API_RFCOMM_CONNECT_EVT; + p_msg->sec_mask = sec_mask; + p_msg->role = role; + p_msg->remote_scn = remote_scn; + memcpy(p_msg->peer_bd_addr, peer_bd_addr, sizeof(BD_ADDR)); + p_msg->p_cback = p_cback; + p_msg->user_data = user_data; + bta_sys_sendmsg(p_msg); + status = BTA_JV_SUCCESS; + } + + return (status); +} + +/******************************************************************************* +** +** Function BTA_JvRfcommClose +** +** Description This function closes an RFCOMM connection +** When the connection is established or failed, +** tBTA_JV_RFCOMM_CBACK is called with BTA_JV_RFCOMM_CLOSE_EVT +** Returns BTA_JV_SUCCESS, if the request is being processed. +** BTA_JV_FAILURE, otherwise. +** +*******************************************************************************/ +tBTA_JV_STATUS BTA_JvRfcommClose(UINT32 handle, void *user_data) +{ + tBTA_JV_STATUS status = BTA_JV_FAILURE; + tBTA_JV_API_RFCOMM_CLOSE *p_msg; + UINT32 hi = ((handle & BTA_JV_RFC_HDL_MASK) & ~BTA_JV_RFCOMM_MASK) - 1; + UINT32 si = BTA_JV_RFC_HDL_TO_SIDX(handle); + + APPL_TRACE_API( "%s", __func__); + if (hi < BTA_JV_MAX_RFC_CONN && bta_jv_cb.rfc_cb[hi].p_cback && + si < BTA_JV_MAX_RFC_SR_SESSION && bta_jv_cb.rfc_cb[hi].rfc_hdl[si] && + (p_msg = (tBTA_JV_API_RFCOMM_CLOSE *)osi_malloc(sizeof(tBTA_JV_API_RFCOMM_CLOSE))) != NULL) { + p_msg->hdr.event = BTA_JV_API_RFCOMM_CLOSE_EVT; + p_msg->handle = handle; + p_msg->p_cb = &bta_jv_cb.rfc_cb[hi]; + p_msg->p_pcb = &bta_jv_cb.port_cb[p_msg->p_cb->rfc_hdl[si] - 1]; + p_msg->user_data = user_data; + bta_sys_sendmsg(p_msg); + status = BTA_JV_SUCCESS; + } + + return (status); +} + +/******************************************************************************* +** +** Function BTA_JvRfcommStartServer +** +** Description This function starts listening for an RFCOMM connection +** request from a remote Bluetooth device. When the server is +** started successfully, tBTA_JV_RFCOMM_CBACK is called +** with BTA_JV_RFCOMM_START_EVT. +** When the connection is established, tBTA_JV_RFCOMM_CBACK +** is called with BTA_JV_RFCOMM_OPEN_EVT. +** +** Returns BTA_JV_SUCCESS, if the request is being processed. +** BTA_JV_FAILURE, otherwise. +** +*******************************************************************************/ +tBTA_JV_STATUS BTA_JvRfcommStartServer(tBTA_SEC sec_mask, + tBTA_JV_ROLE role, UINT8 local_scn, UINT8 max_session, + tBTA_JV_RFCOMM_CBACK *p_cback, void *user_data) +{ + tBTA_JV_STATUS status = BTA_JV_FAILURE; + tBTA_JV_API_RFCOMM_SERVER *p_msg; + + APPL_TRACE_API( "BTA_JvRfcommStartServer"); + if (p_cback && + (p_msg = (tBTA_JV_API_RFCOMM_SERVER *)osi_malloc(sizeof(tBTA_JV_API_RFCOMM_SERVER))) != NULL) { + if (max_session == 0) { + max_session = 1; + } + if (max_session > BTA_JV_MAX_RFC_SR_SESSION) { + APPL_TRACE_DEBUG( "max_session (%d) is too big. use max (%d)", max_session, BTA_JV_MAX_RFC_SR_SESSION); + max_session = BTA_JV_MAX_RFC_SR_SESSION; + } + p_msg->hdr.event = BTA_JV_API_RFCOMM_START_SERVER_EVT; + p_msg->sec_mask = sec_mask; + p_msg->role = role; + p_msg->local_scn = local_scn; + p_msg->max_session = max_session; + p_msg->p_cback = p_cback; + p_msg->user_data = user_data; //caller's private data + bta_sys_sendmsg(p_msg); + status = BTA_JV_SUCCESS; + } + + return (status); +} + +/******************************************************************************* +** +** Function BTA_JvRfcommStopServer +** +** Description This function stops the RFCOMM server. If the server has an +** active connection, it would be closed. +** +** Returns BTA_JV_SUCCESS, if the request is being processed. +** BTA_JV_FAILURE, otherwise. +** +*******************************************************************************/ +tBTA_JV_STATUS BTA_JvRfcommStopServer(UINT32 handle, void *user_data) +{ + tBTA_JV_STATUS status = BTA_JV_FAILURE; + tBTA_JV_API_RFCOMM_SERVER *p_msg; + APPL_TRACE_API( "BTA_JvRfcommStopServer"); + if ((p_msg = (tBTA_JV_API_RFCOMM_SERVER *)osi_malloc(sizeof(tBTA_JV_API_RFCOMM_SERVER))) != NULL) { + p_msg->hdr.event = BTA_JV_API_RFCOMM_STOP_SERVER_EVT; + p_msg->handle = handle; + p_msg->user_data = user_data; //caller's private data + bta_sys_sendmsg(p_msg); + status = BTA_JV_SUCCESS; + } + + return (status); +} + +/******************************************************************************* +** +** Function BTA_JvRfcommRead +** +** Description This function reads data from an RFCOMM connection +** The actual size of data read is returned in p_len. +** +** Returns BTA_JV_SUCCESS, if the request is being processed. +** BTA_JV_FAILURE, otherwise. +** +*******************************************************************************/ +tBTA_JV_STATUS BTA_JvRfcommRead(UINT32 handle, UINT32 req_id, UINT8 *p_data, UINT16 len) +{ + tBTA_JV_STATUS status = BTA_JV_FAILURE; + tBTA_JV_API_RFCOMM_READ *p_msg; + UINT32 hi = ((handle & BTA_JV_RFC_HDL_MASK) & ~BTA_JV_RFCOMM_MASK) - 1; + UINT32 si = BTA_JV_RFC_HDL_TO_SIDX(handle); + + APPL_TRACE_API( "BTA_JvRfcommRead"); + if (hi < BTA_JV_MAX_RFC_CONN && bta_jv_cb.rfc_cb[hi].p_cback && + si < BTA_JV_MAX_RFC_SR_SESSION && bta_jv_cb.rfc_cb[hi].rfc_hdl[si] && + (p_msg = (tBTA_JV_API_RFCOMM_READ *)osi_malloc(sizeof(tBTA_JV_API_RFCOMM_READ))) != NULL) { + p_msg->hdr.event = BTA_JV_API_RFCOMM_READ_EVT; + p_msg->handle = handle; + p_msg->req_id = req_id; + p_msg->p_data = p_data; + p_msg->len = len; + p_msg->p_cb = &bta_jv_cb.rfc_cb[hi]; + p_msg->p_pcb = &bta_jv_cb.port_cb[p_msg->p_cb->rfc_hdl[si] - 1]; + bta_sys_sendmsg(p_msg); + status = BTA_JV_SUCCESS; + } + + return (status); +} + +/******************************************************************************* +** +** Function BTA_JvRfcommGetPortHdl +** +** Description This function fetches the rfcomm port handle +** +** Returns BTA_JV_SUCCESS, if the request is being processed. +** BTA_JV_FAILURE, otherwise. +** +*******************************************************************************/ +UINT16 BTA_JvRfcommGetPortHdl(UINT32 handle) +{ + UINT32 hi = ((handle & BTA_JV_RFC_HDL_MASK) & ~BTA_JV_RFCOMM_MASK) - 1; + UINT32 si = BTA_JV_RFC_HDL_TO_SIDX(handle); + + if (hi < BTA_JV_MAX_RFC_CONN && + si < BTA_JV_MAX_RFC_SR_SESSION && bta_jv_cb.rfc_cb[hi].rfc_hdl[si]) { + return bta_jv_cb.port_cb[bta_jv_cb.rfc_cb[hi].rfc_hdl[si] - 1].port_handle; + } else { + return 0xffff; + } +} + + +/******************************************************************************* +** +** Function BTA_JvRfcommReady +** +** Description This function determined if there is data to read from +** an RFCOMM connection +** +** Returns BTA_JV_SUCCESS, if data queue size is in *p_data_size. +** BTA_JV_FAILURE, if error. +** +*******************************************************************************/ +tBTA_JV_STATUS BTA_JvRfcommReady(UINT32 handle, UINT32 *p_data_size) +{ + tBTA_JV_STATUS status = BTA_JV_FAILURE; + UINT16 size = 0; + UINT32 hi = ((handle & BTA_JV_RFC_HDL_MASK) & ~BTA_JV_RFCOMM_MASK) - 1; + UINT32 si = BTA_JV_RFC_HDL_TO_SIDX(handle); + + APPL_TRACE_API( "BTA_JvRfcommReady"); + if (hi < BTA_JV_MAX_RFC_CONN && bta_jv_cb.rfc_cb[hi].p_cback && + si < BTA_JV_MAX_RFC_SR_SESSION && bta_jv_cb.rfc_cb[hi].rfc_hdl[si]) { + if (PORT_GetRxQueueCnt(bta_jv_cb.rfc_cb[hi].rfc_hdl[si], &size) == PORT_SUCCESS) { + status = BTA_JV_SUCCESS; + } + } + *p_data_size = size; + return (status); +} + +/******************************************************************************* +** +** Function BTA_JvRfcommWrite +** +** Description This function writes data to an RFCOMM connection +** +** Returns BTA_JV_SUCCESS, if the request is being processed. +** BTA_JV_FAILURE, otherwise. +** +*******************************************************************************/ + +tBTA_JV_STATUS BTA_JvRfcommWrite(UINT32 handle, UINT32 req_id, int len, UINT8 *p_data) +{ + tBTA_JV_STATUS status = BTA_JV_FAILURE; + tBTA_JV_API_RFCOMM_WRITE *p_msg; + UINT32 hi = ((handle & BTA_JV_RFC_HDL_MASK) & ~BTA_JV_RFCOMM_MASK) - 1; + UINT32 si = BTA_JV_RFC_HDL_TO_SIDX(handle); + + APPL_TRACE_API( "BTA_JvRfcommWrite"); + APPL_TRACE_DEBUG( "handle:0x%x, hi:%d, si:%d", handle, hi, si); + if (hi < BTA_JV_MAX_RFC_CONN && bta_jv_cb.rfc_cb[hi].p_cback && + si < BTA_JV_MAX_RFC_SR_SESSION && bta_jv_cb.rfc_cb[hi].rfc_hdl[si] && + (p_msg = (tBTA_JV_API_RFCOMM_WRITE *)osi_malloc(sizeof(tBTA_JV_API_RFCOMM_WRITE))) != NULL) { + p_msg->hdr.event = BTA_JV_API_RFCOMM_WRITE_EVT; + p_msg->handle = handle; + p_msg->req_id = req_id; + p_msg->p_cb = &bta_jv_cb.rfc_cb[hi]; + p_msg->p_pcb = &bta_jv_cb.port_cb[p_msg->p_cb->rfc_hdl[si] - 1]; + p_msg->p_data = p_data; + p_msg->len = len; + APPL_TRACE_API( "write ok"); + bta_sys_sendmsg(p_msg); + status = BTA_JV_SUCCESS; + } + return (status); +} + +/******************************************************************************* +** +** Function BTA_JvRfcommFlowControl +** +** Description This function gives credits to the peer +** +** Returns BTA_JV_SUCCESS, if the request is being processed. +** BTA_JV_FAILURE, otherwise. +** +*******************************************************************************/ +tBTA_JV_STATUS BTA_JvRfcommFlowControl(UINT32 handle, UINT16 credits_given) +{ + tBTA_JV_STATUS status = BTA_JV_FAILURE; + tBTA_JV_API_RFCOMM_FLOW_CONTROL *p_msg; + UINT32 hi = ((handle & BTA_JV_RFC_HDL_MASK) & ~BTA_JV_RFCOMM_MASK) - 1; + UINT32 si = BTA_JV_RFC_HDL_TO_SIDX(handle); + + APPL_TRACE_API( "BTA_JvRfcommFlowControl"); + APPL_TRACE_DEBUG( "handle:0x%x, hi:%d, si:%d", handle, hi, si); + if (hi < BTA_JV_MAX_RFC_CONN && bta_jv_cb.rfc_cb[hi].p_cback && + si < BTA_JV_MAX_RFC_SR_SESSION && bta_jv_cb.rfc_cb[hi].rfc_hdl[si] && + (p_msg = (tBTA_JV_API_RFCOMM_FLOW_CONTROL *)osi_malloc(sizeof(tBTA_JV_API_RFCOMM_FLOW_CONTROL))) != NULL) { + p_msg->hdr.event = BTA_JV_API_RFCOMM_FLOW_CONTROL_EVT; + p_msg->p_cb = &bta_jv_cb.rfc_cb[hi]; + p_msg->p_pcb = &bta_jv_cb.port_cb[p_msg->p_cb->rfc_hdl[si] - 1]; + p_msg->credits_given = credits_given; + APPL_TRACE_API( "credits given %d", credits_given); + bta_sys_sendmsg(p_msg); + status = BTA_JV_SUCCESS; + } + return (status); +} +#endif /* BTA_JV_RFCOMM_INCLUDED */ + +/******************************************************************************* + ** + ** Function BTA_JVSetPmProfile + ** + ** Description This function set or free power mode profile for different JV application + ** + ** Parameters: handle, JV handle from RFCOMM or L2CAP + ** app_id: app specific pm ID, can be BTA_JV_PM_ALL, see bta_dm_cfg.c for details + ** BTA_JV_PM_ID_CLEAR: removes pm management on the handle. init_st is ignored and + ** BTA_JV_CONN_CLOSE is called implicitely + ** init_st: state after calling this API. typically it should be BTA_JV_CONN_OPEN + ** + ** Returns BTA_JV_SUCCESS, if the request is being processed. + ** BTA_JV_FAILURE, otherwise. + ** + ** NOTE: BTA_JV_PM_ID_CLEAR: In general no need to be called as jv pm calls automatically + ** BTA_JV_CONN_CLOSE to remove in case of connection close! + ** + *******************************************************************************/ +tBTA_JV_STATUS BTA_JvSetPmProfile(UINT32 handle, tBTA_JV_PM_ID app_id, tBTA_JV_CONN_STATE init_st) +{ + tBTA_JV_STATUS status = BTA_JV_FAILURE; + tBTA_JV_API_SET_PM_PROFILE *p_msg; + + APPL_TRACE_API("BTA_JVSetPmProfile handle:0x%x, app_id:%d", handle, app_id); + if ((p_msg = (tBTA_JV_API_SET_PM_PROFILE *)osi_malloc(sizeof(tBTA_JV_API_SET_PM_PROFILE))) + != NULL) { + p_msg->hdr.event = BTA_JV_API_SET_PM_PROFILE_EVT; + p_msg->handle = handle; + p_msg->app_id = app_id; + p_msg->init_st = init_st; + bta_sys_sendmsg(p_msg); + status = BTA_JV_SUCCESS; + } + + return (status); +} + +#endif ///defined BTA_JV_INCLUDED && BTA_JV_INCLUDED == TRUE diff --git a/lib/bt/host/bluedroid/bta/jv/bta_jv_cfg.c b/lib/bt/host/bluedroid/bta/jv/bta_jv_cfg.c new file mode 100644 index 00000000..9ec4d77d --- /dev/null +++ b/lib/bt/host/bluedroid/bta/jv/bta_jv_cfg.c @@ -0,0 +1,67 @@ +/****************************************************************************** + * + * Copyright (C) 2014 The Android Open Source Project + * Copyright (C) 2004-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains compile-time configurable constants for advanced + * audio + * + ******************************************************************************/ + +#include "osi/allocator.h" +#include "bta/bta_api.h" +#include "bta/bta_jv_api.h" +#include "common/bt_target.h" + +#if (defined BTA_JV_INCLUDED && BTA_JV_INCLUDED == TRUE) +#ifndef BTA_JV_SDP_DB_SIZE +#define BTA_JV_SDP_DB_SIZE 4500 +#endif + +#ifndef BTA_JV_SDP_RAW_DATA_SIZE +#define BTA_JV_SDP_RAW_DATA_SIZE 1800 +#endif + +/* The platform may choose to use dynamic memory for these data buffers. + * p_bta_jv_cfg->p_sdp_db must be allocated/stay allocated + * between BTA_JvEnable and BTA_JvDisable + * p_bta_jv_cfg->p_sdp_raw_data can be allocated before calling BTA_JvStartDiscovery + * it can be de-allocated after the last call to access the database */ +#if BTA_DYNAMIC_MEMORY == FALSE +static UINT8 bta_jv_sdp_raw_data[BTA_JV_SDP_RAW_DATA_SIZE]; +static UINT8 __attribute__ ((aligned(4))) bta_jv_sdp_db_data[BTA_JV_SDP_DB_SIZE]; +#endif + +/* JV configuration structure */ +/*const */tBTA_JV_CFG bta_jv_cfg = { + BTA_JV_SDP_RAW_DATA_SIZE, /* The size of p_sdp_raw_data */ + BTA_JV_SDP_DB_SIZE, /* The size of p_sdp_db_data */ +#if BTA_DYNAMIC_MEMORY == FALSE + bta_jv_sdp_raw_data, /* The data buffer to keep raw data */ + (tSDP_DISCOVERY_DB *)bta_jv_sdp_db_data /* The data buffer to keep SDP database */ +#else + NULL, + NULL +#endif +}; + +tBTA_JV_CFG *p_bta_jv_cfg = (tBTA_JV_CFG *) &bta_jv_cfg; + + +#endif ///defined BTA_JV_INCLUDED && BTA_JV_INCLUDED == TRUE diff --git a/lib/bt/host/bluedroid/bta/jv/bta_jv_main.c b/lib/bt/host/bluedroid/bta/jv/bta_jv_main.c new file mode 100644 index 00000000..f233e933 --- /dev/null +++ b/lib/bt/host/bluedroid/bta/jv/bta_jv_main.c @@ -0,0 +1,110 @@ +/****************************************************************************** + * + * Copyright (C) 2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This is the main implementation file for the BTA Java I/F + * + ******************************************************************************/ + +#include "bta/bta_api.h" +#include "bta/bta_sys.h" +#include "bta/bta_jv_api.h" +#include "bta_jv_int.h" +#include "common/bt_target.h" + +#if (defined BTA_JV_INCLUDED && BTA_JV_INCLUDED == TRUE) +/***************************************************************************** +** Constants and types +*****************************************************************************/ + +#if BTA_DYNAMIC_MEMORY == FALSE +tBTA_JV_CB bta_jv_cb; +#else +tBTA_JV_CB *bta_jv_cb_ptr; +#endif + +/* state machine action enumeration list */ +#define BTA_JV_NUM_ACTIONS (BTA_JV_MAX_INT_EVT & 0x00ff) + +/* type for action functions */ +typedef void (*tBTA_JV_ACTION)(tBTA_JV_MSG *p_data); + +/* action function list */ +const tBTA_JV_ACTION bta_jv_action[] = { + bta_jv_enable, /* BTA_JV_API_ENABLE_EVT */ + bta_jv_disable, /* BTA_JV_API_DISABLE_EVT */ + bta_jv_get_channel_id, /* BTA_JV_API_GET_CHANNEL_EVT */ + bta_jv_free_scn, /* BTA_JV_API_FREE_SCN_EVT */ + bta_jv_start_discovery, /* BTA_JV_API_START_DISCOVERY_EVT */ + bta_jv_create_record, /* BTA_JV_API_CREATE_RECORD_EVT */ + bta_jv_delete_record, /* BTA_JV_API_DELETE_RECORD_EVT */ +#if BTA_JV_L2CAP_INCLUDED + bta_jv_l2cap_connect, /* BTA_JV_API_L2CAP_CONNECT_EVT */ + bta_jv_l2cap_close, /* BTA_JV_API_L2CAP_CLOSE_EVT */ + bta_jv_l2cap_start_server, /* BTA_JV_API_L2CAP_START_SERVER_EVT */ + bta_jv_l2cap_stop_server, /* BTA_JV_API_L2CAP_STOP_SERVER_EVT */ + bta_jv_l2cap_read, /* BTA_JV_API_L2CAP_READ_EVT */ + bta_jv_l2cap_write, /* BTA_JV_API_L2CAP_WRITE_EVT */ +#endif /* BTA_JV_L2CAP_INCLUDED */ +#if BTA_JV_RFCOMM_INCLUDED + bta_jv_rfcomm_config, /* BTA_JV_API_RFCOMM_CONFIG_EVT */ + bta_jv_rfcomm_connect, /* BTA_JV_API_RFCOMM_CONNECT_EVT */ + bta_jv_rfcomm_close, /* BTA_JV_API_RFCOMM_CLOSE_EVT */ + bta_jv_rfcomm_start_server, /* BTA_JV_API_RFCOMM_START_SERVER_EVT */ + bta_jv_rfcomm_stop_server, /* BTA_JV_API_RFCOMM_STOP_SERVER_EVT */ + bta_jv_rfcomm_read, /* BTA_JV_API_RFCOMM_READ_EVT */ + bta_jv_rfcomm_write, /* BTA_JV_API_RFCOMM_WRITE_EVT */ + bta_jv_rfcomm_flow_control, /* BTA_JV_API_RFCOMM_FLOW_CONTROL_EVT */ +#endif /* BTA_JV_RFCOMM_INCLUDED */ + bta_jv_set_pm_profile, /* BTA_JV_API_SET_PM_PROFILE_EVT */ + bta_jv_change_pm_state, /* BTA_JV_API_PM_STATE_CHANGE_EVT */ +#if BTA_JV_L2CAP_INCLUDED + bta_jv_l2cap_connect_le, /* BTA_JV_API_L2CAP_CONNECT_LE_EVT */ + bta_jv_l2cap_start_server_le, /* BTA_JV_API_L2CAP_START_SERVER_LE_EVT */ + bta_jv_l2cap_stop_server_le, /* BTA_JV_API_L2CAP_STOP_SERVER_LE_EVT */ + bta_jv_l2cap_write_fixed, /* BTA_JV_API_L2CAP_WRITE_FIXED_EVT */ + bta_jv_l2cap_close_fixed, /* BTA_JV_API_L2CAP_CLOSE_FIXED_EVT */ +#endif /* BTA_JV_L2CAP_INCLUDED */ +}; + +/******************************************************************************* +** +** Function bta_jv_sm_execute +** +** Description State machine event handling function for JV +** +** +** Returns void +** +*******************************************************************************/ +BOOLEAN bta_jv_sm_execute(BT_HDR *p_msg) +{ + BOOLEAN ret = FALSE; + UINT16 action = (p_msg->event & 0x00ff); + /* execute action functions */ + + if (action < BTA_JV_NUM_ACTIONS) { + (*bta_jv_action[action])((tBTA_JV_MSG *)p_msg); + ret = TRUE; + } + + return (ret); +} + +#endif ///defined BTA_JV_INCLUDED && BTA_JV_INCLUDED == TRUE diff --git a/lib/bt/host/bluedroid/bta/jv/include/bta_jv_int.h b/lib/bt/host/bluedroid/bta/jv/include/bta_jv_int.h new file mode 100644 index 00000000..d6cd785a --- /dev/null +++ b/lib/bt/host/bluedroid/bta/jv/include/bta_jv_int.h @@ -0,0 +1,493 @@ +/****************************************************************************** + * + * Copyright (C) 2006-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This is the private interface file for the BTA Java I/F + * + ******************************************************************************/ +#ifndef BTA_JV_INT_H +#define BTA_JV_INT_H + +#include "bta/bta_sys.h" +#include "bta/bta_api.h" +#include "bta/bta_jv_api.h" +#include "stack/rfcdefs.h" +#include "stack/port_api.h" +#include "stack/sdp_api.h" + +#include "common/bt_target.h" +#if (defined BTA_JV_INCLUDED && BTA_JV_INCLUDED == TRUE) +/***************************************************************************** +** Constants +*****************************************************************************/ + +#define SPP_VERSION 0x0102 + +enum { + /* these events are handled by the state machine */ + BTA_JV_API_ENABLE_EVT = BTA_SYS_EVT_START(BTA_ID_JV), + BTA_JV_API_DISABLE_EVT, + BTA_JV_API_GET_CHANNEL_EVT, + BTA_JV_API_FREE_SCN_EVT, + BTA_JV_API_START_DISCOVERY_EVT, + BTA_JV_API_CREATE_RECORD_EVT, + BTA_JV_API_DELETE_RECORD_EVT, +#if BTA_JV_L2CAP_INCLUDED + BTA_JV_API_L2CAP_CONNECT_EVT, + BTA_JV_API_L2CAP_CLOSE_EVT, + BTA_JV_API_L2CAP_START_SERVER_EVT, + BTA_JV_API_L2CAP_STOP_SERVER_EVT, + BTA_JV_API_L2CAP_READ_EVT, + BTA_JV_API_L2CAP_WRITE_EVT, +#endif /* BTA_JV_L2CAP_INCLUDED */ +#if BTA_JV_RFCOMM_INCLUDED + BTA_JV_API_RFCOMM_CONFIG_EVT, + BTA_JV_API_RFCOMM_CONNECT_EVT, + BTA_JV_API_RFCOMM_CLOSE_EVT, + BTA_JV_API_RFCOMM_START_SERVER_EVT, + BTA_JV_API_RFCOMM_STOP_SERVER_EVT, + BTA_JV_API_RFCOMM_READ_EVT, + BTA_JV_API_RFCOMM_WRITE_EVT, + BTA_JV_API_RFCOMM_FLOW_CONTROL_EVT, +#endif /* BTA_JV_RFCOMM_INCLUDED */ + BTA_JV_API_SET_PM_PROFILE_EVT, + BTA_JV_API_PM_STATE_CHANGE_EVT, +#if BTA_JV_L2CAP_INCLUDED + BTA_JV_API_L2CAP_CONNECT_LE_EVT, + BTA_JV_API_L2CAP_START_SERVER_LE_EVT, + BTA_JV_API_L2CAP_STOP_SERVER_LE_EVT, + BTA_JV_API_L2CAP_WRITE_FIXED_EVT, + BTA_JV_API_L2CAP_CLOSE_FIXED_EVT, +#endif /* BTA_JV_L2CAP_INCLUDED */ + BTA_JV_MAX_INT_EVT +}; + +#ifndef BTA_JV_RFC_EV_MASK +#define BTA_JV_RFC_EV_MASK (PORT_EV_RXCHAR | PORT_EV_TXEMPTY | PORT_EV_FC | PORT_EV_FCS) +#endif + +/* data type for BTA_JV_API_ENABLE_EVT */ +typedef struct { + BT_HDR hdr; + tBTA_JV_DM_CBACK *p_cback; +} tBTA_JV_API_ENABLE; + +/* data type for BTA_JV_API_DISABLE_EVT */ +typedef struct { + BT_HDR hdr; + tBTA_JV_RFCOMM_CBACK *p_cback; +} tBTA_JV_API_DISABLE; + +/* data type for BTA_JV_API_START_DISCOVERY_EVT */ +typedef struct { + BT_HDR hdr; + BD_ADDR bd_addr; + UINT16 num_uuid; + tSDP_UUID uuid_list[BTA_JV_MAX_UUIDS]; + UINT16 num_attr; + UINT16 attr_list[BTA_JV_MAX_ATTRS]; + void *user_data; /* piggyback caller's private data*/ +} tBTA_JV_API_START_DISCOVERY; + +enum { + BTA_JV_PM_FREE_ST = 0, /* empty PM slot */ + BTA_JV_PM_IDLE_ST, + BTA_JV_PM_BUSY_ST +}; + +/* BTA JV PM control block */ +typedef struct { + UINT32 handle; /* The connection handle */ + UINT8 state; /* state: see above enum */ + tBTA_JV_PM_ID app_id; /* JV app specific id indicating power table to use */ + BD_ADDR peer_bd_addr; /* Peer BD address */ +} tBTA_JV_PM_CB; + +enum { + BTA_JV_ST_NONE = 0, + BTA_JV_ST_CL_OPENING, + BTA_JV_ST_CL_OPEN, + BTA_JV_ST_CL_CLOSING, + BTA_JV_ST_SR_LISTEN, + BTA_JV_ST_SR_OPEN, + BTA_JV_ST_SR_CLOSING +} ; +typedef UINT8 tBTA_JV_STATE; +#define BTA_JV_ST_CL_MAX BTA_JV_ST_CL_CLOSING + +#if BTA_JV_L2CAP_INCLUDED +/* JV L2CAP control block */ +typedef struct { + tBTA_JV_L2CAP_CBACK *p_cback; /* the callback function */ + UINT16 psm; /* the psm used for this server connection */ + tBTA_JV_STATE state; /* the state of this control block */ + tBTA_SERVICE_ID sec_id; /* service id */ + UINT32 handle; /* the handle reported to java app (same as gap handle) */ + BOOLEAN cong; /* TRUE, if congested */ + tBTA_JV_PM_CB *p_pm_cb; /* ptr to pm control block, NULL: unused */ + void *user_data; /* user data for callback from higher layers */ +} tBTA_JV_L2C_CB; +#endif /* BTA_JV_L2CAP_INCLUDED */ + +#define BTA_JV_RFC_HDL_MASK 0xFF +#define BTA_JV_RFCOMM_MASK 0x80 +#define BTA_JV_ALL_APP_ID 0xFF +#define BTA_JV_RFC_HDL_TO_SIDX(r) (((r)&0xFF00) >> 8) +#define BTA_JV_RFC_H_S_TO_HDL(h, s) ((h)|(s<<8)) + +/* port control block */ +typedef struct { + UINT32 handle; /* the rfcomm session handle at jv */ + UINT16 port_handle;/* port handle */ + tBTA_JV_STATE state; /* the state of this control block */ + UINT8 max_sess; /* max sessions */ + void *user_data; /* piggyback caller's private data*/ + BOOLEAN cong; /* TRUE, if congested */ + tBTA_JV_PM_CB *p_pm_cb; /* ptr to pm control block, NULL: unused */ +} tBTA_JV_PCB; + +#if BTA_JV_RFCOMM_INCLUDED +/* JV RFCOMM control block */ +typedef struct { + tBTA_JV_RFCOMM_CBACK *p_cback; /* the callback function */ + UINT16 rfc_hdl[BTA_JV_MAX_RFC_SR_SESSION]; + tBTA_SERVICE_ID sec_id; /* service id */ + UINT8 handle; /* index: the handle reported to java app */ + UINT8 scn; /* the scn of the server */ + UINT8 max_sess; /* max sessions */ + int curr_sess; /* current sessions count*/ +} tBTA_JV_RFC_CB; +#endif /* BTA_JV_RFCOMM_INCLUDED */ + +#if BTA_JV_L2CAP_INCLUDED +/* data type for BTA_JV_API_L2CAP_CONNECT_EVT & BTA_JV_API_L2CAP_CONNECT_LE_EVT */ +typedef struct { + BT_HDR hdr; + tBTA_SEC sec_mask; + tBTA_JV_ROLE role; + union { + UINT16 remote_psm; + UINT16 remote_chan; + }; + UINT16 rx_mtu; + BD_ADDR peer_bd_addr; + INT32 has_cfg; + tL2CAP_CFG_INFO cfg; + INT32 has_ertm_info; + tL2CAP_ERTM_INFO ertm_info; + tBTA_JV_L2CAP_CBACK *p_cback; + void *user_data; +} tBTA_JV_API_L2CAP_CONNECT; + +/* data type for BTA_JV_API_L2CAP_SERVER_EVT */ +typedef struct { + BT_HDR hdr; + tBTA_SEC sec_mask; + tBTA_JV_ROLE role; + union { + UINT16 local_psm; + UINT16 local_chan; + }; + UINT16 rx_mtu; + INT32 has_cfg; + tL2CAP_CFG_INFO cfg; + INT32 has_ertm_info; + tL2CAP_ERTM_INFO ertm_info; + tBTA_JV_L2CAP_CBACK *p_cback; + void *user_data; +} tBTA_JV_API_L2CAP_SERVER; + +/* data type for BTA_JV_API_L2CAP_CLOSE_EVT */ +typedef struct { + BT_HDR hdr; + UINT32 handle; + tBTA_JV_L2C_CB *p_cb; + tBTA_JV_L2CAP_CBACK *p_cback; + void *user_data; +} tBTA_JV_API_L2CAP_CLOSE; + +/* data type for BTA_JV_API_L2CAP_READ_EVT */ +typedef struct { + BT_HDR hdr; + UINT32 handle; + UINT32 req_id; + tBTA_JV_L2CAP_CBACK *p_cback; + UINT8 *p_data; + UINT16 len; + void *user_data; +} tBTA_JV_API_L2CAP_READ; + +/* data type for BTA_JV_API_L2CAP_WRITE_EVT */ +typedef struct { + BT_HDR hdr; + UINT32 handle; + UINT32 req_id; + tBTA_JV_L2C_CB *p_cb; + UINT8 *p_data; + UINT16 len; + void *user_data; +} tBTA_JV_API_L2CAP_WRITE; + +/* data type for BTA_JV_API_L2CAP_WRITE_FIXED_EVT */ +typedef struct { + BT_HDR hdr; + UINT16 channel; + BD_ADDR addr; + UINT32 req_id; + tBTA_JV_L2CAP_CBACK *p_cback; + UINT8 *p_data; + UINT16 len; + void *user_data; +} tBTA_JV_API_L2CAP_WRITE_FIXED; +#endif /* BTA_JV_L2CAP_INCLUDED */ + +#if BTA_JV_RFCOMM_INCLUDED +/* data type for BTA_JV_API_RFCOMM_CONFIG_EVT */ +typedef struct { + BT_HDR hdr; + BOOLEAN enable_l2cap_ertm; +} tBTA_JV_API_RFCOMM_CONFIG; + +/* data type for BTA_JV_API_RFCOMM_CONNECT_EVT */ +typedef struct { + BT_HDR hdr; + tBTA_SEC sec_mask; + tBTA_JV_ROLE role; + UINT8 remote_scn; + BD_ADDR peer_bd_addr; + tBTA_JV_RFCOMM_CBACK *p_cback; + void *user_data; +} tBTA_JV_API_RFCOMM_CONNECT; + +/* data type for BTA_JV_API_RFCOMM_SERVER_EVT */ +typedef struct { + BT_HDR hdr; + tBTA_SEC sec_mask; + tBTA_JV_ROLE role; + UINT8 local_scn; + UINT8 max_session; + UINT32 handle; + tBTA_JV_RFCOMM_CBACK *p_cback; + void *user_data; +} tBTA_JV_API_RFCOMM_SERVER; + +/* data type for BTA_JV_API_RFCOMM_READ_EVT */ +typedef struct { + BT_HDR hdr; + UINT32 handle; + UINT32 req_id; + UINT8 *p_data; + UINT16 len; + tBTA_JV_RFC_CB *p_cb; + tBTA_JV_PCB *p_pcb; +} tBTA_JV_API_RFCOMM_READ; +#endif /* BTA_JV_RFCOMM_INCLUDED */ + +/* data type for BTA_JV_API_SET_PM_PROFILE_EVT */ +typedef struct { + BT_HDR hdr; + UINT32 handle; + tBTA_JV_PM_ID app_id; + tBTA_JV_CONN_STATE init_st; +} tBTA_JV_API_SET_PM_PROFILE; + +/* data type for BTA_JV_API_PM_STATE_CHANGE_EVT */ +typedef struct { + BT_HDR hdr; + tBTA_JV_PM_CB *p_cb; + tBTA_JV_CONN_STATE state; +} tBTA_JV_API_PM_STATE_CHANGE; + +#if BTA_JV_RFCOMM_INCLUDED +/* data type for BTA_JV_API_RFCOMM_WRITE_EVT */ +typedef struct { + BT_HDR hdr; + UINT32 handle; + UINT32 req_id; + UINT8 *p_data; + int len; + tBTA_JV_RFC_CB *p_cb; + tBTA_JV_PCB *p_pcb; +} tBTA_JV_API_RFCOMM_WRITE; + +/* data type for BTA_JV_API_RFCOMM_FLOW_CONTROL_EVT */ +typedef struct { + BT_HDR hdr; + tBTA_JV_RFC_CB *p_cb; + tBTA_JV_PCB *p_pcb; + UINT16 credits_given; +} tBTA_JV_API_RFCOMM_FLOW_CONTROL; + +/* data type for BTA_JV_API_RFCOMM_CLOSE_EVT */ +typedef struct { + BT_HDR hdr; + UINT32 handle; + tBTA_JV_RFC_CB *p_cb; + tBTA_JV_PCB *p_pcb; + void *user_data; +} tBTA_JV_API_RFCOMM_CLOSE; +#endif /* BTA_JV_RFCOMM_INCLUDED */ + +/* data type for BTA_JV_API_CREATE_RECORD_EVT */ +typedef struct { + BT_HDR hdr; +#define ESP_SDP_SERVER_NAME_MAX (32) + char name[ESP_SDP_SERVER_NAME_MAX + 1]; + INT32 channel; + void *user_data; +} tBTA_JV_API_CREATE_RECORD; + +/* data type for BTA_JV_API_ADD_ATTRIBUTE_EVT */ +typedef struct { + BT_HDR hdr; + UINT32 handle; + UINT16 attr_id; + UINT8 *p_value; + INT32 value_size; +} tBTA_JV_API_ADD_ATTRIBUTE; + +/* data type for BTA_JV_API_FREE_SCN_EVT */ +typedef struct { + BT_HDR hdr; + INT32 type; /* One of BTA_JV_CONN_TYPE_ */ + UINT16 scn; + tBTA_JV_RFCOMM_CBACK *p_cback; + void *user_data; +} tBTA_JV_API_FREE_CHANNEL; + +/* data type for BTA_JV_API_ALLOC_CHANNEL_EVT */ +typedef struct { + BT_HDR hdr; + INT32 type; /* One of BTA_JV_CONN_TYPE_ */ + INT32 channel; /* optionally request a specific channel */ + void *user_data; +} tBTA_JV_API_ALLOC_CHANNEL; +/* union of all data types */ +typedef union { + /* GKI event buffer header */ + BT_HDR hdr; + tBTA_JV_API_ENABLE enable; + tBTA_JV_API_DISABLE disable; + tBTA_JV_API_START_DISCOVERY start_discovery; + tBTA_JV_API_ALLOC_CHANNEL alloc_channel; + tBTA_JV_API_FREE_CHANNEL free_channel; + tBTA_JV_API_CREATE_RECORD create_record; + tBTA_JV_API_ADD_ATTRIBUTE add_attr; +#if BTA_JV_L2CAP_INCLUDED + tBTA_JV_API_L2CAP_CONNECT l2cap_connect; + tBTA_JV_API_L2CAP_READ l2cap_read; + tBTA_JV_API_L2CAP_WRITE l2cap_write; + tBTA_JV_API_L2CAP_CLOSE l2cap_close; + tBTA_JV_API_L2CAP_SERVER l2cap_server; + tBTA_JV_API_L2CAP_WRITE_FIXED l2cap_write_fixed; +#endif /* BTA_JV_L2CAP_INCLUDED */ +#if BTA_JV_RFCOMM_INCLUDED + tBTA_JV_API_RFCOMM_CONFIG rfcomm_config; + tBTA_JV_API_RFCOMM_CONNECT rfcomm_connect; + tBTA_JV_API_RFCOMM_READ rfcomm_read; + tBTA_JV_API_RFCOMM_WRITE rfcomm_write; + tBTA_JV_API_RFCOMM_FLOW_CONTROL rfcomm_fc; + tBTA_JV_API_RFCOMM_CLOSE rfcomm_close; + tBTA_JV_API_RFCOMM_SERVER rfcomm_server; +#endif /* BTA_JV_RFCOMM_INCLUDED */ + tBTA_JV_API_SET_PM_PROFILE set_pm; + tBTA_JV_API_PM_STATE_CHANGE change_pm_state; +} tBTA_JV_MSG; + +/* JV control block */ +typedef struct { + /* the SDP handle reported to JV user is the (index + 1) to sdp_handle[]. + * if sdp_handle[i]==0, it's not used. + * otherwise sdp_handle[i] is the stack SDP handle. */ + UINT32 sdp_handle[BTA_JV_MAX_SDP_REC]; /* SDP records created */ + UINT8 *p_sel_raw_data;/* the raw data of last service select */ + tBTA_JV_DM_CBACK *p_dm_cback; +#if BTA_JV_L2CAP_INCLUDED + tBTA_JV_L2C_CB l2c_cb[BTA_JV_MAX_L2C_CONN]; /* index is GAP handle (index) */ +#endif /* BTA_JV_L2CAP_INCLUDED */ +#if BTA_JV_RFCOMM_INCLUDED + tBTA_JV_RFC_CB rfc_cb[BTA_JV_MAX_RFC_CONN]; +#endif /* BTA_JV_RFCOMM_INCLUDED */ + tBTA_JV_PCB port_cb[MAX_RFC_PORTS]; /* index of this array is + the port_handle, */ + UINT8 sec_id[BTA_JV_NUM_SERVICE_ID]; /* service ID */ + BOOLEAN scn[BTA_JV_MAX_SCN]; /* SCN allocated by java */ + UINT16 free_psm_list[BTA_JV_MAX_L2C_CONN]; /* PSMs freed by java + (can be reused) */ + UINT8 sdp_active; /* see BTA_JV_SDP_ACT_* */ + tSDP_UUID uuid; /* current uuid of sdp discovery*/ + tBTA_JV_PM_CB pm_cb[BTA_JV_PM_MAX_NUM]; /* PM on a per JV handle bases */ +} tBTA_JV_CB; + +enum { + BTA_JV_SDP_ACT_NONE = 0, + BTA_JV_SDP_ACT_YES, /* waiting for SDP result */ + BTA_JV_SDP_ACT_CANCEL /* waiting for cancel complete */ +}; + +/* JV control block */ +#if BTA_DYNAMIC_MEMORY == FALSE +extern tBTA_JV_CB bta_jv_cb; +#else +extern tBTA_JV_CB *bta_jv_cb_ptr; +#define bta_jv_cb (*bta_jv_cb_ptr) +#endif + +/* config struct */ +extern tBTA_JV_CFG *p_bta_jv_cfg; + +extern BOOLEAN bta_jv_sm_execute(BT_HDR *p_msg); + +extern void bta_jv_enable (tBTA_JV_MSG *p_data); +extern void bta_jv_disable (tBTA_JV_MSG *p_data); +extern void bta_jv_get_channel_id (tBTA_JV_MSG *p_data); +extern void bta_jv_free_scn (tBTA_JV_MSG *p_data); +extern void bta_jv_start_discovery (tBTA_JV_MSG *p_data); +extern void bta_jv_create_record (tBTA_JV_MSG *p_data); +extern void bta_jv_delete_record (tBTA_JV_MSG *p_data); +#if BTA_JV_L2CAP_INCLUDED +extern void bta_jv_l2cap_connect (tBTA_JV_MSG *p_data); +extern void bta_jv_l2cap_close (tBTA_JV_MSG *p_data); +extern void bta_jv_l2cap_start_server (tBTA_JV_MSG *p_data); +extern void bta_jv_l2cap_stop_server (tBTA_JV_MSG *p_data); +extern void bta_jv_l2cap_read (tBTA_JV_MSG *p_data); +extern void bta_jv_l2cap_write (tBTA_JV_MSG *p_data); +#endif /* BTA_JV_L2CAP_INCLUDED */ +#if BTA_JV_RFCOMM_INCLUDED +extern void bta_jv_rfcomm_config (tBTA_JV_MSG *p_data); +extern void bta_jv_rfcomm_connect (tBTA_JV_MSG *p_data); +extern void bta_jv_rfcomm_close (tBTA_JV_MSG *p_data); +extern void bta_jv_rfcomm_start_server (tBTA_JV_MSG *p_data); +extern void bta_jv_rfcomm_stop_server (tBTA_JV_MSG *p_data); +extern void bta_jv_rfcomm_read (tBTA_JV_MSG *p_data); +extern void bta_jv_rfcomm_write (tBTA_JV_MSG *p_data); +extern void bta_jv_rfcomm_flow_control(tBTA_JV_MSG *p_data); +#endif /* BTA_JV_RFCOMM_INCLUDED */ +extern void bta_jv_set_pm_profile (tBTA_JV_MSG *p_data); +extern void bta_jv_change_pm_state(tBTA_JV_MSG *p_data); +#if BTA_JV_L2CAP_INCLUDED +extern void bta_jv_l2cap_connect_le (tBTA_JV_MSG *p_data); +extern void bta_jv_l2cap_start_server_le (tBTA_JV_MSG *p_data); +extern void bta_jv_l2cap_stop_server_le (tBTA_JV_MSG *p_data); +extern void bta_jv_l2cap_write_fixed (tBTA_JV_MSG *p_data); +extern void bta_jv_l2cap_close_fixed (tBTA_JV_MSG *p_data); +#endif /* BTA_JV_L2CAP_INCLUDED */ + +#endif ///defined BTA_JV_INCLUDED && BTA_JV_INCLUDED == TRUE +#endif /* BTA_JV_INT_H */ diff --git a/lib/bt/host/bluedroid/bta/sdp/bta_sdp.c b/lib/bt/host/bluedroid/bta/sdp/bta_sdp.c new file mode 100644 index 00000000..4deabe9a --- /dev/null +++ b/lib/bt/host/bluedroid/bta/sdp/bta_sdp.c @@ -0,0 +1,83 @@ +/****************************************************************************** + * + * Copyright (C) 2014 The Android Open Source Project + + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This is the main implementation file for the BTA MCE I/F + * + ******************************************************************************/ + +#include +#include "common/bt_target.h" +#include "bta/bta_api.h" +#include "bta/bta_sys.h" +#include "bta/bta_sdp_api.h" +#include "bta_sdp_int.h" + +#if defined(BTA_SDP_INCLUDED) && (BTA_SDP_INCLUDED == TRUE) + +/***************************************************************************** +** Constants and types +*****************************************************************************/ + +#if BTA_DYNAMIC_MEMORY == FALSE +tBTA_SDP_CB bta_sdp_cb; +#else +tBTA_SDP_CB *bta_sdp_cb_ptr; +#endif + +/* state machine action enumeration list */ +#define BTA_SDP_NUM_ACTIONS (BTA_SDP_MAX_INT_EVT & 0x00ff) + +/* type for action functions */ +typedef void (*tBTA_SDP_ACTION)(tBTA_SDP_MSG *p_data); + +/* action function list */ +const tBTA_SDP_ACTION bta_sdp_action[] = { + bta_sdp_enable, /* BTA_SDP_API_ENABLE_EVT */ + bta_sdp_search, /* BTA_SDP_API_SEARCH_EVT */ + bta_sdp_create_record, /* BTA_SDP_API_CREATE_RECORD_USER_EVT */ + bta_sdp_remove_record, /* BTA_SDP_API_REMOVE_RECORD_USER_EVT */ +}; + +/******************************************************************************* +** Function bta_sdp_sm_execute +** +** Description State machine event handling function for SDP search +** +** Returns void +*******************************************************************************/ +BOOLEAN bta_sdp_sm_execute(BT_HDR *p_msg) +{ + if (p_msg == NULL) { + return FALSE; + } + + BOOLEAN ret = FALSE; + UINT16 action = (p_msg->event & 0x00ff); + + /* execute action functions */ + if (action < BTA_SDP_NUM_ACTIONS) { + (*bta_sdp_action[action])((tBTA_SDP_MSG *)p_msg); + ret = TRUE; + } + + return (ret); +} + +#endif /* #if defined(BTA_SDP_INCLUDED) && (BTA_SDP_INCLUDED == TRUE) */ diff --git a/lib/bt/host/bluedroid/bta/sdp/bta_sdp_act.c b/lib/bt/host/bluedroid/bta/sdp/bta_sdp_act.c new file mode 100644 index 00000000..70aa00ba --- /dev/null +++ b/lib/bt/host/bluedroid/bta/sdp/bta_sdp_act.c @@ -0,0 +1,586 @@ +/****************************************************************************** + * + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * This file contains action functions for SDP search. + ******************************************************************************/ + +// #include +#include "bt_sdp.h" +// #include +#include "common/bt_defs.h" +#include +#include +#include "common/bt_target.h" +#include "osi/allocator.h" +#include "stack/bt_types.h" +#include "bta/utl.h" +#include "bta/bta_sys.h" +#include "bta/bta_api.h" +#include "bta/bta_sdp_api.h" +#include "bta_sdp_int.h" +#include "stack/btm_api.h" +#include "btm_int.h" +#include "stack/sdp_api.h" + +#if (SDP_INCLUDED == TRUE) + +/***************************************************************************** +** Constants +*****************************************************************************/ + +static const uint8_t UUID_OBEX_OBJECT_PUSH[] = {0x00, 0x00, 0x11, 0x05, 0x00, 0x00, 0x10, 0x00, + 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB + }; +static const uint8_t UUID_PBAP_PSE[] = {0x00, 0x00, 0x11, 0x2F, 0x00, 0x00, 0x10, 0x00, + 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB + }; +static const uint8_t UUID_PBAP_PCE[] = {0x00, 0x00, 0x11, 0x2E, 0x00, 0x00, 0x10, 0x00, + 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB + }; +static const uint8_t UUID_MAP_MAS[] = {0x00, 0x00, 0x11, 0x32, 0x00, 0x00, 0x10, 0x00, + 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB + }; +static const uint8_t UUID_MAP_MNS[] = {0x00, 0x00, 0x11, 0x33, 0x00, 0x00, 0x10, 0x00, + 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB + }; +static const uint8_t UUID_SPP[] = {0x00, 0x00, 0x11, 0x01, 0x00, 0x00, 0x10, 0x00, + 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB + }; +static const uint8_t UUID_SAP[] = {0x00, 0x00, 0x11, 0x2D, 0x00, 0x00, 0x10, 0x00, + 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB + }; +// TODO: +// Both the fact that the UUIDs are declared in multiple places, plus the fact +// that there is a mess of UUID comparison and shortening methods will have to +// be fixed. +// The btcore->uuid module should be used for all instances. + +#define UUID_MAX_LENGTH 16 +#define IS_UUID(u1,u2) !memcmp(u1,u2,UUID_MAX_LENGTH) + +static inline tBT_UUID shorten_sdp_uuid(const tBT_UUID *u) +{ + static uint8_t bt_base_uuid[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, + 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB + }; + + APPL_TRACE_DEBUG("%s() - uuid len:%d\n", __func__, u->len); + if (u->len != 16) { + return *u; + } + + if (memcmp(&u->uu.uuid128[4], &bt_base_uuid[4], 12) != 0) { + return *u; + } + + tBT_UUID su; + memset(&su, 0, sizeof(su)); + if (u->uu.uuid128[0] == 0 && u->uu.uuid128[1] == 0) { + su.len = 2; + uint16_t u16; + memcpy(&u16, &u->uu.uuid128[2], sizeof(u16)); + su.uu.uuid16 = ntohs(u16); + } else { + su.len = 4; + uint32_t u32; + memcpy(&u32, &u->uu.uuid128[0], sizeof(u32)); + su.uu.uuid32 = ntohl(u32); + } + return su; +} + +static void bta_create_mns_sdp_record(bluetooth_sdp_record *record, tSDP_DISC_REC *p_rec) +{ + tSDP_DISC_ATTR *p_attr; + tSDP_PROTOCOL_ELEM pe; + UINT16 pversion = 0; + record->mns.hdr.type = SDP_TYPE_MAP_MNS; + record->mns.hdr.service_name_length = 0; + record->mns.hdr.service_name = NULL; + record->mns.hdr.rfcomm_channel_number = 0; + record->mns.hdr.l2cap_psm = -1; + record->mns.hdr.profile_version = 0; + record->mns.supported_features = 0x0000001F; //default value if not found + + if ((p_attr = SDP_FindAttributeInRec(p_rec, ATTR_ID_MAP_SUPPORTED_FEATURES)) != NULL) { + record->mns.supported_features = p_attr->attr_value.v.u32; + } + + if ((p_attr = SDP_FindAttributeInRec(p_rec, ATTR_ID_SERVICE_NAME)) != NULL) { + record->mns.hdr.service_name_length = SDP_DISC_ATTR_LEN(p_attr->attr_len_type); + record->mns.hdr.service_name = (char *)p_attr->attr_value.v.array; + } + + if (SDP_FindProfileVersionInRec(p_rec, UUID_SERVCLASS_MAP_PROFILE, &pversion)) { + record->mns.hdr.profile_version = pversion; + } + + if (SDP_FindProtocolListElemInRec(p_rec, UUID_PROTOCOL_RFCOMM, &pe)) { + record->mns.hdr.rfcomm_channel_number = pe.params[0]; + } + + if ((p_attr = SDP_FindAttributeInRec(p_rec, ATTR_ID_GOEP_L2CAP_PSM)) != NULL) { + record->mns.hdr.l2cap_psm = p_attr->attr_value.v.u16; + } +} + +static void bta_create_mas_sdp_record(bluetooth_sdp_record *record, tSDP_DISC_REC *p_rec) +{ + tSDP_DISC_ATTR *p_attr; + tSDP_PROTOCOL_ELEM pe; + UINT16 pversion = -1; + + record->mas.hdr.type = SDP_TYPE_MAP_MAS; + record->mas.hdr.service_name_length = 0; + record->mas.hdr.service_name = NULL; + record->mas.hdr.rfcomm_channel_number = 0; + record->mas.hdr.l2cap_psm = -1; + record->mas.hdr.profile_version = 0; + record->mas.mas_instance_id = 0; + record->mas.supported_features = 0x0000001F; + record->mas.supported_message_types = 0; + + if ((p_attr = SDP_FindAttributeInRec(p_rec, ATTR_ID_MAS_INSTANCE_ID)) != NULL) { + record->mas.mas_instance_id = p_attr->attr_value.v.u8; + } + + if ((p_attr = SDP_FindAttributeInRec(p_rec, ATTR_ID_SUPPORTED_MSG_TYPE)) != NULL) { + record->mas.supported_message_types = p_attr->attr_value.v.u8; + } + + if ((p_attr = SDP_FindAttributeInRec(p_rec, ATTR_ID_MAP_SUPPORTED_FEATURES)) != NULL) { + record->mas.supported_features = p_attr->attr_value.v.u32; + } + + if ((p_attr = SDP_FindAttributeInRec(p_rec, ATTR_ID_SERVICE_NAME)) != NULL) { + record->mas.hdr.service_name_length = SDP_DISC_ATTR_LEN(p_attr->attr_len_type); + record->mas.hdr.service_name = (char *)p_attr->attr_value.v.array; + } + + if (SDP_FindProfileVersionInRec(p_rec, UUID_SERVCLASS_MAP_PROFILE, &pversion)) { + record->mas.hdr.profile_version = pversion; + } + + if (SDP_FindProtocolListElemInRec(p_rec, UUID_PROTOCOL_RFCOMM, &pe)) { + record->mas.hdr.rfcomm_channel_number = pe.params[0]; + } + + if ((p_attr = SDP_FindAttributeInRec(p_rec, ATTR_ID_GOEP_L2CAP_PSM)) != NULL) { + record->mas.hdr.l2cap_psm = p_attr->attr_value.v.u16; + } +} + +static void bta_create_pse_sdp_record(bluetooth_sdp_record *record, tSDP_DISC_REC *p_rec) +{ + tSDP_DISC_ATTR *p_attr; + UINT16 pversion; + tSDP_PROTOCOL_ELEM pe; + + record->pse.hdr.type = SDP_TYPE_PBAP_PSE; + record->pse.hdr.service_name_length = 0; + record->pse.hdr.service_name = NULL; + record->pse.hdr.rfcomm_channel_number = 0; + record->pse.hdr.l2cap_psm = -1; + record->pse.hdr.profile_version = 0; + record->pse.supported_features = 0x00000003; + record->pse.supported_repositories = 0; + + if ((p_attr = SDP_FindAttributeInRec(p_rec, ATTR_ID_SUPPORTED_REPOSITORIES)) != NULL) { + record->pse.supported_repositories = p_attr->attr_value.v.u8; + } + if ((p_attr = SDP_FindAttributeInRec(p_rec, ATTR_ID_PBAP_SUPPORTED_FEATURES)) != NULL) { + record->pse.supported_features = p_attr->attr_value.v.u32; + } + + if ((p_attr = SDP_FindAttributeInRec(p_rec, ATTR_ID_SERVICE_NAME)) != NULL) { + record->pse.hdr.service_name_length = SDP_DISC_ATTR_LEN(p_attr->attr_len_type); + record->pse.hdr.service_name = (char *)p_attr->attr_value.v.array; + } + + if (SDP_FindProfileVersionInRec(p_rec, UUID_SERVCLASS_PHONE_ACCESS, &pversion)) { + record->pse.hdr.profile_version = pversion; + } + + if (SDP_FindProtocolListElemInRec(p_rec, UUID_PROTOCOL_RFCOMM, &pe)) { + record->pse.hdr.rfcomm_channel_number = pe.params[0]; + } + + if ((p_attr = SDP_FindAttributeInRec(p_rec, ATTR_ID_GOEP_L2CAP_PSM)) != NULL) { + record->pse.hdr.l2cap_psm = p_attr->attr_value.v.u16; + } +} + +static void bta_create_pce_sdp_record(bluetooth_sdp_record *record, tSDP_DISC_REC *p_rec) +{ + tSDP_DISC_ATTR *p_attr; + UINT16 pversion; + + record->pce.hdr.type = SDP_TYPE_PBAP_PCE; + record->pce.hdr.service_name_length = 0; + record->pce.hdr.service_name = NULL; + record->pce.hdr.rfcomm_channel_number = 0; // unused + record->pce.hdr.l2cap_psm = -1; // unused + record->pce.hdr.profile_version = 0; + + if ((p_attr = SDP_FindAttributeInRec(p_rec, ATTR_ID_SERVICE_NAME)) != NULL) { + record->pce.hdr.service_name_length = SDP_DISC_ATTR_LEN(p_attr->attr_len_type); + record->pce.hdr.service_name = (char *)p_attr->attr_value.v.array; + } + + if (SDP_FindProfileVersionInRec(p_rec, UUID_SERVCLASS_PHONE_ACCESS, &pversion)) { + record->pce.hdr.profile_version = pversion; + } +} + +static void bta_create_ops_sdp_record(bluetooth_sdp_record *record, tSDP_DISC_REC *p_rec) +{ + tSDP_DISC_ATTR *p_attr, *p_sattr; + tSDP_PROTOCOL_ELEM pe; + UINT16 pversion = -1; + + record->ops.hdr.type = SDP_TYPE_OPP_SERVER; + record->ops.hdr.service_name_length = 0; + record->ops.hdr.service_name = NULL; + record->ops.hdr.rfcomm_channel_number = 0; + record->ops.hdr.l2cap_psm = -1; + record->ops.hdr.profile_version = 0; + record->ops.supported_formats_list_len = 0; + + if ((p_attr = SDP_FindAttributeInRec(p_rec, ATTR_ID_SERVICE_NAME)) != NULL) { + record->ops.hdr.service_name_length = SDP_DISC_ATTR_LEN(p_attr->attr_len_type); + record->ops.hdr.service_name = (char *)p_attr->attr_value.v.array; + } + + if (SDP_FindProfileVersionInRec(p_rec, UUID_SERVCLASS_OBEX_OBJECT_PUSH, &pversion)) { + record->ops.hdr.profile_version = pversion; + } + + if (SDP_FindProtocolListElemInRec(p_rec, UUID_PROTOCOL_RFCOMM, &pe)) { + record->ops.hdr.rfcomm_channel_number = pe.params[0]; + } + + if ((p_attr = SDP_FindAttributeInRec(p_rec, ATTR_ID_GOEP_L2CAP_PSM)) != NULL) { + record->ops.hdr.l2cap_psm = p_attr->attr_value.v.u16; + } + if ((p_attr = SDP_FindAttributeInRec(p_rec, ATTR_ID_SUPPORTED_FORMATS_LIST)) != NULL) { + /* Safety check - each entry should itself be a sequence */ + if (SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) != DATA_ELE_SEQ_DESC_TYPE) { + record->ops.supported_formats_list_len = 0; + APPL_TRACE_ERROR("%s() - supported_formats_list - wrong attribute length/type:" + " 0x%02x - expected 0x06", __func__, p_attr->attr_len_type); + } else { + int count = 0; + /* 1 byte for type/length 1 byte for value */ + record->ops.supported_formats_list_len = SDP_DISC_ATTR_LEN(p_attr->attr_len_type) / 2; + + /* Extract each value into */ + for (p_sattr = p_attr->attr_value.v.p_sub_attr; + p_sattr != NULL; p_sattr = p_sattr->p_next_attr) { + if ((SDP_DISC_ATTR_TYPE(p_sattr->attr_len_type) == UINT_DESC_TYPE) + && (SDP_DISC_ATTR_LEN(p_sattr->attr_len_type) == 1)) { + if (count == sizeof(record->ops.supported_formats_list)) { + APPL_TRACE_ERROR("%s() - supported_formats_list - count overflow - " + "too many sub attributes!!\n", __func__); + /* If you hit this, new formats have been added, + * update SDP_OPP_SUPPORTED_FORMATS_MAX_LENGTH */ + break; + } + record->ops.supported_formats_list[count] = p_sattr->attr_value.v.u8; + count++; + } else { + APPL_TRACE_ERROR("%s() - supported_formats_list - wrong sub attribute " + "length/type: 0x%02x - expected 0x80", __func__, + p_sattr->attr_len_type); + break; + } + } + if (record->ops.supported_formats_list_len != count) { + APPL_TRACE_WARNING("%s() - supported_formats_list - Length of attribute different " + "from the actual number of sub-attributes in the sequence " + "att-length: %d - number of elements: %d\n", __func__, + record->ops.supported_formats_list_len , count); + + } + record->ops.supported_formats_list_len = count; + } + } +} + +static void bta_create_sap_sdp_record(bluetooth_sdp_record *record, tSDP_DISC_REC *p_rec) +{ + tSDP_DISC_ATTR *p_attr; + tSDP_PROTOCOL_ELEM pe; + UINT16 pversion = -1; + + record->sap.hdr.type = SDP_TYPE_MAP_MAS; + record->sap.hdr.service_name_length = 0; + record->sap.hdr.service_name = NULL; + record->sap.hdr.rfcomm_channel_number = 0; + record->sap.hdr.l2cap_psm = -1; + record->sap.hdr.profile_version = 0; + + if ((p_attr = SDP_FindAttributeInRec(p_rec, ATTR_ID_SERVICE_NAME)) != NULL) { + record->sap.hdr.service_name_length = SDP_DISC_ATTR_LEN(p_attr->attr_len_type); + record->sap.hdr.service_name = (char *)p_attr->attr_value.v.array; + } + + if (SDP_FindProfileVersionInRec(p_rec, UUID_SERVCLASS_SAP, &pversion)) { + record->sap.hdr.profile_version = pversion; + } + + if (SDP_FindProtocolListElemInRec(p_rec, UUID_PROTOCOL_RFCOMM, &pe)) { + record->sap.hdr.rfcomm_channel_number = pe.params[0]; + } +} + +static void bta_create_raw_sdp_record(bluetooth_sdp_record *record, tSDP_DISC_REC *p_rec) +{ + tSDP_DISC_ATTR *p_attr; + tSDP_PROTOCOL_ELEM pe; + + record->hdr.type = SDP_TYPE_RAW; + record->hdr.service_name_length = 0; + record->hdr.service_name = NULL; + record->hdr.rfcomm_channel_number = -1; + record->hdr.l2cap_psm = -1; + record->hdr.profile_version = -1; + + /* Try to extract a service name */ + if ((p_attr = SDP_FindAttributeInRec(p_rec, ATTR_ID_SERVICE_NAME)) != NULL) { + record->pse.hdr.service_name_length = SDP_DISC_ATTR_LEN(p_attr->attr_len_type); + record->pse.hdr.service_name = (char *)p_attr->attr_value.v.array; + } + + if ((p_attr = SDP_FindAttributeInRec(p_rec, ATTR_ID_GOEP_L2CAP_PSM)) != NULL) { + record->hdr.l2cap_psm = p_attr->attr_value.v.u16; + } + + /* Try to extract an RFCOMM channel */ + if (SDP_FindProtocolListElemInRec(p_rec, UUID_PROTOCOL_RFCOMM, &pe)) { + record->pse.hdr.rfcomm_channel_number = pe.params[0]; + } + record->hdr.user1_ptr_len = p_bta_sdp_cfg->p_sdp_db->raw_size; + record->hdr.user1_ptr = p_bta_sdp_cfg->p_sdp_db->raw_data; +} + + +/******************************************************************************* +** +** Function bta_sdp_search_cback +** +** Description Callback from btm after search is completed +** +** Returns void +** +*******************************************************************************/ +static void bta_sdp_search_cback(UINT16 result, void *user_data) +{ + tSDP_DISC_REC *p_rec = NULL; + tBTA_SDP_SEARCH_COMP evt_data = {0}; // We need to zero-initialize + tBTA_SDP_STATUS status = BTA_SDP_FAILURE; + int count = 0; + tBT_UUID su; + APPL_TRACE_DEBUG("%s() - res: 0x%x\n", __func__, result); + + bta_sdp_cb.sdp_active = BTA_SDP_ACTIVE_NONE; + + if (bta_sdp_cb.p_dm_cback == NULL) { + return; + } + + bdcpy(evt_data.remote_addr, bta_sdp_cb.remote_addr); + tBT_UUID *uuid = (tBT_UUID *)user_data; + memcpy(&evt_data.uuid, uuid, sizeof(tBT_UUID)); + su = shorten_sdp_uuid(uuid); + + if (result == SDP_SUCCESS || result == SDP_DB_FULL) { + do { + p_rec = SDP_FindServiceUUIDInDb(p_bta_sdp_cfg->p_sdp_db, &su, p_rec); + /* generate the matching record data pointer */ + if (p_rec != NULL) { + status = BTA_SDP_SUCCESS; + if (IS_UUID(UUID_MAP_MAS, uuid->uu.uuid128)) { + APPL_TRACE_DEBUG("%s() - found MAP (MAS) uuid\n", __func__); + bta_create_mas_sdp_record(&evt_data.records[count], p_rec); + } else if (IS_UUID(UUID_MAP_MNS, uuid->uu.uuid128)) { + APPL_TRACE_DEBUG("%s() - found MAP (MNS) uuid\n", __func__); + bta_create_mns_sdp_record(&evt_data.records[count], p_rec); + } else if (IS_UUID(UUID_PBAP_PSE, uuid->uu.uuid128)) { + APPL_TRACE_DEBUG("%s() - found PBAP (PSE) uuid\n", __func__); + bta_create_pse_sdp_record(&evt_data.records[count], p_rec); + } else if (IS_UUID(UUID_PBAP_PCE, uuid->uu.uuid128)) { + APPL_TRACE_DEBUG("%s() - found PBAP (PCE) uuid\n", __func__); + bta_create_pce_sdp_record(&evt_data.records[count], p_rec); + } else if (IS_UUID(UUID_OBEX_OBJECT_PUSH, uuid->uu.uuid128)) { + APPL_TRACE_DEBUG("%s() - found Object Push Server (OPS) uuid\n", __func__); + bta_create_ops_sdp_record(&evt_data.records[count], p_rec); + } else if (IS_UUID(UUID_SAP, uuid->uu.uuid128)) { + APPL_TRACE_DEBUG("%s() - found SAP uuid\n", __func__); + bta_create_sap_sdp_record(&evt_data.records[count], p_rec); + } else { + + /* we do not have specific structure for this */ + APPL_TRACE_DEBUG("%s() - profile not identified. using raw data\n", __func__); + bta_create_raw_sdp_record(&evt_data.records[count], p_rec); + p_rec = NULL; // Terminate loop + /* For raw, we only extract the first entry, and then return the entire + raw data chunk. + TODO: Find a way to split the raw data into record chunks, and iterate + to extract generic data for each chunk - e.g. rfcomm channel and + service name. */ + } + count++; + } else { + APPL_TRACE_DEBUG("%s() - UUID not found\n", __func__); + } + } while (p_rec != NULL && count < BTA_SDP_MAX_RECORDS); + + evt_data.record_count = count; + } + evt_data.status = status; + + bta_sdp_cb.p_dm_cback(BTA_SDP_SEARCH_COMP_EVT, (tBTA_SDP *) &evt_data, (void *)&uuid->uu.uuid128); + osi_free(user_data); // We no longer need the user data to track the search +} + +/******************************************************************************* +** +** Function bta_sdp_enable +** +** Description Initializes the SDP I/F +** +** Returns void +** +*******************************************************************************/ +void bta_sdp_enable(tBTA_SDP_MSG *p_data) +{ + APPL_TRACE_DEBUG("%s in, sdp_active:%d\n", __func__, bta_sdp_cb.sdp_active); + tBTA_SDP bta_sdp; + bta_sdp.status = BTA_SDP_SUCCESS; + bta_sdp_cb.p_dm_cback = p_data->enable.p_cback; + bta_sdp_cb.p_dm_cback(BTA_SDP_ENABLE_EVT, (tBTA_SDP *)&bta_sdp, NULL); +} + +/******************************************************************************* +** +** Function bta_sdp_search +** +** Description Discovers all sdp records for an uuid on remote device +** +** Returns void +** +*******************************************************************************/ +void bta_sdp_search(tBTA_SDP_MSG *p_data) +{ + int x = 0; + // TODO: Leaks!!! but needed as user-data pointer + tBT_UUID *bta_sdp_search_uuid = osi_malloc(sizeof(tBT_UUID)); + if (p_data == NULL) { + APPL_TRACE_DEBUG("SDP control block handle is null\n"); + return; + } + tBTA_SDP_STATUS status = BTA_SDP_FAILURE; + + APPL_TRACE_DEBUG("%s in, sdp_active:%d\n", __func__, bta_sdp_cb.sdp_active); + + if (bta_sdp_cb.sdp_active != BTA_SDP_ACTIVE_NONE) { + /* SDP is still in progress */ + status = BTA_SDP_BUSY; + if (bta_sdp_cb.p_dm_cback) { + tBTA_SDP_SEARCH_COMP result = {0}; + result.uuid = p_data->get_search.uuid; + bdcpy(result.remote_addr, p_data->get_search.bd_addr); + result.status = status; + bta_sdp_cb.p_dm_cback(BTA_SDP_SEARCH_COMP_EVT, (tBTA_SDP *)&result, NULL); + } + return; + } + + bta_sdp_cb.sdp_active = BTA_SDP_ACTIVE_YES; + bdcpy(bta_sdp_cb.remote_addr, p_data->get_search.bd_addr); + /* set the uuid used in the search */ + memcpy(bta_sdp_search_uuid, &(p_data->get_search.uuid), sizeof(tBT_UUID)); + + /* initialize the search for the uuid */ + APPL_TRACE_DEBUG("%s init discovery with UUID(len: %d):\n", + __func__, bta_sdp_search_uuid->len); + for (x = 0; x < bta_sdp_search_uuid->len; x++) { + APPL_TRACE_DEBUG("%X", bta_sdp_search_uuid->uu.uuid128[x]); + } + SDP_InitDiscoveryDb (p_bta_sdp_cfg->p_sdp_db, p_bta_sdp_cfg->sdp_db_size, 1, + bta_sdp_search_uuid, 0, NULL); + + if (!SDP_ServiceSearchAttributeRequest2(p_data->get_search.bd_addr, p_bta_sdp_cfg->p_sdp_db, + bta_sdp_search_cback, (void *)bta_sdp_search_uuid)) { + bta_sdp_cb.sdp_active = BTA_SDP_ACTIVE_NONE; + + /* failed to start SDP. report the failure right away */ + if (bta_sdp_cb.p_dm_cback) { + tBTA_SDP_SEARCH_COMP result = {0}; + result.uuid = p_data->get_search.uuid; + bdcpy(result.remote_addr, p_data->get_search.bd_addr); + result.status = status; + bta_sdp_cb.p_dm_cback(BTA_SDP_SEARCH_COMP_EVT, (tBTA_SDP *)&result, NULL); + } + } + /* + else report the result when the cback is called + */ +} + +/******************************************************************************* +** +** Function bta_sdp_create_record +** +** Description Creates an SDP record for a handle +** +** Returns void +** +*******************************************************************************/ +void bta_sdp_create_record(tBTA_SDP_MSG *p_data) +{ + APPL_TRACE_DEBUG("%s() event: %d\n", __func__, p_data->record.hdr.event); + tBTA_SDP_CREATE_RECORD_USER bta_sdp = {0}; + bta_sdp.status = BTA_SDP_SUCCESS; + bta_sdp.handle = (int)p_data->record.user_data; + if (bta_sdp_cb.p_dm_cback) { + bta_sdp_cb.p_dm_cback(BTA_SDP_CREATE_RECORD_USER_EVT, (tBTA_SDP *)&bta_sdp, p_data->record.user_data); + } +} + +/******************************************************************************* +** +** Function bta_sdp_remove_record +** +** Description Removes an SDP record +** +** Returns void +** +*******************************************************************************/ +void bta_sdp_remove_record(tBTA_SDP_MSG *p_data) +{ + APPL_TRACE_DEBUG("%s() event: %d\n", __func__, p_data->record.hdr.event); + tBTA_SDP bta_sdp; + bta_sdp.status = BTA_SDP_SUCCESS; + if (bta_sdp_cb.p_dm_cback) { + bta_sdp_cb.p_dm_cback(BTA_SDP_REMOVE_RECORD_USER_EVT, &bta_sdp, p_data->record.user_data); + } +} + +#endif ///SDP_INCLUDED == TRUE diff --git a/lib/bt/host/bluedroid/bta/sdp/bta_sdp_api.c b/lib/bt/host/bluedroid/bta/sdp/bta_sdp_api.c new file mode 100644 index 00000000..cafb1a4a --- /dev/null +++ b/lib/bt/host/bluedroid/bta/sdp/bta_sdp_api.c @@ -0,0 +1,203 @@ +/****************************************************************************** + * + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This is the implementation of the API for SDP search subsystem + * + ******************************************************************************/ + +#include "common/bt_target.h" +#include "bta/bta_api.h" +#include "bta/bta_sys.h" +#include "bta/bta_sdp_api.h" +#include "bta_sdp_int.h" +#include +#include "osi/allocator.h" +#include "stack/sdp_api.h" + +#if defined(BTA_SDP_INCLUDED) && (BTA_SDP_INCLUDED == TRUE) +/***************************************************************************** +** Constants +*****************************************************************************/ + +static const tBTA_SYS_REG bta_sdp_reg = { + bta_sdp_sm_execute, + NULL +}; + +/******************************************************************************* +** +** Function BTA_SdpEnable +** +** Description Enable the SDP search I/F service. When the enable +** operation is complete the callback function will be +** called with a BTA_SDP_ENABLE_EVT. This function must +** be called before other functions in the SDP search API are +** called. +** +** Returns BTA_SDP_SUCCESS if successful. +** BTA_SDP_FAIL if internal failure. +** +*******************************************************************************/ +tBTA_SDP_STATUS BTA_SdpEnable(tBTA_SDP_DM_CBACK *p_cback) +{ + tBTA_SDP_STATUS status = BTA_SDP_FAILURE; + tBTA_SDP_API_ENABLE *p_buf; + + APPL_TRACE_API("%s\n", __FUNCTION__); + +#if BTA_DYNAMIC_MEMORY == TRUE + /* Malloc buffer for SDP configuration structure */ + p_bta_sdp_cfg->p_sdp_db = (tSDP_DISCOVERY_DB *)osi_malloc(p_bta_sdp_cfg->sdp_db_size); + if (p_bta_sdp_cfg->p_sdp_db == NULL) { + return BTA_SDP_FAILURE; + } +#endif + + if (p_cback && FALSE == bta_sys_is_register(BTA_ID_SDP)) { + memset(&bta_sdp_cb, 0, sizeof(tBTA_SDP_CB)); + + /* register with BTA system manager */ + bta_sys_register(BTA_ID_SDP, &bta_sdp_reg); + + if (p_cback && + (p_buf = (tBTA_SDP_API_ENABLE *) osi_malloc(sizeof(tBTA_SDP_API_ENABLE))) != NULL) { + p_buf->hdr.event = BTA_SDP_API_ENABLE_EVT; + p_buf->p_cback = p_cback; + bta_sys_sendmsg(p_buf); + status = BTA_SDP_SUCCESS; + } + } + return (status); +} + + +/******************************************************************************* +** +** Function BTA_SdpDisable +** +** Description Disable the SDP search I/F service. +** Free buffer for SDP configuration structure. +** +** Returns BTA_SDP_SUCCESS if successful. +** BTA_SDP_FAIL if internal failure. +** +*******************************************************************************/ +tBTA_SDP_STATUS BTA_SdpDisable(void) +{ + tBTA_SDP_STATUS status = BTA_SDP_SUCCESS; + + bta_sys_deregister(BTA_ID_SDP); +#if BTA_DYNAMIC_MEMORY == TRUE + /* Free buffer for SDP configuration structure */ + osi_free(p_bta_sdp_cfg->p_sdp_db); + p_bta_sdp_cfg->p_sdp_db = NULL; +#endif + return (status); +} + +/******************************************************************************* +** +** Function BTA_SdpSearch +** +** Description This function performs service discovery for a specific service +** on given peer device. When the operation is completed +** the tBTA_SDP_DM_CBACK callback function will be called with +** a BTA_SDP_SEARCH_COMPLETE_EVT. +** +** Returns BTA_SDP_SUCCESS, if the request is being processed. +** BTA_SDP_FAILURE, otherwise. +** +*******************************************************************************/ +tBTA_SDP_STATUS BTA_SdpSearch(BD_ADDR bd_addr, tSDP_UUID *uuid) +{ + tBTA_SDP_STATUS ret = BTA_SDP_FAILURE; + tBTA_SDP_API_SEARCH *p_msg; + + APPL_TRACE_API("%s\n", __FUNCTION__); + if ((p_msg = (tBTA_SDP_API_SEARCH *)osi_malloc(sizeof(tBTA_SDP_API_SEARCH))) != NULL) { + p_msg->hdr.event = BTA_SDP_API_SEARCH_EVT; + bdcpy(p_msg->bd_addr, bd_addr); + //p_msg->uuid = uuid; + memcpy(&(p_msg->uuid), uuid, sizeof(tSDP_UUID)); + bta_sys_sendmsg(p_msg); + ret = BTA_SDP_SUCCESS; + } + + return (ret); +} + +/******************************************************************************* +** +** Function BTA_SdpCreateRecordByUser +** +** Description This function is used to request a callback to create a SDP +** record. The registered callback will be called with event +** BTA_SDP_CREATE_RECORD_USER_EVT. +** +** Returns BTA_SDP_SUCCESS, if the request is being processed. +** BTA_SDP_FAILURE, otherwise. +** +*******************************************************************************/ +tBTA_SDP_STATUS BTA_SdpCreateRecordByUser(void *user_data) +{ + tBTA_SDP_STATUS ret = BTA_SDP_FAILURE; + tBTA_SDP_API_RECORD_USER *p_msg; + + APPL_TRACE_API("%s\n", __FUNCTION__); + if ((p_msg = (tBTA_SDP_API_RECORD_USER *)osi_malloc(sizeof(tBTA_SDP_API_RECORD_USER))) != NULL) { + p_msg->hdr.event = BTA_SDP_API_CREATE_RECORD_USER_EVT; + p_msg->user_data = user_data; + bta_sys_sendmsg(p_msg); + ret = BTA_SDP_SUCCESS; + } + + return (ret); +} + +/******************************************************************************* +** +** Function BTA_SdpRemoveRecordByUser +** +** Description This function is used to request a callback to remove a SDP +** record. The registered callback will be called with event +** BTA_SDP_REMOVE_RECORD_USER_EVT. +** +** Returns BTA_SDP_SUCCESS, if the request is being processed. +** BTA_SDP_FAILURE, otherwise. +** +*******************************************************************************/ +tBTA_SDP_STATUS BTA_SdpRemoveRecordByUser(void *user_data) +{ + tBTA_SDP_STATUS ret = BTA_SDP_FAILURE; + tBTA_SDP_API_RECORD_USER *p_msg; + + APPL_TRACE_API("%s\n", __FUNCTION__); + if ((p_msg = (tBTA_SDP_API_RECORD_USER *)osi_malloc(sizeof(tBTA_SDP_API_RECORD_USER))) != NULL) { + p_msg->hdr.event = BTA_SDP_API_REMOVE_RECORD_USER_EVT; + p_msg->user_data = user_data; + bta_sys_sendmsg(p_msg); + ret = BTA_SDP_SUCCESS; + } + + return (ret); +} + + +#endif /* #if defined(BTA_SDP_INCLUDED) && (BTA_SDP_INCLUDED == TRUE) */ diff --git a/lib/bt/host/bluedroid/bta/sdp/bta_sdp_cfg.c b/lib/bt/host/bluedroid/bta/sdp/bta_sdp_cfg.c new file mode 100644 index 00000000..322b25ca --- /dev/null +++ b/lib/bt/host/bluedroid/bta/sdp/bta_sdp_cfg.c @@ -0,0 +1,49 @@ +/****************************************************************************** + * + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * This file contains compile-time configurable constants for SDP Search + ******************************************************************************/ + +#include "common/bt_target.h" +#include "bta/bta_api.h" +#include "bta/bta_sdp_api.h" + +#if defined(BTA_SDP_INCLUDED) && (BTA_SDP_INCLUDED == TRUE) + +#ifndef BTA_SDP_DB_SIZE +#define BTA_SDP_DB_SIZE 1500 +#endif + +#if BTA_DYNAMIC_MEMORY == FALSE +static UINT8 __attribute__ ((aligned(4))) bta_sdp_db_data[BTA_SDP_DB_SIZE]; +#endif + +/* SDP configuration structure */ +tBTA_SDP_CFG bta_sdp_cfg = { + BTA_SDP_DB_SIZE, +#if BTA_DYNAMIC_MEMORY == FALSE + (tSDP_DISCOVERY_DB *)bta_sdp_db_data /* The data buffer to keep SDP database */ +#else + NULL +#endif +}; + +tBTA_SDP_CFG *p_bta_sdp_cfg = (tBTA_SDP_CFG *) &bta_sdp_cfg; + +#endif /* #if defined(BTA_SDP_INCLUDED) && (BTA_SDP_INCLUDED == TRUE) */ diff --git a/lib/bt/host/bluedroid/bta/sdp/include/bta_sdp_int.h b/lib/bt/host/bluedroid/bta/sdp/include/bta_sdp_int.h new file mode 100644 index 00000000..cf0eb99c --- /dev/null +++ b/lib/bt/host/bluedroid/bta/sdp/include/bta_sdp_int.h @@ -0,0 +1,111 @@ + + +/****************************************************************************** + * + * Copyright (C) 2014 The Android Open Source Project + * Copyright (C) 2003-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This is the private interface file for the BTA SDP I/F + * + ******************************************************************************/ +#ifndef BTA_SDP_INT_H +#define BTA_SDP_INT_H + +#include "bta/bta_sys.h" +#include "bta/bta_api.h" +#include "bta/bta_sdp_api.h" + +#if (SDP_INCLUDED == TRUE) +/***************************************************************************** +** Constants +*****************************************************************************/ + +enum { + /* these events are handled by the state machine */ + BTA_SDP_API_ENABLE_EVT = BTA_SYS_EVT_START(BTA_ID_SDP), + BTA_SDP_API_SEARCH_EVT, + BTA_SDP_API_CREATE_RECORD_USER_EVT, + BTA_SDP_API_REMOVE_RECORD_USER_EVT, + BTA_SDP_MAX_INT_EVT +}; + +enum { + BTA_SDP_ACTIVE_NONE = 0, + BTA_SDP_ACTIVE_YES /* waiting for SDP result */ +}; + + + +/* data type for BTA_SDP_API_ENABLE_EVT */ +typedef struct { + BT_HDR hdr; + tBTA_SDP_DM_CBACK *p_cback; +} tBTA_SDP_API_ENABLE; + +/* data type for BTA_SDP_API_SEARCH_EVT */ +typedef struct { + BT_HDR hdr; + BD_ADDR bd_addr; + tSDP_UUID uuid; +} tBTA_SDP_API_SEARCH; + +/* data type for BTA_SDP_API_SEARCH_EVT */ +typedef struct { + BT_HDR hdr; + void *user_data; +} tBTA_SDP_API_RECORD_USER; + +/* union of all data types */ +typedef union { + /* event buffer header */ + BT_HDR hdr; + tBTA_SDP_API_ENABLE enable; + tBTA_SDP_API_SEARCH get_search; + tBTA_SDP_API_RECORD_USER record; +} tBTA_SDP_MSG; + +/* SDP control block */ +typedef struct { + UINT8 sdp_active; /* see BTA_SDP_SDP_ACT_* */ + BD_ADDR remote_addr; + tBTA_SDP_DM_CBACK *p_dm_cback; +} tBTA_SDP_CB; + + +/* SDP control block */ +#if BTA_DYNAMIC_MEMORY == FALSE +extern tBTA_SDP_CB bta_sdp_cb; +#else +extern tBTA_SDP_CB *bta_sdp_cb_ptr; +#define bta_sdp_cb (*bta_sdp_cb_ptr) +#endif + +/* config struct */ +extern tBTA_SDP_CFG *p_bta_sdp_cfg; + +extern BOOLEAN bta_sdp_sm_execute(BT_HDR *p_msg); + +extern void bta_sdp_enable (tBTA_SDP_MSG *p_data); +extern void bta_sdp_search (tBTA_SDP_MSG *p_data); +extern void bta_sdp_create_record(tBTA_SDP_MSG *p_data); +extern void bta_sdp_remove_record(tBTA_SDP_MSG *p_data); + +#endif ///SDP_INCLUDED == TRUE + +#endif /* BTA_SDP_INT_H */ diff --git a/lib/bt/host/bluedroid/bta/sys/bta_sys_conn.c b/lib/bt/host/bluedroid/bta/sys/bta_sys_conn.c new file mode 100644 index 00000000..914df55d --- /dev/null +++ b/lib/bt/host/bluedroid/bta/sys/bta_sys_conn.c @@ -0,0 +1,663 @@ +/****************************************************************************** + * + * Copyright (C) 2003-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * Routes connection status callbacks from various sub systems to DM + * + ******************************************************************************/ + +#include +#include "bta/bta_api.h" +#include "bta/bta_sys.h" +#include "bta_sys_int.h" +#include "bta/utl.h" + +/******************************************************************************* +** +** Function bta_sys_rm_register +** +** Description Called by BTA DM to register role management callbacks +** +** +** Returns void +** +*******************************************************************************/ +void bta_sys_rm_register(tBTA_SYS_CONN_CBACK *p_cback) +{ + bta_sys_cb.prm_cb = p_cback; +} + + +/******************************************************************************* +** +** Function bta_sys_policy_register +** +** Description Called by BTA DM to register link policy change callbacks +** +** +** Returns void +** +*******************************************************************************/ +void bta_sys_policy_register(tBTA_SYS_CONN_CBACK *p_cback) +{ + bta_sys_cb.p_policy_cb = p_cback; +} + +/******************************************************************************* +** +** Function bta_sys_role_chg_register +** +** Description Called by BTA AV to register role change callbacks +** +** +** Returns void +** +*******************************************************************************/ +void bta_sys_role_chg_register(tBTA_SYS_CONN_CBACK *p_cback) +{ + bta_sys_cb.p_role_cb = p_cback; +} +/******************************************************************************* +** +** Function bta_sys_ssr_cfg_register +** +** Description Called by BTA DM to register SSR configuration callback +** +** +** Returns void +** +*******************************************************************************/ +#if (BTM_SSR_INCLUDED == TRUE) +void bta_sys_ssr_cfg_register(tBTA_SYS_SSR_CFG_CBACK *p_cback) +{ + bta_sys_cb.p_ssr_cb = p_cback; +} +#endif +/******************************************************************************* +** +** Function bta_sys_role_chg_register +** +** Description Called by BTA AV to register role change callbacks +** +** +** Returns void +** +*******************************************************************************/ +void bta_sys_notify_role_chg(BD_ADDR_PTR p_bda, UINT8 new_role, UINT8 hci_status) +{ + if (bta_sys_cb.p_role_cb) { + bta_sys_cb.p_role_cb(BTA_SYS_ROLE_CHANGE, new_role, hci_status, p_bda); + } +} + +/******************************************************************************* +** +** Function bta_sys_collision_register +** +** Description Called by any BTA module to register for collision event. +** +** +** Returns void +** +*******************************************************************************/ +void bta_sys_collision_register(UINT8 bta_id, tBTA_SYS_CONN_CBACK *p_cback) +{ + UINT8 index; + + for (index = 0; index < MAX_COLLISION_REG; index++) { + if ((bta_sys_cb.colli_reg.id[index] == bta_id) || + (bta_sys_cb.colli_reg.id[index] == 0)) { + bta_sys_cb.colli_reg.id[index] = bta_id; + bta_sys_cb.colli_reg.p_coll_cback[index] = p_cback; + return; + } + } +} + +/******************************************************************************* +** +** Function bta_sys_notify_collision +** +** Description Called by BTA DM to notify collision event. +** +** +** Returns void +** +*******************************************************************************/ +void bta_sys_notify_collision (BD_ADDR_PTR p_bda) +{ + UINT8 index; + + for (index = 0; index < MAX_COLLISION_REG; index++) { + if ((bta_sys_cb.colli_reg.id[index] != 0) && + (bta_sys_cb.colli_reg.p_coll_cback[index] != NULL)) { + bta_sys_cb.colli_reg.p_coll_cback[index] (0, BTA_ID_SYS, 0, p_bda); + } + } +} + +/******************************************************************************* +** +** Function bta_sys_sco_register +** +** Description Called by BTA AV to register sco connection change callbacks +** +** +** Returns void +** +*******************************************************************************/ +void bta_sys_sco_register(tBTA_SYS_CONN_CBACK *p_cback) +{ + bta_sys_cb.p_sco_cb = p_cback; +} + +/******************************************************************************* +** +** Function bta_sys_pm_register +** +** Description Called by BTA DM to register power management callbacks +** +** +** Returns void +** +*******************************************************************************/ +void bta_sys_pm_register(tBTA_SYS_CONN_CBACK *p_cback) +{ + bta_sys_cb.ppm_cb = p_cback; +} + +/******************************************************************************* +** +** Function bta_sys_conn_open +** +** Description Called by BTA subsystems when a connection is made to +** the service +** +** +** Returns void +** +*******************************************************************************/ +void bta_sys_conn_open(UINT8 id, UINT8 app_id, BD_ADDR peer_addr) +{ + if (bta_sys_cb.prm_cb) { + + bta_sys_cb.prm_cb(BTA_SYS_CONN_OPEN, id, app_id, peer_addr); + + } + + if (bta_sys_cb.ppm_cb) { + + bta_sys_cb.ppm_cb(BTA_SYS_CONN_OPEN, id, app_id, peer_addr); + + } +} + + + +/******************************************************************************* +** +** Function bta_sys_conn_close +** +** Description Called by BTA subsystems when a connection to the service +** is closed +** +** +** Returns void +** +*******************************************************************************/ +void bta_sys_conn_close(UINT8 id, UINT8 app_id, BD_ADDR peer_addr) +{ + if (bta_sys_cb.prm_cb) { + + bta_sys_cb.prm_cb(BTA_SYS_CONN_CLOSE, id, app_id, peer_addr); + + } + + if (bta_sys_cb.ppm_cb) { + + bta_sys_cb.ppm_cb(BTA_SYS_CONN_CLOSE, id, app_id, peer_addr); + + } +} + + +/******************************************************************************* +** +** Function bta_sys_app_open +** +** Description Called by BTA subsystems when application initiates connection +** to a peer device +** +** +** Returns void +** +*******************************************************************************/ +void bta_sys_app_open(UINT8 id, UINT8 app_id, BD_ADDR peer_addr) +{ + if (bta_sys_cb.ppm_cb) { + bta_sys_cb.ppm_cb(BTA_SYS_APP_OPEN, id, app_id, peer_addr); + } +} + + + +/******************************************************************************* +** +** Function bta_sys_app_close +** +** Description Called by BTA subsystems when application initiates close +** of connection to peer device +** +** Returns void +** +*******************************************************************************/ +void bta_sys_app_close(UINT8 id, UINT8 app_id, BD_ADDR peer_addr) +{ + if (bta_sys_cb.ppm_cb) { + bta_sys_cb.ppm_cb(BTA_SYS_APP_CLOSE, id, app_id, peer_addr); + } +} + + +/******************************************************************************* +** +** Function bta_sys_sco_open +** +** Description Called by BTA subsystems when sco connection for that service +** is open +** +** Returns void +** +*******************************************************************************/ +void bta_sys_sco_open(UINT8 id, UINT8 app_id, BD_ADDR peer_addr) +{ + /* AG triggers p_sco_cb by bta_sys_sco_use. */ + if ((id != BTA_ID_AG) && (bta_sys_cb.p_sco_cb)) { + /* without querying BTM_GetNumScoLinks() */ + bta_sys_cb.p_sco_cb(BTA_SYS_SCO_OPEN, 1, app_id, peer_addr); + } + + if (bta_sys_cb.ppm_cb) { + bta_sys_cb.ppm_cb(BTA_SYS_SCO_OPEN, id, app_id, peer_addr); + } +} + +/******************************************************************************* +** +** Function bta_sys_sco_close +** +** Description Called by BTA subsystems when sco connection for that service +** is closed +** +** Returns void +** +*******************************************************************************/ +void bta_sys_sco_close(UINT8 id, UINT8 app_id, BD_ADDR peer_addr) +{ + UINT8 num_sco_links; + + if ((id != BTA_ID_AG) && (bta_sys_cb.p_sco_cb)) { + num_sco_links = BTM_GetNumScoLinks(); + bta_sys_cb.p_sco_cb(BTA_SYS_SCO_CLOSE, num_sco_links, app_id, peer_addr); + } + + if (bta_sys_cb.ppm_cb) { + bta_sys_cb.ppm_cb(BTA_SYS_SCO_CLOSE, id, app_id, peer_addr); + } +} + +/******************************************************************************* +** +** Function bta_sys_sco_use +** +** Description Called by BTA subsystems when that service needs to use sco. +** +** +** Returns void +** +*******************************************************************************/ +void bta_sys_sco_use(UINT8 id, UINT8 app_id, BD_ADDR peer_addr) +{ + UNUSED(id); + + /* AV streaming need to be suspended before SCO is connected. */ + if (bta_sys_cb.p_sco_cb) { + /* without querying BTM_GetNumScoLinks() */ + bta_sys_cb.p_sco_cb(BTA_SYS_SCO_OPEN, 1, app_id, peer_addr); + } +} + +/******************************************************************************* +** +** Function bta_sys_sco_unuse +** +** Description Called by BTA subsystems when sco connection for that service +** is no longer needed. +** +** Returns void +** +*******************************************************************************/ +void bta_sys_sco_unuse(UINT8 id, UINT8 app_id, BD_ADDR peer_addr) +{ + UINT8 num_sco_links; + UNUSED(id); + + if ((bta_sys_cb.p_sco_cb)) { + num_sco_links = BTM_GetNumScoLinks(); + bta_sys_cb.p_sco_cb(BTA_SYS_SCO_CLOSE, num_sco_links, app_id, peer_addr); + } +} +/******************************************************************************* +** +** Function bta_sys_chg_ssr_config +** +** Description Called by BTA subsystems to indicate that the given app SSR setting +** need to be changed. +** +** Returns void +** +*******************************************************************************/ +#if (BTM_SSR_INCLUDED == TRUE) +void bta_sys_chg_ssr_config (UINT8 id, UINT8 app_id, UINT16 max_latency, UINT16 min_tout) +{ + if (bta_sys_cb.p_ssr_cb) { + bta_sys_cb.p_ssr_cb(id, app_id, max_latency, min_tout); + } +} +#endif +/******************************************************************************* +** +** Function bta_sys_set_policy +** +** Description Called by BTA subsystems to indicate that the given link +** policy to peer device should be set +** +** Returns void +** +*******************************************************************************/ +void bta_sys_set_policy (UINT8 id, UINT8 policy, BD_ADDR peer_addr) +{ + if (bta_sys_cb.p_policy_cb) { + bta_sys_cb.p_policy_cb(BTA_SYS_PLCY_SET, id, policy, peer_addr); + } +} + +/******************************************************************************* +** +** Function bta_sys_clear_policy +** +** Description Called by BTA subsystems to indicate that the given link +** policy to peer device should be clear +** +** Returns void +** +*******************************************************************************/ +void bta_sys_clear_policy (UINT8 id, UINT8 policy, BD_ADDR peer_addr) +{ + if (bta_sys_cb.p_policy_cb) { + bta_sys_cb.p_policy_cb(BTA_SYS_PLCY_CLR, id, policy, peer_addr); + } +} + +/******************************************************************************* +** +** Function bta_sys_set_default_policy +** +** Description Called by BTA subsystems to indicate that the given default +** link policy should be set +** +** Returns void +** +*******************************************************************************/ +void bta_sys_set_default_policy (UINT8 id, UINT8 policy) +{ + if (bta_sys_cb.p_policy_cb) { + bta_sys_cb.p_policy_cb(BTA_SYS_PLCY_DEF_SET, id, policy, NULL); + } +} + +/******************************************************************************* +** +** Function bta_sys_clear_default_policy +** +** Description Called by BTA subsystems to indicate that the given default +** link policy should be clear +** +** Returns void +** +*******************************************************************************/ +void bta_sys_clear_default_policy (UINT8 id, UINT8 policy) +{ + if (bta_sys_cb.p_policy_cb) { + bta_sys_cb.p_policy_cb(BTA_SYS_PLCY_DEF_CLR, id, policy, NULL); + } +} + +/******************************************************************************* +** +** Function bta_sys_idle +** +** Description Called by BTA subsystems to indicate that the connection to +** peer device is idle +** +** Returns void +** +*******************************************************************************/ +void bta_sys_idle(UINT8 id, UINT8 app_id, BD_ADDR peer_addr) +{ + + if (bta_sys_cb.prm_cb) { + + bta_sys_cb.prm_cb(BTA_SYS_CONN_IDLE, id, app_id, peer_addr); + + } + + if (bta_sys_cb.ppm_cb) { + + bta_sys_cb.ppm_cb(BTA_SYS_CONN_IDLE, id, app_id, peer_addr); + } +} + +/******************************************************************************* +** +** Function bta_sys_busy +** +** Description Called by BTA subsystems to indicate that the connection to +** peer device is busy +** +** Returns void +** +*******************************************************************************/ +void bta_sys_busy(UINT8 id, UINT8 app_id, BD_ADDR peer_addr) +{ + if (bta_sys_cb.prm_cb) { + + bta_sys_cb.prm_cb(BTA_SYS_CONN_BUSY, id, app_id, peer_addr); + + } + + if (bta_sys_cb.ppm_cb) { + + bta_sys_cb.ppm_cb(BTA_SYS_CONN_BUSY, id, app_id, peer_addr); + + } +} + +#if (BTA_EIR_CANNED_UUID_LIST != TRUE) +/******************************************************************************* +** +** Function bta_sys_eir_register +** +** Description Called by BTA DM to register EIR utility function that can be +** used by the other BTA modules to add/remove UUID. +** +** Returns void +** +*******************************************************************************/ +void bta_sys_eir_register(tBTA_SYS_EIR_CBACK *p_cback) +{ + bta_sys_cb.eir_cb = p_cback; +} + +/******************************************************************************* +** +** Function bta_sys_add_uuid +** +** Description Called by BTA subsystems to indicate to DM that new service +** class UUID is added. +** +** Returns void +** +*******************************************************************************/ +void bta_sys_add_uuid(UINT16 uuid16) +{ + tBT_UUID uuid; + uuid.len = LEN_UUID_16; + uuid.uu.uuid16 = uuid16; + + if (bta_sys_cb.eir_cb) { + bta_sys_cb.eir_cb(uuid, TRUE); + } +} + + +/******************************************************************************* +** +** Function bta_sys_add_uuid_32 +** +** Description Called by BTA subsystems to indicate to DM that new service +** class UUID is added. +** +** Returns void +** +*******************************************************************************/ +void bta_sys_add_uuid_32(UINT32 uuid32) +{ + tBT_UUID uuid; + uuid.len = LEN_UUID_32; + uuid.uu.uuid32 = uuid32; + + if (bta_sys_cb.eir_cb) { + bta_sys_cb.eir_cb(uuid, TRUE); + } +} + +/******************************************************************************* +** +** Function bta_sys_add_uuid_128 +** +** Description Called by BTA subsystems to indicate to DM that new service +** class UUID is added. +** +** Returns void +** +*******************************************************************************/ +void bta_sys_add_uuid_128(UINT8 *uuid128) +{ + tBT_UUID uuid; + uuid.len = LEN_UUID_128; + memcpy(&uuid.uu.uuid128, uuid128, LEN_UUID_128); + + if (bta_sys_cb.eir_cb) { + bta_sys_cb.eir_cb(uuid, TRUE); + } +} + +/******************************************************************************* +** +** Function bta_sys_remove_uuid +** +** Description Called by BTA subsystems to indicate to DM that the service +** class UUID is removed. +** +** Returns void +** +*******************************************************************************/ +void bta_sys_remove_uuid(UINT16 uuid16) +{ + tBT_UUID uuid; + uuid.len = LEN_UUID_16; + uuid.uu.uuid16 = uuid16; + + if (bta_sys_cb.eir_cb) { + bta_sys_cb.eir_cb(uuid, FALSE); + } +} + +/******************************************************************************* +** +** Function bta_sys_remove_uuid_32 +** +** Description Called by BTA subsystems to indicate to DM that the service +** class UUID is removed. +** +** Returns void +** +*******************************************************************************/ +void bta_sys_remove_uuid_32(UINT32 uuid32) +{ + tBT_UUID uuid; + uuid.len = LEN_UUID_32; + uuid.uu.uuid32 = uuid32; + + if (bta_sys_cb.eir_cb) { + bta_sys_cb.eir_cb(uuid, FALSE); + } +} + +/******************************************************************************* +** +** Function bta_sys_remove_uuid_128 +** +** Description Called by BTA subsystems to indicate to DM that the service +** class UUID is removed. +** +** Returns void +** +*******************************************************************************/ +void bta_sys_remove_uuid_128(UINT8 *uuid128) +{ + tBT_UUID uuid; + uuid.len = LEN_UUID_128; + memcpy(&uuid.uu.uuid128, uuid128, LEN_UUID_128); + + if (bta_sys_cb.eir_cb) { + bta_sys_cb.eir_cb(uuid, FALSE); + } +} + +#endif + +/******************************************************************************* +** +** Function bta_sys_vs_hdl +** +** Description Called by BTA subsystems to execute a VS event handler function +** +** Returns void +** +*******************************************************************************/ +BOOLEAN bta_sys_vs_hdl(UINT16 evt, void *p) +{ + if (bta_sys_cb.p_vs_evt_hdlr) { + return (*bta_sys_cb.p_vs_evt_hdlr)(evt, p); + } + + return FALSE; +} diff --git a/lib/bt/host/bluedroid/bta/sys/bta_sys_main.c b/lib/bt/host/bluedroid/bta/sys/bta_sys_main.c new file mode 100644 index 00000000..db6ee489 --- /dev/null +++ b/lib/bt/host/bluedroid/bta/sys/bta_sys_main.c @@ -0,0 +1,766 @@ +/****************************************************************************** + * + * Copyright (C) 2003-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This is the main implementation file for the BTA system manager. + * + ******************************************************************************/ +#define LOG_TAG "bt_bta_sys_main" + +#include +#include + +#include "osi/alarm.h" +#include "osi/thread.h" +#include "stack/btm_api.h" +#include "stack/btu.h" +#include "bta/bta_api.h" +#include "bta/bta_sys.h" +#include "bta_sys_int.h" + +#include "osi/fixed_queue.h" +#include "osi/hash_map.h" +#include "osi/osi.h" +#include "osi/hash_functions.h" +#if( defined BTA_AR_INCLUDED ) && (BTA_AR_INCLUDED == TRUE) +#include "bta/bta_ar_api.h" +#endif +#include "bta/utl.h" +#include "osi/allocator.h" +#include "osi/mutex.h" + + +/* system manager control block definition */ +#if BTA_DYNAMIC_MEMORY == FALSE +tBTA_SYS_CB bta_sys_cb; +#else +tBTA_SYS_CB *bta_sys_cb_ptr; +#endif + +static hash_map_t *bta_alarm_hash_map; +static const size_t BTA_ALARM_HASH_MAP_SIZE = 17; +static osi_mutex_t bta_alarm_lock; +// extern thread_t *bt_workqueue_thread; + +/* trace level */ +/* TODO Bluedroid - Hard-coded trace levels - Needs to be configurable */ +UINT8 appl_trace_level = APPL_INITIAL_TRACE_LEVEL; +UINT8 btif_trace_level = BTIF_INITIAL_TRACE_LEVEL; + +void btu_bta_alarm_ready(fixed_queue_t *queue); + +static const tBTA_SYS_REG bta_sys_hw_reg = { + bta_sys_sm_execute, + NULL +}; + + +/* type for action functions */ +typedef void (*tBTA_SYS_ACTION)(tBTA_SYS_HW_MSG *p_data); + +/* action function list */ +const tBTA_SYS_ACTION bta_sys_action[] = { + /* device manager local device API events - cf bta/bta_sys.h for events */ + bta_sys_hw_api_enable, /* 0 BTA_SYS_HW_API_ENABLE_EVT */ + bta_sys_hw_evt_enabled, /* 1 BTA_SYS_HW_EVT_ENABLED_EVT */ + bta_sys_hw_evt_stack_enabled, /* 2 BTA_SYS_HW_EVT_STACK_ENABLED_EVT */ + bta_sys_hw_api_disable, /* 3 BTA_SYS_HW_API_DISABLE_EVT */ + bta_sys_hw_evt_disabled, /* 4 BTA_SYS_HW_EVT_DISABLED_EVT */ + bta_sys_hw_error /* 5 BTA_SYS_HW_ERROR_EVT */ +}; + +/* state machine action enumeration list */ +enum { + /* device manager local device API events */ + BTA_SYS_HW_API_ENABLE, + BTA_SYS_HW_EVT_ENABLED, + BTA_SYS_HW_EVT_STACK_ENABLED, + BTA_SYS_HW_API_DISABLE, + BTA_SYS_HW_EVT_DISABLED, + BTA_SYS_HW_ERROR +}; + +#define BTA_SYS_NUM_ACTIONS (BTA_SYS_MAX_EVT & 0x00ff) +#define BTA_SYS_IGNORE BTA_SYS_NUM_ACTIONS + +/* state table information */ +#define BTA_SYS_ACTIONS 2 /* number of actions */ +#define BTA_SYS_NEXT_STATE 2 /* position of next state */ +#define BTA_SYS_NUM_COLS 3 /* number of columns in state tables */ + + +/* state table for OFF state */ +const UINT8 bta_sys_hw_off[][BTA_SYS_NUM_COLS] = { + /* Event Action 1 Action 2 Next State */ + /* API_ENABLE */ {BTA_SYS_HW_API_ENABLE, BTA_SYS_IGNORE, BTA_SYS_HW_STARTING}, + /* EVT_ENABLED */ {BTA_SYS_IGNORE, BTA_SYS_IGNORE, BTA_SYS_HW_STARTING}, + /* STACK_ENABLED */ {BTA_SYS_IGNORE, BTA_SYS_IGNORE, BTA_SYS_HW_ON}, + /* API_DISABLE */ {BTA_SYS_HW_EVT_DISABLED, BTA_SYS_IGNORE, BTA_SYS_HW_OFF}, + /* EVT_DISABLED */ {BTA_SYS_IGNORE, BTA_SYS_IGNORE, BTA_SYS_HW_OFF}, + /* EVT_ERROR */ {BTA_SYS_IGNORE, BTA_SYS_IGNORE, BTA_SYS_HW_OFF} +}; + +const UINT8 bta_sys_hw_starting[][BTA_SYS_NUM_COLS] = { + /* Event Action 1 Action 2 Next State */ + /* API_ENABLE */ {BTA_SYS_IGNORE, BTA_SYS_IGNORE, BTA_SYS_HW_STARTING}, /* wait for completion event */ + /* EVT_ENABLED */ {BTA_SYS_HW_EVT_ENABLED, BTA_SYS_IGNORE, BTA_SYS_HW_STARTING}, + /* STACK_ENABLED */ {BTA_SYS_HW_EVT_STACK_ENABLED, BTA_SYS_IGNORE, BTA_SYS_HW_ON}, + /* API_DISABLE */ {BTA_SYS_IGNORE, BTA_SYS_IGNORE, BTA_SYS_HW_STOPPING}, /* successive disable/enable: change state wait for completion to disable */ + /* EVT_DISABLED */ {BTA_SYS_HW_EVT_DISABLED, BTA_SYS_HW_API_ENABLE, BTA_SYS_HW_STARTING}, /* successive enable/disable: notify, then restart HW */ + /* EVT_ERROR */ {BTA_SYS_HW_ERROR, BTA_SYS_IGNORE, BTA_SYS_HW_ON} +}; + +const UINT8 bta_sys_hw_on[][BTA_SYS_NUM_COLS] = { + /* Event Action 1 Action 2 Next State */ + /* API_ENABLE */ {BTA_SYS_HW_API_ENABLE, BTA_SYS_IGNORE, BTA_SYS_HW_ON}, + /* EVT_ENABLED */ {BTA_SYS_IGNORE, BTA_SYS_IGNORE, BTA_SYS_HW_ON}, + /* STACK_ENABLED */ {BTA_SYS_IGNORE, BTA_SYS_IGNORE, BTA_SYS_HW_ON}, + /* API_DISABLE */ {BTA_SYS_HW_API_DISABLE, BTA_SYS_IGNORE, BTA_SYS_HW_ON}, /* don't change the state here, as some other modules might be active */ + /* EVT_DISABLED */ {BTA_SYS_HW_ERROR, BTA_SYS_IGNORE, BTA_SYS_HW_ON}, + /* EVT_ERROR */ {BTA_SYS_HW_ERROR, BTA_SYS_IGNORE, BTA_SYS_HW_ON} +}; + +const UINT8 bta_sys_hw_stopping[][BTA_SYS_NUM_COLS] = { + /* Event Action 1 Action 2 Next State */ + /* API_ENABLE */ {BTA_SYS_IGNORE, BTA_SYS_IGNORE, BTA_SYS_HW_STARTING}, /* change state, and wait for completion event to enable */ + /* EVT_ENABLED */ {BTA_SYS_HW_EVT_ENABLED, BTA_SYS_IGNORE, BTA_SYS_HW_STOPPING}, /* successive enable/disable: finish the enable before disabling */ + /* STACK_ENABLED */ {BTA_SYS_HW_EVT_STACK_ENABLED, BTA_SYS_HW_API_DISABLE, BTA_SYS_HW_STOPPING}, /* successive enable/disable: notify, then stop */ + /* API_DISABLE */ {BTA_SYS_IGNORE, BTA_SYS_IGNORE, BTA_SYS_HW_STOPPING}, /* wait for completion event */ + /* EVT_DISABLED */ {BTA_SYS_HW_EVT_DISABLED, BTA_SYS_IGNORE, BTA_SYS_HW_OFF}, + /* EVT_ERROR */ {BTA_SYS_HW_API_DISABLE, BTA_SYS_IGNORE, BTA_SYS_HW_STOPPING} +}; + +typedef const UINT8 (*tBTA_SYS_ST_TBL)[BTA_SYS_NUM_COLS]; + +/* state table */ +const tBTA_SYS_ST_TBL bta_sys_st_tbl[] = { + bta_sys_hw_off, + bta_sys_hw_starting, + bta_sys_hw_on, + bta_sys_hw_stopping +}; + +/******************************************************************************* +** +** Function bta_sys_init +** +** Description BTA initialization; called from task initialization. +** +** +** Returns void +** +*******************************************************************************/ +void bta_sys_init(void) +{ + memset(&bta_sys_cb, 0, sizeof(tBTA_SYS_CB)); + + osi_mutex_new(&bta_alarm_lock); + + bta_alarm_hash_map = hash_map_new(BTA_ALARM_HASH_MAP_SIZE, + hash_function_pointer, NULL, (data_free_fn)osi_alarm_free, NULL); + + appl_trace_level = APPL_INITIAL_TRACE_LEVEL; + + /* register BTA SYS message handler */ + bta_sys_register( BTA_ID_SYS, &bta_sys_hw_reg); + + /* register for BTM notifications */ + BTM_RegisterForDeviceStatusNotif ((tBTM_DEV_STATUS_CB *)&bta_sys_hw_btm_cback ); + +#if( defined BTA_AR_INCLUDED ) && (BTA_AR_INCLUDED == TRUE) + bta_ar_init(); +#endif + +} + +void bta_sys_free(void) +{ + hash_map_free(bta_alarm_hash_map); + osi_mutex_free(&bta_alarm_lock); +#if BTA_DYNAMIC_MEMORY + FREE_AND_RESET(bta_sys_cb_ptr); +#endif +} + +/******************************************************************************* +** +** Function bta_sys_sm_execute +** +** Description State machine event handling function for DM +** +** +** Returns void +** +*******************************************************************************/ +BOOLEAN bta_sys_sm_execute(BT_HDR *p_msg) +{ + BOOLEAN freebuf = TRUE; + tBTA_SYS_ST_TBL state_table; + UINT8 action; + int i; + + APPL_TRACE_EVENT("bta_sys_sm_execute state:%d, event:0x%x\n", bta_sys_cb.state, p_msg->event); + + /* look up the state table for the current state */ + state_table = bta_sys_st_tbl[bta_sys_cb.state]; + /* update state */ + bta_sys_cb.state = state_table[p_msg->event & 0x00ff][BTA_SYS_NEXT_STATE]; + + /* execute action functions */ + for (i = 0; i < BTA_SYS_ACTIONS; i++) { + if ((action = state_table[p_msg->event & 0x00ff][i]) != BTA_SYS_IGNORE) { + (*bta_sys_action[action])( (tBTA_SYS_HW_MSG *) p_msg); + } else { + break; + } + } + return freebuf; + +} + + +void bta_sys_hw_register( tBTA_SYS_HW_MODULE module, tBTA_SYS_HW_CBACK *cback) +{ + bta_sys_cb.sys_hw_cback[module] = cback; +} + + +void bta_sys_hw_unregister( tBTA_SYS_HW_MODULE module ) +{ + bta_sys_cb.sys_hw_cback[module] = NULL; +} + +/******************************************************************************* +** +** Function bta_sys_hw_btm_cback +** +** Description This function is registered by BTA SYS to BTM in order to get status notifications +** +** +** Returns +** +*******************************************************************************/ +void bta_sys_hw_btm_cback( tBTM_DEV_STATUS status ) +{ + + tBTA_SYS_HW_MSG *sys_event; + + APPL_TRACE_DEBUG(" bta_sys_hw_btm_cback was called with parameter: %i" , status ); + + /* send a message to BTA SYS */ + if ((sys_event = (tBTA_SYS_HW_MSG *) osi_malloc(sizeof(tBTA_SYS_HW_MSG))) != NULL) { + if (status == BTM_DEV_STATUS_UP) { + sys_event->hdr.event = BTA_SYS_EVT_STACK_ENABLED_EVT; + } else if (status == BTM_DEV_STATUS_DOWN) { + sys_event->hdr.event = BTA_SYS_ERROR_EVT; + } else { + /* BTM_DEV_STATUS_CMD_TOUT is ignored for now. */ + osi_free (sys_event); + sys_event = NULL; + } + + if (sys_event) { + bta_sys_sendmsg(sys_event); + } + } else { + APPL_TRACE_DEBUG("ERROR bta_sys_hw_btm_cback couldn't send msg" ); + } +} + + + +/******************************************************************************* +** +** Function bta_sys_hw_error +** +** Description In case the HW device stops answering... Try to turn it off, then re-enable all +** previously active SW modules. +** +** Returns success or failure +** +*******************************************************************************/ +void bta_sys_hw_error(tBTA_SYS_HW_MSG *p_sys_hw_msg) +{ + UINT8 module_index; + UNUSED(p_sys_hw_msg); + + APPL_TRACE_DEBUG("%s\n", __FUNCTION__); + + for (module_index = 0; module_index < BTA_SYS_MAX_HW_MODULES; module_index++) { + if ( bta_sys_cb.sys_hw_module_active & ((UINT32)1 << module_index )) { + switch ( module_index) { + case BTA_SYS_HW_BLUETOOTH: + /* Send BTA_SYS_HW_ERROR_EVT to DM */ + if (bta_sys_cb.sys_hw_cback[module_index] != NULL) { + bta_sys_cb.sys_hw_cback[module_index] (BTA_SYS_HW_ERROR_EVT); + } + break; + default: + /* not yet supported */ + break; + } + } + } +} + + + +/******************************************************************************* +** +** Function bta_sys_hw_enable +** +** Description this function is called after API enable and HW has been turned on +** +** +** Returns success or failure +** +*******************************************************************************/ + +void bta_sys_hw_api_enable( tBTA_SYS_HW_MSG *p_sys_hw_msg ) +{ + if ((!bta_sys_cb.sys_hw_module_active) && (bta_sys_cb.state != BTA_SYS_HW_ON)) { + /* register which HW module was turned on */ + bta_sys_cb.sys_hw_module_active |= ((UINT32)1 << p_sys_hw_msg->hw_module ); + + tBTA_SYS_HW_MSG *p_msg; + if ((p_msg = (tBTA_SYS_HW_MSG *) osi_malloc(sizeof(tBTA_SYS_HW_MSG))) != NULL) { + p_msg->hdr.event = BTA_SYS_EVT_ENABLED_EVT; + p_msg->hw_module = p_sys_hw_msg->hw_module; + + bta_sys_sendmsg(p_msg); + } + } else { + /* register which HW module was turned on */ + bta_sys_cb.sys_hw_module_active |= ((UINT32)1 << p_sys_hw_msg->hw_module ); + + /* HW already in use, so directly notify the caller */ + if (bta_sys_cb.sys_hw_cback[p_sys_hw_msg->hw_module ] != NULL ) { + bta_sys_cb.sys_hw_cback[p_sys_hw_msg->hw_module ]( BTA_SYS_HW_ON_EVT ); + } + } + + APPL_TRACE_EVENT ("bta_sys_hw_api_enable for %d, active modules 0x%04X\n", + p_sys_hw_msg->hw_module, bta_sys_cb.sys_hw_module_active); + +} + +/******************************************************************************* +** +** Function bta_sys_hw_disable +** +** Description if no other module is using the HW, this function will call ( if defined ) a user-macro to turn off the HW +** +** +** Returns success or failure +** +*******************************************************************************/ +void bta_sys_hw_api_disable(tBTA_SYS_HW_MSG *p_sys_hw_msg) +{ + APPL_TRACE_DEBUG("bta_sys_hw_api_disable for %d, active modules: 0x%04X\n", + p_sys_hw_msg->hw_module, bta_sys_cb.sys_hw_module_active ); + + /* make sure the related SW blocks were stopped */ + bta_sys_disable( p_sys_hw_msg->hw_module ); + + + /* register which module we turn off */ + bta_sys_cb.sys_hw_module_active &= ~((UINT32)1 << p_sys_hw_msg->hw_module ); + + + /* if there are still some SW modules using the HW, just provide an answer to the calling */ + if ( bta_sys_cb.sys_hw_module_active != 0 ) { + /* if there are still some SW modules using the HW, directly notify the caller */ + if ( bta_sys_cb.sys_hw_cback[p_sys_hw_msg->hw_module ] != NULL ) { + bta_sys_cb.sys_hw_cback[p_sys_hw_msg->hw_module ]( BTA_SYS_HW_OFF_EVT ); + } + } else { + /* manually update the state of our system */ + bta_sys_cb.state = BTA_SYS_HW_STOPPING; + + tBTA_SYS_HW_MSG *p_msg; + if ((p_msg = (tBTA_SYS_HW_MSG *) osi_malloc(sizeof(tBTA_SYS_HW_MSG))) != NULL) { + p_msg->hdr.event = BTA_SYS_EVT_DISABLED_EVT; + p_msg->hw_module = p_sys_hw_msg->hw_module; + + bta_sys_sendmsg(p_msg); + } + } + +} + + +/******************************************************************************* +** +** Function bta_sys_hw_event_enabled +** +** Description +** +** +** Returns success or failure +** +*******************************************************************************/ +void bta_sys_hw_evt_enabled(tBTA_SYS_HW_MSG *p_sys_hw_msg) +{ + APPL_TRACE_EVENT("bta_sys_hw_evt_enabled for %i\n", p_sys_hw_msg->hw_module); + BTM_DeviceReset( NULL ); +} + + +/******************************************************************************* +** +** Function bta_sys_hw_event_disabled +** +** Description +** +** +** Returns success or failure +** +*******************************************************************************/ +void bta_sys_hw_evt_disabled(tBTA_SYS_HW_MSG *p_sys_hw_msg) +{ + UINT8 hw_module_index; + + APPL_TRACE_DEBUG("bta_sys_hw_evt_disabled - module 0x%X\n", p_sys_hw_msg->hw_module); + + for (hw_module_index = 0; hw_module_index < BTA_SYS_MAX_HW_MODULES; hw_module_index++) { + if (bta_sys_cb.sys_hw_cback[hw_module_index] != NULL) { + bta_sys_cb.sys_hw_cback[hw_module_index] (BTA_SYS_HW_OFF_EVT); + } + } +} + +/******************************************************************************* +** +** Function bta_sys_hw_event_stack_enabled +** +** Description we receive this event once the SW side is ready ( stack, FW download,... ), +** i.e. we can really start using the device. So notify the app. +** +** Returns success or failure +** +*******************************************************************************/ +void bta_sys_hw_evt_stack_enabled(tBTA_SYS_HW_MSG *p_sys_hw_msg) +{ + UINT8 hw_module_index; + UNUSED(p_sys_hw_msg); + + APPL_TRACE_DEBUG(" bta_sys_hw_evt_stack_enabled!notify the callers\n"); + + for (hw_module_index = 0; hw_module_index < BTA_SYS_MAX_HW_MODULES; hw_module_index++ ) { + if (bta_sys_cb.sys_hw_cback[hw_module_index] != NULL) { + bta_sys_cb.sys_hw_cback[hw_module_index] (BTA_SYS_HW_ON_EVT); + } + } +} + + + + +/******************************************************************************* +** +** Function bta_sys_event +** +** Description BTA event handler; called from task event handler. +** +** +** Returns void +** +*******************************************************************************/ +void bta_sys_event(void * param) +{ + BT_HDR *p_msg = (BT_HDR *)param; + + UINT8 id; + BOOLEAN freebuf = TRUE; + + APPL_TRACE_EVENT("BTA got event 0x%x\n", p_msg->event); + + /* get subsystem id from event */ + id = (UINT8) (p_msg->event >> 8); + + /* verify id and call subsystem event handler */ + if ((id < BTA_ID_MAX) && (bta_sys_cb.reg[id] != NULL)) { + freebuf = (*bta_sys_cb.reg[id]->evt_hdlr)(p_msg); + } else { + APPL_TRACE_WARNING("BTA got unregistered event id %d\n", id); + } + + if (freebuf) { + osi_free(p_msg); + } +} + +/******************************************************************************* +** +** Function bta_sys_register +** +** Description Called by other BTA subsystems to register their event +** handler. +** +** +** Returns void +** +*******************************************************************************/ +void bta_sys_register(UINT8 id, const tBTA_SYS_REG *p_reg) +{ + bta_sys_cb.reg[id] = (tBTA_SYS_REG *) p_reg; + bta_sys_cb.is_reg[id] = TRUE; +} + +/******************************************************************************* +** +** Function bta_sys_deregister +** +** Description Called by other BTA subsystems to de-register +** handler. +** +** +** Returns void +** +*******************************************************************************/ +void bta_sys_deregister(UINT8 id) +{ + bta_sys_cb.is_reg[id] = FALSE; +} + +/******************************************************************************* +** +** Function bta_sys_is_register +** +** Description Called by other BTA subsystems to get registeration +** status. +** +** +** Returns void +** +*******************************************************************************/ +BOOLEAN bta_sys_is_register(UINT8 id) +{ + return bta_sys_cb.is_reg[id]; +} + +/******************************************************************************* +** +** Function bta_sys_sendmsg +** +** Description Send a message to BTA. This function is designed to +** optimize sending of messages to BTA. It is called by BTA +** API functions and call-in functions. +** +** +** Returns void +** +*******************************************************************************/ +void bta_sys_sendmsg(void *p_msg) +{ + // There is a race condition that occurs if the stack is shut down while + // there is a procedure in progress that can schedule a task via this + // message queue. This causes |btu_bta_msg_queue| to get cleaned up before + // it gets used here; hence we check for NULL before using it. + if (btu_task_post(SIG_BTU_BTA_MSG, p_msg, OSI_THREAD_MAX_TIMEOUT) == false) { + osi_free(p_msg); + } +} + +/******************************************************************************* +** +** Function bta_sys_start_timer +** +** Description Start a protocol timer for the specified amount +** of time in milliseconds. +** +** Returns void +** +*******************************************************************************/ +void bta_alarm_cb(void *data) +{ + assert(data != NULL); + TIMER_LIST_ENT *p_tle = (TIMER_LIST_ENT *)data; + + btu_task_post(SIG_BTU_BTA_ALARM, p_tle, OSI_THREAD_MAX_TIMEOUT); +} + +void bta_sys_start_timer(TIMER_LIST_ENT *p_tle, UINT16 type, INT32 timeout_ms) +{ + assert(p_tle != NULL); + + // Get the alarm for this p_tle. + osi_mutex_lock(&bta_alarm_lock, OSI_MUTEX_MAX_TIMEOUT); + if (!hash_map_has_key(bta_alarm_hash_map, p_tle)) { + hash_map_set(bta_alarm_hash_map, p_tle, osi_alarm_new("bta_sys", bta_alarm_cb, p_tle, 0)); + } + osi_mutex_unlock(&bta_alarm_lock); + + osi_alarm_t *alarm = hash_map_get(bta_alarm_hash_map, p_tle); + if (alarm == NULL) { + APPL_TRACE_ERROR("%s unable to create alarm.", __func__); + return; + } + + p_tle->event = type; + p_tle->ticks = timeout_ms; + //osi_alarm_set(alarm, (period_ms_t)timeout_ms, bta_alarm_cb, p_tle); + osi_alarm_set(alarm, (period_ms_t)timeout_ms); +} + +bool hash_iter_ro_cb(hash_map_entry_t *hash_map_entry, void *context) +{ + osi_alarm_t *alarm = (osi_alarm_t *)hash_map_entry->data; + period_ms_t *p_remaining_ms = (period_ms_t *)context; + *p_remaining_ms += osi_alarm_get_remaining_ms(alarm); + return true; +} + +UINT32 bta_sys_get_remaining_ticks(TIMER_LIST_ENT *p_target_tle) +{ + period_ms_t remaining_ms = 0; + osi_mutex_lock(&bta_alarm_lock, OSI_MUTEX_MAX_TIMEOUT); + // Get the alarm for this p_tle + hash_map_foreach(bta_alarm_hash_map, hash_iter_ro_cb, &remaining_ms); + osi_mutex_unlock(&bta_alarm_lock); + return remaining_ms; +} + + +/******************************************************************************* +** +** Function bta_sys_timer_is_active +** +** Description Get info of timer is active or not. +** +** Returns true if timer is exist and active, false otherwise. +** +*******************************************************************************/ +BOOLEAN bta_sys_timer_is_active(TIMER_LIST_ENT *p_tle) +{ + assert(p_tle != NULL); + + osi_alarm_t *alarm = hash_map_get(bta_alarm_hash_map, p_tle); + if (alarm != NULL && osi_alarm_is_active(alarm)) { + return TRUE; + } + + return FALSE; +} + +/******************************************************************************* +** +** Function bta_sys_stop_timer +** +** Description Stop a BTA timer. +** +** Returns void +** +*******************************************************************************/ +void bta_sys_stop_timer(TIMER_LIST_ENT *p_tle) +{ + assert(p_tle != NULL); + + osi_alarm_t *alarm = hash_map_get(bta_alarm_hash_map, p_tle); + if (alarm == NULL) { + APPL_TRACE_DEBUG("%s expected alarm was not in bta alarm hash map.", __func__); + return; + } + osi_alarm_cancel(alarm); +} + +/******************************************************************************* +** +** Function bta_sys_free_timer +** +** Description Stop and free a BTA timer. +** +** Returns void +** +*******************************************************************************/ +void bta_sys_free_timer(TIMER_LIST_ENT *p_tle) +{ + assert(p_tle != NULL); + + osi_alarm_t *alarm = hash_map_get(bta_alarm_hash_map, p_tle); + if (alarm == NULL) { + APPL_TRACE_DEBUG("%s expected alarm was not in bta alarm hash map.", __func__); + return; + } + osi_alarm_cancel(alarm); + hash_map_erase(bta_alarm_hash_map, p_tle); +} + +/******************************************************************************* +** +** Function bta_sys_disable +** +** Description For each registered subsystem execute its disable function. +** +** Returns void +** +*******************************************************************************/ +void bta_sys_disable(tBTA_SYS_HW_MODULE module) +{ + int bta_id = 0; + int bta_id_max = 0; + + APPL_TRACE_DEBUG("bta_sys_disable: module %i", module); + + switch ( module ) { + case BTA_SYS_HW_BLUETOOTH: + bta_id = BTA_ID_DM; + bta_id_max = BTA_ID_BLUETOOTH_MAX; + break; + default: + APPL_TRACE_WARNING("bta_sys_disable: unkown module"); + return; + } + + for ( ; bta_id <= bta_id_max; bta_id++) { + if (bta_sys_cb.reg[bta_id] != NULL) { + if (bta_sys_cb.is_reg[bta_id] == TRUE && bta_sys_cb.reg[bta_id]->disable != NULL) { + (*bta_sys_cb.reg[bta_id]->disable)(); + } + } + } +} + +/******************************************************************************* +** +** Function bta_sys_set_trace_level +** +** Description Set trace level for BTA +** +** Returns void +** +*******************************************************************************/ +void bta_sys_set_trace_level(UINT8 level) +{ + appl_trace_level = level; +} + +/******************************************************************************* +** +** Function bta_sys_get_sys_features +** +** Description Returns sys_features to other BTA modules. +** +** Returns sys_features +** +*******************************************************************************/ +UINT16 bta_sys_get_sys_features (void) +{ + return bta_sys_cb.sys_features; +} diff --git a/lib/bt/host/bluedroid/bta/sys/include/bta_sys_int.h b/lib/bt/host/bluedroid/bta/sys/include/bta_sys_int.h new file mode 100644 index 00000000..aa2596d9 --- /dev/null +++ b/lib/bt/host/bluedroid/bta/sys/include/bta_sys_int.h @@ -0,0 +1,101 @@ +/****************************************************************************** + * + * Copyright (C) 2003-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This is the private interface file for the BTA system manager. + * + ******************************************************************************/ +#ifndef BTA_SYS_INT_H +#define BTA_SYS_INT_H + +/***************************************************************************** +** Constants and data types +*****************************************************************************/ + +/***************************************************************************** +** state table +*****************************************************************************/ + +/* SYS HW state */ +enum { + BTA_SYS_HW_OFF, + BTA_SYS_HW_STARTING, + BTA_SYS_HW_ON, + BTA_SYS_HW_STOPPING +}; +typedef UINT8 tBTA_SYS_HW_STATE; + +/* Collision callback */ +#define MAX_COLLISION_REG 5 + +typedef struct { + UINT8 id[MAX_COLLISION_REG]; + tBTA_SYS_CONN_CBACK *p_coll_cback[MAX_COLLISION_REG]; +} tBTA_SYS_COLLISION; + +/* system manager control block */ +typedef struct { + tBTA_SYS_REG *reg[BTA_ID_MAX]; /* registration structures */ + BOOLEAN is_reg[BTA_ID_MAX]; /* registration structures */ + tBTA_SYS_HW_STATE state; + tBTA_SYS_HW_CBACK *sys_hw_cback[BTA_SYS_MAX_HW_MODULES]; /* enable callback for each HW modules */ + UINT32 sys_hw_module_active; /* bitmask of all active modules */ + UINT16 sys_features; /* Bitmask of sys features */ + + tBTA_SYS_CONN_CBACK *prm_cb; /* role management callback registered by DM */ + tBTA_SYS_CONN_CBACK *ppm_cb; /* low power management callback registered by DM */ + tBTA_SYS_CONN_CBACK *p_policy_cb; /* link policy change callback registered by DM */ + tBTA_SYS_CONN_CBACK *p_sco_cb; /* SCO connection change callback registered by AV */ + tBTA_SYS_CONN_CBACK *p_role_cb; /* role change callback registered by AV */ + tBTA_SYS_COLLISION colli_reg; /* collision handling module */ +#if (BTA_EIR_CANNED_UUID_LIST != TRUE) + tBTA_SYS_EIR_CBACK *eir_cb; /* add/remove UUID into EIR */ +#endif +#if (BTM_SSR_INCLUDED == TRUE) + tBTA_SYS_SSR_CFG_CBACK *p_ssr_cb; +#endif + /* VS event handler */ + tBTA_SYS_VS_EVT_HDLR *p_vs_evt_hdlr; + +} tBTA_SYS_CB; + +/***************************************************************************** +** Global variables +*****************************************************************************/ + +/* system manager control block */ +#if BTA_DYNAMIC_MEMORY == FALSE +extern tBTA_SYS_CB bta_sys_cb; +#else +extern tBTA_SYS_CB *bta_sys_cb_ptr; +#define bta_sys_cb (*bta_sys_cb_ptr) +#endif + +/* functions used for BTA SYS HW state machine */ +void bta_sys_hw_btm_cback( tBTM_DEV_STATUS status ); +void bta_sys_hw_error(tBTA_SYS_HW_MSG *p_sys_hw_msg); +void bta_sys_hw_api_enable( tBTA_SYS_HW_MSG *p_sys_hw_msg ); +void bta_sys_hw_api_disable(tBTA_SYS_HW_MSG *p_sys_hw_msg); +void bta_sys_hw_evt_enabled(tBTA_SYS_HW_MSG *p_sys_hw_msg); +void bta_sys_hw_evt_disabled(tBTA_SYS_HW_MSG *p_sys_hw_msg); +void bta_sys_hw_evt_stack_enabled(tBTA_SYS_HW_MSG *p_sys_hw_msg); + +BOOLEAN bta_sys_sm_execute(BT_HDR *p_msg); + +#endif /* BTA_SYS_INT_H */ diff --git a/lib/bt/host/bluedroid/bta/sys/utl.c b/lib/bt/host/bluedroid/bta/sys/utl.c new file mode 100644 index 00000000..c18567cd --- /dev/null +++ b/lib/bt/host/bluedroid/bta/sys/utl.c @@ -0,0 +1,319 @@ +/****************************************************************************** + * + * Copyright (C) 2003-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains utility functions. + * + ******************************************************************************/ +#include +#include "bta/utl.h" +#include "stack/btm_api.h" +#include "osi/allocator.h" + +/******************************************************************************* +** +** Function utl_str2int +** +** Description This utility function converts a character string to an +** integer. Acceptable values in string are 0-9. If invalid +** string or string value too large, -1 is returned. Leading +** spaces are skipped. +** +** +** Returns Integer value or -1 on error. +** +*******************************************************************************/ +INT16 utl_str2int(const char *p_s) +{ + INT32 val = 0; + + for (; *p_s == ' ' && *p_s != 0; p_s++); + + if (*p_s == 0) { + return -1; + } + + for (;;) { + if ((*p_s < '0') || (*p_s > '9')) { + return -1; + } + + val += (INT32) (*p_s++ - '0'); + + if (val > 32767) { + return -1; + } + + if (*p_s == 0) { + return (INT16) val; + } else { + val *= 10; + } + } +} + +/******************************************************************************* +** +** Function utl_strucmp +** +** Description This utility function compares two strings in uppercase. +** String p_s must be uppercase. String p_t is converted to +** uppercase if lowercase. If p_s ends first, the substring +** match is counted as a match. +** +** +** Returns 0 if strings match, nonzero otherwise. +** +*******************************************************************************/ +int utl_strucmp(const char *p_s, const char *p_t) +{ + char c; + + while (*p_s && *p_t) { + c = *p_t++; + if (c >= 'a' && c <= 'z') { + c -= 0x20; + } + if (*p_s++ != c) { + return -1; + } + } + /* if p_t hit null first, no match */ + if (*p_t == 0 && *p_s != 0) { + return 1; + } + /* else p_s hit null first, count as match */ + else { + return 0; + } +} + +/******************************************************************************* +** +** Function utl_itoa +** +** Description This utility function converts a UINT16 to a string. The +** string is NULL-terminated. The length of the string is +** returned; +** +** +** Returns Length of string. +** +*******************************************************************************/ +UINT8 utl_itoa(UINT16 i, char *p_s) +{ + UINT16 j, k; + char *p = p_s; + BOOLEAN fill = FALSE; + + if (i == 0) { + /* take care of zero case */ + *p++ = '0'; + } else { + for (j = 10000; j > 0; j /= 10) { + k = i / j; + i %= j; + if (k > 0 || fill) { + *p++ = k + '0'; + fill = TRUE; + } + } + } + *p = 0; + return (UINT8) (p - p_s); +} + +/******************************************************************************* +** +** Function utl_freebuf +** +** Description This function calls osi_free to free the buffer passed +** in, if buffer pointer is not NULL, and also initializes +** buffer pointer to NULL. +** +** +** Returns Nothing. +** +*******************************************************************************/ +void utl_freebuf(void **p) +{ + if (*p != NULL) { + osi_free(*p); + *p = NULL; + } +} + + +/******************************************************************************* +** +** Function utl_set_device_class +** +** Description This function updates the local Device Class. +** +** Parameters: +** p_cod - Pointer to the device class to set to +** +** cmd - the fields of the device class to update. +** BTA_UTL_SET_COD_MAJOR_MINOR, - overwrite major, minor class +** BTA_UTL_SET_COD_SERVICE_CLASS - set the bits in the input +** BTA_UTL_CLR_COD_SERVICE_CLASS - clear the bits in the input +** BTA_UTL_SET_COD_ALL - overwrite major, minor, set the bits in service class +** BTA_UTL_INIT_COD - overwrite major, minor, and service class +** +** Returns TRUE if successful, Otherwise FALSE +** +*******************************************************************************/ +BOOLEAN utl_set_device_class(tBTA_UTL_COD *p_cod, UINT8 cmd) +{ + UINT8 *dev; + UINT16 service; + UINT8 minor, major; + DEV_CLASS dev_class; + + dev = BTM_ReadDeviceClass(); + BTM_COD_SERVICE_CLASS( service, dev ); + BTM_COD_MINOR_CLASS(minor, dev ); + BTM_COD_MAJOR_CLASS(major, dev ); + + switch (cmd) { + case BTA_UTL_SET_COD_MAJOR_MINOR: + minor = p_cod->minor & BTM_COD_MINOR_CLASS_MASK; + major = p_cod->major & BTM_COD_MAJOR_CLASS_MASK; + break; + + case BTA_UTL_SET_COD_SERVICE_CLASS: + /* clear out the bits that is not SERVICE_CLASS bits */ + p_cod->service &= BTM_COD_SERVICE_CLASS_MASK; + service = service | p_cod->service; + break; + + case BTA_UTL_CLR_COD_SERVICE_CLASS: + p_cod->service &= BTM_COD_SERVICE_CLASS_MASK; + service = service & (~p_cod->service); + break; + + case BTA_UTL_SET_COD_ALL: + minor = p_cod->minor & BTM_COD_MINOR_CLASS_MASK; + major = p_cod->major & BTM_COD_MAJOR_CLASS_MASK; + p_cod->service &= BTM_COD_SERVICE_CLASS_MASK; + service = service | p_cod->service; + break; + + case BTA_UTL_INIT_COD: + minor = p_cod->minor & BTM_COD_MINOR_CLASS_MASK; + major = p_cod->major & BTM_COD_MAJOR_CLASS_MASK; + service = p_cod->service & BTM_COD_SERVICE_CLASS_MASK; + break; + + default: + return FALSE; + } + + /* convert the fields into the device class type */ + FIELDS_TO_COD(dev_class, minor, major, service); + + if (BTM_SetDeviceClass(dev_class) == BTM_SUCCESS) { + return TRUE; + } + + return FALSE; +} + +/******************************************************************************* +** +** Function utl_get_device_class +** +** Description This function get the local Device Class. +** +** Parameters: +** p_cod - Pointer to the device class to get to +** +** +** Returns TRUE if successful, Otherwise FALSE +** +*******************************************************************************/ +BOOLEAN utl_get_device_class(tBTA_UTL_COD *p_cod) +{ + UINT8 *dev; + UINT16 service; + UINT8 minor, major; + + dev = BTM_ReadDeviceClass(); + BTM_COD_SERVICE_CLASS( service, dev ); + BTM_COD_MINOR_CLASS(minor, dev ); + BTM_COD_MAJOR_CLASS(major, dev ); + + p_cod->minor = minor; + p_cod->major = major; + p_cod->service = service; + + return TRUE; +} + +/******************************************************************************* +** +** Function utl_isintstr +** +** Description This utility function checks if the given string is an +** integer string or not +** +** +** Returns TRUE if successful, Otherwise FALSE +** +*******************************************************************************/ +BOOLEAN utl_isintstr(const char *p_s) +{ + UINT16 i = 0; + + for (i = 0; p_s[i] != 0; i++) { + if (((p_s[i] < '0') || (p_s[i] > '9')) && (p_s[i] != ';')) { + return FALSE; + } + } + + return TRUE; +} + +/******************************************************************************* +** +** Function utl_isdialstr +** +** Description This utility function checks if the given string contains +** only dial digits or not +** +** +** Returns TRUE if successful, Otherwise FALSE +** +*******************************************************************************/ +BOOLEAN utl_isdialstr(const char *p_s) +{ + UINT16 i = 0; + + for (i = 0; p_s[i] != 0; i++) { + if (!(((p_s[i] >= '0') && (p_s[i] <= '9')) + || (p_s[i] == '*') || (p_s[i] == '+') || (p_s[i] == '#') || (p_s[i] == ';') + || ((p_s[i] >= 'A') && (p_s[i] <= 'C')) + || ((p_s[i] == 'p') || (p_s[i] == 'P') + || (p_s[i] == 'w') || (p_s[i] == 'W')))) { + return FALSE; + } + } + + return TRUE; +} diff --git a/lib/bt/host/bluedroid/btc/core/btc_ble_storage.c b/lib/bt/host/bluedroid/btc/core/btc_ble_storage.c new file mode 100644 index 00000000..7df1bfe0 --- /dev/null +++ b/lib/bt/host/bluedroid/btc/core/btc_ble_storage.c @@ -0,0 +1,1101 @@ +/* + * SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include "bta/bta_api.h" +#include "btc/btc_config.h" +#include "device/bdaddr.h" +#include "btc/btc_ble_storage.h" +#include "bta/bta_gatts_co.h" +#include "btc/btc_util.h" + +#if (SMP_INCLUDED == TRUE) + +//the maximum nubmer of bonded devices +#define BONED_DEVICES_MAX_COUNT (BTM_SEC_MAX_DEVICE_RECORDS) + +static void _btc_storage_save(void) +{ + uint16_t addr_section_count = 0; + bt_bdaddr_t bd_addr; + + const btc_config_section_iter_t *need_remove_iter = NULL; + const btc_config_section_iter_t *iter = btc_config_section_begin(); + + while (iter != btc_config_section_end()) { + //store the next iter, if remove section, then will not loss the point + + const char *section = btc_config_section_name(iter); + + if (string_is_bdaddr(section) && + !btc_config_exist(section, BTC_BLE_STORAGE_DEV_TYPE_STR) && + !btc_config_exist(section, BTC_BLE_STORAGE_ADDR_TYPE_STR) && + !btc_config_exist(section, BTC_BLE_STORAGE_LINK_KEY_STR) && + !btc_config_exist(section, BTC_BLE_STORAGE_LE_KEY_PENC_STR) && + !btc_config_exist(section, BTC_BLE_STORAGE_LE_KEY_PID_STR) && + !btc_config_exist(section, BTC_BLE_STORAGE_LE_KEY_PCSRK_STR) && + !btc_config_exist(section, BTC_BLE_STORAGE_LE_KEY_LENC_STR) && + !btc_config_exist(section, BTC_BLE_STORAGE_LE_KEY_LCSRK_STR) && + !btc_config_exist(section, BTC_BLE_STORAGE_GATT_CL_SUPP_FEAT_STR) && + !btc_config_exist(section, BTC_BLE_STORAGE_GATT_DB_HASH_STR)) { + iter = btc_config_section_next(iter); + btc_config_remove_section(section); + continue; + } + + if (!string_is_bdaddr(section)) { + iter = btc_config_section_next(iter); + continue; + } + + if(addr_section_count == BONED_DEVICES_MAX_COUNT) { + need_remove_iter = iter; + } + addr_section_count ++; + iter = btc_config_section_next(iter); + } + /*exceeded the maximum nubmer of bonded devices, delete them */ + if (need_remove_iter) { + while(need_remove_iter != btc_config_section_end()) { + const char *need_remove_section = btc_config_section_name(need_remove_iter); + if (!string_is_bdaddr(need_remove_section)) { + need_remove_iter = btc_config_section_next(need_remove_iter); + continue; + } + need_remove_iter = btc_config_section_next(need_remove_iter); + //delete device info + string_to_bdaddr(need_remove_section, &bd_addr); + BTA_DmRemoveDevice(bd_addr.address, BT_TRANSPORT_LE); + BTA_DmRemoveDevice(bd_addr.address, BT_TRANSPORT_BR_EDR); + //delete config info + if (btc_config_remove_section(need_remove_section)) { + // The need_remove_section has been freed + BTIF_TRACE_WARNING("Exceeded the maximum number of bonded devices. Deleting the last device info: %02x:%02x:%02x:%02x:%02x:%02x", + bd_addr.address[0], bd_addr.address[1], bd_addr.address[2], bd_addr.address[3], bd_addr.address[4], bd_addr.address[5]); + } + } + } + btc_config_flush(); +} + +void btc_storage_save(void) +{ + btc_config_lock(); + _btc_storage_save(); + btc_config_unlock(); +} + +#if (BLE_INCLUDED == TRUE) +static bt_status_t _btc_storage_add_ble_bonding_key(bt_bdaddr_t *remote_bd_addr, + char *key, + uint8_t key_type, + uint8_t key_length) +{ + bdstr_t bdstr; + bdaddr_to_string(remote_bd_addr, bdstr, sizeof(bdstr)); + const char* name; + + switch (key_type) { + case BTM_LE_KEY_PENC: + name = BTC_BLE_STORAGE_LE_KEY_PENC_STR; + break; + case BTM_LE_KEY_PID: + name = BTC_BLE_STORAGE_LE_KEY_PID_STR; + break; + case BTM_LE_KEY_PCSRK: + name = BTC_BLE_STORAGE_LE_KEY_PCSRK_STR; + break; + case BTM_LE_KEY_LENC: + name = BTC_BLE_STORAGE_LE_KEY_LENC_STR; + break; + case BTM_LE_KEY_LCSRK: + name = BTC_BLE_STORAGE_LE_KEY_LCSRK_STR; + break; + case BTM_LE_KEY_LID: + name = BTC_BLE_STORAGE_LE_KEY_LID_STR; + break; + default: + return BT_STATUS_FAIL; + } + + int ret = btc_config_set_bin(bdstr, name, (const uint8_t *)key, key_length); + _btc_storage_save(); + return ret ? BT_STATUS_SUCCESS : BT_STATUS_FAIL; +} + +bt_status_t btc_storage_add_ble_bonding_key(bt_bdaddr_t *remote_bd_addr, + char *key, + uint8_t key_type, + uint8_t key_length) +{ + bt_status_t ret; + + btc_config_lock(); + ret = _btc_storage_add_ble_bonding_key(remote_bd_addr, key, key_type, key_length); + btc_config_unlock(); + + return ret; +} + +/******************************************************************************* +** +** Function btc_storage_get_ble_bonding_key +** +** Description +** +** Returns BT_STATUS_SUCCESS if the fetch was successful, +** BT_STATUS_FAIL otherwise +** +*******************************************************************************/ +static bt_status_t _btc_storage_get_ble_bonding_key(bt_bdaddr_t *remote_bd_addr, + uint8_t key_type, + char *key_value, + int key_length) +{ + bdstr_t bdstr; + bdaddr_to_string(remote_bd_addr, bdstr, sizeof(bdstr)); + const char* name; + switch (key_type) { + case BTM_LE_KEY_PENC: + name = BTC_BLE_STORAGE_LE_KEY_PENC_STR; + break; + case BTM_LE_KEY_PID: + name = BTC_BLE_STORAGE_LE_KEY_PID_STR; + break; + case BTM_LE_KEY_PCSRK: + name = BTC_BLE_STORAGE_LE_KEY_PCSRK_STR; + break; + case BTM_LE_KEY_LENC: + name = BTC_BLE_STORAGE_LE_KEY_LENC_STR; + break; + case BTM_LE_KEY_LCSRK: + name = BTC_BLE_STORAGE_LE_KEY_LCSRK_STR; + break; + case BTM_LE_KEY_LID: + name = BTC_BLE_STORAGE_LE_KEY_LID_STR; + default: + return BT_STATUS_FAIL; + } + size_t length = key_length; + int ret = btc_config_get_bin(bdstr, name, (uint8_t *)key_value, &length); + return ret ? BT_STATUS_SUCCESS : BT_STATUS_FAIL; + +} + +bt_status_t btc_storage_get_ble_bonding_key(bt_bdaddr_t *remote_bd_addr, + uint8_t key_type, + char *key_value, + int key_length) +{ + bt_status_t ret; + + btc_config_lock(); + ret = _btc_storage_get_ble_bonding_key(remote_bd_addr, key_type, key_value, key_length); + btc_config_unlock(); + + return ret; +} + +static bt_status_t _btc_storage_remove_all_ble_keys(const char *name) +{ + int ret = 0; + + if (name == NULL) { + return BT_STATUS_FAIL; + } + + if (btc_config_exist(name, BTC_BLE_STORAGE_ADDR_TYPE_STR)) { + ret |= btc_config_remove(name, BTC_BLE_STORAGE_ADDR_TYPE_STR); + } + if (btc_config_exist(name, BTC_BLE_STORAGE_LE_KEY_PENC_STR)) { + ret |= btc_config_remove(name, BTC_BLE_STORAGE_LE_KEY_PENC_STR); + } + if (btc_config_exist(name, BTC_BLE_STORAGE_LE_KEY_PID_STR)) { + ret |= btc_config_remove(name, BTC_BLE_STORAGE_LE_KEY_PID_STR); + } + if (btc_config_exist(name, BTC_BLE_STORAGE_LE_KEY_PCSRK_STR)) { + ret |= btc_config_remove(name, BTC_BLE_STORAGE_LE_KEY_PCSRK_STR); + } + if (btc_config_exist(name, BTC_BLE_STORAGE_LE_KEY_LENC_STR)) { + ret |= btc_config_remove(name, BTC_BLE_STORAGE_LE_KEY_LENC_STR); + } + if (btc_config_exist(name, BTC_BLE_STORAGE_LE_KEY_LCSRK_STR)) { + ret |= btc_config_remove(name, BTC_BLE_STORAGE_LE_KEY_LCSRK_STR); + } + if (btc_config_exist(name, BTC_BLE_STORAGE_LE_KEY_LID_STR)) { + ret |= btc_config_remove(name, BTC_BLE_STORAGE_LE_KEY_LID_STR); + } + + return ret; +} + +void btc_storage_remove_unused_sections(uint8_t *cur_addr, tBTM_LE_PID_KEYS *del_pid_key) +{ + bt_bdaddr_t bd_addr; + uint32_t device_type = 0; + + if (del_pid_key == NULL) { + return; + } + + btc_config_lock(); + + const btc_config_section_iter_t *iter = btc_config_section_begin(); + + while (iter != btc_config_section_end()) { + //store the next iter, if remove section, then will not loss the point + + const char *section = btc_config_section_name(iter); + if (string_is_bdaddr(section) && + !btc_config_exist(section, BTC_BLE_STORAGE_DEV_TYPE_STR) && + !btc_config_exist(section, BTC_BLE_STORAGE_ADDR_TYPE_STR) && + !btc_config_exist(section, BTC_BLE_STORAGE_LINK_KEY_STR) && + !btc_config_exist(section, BTC_BLE_STORAGE_LE_KEY_PENC_STR) && + !btc_config_exist(section, BTC_BLE_STORAGE_LE_KEY_PID_STR) && + !btc_config_exist(section, BTC_BLE_STORAGE_LE_KEY_PCSRK_STR) && + !btc_config_exist(section, BTC_BLE_STORAGE_LE_KEY_LENC_STR) && + !btc_config_exist(section, BTC_BLE_STORAGE_LE_KEY_LCSRK_STR)) { + iter = btc_config_section_next(iter); + btc_config_remove_section(section); + continue; + } + + if (!string_is_bdaddr(section) || + !btc_config_get_int(section, BTC_BLE_STORAGE_DEV_TYPE_STR, (int *)&device_type) || + ((device_type & BT_DEVICE_TYPE_BLE) != BT_DEVICE_TYPE_BLE)) { + iter = btc_config_section_next(iter); + continue; + } + + string_to_bdaddr(section, &bd_addr); + + char buffer[sizeof(tBTM_LE_KEY_VALUE)] = {0}; + + if (_btc_storage_get_ble_bonding_key(&bd_addr, BTM_LE_KEY_PID, buffer, sizeof(tBTM_LE_PID_KEYS)) == BT_STATUS_SUCCESS) { + + tBTM_LE_PID_KEYS *pid_key = (tBTM_LE_PID_KEYS *) buffer; + + iter = btc_config_section_next(iter); + + if (memcmp(del_pid_key->static_addr, pid_key->static_addr, 6) == 0 && memcmp(cur_addr, bd_addr.address, 6) != 0 && del_pid_key->addr_type == pid_key->addr_type) { + if (device_type == BT_DEVICE_TYPE_DUMO) { + btc_config_set_int(section, BTC_BLE_STORAGE_DEV_TYPE_STR, BT_DEVICE_TYPE_BREDR); + _btc_storage_remove_all_ble_keys(section); + } else { + //delete unused sections + BTIF_TRACE_DEBUG("delete section %s\n", section); + btc_config_remove_section(section); + } + } + } else { + iter = btc_config_section_next(iter); + } + } + + btc_config_unlock(); + +} + +void btc_storage_delete_duplicate_ble_devices(void) +{ + bt_bdaddr_t bd_addr; + char buffer[sizeof(tBTM_LE_KEY_VALUE)] = {0}; + char temp_buffer[sizeof(tBTM_LE_KEY_VALUE)] = {0}; + tBTM_LE_PID_KEYS *pid_key; + tBTM_LE_PID_KEYS *temp_pid_key; + uint32_t device_type = 0; + + bt_bdaddr_t temp_bd_addr; + + btc_config_lock(); + for (const btc_config_section_iter_t *iter = btc_config_section_begin(); iter != btc_config_section_end(); + iter = btc_config_section_next(iter)) { + const char *name = btc_config_section_name(iter); + + if (!string_is_bdaddr(name) || + !btc_config_get_int(name, BTC_BLE_STORAGE_DEV_TYPE_STR, (int *)&device_type) || + ((device_type & BT_DEVICE_TYPE_BLE) != BT_DEVICE_TYPE_BLE)) { + continue; + } + + string_to_bdaddr(name, &bd_addr); + if (_btc_storage_get_ble_bonding_key(&bd_addr, BTM_LE_KEY_PID, buffer, sizeof(tBTM_LE_PID_KEYS)) == BT_STATUS_SUCCESS) + { + pid_key = (tBTM_LE_PID_KEYS *) buffer; + + const btc_config_section_iter_t *temp_iter = btc_config_section_next(iter); + while (temp_iter != NULL) + { + const char *temp_name = btc_config_section_name(temp_iter); + if (!string_is_bdaddr(temp_name) || !btc_config_get_int(temp_name, BTC_BLE_STORAGE_DEV_TYPE_STR, (int *)&device_type) || + ((device_type & BT_DEVICE_TYPE_BLE) != BT_DEVICE_TYPE_BLE)) { + temp_iter = btc_config_section_next(temp_iter); + continue; + } + string_to_bdaddr(temp_name, &temp_bd_addr); + if (_btc_storage_get_ble_bonding_key(&temp_bd_addr, BTM_LE_KEY_PID, temp_buffer, sizeof(tBTM_LE_PID_KEYS)) == BT_STATUS_SUCCESS) + { + temp_pid_key = (tBTM_LE_PID_KEYS *) temp_buffer; + if (memcmp(pid_key->static_addr, temp_pid_key->static_addr, 6) == 0 && pid_key->addr_type == temp_pid_key->addr_type) { + const char *temp_name = btc_config_section_name(temp_iter); + temp_iter = btc_config_section_next(temp_iter); + if (device_type == BT_DEVICE_TYPE_DUMO) { + btc_config_set_int(temp_name, BTC_BLE_STORAGE_DEV_TYPE_STR, BT_DEVICE_TYPE_BREDR); + _btc_storage_remove_all_ble_keys(temp_name); + } else { + BTC_TRACE_DEBUG("delete %s\n", temp_name); + btc_config_remove_section(temp_name); + } + } else { + temp_iter = btc_config_section_next(temp_iter); + } + } else { + temp_iter = btc_config_section_next(temp_iter); + } + } + + } + } + btc_config_unlock(); +} + + +/******************************************************************************* +** +** Function btc_storage_remove_ble_bonding_keys +** +** Description btc storage API - Deletes the bonded device from NVRAM +** +** Returns BT_STATUS_SUCCESS if the deletion was successful, +** BT_STATUS_FAIL otherwise +** +*******************************************************************************/ +static bt_status_t _btc_storage_remove_ble_bonding_keys(bt_bdaddr_t *remote_bd_addr) +{ + int ret = 0; + bdstr_t bdstr; + bdaddr_to_string(remote_bd_addr, bdstr, sizeof(bdstr)); + + BTIF_TRACE_DEBUG(" %s in bd addr:%s",__FUNCTION__, bdstr); + + ret = _btc_storage_remove_all_ble_keys(bdstr); + + //here don't remove section, because config_save will check it + _btc_storage_save(); + return ret ? BT_STATUS_SUCCESS : BT_STATUS_FAIL; +} + +bt_status_t btc_storage_remove_ble_bonding_keys(bt_bdaddr_t *remote_bd_addr) +{ + bt_status_t ret; + + btc_config_lock(); + ret = _btc_storage_remove_ble_bonding_keys(remote_bd_addr); + btc_config_unlock(); + + return ret; +} + +/******************************************************************************* +** +** Function btc_storage_add_ble_local_key +** +** Description BTIF storage API - Adds the ble key to NVRAM +** +** Returns BT_STATUS_SUCCESS if the store was successful, +** BT_STATUS_FAIL otherwise +** +*******************************************************************************/ +static bt_status_t _btc_storage_add_ble_local_key(char *key, + uint8_t key_type, + uint8_t key_length) +{ + const char* name; + switch (key_type) { + case BTC_LE_LOCAL_KEY_IR: + name = BTC_BLE_STORAGE_LE_LOCAL_KEY_IR_STR; + break; + case BTC_LE_LOCAL_KEY_IRK: + name = BTC_BLE_STORAGE_LE_LOCAL_KEY_IRK_STR; + break; + case BTC_LE_LOCAL_KEY_DHK: + name = BTC_BLE_STORAGE_LE_LOCAL_KEY_DHK_STR; + break; + case BTC_LE_LOCAL_KEY_ER: + name = BTC_BLE_STORAGE_LE_LOCAL_KEY_ER_STR; + break; + default: + return BT_STATUS_FAIL; + } + + int ret = btc_config_set_bin(BTC_BLE_STORAGE_LOCAL_ADAPTER_STR, name, (const uint8_t *)key, key_length); + _btc_storage_save(); + return ret ? BT_STATUS_SUCCESS : BT_STATUS_FAIL; +} + +bt_status_t btc_storage_add_ble_local_key(char *key, + uint8_t key_type, + uint8_t key_length) +{ + bt_status_t ret; + + btc_config_lock(); + ret = _btc_storage_add_ble_local_key(key, key_type, key_length); + btc_config_unlock(); + + return ret; +} + +/******************************************************************************* +** +** Function btc_storage_get_ble_local_key +** +** Description +** +** Returns BT_STATUS_SUCCESS if the fetch was successful, +** BT_STATUS_FAIL otherwise +** +*******************************************************************************/ +static bt_status_t _btc_storage_get_ble_local_key(uint8_t key_type, + char *key_value, + int key_length) +{ + const char* name; + switch (key_type) { + case BTC_LE_LOCAL_KEY_IR: + name = BTC_BLE_STORAGE_LE_LOCAL_KEY_IR_STR; + break; + case BTC_LE_LOCAL_KEY_IRK: + name = BTC_BLE_STORAGE_LE_LOCAL_KEY_IRK_STR; + break; + case BTC_LE_LOCAL_KEY_DHK: + name = BTC_BLE_STORAGE_LE_LOCAL_KEY_DHK_STR; + break; + case BTC_LE_LOCAL_KEY_ER: + name = BTC_BLE_STORAGE_LE_LOCAL_KEY_ER_STR; + break; + default: + return BT_STATUS_FAIL; + } + size_t length = key_length; + + int ret = btc_config_get_bin(BTC_BLE_STORAGE_LOCAL_ADAPTER_STR, name, (uint8_t *)key_value, &length); + + return ret ? BT_STATUS_SUCCESS : BT_STATUS_FAIL; +} + +bt_status_t btc_storage_get_ble_local_key(uint8_t key_type, + char *key_value, + int key_length) +{ + bt_status_t ret; + + btc_config_lock(); + ret = _btc_storage_get_ble_local_key(key_type, key_value, key_length); + btc_config_unlock(); + + return ret; +} + +/******************************************************************************* +** +** Function btc_storage_remove_ble_local_keys +** +** Description BTC storage API - Deletes the bonded device from NVRAM +** +** Returns BT_STATUS_SUCCESS if the deletion was successful, +** BT_STATUS_FAIL otherwise +** +*******************************************************************************/ +static bt_status_t _btc_storage_remove_ble_local_keys(void) +{ + int ret = 1; + + if (btc_config_exist(BTC_BLE_STORAGE_LOCAL_ADAPTER_STR, BTC_BLE_STORAGE_LE_LOCAL_KEY_IR_STR)) { + ret &= btc_config_remove(BTC_BLE_STORAGE_LOCAL_ADAPTER_STR, BTC_BLE_STORAGE_LE_LOCAL_KEY_IR_STR); + } + if (btc_config_exist(BTC_BLE_STORAGE_LOCAL_ADAPTER_STR, BTC_BLE_STORAGE_LE_LOCAL_KEY_IRK_STR)) { + ret &= btc_config_remove(BTC_BLE_STORAGE_LOCAL_ADAPTER_STR, BTC_BLE_STORAGE_LE_LOCAL_KEY_IRK_STR); + } + if (btc_config_exist(BTC_BLE_STORAGE_LOCAL_ADAPTER_STR, BTC_BLE_STORAGE_LE_LOCAL_KEY_DHK_STR)) { + ret &= btc_config_remove(BTC_BLE_STORAGE_LOCAL_ADAPTER_STR, BTC_BLE_STORAGE_LE_LOCAL_KEY_DHK_STR); + } + if (btc_config_exist(BTC_BLE_STORAGE_LOCAL_ADAPTER_STR, BTC_BLE_STORAGE_LE_LOCAL_KEY_ER_STR)) { + ret &= btc_config_remove(BTC_BLE_STORAGE_LOCAL_ADAPTER_STR, BTC_BLE_STORAGE_LE_LOCAL_KEY_ER_STR); + } + _btc_storage_save(); + + return ret ? BT_STATUS_SUCCESS : BT_STATUS_FAIL; +} + +bt_status_t btc_storage_remove_ble_local_keys(void) +{ + bt_status_t ret; + + btc_config_lock(); + ret = _btc_storage_remove_ble_local_keys(); + btc_config_unlock(); + + return ret; +} + +bool _btc_storage_compare_address_key_value(bt_bdaddr_t *remote_bd_addr, + uint8_t key_type, void *key_value, int key_length) +{ + bdstr_t bdstr; + bdaddr_to_string(remote_bd_addr, bdstr, sizeof(bdstr)); + const char *key_type_str; + switch (key_type) { + case BTM_LE_KEY_PENC: + key_type_str = BTC_BLE_STORAGE_LE_KEY_PENC_STR; + break; + case BTM_LE_KEY_PID: + key_type_str = BTC_BLE_STORAGE_LE_KEY_PID_STR; + break; + case BTM_LE_KEY_PCSRK: + key_type_str = BTC_BLE_STORAGE_LE_KEY_PCSRK_STR; + break; + case BTM_LE_KEY_LENC: + key_type_str = BTC_BLE_STORAGE_LE_KEY_LENC_STR; + break; + case BTM_LE_KEY_LCSRK: + key_type_str = BTC_BLE_STORAGE_LE_KEY_LCSRK_STR; + break; + case BTM_LE_KEY_LID: + key_type_str = BTC_BLE_STORAGE_LE_KEY_LID_STR; + default: + return false; + } + + return btc_compare_address_key_value(bdstr, key_type_str, key_value, key_length); +} + +bool btc_storage_compare_address_key_value(bt_bdaddr_t *remote_bd_addr, + uint8_t key_type, void *key_value, int key_length) +{ + bool ret; + + btc_config_lock(); + ret = _btc_storage_compare_address_key_value(remote_bd_addr, key_type, key_value, key_length); + btc_config_unlock(); + + return ret; +} + +static bt_status_t _btc_storage_set_ble_dev_type(bt_bdaddr_t *bd_addr, bool flush) +{ + bool ret = 1; + bdstr_t bdstr; + uint32_t dev_type = 0; + + bdaddr_to_string(bd_addr, bdstr, sizeof(bdstr)); + + btc_config_get_int(bdstr, BTC_BLE_STORAGE_DEV_TYPE_STR, (int *)&dev_type); + ret = btc_config_set_int(bdstr, BTC_BLE_STORAGE_DEV_TYPE_STR, BT_DEVICE_TYPE_BLE|dev_type); + if (ret == false) { + return BT_STATUS_FAIL; + } + + if (flush) { + _btc_storage_save(); + } + + return BT_STATUS_SUCCESS; +} + +bt_status_t btc_storage_set_ble_dev_type(bt_bdaddr_t *bd_addr, bool flush) +{ + bt_status_t ret; + + btc_config_lock(); + ret = _btc_storage_set_ble_dev_type(bd_addr, flush); + btc_config_unlock(); + + return ret; +} + +static bool _btc_storage_get_ble_dev_type(bt_bdaddr_t *bd_addr) +{ + bool ret = 1; + bdstr_t bdstr; + uint32_t dev_type = 0; + + bdaddr_to_string(bd_addr, bdstr, sizeof(bdstr)); + + BTIF_TRACE_DEBUG(" %s in bd addr:%s",__FUNCTION__, bdstr); + + ret = btc_config_get_int(bdstr, BTC_BLE_STORAGE_DEV_TYPE_STR, (int *)&dev_type); + if (ret == false) { + return false; + } + + return (dev_type & BT_DEVICE_TYPE_BLE); +} + +bool btc_storage_get_ble_dev_type(bt_bdaddr_t *bd_addr) +{ + bt_status_t ret; + + btc_config_lock(); + ret = _btc_storage_get_ble_dev_type(bd_addr); + btc_config_unlock(); + + return ret; +} + + +static bt_status_t _btc_storage_remove_ble_dev_type(bt_bdaddr_t *remote_bd_addr, bool flush) +{ + bool ret = true; + bdstr_t bdstr; + uint32_t dev_type = 0; + + bdaddr_to_string(remote_bd_addr, bdstr, sizeof(bdstr)); + + BTIF_TRACE_DEBUG(" %s in bd addr:%s",__FUNCTION__, bdstr); + + ret = btc_config_get_int(bdstr, BTC_BLE_STORAGE_DEV_TYPE_STR, (int *)&dev_type); + if (ret == false) { + //cannot find the key, just return SUCCESS, indicate already removed + return BT_STATUS_SUCCESS; + } + + if (dev_type == BT_DEVICE_TYPE_DUMO) { + ret = btc_config_set_int(bdstr, BTC_BLE_STORAGE_DEV_TYPE_STR, BT_DEVICE_TYPE_BREDR); + } else if (dev_type == BT_DEVICE_TYPE_BLE) { + ret = btc_config_remove(bdstr, BTC_BLE_STORAGE_DEV_TYPE_STR); + } + + if (ret == false) { + return BT_STATUS_FAIL; + } + + if (flush) { + _btc_storage_save(); + } + + return BT_STATUS_SUCCESS; +} + +bt_status_t btc_storage_remove_ble_dev_type(bt_bdaddr_t *remote_bd_addr, bool flush) +{ + bt_status_t ret; + + btc_config_lock(); + ret = _btc_storage_remove_ble_dev_type(remote_bd_addr, flush); + btc_config_unlock(); + + return ret; +} +#endif ///BLE_INCLUDED == TRUE + +static bt_status_t _btc_storage_set_ble_dev_auth_mode(bt_bdaddr_t *remote_bd_addr, uint8_t auth_mode, bool flush) +{ + int ret; + bdstr_t bdstr; + + bdaddr_to_string(remote_bd_addr, bdstr, sizeof(bdstr_t)); + ret = btc_config_set_int(bdstr, BTC_BLE_STORAGE_LE_AUTH_MODE_STR, (int)auth_mode); + if (ret == false) { + return BT_STATUS_FAIL; + } + + if (flush) { + _btc_storage_save(); + } + + return BT_STATUS_SUCCESS; +} + +bt_status_t btc_storage_set_ble_dev_auth_mode(bt_bdaddr_t *remote_bd_addr, uint8_t auth_mode, bool flush) +{ + bt_status_t ret; + + btc_config_lock(); + ret = _btc_storage_set_ble_dev_auth_mode(remote_bd_addr, auth_mode, flush); + btc_config_unlock(); + + return ret; +} + +static bt_status_t _btc_storage_get_ble_dev_auth_mode(bt_bdaddr_t *remote_bd_addr, int* auth_mode) +{ + bdstr_t bdstr; + bdaddr_to_string(remote_bd_addr, bdstr, sizeof(bdstr)); + int ret = btc_config_get_int(bdstr, BTC_BLE_STORAGE_LE_AUTH_MODE_STR, auth_mode); + return ret ? BT_STATUS_SUCCESS : BT_STATUS_FAIL; +} + +bt_status_t btc_storage_get_ble_dev_auth_mode(bt_bdaddr_t *remote_bd_addr, int* auth_mode) +{ + bt_status_t ret; + + btc_config_lock(); + ret = _btc_storage_get_ble_dev_auth_mode(remote_bd_addr, auth_mode); + btc_config_unlock(); + + return ret; +} + +static bt_status_t _btc_storage_remove_ble_dev_auth_mode(bt_bdaddr_t *remote_bd_addr, bool flush) +{ + bool ret = true; + bdstr_t bdstr; + uint32_t auth_mode = 0; + + bdaddr_to_string(remote_bd_addr, bdstr, sizeof(bdstr)); + + ret = btc_config_get_int(bdstr, BTC_BLE_STORAGE_LE_AUTH_MODE_STR, (int *)&auth_mode); + if (ret == false) { + //cannot find the key, just return SUCCESS, indicate already removed + return BT_STATUS_SUCCESS; + } + + ret = btc_config_remove(bdstr, BTC_BLE_STORAGE_LE_AUTH_MODE_STR); + if (ret == false) { + return BT_STATUS_FAIL; + } + + if (flush) { + _btc_storage_save(); + } + + return BT_STATUS_SUCCESS; +} + +bt_status_t btc_storage_remove_ble_dev_auth_mode(bt_bdaddr_t *remote_bd_addr, bool flush) +{ + bt_status_t ret; + + btc_config_lock(); + ret = _btc_storage_remove_ble_dev_auth_mode(remote_bd_addr, flush); + btc_config_unlock(); + + return ret; +} + +static bt_status_t _btc_storage_set_remote_addr_type(bt_bdaddr_t *remote_bd_addr, uint8_t addr_type, bool flush) +{ + int ret; + bdstr_t bdstr; + + bdaddr_to_string(remote_bd_addr, bdstr, sizeof(bdstr_t)); + ret = btc_config_set_int(bdstr, BTC_BLE_STORAGE_ADDR_TYPE_STR, (int)addr_type); + if (ret == false) { + return BT_STATUS_FAIL; + } + + if (flush) { + _btc_storage_save(); + } + + return BT_STATUS_SUCCESS; +} + +bt_status_t btc_storage_set_remote_addr_type(bt_bdaddr_t *remote_bd_addr, uint8_t addr_type, bool flush) +{ + bt_status_t ret; + + btc_config_lock(); + ret = _btc_storage_set_remote_addr_type(remote_bd_addr, addr_type, flush); + btc_config_unlock(); + + return ret; +} + +static bt_status_t _btc_storage_remove_remote_addr_type(bt_bdaddr_t *remote_bd_addr, bool flush) +{ + bool ret = true; + bdstr_t bdstr; + uint32_t dev_type = 0; + + bdaddr_to_string(remote_bd_addr, bdstr, sizeof(bdstr)); + + ret = btc_config_get_int(bdstr, BTC_BLE_STORAGE_ADDR_TYPE_STR, (int *)&dev_type); + if (ret == false) { + //cannot find the key, just return SUCCESS, indicate already removed + return BT_STATUS_SUCCESS; + } + + ret = btc_config_remove(bdstr, BTC_BLE_STORAGE_ADDR_TYPE_STR); + if (ret == false) { + return BT_STATUS_FAIL; + } + + if (flush) { + _btc_storage_save(); + } + + return BT_STATUS_SUCCESS; +} + +bt_status_t btc_storage_remove_remote_addr_type(bt_bdaddr_t *remote_bd_addr, bool flush) +{ + bt_status_t ret; + + btc_config_lock(); + ret = _btc_storage_remove_remote_addr_type(remote_bd_addr, flush); + btc_config_unlock(); + + return ret; +} + +static bt_status_t _btc_storage_get_remote_addr_type(bt_bdaddr_t *remote_bd_addr, + int*addr_type) +{ + bdstr_t bdstr; + bdaddr_to_string(remote_bd_addr, bdstr, sizeof(bdstr)); + int ret = btc_config_get_int(bdstr, BTC_BLE_STORAGE_ADDR_TYPE_STR, addr_type); + return ret ? BT_STATUS_SUCCESS : BT_STATUS_FAIL; +} + +bt_status_t btc_storage_get_remote_addr_type(bt_bdaddr_t *remote_bd_addr, + int*addr_type) +{ + bt_status_t ret; + + btc_config_lock(); + ret = _btc_storage_get_remote_addr_type(remote_bd_addr, addr_type); + btc_config_unlock(); + + return ret; +} + +#if (BLE_INCLUDED == TRUE) +static void _btc_read_le_key(const uint8_t key_type, const size_t key_len, bt_bdaddr_t bd_addr, + const uint8_t addr_type, const bool add_key, bool *device_added, bool *key_found) +{ + assert(device_added); + assert(key_found); + + char buffer[100]; + memset(buffer, 0, sizeof(buffer)); + + bt_status_t ret = _btc_storage_get_ble_bonding_key(&bd_addr, key_type, buffer, key_len); + + if (ret == BT_STATUS_SUCCESS) { + if (add_key) { + BD_ADDR bta_bd_addr; + bdcpy(bta_bd_addr, bd_addr.address); + + if (!*device_added) { + int auth_mode = 0; + if(_btc_storage_get_ble_dev_auth_mode(&bd_addr, &auth_mode) != BT_STATUS_SUCCESS) { + BTC_TRACE_WARNING("%s Failed to get auth mode from flash, please erase flash and download the firmware again", __func__); + } + BTA_DmAddBleDevice(bta_bd_addr, addr_type, auth_mode, BT_DEVICE_TYPE_BLE); + *device_added = true; + } + +#if (!CONFIG_BT_STACK_NO_LOG) + char bd_str[20] = {0}; +#endif + BTC_TRACE_DEBUG("%s() Adding key type %d for %s", __func__, + key_type, bdaddr_to_string(&bd_addr, bd_str, sizeof(bd_str))); + BTA_DmAddBleKey(bta_bd_addr, (tBTA_LE_KEY_VALUE *)buffer, key_type); + } + + *key_found = true; + } +} +bt_status_t _btc_storage_in_fetch_bonded_ble_device(const char *remote_bd_addr, int add) +{ + uint32_t device_type; + int addr_type; + bt_bdaddr_t bd_addr; + BD_ADDR bta_bd_addr; + bool device_added = false; + bool key_found = false; + + if (!btc_config_get_int(remote_bd_addr, BTC_BLE_STORAGE_DEV_TYPE_STR, (int *)&device_type)) { + BTC_TRACE_ERROR("%s, device_type = %x", __func__, device_type); + return BT_STATUS_FAIL; + } + + string_to_bdaddr(remote_bd_addr, &bd_addr); + bdcpy(bta_bd_addr, bd_addr.address); + + if (_btc_storage_get_remote_addr_type(&bd_addr, &addr_type) != BT_STATUS_SUCCESS) { + addr_type = BLE_ADDR_PUBLIC; + _btc_storage_set_remote_addr_type(&bd_addr, BLE_ADDR_PUBLIC, true); + } + + _btc_read_le_key(BTM_LE_KEY_PENC, sizeof(tBTM_LE_PENC_KEYS), + bd_addr, addr_type, add, &device_added, &key_found); + + _btc_read_le_key(BTM_LE_KEY_PID, sizeof(tBTM_LE_PID_KEYS), + bd_addr, addr_type, add, &device_added, &key_found); + + _btc_read_le_key(BTM_LE_KEY_LID, sizeof(tBTM_LE_PID_KEYS), + bd_addr, addr_type, add, &device_added, &key_found); + + _btc_read_le_key(BTM_LE_KEY_PCSRK, sizeof(tBTM_LE_PCSRK_KEYS), + bd_addr, addr_type, add, &device_added, &key_found); + + _btc_read_le_key(BTM_LE_KEY_LENC, sizeof(tBTM_LE_LENC_KEYS), + bd_addr, addr_type, add, &device_added, &key_found); + + _btc_read_le_key(BTM_LE_KEY_LCSRK, sizeof(tBTM_LE_LCSRK_KEYS), + bd_addr, addr_type, add, &device_added, &key_found); + + if (key_found) { + return BT_STATUS_SUCCESS; + } else { + BTC_TRACE_DEBUG("Remote device:%s, no link key or ble key found", remote_bd_addr); + } + + return BT_STATUS_FAIL; +} + +bt_status_t btc_storage_get_bonded_ble_devices_list(esp_ble_bond_dev_t *bond_dev, int dev_num) +{ + bt_bdaddr_t bd_addr; + char buffer[sizeof(tBTM_LE_KEY_VALUE)] = {0}; + + btc_config_lock(); + for (const btc_config_section_iter_t *iter = btc_config_section_begin(); iter != btc_config_section_end(); + iter = btc_config_section_next(iter)) { + + if (dev_num-- <= 0) { + break; + } + uint32_t device_type = 0; + const char *name = btc_config_section_name(iter); + + if (!string_is_bdaddr(name) || + !btc_config_get_int(name, BTC_BLE_STORAGE_DEV_TYPE_STR, (int *)&device_type) || + !(device_type & BT_DEVICE_TYPE_BLE)) { + dev_num ++; + continue; + } + + string_to_bdaddr(name, &bd_addr); + memcpy(bond_dev->bd_addr, bd_addr.address, sizeof(bt_bdaddr_t)); + //resolve the peer device long term key + if (_btc_storage_get_ble_bonding_key(&bd_addr, BTM_LE_KEY_PENC, buffer, sizeof(tBTM_LE_PENC_KEYS)) == BT_STATUS_SUCCESS) { + bond_dev->bond_key.key_mask |= ESP_BLE_ENC_KEY_MASK; + memcpy(&bond_dev->bond_key.penc_key, buffer, sizeof(tBTM_LE_PENC_KEYS)); + } + //resolve the peer device csrk + if (_btc_storage_get_ble_bonding_key(&bd_addr, BTM_LE_KEY_PCSRK, buffer, sizeof(tBTM_LE_PCSRK_KEYS)) == BT_STATUS_SUCCESS) { + bond_dev->bond_key.key_mask |= ESP_BLE_CSR_KEY_MASK; + memcpy(&bond_dev->bond_key.pcsrk_key, buffer, sizeof(tBTM_LE_PCSRK_KEYS)); + } + //resolve the peer device irk + if (_btc_storage_get_ble_bonding_key(&bd_addr, BTM_LE_KEY_PID, buffer, sizeof(tBTM_LE_PID_KEYS)) == BT_STATUS_SUCCESS) { + bond_dev->bond_key.key_mask |= ESP_BLE_ID_KEY_MASK; + tBTM_LE_PID_KEYS *pid_key = (tBTM_LE_PID_KEYS *) buffer; + //Note: The memory size of the pid_key.addr_type in bond_key is different from that of (tBTM_LE_PID_KEYS) *pid_key. + memcpy(&bond_dev->bond_key.pid_key.irk, pid_key->irk, ESP_BT_OCTET16_LEN); + bond_dev->bond_key.pid_key.addr_type = pid_key->addr_type; + memcpy(&bond_dev->bond_key.pid_key.static_addr, pid_key->static_addr, ESP_BD_ADDR_LEN); + } + //search for the next bond device + bond_dev++; + } + btc_config_unlock(); + + return BT_STATUS_SUCCESS; +} + +int btc_storage_get_num_ble_bond_devices(void) +{ + int num_dev = 0; + uint32_t device_type = 0; + + btc_config_lock(); + for (const btc_config_section_iter_t *iter = btc_config_section_begin(); iter != btc_config_section_end(); + iter = btc_config_section_next(iter)) { + const char *name = btc_config_section_name(iter); + if (!string_is_bdaddr(name) || + !btc_config_get_int(name, BTC_BLE_STORAGE_DEV_TYPE_STR, (int *)&device_type) || + !(device_type & BT_DEVICE_TYPE_BLE)) { + continue; + } + + num_dev++; + } + btc_config_unlock(); + + return num_dev; +} + +bt_status_t btc_storage_get_gatt_cl_supp_feat(bt_bdaddr_t *remote_bd_addr, uint8_t *value, int len) +{ + bdstr_t bdstr; + bdaddr_to_string(remote_bd_addr, bdstr, sizeof(bdstr)); + int ret = btc_config_get_bin(bdstr, BTC_BLE_STORAGE_GATT_CL_SUPP_FEAT_STR, value, (size_t *)&len); + return ret ? BT_STATUS_SUCCESS : BT_STATUS_FAIL; +} + +bt_status_t btc_storage_set_gatt_cl_supp_feat(bt_bdaddr_t *remote_bd_addr, uint8_t *value, int len) +{ + int ret; + bdstr_t bdstr; + + bdaddr_to_string(remote_bd_addr, bdstr, sizeof(bdstr_t)); + ret = btc_config_set_bin(bdstr, BTC_BLE_STORAGE_GATT_CL_SUPP_FEAT_STR, value, (size_t)len); + if (ret == false) { + return BT_STATUS_FAIL; + } + + return BT_STATUS_SUCCESS; +} + +bt_status_t btc_storage_get_gatt_db_hash(bt_bdaddr_t *remote_bd_addr, uint8_t *value, int len) +{ + bdstr_t bdstr; + bdaddr_to_string(remote_bd_addr, bdstr, sizeof(bdstr)); + int ret = btc_config_get_bin(bdstr, BTC_BLE_STORAGE_GATT_DB_HASH_STR, value, (size_t *)&len); + return ret ? BT_STATUS_SUCCESS : BT_STATUS_FAIL; +} + +bt_status_t btc_storage_set_gatt_db_hash(bt_bdaddr_t *remote_bd_addr, uint8_t *value, int len) +{ + int ret; + bdstr_t bdstr; + + bdaddr_to_string(remote_bd_addr, bdstr, sizeof(bdstr_t)); + ret = btc_config_set_bin(bdstr, BTC_BLE_STORAGE_GATT_DB_HASH_STR, value, (size_t)len); + if (ret == false) { + return BT_STATUS_FAIL; + } + + return BT_STATUS_SUCCESS; +} + +bt_status_t btc_storage_remove_gatt_cl_supp_feat(bt_bdaddr_t *remote_bd_addr) +{ + bool ret = true; + bdstr_t bdstr; + + bdaddr_to_string(remote_bd_addr, bdstr, sizeof(bdstr)); + + ret = btc_config_remove(bdstr, BTC_BLE_STORAGE_GATT_CL_SUPP_FEAT_STR); + if (ret == false) { + return BT_STATUS_FAIL; + } + + return BT_STATUS_SUCCESS; +} + +bt_status_t btc_storage_remove_gatt_db_hash(bt_bdaddr_t *remote_bd_addr) +{ + bool ret = true; + bdstr_t bdstr; + + bdaddr_to_string(remote_bd_addr, bdstr, sizeof(bdstr)); + + ret = btc_config_remove(bdstr, BTC_BLE_STORAGE_GATT_DB_HASH_STR); + if (ret == false) { + return BT_STATUS_FAIL; + } + + return BT_STATUS_SUCCESS; +} +#endif ///BLE_INCLUDED == TRUE +#endif ///SMP_INCLUDED == TRUE diff --git a/lib/bt/host/bluedroid/btc/core/btc_config.c b/lib/bt/host/bluedroid/btc/core/btc_config.c new file mode 100644 index 00000000..f46aef89 --- /dev/null +++ b/lib/bt/host/bluedroid/btc/core/btc_config.c @@ -0,0 +1,351 @@ +/* + * SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +#include "common/bt_defs.h" +#include "common/bt_trace.h" +#include "osi/alarm.h" +#include "osi/allocator.h" +#include "device/bdaddr.h" +#include "btc/btc_config.h" +#include "btc/btc_util.h" +#include "osi/config.h" +#include "osi/osi.h" +#include "osi/mutex.h" + +#include "stack/bt_types.h" +#include "nvs.h" + +static char CONFIG_FILE_PATH[NVS_NS_NAME_MAX_SIZE] = "bt_config.conf"; +static const period_ms_t CONFIG_SETTLE_PERIOD_MS = 3000; + +static void btc_key_value_to_string(uint8_t *key_value, char *value_str, int key_length); +static osi_mutex_t lock; // protects operations on |config|. +static config_t *config; + +int btc_config_file_path_update(const char *file_path) +{ + if (file_path != NULL && strlen(file_path) < NVS_NS_NAME_MAX_SIZE) { + memcpy(CONFIG_FILE_PATH, file_path, strlen(file_path)); + CONFIG_FILE_PATH[strlen(file_path)] = '\0'; + return 0; + } + BTC_TRACE_ERROR("Update failed, file_path is NULL or length should be less than %d\n", NVS_NS_NAME_MAX_SIZE); + return -1; +} + +bool btc_compare_address_key_value(const char *section, const char *key_type, void *key_value, int key_length) +{ + assert(key_value != NULL); + bool status = false; + char value_str[100] = {0}; + if(key_length > sizeof(value_str)/2) { + return false; + } + btc_key_value_to_string((uint8_t *)key_value, value_str, key_length); + if ((status = config_has_key_in_section(config, key_type, value_str)) == true) { + config_remove_section(config, section); + } + return status; +} + +static void btc_key_value_to_string(uint8_t *key_value, char *value_str, int key_length) +{ + const char *lookup = "0123456789abcdef"; + + assert(key_value != NULL); + assert(value_str != NULL); + + for (size_t i = 0; i < key_length; ++i) { + value_str[(i * 2) + 0] = lookup[(key_value[i] >> 4) & 0x0F]; + value_str[(i * 2) + 1] = lookup[key_value[i] & 0x0F]; + } + + return; +} + +// Module lifecycle functions + +bool btc_config_init(void) +{ + osi_mutex_new(&lock); + config = config_new(CONFIG_FILE_PATH); + if (!config) { + BTC_TRACE_WARNING("%s unable to load config file; starting unconfigured.\n", __func__); + config = config_new_empty(); + if (!config) { + BTC_TRACE_ERROR("%s unable to allocate a config object.\n", __func__); + goto error; + } + } + if (config_save(config, CONFIG_FILE_PATH)) { + // unlink(LEGACY_CONFIG_FILE_PATH); + } + + return true; + +error:; + config_free(config); + osi_mutex_free(&lock); + config = NULL; + BTC_TRACE_ERROR("%s failed\n", __func__); + return false; +} + +bool btc_config_shut_down(void) +{ + btc_config_flush(); + return true; +} + +bool btc_config_clean_up(void) +{ + btc_config_flush(); + + config_free(config); + osi_mutex_free(&lock); + config = NULL; + return true; +} + +bool btc_config_has_section(const char *section) +{ + assert(config != NULL); + assert(section != NULL); + + return config_has_section(config, section); +} + +bool btc_config_exist(const char *section, const char *key) +{ + assert(config != NULL); + assert(section != NULL); + assert(key != NULL); + + return config_has_key(config, section, key); +} + +bool btc_config_get_int(const char *section, const char *key, int *value) +{ + assert(config != NULL); + assert(section != NULL); + assert(key != NULL); + assert(value != NULL); + + bool ret = config_has_key(config, section, key); + if (ret) { + *value = config_get_int(config, section, key, *value); + } + + return ret; +} + +bool btc_config_set_int(const char *section, const char *key, int value) +{ + assert(config != NULL); + assert(section != NULL); + assert(key != NULL); + + config_set_int(config, section, key, value); + + return true; +} + +bool btc_config_get_str(const char *section, const char *key, char *value, int *size_bytes) +{ + assert(config != NULL); + assert(section != NULL); + assert(key != NULL); + assert(value != NULL); + assert(size_bytes != NULL); + + const char *stored_value = config_get_string(config, section, key, NULL); + + if (!stored_value) { + return false; + } + + strlcpy(value, stored_value, *size_bytes); + *size_bytes = strlen(value) + 1; + + return true; +} + +bool btc_config_set_str(const char *section, const char *key, const char *value) +{ + assert(config != NULL); + assert(section != NULL); + assert(key != NULL); + assert(value != NULL); + + config_set_string(config, section, key, value, false); + + return true; +} + +bool btc_config_get_bin(const char *section, const char *key, uint8_t *value, size_t *length) +{ + assert(config != NULL); + assert(section != NULL); + assert(key != NULL); + assert(value != NULL); + assert(length != NULL); + + const char *value_str = config_get_string(config, section, key, NULL); + + if (!value_str) { + return false; + } + + size_t value_len = strlen(value_str); + if ((value_len % 2) != 0 || *length < (value_len / 2)) { + return false; + } + + for (size_t i = 0; i < value_len; ++i) + if (!isxdigit((unsigned char)value_str[i])) { + return false; + } + + for (*length = 0; *value_str; value_str += 2, *length += 1) { + unsigned int val; + sscanf(value_str, "%02x", &val); + value[*length] = (uint8_t)(val); + } + + return true; +} + +size_t btc_config_get_bin_length(const char *section, const char *key) +{ + assert(config != NULL); + assert(section != NULL); + assert(key != NULL); + + const char *value_str = config_get_string(config, section, key, NULL); + + if (!value_str) { + return 0; + } + + size_t value_len = strlen(value_str); + return ((value_len % 2) != 0) ? 0 : (value_len / 2); +} + +bool btc_config_set_bin(const char *section, const char *key, const uint8_t *value, size_t length) +{ + const char *lookup = "0123456789abcdef"; + + assert(config != NULL); + assert(section != NULL); + assert(key != NULL); + + if (length > 0) { + assert(value != NULL); + } + + char *str = (char *)osi_calloc(length * 2 + 1); + if (!str) { + return false; + } + + for (size_t i = 0; i < length; ++i) { + str[(i * 2) + 0] = lookup[(value[i] >> 4) & 0x0F]; + str[(i * 2) + 1] = lookup[value[i] & 0x0F]; + } + + config_set_string(config, section, key, str, false); + + osi_free(str); + return true; +} + +const btc_config_section_iter_t *btc_config_section_begin(void) +{ + assert(config != NULL); + return (const btc_config_section_iter_t *)config_section_begin(config); +} + +const btc_config_section_iter_t *btc_config_section_end(void) +{ + assert(config != NULL); + return (const btc_config_section_iter_t *)config_section_end(config); +} + +const btc_config_section_iter_t *btc_config_section_next(const btc_config_section_iter_t *section) +{ + assert(config != NULL); + assert(section != NULL); + return (const btc_config_section_iter_t *)config_section_next((const config_section_node_t *)section); +} + +const char *btc_config_section_name(const btc_config_section_iter_t *section) +{ + assert(config != NULL); + assert(section != NULL); + return config_section_name((const config_section_node_t *)section); +} + + + +bool btc_config_remove(const char *section, const char *key) +{ + assert(config != NULL); + assert(section != NULL); + assert(key != NULL); + + return config_remove_key(config, section, key); +} + +bool btc_config_remove_section(const char *section) +{ + assert(config != NULL); + assert(section != NULL); + + return config_remove_section(config, section); +} + +bool btc_config_update_newest_section(const char *section) +{ + assert(config != NULL); + assert(section != NULL); + + return config_update_newest_section(config, section); +} + +void btc_config_flush(void) +{ + assert(config != NULL); + + config_save(config, CONFIG_FILE_PATH); +} + +int btc_config_clear(void) +{ + assert(config != NULL); + + config_free(config); + + config = config_new_empty(); + if (config == NULL) { + return false; + } + int ret = config_save(config, CONFIG_FILE_PATH); + return ret; +} + +void btc_config_lock(void) +{ + osi_mutex_lock(&lock, OSI_MUTEX_MAX_TIMEOUT); +} + +void btc_config_unlock(void) +{ + osi_mutex_unlock(&lock); +} diff --git a/lib/bt/host/bluedroid/btc/core/btc_dev.c b/lib/bt/host/bluedroid/btc/core/btc_dev.c new file mode 100644 index 00000000..1aceface --- /dev/null +++ b/lib/bt/host/bluedroid/btc/core/btc_dev.c @@ -0,0 +1,58 @@ +/* + * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "osi/allocator.h" +#include "bta/bta_api.h" +#include "btc/btc_task.h" +#include "btc/btc_manage.h" +#include "btc/btc_dev.h" + +void btc_dev_arg_deep_free(btc_msg_t *msg) +{ + BTC_TRACE_DEBUG("%s \n", __func__); + + switch (msg->act) { + case BTC_DEV_ACT_SET_DEVICE_NAME:{ + char *device_name = ((btc_dev_args_t *)msg->arg)->set_dev_name.device_name; + if (device_name) { + osi_free(device_name); + } + break; + } +#if (ESP_COEX_VSC_INCLUDED == TRUE) + case BTC_DEV_ACT_CFG_COEX_STATUS: + break; +#endif + default: + BTC_TRACE_DEBUG("Unhandled deep free %d\n", msg->act); + break; + } +} + +void btc_dev_call_handler(btc_msg_t *msg) +{ + btc_dev_args_t *arg = (btc_dev_args_t *)msg->arg; + + BTC_TRACE_DEBUG("%s act %d\n", __FUNCTION__, msg->act); + + switch (msg->act) { + case BTC_DEV_ACT_SET_DEVICE_NAME: + BTA_DmSetDeviceName(arg->set_dev_name.device_name); + break; +#if (ESP_COEX_VSC_INCLUDED == TRUE) + case BTC_DEV_ACT_CFG_COEX_STATUS: + BTA_DmCfgCoexStatus(arg->cfg_coex_status.op, + arg->cfg_coex_status.type, + arg->cfg_coex_status.status); + break; +#endif + default: + break; + } + + btc_dev_arg_deep_free(msg); +} diff --git a/lib/bt/host/bluedroid/btc/core/btc_dm.c b/lib/bt/host/bluedroid/btc/core/btc_dm.c new file mode 100644 index 00000000..b0afdad8 --- /dev/null +++ b/lib/bt/host/bluedroid/btc/core/btc_dm.c @@ -0,0 +1,1080 @@ +/* + * SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "common/bt_target.h" +#include +#include +#include "btc/btc_common.h" +#include "btc/btc_dm.h" +#include "btc/btc_main.h" +#include "common/bt_trace.h" +#include "common/bt_target.h" +#include "btc/btc_storage.h" +#include "btc/btc_ble_storage.h" +#include "btc_gap_ble.h" +#include "btm_int.h" +#include "bta/bta_api.h" +#include "bta/bta_gatt_api.h" +#include "osi/allocator.h" +#include "btc/btc_manage.h" + + +#if (BTC_GAP_BT_INCLUDED == TRUE) +#include "btc_gap_bt.h" +#endif /* BTC_GAP_BT_INCLUDED == TRUE */ +/****************************************************************************** +** Constants & Macros +******************************************************************************/ +#define BTA_SERVICE_ID_TO_SERVICE_MASK(id) (1 << (id)) + +/****************************************************************************** +** Static variables +******************************************************************************/ +#if BTC_DYNAMIC_MEMORY == FALSE +btc_dm_cb_t btc_dm_cb = {0}; +#else +btc_dm_cb_t *btc_dm_cb_ptr; +#endif + +/****************************************************************************** +** Static functions +******************************************************************************/ +/****************************************************************************** +** Externs +******************************************************************************/ +#if BTC_AV_INCLUDED +extern bt_status_t btc_av_source_execute_service(BOOLEAN b_enable); +extern bt_status_t btc_av_sink_execute_service(BOOLEAN b_enable); +#endif +#if BTC_HF_INCLUDED +extern bt_status_t btc_hf_execute_service(BOOLEAN b_enable); +#endif +#if BTC_HF_CLIENT_INCLUDED +extern bt_status_t btc_hf_client_execute_service(BOOLEAN b_enable); +#endif +/****************************************************************************** +** Functions +******************************************************************************/ +static void btc_dm_sec_arg_deep_free(btc_msg_t *msg) +{ + btc_dm_sec_args_t *arg = (btc_dm_sec_args_t *)(msg->arg); + if (msg->act == BTA_DM_BLE_KEY_EVT) { + osi_free(arg->sec.ble_key.p_key_value); + } +} + +void btc_dm_sec_arg_deep_copy(btc_msg_t *msg, void *dst, void *src) +{ + tBTA_DM_SEC *dst_dm_sec = (tBTA_DM_SEC *)dst; + tBTA_DM_SEC *src_dm_sec = (tBTA_DM_SEC *)src; + + if (!src_dm_sec) { + return; + } + + assert(dst_dm_sec); + memcpy(dst_dm_sec, src_dm_sec, sizeof(tBTA_DM_SEC)); + + if (msg->act == BTA_DM_BLE_KEY_EVT) { + dst_dm_sec->ble_key.p_key_value = osi_malloc(sizeof(tBTM_LE_KEY_VALUE)); + assert(src_dm_sec->ble_key.p_key_value); + assert(dst_dm_sec->ble_key.p_key_value); + memcpy(dst_dm_sec->ble_key.p_key_value, src_dm_sec->ble_key.p_key_value, sizeof(tBTM_LE_KEY_VALUE)); + } +} + +/******************************************************************************* +** +** Function btc_dm_evt +** +** Description Switches context from BTE to BTC for all DM events +** +** Returns void +** +*******************************************************************************/ + +void btc_dm_sec_evt(tBTA_DM_SEC_EVT event, tBTA_DM_SEC *data) +{ + btc_msg_t msg = {0}; + + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_DM_SEC; + msg.act = event; + + btc_transfer_context(&msg, (btc_dm_sec_args_t *)data, + data == NULL ? 0 : sizeof(btc_dm_sec_args_t), + btc_dm_sec_arg_deep_copy, btc_dm_sec_arg_deep_free); +} + +static void btc_enable_bluetooth_evt(tBTA_STATUS status) +{ + if (status == BTA_SUCCESS) { + future_ready(*btc_main_get_future_p(BTC_MAIN_ENABLE_FUTURE), FUTURE_SUCCESS); + } else { + future_ready(*btc_main_get_future_p(BTC_MAIN_ENABLE_FUTURE), FUTURE_FAIL); + } +} + +static void btc_disable_bluetooth_evt(void) +{ + BTC_TRACE_DEBUG("%s", __FUNCTION__); + + future_ready(*btc_main_get_future_p(BTC_MAIN_DISABLE_FUTURE), FUTURE_SUCCESS); +} + +#if (SMP_INCLUDED == TRUE) +#if (BLE_INCLUDED == TRUE) +void btc_dm_load_ble_local_keys(void) +{ + memset(&btc_dm_cb.ble_local_key_cb, 0, sizeof(btc_dm_local_key_cb_t)); + + if (btc_storage_get_ble_local_key(BTC_LE_LOCAL_KEY_ER,(char*)&btc_dm_cb.ble_local_key_cb.er[0], + BT_OCTET16_LEN)== BT_STATUS_SUCCESS) { + btc_dm_cb.ble_local_key_cb.is_er_rcvd = TRUE; + BTC_TRACE_DEBUG("%s BLE ER key loaded",__func__ ); + } + + if ((btc_storage_get_ble_local_key(BTC_LE_LOCAL_KEY_IR,(char*)&btc_dm_cb.ble_local_key_cb.id_keys.ir[0], + BT_OCTET16_LEN)== BT_STATUS_SUCCESS )&& + (btc_storage_get_ble_local_key(BTC_LE_LOCAL_KEY_IRK, (char*)&btc_dm_cb.ble_local_key_cb.id_keys.irk[0], + BT_OCTET16_LEN)== BT_STATUS_SUCCESS)&& + (btc_storage_get_ble_local_key(BTC_LE_LOCAL_KEY_DHK,(char*)&btc_dm_cb.ble_local_key_cb.id_keys.dhk[0], + BT_OCTET16_LEN)== BT_STATUS_SUCCESS)) { + btc_dm_cb.ble_local_key_cb.is_id_keys_rcvd = TRUE; + BTC_TRACE_DEBUG("%s BLE ID keys loaded", __func__); + } + +} +void btc_dm_get_ble_local_keys(tBTA_DM_BLE_LOCAL_KEY_MASK *p_key_mask, BT_OCTET16 er, + tBTA_BLE_LOCAL_ID_KEYS *p_id_keys) +{ + if (btc_dm_cb.ble_local_key_cb.is_er_rcvd ) { + memcpy(&er[0], &btc_dm_cb.ble_local_key_cb.er[0], sizeof(BT_OCTET16)); + *p_key_mask |= BTA_BLE_LOCAL_KEY_TYPE_ER; + } + + if (btc_dm_cb.ble_local_key_cb.is_id_keys_rcvd) { + memcpy(&p_id_keys->ir[0], &btc_dm_cb.ble_local_key_cb.id_keys.ir[0], sizeof(BT_OCTET16)); + memcpy(&p_id_keys->irk[0], &btc_dm_cb.ble_local_key_cb.id_keys.irk[0], sizeof(BT_OCTET16)); + memcpy(&p_id_keys->dhk[0], &btc_dm_cb.ble_local_key_cb.id_keys.dhk[0], sizeof(BT_OCTET16)); + *p_key_mask |= BTA_BLE_LOCAL_KEY_TYPE_ID; + } + BTC_TRACE_DEBUG("%s *p_key_mask=0x%02x",__func__, *p_key_mask); +} + + +static void btc_dm_remove_ble_bonding_keys(void) +{ + bt_bdaddr_t bd_addr; + BTC_TRACE_DEBUG("%s\n",__func__); + + bdcpy(bd_addr.address, btc_dm_cb.pairing_cb.bd_addr); + + btc_storage_remove_gatt_cl_supp_feat(&bd_addr); + btc_storage_remove_gatt_db_hash(&bd_addr); + btc_storage_remove_remote_addr_type(&bd_addr, false); + btc_storage_remove_ble_dev_auth_mode(&bd_addr, false); + btc_storage_remove_ble_dev_type(&bd_addr, false); + btc_storage_remove_ble_bonding_keys(&bd_addr); +} + +static void btc_dm_save_ble_bonding_keys(void) +{ + if (!(btc_dm_cb.pairing_cb.ble.is_penc_key_rcvd || btc_dm_cb.pairing_cb.ble.is_pid_key_rcvd || btc_dm_cb.pairing_cb.ble.is_pcsrk_key_rcvd || + btc_dm_cb.pairing_cb.ble.is_lenc_key_rcvd || btc_dm_cb.pairing_cb.ble.is_lcsrk_key_rcvd || btc_dm_cb.pairing_cb.ble.is_lidk_key_rcvd)) { + return ; + } + bt_bdaddr_t bd_addr; + + bdcpy(bd_addr.address, btc_dm_cb.pairing_cb.bd_addr); + + btc_storage_set_ble_dev_type(&bd_addr, false); + BTC_TRACE_DEBUG("%s, penc = %d, pid = %d", __func__, btc_dm_cb.pairing_cb.ble.is_penc_key_rcvd, btc_dm_cb.pairing_cb.ble.is_pid_key_rcvd); + if (btc_dm_cb.pairing_cb.ble.is_penc_key_rcvd) { + btc_storage_add_ble_bonding_key(&bd_addr, + (char *) &btc_dm_cb.pairing_cb.ble.penc_key, + BTM_LE_KEY_PENC, + sizeof(tBTM_LE_PENC_KEYS)); + btc_dm_cb.pairing_cb.ble.is_penc_key_rcvd = false; + } + + if (btc_dm_cb.pairing_cb.ble.is_pid_key_rcvd) { + btc_storage_add_ble_bonding_key(&bd_addr, + (char *) &btc_dm_cb.pairing_cb.ble.pid_key, + BTM_LE_KEY_PID, + sizeof(tBTM_LE_PID_KEYS)); + btc_dm_cb.pairing_cb.ble.is_pid_key_rcvd = false; + } + + + if (btc_dm_cb.pairing_cb.ble.is_pcsrk_key_rcvd) { + btc_storage_add_ble_bonding_key(&bd_addr, + (char *) &btc_dm_cb.pairing_cb.ble.pcsrk_key, + BTM_LE_KEY_PCSRK, + sizeof(tBTM_LE_PCSRK_KEYS)); + btc_dm_cb.pairing_cb.ble.is_pcsrk_key_rcvd = false; + } + + + if (btc_dm_cb.pairing_cb.ble.is_lenc_key_rcvd) { + btc_storage_add_ble_bonding_key(&bd_addr, + (char *) &btc_dm_cb.pairing_cb.ble.lenc_key, + BTM_LE_KEY_LENC, + sizeof(tBTM_LE_LENC_KEYS)); + btc_dm_cb.pairing_cb.ble.is_lenc_key_rcvd = false; + } + + if (btc_dm_cb.pairing_cb.ble.is_lcsrk_key_rcvd) { + btc_storage_add_ble_bonding_key(&bd_addr, + (char *) &btc_dm_cb.pairing_cb.ble.lcsrk_key, + BTM_LE_KEY_LCSRK, + sizeof(tBTM_LE_LCSRK_KEYS)); + btc_dm_cb.pairing_cb.ble.is_lcsrk_key_rcvd = false; + } + + if (btc_dm_cb.pairing_cb.ble.is_lidk_key_rcvd) { + btc_storage_add_ble_bonding_key(&bd_addr, + NULL, + BTM_LE_KEY_LID, + 0); + btc_dm_cb.pairing_cb.ble.is_lidk_key_rcvd = false; + } +} + +static void btc_dm_ble_auth_cmpl_evt (tBTA_DM_AUTH_CMPL *p_auth_cmpl) +{ + /* Save link key, if not temporary */ + BTC_TRACE_DEBUG("%s, status = %d", __func__, p_auth_cmpl->success); + bt_status_t status = BT_STATUS_FAIL; + int addr_type; + bt_bdaddr_t bdaddr; + bdcpy(bdaddr.address, p_auth_cmpl->bd_addr); + bdcpy(btc_dm_cb.pairing_cb.bd_addr, p_auth_cmpl->bd_addr); + + if (p_auth_cmpl->success) { + status = BT_STATUS_SUCCESS; + BTC_TRACE_DEBUG ("%s, - p_auth_cmpl->bd_addr: %08x%04x", __func__, + (p_auth_cmpl->bd_addr[0] << 24) + (p_auth_cmpl->bd_addr[1] << 16) + (p_auth_cmpl->bd_addr[2] << 8) + p_auth_cmpl->bd_addr[3], + (p_auth_cmpl->bd_addr[4] << 8) + p_auth_cmpl->bd_addr[5]); + + // Check if need to save BLE keys + if((p_auth_cmpl->auth_mode & SMP_AUTH_GEN_BOND) == 0) { + return; + } + + if (btc_dm_cb.pairing_cb.ble.is_pid_key_rcvd) { + // delete unused section in NVS + btc_storage_remove_unused_sections(p_auth_cmpl->bd_addr, &btc_dm_cb.pairing_cb.ble.pid_key); + } + + if (btc_storage_get_remote_addr_type(&bdaddr, &addr_type) != BT_STATUS_SUCCESS) { + btc_storage_set_remote_addr_type(&bdaddr, p_auth_cmpl->addr_type, true); + } + btc_storage_set_ble_dev_auth_mode(&bdaddr, p_auth_cmpl->auth_mode, true); + btc_dm_save_ble_bonding_keys(); + } else { + /*Map the HCI fail reason to bt status */ + switch (p_auth_cmpl->fail_reason) { + case BTA_DM_AUTH_SMP_PAIR_AUTH_FAIL: + case BTA_DM_AUTH_SMP_CONFIRM_VALUE_FAIL: + btc_dm_remove_ble_bonding_keys(); + status = BT_STATUS_AUTH_FAILURE; + break; + case BTA_DM_AUTH_SMP_PAIR_NOT_SUPPORT: + status = BT_STATUS_AUTH_REJECTED; + break; + default: + btc_dm_remove_ble_bonding_keys(); + status = BT_STATUS_FAIL; + break; + } + + } + +#if (CONFIG_BT_STACK_NO_LOG) + (void) status; +#endif + BTC_TRACE_DEBUG("%s, authentication status = %x", __func__, status); + return; + +} +#endif ///BLE_INCLUDED == TRUE +#endif ///SMP_INCLUDED == TRUE + +static void btc_dm_link_up_evt(tBTA_DM_LINK_UP *p_link_up) +{ + BD_ADDR bd_addr; + bt_bdaddr_t bt_bdaddr; + + + if (p_link_up->sc_downgrade == 1) { + memcpy(bt_bdaddr.address, p_link_up->bd_addr, sizeof(BD_ADDR)); + if (btc_storage_remove_bonded_device(&bt_bdaddr) == BT_STATUS_SUCCESS) { + memcpy(bd_addr, p_link_up->bd_addr, sizeof(BD_ADDR)); + if (BTA_DmRemoveDevice(bd_addr, BT_TRANSPORT_BR_EDR) == BTA_SUCCESS) { + BTC_TRACE_EVENT(" %s() Bonding information removed.", __FUNCTION__); + } else { + BTC_TRACE_ERROR(" %s() BTA_DmRemoveDevice error", __FUNCTION__); + } + } else { + BTC_TRACE_ERROR(" %s() btc_storage_remove_bonded_device error", __FUNCTION__); + } + } +} + +static void btc_dm_auth_cmpl_evt (tBTA_DM_AUTH_CMPL *p_auth_cmpl) +{ + /* Save link key, if not temporary */ + bt_bdaddr_t bd_addr; + bt_status_t status; + BTC_TRACE_DEBUG("%s: bond state success %d, present %d, type%d\n", __func__, p_auth_cmpl->success, + p_auth_cmpl->key_present, p_auth_cmpl->key_type); + + bdcpy(bd_addr.address, p_auth_cmpl->bd_addr); + if ( (p_auth_cmpl->success == TRUE) && (p_auth_cmpl->key_present) ) { +#if 0 + if ((p_auth_cmpl->key_type < HCI_LKEY_TYPE_DEBUG_COMB) || + (p_auth_cmpl->key_type == HCI_LKEY_TYPE_AUTH_COMB) || + (p_auth_cmpl->key_type == HCI_LKEY_TYPE_CHANGED_COMB) || + (p_auth_cmpl->key_type == HCI_LKEY_TYPE_AUTH_COMB_P_256) + ) +#endif + if (1) { + bt_status_t ret __attribute__((unused)); + BTC_TRACE_DEBUG("%s: Storing link key. key_type=0x%x", + __FUNCTION__, p_auth_cmpl->key_type); + ret = btc_storage_add_bonded_device(&bd_addr, + p_auth_cmpl->key, p_auth_cmpl->key_type, + 16, p_auth_cmpl->sc_support); + BTC_ASSERTC(ret == BT_STATUS_SUCCESS, "storing link key failed", ret); + } else { + BTC_TRACE_DEBUG("%s: Temporary key. Not storing. key_type=0x%x", + __FUNCTION__, p_auth_cmpl->key_type); + } + } + + // Skip SDP for certain HID Devices + if (p_auth_cmpl->success) { + status = BT_STATUS_SUCCESS; + } else { + // Map the HCI fail reason to bt status + switch (p_auth_cmpl->fail_reason) { + case HCI_ERR_PAGE_TIMEOUT: + BTC_TRACE_WARNING("%s() - Pairing timeout; retrying () ...", __FUNCTION__); + return; + /* Fall-through */ + case HCI_ERR_CONNECTION_TOUT: + status = BT_STATUS_RMT_DEV_DOWN; + break; + + case HCI_ERR_PAIRING_NOT_ALLOWED: + status = BT_STATUS_AUTH_REJECTED; + break; + + case HCI_ERR_LMP_RESPONSE_TIMEOUT: + status = BT_STATUS_AUTH_FAILURE; + break; + + /* map the auth failure codes, so we can retry pairing if necessary */ + case HCI_ERR_AUTH_FAILURE: + case HCI_ERR_KEY_MISSING: + btc_storage_remove_bonded_device(&bd_addr); + case HCI_ERR_HOST_REJECT_SECURITY: + case HCI_ERR_ENCRY_MODE_NOT_ACCEPTABLE: + case HCI_ERR_UNIT_KEY_USED: + case HCI_ERR_PAIRING_WITH_UNIT_KEY_NOT_SUPPORTED: + case HCI_ERR_INSUFFCIENT_SECURITY: + case HCI_ERR_PEER_USER: + case HCI_ERR_UNSPECIFIED: + BTC_TRACE_ERROR(" %s() Authentication fail reason %d", + __FUNCTION__, p_auth_cmpl->fail_reason); + /* if autopair attempts are more than 1, or not attempted */ + status = BT_STATUS_AUTH_FAILURE; + break; + default: + status = BT_STATUS_FAIL; + break; + } + } +#if (BTC_GAP_BT_INCLUDED == TRUE) + esp_bt_gap_cb_param_t param; + bt_status_t ret; + btc_msg_t *msg; + + msg = (btc_msg_t *)osi_malloc(sizeof(btc_msg_t) + sizeof(esp_bt_gap_cb_param_t)); + if (msg == NULL) { + BTC_TRACE_ERROR("%s malloc fail", __func__); + return; + } + msg->sig = BTC_SIG_API_CB; + msg->pid = BTC_PID_GAP_BT; + msg->act = BTC_GAP_BT_AUTH_CMPL_EVT; + param.auth_cmpl.stat = status; + param.auth_cmpl.lk_type = p_auth_cmpl->key_type; + memcpy(param.auth_cmpl.bda, p_auth_cmpl->bd_addr, ESP_BD_ADDR_LEN); + memcpy(param.auth_cmpl.device_name, p_auth_cmpl->bd_name, ESP_BT_GAP_MAX_BDNAME_LEN + 1); + memcpy(msg->arg, ¶m, sizeof(esp_bt_gap_cb_param_t)); + + ret = btc_inter_profile_call(msg); + osi_free(msg); + + if (ret != BT_STATUS_SUCCESS) { + BTC_TRACE_ERROR("%s btc_inter_profile_call failed\n", __func__); + } + +#endif /// BTC_GAP_BT_INCLUDED == TRUE + (void) status; +} + +static void btc_dm_enc_chg_evt (tBTA_DM_ENC_CHG *p_enc_chg) +{ +#if (BTC_GAP_BT_INCLUDED == TRUE) + esp_bt_gap_cb_param_t param; + bt_status_t ret; + btc_msg_t *msg; + + msg = (btc_msg_t *)osi_malloc(sizeof(btc_msg_t) + sizeof(esp_bt_gap_cb_param_t)); + if (msg == NULL) { + BTC_TRACE_ERROR("%s malloc fail", __func__); + return; + } + msg->sig = BTC_SIG_API_CB; + msg->pid = BTC_PID_GAP_BT; + msg->act = BTC_GAP_BT_ENC_CHG_EVT; + param.enc_chg.enc_mode = p_enc_chg->enc_mode; + memcpy(param.enc_chg.bda, p_enc_chg->bd_addr, ESP_BD_ADDR_LEN); + memcpy(msg->arg, ¶m, sizeof(esp_bt_gap_cb_param_t)); + + ret = btc_inter_profile_call(msg); + osi_free(msg); + + if (ret != BT_STATUS_SUCCESS) { + BTC_TRACE_ERROR("%s btc_inter_profile_call failed\n", __func__); + } +#endif /// BTC_GAP_BT_INCLUDED == TRUE +} + +static void btc_dm_pin_req_evt(tBTA_DM_PIN_REQ *p_pin_req) +{ +#if (BTC_GAP_BT_INCLUDED == TRUE) + esp_bt_gap_cb_param_t param; + bt_status_t ret; + btc_msg_t *msg; + + msg = (btc_msg_t *)osi_malloc(sizeof(btc_msg_t) + sizeof(esp_bt_gap_cb_param_t)); + if (msg == NULL) { + BTC_TRACE_ERROR("%s malloc fail", __func__); + return; + } + msg->sig = BTC_SIG_API_CB; + msg->pid = BTC_PID_GAP_BT; + msg->act = BTC_GAP_BT_PIN_REQ_EVT; + param.pin_req.min_16_digit = p_pin_req->min_16_digit; + memcpy(param.pin_req.bda, p_pin_req->bd_addr, ESP_BD_ADDR_LEN); + memcpy(msg->arg, ¶m, sizeof(esp_bt_gap_cb_param_t)); + + ret = btc_inter_profile_call(msg); + osi_free(msg); + + if (ret != BT_STATUS_SUCCESS) { + BTC_TRACE_ERROR("%s btc_inter_profile_call failed\n", __func__); + } +#endif /// BTC_GAP_BT_INCLUDED == TRUE +} + +#if (CLASSIC_BT_INCLUDED == TRUE) +static void btc_dm_sp_cfm_req_evt(tBTA_DM_SP_CFM_REQ *p_cfm_req) +{ + if (p_cfm_req->just_works) { + // just work, not show to users. + BTA_DmConfirm(p_cfm_req->bd_addr, true); + return; + } + + esp_bt_gap_cb_param_t param; + bt_status_t ret; + btc_msg_t *msg; + + msg = (btc_msg_t *)osi_malloc(sizeof(btc_msg_t) + sizeof(esp_bt_gap_cb_param_t)); + if (msg == NULL) { + BTC_TRACE_ERROR("%s malloc fail", __func__); + return; + } + msg->sig = BTC_SIG_API_CB; + msg->pid = BTC_PID_GAP_BT; + msg->act = BTC_GAP_BT_CFM_REQ_EVT; + param.cfm_req.num_val = p_cfm_req->num_val; + memcpy(param.cfm_req.bda, p_cfm_req->bd_addr, ESP_BD_ADDR_LEN); + memcpy(msg->arg, ¶m, sizeof(esp_bt_gap_cb_param_t)); + + ret = btc_inter_profile_call(msg); + osi_free(msg); + + if (ret != BT_STATUS_SUCCESS) { + BTC_TRACE_ERROR("%s btc_inter_profile_call failed\n", __func__); + } +} + +static void btc_dm_sp_key_notif_evt(tBTA_DM_SP_KEY_NOTIF *p_key_notif) +{ + esp_bt_gap_cb_param_t param; + bt_status_t ret; + btc_msg_t *msg; + + msg = (btc_msg_t *)osi_malloc(sizeof(btc_msg_t) + sizeof(esp_bt_gap_cb_param_t)); + if (msg == NULL) { + BTC_TRACE_ERROR("%s malloc fail", __func__); + return; + } + msg->sig = BTC_SIG_API_CB; + msg->pid = BTC_PID_GAP_BT; + msg->act = BTC_GAP_BT_KEY_NOTIF_EVT; + param.key_notif.passkey = p_key_notif->passkey; + memcpy(param.key_notif.bda, p_key_notif->bd_addr, ESP_BD_ADDR_LEN); + memcpy(msg->arg, ¶m, sizeof(esp_bt_gap_cb_param_t)); + + ret = btc_inter_profile_call(msg); + osi_free(msg); + + if (ret != BT_STATUS_SUCCESS) { + BTC_TRACE_ERROR("%s btc_inter_profile_call failed\n", __func__); + } +} + +static void btc_dm_sp_key_req_evt(tBTA_DM_SP_KEY_REQ *p_key_req) +{ + esp_bt_gap_cb_param_t param; + bt_status_t ret; + btc_msg_t *msg; + + msg = (btc_msg_t *)osi_malloc(sizeof(btc_msg_t) + sizeof(esp_bt_gap_cb_param_t)); + if (msg == NULL) { + BTC_TRACE_ERROR("%s malloc fail", __func__); + return; + } + msg->sig = BTC_SIG_API_CB; + msg->pid = BTC_PID_GAP_BT; + msg->act = BTC_GAP_BT_KEY_REQ_EVT; + memcpy(param.key_req.bda, p_key_req->bd_addr, ESP_BD_ADDR_LEN); + memcpy(msg->arg, ¶m, sizeof(esp_bt_gap_cb_param_t)); + + ret = btc_inter_profile_call(msg); + osi_free(msg); + + if (ret != BT_STATUS_SUCCESS) { + BTC_TRACE_ERROR("%s btc_inter_profile_call failed\n", __func__); + } +} +#endif /// CLASSIC_BT_INCLUDED == TRUE + +static void btc_dm_dev_unpaired_evt(tBTA_DM_LINK_DOWN *p_link_down) +{ + esp_bt_gap_cb_param_t param; + BTC_TRACE_DEBUG("%s",__func__); + memcpy(param.remove_bond_dev_cmpl.bda, p_link_down->bd_addr, ESP_BD_ADDR_LEN); + btm_set_bond_type_dev(p_link_down->bd_addr, BOND_TYPE_UNKNOWN); + if (p_link_down->status == HCI_SUCCESS) { + //remove the bonded key in the config and nvs flash. + param.remove_bond_dev_cmpl.status = btc_storage_remove_bonded_device((bt_bdaddr_t *)param.remove_bond_dev_cmpl.bda); + } else { + param.remove_bond_dev_cmpl.status = ESP_BT_STATUS_FAIL; + } + +#if (BTC_GAP_BT_INCLUDED == TRUE) + bt_status_t ret; + btc_msg_t *msg; + + msg = (btc_msg_t *)osi_malloc(sizeof(btc_msg_t) + sizeof(esp_bt_gap_cb_param_t)); + if (msg == NULL) { + BTC_TRACE_ERROR("%s malloc fail", __func__); + return; + } + msg->sig = BTC_SIG_API_CB; + msg->pid = BTC_PID_GAP_BT; + msg->act = BTC_GAP_BT_REMOVE_BOND_DEV_COMPLETE_EVT; + memcpy(msg->arg, ¶m, sizeof(esp_bt_gap_cb_param_t)); + + ret = btc_inter_profile_call(msg); + osi_free(msg); + + if (ret != BT_STATUS_SUCCESS) { + BTC_TRACE_ERROR("%s btc_inter_profile_call failed\n", __func__); + } +#endif /// BTC_GAP_BT_INCLUDED == TRUE +} + + +#if (BTC_DM_PM_INCLUDED == TRUE) +static void btc_dm_pm_mode_chg_evt(tBTA_DM_MODE_CHG *p_mode_chg) +{ + esp_bt_gap_cb_param_t param; + bt_status_t ret; + btc_msg_t *msg; + + msg = (btc_msg_t *)osi_malloc(sizeof(btc_msg_t) + sizeof(esp_bt_gap_cb_param_t)); + if (msg == NULL) { + BTC_TRACE_ERROR("%s malloc fail", __func__); + return; + } + msg->sig = BTC_SIG_API_CB; + msg->pid = BTC_PID_GAP_BT; + msg->act = BTC_GAP_BT_MODE_CHG_EVT; + memcpy(param.mode_chg.bda, p_mode_chg->bd_addr, ESP_BD_ADDR_LEN); + param.mode_chg.mode = p_mode_chg->mode; + memcpy(msg->arg, ¶m, sizeof(esp_bt_gap_cb_param_t)); + + ret = btc_inter_profile_call(msg); + osi_free(msg); + + if (ret != BT_STATUS_SUCCESS) { + BTC_TRACE_ERROR("%s btc_inter_profile_call failed\n", __func__); + } +} +#endif /// BTC_DM_PM_INCLUDED == TRUE + +tBTA_SERVICE_MASK btc_get_enabled_services_mask(void) +{ + return btc_dm_cb.btc_enabled_services; +} + +void btc_clear_services_mask(void) +{ + btc_dm_cb.btc_enabled_services = 0; +} + +static bt_status_t btc_in_execute_service_request(tBTA_SERVICE_ID service_id, + BOOLEAN b_enable) +{ + BTC_TRACE_DEBUG("%s service_id: %d\n", __FUNCTION__, service_id); + /* Check the service_ID and invoke the profile's BT state changed API */ + switch (service_id) { +#if BTC_AV_INCLUDED + case BTA_A2DP_SOURCE_SERVICE_ID: + btc_av_source_execute_service(b_enable); + break; + case BTA_A2DP_SINK_SERVICE_ID: + btc_av_sink_execute_service(b_enable); + break; +#endif +#if BTC_HF_INCLUDED + case BTA_HFP_SERVICE_ID: + btc_hf_execute_service(b_enable); + break; +#endif /* #if BTC_HF_INCLUDED */ +#if BTC_HF_CLIENT_INCLUDED + case BTA_HFP_HS_SERVICE_ID: + btc_hf_client_execute_service(b_enable); + break; +#endif /* #if BTC_HF_CLIENT_INCLUDED */ + default: + BTC_TRACE_ERROR("%s: Unknown service being enabled\n", __FUNCTION__); + return BT_STATUS_FAIL; + } + return BT_STATUS_SUCCESS; +} + +void btc_dm_execute_service_request(BOOLEAN enable, char *p_param) +{ + btc_in_execute_service_request(*((tBTA_SERVICE_ID *)p_param), enable); +} + +bt_status_t btc_dm_enable_service(tBTA_SERVICE_ID service_id) +{ + tBTA_SERVICE_ID *p_id = &service_id; + + btc_dm_cb.btc_enabled_services |= (1 << service_id); + + BTC_TRACE_DEBUG("%s: current services:0x%x", __FUNCTION__, btc_dm_cb.btc_enabled_services); + + btc_dm_execute_service_request(TRUE, (char *)p_id); + + return BT_STATUS_SUCCESS; +} + +bt_status_t btc_dm_disable_service(tBTA_SERVICE_ID service_id) +{ + tBTA_SERVICE_ID *p_id = &service_id; + + btc_dm_cb.btc_enabled_services &= (tBTA_SERVICE_MASK)(~(1 << service_id)); + + BTC_TRACE_DEBUG("%s: Current Services:0x%x", __FUNCTION__, btc_dm_cb.btc_enabled_services); + + btc_dm_execute_service_request(FALSE, (char *)p_id); + + return BT_STATUS_SUCCESS; +} + +static void btc_dm_acl_link_stat(tBTA_DM_ACL_LINK_STAT *p_acl_link_stat) +{ +#if (BTC_GAP_BT_INCLUDED == TRUE) + esp_bt_gap_cb_param_t param; + esp_bt_gap_cb_event_t event = ESP_BT_GAP_EVT_MAX; + bt_bdaddr_t bt_addr; + + switch (p_acl_link_stat->event) { + case BTA_ACL_LINK_STAT_CONN_CMPL: { + event = ESP_BT_GAP_ACL_CONN_CMPL_STAT_EVT; + param.acl_conn_cmpl_stat.stat = p_acl_link_stat->link_act.conn_cmpl.status | ESP_BT_STATUS_BASE_FOR_HCI_ERR; + param.acl_conn_cmpl_stat.handle = p_acl_link_stat->link_act.conn_cmpl.handle; + memcpy(param.acl_conn_cmpl_stat.bda, p_acl_link_stat->link_act.conn_cmpl.bd_addr, ESP_BD_ADDR_LEN); + break; + } + case BTA_ACL_LINK_STAT_DISCONN_CMPL: { + event = ESP_BT_GAP_ACL_DISCONN_CMPL_STAT_EVT; + param.acl_disconn_cmpl_stat.reason = p_acl_link_stat->link_act.disconn_cmpl.reason | ESP_BT_STATUS_BASE_FOR_HCI_ERR; + param.acl_disconn_cmpl_stat.handle = p_acl_link_stat->link_act.disconn_cmpl.handle; + memcpy(param.acl_disconn_cmpl_stat.bda, p_acl_link_stat->link_act.disconn_cmpl.bd_addr, ESP_BD_ADDR_LEN); + break; + } + default: { + BTC_TRACE_WARNING("%s: invalid event %x", __FUNCTION__, event); + return; + } + } + +#if (SMP_INCLUDED == TRUE) + if (p_acl_link_stat->event == BTA_ACL_LINK_STAT_CONN_CMPL && + p_acl_link_stat->link_act.conn_cmpl.status == HCI_SUCCESS) { + memcpy(bt_addr.address, p_acl_link_stat->link_act.conn_cmpl.bd_addr, sizeof(bt_addr.address)); + if (btc_storage_update_active_device(&bt_addr)) { + BTC_TRACE_EVENT("Device: %02x:%02x:%02x:%02x:%02x:%02x, is not in bond list", + bt_addr.address[0], bt_addr.address[1], + bt_addr.address[2], bt_addr.address[3], + bt_addr.address[4], bt_addr.address[5]); + } + } +#endif ///SMP_INCLUDED == TRUE + esp_bt_gap_cb_t cb = (esp_bt_gap_cb_t)btc_profile_cb_get(BTC_PID_GAP_BT); + if (cb) { + cb(event, ¶m); + } +#endif +} + +void btc_dm_sec_cb_handler(btc_msg_t *msg) +{ + btc_dm_sec_args_t *arg = (btc_dm_sec_args_t *)(msg->arg); + tBTA_DM_SEC *p_data = &(arg->sec); + esp_ble_gap_cb_param_t param = {0}; + btc_msg_t *ble_msg; + bool rsp_app = false; + bt_status_t ret = BT_STATUS_SUCCESS; + + ble_msg = (btc_msg_t *)osi_malloc(sizeof(btc_msg_t) + sizeof(esp_ble_gap_cb_param_t)); + if (ble_msg == NULL) { + BTC_TRACE_ERROR("%s malloc fail", __func__); + btc_dm_sec_arg_deep_free(msg); + return; + } + ble_msg->sig = BTC_SIG_API_CB; + ble_msg->pid = BTC_PID_GAP_BLE; + // tBTA_SERVICE_MASK service_mask; + BTC_TRACE_DEBUG("btc_dm_upstreams_cback ev: %d\n", msg->act); + + switch (msg->act) { + case BTA_DM_ENABLE_EVT: { + btc_clear_services_mask(); +#if (SMP_INCLUDED == TRUE) +#if (BLE_INCLUDED == TRUE) + btc_storage_delete_duplicate_ble_devices(); +#endif ///BLE_INCLUDED == TRUE + //load the bonding device to the btm layer + btc_storage_load_bonded_devices(); +#endif ///SMP_INCLUDED == TRUE + + /* Set initial device name, it can be overwritten later */ + if (p_data->enable.status == BTA_SUCCESS) { + const char *initial_device_name = "ESP32"; + BTA_DmSetDeviceName(initial_device_name); + } + btc_enable_bluetooth_evt(p_data->enable.status); + break; + } + case BTA_DM_DISABLE_EVT: { + tBTA_SERVICE_MASK service_mask = btc_get_enabled_services_mask(); + for (int i = 0; i <= BTA_MAX_SERVICE_ID; i++) { + if (service_mask & + (tBTA_SERVICE_MASK)(BTA_SERVICE_ID_TO_SERVICE_MASK(i))) { + btc_in_execute_service_request(i, FALSE); + } + } + btc_disable_bluetooth_evt(); + break; + } + case BTA_DM_PIN_REQ_EVT: + BTC_TRACE_DEBUG("BTA_DM_PIN_REQ_EVT"); + btc_dm_pin_req_evt(&p_data->pin_req); + break; + case BTA_DM_AUTH_CMPL_EVT: + btc_dm_auth_cmpl_evt(&p_data->auth_cmpl); + break; + case BTA_DM_ENC_CHG_EVT: + btc_dm_enc_chg_evt(&p_data->enc_chg); + break; + case BTA_DM_BOND_CANCEL_CMPL_EVT: + BTC_TRACE_DEBUG("BTA_DM_BOND_CANCEL_CMPL_EVT"); + break; +#if (CLASSIC_BT_INCLUDED == TRUE) + case BTA_DM_SP_CFM_REQ_EVT: + btc_dm_sp_cfm_req_evt(&p_data->cfm_req); + break; + case BTA_DM_SP_KEY_NOTIF_EVT: + btc_dm_sp_key_notif_evt(&p_data->key_notif); + break; + case BTA_DM_SP_KEY_REQ_EVT: + btc_dm_sp_key_req_evt(&p_data->key_req); + break; + case BTA_DM_SP_KEYPRESS_EVT: + BTC_TRACE_DEBUG("BTA_DM_SP_KEYPRESS_EVT"); + break; +#endif /* (CLASSIC_BT_INCLUDED == TRUE) */ +#if BTM_OOB_INCLUDED == TRUE + case BTA_DM_SP_RMT_OOB_EVT: + BTC_TRACE_DEBUG("BTA_DM_SP_RMT_OOB_EVT"); + break; +#endif /* BTM_OOB_INCLUDED == TRUE */ + case BTA_DM_ACL_LINK_STAT_EVT: { + btc_dm_acl_link_stat(&p_data->acl_link_stat); + break; + } + case BTA_DM_DEV_UNPAIRED_EVT: { + btc_dm_dev_unpaired_evt(&p_data->link_down); + break; + } +#if (BLE_INCLUDED == TRUE) + case BTA_DM_BLE_DEV_UNPAIRED_EVT: { +#if (SMP_INCLUDED == TRUE) + bt_bdaddr_t bd_addr; + rsp_app = true; + BTC_TRACE_DEBUG("BTA_DM_BLE_DEV_UNPAIRED_EVT"); + memcpy(bd_addr.address, p_data->link_down.bd_addr, sizeof(BD_ADDR)); + btm_set_bond_type_dev(p_data->link_down.bd_addr, BOND_TYPE_UNKNOWN); + param.remove_bond_dev_cmpl.status = ESP_BT_STATUS_FAIL; + + if (p_data->link_down.status == HCI_SUCCESS) { + //remove the bonded key in the config and nvs flash. + btc_storage_remove_gatt_cl_supp_feat(&bd_addr); + btc_storage_remove_gatt_db_hash(&bd_addr); + btc_storage_remove_ble_dev_type(&bd_addr, false); + btc_storage_remove_remote_addr_type(&bd_addr, false); + btc_storage_remove_ble_dev_auth_mode(&bd_addr, false); + param.remove_bond_dev_cmpl.status = btc_storage_remove_ble_bonding_keys(&bd_addr); + } + ble_msg->act = ESP_GAP_BLE_REMOVE_BOND_DEV_COMPLETE_EVT; + memcpy(param.remove_bond_dev_cmpl.bd_addr, p_data->link_down.bd_addr, sizeof(BD_ADDR)); +#endif /* #if (SMP_INCLUDED == TRUE) */ + break; + } +#endif ///BLE_INCLUDED == TRUE + case BTA_DM_BUSY_LEVEL_EVT: +#if (BTC_GAP_BT_INCLUDED == TRUE) + { + if (p_data->busy_level.level_flags & BTM_BL_INQUIRY_PAGING_MASK) { + btc_gap_bt_busy_level_updated(p_data->busy_level.level_flags); + } + break; + } +#endif /* BTC_GAP_BT_INCLUDED == TRUE */ + case BTA_DM_LINK_DOWN_EVT: + case BTA_DM_HW_ERROR_EVT: + BTC_TRACE_DEBUG( "btc_dm_sec_cback : unhandled event (%d)\n", msg->act ); + break; + case BTA_DM_LINK_UP_EVT: + btc_dm_link_up_evt(&p_data->link_up); + break; +#if ((BLE_INCLUDED == TRUE) && (SMP_INCLUDED == TRUE)) + case BTA_DM_BLE_AUTH_CMPL_EVT: { + rsp_app = true; + ble_msg->act = ESP_GAP_BLE_AUTH_CMPL_EVT; + param.ble_security.auth_cmpl.addr_type = p_data->auth_cmpl.addr_type; + param.ble_security.auth_cmpl.dev_type = p_data->auth_cmpl.dev_type; + param.ble_security.auth_cmpl.key_type = p_data->auth_cmpl.key_type; + param.ble_security.auth_cmpl.fail_reason = p_data->auth_cmpl.fail_reason; + param.ble_security.auth_cmpl.success = p_data->auth_cmpl.success ? true : false; + param.ble_security.auth_cmpl.key_present = p_data->auth_cmpl.key_present; + memcpy(param.ble_security.auth_cmpl.bd_addr, p_data->auth_cmpl.bd_addr, sizeof(BD_ADDR)); + memcpy(param.ble_security.auth_cmpl.key, p_data->auth_cmpl.key, sizeof(LINK_KEY)); + param.ble_security.auth_cmpl.auth_mode = p_data->auth_cmpl.auth_mode; + btc_dm_ble_auth_cmpl_evt(&p_data->auth_cmpl); + break; + } + case BTA_DM_BLE_KEY_EVT: { + rsp_app = true; + ble_msg->act = ESP_GAP_BLE_KEY_EVT; + param.ble_security.ble_key.key_type = p_data->ble_key.key_type; + memcpy(param.ble_security.ble_key.bd_addr, p_data->ble_key.bd_addr, BD_ADDR_LEN); + switch (p_data->ble_key.key_type) { + case BTM_LE_KEY_PENC: { + BTC_TRACE_DEBUG("Rcv BTA_LE_KEY_PENC"); + btc_dm_cb.pairing_cb.ble.is_penc_key_rcvd = TRUE; + btc_dm_cb.pairing_cb.ble.penc_key = p_data->ble_key.p_key_value->penc_key; + memcpy(&btc_dm_cb.pairing_cb.ble.penc_key, &p_data->ble_key.p_key_value->penc_key, + sizeof(tBTM_LE_PENC_KEYS)); + memcpy(¶m.ble_security.ble_key.p_key_value.penc_key, + &p_data->ble_key.p_key_value->penc_key, sizeof(tBTM_LE_PENC_KEYS)); + break; + } + case BTM_LE_KEY_PID: { + BTC_TRACE_DEBUG("Rcv BTA_LE_KEY_PID"); + btc_dm_cb.pairing_cb.ble.is_pid_key_rcvd = TRUE; + memcpy(&btc_dm_cb.pairing_cb.ble.pid_key, &p_data->ble_key.p_key_value->pid_key, + sizeof(tBTM_LE_PID_KEYS)); + //Note: The memory size of the addr_type in ble_security.ble_key.p_key_value.pid_key is different from that of p_data->ble_key.p_key_value->pid_key. + memcpy(¶m.ble_security.ble_key.p_key_value.pid_key.irk, + &p_data->ble_key.p_key_value->pid_key.irk, ESP_BT_OCTET16_LEN); + param.ble_security.ble_key.p_key_value.pid_key.addr_type = p_data->ble_key.p_key_value->pid_key.addr_type; + memcpy(¶m.ble_security.ble_key.p_key_value.pid_key.static_addr, + &p_data->ble_key.p_key_value->pid_key.static_addr, ESP_BD_ADDR_LEN); + break; + } + case BTM_LE_KEY_PCSRK: { + BTC_TRACE_DEBUG("Rcv BTA_LE_KEY_PCSRK"); + btc_dm_cb.pairing_cb.ble.is_pcsrk_key_rcvd = TRUE; + memcpy(&btc_dm_cb.pairing_cb.ble.pcsrk_key, &p_data->ble_key.p_key_value->pcsrk_key, + sizeof(tBTM_LE_PCSRK_KEYS)); + memcpy(¶m.ble_security.ble_key.p_key_value.pcsrk_key, + &p_data->ble_key.p_key_value->pcsrk_key, sizeof(tBTM_LE_PCSRK_KEYS)); + break; + } + case BTM_LE_KEY_LENC: { + BTC_TRACE_DEBUG("Rcv BTA_LE_KEY_LENC"); + btc_dm_cb.pairing_cb.ble.is_lenc_key_rcvd = TRUE; + memcpy(&btc_dm_cb.pairing_cb.ble.lenc_key, &p_data->ble_key.p_key_value->lenc_key, + sizeof(tBTM_LE_LENC_KEYS)); + memcpy(¶m.ble_security.ble_key.p_key_value.lenc_key, + &p_data->ble_key.p_key_value->lenc_key, sizeof(tBTM_LE_LENC_KEYS)); + break; + } + case BTM_LE_KEY_LCSRK: { + BTC_TRACE_DEBUG("Rcv BTA_LE_KEY_LCSRK"); + btc_dm_cb.pairing_cb.ble.is_lcsrk_key_rcvd = TRUE; + memcpy(&btc_dm_cb.pairing_cb.ble.lcsrk_key, &p_data->ble_key.p_key_value->lcsrk_key, + sizeof(tBTM_LE_LCSRK_KEYS)); + memcpy(¶m.ble_security.ble_key.p_key_value.lcsrk_key, + &p_data->ble_key.p_key_value->lcsrk_key, sizeof(tBTM_LE_LCSRK_KEYS)); + break; + } + case BTM_LE_KEY_LID: { + BTC_TRACE_DEBUG("Rcv BTA_LE_KEY_LID"); + btc_dm_cb.pairing_cb.ble.is_lidk_key_rcvd = TRUE; + break; + } + default: + break; + } + + break; + } + case BTA_DM_BLE_SEC_REQ_EVT: { + rsp_app = true; + ble_msg->act = ESP_GAP_BLE_SEC_REQ_EVT; + memcpy(param.ble_security.ble_req.bd_addr, p_data->ble_req.bd_addr, BD_ADDR_LEN); + break; + } + case BTA_DM_BLE_PASSKEY_NOTIF_EVT: { + rsp_app = true; + ble_msg->act = ESP_GAP_BLE_PASSKEY_NOTIF_EVT; + param.ble_security.key_notif.passkey = p_data->key_notif.passkey; + memcpy(param.ble_security.key_notif.bd_addr, p_data->ble_req.bd_addr, BD_ADDR_LEN); + break; + } + case BTA_DM_BLE_PASSKEY_REQ_EVT: { + rsp_app = true; + ble_msg->act = ESP_GAP_BLE_PASSKEY_REQ_EVT; + memcpy(param.ble_security.ble_req.bd_addr, p_data->ble_req.bd_addr, BD_ADDR_LEN); + break; + } + case BTA_DM_BLE_OOB_REQ_EVT: { + rsp_app = true; + ble_msg->act = ESP_GAP_BLE_OOB_REQ_EVT; + memcpy(param.ble_security.ble_req.bd_addr, p_data->ble_req.bd_addr, BD_ADDR_LEN); + break; + } + case BTA_DM_BLE_SC_OOB_REQ_EVT: { + rsp_app = true; + ble_msg->act = ESP_GAP_BLE_SC_OOB_REQ_EVT; + memcpy(param.ble_security.ble_req.bd_addr, p_data->ble_req.bd_addr, BD_ADDR_LEN); + break; + } + case BTA_DM_BLE_SC_CR_LOC_OOB_EVT: { + rsp_app = true; + ble_msg->act = ESP_GAP_BLE_SC_CR_LOC_OOB_EVT; + memcpy(param.ble_security.oob_data.oob_c, p_data->local_oob_data.local_oob_c, BT_OCTET16_LEN); + memcpy(param.ble_security.oob_data.oob_r, p_data->local_oob_data.local_oob_r, BT_OCTET16_LEN); + break; + } + case BTA_DM_BLE_LOCAL_IR_EVT: { + rsp_app = true; + ble_msg->act = ESP_GAP_BLE_LOCAL_IR_EVT; + memcpy(¶m.ble_security.ble_id_keys, &p_data->ble_id_keys, sizeof(tBTA_BLE_LOCAL_ID_KEYS)); + BTC_TRACE_DEBUG("BTA_DM_BLE_LOCAL_IR_EVT. "); + btc_dm_cb.ble_local_key_cb.is_id_keys_rcvd = TRUE; + memcpy(&btc_dm_cb.ble_local_key_cb.id_keys.irk[0], + &p_data->ble_id_keys.irk[0], sizeof(BT_OCTET16)); + memcpy(&btc_dm_cb.ble_local_key_cb.id_keys.ir[0], + &p_data->ble_id_keys.ir[0], sizeof(BT_OCTET16)); + memcpy(&btc_dm_cb.ble_local_key_cb.id_keys.dhk[0], + &p_data->ble_id_keys.dhk[0], sizeof(BT_OCTET16)); + btc_storage_add_ble_local_key( (char *)&btc_dm_cb.ble_local_key_cb.id_keys.irk[0], + BTC_LE_LOCAL_KEY_IRK, + BT_OCTET16_LEN); + btc_storage_add_ble_local_key( (char *)&btc_dm_cb.ble_local_key_cb.id_keys.ir[0], + BTC_LE_LOCAL_KEY_IR, + BT_OCTET16_LEN); + btc_storage_add_ble_local_key( (char *)&btc_dm_cb.ble_local_key_cb.id_keys.dhk[0], + BTC_LE_LOCAL_KEY_DHK, + BT_OCTET16_LEN); + break; + } + case BTA_DM_BLE_LOCAL_ER_EVT: { + rsp_app = true; + ble_msg->act = ESP_GAP_BLE_LOCAL_ER_EVT; + memcpy(¶m.ble_security.ble_id_keys, &p_data->ble_id_keys, sizeof(tBTA_BLE_LOCAL_ID_KEYS)); + BTC_TRACE_DEBUG("BTA_DM_BLE_LOCAL_ER_EVT. "); + btc_dm_cb.ble_local_key_cb.is_er_rcvd = TRUE; + memcpy(&btc_dm_cb.ble_local_key_cb.er[0], &p_data->ble_er[0], sizeof(BT_OCTET16)); + btc_storage_add_ble_local_key( (char *)&btc_dm_cb.ble_local_key_cb.er[0], + BTC_LE_LOCAL_KEY_ER, + BT_OCTET16_LEN); + break; + } + case BTA_DM_BLE_NC_REQ_EVT: { + rsp_app = true; + ble_msg->act = ESP_GAP_BLE_NC_REQ_EVT; + memcpy(param.ble_security.key_notif.bd_addr, p_data->key_notif.bd_addr, BD_ADDR_LEN); + param.ble_security.key_notif.passkey = p_data->key_notif.passkey; + break; + } +#endif + +#if (BTC_DM_PM_INCLUDED == TRUE) + case BTA_DM_PM_MODE_CHG_EVT: + BTC_TRACE_DEBUG("BTA_DM_PM_MODE_CHG_EVT mode:%d", p_data->mode_chg.mode); + btc_dm_pm_mode_chg_evt(&p_data->mode_chg); + break; +#endif /// BTA_DM_PM_INCLUDED == TRUE + + case BTA_DM_AUTHORIZE_EVT: + case BTA_DM_SIG_STRENGTH_EVT: + case BTA_DM_ROLE_CHG_EVT: + BTC_TRACE_DEBUG( "btc_dm_sec_cback : unhandled event (%d)\n", msg->act ); + break; + default: + BTC_TRACE_DEBUG( "btc_dm_sec_cback : unhandled event (%d)\n", msg->act ); + break; + } + + if (rsp_app) { + memcpy(ble_msg->arg, ¶m, sizeof(esp_ble_gap_cb_param_t)); + ret = btc_inter_profile_call(ble_msg); + + if (ret != BT_STATUS_SUCCESS) { + BTC_TRACE_ERROR("%s btc_inter_profile_call failed\n", __func__); + } + } + osi_free(ble_msg); + btc_dm_sec_arg_deep_free(msg); +} diff --git a/lib/bt/host/bluedroid/btc/core/btc_main.c b/lib/bt/host/bluedroid/btc/core/btc_main.c new file mode 100644 index 00000000..0b9b016d --- /dev/null +++ b/lib/bt/host/bluedroid/btc/core/btc_main.c @@ -0,0 +1,192 @@ +/* + * SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "btc/btc_task.h" +#include "btc/btc_main.h" +#include "btc/btc_dm.h" +#include "osi/future.h" +#include "esp_err.h" +#include "btc/btc_config.h" +#include "osi/alarm.h" +#include "btc/btc_ble_storage.h" +#include "btc_gap_ble.h" +#include "bta_gattc_int.h" +#include "bta_gatts_int.h" +#include "bta_dm_int.h" + +static future_t *main_future[BTC_MAIN_FUTURE_NUM]; + +extern int bte_main_boot_entry(void *cb); +extern int bte_main_shutdown(void); + +future_t **btc_main_get_future_p(btc_main_future_type_t type) +{ + return &main_future[type]; +} + +static void btc_enable_bluetooth(void) +{ + if (BTA_EnableBluetooth(btc_dm_sec_evt) != BTA_SUCCESS) { + future_ready(*btc_main_get_future_p(BTC_MAIN_ENABLE_FUTURE), FUTURE_FAIL); + } +} + +static void btc_disable_bluetooth(void) +{ +#if (SMP_INCLUDED) + btc_config_shut_down(); +#endif + if (BTA_DisableBluetooth() != BTA_SUCCESS) { + future_ready(*btc_main_get_future_p(BTC_MAIN_DISABLE_FUTURE), FUTURE_FAIL); + } +} + +void btc_init_callback(void) +{ + future_ready(*btc_main_get_future_p(BTC_MAIN_INIT_FUTURE), FUTURE_SUCCESS); +} + +static void btc_init_bluetooth(void) +{ + osi_alarm_create_mux(); + osi_alarm_init(); + bte_main_boot_entry(btc_init_callback); +#if (SMP_INCLUDED) + btc_config_init(); + +#if (BLE_INCLUDED == TRUE) + //load the ble local key which has been stored in the flash + btc_dm_load_ble_local_keys(); +#endif ///BLE_INCLUDED == TRUE +#endif /* #if (SMP_INCLUDED) */ +#if BTA_DYNAMIC_MEMORY + deinit_semaphore = xSemaphoreCreateBinary(); +#endif /* #if BTA_DYNAMIC_MEMORY */ +} + + +static void btc_deinit_bluetooth(void) +{ + /* Wait for the disable operation to complete */ +#if BTA_DYNAMIC_MEMORY + xSemaphoreTake(deinit_semaphore, BTA_DISABLE_DELAY / portTICK_PERIOD_MS); +#endif /* #if BTA_DYNAMIC_MEMORY */ + bta_dm_sm_deinit(); +#if (GATTC_INCLUDED) + bta_gattc_deinit(); +#endif /* #if (GATTC_INCLUDED) */ +#if (GATTS_INCLUDED) + bta_gatts_deinit(); +#endif /* #if (GATTS_INCLUDED) */ + bte_main_shutdown(); +#if (SMP_INCLUDED) + btc_config_clean_up(); +#endif + osi_alarm_deinit(); + osi_alarm_delete_mux(); + future_ready(*btc_main_get_future_p(BTC_MAIN_DEINIT_FUTURE), FUTURE_SUCCESS); +#if BTA_DYNAMIC_MEMORY + vSemaphoreDelete(deinit_semaphore); + deinit_semaphore = NULL; +#endif /* #if BTA_DYNAMIC_MEMORY */ +} + +void btc_main_call_handler(btc_msg_t *msg) +{ + BTC_TRACE_DEBUG("%s act %d\n", __func__, msg->act); + + switch (msg->act) { + case BTC_MAIN_ACT_INIT: + btc_init_bluetooth(); + break; + case BTC_MAIN_ACT_DEINIT: + btc_deinit_bluetooth(); + break; + case BTC_MAIN_ACT_ENABLE: + btc_enable_bluetooth(); + break; + case BTC_MAIN_ACT_DISABLE: + btc_disable_bluetooth(); + break; + default: + BTC_TRACE_ERROR("%s UNKNOWN ACT %d\n", __func__, msg->act); + break; + } +} + +uint32_t btc_get_ble_status(void) +{ + uint32_t status = BTC_BLE_STATUS_IDLE; + + #if (BLE_INCLUDED == TRUE) + // Number of active advertising + extern uint8_t btm_ble_adv_active_count(void); + if (btm_ble_adv_active_count()) { + status |= BIT(BTC_BLE_STATUS_ADV); + } + + // Number of active scanning + extern uint8_t btm_ble_scan_active_count(void); + if (btm_ble_scan_active_count()) { + status |= BIT(BTC_BLE_STATUS_SCAN); + } + + // Number of active GATT tcb + extern uint8_t gatt_tcb_active_count(void); + if (gatt_tcb_active_count()) { + status |= BIT(BTC_BLE_STATUS_CONN); + } + + #if (SMP_INCLUDED == TRUE) + // Number of saved bonded devices + if (btc_storage_get_num_ble_bond_devices()) { + status |= BIT(BTC_BLE_STATUS_BOND); + } + #endif + #endif + + // Number of recorded devices + extern uint8_t btdm_sec_dev_active_count(void); + if (btdm_sec_dev_active_count()) { + status |= BIT(BTC_BLE_STATUS_DEV); + } + + // Number of active ACL connection + extern uint8_t btm_acl_active_count(void); + if (btm_acl_active_count()) { + status |= BIT(BTC_BLE_STATUS_CONN); + } + + // Number of active L2C plcb + extern uint8_t l2cu_plcb_active_count(void); + if (l2cu_plcb_active_count()) { + status |= BIT(BTC_BLE_STATUS_CONN); + } + + #if (GATTC_INCLUDED == TRUE) + // Number of registered GATTC APP + extern uint8_t bta_gattc_cl_rcb_active_count(void); + if (bta_gattc_cl_rcb_active_count()) { + status |= BIT(BTC_BLE_STATUS_GATTC_APP); + } + + // Number of saved GATTC cache + extern UINT8 bta_gattc_co_get_addr_num(void); + if (bta_gattc_co_get_addr_num()) { + status |= BIT(BTC_BLE_STATUS_GATTC_CACHE); + } + #endif + + #if (GATTS_INCLUDED == TRUE) + // Number of registered GATTS service + extern uint8_t bta_gatts_srvc_active_count(void); + if (bta_gatts_srvc_active_count()) { + status |= BIT(BTC_BLE_STATUS_GATTS_SRVC); + } + #endif + + return status; +} diff --git a/lib/bt/host/bluedroid/btc/core/btc_profile_queue.c b/lib/bt/host/bluedroid/btc/core/btc_profile_queue.c new file mode 100644 index 00000000..4f359030 --- /dev/null +++ b/lib/bt/host/bluedroid/btc/core/btc_profile_queue.c @@ -0,0 +1,163 @@ +/* + * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "common/bt_target.h" +#include +#include "esp_bt_main.h" +#include "common/bt_trace.h" +#include "common/bt_defs.h" +#include "btc/btc_profile_queue.h" +#include "osi/list.h" +#include "osi/allocator.h" + +#if BTC_PRF_QUEUE_INCLUDED +/******************************************************************************* +** Local type definitions +*******************************************************************************/ +/******************************************************************************* +** Static variables +*******************************************************************************/ + +static list_t *connect_queue; + +static const size_t MAX_REASONABLE_REQUESTS = 10; + +/******************************************************************************* +** Queue helper functions +*******************************************************************************/ + +static void queue_int_add(connect_node_t *p_param) +{ + if (!connect_queue) { + connect_queue = list_new(osi_free_func); + assert(connect_queue != NULL); + } + + // Sanity check to make sure we're not leaking connection requests + assert(list_length(connect_queue) < MAX_REASONABLE_REQUESTS); + + for (const list_node_t *node = list_begin(connect_queue); node != list_end(connect_queue); node = list_next(node)) { + if (((connect_node_t *)list_node(node))->uuid == p_param->uuid) { + BTC_TRACE_DEBUG("%s dropping duplicate connect request for uuid: %04x", __func__, p_param->uuid); + return; + } + } + + connect_node_t *p_node = osi_malloc(sizeof(connect_node_t)); + assert(p_node != NULL); + memcpy(p_node, p_param, sizeof(connect_node_t)); + list_append(connect_queue, p_node); +} + +static void queue_int_advance(void) +{ + if (connect_queue && !list_is_empty(connect_queue)) { + list_remove(connect_queue, list_front(connect_queue)); + } +} + +void btc_profile_queue_handler(btc_msg_t *msg) +{ + btc_prf_que_args_t *arg = (btc_prf_que_args_t *)(msg->arg); + switch (msg->act) { + case BTC_PRF_QUE_CONNECT: + queue_int_add(&(arg->connect_node)); + break; + + case BTC_PRF_QUE_ADVANCE: + queue_int_advance(); + break; + } + + if (esp_bluedroid_get_status() == ESP_BLUEDROID_STATUS_ENABLED) { + btc_queue_connect_next(); + } +} + +/******************************************************************************* +** +** Function btc_queue_connect +** +** Description Add a new connection to the queue and trigger the next +** scheduled connection. +** +** Returns BT_STATUS_SUCCESS if successful +** +*******************************************************************************/ +bt_status_t btc_queue_connect(uint16_t uuid, const bt_bdaddr_t *bda, btc_connect_cb_t connect_cb) +{ + btc_msg_t msg; + btc_prf_que_args_t arg; + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_PRF_QUE; + msg.act = BTC_PRF_QUE_CONNECT; + + memset(&arg, 0, sizeof(btc_prf_que_args_t)); + memcpy(&arg.connect_node.bda, bda, sizeof(bt_bdaddr_t)); + arg.connect_node.uuid = uuid; + arg.connect_node.connect_cb = connect_cb; + + return btc_transfer_context(&msg, &arg, sizeof(btc_prf_que_args_t), NULL, NULL); +} +/******************************************************************************* +** +** Function btc_queue_advance +** +** Description Clear the queue's busy status and advance to the next +** scheduled connection. +** +** Returns void +** +*******************************************************************************/ +void btc_queue_advance(void) +{ + btc_msg_t msg; + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_PRF_QUE; + msg.act = BTC_PRF_QUE_ADVANCE; + + btc_transfer_context(&msg, NULL, 0, NULL, NULL); +} + +// This function dispatches the next pending connect request. It is called from +// stack_manager when the stack comes up. +bt_status_t btc_queue_connect_next(void) +{ + if (!connect_queue || list_is_empty(connect_queue)) { + return BT_STATUS_FAIL; + } + + connect_node_t *p_head = list_front(connect_queue); + + // If the queue is currently busy, we return success anyway, + // since the connection has been queued... + if (p_head->busy) { + return BT_STATUS_SUCCESS; + } + + p_head->busy = true; + return p_head->connect_cb(&p_head->bda, p_head->uuid); +} + + +/******************************************************************************* +** +** Function btc_queue_release +** +** Description Free up all the queue nodes and set the queue head to NULL +** +** Returns void +** +*******************************************************************************/ +void btc_queue_release(void) +{ + list_free(connect_queue); + connect_queue = NULL; +} + +#endif /* if BTC_PRF_QUEUE_INCLUDED */ diff --git a/lib/bt/host/bluedroid/btc/core/btc_sec.c b/lib/bt/host/bluedroid/btc/core/btc_sec.c new file mode 100644 index 00000000..e69de29b diff --git a/lib/bt/host/bluedroid/btc/core/btc_sm.c b/lib/bt/host/bluedroid/btc/core/btc_sm.c new file mode 100644 index 00000000..1c113967 --- /dev/null +++ b/lib/bt/host/bluedroid/btc/core/btc_sm.c @@ -0,0 +1,191 @@ +/* + * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + + +/***************************************************************************** + * + * Filename: btc_sm.c + * + * Description: Generic BTC state machine API + * + *****************************************************************************/ +#include "common/bt_target.h" +#include "common/bt_defs.h" +#include "osi/allocator.h" +#include "btc/btc_common.h" +#include "btc/btc_sm.h" + +#if BTC_SM_INCLUDED +/***************************************************************************** +** Constants & Macros +******************************************************************************/ +/***************************************************************************** +** Local type definitions +******************************************************************************/ +typedef struct { + btc_sm_state_t state; + btc_sm_handler_t *p_handlers; +} btc_sm_cb_t; + +/***************************************************************************** +** Static variables +******************************************************************************/ + +/***************************************************************************** +** Static functions +******************************************************************************/ + +/***************************************************************************** +** Externs +******************************************************************************/ + +/***************************************************************************** +** Functions +******************************************************************************/ + +/***************************************************************************** +** +** Function btc_sm_init +** +** Description Initializes the state machine with the state handlers +** The caller should ensure that the table and the corresponding +** states match. The location that 'p_handlers' points to shall +** be available until the btc_sm_shutdown API is invoked. +** +** Returns Returns a pointer to the initialized state machine handle. +** +******************************************************************************/ + +btc_sm_handle_t btc_sm_init(const btc_sm_handler_t *p_handlers, btc_sm_state_t initial_state) +{ + btc_sm_cb_t *p_cb; + + if (p_handlers == NULL) { + BTC_TRACE_ERROR("%s : p_handlers is NULL", __FUNCTION__); + return NULL; + } + + p_cb = (btc_sm_cb_t *)osi_malloc(sizeof(btc_sm_cb_t)); + p_cb->state = initial_state; + p_cb->p_handlers = (btc_sm_handler_t *)p_handlers; + + /* Send BTC_SM_ENTER_EVT to the initial state */ + p_cb->p_handlers[initial_state](BTC_SM_ENTER_EVT, NULL); + + return (btc_sm_handle_t)p_cb; +} + +/***************************************************************************** +** +** Function btc_sm_shutdown +** +** Description Tears down the state machine +** +** Returns None +** +******************************************************************************/ +void btc_sm_shutdown(btc_sm_handle_t handle) +{ + btc_sm_cb_t *p_cb = (btc_sm_cb_t *)handle; + + if (p_cb == NULL) { + BTC_TRACE_ERROR("%s : Invalid handle", __FUNCTION__); + return; + } + osi_free(p_cb); +} + +/***************************************************************************** +** +** Function btc_sm_get_state +** +** Description Fetches the current state of the state machine +** +** Returns Current state +** +******************************************************************************/ +btc_sm_state_t btc_sm_get_state(btc_sm_handle_t handle) +{ + btc_sm_cb_t *p_cb = (btc_sm_cb_t *)handle; + + if (p_cb == NULL) { + BTC_TRACE_ERROR("%s : Invalid handle", __FUNCTION__); + return 0; + } + + return p_cb->state; +} + +/***************************************************************************** +** +** Function btc_sm_dispatch +** +** Description Dispatches the 'event' along with 'data' to the current state handler +** +** Returns BT_STATUS_SUCCESS on success +** BT_STATUS_UNHANDLED if event was not processed +** BT_STATUS_FAIL otherwise +** +******************************************************************************/ +bt_status_t btc_sm_dispatch(btc_sm_handle_t handle, btc_sm_event_t event, + void *data) +{ + bt_status_t status = BT_STATUS_SUCCESS; + + btc_sm_cb_t *p_cb = (btc_sm_cb_t *)handle; + + if (p_cb == NULL) { + BTC_TRACE_ERROR("%s : Invalid handle", __FUNCTION__); + return BT_STATUS_FAIL; + } + + if (p_cb->p_handlers[p_cb->state](event, data) == FALSE) { + return BT_STATUS_UNHANDLED; + } + + return status; +} + +/***************************************************************************** +** +** Function btc_sm_change_state +** +** Description Make a transition to the new 'state'. The 'BTC_SM_EXIT_EVT' +** shall be invoked before exiting the current state. The +** 'BTC_SM_ENTER_EVT' shall be invoked before entering the new state +** +** Returns BT_STATUS_SUCCESS on success +** BT_STATUS_UNHANDLED if event was not processed +** BT_STATUS_FAIL otherwise +** +******************************************************************************/ +bt_status_t btc_sm_change_state(btc_sm_handle_t handle, btc_sm_state_t state) +{ + bt_status_t status = BT_STATUS_SUCCESS; + btc_sm_cb_t *p_cb = (btc_sm_cb_t *)handle; + + if (p_cb == NULL) { + BTC_TRACE_ERROR("%s : Invalid handle", __FUNCTION__); + return BT_STATUS_FAIL; + } + + /* Send exit event to the current state */ + if (p_cb->p_handlers[p_cb->state](BTC_SM_EXIT_EVT, NULL) == FALSE) { + status = BT_STATUS_UNHANDLED; + } + + /* Change to the new state */ + p_cb->state = state; + + /* Send enter event to the new state */ + if (p_cb->p_handlers[p_cb->state](BTC_SM_ENTER_EVT, NULL) == FALSE) { + status = BT_STATUS_UNHANDLED; + } + + return status; +} + +#endif /* #if BTC_SM_INCLUDED */ diff --git a/lib/bt/host/bluedroid/btc/core/btc_storage.c b/lib/bt/host/bluedroid/btc/core/btc_storage.c new file mode 100644 index 00000000..1923dea0 --- /dev/null +++ b/lib/bt/host/bluedroid/btc/core/btc_storage.c @@ -0,0 +1,603 @@ +/* + * SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include + +#include "btc/btc_storage.h" +#include "btc/btc_ble_storage.h" +#include "btc/btc_util.h" +#include "osi/osi.h" +#include "osi/allocator.h" +#include "common/bt_trace.h" +#include "esp_system.h" +#include "bta/bta_api.h" +#include "device/bdaddr.h" +#include "btc/btc_config.h" +#include "btc_hh.h" + +/******************************************************************************* +** +** Function btc_storage_add_bonded_device +** +** Description BTIF storage API - Adds the newly bonded device to NVRAM +** along with the link-key, Key type and Pin key length +** +** Returns BT_STATUS_SUCCESS if the store was successful, +** BT_STATUS_FAIL otherwise +** +*******************************************************************************/ + +bt_status_t btc_storage_add_bonded_device(bt_bdaddr_t *remote_bd_addr, + LINK_KEY link_key, + uint8_t key_type, + uint8_t pin_length, + BOOLEAN sc_support) +{ + bdstr_t bdstr; + bt_bdaddr_t bd_addr; + bdaddr_to_string(remote_bd_addr, bdstr, sizeof(bdstr)); + + /* device not in bond list and exceed the maximum number of bonded devices, delete the inactive bonded device */ + if (btc_storage_get_num_all_bond_devices() >= BTM_SEC_MAX_DEVICE_RECORDS && !btc_config_has_section(bdstr)) { + const btc_config_section_iter_t *iter = btc_config_section_begin(); + const btc_config_section_iter_t *remove_iter = iter; + /* find the first device(the last node) */ + while (iter != btc_config_section_end()) { + remove_iter = iter; + iter = btc_config_section_next(iter); + } + const char *remove_section = btc_config_section_name(remove_iter); + + // delete device info + string_to_bdaddr(remove_section, &bd_addr); + BTA_DmRemoveDevice(bd_addr.address, BT_TRANSPORT_BR_EDR); + BTA_DmRemoveDevice(bd_addr.address, BT_TRANSPORT_LE); + + // delete config info + if (btc_config_remove_section(remove_section)) { + BTC_TRACE_WARNING("exceeded the maximum nubmer of bonded devices, delete the first device info : %02x:%02x:%02x:%02x:%02x:%02x", + bd_addr.address[0], bd_addr.address[1], bd_addr.address[2], bd_addr.address[3], bd_addr.address[4], bd_addr.address[5]); + } + } + + BTC_TRACE_DEBUG("add to storage: Remote device:%s\n", bdstr); + + btc_config_lock(); + int ret = btc_config_set_int(bdstr, BTC_STORAGE_LINK_KEY_TYPE_STR, (int)key_type); + ret &= btc_config_set_int(bdstr, BTC_STORAGE_PIN_LENGTH_STR, (int)pin_length); + ret &= btc_config_set_bin(bdstr, BTC_STORAGE_LINK_KEY_STR, link_key, sizeof(LINK_KEY)); + ret &= btc_config_set_bin(bdstr, BTC_STORAGE_SC_SUPPORT, (uint8_t *)&sc_support, sizeof(sc_support)); + /* write bonded info immediately */ + btc_config_flush(); + btc_config_unlock(); + + BTC_TRACE_DEBUG("Storage add rslt %d\n", ret); + return ret ? BT_STATUS_SUCCESS : BT_STATUS_FAIL; +} + +#if (SMP_INCLUDED == TRUE) +static bt_status_t _btc_storage_in_fetch_bonded_bt_device(const char *remote_bd_addr, int add) +{ + BOOLEAN bt_linkkey_file_found = FALSE; + UINT8 sc_support = 0; + + BTC_TRACE_DEBUG("Remote device:%s\n", remote_bd_addr); + LINK_KEY link_key; + size_t size = sizeof(link_key); + if (btc_config_get_bin(remote_bd_addr, BTC_STORAGE_LINK_KEY_STR, link_key, &size)) { + int linkkey_type; + if (btc_config_get_int(remote_bd_addr, BTC_STORAGE_LINK_KEY_TYPE_STR, &linkkey_type)) { + bt_bdaddr_t bd_addr; + string_to_bdaddr(remote_bd_addr, &bd_addr); + if (add) { + DEV_CLASS dev_class = {0, 0, 0}; + int cod; + int pin_length = 0; + if (btc_config_get_int(remote_bd_addr, BTC_STORAGE_DEV_CLASS_STR, &cod)) { + uint2devclass((UINT32)cod, dev_class); + } + btc_config_get_int(remote_bd_addr, BTC_STORAGE_PIN_LENGTH_STR, &pin_length); + size = sizeof(sc_support); + btc_config_get_bin(remote_bd_addr, BTC_STORAGE_SC_SUPPORT, &sc_support, &size); + + BTA_DmAddDevice(bd_addr.address, dev_class, link_key, 0, 0, + (UINT8)linkkey_type, 0, pin_length, (UINT8)sc_support); + } + bt_linkkey_file_found = TRUE; + } else { + BTC_TRACE_ERROR("bounded device:%s, LinkKeyType or PinLength is invalid\n", remote_bd_addr); + } + } + if (!bt_linkkey_file_found) { + BTC_TRACE_DEBUG("Remote device:%s, no link key\n", remote_bd_addr); + } + + return BT_STATUS_SUCCESS; +} + +/******************************************************************************* +** +** Function btc_in_fetch_bonded_devices +** +** Description Internal helper function to fetch the bonded devices +** from NVRAM +** +** Returns BT_STATUS_SUCCESS if successful, BT_STATUS_FAIL otherwise +** +*******************************************************************************/ +static bt_status_t btc_in_fetch_bonded_devices(int add) +{ + bt_status_t status = BT_STATUS_FAIL; + uint16_t dev_cnt = 0; + const btc_config_section_iter_t *remove_iter = NULL; + bt_bdaddr_t bd_addr; + + btc_config_lock(); + for (const btc_config_section_iter_t *iter = btc_config_section_begin(); iter != btc_config_section_end(); iter = btc_config_section_next(iter)) { + const char *name = btc_config_section_name(iter); + if (!string_is_bdaddr(name)) { + continue; + } + dev_cnt ++; + /* if the number of device stored in nvs not exceed to BTM_SEC_MAX_DEVICE_RECORDS, load it */ + if (dev_cnt <= BTM_SEC_MAX_DEVICE_RECORDS) { + if (btc_config_exist(name, BTC_STORAGE_LINK_KEY_TYPE_STR) && btc_config_exist(name, BTC_STORAGE_PIN_LENGTH_STR) && + btc_config_exist(name, BTC_STORAGE_SC_SUPPORT) && btc_config_exist(name, BTC_STORAGE_LINK_KEY_STR)) { + /* load bt device */ + status = _btc_storage_in_fetch_bonded_bt_device(name, add); + } + if (btc_config_exist(name, BTC_BLE_STORAGE_DEV_TYPE_STR)) { +#if (BLE_INCLUDED == TRUE) + /* load ble device */ + status = _btc_storage_in_fetch_bonded_ble_device(name, add); +#endif ///BLE_INCLUDED == TRUE + } + } else { + /* delete the exceeded device info from nvs */ + remove_iter = iter; + while (remove_iter != btc_config_section_end()) { + const char *remove_section = btc_config_section_name(remove_iter); + string_to_bdaddr(remove_section, &bd_addr); + if (!string_is_bdaddr(remove_section)) { + remove_iter = btc_config_section_next(remove_iter); + continue; + } + remove_iter = btc_config_section_next(remove_iter); + /* delete config info */ + if (btc_config_remove_section(remove_section)) { + BTC_TRACE_WARNING("exceeded the maximum number of bonded devices, delete the exceed device info : %02x:%02x:%02x:%02x:%02x:%02x", + bd_addr.address[0], bd_addr.address[1], bd_addr.address[2], bd_addr.address[3], bd_addr.address[4], bd_addr.address[5]); + } + } + /* write into nvs */ + btc_config_flush(); + break; + } + } + btc_config_unlock(); + + return status; +} + +/******************************************************************************* +** +** Function btc_storage_load_bonded_devices +** +** Description BTC storage API - Loads all the bonded devices from NVRAM +** and adds to the BTA. +** Additionally, this API also invokes the adaper_properties_cb +** and remote_device_properties_cb for each of the bonded devices. +** +** Returns BT_STATUS_SUCCESS if successful, BT_STATUS_FAIL otherwise +** +*******************************************************************************/ +bt_status_t btc_storage_load_bonded_devices(void) +{ + bt_status_t status; + status = btc_in_fetch_bonded_devices(1); + BTC_TRACE_DEBUG("Storage load rslt %d\n", status); + return status; +} + +/******************************************************************************* +** +** Function btc_storage_update_active_device +** +** Description BTC storage API - Once an ACL link is established and remote +** bd_addr is already stored in NVRAM, update the config and update +** the remote device to be the newest active device, The updates will +** not be stored into NVRAM immediately. +** +** Returns BT_STATUS_SUCCESS if successful, BT_STATUS_FAIL otherwise +** +*******************************************************************************/ +bool btc_storage_update_active_device(bt_bdaddr_t *remote_bd_addr) +{ + bdstr_t bdstr; + bdaddr_to_string(remote_bd_addr, bdstr, sizeof(bdstr)); + bool ret = false; + BTC_TRACE_DEBUG("Update active device: Remote device:%s\n", bdstr); + + btc_config_lock(); + ret = btc_config_update_newest_section(bdstr); + btc_config_unlock(); + + return ret ? BT_STATUS_SUCCESS : BT_STATUS_FAIL; +} +#endif ///SMP_INCLUDED == TRUE + +/******************************************************************************* +** +** Function btc_storage_remove_bonded_device +** +** Description BTC storage API - Deletes the bonded device from NVRAM +** +** Returns BT_STATUS_SUCCESS if the deletion was successful, +** BT_STATUS_FAIL otherwise +** +*******************************************************************************/ +bt_status_t btc_storage_remove_bonded_device(bt_bdaddr_t *remote_bd_addr) +{ + bdstr_t bdstr; + bdaddr_to_string(remote_bd_addr, bdstr, sizeof(bdstr)); + int ret = 1; + BTC_TRACE_DEBUG("Add to storage: Remote device:%s\n", bdstr); + + btc_config_lock(); + if (btc_config_exist(bdstr, BTC_STORAGE_LINK_KEY_TYPE_STR)) { + ret &= btc_config_remove(bdstr, BTC_STORAGE_LINK_KEY_TYPE_STR); + } + if (btc_config_exist(bdstr, BTC_STORAGE_PIN_LENGTH_STR)) { + ret &= btc_config_remove(bdstr, BTC_STORAGE_PIN_LENGTH_STR); + } + if (btc_config_exist(bdstr, BTC_STORAGE_LINK_KEY_STR)) { + ret &= btc_config_remove(bdstr, BTC_STORAGE_LINK_KEY_STR); + } + if (btc_config_exist(bdstr, BTC_STORAGE_SC_SUPPORT)) { + ret &= btc_config_remove(bdstr, BTC_STORAGE_SC_SUPPORT); + } + /* write bonded info immediately */ + btc_config_flush(); + btc_config_unlock(); + + return ret ? BT_STATUS_SUCCESS : BT_STATUS_FAIL; +} + +/******************************************************************************* +** +** Function btc_storage_get_num_bt_bond_devices +** +** Description BTC storage API - get the num of the bonded device from NVRAM +** +** Returns the num of the bonded device +** +*******************************************************************************/ +int btc_storage_get_num_bt_bond_devices(void) +{ + int num_dev = 0; + + btc_config_lock(); + for (const btc_config_section_iter_t *iter = btc_config_section_begin(); iter != btc_config_section_end(); + iter = btc_config_section_next(iter)) { + const char *name = btc_config_section_name(iter); + if (string_is_bdaddr(name) && + btc_config_exist(name, BTC_STORAGE_LINK_KEY_TYPE_STR) && + btc_config_exist(name, BTC_STORAGE_PIN_LENGTH_STR) && + btc_config_exist(name, BTC_STORAGE_SC_SUPPORT) && + btc_config_exist(name, BTC_STORAGE_LINK_KEY_STR)) { + num_dev++; + } + } + btc_config_unlock(); + return num_dev; +} + +/******************************************************************************* +** +** Function btc_storage_get_bonded_bt_devices_list +** +** Description BTC storage API - get the list of the bonded device from NVRAM +** +** Returns BT_STATUS_SUCCESS if get the list successful, +** BT_STATUS_FAIL otherwise +** +*******************************************************************************/ +bt_status_t btc_storage_get_bonded_bt_devices_list(bt_bdaddr_t *bond_dev, int *dev_num) +{ + bt_bdaddr_t bd_addr; + int in_dev_num = *dev_num; /* buffer size */ + int out_dev_num = 0; /* bond_dev size */ + + btc_config_lock(); + for (const btc_config_section_iter_t *iter = btc_config_section_begin(); iter != btc_config_section_end(); + iter = btc_config_section_next(iter)) { + + if (in_dev_num <= 0) { + break; + } + + const char *name = btc_config_section_name(iter); + + if (string_is_bdaddr(name) && + btc_config_exist(name, BTC_STORAGE_LINK_KEY_TYPE_STR) && + btc_config_exist(name, BTC_STORAGE_PIN_LENGTH_STR) && + btc_config_exist(name, BTC_STORAGE_SC_SUPPORT) && + btc_config_exist(name, BTC_STORAGE_LINK_KEY_STR)) { + string_to_bdaddr(name, &bd_addr); + memcpy(bond_dev, &bd_addr, sizeof(bt_bdaddr_t)); + in_dev_num--; + out_dev_num++; + bond_dev++; + } + } + *dev_num = out_dev_num; /* out_dev_num <= in_dev_num */ + btc_config_unlock(); + + return BT_STATUS_SUCCESS; +} + +#if (defined BTC_HH_INCLUDED && BTC_HH_INCLUDED == TRUE) +/******************************************************************************* + * + * Function btc_storage_add_hid_device_info + * + * Description BTC storage API - Adds the hid information of bonded hid + * devices-to NVRAM + * + * Returns BT_STATUS_SUCCESS if the store was successful, + * BT_STATUS_FAIL otherwise + * + ******************************************************************************/ + +bt_status_t btc_storage_add_hid_device_info(bt_bdaddr_t *remote_bd_addr, uint16_t attr_mask, uint8_t sub_class, + uint8_t app_id, uint16_t vendor_id, uint16_t product_id, uint16_t version, + uint8_t ctry_code, uint16_t ssr_max_latency, uint16_t ssr_min_tout, + uint16_t dl_len, uint8_t *dsc_list) +{ + BTC_TRACE_DEBUG("btc_storage_add_hid_device_info:"); + bdstr_t bdstr; + bdaddr_to_string(remote_bd_addr, bdstr, sizeof(bdstr)); + btc_config_lock(); + int ret = btc_config_set_int(bdstr, "HidAttrMask", attr_mask); + ret &= btc_config_set_int(bdstr, "HidSubClass", sub_class); + ret &= btc_config_set_int(bdstr, "HidAppId", app_id); + ret &= btc_config_set_int(bdstr, "HidVendorId", vendor_id); + ret &= btc_config_set_int(bdstr, "HidProductId", product_id); + ret &= btc_config_set_int(bdstr, "HidVersion", version); + ret &= btc_config_set_int(bdstr, "HidCountryCode", ctry_code); + ret &= btc_config_set_int(bdstr, "HidSSRMaxLatency", ssr_max_latency); + ret &= btc_config_set_int(bdstr, "HidSSRMinTimeout", ssr_min_tout); + if (dl_len > 0) + btc_config_set_bin(bdstr, "HidDescriptor", dsc_list, dl_len); + btc_config_flush(); + btc_config_unlock(); + + BTC_TRACE_DEBUG("Storage add hid device info %d\n", ret); + return ret ? BT_STATUS_SUCCESS : BT_STATUS_FAIL; +} + +/******************************************************************************* + * + * Function btc_storage_load_bonded_hid_info + * + * Description BTIF storage API - Loads hid info for all the bonded devices + * from NVRAM and adds those devices to the BTA_HH. + * + * Returns BT_STATUS_SUCCESS if successful, BT_STATUS_FAIL otherwise + * + ******************************************************************************/ +bt_status_t btc_storage_load_bonded_hid_info(void) +{ + int value; + btc_config_lock(); + for (const btc_config_section_iter_t *iter = btc_config_section_begin(); iter != btc_config_section_end(); + iter = btc_config_section_next(iter)) { + const char *name = btc_config_section_name(iter); + if (string_is_bdaddr(name) && btc_config_exist(name, BTC_STORAGE_LINK_KEY_TYPE_STR) && + btc_config_exist(name, BTC_STORAGE_PIN_LENGTH_STR) && btc_config_exist(name, BTC_STORAGE_SC_SUPPORT) && + btc_config_exist(name, BTC_STORAGE_LINK_KEY_STR) && btc_config_exist(name, "HidAttrMask")) { + btc_config_get_int(name, "HidAttrMask", &value); + uint16_t attr_mask = (uint16_t)value; + + tBTA_HH_DEV_DSCP_INFO dscp_info; + memset(&dscp_info, 0, sizeof(dscp_info)); + + btc_config_get_int(name, "HidSubClass", &value); + uint8_t sub_class = (uint8_t)value; + + btc_config_get_int(name, "HidAppId", &value); + uint8_t app_id = (uint8_t)value; + + btc_config_get_int(name, "HidVendorId", &value); + dscp_info.vendor_id = (uint16_t)value; + + btc_config_get_int(name, "HidProductId", &value); + dscp_info.product_id = (uint16_t)value; + + btc_config_get_int(name, "HidVersion", &value); + dscp_info.version = (uint8_t)value; + + btc_config_get_int(name, "HidCountryCode", &value); + dscp_info.ctry_code = (uint8_t)value; + + value = 0; + btc_config_get_int(name, "HidSSRMaxLatency", &value); + dscp_info.ssr_max_latency = (uint16_t)value; + + value = 0; + btc_config_get_int(name, "HidSSRMinTimeout", &value); + dscp_info.ssr_min_tout = (uint16_t)value; + + size_t len = btc_config_get_bin_length(name, "HidDescriptor"); + if (len > 0) { + dscp_info.descriptor.dl_len = (uint16_t)len; + dscp_info.descriptor.dsc_list = (uint8_t *)osi_malloc(len); + btc_config_get_bin(name, "HidDescriptor", (uint8_t *)dscp_info.descriptor.dsc_list, &len); + } + + // add extracted information to BTA HH + bt_bdaddr_t bd_addr; + if (string_to_bdaddr(name, &bd_addr) && btc_hh_add_added_dev(*(BD_ADDR *)&bd_addr, attr_mask)) { + BTA_HhAddDev(*(BD_ADDR *)&bd_addr, attr_mask, sub_class, app_id, dscp_info); + } + + if (dscp_info.descriptor.dsc_list) { + osi_free(dscp_info.descriptor.dsc_list); + dscp_info.descriptor.dsc_list = NULL; + } + } + } + btc_config_unlock(); + return BT_STATUS_SUCCESS; +} + +/******************************************************************************* + * + * Function btc_storage_remove_hid_info + * + * Description BTC storage API - Deletes the bonded hid device info from + * NVRAM + * + * Returns BT_STATUS_SUCCESS if the deletion was successful, + * BT_STATUS_FAIL otherwise + * + ******************************************************************************/ +bt_status_t btc_storage_remove_hid_info(bt_bdaddr_t *remote_bd_addr) +{ + bdstr_t bdstr; + bdaddr_to_string(remote_bd_addr, bdstr, sizeof(bdstr)); + int ret = 1; + btc_config_lock(); + if (btc_config_exist(bdstr, "HidAttrMask")) { + ret &= btc_config_remove(bdstr, "HidAttrMask"); + } + if (btc_config_exist(bdstr, "HidSubClass")) { + ret &= btc_config_remove(bdstr, "HidSubClass"); + } + if (btc_config_exist(bdstr, "HidAppId")) { + ret &= btc_config_remove(bdstr, "HidAppId"); + } + if (btc_config_exist(bdstr, "HidVendorId")) { + ret &= btc_config_remove(bdstr, "HidVendorId"); + } + if (btc_config_exist(bdstr, "HidProductId")) { + ret &= btc_config_remove(bdstr, "HidProductId"); + } + if (btc_config_exist(bdstr, "HidVersion")) { + ret &= btc_config_remove(bdstr, "HidVersion"); + } + if (btc_config_exist(bdstr, "HidCountryCode")) { + ret &= btc_config_remove(bdstr, "HidCountryCode"); + } + if (btc_config_exist(bdstr, "HidSSRMaxLatency")) { + ret &= btc_config_remove(bdstr, "HidSSRMaxLatency"); + } + if (btc_config_exist(bdstr, "HidSSRMinTimeout")) { + ret &= btc_config_remove(bdstr, "HidSSRMinTimeout"); + } + if (btc_config_exist(bdstr, "HidDescriptor")) { + ret &= btc_config_remove(bdstr, "HidDescriptor"); + } + btc_config_flush(); + btc_config_unlock(); + BTC_TRACE_DEBUG("%s ret:%d", __func__, ret); + return ret ? BT_STATUS_SUCCESS : BT_STATUS_FAIL; +} +#endif //(defined BTC_HH_INCLUDED && BTC_HH_INCLUDED == TRUE) + +#if (defined BTC_HD_INCLUDED && BTC_HD_INCLUDED == TRUE) +#include "bta/bta_hd_api.h" +/******************************************************************************* + * Function btc_storage_load_hidd + * + * Description Loads hidd bonded device and "plugs" it into hidd + * + * Returns BT_STATUS_SUCCESS if successful, BT_STATUS_FAIL otherwise + * + ******************************************************************************/ +bt_status_t btc_storage_load_hidd(void) +{ + bt_bdaddr_t bd_addr; + int value; + btc_config_lock(); + for (const btc_config_section_iter_t *iter = btc_config_section_begin(); iter != btc_config_section_end(); + iter = btc_config_section_next(iter)) { + const char *name = btc_config_section_name(iter); + if (string_is_bdaddr(name) && btc_config_exist(name, BTC_STORAGE_LINK_KEY_TYPE_STR) && + btc_config_exist(name, BTC_STORAGE_PIN_LENGTH_STR) && btc_config_exist(name, BTC_STORAGE_SC_SUPPORT) && + btc_config_exist(name, BTC_STORAGE_LINK_KEY_STR)) { + BTC_TRACE_DEBUG("Remote device:%s", name); + if (btc_config_get_int(name, "HidDeviceCabled", &value)) { + string_to_bdaddr(name, &bd_addr); + BTA_HdAddDevice(bd_addr.address); + break; + } + } + } + btc_config_unlock(); + return BT_STATUS_SUCCESS; +} + +/******************************************************************************* + * + * Function btc_storage_set_hidd + * + * Description Stores hidd bonded device info in nvram. + * + * Returns BT_STATUS_SUCCESS + * + ******************************************************************************/ +bt_status_t btc_storage_set_hidd(bt_bdaddr_t *remote_bd_addr) +{ + bdstr_t bdstr = {0}; + bdaddr_to_string(remote_bd_addr, bdstr, sizeof(bdstr)); + btc_config_lock(); + int ret = btc_config_set_int(bdstr, "HidDeviceCabled", 1); + btc_config_flush(); + btc_config_unlock(); + BTC_TRACE_DEBUG("%s ret:%d", __func__, ret); + return ret ? BT_STATUS_SUCCESS : BT_STATUS_FAIL; +} + +/******************************************************************************* + * + * Function btc_storage_remove_hidd + * + * Description Removes hidd bonded device info from nvram + * + * Returns BT_STATUS_SUCCESS + * + ******************************************************************************/ +bt_status_t btc_storage_remove_hidd(bt_bdaddr_t *remote_bd_addr) +{ + bdstr_t bdstr; + int ret = 0; + bdaddr_to_string(remote_bd_addr, bdstr, sizeof(bdstr)); + btc_config_lock(); + if (btc_config_exist(bdstr, "HidVersion")) { + ret = btc_config_remove(bdstr, "HidDeviceCabled"); + } + btc_config_flush(); + btc_config_unlock(); + + BTC_TRACE_DEBUG("%s ret:%d", __func__, ret); + return ret ? BT_STATUS_SUCCESS : BT_STATUS_FAIL; +} +#endif //(defined BTC_HD_INCLUDED && BTC_HD_INCLUDED == TRUE) + +int btc_storage_get_num_all_bond_devices(void) { + int num_dev = 0; + + btc_config_lock(); + for (const btc_config_section_iter_t *iter = btc_config_section_begin(); iter != btc_config_section_end(); + iter = btc_config_section_next(iter)) { + const char *name = btc_config_section_name(iter); + if (string_is_bdaddr(name)) { + num_dev++; + } + } + btc_config_unlock(); + return num_dev; +} diff --git a/lib/bt/host/bluedroid/btc/core/btc_util.c b/lib/bt/host/bluedroid/btc/core/btc_util.c new file mode 100644 index 00000000..9818e706 --- /dev/null +++ b/lib/bt/host/bluedroid/btc/core/btc_util.c @@ -0,0 +1,419 @@ +/* + * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/************************************************************************************ + * + * Filename: btc_util.c + * + * Description: Miscellaneous helper functions + * + * + ***********************************************************************************/ + +#include +#include +#include +#include + +#include "btc/btc_util.h" +#if (BTA_AV_INCLUDED == TRUE) +#include "bta/bta_av_api.h" +#endif ///BTA_AV_INCLUDED == TRUE + +#if (BTA_AG_INCLUDED == TRUE) +#include "bta/bta_ag_api.h" +#endif ///BTA_AG_INCLUDED == TRUE + +#if (BTA_HH_INCLUDED == TRUE) +#include "bta/bta_hh_api.h" +#endif ///BTA_HH_INCLUDED == TRUE +#include "bta/bta_hd_api.h" +#include "common/bt_defs.h" +#include "stack/btm_api.h" +#include "bta/bta_api.h" + +/************************************************************************************ +** Constants & Macros +************************************************************************************/ +#define ISDIGIT(a) ((a>='0') && (a<='9')) +#define ISXDIGIT(a) (((a>='0') && (a<='9'))||((a>='A') && (a<='F'))||((a>='a') && (a<='f'))) + +/************************************************************************************ +** Local type definitions +************************************************************************************/ + +/************************************************************************************ +** Static variables +************************************************************************************/ + +/************************************************************************************ +** Static functions +************************************************************************************/ + +/************************************************************************************ +** Externs +************************************************************************************/ + +/************************************************************************************ +** Functions +************************************************************************************/ + +/***************************************************************************** +** Logging helper functions +*****************************************************************************/ +#if(BTA_AV_INCLUDED == TRUE) +const char *dump_rc_event(UINT8 event) +{ + switch (event) { + CASE_RETURN_STR(BTA_AV_RC_OPEN_EVT) + CASE_RETURN_STR(BTA_AV_RC_CLOSE_EVT) + CASE_RETURN_STR(BTA_AV_REMOTE_CMD_EVT) + CASE_RETURN_STR(BTA_AV_REMOTE_RSP_EVT) + CASE_RETURN_STR(BTA_AV_VENDOR_CMD_EVT) + CASE_RETURN_STR(BTA_AV_VENDOR_RSP_EVT) + CASE_RETURN_STR(BTA_AV_META_MSG_EVT) + CASE_RETURN_STR(BTA_AV_RC_FEAT_EVT) + default: + return "UNKNOWN_EVENT"; + } +} + +const char *dump_rc_notification_event_id(UINT8 event_id) +{ + switch (event_id) { + CASE_RETURN_STR(AVRC_EVT_PLAY_STATUS_CHANGE) + CASE_RETURN_STR(AVRC_EVT_TRACK_CHANGE) + CASE_RETURN_STR(AVRC_EVT_TRACK_REACHED_END) + CASE_RETURN_STR(AVRC_EVT_TRACK_REACHED_START) + CASE_RETURN_STR(AVRC_EVT_PLAY_POS_CHANGED) + CASE_RETURN_STR(AVRC_EVT_BATTERY_STATUS_CHANGE) + CASE_RETURN_STR(AVRC_EVT_SYSTEM_STATUS_CHANGE) + CASE_RETURN_STR(AVRC_EVT_APP_SETTING_CHANGE) + CASE_RETURN_STR(AVRC_EVT_VOLUME_CHANGE) + + default: + return "Unhandled Event ID"; + } +} + +const char *dump_rc_pdu(UINT8 pdu) +{ + switch (pdu) { + CASE_RETURN_STR(AVRC_PDU_LIST_PLAYER_APP_ATTR) + CASE_RETURN_STR(AVRC_PDU_LIST_PLAYER_APP_VALUES) + CASE_RETURN_STR(AVRC_PDU_GET_CUR_PLAYER_APP_VALUE) + CASE_RETURN_STR(AVRC_PDU_SET_PLAYER_APP_VALUE) + CASE_RETURN_STR(AVRC_PDU_GET_PLAYER_APP_ATTR_TEXT) + CASE_RETURN_STR(AVRC_PDU_GET_PLAYER_APP_VALUE_TEXT) + CASE_RETURN_STR(AVRC_PDU_INFORM_DISPLAY_CHARSET) + CASE_RETURN_STR(AVRC_PDU_INFORM_BATTERY_STAT_OF_CT) + CASE_RETURN_STR(AVRC_PDU_GET_ELEMENT_ATTR) + CASE_RETURN_STR(AVRC_PDU_GET_PLAY_STATUS) + CASE_RETURN_STR(AVRC_PDU_REGISTER_NOTIFICATION) + CASE_RETURN_STR(AVRC_PDU_REQUEST_CONTINUATION_RSP) + CASE_RETURN_STR(AVRC_PDU_ABORT_CONTINUATION_RSP) + CASE_RETURN_STR(AVRC_PDU_SET_ABSOLUTE_VOLUME) + default: + return "Unknown PDU"; + } +} +#endif ///BTA_AV_INCLUDED == TRUE + +#if (BTA_AG_INCLUDED == TRUE) +const char* dump_hf_conn_state(UINT16 event) +{ + switch(event) + { + CASE_RETURN_STR(ESP_HF_CONNECTION_STATE_DISCONNECTED) + CASE_RETURN_STR(ESP_HF_CONNECTION_STATE_CONNECTING) + CASE_RETURN_STR(ESP_HF_CONNECTION_STATE_CONNECTED) + CASE_RETURN_STR(ESP_HF_CONNECTION_STATE_SLC_CONNECTED) + CASE_RETURN_STR(ESP_HF_CONNECTION_STATE_DISCONNECTING) + default: + return "UNKNOWN MSG ID"; + } +} + +const char* dump_hf_event(UINT16 event) +{ + switch(event) + { + CASE_RETURN_STR(BTA_AG_ENABLE_EVT) + CASE_RETURN_STR(BTA_AG_REGISTER_EVT) + CASE_RETURN_STR(BTA_AG_OPEN_EVT) + CASE_RETURN_STR(BTA_AG_CLOSE_EVT) + CASE_RETURN_STR(BTA_AG_CONN_EVT) + CASE_RETURN_STR(BTA_AG_AUDIO_OPEN_EVT) + CASE_RETURN_STR(BTA_AG_AUDIO_CLOSE_EVT) + CASE_RETURN_STR(BTA_AG_SPK_EVT) + CASE_RETURN_STR(BTA_AG_MIC_EVT) + CASE_RETURN_STR(BTA_AG_AT_CKPD_EVT) + CASE_RETURN_STR(BTA_AG_DISABLE_EVT) +#if (BTM_WBS_INCLUDED == TRUE) + CASE_RETURN_STR(BTA_AG_WBS_EVT) +#endif + CASE_RETURN_STR(BTA_AG_AT_A_EVT) + CASE_RETURN_STR(BTA_AG_AT_D_EVT) + CASE_RETURN_STR(BTA_AG_AT_CHLD_EVT) + CASE_RETURN_STR(BTA_AG_AT_CHUP_EVT) + CASE_RETURN_STR(BTA_AG_AT_CIND_EVT) + CASE_RETURN_STR(BTA_AG_AT_VTS_EVT) + CASE_RETURN_STR(BTA_AG_AT_BINP_EVT) + CASE_RETURN_STR(BTA_AG_AT_BLDN_EVT) + CASE_RETURN_STR(BTA_AG_AT_BVRA_EVT) + CASE_RETURN_STR(BTA_AG_AT_NREC_EVT) + CASE_RETURN_STR(BTA_AG_AT_CNUM_EVT) + CASE_RETURN_STR(BTA_AG_AT_BTRH_EVT) + CASE_RETURN_STR(BTA_AG_AT_CLCC_EVT) + CASE_RETURN_STR(BTA_AG_AT_COPS_EVT) + CASE_RETURN_STR(BTA_AG_AT_UNAT_EVT) + CASE_RETURN_STR(BTA_AG_AT_CBC_EVT) + CASE_RETURN_STR(BTA_AG_AT_BAC_EVT) + CASE_RETURN_STR(BTA_AG_AT_BCS_EVT) + + default: + return "UNKNOWN MSG ID"; + } +} + +const char* dump_hf_call_state(esp_hf_call_status_t call_state) +{ + switch(call_state) + { + CASE_RETURN_STR(ESP_HF_CALL_STATUS_NO_CALLS) + CASE_RETURN_STR(ESP_HF_CALL_STATUS_CALL_IN_PROGRESS) + default: + return "UNKNOWN CALL STATE"; + } +} + +const char* dump_hf_call_setup_state(esp_hf_call_setup_status_t call_setup_state) +{ + switch(call_setup_state) + { + CASE_RETURN_STR(ESP_HF_CALL_SETUP_STATUS_IDLE) + CASE_RETURN_STR(ESP_HF_CALL_SETUP_STATUS_INCOMING) + CASE_RETURN_STR(ESP_HF_CALL_SETUP_STATUS_OUTGOING_DIALING) + CASE_RETURN_STR(ESP_HF_CALL_SETUP_STATUS_OUTGOING_ALERTING) + default: + return "UNKNOWN CALL SETUP STATE"; + } +} + +#endif // #if (BTA_AG_INCLUDED == TRUE) + +#if (BTA_HH_INCLUDED == TRUE) +const char *dump_hh_event(uint16_t event) +{ + switch (event) { + CASE_RETURN_STR(BTA_HH_ENABLE_EVT) + CASE_RETURN_STR(BTA_HH_DISABLE_EVT) + CASE_RETURN_STR(BTA_HH_OPEN_EVT) + CASE_RETURN_STR(BTA_HH_CLOSE_EVT) + CASE_RETURN_STR(BTA_HH_GET_RPT_EVT) + CASE_RETURN_STR(BTA_HH_SET_RPT_EVT) + CASE_RETURN_STR(BTA_HH_GET_PROTO_EVT) + CASE_RETURN_STR(BTA_HH_SET_PROTO_EVT) + CASE_RETURN_STR(BTA_HH_GET_IDLE_EVT) + CASE_RETURN_STR(BTA_HH_SET_IDLE_EVT) + CASE_RETURN_STR(BTA_HH_GET_DSCP_EVT) + CASE_RETURN_STR(BTA_HH_ADD_DEV_EVT) + CASE_RETURN_STR(BTA_HH_RMV_DEV_EVT) + CASE_RETURN_STR(BTA_HH_VC_UNPLUG_EVT) + CASE_RETURN_STR(BTA_HH_DATA_EVT) + CASE_RETURN_STR(BTA_HH_API_ERR_EVT) + CASE_RETURN_STR(BTA_HH_UPDATE_SCPP_EVT) + CASE_RETURN_STR(BTA_HH_DATA_IND_EVT) + default: + return "UNKNOWN MSG ID"; + } +} +#endif ///BTA_HH_INCLUDED + +#if BTA_HD_INCLUDED == TRUE +const char* dump_hd_event(uint16_t event) { + switch (event) { + CASE_RETURN_STR(BTA_HD_ENABLE_EVT) + CASE_RETURN_STR(BTA_HD_DISABLE_EVT) + CASE_RETURN_STR(BTA_HD_REGISTER_APP_EVT) + CASE_RETURN_STR(BTA_HD_UNREGISTER_APP_EVT) + CASE_RETURN_STR(BTA_HD_OPEN_EVT) + CASE_RETURN_STR(BTA_HD_CLOSE_EVT) + CASE_RETURN_STR(BTA_HD_GET_REPORT_EVT) + CASE_RETURN_STR(BTA_HD_SET_REPORT_EVT) + CASE_RETURN_STR(BTA_HD_SET_PROTOCOL_EVT) + CASE_RETURN_STR(BTA_HD_INTR_DATA_EVT) + CASE_RETURN_STR(BTA_HD_VC_UNPLUG_EVT) + //CASE_RETURN_STR(BTA_HD_CONN_STATE_EVT) + CASE_RETURN_STR(BTA_HD_API_ERR_EVT) + default: + return "UNKNOWN MSG ID"; + } +} +#endif ///BTA_HD_INCLUDED + +UINT32 devclass2uint(DEV_CLASS dev_class) +{ + UINT32 cod = 0; + + if (dev_class != NULL) { + /* if COD is 0, irrespective of the device type set it to Unclassified device */ + cod = (dev_class[2]) | (dev_class[1] << 8) | (dev_class[0] << 16); + } + return cod; +} + +void uint2devclass(UINT32 cod, DEV_CLASS dev_class) +{ + dev_class[2] = (UINT8)cod; + dev_class[1] = (UINT8)(cod >> 8); + dev_class[0] = (UINT8)(cod >> 16); +} + +static const UINT8 base_uuid_be[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, + 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB}; + +void uuid128_be_to_esp_uuid(esp_bt_uuid_t *u, uint8_t* uuid128) +{ + if (memcmp(base_uuid_be+4, uuid128 + 4, 12) != 0) { + u->len = ESP_UUID_LEN_128; + uint8_t *p_i = uuid128 + ESP_UUID_LEN_128 - 1; + uint8_t *p_o = u->uuid.uuid128; + uint8_t *p_end = p_o + ESP_UUID_LEN_128; + for (; p_o != p_end; *p_o++ = *p_i--) + ; + } else if (uuid128[0] == 0 && uuid128[1] == 0) { + u->len = 2; + u->uuid.uuid16 = (uuid128[2] << 8) + uuid128[3]; + } else { + u->len = 4; + u->uuid.uuid32 = (uuid128[2] << 8) + uuid128[3]; + u->uuid.uuid32 += (uuid128[0] << 24) + (uuid128[1] << 16); + } + + return; +} + +void uuid_to_string_legacy(bt_uuid_t *p_uuid, char *str) +{ + uint32_t uuid0, uuid4; + uint16_t uuid1, uuid2, uuid3, uuid5; + + memcpy(&uuid0, &(p_uuid->uu[0]), 4); + memcpy(&uuid1, &(p_uuid->uu[4]), 2); + memcpy(&uuid2, &(p_uuid->uu[6]), 2); + memcpy(&uuid3, &(p_uuid->uu[8]), 2); + memcpy(&uuid4, &(p_uuid->uu[10]), 4); + memcpy(&uuid5, &(p_uuid->uu[14]), 2); + + sprintf((char *)str, "%.8x-%.4x-%.4x-%.4x-%.8x%.4x", + ntohl(uuid0), ntohs(uuid1), + ntohs(uuid2), ntohs(uuid3), + ntohl(uuid4), ntohs(uuid5)); + return; +} + +esp_bt_status_t btc_hci_to_esp_status(uint8_t hci_status) +{ + esp_bt_status_t esp_status = ESP_BT_STATUS_FAIL; + switch(hci_status) { + case HCI_SUCCESS: + esp_status = ESP_BT_STATUS_SUCCESS; + break; + case HCI_ERR_HOST_TIMEOUT: + esp_status = ESP_BT_STATUS_TIMEOUT; + break; + case HCI_ERR_ILLEGAL_COMMAND: + esp_status = ESP_BT_STATUS_PENDING; + break; + case HCI_ERR_UNACCEPT_CONN_INTERVAL: + esp_status = ESP_BT_STATUS_UNACCEPT_CONN_INTERVAL; + break; + case HCI_ERR_PARAM_OUT_OF_RANGE: + esp_status = ESP_BT_STATUS_PARAM_OUT_OF_RANGE; + break; + case HCI_ERR_ILLEGAL_PARAMETER_FMT: + esp_status = ESP_BT_STATUS_ERR_ILLEGAL_PARAMETER_FMT; + break; + default: + esp_status = ESP_BT_STATUS_FAIL; + break; + } + + return esp_status; +} + +esp_bt_status_t btc_btm_status_to_esp_status (uint8_t btm_status) +{ + esp_bt_status_t esp_status = ESP_BT_STATUS_FAIL; + switch(btm_status){ + case BTM_SUCCESS: + esp_status = ESP_BT_STATUS_SUCCESS; + break; + case BTM_BUSY: + esp_status = ESP_BT_STATUS_BUSY; + break; + case BTM_NO_RESOURCES: + esp_status = ESP_BT_STATUS_NOMEM; + break; + case BTM_ILLEGAL_VALUE: + esp_status = ESP_BT_STATUS_PARM_INVALID; + break; + case BTM_ERR_PROCESSING: + esp_status = ESP_BT_STATUS_PENDING; + break; + case BTM_PEER_LE_DATA_LEN_UNSUPPORTED: + esp_status = ESP_BT_STATUS_PEER_LE_DATA_LEN_UNSUPPORTED; + break; + case BTM_CONTROL_LE_DATA_LEN_UNSUPPORTED: + esp_status = ESP_BT_STATUS_CONTROL_LE_DATA_LEN_UNSUPPORTED; + break; + case BTM_SET_PRIVACY_SUCCESS: + esp_status = ESP_BT_STATUS_SUCCESS; + break; + case BTM_SET_PRIVACY_FAIL: + esp_status = ESP_BT_STATUS_FAIL; + break; + default: + esp_status = ESP_BT_STATUS_FAIL; + break; + } + + return esp_status; +} + +esp_bt_status_t btc_bta_status_to_esp_status (uint8_t bta_status) +{ + esp_bt_status_t esp_status = ESP_BT_STATUS_FAIL; + switch(bta_status){ + case BTA_SUCCESS: + esp_status = ESP_BT_STATUS_SUCCESS; + break; + case BTA_FAILURE: + esp_status = ESP_BT_STATUS_FAIL; + break; + case BTA_PENDING: + esp_status = ESP_BT_STATUS_PENDING; + break; + case BTA_BUSY: + esp_status = ESP_BT_STATUS_BUSY; + break; + case BTA_NO_RESOURCES: + esp_status = ESP_BT_STATUS_NOMEM; + break; + case BTA_WRONG_MODE: + esp_status = ESP_BT_STATUS_NOT_READY; + break; + case BTA_EIR_TOO_LARGE: + esp_status = ESP_BT_STATUS_EIR_TOO_LARGE; + break; + default: + esp_status = ESP_BT_STATUS_FAIL; + break; + } + + return esp_status; +} diff --git a/lib/bt/host/bluedroid/btc/include/btc/btc_ble_storage.h b/lib/bt/host/bluedroid/btc/include/btc/btc_ble_storage.h new file mode 100644 index 00000000..2cfb38b8 --- /dev/null +++ b/lib/bt/host/bluedroid/btc/include/btc/btc_ble_storage.h @@ -0,0 +1,113 @@ +// Copyright (C) 2014 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#ifndef __BTC_BLE_STORAGE_H__ +#define __BTC_BLE_STORAGE_H__ +#include "stack/bt_types.h" +#include "common/bt_target.h" +#include "esp_gap_ble_api.h" +#include "bta/bta_api.h" + +#if (SMP_INCLUDED == TRUE) +#define BTC_LE_LOCAL_KEY_IR (1<<0) +#define BTC_LE_LOCAL_KEY_IRK (1<<1) +#define BTC_LE_LOCAL_KEY_DHK (1<<2) +#define BTC_LE_LOCAL_KEY_ER (1<<3) + +#define BTC_BLE_STORAGE_DEV_TYPE_STR "DevType" +#define BTC_BLE_STORAGE_ADDR_TYPE_STR "AddrType" +#define BTC_BLE_STORAGE_LINK_KEY_STR "LinkKey" +#define BTC_BLE_STORAGE_LE_KEY_PENC_STR "LE_KEY_PENC" +#define BTC_BLE_STORAGE_LE_KEY_PID_STR "LE_KEY_PID" +#define BTC_BLE_STORAGE_LE_KEY_PCSRK_STR "LE_KEY_PCSRK" +#define BTC_BLE_STORAGE_LE_KEY_LENC_STR "LE_KEY_LENC" +#define BTC_BLE_STORAGE_LE_KEY_LID_STR "LE_KEY_LID" +#define BTC_BLE_STORAGE_LE_KEY_LCSRK_STR "LE_KEY_LCSRK" +#define BTC_BLE_STORAGE_LE_AUTH_MODE_STR "AuthMode" + +#define BTC_BLE_STORAGE_LOCAL_ADAPTER_STR "Adapter" +#define BTC_BLE_STORAGE_LE_LOCAL_KEY_IR_STR "LE_LOCAL_KEY_IR" +#define BTC_BLE_STORAGE_LE_LOCAL_KEY_IRK_STR "LE_LOCAL_KEY_IRK" +#define BTC_BLE_STORAGE_LE_LOCAL_KEY_DHK_STR "LE_LOCAL_KEY_DHK" +#define BTC_BLE_STORAGE_LE_LOCAL_KEY_ER_STR "LE_LOCAL_KEY_ER" + +/************************************************************************************ +** Local type definitions +************************************************************************************/ +typedef struct +{ + BT_OCTET16 sp_c; + BT_OCTET16 sp_r; + BD_ADDR oob_bdaddr; /* peer bdaddr*/ +} btc_dm_oob_cb_t; + + +void btc_storage_save(void); + +bt_status_t btc_storage_add_ble_bonding_key( bt_bdaddr_t *remote_bd_addr, char *key, uint8_t key_type, uint8_t key_length); + +bt_status_t btc_storage_get_ble_bonding_key(bt_bdaddr_t *remote_bd_addr, uint8_t key_type, char *key_value, int key_length); + +bt_status_t btc_storage_remove_ble_bonding_keys(bt_bdaddr_t *remote_bd_addr); + +bool btc_storage_compare_address_key_value(bt_bdaddr_t *remote_bd_addr, uint8_t key_type, void *key_value, int key_length); + +bt_status_t _btc_storage_in_fetch_bonded_ble_device(const char *remote_bd_addr, int add); + +bt_status_t btc_storage_add_ble_local_key(char *key, uint8_t key_type, uint8_t key_length); + +bt_status_t btc_storage_remove_ble_local_keys(void); + +bt_status_t btc_storage_get_ble_local_key(uint8_t key_type, char *key_value, int key_len); + +bt_status_t btc_storage_set_ble_dev_auth_mode(bt_bdaddr_t *remote_bd_addr, uint8_t auth_mode, bool flush); + +bt_status_t btc_storage_get_ble_dev_auth_mode(bt_bdaddr_t *remote_bd_addr, int* auth_mode); + +bt_status_t btc_storage_remove_ble_dev_auth_mode(bt_bdaddr_t *remote_bd_addr, bool flush); + +bt_status_t btc_storage_get_remote_addr_type(bt_bdaddr_t *remote_bd_addr, int *addr_type); + +bt_status_t btc_storage_set_remote_addr_type(bt_bdaddr_t *remote_bd_addr, uint8_t addr_type, bool flush); + +bt_status_t btc_storage_remove_remote_addr_type(bt_bdaddr_t *remote_bd_addr, bool flush); + +bt_status_t btc_storage_set_ble_dev_type(bt_bdaddr_t *bd_addr, bool flush); + +bt_status_t btc_storage_remove_ble_dev_type(bt_bdaddr_t *remote_bd_addr, bool flush); + +bt_status_t btc_storage_get_bonded_ble_devices_list(esp_ble_bond_dev_t *bond_dev, int dev_num); + +int btc_storage_get_num_ble_bond_devices(void); + +void btc_storage_delete_duplicate_ble_devices(void); + +void btc_storage_remove_unused_sections(uint8_t *cur_addr, tBTM_LE_PID_KEYS *del_pid_key); + +#endif ///SMP_INCLUDED == TRUE + +#define BTC_BLE_STORAGE_GATT_CL_SUPP_FEAT_STR "GATT_CL_SUPP_FEAT" +#define BTC_BLE_STORAGE_GATT_DB_HASH_STR "GATT_DB_HASH" + +bt_status_t btc_storage_get_gatt_cl_supp_feat(bt_bdaddr_t *remote_bd_addr, uint8_t *value, int len); + +bt_status_t btc_storage_set_gatt_cl_supp_feat(bt_bdaddr_t *remote_bd_addr, uint8_t *value, int len); + +bt_status_t btc_storage_remove_gatt_cl_supp_feat(bt_bdaddr_t *remote_bd_addr); + +bt_status_t btc_storage_get_gatt_db_hash(bt_bdaddr_t *remote_bd_addr, uint8_t *value, int len); + +bt_status_t btc_storage_set_gatt_db_hash(bt_bdaddr_t *remote_bd_addr, uint8_t *value, int len); + +bt_status_t btc_storage_remove_gatt_db_hash(bt_bdaddr_t *remote_bd_addr); +#endif ///__BTC_BLE_STORAGE_H__ diff --git a/lib/bt/host/bluedroid/btc/include/btc/btc_common.h b/lib/bt/host/bluedroid/btc/include/btc/btc_common.h new file mode 100644 index 00000000..2f0a64c3 --- /dev/null +++ b/lib/bt/host/bluedroid/btc/include/btc/btc_common.h @@ -0,0 +1,27 @@ +/* + * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + + +#ifndef __BTC_COMMON_H__ +#define __BTC_COMMON_H__ + +#include +#include "common/bt_trace.h" +#include "stack/bt_types.h" +#include "osi/osi.h" + +#define BTC_ASSERTC(cond, msg, val) assert(cond && msg) + +#define BTC_HAL_CBACK(P_CB, P_CBACK, ...)\ + if (P_CB && P_CB->P_CBACK) { \ + LOG_INFO("HAL %s->%s", #P_CB, #P_CBACK); \ + P_CB->P_CBACK(__VA_ARGS__); \ + } \ + else { \ + BTC_ASSERTC(0, "Callback is NULL", 0); \ + } + +#endif /* __BTC_COMMON_H__ */ diff --git a/lib/bt/host/bluedroid/btc/include/btc/btc_config.h b/lib/bt/host/bluedroid/btc/include/btc/btc_config.h new file mode 100644 index 00000000..8467ced8 --- /dev/null +++ b/lib/bt/host/bluedroid/btc/include/btc/btc_config.h @@ -0,0 +1,52 @@ +/* + * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __BTC_CONFIG_H__ +#define __BTC_CONFIG_H__ + +#include +#include + +#include "stack/bt_types.h" + +typedef struct btc_config_section_iter_t btc_config_section_iter_t; + +bool btc_config_init(void); +bool btc_config_shut_down(void); +bool btc_config_clean_up(void); + +bool btc_config_has_section(const char *section); +bool btc_config_exist(const char *section, const char *key); +bool btc_config_get_int(const char *section, const char *key, int *value); +bool btc_config_set_int(const char *section, const char *key, int value); +bool btc_config_get_str(const char *section, const char *key, char *value, int *size_bytes); +bool btc_config_set_str(const char *section, const char *key, const char *value); +bool btc_config_get_bin(const char *section, const char *key, uint8_t *value, size_t *length); +bool btc_config_set_bin(const char *section, const char *key, const uint8_t *value, size_t length); +bool btc_config_remove(const char *section, const char *key); +bool btc_config_remove_section(const char *section); +bool btc_config_update_newest_section(const char *section); + +size_t btc_config_get_bin_length(const char *section, const char *key); + +const btc_config_section_iter_t *btc_config_section_begin(void); +const btc_config_section_iter_t *btc_config_section_end(void); +const btc_config_section_iter_t *btc_config_section_next(const btc_config_section_iter_t *section); +const char *btc_config_section_name(const btc_config_section_iter_t *section); + +void btc_config_flush(void); +int btc_config_clear(void); + +// TODO(zachoverflow): Eww...we need to move these out. These are peer specific, not config general. +bool btc_get_address_type(const BD_ADDR bd_addr, int *p_addr_type); +bool btc_compare_address_key_value(const char *section, const char *key_type, void *key_value, int key_length); +bool btc_get_device_type(const BD_ADDR bd_addr, int *p_device_type); + +void btc_config_lock(void); +void btc_config_unlock(void); + +int btc_config_file_path_update(const char *file_path); +#endif diff --git a/lib/bt/host/bluedroid/btc/include/btc/btc_dev.h b/lib/bt/host/bluedroid/btc/include/btc/btc_dev.h new file mode 100644 index 00000000..4cf085d7 --- /dev/null +++ b/lib/bt/host/bluedroid/btc/include/btc/btc_dev.h @@ -0,0 +1,40 @@ +/* + * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __BTC_DEV_H__ +#define __BTC_DEV_H__ + +#include "esp_bt_defs.h" +#include "esp_bt_device.h" +#include "btc/btc_task.h" + +typedef enum { + BTC_DEV_ACT_SET_DEVICE_NAME, +#if (ESP_COEX_VSC_INCLUDED == TRUE) + BTC_DEV_ACT_CFG_COEX_STATUS, +#endif +} btc_dev_act_t; + +/* btc_dev_args_t */ +typedef union { + // BTC_BT_GAP_ACT_SET_DEV_NAME + struct set_bt_dev_name_args { + char *device_name; + } set_dev_name; + +#if (ESP_COEX_VSC_INCLUDED == TRUE) + // BTC_DEV_ACT_CFG_COEX_STATUS + struct cfg_bt_dev_coex_st_args { + esp_bt_dev_coex_type_t type; + esp_bt_dev_coex_op_t op; + uint8_t status; + } cfg_coex_status; +#endif +} btc_dev_args_t; + +void btc_dev_call_handler(btc_msg_t *msg); + +#endif /* __BTC_DEV_H__ */ diff --git a/lib/bt/host/bluedroid/btc/include/btc/btc_dm.h b/lib/bt/host/bluedroid/btc/include/btc/btc_dm.h new file mode 100644 index 00000000..4b4d7134 --- /dev/null +++ b/lib/bt/host/bluedroid/btc/include/btc/btc_dm.h @@ -0,0 +1,92 @@ +/* + * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __BTC_DM_H__ +#define __BTC_DM_H__ + +#include "btc/btc_task.h" +#include "esp_bt_defs.h" +#include "bta/bta_api.h" + +typedef enum { + BTC_DM_SEC_ACT +} btc_dm_sec_act_t; + +/* btc_dm_args_t */ +typedef union { + //BTC_DM_SEC_ACT + tBTA_DM_SEC sec; +} btc_dm_sec_args_t; + +typedef struct +{ + bool is_penc_key_rcvd; + tBTM_LE_PENC_KEYS penc_key; /* received peer encryption key */ + bool is_pcsrk_key_rcvd; + tBTM_LE_PCSRK_KEYS pcsrk_key; /* received peer device SRK */ + bool is_pid_key_rcvd; + tBTM_LE_PID_KEYS pid_key; /* peer device ID key */ + bool is_lenc_key_rcvd; + tBTM_LE_LENC_KEYS lenc_key; /* local encryption reproduction keys LTK = = d1(ER,DIV,0)*/ + bool is_lcsrk_key_rcvd; + tBTM_LE_LCSRK_KEYS lcsrk_key; /* local device CSRK = d1(ER,DIV,1)*/ + bool is_lidk_key_rcvd; /* local identity key received */ +} btc_dm_ble_cb_t; + +typedef struct +{ + bt_bdaddr_t static_bdaddr; + BD_ADDR bd_addr; + btc_dm_ble_cb_t ble; +} btc_dm_pairing_cb_t; + +typedef struct +{ + uint8_t ir[BT_OCTET16_LEN]; + uint8_t irk[BT_OCTET16_LEN]; + uint8_t dhk[BT_OCTET16_LEN]; +} btc_dm_local_key_id_t; + +typedef struct +{ + bool is_er_rcvd; + uint8_t er[BT_OCTET16_LEN]; + bool is_id_keys_rcvd; + btc_dm_local_key_id_t id_keys; /* ID kyes */ +} btc_dm_local_key_cb_t; + +typedef struct +{ + tBTA_SERVICE_MASK btc_enabled_services; +#if (SMP_INCLUDED == TRUE) + btc_dm_pairing_cb_t pairing_cb; + btc_dm_local_key_cb_t ble_local_key_cb; +#endif +} btc_dm_cb_t; + +#if BTC_DYNAMIC_MEMORY == FALSE +extern btc_dm_cb_t btc_dm_cb; +#else +extern btc_dm_cb_t *btc_dm_cb_ptr; +#define btc_dm_cb (*btc_dm_cb_ptr) +#endif + +// void btc_dm_call_handler(btc_msg_t *msg); +void btc_dm_sec_evt(tBTA_DM_SEC_EVT event, tBTA_DM_SEC *data); +void btc_dm_sec_cb_handler(btc_msg_t *msg); +void btc_dm_sec_arg_deep_copy(btc_msg_t *msg, void *dst, void *src); + +bt_status_t btc_dm_enable_service(tBTA_SERVICE_ID service_id); +bt_status_t btc_dm_disable_service(tBTA_SERVICE_ID service_id); + +#if (SMP_INCLUDED == TRUE) +void btc_dm_load_ble_local_keys(void); + +void btc_dm_get_ble_local_keys(tBTA_DM_BLE_LOCAL_KEY_MASK *p_key_mask, BT_OCTET16 er, + tBTA_BLE_LOCAL_ID_KEYS *p_id_keys); +#endif + +#endif /* __BTC_DM_H__ */ diff --git a/lib/bt/host/bluedroid/btc/include/btc/btc_main.h b/lib/bt/host/bluedroid/btc/include/btc/btc_main.h new file mode 100644 index 00000000..935dc77e --- /dev/null +++ b/lib/bt/host/bluedroid/btc/include/btc/btc_main.h @@ -0,0 +1,68 @@ +/* + * SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __BTC_BT_MAIN_H__ +#define __BTC_BT_MAIN_H__ + +#include "osi/future.h" +#include "stack/bt_types.h" +#include "bta/bta_api.h" +#include "btc/btc_main.h" +#include "btc/btc_task.h" + +typedef enum { + BTC_MAIN_ACT_INIT = 0, + BTC_MAIN_ACT_DEINIT, + BTC_MAIN_ACT_ENABLE, + BTC_MAIN_ACT_DISABLE, +} btc_main_act_t; + +typedef enum { + BTC_MAIN_INIT_FUTURE = 0, + BTC_MAIN_DEINIT_FUTURE, + BTC_MAIN_ENABLE_FUTURE, + BTC_MAIN_DISABLE_FUTURE, + BTC_MAIN_FUTURE_NUM, +} btc_main_future_type_t; + +#define BTC_BLE_STATUS_IDLE 0 +typedef enum { + BTC_BLE_STATUS_ADV = 0, // Advertising exist + BTC_BLE_STATUS_SCAN, // Scanning exist + BTC_BLE_STATUS_CONN, // Connection exist + BTC_BLE_STATUS_DEV, // Device record exist + BTC_BLE_STATUS_BOND, // Bond info exist + BTC_BLE_STATUS_GATTC_CACHE, // GATTC cache exist + BTC_BLE_STATUS_GATTC_APP, // GATTC application exist + BTC_BLE_STATUS_GATTS_SRVC, // GATTS service exist +} tBTC_BLE_STATUS; + +future_t **btc_main_get_future_p(btc_main_future_type_t type); + +#if 0 +typedef union { + struct btc_main_init_args { + future_t *future; + } init; + struct btc_main_deinit_args { + future_t *future; + } deinit; + struct btc_main_init_args { + future_t *future; + } enable; + struct btc_main_init_args { + future_t *future; + } disable; +} btc_main_args_t; + +bt_status_t btc_enable_bluetooth(future_t *future); +void btc_disable_bluetooth(future_t *future); +bt_status_t btc_init_bluetooth(future_t *future); +void btc_deinit_bluetooth(future_t *future); +#endif + +void btc_main_call_handler(btc_msg_t *msg); +#endif /* __BTC_BT_MAIN_H__ */ diff --git a/lib/bt/host/bluedroid/btc/include/btc/btc_profile_queue.h b/lib/bt/host/bluedroid/btc/include/btc/btc_profile_queue.h new file mode 100644 index 00000000..1a082f9b --- /dev/null +++ b/lib/bt/host/bluedroid/btc/include/btc/btc_profile_queue.h @@ -0,0 +1,47 @@ +/* + * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/******************************************************************************* + * + * Filename: btc/btc_profile_queue.h + * + * Description: Bluetooth remote device connection queuing + * + *******************************************************************************/ + +#ifndef __BTC_PROFILE_QUEUE_H__ +#define __BTC_PROFILE_QUEUE_H__ + +#include "common/bt_defs.h" +#include "btc/btc_task.h" + +typedef enum { + BTC_PRF_QUE_CONNECT = 0, + BTC_PRF_QUE_ADVANCE +} btc_prf_que_act_t; + +typedef bt_status_t (*btc_connect_cb_t) (bt_bdaddr_t *bda, uint16_t uuid); + +typedef struct connect_node_t { + bt_bdaddr_t bda; + uint16_t uuid; + bool busy; + btc_connect_cb_t connect_cb; +} connect_node_t; + +/* btc_prf_que_args_t */ +typedef union { + // BTC_PRF_QUE_CONNECT + connect_node_t connect_node; +} btc_prf_que_args_t; + +bt_status_t btc_queue_connect(uint16_t uuid, const bt_bdaddr_t *bda, btc_connect_cb_t connect_cb); +void btc_queue_advance(void); +bt_status_t btc_queue_connect_next(void); +void btc_queue_release(void); + +void btc_profile_queue_handler(btc_msg_t *msg); +#endif /* __BTC_PROFILE_QUEUE_H__ */ diff --git a/lib/bt/host/bluedroid/btc/include/btc/btc_sm.h b/lib/bt/host/bluedroid/btc/include/btc/btc_sm.h new file mode 100644 index 00000000..8ac34583 --- /dev/null +++ b/lib/bt/host/bluedroid/btc/include/btc/btc_sm.h @@ -0,0 +1,107 @@ +/* + * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + + +/***************************************************************************** + * + * Filename: btc/btc_sm.h + * + * Description: Generic BTC state machine API + * + *****************************************************************************/ + +#ifndef __BTC_SM_H__ +#define __BTC_SM_H__ + +/***************************************************************************** +** Constants & Macros +******************************************************************************/ + +/* Generic Enter/Exit state machine events */ +#define BTC_SM_ENTER_EVT 0xFFFF +#define BTC_SM_EXIT_EVT 0xFFFE + + +/***************************************************************************** +** Type definitions and return values +******************************************************************************/ +typedef UINT32 btc_sm_state_t; +typedef UINT32 btc_sm_event_t; +typedef void *btc_sm_handle_t; +typedef BOOLEAN (* btc_sm_handler_t)(btc_sm_event_t event, void *data); + + +/***************************************************************************** +** Functions +** +** NOTE: THESE APIs SHOULD BE INVOKED ONLY IN THE BTC CONTEXT +** +******************************************************************************/ + +/***************************************************************************** +** +** Function btc_sm_init +** +** Description Initializes the state machine with the state handlers +** The caller should ensure that the table and the corresponding +** states match. The location that 'p_handlers' points to shall +** be available until the btc_sm_shutdown API is invoked. +** +** Returns Returns a pointer to the initialized state machine handle. +** +******************************************************************************/ +btc_sm_handle_t btc_sm_init(const btc_sm_handler_t *p_handlers, + btc_sm_state_t initial_state); + +/***************************************************************************** +** +** Function btc_sm_shutdown +** +** Description Tears down the state machine +** +** Returns None +** +******************************************************************************/ +void btc_sm_shutdown(btc_sm_handle_t handle); + +/***************************************************************************** +** +** Function btc_sm_get_state +** +** Description Fetches the current state of the state machine +** +** Returns Current state +** +******************************************************************************/ +btc_sm_state_t btc_sm_get_state(btc_sm_handle_t handle); + +/***************************************************************************** +** +** Function btc_sm_dispatch +** +** Description Dispatches the 'event' along with 'data' to the current state handler +** +** Returns Returns BT_STATUS_OK on success, BT_STATUS_FAIL otherwise +** +******************************************************************************/ +bt_status_t btc_sm_dispatch(btc_sm_handle_t handle, btc_sm_event_t event, + void *data); + +/***************************************************************************** +** +** Function btc_sm_change_state +** +** Description Make a transition to the new 'state'. The 'BTC_SM_EXIT_EVT' +** shall be invoked before exiting the current state. The +** 'BTC_SM_ENTER_EVT' shall be invoked before entering the new state +** +** +** Returns Returns BT_STATUS_OK on success, BT_STATUS_FAIL otherwise +** +******************************************************************************/ +bt_status_t btc_sm_change_state(btc_sm_handle_t handle, btc_sm_state_t state); + +#endif /* __BTC_SM_H__ */ diff --git a/lib/bt/host/bluedroid/btc/include/btc/btc_storage.h b/lib/bt/host/bluedroid/btc/include/btc/btc_storage.h new file mode 100644 index 00000000..05c05430 --- /dev/null +++ b/lib/bt/host/bluedroid/btc/include/btc/btc_storage.h @@ -0,0 +1,197 @@ +/* + * SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __BTC_STORAGE_H__ +#define __BTC_STORAGE_H__ + +#include +#include "common/bt_defs.h" +#include "stack/bt_types.h" +#include "esp_gap_bt_api.h" + + +#define BTC_STORAGE_DEV_CLASS_STR "DevClass" +#define BTC_STORAGE_LINK_KEY_STR "LinkKey" /* same as the ble */ +#define BTC_STORAGE_LINK_KEY_TYPE_STR "LinkKeyType" +#define BTC_STORAGE_PIN_LENGTH_STR "PinLength" +#define BTC_STORAGE_SC_SUPPORT "SCSupport" + +#ifdef __cplusplus +extern "C" { +#endif + +/******************************************************************************* +** +** Function btc_storage_add_bonded_device +** +** Description BTC storage API - Adds the newly bonded device to NVRAM +** along with the link-key, Key type and Pin key length +** +** Returns BT_STATUS_SUCCESS if the store was successful, +** BT_STATUS_FAIL otherwise +** +*******************************************************************************/ +bt_status_t btc_storage_add_bonded_device(bt_bdaddr_t *remote_bd_addr, + LINK_KEY link_key, + uint8_t key_type, + uint8_t pin_length, + BOOLEAN sc_support); + +/******************************************************************************* +** +** Function btc_storage_remove_bonded_device +** +** Description BTC storage API - Deletes the bonded device from NVRAM +** +** Returns BT_STATUS_SUCCESS if the deletion was successful, +** BT_STATUS_FAIL otherwise +** +*******************************************************************************/ +bt_status_t btc_storage_remove_bonded_device(bt_bdaddr_t *remote_bd_addr); + +/******************************************************************************* +** +** Function btc_storage_load_bonded_devices +** +** Description BTC storage API - Loads all the bonded devices from NVRAM +** and adds to the BTA. +** Additionally, this API also invokes the adaper_properties_cb +** and remote_device_properties_cb for each of the bonded devices. +** +** Returns BT_STATUS_SUCCESS if successful, BT_STATUS_FAIL otherwise +** +*******************************************************************************/ +bt_status_t btc_storage_load_bonded_devices(void); + +/******************************************************************************* +** +** Function btc_storage_get_num_bt_bond_devices +** +** Description BTC storage API - get the num of the bonded device from NVRAM +** +** Returns the num of the bonded device +** +*******************************************************************************/ +int btc_storage_get_num_bt_bond_devices(void); + +/******************************************************************************* +** +** Function btc_storage_get_bonded_bt_devices_list +** +** Description BTC storage API - get the list of the bonded device from NVRAM +** +** Returns BT_STATUS_SUCCESS if get the list successful, +** BT_STATUS_FAIL otherwise +** +*******************************************************************************/ +bt_status_t btc_storage_get_bonded_bt_devices_list(bt_bdaddr_t *bond_dev, int *dev_num); + +/******************************************************************************* +** +** Function btc_storage_get_num_all_bond_devices +** +** Description BTC storage API - get all the num of the bonded device from NVRAM +** +** Returns the num of the bonded device +** +*******************************************************************************/ +int btc_storage_get_num_all_bond_devices(void); + +/******************************************************************************* +** +** Function btc_storage_update_active_device +** +** Description BTC storage API - Once an ACL link is established and remote +** bd_addr is already stored in NVRAM, update the config and update +** the remote device to be the newest active device. The updates will +** not be stored into NVRAM immediately. +** +** Returns BT_STATUS_SUCCESS if successful, BT_STATUS_FAIL otherwise +** +*******************************************************************************/ +bool btc_storage_update_active_device(bt_bdaddr_t *remote_bd_addr); + +#if (defined BTC_HH_INCLUDED && BTC_HH_INCLUDED == TRUE) +/******************************************************************************* + * + * Function btc_storage_add_hid_device_info + * + * Description BTC storage API - Adds the hid information of bonded hid + * devices-to NVRAM + * + * Returns BT_STATUS_SUCCESS if the store was successful, + * BT_STATUS_FAIL otherwise + * + ******************************************************************************/ + +bt_status_t btc_storage_add_hid_device_info(bt_bdaddr_t *remote_bd_addr, uint16_t attr_mask, uint8_t sub_class, + uint8_t app_id, uint16_t vendor_id, uint16_t product_id, uint16_t version, + uint8_t ctry_code, uint16_t ssr_max_latency, uint16_t ssr_min_tout, + uint16_t dl_len, uint8_t *dsc_list); + +/******************************************************************************* + * + * Function btc_storage_load_bonded_hid_info + * + * Description BTIF storage API - Loads hid info for all the bonded devices + * from NVRAM and adds those devices to the BTA_HH. + * + * Returns BT_STATUS_SUCCESS if successful, BT_STATUS_FAIL otherwise + * + ******************************************************************************/ +bt_status_t btc_storage_load_bonded_hid_info(void); + +/******************************************************************************* + * + * Function btc_storage_remove_hid_info + * + * Description BTC storage API - Deletes the bonded hid device info from + * NVRAM + * + * Returns BT_STATUS_SUCCESS if the deletion was successful, + * BT_STATUS_FAIL otherwise + * + ******************************************************************************/ +bt_status_t btc_storage_remove_hid_info(bt_bdaddr_t *remote_bd_addr); +#endif // (defined BTC_HH_INCLUDED && BTC_HH_INCLUDED == TRUE) + +#if (defined BTC_HD_INCLUDED && BTC_HD_INCLUDED == TRUE) +/******************************************************************************* + * Function btc_storage_load_hidd + * + * Description Loads hidd bonded device and "plugs" it into hidd + * + * Returns BT_STATUS_SUCCESS if successful, BT_STATUS_FAIL otherwise + * + ******************************************************************************/ +bt_status_t btc_storage_load_hidd(void); + +/******************************************************************************* + * + * Function btc_storage_set_hidd + * + * Description Stores hidd bonded device info in nvram. + * + * Returns BT_STATUS_SUCCESS + * + ******************************************************************************/ +bt_status_t btc_storage_set_hidd(bt_bdaddr_t *remote_bd_addr); + +/******************************************************************************* + * + * Function btc_storage_remove_hidd + * + * Description Removes hidd bonded device info from nvram + * + * Returns BT_STATUS_SUCCESS + * + ******************************************************************************/ +bt_status_t btc_storage_remove_hidd(bt_bdaddr_t *remote_bd_addr); +#endif //(defined BTC_HD_INCLUDED && BTC_HD_INCLUDED == TRUE) +#endif /* BTC_STORAGE_H */ +#ifdef __cplusplus +} +#endif diff --git a/lib/bt/host/bluedroid/btc/include/btc/btc_util.h b/lib/bt/host/bluedroid/btc/include/btc/btc_util.h new file mode 100644 index 00000000..deb55990 --- /dev/null +++ b/lib/bt/host/bluedroid/btc/include/btc/btc_util.h @@ -0,0 +1,67 @@ +/* + * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __BTC_UTIL_H__ +#define __BTC_UTIL_H__ + +#include +#include "stack/bt_types.h" +#include "common/bt_defs.h" +#include "esp_bt_defs.h" +#include "esp_hf_defs.h" + +/******************************************************************************* +** Constants & Macros +********************************************************************************/ +#define CASE_RETURN_STR(const) case const: return #const; + +/******************************************************************************* +** Type definitions for callback functions +********************************************************************************/ +typedef char bdstr_t[18]; + +#ifdef __cplusplus +extern "C" { +#endif +/******************************************************************************* +** Functions +********************************************************************************/ +#if(BTA_AV_INCLUDED == TRUE) +const char *dump_rc_event(UINT8 event); +const char *dump_rc_notification_event_id(UINT8 event_id); +const char *dump_rc_pdu(UINT8 pdu); +#endif + +#if(BTA_AG_INCLUDED == TRUE) +const char *dump_hf_conn_state(UINT16 event); +const char *dump_hf_event(UINT16 event); +const char *dump_hf_call_state(esp_hf_call_status_t call_state); +const char* dump_hf_call_setup_state(esp_hf_call_setup_status_t call_setup_state); +#endif + +#if(BTA_HD_INCLUDED == TRUE) +const char* dump_hd_event(uint16_t event); +#endif + +#if(BTA_HH_INCLUDED == TRUE) +const char* dump_hh_event(uint16_t event); +#endif + +UINT32 devclass2uint(DEV_CLASS dev_class); +void uint2devclass(UINT32 dev, DEV_CLASS dev_class); +void uuid128_be_to_esp_uuid(esp_bt_uuid_t *u, uint8_t* uuid128); + +void uuid_to_string_legacy(bt_uuid_t *p_uuid, char *str); + +esp_bt_status_t btc_hci_to_esp_status(uint8_t hci_status); +esp_bt_status_t btc_btm_status_to_esp_status (uint8_t btm_status); +esp_bt_status_t btc_bta_status_to_esp_status (uint8_t bta_status); + +#ifdef __cplusplus +} +#endif + +#endif /* __BTC_UTIL_H__ */ diff --git a/lib/bt/host/bluedroid/btc/profile/esp/ble_button/button_pro.c b/lib/bt/host/bluedroid/btc/profile/esp/ble_button/button_pro.c new file mode 100644 index 00000000..11126e08 --- /dev/null +++ b/lib/bt/host/bluedroid/btc/profile/esp/ble_button/button_pro.c @@ -0,0 +1,334 @@ +/* + * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include +#include +#include + + +#include "common/bt_target.h" +#include "common/bt_trace.h" +#include "stack/bt_types.h" +#include "stack/gatt_api.h" +#include "bta/bta_api.h" +#include "bta/bta_gatt_api.h" +#include "bta_gatts_int.h" +#include "button_pro.h" + +#include "prf_defs.h" + +#if (BUT_PROFILE_CFG) + + +#define ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0])) + +button_env_cb_t button_cb_env; + + + + +/***************************************************************************** +** Constants +*****************************************************************************/ +static void button_profile_cb(esp_gatts_evt_t event, esp_gatts_t *p_data); + + +/******************************************************************************* +** +** Function button_profile_cb +** +** Description the callback function after the profile has been register to the BTA manager module +** +** Returns NULL +** +*******************************************************************************/ +static void button_profile_cb(esp_gatts_evt_t event, esp_gatts_t *p_data) +{ + esp_gatts_rsp_t rsp; + esp_bt_uuid_t uuid = {LEN_UUID_16, {ATT_SVC_BUTTON}}; + but_inst_t *p_inst = &button_cb_env.button_inst; + uint8_t net_event = 0xff; + uint8_t len = 0; + uint8_t *p_rec_data = NULL; + //BTC_TRACE_ERROR("p_data->status = %x\n",p_data->status); + //if(p_data->status != BTA_GATT_OK){ + // BTC_TRACE_ERROR("button profile register failed\n"); + // return; + //} + BTC_TRACE_ERROR("button profile cb event = %x\n", event); + switch (event) { + case ESP_GATTS_REG_EVT: + + BTC_TRACE_ERROR("p_data->reg_oper.status = %x\n", p_data->reg_oper.status); + BTC_TRACE_ERROR("(p_data->reg_oper.uuid.uu.uuid16=%x\n", p_data->reg_oper.uuid.uu.uuid16); + if (p_data->reg_oper.status != BTA_GATT_OK) { + BTC_TRACE_ERROR("button profile register failed\n"); + } + button_cb_env.gatt_if = p_data->reg_oper.server_if; + button_cb_env.enabled = true; + //button_cb_env.button_inst.app_id = p_data->reg_oper.uuid; + //create the button service to the service data base. + if (p_data->reg_oper.uuid.uu.uuid16 == ATT_SVC_BUTTON) { + Button_CreateService(); + } + break; + case ESP_GATTS_READ_EVT: + //tBTA_GATTS_RSP rsp; + memset(&rsp, 0, sizeof(tBTA_GATTS_API_RSP)); + rsp.attr_value.handle = p_data->req_data.p_data->read_req.handle; + rsp.attr_value.len = 2; + esp_ble_gatts_send_rsp(p_data->req_data.conn_id, p_data->req_data.trans_id, + p_data->req_data.status, &rsp); + break; + case ESP_GATTS_WRITE_EVT: + esp_ble_gatts_send_rsp(p_data->req_data.conn_id, p_data->req_data.trans_id, + p_data->req_data.status, NULL); + BTC_TRACE_ERROR("Received button data:"); + for (int i = 0; i < p_data->req_data.p_data->write_req.len; i++) { + BTC_TRACE_ERROR("%x", p_data->req_data.p_data->write_req.value[i]); + } + BTC_TRACE_ERROR("\n"); + if (p_data->req_data.p_data->write_req.handle == button_cb_env.button_inst.but_wirt_hdl) { + + p_rec_data = &p_data->req_data.p_data->write_req.value[0]; + // button_msg_notify(len,p_rec_data); + (*p_inst->p_cback)(button_cb_env.button_inst.app_id, net_event, len, p_rec_data); + + } + break; + case ESP_GATTS_CFM_EVT: + + break; + case ESP_GATTS_CREATE_EVT: + //tBT_UUID uuid_butt_write; + uuid.uu.uuid16 = ATT_CHAR_BUTTON_WIT; + //tBTA_GATT_PERM perm = (GATT_PERM_WRITE|GATT_PERM_READ); + //tBTA_GATT_CHAR_PROP prop = (GATT_CHAR_PROP_BIT_READ|GATT_CHAR_PROP_BIT_WRITE); + //uuid = {LEN_UUID_16, {ATT_SVC_BUTTON}}; + button_cb_env.clcb.cur_srvc_id = p_data->create.service_id; + button_cb_env.is_primery = p_data->create.is_primary; + //uuid = {LEN_UUID_16, {ATT_CHAR_BUTTON_WIT}}; + //start the button service after created + esp_ble_gatts_start_srvc(p_data->create.service_id); + //add the frist button characteristic --> write characteristic + esp_ble_gatts_add_char(button_cb_env.clcb.cur_srvc_id, &uuid, + (GATT_PERM_WRITE | GATT_PERM_READ), + (GATT_CHAR_PROP_BIT_READ | GATT_CHAR_PROP_BIT_WRITE)); + break; + + case ESP_GATTS_ADD_CHAR_EVT: + if (p_data->add_result.char_uuid.uu.uuid16 == ATT_CHAR_BUTTON_WIT) { + uuid.uu.uuid16 = ATT_CHAR_BUTTON_NTF; + //tBTA_GATT_PERM perm = GATT_PERM_READ; + tBTA_GATT_CHAR_PROP prop = (GATT_CHAR_PROP_BIT_READ | GATT_CHAR_PROP_BIT_NOTIFY); + //save the att handle to the env + button_cb_env.button_inst.but_wirt_hdl = p_data->add_result.attr_id; + //add the frist button characteristic --> Notify characteristic + esp_ble_gatts_add_char(button_cb_env.clcb.cur_srvc_id, &uuid, + GATT_PERM_READ, (GATT_CHAR_PROP_BIT_READ | GATT_CHAR_PROP_BIT_NOTIFY)); + } else if (p_data->add_result.char_uuid.uu.uuid16 == ATT_CHAR_BUTTON_NTF) { // add the gattc config descriptor to the notify charateristic + //tBTA_GATT_PERM perm = (GATT_PERM_WRITE|GATT_PERM_WRITE); + uuid.uu.uuid16 = GATT_UUID_CHAR_CLIENT_CONFIG; + button_cb_env.button_inst.but_ntf_hdl = p_data->add_result.attr_id; + esp_ble_gatts_add_char_descr (button_cb_env.clcb.cur_srvc_id, + (GATT_PERM_WRITE | GATT_PERM_WRITE), + &uuid); + } + + break; + case ESP_GATTS_ADD_CHAR_DESCR_EVT: + if (p_data->add_result.char_uuid.uu.uuid16 == GATT_UUID_CHAR_CLIENT_CONFIG) { + button_cb_env.button_inst.but_cfg_hdl = p_data->add_result.attr_id; + } + ///Start advertising + BTC_TRACE_ERROR("\n*******Start sent the ADV.*************\n"); + //esp_ble_start_advertising (&adv_params); + //BTA_GATTS_Listen(button_cb_env.gatt_if, true, NULL); + break; + case ESP_GATTS_CONNECT_EVT: + BTC_TRACE_ERROR("############BUTTON CONNCET EVT################\n"); + //esp_ble_stop_advertising(); + //set the connection flag to true + button_env_clcb_alloc(p_data->conn.conn_id, p_data->conn.remote_bda); + break; + case ESP_GATTS_DISCONNECT_EVT: + //set the connection flag to true + button_cb_env.clcb.connected = false; + break; + case ESP_GATTS_OPEN_EVT: + ///stop the advertising after connected + + break; + case ESP_GATTS_CLOSE_EVT: + if (button_cb_env.clcb.connected && (button_cb_env.clcb.conn_id == p_data->conn.conn_id)) { + //set the connection channal congested flag to true + button_cb_env.clcb.congest = p_data->congest.congested; + } + break; + case ESP_GATTS_CONGEST_EVT: + break; + default: + break; + } +} + + +/******************************************************************************* +** +** Function Button_CreateService +** +** Description Create a Service for the button profile +** +** Returns NULL +** +*******************************************************************************/ +void Button_CreateService(void) +{ + esp_gatts_if_t server_if ; + esp_bt_uuid_t uuid = {LEN_UUID_16, {ATT_SVC_BUTTON}}; + uint16_t num_handle = KEY_IDX_NB; + uint8_t inst = 0x00; + server_if = button_cb_env.gatt_if; + button_cb_env.inst_id = inst; + //if(!button_cb_env.enabled) + //{ + // BTC_TRACE_ERROR("button service added error."); + //} + esp_ble_gatts_create_srvc(server_if, &uuid, inst, num_handle, true); + +} + +/******************************************************************************* +** +** Function button_env_clcb_alloc +** +** Description The function allocates a GATT profile connection link control block +** +** Returns NULL if not found. Otherwise pointer to the connection link block. +** +*******************************************************************************/ +but_clcb_t *button_env_clcb_alloc (uint16_t conn_id, BD_ADDR remote_bda) +{ + but_clcb_t *p_clcb = NULL; + p_clcb = &button_cb_env.clcb; + + if (!p_clcb->in_use) { + p_clcb->in_use = TRUE; + p_clcb->conn_id = conn_id; + BTC_TRACE_ERROR("p_clcb->conn_id = %x\n", conn_id); + p_clcb->connected = TRUE; + memcpy(p_clcb->remote_bda, remote_bda, BD_ADDR_LEN); + } + + return p_clcb; +} + +/******************************************************************************* +** +** Function button_env_find_conn_id_by_bd_adddr +** +** Description The function searches all LCB with macthing bd address +** +** Returns total number of clcb found. +** +*******************************************************************************/ +uint16_t button_env_find_conn_id_by_bd_adddr(BD_ADDR remote_bda) +{ + uint8_t i_clcb; + but_clcb_t *p_clcb = NULL; + + for (i_clcb = 0, p_clcb = &button_cb_env.clcb; i_clcb < BUTT_MAX_APPS; i_clcb++, p_clcb++) { + if (p_clcb->in_use && p_clcb->connected && memcmp(p_clcb->remote_bda, remote_bda, BD_ADDR_LEN)) { + return p_clcb->conn_id; + } + } + + return GATT_INVALID_CONN_ID; +} + +/******************************************************************************* +** +** Function button_env_clcb_dealloc +** +** Description The function deallocates a GATT profile connection link control block +** +** Returns True the deallocation is successful +** +*******************************************************************************/ + +BOOLEAN button_env_clcb_dealloc(uint16_t conn_id) +{ + uint16_t i_clcb = 0; + but_clcb_t *p_clcb = NULL; + + for (i_clcb = 0, p_clcb = &button_cb_env.clcb; i_clcb < 1; i_clcb++, p_clcb++) { + if (p_clcb->in_use && p_clcb->connected && (p_clcb->conn_id == conn_id)) { + memset(p_clcb, 0, sizeof(but_clcb_t)); + return TRUE; + } + } + + return FALSE; +} + +/******************************************************************************* +** +** Function button_init +** +** Description Initializa the GATT Service for button profiles. +** +*******************************************************************************/ +esp_gatt_status_t button_init (but_prf_cb_t call_back) +{ + tBT_UUID app_uuid = {LEN_UUID_16, {ATT_SVC_BUTTON}}; + + BTC_TRACE_ERROR("\n=============================button_init==============================================\n"); + if (button_cb_env.enabled) { + BTC_TRACE_ERROR("button svc already initaliezd\n"); + return ESP_GATT_ERROR; + } else { + memset(&button_cb_env, 0, sizeof(button_env_cb_t)); + } + + + if (call_back != NULL) { + button_cb_env.button_inst.p_cback = call_back; + } + + + /* register the button profile to the BTA_GATTS module*/ + esp_ble_gatts_app_register(&app_uuid, button_profile_cb); + + button_cb_env.enabled = TRUE; + + return ESP_GATT_OK; +} + +void button_disable(uint16_t connid) +{ + button_env_clcb_dealloc(connid); +} + + +void button_msg_notify(uint16_t len, uint8_t *button_msg) +{ + BOOLEAN conn_status = button_cb_env.clcb.connected; + uint16_t conn_id = button_cb_env.clcb.conn_id; + uint16_t attr_id = button_cb_env.button_inst.but_ntf_hdl; + //notify rsp==false; indicate rsp==true. + BOOLEAN rsp = false; + if (!conn_status && button_cb_env.clcb.congest) { + BTC_TRACE_ERROR("the conneciton for button profile has been loss\n"); + return; + } + + esp_ble_gatts_hdl_val_indica (conn_id, attr_id, len, + button_msg, rsp); +} + +#endif ///BUT_PROFILE_CFG diff --git a/lib/bt/host/bluedroid/btc/profile/esp/include/button_pro.h b/lib/bt/host/bluedroid/btc/profile/esp/include/button_pro.h new file mode 100644 index 00000000..c509596d --- /dev/null +++ b/lib/bt/host/bluedroid/btc/profile/esp/include/button_pro.h @@ -0,0 +1,112 @@ +#include "prf_defs.h" +/* + * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + + +#if (BUT_PROFILE_CFG) +#include "common/bt_target.h" +#include "stack/gatt_api.h" +#include "stack/gattdefs.h" +#include "esp_gatt_api.h" + +#define KEY_SUCCESS GATT_SUCCESS +#define KEY_ILLEGAL_PARAM GATT_ILLEGAL_PARAMETER +#define KEY_NO_RESOURCES GATT_NO_RESOURCES + +//define the key serivce uuid +#define ATT_SVC_BUTTON 0xFFFF +//define the key Char uuid +#define ATT_CHAR_BUTTON_WIT 0xFF01 +#define ATT_CHAR_BUTTON_NTF 0xFF02 + +#define BUTTON_PRESS_NTF_CFG 0x01 + +#define BUTTON_VAL_MAX_LEN (10) + +#define BUTT_MAX_APPS GATT_CL_MAX_LCB + +#define BUT_MAX_STRING_DATA 7 + +typedef void (*but_prf_cb_t)(uint8_t app_id, uint8_t event, uint16_t len, uint8_t *value); + +#ifndef BUT_MAX_INT_NUM +#define BUT_MAX_INT_NUM 4 +#endif + +enum { + RECEIVE_NET_PASSWD_EVT, + RECEIVE_NET_SSD_EVT, + RECEIVE_EVT_MAX +}; + +/// button Service Attributes Indexes +enum { + KEY_IDX_SVC, + KEY_IDX_BUTTON_WIT_CHAR, + KEY_IDX_BUTTON_WIT_VAL, + KEY_IDX_BUTTON_NTF_CHAR, + KEY_IDX_BUTTON_NTF_VAL, + KEY_IDX_BUTTON_NTF_CFG, + + KEY_IDX_NB, +}; + +typedef struct { + BD_ADDR remote_bda; + BOOLEAN need_rsp; + uint16_t clt_cfg; +} but_write_data_t; + +typedef struct { + BOOLEAN in_use; + BOOLEAN congest; + uint16_t conn_id; + BOOLEAN connected; + BD_ADDR remote_bda; + uint32_t trans_id; + uint8_t cur_srvc_id; + +} but_clcb_t; + + +typedef struct { + uint8_t app_id; + uint16_t but_wirt_hdl; + uint16_t but_ntf_hdl; + uint16_t but_cfg_hdl; + + but_prf_cb_t p_cback; + +} but_inst_t; + + +/* service engine control block */ +typedef struct { + but_clcb_t clcb; /* connection link*/ + esp_gatt_if_t gatt_if; + BOOLEAN enabled; + BOOLEAN is_primery; + but_inst_t button_inst; + uint8_t inst_id; +} button_env_cb_t; + +void Button_CreateService(void); + +but_clcb_t *button_env_clcb_alloc(uint16_t conn_id, BD_ADDR bda); + +uint16_t button_env_find_conn_id_by_bd_adddr(BD_ADDR bda); + +BOOLEAN button_env_clcb_dealloc(uint16_t conn_id); + +esp_gatt_status_t button_init(but_prf_cb_t call_back); + +void button_disable(uint16_t connid); + +void button_msg_notify(uint16_t len, uint8_t *button_msg); + +extern button_env_cb_t button_cb_env; + +#endif ///BUT_PROFILE_CFG diff --git a/lib/bt/host/bluedroid/btc/profile/esp/include/wx_airsync_prf.h b/lib/bt/host/bluedroid/btc/profile/esp/include/wx_airsync_prf.h new file mode 100644 index 00000000..3cd74699 --- /dev/null +++ b/lib/bt/host/bluedroid/btc/profile/esp/include/wx_airsync_prf.h @@ -0,0 +1,102 @@ +/* + * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "prf_defs.h" + +#if (WX_AIRSYNC_CFG) + +#include "common/bt_target.h" +#include "stack/gatt_api.h" +#include "stack/gattdefs.h" +#include "bt_app_api.h" + + +/// Maximum Transmission Unit +#define ATT_DEFAULT_MTU (23) + +#define BLE_WECHAT_MAX_DATA_LEN (ATT_DEFAULT_MTU - 3) + + +//define the key serivce uuid +#define ATT_SVC_AIRSYNC 0xFEE7 +//define the airsync Char uuid +#define ATT_CHAR_AIRSYNC_WIT 0xFEC7 +#define ATT_CHAR_AIRSYBC_NTF 0xFEC8 +#define ATT_CHAR_AIRSYNC_READ 0xFEC9 + + +typedef void (tAIRSYNC_CBACK)(UINT8 app_id, UINT8 event, UINT8 len, UINT8 *data); + + +/// WX AirSync Service Attributes Indexes +enum { + WX_IDX_SVC, + WX_IDX_AIRSYNC_WIT_CHAR, + WX_IDX_AIRSYNC_WIT_VAL, + WX_IDX_AIRSYNC_NTF_CHAR, + WX_IDX_AIRSYNC_NTF_VAL, + WX_IDX_AIRSYNC_READ_CHAR, + WX_IDX_AIRSYNC_READ_VAL, + WX_IDX_AIRSYNC_NTF_CFG, + + WX_IDX_NB, +}; + +typedef struct { + BD_ADDR remote_bda; + BOOLEAN need_rsp; + UINT16 clt_cfg; +} tAirSync_WRITE_DATA; + +typedef struct { + BOOLEAN in_use; + BOOLEAN congest; + UINT16 conn_id; + BOOLEAN connected; + BD_ADDR remote_bda; + UINT32 trans_id; + UINT8 cur_srvc_id; + +} tAirSync_CLCB; + + +typedef struct { + UINT8 app_id; + UINT16 airsync_wirt_hdl; + UINT16 airsync_ntf_hdl; + UINT16 airsync_read_hdl; + UINT16 airsync_cfg_hdl; + + tAIRSYNC_CBACK *p_cback; + +} tAirSync_INST; + + +/* service engine control block */ +typedef struct { + tAirSync_CLCB clcb; /* connection link*/ + tGATT_IF gatt_if; + BOOLEAN enabled; + BOOLEAN is_primery; + tAirSync_INST airsync_inst; + UINT8 inst_id; +} tAIRSYNC_CB_ENV; + +void AirSync_CreateService(void); + +tAirSync_CLCB *airsync_env_clcb_alloc (UINT16 conn_id, BD_ADDR remote_bda); + +UINT16 AirSync_env_find_conn_id_by_bd_adddr(BD_ADDR bda); + +BOOLEAN AirSync_env_clcb_dealloc(UINT16 conn_id); + +tGATT_STATUS AirSync_Init(tAIRSYNC_CBACK *call_back); + +void AirSync_msg_notify(UINT8 len, UINT8 *button_msg); + +extern tAIRSYNC_CB_ENV airsync_cb_env; + +#endif ///WX_AIRSYNC_CFG diff --git a/lib/bt/host/bluedroid/btc/profile/esp/wechat_AirSync/wx_airsync_prf.c b/lib/bt/host/bluedroid/btc/profile/esp/wechat_AirSync/wx_airsync_prf.c new file mode 100644 index 00000000..9ba7ceed --- /dev/null +++ b/lib/bt/host/bluedroid/btc/profile/esp/wechat_AirSync/wx_airsync_prf.c @@ -0,0 +1,263 @@ +#include "wx_airsync_prf.h" +/* + * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + + +#if (WX_AIRSYNC_CFG) + +#include + +#include +#include +#include + +#include "common/bt_target.h" +#include "common/bt_trace.h" +#include "stack/bt_types.h" +#include "stack/gatt_api.h" +#include "bta/bta_api.h" +#include "bta/bta_gatt_api.h" +#include "bta_gatts_int.h" + + + +tAIRSYNC_CB_ENV airsync_cb_env; + + +/***************************************************************************** +** Constants +*****************************************************************************/ +static void airsync_profile_cb(esp_gatts_evt_t event, esp_gatts_t *p_data); + + +/******************************************************************************* +** +** Function airsync_profile_cb +** +** Description the callback function after the profile has been register to the BTA manager module +** +** Returns NULL +** +*******************************************************************************/ +static void airsync_profile_cb(esp_gatts_evt_t event, esp_gatts_t *p_data) +{ + esp_gatts_rsp_t rsp; + esp_bt_uuid_t uuid = {LEN_UUID_16, {ATT_SVC_AIRSYNC}}; + tAirSync_INST *p_inst = &airsync_cb_env.airsync_inst; + + + BTC_TRACE_ERROR("airsync profile cb event = %x\n", event); + switch (event) { + case ESP_GATTS_REG_EVT: + + if (p_data->reg_oper.status != BTA_GATT_OK) { + BTC_TRACE_ERROR("button profile register failed\n"); + } + airsync_cb_env.gatt_if = p_data->reg_oper.server_if; + airsync_cb_env.enabled = true; + + if (p_data->reg_oper.uuid.uu.uuid16 == ATT_SVC_AIRSYNC) { + AirSync_CreateService(); + } + break; + case ESP_GATTS_READ_EVT: + + if (airsync_cb_env.clcb.connected && airsync_cb_env.enabled) { + //tBTA_GATTS_RSP rsp; + memset(&rsp, 0, sizeof(tBTA_GATTS_API_RSP)); + rsp.attr_value.handle = p_data->req_data.p_data->read_req.handle; + rsp.attr_value.len = 2; + esp_ble_gatts_send_rsp(p_data->req_data.conn_id, p_data->req_data.trans_id, + p_data->req_data.status, &rsp); + } + break; + case ESP_GATTS_WRITE_EVT: + if (airsync_cb_env.clcb.connected && airsync_cb_env.enabled) { + esp_ble_gatts_send_rsp(p_data->req_data.conn_id, p_data->req_data.trans_id, + p_data->req_data.status, NULL); + + } + break; + case ESP_GATTS_CFM_EVT: + + break; + case ESP_GATTS_CREATE_EVT: + uuid.uu.uuid16 = ATT_CHAR_AIRSYNC_WIT; + + airsync_cb_env.clcb.cur_srvc_id = p_data->create.service_id; + airsync_cb_env.is_primery = p_data->create.is_primary; + //start the airsync service after created + esp_ble_gatts_start_srvc(p_data->create.service_id); + //add the frist airsync characteristic --> write characteristic + esp_ble_gatts_add_char(airsync_cb_env.clcb.cur_srvc_id, &uuid, + (GATT_PERM_WRITE | GATT_PERM_READ), + (GATT_CHAR_PROP_BIT_READ | GATT_CHAR_PROP_BIT_WRITE)); + break; + + case ESP_GATTS_ADD_CHAR_EVT: + if (p_data->add_result.char_uuid.uu.uuid16 == ATT_CHAR_AIRSYNC_WIT) { + uuid.uu.uuid16 = ATT_CHAR_AIRSYBC_NTF; + //tBTA_GATT_PERM perm = GATT_PERM_READ; + //tBTA_GATT_CHAR_PROP prop = (GATT_CHAR_PROP_BIT_READ|GATT_CHAR_PROP_BIT_NOTIFY); + //save the att handle to the env + airsync_cb_env.airsync_inst.airsync_wirt_hdl = p_data->add_result.attr_id; + //add the second airsync characteristic --> Notify characteristic + esp_ble_gatts_add_char(airsync_cb_env.clcb.cur_srvc_id, &uuid, + GATT_PERM_READ, (GATT_CHAR_PROP_BIT_READ | GATT_CHAR_PROP_BIT_INDICATE)); + } else if (p_data->add_result.char_uuid.uu.uuid16 == ATT_CHAR_AIRSYBC_NTF) { + //tBTA_GATT_PERM perm = (GATT_PERM_WRITE|GATT_PERM_WRITE); + uuid.uu.uuid16 = GATT_UUID_CHAR_CLIENT_CONFIG; + airsync_cb_env.airsync_inst.airsync_ntf_hdl = p_data->add_result.attr_id; + esp_ble_gatts_add_char_descr (airsync_cb_env.clcb.cur_srvc_id, + (GATT_PERM_WRITE | GATT_PERM_WRITE), + &uuid); + + uuid.uu.uuid16 = ATT_CHAR_AIRSYNC_READ; + //add the third airsync characteristic --> Read characteristic + esp_ble_gatts_add_char(airsync_cb_env.clcb.cur_srvc_id, &uuid, + GATT_PERM_READ, + GATT_CHAR_PROP_BIT_READ); + } else if (p_data->add_result.char_uuid.uu.uuid16 == ATT_CHAR_AIRSYNC_READ) { + airsync_cb_env.airsync_inst.airsync_read_hdl = p_data->add_result.attr_id; + } + + break; + case ESP_GATTS_ADD_CHAR_DESCR_EVT: + if (p_data->add_result.char_uuid.uu.uuid16 == GATT_UUID_CHAR_CLIENT_CONFIG) { + airsync_cb_env.airsync_inst.airsync_cfg_hdl = p_data->add_result.attr_id; + } + break; + case ESP_GATTS_CONNECT_EVT: + //set the connection flag to true + airsync_env_clcb_alloc(p_data->conn.conn_id, p_data->conn.remote_bda); + break; + case ESP_GATTS_DISCONNECT_EVT: + //set the connection flag to true + airsync_cb_env.clcb.connected = false; + break; + case ESP_GATTS_OPEN_EVT: + break; + case ESP_GATTS_CLOSE_EVT: + if (airsync_cb_env.clcb.connected && (airsync_cb_env.clcb.conn_id == p_data->conn.conn_id)) { + //set the connection channal congested flag to true + airsync_cb_env.clcb.congest = p_data->congest.congested; + } + break; + case ESP_GATTS_CONGEST_EVT: + //set the congest flag + airsync_cb_env.clcb.congest = p_data->congest.congested; + break; + default: + break; + } +} + + +/******************************************************************************* +** +** Function AirSync_CreateService +** +** Description Create a Service for the airsync profile +** +** Returns NULL +** +*******************************************************************************/ +void AirSync_CreateService(void) +{ + esp_gatts_if_t server_if ; + esp_bt_uuid_t uuid = {LEN_UUID_16, {ATT_SVC_AIRSYNC}}; + UINT16 num_handle = WX_IDX_NB; + UINT8 inst = 0x00; + server_if = airsync_cb_env.gatt_if; + airsync_cb_env.inst_id = inst; + + esp_ble_gatts_create_srvc(server_if, &uuid, inst, num_handle, true); + +} + +/******************************************************************************* +** +** Function airsync_env_clcb_alloc +** +** Description The function allocates a GATT profile connection link control block +** +** Returns NULL if not found. Otherwise pointer to the connection link block. +** +*******************************************************************************/ +tAirSync_CLCB *airsync_env_clcb_alloc (UINT16 conn_id, BD_ADDR remote_bda) +{ + tAirSync_CLCB *p_clcb = NULL; + p_clcb = &airsync_cb_env.clcb; + + if (!p_clcb->in_use) { + p_clcb->in_use = TRUE; + p_clcb->conn_id = conn_id; + BTC_TRACE_ERROR("p_clcb->conn_id = %x\n", conn_id); + p_clcb->connected = TRUE; + memcpy(p_clcb->remote_bda, remote_bda, BD_ADDR_LEN); + } + + return p_clcb; +} + +/******************************************************************************* +** +** Function airsync_env_find_conn_id_by_bd_adddr +** +** Description The function searches all LCB with macthing bd address +** +** Returns total number of clcb found. +** +*******************************************************************************/ +UINT16 airsync_env_find_conn_id_by_bd_adddr(BD_ADDR remote_bda) +{ + UINT8 i_clcb; + tAirSync_CLCB *p_clcb = NULL; + + for (i_clcb = 0, p_clcb = &airsync_cb_env.clcb; i_clcb < 1; i_clcb++, p_clcb++) { + if (p_clcb->in_use && p_clcb->connected && memcmp(p_clcb->remote_bda, remote_bda, BD_ADDR_LEN)) { + return p_clcb->conn_id; + } + } + + return GATT_INVALID_CONN_ID; +} + + +/******************************************************************************* +** +** Function airsync_init +** +** Description Initializa the GATT Service for airsync profiles. +** +*******************************************************************************/ +tGATT_STATUS AirSync_Init(tAIRSYNC_CBACK *call_back) +{ + esp_bt_uuid_t app_uuid = {LEN_UUID_16, {ATT_SVC_AIRSYNC}}; + + + if (airsync_cb_env.enabled) { + BTC_TRACE_ERROR("airsync svc already initaliezd\n"); + return ESP_GATT_ERROR; + } else { + memset(&airsync_cb_env, 0, sizeof(tAIRSYNC_CB_ENV)); + } + + + if (call_back != NULL) { + airsync_cb_env.airsync_inst.p_cback = call_back; + } + + + /* register the airsync profile to the BTA_GATTS module*/ + esp_ble_gatts_app_register(&app_uuid, airsync_profile_cb); + + airsync_cb_env.enabled = TRUE; + + return ESP_GATT_OK; +} + +#endif ///WX_AIRSYNC_CFG diff --git a/lib/bt/host/bluedroid/btc/profile/std/a2dp/bta_av_co.c b/lib/bt/host/bluedroid/btc/profile/std/a2dp/bta_av_co.c new file mode 100644 index 00000000..87def766 --- /dev/null +++ b/lib/bt/host/bluedroid/btc/profile/std/a2dp/bta_av_co.c @@ -0,0 +1,1764 @@ +/****************************************************************************** + * + * Copyright (C) 2004-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This is the advanced audio/video call-out function implementation for + * BTC. + * + ******************************************************************************/ +#include "string.h" +#include "common/bt_target.h" +#include "stack/a2d_api.h" +#include "stack/a2d_sbc.h" +#include "bta/bta_sys.h" +#include "bta/bta_av_api.h" +#include "bta/bta_av_co.h" +#include "bta/bta_av_ci.h" +#include "bta/bta_av_sbc.h" +#include "btc_a2dp.h" +#include "btc_a2dp_source.h" +#include "btc_av_co.h" +#include "btc/btc_util.h" +#include "osi/mutex.h" + +#if BTC_AV_INCLUDED + +/***************************************************************************** + ** Constants + *****************************************************************************/ + +#define FUNC_TRACE() APPL_TRACE_DEBUG("%s", __FUNCTION__); + +/* Macro to retrieve the number of elements in a statically allocated array */ +#define BTA_AV_CO_NUM_ELEMENTS(__a) (sizeof(__a)/sizeof((__a)[0])) + +/* MIN and MAX macros */ +#define BTA_AV_CO_MIN(X,Y) ((X) < (Y) ? (X) : (Y)) +#define BTA_AV_CO_MAX(X,Y) ((X) > (Y) ? (X) : (Y)) + +/* Macro to convert audio handle to index and vice versa */ +#define BTA_AV_CO_AUDIO_HNDL_TO_INDX(hndl) (((hndl) & (~BTA_AV_CHNL_MSK)) - 1) +#define BTA_AV_CO_AUDIO_INDX_TO_HNDL(indx) (((indx) + 1) | BTA_AV_CHNL_AUDIO) + + +/* Offsets to access codec information in SBC codec */ +#define BTA_AV_CO_SBC_FREQ_CHAN_OFF 3 +#define BTA_AV_CO_SBC_BLOCK_BAND_OFF 4 +#define BTA_AV_CO_SBC_MIN_BITPOOL_OFF 5 +#define BTA_AV_CO_SBC_MAX_BITPOOL_OFF 6 + +#define BTA_AV_CO_SBC_MAX_BITPOOL 53 + +/* SCMS-T protect info */ +const UINT8 bta_av_co_cp_scmst[BTA_AV_CP_INFO_LEN] = "\x02\x02\x00"; + +/* SBC SRC codec capabilities */ +const tA2D_SBC_CIE bta_av_co_sbc_caps = { + (A2D_SBC_IE_SAMP_FREQ_44), /* samp_freq */ + (A2D_SBC_IE_CH_MD_MONO | A2D_SBC_IE_CH_MD_STEREO | A2D_SBC_IE_CH_MD_JOINT | A2D_SBC_IE_CH_MD_DUAL), /* ch_mode */ + (A2D_SBC_IE_BLOCKS_16 | A2D_SBC_IE_BLOCKS_12 | A2D_SBC_IE_BLOCKS_8 | A2D_SBC_IE_BLOCKS_4), /* block_len */ + (A2D_SBC_IE_SUBBAND_4 | A2D_SBC_IE_SUBBAND_8), /* num_subbands */ + (A2D_SBC_IE_ALLOC_MD_L | A2D_SBC_IE_ALLOC_MD_S), /* alloc_mthd */ + BTA_AV_CO_SBC_MAX_BITPOOL, /* max_bitpool */ + A2D_SBC_IE_MIN_BITPOOL /* min_bitpool */ +}; + +/* SBC SINK codec capabilities */ +const tA2D_SBC_CIE bta_av_co_sbc_sink_caps = { + (A2D_SBC_IE_SAMP_FREQ_48 | A2D_SBC_IE_SAMP_FREQ_44), /* samp_freq */ + (A2D_SBC_IE_CH_MD_MONO | A2D_SBC_IE_CH_MD_STEREO | A2D_SBC_IE_CH_MD_JOINT | A2D_SBC_IE_CH_MD_DUAL), /* ch_mode */ + (A2D_SBC_IE_BLOCKS_16 | A2D_SBC_IE_BLOCKS_12 | A2D_SBC_IE_BLOCKS_8 | A2D_SBC_IE_BLOCKS_4), /* block_len */ + (A2D_SBC_IE_SUBBAND_4 | A2D_SBC_IE_SUBBAND_8), /* num_subbands */ + (A2D_SBC_IE_ALLOC_MD_L | A2D_SBC_IE_ALLOC_MD_S), /* alloc_mthd */ + A2D_SBC_IE_MAX_BITPOOL, /* max_bitpool */ + A2D_SBC_IE_MIN_BITPOOL /* min_bitpool */ +}; + +#if !defined(BTC_AV_SBC_DEFAULT_SAMP_FREQ) +#define BTC_AV_SBC_DEFAULT_SAMP_FREQ A2D_SBC_IE_SAMP_FREQ_44 +#endif + +/* Default SBC codec configuration */ +const tA2D_SBC_CIE btc_av_sbc_default_config = { + BTC_AV_SBC_DEFAULT_SAMP_FREQ, /* samp_freq */ + A2D_SBC_IE_CH_MD_JOINT, /* ch_mode */ + A2D_SBC_IE_BLOCKS_16, /* block_len */ + A2D_SBC_IE_SUBBAND_8, /* num_subbands */ + A2D_SBC_IE_ALLOC_MD_L, /* alloc_mthd */ + BTA_AV_CO_SBC_MAX_BITPOOL, /* max_bitpool */ + A2D_SBC_IE_MIN_BITPOOL /* min_bitpool */ +}; + +/* Control block instance */ +#if AVRC_DYNAMIC_MEMORY == FALSE +tBTA_AV_CO_CB bta_av_co_cb; +#else +tBTA_AV_CO_CB *bta_av_co_cb_ptr; +#endif + +static BOOLEAN bta_av_co_audio_codec_build_config(const UINT8 *p_codec_caps, UINT8 *p_codec_cfg); +static void bta_av_co_audio_peer_reset_config(tBTA_AV_CO_PEER *p_peer); +static BOOLEAN bta_av_co_cp_is_scmst(const UINT8 *p_protectinfo); +static BOOLEAN bta_av_co_audio_sink_has_scmst(const tBTA_AV_CO_SINK *p_sink); +static BOOLEAN bta_av_co_audio_peer_supports_codec(tBTA_AV_CO_PEER *p_peer, UINT8 *p_snk_index); +static UINT8 bta_av_co_audio_media_supports_config(UINT8 codec_type, const UINT8 *p_codec_cfg); +static UINT8 bta_av_co_audio_sink_supports_config(UINT8 codec_type, const UINT8 *p_codec_cfg); +static BOOLEAN bta_av_co_audio_peer_src_supports_codec(tBTA_AV_CO_PEER *p_peer, UINT8 *p_src_index); + + + +/******************************************************************************* + ** + ** Function bta_av_co_cp_is_active + ** + ** Description Get the current configuration of content protection + ** + ** Returns TRUE if the current streaming has CP, FALSE otherwise + ** + *******************************************************************************/ +BOOLEAN bta_av_co_cp_is_active(void) +{ + FUNC_TRACE(); + return bta_av_co_cb.cp.active; +} + +/******************************************************************************* + ** + ** Function bta_av_co_cp_get_flag + ** + ** Description Get content protection flag + ** BTA_AV_CP_SCMS_COPY_NEVER + ** BTA_AV_CP_SCMS_COPY_ONCE + ** BTA_AV_CP_SCMS_COPY_FREE + ** + ** Returns The current flag value + ** + *******************************************************************************/ +UINT8 bta_av_co_cp_get_flag(void) +{ + FUNC_TRACE(); + return bta_av_co_cb.cp.flag; +} + +/******************************************************************************* + ** + ** Function bta_av_co_cp_set_flag + ** + ** Description Set content protection flag + ** BTA_AV_CP_SCMS_COPY_NEVER + ** BTA_AV_CP_SCMS_COPY_ONCE + ** BTA_AV_CP_SCMS_COPY_FREE + ** + ** Returns TRUE if setting the SCMS flag is supported else FALSE + ** + *******************************************************************************/ +BOOLEAN bta_av_co_cp_set_flag(UINT8 cp_flag) +{ + FUNC_TRACE(); + +#if defined(BTA_AV_CO_CP_SCMS_T) && (BTA_AV_CO_CP_SCMS_T == TRUE) +#else + if (cp_flag != BTA_AV_CP_SCMS_COPY_FREE) { + return FALSE; + } +#endif + bta_av_co_cb.cp.flag = cp_flag; + return TRUE; +} + +/******************************************************************************* + ** + ** Function bta_av_co_get_peer + ** + ** Description find the peer entry for a given handle + ** + ** Returns the control block + ** + *******************************************************************************/ +static tBTA_AV_CO_PEER *bta_av_co_get_peer(tBTA_AV_HNDL hndl) +{ + UINT8 index; + FUNC_TRACE(); + + index = BTA_AV_CO_AUDIO_HNDL_TO_INDX(hndl); + + /* Sanity check */ + if (index >= BTA_AV_CO_NUM_ELEMENTS(bta_av_co_cb.peers)) { + APPL_TRACE_ERROR("bta_av_co_get_peer peer index out of bounds:%d", index); + return NULL; + } + + return &bta_av_co_cb.peers[index]; +} + +/******************************************************************************* + ** + ** Function bta_av_co_audio_init + ** + ** Description This callout function is executed by AV when it is + ** started by calling BTA_AvRegister(). This function can be + ** used by the phone to initialize audio paths or for other + ** initialization purposes. + ** + ** + ** Returns Stream codec and content protection capabilities info. + ** + *******************************************************************************/ +BOOLEAN bta_av_co_audio_init(UINT8 *p_codec_type, UINT8 *p_codec_info, UINT8 *p_num_protect, + UINT8 *p_protect_info, UINT8 tsep) +{ + FUNC_TRACE(); + + APPL_TRACE_DEBUG("bta_av_co_audio_init: %d", tsep); + + /* By default - no content protection info */ + *p_num_protect = 0; + *p_protect_info = 0; + + /* reset remote preference through setconfig */ + bta_av_co_cb.codec_cfg_setconfig.id = BTC_AV_CODEC_NONE; + + if (tsep == AVDT_TSEP_SRC) { +#if defined(BTA_AV_CO_CP_SCMS_T) && (BTA_AV_CO_CP_SCMS_T == TRUE) + do { + UINT8 *p = p_protect_info; + + /* Content protection info - support SCMS-T */ + *p_num_protect = 1; + *p++ = BTA_AV_CP_LOSC; + UINT16_TO_STREAM(p, BTA_AV_CP_SCMS_T_ID); + } while (0); +#endif + /* Set up for SBC codec for SRC*/ + *p_codec_type = BTA_AV_CODEC_SBC; + + /* This should not fail because we are using constants for parameters */ + A2D_BldSbcInfo(AVDT_MEDIA_AUDIO, (tA2D_SBC_CIE *) &bta_av_co_sbc_caps, p_codec_info); + return TRUE; + } else if (tsep == AVDT_TSEP_SNK) { + *p_codec_type = BTA_AV_CODEC_SBC; + + /* This should not fail because we are using constants for parameters */ + A2D_BldSbcInfo(AVDT_MEDIA_AUDIO, (tA2D_SBC_CIE *) &bta_av_co_sbc_sink_caps, p_codec_info); + + /* Codec is valid */ + return TRUE; + } else { + APPL_TRACE_WARNING("invalid SEP type %d", tsep); + return FALSE; + } +} + +/******************************************************************************* + ** + ** Function bta_av_co_audio_disc_res + ** + ** Description This callout function is executed by AV to report the + ** number of stream end points (SEP) were found during the + ** AVDT stream discovery process. + ** + ** + ** Returns void. + ** + *******************************************************************************/ +void bta_av_co_audio_disc_res(tBTA_AV_HNDL hndl, UINT8 num_seps, UINT8 num_snk, + UINT8 num_src, BD_ADDR addr, UINT16 uuid_local) +{ + tBTA_AV_CO_PEER *p_peer; + + FUNC_TRACE(); + + APPL_TRACE_DEBUG("bta_av_co_audio_disc_res h:x%x num_seps:%d num_snk:%d num_src:%d", + hndl, num_seps, num_snk, num_src); + + /* Find the peer info */ + p_peer = bta_av_co_get_peer(hndl); + if (p_peer == NULL) { + APPL_TRACE_ERROR("bta_av_co_audio_disc_res could not find peer entry"); + return; + } + + /* Sanity check : this should never happen */ + if (p_peer->opened) { + APPL_TRACE_ERROR("bta_av_co_audio_disc_res peer already opened"); + } + + /* Copy the discovery results */ + bdcpy(p_peer->addr, addr); + p_peer->num_snks = num_snk; + p_peer->num_srcs = num_src; + p_peer->num_seps = num_seps; + p_peer->num_rx_snks = 0; + p_peer->num_rx_srcs = 0; + p_peer->num_sup_snks = 0; + if (uuid_local == UUID_SERVCLASS_AUDIO_SINK) { + p_peer->uuid_to_connect = UUID_SERVCLASS_AUDIO_SOURCE; + } else if (uuid_local == UUID_SERVCLASS_AUDIO_SOURCE) { + p_peer->uuid_to_connect = UUID_SERVCLASS_AUDIO_SINK; + } + p_peer->got_disc_res = TRUE; +} + +/******************************************************************************* + ** + ** Function bta_av_co_audio_cfg_res + ** + ** Description This callout function is executed by AV to report the + ** number of stream end points (SEP) were found during the + ** incoming AVDT stream config request process. + ** + ** + ** Returns void. + ** + *******************************************************************************/ +void bta_av_co_audio_cfg_res(tBTA_AV_HNDL hndl, UINT8 num_seps, UINT8 num_snk, + UINT8 num_src, BD_ADDR addr, UINT16 uuid_local) +{ + tBTA_AV_CO_PEER *p_peer; + + FUNC_TRACE(); + + APPL_TRACE_DEBUG("bta_av_co_audio_cfg_res h:x%x num_seps:%d num_snk:%d num_src:%d", + hndl, num_seps, num_snk, num_src); + + /* Find the peer info */ + p_peer = bta_av_co_get_peer(hndl); + if (p_peer == NULL) { + APPL_TRACE_ERROR("bta_av_co_audio_cfg_res could not find peer entry"); + return; + } + + /* Sanity check : this should never happen */ + if (p_peer->opened) { + APPL_TRACE_ERROR("bta_av_co_audio_cfg_res peer already opened"); + } + + /* Copy the discovery results */ + bdcpy(p_peer->addr, addr); + if (!p_peer->got_disc_res) { + p_peer->num_snks = num_snk; + p_peer->num_srcs = num_src; + p_peer->num_seps = num_seps; + p_peer->num_rx_snks = 0; + p_peer->num_rx_srcs = 0; + p_peer->num_sup_snks = 0; + } + if (uuid_local == UUID_SERVCLASS_AUDIO_SINK) { + p_peer->uuid_to_connect = UUID_SERVCLASS_AUDIO_SOURCE; + } else if (uuid_local == UUID_SERVCLASS_AUDIO_SOURCE) { + p_peer->uuid_to_connect = UUID_SERVCLASS_AUDIO_SINK; + } +} + +/******************************************************************************* + ** + ** Function bta_av_build_src_cfg + ** + ** Description This function will build preferred config from src capabilities + ** + ** + ** Returns Pass or Fail for current getconfig. + ** + *******************************************************************************/ +void bta_av_build_src_cfg (UINT8 *p_pref_cfg, UINT8 *p_src_cap) +{ + tA2D_SBC_CIE src_cap; + tA2D_SBC_CIE pref_cap; + UINT8 status = 0; + + /* initialize it to default SBC configuration */ + A2D_BldSbcInfo(AVDT_MEDIA_AUDIO, (tA2D_SBC_CIE *) &btc_av_sbc_default_config, p_pref_cfg); + /* now try to build a preferred one */ + /* parse configuration */ + if ((status = A2D_ParsSbcInfo(&src_cap, p_src_cap, TRUE)) != 0) { + APPL_TRACE_DEBUG(" Cant parse src cap ret = %d", status); + return ; + } + + if (src_cap.samp_freq & A2D_SBC_IE_SAMP_FREQ_48) { + pref_cap.samp_freq = A2D_SBC_IE_SAMP_FREQ_48; + } else if (src_cap.samp_freq & A2D_SBC_IE_SAMP_FREQ_44) { + pref_cap.samp_freq = A2D_SBC_IE_SAMP_FREQ_44; + } + + if (src_cap.ch_mode & A2D_SBC_IE_CH_MD_JOINT) { + pref_cap.ch_mode = A2D_SBC_IE_CH_MD_JOINT; + } else if (src_cap.ch_mode & A2D_SBC_IE_CH_MD_STEREO) { + pref_cap.ch_mode = A2D_SBC_IE_CH_MD_STEREO; + } else if (src_cap.ch_mode & A2D_SBC_IE_CH_MD_DUAL) { + pref_cap.ch_mode = A2D_SBC_IE_CH_MD_DUAL; + } else if (src_cap.ch_mode & A2D_SBC_IE_CH_MD_MONO) { + pref_cap.ch_mode = A2D_SBC_IE_CH_MD_MONO; + } + + if (src_cap.block_len & A2D_SBC_IE_BLOCKS_16) { + pref_cap.block_len = A2D_SBC_IE_BLOCKS_16; + } else if (src_cap.block_len & A2D_SBC_IE_BLOCKS_12) { + pref_cap.block_len = A2D_SBC_IE_BLOCKS_12; + } else if (src_cap.block_len & A2D_SBC_IE_BLOCKS_8) { + pref_cap.block_len = A2D_SBC_IE_BLOCKS_8; + } else if (src_cap.block_len & A2D_SBC_IE_BLOCKS_4) { + pref_cap.block_len = A2D_SBC_IE_BLOCKS_4; + } + + if (src_cap.num_subbands & A2D_SBC_IE_SUBBAND_8) { + pref_cap.num_subbands = A2D_SBC_IE_SUBBAND_8; + } else if (src_cap.num_subbands & A2D_SBC_IE_SUBBAND_4) { + pref_cap.num_subbands = A2D_SBC_IE_SUBBAND_4; + } + + if (src_cap.alloc_mthd & A2D_SBC_IE_ALLOC_MD_L) { + pref_cap.alloc_mthd = A2D_SBC_IE_ALLOC_MD_L; + } else if (src_cap.alloc_mthd & A2D_SBC_IE_ALLOC_MD_S) { + pref_cap.alloc_mthd = A2D_SBC_IE_ALLOC_MD_S; + } + + pref_cap.max_bitpool = src_cap.max_bitpool; + pref_cap.min_bitpool = src_cap.min_bitpool; + + A2D_BldSbcInfo(AVDT_MEDIA_AUDIO, (tA2D_SBC_CIE *) &pref_cap, p_pref_cfg); +} + +/******************************************************************************* + ** + ** Function bta_av_audio_sink_getconfig + ** + ** Description This callout function is executed by AV to retrieve the + ** desired codec and content protection configuration for the + ** A2DP Sink audio stream in Initiator. + ** + ** + ** Returns Pass or Fail for current getconfig. + ** + *******************************************************************************/ +UINT8 bta_av_audio_sink_getconfig(tBTA_AV_HNDL hndl, tBTA_AV_CODEC codec_type, + UINT8 *p_codec_info, UINT8 *p_sep_info_idx, UINT8 seid, UINT8 *p_num_protect, + UINT8 *p_protect_info) +{ + + UINT8 result = A2D_FAIL; + BOOLEAN supported; + tBTA_AV_CO_PEER *p_peer; + tBTA_AV_CO_SINK *p_src; + UINT8 pref_cfg[AVDT_CODEC_SIZE]; + UINT8 index; + + FUNC_TRACE(); + + APPL_TRACE_DEBUG("bta_av_audio_sink_getconfig handle:0x%x codec_type:%d seid:%d", + hndl, codec_type, seid); + APPL_TRACE_DEBUG("num_protect:0x%02x protect_info:0x%02x%02x%02x", + *p_num_protect, p_protect_info[0], p_protect_info[1], p_protect_info[2]); + + /* Retrieve the peer info */ + p_peer = bta_av_co_get_peer(hndl); + if (p_peer == NULL) { + APPL_TRACE_ERROR("bta_av_audio_sink_getconfig could not find peer entry"); + return A2D_FAIL; + } + + APPL_TRACE_DEBUG("bta_av_audio_sink_getconfig peer(o=%d,n_snks=%d,n_rx_snks=%d,n_sup_snks=%d)", + p_peer->opened, p_peer->num_srcs, p_peer->num_rx_srcs, p_peer->num_sup_srcs); + + p_peer->num_rx_srcs++; + + /* Check if this is a supported configuration */ + supported = FALSE; + switch (codec_type) { + case BTA_AV_CODEC_SBC: + supported = TRUE; + break; + + default: + break; + } + + if (supported) { + /* If there is room for a new one */ + if (p_peer->num_sup_srcs < BTA_AV_CO_NUM_ELEMENTS(p_peer->srcs)) { + p_src = &p_peer->srcs[p_peer->num_sup_srcs++]; + + APPL_TRACE_DEBUG("bta_av_audio_sink_getconfig saved caps[%x:%x:%x:%x:%x:%x]", + p_codec_info[1], p_codec_info[2], p_codec_info[3], + p_codec_info[4], p_codec_info[5], p_codec_info[6]); + + memcpy(p_src->codec_caps, p_codec_info, AVDT_CODEC_SIZE); + p_src->codec_type = codec_type; + p_src->sep_info_idx = *p_sep_info_idx; + p_src->seid = seid; + p_src->num_protect = *p_num_protect; + memcpy(p_src->protect_info, p_protect_info, BTA_AV_CP_INFO_LEN); + } else { + APPL_TRACE_ERROR("bta_av_audio_sink_getconfig no more room for SRC info"); + } + } + + /* If last SNK get capabilities or all supported codec caps retrieved */ + if ((p_peer->num_rx_srcs == p_peer->num_srcs) || + (p_peer->num_sup_srcs == BTA_AV_CO_NUM_ELEMENTS(p_peer->srcs))) { + APPL_TRACE_DEBUG("bta_av_audio_sink_getconfig last SRC reached"); + + /* Protect access to bta_av_co_cb.codec_cfg */ + osi_mutex_global_lock(); + + /* Find a src that matches the codec config */ + if (bta_av_co_audio_peer_src_supports_codec(p_peer, &index)) { + APPL_TRACE_DEBUG(" Codec Supported "); + p_src = &p_peer->srcs[index]; + + /* Build the codec configuration for this sink */ + { + /* Save the new configuration */ + p_peer->p_src = p_src; + /* get preferred config from src_caps */ + bta_av_build_src_cfg(pref_cfg, p_src->codec_caps); + memcpy(p_peer->codec_cfg, pref_cfg, AVDT_CODEC_SIZE); + + APPL_TRACE_DEBUG("bta_av_audio_sink_getconfig p_codec_info[%x:%x:%x:%x:%x:%x]", + p_peer->codec_cfg[1], p_peer->codec_cfg[2], p_peer->codec_cfg[3], + p_peer->codec_cfg[4], p_peer->codec_cfg[5], p_peer->codec_cfg[6]); + /* By default, no content protection */ + *p_num_protect = 0; + +#if defined(BTA_AV_CO_CP_SCMS_T) && (BTA_AV_CO_CP_SCMS_T == TRUE) + p_peer->cp_active = FALSE; + bta_av_co_cb.cp.active = FALSE; +#endif + + *p_sep_info_idx = p_src->sep_info_idx; + memcpy(p_codec_info, p_peer->codec_cfg, AVDT_CODEC_SIZE); + result = A2D_SUCCESS; + } + } + /* Protect access to bta_av_co_cb.codec_cfg */ + osi_mutex_global_unlock(); + } + return result; +} +/******************************************************************************* + ** + ** Function bta_av_co_audio_getconfig + ** + ** Description This callout function is executed by AV to retrieve the + ** desired codec and content protection configuration for the + ** audio stream. + ** + ** + ** Returns Stream codec and content protection configuration info. + ** + *******************************************************************************/ +UINT8 bta_av_co_audio_getconfig(tBTA_AV_HNDL hndl, tBTA_AV_CODEC codec_type, + UINT8 *p_codec_info, UINT8 *p_sep_info_idx, UINT8 seid, UINT8 *p_num_protect, + UINT8 *p_protect_info) + +{ + UINT8 result = A2D_FAIL; + BOOLEAN supported; + tBTA_AV_CO_PEER *p_peer; + tBTA_AV_CO_SINK *p_sink; + UINT8 codec_cfg[AVDT_CODEC_SIZE]; + UINT8 index; + + FUNC_TRACE(); + + /* Retrieve the peer info */ + p_peer = bta_av_co_get_peer(hndl); + if (p_peer == NULL) { + APPL_TRACE_ERROR("bta_av_co_audio_getconfig could not find peer entry"); + return A2D_FAIL; + } + + if (p_peer->uuid_to_connect == UUID_SERVCLASS_AUDIO_SOURCE) { + result = bta_av_audio_sink_getconfig(hndl, codec_type, p_codec_info, p_sep_info_idx, + seid, p_num_protect, p_protect_info); + return result; + } + APPL_TRACE_DEBUG("bta_av_co_audio_getconfig handle:0x%x codec_type:%d seid:%d", + hndl, codec_type, seid); + APPL_TRACE_DEBUG("num_protect:0x%02x protect_info:0x%02x%02x%02x", + *p_num_protect, p_protect_info[0], p_protect_info[1], p_protect_info[2]); + + APPL_TRACE_DEBUG("bta_av_co_audio_getconfig peer(o=%d,n_snks=%d,n_rx_snks=%d,n_sup_snks=%d)", + p_peer->opened, p_peer->num_snks, p_peer->num_rx_snks, p_peer->num_sup_snks); + + p_peer->num_rx_snks++; + + /* Check if this is a supported configuration */ + supported = FALSE; + switch (codec_type) { + case BTA_AV_CODEC_SBC: + supported = TRUE; + break; + + default: + break; + } + + if (supported) { + /* If there is room for a new one */ + if (p_peer->num_sup_snks < BTA_AV_CO_NUM_ELEMENTS(p_peer->snks)) { + p_sink = &p_peer->snks[p_peer->num_sup_snks++]; + + APPL_TRACE_DEBUG("bta_av_co_audio_getconfig saved caps[%x:%x:%x:%x:%x:%x]", + p_codec_info[1], p_codec_info[2], p_codec_info[3], + p_codec_info[4], p_codec_info[5], p_codec_info[6]); + + memcpy(p_sink->codec_caps, p_codec_info, AVDT_CODEC_SIZE); + p_sink->codec_type = codec_type; + p_sink->sep_info_idx = *p_sep_info_idx; + p_sink->seid = seid; + p_sink->num_protect = *p_num_protect; + memcpy(p_sink->protect_info, p_protect_info, BTA_AV_CP_INFO_LEN); + } else { + APPL_TRACE_ERROR("bta_av_co_audio_getconfig no more room for SNK info"); + } + } + + /* If last SNK get capabilities or all supported codec capa retrieved */ + if ((p_peer->num_rx_snks == p_peer->num_snks) || + (p_peer->num_sup_snks == BTA_AV_CO_NUM_ELEMENTS(p_peer->snks))) { + APPL_TRACE_DEBUG("bta_av_co_audio_getconfig last sink reached"); + + /* Protect access to bta_av_co_cb.codec_cfg */ + osi_mutex_global_lock(); + + /* Find a sink that matches the codec config */ + if (bta_av_co_audio_peer_supports_codec(p_peer, &index)) { + /* stop fetching caps once we retrieved a supported codec */ + if (p_peer->acp) { + *p_sep_info_idx = p_peer->num_seps; + APPL_TRACE_EVENT("no need to fetch more SEPs"); + } + + p_sink = &p_peer->snks[index]; + + /* Build the codec configuration for this sink */ + if (bta_av_co_audio_codec_build_config(p_sink->codec_caps, codec_cfg)) { + APPL_TRACE_DEBUG("bta_av_co_audio_getconfig reconfig p_codec_info[%x:%x:%x:%x:%x:%x]", + codec_cfg[1], codec_cfg[2], codec_cfg[3], + codec_cfg[4], codec_cfg[5], codec_cfg[6]); + + /* Save the new configuration */ + p_peer->p_snk = p_sink; + memcpy(p_peer->codec_cfg, codec_cfg, AVDT_CODEC_SIZE); + + /* By default, no content protection */ + *p_num_protect = 0; + +#if defined(BTA_AV_CO_CP_SCMS_T) && (BTA_AV_CO_CP_SCMS_T == TRUE) + /* Check if this sink supports SCMS */ + if (bta_av_co_audio_sink_has_scmst(p_sink)) { + p_peer->cp_active = TRUE; + bta_av_co_cb.cp.active = TRUE; + *p_num_protect = BTA_AV_CP_INFO_LEN; + memcpy(p_protect_info, bta_av_co_cp_scmst, BTA_AV_CP_INFO_LEN); + } else { + p_peer->cp_active = FALSE; + bta_av_co_cb.cp.active = FALSE; + } +#endif + + /* If acceptor -> reconfig otherwise reply for configuration */ + if (p_peer->acp) { + if (p_peer->recfg_needed) { + APPL_TRACE_DEBUG("bta_av_co_audio_getconfig call BTA_AvReconfig(x%x)", hndl); + BTA_AvReconfig(hndl, TRUE, p_sink->sep_info_idx, p_peer->codec_cfg, *p_num_protect, (UINT8 *)bta_av_co_cp_scmst); + } + } else { + *p_sep_info_idx = p_sink->sep_info_idx; + memcpy(p_codec_info, p_peer->codec_cfg, AVDT_CODEC_SIZE); + } + result = A2D_SUCCESS; + } + } + /* Protect access to bta_av_co_cb.codec_cfg */ + osi_mutex_global_unlock(); + } + return result; +} + +/******************************************************************************* + ** + ** Function bta_av_co_audio_setconfig + ** + ** Description This callout function is executed by AV to set the codec and + ** content protection configuration of the audio stream. + ** + ** + ** Returns void + ** + *******************************************************************************/ +void bta_av_co_audio_setconfig(tBTA_AV_HNDL hndl, tBTA_AV_CODEC codec_type, + UINT8 *p_codec_info, UINT8 seid, BD_ADDR addr, UINT8 num_protect, UINT8 *p_protect_info, + UINT8 t_local_sep, UINT8 avdt_handle) +{ + tBTA_AV_CO_PEER *p_peer; + UINT8 status = A2D_SUCCESS; + UINT8 category = A2D_SUCCESS; + BOOLEAN recfg_needed = FALSE; + UINT8 codec_cfg_status = A2D_SUCCESS; + UNUSED(seid); + UNUSED(addr); + + FUNC_TRACE(); + + APPL_TRACE_DEBUG("bta_av_co_audio_setconfig p_codec_info[%x:%x:%x:%x:%x:%x]", + p_codec_info[1], p_codec_info[2], p_codec_info[3], + p_codec_info[4], p_codec_info[5], p_codec_info[6]); + APPL_TRACE_DEBUG("num_protect:0x%02x protect_info:0x%02x%02x%02x", + num_protect, p_protect_info[0], p_protect_info[1], p_protect_info[2]); + + /* Retrieve the peer info */ + p_peer = bta_av_co_get_peer(hndl); + if (p_peer == NULL) { + APPL_TRACE_ERROR("bta_av_co_audio_setconfig could not find peer entry"); + + /* Call call-in rejecting the configuration */ + bta_av_ci_setconfig(hndl, A2D_BUSY, AVDT_ASC_CODEC, 0, NULL, FALSE, avdt_handle); + return; + } + APPL_TRACE_DEBUG("bta_av_co_audio_setconfig peer(o=%d,n_snks=%d,n_rx_snks=%d,n_sup_snks=%d)", + p_peer->opened, p_peer->num_snks, p_peer->num_rx_snks, p_peer->num_sup_snks); + + /* Sanity check: should not be opened at this point */ + if (p_peer->opened) { + APPL_TRACE_ERROR("bta_av_co_audio_setconfig peer already in use"); + } + +#if defined(BTA_AV_CO_CP_SCMS_T) && (BTA_AV_CO_CP_SCMS_T == TRUE) + if (num_protect != 0) { + /* If CP is supported */ + if ((num_protect != 1) || + (bta_av_co_cp_is_scmst(p_protect_info) == FALSE)) { + APPL_TRACE_ERROR("bta_av_co_audio_setconfig wrong CP configuration"); + status = A2D_BAD_CP_TYPE; + category = AVDT_ASC_PROTECT; + } + } +#else + /* Do not support content protection for the time being */ + if (num_protect != 0) { + APPL_TRACE_ERROR("bta_av_co_audio_setconfig wrong CP configuration"); + status = A2D_BAD_CP_TYPE; + category = AVDT_ASC_PROTECT; + } +#endif + if (status == A2D_SUCCESS) { + if (AVDT_TSEP_SNK == t_local_sep) { + codec_cfg_status = bta_av_co_audio_sink_supports_config(codec_type, p_codec_info); + APPL_TRACE_DEBUG(" Peer is A2DP SRC "); + } + if (AVDT_TSEP_SRC == t_local_sep) { + codec_cfg_status = bta_av_co_audio_media_supports_config(codec_type, p_codec_info); + APPL_TRACE_DEBUG(" Peer is A2DP SINK "); + } + /* Check if codec configuration is supported */ + if (codec_cfg_status == A2D_SUCCESS) { + + /* Protect access to bta_av_co_cb.codec_cfg */ + osi_mutex_global_lock(); + + /* Check if the configuration matches the current codec config */ + switch (bta_av_co_cb.codec_cfg.id) { + case BTC_AV_CODEC_SBC: + if ((codec_type != BTA_AV_CODEC_SBC) || memcmp(p_codec_info, bta_av_co_cb.codec_cfg.info, 5)) { + recfg_needed = TRUE; + } else if ((num_protect == 1) && (!bta_av_co_cb.cp.active)) { + recfg_needed = TRUE; + } + + /* if remote side requests a restricted notify sinks preferred bitpool range as all other params are + already checked for validify */ + APPL_TRACE_EVENT("remote peer setconfig bitpool range [%d:%d]", + p_codec_info[BTA_AV_CO_SBC_MIN_BITPOOL_OFF], + p_codec_info[BTA_AV_CO_SBC_MAX_BITPOOL_OFF] ); + + bta_av_co_cb.codec_cfg_setconfig.id = BTC_AV_CODEC_SBC; + memcpy(bta_av_co_cb.codec_cfg_setconfig.info, p_codec_info, AVDT_CODEC_SIZE); + if (AVDT_TSEP_SNK == t_local_sep) { + /* If Peer is SRC, and our cfg subset matches with what is requested by peer, then + just accept what peer wants */ + memcpy(bta_av_co_cb.codec_cfg.info, p_codec_info, AVDT_CODEC_SIZE); + recfg_needed = FALSE; + } + break; + + + default: + APPL_TRACE_ERROR("bta_av_co_audio_setconfig unsupported cid %d", bta_av_co_cb.codec_cfg.id); + recfg_needed = TRUE; + break; + } + /* Protect access to bta_av_co_cb.codec_cfg */ + osi_mutex_global_unlock(); + } else { + category = AVDT_ASC_CODEC; + status = A2D_FAIL; + } + } + + if (status != A2D_SUCCESS) { + APPL_TRACE_DEBUG("bta_av_co_audio_setconfig reject s=%d c=%d", codec_cfg_status, category); + + /* Call call-in rejecting the configuration */ + bta_av_ci_setconfig(hndl, codec_cfg_status, category, 0, NULL, FALSE, avdt_handle); + } else { + /* Mark that this is an acceptor peer */ + p_peer->acp = TRUE; + p_peer->recfg_needed = recfg_needed; + + APPL_TRACE_DEBUG("bta_av_co_audio_setconfig accept reconf=%d", recfg_needed); + + /* Call call-in accepting the configuration */ + bta_av_ci_setconfig(hndl, A2D_SUCCESS, A2D_SUCCESS, 0, NULL, recfg_needed, avdt_handle); + } +} + +/******************************************************************************* + ** + ** Function bta_av_co_audio_open + ** + ** Description This function is called by AV when the audio stream connection + ** is opened. + ** + ** + ** Returns void + ** + *******************************************************************************/ +void bta_av_co_audio_open(tBTA_AV_HNDL hndl, tBTA_AV_CODEC codec_type, UINT8 *p_codec_info, + UINT16 mtu) +{ + tBTA_AV_CO_PEER *p_peer; + UNUSED(p_codec_info); + + FUNC_TRACE(); + + APPL_TRACE_DEBUG("bta_av_co_audio_open mtu:%d codec_type:%d", mtu, codec_type); + + /* Retrieve the peer info */ + p_peer = bta_av_co_get_peer(hndl); + if (p_peer == NULL) { + APPL_TRACE_ERROR("bta_av_co_audio_setconfig could not find peer entry"); + } else { + p_peer->opened = TRUE; + p_peer->mtu = mtu; + } +} + +/******************************************************************************* + ** + ** Function bta_av_co_audio_close + ** + ** Description This function is called by AV when the audio stream connection + ** is closed. + ** + ** + ** Returns void + ** + *******************************************************************************/ +void bta_av_co_audio_close(tBTA_AV_HNDL hndl, tBTA_AV_CODEC codec_type, UINT16 mtu) + +{ + tBTA_AV_CO_PEER *p_peer; + UNUSED(codec_type); + UNUSED(mtu); + + FUNC_TRACE(); + + APPL_TRACE_DEBUG("bta_av_co_audio_close"); + + /* Retrieve the peer info */ + p_peer = bta_av_co_get_peer(hndl); + if (p_peer) { + /* Mark the peer closed and clean the peer info */ + memset(p_peer, 0, sizeof(*p_peer)); + } else { + APPL_TRACE_ERROR("bta_av_co_audio_close could not find peer entry"); + } + + /* reset remote preference through setconfig */ + bta_av_co_cb.codec_cfg_setconfig.id = BTC_AV_CODEC_NONE; +} + +/******************************************************************************* + ** + ** Function bta_av_co_audio_start + ** + ** Description This function is called by AV when the audio streaming data + ** transfer is started. + ** + ** + ** Returns void + ** + *******************************************************************************/ +void bta_av_co_audio_start(tBTA_AV_HNDL hndl, tBTA_AV_CODEC codec_type, + UINT8 *p_codec_info, BOOLEAN *p_no_rtp_hdr) +{ + UNUSED(hndl); + UNUSED(codec_type); + UNUSED(p_codec_info); + UNUSED(p_no_rtp_hdr); + + FUNC_TRACE(); + + APPL_TRACE_DEBUG("bta_av_co_audio_start"); + +} + +/******************************************************************************* + ** + ** Function bta_av_co_audio_stop + ** + ** Description This function is called by AV when the audio streaming data + ** transfer is stopped. + ** + ** + ** Returns void + ** + *******************************************************************************/ +extern void bta_av_co_audio_stop(tBTA_AV_HNDL hndl, tBTA_AV_CODEC codec_type) +{ + UNUSED(hndl); + UNUSED(codec_type); + + FUNC_TRACE(); + + APPL_TRACE_DEBUG("bta_av_co_audio_stop"); +} + +/******************************************************************************* + ** + ** Function bta_av_co_audio_src_data_path + ** + ** Description This function is called to manage data transfer from + ** the audio codec to AVDTP. + ** + ** Returns Pointer to the buffer to send, NULL if no buffer to send + ** + *******************************************************************************/ +void *bta_av_co_audio_src_data_path(tBTA_AV_CODEC codec_type, UINT32 *p_len, + UINT32 *p_timestamp) +{ +#if BTC_AV_SRC_INCLUDED + BT_HDR *p_buf; + UNUSED(p_len); + + FUNC_TRACE(); + + p_buf = btc_a2dp_source_audio_readbuf(); + if (p_buf != NULL) { + switch (codec_type) { + case BTA_AV_CODEC_SBC: + /* In media packet SBC, the following information is available: + * p_buf->layer_specific : number of SBC frames in the packet + * p_buf->word[0] : timestamp + */ + /* Retrieve the timestamp information from the media packet */ + *p_timestamp = *((UINT32 *) (p_buf + 1)); + + /* Set up packet header */ + bta_av_sbc_bld_hdr(p_buf, p_buf->layer_specific); + break; + + + default: + APPL_TRACE_ERROR("bta_av_co_audio_src_data_path Unsupported codec type (%d)", codec_type); + break; + } +#if defined(BTA_AV_CO_CP_SCMS_T) && (BTA_AV_CO_CP_SCMS_T == TRUE) + { + UINT8 *p; + if (bta_av_co_cp_is_active()) { + p_buf->len++; + p_buf->offset--; + p = (UINT8 *)(p_buf + 1) + p_buf->offset; + *p = bta_av_co_cp_get_flag(); + } + } +#endif + } + return p_buf; +#else /* BTC_AV_SRC_INCLUDED */ + return NULL; +#endif /* BTC_AV_SRC_INCLUDED */ +} + +/******************************************************************************* + ** + ** Function bta_av_co_audio_drop + ** + ** Description An Audio packet is dropped. . + ** It's very likely that the connected headset with this handle + ** is moved far away. The implementation may want to reduce + ** the encoder bit rate setting to reduce the packet size. + ** + ** Returns void + ** + *******************************************************************************/ +void bta_av_co_audio_drop(tBTA_AV_HNDL hndl) +{ + FUNC_TRACE(); + + APPL_TRACE_ERROR("bta_av_co_audio_drop dropped: x%x", hndl); +} + +/******************************************************************************* + ** + ** Function bta_av_co_audio_delay + ** + ** Description This function is called by AV when the audio stream connection + ** needs to send the initial delay report to the connected SRC. + ** + ** + ** Returns void + ** + *******************************************************************************/ +void bta_av_co_audio_delay(tBTA_AV_HNDL hndl, UINT16 delay) +{ + FUNC_TRACE(); + + btc_source_report_delay_value(delay); + + APPL_TRACE_DEBUG("bta_av_co_audio_delay handle: x%x, delay:0x%x", hndl, delay); +} + + + +/******************************************************************************* + ** + ** Function bta_av_co_audio_codec_build_config + ** + ** Description Build the codec configuration + ** + ** Returns TRUE if the codec was built successfully, FALSE otherwise + ** + *******************************************************************************/ +static BOOLEAN bta_av_co_audio_codec_build_config(const UINT8 *p_codec_caps, UINT8 *p_codec_cfg) +{ + FUNC_TRACE(); + + memset(p_codec_cfg, 0, AVDT_CODEC_SIZE); + + switch (bta_av_co_cb.codec_cfg.id) { + case BTC_AV_CODEC_SBC: + /* only copy the relevant portions for this codec to avoid issues when + comparing codec configs covering larger codec sets than SBC (7 bytes) */ + memcpy(p_codec_cfg, bta_av_co_cb.codec_cfg.info, BTA_AV_CO_SBC_MAX_BITPOOL_OFF + 1); + + /* Update the bit pool boundaries with the codec capabilities */ + p_codec_cfg[BTA_AV_CO_SBC_MIN_BITPOOL_OFF] = p_codec_caps[BTA_AV_CO_SBC_MIN_BITPOOL_OFF]; + p_codec_cfg[BTA_AV_CO_SBC_MAX_BITPOOL_OFF] = p_codec_caps[BTA_AV_CO_SBC_MAX_BITPOOL_OFF]; + + APPL_TRACE_EVENT("bta_av_co_audio_codec_build_config : bitpool min %d, max %d", + p_codec_cfg[BTA_AV_CO_SBC_MIN_BITPOOL_OFF], + p_codec_caps[BTA_AV_CO_SBC_MAX_BITPOOL_OFF]); + break; + default: + APPL_TRACE_ERROR("bta_av_co_audio_codec_build_config: unsupported codec id %d", bta_av_co_cb.codec_cfg.id); + return FALSE; + break; + } + return TRUE; +} + +/******************************************************************************* + ** + ** Function bta_av_co_audio_codec_cfg_matches_caps + ** + ** Description Check if a codec config matches a codec capabilities + ** + ** Returns TRUE if it codec config is supported, FALSE otherwise + ** + *******************************************************************************/ +static BOOLEAN bta_av_co_audio_codec_cfg_matches_caps(UINT8 codec_id, const UINT8 *p_codec_caps, const UINT8 *p_codec_cfg) +{ + FUNC_TRACE(); + + switch (codec_id) { + case BTC_AV_CODEC_SBC: + + APPL_TRACE_EVENT("bta_av_co_audio_codec_cfg_matches_caps : min %d/%d max %d/%d", + p_codec_caps[BTA_AV_CO_SBC_MIN_BITPOOL_OFF], + p_codec_cfg[BTA_AV_CO_SBC_MIN_BITPOOL_OFF], + p_codec_caps[BTA_AV_CO_SBC_MAX_BITPOOL_OFF], + p_codec_cfg[BTA_AV_CO_SBC_MAX_BITPOOL_OFF]); + + /* Must match all items exactly except bitpool boundaries which can be adjusted */ + if (!((p_codec_caps[BTA_AV_CO_SBC_FREQ_CHAN_OFF] & p_codec_cfg[BTA_AV_CO_SBC_FREQ_CHAN_OFF]) && + (p_codec_caps[BTA_AV_CO_SBC_BLOCK_BAND_OFF] & p_codec_cfg[BTA_AV_CO_SBC_BLOCK_BAND_OFF]))) { + APPL_TRACE_EVENT("FALSE %x %x %x %x", + p_codec_caps[BTA_AV_CO_SBC_FREQ_CHAN_OFF], + p_codec_cfg[BTA_AV_CO_SBC_FREQ_CHAN_OFF], + p_codec_caps[BTA_AV_CO_SBC_BLOCK_BAND_OFF], + p_codec_cfg[BTA_AV_CO_SBC_BLOCK_BAND_OFF]); + return FALSE; + } + break; + + + default: + APPL_TRACE_ERROR("bta_av_co_audio_codec_cfg_matches_caps: unsupported codec id %d", codec_id); + return FALSE; + break; + } + APPL_TRACE_EVENT("TRUE"); + + return TRUE; +} + +/******************************************************************************* + ** + ** Function bta_av_co_audio_codec_match + ** + ** Description Check if a codec capabilities supports the codec config + ** + ** Returns TRUE if the connection supports this codec, FALSE otherwise + ** + *******************************************************************************/ +static BOOLEAN bta_av_co_audio_codec_match(const UINT8 *p_codec_caps) +{ + FUNC_TRACE(); + + return bta_av_co_audio_codec_cfg_matches_caps(bta_av_co_cb.codec_cfg.id, p_codec_caps, bta_av_co_cb.codec_cfg.info); +} + +/******************************************************************************* + ** + ** Function bta_av_co_audio_peer_reset_config + ** + ** Description Reset the peer codec configuration + ** + ** Returns Nothing + ** + *******************************************************************************/ +static void bta_av_co_audio_peer_reset_config(tBTA_AV_CO_PEER *p_peer) +{ + FUNC_TRACE(); + + /* Indicate that there is no currently selected sink */ + p_peer->p_snk = NULL; +} + +/******************************************************************************* + ** + ** Function bta_av_co_cp_is_scmst + ** + ** Description Check if a content protection service is SCMS-T + ** + ** Returns TRUE if this CP is SCMS-T, FALSE otherwise + ** + *******************************************************************************/ +static BOOLEAN bta_av_co_cp_is_scmst(const UINT8 *p_protectinfo) +{ + UINT16 cp_id; + FUNC_TRACE(); + + if (*p_protectinfo >= BTA_AV_CP_LOSC) { + p_protectinfo++; + STREAM_TO_UINT16(cp_id, p_protectinfo); + if (cp_id == BTA_AV_CP_SCMS_T_ID) { + APPL_TRACE_DEBUG("bta_av_co_cp_is_scmst: SCMS-T found"); + return TRUE; + } + } + + return FALSE; +} + +/******************************************************************************* + ** + ** Function bta_av_co_audio_sink_has_scmst + ** + ** Description Check if a sink supports SCMS-T + ** + ** Returns TRUE if the sink supports this CP, FALSE otherwise + ** + *******************************************************************************/ +static BOOLEAN bta_av_co_audio_sink_has_scmst(const tBTA_AV_CO_SINK *p_sink) +{ + UINT8 index; + const UINT8 *p; + FUNC_TRACE(); + + /* Check if sink supports SCMS-T */ + index = p_sink->num_protect; + p = &p_sink->protect_info[0]; + + while (index) { + if (bta_av_co_cp_is_scmst(p)) { + return TRUE; + } + /* Move to the next SC */ + p += *p + 1; + /* Decrement the SC counter */ + index--; + } + APPL_TRACE_DEBUG("bta_av_co_audio_sink_has_scmst: SCMS-T not found"); + return FALSE; +} + +/******************************************************************************* + ** + ** Function bta_av_co_audio_sink_supports_cp + ** + ** Description Check if a sink supports the current content protection + ** + ** Returns TRUE if the sink supports this CP, FALSE otherwise + ** + *******************************************************************************/ +static BOOLEAN bta_av_co_audio_sink_supports_cp(const tBTA_AV_CO_SINK *p_sink) +{ + FUNC_TRACE(); + + /* Check if content protection is enabled for this stream */ + if (bta_av_co_cp_get_flag() != BTA_AV_CP_SCMS_COPY_FREE) { + return bta_av_co_audio_sink_has_scmst(p_sink); + } else { + APPL_TRACE_DEBUG("bta_av_co_audio_sink_supports_cp: not required"); + return TRUE; + } +} + +/******************************************************************************* + ** + ** Function bta_av_co_audio_peer_supports_codec + ** + ** Description Check if a connection supports the codec config + ** + ** Returns TRUE if the connection supports this codec, FALSE otherwise + ** + *******************************************************************************/ +static BOOLEAN bta_av_co_audio_peer_supports_codec(tBTA_AV_CO_PEER *p_peer, UINT8 *p_snk_index) +{ + int index; + UINT8 codec_type; + FUNC_TRACE(); + + /* Configure the codec type to look for */ + codec_type = bta_av_co_cb.codec_cfg.id; + + + for (index = 0; index < p_peer->num_sup_snks; index++) { + if (p_peer->snks[index].codec_type == codec_type) { + switch (bta_av_co_cb.codec_cfg.id) { + case BTC_AV_CODEC_SBC: + if (p_snk_index) { + *p_snk_index = index; + } + return bta_av_co_audio_codec_match(p_peer->snks[index].codec_caps); + break; + + + default: + APPL_TRACE_ERROR("bta_av_co_audio_peer_supports_codec: unsupported codec id %d", bta_av_co_cb.codec_cfg.id); + return FALSE; + break; + } + } + } + return FALSE; +} + +/******************************************************************************* + ** + ** Function bta_av_co_audio_peer_src_supports_codec + ** + ** Description Check if a peer acting as src supports codec config + ** + ** Returns TRUE if the connection supports this codec, FALSE otherwise + ** + *******************************************************************************/ +static BOOLEAN bta_av_co_audio_peer_src_supports_codec(tBTA_AV_CO_PEER *p_peer, UINT8 *p_src_index) +{ + int index; + UINT8 codec_type; + FUNC_TRACE(); + + /* Configure the codec type to look for */ + codec_type = bta_av_co_cb.codec_cfg.id; + + + for (index = 0; index < p_peer->num_sup_srcs; index++) { + if (p_peer->srcs[index].codec_type == codec_type) { + switch (bta_av_co_cb.codec_cfg.id) { + case BTC_AV_CODEC_SBC: + if (p_src_index) { + *p_src_index = index; + } + if (0 == bta_av_sbc_cfg_matches_cap((UINT8 *)p_peer->srcs[index].codec_caps, + (tA2D_SBC_CIE *)&bta_av_co_sbc_sink_caps)) { + return TRUE; + } + break; + + default: + APPL_TRACE_ERROR("peer_src_supports_codec: unsupported codec id %d", + bta_av_co_cb.codec_cfg.id); + return FALSE; + break; + } + } + } + return FALSE; +} + +/******************************************************************************* + ** + ** Function bta_av_co_audio_sink_supports_config + ** + ** Description Check if the media source supports a given configuration + ** + ** Returns TRUE if the media source supports this config, FALSE otherwise + ** + *******************************************************************************/ +static UINT8 bta_av_co_audio_sink_supports_config(UINT8 codec_type, const UINT8 *p_codec_cfg) +{ + FUNC_TRACE(); + UINT8 status = A2D_BAD_CODEC_TYPE; + + switch (codec_type) { + case BTA_AV_CODEC_SBC: + status = bta_av_sbc_cfg_in_cap((UINT8 *)p_codec_cfg, (tA2D_SBC_CIE *)&bta_av_co_sbc_sink_caps); + break; + case BTA_AV_CODEC_M12: + case BTA_AV_CODEC_M24: + case BTA_AV_CODEC_ATRAC: + status = A2D_NS_CODEC_TYPE; + APPL_TRACE_ERROR("bta_av_co_audio_sink_supports_config unsupported codec type %d", codec_type); + break; + default: + APPL_TRACE_ERROR("bta_av_co_audio_sink_supports_config invalid codec type %d", codec_type); + break; + } + return status; +} + +/******************************************************************************* + ** + ** Function bta_av_co_audio_media_supports_config + ** + ** Description Check if the media sink supports a given configuration + ** + ** Returns TRUE if the media source supports this config, FALSE otherwise + ** + *******************************************************************************/ +static UINT8 bta_av_co_audio_media_supports_config(UINT8 codec_type, const UINT8 *p_codec_cfg) +{ + FUNC_TRACE(); + UINT8 status = A2D_BAD_CODEC_TYPE; + + switch (codec_type) { + case BTA_AV_CODEC_SBC: + status = bta_av_sbc_cfg_in_cap((UINT8 *)p_codec_cfg, (tA2D_SBC_CIE *)&bta_av_co_sbc_caps); + break; + case BTA_AV_CODEC_M12: + case BTA_AV_CODEC_M24: + case BTA_AV_CODEC_ATRAC: + status = A2D_NS_CODEC_TYPE; + APPL_TRACE_ERROR("bta_av_co_audio_media_supports_config unsupported codec type %d", codec_type); + break; + default: + APPL_TRACE_ERROR("bta_av_co_audio_media_supports_config invalid codec type %d", codec_type); + break; + } + return status; +} + +/******************************************************************************* + ** + ** Function bta_av_co_audio_codec_supported + ** + ** Description Check if all opened connections are compatible with a codec + ** configuration and content protection + ** + ** Returns TRUE if all opened devices support this codec, FALSE otherwise + ** + *******************************************************************************/ +BOOLEAN bta_av_co_audio_codec_supported(tBTC_AV_STATUS *p_status) +{ + UINT8 index; + UINT8 snk_index; + tBTA_AV_CO_PEER *p_peer; + tBTA_AV_CO_SINK *p_sink; + UINT8 codec_cfg[AVDT_CODEC_SIZE]; + UINT8 num_protect = 0; +#if defined(BTA_AV_CO_CP_SCMS_T) && (BTA_AV_CO_CP_SCMS_T == TRUE) + BOOLEAN cp_active; +#endif + + FUNC_TRACE(); + + APPL_TRACE_DEBUG("bta_av_co_audio_codec_supported"); + + /* Check AV feeding is supported */ + *p_status = BTC_ERROR_SRV_AV_FEEDING_NOT_SUPPORTED; + + for (index = 0; index < BTA_AV_CO_NUM_ELEMENTS(bta_av_co_cb.peers); index++) { + p_peer = &bta_av_co_cb.peers[index]; + if (p_peer->opened) { + if (bta_av_co_audio_peer_supports_codec(p_peer, &snk_index)) { + p_sink = &p_peer->snks[snk_index]; + + /* Check that this sink is compatible with the CP */ + if (!bta_av_co_audio_sink_supports_cp(p_sink)) { + APPL_TRACE_DEBUG("bta_av_co_audio_codec_supported sink %d of peer %d doesn't support cp", + snk_index, index); + *p_status = BTC_ERROR_SRV_AV_CP_NOT_SUPPORTED; + return FALSE; + } + + /* Build the codec configuration for this sink */ + if (bta_av_co_audio_codec_build_config(p_sink->codec_caps, codec_cfg)) { +#if defined(BTA_AV_CO_CP_SCMS_T) && (BTA_AV_CO_CP_SCMS_T == TRUE) + /* Check if this sink supports SCMS */ + cp_active = bta_av_co_audio_sink_has_scmst(p_sink); +#endif + /* Check if this is a new configuration (new sink or new config) */ + if ((p_sink != p_peer->p_snk) || + (memcmp(codec_cfg, p_peer->codec_cfg, AVDT_CODEC_SIZE)) +#if defined(BTA_AV_CO_CP_SCMS_T) && (BTA_AV_CO_CP_SCMS_T == TRUE) + || (p_peer->cp_active != cp_active) +#endif + ) { + /* Save the new configuration */ + p_peer->p_snk = p_sink; + memcpy(p_peer->codec_cfg, codec_cfg, AVDT_CODEC_SIZE); +#if defined(BTA_AV_CO_CP_SCMS_T) && (BTA_AV_CO_CP_SCMS_T == TRUE) + p_peer->cp_active = cp_active; + if (p_peer->cp_active) { + bta_av_co_cb.cp.active = TRUE; + num_protect = BTA_AV_CP_INFO_LEN; + } else { + bta_av_co_cb.cp.active = FALSE; + } +#endif + APPL_TRACE_DEBUG("bta_av_co_audio_codec_supported call BTA_AvReconfig(x%x)", BTA_AV_CO_AUDIO_INDX_TO_HNDL(index)); + BTA_AvReconfig(BTA_AV_CO_AUDIO_INDX_TO_HNDL(index), TRUE, p_sink->sep_info_idx, + p_peer->codec_cfg, num_protect, (UINT8 *)bta_av_co_cp_scmst); + } + } + } else { + APPL_TRACE_DEBUG("bta_av_co_audio_codec_supported index %d doesn't support codec", index); + return FALSE; + } + } + } + + *p_status = BTC_AV_SUCCESS; + return TRUE; +} + +/******************************************************************************* + ** + ** Function bta_av_co_audio_codec_reset + ** + ** Description Reset the current codec configuration + ** + ** Returns void + ** + *******************************************************************************/ +void bta_av_co_audio_codec_reset(void) +{ + osi_mutex_global_lock(); + FUNC_TRACE(); + + /* Reset the current configuration to SBC */ + bta_av_co_cb.codec_cfg.id = BTC_AV_CODEC_SBC; + + if (A2D_BldSbcInfo(A2D_MEDIA_TYPE_AUDIO, (tA2D_SBC_CIE *)&btc_av_sbc_default_config, bta_av_co_cb.codec_cfg.info) != A2D_SUCCESS) { + APPL_TRACE_ERROR("bta_av_co_audio_codec_reset A2D_BldSbcInfo failed"); + } + + osi_mutex_global_unlock(); +} + +/******************************************************************************* + ** + ** Function bta_av_co_audio_set_codec + ** + ** Description Set the current codec configuration from the feeding type. + ** This function is starting to modify the configuration, it + ** should be protected. + ** + ** Returns TRUE if successful, FALSE otherwise + ** + *******************************************************************************/ +BOOLEAN bta_av_co_audio_set_codec(const tBTC_AV_MEDIA_FEEDINGS *p_feeding, tBTC_AV_STATUS *p_status) +{ + tA2D_SBC_CIE sbc_config; + tBTC_AV_CODEC_INFO new_cfg; + + FUNC_TRACE(); + + /* Check AV feeding is supported */ + *p_status = BTC_ERROR_SRV_AV_FEEDING_NOT_SUPPORTED; + + APPL_TRACE_DEBUG("bta_av_co_audio_set_codec cid=%d", p_feeding->format); + + /* Supported codecs */ + switch (p_feeding->format) { + case BTC_AV_CODEC_PCM: + new_cfg.id = BTC_AV_CODEC_SBC; + + sbc_config = btc_av_sbc_default_config; + if ((p_feeding->cfg.pcm.num_channel != 1) && + (p_feeding->cfg.pcm.num_channel != 2)) { + APPL_TRACE_ERROR("bta_av_co_audio_set_codec PCM channel number unsupported"); + return FALSE; + } + if ((p_feeding->cfg.pcm.bit_per_sample != 8) && + (p_feeding->cfg.pcm.bit_per_sample != 16)) { + APPL_TRACE_ERROR("bta_av_co_audio_set_codec PCM sample size unsupported"); + return FALSE; + } + switch (p_feeding->cfg.pcm.sampling_freq) { + case 8000: + case 12000: + case 16000: + case 24000: + case 32000: + case 48000: + sbc_config.samp_freq = A2D_SBC_IE_SAMP_FREQ_48; + break; + + case 11025: + case 22050: + case 44100: + sbc_config.samp_freq = A2D_SBC_IE_SAMP_FREQ_44; + break; + default: + APPL_TRACE_ERROR("bta_av_co_audio_set_codec PCM sampling frequency unsupported"); + return FALSE; + break; + } + /* Build the codec config */ + if (A2D_BldSbcInfo(A2D_MEDIA_TYPE_AUDIO, &sbc_config, new_cfg.info) != A2D_SUCCESS) { + APPL_TRACE_ERROR("bta_av_co_audio_set_codec A2D_BldSbcInfo failed"); + return FALSE; + } + break; + + + default: + APPL_TRACE_ERROR("bta_av_co_audio_set_codec Feeding format unsupported"); + return FALSE; + break; + } + + /* The new config was correctly built */ + bta_av_co_cb.codec_cfg = new_cfg; + + + /* Check all devices support it */ + *p_status = BTC_AV_SUCCESS; + return bta_av_co_audio_codec_supported(p_status); +} + +/******************************************************************************* + ** + ** Function bta_av_co_audio_get_sbc_config + ** + ** Description Retrieves the SBC codec configuration. If the codec in use + ** is not SBC, return the default SBC codec configuration. + ** + ** Returns TRUE if codec is SBC, FALSE otherwise + ** + *******************************************************************************/ +BOOLEAN bta_av_co_audio_get_sbc_config(tA2D_SBC_CIE *p_sbc_config, UINT16 *p_minmtu) +{ + BOOLEAN result = FALSE; + UINT8 index, jndex; + tBTA_AV_CO_PEER *p_peer; + tBTA_AV_CO_SINK *p_sink; + + APPL_TRACE_EVENT("bta_av_co_cb.codec_cfg.id : codec 0x%x", bta_av_co_cb.codec_cfg.id); + + /* Minimum MTU is by default very large */ + *p_minmtu = 0xFFFF; + + osi_mutex_global_lock(); + if (bta_av_co_cb.codec_cfg.id == BTC_AV_CODEC_SBC) { + if (A2D_ParsSbcInfo(p_sbc_config, bta_av_co_cb.codec_cfg.info, FALSE) == A2D_SUCCESS) { + for (index = 0; index < BTA_AV_CO_NUM_ELEMENTS(bta_av_co_cb.peers); index++) { + p_peer = &bta_av_co_cb.peers[index]; + if (p_peer->opened) { + if (p_peer->mtu < *p_minmtu) { + *p_minmtu = p_peer->mtu; + } + for (jndex = 0; jndex < p_peer->num_sup_snks; jndex++) { + p_sink = &p_peer->snks[jndex]; + if (p_sink->codec_type == A2D_MEDIA_CT_SBC) { + /* Update the bitpool boundaries of the current config */ + p_sbc_config->min_bitpool = + BTA_AV_CO_MAX(p_sink->codec_caps[BTA_AV_CO_SBC_MIN_BITPOOL_OFF], + p_sbc_config->min_bitpool); + p_sbc_config->max_bitpool = + BTA_AV_CO_MIN(p_sink->codec_caps[BTA_AV_CO_SBC_MAX_BITPOOL_OFF], + p_sbc_config->max_bitpool); + APPL_TRACE_EVENT("bta_av_co_audio_get_sbc_config : sink bitpool min %d, max %d", + p_sbc_config->min_bitpool, p_sbc_config->max_bitpool); + break; + } + } + } + } + result = TRUE; + } + } + + if (!result) { + /* Not SBC, still return the default values */ + *p_sbc_config = btc_av_sbc_default_config; + } + osi_mutex_global_unlock(); + + return result; +} + +/******************************************************************************* + ** + ** Function bta_av_co_audio_discard_config + ** + ** Description Discard the codec configuration of a connection + ** + ** Returns Nothing + ** + *******************************************************************************/ +void bta_av_co_audio_discard_config(tBTA_AV_HNDL hndl) +{ + tBTA_AV_CO_PEER *p_peer; + + FUNC_TRACE(); + + /* Find the peer info */ + p_peer = bta_av_co_get_peer(hndl); + if (p_peer == NULL) { + APPL_TRACE_ERROR("bta_av_co_audio_discard_config could not find peer entry"); + return; + } + + /* Reset the peer codec configuration */ + bta_av_co_audio_peer_reset_config(p_peer); +} + +/******************************************************************************* + ** + ** Function bta_av_co_init + ** + ** Description Initialization + ** + ** Returns Nothing + ** + *******************************************************************************/ +void bta_av_co_init(void) +{ + FUNC_TRACE(); + + /* Reset the control block */ + memset(&bta_av_co_cb, 0, sizeof(bta_av_co_cb)); + + bta_av_co_cb.codec_cfg_setconfig.id = BTC_AV_CODEC_NONE; + +#if defined(BTA_AV_CO_CP_SCMS_T) && (BTA_AV_CO_CP_SCMS_T == TRUE) + bta_av_co_cp_set_flag(BTA_AV_CP_SCMS_COPY_NEVER); +#else + bta_av_co_cp_set_flag(BTA_AV_CP_SCMS_COPY_FREE); +#endif + + /* Reset the current config */ + bta_av_co_audio_codec_reset(); +} + + +/******************************************************************************* + ** + ** Function bta_av_co_peer_cp_supported + ** + ** Description Checks if the peer supports CP + ** + ** Returns TRUE if the peer supports CP + ** + *******************************************************************************/ +BOOLEAN bta_av_co_peer_cp_supported(tBTA_AV_HNDL hndl) +{ + tBTA_AV_CO_PEER *p_peer; + tBTA_AV_CO_SINK *p_sink; + UINT8 index; + + FUNC_TRACE(); + + /* Find the peer info */ + p_peer = bta_av_co_get_peer(hndl); + if (p_peer == NULL) { + APPL_TRACE_ERROR("bta_av_co_peer_cp_supported could not find peer entry"); + return FALSE; + } + + for (index = 0; index < p_peer->num_sup_snks; index++) { + p_sink = &p_peer->snks[index]; + if (p_sink->codec_type == A2D_MEDIA_CT_SBC) { + return bta_av_co_audio_sink_has_scmst(p_sink); + } + } + APPL_TRACE_ERROR("bta_av_co_peer_cp_supported did not find SBC sink"); + return FALSE; +} + + +/******************************************************************************* + ** + ** Function bta_av_co_get_remote_bitpool_pref + ** + ** Description Check if remote side did a setconfig within the limits + ** of our exported bitpool range. If set we will set the + ** remote preference. + ** + ** Returns TRUE if config set, FALSE otherwize + ** + *******************************************************************************/ + +BOOLEAN bta_av_co_get_remote_bitpool_pref(UINT8 *min, UINT8 *max) +{ + /* check if remote peer did a set config */ + if (bta_av_co_cb.codec_cfg_setconfig.id == BTC_AV_CODEC_NONE) { + return FALSE; + } + + *min = bta_av_co_cb.codec_cfg_setconfig.info[BTA_AV_CO_SBC_MIN_BITPOOL_OFF]; + *max = bta_av_co_cb.codec_cfg_setconfig.info[BTA_AV_CO_SBC_MAX_BITPOOL_OFF]; + + return TRUE; +} + +/* the call out functions for audio stream */ +const tBTA_AV_CO_FUNCTS bta_av_a2d_cos = { + bta_av_co_audio_init, + bta_av_co_audio_disc_res, + bta_av_co_audio_cfg_res, + bta_av_co_audio_getconfig, + bta_av_co_audio_setconfig, + bta_av_co_audio_open, + bta_av_co_audio_close, + bta_av_co_audio_start, + bta_av_co_audio_stop, + bta_av_co_audio_src_data_path, + bta_av_co_audio_delay +}; + +#endif /* #if BTC_AV_INCLUDED */ diff --git a/lib/bt/host/bluedroid/btc/profile/std/a2dp/btc_a2dp.c b/lib/bt/host/bluedroid/btc/profile/std/a2dp/btc_a2dp.c new file mode 100644 index 00000000..279b1001 --- /dev/null +++ b/lib/bt/host/bluedroid/btc/profile/std/a2dp/btc_a2dp.c @@ -0,0 +1,146 @@ +/* + * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/***************************************************************************** + * + * Filename: btc_a2dp.c + * + *****************************************************************************/ +#include "common/bt_target.h" +#include "common/bt_trace.h" +#include "bta/bta_api.h" +#include "bta/bta_av_api.h" +#include "btc_av.h" +#include "btc_av_co.h" +#include "btc_a2dp.h" +#include "btc_a2dp_control.h" +#include "btc_a2dp_sink.h" +#include "btc_a2dp_source.h" + + +#if BTC_AV_INCLUDED + +/***************************************************************************** +** +** Function btc_a2dp_on_init +** +*******************************************************************************/ +void btc_a2dp_on_init(void) +{ + BTC_TRACE_EVENT("A2DP Initialized."); +} + +/***************************************************************************** +** +** Function btc_a2dp_on_idle +** +*******************************************************************************/ + +void btc_a2dp_on_idle(void) +{ + APPL_TRACE_EVENT("## ON A2DP IDLE ## peer_sep = %d, service id = %d", btc_av_get_peer_sep(), + btc_av_get_service_id()); +#if BTC_AV_SRC_INCLUDED + if (btc_av_get_peer_sep() == AVDT_TSEP_SNK && btc_av_get_service_id() == BTA_A2DP_SOURCE_SERVICE_ID) { + btc_a2dp_source_on_idle(); + } +#endif // BTC_AV_SRC_INCLUDED + + bta_av_co_init(); + +#if BTC_AV_SINK_INCLUDED + if (btc_av_get_peer_sep() == AVDT_TSEP_SRC && btc_av_get_service_id() == BTA_A2DP_SINK_SERVICE_ID) { + btc_a2dp_sink_on_idle(); + } +#endif // BTC_AV_SINK_INCLUDED +} + +/***************************************************************************** +** +** Function btc_a2dp_on_started +** +** Description +** +** Returns +** +*******************************************************************************/ + +BOOLEAN btc_a2dp_on_started(tBTA_AV_START *p_av, BOOLEAN pending_start) +{ + BOOLEAN ack = FALSE; + + APPL_TRACE_EVENT("## ON A2DP STARTED ##"); +#if BTC_AV_SRC_INCLUDED + if (p_av == NULL) { + /* ack back a local start request */ + btc_a2dp_control_command_ack(ESP_A2D_MEDIA_CTRL_ACK_SUCCESS); + return TRUE; + } + + if (p_av->status == BTA_AV_SUCCESS) { + if (p_av->suspending == FALSE) { + if (p_av->initiator) { + if (pending_start) { + btc_a2dp_control_command_ack(ESP_A2D_MEDIA_CTRL_ACK_SUCCESS); + ack = TRUE; + } + } else { + /* we were remotely started, make sure codec + is setup before datapath is started */ + btc_a2dp_source_setup_codec(); + } + + /* media task is autostarted upon a2dp audiopath connection */ + } + } else if (pending_start) { + btc_a2dp_control_command_ack(ESP_A2D_MEDIA_CTRL_ACK_FAILURE); + ack = TRUE; + } +#endif /* BTC_AV_SRC_INCLUDED */ + return ack; +} + +/***************************************************************************** +** +** Function btc_a2dp_on_stopped +** +*******************************************************************************/ + +void btc_a2dp_on_stopped(tBTA_AV_SUSPEND *p_av) +{ + APPL_TRACE_EVENT("## ON A2DP STOPPED ##"); +#if BTC_AV_SINK_INCLUDED + if (btc_av_get_peer_sep() == AVDT_TSEP_SRC && btc_av_get_service_id() == BTA_A2DP_SINK_SERVICE_ID) { + btc_a2dp_sink_on_stopped(p_av); + return; + } +#endif // BTC_AV_SINK_INCLUDED + +#if BTC_AV_SRC_INCLUDED + btc_a2dp_source_on_stopped(p_av); +#endif // BTC_AV_SRC_INCLUDED +} + +/***************************************************************************** +** +** Function btc_a2dp_on_suspended +** +*******************************************************************************/ +void btc_a2dp_on_suspended(tBTA_AV_SUSPEND *p_av) +{ + APPL_TRACE_EVENT("## ON A2DP SUSPENDED ##"); +#if BTC_AV_SINK_INCLUDED + if (btc_av_get_peer_sep() == AVDT_TSEP_SRC && btc_av_get_service_id() == BTA_A2DP_SINK_SERVICE_ID) { + btc_a2dp_sink_on_suspended(p_av); + return; + } +#endif // BTC_AV_SINK_INCLUDED +#if BTC_AV_SRC_INCLUDED + btc_a2dp_source_on_suspended(p_av); +#endif // BTC_AV_SRC_INCLUDED +} + +#endif /* #if BTC_AV_INCLUDED */ diff --git a/lib/bt/host/bluedroid/btc/profile/std/a2dp/btc_a2dp_control.c b/lib/bt/host/bluedroid/btc/profile/std/a2dp/btc_a2dp_control.c new file mode 100644 index 00000000..ba264f93 --- /dev/null +++ b/lib/bt/host/bluedroid/btc/profile/std/a2dp/btc_a2dp_control.c @@ -0,0 +1,189 @@ +/* + * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/***************************************************************************** + * + * Filename: btc_a2dp_control.c + * + *****************************************************************************/ +#include "common/bt_target.h" +#include +#include "common/bt_trace.h" +#include "bta/bta_api.h" +#include "bta/bta_av_api.h" +#include "btc/btc_manage.h" +#include "btc_av.h" +#include "btc_a2dp.h" +#include "btc_a2dp_control.h" +#include "btc_a2dp_sink.h" +#include "btc_a2dp_source.h" +#include "esp_a2dp_api.h" + +#if BTC_AV_INCLUDED + +typedef struct { + BOOLEAN data_channel_open; /* used only by A2DP sink */ + UINT8 a2dp_cmd_pending; /* we can have max one command pending */ +} tBTC_AA_CTRL_CB; + +static tBTC_AA_CTRL_CB btc_aa_ctrl_cb; + +static inline void btc_a2d_cb_to_app(esp_a2d_cb_event_t event, esp_a2d_cb_param_t *param) +{ + esp_a2d_cb_t btc_a2d_cb = (esp_a2d_cb_t)btc_profile_cb_get(BTC_PID_A2DP); + if (btc_a2d_cb) { + btc_a2d_cb(event, param); + } +} + +static inline void a2dp_cmd_acknowledge(int cmd, int status) +{ + esp_a2d_cb_param_t param; + + param.media_ctrl_stat.cmd = cmd; + param.media_ctrl_stat.status = status; + + btc_a2d_cb_to_app(ESP_A2D_MEDIA_CTRL_ACK_EVT, ¶m); +} + +void btc_a2dp_control_command_ack(int status) +{ + /* sanity check */ + if (btc_aa_ctrl_cb.a2dp_cmd_pending == ESP_A2D_MEDIA_CTRL_NONE) { + APPL_TRACE_ERROR("warning : no command pending, ignore ack"); + return; + } + + /* clear pending */ + int cmd = btc_aa_ctrl_cb.a2dp_cmd_pending; + btc_aa_ctrl_cb.a2dp_cmd_pending = ESP_A2D_MEDIA_CTRL_NONE; + + a2dp_cmd_acknowledge(cmd, status); +} + +static void btc_a2dp_datapath_open(void) +{ +#if BTC_AV_SRC_INCLUDED + if (btc_av_get_peer_sep() == AVDT_TSEP_SNK && btc_av_get_service_id() == BTA_A2DP_SOURCE_SERVICE_ID) { + /* Start the media task to encode SBC */ + btc_a2dp_source_start_audio_req(); + + /* make sure we update any changed sbc encoder params */ + btc_a2dp_source_encoder_update(); + } +#endif +#if (BTC_AV_SINK_INCLUDED == TRUE) + btc_aa_ctrl_cb.data_channel_open = TRUE; +#endif +} + +BOOLEAN btc_a2dp_control_get_datachnl_stat(void) +{ + return btc_aa_ctrl_cb.data_channel_open; +} + +void btc_a2dp_control_set_datachnl_stat(BOOLEAN open) +{ + btc_aa_ctrl_cb.data_channel_open = open; +} + +void btc_a2dp_control_media_ctrl(esp_a2d_media_ctrl_t ctrl) +{ + APPL_TRACE_DEBUG("BTC MEDIA (A2DP-DATA) EVENT %u", ctrl); + + if (btc_aa_ctrl_cb.a2dp_cmd_pending != ESP_A2D_MEDIA_CTRL_NONE) { + APPL_TRACE_WARNING("un-acked a2dp cmd: %u", btc_aa_ctrl_cb.a2dp_cmd_pending); + a2dp_cmd_acknowledge(ctrl, ESP_A2D_MEDIA_CTRL_ACK_BUSY); + return; + } + + btc_aa_ctrl_cb.a2dp_cmd_pending = ctrl; + + switch (ctrl) { + case ESP_A2D_MEDIA_CTRL_CHECK_SRC_RDY: +#if BTC_AV_SRC_INCLUDED + if (btc_av_get_service_id() == BTA_A2DP_SOURCE_SERVICE_ID) { + if (btc_a2dp_source_is_task_shutting_down()) { + btc_a2dp_control_command_ack(ESP_A2D_MEDIA_CTRL_ACK_FAILURE); + } else if ((btc_av_stream_ready() == TRUE) || + (btc_av_stream_started_ready() == TRUE)) { + /* check whether av is ready to setup a2dp datapath */ + btc_a2dp_control_command_ack(ESP_A2D_MEDIA_CTRL_ACK_SUCCESS); + } else { + btc_a2dp_control_command_ack(ESP_A2D_MEDIA_CTRL_ACK_FAILURE); + } + } else { + btc_a2dp_control_command_ack(ESP_A2D_MEDIA_CTRL_ACK_FAILURE); + } +#else /* BTC_AV_SRC_INCLUDED */ + btc_a2dp_control_command_ack(ESP_A2D_MEDIA_CTRL_ACK_FAILURE); +#endif /* #if BTC_AV_SRC_INCLUDED */ + break; + case ESP_A2D_MEDIA_CTRL_START: + if (btc_av_stream_ready() == TRUE ) { + /* post start event */ + btc_dispatch_sm_event(BTC_AV_START_STREAM_REQ_EVT, NULL, 0); +#if (BTC_AV_SINK_INCLUDED == TRUE) + if (btc_av_get_peer_sep() == AVDT_TSEP_SRC && btc_av_get_service_id() == BTA_A2DP_SINK_SERVICE_ID) { + btc_a2dp_control_command_ack(ESP_A2D_MEDIA_CTRL_ACK_SUCCESS); + } +#endif + } else if (btc_av_stream_started_ready()) { + btc_a2dp_control_command_ack(ESP_A2D_MEDIA_CTRL_ACK_SUCCESS); + } else { + btc_a2dp_control_command_ack(ESP_A2D_MEDIA_CTRL_ACK_FAILURE); + } + break; + case ESP_A2D_MEDIA_CTRL_SUSPEND: + /* local suspend */ + if (btc_av_stream_started_ready()) { + btc_dispatch_sm_event(BTC_AV_SUSPEND_STREAM_REQ_EVT, NULL, 0); +#if (BTC_AV_SINK_INCLUDED == TRUE) + if (btc_av_get_peer_sep() == AVDT_TSEP_SRC && btc_av_get_service_id() == BTA_A2DP_SINK_SERVICE_ID) { + btc_a2dp_control_command_ack(ESP_A2D_MEDIA_CTRL_ACK_SUCCESS); + } +#endif + } else if (btc_av_is_connected() == TRUE) { + /* we are not in started state; just ack back ok. This can happen if we are + remotely suspended; clear REMOTE SUSPEND Flag */ + btc_av_clear_remote_suspend_flag(); + btc_a2dp_control_command_ack(ESP_A2D_MEDIA_CTRL_ACK_SUCCESS); + } else { + btc_a2dp_control_command_ack(ESP_A2D_MEDIA_CTRL_ACK_FAILURE); + } + break; + default : + APPL_TRACE_ERROR("### A2DP-MEDIA EVENT %u NOT HANDLED ###", ctrl); + btc_a2dp_control_command_ack(ESP_A2D_MEDIA_CTRL_ACK_FAILURE); + break; + } +} + +void btc_a2dp_control_datapath_ctrl(uint32_t dp_evt) +{ + switch (dp_evt) { + case BTC_AV_DATAPATH_OPEN_EVT: { + btc_a2dp_datapath_open(); + break; + } + default: + break; + } + return; +} + +bool btc_a2dp_control_init(void) +{ + memset(&btc_aa_ctrl_cb, 0, sizeof(tBTC_AA_CTRL_CB)); + return true; +} + +void btc_a2dp_control_cleanup(void) +{ + memset(&btc_aa_ctrl_cb, 0, sizeof(tBTC_AA_CTRL_CB)); +} + +#endif /* #if BTC_AV_INCLUDED */ diff --git a/lib/bt/host/bluedroid/btc/profile/std/a2dp/btc_a2dp_sink.c b/lib/bt/host/bluedroid/btc/profile/std/a2dp/btc_a2dp_sink.c new file mode 100644 index 00000000..4a1f060f --- /dev/null +++ b/lib/bt/host/bluedroid/btc/profile/std/a2dp/btc_a2dp_sink.c @@ -0,0 +1,752 @@ +/* + * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/****************************************************************************** + ** + ** Name: btc_a2dp_sink.c + ** + ******************************************************************************/ +#include "common/bt_target.h" +#include "common/bt_trace.h" +#include +#include +#include "common/bt_defs.h" +#include "osi/allocator.h" +#include "osi/mutex.h" +#include "osi/thread.h" +#include "osi/fixed_queue.h" +#include "stack/a2d_api.h" +#include "stack/a2d_sbc.h" +#include "bta/bta_av_api.h" +#include "bta/bta_av_ci.h" +#include "btc_av_co.h" +#include "btc_a2dp.h" +#include "btc_a2dp_control.h" +#include "btc_a2dp_sink.h" +#include "btc/btc_manage.h" +#include "btc_av.h" +#include "btc/btc_util.h" +#include "esp_a2dp_api.h" +#include "oi_codec_sbc.h" +#include "oi_status.h" +#include "osi/future.h" +#include + +#if (BTC_AV_SINK_INCLUDED == TRUE) + +/***************************************************************************** + ** Constants + *****************************************************************************/ + +/* BTC media cmd event definition : BTC_MEDIA_TASK_CMD */ +enum { + BTC_MEDIA_TASK_SINK_INIT, + BTC_MEDIA_TASK_SINK_CLEAN_UP, + BTC_MEDIA_FLUSH_AA_RX, + BTC_MEDIA_AUDIO_SINK_CFG_UPDATE, + BTC_MEDIA_AUDIO_SINK_CLEAR_TRACK, +}; + +enum { + BTC_A2DP_SINK_STATE_OFF = 0, + BTC_A2DP_SINK_STATE_ON = 1, + BTC_A2DP_SINK_STATE_SHUTTING_DOWN = 2 +}; + +/* + * CONGESTION COMPENSATION CTRL :: + * + * Thus setting controls how many buffers we will hold in media task + * during temp link congestion. Together with the stack buffer queues + * it controls much temporary a2dp link congestion we can + * compensate for. It however also depends on the default run level of sinks + * jitterbuffers. Depending on type of sink this would vary. + * Ideally the (SRC) max tx buffer capacity should equal the sinks + * jitterbuffer runlevel including any intermediate buffers on the way + * towards the sinks codec. + */ + +/* fixme -- define this in pcm time instead of buffer count */ + +/* The typical runlevel of the tx queue size is ~1 buffer + but due to link flow control or thread preemption in lower + layers we might need to temporarily buffer up data */ + +/* 18 frames is equivalent to 6.89*18*2.9 ~= 360 ms @ 44.1 khz, 20 ms mediatick */ +#define MAX_OUTPUT_A2DP_SNK_FRAME_QUEUE_SZ (25) + +#define BTC_A2DP_SNK_DATA_QUEUE_IDX (1) + +typedef struct { + uint32_t sig; + void *param; +} a2dp_sink_task_evt_t; + +typedef struct { + UINT16 num_frames_to_be_processed; + UINT16 len; + UINT16 offset; + UINT16 layer_specific; +} tBT_SBC_HDR; + +typedef struct { + BOOLEAN rx_flush; /* discards any incoming data when true */ + UINT8 channel_count; + struct osi_event *data_ready_event; + fixed_queue_t *RxSbcQ; + UINT32 sample_rate; +} tBTC_A2DP_SINK_CB; + +typedef struct { + uint16_t expected_seq_num; + bool seq_num_recount; +} a2dp_sink_media_pkt_seq_num_t; + +typedef struct { + tBTC_A2DP_SINK_CB btc_aa_snk_cb; + osi_thread_t *btc_aa_snk_task_hdl; + OI_CODEC_SBC_DECODER_CONTEXT context; + OI_UINT32 contextData[CODEC_DATA_WORDS(2, SBC_CODEC_FAST_FILTER_BUFFERS)]; + OI_INT16 pcmData[15 * SBC_MAX_SAMPLES_PER_FRAME * SBC_MAX_CHANNELS]; + a2dp_sink_media_pkt_seq_num_t media_pkt_seq_num; +} a2dp_sink_local_param_t; + +static void btc_a2dp_sink_thread_init(UNUSED_ATTR void *context); +static void btc_a2dp_sink_thread_cleanup(UNUSED_ATTR void *context); +static void btc_a2dp_sink_flush_q(fixed_queue_t *p_q); +static void btc_a2dp_sink_rx_flush(void); +static int btc_a2dp_sink_get_track_frequency(UINT8 frequency); +static int btc_a2dp_sink_get_track_channel_count(UINT8 channeltype); +/* Handle incoming media packets A2DP SINK streaming*/ +static void btc_a2dp_sink_handle_inc_media(tBT_SBC_HDR *p_msg); +static void btc_a2dp_sink_handle_decoder_reset(tBTC_MEDIA_SINK_CFG_UPDATE *p_msg); +static void btc_a2dp_sink_handle_clear_track(void); +static BOOLEAN btc_a2dp_sink_clear_track(void); + +static void btc_a2dp_sink_data_ready(void *context); + +static int btc_a2dp_sink_state = BTC_A2DP_SINK_STATE_OFF; +static esp_a2d_sink_data_cb_t bt_aa_snk_data_cb = NULL; +#if A2D_DYNAMIC_MEMORY == FALSE +static a2dp_sink_local_param_t a2dp_sink_local_param; +#else +static a2dp_sink_local_param_t *a2dp_sink_local_param_ptr; +#define a2dp_sink_local_param (*a2dp_sink_local_param_ptr) +#endif ///A2D_DYNAMIC_MEMORY == FALSE + +void btc_a2dp_sink_reg_data_cb(esp_a2d_sink_data_cb_t callback) +{ + // todo: critical section protection + bt_aa_snk_data_cb = callback; +} + +static inline void btc_a2d_data_cb_to_app(const uint8_t *data, uint32_t len) +{ + // todo: critical section protection + if (bt_aa_snk_data_cb) { + bt_aa_snk_data_cb(data, len); + } +} + +/***************************************************************************** + ** Misc helper functions + *****************************************************************************/ +static inline void btc_a2d_cb_to_app(esp_a2d_cb_event_t event, esp_a2d_cb_param_t *param) +{ + esp_a2d_cb_t btc_aa_cb = (esp_a2d_cb_t)btc_profile_cb_get(BTC_PID_A2DP); + if (btc_aa_cb) { + btc_aa_cb(event, param); + } +} + +/***************************************************************************** + ** BTC ADAPTATION + *****************************************************************************/ + +static bool btc_a2dp_sink_ctrl(uint32_t sig, void *param) +{ + switch (sig) { + case BTC_MEDIA_TASK_SINK_INIT: + btc_a2dp_sink_thread_init(NULL); + break; + case BTC_MEDIA_TASK_SINK_CLEAN_UP: + btc_a2dp_sink_thread_cleanup(NULL); + break; + case BTC_MEDIA_AUDIO_SINK_CFG_UPDATE: + btc_a2dp_sink_handle_decoder_reset(param); + break; + case BTC_MEDIA_AUDIO_SINK_CLEAR_TRACK: + btc_a2dp_sink_handle_clear_track(); + break; + case BTC_MEDIA_FLUSH_AA_RX: + btc_a2dp_sink_rx_flush(); + break; + default: + APPL_TRACE_WARNING("media task unhandled evt: 0x%x\n", sig); + } + + if (param != NULL) { + osi_free(param); + } + + return true; +} + +bool btc_a2dp_sink_startup(void) +{ + if (btc_a2dp_sink_state != BTC_A2DP_SINK_STATE_OFF) { + APPL_TRACE_ERROR("warning : media task already running"); + return false; + } + +#if A2D_DYNAMIC_MEMORY == TRUE + if ((a2dp_sink_local_param_ptr = (a2dp_sink_local_param_t *)osi_malloc(sizeof(a2dp_sink_local_param_t))) == NULL) { + APPL_TRACE_ERROR("%s malloc failed!", __func__); + return false; + } + memset((void *)a2dp_sink_local_param_ptr, 0, sizeof(a2dp_sink_local_param_t)); +#endif + + APPL_TRACE_EVENT("## A2DP SINK START MEDIA THREAD ##"); + + a2dp_sink_local_param.btc_aa_snk_task_hdl = btc_get_current_thread(); + + if (btc_a2dp_sink_ctrl(BTC_MEDIA_TASK_SINK_INIT, NULL) == false) { + goto error_exit; + } + + APPL_TRACE_EVENT("## A2DP SINK MEDIA THREAD STARTED ##\n"); + + return true; + +error_exit:; + APPL_TRACE_ERROR("%s unable to start up media thread\n", __func__); + a2dp_sink_local_param.btc_aa_snk_task_hdl = NULL; + +#if A2D_DYNAMIC_MEMORY == TRUE + osi_free(a2dp_sink_local_param_ptr); + a2dp_sink_local_param_ptr = NULL; +#endif + + return false; +} + +void btc_a2dp_sink_shutdown(void) +{ + APPL_TRACE_EVENT("## A2DP SINK STOP MEDIA THREAD ##\n"); + + // Exit thread + btc_a2dp_sink_state = BTC_A2DP_SINK_STATE_SHUTTING_DOWN; + + btc_a2dp_sink_ctrl(BTC_MEDIA_TASK_SINK_CLEAN_UP, NULL); + + a2dp_sink_local_param.btc_aa_snk_task_hdl = NULL; + +#if A2D_DYNAMIC_MEMORY == TRUE + osi_free(a2dp_sink_local_param_ptr); + a2dp_sink_local_param_ptr = NULL; +#endif +} + +/***************************************************************************** +** +** Function btc_a2dp_sink_on_idle +** +*******************************************************************************/ + +void btc_a2dp_sink_on_idle(void) +{ + a2dp_sink_local_param.btc_aa_snk_cb.rx_flush = TRUE; + btc_a2dp_sink_rx_flush_req(); + btc_a2dp_sink_clear_track(); + + APPL_TRACE_DEBUG("Stopped BT track"); +} + +/***************************************************************************** +** +** Function btc_a2dp_sink_on_stopped +** +*******************************************************************************/ + +void btc_a2dp_sink_on_stopped(tBTA_AV_SUSPEND *p_av) +{ + a2dp_sink_local_param.btc_aa_snk_cb.rx_flush = TRUE; + btc_a2dp_sink_rx_flush_req(); + btc_a2dp_control_set_datachnl_stat(FALSE); +} + +/***************************************************************************** +** +** Function btc_a2dp_on_suspended +** +*******************************************************************************/ + +void btc_a2dp_sink_on_suspended(tBTA_AV_SUSPEND *p_av) +{ + a2dp_sink_local_param.btc_aa_snk_cb.rx_flush = TRUE; + btc_a2dp_sink_rx_flush_req(); + return; +} + +/******************************************************************************* + ** + ** Function btc_a2dp_sink_clear_track + ** + ** Description + ** + ** Returns TRUE is success + ** + *******************************************************************************/ +static BOOLEAN btc_a2dp_sink_clear_track(void) +{ + return btc_a2dp_sink_ctrl(BTC_MEDIA_AUDIO_SINK_CLEAR_TRACK, NULL); +} + +/* when true media task discards any rx frames */ +void btc_a2dp_sink_set_rx_flush(BOOLEAN enable) +{ + APPL_TRACE_EVENT("## DROP RX %d ##\n", enable); + if (enable == FALSE) { + a2dp_sink_local_param.media_pkt_seq_num.expected_seq_num = 0x1; + a2dp_sink_local_param.media_pkt_seq_num.seq_num_recount = true; + } + a2dp_sink_local_param.btc_aa_snk_cb.rx_flush = enable; +} + +/***************************************************************************** +** +** Function btc_a2dp_sink_reset_decoder +** +** Description +** +** Returns +** +*******************************************************************************/ + +void btc_a2dp_sink_reset_decoder(UINT8 *p_av) +{ + APPL_TRACE_EVENT("btc reset decoder"); + APPL_TRACE_DEBUG("btc reset decoder p_codec_info[%x:%x:%x:%x:%x:%x]\n", + p_av[1], p_av[2], p_av[3], + p_av[4], p_av[5], p_av[6]); + + tBTC_MEDIA_SINK_CFG_UPDATE *p_buf; + if (NULL == (p_buf = osi_malloc(sizeof(tBTC_MEDIA_SINK_CFG_UPDATE)))) { + APPL_TRACE_ERROR("btc reset decoder No Buffer "); + return; + } + + memcpy(p_buf->codec_info, p_av, AVDT_CODEC_SIZE); + btc_a2dp_sink_ctrl(BTC_MEDIA_AUDIO_SINK_CFG_UPDATE, p_buf); +} + +static void btc_a2dp_sink_data_ready(UNUSED_ATTR void *context) +{ + tBT_SBC_HDR *p_msg; + int nb_of_msgs_to_process = 0; + + if (fixed_queue_is_empty(a2dp_sink_local_param.btc_aa_snk_cb.RxSbcQ)) { + APPL_TRACE_DEBUG(" QUE EMPTY "); + } else { + if (a2dp_sink_local_param.btc_aa_snk_cb.rx_flush == TRUE) { + btc_a2dp_sink_flush_q(a2dp_sink_local_param.btc_aa_snk_cb.RxSbcQ); + return; + } + nb_of_msgs_to_process = fixed_queue_length(a2dp_sink_local_param.btc_aa_snk_cb.RxSbcQ); + APPL_TRACE_DEBUG("nb:%d", nb_of_msgs_to_process); + while (nb_of_msgs_to_process > 0) { + if (btc_a2dp_sink_state != BTC_A2DP_SINK_STATE_ON){ + return; + } + p_msg = (tBT_SBC_HDR *)fixed_queue_dequeue(a2dp_sink_local_param.btc_aa_snk_cb.RxSbcQ, 0); + if ( p_msg == NULL ) { + APPL_TRACE_ERROR("Insufficient data in que "); + break; + } + btc_a2dp_sink_handle_inc_media(p_msg); + osi_free(p_msg); + nb_of_msgs_to_process--; + } + APPL_TRACE_DEBUG(" Process Frames - "); + + if (!fixed_queue_is_empty(a2dp_sink_local_param.btc_aa_snk_cb.RxSbcQ)) { + osi_thread_post_event(a2dp_sink_local_param.btc_aa_snk_cb.data_ready_event, OSI_THREAD_MAX_TIMEOUT); + } + } +} + +/******************************************************************************* + ** + ** Function btc_a2dp_sink_handle_decoder_reset + ** + ** Description + ** + ** Returns void + ** + *******************************************************************************/ +static void btc_a2dp_sink_handle_decoder_reset(tBTC_MEDIA_SINK_CFG_UPDATE *p_msg) +{ + tBTC_MEDIA_SINK_CFG_UPDATE *p_buf = p_msg; + tA2D_STATUS a2d_status; + tA2D_SBC_CIE sbc_cie; + OI_STATUS status; + UINT32 freq_multiple = 48 * 20; /* frequency multiple for 20ms of data , initialize with 48K*/ + UINT32 num_blocks = 16; + UINT32 num_subbands = 8; + + APPL_TRACE_EVENT("%s p_codec_info[%x:%x:%x:%x:%x:%x]\n", __FUNCTION__, + p_buf->codec_info[1], p_buf->codec_info[2], p_buf->codec_info[3], + p_buf->codec_info[4], p_buf->codec_info[5], p_buf->codec_info[6]); + + a2d_status = A2D_ParsSbcInfo(&sbc_cie, p_buf->codec_info, FALSE); + if (a2d_status != A2D_SUCCESS) { + APPL_TRACE_ERROR("ERROR dump_codec_info A2D_ParsSbcInfo fail:%d\n", a2d_status); + return; + } + + a2dp_sink_local_param.btc_aa_snk_cb.sample_rate = btc_a2dp_sink_get_track_frequency(sbc_cie.samp_freq); + a2dp_sink_local_param.btc_aa_snk_cb.channel_count = btc_a2dp_sink_get_track_channel_count(sbc_cie.ch_mode); + + a2dp_sink_local_param.btc_aa_snk_cb.rx_flush = FALSE; + APPL_TRACE_EVENT("Reset to sink role"); + status = OI_CODEC_SBC_DecoderReset(&a2dp_sink_local_param.context, a2dp_sink_local_param.contextData, + sizeof(a2dp_sink_local_param.contextData), a2dp_sink_local_param.btc_aa_snk_cb.channel_count, + a2dp_sink_local_param.btc_aa_snk_cb.channel_count, FALSE, FALSE); + if (!OI_SUCCESS(status)) { + APPL_TRACE_ERROR("OI_CODEC_SBC_DecoderReset failed with error code %d\n", status); + } + + btc_a2dp_control_set_datachnl_stat(TRUE); + + switch (sbc_cie.samp_freq) { + case A2D_SBC_IE_SAMP_FREQ_16: + APPL_TRACE_DEBUG("\tsamp_freq:%d (16000)\n", sbc_cie.samp_freq); + freq_multiple = 16 * 20; + break; + case A2D_SBC_IE_SAMP_FREQ_32: + APPL_TRACE_DEBUG("\tsamp_freq:%d (32000)\n", sbc_cie.samp_freq); + freq_multiple = 32 * 20; + break; + case A2D_SBC_IE_SAMP_FREQ_44: + APPL_TRACE_DEBUG("\tsamp_freq:%d (44100)\n", sbc_cie.samp_freq); + freq_multiple = 441 * 2; + break; + case A2D_SBC_IE_SAMP_FREQ_48: + APPL_TRACE_DEBUG("\tsamp_freq:%d (48000)\n", sbc_cie.samp_freq); + freq_multiple = 48 * 20; + break; + default: + APPL_TRACE_DEBUG(" Unknown Frequency "); + break; + } + + switch (sbc_cie.ch_mode) { + case A2D_SBC_IE_CH_MD_MONO: + APPL_TRACE_DEBUG("\tch_mode:%d (Mono)\n", sbc_cie.ch_mode); + break; + case A2D_SBC_IE_CH_MD_DUAL: + APPL_TRACE_DEBUG("\tch_mode:%d (DUAL)\n", sbc_cie.ch_mode); + break; + case A2D_SBC_IE_CH_MD_STEREO: + APPL_TRACE_DEBUG("\tch_mode:%d (STEREO)\n", sbc_cie.ch_mode); + break; + case A2D_SBC_IE_CH_MD_JOINT: + APPL_TRACE_DEBUG("\tch_mode:%d (JOINT)\n", sbc_cie.ch_mode); + break; + default: + APPL_TRACE_DEBUG(" Unknown Mode "); + break; + } + + switch (sbc_cie.block_len) { + case A2D_SBC_IE_BLOCKS_4: + APPL_TRACE_DEBUG("\tblock_len:%d (4)\n", sbc_cie.block_len); + num_blocks = 4; + break; + case A2D_SBC_IE_BLOCKS_8: + APPL_TRACE_DEBUG("\tblock_len:%d (8)\n", sbc_cie.block_len); + num_blocks = 8; + break; + case A2D_SBC_IE_BLOCKS_12: + APPL_TRACE_DEBUG("\tblock_len:%d (12)\n", sbc_cie.block_len); + num_blocks = 12; + break; + case A2D_SBC_IE_BLOCKS_16: + APPL_TRACE_DEBUG("\tblock_len:%d (16)\n", sbc_cie.block_len); + num_blocks = 16; + break; + default: + APPL_TRACE_DEBUG(" Unknown BlockLen "); + break; + } + + switch (sbc_cie.num_subbands) { + case A2D_SBC_IE_SUBBAND_4: + APPL_TRACE_DEBUG("\tnum_subbands:%d (4)\n", sbc_cie.num_subbands); + num_subbands = 4; + break; + case A2D_SBC_IE_SUBBAND_8: + APPL_TRACE_DEBUG("\tnum_subbands:%d (8)\n", sbc_cie.num_subbands); + num_subbands = 8; + break; + default: + APPL_TRACE_DEBUG(" Unknown SubBands "); + break; + } + + switch (sbc_cie.alloc_mthd) { + case A2D_SBC_IE_ALLOC_MD_S: + APPL_TRACE_DEBUG("\talloc_mthd:%d (SNR)\n", sbc_cie.alloc_mthd); + break; + case A2D_SBC_IE_ALLOC_MD_L: + APPL_TRACE_DEBUG("\talloc_mthd:%d (Loudness)\n", sbc_cie.alloc_mthd); + break; + default: + APPL_TRACE_DEBUG(" Unknown Allocation Method"); + break; + } + + APPL_TRACE_EVENT("\tBit pool Min:%d Max:%d\n", sbc_cie.min_bitpool, sbc_cie.max_bitpool); + + int frames_to_process = ((freq_multiple) / (num_blocks * num_subbands)) + 1; + APPL_TRACE_EVENT(" Frames to be processed in 20 ms %d\n", frames_to_process); + UNUSED(frames_to_process); +} + +/******************************************************************************* + ** + ** Function btc_a2dp_sink_handle_inc_media + ** + ** Description + ** + ** Returns void + ** + *******************************************************************************/ +static void btc_a2dp_sink_handle_inc_media(tBT_SBC_HDR *p_msg) +{ + UINT8 *sbc_start_frame = ((UINT8 *)(p_msg + 1) + p_msg->offset + 1); + int count; + UINT32 pcmBytes, availPcmBytes; + OI_INT16 *pcmDataPointer = a2dp_sink_local_param.pcmData; /*Will be overwritten on next packet receipt*/ + OI_STATUS status; + int num_sbc_frames = p_msg->num_frames_to_be_processed; + UINT32 sbc_frame_len = p_msg->len - 1; + availPcmBytes = sizeof(a2dp_sink_local_param.pcmData); + + /* XXX: Check if the below check is correct, we are checking for peer to be sink when we are sink */ + if (btc_av_get_peer_sep() == AVDT_TSEP_SNK || (a2dp_sink_local_param.btc_aa_snk_cb.rx_flush)) { + APPL_TRACE_DEBUG(" State Changed happened in this tick "); + return; + } + + // ignore data if no one is listening + if (!btc_a2dp_control_get_datachnl_stat()) { + return; + } + + if (p_msg->layer_specific != a2dp_sink_local_param.media_pkt_seq_num.expected_seq_num) { + /* Because the sequence number of some devices is not recounted */ + if (!a2dp_sink_local_param.media_pkt_seq_num.seq_num_recount || + a2dp_sink_local_param.media_pkt_seq_num.expected_seq_num != 0x1) { + APPL_TRACE_WARNING("Sequence numbers error, recv:0x%x, expect:0x%x, recount:0x%x", + p_msg->layer_specific, a2dp_sink_local_param.media_pkt_seq_num.expected_seq_num, + a2dp_sink_local_param.media_pkt_seq_num.seq_num_recount); + } + } + a2dp_sink_local_param.media_pkt_seq_num.expected_seq_num = p_msg->layer_specific + 1; + a2dp_sink_local_param.media_pkt_seq_num.seq_num_recount = false; + + APPL_TRACE_DEBUG("Number of sbc frames %d, frame_len %d\n", num_sbc_frames, sbc_frame_len); + + for (count = 0; count < num_sbc_frames && sbc_frame_len != 0; count ++) { + pcmBytes = availPcmBytes; + status = OI_CODEC_SBC_DecodeFrame(&a2dp_sink_local_param.context, (const OI_BYTE **)&sbc_start_frame, + (OI_UINT32 *)&sbc_frame_len, + (OI_INT16 *)pcmDataPointer, + (OI_UINT32 *)&pcmBytes); + if (!OI_SUCCESS(status)) { + APPL_TRACE_ERROR("Decoding failure: %d\n", status); + break; + } + availPcmBytes -= pcmBytes; + pcmDataPointer += pcmBytes / 2; + p_msg->offset += (p_msg->len - 1) - sbc_frame_len; + p_msg->len = sbc_frame_len + 1; + } + + btc_a2d_data_cb_to_app((uint8_t *)a2dp_sink_local_param.pcmData, (sizeof(a2dp_sink_local_param.pcmData) - availPcmBytes)); +} + +/******************************************************************************* + ** + ** Function btc_a2dp_sink_rx_flush_req + ** + ** Description + ** + ** Returns TRUE is success + ** + *******************************************************************************/ +BOOLEAN btc_a2dp_sink_rx_flush_req(void) +{ + if (fixed_queue_is_empty(a2dp_sink_local_param.btc_aa_snk_cb.RxSbcQ) == TRUE) { /* Que is already empty */ + return TRUE; + } + + return btc_a2dp_sink_ctrl(BTC_MEDIA_FLUSH_AA_RX, NULL); +} + +/******************************************************************************* + ** + ** Function btc_a2dp_sink_rx_flush + ** + ** Description + ** + ** Returns void + ** + *******************************************************************************/ +static void btc_a2dp_sink_rx_flush(void) +{ + /* Flush all enqueued SBC buffers (encoded) */ + APPL_TRACE_DEBUG("btc_a2dp_sink_rx_flush"); + + btc_a2dp_sink_flush_q(a2dp_sink_local_param.btc_aa_snk_cb.RxSbcQ); +} + +static int btc_a2dp_sink_get_track_frequency(UINT8 frequency) +{ + int freq = 48000; + switch (frequency) { + case A2D_SBC_IE_SAMP_FREQ_16: + freq = 16000; + break; + case A2D_SBC_IE_SAMP_FREQ_32: + freq = 32000; + break; + case A2D_SBC_IE_SAMP_FREQ_44: + freq = 44100; + break; + case A2D_SBC_IE_SAMP_FREQ_48: + freq = 48000; + break; + } + return freq; +} + +static int btc_a2dp_sink_get_track_channel_count(UINT8 channeltype) +{ + int count = 1; + switch (channeltype) { + case A2D_SBC_IE_CH_MD_MONO: + count = 1; + break; + case A2D_SBC_IE_CH_MD_DUAL: + case A2D_SBC_IE_CH_MD_STEREO: + case A2D_SBC_IE_CH_MD_JOINT: + count = 2; + break; + } + return count; +} + +/******************************************************************************* + ** + ** Function btc_a2dp_sink_enque_buf + ** + ** Description This function is called by the av_co to fill A2DP Sink Queue + ** + ** + ** Returns size of the queue + *******************************************************************************/ +UINT8 btc_a2dp_sink_enque_buf(BT_HDR *p_pkt) +{ + tBT_SBC_HDR *p_msg; + + if (btc_a2dp_sink_state != BTC_A2DP_SINK_STATE_ON){ + return 0; + } + + if (a2dp_sink_local_param.btc_aa_snk_cb.rx_flush == TRUE) { /* Flush enabled, do not enque*/ + return fixed_queue_length(a2dp_sink_local_param.btc_aa_snk_cb.RxSbcQ); + } + + if (fixed_queue_length(a2dp_sink_local_param.btc_aa_snk_cb.RxSbcQ) >= MAX_OUTPUT_A2DP_SNK_FRAME_QUEUE_SZ) { + APPL_TRACE_WARNING("Pkt dropped\n"); + return fixed_queue_length(a2dp_sink_local_param.btc_aa_snk_cb.RxSbcQ); + } + + APPL_TRACE_DEBUG("btc_a2dp_sink_enque_buf + "); + + /* allocate and Queue this buffer */ + if ((p_msg = (tBT_SBC_HDR *) osi_malloc(sizeof(tBT_SBC_HDR) + + p_pkt->offset + p_pkt->len)) != NULL) { + memcpy(p_msg, p_pkt, (sizeof(BT_HDR) + p_pkt->offset + p_pkt->len)); + p_msg->num_frames_to_be_processed = (*((UINT8 *)(p_msg + 1) + p_msg->offset)) & 0x0f; + APPL_TRACE_VERBOSE("btc_a2dp_sink_enque_buf %d + \n", p_msg->num_frames_to_be_processed); + fixed_queue_enqueue(a2dp_sink_local_param.btc_aa_snk_cb.RxSbcQ, p_msg, FIXED_QUEUE_MAX_TIMEOUT); + osi_thread_post_event(a2dp_sink_local_param.btc_aa_snk_cb.data_ready_event, OSI_THREAD_MAX_TIMEOUT); + } else { + /* let caller deal with a failed allocation */ + APPL_TRACE_WARNING("btc_a2dp_sink_enque_buf No Buffer left - "); + } + return fixed_queue_length(a2dp_sink_local_param.btc_aa_snk_cb.RxSbcQ); +} + +static void btc_a2dp_sink_handle_clear_track (void) +{ + APPL_TRACE_DEBUG("%s", __FUNCTION__); +} + +/******************************************************************************* + ** + ** Function btc_a2dp_sink_flush_q + ** + ** Description + ** + ** Returns void + ** + *******************************************************************************/ +static void btc_a2dp_sink_flush_q(fixed_queue_t *p_q) +{ + while (! fixed_queue_is_empty(p_q)) { + osi_free(fixed_queue_dequeue(p_q, 0)); + } +} + +static void btc_a2dp_sink_thread_init(UNUSED_ATTR void *context) +{ + APPL_TRACE_EVENT("%s\n", __func__); + memset(&a2dp_sink_local_param.btc_aa_snk_cb, 0, sizeof(a2dp_sink_local_param.btc_aa_snk_cb)); + + btc_a2dp_sink_state = BTC_A2DP_SINK_STATE_ON; + + struct osi_event *data_event = osi_event_create(btc_a2dp_sink_data_ready, NULL); + assert (data_event != NULL); + osi_event_bind(data_event, a2dp_sink_local_param.btc_aa_snk_task_hdl, BTC_A2DP_SNK_DATA_QUEUE_IDX); + a2dp_sink_local_param.btc_aa_snk_cb.data_ready_event = data_event; + + a2dp_sink_local_param.btc_aa_snk_cb.RxSbcQ = fixed_queue_new(QUEUE_SIZE_MAX); + + btc_a2dp_control_init(); +} + +static void btc_a2dp_sink_thread_cleanup(UNUSED_ATTR void *context) +{ + btc_a2dp_control_set_datachnl_stat(FALSE); + /* Clear task flag */ + btc_a2dp_sink_state = BTC_A2DP_SINK_STATE_OFF; + + btc_a2dp_control_cleanup(); + + fixed_queue_free(a2dp_sink_local_param.btc_aa_snk_cb.RxSbcQ, osi_free_func); + + a2dp_sink_local_param.btc_aa_snk_cb.RxSbcQ = NULL; + + osi_event_delete(a2dp_sink_local_param.btc_aa_snk_cb.data_ready_event); + a2dp_sink_local_param.btc_aa_snk_cb.data_ready_event = NULL; +} + +#endif /* BTC_AV_SINK_INCLUDED */ diff --git a/lib/bt/host/bluedroid/btc/profile/std/a2dp/btc_a2dp_source.c b/lib/bt/host/bluedroid/btc/profile/std/a2dp/btc_a2dp_source.c new file mode 100644 index 00000000..d388e94a --- /dev/null +++ b/lib/bt/host/bluedroid/btc/profile/std/a2dp/btc_a2dp_source.c @@ -0,0 +1,1650 @@ +/* + * SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/****************************************************************************** + ** + ** Name: btc_a2dp_source.c + ** + ******************************************************************************/ +#include "common/bt_target.h" +#include "common/bt_trace.h" +#include +#include +#include +#include +#include +#include +#include "osi/allocator.h" +#include "osi/alarm.h" +#include "osi/thread.h" +#include "osi/mutex.h" +#include "osi/fixed_queue.h" +#include "stack/a2d_api.h" +#include "stack/a2d_sbc.h" +#include "bta/bta_av_api.h" +#include "bta/bta_av_sbc.h" +#include "bta/bta_av_ci.h" +#include "btc/btc_manage.h" +#include "btc/btc_common.h" +#include "btc_av_co.h" +#include "btc_a2dp.h" +#include "btc_a2dp_control.h" +#include "btc_a2dp_source.h" +#include "btc_av.h" +#include "btc/btc_util.h" +#include "esp_a2dp_api.h" +#include "sbc_encoder.h" +#include "osi/future.h" +#include + +#if BTC_AV_SRC_INCLUDED + +/***************************************************************************** + ** BQB global variables + *****************************************************************************/ +#if A2D_SRC_BQB_INCLUDED +bool a2dp_src_bqb_set_sbc_encoder_flag = FALSE; +#endif /* A2D_SRC_BQB_INCLUDED */ + +/***************************************************************************** + ** Constants + *****************************************************************************/ + +/* BTC source command event definition */ +enum { + BTC_MEDIA_TASK_INIT, + BTC_MEDIA_TASK_CLEAN_UP, + BTC_MEDIA_START_AA_TX, + BTC_MEDIA_STOP_AA_TX, + BTC_MEDIA_SBC_ENC_INIT, + BTC_MEDIA_SBC_ENC_UPDATE, + BTC_MEDIA_FLUSH_AA_TX, + BTC_MEDIA_AUDIO_FEEDING_INIT, +}; + +enum { + BTC_A2DP_SOURCE_STATE_OFF = 0, + BTC_A2DP_SOURCE_STATE_ON = 1, + BTC_A2DP_SOURCE_STATE_SHUTTING_DOWN = 2 +}; + + +/* Media task tick in milliseconds, must be set to multiple of + (1000/TICKS_PER_SEC) */ +#define BTC_MEDIA_TIME_TICK_MS (30) +#define A2DP_DATA_READ_POLL_MS (BTC_MEDIA_TIME_TICK_MS / 2) + +#ifndef MAX_PCM_FRAME_NUM_PER_TICK +#define MAX_PCM_FRAME_NUM_PER_TICK 21 // 14 for 20ms +#endif + +#define BTC_MEDIA_AA_BUF_SIZE (4096+16) + +#if (BTA_AV_CO_CP_SCMS_T == TRUE) +#define BTC_MEDIA_AA_SBC_OFFSET (AVDT_MEDIA_OFFSET + BTA_AV_SBC_HDR_SIZE + 1) +#else +#define BTC_MEDIA_AA_SBC_OFFSET (AVDT_MEDIA_OFFSET + BTA_AV_SBC_HDR_SIZE) +#endif + +#ifndef BTC_MEDIA_BITRATE_STEP +#define BTC_MEDIA_BITRATE_STEP 5 +#endif + +#ifndef BTC_A2DP_NON_EDR_MAX_RATE +#define BTC_A2DP_NON_EDR_MAX_RATE 229 +#endif + +/* Middle quality quality setting @ 44.1 khz */ +#define DEFAULT_SBC_BITRATE 328 + +/* + * CONGESTION COMPENSATION CTRL :: + * + * Thus setting controls how many buffers we will hold in media task + * during temp link congestion. Together with the stack buffer queues + * it controls much temporary a2dp link congestion we can + * compensate for. It however also depends on the default run level of sinks + * jitterbuffers. Depending on type of sink this would vary. + * Ideally the (SRC) max tx buffer capacity should equal the sinks + * jitterbuffer runlevel including any intermediate buffers on the way + * towards the sinks codec. + */ + +/* fixme -- define this in pcm time instead of buffer count */ + +/* The typical runlevel of the tx queue size is ~1 buffer + but due to link flow control or thread preemption in lower + layers we might need to temporarily buffer up data */ + +/* 5 frames is equivalent to 6.89*5*2.9 ~= 100 ms @ 44.1 khz, 20 ms mediatick */ +#define MAX_OUTPUT_A2DP_FRAME_QUEUE_SZ (5) +#define MAX_OUTPUT_A2DP_SRC_FRAME_QUEUE_SZ (27) // 18 for 20ms tick + +#define BTC_A2DP_SRC_DATA_QUEUE_IDX (1) + +typedef struct { + uint32_t sig; + void *param; +} a2dp_src_task_evt_t; + +typedef struct { + UINT16 num_frames_to_be_processed; + UINT16 len; + UINT16 offset; + UINT16 layer_specific; +} tBT_SBC_HDR; + +typedef struct { + UINT32 aa_frame_counter; + INT32 aa_feed_counter; + INT32 aa_feed_residue; + UINT32 counter; + UINT32 bytes_per_tick; /* pcm bytes read each media task tick */ +} tBTC_AV_MEDIA_FEEDINGS_PCM_STATE; + +typedef union { + tBTC_AV_MEDIA_FEEDINGS_PCM_STATE pcm; +} tBTC_AV_MEDIA_FEEDINGS_STATE; + +typedef struct { + UINT8 TxTranscoding; + BOOLEAN tx_flush; /* discards any outgoing data when true */ + BOOLEAN is_tx_timer; + UINT16 TxAaMtuSize; + UINT32 timestamp; + fixed_queue_t *TxAaQ; + tBTC_AV_FEEDING_MODE feeding_mode; + tBTC_AV_MEDIA_FEEDINGS_STATE media_feeding_state; + tBTC_AV_MEDIA_FEEDINGS media_feeding; + SBC_ENC_PARAMS encoder; + osi_alarm_t *media_alarm; + struct osi_event *poll_data; +} tBTC_A2DP_SOURCE_CB; + +typedef struct { + tBTC_A2DP_SOURCE_CB btc_aa_src_cb; + osi_thread_t *btc_aa_src_task_hdl; + UINT64 last_frame_us; +} a2dp_source_local_param_t; + +static void btc_a2dp_source_thread_init(UNUSED_ATTR void *context); +static void btc_a2dp_source_thread_cleanup(UNUSED_ATTR void *context); +static void btc_a2dp_source_flush_q(fixed_queue_t *p_q); + +static void btc_a2dp_source_feeding_state_reset(void); +static void btc_a2dp_source_send_aa_frame(void); +static void btc_a2dp_source_aa_start_tx(void); +static void btc_a2dp_source_aa_stop_tx(void); +static void btc_a2dp_source_enc_init(BT_HDR *p_msg); +static void btc_a2dp_source_enc_update(BT_HDR *p_msg); +static void btc_a2dp_source_audio_feeding_init(BT_HDR *p_msg); +static void btc_a2dp_source_aa_tx_flush(void); +static void btc_a2dp_source_prep_2_send(UINT8 nb_frame); +static void btc_a2dp_source_handle_timer(UNUSED_ATTR void *context); +static void btc_a2dp_source_encoder_init(void); + +static int btc_a2dp_source_state = BTC_A2DP_SOURCE_STATE_OFF; +static esp_a2d_source_data_cb_t btc_aa_src_data_cb = NULL; +#if A2D_DYNAMIC_MEMORY == FALSE +static a2dp_source_local_param_t a2dp_source_local_param; +#else +static a2dp_source_local_param_t *a2dp_source_local_param_ptr; +#define a2dp_source_local_param (*a2dp_source_local_param_ptr) +#endif ///A2D_DYNAMIC_MEMORY == FALSE + +void btc_a2dp_src_reg_data_cb(esp_a2d_source_data_cb_t callback) +{ + // todo: critical section protection + btc_aa_src_data_cb = callback; +} + +static inline uint32_t btc_aa_src_data_read(uint8_t *data, int32_t len) +{ + // todo: critical section protection + if (btc_aa_src_data_cb) { + return btc_aa_src_data_cb(data, len); + } else { + return 0; + } +} + +/***************************************************************************** + ** Misc helper functions + *****************************************************************************/ +static inline void btc_aa_cb_to_app(esp_a2d_cb_event_t event, esp_a2d_cb_param_t *param) +{ + esp_a2d_cb_t btc_aa_cb = (esp_a2d_cb_t)btc_profile_cb_get(BTC_PID_A2DP); + if (btc_aa_cb) { + btc_aa_cb(event, param); + } +} + +/***************************************************************************** + ** BTC ADAPTATION + *****************************************************************************/ + +bool btc_a2dp_source_is_streaming(void) +{ + return a2dp_source_local_param.btc_aa_src_cb.is_tx_timer == TRUE; +} + +bool btc_a2dp_source_is_task_shutting_down(void) +{ + return btc_a2dp_source_state == BTC_A2DP_SOURCE_STATE_SHUTTING_DOWN; +} + +static bool btc_a2dp_source_ctrl(uint32_t sig, void *param) +{ + switch (sig) { + case BTC_MEDIA_TASK_INIT: + btc_a2dp_source_thread_init(NULL); + break; + case BTC_MEDIA_TASK_CLEAN_UP: + btc_a2dp_source_thread_cleanup(NULL); + break; + case BTC_MEDIA_START_AA_TX: + btc_a2dp_source_aa_start_tx(); + break; + case BTC_MEDIA_STOP_AA_TX: + btc_a2dp_source_aa_stop_tx(); + break; + case BTC_MEDIA_SBC_ENC_INIT: + btc_a2dp_source_enc_init(param); + break; + case BTC_MEDIA_SBC_ENC_UPDATE: + btc_a2dp_source_enc_update(param); + break; + case BTC_MEDIA_AUDIO_FEEDING_INIT: + btc_a2dp_source_audio_feeding_init(param); + break; + case BTC_MEDIA_FLUSH_AA_TX: + btc_a2dp_source_aa_tx_flush(); + break; + default: + APPL_TRACE_WARNING("media task unhandled evt: 0x%x\n", sig); + } + + if (param != NULL) { + osi_free(param); + } + + return true; +} + +bool btc_a2dp_source_startup(void) +{ + if (btc_a2dp_source_state != BTC_A2DP_SOURCE_STATE_OFF) { + APPL_TRACE_ERROR("warning : media task already running"); + return false; + } + +#if A2D_DYNAMIC_MEMORY == TRUE + if ((a2dp_source_local_param_ptr = (a2dp_source_local_param_t *)osi_malloc(sizeof(a2dp_source_local_param_t))) == NULL) { + APPL_TRACE_ERROR("%s malloc failed!", __func__); + return false; + } + memset((void *)a2dp_source_local_param_ptr, 0, sizeof(a2dp_source_local_param_t)); +#endif + + APPL_TRACE_EVENT("## A2DP SOURCE START MEDIA THREAD ##"); + + a2dp_source_local_param.btc_aa_src_task_hdl = btc_get_current_thread(); + + if (btc_a2dp_source_ctrl(BTC_MEDIA_TASK_INIT, NULL) == false) { + goto error_exit; + } + + APPL_TRACE_EVENT("## A2DP SOURCE MEDIA THREAD STARTED ##\n"); + + return true; + +error_exit:; + APPL_TRACE_ERROR("%s unable to start up media thread\n", __func__); + a2dp_source_local_param.btc_aa_src_task_hdl = NULL; + +#if A2D_DYNAMIC_MEMORY == TRUE + osi_free(a2dp_source_local_param_ptr); + a2dp_source_local_param_ptr = NULL; +#endif + + return false; +} + +void btc_a2dp_source_shutdown(void) +{ + APPL_TRACE_EVENT("## A2DP SOURCE STOP MEDIA THREAD ##\n"); + + // Exit thread + btc_a2dp_source_state = BTC_A2DP_SOURCE_STATE_SHUTTING_DOWN; + btc_a2dp_source_ctrl(BTC_MEDIA_TASK_CLEAN_UP, NULL); + + a2dp_source_local_param.btc_aa_src_task_hdl = NULL; + +#if A2D_DYNAMIC_MEMORY == TRUE + osi_free(a2dp_source_local_param_ptr); + a2dp_source_local_param_ptr = NULL; +#endif +} + +/***************************************************************************** +** +** Function btc_a2dp_source_on_idle +** +*******************************************************************************/ +void btc_a2dp_source_on_idle(void) +{ + /* Make sure media task is stopped */ + btc_a2dp_source_stop_audio_req(); +} + +/***************************************************************************** +** +** Function btc_a2dp_source_on_stopped +** +*******************************************************************************/ +void btc_a2dp_source_on_stopped(tBTA_AV_SUSPEND *p_av) +{ + /* allow using this api for other than suspend */ + if (p_av != NULL) { + if (p_av->status != BTA_AV_SUCCESS) { + APPL_TRACE_EVENT("AV STOP FAILED (%d)", p_av->status); + if (p_av->initiator) { + btc_a2dp_control_command_ack(ESP_A2D_MEDIA_CTRL_ACK_FAILURE); + } + return; + } + } + + /* ensure tx frames are immediately suspended */ + a2dp_source_local_param.btc_aa_src_cb.tx_flush = 1; + + /* request to stop media task */ + btc_a2dp_source_tx_flush_req(); + btc_a2dp_source_stop_audio_req(); + + /* once stream is fully stopped we will ack back */ +} + +/***************************************************************************** +** +** Function btc_a2dp_source_on_suspended +** +** +*******************************************************************************/ + +void btc_a2dp_source_on_suspended(tBTA_AV_SUSPEND *p_av) +{ + /* check for status failures */ + if (p_av->status != BTA_AV_SUCCESS) { + if (p_av->initiator == TRUE) { + btc_a2dp_control_command_ack(ESP_A2D_MEDIA_CTRL_ACK_FAILURE); + } + } + + /* once stream is fully stopped we will ack back */ + + /* ensure tx frames are immediately flushed */ + a2dp_source_local_param.btc_aa_src_cb.tx_flush = 1; + + /* stop timer tick */ + btc_a2dp_source_stop_audio_req(); +} + +static UINT64 time_now_us(void) +{ +#if _POSIX_TIMERS + struct timespec ts_now; + clock_gettime(CLOCK_MONOTONIC, &ts_now); + return ((UINT64)ts_now.tv_sec * 1000000L) + ((UINT64)ts_now.tv_nsec / 1000); +#else + struct timeval ts_now; + gettimeofday(&ts_now, NULL); + return ((UINT64)ts_now.tv_sec * 1000000L) + ((UINT64)ts_now.tv_usec); +#endif +} + +static void log_tstamps_us(char *comment) +{ + static UINT64 prev_us = 0; + UINT64 now_us = time_now_us(); + APPL_TRACE_DEBUG("[%s] ts %08llu, diff : %08llu, queue sz %d", comment, now_us, now_us - prev_us, + fixed_queue_length(a2dp_source_local_param.btc_aa_src_cb.TxAaQ)); + prev_us = now_us; + UNUSED(prev_us); +} + +/* when true media task discards any tx frames */ +void btc_a2dp_source_set_tx_flush(BOOLEAN enable) +{ + APPL_TRACE_EVENT("## DROP TX %d ##", enable); + a2dp_source_local_param.btc_aa_src_cb.tx_flush = enable; +} + +/***************************************************************************** +** +** Function btc_a2dp_source_setup_codec +** +** Description +** +** Returns +** +*******************************************************************************/ +void btc_a2dp_source_setup_codec(void) +{ + tBTC_AV_MEDIA_FEEDINGS media_feeding; + tBTC_AV_STATUS status; + + APPL_TRACE_EVENT("## A2DP SETUP CODEC ##\n"); + + osi_mutex_global_lock(); + + /* for now hardcode 44.1 khz 16 bit stereo PCM format */ + media_feeding.cfg.pcm.sampling_freq = 44100; + media_feeding.cfg.pcm.bit_per_sample = 16; + media_feeding.cfg.pcm.num_channel = 2; + media_feeding.format = BTC_AV_CODEC_PCM; + + if (bta_av_co_audio_set_codec(&media_feeding, &status)) { + tBTC_MEDIA_INIT_AUDIO_FEEDING mfeed; + + /* Init the encoding task */ + btc_a2dp_source_encoder_init(); + + /* Build the media task configuration */ + mfeed.feeding = media_feeding; + mfeed.feeding_mode = BTC_AV_FEEDING_ASYNCHRONOUS; + /* Send message to Media task to configure transcoding */ + btc_a2dp_source_audio_feeding_init_req(&mfeed); + } + + osi_mutex_global_unlock(); +} + + +/******************************************************************************* + ** + ** Function btc_a2dp_source_audio_readbuf + ** + ** Description This function is called by the av_co to get the next buffer to send + ** + ** + ** Returns void + *******************************************************************************/ +BT_HDR *btc_a2dp_source_audio_readbuf(void) +{ + if (btc_a2dp_source_state != BTC_A2DP_SOURCE_STATE_ON){ + return NULL; + } + return fixed_queue_dequeue(a2dp_source_local_param.btc_aa_src_cb.TxAaQ, 0); +} + +/******************************************************************************* + ** + ** Function btc_a2dp_source_start_audio_req + ** + ** Description + ** + ** Returns TRUE is success + ** + *******************************************************************************/ +BOOLEAN btc_a2dp_source_start_audio_req(void) +{ + btc_a2dp_source_ctrl(BTC_MEDIA_START_AA_TX, NULL); + return TRUE; +} + +/******************************************************************************* + ** + ** Function btc_a2dp_source_stop_audio_req + ** + ** Description + ** + ** Returns TRUE is success + ** + *******************************************************************************/ +BOOLEAN btc_a2dp_source_stop_audio_req(void) +{ + /* + * Explicitly check whether the btc_aa_src_ctrl_queue is not NULL to + * avoid a race condition during shutdown of the Bluetooth stack. + * This race condition is triggered when A2DP audio is streaming on + * shutdown: + * "btc_a2dp_on_stopped() -> btc_a2dp_source_stop_audio_req()" is called + * to stop the particular audio stream, and this happens right after + * the "cleanup() -> btc_a2dp_stop_media_task()" processing during + * the shutdown of the Bluetooth stack. + */ +#if 0 + if (btc_aa_src_ctrl_queue != NULL) { +#endif + btc_a2dp_source_ctrl(BTC_MEDIA_STOP_AA_TX, NULL); +#if 0 + } +#endif + return TRUE; +} + +/******************************************************************************* + ** + ** Function btc_a2dp_source_enc_init_req + ** + ** Description + ** + ** Returns TRUE is success + ** + *******************************************************************************/ +BOOLEAN btc_a2dp_source_enc_init_req(tBTC_MEDIA_INIT_AUDIO *p_msg) +{ + tBTC_MEDIA_INIT_AUDIO *p_buf; + if (NULL == (p_buf = osi_malloc(sizeof(tBTC_MEDIA_INIT_AUDIO)))) { + return FALSE; + } + + memcpy(p_buf, p_msg, sizeof(tBTC_MEDIA_INIT_AUDIO)); + + btc_a2dp_source_ctrl(BTC_MEDIA_SBC_ENC_INIT, p_buf); + + return TRUE; +} + +/******************************************************************************* + ** + ** Function btc_a2dp_source_enc_update_req + ** + ** Description + ** + ** Returns TRUE is success + ** + *******************************************************************************/ +BOOLEAN btc_a2dp_source_enc_update_req(tBTC_MEDIA_UPDATE_AUDIO *p_msg) +{ + tBTC_MEDIA_UPDATE_AUDIO *p_buf; + if (NULL == (p_buf = osi_malloc(sizeof(tBTC_MEDIA_UPDATE_AUDIO)))) { + return FALSE; + } + + memcpy(p_buf, p_msg, sizeof(tBTC_MEDIA_UPDATE_AUDIO)); + btc_a2dp_source_ctrl(BTC_MEDIA_SBC_ENC_UPDATE, p_buf); + return TRUE; +} + +/******************************************************************************* + ** + ** Function btc_a2dp_source_audio_feeding_init_req + ** + ** Description + ** + ** Returns TRUE is success + ** + *******************************************************************************/ +BOOLEAN btc_a2dp_source_audio_feeding_init_req(tBTC_MEDIA_INIT_AUDIO_FEEDING *p_msg) +{ + tBTC_MEDIA_INIT_AUDIO_FEEDING *p_buf; + if (NULL == (p_buf = osi_malloc(sizeof(tBTC_MEDIA_INIT_AUDIO_FEEDING)))) { + return FALSE; + } + + memcpy(p_buf, p_msg, sizeof(tBTC_MEDIA_INIT_AUDIO_FEEDING)); + btc_a2dp_source_ctrl(BTC_MEDIA_AUDIO_FEEDING_INIT, p_buf); + return TRUE; +} + +/******************************************************************************* + ** + ** Function btc_a2dp_source_tx_flush_req + ** + ** Description + ** + ** Returns TRUE is success + ** + *******************************************************************************/ +BOOLEAN btc_a2dp_source_tx_flush_req(void) +{ + /* + * Explicitly check whether the btc_aa_src_ctrl_queue is not NULL to + * avoid a race condition during shutdown of the Bluetooth stack. + * This race condition is triggered when A2DP audio is streaming on + * shutdown: + * "btc_a2dp_on_stopped() -> btc_a2dp_source_tx_flush_req()" is called + * to stop the particular audio stream, and this happens right after + * the "cleanup() -> btc_a2dp_stop_media_task()" processing during + * the shutdown of the Bluetooth stack. + */ +#if 0 + if (btc_aa_src_ctrl_queue != NULL) { +#endif + btc_a2dp_source_ctrl(BTC_MEDIA_FLUSH_AA_TX, NULL); +#if 0 + } +#endif + + return TRUE; +} + +/***************************************************************************** +** +** Function btc_source_report_delay_value +** +** Description +** +** Returns +** +*******************************************************************************/ +void btc_source_report_delay_value(UINT16 delay_value) +{ + esp_a2d_cb_param_t param; + +#if A2D_DYNAMIC_MEMORY == TRUE + if (a2dp_source_local_param_ptr == NULL) { + return; + } +#endif + + param.a2d_report_delay_value_stat.delay_value = delay_value; + + btc_aa_cb_to_app(ESP_A2D_REPORT_SNK_DELAY_VALUE_EVT, ¶m); +} + +/***************************************************************************** + ** BTC ADAPTATION + *****************************************************************************/ +static UINT16 btc_a2dp_source_get_sbc_rate(void) +{ + UINT16 rate = DEFAULT_SBC_BITRATE; + + /* restrict bitrate if a2dp link is non-edr */ + if (!btc_av_is_peer_edr()) { + rate = BTC_A2DP_NON_EDR_MAX_RATE; + APPL_TRACE_DEBUG("non-edr a2dp sink detected, restrict rate to %d", rate); + } + return rate; +} + +static void btc_a2dp_source_encoder_init(void) +{ + UINT16 minmtu; + tBTC_MEDIA_INIT_AUDIO msg; + tA2D_SBC_CIE sbc_config; + + /* lookup table for converting channel mode */ + UINT16 codec_mode_tbl[5] = { SBC_JOINT_STEREO, SBC_STEREO, SBC_DUAL, 0, SBC_MONO }; + + /* lookup table for converting number of blocks */ + UINT16 codec_block_tbl[5] = { 16, 12, 8, 0, 4 }; + + /* lookup table to convert freq */ + UINT16 freq_block_tbl[5] = { SBC_sf48000, SBC_sf44100, SBC_sf32000, 0, SBC_sf16000 }; + + APPL_TRACE_DEBUG("%s", __FUNCTION__); + + /* Retrieve the current SBC configuration (default if currently not used) */ + bta_av_co_audio_get_sbc_config(&sbc_config, &minmtu); + msg.NumOfSubBands = (sbc_config.num_subbands == A2D_SBC_IE_SUBBAND_4) ? 4 : 8; + msg.NumOfBlocks = codec_block_tbl[sbc_config.block_len >> 5]; + msg.AllocationMethod = (sbc_config.alloc_mthd == A2D_SBC_IE_ALLOC_MD_L) ? SBC_LOUDNESS : SBC_SNR; + msg.ChannelMode = codec_mode_tbl[sbc_config.ch_mode >> 1]; + msg.SamplingFreq = freq_block_tbl[sbc_config.samp_freq >> 5]; + msg.MtuSize = minmtu; + + APPL_TRACE_EVENT("msg.ChannelMode %x", msg.ChannelMode); + + /* Init the media task to encode SBC properly */ + btc_a2dp_source_enc_init_req(&msg); +} + +void btc_a2dp_source_encoder_update(void) +{ + UINT16 minmtu; + tA2D_SBC_CIE sbc_config; + tBTC_MEDIA_UPDATE_AUDIO msg; + UINT8 pref_min; + UINT8 pref_max; + + APPL_TRACE_DEBUG("%s", __FUNCTION__); + + /* Retrieve the current SBC configuration (default if currently not used) */ + bta_av_co_audio_get_sbc_config(&sbc_config, &minmtu); + + APPL_TRACE_DEBUG("%s: Common min_bitpool:%d(0x%x) max_bitpool:%d(0x%x)", __FUNCTION__, + sbc_config.min_bitpool, sbc_config.min_bitpool, + sbc_config.max_bitpool, sbc_config.max_bitpool); + + if (sbc_config.min_bitpool > sbc_config.max_bitpool) { + APPL_TRACE_ERROR("%s: ERROR min_bitpool > max_bitpool", __FUNCTION__); + } + + /* check if remote sink has a preferred bitpool range */ + if (bta_av_co_get_remote_bitpool_pref(&pref_min, &pref_max) == TRUE) { + /* adjust our preferred bitpool with the remote preference if within + our capable range */ + + if (pref_min < sbc_config.min_bitpool) { + pref_min = sbc_config.min_bitpool; + } + + if (pref_max > sbc_config.max_bitpool) { + pref_max = sbc_config.max_bitpool; + } + + msg.MinBitPool = pref_min; + msg.MaxBitPool = pref_max; + + if ((pref_min != sbc_config.min_bitpool) || (pref_max != sbc_config.max_bitpool)) { + APPL_TRACE_EVENT("## adjusted our bitpool range to peer pref [%d:%d] ##", + pref_min, pref_max); + } + } else { + msg.MinBitPool = sbc_config.min_bitpool; + msg.MaxBitPool = sbc_config.max_bitpool; + } + + msg.MinMtuSize = minmtu; + + /* Update the media task to encode SBC properly */ + btc_a2dp_source_enc_update_req(&msg); +} + +/******************************************************************************* + ** + ** Function btc_a2dp_source_enc_init + ** + ** Description Initialize encoding task + ** + ** Returns void + ** + *******************************************************************************/ +static void btc_a2dp_source_enc_init(BT_HDR *p_msg) +{ + tBTC_MEDIA_INIT_AUDIO *pInitAudio = (tBTC_MEDIA_INIT_AUDIO *) p_msg; + + APPL_TRACE_DEBUG("btc_a2dp_source_enc_init"); + + a2dp_source_local_param.btc_aa_src_cb.timestamp = 0; + + /* SBC encoder config (enforced even if not used) */ + a2dp_source_local_param.btc_aa_src_cb.encoder.sbc_mode = SBC_MODE_STD; + a2dp_source_local_param.btc_aa_src_cb.encoder.s16ChannelMode = pInitAudio->ChannelMode; + a2dp_source_local_param.btc_aa_src_cb.encoder.s16NumOfSubBands = pInitAudio->NumOfSubBands; + a2dp_source_local_param.btc_aa_src_cb.encoder.s16NumOfBlocks = pInitAudio->NumOfBlocks; + a2dp_source_local_param.btc_aa_src_cb.encoder.s16AllocationMethod = pInitAudio->AllocationMethod; + a2dp_source_local_param.btc_aa_src_cb.encoder.s16SamplingFreq = pInitAudio->SamplingFreq; + + a2dp_source_local_param.btc_aa_src_cb.encoder.u16BitRate = btc_a2dp_source_get_sbc_rate(); + + /* Default transcoding is PCM to SBC, modified by feeding configuration */ + a2dp_source_local_param.btc_aa_src_cb.TxTranscoding = BTC_MEDIA_TRSCD_PCM_2_SBC; + a2dp_source_local_param.btc_aa_src_cb.TxAaMtuSize = ((BTC_MEDIA_AA_BUF_SIZE - BTC_MEDIA_AA_SBC_OFFSET - sizeof(BT_HDR)) + < pInitAudio->MtuSize) ? (BTC_MEDIA_AA_BUF_SIZE - BTC_MEDIA_AA_SBC_OFFSET + - sizeof(BT_HDR)) : pInitAudio->MtuSize; + + APPL_TRACE_EVENT("btc_a2dp_source_enc_init mtu %d, peer mtu %d", + a2dp_source_local_param.btc_aa_src_cb.TxAaMtuSize, pInitAudio->MtuSize); + APPL_TRACE_EVENT(" ch mode %d, subnd %d, nb blk %d, alloc %d, rate %d, freq %d", + a2dp_source_local_param.btc_aa_src_cb.encoder.s16ChannelMode, a2dp_source_local_param.btc_aa_src_cb.encoder.s16NumOfSubBands, + a2dp_source_local_param.btc_aa_src_cb.encoder.s16NumOfBlocks, + a2dp_source_local_param.btc_aa_src_cb.encoder.s16AllocationMethod, a2dp_source_local_param.btc_aa_src_cb.encoder.u16BitRate, + a2dp_source_local_param.btc_aa_src_cb.encoder.s16SamplingFreq); + + /* Reset entirely the SBC encoder */ + SBC_Encoder_Init(&(a2dp_source_local_param.btc_aa_src_cb.encoder)); + APPL_TRACE_DEBUG("btc_a2dp_source_enc_init bit pool %d", a2dp_source_local_param.btc_aa_src_cb.encoder.s16BitPool); +} + + +/******************************************************************************* + ** + ** Function btc_a2dp_source_enc_update + ** + ** Description Update encoding task + ** + ** Returns void + ** + *******************************************************************************/ + +static void btc_a2dp_source_enc_update(BT_HDR *p_msg) +{ + tBTC_MEDIA_UPDATE_AUDIO *pUpdateAudio = (tBTC_MEDIA_UPDATE_AUDIO *) p_msg; + SBC_ENC_PARAMS *pstrEncParams = &a2dp_source_local_param.btc_aa_src_cb.encoder; + UINT16 s16SamplingFreq; + SINT16 s16BitPool = 0; + SINT16 s16BitRate; + SINT16 s16FrameLen; + UINT8 protect = 0; + + APPL_TRACE_DEBUG("%s : minmtu %d, maxbp %d minbp %d", __FUNCTION__, + pUpdateAudio->MinMtuSize, pUpdateAudio->MaxBitPool, pUpdateAudio->MinBitPool); + + /* Only update the bitrate and MTU size while timer is running to make sure it has been initialized */ + //if (a2dp_source_local_param.btc_aa_src_cb.is_tx_timer) + { + a2dp_source_local_param.btc_aa_src_cb.TxAaMtuSize = ((BTC_MEDIA_AA_BUF_SIZE - + BTC_MEDIA_AA_SBC_OFFSET - sizeof(BT_HDR)) + < pUpdateAudio->MinMtuSize) ? (BTC_MEDIA_AA_BUF_SIZE - BTC_MEDIA_AA_SBC_OFFSET + - sizeof(BT_HDR)) : pUpdateAudio->MinMtuSize; + /* Set the initial target bit rate */ + pstrEncParams->u16BitRate = btc_a2dp_source_get_sbc_rate(); + + if (pstrEncParams->s16SamplingFreq == SBC_sf16000) { + s16SamplingFreq = 16000; + } else if (pstrEncParams->s16SamplingFreq == SBC_sf32000) { + s16SamplingFreq = 32000; + } else if (pstrEncParams->s16SamplingFreq == SBC_sf44100) { + s16SamplingFreq = 44100; + } else { + s16SamplingFreq = 48000; + } + + do { + if (pstrEncParams->s16NumOfBlocks == 0 || pstrEncParams->s16NumOfSubBands == 0 + || pstrEncParams->s16NumOfChannels == 0) { + APPL_TRACE_ERROR("%s - Avoiding division by zero...", __FUNCTION__); + APPL_TRACE_ERROR("%s - block=%d, subBands=%d, channels=%d", __FUNCTION__, + pstrEncParams->s16NumOfBlocks, pstrEncParams->s16NumOfSubBands, + pstrEncParams->s16NumOfChannels); + break; + } + + if ((pstrEncParams->s16ChannelMode == SBC_JOINT_STEREO) || + (pstrEncParams->s16ChannelMode == SBC_STEREO) ) { + s16BitPool = (SINT16)( (pstrEncParams->u16BitRate * + pstrEncParams->s16NumOfSubBands * 1000 / s16SamplingFreq) + - ( (32 + (4 * pstrEncParams->s16NumOfSubBands * + pstrEncParams->s16NumOfChannels) + + ( (pstrEncParams->s16ChannelMode - 2) * + pstrEncParams->s16NumOfSubBands ) ) + / pstrEncParams->s16NumOfBlocks) ); + + s16FrameLen = 4 + (4 * pstrEncParams->s16NumOfSubBands * + pstrEncParams->s16NumOfChannels) / 8 + + ( ((pstrEncParams->s16ChannelMode - 2) * + pstrEncParams->s16NumOfSubBands) + + (pstrEncParams->s16NumOfBlocks * s16BitPool) ) / 8; + + s16BitRate = (8 * s16FrameLen * s16SamplingFreq) + / (pstrEncParams->s16NumOfSubBands * + pstrEncParams->s16NumOfBlocks * 1000); + + if (s16BitRate > pstrEncParams->u16BitRate) { + s16BitPool--; + } + + if (pstrEncParams->s16NumOfSubBands == 8) { + s16BitPool = (s16BitPool > 255) ? 255 : s16BitPool; + } else { + s16BitPool = (s16BitPool > 128) ? 128 : s16BitPool; + } + } else { + s16BitPool = (SINT16)( ((pstrEncParams->s16NumOfSubBands * + pstrEncParams->u16BitRate * 1000) + / (s16SamplingFreq * pstrEncParams->s16NumOfChannels)) + - ( ( (32 / pstrEncParams->s16NumOfChannels) + + (4 * pstrEncParams->s16NumOfSubBands) ) + / pstrEncParams->s16NumOfBlocks ) ); + + pstrEncParams->s16BitPool = (s16BitPool > + (16 * pstrEncParams->s16NumOfSubBands)) + ? (16 * pstrEncParams->s16NumOfSubBands) : s16BitPool; + } + + if (s16BitPool < 0) { + s16BitPool = 0; + } + + APPL_TRACE_EVENT("bitpool candidate : %d (%d kbps)", + s16BitPool, pstrEncParams->u16BitRate); + + if (s16BitPool > pUpdateAudio->MaxBitPool) { + APPL_TRACE_DEBUG("%s computed bitpool too large (%d)", __FUNCTION__, s16BitPool); + /* Decrease bitrate */ + a2dp_source_local_param.btc_aa_src_cb.encoder.u16BitRate -= BTC_MEDIA_BITRATE_STEP; + /* Record that we have decreased the bitrate */ + protect |= 1; + } else if (s16BitPool < pUpdateAudio->MinBitPool) { + APPL_TRACE_WARNING("%s computed bitpool too small (%d)", __FUNCTION__, s16BitPool); + + /* Increase bitrate */ + UINT16 previous_u16BitRate = a2dp_source_local_param.btc_aa_src_cb.encoder.u16BitRate; + a2dp_source_local_param.btc_aa_src_cb.encoder.u16BitRate += BTC_MEDIA_BITRATE_STEP; + /* Record that we have increased the bitrate */ + protect |= 2; + /* Check over-flow */ + if (a2dp_source_local_param.btc_aa_src_cb.encoder.u16BitRate < previous_u16BitRate) { + protect |= 3; + } + } else { + break; + } + /* In case we have already increased and decreased the bitrate, just stop */ + if (protect == 3) { + APPL_TRACE_ERROR("%s could not find bitpool in range", __FUNCTION__); + break; + } + } while (1); + + /* Finally update the bitpool in the encoder structure */ + pstrEncParams->s16BitPool = s16BitPool; + + APPL_TRACE_DEBUG("%s final bit rate %d, final bit pool %d", __FUNCTION__, + a2dp_source_local_param.btc_aa_src_cb.encoder.u16BitRate, a2dp_source_local_param.btc_aa_src_cb.encoder.s16BitPool); + + /* make sure we reinitialize encoder with new settings */ + SBC_Encoder_Init(&(a2dp_source_local_param.btc_aa_src_cb.encoder)); + } +} + +#if A2D_SRC_BQB_INCLUDED +/******************************************************************************* + ** + ** Function btc_a2dp_source_bqb_sbc_encoder_set + ** + ** Description Set SBC encoder for bqb test cases A2DP/SRC/SET/BV-04-I and A2DP/SRC/SET/BV-06-I + ** + ** Returns void + ** + *******************************************************************************/ +void btc_a2dp_source_bqb_sbc_encoder_set(void) +{ + a2dp_source_local_param.btc_aa_src_cb.encoder.s16NumOfSubBands = 8; + a2dp_source_local_param.btc_aa_src_cb.encoder.s16NumOfBlocks = 8; + a2dp_source_local_param.btc_aa_src_cb.encoder.s16AllocationMethod = SBC_LOUDNESS; + a2dp_source_local_param.btc_aa_src_cb.encoder.s16ChannelMode = SBC_MONO; + a2dp_source_local_param.btc_aa_src_cb.encoder.s16SamplingFreq = SBC_sf44100; + SBC_Encoder_Init(&(a2dp_source_local_param.btc_aa_src_cb.encoder)); +} +#endif /* A2D_SRC_BQB_INCLUDED */ + +/******************************************************************************* + ** + ** Function btc_a2dp_source_pcm2sbc_init + ** + ** Description Init encoding task for PCM to SBC according to feeding + ** + ** Returns void + ** + *******************************************************************************/ +static void btc_a2dp_source_pcm2sbc_init(tBTC_MEDIA_INIT_AUDIO_FEEDING *p_feeding) +{ + BOOLEAN reconfig_needed = FALSE; + + APPL_TRACE_DEBUG("PCM feeding:"); + APPL_TRACE_DEBUG("sampling_freq:%d", p_feeding->feeding.cfg.pcm.sampling_freq); + APPL_TRACE_DEBUG("num_channel:%d", p_feeding->feeding.cfg.pcm.num_channel); + APPL_TRACE_DEBUG("bit_per_sample:%d", p_feeding->feeding.cfg.pcm.bit_per_sample); + + /* Check the PCM feeding sampling_freq */ + switch (p_feeding->feeding.cfg.pcm.sampling_freq) { + case 8000: + case 12000: + case 16000: + case 24000: + case 32000: + case 48000: + /* For these sampling_freq the AV connection must be 48000 */ + if (a2dp_source_local_param.btc_aa_src_cb.encoder.s16SamplingFreq != SBC_sf48000) { + /* Reconfiguration needed at 48000 */ + APPL_TRACE_DEBUG("SBC Reconfiguration needed at 48000"); + a2dp_source_local_param.btc_aa_src_cb.encoder.s16SamplingFreq = SBC_sf48000; + reconfig_needed = TRUE; + } + break; + + case 11025: + case 22050: + case 44100: + /* For these sampling_freq the AV connection must be 44100 */ + if (a2dp_source_local_param.btc_aa_src_cb.encoder.s16SamplingFreq != SBC_sf44100) { + /* Reconfiguration needed at 44100 */ + APPL_TRACE_DEBUG("SBC Reconfiguration needed at 44100"); + a2dp_source_local_param.btc_aa_src_cb.encoder.s16SamplingFreq = SBC_sf44100; + reconfig_needed = TRUE; + } + break; + default: + APPL_TRACE_DEBUG("Feeding PCM sampling_freq unsupported"); + break; + } + + /* Some AV Headsets do not support Mono => always ask for Stereo */ + if (a2dp_source_local_param.btc_aa_src_cb.encoder.s16ChannelMode == SBC_MONO) { + APPL_TRACE_DEBUG("SBC Reconfiguration needed in Stereo"); + a2dp_source_local_param.btc_aa_src_cb.encoder.s16ChannelMode = SBC_JOINT_STEREO; + reconfig_needed = TRUE; + } + + if (reconfig_needed != FALSE) { + APPL_TRACE_DEBUG("%s :: mtu %d", __FUNCTION__, a2dp_source_local_param.btc_aa_src_cb.TxAaMtuSize); + APPL_TRACE_DEBUG("ch mode %d, nbsubd %d, nb %d, alloc %d, rate %d, freq %d", + a2dp_source_local_param.btc_aa_src_cb.encoder.s16ChannelMode, + a2dp_source_local_param.btc_aa_src_cb.encoder.s16NumOfSubBands, a2dp_source_local_param.btc_aa_src_cb.encoder.s16NumOfBlocks, + a2dp_source_local_param.btc_aa_src_cb.encoder.s16AllocationMethod, a2dp_source_local_param.btc_aa_src_cb.encoder.u16BitRate, + a2dp_source_local_param.btc_aa_src_cb.encoder.s16SamplingFreq); + + SBC_Encoder_Init(&(a2dp_source_local_param.btc_aa_src_cb.encoder)); + } else { + APPL_TRACE_DEBUG("%s no SBC reconfig needed", __FUNCTION__); + } + +#if A2D_SRC_BQB_INCLUDED + if (a2dp_src_bqb_set_sbc_encoder_flag) { + btc_a2dp_source_bqb_sbc_encoder_set(); + } +#endif /* A2D_SRC_BQB_INCLUDED */ +} + +/******************************************************************************* + ** + ** Function btc_a2dp_source_audio_feeding_init + ** + ** Description Initialize the audio path according to the feeding format + ** + ** Returns void + ** + *******************************************************************************/ +static void btc_a2dp_source_audio_feeding_init(BT_HDR *p_msg) +{ + tBTC_MEDIA_INIT_AUDIO_FEEDING *p_feeding = (tBTC_MEDIA_INIT_AUDIO_FEEDING *) p_msg; + + APPL_TRACE_DEBUG("%s format:%d", __FUNCTION__, p_feeding->feeding.format); + + /* Save Media Feeding information */ + a2dp_source_local_param.btc_aa_src_cb.feeding_mode = p_feeding->feeding_mode; + a2dp_source_local_param.btc_aa_src_cb.media_feeding = p_feeding->feeding; + + /* Handle different feeding formats */ + switch (p_feeding->feeding.format) { + case BTC_AV_CODEC_PCM: + a2dp_source_local_param.btc_aa_src_cb.TxTranscoding = BTC_MEDIA_TRSCD_PCM_2_SBC; + btc_a2dp_source_pcm2sbc_init(p_feeding); + break; + + default : + APPL_TRACE_ERROR("unknown feeding format %d", p_feeding->feeding.format); + break; + } +} + +/******************************************************************************* + ** + ** Function btc_a2dp_source_aa_tx_flush + ** + ** Description + ** + ** Returns void + ** + *******************************************************************************/ +static void btc_a2dp_source_aa_tx_flush(void) +{ + /* Flush all enqueued music buffers (encoded) */ + APPL_TRACE_DEBUG("%s", __FUNCTION__); + + a2dp_source_local_param.btc_aa_src_cb.media_feeding_state.pcm.counter = 0; + a2dp_source_local_param.btc_aa_src_cb.media_feeding_state.pcm.aa_feed_residue = 0; + + btc_a2dp_source_flush_q(a2dp_source_local_param.btc_aa_src_cb.TxAaQ); + + btc_aa_src_data_read(NULL, -1); +} + +/******************************************************************************* + ** + ** Function btc_get_num_aa_frame + ** + ** Description + ** + ** Returns The number of media frames in this time slice + ** + *******************************************************************************/ +static UINT8 btc_get_num_aa_frame(void) +{ + UINT8 result = 0; + + switch (a2dp_source_local_param.btc_aa_src_cb.TxTranscoding) { + case BTC_MEDIA_TRSCD_PCM_2_SBC: { + UINT32 pcm_bytes_per_frame = a2dp_source_local_param.btc_aa_src_cb.encoder.s16NumOfSubBands * + a2dp_source_local_param.btc_aa_src_cb.encoder.s16NumOfBlocks * + a2dp_source_local_param.btc_aa_src_cb.media_feeding.cfg.pcm.num_channel * + a2dp_source_local_param.btc_aa_src_cb.media_feeding.cfg.pcm.bit_per_sample / 8; + + UINT32 us_this_tick = BTC_MEDIA_TIME_TICK_MS * 1000; + UINT64 now_us = time_now_us(); + if (a2dp_source_local_param.last_frame_us != 0) { +#if _POSIX_TIMERS + us_this_tick = (now_us - a2dp_source_local_param.last_frame_us); +#else + // consider the case that the number of day increases and timeofday wraps around + us_this_tick = (now_us > a2dp_source_local_param.last_frame_us) ? (now_us - a2dp_source_local_param.last_frame_us) : + (now_us + 86400000000ull - a2dp_source_local_param.last_frame_us); +#endif + } + a2dp_source_local_param.last_frame_us = now_us; + + a2dp_source_local_param.btc_aa_src_cb.media_feeding_state.pcm.counter += + a2dp_source_local_param.btc_aa_src_cb.media_feeding_state.pcm.bytes_per_tick * + us_this_tick / (BTC_MEDIA_TIME_TICK_MS * 1000); + + /* calculate nbr of frames pending for this media tick */ + result = a2dp_source_local_param.btc_aa_src_cb.media_feeding_state.pcm.counter / pcm_bytes_per_frame; + + /* limit the frames to be sent */ + UINT32 frm_nb_threshold = MAX_OUTPUT_A2DP_SRC_FRAME_QUEUE_SZ - fixed_queue_length(a2dp_source_local_param.btc_aa_src_cb.TxAaQ); + if (frm_nb_threshold > MAX_PCM_FRAME_NUM_PER_TICK) { + frm_nb_threshold = MAX_PCM_FRAME_NUM_PER_TICK; + } + + if (result > frm_nb_threshold) { + APPL_TRACE_EVENT("Limit frms to send from %d to %d", result, frm_nb_threshold); + result = frm_nb_threshold; + } + a2dp_source_local_param.btc_aa_src_cb.media_feeding_state.pcm.counter -= result * pcm_bytes_per_frame; + + BTC_TRACE_VERBOSE("WRITE %d FRAMES", result); + } + break; + + default: + APPL_TRACE_ERROR("ERROR btc_get_num_aa_frame Unsupported transcoding format 0x%x", + a2dp_source_local_param.btc_aa_src_cb.TxTranscoding); + result = 0; + break; + } + + return (UINT8)result; +} + +/******************************************************************************* + ** + ** Function btc_media_aa_read_feeding + ** + ** Description + ** + ** Returns void + ** + *******************************************************************************/ + +BOOLEAN btc_media_aa_read_feeding(void) +{ + UINT16 blocm_x_subband = a2dp_source_local_param.btc_aa_src_cb.encoder.s16NumOfSubBands * \ + a2dp_source_local_param.btc_aa_src_cb.encoder.s16NumOfBlocks; + UINT32 read_size; + UINT16 sbc_sampling = 48000; + UINT32 src_samples; + UINT16 bytes_needed = blocm_x_subband * a2dp_source_local_param.btc_aa_src_cb.encoder.s16NumOfChannels * \ + a2dp_source_local_param.btc_aa_src_cb.media_feeding.cfg.pcm.bit_per_sample / 8; + static UINT16 up_sampled_buffer[SBC_MAX_NUM_FRAME * SBC_MAX_NUM_OF_BLOCKS + * SBC_MAX_NUM_OF_CHANNELS * SBC_MAX_NUM_OF_SUBBANDS * 2]; + static UINT16 read_buffer[SBC_MAX_NUM_FRAME * SBC_MAX_NUM_OF_BLOCKS + * SBC_MAX_NUM_OF_CHANNELS * SBC_MAX_NUM_OF_SUBBANDS]; + UINT32 src_size_used; + UINT32 dst_size_used; + BOOLEAN fract_needed; + INT32 fract_max; + INT32 fract_threshold; + UINT32 nb_byte_read = 0; + + /* Get the SBC sampling rate */ + switch (a2dp_source_local_param.btc_aa_src_cb.encoder.s16SamplingFreq) { + case SBC_sf48000: + sbc_sampling = 48000; + break; + case SBC_sf44100: + sbc_sampling = 44100; + break; + case SBC_sf32000: + sbc_sampling = 32000; + break; + case SBC_sf16000: + sbc_sampling = 16000; + break; + } + + if (sbc_sampling == a2dp_source_local_param.btc_aa_src_cb.media_feeding.cfg.pcm.sampling_freq) { + read_size = bytes_needed - a2dp_source_local_param.btc_aa_src_cb.media_feeding_state.pcm.aa_feed_residue; + nb_byte_read = btc_aa_src_data_read( + ((uint8_t *)a2dp_source_local_param.btc_aa_src_cb.encoder.as16PcmBuffer) + + a2dp_source_local_param.btc_aa_src_cb.media_feeding_state.pcm.aa_feed_residue, + read_size); + if (nb_byte_read == read_size) { + a2dp_source_local_param.btc_aa_src_cb.media_feeding_state.pcm.aa_feed_residue = 0; + return TRUE; + } else { + APPL_TRACE_WARNING("### UNDERFLOW :: ONLY READ %d BYTES OUT OF %d ###", + nb_byte_read, read_size); + a2dp_source_local_param.btc_aa_src_cb.media_feeding_state.pcm.aa_feed_residue += nb_byte_read; + return FALSE; + } + } + + /* Some Feeding PCM frequencies require to split the number of sample */ + /* to read. */ + /* E.g 128/6=21.3333 => read 22 and 21 and 21 => max = 2; threshold = 0*/ + fract_needed = FALSE; /* Default */ + switch (a2dp_source_local_param.btc_aa_src_cb.media_feeding.cfg.pcm.sampling_freq) { + case 32000: + case 8000: + fract_needed = TRUE; + fract_max = 2; /* 0, 1 and 2 */ + fract_threshold = 0; /* Add one for the first */ + break; + case 16000: + fract_needed = TRUE; + fract_max = 2; /* 0, 1 and 2 */ + fract_threshold = 1; /* Add one for the first two frames*/ + break; + } + + /* Compute number of sample to read from source */ + src_samples = blocm_x_subband; + src_samples *= a2dp_source_local_param.btc_aa_src_cb.media_feeding.cfg.pcm.sampling_freq; + src_samples /= sbc_sampling; + + /* The previous division may have a remainder not null */ + if (fract_needed) { + if (a2dp_source_local_param.btc_aa_src_cb.media_feeding_state.pcm.aa_feed_counter <= fract_threshold) { + src_samples++; /* for every read before threshold add one sample */ + } + + /* do nothing if counter >= threshold */ + a2dp_source_local_param.btc_aa_src_cb.media_feeding_state.pcm.aa_feed_counter++; /* one more read */ + if (a2dp_source_local_param.btc_aa_src_cb.media_feeding_state.pcm.aa_feed_counter > fract_max) { + a2dp_source_local_param.btc_aa_src_cb.media_feeding_state.pcm.aa_feed_counter = 0; + } + } + + /* Compute number of bytes to read from source */ + read_size = src_samples; + read_size *= a2dp_source_local_param.btc_aa_src_cb.media_feeding.cfg.pcm.num_channel; + read_size *= (a2dp_source_local_param.btc_aa_src_cb.media_feeding.cfg.pcm.bit_per_sample / 8); + + /* Read Data from data channel */ + nb_byte_read = btc_aa_src_data_read((uint8_t *)read_buffer, read_size); + + if (nb_byte_read < read_size) { + APPL_TRACE_WARNING("### UNDERRUN :: ONLY READ %d BYTES OUT OF %d ###", + nb_byte_read, read_size); + + if (nb_byte_read == 0) { + return FALSE; + } + + if (a2dp_source_local_param.btc_aa_src_cb.feeding_mode == BTC_AV_FEEDING_ASYNCHRONOUS) { + /* Fill the unfilled part of the read buffer with silence (0) */ + memset(((UINT8 *)read_buffer) + nb_byte_read, 0, read_size - nb_byte_read); + nb_byte_read = read_size; + } + } + + /* Initialize PCM up-sampling engine */ + bta_av_sbc_init_up_sample(a2dp_source_local_param.btc_aa_src_cb.media_feeding.cfg.pcm.sampling_freq, + sbc_sampling, a2dp_source_local_param.btc_aa_src_cb.media_feeding.cfg.pcm.bit_per_sample, + a2dp_source_local_param.btc_aa_src_cb.media_feeding.cfg.pcm.num_channel); + + /* re-sample read buffer */ + /* The output PCM buffer will be stereo, 16 bit per sample */ + dst_size_used = bta_av_sbc_up_sample((UINT8 *)read_buffer, + (UINT8 *)up_sampled_buffer + a2dp_source_local_param.btc_aa_src_cb.media_feeding_state.pcm.aa_feed_residue, + nb_byte_read, + sizeof(up_sampled_buffer) - a2dp_source_local_param.btc_aa_src_cb.media_feeding_state.pcm.aa_feed_residue, + &src_size_used); + + /* update the residue */ + a2dp_source_local_param.btc_aa_src_cb.media_feeding_state.pcm.aa_feed_residue += dst_size_used; + + /* only copy the pcm sample when we have up-sampled enough PCM */ + if (a2dp_source_local_param.btc_aa_src_cb.media_feeding_state.pcm.aa_feed_residue >= bytes_needed) { + /* Copy the output pcm samples in SBC encoding buffer */ + memcpy((UINT8 *)a2dp_source_local_param.btc_aa_src_cb.encoder.as16PcmBuffer, + (UINT8 *)up_sampled_buffer, + bytes_needed); + /* update the residue */ + a2dp_source_local_param.btc_aa_src_cb.media_feeding_state.pcm.aa_feed_residue -= bytes_needed; + + if (a2dp_source_local_param.btc_aa_src_cb.media_feeding_state.pcm.aa_feed_residue != 0) { + memcpy((UINT8 *)up_sampled_buffer, + (UINT8 *)up_sampled_buffer + bytes_needed, + a2dp_source_local_param.btc_aa_src_cb.media_feeding_state.pcm.aa_feed_residue); + } + return TRUE; + } + + return FALSE; +} + +/******************************************************************************* + ** + ** Function btc_media_aa_prep_sbc_2_send + ** + ** Description + ** + ** Returns void + ** + *******************************************************************************/ +static void btc_media_aa_prep_sbc_2_send(UINT8 nb_frame) +{ + BT_HDR *p_buf; + UINT16 blocm_x_subband = a2dp_source_local_param.btc_aa_src_cb.encoder.s16NumOfSubBands * + a2dp_source_local_param.btc_aa_src_cb.encoder.s16NumOfBlocks; + + while (nb_frame) { + if (NULL == (p_buf = osi_malloc(BTC_MEDIA_AA_BUF_SIZE))) { + APPL_TRACE_ERROR ("ERROR btc_media_aa_prep_sbc_2_send no buffer TxCnt %d ", + fixed_queue_length(a2dp_source_local_param.btc_aa_src_cb.TxAaQ)); + return; + } + + /* Init buffer */ + p_buf->offset = BTC_MEDIA_AA_SBC_OFFSET; + p_buf->len = 0; + p_buf->layer_specific = 0; + + do { + /* Write @ of allocated buffer in encoder.pu8Packet */ + a2dp_source_local_param.btc_aa_src_cb.encoder.pu8Packet = (UINT8 *) (p_buf + 1) + p_buf->offset + p_buf->len; + /* Fill allocated buffer with 0 */ + memset(a2dp_source_local_param.btc_aa_src_cb.encoder.as16PcmBuffer, 0, blocm_x_subband + * a2dp_source_local_param.btc_aa_src_cb.encoder.s16NumOfChannels); + + /* Read PCM data and upsample them if needed */ + if (btc_media_aa_read_feeding()) { + /* SBC encode and descramble frame */ + SBC_Encoder(&(a2dp_source_local_param.btc_aa_src_cb.encoder)); + + /* Update SBC frame length */ + p_buf->len += a2dp_source_local_param.btc_aa_src_cb.encoder.u16PacketLength; + nb_frame--; + p_buf->layer_specific++; + } else { + APPL_TRACE_WARNING("btc_media_aa_prep_sbc_2_send underflow %d, %d", + nb_frame, a2dp_source_local_param.btc_aa_src_cb.media_feeding_state.pcm.aa_feed_residue); + a2dp_source_local_param.btc_aa_src_cb.media_feeding_state.pcm.counter += nb_frame * + a2dp_source_local_param.btc_aa_src_cb.encoder.s16NumOfSubBands * + a2dp_source_local_param.btc_aa_src_cb.encoder.s16NumOfBlocks * + a2dp_source_local_param.btc_aa_src_cb.media_feeding.cfg.pcm.num_channel * + a2dp_source_local_param.btc_aa_src_cb.media_feeding.cfg.pcm.bit_per_sample / 8; + /* no more pcm to read */ + nb_frame = 0; + + /* break read loop if timer was stopped (media task stopped) */ + if ( a2dp_source_local_param.btc_aa_src_cb.is_tx_timer == FALSE ) { + osi_free(p_buf); + return; + } + } + + } while (((p_buf->len + a2dp_source_local_param.btc_aa_src_cb.encoder.u16PacketLength) < a2dp_source_local_param.btc_aa_src_cb.TxAaMtuSize) + && (p_buf->layer_specific < 0x0F) && nb_frame); + + if (p_buf->len) { + /* timestamp of the media packet header represent the TS of the first SBC frame + i.e the timestamp before including this frame */ + *((UINT32 *) (p_buf + 1)) = a2dp_source_local_param.btc_aa_src_cb.timestamp; + + a2dp_source_local_param.btc_aa_src_cb.timestamp += p_buf->layer_specific * blocm_x_subband; + + if (a2dp_source_local_param.btc_aa_src_cb.tx_flush) { + APPL_TRACE_DEBUG("### tx suspended, discarded frame ###"); + + if (fixed_queue_length(a2dp_source_local_param.btc_aa_src_cb.TxAaQ) > 0) { + btc_a2dp_source_flush_q(a2dp_source_local_param.btc_aa_src_cb.TxAaQ); + } + + osi_free(p_buf); + return; + } + + /* Enqueue the encoded SBC frame in AA Tx Queue */ + fixed_queue_enqueue(a2dp_source_local_param.btc_aa_src_cb.TxAaQ, p_buf, FIXED_QUEUE_MAX_TIMEOUT); + } else { + osi_free(p_buf); + } + } +} + +/******************************************************************************* + ** + ** Function btc_a2dp_source_prep_2_send + ** + ** Description + ** + ** Returns void + ** + *******************************************************************************/ +static void btc_a2dp_source_prep_2_send(UINT8 nb_frame) +{ + // Check for TX queue overflow + if (nb_frame > MAX_OUTPUT_A2DP_SRC_FRAME_QUEUE_SZ) { + nb_frame = MAX_OUTPUT_A2DP_SRC_FRAME_QUEUE_SZ; + } + + if (fixed_queue_length(a2dp_source_local_param.btc_aa_src_cb.TxAaQ) > (MAX_OUTPUT_A2DP_SRC_FRAME_QUEUE_SZ - nb_frame)) { + APPL_TRACE_WARNING("TX Q overflow: %d/%d", + fixed_queue_length(a2dp_source_local_param.btc_aa_src_cb.TxAaQ), MAX_OUTPUT_A2DP_SRC_FRAME_QUEUE_SZ - nb_frame); + } + + while (fixed_queue_length(a2dp_source_local_param.btc_aa_src_cb.TxAaQ) > (MAX_OUTPUT_A2DP_SRC_FRAME_QUEUE_SZ - nb_frame)) { + osi_free(fixed_queue_dequeue(a2dp_source_local_param.btc_aa_src_cb.TxAaQ, 0)); + } + + // Transcode frame + + switch (a2dp_source_local_param.btc_aa_src_cb.TxTranscoding) { + case BTC_MEDIA_TRSCD_PCM_2_SBC: + btc_media_aa_prep_sbc_2_send(nb_frame); + break; + + default: + APPL_TRACE_ERROR("%s unsupported transcoding format 0x%x", __func__, a2dp_source_local_param.btc_aa_src_cb.TxTranscoding); + break; + } +} + +/******************************************************************************* + ** + ** Function btc_a2dp_source_send_aa_frame + ** + ** Description + ** + ** Returns void + ** + *******************************************************************************/ +static void btc_a2dp_source_send_aa_frame(void) +{ + UINT8 nb_frame_2_send; + + /* get the number of frame to send */ + nb_frame_2_send = btc_get_num_aa_frame(); + + if (nb_frame_2_send != 0) { + /* format and Q buffer to send */ + btc_a2dp_source_prep_2_send(nb_frame_2_send); + } + + /* send it */ + BTC_TRACE_VERBOSE("%s: send %d frames", __FUNCTION__, nb_frame_2_send); + bta_av_ci_src_data_ready(BTA_AV_CHNL_AUDIO); +} + +static void btc_a2dp_source_handle_timer(UNUSED_ATTR void *context) +{ + log_tstamps_us("media task tx timer"); + +#if (BTA_AV_INCLUDED == TRUE) + if (btc_a2dp_source_state != BTC_A2DP_SOURCE_STATE_ON || g_a2dp_source_ongoing_deinit){ + return; + } + + if (a2dp_source_local_param.btc_aa_src_cb.is_tx_timer == TRUE) { + btc_a2dp_source_send_aa_frame(); + } else { + APPL_TRACE_WARNING("Media task Scheduled after Suspend"); + } +#endif +} + +/******************************************************************************* + ** + ** Function btc_a2dp_source_feeding_state_reset + ** + ** Description Reset the media feeding state + ** + ** Returns void + ** + *******************************************************************************/ +static void btc_a2dp_source_feeding_state_reset(void) +{ + /* By default, just clear the entire state */ + memset(&a2dp_source_local_param.btc_aa_src_cb.media_feeding_state, 0, sizeof(a2dp_source_local_param.btc_aa_src_cb.media_feeding_state)); + + if (a2dp_source_local_param.btc_aa_src_cb.TxTranscoding == BTC_MEDIA_TRSCD_PCM_2_SBC) { + a2dp_source_local_param.btc_aa_src_cb.media_feeding_state.pcm.bytes_per_tick = + (a2dp_source_local_param.btc_aa_src_cb.media_feeding.cfg.pcm.sampling_freq * + a2dp_source_local_param.btc_aa_src_cb.media_feeding.cfg.pcm.bit_per_sample / 8 * + a2dp_source_local_param.btc_aa_src_cb.media_feeding.cfg.pcm.num_channel * + BTC_MEDIA_TIME_TICK_MS) / 1000; + + APPL_TRACE_EVENT("pcm bytes per tick %d", + (int)a2dp_source_local_param.btc_aa_src_cb.media_feeding_state.pcm.bytes_per_tick); + } +} + +/******************************************************************************* + ** + ** Function btc_a2dp_source_aa_stop_tx + ** + ** Description Stop media task encoding + ** + ** Returns void + ** + *******************************************************************************/ +static void btc_a2dp_source_aa_stop_tx(void) +{ + APPL_TRACE_DEBUG("%s is_tx_timer: %d", __func__, a2dp_source_local_param.btc_aa_src_cb.is_tx_timer); + + const bool send_ack = (a2dp_source_local_param.btc_aa_src_cb.is_tx_timer != FALSE); + + /* Stop the timer first */ + if (a2dp_source_local_param.btc_aa_src_cb.media_alarm) { + osi_alarm_cancel(a2dp_source_local_param.btc_aa_src_cb.media_alarm); + osi_alarm_free(a2dp_source_local_param.btc_aa_src_cb.media_alarm); + } + a2dp_source_local_param.btc_aa_src_cb.media_alarm = NULL; + a2dp_source_local_param.btc_aa_src_cb.is_tx_timer = FALSE; + + /* Try to send acknowldegment once the media stream is + stopped. This will make sure that the A2DP HAL layer is + un-blocked on wait for acknowledgment for the sent command. + This resolves a corner cases AVDTP SUSPEND collision + when the DUT and the remote device issue SUSPEND simultaneously + and due to the processing of the SUSPEND request from the remote, + the media path is torn down. If the A2DP HAL happens to wait + for ACK for the initiated SUSPEND, it would never receive it casuing + a block/wait. Due to this acknowledgement, the A2DP HAL is guranteed + to get the ACK for any pending command in such cases. */ + + if (send_ack) { + btc_a2dp_control_command_ack(ESP_A2D_MEDIA_CTRL_ACK_SUCCESS); + } + + /* audio engine stopped, reset tx suspended flag */ + a2dp_source_local_param.btc_aa_src_cb.tx_flush = 0; + a2dp_source_local_param.last_frame_us = 0; + + /* Reset the feeding state */ + btc_a2dp_source_feeding_state_reset(); +} + +/******************************************************************************* + ** + ** Function btc_a2dp_source_aa_start_tx + ** + ** Description Start media task encoding + ** + ** Returns void + ** + *******************************************************************************/ +static void btc_a2dp_source_alarm_cb(UNUSED_ATTR void *context) +{ + if (a2dp_source_local_param.btc_aa_src_task_hdl) { + osi_thread_post_event(a2dp_source_local_param.btc_aa_src_cb.poll_data, OSI_THREAD_MAX_TIMEOUT); + } else { + APPL_TRACE_DEBUG("[%s] A2DP ALREADY FREED", __func__); + btc_a2dp_source_aa_stop_tx(); + } +} + +static void btc_a2dp_source_aa_start_tx(void) +{ + APPL_TRACE_DEBUG("btc_a2dp_source_aa_start_tx is timer %d, feeding mode %d", + a2dp_source_local_param.btc_aa_src_cb.is_tx_timer, a2dp_source_local_param.btc_aa_src_cb.feeding_mode); + + a2dp_source_local_param.btc_aa_src_cb.is_tx_timer = TRUE; + a2dp_source_local_param.last_frame_us = 0; + + /* Reset the media feeding state */ + btc_a2dp_source_feeding_state_reset(); + + APPL_TRACE_EVENT("starting timer %dms", BTC_MEDIA_TIME_TICK_MS); + + assert(a2dp_source_local_param.btc_aa_src_cb.media_alarm == NULL); + + a2dp_source_local_param.btc_aa_src_cb.media_alarm = osi_alarm_new("aaTx", btc_a2dp_source_alarm_cb, NULL, BTC_MEDIA_TIME_TICK_MS); + + if (!a2dp_source_local_param.btc_aa_src_cb.media_alarm) { + BTC_TRACE_ERROR("%s unable to allocate media alarm.", __func__); + return; + } + + osi_alarm_set_periodic(a2dp_source_local_param.btc_aa_src_cb.media_alarm, BTC_MEDIA_TIME_TICK_MS); +} + +/******************************************************************************* + ** + ** Function btc_a2dp_source_flush_q + ** + ** Description + ** + ** Returns void + ** + *******************************************************************************/ +static void btc_a2dp_source_flush_q(fixed_queue_t *p_q) +{ + while (! fixed_queue_is_empty(p_q)) { + osi_free(fixed_queue_dequeue(p_q, 0)); + } +} + +static void btc_a2dp_source_thread_init(UNUSED_ATTR void *context) +{ + APPL_TRACE_EVENT("%s\n", __func__); + memset(&a2dp_source_local_param.btc_aa_src_cb, 0, sizeof(a2dp_source_local_param.btc_aa_src_cb)); + + btc_a2dp_source_state = BTC_A2DP_SOURCE_STATE_ON; + + struct osi_event *poll_data = osi_event_create(btc_a2dp_source_handle_timer, NULL); + assert(poll_data != NULL); + osi_event_bind(poll_data, a2dp_source_local_param.btc_aa_src_task_hdl, BTC_A2DP_SRC_DATA_QUEUE_IDX); + a2dp_source_local_param.btc_aa_src_cb.poll_data = poll_data; + + a2dp_source_local_param.btc_aa_src_cb.TxAaQ = fixed_queue_new(QUEUE_SIZE_MAX); + + btc_a2dp_control_init(); +} + +static void btc_a2dp_source_thread_cleanup(UNUSED_ATTR void *context) +{ + /* Clear media task flag */ + btc_a2dp_source_state = BTC_A2DP_SOURCE_STATE_OFF; + + btc_a2dp_control_cleanup(); + + fixed_queue_free(a2dp_source_local_param.btc_aa_src_cb.TxAaQ, osi_free_func); + + a2dp_source_local_param.btc_aa_src_cb.TxAaQ = NULL; + + osi_event_delete(a2dp_source_local_param.btc_aa_src_cb.poll_data); + a2dp_source_local_param.btc_aa_src_cb.poll_data = NULL; +} + +#endif /* BTC_AV_INCLUDED */ diff --git a/lib/bt/host/bluedroid/btc/profile/std/a2dp/btc_av.c b/lib/bt/host/bluedroid/btc/profile/std/a2dp/btc_av.c new file mode 100644 index 00000000..4c237b1e --- /dev/null +++ b/lib/bt/host/bluedroid/btc/profile/std/a2dp/btc_av.c @@ -0,0 +1,1735 @@ +/* + * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/***************************************************************************** + * + * Filename: btc_avk.c + * + * Description: AV implementation + * + *****************************************************************************/ +#include "common/bt_target.h" +#include +#include "common/bt_trace.h" +#include "common/bt_defs.h" +#include "osi/allocator.h" +#include "stack/btu.h" +#include "bta/bta_av_api.h" +#include "btc/btc_dm.h" +#include "btc/btc_common.h" +#include "btc/btc_manage.h" +#include "btc_av.h" +#include "btc_avrc.h" +#include "btc/btc_util.h" +#include "btc/btc_profile_queue.h" +#include "btc_a2dp.h" +#include "btc_a2dp_control.h" +#include "btc_a2dp_sink.h" +#include "btc_a2dp_source.h" +#include "esp_a2dp_api.h" +#include "osi/alarm.h" + +#if BTC_AV_INCLUDED + +// global variable to inidcate avrc is initialized with a2dp +bool g_av_with_rc; +// global variable to indicate a2dp is initialized +bool g_a2dp_on_init; +// global variable to indicate a2dp is deinitialized +bool g_a2dp_on_deinit; +// global variable to indicate a2dp source deinitialization is ongoing +bool g_a2dp_source_ongoing_deinit; +// global variable to indicate a2dp sink deinitialization is ongoing +bool g_a2dp_sink_ongoing_deinit; + + +/***************************************************************************** +** Constants & Macros +******************************************************************************/ +#define BTC_AV_SERVICE_NAME "Advanced Audio" + +#define BTC_TIMEOUT_AV_OPEN_ON_RC_SECS 2 +/* Max audio per 3-DH5 EDR packet: 23.2ms + jitter buffer: 5(JITTER_BUFFER_WATER_LEVEL) */ +#define BTC_DELAY_REPORT_DFT_VALUE 1200 // 120ms + +typedef enum { + BTC_AV_STATE_IDLE = 0x0, + BTC_AV_STATE_OPENING, + BTC_AV_STATE_OPENED, + BTC_AV_STATE_STARTED, + BTC_AV_STATE_CLOSING +} btc_av_state_t; + +/* Should not need dedicated suspend state as actual actions are no + different than open state. Suspend flags are needed however to prevent + media task from trying to restart stream during remote suspend or while + we are in the process of a local suspend */ + +#define BTC_AV_FLAG_LOCAL_SUSPEND_PENDING 0x1 +#define BTC_AV_FLAG_REMOTE_SUSPEND 0x2 +#define BTC_AV_FLAG_PENDING_START 0x4 +#define BTC_AV_FLAG_PENDING_STOP 0x8 + +/***************************************************************************** +** Local type definitions +******************************************************************************/ + +typedef struct { + int service_id; + tBTA_AV_HNDL bta_handle; + bt_bdaddr_t peer_bda; + btc_sm_handle_t sm_handle; + UINT8 flags; + tBTA_AV_EDR edr; + UINT8 peer_sep; /* sep type of peer device */ +#if BTC_AV_SRC_INCLUDED + osi_alarm_t *tle_av_open_on_rc; +#endif /* BTC_AV_SRC_INCLUDED */ +} btc_av_cb_t; + +typedef struct { + bt_bdaddr_t target_bda; + uint16_t uuid; +} btc_av_connect_req_t; + +typedef struct { + bt_bdaddr_t target_bda; +} btc_av_disconn_req_t; + +/***************************************************************************** +** Static variables +******************************************************************************/ + +#if A2D_DYNAMIC_MEMORY == FALSE +static btc_av_cb_t btc_av_cb = {0}; +#else +static btc_av_cb_t *btc_av_cb_ptr = NULL; +#define btc_av_cb (*btc_av_cb_ptr) +#endif ///A2D_DYNAMIC_MEMORY == FALSE + +/* both interface and media task needs to be ready to alloc incoming request */ +#define CHECK_BTAV_INIT() do \ +{ \ + assert (btc_av_cb.sm_handle != NULL); \ +} while (0) + + +/* Helper macro to avoid code duplication in the state machine handlers */ +#define CHECK_RC_EVENT(e, d) \ + case BTA_AV_RC_OPEN_EVT: \ + case BTA_AV_RC_CLOSE_EVT: \ + case BTA_AV_REMOTE_CMD_EVT: \ + case BTA_AV_VENDOR_CMD_EVT: \ + case BTA_AV_META_MSG_EVT: \ + case BTA_AV_RC_FEAT_EVT: \ + case BTA_AV_REMOTE_RSP_EVT: \ + { \ + btc_rc_handler(e, d);\ + }break; \ + +static BOOLEAN btc_av_state_idle_handler(btc_sm_event_t event, void *data); +static BOOLEAN btc_av_state_opening_handler(btc_sm_event_t event, void *data); +static BOOLEAN btc_av_state_opened_handler(btc_sm_event_t event, void *data); +static BOOLEAN btc_av_state_started_handler(btc_sm_event_t event, void *data); +static BOOLEAN btc_av_state_closing_handler(btc_sm_event_t event, void *data); +static void clean_up(int service_id); + +#if BTC_AV_SRC_INCLUDED +static bt_status_t btc_a2d_src_init(void); +static bt_status_t btc_a2d_src_connect(bt_bdaddr_t *remote_bda); +static void btc_a2d_src_deinit(void); +#endif /* BTC_AV_SRC_INCLUDED */ + +#if BTC_AV_SINK_INCLUDED +static bt_status_t btc_a2d_sink_init(void); +static bt_status_t btc_a2d_sink_connect(bt_bdaddr_t *remote_bda); +static void btc_a2d_sink_set_delay_value(UINT16 delay_value); +static void btc_a2d_sink_get_delay_value(void); +static void btc_a2d_sink_deinit(void); +#endif /* BTC_AV_SINK_INCLUDED */ + +static const btc_sm_handler_t btc_av_state_handlers[] = { + btc_av_state_idle_handler, + btc_av_state_opening_handler, + btc_av_state_opened_handler, + btc_av_state_started_handler, + btc_av_state_closing_handler +}; + +static void btc_av_event_free_data(btc_msg_t *msg); + +/************************************************************************* +** Extern functions +*************************************************************************/ + +extern tBTA_AV_CO_FUNCTS bta_av_a2d_cos; +extern tBTA_AVRC_CO_FUNCTS bta_avrc_cos; +/***************************************************************************** +** Local helper functions +******************************************************************************/ +static inline void btc_a2d_cb_to_app(esp_a2d_cb_event_t event, esp_a2d_cb_param_t *param) +{ + esp_a2d_cb_t btc_a2d_cb = (esp_a2d_cb_t)btc_profile_cb_get(BTC_PID_A2DP); + if (btc_a2d_cb) { + btc_a2d_cb(event, param); + } +} + +UNUSED_ATTR static const char *dump_av_sm_state_name(btc_av_state_t state) +{ + switch (state) { + CASE_RETURN_STR(BTC_AV_STATE_IDLE) + CASE_RETURN_STR(BTC_AV_STATE_OPENING) + CASE_RETURN_STR(BTC_AV_STATE_OPENED) + CASE_RETURN_STR(BTC_AV_STATE_STARTED) + CASE_RETURN_STR(BTC_AV_STATE_CLOSING) + default: return "UNKNOWN_STATE"; + } +} + +UNUSED_ATTR static const char *dump_av_sm_event_name(btc_av_sm_event_t event) +{ + switch ((int)event) { + CASE_RETURN_STR(BTA_AV_ENABLE_EVT) + CASE_RETURN_STR(BTA_AV_REGISTER_EVT) + CASE_RETURN_STR(BTA_AV_OPEN_EVT) + CASE_RETURN_STR(BTA_AV_CLOSE_EVT) + CASE_RETURN_STR(BTA_AV_START_EVT) + CASE_RETURN_STR(BTA_AV_STOP_EVT) + CASE_RETURN_STR(BTA_AV_PROTECT_REQ_EVT) + CASE_RETURN_STR(BTA_AV_PROTECT_RSP_EVT) + CASE_RETURN_STR(BTA_AV_RC_OPEN_EVT) + CASE_RETURN_STR(BTA_AV_RC_CLOSE_EVT) + CASE_RETURN_STR(BTA_AV_REMOTE_CMD_EVT) + CASE_RETURN_STR(BTA_AV_REMOTE_RSP_EVT) + CASE_RETURN_STR(BTA_AV_VENDOR_CMD_EVT) + CASE_RETURN_STR(BTA_AV_VENDOR_RSP_EVT) + CASE_RETURN_STR(BTA_AV_RECONFIG_EVT) + CASE_RETURN_STR(BTA_AV_SUSPEND_EVT) + CASE_RETURN_STR(BTA_AV_PENDING_EVT) + CASE_RETURN_STR(BTA_AV_META_MSG_EVT) + CASE_RETURN_STR(BTA_AV_REJECT_EVT) + CASE_RETURN_STR(BTA_AV_RC_FEAT_EVT) + CASE_RETURN_STR(BTC_SM_ENTER_EVT) + CASE_RETURN_STR(BTC_SM_EXIT_EVT) + CASE_RETURN_STR(BTC_AV_CONNECT_REQ_EVT) + CASE_RETURN_STR(BTC_AV_DISCONNECT_REQ_EVT) + CASE_RETURN_STR(BTC_AV_START_STREAM_REQ_EVT) + CASE_RETURN_STR(BTC_AV_SUSPEND_STREAM_REQ_EVT) + CASE_RETURN_STR(BTC_AV_SINK_CONFIG_REQ_EVT) + default: return "UNKNOWN_EVENT"; + } +} + +/**************************************************************************** +** Local helper functions +*****************************************************************************/ +#if BTC_AV_SRC_INCLUDED +/******************************************************************************* +** +** Function btc_initiate_av_open_tmr_hdlr +** +** Description Timer to trigger AV open if the remote headset establishes +** RC connection w/o AV connection. The timer is needed to IOP +** with headsets that do establish AV after RC connection. +** +** Returns void +** +*******************************************************************************/ +static void btc_initiate_av_open_tmr_hdlr(void *arg) +{ + UNUSED(arg); + BD_ADDR peer_addr; + btc_av_connect_req_t connect_req; + /* is there at least one RC connection - There should be */ + if (btc_rc_get_connected_peer(peer_addr)) { + BTC_TRACE_DEBUG("%s Issuing connect to the remote RC peer", __FUNCTION__); + /* In case of AVRCP connection request, we will initiate SRC connection */ + memcpy(connect_req.target_bda.address, peer_addr, sizeof(bt_bdaddr_t)); + connect_req.uuid = UUID_SERVCLASS_AUDIO_SOURCE; + btc_dispatch_sm_event(BTC_AV_CONNECT_REQ_EVT, &connect_req, sizeof(btc_av_connect_req_t)); + } else { + BTC_TRACE_ERROR("%s No connected RC peers", __FUNCTION__); + } +} +#endif /* BTC_AV_SRC_INCLUDED */ + +/***************************************************************************** +** Static functions +******************************************************************************/ +static void btc_report_connection_state(esp_a2d_connection_state_t state, bt_bdaddr_t *bd_addr, int disc_rsn) +{ + // todo: add callback for SRC + esp_a2d_cb_param_t param; + memset(¶m, 0, sizeof(esp_a2d_cb_param_t)); + + param.conn_stat.state = state; + if (bd_addr) { + memcpy(param.conn_stat.remote_bda, bd_addr, sizeof(esp_bd_addr_t)); + } + if (state == ESP_A2D_CONNECTION_STATE_DISCONNECTED) { + param.conn_stat.disc_rsn = (disc_rsn == 0) ? ESP_A2D_DISC_RSN_NORMAL : + ESP_A2D_DISC_RSN_ABNORMAL; + } + btc_a2d_cb_to_app(ESP_A2D_CONNECTION_STATE_EVT, ¶m); +} + +static void btc_report_audio_state(esp_a2d_audio_state_t state, bt_bdaddr_t *bd_addr) +{ + // todo: add callback for SRC + esp_a2d_cb_param_t param; + memset(¶m, 0, sizeof(esp_a2d_cb_param_t)); + + param.audio_stat.state = state; + if (bd_addr) { + memcpy(param.audio_stat.remote_bda, bd_addr, sizeof(esp_bd_addr_t)); + } + btc_a2d_cb_to_app(ESP_A2D_AUDIO_STATE_EVT, ¶m); +} + +/***************************************************************************** +** +** Function btc_av_state_idle_handler +** +** Description State managing disconnected AV link +** +** Returns TRUE if event was processed, FALSE otherwise +** +*******************************************************************************/ + +static BOOLEAN btc_av_state_idle_handler(btc_sm_event_t event, void *p_data) +{ + esp_a2d_cb_param_t param; + + BTC_TRACE_DEBUG("%s event: %s flags %x\n", __FUNCTION__, + dump_av_sm_event_name(event), btc_av_cb.flags); + + switch (event) { + case BTC_SM_ENTER_EVT: + /* clear the peer_bda */ + memset(&btc_av_cb.peer_bda, 0, sizeof(bt_bdaddr_t)); + btc_av_cb.flags = 0; + btc_av_cb.edr = 0; + btc_a2dp_on_idle(); + break; + + case BTC_SM_EXIT_EVT: + break; + + case BTA_AV_ENABLE_EVT: + break; + + case BTA_AV_REGISTER_EVT: + btc_av_cb.bta_handle = ((tBTA_AV *)p_data)->registr.hndl; + break; + + case BTA_AV_PENDING_EVT: + case BTC_AV_CONNECT_REQ_EVT: { + if (event == BTC_AV_CONNECT_REQ_EVT) { + memcpy(&btc_av_cb.peer_bda, &((btc_av_connect_req_t *)p_data)->target_bda, + sizeof(bt_bdaddr_t)); + if (g_av_with_rc) { + BTA_AvOpen(btc_av_cb.peer_bda.address, btc_av_cb.bta_handle, + TRUE, BTA_SEC_AUTHENTICATE, ((btc_av_connect_req_t *)p_data)->uuid); + } else { + BTA_AvOpen(btc_av_cb.peer_bda.address, btc_av_cb.bta_handle, + FALSE, BTA_SEC_AUTHENTICATE, ((btc_av_connect_req_t *)p_data)->uuid); + } + } else if (event == BTA_AV_PENDING_EVT) { + bdcpy(btc_av_cb.peer_bda.address, ((tBTA_AV *)p_data)->pend.bd_addr); + UINT16 uuid = (btc_av_cb.service_id == BTA_A2DP_SOURCE_SERVICE_ID) ? UUID_SERVCLASS_AUDIO_SOURCE : + UUID_SERVCLASS_AUDIO_SINK; + if (g_av_with_rc) { + BTA_AvOpen(btc_av_cb.peer_bda.address, btc_av_cb.bta_handle, + TRUE, BTA_SEC_AUTHENTICATE, uuid); + } else { + BTA_AvOpen(btc_av_cb.peer_bda.address, btc_av_cb.bta_handle, + FALSE, BTA_SEC_AUTHENTICATE, uuid); + } + } + btc_sm_change_state(btc_av_cb.sm_handle, BTC_AV_STATE_OPENING); + } break; + + case BTC_AV_DISCONNECT_REQ_EVT: + BTC_TRACE_WARNING("No Link At All."); + btc_report_connection_state(ESP_A2D_CONNECTION_STATE_DISCONNECTED, &((btc_av_disconn_req_t *)p_data)->target_bda, 0); + break; + + case BTA_AV_RC_OPEN_EVT: + /* IOP_FIX: Jabra 620 only does RC open without AV open whenever it connects. So + * as per the AV WP, an AVRC connection cannot exist without an AV connection. Therefore, + * we initiate an AV connection if an RC_OPEN_EVT is received when we are in AV_CLOSED state. + * We initiate the AV connection after a small 3s timeout to avoid any collisions from the + * headsets, as some headsets initiate the AVRC connection first and then + * immediately initiate the AV connection + * + * TODO: We may need to do this only on an AVRCP Play. FixMe + */ +#if BTC_AV_SRC_INCLUDED + BTC_TRACE_DEBUG("BTA_AV_RC_OPEN_EVT received w/o AV"); + btc_av_cb.tle_av_open_on_rc = osi_alarm_new("AVconn", btc_initiate_av_open_tmr_hdlr, NULL, BTC_TIMEOUT_AV_OPEN_ON_RC_SECS * 1000); + osi_alarm_set(btc_av_cb.tle_av_open_on_rc, BTC_TIMEOUT_AV_OPEN_ON_RC_SECS * 1000); +#endif /* BTC_AV_SRC_INCLUDED */ + btc_rc_handler(event, p_data); + break; + + case BTA_AV_REMOTE_CMD_EVT: + case BTA_AV_VENDOR_CMD_EVT: + case BTA_AV_META_MSG_EVT: + case BTA_AV_RC_FEAT_EVT: + case BTA_AV_REMOTE_RSP_EVT: + btc_rc_handler(event, (tBTA_AV *)p_data); + break; + + case BTA_AV_RC_CLOSE_EVT: +#if BTC_AV_SRC_INCLUDED + if (btc_av_cb.tle_av_open_on_rc) { + osi_alarm_free(btc_av_cb.tle_av_open_on_rc); + btc_av_cb.tle_av_open_on_rc = NULL; + } +#endif /* BTC_AV_SRC_INCLUDED */ + btc_rc_handler(event, p_data); + break; + + case BTA_AV_SNK_PSC_CFG_EVT: +#if BTC_AV_SINK_INCLUDED + param.a2d_psc_cfg_stat.psc_mask = ((tBTA_AV *)p_data)->psc.psc_mask; + btc_a2d_cb_to_app(ESP_A2D_SNK_PSC_CFG_EVT, ¶m); +#endif /* BTC_AV_SINK_INCLUDED */ + break; + case BTA_AV_SET_DELAY_VALUE_EVT: +#if BTC_AV_SINK_INCLUDED + param.a2d_set_delay_value_stat.delay_value = ((tBTA_AV *)p_data)->delay.delay_value; + param.a2d_set_delay_value_stat.set_state = ((tBTA_AV *)p_data)->delay.status; + btc_a2d_cb_to_app(ESP_A2D_SNK_SET_DELAY_VALUE_EVT, ¶m); +#endif /* BTC_AV_SINK_INCLUDED */ + break; + case BTA_AV_GET_DELAY_VALUE_EVT: +#if BTC_AV_SINK_INCLUDED + param.a2d_get_delay_value_stat.delay_value = ((tBTA_AV *)p_data)->delay.delay_value; + btc_a2d_cb_to_app(ESP_A2D_SNK_GET_DELAY_VALUE_EVT, ¶m); +#endif /* BTC_AV_SINK_INCLUDED */ + break; + + default: + BTC_TRACE_WARNING("%s : unhandled event:%s\n", __FUNCTION__, + dump_av_sm_event_name(event)); + return FALSE; + } + + return TRUE; +} +/***************************************************************************** +** +** Function btc_av_state_opening_handler +** +** Description Intermediate state managing events during establishment +** of avdtp channel +** +** Returns TRUE if event was processed, FALSE otherwise +** +*******************************************************************************/ + +static BOOLEAN btc_av_state_opening_handler(btc_sm_event_t event, void *p_data) +{ + esp_a2d_cb_param_t param; + + BTC_TRACE_DEBUG("%s event: %s flags %x\n", __FUNCTION__, + dump_av_sm_event_name(event), btc_av_cb.flags); + + switch (event) { + case BTC_SM_ENTER_EVT: + /* inform the application that we are entering connecting state */ + btc_report_connection_state(ESP_A2D_CONNECTION_STATE_CONNECTING, &(btc_av_cb.peer_bda), 0); + break; + + case BTC_SM_EXIT_EVT: + break; + + case BTA_AV_REJECT_EVT: + BTC_TRACE_WARNING(" Received BTA_AV_REJECT_EVT \n"); + btc_report_connection_state(ESP_A2D_CONNECTION_STATE_DISCONNECTED, &(btc_av_cb.peer_bda), 0); + btc_sm_change_state(btc_av_cb.sm_handle, BTC_AV_STATE_IDLE); + break; + + case BTA_AV_OPEN_EVT: { + tBTA_AV *p_bta_data = (tBTA_AV *)p_data; + esp_a2d_connection_state_t conn_stat; + btc_sm_state_t av_state; + BTC_TRACE_DEBUG("status:%d, edr 0x%x, peer sep %d\n", p_bta_data->open.status, + p_bta_data->open.edr, p_bta_data->open.sep); + + if (p_bta_data->open.status == BTA_AV_SUCCESS) { + btc_av_cb.edr = p_bta_data->open.edr; + btc_av_cb.peer_sep = p_bta_data->open.sep; + + conn_stat = ESP_A2D_CONNECTION_STATE_CONNECTED; + av_state = BTC_AV_STATE_OPENED; + } else { + BTC_TRACE_WARNING("BTA_AV_OPEN_EVT::FAILED status: %d\n", p_bta_data->open.status); + + conn_stat = ESP_A2D_CONNECTION_STATE_DISCONNECTED; + av_state = BTC_AV_STATE_IDLE; + } + /* inform the application of the event */ + btc_report_connection_state(conn_stat, &(btc_av_cb.peer_bda), 0); + /* change state to open/idle based on the status */ + btc_sm_change_state(btc_av_cb.sm_handle, av_state); + + if (btc_av_cb.peer_sep == AVDT_TSEP_SNK) { + /* if queued PLAY command, send it now */ + /* necessary to add this? + btc_rc_check_handle_pending_play(p_bta_data->open.bd_addr, + (p_bta_data->open.status == BTA_AV_SUCCESS)); + */ + } else if (btc_av_cb.peer_sep == AVDT_TSEP_SRC && + (p_bta_data->open.status == BTA_AV_SUCCESS)) { + /* Bring up AVRCP connection too if AVRC Initialized */ + if(g_av_with_rc) { + BTA_AvOpenRc(btc_av_cb.bta_handle); + } else { + BTC_TRACE_WARNING("AVRC not Init, not using it."); + } + } + btc_queue_advance(); + } break; + + case BTC_AV_SINK_CONFIG_REQ_EVT: { + if (btc_av_cb.peer_sep == AVDT_TSEP_SRC) { + esp_a2d_cb_param_t param; + memcpy(param.audio_cfg.remote_bda, &btc_av_cb.peer_bda, sizeof(esp_bd_addr_t)); + memcpy(¶m.audio_cfg.mcc, p_data, sizeof(esp_a2d_mcc_t)); + btc_a2d_cb_to_app(ESP_A2D_AUDIO_CFG_EVT, ¶m); + } + } break; + + case BTC_AV_CONNECT_REQ_EVT: + // Check for device, if same device which moved to opening then ignore callback + if (memcmp ((bt_bdaddr_t *)p_data, &(btc_av_cb.peer_bda), + sizeof(btc_av_cb.peer_bda)) == 0) { + BTC_TRACE_DEBUG("%s: Same device moved to Opening state,ignore Connect Req\n", __func__); + btc_queue_advance(); + break; + } else { + BTC_TRACE_DEBUG("%s: Moved from idle by Incoming Connection request\n", __func__); + btc_report_connection_state(ESP_A2D_CONNECTION_STATE_DISCONNECTED, (bt_bdaddr_t *)p_data, 0); + btc_queue_advance(); + break; + } + + case BTA_AV_PENDING_EVT: + // Check for device, if same device which moved to opening then ignore callback + if (memcmp (((tBTA_AV *)p_data)->pend.bd_addr, &(btc_av_cb.peer_bda), + sizeof(btc_av_cb.peer_bda)) == 0) { + BTC_TRACE_DEBUG("%s: Same device moved to Opening state,ignore Pending Req\n", __func__); + break; + } else { + BTC_TRACE_DEBUG("%s: Moved from idle by outgoing Connection request\n", __func__); + BTA_AvDisconnect(((tBTA_AV *)p_data)->pend.bd_addr); + break; + } + + CHECK_RC_EVENT(event, p_data); + + case BTA_AV_SNK_PSC_CFG_EVT: +#if BTC_AV_SINK_INCLUDED + param.a2d_psc_cfg_stat.psc_mask = ((tBTA_AV *)p_data)->psc.psc_mask; + btc_a2d_cb_to_app(ESP_A2D_SNK_PSC_CFG_EVT, ¶m); +#endif /* BTC_AV_SINK_INCLUDED */ + break; + case BTA_AV_SET_DELAY_VALUE_EVT: +#if BTC_AV_SINK_INCLUDED + param.a2d_set_delay_value_stat.delay_value = ((tBTA_AV *)p_data)->delay.delay_value; + param.a2d_set_delay_value_stat.set_state = ((tBTA_AV *)p_data)->delay.status; + btc_a2d_cb_to_app(ESP_A2D_SNK_SET_DELAY_VALUE_EVT, ¶m); +#endif /* BTC_AV_SINK_INCLUDED */ + break; + + case BTA_AV_GET_DELAY_VALUE_EVT: +#if BTC_AV_SINK_INCLUDED + param.a2d_get_delay_value_stat.delay_value = ((tBTA_AV *)p_data)->delay.delay_value; + btc_a2d_cb_to_app(ESP_A2D_SNK_GET_DELAY_VALUE_EVT, ¶m); +#endif /* BTC_AV_SINK_INCLUDED */ + break; + + default: + BTC_TRACE_WARNING("%s : unhandled event:%s\n", __FUNCTION__, dump_av_sm_event_name(event)); + return FALSE; + } + return TRUE; +} + + +/***************************************************************************** +** +** Function btc_av_state_closing_handler +** +** Description Intermediate state managing events during closing +** of avdtp channel +** +** Returns TRUE if event was processed, FALSE otherwise +** +*******************************************************************************/ + +static BOOLEAN btc_av_state_closing_handler(btc_sm_event_t event, void *p_data) +{ + esp_a2d_cb_param_t param; + + BTC_TRACE_DEBUG("%s event: %s flags %x\n", __FUNCTION__, + dump_av_sm_event_name(event), btc_av_cb.flags); + + switch (event) { + case BTC_SM_ENTER_EVT: +#if BTC_AV_SRC_INCLUDED + if (btc_av_cb.peer_sep == AVDT_TSEP_SNK) { + /* immediately stop transmission of frames */ + btc_a2dp_source_set_tx_flush(TRUE); + /* wait for audioflinger to stop a2dp */ + } +#endif /* BTC_AV_SRC_INCLUDED */ +#if BTC_AV_SINK_INCLUDED + if (btc_av_cb.peer_sep == AVDT_TSEP_SRC) { + btc_a2dp_sink_set_rx_flush(TRUE); + } +#endif /* BTC_AV_SINK_INCLUDED */ + break; + + case BTA_AV_STOP_EVT: +#if BTC_AV_SRC_INCLUDED + if (btc_av_cb.peer_sep == AVDT_TSEP_SNK) { + /* immediately flush any pending tx frames while suspend is pending */ + btc_a2dp_source_set_tx_flush(TRUE); + } +#endif /* BTC_AV_SRC_INCLUDED */ +#if BTC_AV_SINK_INCLUDED + if (btc_av_cb.peer_sep == AVDT_TSEP_SRC) { + btc_a2dp_sink_set_rx_flush(TRUE); + } +#endif /* BTC_AV_SINK_INCLUDED */ + btc_a2dp_on_stopped(NULL); + break; + + case BTC_SM_EXIT_EVT: + break; + + case BTA_AV_CLOSE_EVT: { + tBTA_AV_CLOSE *close = (tBTA_AV_CLOSE *)p_data; + /* inform the application that we are disconnecting */ + btc_report_connection_state(ESP_A2D_CONNECTION_STATE_DISCONNECTED, &(btc_av_cb.peer_bda), close->disc_rsn); + btc_sm_change_state(btc_av_cb.sm_handle, BTC_AV_STATE_IDLE); + break; + } + + /* Handle the RC_CLOSE event for the cleanup */ + case BTA_AV_RC_CLOSE_EVT: + btc_rc_handler(event, (tBTA_AV *)p_data); + break; + + case BTA_AV_SNK_PSC_CFG_EVT: + break; + case BTA_AV_SET_DELAY_VALUE_EVT: +#if BTC_AV_SINK_INCLUDED + param.a2d_set_delay_value_stat.delay_value = ((tBTA_AV *)p_data)->delay.delay_value; + param.a2d_set_delay_value_stat.set_state = ((tBTA_AV *)p_data)->delay.status; + btc_a2d_cb_to_app(ESP_A2D_SNK_SET_DELAY_VALUE_EVT, ¶m); +#endif /* BTC_AV_SINK_INCLUDED */ + break; + + case BTA_AV_GET_DELAY_VALUE_EVT: +#if BTC_AV_SINK_INCLUDED + param.a2d_get_delay_value_stat.delay_value = ((tBTA_AV *)p_data)->delay.delay_value; + btc_a2d_cb_to_app(ESP_A2D_SNK_GET_DELAY_VALUE_EVT, ¶m); +#endif /* BTC_AV_SINK_INCLUDED */ + break; + + default: + BTC_TRACE_WARNING("%s : unhandled event:%s\n", __FUNCTION__, dump_av_sm_event_name(event)); + return FALSE; + } + return TRUE; +} + +/***************************************************************************** +** +** Function btc_av_state_opened_handler +** +** Description Handles AV events while AVDTP is in OPEN state +** +** Returns TRUE if event was processed, FALSE otherwise +** +*******************************************************************************/ +static BOOLEAN btc_av_state_opened_handler(btc_sm_event_t event, void *p_data) +{ + esp_a2d_cb_param_t param; + tBTA_AV *p_av = (tBTA_AV *)p_data; + + BTC_TRACE_DEBUG("%s event: %s flags %x\n", __FUNCTION__, + dump_av_sm_event_name(event), btc_av_cb.flags); + + if ( (event == BTA_AV_REMOTE_CMD_EVT) && (btc_av_cb.flags & BTC_AV_FLAG_REMOTE_SUSPEND) && + (p_av->remote_cmd.rc_id == BTA_AV_RC_PLAY) ) { + BTC_TRACE_DEBUG("%s: Resetting remote suspend flag on RC PLAY\n", __FUNCTION__); + btc_av_cb.flags &= ~BTC_AV_FLAG_REMOTE_SUSPEND; + } + + switch (event) { + case BTC_SM_ENTER_EVT: + btc_av_cb.flags &= ~BTC_AV_FLAG_PENDING_STOP; + btc_av_cb.flags &= ~BTC_AV_FLAG_PENDING_START; + break; + + case BTC_SM_EXIT_EVT: + btc_av_cb.flags &= ~BTC_AV_FLAG_PENDING_START; + break; + + case BTC_AV_START_STREAM_REQ_EVT: +#if BTC_AV_SRC_INCLUDED + if (btc_av_cb.peer_sep != AVDT_TSEP_SRC) { + btc_a2dp_source_setup_codec(); + } +#endif /* BTC_AV_SRC_INCLUDED */ + BTA_AvStart(); + btc_av_cb.flags |= BTC_AV_FLAG_PENDING_START; + break; + + case BTA_AV_START_EVT: { + BTC_TRACE_DEBUG("BTA_AV_START_EVT status %d, suspending %d, init %d\n", + p_av->start.status, p_av->start.suspending, p_av->start.initiator); + + if ((p_av->start.status == BTA_SUCCESS) && (p_av->start.suspending == TRUE)) { + return TRUE; + } +#if BTC_AV_SRC_INCLUDED + /* if remote tries to start a2dp when DUT is a2dp source + * then suspend. In case a2dp is sink and call is active + * then disconnect the AVDTP channel + */ + if (!(btc_av_cb.flags & BTC_AV_FLAG_PENDING_START)) { + if (btc_av_cb.peer_sep == AVDT_TSEP_SNK) { + BTC_TRACE_EVENT("%s: trigger suspend as remote initiated!!", __FUNCTION__); + btc_dispatch_sm_event(BTC_AV_SUSPEND_STREAM_REQ_EVT, NULL, 0); + } + } + /* In case peer is A2DP SRC we do not want to ack commands on UIPC*/ + if (btc_av_cb.peer_sep == AVDT_TSEP_SNK) { + if (btc_a2dp_on_started(&p_av->start, + ((btc_av_cb.flags & BTC_AV_FLAG_PENDING_START) != 0))) { + /* only clear pending flag after acknowledgement */ + btc_av_cb.flags &= ~BTC_AV_FLAG_PENDING_START; + } + } +#endif /* BTC_AV_SRC_INCLUDED */ + /* remain in open state if status failed */ + if (p_av->start.status != BTA_AV_SUCCESS) { + return FALSE; + } +#if BTC_AV_SINK_INCLUDED + if (btc_av_cb.peer_sep == AVDT_TSEP_SRC) { + btc_a2dp_sink_set_rx_flush(FALSE); /* remove flush state, ready for streaming*/ + } +#endif /* BTC_AV_SINK_INCLUDED */ +#if BTC_AV_SRC_INCLUDED + /* change state to started, send acknowledgement if start is pending */ + if (btc_av_cb.flags & BTC_AV_FLAG_PENDING_START) { + if (btc_av_cb.peer_sep == AVDT_TSEP_SNK) { + btc_a2dp_on_started(NULL, TRUE); + } + /* pending start flag will be cleared when exit current state */ + } +#endif /* BTC_AV_SRC_INCLUDED */ + /* wait for audio path to open */ + btc_a2dp_control_datapath_ctrl(BTC_AV_DATAPATH_OPEN_EVT); + + btc_sm_change_state(btc_av_cb.sm_handle, BTC_AV_STATE_STARTED); + + } break; + + case BTC_AV_DISCONNECT_REQ_EVT: + BTA_AvClose(btc_av_cb.bta_handle); + if (btc_av_cb.peer_sep == AVDT_TSEP_SRC && g_av_with_rc == true) { + BTA_AvCloseRc(btc_av_cb.bta_handle); + } + + /* inform the application that we are disconnecting */ + btc_report_connection_state(ESP_A2D_CONNECTION_STATE_DISCONNECTING, &(btc_av_cb.peer_bda), 0); + break; + + case BTA_AV_CLOSE_EVT: { + /* avdtp link is closed */ + btc_a2dp_on_stopped(NULL); + tBTA_AV_CLOSE *close = (tBTA_AV_CLOSE *)p_data; + /* inform the application that we are disconnected */ + btc_report_connection_state(ESP_A2D_CONNECTION_STATE_DISCONNECTED, &(btc_av_cb.peer_bda), + close->disc_rsn); + + if (btc_av_cb.flags & BTC_AV_FLAG_PENDING_START) { + btc_a2dp_control_command_ack(ESP_A2D_MEDIA_CTRL_ACK_FAILURE); + /* pending start flag will be cleared when exit current state */ + } + + /* change state to idle, send acknowledgement if start is pending */ + btc_sm_change_state(btc_av_cb.sm_handle, BTC_AV_STATE_IDLE); + + if (g_a2dp_source_ongoing_deinit) { + clean_up(BTA_A2DP_SOURCE_SERVICE_ID); + } else if (g_a2dp_sink_ongoing_deinit) { + clean_up(BTA_A2DP_SINK_SERVICE_ID); + } + break; + } + + case BTA_AV_RECONFIG_EVT: + if ((btc_av_cb.flags & BTC_AV_FLAG_PENDING_START) && + (p_av->reconfig.status == BTA_AV_SUCCESS)) { + BTC_TRACE_WARNING("reconfig done BTA_AVstart()\n"); + BTA_AvStart(); + } else if (btc_av_cb.flags & BTC_AV_FLAG_PENDING_START) { + btc_av_cb.flags &= ~BTC_AV_FLAG_PENDING_START; + btc_a2dp_control_command_ack(ESP_A2D_MEDIA_CTRL_ACK_FAILURE); + } + break; + + case BTC_AV_CONNECT_REQ_EVT: + if (memcmp (&((btc_av_connect_req_t *)p_data)->target_bda, &(btc_av_cb.peer_bda), + sizeof(btc_av_cb.peer_bda)) == 0) { + BTC_TRACE_DEBUG("%s: Ignore BTC_AVCONNECT_REQ_EVT for same device\n", __func__); + } else { + BTC_TRACE_DEBUG("%s: Moved to opened by Other Incoming Conn req\n", __func__); + btc_report_connection_state(ESP_A2D_CONNECTION_STATE_DISCONNECTED, + (bt_bdaddr_t *)p_data, ESP_A2D_DISC_RSN_NORMAL); + } + btc_queue_advance(); + break; + + CHECK_RC_EVENT(event, p_data); + + case BTA_AV_SNK_PSC_CFG_EVT: + break; + case BTA_AV_SET_DELAY_VALUE_EVT: +#if BTC_AV_SINK_INCLUDED + param.a2d_set_delay_value_stat.delay_value = ((tBTA_AV *)p_data)->delay.delay_value; + param.a2d_set_delay_value_stat.set_state = ((tBTA_AV *)p_data)->delay.status; + btc_a2d_cb_to_app(ESP_A2D_SNK_SET_DELAY_VALUE_EVT, ¶m); +#endif /* BTC_AV_SINK_INCLUDED */ + break; + + case BTA_AV_GET_DELAY_VALUE_EVT: +#if BTC_AV_SINK_INCLUDED + param.a2d_get_delay_value_stat.delay_value = ((tBTA_AV *)p_data)->delay.delay_value; + btc_a2d_cb_to_app(ESP_A2D_SNK_GET_DELAY_VALUE_EVT, ¶m); +#endif /* BTC_AV_SINK_INCLUDED */ + break; + + default: + BTC_TRACE_WARNING("%s : unhandled event:%s\n", __FUNCTION__, + dump_av_sm_event_name(event)); + return FALSE; + + } + return TRUE; +} + +/***************************************************************************** +** +** Function btc_av_state_started_handler +** +** Description Handles AV events while A2DP stream is started +** +** Returns TRUE if event was processed, FALSE otherwise +** +*******************************************************************************/ + +static BOOLEAN btc_av_state_started_handler(btc_sm_event_t event, void *p_data) +{ + tBTA_AV *p_av = (tBTA_AV *)p_data; + esp_a2d_cb_param_t param; + + BTC_TRACE_DEBUG("%s event: %s flags %x\n", __FUNCTION__, + dump_av_sm_event_name(event), btc_av_cb.flags); + + switch (event) { + case BTC_SM_ENTER_EVT: + + /* we are again in started state, clear any remote suspend flags */ + btc_av_cb.flags &= ~BTC_AV_FLAG_REMOTE_SUSPEND; + + btc_report_audio_state(ESP_A2D_AUDIO_STATE_STARTED, &(btc_av_cb.peer_bda)); + + /* increase the a2dp consumer task priority temporarily when start + ** audio playing, to avoid overflow the audio packet queue. */ + // adjust_priority_a2dp(TRUE); + + break; + + case BTC_SM_EXIT_EVT: + /* restore the a2dp consumer task priority when stop audio playing. */ + // adjust_priority_a2dp(FALSE); + + break; + + case BTC_AV_START_STREAM_REQ_EVT: +#if BTC_AV_SRC_INCLUDED + /* we were remotely started, just ack back the local request */ + if (btc_av_cb.peer_sep == AVDT_TSEP_SNK) { + btc_a2dp_on_started(NULL, TRUE); + } +#endif /* BTC_AV_SRC_INCLUDED */ + break; + + case BTC_AV_SUSPEND_STREAM_REQ_EVT: + + /* set pending flag to ensure btc task is not trying to restart + stream while suspend is in progress */ + btc_av_cb.flags |= BTC_AV_FLAG_LOCAL_SUSPEND_PENDING; + + /* if we were remotely suspended but suspend locally, local suspend + always overrides */ + btc_av_cb.flags &= ~BTC_AV_FLAG_REMOTE_SUSPEND; +#if BTC_AV_SRC_INCLUDED + if (btc_av_cb.peer_sep == AVDT_TSEP_SNK) { + /* immediately stop transmission of frames while suspend is pending */ + btc_a2dp_source_set_tx_flush(TRUE); + } +#endif /* BTC_AV_SRC_INCLUDED */ +#if BTC_AV_SINK_INCLUDED + if (btc_av_cb.peer_sep == AVDT_TSEP_SRC) { + btc_a2dp_sink_set_rx_flush(TRUE); + btc_a2dp_on_stopped(NULL); + } +#endif /* BTC_AV_SINK_INCLUDED */ + BTA_AvStop(TRUE); + break; + + case BTC_AV_DISCONNECT_REQ_EVT: + + /* request avdtp to close */ + BTA_AvClose(btc_av_cb.bta_handle); + if (btc_av_cb.peer_sep == AVDT_TSEP_SRC && g_av_with_rc == true) { + BTA_AvCloseRc(btc_av_cb.bta_handle); + } + + /* inform the application that we are disconnecting */ + btc_report_connection_state(ESP_A2D_CONNECTION_STATE_DISCONNECTING, &(btc_av_cb.peer_bda), 0); + + /* wait in closing state until fully closed */ + btc_sm_change_state(btc_av_cb.sm_handle, BTC_AV_STATE_CLOSING); + break; + + case BTA_AV_SUSPEND_EVT: + + BTC_TRACE_DEBUG("BTA_AV_SUSPEND_EVT status %d, init %d\n", + p_av->suspend.status, p_av->suspend.initiator); + + /* a2dp suspended, stop media task until resumed */ + btc_a2dp_on_suspended(&p_av->suspend); + + /* if not successful, remain in current state */ + if (p_av->suspend.status != BTA_AV_SUCCESS) { + btc_av_cb.flags &= ~BTC_AV_FLAG_LOCAL_SUSPEND_PENDING; +#if BTC_AV_SRC_INCLUDED + if (btc_av_cb.peer_sep == AVDT_TSEP_SNK) { + /* suspend failed, reset back tx flush state */ + btc_a2dp_source_set_tx_flush(FALSE); + } +#endif /* BTC_AV_SRC_INCLUDED */ + return FALSE; + } + + if (p_av->suspend.initiator != TRUE) { + /* remote suspend, notify HAL and await audioflinger to + suspend/stop stream */ + + /* set remote suspend flag to block media task from restarting + stream only if we did not already initiate a local suspend */ + if ((btc_av_cb.flags & BTC_AV_FLAG_LOCAL_SUSPEND_PENDING) == 0) { + btc_av_cb.flags |= BTC_AV_FLAG_REMOTE_SUSPEND; + } + + } + btc_report_audio_state(ESP_A2D_AUDIO_STATE_SUSPEND, &(btc_av_cb.peer_bda)); + + btc_sm_change_state(btc_av_cb.sm_handle, BTC_AV_STATE_OPENED); + + /* suspend completed and state changed, clear pending status */ + btc_av_cb.flags &= ~BTC_AV_FLAG_LOCAL_SUSPEND_PENDING; + break; + + case BTA_AV_STOP_EVT: + + btc_av_cb.flags |= BTC_AV_FLAG_PENDING_STOP; + btc_a2dp_on_stopped(&p_av->suspend); + + btc_report_audio_state(ESP_A2D_AUDIO_STATE_SUSPEND, &(btc_av_cb.peer_bda)); + + /* if stop was successful, change state to open */ + if (p_av->suspend.status == BTA_AV_SUCCESS) { + btc_sm_change_state(btc_av_cb.sm_handle, BTC_AV_STATE_OPENED); + } + break; + + case BTA_AV_CLOSE_EVT: + btc_av_cb.flags |= BTC_AV_FLAG_PENDING_STOP; + + /* avdtp link is closed */ + btc_a2dp_on_stopped(NULL); + tBTA_AV_CLOSE *close = (tBTA_AV_CLOSE *)p_data; + /* inform the application that we are disconnected */ + btc_report_connection_state(ESP_A2D_CONNECTION_STATE_DISCONNECTED, &(btc_av_cb.peer_bda), + close->disc_rsn); + btc_sm_change_state(btc_av_cb.sm_handle, BTC_AV_STATE_IDLE); + + if (g_a2dp_source_ongoing_deinit) { + clean_up(BTA_A2DP_SOURCE_SERVICE_ID); + } else if (g_a2dp_sink_ongoing_deinit) { + clean_up(BTA_A2DP_SINK_SERVICE_ID); + } + break; + + CHECK_RC_EVENT(event, p_data); + + case BTA_AV_SNK_PSC_CFG_EVT: + break; + case BTA_AV_SET_DELAY_VALUE_EVT: +#if BTC_AV_SINK_INCLUDED + param.a2d_set_delay_value_stat.delay_value = ((tBTA_AV *)p_data)->delay.delay_value; + param.a2d_set_delay_value_stat.set_state = ((tBTA_AV *)p_data)->delay.status; + btc_a2d_cb_to_app(ESP_A2D_SNK_SET_DELAY_VALUE_EVT, ¶m); +#endif /* BTC_AV_SINK_INCLUDED */ + break; + + case BTA_AV_GET_DELAY_VALUE_EVT: +#if BTC_AV_SINK_INCLUDED + param.a2d_get_delay_value_stat.delay_value = ((tBTA_AV *)p_data)->delay.delay_value; + btc_a2d_cb_to_app(ESP_A2D_SNK_GET_DELAY_VALUE_EVT, ¶m); +#endif /* BTC_AV_SINK_INCLUDED */ + break; + + default: + BTC_TRACE_WARNING("%s : unhandled event:%s\n", __FUNCTION__, + dump_av_sm_event_name(event)); + return FALSE; + + } + return TRUE; +} + +/***************************************************************************** +** Local event handlers +******************************************************************************/ + +void btc_av_event_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src) +{ + tBTA_AV *av_src = (tBTA_AV *)p_src; + tBTA_AV *av_dest = (tBTA_AV *)p_dest; + + // First copy the structure + memcpy(p_dest, p_src, sizeof(tBTA_AV)); + + switch (msg->act) { + case BTA_AV_META_MSG_EVT: + if (av_src->meta_msg.p_data && av_src->meta_msg.len) { + av_dest->meta_msg.p_data = osi_calloc(av_src->meta_msg.len); + assert(av_dest->meta_msg.p_data); + memcpy(av_dest->meta_msg.p_data, av_src->meta_msg.p_data, av_src->meta_msg.len); + } + + if (av_src->meta_msg.p_msg) { + av_dest->meta_msg.p_msg = osi_calloc(sizeof(tAVRC_MSG)); + assert(av_dest->meta_msg.p_msg); + memcpy(av_dest->meta_msg.p_msg, av_src->meta_msg.p_msg, sizeof(tAVRC_MSG)); + + if (av_src->meta_msg.p_msg->vendor.p_vendor_data && + av_src->meta_msg.p_msg->vendor.vendor_len) { + av_dest->meta_msg.p_msg->vendor.p_vendor_data = osi_calloc( + av_src->meta_msg.p_msg->vendor.vendor_len); + assert(av_dest->meta_msg.p_msg->vendor.p_vendor_data); + memcpy(av_dest->meta_msg.p_msg->vendor.p_vendor_data, + av_src->meta_msg.p_msg->vendor.p_vendor_data, + av_src->meta_msg.p_msg->vendor.vendor_len); + } + } + break; + + default: + break; + } +} + +static void btc_av_event_free_data(btc_msg_t *msg) +{ + switch (msg->act) { + case BTA_AV_META_MSG_EVT: { + tBTA_AV *av = (tBTA_AV *)msg->arg; + if (av->meta_msg.p_data) { + osi_free(av->meta_msg.p_data); + } + + if (av->meta_msg.p_msg) { + if (av->meta_msg.p_msg->vendor.p_vendor_data) { + osi_free(av->meta_msg.p_msg->vendor.p_vendor_data); + } + osi_free(av->meta_msg.p_msg); + } + } + break; + + default: + break; + } +} + +/******************************************************************************* +** +** Function btc_av_init +** +** Description Initializes btc AV if not already done +** +** Returns bt_status_t +** +*******************************************************************************/ +static bt_status_t btc_av_init(int service_id) +{ + +#if A2D_DYNAMIC_MEMORY == TRUE + if (btc_av_cb_ptr != NULL) { + return BT_STATUS_FAIL; + } + + if ((btc_av_cb_ptr = (btc_av_cb_t *)osi_malloc(sizeof(btc_av_cb_t))) == NULL) { + APPL_TRACE_ERROR("%s malloc failed!", __func__); + return BT_STATUS_NOMEM; + } + memset((void *)btc_av_cb_ptr, 0, sizeof(btc_av_cb_t)); +#endif + + if (btc_av_cb.sm_handle == NULL) { + btc_av_cb.service_id = service_id; + bool stat = false; + if (service_id == BTA_A2DP_SOURCE_SERVICE_ID) { +#if BTC_AV_SRC_INCLUDED + stat = btc_a2dp_source_startup(); +#endif + } else if (service_id == BTA_A2DP_SINK_SERVICE_ID) { +#if BTC_AV_SINK_INCLUDED + stat = btc_a2dp_sink_startup(); +#endif + } + + if (!stat) { +#if A2D_DYNAMIC_MEMORY == TRUE + osi_free(btc_av_cb_ptr); + btc_av_cb_ptr = NULL; +#endif + g_a2dp_on_init = false; + g_a2dp_on_deinit = true; + g_a2dp_source_ongoing_deinit = false; + g_a2dp_sink_ongoing_deinit = false; + goto av_init_fail; + } + + /* Also initialize the AV state machine */ + btc_av_cb.sm_handle = + btc_sm_init((const btc_sm_handler_t *)btc_av_state_handlers, BTC_AV_STATE_IDLE); + + if (service_id == BTA_A2DP_SINK_SERVICE_ID) { + btc_dm_enable_service(BTA_A2DP_SINK_SERVICE_ID); + } else { + btc_dm_enable_service(BTA_A2DP_SOURCE_SERVICE_ID); + } + + btc_a2dp_on_init(); + g_a2dp_on_init = true; + g_a2dp_on_deinit = false; + g_a2dp_source_ongoing_deinit = false; + g_a2dp_sink_ongoing_deinit = false; + + esp_a2d_cb_param_t param; + memset(¶m, 0, sizeof(esp_a2d_cb_param_t)); + param.a2d_prof_stat.init_state = ESP_A2D_INIT_SUCCESS; + btc_a2d_cb_to_app(ESP_A2D_PROF_STATE_EVT, ¶m); + return BT_STATUS_SUCCESS; + } + +av_init_fail: + return BT_STATUS_FAIL; +} + +/******************************************************************************* +** +** Function connect +** +** Description Establishes the AV signalling channel with the remote headset +** +** Returns bt_status_t +** +*******************************************************************************/ + +static bt_status_t connect_int(bt_bdaddr_t *bd_addr, uint16_t uuid) +{ + btc_av_connect_req_t connect_req; + memcpy(&connect_req.target_bda, bd_addr, sizeof(bt_bdaddr_t)); + connect_req.uuid = uuid; + BTC_TRACE_DEBUG("%s\n", __FUNCTION__); + + btc_sm_dispatch(btc_av_cb.sm_handle, BTC_AV_CONNECT_REQ_EVT, (char *)&connect_req); + + return BT_STATUS_SUCCESS; +} + +/******************************************************************************* +** +** Function clean_up +** +** Description Shuts down the AV interface and does the cleanup +** +** Returns None +** +*******************************************************************************/ +static void clean_up(int service_id) +{ + BTC_TRACE_DEBUG("%s\n", __FUNCTION__); + + if (service_id == BTA_A2DP_SOURCE_SERVICE_ID) { +#if BTC_AV_SRC_INCLUDED + btc_a2dp_source_shutdown(); + if (btc_av_cb.tle_av_open_on_rc) { + osi_alarm_free(btc_av_cb.tle_av_open_on_rc); + btc_av_cb.tle_av_open_on_rc = NULL; + } +#endif /* BTC_AV_SRC_INCLUDED */ + btc_dm_disable_service(BTA_A2DP_SOURCE_SERVICE_ID); + } + + if (service_id == BTA_A2DP_SINK_SERVICE_ID) { +#if BTC_AV_SINK_INCLUDED + btc_a2dp_sink_shutdown(); +#endif /* BTC_AV_SINK_INCLUDED */ + btc_dm_disable_service(BTA_A2DP_SINK_SERVICE_ID); + } + + /* Also shut down the AV state machine */ + btc_sm_shutdown(btc_av_cb.sm_handle); + btc_av_cb.sm_handle = NULL; + +#if A2D_DYNAMIC_MEMORY == TRUE + osi_free(btc_av_cb_ptr); + btc_av_cb_ptr = NULL; +#endif + g_a2dp_on_init = false; + g_a2dp_on_deinit = true; + g_a2dp_source_ongoing_deinit = false; + g_a2dp_sink_ongoing_deinit = false; + + esp_a2d_cb_param_t param; + memset(¶m, 0, sizeof(esp_a2d_cb_param_t)); + param.a2d_prof_stat.init_state = ESP_A2D_DEINIT_SUCCESS; + btc_a2d_cb_to_app(ESP_A2D_PROF_STATE_EVT, ¶m); +} + +/******************************************************************************* +** +** Function btc_av_get_sm_handle +** +** Description Fetches current av SM handle +** +** Returns None +** +*******************************************************************************/ + +btc_sm_handle_t btc_av_get_sm_handle(void) +{ + return btc_av_cb.sm_handle; +} + +/******************************************************************************* +** +** Function btc_av_stream_ready +** +** Description Checks whether AV is ready for starting a stream +** +** Returns None +** +*******************************************************************************/ + +BOOLEAN btc_av_stream_ready(void) +{ + btc_sm_state_t state = btc_sm_get_state(btc_av_cb.sm_handle); + + BTC_TRACE_DEBUG("btc_av_stream_ready : sm hdl %d, state %d, flags %x\n", + (int)btc_av_cb.sm_handle, state, btc_av_cb.flags); + + /* check if we are remotely suspended or stop is pending */ + if (btc_av_cb.flags & BTC_AV_FLAG_PENDING_STOP) { + return FALSE; + } + + return (state == BTC_AV_STATE_OPENED); +} + +/******************************************************************************* +** +** Function btc_av_stream_started_ready +** +** Description Checks whether AV ready for media start in streaming state +** +** Returns None +** +*******************************************************************************/ + +BOOLEAN btc_av_stream_started_ready(void) +{ + btc_sm_state_t state = btc_sm_get_state(btc_av_cb.sm_handle); + + BTC_TRACE_DEBUG("btc_av_stream_started : sm hdl %d, state %d, flags %x\n", + (int)btc_av_cb.sm_handle, state, btc_av_cb.flags); + + /* disallow media task to start if we have pending actions */ + if (btc_av_cb.flags & (BTC_AV_FLAG_LOCAL_SUSPEND_PENDING + | BTC_AV_FLAG_PENDING_STOP)) { + return FALSE; + } + + return (state == BTC_AV_STATE_STARTED); +} + +/******************************************************************************* +** +** Function btc_dispatch_sm_event +** +** Description Send event to AV statemachine +** +** Returns None +** +*******************************************************************************/ + +/* used to pass events to AV statemachine from other tasks */ +void btc_dispatch_sm_event(btc_av_sm_event_t event, void *p_data, int len) +{ + btc_msg_t msg; + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_A2DP; + msg.act = event; + btc_transfer_context(&msg, p_data, len, NULL, NULL); +} + +static void bte_av_callback(tBTA_AV_EVT event, tBTA_AV *p_data) +{ + bt_status_t stat; + btc_msg_t msg; + + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_A2DP; + msg.act = (uint8_t) event; + stat = btc_transfer_context(&msg, p_data, sizeof(tBTA_AV), + btc_av_event_deep_copy, btc_av_event_free_data); + + if (stat) { + BTC_TRACE_ERROR("%s transfer failed\n", __func__); + } +} + +#if BTC_AV_SINK_INCLUDED +static void bte_av_media_callback(tBTA_AV_EVT event, tBTA_AV_MEDIA *p_data) +{ + btc_sm_state_t state; + UINT8 que_len; + tA2D_STATUS a2d_status; + tA2D_SBC_CIE sbc_cie; + + if (event == BTA_AV_MEDIA_DATA_EVT) { /* Switch to BTC_MEDIA context */ + state = btc_sm_get_state(btc_av_cb.sm_handle); + if ( (state == BTC_AV_STATE_STARTED) || /* send SBC packets only in Started State */ + (state == BTC_AV_STATE_OPENED) ) { + que_len = btc_a2dp_sink_enque_buf((BT_HDR *)p_data); + BTC_TRACE_DEBUG(" Packets in Que %d\n", que_len); + } else { + return; + } + } + + if (event == BTA_AV_MEDIA_SINK_CFG_EVT) { + /* send a command to BT Media Task */ + btc_a2dp_sink_reset_decoder((UINT8 *)p_data); + + /* currently only supportes SBC */ + a2d_status = A2D_ParsSbcInfo(&sbc_cie, (UINT8 *)p_data, FALSE); + if (a2d_status == A2D_SUCCESS) { + btc_msg_t msg; + btc_av_args_t arg; + + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_A2DP; + msg.act = BTC_AV_SINK_CONFIG_REQ_EVT; + + memset(&arg, 0, sizeof(btc_av_args_t)); + arg.mcc.type = ESP_A2D_MCT_SBC; + memcpy(arg.mcc.cie.sbc, (uint8_t *)p_data + 3, ESP_A2D_CIE_LEN_SBC); + btc_transfer_context(&msg, &arg, sizeof(btc_av_args_t), NULL, NULL); + } else { + BTC_TRACE_ERROR("ERROR dump_codec_info A2D_ParsSbcInfo fail:%d\n", a2d_status); + } + } + UNUSED(que_len); +} +#else +static void bte_av_media_callback(tBTA_AV_EVT event, tBTA_AV_MEDIA *p_data) +{ + UNUSED(event); + UNUSED(p_data); + BTC_TRACE_WARNING("%s : event %u\n", __func__, event); +} +#endif + +/******************************************************************************* +** +** Function btc_av_execute_service +** +** Description Initializes/Shuts down the service +** +** Returns BT_STATUS_SUCCESS on success, BT_STATUS_FAIL otherwise +** +*******************************************************************************/ +bt_status_t btc_av_execute_service(BOOLEAN b_enable, UINT8 tsep) +{ + if (b_enable) { + /* TODO: Removed BTA_SEC_AUTHORIZE since the Java/App does not + * handle this request in order to allow incoming connections to succeed. + * We need to put this back once support for this is added */ + + /* Added BTA_AV_FEAT_NO_SCO_SSPD - this ensures that the BTA does not + * auto-suspend av streaming on AG events(SCO or Call). The suspend shall + * be initiated by the app/audioflinger layers */ + if (g_av_with_rc) { + BTC_TRACE_WARNING("A2DP Enable with AVRC") + BTA_AvEnable(BTA_SEC_AUTHENTICATE, BTA_AV_FEAT_NO_SCO_SSPD | + BTA_AV_FEAT_RCTG | BTA_AV_FEAT_METADATA | BTA_AV_FEAT_VENDOR | + BTA_AV_FEAT_RCCT | BTA_AV_FEAT_ADV_CTRL | BTA_AV_FEAT_DELAY_RPT, + bte_av_callback); + BTA_AvRegister(BTA_AV_CHNL_AUDIO, BTC_AV_SERVICE_NAME, 0, bte_av_media_callback, &bta_av_a2d_cos, &bta_avrc_cos, tsep); + } else { + BTC_TRACE_WARNING("A2DP Enable without AVRC") + BTA_AvEnable(BTA_SEC_AUTHENTICATE, BTA_AV_FEAT_NO_SCO_SSPD | BTA_AV_FEAT_DELAY_RPT, bte_av_callback); + BTA_AvRegister(BTA_AV_CHNL_AUDIO, BTC_AV_SERVICE_NAME, 0, bte_av_media_callback, &bta_av_a2d_cos, NULL, tsep); + } + } else { + BTA_AvDeregister(btc_av_cb.bta_handle); + BTA_AvDisable(); + } + return BT_STATUS_SUCCESS; +} + +/******************************************************************************* +** +** Function btc_av_source_execute_service +** +** Description Initializes/Shuts down the A2DP source service +** +** Returns BT_STATUS_SUCCESS on success, BT_STATUS_FAIL otherwise +** +*******************************************************************************/ +bt_status_t btc_av_source_execute_service(BOOLEAN b_enable) +{ + return btc_av_execute_service(b_enable, AVDT_TSEP_SRC); +} + +/******************************************************************************* +** +** Function btc_av_sink_execute_service +** +** Description Initializes/Shuts down the service +** +** Returns BT_STATUS_SUCCESS on success, BT_STATUS_FAIL otherwise +** +*******************************************************************************/ +bt_status_t btc_av_sink_execute_service(BOOLEAN b_enable) +{ + bt_status_t ret = btc_av_execute_service(b_enable, AVDT_TSEP_SNK); + if (ret != BT_STATUS_SUCCESS) { + return ret; + } +#if (BTA_AV_SINK_INCLUDED == TRUE) + BTA_AvEnable_Sink(b_enable); +#endif + return BT_STATUS_SUCCESS; +} + +/******************************************************************************* +** +** Function btc_av_is_connected +** +** Description Checks if av has a connected sink +** +** Returns BOOLEAN +** +*******************************************************************************/ +BOOLEAN btc_av_is_connected(void) +{ + btc_sm_state_t state = btc_sm_get_state(btc_av_cb.sm_handle); + return ((state == BTC_AV_STATE_OPENED) || (state == BTC_AV_STATE_STARTED)); +} + +/******************************************************************************* + * + * Function btc_av_get_service_id + * + * Description Get the current AV service ID. + * + * Returns The stream endpoint type: either BTA_A2DP_SOURCE_SERVICE_ID or + * BTA_A2DP_SINK_SERVICE_ID. + * + ******************************************************************************/ +uint8_t btc_av_get_service_id(void) +{ + return btc_av_cb.service_id; +} + +/******************************************************************************* + * + * Function btc_av_get_peer_sep + * + * Description Get the stream endpoint type. + * + * Returns The stream endpoint type: either AVDT_TSEP_SRC or + * AVDT_TSEP_SNK. + * + ******************************************************************************/ + +uint8_t btc_av_get_peer_sep(void) +{ + return btc_av_cb.peer_sep; +} +/******************************************************************************* +** +** Function btc_av_is_peer_edr +** +** Description Check if the connected a2dp device supports +** EDR or not. Only when connected this function +** will accurately provide a true capability of +** remote peer. If not connected it will always be false. +** +** Returns TRUE if remote device is capable of EDR +** +*******************************************************************************/ +BOOLEAN btc_av_is_peer_edr(void) +{ + BTC_ASSERTC(btc_av_is_connected(), "No active a2dp connection\n", 0); + + if (btc_av_cb.edr) { + return TRUE; + } else { + return FALSE; + } +} + +/****************************************************************************** +** +** Function btc_av_clear_remote_suspend_flag +** +** Description Clears btc_av_cb.flags if BTC_AV_FLAG_REMOTE_SUSPEND is set +** +** Returns void +******************************************************************************/ +void btc_av_clear_remote_suspend_flag(void) +{ + BTC_TRACE_DEBUG("%s: flag :%x\n", __func__, btc_av_cb.flags); + btc_av_cb.flags &= ~BTC_AV_FLAG_REMOTE_SUSPEND; +} + +void btc_a2dp_call_handler(btc_msg_t *msg) +{ + btc_av_args_t *arg = (btc_av_args_t *)(msg->arg); + switch (msg->act) { +#if BTC_AV_SINK_INCLUDED + case BTC_AV_SINK_CONFIG_REQ_EVT: { + btc_sm_dispatch(btc_av_cb.sm_handle, msg->act, (void *)(msg->arg)); + break; + } + case BTC_AV_SINK_API_INIT_EVT: { + btc_a2d_sink_init(); + // todo: callback to application + break; + } + case BTC_AV_SINK_API_DEINIT_EVT: { + btc_a2d_sink_deinit(); + // todo: callback to application + break; + } + case BTC_AV_SINK_API_CONNECT_EVT: { + btc_a2d_sink_connect(&arg->connect); + // todo: callback to application + break; + } + case BTC_AV_SINK_API_DISCONNECT_EVT: { + CHECK_BTAV_INIT(); + btc_av_disconn_req_t disconn_req; + memcpy(&disconn_req.target_bda, &arg->disconn, sizeof(bt_bdaddr_t)); + btc_sm_dispatch(btc_av_cb.sm_handle, BTC_AV_DISCONNECT_REQ_EVT, &disconn_req); + break; + } + case BTC_AV_SINK_API_REG_DATA_CB_EVT: { + btc_a2dp_sink_reg_data_cb(arg->data_cb); + break; + } + case BTC_AV_SINK_API_SET_DELAY_VALUE_EVT: { + btc_a2d_sink_set_delay_value(arg->delay_value); + break; + } + case BTC_AV_SINK_API_GET_DELAY_VALUE_EVT: { + btc_a2d_sink_get_delay_value(); + break; + } +#endif /* BTC_AV_SINK_INCLUDED */ +#if BTC_AV_SRC_INCLUDED + case BTC_AV_SRC_API_INIT_EVT: { + btc_a2d_src_init(); + break; + } + case BTC_AV_SRC_API_DEINIT_EVT: { + btc_a2d_src_deinit(); + break; + } + case BTC_AV_SRC_API_CONNECT_EVT: { + btc_a2d_src_connect(&arg->src_connect); + break; + } + case BTC_AV_SRC_API_DISCONNECT_EVT: { + CHECK_BTAV_INIT(); + btc_av_disconn_req_t disconn_req; + memcpy(&disconn_req.target_bda, &arg->src_disconn, sizeof(bt_bdaddr_t)); + btc_sm_dispatch(btc_av_cb.sm_handle, BTC_AV_DISCONNECT_REQ_EVT, &disconn_req); + break; + } + case BTC_AV_SRC_API_REG_DATA_CB_EVT: { + btc_a2dp_src_reg_data_cb(arg->src_data_cb); + break; + } +#endif /* BTC_AV_SRC_INCLUDED */ + case BTC_AV_API_MEDIA_CTRL_EVT: { + btc_a2dp_control_media_ctrl(arg->ctrl); + break; + } + case BTC_AV_CONNECT_REQ_EVT: + btc_sm_dispatch(btc_av_cb.sm_handle, msg->act, (char *)msg->arg); + break; + // case BTC_AV_DISCONNECT_REQ_EVT: + case BTC_AV_START_STREAM_REQ_EVT: + case BTC_AV_SUSPEND_STREAM_REQ_EVT: { + btc_sm_dispatch(btc_av_cb.sm_handle, msg->act, NULL); + break; + } + default: + BTC_TRACE_WARNING("%s : unhandled event: %d\n", __FUNCTION__, msg->act); + } +} + +void btc_a2dp_cb_handler(btc_msg_t *msg) +{ + btc_sm_dispatch(btc_av_cb.sm_handle, msg->act, (void *)(msg->arg)); + btc_av_event_free_data(msg); +} + +#if BTC_AV_SINK_INCLUDED + +/******************************************************************************* +** +** Function init_sink +** +** Description Initializes the AV interface for sink mode +** +** Returns bt_status_t +** +*******************************************************************************/ +static bt_status_t btc_a2d_sink_init(void) +{ + BTC_TRACE_DEBUG("%s()\n", __func__); + + return btc_av_init(BTA_A2DP_SINK_SERVICE_ID); +} + +static bt_status_t btc_a2d_sink_connect(bt_bdaddr_t *remote_bda) +{ + BTC_TRACE_DEBUG("%s\n", __FUNCTION__); + CHECK_BTAV_INIT(); + + return btc_queue_connect(UUID_SERVCLASS_AUDIO_SINK, remote_bda, connect_int); +} + +static void btc_a2d_sink_set_delay_value(UINT16 delay_value) +{ + esp_a2d_cb_param_t param; + + if (delay_value <= BTC_DELAY_REPORT_DFT_VALUE) { + param.a2d_set_delay_value_stat.delay_value = 0; + param.a2d_set_delay_value_stat.set_state = ESP_A2D_SET_INVALID_PARAMS; + btc_a2d_cb_to_app(ESP_A2D_SNK_SET_DELAY_VALUE_EVT, ¶m); + } else { + BTA_SetDelayValue(delay_value); + } +} + +static void btc_a2d_sink_get_delay_value(void) +{ + BTA_GetDelayValue(); +} + +static void btc_a2d_sink_deinit(void) +{ + g_a2dp_sink_ongoing_deinit = true; + if (btc_av_is_connected()) { + BTA_AvClose(btc_av_cb.bta_handle); + if (btc_av_cb.peer_sep == AVDT_TSEP_SRC && g_av_with_rc == true) { + BTA_AvCloseRc(btc_av_cb.bta_handle); + } + } else { + clean_up(BTA_A2DP_SINK_SERVICE_ID); + } +} + +#endif /* BTC_AV_SINK_INCLUDED */ + +#if BTC_AV_SRC_INCLUDED + +/******************************************************************************* +** +** Function btc_a2d_src_init +** +** Description Initializes the AV interface for source mode +** +** Returns bt_status_t +** +*******************************************************************************/ +static bt_status_t btc_a2d_src_init(void) +{ + BTC_TRACE_DEBUG("%s()\n", __func__); + + return btc_av_init(BTA_A2DP_SOURCE_SERVICE_ID); +} + +static void btc_a2d_src_deinit(void) +{ + g_a2dp_source_ongoing_deinit = true; + if (btc_av_is_connected()) { + BTA_AvClose(btc_av_cb.bta_handle); + if (btc_av_cb.peer_sep == AVDT_TSEP_SNK && g_av_with_rc == true) { + BTA_AvCloseRc(btc_av_cb.bta_handle); + } + } else { + clean_up(BTA_A2DP_SOURCE_SERVICE_ID); + } +} + +static bt_status_t btc_a2d_src_connect(bt_bdaddr_t *remote_bda) +{ + BTC_TRACE_DEBUG("%s\n", __FUNCTION__); + CHECK_BTAV_INIT(); + + return btc_queue_connect(UUID_SERVCLASS_AUDIO_SOURCE, remote_bda, connect_int); +} + +#endif /* BTC_AV_SRC_INCLUDED */ + +#endif /* #if BTC_AV_INCLUDED */ diff --git a/lib/bt/host/bluedroid/btc/profile/std/a2dp/include/btc_av_co.h b/lib/bt/host/bluedroid/btc/profile/std/a2dp/include/btc_av_co.h new file mode 100644 index 00000000..34d7a729 --- /dev/null +++ b/lib/bt/host/bluedroid/btc/profile/std/a2dp/include/btc_av_co.h @@ -0,0 +1,221 @@ +/* + * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __BTC_AV_CO_H__ +#define __BTC_AV_CO_H__ + +#include "btc_a2dp.h" +#include "bta/bta_av_co.h" + +#if (BTA_AV_INCLUDED == TRUE) +/******************************************************************************* +** Constants & Macros +********************************************************************************/ + +enum { + BTC_SV_AV_AA_SBC_INDEX = 0, + BTC_SV_AV_AA_SBC_SINK_INDEX, + BTC_SV_AV_AA_SEP_INDEX /* Last index */ +}; + +/***************************************************************************** +** Local data +*****************************************************************************/ +typedef struct { + UINT8 sep_info_idx; /* local SEP index (in BTA tables) */ + UINT8 seid; /* peer SEP index (in peer tables) */ + UINT8 codec_type; /* peer SEP codec type */ + UINT8 codec_caps[AVDT_CODEC_SIZE]; /* peer SEP codec capabilities */ + UINT8 num_protect; /* peer SEP number of CP elements */ + UINT8 protect_info[BTA_AV_CP_INFO_LEN]; /* peer SEP content protection info */ +} tBTA_AV_CO_SINK; + +typedef struct { + BD_ADDR addr; /* address of audio/video peer */ + tBTA_AV_CO_SINK snks[BTC_SV_AV_AA_SEP_INDEX]; /* array of supported sinks */ + tBTA_AV_CO_SINK srcs[BTC_SV_AV_AA_SEP_INDEX]; /* array of supported srcs */ + UINT8 num_snks; /* total number of sinks at peer */ + UINT8 num_srcs; /* total number of srcs at peer */ + UINT8 num_seps; /* total number of seids at peer */ + UINT8 num_rx_snks; /* number of received sinks */ + UINT8 num_rx_srcs; /* number of received srcs */ + UINT8 num_sup_snks; /* number of supported sinks in the snks array */ + UINT8 num_sup_srcs; /* number of supported srcs in the srcs array */ + tBTA_AV_CO_SINK *p_snk; /* currently selected sink */ + tBTA_AV_CO_SINK *p_src; /* currently selected src */ + UINT8 codec_cfg[AVDT_CODEC_SIZE]; /* current codec configuration */ + BOOLEAN cp_active; /* current CP configuration */ + BOOLEAN acp; /* acceptor */ + BOOLEAN recfg_needed; /* reconfiguration is needed */ + BOOLEAN opened; /* opened */ + UINT16 mtu; /* maximum transmit unit size */ + UINT16 uuid_to_connect; /* uuid of peer device */ + BOOLEAN got_disc_res; /* got the results of initiating discovery */ +} tBTA_AV_CO_PEER; + +typedef struct { + BOOLEAN active; + UINT8 flag; +} tBTA_AV_CO_CP; + +typedef struct { + /* Connected peer information */ + tBTA_AV_CO_PEER peers[BTA_AV_NUM_STRS]; + /* Current codec configuration - access to this variable must be protected */ + tBTC_AV_CODEC_INFO codec_cfg; + tBTC_AV_CODEC_INFO codec_cfg_setconfig; /* remote peer setconfig preference */ + + tBTA_AV_CO_CP cp; +} tBTA_AV_CO_CB; + +/* Control block instance */ +#if AVRC_DYNAMIC_MEMORY == FALSE +extern tBTA_AV_CO_CB bta_av_co_cb; +#else +extern tBTA_AV_CO_CB *bta_av_co_cb_ptr; +#define bta_av_co_cb (*bta_av_co_cb_ptr) +#endif +/******************************************************************************* +** Functions +********************************************************************************/ + +/******************************************************************************* + ** + ** Function bta_av_co_cp_is_active + ** + ** Description Get the current configuration of content protection + ** + ** Returns TRUE if the current streaming has CP, FALSE otherwise + ** + *******************************************************************************/ +BOOLEAN bta_av_co_cp_is_active(void); + +/******************************************************************************* + ** + ** Function bta_av_co_cp_get_flag + ** + ** Description Get content protection flag + ** BTA_AV_CP_SCMS_COPY_NEVER + ** BTA_AV_CP_SCMS_COPY_ONCE + ** BTA_AV_CP_SCMS_COPY_FREE + ** + ** Returns The current flag value + ** + *******************************************************************************/ +UINT8 bta_av_co_cp_get_flag(void); + +/******************************************************************************* + ** + ** Function bta_av_co_cp_set_flag + ** + ** Description Set content protection flag + ** BTA_AV_CP_SCMS_COPY_NEVER + ** BTA_AV_CP_SCMS_COPY_ONCE + ** BTA_AV_CP_SCMS_COPY_FREE + ** + ** Returns TRUE if setting the SCMS flag is supported else FALSE + ** + *******************************************************************************/ +BOOLEAN bta_av_co_cp_set_flag(UINT8 cp_flag); + +/******************************************************************************* + ** + ** Function bta_av_co_audio_codec_reset + ** + ** Description Reset the current codec configuration + ** + ** Returns void + ** + *******************************************************************************/ +void bta_av_co_audio_codec_reset(void); + +/******************************************************************************* + ** + ** Function bta_av_co_audio_codec_supported + ** + ** Description Check if all opened connections are compatible with a codec + ** configuration + ** + ** Returns TRUE if all opened devices support this codec, FALSE otherwise + ** + *******************************************************************************/ +BOOLEAN bta_av_co_audio_codec_supported(tBTC_AV_STATUS *p_status); + +/******************************************************************************* + ** + ** Function bta_av_co_audio_set_codec + ** + ** Description Set the current codec configuration from the feeding type. + ** This function is starting to modify the configuration, it + ** should be protected. + ** + ** Returns TRUE if successful, FALSE otherwise + ** + *******************************************************************************/ +BOOLEAN bta_av_co_audio_set_codec(const tBTC_AV_MEDIA_FEEDINGS *p_feeding, tBTC_AV_STATUS *p_status); + +/******************************************************************************* + ** + ** Function bta_av_co_audio_get_sbc_config + ** + ** Description Retrieves the SBC codec configuration. If the codec in use + ** is not SBC, return the default SBC codec configuration. + ** + ** Returns TRUE if codec is SBC, FALSE otherwise + ** + *******************************************************************************/ +BOOLEAN bta_av_co_audio_get_sbc_config(tA2D_SBC_CIE *p_sbc_config, UINT16 *p_minmtu); + +/******************************************************************************* + ** + ** Function bta_av_co_audio_discard_config + ** + ** Description Discard the codec configuration of a connection + ** + ** Returns Nothing + ** + *******************************************************************************/ +void bta_av_co_audio_discard_config(tBTA_AV_HNDL hndl); + +/******************************************************************************* + ** + ** Function bta_av_co_init + ** + ** Description Initialization + ** + ** Returns Nothing + ** + *******************************************************************************/ +void bta_av_co_init(void); + + +/******************************************************************************* + ** + ** Function bta_av_co_peer_cp_supported + ** + ** Description Checks if the peer supports CP + ** + ** Returns TRUE if the peer supports CP + ** + *******************************************************************************/ +BOOLEAN bta_av_co_peer_cp_supported(tBTA_AV_HNDL hndl); + +/******************************************************************************* + ** + ** Function bta_av_co_get_remote_bitpool_pref + ** + ** Description Check if remote side did a setconfig within the limits + ** of our exported bitpool range. If set we will set the + ** remote preference. + ** + ** Returns TRUE if config set, FALSE otherwize + ** + *******************************************************************************/ +BOOLEAN bta_av_co_get_remote_bitpool_pref(UINT8 *min, UINT8 *max); + +#endif ///BTA_AV_INCLUDED == TRUE + +#endif diff --git a/lib/bt/host/bluedroid/btc/profile/std/avrc/bta_avrc_co.c b/lib/bt/host/bluedroid/btc/profile/std/avrc/bta_avrc_co.c new file mode 100644 index 00000000..bc2226e7 --- /dev/null +++ b/lib/bt/host/bluedroid/btc/profile/std/avrc/bta_avrc_co.c @@ -0,0 +1,99 @@ +/* + * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/****************************************************************************** + * + * This is the AVRC call-out function implementation for BTC. + * + ******************************************************************************/ + +/***************************************************************************** + * + * Filename: bta_avrc_co.c + * + * Description: Bluetooth AVRC implementation + * + *****************************************************************************/ + +#include +#include "common/bt_target.h" +#include "bta/bta_sys.h" +#include "bta/bta_av_api.h" +#include "btc_avrc.h" + +#if BTC_AV_INCLUDED +/******************************************************************************* + ** + ** Function bta_avrc_co_cmd_allowed + ** + ** Description Check if local AVRCP TG configuration supports a specific + ** PASSTHROUGH command with the given operation_id + ** + ** Returns TRUE if operation_id is supported, FALSE otherwise + ** + *******************************************************************************/ +BOOLEAN bta_avrc_co_cmd_allowed(tBTA_AV_RC rc_id) +{ + if (rc_id >= BTA_AV_VENDOR) { + return FALSE; + } + const uint16_t *rc_cmd = btc_avrc_tg_get_supported_command(); + if (rc_cmd[rc_id >> 4] & ((uint16_t)1 << (rc_id & 0x0F))) { + return TRUE; + } else { + return FALSE; + } +} + +/******************************************************************************* + ** + ** Function bta_avrc_co_rn_evt_cap + ** + ** Description get the event notifcation capabilities on AVRCP target + ** + ** Returns number of event_ids supported + ** + *******************************************************************************/ +UINT8 bta_avrc_co_rn_evt_cap(UINT8 *event_ids) +{ + if (event_ids == 0) { + return 0; + } + + UINT16 event_bits = btc_avrc_tg_get_rn_supported_evt(); + UINT8 count = 0; + for (UINT8 i = 0; i < 16; ++i, event_bits >>= 1) { + if (event_bits & 0x01) { + event_ids[count++] = i; + } + } + return count; +} + +/******************************************************************************* + ** + ** Function bta_avrc_co_evt_supported + ** + ** Description Check if local AVRCP TG configuration supports the given + ** event_id + ** + ** Returns TRUE if operation_id is supported, FALSE otherwise + ** + *******************************************************************************/ +BOOLEAN bta_avrc_co_rn_evt_supported(UINT8 event_id) +{ + return btc_avrc_tg_rn_evt_supported(event_id) ? + TRUE : FALSE; +} + +/* the call out functions for AVRC */ +tBTA_AVRC_CO_FUNCTS bta_avrc_cos = { + bta_avrc_co_cmd_allowed, + bta_avrc_co_rn_evt_cap, + bta_avrc_co_rn_evt_supported, +}; + +#endif /* #if BTC_AV_INCLUDED */ diff --git a/lib/bt/host/bluedroid/btc/profile/std/avrc/btc_avrc.c b/lib/bt/host/bluedroid/btc/profile/std/avrc/btc_avrc.c new file mode 100644 index 00000000..7e7f1b2b --- /dev/null +++ b/lib/bt/host/bluedroid/btc/profile/std/avrc/btc_avrc.c @@ -0,0 +1,1458 @@ +/* + * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/***************************************************************************** + * + * Filename: btc_avrc.c + * + * Description: Bluetooth AVRC implementation + * + *****************************************************************************/ +#include "common/bt_target.h" +#include +#include "bta/bta_api.h" +#include "bta/bta_av_api.h" +#include "stack/avrc_defs.h" +#include "btc/btc_common.h" +#include "btc/btc_util.h" +#include "btc_av.h" +#include "btc_avrc.h" +#include "btc/btc_manage.h" +#include "esp_avrc_api.h" +#include "osi/mutex.h" +#include "osi/allocator.h" + +#if BTC_AV_INCLUDED + +static UINT8 opcode_from_pdu(UINT8 pdu); +static void send_reject_response (UINT8 rc_handle, UINT8 label, UINT8 pdu, UINT8 status); +static void handle_rc_connect (tBTA_AV_RC_OPEN *p_rc_open); +static void handle_rc_disconnect (tBTA_AV_RC_CLOSE *p_rc_close); +static void handle_rc_passthrough_rsp ( tBTA_AV_REMOTE_RSP *p_remote_rsp); +static void handle_rc_metamsg_cmd ( tBTA_AV_META_MSG *p_meta_msg); +static void handle_rc_metamsg_rsp ( tBTA_AV_META_MSG *p_remote_rsp); +static void btc_rc_upstreams_evt(UINT16 event, tAVRC_COMMAND *pavrc_cmd, UINT8 ctype, UINT8 label); + +/***************************************************************************** +** Static variables +******************************************************************************/ + +/* flag indicating wheter TG/CT is initialized */ +static uint32_t s_rc_ct_init; +static uint32_t s_rc_tg_init; + +#if AVRC_DYNAMIC_MEMORY == FALSE +static btc_rc_cb_t btc_rc_cb; +#else +btc_rc_cb_t *btc_rc_cb_ptr; +#endif ///AVRC_DYNAMIC_MEMORY == FALSE + +const static uint16_t cs_psth_allowed_cmd[8] = { + 0x0000, /* bit mask: 0=SELECT, 1=UP, 2=DOWN, 3=LEFT, + 4=RIGHT, 5=RIGHT_UP, 6=RIGHT_DOWN, 7=LEFT_UP, + 8=LEFT_DOWN, 9=ROOT_MENU, 10=SETUP_MENU, 11=CONT_MENU, + 12=FAV_MENU, 13=EXIT */ + 0x0000, /* not used */ + 0x1FFF, /* bit mask: 0=0, 1=1, 2=2, 3=3, + 4=4, 5=5, 6=6, 7=7, + 8=8, 9=9, 10=DOT, 11=ENTER, + 12=CLEAR */ + 0x0078, /* bit mask: 0=CHAN_UP, 1=CHAN_DOWN, 2=PREV_CHAN, 3=SOUND_SEL, + 4=INPUT_SEL, 5=DISP_INFO, 6=HELP, 7=PAGE_UP, + 8=PAGE_DOWN */ + 0x1b7F, /* bit mask: 0=POWER, 1=VOL_UP, 2=VOL_DOWN, 3=MUTE, + 4=PLAY, 5=STOP, 6=PAUSE, 7=RECORD, + 8=REWIND, 9=FAST_FOR, 10=EJECT, 11=FORWARD, + 12=BACKWARD */ + 0x0000, /* bit mask: 0=ANGLE, 1=SUBPICT */ + 0x0000, /* not used */ + 0x003E /* bit mask: 0=not used, 1=F1, 2=F2, 3=F3, + 4=F4, 5=F5 */ +}; + +const static uint16_t cs_psth_dft_supported_cmd[8] = {0}; +static uint16_t s_psth_supported_cmd[8]; + +const static uint16_t cs_rn_allowed_evt = \ + 0x2000; /* bit mask: 0=rsvd, 1=play_status, 2=track_chg, 3=reached_end, + 4=reached_start, 5=pos_chg, 6=batt_status, 7=sys_status_chg, + 8=player_app_setting, 9=now_playing_content, 10=avail_players, 11=addressed_player, + 12=UID, 13=volume_chg */ + +const static uint16_t cs_rn_dft_supported_evt = 0; +static uint16_t s_rn_supported_evt; + +// event ids supported to register as CT +const static uint16_t cs_ct_rn_supported_evt = \ + 0x2026; /* bit mask: 0=rsvd, 1=play_status, 2=track_chg, 3=reached_end, + 4=reached_start, 5=pos_chg, 6=batt_status, 7=sys_status_chg, + 8=player_app_setting, 9=now_playing_content, 10=avail_players, 11=addressed_player, + 12=UID, 13=volume_chg */ + +/***************************************************************************** +** Externs +******************************************************************************/ +const uint16_t *btc_avrc_tg_get_allowed_command(void) +{ + return cs_psth_allowed_cmd; +} + +const uint16_t *btc_avrc_tg_get_supported_command(void) +{ + return s_psth_supported_cmd; +} + +bool btc_avrc_tg_check_supported_command(const uint16_t *cmd_set) +{ + if (cmd_set == NULL) { + return false; + } + + // check if cmd_set is a subset of allowed command set + bool ret = true; + for (int i = 0; i < 8; ++i) { + if (cs_psth_allowed_cmd[i] != (cmd_set[i] | cs_psth_allowed_cmd[i])) { + ret = false; + break; + } + } + + return ret; +} + +uint16_t btc_avrc_tg_get_rn_allowed_evt(void) +{ + return cs_rn_allowed_evt; +} + +uint16_t btc_avrc_tg_get_rn_supported_evt(void) +{ + return s_rn_supported_evt; +} + +bool btc_avrc_tg_check_rn_supported_evt(uint16_t evt_set) +{ + uint16_t evt_super_set = evt_set | cs_rn_allowed_evt; + if (evt_super_set == cs_rn_allowed_evt) { + return true; + } else { + return false; + } +} + +bool btc_avrc_tg_rn_evt_supported(uint8_t event_id) +{ + uint16_t event_bits = s_rn_supported_evt; + return ((event_bits >> event_id) & 0x0001) ? + true : false; +} + +bool btc_avrc_ct_rn_evt_supported(uint8_t event_id) +{ + uint16_t event_bits = cs_ct_rn_supported_evt; + return ((event_bits >> event_id) & 0x0001) ? + true : false; +} + +bool btc_avrc_tg_init_p(void) +{ + return (s_rc_tg_init == BTC_RC_TG_INIT_MAGIC); +} + +bool btc_avrc_ct_init_p(void) +{ + return (s_rc_ct_init == BTC_RC_CT_INIT_MAGIC); +} + +bool btc_avrc_tg_connected_p(void) +{ + return (s_rc_tg_init == BTC_RC_TG_INIT_MAGIC) && + (btc_rc_cb.rc_connected == TRUE) && + (btc_rc_cb.rc_features & BTA_AV_FEAT_RCCT); +} + +bool btc_avrc_ct_connected_p(void) +{ + return (s_rc_ct_init == BTC_RC_CT_INIT_MAGIC) && + (btc_rc_cb.rc_connected == TRUE) && + (btc_rc_cb.rc_features & BTA_AV_FEAT_RCTG); +} + +void btc_avrc_tg_arg_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src) +{ + btc_avrc_tg_args_t *dst = (btc_avrc_tg_args_t *) p_dest; + btc_avrc_tg_args_t *src = (btc_avrc_tg_args_t *)p_src; + size_t len; + + switch (msg->act) { + case BTC_AVRC_TG_API_SET_PSTH_SUPPORTED_CMD_EVT: + len = 8 * sizeof(uint16_t); + dst->set_psth_cmd = (uint16_t *)osi_malloc(len); + if (dst->set_psth_cmd) { + memcpy(dst->set_psth_cmd, src->set_psth_cmd, len); + } else { + BTC_TRACE_ERROR("%s %d no mem\n", __FUNCTION__, msg->act); + } + break; + default: + BTC_TRACE_DEBUG("%s Unhandled deep copy %d\n", __FUNCTION__, msg->act); + break; + } +} + +void btc_avrc_tg_arg_deep_free(btc_msg_t *msg) +{ + btc_avrc_tg_args_t *arg = (btc_avrc_tg_args_t *)msg->arg; + + switch (msg->act) { + case BTC_AVRC_TG_API_SET_PSTH_SUPPORTED_CMD_EVT: + if (arg->set_psth_cmd) { + osi_free(arg->set_psth_cmd); + } + break; + default: + BTC_TRACE_DEBUG("%s Unhandled deep free %d\n", __FUNCTION__, msg->act); + break; + } +} + +/***************************************************************************** +** Static functions +******************************************************************************/ +static bool btc_avrc_tg_set_supported_command(const uint16_t *cmd_set) +{ + if (!btc_avrc_tg_init_p()) { + BTC_TRACE_WARNING("%s failed: AVRC TG not yet initialized\n", __FUNCTION__); + return false; + } + if (!btc_avrc_tg_check_supported_command(cmd_set)) { + return false; + } else { + memcpy(s_psth_supported_cmd, cmd_set, sizeof(s_psth_supported_cmd)); + return true; + } +} + +static bool btc_avrc_tg_set_rn_supported_evt(uint16_t evt_set) +{ + if (!btc_avrc_tg_init_p()) { + BTC_TRACE_WARNING("%s failed: AVRC TG not yet initialized\n", __FUNCTION__); + return false; + } + + if (btc_avrc_tg_check_rn_supported_evt(evt_set)) { + s_rn_supported_evt = evt_set; + return true; + } else { + return false; + } +} + +static inline void btc_avrc_ct_cb_to_app(esp_avrc_ct_cb_event_t event, esp_avrc_ct_cb_param_t *param) +{ + if (s_rc_ct_init != BTC_RC_CT_INIT_MAGIC) { + return; + } + + esp_avrc_ct_cb_t btc_avrc_ct_cb = (esp_avrc_ct_cb_t)btc_profile_cb_get(BTC_PID_AVRC_CT); + if (btc_avrc_ct_cb) { + btc_avrc_ct_cb(event, param); + } +} + +static inline void btc_avrc_tg_cb_to_app(esp_avrc_tg_cb_event_t event, esp_avrc_tg_cb_param_t *param) +{ + if (s_rc_tg_init != BTC_RC_TG_INIT_MAGIC) { + return; + } + + esp_avrc_tg_cb_t btc_avrc_tg_cb = (esp_avrc_tg_cb_t)btc_profile_cb_get(BTC_PID_AVRC_TG); + if (btc_avrc_tg_cb) { + btc_avrc_tg_cb(event, param); + } +} + +/*************************************************************************** + * Function send_metamsg_rsp + * + * - Argument: + * rc_handle RC handle corresponding to the connected RC + * label Label of the RC response + * code Response type + * p_meta_rsp Vendor response + * + * - Description: Remote control metamsg response handler (AVRCP 1.3) + * + ***************************************************************************/ +static void send_metamsg_rsp (UINT8 rc_handle, UINT8 label, tBTA_AV_CODE code, + tAVRC_RESPONSE *p_meta_rsp) +{ + UINT8 ctype; + + if (!p_meta_rsp) { + BTC_TRACE_WARNING("%s: Invalid response received from application", __FUNCTION__); + return; + } + + BTC_TRACE_EVENT("%s: rc_handle: %d, label: %d, code: 0x%02x, pdu: %d", __FUNCTION__, + rc_handle, label, code, p_meta_rsp->rsp.pdu); + + if (p_meta_rsp->rsp.status != AVRC_STS_NO_ERROR) { + ctype = AVRC_RSP_REJ; + } else { + if (code < AVRC_RSP_NOT_IMPL) { + if (code == AVRC_CMD_NOTIF) { + ctype = AVRC_RSP_INTERIM; + } else if (code == AVRC_CMD_STATUS) { + ctype = AVRC_RSP_IMPL_STBL; + } else { + ctype = AVRC_RSP_ACCEPT; + } + } else { + ctype = code; + } + } + + /* if response is for register_notification, make sure the rc has actually registered for this */ + if ((p_meta_rsp->rsp.pdu == AVRC_PDU_REGISTER_NOTIFICATION) && (code == AVRC_RSP_CHANGED)) { + UINT8 event_id = p_meta_rsp->reg_notif.event_id; + BOOLEAN notify = (btc_rc_cb.rc_connected) && (btc_rc_cb.rc_ntf[event_id - 1].registered); + + /* de-register this notification for a CHANGED response */ + btc_rc_cb.rc_ntf[event_id - 1].registered = FALSE; + BTC_TRACE_DEBUG("%s rc_handle: %d. event_id: 0x%02d deregistered", __FUNCTION__, + btc_rc_cb.rc_handle, event_id); + if (notify) { + BT_HDR *p_msg = NULL; + tAVRC_STS status = AVRC_BldResponse(btc_rc_cb.rc_handle, p_meta_rsp, &p_msg); + + if (status == AVRC_STS_NO_ERROR) { + BTC_TRACE_DEBUG("%s Sending notification to rc_handle: %d. event_id: 0x%02d", + __FUNCTION__, btc_rc_cb.rc_handle, event_id); + BTA_AvMetaRsp(btc_rc_cb.rc_handle, btc_rc_cb.rc_ntf[event_id - 1].label, ctype, p_msg); + } else { + BTC_TRACE_WARNING("%s failed to build metamsg response. status: 0x%02x", + __FUNCTION__, status); + } + } + } else { + // send response + BT_HDR *p_msg = NULL; + tAVRC_STS status; + + status = AVRC_BldResponse(rc_handle, p_meta_rsp, &p_msg); + + if (status == AVRC_STS_NO_ERROR) { + BTA_AvMetaRsp(rc_handle, label, ctype, p_msg); + } else { + BTC_TRACE_ERROR("%s: failed to build metamsg response. status: 0x%02x", + __FUNCTION__, status); + } + } +} + + +static UINT8 opcode_from_pdu(UINT8 pdu) +{ + UINT8 opcode = 0; + + switch (pdu) { + case AVRC_PDU_NEXT_GROUP: + case AVRC_PDU_PREV_GROUP: /* pass thru */ + opcode = AVRC_OP_PASS_THRU; + break; + + default: /* vendor */ + opcode = AVRC_OP_VENDOR; + break; + } + + return opcode; +} + +/* Generic reject response */ +static void send_reject_response (UINT8 rc_handle, UINT8 label, UINT8 pdu, UINT8 status) +{ + UINT8 ctype = AVRC_RSP_REJ; + tAVRC_RESPONSE avrc_rsp; + BT_HDR *p_msg = NULL; + memset (&avrc_rsp, 0, sizeof(tAVRC_RESPONSE)); + + avrc_rsp.rsp.opcode = opcode_from_pdu(pdu); + avrc_rsp.rsp.pdu = pdu; + avrc_rsp.rsp.status = status; + + if (AVRC_STS_NO_ERROR == (status = AVRC_BldResponse(rc_handle, &avrc_rsp, &p_msg)) ) { + BTC_TRACE_DEBUG("%s: Sending error notification to handle:%d. pdu:%s,status:0x%02x", + __FUNCTION__, rc_handle, dump_rc_pdu(pdu), status); + BTA_AvMetaRsp(rc_handle, label, ctype, p_msg); + } +} + +static void handle_rc_features(void) +{ + if ((btc_rc_cb.rc_features & BTA_AV_FEAT_RCTG) && + (btc_rc_cb.rc_tg_features != 0)) { + esp_avrc_ct_cb_param_t param; + memset(¶m, 0, sizeof(esp_avrc_ct_cb_param_t)); + param.rmt_feats.feat_mask = btc_rc_cb.rc_features; + param.rmt_feats.tg_feat_flag = btc_rc_cb.rc_tg_features; + memcpy(param.rmt_feats.remote_bda, btc_rc_cb.rc_addr, sizeof(esp_bd_addr_t)); + btc_avrc_ct_cb_to_app(ESP_AVRC_CT_REMOTE_FEATURES_EVT, ¶m); + } + + if ((btc_rc_cb.rc_features & BTA_AV_FEAT_RCCT) && + (btc_rc_cb.rc_ct_features != 0)) { + esp_avrc_tg_cb_param_t param; + memset(¶m, 0, sizeof(esp_avrc_tg_cb_param_t)); + param.rmt_feats.feat_mask = btc_rc_cb.rc_features; + param.rmt_feats.ct_feat_flag = btc_rc_cb.rc_ct_features; + memcpy(param.rmt_feats.remote_bda, btc_rc_cb.rc_addr, sizeof(esp_bd_addr_t)); + btc_avrc_tg_cb_to_app(ESP_AVRC_TG_REMOTE_FEATURES_EVT, ¶m); + } +} + + +/*************************************************************************** + * Function handle_rc_connect + * + * - Argument: tBTA_AV_RC_OPEN RC open data structure + * + * - Description: RC connection event handler + * + ***************************************************************************/ +static void handle_rc_connect (tBTA_AV_RC_OPEN *p_rc_open) +{ + BTC_TRACE_DEBUG("%s: rc_handle: %d", __FUNCTION__, p_rc_open->rc_handle); + bt_bdaddr_t rc_addr; + + if (p_rc_open->status == BTA_AV_SUCCESS) { + //check if already some RC is connected + if (btc_rc_cb.rc_connected) { + BTC_TRACE_ERROR("Got RC OPEN in connected state, Connected RC: %d \ + and Current RC: %d", btc_rc_cb.rc_handle, p_rc_open->rc_handle ); + if ((btc_rc_cb.rc_handle != p_rc_open->rc_handle) + && (bdcmp(btc_rc_cb.rc_addr, p_rc_open->peer_addr))) { + BTC_TRACE_DEBUG("Got RC connected for some other handle"); + BTA_AvCloseRc(p_rc_open->rc_handle); + return; + } + } + memcpy(btc_rc_cb.rc_addr, p_rc_open->peer_addr, sizeof(BD_ADDR)); + btc_rc_cb.rc_features = p_rc_open->peer_features; + btc_rc_cb.rc_connected = TRUE; + btc_rc_cb.rc_handle = p_rc_open->rc_handle; + + bdcpy(rc_addr.address, btc_rc_cb.rc_addr); + // callback to application + if (p_rc_open->peer_features & BTA_AV_FEAT_RCTG) { + esp_avrc_ct_cb_param_t param; + memset(¶m, 0, sizeof(esp_avrc_ct_cb_param_t)); + param.conn_stat.connected = true; + memcpy(param.conn_stat.remote_bda, &rc_addr, sizeof(esp_bd_addr_t)); + btc_avrc_ct_cb_to_app(ESP_AVRC_CT_CONNECTION_STATE_EVT, ¶m); + } + + if (p_rc_open->peer_features & BTA_AV_FEAT_RCCT) { + esp_avrc_tg_cb_param_t param; + memset(¶m, 0, sizeof(esp_avrc_tg_cb_param_t)); + param.conn_stat.connected = true; + memcpy(param.conn_stat.remote_bda, &rc_addr, sizeof(esp_bd_addr_t)); + btc_avrc_tg_cb_to_app(ESP_AVRC_TG_CONNECTION_STATE_EVT, ¶m); + } + + /* on locally initiated connection we will get remote features as part of connect */ + if (p_rc_open->sdp_disc_done == TRUE) { + btc_rc_cb.rc_ct_features = p_rc_open->peer_ct_features; + btc_rc_cb.rc_tg_features = p_rc_open->peer_tg_features; + handle_rc_features(); + } + } else { + BTC_TRACE_ERROR("%s Connect failed with error code: %d", + __FUNCTION__, p_rc_open->status); + btc_rc_cb.rc_connected = FALSE; + } +} + +/*************************************************************************** + * Function handle_rc_disconnect + * + * - Argument: tBTA_AV_RC_CLOSE RC close data structure + * + * - Description: RC disconnection event handler + * + ***************************************************************************/ +static void handle_rc_disconnect (tBTA_AV_RC_CLOSE *p_rc_close) +{ + BTC_TRACE_DEBUG("%s: rc_handle: %d", __FUNCTION__, p_rc_close->rc_handle); + if ((p_rc_close->rc_handle != btc_rc_cb.rc_handle) + && (bdcmp(btc_rc_cb.rc_addr, p_rc_close->peer_addr))) { + BTC_TRACE_ERROR("Got disconnect of unknown device"); + return; + } + + tBTA_AV_FEAT rc_features = btc_rc_cb.rc_features; + + // clean up the state + btc_rc_cb.rc_handle = 0; + btc_rc_cb.rc_connected = FALSE; + + btc_rc_cb.rc_features = 0; + btc_rc_cb.rc_ct_features = 0; + btc_rc_cb.rc_tg_features = 0; + memset(btc_rc_cb.rc_addr, 0, sizeof(BD_ADDR)); + memset(btc_rc_cb.rc_ntf, 0, sizeof(btc_rc_cb.rc_ntf)); + + /* report connection state */ + if (rc_features & BTA_AV_FEAT_RCTG) { + esp_avrc_ct_cb_param_t param; + memset(¶m, 0, sizeof(esp_avrc_ct_cb_param_t)); + param.conn_stat.connected = false; + memcpy(param.conn_stat.remote_bda, btc_rc_cb.rc_addr, sizeof(esp_bd_addr_t)); + btc_avrc_ct_cb_to_app(ESP_AVRC_CT_CONNECTION_STATE_EVT, ¶m); + } + + if (rc_features & BTA_AV_FEAT_RCCT) { + esp_avrc_tg_cb_param_t param; + memset(¶m, 0, sizeof(esp_avrc_ct_cb_param_t)); + param.conn_stat.connected = false; + memcpy(param.conn_stat.remote_bda, btc_rc_cb.rc_addr, sizeof(esp_bd_addr_t)); + btc_avrc_tg_cb_to_app(ESP_AVRC_TG_CONNECTION_STATE_EVT, ¶m); + } +} + +static void handle_rc_attributes_rsp (tAVRC_MSG_VENDOR *vendor_msg) +{ + uint8_t attr_count = vendor_msg->p_vendor_data[4]; + int attr_index = 5; + int attr_length = 0; + uint32_t attr_id = 0; + + //Check if there are any attributes + if (attr_count < 1) { + return; + } + + esp_avrc_ct_cb_param_t param[attr_count]; + memset(¶m[0], 0, sizeof(esp_avrc_ct_cb_param_t) * attr_count); + + for (int i = 0; i < attr_count; i++) { + attr_length = (int) vendor_msg->p_vendor_data[7 + attr_index] | vendor_msg->p_vendor_data[6 + attr_index] << 8; + + //Received attribute text is not null terminated, so it's useful to know it's length + param[i].meta_rsp.attr_length = attr_length; + param[i].meta_rsp.attr_text = &vendor_msg->p_vendor_data[8 + attr_index]; + + attr_id = vendor_msg->p_vendor_data[3 + attr_index] | + vendor_msg->p_vendor_data[2 + attr_index] << 8 | vendor_msg->p_vendor_data[1 + attr_index] << 16 | + vendor_msg->p_vendor_data[attr_index] << 24; + + //Convert to mask id + param[i].meta_rsp.attr_id = (1 << (attr_id - 1)); + + btc_avrc_ct_cb_to_app(ESP_AVRC_CT_METADATA_RSP_EVT, ¶m[i]); + + attr_index += attr_length + 8; + } +} + +static void handle_rc_notification_rsp (tAVRC_MSG_VENDOR *vendor_msg) +{ + esp_avrc_ct_cb_param_t param; + memset(¶m, 0, sizeof(esp_avrc_ct_cb_param_t)); + + param.change_ntf.event_id = vendor_msg->p_vendor_data[4]; + + uint8_t *data = &vendor_msg->p_vendor_data[5]; + if (!btc_avrc_ct_rn_evt_supported(param.change_ntf.event_id)) { + BTC_TRACE_WARNING("%s unsupported notification on CT, event id 0x%x", __FUNCTION__, + param.change_ntf.event_id); + return; + } + + switch (param.change_ntf.event_id) { + case ESP_AVRC_RN_PLAY_STATUS_CHANGE: + BE_STREAM_TO_UINT8(param.change_ntf.event_parameter.playback, data); + break; + case ESP_AVRC_RN_TRACK_CHANGE: + memcpy(param.change_ntf.event_parameter.elm_id, data, 8); + break; + case ESP_AVRC_RN_PLAY_POS_CHANGED: + BE_STREAM_TO_UINT32(param.change_ntf.event_parameter.play_pos, data); + break; + case ESP_AVRC_RN_BATTERY_STATUS_CHANGE: + BE_STREAM_TO_UINT8(param.change_ntf.event_parameter.batt, data); + break; + case ESP_AVRC_RN_VOLUME_CHANGE: + BE_STREAM_TO_UINT8(param.change_ntf.event_parameter.volume, data); + break; + // for non-parameter event response + case ESP_AVRC_RN_TRACK_REACHED_END: + case ESP_AVRC_RN_TRACK_REACHED_START: + break; + // for other unsupported event: + case ESP_AVRC_RN_SYSTEM_STATUS_CHANGE: + case ESP_AVRC_RN_APP_SETTING_CHANGE: + case ESP_AVRC_RN_NOW_PLAYING_CHANGE: + case ESP_AVRC_RN_AVAILABLE_PLAYERS_CHANGE: + case ESP_AVRC_RN_ADDRESSED_PLAYER_CHANGE: + case ESP_AVRC_RN_UIDS_CHANGE: + default: + BTC_TRACE_WARNING("%s RC unhandled notification response, event id 0x%x", __FUNCTION__, + param.change_ntf.event_id); + break; + } + btc_avrc_ct_cb_to_app(ESP_AVRC_CT_CHANGE_NOTIFY_EVT, ¶m); +} + +static void handle_rc_get_caps_rsp (tAVRC_GET_CAPS_RSP *rsp) +{ + if (rsp->capability_id == AVRC_CAP_EVENTS_SUPPORTED) { + esp_avrc_ct_cb_param_t param; + memset(¶m, 0, sizeof(esp_avrc_ct_cb_param_t)); + param.get_rn_caps_rsp.cap_count = rsp->count; + for (int i = 0; i < rsp->count; ++i) { + esp_avrc_rn_evt_bit_mask_operation(ESP_AVRC_BIT_MASK_OP_SET, ¶m.get_rn_caps_rsp.evt_set, + rsp->param.event_id[i]); + } + btc_avrc_ct_cb_to_app(ESP_AVRC_CT_GET_RN_CAPABILITIES_RSP_EVT, ¶m); + } else { + // ignore other capability_id + BTC_TRACE_WARNING("AVRC unhandled event, CapabilityID: 0x%x", rsp->capability_id); + return; + } +} + +static void handle_rc_set_absolute_volume_rsp(tAVRC_SET_VOLUME_RSP *rsp) +{ + esp_avrc_ct_cb_param_t param; + memset(¶m, 0, sizeof(esp_avrc_ct_cb_param_t)); + + param.set_volume_rsp.volume = rsp->volume; + + btc_avrc_ct_cb_to_app(ESP_AVRC_CT_SET_ABSOLUTE_VOLUME_RSP_EVT, ¶m); +} + +/*************************************************************************** + * Function handle_rc_metamsg_cmd + * + * - Argument: tBTA_AV_VENDOR Structure containing the received + * metamsg command + * + * - Description: Remote control metamsg command handler (AVRCP 1.3) + * + ***************************************************************************/ +static void handle_rc_metamsg_cmd (tBTA_AV_META_MSG *p_meta_msg) +{ + BTC_TRACE_DEBUG("%s, opcode 0x%x, len %d, code %d", __FUNCTION__, p_meta_msg->p_msg->hdr.opcode, p_meta_msg->len, p_meta_msg->code); + + if (p_meta_msg->p_msg->hdr.opcode != AVRC_OP_VENDOR) { + BTC_TRACE_WARNING("Invalid opcode: %x", p_meta_msg->p_msg->hdr.opcode); + return; + } + if (p_meta_msg->len < 3) { + BTC_TRACE_WARNING("Invalid length.Opcode: 0x%x, len: 0x%x", p_meta_msg->p_msg->hdr.opcode, + p_meta_msg->len); + return; + } + + if (p_meta_msg->code >= AVRC_RSP_NOT_IMPL) { +#if (AVRC_ADV_CTRL_INCLUDED == TRUE) + handle_rc_metamsg_rsp(p_meta_msg); +#else + BTC_TRACE_WARNING("%s:Received vendor dependent rsp. code: %d len: %d. Not processing it.", + __FUNCTION__, p_meta_msg->code, p_meta_msg->len); +#endif + return; + } + +#define RC_CMD_PRS_BUF_LEN (512) + uint8_t *buf = (uint8_t *)osi_calloc(RC_CMD_PRS_BUF_LEN); + tAVRC_COMMAND avrc_command = {0}; + tAVRC_STS status; + + status = AVRC_ParsCommand(p_meta_msg->p_msg, &avrc_command, buf, RC_CMD_PRS_BUF_LEN); + BTC_TRACE_DEBUG("Rcv vendor cmd: code %d, PDU 0x%x, label %d", p_meta_msg->code, + avrc_command.cmd.pdu, p_meta_msg->label); + + if (status != AVRC_STS_NO_ERROR) { + /* return error */ + BTC_TRACE_WARNING("%s: Error in parsing vendor command. status: 0x%02x", + __FUNCTION__, status); + send_reject_response(p_meta_msg->rc_handle, p_meta_msg->label, avrc_command.pdu, status); + } else { + btc_rc_upstreams_evt(avrc_command.cmd.pdu, &avrc_command, p_meta_msg->code, p_meta_msg->label); + } + osi_free(buf); +} + +/******************************************************************************* +** +** Function btc_rc_upstreams_evt +** +** Description Executes AVRC UPSTREAMS events in btc context. +** +** Returns void +** +*******************************************************************************/ +static void btc_rc_upstreams_evt(UINT16 event, tAVRC_COMMAND *pavrc_cmd, UINT8 ctype, UINT8 label) +{ + BTC_TRACE_EVENT("%s pdu: 0x%x handle: 0x%x ctype:%x label:%x", __FUNCTION__, + pavrc_cmd->pdu, btc_rc_cb.rc_handle, ctype, label); + + switch (event) { + case AVRC_PDU_SET_ABSOLUTE_VOLUME: { + // set up response + tAVRC_RESPONSE avrc_rsp; + BTC_TRACE_EVENT("%s() AVRC_PDU_SET_ABSOLUTE_VOLUME", __FUNCTION__); + memset(&(avrc_rsp.volume), 0, sizeof(tAVRC_RSP)); + avrc_rsp.volume.opcode = opcode_from_pdu(AVRC_PDU_SET_ABSOLUTE_VOLUME); + avrc_rsp.volume.pdu = AVRC_PDU_SET_ABSOLUTE_VOLUME; + avrc_rsp.volume.status = AVRC_STS_NO_ERROR; + avrc_rsp.volume.volume = pavrc_cmd->volume.volume; + send_metamsg_rsp(btc_rc_cb.rc_handle, label, ctype, &avrc_rsp); + + // set up callback + esp_avrc_tg_cb_param_t param; + memset(¶m, 0, sizeof(esp_avrc_tg_cb_param_t)); + param.set_abs_vol.volume = pavrc_cmd->volume.volume; + btc_avrc_tg_cb_to_app(ESP_AVRC_TG_SET_ABSOLUTE_VOLUME_CMD_EVT, ¶m); + } + break; + case AVRC_PDU_SET_PLAYER_APP_VALUE: { + // set up callback + esp_avrc_tg_cb_param_t param; + param.set_app_value.num_val = pavrc_cmd->set_app_val.num_val; + param.set_app_value.p_vals = (esp_avrc_set_app_value_param_t *)pavrc_cmd->set_app_val.p_vals; + btc_avrc_tg_cb_to_app(ESP_AVRC_TG_SET_PLAYER_APP_VALUE_EVT, ¶m); + } + break; + case AVRC_PDU_GET_PLAY_STATUS: + case AVRC_PDU_GET_ELEMENT_ATTR: + case AVRC_PDU_INFORM_DISPLAY_CHARSET: + //todo: check the valid response for these PDUs + case AVRC_PDU_LIST_PLAYER_APP_ATTR: + case AVRC_PDU_LIST_PLAYER_APP_VALUES: + case AVRC_PDU_GET_CUR_PLAYER_APP_VALUE: + case AVRC_PDU_GET_PLAYER_APP_ATTR_TEXT: + case AVRC_PDU_GET_PLAYER_APP_VALUE_TEXT: { + send_reject_response (btc_rc_cb.rc_handle, label, pavrc_cmd->pdu, AVRC_STS_BAD_CMD); + } + break; + case AVRC_PDU_REGISTER_NOTIFICATION: { + UINT8 event_id = pavrc_cmd->reg_notif.event_id; + if (event_id > MAX_RC_NOTIFICATIONS) { + BTC_TRACE_WARNING("Invalid event_id: 0x%x", event_id); + break; + } + + btc_rc_cb.rc_ntf[event_id - 1].registered = TRUE; + btc_rc_cb.rc_ntf[event_id - 1].label = label; + BTC_TRACE_EVENT("%s: New registerd notification: event_id:0x%x, label:0x%x", + __FUNCTION__, event_id, label); + + // set up callback + esp_avrc_tg_cb_param_t param; + memset(¶m, 0, sizeof(esp_avrc_tg_cb_param_t)); + param.reg_ntf.event_id = pavrc_cmd->reg_notif.event_id; + param.reg_ntf.event_parameter = pavrc_cmd->reg_notif.param; + btc_avrc_tg_cb_to_app(ESP_AVRC_TG_REGISTER_NOTIFICATION_EVT, ¶m); + } + break; + default: { + send_reject_response (btc_rc_cb.rc_handle, label, pavrc_cmd->pdu, + (pavrc_cmd->pdu == AVRC_PDU_SEARCH) ? AVRC_STS_SEARCH_NOT_SUP : AVRC_STS_BAD_CMD); + } + break; + } + return; +} + +/*************************************************************************** + * Function handle_rc_metamsg_rsp + * + * - Argument: tBTA_AV_META_MSG metadata command response + * + * - Description: Vendor metadata response handler + * + ***************************************************************************/ +static void handle_rc_metamsg_rsp (tBTA_AV_META_MSG *p_meta_msg) +{ + tAVRC_RESPONSE avrc_response = {0}; + tAVRC_STS status; + tAVRC_MSG_VENDOR *vendor_msg = &p_meta_msg->p_msg->vendor; + BTC_TRACE_DEBUG("%s: opcode %d, pdu 0x%x, code %d", __FUNCTION__, p_meta_msg->p_msg->hdr.opcode, vendor_msg->p_vendor_data[0], + p_meta_msg->code); + if ( p_meta_msg->p_msg->hdr.opcode != AVRC_OP_VENDOR) { + return; + } + if (p_meta_msg->code != AVRC_RSP_CHANGED && + p_meta_msg->code != AVRC_RSP_INTERIM && + p_meta_msg->code != AVRC_RSP_ACCEPT && + p_meta_msg->code != AVRC_RSP_REJ && + p_meta_msg->code != AVRC_RSP_NOT_IMPL && + p_meta_msg->code != AVRC_RSP_IMPL_STBL) { + return; + } + + // handle GET_ELEMENT_ATTR response + if (p_meta_msg->code == AVRC_RSP_IMPL_STBL && + vendor_msg->p_vendor_data[0] == AVRC_PDU_GET_ELEMENT_ATTR) { + handle_rc_attributes_rsp(vendor_msg); + return; + } + + status = AVRC_ParsResponse(p_meta_msg->p_msg, &avrc_response); + if (status != AVRC_STS_NO_ERROR) { + BTC_TRACE_WARNING("%s: code %d error 0x%x", __FUNCTION__, p_meta_msg->code, status); + return; + } + + tAVRC_MSG *avrc_msg = p_meta_msg->p_msg; + vendor_msg = &avrc_msg->vendor; + switch (avrc_response.rsp.pdu) { + case AVRC_PDU_REGISTER_NOTIFICATION: + if (vendor_msg->hdr.ctype == AVRC_RSP_CHANGED) { + handle_rc_notification_rsp(vendor_msg); + } else if (vendor_msg->hdr.ctype == AVRC_RSP_INTERIM) { + // ignore this response + } + break; + case AVRC_PDU_GET_ELEMENT_ATTR: + // todo: handle this PDU here + break; + case AVRC_PDU_GET_CAPABILITIES: + if (vendor_msg->hdr.ctype == AVRC_RSP_IMPL_STBL) { + handle_rc_get_caps_rsp(&avrc_response.get_caps); + } + break; + case AVRC_PDU_SET_ABSOLUTE_VOLUME: + if (vendor_msg->hdr.ctype == AVRC_RSP_ACCEPT) { + handle_rc_set_absolute_volume_rsp(&avrc_response.volume); + } + break; + default: + BTC_TRACE_WARNING("%s: unhandled meta rsp: pdu 0x%x", __FUNCTION__, avrc_response.rsp.pdu); + } +} + +/*************************************************************************** + * Function handle_rc_passthrough_rsp + * + * - Argument: tBTA_AV_REMOTE_RSP passthrough command response + * + * - Description: Remote control passthrough response handler + * + ***************************************************************************/ +static void handle_rc_passthrough_rsp ( tBTA_AV_REMOTE_RSP *p_remote_rsp) +{ +#if (AVRC_CTLR_INCLUDED == TRUE) + const char *status; + if (btc_rc_cb.rc_features & BTA_AV_FEAT_RCTG) { + int key_state; + if (p_remote_rsp->key_state == AVRC_STATE_RELEASE) { + status = "released"; + key_state = 1; + } else { + status = "pressed"; + key_state = 0; + } + + BTC_TRACE_DEBUG("%s: rc_id=%d status=%s", __FUNCTION__, p_remote_rsp->rc_id, status); + + do { + esp_avrc_ct_cb_param_t param; + memset(¶m, 0, sizeof(esp_avrc_ct_cb_param_t)); + param.psth_rsp.tl = p_remote_rsp->label; + param.psth_rsp.key_code = p_remote_rsp->rc_id; + param.psth_rsp.key_state = key_state; + param.psth_rsp.rsp_code = p_remote_rsp->rsp_code; + btc_avrc_ct_cb_to_app(ESP_AVRC_CT_PASSTHROUGH_RSP_EVT, ¶m); + } while (0); + } else { + BTC_TRACE_ERROR("%s DUT does not support AVRCP controller role", __FUNCTION__); + } + + UNUSED(status); +#else + BTC_TRACE_ERROR("%s AVRCP controller role is not enabled", __FUNCTION__); +#endif +} + +/*************************************************************************** + * Function handle_rc_passthrough_cmd + * + * - Argument: tBTA_AV_RC rc_id remote control command ID + * tBTA_AV_STATE key_state status of key press + * + * - Description: Remote control command handler + * + ***************************************************************************/ +void handle_rc_passthrough_cmd ( tBTA_AV_REMOTE_CMD *p_remote_cmd) +{ + esp_avrc_tg_cb_param_t param; + memset(¶m, 0, sizeof(esp_avrc_tg_cb_param_t)); + param.psth_cmd.key_code = p_remote_cmd->rc_id; + param.psth_cmd.key_state = p_remote_cmd->key_state; + btc_avrc_tg_cb_to_app(ESP_AVRC_TG_PASSTHROUGH_CMD_EVT, ¶m); +} + +/*************************************************************************** + ** + ** Function btc_rc_handler + ** + ** Description RC event handler + ** + ***************************************************************************/ +void btc_rc_handler(tBTA_AV_EVT event, tBTA_AV *p_data) +{ + BTC_TRACE_DEBUG ("%s event:%s", __FUNCTION__, dump_rc_event(event)); + switch (event) { + case BTA_AV_RC_OPEN_EVT: { + BTC_TRACE_DEBUG("RC open, peer_features:%x", p_data->rc_open.peer_features); + handle_rc_connect( &(p_data->rc_open) ); + } break; + + case BTA_AV_RC_CLOSE_EVT: { + handle_rc_disconnect( &(p_data->rc_close) ); + } break; + +#if (AVRC_CTLR_INCLUDED == TRUE) + case BTA_AV_REMOTE_RSP_EVT: { + BTC_TRACE_DEBUG("RSP: rc_id:0x%x key_state:%d", p_data->remote_rsp.rc_id, + p_data->remote_rsp.key_state); + handle_rc_passthrough_rsp( (&p_data->remote_rsp) ); + } + break; +#endif + case BTA_AV_RC_FEAT_EVT: { + BTC_TRACE_DEBUG("Peer_features:%x", p_data->rc_feat.peer_features); + do { + // report connection state if connection state wasn't reported on BTA_AV_RC_OPEN_EVT + tBTA_AV_FEAT old_feats = btc_rc_cb.rc_features; + if ((p_data->rc_feat.peer_features & BTA_AV_FEAT_RCTG) && + !(old_feats & BTA_AV_FEAT_RCTG)) { + esp_avrc_ct_cb_param_t param; + memset(¶m, 0, sizeof(esp_avrc_ct_cb_param_t)); + param.conn_stat.connected = true; + memcpy(param.conn_stat.remote_bda, btc_rc_cb.rc_addr, sizeof(esp_bd_addr_t)); + btc_avrc_ct_cb_to_app(ESP_AVRC_CT_CONNECTION_STATE_EVT, ¶m); + } + if ((p_data->rc_feat.peer_features & BTA_AV_FEAT_RCCT) && + !(old_feats & BTA_AV_FEAT_RCCT)) { + esp_avrc_tg_cb_param_t param; + memset(¶m, 0, sizeof(esp_avrc_ct_cb_param_t)); + param.conn_stat.connected = true; + memcpy(param.conn_stat.remote_bda, btc_rc_cb.rc_addr, sizeof(esp_bd_addr_t)); + btc_avrc_tg_cb_to_app(ESP_AVRC_CT_CONNECTION_STATE_EVT, ¶m); + } + } while (0); + btc_rc_cb.rc_features = p_data->rc_feat.peer_features; + btc_rc_cb.rc_ct_features = p_data->rc_feat.peer_ct_features; + btc_rc_cb.rc_tg_features = p_data->rc_feat.peer_tg_features; + handle_rc_features(); + } + break; + + case BTA_AV_META_MSG_EVT: { + handle_rc_metamsg_cmd(&(p_data->meta_msg)); + } + break; + case BTA_AV_REMOTE_CMD_EVT: { + BTC_TRACE_DEBUG("rc_id:0x%x key_state:%d", p_data->remote_cmd.rc_id, + p_data->remote_cmd.key_state); + handle_rc_passthrough_cmd(&p_data->remote_cmd); + } + break; + default: + BTC_TRACE_DEBUG("Unhandled RC event : 0x%x", event); + } +} + +/*************************************************************************** + ** + ** Function btc_rc_get_connected_peer + ** + ** Description Fetches the connected headset's BD_ADDR if any + ** + ***************************************************************************/ +BOOLEAN btc_rc_get_connected_peer(BD_ADDR peer_addr) +{ + if (btc_rc_cb.rc_connected == TRUE) { + bdcpy(peer_addr, btc_rc_cb.rc_addr); + return TRUE; + } + return FALSE; +} + +/************************************************************************************ +** AVRCP API Functions +************************************************************************************/ + +/******************************************************************************* +** +** Function btc_avrc_ct_init +** +** Description Initializes the AVRC Controller interface +** +** Returns esp_err_t +** +*******************************************************************************/ +static void btc_avrc_ct_init(void) +{ + BTC_TRACE_DEBUG("## %s ##", __FUNCTION__); + if (s_rc_ct_init == BTC_RC_CT_INIT_MAGIC) { + BTC_TRACE_WARNING("%s already initialized", __FUNCTION__); + return; + } + + /// initialize CT-specific resources + s_rc_ct_init = BTC_RC_CT_INIT_MAGIC; + + /// initialize CT-TG shared resources + if (s_rc_tg_init != BTC_RC_TG_INIT_MAGIC) { + memset (&btc_rc_cb, 0, sizeof(btc_rc_cb_t)); + + if (!g_av_with_rc) { + g_av_with_rc = true; + } + + if (g_a2dp_on_init) { + BTC_TRACE_WARNING("AVRC Controller is expected to be initialized in advance of A2DP !!!"); + } + } +} + + +/*************************************************************************** +** +** Function btc_avrc_ct_deinit +** +** Description Closes the AVRC Controller interface +** +** Returns void +** +***************************************************************************/ +static void btc_avrc_ct_deinit(void) +{ + BTC_TRACE_API("## %s ##", __FUNCTION__); + + if (g_a2dp_on_deinit) { + BTC_TRACE_WARNING("A2DP already deinit, AVRC CT shuold deinit in advance of A2DP !!!"); + } + + if (s_rc_ct_init != BTC_RC_CT_INIT_MAGIC) { + BTC_TRACE_WARNING("%s not initialized", __FUNCTION__); + return; + } + + /// deinit CT-specific resources + s_rc_ct_init = 0; + + /// deinit CT-TG shared resources + if (s_rc_tg_init != BTC_RC_TG_INIT_MAGIC) { + memset (&btc_rc_cb, 0, sizeof(btc_rc_cb_t)); + if (g_av_with_rc) { + g_av_with_rc = false; + } + } + + BTC_TRACE_API("## %s ## completed", __FUNCTION__); +} + +static bt_status_t btc_avrc_ct_send_set_player_value_cmd(uint8_t tl, uint8_t attr_id, uint8_t value_id) +{ + tAVRC_STS status = BT_STATUS_UNSUPPORTED; + +#if (AVRC_METADATA_INCLUDED == TRUE) + CHECK_ESP_RC_CONNECTED; + + tAVRC_COMMAND avrc_cmd = {0}; + BT_HDR *p_msg = NULL; + tAVRC_APP_SETTING values = {0}; + + values.attr_id = attr_id; + values.attr_val = value_id; + + avrc_cmd.set_app_val.opcode = AVRC_OP_VENDOR; + avrc_cmd.set_app_val.status = AVRC_STS_NO_ERROR; + avrc_cmd.set_app_val.num_val = 1; + avrc_cmd.set_app_val.p_vals = &values; + avrc_cmd.set_app_val.pdu = AVRC_PDU_SET_PLAYER_APP_VALUE; + + if (btc_rc_cb.rc_features & BTA_AV_FEAT_METADATA) { + status = AVRC_BldCommand(&avrc_cmd, &p_msg); + if (status == AVRC_STS_NO_ERROR) { + BTA_AvMetaCmd(btc_rc_cb.rc_handle, tl, BTA_AV_CMD_CTRL, p_msg); + status = BT_STATUS_SUCCESS; + } + } else { + status = BT_STATUS_FAIL; + BTC_TRACE_DEBUG("%s: feature not supported", __FUNCTION__); + } + +#else + BTC_TRACE_DEBUG("%s: feature not enabled", __FUNCTION__); +#endif + + return status; +} + +static bt_status_t btc_avrc_ct_send_get_rn_caps_cmd(uint8_t tl) +{ + tAVRC_STS status = BT_STATUS_UNSUPPORTED; + +#if (AVRC_METADATA_INCLUDED == TRUE) + CHECK_ESP_RC_CONNECTED; + + tAVRC_COMMAND avrc_cmd = {0}; + BT_HDR *p_msg = NULL; + + avrc_cmd.get_caps.opcode = AVRC_OP_VENDOR; + avrc_cmd.get_caps.status = AVRC_STS_NO_ERROR; + avrc_cmd.get_caps.pdu = AVRC_PDU_GET_CAPABILITIES; + avrc_cmd.get_caps.capability_id = AVRC_CAP_EVENTS_SUPPORTED; + + if (btc_rc_cb.rc_features & BTA_AV_FEAT_METADATA) { + status = AVRC_BldCommand(&avrc_cmd, &p_msg); + if (status == AVRC_STS_NO_ERROR) { + BTA_AvMetaCmd(btc_rc_cb.rc_handle, tl, AVRC_CMD_STATUS, p_msg); + status = BT_STATUS_SUCCESS; + } + } else { + status = BT_STATUS_FAIL; + BTC_TRACE_DEBUG("%s: feature not supported", __FUNCTION__); + } + +#else + BTC_TRACE_DEBUG("%s: feature not enabled", __FUNCTION__); +#endif + + return status; +} + +static bt_status_t btc_avrc_ct_send_register_notification_cmd(uint8_t tl, uint8_t event_id, uint32_t event_parameter) +{ + tAVRC_STS status = BT_STATUS_UNSUPPORTED; + +#if (AVRC_METADATA_INCLUDED == TRUE) + CHECK_ESP_RC_CONNECTED; + + tAVRC_COMMAND avrc_cmd = {0}; + BT_HDR *p_msg = NULL; + + avrc_cmd.reg_notif.opcode = AVRC_OP_VENDOR; + avrc_cmd.reg_notif.status = AVRC_STS_NO_ERROR; + avrc_cmd.reg_notif.event_id = event_id; + avrc_cmd.reg_notif.param = event_parameter; + avrc_cmd.reg_notif.pdu = AVRC_PDU_REGISTER_NOTIFICATION; + + if (btc_rc_cb.rc_features & BTA_AV_FEAT_METADATA) { + status = AVRC_BldCommand(&avrc_cmd, &p_msg); + if (status == AVRC_STS_NO_ERROR) { + BTA_AvMetaCmd(btc_rc_cb.rc_handle, tl, AVRC_CMD_NOTIF, p_msg); + status = BT_STATUS_SUCCESS; + } + } else { + status = BT_STATUS_FAIL; + BTC_TRACE_DEBUG("%s: feature not supported", __FUNCTION__); + } + +#else + BTC_TRACE_DEBUG("%s: feature not enabled", __FUNCTION__); +#endif + + return status; +} + +static bt_status_t btc_avrc_ct_send_set_absolute_volume_cmd(uint8_t tl, uint8_t volume) +{ + tAVRC_STS status = BT_STATUS_UNSUPPORTED; + +#if (AVRC_METADATA_INCLUDED == TRUE) + CHECK_ESP_RC_CONNECTED; + + tAVRC_COMMAND avrc_cmd = {0}; + BT_HDR *p_msg = NULL; + + avrc_cmd.volume.opcode = AVRC_OP_VENDOR; + avrc_cmd.volume.status = AVRC_STS_NO_ERROR; + avrc_cmd.volume.volume = volume; + avrc_cmd.volume.pdu = AVRC_PDU_SET_ABSOLUTE_VOLUME; + + if (btc_rc_cb.rc_features & BTA_AV_FEAT_METADATA) { + status = AVRC_BldCommand(&avrc_cmd, &p_msg); + if (status == AVRC_STS_NO_ERROR) { + BTA_AvMetaCmd(btc_rc_cb.rc_handle, tl, AVRC_CMD_CTRL, p_msg); + status = BT_STATUS_SUCCESS; + } + } else { + status = BT_STATUS_FAIL; + BTC_TRACE_DEBUG("%s: feature not supported", __FUNCTION__); + } + +#else + BTC_TRACE_DEBUG("%s: feature not enabled", __FUNCTION__); +#endif + + return status; +} + +static bt_status_t btc_avrc_ct_send_metadata_cmd (uint8_t tl, uint8_t attr_mask) +{ + tAVRC_STS status = BT_STATUS_UNSUPPORTED; + +#if (AVRC_METADATA_INCLUDED == TRUE) + CHECK_ESP_RC_CONNECTED; + uint32_t index = 0; + + tAVRC_COMMAND avrc_cmd = {0}; + BT_HDR *p_msg = NULL; + + avrc_cmd.get_elem_attrs.opcode = AVRC_OP_VENDOR; + avrc_cmd.get_elem_attrs.status = AVRC_STS_NO_ERROR; + avrc_cmd.get_elem_attrs.pdu = AVRC_PDU_GET_ELEMENT_ATTR; + + for (int count = 0; count < AVRC_MAX_ELEM_ATTR_SIZE; count++) { + if ((attr_mask & (1 << count)) > 0) { + avrc_cmd.get_elem_attrs.attrs[index] = count + 1; + index++; + } + } + + avrc_cmd.get_elem_attrs.num_attr = index; + + if (btc_rc_cb.rc_features & BTA_AV_FEAT_METADATA) { + status = AVRC_BldCommand(&avrc_cmd, &p_msg); + if (status == AVRC_STS_NO_ERROR) { + BTA_AvMetaCmd(btc_rc_cb.rc_handle, tl, AVRC_CMD_STATUS, p_msg); + status = BT_STATUS_SUCCESS; + } + } else { + status = BT_STATUS_FAIL; + BTC_TRACE_DEBUG("%s: feature not supported", __FUNCTION__); + } + +#else + BTC_TRACE_DEBUG("%s: feature not enabled", __FUNCTION__); +#endif + + return status; +} + +static bt_status_t btc_avrc_ct_send_passthrough_cmd(uint8_t tl, uint8_t key_code, uint8_t key_state) +{ + tAVRC_STS status = BT_STATUS_UNSUPPORTED; + +#if (AVRC_CTLR_INCLUDED == TRUE) + CHECK_ESP_RC_CONNECTED; + BTC_TRACE_DEBUG("%s: key-code: %d, key-state: %d", __FUNCTION__, + key_code, key_state); + if (btc_rc_cb.rc_features & BTA_AV_FEAT_RCTG) { + BTA_AvRemoteCmd(btc_rc_cb.rc_handle, tl, + (tBTA_AV_RC)key_code, (tBTA_AV_STATE)key_state); + status = BT_STATUS_SUCCESS; + BTC_TRACE_API("%s: succesfully sent passthrough command to BTA", __FUNCTION__); + } else { + status = BT_STATUS_FAIL; + BTC_TRACE_DEBUG("%s: feature not supported", __FUNCTION__); + } +#else + BTC_TRACE_DEBUG("%s: feature not enabled", __FUNCTION__); +#endif + + return status; +} + + +/******************************************************************************* +** +** Function btc_avrc_tg_init +** +** Description Initializes the AVRC Target interface +** +** Returns esp_err_t +** +*******************************************************************************/ +static void btc_avrc_tg_init(void) +{ + BTC_TRACE_DEBUG("## %s ##", __FUNCTION__); + if (s_rc_tg_init == BTC_RC_TG_INIT_MAGIC) { + BTC_TRACE_WARNING("%s already initialized", __FUNCTION__); + return; + } + + /// initialize TG-specific resources + memcpy(s_psth_supported_cmd, cs_psth_dft_supported_cmd, sizeof(s_psth_supported_cmd)); + s_rn_supported_evt = cs_rn_dft_supported_evt; + + /// initialize CT-TG shared resources + if (s_rc_ct_init != BTC_RC_CT_INIT_MAGIC) { + memset (&btc_rc_cb, 0, sizeof(btc_rc_cb)); + + if (!g_av_with_rc) { + g_av_with_rc = true; + } + + if (g_a2dp_on_init) { + BTC_TRACE_WARNING("AVRC Taget is expected to be initialized in advance of A2DP !!!"); + } + } + + s_rc_tg_init = BTC_RC_TG_INIT_MAGIC; +} + + +/*************************************************************************** +** +** Function btc_avrc_tg_deinit +** +** Description Closes the AVRC Target interface +** +** Returns void +** +***************************************************************************/ +static void btc_avrc_tg_deinit(void) +{ + BTC_TRACE_API("## %s ##", __FUNCTION__); + + if (g_a2dp_on_deinit) { + BTC_TRACE_WARNING("A2DP already deinit, AVRC TG shuold deinit in advance of A2DP !!!"); + } + + if (s_rc_tg_init != BTC_RC_TG_INIT_MAGIC) { + BTC_TRACE_WARNING("%s not initialized", __FUNCTION__); + return; + } + + /// deinit TG-specific resources + memset(s_psth_supported_cmd, 0, sizeof(s_psth_supported_cmd)); + s_rn_supported_evt = 0; + s_rc_tg_init = 0; + + /// deinit CT-TG shared resources + if (s_rc_ct_init != BTC_RC_CT_INIT_MAGIC) { + memset (&btc_rc_cb, 0, sizeof(btc_rc_cb)); + if (g_av_with_rc) { + g_av_with_rc = false; + } + } + + BTC_TRACE_API("## %s ## completed", __FUNCTION__); +} + +static void btc_avrc_tg_send_rn_rsp(esp_avrc_rn_event_ids_t event_id, esp_avrc_rn_rsp_t rsp, const esp_avrc_rn_param_t *param) +{ + tAVRC_RESPONSE avrc_rsp; + if (! btc_avrc_tg_connected_p()) { + BTC_TRACE_WARNING("%s, RC unconnected, operation fail, event_id 0x%x", __FUNCTION__, event_id); + return; + } + + if (btc_rc_cb.rc_ntf[event_id - 1].registered == FALSE) { + BTC_TRACE_ERROR("Event id not registered: event_id = %x", event_id); + return; + } + memset(&(avrc_rsp.reg_notif), 0, sizeof(tAVRC_REG_NOTIF_RSP)); + avrc_rsp.reg_notif.event_id = event_id; + + switch (event_id) { + case ESP_AVRC_RN_VOLUME_CHANGE: + avrc_rsp.reg_notif.param.volume = param->volume; + break; + // todo: implement other event notifications + default: + BTC_TRACE_WARNING("%s : Unhandled event ID : 0x%x", __FUNCTION__, event_id); + return; + } + + avrc_rsp.reg_notif.pdu = AVRC_PDU_REGISTER_NOTIFICATION; + avrc_rsp.reg_notif.opcode = opcode_from_pdu(AVRC_PDU_REGISTER_NOTIFICATION); + avrc_rsp.get_play_status.status = AVRC_STS_NO_ERROR; + + /* Send the response. */ + send_metamsg_rsp(btc_rc_cb.rc_handle, btc_rc_cb.rc_ntf[event_id - 1].label, + ((rsp == ESP_AVRC_RN_RSP_INTERIM) ? AVRC_CMD_NOTIF : AVRC_RSP_CHANGED), &avrc_rsp); + return; +} + +void btc_avrc_ct_call_handler(btc_msg_t *msg) +{ + btc_avrc_args_t *arg = (btc_avrc_args_t *)(msg->arg); + switch (msg->act) { + case BTC_AVRC_CT_API_INIT_EVT: { + btc_avrc_ct_init(); + // todo: callback to application + break; + } + case BTC_AVRC_CT_API_DEINIT_EVT: { + btc_avrc_ct_deinit(); + // todo: callback to application + break; + } + case BTC_AVRC_CTRL_API_SND_PTCMD_EVT: { + btc_avrc_ct_send_passthrough_cmd(arg->pt_cmd.tl, arg->pt_cmd.key_code, arg->pt_cmd.key_state); + // todo: callback to application + break; + } + case BTC_AVRC_STATUS_API_SND_META_EVT: { + btc_avrc_ct_send_metadata_cmd(arg->md_cmd.tl, arg->md_cmd.attr_mask); + break; + } + case BTC_AVRC_STATUS_API_SND_GET_RN_CAPS_EVT: { + btc_avrc_ct_send_get_rn_caps_cmd(arg->get_caps_cmd.tl); + break; + } + case BTC_AVRC_NOTIFY_API_SND_REG_NOTIFY_EVT: { + btc_avrc_ct_send_register_notification_cmd(arg->rn_cmd.tl, arg->rn_cmd.event_id, arg->rn_cmd.event_parameter); + break; + } + case BTC_AVRC_CTRL_API_SND_SET_PLAYER_SETTING_EVT: { + btc_avrc_ct_send_set_player_value_cmd(arg->ps_cmd.tl, arg->ps_cmd.attr_id, arg->ps_cmd.value_id); + break; + } + case BTC_AVRC_CTRL_API_SND_SET_ABSOLUTE_VOLUME_EVT: { + btc_avrc_ct_send_set_absolute_volume_cmd(arg->set_abs_vol_cmd.tl, arg->set_abs_vol_cmd.volume); + break; + } + default: + BTC_TRACE_WARNING("%s : unhandled event: %d\n", __FUNCTION__, msg->act); + } +} + +void btc_avrc_tg_call_handler(btc_msg_t *msg) +{ + btc_avrc_tg_args_t *arg = (btc_avrc_tg_args_t *)(msg->arg); + switch (msg->act) { + case BTC_AVRC_TG_API_INIT_EVT: { + btc_avrc_tg_init(); + break; + } + case BTC_AVRC_TG_API_DEINIT_EVT: { + btc_avrc_tg_deinit(); + break; + } + case BTC_AVRC_TG_API_SET_RN_SUPPORTED_EVT: { + btc_avrc_tg_set_rn_supported_evt(arg->set_rn_evt); + break; + } + case BTC_AVRC_TG_API_SET_PSTH_SUPPORTED_CMD_EVT: { + btc_avrc_tg_set_supported_command(arg->set_psth_cmd); + break; + } + case BTC_AVRC_TG_API_SEND_RN_RSP_EVT: { + btc_avrc_tg_send_rn_rsp(arg->rn_rsp.event_id, arg->rn_rsp.rsp, + &arg->rn_rsp.param); + break; + } + default: + BTC_TRACE_WARNING("%s : unhandled event: %d\n", __FUNCTION__, msg->act); + } + + btc_avrc_tg_arg_deep_free(msg); +} + +#endif /* #if BTC_AV_INCLUDED */ diff --git a/lib/bt/host/bluedroid/btc/profile/std/battery/battery_prf.c b/lib/bt/host/bluedroid/btc/profile/std/battery/battery_prf.c new file mode 100644 index 00000000..0a01fd6b --- /dev/null +++ b/lib/bt/host/bluedroid/btc/profile/std/battery/battery_prf.c @@ -0,0 +1,597 @@ +/* + * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include + +//#include "bluedroid_test.h" +#include "bta/bta_api.h" +#include "bta/bta_gatt_api.h" +#include "device/controller.h" + +#include "gatt_int.h" +#include "common/bt_trace.h" +#include "stack/btm_api.h" +#include "stack/bt_types.h" +#include "dis_api.h" + +#if BLE_INCLUDED == true + +#define BA_MAX_CHAR_NUM 1 +#define BA_MAX_ATTR_NUM (BA_MAX_CHAR_NUM * 5 + 1) +/*max 3 descriptors, 1 desclaration and 1 value*/ + +#ifndef BATTER_LEVEL_PROP +#define BATTER_LEVEL_PROP (GATT_CHAR_PROP_BIT_READ|GATT_CHAR_PROP_BIT_NOTIFY) +#endif + +#ifndef BATTER_LEVEL_PERM +#define BATTER_LEVEL_PERM (GATT_PERM_READ) +#endif + +#define BT_BD_ADDR_STR "%02x:%02x:%02x:%02x:%02x:%02x" +#define BT_BD_ADDR_HEX(addr) addr[0], addr[1], addr[2], addr[3], addr[4], addr[5] +esp_gatts_if_t server_if; + +tBATTERY_CB battery_cb; +tGATT_CHAR_PROP prop = GATT_CHAR_PROP_BIT_READ; +tBA_REG_INFO ba_reg_info; +UINT8 attr_handle_bit = 0x00; + +extern tDIS_CB dis_cb; +esp_bt_uuid_t bas_uuid = {LEN_UUID_16, {UUID_SERVCLASS_BATTERY}}; +/****************************************************************************** +** Function bas_gatts_callback +** +** Description battery service register callback function +*******************************************************************************/ +static void bas_gatts_callback(esp_gatts_evt_t event, tBTA_GATTS *p_data) +{ + switch (event) { + case ESP_GATTS_REG_EVT: { + esp_gatt_status_t status = p_data->reg_oper.status; + server_if = p_data->reg_oper.server_if; + BTC_TRACE_ERROR("BAS register completed: event=%d, status=%d, server_if=%d\n", + event, status, server_if); + + UINT8 app_id = 0xff; + bas_init(server_if, app_id); + + tDIS_ATTR_MASK mask = 0x01ff; + DIS_Init(server_if, mask); + } + break; + + /*connect callback*/ + case ESP_GATTS_CONNECT_EVT: { + BTC_TRACE_ERROR("\ndevice is connected "BT_BD_ADDR_STR", server_if=%d,reason=0x%x,connect_id=%d\n", + BT_BD_ADDR_HEX(p_data->conn.remote_bda), p_data->conn.server_if, + p_data->conn.reason, p_data->conn.conn_id); + /*return whether the remote device is currently connected*/ + int is_connected = BTA_DmGetConnectionState(p_data->conn.remote_bda); + BTC_TRACE_ERROR("is_connected=%d\n", is_connected); + } + break; + + /*create service callback*/ + case ESP_GATTS_CREATE_EVT: { + BTC_TRACE_ERROR("create service:server_if=%d,service_id=0x%x,service_uuid=0x%x\n", + p_data->create.server_if, p_data->create.service_id, + p_data->create.uuid.uu.uuid16); + UINT16 service_uuid = p_data->create.uuid.uu.uuid16; + UINT16 service_id = p_data->create.service_id; + if (service_uuid == 0x180f) { + tBT_UUID uuid = {LEN_UUID_16, {GATT_UUID_BATTERY_LEVEL}}; + bas_AddChar(service_id, &uuid); + } + if (service_uuid == 0x180a) { + dis_cb.service_handle = service_id; + dis_cb.max_handle = service_id + DIS_MAX_ATTR_NUM; + dis_AddChar(service_id); + } + + } + break; + + case ESP_GATTS_ADD_CHAR_EVT: { + BTC_TRACE_ERROR("create characteristic:server_if=%d,service_id=0x%x,char_uuid=0x%x\n", + p_data->add_result.server_if, p_data->add_result.service_id, + p_data->add_result.char_uuid.uu.uuid16); + UINT16 char_uuid = p_data->add_result.char_uuid.uu.uuid16; + UINT16 service_id = p_data->add_result.service_id; + UINT16 uuid_len = p_data->add_result.char_uuid.len; + + if (uuid_len == ESP_UUID_LEN_16) { + if (char_uuid == GATT_UUID_BATTERY_LEVEL) { + bas_AddCharDescr(service_id, p_data->add_result.attr_id); + } + + switch (char_uuid) { + case GATT_UUID_SYSTEM_ID: + dis_cb.dis_attr[0].handle = service_id; break; + case GATT_UUID_MODEL_NUMBER_STR: + dis_cb.dis_attr[1].handle = service_id; break; + case GATT_UUID_SERIAL_NUMBER_STR: + dis_cb.dis_attr[2].handle = service_id; break; + case GATT_UUID_FW_VERSION_STR: + dis_cb.dis_attr[3].handle = service_id; break; + case GATT_UUID_HW_VERSION_STR: + dis_cb.dis_attr[4].handle = service_id; break; + case GATT_UUID_SW_VERSION_STR: + dis_cb.dis_attr[5].handle = service_id; break; + case GATT_UUID_MANU_NAME: + dis_cb.dis_attr[6].handle = service_id; break; + case GATT_UUID_IEEE_DATA: + dis_cb.dis_attr[7].handle = service_id; break; + case GATT_UUID_PNP_ID: + dis_cb.dis_attr[8].handle = service_id; break; + } + default: + break; + } + } + break; + + case ESP_GATTS_ADD_CHAR_DESCR_EVT: { + + BTC_TRACE_ERROR("create descriptor:server_if=%d,service_id=0x%x,attr_id=0x%x,char_uuid=0x%x\n", + p_data->add_result.server_if, p_data->add_result.service_id, + p_data->add_result.attr_id, p_data->add_result.char_uuid.uu.uuid16); + bas_AddCharDescr(p_data->add_result.service_id, p_data->add_result.attr_id); + } + break; + + case ESP_GATTS_START_EVT: { + BTC_TRACE_ERROR("start service:server_if=%d,service_id=0x%x\n", p_data->srvc_oper.server_if, + p_data->srvc_oper.service_id); + bas_service_cmpl(p_data->srvc_oper.service_id, p_data->srvc_oper.status); + + /*start advertising*/ + //if(p_data->srvc_oper.status == GATT_SUCCESS) + // BTA_GATTS_Listen(server_if, true, NULL); + // BTA_GATTC_Broadcast(client_if, true); //non-connectable + } + break; + + case ESP_GATTS_READ_EVT: { + UINT32 trans_id = p_data->req_data.trans_id; + UINT16 conn_id = p_data->req_data.conn_id; + UINT16 handle = p_data->req_data.p_data->read_req.handle; + bool is_long = p_data->req_data.p_data->read_req.is_long; + BTC_TRACE_ERROR("read request:event=0x%x,handle=0x%x,trans_id=0x%x,conn_id=0x%x\n", + event, handle, trans_id, conn_id); + + if (dis_valid_handle_range(handle)) { + tGATT_VALUE p_value; + p_value.handle = handle; + p_value.conn_id = conn_id; + p_value.offset = p_data->req_data.p_data->read_req.offset; + dis_s_read_attr_value(p_data->req_data.p_data, &p_value, trans_id, conn_id); + } else { + bas_s_read_attr_value(p_data->req_data.p_data, trans_id, conn_id); + } + } + break; + + case ESP_GATTS_WRITE_EVT: { + + UINT32 trans_id = p_data->req_data.trans_id; + UINT16 conn_id = p_data->req_data.conn_id; + UINT16 handle = p_data->req_data.p_data->write_req.handle; + BTC_TRACE_ERROR("write request:event=0x%x,handle=0x%x,trans_id=0x%x,conn_id=0x%x\n", + event, handle, trans_id, conn_id); + bas_s_write_attr_value(p_data->req_data.p_data, trans_id, conn_id, + p_data->req_data.remote_bda); + } + break; + + case ESP_GATTS_EXEC_WRITE_EVT: { + UINT32 trans_id = p_data->req_data.trans_id; + UINT16 conn_id = p_data->req_data.conn_id; + UINT8 exec_write = p_data->req_data.p_data->exec_write; + BTC_TRACE_ERROR("execute write request:event=0x%x,exce_write=0x%x,trans_id=0x%x,conn_id=0x%x\n", + event, exec_write, trans_id, conn_id); + } + break; + + case ESP_GATTS_MTU_EVT: { + UINT32 trans_id = p_data->req_data.trans_id; + UINT16 conn_id = p_data->req_data.conn_id; + UINT16 mtu = p_data->req_data.p_data->mtu; + BTC_TRACE_ERROR("exchange mtu request:event=0x%x,mtu=0x%x,trans_id=0x%x,conn_id=0x%x\n", + event, mtu, trans_id, conn_id); + } + break; + + case ESP_GATTS_CFM_EVT: { + + UINT32 trans_id = p_data->req_data.trans_id; + UINT16 conn_id = p_data->req_data.conn_id; + BTC_TRACE_ERROR("configue request:trans_id=0x%x,conn_id=0x%x\n", + trans_id, conn_id); + } + break; + + default: + BTC_TRACE_ERROR("unsettled event: %d\n", event); + break; + } + +} +/****************************************************************************** +** Function bas_callback +** +** Description battery service callback for client request +*******************************************************************************/ +static void bas_callback(UINT32 trans_id, UINT16 conn_id, UINT8 app_id, + UINT8 event, tBA_WRITE_DATA *p_data) +{ + tBA_RSP_DATA p_rsp; + tGATT_STATUS st = ESP_GATT_OK; + switch (event) { + case BA_READ_LEVEL_REQ : { + BTC_TRACE_ERROR("read battery level\n"); + p_rsp.ba_level = 60; //battery level + Battery_Rsp(trans_id, conn_id, app_id, st, event, &p_rsp); + } + break; + + case BA_READ_PRE_FMT_REQ : { + BTC_TRACE_ERROR("read presentation format\n"); + } + break; + + case BA_READ_CLT_CFG_REQ : { + BTC_TRACE_ERROR("read client characteristic configuration request\n"); + p_rsp.clt_cfg = 0x0001; //notification + Battery_Rsp(trans_id, conn_id, app_id, st, event, &p_rsp); + } + break; + + case BA_READ_RPT_REF_REQ : { + BTC_TRACE_ERROR("read report reference descriptor\n"); + } + break; + + /*battery level notify*/ + case BA_WRITE_CLT_CFG_REQ : { + BTC_TRACE_ERROR("write client characteristic configuration request\n"); + Battery_Rsp(trans_id, conn_id, app_id, st, event, NULL); + + int battery_level = 50; + Battery_Notify(conn_id, app_id, p_data->remote_bda, battery_level); + } + break; + + default: + break; + } + + return; +} +/***************************************************************************** +** Function bas_s_read_attr_value +** +** Description it will be called when client sends a read request +******************************************************************************/ +void bas_s_read_attr_value(tGATTS_DATA *p_data, UINT32 trans_id, UINT16 conn_id) +{ + + tBA_INST *p_inst = &battery_cb.battery_inst[0]; + UINT8 i; + esp_gatt_status_t st = ESP_GATT_NOT_FOUND; + UINT16 handle = p_data->read_req.handle; + + + for (i = 0; i < BA_MAX_INT_NUM; i ++, p_inst ++) { + // read battery level + if (handle == p_inst->ba_level_hdl || + handle == p_inst->clt_cfg_hdl || + handle == p_inst->rpt_ref_hdl || + handle == p_inst->pres_fmt_hdl) { + if (p_data->read_req.is_long) { + st = ESP_GATT_NOT_LONG; + } + + if (p_inst->p_cback) { + if (handle == p_inst->ba_level_hdl) { + p_inst->pending_evt = BA_READ_LEVEL_REQ; + } + if (handle == p_inst->clt_cfg_hdl) { + p_inst->pending_evt = BA_READ_CLT_CFG_REQ; + } + if (handle == p_inst->pres_fmt_hdl) { + p_inst->pending_evt = BA_READ_PRE_FMT_REQ; + } + if (handle == p_inst->rpt_ref_hdl) { + p_inst->pending_evt = BA_READ_RPT_REF_REQ ; + } + + // p_inst->pending_clcb_idx = clcb_idx; + p_inst->pending_handle = handle; + //act = SRVC_ACT_PENDING; + (*p_inst->p_cback)(trans_id, conn_id, p_inst->app_id, p_inst->pending_evt, NULL); + } else { /* application is not registered */ + st = ESP_GATT_ERR_UNLIKELY; + } + break; + } + /* else attribute not found */ + } +} + +/***************************************************************************** +** Function bas_s_write_attr_value +** +** Description it will be called when client sends a write request +******************************************************************************/ +void bas_s_write_attr_value(tGATTS_DATA *p_data, UINT32 trans_id, UINT16 conn_id, BD_ADDR bd_addr) +{ + tBA_WRITE_DATA cfg; + UINT8 *p = p_data->write_req.value; + tBA_INST *p_inst = &battery_cb.battery_inst[0]; + UINT8 i; + esp_gatt_status_t st = ESP_GATT_NOT_FOUND; + UINT16 handle = p_data->write_req.handle; + + + for (i = 0; i < BA_MAX_INT_NUM; i ++, p_inst ++) { + if (handle == p_inst->clt_cfg_hdl) { + memcpy(cfg.remote_bda, bd_addr, BD_ADDR_LEN); + STREAM_TO_UINT16(cfg.clt_cfg, p); + + if (p_inst->p_cback) { + p_inst->pending_evt = BA_WRITE_CLT_CFG_REQ; + p_inst->pending_handle = handle; + cfg.need_rsp = p_data->write_req.need_rsp; + (*p_inst->p_cback)(trans_id, conn_id, p_inst->app_id, p_inst->pending_evt, &cfg); + } else { /* all other handle is not writable */ + st = ESP_GATT_WRITE_NOT_PERMIT; + } + break; + } + + } +} +/*************************************************************** +** +** Function bas_register +** +** Description register app for battery service +** +****************************************************************/ +void bas_register(void) +{ + esp_ble_gatts_app_register(&bas_uuid, bas_gatts_callback); + +} +/*************************************************************** +** +** Function bas_init +** +** Description register battery service +** +****************************************************************/ +void bas_init(tBTA_GATTS_IF gatt_if, UINT16 app_id) +{ + + tBA_INST *p_inst; + + ba_reg_info.is_pri = true; + ba_reg_info.ba_level_descr = BA_LEVEL_NOTIFY; + ba_reg_info.transport = GATT_TRANSPORT_LE; + ba_reg_info.p_cback = bas_callback; + if (battery_cb.inst_id == BA_MAX_INT_NUM) { + GATT_TRACE_ERROR("MAX battery service has been reached\n"); + return; + } + + p_inst = &battery_cb.battery_inst[battery_cb.inst_id]; + + BTC_TRACE_ERROR("create battery service\n"); + BTC_TRACE_ERROR("inst_id=%d\n", battery_cb.inst_id); + esp_ble_gatts_create_srvc (gatt_if, &bas_uuid, battery_cb.inst_id , + BA_MAX_ATTR_NUM, ba_reg_info.is_pri); + + battery_cb.inst_id ++; + + p_inst->app_id = app_id; + p_inst->p_cback = ba_reg_info.p_cback; + +} + +/*************************************************************** +** +** Function bas_AddChar +** +** Description add characteristic for battery service +** +****************************************************************/ +void bas_AddChar(UINT16 service_id, tBT_UUID *char_uuid) +{ + if (ba_reg_info.ba_level_descr & BA_LEVEL_NOTIFY) { + prop |= GATT_CHAR_PROP_BIT_NOTIFY; + } + attr_handle_bit = 0x01; + esp_ble_gatts_add_char(service_id, char_uuid, BATTER_LEVEL_PERM, prop); + +} + +/*************************************************************** +** +** Function bas_AddCharDescr +** +** Description add descriptor for battery service if needed +** +****************************************************************/ +void bas_AddCharDescr(UINT16 service_id, UINT16 attr_id) +{ + tBT_UUID uuid; + uuid.len = LEN_UUID_16; + + battery_cb.inst_id --; + tBA_INST *p_inst = &battery_cb.battery_inst[battery_cb.inst_id++]; + /*store the attribute handles*/ + if (attr_handle_bit == 0x01) { + p_inst->ba_level_hdl = attr_id; + } else if (attr_handle_bit == 0x02) { + p_inst->clt_cfg_hdl = attr_id; + } else if (attr_handle_bit == 0x04) { + p_inst->pres_fmt_hdl = attr_id; + } else if (attr_handle_bit == 0x08) { + p_inst->rpt_ref_hdl = attr_id; + } + + + if (ba_reg_info.ba_level_descr != 0) { + if (ba_reg_info.ba_level_descr & BA_LEVEL_NOTIFY) { + uuid.uu.uuid16 = GATT_UUID_CHAR_CLIENT_CONFIG; + ba_reg_info.ba_level_descr &= 0xfe; + attr_handle_bit = 0x02; + esp_ble_gatts_add_char_descr(service_id, (GATT_PERM_READ | GATT_PERM_WRITE), &uuid); + return; + } + + /* need presentation format descriptor? */ + if (ba_reg_info.ba_level_descr & BA_LEVEL_PRE_FMT) { + uuid.uu.uuid16 = GATT_UUID_CHAR_PRESENT_FORMAT; + esp_ble_gatts_add_char_descr(service_id, GATT_PERM_READ, &uuid); + ba_reg_info.ba_level_descr &= 0xfd; + attr_handle_bit = 0x04; + return; + } + /* need report reference format descriptor? */ + if (ba_reg_info.ba_level_descr & BA_LEVEL_RPT_REF) { + uuid.uu.uuid16 = GATT_UUID_RPT_REF_DESCR; + ba_reg_info.ba_level_descr &= 0xfb; + esp_ble_gatts_add_char_descr(service_id, GATT_PERM_READ, &uuid); + attr_handle_bit = 0x08; + return; + } + } + + else { + esp_ble_gatts_start_srvc(service_id); + } + +} + +/*************************************************************** +** +** Function bas_service_cmpl +** +** Description create battery service complete +** +****************************************************************/ +void bas_service_cmpl(UINT16 service_id, esp_gatt_status_t status) +{ + if (status != ESP_GATT_OK) { + battery_cb.inst_id --; + esp_ble_gatts_dele_srvc(service_id); + } + +} +/******************************************************************************* +** +** Function Battery_Rsp +** +** Description Respond to a battery service request +** +*******************************************************************************/ +void Battery_Rsp (UINT32 trans_id, UINT16 conn_id, UINT8 app_id, + esp_gatt_status_t st, UINT8 event, tBA_RSP_DATA *p_rsp) +{ + tBA_INST *p_inst = &battery_cb.battery_inst[0]; + tGATTS_RSP rsp; + UINT8 *pp; + + UINT8 i = 0; + while (i < BA_MAX_INT_NUM) { + if (p_inst->app_id == app_id && p_inst->ba_level_hdl != 0) { + break; + } + i ++; + } + + if (i == BA_MAX_INT_NUM) { + return; + } + + memset(&rsp, 0, sizeof(tGATTS_RSP)); + + if (p_inst->pending_evt == event) { + switch (event) { + case BA_READ_CLT_CFG_REQ: + rsp.attr_value.handle = p_inst->pending_handle; + rsp.attr_value.len = 2; + pp = rsp.attr_value.value; + UINT16_TO_STREAM(pp, p_rsp->clt_cfg); + esp_ble_gatts_send_rsp(conn_id, trans_id, st, &rsp); + //srvc_sr_rsp(p_inst->pending_clcb_idx, st, &rsp); + break; + + case BA_READ_LEVEL_REQ: + rsp.attr_value.handle = p_inst->pending_handle; + rsp.attr_value.len = 1; + pp = rsp.attr_value.value; + UINT8_TO_STREAM(pp, p_rsp->ba_level); + esp_ble_gatts_send_rsp(conn_id, trans_id, st, &rsp); + //srvc_sr_rsp(p_inst->pending_clcb_idx, st, &rsp); + break; + + case BA_WRITE_CLT_CFG_REQ: + esp_ble_gatts_send_rsp(conn_id, trans_id, st, NULL); + //srvc_sr_rsp(p_inst->pending_clcb_idx, st, NULL); + break; + + case BA_READ_RPT_REF_REQ: + rsp.attr_value.handle = p_inst->pending_handle; + rsp.attr_value.len = 2; + pp = rsp.attr_value.value; + UINT8_TO_STREAM(pp, p_rsp->rpt_ref.rpt_id); + UINT8_TO_STREAM(pp, p_rsp->rpt_ref.rpt_type); + esp_ble_gatts_send_rsp(conn_id, trans_id, st, &rsp); + //srvc_sr_rsp(p_inst->pending_clcb_idx, st, &rsp); + break; + + default: + break; + } + // p_inst->pending_clcb_idx = 0; + p_inst->pending_evt = 0; + p_inst->pending_handle = 0; + } + return; +} +/******************************************************************************* +** +** Function Battery_Notify +** +** Description Send battery level notification +** +*******************************************************************************/ +void Battery_Notify (UINT16 conn_id, UINT8 app_id, BD_ADDR remote_bda, UINT8 battery_level) +{ + tBA_INST *p_inst = &battery_cb.battery_inst[0]; + UINT8 i = 0; + + while (i < BA_MAX_INT_NUM) { + if (p_inst->app_id == app_id && p_inst->ba_level_hdl != 0) { + break; + } + i ++; + } + + if (i == BA_MAX_INT_NUM || p_inst->clt_cfg_hdl == 0) { + return; + } + esp_ble_gatts_hdl_val_indica(conn_id, p_inst->ba_level_hdl, 1, &battery_level, false); + //srvc_sr_notify(remote_bda, p_inst->ba_level_hdl, 1, &battery_level); + +} +#endif diff --git a/lib/bt/host/bluedroid/btc/profile/std/battery/include/srvc_battery_int.h b/lib/bt/host/bluedroid/btc/profile/std/battery/include/srvc_battery_int.h new file mode 100644 index 00000000..7aee0ff1 --- /dev/null +++ b/lib/bt/host/bluedroid/btc/profile/std/battery/include/srvc_battery_int.h @@ -0,0 +1,79 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +#ifndef SRVC_BATTERY_INT_H +#define SRVC_BATTERY_INT_H + +#include "common/bt_target.h" +#include "srvc_api.h" +#include "stack/gatt_api.h" + +#ifndef BA_MAX_INT_NUM +#define BA_MAX_INT_NUM 4 +#endif + +#define BATTERY_LEVEL_SIZE 1 + + +typedef struct { + UINT8 app_id; + UINT16 ba_level_hdl; + UINT16 clt_cfg_hdl; + UINT16 rpt_ref_hdl; + UINT16 pres_fmt_hdl; + + tBA_CBACK *p_cback; + + UINT16 pending_handle; + UINT8 pending_clcb_idx; + UINT8 pending_evt; + +} tBA_INST; + +typedef struct { + tBA_INST battery_inst[BA_MAX_INT_NUM]; + UINT8 inst_id; + BOOLEAN enabled; + +} tBATTERY_CB; + +#ifdef __cplusplus +extern "C" { +#endif + +/* Global GATT data */ +#if GATT_DYNAMIC_MEMORY == FALSE +extern tBATTERY_CB battery_cb; +#else +extern tBATTERY_CB *battery_cb_ptr; +#define battery_cb (*battery_cb_ptr) +#endif + + +extern BOOLEAN battery_valid_handle_range(UINT16 handle); + +extern UINT8 battery_s_write_attr_value(UINT8 clcb_idx, tGATT_WRITE_REQ *p_value, + tGATT_STATUS *p_status); +extern UINT8 battery_s_read_attr_value (UINT8 clcb_idx, UINT16 handle, tGATT_VALUE *p_value, BOOLEAN is_long, tGATT_STATUS *p_status); + + + +#ifdef __cplusplus +} +#endif +#endif diff --git a/lib/bt/host/bluedroid/btc/profile/std/dis/dis_profile.c b/lib/bt/host/bluedroid/btc/profile/std/dis/dis_profile.c new file mode 100644 index 00000000..4894f7c1 --- /dev/null +++ b/lib/bt/host/bluedroid/btc/profile/std/dis/dis_profile.c @@ -0,0 +1,291 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2013 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +#include "common/bt_target.h" +//#include "bt_utils.h" +//#include "stack/gatt_api.h" + +#define LOG_TAG "bt_srvc" +//#include "osi/include/log.h" +#include "stdio.h" +#include "stdint.h" +#include "string.h" + +#include "bta/bta_api.h" +#include "bta/bta_gatt_api.h" +#include "device/controller.h" + +#include "gatt_int.h" +#include "common/bt_trace.h" +#include "stack/btm_api.h" +#include "stack/bt_types.h" +#include "dis_api.h" + +#if BLE_INCLUDED == TRUE + +#define UINT64_TO_STREAM(p, u64) {*(p)++ = (UINT8)(u64); *(p)++ = (UINT8)((u64) >> 8);*(p)++ = (UINT8)((u64) >> 16); *(p)++ = (UINT8)((u64) >> 24); \ + *(p)++ = (UINT8)((u64) >> 32); *(p)++ = (UINT8)((u64) >> 40);*(p)++ = (UINT8)((u64) >> 48); *(p)++ = (UINT8)((u64) >> 56);} + +#define STREAM_TO_UINT64(u64, p) {u64 = (((UINT64)(*(p))) + ((((UINT64)(*((p) + 1)))) << 8) + ((((UINT64)(*((p) + 2)))) << 16) + ((((UINT64)(*((p) + 3)))) << 24) \ + + ((((UINT64)(*((p) + 4)))) << 32) + ((((UINT64)(*((p) + 5)))) << 40) + ((((UINT64)(*((p) + 6)))) << 48) + ((((UINT64)(*((p) + 7)))) << 56)); (p) += 8;} + +esp_bt_uuid_t uuid = {LEN_UUID_16, {UUID_SERVCLASS_DEVICE_INFO}}; +UINT16 i = 0; +tDIS_ATTR_MASK dis_mask; +static const UINT16 dis_attr_uuid[DIS_MAX_CHAR_NUM] = { + GATT_UUID_SYSTEM_ID, + GATT_UUID_MODEL_NUMBER_STR, + GATT_UUID_SERIAL_NUMBER_STR, + GATT_UUID_FW_VERSION_STR, + GATT_UUID_HW_VERSION_STR, + GATT_UUID_SW_VERSION_STR, + GATT_UUID_MANU_NAME, + GATT_UUID_IEEE_DATA, + GATT_UUID_PNP_ID +}; + +tDIS_CB dis_cb; + +static tDIS_ATTR_MASK dis_uuid_to_attr(UINT16 uuid) +{ + switch (uuid) { + case GATT_UUID_SYSTEM_ID: + return DIS_ATTR_SYS_ID_BIT; + case GATT_UUID_MODEL_NUMBER_STR: + return DIS_ATTR_MODEL_NUM_BIT; + case GATT_UUID_SERIAL_NUMBER_STR: + return DIS_ATTR_SERIAL_NUM_BIT; + case GATT_UUID_FW_VERSION_STR: + return DIS_ATTR_FW_NUM_BIT; + case GATT_UUID_HW_VERSION_STR: + return DIS_ATTR_HW_NUM_BIT; + case GATT_UUID_SW_VERSION_STR: + return DIS_ATTR_SW_NUM_BIT; + case GATT_UUID_MANU_NAME: + return DIS_ATTR_MANU_NAME_BIT; + case GATT_UUID_IEEE_DATA: + return DIS_ATTR_IEEE_DATA_BIT; + case GATT_UUID_PNP_ID: + return DIS_ATTR_PNP_ID_BIT; + default: + return 0; + }; +} + +/******************************************************************************* +** dis_valid_handle_range +** +** validate a handle to be a DIS attribute handle or not. +*******************************************************************************/ +BOOLEAN dis_valid_handle_range(UINT16 handle) +{ + if (handle >= dis_cb.service_handle && handle <= dis_cb.max_handle) { + return TRUE; + } else { + return FALSE; + } +} +/******************************************************************************* +** dis_write_attr_value +** +** Process write DIS attribute request. +*******************************************************************************/ +UINT8 dis_write_attr_value(tGATT_WRITE_REQ *p_data, esp_gatt_status_t *p_status) +{ + UNUSED(p_data); + + *p_status = GATT_WRITE_NOT_PERMIT; + return ESP_GATT_OK; +} +/******************************************************************************* +** DIS Attributes Database Server Request callback +*******************************************************************************/ + +/******************************************************************************* +** dis_s_read_attr_value +** +** Process read DIS attribute request. +*******************************************************************************/ +void dis_s_read_attr_value (tGATTS_DATA *p_data, tGATT_VALUE *p_value, UINT32 trans_id, UINT16 conn_id) +{ + tDIS_DB_ENTRY *p_db_attr = dis_cb.dis_attr; + UINT8 *p = p_value->value, i, *pp; + UINT16 offset = p_data->read_req.offset; + tGATT_STATUS st = ESP_GATT_NOT_FOUND; + UINT16 handle = p_data->read_req.handle; + bool is_long = p_data->read_req.is_long; + + for (i = 0; i < DIS_MAX_CHAR_NUM; i ++, p_db_attr ++) { + if (handle == p_db_attr->handle) { + if ((p_db_attr->uuid == GATT_UUID_PNP_ID || p_db_attr->uuid == GATT_UUID_SYSTEM_ID) && + is_long == TRUE) { + st = ESP_GATT_NOT_LONG; + break; + } + st = ESP_GATT_NOT_FOUND; + + switch (p_db_attr->uuid) { + case GATT_UUID_MANU_NAME: + case GATT_UUID_MODEL_NUMBER_STR: + case GATT_UUID_SERIAL_NUMBER_STR: + case GATT_UUID_FW_VERSION_STR: + case GATT_UUID_HW_VERSION_STR: + case GATT_UUID_SW_VERSION_STR: + case GATT_UUID_IEEE_DATA: + pp = dis_cb.dis_value.data_string[p_db_attr->uuid - GATT_UUID_MODEL_NUMBER_STR]; + if (pp != NULL) { + if (strlen ((char *)pp) > GATT_MAX_ATTR_LEN) { + p_value->len = GATT_MAX_ATTR_LEN; + } else { + p_value->len = (UINT16)strlen ((char *)pp); + } + } else { + p_value->len = 0; + } + + if (offset > p_value->len) { + st = ESP_GATT_INVALID_OFFSET; + break; + } else { + p_value->len -= offset; + pp += offset; + ARRAY_TO_STREAM(p, pp, p_value->len); + GATT_TRACE_EVENT("GATT_UUID_MANU_NAME len=0x%04x", p_value->len); + } + break; + + + case GATT_UUID_SYSTEM_ID: + UINT64_TO_STREAM(p, dis_cb.dis_value.system_id); /* int_min */ + p_value->len = DIS_SYSTEM_ID_SIZE; + break; + + case GATT_UUID_PNP_ID: + UINT8_TO_STREAM(p, dis_cb.dis_value.pnp_id.vendor_id_src); + UINT16_TO_STREAM(p, dis_cb.dis_value.pnp_id.vendor_id); + UINT16_TO_STREAM(p, dis_cb.dis_value.pnp_id.product_id); + UINT16_TO_STREAM(p, dis_cb.dis_value.pnp_id.product_version); + p_value->len = DIS_PNP_ID_SIZE; + break; + + } + break; + } + } + tGATTS_RSP rsp; + rsp.attr_value = *p_value; + esp_ble_gatts_send_rsp(conn_id, trans_id, st, &rsp); + +} + + +/******************************************************************************* +** +** Function DIS_Init +** +** Description Initialize the Device Information Service Server. +** +*******************************************************************************/ +void DIS_Init (esp_gatts_if_t gatt_if, tDIS_ATTR_MASK dis_attr_mask) +{ + + tGATT_STATUS status; + dis_mask = dis_attr_mask; + if (dis_cb.enabled) { + GATT_TRACE_ERROR("DIS already initalized"); + return; + } + + memset(&dis_cb, 0, sizeof(tDIS_CB)); + + esp_ble_gatts_create_srvc (gatt_if , &uuid, 0, DIS_MAX_ATTR_NUM, TRUE); + +} +/******************************************************************************* +** +** Function dis_AddChar +** +** Description add characteristic for dis +** +*******************************************************************************/ + +void dis_AddChar(UINT16 service_id) +{ + //dis_cb.service_handle = service_id; + //dis_cb.max_handle = service_id + DIS_MAX_ATTR_NUM; + tDIS_DB_ENTRY *p_db_attr = &dis_cb.dis_attr[0]; + while (dis_mask != 0 && i < DIS_MAX_CHAR_NUM) { + uuid.uu.uuid16 = p_db_attr->uuid = dis_attr_uuid[i]; + esp_ble_gatts_add_char(dis_cb.service_handle, &uuid, GATT_PERM_READ, + GATT_CHAR_PROP_BIT_READ); + p_db_attr ++; + i ++; + dis_mask >>= 1; + } + /*start service*/ + esp_ble_gatts_start_srvc(dis_cb.service_handle); + dis_cb.enabled = TRUE; +} +/******************************************************************************* +** +** Function DIS_SrUpdate +** +** Description Update the DIS server attribute values +** +*******************************************************************************/ +tDIS_STATUS DIS_SrUpdate(tDIS_ATTR_BIT dis_attr_bit, tDIS_ATTR *p_info) +{ + UINT8 i = 1; + tDIS_STATUS st = DIS_SUCCESS; + + if (dis_attr_bit & DIS_ATTR_SYS_ID_BIT) { + dis_cb.dis_value.system_id = p_info->system_id; + } else if (dis_attr_bit & DIS_ATTR_PNP_ID_BIT) { + dis_cb.dis_value.pnp_id.vendor_id = p_info->pnp_id.vendor_id; + dis_cb.dis_value.pnp_id.vendor_id_src = p_info->pnp_id.vendor_id_src; + dis_cb.dis_value.pnp_id.product_id = p_info->pnp_id.product_id; + dis_cb.dis_value.pnp_id.product_version = p_info->pnp_id.product_version; + } else { + st = DIS_ILLEGAL_PARAM; + + while (dis_attr_bit && i < (DIS_MAX_CHAR_NUM - 1 )) { + if (dis_attr_bit & (UINT16)(1 << i)) { + if (dis_cb.dis_value.data_string[i - 1] != NULL) { + osi_free(dis_cb.dis_value.data_string[i - 1]); + } + /* coverity[OVERRUN-STATIC] False-positive : when i = 8, (1 << i) == DIS_ATTR_PNP_ID_BIT, and it will never come down here + CID 49902: Out-of-bounds read (OVERRUN_STATIC) + Overrunning static array "dis_cb.dis_value.data_string", with 7 elements, at position 7 with index variable "i". + */ + if ((dis_cb.dis_value.data_string[i - 1] = (UINT8 *)osi_malloc((UINT16)(p_info->data_str.len + 1))) != NULL) { + + memcpy(dis_cb.dis_value.data_string[i - 1], p_info->data_str.p_data, p_info->data_str.len); + dis_cb.dis_value.data_string[i - 1][p_info->data_str.len] = 0; /* make sure null terminate */ + st = DIS_SUCCESS; + } else { + st = DIS_NO_RESOURCES; + } + + break; + } + i ++; + } + } + return st; +} +#endif /* BLE_INCLUDED */ diff --git a/lib/bt/host/bluedroid/btc/profile/std/dis/include/srvc_dis_int.h b/lib/bt/host/bluedroid/btc/profile/std/dis/include/srvc_dis_int.h new file mode 100644 index 00000000..4d5913ab --- /dev/null +++ b/lib/bt/host/bluedroid/btc/profile/std/dis/include/srvc_dis_int.h @@ -0,0 +1,82 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +#ifndef SRVC_DIS_INT_H +#define SRVC_DIS_INT_H + +#include "common/bt_target.h" +#include "srvc_api.h" +#include "stack/gatt_api.h" + +#define DIS_MAX_CHAR_NUM 9 + + +typedef struct { + UINT16 uuid; + UINT16 handle; +} tDIS_DB_ENTRY; + +#define DIS_SYSTEM_ID_SIZE 8 +#define DIS_PNP_ID_SIZE 7 + + + +typedef struct { + tDIS_DB_ENTRY dis_attr[DIS_MAX_CHAR_NUM]; + tDIS_VALUE dis_value; + + tDIS_READ_CBACK *p_read_dis_cback; + + UINT16 service_handle; + UINT16 max_handle; + + BOOLEAN enabled; + + UINT8 dis_read_uuid_idx; + + tDIS_ATTR_MASK request_mask; +} tDIS_CB; + + + +#ifdef __cplusplus +extern "C" { +#endif + +/* Global GATT data */ +#if GATT_DYNAMIC_MEMORY == FALSE +extern tDIS_CB dis_cb; +#else +extern tDIS_CB *dis_cb_ptr; +#define dis_cb (*dis_cb_ptr) +#endif + + +extern BOOLEAN dis_valid_handle_range(UINT16 handle); +extern UINT8 dis_read_attr_value (UINT8 clcb_idx, UINT16 handle, tGATT_VALUE *p_value, + BOOLEAN is_long, tGATT_STATUS *p_status); +extern UINT8 dis_write_attr_value(tGATT_WRITE_REQ *p_data, tGATT_STATUS *p_status); + +extern void dis_c_cmpl_cback (tSRVC_CLCB *p_clcb, tGATTC_OPTYPE op, + tGATT_STATUS status, tGATT_CL_COMPLETE *p_data); + + +#ifdef __cplusplus +} +#endif +#endif diff --git a/lib/bt/host/bluedroid/btc/profile/std/gap/bta_gap_bt_co.c b/lib/bt/host/bluedroid/btc/profile/std/gap/bta_gap_bt_co.c new file mode 100644 index 00000000..e4c8ebd7 --- /dev/null +++ b/lib/bt/host/bluedroid/btc/profile/std/gap/bta_gap_bt_co.c @@ -0,0 +1,32 @@ +/* + * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "btc_gap_bt.h" +#include "btc/btc_util.h" + +#if (BTC_GAP_BT_INCLUDED == TRUE) +void btc_gap_bt_config_eir_cmpl_callback (uint8_t status, uint8_t eir_type_num, uint8_t *eir_type) +{ + esp_bt_gap_cb_param_t param; + bt_status_t ret; + btc_msg_t msg; + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_GAP_BT; + msg.act = BTC_GAP_BT_CONFIG_EIR_DATA_EVT; + + param.config_eir_data.stat = btc_bta_status_to_esp_status(status); + param.config_eir_data.eir_type_num = eir_type_num; + memcpy(param.config_eir_data.eir_type, eir_type, eir_type_num); + + ret = btc_transfer_context(&msg, ¶m, + sizeof(esp_bt_gap_cb_param_t), NULL, NULL); + + if (ret != BT_STATUS_SUCCESS) { + BTC_TRACE_ERROR("%s btc_transfer_context failed\n", __func__); + } +} +#endif /// (BTC_GAP_BT_INCLUDED == TRUE) diff --git a/lib/bt/host/bluedroid/btc/profile/std/gap/btc_gap_ble.c b/lib/bt/host/bluedroid/btc/profile/std/gap/btc_gap_ble.c new file mode 100644 index 00000000..2e6c7b8d --- /dev/null +++ b/lib/bt/host/bluedroid/btc/profile/std/gap/btc_gap_ble.c @@ -0,0 +1,2336 @@ +/* + * SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include "osi/allocator.h" +#include "stack/bt_types.h" +#include "common/bt_defs.h" +#include "common/bt_target.h" +#include "bta/bta_api.h" +#include "bta/bta_dm_co.h" +#include "btc/btc_task.h" +#include "btc/btc_manage.h" +#include "btc_gap_ble.h" +#include "btc_gatt_util.h" +#include "esp_bt_defs.h" +#include "esp_gap_ble_api.h" +#include "btc/btc_ble_storage.h" +#include "btc/btc_dm.h" +#include "btc/btc_util.h" +#include "osi/mutex.h" +#include "osi/thread.h" +#include "osi/pkt_queue.h" +#if (BT_CONTROLLER_INCLUDED == TRUE) +#include "esp_bt.h" +#endif + +#if (BLE_INCLUDED == TRUE) +#if (BLE_42_FEATURE_SUPPORT == TRUE) +#if BTC_DYNAMIC_MEMORY == FALSE +static tBTA_BLE_ADV_DATA gl_bta_adv_data; +static tBTA_BLE_ADV_DATA gl_bta_scan_rsp_data; +#else +tBTA_BLE_ADV_DATA *gl_bta_adv_data_ptr; +tBTA_BLE_ADV_DATA *gl_bta_scan_rsp_data_ptr; +#endif +#endif // #if (BLE_42_FEATURE_SUPPORT == TRUE) + +#if SCAN_QUEUE_CONGEST_CHECK +static list_t *adv_filter_list; +static osi_mutex_t adv_list_lock; +bool btc_check_adv_list(uint8_t * addr, uint8_t addr_type); +uint32_t btc_get_adv_list_length(void); +void btc_adv_list_refresh(void); +void btc_adv_list_lock(void); +void btc_adv_list_unlock(void); +static uint16_t btc_adv_list_count = 0; + +#define BTC_ADV_LIST_MAX_LENGTH 50 +#define BTC_ADV_LIST_MAX_COUNT 200 +#endif + +#define BTC_GAP_BLE_ADV_RPT_QUEUE_IDX (1) +#define BTC_GAP_BLE_ADV_RPT_BATCH_SIZE (10) +#define BTC_GAP_BLE_ADV_RPT_QUEUE_LEN_MAX (200) + +#if (BLE_42_FEATURE_SUPPORT == TRUE) +typedef struct { + struct pkt_queue *adv_rpt_queue; + struct osi_event *adv_rpt_ready; +} btc_gap_ble_env_t; + +static btc_gap_ble_env_t btc_gap_ble_env; +#endif + +static inline void btc_gap_ble_cb_to_app(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) +{ + esp_gap_ble_cb_t btc_gap_ble_cb = (esp_gap_ble_cb_t)btc_profile_cb_get(BTC_PID_GAP_BLE); + if (btc_gap_ble_cb) { + btc_gap_ble_cb(event, param); + } +} + +static void btc_gap_ble_get_dev_name_callback(UINT8 status, char *name) +{ + esp_ble_gap_cb_param_t param; + bt_status_t ret; + btc_msg_t msg = {0}; + + memset(¶m, 0, sizeof(esp_ble_gap_cb_param_t)); + + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_GAP_BLE; + msg.act = ESP_GAP_BLE_GET_DEV_NAME_COMPLETE_EVT; + + param.get_dev_name_cmpl.status = btc_btm_status_to_esp_status(status); + param.get_dev_name_cmpl.name = (char *)osi_malloc(BTC_MAX_LOC_BD_NAME_LEN + 1); + if (param.get_dev_name_cmpl.name) { + BCM_STRNCPY_S(param.get_dev_name_cmpl.name, name, BTC_MAX_LOC_BD_NAME_LEN); + param.get_dev_name_cmpl.name[BTC_MAX_LOC_BD_NAME_LEN] = '\0'; + } else { + param.get_dev_name_cmpl.status = ESP_BT_STATUS_NOMEM; + } + + ret = btc_transfer_context(&msg, ¶m, sizeof(esp_ble_gap_cb_param_t), NULL, NULL); + if (ret != BT_STATUS_SUCCESS) { + BTC_TRACE_ERROR("%s btc_transfer_context failed\n", __func__); + } +} + +#if (BLE_42_FEATURE_SUPPORT == TRUE) +static void btc_gap_adv_point_cleanup(void **buf) +{ + if (NULL == *buf) { + return; + } + osi_free(*buf); + *buf = NULL; +} + + +static void btc_cleanup_adv_data(tBTA_BLE_ADV_DATA *bta_adv_data) +{ + if (bta_adv_data == NULL) { + return; + } + + // Manufacturer data cleanup + if (bta_adv_data->p_manu != NULL) { + btc_gap_adv_point_cleanup((void **) &bta_adv_data->p_manu->p_val); + btc_gap_adv_point_cleanup((void **) &bta_adv_data->p_manu); + } + + // Proprietary data cleanup + if (bta_adv_data->p_proprietary != NULL) { + int i = 0; + tBTA_BLE_PROP_ELEM *p_elem = bta_adv_data->p_proprietary->p_elem; + while (i++ != bta_adv_data->p_proprietary->num_elem + && p_elem) { + btc_gap_adv_point_cleanup((void **) &p_elem->p_val); + ++p_elem; + } + + btc_gap_adv_point_cleanup((void **) &bta_adv_data->p_proprietary->p_elem); + btc_gap_adv_point_cleanup((void **) &bta_adv_data->p_proprietary); + } + + // Service list cleanup + if (bta_adv_data->p_services != NULL) { + btc_gap_adv_point_cleanup((void **) &bta_adv_data->p_services->p_uuid); + btc_gap_adv_point_cleanup((void **) &bta_adv_data->p_services); + } + + // Service data cleanup + if (bta_adv_data->p_service_data != NULL) { + btc_gap_adv_point_cleanup((void **) &bta_adv_data->p_service_data->p_val); + btc_gap_adv_point_cleanup((void **) &bta_adv_data->p_service_data); + } + + btc_gap_adv_point_cleanup((void **) &bta_adv_data->p_services_128b); + + if (bta_adv_data->p_service_32b != NULL) { + btc_gap_adv_point_cleanup((void **) &bta_adv_data->p_service_32b->p_uuid); + btc_gap_adv_point_cleanup((void **) &bta_adv_data->p_service_32b); + } + + if (bta_adv_data->p_sol_services != NULL) { + btc_gap_adv_point_cleanup((void **) &bta_adv_data->p_sol_services->p_uuid); + btc_gap_adv_point_cleanup((void **) &bta_adv_data->p_sol_services); + } + + if (bta_adv_data->p_sol_service_32b != NULL) { + btc_gap_adv_point_cleanup((void **) &bta_adv_data->p_sol_service_32b->p_uuid); + btc_gap_adv_point_cleanup((void **) &bta_adv_data->p_sol_service_32b); + } + + btc_gap_adv_point_cleanup((void **) &bta_adv_data->p_sol_service_128b); +} + +static void btc_to_bta_adv_data(esp_ble_adv_data_t *p_adv_data, tBTA_BLE_ADV_DATA *bta_adv_data, uint32_t *data_mask) +{ + uint32_t mask; + + btc_cleanup_adv_data(bta_adv_data); + + memset(bta_adv_data, 0, sizeof(tBTA_BLE_ADV_DATA)); + mask = 0; + + if (p_adv_data->flag != 0) { + mask = BTM_BLE_AD_BIT_FLAGS; + bta_adv_data->flag = p_adv_data->flag; + } + + if (p_adv_data->include_name) { + mask |= BTM_BLE_AD_BIT_DEV_NAME; + } + + if (p_adv_data->include_txpower) { + mask |= BTM_BLE_AD_BIT_TX_PWR; +#if (BT_CONTROLLER_INCLUDED == TRUE) + bta_adv_data->tx_power = esp_ble_tx_power_get(ESP_BLE_PWR_TYPE_ADV); +#else + bta_adv_data->tx_power = 0; +#endif + } + + if (p_adv_data->min_interval > 0 && p_adv_data->max_interval > 0 && + p_adv_data->max_interval >= p_adv_data->min_interval) { + mask |= BTM_BLE_AD_BIT_INT_RANGE; + bta_adv_data->int_range.low = p_adv_data->min_interval; + bta_adv_data->int_range.hi = p_adv_data->max_interval; + } + + if (p_adv_data->include_txpower) { + //TODO + } + + if (p_adv_data->appearance != 0) { + mask |= BTM_BLE_AD_BIT_APPEARANCE; + bta_adv_data->appearance = p_adv_data->appearance; + } + + if (p_adv_data->manufacturer_len > 0 && p_adv_data->p_manufacturer_data != NULL) { + bta_adv_data->p_manu = osi_malloc(sizeof(tBTA_BLE_MANU)); + if (bta_adv_data->p_manu != NULL) { + bta_adv_data->p_manu->p_val = osi_malloc(p_adv_data->manufacturer_len); + if (bta_adv_data->p_manu->p_val != NULL) { + mask |= BTM_BLE_AD_BIT_MANU; + bta_adv_data->p_manu->len = p_adv_data->manufacturer_len; + memcpy(bta_adv_data->p_manu->p_val, p_adv_data->p_manufacturer_data, p_adv_data->manufacturer_len); + } + } + } + + tBTA_BLE_PROP_ELEM *p_elem_service_data = NULL; + if (p_adv_data->service_data_len > 0 && p_adv_data->p_service_data != NULL) { + p_elem_service_data = osi_malloc(sizeof(tBTA_BLE_PROP_ELEM)); + if (p_elem_service_data != NULL) { + p_elem_service_data->p_val = osi_malloc(p_adv_data->service_data_len); + if (p_elem_service_data->p_val != NULL) { + p_elem_service_data->adv_type = BTM_BLE_AD_TYPE_SERVICE_DATA; + p_elem_service_data->len = p_adv_data->service_data_len; + memcpy(p_elem_service_data->p_val, p_adv_data->p_service_data, + p_adv_data->service_data_len); + } else { + osi_free(p_elem_service_data); + p_elem_service_data = NULL; + } + } + } + + if (NULL != p_elem_service_data) { + bta_adv_data->p_proprietary = osi_malloc(sizeof(tBTA_BLE_PROPRIETARY)); + if (NULL != bta_adv_data->p_proprietary) { + tBTA_BLE_PROP_ELEM *p_elem = NULL; + tBTA_BLE_PROPRIETARY *p_prop = bta_adv_data->p_proprietary; + p_prop->num_elem = 0; + mask |= BTM_BLE_AD_BIT_PROPRIETARY; + p_prop->num_elem = 1; + p_prop->p_elem = osi_malloc(sizeof(tBTA_BLE_PROP_ELEM) * p_prop->num_elem); + p_elem = p_prop->p_elem; + if (NULL != p_elem) { + memcpy(p_elem++, p_elem_service_data, sizeof(tBTA_BLE_PROP_ELEM)); + } + } + osi_free(p_elem_service_data); + } + + if (p_adv_data->service_uuid_len && p_adv_data->p_service_uuid) { + UINT16 *p_uuid_out16 = NULL; + UINT32 *p_uuid_out32 = NULL; + for (int position = 0; position < p_adv_data->service_uuid_len; position += LEN_UUID_128) { + tBT_UUID bt_uuid; + + btc128_to_bta_uuid(&bt_uuid, p_adv_data->p_service_uuid + position); + + switch (bt_uuid.len) { + case (LEN_UUID_16): { + if (NULL == bta_adv_data->p_services) { + bta_adv_data->p_services = osi_malloc(sizeof(tBTA_BLE_SERVICE)); + bta_adv_data->p_services->list_cmpl = FALSE; + bta_adv_data->p_services->num_service = 0; + bta_adv_data->p_services->p_uuid = osi_malloc(p_adv_data->service_uuid_len / LEN_UUID_128 * LEN_UUID_16); + p_uuid_out16 = bta_adv_data->p_services->p_uuid; + } + + if (NULL != bta_adv_data->p_services->p_uuid) { + BTC_TRACE_DEBUG("%s - In 16-UUID_data", __FUNCTION__); + mask |= BTM_BLE_AD_BIT_SERVICE; + ++bta_adv_data->p_services->num_service; + *p_uuid_out16++ = bt_uuid.uu.uuid16; + } + break; + } + + case (LEN_UUID_32): { + if (NULL == bta_adv_data->p_service_32b) { + bta_adv_data->p_service_32b = + osi_malloc(sizeof(tBTA_BLE_32SERVICE)); + bta_adv_data->p_service_32b->list_cmpl = FALSE; + bta_adv_data->p_service_32b->num_service = 0; + bta_adv_data->p_service_32b->p_uuid = + osi_malloc(p_adv_data->service_uuid_len / LEN_UUID_128 * LEN_UUID_32); + p_uuid_out32 = bta_adv_data->p_service_32b->p_uuid; + } + + if (NULL != bta_adv_data->p_service_32b->p_uuid) { + BTC_TRACE_DEBUG("%s - In 32-UUID_data", __FUNCTION__); + mask |= BTM_BLE_AD_BIT_SERVICE_32; + ++bta_adv_data->p_service_32b->num_service; + *p_uuid_out32++ = bt_uuid.uu.uuid32; + } + break; + } + + case (LEN_UUID_128): { + /* Currently, only one 128-bit UUID is supported */ + if (NULL == bta_adv_data->p_services_128b) { + bta_adv_data->p_services_128b = + osi_malloc(sizeof(tBTA_BLE_128SERVICE)); + if (NULL != bta_adv_data->p_services_128b) { + BTC_TRACE_DEBUG("%s - In 128-UUID_data", __FUNCTION__); + mask |= BTM_BLE_AD_BIT_SERVICE_128; + memcpy(bta_adv_data->p_services_128b->uuid128, + bt_uuid.uu.uuid128, LEN_UUID_128); + BTC_TRACE_DEBUG("%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x", bt_uuid.uu.uuid128[0], + bt_uuid.uu.uuid128[1], bt_uuid.uu.uuid128[2], bt_uuid.uu.uuid128[3], + bt_uuid.uu.uuid128[4], bt_uuid.uu.uuid128[5], bt_uuid.uu.uuid128[6], + bt_uuid.uu.uuid128[7], bt_uuid.uu.uuid128[8], bt_uuid.uu.uuid128[9], + bt_uuid.uu.uuid128[10], bt_uuid.uu.uuid128[11], bt_uuid.uu.uuid128[12], + bt_uuid.uu.uuid128[13], bt_uuid.uu.uuid128[14], bt_uuid.uu.uuid128[15]); + bta_adv_data->p_services_128b->list_cmpl = TRUE; + } + } + break; + } + + default: + break; + } + } + } + + *data_mask = mask; +} + +static void btc_adv_data_callback(tBTA_STATUS status) +{ + esp_ble_gap_cb_param_t param; + bt_status_t ret; + btc_msg_t msg = {0}; + + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_GAP_BLE; + msg.act = ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT; + param.adv_data_cmpl.status = status; + + ret = btc_transfer_context(&msg, ¶m, + sizeof(esp_ble_gap_cb_param_t), NULL, NULL); + + if (ret != BT_STATUS_SUCCESS) { + BTC_TRACE_ERROR("%s btc_transfer_context failed\n", __func__); + } +} + +static void btc_scan_rsp_data_callback(tBTA_STATUS status) +{ + esp_ble_gap_cb_param_t param; + bt_status_t ret; + btc_msg_t msg = {0}; + + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_GAP_BLE; + msg.act = ESP_GAP_BLE_SCAN_RSP_DATA_SET_COMPLETE_EVT; + param.scan_rsp_data_cmpl.status = status; + + ret = btc_transfer_context(&msg, ¶m, + sizeof(esp_ble_gap_cb_param_t), NULL, NULL); + + if (ret != BT_STATUS_SUCCESS) { + BTC_TRACE_ERROR("%s btc_transfer_context failed\n", __func__); + } +} + +static void btc_adv_data_raw_callback(tBTA_STATUS status) +{ + esp_ble_gap_cb_param_t param; + bt_status_t ret; + btc_msg_t msg = {0}; + + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_GAP_BLE; + msg.act = ESP_GAP_BLE_ADV_DATA_RAW_SET_COMPLETE_EVT; + param.adv_data_raw_cmpl.status = status; + + ret = btc_transfer_context(&msg, ¶m, + sizeof(esp_ble_gap_cb_param_t), NULL, NULL); + + if (ret != BT_STATUS_SUCCESS) { + BTC_TRACE_ERROR("%s btc_transfer_context failed\n", __func__); + } +} + +static void btc_scan_rsp_data_raw_callback(tBTA_STATUS status) +{ + esp_ble_gap_cb_param_t param; + bt_status_t ret; + btc_msg_t msg = {0}; + + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_GAP_BLE; + msg.act = ESP_GAP_BLE_SCAN_RSP_DATA_RAW_SET_COMPLETE_EVT; + param.scan_rsp_data_raw_cmpl.status = status; + + ret = btc_transfer_context(&msg, ¶m, + sizeof(esp_ble_gap_cb_param_t), NULL, NULL); + + if (ret != BT_STATUS_SUCCESS) { + BTC_TRACE_ERROR("%s btc_transfer_context failed\n", __func__); + } +} + +static void btc_ble_set_adv_data(esp_ble_adv_data_t *adv_data, + tBTA_SET_ADV_DATA_CMPL_CBACK p_adv_data_cback) +{ + tBTA_BLE_AD_MASK data_mask = 0; + + if (!adv_data->set_scan_rsp) { + btc_to_bta_adv_data(adv_data, &gl_bta_adv_data, &data_mask); + BTA_DmBleSetAdvConfig(data_mask, &gl_bta_adv_data, p_adv_data_cback); + } else { + btc_to_bta_adv_data(adv_data, &gl_bta_scan_rsp_data, &data_mask); + BTA_DmBleSetScanRsp(data_mask, &gl_bta_scan_rsp_data, p_adv_data_cback); + } +} + +static void btc_ble_set_adv_data_raw(uint8_t *raw_adv, uint32_t raw_adv_len, + tBTA_SET_ADV_DATA_CMPL_CBACK p_adv_data_cback) +{ + BTA_DmBleSetAdvConfigRaw(raw_adv, raw_adv_len, p_adv_data_cback); +} + +static void btc_ble_set_scan_rsp_data_raw(uint8_t *raw_scan_rsp, uint32_t raw_scan_rsp_len, + tBTA_SET_ADV_DATA_CMPL_CBACK p_scan_rsp_data_cback) +{ + BTA_DmBleSetScanRspRaw(raw_scan_rsp, raw_scan_rsp_len, p_scan_rsp_data_cback); +} + +static void btc_start_adv_callback(uint8_t status) +{ + esp_ble_gap_cb_param_t param; + bt_status_t ret; + btc_msg_t msg = {0}; + + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_GAP_BLE; + msg.act = ESP_GAP_BLE_ADV_START_COMPLETE_EVT; + param.adv_start_cmpl.status = btc_hci_to_esp_status(status); + + ret = btc_transfer_context(&msg, ¶m, + sizeof(esp_ble_gap_cb_param_t), NULL, NULL); + + if (ret != BT_STATUS_SUCCESS) { + BTC_TRACE_ERROR("%s btc_transfer_context failed\n", __func__); + } +} + +static void btc_stop_adv_callback(uint8_t status) +{ + esp_ble_gap_cb_param_t param; + bt_status_t ret; + btc_msg_t msg = {0}; + + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_GAP_BLE; + msg.act = ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT; + param.adv_stop_cmpl.status = btc_hci_to_esp_status(status); + + ret = btc_transfer_context(&msg, ¶m, + sizeof(esp_ble_gap_cb_param_t), NULL, NULL); + + if (ret != BT_STATUS_SUCCESS) { + BTC_TRACE_ERROR("%s btc_transfer_context failed\n", __func__); + } +} + +static void btc_clear_adv_callback(uint8_t status) +{ + esp_ble_gap_cb_param_t param; + bt_status_t ret; + btc_msg_t msg = {0}; + + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_GAP_BLE; + msg.act = ESP_GAP_BLE_ADV_CLEAR_COMPLETE_EVT; + param.adv_clear_cmpl.status = btc_hci_to_esp_status(status); + + ret = btc_transfer_context(&msg, ¶m, + sizeof(esp_ble_gap_cb_param_t), NULL, NULL); + + if (ret != BT_STATUS_SUCCESS) { + BTC_TRACE_ERROR("%s btc_transfer_context failed\n", __func__); + } +} + +void btc_update_duplicate_exceptional_list_callback(tBTA_STATUS status, uint8_t subcode, uint32_t length, uint8_t *device_info) +{ + esp_ble_gap_cb_param_t param; + bt_status_t ret; + btc_msg_t msg = {0}; + + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_GAP_BLE; + msg.act = ESP_GAP_BLE_UPDATE_DUPLICATE_EXCEPTIONAL_LIST_COMPLETE_EVT; + param.update_duplicate_exceptional_list_cmpl.status = status; + param.update_duplicate_exceptional_list_cmpl.subcode = subcode; + if(length > sizeof(param.update_duplicate_exceptional_list_cmpl.device_info)) { + length = sizeof(param.update_duplicate_exceptional_list_cmpl.device_info); + } + param.update_duplicate_exceptional_list_cmpl.length = length; + memcpy(param.update_duplicate_exceptional_list_cmpl.device_info, device_info, length); + ret = btc_transfer_context(&msg, ¶m, sizeof(esp_ble_gap_cb_param_t), NULL, NULL); + + if (ret != BT_STATUS_SUCCESS) { + BTC_TRACE_ERROR("%s btc_transfer_context failed\n", __func__); + } +} + +static void btc_ble_update_duplicate_exceptional_list(uint8_t subcode, uint32_t info_type, BD_ADDR device_info, + tBTA_UPDATE_DUPLICATE_EXCEPTIONAL_LIST_CMPL_CBACK p_update_duplicate_ignore_list_cback) +{ + BTA_DmUpdateDuplicateExceptionalList(subcode, info_type, device_info, p_update_duplicate_ignore_list_cback); +} + +static void btc_ble_start_advertising (esp_ble_adv_params_t *ble_adv_params, tBTA_START_ADV_CMPL_CBACK start_adv_cback) +{ + tBLE_BD_ADDR peer_addr; + esp_bt_status_t status = ESP_BT_STATUS_SUCCESS; + if (!BLE_ISVALID_PARAM(ble_adv_params->adv_int_min, BTM_BLE_ADV_INT_MIN, BTM_BLE_ADV_INT_MAX) || + !BLE_ISVALID_PARAM(ble_adv_params->adv_int_max, BTM_BLE_ADV_INT_MIN, BTM_BLE_ADV_INT_MAX)) { + status = ESP_BT_STATUS_PARM_INVALID; + BTC_TRACE_ERROR("Invalid advertisting interval parameters.\n"); + } + + if ((ble_adv_params->adv_type < ADV_TYPE_IND) || + (ble_adv_params->adv_type > ADV_TYPE_DIRECT_IND_LOW) ) { + status = ESP_BT_STATUS_PARM_INVALID; + BTC_TRACE_ERROR("Invalid advertisting type parameters.\n"); + } + + if ((ble_adv_params->adv_filter_policy < ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY) || + (ble_adv_params->adv_filter_policy > ADV_FILTER_ALLOW_SCAN_WLST_CON_WLST) ) { + status = ESP_BT_STATUS_PARM_INVALID; + BTC_TRACE_ERROR("Invalid advertisting type parameters.\n"); + } + + if((ble_adv_params->channel_map | ADV_CHNL_ALL) != ADV_CHNL_ALL || ble_adv_params->channel_map == 0) { + status = ESP_BT_STATUS_PARM_INVALID; + BTC_TRACE_ERROR("Invalid advertisting channel map parameters.\n"); + } + if (!BLE_ISVALID_PARAM(ble_adv_params->peer_addr_type, BLE_ADDR_TYPE_PUBLIC, BLE_ADDR_TYPE_RANDOM)) { + status = ESP_BT_STATUS_PARM_INVALID; + BTC_TRACE_ERROR("Invalid advertisting peer address type parameters.\n"); + } + if(status != ESP_BT_STATUS_SUCCESS) { + if(start_adv_cback) { + start_adv_cback(status); + } + return; + } + + BTC_TRACE_DEBUG("API_Ble_AppStartAdvertising\n"); + + memcpy(peer_addr.bda, ble_adv_params->peer_addr, ESP_BD_ADDR_LEN); + peer_addr.type = ble_adv_params->peer_addr_type; + BTA_DmSetBleAdvParamsAll(ble_adv_params->adv_int_min, + ble_adv_params->adv_int_max, + ble_adv_params->adv_type, + ble_adv_params->own_addr_type, + ble_adv_params->channel_map, + ble_adv_params->adv_filter_policy, + &peer_addr, + start_adv_cback); +} + + +static void btc_scan_params_callback(tGATT_IF gatt_if, tBTM_STATUS status) +{ + esp_ble_gap_cb_param_t param; + bt_status_t ret; + btc_msg_t msg = {0}; + + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_GAP_BLE; + msg.act = ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT; + param.scan_param_cmpl.status = status; + + ret = btc_transfer_context(&msg, ¶m, + sizeof(esp_ble_gap_cb_param_t), NULL, NULL); + + if (ret != BT_STATUS_SUCCESS) { + BTC_TRACE_ERROR("%s btc_transfer_context failed\n", __func__); + } +} + +static void btc_ble_set_scan_params(esp_ble_scan_params_t *scan_params, tBLE_SCAN_PARAM_SETUP_CBACK scan_param_setup_cback) +{ + if (BLE_ISVALID_PARAM(scan_params->scan_interval, BTM_BLE_SCAN_INT_MIN, BTM_BLE_SCAN_INT_MAX) && + BLE_ISVALID_PARAM(scan_params->scan_window, BTM_BLE_SCAN_WIN_MIN, BTM_BLE_SCAN_WIN_MAX) && + BLE_ISVALID_PARAM(scan_params->own_addr_type, BLE_ADDR_TYPE_PUBLIC, BLE_ADDR_TYPE_RPA_RANDOM) && + BLE_ISVALID_PARAM(scan_params->scan_filter_policy, BLE_SCAN_FILTER_ALLOW_ALL, BLE_SCAN_FILTER_ALLOW_WLIST_RPA_DIR) && + BLE_ISVALID_PARAM(scan_params->scan_duplicate, BLE_SCAN_DUPLICATE_DISABLE, BLE_SCAN_DUPLICATE_MAX -1) && + (scan_params->scan_type == BTM_BLE_SCAN_MODE_ACTI || scan_params->scan_type == BTM_BLE_SCAN_MODE_PASS)) { + BTA_DmSetBleScanFilterParams(ESP_DEFAULT_GATT_IF, /*client_if*/ + scan_params->scan_interval, + scan_params->scan_window, + scan_params->scan_type, + scan_params->scan_filter_policy, + scan_params->own_addr_type, + scan_params->scan_duplicate, + scan_param_setup_cback); + } else { + btc_scan_params_callback(ESP_DEFAULT_GATT_IF, BTM_ILLEGAL_VALUE); + } +} + +static void btc_gap_ble_adv_pkt_handler(void *arg) +{ + btc_gap_ble_env_t *p_env = &btc_gap_ble_env; + size_t pkts_to_process = pkt_queue_length(p_env->adv_rpt_queue); + if (pkts_to_process > BTC_GAP_BLE_ADV_RPT_BATCH_SIZE) { + pkts_to_process = BTC_GAP_BLE_ADV_RPT_BATCH_SIZE; + } + + for (size_t i = 0; i < pkts_to_process; i++) { + pkt_linked_item_t *linked_pkt = pkt_queue_dequeue(p_env->adv_rpt_queue); + if (linked_pkt != NULL) { + esp_ble_gap_cb_param_t *param = (esp_ble_gap_cb_param_t *)(linked_pkt->data); + btc_gap_ble_cb_to_app(ESP_GAP_BLE_SCAN_RESULT_EVT, param); + osi_free(linked_pkt); + } + } + + if (pkt_queue_length(p_env->adv_rpt_queue) != 0) { + osi_thread_post_event(p_env->adv_rpt_ready, OSI_THREAD_MAX_TIMEOUT); + } +} + +static void btc_process_adv_rpt_pkt(tBTA_DM_SEARCH_EVT event, tBTA_DM_SEARCH *p_data) +{ +#if SCAN_QUEUE_CONGEST_CHECK + if(btc_check_queue_is_congest()) { + BTC_TRACE_DEBUG("BtcQueue is congested"); + if(btc_get_adv_list_length() > BTC_ADV_LIST_MAX_LENGTH || btc_adv_list_count > BTC_ADV_LIST_MAX_COUNT) { + btc_adv_list_refresh(); + btc_adv_list_count = 0; + } + if(btc_check_adv_list(p_data->inq_res.bd_addr, p_data->inq_res.ble_addr_type)) { + return; + } + } + btc_adv_list_count ++; +#endif + + // drop ADV packets if data queue length goes above threshold + btc_gap_ble_env_t *p_env = &btc_gap_ble_env; + if (pkt_queue_length(p_env->adv_rpt_queue) >= BTC_GAP_BLE_ADV_RPT_QUEUE_LEN_MAX) { + return; + } + + pkt_linked_item_t *linked_pkt = osi_calloc(BT_PKT_LINKED_HDR_SIZE + sizeof(esp_ble_gap_cb_param_t)); + if (linked_pkt == NULL) { + return; + } + + struct ble_scan_result_evt_param *scan_rst = (struct ble_scan_result_evt_param *)linked_pkt->data; + + do { + scan_rst->search_evt = event; + bdcpy(scan_rst->bda, p_data->inq_res.bd_addr); + scan_rst->dev_type = p_data->inq_res.device_type; + scan_rst->rssi = p_data->inq_res.rssi; + scan_rst->ble_addr_type = p_data->inq_res.ble_addr_type; + scan_rst->ble_evt_type = p_data->inq_res.ble_evt_type; + scan_rst->flag = p_data->inq_res.flag; + scan_rst->num_resps = 1; + scan_rst->adv_data_len = p_data->inq_res.adv_data_len; + scan_rst->scan_rsp_len = p_data->inq_res.scan_rsp_len; + memcpy(scan_rst->ble_adv, p_data->inq_res.p_eir, sizeof(scan_rst->ble_adv)); + } while (0); + + pkt_queue_enqueue(p_env->adv_rpt_queue, linked_pkt); + osi_thread_post_event(p_env->adv_rpt_ready, OSI_THREAD_MAX_TIMEOUT); +} + +static void btc_search_callback(tBTA_DM_SEARCH_EVT event, tBTA_DM_SEARCH *p_data) +{ + if (event == BTA_DM_INQ_RES_EVT) { + btc_process_adv_rpt_pkt(event, p_data); + return; + } + + esp_ble_gap_cb_param_t param; + btc_msg_t msg = {0}; + + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_GAP_BLE; + msg.act = ESP_GAP_BLE_SCAN_RESULT_EVT; + + param.scan_rst.search_evt = event; + switch (event) { + case BTA_DM_INQ_RES_EVT: + break; + case BTA_DM_INQ_CMPL_EVT: { + param.scan_rst.num_resps = p_data->inq_cmpl.num_resps; + BTC_TRACE_DEBUG("%s BLE observe complete. Num Resp %d\n", __FUNCTION__, p_data->inq_cmpl.num_resps); + break; + } + case BTA_DM_DISC_RES_EVT: + BTC_TRACE_DEBUG("BTA_DM_DISC_RES_EVT\n"); + break; + case BTA_DM_DISC_BLE_RES_EVT: + BTC_TRACE_DEBUG("BTA_DM_DISC_BLE_RES_EVT\n"); + break; + case BTA_DM_DISC_CMPL_EVT: + BTC_TRACE_DEBUG("BTA_DM_DISC_CMPL_EVT\n"); + break; + case BTA_DM_DI_DISC_CMPL_EVT: + BTC_TRACE_DEBUG("BTA_DM_DI_DISC_CMPL_EVT\n"); + break; + case BTA_DM_SEARCH_CANCEL_CMPL_EVT: + BTC_TRACE_DEBUG("BTA_DM_SEARCH_CANCEL_CMPL_EVT\n"); + break; + case BTA_DM_INQ_DISCARD_NUM_EVT: + param.scan_rst.num_dis = p_data->inq_dis.num_dis; + break; + default: + BTC_TRACE_ERROR("%s : Unknown event 0x%x\n", __FUNCTION__, event); + return; + } + btc_transfer_context(&msg, ¶m, sizeof(esp_ble_gap_cb_param_t), NULL, NULL); +} + +static void btc_start_scan_callback(uint8_t status) +{ + esp_ble_gap_cb_param_t param; + bt_status_t ret; + btc_msg_t msg = {0}; + + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_GAP_BLE; + msg.act = ESP_GAP_BLE_SCAN_START_COMPLETE_EVT; + param.scan_start_cmpl.status = status; + + ret = btc_transfer_context(&msg, ¶m, + sizeof(esp_ble_gap_cb_param_t), NULL, NULL); + + if (ret != BT_STATUS_SUCCESS) { + BTC_TRACE_ERROR("%s btc_transfer_context failed\n", __func__); + } +} + +static void btc_stop_scan_callback(tBTA_STATUS status) +{ + esp_ble_gap_cb_param_t param; + bt_status_t ret; + btc_msg_t msg = {0}; + + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_GAP_BLE; + msg.act = ESP_GAP_BLE_SCAN_STOP_COMPLETE_EVT; + param.scan_stop_cmpl.status = status; + + ret = btc_transfer_context(&msg, ¶m, + sizeof(esp_ble_gap_cb_param_t), NULL, NULL); + + if (ret != BT_STATUS_SUCCESS) { + BTC_TRACE_ERROR("%s btc_transfer_context failed\n", __func__); + } +#if SCAN_QUEUE_CONGEST_CHECK + btc_adv_list_refresh(); +#endif +} +#endif // #if (BLE_42_FEATURE_SUPPORT == TRUE) +void btc_update_conn_param_callback (UINT8 status, BD_ADDR bd_addr, tBTM_LE_UPDATE_CONN_PRAMS *update_conn_params) +{ + esp_ble_gap_cb_param_t param; + bt_status_t ret; + btc_msg_t msg = {0}; + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_GAP_BLE; + msg.act = ESP_GAP_BLE_UPDATE_CONN_PARAMS_EVT; + param.update_conn_params.status = btc_hci_to_esp_status(status); + param.update_conn_params.min_int = update_conn_params->min_conn_int; + param.update_conn_params.max_int = update_conn_params->max_conn_int; + param.update_conn_params.conn_int = update_conn_params->conn_int; + param.update_conn_params.latency = update_conn_params->slave_latency; + param.update_conn_params.timeout = update_conn_params->supervision_tout; + memcpy(param.update_conn_params.bda, bd_addr, sizeof(esp_bd_addr_t)); + ret = btc_transfer_context(&msg, ¶m, + sizeof(esp_ble_gap_cb_param_t), NULL, NULL); + + if (ret != BT_STATUS_SUCCESS) { + BTC_TRACE_ERROR("%s btc_transfer_context failed\n", __func__); + } +} + +static void btc_set_pkt_length_callback(UINT8 status, tBTM_LE_SET_PKT_DATA_LENGTH_PARAMS *data_len_params) +{ + esp_ble_gap_cb_param_t param; + bt_status_t ret; + btc_msg_t msg = {0}; + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_GAP_BLE; + msg.act = ESP_GAP_BLE_SET_PKT_LENGTH_COMPLETE_EVT; + param.pkt_data_length_cmpl.status = btc_btm_status_to_esp_status(status); + param.pkt_data_length_cmpl.params.rx_len = data_len_params->rx_len; + param.pkt_data_length_cmpl.params.tx_len = data_len_params->tx_len; + ret = btc_transfer_context(&msg, ¶m, + sizeof(esp_ble_gap_cb_param_t), NULL, NULL); + + if (ret != BT_STATUS_SUCCESS) { + BTC_TRACE_ERROR("%s btc_transfer_context failed\n", __func__); + } +} + +static void btc_gap_ble_set_channels_cmpl_callback(void *p_data) +{ + tBTA_BLE_SET_CHANNELS_RESULTS *result = (tBTA_BLE_SET_CHANNELS_RESULTS *)p_data; + esp_ble_gap_cb_param_t param; + bt_status_t ret; + btc_msg_t msg = {0}; + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_GAP_BLE; + msg.act = ESP_GAP_BLE_SET_CHANNELS_EVT; + + param.ble_set_channels.stat = btc_btm_status_to_esp_status(result->status); + + ret = btc_transfer_context(&msg, ¶m, + sizeof(esp_ble_gap_cb_param_t), NULL, NULL); + + if (ret != BT_STATUS_SUCCESS) { + BTC_TRACE_ERROR("%s btc_transfer_context failed\n", __func__); + } +} + + +static void btc_update_whitelist_complete_callback(UINT8 status, tBTM_WL_OPERATION wl_opration) +{ + esp_ble_gap_cb_param_t param; + bt_status_t ret; + btc_msg_t msg = {0}; + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_GAP_BLE; + msg.act = ESP_GAP_BLE_UPDATE_WHITELIST_COMPLETE_EVT; + param.update_whitelist_cmpl.status = btc_hci_to_esp_status(status); + param.update_whitelist_cmpl.wl_operation = wl_opration; + ret = btc_transfer_context(&msg, ¶m, + sizeof(esp_ble_gap_cb_param_t), NULL, NULL); + + if (ret != BT_STATUS_SUCCESS) { + BTC_TRACE_ERROR("%s btc_transfer_context failed\n", __func__); + } +} + +static void btc_set_rand_addr_callback(UINT8 status) +{ + esp_ble_gap_cb_param_t param; + bt_status_t ret; + btc_msg_t msg = {0}; + param.set_rand_addr_cmpl.status = btc_btm_status_to_esp_status(status); //todo status + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_GAP_BLE; + msg.act = ESP_GAP_BLE_SET_STATIC_RAND_ADDR_EVT; + ret = btc_transfer_context(&msg, ¶m, + sizeof(esp_ble_gap_cb_param_t), NULL, NULL); + + if (ret != BT_STATUS_SUCCESS) { + BTC_TRACE_ERROR("%s btc_transfer_context failed\n", __func__); + } + +} + +static void btc_set_local_privacy_callback(UINT8 status) +{ + esp_ble_gap_cb_param_t param; + bt_status_t ret; + btc_msg_t msg = {0}; + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_GAP_BLE; + msg.act = ESP_GAP_BLE_SET_LOCAL_PRIVACY_COMPLETE_EVT; + param.local_privacy_cmpl.status = btc_btm_status_to_esp_status(status); + ret = btc_transfer_context(&msg, ¶m, + sizeof(esp_ble_gap_cb_param_t), NULL, NULL); + + if (ret != BT_STATUS_SUCCESS) { + BTC_TRACE_ERROR("%s btc_transfer_context failed\n", __func__); + } +} + + +#if (SMP_INCLUDED == TRUE) +static void btc_set_encryption_callback(BD_ADDR bd_addr, tBTA_TRANSPORT transport, tBTA_STATUS enc_status) +{ + UNUSED(bd_addr); + UNUSED(transport); + BTC_TRACE_DEBUG("enc_status = %x\n", enc_status); + return; +} +#endif ///SMP_INCLUDED == TRUE + +static void btc_read_ble_rssi_cmpl_callback(void *p_data) +{ + tBTA_RSSI_RESULTS *result = (tBTA_RSSI_RESULTS *)p_data; + esp_ble_gap_cb_param_t param; + bt_status_t ret; + btc_msg_t msg = {0}; + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_GAP_BLE; + msg.act = ESP_GAP_BLE_READ_RSSI_COMPLETE_EVT; + param.read_rssi_cmpl.rssi = result->rssi; + param.read_rssi_cmpl.status = btc_btm_status_to_esp_status(result->status); + memcpy(param.read_rssi_cmpl.remote_addr, result->rem_bda, sizeof(BD_ADDR)); + + ret = btc_transfer_context(&msg, ¶m, + sizeof(esp_ble_gap_cb_param_t), NULL, NULL); + + if (ret != BT_STATUS_SUCCESS) { + BTC_TRACE_ERROR("%s btc_transfer_context failed\n", __func__); + } +} + +#if (BLE_50_FEATURE_SUPPORT == TRUE) +static void btc_ble_5_gap_callback(tBTA_DM_BLE_5_GAP_EVENT event, + tBTA_DM_BLE_5_GAP_CB_PARAMS *params) +{ + esp_ble_gap_cb_param_t param; + bt_status_t ret; + btc_msg_t msg; + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_GAP_BLE; + + switch(event) { + case BTA_DM_BLE_5_GAP_READ_PHY_COMPLETE_EVT: { + msg.act = ESP_GAP_BLE_READ_PHY_COMPLETE_EVT; + param.read_phy.status = btc_btm_status_to_esp_status(params->read_phy.status); + memcpy(param.read_phy.bda, params->read_phy.addr, BD_ADDR_LEN); + param.read_phy.tx_phy = params->read_phy.tx_phy; + param.read_phy.rx_phy = params->read_phy.rx_phy; + break; + } + case BTA_DM_BLE_5_GAP_SET_PREFERED_DEFAULT_PHY_COMPLETE_EVT: { + msg.act = ESP_GAP_BLE_SET_PREFERRED_DEFAULT_PHY_COMPLETE_EVT; + param.set_perf_def_phy.status = btc_btm_status_to_esp_status(params->set_perf_def_phy.status); + break; + } + case BTA_DM_BLE_5_GAP_SET_PREFERED_PHY_COMPLETE_EVT: { + msg.act = ESP_GAP_BLE_SET_PREFERRED_PHY_COMPLETE_EVT; + param.set_perf_phy.status = btc_btm_status_to_esp_status(params->set_perf_phy.status); + break; + } + case BTA_DM_BLE_5_GAP_EXT_ADV_SET_RAND_ADDR_COMPLETE_EVT: + msg.act = ESP_GAP_BLE_EXT_ADV_SET_RAND_ADDR_COMPLETE_EVT; + param.ext_adv_set_rand_addr.status = btc_btm_status_to_esp_status(params->set_ext_rand_addr.status); + break; + case BTA_DM_BLE_5_GAP_EXT_ADV_SET_PARAMS_COMPLETE_EVT: { + msg.act = ESP_GAP_BLE_EXT_ADV_SET_PARAMS_COMPLETE_EVT; + param.ext_adv_set_rand_addr.status = btc_btm_status_to_esp_status(params->set_params.status); + break; + } + case BTA_DM_BLE_5_GAP_EXT_ADV_DATA_SET_COMPLETE_EVT: { + msg.act = ESP_GAP_BLE_EXT_ADV_DATA_SET_COMPLETE_EVT; + param.ext_adv_data_set.status = btc_btm_status_to_esp_status(params->adv_data_set.status); + break; + } + case BTA_DM_BLE_5_GAP_EXT_SCAN_RSP_DATA_SET_COMPLETE_EVT: { + msg.act = ESP_GAP_BLE_EXT_SCAN_RSP_DATA_SET_COMPLETE_EVT; + param.scan_rsp_set.status = btc_btm_status_to_esp_status(params->scan_rsp_data_set.status); + break; + } + case BTA_DM_BLE_5_GAP_EXT_ADV_START_COMPLETE_EVT: { + msg.act = ESP_GAP_BLE_EXT_ADV_START_COMPLETE_EVT; + param.ext_adv_start.status = btc_btm_status_to_esp_status(params->adv_start.status); + break; + } + case BTA_DM_BLE_5_GAP_EXT_ADV_STOP_COMPLETE_EVT: + msg.act = ESP_GAP_BLE_EXT_ADV_STOP_COMPLETE_EVT; + param.ext_adv_stop.status = btc_btm_status_to_esp_status(params->adv_start.status); + break; + case BTA_DM_BLE_5_GAP_EXT_ADV_SET_REMOVE_COMPLETE_EVT: + msg.act = ESP_GAP_BLE_EXT_ADV_SET_REMOVE_COMPLETE_EVT; + param.ext_adv_remove.status = btc_btm_status_to_esp_status(params->adv_start.status); + break; + case BTA_DM_BLE_5_GAP_EXT_ADV_SET_CLEAR_COMPLETE_EVT: + msg.act = ESP_GAP_BLE_EXT_ADV_SET_CLEAR_COMPLETE_EVT; + param.ext_adv_clear.status = btc_btm_status_to_esp_status(params->adv_start.status); + break; + case BTA_DM_BLE_5_GAP_PERIODIC_ADV_SET_PARAMS_COMPLETE_EVT: { + msg.act = ESP_GAP_BLE_PERIODIC_ADV_SET_PARAMS_COMPLETE_EVT; + param.peroid_adv_set_params.status = btc_btm_status_to_esp_status(params->per_adv_set_params.status); + break; + } + case BTA_DM_BLE_5_GAP_PERIODIC_ADV_DATA_SET_COMPLETE_EVT: { + msg.act = ESP_GAP_BLE_PERIODIC_ADV_DATA_SET_COMPLETE_EVT; + param.period_adv_data_set.status = btc_btm_status_to_esp_status(params->per_adv_data_set.status); + break; + } + case BTA_DM_BLE_5_GAP_PERIODIC_ADV_START_COMPLETE_EVT: { + msg.act = ESP_GAP_BLE_PERIODIC_ADV_START_COMPLETE_EVT; + param.period_adv_start.status = btc_btm_status_to_esp_status(params->per_adv_start.status); + break; + } + case BTA_DM_BLE_5_GAP_PERIODIC_ADV_STOP_COMPLETE_EVT: { + msg.act = ESP_GAP_BLE_PERIODIC_ADV_STOP_COMPLETE_EVT; + param.period_adv_stop.status = btc_btm_status_to_esp_status(params->per_adv_stop.status); + break; + } + case BTA_DM_BLE_5_GAP_PERIODIC_ADV_CREATE_SYNC_COMPLETE_EVT: { + msg.act = ESP_GAP_BLE_PERIODIC_ADV_CREATE_SYNC_COMPLETE_EVT; + param.period_adv_create_sync.status = btc_btm_status_to_esp_status(params->per_adv_sync_create.status); + break; + } + case BTA_DM_BLE_5_GAP_PERIODIC_ADV_SYNC_CANCEL_COMPLETE_EVT: { + msg.act = ESP_GAP_BLE_PERIODIC_ADV_SYNC_CANCEL_COMPLETE_EVT; + param.period_adv_sync_cancel.status = btc_btm_status_to_esp_status(params->per_adv_sync_cancel.status); + break; + } + case BTA_DM_BLE_5_GAP_PERIODIC_ADV_SYNC_TERMINATE_COMPLETE_EVT: { + msg.act = ESP_GAP_BLE_PERIODIC_ADV_SYNC_TERMINATE_COMPLETE_EVT; + param.period_adv_sync_term.status = btc_btm_status_to_esp_status(params->per_adv_sync_term.status); + break; + } + case BTA_DM_BLE_5_GAP_PERIODIC_ADV_ADD_DEV_COMPLETE_EVT: { + msg.act = ESP_GAP_BLE_PERIODIC_ADV_ADD_DEV_COMPLETE_EVT; + param.period_adv_add_dev.status = btc_btm_status_to_esp_status(params->per_adv_add_dev.status); + break; + } + case BTA_DM_BLE_5_GAP_PERIODIC_ADV_REMOVE_DEV_COMPLETE_EVT: { + msg.act = ESP_GAP_BLE_PERIODIC_ADV_REMOVE_DEV_COMPLETE_EVT; + param.period_adv_remove_dev.status = btc_btm_status_to_esp_status(params->per_adv_remove_dev.status); + break; + } + case BTA_DM_BLE_5_GAP_PERIODIC_ADV_CLEAR_DEV_COMPLETE_EVT: { + msg.act = ESP_GAP_BLE_PERIODIC_ADV_CLEAR_DEV_COMPLETE_EVT; + param.period_adv_clear_dev.status = btc_btm_status_to_esp_status(params->per_adv_clear_dev.status); + break; + } + case BTA_DM_BLE_5_GAP_SET_EXT_SCAN_PARAMS_COMPLETE_EVT: { + msg.act = ESP_GAP_BLE_SET_EXT_SCAN_PARAMS_COMPLETE_EVT; + param.set_ext_scan_params.status = btc_btm_status_to_esp_status(params->ext_scan.status); + break; + } + case BTA_DM_BLE_5_GAP_EXT_SCAN_START_COMPLETE_EVT: { + msg.act = ESP_GAP_BLE_EXT_SCAN_START_COMPLETE_EVT; + param.ext_scan_start.status = btc_btm_status_to_esp_status(params->scan_start.status); + break; + } + case BTA_DM_BLE_5_GAP_EXT_SCAN_STOP_COMPLETE_EVT: { + msg.act = ESP_GAP_BLE_EXT_SCAN_STOP_COMPLETE_EVT; + param.ext_scan_stop.status = btc_btm_status_to_esp_status(params->scan_stop.status); + break; + } + case BTA_DM_BLE_5_GAP_PREFER_EXT_CONN_PARAMS_SET_COMPLETE_EVT: { + msg.act = ESP_GAP_BLE_PREFER_EXT_CONN_PARAMS_SET_COMPLETE_EVT; + param.ext_conn_params_set.status = btc_btm_status_to_esp_status(params->ext_conn_set_params.status); + break; + } + case BTA_DM_BLE_5_GAP_PHY_UPDATE_COMPLETE_EVT: + msg.act = ESP_GAP_BLE_PHY_UPDATE_COMPLETE_EVT; + param.phy_update.status = btc_btm_status_to_esp_status(params->phy_update.status); + memcpy(param.phy_update.bda, params->phy_update.addr, BD_ADDR_LEN); + param.phy_update.tx_phy = params->phy_update.tx_phy; + param.phy_update.rx_phy = params->phy_update.rx_phy; + break; + case BTA_DM_BLE_5_GAP_EXT_ADV_REPORT_EVT: + msg.act = ESP_GAP_BLE_EXT_ADV_REPORT_EVT; + memcpy(¶m.ext_adv_report.params, ¶ms->ext_adv_report, sizeof(esp_ble_gap_ext_adv_reprot_t)); + if (params->ext_adv_report.adv_data) { + memcpy(param.ext_adv_report.params.adv_data, + params->ext_adv_report.adv_data, params->ext_adv_report.adv_data_len); + } + break; + case BTA_DM_BLE_5_GAP_SCAN_TIMEOUT_EVT: + msg.act = ESP_GAP_BLE_SCAN_TIMEOUT_EVT; + break; + case BTA_DM_BLE_5_GAP_ADV_TERMINATED_EVT: { + param.adv_terminate.status = params->adv_term.status; + param.adv_terminate.adv_instance = params->adv_term.adv_handle; + param.adv_terminate.conn_idx = params->adv_term.conn_handle; + param.adv_terminate.completed_event = params->adv_term.completed_event; + msg.act = ESP_GAP_BLE_ADV_TERMINATED_EVT; + break; + } + case BTA_DM_BLE_5_GAP_SCAN_REQ_RECEIVED_EVT: { + msg.act = ESP_GAP_BLE_SCAN_REQ_RECEIVED_EVT; + param.scan_req_received.adv_instance = params->scan_req.adv_handle; + param.scan_req_received.scan_addr_type = params->scan_req.scan_addr_type; + memcpy(param.scan_req_received.scan_addr, params->scan_req.scan_addr, sizeof(BD_ADDR)); + break; + } + case BTA_DM_BLE_5_GAP_CHANNEL_SELETE_ALGORITHM_EVT: { + msg.act = ESP_GAP_BLE_CHANNEL_SELECT_ALGORITHM_EVT; + param.channel_sel_alg.conn_handle = params->channel_sel.conn_handle; + param.channel_sel_alg.channel_sel_alg = params->channel_sel.channel_sel_alg; + break; + } + case BTA_DM_BLE_5_GAP_PERIODIC_ADV_REPORT_EVT: { + msg.act = ESP_GAP_BLE_PERIODIC_ADV_REPORT_EVT; + memcpy(¶m.period_adv_report, ¶ms->period_adv_report, + sizeof(esp_ble_gap_periodic_adv_report_t)); + if (params->period_adv_report.data) { + memcpy(param.period_adv_report.params.data, params->period_adv_report.data, + params->period_adv_report.data_length); + } + break; + } + case BTA_DM_BLE_5_GAP_PERIODIC_ADV_SYNC_LOST_EVT: { + msg.act = ESP_GAP_BLE_PERIODIC_ADV_SYNC_LOST_EVT; + param.periodic_adv_sync_lost.sync_handle = params->sync_lost.sync_handle; + break; + } + case BTA_DM_BLE_5_GAP_PERIODIC_ADV_SYNC_ESTAB_EVT: { + msg.act = ESP_GAP_BLE_PERIODIC_ADV_SYNC_ESTAB_EVT; + param.periodic_adv_sync_estab.status = btc_btm_status_to_esp_status(params->sync_estab.status); + param.periodic_adv_sync_estab.sync_handle = params->sync_estab.sync_handle; + param.periodic_adv_sync_estab.sid = params->sync_estab.sid; + param.periodic_adv_sync_estab.adv_addr_type = params->sync_estab.adv_addr_type; + memcpy(param.periodic_adv_sync_estab.adv_addr, params->sync_estab.adv_addr, + sizeof(BD_ADDR)); + param.periodic_adv_sync_estab.adv_phy = params->sync_estab.adv_phy; + param.periodic_adv_sync_estab.period_adv_interval = params->sync_estab.period_adv_interval; + param.periodic_adv_sync_estab.adv_clk_accuracy = params->sync_estab.adv_clk_accuracy; + break; + } +#if (BLE_FEAT_PERIODIC_ADV_SYNC_TRANSFER == TRUE) + case BTA_BLE_GAP_PERIODIC_ADV_RECV_ENABLE_COMPLETE_EVT: + msg.act = ESP_GAP_BLE_PERIODIC_ADV_RECV_ENABLE_COMPLETE_EVT; + param.period_adv_recv_enable.status = btc_btm_status_to_esp_status(params->per_adv_recv_enable.status); + break; + case BTA_BLE_GAP_PERIODIC_ADV_SYNC_TRANS_COMPLETE_EVT: + msg.act = ESP_GAP_BLE_PERIODIC_ADV_SYNC_TRANS_COMPLETE_EVT; + param.period_adv_sync_trans.status = btc_btm_status_to_esp_status(params->per_adv_sync_trans.status); + memcpy(param.period_adv_sync_trans.bda, params->per_adv_sync_trans.addr, sizeof(BD_ADDR)); + break; + case BTA_BLE_GAP_PERIODIC_ADV_SET_INFO_TRANS_COMPLETE_EVT: + msg.act = ESP_GAP_BLE_PERIODIC_ADV_SET_INFO_TRANS_COMPLETE_EVT; + param.period_adv_set_info_trans.status = btc_btm_status_to_esp_status(params->per_adv_set_info_trans.status); + memcpy(param.period_adv_set_info_trans.bda, params->per_adv_set_info_trans.addr, sizeof(BD_ADDR)); + break; + case BTA_BLE_GAP_SET_PAST_PARAMS_COMPLETE_EVT: + msg.act = ESP_GAP_BLE_SET_PAST_PARAMS_COMPLETE_EVT; + param.set_past_params.status = btc_btm_status_to_esp_status(params->set_past_params.status); + memcpy(param.set_past_params.bda, params->set_past_params.addr, sizeof(BD_ADDR)); + break; + case BTA_BLE_GAP_PERIODIC_ADV_SYNC_TRANS_RECV_EVT: + msg.act = ESP_GAP_BLE_PERIODIC_ADV_SYNC_TRANS_RECV_EVT; + param.past_received.status = btc_btm_status_to_esp_status(params->past_recv.status); + memcpy(param.past_received.bda, params->past_recv.addr, sizeof(BD_ADDR)); + param.past_received.service_data = params->past_recv.service_data; + param.past_received.sync_handle = params->past_recv.sync_handle; + param.past_received.adv_sid = params->past_recv.adv_sid; + param.past_received.adv_addr_type = params->past_recv.adv_addr_type; + memcpy(param.past_received.adv_addr, params->past_recv.adv_addr, sizeof(BD_ADDR)); + param.past_received.adv_phy = params->past_recv.adv_phy; + param.past_received.adv_interval = params->past_recv.adv_interval; + param.past_received.adv_clk_accuracy = params->past_recv.adv_clk_accuracy; + break; +#endif + default: + break; + } + + ret = btc_transfer_context(&msg, ¶m, + sizeof(esp_ble_gap_cb_param_t), NULL, NULL); + + if (ret != BT_STATUS_SUCCESS) { + BTC_TRACE_ERROR("%s btc_transfer_context failed\n", __func__); + } +} +#endif // #if (BLE_50_FEATURE_SUPPORT == TRUE) + +void btc_dtm_tx_start_callback(void *p1) +{ + UINT8 status; + UINT8 *p; + p = (UINT8*) p1; + if (p1) { + STREAM_TO_UINT8(status, p); + BTC_TRACE_DEBUG("DTM TX start, status 0x%x\n", status); + esp_ble_gap_cb_param_t param; + bt_status_t ret; + btc_msg_t msg = {0}; + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_GAP_BLE; + msg.act = ESP_GAP_BLE_DTM_TEST_UPDATE_EVT; + param.dtm_state_update.status = status; + param.dtm_state_update.update_evt = DTM_TX_START_EVT; + param.dtm_state_update.num_of_pkt = 0; + + ret = btc_transfer_context(&msg, ¶m, + sizeof(esp_ble_gap_cb_param_t), NULL, NULL); + + if (ret != BT_STATUS_SUCCESS) { + BTC_TRACE_ERROR("%s btc_transfer_context failed\n", __func__); + } + } +} + +void btc_dtm_rx_start_callback(void *p1) +{ + + UINT8 status; + UINT8 *p; + p = (UINT8*) p1; + if (p1) { + STREAM_TO_UINT8(status, p); + BTC_TRACE_DEBUG("DTM RX start, status 0x%x\n", status); + esp_ble_gap_cb_param_t param; + bt_status_t ret; + btc_msg_t msg = {0}; + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_GAP_BLE; + msg.act = ESP_GAP_BLE_DTM_TEST_UPDATE_EVT; + param.dtm_state_update.status = status; + param.dtm_state_update.update_evt = DTM_RX_START_EVT; + param.dtm_state_update.num_of_pkt = 0; + + ret = btc_transfer_context(&msg, ¶m, + sizeof(esp_ble_gap_cb_param_t), NULL, NULL); + + if (ret != BT_STATUS_SUCCESS) { + BTC_TRACE_ERROR("%s btc_transfer_context failed\n", __func__); + } + } +} + +void btc_dtm_stop_callback(void *p1) +{ + UINT8 status; + UINT16 num_pkt; + UINT8 *p; + p = (UINT8*) p1; + if (p1) { + STREAM_TO_UINT8(status, p); + STREAM_TO_UINT16(num_pkt, p); + BTC_TRACE_DEBUG("DTM stop, status 0x%x num_pkt %d\n", status, num_pkt); + esp_ble_gap_cb_param_t param; + bt_status_t ret; + btc_msg_t msg = {0}; + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_GAP_BLE; + msg.act = ESP_GAP_BLE_DTM_TEST_UPDATE_EVT; + param.dtm_state_update.status = status; + param.dtm_state_update.update_evt = DTM_TEST_STOP_EVT; + param.dtm_state_update.num_of_pkt = num_pkt; + + ret = btc_transfer_context(&msg, ¶m, + sizeof(esp_ble_gap_cb_param_t), NULL, NULL); + + if (ret != BT_STATUS_SUCCESS) { + BTC_TRACE_ERROR("%s btc_transfer_context failed\n", __func__); + } + } +} + +void btc_get_whitelist_size(uint16_t *length) +{ + BTM_BleGetWhiteListSize(length); + return; +} +#if (BLE_42_FEATURE_SUPPORT == TRUE) +static void btc_ble_start_scanning(uint32_t duration, + tBTA_DM_SEARCH_CBACK *results_cb, + tBTA_START_STOP_SCAN_CMPL_CBACK *start_scan_cb) +{ + if ((results_cb != NULL) && (start_scan_cb != NULL)) { +#if SCAN_QUEUE_CONGEST_CHECK + btc_adv_list_refresh(); +#endif + //Start scan the device + BTA_DmBleScan(true, duration, results_cb, start_scan_cb); + } else { + BTC_TRACE_ERROR("The start_scan_cb or results_cb invalid\n"); + } +} + +static void btc_ble_stop_scanning(tBTA_START_STOP_SCAN_CMPL_CBACK *stop_scan_cb) +{ + uint8_t duration = 0; + BTA_DmBleScan(false, duration, NULL, stop_scan_cb); +} + +static void btc_ble_stop_advertising(tBTA_START_STOP_ADV_CMPL_CBACK *stop_adv_cb) +{ + bool stop_adv = false; + + BTA_DmBleBroadcast(stop_adv, stop_adv_cb); +} + +static void btc_ble_clear_advertising(tBTA_CLEAR_ADV_CMPL_CBACK *clear_adv_cb) +{ + BTA_DmBleClearAdv(clear_adv_cb); +} +#endif // #if (BLE_42_FEATURE_SUPPORT == TRUE) +static void btc_ble_update_conn_params(BD_ADDR bd_addr, uint16_t min_int, + uint16_t max_int, uint16_t latency, uint16_t timeout) +{ + if (min_int > max_int) { + min_int = max_int; + } + + if (min_int < BTM_BLE_CONN_INT_MIN || max_int > BTM_BLE_CONN_INT_MAX) { + BTC_TRACE_ERROR("Invalid interval value.\n"); + } + + BTA_DmBleUpdateConnectionParams(bd_addr, min_int, max_int, + latency, timeout); +} + +static void btc_ble_set_pkt_data_len(BD_ADDR remote_device, uint16_t tx_data_length, tBTA_SET_PKT_DATA_LENGTH_CBACK *p_set_pkt_data_cback) +{ + if (tx_data_length > BTM_BLE_DATA_SIZE_MAX) { + tx_data_length = BTM_BLE_DATA_SIZE_MAX; + } else if (tx_data_length < BTM_BLE_DATA_SIZE_MIN) { + tx_data_length = BTM_BLE_DATA_SIZE_MIN; + } + + BTA_DmBleSetDataLength(remote_device, tx_data_length, p_set_pkt_data_cback); +} + +static void btc_ble_config_local_icon(uint16_t icon) +{ + BTA_DmBleConfigLocalIcon(icon); +} + +static void btc_ble_set_rand_addr (BD_ADDR rand_addr, tBTA_SET_RAND_ADDR_CBACK *p_set_rand_addr_cback) +{ + if (rand_addr != NULL) { + /* + A static address is a 48-bit randomly generated address and shall meet the following requirements: + • The two most significant bits of the address shall be equal to 1 + • All bits of the random part of the address shall not be equal to 1 + • All bits of the random part of the address shall not be equal to 0 + A non-resolvable private address is a 48-bit randomly generated address and shall meet the following requirements: + • The two most significant bits of the address shall be equal to 0 + • All bits of the random part of the address shall not be equal to 1 + • All bits of the random part of the address shall not be equal to 0 + */ + BD_ADDR invalid_rand_addr_a, invalid_rand_addr_b; + memset(invalid_rand_addr_a, 0xff, sizeof(BD_ADDR)); + memset(invalid_rand_addr_b, 0x00, sizeof(BD_ADDR)); + + if((rand_addr[0] & BT_STATIC_RAND_ADDR_MASK) == BT_STATIC_RAND_ADDR_MASK) { + invalid_rand_addr_b[0] = invalid_rand_addr_b[0] | BT_STATIC_RAND_ADDR_MASK; + if (memcmp(invalid_rand_addr_a, rand_addr, BD_ADDR_LEN) != 0 && memcmp(invalid_rand_addr_b, rand_addr, BD_ADDR_LEN) != 0) { + BTA_DmSetRandAddress(rand_addr, btc_set_rand_addr_callback); + } else { + btc_set_rand_addr_callback(BTM_INVALID_STATIC_RAND_ADDR); + BTC_TRACE_ERROR("Invalid static random address, the high bit should be 0b11, bits of the random part shall not be all 1 or 0"); + } + } else if ((rand_addr[0] | BT_NON_RPA_MASK) == BT_NON_RPA_MASK) { + invalid_rand_addr_a[0] = invalid_rand_addr_a[0] & BT_NON_RPA_MASK; + if (memcmp(invalid_rand_addr_a, rand_addr, BD_ADDR_LEN) != 0 && memcmp(invalid_rand_addr_b, rand_addr, BD_ADDR_LEN) != 0) { + BTA_DmSetRandAddress(rand_addr, btc_set_rand_addr_callback); + } else { + btc_set_rand_addr_callback(BTM_INVALID_STATIC_RAND_ADDR); + BTC_TRACE_ERROR("Invalid non-resolvable private address, the high bit should be 0b00, bits of the random part shall not be all 1 or 0"); + } + }else { + btc_set_rand_addr_callback(BTM_INVALID_STATIC_RAND_ADDR); + BTC_TRACE_ERROR("Invalid random address type"); + } + } else { + btc_set_rand_addr_callback(BTM_INVALID_STATIC_RAND_ADDR); + BTC_TRACE_ERROR("Invalid address, the address value is NULL"); + } +} + +static void btc_ble_clear_rand_addr (void) +{ + BTA_DmClearRandAddress(); +} + +static void btc_ble_config_local_privacy(bool privacy_enable, tBTA_SET_LOCAL_PRIVACY_CBACK *set_local_privacy_cback) +{ + BTA_DmBleConfigLocalPrivacy(privacy_enable, set_local_privacy_cback); +} + +static void btc_ble_disconnect(BD_ADDR bd_addr) +{ + BTA_DmBleDisconnect(bd_addr); +} + +static void btc_gap_ble_set_channels(esp_gap_ble_channels channels) +{ + BTA_DmBleSetChannels(channels, btc_gap_ble_set_channels_cmpl_callback); +} + +#if (BLE_42_FEATURE_SUPPORT == TRUE) +static void btc_ble_dtm_tx_start(uint8_t tx_channel, uint8_t len_of_data, uint8_t pkt_payload, tBTA_DTM_CMD_CMPL_CBACK *p_dtm_cmpl_cback) +{ + BTA_DmBleDtmTxStart(tx_channel, len_of_data, pkt_payload, p_dtm_cmpl_cback); +} + +static void btc_ble_dtm_rx_start(uint8_t rx_channel, tBTA_DTM_CMD_CMPL_CBACK *p_dtm_cmpl_cback) +{ + + BTA_DmBleDtmRxStart(rx_channel, p_dtm_cmpl_cback); +} +#endif // #if (BLE_42_FEATURE_SUPPORT == TRUE) + +#if (BLE_50_FEATURE_SUPPORT == TRUE) +static void btc_ble_dtm_enhance_tx_start(uint8_t tx_channel, uint8_t len_of_data, uint8_t pkt_payload, uint8_t phy, tBTA_DTM_CMD_CMPL_CBACK *p_dtm_cmpl_cback) +{ + BTA_DmBleDtmEnhTxStart(tx_channel, len_of_data, pkt_payload, phy, p_dtm_cmpl_cback); +} + +static void btc_ble_dtm_enhance_rx_start(uint8_t rx_channel, uint8_t phy, uint8_t modulation_index, tBTA_DTM_CMD_CMPL_CBACK *p_dtm_cmpl_cback) +{ + + BTA_DmBleDtmEnhRxStart(rx_channel, phy, modulation_index, p_dtm_cmpl_cback); +} +#endif // #if (BLE_50_FEATURE_SUPPORT == TRUE) + +static void btc_ble_dtm_stop(tBTA_DTM_CMD_CMPL_CBACK *p_dtm_cmpl_cback) +{ + + BTA_DmBleDtmStop(p_dtm_cmpl_cback); +} + + +void btc_gap_ble_cb_handler(btc_msg_t *msg) +{ + esp_ble_gap_cb_param_t *param = (esp_ble_gap_cb_param_t *)msg->arg; + + if (msg->act < ESP_GAP_BLE_EVT_MAX) { + btc_gap_ble_cb_to_app(msg->act, param); + } else { + BTC_TRACE_ERROR("%s, unknow msg->act = %d", __func__, msg->act); + } + + btc_gap_ble_cb_deep_free(msg); + +} + +void btc_gap_ble_arg_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src) +{ + switch (msg->act) { +#if (BLE_42_FEATURE_SUPPORT == TRUE) + case BTC_GAP_BLE_ACT_CFG_ADV_DATA: { + btc_ble_gap_args_t *src = (btc_ble_gap_args_t *)p_src; + btc_ble_gap_args_t *dst = (btc_ble_gap_args_t *) p_dest; + + if (src->cfg_adv_data.adv_data.p_manufacturer_data) { + dst->cfg_adv_data.adv_data.p_manufacturer_data = osi_malloc(src->cfg_adv_data.adv_data.manufacturer_len); + memcpy(dst->cfg_adv_data.adv_data.p_manufacturer_data, src->cfg_adv_data.adv_data.p_manufacturer_data, + src->cfg_adv_data.adv_data.manufacturer_len); + } + + if (src->cfg_adv_data.adv_data.p_service_data) { + dst->cfg_adv_data.adv_data.p_service_data = osi_malloc(src->cfg_adv_data.adv_data.service_data_len); + memcpy(dst->cfg_adv_data.adv_data.p_service_data, src->cfg_adv_data.adv_data.p_service_data, src->cfg_adv_data.adv_data.service_data_len); + } + + if (src->cfg_adv_data.adv_data.p_service_uuid) { + dst->cfg_adv_data.adv_data.p_service_uuid = osi_malloc(src->cfg_adv_data.adv_data.service_uuid_len); + memcpy(dst->cfg_adv_data.adv_data.p_service_uuid, src->cfg_adv_data.adv_data.p_service_uuid, src->cfg_adv_data.adv_data.service_uuid_len); + } + break; + } + case BTC_GAP_BLE_ACT_CFG_ADV_DATA_RAW: { + btc_ble_gap_args_t *src = (btc_ble_gap_args_t *)p_src; + btc_ble_gap_args_t *dst = (btc_ble_gap_args_t *) p_dest; + + if (src && src->cfg_adv_data_raw.raw_adv && src->cfg_adv_data_raw.raw_adv_len > 0) { + dst->cfg_adv_data_raw.raw_adv = osi_malloc(src->cfg_adv_data_raw.raw_adv_len); + if (dst->cfg_adv_data_raw.raw_adv) { + memcpy(dst->cfg_adv_data_raw.raw_adv, src->cfg_adv_data_raw.raw_adv, src->cfg_adv_data_raw.raw_adv_len); + } + } + break; + } + case BTC_GAP_BLE_ACT_CFG_SCAN_RSP_DATA_RAW: { + btc_ble_gap_args_t *src = (btc_ble_gap_args_t *)p_src; + btc_ble_gap_args_t *dst = (btc_ble_gap_args_t *) p_dest; + + if (src && src->cfg_scan_rsp_data_raw.raw_scan_rsp && src->cfg_scan_rsp_data_raw.raw_scan_rsp_len > 0) { + dst->cfg_scan_rsp_data_raw.raw_scan_rsp = osi_malloc(src->cfg_scan_rsp_data_raw.raw_scan_rsp_len); + if (dst->cfg_scan_rsp_data_raw.raw_scan_rsp) { + memcpy(dst->cfg_scan_rsp_data_raw.raw_scan_rsp, src->cfg_scan_rsp_data_raw.raw_scan_rsp, src->cfg_scan_rsp_data_raw.raw_scan_rsp_len); + } + } + break; + } +#endif // #if (BLE_42_FEATURE_SUPPORT == TRUE) + case BTC_GAP_BLE_SET_SECURITY_PARAM_EVT: { + btc_ble_gap_args_t *src = (btc_ble_gap_args_t *)p_src; + btc_ble_gap_args_t *dst = (btc_ble_gap_args_t *) p_dest; + uint8_t length = 0; + if (src->set_security_param.value) { + length = dst->set_security_param.len; + dst->set_security_param.value = osi_malloc(length); + if (dst->set_security_param.value != NULL) { + memcpy(dst->set_security_param.value, src->set_security_param.value, length); + } else { + BTC_TRACE_ERROR("%s %d no mem\n",__func__, msg->act); + } + } + break; + } + case BTC_GAP_BLE_OOB_REQ_REPLY_EVT: { + btc_ble_gap_args_t *src = (btc_ble_gap_args_t *)p_src; + btc_ble_gap_args_t *dst = (btc_ble_gap_args_t *) p_dest; + uint8_t length = 0; + if (src->oob_req_reply.p_value) { + length = dst->oob_req_reply.len; + dst->oob_req_reply.p_value = osi_malloc(length); + if (dst->oob_req_reply.p_value != NULL) { + memcpy(dst->oob_req_reply.p_value, src->oob_req_reply.p_value, length); + } else { + BTC_TRACE_ERROR("%s %d no mem\n",__func__, msg->act); + } + } + break; + } + case BTC_GAP_BLE_SC_OOB_REQ_REPLY_EVT: { + btc_ble_gap_args_t *src = (btc_ble_gap_args_t *)p_src; + btc_ble_gap_args_t *dst = (btc_ble_gap_args_t *)p_dest; + if (src->sc_oob_req_reply.p_c) { + dst->sc_oob_req_reply.p_c = osi_malloc(BT_OCTET16_LEN); + if (dst->sc_oob_req_reply.p_c) { + memcpy(dst->sc_oob_req_reply.p_c, src->sc_oob_req_reply.p_c, BT_OCTET16_LEN); + } else { + BTC_TRACE_ERROR("%s %d no mem\n",__func__, msg->act); + } + } + if (src->sc_oob_req_reply.p_r) { + dst->sc_oob_req_reply.p_r = osi_malloc(BT_OCTET16_LEN); + if (dst->sc_oob_req_reply.p_r) { + memcpy(dst->sc_oob_req_reply.p_r, src->sc_oob_req_reply.p_r, BT_OCTET16_LEN); + } else { + BTC_TRACE_ERROR("%s %d no mem\n",__func__, msg->act); + } + } + break; + } +#if (BLE_50_FEATURE_SUPPORT == TRUE) + case BTC_GAP_BLE_CFG_EXT_ADV_DATA_RAW: + case BTC_GAP_BLE_CFG_EXT_SCAN_RSP_DATA_RAW: { + btc_ble_5_gap_args_t *src = (btc_ble_5_gap_args_t *)p_src; + btc_ble_5_gap_args_t *dst = (btc_ble_5_gap_args_t *)p_dest; + uint16_t length = 0; + + if (src->ext_adv_cfg_data.data) { + length = src->ext_adv_cfg_data.length; + dst->ext_adv_cfg_data.data = osi_malloc(length); + if (dst->ext_adv_cfg_data.data) { + memcpy(dst->ext_adv_cfg_data.data, src->ext_adv_cfg_data.data, length); + } else { + BTC_TRACE_ERROR("%s %d no mem\n",__func__, msg->act); + } + } + break; + } + case BTC_GAP_BLE_CFG_PERIODIC_ADV_DATA_RAW: { + btc_ble_5_gap_args_t *src = (btc_ble_5_gap_args_t *)p_src; + btc_ble_5_gap_args_t *dst = (btc_ble_5_gap_args_t *)p_dest; + uint16_t length = 0; + + if (src->periodic_adv_cfg_data.data) { + length = src->periodic_adv_cfg_data.len; + dst->periodic_adv_cfg_data.data = osi_malloc(length); + if (dst->periodic_adv_cfg_data.data) { + memcpy(dst->periodic_adv_cfg_data.data, src->periodic_adv_cfg_data.data, length); + } else { + BTC_TRACE_ERROR("%s %d no mem\n",__func__, msg->act); + } + } + break; + } + case BTC_GAP_BLE_EXT_ADV_START: { + btc_ble_5_gap_args_t *src = (btc_ble_5_gap_args_t *)p_src; + btc_ble_5_gap_args_t *dst = (btc_ble_5_gap_args_t *)p_dest; + + if (src->ext_adv_start.ext_adv) { + dst->ext_adv_start.ext_adv = osi_malloc(src->ext_adv_start.num_adv*sizeof(esp_ble_gap_ext_adv_t)); + if (dst->ext_adv_start.ext_adv) { + memcpy(dst->ext_adv_start.ext_adv, src->ext_adv_start.ext_adv, + src->ext_adv_start.num_adv*sizeof(esp_ble_gap_ext_adv_t)); + } + } + break; + } + case BTC_GAP_BLE_EXT_ADV_STOP: { + btc_ble_5_gap_args_t *src = (btc_ble_5_gap_args_t *)p_src; + btc_ble_5_gap_args_t *dst = (btc_ble_5_gap_args_t *)p_dest; + + if (src->ext_adv_stop.ext_adv_inst) { + dst->ext_adv_stop.ext_adv_inst = osi_malloc(src->ext_adv_stop.num_adv*sizeof(uint8_t)); + if (dst->ext_adv_stop.ext_adv_inst) { + memcpy(dst->ext_adv_stop.ext_adv_inst, src->ext_adv_stop.ext_adv_inst, + src->ext_adv_stop.num_adv * sizeof(uint8_t)); + } + } + break; + } +#endif // #if (BLE_50_FEATURE_SUPPORT == TRUE) + default: + BTC_TRACE_ERROR("Unhandled deep copy %d\n", msg->act); + break; + } +} + +void btc_gap_ble_cb_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src) +{ + switch (msg->act) { + default: + BTC_TRACE_ERROR("%s, Unhandled deep copy %d\n", __func__, msg->act); + break; + } +} + +void btc_gap_ble_arg_deep_free(btc_msg_t *msg) +{ + BTC_TRACE_DEBUG("%s \n", __func__); + switch (msg->act) { +#if (BLE_42_FEATURE_SUPPORT == TRUE) + case BTC_GAP_BLE_ACT_CFG_ADV_DATA: { + esp_ble_adv_data_t *adv = &((btc_ble_gap_args_t *)msg->arg)->cfg_adv_data.adv_data; + if (adv->p_service_data) { + osi_free(adv->p_service_data); + } + + if (adv->p_service_uuid) { + osi_free(adv->p_service_uuid); + } + + if (adv->p_manufacturer_data) { + osi_free(adv->p_manufacturer_data); + } + break; + } + case BTC_GAP_BLE_ACT_CFG_ADV_DATA_RAW: { + uint8_t *raw_adv = ((btc_ble_gap_args_t *)msg->arg)->cfg_adv_data_raw.raw_adv; + if (raw_adv) { + osi_free(raw_adv); + } + break; + } + case BTC_GAP_BLE_ACT_CFG_SCAN_RSP_DATA_RAW: { + uint8_t *raw_scan_rsp = ((btc_ble_gap_args_t *)msg->arg)->cfg_scan_rsp_data_raw.raw_scan_rsp; + if (raw_scan_rsp) { + osi_free(raw_scan_rsp); + } + break; + } +#endif // #if (BLE_42_FEATURE_SUPPORT == TRUE) + case BTC_GAP_BLE_SET_SECURITY_PARAM_EVT: { + uint8_t *value = ((btc_ble_gap_args_t *)msg->arg)->set_security_param.value; + if (value) { + osi_free(value); + } + break; + } + case BTC_GAP_BLE_OOB_REQ_REPLY_EVT: { + uint8_t *value = ((btc_ble_gap_args_t *)msg->arg)->oob_req_reply.p_value; + if (value) { + osi_free(value); + } + break; + } + case BTC_GAP_BLE_SC_OOB_REQ_REPLY_EVT: { + uint8_t *value = ((btc_ble_gap_args_t *)msg->arg)->sc_oob_req_reply.p_c; + if (value) { + osi_free(value); + } + value = ((btc_ble_gap_args_t *)msg->arg)->sc_oob_req_reply.p_r; + if (value) { + osi_free(value); + } + break; + } +#if (BLE_50_FEATURE_SUPPORT == TRUE) + case BTC_GAP_BLE_CFG_EXT_ADV_DATA_RAW: + case BTC_GAP_BLE_CFG_EXT_SCAN_RSP_DATA_RAW: { + uint8_t *value = ((btc_ble_5_gap_args_t *)msg->arg)->ext_adv_cfg_data.data; + if (value) { + osi_free(value); + } + break; + } + case BTC_GAP_BLE_CFG_PERIODIC_ADV_DATA_RAW: { + uint8_t *value = ((btc_ble_5_gap_args_t *)msg->arg)->periodic_adv_cfg_data.data; + if (value) { + osi_free(value); + } + break; + } + case BTC_GAP_BLE_EXT_ADV_START: { + esp_ble_gap_ext_adv_t *value = ((btc_ble_5_gap_args_t *)msg->arg)->ext_adv_start.ext_adv; + if (value) { + osi_free(value); + } + break; + } + case BTC_GAP_BLE_EXT_ADV_STOP: { + uint8_t *value = ((btc_ble_5_gap_args_t *)msg->arg)->ext_adv_stop.ext_adv_inst; + if (value) { + osi_free(value); + } + break; + } +#endif // #if (BLE_50_FEATURE_SUPPORT == TRUE) + default: + BTC_TRACE_DEBUG("Unhandled deep free %d\n", msg->act); + break; + } +} + +void btc_gap_ble_cb_deep_free(btc_msg_t *msg) +{ + BTC_TRACE_DEBUG("%s", __func__); + switch (msg->act) { + case ESP_GAP_BLE_GET_DEV_NAME_COMPLETE_EVT: { + char *value = ((esp_ble_gap_cb_param_t *)msg->arg)->get_dev_name_cmpl.name; + if (value) { + osi_free(value); + } + break; + } + default: + BTC_TRACE_DEBUG("Unhandled deep free %d", msg->act); + break; + } +} + +void btc_gap_ble_call_handler(btc_msg_t *msg) +{ + btc_ble_gap_args_t *arg = (btc_ble_gap_args_t *)msg->arg; +#if (BLE_50_FEATURE_SUPPORT == TRUE) + btc_ble_5_gap_args_t *arg_5 = (btc_ble_5_gap_args_t *)msg->arg; +#endif // #if (BLE_50_FEATURE_SUPPORT == TRUE) + + BTC_TRACE_DEBUG("%s act %d\n", __FUNCTION__, msg->act); + + switch (msg->act) { +#if (BLE_42_FEATURE_SUPPORT == TRUE) + case BTC_GAP_BLE_ACT_CFG_ADV_DATA: { + if (arg->cfg_adv_data.adv_data.set_scan_rsp == false) { + btc_ble_set_adv_data(&arg->cfg_adv_data.adv_data, btc_adv_data_callback); + } else { + btc_ble_set_adv_data(&arg->cfg_adv_data.adv_data, btc_scan_rsp_data_callback); + } + break; + } + case BTC_GAP_BLE_ACT_SET_SCAN_PARAM: + btc_ble_set_scan_params(&arg->set_scan_param.scan_params, btc_scan_params_callback); + break; + case BTC_GAP_BLE_ACT_START_SCAN: + btc_ble_start_scanning(arg->start_scan.duration, btc_search_callback, btc_start_scan_callback); + break; + case BTC_GAP_BLE_ACT_STOP_SCAN: + btc_ble_stop_scanning(btc_stop_scan_callback); + break; + case BTC_GAP_BLE_ACT_START_ADV: + btc_ble_start_advertising(&arg->start_adv.adv_params, btc_start_adv_callback); + break; + case BTC_GAP_BLE_ACT_STOP_ADV: + btc_ble_stop_advertising(btc_stop_adv_callback); + break; + case BTC_GAP_BLE_ACT_CLEAR_ADV: + btc_ble_clear_advertising(btc_clear_adv_callback); + break; +#endif // #if (BLE_42_FEATURE_SUPPORT == TRUE) + case BTC_GAP_BLE_ACT_UPDATE_CONN_PARAM: + btc_ble_update_conn_params(arg->conn_update_params.conn_params.bda, + arg->conn_update_params.conn_params.min_int, + arg->conn_update_params.conn_params.max_int, + arg->conn_update_params.conn_params.latency, + arg->conn_update_params.conn_params.timeout); + break; + case BTC_GAP_BLE_ACT_SET_PKT_DATA_LEN: + btc_ble_set_pkt_data_len(arg->set_pkt_data_len.remote_device, arg->set_pkt_data_len.tx_data_length, btc_set_pkt_length_callback); + break; + case BTC_GAP_BLE_ACT_SET_RAND_ADDRESS: { + BD_ADDR bd_addr; + memcpy(bd_addr, arg->set_rand_addr.rand_addr, sizeof(BD_ADDR)); + btc_ble_set_rand_addr(bd_addr, btc_set_rand_addr_callback); + break; + } + case BTC_GAP_BLE_ACT_CLEAR_RAND_ADDRESS: { + btc_ble_clear_rand_addr(); + break; + } + case BTC_GAP_BLE_ACT_CONFIG_LOCAL_PRIVACY: + btc_ble_config_local_privacy(arg->cfg_local_privacy.privacy_enable, btc_set_local_privacy_callback); + break; + case BTC_GAP_BLE_ACT_CONFIG_LOCAL_ICON: + btc_ble_config_local_icon(arg->cfg_local_icon.icon); + break; + case BTC_GAP_BLE_ACT_UPDATE_WHITE_LIST: + BTA_DmUpdateWhiteList(arg->update_white_list.add_remove, arg->update_white_list.remote_bda, arg->update_white_list.wl_addr_type, btc_update_whitelist_complete_callback); + break; + case BTC_GAP_BLE_ACT_CLEAR_WHITE_LIST: + BTA_DmClearWhiteList(btc_update_whitelist_complete_callback); + break; + case BTC_GAP_BLE_ACT_READ_RSSI: + BTA_DmReadRSSI(arg->read_rssi.remote_addr, BTA_TRANSPORT_LE, btc_read_ble_rssi_cmpl_callback); + break; +#if (BLE_42_FEATURE_SUPPORT == TRUE) + case BTC_GAP_BLE_ACT_SET_CONN_PARAMS: + BTA_DmSetBlePrefConnParams(arg->set_conn_params.bd_addr, arg->set_conn_params.min_conn_int, + arg->set_conn_params.max_conn_int, arg->set_conn_params.slave_latency, + arg->set_conn_params.supervision_tout); + break; +#endif // #if (BLE_42_FEATURE_SUPPORT == TRUE) + case BTC_GAP_BLE_ACT_SET_DEV_NAME: + BTA_DmSetDeviceName(arg->set_dev_name.device_name); + break; + case BTC_GAP_BLE_ACT_GET_DEV_NAME: + BTA_DmGetDeviceName(btc_gap_ble_get_dev_name_callback); + break; +#if (BLE_42_FEATURE_SUPPORT == TRUE) + case BTC_GAP_BLE_ACT_CFG_ADV_DATA_RAW: + btc_ble_set_adv_data_raw(arg->cfg_adv_data_raw.raw_adv, + arg->cfg_adv_data_raw.raw_adv_len, + btc_adv_data_raw_callback); + break; + case BTC_GAP_BLE_ACT_CFG_SCAN_RSP_DATA_RAW: + btc_ble_set_scan_rsp_data_raw(arg->cfg_scan_rsp_data_raw.raw_scan_rsp, + arg->cfg_scan_rsp_data_raw.raw_scan_rsp_len, + btc_scan_rsp_data_raw_callback); + break; + case BTC_GAP_BLE_UPDATE_DUPLICATE_SCAN_EXCEPTIONAL_LIST: + btc_ble_update_duplicate_exceptional_list(arg->update_duplicate_exceptional_list.subcode, + arg->update_duplicate_exceptional_list.info_type, + arg->update_duplicate_exceptional_list.device_info, + btc_update_duplicate_exceptional_list_callback); + break; +#endif // #if (BLE_42_FEATURE_SUPPORT == TRUE) +#if (SMP_INCLUDED == TRUE) + case BTC_GAP_BLE_SET_ENCRYPTION_EVT: { + BD_ADDR bd_addr; + memcpy(bd_addr, arg->set_encryption.bd_addr, sizeof(BD_ADDR)); + BTA_DmSetEncryption(bd_addr, BT_TRANSPORT_LE, btc_set_encryption_callback, + (tBTA_DM_BLE_SEC_ACT)arg->set_encryption.sec_act); + break; + } + case BTC_GAP_BLE_SET_SECURITY_PARAM_EVT: { + uint8_t *value = arg->set_security_param.value; + switch(arg->set_security_param.param_type) { + case ESP_BLE_SM_PASSKEY: + break; + case ESP_BLE_SM_AUTHEN_REQ_MODE: { + uint8_t authen_req = 0; + STREAM_TO_UINT8(authen_req, value); + bta_dm_co_ble_set_auth_req(authen_req); + break; + } + case ESP_BLE_SM_IOCAP_MODE: { + uint8_t iocap = 0; + STREAM_TO_UINT8(iocap, value); + bta_dm_co_ble_set_io_cap(iocap); + break; + } + case ESP_BLE_SM_SET_INIT_KEY: { + uint8_t init_key = 0; + STREAM_TO_UINT8(init_key, value); + bta_dm_co_ble_set_init_key_req(init_key); + break; + } + case ESP_BLE_SM_SET_RSP_KEY: { + uint8_t rsp_key = 0; + STREAM_TO_UINT8(rsp_key, value); + bta_dm_co_ble_set_rsp_key_req(rsp_key); + break; + } + case ESP_BLE_SM_MAX_KEY_SIZE: { + uint8_t key_size = 0; + STREAM_TO_UINT8(key_size, value); + bta_dm_co_ble_set_max_key_size(key_size); + break; + } + case ESP_BLE_SM_MIN_KEY_SIZE: { + uint8_t key_size = 0; + STREAM_TO_UINT8(key_size, value); + bta_dm_co_ble_set_min_key_size(key_size); + break; + } + case ESP_BLE_SM_SET_STATIC_PASSKEY: { + uint32_t passkey = 0; + for(uint8_t i = 0; i < arg->set_security_param.len; i++) + { + passkey += (((uint8_t *)value)[i]<<(8*i)); + } + BTA_DmBleSetStaticPasskey(true, passkey); + break; + } + case ESP_BLE_SM_CLEAR_STATIC_PASSKEY: { + BTA_DmBleSetStaticPasskey(false, 0); + break; + } + case ESP_BLE_SM_ONLY_ACCEPT_SPECIFIED_SEC_AUTH: { + uint8_t enable = 0; + STREAM_TO_UINT8(enable, value); + bta_dm_co_ble_set_accept_auth_enable(enable); + break; + } + case ESP_BLE_SM_OOB_SUPPORT: { + uint8_t enable = 0; + STREAM_TO_UINT8(enable, value); + bta_dm_co_ble_oob_support(enable); + break; + } + default: + break; + } + break; + } + case BTC_GAP_BLE_SECURITY_RSP_EVT: { + BD_ADDR bd_addr; + tBTA_DM_BLE_SEC_GRANT res = arg->sec_rsp.accept ? BTA_DM_SEC_GRANTED : BTA_DM_SEC_PAIR_NOT_SPT; + memcpy(bd_addr, arg->sec_rsp.bd_addr, sizeof(BD_ADDR)); + BTA_DmBleSecurityGrant(bd_addr, res); + break; + } + case BTC_GAP_BLE_PASSKEY_REPLY_EVT: { + BD_ADDR bd_addr; + memcpy(bd_addr, arg->enc_passkey_replay.bd_addr, sizeof(BD_ADDR)); + BTA_DmBlePasskeyReply(bd_addr, arg->enc_passkey_replay.accept, arg->enc_passkey_replay.passkey); + break; + } + case BTC_GAP_BLE_CONFIRM_REPLY_EVT: { + BD_ADDR bd_addr; + memcpy(bd_addr, arg->enc_comfirm_replay.bd_addr, sizeof(BD_ADDR)); + BTA_DmBleConfirmReply(bd_addr, arg->enc_comfirm_replay.accept); + break; + } + case BTC_GAP_BLE_REMOVE_BOND_DEV_EVT: { + BD_ADDR bd_addr; + memcpy(bd_addr, arg->remove_bond_device.bd_addr, sizeof(BD_ADDR)); + BTA_DmRemoveDevice(bd_addr, BT_TRANSPORT_LE); + break; + } + case BTC_GAP_BLE_OOB_REQ_REPLY_EVT: + BTA_DmOobReply(arg->oob_req_reply.bd_addr, arg->oob_req_reply.len, arg->oob_req_reply.p_value); + break; + case BTC_GAP_BLE_SC_OOB_REQ_REPLY_EVT: + BTA_DmSecureConnectionOobReply(arg->sc_oob_req_reply.bd_addr, arg->sc_oob_req_reply.p_c, arg->sc_oob_req_reply.p_r); + break; + case BTC_GAP_BLE_SC_CR_OOB_DATA_EVT: + BTA_DmSecureConnectionCreateOobData(); + break; +#endif ///SMP_INCLUDED == TRUE + case BTC_GAP_BLE_DISCONNECT_EVT: + btc_ble_disconnect(arg->disconnect.remote_device); + break; + case BTC_GAP_BLE_SET_AFH_CHANNELS: + btc_gap_ble_set_channels(arg->set_channels.channels); + break; +#if (BLE_50_FEATURE_SUPPORT == TRUE) + case BTC_GAP_BLE_READ_PHY: + BTC_TRACE_DEBUG("BTC_GAP_BLE_READ_PHY"); + BTA_DmBleGapReadPHY(arg_5->read_phy.bd_addr); + break; + case BTC_GAP_BLE_SET_PREFERED_DEF_PHY: + BTC_TRACE_DEBUG("BTC_GAP_BLE_SET_PREFERED_DEF_PHY"); + BTA_DmBleGapSetPreferedDefaultPHY(arg_5->set_perf_def_phy.tx_phy_mask, + arg_5->set_perf_def_phy.rx_phy_mask); + break; + case BTC_GAP_BLE_SET_DEF_PHY: + BTC_TRACE_DEBUG("BTC_GAP_BLE_SET_DEF_PHY"); + BTA_DmBleGapSetPreferedPHY(arg_5->set_def_phy.bd_addr, + arg_5->set_def_phy.all_phys_mask, + arg_5->set_def_phy.tx_phy_mask, + arg_5->set_def_phy.rx_phy_mask, + arg_5->set_def_phy.phy_options); + break; + case BTC_GAP_BLE_SET_EXT_ADV_RAND_ADDR: + BTA_DmBleGapExtAdvSetRandaddr(arg_5->ext_adv_set_rand_addr.instance, arg_5->ext_adv_set_rand_addr.rand_addr); + break; + case BTC_GAP_BLE_SET_EXT_ADV_PARAMS: { + tBTA_DM_BLE_GAP_EXT_ADV_PARAMS params = {0}; + params.type = arg_5->ext_adv_set_params.params.type; + params.interval_min = arg_5->ext_adv_set_params.params.interval_min; + params.interval_max = arg_5->ext_adv_set_params.params.interval_max; + params.channel_map = arg_5->ext_adv_set_params.params.channel_map; + params.own_addr_type = arg_5->ext_adv_set_params.params.own_addr_type; + params.peer_addr_type = arg_5->ext_adv_set_params.params.peer_addr_type; + params.filter_policy = arg_5->ext_adv_set_params.params.filter_policy; + params.tx_power = arg_5->ext_adv_set_params.params.tx_power; + params.primary_phy = arg_5->ext_adv_set_params.params.primary_phy; + params.max_skip = arg_5->ext_adv_set_params.params.max_skip; + params.secondary_phy = arg_5->ext_adv_set_params.params.secondary_phy; + params.sid = arg_5->ext_adv_set_params.params.sid; + params.scan_req_notif = arg_5->ext_adv_set_params.params.scan_req_notif; + + memcpy(params.peer_addr, arg_5->ext_adv_set_params.params.peer_addr, sizeof(BD_ADDR)); + + BTC_TRACE_DEBUG("BTC_GAP_BLE_SET_EXT_ADV_PARAMS"); + BTA_DmBleGapExtAdvSetParams(arg_5->ext_adv_set_params.instance, + (const tBTA_DM_BLE_GAP_EXT_ADV_PARAMS *)¶ms); + break; + } + case BTC_GAP_BLE_CFG_EXT_ADV_DATA_RAW: + BTC_TRACE_DEBUG("BTC_GAP_BLE_CFG_EXT_ADV_DATA_RAW"); + BTA_DmBleGapConfigExtAdvDataRaw(FALSE, arg_5->ext_adv_cfg_data.instance, + arg_5->ext_adv_cfg_data.length, + (const UINT8 *)arg_5->ext_adv_cfg_data.data); + break; + case BTC_GAP_BLE_CFG_EXT_SCAN_RSP_DATA_RAW: + BTC_TRACE_DEBUG("BTC_GAP_BLE_CFG_EXT_SCAN_RSP_DATA_RAW"); + BTA_DmBleGapConfigExtAdvDataRaw(TRUE, arg_5->ext_adv_cfg_data.instance, + arg_5->ext_adv_cfg_data.length, + (const UINT8 *)arg_5->ext_adv_cfg_data.data); + break; + case BTC_GAP_BLE_EXT_ADV_START: { + BTC_TRACE_DEBUG("BTC_GAP_BLE_EXT_ADV_START"); + for (int k = 0; k < arg_5->ext_adv_start.num_adv; k++) { + BTC_TRACE_DEBUG("adv_handle[%d] = %d, duration[%d] = %d, max_adv_evt[%d] = %d", k, arg_5->ext_adv_start.ext_adv[k].instance, k, + arg_5->ext_adv_start.ext_adv[k].duration, k, arg_5->ext_adv_start.ext_adv[k].max_events); + } + BTA_DmBleGapExtAdvEnable(TRUE, arg_5->ext_adv_start.num_adv, + (tBTA_DM_BLE_EXT_ADV *)arg_5->ext_adv_start.ext_adv); + break; + } + case BTC_GAP_BLE_EXT_ADV_STOP: { + BTC_TRACE_DEBUG("BTC_GAP_BLE_EXT_ADV_STOP"); + uint8_t num_adv = arg_5->ext_adv_stop.num_adv; + if(num_adv > 0) { + tBTA_DM_BLE_EXT_ADV *ext_adv = osi_malloc(num_adv * sizeof(esp_ble_gap_ext_adv_t)); + if(ext_adv) { + for (uint8_t i = 0; i < num_adv; i++) { + ext_adv[i].instance = arg_5->ext_adv_stop.ext_adv_inst[i]; + ext_adv[i].duration = 0; + ext_adv[i].max_events = 0; + } + BTA_DmBleGapExtAdvEnable(FALSE, num_adv, ext_adv); + osi_free(ext_adv); + } else { + BTC_TRACE_ERROR("%s no mem\n", __func__); + } + } else { + BTA_DmBleGapExtAdvEnable(FALSE, 0, NULL); + } + break; + } + case BTC_GAP_BLE_EXT_ADV_SET_REMOVE: + BTA_DmBleGapExtAdvSetRemove(arg_5->ext_adv_set_remove.instance); + break; + case BTC_GAP_BLE_EXT_ADV_SET_CLEAR: + BTA_DmBleGapExtAdvSetClear(); + break; + case BTC_GAP_BLE_SET_PERIODIC_ADV_PARAMS: { + tBTA_DM_BLE_Periodic_Adv_Params params = {0}; + params.interval_min = arg_5->peridic_adv_set_params.params.interval_min; + params.interval_max = arg_5->peridic_adv_set_params.params.interval_max; + params.properties = arg_5->peridic_adv_set_params.params.properties; + BTC_TRACE_DEBUG("BTC_GAP_BLE_SET_PERIODIC_ADV_PARAMS"); + BTA_DmBleGapPeriodicAdvSetParams(arg_5->peridic_adv_set_params.instance, + ¶ms); + break; + } + case BTC_GAP_BLE_CFG_PERIODIC_ADV_DATA_RAW: + BTC_TRACE_DEBUG("BTC_GAP_BLE_CFG_PERIODIC_ADV_DATA_RAW"); + BTA_DmBleGapPeriodicAdvCfgDataRaw(arg_5->periodic_adv_cfg_data.instance, + arg_5->periodic_adv_cfg_data.len, + (const UINT8 *)arg_5->periodic_adv_cfg_data.data, + arg_5->periodic_adv_cfg_data.only_update_did); + break; + case BTC_GAP_BLE_PERIODIC_ADV_START: + BTC_TRACE_DEBUG("BTC_GAP_BLE_PERIODIC_ADV_START"); + BTA_DmBleGapPeriodicAdvEnable(((arg_5->periodic_adv_start.include_adi)<<1)|0x01, arg_5->periodic_adv_start.instance); + break; + case BTC_GAP_BLE_PERIODIC_ADV_STOP: + BTC_TRACE_DEBUG("BTC_GAP_BLE_PERIODIC_ADV_STOP"); + BTA_DmBleGapPeriodicAdvEnable(FALSE, arg_5->periodic_adv_stop.instance); + break; + case BTC_GAP_BLE_PERIODIC_ADV_CREATE_SYNC: { + tBTA_DM_BLE_Periodic_Sync_Params params = {0}; + params.filter_policy = arg_5->periodic_adv_create_sync.params.filter_policy; + params.sid = arg_5->periodic_adv_create_sync.params.sid; + params.addr_type = arg_5->periodic_adv_create_sync.params.addr_type; + params.skip = arg_5->periodic_adv_create_sync.params.skip; + params.sync_timeout = arg_5->periodic_adv_create_sync.params.sync_timeout; + #if (CONFIG_BT_BLE_FEAT_CREATE_SYNC_ENH) + params.reports_disabled = arg_5->periodic_adv_create_sync.params.reports_disabled; + params.filter_duplicates = arg_5->periodic_adv_create_sync.params.filter_duplicates; + #endif + + memcpy(params.addr, arg_5->periodic_adv_create_sync.params.addr, sizeof(BD_ADDR)); + BTC_TRACE_DEBUG("BTC_GAP_BLE_PERIODIC_ADV_CREATE_SYNC"); + BTA_DmBleGapPeriodicAdvCreateSync(¶ms); + break; + } + case BTC_GAP_BLE_PERIODIC_ADV_SYNC_CANCEL: + BTC_TRACE_DEBUG("BTC_GAP_BLE_PERIODIC_ADV_SYNC_CANCEL"); + BTA_DmBleGapPeriodicAdvSyncCancel(); + break; + case BTC_GAP_BLE_PERIODIC_ADV_SYNC_TERMINATE: + BTC_TRACE_DEBUG("BTC_GAP_BLE_PERIODIC_ADV_SYNC_TERMINATE"); + BTA_DmBleGapPeriodicAdvSyncTerm(arg_5->periodic_adv_sync_term.sync_handle); + break; + case BTC_GAP_BLE_PERIODIC_ADV_ADD_DEV_TO_LIST: + BTC_TRACE_DEBUG("BTC_GAP_BLE_PERIODIC_ADV_ADD_DEV_TO_LIST"); + BTA_DmBleGapPeriodicAdvAddDevToList(arg_5->periodic_adv_add_dev.addr_type, + arg_5->periodic_adv_add_dev.addr, + arg_5->periodic_adv_add_dev.sid); + break; + case BTC_GAP_BLE_PERIODIC_REMOVE_ADD_DEV_FROM_LIST: + BTC_TRACE_DEBUG("BTC_GAP_BLE_PERIODIC_REMOVE_ADD_DEV_FROM_LIST"); + BTA_DmBleGapPeriodicAdvRemoveDevFromList(arg_5->periodic_adv_remove_dev.addr_type, + arg_5->periodic_adv_remove_dev.addr, + arg_5->periodic_adv_remove_dev.sid); + break; + case BTC_GAP_BLE_PERIODIC_CLEAR_DEV: + BTC_TRACE_DEBUG("BTC_GAP_BLE_PERIODIC_CLEAR_DEV"); + BTA_DmBleGapPeriodicAdvClearDev(); + break; + case BTC_GAP_BLE_SET_EXT_SCAN_PARAMS: { + tBTA_DM_BLE_EXT_SCAN_PARAMS params = {0}; + params.own_addr_type = arg_5->set_ext_scan_params.params.own_addr_type; + params.filter_policy = arg_5->set_ext_scan_params.params.filter_policy; + params.scan_duplicate = arg_5->set_ext_scan_params.params.scan_duplicate; + params.cfg_mask = arg_5->set_ext_scan_params.params.cfg_mask; + if (params.cfg_mask & BTA_DM_BLE_GAP_EXT_SCAN_UNCODE_MASK) { + params.uncoded_cfg.scan_type = arg_5->set_ext_scan_params.params.uncoded_cfg.scan_type; + params.uncoded_cfg.scan_interval = arg_5->set_ext_scan_params.params.uncoded_cfg.scan_interval; + params.uncoded_cfg.scan_window = arg_5->set_ext_scan_params.params.uncoded_cfg.scan_window; + } + + if (params.cfg_mask & BTA_DM_BLE_GAP_EXT_SCAN_CODE_MASK) { + params.coded_cfg.scan_type = arg_5->set_ext_scan_params.params.coded_cfg.scan_type; + params.coded_cfg.scan_interval = arg_5->set_ext_scan_params.params.coded_cfg.scan_interval; + params.coded_cfg.scan_window = arg_5->set_ext_scan_params.params.coded_cfg.scan_window; + } + + BTC_TRACE_DEBUG("BTC_GAP_BLE_SET_EXT_SCAN_PARAMS"); + BTA_DmBleGapSetExtScanParams(¶ms); + break; + } + case BTC_GAP_BLE_START_EXT_SCAN: + BTC_TRACE_DEBUG("BTC_GAP_BLE_START_EXT_SCAN"); + BTA_DmBleGapExtScan(TRUE, arg_5->start_ext_scan.duration, arg_5->start_ext_scan.period); + break; + case BTC_GAP_BLE_STOP_EXT_SCAN: + BTC_TRACE_DEBUG("BTC_GAP_BLE_STOP_EXT_SCAN"); + BTA_DmBleGapExtScan(FALSE, 0, 0); + break; + case BTC_GAP_BLE_SET_EXT_PEFER_CONNET_PARAMS: + BTC_TRACE_DEBUG("BTC_GAP_BLE_SET_EXT_PEFER_CONNET_PARAMS"); + BTA_DmBleGapPreferExtConnectParamsSet(arg_5->set_ext_conn_params.addr, + arg_5->set_ext_conn_params.phy_mask, + (const tBTA_DM_BLE_CONN_PARAMS *)&arg_5->set_ext_conn_params.phy_1m_conn_params, + (const tBTA_DM_BLE_CONN_PARAMS *)&arg_5->set_ext_conn_params.phy_2m_conn_params, + (const tBTA_DM_BLE_CONN_PARAMS *)&arg_5->set_ext_conn_params.phy_coded_conn_params); + break; +#endif // #if (BLE_50_FEATURE_SUPPORT == TRUE) +#if (BLE_FEAT_PERIODIC_ADV_SYNC_TRANSFER == TRUE) + case BTC_GAP_BLE_PERIODIC_ADV_RECV_ENABLE: + BTC_TRACE_DEBUG("BTC_GAP_BLE_PERIODIC_ADV_RECV_ENABLE"); + BTA_DmBleGapPeriodicAdvRecvEnable(arg_5->periodic_adv_recv_en.sync_handle, + arg_5->periodic_adv_recv_en.enable); + break; + case BTC_GAP_BLE_PERIODIC_ADV_SYNC_TRANS: + BTC_TRACE_DEBUG("BTC_GAP_BLE_PERIODIC_ADV_SYNC_TRANS"); + BTA_DmBleGapPeriodicAdvSyncTrans(arg_5->periodic_adv_sync_trans.addr, + arg_5->periodic_adv_sync_trans.service_data, + arg_5->periodic_adv_sync_trans.sync_handle); + break; + case BTC_GAP_BLE_PERIODIC_ADV_SET_INFO_TRANS: + BTC_TRACE_DEBUG("BTC_GAP_BLE_PERIODIC_ADV_SET_INFO_TRANS"); + BTA_DmBleGapPeriodicAdvSetInfoTrans(arg_5->periodic_adv_set_info_trans.addr, + arg_5->periodic_adv_set_info_trans.service_data, + arg_5->periodic_adv_set_info_trans.adv_handle); + break; + case BTC_GAP_BLE_SET_PERIODIC_ADV_SYNC_TRANS_PARAMS: + BTC_TRACE_DEBUG("BTC_GAP_BLE_SET_PERIODIC_ADV_SYNC_TRANS_PARAMS"); + BTA_DmBleGapSetPeriodicAdvSyncTransParams(arg_5->set_periodic_adv_sync_trans_params.addr, + (tBTA_DM_BLE_PAST_PARAMS *)&arg_5->set_periodic_adv_sync_trans_params.params); + break; +#endif +#if (BLE_42_FEATURE_SUPPORT == TRUE) + case BTC_GAP_BLE_DTM_TX_START: + btc_ble_dtm_tx_start(arg->dtm_tx_start.tx_channel, arg->dtm_tx_start.len_of_data, arg->dtm_tx_start.pkt_payload, btc_dtm_tx_start_callback); + break; + case BTC_GAP_BLE_DTM_RX_START: + btc_ble_dtm_rx_start(arg->dtm_rx_start.rx_channel, btc_dtm_rx_start_callback); + break; +#endif // if (BLE_42_FEATURE_SUPPORT == TRUE) + case BTC_GAP_BLE_DTM_STOP: + btc_ble_dtm_stop(btc_dtm_stop_callback); + break; +#if (BLE_50_FEATURE_SUPPORT == TRUE) + case BTC_GAP_BLE_DTM_ENH_TX_START: + btc_ble_dtm_enhance_tx_start(arg_5->dtm_enh_tx_start.tx_channel, arg_5->dtm_enh_tx_start.len_of_data, arg_5->dtm_enh_tx_start.pkt_payload, arg_5->dtm_enh_tx_start.phy, btc_dtm_tx_start_callback); + break; + case BTC_GAP_BLE_DTM_ENH_RX_START: + btc_ble_dtm_enhance_rx_start(arg_5->dtm_enh_rx_start.rx_channel, arg_5->dtm_enh_rx_start.phy, arg_5->dtm_enh_rx_start.modulation_index, btc_dtm_rx_start_callback); + break; +#endif // if (BLE_50_FEATURE_SUPPORT == TRUE) + default: + break; + } + + btc_gap_ble_arg_deep_free(msg); +} + +//register connection parameter update callback +void btc_gap_callback_init(void) +{ + BTM_BleRegiseterConnParamCallback(btc_update_conn_param_callback); +#if (BLE_50_FEATURE_SUPPORT == TRUE) + BTM_BleGapRegisterCallback(btc_ble_5_gap_callback); +#endif // #if (BLE_50_FEATURE_SUPPORT == TRUE) +} + +bool btc_gap_ble_init(void) +{ +#if (BLE_42_FEATURE_SUPPORT == TRUE) + btc_gap_ble_env_t *p_env = &btc_gap_ble_env; + p_env->adv_rpt_queue = pkt_queue_create(); + assert(p_env->adv_rpt_queue != NULL); + + p_env->adv_rpt_ready = osi_event_create(btc_gap_ble_adv_pkt_handler, NULL); + assert(p_env->adv_rpt_ready != NULL); + osi_event_bind(p_env->adv_rpt_ready, btc_get_current_thread(), BTC_GAP_BLE_ADV_RPT_QUEUE_IDX); +#endif + return true; +} + +void btc_gap_ble_deinit(void) +{ +#if (BLE_42_FEATURE_SUPPORT == TRUE) + btc_gap_ble_env_t *p_env = &btc_gap_ble_env; + + osi_event_delete(p_env->adv_rpt_ready); + p_env->adv_rpt_ready = NULL; + + pkt_queue_destroy(p_env->adv_rpt_queue, NULL); + p_env->adv_rpt_queue = NULL; + + btc_cleanup_adv_data(&gl_bta_adv_data); + btc_cleanup_adv_data(&gl_bta_scan_rsp_data); +#endif // #if (BLE_42_FEATURE_SUPPORT == TRUE) +} + +#if SCAN_QUEUE_CONGEST_CHECK +void btc_adv_list_free(void *data) +{ + osi_free(data); +} + +void btc_adv_list_init(void) +{ + osi_mutex_new(&adv_list_lock); + adv_filter_list = list_new(btc_adv_list_free); +} + +void btc_adv_list_deinit(void) +{ + osi_mutex_free(&adv_list_lock); + if(adv_filter_list) { + list_free(adv_filter_list); + adv_filter_list = NULL; + } +} +void btc_adv_list_add_packet(void * data) +{ + if(!data) { + BTC_TRACE_ERROR("%s data is NULL", __func__); + return; + } + btc_adv_list_lock(); + list_prepend(adv_filter_list, data); + btc_adv_list_unlock(); +} + +uint32_t btc_get_adv_list_length(void) +{ + if(!adv_filter_list) { + BTC_TRACE_ERROR("%s adv_filter_list is NULL", __func__); + return 0; + } + btc_adv_list_lock(); + size_t length = list_length(adv_filter_list); + btc_adv_list_unlock(); + + return length; +} + +void btc_adv_list_refresh(void) +{ + if(!adv_filter_list) { + BTC_TRACE_ERROR("%s adv_filter_list is NULL", __func__); + return ; + } + btc_adv_list_lock(); + list_clear(adv_filter_list); + btc_adv_list_unlock(); +} + +bool btc_check_adv_list(uint8_t * addr, uint8_t addr_type) +{ + bool found = false; + if(!adv_filter_list || !addr) { + BTC_TRACE_ERROR("%s adv_filter_list is NULL", __func__); + return found; + } + + btc_adv_list_lock(); + for (const list_node_t *node = list_begin(adv_filter_list); node != list_end(adv_filter_list); node = list_next(node)) { + btc_adv_packet_t *packet = (btc_adv_packet_t *)list_node(node); + if(!bdcmp(addr, packet->addr) && packet->addr_type == addr_type) { + found = true; + break; + } + } + btc_adv_list_unlock(); + if(!found) { + btc_adv_packet_t *adv_packet = osi_malloc(sizeof(btc_adv_packet_t)); + if(adv_packet) { + adv_packet->addr_type = addr_type; + bdcpy(adv_packet->addr, addr); + btc_adv_list_add_packet(adv_packet); + } else { + BTC_TRACE_ERROR("%s adv_packet malloc failed", __func__); + } + } + return found; +} + +void btc_adv_list_lock(void) +{ + osi_mutex_lock(&adv_list_lock, OSI_MUTEX_MAX_TIMEOUT); +} + +void btc_adv_list_unlock(void) +{ + osi_mutex_unlock(&adv_list_lock); +} +#endif +#endif ///BLE_INCLUDED == TRUE diff --git a/lib/bt/host/bluedroid/btc/profile/std/gap/btc_gap_bt.c b/lib/bt/host/bluedroid/btc/profile/std/gap/btc_gap_bt.c new file mode 100644 index 00000000..2f9dedfd --- /dev/null +++ b/lib/bt/host/bluedroid/btc/profile/std/gap/btc_gap_bt.c @@ -0,0 +1,1201 @@ +/* + * SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "esp_bt_defs.h" +#include "esp_gap_bt_api.h" +#include "btc_gap_bt.h" +#include "btc/btc_storage.h" +#include "bta/bta_api.h" +#include "common/bt_trace.h" +#include "common/bt_target.h" +#include "btc/btc_manage.h" +#include "btc/btc_util.h" +#include "osi/allocator.h" +#include "bta/bta_dm_co.h" + +#if (BTC_GAP_BT_INCLUDED == TRUE) + +#define COD_UNCLASSIFIED ((0x1F) << 8) + +#define BTC_STORAGE_FILL_PROPERTY(p_prop, t, l, p_v) \ + (p_prop)->type = t;(p_prop)->len = l; (p_prop)->val = (p_v); + +static void bte_search_devices_evt(tBTA_DM_SEARCH_EVT event, tBTA_DM_SEARCH *p_data); +static void bte_dm_search_services_evt(tBTA_DM_SEARCH_EVT event, tBTA_DM_SEARCH *p_data); +static void bte_dm_remote_service_record_evt(tBTA_DM_SEARCH_EVT event, tBTA_DM_SEARCH *p_data); +static void search_services_copy_cb(btc_msg_t *msg, void *p_dest, void *p_src); +static void search_service_record_copy_cb(btc_msg_t *msg, void *p_dest, void *p_src); + +static bool btc_gap_bt_inquiry_in_progress = false; + +static inline void btc_gap_bt_cb_to_app(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *param) +{ + esp_bt_gap_cb_t cb = (esp_bt_gap_cb_t)btc_profile_cb_get(BTC_PID_GAP_BT); + if (cb) { + cb(event, param); + } +} + +static void btc_bt_set_scan_mode(esp_bt_connection_mode_t c_mode, esp_bt_discovery_mode_t d_mode) +{ + tBTA_DM_DISC disc_mode; + tBTA_DM_CONN conn_mode; + + switch (c_mode) { + case ESP_BT_NON_CONNECTABLE: + conn_mode = BTA_DM_NON_CONN; + break; + case ESP_BT_CONNECTABLE: + conn_mode = BTA_DM_CONN; + break; + default: + BTC_TRACE_WARNING("invalid connection mode (0x%x)", c_mode); + return; + } + + switch (d_mode) { + case ESP_BT_NON_DISCOVERABLE: + disc_mode = BTA_DM_NON_DISC; + break; + case ESP_BT_LIMITED_DISCOVERABLE: + disc_mode = BTA_DM_LIMITED_DISC; + break; + case ESP_BT_GENERAL_DISCOVERABLE: + disc_mode = BTA_DM_GENERAL_DISC; + break; + default: + BTC_TRACE_WARNING("invalid discovery mode (0x%x)", d_mode); + return; + } + + BTA_DmSetVisibility(disc_mode, conn_mode, BTA_DM_IGNORE, BTA_DM_IGNORE); + return; +} + +static void btc_gap_bt_start_discovery(btc_gap_bt_args_t *arg) +{ + tBTA_DM_INQ inq_params; + tBTA_SERVICE_MASK services = 0; + + BTIF_TRACE_EVENT("%s", __FUNCTION__); + + inq_params.mode = (arg->start_disc.mode == ESP_BT_INQ_MODE_GENERAL_INQUIRY) ? + BTA_DM_GENERAL_INQUIRY : BTA_DM_LIMITED_INQUIRY; + inq_params.duration = arg->start_disc.inq_len; + inq_params.max_resps = arg->start_disc.num_rsps; + + inq_params.report_dup = TRUE; + inq_params.filter_type = BTA_DM_INQ_CLR; + /* TODO: Filter device by BDA needs to be implemented here */ + + /* Will be enabled to TRUE once inquiry busy level has been received */ + btc_gap_bt_inquiry_in_progress = FALSE; + /* find nearby devices */ + BTA_DmSearch(&inq_params, services, bte_search_devices_evt); + + return; +} + +static void btc_gap_bt_cancel_discovery(void) +{ + BTA_DmSearchCancel(); +} + +static void btc_gap_bt_get_remote_services(bt_bdaddr_t *remote_bda) +{ + BTA_DmDiscover(remote_bda->address, BTA_ALL_SERVICE_MASK, + bte_dm_search_services_evt, TRUE); +} + +static void btc_gap_bt_get_remote_service_record(btc_gap_bt_args_t *arg) +{ + esp_bt_uuid_t *uuid = &arg->get_rmt_srv_rcd.uuid; + bt_bdaddr_t *remote_bda = &arg->get_rmt_srv_rcd.bda; + + tSDP_UUID sdp_uuid; + + sdp_uuid.len = uuid->len; + memcpy(&sdp_uuid.uu, &uuid->uuid, uuid->len); + + BTA_DmDiscoverUUID(remote_bda->address, &sdp_uuid, + bte_dm_remote_service_record_evt, TRUE); +} + + +/******************************************************************************* +** +** Function search_devices_copy_cb +** +** Description Deep copy callback for search devices event +** +** Returns void +** +*******************************************************************************/ +static void search_devices_copy_cb(btc_msg_t *msg, void *p_dest, void *p_src) +{ + tBTA_DM_SEARCH_PARAM *p_dest_data = (tBTA_DM_SEARCH_PARAM *) p_dest; + tBTA_DM_SEARCH_PARAM *p_src_data = (tBTA_DM_SEARCH_PARAM *) p_src; + if (!p_src) { + return; + } + p_dest_data->p_data = (void *)osi_malloc(p_dest_data->len); + memset(p_dest_data->p_data, 0x00, p_dest_data->len); + memcpy(p_dest_data->p_data, p_src_data->p_data, p_dest_data->len); + + if ( p_dest_data->len > sizeof(tBTA_DM_SEARCH)){ + switch (p_dest_data->event) { + case BTA_DM_INQ_RES_EVT: { + if (p_src_data->p_data->inq_res.p_eir) { + p_dest_data->p_data->inq_res.p_eir = (UINT8 *)(p_dest_data->p_data) + sizeof(tBTA_DM_SEARCH); + memcpy(p_dest_data->p_data->inq_res.p_eir, p_src_data->p_data->inq_res.p_eir, HCI_EXT_INQ_RESPONSE_LEN); + } + } + break; + + case BTA_DM_DISC_RES_EVT: { + if (p_src_data->p_data->disc_res.raw_data_size && p_src_data->p_data->disc_res.p_raw_data) { + p_dest_data->p_data->disc_res.p_raw_data = (UINT8 *)(p_dest_data->p_data) + sizeof(tBTA_DM_SEARCH); + memcpy(p_dest_data->p_data->disc_res.p_raw_data, + p_src_data->p_data->disc_res.p_raw_data, + p_src_data->p_data->disc_res.raw_data_size); + } + } + break; + } + } +} + +/******************************************************************************* +** +** Function search_service_record_copy_cb +** +** Description Deep copy callback for search service record event +** +** Returns void +** +*******************************************************************************/ +static void search_service_record_copy_cb(btc_msg_t *msg, void *p_dest, void *p_src) +{ + tBTA_DM_SEARCH_PARAM *p_dest_data = (tBTA_DM_SEARCH_PARAM *) p_dest; + tBTA_DM_SEARCH_PARAM *p_src_data = (tBTA_DM_SEARCH_PARAM *) p_src; + + if (!p_src) { + return; + } + p_dest_data->p_data = osi_malloc(p_dest_data->len); + memset(p_dest_data->p_data, 0x00, p_dest_data->len); + memcpy(p_dest_data->p_data, p_src_data->p_data, p_dest_data->len); + if ( p_dest_data->len > sizeof(tBTA_DM_SEARCH)){ + switch (p_dest_data->event) { + case BTA_DM_DISC_RES_EVT: { + if (p_src_data->p_data->disc_res.p_raw_data && p_src_data->p_data->disc_res.raw_data_size > 0) { + p_dest_data->p_data->disc_res.p_raw_data = (UINT8 *)(p_dest_data->p_data) + sizeof(tBTA_DM_SEARCH); + memcpy(p_dest_data->p_data->disc_res.p_raw_data, + p_src_data->p_data->disc_res.p_raw_data, + p_src_data->p_data->disc_res.raw_data_size); + } + } + break; + + default: + break; + } + } +} + +/******************************************************************************* +** +** Function check_eir_remote_name +** +** Description Check if remote name is in the EIR data +** +** Returns TRUE if remote name found +** Populate p_remote_name, if provided and remote name found +** +*******************************************************************************/ +static BOOLEAN check_eir_remote_name(tBTA_DM_SEARCH *p_search_data, + UINT8 *p_remote_name, UINT8 *p_remote_name_len) +{ + UINT8 *p_eir_remote_name = NULL; + UINT8 remote_name_len = 0; + + /* Check EIR for remote name and services */ + if (p_search_data->inq_res.p_eir) { + p_eir_remote_name = BTM_CheckEirData(p_search_data->inq_res.p_eir, + BTM_EIR_COMPLETE_LOCAL_NAME_TYPE, &remote_name_len); + if (!p_eir_remote_name) { + p_eir_remote_name = BTM_CheckEirData(p_search_data->inq_res.p_eir, + BTM_EIR_SHORTENED_LOCAL_NAME_TYPE, &remote_name_len); + } + + if (p_eir_remote_name) { + if (remote_name_len > BD_NAME_LEN) { + remote_name_len = BD_NAME_LEN; + } + + if (p_remote_name && p_remote_name_len) { + memcpy(p_remote_name, p_eir_remote_name, remote_name_len); + *(p_remote_name + remote_name_len) = 0; + *p_remote_name_len = remote_name_len; + } + + return TRUE; + } + } + + return FALSE; + +} + +/******************************************************************************* +** +** Function bte_search_devices_evt +** +** Description Switches context from BTE to BTIF for DM search events +** +** Returns void +** +*******************************************************************************/ +static void bte_search_devices_evt(tBTA_DM_SEARCH_EVT event, tBTA_DM_SEARCH *p_data) +{ + tBTA_DM_SEARCH_PARAM search; + search.event = event; + search.p_data = p_data; + + UINT16 param_len = 0; + + if (p_data) { + param_len += sizeof(tBTA_DM_SEARCH); + } + /* Allocate buffer to hold the pointers (deep copy). The pointers will point to the end of the tBTA_DM_SEARCH */ + switch (event) { + case BTA_DM_INQ_RES_EVT: { + if (p_data->inq_res.p_eir) { + param_len += HCI_EXT_INQ_RESPONSE_LEN; + } + } + break; + + case BTA_DM_DISC_RES_EVT: { + if (p_data->disc_res.raw_data_size && p_data->disc_res.p_raw_data) { + param_len += p_data->disc_res.raw_data_size; + } + } + break; + } + + /* if remote name is available in EIR, set the flag so that stack doesn't trigger RNR */ + if (event == BTA_DM_INQ_RES_EVT) { + p_data->inq_res.remt_name_not_required = check_eir_remote_name(p_data, NULL, NULL); + } + + search.len = param_len; + do { + btc_msg_t msg; + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_GAP_BT; + msg.act = BTC_GAP_BT_SEARCH_DEVICES_EVT; + + btc_transfer_context(&msg, &search, sizeof(tBTA_DM_SEARCH_PARAM), search_devices_copy_cb, NULL); + } while (0); +} + +static void btc_gap_bt_search_devices_evt(tBTA_DM_SEARCH_PARAM *p_data) +{ + switch (p_data->event) { + case BTA_DM_DISC_RES_EVT: { + /* remote name update */ + uint32_t bdname_len = strlen((const char *)p_data->p_data->disc_res.bd_name); + if (bdname_len) { + esp_bt_gap_dev_prop_t prop[1]; + + BTC_STORAGE_FILL_PROPERTY(&prop[0], ESP_BT_GAP_DEV_PROP_BDNAME, bdname_len + 1, p_data->p_data->disc_res.bd_name); + + esp_bt_gap_cb_param_t param; + bdcpy(param.disc_res.bda, p_data->p_data->disc_res.bd_addr); + param.disc_res.num_prop = 1; + param.disc_res.prop = prop; + btc_gap_bt_cb_to_app(ESP_BT_GAP_DISC_RES_EVT, ¶m); + } + break; + } + case BTA_DM_INQ_RES_EVT: { + /* inquiry result */ + uint32_t cod = devclass2uint (p_data->p_data->inq_res.dev_class); + + if (cod == 0) { + BTC_TRACE_DEBUG("%s cod is 0, set as unclassified", __func__); + cod = COD_UNCLASSIFIED; + } + + do { + esp_bt_gap_dev_prop_t prop[3]; + int num_prop = 0; + + memset(prop, 0, sizeof(prop)); + BTC_STORAGE_FILL_PROPERTY(&prop[0], ESP_BT_GAP_DEV_PROP_COD, sizeof(cod), &cod); + num_prop++; + + BTC_STORAGE_FILL_PROPERTY(&prop[1], ESP_BT_GAP_DEV_PROP_RSSI, 1, &(p_data->p_data->inq_res.rssi)); + num_prop++; + + if (p_data->p_data->inq_res.p_eir) { + BTC_STORAGE_FILL_PROPERTY(&prop[2], ESP_BT_GAP_DEV_PROP_EIR, HCI_EXT_INQ_RESPONSE_LEN, p_data->p_data->inq_res.p_eir); + num_prop++; + } + + /* Callback to notify upper layer of device */ + esp_bt_gap_cb_param_t param; + bdcpy(param.disc_res.bda, p_data->p_data->inq_res.bd_addr); + param.disc_res.num_prop = num_prop; + param.disc_res.prop = prop; + btc_gap_bt_cb_to_app(ESP_BT_GAP_DISC_RES_EVT, ¶m); + } while (0); + } + break; + + case BTA_DM_INQ_CMPL_EVT: + break; + case BTA_DM_DISC_CMPL_EVT: { + esp_bt_gap_cb_param_t param; + param.disc_st_chg.state = ESP_BT_GAP_DISCOVERY_STOPPED; + btc_gap_bt_cb_to_app(ESP_BT_GAP_DISC_STATE_CHANGED_EVT, ¶m); + break; + } + case BTA_DM_SEARCH_CANCEL_CMPL_EVT: { + /* if inquiry is not in progress and we get a cancel event, then + * it means we are done with inquiry, but remote_name fetches are in + * progress + * + * if inquiry is in progress, then we don't want to act on this cancel_cmpl_evt + * but instead wait for the cancel_cmpl_evt_via the busy level + */ + if (btc_gap_bt_inquiry_in_progress == false) { + esp_bt_gap_cb_param_t param; + param.disc_st_chg.state = ESP_BT_GAP_DISCOVERY_STOPPED; + btc_gap_bt_cb_to_app(ESP_BT_GAP_DISC_STATE_CHANGED_EVT, ¶m); + } + break; + } + } +} +/******************************************************************************* +** +** Function btc_gap_bt_search_service_record +** +** Description Executes search service record event in btif context +** +** Returns void +** +*******************************************************************************/ +static void btc_gap_bt_search_service_record(char *p_param) +{ + tBTA_DM_SEARCH_PARAM *p_data = (tBTA_DM_SEARCH_PARAM *)p_param; + + switch (p_data->event) { + case BTA_DM_DISC_RES_EVT: { + esp_bt_gap_cb_param_t param; + memcpy(param.rmt_srvcs.bda, p_data->p_data->disc_res.bd_addr, BD_ADDR_LEN); + if (p_data->p_data->disc_res.p_raw_data && p_data->p_data->disc_res.raw_data_size > 0) { + param.rmt_srvc_rec.stat = ESP_BT_STATUS_SUCCESS; + // param.rmt_srvc_rec.raw_data_size = p_data->p_data->disc_res.raw_data_size; + // param.rmt_srvc_rec.raw_data = p_data->p_data->disc_res.p_raw_data; + } else { + param.rmt_srvc_rec.stat = ESP_BT_STATUS_FAIL; + // param.rmt_srvc_rec.raw_data_size = 0; + // param.rmt_srvc_rec.raw_data = NULL; + } + btc_gap_bt_cb_to_app(ESP_BT_GAP_RMT_SRVC_REC_EVT, ¶m); + } + break; + case BTA_DM_DISC_CMPL_EVT: + default: + break; + } +} + + +/******************************************************************************* +** +** Function bte_dm_remote_service_record_evt +** +** Description Switches context from BTE to BTC for DM search service +** record event +** +** Returns void +** +*******************************************************************************/ +static void bte_dm_remote_service_record_evt(tBTA_DM_SEARCH_EVT event, tBTA_DM_SEARCH *p_data) +{ + tBTA_DM_SEARCH_PARAM search; + search.event = event; + search.p_data = p_data; + UINT16 param_len = 0; + + if (p_data) { + param_len += sizeof(tBTA_DM_SEARCH); + } + /* Allocate buffer to hold the pointers (deep copy). The pointers will point to the end of the tBTA_DM_SEARCH */ + if (event == BTA_DM_DISC_RES_EVT) { + if (p_data->disc_res.raw_data_size && p_data->disc_res.p_raw_data) { + param_len += p_data->disc_res.raw_data_size; + } + } + search.len = param_len; + do { + btc_msg_t msg; + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_GAP_BT; + msg.act = BTC_GAP_BT_SEARCH_SERVICE_RECORD_EVT; + btc_transfer_context(&msg, &search, sizeof(tBTA_DM_SEARCH_PARAM), search_service_record_copy_cb, NULL); + } while (0); + +} + +/******************************************************************************* +** +** Function btc_gap_bt_search_services +** +** Description Executes search services event in btc context +** +** Returns void +** +*******************************************************************************/ +static void btc_gap_bt_search_services(char *p_param) +{ + tBTA_DM_SEARCH_PARAM *p_data = (tBTA_DM_SEARCH_PARAM *)p_param; + + switch (p_data->event) { + case BTA_DM_DISC_RES_EVT: { + esp_bt_gap_cb_param_t param; + esp_bt_uuid_t *uuid_list = NULL; + memcpy(param.rmt_srvcs.bda, p_data->p_data->disc_res.bd_addr, BD_ADDR_LEN); + + param.rmt_srvcs.stat = ESP_BT_STATUS_FAIL; + if (p_data->p_data->disc_res.result == BTA_SUCCESS) { + uuid_list = osi_malloc(sizeof(esp_bt_uuid_t) * p_data->p_data->disc_res.num_uuids); + if (uuid_list) { + param.rmt_srvcs.stat = ESP_BT_STATUS_SUCCESS; + param.rmt_srvcs.num_uuids = p_data->p_data->disc_res.num_uuids; + param.rmt_srvcs.uuid_list = uuid_list; + // copy UUID list + uint8_t *i_uu = (uint8_t *)p_data->p_data->disc_res.p_uuid_list; + esp_bt_uuid_t *o_uu = uuid_list; + for (int i = 0; i < p_data->p_data->disc_res.num_uuids; i++, i_uu += ESP_UUID_LEN_128, o_uu++) { + uuid128_be_to_esp_uuid(o_uu, i_uu); + } + } + } + + if (param.rmt_srvcs.stat == ESP_BT_STATUS_FAIL) { + param.rmt_srvcs.num_uuids = 0; + param.rmt_srvcs.uuid_list = NULL; + } + btc_gap_bt_cb_to_app(ESP_BT_GAP_RMT_SRVCS_EVT, ¶m); + + if (uuid_list) { + osi_free(uuid_list); + } + } + break; + + case BTA_DM_DISC_BLE_RES_EVT: + case BTA_DM_DISC_CMPL_EVT: + default: + break; + } +} + +/******************************************************************************* +** +** Function bte_dm_search_services_evt +** +** Description Switches context from BTE to BTIF for DM search services +** event +** +** Returns void +** +*******************************************************************************/ +static void bte_dm_search_services_evt(tBTA_DM_SEARCH_EVT event, tBTA_DM_SEARCH *p_data) +{ + tBTA_DM_SEARCH_PARAM search; + search.event = event; + search.p_data = p_data; + + UINT16 param_len = 0; + if (p_data) { + param_len += sizeof(tBTA_DM_SEARCH); + } + + switch (event) { + case BTA_DM_DISC_RES_EVT: { + if ((p_data->disc_res.result == BTA_SUCCESS) && (p_data->disc_res.num_uuids > 0)) { + param_len += (p_data->disc_res.num_uuids * MAX_UUID_SIZE); + } + } break; + } + search.len = param_len; + do { + btc_msg_t msg; + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_GAP_BT; + msg.act = BTC_GAP_BT_SEARCH_SERVICES_EVT; + btc_transfer_context(&msg, &search, sizeof(tBTA_DM_SEARCH_PARAM), search_services_copy_cb, NULL); + } while (0); +} + +static void search_services_copy_cb(btc_msg_t *msg, void *p_dest, void *p_src) +{ + tBTA_DM_SEARCH_PARAM *p_dest_data = (tBTA_DM_SEARCH_PARAM *) p_dest; + tBTA_DM_SEARCH_PARAM *p_src_data = (tBTA_DM_SEARCH_PARAM *) p_src; + + if (!p_src) { + return; + } + p_dest_data->p_data = osi_malloc(p_dest_data->len); + memset(p_dest_data->p_data, 0x00, p_dest_data->len); + memcpy(p_dest_data->p_data, p_src_data->p_data, p_dest_data->len); + + if ( p_dest_data->len > sizeof(tBTA_DM_SEARCH)){ + switch (p_dest_data->event) { + case BTA_DM_DISC_RES_EVT: { + if (p_src_data->p_data->disc_res.result == BTA_SUCCESS) { + if (p_src_data->p_data->disc_res.num_uuids > 0) { + p_dest_data->p_data->disc_res.p_uuid_list = (UINT8 *)(p_dest_data->p_data) + sizeof(tBTA_DM_SEARCH); + memcpy(p_dest_data->p_data->disc_res.p_uuid_list, p_src_data->p_data->disc_res.p_uuid_list, + p_src_data->p_data->disc_res.num_uuids * MAX_UUID_SIZE); + osi_free(p_src_data->p_data->disc_res.p_uuid_list); + p_src_data->p_data->disc_res.p_uuid_list = NULL; + } + if (p_src_data->p_data->disc_res.p_raw_data != NULL) { + osi_free(p_src_data->p_data->disc_res.p_raw_data); + p_src_data->p_data->disc_res.p_raw_data = NULL; + } + } + } break; + } + } +} + +static void btc_gap_bt_set_cod(btc_gap_bt_args_t *arg) +{ + tBTA_UTL_COD p_cod; + esp_bt_cod_t *cod = &(arg->set_cod.cod); + p_cod.minor = cod->minor << 2; + p_cod.major = cod->major; + p_cod.service = cod->service << 5; + bool ret = utl_set_device_class(&p_cod, arg->set_cod.mode); + if (!ret){ + BTC_TRACE_ERROR("%s set class of device failed!",__func__); + } +} + +esp_err_t btc_gap_bt_get_cod(esp_bt_cod_t *cod) +{ + tBTA_UTL_COD p_cod; + bool ret = utl_get_device_class(&p_cod); + if (!ret){ + BTC_TRACE_ERROR("%s get class of device failed!",__func__); + return ESP_BT_STATUS_FAIL; + } + cod->minor = p_cod.minor >> 2; + cod->major = p_cod.major; + cod->service = p_cod.service >> 5; + return ESP_BT_STATUS_SUCCESS; +} + +static void btc_gap_bt_read_rssi_delta_cmpl_callback(void *p_data) +{ + tBTA_RSSI_RESULTS *result = (tBTA_RSSI_RESULTS *)p_data; + esp_bt_gap_cb_param_t param; + bt_status_t ret; + btc_msg_t msg; + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_GAP_BT; + msg.act = BTC_GAP_BT_READ_RSSI_DELTA_EVT; + memcpy(param.read_rssi_delta.bda, result->rem_bda, sizeof(BD_ADDR)); + param.read_rssi_delta.stat = btc_btm_status_to_esp_status(result->status); + param.read_rssi_delta.rssi_delta = result->rssi; + + ret = btc_transfer_context(&msg, ¶m, + sizeof(esp_bt_gap_cb_param_t), NULL, NULL); + + if (ret != BT_STATUS_SUCCESS) { + BTC_TRACE_ERROR("%s btc_transfer_context failed\n", __func__); + } +} + +static void btc_gap_bt_read_rssi_delta(btc_gap_bt_args_t *arg) +{ + BTA_DmReadRSSI(arg->read_rssi_delta.bda.address, BTA_TRANSPORT_BR_EDR, btc_gap_bt_read_rssi_delta_cmpl_callback); +} + +static esp_err_t btc_gap_bt_remove_bond_device(btc_gap_bt_args_t *arg) +{ + BD_ADDR bd_addr; + memcpy(bd_addr, arg->rm_bond_device.bda.address, sizeof(BD_ADDR)); + if(BTA_DmRemoveDevice(bd_addr, BT_TRANSPORT_BR_EDR) == BTA_SUCCESS){ + return ESP_BT_STATUS_SUCCESS; + } + return ESP_BT_STATUS_FAIL; +} + +static void btc_gap_bt_set_pin_type(btc_gap_bt_args_t *arg){ + BTA_DMSetPinType (arg->set_pin_type.pin_type, arg->set_pin_type.pin_code, arg->set_pin_type.pin_code_len); +} + +static void btc_gap_bt_pin_reply(btc_gap_bt_args_t *arg){ + BTA_DmPinReply(arg->pin_reply.bda.address, arg->pin_reply.accept, arg->pin_reply.pin_code_len, arg->pin_reply.pin_code); +} + +static esp_err_t btc_gap_bt_set_security_param(btc_gap_bt_args_t *arg) +{ + esp_err_t ret; + switch(arg->set_security_param.param_type) { + case ESP_BT_SP_IOCAP_MODE:{ + uint8_t iocap = 0; + uint8_t *p = arg->set_security_param.value; + STREAM_TO_UINT8(iocap, p); + ret = bta_dm_co_bt_set_io_cap(iocap); + break; + } + default: + ret = ESP_BT_STATUS_FAIL; + break; + } + return ret; +} + +static void btc_gap_bt_ssp_passkey_reply(btc_gap_bt_args_t *arg) +{ + BTA_DmPasskeyReqReply(arg->passkey_reply.accept, arg->passkey_reply.bda.address, arg->passkey_reply.passkey); +} + +static void btc_gap_bt_ssp_confirm(btc_gap_bt_args_t *arg) +{ + BTA_DmConfirm(arg->confirm_reply.bda.address, arg->confirm_reply.accept); +} + +static void btc_gap_bt_config_eir(btc_gap_bt_args_t *arg) +{ + tBTA_DM_EIR_CONF eir_config; + esp_bt_eir_data_t *eir_data = &arg->config_eir.eir_data; + + eir_config.bta_dm_eir_fec_required = eir_data->fec_required; + eir_config.bta_dm_eir_included_name = eir_data->include_name; + eir_config.bta_dm_eir_included_tx_power = eir_data->include_txpower; + eir_config.bta_dm_eir_included_uuid = eir_data->include_uuid; + eir_config.bta_dm_eir_flags = eir_data->flag; + eir_config.bta_dm_eir_manufac_spec_len = eir_data->manufacturer_len; + eir_config.bta_dm_eir_manufac_spec = eir_data->p_manufacturer_data; + eir_config.bta_dm_eir_url_len = eir_data->url_len; + eir_config.bta_dm_eir_url = eir_data->p_url; + + BTA_DmConfigEir(&eir_config); +} + +static void btc_gap_bt_set_afh_channels_cmpl_callback(void *p_data) +{ + tBTA_SET_AFH_CHANNELS_RESULTS *result = (tBTA_SET_AFH_CHANNELS_RESULTS *)p_data; + esp_bt_gap_cb_param_t param; + bt_status_t ret; + btc_msg_t msg; + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_GAP_BT; + msg.act = BTC_GAP_BT_SET_AFH_CHANNELS_EVT; + + param.set_afh_channels.stat = btc_btm_status_to_esp_status(result->status); + + ret = btc_transfer_context(&msg, ¶m, + sizeof(esp_bt_gap_cb_param_t), NULL, NULL); + + if (ret != BT_STATUS_SUCCESS) { + BTC_TRACE_ERROR("%s btc_transfer_context failed\n", __func__); + } +} + +static void btc_gap_bt_set_afh_channels(btc_gap_bt_args_t *arg) +{ + BTA_DmSetAfhChannels(arg->set_afh_channels.channels, btc_gap_bt_set_afh_channels_cmpl_callback); +} + +static void btc_gap_bt_set_page_timeout_cmpl_callback(void *p_data) +{ + tBTA_SET_PAGE_TIMEOUT_RESULTS *result = (tBTA_SET_PAGE_TIMEOUT_RESULTS *)p_data; + esp_bt_gap_cb_param_t param; + bt_status_t ret; + btc_msg_t msg; + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_GAP_BT; + msg.act = BTC_GAP_BT_SET_PAGE_TO_EVT; + + param.set_page_timeout.stat = btc_btm_status_to_esp_status(result->status); + + ret = btc_transfer_context(&msg, ¶m, sizeof(esp_bt_gap_cb_param_t), NULL, NULL); + if (ret != BT_STATUS_SUCCESS) { + BTC_TRACE_ERROR("%s btc_transfer_context failed\n", __func__); + } +} + +static void btc_gap_set_page_timeout(btc_gap_bt_args_t *arg) +{ + BTA_DmSetPageTimeout(arg->set_page_to.page_to, btc_gap_bt_set_page_timeout_cmpl_callback); +} + +static void btc_gap_bt_get_page_timeout_cmpl_callback(void *p_data) +{ + tBTA_GET_PAGE_TIMEOUT_RESULTS *result = (tBTA_GET_PAGE_TIMEOUT_RESULTS *)p_data; + esp_bt_gap_cb_param_t param; + bt_status_t ret; + btc_msg_t msg; + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_GAP_BT; + msg.act = BTC_GAP_BT_GET_PAGE_TO_EVT; + + param.get_page_timeout.stat = btc_btm_status_to_esp_status(result->status); + param.get_page_timeout.page_to = result->page_to; + + ret = btc_transfer_context(&msg, ¶m, sizeof(esp_bt_gap_cb_param_t), NULL, NULL); + if (ret != BT_STATUS_SUCCESS) { + BTC_TRACE_ERROR("%s btc_transfer_context failed\n", __func__); + } +} + +static void btc_gap_get_page_timeout(void) +{ + BTA_DmGetPageTimeout(btc_gap_bt_get_page_timeout_cmpl_callback); +} + +static void btc_gap_bt_set_acl_pkt_types_cmpl_callback(void *p_data) +{ + tBTA_SET_ACL_PKT_TYPES_RESULTS *result = (tBTA_SET_ACL_PKT_TYPES_RESULTS *)p_data; + esp_bt_gap_cb_param_t param; + bt_status_t ret; + btc_msg_t msg; + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_GAP_BT; + msg.act = BTC_GAP_BT_SET_ACL_PKT_TYPES_EVT; + + param.set_acl_pkt_types.status = btc_btm_status_to_esp_status(result->status); + memcpy(param.set_acl_pkt_types.bda, result->rem_bda, sizeof(esp_bd_addr_t)); + param.set_acl_pkt_types.pkt_types = result->pkt_types; + + ret = btc_transfer_context(&msg, ¶m, sizeof(esp_bt_gap_cb_param_t), NULL, NULL); + if (ret != BT_STATUS_SUCCESS) { + BTC_TRACE_ERROR("%s btc_transfer_context failed\n", __func__); + } +} + +static void btc_gap_set_acl_pkt_types(btc_gap_bt_args_t *arg) +{ + BTA_DmSetAclPktTypes(arg->set_acl_pkt_types.bda.address, + arg->set_acl_pkt_types.pkt_types, + btc_gap_bt_set_acl_pkt_types_cmpl_callback); +} + +static void btc_gap_bt_read_remote_name_cmpl_callback(void *p_data) +{ + tBTA_REMOTE_DEV_NAME *result = (tBTA_REMOTE_DEV_NAME *)p_data; + esp_bt_gap_cb_param_t param; + btc_msg_t msg; + bt_status_t ret; + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_GAP_BT; + msg.act = BTC_GAP_BT_READ_REMOTE_NAME_EVT; + + memcpy(param.read_rmt_name.bda,result->bd_addr,BD_ADDR_LEN); + param.read_rmt_name.stat = btc_btm_status_to_esp_status(result->status); + memcpy(param.read_rmt_name.rmt_name,result->remote_bd_name,ESP_BT_GAP_MAX_BDNAME_LEN); + + ret = btc_transfer_context(&msg, ¶m, sizeof(esp_bt_gap_cb_param_t), NULL, NULL); + if (ret != BT_STATUS_SUCCESS) { + BTC_TRACE_ERROR("%s btc_transfer_context failed\n", __func__); + } +} + +static void btc_gap_bt_read_remote_name(btc_gap_bt_args_t *arg) +{ + BTA_DmGetRemoteName(arg->rmt_name_bda.address, btc_gap_bt_read_remote_name_cmpl_callback); +} + +#if (BTA_DM_QOS_INCLUDED == TRUE) +static void btc_gap_bt_set_qos_cmpl_callback(void *p_data) +{ + tBTM_QOS_SETUP_CMPL *result = (tBTM_QOS_SETUP_CMPL *)p_data; + esp_bt_gap_cb_param_t param; + btc_msg_t msg; + bt_status_t ret; + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_GAP_BT; + msg.act = BTC_GAP_BT_QOS_EVT; + + param.qos_cmpl.stat = btc_btm_status_to_esp_status(result->status); + param.qos_cmpl.t_poll = result->flow.latency / 625; + memcpy(param.qos_cmpl.bda,result->rem_bda,BD_ADDR_LEN); + + ret = btc_transfer_context(&msg, ¶m, sizeof(esp_bt_gap_cb_param_t), NULL, NULL); + if (ret != BT_STATUS_SUCCESS) { + BTC_TRACE_ERROR("%s btc_transfer_context failed\n", __func__); + } +} +#endif /// (BTA_DM_QOS_INCLUDED == TRUE) + +static void btc_gap_bt_set_qos(btc_gap_bt_args_t *arg) +{ +#if (BTA_DM_QOS_INCLUDED == TRUE) + BTA_DmSetQos(arg->set_qos.bda.address, arg->set_qos.t_poll, btc_gap_bt_set_qos_cmpl_callback); +#else + BTC_TRACE_ERROR("%s: QoS is not supported.\n",__func__); +#endif /// (BTA_DM_QOS_INCLUDED == TRUE) +} + +void btc_gap_bt_arg_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src) +{ + switch (msg->act) { + case BTC_GAP_BT_ACT_SET_SCAN_MODE: + case BTC_GAP_BT_ACT_START_DISCOVERY: + case BTC_GAP_BT_ACT_CANCEL_DISCOVERY: + case BTC_GAP_BT_ACT_GET_REMOTE_SERVICES: + case BTC_GAP_BT_ACT_GET_REMOTE_SERVICE_RECORD: + case BTC_GAP_BT_ACT_SET_COD: + case BTC_GAP_BT_ACT_READ_RSSI_DELTA: + case BTC_GAP_BT_ACT_REMOVE_BOND_DEVICE: + case BTC_GAP_BT_ACT_PIN_REPLY: + case BTC_GAP_BT_ACT_SET_PIN_TYPE: + case BTC_GAP_BT_ACT_SET_AFH_CHANNELS: + case BTC_GAP_BT_ACT_READ_REMOTE_NAME: + case BTC_GAP_BT_ACT_SET_QOS: + case BTC_GAP_BT_ACT_SET_PAGE_TIMEOUT: + case BTC_GAP_BT_ACT_GET_PAGE_TIMEOUT: + case BTC_GAP_BT_ACT_SET_ACL_PKT_TYPES: + break; + case BTC_GAP_BT_ACT_PASSKEY_REPLY: + case BTC_GAP_BT_ACT_CONFIRM_REPLY: + break; + case BTC_GAP_BT_ACT_SET_SECURITY_PARAM:{ + btc_gap_bt_args_t *src = (btc_gap_bt_args_t *)p_src; + btc_gap_bt_args_t *dst = (btc_gap_bt_args_t *)p_dest; + if (src->set_security_param.value) { + dst->set_security_param.value = osi_malloc(src->set_security_param.len); + if (dst->set_security_param.value != NULL) { + memcpy(dst->set_security_param.value, src->set_security_param.value, src->set_security_param.len); + } else { + BTC_TRACE_ERROR("%s %d no mem\n",__func__, msg->act); + } + } + break; + } + + case BTC_GAP_BT_ACT_CONFIG_EIR:{ + btc_gap_bt_args_t *src = (btc_gap_bt_args_t *)p_src; + btc_gap_bt_args_t *dst = (btc_gap_bt_args_t *)p_dest; + if (src->config_eir.eir_data.p_manufacturer_data) { + dst->config_eir.eir_data.p_manufacturer_data = osi_malloc(src->config_eir.eir_data.manufacturer_len); + if (dst->config_eir.eir_data.p_manufacturer_data != NULL) { + memcpy(dst->config_eir.eir_data.p_manufacturer_data, src->config_eir.eir_data.p_manufacturer_data, src->config_eir.eir_data.manufacturer_len); + } else { + dst->config_eir.eir_data.manufacturer_len = 0; + BTC_TRACE_ERROR("%s %d no mem\n",__func__, msg->act); + } + } + if (src->config_eir.eir_data.p_url) { + dst->config_eir.eir_data.p_url = osi_malloc(src->config_eir.eir_data.url_len); + if (dst->config_eir.eir_data.p_url != NULL) { + memcpy(dst->config_eir.eir_data.p_url, src->config_eir.eir_data.p_url, src->config_eir.eir_data.url_len); + } else { + dst->config_eir.eir_data.url_len = 0; + BTC_TRACE_ERROR("%s %d no mem\n",__func__, msg->act); + } + } + break; + } + default: + BTC_TRACE_ERROR("Unhandled deep copy %d\n", msg->act); + break; + } +} + +void btc_gap_bt_arg_deep_free(btc_msg_t *msg) +{ + btc_gap_bt_args_t *arg = (btc_gap_bt_args_t *)msg->arg; + switch (msg->act) { + case BTC_GAP_BT_ACT_SET_SCAN_MODE: + case BTC_GAP_BT_ACT_START_DISCOVERY: + case BTC_GAP_BT_ACT_CANCEL_DISCOVERY: + case BTC_GAP_BT_ACT_GET_REMOTE_SERVICES: + case BTC_GAP_BT_ACT_GET_REMOTE_SERVICE_RECORD: + case BTC_GAP_BT_ACT_SET_COD: + case BTC_GAP_BT_ACT_READ_RSSI_DELTA: + case BTC_GAP_BT_ACT_REMOVE_BOND_DEVICE: + case BTC_GAP_BT_ACT_PIN_REPLY: + case BTC_GAP_BT_ACT_SET_PIN_TYPE: + case BTC_GAP_BT_ACT_SET_AFH_CHANNELS: + case BTC_GAP_BT_ACT_READ_REMOTE_NAME: + case BTC_GAP_BT_ACT_SET_QOS: + case BTC_GAP_BT_ACT_SET_PAGE_TIMEOUT: + case BTC_GAP_BT_ACT_GET_PAGE_TIMEOUT: + case BTC_GAP_BT_ACT_SET_ACL_PKT_TYPES: + break; + case BTC_GAP_BT_ACT_PASSKEY_REPLY: + case BTC_GAP_BT_ACT_CONFIRM_REPLY: + break; + case BTC_GAP_BT_ACT_SET_SECURITY_PARAM: + if (arg->set_security_param.value) { + osi_free(arg->set_security_param.value); + } + break; + + case BTC_GAP_BT_ACT_CONFIG_EIR: + if (arg->config_eir.eir_data.p_manufacturer_data) { + osi_free(arg->config_eir.eir_data.p_manufacturer_data); + } + if (arg->config_eir.eir_data.p_url) { + osi_free(arg->config_eir.eir_data.p_url); + } + break; + default: + BTC_TRACE_ERROR("Unhandled deep copy %d, arg: %p\n", msg->act, arg); + break; + } +} + +void btc_gap_bt_call_handler(btc_msg_t *msg) +{ + btc_gap_bt_args_t *arg = (btc_gap_bt_args_t *)msg->arg; + BTC_TRACE_DEBUG("%s act %d\n", __func__, msg->act); + switch (msg->act) { + case BTC_GAP_BT_ACT_SET_SCAN_MODE: { + btc_bt_set_scan_mode(arg->set_scan_mode.c_mode, arg->set_scan_mode.d_mode); + break; + } + case BTC_GAP_BT_ACT_START_DISCOVERY: { + btc_gap_bt_start_discovery(arg); + break; + } + case BTC_GAP_BT_ACT_CANCEL_DISCOVERY: { + btc_gap_bt_cancel_discovery(); + break; + } + case BTC_GAP_BT_ACT_GET_REMOTE_SERVICES: { + btc_gap_bt_get_remote_services((bt_bdaddr_t *)msg->arg); + break; + } + case BTC_GAP_BT_ACT_GET_REMOTE_SERVICE_RECORD: { + btc_gap_bt_get_remote_service_record(arg); + break; + } + case BTC_GAP_BT_ACT_SET_COD: { + btc_gap_bt_set_cod(arg); + break; + } + case BTC_GAP_BT_ACT_READ_RSSI_DELTA: { + btc_gap_bt_read_rssi_delta(arg); + break; + } + case BTC_GAP_BT_ACT_REMOVE_BOND_DEVICE:{ + btc_gap_bt_remove_bond_device(arg); + break; + } + case BTC_GAP_BT_ACT_SET_PIN_TYPE:{ + btc_gap_bt_set_pin_type(arg); + break; + } + case BTC_GAP_BT_ACT_PIN_REPLY: { + btc_gap_bt_pin_reply(arg); + break; + } + case BTC_GAP_BT_ACT_SET_SECURITY_PARAM:{ + btc_gap_bt_set_security_param(arg); + break; + } + case BTC_GAP_BT_ACT_PASSKEY_REPLY:{ + btc_gap_bt_ssp_passkey_reply(arg); + break; + } + case BTC_GAP_BT_ACT_CONFIRM_REPLY:{ + btc_gap_bt_ssp_confirm(arg); + break; + } + case BTC_GAP_BT_ACT_CONFIG_EIR: { + btc_gap_bt_config_eir(arg); + break; + } + + case BTC_GAP_BT_ACT_SET_AFH_CHANNELS: { + btc_gap_bt_set_afh_channels(arg); + break; + } + case BTC_GAP_BT_ACT_READ_REMOTE_NAME: { + btc_gap_bt_read_remote_name(arg); + break; + } + case BTC_GAP_BT_ACT_SET_QOS: { + btc_gap_bt_set_qos(arg); + break; + } + case BTC_GAP_BT_ACT_SET_PAGE_TIMEOUT: { + btc_gap_set_page_timeout(arg); + break; + } + case BTC_GAP_BT_ACT_GET_PAGE_TIMEOUT: { + btc_gap_get_page_timeout(); + break; + } + case BTC_GAP_BT_ACT_SET_ACL_PKT_TYPES: { + btc_gap_set_acl_pkt_types(arg); + break; + } + default: + break; + } + btc_gap_bt_arg_deep_free(msg); + return; +} + +void btc_gap_bt_busy_level_updated(uint8_t bl_flags) +{ + esp_bt_gap_cb_param_t param; + + if (bl_flags == BTM_BL_INQUIRY_STARTED) { + param.disc_st_chg.state = ESP_BT_GAP_DISCOVERY_STARTED; + btc_gap_bt_cb_to_app(ESP_BT_GAP_DISC_STATE_CHANGED_EVT, ¶m); + btc_gap_bt_inquiry_in_progress = true; + } else if (bl_flags == BTM_BL_INQUIRY_CANCELLED) { + param.disc_st_chg.state = ESP_BT_GAP_DISCOVERY_STOPPED; + btc_gap_bt_cb_to_app(ESP_BT_GAP_DISC_STATE_CHANGED_EVT, ¶m); + btc_gap_bt_inquiry_in_progress = false; + } else if (bl_flags == BTM_BL_INQUIRY_COMPLETE) { + /* The Inquiry Complete event is not transported to app layer, + since the app only cares about the Name Discovery Complete event */ + btc_gap_bt_inquiry_in_progress = false; + } +} + +void btc_gap_bt_cb_deep_free(btc_msg_t *msg) +{ + switch (msg->act) { + case BTC_GAP_BT_SEARCH_DEVICES_EVT: + case BTC_GAP_BT_SEARCH_SERVICES_EVT: + case BTC_GAP_BT_SEARCH_SERVICE_RECORD_EVT: + osi_free(((tBTA_DM_SEARCH_PARAM *) (msg->arg)) ->p_data); + break; + case BTC_GAP_BT_READ_RSSI_DELTA_EVT: + case BTC_GAP_BT_CONFIG_EIR_DATA_EVT: + case BTC_GAP_BT_AUTH_CMPL_EVT: + case BTC_GAP_BT_ENC_CHG_EVT: + case BTC_GAP_BT_PIN_REQ_EVT: + case BTC_GAP_BT_SET_AFH_CHANNELS_EVT: + case BTC_GAP_BT_READ_REMOTE_NAME_EVT: + case BTC_GAP_BT_REMOVE_BOND_DEV_COMPLETE_EVT: + case BTC_GAP_BT_QOS_EVT: + case BTC_GAP_BT_SET_PAGE_TO_EVT: + case BTC_GAP_BT_GET_PAGE_TO_EVT: + case BTC_GAP_BT_SET_ACL_PKT_TYPES_EVT: + case BTC_GAP_BT_CFM_REQ_EVT: + case BTC_GAP_BT_KEY_NOTIF_EVT: + case BTC_GAP_BT_KEY_REQ_EVT: +#if (BTC_DM_PM_INCLUDED == TRUE) + case BTC_GAP_BT_MODE_CHG_EVT: +#endif /// BTC_DM_PM_INCLUDED == TRUE + break; + default: + BTC_TRACE_ERROR("%s: Unhandled event (%d)!\n", __FUNCTION__, msg->act); + break; + } +} + +void btc_gap_bt_cb_handler(btc_msg_t *msg) +{ + switch (msg->act) { + case BTC_GAP_BT_SEARCH_DEVICES_EVT: { + btc_gap_bt_search_devices_evt((tBTA_DM_SEARCH_PARAM *)msg->arg); + break; + } + case BTC_GAP_BT_SEARCH_SERVICES_EVT: { + btc_gap_bt_search_services((char *)msg->arg); + break; + } + case BTC_GAP_BT_SEARCH_SERVICE_RECORD_EVT: { + btc_gap_bt_search_service_record((char *)msg->arg); + break; + } + case BTC_GAP_BT_READ_RSSI_DELTA_EVT:{ + btc_gap_bt_cb_to_app(ESP_BT_GAP_READ_RSSI_DELTA_EVT, (esp_bt_gap_cb_param_t *)msg->arg); + break; + } + case BTC_GAP_BT_CONFIG_EIR_DATA_EVT: { + btc_gap_bt_cb_to_app(ESP_BT_GAP_CONFIG_EIR_DATA_EVT, (esp_bt_gap_cb_param_t *)msg->arg); + break; + } + case BTC_GAP_BT_AUTH_CMPL_EVT:{ + btc_gap_bt_cb_to_app(ESP_BT_GAP_AUTH_CMPL_EVT, (esp_bt_gap_cb_param_t *)msg->arg); + break; + } + case BTC_GAP_BT_ENC_CHG_EVT:{ + btc_gap_bt_cb_to_app(ESP_BT_GAP_ENC_CHG_EVT, (esp_bt_gap_cb_param_t *)msg->arg); + break; + } + case BTC_GAP_BT_PIN_REQ_EVT:{ + btc_gap_bt_cb_to_app(ESP_BT_GAP_PIN_REQ_EVT, (esp_bt_gap_cb_param_t *)msg->arg); + break; + } + case BTC_GAP_BT_CFM_REQ_EVT:{ + btc_gap_bt_cb_to_app(ESP_BT_GAP_CFM_REQ_EVT, (esp_bt_gap_cb_param_t *)msg->arg); + break; + } + case BTC_GAP_BT_KEY_NOTIF_EVT:{ + btc_gap_bt_cb_to_app(ESP_BT_GAP_KEY_NOTIF_EVT, (esp_bt_gap_cb_param_t *)msg->arg); + break; + } + case BTC_GAP_BT_KEY_REQ_EVT:{ + btc_gap_bt_cb_to_app(ESP_BT_GAP_KEY_REQ_EVT, (esp_bt_gap_cb_param_t *)msg->arg); + break; + } + case BTC_GAP_BT_SET_AFH_CHANNELS_EVT:{ + btc_gap_bt_cb_to_app(ESP_BT_GAP_SET_AFH_CHANNELS_EVT, (esp_bt_gap_cb_param_t *)msg->arg); + break; + } +#if (SDP_INCLUDED == TRUE) + case BTC_GAP_BT_READ_REMOTE_NAME_EVT:{ + btc_gap_bt_cb_to_app(ESP_BT_GAP_READ_REMOTE_NAME_EVT,(esp_bt_gap_cb_param_t *)msg->arg); + break; + } +#endif + +#if (BTC_DM_PM_INCLUDED == TRUE) + case BTC_GAP_BT_MODE_CHG_EVT: + btc_gap_bt_cb_to_app(ESP_BT_GAP_MODE_CHG_EVT,(esp_bt_gap_cb_param_t *)msg->arg); + break; +#endif /// BTC_DM_PM_INCLUDED == TRUE + case BTC_GAP_BT_REMOVE_BOND_DEV_COMPLETE_EVT:{ + btc_gap_bt_cb_to_app(ESP_BT_GAP_REMOVE_BOND_DEV_COMPLETE_EVT,(esp_bt_gap_cb_param_t *)msg->arg); + break; + } + + case BTC_GAP_BT_QOS_EVT:{ + btc_gap_bt_cb_to_app(ESP_BT_GAP_QOS_CMPL_EVT, (esp_bt_gap_cb_param_t *)msg->arg); + break; + } + case BTC_GAP_BT_SET_PAGE_TO_EVT: { + btc_gap_bt_cb_to_app(ESP_BT_GAP_SET_PAGE_TO_EVT, (esp_bt_gap_cb_param_t *)msg->arg); + break; + } + case BTC_GAP_BT_GET_PAGE_TO_EVT: { + btc_gap_bt_cb_to_app(ESP_BT_GAP_GET_PAGE_TO_EVT, (esp_bt_gap_cb_param_t *)msg->arg); + break; + } + case BTC_GAP_BT_SET_ACL_PKT_TYPES_EVT: { + btc_gap_bt_cb_to_app(ESP_BT_GAP_ACL_PKT_TYPE_CHANGED_EVT, (esp_bt_gap_cb_param_t *)msg->arg); + break; + } + default: + BTC_TRACE_ERROR("%s: Unhandled event (%d)!\n", __FUNCTION__, msg->act); + break; + } + btc_gap_bt_cb_deep_free(msg); +} +#endif /* (BTC_GAP_BT_INCLUDED == TRUE) */ diff --git a/lib/bt/host/bluedroid/btc/profile/std/gatt/btc_gatt_common.c b/lib/bt/host/bluedroid/btc/profile/std/gatt/btc_gatt_common.c new file mode 100644 index 00000000..45a2b834 --- /dev/null +++ b/lib/bt/host/bluedroid/btc/profile/std/gatt/btc_gatt_common.c @@ -0,0 +1,38 @@ +/* + * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "btc/btc_task.h" +#include "btc/btc_main.h" +#include "btc/btc_dm.h" +#include "osi/future.h" +#include "esp_err.h" +#include "btc/btc_config.h" +#include "osi/alarm.h" +#include "btc/btc_ble_storage.h" +#include "btc_gatt_common.h" +#include "bta/bta_gatt_common.h" + + +static void btc_set_local_mtu(uint16_t mtu) +{ + BTA_GATT_SetLocalMTU(mtu); +} + +void btc_gatt_com_call_handler(btc_msg_t *msg) +{ + BTC_TRACE_DEBUG("%s act %d\n", __func__, msg->act); + switch (msg->act) { + case BTC_GATT_ACT_SET_LOCAL_MTU: + { + btc_ble_gatt_com_args_t *arg = (btc_ble_gatt_com_args_t *)(msg->arg); + btc_set_local_mtu(arg->set_mtu.mtu); + break; + } + default: + BTC_TRACE_ERROR("%s UNKNOWN ACT %d\n", __func__, msg->act); + break; + } +} diff --git a/lib/bt/host/bluedroid/btc/profile/std/gatt/btc_gatt_util.c b/lib/bt/host/bluedroid/btc/profile/std/gatt/btc_gatt_util.c new file mode 100644 index 00000000..bca8ea66 --- /dev/null +++ b/lib/bt/host/bluedroid/btc/profile/std/gatt/btc_gatt_util.c @@ -0,0 +1,193 @@ +/* + * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include "btc_gatt_util.h" + +#define GATTC_READ_VALUE_TYPE_VALUE 0x0000 /* Attribute value itself */ +#define GATTC_READ_VALUE_TYPE_AGG_FORMAT 0x2905 /* Characteristic Aggregate Format*/ + +static const unsigned char BASE_UUID[16] = { + 0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80, + 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +/******************************************************************************* + * BTIF -> BTA conversion functions + *******************************************************************************/ +int uuidType(unsigned char *p_uuid) +{ + int i = 0; + int match = 0; + int all_zero = 1; + + for (i = 0; i != 16; ++i) { + if (i == 12 || i == 13) { + continue; + } + + if (p_uuid[i] == BASE_UUID[i]) { + ++match; + } + + if (p_uuid[i] != 0) { + all_zero = 0; + } + } + if (all_zero) { + return 0; + } + if (match == 12) { + return LEN_UUID_32; + } + if (match == 14) { + return LEN_UUID_16; + } + return LEN_UUID_128; +} + +void btc128_to_bta_uuid(tBT_UUID *p_dest, uint8_t *p_src) +{ + int i = 0; + + p_dest->len = uuidType(p_src); + + switch (p_dest->len) { + case LEN_UUID_16: + p_dest->uu.uuid16 = (p_src[13] << 8) + p_src[12]; + break; + + case LEN_UUID_32: + p_dest->uu.uuid32 = (p_src[13] << 8) + p_src[12]; + p_dest->uu.uuid32 += (p_src[15] << 24) + (p_src[14] << 16); + break; + + case LEN_UUID_128: + for (i = 0; i != 16; ++i) { + p_dest->uu.uuid128[i] = p_src[i]; + } + break; + + default: + BTC_TRACE_ERROR("%s: Unknown UUID length %d!", __FUNCTION__, p_dest->len); + break; + } +} + +/******************************************************************************* + * BTC -> BTA conversion functions + *******************************************************************************/ + +void btc_to_bta_uuid(tBT_UUID *p_dest, esp_bt_uuid_t *p_src) +{ + p_dest->len = p_src->len; + if (p_src->len == LEN_UUID_16) { + p_dest->uu.uuid16 = p_src->uuid.uuid16; + } else if (p_src->len == LEN_UUID_32) { + p_dest->uu.uuid32 = p_src->uuid.uuid32; + } else if (p_src->len == LEN_UUID_128) { + memcpy(&p_dest->uu.uuid128, p_src->uuid.uuid128, p_dest->len); + } else if (p_src->len == 0) { + /* do nothing for now, there's some scenario will input 0 */ + } else { + BTC_TRACE_ERROR("%s UUID len is invalid %d\n", __func__, p_src->len); + } +} + +void btc_to_bta_gatt_id(tBTA_GATT_ID *p_dest, esp_gatt_id_t *p_src) +{ + p_dest->inst_id = p_src->inst_id; + btc_to_bta_uuid(&p_dest->uuid, &p_src->uuid); +} + +void btc_to_bta_srvc_id(tBTA_GATT_SRVC_ID *p_dest, esp_gatt_srvc_id_t *p_src) +{ + p_dest->is_primary = p_src->is_primary; + btc_to_bta_gatt_id(&p_dest->id, &p_src->id); +} + + +/******************************************************************************* + * BTA -> BTC conversion functions + *******************************************************************************/ +void bta_to_btc_uuid(esp_bt_uuid_t *p_dest, tBT_UUID *p_src) +{ + p_dest->len = p_src->len; + if (p_src->len == LEN_UUID_16) { + p_dest->uuid.uuid16 = p_src->uu.uuid16; + } else if (p_src->len == LEN_UUID_32) { + p_dest->uuid.uuid32 = p_src->uu.uuid32; + } else if (p_src->len == LEN_UUID_128) { + memcpy(&p_dest->uuid.uuid128, p_src->uu.uuid128, p_dest->len); + } else if (p_src->len == 0) { + /* do nothing for now, there's some scenario will input 0 + such as, receive notify, the descriptor may be 0 */ + } else { + BTC_TRACE_ERROR("%s UUID len is invalid %d\n", __func__, p_src->len); + } +} + +void bta_to_btc_gatt_id(esp_gatt_id_t *p_dest, tBTA_GATT_ID *p_src) +{ + p_dest->inst_id = p_src->inst_id; + bta_to_btc_uuid(&p_dest->uuid, &p_src->uuid); +} + +void bta_to_btc_srvc_id(esp_gatt_srvc_id_t *p_dest, tBTA_GATT_SRVC_ID *p_src) +{ + p_dest->is_primary = p_src->is_primary; + bta_to_btc_gatt_id(&p_dest->id, &p_src->id); +} + +void btc_to_bta_response(tBTA_GATTS_RSP *p_dest, esp_gatt_rsp_t *p_src) +{ + p_dest->attr_value.auth_req = p_src->attr_value.auth_req; + p_dest->attr_value.handle = p_src->attr_value.handle; + p_dest->attr_value.len = p_src->attr_value.len; + p_dest->attr_value.offset = p_src->attr_value.offset; + memcpy(p_dest->attr_value.value, p_src->attr_value.value, ESP_GATT_MAX_ATTR_LEN); +} + +uint16_t get_uuid16(tBT_UUID *p_uuid) +{ + if (p_uuid->len == LEN_UUID_16) { + return p_uuid->uu.uuid16; + } else if (p_uuid->len == LEN_UUID_128) { + UINT16 u16; + UINT8 *p = &p_uuid->uu.uuid128[LEN_UUID_128 - 4]; + STREAM_TO_UINT16(u16, p); + return u16; + } else { /* p_uuid->len == LEN_UUID_32 */ + return (UINT16) p_uuid->uu.uuid32; + } +} + +uint16_t set_read_value(uint8_t *gattc_if, esp_ble_gattc_cb_param_t *p_dest, tBTA_GATTC_READ *p_src) +{ + uint16_t len = 0; + + p_dest->read.status = p_src->status; + p_dest->read.conn_id = BTC_GATT_GET_CONN_ID(p_src->conn_id); + *gattc_if = BTC_GATT_GET_GATT_IF(p_src->conn_id); + p_dest->read.status = p_src->status; + p_dest->read.handle = p_src->handle; + + if (( p_src->status == BTA_GATT_OK ) && (p_src->p_value != NULL)) + { + BTC_TRACE_DEBUG("%s len = %d ", __func__, p_src->p_value->len); + p_dest->read.value_len = p_src->p_value->len; + if ( p_src->p_value->len > 0 && p_src->p_value->p_value != NULL ) { + p_dest->read.value = p_src->p_value->p_value; + } + len += p_src->p_value->len; + } else { + p_dest->read.value_len = 0; + } + + return len; +} diff --git a/lib/bt/host/bluedroid/btc/profile/std/gatt/btc_gattc.c b/lib/bt/host/bluedroid/btc/profile/std/gatt/btc_gattc.c new file mode 100644 index 00000000..e69eed48 --- /dev/null +++ b/lib/bt/host/bluedroid/btc/profile/std/gatt/btc_gattc.c @@ -0,0 +1,1060 @@ +/* + * SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD + + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include "stack/btm_ble_api.h" +#include "btc_gattc.h" +#include "btc_gatt_util.h" +#include "btc/btc_manage.h" +#include "bta/bta_gatt_api.h" +#include "common/bt_trace.h" +#include "osi/allocator.h" +#include "esp_gattc_api.h" +#include "btc/btc_storage.h" +#include "common/bt_defs.h" + +#if (GATTC_INCLUDED == TRUE) +static inline void btc_gattc_cb_to_app(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param) +{ + esp_gattc_cb_t btc_gattc_cb = (esp_gattc_cb_t )btc_profile_cb_get(BTC_PID_GATTC); + if (btc_gattc_cb) { + btc_gattc_cb(event, gattc_if, param); + } +} + +void btc_gattc_arg_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src) +{ + btc_ble_gattc_args_t *dst = (btc_ble_gattc_args_t *) p_dest; + btc_ble_gattc_args_t *src = (btc_ble_gattc_args_t *)p_src; + + switch (msg->act) { + case BTC_GATTC_ACT_WRITE_CHAR: { + dst->write_char.value = (uint8_t *)osi_malloc(src->write_char.value_len); + if (dst->write_char.value) { + memcpy(dst->write_char.value, src->write_char.value, src->write_char.value_len); + } else { + BTC_TRACE_ERROR("%s %d no mem\n", __func__, msg->act); + } + break; + } + case BTC_GATTC_ACT_WRITE_CHAR_DESCR: { + dst->write_descr.value = (uint8_t *)osi_malloc(src->write_descr.value_len); + if (dst->write_descr.value) { + memcpy(dst->write_descr.value, src->write_descr.value, src->write_descr.value_len); + } else { + BTC_TRACE_ERROR("%s %d no mem\n", __func__, msg->act); + } + break; + } + case BTC_GATTC_ACT_PREPARE_WRITE: { + dst->prep_write.value = (uint8_t *)osi_malloc(src->prep_write.value_len); + if (dst->prep_write.value) { + memcpy(dst->prep_write.value, src->prep_write.value, src->prep_write.value_len); + } else { + BTC_TRACE_ERROR("%s %d no mem\n", __func__, msg->act); + } + break; + } + case BTC_GATTC_ACT_PREPARE_WRITE_CHAR_DESCR: { + dst->prep_write_descr.value = (uint8_t *)osi_malloc(src->prep_write_descr.value_len); + if (dst->prep_write_descr.value) { + memcpy(dst->prep_write_descr.value, src->prep_write_descr.value, src->prep_write_descr.value_len); + } else { + BTC_TRACE_ERROR("%s %d no mem\n", __func__, msg->act); + } + break; + } + default: + BTC_TRACE_DEBUG("%s Unhandled deep copy %d\n", __func__, msg->act); + break; + } +} + +void btc_gattc_arg_deep_free(btc_msg_t *msg) +{ + btc_ble_gattc_args_t *arg = (btc_ble_gattc_args_t *)msg->arg; + + switch (msg->act) { + case BTC_GATTC_ACT_WRITE_CHAR: { + if (arg->write_char.value) { + osi_free(arg->write_char.value); + } + break; + } + case BTC_GATTC_ACT_WRITE_CHAR_DESCR: { + if (arg->write_descr.value) { + osi_free(arg->write_descr.value); + } + break; + } + case BTC_GATTC_ACT_PREPARE_WRITE: { + if (arg->prep_write.value) { + osi_free(arg->prep_write.value); + } + break; + } + case BTC_GATTC_ACT_PREPARE_WRITE_CHAR_DESCR: { + if (arg->prep_write_descr.value) { + osi_free(arg->prep_write_descr.value); + } + break; + } + default: + BTC_TRACE_DEBUG("%s Unhandled deep free %d\n", __func__, msg->act); + break; + } + +} + +static void btc_gattc_copy_req_data(btc_msg_t *msg, void *p_dest, void *p_src) +{ + tBTA_GATTC *p_dest_data = (tBTA_GATTC *) p_dest; + tBTA_GATTC *p_src_data = (tBTA_GATTC *) p_src; + + if (!p_src_data || !p_dest_data || !msg) { + return; + } + + // Allocate buffer for request data if necessary + switch (msg->act) { + case BTA_GATTC_READ_DESCR_EVT: + case BTA_GATTC_READ_CHAR_EVT: + case BTA_GATTC_READ_MULTIPLE_EVT: + case BTA_GATTC_READ_MULTI_VAR_EVT: { + if (p_src_data->read.p_value && p_src_data->read.p_value->p_value) { + p_dest_data->read.p_value = (tBTA_GATT_UNFMT *)osi_malloc(sizeof(tBTA_GATT_UNFMT) + p_src_data->read.p_value->len); + p_dest_data->read.p_value->p_value = (uint8_t *)(p_dest_data->read.p_value + 1); + if (p_dest_data->read.p_value && p_dest_data->read.p_value->p_value) { + p_dest_data->read.p_value->len = p_src_data->read.p_value->len; + memcpy(p_dest_data->read.p_value->p_value, p_src_data->read.p_value->p_value, p_src_data->read.p_value->len); + } else { + BTC_TRACE_ERROR("%s %d no mem\n", __func__, msg->act); + } + } + break; + } + case BTA_GATTC_GET_ADDR_LIST_EVT: { + if (p_src_data->get_addr_list.bda_list != NULL) { + uint8_t num_addr = p_src_data->get_addr_list.num_addr; + p_dest_data->get_addr_list.bda_list = (BD_ADDR *)osi_malloc(sizeof(BD_ADDR) * num_addr); + if (p_dest_data->get_addr_list.bda_list) { + memcpy(p_dest_data->get_addr_list.bda_list, p_src_data->get_addr_list.bda_list, sizeof(BD_ADDR) * num_addr); + } else { + BTC_TRACE_ERROR("%s %d no mem\n", __func__, msg->act); + } + } + break; + } + default: + break; + } +} + +static void btc_gattc_free_req_data(btc_msg_t *msg) +{ + tBTA_GATTC *arg = (tBTA_GATTC *)(msg->arg); + switch (msg->act) { + case BTA_GATTC_READ_DESCR_EVT: + case BTA_GATTC_READ_CHAR_EVT: + case BTA_GATTC_READ_MULTIPLE_EVT: + case BTA_GATTC_READ_MULTI_VAR_EVT: { + if (arg->read.p_value) { + osi_free(arg->read.p_value); + } + break; + } + case BTA_GATTC_GET_ADDR_LIST_EVT: { + if (arg->get_addr_list.bda_list) { + osi_free(arg->get_addr_list.bda_list); + } + break; + } + default: + break; + } + return; +} + +static void btc_gattc_cback(tBTA_GATTC_EVT event, tBTA_GATTC *p_data) +{ + bt_status_t ret; + btc_msg_t msg = {0}; + + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_GATTC; + msg.act = (uint8_t) event; + ret = btc_transfer_context(&msg, p_data, sizeof(tBTA_GATTC), + btc_gattc_copy_req_data, btc_gattc_free_req_data); + + if (ret) { + BTC_TRACE_ERROR("%s transfer failed\n", __func__); + } +} + +static void btc_gattc_app_register(btc_ble_gattc_args_t *arg) +{ + tBT_UUID app_uuid; + app_uuid.len = 2; + app_uuid.uu.uuid16 = arg->app_reg.app_id; + BTA_GATTC_AppRegister(&app_uuid, btc_gattc_cback); +} + +static void btc_gattc_app_unregister(btc_ble_gattc_args_t *arg) +{ + BTA_GATTC_AppDeregister(arg->app_unreg.gattc_if); +} + +static void btc_gattc_open(btc_ble_gattc_args_t *arg) +{ + tBTA_GATT_TRANSPORT transport = BTA_GATT_TRANSPORT_LE; + BTA_GATTC_Open(arg->open.gattc_if, arg->open.remote_bda, + arg->open.remote_addr_type, arg->open.is_direct, + transport, arg->open.is_aux); +} + +static void btc_gattc_close(btc_ble_gattc_args_t *arg) +{ + // TODO; Review this call of BTA_API, check the usage of BTA_GATTC_CancelOpen + BTA_GATTC_Close(arg->close.conn_id); +} + +static void btc_gattc_cfg_mtu(btc_ble_gattc_args_t *arg) +{ + BTA_GATTC_ConfigureMTU (arg->cfg_mtu.conn_id); +} + +static esp_gatt_status_t btc_gattc_check_valid_param(int num, uint16_t offset) +{ + if (num == 0) { + return ESP_GATT_NOT_FOUND; + } else if (offset >= num) { + return ESP_GATT_INVALID_OFFSET; + } + + return ESP_GATT_OK; +} + +static void btc_gattc_fill_gatt_db_conversion(uint16_t count, uint16_t num, esp_gatt_db_attr_type_t type, + uint16_t offset, void *result, btgatt_db_element_t *db) +{ + tBT_UUID bta_uuid = {0}; + uint16_t db_size = (count + offset > num) ? (num - offset) : count; + switch(type) { + case ESP_GATT_DB_PRIMARY_SERVICE: + case ESP_GATT_DB_SECONDARY_SERVICE: { + esp_gattc_service_elem_t *svc_result = (esp_gattc_service_elem_t *)result; + for (int i = 0; i < db_size; i++) { + svc_result->is_primary = (db[offset + i].type == BTGATT_DB_PRIMARY_SERVICE) ? true : false; + svc_result->start_handle = db[offset + i].start_handle; + svc_result->end_handle = db[offset + i].end_handle; + btc128_to_bta_uuid(&bta_uuid, db[offset + i].uuid.uu); + bta_to_btc_uuid(&svc_result->uuid, &bta_uuid); + svc_result++; + } + break; + } + case ESP_GATT_DB_CHARACTERISTIC: { + esp_gattc_char_elem_t *char_result = (esp_gattc_char_elem_t *)result; + for (int i = 0; i < db_size; i++) { + char_result->char_handle = db[offset + i].attribute_handle; + char_result->properties = db[offset + i].properties; + btc128_to_bta_uuid(&bta_uuid, db[offset + i].uuid.uu); + bta_to_btc_uuid(&char_result->uuid, &bta_uuid); + char_result++; + } + break; + } + case ESP_GATT_DB_DESCRIPTOR: { + esp_gattc_descr_elem_t *descr_result = (esp_gattc_descr_elem_t *)result; + for (int i = 0; i < db_size; i++) { + descr_result->handle = db[offset + i].attribute_handle; + btc128_to_bta_uuid(&bta_uuid, db[offset + i].uuid.uu); + bta_to_btc_uuid(&descr_result->uuid, &bta_uuid); + descr_result++; + } + break; + } + case ESP_GATT_DB_INCLUDED_SERVICE: { + esp_gattc_incl_svc_elem_t *incl_result = (esp_gattc_incl_svc_elem_t *)result; + for (int i = 0; i < db_size; i++) { + incl_result->handle = db[offset + i].attribute_handle; + incl_result->incl_srvc_s_handle = db[offset + i].start_handle; + incl_result->incl_srvc_e_handle = db[offset + i].end_handle; + btc128_to_bta_uuid(&bta_uuid, db[offset + i].uuid.uu); + bta_to_btc_uuid(&incl_result->uuid, &bta_uuid); + incl_result++; + } + break; + } + default: + BTC_TRACE_WARNING("%s(), Not support type(%d)", __func__, type); + break; + } +} + +static void btc_gattc_search_service(btc_ble_gattc_args_t *arg) +{ + tBT_UUID srvc_uuid; + + if (arg->search_srvc.filter_uuid_enable) { + btc_to_bta_uuid(&srvc_uuid, &arg->search_srvc.filter_uuid); + BTA_GATTC_ServiceSearchRequest(arg->search_srvc.conn_id, &srvc_uuid); + } else { + BTA_GATTC_ServiceSearchRequest(arg->search_srvc.conn_id, NULL); + } +} + +esp_gatt_status_t btc_ble_gattc_get_service(uint16_t conn_id, esp_bt_uuid_t *svc_uuid, + esp_gattc_service_elem_t *result, + uint16_t *count, uint16_t offset) +{ + esp_gatt_status_t status; + btgatt_db_element_t *db = NULL; + uint16_t svc_num = 0; + tBT_UUID *bta_uuid = NULL; + if (svc_uuid) { + bta_uuid = osi_malloc(sizeof(tBT_UUID)); + btc_to_bta_uuid(bta_uuid, svc_uuid); + } + + BTA_GATTC_GetServiceWithUUID(conn_id, bta_uuid, &db, &svc_num); + + if ((status = btc_gattc_check_valid_param((int)svc_num, offset)) != ESP_GATT_OK) { + if (db) { + osi_free(db); + } + if (bta_uuid) { + osi_free(bta_uuid); + } + *count = 0; + return status; + } else { + btc_gattc_fill_gatt_db_conversion(*count, svc_num, ESP_GATT_DB_PRIMARY_SERVICE, offset, (void *)result, db); + } + + *count = svc_num; + //don't forget to free the db buffer after used. + if (db) { + osi_free(db); + } + if (bta_uuid) { + osi_free(bta_uuid); + } + return ESP_GATT_OK; +} + +esp_gatt_status_t btc_ble_gattc_get_all_char(uint16_t conn_id, + uint16_t start_handle, + uint16_t end_handle, + esp_gattc_char_elem_t *result, + uint16_t *count, uint16_t offset) +{ + esp_gatt_status_t status; + btgatt_db_element_t *db = NULL; + uint16_t char_num = 0; + BTA_GATTC_GetAllChar(conn_id, start_handle, end_handle, &db, &char_num); + + if ((status = btc_gattc_check_valid_param((int)char_num, offset)) != ESP_GATT_OK) { + if (db) { + osi_free(db); + } + *count = 0; + return status; + } else { + btc_gattc_fill_gatt_db_conversion(*count, char_num, ESP_GATT_DB_CHARACTERISTIC, offset, (void *)result, db); + } + + *count = char_num; + //don't forget to free the db buffer after used. + if (db) { + osi_free(db); + } + return ESP_GATT_OK; +} + +esp_gatt_status_t btc_ble_gattc_get_all_descr(uint16_t conn_id, + uint16_t char_handle, + esp_gattc_descr_elem_t *result, + uint16_t *count, uint16_t offset) +{ + esp_gatt_status_t status; + btgatt_db_element_t *db = NULL; + uint16_t descr_num = 0; + BTA_GATTC_GetAllDescriptor(conn_id, char_handle, &db, &descr_num); + + if ((status = btc_gattc_check_valid_param((int)descr_num, offset)) != ESP_GATT_OK) { + if (db) { + osi_free(db); + } + *count = 0; + return status; + } else { + btc_gattc_fill_gatt_db_conversion(*count, descr_num, ESP_GATT_DB_DESCRIPTOR, offset, (void *)result, db); + } + + *count = descr_num; + //don't forget to free the db buffer after used. + if (db) { + osi_free(db); + } + return ESP_GATT_OK; +} + +esp_gatt_status_t btc_ble_gattc_get_char_by_uuid(uint16_t conn_id, + uint16_t start_handle, + uint16_t end_handle, + esp_bt_uuid_t char_uuid, + esp_gattc_char_elem_t *result, + uint16_t *count) +{ + esp_gatt_status_t status; + btgatt_db_element_t *db = NULL; + uint16_t char_num = 0; + tBT_UUID bta_uuid = {0}; + btc_to_bta_uuid(&bta_uuid, &char_uuid); + BTA_GATTC_GetCharByUUID(conn_id, start_handle, end_handle, bta_uuid, &db, &char_num); + + if ((status = btc_gattc_check_valid_param((int)char_num, 0)) != ESP_GATT_OK) { + if (db) { + osi_free(db); + } + *count = 0; + return status; + } else { + btc_gattc_fill_gatt_db_conversion(*count, char_num, ESP_GATT_DB_CHARACTERISTIC, 0, (void *)result, db); + } + + *count = char_num; + //don't forget to free the db buffer after used. + if (db) { + osi_free(db); + } + return ESP_GATT_OK; +} + +esp_gatt_status_t btc_ble_gattc_get_descr_by_uuid(uint16_t conn_id, + uint16_t start_handle, + uint16_t end_handle, + esp_bt_uuid_t char_uuid, + esp_bt_uuid_t descr_uuid, + esp_gattc_descr_elem_t *result, + uint16_t *count) +{ + esp_gatt_status_t status; + btgatt_db_element_t *db = NULL; + uint16_t descr_num = 0; + tBT_UUID bta_char_uuid = {0}; + tBT_UUID bta_descr_uuid = {0}; + btc_to_bta_uuid(&bta_char_uuid, &char_uuid); + btc_to_bta_uuid(&bta_descr_uuid, &descr_uuid); + + BTA_GATTC_GetDescrByUUID(conn_id, start_handle, end_handle, + bta_char_uuid, bta_descr_uuid, &db, &descr_num); + + if ((status = btc_gattc_check_valid_param((int)descr_num, 0)) != ESP_GATT_OK) { + if (db) { + osi_free(db); + } + *count = 0; + return status; + } else { + btc_gattc_fill_gatt_db_conversion(*count, descr_num, ESP_GATT_DB_DESCRIPTOR, 0, (void *)result, db); + } + + *count = descr_num; + //don't forget to free the db buffer after used. + if (db) { + osi_free(db); + } + return ESP_GATT_OK; +} + +esp_gatt_status_t btc_ble_gattc_get_descr_by_char_handle(uint16_t conn_id, + uint16_t char_handle, + esp_bt_uuid_t descr_uuid, + esp_gattc_descr_elem_t *result, + uint16_t *count) +{ + esp_gatt_status_t status; + btgatt_db_element_t *db = NULL; + uint16_t descr_num = 0; + tBT_UUID bta_descr_uuid = {0}; + btc_to_bta_uuid(&bta_descr_uuid, &descr_uuid); + + BTA_GATTC_GetDescrByCharHandle(conn_id, char_handle, bta_descr_uuid, &db, &descr_num); + + if ((status = btc_gattc_check_valid_param((int)descr_num, 0)) != ESP_GATT_OK) { + if (db) { + osi_free(db); + } + *count = 0; + return status; + } else { + btc_gattc_fill_gatt_db_conversion(*count, descr_num, ESP_GATT_DB_DESCRIPTOR, 0, (void *)result, db); + } + + *count = descr_num; + //don't forget to free the db buffer after used. + if (db) { + osi_free(db); + } + return ESP_GATT_OK; + +} + +esp_gatt_status_t btc_ble_gattc_get_include_service(uint16_t conn_id, + uint16_t start_handle, + uint16_t end_handle, + esp_bt_uuid_t *incl_uuid, + esp_gattc_incl_svc_elem_t *result, + uint16_t *count) +{ + esp_gatt_status_t status; + btgatt_db_element_t *db = NULL; + uint16_t incl_num = 0; + tBT_UUID bta_uuid = {0}; + + if (incl_uuid != NULL) { + btc_to_bta_uuid(&bta_uuid, incl_uuid); + BTA_GATTC_GetIncludeService(conn_id, start_handle, end_handle, &bta_uuid, &db, &incl_num); + } else { + BTA_GATTC_GetIncludeService(conn_id, start_handle, end_handle, NULL, &db, &incl_num); + } + + if ((status = btc_gattc_check_valid_param((int)incl_num, 0)) != ESP_GATT_OK) { + if (db) { + osi_free(db); + } + *count = 0; + return status; + }else { + btc_gattc_fill_gatt_db_conversion(*count, incl_num, ESP_GATT_DB_INCLUDED_SERVICE, 0, (void *)result, db); + } + + *count = incl_num; + //don't forget to free the db buffer after used. + if (db) { + osi_free(db); + } + return ESP_GATT_OK; +} + +esp_gatt_status_t btc_ble_gattc_get_attr_count(uint16_t conn_id, + esp_gatt_db_attr_type_t type, + uint16_t start_handle, + uint16_t end_handle, + uint16_t char_handle, + uint16_t *count) +{ + if (type == ESP_GATT_DB_ALL) { + BTA_GATTC_GetDBSize(conn_id, start_handle, end_handle, count); + } else { + BTA_GATTC_GetDBSizeByType(conn_id, type, start_handle, end_handle, char_handle, count); + } + + return ESP_GATT_OK; +} + +esp_gatt_status_t btc_ble_gattc_get_db(uint16_t conn_id, uint16_t start_handle, uint16_t end_handle, + esp_gattc_db_elem_t *db, uint16_t *count) +{ + btgatt_db_element_t *get_db = NULL; + uint16_t num = 0; + tBT_UUID bta_uuid; + uint16_t db_size = 0; + BTA_GATTC_GetGattDb(conn_id, start_handle, end_handle, &get_db, &num); + + if (num == 0) { + if (get_db) { + osi_free(get_db); + } + *count = 0; + return ESP_GATT_NOT_FOUND; + } + + db_size = (*count > num) ? num : (*count); + for (int i = 0; i < db_size; i++) { + db[i].type = get_db[i].type; + db[i].attribute_handle = get_db[i].id; + db[i].start_handle = get_db[i].start_handle; + db[i].end_handle = get_db[i].end_handle; + db[i].properties = get_db[i].properties; + btc128_to_bta_uuid(&bta_uuid, get_db[i].uuid.uu); + bta_to_btc_uuid(&db[i].uuid, &bta_uuid); + } + *count = db_size; + //don't forget to free the db buffer after used. + if (get_db) { + osi_free(get_db); + } + return ESP_GATT_OK; +} + +static void btc_gattc_read_char(btc_ble_gattc_args_t *arg) +{ + BTA_GATTC_ReadCharacteristic(arg->read_char.conn_id, arg->read_char.handle, arg->read_char.auth_req); +} + +static void btc_gattc_read_multiple_char(btc_ble_gattc_args_t *arg) +{ + tBTA_GATTC_MULTI bta_multi; + bta_multi.num_attr = arg->read_multiple.num_attr; + memcpy(bta_multi.handles, arg->read_multiple.handles, BTA_GATTC_MULTI_MAX); + BTA_GATTC_ReadMultiple(arg->read_multiple.conn_id, &bta_multi, arg->read_multiple.auth_req); +} + +static void btc_gattc_read_multiple_variable_char(btc_ble_gattc_args_t *arg) +{ + tBTA_GATTC_MULTI bta_multi; + bta_multi.num_attr = arg->read_multiple.num_attr; + memcpy(bta_multi.handles, arg->read_multiple.handles, BTA_GATTC_MULTI_MAX); + BTA_GATTC_ReadMultipleVariable(arg->read_multiple.conn_id, &bta_multi, arg->read_multiple.auth_req); +} + +static void btc_gattc_read_char_descr(btc_ble_gattc_args_t *arg) +{ + BTA_GATTC_ReadCharDescr(arg->read_descr.conn_id, arg->read_descr.handle, arg->read_descr.auth_req); +} + +static void btc_gattc_read_by_type(btc_ble_gattc_args_t *arg) +{ + tBT_UUID uuid; + btc_to_bta_uuid(&uuid, &(arg->read_by_type.uuid)); + BTA_GATTC_Read_by_type(arg->read_by_type.conn_id, arg->read_by_type.s_handle, arg->read_by_type.e_handle, &uuid, arg->read_by_type.auth_req); +} + +static void btc_gattc_write_char(btc_ble_gattc_args_t *arg) +{ + BTA_GATTC_WriteCharValue(arg->write_char.conn_id, + arg->write_char.handle, + arg->write_char.write_type, + arg->write_char.value_len, + arg->write_char.value, + arg->write_char.auth_req); +} + +static void btc_gattc_write_char_descr(btc_ble_gattc_args_t *arg) +{ + tBTA_GATT_UNFMT descr_val; + + descr_val.len = arg->write_descr.value_len; + descr_val.p_value = arg->write_descr.value; + + BTA_GATTC_WriteCharDescr(arg->write_descr.conn_id, + arg->write_descr.handle, + arg->write_descr.write_type, &descr_val, + arg->write_descr.auth_req); +} + +static void btc_gattc_prepare_write(btc_ble_gattc_args_t *arg) +{ + BTA_GATTC_PrepareWrite(arg->prep_write.conn_id, + arg->prep_write.handle, + arg->prep_write.offset, + arg->prep_write.value_len, + arg->prep_write.value, + arg->prep_write.auth_req); +} +static void btc_gattc_prepare_write_char_descr(btc_ble_gattc_args_t *arg) +{ + tBTA_GATT_UNFMT descr_val; + + descr_val.len = arg->prep_write_descr.value_len; + descr_val.p_value = arg->prep_write_descr.value; + BTA_GATTC_PrepareWriteCharDescr(arg->prep_write_descr.conn_id, + arg->prep_write_descr.handle, + arg->prep_write_descr.offset, + &descr_val, + arg->prep_write_descr.auth_req); +} + +static void btc_gattc_execute_write(btc_ble_gattc_args_t *arg) +{ + BTA_GATTC_ExecuteWrite(arg->exec_write.conn_id, arg->exec_write.is_execute); +} + +static void btc_gattc_reg_for_notify(btc_ble_gattc_args_t *arg) +{ + tBTA_GATT_STATUS status; + esp_ble_gattc_cb_param_t param; + + status = BTA_GATTC_RegisterForNotifications(arg->reg_for_notify.gattc_if, + arg->reg_for_notify.remote_bda, + arg->reg_for_notify.handle); + + memset(¶m, 0, sizeof(esp_ble_gattc_cb_param_t)); + param.reg_for_notify.status = status; + param.reg_for_notify.handle = arg->reg_for_notify.handle; + btc_gattc_cb_to_app(ESP_GATTC_REG_FOR_NOTIFY_EVT, arg->reg_for_notify.gattc_if, ¶m); +} + +static void btc_gattc_unreg_for_notify(btc_ble_gattc_args_t *arg) +{ + tBTA_GATT_STATUS status; + esp_ble_gattc_cb_param_t param; + + status = BTA_GATTC_DeregisterForNotifications(arg->unreg_for_notify.gattc_if, + arg->unreg_for_notify.remote_bda, + arg->unreg_for_notify.handle); + + memset(¶m, 0, sizeof(esp_ble_gattc_cb_param_t)); + param.unreg_for_notify.status = status; + param.unreg_for_notify.handle = arg->unreg_for_notify.handle; + btc_gattc_cb_to_app(ESP_GATTC_UNREG_FOR_NOTIFY_EVT, arg->unreg_for_notify.gattc_if, ¶m); +} + +void btc_gattc_call_handler(btc_msg_t *msg) +{ + btc_ble_gattc_args_t *arg = (btc_ble_gattc_args_t *)(msg->arg); + switch (msg->act) { + case BTC_GATTC_ACT_APP_REGISTER: + btc_gattc_app_register(arg); + break; + case BTC_GATTC_ACT_APP_UNREGISTER: + btc_gattc_app_unregister(arg); + break; + case BTC_GATTC_ACT_OPEN: +#if (BLE_50_FEATURE_SUPPORT == TRUE) + case BTC_GATTC_ACT_AUX_OPEN: +#endif // #if (BLE_50_FEATURE_SUPPORT == TRUE) + btc_gattc_open(arg); + break; + case BTC_GATTC_ACT_CLOSE: + btc_gattc_close(arg); + break; + case BTC_GATTC_ACT_CFG_MTU: + btc_gattc_cfg_mtu(arg); + break; + case BTC_GATTC_ACT_SEARCH_SERVICE: + btc_gattc_search_service(arg); + break; + case BTC_GATTC_ACT_READ_CHAR: + btc_gattc_read_char(arg); + break; + case BTC_GATTC_ACT_READ_MULTIPLE_CHAR: + btc_gattc_read_multiple_char(arg); + break; + case BTC_GATTC_ACT_READ_MULTIPLE_VARIABLE_CHAR: + btc_gattc_read_multiple_variable_char(arg); + break; + case BTC_GATTC_ACT_READ_CHAR_DESCR: + btc_gattc_read_char_descr(arg); + break; + case BTC_GATTC_ACT_READ_BY_TYPE: + btc_gattc_read_by_type(arg); + break; + case BTC_GATTC_ACT_WRITE_CHAR: + btc_gattc_write_char(arg); + break; + case BTC_GATTC_ACT_WRITE_CHAR_DESCR: + btc_gattc_write_char_descr(arg); + break; + case BTC_GATTC_ACT_PREPARE_WRITE: + btc_gattc_prepare_write(arg); + break; + case BTC_GATTC_ACT_PREPARE_WRITE_CHAR_DESCR: + btc_gattc_prepare_write_char_descr(arg); + break; + case BTC_GATTC_ACT_EXECUTE_WRITE: + btc_gattc_execute_write(arg); + break; + case BTC_GATTC_ACT_REG_FOR_NOTIFY: + btc_gattc_reg_for_notify(arg); + break; + case BTC_GATTC_ACT_UNREG_FOR_NOTIFY: + btc_gattc_unreg_for_notify(arg); + break; + case BTC_GATTC_ACT_CACHE_REFRESH: + BTA_GATTC_Refresh(arg->cache_refresh.remote_bda, true); + break; + case BTC_GATTC_ACT_CACHE_ASSOC: + BTA_GATTC_CacheAssoc(arg->cache_assoc.gattc_if, + arg->cache_assoc.src_addr, + arg->cache_assoc.assoc_addr, + arg->cache_assoc.is_assoc); + break; + case BTC_GATTC_ATC_CACHE_GET_ADDR_LIST: + BTA_GATTC_CacheGetAddrList(arg->get_addr_list.gattc_if); + break; + case BTC_GATTC_ACT_CACHE_CLEAN: + BTA_GATTC_Clean(arg->cache_clean.remote_bda); + break; + default: + BTC_TRACE_ERROR("%s: Unhandled event (%d)!\n", __FUNCTION__, msg->act); + break; + } + + btc_gattc_arg_deep_free(msg); +} + +void btc_gattc_cb_handler(btc_msg_t *msg) +{ + tBTA_GATTC *arg = (tBTA_GATTC *)(msg->arg); + esp_gatt_if_t gattc_if = 0; + esp_ble_gattc_cb_param_t param = {0}; + + memset(¶m, 0, sizeof(esp_ble_gattc_cb_param_t)); + + switch (msg->act) { + case BTA_GATTC_REG_EVT: { + tBTA_GATTC_REG *reg_oper = &arg->reg_oper; + + gattc_if = reg_oper->client_if; + param.reg.status = reg_oper->status; + param.reg.app_id = reg_oper->app_uuid.uu.uuid16; + btc_gattc_cb_to_app(ESP_GATTC_REG_EVT, gattc_if, ¶m); + break; + } + case BTA_GATTC_DEREG_EVT: { + tBTA_GATTC_REG *reg_oper = &arg->reg_oper; + + gattc_if = reg_oper->client_if; + btc_gattc_cb_to_app(ESP_GATTC_UNREG_EVT, gattc_if, NULL); + break; + } + case BTA_GATTC_READ_CHAR_EVT: { + set_read_value(&gattc_if, ¶m, &arg->read); + btc_gattc_cb_to_app(ESP_GATTC_READ_CHAR_EVT, gattc_if, ¶m); + break; + } + case BTA_GATTC_WRITE_CHAR_EVT: + case BTA_GATTC_PREP_WRITE_EVT: { + tBTA_GATTC_WRITE *write = &arg->write; + uint32_t ret_evt = (msg->act == BTA_GATTC_WRITE_CHAR_EVT) ? + ESP_GATTC_WRITE_CHAR_EVT : ESP_GATTC_PREP_WRITE_EVT; + + gattc_if = BTC_GATT_GET_GATT_IF(write->conn_id); + param.write.conn_id = BTC_GATT_GET_CONN_ID(write->conn_id); + param.write.status = write->status; + param.write.handle = write->handle; + param.write.offset = write->offset; + btc_gattc_cb_to_app(ret_evt, gattc_if, ¶m); + break; + } + + case BTA_GATTC_EXEC_EVT: { + tBTA_GATTC_EXEC_CMPL *exec_cmpl = &arg->exec_cmpl; + + gattc_if = BTC_GATT_GET_GATT_IF(exec_cmpl->conn_id); + param.exec_cmpl.conn_id = BTC_GATT_GET_CONN_ID(exec_cmpl->conn_id); + param.exec_cmpl.status = exec_cmpl->status; + btc_gattc_cb_to_app(ESP_GATTC_EXEC_EVT, gattc_if, ¶m); + break; + } + + case BTA_GATTC_SEARCH_CMPL_EVT: { + tBTA_GATTC_SEARCH_CMPL *search_cmpl = &arg->search_cmpl; + + gattc_if = BTC_GATT_GET_GATT_IF(search_cmpl->conn_id); + param.search_cmpl.conn_id = BTC_GATT_GET_CONN_ID(search_cmpl->conn_id); + param.search_cmpl.status = search_cmpl->status; + param.search_cmpl.searched_service_source = search_cmpl->searched_service_source; + btc_gattc_cb_to_app(ESP_GATTC_SEARCH_CMPL_EVT, gattc_if, ¶m); + break; + } + case BTA_GATTC_SEARCH_RES_EVT: { + tBTA_GATTC_SRVC_RES *srvc_res = &arg->srvc_res; + + gattc_if = BTC_GATT_GET_GATT_IF(srvc_res->conn_id); + param.search_res.conn_id = BTC_GATT_GET_CONN_ID(srvc_res->conn_id); + param.search_res.start_handle = srvc_res->start_handle; + param.search_res.end_handle = srvc_res->end_handle; + param.search_res.is_primary = srvc_res->is_primary; + bta_to_btc_gatt_id(¶m.search_res.srvc_id, &srvc_res->service_uuid); + btc_gattc_cb_to_app(ESP_GATTC_SEARCH_RES_EVT, gattc_if, ¶m); + break; + } + case BTA_GATTC_READ_DESCR_EVT: { + set_read_value(&gattc_if, ¶m, &arg->read); + btc_gattc_cb_to_app(ESP_GATTC_READ_DESCR_EVT, gattc_if, ¶m); + break; + } + case BTA_GATTC_READ_MULTIPLE_EVT: { + set_read_value(&gattc_if, ¶m, &arg->read); + btc_gattc_cb_to_app(ESP_GATTC_READ_MULTIPLE_EVT, gattc_if, ¶m); + break; + } + case BTA_GATTC_READ_MULTI_VAR_EVT: { + set_read_value(&gattc_if, ¶m, &arg->read); + btc_gattc_cb_to_app(ESP_GATTC_READ_MULTI_VAR_EVT, gattc_if, ¶m); + break; + } + case BTA_GATTC_WRITE_DESCR_EVT: { + tBTA_GATTC_WRITE *write = &arg->write; + + gattc_if = BTC_GATT_GET_GATT_IF(write->conn_id); + param.write.conn_id = BTC_GATT_GET_CONN_ID(write->conn_id); + param.write.status = write->status; + param.write.handle = write->handle; + btc_gattc_cb_to_app(ESP_GATTC_WRITE_DESCR_EVT, gattc_if, ¶m); + break; + } + case BTA_GATTC_NOTIF_EVT: { + tBTA_GATTC_NOTIFY *notify = &arg->notify; + + gattc_if = BTC_GATT_GET_GATT_IF(notify->conn_id); + param.notify.conn_id = BTC_GATT_GET_CONN_ID(notify->conn_id); + memcpy(param.notify.remote_bda, notify->bda, sizeof(esp_bd_addr_t)); + param.notify.handle = notify->handle; + param.notify.is_notify = (notify->is_notify == TRUE) ? true : false; + param.notify.value_len = (notify->len > ESP_GATT_MAX_ATTR_LEN) ? \ + ESP_GATT_MAX_ATTR_LEN : notify->len; + param.notify.value = notify->value; + + if (notify->is_notify == FALSE) { + BTA_GATTC_SendIndConfirm(notify->conn_id, notify->handle); + } + + btc_gattc_cb_to_app(ESP_GATTC_NOTIFY_EVT, gattc_if, ¶m); + break; + } + case BTA_GATTC_OPEN_EVT: { + tBTA_GATTC_OPEN *open = &arg->open; + + gattc_if = open->client_if; + param.open.status = open->status; + param.open.conn_id = BTC_GATT_GET_CONN_ID(open->conn_id); + memcpy(param.open.remote_bda, open->remote_bda, sizeof(esp_bd_addr_t)); + param.open.mtu = open->mtu; + btc_gattc_cb_to_app(ESP_GATTC_OPEN_EVT, gattc_if, ¶m); + break; + } + case BTA_GATTC_CONNECT_EVT: { + tBTA_GATTC_CONNECT *connect = &arg->connect; +#if (SMP_INCLUDED == TRUE) + bt_bdaddr_t bt_addr; + + memcpy(bt_addr.address, connect->remote_bda, sizeof(bt_addr.address)); + if (btc_storage_update_active_device(&bt_addr)) { + BTC_TRACE_EVENT("Device: %02x:%02x:%02x:%02x:%02x:%02x, is not in bond list", + bt_addr.address[0], bt_addr.address[1], + bt_addr.address[2], bt_addr.address[3], + bt_addr.address[4], bt_addr.address[5]); + } +#endif ///SMP_INCLUDED == TRUE + gattc_if = connect->client_if; + param.connect.conn_id = BTC_GATT_GET_CONN_ID(connect->conn_id); + param.connect.link_role = connect->link_role; + memcpy(param.connect.remote_bda, connect->remote_bda, sizeof(esp_bd_addr_t)); + param.connect.conn_params.interval = connect->conn_params.interval; + param.connect.conn_params.latency = connect->conn_params.latency; + param.connect.conn_params.timeout = connect->conn_params.timeout; + param.connect.ble_addr_type = connect->ble_addr_type; + param.connect.conn_handle = connect->conn_handle; + btc_gattc_cb_to_app(ESP_GATTC_CONNECT_EVT, gattc_if, ¶m); + break; + } + case BTA_GATTC_CLOSE_EVT: { + tBTA_GATTC_CLOSE *close = &arg->close; + + // Free gattc clcb in BTC task to avoid race condition + bta_gattc_clcb_dealloc_by_conn_id(close->conn_id); + gattc_if = close->client_if; + param.close.status = close->status; + param.close.conn_id = BTC_GATT_GET_CONN_ID(close->conn_id); + memcpy(param.close.remote_bda, close->remote_bda, sizeof(esp_bd_addr_t)); + param.close.reason = close->reason; + btc_gattc_cb_to_app(ESP_GATTC_CLOSE_EVT, gattc_if, ¶m); + break; + } + case BTA_GATTC_DISCONNECT_EVT: { + tBTA_GATTC_DISCONNECT *disconnect = &arg->disconnect; + + gattc_if = disconnect->client_if; + param.disconnect.reason = disconnect->reason; + param.disconnect.conn_id = BTC_GATT_GET_CONN_ID(disconnect->conn_id); + memcpy(param.disconnect.remote_bda, disconnect->remote_bda, sizeof(esp_bd_addr_t)); + btc_gattc_cb_to_app(ESP_GATTC_DISCONNECT_EVT, gattc_if, ¶m); + break; + } + case BTA_GATTC_CFG_MTU_EVT: { + tBTA_GATTC_CFG_MTU *cfg_mtu = &arg->cfg_mtu; + + gattc_if = BTC_GATT_GET_GATT_IF(cfg_mtu->conn_id); + param.cfg_mtu.conn_id = BTC_GATT_GET_CONN_ID(cfg_mtu->conn_id); + param.cfg_mtu.status = cfg_mtu->status; + param.cfg_mtu.mtu = cfg_mtu->mtu; + btc_gattc_cb_to_app(ESP_GATTC_CFG_MTU_EVT, gattc_if, ¶m); + break; + } + + case BTA_GATTC_ACL_EVT: { + /* Currently, this event will never happen */ + break; + } + case BTA_GATTC_CANCEL_OPEN_EVT: { + /* Currently, this event will never happen */ + break; + } + case BTA_GATTC_CONGEST_EVT: { + tBTA_GATTC_CONGEST *congest = &arg->congest; + + gattc_if = BTC_GATT_GET_GATT_IF(congest->conn_id); + param.congest.conn_id = BTC_GATT_GET_CONN_ID(congest->conn_id); + param.congest.congested = (congest->congested == TRUE) ? true : false; + btc_gattc_cb_to_app(ESP_GATTC_CONGEST_EVT, gattc_if, ¶m); + break; + } + case BTA_GATTC_SRVC_CHG_EVT: { + tBTA_GATTC_SERVICE_CHANGE *srvc_change = &arg->srvc_chg; + gattc_if = BTC_GATT_GET_GATT_IF(srvc_change->conn_id); + memcpy(param.srvc_chg.remote_bda, srvc_change->remote_bda, sizeof(esp_bd_addr_t)); + btc_gattc_cb_to_app(ESP_GATTC_SRVC_CHG_EVT, gattc_if, ¶m); + break; + } + case BTA_GATTC_QUEUE_FULL_EVT: { + tBTA_GATTC_QUEUE_FULL *queue_full = &arg->queue_full; + gattc_if = BTC_GATT_GET_GATT_IF(queue_full->conn_id); + param.queue_full.conn_id = BTC_GATT_GET_CONN_ID(queue_full->conn_id); + param.queue_full.status = arg->status; + param.queue_full.is_full = queue_full->is_full; + btc_gattc_cb_to_app(ESP_GATTC_QUEUE_FULL_EVT, gattc_if, ¶m); + break; + } + case BTA_GATTC_ASSOC_EVT: { + gattc_if = arg->set_assoc.client_if; + param.set_assoc_cmp.status = arg->set_assoc.status; + btc_gattc_cb_to_app(ESP_GATTC_SET_ASSOC_EVT, gattc_if, ¶m); + break; + } + case BTA_GATTC_GET_ADDR_LIST_EVT: { + gattc_if = arg->get_addr_list.client_if; + param.get_addr_list.status = arg->get_addr_list.status; + param.get_addr_list.num_addr = arg->get_addr_list.num_addr; + param.get_addr_list.addr_list = arg->get_addr_list.bda_list; + btc_gattc_cb_to_app(ESP_GATTC_GET_ADDR_LIST_EVT, gattc_if, ¶m); + break; + } + case BTA_GATTC_DIS_SRVC_CMPL_EVT: + gattc_if = BTC_GATT_GET_GATT_IF(arg->dis_cmpl.conn_id); + param.dis_srvc_cmpl.status = arg->dis_cmpl.status; + param.dis_srvc_cmpl.conn_id = BTC_GATT_GET_CONN_ID(arg->dis_cmpl.conn_id); + btc_gattc_cb_to_app(ESP_GATTC_DIS_SRVC_CMPL_EVT, gattc_if, ¶m); + break; + default: + BTC_TRACE_DEBUG("%s: Unhandled event (%d)!", __FUNCTION__, msg->act); + break; + } + + // free the deep-copied data + btc_gattc_free_req_data(msg); +} + +void btc_gattc_congest_callback(tBTA_GATTC *param) +{ + esp_ble_gattc_cb_param_t esp_param = {0}; + memset(&esp_param, 0, sizeof(esp_ble_gattc_cb_param_t)); + + uint8_t gattc_if = BTC_GATT_GET_GATT_IF(param->congest.conn_id); + esp_param.congest.conn_id = BTC_GATT_GET_CONN_ID(param->congest.conn_id); + esp_param.congest.congested = (param->congest.congested == TRUE) ? true : false; + btc_gattc_cb_to_app(ESP_GATTC_CONGEST_EVT, gattc_if, &esp_param); + +} + +#endif ///GATTC_INCLUDED == TRUE diff --git a/lib/bt/host/bluedroid/btc/profile/std/gatt/btc_gatts.c b/lib/bt/host/bluedroid/btc/profile/std/gatt/btc_gatts.c new file mode 100644 index 00000000..df9a4ceb --- /dev/null +++ b/lib/bt/host/bluedroid/btc/profile/std/gatt/btc_gatts.c @@ -0,0 +1,992 @@ +/* + * SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include "bta/bta_gatt_api.h" + +#include "btc/btc_task.h" +#include "btc/btc_manage.h" +#include "btc_gatts.h" +#include "btc_gatt_util.h" +#include "osi/future.h" +#include "osi/allocator.h" +#include "btc/btc_main.h" +#include "esp_gatts_api.h" +#include "btc/btc_storage.h" +#include "common/bt_defs.h" + +#if (GATTS_INCLUDED == TRUE) + +#define A2C_GATTS_EVT(_bta_event) (_bta_event) //BTA TO BTC EVT +#define C2A_GATTS_EVT(_btc_event) (_btc_event) //BTC TO BTA EVT + +#if GATT_DYNAMIC_MEMORY == FALSE +static esp_btc_creat_tab_t btc_creat_tab_env; +#else +esp_btc_creat_tab_t *btc_creat_tab_env_ptr; +#endif + +static esp_gatt_status_t btc_gatts_check_valid_attr_tab(esp_gatts_attr_db_t *gatts_attr_db, + uint8_t max_nb_attr); + +static inline void btc_gatts_cb_to_app(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param) +{ + esp_gatts_cb_t btc_gatts_cb = (esp_gatts_cb_t)btc_profile_cb_get(BTC_PID_GATTS); + if (btc_gatts_cb) { + btc_gatts_cb(event, gatts_if, param); + } +} + +static inline void btc_gatts_uuid_format_convert(esp_bt_uuid_t* dest_uuid, uint16_t src_uuid_len, uint8_t* src_uuid_p) +{ + dest_uuid->len = src_uuid_len; + if(src_uuid_len == ESP_UUID_LEN_16){ + dest_uuid->uuid.uuid16 = src_uuid_p[0] + (src_uuid_p[1]<<8); + } + else if(src_uuid_len == ESP_UUID_LEN_32){ + dest_uuid->uuid.uuid32 = src_uuid_p[0] + (src_uuid_p[1]<<8) + (src_uuid_p[2]<<16) + (src_uuid_p[3]<<24); + } + else if(src_uuid_len == ESP_UUID_LEN_128){ + memcpy(dest_uuid->uuid.uuid128, src_uuid_p, src_uuid_len); + } + else{ + BTC_TRACE_ERROR("%s wrong uuid length %d\n", __func__, src_uuid_len); + } + +} + + +void btc_gatts_arg_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src) +{ + btc_ble_gatts_args_t *dst = (btc_ble_gatts_args_t *) p_dest; + btc_ble_gatts_args_t *src = (btc_ble_gatts_args_t *)p_src; + + switch (msg->act) { + case BTC_GATTS_ACT_SEND_INDICATE: { + if (src->send_ind.value && (src->send_ind.value_len > 0)) { + dst->send_ind.value = (uint8_t *) osi_malloc(src->send_ind.value_len); + if (dst->send_ind.value) { + memcpy(dst->send_ind.value, src->send_ind.value, src->send_ind.value_len); + } else { + BTC_TRACE_ERROR("%s %d no mem\n", __func__, msg->act); + } + } else { + dst->send_ind.value = NULL; + if (src->send_ind.value) { + BTC_TRACE_ERROR("%s %d, invalid length", __func__, msg->act); + } + } + break; + } + case BTC_GATTS_ACT_SEND_RESPONSE: { + if (src->send_rsp.rsp) { + dst->send_rsp.rsp = (esp_gatt_rsp_t *) osi_malloc(sizeof(esp_gatt_rsp_t)); + if (dst->send_rsp.rsp) { + memcpy(dst->send_rsp.rsp, src->send_rsp.rsp, sizeof(esp_gatt_rsp_t)); + } else { + BTC_TRACE_ERROR("%s %d no mem\n", __func__, msg->act); + } + } + break; + + } + case BTC_GATTS_ACT_ADD_CHAR: { + if (src->add_char.char_val.attr_value && (src->add_char.char_val.attr_len > 0)) { + dst->add_char.char_val.attr_value = (uint8_t *) osi_malloc(src->add_char.char_val.attr_len); + if (dst->add_char.char_val.attr_value) { + memcpy(dst->add_char.char_val.attr_value, src->add_char.char_val.attr_value, + src->add_char.char_val.attr_len); + } else { + BTC_TRACE_ERROR("%s %d no mem\n", __func__, msg->act); + } + } else { + dst->add_char.char_val.attr_value = NULL; + if (src->add_char.char_val.attr_value) { + BTC_TRACE_ERROR("%s %d, invalid length", __func__, msg->act); + } + } + break; + } + case BTC_GATTS_ACT_ADD_CHAR_DESCR: { + if (src->add_descr.descr_val.attr_value && (src->add_descr.descr_val.attr_len > 0)) { + dst->add_descr.descr_val.attr_value = (uint8_t *) osi_malloc(src->add_descr.descr_val.attr_len); + if (dst->add_descr.descr_val.attr_value) { + memcpy(dst->add_descr.descr_val.attr_value, src->add_descr.descr_val.attr_value, + src->add_descr.descr_val.attr_len); + } else { + BTC_TRACE_ERROR("%s %d no mem\n", __func__, msg->act); + } + } else { + dst->add_descr.descr_val.attr_value = NULL; + if (src->add_descr.descr_val.attr_value) { + BTC_TRACE_ERROR("%s %d, invalid length", __func__, msg->act); + } + } + break; + } + case BTC_GATTS_ACT_CREATE_ATTR_TAB: { + uint16_t num_attr = src->create_attr_tab.max_nb_attr; + if (src->create_attr_tab.gatts_attr_db && (num_attr > 0)) { + dst->create_attr_tab.gatts_attr_db = (esp_gatts_attr_db_t *) osi_malloc(sizeof(esp_gatts_attr_db_t) * num_attr); + if (dst->create_attr_tab.gatts_attr_db) { + memcpy(dst->create_attr_tab.gatts_attr_db, src->create_attr_tab.gatts_attr_db, + sizeof(esp_gatts_attr_db_t) * num_attr); + } else { + BTC_TRACE_ERROR("%s %d no mem\n",__func__, msg->act); + } + } else { + BTC_TRACE_ERROR("%s %d, NULL data", __func__, msg->act); + } + break; + } + case BTC_GATTS_ACT_SET_ATTR_VALUE: { + if (src->set_attr_val.value && (src->set_attr_val.length > 0)) { + dst->set_attr_val.value = (uint8_t *) osi_malloc(src->set_attr_val.length); + if (dst->set_attr_val.value) { + memcpy(dst->set_attr_val.value, src->set_attr_val.value, src->set_attr_val.length); + } else { + BTC_TRACE_ERROR("%s %d no mem\n",__func__, msg->act); + } + } else { + dst->set_attr_val.value = NULL; + if (src->set_attr_val.value) { + BTC_TRACE_ERROR("%s %d, invalid length", __func__, msg->act); + } else { + BTC_TRACE_WARNING("%s %d, NULL value", __func__, msg->act); + } + } + break; + } + default: + BTC_TRACE_DEBUG("%s Unhandled deep copy %d\n", __func__, msg->act); + break; + } + +} + +void btc_gatts_arg_deep_free(btc_msg_t *msg) +{ + btc_ble_gatts_args_t *arg = (btc_ble_gatts_args_t *)msg->arg; + + switch (msg->act) { + case BTC_GATTS_ACT_SEND_INDICATE: { + if (arg->send_ind.value) { + osi_free(arg->send_ind.value); + } + break; + } + case BTC_GATTS_ACT_SEND_RESPONSE: { + if (arg->send_rsp.rsp) { + osi_free(arg->send_rsp.rsp); + } + break; + } + case BTC_GATTS_ACT_ADD_CHAR:{ + if (arg->add_char.char_val.attr_value != NULL) { + osi_free(arg->add_char.char_val.attr_value); + } + break; + } + case BTC_GATTS_ACT_ADD_CHAR_DESCR:{ + if (arg->add_descr.descr_val.attr_value != NULL){ + osi_free(arg->add_descr.descr_val.attr_value); + } + break; + } + case BTC_GATTS_ACT_CREATE_ATTR_TAB:{ + if (arg->create_attr_tab.gatts_attr_db != NULL){ + osi_free(arg->create_attr_tab.gatts_attr_db); + } + break; + } + case BTC_GATTS_ACT_SET_ATTR_VALUE:{ + if (arg->set_attr_val.value != NULL){ + osi_free(arg->set_attr_val.value); + } + } + break; + + default: + BTC_TRACE_DEBUG("%s Unhandled deep free %d\n", __func__, msg->act); + break; + } + +} + +static void btc_gatts_act_create_attr_tab(esp_gatts_attr_db_t *gatts_attr_db, + esp_gatt_if_t gatts_if, + uint16_t max_nb_attr, + uint8_t srvc_inst_id) +{ + uint16_t uuid = 0; + future_t *future_p; + esp_ble_gatts_cb_param_t param; + param.add_attr_tab.status = ESP_GATT_OK; + param.add_attr_tab.num_handle = max_nb_attr; + + if (param.add_attr_tab.status != ESP_GATT_OK) { + btc_gatts_cb_to_app(ESP_GATTS_CREAT_ATTR_TAB_EVT, gatts_if, ¶m); + //reset the env after sent the data to app + memset(&btc_creat_tab_env, 0, sizeof(esp_btc_creat_tab_t)); + return; + } + + // Check the attribute table is valid or not + if ((param.add_attr_tab.status = btc_gatts_check_valid_attr_tab(gatts_attr_db, max_nb_attr)) != ESP_GATT_OK) { + //sent the callback event to the application + btc_gatts_cb_to_app(ESP_GATTS_CREAT_ATTR_TAB_EVT, gatts_if, ¶m); + return; + } + + + //set the attribute table create service flag to true + btc_creat_tab_env.is_tab_creat_svc = true; + btc_creat_tab_env.num_handle = max_nb_attr; + for(int i = 0; i < max_nb_attr; i++){ + if(gatts_attr_db[i].att_desc.uuid_length == ESP_UUID_LEN_16){ + uuid = (gatts_attr_db[i].att_desc.uuid_p[1] << 8) + (gatts_attr_db[i].att_desc.uuid_p[0]); + } + else{ + continue; + } + future_p = future_new(); + if (future_p == NULL) { + BTC_TRACE_ERROR("%s failed:no mem\n", __func__); + return ; + } + btc_creat_tab_env.complete_future = future_p; + btc_creat_tab_env.handle_idx = i; + switch(uuid) + { + case ESP_GATT_UUID_PRI_SERVICE:{ + tBTA_GATT_SRVC_ID srvc_id; + esp_gatt_srvc_id_t esp_srvc_id; + + esp_srvc_id.id.inst_id = srvc_inst_id; + btc_gatts_uuid_format_convert(&esp_srvc_id.id.uuid,gatts_attr_db[i].att_desc.length, + gatts_attr_db[i].att_desc.value); + + btc_to_bta_srvc_id(&srvc_id, &esp_srvc_id); + if (btc_creat_tab_env.is_use_svc != true) { + BTA_GATTS_CreateService(gatts_if, &srvc_id.id.uuid, + srvc_inst_id, max_nb_attr, true); + btc_creat_tab_env.is_use_svc = true; + } else { + BTC_TRACE_ERROR("Each service table can only created one primary service."); + param.add_attr_tab.status = ESP_GATT_ERROR; + btc_gatts_cb_to_app(ESP_GATTS_CREAT_ATTR_TAB_EVT, gatts_if, ¶m); + //reset the env after sent the data to app + memset(&btc_creat_tab_env, 0, sizeof(esp_btc_creat_tab_t)); + return; + } + + if (future_await(future_p) == FUTURE_FAIL) { + BTC_TRACE_ERROR("%s failed\n", __func__); + return; + } + break; + } + case ESP_GATT_UUID_SEC_SERVICE:{ + tBTA_GATT_SRVC_ID srvc_id = {0}; + esp_gatt_srvc_id_t esp_srvc_id; + + esp_srvc_id.id.inst_id = srvc_inst_id; + btc_gatts_uuid_format_convert(&esp_srvc_id.id.uuid,gatts_attr_db[i].att_desc.length, + gatts_attr_db[i].att_desc.value); + btc_to_bta_srvc_id(&srvc_id, &esp_srvc_id); + if (btc_creat_tab_env.is_use_svc != true) { + BTA_GATTS_CreateService(gatts_if, &srvc_id.id.uuid, + srvc_inst_id, max_nb_attr, false); + btc_creat_tab_env.is_use_svc = true; + } else { + BTC_TRACE_ERROR("Each service table can only created one secondary service."); + param.add_attr_tab.status = ESP_GATT_ERROR; + btc_gatts_cb_to_app(ESP_GATTS_CREAT_ATTR_TAB_EVT, gatts_if, ¶m); + //reset the env after sent the data to app + memset(&btc_creat_tab_env, 0, sizeof(esp_btc_creat_tab_t)); + return; + } + if (future_await(future_p) == FUTURE_FAIL) { + BTC_TRACE_ERROR("%s failed\n", __func__); + return; + } + break; + } + case ESP_GATT_UUID_INCLUDE_SERVICE:{ + esp_gatts_incl_svc_desc_t *incl_svc_desc = (esp_gatts_incl_svc_desc_t *)gatts_attr_db[i].att_desc.value; + + if(incl_svc_desc!= NULL){ + if(btc_creat_tab_env.svc_start_hdl != 0){ + BTA_GATTS_AddIncludeService(btc_creat_tab_env.svc_start_hdl, + incl_svc_desc->start_hdl); + + if (future_await(future_p) == FUTURE_FAIL) { + BTC_TRACE_ERROR("%s failed\n", __func__); + return; + } + } + } + break; + } + case ESP_GATT_UUID_CHAR_DECLARE:{ + uint16_t svc_hal = 0; + tBT_UUID bta_char_uuid; + tGATT_ATTR_VAL attr_val; + esp_bt_uuid_t uuid_temp; + tBTA_GATT_PERM perm; + tBTA_GATTS_ATTR_CONTROL control; + uint8_t char_property; + + if(btc_creat_tab_env.svc_start_hdl != 0){ + svc_hal = btc_creat_tab_env.svc_start_hdl; + if((gatts_attr_db[i].att_desc.value) == NULL){ + BTC_TRACE_ERROR("%s Characteristic declaration should not be NULL\n", __func__); + } + else{ + char_property = (uint8_t)(*(uint8_t*)(gatts_attr_db[i].att_desc.value)); + perm = gatts_attr_db[i+1].att_desc.perm; + attr_val.attr_len = gatts_attr_db[i+1].att_desc.length; + attr_val.attr_max_len = gatts_attr_db[i+1].att_desc.max_length; + btc_gatts_uuid_format_convert(&uuid_temp, gatts_attr_db[i+1].att_desc.uuid_length,gatts_attr_db[i+1].att_desc.uuid_p); + btc_to_bta_uuid(&bta_char_uuid, &uuid_temp); + attr_val.attr_val = gatts_attr_db[i+1].att_desc.value; + control.auto_rsp = gatts_attr_db[i+1].attr_control.auto_rsp; + BTA_GATTS_AddCharacteristic (svc_hal, &bta_char_uuid, + perm, char_property, &attr_val, &control); + + if (future_await(future_p) == FUTURE_FAIL) { + BTC_TRACE_ERROR("%s failed\n", __func__); + return; + } + } + } + + break; + } + case ESP_GATT_UUID_CHAR_EXT_PROP: + case ESP_GATT_UUID_CHAR_DESCRIPTION: + case ESP_GATT_UUID_CHAR_CLIENT_CONFIG: + case ESP_GATT_UUID_CHAR_SRVR_CONFIG: + case ESP_GATT_UUID_CHAR_PRESENT_FORMAT: + case ESP_GATT_UUID_CHAR_AGG_FORMAT: + case ESP_GATT_UUID_CHAR_VALID_RANGE: + case ESP_GATT_UUID_EXT_RPT_REF_DESCR: + case ESP_GATT_UUID_RPT_REF_DESCR: + case ESP_GATT_UUID_NUM_DIGITALS_DESCR: + case ESP_GATT_UUID_VALUE_TRIGGER_DESCR: + case ESP_GATT_UUID_ENV_SENSING_CONFIG_DESCR: + case ESP_GATT_UUID_ENV_SENSING_MEASUREMENT_DESCR: + case ESP_GATT_UUID_ENV_SENSING_TRIGGER_DESCR: + case ESP_GATT_UUID_TIME_TRIGGER_DESCR: { + uint16_t svc_hal = btc_creat_tab_env.svc_start_hdl; + tBT_UUID bta_char_uuid; + esp_bt_uuid_t uuid_temp; + tGATT_ATTR_VAL attr_val; + tBTA_GATT_PERM perm = gatts_attr_db[i].att_desc.perm; + tBTA_GATTS_ATTR_CONTROL control; + + if(svc_hal != 0){ + attr_val.attr_len = gatts_attr_db[i].att_desc.length; + attr_val.attr_max_len = gatts_attr_db[i].att_desc.max_length; + attr_val.attr_val = gatts_attr_db[i].att_desc.value; + btc_gatts_uuid_format_convert(&uuid_temp, gatts_attr_db[i].att_desc.uuid_length, + gatts_attr_db[i].att_desc.uuid_p); + btc_to_bta_uuid(&bta_char_uuid, &uuid_temp); + control.auto_rsp = gatts_attr_db[i].attr_control.auto_rsp; + BTA_GATTS_AddCharDescriptor(svc_hal, perm, &bta_char_uuid, &attr_val, &control); + + if (future_await(future_p) == FUTURE_FAIL) { + BTC_TRACE_ERROR("%s failed\n", __func__); + return; + } + } + break; + } + default: + future_free(future_p); + break; + } + + + } + + param.add_attr_tab.handles = btc_creat_tab_env.handles; + memcpy(¶m.add_attr_tab.svc_uuid, &btc_creat_tab_env.svc_uuid, sizeof(esp_bt_uuid_t)); + + param.add_attr_tab.svc_inst_id = srvc_inst_id; + + btc_gatts_cb_to_app(ESP_GATTS_CREAT_ATTR_TAB_EVT, gatts_if, ¶m); + //reset the env after sent the data to app + memset(&btc_creat_tab_env, 0, sizeof(esp_btc_creat_tab_t)); + + //set the flag value to false after the service is created. + btc_creat_tab_env.is_tab_creat_svc = false; +} + +static esp_gatt_status_t btc_gatts_check_valid_attr_tab(esp_gatts_attr_db_t *gatts_attr_db, + uint8_t max_nb_attr) +{ + uint8_t svc_num = 0; + uint16_t uuid = 0; + + for(int i = 0; i < max_nb_attr; i++) { + if(gatts_attr_db[i].att_desc.uuid_length != ESP_UUID_LEN_16) { + continue; + } + + uuid = (gatts_attr_db[i].att_desc.uuid_p[1] << 8) + (gatts_attr_db[i].att_desc.uuid_p[0]); + switch(uuid) { + case ESP_GATT_UUID_PRI_SERVICE: + case ESP_GATT_UUID_SEC_SERVICE: + if (++svc_num > 1) { + BTC_TRACE_ERROR("Each service table can only created one primary service or secondary service."); + return ESP_GATT_ERROR; + } + break; + case ESP_GATT_UUID_INCLUDE_SERVICE: { + esp_gatts_incl_svc_desc_t *svc_desc = (esp_gatts_incl_svc_desc_t *)gatts_attr_db[i].att_desc.value; + if(svc_desc == NULL) { + BTC_TRACE_ERROR("%s, The include service attribute should not be NULL.", __func__); + return ESP_GATT_INVALID_PDU; + } else if((svc_desc->start_hdl == 0) || (svc_desc->end_hdl == 0) || + (svc_desc->start_hdl == svc_desc->end_hdl)) { + BTC_TRACE_ERROR("%s, The include service attribute handle is invalid, start_hanlde = %d, end_handle = %d",\ + __func__, svc_desc->start_hdl, svc_desc->end_hdl); + return ESP_GATT_INVALID_HANDLE; + } + break; + } + case ESP_GATT_UUID_CHAR_DECLARE: + if((gatts_attr_db[i].att_desc.value) == NULL) { + BTC_TRACE_ERROR("%s, Characteristic declaration should not be NULL.", __func__); + return ESP_GATT_INVALID_PDU; + } + + if(gatts_attr_db[i+1].att_desc.uuid_length != ESP_UUID_LEN_16 && + gatts_attr_db[i+1].att_desc.uuid_length != ESP_UUID_LEN_32 && + gatts_attr_db[i+1].att_desc.uuid_length != ESP_UUID_LEN_128) { + BTC_TRACE_ERROR("%s, The Charateristic uuid length = %d is invalid", __func__,\ + gatts_attr_db[i+1].att_desc.uuid_length); + return ESP_GATT_INVALID_ATTR_LEN; + } + + if(gatts_attr_db[i+1].att_desc.uuid_length == ESP_UUID_LEN_16) { + uuid = (gatts_attr_db[i+1].att_desc.uuid_p[1] << 8) + (gatts_attr_db[i+1].att_desc.uuid_p[0]); + if(uuid == ESP_GATT_UUID_CHAR_DECLARE || uuid == ESP_GATT_UUID_CHAR_EXT_PROP || + uuid == ESP_GATT_UUID_CHAR_DESCRIPTION || uuid == ESP_GATT_UUID_CHAR_CLIENT_CONFIG || + uuid == ESP_GATT_UUID_CHAR_SRVR_CONFIG || uuid == ESP_GATT_UUID_CHAR_PRESENT_FORMAT || + uuid == ESP_GATT_UUID_CHAR_AGG_FORMAT || uuid == ESP_GATT_UUID_CHAR_VALID_RANGE || + uuid == ESP_GATT_UUID_EXT_RPT_REF_DESCR || uuid == ESP_GATT_UUID_RPT_REF_DESCR) { + BTC_TRACE_ERROR("%s, The charateristic value uuid = %d is invalid", __func__, uuid); + return ESP_GATT_INVALID_PDU; + } + } + break; + default: + break; + } + } + + return ESP_GATT_OK; +} + +esp_gatt_status_t btc_gatts_get_attr_value(uint16_t attr_handle, uint16_t *length, uint8_t **value) +{ + + return BTA_GetAttributeValue(attr_handle, length, value); +} + +esp_gatt_status_t btc_gatts_show_local_database(void) +{ + BTA_GATTS_ShowLocalDatabase(); + return ESP_GATT_OK; +} + +static void btc_gatts_cb_param_copy_req(btc_msg_t *msg, void *p_dest, void *p_src) +{ + uint16_t event = msg->act; + + tBTA_GATTS *p_dest_data = (tBTA_GATTS *) p_dest; + tBTA_GATTS *p_src_data = (tBTA_GATTS *) p_src; + + if (!p_src_data || !p_dest_data) { + return; + } + + // Copy basic structure first + memcpy(p_dest_data, p_src_data, sizeof(tBTA_GATTS)); + + // Allocate buffer for request data if necessary + switch (event) { + case BTA_GATTS_READ_EVT: + case BTA_GATTS_WRITE_EVT: + case BTA_GATTS_EXEC_WRITE_EVT: + case BTA_GATTS_MTU_EVT: + p_dest_data->req_data.p_data = osi_malloc(sizeof(tBTA_GATTS_REQ_DATA)); + if (p_dest_data->req_data.p_data != NULL) { + memcpy(p_dest_data->req_data.p_data, p_src_data->req_data.p_data, + sizeof(tBTA_GATTS_REQ_DATA)); + } else { + BTC_TRACE_ERROR("%s %d no mem\n", __func__, msg->act); + } + break; + + default: + break; + } +} + +static void btc_gatts_cb_param_copy_free(btc_msg_t *msg) +{ + uint16_t event = msg->act; + tBTA_GATTS *p_data = (tBTA_GATTS *)msg->arg; + + switch (event) { + case BTA_GATTS_READ_EVT: + case BTA_GATTS_WRITE_EVT: + case BTA_GATTS_EXEC_WRITE_EVT: + case BTA_GATTS_MTU_EVT: + if (p_data && p_data->req_data.p_data) { + osi_free(p_data->req_data.p_data); + } + break; + case BTA_GATTS_CONF_EVT: + break; + default: + break; + } +} + + +static void btc_gatts_inter_cb(tBTA_GATTS_EVT event, tBTA_GATTS *p_data) +{ + bt_status_t status; + btc_msg_t msg = {0}; + + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_GATTS; + msg.act = event; + if(btc_creat_tab_env.is_tab_creat_svc && btc_creat_tab_env.complete_future) { + switch(event) { + case BTA_GATTS_CREATE_EVT: { + //save the service handle to the btc module after used + //the attribute table method to creat a service + bta_to_btc_uuid(&btc_creat_tab_env.svc_uuid, &p_data->create.uuid); + uint16_t index = btc_creat_tab_env.handle_idx; + btc_creat_tab_env.svc_start_hdl = p_data->create.service_id; + btc_creat_tab_env.handles[index] = p_data->create.service_id; + break; + } + case BTA_GATTS_ADD_INCL_SRVC_EVT: { + uint16_t index = btc_creat_tab_env.handle_idx; + btc_creat_tab_env.handles[index] = p_data->add_result.attr_id; + break; + } + case BTA_GATTS_ADD_CHAR_EVT: { + uint16_t index = btc_creat_tab_env.handle_idx; + btc_creat_tab_env.handles[index] = p_data->add_result.attr_id - 1; + btc_creat_tab_env.handles[index+1] = p_data->add_result.attr_id; + break; + } + case BTA_GATTS_ADD_CHAR_DESCR_EVT: { + uint16_t index = btc_creat_tab_env.handle_idx; + btc_creat_tab_env.handles[index] = p_data->add_result.attr_id; + break; + } + default: + break; + } + + future_ready(btc_creat_tab_env.complete_future, FUTURE_SUCCESS); + return; + } + status = btc_transfer_context(&msg, p_data, sizeof(tBTA_GATTS), + btc_gatts_cb_param_copy_req, btc_gatts_cb_param_copy_free); + + if (status != BT_STATUS_SUCCESS) { + BTC_TRACE_ERROR("%s btc_transfer_context failed\n", __func__); + } +} + +void btc_gatts_call_handler(btc_msg_t *msg) +{ + btc_ble_gatts_args_t *arg = (btc_ble_gatts_args_t *)msg->arg; + + switch (msg->act) { + case BTC_GATTS_ACT_APP_REGISTER: { + tBT_UUID uuid; + + uuid.len = LEN_UUID_16; + uuid.uu.uuid16 = arg->app_reg.app_id; + + BTA_GATTS_AppRegister(&uuid, btc_gatts_inter_cb); + + break; + } + case BTC_GATTS_ACT_APP_UNREGISTER: + BTA_GATTS_AppDeregister(arg->app_unreg.gatts_if); + break; + case BTC_GATTS_ACT_CREATE_SERVICE: { + tBTA_GATT_SRVC_ID srvc_id; + btc_to_bta_srvc_id(&srvc_id, &arg->create_srvc.service_id); + BTA_GATTS_CreateService(arg->create_srvc.gatts_if, &srvc_id.id.uuid, + srvc_id.id.inst_id, arg->create_srvc.num_handle, + srvc_id.is_primary); + break; + } + case BTC_GATTS_ACT_CREATE_ATTR_TAB: + btc_gatts_act_create_attr_tab(arg->create_attr_tab.gatts_attr_db, + arg->create_attr_tab.gatts_if, + arg->create_attr_tab.max_nb_attr, + arg->create_attr_tab.srvc_inst_id); + break; + case BTC_GATTS_ACT_DELETE_SERVICE: + BTA_GATTS_DeleteService(arg->delete_srvc.service_handle); + break; + case BTC_GATTS_ACT_START_SERVICE: + BTA_GATTS_StartService(arg->start_srvc.service_handle, BTA_GATT_TRANSPORT_LE); + break; + case BTC_GATTS_ACT_STOP_SERVICE: + BTA_GATTS_StopService(arg->stop_srvc.service_handle); + break; + case BTC_GATTS_ACT_ADD_INCLUDE_SERVICE: + BTA_GATTS_AddIncludeService(arg->add_incl_srvc.service_handle, arg->add_incl_srvc.included_service_handle); + break; + case BTC_GATTS_ACT_ADD_CHAR: { + tBT_UUID uuid; + btc_to_bta_uuid(&uuid, &arg->add_char.char_uuid); + + BTA_GATTS_AddCharacteristic(arg->add_char.service_handle, &uuid, + arg->add_char.perm, arg->add_char.property, + (tGATT_ATTR_VAL *)&arg->add_char.char_val, + (tBTA_GATTS_ATTR_CONTROL *)&arg->add_char.attr_control); + break; + } + case BTC_GATTS_ACT_ADD_CHAR_DESCR: { + tBT_UUID uuid; + btc_to_bta_uuid(&uuid, &arg->add_descr.descr_uuid); + BTA_GATTS_AddCharDescriptor(arg->add_descr.service_handle, arg->add_descr.perm, &uuid, + (tBTA_GATT_ATTR_VAL *)&arg->add_descr.descr_val, + (tBTA_GATTS_ATTR_CONTROL *)&arg->add_descr.attr_control); + break; + } + case BTC_GATTS_ACT_SEND_INDICATE: + BTA_GATTS_HandleValueIndication(arg->send_ind.conn_id, arg->send_ind.attr_handle, + arg->send_ind.value_len, arg->send_ind.value, arg->send_ind.need_confirm); + break; + case BTC_GATTS_ACT_SEND_RESPONSE: { + esp_ble_gatts_cb_param_t param; + esp_gatt_rsp_t *p_rsp = arg->send_rsp.rsp; + + if (p_rsp) { + tBTA_GATTS_RSP rsp_struct; + btc_to_bta_response(&rsp_struct, p_rsp); + BTA_GATTS_SendRsp(arg->send_rsp.conn_id, arg->send_rsp.trans_id, + arg->send_rsp.status, &rsp_struct); + param.rsp.handle = rsp_struct.attr_value.handle; + } else { + BTA_GATTS_SendRsp(arg->send_rsp.conn_id, arg->send_rsp.trans_id, + arg->send_rsp.status, NULL); + } + + param.rsp.status = 0; + btc_gatts_cb_to_app(ESP_GATTS_RESPONSE_EVT, BTC_GATT_GET_GATT_IF(arg->send_rsp.conn_id), ¶m); + break; + } + case BTC_GATTS_ACT_SET_ATTR_VALUE: + BTA_SetAttributeValue(arg->set_attr_val.handle, arg->set_attr_val.length, + arg->set_attr_val.value); + break; + case BTC_GATTS_ACT_OPEN: { + // Ensure device is in inquiry database + tBTA_GATT_TRANSPORT transport = BTA_GATT_TRANSPORT_LE; + + //TODO : implement address type and device type +#if 0 + if (_get_address_type(arg->remote_bda, &addr_type) && + btif_get_device_type(arg->remote_bda, &device_type) && + device_type != BT_DEVICE_TYPE_BREDR) { + BTA_DmAddBleDevice(p_cb->bd_addr.address, addr_type, device_type); + } +#else + //BTA_DmAddBleDevice(p_cb->bd_addr.address, addr_type, device_type); +#endif + /* + not support background connection + // Mark background connections + if (!arg->open.is_direct) { + BTA_DmBleSetBgConnType(BTM_BLE_CONN_AUTO, NULL); + } + */ + + transport = BTA_GATT_TRANSPORT_LE; + + // Connect! + BTA_GATTS_Open(arg->open.gatts_if, arg->open.remote_bda, + arg->open.is_direct, transport); + break; + } + case BTC_GATTS_ACT_CLOSE: + // TODO : implement cancel open + // Cancel pending foreground/background connections + //BTA_GATTS_CancelOpen(p_cb->server_if, p_cb->bd_addr.address, TRUE); + //BTA_GATTS_CancelOpen(p_cb->server_if, p_cb->bd_addr.address, FALSE); + + // Close active connection + if (arg->close.conn_id != 0) { + BTA_GATTS_Close(arg->close.conn_id); + } + + break; + case BTC_GATTS_ACT_SEND_SERVICE_CHANGE: { + BD_ADDR remote_bda; + memcpy(remote_bda, arg->send_service_change.remote_bda, BD_ADDR_LEN); + BTA_GATTS_SendServiceChangeIndication(arg->send_service_change.gatts_if, remote_bda); + break; + } + case BTC_GATTS_ACT_SHOW_LOCAL_DATABASE: + BTA_GATTS_ShowLocalDatabase(); + break; + default: + break; + } + btc_gatts_arg_deep_free(msg); +} + +void btc_gatts_cb_handler(btc_msg_t *msg) +{ + esp_ble_gatts_cb_param_t param; + tBTA_GATTS *p_data = (tBTA_GATTS *)msg->arg; + esp_gatt_if_t gatts_if; + + switch (msg->act) { + case BTA_GATTS_REG_EVT: { + gatts_if = p_data->reg_oper.server_if; + param.reg.status = p_data->reg_oper.status; + param.reg.app_id = p_data->reg_oper.uuid.uu.uuid16; + + btc_gatts_cb_to_app(ESP_GATTS_REG_EVT, gatts_if, ¶m); + break; + } + case BTA_GATTS_DEREG_EVT: { + gatts_if = p_data->reg_oper.server_if; + btc_gatts_cb_to_app(ESP_GATTS_UNREG_EVT, gatts_if, NULL); + break; + } + case BTA_GATTS_READ_EVT: { + gatts_if = BTC_GATT_GET_GATT_IF(p_data->req_data.conn_id); + param.read.conn_id = BTC_GATT_GET_CONN_ID(p_data->req_data.conn_id); + param.read.trans_id = p_data->req_data.trans_id; + memcpy(param.read.bda, p_data->req_data.remote_bda, ESP_BD_ADDR_LEN); + param.read.handle = p_data->req_data.p_data->read_req.handle; + param.read.offset = p_data->req_data.p_data->read_req.offset; + param.read.is_long = p_data->req_data.p_data->read_req.is_long; + + param.read.need_rsp = p_data->req_data.p_data->read_req.need_rsp; + btc_gatts_cb_to_app(ESP_GATTS_READ_EVT, gatts_if, ¶m); + break; + } + case BTA_GATTS_WRITE_EVT: { + gatts_if = BTC_GATT_GET_GATT_IF(p_data->req_data.conn_id); + param.write.conn_id = BTC_GATT_GET_CONN_ID(p_data->req_data.conn_id); + param.write.trans_id = p_data->req_data.trans_id; + memcpy(param.write.bda, p_data->req_data.remote_bda, ESP_BD_ADDR_LEN); + if (p_data->req_data.p_data == NULL) { + break; + } + param.write.handle = p_data->req_data.p_data->write_req.handle; + param.write.offset = p_data->req_data.p_data->write_req.offset; + param.write.need_rsp = p_data->req_data.p_data->write_req.need_rsp; + param.write.is_prep = p_data->req_data.p_data->write_req.is_prep; + param.write.len = p_data->req_data.p_data->write_req.len; + param.write.value = p_data->req_data.p_data->write_req.value; + + btc_gatts_cb_to_app(ESP_GATTS_WRITE_EVT, gatts_if, ¶m); + + break; + } + case BTA_GATTS_EXEC_WRITE_EVT: { + gatts_if = BTC_GATT_GET_GATT_IF(p_data->req_data.conn_id); + param.exec_write.conn_id = BTC_GATT_GET_CONN_ID(p_data->req_data.conn_id); + param.exec_write.trans_id = p_data->req_data.trans_id; + memcpy(param.exec_write.bda, p_data->req_data.remote_bda, ESP_BD_ADDR_LEN); + if (p_data->req_data.p_data == NULL) { + break; + } + param.exec_write.exec_write_flag = p_data->req_data.p_data->exec_write; + + btc_gatts_cb_to_app(ESP_GATTS_EXEC_WRITE_EVT, gatts_if, ¶m); + break; + } + case BTA_GATTS_MTU_EVT: + gatts_if = BTC_GATT_GET_GATT_IF(p_data->req_data.conn_id); + param.mtu.conn_id = BTC_GATT_GET_CONN_ID(p_data->req_data.conn_id); + param.mtu.mtu = p_data->req_data.p_data->mtu; + + btc_gatts_cb_to_app(ESP_GATTS_MTU_EVT, gatts_if, ¶m); + break; + case BTA_GATTS_CONF_EVT: + gatts_if = BTC_GATT_GET_GATT_IF(p_data->req_data.conn_id); + param.conf.conn_id = BTC_GATT_GET_CONN_ID(p_data->req_data.conn_id); + param.conf.status = p_data->req_data.status; + param.conf.handle = p_data->req_data.handle; + + if (p_data->req_data.status != ESP_GATT_OK && p_data->req_data.value){ + param.conf.len = p_data->req_data.data_len; + param.conf.value = p_data->req_data.value; + }else{ + param.conf.len = 0; + } + btc_gatts_cb_to_app(ESP_GATTS_CONF_EVT, gatts_if, ¶m); + break; + case BTA_GATTS_CREATE_EVT: + gatts_if = p_data->create.server_if; + param.create.status = p_data->create.status; + param.create.service_handle = p_data->create.service_id; + param.create.service_id.is_primary = p_data->create.is_primary; + param.create.service_id.id.inst_id = p_data->create.svc_instance; + bta_to_btc_uuid(¶m.create.service_id.id.uuid, &p_data->create.uuid); + + btc_gatts_cb_to_app(ESP_GATTS_CREATE_EVT, gatts_if, ¶m); + break; + case BTA_GATTS_ADD_INCL_SRVC_EVT: + gatts_if = p_data->add_result.server_if; + param.add_incl_srvc.status = p_data->add_result.status; + param.add_incl_srvc.attr_handle = p_data->add_result.attr_id; + param.add_incl_srvc.service_handle = p_data->add_result.service_id; + + btc_gatts_cb_to_app(ESP_GATTS_ADD_INCL_SRVC_EVT, gatts_if, ¶m); + break; + case BTA_GATTS_ADD_CHAR_EVT: + gatts_if = p_data->add_result.server_if; + param.add_char.status = p_data->add_result.status; + param.add_char.attr_handle = p_data->add_result.attr_id; + param.add_char.service_handle = p_data->add_result.service_id; + bta_to_btc_uuid(¶m.add_char.char_uuid, &p_data->add_result.char_uuid); + + btc_gatts_cb_to_app(ESP_GATTS_ADD_CHAR_EVT, gatts_if, ¶m); + break; + case BTA_GATTS_ADD_CHAR_DESCR_EVT: + gatts_if = p_data->add_result.server_if; + param.add_char_descr.status = p_data->add_result.status; + param.add_char_descr.attr_handle = p_data->add_result.attr_id; + param.add_char_descr.service_handle = p_data->add_result.service_id; + bta_to_btc_uuid(¶m.add_char_descr.descr_uuid, &p_data->add_result.char_uuid); + + btc_gatts_cb_to_app(ESP_GATTS_ADD_CHAR_DESCR_EVT, gatts_if, ¶m); + break; + case BTA_GATTS_DELELTE_EVT: + gatts_if = p_data->srvc_oper.server_if; + param.del.status = p_data->srvc_oper.status; + param.del.service_handle = p_data->srvc_oper.service_id; + + btc_gatts_cb_to_app(ESP_GATTS_DELETE_EVT, gatts_if, ¶m); + break; + case BTA_GATTS_START_EVT: + gatts_if = p_data->srvc_oper.server_if; + param.start.status = p_data->srvc_oper.status; + param.start.service_handle = p_data->srvc_oper.service_id; + + btc_gatts_cb_to_app(ESP_GATTS_START_EVT, gatts_if, ¶m); + break; + case BTA_GATTS_STOP_EVT: + gatts_if = p_data->srvc_oper.server_if; + param.stop.status = p_data->srvc_oper.status; + param.stop.service_handle = p_data->srvc_oper.service_id; + + btc_gatts_cb_to_app(ESP_GATTS_STOP_EVT, gatts_if, ¶m); + break; + case BTA_GATTS_CONNECT_EVT: { +#if (SMP_INCLUDED == TRUE) + bt_bdaddr_t bt_addr; + memcpy(bt_addr.address, p_data->conn.remote_bda, sizeof(bt_addr.address)); + if (btc_storage_update_active_device(&bt_addr)) { + BTC_TRACE_EVENT("Device: %02x:%02x:%02x:%02x:%02x:%02x, is not in bond list", + bt_addr.address[0], bt_addr.address[1], + bt_addr.address[2], bt_addr.address[3], + bt_addr.address[4], bt_addr.address[5]); + } +#endif ///SMP_INCLUDED == TRUE + gatts_if = p_data->conn.server_if; + param.connect.conn_id = BTC_GATT_GET_CONN_ID(p_data->conn.conn_id); + param.connect.link_role = p_data->conn.link_role; + memcpy(param.connect.remote_bda, p_data->conn.remote_bda, ESP_BD_ADDR_LEN); + param.connect.conn_params.interval = p_data->conn.conn_params.interval; + param.connect.conn_params.latency = p_data->conn.conn_params.latency; + param.connect.conn_params.timeout = p_data->conn.conn_params.timeout; + param.connect.ble_addr_type = p_data->conn.ble_addr_type; + param.connect.conn_handle = p_data->conn.conn_handle; + btc_gatts_cb_to_app(ESP_GATTS_CONNECT_EVT, gatts_if, ¶m); + break; + } + case BTA_GATTS_DISCONNECT_EVT: + gatts_if = p_data->conn.server_if; + param.disconnect.conn_id = BTC_GATT_GET_CONN_ID(p_data->conn.conn_id); + param.disconnect.reason = p_data->conn.reason; + memcpy(param.disconnect.remote_bda, p_data->conn.remote_bda, ESP_BD_ADDR_LEN); + + btc_gatts_cb_to_app(ESP_GATTS_DISCONNECT_EVT, gatts_if, ¶m); + break; + case BTA_GATTS_OPEN_EVT: + gatts_if = p_data->open.server_if; + param.open.status = p_data->open.status; + + btc_gatts_cb_to_app(BTA_GATTS_OPEN_EVT, gatts_if, ¶m); + break; + case BTA_GATTS_CANCEL_OPEN_EVT: + gatts_if = p_data->cancel_open.server_if; + param.cancel_open.status = p_data->cancel_open.status; + + btc_gatts_cb_to_app(BTA_GATTS_CANCEL_OPEN_EVT, gatts_if, ¶m); + break; + + case BTA_GATTS_CLOSE_EVT: + gatts_if = BTC_GATT_GET_GATT_IF(p_data->close.conn_id); + param.close.status = p_data->close.status; + param.close.conn_id = BTC_GATT_GET_CONN_ID(p_data->close.conn_id); + + btc_gatts_cb_to_app(BTA_GATTS_CLOSE_EVT, gatts_if, ¶m); + break; + case BTA_GATTS_SEND_SERVICE_CHANGE_EVT: + gatts_if = p_data->service_change.server_if; + param.service_change.status = p_data->service_change.status; + btc_gatts_cb_to_app(ESP_GATTS_SEND_SERVICE_CHANGE_EVT, gatts_if, ¶m); + break; + case BTA_GATTS_LISTEN_EVT: + // do nothing + break; + case BTA_GATTS_CONGEST_EVT: + gatts_if = BTC_GATT_GET_GATT_IF(p_data->congest.conn_id); + param.congest.conn_id = BTC_GATT_GET_CONN_ID(p_data->congest.conn_id); + param.congest.congested = p_data->congest.congested; + btc_gatts_cb_to_app(ESP_GATTS_CONGEST_EVT, gatts_if, ¶m); + break; + case BTA_GATTS_SET_ATTR_VAL_EVT: + gatts_if = p_data->attr_val.server_if; + param.set_attr_val.srvc_handle = p_data->attr_val.service_id; + param.set_attr_val.attr_handle = p_data->attr_val.attr_id; + param.set_attr_val.status = p_data->attr_val.status; + btc_gatts_cb_to_app(ESP_GATTS_SET_ATTR_VAL_EVT, gatts_if, ¶m); + break; + default: + // do nothing + break; + } + + btc_gatts_cb_param_copy_free(msg); +} + +void btc_congest_callback(tBTA_GATTS *param) +{ + esp_ble_gatts_cb_param_t esp_param; + esp_gatt_if_t gatts_if = BTC_GATT_GET_GATT_IF(param->congest.conn_id); + esp_param.congest.conn_id = BTC_GATT_GET_CONN_ID(param->congest.conn_id); + esp_param.congest.congested = param->congest.congested; + btc_gatts_cb_to_app(ESP_GATTS_CONGEST_EVT, gatts_if, &esp_param); +} + +#endif ///GATTS_INCLUDED diff --git a/lib/bt/host/bluedroid/btc/profile/std/hf_ag/bta_ag_co.c b/lib/bt/host/bluedroid/btc/profile/std/hf_ag/bta_ag_co.c new file mode 100644 index 00000000..6000f832 --- /dev/null +++ b/lib/bt/host/bluedroid/btc/profile/std/hf_ag/bta_ag_co.c @@ -0,0 +1,590 @@ +/****************************************************************************** + * + * Copyright (C) 2009-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +#define LOG_TAG "bt_btc_bta_ag" + +#include "btc_hf_ag.h" +#include "bta_ag_int.h" +#include "bta/bta_api.h" +#include "bta/bta_ag_api.h" +#include "bta/bta_ag_co.h" +#include "bta/bta_dm_co.h" +#include "common/bt_target.h" +#include "hci/hci_audio.h" +#include "osi/allocator.h" +#include + +#if (BTA_AG_INCLUDED == TRUE) + +/******************************************************************************* + * CONST +********************************************************************************/ +#if (BTM_SCO_HCI_INCLUDED == TRUE) +#include "oi_codec_sbc.h" +#include "oi_status.h" +#include "sbc_encoder.h" + +#if (PLC_INCLUDED == TRUE) +#include "sbc_plc.h" +typedef struct { + bool first_good_frame_found; + sbc_plc_state_t plc_state; + int16_t sbc_plc_out[SBC_FS]; +} bta_hf_ct_plc_t; +#if HFP_DYNAMIC_MEMORY == FALSE +static bta_hf_ct_plc_t bta_hf_ct_plc; +#else +static bta_hf_ct_plc_t *bta_hf_ct_plc_ptr; +#define bta_hf_ct_plc (*bta_hf_ct_plc_ptr) +#endif ///HFP_DYNAMIC_MEMORY == FALSE +#endif ///(PLC_INCLUDED == TRUE) + +#define HF_SBC_DEC_CONTEXT_DATA_LEN (CODEC_DATA_WORDS(1, SBC_CODEC_FAST_FILTER_BUFFERS)) +#define HF_SBC_DEC_RAW_DATA_SIZE 240 +#define HF_SBC_ENC_RAW_DATA_SIZE 240 + +/* BTA-AG-CO control block to map bdaddr to BTA handle */ +typedef struct +{ + OI_CODEC_SBC_DECODER_CONTEXT decoder_context; + OI_UINT32 decoder_context_data[HF_SBC_DEC_CONTEXT_DATA_LEN]; + OI_INT16 decode_raw_data[HF_SBC_DEC_RAW_DATA_SIZE]; + + SBC_ENC_PARAMS encoder; + + UINT8 sequence_number; + bool is_bad_frame; + bool decode_first_pkt; + OI_BYTE decode_msbc_data[BTM_MSBC_FRAME_SIZE]; + bool encode_first_pkt; + OI_BYTE encode_msbc_data[BTM_MSBC_FRAME_SIZE]; +} bta_ag_co_cb_t; + +#if HFP_DYNAMIC_MEMORY == FALSE +static bta_ag_co_cb_t bta_ag_co_cb; +#else +static bta_ag_co_cb_t *bta_ag_co_cb_ptr; +#define bta_ag_co_cb (*bta_ag_co_cb_ptr) +#endif /* HFP_DYNAMIC_MEMORY == FALSE */ + +static UINT8 hf_air_mode = BTM_SCO_AIR_MODE_UNKNOWN; +static UINT8 hf_inout_pkt_size = 0; + +/* ========================================================================= +* AG pass-through mode handle +*===========================================================================*/ +/******************************************************************************* + ** + ** Function bta_ag_co_tx_write + ** + ** Description This function is called by the AG to send data to the + ** phone when the AG is configured for AT command pass-through. + ** The implementation of this function must copy the data to + ** the phones memory. + ** + ** Returns void + ** + *******************************************************************************/ +void bta_ag_co_tx_write(UINT16 handle, UNUSED_ATTR UINT8 * p_data, UINT16 len) +{ + BTIF_TRACE_DEBUG( "bta_ag_co_tx_write: handle: %d, len: %d", handle, len ); +} + +/****************************************************************************** +** +** Function bta_ag_ci_rx_write +** +** Description This function is called to send data to the AG when the AG +** is configured for AT command pass-through. The function +** copies data to an event buffer and sends it. +** +** Returns void +** +******************************************************************************/ +void bta_ag_ci_rx_write(UINT16 handle, char *p_data, UINT16 len) +{ + tBTA_AG_CI_RX_WRITE *p_buf; + UINT16 len_remaining = len; + char *p_data_area; + + if (len > (BT_DEFAULT_BUFFER_SIZE - sizeof(tBTA_AG_CI_RX_WRITE) - 1)) { + len = BT_DEFAULT_BUFFER_SIZE - sizeof(tBTA_AG_CI_RX_WRITE) - 1; + } + + while (len_remaining) { + if (len_remaining < len) { + len = len_remaining; + } + if ((p_buf = (tBTA_AG_CI_RX_WRITE *) osi_malloc((UINT16)(sizeof(tBTA_AG_CI_RX_WRITE) + len + 1))) != NULL) { + p_buf->hdr.event = BTA_AG_CI_RX_WRITE_EVT; + p_buf->hdr.layer_specific = handle; + p_data_area = (char *)(p_buf+1); /* Point to data area after header */ + strncpy(p_data_area, p_data, len); + p_data_area[len] = 0; + bta_sys_sendmsg(p_buf); + } else { + APPL_TRACE_ERROR("ERROR: Unable to allocate buffer to hold AT response code. len=%i", len); + break; + } + len_remaining-=len; + p_data+=len; + } +} + +/****************************************************************************** +** +** Function bta_ag_ci_slc_ready +** +** Description This function is called to notify AG that SLC is up at +** the application. This funcion is only used when the app +** is running in pass-through mode. +** +** Returns void +** +******************************************************************************/ +void bta_ag_ci_slc_ready(UINT16 handle) +{ + tBTA_AG_DATA *p_buf; + if ((p_buf = (tBTA_AG_DATA *)osi_malloc(sizeof(tBTA_AG_DATA))) != NULL) { + p_buf->hdr.event = BTA_AG_CI_SLC_READY_EVT; + p_buf->hdr.layer_specific = handle; + bta_sys_sendmsg(p_buf); + } +} + +/******************************************************************************* + * H2 & DEC +********************************************************************************/ +/******************************************************************************* +** +** Function bta_ag_h2_header +** +** Description This function is called to fill in H2 header +** +** Returns void +** +*******************************************************************************/ +static void bta_ag_h2_header(UINT16 *p_buf) +{ + // H2: Header with synchronization word and sequence number +#define BTA_HF_H2_HEADER 0x0801 +#define BTA_HF_H2_HEADER_BIT0_MASK (1 << 0) +#define BTA_HF_H2_HEADER_BIT1_MASK (1 << 1) +#define BTA_HF_H2_HEADER_SN0_BIT_OFFSET1 12 +#define BTA_HF_H2_HEADER_SN0_BIT_OFFSET2 13 +#define BTA_HF_H2_HEADER_SN1_BIT_OFFSET1 14 +#define BTA_HF_H2_HEADER_SN1_BIT_OFFSET2 15 + + UINT16 h2_header = BTA_HF_H2_HEADER; + UINT8 h2_header_sn0 = bta_ag_co_cb.sequence_number & BTA_HF_H2_HEADER_BIT0_MASK; + UINT8 h2_header_sn1 = bta_ag_co_cb.sequence_number & BTA_HF_H2_HEADER_BIT1_MASK; + h2_header |= (h2_header_sn0 << BTA_HF_H2_HEADER_SN0_BIT_OFFSET1 + | h2_header_sn0 << BTA_HF_H2_HEADER_SN0_BIT_OFFSET2 + | h2_header_sn1 << (BTA_HF_H2_HEADER_SN1_BIT_OFFSET1 - 1) + | h2_header_sn1 << (BTA_HF_H2_HEADER_SN1_BIT_OFFSET2 - 1) + ); + bta_ag_co_cb.sequence_number++; + *p_buf = h2_header; +} + +/******************************************************************************* + ** + ** Function bta_hf_dec_init + ** + ** Description Initialize decoding task + ** + ** Returns void + ** + *******************************************************************************/ +static void bta_hf_dec_init(void) +{ +#if (PLC_INCLUDED == TRUE) + sbc_plc_init(&(bta_hf_ct_plc.plc_state)); +#endif ///(PLC_INCLUDED == TRUE) + + OI_STATUS status = OI_CODEC_SBC_DecoderReset(&bta_ag_co_cb.decoder_context, bta_ag_co_cb.decoder_context_data, + HF_SBC_DEC_CONTEXT_DATA_LEN * sizeof(OI_UINT32), 1, 1, FALSE, TRUE); + if (!OI_SUCCESS(status)) { + APPL_TRACE_ERROR("OI_CODEC_SBC_DecoderReset failed with error code %d\n", status); + } +} + +/******************************************************************************* + ** + ** Function bta_hf_enc_init + ** + ** Description Initialize encoding task for mSBC + ** + ** Returns void + ** + *******************************************************************************/ +static void bta_hf_enc_init(void) +{ + bta_ag_co_cb.sequence_number = 0; + bta_ag_co_cb.decode_first_pkt = true; + bta_ag_co_cb.encode_first_pkt = true; + bta_ag_co_cb.is_bad_frame = false; + + bta_ag_co_cb.encoder.sbc_mode = SBC_MODE_MSBC; + bta_ag_co_cb.encoder.s16NumOfBlocks = 15; + bta_ag_co_cb.encoder.s16NumOfSubBands = 8; + bta_ag_co_cb.encoder.s16AllocationMethod = SBC_LOUDNESS; + bta_ag_co_cb.encoder.s16BitPool = 26; + bta_ag_co_cb.encoder.s16ChannelMode = SBC_MONO; + bta_ag_co_cb.encoder.s16NumOfChannels = 1; + bta_ag_co_cb.encoder.s16SamplingFreq = SBC_sf16000; + + SBC_Encoder_Init(&(bta_ag_co_cb.encoder)); +} + +/******************************************************************************* +** +** Function bta_ag_decode_msbc_frame +** +** Description This function is called decode a mSBC frame +** +** Returns void +** +*******************************************************************************/ +static void bta_ag_decode_msbc_frame(UINT8 **data, UINT8 *length, BOOLEAN is_bad_frame) +{ + OI_STATUS status; + const OI_BYTE *zero_signal_frame_data; + UINT8 zero_signal_frame_len = BTM_MSBC_FRAME_DATA_SIZE; + UINT32 sbc_raw_data_size = HF_SBC_DEC_RAW_DATA_SIZE; + + if (is_bad_frame) { + status = OI_CODEC_SBC_CHECKSUM_MISMATCH; + } else { + status = OI_CODEC_SBC_DecodeFrame(&bta_ag_co_cb.decoder_context, (const OI_BYTE **)data, + (OI_UINT32 *)length, + (OI_INT16 *)bta_ag_co_cb.decode_raw_data, + (OI_UINT32 *)&sbc_raw_data_size); + } + +// PLC_INCLUDED will be set to TRUE when enabling Wide Band Speech +#if (PLC_INCLUDED == TRUE) + switch(status) { + case OI_OK: + { + bta_hf_ct_plc.first_good_frame_found = TRUE; + sbc_plc_good_frame(&(bta_hf_ct_plc.plc_state), (int16_t *)bta_ag_co_cb.decode_raw_data, bta_hf_ct_plc.sbc_plc_out); + } + + case OI_CODEC_SBC_NOT_ENOUGH_HEADER_DATA: + case OI_CODEC_SBC_NOT_ENOUGH_BODY_DATA: + case OI_CODEC_SBC_NOT_ENOUGH_AUDIO_DATA: + break; + + case OI_CODEC_SBC_NO_SYNCWORD: + case OI_CODEC_SBC_CHECKSUM_MISMATCH: + { + if (!bta_hf_ct_plc.first_good_frame_found) { + break; + } + zero_signal_frame_data = sbc_plc_zero_signal_frame(); + sbc_raw_data_size = HF_SBC_DEC_RAW_DATA_SIZE; + status = OI_CODEC_SBC_DecodeFrame(&bta_ag_co_cb.decoder_context, &zero_signal_frame_data, + (OI_UINT32 *)&zero_signal_frame_len, + (OI_INT16 *)bta_ag_co_cb.decode_raw_data, + (OI_UINT32 *)&sbc_raw_data_size); + sbc_plc_bad_frame(&(bta_hf_ct_plc.plc_state), bta_ag_co_cb.decode_raw_data, bta_hf_ct_plc.sbc_plc_out); + APPL_TRACE_DEBUG("bad frame, using PLC to fix it."); + break; + } + + case OI_STATUS_INVALID_PARAMETERS: + { + // This caused by corrupt frames. + // The codec apparently does not recover from this. + // Re-initialize the codec. + APPL_TRACE_ERROR("Frame decode error: OI_STATUS_INVALID_PARAMETERS"); + + if (!OI_SUCCESS(OI_CODEC_SBC_DecoderReset(&bta_ag_co_cb.decoder_context, bta_ag_co_cb.decoder_context_data, + HF_SBC_DEC_CONTEXT_DATA_LEN * sizeof(OI_UINT32), 1, 1, FALSE, TRUE))) { + APPL_TRACE_ERROR("OI_CODEC_SBC_DecoderReset failed with error code %d\n", status); + } + break; + } + + default: + APPL_TRACE_ERROR("Frame decode error: %d", status); + break; + } +#endif ///(PLC_INCLUDED == TRUE) + + if (OI_SUCCESS(status)) { + btc_hf_incoming_data_cb_to_app((const uint8_t *)(bta_hf_ct_plc.sbc_plc_out), sbc_raw_data_size); + } +} + +/******************************************************************************* + * BTA AG SCO CO FUNCITONS +********************************************************************************/ +/******************************************************************************* +** +** Function bta_ag_sco_audio_state +** +** Description This function is called by the AG before the audio connection +** is brought up, after it comes up, and after it goes down. +** +** Parameters handle - handle of the AG instance +** state - Audio state +** codec - if WBS support is compiled in, codec to going to be used is provided +** and when in SCO_STATE_SETUP, BTM_I2SPCMConfig() must be called with +** the correct platform parameters. +** in the other states codec type should not be ignored +** +** Returns void +** +*******************************************************************************/ +#if (BTM_WBS_INCLUDED == TRUE) +void bta_ag_sco_audio_state(UINT16 handle, UINT8 app_id, UINT8 state, tBTA_AG_PEER_CODEC codec) +#else +void bta_ag_sco_audio_state(UINT16 handle, UINT8 app_id, UINT8 state) +#endif +{ + BTIF_TRACE_DEBUG("bta_ag_sco_audio_state: handle %d, state %d", handle, state); + switch (state) { + case SCO_STATE_ON: + case SCO_STATE_OFF: + case SCO_STATE_OFF_TRANSFER: + case SCO_STATE_SETUP: + default: + break; + } +} + +/******************************************************************************* +** +** Function bta_ag_sco_co_init +** +** Description Set default data path for SCO/eSCO. +** +** +** Returns Void. +** +*******************************************************************************/ +tBTA_HFP_SCO_ROUTE_TYPE bta_ag_sco_co_init(UINT32 rx_bw, UINT32 tx_bw, tBTA_HFP_CODEC_INFO *p_codec_info, UINT8 app_id) +{ + APPL_TRACE_EVENT("%s rx_bw %d, tx_bw %d, codec %d", __FUNCTION__, rx_bw, tx_bw, p_codec_info->codec_type); + return BTA_HFP_SCO_ROUTE_HCI; +} + +/******************************************************************************* +** +** Function bta_ag_sco_co_open +** +** Description This function is executed by AG when a service level connection +** is opened. +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_sco_co_open(UINT16 handle, tBTM_SCO_AIR_MODE_TYPE air_mode, UINT8 inout_pkt_size, UINT16 event) +{ + APPL_TRACE_EVENT("%s hdl %x, pkt_sz %u, event %u", __FUNCTION__, handle, inout_pkt_size, event); + hf_air_mode = air_mode; + hf_inout_pkt_size = inout_pkt_size; + + if (air_mode == BTM_SCO_AIR_MODE_TRANSPNT) { +#if (HFP_DYNAMIC_MEMORY == TRUE) + bta_ag_co_cb_ptr = osi_calloc(sizeof(bta_ag_co_cb_t)); + if (!bta_ag_co_cb_ptr) { + APPL_TRACE_ERROR("%s allocate failed", __FUNCTION__); + goto error_exit; + } +#if (PLC_INCLUDED == TRUE) + bta_hf_ct_plc_ptr = (bta_hf_ct_plc_t *)osi_calloc(sizeof(bta_hf_ct_plc_t)); + if (!bta_hf_ct_plc_ptr) { + APPL_TRACE_ERROR("%s malloc fail.", __FUNCTION__); + goto error_exit; + } +#endif ///(PLC_INCLUDED == TRUE) +#endif /// (HFP_DYNAMIC_MEMORY == TRUE) + bta_hf_dec_init(); + bta_hf_enc_init(); + return; + } else { + return; // Nothing to do + } + +#if (HFP_DYNAMIC_MEMORY == TRUE) +error_exit:; + hf_air_mode = BTM_SCO_AIR_MODE_UNKNOWN; + hf_inout_pkt_size = 0; + if (bta_ag_co_cb_ptr) { + osi_free(bta_ag_co_cb_ptr); + bta_ag_co_cb_ptr = NULL; + } +#if (PLC_INCLUDED == TRUE) + if (bta_hf_ct_plc_ptr) { + osi_free(bta_hf_ct_plc_ptr); + bta_hf_ct_plc_ptr = NULL; + } +#endif ///(PLC_INCLUDED == TRUE) +#endif /// (HFP_DYNAMIC_MEMORY == TRUE) + return; +} + +/******************************************************************************* +** +** Function bta_ag_sco_co_close +** +** Description Nothing but print some log. +** +** Returns void +** +*******************************************************************************/ +void bta_ag_sco_co_close(void) +{ + APPL_TRACE_EVENT("%s", __FUNCTION__); + if (hf_air_mode == BTM_SCO_AIR_MODE_TRANSPNT) { +#if (PLC_INCLUDED == TRUE) + sbc_plc_deinit(&(bta_hf_ct_plc.plc_state)); + bta_hf_ct_plc.first_good_frame_found = FALSE; +#if (HFP_DYNAMIC_MEMORY == TRUE) + osi_free(bta_hf_ct_plc_ptr); + bta_hf_ct_plc_ptr = NULL; +#endif /// (HFP_DYNAMIC_MEMORY == TRUE) +#endif ///(PLC_INCLUDED == TRUE) + +#if (HFP_DYNAMIC_MEMORY == TRUE) + osi_free(bta_ag_co_cb_ptr); + bta_ag_co_cb_ptr = NULL; +#endif /* HFP_DYNAMIC_MEMORY == TRUE */ + } else { + // Nothing to do + } + hf_air_mode = BTM_SCO_AIR_MODE_UNKNOWN; + hf_inout_pkt_size = 0; +} + +/******************************************************************************* +** +** Function bta_ag_sco_co_out_data +** +** Description This function is called to send SCO data over HCI. +** +** Returns number of bytes got from application +** +*******************************************************************************/ +uint32_t bta_ag_sco_co_out_data(UINT8 *p_buf) +{ + if (hf_air_mode == BTM_SCO_AIR_MODE_CVSD) { + // CVSD + uint32_t hf_raw_pkt_size = hf_inout_pkt_size; + return btc_hf_outgoing_data_cb_to_app(p_buf, hf_raw_pkt_size); + } else if (hf_air_mode == BTM_SCO_AIR_MODE_TRANSPNT) { + // mSBC + if (hf_inout_pkt_size == BTM_MSBC_FRAME_SIZE / 2) { + if (bta_ag_co_cb.encode_first_pkt) { + UINT32 size = btc_hf_outgoing_data_cb_to_app((UINT8 *)bta_ag_co_cb.encoder.as16PcmBuffer, HF_SBC_ENC_RAW_DATA_SIZE); + if (size != HF_SBC_ENC_RAW_DATA_SIZE) { + return 0; + } + bta_ag_h2_header((UINT16 *)bta_ag_co_cb.encode_msbc_data); + bta_ag_co_cb.encoder.pu8Packet = bta_ag_co_cb.encode_msbc_data + 2; + + SBC_Encoder(&bta_ag_co_cb.encoder); + memcpy(p_buf, bta_ag_co_cb.encode_msbc_data, hf_inout_pkt_size); + bta_ag_co_cb.encode_first_pkt = !bta_ag_co_cb.encode_first_pkt; + return hf_inout_pkt_size; + } else { + memcpy(p_buf, bta_ag_co_cb.encode_msbc_data + hf_inout_pkt_size, hf_inout_pkt_size); + bta_ag_co_cb.encode_first_pkt = !bta_ag_co_cb.encode_first_pkt; + return hf_inout_pkt_size; + } + } else if (hf_inout_pkt_size == BTM_MSBC_FRAME_SIZE) { + UINT32 size = btc_hf_outgoing_data_cb_to_app((UINT8 *)bta_ag_co_cb.encoder.as16PcmBuffer, HF_SBC_ENC_RAW_DATA_SIZE); + if (size != HF_SBC_ENC_RAW_DATA_SIZE) { + return 0; + } + bta_ag_h2_header((UINT16 *)p_buf); + bta_ag_co_cb.encoder.pu8Packet = p_buf + 2; + + SBC_Encoder(&bta_ag_co_cb.encoder); + return hf_inout_pkt_size; + } else { + //Never run to here. + } + } else { + APPL_TRACE_ERROR("%s invaild air mode: %d", __FUNCTION__, hf_air_mode); + } + return 0; +} + +/******************************************************************************* +** +** Function bta_ag_sco_co_in_data +** +** Description This function is called to send incoming SCO data to application. +** +** Returns void +** +*******************************************************************************/ +void bta_ag_sco_co_in_data(BT_HDR *p_buf, tBTM_SCO_DATA_FLAG status) +{ + UINT8 *p = (UINT8 *)(p_buf + 1) + p_buf->offset; + UINT8 pkt_size = 0; + STREAM_SKIP_UINT16(p); + STREAM_TO_UINT8(pkt_size, p); + + if (hf_air_mode == BTM_SCO_AIR_MODE_CVSD) { + // CVSD + if(status != BTM_SCO_DATA_CORRECT) { + APPL_TRACE_DEBUG("%s: not a correct frame(%d).", __func__, status); + } + btc_hf_incoming_data_cb_to_app(p, pkt_size); + } else if (hf_air_mode == BTM_SCO_AIR_MODE_TRANSPNT) { + // mSBC + UINT8 *data = NULL; + if (pkt_size != hf_inout_pkt_size) { + bta_ag_co_cb.is_bad_frame = true; + } + if (status != BTM_SCO_DATA_CORRECT) { + bta_ag_co_cb.is_bad_frame = true; + } + if (hf_inout_pkt_size == BTM_MSBC_FRAME_SIZE / 2) { + if (bta_ag_co_cb.decode_first_pkt) { + if (!bta_ag_co_cb.is_bad_frame) { + memcpy(bta_ag_co_cb.decode_msbc_data, p, pkt_size); + } + } else { + if (!bta_ag_co_cb.is_bad_frame) { + memcpy(bta_ag_co_cb.decode_msbc_data + BTM_MSBC_FRAME_SIZE / 2, p, pkt_size); + } + data = bta_ag_co_cb.decode_msbc_data; + bta_ag_decode_msbc_frame(&data, &pkt_size, bta_ag_co_cb.is_bad_frame); + bta_ag_co_cb.is_bad_frame = false; + } + bta_ag_co_cb.decode_first_pkt = !bta_ag_co_cb.decode_first_pkt; + } else if (hf_inout_pkt_size == BTM_MSBC_FRAME_SIZE) { + data = p; + bta_ag_decode_msbc_frame(&data, &pkt_size, bta_ag_co_cb.is_bad_frame); + bta_ag_co_cb.is_bad_frame = false; + } else { + //Never run to here. + } + } else { + APPL_TRACE_ERROR("%s invaild air mode: %d", __FUNCTION__, hf_air_mode); + } +} +#endif /* #if (BTM_SCO_HCI_INCLUDED == TRUE) */ +#endif /* #if (BTA_AG_INCLUDED == TRUE) */ diff --git a/lib/bt/host/bluedroid/btc/profile/std/hf_ag/btc_hf_ag.c b/lib/bt/host/bluedroid/btc/profile/std/hf_ag/btc_hf_ag.c new file mode 100644 index 00000000..129d1b17 --- /dev/null +++ b/lib/bt/host/bluedroid/btc/profile/std/hf_ag/btc_hf_ag.c @@ -0,0 +1,1628 @@ +/* + * SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/************************************************************************************ + * + * Filename: btc_hf.c + * + * Description: Handsfree Profile Bluetooth Interface + * * + ***********************************************************************************/ +#include +#include +#include +#include +#include +#include "time.h" +#include "btc/btc_dm.h" +#include "btc_hf_ag.h" +#include "btc/btc_profile_queue.h" +#include "btc/btc_manage.h" +#include "btc/btc_util.h" +#include "btc/btc_common.h" +#include "bta/bta_ag_api.h" +#include "bt_common.h" +#include "common/bt_target.h" +#include "common/bt_trace.h" +#include "common/bt_defs.h" +#include "device/bdaddr.h" +#if (BT_CONTROLLER_INCLUDED == TRUE) +#include "esp_bt.h" +#endif +#include "esp_hf_ag_api.h" +#include "osi/allocator.h" + + +#if (BTC_HF_INCLUDED == TRUE) +/************************************************************************************ +** Constants & Macros +************************************************************************************/ +#ifndef BTC_HSAG_SERVICE_NAME +#define BTC_HSAG_SERVICE_NAME ("Headset Gateway") +#endif + +#ifndef BTC_HFAG_SERVICE_NAME +#define BTC_HFAG_SERVICE_NAME ("Handsfree Gateway") +#endif + +#ifndef BTC_HF_SERVICES +#define BTC_HF_SERVICES (BTA_HSP_SERVICE_MASK | BTA_HFP_SERVICE_MASK ) +#endif + +#ifndef BTC_HF_SERVICE_NAMES +#define BTC_HF_SERVICE_NAMES {BTC_HSAG_SERVICE_NAME , BTC_HFAG_SERVICE_NAME} +#endif + +#ifndef BTC_HF_SECURITY +#define BTC_HF_SECURITY (BTA_SEC_AUTHENTICATE | BTA_SEC_ENCRYPT) +#endif + +#define BTC_HF_INVALID_IDX -1 + +/* Max HF Clients Supported From App */ +static UINT16 btc_max_hf_clients = BTC_HF_NUM_CB; +/* HF Param Definition */ +#if HFP_DYNAMIC_MEMORY == FALSE +static hf_local_param_t hf_local_param[BTC_HF_NUM_CB]; +#else +static hf_local_param_t *hf_local_param = NULL; +#endif + +#if (BTM_WBS_INCLUDED == TRUE) +#ifndef BTC_HF_FEATURES +#define BTC_HF_FEATURES ( BTA_AG_FEAT_ECNR | \ + BTA_AG_FEAT_REJECT | \ + BTA_AG_FEAT_ECS | \ + BTA_AG_FEAT_EXTERR | \ + BTA_AG_FEAT_VREC | \ + BTA_AG_FEAT_INBAND | \ + BTA_AG_FEAT_CODEC | \ + BTA_AG_FEAT_ESCO_S4 | \ + BTA_AG_FEAT_UNAT ) +#endif +#else +#ifndef BTC_HF_FEATURES +#if BT_HF_AG_BQB_INCLUDED +#define BTC_HF_FEATURES ( BTA_AG_FEAT_REJECT | \ + BTA_AG_FEAT_ECS | \ + BTA_AG_FEAT_EXTERR | \ + BTA_AG_FEAT_VREC | \ + BTA_AG_FEAT_INBAND | \ + BTA_AG_FEAT_ESCO_S4 | \ + BTA_AG_FEAT_UNAT ) +#else +#define BTC_HF_FEATURES ( BTA_AG_FEAT_ECNR | \ + BTA_AG_FEAT_REJECT | \ + BTA_AG_FEAT_ECS | \ + BTA_AG_FEAT_EXTERR | \ + BTA_AG_FEAT_VREC | \ + BTA_AG_FEAT_INBAND | \ + BTA_AG_FEAT_ESCO_S4 | \ + BTA_AG_FEAT_UNAT ) +#endif /* BT_HF_AG_BQB_INCLUDED */ +#endif +#endif + +/* wide band synchronous */ +#ifndef BTC_HF_WBS_PREFERRED +#define BTC_HF_WBS_PREFERRED TRUE +#endif +BOOLEAN btc_conf_hf_force_wbs = BTC_HF_WBS_PREFERRED; + +#define CHECK_HF_INIT(idx) \ +do { \ + if ((idx < 0) || (idx >= BTC_HF_NUM_CB)) { \ + return BT_STATUS_FAIL; \ + } \ + if (!hf_local_param[idx].btc_hf_cb.initialized) { \ + BTIF_TRACE_WARNING("CHECK_HF_INIT: %s: HF AG not initialized", __FUNCTION__); \ + return BT_STATUS_NOT_READY; \ + } else { \ + BTIF_TRACE_EVENT("CHECK_HF_INIT: %s", __FUNCTION__); \ + } \ +} while (0) + +#define CHECK_HF_SLC_CONNECTED(idx) \ +do { \ + if ((idx < 0) || (idx >= BTC_HF_NUM_CB)) { \ + return BT_STATUS_FAIL; \ + } \ + if (!hf_local_param[idx].btc_hf_cb.initialized || \ + hf_local_param[idx].btc_hf_cb.connection_state != ESP_HF_CONNECTION_STATE_SLC_CONNECTED) { \ + BTIF_TRACE_WARNING("CHECK_HF_SLC_CONNECTED: %s: HF AG SLC not connected", __FUNCTION__); \ + return BT_STATUS_NOT_READY; \ + } else { \ + BTIF_TRACE_EVENT("CHECK_HF_SLC_CONNECTED: %s", __FUNCTION__); \ + } \ +} while (0) + + +#define clear_phone_state() \ + hf_local_param[idx].btc_hf_cb.call_state = ESP_HF_CALL_STATUS_NO_CALLS; \ + hf_local_param[idx].btc_hf_cb.call_setup_state = ESP_HF_CALL_SETUP_STATUS_IDLE;\ + hf_local_param[idx].btc_hf_cb.num_active = 0; \ + hf_local_param[idx].btc_hf_cb.num_held = 0; + +#define CHECK_HF_IDX(idx) \ +do { \ + if ((idx < 0) || (idx >= BTC_HF_NUM_CB)) { \ + BTC_TRACE_ERROR("%s:%d Invalid index %d", __FUNCTION__, __LINE__, idx); \ + return; \ + } \ +} while (0) + +/************************************************************************************ +** Static Function +************************************************************************************/ +static int btc_hf_idx_by_bdaddr(bt_bdaddr_t *bd_addr) +{ + for (int i = 0; i < btc_max_hf_clients; ++i) { + if (bdcmp(bd_addr->address, hf_local_param[i].btc_hf_cb.connected_bda.address) == 0) { + return i; + } + } + return BTC_HF_INVALID_IDX; +} + +static int btc_hf_find_free_idx(void) +{ + for (int idx = 0; idx < btc_max_hf_clients; ++idx) { + if (hf_local_param[idx].btc_hf_cb.initialized && + hf_local_param[idx].btc_hf_cb.connection_state == ESP_HF_CONNECTION_STATE_DISCONNECTED) { + return idx; + } + } + return BTC_HF_INVALID_IDX; +} + +static BOOLEAN is_connected(int idx, bt_bdaddr_t *bd_addr) +{ + if ((bdcmp(bd_addr->address,hf_local_param[idx].btc_hf_cb.connected_bda.address) == 0) && + ((hf_local_param[idx].btc_hf_cb.connection_state == ESP_HF_CONNECTION_STATE_CONNECTED) || + (hf_local_param[idx].btc_hf_cb.connection_state == ESP_HF_CONNECTION_STATE_SLC_CONNECTED))) { + return TRUE; + } + return FALSE; +} + +static int btc_hf_latest_connected_idx(void) +{ + struct timespec now, conn_time_delta; + int latest_conn_idx = BTC_HF_INVALID_IDX; + clock_gettime(CLOCK_MONOTONIC, &now); + conn_time_delta.tv_sec = now.tv_sec; + + for (int i = 0; i < btc_max_hf_clients; i++) { + if (hf_local_param[i].btc_hf_cb.connection_state == ESP_HF_CONNECTION_STATE_SLC_CONNECTED) { + if ((now.tv_sec - hf_local_param[i].btc_hf_cb.connected_timestamp.tv_sec) < conn_time_delta.tv_sec) { + conn_time_delta.tv_sec = now.tv_sec - hf_local_param[i].btc_hf_cb.connected_timestamp.tv_sec; + latest_conn_idx = i; + } + } + } + return latest_conn_idx; +} + +/************************************************************************************ +** Cb and Evt +************************************************************************************/ +static inline void btc_hf_cb_to_app(esp_hf_cb_event_t event, esp_hf_cb_param_t *param) +{ + esp_hf_cb_t btc_hf_callbacks = (esp_hf_cb_t)btc_profile_cb_get(BTC_PID_HF); + if (btc_hf_callbacks) { + btc_hf_callbacks(event, param); + } +} + +static void send_indicator_update(UINT16 indicator, UINT16 value) +{ + tBTA_AG_RES_DATA ag_res; + memset(&ag_res, 0, sizeof(tBTA_AG_RES_DATA)); + ag_res.ind.type = indicator; + ag_res.ind.value = value; + BTA_AgResult(BTA_AG_HANDLE_ALL, BTA_AG_IND_RES, &ag_res); +} + +static void bte_hf_evt(tBTA_AG_EVT event, tBTA_AG *param) +{ + int param_len = 0; + /* TODO: BTA sends the union members and not tBTA_AG. If using param_len=sizeof(tBTA_AG), we get a crash on memcpy */ + if (BTA_AG_REGISTER_EVT == event) { + param_len = sizeof(tBTA_AG_REGISTER); + } + else if (BTA_AG_OPEN_EVT == event) { + param_len = sizeof(tBTA_AG_OPEN); + } + else if ((BTA_AG_CLOSE_EVT == event) || (BTA_AG_AUDIO_OPEN_EVT == event) || (BTA_AG_AUDIO_CLOSE_EVT == event)) { + param_len = sizeof(tBTA_AG_HDR); + } + else if (BTA_AG_CONN_EVT == event) { + param_len = sizeof(tBTA_AG_CONN); + } + else if (param) { + param_len = sizeof(tBTA_AG_VAL); + } + btc_msg_t msg; + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_HF; + msg.act = event; + + /* Switch to BTC context */ + bt_status_t status = btc_transfer_context(&msg, param, param_len, NULL, NULL); + /* catch any failed context transfers */ + BTC_ASSERTC(status == BT_STATUS_SUCCESS, "context transfer failed", status); +} + +/************************************************************************************ +** Data flow control & Service management. +************************************************************************************/ +void btc_hf_reg_data_cb(esp_hf_incoming_data_cb_t recv, esp_hf_outgoing_data_cb_t send) +{ + hf_local_param[0].btc_hf_incoming_data_cb = recv; + hf_local_param[0].btc_hf_outgoing_data_cb = send; +} + +void btc_hf_incoming_data_cb_to_app(const uint8_t *data, uint32_t len) +{ + int idx = 0; + // todo: critical section protection + if (hf_local_param[idx].btc_hf_incoming_data_cb) { + hf_local_param[idx].btc_hf_incoming_data_cb(data, len); + } +} + +uint32_t btc_hf_outgoing_data_cb_to_app(uint8_t *data, uint32_t len) +{ + int idx = 0; + // todo: critical section protection + if (hf_local_param[idx].btc_hf_outgoing_data_cb) { + return hf_local_param[idx].btc_hf_outgoing_data_cb(data, len); + } else { + return 0; + } +} + +bt_status_t btc_hf_execute_service(BOOLEAN b_enable) +{ + char * p_service_names[] = BTC_HF_SERVICE_NAMES; + int idx; + if (b_enable) { + /* Enable and register with BTA-AG */ + BTA_AgEnable(BTA_AG_PARSE, bte_hf_evt); + for (idx = 0; idx < btc_max_hf_clients; idx++) { + BTA_AgRegister(BTC_HF_SERVICES, BTC_HF_SECURITY, BTC_HF_FEATURES, p_service_names, BTC_HF_ID_1); + } + } else { + /* De-register AG */ + for (idx = 0; idx < btc_max_hf_clients; idx++) { + BTA_AgDeregister(hf_local_param[idx].btc_hf_cb.handle); + } + /* Disable AG */ + BTA_AgDisable(); + } + return BT_STATUS_SUCCESS; +} + +/************************************************************************************ +** BTC HFP AG API FUNCTION +************************************************************************************/ +/************************************************************************************ +** Initialization and Connection Handle +************************************************************************************/ +bt_status_t btc_hf_init(void) +{ + int idx = 0; + + BTC_TRACE_DEBUG("%s - max_hf_clients=%d", __func__, btc_max_hf_clients); + +#if HFP_DYNAMIC_MEMORY == TRUE + if (hf_local_param != NULL) { + return BT_STATUS_FAIL; + } + + if ((hf_local_param = (hf_local_param_t *)osi_malloc(BTC_HF_NUM_CB * sizeof(hf_local_param_t))) == NULL) { + APPL_TRACE_ERROR("%s malloc failed!", __func__); + return BT_STATUS_NOMEM; + } + memset((void *)hf_local_param, 0, BTC_HF_NUM_CB * sizeof(hf_local_param_t)); +#endif + + /* Invoke the enable service API to the core to set the appropriate service_id + * Internally, the HSP_SERVICE_ID shall also be enabled if HFP is enabled (phone) + * othwerwise only HSP is enabled (tablet)*/ +#if (defined(BTC_HF_SERVICES) && (BTC_HF_SERVICES & BTA_HFP_SERVICE_MASK)) + btc_dm_enable_service(BTA_HFP_SERVICE_ID); +#else + btc_dm_enable_service(BTA_HSP_SERVICE_ID); +#endif + clear_phone_state(); + memset(&hf_local_param[idx].btc_hf_cb, 0, sizeof(btc_hf_cb_t)); + // custom initialization here + hf_local_param[idx].btc_hf_cb.initialized = true; +// set audio path +#if (BT_CONTROLLER_INCLUDED == TRUE) +#if BTM_SCO_HCI_INCLUDED + uint8_t data_path = ESP_SCO_DATA_PATH_HCI; +#else + uint8_t data_path = ESP_SCO_DATA_PATH_PCM; +#endif + esp_bredr_sco_datapath_set(data_path); +#endif + return BT_STATUS_SUCCESS; +} + +void btc_hf_deinit(void) +{ + BTC_TRACE_EVENT("%s", __FUNCTION__); + btc_dm_disable_service(BTA_HFP_SERVICE_ID); + hf_local_param[0].btc_hf_cb.initialized = false; +} + +static void btc_hf_cb_release(void) +{ +#if HFP_DYNAMIC_MEMORY == TRUE + if (hf_local_param) { + osi_free(hf_local_param); + hf_local_param = NULL; + } +#endif +} + +static bt_status_t connect_init(bt_bdaddr_t *bd_addr, uint16_t uuid) +{ + int idx = btc_hf_find_free_idx(); + + if (idx == BTC_HF_INVALID_IDX) { + return BT_STATUS_BUSY; + } + + if (!is_connected(idx, bd_addr)) { + hf_local_param[idx].btc_hf_cb.connection_state = ESP_HF_CONNECTION_STATE_CONNECTING; + bdcpy(hf_local_param[idx].btc_hf_cb.connected_bda.address, bd_addr->address); + BTA_AgOpen(hf_local_param[idx].btc_hf_cb.handle, hf_local_param[idx].btc_hf_cb.connected_bda.address, BTC_HF_SECURITY, BTC_HF_SERVICES); + return BT_STATUS_SUCCESS; + } + return BT_STATUS_BUSY; +} +bt_status_t btc_hf_connect(bt_bdaddr_t *bd_addr) +{ + return btc_queue_connect(UUID_SERVCLASS_AG_HANDSFREE, bd_addr, connect_init); +} + +bt_status_t btc_hf_disconnect(bt_bdaddr_t *bd_addr) +{ + int idx = btc_hf_idx_by_bdaddr(bd_addr); + + if ((idx < 0) || (idx >= BTC_HF_NUM_CB)) { + BTC_TRACE_ERROR("%s: Invalid index %d", __FUNCTION__, idx); + return BT_STATUS_FAIL; + } + + if (is_connected(idx, bd_addr)) { + BTA_AgClose(hf_local_param[idx].btc_hf_cb.handle); + return BT_STATUS_SUCCESS; + } + return BT_STATUS_FAIL; +} + +bt_status_t btc_hf_connect_audio(bt_bdaddr_t *bd_addr) +{ + int idx = btc_hf_idx_by_bdaddr(bd_addr); + CHECK_HF_SLC_CONNECTED(idx); + + if (is_connected(idx, bd_addr)) { + BTA_AgAudioOpen(hf_local_param[idx].btc_hf_cb.handle); + /* Inform the application that the audio connection has been initiated successfully */ + do { + esp_hf_cb_param_t param; + memset(¶m, 0, sizeof(esp_hf_cb_param_t)); + param.audio_stat.state = ESP_HF_AUDIO_STATE_CONNECTING; + memcpy(param.audio_stat.remote_addr, &hf_local_param[idx].btc_hf_cb.connected_bda, sizeof(esp_bd_addr_t)); + btc_hf_cb_to_app(ESP_HF_AUDIO_STATE_EVT, ¶m); + } while (0); + return BT_STATUS_SUCCESS; + } + return BT_STATUS_FAIL; +} + +bt_status_t btc_hf_disconnect_audio(bt_bdaddr_t *bd_addr) +{ + int idx = btc_hf_idx_by_bdaddr(bd_addr); + CHECK_HF_SLC_CONNECTED(idx); + + if (is_connected(idx, bd_addr)) { + BTA_AgAudioClose(hf_local_param[idx].btc_hf_cb.handle); + return BT_STATUS_SUCCESS; + } + return BT_STATUS_FAIL; +} + +static bt_status_t btc_hf_pkt_stat_nums_get(UINT16 sync_conn_handle) +{ + bt_status_t status = BT_STATUS_FAIL; +#if (BTM_SCO_HCI_INCLUDED == TRUE) + int idx = btc_hf_latest_connected_idx(); + CHECK_HF_SLC_CONNECTED(idx); + + if (idx != BTC_HF_INVALID_IDX) { + BTA_AgPktStatsNumsGet(hf_local_param[idx].btc_hf_cb.handle, sync_conn_handle); + status = BT_STATUS_SUCCESS; + } +#endif /*#if (BTM_SCO_HCI_INCLUDED == TRUE) */ + return status; +} + +/************************************************************************************ +** AT cmd Handle +************************************************************************************/ +//AT+VRA +static bt_status_t btc_hf_vra(bt_bdaddr_t *bd_addr, esp_hf_vr_state_t value) +{ + int idx = btc_hf_idx_by_bdaddr(bd_addr); + CHECK_HF_SLC_CONNECTED(idx); + + if (is_connected(idx, bd_addr)) { + if (hf_local_param[idx].btc_hf_cb.peer_feat & BTA_AG_PEER_FEAT_VREC) { + tBTA_AG_RES_DATA ag_res; + memset(&ag_res, 0, sizeof(ag_res)); + ag_res.state = value; + BTA_AgResult(hf_local_param[idx].btc_hf_cb.handle, BTA_AG_BVRA_RES, &ag_res); + return BT_STATUS_SUCCESS; + } else { + return BT_STATUS_UNSUPPORTED; + } + } + return BT_STATUS_NOT_READY; +} + +static bt_status_t btc_hf_volume_control(bt_bdaddr_t *bd_addr, esp_hf_volume_type_t type, int volume) +{ + int idx = btc_hf_idx_by_bdaddr(bd_addr); + CHECK_HF_SLC_CONNECTED(idx); + tBTA_AG_RES_DATA ag_res; + memset(&ag_res, 0, sizeof(tBTA_AG_RES_DATA)); + + if (is_connected(idx, bd_addr)) { + ag_res.num = volume; + BTA_AgResult(hf_local_param[idx].btc_hf_cb.handle, (type == ESP_HF_VOLUME_TYPE_SPK) ? BTA_AG_SPK_RES : BTA_AG_MIC_RES, &ag_res); + return BT_STATUS_SUCCESS; + } + return BT_STATUS_FAIL; +} + +// Pre-formatted AT response, typically in response to unknown AT cmd +static bt_status_t btc_hf_unat_response(bt_bdaddr_t *bd_addr, const char *unat) +{ + int idx = btc_hf_idx_by_bdaddr(bd_addr); + CHECK_HF_INIT(idx); + + if (is_connected(idx, bd_addr)) + { + tBTA_AG_RES_DATA ag_res; + /* Format the response and send */ + memset(&ag_res, 0, sizeof(ag_res)); + if (unat != NULL) { + strncpy(ag_res.str, unat, BTA_AG_AT_MAX_LEN); + } else { + ag_res.ok_flag = BTA_AG_OK_ERROR; + ag_res.errcode = BTA_AG_ERR_OP_NOT_SUPPORTED; + } + + BTA_AgResult(hf_local_param[idx].btc_hf_cb.handle, BTA_AG_UNAT_RES, &ag_res); + return BT_STATUS_SUCCESS; + } + return BT_STATUS_FAIL; +} + +// +CMEE: +static bt_status_t btc_hf_cmee_response(bt_bdaddr_t *bd_addr, esp_hf_at_response_code_t response_code, esp_hf_cme_err_t error_code) +{ + int idx = btc_hf_idx_by_bdaddr(bd_addr); + CHECK_HF_INIT(idx); + + if (is_connected(idx, bd_addr)) { + tBTA_AG_RES_DATA ag_res; + memset(&ag_res, 0, sizeof(ag_res)); + if (response_code == ESP_HF_AT_RESPONSE_CODE_OK) { + ag_res.ok_flag = BTA_AG_OK_DONE; + } else { + ag_res.ok_flag = BTA_AG_OK_ERROR; + ag_res.errcode = error_code; + } + BTA_AgResult(hf_local_param[idx].btc_hf_cb.handle, BTA_AG_UNAT_RES, &ag_res); + return BT_STATUS_SUCCESS; + } + return BT_STATUS_FAIL; +} + +// +CIEV<...> for device status update +static bt_status_t btc_hf_indchange_notification(bt_bdaddr_t *bd_addr, + esp_hf_call_status_t call_state, + esp_hf_call_setup_status_t call_setup_state, + esp_hf_network_state_t ntk_state, int signal) +{ + int idx = btc_hf_idx_by_bdaddr(bd_addr); + CHECK_HF_INIT(idx); + if (is_connected(idx, bd_addr)) { + /* Send all indicators to BTA. + * BTA will make sure no duplicates are sent out*/ + send_indicator_update(BTA_AG_IND_CALL, call_state); + send_indicator_update(BTA_AG_IND_CALLSETUP, call_setup_state); + send_indicator_update(BTA_AG_IND_SERVICE, ntk_state); + send_indicator_update(BTA_AG_IND_SIGNAL, signal); + return BT_STATUS_SUCCESS; + } + return BT_STATUS_FAIL; +} + +// +CIEV<...> for device status update, send other indicators, e.g. roaming, battery, call held and bearer +bt_status_t btc_hf_ciev_report(bt_bdaddr_t *bd_addr, tBTA_AG_IND_TYPE indicator, uint16_t value) +{ + int idx = btc_hf_idx_by_bdaddr(bd_addr); + CHECK_HF_INIT(idx); + + if (is_connected(idx, bd_addr)) { + send_indicator_update(indicator, value); + return BT_STATUS_SUCCESS; + } + return BT_STATUS_FAIL; +} + +//AT+CIND response +static bt_status_t btc_hf_cind_response(bt_bdaddr_t *bd_addr, + esp_hf_call_setup_status_t call_status, + esp_hf_call_setup_status_t call_setup_status, + esp_hf_network_state_t ntk_state, int signal, esp_hf_service_type_t roam, int batt_lev, + esp_hf_call_held_status_t call_held_status) +{ + int idx = btc_hf_idx_by_bdaddr(bd_addr); + CHECK_HF_INIT(idx); + + if (is_connected(idx, bd_addr)) { + tBTA_AG_RES_DATA ag_res; + memset(&ag_res, 0, sizeof (ag_res)); + sprintf(ag_res.str, "%d,%d,%d,%d,%d,%d,%d", + call_status, /* Call state*/ + call_setup_status, /* Callsetup state */ + ntk_state, /* network service */ + signal, /* Signal strength */ + roam, /* Roaming indicator */ + batt_lev, /* Battery level */ + call_held_status /* Callheld state */ + ); + BTA_AgResult(hf_local_param[idx].btc_hf_cb.handle, BTA_AG_CIND_RES, &ag_res); + return BT_STATUS_SUCCESS; + } + return BT_STATUS_FAIL; +} + +//AT+COPS response +static bt_status_t btc_hf_cops_response(bt_bdaddr_t *bd_addr, const char *name) +{ + int idx = btc_hf_idx_by_bdaddr(bd_addr); + CHECK_HF_SLC_CONNECTED(idx); + + if (is_connected(idx, bd_addr)) { + tBTA_AG_RES_DATA ag_res; + memset (&ag_res, 0, sizeof (ag_res)); + /* Format the response */ + sprintf(ag_res.str, "0,0,\"%s\"", name); + ag_res.ok_flag = BTA_AG_OK_DONE; + BTA_AgResult(hf_local_param[idx].btc_hf_cb.handle, BTA_AG_COPS_RES, &ag_res); + return BT_STATUS_SUCCESS; + } + return BT_STATUS_FAIL; +} + +//AT+CLCC response +static bt_status_t btc_hf_clcc_response(bt_bdaddr_t *bd_addr, int index, esp_hf_current_call_direction_t dir, + esp_hf_current_call_status_t current_call_state, + esp_hf_current_call_mode_t mode, esp_hf_current_call_mpty_type_t mpty, + const char *number, esp_hf_call_addr_type_t type) +{ + int idx = btc_hf_idx_by_bdaddr(bd_addr); + CHECK_HF_SLC_CONNECTED(idx); + + if (is_connected(idx, bd_addr)) { + tBTA_AG_RES_DATA ag_res; + memset (&ag_res, 0, sizeof (ag_res)); + if (index == 0) { + ag_res.ok_flag = BTA_AG_OK_DONE; + } else { + BTC_TRACE_EVENT("clcc_response: [%d] dir: %d current_call_state: %d mode: %d number: %s type: %d", + index, dir, current_call_state, mode, number, type); + int loc = sprintf (ag_res.str, "%d,%d,%d,%d,%d", index, dir, current_call_state, mode, mpty); + if (number) { + if ((type == ESP_HF_CALL_ADDR_TYPE_INTERNATIONAL) && (*number != '+')) { + sprintf(&ag_res.str[loc], ",\"+%s\",%d", number, type); + } else { + sprintf(&ag_res.str[loc], ",\"%s\",%d", number, type); + } + } + } + BTA_AgResult(hf_local_param[idx].btc_hf_cb.handle, BTA_AG_CLCC_RES, &ag_res); + return BT_STATUS_SUCCESS; + } + return BT_STATUS_FAIL; +} + +//AT+CNUM +static bt_status_t btc_hf_cnum_response(bt_bdaddr_t *bd_addr, const char *number, int number_type, esp_hf_subscriber_service_type_t service_type) +{ + int idx = btc_hf_idx_by_bdaddr(bd_addr); + CHECK_HF_SLC_CONNECTED(idx); + + if (is_connected(idx, bd_addr)) { + tBTA_AG_RES_DATA ag_res; + memset(&ag_res, 0, sizeof (ag_res)); + BTC_TRACE_EVENT("cnum_response: number = %s, number type = %d, service type = %d", number, number_type, service_type); + if (service_type) { + sprintf(ag_res.str, ",\"%s\",%d,,%d",number, number_type, service_type); + } else { + sprintf(ag_res.str, ",\"%s\",%d,,",number, number_type); + } + ag_res.ok_flag = BTA_AG_OK_DONE; + BTA_AgResult(hf_local_param[idx].btc_hf_cb.handle, BTA_AG_CNUM_RES, &ag_res); + return BT_STATUS_SUCCESS; + } + return BT_STATUS_FAIL; +} + +//+BSIR +static bt_status_t btc_hf_inband_ring(bt_bdaddr_t *bd_addr, esp_hf_in_band_ring_state_t state) +{ + int idx = btc_hf_idx_by_bdaddr(bd_addr); + CHECK_HF_SLC_CONNECTED(idx); + + if (is_connected(idx, bd_addr)) { + tBTA_AG_RES_DATA ag_res; + memset (&ag_res, 0, sizeof (ag_res)); + ag_res.state = state; + BTA_AgResult(hf_local_param[idx].btc_hf_cb.handle, BTA_AG_INBAND_RING_RES, &ag_res); + return BT_STATUS_SUCCESS; + } + return BT_STATUS_FAIL; +} + +//Update phone status whether AG or HF act +static bt_status_t btc_hf_phone_state_update(bt_bdaddr_t *bd_addr,int num_active, int num_held, + esp_hf_call_status_t call_state, esp_hf_call_setup_status_t call_setup_state, + char *number, esp_hf_call_addr_type_t type) +{ + tBTA_AG_RES res = 0xff; + tBTA_AG_RES_DATA ag_res; + bt_status_t status = BT_STATUS_SUCCESS; + BOOLEAN activeCallUpdated = FALSE; + int idx = btc_hf_idx_by_bdaddr(bd_addr), i; + + /* hf_idx is index of connected HS that sent ATA/BLDN, otherwise index of latest connected HS */ + if (hf_local_param->hf_idx != BTC_HF_INVALID_IDX) { + idx = hf_local_param->hf_idx; + } else { + idx = btc_hf_latest_connected_idx(); + } + + BTC_TRACE_DEBUG("phone_state_change: idx = %d", idx); + CHECK_HF_SLC_CONNECTED(idx); + BTC_TRACE_DEBUG("phone_state_change: num_active=%d [prev: %d] num_held=%d[prev: %d] call =%s [prev: %s] call_setup=%s [prev: %s]", + num_active, hf_local_param[idx].btc_hf_cb.num_active, + num_held, hf_local_param[idx].btc_hf_cb.num_held, + dump_hf_call_state(call_state), dump_hf_call_state(hf_local_param[idx].btc_hf_cb.call_state), + dump_hf_call_setup_state(call_setup_state), dump_hf_call_setup_state(hf_local_param[idx].btc_hf_cb.call_setup_state)); + + /* If all indicators are 0, send end call and return */ + if (num_active == 0 && num_held == 0 && call_state == ESP_HF_CALL_STATUS_NO_CALLS && call_setup_state == ESP_HF_CALL_SETUP_STATUS_IDLE) { + BTC_TRACE_DEBUG("%s: Phone on hook", __FUNCTION__); + + /* Record call termination timestamp if there was an active/held call or call_setup_state > ESP_HF_CALL_SETUP_STATUS_IDLE */ + if ((hf_local_param[idx].btc_hf_cb.call_state != ESP_HF_CALL_STATUS_NO_CALLS) || + (hf_local_param[idx].btc_hf_cb.call_setup_state != ESP_HF_CALL_SETUP_STATUS_IDLE) || + (hf_local_param[idx].btc_hf_cb.num_active) || + (hf_local_param[idx].btc_hf_cb.num_held)) { + BTC_TRACE_DEBUG("%s: Record call termination timestamp", __FUNCTION__); + clock_gettime(CLOCK_MONOTONIC, &hf_local_param[0].btc_hf_cb.call_end_timestamp); + } + BTA_AgResult(BTA_AG_HANDLE_ALL, BTA_AG_END_CALL_RES, NULL); + hf_local_param->hf_idx = BTC_HF_INVALID_IDX; + + /* If held call was present, reset that as well. */ + if (hf_local_param[idx].btc_hf_cb.num_held) { + send_indicator_update(BTA_AG_IND_CALLHELD, 0); + } + goto update_call_states; + } + + /* Phone state can change when: + * 1. An outgoing/incoming call was answered. + * 2. A held was resumed. + * 3. Without callsetup notifications, call became active. (HF Unit links in during an Active call.) + */ + + /* Handle case(3) here prior to call setup handling.*/ + if (((num_active + num_held) > 0) && + (hf_local_param[idx].btc_hf_cb.num_active == 0) && + (hf_local_param[idx].btc_hf_cb.num_held == 0) && + (hf_local_param[idx].btc_hf_cb.call_setup_state == ESP_HF_CALL_SETUP_STATUS_IDLE)) { + BTC_TRACE_DEBUG("%s: Active/Held call notification received without call setup update", __FUNCTION__); + + memset(&ag_res, 0, sizeof(tBTA_AG_RES_DATA)); + ag_res.audio_handle = hf_local_param[idx].btc_hf_cb.handle; + + /* Addition callsetup with the Active call. */ + if (call_setup_state != ESP_HF_CALL_SETUP_STATUS_IDLE) { + res = BTA_AG_MULTI_CALL_RES; + } else { + res = BTA_AG_OUT_CALL_CONN_RES; + } + /* CIND response should have been updated. */ + BTA_AgResult(BTA_AG_HANDLE_ALL, res, &ag_res); + /* Just open SCO conenction. */ + BTA_AgAudioOpen(ag_res.audio_handle); + activeCallUpdated = TRUE; + } + + /* Handle call_setup indicator change. */ + if (call_setup_state != hf_local_param[idx].btc_hf_cb.call_setup_state) { + BTC_TRACE_DEBUG("%s: Call setup states changed. old: %s new: %s", __FUNCTION__, + dump_hf_call_setup_state(hf_local_param[idx].btc_hf_cb.call_setup_state), + dump_hf_call_setup_state(call_setup_state)); + memset(&ag_res, 0, sizeof(tBTA_AG_RES_DATA)); + + switch(call_setup_state) + { + case ESP_HF_CALL_SETUP_STATUS_IDLE: + { + switch(hf_local_param[idx].btc_hf_cb.call_setup_state) + { + case ESP_HF_CALL_SETUP_STATUS_INCOMING: + { + if (num_active > hf_local_param[idx].btc_hf_cb.num_active) { + res = BTA_AG_IN_CALL_CONN_RES; + ag_res.audio_handle = hf_local_param[idx].btc_hf_cb.handle; + } else if (num_held > hf_local_param[idx].btc_hf_cb.num_held) { + res = BTA_AG_IN_CALL_HELD_RES; + } else { + res = BTA_AG_CALL_CANCEL_RES; + } + break; + } + + case ESP_HF_CALL_SETUP_STATUS_OUTGOING_DIALING: + case ESP_HF_CALL_SETUP_STATUS_OUTGOING_ALERTING: + { + if (num_active > hf_local_param[idx].btc_hf_cb.num_active) { + res = BTA_AG_OUT_CALL_CONN_RES; + ag_res.audio_handle = BTA_AG_HANDLE_SCO_NO_CHANGE; + } else { + res = BTA_AG_CALL_CANCEL_RES; + } + break; + } + + default: + BTC_TRACE_ERROR("%s: Call setup state no change.", __FUNCTION__); + status = BT_STATUS_PARM_INVALID; + break; + } + break; + } + + case ESP_HF_CALL_SETUP_STATUS_INCOMING: + { + if (num_active || num_held) { + res = BTA_AG_CALL_WAIT_RES; + } else { + res = BTA_AG_IN_CALL_RES; + } + + if (number) { + int loc = 0; + if ((type == ESP_HF_CALL_ADDR_TYPE_INTERNATIONAL) && (*number != '+')) { + loc = sprintf (ag_res.str, "\"+%s\"", number); + } else { + loc = sprintf (ag_res.str, "\"%s\"", number); + } + ag_res.num = type; + if (res == BTA_AG_CALL_WAIT_RES) { + sprintf(&ag_res.str[loc], ",%d", type); + } + } + break; + } + + case ESP_HF_CALL_SETUP_STATUS_OUTGOING_DIALING: + { + if (!(num_active + num_held)) { + ag_res.audio_handle = hf_local_param[idx].btc_hf_cb.handle; + } + res = BTA_AG_OUT_CALL_ORIG_RES; + break; + } + + case ESP_HF_CALL_SETUP_STATUS_OUTGOING_ALERTING: + { + if ((hf_local_param[idx].btc_hf_cb.call_setup_state == ESP_HF_CALL_SETUP_STATUS_IDLE) && + !(num_active + num_held)) { + ag_res.audio_handle = hf_local_param[idx].btc_hf_cb.handle; + /* Force SCO setup here.*/ + BTA_AgAudioOpen(ag_res.audio_handle); + } + res = BTA_AG_OUT_CALL_ALERT_RES; + break; + } + + default: + BTC_TRACE_ERROR("%s: Incorrect new ringing call state", __FUNCTION__); + status = BT_STATUS_PARM_INVALID; + break; + } + BTC_TRACE_DEBUG("%s: Call setup state changed. res=%d, audio_handle=%d", __FUNCTION__, res, ag_res.audio_handle); + if (res) { + BTA_AgResult(BTA_AG_HANDLE_ALL, res, &ag_res); + } + /* If call setup is idle, we have already updated call indicator, jump out */ + if (call_setup_state == ESP_HF_CALL_SETUP_STATUS_IDLE) { + /* Check & Update call_held_state */ + if ((num_held > 0) && (num_active > 0)) + send_indicator_update(BTA_AG_IND_CALLHELD, ESP_HF_CALL_HELD_STATUS_HELD_AND_ACTIVE); + goto update_call_states; + } + } + + memset(&ag_res, 0, sizeof(tBTA_AG_RES_DATA)); + + /* Handle call_state indicator change. */ + if (!activeCallUpdated && + ((num_active + num_held) != (hf_local_param[idx].btc_hf_cb.num_active + hf_local_param[idx].btc_hf_cb.num_held))) { + BTC_TRACE_DEBUG("%s: Active call states changed. old: %d new: %d", __FUNCTION__, hf_local_param[idx].btc_hf_cb.num_active, num_active); + send_indicator_update(BTA_AG_IND_CALL, ((num_active + num_held) > 0) ? 1 : 0); + } + + /* Handle call_held_state indicator change. */ + if (num_held != hf_local_param[idx].btc_hf_cb.num_held || + ((num_active == 0) && ((num_held + hf_local_param[idx].btc_hf_cb.num_held) > 1))) { + BTC_TRACE_DEBUG("%s: Held call states changed. old: %d new: %d", __FUNCTION__, hf_local_param[idx].btc_hf_cb.num_held, num_held); + send_indicator_update(BTA_AG_IND_CALLHELD, ((num_held == 0) ? 0 : ((num_active == 0) ? 2 : 1))); + } + + /* Handle Call Active/Held Swap indicator update.*/ + if ((call_setup_state == hf_local_param[idx].btc_hf_cb.call_setup_state) && + (num_active) && + (num_held) && + (num_active == hf_local_param[idx].btc_hf_cb.num_active) && + (num_held == hf_local_param[idx].btc_hf_cb.num_held)) { + BTC_TRACE_DEBUG("%s: Calls swapped", __FUNCTION__); + send_indicator_update(BTA_AG_IND_CALLHELD, 1); + } + +update_call_states: + for (i = 0; i < btc_max_hf_clients; i++) { + hf_local_param[i].btc_hf_cb.num_active = num_active; + hf_local_param[i].btc_hf_cb.num_held = num_held; + hf_local_param[i].btc_hf_cb.call_state = call_state; + hf_local_param[i].btc_hf_cb.call_setup_state = call_setup_state; + } + return status; +} + +bt_status_t btc_hf_ci_sco_data(void) +{ + bt_status_t status = BT_STATUS_SUCCESS; +#if (BTM_SCO_HCI_INCLUDED == TRUE) + int idx = btc_hf_latest_connected_idx(); + CHECK_HF_SLC_CONNECTED(idx); + + if (idx != BTC_HF_INVALID_IDX) { + BTA_AgCiData(hf_local_param[idx].btc_hf_cb.handle); + return status; + } + status = BT_STATUS_FAIL; +#endif /*#if (BTM_SCO_HCI_INCLUDED == TRUE ) */ + return status; +} + +/************************************************************************************ +** Memory malloc and release +************************************************************************************/ +void btc_hf_arg_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src) +{ + btc_hf_args_t *dst = (btc_hf_args_t *) p_dest; + btc_hf_args_t *src = (btc_hf_args_t *) p_src; + + switch (msg->act) { + case BTC_HF_UNAT_RESPONSE_EVT: + { + if (src->unat_rep.unat == NULL) { + break; + } + + dst->unat_rep.unat = (char *)osi_malloc(strlen(src->unat_rep.unat)+1); + if(dst->unat_rep.unat) { + memcpy(dst->unat_rep.unat, src->unat_rep.unat, strlen(src->unat_rep.unat)+1); + } else if (strlen(src->unat_rep.unat) == 0) { + BTC_TRACE_DEBUG("%s %d no mem\n", __func__, msg->act); + } else { + BTC_TRACE_ERROR("%s %d osi_malloc failed\n", __func__, msg->act); + } + break; + } + + case BTC_HF_COPS_RESPONSE_EVT: + { + if (src->cops_rep.name == NULL) { + break; + } + + dst->cops_rep.name = (char *)osi_malloc(strlen(src->cops_rep.name)+1); + if(dst->cops_rep.name) { + memcpy(dst->cops_rep.name, src->cops_rep.name, strlen(src->cops_rep.name)+1); + } else if (strlen(src->cops_rep.name) == 0) { + BTC_TRACE_DEBUG("%s %d no mem\n", __func__, msg->act); + } else { + BTC_TRACE_ERROR("%s %d osi_malloc failed\n", __func__, msg->act); + } + break; + } + + case BTC_HF_CLCC_RESPONSE_EVT: + { + if (src->clcc_rep.number == NULL) { + break; + } + + dst->clcc_rep.number = (char *)osi_malloc(strlen(src->clcc_rep.number)+1); + if(dst->clcc_rep.number) { + memcpy(dst->clcc_rep.number, src->clcc_rep.number, strlen(src->clcc_rep.number)+1); + } else if (strlen(src->clcc_rep.number) == 0) { + BTC_TRACE_DEBUG("%s %d no mem\n", __func__, msg->act); + } else { + BTC_TRACE_ERROR("%s %d osi_malloc failed\n", __func__, msg->act); + } + break; + } + + case BTC_HF_CNUM_RESPONSE_EVT: + { + if (src->cnum_rep.number == NULL) { + break; + } + + dst->cnum_rep.number = (char *)osi_malloc(strlen(src->cnum_rep.number)+1); + if(dst->cnum_rep.number) { + memcpy(dst->cnum_rep.number, src->cnum_rep.number, strlen(src->cnum_rep.number)+1); + } else if (strlen(src->cnum_rep.number) == 0) { + BTC_TRACE_DEBUG("%s %d no mem\n", __func__, msg->act); + } else { + BTC_TRACE_ERROR("%s %d osi_malloc failed\n", __func__, msg->act); + } + break; + } + + case BTC_HF_AC_INCALL_EVT: + case BTC_HF_RJ_INCALL_EVT: + case BTC_HF_OUT_CALL_EVT: + case BTC_HF_END_CALL_EVT: + { + if (src->phone.number == NULL) { + break; + } + + dst->phone.number = (char *)osi_malloc(strlen(src->phone.number)+1); + if(dst->phone.number) { + memcpy(dst->phone.number, src->phone.number, strlen(src->phone.number)+1); + } else if (strlen(src->phone.number) == 0) { + BTC_TRACE_DEBUG("%s %d no mem\n", __func__, msg->act); + } else { + BTC_TRACE_ERROR("%s %d osi_malloc failed\n", __func__, msg->act); + } + break; + } + + default: + break; + } +} + +void btc_hf_arg_deep_free(btc_msg_t *msg) +{ + btc_hf_args_t *arg = (btc_hf_args_t *)msg->arg; + + switch (msg->act) { + case BTC_HF_UNAT_RESPONSE_EVT: + { + if (arg->unat_rep.unat) { + osi_free(arg->unat_rep.unat); + } + break; + } + + case BTC_HF_COPS_RESPONSE_EVT: + { + if (arg->cops_rep.name) { + osi_free(arg->cops_rep.name); + } + break; + } + + case BTC_HF_CLCC_RESPONSE_EVT: + { + if (arg->clcc_rep.number) { + osi_free(arg->clcc_rep.number); + } + break; + } + + case BTC_HF_CNUM_RESPONSE_EVT: + { + if (arg->cnum_rep.number) { + osi_free(arg->cnum_rep.number); + } + break; + } + + case BTC_HF_AC_INCALL_EVT: + case BTC_HF_RJ_INCALL_EVT: + case BTC_HF_OUT_CALL_EVT: + case BTC_HF_END_CALL_EVT: + { + if (arg->phone.number) { + osi_free(arg->phone.number); + } + break; + } + + default: + break; + } +} + +/************************************************************************************ +** Handler Functions (handle the cmd from app) +************************************************************************************/ +void btc_hf_call_handler(btc_msg_t *msg) +{ + btc_hf_args_t *arg = (btc_hf_args_t *)(msg->arg); + + switch (msg->act) { + case BTC_HF_INIT_EVT: + { + btc_hf_init(); + break; + } + + case BTC_HF_DEINIT_EVT: + { + btc_hf_deinit(); + break; + } + + case BTC_HF_CONNECT_EVT: + { + btc_hf_connect(&arg->connect); + break; + } + + case BTC_HF_DISCONNECT_EVT: + { + btc_hf_disconnect(&arg->disconnect); + break; + } + + case BTC_HF_CONNECT_AUDIO_EVT: + { + btc_hf_connect_audio(&arg->connect_audio); + break; + } + + case BTC_HF_DISCONNECT_AUDIO_EVT: + { + btc_hf_disconnect_audio(&arg->disconnect_audio); + break; + } + + case BTC_HF_VRA_EVT: + { + btc_hf_vra(&arg->vra_rep.remote_addr, arg->vra_rep.value); + if (arg->vra_rep.value) { + btc_hf_connect_audio(&arg->vra_rep.remote_addr); + } else { + btc_hf_disconnect_audio(&arg->vra_rep.remote_addr); + } + break; + } + + case BTC_HF_VOLUME_CONTROL_EVT: + { + btc_hf_volume_control(&arg->volcon.remote_addr, arg->volcon.target_type, arg->volcon.volume); + break; + } + + case BTC_HF_UNAT_RESPONSE_EVT: + { + btc_hf_unat_response(&arg->unat_rep.remote_addr, arg->unat_rep.unat); + break; + } + + case BTC_HF_CME_ERR_EVT: + { + btc_hf_cmee_response(&arg->ext_at.remote_addr, arg->ext_at.response_code, arg->ext_at.error_code); + break; + } + + case BTC_HF_IND_NOTIFICATION_EVT: + { + btc_hf_indchange_notification(&arg->ind_change.remote_addr, + arg->ind_change.call_state, arg->ind_change.call_setup_state, + arg->ind_change.ntk_state, arg->ind_change.signal); + break; + } + + case BTC_HF_CIEV_REPORT_EVT: + { + btc_hf_ciev_report(&arg->ciev_rep.remote_addr, arg->ciev_rep.ind.type, arg->ciev_rep.ind.value); + break; + } + + case BTC_HF_CIND_RESPONSE_EVT: + { + btc_hf_cind_response(&arg->cind_rep.remote_addr, + arg->cind_rep.call_state, arg->cind_rep.call_setup_state, + arg->cind_rep.ntk_state, arg->cind_rep.signal, arg->cind_rep.roam, arg->cind_rep.batt_lev, + arg->cind_rep.call_held_state); + break; + } + + case BTC_HF_COPS_RESPONSE_EVT: + { + btc_hf_cops_response(&arg->cops_rep.remote_addr, arg->cops_rep.name); + break; + } + + case BTC_HF_CLCC_RESPONSE_EVT: + { + btc_hf_clcc_response(&arg->clcc_rep.remote_addr, arg->clcc_rep.index, + arg->clcc_rep.dir, arg->clcc_rep.current_call_state, + arg->clcc_rep.mode, arg->clcc_rep.mpty, arg->clcc_rep.number, arg->clcc_rep.type); + break; + } + + case BTC_HF_CNUM_RESPONSE_EVT: + { + btc_hf_cnum_response(&arg->cnum_rep.remote_addr, arg->cnum_rep.number, arg->cnum_rep.number_type, arg->cnum_rep.service_type); + break; + } + + case BTC_HF_INBAND_RING_EVT: + { + btc_hf_inband_ring(&arg->bsir.remote_addr, arg->bsir.state); + break; + } + + case BTC_HF_AC_INCALL_EVT: + { + btc_hf_phone_state_update(&arg->phone.remote_addr, arg->phone.num_active, arg->phone.num_held, + arg->phone.call_state, arg->phone.call_setup_state, arg->phone.number, + arg->phone.call_addr_type); + break; + } + + case BTC_HF_RJ_INCALL_EVT: + { + btc_hf_phone_state_update(&arg->phone.remote_addr, arg->phone.num_active, arg->phone.num_held, + arg->phone.call_state, arg->phone.call_setup_state, arg->phone.number, + arg->phone.call_addr_type); + btc_hf_disconnect_audio(&arg->phone.remote_addr); + break; + } + + case BTC_HF_OUT_CALL_EVT: + { + btc_hf_connect_audio(&arg->phone.remote_addr); + btc_hf_phone_state_update(&arg->phone.remote_addr, arg->phone.num_active, arg->phone.num_held, + arg->phone.call_state, arg->phone.call_setup_state, arg->phone.number, + arg->phone.call_addr_type); + break; + } + + case BTC_HF_END_CALL_EVT: + { + btc_hf_phone_state_update(&arg->phone.remote_addr, arg->phone.num_active, arg->phone.num_held, + arg->phone.call_state, arg->phone.call_setup_state, arg->phone.number, + arg->phone.call_addr_type); + btc_hf_disconnect_audio(&arg->phone.remote_addr); + break; + } + + case BTC_HF_REGISTER_DATA_CALLBACK_EVT: + { + btc_hf_reg_data_cb(arg->reg_data_cb.recv, arg->reg_data_cb.send); + break; + } + case BTC_HF_REQUEST_PKT_STAT_EVT: + { + btc_hf_pkt_stat_nums_get(arg->pkt_sync_hd.sync_conn_handle); + break; + } + + default: + BTC_TRACE_WARNING("%s : unhandled event: %d\n", __FUNCTION__, msg->act); + } + btc_hf_arg_deep_free(msg); +} + +void btc_hf_cb_handler(btc_msg_t *msg) +{ + uint16_t event = msg->act; + tBTA_AG *p_data = (tBTA_AG *)msg->arg; + esp_hf_cb_param_t param; + bdstr_t bdstr; + int idx = BTC_HF_INVALID_IDX; + + BTC_TRACE_DEBUG("%s: event = %s", __FUNCTION__, dump_hf_event(event)); + + memset(¶m, 0, sizeof(esp_hf_cb_param_t)); + + switch (event) { + case BTA_AG_ENABLE_EVT: + break; + case BTA_AG_DISABLE_EVT: + { + btc_hf_cb_release(); + break; + } + case BTA_AG_REGISTER_EVT: + { + idx = p_data->hdr.handle - 1; + CHECK_HF_IDX(idx); + hf_local_param[idx].btc_hf_cb.handle = p_data->reg.hdr.handle; + BTC_TRACE_DEBUG("%s: BTA_AG_REGISTER_EVT," "hf_local_param[%d].btc_hf_cb.handle = %d", + __FUNCTION__, idx, hf_local_param[idx].btc_hf_cb.handle); + break; + } + + case BTA_AG_OPEN_EVT: + { + idx = p_data->hdr.handle - 1; + CHECK_HF_IDX(idx); + if (p_data->open.hdr.status == BTA_AG_SUCCESS) + { + bdcpy(hf_local_param[idx].btc_hf_cb.connected_bda.address, p_data->open.bd_addr); + hf_local_param[idx].btc_hf_cb.connection_state = ESP_HF_CONNECTION_STATE_CONNECTED; + hf_local_param[idx].btc_hf_cb.peer_feat = 0; + hf_local_param[idx].btc_hf_cb.chld_feat = 0; + //clear_phone_state(); + } else if (hf_local_param[idx].btc_hf_cb.connection_state == ESP_HF_CONNECTION_STATE_CONNECTING) { + hf_local_param[idx].btc_hf_cb.connection_state = ESP_HF_CONNECTION_STATE_DISCONNECTED; + } else { + BTC_TRACE_WARNING("%s: AG open failed, but another device connected. status=%d state=%d connected device=%s", __FUNCTION__, + p_data->open.hdr.status, hf_local_param[idx].btc_hf_cb.connection_state, + bdaddr_to_string(&hf_local_param[idx].btc_hf_cb.connected_bda, bdstr, sizeof(bdstr))); + break; + } + + do { + memcpy(param.conn_stat.remote_bda, &hf_local_param[idx].btc_hf_cb.connected_bda, sizeof(esp_bd_addr_t)); + param.conn_stat.state = hf_local_param[idx].btc_hf_cb.connection_state; + param.conn_stat.peer_feat = 0; + param.conn_stat.chld_feat = 0; + btc_hf_cb_to_app(ESP_HF_CONNECTION_STATE_EVT, ¶m); + } while (0); + + if (hf_local_param[idx].btc_hf_cb.connection_state == ESP_HF_CONNECTION_STATE_DISCONNECTED) + bdsetany(hf_local_param[idx].btc_hf_cb.connected_bda.address); + + if (p_data->open.hdr.status != BTA_AG_SUCCESS) + btc_queue_advance(); + break; + } + + case BTA_AG_CONN_EVT: + { + idx = p_data->hdr.handle - 1; + CHECK_HF_IDX(idx); + clock_gettime(CLOCK_MONOTONIC, &(hf_local_param[idx].btc_hf_cb.connected_timestamp)); + BTC_TRACE_DEBUG("%s: BTA_AG_CONN_EVT, idx = %d ", __FUNCTION__, idx); + hf_local_param[idx].btc_hf_cb.peer_feat = p_data->conn.peer_feat; + hf_local_param[idx].btc_hf_cb.chld_feat = p_data->conn.chld_feat; + hf_local_param[idx].btc_hf_cb.connection_state = ESP_HF_CONNECTION_STATE_SLC_CONNECTED; + + do { + param.conn_stat.state = hf_local_param[idx].btc_hf_cb.connection_state; + param.conn_stat.peer_feat = hf_local_param[idx].btc_hf_cb.peer_feat; + param.conn_stat.chld_feat = hf_local_param[idx].btc_hf_cb.chld_feat; + memcpy(param.conn_stat.remote_bda, &hf_local_param[idx].btc_hf_cb.connected_bda, sizeof(esp_bd_addr_t)); + btc_hf_cb_to_app(ESP_HF_CONNECTION_STATE_EVT, ¶m); + } while(0); + hf_local_param[idx].hf_idx = btc_hf_latest_connected_idx(); + btc_queue_advance(); + break; + } + + case BTA_AG_CLOSE_EVT: + { + idx = p_data->hdr.handle - 1; + CHECK_HF_IDX(idx); + hf_local_param[idx].btc_hf_cb.connected_timestamp.tv_sec = 0; + hf_local_param[idx].btc_hf_cb.connection_state = ESP_HF_CONNECTION_STATE_DISCONNECTED; + BTC_TRACE_DEBUG("%s: BTA_AG_CLOSE_EVT," "hf_local_param[%d].btc_hf_cb.handle = %d", __FUNCTION__, + idx, hf_local_param[idx].btc_hf_cb.handle); + do { + param.conn_stat.state = ESP_HF_CONNECTION_STATE_DISCONNECTED; + param.conn_stat.peer_feat = 0; + param.conn_stat.chld_feat = 0; + memcpy(param.conn_stat.remote_bda, &hf_local_param[idx].btc_hf_cb.connected_bda, sizeof(esp_bd_addr_t)); + btc_hf_cb_to_app(ESP_HF_CONNECTION_STATE_EVT, ¶m); + } while(0); + bdsetany(hf_local_param[idx].btc_hf_cb.connected_bda.address); + clear_phone_state(); + hf_local_param[idx].hf_idx = btc_hf_latest_connected_idx(); + btc_queue_advance(); + break; + } + + case BTA_AG_AUDIO_OPEN_EVT: + { + idx = p_data->hdr.handle - 1; + CHECK_HF_IDX(idx); + do { + param.audio_stat.state = ESP_HF_AUDIO_STATE_CONNECTED; + memcpy(param.audio_stat.remote_addr, &hf_local_param[idx].btc_hf_cb.connected_bda,sizeof(esp_bd_addr_t)); + param.audio_stat.sync_conn_handle = p_data->hdr.sync_conn_handle; + btc_hf_cb_to_app(ESP_HF_AUDIO_STATE_EVT, ¶m); + } while(0); + break; + } + + case BTA_AG_AUDIO_MSBC_OPEN_EVT: + { + idx = p_data->hdr.handle - 1; + CHECK_HF_IDX(idx); + do { + param.audio_stat.state = ESP_HF_AUDIO_STATE_CONNECTED_MSBC; + memcpy(param.audio_stat.remote_addr, &hf_local_param[idx].btc_hf_cb.connected_bda,sizeof(esp_bd_addr_t)); + param.audio_stat.sync_conn_handle = p_data->hdr.sync_conn_handle; + btc_hf_cb_to_app(ESP_HF_AUDIO_STATE_EVT, ¶m); + } while (0); + break; + } + case BTA_AG_AUDIO_CLOSE_EVT: + { + idx = p_data->hdr.handle - 1; + CHECK_HF_IDX(idx); + do { + param.audio_stat.state = ESP_HF_AUDIO_STATE_DISCONNECTED; + memcpy(param.audio_stat.remote_addr, &hf_local_param[idx].btc_hf_cb.connected_bda, sizeof(esp_bd_addr_t)); + param.audio_stat.sync_conn_handle = p_data->hdr.sync_conn_handle; + btc_hf_cb_to_app(ESP_HF_AUDIO_STATE_EVT, ¶m); + } while(0); + break; + } + + case BTA_AG_AT_BVRA_EVT: + { + idx = p_data->hdr.handle - 1; + CHECK_HF_IDX(idx); + do { + param.vra_rep.value = p_data->val.num; + memcpy(param.vra_rep.remote_addr, &hf_local_param[idx].btc_hf_cb.connected_bda,sizeof(esp_bd_addr_t)); + btc_hf_cb_to_app(ESP_HF_BVRA_RESPONSE_EVT, ¶m); + if (p_data->val.num) { + btc_hf_connect_audio(&hf_local_param[idx].btc_hf_cb.connected_bda); + } else { + btc_hf_disconnect_audio(&hf_local_param[idx].btc_hf_cb.connected_bda); + } + } while (0); + break; + } + + case BTA_AG_SPK_EVT: + case BTA_AG_MIC_EVT: + { + idx = p_data->hdr.handle - 1; + CHECK_HF_IDX(idx); + do { + memcpy(param.volume_control.remote_addr, &hf_local_param[idx].btc_hf_cb.connected_bda,sizeof(esp_bd_addr_t)); + param.volume_control.type = (event == BTA_AG_SPK_EVT) ? ESP_HF_VOLUME_CONTROL_TARGET_SPK : ESP_HF_VOLUME_CONTROL_TARGET_MIC; + param.volume_control.volume = p_data->val.num; + btc_hf_cb_to_app(ESP_HF_VOLUME_CONTROL_EVT, ¶m); + } while (0); + break; + } + + case BTA_AG_AT_UNAT_EVT: + { + idx = p_data->hdr.handle - 1; + CHECK_HF_IDX(idx); + do { + memcpy(param.unat_rep.remote_addr, &hf_local_param[idx].btc_hf_cb.connected_bda,sizeof(esp_bd_addr_t)); + param.unat_rep.unat = p_data->val.str; + btc_hf_cb_to_app(ESP_HF_UNAT_RESPONSE_EVT, ¶m); + } while (0); + break; + } + + case BTA_AG_AT_CBC_EVT: + { + idx = p_data->hdr.handle - 1; + CHECK_HF_IDX(idx); + memcpy(param.ind_upd.remote_addr, &hf_local_param[idx].btc_hf_cb.connected_bda,sizeof(esp_bd_addr_t)); + btc_hf_cb_to_app(ESP_HF_IND_UPDATE_EVT, ¶m); + break; + } + + case BTA_AG_AT_CIND_EVT: + { + idx = p_data->hdr.handle - 1; + CHECK_HF_IDX(idx); + memcpy(param.cind_rep.remote_addr, &hf_local_param[idx].btc_hf_cb.connected_bda,sizeof(esp_bd_addr_t)); + btc_hf_cb_to_app(ESP_HF_CIND_RESPONSE_EVT, ¶m); + break; + } + + case BTA_AG_AT_COPS_EVT: + { + idx = p_data->hdr.handle - 1; + CHECK_HF_IDX(idx); + memcpy(param.cops_rep.remote_addr, &hf_local_param[idx].btc_hf_cb.connected_bda,sizeof(esp_bd_addr_t)); + btc_hf_cb_to_app(ESP_HF_COPS_RESPONSE_EVT, ¶m); + break; + } + + case BTA_AG_AT_CLCC_EVT: + { + idx = p_data->hdr.handle - 1; + CHECK_HF_IDX(idx); + memcpy(param.clcc_rep.remote_addr, &hf_local_param[idx].btc_hf_cb.connected_bda,sizeof(esp_bd_addr_t)); + btc_hf_cb_to_app(ESP_HF_CLCC_RESPONSE_EVT, ¶m); + break; + } + + case BTA_AG_AT_CNUM_EVT: + { + idx = p_data->hdr.handle - 1; + CHECK_HF_IDX(idx); + memcpy(param.cnum_rep.remote_addr, &hf_local_param[idx].btc_hf_cb.connected_bda,sizeof(esp_bd_addr_t)); + btc_hf_cb_to_app(ESP_HF_CNUM_RESPONSE_EVT, ¶m); + break; + } + + case BTA_AG_AT_VTS_EVT: + { + idx = p_data->hdr.handle - 1; + CHECK_HF_IDX(idx); + do { + memcpy(param.vts_rep.remote_addr, &hf_local_param[idx].btc_hf_cb.connected_bda,sizeof(esp_bd_addr_t)); + param.vts_rep.code = p_data->val.str; + btc_hf_cb_to_app(ESP_HF_VTS_RESPONSE_EVT, ¶m); + } while(0); + break; + } + + case BTA_AG_AT_NREC_EVT: + { + idx = p_data->hdr.handle - 1; + CHECK_HF_IDX(idx); + do { + memcpy(param.nrec.remote_addr, &hf_local_param[idx].btc_hf_cb.connected_bda,sizeof(esp_bd_addr_t)); + param.nrec.state = p_data->val.num; + btc_hf_cb_to_app(ESP_HF_NREC_RESPONSE_EVT, ¶m); + } while(0); + break; + } + + case BTA_AG_AT_A_EVT: + { + idx = p_data->hdr.handle - 1; + CHECK_HF_IDX(idx); + memcpy(param.ata_rep.remote_addr, &hf_local_param[idx].btc_hf_cb.connected_bda,sizeof(esp_bd_addr_t)); + btc_hf_cb_to_app(ESP_HF_ATA_RESPONSE_EVT, ¶m); + break; + } + + case BTA_AG_AT_CHUP_EVT: + { + idx = p_data->hdr.handle - 1; + CHECK_HF_IDX(idx); + memcpy(param.chup_rep.remote_addr, &hf_local_param[idx].btc_hf_cb.connected_bda,sizeof(esp_bd_addr_t)); + btc_hf_cb_to_app(ESP_HF_CHUP_RESPONSE_EVT, ¶m); + break; + } + + case BTA_AG_AT_BLDN_EVT: + case BTA_AG_AT_D_EVT: + { + idx = p_data->hdr.handle - 1; + CHECK_HF_IDX(idx); + do { + if (event == BTA_AG_AT_D_EVT) { // dial_number_or_memory + memcpy(param.out_call.remote_addr, &hf_local_param[idx].btc_hf_cb.connected_bda,sizeof(esp_bd_addr_t)); + param.out_call.type = p_data->val.value; + param.out_call.num_or_loc = osi_malloc((strlen(p_data->val.str) + 1) * sizeof(char)); + sprintf(param.out_call.num_or_loc, "%s", p_data->val.str); + btc_hf_cb_to_app(ESP_HF_DIAL_EVT, ¶m); + osi_free(param.out_call.num_or_loc); + } else if (event == BTA_AG_AT_BLDN_EVT) { //dial_last + memcpy(param.out_call.remote_addr, &hf_local_param[idx].btc_hf_cb.connected_bda,sizeof(esp_bd_addr_t)); + param.out_call.num_or_loc = NULL; + btc_hf_cb_to_app(ESP_HF_DIAL_EVT, ¶m); + } + } while(0); + break; + } + + case BTA_AG_AT_BINP_EVT: + case BTA_AG_AT_BTRH_EVT: + { + idx = p_data->hdr.handle - 1; + CHECK_HF_IDX(idx); + tBTA_AG_RES_DATA ag_res; + memset(&ag_res, 0, sizeof(ag_res)); + ag_res.ok_flag = BTA_AG_OK_ERROR; + ag_res.errcode = BTA_AG_ERR_OP_NOT_SUPPORTED; + BTA_AgResult(hf_local_param[idx].btc_hf_cb.handle, BTA_AG_UNAT_RES, &ag_res); + break; + } + + case BTA_AG_AT_BAC_EVT: + { + idx = p_data->hdr.handle - 1; + CHECK_HF_IDX(idx); + BTC_TRACE_DEBUG("AG Bitmap of peer-codecs %d", p_data->val.num); +#if (BTM_WBS_INCLUDED == TRUE) + /* If the peer supports mSBC and the BTC prefferred codec is also mSBC, then + ** we should set the BTA AG Codec to mSBC. This would trigger a +BCS to mSBC at the time + ** of SCO connection establishment */ + if ((btc_conf_hf_force_wbs == TRUE) && (p_data->val.num & BTA_AG_CODEC_MSBC)) { + BTC_TRACE_DEBUG("%s btc_hf override-Preferred Codec to MSBC", __FUNCTION__); + BTA_AgSetCodec(hf_local_param[idx].btc_hf_cb.handle,BTA_AG_CODEC_MSBC); + } + else { + BTC_TRACE_DEBUG("%s btc_hf override-Preferred Codec to CVSD", __FUNCTION__); + BTA_AgSetCodec(hf_local_param[idx].btc_hf_cb.handle,BTA_AG_CODEC_CVSD); + } +#endif + break; + } +#if (BTM_WBS_INCLUDED == TRUE) + case BTA_AG_WBS_EVT: + { + idx = p_data->hdr.handle - 1; + CHECK_HF_IDX(idx); + do { + BTC_TRACE_DEBUG("Set codec status %d codec %d 1=CVSD 2=MSBC", p_data->val.hdr.status, p_data->val.num); + memcpy(param.wbs_rep.remote_addr, &hf_local_param[idx].btc_hf_cb.connected_bda,sizeof(esp_bd_addr_t)); + param.wbs_rep.codec = p_data->val.num; + btc_hf_cb_to_app(ESP_HF_WBS_RESPONSE_EVT, ¶m); + } while (0); + break; + } + + case BTA_AG_AT_BCS_EVT: + { + idx = p_data->hdr.handle - 1; + CHECK_HF_IDX(idx); + do { + BTC_TRACE_DEBUG("AG final seleded codec is %d 1=CVSD 2=MSBC", p_data->val.num); + memcpy(param.bcs_rep.remote_addr, &hf_local_param[idx].btc_hf_cb.connected_bda,sizeof(esp_bd_addr_t)); + param.bcs_rep.mode = p_data->val.num; + /* No ESP_HF_WBS_NONE case, becuase HFP 1.6 supported device can send BCS */ + btc_hf_cb_to_app(ESP_HF_BCS_RESPONSE_EVT, ¶m); + } while (0); + break; + } +#endif + case BTA_AG_PKT_NUMS_GET_EVT: + { + memcpy(¶m.pkt_nums, &p_data->pkt_num, sizeof(struct ag_pkt_status_nums)); + btc_hf_cb_to_app(ESP_HF_PKT_STAT_NUMS_GET_EVT, ¶m); + break; + } + default: + BTC_TRACE_WARNING("%s: Unhandled event: %d", __FUNCTION__, event); + break; + } +} +#endif // #if (BTC_HF_INCLUDED == TRUE) diff --git a/lib/bt/host/bluedroid/btc/profile/std/hf_client/bta_hf_client_co.c b/lib/bt/host/bluedroid/btc/profile/std/hf_client/bta_hf_client_co.c new file mode 100644 index 00000000..555e7137 --- /dev/null +++ b/lib/bt/host/bluedroid/btc/profile/std/hf_client/bta_hf_client_co.c @@ -0,0 +1,505 @@ +/* + * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "bta/bta_hf_client_co.h" +#include "hci/hci_audio.h" +#include "btc_hf_client.h" +#include "osi/allocator.h" +#include + +#if (BTA_HF_INCLUDED == TRUE) + +#if (BTM_SCO_HCI_INCLUDED == TRUE) + +#include "oi_codec_sbc.h" +#include "oi_status.h" +#include "sbc_encoder.h" + +#if (PLC_INCLUDED == TRUE) +#include "sbc_plc.h" + +typedef struct { + bool first_good_frame_found; + sbc_plc_state_t plc_state; + int16_t sbc_plc_out[SBC_FS]; +} bta_hf_ct_plc_t; + +#if HFP_DYNAMIC_MEMORY == FALSE +static bta_hf_ct_plc_t bta_hf_ct_plc; +#else +static bta_hf_ct_plc_t *bta_hf_ct_plc_ptr; +#define bta_hf_ct_plc (*bta_hf_ct_plc_ptr) +#endif + +#endif ///(PLC_INCLUDED == TRUE) + + +#define HF_SBC_DEC_CONTEXT_DATA_LEN (CODEC_DATA_WORDS(1, SBC_CODEC_FAST_FILTER_BUFFERS)) +#define HF_SBC_DEC_RAW_DATA_SIZE 240 +#define HF_SBC_ENC_RAW_DATA_SIZE 240 + +/* BTA-HF-CO control block to map bdaddr to BTA handle */ +typedef struct +{ + OI_CODEC_SBC_DECODER_CONTEXT decoder_context; + OI_UINT32 decoder_context_data[HF_SBC_DEC_CONTEXT_DATA_LEN]; + OI_INT16 decode_raw_data[HF_SBC_DEC_RAW_DATA_SIZE]; + + SBC_ENC_PARAMS encoder; + + UINT8 sequence_number; + bool is_bad_frame; + bool decode_first_pkt; + OI_BYTE decode_msbc_data[BTM_MSBC_FRAME_SIZE]; + bool encode_first_pkt; + OI_BYTE encode_msbc_data[BTM_MSBC_FRAME_SIZE]; +} bta_hf_client_co_cb_t; + +#if HFP_DYNAMIC_MEMORY == FALSE +static bta_hf_client_co_cb_t bta_hf_client_co_cb; +#else +static bta_hf_client_co_cb_t *bta_hf_client_co_cb_ptr; +#define bta_hf_client_co_cb (*bta_hf_client_co_cb_ptr) +#endif /* HFP_DYNAMIC_MEMORY == FALSE */ + +static UINT8 hf_air_mode = BTM_SCO_AIR_MODE_UNKNOWN; +static UINT8 hf_inout_pkt_size = 0; + +/******************************************************************************* +** +** Function bta_hf_client_co_audio_state +** +** Description This function is called by the HF CLIENT before the audio connection +** is brought up, after it comes up, and after it goes down. +** +** Parameters handle - handle of the AG instance +** state - Audio state +** codec - if WBS support is compiled in, codec to going to be used is provided +** and when in SCO_STATE_SETUP, BTM_I2SPCMConfig() must be called with +** the correct platform parameters. +** in the other states codec type should not be ignored +** +** Returns void +** +*******************************************************************************/ +void bta_hf_client_co_audio_state(UINT16 handle, UINT8 state, tBTA_HFP_PEER_CODEC codec) +{ + switch (state) + { + case SCO_STATE_ON: + case SCO_STATE_OFF: + case SCO_STATE_OFF_TRANSFER: + case SCO_STATE_SETUP: + default: + break; + } +} + +/******************************************************************************* +** +** Function bta_hf_client_sco_co_init +** +** Description This function can be used by the phone to initialize audio +** codec or for other initialization purposes before SCO connection +** is opened. +** +** +** Returns Void. +** +*******************************************************************************/ +tBTA_HFP_SCO_ROUTE_TYPE bta_hf_client_sco_co_init(UINT32 rx_bw, UINT32 tx_bw, + tBTA_HFP_CODEC_INFO *p_codec_info, UINT8 app_id) +{ + APPL_TRACE_EVENT("%s rx_bw %d, tx_bw %d, codec %d", __FUNCTION__, rx_bw, tx_bw, + p_codec_info->codec_type); + return BTA_HFP_SCO_ROUTE_HCI; +} + +/******************************************************************************* + ** + ** Function bta_hf_dec_init + ** + ** Description Initialize decoding task + ** + ** Returns void + ** + *******************************************************************************/ +static void bta_hf_dec_init(void) { +#if (PLC_INCLUDED == TRUE) + sbc_plc_init(&(bta_hf_ct_plc.plc_state)); +#endif ///(PLC_INCLUDED == TRUE) + + OI_STATUS status; + + status = OI_CODEC_SBC_DecoderReset(&bta_hf_client_co_cb.decoder_context, bta_hf_client_co_cb.decoder_context_data, + HF_SBC_DEC_CONTEXT_DATA_LEN * sizeof(OI_UINT32), 1, 1, FALSE, TRUE); + if (!OI_SUCCESS(status)) { + APPL_TRACE_ERROR("OI_CODEC_SBC_DecoderReset failed with error code %d\n", status); + } +} + +/******************************************************************************* + ** + ** Function bta_hf_enc_init + ** + ** Description Initialize encoding task + ** + ** Returns void + ** + *******************************************************************************/ +static void bta_hf_enc_init(void) { + bta_hf_client_co_cb.sequence_number = 0; + bta_hf_client_co_cb.decode_first_pkt = true; + bta_hf_client_co_cb.encode_first_pkt = true; + bta_hf_client_co_cb.is_bad_frame = false; + + bta_hf_client_co_cb.encoder.sbc_mode = SBC_MODE_MSBC; + bta_hf_client_co_cb.encoder.s16NumOfBlocks = 15; + bta_hf_client_co_cb.encoder.s16NumOfSubBands = 8; + bta_hf_client_co_cb.encoder.s16AllocationMethod = SBC_LOUDNESS; + bta_hf_client_co_cb.encoder.s16BitPool = 26; + bta_hf_client_co_cb.encoder.s16ChannelMode = SBC_MONO; + bta_hf_client_co_cb.encoder.s16NumOfChannels = 1; + bta_hf_client_co_cb.encoder.s16SamplingFreq = SBC_sf16000; + + SBC_Encoder_Init(&(bta_hf_client_co_cb.encoder)); +} + +/******************************************************************************* +** +** Function bta_hf_client_sco_co_open +** +** Description This function is executed when a SCO connection is open. +** +** +** Returns void +** +*******************************************************************************/ +void bta_hf_client_sco_co_open(UINT16 handle, UINT8 air_mode, UINT8 inout_pkt_size, UINT16 event) +{ + APPL_TRACE_EVENT("%s hdl %x, pkt_sz %u, event %u", __FUNCTION__, handle, + inout_pkt_size, event); + + hf_air_mode = air_mode; + hf_inout_pkt_size = inout_pkt_size; + + if (air_mode == BTM_SCO_AIR_MODE_TRANSPNT) { +#if (HFP_DYNAMIC_MEMORY == TRUE) + bta_hf_client_co_cb_ptr = osi_calloc(sizeof(bta_hf_client_co_cb_t)); + if (!bta_hf_client_co_cb_ptr) { + APPL_TRACE_ERROR("%s allocate failed", __FUNCTION__); + goto error_exit; + } + +#if (PLC_INCLUDED == TRUE) + bta_hf_ct_plc_ptr = (bta_hf_ct_plc_t *)osi_calloc(sizeof(bta_hf_ct_plc_t)); + if (!bta_hf_ct_plc_ptr) { + APPL_TRACE_ERROR("%s malloc fail.", __FUNCTION__); + goto error_exit; + } +#endif ///(PLC_INCLUDED == TRUE) + +#endif /// (HFP_DYNAMIC_MEMORY == TRUE) + + bta_hf_dec_init(); + bta_hf_enc_init(); + + return; + } else { + return; + // Nothing to do + } + +#if (HFP_DYNAMIC_MEMORY == TRUE) +error_exit:; + hf_air_mode = BTM_SCO_AIR_MODE_UNKNOWN; + hf_inout_pkt_size = 0; + + if (bta_hf_client_co_cb_ptr) { + osi_free(bta_hf_client_co_cb_ptr); + bta_hf_client_co_cb_ptr = NULL; + } + +#if (PLC_INCLUDED == TRUE) + if (bta_hf_ct_plc_ptr) { + osi_free(bta_hf_ct_plc_ptr); + bta_hf_ct_plc_ptr = NULL; + } +#endif ///(PLC_INCLUDED == TRUE) +#endif /// (HFP_DYNAMIC_MEMORY == TRUE) + return; +} + +/******************************************************************************* +** +** Function bta_hf_client_sco_co_close +** +** Description This function is called when a SCO connection is closed +** +** +** Returns void +** +*******************************************************************************/ +void bta_hf_client_sco_co_close(void) +{ + APPL_TRACE_EVENT("%s", __FUNCTION__); + + if (hf_air_mode == BTM_SCO_AIR_MODE_TRANSPNT) { +#if (PLC_INCLUDED == TRUE) + sbc_plc_deinit(&(bta_hf_ct_plc.plc_state)); + bta_hf_ct_plc.first_good_frame_found = FALSE; + +#if (HFP_DYNAMIC_MEMORY == TRUE) + osi_free(bta_hf_ct_plc_ptr); + bta_hf_ct_plc_ptr = NULL; +#endif /// (HFP_DYNAMIC_MEMORY == TRUE) + +#endif ///(PLC_INCLUDED == TRUE) + +#if (HFP_DYNAMIC_MEMORY == TRUE) + osi_free(bta_hf_client_co_cb_ptr); + bta_hf_client_co_cb_ptr = NULL; +#endif /* HFP_DYNAMIC_MEMORY == TRUE */ + } else { + // Nothing to do + } + + hf_air_mode = BTM_SCO_AIR_MODE_UNKNOWN; + hf_inout_pkt_size = 0; +} + +/******************************************************************************* +** +** Function bta_hf_client_h2_header +** +** Description This function is called to fill in H2 header +** +** Returns void +** +*******************************************************************************/ +static void bta_hf_client_h2_header(UINT16 *p_buf) +{ + // H2: Header with synchronization word and sequence number +#define BTA_HF_H2_HEADER 0x0801 +#define BTA_HF_H2_HEADER_BIT0_MASK (1 << 0) +#define BTA_HF_H2_HEADER_BIT1_MASK (1 << 1) +#define BTA_HF_H2_HEADER_SN0_BIT_OFFSET1 12 +#define BTA_HF_H2_HEADER_SN0_BIT_OFFSET2 13 +#define BTA_HF_H2_HEADER_SN1_BIT_OFFSET1 14 +#define BTA_HF_H2_HEADER_SN1_BIT_OFFSET2 15 + + UINT16 h2_header = BTA_HF_H2_HEADER; + UINT8 h2_header_sn0 = bta_hf_client_co_cb.sequence_number & BTA_HF_H2_HEADER_BIT0_MASK; + UINT8 h2_header_sn1 = bta_hf_client_co_cb.sequence_number & BTA_HF_H2_HEADER_BIT1_MASK; + h2_header |= (h2_header_sn0 << BTA_HF_H2_HEADER_SN0_BIT_OFFSET1 + | h2_header_sn0 << BTA_HF_H2_HEADER_SN0_BIT_OFFSET2 + | h2_header_sn1 << (BTA_HF_H2_HEADER_SN1_BIT_OFFSET1 - 1) + | h2_header_sn1 << (BTA_HF_H2_HEADER_SN1_BIT_OFFSET2 - 1) + ); + + bta_hf_client_co_cb.sequence_number++; + *p_buf = h2_header; +} + +/******************************************************************************* +** +** Function bta_hf_client_sco_co_out_data +** +** Description This function is called to send SCO data over HCI. +** +** Returns number of bytes got from application +** +*******************************************************************************/ +uint32_t bta_hf_client_sco_co_out_data(UINT8 *p_buf) +{ + if (hf_air_mode == BTM_SCO_AIR_MODE_CVSD) { + // CVSD + uint32_t hf_raw_pkt_size = hf_inout_pkt_size; + return btc_hf_client_outgoing_data_cb_to_app(p_buf, hf_raw_pkt_size); + } else if (hf_air_mode == BTM_SCO_AIR_MODE_TRANSPNT) { + // mSBC +#if (HFP_DYNAMIC_MEMORY == TRUE) + if(bta_hf_client_co_cb_ptr == NULL) { + return 0; + } +#endif /* HFP_DYNAMIC_MEMORY == TRUE */ + if (hf_inout_pkt_size == BTM_MSBC_FRAME_SIZE / 2) { + if (bta_hf_client_co_cb.encode_first_pkt){ + UINT32 size = btc_hf_client_outgoing_data_cb_to_app((UINT8 *)bta_hf_client_co_cb.encoder.as16PcmBuffer, HF_SBC_ENC_RAW_DATA_SIZE); + if (size != HF_SBC_ENC_RAW_DATA_SIZE){ + return 0; + } + + bta_hf_client_h2_header((UINT16 *)bta_hf_client_co_cb.encode_msbc_data); + bta_hf_client_co_cb.encoder.pu8Packet = bta_hf_client_co_cb.encode_msbc_data + 2; + + SBC_Encoder(&bta_hf_client_co_cb.encoder); + memcpy(p_buf, bta_hf_client_co_cb.encode_msbc_data, hf_inout_pkt_size); + bta_hf_client_co_cb.encode_first_pkt = !bta_hf_client_co_cb.encode_first_pkt; + return hf_inout_pkt_size; + } else { + memcpy(p_buf, bta_hf_client_co_cb.encode_msbc_data + hf_inout_pkt_size, hf_inout_pkt_size); + bta_hf_client_co_cb.encode_first_pkt = !bta_hf_client_co_cb.encode_first_pkt; + return hf_inout_pkt_size; + } + + } else if (hf_inout_pkt_size == BTM_MSBC_FRAME_SIZE) { + UINT32 size = btc_hf_client_outgoing_data_cb_to_app((UINT8 *)bta_hf_client_co_cb.encoder.as16PcmBuffer, HF_SBC_ENC_RAW_DATA_SIZE); + if (size != HF_SBC_ENC_RAW_DATA_SIZE){ + return 0; + } + + bta_hf_client_h2_header((UINT16 *)p_buf); + bta_hf_client_co_cb.encoder.pu8Packet = p_buf + 2; + + SBC_Encoder(&bta_hf_client_co_cb.encoder); + return hf_inout_pkt_size; + } else { + //Never run to here. + } + + + + } else { + APPL_TRACE_ERROR("%s invaild air mode: %d", __FUNCTION__, hf_air_mode); + } + return 0; +} + +/******************************************************************************* +** +** Function bta_hf_client_decode_msbc_frame +** +** Description This function is called decode a mSBC frame +** +** Returns void +** +*******************************************************************************/ +static void bta_hf_client_decode_msbc_frame(UINT8 **data, UINT8 *length, BOOLEAN is_bad_frame){ + OI_STATUS status; + const OI_BYTE *zero_signal_frame_data; + UINT8 zero_signal_frame_len = BTM_MSBC_FRAME_DATA_SIZE; + UINT32 sbc_raw_data_size = HF_SBC_DEC_RAW_DATA_SIZE; + + if (is_bad_frame){ + status = OI_CODEC_SBC_CHECKSUM_MISMATCH; + } else { + status = OI_CODEC_SBC_DecodeFrame(&bta_hf_client_co_cb.decoder_context, (const OI_BYTE **)data, + (OI_UINT32 *)length, + (OI_INT16 *)bta_hf_client_co_cb.decode_raw_data, + (OI_UINT32 *)&sbc_raw_data_size); + } + +// PLC_INCLUDED will be set to TRUE when enabling Wide Band Speech +#if (PLC_INCLUDED == TRUE) + switch(status){ + case OI_OK: + bta_hf_ct_plc.first_good_frame_found = TRUE; + sbc_plc_good_frame(&(bta_hf_ct_plc.plc_state), (int16_t *)bta_hf_client_co_cb.decode_raw_data, bta_hf_ct_plc.sbc_plc_out); + case OI_CODEC_SBC_NOT_ENOUGH_HEADER_DATA: + case OI_CODEC_SBC_NOT_ENOUGH_BODY_DATA: + case OI_CODEC_SBC_NOT_ENOUGH_AUDIO_DATA: + break; + case OI_CODEC_SBC_NO_SYNCWORD: + case OI_CODEC_SBC_CHECKSUM_MISMATCH: + if (!bta_hf_ct_plc.first_good_frame_found) { + break; + } + zero_signal_frame_data = sbc_plc_zero_signal_frame(); + sbc_raw_data_size = HF_SBC_DEC_RAW_DATA_SIZE; + status = OI_CODEC_SBC_DecodeFrame(&bta_hf_client_co_cb.decoder_context, &zero_signal_frame_data, + (OI_UINT32 *)&zero_signal_frame_len, + (OI_INT16 *)bta_hf_client_co_cb.decode_raw_data, + (OI_UINT32 *)&sbc_raw_data_size); + sbc_plc_bad_frame(&(bta_hf_ct_plc.plc_state), bta_hf_client_co_cb.decode_raw_data, bta_hf_ct_plc.sbc_plc_out); + APPL_TRACE_DEBUG("bad frame, using PLC to fix it."); + break; + case OI_STATUS_INVALID_PARAMETERS: + // This caused by corrupt frames. + // The codec apparently does not recover from this. + // Re-initialize the codec. + APPL_TRACE_ERROR("Frame decode error: OI_STATUS_INVALID_PARAMETERS"); + + if (!OI_SUCCESS(OI_CODEC_SBC_DecoderReset(&bta_hf_client_co_cb.decoder_context, bta_hf_client_co_cb.decoder_context_data, + HF_SBC_DEC_CONTEXT_DATA_LEN * sizeof(OI_UINT32), 1, 1, FALSE, TRUE))) { + APPL_TRACE_ERROR("OI_CODEC_SBC_DecoderReset failed with error code %d\n", status); + } + break; + default: + APPL_TRACE_ERROR("Frame decode error: %d", status); + break; + } +#endif ///(PLC_INCLUDED == TRUE) + + if (OI_SUCCESS(status)){ + btc_hf_client_incoming_data_cb_to_app((const uint8_t *)(bta_hf_ct_plc.sbc_plc_out), sbc_raw_data_size); + } +} + +/******************************************************************************* +** +** Function bta_hf_client_sco_co_in_data +** +** Description This function is called to send incoming SCO data to application. +** +** Returns void +** +*******************************************************************************/ +void bta_hf_client_sco_co_in_data(BT_HDR *p_buf, tBTM_SCO_DATA_FLAG status) +{ + UINT8 *p = (UINT8 *)(p_buf + 1) + p_buf->offset; + UINT8 pkt_size = 0; + + STREAM_SKIP_UINT16(p); + STREAM_TO_UINT8 (pkt_size, p); + + if (hf_air_mode == BTM_SCO_AIR_MODE_CVSD) { + // CVSD + if(status != BTM_SCO_DATA_CORRECT){ + APPL_TRACE_DEBUG("%s: not a correct frame(%d).", __func__, status); + } + btc_hf_client_incoming_data_cb_to_app(p, pkt_size); + } else if (hf_air_mode == BTM_SCO_AIR_MODE_TRANSPNT) { + // mSBC + UINT8 *data = NULL; + + if (pkt_size != hf_inout_pkt_size) { + bta_hf_client_co_cb.is_bad_frame = true; + } + if (status != BTM_SCO_DATA_CORRECT) { + bta_hf_client_co_cb.is_bad_frame = true; + } + if (hf_inout_pkt_size == BTM_MSBC_FRAME_SIZE / 2) { + if (bta_hf_client_co_cb.decode_first_pkt){ + if (!bta_hf_client_co_cb.is_bad_frame){ + memcpy(bta_hf_client_co_cb.decode_msbc_data, p, pkt_size); + } + } else { + if (!bta_hf_client_co_cb.is_bad_frame){ + memcpy(bta_hf_client_co_cb.decode_msbc_data + BTM_MSBC_FRAME_SIZE / 2, p, pkt_size); + } + + data = bta_hf_client_co_cb.decode_msbc_data; + bta_hf_client_decode_msbc_frame(&data, &pkt_size, bta_hf_client_co_cb.is_bad_frame); + bta_hf_client_co_cb.is_bad_frame = false; + } + bta_hf_client_co_cb.decode_first_pkt = !bta_hf_client_co_cb.decode_first_pkt; + + } else if (hf_inout_pkt_size == BTM_MSBC_FRAME_SIZE) { + data = p; + bta_hf_client_decode_msbc_frame(&data, &pkt_size, bta_hf_client_co_cb.is_bad_frame); + bta_hf_client_co_cb.is_bad_frame = false; + } else { + //Never run to here. + } + } else { + APPL_TRACE_ERROR("%s invaild air mode: %d", __FUNCTION__, hf_air_mode); + } +} + +#endif /* #if (BTM_SCO_HCI_INCLUDED == TRUE) */ + +#endif /* #if (BTA_HF_INCLUDED == TRUE) */ diff --git a/lib/bt/host/bluedroid/btc/profile/std/hf_client/btc_hf_client.c b/lib/bt/host/bluedroid/btc/profile/std/hf_client/btc_hf_client.c new file mode 100644 index 00000000..67973bc2 --- /dev/null +++ b/lib/bt/host/bluedroid/btc/profile/std/hf_client/btc_hf_client.c @@ -0,0 +1,1180 @@ +/* + * SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/****************************************************************************** + ** + ** Name: btc_hf_client.c + ** + ******************************************************************************/ +#include "common/bt_target.h" +#include "common/bt_trace.h" +#include +#include +#include +#include +#include "common/bt_defs.h" +#include "device/bdaddr.h" +#include "btc/btc_dm.h" +#include "btc_hf_client.h" +#include "btc/btc_profile_queue.h" +#include "osi/allocator.h" +#include "btc/btc_manage.h" +#include "btc/btc_util.h" +#include "esp_hf_client_api.h" +#include "bta/bta_hf_client_api.h" +#if (BT_CONTROLLER_INCLUDED == TRUE) +#include "esp_bt.h" +#endif +#include + +#if BT_HF_CLIENT_BQB_INCLUDED +static BOOLEAN s_bta_hf_client_bqb_esco_s4_flag = false; +#endif /* BT_HF_CLIENT_BQB_INCLUDED */ + +#if (BTC_HF_CLIENT_INCLUDED == TRUE) + +/************************************************************************************ +** Constants & Macros +************************************************************************************/ + +#ifndef BTC_HF_CLIENT_SERVICE_NAME +#define BTC_HF_CLIENT_SERVICE_NAME ("Handsfree") +#endif + +#ifndef BTC_HF_CLIENT_SECURITY +#define BTC_HF_CLIENT_SECURITY (BTA_SEC_AUTHENTICATE | BTA_SEC_ENCRYPT) +#endif + +#ifndef BTC_HF_CLIENT_FEATURES +#define BTC_HF_CLIENT_FEATURES ( BTA_HF_CLIENT_FEAT_ECNR | \ + BTA_HF_CLIENT_FEAT_3WAY | \ + BTA_HF_CLIENT_FEAT_CLI | \ + BTA_HF_CLIENT_FEAT_VREC | \ + BTA_HF_CLIENT_FEAT_VOL | \ + BTA_HF_CLIENT_FEAT_ECS | \ + BTA_HF_CLIENT_FEAT_ECC | \ + BTA_HF_CLIENT_FEAT_CODEC) +#endif + + +/******************************************************************************* +** +** Function bta_hf_client_bqb_esco_s4_ctrl +** +** Description Control the usage of BTA_HF_CLIENT_FEAT_ESCO_S4 for BQB test +** +** Returns void +** +*******************************************************************************/ +#if BT_HF_CLIENT_BQB_INCLUDED +void bta_hf_client_bqb_esco_s4_ctrl(BOOLEAN enable) +{ + s_bta_hf_client_bqb_esco_s4_flag = enable; +} +#endif /* BT_HF_CLIENT_BQB_INCLUDED */ + +/************************************************************************************ +** Static variables +************************************************************************************/ +const int btc_hf_client_version = HFP_HF_VERSION_1_7; + +#if HFP_DYNAMIC_MEMORY == FALSE +static hf_client_local_param_t hf_client_local_param; +#else +hf_client_local_param_t *hf_client_local_param_ptr; +#endif + +/************************************************************************************ +** Static functions +************************************************************************************/ +#define CHECK_HF_CLIENT_INIT() do { \ +if (! hf_client_local_param.btc_hf_client_cb.initialized) { \ + return BT_STATUS_NOT_READY; \ +} \ +} while (0) + +#define CHECK_HF_CLIENT_SLC_CONNECTED() do { \ +if (! hf_client_local_param.btc_hf_client_cb.initialized || \ + hf_client_local_param.btc_hf_client_cb.state != ESP_HF_CLIENT_CONNECTION_STATE_SLC_CONNECTED) { \ + return BT_STATUS_NOT_READY; \ +} \ +} while (0) + +static inline void btc_hf_client_cb_to_app(esp_hf_client_cb_event_t event, esp_hf_client_cb_param_t *param) +{ + esp_hf_client_cb_t btc_hf_client_callback = (esp_hf_client_cb_t)btc_profile_cb_get(BTC_PID_HF_CLIENT); + if (btc_hf_client_callback) { + btc_hf_client_callback(event, param); + } +} + +static void clear_state(void) +{ + memset(&hf_client_local_param.btc_hf_client_cb, 0, sizeof(btc_hf_client_cb_t)); +} + +static BOOLEAN is_connected(bt_bdaddr_t *bd_addr) +{ + if (((hf_client_local_param.btc_hf_client_cb.state == ESP_HF_CLIENT_CONNECTION_STATE_CONNECTED) || + (hf_client_local_param.btc_hf_client_cb.state == ESP_HF_CLIENT_CONNECTION_STATE_SLC_CONNECTED))&& + ((bd_addr == NULL) || (bdcmp(bd_addr->address, hf_client_local_param.btc_hf_client_cb.connected_bda.address) == 0))) + return TRUE; + return FALSE; +} + +void btc_hf_client_reg_data_cb(esp_hf_client_incoming_data_cb_t recv, + esp_hf_client_outgoing_data_cb_t send) +{ + hf_client_local_param.btc_hf_client_incoming_data_cb = recv; + hf_client_local_param.btc_hf_client_outgoing_data_cb = send; +} + +void btc_hf_client_incoming_data_cb_to_app(const uint8_t *data, uint32_t len) +{ + // todo: critical section protection + if (hf_client_local_param.btc_hf_client_incoming_data_cb) { + hf_client_local_param.btc_hf_client_incoming_data_cb(data, len); + } +} + +uint32_t btc_hf_client_outgoing_data_cb_to_app(uint8_t *data, uint32_t len) +{ + // todo: critical section protection + if (hf_client_local_param.btc_hf_client_outgoing_data_cb) { + return hf_client_local_param.btc_hf_client_outgoing_data_cb(data, len); + } else { + return 0; + } +} + +/***************************************************************************** +** +** btc hf api functions +** +*****************************************************************************/ + +/******************************************************************************* +** +** Function btc_hf_client_init +** +** Description initializes the hf interface +** +** Returns bt_status_t +** +*******************************************************************************/ +bt_status_t btc_hf_client_init(void) +{ + BTC_TRACE_EVENT("%s", __FUNCTION__); + + btc_dm_enable_service(BTA_HFP_HS_SERVICE_ID); + + clear_state(); + + hf_client_local_param.btc_hf_client_cb.initialized = true; + +#if (BT_CONTROLLER_INCLUDED == TRUE) +#if BTM_SCO_HCI_INCLUDED + uint8_t data_path = ESP_SCO_DATA_PATH_HCI; +#else + uint8_t data_path = ESP_SCO_DATA_PATH_PCM; +#endif + esp_bredr_sco_datapath_set(data_path); +#endif + return BT_STATUS_SUCCESS; +} + + +/******************************************************************************* +** +** Function btc_hf_client_connect +** +** Description connect to audio gateway +** +** Returns bt_status_t +** +*******************************************************************************/ +static bt_status_t connect_int( bt_bdaddr_t *bd_addr, uint16_t uuid ) +{ + if (is_connected(bd_addr)) { + return BT_STATUS_BUSY; + } + + hf_client_local_param.btc_hf_client_cb.state = ESP_HF_CLIENT_CONNECTION_STATE_CONNECTING; + bdcpy(hf_client_local_param.btc_hf_client_cb.connected_bda.address, bd_addr->address); + + BTA_HfClientOpen(hf_client_local_param.btc_hf_client_cb.handle, hf_client_local_param.btc_hf_client_cb.connected_bda.address, + BTC_HF_CLIENT_SECURITY); + + return BT_STATUS_SUCCESS; +} + + +bt_status_t btc_hf_client_connect( bt_bdaddr_t *bd_addr ) +{ + BTC_TRACE_EVENT("HFP Client version is 0x%04x", btc_hf_client_version); + CHECK_HF_CLIENT_INIT(); + return btc_queue_connect(UUID_SERVCLASS_HF_HANDSFREE, bd_addr, connect_int); +} + +/******************************************************************************* +** +** Function btc_hf_client_deinit +** +** Description Closes the HF interface +** +** Returns bt_status_t +** +*******************************************************************************/ +void btc_hf_client_deinit( void ) +{ + BTC_TRACE_EVENT("%s", __FUNCTION__); + + btc_dm_disable_service(BTA_HFP_HS_SERVICE_ID); + + hf_client_local_param.btc_hf_client_cb.initialized = false; +} + +/******************************************************************************* +** +** Function btc_hf_client_disconnect +** +** Description disconnect from audio gateway +** +** Returns bt_status_t +** +*******************************************************************************/ +bt_status_t btc_hf_client_disconnect( bt_bdaddr_t *bd_addr ) +{ + CHECK_HF_CLIENT_INIT(); + + if (is_connected(bd_addr)) + { + BTA_HfClientClose(hf_client_local_param.btc_hf_client_cb.handle); + return BT_STATUS_SUCCESS; + } + + return BT_STATUS_FAIL; +} + +/******************************************************************************* +** +** Function btc_hf_client_connect_audio +** +** Description create an audio connection +** +** Returns bt_status_t +** +*******************************************************************************/ +bt_status_t btc_hf_client_connect_audio( bt_bdaddr_t *bd_addr ) +{ + CHECK_HF_CLIENT_SLC_CONNECTED(); + + if (is_connected(bd_addr)) + { + if (hf_client_local_param.btc_hf_client_cb.peer_feat & BTA_HF_CLIENT_PEER_CODEC) + { + BTA_HfClientSendAT(hf_client_local_param.btc_hf_client_cb.handle, BTA_HF_CLIENT_AT_CMD_BCC, 0, 0, NULL); + } + else + { + BTA_HfClientAudioOpen(hf_client_local_param.btc_hf_client_cb.handle); + } + + /* Inform the application that the audio connection has been initiated successfully */ + do { + esp_hf_client_cb_param_t param; + memset(¶m, 0, sizeof(esp_hf_client_cb_param_t)); + param.audio_stat.state = ESP_HF_CLIENT_AUDIO_STATE_CONNECTING; + memcpy(param.audio_stat.remote_bda, &hf_client_local_param.btc_hf_client_cb.connected_bda, sizeof(esp_bd_addr_t)); + btc_hf_client_cb_to_app(ESP_HF_CLIENT_AUDIO_STATE_EVT, ¶m); + } while (0); + + return BT_STATUS_SUCCESS; + } + + return BT_STATUS_FAIL; +} + +/******************************************************************************* +** +** Function btc_hf_client_disconnect_audio +** +** Description close the audio connection +** +** Returns bt_status_t +** +*******************************************************************************/ +bt_status_t btc_hf_client_disconnect_audio( bt_bdaddr_t *bd_addr ) +{ + CHECK_HF_CLIENT_SLC_CONNECTED(); + + if (is_connected(bd_addr)) + { + BTA_HfClientAudioClose(hf_client_local_param.btc_hf_client_cb.handle); + return BT_STATUS_SUCCESS; + } + + return BT_STATUS_FAIL; +} + +/******************************************************************************* +** +** Function btc_hf_client_start_voice_recognition +** +** Description start voice recognition +** +** Returns bt_status_t +** +*******************************************************************************/ +static bt_status_t btc_hf_client_start_voice_recognition(void) +{ + CHECK_HF_CLIENT_SLC_CONNECTED(); + + if (hf_client_local_param.btc_hf_client_cb.peer_feat & BTA_HF_CLIENT_PEER_FEAT_VREC) + { + BTA_HfClientSendAT(hf_client_local_param.btc_hf_client_cb.handle, BTA_HF_CLIENT_AT_CMD_BVRA, 1, 0, NULL); + + return BT_STATUS_SUCCESS; + } + + return BT_STATUS_UNSUPPORTED; +} + + +/******************************************************************************* +** +** Function btc_hf_client_stop_voice_recognition +** +** Description stop voice recognition +** +** Returns bt_status_t +** +*******************************************************************************/ +static bt_status_t btc_hf_client_stop_voice_recognition(void) +{ + CHECK_HF_CLIENT_SLC_CONNECTED(); + + if (hf_client_local_param.btc_hf_client_cb.peer_feat & BTA_HF_CLIENT_PEER_FEAT_VREC) + { + BTA_HfClientSendAT(hf_client_local_param.btc_hf_client_cb.handle, BTA_HF_CLIENT_AT_CMD_BVRA, 0, 0, NULL); + + return BT_STATUS_SUCCESS; + } + + return BT_STATUS_UNSUPPORTED; +} + +/******************************************************************************* +** +** Function btc_hf_client_volume_update +** +** Description volume update +** +** Returns bt_status_t +** +*******************************************************************************/ +static bt_status_t btc_hf_client_volume_update(esp_hf_volume_control_target_t type, int volume) +{ + CHECK_HF_CLIENT_SLC_CONNECTED(); + + switch (type) + { + case ESP_HF_VOLUME_CONTROL_TARGET_SPK: + BTA_HfClientSendAT(hf_client_local_param.btc_hf_client_cb.handle, BTA_HF_CLIENT_AT_CMD_VGS, volume, 0, NULL); + break; + case ESP_HF_VOLUME_CONTROL_TARGET_MIC: + BTA_HfClientSendAT(hf_client_local_param.btc_hf_client_cb.handle, BTA_HF_CLIENT_AT_CMD_VGM, volume, 0, NULL); + break; + default: + return BT_STATUS_UNSUPPORTED; + } + + return BT_STATUS_SUCCESS; +} + +/******************************************************************************* +** +** Function btc_hf_client_dial +** +** Description place a call +** +** Returns bt_status_t +** +*******************************************************************************/ +static bt_status_t btc_hf_client_dial(const char *number) +{ + CHECK_HF_CLIENT_SLC_CONNECTED(); + + if (strlen(number) != 0) + { + BTA_HfClientSendAT(hf_client_local_param.btc_hf_client_cb.handle, BTA_HF_CLIENT_AT_CMD_ATD, 0, 0, number); + } + else + { + BTA_HfClientSendAT(hf_client_local_param.btc_hf_client_cb.handle, BTA_HF_CLIENT_AT_CMD_BLDN, 0, 0, NULL); + } + + return BT_STATUS_SUCCESS; +} + +/******************************************************************************* +** +** Function dial_memory +** +** Description place a call with number specified by location (speed dial) +** +** Returns bt_status_t +** +*******************************************************************************/ +static bt_status_t btc_hf_client_dial_memory(int location) +{ + CHECK_HF_CLIENT_SLC_CONNECTED(); + + BTA_HfClientSendAT(hf_client_local_param.btc_hf_client_cb.handle, BTA_HF_CLIENT_AT_CMD_ATD, location, 0, NULL); + + return BT_STATUS_SUCCESS; +} + +static bt_status_t btc_hf_client_send_chld_cmd(esp_hf_chld_type_t type, int idx) +{ + CHECK_HF_CLIENT_SLC_CONNECTED(); + + switch (type) + { + case ESP_HF_CHLD_TYPE_REL: + if (hf_client_local_param.btc_hf_client_cb.chld_feat & BTA_HF_CLIENT_CHLD_REL) + { + BTA_HfClientSendAT(hf_client_local_param.btc_hf_client_cb.handle, BTA_HF_CLIENT_AT_CMD_CHLD, 0, 0, NULL); + break; + } + return BT_STATUS_UNSUPPORTED; + case ESP_HF_CHLD_TYPE_REL_ACC: + // CHLD 1 is mandatory for 3 way calling + if (hf_client_local_param.btc_hf_client_cb.peer_feat & BTA_HF_CLIENT_PEER_FEAT_3WAY) + { + BTA_HfClientSendAT(hf_client_local_param.btc_hf_client_cb.handle, BTA_HF_CLIENT_AT_CMD_CHLD, 1, 0, NULL); + break; + } + return BT_STATUS_UNSUPPORTED; + case ESP_HF_CHLD_TYPE_HOLD_ACC: + // CHLD 2 is mandatory for 3 way calling + if (hf_client_local_param.btc_hf_client_cb.peer_feat & BTA_HF_CLIENT_PEER_FEAT_3WAY) + { + BTA_HfClientSendAT(hf_client_local_param.btc_hf_client_cb.handle, BTA_HF_CLIENT_AT_CMD_CHLD, 2, 0, NULL); + break; + } + return BT_STATUS_UNSUPPORTED; + case ESP_HF_CHLD_TYPE_MERGE: + if (hf_client_local_param.btc_hf_client_cb.chld_feat & BTA_HF_CLIENT_CHLD_MERGE) + { + BTA_HfClientSendAT(hf_client_local_param.btc_hf_client_cb.handle, BTA_HF_CLIENT_AT_CMD_CHLD, 3, 0, NULL); + break; + } + return BT_STATUS_UNSUPPORTED; + case ESP_HF_CHLD_TYPE_MERGE_DETACH: + if (hf_client_local_param.btc_hf_client_cb.chld_feat & BTA_HF_CLIENT_CHLD_MERGE_DETACH) + { + BTA_HfClientSendAT(hf_client_local_param.btc_hf_client_cb.handle, BTA_HF_CLIENT_AT_CMD_CHLD, 4, 0, NULL); + break; + } + return BT_STATUS_UNSUPPORTED; + case ESP_HF_CHLD_TYPE_REL_X: + if (hf_client_local_param.btc_hf_client_cb.peer_feat & BTA_HF_CLIENT_PEER_ECC) + { + if (idx < 1) + { + return BT_STATUS_FAIL; + } + BTA_HfClientSendAT(hf_client_local_param.btc_hf_client_cb.handle, BTA_HF_CLIENT_AT_CMD_CHLD, 1, idx, NULL); + break; + } + return BT_STATUS_UNSUPPORTED; + case ESP_HF_CHLD_TYPE_PRIV_X: + if (hf_client_local_param.btc_hf_client_cb.peer_feat & BTA_HF_CLIENT_PEER_ECC) + { + if (idx < 1) + { + return BT_STATUS_FAIL; + } + BTA_HfClientSendAT(hf_client_local_param.btc_hf_client_cb.handle, BTA_HF_CLIENT_AT_CMD_CHLD, 2, idx, NULL); + break; + } + return BT_STATUS_UNSUPPORTED; + + } + return BT_STATUS_SUCCESS; +} + +static bt_status_t btc_hf_client_send_btrh_cmd(esp_hf_btrh_cmd_t btrh) +{ + CHECK_HF_CLIENT_SLC_CONNECTED(); + + switch (btrh) { + case ESP_HF_BTRH_CMD_HOLD: + BTA_HfClientSendAT(hf_client_local_param.btc_hf_client_cb.handle, BTA_HF_CLIENT_AT_CMD_BTRH, 0, 0, NULL); + break; + case ESP_HF_BTRH_CMD_ACCEPT: + BTA_HfClientSendAT(hf_client_local_param.btc_hf_client_cb.handle, BTA_HF_CLIENT_AT_CMD_BTRH, 1, 0, NULL); + break; + case ESP_HF_BTRH_CMD_REJECT: + BTA_HfClientSendAT(hf_client_local_param.btc_hf_client_cb.handle, BTA_HF_CLIENT_AT_CMD_BTRH, 2, 0, NULL); + break; + default: + return BT_STATUS_FAIL; + } + + return BT_STATUS_SUCCESS; +} + +static bt_status_t btc_hf_client_answer_call(void) +{ + CHECK_HF_CLIENT_SLC_CONNECTED(); + BTA_HfClientSendAT(hf_client_local_param.btc_hf_client_cb.handle, BTA_HF_CLIENT_AT_CMD_ATA, 0, 0, NULL); + return BT_STATUS_SUCCESS; +} + +static bt_status_t btc_hf_client_reject_call(void) +{ + CHECK_HF_CLIENT_SLC_CONNECTED(); + BTA_HfClientSendAT(hf_client_local_param.btc_hf_client_cb.handle, BTA_HF_CLIENT_AT_CMD_CHUP, 0, 0, NULL); + return BT_STATUS_SUCCESS; +} + +/******************************************************************************* +** +** Function btc_hf_client_query_current_calls +** +** Description query list of current calls +** +** Returns bt_status_t +** +*******************************************************************************/ +static bt_status_t btc_hf_client_query_current_calls(void) +{ + CHECK_HF_CLIENT_SLC_CONNECTED(); + + if (hf_client_local_param.btc_hf_client_cb.peer_feat & BTA_HF_CLIENT_PEER_ECS) + { + BTA_HfClientSendAT(hf_client_local_param.btc_hf_client_cb.handle, BTA_HF_CLIENT_AT_CMD_CLCC, 0, 0, NULL); + + return BT_STATUS_SUCCESS; + } + + return BT_STATUS_UNSUPPORTED; +} + +/******************************************************************************* +** +** Function btc_hf_client_query_current_operator_name +** +** Description query current selected operator name +** +** Returns bt_status_t +** +*******************************************************************************/ +static bt_status_t btc_hf_client_query_current_operator_name(void) +{ + CHECK_HF_CLIENT_SLC_CONNECTED(); + + BTA_HfClientSendAT(hf_client_local_param.btc_hf_client_cb.handle, BTA_HF_CLIENT_AT_CMD_COPS, 0, 0, NULL); + + return BT_STATUS_SUCCESS; +} + +/******************************************************************************* +** +** Function btc_hf_client_retieve_subscriber_info +** +** Description retrieve subscriber number information +** +** Returns bt_status_t +** +*******************************************************************************/ +static bt_status_t btc_hf_client_retrieve_subscriber_info(void) +{ + CHECK_HF_CLIENT_SLC_CONNECTED(); + + BTA_HfClientSendAT(hf_client_local_param.btc_hf_client_cb.handle, BTA_HF_CLIENT_AT_CMD_CNUM, 0, 0, NULL); + + return BT_STATUS_SUCCESS; +} + +/******************************************************************************* +** +** Function btc_hf_client_send_dtmf +** +** Description send dtmf +** +** Returns bt_status_t +** +*******************************************************************************/ +static bt_status_t btc_hf_client_send_dtmf(char code) +{ + CHECK_HF_CLIENT_SLC_CONNECTED(); + + BTA_HfClientSendAT(hf_client_local_param.btc_hf_client_cb.handle, BTA_HF_CLIENT_AT_CMD_VTS, code, 0, NULL); + + return BT_STATUS_SUCCESS; +} + +/******************************************************************************* +** +** Function btc_hf_client_send_xapl +** +** Description send xapl +** +** Returns bt_status_t +** +*******************************************************************************/ +static bt_status_t btc_hf_client_send_xapl(char *buf, UINT32 features) +{ + CHECK_HF_CLIENT_SLC_CONNECTED(); + + BTA_HfClientSendAT(hf_client_local_param.btc_hf_client_cb.handle, BTA_HF_CLIENT_AT_CMD_XAPL, features, 0, buf); + + return BT_STATUS_SUCCESS; +} + +/******************************************************************************* +** +** Function btc_hf_client_send_iphoneaccev +** +** Description send IPHONEACCEV +** +** Returns bt_status_t +** +*******************************************************************************/ +static bt_status_t btc_hf_client_send_iphoneaccev(uint32_t bat_level, BOOLEAN docked) +{ + CHECK_HF_CLIENT_SLC_CONNECTED(); + + BTA_HfClientSendAT(hf_client_local_param.btc_hf_client_cb.handle, BTA_HF_CLIENT_AT_CMD_IPHONEACCEV, bat_level, (UINT32)docked, NULL); + + return BT_STATUS_SUCCESS; +} +/******************************************************************************* +** +** Function btc_hf_client_request_last_voice_tag_number +** +** Description Request number from AG for VR purposes +** +** Returns bt_status_t +** +*******************************************************************************/ +static bt_status_t btc_hf_client_request_last_voice_tag_number(void) +{ + CHECK_HF_CLIENT_SLC_CONNECTED(); + + if (hf_client_local_param.btc_hf_client_cb.peer_feat & BTA_HF_CLIENT_PEER_VTAG) + { + BTA_HfClientSendAT(hf_client_local_param.btc_hf_client_cb.handle, BTA_HF_CLIENT_AT_CMD_BINP, 1, 0, NULL); + + return BT_STATUS_SUCCESS; + } + + return BT_STATUS_UNSUPPORTED; +} + +/******************************************************************************* +** +** Function btc_hf_client_send_nrec +** +** Description Request AG to disable echo cancellation & noise reduction +** +** Returns bt_status_t +** +*******************************************************************************/ +static bt_status_t btc_hf_client_send_nrec(void) +{ + CHECK_HF_CLIENT_SLC_CONNECTED(); + + if (hf_client_local_param.btc_hf_client_cb.peer_feat & BTA_HF_CLIENT_PEER_FEAT_ECNR) + { + BTA_HfClientSendAT(hf_client_local_param.btc_hf_client_cb.handle, BTA_HF_CLIENT_AT_CMD_NREC, 0, 0, NULL); + return BT_STATUS_SUCCESS; + } + return BT_STATUS_UNSUPPORTED; +} + +/******************************************************************************* +** +** Function btc_hf_client_pkt_stat_nums_get; +** +** Description Request packet status numbers for a specific (e)SCO connection handle +** +** Returns bt_status_t +** +*******************************************************************************/ +static bt_status_t btc_hf_client_pkt_stat_nums_get(UINT16 sync_conn_handle) +{ + CHECK_HF_CLIENT_SLC_CONNECTED(); +#if (BTM_SCO_HCI_INCLUDED == TRUE) + BTA_HfClientPktStatsNumsGet(sync_conn_handle); +#endif /*#if (BTM_SCO_HCI_INCLUDED == TRUE) */ + return BT_STATUS_SUCCESS; +} + +/******************************************************************************* +** +** Function bte_hf_client_evt +** +** Description Switches context from BTE to BTIF for all HF Client events +** +** Returns void +** +*******************************************************************************/ +static void bte_hf_client_evt(tBTA_HF_CLIENT_EVT event, void *p_data) +{ + bt_status_t stat; + btc_msg_t msg; + int arg_len = BTA_HfClientGetCbDataSize(event); + void *arg = (p_data != NULL && arg_len > 0) ? p_data : NULL; + + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_HF_CLIENT; + msg.act = (uint8_t) event; + + stat = btc_transfer_context(&msg, arg, arg_len, NULL, NULL); + + if (stat) { + BTC_TRACE_ERROR("%s transfer failed\n", __func__); + } +} + +/******************************************************************************* +** +** Function btc_hf_client_execute_service +** +** Description Initializes/Shuts down the service +** +** Returns BT_STATUS_SUCCESS on success, BT_STATUS_FAIL otherwise +** +*******************************************************************************/ +bt_status_t btc_hf_client_execute_service(BOOLEAN b_enable) +{ + BTC_TRACE_EVENT("%s enable:%d", __FUNCTION__, b_enable); + + if (b_enable) + { + /* Enable and register with BTA-HFClient */ + BTA_HfClientEnable(bte_hf_client_evt); + hf_client_local_param.btc_hf_client_features = BTC_HF_CLIENT_FEATURES; + if (btc_hf_client_version >= HFP_HF_VERSION_1_7) + { + hf_client_local_param.btc_hf_client_features |= BTA_HF_CLIENT_FEAT_ESCO_S4; +#if BT_HF_CLIENT_BQB_INCLUDED + if (s_bta_hf_client_bqb_esco_s4_flag == true) { + hf_client_local_param.btc_hf_client_features = BTA_HF_CLIENT_FEAT_ESCO_S4; + } +#endif /* BT_HF_CLIENT_BQB_INCLUDED */ + BTC_TRACE_EVENT("eSCO S4 Setting Supported"); + + } + else if (btc_hf_client_version >= HFP_HF_VERSION_1_6) + { + BTC_TRACE_EVENT("No eSCO S4 Setting Supported"); + } + else + { + BTC_TRACE_EVENT("No Codec Nego Supported"); + hf_client_local_param.btc_hf_client_features = hf_client_local_param.btc_hf_client_features & (~BTA_HF_CLIENT_FEAT_CODEC); + } + BTC_TRACE_EVENT("hf_client_local_param.btc_hf_client_features is %d", hf_client_local_param.btc_hf_client_features); + BTA_HfClientRegister(BTC_HF_CLIENT_SECURITY, hf_client_local_param.btc_hf_client_features, + BTC_HF_CLIENT_SERVICE_NAME); + } + else + { + BTA_HfClientDeregister(hf_client_local_param.btc_hf_client_cb.handle); + BTA_HfClientDisable(); + } + return BT_STATUS_SUCCESS; +} + +static void process_ind_evt(tBTA_HF_CLIENT_IND *ind) +{ + esp_hf_client_cb_param_t param; + memset(¶m, 0, sizeof(esp_hf_client_cb_param_t)); + + switch (ind->type) + { + case BTA_HF_CLIENT_IND_CALL: + param.call.status = ind->value; + btc_hf_client_cb_to_app(ESP_HF_CLIENT_CIND_CALL_EVT, ¶m); + break; + + case BTA_HF_CLIENT_IND_CALLSETUP: + param.call_setup.status = ind->value; + btc_hf_client_cb_to_app(ESP_HF_CLIENT_CIND_CALL_SETUP_EVT, ¶m); + break; + case BTA_HF_CLIENT_IND_CALLHELD: + param.call_held.status = ind->value; + btc_hf_client_cb_to_app(ESP_HF_CLIENT_CIND_CALL_HELD_EVT, ¶m); + break; + + case BTA_HF_CLIENT_IND_SERVICE: + param.service_availability.status = ind->value; + btc_hf_client_cb_to_app(ESP_HF_CLIENT_CIND_SERVICE_AVAILABILITY_EVT, ¶m); + break; + + case BTA_HF_CLIENT_IND_SIGNAL: + param.signal_strength.value = ind->value; + btc_hf_client_cb_to_app(ESP_HF_CLIENT_CIND_SIGNAL_STRENGTH_EVT, ¶m); + break; + + case BTA_HF_CLIENT_IND_ROAM: + param.roaming.status = ind->value; + btc_hf_client_cb_to_app(ESP_HF_CLIENT_CIND_ROAMING_STATUS_EVT, ¶m); + break; + + case BTA_HF_CLIENT_IND_BATTCH: + param.battery_level.value = ind->value; + btc_hf_client_cb_to_app(ESP_HF_CLIENT_CIND_BATTERY_LEVEL_EVT, ¶m); + break; + + default: + break; + } +} + +void btc_hf_client_cb_handler(btc_msg_t *msg) +{ + uint16_t event = msg->act; + tBTA_HF_CLIENT *p_data = (tBTA_HF_CLIENT *)msg->arg; + esp_hf_client_cb_param_t param; + bdstr_t bdstr; + + memset(¶m, 0, sizeof(esp_hf_client_cb_param_t)); + + switch (event) + { + case BTA_HF_CLIENT_ENABLE_EVT: + case BTA_HF_CLIENT_DISABLE_EVT: + break; + case BTA_HF_CLIENT_REGISTER_EVT: + hf_client_local_param.btc_hf_client_cb.handle = p_data->reg.handle; + break; + case BTA_HF_CLIENT_OPEN_EVT: + if (p_data->open.status == BTA_HF_CLIENT_SUCCESS) + { + bdcpy(hf_client_local_param.btc_hf_client_cb.connected_bda.address, p_data->open.bd_addr); + hf_client_local_param.btc_hf_client_cb.state = ESP_HF_CLIENT_CONNECTION_STATE_CONNECTED; + hf_client_local_param.btc_hf_client_cb.peer_feat = 0; + hf_client_local_param.btc_hf_client_cb.chld_feat = 0; + //clear_phone_state(); + } + else if (hf_client_local_param.btc_hf_client_cb.state == ESP_HF_CLIENT_CONNECTION_STATE_CONNECTING) + { + hf_client_local_param.btc_hf_client_cb.state = ESP_HF_CLIENT_CONNECTION_STATE_DISCONNECTED; + } + else + { + BTC_TRACE_WARNING("%s: HF CLient open failed, but another device connected. status=%d state=%d connected device=%s", + __FUNCTION__, p_data->open.status, hf_client_local_param.btc_hf_client_cb.state, bdaddr_to_string(&hf_client_local_param.btc_hf_client_cb.connected_bda, bdstr, sizeof(bdstr))); + UNUSED(bdstr); + break; + } + + do { + param.conn_stat.state = hf_client_local_param.btc_hf_client_cb.state; + param.conn_stat.peer_feat = 0; + param.conn_stat.chld_feat = 0; + + memcpy(param.conn_stat.remote_bda, &hf_client_local_param.btc_hf_client_cb.connected_bda, + sizeof(esp_bd_addr_t)); + + btc_hf_client_cb_to_app(ESP_HF_CLIENT_CONNECTION_STATE_EVT, ¶m); + } while (0); + + if (hf_client_local_param.btc_hf_client_cb.state == ESP_HF_CLIENT_CONNECTION_STATE_DISCONNECTED) + bdsetany(hf_client_local_param.btc_hf_client_cb.connected_bda.address); + + if (p_data->open.status != BTA_HF_CLIENT_SUCCESS) { + btc_queue_advance(); + } + + break; + + case BTA_HF_CLIENT_CONN_EVT: + hf_client_local_param.btc_hf_client_cb.peer_feat = p_data->conn.peer_feat; + hf_client_local_param.btc_hf_client_cb.chld_feat = p_data->conn.chld_feat; + hf_client_local_param.btc_hf_client_cb.state = ESP_HF_CLIENT_CONNECTION_STATE_SLC_CONNECTED; + + do { + param.conn_stat.state = hf_client_local_param.btc_hf_client_cb.state; + param.conn_stat.peer_feat = hf_client_local_param.btc_hf_client_cb.peer_feat; + param.conn_stat.chld_feat = hf_client_local_param.btc_hf_client_cb.chld_feat; + + memcpy(param.conn_stat.remote_bda, &hf_client_local_param.btc_hf_client_cb.connected_bda, + sizeof(esp_bd_addr_t)); + + btc_hf_client_cb_to_app(ESP_HF_CLIENT_CONNECTION_STATE_EVT, ¶m); + } while (0); + + /* Inform the application about in-band ringtone */ + if (hf_client_local_param.btc_hf_client_cb.peer_feat & BTA_HF_CLIENT_PEER_INBAND) + { + do { + param.bsir.state = ESP_HF_CLIENT_IN_BAND_RINGTONE_PROVIDED; + btc_hf_client_cb_to_app(ESP_HF_CLIENT_BSIR_EVT, ¶m); + } while (0); + } + + btc_queue_advance(); + break; + + case BTA_HF_CLIENT_CLOSE_EVT: + hf_client_local_param.btc_hf_client_cb.state = ESP_HF_CLIENT_CONNECTION_STATE_DISCONNECTED; + do { + param.conn_stat.state = ESP_HF_CLIENT_CONNECTION_STATE_DISCONNECTED; + param.conn_stat.peer_feat = 0; + param.conn_stat.chld_feat = 0; + + memcpy(param.conn_stat.remote_bda, &hf_client_local_param.btc_hf_client_cb.connected_bda, + sizeof(esp_bd_addr_t)); + + btc_hf_client_cb_to_app(ESP_HF_CLIENT_CONNECTION_STATE_EVT, ¶m); + } while (0); + + bdsetany(hf_client_local_param.btc_hf_client_cb.connected_bda.address); + hf_client_local_param.btc_hf_client_cb.peer_feat = 0; + hf_client_local_param.btc_hf_client_cb.chld_feat = 0; + btc_queue_advance(); + break; + case BTA_HF_CLIENT_IND_EVT: + process_ind_evt(&p_data->ind); + break; + case BTA_HF_CLIENT_MIC_EVT: + do { + param.volume_control.type = ESP_HF_VOLUME_CONTROL_TARGET_MIC; + param.volume_control.volume = p_data->val.value; + + btc_hf_client_cb_to_app(ESP_HF_CLIENT_VOLUME_CONTROL_EVT, ¶m); + } while (0); + break; + case BTA_HF_CLIENT_SPK_EVT: + do { + param.volume_control.type = ESP_HF_VOLUME_CONTROL_TARGET_SPK; + param.volume_control.volume = p_data->val.value; + + btc_hf_client_cb_to_app(ESP_HF_CLIENT_VOLUME_CONTROL_EVT, ¶m); + } while (0); + break; + case BTA_HF_CLIENT_VOICE_REC_EVT: + do { + param.bvra.value = p_data->val.value; + + btc_hf_client_cb_to_app(ESP_HF_CLIENT_BVRA_EVT, ¶m); + } while (0); + break; + case BTA_HF_CLIENT_OPERATOR_NAME_EVT: + do { + param.cops.name = p_data->operator.name; + btc_hf_client_cb_to_app(ESP_HF_CLIENT_COPS_CURRENT_OPERATOR_EVT, ¶m); + } while (0); + break; + case BTA_HF_CLIENT_CLIP_EVT: + do { + param.clip.number = p_data->number.number; + btc_hf_client_cb_to_app(ESP_HF_CLIENT_CLIP_EVT, ¶m); + } while (0); + break; + case BTA_HF_CLIENT_BINP_EVT: + do { + param.binp.number = p_data->number.number; + btc_hf_client_cb_to_app(ESP_HF_CLIENT_BINP_EVT, ¶m); + } while (0); + break; + case BTA_HF_CLIENT_CCWA_EVT: + do { + param.ccwa.number = p_data->number.number; + btc_hf_client_cb_to_app(ESP_HF_CLIENT_CCWA_EVT, ¶m); + } while (0); + break; + case BTA_HF_CLIENT_AT_RESULT_EVT: + do { + param.at_response.code = p_data->result.type; + param.at_response.cme = p_data->result.cme; + + btc_hf_client_cb_to_app(ESP_HF_CLIENT_AT_RESPONSE_EVT, ¶m); + } while (0); + break; + case BTA_HF_CLIENT_CLCC_EVT: + do { + param.clcc.idx = p_data->clcc.idx; + param.clcc.dir = p_data->clcc.inc ? ESP_HF_CURRENT_CALL_DIRECTION_INCOMING : + ESP_HF_CURRENT_CALL_DIRECTION_OUTGOING; + param.clcc.status = p_data->clcc.status; + param.clcc.mpty = p_data->clcc.mpty ? ESP_HF_CURRENT_CALL_MPTY_TYPE_MULTI : + ESP_HF_CURRENT_CALL_MPTY_TYPE_SINGLE; + param.clcc.number = p_data->clcc.number_present ? p_data->clcc.number : NULL; + btc_hf_client_cb_to_app(ESP_HF_CLIENT_CLCC_EVT, ¶m); + } while (0); + break; + case BTA_HF_CLIENT_CNUM_EVT: + do { + param.cnum.number = p_data->cnum.number; + if (p_data->cnum.service == 4) { + param.cnum.type = ESP_HF_SUBSCRIBER_SERVICE_TYPE_VOICE; + } else if (p_data->cnum.service == 5) { + param.cnum.type = ESP_HF_SUBSCRIBER_SERVICE_TYPE_FAX; + } else { + param.cnum.type = ESP_HF_SUBSCRIBER_SERVICE_TYPE_UNKNOWN; + } + btc_hf_client_cb_to_app(ESP_HF_CLIENT_CNUM_EVT, ¶m); + break; + } while (0); + break; + case BTA_HF_CLIENT_BTRH_EVT: + if (p_data->val.value <= ESP_HF_BTRH_STATUS_REJECTED) { + do { + param.btrh.status = p_data->val.value; + + btc_hf_client_cb_to_app(ESP_HF_CLIENT_BTRH_EVT, ¶m); + } while (0); + } + break; + case BTA_HF_CLIENT_BSIR_EVT: + if (p_data->val.value != 0) + { + param.bsir.state = ESP_HF_CLIENT_IN_BAND_RINGTONE_PROVIDED; + } + else + { + param.bsir.state = ESP_HF_CLIENT_IN_BAND_RINGTONE_NOT_PROVIDED; + } + btc_hf_client_cb_to_app(ESP_HF_CLIENT_BSIR_EVT, ¶m); + break; + case BTA_HF_CLIENT_AUDIO_OPEN_EVT: + do { + param.audio_stat.state = ESP_HF_CLIENT_AUDIO_STATE_CONNECTED; + memcpy(param.audio_stat.remote_bda, &hf_client_local_param.btc_hf_client_cb.connected_bda, + sizeof(esp_bd_addr_t)); + param.audio_stat.sync_conn_handle = p_data->hdr.sync_conn_handle; + btc_hf_client_cb_to_app(ESP_HF_CLIENT_AUDIO_STATE_EVT, ¶m); + } while (0); + break; + case BTA_HF_CLIENT_AUDIO_MSBC_OPEN_EVT: + do { + param.audio_stat.state = ESP_HF_CLIENT_AUDIO_STATE_CONNECTED_MSBC; + memcpy(param.audio_stat.remote_bda, &hf_client_local_param.btc_hf_client_cb.connected_bda, + sizeof(esp_bd_addr_t)); + param.audio_stat.sync_conn_handle = p_data->hdr.sync_conn_handle; + btc_hf_client_cb_to_app(ESP_HF_CLIENT_AUDIO_STATE_EVT, ¶m); + } while (0); + break; + case BTA_HF_CLIENT_AUDIO_CLOSE_EVT: + do { + param.audio_stat.state = ESP_HF_CLIENT_AUDIO_STATE_DISCONNECTED; + memcpy(param.audio_stat.remote_bda, &hf_client_local_param.btc_hf_client_cb.connected_bda, + sizeof(esp_bd_addr_t)); + param.audio_stat.sync_conn_handle = p_data->hdr.sync_conn_handle; + btc_hf_client_cb_to_app(ESP_HF_CLIENT_AUDIO_STATE_EVT, ¶m); + } while (0); + break; + case BTA_HF_CLIENT_RING_INDICATION: + do { + btc_hf_client_cb_to_app(ESP_HF_CLIENT_RING_IND_EVT, NULL); + } while (0); + break; + case BTA_HF_CLIENT_PKT_STAT_NUMS_GET_EVT: + { + memcpy(¶m.pkt_nums, &p_data->pkt_num, sizeof(struct hf_client_pkt_status_nums)); + btc_hf_client_cb_to_app(ESP_HF_CLIENT_PKT_STAT_NUMS_GET_EVT, ¶m); + break; + } + default: + BTC_TRACE_WARNING("%s: Unhandled event: %d", __FUNCTION__, event); + break; + } +} + +void btc_hf_client_call_handler(btc_msg_t *msg) +{ + btc_hf_client_args_t *arg = (btc_hf_client_args_t *)(msg->arg); + switch (msg->act) { + + case BTC_HF_CLIENT_INIT_EVT: + btc_hf_client_init(); + break; + case BTC_HF_CLIENT_DEINIT_EVT: + btc_hf_client_deinit(); + break; + case BTC_HF_CLIENT_CONNECT_EVT: + btc_hf_client_connect(&arg->connect); + break; + case BTC_HF_CLIENT_DISCONNECT_EVT: + btc_hf_client_disconnect(&arg->disconnect); + break; + case BTC_HF_CLIENT_CONNECT_AUDIO_EVT: + btc_hf_client_connect_audio(&arg->connect_audio); + break; + case BTC_HF_CLIENT_DISCONNECT_AUDIO_EVT: + btc_hf_client_disconnect_audio(&arg->disconnect_audio); + break; + case BTC_HF_CLIENT_START_VOICE_RECOGNITION_EVT: + btc_hf_client_start_voice_recognition(); + break; + case BTC_HF_CLIENT_STOP_VOICE_RECOGNITION_EVT: + btc_hf_client_stop_voice_recognition(); + break; + case BTC_HF_CLIENT_VOLUME_UPDATE_EVT: + btc_hf_client_volume_update(arg->volume_update.type, arg->volume_update.volume); + break; + case BTC_HF_CLIENT_DIAL_EVT: + btc_hf_client_dial(arg->dial.number); + break; + case BTC_HF_CLIENT_DIAL_MEMORY_EVT: + btc_hf_client_dial_memory(arg->dial_memory.location); + break; + case BTC_HF_CLIENT_SEND_CHLD_CMD_EVT: + btc_hf_client_send_chld_cmd(arg->chld.type, arg->chld.idx); + break; + case BTC_HF_CLIENT_SEND_BTRH_CMD_EVT: + btc_hf_client_send_btrh_cmd(arg->btrh.cmd); + break; + case BTC_HF_CLIENT_ANSWER_CALL_EVT: + btc_hf_client_answer_call(); + break; + case BTC_HF_CLIENT_REJECT_CALL_EVT: + btc_hf_client_reject_call(); + break; + case BTC_HF_CLIENT_QUERY_CURRENT_CALLS_EVT: + btc_hf_client_query_current_calls(); + break; + case BTC_HF_CLIENT_QUERY_CURRENT_OPERATOR_NAME_EVT: + btc_hf_client_query_current_operator_name(); + break; + case BTC_HF_CLIENT_RETRIEVE_SUBSCRIBER_INFO_EVT: + btc_hf_client_retrieve_subscriber_info(); + break; + case BTC_HF_CLIENT_SEND_DTMF_EVT: + btc_hf_client_send_dtmf(arg->send_dtmf.code); + break; + case BTC_HF_CLIENT_REQUEST_LAST_VOICE_TAG_NUMBER_EVT: + btc_hf_client_request_last_voice_tag_number(); + break; + case BTC_HF_CLIENT_REGISTER_DATA_CALLBACK_EVT: + btc_hf_client_reg_data_cb(arg->reg_data_cb.recv, arg->reg_data_cb.send); + break; + case BTC_HF_CLIENT_SEND_NREC_EVT: + btc_hf_client_send_nrec(); + break; + case BTC_HF_CLIENT_SEND_XAPL_EVT: + btc_hf_client_send_xapl(arg->send_xapl.information, arg->send_xapl.features); + break; + case BTC_HF_CLIENT_SEND_IPHONEACCEV_EVT: + btc_hf_client_send_iphoneaccev(arg->send_iphoneaccev.bat_level, arg->send_iphoneaccev.docked); + break; + case BTC_HF_CLIENT_REQUEST_PKT_STAT_EVT: + btc_hf_client_pkt_stat_nums_get(arg->pkt_sync_hd.sync_conn_handle); + break; + default: + BTC_TRACE_WARNING("%s : unhandled event: %d\n", __FUNCTION__, msg->act); + } +} + +#endif /* #if (BTC_HF_CLIENT_INCLUDED == TRUE) */ diff --git a/lib/bt/host/bluedroid/btc/profile/std/hid/bta_hh_co.c b/lib/bt/host/bluedroid/btc/profile/std/hid/bta_hh_co.c new file mode 100644 index 00000000..066c70b6 --- /dev/null +++ b/lib/bt/host/bluedroid/btc/profile/std/hid/bta_hh_co.c @@ -0,0 +1,152 @@ +#include "btc_hh.h" +#include "osi/allocator.h" +#include "string.h" +#if HID_HOST_INCLUDED == TRUE + +/******************************************************************************* + * + * Function bta_hh_co_open + * + * Description When connection is opened, this call-out function is executed + * by HH to do platform specific initialization. + * + * Returns void. + ******************************************************************************/ +void bta_hh_co_open(uint8_t dev_handle, uint8_t sub_class, tBTA_HH_ATTR_MASK attr_mask, uint8_t app_id) +{ + uint32_t i; + btc_hh_device_t *p_dev = NULL; + + if (dev_handle == BTA_HH_INVALID_HANDLE) { + APPL_TRACE_WARNING("%s: Oops, dev_handle (%d) is invalid...", __func__, dev_handle); + return; + } + + for (i = 0; i < BTC_HH_MAX_HID; i++) { + p_dev = &btc_hh_cb.devices[i]; + if (p_dev->dev_status != ESP_HIDH_CONN_STATE_UNKNOWN && p_dev->dev_handle == dev_handle) { + // We found a device with the same handle. Must be a device reconnected. + APPL_TRACE_WARNING("%s: Found an existing device with the same handle dev_status=%d, " + "dev_handle=0x%2x, attr_mask=0x%04x, sub_class=0x%02x, app_id=%d", + __func__, p_dev->dev_status, dev_handle, p_dev->attr_mask, p_dev->sub_class, + p_dev->app_id); + break; + } + p_dev = NULL; + } + + if (p_dev == NULL) { + // Did not find a device reconnection case. Find an empty slot now. + for (i = 0; i < BTC_HH_MAX_HID; i++) { + if (btc_hh_cb.devices[i].dev_status == ESP_HIDH_CONN_STATE_UNKNOWN) { + p_dev = &btc_hh_cb.devices[i]; + p_dev->dev_handle = dev_handle; + p_dev->attr_mask = attr_mask; + p_dev->sub_class = sub_class; + p_dev->app_id = app_id; + p_dev->local_vup = false; + + btc_hh_cb.device_num++; + break; + } + } + } + + if (p_dev == NULL) { + APPL_TRACE_ERROR("%s: Error: too many HID devices are connected", __func__); + return; + } + + p_dev->dev_status = ESP_HIDH_CONN_STATE_CONNECTED; + APPL_TRACE_DEBUG("%s: Return device status %d", __func__, p_dev->dev_status); +} + +/******************************************************************************* + * + * Function bta_hh_co_close + * + * Description When connection is closed, this call-out function is executed + * by HH to do platform specific finalization. + * + * Parameters dev_handle - device handle + * app_id - application id + * + * Returns void. + ******************************************************************************/ +void bta_hh_co_close(uint8_t dev_handle, uint8_t app_id) +{ + uint32_t i; + btc_hh_device_t *p_dev = NULL; + + APPL_TRACE_WARNING("%s: dev_handle = %d, app_id = %d", __func__, dev_handle, app_id); + if (dev_handle == BTA_HH_INVALID_HANDLE) { + APPL_TRACE_WARNING("%s: Oops, dev_handle (%d) is invalid...", __func__, dev_handle); + return; + } + + for (i = 0; i < BTC_HH_MAX_HID; i++) { + p_dev = &btc_hh_cb.devices[i]; + if (p_dev->dev_status != ESP_HIDH_CONN_STATE_UNKNOWN && p_dev->dev_handle == dev_handle) { + APPL_TRACE_WARNING("%s: Found an existing device with the same handle " + "dev_status = %d, dev_handle =%d", + __func__, p_dev->dev_status, p_dev->dev_handle); + break; + } + } +} + +/******************************************************************************* + * + * Function bta_hh_co_data + * + * Description This function is executed by BTA when HID host receive a + * data report on interrupt channel. + * + * Parameters dev_handle - device handle + * *p_rpt - pointer to the report data + * len - length of report data + * mode - Hid host Protocol Mode + * sub_clas - Device Subclass + * app_id - application id + * + * Returns void + ******************************************************************************/ +void bta_hh_co_data(UINT8 dev_handle, UINT8 *p_rpt, UINT16 len, tBTA_HH_PROTO_MODE mode, UINT8 sub_class, UINT8 ctry_code, + BD_ADDR peer_addr, UINT8 app_id) +{ + btc_msg_t msg; + tBTA_HH p_data; + BT_HDR *p_buf = NULL; + bt_status_t status; + tBTA_HH_STATUS ret = BTA_HH_OK; + + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_HH; + msg.act = BTA_HH_DATA_IND_EVT; + + APPL_TRACE_DEBUG("%s: dev_handle = %d, subclass = 0x%02X, mode = %d, " + "ctry_code = %d, app_id = %d", + __func__, dev_handle, sub_class, mode, ctry_code, app_id); + + do { + if ((p_buf = osi_malloc(sizeof(BT_HDR) + len)) == NULL) { + APPL_TRACE_ERROR("%s malloc failed!", __func__); + ret = BTA_HH_ERR_NO_RES; + break; + } + p_buf->offset = 0; + p_buf->len = len; + p_buf->event = 0; + p_buf->layer_specific = dev_handle; + memcpy(p_buf->data, p_rpt, len); + } while (0); + + p_data.int_data.status = ret; + p_data.int_data.handle = dev_handle; + p_data.int_data.p_data = p_buf; + p_data.int_data.proto_mode = mode; + status = btc_transfer_context(&msg, &p_data, sizeof(tBTA_HH), NULL, NULL); + assert(status == BT_STATUS_SUCCESS); +} + +#endif /* HID_HOST_INCLUDED == TRUE */ diff --git a/lib/bt/host/bluedroid/btc/profile/std/hid/btc_hd.c b/lib/bt/host/bluedroid/btc/profile/std/hid/btc_hd.c new file mode 100644 index 00000000..d280f8f7 --- /dev/null +++ b/lib/bt/host/bluedroid/btc/profile/std/hid/btc_hd.c @@ -0,0 +1,964 @@ +/****************************************************************************** + * + * Copyright (C) 2016 The Android Open Source Project + * Copyright (C) 2009-2012 Broadcom Corporation + * Copyright (C) 2019 Blake Felt + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ +/************************************************************************************ + * + * Filename: btc_hd.c + * + * Description: HID Device Profile Bluetooth Interface + * + * + ***********************************************************************************/ + +#include +#include +#include + +#include "bta/bta_api.h" +#include "bta/bta_hd_api.h" +#include "bta/bta_hh_api.h" +#include "bta/utl.h" +#include "btc/btc_storage.h" +#include "btc/btc_util.h" +#include "btc/btc_manage.h" +#include "btc_hd.h" + +#include "osi/allocator.h" + +#include "esp_hidd_api.h" + +#if HID_DEV_INCLUDED == TRUE +#include "bta_dm_int.h" + +/* HD request events */ +typedef enum { BTC_HD_DUMMY_REQ_EVT = 0 } btc_hd_req_evt_t; + +/******************************************************************************* + * Static variables + ******************************************************************************/ +btc_hd_cb_t btc_hd_cb = {0}; + +// static tBTA_HD_APP_INFO app_info; +// static tBTA_HD_QOS_INFO in_qos; +// static tBTA_HD_QOS_INFO out_qos; + +/****************************************************************************** + * Constants & Macros + *****************************************************************************/ +#define BTC_HD_APP_NAME_LEN 50 +#define BTC_HD_APP_DESCRIPTION_LEN 50 +#define BTC_HD_APP_PROVIDER_LEN 50 +#define BTC_HD_APP_DESCRIPTOR_LEN 2048 +#define COD_HID_KEYBOARD 0x0540 +#define COD_HID_POINTING 0x0580 +#define COD_HID_COMBO 0x05C0 +#define COD_HID_MAJOR 0x0500 + +#define is_hidd_init() (btc_hd_cb.status > BTC_HD_DISABLED) +#define is_hidd_app_register() (btc_hd_cb.app_registered) + +typedef void (bt_hid_copy_cb_t)(btc_msg_t *msg, void *p_dest, void *p_src); + +static inline void btc_hd_cb_to_app(esp_hidd_cb_event_t event, esp_hidd_cb_param_t *param) +{ + esp_hd_cb_t btc_hd_cb = (esp_hd_cb_t)btc_profile_cb_get(BTC_PID_HD); + if (btc_hd_cb) { + btc_hd_cb(event, param); + } +} + +static void free_app_info_param(void) +{ + utl_freebuf((void **)&btc_hd_cb.app_info.descriptor.dsc_list); + utl_freebuf((void **)&btc_hd_cb.app_info.p_provider); + utl_freebuf((void **)&btc_hd_cb.app_info.p_description); + utl_freebuf((void **)&btc_hd_cb.app_info.p_name); +} + +static void bte_hd_arg_deep_copy(btc_msg_t *msg, void *p_dst, void *p_src) +{ + tBTA_HD *p_dst_data = (tBTA_HD *)p_dst; + tBTA_HD *p_src_data = (tBTA_HD *)p_src; + switch (msg->act) + { + case BTA_HD_SET_REPORT_EVT: { + uint8_t *src_data = p_src_data->set_report.p_data; + if (src_data) { + p_dst_data->set_report.p_data = osi_malloc(p_src_data->set_report.len); + if (p_dst_data->set_report.p_data == NULL) { + BTC_TRACE_ERROR("%s malloc set_report data failed!", __func__); + break; + } + memcpy(p_dst_data->set_report.p_data, src_data, p_src_data->set_report.len); + } + break; + } + case BTA_HD_INTR_DATA_EVT: { + uint8_t *src_data = p_src_data->intr_data.p_data; + if (src_data) { + p_dst_data->intr_data.p_data = osi_malloc(p_src_data->intr_data.len); + if (p_dst_data->intr_data.p_data == NULL) { + BTC_TRACE_ERROR("%s malloc intr_data data failed!", __func__); + break; + } + memcpy(p_dst_data->intr_data.p_data, src_data, p_src_data->intr_data.len); + } + break; + } + default: + break; + } +} + +/******************************************************************************* + * + * Function btc_hd_remove_device + * + * Description Removes plugged device + * + * Returns void + * + ******************************************************************************/ +void btc_hd_remove_device(bt_bdaddr_t bd_addr) +{ + BTA_HdRemoveDevice((uint8_t *)&bd_addr); + // btc_storage_remove_hidd(&bd_addr); +} + +/******************************************************************************* + * + * Function bte_hd_evt + * + * Description Switches context from BTE to BTC for all BT-HD events + * + * Returns void + * + ******************************************************************************/ +static void bte_hd_evt(tBTA_HD_EVT event, tBTA_HD *p_data) +{ + bt_status_t status; + int param_len = 0; + + BTC_TRACE_API("%s event=%d", __func__, event); + + switch (event) { + case BTA_HD_ENABLE_EVT: + case BTA_HD_DISABLE_EVT: + case BTA_HD_UNREGISTER_APP_EVT: + param_len = sizeof(tBTA_HD_STATUS); + break; + case BTA_HD_REGISTER_APP_EVT: + param_len = sizeof(tBTA_HD_REG_STATUS); + break; + case BTA_HD_OPEN_EVT: + case BTA_HD_CLOSE_EVT: + case BTA_HD_VC_UNPLUG_EVT: + param_len = sizeof(tBTA_HD_CONN); + break; + case BTA_HD_GET_REPORT_EVT: + param_len += sizeof(tBTA_HD_GET_REPORT); + break; + case BTA_HD_SET_REPORT_EVT: + param_len = sizeof(tBTA_HD_SET_REPORT); + break; + case BTA_HD_SET_PROTOCOL_EVT: + param_len += sizeof(p_data->set_protocol); + break; + case BTA_HD_INTR_DATA_EVT: + param_len = sizeof(tBTA_HD_INTR_DATA); + break; + case BTA_HD_SEND_REPORT_EVT: + param_len = sizeof(tBTA_HD_API_SEND_REPORT); + break; + case BTA_HD_REPORT_ERR_EVT: + param_len = sizeof(tBTA_HD_API_REPORT_ERR); + break; + } + + btc_msg_t msg; + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_HD; + msg.act = event; + + status = btc_transfer_context(&msg, p_data, param_len, bte_hd_arg_deep_copy, btc_hd_cb_arg_deep_free); + if (status != BT_STATUS_SUCCESS) { + BTC_TRACE_ERROR("context transfer failed"); + } +} + +/******************************************************************************* + * + * Function btc_hd_init + * + * Description Initializes BT-HD interface + * + * Returns void + * + ******************************************************************************/ +static void btc_hd_init(void) +{ + BTC_TRACE_API("%s", __func__); + esp_hidd_status_t ret = ESP_HIDD_SUCCESS; + do { + if (is_hidd_init()) { + BTC_TRACE_ERROR("%s HD has been initiated, shall uninit first!", __func__); + ret = ESP_HIDD_NEED_DEINIT; + break; + } + memset(&btc_hd_cb, 0, sizeof(btc_hd_cb)); + /* enable HD */ + BTA_HdEnable(bte_hd_evt); + } while (0); + + if (ret != ESP_HIDD_SUCCESS) { + esp_hidd_cb_param_t param; + param.init.status = ret; + btc_hd_cb_to_app(ESP_HIDD_INIT_EVT, ¶m); + } +} + +/******************************************************************************* + * + * Function btc_hd_deinit + * + * Description de-initializes the hd interface + * + * Returns void + * + ******************************************************************************/ +static void btc_hd_unregister_app(bool need_deinit); +static void btc_hd_deinit(void) +{ + BTC_TRACE_API("%s", __func__); + esp_hidd_status_t ret = ESP_HIDD_SUCCESS; + do { + if (!is_hidd_init()) { + BTC_TRACE_ERROR("%s HD has not been initiated, shall init first!", __func__); + ret = ESP_HIDD_NEED_INIT; + break; + } + + if (btc_hd_cb.status == BTC_HD_DISABLING) { + BTC_TRACE_ERROR("%s is disabling, try later!", __func__); + ret = ESP_HIDD_BUSY; + break; + } + + btc_hd_cb.service_dereg_active = FALSE; + // unresgister app will also relase the connection + // and disable after receiving unregister event from lower layer + if (is_hidd_app_register()) { + btc_hd_unregister_app(true); + } else { + btc_hd_cb.status = BTC_HD_DISABLING; + BTC_TRACE_WARNING("%s disabling hid device service now", __func__); + BTA_HdDisable(); + } + } while (0); + + if (ret != ESP_HIDD_SUCCESS) { + esp_hidd_cb_param_t param; + param.deinit.status = ret; + btc_hd_cb_to_app(ESP_HIDD_DEINIT_EVT, ¶m); + } +} + +/******************************************************************************* + * + * Function btc_hd_register_app + * + * Description Registers HID Device application + * + * Returns void + * + ******************************************************************************/ +static void btc_hd_register_app(esp_hidd_app_param_t *p_app_param, esp_hidd_qos_param_t *p_in_qos, + esp_hidd_qos_param_t *p_out_qos) +{ + BTC_TRACE_API("%s", __func__); + esp_hidd_status_t ret = ESP_HIDD_SUCCESS; + do { + if (!is_hidd_init()) { + BTC_TRACE_ERROR("%s HD has not been initiated, shall init first!", __func__); + ret = ESP_HIDD_NEED_INIT; + break; + } else if (btc_hd_cb.status == BTC_HD_DISABLING) { + BTC_TRACE_ERROR("%s: deinit is in progress!", __func__); + ret = ESP_HIDD_BUSY; + break; + } + + if (is_hidd_app_register()) { + BTC_TRACE_ERROR("%s: application already registered, shall deregister first!", __func__); + ret = ESP_HIDD_NEED_DEREG; + break; + } + + if ((btc_hd_cb.app_info.p_name = (char *)osi_malloc(BTC_HD_APP_NAME_LEN)) == NULL || + (btc_hd_cb.app_info.p_description = (char *)osi_malloc(BTC_HD_APP_DESCRIPTION_LEN)) == NULL || + (btc_hd_cb.app_info.p_provider = (char *)osi_malloc(BTC_HD_APP_PROVIDER_LEN)) == NULL || + (btc_hd_cb.app_info.descriptor.dsc_list = (uint8_t *)osi_malloc(p_app_param->desc_list_len)) == NULL) { + BTC_TRACE_ERROR( + "%s malloc app_info failed! p_name:%p, p_description:%p, p_provider:%p, descriptor.dsc_list:%p", + __func__, btc_hd_cb.app_info.p_name, btc_hd_cb.app_info.p_description, btc_hd_cb.app_info.p_provider, + btc_hd_cb.app_info.descriptor.dsc_list); + ret = ESP_HIDD_NO_RES; + break; + } + memcpy(btc_hd_cb.app_info.p_name, p_app_param->name, BTC_HD_APP_NAME_LEN); + memcpy(btc_hd_cb.app_info.p_description, p_app_param->description, BTC_HD_APP_DESCRIPTION_LEN); + memcpy(btc_hd_cb.app_info.p_provider, p_app_param->provider, BTC_HD_APP_PROVIDER_LEN); + memcpy(btc_hd_cb.app_info.descriptor.dsc_list, p_app_param->desc_list, p_app_param->desc_list_len); + btc_hd_cb.app_info.subclass = p_app_param->subclass; + btc_hd_cb.app_info.descriptor.dl_len = p_app_param->desc_list_len; + + btc_hd_cb.in_qos.service_type = p_in_qos->service_type; + btc_hd_cb.in_qos.token_rate = p_in_qos->token_rate; + btc_hd_cb.in_qos.token_bucket_size = p_in_qos->token_bucket_size; + btc_hd_cb.in_qos.peak_bandwidth = p_in_qos->peak_bandwidth; + btc_hd_cb.in_qos.access_latency = p_in_qos->access_latency; + btc_hd_cb.in_qos.delay_variation = p_in_qos->delay_variation; + btc_hd_cb.out_qos.service_type = p_out_qos->service_type; + btc_hd_cb.out_qos.token_rate = p_out_qos->token_rate; + btc_hd_cb.out_qos.token_bucket_size = p_out_qos->token_bucket_size; + btc_hd_cb.out_qos.peak_bandwidth = p_out_qos->peak_bandwidth; + btc_hd_cb.out_qos.access_latency = p_out_qos->access_latency; + btc_hd_cb.out_qos.delay_variation = p_out_qos->delay_variation; + + BTA_HdRegisterApp(&btc_hd_cb.app_info, &btc_hd_cb.in_qos, &btc_hd_cb.out_qos); + } while(0); + + if (ret != ESP_HIDD_SUCCESS) { + esp_hidd_cb_param_t param; + param.register_app.status = ret; + param.register_app.in_use = false; + memset(param.register_app.bd_addr, 0, BD_ADDR_LEN); + btc_hd_cb_to_app(ESP_HIDD_REGISTER_APP_EVT, ¶m); + } + free_app_info_param(); +} + +/******************************************************************************* + * + * Function btc_hd_unregister_app + * + * Description Unregisters HID Device application + * + * Returns void + * + ******************************************************************************/ +static void btc_hd_unregister_app(bool need_deinit) +{ + BTC_TRACE_API("%s", __func__); + esp_hidd_status_t ret = ESP_HIDD_SUCCESS; + do { + if (!is_hidd_init()) { + BTC_TRACE_ERROR("%s HD has not been initiated, shall init first!", __func__); + ret = ESP_HIDD_NEED_INIT; + break; + } else if (btc_hd_cb.status == BTC_HD_DISABLING) { + BTC_TRACE_ERROR("%s: deinit is in progress!", __func__); + ret = ESP_HIDD_BUSY; + break; + } + + if (!is_hidd_app_register()) { + BTC_TRACE_ERROR("%s: application has not been registered, shall register first!", __func__); + ret = ESP_HIDD_NEED_REG; + break; + } + + if (btc_hd_cb.service_dereg_active) { + BTC_TRACE_ERROR("%s: BT-HD deregistering in progress", __func__); + ret = ESP_HIDD_BUSY; + break; + } + btc_hd_cb.service_dereg_active = TRUE; + + if (need_deinit) { + btc_hd_cb.status = BTC_HD_DISABLING; + } + + BTA_HdUnregisterApp(); + } while(0); + + if (ret != ESP_HIDD_SUCCESS) { + esp_hidd_cb_param_t param = {0}; + param.unregister_app.status = ret; + btc_hd_cb_to_app(ESP_HIDD_UNREGISTER_APP_EVT, ¶m); + } +} + +/******************************************************************************* + * + * Function btc_hd_connect + * + * Description Connects to host + * + * Returns void + * + ******************************************************************************/ +static void btc_hd_connect(BD_ADDR bd_addr) +{ + BTC_TRACE_API("%s", __func__); + esp_hidd_status_t ret = ESP_HIDD_SUCCESS; + + do { + switch (btc_hd_cb.status) { + case BTC_HD_DISABLED: + BTC_TRACE_ERROR("%s HD has not been initiated, shall init first!", __func__); + ret = ESP_HIDD_NEED_INIT; + break; + case BTC_HD_DISABLING: + BTC_TRACE_ERROR("%s: deinit is in progress!", __func__); + ret = ESP_HIDD_BUSY; + break; + case BTC_HD_CONNECTING: + case BTC_HD_DISCONNECTING: + BTC_TRACE_ERROR("%s: busy now, status:%d, try later!", __func__, btc_hd_cb.status); + ret = ESP_HIDD_BUSY; + break; + case BTC_HD_CONNECTED: + BTC_TRACE_ERROR("%s: already connect to the other HID host!", __func__); + ret = ESP_HIDD_NO_RES; + break; + default: + break; + } + + if (ret != ESP_HIDD_SUCCESS) { + break; + } + + if (!is_hidd_app_register()) { + BTC_TRACE_ERROR("%s: application has not been registered, shall register first!", __func__); + ret = ESP_HIDD_NEED_REG; + break; + } + + BTA_HdConnect(bd_addr); + btc_hd_cb.status = BTC_HD_CONNECTING; + } while (0); + + if (ret != ESP_HIDD_SUCCESS) { + esp_hidd_cb_param_t param = {0}; + param.open.status = ret; + param.open.conn_status = ESP_HIDD_CONN_STATE_DISCONNECTED; + memcpy(param.open.bd_addr, bd_addr, BD_ADDR_LEN); + btc_hd_cb_to_app(ESP_HIDD_OPEN_EVT, ¶m); + } +} + +/******************************************************************************* + * + * Function btc_hd_disconnect + * + * Description Disconnects from host + * + * Returns void + * + ******************************************************************************/ +static void btc_hd_disconnect(void) +{ + BTC_TRACE_API("%s", __func__); + esp_hidd_status_t ret = ESP_HIDD_SUCCESS; + + do { + switch (btc_hd_cb.status) { + case BTC_HD_DISABLED: + BTC_TRACE_ERROR("%s HD has not been initiated, shall init first!", __func__); + ret = ESP_HIDD_NEED_INIT; + break; + case BTC_HD_DISABLING: + BTC_TRACE_ERROR("%s: deinit is in progress!", __func__); + ret = ESP_HIDD_BUSY; + break; + case BTC_HD_CONNECTING: + case BTC_HD_DISCONNECTING: + BTC_TRACE_ERROR("%s: busy now, status:%d, try later!", __func__, btc_hd_cb.status); + ret = ESP_HIDD_BUSY; + break; + case BTC_HD_ENABLED: + case BTC_HD_DISCONNECTED: + BTC_TRACE_ERROR("%s: no connection!", __func__); + ret = ESP_HIDD_NO_CONNECTION; + break; + default: + break; + } + + if (ret != ESP_HIDD_SUCCESS) { + break; + } + + if (!is_hidd_app_register()) { + BTC_TRACE_ERROR("%s: application has not been registered, shall register first!", __func__); + ret = ESP_HIDD_NEED_REG; + break; + } + + BTA_HdDisconnect(); + btc_hd_cb.status = BTC_HD_DISCONNECTING; + } while (0); + + if (ret != ESP_HIDD_SUCCESS) { + esp_hidd_cb_param_t param = {0}; + param.close.status = ret; + param.close.conn_status = ESP_HIDD_CONN_STATE_DISCONNECTED; + btc_hd_cb_to_app(ESP_HIDD_CLOSE_EVT, ¶m); + } +} + +/******************************************************************************* + * + * Function btc_hd_send_report + * + * Description Sends Reports to hid host + * + * Returns void + * + ******************************************************************************/ +static void btc_hd_send_report(esp_hidd_report_type_t type, uint8_t id, uint16_t len, uint8_t *p_data) +{ + BTC_TRACE_API("%s: type=%d id=%d len=%d", __func__, type, id, len); + tBTA_HD_REPORT report = {0}; + esp_hidd_status_t ret = ESP_HIDD_SUCCESS; + + do { + switch (btc_hd_cb.status) { + case BTC_HD_DISABLED: + BTC_TRACE_ERROR("%s HD has not been initiated, shall init first!", __func__); + ret = ESP_HIDD_NEED_INIT; + break; + case BTC_HD_DISABLING: + BTC_TRACE_ERROR("%s: deinit is in progress!", __func__); + ret = ESP_HIDD_BUSY; + break; + case BTC_HD_CONNECTING: + case BTC_HD_DISCONNECTING: + BTC_TRACE_ERROR("%s: busy now, status:%d, try later!", __func__, btc_hd_cb.status); + ret = ESP_HIDD_BUSY; + break; + case BTC_HD_ENABLED: + case BTC_HD_DISCONNECTED: + if (type == ESP_HIDD_REPORT_TYPE_INTRDATA) { + BTC_TRACE_WARNING("%s: no connection, try to reconnect!", __func__); + btc_hd_cb.status = BTC_HD_CONNECTING; + } else { + BTC_TRACE_ERROR("%s: no connection!", __func__); + ret = ESP_HIDD_NO_CONNECTION; + } + break; + default: + break; + } + + if (ret != ESP_HIDD_SUCCESS) { + break; + } + + if (!is_hidd_app_register()) { + BTC_TRACE_ERROR("%s: application has not been registered, shall register first!", __func__); + ret = ESP_HIDD_NEED_REG; + break; + } + + if (type == ESP_HIDD_REPORT_TYPE_INTRDATA) { + report.type = ESP_HIDD_REPORT_TYPE_INPUT; + report.use_intr = TRUE; + } else { + report.type = (type & 0x03); + report.use_intr = FALSE; + } + + report.id = id; + report.len = len; + report.p_data = p_data; + + BTA_HdSendReport(&report); + } while (0); + + if (ret != ESP_HIDD_SUCCESS) { + esp_hidd_cb_param_t param = {0}; + param.send_report.status = ret; + param.send_report.reason = 0; + param.send_report.report_type = report.type; + param.send_report.report_id = report.id; + btc_hd_cb_to_app(ESP_HIDD_SEND_REPORT_EVT, ¶m); + } +} + +/******************************************************************************* + * + * Function btc_hd_report_error + * + * Description Sends HANDSHAKE with error info for invalid SET_REPORT + * + * Returns void + * + ******************************************************************************/ +static void btc_hd_report_error(uint8_t error) +{ + BTC_TRACE_API("%s", __func__); + esp_hidd_status_t ret = ESP_HIDD_SUCCESS; + do { + if (!is_hidd_init()) { + BTC_TRACE_ERROR("%s HD has not been initiated, shall init first!", __func__); + ret = ESP_HIDD_NEED_INIT; + break; + } + + if (!is_hidd_app_register()) { + BTC_TRACE_ERROR("%s: application has not been registered, shall register first!", __func__); + ret = ESP_HIDD_NEED_REG; + break; + } + + if (btc_hd_cb.status != BTC_HD_CONNECTED) { + BTC_TRACE_ERROR("%s: no connection!", __func__); + ret = ESP_HIDD_NO_CONNECTION; + break; + } + + BTA_HdReportError(error); + } while (0); + + if (ret != ESP_HIDD_SUCCESS) { + esp_hidd_cb_param_t param = {0}; + param.report_err.status = ret; + param.report_err.reason = 0; + btc_hd_cb_to_app(ESP_HIDD_REPORT_ERR_EVT, ¶m); + } +} + +/******************************************************************************* + * + * Function btc_hd_virtual_cable_unplug + * + * Description Sends Virtual Cable Unplug to host + * + * Returns void + * + ******************************************************************************/ +static void btc_hd_virtual_cable_unplug(void) +{ + BTC_TRACE_API("%s", __func__); + esp_hidd_status_t ret = ESP_HIDD_SUCCESS; + + do { + switch (btc_hd_cb.status) { + case BTC_HD_DISABLED: + BTC_TRACE_ERROR("%s HD has not been initiated, shall init first!", __func__); + ret = ESP_HIDD_NEED_INIT; + break; + case BTC_HD_DISABLING: + BTC_TRACE_ERROR("%s: deinit is in progress!", __func__); + ret = ESP_HIDD_BUSY; + break; + case BTC_HD_CONNECTING: + case BTC_HD_DISCONNECTING: + BTC_TRACE_ERROR("%s: busy now, status:%d, try later!", __func__, btc_hd_cb.status); + ret = ESP_HIDD_BUSY; + break; + default: + break; + } + + if (ret != ESP_HIDD_SUCCESS) { + break; + } + + if (!is_hidd_app_register()) { + BTC_TRACE_ERROR("%s: application has not been registered, shall register first!", __func__); + ret = ESP_HIDD_NEED_REG; + break; + } + + BTA_HdVirtualCableUnplug(); + + if (btc_hd_cb.status == BTC_HD_CONNECTED) { + btc_hd_cb.status = BTC_HD_DISCONNECTING; + } + } while (0); + + if (ret != ESP_HIDD_SUCCESS) { + esp_hidd_cb_param_t param = {0}; + param.vc_unplug.status = ret; + param.vc_unplug.conn_status = ESP_HIDD_CONN_STATE_DISCONNECTED; + btc_hd_cb_to_app(ESP_HIDD_VC_UNPLUG_EVT, ¶m); + } +} + +static void btc_hd_call_arg_deep_free(btc_msg_t *msg) +{ + btc_hidd_args_t *arg = (btc_hidd_args_t *)msg->arg; + + switch (msg->act) { + case BTC_HD_SEND_REPORT_EVT: + utl_freebuf((void **)&arg->send_report.data); + break; + default: + break; + } +} + +void btc_hd_call_handler(btc_msg_t *msg) +{ + btc_hidd_args_t *arg = (btc_hidd_args_t *)(msg->arg); + switch (msg->act) { + case BTC_HD_INIT_EVT: + btc_hd_init(); + break; + case BTC_HD_DEINIT_EVT: + btc_hd_deinit(); + break; + case BTC_HD_REGISTER_APP_EVT: + btc_hd_register_app(arg->register_app.app_param, arg->register_app.in_qos, arg->register_app.out_qos); + break; + case BTC_HD_UNREGISTER_APP_EVT: + btc_hd_unregister_app(false); + break; + case BTC_HD_CONNECT_EVT: + btc_hd_connect(arg->connect.bd_addr); + break; + case BTC_HD_DISCONNECT_EVT: + btc_hd_disconnect(); + break; + case BTC_HD_SEND_REPORT_EVT: + btc_hd_send_report(arg->send_report.type, arg->send_report.id, arg->send_report.len, arg->send_report.data); + break; + case BTC_HD_REPORT_ERROR_EVT: + btc_hd_report_error(arg->error); + break; + case BTC_HD_UNPLUG_EVT: + btc_hd_virtual_cable_unplug(); + break; + default: + BTC_TRACE_WARNING("unknown hidd action %i", msg->act); + break; + } + btc_hd_call_arg_deep_free(msg); +} + +void btc_hd_cb_arg_deep_free(btc_msg_t *msg) +{ + tBTA_HD *arg = (tBTA_HD *)msg->arg; + + switch (msg->act) { + case BTA_HD_SET_REPORT_EVT: + utl_freebuf((void **)&arg->set_report.p_data); + break; + case BTA_HD_INTR_DATA_EVT: + utl_freebuf((void **)&arg->intr_data.p_data); + break; + default: + break; + } +} + +void btc_hd_cb_handler(btc_msg_t *msg) +{ + uint16_t event = msg->act; + tBTA_HD *p_data = (tBTA_HD *)msg->arg; + esp_hidd_cb_param_t param = {0}; + BTC_TRACE_API("%s: event=%s", __func__, dump_hd_event(event)); + + switch (event) { + case BTA_HD_ENABLE_EVT: + BTC_TRACE_DEBUG("%s: status=%d", __func__, p_data->status); + if (p_data->status == BTA_HD_OK) { + btc_storage_load_hidd(); + btc_hd_cb.status = BTC_HD_ENABLED; + } else { + btc_hd_cb.status = BTC_HD_DISABLED; + BTC_TRACE_WARNING("Failed to enable BT-HD, status=%d", p_data->status); + } + param.init.status = p_data->status; + btc_hd_cb_to_app(ESP_HIDD_INIT_EVT, ¶m); + break; + case BTA_HD_DISABLE_EVT: + BTC_TRACE_DEBUG("%s: status=%d", __func__, p_data->status); + if (p_data->status == BTA_HD_OK){ + btc_hd_cb.status = BTC_HD_DISABLED; + if (btc_hd_cb.service_dereg_active) { + btc_hd_cb.service_dereg_active = FALSE; + } + free_app_info_param(); + memset(&btc_hd_cb, 0, sizeof(btc_hd_cb)); + } else { + BTC_TRACE_WARNING("Failed to disable BT-HD, status=%d", p_data->status); + } + param.deinit.status = p_data->status; + btc_hd_cb_to_app(ESP_HIDD_DEINIT_EVT, ¶m); + break; + case BTA_HD_REGISTER_APP_EVT: + if (p_data->reg_status.status == BTA_HD_OK) { + btc_hd_cb.app_registered = TRUE; + } + param.register_app.status = p_data->reg_status.status; + param.register_app.in_use = p_data->reg_status.in_use; + if (!p_data->reg_status.in_use) { + memset(param.register_app.bd_addr, 0, BD_ADDR_LEN); + } else { + memcpy(param.register_app.bd_addr, p_data->reg_status.bda, BD_ADDR_LEN); + } + btc_hd_cb_to_app(ESP_HIDD_REGISTER_APP_EVT, ¶m); + break; + case BTA_HD_UNREGISTER_APP_EVT: + btc_hd_cb.app_registered = FALSE; + param.unregister_app.status = p_data->status; + btc_hd_cb_to_app(ESP_HIDD_UNREGISTER_APP_EVT, ¶m); + if (btc_hd_cb.status == BTC_HD_DISABLING) { + BTC_TRACE_WARNING("disabling hid device service now"); + BTA_HdDisable(); + } + break; + case BTA_HD_OPEN_EVT: { + bt_bdaddr_t *addr = (bt_bdaddr_t *)&p_data->conn.bda; + BTC_TRACE_EVENT("BTA_HD_OPEN_EVT, address (%02x:%02x:%02x:%02x:%02x:%02x)", addr->address[0], addr->address[1], + addr->address[2], addr->address[3], addr->address[4], addr->address[5]); + if (p_data->conn.status == BTA_HD_OK && p_data->conn.conn_status == BTA_HD_CONN_STATE_CONNECTED) { + // /* Check if the connection is from hid host and not hid device */ + // if (check_cod_hid(addr)) { + // /* Incoming connection from hid device, reject it */ + // BTC_TRACE_WARNING("remote device is not hid host, disconnecting"); + // btc_hd_cb.forced_disc = TRUE; + // BTA_HdDisconnect(); + // break; + // } + // btc_storage_set_hidd((bt_bdaddr_t *)&p_data->conn.bda); + btc_hd_cb.status = BTC_HD_CONNECTED; + } + param.open.status = p_data->conn.status; + param.open.conn_status = p_data->conn.conn_status; + memcpy(param.open.bd_addr, p_data->conn.bda, BD_ADDR_LEN); + btc_hd_cb_to_app(ESP_HIDD_OPEN_EVT, ¶m); + break; + } + case BTA_HD_CLOSE_EVT: + if (p_data->conn.conn_status == BTA_HD_CONN_STATE_DISCONNECTED) { + btc_hd_cb.status = BTC_HD_DISCONNECTED; + if (btc_hd_cb.forced_disc) { + bt_bdaddr_t *addr = (bt_bdaddr_t *)&p_data->conn.bda; + BTC_TRACE_WARNING("remote device was forcefully disconnected"); + btc_hd_remove_device(*addr); + btc_hd_cb.forced_disc = FALSE; + break; + } + } + + param.close.status = p_data->conn.status; + param.close.conn_status = p_data->conn.conn_status; + btc_hd_cb_to_app(ESP_HIDD_CLOSE_EVT, ¶m); + break; + case BTA_HD_GET_REPORT_EVT: + param.get_report.report_type = p_data->get_report.report_type; + param.get_report.report_id = p_data->get_report.report_id; + param.get_report.buffer_size = p_data->get_report.buffer_size; + btc_hd_cb_to_app(ESP_HIDD_GET_REPORT_EVT, ¶m); + break; + case BTA_HD_SET_REPORT_EVT: + param.set_report.report_type = p_data->set_report.report_type; + param.set_report.report_id = p_data->set_report.report_id; + param.set_report.len = p_data->set_report.len; + param.set_report.data = p_data->set_report.p_data; + btc_hd_cb_to_app(ESP_HIDD_SET_REPORT_EVT, ¶m); + break; + case BTA_HD_SET_PROTOCOL_EVT: + switch (p_data->set_protocol) { + case HID_PAR_PROTOCOL_BOOT_MODE: + param.set_protocol.protocol_mode = ESP_HIDD_BOOT_MODE; + break; + case HID_PAR_PROTOCOL_REPORT: + param.set_protocol.protocol_mode = ESP_HIDD_REPORT_MODE; + break; + default: + param.set_protocol.protocol_mode = ESP_HIDD_UNSUPPORTED_MODE; + break; + } + btc_hd_cb_to_app(ESP_HIDD_SET_PROTOCOL_EVT, ¶m); + break; + case BTA_HD_INTR_DATA_EVT: + param.intr_data.report_id = p_data->intr_data.report_id; + param.intr_data.len = p_data->intr_data.len; + param.intr_data.data = p_data->intr_data.p_data; + btc_hd_cb_to_app(ESP_HIDD_INTR_DATA_EVT, ¶m); + break; + case BTA_HD_VC_UNPLUG_EVT: { + bt_bdaddr_t *bd_addr = (bt_bdaddr_t *)&p_data->conn.bda; + if (bta_dm_check_if_only_hd_connected(p_data->conn.bda)) { + BTC_TRACE_DEBUG("%s: Removing bonding as only HID profile connected", __func__); + BTA_DmRemoveDevice((uint8_t *)&p_data->conn.bda, BT_TRANSPORT_BR_EDR); + } else { + BTC_TRACE_DEBUG("%s: Only removing HID data as some other profiles connected", __func__); + btc_hd_remove_device(*bd_addr); + } + + if (btc_hd_cb.status == BTC_HD_DISCONNECTING || btc_hd_cb.status == BTC_HD_CONNECTING || + btc_hd_cb.status == BTC_HD_CONNECTED) { + btc_hd_cb.status = BTC_HD_DISCONNECTED; + param.close.status = p_data->conn.status; + param.close.conn_status = p_data->conn.conn_status; + btc_hd_cb_to_app(ESP_HIDD_CLOSE_EVT, ¶m); + } + + param.vc_unplug.status = p_data->conn.status; + param.vc_unplug.conn_status = p_data->conn.conn_status; + btc_hd_cb_to_app(ESP_HIDD_VC_UNPLUG_EVT, ¶m); + break; + } + case BTA_HD_SEND_REPORT_EVT: + param.send_report.status = p_data->send_report.status; + param.send_report.reason = p_data->send_report.reason; + param.send_report.report_type = p_data->send_report.report_type; + param.send_report.report_id = p_data->send_report.report_id; + btc_hd_cb_to_app(ESP_HIDD_SEND_REPORT_EVT, ¶m); + break; + case BTA_HD_REPORT_ERR_EVT: + param.report_err.status = p_data->report_err.status; + param.report_err.reason = p_data->report_err.reason; + btc_hd_cb_to_app(ESP_HIDD_REPORT_ERR_EVT, ¶m); + break; + default: + BTC_TRACE_WARNING("%s: unknown event (%d)", __func__, event); + break; + } + btc_hd_cb_arg_deep_free(msg); +} + +void btc_hd_arg_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src) +{ + btc_hidd_args_t *dst = (btc_hidd_args_t *)p_dest; + btc_hidd_args_t *src = (btc_hidd_args_t *)p_src; + + switch (msg->act) { + case BTC_HD_SEND_REPORT_EVT: + dst->send_report.data = (uint8_t *)osi_malloc(src->send_report.len); + if (dst->send_report.data) { + memcpy(dst->send_report.data, src->send_report.data, src->send_report.len); + } else { + BTC_TRACE_ERROR("%s %d osi_malloc failed\n", __func__, msg->act); + } + break; + default: + break; + } +} + +#endif // HID_DEV_INCLUDED==TRUE diff --git a/lib/bt/host/bluedroid/btc/profile/std/hid/btc_hh.c b/lib/bt/host/bluedroid/btc/profile/std/hid/btc_hh.c new file mode 100644 index 00000000..7202371e --- /dev/null +++ b/lib/bt/host/bluedroid/btc/profile/std/hid/btc_hh.c @@ -0,0 +1,1569 @@ +/****************************************************************************** + * + * Copyright (C) 2016 The Android Open Source Project + * Copyright (C) 2009-2012 Broadcom Corporation + * Copyright (C) 2019 Blake Felt + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ +/************************************************************************************ + * + * Filename: btc_hd.c + * + * Description: HID Device Profile Bluetooth Interface + * + * + ***********************************************************************************/ + +#include +#include +#include +#include "btc/btc_util.h" +#include "btc/btc_manage.h" +#include "device/bdaddr.h" +#include "btc/btc_storage.h" +#include "osi/allocator.h" +#include "bta/utl.h" +#include "bta/bta_hh_api.h" +#include "stack/l2c_api.h" +// #include "bta_dm_int.h" + +#if HID_HOST_INCLUDED == TRUE +#include "btc_hh.h" + + +/******************************************************************************* + * Static variables + ******************************************************************************/ +btc_hh_cb_t btc_hh_cb; +static bdstr_t bdstr; + +/****************************************************************************** + * Constants & Macros + *****************************************************************************/ +#define COD_MASK 0x07FF + +#define COD_UNCLASSIFIED ((0x1F) << 8) +#define COD_HID_KEYBOARD 0x0540 +#define COD_HID_POINTING 0x0580 +#define COD_HID_COMBO 0x05C0 +#define COD_HID_MAJOR 0x0500 +#define COD_HID_MASK 0x0700 + +#define is_hidh_init() (btc_hh_cb.status > BTC_HH_DISABLED) +#define BTC_TIMEOUT_VUP_MS (3 * 1000) + +static inline void btc_hh_cb_to_app(esp_hidh_cb_event_t event, esp_hidh_cb_param_t *param) +{ + esp_hh_cb_t btc_hh_cb = (esp_hh_cb_t)btc_profile_cb_get(BTC_PID_HH); + if (btc_hh_cb) { + btc_hh_cb(event, param); + } +} + +/******************************************************************************* + * + * Function proto_mode_change_to_lower_layer + * + * Description Change the upper layer protocol mode definition to the lower layer protocol mode definition + * + * Returns Lower layer protocol mode definition + ******************************************************************************/ +static inline tBTA_HH_PROTO_MODE proto_mode_change_to_lower_layer(esp_hidh_protocol_mode_t protocol_mode) +{ + tBTA_HH_PROTO_MODE proto_mode = BTA_HH_PROTO_UNKNOWN; + + switch (protocol_mode) { + case ESP_HIDH_REPORT_MODE: + proto_mode = BTA_HH_PROTO_RPT_MODE; + break; + case ESP_HIDH_BOOT_MODE: + proto_mode = BTA_HH_PROTO_BOOT_MODE; + break; + default: + break; + } + + return proto_mode; +} + +/******************************************************************************* + * + * Function proto_mode_change_to_upper_layer + * + * Description Change the lower layer protocol mode definition to the upper layer protocol mode definition + * + * Returns Upper layer protocol mode definition + ******************************************************************************/ +static inline esp_hidh_protocol_mode_t proto_mode_change_to_upper_layer(tBTA_HH_PROTO_MODE proto_mode) +{ + esp_hidh_protocol_mode_t protocol_mode = ESP_HIDH_UNSUPPORTED_MODE; + + switch (proto_mode) { + case BTA_HH_PROTO_RPT_MODE: + protocol_mode = ESP_HIDH_REPORT_MODE; + break; + case BTA_HH_PROTO_BOOT_MODE: + protocol_mode = ESP_HIDH_BOOT_MODE; + break; + default: + break; + } + + return protocol_mode; +} + +/******************************************************************************* + * + * Function btc_hh_find_connected_dev_by_handle + * + * Description Return the connected device pointer of the specified device + * handle + * + * Returns Device entry pointer in the device table + ******************************************************************************/ +btc_hh_device_t *btc_hh_find_connected_dev_by_handle(uint8_t handle) +{ + uint32_t i; + for (i = 0; i < BTC_HH_MAX_HID; i++) { + if (btc_hh_cb.devices[i].dev_status == ESP_HIDH_CONN_STATE_CONNECTED && btc_hh_cb.devices[i].dev_handle == handle) { + return &btc_hh_cb.devices[i]; + } + } + return NULL; +} + +/******************************************************************************* + * + * Function btc_hh_find_dev_by_bda + * + * Description Return the device pointer of the specified bd_addr. + * + * Returns Device entry pointer in the device table + ******************************************************************************/ +static btc_hh_device_t *btc_hh_find_dev_by_bda(BD_ADDR bd_addr) +{ + uint8_t i; + for (i = 0; i < BTC_HH_MAX_HID; i++) { + if (btc_hh_cb.devices[i].dev_status != ESP_HIDH_CONN_STATE_UNKNOWN && + memcmp(btc_hh_cb.devices[i].bd_addr, bd_addr, BD_ADDR_LEN) == 0) { + return &btc_hh_cb.devices[i]; + } + } + return NULL; +} + +/******************************************************************************* + * + * Function btc_hh_find_connected_dev_by_bda + * + * Description Return the connected device pointer of the specified + * RawAddress. + * + * Returns Device entry pointer in the device table + ******************************************************************************/ +static btc_hh_device_t *btc_hh_find_connected_dev_by_bda(BD_ADDR bd_addr) +{ + uint32_t i; + for (i = 0; i < BTC_HH_MAX_HID; i++) { + if (btc_hh_cb.devices[i].dev_status == ESP_HIDH_CONN_STATE_CONNECTED && + memcmp(btc_hh_cb.devices[i].bd_addr, bd_addr, BD_ADDR_LEN) == 0) { + return &btc_hh_cb.devices[i]; + } + } + return NULL; +} + +/******************************************************************************* + * + * Function btc_hh_stop_vup_timer + * + * Description stop vitual unplug timer + * + * Returns void + ******************************************************************************/ +void btc_hh_stop_vup_timer(BD_ADDR bd_addr) +{ + BTIF_TRACE_API("%s", __func__); + btc_hh_device_t *p_dev = btc_hh_find_connected_dev_by_bda(bd_addr); + + if (p_dev != NULL) { + BTC_TRACE_DEBUG("stop VUP timer"); + if (p_dev->vup_timer) { + osi_alarm_free(p_dev->vup_timer); + p_dev->vup_timer = NULL; + } + } +} + +/******************************************************************************* + * + * Function btc_hh_timer_timeout + * + * Description Process timer timeout + * + * Returns void + ******************************************************************************/ +void btc_hh_timer_timeout(void *data) +{ + btc_hh_device_t *p_dev = (btc_hh_device_t *)data; + bt_status_t status; + tBTA_HH p_data; + btc_msg_t msg; + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_HH; + msg.act = BTA_HH_VC_UNPLUG_EVT; + + BTC_TRACE_API("%s", __func__); + if (p_dev->dev_status != ESP_HIDH_CONN_STATE_CONNECTED){ + BTC_TRACE_ERROR("%s Device[%s] is not connected!", __func__, + bdaddr_to_string((const bt_bdaddr_t *)p_dev->bd_addr, bdstr, sizeof(bdstr))); + return; + } + + memset(&p_data, 0, sizeof(tBTA_HH)); + p_data.dev_status.status = BTA_HH_ERR; + p_data.dev_status.handle = p_dev->dev_handle; + + /* switch context to btif task context */ + status = btc_transfer_context(&msg, &p_data, sizeof(tBTA_HH), NULL, NULL); + if (status != BT_STATUS_SUCCESS) { + BTC_TRACE_ERROR("%s context transfer failed", __func__); + } +} + +/******************************************************************************* + * + * Function btc_hh_start_vup_timer + * + * Description start virtual unplug timer + * + * Returns void + ******************************************************************************/ +void btc_hh_start_vup_timer(BD_ADDR bd_addr) +{ + BTC_TRACE_API("%s", __func__); + + btc_hh_device_t *p_dev = btc_hh_find_connected_dev_by_bda(bd_addr); + if (p_dev == NULL) { + BTC_TRACE_ERROR("%s Error: device not connected!", __func__); + return; + } + + if (p_dev->vup_timer) { + osi_alarm_free(p_dev->vup_timer); + p_dev->vup_timer = NULL; + } + if ((p_dev->vup_timer = osi_alarm_new("btc_hh.vup_timer", btc_hh_timer_timeout, p_dev, BTC_TIMEOUT_VUP_MS)) == + NULL) { + BTC_TRACE_ERROR("%s unable to malloc vup_timer!", __func__); + } +} + +/******************************************************************************* + * + * Function btc_hh_add_added_dev + * + * Description Add a new device to the added device list. + * + * Returns true if add successfully, otherwise false. + ******************************************************************************/ +bool btc_hh_add_added_dev(BD_ADDR bd_addr, tBTA_HH_ATTR_MASK attr_mask) +{ + int i; + for (i = 0; i < BTC_HH_MAX_ADDED_DEV; i++) { + if (memcmp(btc_hh_cb.added_devices[i].bd_addr, bd_addr, BD_ADDR_LEN) == 0) { + return false; + } + } + for (i = 0; i < BTC_HH_MAX_ADDED_DEV; i++) { + if (memcmp(btc_hh_cb.added_devices[i].bd_addr, bd_addr_null, BD_ADDR_LEN) == 0) { + memcpy(btc_hh_cb.added_devices[i].bd_addr, bd_addr, BD_ADDR_LEN); + btc_hh_cb.added_devices[i].dev_handle = BTA_HH_INVALID_HANDLE; + btc_hh_cb.added_devices[i].attr_mask = attr_mask; + return true; + } + } + + BTC_TRACE_ERROR("%s: Error, out of space to add device", __func__); + return false; +} + +/******************************************************************************* + ** + ** Function btc_hh_remove_device + ** + ** Description Remove an added device from the stack. + ** + ** Returns void + ******************************************************************************/ +void btc_hh_remove_device(BD_ADDR bd_addr) +{ + int i; + btc_hh_device_t *p_dev; + btc_hh_added_device_t *p_added_dev; + + for (i = 0; i < BTC_HH_MAX_ADDED_DEV; i++) { + p_added_dev = &btc_hh_cb.added_devices[i]; + if (p_added_dev->bd_addr == bd_addr) { + BTA_HhRemoveDev(p_added_dev->dev_handle); + btc_storage_remove_hid_info((bt_bdaddr_t *)p_added_dev->bd_addr); + memset(p_added_dev->bd_addr, 0, 6); + p_added_dev->dev_handle = BTA_HH_INVALID_HANDLE; + break; + } + } + + p_dev = btc_hh_find_dev_by_bda(bd_addr); + if (p_dev == NULL) { + BTC_TRACE_ERROR("%s Oops, can't find device", __func__); + return; + } + + /* need to notify up-layer device is disconnected to avoid state out of sync + * with up-layer */ //[boblane] + // HAL_CBACK(bt_hh_callbacks, connection_state_cb, &(p_dev->bd_addr), BTHH_CONN_STATE_DISCONNECTED); + + p_dev->dev_status = ESP_HIDH_CONN_STATE_UNKNOWN; + p_dev->dev_handle = BTA_HH_INVALID_HANDLE; + p_dev->ready_for_data = false; + + if (btc_hh_cb.device_num > 0) { + btc_hh_cb.device_num--; + } else { + BTC_TRACE_WARNING("%s: device_num = 0", __func__); + } +} + +static void bte_hh_arg_deep_copy(btc_msg_t *msg, void *p_dst, void *p_src) +{ + tBTA_HH *p_dst_data = (tBTA_HH *)p_dst; + tBTA_HH *p_src_data = (tBTA_HH *)p_src; + switch (msg->act) + { + case BTA_HH_GET_RPT_EVT: { + BT_HDR *src_hdr = p_src_data->hs_data.rsp_data.p_rpt_data; + if (src_hdr) { + p_dst_data->hs_data.rsp_data.p_rpt_data = osi_malloc(sizeof(BT_HDR) + src_hdr->offset + src_hdr->len); + if (p_dst_data->hs_data.rsp_data.p_rpt_data == NULL) { + BTC_TRACE_ERROR("%s malloc p_rpt_data failed!", __func__); + p_dst_data->hs_data.status = ESP_HIDH_ERR_NO_RES; + break; + } + memcpy(p_dst_data->hs_data.rsp_data.p_rpt_data, src_hdr, sizeof(BT_HDR) + src_hdr->offset + src_hdr->len); + } + break; + } + default: + break; + } +} + +static void bte_hh_evt(tBTA_HH_EVT event, tBTA_HH *p_data) +{ + bt_status_t status; + int param_len = 0; + + BTC_TRACE_API("%s event=%d", __func__, event); + + switch (event) { + case BTA_HH_ENABLE_EVT: + param_len = sizeof(tBTA_HH_STATUS); + break; + case BTA_HH_DISABLE_EVT: + param_len = sizeof(tBTA_HH_STATUS); + break; + case BTA_HH_OPEN_EVT: + param_len = sizeof(tBTA_HH_CONN); + break; + case BTA_HH_CLOSE_EVT: + param_len = sizeof(tBTA_HH_CBDATA); + break; + case BTA_HH_GET_RPT_EVT: + param_len = sizeof(tBTA_HH_HSDATA); + break; + case BTA_HH_SET_RPT_EVT: + param_len = sizeof(tBTA_HH_CBDATA); + break; + case BTA_HH_GET_PROTO_EVT: + param_len = sizeof(tBTA_HH_HSDATA); + break; + case BTA_HH_SET_PROTO_EVT: + param_len = sizeof(tBTA_HH_CBDATA); + break; + case BTA_HH_GET_IDLE_EVT: + param_len = sizeof(tBTA_HH_HSDATA); + break; + case BTA_HH_SET_IDLE_EVT: + param_len = sizeof(tBTA_HH_CBDATA); + break; + case BTA_HH_GET_DSCP_EVT: + param_len = sizeof(tBTA_HH_DEV_DSCP_INFO); + break; + case BTA_HH_ADD_DEV_EVT: + param_len = sizeof(tBTA_HH_DEV_INFO); + break; + case BTA_HH_RMV_DEV_EVT: + param_len = sizeof(tBTA_HH_DEV_INFO); + break; + case BTA_HH_VC_UNPLUG_EVT: + param_len = sizeof(tBTA_HH_CBDATA); + break; + case BTA_HH_DATA_EVT: + param_len = sizeof(tBTA_HH_API_SENDDATA); + break; + case BTA_HH_API_ERR_EVT: + param_len = 0; + break; + } + + btc_msg_t msg; + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_HH; + msg.act = event; + + status = btc_transfer_context(&msg, p_data, param_len, bte_hh_arg_deep_copy, btc_hh_cb_arg_deep_free); + assert(status == BT_STATUS_SUCCESS); +} + +/******************************************************************************* + * + * Function btc_hh_init + * + * Description initializes the hh interface + * + * Returns void + * + ******************************************************************************/ +static void btc_hh_init(void) +{ + BTC_TRACE_API("%s", __func__); + esp_hidh_status_t ret = ESP_HIDH_OK; + do { + if (is_hidh_init()) { + BTC_TRACE_ERROR("%s HH has been initiated, shall uninit first!", __func__); + ret = ESP_HIDH_NEED_DEINIT; + break; + } + + memset(&btc_hh_cb, 0, sizeof(btc_hh_cb)); + for (uint8_t i = 0; i < BTC_HH_MAX_HID; i++) { + btc_hh_cb.devices[i].dev_status = ESP_HIDH_CONN_STATE_UNKNOWN; + } + BTA_HhEnable(BTA_SEC_AUTHENTICATE | BTA_SEC_ENCRYPT, bte_hh_evt); + } while (0); + + if (ret != ESP_HIDH_OK) { + esp_hidh_cb_param_t param; + param.init.status = ret; + btc_hh_cb_to_app(ESP_HIDH_INIT_EVT, ¶m); + } +} + +/******************************************************************************* + * + * Function btc_hh_deinit + * + * Description de-initializes the hh interface + * + * Returns void + * + ******************************************************************************/ +static void btc_hh_deinit(void) +{ + BTC_TRACE_API("%s", __func__); + esp_hidh_status_t ret = ESP_HIDH_OK; + do { + if (!is_hidh_init()) { + BTC_TRACE_ERROR("%s HH has not been initiated, shall init first!", __func__); + ret = ESP_HIDH_NEED_INIT; + break; + } + + // close all connections + for (uint8_t i = 0; i < BTC_HH_MAX_HID; i++) { + if(btc_hh_cb.devices[i].dev_status == ESP_HIDH_CONN_STATE_CONNECTED){ + BTA_HhClose(btc_hh_cb.devices[i].dev_handle); + } + } + btc_hh_cb.service_dereg_active = TRUE; + btc_hh_cb.status = BTC_HH_DISABLING; + BTA_HhDisable(); + } while (0); + + if (ret != ESP_HIDH_OK) { + esp_hidh_cb_param_t param; + param.deinit.status = ret; + btc_hh_cb_to_app(ESP_HIDH_DEINIT_EVT, ¶m); + } +} + +/******************************************************************************* + * + * Function btc_hh_connect + * + * Description connection initiated from the BTC thread context + * + * Returns void + * + ******************************************************************************/ +static void btc_hh_connect(btc_hidh_args_t *arg) +{ + BTC_TRACE_API("%s", __func__); + esp_hidh_status_t ret = ESP_HIDH_OK; + btc_hh_added_device_t* added_dev = NULL; + btc_hh_device_t* dev = NULL; + esp_hidh_cb_param_t param; + + do { + if (!is_hidh_init()) { + BTC_TRACE_ERROR("%s HH has not been initiated, shall init first!", __func__); + ret = ESP_HIDH_NEED_INIT; + break; + } + + if (btc_hh_cb.status == BTC_HH_DEV_CONNECTING) { + BTC_TRACE_ERROR("%s HH is connecting, ignore!", __func__); + ret = ESP_HIDH_BUSY; + break; + } + + dev = btc_hh_find_dev_by_bda(arg->connect.bd_addr); + if (!dev && btc_hh_cb.device_num >= BTC_HH_MAX_HID) { + BTC_TRACE_ERROR("%s exceeded the maximum supported HID device number %d!", __func__, BTC_HH_MAX_HID); + ret = ESP_HIDH_ERR_NO_RES; + break; + } + + for (int i = 0; i < BTC_HH_MAX_ADDED_DEV; i++) { + if (memcmp(btc_hh_cb.added_devices[i].bd_addr, arg->connect.bd_addr, BD_ADDR_LEN) == 0) { + added_dev = &btc_hh_cb.added_devices[i]; + BTC_TRACE_WARNING("%s Device[%s] already added, attr_mask = 0x%x", __func__, + bdaddr_to_string((const bt_bdaddr_t *)arg->connect.bd_addr, bdstr, sizeof(bdstr)), + added_dev->attr_mask); + } + } + + if (added_dev != NULL) { + if (added_dev->dev_handle == BTA_HH_INVALID_HANDLE) { + // No space for more HID device now. + BTC_TRACE_ERROR("device added but addition failed"); + memset(added_dev->bd_addr, 0, sizeof(added_dev->bd_addr)); + ret = ESP_HIDH_ERR; + break; + } + } + + /* Not checking the NORMALLY_Connectible flags from sdp record, and anyways + sending this request from host, for subsequent user initiated connection. If the remote is + not in pagescan mode, we will do 2 retries to connect before giving up */ + btc_hh_cb.status = BTC_HH_DEV_CONNECTING; + memcpy(btc_hh_cb.pending_conn_address, arg->connect.bd_addr, BD_ADDR_LEN); + BTA_HhOpen(arg->connect.bd_addr, BTA_HH_PROTO_RPT_MODE, (BTA_SEC_AUTHENTICATE | BTA_SEC_ENCRYPT)); + param.open.conn_status = ESP_HIDH_CONN_STATE_CONNECTING; + ret = ESP_HIDH_OK; + } while (0); + + if (ret != ESP_HIDH_OK) { + param.open.conn_status = ESP_HIDH_CONN_STATE_DISCONNECTED; + } + param.open.status = ret; + param.open.handle = BTA_HH_INVALID_HANDLE; + memcpy(param.open.bd_addr, arg->connect.bd_addr, BD_ADDR_LEN); + param.open.is_orig = true; + btc_hh_cb_to_app(ESP_HIDH_OPEN_EVT, ¶m); +} + +/******************************************************************************* + * + * Function btc_hh_disconnect + * + * Description disconnection initiated from the BTC thread context + * + * Returns void + * + ******************************************************************************/ +static void btc_hh_disconnect(btc_hidh_args_t *arg) +{ + BTC_TRACE_API("%s", __func__); + esp_hidh_status_t ret = ESP_HIDH_OK; + btc_hh_device_t *p_dev; + esp_hidh_cb_param_t param; + + do { + if (!is_hidh_init()) { + BTC_TRACE_ERROR("%s HH has not been initiated, shall init first!", __func__); + ret = ESP_HIDH_NEED_INIT; + break; + } + p_dev = btc_hh_find_connected_dev_by_bda(arg->disconnect.bd_addr); + if (p_dev != NULL) { + BTA_HhClose(p_dev->dev_handle); + param.close.conn_status = ESP_HIDH_CONN_STATE_DISCONNECTING; + param.close.handle = p_dev->dev_handle; + } else { + ret = ESP_HIDH_NO_CONNECTION; + BTC_TRACE_ERROR("%s Error: device not connected!", __func__); + } + + } while (0); + + if (ret != ESP_HIDH_OK) { + param.close.conn_status = ESP_HIDH_CONN_STATE_DISCONNECTED; + param.close.handle = BTA_HH_INVALID_HANDLE; + } + param.close.status = ret; + btc_hh_cb_to_app(ESP_HIDH_CLOSE_EVT, ¶m); +} + +/******************************************************************************* + * + * Function btc_hh_virtual_unplug + * + * Description Virtual unplug initiated from the BTC thread context + * Special handling for HID mouse- + * + * Returns void + * + ******************************************************************************/ +static void btc_hh_virtual_unplug(btc_hidh_args_t *arg) +{ + BTC_TRACE_API("%s", __func__); + esp_hidh_status_t ret = ESP_HIDH_OK; + btc_hh_device_t *p_dev; + esp_hidh_cb_param_t param; + + do { + if (!is_hidh_init()) { + BTC_TRACE_ERROR("%s HH has not been initiated, shall init first!", __func__); + ret = ESP_HIDH_NEED_INIT; + break; + } + p_dev = btc_hh_find_dev_by_bda(arg->unplug.bd_addr); + if ((p_dev != NULL) && (p_dev->dev_status == ESP_HIDH_CONN_STATE_CONNECTED) && + (p_dev->attr_mask & HID_VIRTUAL_CABLE)) { + BTC_TRACE_DEBUG("%s: Sending BTA_HH_CTRL_VIRTUAL_CABLE_UNPLUG", __func__); + /* start the timer */ + btc_hh_start_vup_timer(arg->unplug.bd_addr); + p_dev->local_vup = true; + BTA_HhSendCtrl(p_dev->dev_handle, BTA_HH_CTRL_VIRTUAL_CABLE_UNPLUG); + + param.unplug.conn_status = ESP_HIDH_CONN_STATE_DISCONNECTING; + param.unplug.handle = p_dev->dev_handle; + } else if ((p_dev != NULL) && (p_dev->dev_status == ESP_HIDH_CONN_STATE_CONNECTED)) { + BTC_TRACE_WARNING("%s: Virtual unplug not suported, disconnecting device", __func__); + /* start the timer */ + btc_hh_start_vup_timer(arg->unplug.bd_addr); + p_dev->local_vup = true; + BTA_HhClose(p_dev->dev_handle); + + param.unplug.conn_status = ESP_HIDH_CONN_STATE_DISCONNECTING; + param.unplug.handle = p_dev->dev_handle; + } else { + BTC_TRACE_ERROR("%s: Error, device not opened, status = %d", __func__, btc_hh_cb.status); + ret = ESP_HIDH_NO_CONNECTION; + if (memcmp(btc_hh_cb.pending_conn_address, arg->unplug.bd_addr, BD_ADDR_LEN) == 0 && + (btc_hh_cb.status == BTC_HH_DEV_CONNECTING)) { + btc_hh_cb.status = (BTC_HH_STATUS)BTC_HH_DEV_DISCONNECTED; + memset(btc_hh_cb.pending_conn_address, 0, BD_ADDR_LEN); + } + } + } while (0); + + if (ret != ESP_HIDH_OK) { + param.unplug.conn_status = ESP_HIDH_CONN_STATE_DISCONNECTED; + param.unplug.handle = BTA_HH_INVALID_HANDLE; + } + param.unplug.status = ret; + btc_hh_cb_to_app(ESP_HIDH_VC_UNPLUG_EVT, ¶m); +} + +/******************************************************************************* + * + * Function btc_hh_set_info + * + * Description Set the HID device descriptor for the specified HID device. + * + * Returns void + * + ******************************************************************************/ +static void btc_hh_set_info(btc_hidh_args_t *arg) +{ + BTC_TRACE_API("%s", __func__); + esp_hidh_status_t ret = ESP_HIDH_OK; + esp_hidh_cb_param_t param; + tBTA_HH_DEV_DSCP_INFO dscp_info; + + BTC_TRACE_DEBUG("%s: sub_class = 0x%02x, app_id = %d, vendor_id = 0x%04x, " + "product_id = 0x%04x, version= 0x%04x", + __func__, arg->set_info.hid_info->sub_class, arg->set_info.hid_info->app_id, + arg->set_info.hid_info->vendor_id, arg->set_info.hid_info->product_id, + arg->set_info.hid_info->version); + do { + if (!is_hidh_init()) { + BTC_TRACE_ERROR("%s HH has not been initiated, shall init first!", __func__); + ret = ESP_HIDH_NEED_INIT; + break; + } + + memset(&dscp_info, 0, sizeof(dscp_info)); + dscp_info.vendor_id = arg->set_info.hid_info->vendor_id; + dscp_info.product_id = arg->set_info.hid_info->product_id; + dscp_info.version = arg->set_info.hid_info->version; + dscp_info.ctry_code = arg->set_info.hid_info->ctry_code; + + dscp_info.descriptor.dl_len = arg->set_info.hid_info->dl_len; + dscp_info.descriptor.dsc_list = (uint8_t *)osi_malloc(dscp_info.descriptor.dl_len); + if (dscp_info.descriptor.dsc_list == NULL) { + BTC_TRACE_ERROR("%s malloc dsc_list failed!", __func__); + ret = ESP_HIDH_ERR_NO_RES; + break; + } + memcpy(dscp_info.descriptor.dsc_list, arg->set_info.hid_info->dsc_list, dscp_info.descriptor.dl_len); + + if (btc_hh_add_added_dev(arg->set_info.bd_addr, arg->set_info.hid_info->attr_mask)) { + btc_hh_cb.add_event = BTC_HH_SET_INFO_EVT; + BTA_HhAddDev(arg->set_info.bd_addr, arg->set_info.hid_info->attr_mask, arg->set_info.hid_info->sub_class, + arg->set_info.hid_info->app_id, dscp_info); + } else { + BTC_TRACE_ERROR("%s malloc dsc_list failed!", __func__); + ret = ESP_HIDH_ERR; + break; + } + } while(0); + utl_freebuf((void **)&dscp_info.descriptor.dsc_list); + + if (ret != ESP_HIDH_OK) { + param.set_info.status = ret; + param.set_info.handle = BTA_HH_INVALID_HANDLE; + memcpy(param.set_info.bd_addr, arg->set_info.bd_addr, BD_ADDR_LEN); + btc_hh_cb_to_app(ESP_HIDH_SET_INFO_EVT, ¶m); + } +} + +/******************************************************************************* + * + * Function btc_hh_get_protocol + * + * Description Get the HID proto mode. + * + * Returns void + * + ******************************************************************************/ +static void btc_hh_get_protocol(btc_hidh_args_t *arg) +{ + BTC_TRACE_API("%s", __func__); + esp_hidh_status_t ret = ESP_HIDH_OK; + esp_hidh_cb_param_t param; + btc_hh_device_t *p_dev; + + do { + if (!is_hidh_init()) { + BTC_TRACE_ERROR("%s HH has not been initiated, shall init first!", __func__); + ret = ESP_HIDH_NEED_INIT; + break; + } + p_dev = btc_hh_find_connected_dev_by_bda(arg->get_protocol.bd_addr); + if (p_dev == NULL) { + ret = ESP_HIDH_NO_CONNECTION; + BTC_TRACE_ERROR("%s Error: device not connected!", __func__); + break; + } + BTA_HhGetProtoMode(p_dev->dev_handle); + } while(0); + + if (ret != ESP_HIDH_OK) { + param.get_proto.proto_mode = ESP_HIDH_UNSUPPORTED_MODE; + param.get_proto.handle = BTA_HH_INVALID_HANDLE; + param.get_proto.status = ret; + btc_hh_cb_to_app(ESP_HIDH_GET_PROTO_EVT, ¶m); + } +} + +/******************************************************************************* + * + * Function btc_hh_set_protocol + * + * Description Set the HID proto mode. + * + * Returns void + * + ******************************************************************************/ +static void btc_hh_set_protocol(btc_hidh_args_t *arg) +{ + BTC_TRACE_API("%s", __func__); + esp_hidh_status_t ret = ESP_HIDH_OK; + esp_hidh_cb_param_t param; + btc_hh_device_t *p_dev; + + do { + if (!is_hidh_init()) { + BTC_TRACE_ERROR("%s HH has not been initiated, shall init first!", __func__); + ret = ESP_HIDH_NEED_INIT; + break; + } + p_dev = btc_hh_find_connected_dev_by_bda(arg->set_protocol.bd_addr); + if (p_dev == NULL) { + ret = ESP_HIDH_NO_CONNECTION; + BTC_TRACE_ERROR("%s Error: device not connected!", __func__); + break; + } else if (arg->set_protocol.protocol_mode != ESP_HIDH_REPORT_MODE && arg->set_protocol.protocol_mode != ESP_HIDH_BOOT_MODE) { + BTC_TRACE_ERROR("%s: Error, device proto_mode = %d.", __func__, arg->set_protocol.protocol_mode); + ret = ESP_HIDH_HS_INVALID_PARAM; + break; + } else { + BTA_HhSetProtoMode(p_dev->dev_handle, proto_mode_change_to_lower_layer(arg->set_protocol.protocol_mode)); + } + } while (0); + + if (ret != ESP_HIDH_OK) { + param.set_proto.handle = BTA_HH_INVALID_HANDLE; + param.set_proto.status = ret; + btc_hh_cb_to_app(ESP_HIDH_SET_PROTO_EVT, ¶m); + } +} + +/******************************************************************************* + * + * Function btc_hh_get_report + * + * Description Send a GET_REPORT to HID device. + * + * Returns void + * + ******************************************************************************/ +static void btc_hh_get_report(btc_hidh_args_t *arg) +{ + BTC_TRACE_API("%s", __func__); + esp_hidh_status_t ret = ESP_HIDH_OK; + esp_hidh_cb_param_t param; + btc_hh_device_t *p_dev; + + do { + if (!is_hidh_init()) { + BTC_TRACE_ERROR("%s HH has not been initiated, shall init first!", __func__); + ret = ESP_HIDH_NEED_INIT; + break; + } + + p_dev = btc_hh_find_connected_dev_by_bda(arg->get_report.bd_addr); + if (p_dev == NULL) { + ret = ESP_HIDH_NO_CONNECTION; + BTC_TRACE_ERROR("%s Error: device not connected!", __func__); + break; + } else if (((int)arg->get_report.report_type) <= BTA_HH_RPTT_RESRV || + ((int)arg->get_report.report_type) > BTA_HH_RPTT_FEATURE) { + BTC_TRACE_ERROR("%s Error: report type=%d not supported!", __func__, arg->get_report.report_type); + ret = ESP_HIDH_HS_INVALID_PARAM; + break; + } else { + BTA_HhGetReport(p_dev->dev_handle, arg->get_report.report_type, arg->get_report.report_id, + arg->get_report.buffer_size); + } + } while (0); + + if (ret != ESP_HIDH_OK) { + param.get_rpt.handle = BTA_HH_INVALID_HANDLE; + param.get_rpt.status = ret; + param.get_rpt.len = 0; + param.get_rpt.data = NULL; + btc_hh_cb_to_app(ESP_HIDH_GET_RPT_EVT, ¶m); + } +} + +/******************************************************************************* + * + * Function create_pbuf + * + * Description Helper function to create p_buf for send_data or set_report + * + * Returns BT_HDR * + * + ******************************************************************************/ +static BT_HDR *create_pbuf(uint16_t len, uint8_t *data) +{ + uint8_t *pbuf_data; + BT_HDR *p_buf = (BT_HDR *)osi_malloc(len + BTA_HH_MIN_OFFSET + sizeof(BT_HDR)); + if (p_buf == NULL) { + BTC_TRACE_ERROR("%s failed!", __func__); + return NULL; + } + p_buf->len = len; + p_buf->offset = BTA_HH_MIN_OFFSET; + + pbuf_data = (uint8_t *)(p_buf + 1) + p_buf->offset; + memcpy(pbuf_data, data, len); + + return p_buf; +} + +/******************************************************************************* + * + * Function btc_hh_set_report + * + * Description Send a SET_REPORT to HID device. + * + * Returns void + * + ******************************************************************************/ +static void btc_hh_set_report(btc_hidh_args_t *arg) +{ + BTC_TRACE_API("%s", __func__); + esp_hidh_status_t ret = ESP_HIDH_OK; + esp_hidh_cb_param_t param; + btc_hh_device_t *p_dev; + + do { + if (!is_hidh_init()) { + BTC_TRACE_ERROR("%s HH has not been initiated, shall init first!", __func__); + ret = ESP_HIDH_NEED_INIT; + break; + } + + p_dev = btc_hh_find_connected_dev_by_bda(arg->set_report.bd_addr); + if (p_dev == NULL) { + ret = ESP_HIDH_NO_CONNECTION; + BTC_TRACE_ERROR("%s Error: device not connected!", __func__); + break; + } else if (((int)arg->set_report.report_type) <= BTA_HH_RPTT_RESRV || + ((int)arg->set_report.report_type) > BTA_HH_RPTT_FEATURE) { + BTC_TRACE_ERROR("%s Error: report type=%d not supported!", __func__, arg->set_report.report_type); + ret = ESP_HIDH_HS_INVALID_PARAM; + break; + } else if (arg->set_report.report == NULL || arg->set_report.len == 0) { + BTC_TRACE_ERROR("%s Error: report is empty!", __func__); + ret = ESP_HIDH_HS_INVALID_PARAM; + break; + } else { + BT_HDR* p_buf = create_pbuf(arg->set_report.len, arg->set_report.report); + if (p_buf == NULL) { + ret = ESP_HIDH_ERR_NO_RES; + break; + } + BTA_HhSetReport(p_dev->dev_handle, arg->set_report.report_type, p_buf); + } + } while(0); + + if (ret != ESP_HIDH_OK) { + param.set_rpt.handle = BTA_HH_INVALID_HANDLE; + param.set_rpt.status = ret; + btc_hh_cb_to_app(ESP_HIDH_SET_RPT_EVT, ¶m); + } +} + +/******************************************************************************* + * + * Function btc_hh_send_data + * + * Description Send a SEND_DATA to HID device. + * + * Returns void + * + ******************************************************************************/ +static void btc_hh_send_data(btc_hidh_args_t *arg) +{ + BTC_TRACE_API("%s", __func__); + esp_hidh_status_t ret = ESP_HIDH_OK; + esp_hidh_cb_param_t param; + btc_hh_device_t *p_dev; + + do { + if (!is_hidh_init()) { + BTC_TRACE_ERROR("%s HH has not been initiated, shall init first!", __func__); + ret = ESP_HIDH_NEED_INIT; + break; + } + + p_dev = btc_hh_find_connected_dev_by_bda(arg->send_data.bd_addr); + if (p_dev == NULL) { + ret = ESP_HIDH_NO_CONNECTION; + BTC_TRACE_ERROR("%s Error: device not connected!", __func__); + break; + } else if (arg->send_data.data == NULL || arg->send_data.len == 0) { + BTC_TRACE_ERROR("%s Error: send data is empty!", __func__); + ret = ESP_HIDH_HS_INVALID_PARAM; + break; + } else { + BT_HDR *p_buf = create_pbuf(arg->send_data.len, arg->send_data.data); + if (p_buf == NULL) { + ret = ESP_HIDH_ERR_NO_RES; + break; + } + p_buf->layer_specific = BTA_HH_RPTT_OUTPUT; + BTA_HhSendData(p_dev->dev_handle, arg->send_data.bd_addr, p_buf); + } + } while(0); + + if (ret != ESP_HIDH_OK) { + param.send_data.handle = BTA_HH_INVALID_HANDLE; + param.send_data.status = ret; + param.send_data.reason = 0; + btc_hh_cb_to_app(ESP_HIDH_DATA_EVT, ¶m); + } +} + +/******************************************************************************* +** +** Function btc_hh_get_idle_time +** +** Description Get the HID idle time +** +** Returns void +** +*******************************************************************************/ +static void btc_hh_get_idle_time(btc_hidh_args_t *arg) +{ + BTC_TRACE_API("%s", __func__); + esp_hidh_status_t ret = ESP_HIDH_OK; + esp_hidh_cb_param_t param; + btc_hh_device_t *p_dev; + + do { + if (!is_hidh_init()) { + BTC_TRACE_ERROR("%s HH has not been initiated, shall init first!", __func__); + ret = ESP_HIDH_NEED_INIT; + break; + } + + p_dev = btc_hh_find_connected_dev_by_bda(arg->get_idle.bd_addr); + if (p_dev == NULL) { + ret = ESP_HIDH_NO_CONNECTION; + BTC_TRACE_ERROR("%s Error: device not connected!", __func__); + break; + } + BTA_HhGetIdle(p_dev->dev_handle); + } while (0); + + if (ret != ESP_HIDH_OK) { + param.get_idle.handle = BTA_HH_INVALID_HANDLE; + param.get_idle.status = ret; + param.get_idle.idle_rate = 0; + btc_hh_cb_to_app(ESP_HIDH_GET_IDLE_EVT, ¶m); + } +} + +/******************************************************************************* +** +** Function btc_hh_set_idle_time +** +** Description Set the HID idle time +** +** Returns void +** +*******************************************************************************/ +static void btc_hh_set_idle_time(btc_hidh_args_t *arg) +{ + BTC_TRACE_API("%s", __func__); + esp_hidh_status_t ret = ESP_HIDH_OK; + esp_hidh_cb_param_t param; + btc_hh_device_t *p_dev; + + do { + if (!is_hidh_init()) { + BTC_TRACE_ERROR("%s HH has not been initiated, shall init first!", __func__); + ret = ESP_HIDH_NEED_INIT; + break; + } + + p_dev = btc_hh_find_connected_dev_by_bda(arg->set_idle.bd_addr); + if (p_dev == NULL) { + ret = ESP_HIDH_NO_CONNECTION; + BTC_TRACE_ERROR("%s Error: device not connected!", __func__); + break; + } + BTA_HhSetIdle(p_dev->dev_handle, arg->set_idle.idle_time); + } while (0); + + if (ret != ESP_HIDH_OK) { + param.set_idle.handle = BTA_HH_INVALID_HANDLE; + param.set_idle.status = ret; + btc_hh_cb_to_app(ESP_HIDH_SET_IDLE_EVT, ¶m); + } +} + +static void btc_hh_call_arg_deep_free(btc_msg_t *msg) +{ + btc_hidh_args_t *arg = (btc_hidh_args_t *)msg->arg; + + switch (msg->act) { + case BTC_HH_SET_INFO_EVT: + utl_freebuf((void **)&arg->set_info.hid_info); + break; + case BTC_HH_SET_REPORT_EVT: + utl_freebuf((void **)&arg->set_report.report); + break; + case BTC_HH_SEND_DATA_EVT: + utl_freebuf((void **)&arg->send_data.data); + break; + default: + break; + } +} + +void btc_hh_call_handler(btc_msg_t *msg) +{ + btc_hidh_args_t *arg = (btc_hidh_args_t *)(msg->arg); + switch (msg->act) { + case BTC_HH_INIT_EVT: + btc_hh_init(); + break; + case BTC_HH_CONNECT_EVT: + btc_hh_connect(arg); + break; + case BTC_HH_DISCONNECT_EVT: + btc_hh_disconnect(arg); + break; + case BTC_HH_UNPLUG_EVT: + btc_hh_virtual_unplug(arg); + break; + case BTC_HH_SET_INFO_EVT: + btc_hh_set_info(arg); + break; + case BTC_HH_GET_PROTO_EVT: + btc_hh_get_protocol(arg); + break; + case BTC_HH_SET_PROTO_EVT: + btc_hh_set_protocol(arg); + break; + case BTC_HH_GET_IDLE_EVT: + btc_hh_get_idle_time(arg); + break; + case BTC_HH_SET_IDLE_EVT: + btc_hh_set_idle_time(arg); + break; + case BTC_HH_GET_REPORT_EVT: + btc_hh_get_report(arg); + break; + case BTC_HH_SET_REPORT_EVT: + btc_hh_set_report(arg); + break; + case BTC_HH_SEND_DATA_EVT: + btc_hh_send_data(arg); + break; + case BTC_HH_DEINIT_EVT: + btc_hh_deinit(); + break; + default: + BTC_TRACE_WARNING("unknown hidh action %d", msg->act); + break; + } + btc_hh_call_arg_deep_free(msg); +} + +void btc_hh_cb_arg_deep_free(btc_msg_t *msg) +{ + tBTA_HH *arg = (tBTA_HH *)msg->arg; + + switch (msg->act) { + case BTA_HH_GET_RPT_EVT: + utl_freebuf((void **)&arg->hs_data.rsp_data.p_rpt_data); + break; + case BTA_HH_DATA_IND_EVT: + utl_freebuf((void **)&arg->int_data.p_data); + break; + default: + break; + } +} + +bool btc_hh_copy_hid_info(tBTA_HH_DEV_DSCP_INFO *dest, tBTA_HH_DEV_DSCP_INFO *src) +{ + dest->descriptor.dl_len = 0; + if (src->descriptor.dl_len > 0) { + dest->descriptor.dsc_list = (uint8_t *)osi_malloc(src->descriptor.dl_len); + } + if (dest->descriptor.dsc_list) { + memcpy(dest->descriptor.dsc_list, src->descriptor.dsc_list, src->descriptor.dl_len); + dest->descriptor.dl_len = src->descriptor.dl_len; + } + dest->vendor_id = src->vendor_id; + dest->product_id = src->product_id; + dest->version = src->version; + dest->ctry_code = src->ctry_code; + dest->ssr_max_latency = src->ssr_max_latency; + dest->ssr_min_tout = src->ssr_min_tout; + return true; +} + +bool btc_hh_cb_copy_hid_info(esp_hidh_cb_param_t *param, tBTA_HH_DEV_DSCP_INFO *src) +{ + param->dscp.dl_len = 0; + if (src->descriptor.dl_len > 0) { + param->dscp.dsc_list = (uint8_t *)osi_malloc(src->descriptor.dl_len); + } + if (param->dscp.dsc_list) { + memcpy(param->dscp.dsc_list, src->descriptor.dsc_list, src->descriptor.dl_len); + param->dscp.dl_len = src->descriptor.dl_len; + } + param->dscp.vendor_id = src->vendor_id; + param->dscp.product_id = src->product_id; + param->dscp.version = src->version; + param->dscp.ctry_code = src->ctry_code; + param->dscp.ssr_max_latency = src->ssr_max_latency; + param->dscp.ssr_min_tout = src->ssr_min_tout; + return true; +} + +void btc_hh_cb_handler(btc_msg_t *msg) +{ + esp_hidh_cb_param_t param = {0}; + tBTA_HH *p_data = (tBTA_HH *)msg->arg; + btc_hh_device_t *p_dev = NULL; + int len, i; + BTC_TRACE_DEBUG("%s: event=%s dereg = %d", __func__, dump_hh_event(msg->act), btc_hh_cb.service_dereg_active); + switch (msg->act) { + case BTA_HH_ENABLE_EVT: + if (p_data->status == BTA_HH_OK) { + btc_hh_cb.status = BTC_HH_ENABLED; + BTC_TRACE_DEBUG("Loading added devices"); + /* Add hid descriptors for already bonded hid devices*/ + // btc_storage_load_bonded_hid_info(); + } else { + btc_hh_cb.status = BTC_HH_DISABLED; + BTC_TRACE_ERROR("Error, HH enabling failed, status = %d", p_data->status); + } + param.init.status = p_data->status; + btc_hh_cb_to_app(ESP_HIDH_INIT_EVT, ¶m); + break; + case BTA_HH_DISABLE_EVT: + btc_hh_cb.status = BTC_HH_DISABLED; + if (btc_hh_cb.service_dereg_active) { + BTIF_TRACE_DEBUG("BTA_HH_DISABLE_EVT: enabling HID Device service"); + // btif_hd_service_registration(); + btc_hh_cb.service_dereg_active = FALSE; + } + if (p_data->status == BTA_HH_OK) { + // Clear the control block + for (uint8_t i = 0; i < BTC_HH_MAX_HID; i++) { + if (btc_hh_cb.devices[i].vup_timer) { + osi_alarm_free(btc_hh_cb.devices[i].vup_timer); + } + } + memset(&btc_hh_cb, 0, sizeof(btc_hh_cb)); + for (i = 0; i < BTC_HH_MAX_HID; i++) { + btc_hh_cb.devices[i].dev_status = ESP_HIDH_CONN_STATE_UNKNOWN; + } + } else { + BTC_TRACE_ERROR("Error, HH disabling failed, status = %d", p_data->status); + } + param.deinit.status = p_data->status; + btc_hh_cb_to_app(ESP_HIDH_DEINIT_EVT, ¶m); + break; + case BTA_HH_OPEN_EVT: + BTC_TRACE_DEBUG("handle=%d, status =%d", p_data->conn.handle, p_data->conn.status); + memset(btc_hh_cb.pending_conn_address, 0, BD_ADDR_LEN); + if (p_data->conn.status == BTA_HH_OK) { + p_dev = btc_hh_find_connected_dev_by_handle(p_data->conn.handle); + if (p_dev == NULL) { + BTC_TRACE_ERROR("Error, cannot find device with handle %d", p_data->conn.handle); + btc_hh_cb.status = (BTC_HH_STATUS)BTC_HH_DEV_DISCONNECTED; + // The connect request must come from device side and exceeded the + // connected HID device number. + BTA_HhClose(p_data->conn.handle); + + param.open.status = ESP_HIDH_ERR; + param.open.conn_status = ESP_HIDH_CONN_STATE_DISCONNECTED; + } else { + BTC_TRACE_DEBUG("Found device...Getting dscp info for handle " + "... %d", + p_data->conn.handle); + memcpy(p_dev->bd_addr, p_data->conn.bda, BD_ADDR_LEN); + btc_hh_cb.status = (BTC_HH_STATUS)BTC_HH_DEV_CONNECTED; + // Send set_idle if the peer_device is a keyboard [boblane] + // if (check_cod(&p_data->conn.bda, COD_HID_KEYBOARD) || check_cod(&p_data->conn.bda, COD_HID_COMBO)) + // BTA_HhSetIdle(p_data->conn.handle, 0); + btc_hh_cb.p_curr_dev = btc_hh_find_connected_dev_by_handle(p_data->conn.handle); + BTA_HhGetDscpInfo(p_data->conn.handle); + p_dev->dev_status = ESP_HIDH_CONN_STATE_CONNECTED; + + param.open.status = ESP_HIDH_OK; + param.open.conn_status = ESP_HIDH_CONN_STATE_CONNECTED; + } + } else { + p_dev = btc_hh_find_dev_by_bda(p_data->conn.bda); + if (p_dev != NULL) { + btc_hh_stop_vup_timer(p_dev->bd_addr); + p_dev->dev_status = ESP_HIDH_CONN_STATE_DISCONNECTED; + } + btc_hh_cb.status = (BTC_HH_STATUS)BTC_HH_DEV_DISCONNECTED; + + param.open.status = p_data->conn.status; + param.open.conn_status = ESP_HIDH_CONN_STATE_DISCONNECTED; + } + param.open.handle = p_data->conn.handle; + param.open.is_orig = p_data->conn.is_orig; + memcpy(param.open.bd_addr, p_data->conn.bda, BD_ADDR_LEN); + btc_hh_cb_to_app(ESP_HIDH_OPEN_EVT, ¶m); + break; + case BTA_HH_GET_DSCP_EVT: + len = p_data->dscp_info.descriptor.dl_len; + BTC_TRACE_DEBUG("len = %d", len); + do { + param.dscp.status = ESP_HIDH_OK; + param.dscp.handle = BTA_HH_INVALID_HANDLE; + param.dscp.added = false; + p_dev = btc_hh_cb.p_curr_dev; + if (p_dev == NULL) { + BTC_TRACE_ERROR("No HID device is currently connected"); + param.dscp.status = ESP_HIDH_NO_CONNECTION; + break; + } + + if (btc_hh_add_added_dev(p_dev->bd_addr, p_dev->attr_mask)) { + tBTA_HH_DEV_DSCP_INFO dscp_info; + bt_status_t ret; + btc_hh_copy_hid_info(&dscp_info, &p_data->dscp_info); + btc_hh_cb.add_event = BTC_HH_CONNECT_EVT; + BTA_HhAddDev(p_dev->bd_addr, p_dev->attr_mask, p_dev->sub_class, p_dev->app_id, dscp_info); + // write hid info to nvs + ret = btc_storage_add_hid_device_info((bt_bdaddr_t *)p_dev->bd_addr, p_dev->attr_mask, p_dev->sub_class, p_dev->app_id, + p_data->dscp_info.vendor_id, p_data->dscp_info.product_id, + p_data->dscp_info.version, p_data->dscp_info.ctry_code, + p_data->dscp_info.ssr_max_latency, p_data->dscp_info.ssr_min_tout, + len, p_data->dscp_info.descriptor.dsc_list); + + if (ret != BT_STATUS_SUCCESS) { + BTC_TRACE_ERROR("write hid info to nvs failed!"); + } + // Free buffer created for dscp_info; + if (dscp_info.descriptor.dl_len > 0 && dscp_info.descriptor.dsc_list != NULL) { + utl_freebuf((void **)&dscp_info.descriptor.dsc_list); + dscp_info.descriptor.dl_len = 0; + } + } else { + // Device already added. + BTC_TRACE_WARNING("%s: Device already added ", __func__); + param.dscp.added = true; + } + btc_hh_cb_copy_hid_info(¶m, &p_data->dscp_info); + param.dscp.handle = p_dev->dev_handle; + } while(0); + btc_hh_cb_to_app(ESP_HIDH_GET_DSCP_EVT, ¶m); + if (param.dscp.dl_len > 0 && param.dscp.dsc_list != NULL) { + utl_freebuf((void **)¶m.dscp.dsc_list); + param.dscp.dl_len = 0; + } + break; + case BTA_HH_CLOSE_EVT: + BTC_TRACE_DEBUG("status = %d, handle = %d", p_data->dev_status.status, + p_data->dev_status.handle); + p_dev = btc_hh_find_connected_dev_by_handle(p_data->dev_status.handle); + if (p_dev != NULL) { + BTC_TRACE_DEBUG("uhid local_vup=%d", p_dev->local_vup); + btc_hh_stop_vup_timer(p_dev->bd_addr); + /* If this is a locally initiated VUP, remove the bond as ACL got + * disconnected while VUP being processed. + */ + if (p_dev->local_vup) { + p_dev->local_vup = false; + BTA_DmRemoveDevice(p_dev->bd_addr, BT_TRANSPORT_BR_EDR); + } + + btc_hh_cb.status = (BTC_HH_STATUS)BTC_HH_DEV_DISCONNECTED; + p_dev->dev_status = ESP_HIDH_CONN_STATE_DISCONNECTED; + param.close.status = p_data->dev_status.status; + } else { + BTC_TRACE_ERROR("Error: cannot find device with handle %d", p_data->dev_status.handle); + param.close.status = ESP_HIDH_NO_CONNECTION; + } + param.close.handle = p_data->dev_status.handle; + param.close.conn_status = ESP_HIDH_CONN_STATE_DISCONNECTED; + btc_hh_cb_to_app(ESP_HIDH_CLOSE_EVT, ¶m); + break; + case BTA_HH_VC_UNPLUG_EVT: + BTC_TRACE_DEBUG("status = %d, handle = %d", p_data->dev_status.status, + p_data->dev_status.handle); + p_dev = btc_hh_find_connected_dev_by_handle(p_data->dev_status.handle); + btc_hh_cb.status = (BTC_HH_STATUS)BTC_HH_DEV_DISCONNECTED; + if (p_dev != NULL) { + /* Stop the VUP timer */ + btc_hh_stop_vup_timer(p_dev->bd_addr); + p_dev->dev_status = ESP_HIDH_CONN_STATE_DISCONNECTED; + BTC_TRACE_DEBUG("%s---Sending connection state change", __func__); + param.close.status = ESP_HIDH_OK; + param.close.handle = p_data->dev_status.handle; + param.close.conn_status = ESP_HIDH_CONN_STATE_DISCONNECTED; + btc_hh_cb_to_app(ESP_HIDH_CLOSE_EVT, ¶m); + BTC_TRACE_DEBUG("%s---Removing HID bond", __func__); + /* If it is locally initiated VUP or remote device has its major COD as + Peripheral removed the bond.*/ + // [boblane] + if (p_dev->local_vup) { + p_dev->local_vup = false; + BTA_DmRemoveDevice(p_dev->bd_addr, BT_TRANSPORT_BR_EDR); + } else { + btc_hh_remove_device(p_dev->bd_addr); + } + param.unplug.status = p_data->dev_status.status; + } else { + BTC_TRACE_ERROR("Error: cannot find device with handle %d", p_data->dev_status.handle); + param.unplug.status = ESP_HIDH_NO_CONNECTION; + } + param.unplug.handle = p_data->dev_status.handle; + param.unplug.conn_status = ESP_HIDH_CONN_STATE_DISCONNECTED; + btc_hh_cb_to_app(ESP_HIDH_VC_UNPLUG_EVT, ¶m); + break; + case BTA_HH_DATA_EVT: + BTC_TRACE_DEBUG("status = %d, handle = %d", p_data->send_data.status, + p_data->send_data.handle); + param.send_data.handle = p_data->send_data.handle; + param.send_data.status = p_data->send_data.status; + param.send_data.reason = p_data->send_data.reason; + btc_hh_cb_to_app(ESP_HIDH_DATA_EVT, ¶m); + break; + case BTA_HH_GET_PROTO_EVT: + BTC_TRACE_DEBUG("status = %d, handle = %d, proto = [%d], %s", p_data->hs_data.status, + p_data->hs_data.handle, p_data->hs_data.rsp_data.proto_mode, + (p_data->hs_data.rsp_data.proto_mode == BTA_HH_PROTO_RPT_MODE) + ? "Report Mode" + : (p_data->hs_data.rsp_data.proto_mode == BTA_HH_PROTO_BOOT_MODE) ? "Boot Mode" + : "Unsupported"); + param.get_proto.proto_mode = proto_mode_change_to_upper_layer(p_data->hs_data.rsp_data.proto_mode); + param.get_proto.handle = p_data->hs_data.handle; + param.get_proto.status = p_data->hs_data.status; + btc_hh_cb_to_app(ESP_HIDH_GET_PROTO_EVT, ¶m); + break; + case BTA_HH_SET_PROTO_EVT: + BTIF_TRACE_DEBUG("status = %d, handle = %d", p_data->dev_status.status, + p_data->dev_status.handle); + param.set_proto.handle = p_data->dev_status.handle; + param.set_proto.status = p_data->dev_status.status; + btc_hh_cb_to_app(ESP_HIDH_SET_PROTO_EVT, ¶m); + break; + case BTA_HH_GET_RPT_EVT: { + BT_HDR *hdr = p_data->hs_data.rsp_data.p_rpt_data; + uint8_t *data = NULL; + uint16_t len = 0; + BTC_TRACE_DEBUG("status = %d, handle = %d", p_data->hs_data.status, p_data->hs_data.handle); + /* p_rpt_data is NULL in HANDSHAKE response case */ + if (hdr) { + data = (uint8_t *)(hdr + 1) + hdr->offset; + len = hdr->len; + } + param.get_rpt.handle = p_data->hs_data.handle; + param.get_rpt.status = p_data->hs_data.status; + param.get_rpt.len = len; + param.get_rpt.data = data; + btc_hh_cb_to_app(ESP_HIDH_GET_RPT_EVT, ¶m); + break; + } + case BTA_HH_SET_RPT_EVT: + BTC_TRACE_DEBUG("status = %d, handle = %d", p_data->dev_status.status, + p_data->dev_status.handle); + param.set_rpt.handle = p_data->dev_status.handle; + param.set_rpt.status = p_data->dev_status.status; + btc_hh_cb_to_app(ESP_HIDH_SET_RPT_EVT, ¶m); + break; + case BTA_HH_GET_IDLE_EVT: + BTC_TRACE_DEBUG("handle = %d, status = %d, rate = %d", p_data->hs_data.handle, p_data->hs_data.status, + p_data->hs_data.rsp_data.idle_rate); + param.get_idle.handle = p_data->hs_data.handle; + param.get_idle.status = p_data->hs_data.status; + param.get_idle.idle_rate = p_data->hs_data.rsp_data.idle_rate; + btc_hh_cb_to_app(ESP_HIDH_GET_IDLE_EVT, ¶m); + break; + case BTA_HH_SET_IDLE_EVT: + BTC_TRACE_DEBUG("status = %d, handle = %d", p_data->dev_status.status, p_data->dev_status.handle); + param.set_idle.handle = p_data->dev_status.handle; + param.set_idle.status = p_data->dev_status.status; + btc_hh_cb_to_app(BTA_HH_SET_IDLE_EVT, ¶m); + break; + case BTA_HH_ADD_DEV_EVT: + BTC_TRACE_DEBUG("status = %d, handle = %d", p_data->dev_info.status, p_data->dev_info.handle); + for (uint8_t i = 0; i < BTC_HH_MAX_ADDED_DEV; i++) { + if (memcmp(btc_hh_cb.added_devices[i].bd_addr, p_data->dev_info.bda, BD_ADDR_LEN) == 0) { + if (p_data->dev_info.status == BTA_HH_OK) { + btc_hh_cb.added_devices[i].dev_handle = p_data->dev_info.handle; + } else { + memset(btc_hh_cb.added_devices[i].bd_addr, 0, BD_ADDR_LEN); + btc_hh_cb.added_devices[i].dev_handle = BTA_HH_INVALID_HANDLE; + } + break; + } + } + if (btc_hh_cb.add_event == BTC_HH_SET_INFO_EVT) { + param.set_info.handle = p_data->dev_info.handle; + param.set_info.status = p_data->dev_info.status; + memcpy(param.set_info.bd_addr, p_data->dev_info.bda, BD_ADDR_LEN); + btc_hh_cb_to_app(ESP_HIDH_SET_INFO_EVT, ¶m); + } else { + param.add_dev.handle = p_data->dev_info.handle; + param.add_dev.status = p_data->dev_info.status; + memcpy(param.add_dev.bd_addr, p_data->dev_info.bda, BD_ADDR_LEN); + btc_hh_cb_to_app(ESP_HIDH_ADD_DEV_EVT, ¶m); + } + break; + case BTA_HH_RMV_DEV_EVT: + BTC_TRACE_DEBUG("status = %d, handle = %d", p_data->dev_info.status, p_data->dev_info.handle); + param.rmv_dev.handle = p_data->dev_info.status; + param.rmv_dev.status = p_data->dev_info.handle; + memcpy(param.rmv_dev.bd_addr, p_data->dev_info.bda, BD_ADDR_LEN); + btc_hh_cb_to_app(ESP_HIDH_RMV_DEV_EVT, ¶m); + break; + case BTA_HH_DATA_IND_EVT: + BTC_TRACE_DEBUG("status = %d, handle = %d", p_data->int_data.status, p_data->int_data.handle); + if (p_data->int_data.status == BTA_HH_OK && p_data->int_data.p_data) { + param.data_ind.len = p_data->int_data.p_data->len; + param.data_ind.data = p_data->int_data.p_data->data + p_data->int_data.p_data->offset; + } + param.data_ind.handle = p_data->int_data.handle; + param.data_ind.status = p_data->int_data.status; + param.data_ind.proto_mode = proto_mode_change_to_upper_layer(p_data->int_data.proto_mode); + btc_hh_cb_to_app(ESP_HIDH_DATA_IND_EVT, ¶m); + break; + case BTA_HH_API_ERR_EVT: + break; + default: + BTC_TRACE_WARNING("%s: Unhandled event: %d", __func__, msg->act); + break; + } + btc_hh_cb_arg_deep_free(msg); +} + +void btc_hh_arg_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src) +{ + btc_hidh_args_t *dst = (btc_hidh_args_t *)p_dest; + btc_hidh_args_t *src = (btc_hidh_args_t *)p_src; + + switch (msg->act) { + case BTC_HH_SET_INFO_EVT: + dst->set_info.hid_info = (esp_hidh_hid_info_t *)osi_malloc(sizeof(esp_hidh_hid_info_t)); + if (dst->set_info.hid_info) { + memcpy(dst->set_info.hid_info, src->set_info.hid_info, sizeof(esp_hidh_hid_info_t)); + } else { + BTC_TRACE_ERROR("%s %d osi_malloc failed\n", __func__, msg->act); + } + break; + case BTC_HH_SET_REPORT_EVT: + dst->set_report.report = (uint8_t *)osi_malloc(src->set_report.len); + if (dst->set_report.report) { + memcpy(dst->set_report.report, src->set_report.report, src->set_report.len); + } else { + BTC_TRACE_ERROR("%s %d osi_malloc failed\n", __func__, msg->act); + } + break; + case BTC_HH_SEND_DATA_EVT: + dst->send_data.data = (uint8_t *)osi_malloc(src->send_data.len); + if (dst->send_data.data) { + memcpy(dst->send_data.data, src->send_data.data, src->send_data.len); + } else { + BTC_TRACE_ERROR("%s %d osi_malloc failed\n", __func__, msg->act); + } + break; + default: + break; + } +} + +#endif // HID_HOST_INCLUDED == TRUE diff --git a/lib/bt/host/bluedroid/btc/profile/std/include/bt_sdp.h b/lib/bt/host/bluedroid/btc/profile/std/include/bt_sdp.h new file mode 100644 index 00000000..63ca09a8 --- /dev/null +++ b/lib/bt/host/bluedroid/btc/profile/std/include/bt_sdp.h @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __BT_SDP_H__ +#define __BT_SDP_H__ + +#include +// #include "bluetooth.h" +#include "common/bt_defs.h" +#include "esp_bt_defs.h" + +#define SDP_OPP_SUPPORTED_FORMATS_MAX_LENGTH 15 + +/** + * These events are handled by the state machine + */ +typedef enum { + SDP_TYPE_RAW, // Used to carry raw SDP search data for unknown UUIDs + SDP_TYPE_MAP_MAS, // Message Access Profile - Server + SDP_TYPE_MAP_MNS, // Message Access Profile - Client (Notification Server) + SDP_TYPE_PBAP_PSE, // Phone Book Profile - Server + SDP_TYPE_PBAP_PCE, // Phone Book Profile - Client + SDP_TYPE_OPP_SERVER, // Object Push Profile + SDP_TYPE_SAP_SERVER // SIM Access Profile +} bluetooth_sdp_types; + +typedef struct _bluetooth_sdp_hdr { + bluetooth_sdp_types type; + esp_bt_uuid_t uuid; + uint32_t service_name_length; + char *service_name; + int32_t rfcomm_channel_number; + int32_t l2cap_psm; + int32_t profile_version; +} bluetooth_sdp_hdr; + +/** + * Some signals need additional pointers, hence we introduce a + * generic way to handle these pointers. + */ +typedef struct _bluetooth_sdp_hdr_overlay { + bluetooth_sdp_types type; + esp_bt_uuid_t bt_uuid; + uint32_t service_name_length; + char *service_name; + int32_t rfcomm_channel_number; + int32_t l2cap_psm; + int32_t profile_version; + + // User pointers, only used for some signals - see bluetooth_sdp_ops_record + int user1_ptr_len; + uint8_t *user1_ptr; + int user2_ptr_len; + uint8_t *user2_ptr; +} bluetooth_sdp_hdr_overlay; + +typedef struct _bluetooth_sdp_mas_record { + bluetooth_sdp_hdr_overlay hdr; + uint32_t mas_instance_id; + uint32_t supported_features; + uint32_t supported_message_types; +} bluetooth_sdp_mas_record; + +typedef struct _bluetooth_sdp_mns_record { + bluetooth_sdp_hdr_overlay hdr; + uint32_t supported_features; +} bluetooth_sdp_mns_record; + +typedef struct _bluetooth_sdp_pse_record { + bluetooth_sdp_hdr_overlay hdr; + uint32_t supported_features; + uint32_t supported_repositories; +} bluetooth_sdp_pse_record; + +typedef struct _bluetooth_sdp_pce_record { + bluetooth_sdp_hdr_overlay hdr; +} bluetooth_sdp_pce_record; + +typedef struct _bluetooth_sdp_ops_record { + bluetooth_sdp_hdr_overlay hdr; + int supported_formats_list_len; + uint8_t supported_formats_list[SDP_OPP_SUPPORTED_FORMATS_MAX_LENGTH]; +} bluetooth_sdp_ops_record; + +typedef struct _bluetooth_sdp_sap_record { + bluetooth_sdp_hdr_overlay hdr; +} bluetooth_sdp_sap_record; + +typedef union { + bluetooth_sdp_hdr_overlay hdr; + bluetooth_sdp_mas_record mas; + bluetooth_sdp_mns_record mns; + bluetooth_sdp_pse_record pse; + bluetooth_sdp_pce_record pce; + bluetooth_sdp_ops_record ops; + bluetooth_sdp_sap_record sap; +} bluetooth_sdp_record; + +#endif /* __BT_SDP_H__ */ diff --git a/lib/bt/host/bluedroid/btc/profile/std/include/btc_a2dp.h b/lib/bt/host/bluedroid/btc/profile/std/include/btc_a2dp.h new file mode 100644 index 00000000..6474d896 --- /dev/null +++ b/lib/bt/host/bluedroid/btc/profile/std/include/btc_a2dp.h @@ -0,0 +1,100 @@ +/* + * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/******************************************************************************* + * + * Filename: btc_a2dp.h + * + * Description: Common definitions for A2DP + * + *******************************************************************************/ + +#ifndef __BTC_A2DP_H__ +#define __BTC_A2DP_H__ + +#include +#include "common/bt_target.h" +#include "bta/bta_api.h" +#include "btc_av_api.h" +#include "esp_a2dp_api.h" + +#if BTC_AV_INCLUDED + +/******************************************************************************* + ** Constants + *******************************************************************************/ +#define BTC_AV_SUCCESS (0) +/** + * AV (Audio Video source) Errors + */ +#define BTC_ERROR_SRV_AV_NOT_ENABLED 700 /* AV is not enabled */ +#define BTC_ERROR_SRV_AV_FEEDING_NOT_SUPPORTED 701 /* Requested Feeding not supported */ +#define BTC_ERROR_SRV_AV_BUSY 702 /* Another operation ongoing */ +#define BTC_ERROR_SRV_AV_NOT_OPENED 703 /* No AV link opened */ +#define BTC_ERROR_SRV_AV_NOT_STARTED 704 /* AV is not started */ +#define BTC_ERROR_SRV_AV_CP_NOT_SUPPORTED 705 /* Content protection is not supported by all headsets */ + +/* Transcoding definition for TxTranscoding and RxTranscoding */ +#define BTC_MEDIA_TRSCD_OFF 0 +#define BTC_MEDIA_TRSCD_PCM_2_SBC 1 /* Tx */ + + +/******************************************************************************* + ** Data types + *******************************************************************************/ +typedef int tBTC_AV_STATUS; + +/******************************************************************************* + ** Public functions + *******************************************************************************/ + +void btc_a2dp_on_init(void); + +/******************************************************************************* + ** + ** Function btc_a2dp_on_idle + ** + ** Description Process 'idle' request from BTC AV state machine during + ** initialization + ** + *******************************************************************************/ +void btc_a2dp_on_idle(void); + +/******************************************************************************* + ** + ** Function btc_a2dp_on_started + ** + ** Description Process 'start' request from BTC AV state machine to prepare + ** for A2DP streaming + ** + ** Return TRUE if an ACK for the local command is sent + ** + *******************************************************************************/ +BOOLEAN btc_a2dp_on_started(tBTA_AV_START *p_av, BOOLEAN pending_start); + +/******************************************************************************* + ** + ** Function btc_a2dp_on_stopped + ** + ** Description Process 'stop' request from BTC AV state machine to stop + ** A2DP streaming + ** + *******************************************************************************/ +void btc_a2dp_on_stopped(tBTA_AV_SUSPEND *p_av); + +/******************************************************************************* + ** + ** Function btc_a2dp_on_suspended + ** + ** Description Process 'stop' request from BTC AV state machine to suspend + ** A2DP streaming + ** + *******************************************************************************/ +void btc_a2dp_on_suspended(tBTA_AV_SUSPEND *p_av); + +#endif /* #if BTC_AV_INCLUDED */ + +#endif /* __BTC_A2DP_H__ */ diff --git a/lib/bt/host/bluedroid/btc/profile/std/include/btc_a2dp_control.h b/lib/bt/host/bluedroid/btc/profile/std/include/btc_a2dp_control.h new file mode 100644 index 00000000..fc2640f6 --- /dev/null +++ b/lib/bt/host/bluedroid/btc/profile/std/include/btc_a2dp_control.h @@ -0,0 +1,102 @@ +/* + * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/******************************************************************************* + * + * Filename: btc_a2dp_control.h + * + *******************************************************************************/ + +#ifndef __BTC_A2DP_CONTROL_H__ +#define __BTC_A2DP_CONTROL_H__ + +#include +#include "common/bt_target.h" +#include "bta/bta_api.h" +#include "btc_av_api.h" +#include "esp_a2dp_api.h" + +#if BTC_AV_INCLUDED +/******************************************************************************* + ** Public functions + *******************************************************************************/ + +/******************************************************************************* + ** + ** Function btc_a2dp_control_media_ctrl + ** + ** Description Handle the media_ctrl command + ** + *******************************************************************************/ +void btc_a2dp_control_media_ctrl(esp_a2d_media_ctrl_t ctrl); + + +/******************************************************************************* + ** + ** Function btc_a2dp_control_datapath_ctrl + ** + ** Description Handle the media datapath event, which is adapted from UIPC + ** data channel from bluedroid + ** + *******************************************************************************/ +void btc_a2dp_control_datapath_ctrl(uint32_t dp_evt); + + +/******************************************************************************* + ** + ** Function btc_a2dp_control_command_ack + ** + ** Description Acknowledge the pending media_ctrl command + ** + *******************************************************************************/ +void btc_a2dp_control_command_ack(int status); + + +/******************************************************************************* + ** + ** Function btc_a2dp_control_get_datachnl_stat + ** + ** Description Check whether the data channel state is open + ** + ** Return TRUE if the data channel state is open + ** + *******************************************************************************/ +BOOLEAN btc_a2dp_control_get_datachnl_stat(void); + + +/******************************************************************************* + ** + ** Function btc_a2dp_control_set_datachnl_stat + ** + ** Description Set the data channel state flag + ** + *******************************************************************************/ +void btc_a2dp_control_set_datachnl_stat(BOOLEAN open); + + +/******************************************************************************* + ** + ** Function btc_a2dp_control_init + ** + ** Description Initialize the A2DP control module. It should be called during + ** the startup stage of A2DP streaming. + ** + *******************************************************************************/ +bool btc_a2dp_control_init(void); + + +/******************************************************************************* + ** + ** Function btc_a2dp_control_cleanup + ** + ** Description Cleanup the A2DP control module + ** + *******************************************************************************/ +void btc_a2dp_control_cleanup(void); + +#endif /* #if BTC_AV_INCLUDED */ + +#endif /* __BTC_A2DP_CONTROL_H__ */ diff --git a/lib/bt/host/bluedroid/btc/profile/std/include/btc_a2dp_sink.h b/lib/bt/host/bluedroid/btc/profile/std/include/btc_a2dp_sink.h new file mode 100644 index 00000000..18ebe430 --- /dev/null +++ b/lib/bt/host/bluedroid/btc/profile/std/include/btc_a2dp_sink.h @@ -0,0 +1,131 @@ +/* + * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/******************************************************************************* + * + * Filename: btc_a2dp_sink.h + * + *******************************************************************************/ + +#ifndef __BTC_A2DP_SINK_H__ +#define __BTC_A2DP_SINK_H__ + +#include +#include "common/bt_target.h" +#include "bta/bta_api.h" +#include "btc_av_api.h" +#include "esp_a2dp_api.h" + +#if BTC_AV_SINK_INCLUDED +/******************************************************************************* + ** Data types + *******************************************************************************/ +typedef struct { + BT_HDR hdr; + UINT8 codec_info[AVDT_CODEC_SIZE]; +} tBTC_MEDIA_SINK_CFG_UPDATE; + +/******************************************************************************* + ** Public functions + *******************************************************************************/ + +/******************************************************************************* + ** + ** Function btc_a2dp_sink_startup + ** + ** Description Initialize and startup the A2DP sink module. This function + ** should be called by the BTC AV state machine prior to using + ** the module. + ** + ** Returns true if success + ** + *******************************************************************************/ +bool btc_a2dp_sink_startup(void); + +/******************************************************************************* + ** + ** Function btc_a2dp_sink_shutdown + ** + ** Description Shutdown and cleanup the A2DP sink module + ** + *******************************************************************************/ +void btc_a2dp_sink_shutdown(void); + +/******************************************************************************* + ** + ** Function btc_a2dp_sink_rx_flush_req + ** + ** Description Request to flush audio decoding pipe + ** + ** Returns TRUE if success + ** + *******************************************************************************/ +BOOLEAN btc_a2dp_sink_rx_flush_req(void); + +/******************************************************************************* + ** + ** Function btc_a2dp_sink_enque_buf + ** + ** Description Enqueue a Advance Audio media buffer to be processed by btc media task. + ** + ** Returns size of the queue + ** + *******************************************************************************/ +UINT8 btc_a2dp_sink_enque_buf(BT_HDR *p_buf); + + +/******************************************************************************* + ** + ** Function btc_a2dp_sink_on_idle + ** + ** Description Process 'idle' request from the BTC AV state machine during + ** initialization + ** + *******************************************************************************/ +void btc_a2dp_sink_on_idle(void); + +/******************************************************************************* + ** + ** Function btc_a2dp_sink_on_stopped + ** + ** Description Process 'stop' request from the BTC AV state machine to stop + ** A2DP streaming + ** + *******************************************************************************/ +void btc_a2dp_sink_on_stopped(tBTA_AV_SUSPEND *p_av); + +/******************************************************************************* + ** + ** Function btc_a2dp_sink_on_suspended + ** + ** Description Process 'suspend' request from the BTC AV state machine to + ** suspend A2DP streaming + ** + *******************************************************************************/ +void btc_a2dp_sink_on_suspended(tBTA_AV_SUSPEND *p_av); + +/******************************************************************************* + ** + ** Function btc_a2dp_sink_set_rx_flush + ** + ** Description enable/disabel discarding of received A2DP frames + ** + *******************************************************************************/ +void btc_a2dp_sink_set_rx_flush(BOOLEAN enable); + +/******************************************************************************* + ** + ** Function btc_a2dp_sink_reset_decoder + ** + ** Description Reset decoder parameters according to configuration from remote + ** device + ** + *******************************************************************************/ +void btc_a2dp_sink_reset_decoder(UINT8 *p_av); + +#endif /* #if BTC_AV_SINK_INCLUDED */ + +#endif /* __BTC_A2DP_SINK_H__ */ diff --git a/lib/bt/host/bluedroid/btc/profile/std/include/btc_a2dp_source.h b/lib/bt/host/bluedroid/btc/profile/std/include/btc_a2dp_source.h new file mode 100644 index 00000000..f7ce1cbc --- /dev/null +++ b/lib/bt/host/bluedroid/btc/profile/std/include/btc_a2dp_source.h @@ -0,0 +1,245 @@ +/* + * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/******************************************************************************* + * + * Filename: btc_a2dp_source.h + * + *******************************************************************************/ + +#ifndef __BTC_A2DP_SOURCE_H__ +#define __BTC_A2DP_SOURCE_H__ + +#include +#include "common/bt_target.h" +#include "bta/bta_api.h" +#include "btc_av_api.h" +#include "esp_a2dp_api.h" + +#if BTC_AV_SRC_INCLUDED +/******************************************************************************* + ** Data types + *******************************************************************************/ + +/* tBTC_MEDIA_INIT_AUDIO msg structure */ +typedef struct { + BT_HDR hdr; + UINT16 SamplingFreq; /* 16k, 32k, 44.1k or 48k*/ + UINT8 ChannelMode; /* mono, dual, stereo or joint stereo*/ + UINT8 NumOfSubBands; /* 4 or 8 */ + UINT8 NumOfBlocks; /* 4, 8, 12 or 16*/ + UINT8 AllocationMethod; /* loudness or SNR*/ + UINT16 MtuSize; /* peer mtu size */ +} tBTC_MEDIA_INIT_AUDIO; + +/* tBTC_MEDIA_UPDATE_AUDIO msg structure */ +typedef struct { + BT_HDR hdr; + UINT16 MinMtuSize; /* Minimum peer mtu size */ + UINT8 MaxBitPool; /* Maximum peer bitpool */ + UINT8 MinBitPool; /* Minimum peer bitpool */ +} tBTC_MEDIA_UPDATE_AUDIO; + +/* tBTC_MEDIA_INIT_AUDIO_FEEDING msg structure */ +typedef struct { + BT_HDR hdr; + tBTC_AV_FEEDING_MODE feeding_mode; + tBTC_AV_MEDIA_FEEDINGS feeding; +} tBTC_MEDIA_INIT_AUDIO_FEEDING; + +/******************************************************************************* + ** Public functions + *******************************************************************************/ + +/******************************************************************************* + ** + ** Function btc_a2dp_source_startup + ** + ** Description Initialize and startup the A2DP source module. This function + ** should be called by the BTC AV state machine prior to using + ** the module + ** + ** Returns TRUE is success + ** + *******************************************************************************/ +bool btc_a2dp_source_startup(void); + +/******************************************************************************* + ** + ** Function btc_a2dp_source_shutdown + ** + ** Description Shutdown and cleanup the A2DP source module. + ** + *******************************************************************************/ +void btc_a2dp_source_shutdown(void); + +/******************************************************************************* + ** + ** Function btc_a2dp_source_enc_init_req + ** + ** Description Request to initialize the media task encoder + ** + ** Returns TRUE is success + ** + *******************************************************************************/ +BOOLEAN btc_a2dp_source_enc_init_req(tBTC_MEDIA_INIT_AUDIO *p_msg); + +/******************************************************************************* + ** + ** Function btc_a2dp_source_enc_udpate_req + ** + ** Description Request to update the media task encoder + ** + ** Returns TRUE is success + ** + *******************************************************************************/ +BOOLEAN btc_a2dp_source_enc_update_req(tBTC_MEDIA_UPDATE_AUDIO *p_msg); + + +/******************************************************************************* + ** + ** Function btc_a2dp_source_start_audio_req + ** + ** Description Request to start audio encoding task + ** + ** Returns TRUE is success + ** + *******************************************************************************/ +BOOLEAN btc_a2dp_source_start_audio_req(void); + +/******************************************************************************* + ** + ** Function btc_a2dp_source_stop_audio_req + ** + ** Description Request to stop audio encoding task + ** + ** Returns TRUE is success + ** + *******************************************************************************/ +BOOLEAN btc_a2dp_source_stop_audio_req(void); + +/******************************************************************************* + ** + ** Function btc_a2dp_source_tx_flush_req + ** + ** Description Request to flush audio encoding pipe + ** + ** Returns TRUE is success + ** + *******************************************************************************/ +BOOLEAN btc_a2dp_source_tx_flush_req(void); + +/******************************************************************************* + ** + ** Function btc_a2dp_source_audio_readbuf + ** + ** Description Read an audio buffer from the BTC media TX queue + ** + ** Returns pointer on a aa buffer ready to send + ** + *******************************************************************************/ +BT_HDR *btc_a2dp_source_audio_readbuf(void); + +/******************************************************************************* + ** + ** Function btc_a2dp_source_audio_feeding_init_req + ** + ** Description Request to initialize audio feeding + ** + ** Returns TRUE if success + ** + *******************************************************************************/ + +BOOLEAN btc_a2dp_source_audio_feeding_init_req(tBTC_MEDIA_INIT_AUDIO_FEEDING *p_msg); + +/******************************************************************************* + ** + ** Function btc_a2dp_source_is_streaming + ** + ** Description Check whether A2DP source is in streaming state + ** + *******************************************************************************/ +bool btc_a2dp_source_is_streaming(void); + +/******************************************************************************* + ** + ** Function btc_a2dp_source_is_task_shutting_down + ** + ** Description Check whether A2DP source media task is shutting down + ** + *******************************************************************************/ +bool btc_a2dp_source_is_task_shutting_down(void); + + +/******************************************************************************* + ** + ** Function btc_a2dp_source_on_idle + ** + ** Description Request 'idle' request from BTC AV state machine during + ** initialization + ** + *******************************************************************************/ +void btc_a2dp_source_on_idle(void); + +/******************************************************************************* + ** + ** Function btc_a2dp_source_on_stopped + ** + ** Description Process 'stop' request from the BTC AV state machine to stop + ** A2DP streaming + ** + *******************************************************************************/ +void btc_a2dp_source_on_stopped(tBTA_AV_SUSPEND *p_av); + +/******************************************************************************* + ** + ** Function btc_a2dp_source_on_suspended + ** + ** Description Process 'suspend' request from the BTC AV state machine to stop + ** A2DP streaming + ** + *******************************************************************************/ +void btc_a2dp_source_on_suspended(tBTA_AV_SUSPEND *p_av); + +/******************************************************************************* + ** + ** Function btc_a2dp_source_setup_codec + ** + ** Description initialize the encoder parameters + ** + *******************************************************************************/ +void btc_a2dp_source_setup_codec(void); + +/******************************************************************************* + ** + ** Function btc_a2dp_source_set_tx_flush + ** + ** Description enable/disable discarding of transmitted frames + ** + *******************************************************************************/ +void btc_a2dp_source_set_tx_flush(BOOLEAN enable); + +/******************************************************************************* + ** + ** Function btc_a2dp_source_encoder_update + ** + ** Description update changed SBC encoder parameters + ** + *******************************************************************************/ +void btc_a2dp_source_encoder_update(void); + +/***************************************************************************** +** +** Function btc_source_report_delay_value +** +** Description Report sink delay report value +** +*******************************************************************************/ +void btc_source_report_delay_value(UINT16 delay_value); + +#endif /* #if BTC_AV_SRC_INCLUDED */ + +#endif /* __BTC_A2DP_SOURCE_H__ */ diff --git a/lib/bt/host/bluedroid/btc/profile/std/include/btc_av.h b/lib/bt/host/bluedroid/btc/profile/std/include/btc_av.h new file mode 100644 index 00000000..3dd2ebe6 --- /dev/null +++ b/lib/bt/host/bluedroid/btc/profile/std/include/btc_av.h @@ -0,0 +1,228 @@ +/* + * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + + +/******************************************************************************* + * + * Filename: btc_av.h + * + * Description: Main API header file for all BTC AV functions accessed + * from internal stack. + * + *******************************************************************************/ + +#ifndef __BTC_AV_H__ +#define __BTC_AV_H__ + +#include "common/bt_target.h" +#include "esp_a2dp_api.h" +#include "btc/btc_task.h" +#include "btc/btc_common.h" +#include "btc/btc_sm.h" +#include "bta/bta_av_api.h" + +#if (BTC_AV_INCLUDED == TRUE) + +// global variable to inidcate avrc is initialized with a2dp +extern bool g_av_with_rc; +// global variable to indicate a2dp is initialized +extern bool g_a2dp_on_init; +// global variable to indicate a2dp is deinitialized +extern bool g_a2dp_on_deinit; +// global variable to indicate a2dp source deinitialization is ongoing +extern bool g_a2dp_source_ongoing_deinit; +// global variable to indicate a2dp sink deinitialization is ongoing +extern bool g_a2dp_sink_ongoing_deinit; + +/******************************************************************************* +** Type definitions for callback functions +********************************************************************************/ + +enum { + BTC_AV_DATAPATH_OPEN_EVT, // original UIPC_OPEN_EVT for data channel in bluedroid + BTC_AV_DATAPATH_MAX_EVT, +}; + +typedef enum { + BTC_AV_CONNECT_REQ_EVT = BTA_AV_MAX_EVT, + BTC_AV_DISCONNECT_REQ_EVT, + BTC_AV_START_STREAM_REQ_EVT, + BTC_AV_SUSPEND_STREAM_REQ_EVT, + BTC_AV_SINK_CONFIG_REQ_EVT, +} btc_av_sm_event_t; + +typedef enum { +#if BTC_AV_SINK_INCLUDED + BTC_AV_SINK_API_INIT_EVT = 0, + BTC_AV_SINK_API_DEINIT_EVT, + BTC_AV_SINK_API_CONNECT_EVT, + BTC_AV_SINK_API_DISCONNECT_EVT, + BTC_AV_SINK_API_REG_DATA_CB_EVT, + BTC_AV_SINK_API_SET_DELAY_VALUE_EVT, + BTC_AV_SINK_API_GET_DELAY_VALUE_EVT, +#endif /* BTC_AV_SINK_INCLUDED */ +#if BTC_AV_SRC_INCLUDED + BTC_AV_SRC_API_INIT_EVT, + BTC_AV_SRC_API_DEINIT_EVT, + BTC_AV_SRC_API_CONNECT_EVT, + BTC_AV_SRC_API_DISCONNECT_EVT, + BTC_AV_SRC_API_REG_DATA_CB_EVT, +#endif /* BTC_AV_SRC_INCLUDED */ + BTC_AV_API_MEDIA_CTRL_EVT, +} btc_av_act_t; + +/* btc_av_args_t */ +typedef union { +#if BTC_AV_SINK_INCLUDED + // BTC_AV_SINK_CONFIG_REQ_EVT -- internal event + esp_a2d_mcc_t mcc; + // BTC_AV_SINK_API_CONNECT_EVT + bt_bdaddr_t connect; + // BTC_AV_SINK_API_DISCONNECT_EVT + bt_bdaddr_t disconn; + // BTC_AV_SINK_API_REG_DATA_CB_EVT + esp_a2d_sink_data_cb_t data_cb; + // BTC_AV_SINK_API_SET_DELAY_VALUE_EVT + uint16_t delay_value; +#endif /* BTC_AV_SINK_INCLUDED */ +#if BTC_AV_SRC_INCLUDED + // BTC_AV_SRC_API_REG_DATA_CB_EVT + esp_a2d_source_data_cb_t src_data_cb; + // BTC_AV_SRC_API_CONNECT + bt_bdaddr_t src_connect; + // BTC_AV_SRC_API_DISCONNECT_EVT + bt_bdaddr_t src_disconn; +#endif /* BTC_AV_SRC_INCLUDED */ + // BTC_AV_API_MEDIA_CTRL_EVT + esp_a2d_media_ctrl_t ctrl; +} btc_av_args_t; + +/******************************************************************************* +** BTC AV API +********************************************************************************/ + +void btc_a2dp_call_handler(btc_msg_t *msg); + +void btc_a2dp_cb_handler(btc_msg_t *msg); + +void btc_a2dp_sink_reg_data_cb(esp_a2d_sink_data_cb_t callback); + +void btc_a2dp_src_reg_data_cb(esp_a2d_source_data_cb_t callback); +/******************************************************************************* +** +** Function btc_av_get_sm_handle +** +** Description Fetches current av SM handle +** +** Returns None +** +*******************************************************************************/ + +btc_sm_handle_t btc_av_get_sm_handle(void); + +/******************************************************************************* +** +** Function btc_av_stream_ready +** +** Description Checks whether AV is ready for starting a stream +** +** Returns None +** +*******************************************************************************/ + +BOOLEAN btc_av_stream_ready(void); + +/******************************************************************************* +** +** Function btc_av_stream_started_ready +** +** Description Checks whether AV ready for media start in streaming state +** +** Returns None +** +*******************************************************************************/ + +BOOLEAN btc_av_stream_started_ready(void); + +/******************************************************************************* +** +** Function btc_dispatch_sm_event +** +** Description Send event to AV statemachine +** +** Returns None +** +*******************************************************************************/ + +/* used to pass events to AV statemachine from other tasks */ +void btc_dispatch_sm_event(btc_av_sm_event_t event, void *p_data, int len); + +/******************************************************************************* +** +** Function btc_av_is_connected +** +** Description Checks if av has a connected sink +** +** Returns BOOLEAN +** +*******************************************************************************/ + +BOOLEAN btc_av_is_connected(void); + + +/******************************************************************************* + * + * Function btc_av_get_peer_sep + * + * Description Get the stream endpoint type. + * + * Returns The stream endpoint type: either AVDT_TSEP_SRC or + * AVDT_TSEP_SNK. + * + ******************************************************************************/ + +uint8_t btc_av_get_peer_sep(void); + +/******************************************************************************* +** +** Function btc_av_is_peer_edr +** +** Description Check if the connected a2dp device supports +** EDR or not. Only when connected this function +** will accurately provide a true capability of +** remote peer. If not connected it will always be false. +** +** Returns TRUE if remote device is capable of EDR +** +*******************************************************************************/ + +BOOLEAN btc_av_is_peer_edr(void); + +/****************************************************************************** +** +** Function btc_av_clear_remote_suspend_flag +** +** Description Clears remote suspended flag +** +** Returns Void +********************************************************************************/ +void btc_av_clear_remote_suspend_flag(void); + +/******************************************************************************* + * + * Function btc_av_get_service_id + * + * Description Get the current AV service ID. + * + * Returns The stream endpoint type: either BTA_A2DP_SOURCE_SERVICE_ID or + * BTA_A2DP_SINK_SERVICE_ID. + * + ******************************************************************************/ +uint8_t btc_av_get_service_id(void); + +#endif ///BTC_AV_INCLUDED == TRUE + +#endif /* __BTC_AV_H__ */ diff --git a/lib/bt/host/bluedroid/btc/profile/std/include/btc_av_api.h b/lib/bt/host/bluedroid/btc/profile/std/include/btc_av_api.h new file mode 100644 index 00000000..72ee3bb7 --- /dev/null +++ b/lib/bt/host/bluedroid/btc/profile/std/include/btc_av_api.h @@ -0,0 +1,193 @@ +/* + * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + + +/***************************************************************************** + ** + ** Name: btc_av_api.h + ** + ** Description: This is the public interface file for the advanced + ** audio/video streaming (AV) subsystem of BTC. + ** + *****************************************************************************/ + +#ifndef __BTC_AV_API_H__ +#define __BTC_AV_API_H__ + +#include "common/bt_target.h" +#include "bta/bta_av_api.h" +#include "stack/a2d_api.h" +#include "stack/a2d_sbc.h" + +#if (BTC_AV_INCLUDED == TRUE) +/***************************************************************************** + ** Constants and data types + *****************************************************************************/ + +/* Codec type */ +#define BTC_AV_CODEC_NONE 0xFF +#define BTC_AV_CODEC_SBC A2D_MEDIA_CT_SBC /* SBC media codec type */ + +#define BTC_AV_CODEC_PCM 0x5 /* Raw PCM */ + +typedef UINT8 tBTC_AV_CODEC_ID; + +/* AV features masks */ +#define BTC_AV_FEAT_RCTG BTA_AV_FEAT_RCTG /* remote control target */ +#define BTC_AV_FEAT_RCCT BTA_AV_FEAT_RCCT /* remote control controller */ +#define BTC_AV_FEAT_METADATA BTA_AV_FEAT_METADATA /* remote control Metadata Transfer command/response */ + +typedef UINT16 tBTC_AV_FEAT; + +/* AV channel values */ +#define BTC_AV_CHNL_MSK BTA_AV_CHNL_MSK +#define BTC_AV_CHNL_AUDIO BTA_AV_CHNL_AUDIO /* audio channel */ +#define BTC_AV_CHNL_VIDEO BTA_AV_CHNL_VIDEO /* video channel */ +typedef UINT8 tBTC_AV_CHNL; + +typedef UINT8 tBTC_AV_HNDL; + +/* Operation id list for BTC_AvRemoteCmd */ +#define BTC_AV_ID_SELECT 0x00 /* select */ +#define BTC_AV_ID_UP 0x01 /* up */ +#define BTC_AV_ID_DOWN 0x02 /* down */ +#define BTC_AV_ID_LEFT 0x03 /* left */ +#define BTC_AV_ID_RIGHT 0x04 /* right */ +#define BTC_AV_ID_RIGHT_UP 0x05 /* right-up */ +#define BTC_AV_ID_RIGHT_DOWN 0x06 /* right-down */ +#define BTC_AV_ID_LEFT_UP 0x07 /* left-up */ +#define BTC_AV_ID_LEFT_DOWN 0x08 /* left-down */ +#define BTC_AV_ID_ROOT_MENU 0x09 /* root menu */ +#define BTC_AV_ID_SETUP_MENU 0x0A /* setup menu */ +#define BTC_AV_ID_CONT_MENU 0x0B /* contents menu */ +#define BTC_AV_ID_FAV_MENU 0x0C /* favorite menu */ +#define BTC_AV_ID_EXIT 0x0D /* exit */ +#define BTC_AV_ID_0 0x20 /* 0 */ +#define BTC_AV_ID_1 0x21 /* 1 */ +#define BTC_AV_ID_2 0x22 /* 2 */ +#define BTC_AV_ID_3 0x23 /* 3 */ +#define BTC_AV_ID_4 0x24 /* 4 */ +#define BTC_AV_ID_5 0x25 /* 5 */ +#define BTC_AV_ID_6 0x26 /* 6 */ +#define BTC_AV_ID_7 0x27 /* 7 */ +#define BTC_AV_ID_8 0x28 /* 8 */ +#define BTC_AV_ID_9 0x29 /* 9 */ +#define BTC_AV_ID_DOT 0x2A /* dot */ +#define BTC_AV_ID_ENTER 0x2B /* enter */ +#define BTC_AV_ID_CLEAR 0x2C /* clear */ +#define BTC_AV_ID_CHAN_UP 0x30 /* channel up */ +#define BTC_AV_ID_CHAN_DOWN 0x31 /* channel down */ +#define BTC_AV_ID_PREV_CHAN 0x32 /* previous channel */ +#define BTC_AV_ID_SOUND_SEL 0x33 /* sound select */ +#define BTC_AV_ID_INPUT_SEL 0x34 /* input select */ +#define BTC_AV_ID_DISP_INFO 0x35 /* display information */ +#define BTC_AV_ID_HELP 0x36 /* help */ +#define BTC_AV_ID_PAGE_UP 0x37 /* page up */ +#define BTC_AV_ID_PAGE_DOWN 0x38 /* page down */ +#define BTC_AV_ID_POWER 0x40 /* power */ +#define BTC_AV_ID_VOL_UP 0x41 /* volume up */ +#define BTC_AV_ID_VOL_DOWN 0x42 /* volume down */ +#define BTC_AV_ID_MUTE 0x43 /* mute */ +#define BTC_AV_ID_PLAY 0x44 /* play */ +#define BTC_AV_ID_STOP 0x45 /* stop */ +#define BTC_AV_ID_PAUSE 0x46 /* pause */ +#define BTC_AV_ID_RECORD 0x47 /* record */ +#define BTC_AV_ID_REWIND 0x48 /* rewind */ +#define BTC_AV_ID_FAST_FOR 0x49 /* fast forward */ +#define BTC_AV_ID_EJECT 0x4A /* eject */ +#define BTC_AV_ID_FORWARD 0x4B /* forward */ +#define BTC_AV_ID_BACKWARD 0x4C /* backward */ +#define BTC_AV_ID_ANGLE 0x50 /* angle */ +#define BTC_AV_ID_SUBPICT 0x51 /* subpicture */ +#define BTC_AV_ID_F1 0x71 /* F1 */ +#define BTC_AV_ID_F2 0x72 /* F2 */ +#define BTC_AV_ID_F3 0x73 /* F3 */ +#define BTC_AV_ID_F4 0x74 /* F4 */ +#define BTC_AV_ID_F5 0x75 /* F5 */ +#define BTC_AV_ID_VENDOR 0x7E /* vendor unique */ +#define BTC_AV_KEYPRESSED_RELEASE 0x80 + +typedef UINT8 tBTC_AV_RC; + +/* State flag for pass through command */ +#define BTC_AV_STATE_PRESS 0 /* key pressed */ +#define BTC_AV_STATE_RELEASE 1 /* key released */ + +typedef UINT8 tBTC_AV_STATE; + +typedef UINT8 tBTC_AV_RC_HNDL; + +/* Command codes for BTC_AvVendorCmd */ +#define BTC_AV_CMD_CTRL 0 +#define BTC_AV_CMD_STATUS 1 +#define BTC_AV_CMD_SPEC_INQ 2 +#define BTC_AV_CMD_NOTIF 3 +#define BTC_AV_CMD_GEN_INQ 4 + +typedef UINT8 tBTC_AV_CMD; + +/* AV callback events */ +#define BTC_AV_OPEN_EVT 0 /* connection opened */ +#define BTC_AV_CLOSE_EVT 1 /* connection closed */ +#define BTC_AV_START_EVT 2 /* stream data transfer started */ +#define BTC_AV_STOP_EVT 3 /* stream data transfer stopped */ +#define BTC_AV_RC_OPEN_EVT 4 /* remote control channel open */ +#define BTC_AV_RC_CLOSE_EVT 5 /* remote control channel closed */ +#define BTC_AV_REMOTE_CMD_EVT 6 /* remote control command */ +#define BTC_AV_REMOTE_RSP_EVT 7 /* remote control response */ +#define BTC_AV_META_MSG_EVT 8 /* metadata messages */ + +typedef UINT8 tBTC_AV_EVT; + +#define BTC_AV_FEEDING_ASYNCHRONOUS 0 /* asynchronous feeding, use tx av timer */ +#define BTC_AV_FEEDING_SYNCHRONOUS 1 /* synchronous feeding, no av tx timer */ + +#define BTC_AV_MAX_SYNCHRONOUS_LATENCY 80 /* max latency in ms for BTC_AV_FEEDING_SYNCHRONOUS */ +#define BTC_AV_MIN_SYNCHRONOUS_LATENCY 4 /* min latency in ms for BTC_AV_FEEDING_SYNCHRONOUS */ + +typedef UINT8 tBTC_AV_FEEDING_MODE; + +#define BTC_AV_CHANNEL_MODE_MONO A2D_SBC_IE_CH_MD_MONO +#define BTC_AV_CHANNEL_MODE_STEREO A2D_SBC_IE_CH_MD_STEREO +#define BTC_AV_CHANNEL_MODE_JOINT A2D_SBC_IE_CH_MD_JOINT +#define BTC_AV_CHANNEL_MODE_DUAL A2D_SBC_IE_CH_MD_DUAL + +typedef UINT8 tBTC_AV_CHANNEL_MODE; + +/** + * Structure used to configure the AV codec capabilities/config + */ +typedef struct { + tBTC_AV_CODEC_ID id; /* Codec ID (in terms of BTC) */ + UINT8 info[AVDT_CODEC_SIZE]; /* Codec info (can be config or capabilities) */ +} tBTC_AV_CODEC_INFO; + +/** + * Structure used to configure the AV media feeding + */ +typedef struct { + UINT16 sampling_freq; /* 44100, 48000 etc */ + UINT16 num_channel; /* 1 for mono or 2 stereo */ + UINT8 bit_per_sample; /* Number of bits per sample (8, 16) */ +} tBTC_AV_MEDIA_FEED_CFG_PCM; + +typedef union { + tBTC_AV_MEDIA_FEED_CFG_PCM pcm; /* Raw PCM feeding format */ +} tBTC_AV_MEDIA_FEED_CFG; + +typedef struct { + tBTC_AV_CODEC_ID format; /* Media codec identifier */ + tBTC_AV_MEDIA_FEED_CFG cfg; /* Media codec configuration */ +} tBTC_AV_MEDIA_FEEDINGS; + + +#ifdef __cplusplus +} +#endif + +#endif ///BTC_AV_INCLUDED == TRUE + +#endif /* __BTC_AV_API_H__ */ diff --git a/lib/bt/host/bluedroid/btc/profile/std/include/btc_avrc.h b/lib/bt/host/bluedroid/btc/profile/std/include/btc_avrc.h new file mode 100644 index 00000000..83200ae1 --- /dev/null +++ b/lib/bt/host/bluedroid/btc/profile/std/include/btc_avrc.h @@ -0,0 +1,185 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __BTC_AVRC_H__ +#define __BTC_AVRC_H__ + +#include +#include +#include "common/bt_defs.h" +#include "stack/bt_types.h" +#include "bta/bta_av_api.h" +#include "btc/btc_task.h" +#include "esp_avrc_api.h" + +#if (BTC_AV_INCLUDED == TRUE) +#ifndef BTC_AVRC_TGT_INCLUDED +#define BTC_AVRC_TGT_INCLUDED FALSE +#endif + +typedef enum { + BTC_AVRC_CT_API_INIT_EVT = 0, + BTC_AVRC_CT_API_DEINIT_EVT, + BTC_AVRC_CTRL_API_SND_PTCMD_EVT, + BTC_AVRC_STATUS_API_SND_META_EVT, + BTC_AVRC_STATUS_API_SND_PLAY_STATUS_EVT, + BTC_AVRC_STATUS_API_SND_GET_RN_CAPS_EVT, + BTC_AVRC_NOTIFY_API_SND_REG_NOTIFY_EVT, + BTC_AVRC_CTRL_API_SND_SET_PLAYER_SETTING_EVT, + BTC_AVRC_CTRL_API_SND_SET_ABSOLUTE_VOLUME_EVT +} btc_avrc_act_t; + +typedef struct { + uint8_t tl; /* transaction label */ + uint8_t key_code; + uint8_t key_state; +} pt_cmd_t; + +typedef struct { + uint8_t tl; + uint8_t attr_mask; +} md_cmd_t; + +typedef struct { + uint8_t tl; + uint8_t event_id; + uint32_t event_parameter; +} rn_cmd_t; + +typedef struct { + uint8_t tl; + uint8_t attr_id; + uint8_t value_id; +} ps_cmd_t; + +typedef struct { + uint8_t tl; +} get_caps_cmd_t; + +#define BTC_AVRC_MIN_VOLUME 0x00 +#define BTC_AVRC_MAX_VOLUME 0x7f + +typedef struct { + uint8_t tl; + uint8_t volume; +} set_abs_vol_cmd_t; + +/* btc_avrc_args_t */ +typedef union { + pt_cmd_t pt_cmd; + md_cmd_t md_cmd; + rn_cmd_t rn_cmd; + ps_cmd_t ps_cmd; + get_caps_cmd_t get_caps_cmd; + set_abs_vol_cmd_t set_abs_vol_cmd; +} btc_avrc_args_t; + +/* btc_avrc_tg_act_t */ +typedef enum { + BTC_AVRC_TG_API_INIT_EVT = 0, + BTC_AVRC_TG_API_DEINIT_EVT, + BTC_AVRC_TG_API_SET_RN_SUPPORTED_EVT, + BTC_AVRC_TG_API_SET_PSTH_SUPPORTED_CMD_EVT, + BTC_AVRC_TG_API_SEND_RN_RSP_EVT, +} btc_avrc_tg_act_t; + +/***************************************************************************** +** Constants & Macros +******************************************************************************/ +/* for AVRC 1.4 need to change this */ +#define BTC_RC_CT_INIT_MAGIC 0x20181128 +#define BTC_RC_TG_INIT_MAGIC 0x20181129 + +#define MAX_RC_NOTIFICATIONS (13) // refer to ESP_AVRC_RN_MAX_EVT + + +#define CHECK_ESP_RC_CONNECTED do { \ + BTC_TRACE_DEBUG("## %s ##", __FUNCTION__); \ + if (btc_rc_cb.rc_connected == FALSE) { \ + BTC_TRACE_WARNING("Function %s() called when RC is not connected", __FUNCTION__); \ + return ESP_ERR_INVALID_STATE; \ + } \ + } while (0) + +/***************************************************************************** +** Local type definitions +******************************************************************************/ +typedef struct { + BOOLEAN registered; + UINT8 label; +} btc_rc_reg_ntf_t; + +typedef struct { + BOOLEAN rc_connected; + UINT8 rc_handle; + tBTA_AV_FEAT rc_features; + UINT16 rc_ct_features; + UINT16 rc_tg_features; + BD_ADDR rc_addr; + btc_rc_reg_ntf_t rc_ntf[MAX_RC_NOTIFICATIONS]; +} btc_rc_cb_t; + +/***************************************************************************** +** Static variables +******************************************************************************/ +#if AVRC_DYNAMIC_MEMORY == TRUE +extern btc_rc_cb_t *btc_rc_cb_ptr; +#define btc_rc_cb (*btc_rc_cb_ptr) +#endif ///AVRC_DYNAMIC_MEMORY == FALSE + +typedef struct { + esp_avrc_rn_event_ids_t event_id; + esp_avrc_rn_rsp_t rsp; + esp_avrc_rn_param_t param; +} rn_rsp_t; + +/* btc_avrc_tg_args_t */ +typedef union { + rn_rsp_t rn_rsp; /* BTC_AVRC_TG_API_SEND_RN_RSP_EVT */ + uint16_t set_rn_evt; /* BTC_AVRC_TG_API_SET_RN_SUPPORTED_EVT */ + uint16_t *set_psth_cmd; /* BTC_AVRC_TG_API_SET_PSTH_SUPPORTED_CMD_EVT */ +} btc_avrc_tg_args_t; + +void btc_rc_handler(tBTA_AV_EVT event, tBTA_AV *p_data); + +BOOLEAN btc_rc_get_connected_peer(BD_ADDR peer_addr); + +/******************************************************************************* +** BTC AVRC API +********************************************************************************/ +void btc_avrc_ct_call_handler(btc_msg_t *msg); +void btc_avrc_tg_call_handler(btc_msg_t *msg); +void btc_avrc_tg_arg_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src); +void btc_avrc_tg_arg_deep_free(btc_msg_t *msg); + +bool btc_avrc_tg_init_p(void); +bool btc_avrc_ct_init_p(void); +bool btc_avrc_tg_connected_p(void); +bool btc_avrc_ct_connected_p(void); + +const uint16_t *btc_avrc_tg_get_supported_command(void); +const uint16_t *btc_avrc_tg_get_allowed_command(void); +bool btc_avrc_tg_check_supported_command(const uint16_t *cmd_set); + +uint16_t btc_avrc_tg_get_rn_allowed_evt(void); +uint16_t btc_avrc_tg_get_rn_supported_evt(void); +bool btc_avrc_tg_check_rn_supported_evt(uint16_t evt_set); +bool btc_avrc_tg_rn_evt_supported(uint8_t event_id); +bool btc_avrc_ct_rn_evt_supported(uint8_t event_id); + +#endif ///BTC_AV_INCLUDED == TRUE + +#endif /* __BTC_AVRC_H__ */ diff --git a/lib/bt/host/bluedroid/btc/profile/std/include/btc_gap_ble.h b/lib/bt/host/bluedroid/btc/profile/std/include/btc_gap_ble.h new file mode 100644 index 00000000..3fb5f4b3 --- /dev/null +++ b/lib/bt/host/bluedroid/btc/profile/std/include/btc_gap_ble.h @@ -0,0 +1,421 @@ +/* + * SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __BTC_GAP_BLE_H__ +#define __BTC_GAP_BLE_H__ + +#include "esp_bt_defs.h" +#include "esp_gap_ble_api.h" + +#if BTC_DYNAMIC_MEMORY == TRUE +#include "bta/bta_api.h" +extern tBTA_BLE_ADV_DATA *gl_bta_adv_data_ptr; +extern tBTA_BLE_ADV_DATA *gl_bta_scan_rsp_data_ptr; +#define gl_bta_adv_data (*gl_bta_adv_data_ptr) +#define gl_bta_scan_rsp_data (*gl_bta_scan_rsp_data_ptr) +#endif + +#define BLE_ISVALID_PARAM(x, min, max) (((x) >= (min) && (x) <= (max))) + +typedef enum { +#if (BLE_42_FEATURE_SUPPORT == TRUE) + BTC_GAP_BLE_ACT_CFG_ADV_DATA = 0, + BTC_GAP_BLE_ACT_SET_SCAN_PARAM, + BTC_GAP_BLE_ACT_START_SCAN, + BTC_GAP_BLE_ACT_STOP_SCAN, + BTC_GAP_BLE_ACT_START_ADV, + BTC_GAP_BLE_ACT_STOP_ADV, +#endif // #if (BLE_42_FEATURE_SUPPORT == TRUE) + BTC_GAP_BLE_ACT_UPDATE_CONN_PARAM = 6, + BTC_GAP_BLE_ACT_SET_PKT_DATA_LEN, + BTC_GAP_BLE_ACT_SET_RAND_ADDRESS, + BTC_GAP_BLE_ACT_CLEAR_RAND_ADDRESS, + BTC_GAP_BLE_ACT_CONFIG_LOCAL_PRIVACY, + BTC_GAP_BLE_ACT_CONFIG_LOCAL_ICON, + BTC_GAP_BLE_ACT_UPDATE_WHITE_LIST, + BTC_GAP_BLE_ACT_CLEAR_WHITE_LIST, +#if (BLE_42_FEATURE_SUPPORT == TRUE) + BTC_GAP_BLE_ACT_SET_CONN_PARAMS, +#endif // #if (BLE_42_FEATURE_SUPPORT == TRUE) + BTC_GAP_BLE_ACT_SET_DEV_NAME = 15, +#if (BLE_42_FEATURE_SUPPORT == TRUE) + BTC_GAP_BLE_ACT_CFG_ADV_DATA_RAW, + BTC_GAP_BLE_ACT_CFG_SCAN_RSP_DATA_RAW, +#endif // #if (BLE_42_FEATURE_SUPPORT == TRUE) + BTC_GAP_BLE_ACT_READ_RSSI = 18, + BTC_GAP_BLE_SET_ENCRYPTION_EVT, + BTC_GAP_BLE_SET_SECURITY_PARAM_EVT, + BTC_GAP_BLE_SECURITY_RSP_EVT, + BTC_GAP_BLE_PASSKEY_REPLY_EVT, + BTC_GAP_BLE_CONFIRM_REPLY_EVT, + BTC_GAP_BLE_DISCONNECT_EVT, + BTC_GAP_BLE_REMOVE_BOND_DEV_EVT, + BTC_GAP_BLE_OOB_REQ_REPLY_EVT, + BTC_GAP_BLE_SC_OOB_REQ_REPLY_EVT, + BTC_GAP_BLE_SC_CR_OOB_DATA_EVT, + BTC_GAP_BLE_UPDATE_DUPLICATE_SCAN_EXCEPTIONAL_LIST, + BTC_GAP_BLE_SET_AFH_CHANNELS, +#if (BLE_50_FEATURE_SUPPORT == TRUE) + BTC_GAP_BLE_READ_PHY, + BTC_GAP_BLE_SET_PREFERED_DEF_PHY, + BTC_GAP_BLE_SET_DEF_PHY, + BTC_GAP_BLE_SET_EXT_ADV_RAND_ADDR, + BTC_GAP_BLE_SET_EXT_ADV_PARAMS, + BTC_GAP_BLE_CFG_EXT_ADV_DATA_RAW, + BTC_GAP_BLE_CFG_EXT_SCAN_RSP_DATA_RAW, + BTC_GAP_BLE_EXT_ADV_START, + BTC_GAP_BLE_EXT_ADV_STOP, + BTC_GAP_BLE_EXT_ADV_SET_REMOVE, + BTC_GAP_BLE_EXT_ADV_SET_CLEAR, + BTC_GAP_BLE_SET_PERIODIC_ADV_PARAMS, + BTC_GAP_BLE_CFG_PERIODIC_ADV_DATA_RAW, + BTC_GAP_BLE_PERIODIC_ADV_START, + BTC_GAP_BLE_PERIODIC_ADV_STOP, + BTC_GAP_BLE_PERIODIC_ADV_CREATE_SYNC, + BTC_GAP_BLE_PERIODIC_ADV_SYNC_CANCEL, + BTC_GAP_BLE_PERIODIC_ADV_SYNC_TERMINATE, + BTC_GAP_BLE_PERIODIC_ADV_ADD_DEV_TO_LIST, + BTC_GAP_BLE_PERIODIC_REMOVE_ADD_DEV_FROM_LIST, + BTC_GAP_BLE_PERIODIC_CLEAR_DEV, + BTC_GAP_BLE_SET_EXT_SCAN_PARAMS, + BTC_GAP_BLE_START_EXT_SCAN, + BTC_GAP_BLE_STOP_EXT_SCAN, + BTC_GAP_BLE_SET_EXT_PEFER_CONNET_PARAMS, + BTC_GAP_BLE_DTM_ENH_TX_START, + BTC_GAP_BLE_DTM_ENH_RX_START, +#endif // #if (BLE_50_FEATURE_SUPPORT == TRUE) + BTC_GAP_BLE_ACT_GET_DEV_NAME, +#if (BLE_FEAT_PERIODIC_ADV_SYNC_TRANSFER == TRUE) + BTC_GAP_BLE_PERIODIC_ADV_RECV_ENABLE, + BTC_GAP_BLE_PERIODIC_ADV_SYNC_TRANS, + BTC_GAP_BLE_PERIODIC_ADV_SET_INFO_TRANS, + BTC_GAP_BLE_SET_PERIODIC_ADV_SYNC_TRANS_PARAMS, +#endif //#if (BLE_FEAT_PERIODIC_ADV_SYNC_TRANSFER == TRUE) +#if (BLE_42_FEATURE_SUPPORT == TRUE) + BTC_GAP_BLE_DTM_TX_START, + BTC_GAP_BLE_DTM_RX_START, +#endif // #if (BLE_42_FEATURE_SUPPORT == TRUE) + BTC_GAP_BLE_DTM_STOP, +#if (BLE_42_FEATURE_SUPPORT == TRUE) + BTC_GAP_BLE_ACT_CLEAR_ADV, +#endif // #if (BLE_42_FEATURE_SUPPORT == TRUE) +} btc_gap_ble_act_t; + +/* btc_ble_gap_args_t */ +typedef union { +#if (BLE_42_FEATURE_SUPPORT == TRUE) + //BTC_GAP_BLE_ACT_CFG_ADV_DATA = 0, + struct config_adv_data_args { + esp_ble_adv_data_t adv_data; + } cfg_adv_data; + //BTC_GAP_BLE_ACT_SET_SCAN_PARAM, + struct set_scan_params_args { + esp_ble_scan_params_t scan_params; + } set_scan_param; + //BTC_GAP_BLE_ACT_START_SCAN, + struct start_scan_args { + uint32_t duration; + } start_scan; + //BTC_GAP_BLE_ACT_STOP_SCAN, no args + //BTC_GAP_BLE_ACT_START_ADV, + struct start_adv_args { + esp_ble_adv_params_t adv_params; + } start_adv; + //BTC_GAP_BLE_ACT_STOP_ADV, no args +#endif // #if (BLE_42_FEATURE_SUPPORT == TRUE) + //BTC_GAP_BLE_ACT_UPDATE_CONN_PARAM, + struct conn_update_params_args { + esp_ble_conn_update_params_t conn_params; + } conn_update_params; + //BTC_GAP_BLE_ACT_SET_PKT_DATA_LEN + struct set_pkt_data_len_args { + esp_bd_addr_t remote_device; + uint16_t tx_data_length; + } set_pkt_data_len; + //BTC_GAP_BLE_ACT_SET_RAND_ADDRESS, + struct set_rand_addr_args { + esp_bd_addr_t rand_addr; + } set_rand_addr; + //BTC_GAP_BLE_ACT_CONFIG_LOCAL_PRIVACY, + struct cfg_local_privacy_args { + bool privacy_enable; + } cfg_local_privacy; + //BTC_GAP_BLE_ACT_CONFIG_LOCAL_ICON, + struct cfg_local_icon_args { + uint16_t icon; + } cfg_local_icon; + //BTC_GAP_BLE_ACT_UPDATE_WHITE_LIST + struct update_white_list_args { + bool add_remove; + esp_bd_addr_t remote_bda; + esp_ble_wl_addr_type_t wl_addr_type; + } update_white_list; +#if (BLE_42_FEATURE_SUPPORT == TRUE) + //BTC_GAP_BLE_UPDATE_DUPLICATE_SCAN_EXCEPTIONAL_LIST + struct update_duplicate_exceptional_list_args { + uint8_t subcode; + uint32_t info_type; + esp_duplicate_info_t device_info; + } update_duplicate_exceptional_list; + //BTC_GAP_BLE_ACT_SET_CONN_PARAMS + struct set_conn_params_args { + esp_bd_addr_t bd_addr; + uint16_t min_conn_int; + uint16_t max_conn_int; + uint16_t slave_latency; + uint16_t supervision_tout; + } set_conn_params; +#endif // #if (BLE_42_FEATURE_SUPPORT == TRUE) + //BTC_GAP_BLE_ACT_SET_DEV_NAME, + struct set_dev_name_args { +#define ESP_GAP_DEVICE_NAME_MAX (32) + char device_name[ESP_GAP_DEVICE_NAME_MAX + 1]; + } set_dev_name; +#if (BLE_42_FEATURE_SUPPORT == TRUE) + //BTC_GAP_BLE_ACT_CFG_ADV_DATA_RAW, + struct config_adv_data_raw_args { + uint8_t *raw_adv; + uint32_t raw_adv_len; + } cfg_adv_data_raw; + //BTC_GAP_BLE_ACT_CFG_SCAN_RSP_DATA_RAW, + struct config_scan_rsp_data_raw_args { + uint8_t *raw_scan_rsp; + uint32_t raw_scan_rsp_len; + } cfg_scan_rsp_data_raw; +#endif // #if (BLE_42_FEATURE_SUPPORT == TRUE) + //BTC_GAP_BLE_SET_ENCRYPTION_EVT + struct set_encryption_args { + esp_bd_addr_t bd_addr; + esp_ble_sec_act_t sec_act; + } set_encryption; + //BTC_GAP_BLE_SET_SECURITY_PARAM_EVT + struct set_security_param_args { + esp_ble_sm_param_t param_type; + uint8_t len; + uint8_t *value; + } set_security_param; + //BTC_GAP_BLE_SECURITY_RSP_EVT + struct enc_rsp_args { + esp_bd_addr_t bd_addr; + bool accept; + } sec_rsp; + //BTC_GAP_BLE_PASSKEY_REPLY_EVT + struct enc_passkey_reply_args { + esp_bd_addr_t bd_addr; + bool accept; + uint32_t passkey; + } enc_passkey_replay; + //BTC_GAP_BLE_CONFIRM_REPLY_EVT + struct enc_comfirm_reply_args { + esp_bd_addr_t bd_addr; + bool accept; + } enc_comfirm_replay; + //BTC_GAP_BLE_OOB_DATA_REPLY_EVT + struct oob_req_reply_args { + esp_bd_addr_t bd_addr; + uint8_t len; + uint8_t *p_value; + } oob_req_reply; + struct sc_oob_req_reply_args { + esp_bd_addr_t bd_addr; + uint8_t *p_c; + uint8_t *p_r; + } sc_oob_req_reply; + //BTC_GAP_BLE_DISCONNECT_EVT + struct disconnect_args { + esp_bd_addr_t remote_device; + } disconnect; + //BTC_GAP_BLE_REMOVE_BOND_DEV_EVT + struct remove_bond_device_args { + esp_bd_addr_t bd_addr; + } remove_bond_device; + //BTC_GAP_BLE_ACT_READ_RSSI + struct read_rssi_args { + esp_bd_addr_t remote_addr; + } read_rssi; + // BTC_GAP_BLE_SET_AFH_CHANNELS + struct set_channels_args { + esp_gap_ble_channels channels; + } set_channels; + struct dtm_tx_start_args { + uint8_t tx_channel; + uint8_t len_of_data; + uint8_t pkt_payload; + } dtm_tx_start; + struct dtm_rx_start_args { + uint8_t rx_channel; + } dtm_rx_start; +} btc_ble_gap_args_t; +#if (BLE_50_FEATURE_SUPPORT == TRUE) + +typedef union { + struct read_phy_args { + esp_bd_addr_t bd_addr; + } read_phy; + + struct set_perf_def_phy_args { + esp_ble_gap_phy_mask_t tx_phy_mask; + esp_ble_gap_phy_mask_t rx_phy_mask; + } set_perf_def_phy; + + struct set_def_phy_args { + esp_bd_addr_t bd_addr; + esp_ble_gap_all_phys_t all_phys_mask; + esp_ble_gap_phy_mask_t tx_phy_mask; + esp_ble_gap_phy_mask_t rx_phy_mask; + uint16_t phy_options; + } set_def_phy; + + struct ext_adv_set_rand_addr_args { + uint8_t instance; + esp_bd_addr_t rand_addr; + } ext_adv_set_rand_addr; + + struct ext_adv_set_params_args { + uint8_t instance; + esp_ble_gap_ext_adv_params_t params; + } ext_adv_set_params; + + struct ext_adv_cfg_data_args { + uint8_t instance; + uint16_t length; + uint8_t *data; + } ext_adv_cfg_data; + + struct ext_adv_cfg_scan_rsp_args { + uint8_t instance; + uint16_t length; + uint8_t *data; + } cfg_scan_rsp; + + struct ext_adv_start_args { + uint8_t num_adv; + esp_ble_gap_ext_adv_t *ext_adv; + } ext_adv_start; + + struct ext_adv_stop_args { + uint8_t num_adv; + uint8_t *ext_adv_inst; + } ext_adv_stop; + + struct ext_adv_set_remove_args { + uint8_t instance; + } ext_adv_set_remove; + + struct peridic_adv_set_params_args { + uint8_t instance; + esp_ble_gap_periodic_adv_params_t params; + } peridic_adv_set_params; + + struct periodic_adv_cfg_data_args { + uint8_t instance; + uint16_t len; + uint8_t *data; + bool only_update_did; + } periodic_adv_cfg_data; + + struct periodic_adv_start_args { + bool include_adi; + uint8_t instance; + } periodic_adv_start; + + struct periodic_adv_stop_args { + uint8_t instance; + } periodic_adv_stop; + + struct periodic_adv_create_sync_args { + esp_ble_gap_periodic_adv_sync_params_t params; + } periodic_adv_create_sync; + + struct periodic_adv_sync_term_args { + uint16_t sync_handle; + } periodic_adv_sync_term; + + struct periodic_adv_add_dev_args { + esp_ble_addr_type_t addr_type; + esp_bd_addr_t addr; + uint16_t sid; + } periodic_adv_add_dev; + + struct periodic_adv_remove_dev_args { + esp_ble_addr_type_t addr_type; + esp_bd_addr_t addr; + uint16_t sid; + } periodic_adv_remove_dev; + + struct set_ext_scan_params_args { + esp_ble_ext_scan_params_t params; + } set_ext_scan_params; + + struct start_ext_scan_args { + uint32_t duration; + uint16_t period; + } start_ext_scan; + + struct set_ext_conn_params_args { + esp_bd_addr_t addr; + uint8_t phy_mask; + esp_ble_gap_conn_params_t phy_1m_conn_params; + esp_ble_gap_conn_params_t phy_2m_conn_params; + esp_ble_gap_conn_params_t phy_coded_conn_params; + } set_ext_conn_params; + + struct ext_conn_args { + esp_ble_addr_type_t own_addr_type; + esp_bd_addr_t peer_addr; + } ext_conn; + +#if (BLE_FEAT_PERIODIC_ADV_SYNC_TRANSFER == TRUE) + struct periodic_adv_recv_en_args { + uint16_t sync_handle; + uint8_t enable; + } periodic_adv_recv_en; + + struct periodic_adv_sync_trans_args { + esp_bd_addr_t addr; + uint16_t service_data; + uint16_t sync_handle; + } periodic_adv_sync_trans; + + struct periodic_adv_set_info_trans_args { + esp_bd_addr_t addr; + uint16_t service_data; + uint16_t adv_handle; + } periodic_adv_set_info_trans; + + struct set_periodic_adv_sync_trans_params_args { + esp_bd_addr_t addr; + esp_ble_gap_past_params_t params; + } set_periodic_adv_sync_trans_params; +#endif + struct dtm_enh_tx_start_args { + uint8_t tx_channel; + uint8_t len_of_data; + uint8_t pkt_payload; + uint8_t phy; + } dtm_enh_tx_start; + struct dtm_enh_rx_start_args { + uint8_t rx_channel; + uint8_t phy; + uint8_t modulation_index; + } dtm_enh_rx_start; +} btc_ble_5_gap_args_t; +#endif // #if (BLE_50_FEATURE_SUPPORT == TRUE) + +void btc_gap_ble_call_handler(btc_msg_t *msg); +void btc_gap_ble_cb_handler(btc_msg_t *msg); +void btc_get_whitelist_size(uint16_t *length); +void btc_gap_ble_arg_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src); +void btc_gap_ble_arg_deep_free(btc_msg_t *msg); +void btc_gap_ble_cb_deep_free(btc_msg_t *msg); +void btc_gap_ble_cb_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src); +void btc_gap_callback_init(void); +bool btc_gap_ble_init(void); +void btc_gap_ble_deinit(void); +void btc_adv_list_init(void); +void btc_adv_list_deinit(void); + +#endif /* __BTC_GAP_BLE_H__ */ diff --git a/lib/bt/host/bluedroid/btc/profile/std/include/btc_gap_bt.h b/lib/bt/host/bluedroid/btc/profile/std/include/btc_gap_bt.h new file mode 100644 index 00000000..2b631da9 --- /dev/null +++ b/lib/bt/host/bluedroid/btc/profile/std/include/btc_gap_bt.h @@ -0,0 +1,179 @@ +/* + * SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __BTC_GAP_BT_H__ +#define __BTC_GAP_BT_H__ + +#include "common/bt_target.h" +#include "common/bt_defs.h" +#include "esp_bt_defs.h" +#include "esp_gap_bt_api.h" +#include "btc/btc_task.h" +#include "bta/utl.h" + +#if (BTC_GAP_BT_INCLUDED == TRUE) +typedef enum { + BTC_GAP_BT_SEARCH_DEVICES_EVT = 0, + BTC_GAP_BT_SEARCH_SERVICES_EVT, + BTC_GAP_BT_SEARCH_SERVICE_RECORD_EVT, + BTC_GAP_BT_AUTH_CMPL_EVT, + BTC_GAP_BT_ENC_CHG_EVT, + BTC_GAP_BT_PIN_REQ_EVT, + BTC_GAP_BT_CFM_REQ_EVT, + BTC_GAP_BT_KEY_NOTIF_EVT, + BTC_GAP_BT_KEY_REQ_EVT, + BTC_GAP_BT_READ_RSSI_DELTA_EVT, + BTC_GAP_BT_CONFIG_EIR_DATA_EVT, + BTC_GAP_BT_SET_AFH_CHANNELS_EVT, + BTC_GAP_BT_READ_REMOTE_NAME_EVT, + BTC_GAP_BT_MODE_CHG_EVT, + BTC_GAP_BT_REMOVE_BOND_DEV_COMPLETE_EVT, + BTC_GAP_BT_QOS_EVT, + BTC_GAP_BT_SET_PAGE_TO_EVT, + BTC_GAP_BT_GET_PAGE_TO_EVT, + BTC_GAP_BT_SET_ACL_PKT_TYPES_EVT, +}btc_gap_bt_evt_t; + +typedef enum { + BTC_GAP_BT_ACT_SET_SCAN_MODE = 0, + BTC_GAP_BT_ACT_START_DISCOVERY, + BTC_GAP_BT_ACT_CANCEL_DISCOVERY, + BTC_GAP_BT_ACT_GET_REMOTE_SERVICES, + BTC_GAP_BT_ACT_GET_REMOTE_SERVICE_RECORD, + BTC_GAP_BT_ACT_SET_COD, + BTC_GAP_BT_ACT_READ_RSSI_DELTA, + BTC_GAP_BT_ACT_REMOVE_BOND_DEVICE, + BTC_GAP_BT_ACT_SET_PIN_TYPE, + BTC_GAP_BT_ACT_PIN_REPLY, + BTC_GAP_BT_ACT_SET_SECURITY_PARAM, + BTC_GAP_BT_ACT_PASSKEY_REPLY, + BTC_GAP_BT_ACT_CONFIRM_REPLY, + BTC_GAP_BT_ACT_CONFIG_EIR, + BTC_GAP_BT_ACT_SET_AFH_CHANNELS, + BTC_GAP_BT_ACT_READ_REMOTE_NAME, + BTC_GAP_BT_ACT_SET_QOS, + BTC_GAP_BT_ACT_SET_PAGE_TIMEOUT, + BTC_GAP_BT_ACT_GET_PAGE_TIMEOUT, + BTC_GAP_BT_ACT_SET_ACL_PKT_TYPES, +} btc_gap_bt_act_t; + +/* btc_bt_gap_args_t */ +typedef union { + // BTC_BT_GAP_ACT_SET_SCAN_MODE, + struct set_bt_scan_mode_args { + esp_bt_connection_mode_t c_mode; + esp_bt_discovery_mode_t d_mode; + } set_scan_mode; + + // BTC_GAP_BT_ACT_START_DISCOVERY + struct start_disc_args { + esp_bt_inq_mode_t mode; + uint8_t inq_len; + uint8_t num_rsps; + } start_disc; + + // BTC_GAP_BT_ACT_GET_REMOTE_SERVICES + bt_bdaddr_t bda; + + // BTC_GAP_BT_ACT_GET_REMOTE_SERVICE_RECORD + struct get_rmt_srv_rcd_args { + bt_bdaddr_t bda; + esp_bt_uuid_t uuid; + } get_rmt_srv_rcd; + + // BTC_GAP_BT_ACT_SET_COD + struct set_cod_args { + esp_bt_cod_t cod; + esp_bt_cod_mode_t mode; + } set_cod; + + //BTC_GAP_BT_ACT_READ_RSSI_DELTA, + struct bt_read_rssi_delta_args { + bt_bdaddr_t bda; + } read_rssi_delta; + + // BTC_GAP_BT_ACT_REMOVE_BOND_DEVICE + struct rm_bond_device_args { + bt_bdaddr_t bda; + } rm_bond_device; + + // BTC_GAP_BT_ACT_SET_PIN_TYPE + struct set_pin_type_args { + esp_bt_pin_type_t pin_type; + uint8_t pin_code_len; + esp_bt_pin_code_t pin_code; + } set_pin_type; + + // BTC_GAP_BT_ACT_PIN_REPLY + struct pin_reply_args { + bt_bdaddr_t bda; + bool accept; + uint8_t pin_code_len; + esp_bt_pin_code_t pin_code; + } pin_reply; + + // BTC_GAP_BT_ACT_SET_SECURITY_PARAM + struct set_sec_param_args { + esp_bt_sp_param_t param_type; + uint8_t len; + uint8_t *value; + } set_security_param; + + // BTC_GAP_BT_ACT_PASSKEY_REPLY + struct passkey_reply_args { + bt_bdaddr_t bda; + bool accept; + uint32_t passkey; + } passkey_reply; + + // BTC_GAP_BT_ACT_CONFIRM_REPLY + struct confirm_reply_args { + bt_bdaddr_t bda; + bool accept; + } confirm_reply; + + // BTC_GAP_BT_ACT_CONFIG_EIR + struct config_eir_args { + esp_bt_eir_data_t eir_data; + } config_eir; + + // BTC_GAP_BT_ACT_SET_AFH_CHANNELS + struct set_afh_channels_args { + esp_bt_gap_afh_channels channels; + } set_afh_channels; + + // BTC_GAP_BT_ACT_READ_REMOTE_NAME + bt_bdaddr_t rmt_name_bda; + + // BTC_GAP_BT_ACT_SET_QOS + struct set_qos_args { + bt_bdaddr_t bda; + uint32_t t_poll; + } set_qos; + + // BTC_GAP_BT_ACT_SET_PAGE_TIMEOUT + struct set_page_to_args { + uint16_t page_to; + } set_page_to; + + // BTC_GAP_BT_ACT_SET_ACL_PKT_TYPES + struct set_acl_pkt_types_args { + bt_bdaddr_t bda; + uint16_t pkt_types; + } set_acl_pkt_types; + +} btc_gap_bt_args_t; + +void btc_gap_bt_call_handler(btc_msg_t *msg); +void btc_gap_bt_cb_handler(btc_msg_t *msg); +void btc_gap_bt_arg_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src); +void btc_gap_bt_arg_deep_free(btc_msg_t *msg); +void btc_gap_bt_busy_level_updated(uint8_t bl_flags); + +esp_err_t btc_gap_bt_get_cod(esp_bt_cod_t *cod); +#endif /* #if BTC_GAP_BT_INCLUDED */ + +#endif /* __BTC_GAP_BT_H__ */ diff --git a/lib/bt/host/bluedroid/btc/profile/std/include/btc_gatt_common.h b/lib/bt/host/bluedroid/btc/profile/std/include/btc_gatt_common.h new file mode 100644 index 00000000..75cf3405 --- /dev/null +++ b/lib/bt/host/bluedroid/btc/profile/std/include/btc_gatt_common.h @@ -0,0 +1,29 @@ +/* + * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __BTC_GATT_COMMON_H__ +#define __BTC_GATT_COMMON_H__ + +#include "osi/future.h" +#include "stack/bt_types.h" +#include "bta/bta_api.h" +#include "btc/btc_main.h" +#include "btc/btc_task.h" + +typedef enum { + BTC_GATT_ACT_SET_LOCAL_MTU = 0, +} btc_gatt_com_act_t; + +/* btc_ble_gattc_args_t */ +typedef union { + //BTC_GATT_ACT_SET_LOCAL_MTU, + struct set_mtu_arg { + uint16_t mtu; + } set_mtu; +} btc_ble_gatt_com_args_t; + +void btc_gatt_com_call_handler(btc_msg_t *msg); +#endif /* __BTC_GATT_COMMON_H__ */ diff --git a/lib/bt/host/bluedroid/btc/profile/std/include/btc_gatt_util.h b/lib/bt/host/bluedroid/btc/profile/std/include/btc_gatt_util.h new file mode 100644 index 00000000..3eb38abd --- /dev/null +++ b/lib/bt/host/bluedroid/btc/profile/std/include/btc_gatt_util.h @@ -0,0 +1,32 @@ +/* + * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __BTC_GATT_UTIL_H__ +#define __BTC_GATT_UTIL_H__ + +#include "stack/bt_types.h" +#include "bta/bta_gatt_api.h" +#include "esp_bt_defs.h" +#include "esp_gatt_defs.h" +#include "esp_gattc_api.h" + +#define BTC_GATT_CREATE_CONN_ID(gatt_if, conn_id) ((uint16_t) ((((uint8_t)(conn_id)) << 8) | ((uint8_t)(gatt_if)))) +#define BTC_GATT_GET_CONN_ID(conn_id) (((uint16_t)(conn_id)) >> 8) +#define BTC_GATT_GET_GATT_IF(conn_id) ((uint8_t)(conn_id)) + +void btc128_to_bta_uuid(tBT_UUID *p_dest, uint8_t *p_src); +void btc_to_bta_uuid(tBT_UUID *p_dest, esp_bt_uuid_t *p_src); +void btc_to_bta_gatt_id(tBTA_GATT_ID *p_dest, esp_gatt_id_t *p_src); +void btc_to_bta_srvc_id(tBTA_GATT_SRVC_ID *p_dest, esp_gatt_srvc_id_t *p_src); +void btc_to_bta_response(tBTA_GATTS_RSP *rsp_struct, esp_gatt_rsp_t *p_rsp); + +void bta_to_btc_uuid(esp_bt_uuid_t *p_dest, tBT_UUID *p_src); +void bta_to_btc_gatt_id(esp_gatt_id_t *p_dest, tBTA_GATT_ID *p_src); +void bta_to_btc_srvc_id(esp_gatt_srvc_id_t *p_dest, tBTA_GATT_SRVC_ID *p_src); + +uint16_t set_read_value(uint8_t *gattc_if, esp_ble_gattc_cb_param_t *p_dest, tBTA_GATTC_READ *p_src); + +#endif /* __BTC_GATT_UTIL_H__*/ diff --git a/lib/bt/host/bluedroid/btc/profile/std/include/btc_gattc.h b/lib/bt/host/bluedroid/btc/profile/std/include/btc_gattc.h new file mode 100644 index 00000000..eeebb427 --- /dev/null +++ b/lib/bt/host/bluedroid/btc/profile/std/include/btc_gattc.h @@ -0,0 +1,252 @@ +/* + * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __BTC_GATTC_H__ +#define __BTC_GATTC_H__ + +#include "btc/btc_task.h" +#include "esp_bt_defs.h" +#include "esp_gatt_defs.h" +#include "esp_gattc_api.h" + +typedef enum { + BTC_GATTC_ACT_APP_REGISTER = 0, + BTC_GATTC_ACT_APP_UNREGISTER, + BTC_GATTC_ACT_OPEN, +#if (BLE_50_FEATURE_SUPPORT == TRUE) + BTC_GATTC_ACT_AUX_OPEN, +#endif // #if (BLE_50_FEATURE_SUPPORT == TRUE) + BTC_GATTC_ACT_CLOSE, + BTC_GATTC_ACT_CFG_MTU, + BTC_GATTC_ACT_SEARCH_SERVICE, + BTC_GATTC_ACT_READ_CHAR, + BTC_GATTC_ACT_READ_MULTIPLE_CHAR, + BTC_GATTC_ACT_READ_MULTIPLE_VARIABLE_CHAR, + BTC_GATTC_ACT_READ_CHAR_DESCR, + BTC_GATTC_ACT_READ_BY_TYPE, + BTC_GATTC_ACT_WRITE_CHAR, + BTC_GATTC_ACT_WRITE_CHAR_DESCR, + BTC_GATTC_ACT_PREPARE_WRITE, + BTC_GATTC_ACT_PREPARE_WRITE_CHAR_DESCR, + BTC_GATTC_ACT_EXECUTE_WRITE, + BTC_GATTC_ACT_REG_FOR_NOTIFY, + BTC_GATTC_ACT_UNREG_FOR_NOTIFY, + BTC_GATTC_ACT_CACHE_REFRESH, + BTC_GATTC_ACT_CACHE_ASSOC, + BTC_GATTC_ATC_CACHE_GET_ADDR_LIST, + BTC_GATTC_ACT_CACHE_CLEAN, +} btc_gattc_act_t; + +/* btc_ble_gattc_args_t */ +typedef union { + //BTC_GATTC_ACT_APP_REGISTER, + struct app_reg_arg { + uint16_t app_id; + } app_reg; + //BTC_GATTC_ACT_APP_UNREGISTER, + struct app_unreg_arg { + esp_gatt_if_t gattc_if; + } app_unreg; + //BTC_GATTC_ACT_OPEN, + struct open_arg { + esp_gatt_if_t gattc_if; + esp_bd_addr_t remote_bda; + esp_ble_addr_type_t remote_addr_type; + bool is_direct; + bool is_aux; + } open; + //BTC_GATTC_ACT_CLOSE, + struct close_arg { + uint16_t conn_id; + } close; + //BTC_GATTC_ACT_CFG_MTU, + struct cfg_mtu_arg { + uint16_t conn_id; + } cfg_mtu; + //BTC_GATTC_ACT_SEARCH_SERVICE, + struct search_srvc_arg { + uint16_t conn_id; + bool filter_uuid_enable; + esp_bt_uuid_t filter_uuid; + } search_srvc; + //BTC_GATTC_ACT_GET_CHAR, + struct get_char_arg { + uint16_t conn_id; + uint16_t handle; + } get_char; + //BTC_GATTC_ACT_GET_DESCR, + struct get_descr_arg { + uint16_t conn_id; + uint16_t handle; + } get_descr; + //BTC_GATTC_ACT_GET_FIRST_INCL_SERVICE, + struct get_first_incl_srvc_arg { + uint16_t conn_id; + uint16_t handle; + } get_first_incl_srvc; + //BTC_GATTC_ACT_GET_NEXT_INCL_SERVICE, + struct get_next_incl_srvc_arg { + uint16_t conn_id; + uint16_t handle; + } get_next_incl_srvc; + //BTC_GATTC_ACT_READ_CHAR, + struct read_char_arg { + uint16_t conn_id; + uint16_t handle; + esp_gatt_auth_req_t auth_req; + } read_char; + //BTC_GATTC_ACT_READ_MULTIPLE_CHAR + struct read_multiple_arg { + uint16_t conn_id; + uint8_t num_attr; + uint16_t handles[ESP_GATT_MAX_READ_MULTI_HANDLES]; + esp_gatt_auth_req_t auth_req; + } read_multiple; + //BTC_GATTC_ACT_READ_CHAR_DESCR, + struct read_descr_arg { + uint16_t conn_id; + uint16_t handle; + esp_gatt_auth_req_t auth_req; + } read_descr; + // BTC_GATTC_ACT_READ_BY_TYPE + struct read_by_type_arg { + uint16_t conn_id; + uint16_t s_handle; + uint16_t e_handle; + esp_bt_uuid_t uuid; + esp_gatt_auth_req_t auth_req; + } read_by_type; + //BTC_GATTC_ACT_WRITE_CHAR, + struct write_char_arg { + uint16_t conn_id; + uint16_t value_len; + uint16_t handle; + uint8_t *value; + esp_gatt_write_type_t write_type; + esp_gatt_auth_req_t auth_req; + } write_char; + //BTC_GATTC_ACT_WRITE_CHAR_DESCR, + struct write_descr_arg { + uint16_t conn_id; + uint16_t value_len; + uint16_t handle; + uint8_t *value; + esp_gatt_write_type_t write_type; + esp_gatt_auth_req_t auth_req; + } write_descr; + //BTC_GATTC_ACT_PREPARE_WRITE, + struct prep_write_arg { + uint16_t conn_id; + uint16_t handle; + uint16_t offset; + uint16_t value_len; + uint8_t *value; + esp_gatt_auth_req_t auth_req; + } prep_write; + //BTC_GATTC_ACT_PREPARE_WRITE_CHAR_DESCR, + struct prep_write_descr_arg { + uint16_t conn_id; + uint16_t handle; + uint16_t offset; + uint16_t value_len; + uint8_t *value; + esp_gatt_auth_req_t auth_req; + } prep_write_descr; + //BTC_GATTC_ACT_EXECUTE_WRITE, + struct exec_write_arg { + uint16_t conn_id; + bool is_execute; + } exec_write; + //BTC_GATTC_ACT_REG_FOR_NOTIFY, + struct reg_for_notify_arg { + esp_gatt_if_t gattc_if; + esp_bd_addr_t remote_bda; + uint16_t handle; + } reg_for_notify; + //BTC_GATTC_ACT_UNREG_FOR_NOTIFY + struct unreg_for_notify_arg { + esp_gatt_if_t gattc_if; + esp_bd_addr_t remote_bda; + uint16_t handle; + } unreg_for_notify; + //BTC_GATTC_ACT_CACHE_REFRESH, + struct cache_refresh_arg { + esp_bd_addr_t remote_bda; + } cache_refresh; + //BTC_GATTC_ACT_CACHE_ASSOC + struct cache_assoc_arg { + esp_gatt_if_t gattc_if; + esp_bd_addr_t src_addr; + esp_bd_addr_t assoc_addr; + bool is_assoc; + } cache_assoc; + //BTC_GATTC_ATC_CACHE_GET_ADDR_LIST + struct cache_get_addr_list_arg { + esp_gatt_if_t gattc_if; + }get_addr_list; + //BTC_GATTC_ACT_CACHE_CLEAN, + struct cache_clean_arg { + esp_bd_addr_t remote_bda; + } cache_clean; +} btc_ble_gattc_args_t; + +void btc_gattc_call_handler(btc_msg_t *msg); +void btc_gattc_cb_handler(btc_msg_t *msg); +void btc_gattc_arg_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src); +void btc_gattc_arg_deep_free(btc_msg_t *msg); +esp_gatt_status_t btc_ble_gattc_get_service(uint16_t conn_id, esp_bt_uuid_t *svc_uuid, + esp_gattc_service_elem_t *result, + uint16_t *count, uint16_t offset); +esp_gatt_status_t btc_ble_gattc_get_all_char(uint16_t conn_id, + uint16_t start_handle, + uint16_t end_handle, + esp_gattc_char_elem_t *result, + uint16_t *count, uint16_t offset); +esp_gatt_status_t btc_ble_gattc_get_all_descr(uint16_t conn_id, + uint16_t char_handle, + esp_gattc_descr_elem_t *result, + uint16_t *count, uint16_t offset); +esp_gatt_status_t btc_ble_gattc_get_char_by_uuid(uint16_t conn_id, + uint16_t start_handle, + uint16_t end_handle, + esp_bt_uuid_t char_uuid, + esp_gattc_char_elem_t *result, + uint16_t *count); +esp_gatt_status_t btc_ble_gattc_get_descr_by_uuid(uint16_t conn_id, + uint16_t start_handle, + uint16_t end_handle, + esp_bt_uuid_t char_uuid, + esp_bt_uuid_t descr_uuid, + esp_gattc_descr_elem_t *result, + uint16_t *count); + +esp_gatt_status_t btc_ble_gattc_get_descr_by_char_handle(uint16_t conn_id, + uint16_t char_handle, + esp_bt_uuid_t descr_uuid, + esp_gattc_descr_elem_t *result, + uint16_t *count); + +esp_gatt_status_t btc_ble_gattc_get_include_service(uint16_t conn_id, + uint16_t start_handle, + uint16_t end_handle, + esp_bt_uuid_t *incl_uuid, + esp_gattc_incl_svc_elem_t *result, + uint16_t *count); + +esp_gatt_status_t btc_ble_gattc_get_attr_count(uint16_t conn_id, + esp_gatt_db_attr_type_t type, + uint16_t start_handle, + uint16_t end_handle, + uint16_t char_handle, + uint16_t *count); + +esp_gatt_status_t btc_ble_gattc_get_db(uint16_t conn_id, uint16_t start_handle, uint16_t end_handle, + esp_gattc_db_elem_t *db, uint16_t *count); + + + + +#endif /* __BTC_GATTC_H__ */ diff --git a/lib/bt/host/bluedroid/btc/profile/std/include/btc_gatts.h b/lib/bt/host/bluedroid/btc/profile/std/include/btc_gatts.h new file mode 100644 index 00000000..82ae4353 --- /dev/null +++ b/lib/bt/host/bluedroid/btc/profile/std/include/btc_gatts.h @@ -0,0 +1,171 @@ +/* + * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __BTC_GATTS_H__ +#define __BTC_GATTS_H__ + +#include "btc/btc_task.h" +#include "esp_bt_defs.h" +#include "esp_gatt_defs.h" +#include "esp_gatts_api.h" +#include "osi/future.h" + +typedef enum { + BTC_GATTS_ACT_APP_REGISTER = 0, + BTC_GATTS_ACT_APP_UNREGISTER, + BTC_GATTS_ACT_CREATE_SERVICE, + BTC_GATTS_ACT_CREATE_ATTR_TAB, + BTC_GATTS_ACT_DELETE_SERVICE, + BTC_GATTS_ACT_START_SERVICE, + BTC_GATTS_ACT_STOP_SERVICE, + BTC_GATTS_ACT_ADD_INCLUDE_SERVICE, + BTC_GATTS_ACT_ADD_CHAR, + BTC_GATTS_ACT_ADD_CHAR_DESCR, + BTC_GATTS_ACT_SEND_INDICATE, + BTC_GATTS_ACT_SEND_RESPONSE, + BTC_GATTS_ACT_SET_ATTR_VALUE, + BTC_GATTS_ACT_OPEN, + BTC_GATTS_ACT_CLOSE, + BTC_GATTS_ACT_SEND_SERVICE_CHANGE, + BTC_GATTS_ACT_SHOW_LOCAL_DATABASE, +} btc_gatts_act_t; + +/* btc_ble_gatts_args_t */ +typedef union { + //BTC_GATTS_ACT_APP_REGISTER = 0, + struct app_reg_args { + uint16_t app_id; + } app_reg; + + //BTC_GATTS_ACT_APP_UNREGISTER, + struct app_unreg_args { + esp_gatt_if_t gatts_if; + } app_unreg; + + //BTC_GATTS_ACT_CREATE_SERVICE, + struct create_srvc_args { + esp_gatt_if_t gatts_if; + esp_gatt_srvc_id_t service_id; + uint16_t num_handle; + } create_srvc; + + //BTC_GATTS_ACT_CREATE_ATTR_TAB + struct create_attr_tab_args{ + esp_gatt_if_t gatts_if; + uint8_t srvc_inst_id; + uint16_t max_nb_attr; + esp_gatts_attr_db_t *gatts_attr_db; + }create_attr_tab; + + //BTC_GATTS_ACT_DELETE_SERVICE, + struct delete_srvc_args { + uint16_t service_handle; + } delete_srvc; + + //BTC_GATTS_ACT_START_SERVICE, + struct start_srvc_args { + uint16_t service_handle; + } start_srvc; + + //BTC_GATTS_ACT_STOP_SERVICE, + struct stop_srvc_args { + uint16_t service_handle; + } stop_srvc; + + //BTC_GATTS_ACT_ADD_INCLUDE_SERVICE, + struct add_incl_srvc_args { + uint16_t service_handle; + uint16_t included_service_handle; + } add_incl_srvc; + + //BTC_GATTS_ACT_ADD_CHAR, + struct add_char_args { + uint16_t service_handle; + esp_bt_uuid_t char_uuid; + esp_gatt_perm_t perm; + esp_gatt_char_prop_t property; + esp_attr_control_t attr_control; + esp_attr_value_t char_val; + } add_char; + + //BTC_GATTS_ACT_ADD_CHAR_DESCR, + struct add_descr_args { + uint16_t service_handle; + esp_bt_uuid_t descr_uuid; + esp_gatt_perm_t perm; + esp_attr_control_t attr_control; + esp_attr_value_t descr_val; + } add_descr; + + //BTC_GATTS_ACT_SEND_INDICATE, + struct send_indicate_args { + uint16_t conn_id; + uint16_t attr_handle; + bool need_confirm; + uint16_t value_len; + uint8_t *value; + } send_ind; + + //BTC_GATTS_ACT_SEND_RESPONSE, + struct send_rsp_args { + uint16_t conn_id; + uint32_t trans_id; + esp_gatt_status_t status; + esp_gatt_rsp_t *rsp; + } send_rsp; + + //BTC_GATTS_SET_ATTR_VALUE + struct set_attr_val_args { + uint16_t handle; + uint16_t length; + uint8_t *value; + } set_attr_val; + + //BTC_GATTS_ACT_OPEN, + struct open_args { + esp_gatt_if_t gatts_if; + esp_bd_addr_t remote_bda; + bool is_direct; + } open; + + //BTC_GATTS_ACT_CLOSE, + struct close_args { + uint16_t conn_id; + } close; + + //BTC_GATTS_ACT_SEND_SERVICE_CHANGE, + struct send_service_change_args { + esp_gatt_if_t gatts_if; + esp_bd_addr_t remote_bda; + } send_service_change; + +} btc_ble_gatts_args_t; + +typedef struct { + future_t *complete_future; + uint16_t svc_start_hdl; + esp_bt_uuid_t svc_uuid; + bool is_tab_creat_svc; + bool is_use_svc; + uint16_t num_handle; + uint16_t handle_idx; + uint16_t handles[ESP_GATT_ATTR_HANDLE_MAX]; +} esp_btc_creat_tab_t; + +#if GATT_DYNAMIC_MEMORY == TRUE +extern esp_btc_creat_tab_t *btc_creat_tab_env_ptr; +#define btc_creat_tab_env (*btc_creat_tab_env_ptr) +#endif + +void btc_gatts_call_handler(btc_msg_t *msg); +void btc_gatts_cb_handler(btc_msg_t *msg); +void btc_gatts_arg_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src); +void btc_gatts_arg_deep_free(btc_msg_t *msg); +esp_gatt_status_t btc_gatts_get_attr_value(uint16_t attr_handle, uint16_t *length, uint8_t **value); +esp_gatt_status_t btc_gatts_show_local_database(void); + + +#endif /* __BTC_GATTS_H__ */ diff --git a/lib/bt/host/bluedroid/btc/profile/std/include/btc_hd.h b/lib/bt/host/bluedroid/btc/profile/std/include/btc_hd.h new file mode 100644 index 00000000..313930a4 --- /dev/null +++ b/lib/bt/host/bluedroid/btc/profile/std/include/btc_hd.h @@ -0,0 +1,112 @@ +/****************************************************************************** + * + * Copyright (C) 2016 The Android Open Source Project + * Copyright (C) 2009-2012 Broadcom Corporation + * Copyright (C) 2019 Blake Felt + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ +#ifndef BTC_HD_H +#define BTC_HD_H + +#if BTC_HD_INCLUDED == TRUE + +#include +#include "bta/bta_hd_api.h" +#include "btc/btc_task.h" +#include "esp_hidd_api.h" + +typedef enum { + BTC_HD_INIT_EVT = 0, + BTC_HD_DEINIT_EVT, + BTC_HD_REGISTER_APP_EVT, + BTC_HD_UNREGISTER_APP_EVT, + BTC_HD_CONNECT_EVT, + BTC_HD_DISCONNECT_EVT, + BTC_HD_SEND_REPORT_EVT, + BTC_HD_REPORT_ERROR_EVT, + BTC_HD_UNPLUG_EVT, +} BTC_HD_EVT; + +typedef enum { + BTC_HD_DISABLED = 0, + BTC_HD_ENABLED, + BTC_HD_DISABLING, + BTC_HD_CONNECTING, + BTC_HD_CONNECTED, + BTC_HD_DISCONNECTING, + BTC_HD_DISCONNECTED, +} BTC_HD_STATUS; + +/* BTIF-HD control block */ +typedef struct { + BTC_HD_STATUS status; + bool app_registered; + bool service_dereg_active; + bool forced_disc; + tBTA_HD_APP_INFO app_info; + tBTA_HD_QOS_INFO in_qos; + tBTA_HD_QOS_INFO out_qos; +} btc_hd_cb_t; + +/* btc_hidd_args_t */ +typedef union { + // BTC_HD_CONNECT_EVT + struct hd_connect_arg { + BD_ADDR bd_addr; + } connect; + + // BTC_HD_REGISTER_APP_EVT + struct register_app_arg { + esp_hidd_app_param_t *app_param; + esp_hidd_qos_param_t *in_qos; + esp_hidd_qos_param_t *out_qos; + } register_app; + + // BTC_HD_SEND_REPORT_EVT + struct send_report_arg { + esp_hidd_report_type_t type; + uint8_t id; + uint16_t len; + uint8_t *data; + } send_report; + + // BTC_HD_REPORT_ERROR_EVT + uint8_t error; +} btc_hidd_args_t; + +#ifdef __cplusplus +extern "C" { +#endif + +/****************************************************************************** + * Functions + ******************************************************************************/ +void btc_hd_call_handler(btc_msg_t *msg); + +void btc_hd_cb_handler(btc_msg_t *msg); + +// extern btc_hd_cb_t btc_hd_cb; +// extern void btc_hd_remove_device(bt_bdaddr_t bd_addr); +// extern void btc_hd_service_registration(); + +void btc_hd_arg_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src); +void btc_hd_cb_arg_deep_free(btc_msg_t *msg); + +#ifdef __cplusplus +} +#endif + +#endif /* BTC_HD_INCLUDED == TRUE */ +#endif /* BTC_HD_H */ diff --git a/lib/bt/host/bluedroid/btc/profile/std/include/btc_hf_ag.h b/lib/bt/host/bluedroid/btc/profile/std/include/btc_hf_ag.h new file mode 100644 index 00000000..f936d181 --- /dev/null +++ b/lib/bt/host/bluedroid/btc/profile/std/include/btc_hf_ag.h @@ -0,0 +1,255 @@ +/* + * SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/******************************************************************************* + * + * Filename: btc_hf_ag.h + * + * Description: Main API header file for all BTC HF AG functions accessed + * from internal stack. + * + *******************************************************************************/ + +#ifndef __BTC_HF_AG_H__ +#define __BTC_HF_AG_H__ + +#include "common/bt_target.h" +#include "btc/btc_task.h" +#include "btc/btc_common.h" +#include "bta/bta_ag_api.h" +#include "esp_hf_ag_api.h" + + +#if (BTC_HF_INCLUDED == TRUE) + +/******************************************************************************* +** Type Defs +********************************************************************************/ +/* btc_hf_act_t */ +typedef enum +{ + //INIT + BTC_HF_INIT_EVT, + BTC_HF_DEINIT_EVT, + BTC_HF_CONNECT_EVT, + BTC_HF_DISCONNECT_EVT, + BTC_HF_CONNECT_AUDIO_EVT, + BTC_HF_DISCONNECT_AUDIO_EVT, + BTC_HF_VRA_EVT, + BTC_HF_VOLUME_CONTROL_EVT, + //AT_RESPONSE + BTC_HF_UNAT_RESPONSE_EVT, + BTC_HF_CME_ERR_EVT, + BTC_HF_IND_NOTIFICATION_EVT, + BTC_HF_CIEV_REPORT_EVT, + BTC_HF_CIND_RESPONSE_EVT, + BTC_HF_COPS_RESPONSE_EVT, + BTC_HF_CLCC_RESPONSE_EVT, + BTC_HF_CNUM_RESPONSE_EVT, + BTC_HF_INBAND_RING_EVT, + //CALL_HANDLE + BTC_HF_AC_INCALL_EVT, + BTC_HF_RJ_INCALL_EVT, + BTC_HF_OUT_CALL_EVT, + BTC_HF_END_CALL_EVT, + //REG + BTC_HF_REGISTER_DATA_CALLBACK_EVT, + BTC_HF_REQUEST_PKT_STAT_EVT +} btc_hf_act_t; + +/* btc_hf_args_t */ +typedef union +{ + // BTC_HF_CONNECT_EVT + bt_bdaddr_t connect; + + // BTC_HF_DISCONNECT_EVT + bt_bdaddr_t disconnect; + + // BTC_HF_CONNECT_AUDIO_EVT + bt_bdaddr_t connect_audio; + + // BTC_HF_DISCONNECT_AUDIO_EVT + bt_bdaddr_t disconnect_audio; + + //BTC_HF_VRA_EVT + struct vra_param { + bt_bdaddr_t remote_addr; + esp_hf_vr_state_t value; + } vra_rep; + + // BTC_HF_VOLUME_CONTROL_EVT + struct volcon_args { + bt_bdaddr_t remote_addr; + esp_hf_volume_control_target_t target_type; + int volume; + } volcon; + + //BTC_HF_UNAT_RESPONSE_EVT + struct unat_param { + bt_bdaddr_t remote_addr; + char *unat; + } unat_rep; + + //BTC_HF_CME_ERR_EVT + struct at_ok_err_args { + bt_bdaddr_t remote_addr; + esp_hf_at_response_code_t response_code; + esp_hf_cme_err_t error_code; + } ext_at; + + // BTC_HF_IND_NOTIFICATION_EVT + struct indchange_status { + bt_bdaddr_t remote_addr; + esp_hf_call_status_t call_state; + esp_hf_call_setup_status_t call_setup_state; + esp_hf_network_state_t ntk_state; + int signal; + } ind_change; + + //BTC_HF_CIEV_REPORT_EVT + struct ciev_args { + bt_bdaddr_t remote_addr; + tBTA_AG_IND ind; + } ciev_rep; + + //BTC_HF_CIND_RESPONSE_EVT + struct cind_args { + bt_bdaddr_t remote_addr; + esp_hf_call_status_t call_state; + esp_hf_call_setup_status_t call_setup_state; + esp_hf_network_state_t ntk_state; + int signal; + esp_hf_roaming_status_t roam; + int batt_lev; + esp_hf_call_held_status_t call_held_state; + } cind_rep; + + //BTC_HF_COPS_RESPONSE_EVT + struct cops_args { + bt_bdaddr_t remote_addr; + char *name; + } cops_rep; + + // BTC_HF_CLCC_RESPONSE_EVT + struct clcc_args { + bt_bdaddr_t remote_addr; + int index; + esp_hf_current_call_direction_t dir; + esp_hf_current_call_status_t current_call_state; + esp_hf_current_call_mode_t mode; + esp_hf_current_call_mpty_type_t mpty; + char *number; + esp_hf_call_addr_type_t type; + } clcc_rep; + + // BTC_HF_CNUM_RESPONSE_EVT + struct cnum_args { + bt_bdaddr_t remote_addr; + char *number; + int number_type; + esp_hf_subscriber_service_type_t service_type; + } cnum_rep; + + //BTC_HF_NREC_RESPONSE_EVT + bt_bdaddr_t nrec_rep; + + //BTC_HF_VTC_RESPONSE_EVT + struct bts_args { + bt_bdaddr_t remote_addr; + char *code; + } vts_rep; + + //BTC_HF_INBAND_RING_EVT + struct bsir_args { + bt_bdaddr_t remote_addr; + esp_hf_in_band_ring_state_t state; + } bsir; + + // BTC_HF_AC_INCALL_EVT + // BTC_HF_RJ_INCALL_EVT + // BTC_HF_OUT_CALL_EVT + // BTC_HF_END_CALL_EVT + struct phone_args { + bt_bdaddr_t remote_addr; + int num_active; + int num_held; + esp_hf_call_status_t call_state; + esp_hf_call_setup_status_t call_setup_state; + char *number; + esp_hf_call_addr_type_t call_addr_type; + } phone; + + // BTC_HF_REGISTER_DATA_CALLBACK_EVT + struct ag_reg_data_callback { + esp_hf_incoming_data_cb_t recv; + esp_hf_outgoing_data_cb_t send; + } reg_data_cb; + + // BTC_HF_REQUEST_PKT_STAT_EVT + struct ag_req_pkt_stat_sync_handle { + UINT16 sync_conn_handle; + } pkt_sync_hd; + +} btc_hf_args_t; + +/************************************************************************************ +** Local definitions +************************************************************************************/ +/* Number of BTC-HF-AG control blocks */ +#define BTC_HF_NUM_CB 1 + +/* Handsfree AG app ids for service registration */ +/* APP ID definition*/ +#define BTC_HF_ID_1 0 + +/* BTC-AG control block to map bdaddr to BTA handle */ +typedef struct +{ + bool initialized; + UINT16 handle; + bt_bdaddr_t connected_bda; + tBTA_AG_PEER_FEAT peer_feat; + tBTA_AG_CHLD_FEAT chld_feat; + struct timespec call_end_timestamp; + struct timespec connected_timestamp; + esp_hf_connection_state_t connection_state; + esp_hf_vr_state_t vr_state; + int num_active; + int num_held; + esp_hf_call_status_t call_state; + esp_hf_call_setup_status_t call_setup_state; +} btc_hf_cb_t; + +typedef struct +{ + int hf_idx; + UINT32 btc_hf_features; + btc_hf_cb_t btc_hf_cb; + esp_hf_incoming_data_cb_t btc_hf_incoming_data_cb; + esp_hf_outgoing_data_cb_t btc_hf_outgoing_data_cb; +} hf_local_param_t; + +/******************************************************************************* +** BTC HF AG Handle Hub +********************************************************************************/ +void btc_hf_call_handler(btc_msg_t *msg); // act the cmd from esp-application + +void btc_hf_cb_handler(btc_msg_t *msg); //handle the event from bta + +void btc_hf_incoming_data_cb_to_app(const uint8_t *data, uint32_t len); + +uint32_t btc_hf_outgoing_data_cb_to_app(uint8_t *data, uint32_t len); + +void btc_hf_arg_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src); + +void btc_hf_arg_deep_free(btc_msg_t *msg); + +bt_status_t btc_hf_ci_sco_data(void); + +#endif // BTC_HF_INCLUDED == TRUE + +#endif /* __BTC_HF_AG_H__ */ diff --git a/lib/bt/host/bluedroid/btc/profile/std/include/btc_hf_client.h b/lib/bt/host/bluedroid/btc/profile/std/include/btc_hf_client.h new file mode 100644 index 00000000..4f10371b --- /dev/null +++ b/lib/bt/host/bluedroid/btc/profile/std/include/btc_hf_client.h @@ -0,0 +1,170 @@ +/* + * SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + + +/******************************************************************************* + * + * Filename: btc_hf_client.h + * + * Description: Main API header file for all BTC HF client functions accessed + * from internal stack. + * + *******************************************************************************/ + +#ifndef __BTC_HF_CLIENT_H__ +#define __BTC_HF_CLIENT_H__ + +#include "common/bt_target.h" +#include "esp_hf_client_api.h" +#include "btc/btc_task.h" +#include "btc/btc_common.h" +#include "bta/bta_hf_client_api.h" + +#if (BTC_HF_CLIENT_INCLUDED == TRUE) +/******************************************************************************* +** Type definitions for callback functions +********************************************************************************/ +typedef enum { + BTC_HF_CLIENT_INIT_EVT, + BTC_HF_CLIENT_DEINIT_EVT, + BTC_HF_CLIENT_CONNECT_EVT, + BTC_HF_CLIENT_DISCONNECT_EVT, + BTC_HF_CLIENT_CONNECT_AUDIO_EVT, + BTC_HF_CLIENT_DISCONNECT_AUDIO_EVT, + BTC_HF_CLIENT_START_VOICE_RECOGNITION_EVT, + BTC_HF_CLIENT_STOP_VOICE_RECOGNITION_EVT, + BTC_HF_CLIENT_VOLUME_UPDATE_EVT, + BTC_HF_CLIENT_DIAL_EVT, + BTC_HF_CLIENT_DIAL_MEMORY_EVT, + BTC_HF_CLIENT_SEND_CHLD_CMD_EVT, + BTC_HF_CLIENT_SEND_BTRH_CMD_EVT, + BTC_HF_CLIENT_ANSWER_CALL_EVT, + BTC_HF_CLIENT_REJECT_CALL_EVT, + BTC_HF_CLIENT_QUERY_CURRENT_CALLS_EVT, + BTC_HF_CLIENT_QUERY_CURRENT_OPERATOR_NAME_EVT, + BTC_HF_CLIENT_RETRIEVE_SUBSCRIBER_INFO_EVT, + BTC_HF_CLIENT_SEND_DTMF_EVT, + BTC_HF_CLIENT_REQUEST_LAST_VOICE_TAG_NUMBER_EVT, + BTC_HF_CLIENT_REGISTER_DATA_CALLBACK_EVT, + BTC_HF_CLIENT_SEND_NREC_EVT, + BTC_HF_CLIENT_SEND_XAPL_EVT, + BTC_HF_CLIENT_SEND_IPHONEACCEV_EVT, + BTC_HF_CLIENT_REQUEST_PKT_STAT_EVT, +} btc_hf_client_act_t; + +/* btc_hf_client_args_t */ +typedef union { + // BTC_HF_CLIENT_CONNECT_EVT + bt_bdaddr_t connect; + + // BTC_HF_CLIENT_DISCONNECT_EVT + bt_bdaddr_t disconnect; + + // BTC_HF_CLIENT_CONNECT_AUDIO_EVT + bt_bdaddr_t connect_audio; + + // BTC_HF_CLIENT_DISCONNECT_AUDIO_EVT + bt_bdaddr_t disconnect_audio; + + // BTC_HF_CLIENT_VOLUME_UPDATE_EVT, + struct volume_update_args { + esp_hf_volume_control_target_t type; + int volume; + } volume_update; + + // BTC_HF_CLIENT_DIAL_EVT + struct dial_args { + char number[ESP_BT_HF_CLIENT_NUMBER_LEN + 1]; + } dial; + + // BTC_HF_CLIENT_DIAL_MEMORY_EVT + struct dial_memory_args { + int location; + } dial_memory; + + // BTC_HF_CLIENT_SEND_CHLD_CMD_EVT + struct send_chld_cmd_args { + esp_hf_chld_type_t type; + int idx; + } chld; + + // BTC_HF_CLIENT_SEND_BTRH_CMD_EVT + struct send_btrh_cmd_args { + esp_hf_btrh_cmd_t cmd; + } btrh; + + // BTC_HF_CLIENT_SEND_DTMF_EVT + struct send_dtmf { + char code; + } send_dtmf; + + // BTC_HF_CLIENT_REGISTER_DATA_CALLBACK_EVT + struct hf_client_reg_data_callback { + esp_hf_client_incoming_data_cb_t recv; + esp_hf_client_outgoing_data_cb_t send; + } reg_data_cb; + + //BTC_HF_CLIENT_SEND_XAPL_EVT + struct send_xapl_args { + char information[ESP_BT_HF_AT_SEND_XAPL_LEN + 1]; + uint32_t features; + } send_xapl; + + // BTC_HF_CLIENT_SEND_IPHONEACCEV_EVT + struct send_iphoneaccev_args { + uint32_t bat_level; + bool docked; + } send_iphoneaccev; + + // BTC_HF_CLIENT_REQUEST_PKT_STAT_EVT + struct hf_client_req_pkt_stat_sync_handle { + UINT16 sync_conn_handle; + } pkt_sync_hd; + +} btc_hf_client_args_t; + +/************************************************************************************ +** Local type definitions +************************************************************************************/ +/* BTC-HF control block to map bdaddr to BTA handle */ +typedef struct +{ + bool initialized; + UINT16 handle; + bt_bdaddr_t connected_bda; + esp_hf_client_connection_state_t state; + esp_hf_vr_state_t vr_state; + tBTA_HF_CLIENT_PEER_FEAT peer_feat; + tBTA_HF_CLIENT_CHLD_FEAT chld_feat; +} btc_hf_client_cb_t; + +typedef struct +{ + UINT32 btc_hf_client_features; + btc_hf_client_cb_t btc_hf_client_cb; + esp_hf_client_incoming_data_cb_t btc_hf_client_incoming_data_cb; + esp_hf_client_outgoing_data_cb_t btc_hf_client_outgoing_data_cb; +}hf_client_local_param_t; + +#if HFP_DYNAMIC_MEMORY == TRUE +extern hf_client_local_param_t *hf_client_local_param_ptr; +#define hf_client_local_param (*hf_client_local_param_ptr) +#endif + +/******************************************************************************* +** BTC HF AG API +********************************************************************************/ + +void btc_hf_client_call_handler(btc_msg_t *msg); + +void btc_hf_client_cb_handler(btc_msg_t *msg); + +void btc_hf_client_incoming_data_cb_to_app(const uint8_t *data, uint32_t len); + +uint32_t btc_hf_client_outgoing_data_cb_to_app(uint8_t *data, uint32_t len); +#endif ///BTC_HF_CLIENT_INCLUDED == TRUE + +#endif /* __BTC_HF_CLIENT_H__ */ diff --git a/lib/bt/host/bluedroid/btc/profile/std/include/btc_hh.h b/lib/bt/host/bluedroid/btc/profile/std/include/btc_hh.h new file mode 100644 index 00000000..ae6faefc --- /dev/null +++ b/lib/bt/host/bluedroid/btc/profile/std/include/btc_hh.h @@ -0,0 +1,189 @@ +/****************************************************************************** + * + * Copyright (C) 2016 The Android Open Source Project + * Copyright (C) 2009-2012 Broadcom Corporation + * Copyright (C) 2019 Blake Felt + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ +#ifndef BTC_HH_H +#define BTC_HH_H + +#include +#include "bta/bta_hh_api.h" +#include "btc/btc_task.h" +#include "osi/alarm.h" +#include "esp_hidh_api.h" + +#define BTC_HH_MAX_HID 8 +#define BTC_HH_MAX_ADDED_DEV 32 + +#define BTC_HH_MAX_KEYSTATES 3 +#define BTC_HH_KEYSTATE_MASK_NUMLOCK 0x01 +#define BTC_HH_KEYSTATE_MASK_CAPSLOCK 0x02 +#define BTC_HH_KEYSTATE_MASK_SCROLLLOCK 0x04 + +#define BTC_HH_MAX_POLLING_ATTEMPTS 10 +#define BTC_HH_POLLING_SLEEP_DURATION_US 5000 + +/******************************************************************************* + * Type definitions and return values + ******************************************************************************/ +typedef enum { + BTC_HH_INIT_EVT = 0, + BTC_HH_CONNECT_EVT, + BTC_HH_DISCONNECT_EVT, + BTC_HH_UNPLUG_EVT, + BTC_HH_SET_INFO_EVT, + BTC_HH_GET_PROTO_EVT, + BTC_HH_SET_PROTO_EVT, + BTC_HH_GET_IDLE_EVT, + BTC_HH_SET_IDLE_EVT, + BTC_HH_GET_REPORT_EVT, + BTC_HH_SET_REPORT_EVT, + BTC_HH_SEND_DATA_EVT, + BTC_HH_DEINIT_EVT, +} BTC_HH_EVT; + +typedef enum { + BTC_HH_DISABLED = 0, + BTC_HH_ENABLED, + BTC_HH_DISABLING, + BTC_HH_DEV_UNKNOWN, + BTC_HH_DEV_CONNECTING, + BTC_HH_DEV_CONNECTED, + BTC_HH_DEV_DISCONNECTED +} BTC_HH_STATUS; + +typedef struct { + esp_hidh_connection_state_t dev_status; + uint8_t dev_handle; + BD_ADDR bd_addr; + uint16_t attr_mask; + uint8_t sub_class; + uint8_t app_id; + bool ready_for_data; + osi_alarm_t *vup_timer; + bool local_vup; // Indicated locally initiated VUP +} btc_hh_device_t; + +/* Control block to maintain properties of devices */ +typedef struct { + uint8_t dev_handle; + BD_ADDR bd_addr; + uint16_t attr_mask; +} btc_hh_added_device_t; + +/** + * BTC-HH control block to maintain added devices and currently + * connected hid devices + */ +typedef struct { + BTC_HH_STATUS status; + btc_hh_device_t devices[BTC_HH_MAX_HID]; + uint32_t device_num; + BTC_HH_EVT add_event; + btc_hh_added_device_t added_devices[BTC_HH_MAX_ADDED_DEV]; + btc_hh_device_t *p_curr_dev; + bool service_dereg_active; + BD_ADDR pending_conn_address; +} btc_hh_cb_t; + +/* btc_spp_args_t */ +typedef union { + // BTC_HH_CONNECT_EVT + struct hh_connect_arg { + BD_ADDR bd_addr; + } connect; + + // BTC_HH_DISCONNECT_EVT + struct disconnect_arg { + BD_ADDR bd_addr; + } disconnect; + + // BTC_HH_UNPLUG_EVT + struct unplug_arg { + BD_ADDR bd_addr; + } unplug; + + // BTC_HH_SET_INFO_EVT + struct set_info_arg { + BD_ADDR bd_addr; + esp_hidh_hid_info_t *hid_info; + } set_info; + + // BTC_HH_GET_PROTO_EVT + struct get_protocol_arg { + BD_ADDR bd_addr; + } get_protocol; + + // BTC_HH_SET_PROTO_EVT + struct set_protocol_arg { + BD_ADDR bd_addr; + esp_hidh_protocol_mode_t protocol_mode; + } set_protocol; + + // BTC_HH_GET_IDLE_EVT + struct get_idle_arg { + BD_ADDR bd_addr; + } get_idle; + + // BTC_HH_SET_IDLE_EVT + struct set_idle_arg { + BD_ADDR bd_addr; + uint16_t idle_time; + } set_idle; + + // BTC_HH_GET_REPORT_EVT + struct get_report_arg { + BD_ADDR bd_addr; + esp_hidh_report_type_t report_type; + uint8_t report_id; + int buffer_size; + } get_report; + + // BTC_HH_SET_REPORT_EVT + struct set_report_arg { + BD_ADDR bd_addr; + esp_hidh_report_type_t report_type; + size_t len; + uint8_t *report; + } set_report; + + // BTC_HH_SEND_DATA_EVT + struct send_data_arg { + BD_ADDR bd_addr; + size_t len; + uint8_t *data; + } send_data; +} btc_hidh_args_t; +/******************************************************************************* + * Variables + ******************************************************************************/ +extern btc_hh_cb_t btc_hh_cb; +/******************************************************************************* + * Functions + ******************************************************************************/ + +void btc_hh_call_handler(btc_msg_t *msg); + +void btc_hh_cb_handler(btc_msg_t *msg); + +void btc_hh_arg_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src); + +void btc_hh_cb_arg_deep_free(btc_msg_t *msg); + +bool btc_hh_add_added_dev(BD_ADDR bd_addr, uint16_t attr_mask); + +#endif /* BTC_HH_H */ diff --git a/lib/bt/host/bluedroid/btc/profile/std/include/btc_l2cap.h b/lib/bt/host/bluedroid/btc/profile/std/include/btc_l2cap.h new file mode 100644 index 00000000..83708dbb --- /dev/null +++ b/lib/bt/host/bluedroid/btc/profile/std/include/btc_l2cap.h @@ -0,0 +1,72 @@ +/* + * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __BTC_L2CAP_H__ +#define __BTC_L2CAP_H__ + +#include "btc/btc_task.h" +#include "esp_bt_defs.h" +#include "esp_l2cap_bt_api.h" +#include "common/bt_target.h" +#include "bta/bta_jv_api.h" + +#if (defined BTC_L2CAP_INCLUDED && BTC_L2CAP_INCLUDED == TRUE) + +#define BTC_L2CAP_INVALID_PSM 0x00 + +typedef enum { + BTC_L2CAP_ACT_INIT = 0, + BTC_L2CAP_ACT_UNINIT, + BTC_L2CAP_ACT_START_DISCOVERY, + BTC_L2CAP_ACT_CONNECT, + BTC_L2CAP_ACT_START_SRV, + BTC_L2CAP_ACT_STOP_SRV, +} btc_l2cap_act_t; + +/* btc_l2cap_args_t */ +typedef union { + //BTC_L2CAP_ACT_INIT + struct l2cap_init_arg { + } init; + + //BTC_L2CAP_ACT_UNINIT + struct l2cap_uninit_arg { + } uninit; + + //BTC_L2CAP_ACT_START_DISCOVERY + struct l2cap_start_discovery_arg { + BD_ADDR bd_addr; + UINT16 num_uuid; + tSDP_UUID *p_uuid_list; + } start_discovery; + + //BTC_L2CAP_ACT_CONNECT + struct l2cap_connect_arg { + UINT16 sec_mask; + UINT16 remote_psm; + esp_bd_addr_t peer_bd_addr; + } connect; + + //BTC_L2CAP_ACT_START_SRV + struct l2cap_start_srv_arg { + UINT16 sec_mask; + UINT16 local_psm; + } start_srv; + + //BTC_L2CAP_ACT_STOP_SRV + struct l2cap_stop_srv_arg { + UINT16 psm; + } stop_srv; + +} btc_l2cap_args_t; + +void btc_l2cap_call_handler(btc_msg_t *msg); +void btc_l2cap_cb_handler(btc_msg_t *msg); +esp_err_t btc_l2cap_vfs_register(void); +esp_err_t btc_l2cap_vfs_unregister(void); + +#endif ///defined BTC_L2CAP_INCLUDED && BTC_L2CAP_INCLUDED == TRUE +#endif ///__BTC_L2CAP_H__ diff --git a/lib/bt/host/bluedroid/btc/profile/std/include/btc_sdp.h b/lib/bt/host/bluedroid/btc/profile/std/include/btc_sdp.h new file mode 100644 index 00000000..60772fa3 --- /dev/null +++ b/lib/bt/host/bluedroid/btc/profile/std/include/btc_sdp.h @@ -0,0 +1,53 @@ +/* + * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __BTC_SDP_H__ +#define __BTC_SDP_H__ + +#include "btc/btc_task.h" +#include "esp_bt_defs.h" +#include "common/bt_target.h" +#include "bta/bta_sdp_api.h" +#include "bt_sdp.h" + +#if (defined BTC_SDP_INCLUDED && BTC_SDP_INCLUDED == TRUE) + +typedef enum { + BTC_SDP_ACT_INIT = 0, + BTC_SDP_ACT_DEINIT, + BTC_SDP_ACT_SEARCH, + BTC_SDP_ACT_CREATE_RECORD, + BTC_SDP_ACT_REMOVE_RECORD, +} btc_sdp_act_t; + +/* btc_sdp_args_t */ +typedef union { + //BTC_SDP_ACT_SEARCH + struct search_record_arg { + BD_ADDR bd_addr; + tSDP_UUID sdp_uuid; + } search; + + //BTC_SDP_ACT_CREATE_RECORD + struct creat_record_arg { + bluetooth_sdp_record *record; + } creat_record; + + //BTC_SDP_ACT_REMOVE_RECORD + struct remove_record_arg { + int record_handle; + } remove_record; + +} btc_sdp_args_t; + +void btc_sdp_arg_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src); +void btc_sdp_arg_deep_free(btc_msg_t *msg); + +void btc_sdp_call_handler(btc_msg_t *msg); +void btc_sdp_cb_handler(btc_msg_t *msg); + +#endif ///defined BTC_SDP_INCLUDED && BTC_SDP_INCLUDED == TRUE +#endif ///__BTC_SDP_H__ diff --git a/lib/bt/host/bluedroid/btc/profile/std/include/btc_spp.h b/lib/bt/host/bluedroid/btc/profile/std/include/btc_spp.h new file mode 100644 index 00000000..b3498132 --- /dev/null +++ b/lib/bt/host/bluedroid/btc/profile/std/include/btc_spp.h @@ -0,0 +1,94 @@ +/* + * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __BTC_SPP_H__ +#define __BTC_SPP_H__ + +#include "btc/btc_task.h" +#include "esp_bt_defs.h" +#include "esp_spp_api.h" +#include "common/bt_target.h" +#include "bta/bta_jv_api.h" + +#if (defined BTC_SPP_INCLUDED && BTC_SPP_INCLUDED == TRUE) + +#define ESP_SPP_MAX_SESSION BTA_JV_MAX_RFC_SR_SESSION +#define ESP_SPP_SERVER_NAME_MAX 32 + +#define BTC_SPP_INVALID_SCN 0x00 + +typedef enum { + BTC_SPP_ACT_INIT = 0, + BTC_SPP_ACT_UNINIT, + BTC_SPP_ACT_START_DISCOVERY, + BTC_SPP_ACT_CONNECT, + BTC_SPP_ACT_DISCONNECT, + BTC_SPP_ACT_START_SRV, + BTC_SPP_ACT_STOP_SRV, + BTC_SPP_ACT_WRITE, + BTC_SPP_ACT_VFS_REGISTER, + BTC_SPP_ACT_VFS_UNREGISTER, +} btc_spp_act_t; + +/* btc_spp_args_t */ +typedef union { + //BTC_SPP_ACT_INIT + struct init_arg { + esp_spp_mode_t mode; + bool enable_l2cap_ertm; + UINT16 tx_buffer_size; + } init; + //BTC_SPP_ACT_UNINIT + struct uninit_arg { + } uninit; + + //BTC_SPP_ACT_START_DISCOVERY + struct start_discovery_arg { + BD_ADDR bd_addr; + UINT16 num_uuid; + tSDP_UUID *p_uuid_list; + } start_discovery; + //BTC_SPP_ACT_CONNECT + struct conn_arg { + esp_spp_sec_t sec_mask; + esp_spp_role_t role; + UINT8 remote_scn; + esp_bd_addr_t peer_bd_addr; + } connect; + //BTC_SPP_ACT_DISCONNECT + struct disconn_arg { + UINT32 handle; + } disconnect; + //BTC_SPP_ACT_START_SRV + struct start_srv_arg { + esp_spp_sec_t sec_mask; + esp_spp_role_t role; + UINT8 local_scn; + UINT8 max_session; + char name[ESP_SPP_SERVER_NAME_MAX + 1]; + } start_srv; + //BTC_SPP_ACT_STOP_SRV + struct stop_srv_arg { + UINT8 scn; + } stop_srv; + //BTC_SPP_ACT_WRITE + struct write_arg { + UINT32 handle; + int len; + UINT8 *p_data; + } write; + +} btc_spp_args_t; + + +void btc_spp_call_handler(btc_msg_t *msg); +void btc_spp_cb_handler(btc_msg_t *msg); +void btc_spp_arg_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src); +void btc_spp_arg_deep_free(btc_msg_t *msg); + +esp_err_t spp_send_data_to_btc(uint32_t handle, int len, uint8_t *p_data, esp_spp_mode_t spp_mode); +#endif ///defined BTC_SPP_INCLUDED && BTC_SPP_INCLUDED == TRUE +#endif ///__BTC_SPP_H__ diff --git a/lib/bt/host/bluedroid/btc/profile/std/include/dis_api.h b/lib/bt/host/bluedroid/btc/profile/std/include/dis_api.h new file mode 100644 index 00000000..7edbd67d --- /dev/null +++ b/lib/bt/host/bluedroid/btc/profile/std/include/dis_api.h @@ -0,0 +1,338 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2013 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/******************************************************************************* +** +** Header file for profile folder. +** +********************************************************************************/ + +#ifndef SRVC_DIS_API_H +#define SRVC_DIS_API_H + +#include "common/bt_target.h" +#include "stack/gatt_api.h" +#include "stack/gattdefs.h" +#include "esp_gatts_api.h" + +#define DIS_SUCCESS GATT_SUCCESS +#define DIS_ILLEGAL_PARAM GATT_ILLEGAL_PARAMETER +#define DIS_NO_RESOURCES GATT_NO_RESOURCES +typedef UINT8 tDIS_STATUS; + + +/***************************************************************************** +** Data structure for DIS +*****************************************************************************/ + +#define DIS_ATTR_SYS_ID_BIT 0x0001 +#define DIS_ATTR_MODEL_NUM_BIT 0x0002 +#define DIS_ATTR_SERIAL_NUM_BIT 0x0004 +#define DIS_ATTR_FW_NUM_BIT 0x0008 +#define DIS_ATTR_HW_NUM_BIT 0x0010 +#define DIS_ATTR_SW_NUM_BIT 0x0020 +#define DIS_ATTR_MANU_NAME_BIT 0x0040 +#define DIS_ATTR_IEEE_DATA_BIT 0x0080 +#define DIS_ATTR_PNP_ID_BIT 0x0100 +typedef UINT16 tDIS_ATTR_MASK; + +#define DIS_ATTR_ALL_MASK 0xffff + +typedef tDIS_ATTR_MASK tDIS_ATTR_BIT ; + +#define DIS_MAX_NUM_INC_SVR 0 +#define DIS_MAX_CHAR_NUM 9 +#define DIS_MAX_ATTR_NUM (DIS_MAX_CHAR_NUM * 2 + DIS_MAX_NUM_INC_SVR + 1) + +#ifndef DIS_ATTR_DB_SIZE +#define DIS_ATTR_DB_SIZE GATT_DB_MEM_SIZE(DIS_MAX_NUM_INC_SVR, DIS_MAX_CHAR_NUM, 0) +#endif + +#define DIS_SYSTEM_ID_SIZE 8 +#define DIS_PNP_ID_SIZE 7 + + +typedef struct { + UINT16 uuid; + UINT16 handle; +} tDIS_DB_ENTRY; + +typedef struct { + UINT16 len; + UINT8 *p_data; +} tDIS_STRING; + +typedef struct { + UINT16 vendor_id; + UINT16 product_id; + UINT16 product_version; + UINT8 vendor_id_src; + +} tDIS_PNP_ID; + +typedef union { + UINT64 system_id; + tDIS_PNP_ID pnp_id; + tDIS_STRING data_str; +} tDIS_ATTR; + +#define DIS_MAX_STRING_DATA 7 + +typedef struct { + UINT16 attr_mask; + UINT64 system_id; + tDIS_PNP_ID pnp_id; + UINT8 *data_string[DIS_MAX_STRING_DATA]; +} tDIS_VALUE; + +//typedef void (tDIS_READ_CBACK)(BD_ADDR addr, tDIS_VALUE *p_dis_value); + +typedef struct { + tDIS_DB_ENTRY dis_attr[DIS_MAX_CHAR_NUM]; + tDIS_VALUE dis_value; + +// tDIS_READ_CBACK *p_read_dis_cback; + + UINT16 service_handle; + UINT16 max_handle; + + bool enabled; + + // UINT8 dis_read_uuid_idx; + // tDIS_ATTR_MASK request_mask; +} tDIS_CB; + +/***************************************************************************** +** Data structure used by Battery Service +*****************************************************************************/ + +#ifndef BA_MAX_INT_NUM +#define BA_MAX_INT_NUM 4 +#endif + +#define BATTERY_LEVEL_SIZE 1 + +typedef struct { + BD_ADDR remote_bda; + BOOLEAN need_rsp; + UINT16 clt_cfg; +} tBA_WRITE_DATA; + +#define BA_READ_CLT_CFG_REQ 1 +#define BA_READ_PRE_FMT_REQ 2 +#define BA_READ_RPT_REF_REQ 3 +#define BA_READ_LEVEL_REQ 4 +#define BA_WRITE_CLT_CFG_REQ 5 + +typedef void (tBA_CBACK)(UINT32 trans_id, UINT16 conn_id, UINT8 app_id, UINT8 event, tBA_WRITE_DATA *p_data); + +#define BA_LEVEL_NOTIFY 0x01 +#define BA_LEVEL_PRE_FMT 0x02 +#define BA_LEVEL_RPT_REF 0x04 +typedef UINT8 tBA_LEVEL_DESCR; + +typedef struct { + BOOLEAN is_pri; + tBA_LEVEL_DESCR ba_level_descr; + tGATT_TRANSPORT transport; + tBA_CBACK *p_cback; + +} tBA_REG_INFO; + +typedef union { + UINT8 ba_level; + UINT16 clt_cfg; + tGATT_CHAR_RPT_REF rpt_ref; + tGATT_CHAR_PRES pres_fmt; +} tBA_RSP_DATA; + +typedef struct { + UINT8 app_id; + UINT16 ba_level_hdl; + UINT16 clt_cfg_hdl; + UINT16 rpt_ref_hdl; + UINT16 pres_fmt_hdl; + + tBA_CBACK *p_cback; + + UINT16 pending_handle; + //UINT8 pending_clcb_idx; + UINT8 pending_evt; +} tBA_INST; + +typedef struct { + tBA_INST battery_inst[BA_MAX_INT_NUM]; + UINT8 inst_id; + bool enabled; +} tBATTERY_CB; +/***************************************************************************** +** External Function Declarations +*****************************************************************************/ +#ifdef __cplusplus +extern "C" +{ +#endif +/***************************************************************************** +** Service Engine API +*****************************************************************************/ +/******************************************************************************* +** +** Function srvc_eng_init +** +** Description Initializa the GATT Service engine, register a GATT application +** as for a central service management. +** +*******************************************************************************/ +//extern tGATT_STATUS srvc_eng_init (void); + + +/***************************************************************************** +** DIS Server Function +*****************************************************************************/ + +extern bool dis_valid_handle_range(UINT16 handle); +/******************************************************************************* +** +** Function DIS_Init +** +** Description Initializa the Device Information Service Server. +** +*******************************************************************************/ +extern void DIS_Init (tBTA_GATTS_IF gatt_if, tDIS_ATTR_MASK dis_attr_mask); +/******************************************************************************* +** +** Function DIS_SrUpdate +** +** Description Update the DIS server attribute values +** +*******************************************************************************/ +extern tDIS_STATUS DIS_SrUpdate(tDIS_ATTR_BIT dis_attr_bit, tDIS_ATTR *p_info); +/******************************************************************************* +** +** Function dis_AddChar +** +** Description add characteristic for dis +** +*******************************************************************************/ +extern void dis_AddChar(UINT16 service_id); +/******************************************************************************* +** dis_s_read_attr_value +** +** Process read DIS attribute request. +*******************************************************************************/ + +extern void dis_s_read_attr_value (tGATTS_DATA *p_data, tGATT_VALUE *p_value, + UINT32 trans_id, UINT16 conn_id); +/***************************************************************************** +** DIS Client Function +*****************************************************************************/ +/******************************************************************************* +** +** Function DIS_ReadDISInfo +** +** Description Read remote device DIS information. +** +** Returns void +** +*******************************************************************************/ +//extern BOOLEAN DIS_ReadDISInfo(BD_ADDR peer_bda, tDIS_READ_CBACK *p_cback, +// tDIS_ATTR_MASK mask); + +/******************************************************************************* +** BATTERY SERVICE API +*******************************************************************************/ +/*************************************************************** +** +** Function bas_register +** +** Description register app for battery service +** +****************************************************************/ +extern void bas_register(void); +/*************************************************************** +** +** Function bas_init +** +** Description register battery service +** +****************************************************************/ +extern void bas_init(tBTA_GATTS_IF gatt_if, UINT16 app_id); + +/*************************************************************** +** +** Function bas_AddChar +** +** Description add characteristic for battery service +** +****************************************************************/ +extern void bas_AddChar(UINT16 service_id, tBT_UUID *char_uuid); +/*************************************************************** +** +** Function bas_AddCharDescr +** +** Description add descriptor for battery service if needed +** +****************************************************************/ +extern void bas_AddCharDescr(UINT16 service_id, UINT16 attr_id); +/*************************************************************** +** +** Function bas_service_cmpl +** +** Description create battery service complete +** +****************************************************************/ +extern void bas_service_cmpl(UINT16 service_id, tBTA_GATT_STATUS status); +/******************************************************************************* +** +** Function Battery_Rsp +** +** Description Respond to a battery service request +** +*******************************************************************************/ +extern void Battery_Rsp (UINT32 trans_id, UINT16 conn_id, UINT8 app_id, + tGATT_STATUS st, UINT8 event, tBA_RSP_DATA *p_rsp); +/******************************************************************************* +** +** Function Battery_Notify +** +** Description Send battery level notification +** +*******************************************************************************/ +extern void Battery_Notify (UINT16 conn_id, UINT8 app_id, BD_ADDR remote_bda, UINT8 battery_level); + +/***************************************************************************** +** Function bas_s_read_attr_value +** +** Description it will be called when client send a read request +******************************************************************************/ +extern void bas_s_read_attr_value(tGATTS_DATA *p_data, UINT32 trans_id, UINT16 conn_id); +/***************************************************************************** +** Function bas_s_write_attr_value +** +** Description it will be called when client send a write request +******************************************************************************/ +extern void bas_s_write_attr_value(tGATTS_DATA *p_data, UINT32 trans_id, + UINT16 conn_id, BD_ADDR bd_addr); + +extern void gatts_server_test(void); +#ifdef __cplusplus + +} +#endif + +#endif diff --git a/lib/bt/host/bluedroid/btc/profile/std/include/srvc_api.h b/lib/bt/host/bluedroid/btc/profile/std/include/srvc_api.h new file mode 100644 index 00000000..e84a86be --- /dev/null +++ b/lib/bt/host/bluedroid/btc/profile/std/include/srvc_api.h @@ -0,0 +1,210 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2013 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +#ifndef SRVC_DIS_API_H +#define SRVC_DIS_API_H + +#include "common/bt_target.h" +#include "stack/gatt_api.h" +#include "stack/gattdefs.h" + +#define DIS_SUCCESS GATT_SUCCESS +#define DIS_ILLEGAL_PARAM GATT_ILLEGAL_PARAMETER +#define DIS_NO_RESOURCES GATT_NO_RESOURCES +typedef UINT8 tDIS_STATUS; + + +/***************************************************************************** +** Data structure for DIS +*****************************************************************************/ + +#define DIS_ATTR_SYS_ID_BIT 0x0001 +#define DIS_ATTR_MODEL_NUM_BIT 0x0002 +#define DIS_ATTR_SERIAL_NUM_BIT 0x0004 +#define DIS_ATTR_FW_NUM_BIT 0x0008 +#define DIS_ATTR_HW_NUM_BIT 0x0010 +#define DIS_ATTR_SW_NUM_BIT 0x0020 +#define DIS_ATTR_MANU_NAME_BIT 0x0040 +#define DIS_ATTR_IEEE_DATA_BIT 0x0080 +#define DIS_ATTR_PNP_ID_BIT 0x0100 +typedef UINT16 tDIS_ATTR_MASK; + +#define DIS_ATTR_ALL_MASK 0xffff + +typedef tDIS_ATTR_MASK tDIS_ATTR_BIT ; + +typedef struct { + UINT16 len; + UINT8 *p_data; +} tDIS_STRING; + +typedef struct { + UINT16 vendor_id; + UINT16 product_id; + UINT16 product_version; + UINT8 vendor_id_src; + +} tDIS_PNP_ID; + +typedef union { + UINT64 system_id; + tDIS_PNP_ID pnp_id; + tDIS_STRING data_str; +} tDIS_ATTR; + +#define DIS_MAX_STRING_DATA 7 + +typedef struct { + UINT16 attr_mask; + UINT64 system_id; + tDIS_PNP_ID pnp_id; + UINT8 *data_string[DIS_MAX_STRING_DATA]; +} tDIS_VALUE; + + +typedef void (tDIS_READ_CBACK)(BD_ADDR addr, tDIS_VALUE *p_dis_value); + +/***************************************************************************** +** Data structure used by Battery Service +*****************************************************************************/ +typedef struct { + BD_ADDR remote_bda; + BOOLEAN need_rsp; + UINT16 clt_cfg; +} tBA_WRITE_DATA; + +#define BA_READ_CLT_CFG_REQ 1 +#define BA_READ_PRE_FMT_REQ 2 +#define BA_READ_RPT_REF_REQ 3 +#define BA_READ_LEVEL_REQ 4 +#define BA_WRITE_CLT_CFG_REQ 5 + +typedef void (tBA_CBACK)(UINT8 app_id, UINT8 event, tBA_WRITE_DATA *p_data); + +#define BA_LEVEL_NOTIFY 0x01 +#define BA_LEVEL_PRE_FMT 0x02 +#define BA_LEVEL_RPT_REF 0x04 +typedef UINT8 tBA_LEVEL_DESCR; + +typedef struct { + BOOLEAN is_pri; + tBA_LEVEL_DESCR ba_level_descr; + tGATT_TRANSPORT transport; + tBA_CBACK *p_cback; + +} tBA_REG_INFO; + +typedef union { + UINT8 ba_level; + UINT16 clt_cfg; + tGATT_CHAR_RPT_REF rpt_ref; + tGATT_CHAR_PRES pres_fmt; +} tBA_RSP_DATA; + +/***************************************************************************** +** External Function Declarations +*****************************************************************************/ +#ifdef __cplusplus +extern "C" +{ +#endif +/***************************************************************************** +** Service Engine API +*****************************************************************************/ +/******************************************************************************* +** +** Function srvc_eng_init +** +** Description Initializa the GATT Service engine, register a GATT application +** as for a central service management. +** +*******************************************************************************/ +extern tGATT_STATUS srvc_eng_init (void); + + +/***************************************************************************** +** DIS Server Function +*****************************************************************************/ + +/******************************************************************************* +** +** Function DIS_SrInit +** +** Description Initializa the Device Information Service Server. +** +*******************************************************************************/ +extern tDIS_STATUS DIS_SrInit (tDIS_ATTR_MASK dis_attr_mask); +/******************************************************************************* +** +** Function DIS_SrUpdate +** +** Description Update the DIS server attribute values +** +*******************************************************************************/ +extern tDIS_STATUS DIS_SrUpdate(tDIS_ATTR_BIT dis_attr_bit, tDIS_ATTR *p_info); +/***************************************************************************** +** DIS Client Function +*****************************************************************************/ +/******************************************************************************* +** +** Function DIS_ReadDISInfo +** +** Description Read remote device DIS information. +** +** Returns void +** +*******************************************************************************/ +extern BOOLEAN DIS_ReadDISInfo(BD_ADDR peer_bda, tDIS_READ_CBACK *p_cback, + tDIS_ATTR_MASK mask); + +/******************************************************************************* +** BATTERY SERVICE API +*******************************************************************************/ +/******************************************************************************* +** +** Function Battery_Instantiate +** +** Description Instantiate a Battery service +** +*******************************************************************************/ +extern UINT16 Battery_Instantiate (UINT8 app_id, tBA_REG_INFO *p_reg_info); + +/******************************************************************************* +** +** Function Battery_Rsp +** +** Description Respond to a battery service request +** +*******************************************************************************/ +extern void Battery_Rsp (UINT8 app_id, tGATT_STATUS st, UINT8 event, tBA_RSP_DATA *p_rsp); +/******************************************************************************* +** +** Function Battery_Notify +** +** Description Send battery level notification +** +*******************************************************************************/ +extern void Battery_Notify (UINT8 app_id, BD_ADDR remote_bda, UINT8 battery_level); + + +#ifdef __cplusplus + +} +#endif + +#endif diff --git a/lib/bt/host/bluedroid/btc/profile/std/l2cap/btc_l2cap.c b/lib/bt/host/bluedroid/btc/profile/std/l2cap/btc_l2cap.c new file mode 100644 index 00000000..5cd777c5 --- /dev/null +++ b/lib/bt/host/bluedroid/btc/profile/std/l2cap/btc_l2cap.c @@ -0,0 +1,1257 @@ +/* + * SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include "btc_l2cap.h" +#include "stack/l2c_api.h" +#include "btc/btc_manage.h" +#include "btc/btc_task.h" +#include "bta/bta_jv_api.h" +#include "common/bt_trace.h" +#include "osi/allocator.h" +#include "esp_l2cap_bt_api.h" +#include "osi/list.h" +#include "freertos/ringbuf.h" +#include "osi/mutex.h" +#include "osi/alarm.h" +#include +#include +#include +#include "esp_vfs.h" +#include "esp_vfs_dev.h" +#include "stack/port_api.h" +#include "freertos/FreeRTOS.h" +#include "freertos/event_groups.h" +#include "btc_sdp.h" + +#include "btc/btc_task.h" +#include "stack/btu.h" + +#if (defined BTC_L2CAP_INCLUDED && BTC_L2CAP_INCLUDED == TRUE) + +#define SLOT_WRITE_BIT(i) (1UL << (i - 1)) +#define SLOT_CLOSE_BIT(i) (1UL << (i + BTA_JV_MAX_L2C_CONN - 1)) +#define VFS_WRITE_TIMEOUT (40 * 1000) +#define SLOT_TX_QUEUE_SIZE 10 +#define SLOT_TX_QUEUE_LOW_WM 4 +#define SLOT_TX_DATA_HIGH_WM (SLOT_TX_QUEUE_SIZE * BTA_JV_DEF_RFC_MTU) +#define VFS_CLOSE_TIMEOUT (20 * 1000) +#define BTC_L2CAP_ROLE_MASTER 0 +#define BTC_L2CAP_ROLE_SLAVE 1 + +typedef struct { + bool peer_fc; /* true if flow control is set based on peer's request */ + bool user_fc; /* true if flow control is set based on user's request */ + fixed_queue_t *queue; /* Queue of buffers waiting to be sent */ + uint32_t data_size; /* Number of data bytes in the queue */ +} slot_data_t; + +typedef struct { + bool connected; + bool is_server; + bool fix_chan; // unused + uint16_t psm; + uint8_t serial; + uint8_t max_session; + uint32_t id; + uint32_t handle; + int fd; + int tx_mtu; + uint8_t *write_data; + osi_alarm_t *close_alarm; + void *alarm_arg; + uint8_t role; + uint16_t security; + esp_bd_addr_t addr; + slot_data_t rx; + slot_data_t tx; + uint8_t service_uuid[16]; +} l2cap_slot_t; + + +typedef struct { + l2cap_slot_t *l2cap_slots[BTA_JV_MAX_L2C_CONN + 1]; + uint32_t l2cap_slot_id; + osi_mutex_t l2cap_slot_mutex; + EventGroupHandle_t tx_event_group; + esp_vfs_id_t l2cap_vfs_id; +} l2cap_local_param_t; + +static l2cap_local_param_t l2cap_local_param; + +/* L2CAP default options for OBEX connections */ +static const tL2CAP_FCR_OPTS obex_l2c_fcr_opts_def = +{ + L2CAP_FCR_ERTM_MODE, /* Mandatory for OBEX over l2cap */ + OBX_FCR_OPT_TX_WINDOW_SIZE_BR_EDR,/* Tx window size */ + OBX_FCR_OPT_MAX_TX_B4_DISCNT, /* Maximum transmissions before disconnecting */ + OBX_FCR_OPT_RETX_TOUT, /* Retransmission timeout (2 secs) */ + OBX_FCR_OPT_MONITOR_TOUT, /* Monitor timeout (12 secs) */ + OBX_FCR_OPT_MAX_PDU_SIZE /* MPS segment size */ +}; +static const tL2CAP_ERTM_INFO obex_l2c_etm_opt = +{ + L2CAP_FCR_ERTM_MODE, /* Mandatory for OBEX over l2cap */ + L2CAP_FCR_CHAN_OPT_ERTM, /* Mandatory for OBEX over l2cap */ + OBX_USER_RX_POOL_ID, + OBX_USER_TX_POOL_ID, + OBX_FCR_RX_POOL_ID, + OBX_FCR_TX_POOL_ID +}; + + +#if L2CAP_DYNAMIC_MEMORY == FALSE +#define is_l2cap_init() (l2cap_local_param.l2cap_slot_mutex != NULL) +#else +#define is_l2cap_init() (&l2cap_local_param != NULL && l2cap_local_param.l2cap_slot_mutex != NULL) +#endif + +static void l2cap_osi_free(void *p) +{ + osi_free(p); +} + +static l2cap_slot_t *l2cap_find_slot_by_handle(uint32_t handle) +{ + l2cap_slot_t *slot = NULL; + + for (size_t i = 1; i <= BTA_JV_MAX_L2C_CONN; i++) { + slot = l2cap_local_param.l2cap_slots[i]; + if (slot != NULL && slot->handle == handle) { + return slot; + } + } + return NULL; +} + +static l2cap_slot_t *l2cap_find_slot_by_id(uint32_t id) +{ + l2cap_slot_t *slot = NULL; + + for (size_t i = 1; i <= BTA_JV_MAX_L2C_CONN; i++) { + slot = l2cap_local_param.l2cap_slots[i]; + if (slot != NULL && slot->id == id) { + return slot; + } + } + return NULL; +} + +static l2cap_slot_t *l2cap_find_slot_by_fd(int fd) +{ + l2cap_slot_t *slot = NULL; + + for (size_t i = 1; i <= BTA_JV_MAX_L2C_CONN; i++) { + slot = l2cap_local_param.l2cap_slots[i]; + if (slot != NULL && slot->fd == fd) { + return slot; + } + } + return NULL; +} + +static int init_slot_data(slot_data_t *slot_data, size_t queue_size) +{ + memset(slot_data, 0, sizeof(slot_data_t)); + if ((slot_data->queue = fixed_queue_new(queue_size)) == NULL) { + return -1; + } + slot_data->data_size = 0; + return 0; +} + +static void free_slot_data(slot_data_t *slot_data) +{ + fixed_queue_free(slot_data->queue, l2cap_osi_free); + slot_data->queue = NULL; +} + +static l2cap_slot_t *l2cap_malloc_slot(void) +{ + uint8_t err_no = 0; + l2cap_slot_t **slot = NULL; + + if (++l2cap_local_param.l2cap_slot_id == 0) { + l2cap_local_param.l2cap_slot_id = 1; + } + for (size_t i = 1; i <= BTA_JV_MAX_L2C_CONN; i++) { + slot = &l2cap_local_param.l2cap_slots[i]; + if ((*slot) == NULL) { + if (((*slot) = (l2cap_slot_t *)osi_malloc(sizeof(l2cap_slot_t))) == NULL) { + return NULL; + } + (*slot)->id = l2cap_local_param.l2cap_slot_id; + (*slot)->psm = 0; + (*slot)->serial = i; + (*slot)->handle = 0xffff; + (*slot)->fd = -1; + (*slot)->connected = false; + (*slot)->is_server = false; + (*slot)->write_data = NULL; + (*slot)->close_alarm = NULL; + (*slot)->alarm_arg = NULL; + /* clear the old event bits */ + if (l2cap_local_param.tx_event_group) { + xEventGroupClearBits(l2cap_local_param.tx_event_group, SLOT_WRITE_BIT(i) | SLOT_CLOSE_BIT(i)); + } + + if (init_slot_data(&(*slot)->rx, QUEUE_SIZE_MAX)) { + BTC_TRACE_ERROR("%s unable to malloc rx queue!", __func__); + err_no = 1; + goto err; + } + if (init_slot_data(&(*slot)->tx, SLOT_TX_QUEUE_SIZE)) { + BTC_TRACE_ERROR("%s unable to malloc tx queue!", __func__); + err_no = 2; + goto err; + } + if (esp_vfs_register_fd(l2cap_local_param.l2cap_vfs_id, &(*slot)->fd) != ESP_OK) { + BTC_TRACE_ERROR("%s unable to register fd!", __func__); + err_no = 3; + goto err; + } + + return (*slot); + } + } + + return NULL; +err: + switch (err_no) { + case 3: + free_slot_data(&(*slot)->tx); + case 2: + free_slot_data(&(*slot)->rx); + case 1: + osi_free((*slot)); + (*slot) = NULL; + break; + default: + break; + } + return (*slot); +} + +static void l2cap_free_slot(l2cap_slot_t *slot) +{ + if (!slot) { + return; + } + l2cap_local_param.l2cap_slots[slot->serial] = NULL; + esp_vfs_unregister_fd(l2cap_local_param.l2cap_vfs_id, slot->fd); + xEventGroupSetBits(l2cap_local_param.tx_event_group, SLOT_CLOSE_BIT(slot->serial)); + free_slot_data(&slot->tx); + free_slot_data(&slot->rx); + if (slot->close_alarm) { + osi_alarm_free(slot->close_alarm); + if (slot->alarm_arg) { + osi_free(slot->alarm_arg); + slot->alarm_arg = NULL; + } + } + osi_free(slot); +} + +static void l2cap_free_pending_slots(void) +{ + l2cap_slot_t *slot = NULL; + for (size_t i = 1; i <= BTA_JV_MAX_L2C_CONN; i++) { + slot = l2cap_local_param.l2cap_slots[i]; + if (slot) { + BTC_TRACE_WARNING("%s found slot(handle=0x%x) pending to close, close it now!", __func__, slot->handle); + l2cap_free_slot(slot); + } + } +} + +static void close_timeout_handler(void *arg) +{ + btc_msg_t msg; + bt_status_t status; + l2cap_slot_t *slot = (l2cap_slot_t *)arg; + + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_L2CAP; + msg.act = BTA_JV_L2CAP_CLOSE_EVT; + + status = btc_transfer_context(&msg, slot->alarm_arg, sizeof(tBTA_JV), NULL, NULL); + + if (slot->alarm_arg) { + free(slot->alarm_arg); + slot->alarm_arg = NULL; + } + + if (status != BT_STATUS_SUCCESS) { + BTC_TRACE_ERROR("%s btc_transfer_context failed", __func__); + } +} + +static inline void btc_l2cap_cb_to_app(esp_bt_l2cap_cb_event_t event, esp_bt_l2cap_cb_param_t *param) +{ + esp_bt_l2cap_cb_t btc_l2cap_cb = (esp_bt_l2cap_cb_t)btc_profile_cb_get(BTC_PID_L2CAP); + if (btc_l2cap_cb) { + btc_l2cap_cb(event, param); + } +} + +static void *btc_l2cap_inter_cb(tBTA_JV_EVT event, tBTA_JV *p_data, void *user_data) +{ + bt_status_t status; + btc_msg_t msg; + uint32_t id = (uintptr_t)user_data; + l2cap_slot_t *slot = NULL; + + switch (event) { + case BTA_JV_L2CAP_OPEN_EVT: + slot = l2cap_find_slot_by_id(id); + if (!slot) { + BTC_TRACE_ERROR("%s unable to find L2CAP slot, event:%d!", __func__, event); + p_data->l2c_open.status = ESP_BT_L2CAP_NO_CONNECTION; + break; + } + slot->connected = TRUE; + slot->handle = p_data->l2c_open.handle; + slot->tx_mtu = p_data->l2c_open.tx_mtu; + BTA_JvSetPmProfile(p_data->l2c_open.handle, BTA_JV_PM_ID_1, BTA_JV_CONN_OPEN); + break; + case BTA_JV_L2CAP_START_EVT: + slot = l2cap_find_slot_by_id(id); + if (!slot) { + BTC_TRACE_ERROR("%s unable to find L2CAP slot, event:%d!", __func__, event); + p_data->l2c_open.status = ESP_BT_L2CAP_NO_CONNECTION; + break; + } + slot->handle = p_data->l2c_start.handle; + break; + case BTA_JV_L2CAP_CLOSE_EVT: + slot = l2cap_find_slot_by_id(id); + if (!slot) { + BTC_TRACE_ERROR("%s unable to find RFCOMM slot, event:%d!", __func__, event); + p_data->l2c_close.status = ESP_BT_L2CAP_NO_CONNECTION; + break; + } + p_data->l2c_close.status = BTA_JV_SUCCESS; + p_data->l2c_close.user_data = (void *)(uintptr_t)slot->id; + break; + case BTA_JV_L2CAP_CL_INIT_EVT: + slot = l2cap_find_slot_by_id(id); + if (!slot) { + BTC_TRACE_ERROR("%s unable to find L2CAP slot, event:%d!", __func__, event); + p_data->l2c_cl_init.status = ESP_BT_L2CAP_FAILURE; + break; + } + + if (p_data->l2c_cl_init.status == BTA_JV_SUCCESS) { + slot->handle = p_data->l2c_cl_init.handle; + } else { + l2cap_free_slot(slot); + } + break; + case BTA_JV_L2CAP_DATA_IND_EVT: + // to do + break; + case BTA_JV_FREE_SCN_EVT: + slot = l2cap_find_slot_by_id(id); + if (slot) { + l2cap_free_slot(slot); + } else { + BTC_TRACE_ERROR("%s unable to find L2CAP slot, event:%d!", __func__, event); + p_data->free_scn.status = ESP_BT_L2CAP_NO_CONNECTION; + } + break; + default: + break; + } + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_L2CAP; + msg.act = event; + + status = btc_transfer_context(&msg, p_data, sizeof(tBTA_JV), NULL, NULL); + if (status != BT_STATUS_SUCCESS) { + BTC_TRACE_ERROR("%s btc_transfer_context failed\n", __func__); + } + + return NULL; +} + +static void btc_l2cap_dm_inter_cb(tBTA_JV_EVT event, tBTA_JV *p_data, void *user_data) +{ + bt_status_t status; + btc_msg_t msg; + + switch (event) { + default: + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_L2CAP; + msg.act = event; + + status = btc_transfer_context(&msg, p_data, sizeof(tBTA_JV), NULL, NULL); + if (status != BT_STATUS_SUCCESS) { + BTC_TRACE_ERROR("%s btc_transfer_context failed\n", __func__); + } + break; + } + + return; +} + +static void btc_l2cap_init(void) +{ + esp_bt_l2cap_status_t ret = ESP_BT_L2CAP_SUCCESS; + esp_bt_l2cap_cb_param_t param; + + do { + if (is_l2cap_init()) { + BTC_TRACE_ERROR("%s L2CAP has been initiated, shall uninit first!", __func__); + ret = ESP_BT_L2CAP_NEED_DEINIT; + break; + } + +#if L2CAP_DYNAMIC_MEMORY == TRUE + if ((l2cap_local_param_ptr = (l2cap_local_param_t *)osi_malloc(sizeof(l2cap_local_param_t))) == NULL) { + BTC_TRACE_ERROR("%s malloc failed\n", __func__); + ret = ESP_BT_L2CAP_NO_RESOURCE; + break; + } + memset((void *)l2cap_local_param_ptr, 0, sizeof(l2cap_local_param_t)); +#endif + l2cap_local_param.l2cap_vfs_id = -1; + + if (osi_mutex_new(&l2cap_local_param.l2cap_slot_mutex) != 0) { +#if L2CAP_DYNAMIC_MEMORY == TRUE + osi_free(l2cap_local_param_ptr); + l2cap_local_param_ptr = NULL; +#endif + BTC_TRACE_ERROR("%s osi_mutex_new failed\n", __func__); + ret = ESP_BT_L2CAP_NO_RESOURCE; + break; + } + if ((l2cap_local_param.tx_event_group = xEventGroupCreate()) == NULL) { + BTC_TRACE_ERROR("%s create tx_event_group failed\n", __func__); + osi_mutex_free(&l2cap_local_param.l2cap_slot_mutex); +#if L2CAP_DYNAMIC_MEMORY == TRUE + osi_free(l2cap_local_param_ptr); + l2cap_local_param_ptr = NULL; +#endif + ret = ESP_BT_L2CAP_NO_RESOURCE; + break; + } + l2cap_local_param.l2cap_slot_id = 0; + ret = BTA_JvEnable((tBTA_JV_DM_CBACK *)btc_l2cap_dm_inter_cb); + if (BTA_JV_ALREADY_DONE == ret) { + ret = ESP_BT_L2CAP_SUCCESS; + param.init.status = ESP_BT_L2CAP_SUCCESS; + btc_l2cap_cb_to_app(ESP_BT_L2CAP_INIT_EVT, ¶m); + } + } while (0); + + if (ret != ESP_BT_L2CAP_SUCCESS) { + param.init.status = ret; + btc_l2cap_cb_to_app(ESP_BT_L2CAP_INIT_EVT, ¶m); + } +} + +static void btc_l2cap_uninit(void) +{ + esp_bt_l2cap_status_t ret = ESP_BT_L2CAP_SUCCESS; + + do { + if (!is_l2cap_init()) { + BTC_TRACE_ERROR("%s L2CAP has not been initiated, shall init first!", __func__); + ret = ESP_BT_L2CAP_NEED_INIT; + break; + } + osi_mutex_lock(&l2cap_local_param.l2cap_slot_mutex, OSI_MUTEX_MAX_TIMEOUT); + // first, remove all connection + for (size_t i = 1; i <= BTA_JV_MAX_L2C_CONN; i++) { + if (l2cap_local_param.l2cap_slots[i] != NULL && !l2cap_local_param.l2cap_slots[i]->is_server) { + BTA_JvL2capClose(l2cap_local_param.l2cap_slots[i]->handle, (tBTA_JV_L2CAP_CBACK *)btc_l2cap_inter_cb, + (void *)l2cap_local_param.l2cap_slots[i]->id); + } + } + // second, remove all server + for (size_t i = 1; i <= BTA_JV_MAX_L2C_CONN; i++) { + if (l2cap_local_param.l2cap_slots[i] != NULL && l2cap_local_param.l2cap_slots[i]->is_server) { + if (l2cap_local_param.l2cap_slots[i]->handle != 0xffff) { + BTA_JvL2capStopServer(l2cap_local_param.l2cap_slots[i]->psm, + (void *)l2cap_local_param.l2cap_slots[i]->id); + } + + BTA_JvFreeChannel(l2cap_local_param.l2cap_slots[i]->psm, BTA_JV_CONN_TYPE_L2CAP, + (tBTA_JV_RFCOMM_CBACK *)btc_l2cap_inter_cb, (void *)l2cap_local_param.l2cap_slots[i]->id); + } + } + BTA_JvDisable((tBTA_JV_L2CAP_CBACK *)btc_l2cap_inter_cb); + osi_mutex_unlock(&l2cap_local_param.l2cap_slot_mutex); + } while(0); + + if (ret != ESP_BT_L2CAP_SUCCESS) { + esp_bt_l2cap_cb_param_t param; + param.uninit.status = ret; + btc_l2cap_cb_to_app(ESP_BT_L2CAP_UNINIT_EVT, ¶m); + } +} + +static void btc_l2cap_start_srv(btc_l2cap_args_t *arg) +{ + esp_bt_l2cap_status_t ret = ESP_BT_L2CAP_SUCCESS; + tL2CAP_CFG_INFO cfg; + + do { + if (!is_l2cap_init()) { + BTC_TRACE_ERROR("%s L2CAP have not been init\n", __func__); + ret = ESP_BT_L2CAP_NEED_INIT; + break; + } + osi_mutex_lock(&l2cap_local_param.l2cap_slot_mutex, OSI_MUTEX_MAX_TIMEOUT); + l2cap_slot_t *slot = l2cap_malloc_slot(); + if (!slot) { + BTC_TRACE_ERROR("%s unable to malloc L2CAP slot!", __func__); + osi_mutex_unlock(&l2cap_local_param.l2cap_slot_mutex); + ret = ESP_BT_L2CAP_NO_RESOURCE; + break; + } + /** + * make this slot become a listening slot + */ + slot->is_server = true; + slot->security = arg->start_srv.sec_mask; + slot->role = BTC_L2CAP_ROLE_SLAVE; + slot->psm = arg->start_srv.local_psm; + + /* Setup ETM settings */ + memset(&cfg, 0, sizeof(tL2CAP_CFG_INFO)); + cfg.fcr_present = TRUE; + cfg.fcr = obex_l2c_fcr_opts_def; + BTA_JvL2capStartServer(slot->security, slot->role, &obex_l2c_etm_opt, slot->psm, + L2CAP_MAX_SDU_LENGTH, &cfg, (tBTA_JV_L2CAP_CBACK *)btc_l2cap_inter_cb, (void *)slot->id); + osi_mutex_unlock(&l2cap_local_param.l2cap_slot_mutex); + } while(0); + + if (ret != ESP_BT_L2CAP_SUCCESS) { + esp_bt_l2cap_cb_param_t param; + param.start.status = ret; + param.start.handle = 0xffff; + param.start.sec_id = 0; + btc_l2cap_cb_to_app(ESP_BT_L2CAP_START_EVT, ¶m); + } + return; +} + +static void btc_l2cap_stop_srv(btc_l2cap_args_t *arg) +{ + esp_bt_l2cap_status_t ret = ESP_BT_L2CAP_SUCCESS; + bool is_remove_all = false; + uint8_t i, j, srv_cnt = 0; + uint8_t *srv_psm_arr = osi_malloc(BTA_JV_MAX_L2C_CONN); + + if (arg->stop_srv.psm == BTC_L2CAP_INVALID_PSM) { + is_remove_all = true; + } + + do { + if (!is_l2cap_init()) { + BTC_TRACE_ERROR("%s L2CAP have not been init\n", __func__); + ret = ESP_BT_L2CAP_NEED_INIT; + break; + } + if (srv_psm_arr == NULL) { + BTC_TRACE_ERROR("%s malloc srv_psm_arr failed\n", __func__); + ret = ESP_BT_L2CAP_NO_RESOURCE; + break; + } + + osi_mutex_lock(&l2cap_local_param.l2cap_slot_mutex, OSI_MUTEX_MAX_TIMEOUT); + // [1] find all server + for (i = 1; i <= BTA_JV_MAX_L2C_CONN; i++) { + if (l2cap_local_param.l2cap_slots[i] != NULL && l2cap_local_param.l2cap_slots[i]->is_server && + l2cap_local_param.l2cap_slots[i]->handle != 0xffff) { + if (is_remove_all) { + srv_psm_arr[srv_cnt++] = l2cap_local_param.l2cap_slots[i]->psm; + } else if (l2cap_local_param.l2cap_slots[i]->psm == arg->stop_srv.psm) { + srv_psm_arr[srv_cnt++] = l2cap_local_param.l2cap_slots[i]->psm; + break; + } + } + } + if (srv_cnt == 0) { + if (is_remove_all) { + BTC_TRACE_ERROR("%s can not find any server!\n", __func__); + } else { + BTC_TRACE_ERROR("%s can not find server:%d!\n", __func__, arg->stop_srv.psm); + } + ret = ESP_BT_L2CAP_NO_SERVER; + break; + } + + // [2] remove all local related connection + for (j = 0; j < srv_cnt; j++) { + for (i = 1; i <= BTA_JV_MAX_L2C_CONN; i++) { + if (l2cap_local_param.l2cap_slots[i] != NULL && l2cap_local_param.l2cap_slots[i]->connected && + l2cap_local_param.l2cap_slots[i]->handle != 0xffff && + l2cap_local_param.l2cap_slots[i]->psm == srv_psm_arr[j]) { + BTA_JvL2capClose(l2cap_local_param.l2cap_slots[i]->handle, (tBTA_JV_L2CAP_CBACK *)btc_l2cap_inter_cb, + (void *)l2cap_local_param.l2cap_slots[i]->id); + } + } + } + + // [3] remove all server + for (j = 0; j < srv_cnt; j++) { + for (i = 1; i <= BTA_JV_MAX_L2C_CONN; i++) { + if (l2cap_local_param.l2cap_slots[i] != NULL && l2cap_local_param.l2cap_slots[i]->is_server && + l2cap_local_param.l2cap_slots[i]->handle != 0xffff && + l2cap_local_param.l2cap_slots[i]->psm == srv_psm_arr[j]) { + + if (l2cap_local_param.l2cap_slots[i]->handle > 0) { + BTA_JvL2capStopServer(l2cap_local_param.l2cap_slots[i]->psm, + (void *)l2cap_local_param.l2cap_slots[i]->id); + } + + BTA_JvFreeChannel(l2cap_local_param.l2cap_slots[i]->psm, BTA_JV_CONN_TYPE_L2CAP, + (tBTA_JV_RFCOMM_CBACK *)btc_l2cap_inter_cb, (void *)l2cap_local_param.l2cap_slots[i]->id); + } + } + } + osi_mutex_unlock(&l2cap_local_param.l2cap_slot_mutex); + } while (0); + + if (ret != ESP_BT_L2CAP_SUCCESS) { + esp_bt_l2cap_cb_param_t param; + param.srv_stop.status = ret; + param.srv_stop.psm = BTC_L2CAP_INVALID_PSM; + btc_l2cap_cb_to_app(ESP_BT_L2CAP_SRV_STOP_EVT, ¶m); + } + + if (srv_psm_arr) { + osi_free(srv_psm_arr); + srv_psm_arr = NULL; + } +} + +static void btc_l2cap_connect(btc_l2cap_args_t *arg) +{ + esp_bt_l2cap_status_t ret = ESP_BT_L2CAP_SUCCESS; + tL2CAP_CFG_INFO cfg; + + do { + if (!is_l2cap_init()) { + BTC_TRACE_ERROR("%s L2CAP have not been init\n", __func__); + ret = ESP_BT_L2CAP_NEED_INIT; + break; + } + osi_mutex_lock(&l2cap_local_param.l2cap_slot_mutex, OSI_MUTEX_MAX_TIMEOUT); + l2cap_slot_t *slot = l2cap_malloc_slot(); + if (!slot) { + BTC_TRACE_ERROR("%s unable to malloc L2CAP slot!", __func__); + osi_mutex_unlock(&l2cap_local_param.l2cap_slot_mutex); + ret = ESP_BT_L2CAP_NO_RESOURCE; + break; + } + + slot->security = arg->connect.sec_mask; + slot->role = BTC_L2CAP_ROLE_MASTER; + slot->psm = arg->connect.remote_psm; + memcpy(slot->addr, arg->connect.peer_bd_addr, ESP_BD_ADDR_LEN); + memset(&cfg, 0, sizeof(tL2CAP_CFG_INFO)); + cfg.fcr_present = TRUE; + cfg.fcr = obex_l2c_fcr_opts_def; + + BTA_JvL2capConnect(slot->security, slot->role, &obex_l2c_etm_opt, slot->psm, + L2CAP_MAX_SDU_LENGTH, &cfg, slot->addr, (tBTA_JV_L2CAP_CBACK *)btc_l2cap_inter_cb, (void *)slot->id); + osi_mutex_unlock(&l2cap_local_param.l2cap_slot_mutex); + } while (0); + + if (ret != ESP_BT_L2CAP_SUCCESS) { + esp_bt_l2cap_cb_param_t param; + param.open.status = ret; + param.open.handle = 0; + param.open.fd = -1; + param.open.tx_mtu = 0; + memset(param.open.rem_bda, 0, ESP_BD_ADDR_LEN); + btc_l2cap_cb_to_app(ESP_BT_L2CAP_OPEN_EVT, ¶m); + } +} + +static void btc_l2cap_write(uint32_t handle) +{ + do { + if (!is_l2cap_init()) { + BTC_TRACE_ERROR("%s L2CAP have not been init\n", __func__); + break; + } + l2cap_slot_t *slot = NULL; + slot = l2cap_find_slot_by_handle(handle); + if (!slot || (slot && !slot->connected)) { + if (!slot) { + BTC_TRACE_ERROR("%s unable to find l2cap slot!", __func__); + } else { + BTC_TRACE_ERROR("%s l2cap has been disconnected already!", __func__); + } + break; + } + + BT_HDR *p_buf; + if ((p_buf = fixed_queue_try_peek_first(slot->tx.queue)) != NULL && p_buf->layer_specific == 0) { + p_buf->event++; + p_buf->layer_specific = 1; + BTA_JvL2capWrite(handle, slot->id, p_buf->data + p_buf->offset, p_buf->len, (void *)slot->id); + } + } while (0); +} + +static void btc_l2cap_disconnect(uint32_t handle) +{ + esp_bt_l2cap_status_t ret = ESP_BT_L2CAP_SUCCESS; + + do { + if (!is_l2cap_init()) { + BTC_TRACE_ERROR("%s L2CAP have not been init\n", __func__); + ret = ESP_BT_L2CAP_NEED_INIT; + break; + } + l2cap_slot_t *slot = NULL; + slot = l2cap_find_slot_by_handle(handle); + if (!slot || (slot && !slot->connected)) { + if (!slot) { + BTC_TRACE_ERROR("%s unable to find L2CAP slot! disconnect fail!", __func__); + } else { + BTC_TRACE_ERROR("%s L2CAP has been disconnected already!", __func__); + } + ret = ESP_BT_L2CAP_NO_CONNECTION; + break; + } + BTA_JvL2capClose(handle, (tBTA_JV_L2CAP_CBACK *)btc_l2cap_inter_cb, (void *)slot->id); + } while(0); + + if (ret != ESP_BT_L2CAP_SUCCESS) { + esp_bt_l2cap_cb_param_t param; + param.close.status = ret; + param.close.handle = 0; + param.close.async = FALSE; + btc_l2cap_cb_to_app(ESP_BT_L2CAP_CLOSE_EVT, ¶m); + } +} + +void btc_l2cap_call_handler(btc_msg_t *msg) +{ + btc_l2cap_args_t *arg = (btc_l2cap_args_t *)(msg->arg); + switch (msg->act) { + case BTC_L2CAP_ACT_INIT: + btc_l2cap_init(); + break; + case BTC_L2CAP_ACT_UNINIT: + btc_l2cap_uninit(); + break; + case BTC_L2CAP_ACT_CONNECT: + btc_l2cap_connect(arg); + break; + case BTC_L2CAP_ACT_START_SRV: + btc_l2cap_start_srv(arg); + break; + case BTC_L2CAP_ACT_STOP_SRV: + btc_l2cap_stop_srv(arg); + break; + default: + BTC_TRACE_ERROR("%s: Unhandled event (%d)!\n", __FUNCTION__, msg->act); + break; + } +} + +void btc_l2cap_cb_handler(btc_msg_t *msg) +{ + esp_bt_l2cap_cb_param_t param; + tBTA_JV *p_data = (tBTA_JV *)msg->arg; + l2cap_slot_t *slot = NULL; + uint8_t event = msg->act; + uint8_t serial = 0; + uint32_t count = 0; + + switch (event) { + case BTA_JV_ENABLE_EVT: + param.init.status = p_data->status; + btc_l2cap_cb_to_app(ESP_BT_L2CAP_INIT_EVT, ¶m); + break; + case BTA_JV_DISABLE_EVT: + param.uninit.status = ESP_BT_L2CAP_SUCCESS; + l2cap_free_pending_slots(); + BTA_JvFree(); + osi_mutex_free(&l2cap_local_param.l2cap_slot_mutex); + if (l2cap_local_param.tx_event_group) { + vEventGroupDelete(l2cap_local_param.tx_event_group); + l2cap_local_param.tx_event_group = NULL; + } + if (l2cap_local_param.l2cap_vfs_id != -1) { + esp_vfs_unregister_with_id(l2cap_local_param.l2cap_vfs_id); + l2cap_local_param.l2cap_vfs_id = -1; + } +#if L2CAP_DYNAMIC_MEMORY == TRUE + osi_free(l2cap_local_param_ptr); + l2cap_local_param_ptr = NULL; +#endif + btc_l2cap_cb_to_app(ESP_BT_L2CAP_UNINIT_EVT, ¶m); + break; + case BTA_JV_L2CAP_OPEN_EVT: + do { + osi_mutex_lock(&l2cap_local_param.l2cap_slot_mutex, OSI_MUTEX_MAX_TIMEOUT); + slot = l2cap_find_slot_by_handle(p_data->l2c_open.handle); + if (!slot) { + osi_mutex_unlock(&l2cap_local_param.l2cap_slot_mutex); + BTC_TRACE_ERROR("%s unable to find L2CAP slot, event:%d!", __func__, event); + param.open.status = ESP_BT_L2CAP_NO_CONNECTION; + break; + } + param.open.fd = slot->fd; + osi_mutex_unlock(&l2cap_local_param.l2cap_slot_mutex); + param.open.status = p_data->l2c_open.status; + } while (0); + param.open.handle = p_data->l2c_open.handle; + param.open.tx_mtu = p_data->l2c_open.tx_mtu; + memcpy(param.open.rem_bda, p_data->l2c_open.rem_bda, ESP_BD_ADDR_LEN); + btc_l2cap_cb_to_app(ESP_BT_L2CAP_OPEN_EVT, ¶m); + break; + case BTA_JV_L2CAP_CLOSE_EVT: + param.close.status = p_data->l2c_close.status; + param.close.handle = p_data->l2c_close.handle; + param.close.async = p_data->l2c_close.async; + bool need_call = true; + do { + osi_mutex_lock(&l2cap_local_param.l2cap_slot_mutex, OSI_MUTEX_MAX_TIMEOUT); + uint32_t id = (uintptr_t)p_data->l2c_close.user_data; + slot = l2cap_find_slot_by_id(id); + if (!slot) { + param.close.status = ESP_BT_L2CAP_NO_CONNECTION; + osi_mutex_unlock(&l2cap_local_param.l2cap_slot_mutex); + BTC_TRACE_ERROR("%s unable to find RFCOMM slot, event:%d!", __func__, event); + break; + } + // if rx still has data, delay free slot + if (slot->close_alarm == NULL && slot->rx.queue && fixed_queue_length(slot->rx.queue) > 0) { + tBTA_JV *p_arg = NULL; + if ((p_arg = malloc(sizeof(tBTA_JV))) == NULL) { + param.close.status = ESP_BT_L2CAP_NO_RESOURCE; + osi_mutex_unlock(&l2cap_local_param.l2cap_slot_mutex); + BTC_TRACE_ERROR("%s unable to malloc slot close_alarm arg!", __func__); + break; + } + memcpy(p_arg, p_data, sizeof(tBTA_JV)); + slot->alarm_arg = (void *)p_arg; + if ((slot->close_alarm = + osi_alarm_new("slot", close_timeout_handler, (void *)slot, VFS_CLOSE_TIMEOUT)) == NULL) { + free(p_arg); + slot->alarm_arg = NULL; + param.close.status = ESP_BT_L2CAP_NO_RESOURCE; + osi_mutex_unlock(&l2cap_local_param.l2cap_slot_mutex); + BTC_TRACE_ERROR("%s unable to malloc slot close_alarm!", __func__); + break; + } + if (osi_alarm_set(slot->close_alarm, VFS_CLOSE_TIMEOUT) != OSI_ALARM_ERR_PASS) { + free(p_arg); + slot->alarm_arg = NULL; + osi_alarm_free(slot->close_alarm); + param.close.status = ESP_BT_L2CAP_BUSY; + osi_mutex_unlock(&l2cap_local_param.l2cap_slot_mutex); + BTC_TRACE_ERROR("%s set slot close_alarm failed!", __func__); + break; + } + BTC_TRACE_WARNING("%s slot rx data will be discard in %d milliseconds!", + __func__, VFS_CLOSE_TIMEOUT); + slot->connected = false; + need_call = false; + } + osi_mutex_unlock(&l2cap_local_param.l2cap_slot_mutex); + } while (0); + + if (need_call) { + btc_l2cap_cb_to_app(ESP_BT_L2CAP_CLOSE_EVT, ¶m); + osi_mutex_lock(&l2cap_local_param.l2cap_slot_mutex, OSI_MUTEX_MAX_TIMEOUT); + l2cap_free_slot(slot); + osi_mutex_unlock(&l2cap_local_param.l2cap_slot_mutex); + } + break; + case BTA_JV_L2CAP_START_EVT: + param.start.status = p_data->l2c_start.status; + param.start.handle = p_data->l2c_start.handle; + param.start.sec_id = p_data->l2c_start.sec_id; + btc_l2cap_cb_to_app(ESP_BT_L2CAP_START_EVT, ¶m); + break; + case BTA_JV_L2CAP_CL_INIT_EVT: + param.cl_init.status = p_data->l2c_cl_init.status; + param.cl_init.handle = p_data->l2c_cl_init.handle; + param.cl_init.sec_id = p_data->l2c_cl_init.sec_id; + btc_l2cap_cb_to_app(ESP_BT_L2CAP_CL_INIT_EVT, ¶m); + break; + case BTA_JV_L2CAP_DATA_IND_EVT: + osi_mutex_lock(&l2cap_local_param.l2cap_slot_mutex, OSI_MUTEX_MAX_TIMEOUT); + slot = l2cap_find_slot_by_handle(p_data->data_ind.handle); + if (!slot) { + osi_mutex_unlock(&l2cap_local_param.l2cap_slot_mutex); + BTC_TRACE_ERROR("%s unable to find L2CAP slot, event:%d!", __func__, event); + break; + } + if (BTA_JvL2capReady(p_data->data_ind.handle, &count) == BTA_JV_SUCCESS && count > 0) { + BT_HDR *p_data_buf = osi_malloc(count + sizeof(BT_HDR)); + if (p_data_buf == NULL) { + osi_mutex_unlock(&l2cap_local_param.l2cap_slot_mutex); + BTC_TRACE_ERROR("%s, %d count = %d malloc failed!", __func__, __LINE__, count); + break; // to do disconnect + } + memset(p_data_buf, 0, count + sizeof(BT_HDR)); + p_data_buf->len = BTA_JvL2capRead(p_data->data_ind.handle, slot->id, p_data_buf->data, count); + if (p_data_buf->len > 0) { + fixed_queue_enqueue(slot->rx.queue, p_data_buf, FIXED_QUEUE_MAX_TIMEOUT); + } else { + osi_free(p_data_buf); + break; + } + } + osi_mutex_unlock(&l2cap_local_param.l2cap_slot_mutex); + break; + case BTA_JV_L2CAP_CONG_EVT: + osi_mutex_lock(&l2cap_local_param.l2cap_slot_mutex, OSI_MUTEX_MAX_TIMEOUT); + slot = l2cap_find_slot_by_handle(p_data->l2c_cong.handle); + if (!slot) { + osi_mutex_unlock(&l2cap_local_param.l2cap_slot_mutex); + BTC_TRACE_ERROR("%s unable to find L2CAP slot, event:%d!", __func__, event); + break; + } + if (!p_data->l2c_cong.cong) { + BT_HDR *p_buf; + if ((p_buf = fixed_queue_try_peek_first(slot->tx.queue)) != NULL && p_buf->layer_specific == 0) { + p_buf->event++; + p_buf->layer_specific = 1; + BTA_JvL2capWrite(p_data->l2c_cong.handle, slot->id, p_buf->data + p_buf->offset, p_buf->len, (void *)slot->id); + } + } + osi_mutex_unlock(&l2cap_local_param.l2cap_slot_mutex); + break; + case BTA_JV_L2CAP_READ_EVT: + osi_mutex_lock(&l2cap_local_param.l2cap_slot_mutex, OSI_MUTEX_MAX_TIMEOUT); + slot = l2cap_find_slot_by_handle(p_data->l2c_read.handle); + if (!slot) { + osi_mutex_unlock(&l2cap_local_param.l2cap_slot_mutex); + BTC_TRACE_ERROR("%s unable to find L2CAP slot, event:%d!", __func__, event); + break; + } + if (BTA_JvL2capReady(p_data->l2c_read.handle, &count) == BTA_JV_SUCCESS && count > 0) { + BT_HDR *p_data_buf = osi_malloc(count + sizeof(BT_HDR)); + if (p_data_buf == NULL) { + osi_mutex_unlock(&l2cap_local_param.l2cap_slot_mutex); + BTC_TRACE_ERROR("%s, %d count = %d malloc failed!", __func__, __LINE__, count); + break; // to do disconnect + } + memset(p_data_buf, 0, count + sizeof(BT_HDR)); + p_data_buf->len = BTA_JvL2capRead(p_data->data_ind.handle, slot->id, p_data_buf->data, count); + if (p_data_buf->len > 0) { + fixed_queue_enqueue(slot->rx.queue, p_data_buf, FIXED_QUEUE_MAX_TIMEOUT); + } else { + osi_free(p_data_buf); + break; + } + } + osi_mutex_unlock(&l2cap_local_param.l2cap_slot_mutex); + break; + case BTA_JV_L2CAP_WRITE_EVT: + osi_mutex_lock(&l2cap_local_param.l2cap_slot_mutex, OSI_MUTEX_MAX_TIMEOUT); + slot = l2cap_find_slot_by_handle(p_data->l2c_write.handle); + if (!slot) { + BTC_TRACE_ERROR("%s unable to find L2CAP slot!, handle:%d", __func__, p_data->l2c_write.handle); + } + if (slot) { + BT_HDR *p_buf; + serial = slot->serial; + if ((p_buf = fixed_queue_try_peek_first(slot->tx.queue)) == NULL) { + osi_mutex_unlock(&l2cap_local_param.l2cap_slot_mutex); + break; + } + if (p_data->l2c_write.status == BTA_JV_SUCCESS) { + p_buf->len -= p_data->l2c_write.len; + p_buf->offset += p_data->l2c_write.len; + p_buf->layer_specific = 0; + if (p_buf->len == 0) { + osi_free(fixed_queue_dequeue(slot->tx.queue, FIXED_QUEUE_MAX_TIMEOUT)); + if (fixed_queue_length(slot->tx.queue) <= SLOT_TX_QUEUE_LOW_WM) { + xEventGroupSetBits(l2cap_local_param.tx_event_group, SLOT_WRITE_BIT(serial)); + } + } + + if ((p_buf = fixed_queue_try_peek_first(slot->tx.queue)) != NULL && p_buf->layer_specific == 0 && + !p_data->l2c_write.cong) { + p_buf->layer_specific = 1; + p_buf->event++; + BTA_JvL2capWrite(p_data->l2c_write.handle, slot->id, p_buf->data + p_buf->offset, p_buf->len, (void *)slot->id); + } + } + } + osi_mutex_unlock(&l2cap_local_param.l2cap_slot_mutex); + break; + case BTA_JV_FREE_SCN_EVT: + param.srv_stop.status = p_data->free_scn.status; + param.srv_stop.psm = p_data->free_scn.scn; + btc_l2cap_cb_to_app(ESP_BT_L2CAP_SRV_STOP_EVT, ¶m); + break; + default: + break; + } + + return; +} + +static ssize_t l2cap_vfs_write(int fd, const void * data, size_t size) +{ + assert(data != NULL); + errno = 0; + if (size == 0) { + return 0; + } + if (!is_l2cap_init()) { + BTC_TRACE_ERROR("%s L2CAP have not been init\n", __func__); + errno = ESRCH; + return -1; + } + + l2cap_slot_t *slot = NULL; + uint8_t serial = 0; + osi_mutex_lock(&l2cap_local_param.l2cap_slot_mutex, OSI_MUTEX_MAX_TIMEOUT); + slot = l2cap_find_slot_by_fd(fd); + if (!slot) { + osi_mutex_unlock(&l2cap_local_param.l2cap_slot_mutex); + BTC_TRACE_ERROR("%s unable to find L2CAP slot!", __func__); + errno = ENOENT; + return -1; + } + serial = slot->serial; + osi_mutex_unlock(&l2cap_local_param.l2cap_slot_mutex); + + ssize_t sent = 0, write_size = 0; + size_t tx_len; + BT_HDR *p_buf = NULL; + bool enqueue_status= false; + EventBits_t tx_event_group_val = 0; + while (1) { + tx_event_group_val = 0; + if (size) { + if (p_buf == NULL) { + write_size = size < slot->tx_mtu ? size : slot->tx_mtu; + if ((p_buf = osi_malloc(sizeof(BT_HDR) + write_size)) == NULL) { + BTC_TRACE_ERROR("%s malloc failed!", __func__); + errno = ENOMEM; + sent = -1; + break; + } + p_buf->offset = 0; + p_buf->len = write_size; + p_buf->event = 0; // indicate the p_buf be sent count + p_buf->layer_specific = 0; // indicate the p_buf whether to be sent, 0 - ready to send; 1 - have sent + memcpy((UINT8 *)(p_buf + 1), data + sent, write_size); + } + } else { + break; + } + + osi_mutex_lock(&l2cap_local_param.l2cap_slot_mutex, OSI_MUTEX_MAX_TIMEOUT); + if ((slot = l2cap_local_param.l2cap_slots[serial]) != NULL) { + tx_len = fixed_queue_length(slot->tx.queue); + enqueue_status = fixed_queue_enqueue(slot->tx.queue, p_buf, 0); + if (!enqueue_status) { + BTC_TRACE_DEBUG("%s tx_len:%d, fd:%d\n", __func__, fixed_queue_length(slot->tx.queue), fd); + osi_mutex_unlock(&l2cap_local_param.l2cap_slot_mutex); + //block untill under water level, be closed or time out + tx_event_group_val = + xEventGroupWaitBits(l2cap_local_param.tx_event_group, SLOT_WRITE_BIT(serial) | SLOT_CLOSE_BIT(serial), pdTRUE, + pdFALSE, VFS_WRITE_TIMEOUT / portTICK_PERIOD_MS); + if (tx_event_group_val & SLOT_CLOSE_BIT(serial)) { + BTC_TRACE_ERROR("%s exit for L2CAP close, fd:%d!", __func__, fd); + errno = EPIPE; + sent = -1; + break; + } else if (tx_event_group_val & SLOT_WRITE_BIT(serial)) { + continue; + } else if (tx_event_group_val == 0) { + BTC_TRACE_ERROR("%s exit for time out, fd:%d!", __func__, fd); + errno = EBUSY; + sent = -1; + break; + } + } + if (tx_len == 0) { + btc_l2cap_write(slot->handle); + } + sent += write_size; + size -= write_size; + p_buf = NULL; + } else { + osi_mutex_unlock(&l2cap_local_param.l2cap_slot_mutex); + errno = EPIPE; + sent = -1; + break; + } + osi_mutex_unlock(&l2cap_local_param.l2cap_slot_mutex); + } + + //errors occur, need to cleanup + if (p_buf) { + osi_free(p_buf); + p_buf = NULL; + } + + return sent; +} + +static int l2cap_vfs_close(int fd) +{ + errno = 0; + if (!is_l2cap_init()) { + BTC_TRACE_ERROR("%s L2CAP have not been init\n", __func__); + errno = ESRCH; + return -1; + } + l2cap_slot_t *slot = NULL; + osi_mutex_lock(&l2cap_local_param.l2cap_slot_mutex, OSI_MUTEX_MAX_TIMEOUT); + slot = l2cap_find_slot_by_fd(fd); + if (!slot) { + osi_mutex_unlock(&l2cap_local_param.l2cap_slot_mutex); + BTC_TRACE_ERROR("%s unable to find L2CAP slot!", __func__); + errno = ENOENT; + return -1; + } + btc_l2cap_disconnect(slot->handle); + osi_mutex_unlock(&l2cap_local_param.l2cap_slot_mutex); + return 0; +} + +static ssize_t l2cap_vfs_read(int fd, void * dst, size_t size) +{ + assert(dst != NULL); + errno = 0; + if (!is_l2cap_init()) { + BTC_TRACE_ERROR("%s L2CAP have not been init\n", __func__); + errno = ESRCH; + return -1; + } + + l2cap_slot_t *slot = NULL; + uint8_t serial = 0; + osi_mutex_lock(&l2cap_local_param.l2cap_slot_mutex, OSI_MUTEX_MAX_TIMEOUT); + slot = l2cap_find_slot_by_fd(fd); + if (!slot) { + osi_mutex_unlock(&l2cap_local_param.l2cap_slot_mutex); + BTC_TRACE_ERROR("%s unable to find L2CAP slot!", __func__); + errno = ENOENT; + return -1; + } + serial = slot->serial; + osi_mutex_unlock(&l2cap_local_param.l2cap_slot_mutex); + + ssize_t item_size = 0; + BT_HDR *p_buf; + while (1) { + osi_mutex_lock(&l2cap_local_param.l2cap_slot_mutex, OSI_MUTEX_MAX_TIMEOUT); + if ((slot = l2cap_local_param.l2cap_slots[serial]) != NULL) { + if (fixed_queue_length(slot->rx.queue) > 0) { + // free unused p_buf + if ((p_buf = (BT_HDR *)fixed_queue_try_peek_first(slot->rx.queue)) != NULL && p_buf->len == 0) { + osi_free(fixed_queue_dequeue(slot->rx.queue, FIXED_QUEUE_MAX_TIMEOUT)); + p_buf = NULL; + } + if (size == 0 || (p_buf = (BT_HDR *)fixed_queue_try_peek_first(slot->rx.queue)) == NULL) { + osi_mutex_unlock(&l2cap_local_param.l2cap_slot_mutex); + break; + } + } else { + /** + * If close_alarm is not NULL, it means that we have received the BTA_JV_L2CAP_CLOSE_EVT. + * And we can trigger close_alarm immediately. + */ + if (slot->close_alarm && osi_alarm_is_active(slot->close_alarm)) { + osi_alarm_cancel(slot->close_alarm); + osi_alarm_set(slot->close_alarm, 0); + } + osi_mutex_unlock(&l2cap_local_param.l2cap_slot_mutex); + break; + } + } else { + osi_mutex_unlock(&l2cap_local_param.l2cap_slot_mutex); + BTC_TRACE_ERROR("%s peer close, data will be discarded!\n", __func__); + errno = EPIPE; + item_size = -1; + break; + } + osi_mutex_unlock(&l2cap_local_param.l2cap_slot_mutex); + + if (p_buf->len <= size) { + memcpy(dst, p_buf->data + p_buf->offset, p_buf->len); + size -= p_buf->len; + item_size += p_buf->len; + dst += p_buf->len; + p_buf->offset += p_buf->len; + p_buf->len = 0; // indicate the p_buf is unused + } else { + memcpy(dst, p_buf->data + p_buf->offset, size); + item_size += size; + p_buf->offset += size; + p_buf->len -= size; + size = 0; + } + } + return item_size; +} + +esp_err_t btc_l2cap_vfs_register(void) +{ + esp_err_t ret = ESP_OK; + + do { + if (!is_l2cap_init()) { + BTC_TRACE_ERROR("%s L2CAP have not been init\n", __func__); + ret = ESP_FAIL; + break; + } + + esp_vfs_t vfs = { + .flags = ESP_VFS_FLAG_DEFAULT, + .write = l2cap_vfs_write, + .open = NULL, + .fstat = NULL, + .close = l2cap_vfs_close, + .read = l2cap_vfs_read, + .fcntl = NULL + }; + + // No FD range is registered here: l2cap_vfs_id is used to register/unregister + // file descriptors + if (esp_vfs_register_with_id(&vfs, NULL, &l2cap_local_param.l2cap_vfs_id) != ESP_OK) { + ret = ESP_FAIL; + break; + } + } while (0); + + return ret; +} + +esp_err_t btc_l2cap_vfs_unregister(void) +{ + esp_err_t ret = ESP_OK; + do { + if (!is_l2cap_init()) { + BTC_TRACE_ERROR("%s L2CAP have not been init\n", __func__); + ret = ESP_FAIL; + break; + } + + if (l2cap_local_param.l2cap_vfs_id != -1) { + if (esp_vfs_unregister_with_id(l2cap_local_param.l2cap_vfs_id) != ESP_OK) { + ret = ESP_FAIL; + } + } + l2cap_local_param.l2cap_vfs_id = -1; + } while (0); + + return ret; +} + +#endif ///defined BTC_L2CAP_INCLUDED && BTC_L2CAP_INCLUDED == TRUE diff --git a/lib/bt/host/bluedroid/btc/profile/std/sdp/btc_sdp.c b/lib/bt/host/bluedroid/btc/profile/std/sdp/btc_sdp.c new file mode 100644 index 00000000..143ae336 --- /dev/null +++ b/lib/bt/host/bluedroid/btc/profile/std/sdp/btc_sdp.c @@ -0,0 +1,1218 @@ +/* + * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include "btc_sdp.h" +#include "btc/btc_manage.h" +#include "btc/btc_task.h" +#include "bta/bta_sdp_api.h" +#include "bta/bta_sys.h" +#include "bta/utl.h" +#include "bt_sdp.h" +#include "osi/mutex.h" +#include "osi/allocator.h" +#include "esp_sdp_api.h" + +#if (defined BTC_SDP_INCLUDED && BTC_SDP_INCLUDED == TRUE) + +typedef enum { + SDP_RECORD_FREE = 0, + SDP_RECORD_ALLOCED, +} sdp_state_t; + +typedef struct { + sdp_state_t state; + int sdp_handle; + bluetooth_sdp_record* record_data; +} sdp_slot_t; + +typedef struct { + sdp_slot_t *sdp_slots[SDP_MAX_RECORDS]; + osi_mutex_t sdp_slot_mutex; +} sdp_local_param_t; + +#if SDP_DYNAMIC_MEMORY == FALSE +static sdp_local_param_t sdp_local_param; +#else +static sdp_local_param_t *sdp_local_param_ptr; +#define sdp_local_param (*sdp_local_param_ptr) +#endif + +#if SDP_DYNAMIC_MEMORY == FALSE +#define is_sdp_init() (sdp_local_param.sdp_slot_mutex != NULL) +#else +#define is_sdp_init() (&sdp_local_param != NULL && sdp_local_param.sdp_slot_mutex != NULL) +#endif + + +static inline void btc_sdp_cb_to_app(esp_sdp_cb_event_t event, esp_sdp_cb_param_t *param) +{ + esp_sdp_cb_t btc_sdp_cb = (esp_sdp_cb_t)btc_profile_cb_get(BTC_PID_SDP); + if (btc_sdp_cb) { + btc_sdp_cb(event, param); + } +} + +static void sdp_disable_handler(void) +{ + btc_msg_t msg; + bt_status_t status; + + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_SDP; + msg.act = BTA_SDP_DISENABLE_EVT; + + status = btc_transfer_context(&msg, NULL, 0, NULL, NULL); + + if (status != BT_STATUS_SUCCESS) { + BTC_TRACE_ERROR("%s btc_transfer_context failed", __func__); + } +} + +static int get_sdp_records_size(bluetooth_sdp_record* in_record, int count) +{ + bluetooth_sdp_record* record = in_record; + int records_size = 0; + + for(int i = 0; i < count; i++) { + record = &in_record[i]; + records_size += sizeof(bluetooth_sdp_record); + records_size += record->hdr.service_name_length; + if(record->hdr.service_name_length > 0){ + records_size++; /* + '\0' termination of string */ + } + records_size += record->hdr.user1_ptr_len; + records_size += record->hdr.user2_ptr_len; + } + + return records_size; +} + +static void set_sdp_handle(int id, int handle) +{ + sdp_slot_t *slot = NULL; + + BTC_TRACE_DEBUG("%s() id=%d to handle=0x%08x", __func__, id, handle); + + if(id >= SDP_MAX_RECORDS) { + BTC_TRACE_ERROR("%s() failed - id %d is invalid", __func__, id); + return; + } + + osi_mutex_lock(&sdp_local_param.sdp_slot_mutex, OSI_MUTEX_MAX_TIMEOUT); + slot = sdp_local_param.sdp_slots[id]; + if (slot == NULL) { + osi_mutex_unlock(&sdp_local_param.sdp_slot_mutex); + BTC_TRACE_ERROR("%s() id=%d to handle=0x%08x, set failed", __func__, id, handle); + return; + } + slot->sdp_handle = handle; + osi_mutex_unlock(&sdp_local_param.sdp_slot_mutex); +} + + +static bool get_sdp_record_by_handle(int handle, bluetooth_sdp_record* record) +{ + sdp_slot_t *slot = NULL; + + osi_mutex_lock(&sdp_local_param.sdp_slot_mutex, OSI_MUTEX_MAX_TIMEOUT); + + for (int i = 0; i < SDP_MAX_RECORDS; i++) { + slot = sdp_local_param.sdp_slots[i]; + if ((slot != NULL) && (slot->sdp_handle == handle)) { + memcpy(record, slot->record_data, sizeof(bluetooth_sdp_record)); + osi_mutex_unlock(&sdp_local_param.sdp_slot_mutex); + return true; + } + } + + osi_mutex_unlock(&sdp_local_param.sdp_slot_mutex); + return false; +} + +static int get_sdp_slot_id_by_handle(int handle) +{ + sdp_slot_t *slot = NULL; + + osi_mutex_lock(&sdp_local_param.sdp_slot_mutex, OSI_MUTEX_MAX_TIMEOUT); + + for (int i = 0; i < SDP_MAX_RECORDS; i++) { + slot = sdp_local_param.sdp_slots[i]; + if ((slot != NULL) && (slot->sdp_handle == handle)) { + osi_mutex_unlock(&sdp_local_param.sdp_slot_mutex); + return i; + } + } + + osi_mutex_unlock(&sdp_local_param.sdp_slot_mutex); + return -1; +} + +static sdp_slot_t *start_create_sdp(int id) +{ + sdp_slot_t *sdp_slot = NULL; + + if(id >= SDP_MAX_RECORDS) { + BTC_TRACE_ERROR("%s() failed - id %d is invalid", __func__, id); + return NULL; + } + + osi_mutex_lock(&sdp_local_param.sdp_slot_mutex, OSI_MUTEX_MAX_TIMEOUT); + sdp_slot = sdp_local_param.sdp_slots[id]; + if (sdp_slot == NULL) { + BTC_TRACE_ERROR("%s() id = %d ", __func__, id); + } else if(sdp_slot->state != SDP_RECORD_ALLOCED) { + BTC_TRACE_ERROR("%s() failed - state for id %d is state = %d expected %d", __func__, + id, sdp_local_param.sdp_slots[id]->state, SDP_RECORD_ALLOCED); + /* The record have been removed before this event occurred - e.g. deinit */ + sdp_slot = NULL; + } + osi_mutex_unlock(&sdp_local_param.sdp_slot_mutex); + + return sdp_slot; +} + +/* Deep copy all content of in_records into out_records. + * out_records must point to a chunk of memory large enough to contain all + * the data. Use getSdpRecordsSize() to calculate the needed size. */ +static void copy_sdp_records(bluetooth_sdp_record* in_records, bluetooth_sdp_record* out_records, int count) +{ + bluetooth_sdp_record *in_record; + bluetooth_sdp_record *out_record; + char *free_ptr = (char*)(&out_records[count]); /* set pointer to after the last entry */ + + for(int i = 0; i < count; i++) { + in_record = &in_records[i]; + out_record = &out_records[i]; + *out_record = *in_record; + + if(in_record->hdr.service_name == NULL || in_record->hdr.service_name_length == 0) { + out_record->hdr.service_name = NULL; + out_record->hdr.service_name_length = 0; + } else { + out_record->hdr.service_name = free_ptr; // Update service_name pointer + // Copy string + memcpy(free_ptr, in_record->hdr.service_name, in_record->hdr.service_name_length); + free_ptr += in_record->hdr.service_name_length; + *(free_ptr) = '\0'; // Set '\0' termination of string + free_ptr++; + } + if(in_record->hdr.user1_ptr != NULL) { + out_record->hdr.user1_ptr = (UINT8*)free_ptr; // Update pointer + memcpy(free_ptr, in_record->hdr.user1_ptr, in_record->hdr.user1_ptr_len); // Copy content + free_ptr += in_record->hdr.user1_ptr_len; + } + if(in_record->hdr.user2_ptr != NULL) { + out_record->hdr.user2_ptr = (UINT8*)free_ptr; // Update pointer + memcpy(free_ptr, in_record->hdr.user2_ptr, in_record->hdr.user2_ptr_len); // Copy content + free_ptr += in_record->hdr.user2_ptr_len; + } + } +} + +static int alloc_sdp_slot(bluetooth_sdp_record* in_record) +{ + int i; + int record_size = get_sdp_records_size(in_record, 1); + bluetooth_sdp_record *record = NULL; + sdp_slot_t **slot = NULL; + + record = osi_malloc(record_size); + if (record == NULL) { + BTC_TRACE_ERROR("%s() osi_malloc failed!", __func__); + return -1; + } + + copy_sdp_records(in_record, record, 1); + + osi_mutex_lock(&sdp_local_param.sdp_slot_mutex, OSI_MUTEX_MAX_TIMEOUT); + for(i = 0; i < SDP_MAX_RECORDS; i++) + { + slot = &sdp_local_param.sdp_slots[i]; + if ((*slot) == NULL) { + if (((*slot) = (sdp_slot_t *)osi_malloc(sizeof(sdp_slot_t))) == NULL) { + osi_mutex_unlock(&sdp_local_param.sdp_slot_mutex); + BTC_TRACE_ERROR("%s() osi_malloc slot failed!", __func__); + osi_free(record); + return -1; + } + (*slot)->state = SDP_RECORD_ALLOCED; + (*slot)->record_data = record; + break; + } + } + osi_mutex_unlock(&sdp_local_param.sdp_slot_mutex); + if(i >= SDP_MAX_RECORDS) { + BTC_TRACE_ERROR("%s() failed - no more free slots!", __func__); + osi_free(record); + return -1; + } + + return i; +} + +static int free_sdp_slot(int id) +{ + int handle = -1; + bluetooth_sdp_record *record = NULL; + sdp_slot_t *slot = NULL; + + if(id >= SDP_MAX_RECORDS) { + APPL_TRACE_ERROR("%s() failed - id %d is invalid", __func__, id); + return handle; + } + slot = sdp_local_param.sdp_slots[id]; + if (slot == NULL) { + // already freed + return handle; + } + + osi_mutex_lock(&sdp_local_param.sdp_slot_mutex, OSI_MUTEX_MAX_TIMEOUT); + handle = slot->sdp_handle; + if (slot->state != SDP_RECORD_FREE) { + /* safe a copy of the pointer, and free after unlock() */ + record = slot->record_data; + } + osi_mutex_unlock(&sdp_local_param.sdp_slot_mutex); + + if(record != NULL) { + osi_free(record); + } else { + // Record have already been freed + handle = -1; + } + osi_free(slot); + slot = NULL; + + return handle; +} + +/* Create a raw SDP record based on information stored in a bluetooth_sdp_raw_record */ +static int add_raw_sdp(const bluetooth_sdp_record* rec) +{ + tSDP_PROTOCOL_ELEM protoList [2]; + UINT16 browse = UUID_SERVCLASS_PUBLIC_BROWSE_GROUP; + bool status = true; + // Buffer capable to hold 2, 4 and 16-byte UUIDs + UINT8 temp[LEN_UUID_128]; + UINT8* p_temp = temp; + UINT32 sdp_handle = 0; + + BTC_TRACE_DEBUG("%s(): scn 0x%02x, psm = 0x%04x\n service name %s", __func__, + rec->hdr.rfcomm_channel_number, rec->hdr.l2cap_psm, rec->hdr.service_name); + + if ((sdp_handle = SDP_CreateRecord()) == 0) { + BTC_TRACE_ERROR("%s(): Unable to register raw sdp record", __func__); + return sdp_handle; + } + + if (rec->hdr.bt_uuid.len == ESP_UUID_LEN_16) { + UINT8_TO_BE_STREAM (p_temp, (UUID_DESC_TYPE << 3) | SIZE_TWO_BYTES); + UINT16_TO_BE_STREAM (p_temp, rec->hdr.bt_uuid.uuid.uuid16); + } else if (rec->hdr.bt_uuid.len == ESP_UUID_LEN_32) { + UINT8_TO_BE_STREAM (p_temp, (UUID_DESC_TYPE << 3) | SIZE_FOUR_BYTES); + UINT32_TO_BE_STREAM (p_temp, rec->hdr.bt_uuid.uuid.uuid32); + } else if (rec->hdr.bt_uuid.len == ESP_UUID_LEN_128) { + UINT8_TO_BE_STREAM (p_temp, (UUID_DESC_TYPE << 3) | SIZE_SIXTEEN_BYTES); + ARRAY_TO_BE_STREAM (p_temp, rec->hdr.bt_uuid.uuid.uuid128, LEN_UUID_128); + } else { + SDP_DeleteRecord(sdp_handle); + sdp_handle = 0; + return sdp_handle; + } + /* add service class */ + status &= SDP_AddAttribute(sdp_handle, ATTR_ID_SERVICE_CLASS_ID_LIST, + DATA_ELE_SEQ_DESC_TYPE, (UINT32) (p_temp - temp), temp); + + memset( protoList, 0 , 2*sizeof(tSDP_PROTOCOL_ELEM) ); + + /* add protocol list, including RFCOMM scn */ + protoList[0].protocol_uuid = UUID_PROTOCOL_L2CAP; + protoList[0].num_params = 0; + if (rec->hdr.rfcomm_channel_number < 0) { + status &= SDP_AddProtocolList(sdp_handle, 1, protoList); + } else { + protoList[1].protocol_uuid = UUID_PROTOCOL_RFCOMM; + protoList[1].num_params = 1; + protoList[1].params[0] = rec->hdr.rfcomm_channel_number; + status &= SDP_AddProtocolList(sdp_handle, 2, protoList); + } + + /* Add a name entry */ + status &= SDP_AddAttribute(sdp_handle, + (UINT16)ATTR_ID_SERVICE_NAME, + (UINT8)TEXT_STR_DESC_TYPE, + (UINT32)(rec->hdr.service_name_length + 1), + (UINT8 *)rec->hdr.service_name); + + /* Add the L2CAP PSM if present */ + if(rec->hdr.l2cap_psm != -1) { + p_temp = temp;// The macro modifies p_temp, hence rewind. + UINT16_TO_BE_STREAM(p_temp, rec->hdr.l2cap_psm); + status &= SDP_AddAttribute(sdp_handle, ATTR_ID_GOEP_L2CAP_PSM, + UINT_DESC_TYPE, (UINT32)2, temp); + } + + /* Make the service browseable */ + status &= SDP_AddUuidSequence (sdp_handle, ATTR_ID_BROWSE_GROUP_LIST, 1, &browse); + + if (!status) { + SDP_DeleteRecord(sdp_handle); + sdp_handle = 0; + BTC_TRACE_ERROR("%s() FAILED, status = %d", __func__, status); + } else { + if (rec->hdr.bt_uuid.len == ESP_UUID_LEN_16) { + bta_sys_add_uuid(rec->hdr.bt_uuid.uuid.uuid16); + } else if (rec->hdr.bt_uuid.len == ESP_UUID_LEN_32) { + bta_sys_add_uuid_32(rec->hdr.bt_uuid.uuid.uuid32); + } else if (rec->hdr.bt_uuid.len == ESP_UUID_LEN_128) { + bta_sys_add_uuid_128((UINT8 *)&rec->hdr.bt_uuid.uuid.uuid128); + } + BTC_TRACE_DEBUG("%s(): SDP Registered (handle 0x%08x)", __func__, sdp_handle); + } + + return sdp_handle; +} + +/* Create a MAP MAS SDP record based on information stored in a bluetooth_sdp_mas_record */ +static int add_maps_sdp(const bluetooth_sdp_mas_record* rec) +{ + tSDP_PROTOCOL_ELEM protoList [3]; + UINT16 service = UUID_SERVCLASS_MESSAGE_ACCESS; + UINT16 browse = UUID_SERVCLASS_PUBLIC_BROWSE_GROUP; + bool status = true; + UINT32 sdp_handle = 0; + UINT8 temp[4]; + UINT8* p_temp = temp; + + BTC_TRACE_DEBUG("%s(): MASID = 0x%02x, scn 0x%02x, psm = 0x%04x\n service name %s", __func__, + rec->mas_instance_id, rec->hdr.rfcomm_channel_number, + rec->hdr.l2cap_psm, rec->hdr.service_name); + + BTC_TRACE_DEBUG(" msg_types: 0x%02x, feature_bits: 0x%08x", + rec->supported_message_types, rec->supported_features); + + if ((sdp_handle = SDP_CreateRecord()) == 0) { + BTC_TRACE_ERROR("%s() - Unable to register MAPS Service", __func__); + return sdp_handle; + } + + /* add service class */ + status &= SDP_AddServiceClassIdList(sdp_handle, 1, &service); + memset( protoList, 0 , 3*sizeof(tSDP_PROTOCOL_ELEM) ); + + /* add protocol list, including RFCOMM scn */ + protoList[0].protocol_uuid = UUID_PROTOCOL_L2CAP; + protoList[0].num_params = 0; + protoList[1].protocol_uuid = UUID_PROTOCOL_RFCOMM; + protoList[1].num_params = 1; + protoList[1].params[0] = rec->hdr.rfcomm_channel_number; + protoList[2].protocol_uuid = UUID_PROTOCOL_OBEX; + protoList[2].num_params = 0; + status &= SDP_AddProtocolList(sdp_handle, 3, protoList); + + /* Add a name entry */ + status &= SDP_AddAttribute(sdp_handle, + (UINT16)ATTR_ID_SERVICE_NAME, + (UINT8)TEXT_STR_DESC_TYPE, + (UINT32)(rec->hdr.service_name_length + 1), + (UINT8 *)rec->hdr.service_name); + + /* Add in the Bluetooth Profile Descriptor List */ + status &= SDP_AddProfileDescriptorList(sdp_handle, + UUID_SERVCLASS_MAP_PROFILE, + rec->hdr.profile_version); + + /* Add MAS instance ID */ + status &= SDP_AddAttribute(sdp_handle, ATTR_ID_MAS_INSTANCE_ID, UINT_DESC_TYPE, + (UINT32)1, (UINT8*)&rec->mas_instance_id); + + /* Add supported message types */ + status &= SDP_AddAttribute(sdp_handle, ATTR_ID_SUPPORTED_MSG_TYPE, UINT_DESC_TYPE, + (UINT32)1, (UINT8*)&rec->supported_message_types); + + /* Add supported feature */ + UINT32_TO_BE_STREAM(p_temp, rec->supported_features); + status &= SDP_AddAttribute(sdp_handle, ATTR_ID_MAP_SUPPORTED_FEATURES, + UINT_DESC_TYPE, (UINT32)4, temp); + + /* Add the L2CAP PSM if present */ + if(rec->hdr.l2cap_psm != -1) { + p_temp = temp;// The macro modifies p_temp, hence rewind. + UINT16_TO_BE_STREAM(p_temp, rec->hdr.l2cap_psm); + status &= SDP_AddAttribute(sdp_handle, ATTR_ID_GOEP_L2CAP_PSM, + UINT_DESC_TYPE, (UINT32)2, temp); + } + + /* Make the service browseable */ + status &= SDP_AddUuidSequence (sdp_handle, ATTR_ID_BROWSE_GROUP_LIST, 1, &browse); + + if (!status) { + SDP_DeleteRecord(sdp_handle); + sdp_handle = 0; + BTC_TRACE_ERROR("%s() FAILED", __func__); + } else { + bta_sys_add_uuid(service); /* UUID_SERVCLASS_MESSAGE_ACCESS */ + BTC_TRACE_DEBUG("%s(): SDP Registered (handle 0x%08x)", __func__, sdp_handle); + } + + return sdp_handle; +} + +/* Create a MAP MNS SDP record based on information stored in a bluetooth_sdp_mns_record */ +static int add_mapc_sdp(const bluetooth_sdp_mns_record* rec) +{ + tSDP_PROTOCOL_ELEM protoList [3]; + UINT16 service = UUID_SERVCLASS_MESSAGE_NOTIFICATION; + UINT16 browse = UUID_SERVCLASS_PUBLIC_BROWSE_GROUP; + bool status = true; + UINT32 sdp_handle = 0; + UINT8 temp[4]; + UINT8* p_temp = temp; + + BTC_TRACE_DEBUG("%s(): scn 0x%02x, psm = 0x%04x\n service name %s", __func__, + rec->hdr.rfcomm_channel_number, rec->hdr.l2cap_psm, rec->hdr.service_name); + + BTC_TRACE_DEBUG(" feature_bits: 0x%08x", rec->supported_features); + + if ((sdp_handle = SDP_CreateRecord()) == 0) { + BTC_TRACE_ERROR("%s(): Unable to register MAP Notification Service", __func__); + return sdp_handle; + } + + /* add service class */ + status &= SDP_AddServiceClassIdList(sdp_handle, 1, &service); + memset( protoList, 0 , 3*sizeof(tSDP_PROTOCOL_ELEM) ); + + /* add protocol list, including RFCOMM scn */ + protoList[0].protocol_uuid = UUID_PROTOCOL_L2CAP; + protoList[0].num_params = 0; + protoList[1].protocol_uuid = UUID_PROTOCOL_RFCOMM; + protoList[1].num_params = 1; + protoList[1].params[0] = rec->hdr.rfcomm_channel_number; + protoList[2].protocol_uuid = UUID_PROTOCOL_OBEX; + protoList[2].num_params = 0; + status &= SDP_AddProtocolList(sdp_handle, 3, protoList); + + /* Add a name entry */ + status &= SDP_AddAttribute(sdp_handle, + (UINT16)ATTR_ID_SERVICE_NAME, + (UINT8)TEXT_STR_DESC_TYPE, + (UINT32)(rec->hdr.service_name_length + 1), + (UINT8 *)rec->hdr.service_name); + + /* Add in the Bluetooth Profile Descriptor List */ + status &= SDP_AddProfileDescriptorList(sdp_handle, + UUID_SERVCLASS_MAP_PROFILE, + rec->hdr.profile_version); + + /* Add supported feature */ + UINT32_TO_BE_STREAM(p_temp, rec->supported_features); + status &= SDP_AddAttribute(sdp_handle, ATTR_ID_MAP_SUPPORTED_FEATURES, + UINT_DESC_TYPE, (UINT32)4, temp); + + /* Add the L2CAP PSM if present */ + if(rec->hdr.l2cap_psm != -1) { + p_temp = temp;// The macro modifies p_temp, hence rewind. + UINT16_TO_BE_STREAM(p_temp, rec->hdr.l2cap_psm); + status &= SDP_AddAttribute(sdp_handle, ATTR_ID_GOEP_L2CAP_PSM, + UINT_DESC_TYPE, (UINT32)2, temp); + } + + /* Make the service browseable */ + status &= SDP_AddUuidSequence (sdp_handle, ATTR_ID_BROWSE_GROUP_LIST, 1, &browse); + + if (!status) { + SDP_DeleteRecord(sdp_handle); + sdp_handle = 0; + BTC_TRACE_ERROR("%s() FAILED", __func__); + } else { + bta_sys_add_uuid(service); /* UUID_SERVCLASS_MESSAGE_ACCESS */ + BTC_TRACE_DEBUG("%s(): SDP Registered (handle 0x%08x)", __func__, sdp_handle); + } + + return sdp_handle; +} + +/* Create a PBAP Server SDP record based on information stored in a bluetooth_sdp_pse_record */ +static int add_pbaps_sdp(const bluetooth_sdp_pse_record* rec) +{ + tSDP_PROTOCOL_ELEM protoList [3]; + UINT16 service = UUID_SERVCLASS_PBAP_PSE; + UINT16 browse = UUID_SERVCLASS_PUBLIC_BROWSE_GROUP; + bool status = true; + UINT32 sdp_handle = 0; + UINT8 temp[4]; + UINT8* p_temp = temp; + + BTC_TRACE_DEBUG("%s(): scn 0x%02x, psm = 0x%04x\n service name %s", __func__, + rec->hdr.rfcomm_channel_number, rec->hdr.l2cap_psm, rec->hdr.service_name); + + BTC_TRACE_DEBUG(" supported_repositories: 0x%08x, feature_bits: 0x%08x", + rec->supported_repositories, rec->supported_features); + + if ((sdp_handle = SDP_CreateRecord()) == 0) { + BTC_TRACE_ERROR("%s(): Unable to register PBAP Server Service", __func__); + return sdp_handle; + } + + /* add service class */ + status &= SDP_AddServiceClassIdList(sdp_handle, 1, &service); + memset( protoList, 0 , 3*sizeof(tSDP_PROTOCOL_ELEM) ); + + /* add protocol list, including RFCOMM scn */ + protoList[0].protocol_uuid = UUID_PROTOCOL_L2CAP; + protoList[0].num_params = 0; + protoList[1].protocol_uuid = UUID_PROTOCOL_RFCOMM; + protoList[1].num_params = 1; + protoList[1].params[0] = rec->hdr.rfcomm_channel_number; + protoList[2].protocol_uuid = UUID_PROTOCOL_OBEX; + protoList[2].num_params = 0; + status &= SDP_AddProtocolList(sdp_handle, 3, protoList); + + /* Add a name entry */ + status &= SDP_AddAttribute(sdp_handle, + (UINT16)ATTR_ID_SERVICE_NAME, + (UINT8)TEXT_STR_DESC_TYPE, + (UINT32)(rec->hdr.service_name_length + 1), + (UINT8 *)rec->hdr.service_name); + + /* Add in the Bluetooth Profile Descriptor List */ + status &= SDP_AddProfileDescriptorList(sdp_handle, + UUID_SERVCLASS_PHONE_ACCESS, + rec->hdr.profile_version); + + /* Add supported repositories 1 byte */ + status &= SDP_AddAttribute(sdp_handle, ATTR_ID_SUPPORTED_REPOSITORIES, + UINT_DESC_TYPE, (UINT32)1, (UINT8*)&rec->supported_repositories); + + /* Add supported feature 4 bytes*/ + UINT32_TO_BE_STREAM(p_temp, rec->supported_features); + status &= SDP_AddAttribute(sdp_handle, ATTR_ID_PBAP_SUPPORTED_FEATURES, + UINT_DESC_TYPE, (UINT32)4, temp); + + /* Add the L2CAP PSM if present */ + if(rec->hdr.l2cap_psm != -1) { + p_temp = temp;// The macro modifies p_temp, hence rewind. + UINT16_TO_BE_STREAM(p_temp, rec->hdr.l2cap_psm); + status &= SDP_AddAttribute(sdp_handle, ATTR_ID_GOEP_L2CAP_PSM, + UINT_DESC_TYPE, (UINT32)2, temp); + } + + /* Make the service browseable */ + status &= SDP_AddUuidSequence (sdp_handle, ATTR_ID_BROWSE_GROUP_LIST, 1, &browse); + + if (!status) { + SDP_DeleteRecord(sdp_handle); + sdp_handle = 0; + BTC_TRACE_ERROR("%s() FAILED, status = %d", __func__, status); + } else { + bta_sys_add_uuid(service); /* UUID_SERVCLASS_MESSAGE_ACCESS */ + BTC_TRACE_DEBUG("%s(): SDP Registered (handle 0x%08x)", __func__, sdp_handle); + } + + return sdp_handle; +} + +/* Create a PBAP Client SDP record based on information stored in a bluetooth_sdp_pse_record */ +static int add_pbapc_sdp(const bluetooth_sdp_pce_record* rec) +{ + UINT16 service = UUID_SERVCLASS_PBAP_PCE; + UINT16 browse = UUID_SERVCLASS_PUBLIC_BROWSE_GROUP; + bool status = true; + UINT32 sdp_handle = 0; + + BTC_TRACE_DEBUG("%s(): scn 0x%02x, psm = 0x%04x\n service name %s", __func__, + rec->hdr.rfcomm_channel_number, rec->hdr.l2cap_psm, rec->hdr.service_name); + + if ((sdp_handle = SDP_CreateRecord()) == 0) { + BTC_TRACE_ERROR("%s(): Unable to register PBAP Client Service", __func__); + return sdp_handle; + } + + /* add service class */ + status &= SDP_AddServiceClassIdList(sdp_handle, 1, &service); + + /* Add a name entry */ + status &= SDP_AddAttribute(sdp_handle, + (UINT16)ATTR_ID_SERVICE_NAME, + (UINT8)TEXT_STR_DESC_TYPE, + (UINT32)(rec->hdr.service_name_length + 1), + (UINT8 *)rec->hdr.service_name); + + /* Add in the Bluetooth Profile Descriptor List */ + status &= SDP_AddProfileDescriptorList(sdp_handle, + UUID_SERVCLASS_PHONE_ACCESS, + rec->hdr.profile_version); + + /* Make the service browseable */ + status &= SDP_AddUuidSequence (sdp_handle, ATTR_ID_BROWSE_GROUP_LIST, 1, &browse); + + if (!status) { + SDP_DeleteRecord(sdp_handle); + sdp_handle = 0; + BTC_TRACE_ERROR("%s() FAILED, status = %d", __func__, status); + } else { + bta_sys_add_uuid(service); /* UUID_SERVCLASS_MESSAGE_ACCESS */ + BTC_TRACE_DEBUG("%s(): SDP Registered (handle 0x%08x)", __func__, sdp_handle); + } + + return sdp_handle; +} + + +/* Create a OPP Server SDP record based on information stored in a bluetooth_sdp_ops_record */ +static int add_opps_sdp(const bluetooth_sdp_ops_record* rec) +{ + tSDP_PROTOCOL_ELEM protoList [3]; + UINT16 service = UUID_SERVCLASS_OBEX_OBJECT_PUSH; + UINT16 browse = UUID_SERVCLASS_PUBLIC_BROWSE_GROUP; + UINT8 type_len[rec->supported_formats_list_len]; + UINT8 desc_type[rec->supported_formats_list_len]; + UINT8 *type_value[rec->supported_formats_list_len]; + bool status = true; + UINT32 sdp_handle = 0; + UINT8 temp[4]; + UINT8* p_temp = temp; + tBTA_UTL_COD cod; + int i,j; + + BTC_TRACE_DEBUG("%s(): scn 0x%02x, psm = 0x%04x\n service name %s", __func__, + rec->hdr.rfcomm_channel_number, rec->hdr.l2cap_psm, rec->hdr.service_name); + + BTC_TRACE_DEBUG(" supported formats count: %d", + rec->supported_formats_list_len); + + if ((sdp_handle = SDP_CreateRecord()) == 0) { + BTC_TRACE_ERROR("%s(): Unable to register Object Push Server Service", __func__); + return sdp_handle; + } + + /* add service class */ + status &= SDP_AddServiceClassIdList(sdp_handle, 1, &service); + memset( protoList, 0 , 3*sizeof(tSDP_PROTOCOL_ELEM) ); + + /* add protocol list, including RFCOMM scn */ + protoList[0].protocol_uuid = UUID_PROTOCOL_L2CAP; + protoList[0].num_params = 0; + protoList[1].protocol_uuid = UUID_PROTOCOL_RFCOMM; + protoList[1].num_params = 1; + protoList[1].params[0] = rec->hdr.rfcomm_channel_number; + protoList[2].protocol_uuid = UUID_PROTOCOL_OBEX; + protoList[2].num_params = 0; + status &= SDP_AddProtocolList(sdp_handle, 3, protoList); + + /* Add a name entry */ + status &= SDP_AddAttribute(sdp_handle, + (UINT16)ATTR_ID_SERVICE_NAME, + (UINT8)TEXT_STR_DESC_TYPE, + (UINT32)(rec->hdr.service_name_length + 1), + (UINT8 *)rec->hdr.service_name); + + /* Add in the Bluetooth Profile Descriptor List */ + status &= SDP_AddProfileDescriptorList(sdp_handle, + UUID_SERVCLASS_OBEX_OBJECT_PUSH, + rec->hdr.profile_version); + + /* add sequence for supported types */ + for (i = 0, j = 0; i < rec->supported_formats_list_len; i++) { + type_value[j] = (UINT8 *) &rec->supported_formats_list[i]; + desc_type[j] = UINT_DESC_TYPE; + type_len[j++] = 1; + } + + status &= SDP_AddSequence(sdp_handle, (UINT16) ATTR_ID_SUPPORTED_FORMATS_LIST, + (UINT8) rec->supported_formats_list_len, desc_type, type_len, type_value); + + /* Add the L2CAP PSM if present */ + if(rec->hdr.l2cap_psm != -1) { + p_temp = temp;// The macro modifies p_temp, hence rewind. + UINT16_TO_BE_STREAM(p_temp, rec->hdr.l2cap_psm); + status &= SDP_AddAttribute(sdp_handle, ATTR_ID_GOEP_L2CAP_PSM, + UINT_DESC_TYPE, (UINT32)2, temp); + } + + /* Make the service browseable */ + status &= SDP_AddUuidSequence (sdp_handle, ATTR_ID_BROWSE_GROUP_LIST, 1, &browse); + + if (!status) { + SDP_DeleteRecord(sdp_handle); + sdp_handle = 0; + BTC_TRACE_ERROR("%s() FAILED", __func__); + } else { + /* set class of device */ + cod.service = BTM_COD_SERVICE_OBJ_TRANSFER; + utl_set_device_class(&cod, BTA_UTL_SET_COD_SERVICE_CLASS); + + bta_sys_add_uuid(service); /* UUID_SERVCLASS_OBEX_OBJECT_PUSH */ + BTC_TRACE_DEBUG("%s(): SDP Registered (handle 0x%08x)", __func__, sdp_handle); + } + + return sdp_handle; +} + +// Create a Sim Access Profile SDP record based on information stored in a bluetooth_sdp_sap_record. +static int add_saps_sdp(const bluetooth_sdp_sap_record* rec) +{ + tSDP_PROTOCOL_ELEM protoList [2]; + UINT16 services[2]; + UINT16 browse = UUID_SERVCLASS_PUBLIC_BROWSE_GROUP; + bool status = true; + UINT32 sdp_handle = 0; + + BTC_TRACE_DEBUG("%s(): scn 0x%02x, service name %s", __func__, + rec->hdr.rfcomm_channel_number, rec->hdr.service_name); + + if ((sdp_handle = SDP_CreateRecord()) == 0) { + BTC_TRACE_ERROR("%s(): Unable to register SAPS Service", __func__); + return sdp_handle; + } + + services[0] = UUID_SERVCLASS_SAP; + services[1] = UUID_SERVCLASS_GENERIC_TELEPHONY; + + // add service class + status &= SDP_AddServiceClassIdList(sdp_handle, 2, services); + memset(protoList, 0, 2 * sizeof(tSDP_PROTOCOL_ELEM)); + + // add protocol list, including RFCOMM scn + protoList[0].protocol_uuid = UUID_PROTOCOL_L2CAP; + protoList[0].num_params = 0; + protoList[1].protocol_uuid = UUID_PROTOCOL_RFCOMM; + protoList[1].num_params = 1; + protoList[1].params[0] = rec->hdr.rfcomm_channel_number; + status &= SDP_AddProtocolList(sdp_handle, 2, protoList); + + // Add a name entry + status &= SDP_AddAttribute(sdp_handle, + (UINT16)ATTR_ID_SERVICE_NAME, + (UINT8)TEXT_STR_DESC_TYPE, + (UINT32)(rec->hdr.service_name_length + 1), + (UINT8 *)rec->hdr.service_name); + + // Add in the Bluetooth Profile Descriptor List + status &= SDP_AddProfileDescriptorList(sdp_handle, + UUID_SERVCLASS_SAP, + rec->hdr.profile_version); + + // Make the service browseable + status &= SDP_AddUuidSequence (sdp_handle, ATTR_ID_BROWSE_GROUP_LIST, 1, &browse); + + if (!status) { + SDP_DeleteRecord(sdp_handle); + sdp_handle = 0; + BTC_TRACE_ERROR("%s(): FAILED deleting record", __func__); + } else { + bta_sys_add_uuid(UUID_SERVCLASS_SAP); + BTC_TRACE_DEBUG("%s(): SDP Registered (handle 0x%08x)", __func__, sdp_handle); + } + + return sdp_handle; +} + +static int btc_handle_create_record_event(int id) +{ + int handle = -1; + const sdp_slot_t *sdp_slot = NULL; + + BTC_TRACE_DEBUG("Sdp Server %s", __func__); + + sdp_slot = start_create_sdp(id); + if(sdp_slot != NULL) { + bluetooth_sdp_record* record = sdp_slot->record_data; + switch(record->hdr.type) { + case SDP_TYPE_RAW: + handle = add_raw_sdp(record); + break; + case SDP_TYPE_MAP_MAS: + handle = add_maps_sdp(&record->mas); + break; + case SDP_TYPE_MAP_MNS: + handle = add_mapc_sdp(&record->mns); + break; + case SDP_TYPE_PBAP_PSE: + handle = add_pbaps_sdp(&record->pse); + break; + case SDP_TYPE_PBAP_PCE: + handle = add_pbapc_sdp(&record->pce); + break; + case SDP_TYPE_OPP_SERVER: + handle = add_opps_sdp(&record->ops); + break; + case SDP_TYPE_SAP_SERVER: + handle = add_saps_sdp(&record->sap); + break; + default: + BTC_TRACE_DEBUG("Record type %d is not supported",record->hdr.type); + break; + } + if(handle != -1) { + set_sdp_handle(id, handle); + } + } + + return handle; +} + +static bool btc_sdp_remove_record_event(int handle) +{ + bool result = false; + + BTC_TRACE_DEBUG("Sdp Server %s", __func__); + + if(handle != -1 && handle != 0) { + result = SDP_DeleteRecord(handle); + if(result == false) { + BTC_TRACE_ERROR(" Unable to remove handle 0x%08x", handle); + } + } + + return result; +} + +static void btc_sdp_dm_cback(tBTA_SDP_EVT event, tBTA_SDP* p_data, void* user_data) +{ + btc_msg_t msg; + bt_status_t status; + + switch (event) { + case BTA_SDP_CREATE_RECORD_USER_EVT: { + if (p_data->status == BTA_SDP_SUCCESS) { + p_data->sdp_create_record.handle = btc_handle_create_record_event((int)user_data); + if (p_data->sdp_create_record.handle < 0) { + p_data->status = BTA_SDP_FAILURE; + } + } + } + break; + case BTA_SDP_REMOVE_RECORD_USER_EVT: { + if (p_data->status == BTA_SDP_SUCCESS) { + if (btc_sdp_remove_record_event((int)user_data) == false) { + p_data->status = BTA_SDP_FAILURE; + } + } + } + break; + default: + break; + } + + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_SDP; + msg.act = event; + + status = btc_transfer_context(&msg, p_data, sizeof(tBTA_SDP), NULL, NULL); + + if (status != BT_STATUS_SUCCESS) { + BTC_TRACE_ERROR("%s btc_transfer_context failed", __func__); + } +} + +static void btc_sdp_init(void) +{ + esp_sdp_cb_param_t param; + esp_sdp_status_t ret = ESP_SDP_SUCCESS; + + do { + if (is_sdp_init()) { + BTC_TRACE_ERROR("%s SDP has been initiated, shall deinit first!", __func__); + ret = ESP_SDP_NEED_DEINIT; + break; + } + +#if SDP_DYNAMIC_MEMORY == TRUE + if ((sdp_local_param_ptr = (sdp_local_param_t *)osi_malloc(sizeof(sdp_local_param_t))) == NULL) { + BTC_TRACE_ERROR("%s malloc failed\n", __func__); + ret = ESP_SDP_NO_RESOURCE; + break; + } + memset((void *)sdp_local_param_ptr, 0, sizeof(sdp_local_param_t)); +#endif + + if (osi_mutex_new(&sdp_local_param.sdp_slot_mutex) != 0) { +#if SDP_DYNAMIC_MEMORY == TRUE + osi_free(sdp_local_param_ptr); + sdp_local_param_ptr = NULL; +#endif + BTC_TRACE_ERROR("%s osi_mutex_new failed\n", __func__); + ret = ESP_SDP_NO_RESOURCE; + break; + } + + ret = BTA_SdpEnable(btc_sdp_dm_cback); + } while(0); + + if (ret != ESP_SDP_SUCCESS) { + param.init.status = ret; + btc_sdp_cb_to_app(ESP_SDP_INIT_EVT, ¶m); + } +} + +static void btc_sdp_deinit(void) +{ + esp_sdp_cb_param_t param; + esp_sdp_status_t ret = ESP_SDP_SUCCESS; + int handle; + + do { + if (!is_sdp_init()) { + BTC_TRACE_ERROR("%s SDP has not been initiated, shall init first!", __func__); + ret = ESP_SDP_NEED_INIT; + break; + } + + for(int i = 0; i < SDP_MAX_RECORDS; i++) { + handle = free_sdp_slot(i); + if (handle > 0) { + BTA_SdpRemoveRecordByUser((void*)handle); + } + } + sdp_disable_handler(); + } while(0); + + if (ret != ESP_SDP_SUCCESS) { + param.deinit.status = ret; + btc_sdp_cb_to_app(ESP_SDP_DEINIT_EVT, ¶m); + } +} + +static void btc_sdp_create_record(btc_sdp_args_t *arg) +{ + int handle; + esp_sdp_cb_param_t param; + esp_sdp_status_t ret = ESP_SDP_SUCCESS; + + do { + if (!is_sdp_init()) { + BTC_TRACE_ERROR("%s SDP has not been initiated, shall init first!", __func__); + ret = ESP_SDP_NEED_INIT; + break; + } + + handle = alloc_sdp_slot(arg->creat_record.record); + if (handle < 0) { + ret = ESP_SDP_FAILURE; + break; + } + + BTA_SdpCreateRecordByUser((void *) handle); + } while(0); + + if (ret != ESP_SDP_SUCCESS) { + param.create_record.status = ret; + param.create_record.record_handle = -1; + btc_sdp_cb_to_app(ESP_SDP_CREATE_RECORD_COMP_EVT, ¶m); + } +} + +static void btc_sdp_remove_record(btc_sdp_args_t *arg) +{ + int handle; + esp_sdp_cb_param_t param; + esp_sdp_status_t ret = ESP_SDP_SUCCESS; + + do { + if (!is_sdp_init()) { + BTC_TRACE_ERROR("%s SDP has not been initiated, shall init first!", __func__); + ret = ESP_SDP_NEED_INIT; + break; + } + + bluetooth_sdp_record rec; + if (get_sdp_record_by_handle(arg->remove_record.record_handle, &rec)) { + if (rec.hdr.bt_uuid.len == ESP_UUID_LEN_16) { + bta_sys_remove_uuid(rec.hdr.bt_uuid.uuid.uuid16); + } else if (rec.hdr.bt_uuid.len == ESP_UUID_LEN_32) { + bta_sys_remove_uuid_32(rec.hdr.bt_uuid.uuid.uuid32); + } else if (rec.hdr.bt_uuid.len == ESP_UUID_LEN_128) { + bta_sys_remove_uuid_128((UINT8 *)&rec.hdr.bt_uuid.uuid.uuid128); + } + } else { + BTC_TRACE_ERROR("%s SDP record with handle %d not found", + __func__, arg->remove_record.record_handle); + return; + } + + /* Get the Record handle, and free the slot */ + /* The application layer record_handle is equivalent to the id of the btc layer */ + int slot = get_sdp_slot_id_by_handle(arg->remove_record.record_handle); + if (slot < 0) { + return; + } + + handle = free_sdp_slot(slot); + + BTC_TRACE_DEBUG("Sdp Server %s id=%d to handle=0x%08x", + __func__, arg->remove_record.record_handle, handle); + + /* Pass the actual record handle */ + if(handle > 0) { + BTA_SdpRemoveRecordByUser((void*) handle); + } else { + ret = ESP_SDP_NO_CREATE_RECORD; + break; + } + } while(0); + + if (ret != ESP_SDP_SUCCESS) { + param.create_record.status = ret; + btc_sdp_cb_to_app(ESP_SDP_REMOVE_RECORD_COMP_EVT, ¶m); + } +} + +static void btc_sdp_search(btc_sdp_args_t *arg) +{ + esp_sdp_cb_param_t param = {0}; + esp_sdp_status_t ret = ESP_SDP_SUCCESS; + + do { + if (!is_sdp_init()) { + BTC_TRACE_ERROR("%s SDP has not been initiated, shall init first!", __func__); + ret = ESP_SDP_NEED_INIT; + break; + } + + BTA_SdpSearch(arg->search.bd_addr, &arg->search.sdp_uuid); + } while(0); + + if (ret != ESP_SDP_SUCCESS) { + param.search.status = ret; + btc_sdp_cb_to_app(ESP_SDP_SEARCH_COMP_EVT, ¶m); + } +} + +void btc_sdp_arg_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src) +{ + btc_sdp_args_t *dst = (btc_sdp_args_t *)p_dest; + btc_sdp_args_t *src = (btc_sdp_args_t *)p_src; + + switch (msg->act) { + case BTC_SDP_ACT_CREATE_RECORD: + dst->creat_record.record = (bluetooth_sdp_record *)osi_calloc(sizeof(bluetooth_sdp_record)); + if (dst->creat_record.record) { + memcpy(dst->creat_record.record, src->creat_record.record, sizeof(bluetooth_sdp_record)); + } else { + BTC_TRACE_ERROR("%s %d osi_malloc failed\n", __func__, msg->act); + break; + } + + dst->creat_record.record->hdr.service_name = (char *)osi_calloc(src->creat_record.record->hdr.service_name_length); + if (dst->creat_record.record->hdr.service_name) { + strcpy(dst->creat_record.record->hdr.service_name, src->creat_record.record->hdr.service_name); + } else { + BTC_TRACE_ERROR("%s %d osi_malloc failed\n", __func__, msg->act); + } + break; + default: + break; + } +} + +void btc_sdp_arg_deep_free(btc_msg_t *msg) +{ + btc_sdp_args_t *arg = (btc_sdp_args_t *)msg->arg; + + switch (msg->act) { + case BTC_SDP_ACT_CREATE_RECORD: + if (arg->creat_record.record) { + osi_free(arg->creat_record.record); + } + if (arg->creat_record.record->hdr.service_name) { + osi_free(arg->creat_record.record->hdr.service_name); + } + break; + default: + break; + } +} + +void btc_sdp_call_handler(btc_msg_t *msg) +{ + btc_sdp_args_t *arg = (btc_sdp_args_t *)(msg->arg); + + BTC_TRACE_DEBUG("%s: event = %d\n", __func__, msg->act); + + switch (msg->act) { + case BTC_SDP_ACT_INIT: + btc_sdp_init(); + break; + case BTC_SDP_ACT_DEINIT: + btc_sdp_deinit(); + break; + case BTC_SDP_ACT_SEARCH: + btc_sdp_search(arg); + break; + case BTC_SDP_ACT_CREATE_RECORD: + btc_sdp_create_record(arg); + break; + case BTC_SDP_ACT_REMOVE_RECORD: + btc_sdp_remove_record(arg); + break; + default: + BTC_TRACE_ERROR("%s: Unhandled event (%d)!\n", __func__, msg->act); + break; + } + btc_sdp_arg_deep_free(msg); +} + +void btc_sdp_cb_handler(btc_msg_t *msg) +{ + esp_sdp_cb_param_t param = {0}; + uint8_t event = msg->act; + tBTA_SDP *p_data = (tBTA_SDP *)msg->arg; + + switch(event) { + case BTA_SDP_ENABLE_EVT: + param.init.status = p_data->status; + btc_sdp_cb_to_app(ESP_SDP_INIT_EVT, ¶m); + break; + case BTA_SDP_DISENABLE_EVT: + BTA_SdpDisable(); + osi_mutex_free(&sdp_local_param.sdp_slot_mutex); + #if SDP_DYNAMIC_MEMORY == TRUE + osi_free(sdp_local_param_ptr); + sdp_local_param_ptr = NULL; + #endif + param.deinit.status = ESP_SDP_SUCCESS; + btc_sdp_cb_to_app(ESP_SDP_DEINIT_EVT, ¶m); + break; + case BTA_SDP_SEARCH_COMP_EVT: + param.search.status = p_data->sdp_search_comp.status; + if (param.search.status == ESP_SDP_SUCCESS) { + memcpy(param.search.remote_addr, p_data->sdp_search_comp.remote_addr, sizeof(BD_ADDR)); + memcpy(¶m.search.sdp_uuid, &p_data->sdp_search_comp.uuid, sizeof(tSDP_UUID)); + param.search.record_count = p_data->sdp_search_comp.record_count; + param.search.records = osi_malloc(sizeof(esp_bluetooth_sdp_record_t)*p_data->sdp_search_comp.record_count); + if (param.search.records != NULL) { + memcpy(param.search.records, p_data->sdp_search_comp.records, + sizeof(esp_bluetooth_sdp_record_t)*p_data->sdp_search_comp.record_count); + } else { + BTC_TRACE_ERROR("%s %d osi_malloc failed\n", __func__, event); + param.search.status = ESP_SDP_NO_RESOURCE; + } + } + btc_sdp_cb_to_app(ESP_SDP_SEARCH_COMP_EVT, ¶m); + if (param.search.records != NULL) { + osi_free(param.search.records); + } + break; + case BTA_SDP_CREATE_RECORD_USER_EVT: + param.create_record.status = p_data->sdp_create_record.status; + param.create_record.record_handle = p_data->sdp_create_record.handle; + btc_sdp_cb_to_app(ESP_SDP_CREATE_RECORD_COMP_EVT, ¶m); + break; + case BTA_SDP_REMOVE_RECORD_USER_EVT: + param.remove_record.status = p_data->status; + btc_sdp_cb_to_app(ESP_SDP_REMOVE_RECORD_COMP_EVT, ¶m); + break; + default: + BTC_TRACE_DEBUG("%s: Unhandled event (%d)!", __func__, msg->act); + break; + } +} + +#endif ///defined BTC_SDP_INCLUDED && BTC_SDP_INCLUDED == TRUE diff --git a/lib/bt/host/bluedroid/btc/profile/std/smp/esp_app_sec.c b/lib/bt/host/bluedroid/btc/profile/std/smp/esp_app_sec.c new file mode 100644 index 00000000..ccdb2ef6 --- /dev/null +++ b/lib/bt/host/bluedroid/btc/profile/std/smp/esp_app_sec.c @@ -0,0 +1,93 @@ +/* + * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include // standard library +#include + +#include "esp_sec_api.h" + + +extern void srand (unsigned int seed); +extern int random (void); + +/// Application Security Environment Structure +tAPP_SEC_ENV app_sec_env; + + +/******************************************************************************* +** +** Function app_ble_sec_gen_tk +** +** Description This function is called to generate the ble tk +** +** Returns the generate tk value +** +*******************************************************************************/ +UINT32 app_ble_sec_gen_tk(void) +{ + // Generate a PIN Code (Between 100000 and 999999) + return (100000 + (random() % 900000)); +} + +/******************************************************************************* +** +** Function app_ble_sec_gen_ltk +** +** Description This function is called to generate the ble ltk +** +** Returns NULL +** +*******************************************************************************/ +void app_ble_sec_gen_ltk(UINT8 key_size) +{ + // Counter + UINT8 i; + app_sec_env.key_size = key_size; + + // Randomly generate the LTK and the Random Number + for (i = 0; i < RAND_NB_LEN; i++) { + app_sec_env.rand_nb.nb[i] = random() % 256; + } + + // Randomly generate the end of the LTK + for (i = 0; i < SEC_KEY_LEN; i++) { + app_sec_env.ltk.key[i] = (((key_size) < (16 - i)) ? 0 : random() % 256); + } + + // Randomly generate the EDIV + app_sec_env.ediv = random() % 65536; +} + + +/******************************************************************************* +** +** Function app_ble_sec_init +** +** Description This function is init the security env and function +** +** Returns NULL +** +*******************************************************************************/ +void app_ble_sec_init(void) +{ + // Reset Security Environment + memset(&app_sec_env, 0, sizeof(app_sec_env)); +} + + +/******************************************************************************* +** +** Function app_ble_security_start +** +** Description This function is called by the slave when the seurity start +** +** Returns NULL +** +*******************************************************************************/ +void app_ble_security_start(void) +{ + +} diff --git a/lib/bt/host/bluedroid/btc/profile/std/smp/include/esp_sec_api.h b/lib/bt/host/bluedroid/btc/profile/std/smp/include/esp_sec_api.h new file mode 100644 index 00000000..e4477467 --- /dev/null +++ b/lib/bt/host/bluedroid/btc/profile/std/smp/include/esp_sec_api.h @@ -0,0 +1,71 @@ +/* + * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __ESP_SEC_API_H__ +#define __ESP_SEC_API_H__ + +#include "stack/bt_types.h" + +#define APP_SEC_IRK_FLAG (0) +#define RAND_NB_LEN 0x08 +#define SEC_KEY_LEN 0x10 + +/* + * STRUCTURES DEFINITIONS + **************************************************************************************** + */ + + +/// Generic Security key structure +typedef struct { + /// Key value MSB -> LSB + UINT8 key[SEC_KEY_LEN]; +} smp_sec_key; + +///Random number structure +typedef struct { + ///8-byte array for random number + UINT8 nb[RAND_NB_LEN]; +} rand_nb; + +typedef struct { + // LTK + smp_sec_key ltk; + // Random Number + rand_nb rand_nb; + // EDIV + UINT16 ediv; + // LTK key size + UINT8 key_size; + + // Last paired peer address type + UINT8 peer_addr_type; + // Last paired peer address + BD_ADDR peer_addr; + + // authentication level + UINT8 auth; + +} tAPP_SEC_ENV; + +extern tAPP_SEC_ENV app_sec_env; + +/* +* GLOBAL FUNCTIONS DECLARATIONS +**************************************************************************************** +*/ + +void app_ble_sec_init(void); + +void app_ble_sec_pairing_cmp_evt_send(UINT8); + +UINT32 app_ble_sec_gen_tk(void); + +void app_ble_sec_gen_ltk(UINT8 key_size); + +void app_ble_security_start(void); + +#endif /* __ESP_SEC_API_H__ */ diff --git a/lib/bt/host/bluedroid/btc/profile/std/spp/btc_spp.c b/lib/bt/host/bluedroid/btc/profile/std/spp/btc_spp.c new file mode 100644 index 00000000..cf334494 --- /dev/null +++ b/lib/bt/host/bluedroid/btc/profile/std/spp/btc_spp.c @@ -0,0 +1,1690 @@ +/* + * SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include "btc_spp.h" +#include "btc/btc_manage.h" +#include "btc/btc_task.h" +#include "bta/bta_jv_api.h" +#include "common/bt_trace.h" +#include "osi/allocator.h" +#include "esp_spp_api.h" +#include "osi/list.h" +#include "freertos/ringbuf.h" +#include "osi/mutex.h" +#include "osi/alarm.h" +#include +#include +#include +#include "esp_vfs.h" +#include "esp_vfs_dev.h" +#include "stack/port_api.h" +#include "freertos/FreeRTOS.h" +#include "freertos/event_groups.h" + +#include "btc/btc_task.h" +#include "stack/btu.h" + +#if (defined BTC_SPP_INCLUDED && BTC_SPP_INCLUDED == TRUE) + +#define SLOT_WRITE_BIT(i) (1UL << (i - 1)) +#define SLOT_CLOSE_BIT(i) (1UL << (i + MAX_RFC_PORTS - 1)) +#define VFS_WRITE_TIMEOUT (40 * 1000) +#define SLOT_TX_QUEUE_SIZE 10 +#define SLOT_TX_QUEUE_LOW_WM 4 +#define SLOT_TX_DATA_HIGH_WM (SLOT_TX_QUEUE_SIZE * BTA_JV_DEF_RFC_MTU) +#define VFS_CLOSE_TIMEOUT (20 * 1000) + +typedef struct { + bool peer_fc; /* true if flow control is set based on peer's request */ + bool user_fc; /* true if flow control is set based on user's request */ + fixed_queue_t *queue; /* Queue of buffers waiting to be sent */ + uint32_t data_size; /* Number of data bytes in the queue */ +} slot_data_t; + +typedef struct { + bool connected; + bool is_server; + bool is_writing; + uint8_t serial; + uint8_t scn; + uint8_t max_session; + uint16_t mtu; + uint16_t credit_rx; + uint16_t write_data_len; + uint32_t id; + uint32_t sdp_handle; + uint32_t rfc_handle; + uint32_t rfc_port_handle; + int fd; + uint8_t *write_data; + osi_alarm_t *close_alarm; + void *alarm_arg; + esp_spp_role_t role; + esp_spp_sec_t security; + esp_bd_addr_t addr; + slot_data_t rx; + union { + slot_data_t tx; + RingbufHandle_t ringbuf_write; + }; + uint8_t service_uuid[16]; + char service_name[ESP_SPP_SERVER_NAME_MAX + 1]; +} spp_slot_t; + +typedef struct { + uint16_t tx_buffer_size; + spp_slot_t *spp_slots[MAX_RFC_PORTS + 1]; + uint32_t spp_slot_id; + esp_spp_mode_t spp_mode; + osi_mutex_t spp_slot_mutex; + EventGroupHandle_t tx_event_group; + esp_vfs_id_t spp_vfs_id; +} spp_local_param_t; + +#if SPP_DYNAMIC_MEMORY == FALSE +static spp_local_param_t spp_local_param; +#else +static spp_local_param_t *spp_local_param_ptr; +#define spp_local_param (*spp_local_param_ptr) +#endif + +static void btc_spp_vfs_register(void); +static void btc_spp_vfs_unregister(void); + +static void spp_osi_free(void *p) +{ + osi_free(p); +} + +#if SPP_DYNAMIC_MEMORY == FALSE +#define is_spp_init() (spp_local_param.spp_slot_mutex != NULL) +#else +#define is_spp_init() (&spp_local_param != NULL && spp_local_param.spp_slot_mutex != NULL) +#endif + +static int init_slot_data(slot_data_t *slot_data, size_t queue_size) +{ + memset(slot_data, 0, sizeof(slot_data_t)); + if ((slot_data->queue = fixed_queue_new(queue_size)) == NULL) { + return -1; + } + slot_data->data_size = 0; + return 0; +} + +static void free_slot_data(slot_data_t *slot_data) +{ + fixed_queue_free(slot_data->queue, spp_osi_free); + slot_data->queue = NULL; +} + +static spp_slot_t *spp_malloc_slot(void) +{ + uint8_t err_no = 0; + spp_slot_t **slot = NULL; + if (++spp_local_param.spp_slot_id == 0) { + spp_local_param.spp_slot_id = 1; + } + for (size_t i = 1; i <= MAX_RFC_PORTS; i++) { + slot = &spp_local_param.spp_slots[i]; + if ((*slot) == NULL) { + if (((*slot) = (spp_slot_t *)osi_malloc(sizeof(spp_slot_t))) == NULL) { + return NULL; + } + (*slot)->id = spp_local_param.spp_slot_id; + (*slot)->serial = i; + (*slot)->sdp_handle = 0; + (*slot)->rfc_handle = 0; + (*slot)->rfc_port_handle = 0; + (*slot)->fd = -1; + (*slot)->connected = false; + (*slot)->is_server = false; + (*slot)->mtu = 0; + (*slot)->credit_rx = BTA_JV_MAX_CREDIT_NUM; + (*slot)->write_data = NULL; + (*slot)->write_data_len = 0; + (*slot)->is_writing = false; + (*slot)->close_alarm = NULL; + (*slot)->alarm_arg = NULL; + /* clear the old event bits */ + if (spp_local_param.tx_event_group) { + xEventGroupClearBits(spp_local_param.tx_event_group, SLOT_WRITE_BIT(i) | SLOT_CLOSE_BIT(i)); + } + + if (init_slot_data(&(*slot)->rx, QUEUE_SIZE_MAX)) { + BTC_TRACE_ERROR("%s unable to malloc rx queue!", __func__); + err_no = 1; + goto err; + } + if (spp_local_param.spp_mode == ESP_SPP_MODE_CB) { + if (init_slot_data(&(*slot)->tx, SLOT_TX_QUEUE_SIZE)) { + BTC_TRACE_ERROR("%s unable to malloc tx queue!", __func__); + err_no = 2; + goto err; + } + } else { + if (((*slot)->ringbuf_write = xRingbufferCreate(spp_local_param.tx_buffer_size, RINGBUF_TYPE_BYTEBUF)) == NULL) { + BTC_TRACE_ERROR("%s write ringbuffer create error!", __func__); + err_no = 2; + goto err; + } + if (esp_vfs_register_fd(spp_local_param.spp_vfs_id, &(*slot)->fd) != ESP_OK) { + BTC_TRACE_ERROR("%s unable to register fd!", __func__); + err_no = 3; + goto err; + } + } + return (*slot); + } + } + + return NULL; +err: + switch (err_no) { + case 3: + if (spp_local_param.spp_mode == ESP_SPP_MODE_VFS) { + vRingbufferDelete((*slot)->ringbuf_write); + } + case 2: + free_slot_data(&(*slot)->rx); + case 1: + osi_free((*slot)); + (*slot) = NULL; + break; + default: + break; + } + return (*slot); +} + +static spp_slot_t *spp_find_slot_by_id(uint32_t id) +{ + spp_slot_t *slot = NULL; + for (size_t i = 1; i <= MAX_RFC_PORTS; i++) { + slot = spp_local_param.spp_slots[i]; + if (slot != NULL && slot->id == id) { + return slot; + } + } + return NULL; +} + +static spp_slot_t *spp_find_slot_by_handle(uint32_t handle) +{ + spp_slot_t *slot = NULL; + for (size_t i = 1; i <= MAX_RFC_PORTS; i++) { + slot = spp_local_param.spp_slots[i]; + if (slot != NULL && slot->rfc_handle == handle) { + return slot; + } + } + return NULL; +} + +static spp_slot_t *spp_find_slot_by_fd(int fd) +{ + spp_slot_t *slot = NULL; + for (size_t i = 1; i <= MAX_RFC_PORTS; i++) { + slot = spp_local_param.spp_slots[i]; + if (slot != NULL && slot->fd == fd) { + return slot; + } + } + return NULL; +} + +static spp_slot_t *spp_find_slot_by_scn(uint32_t scn) +{ + spp_slot_t *slot = NULL; + for (size_t i = 1; i <= MAX_RFC_PORTS; i++) { + slot = spp_local_param.spp_slots[i]; + if (slot != NULL && slot->is_server && slot->scn == (uint8_t)scn) { + return slot; + } + } + return NULL; +} + +static void close_timeout_handler(void *arg) +{ + btc_msg_t msg; + bt_status_t status; + spp_slot_t *slot = (spp_slot_t *)arg; + + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_SPP; + msg.act = BTA_JV_RFCOMM_CLOSE_EVT; + + status = btc_transfer_context(&msg, slot->alarm_arg, sizeof(tBTA_JV), NULL, NULL); + + if (slot->alarm_arg) { + osi_free(slot->alarm_arg); + slot->alarm_arg = NULL; + } + + if (status != BT_STATUS_SUCCESS) { + BTC_TRACE_ERROR("%s btc_transfer_context failed", __func__); + } +} + +static void spp_free_slot(spp_slot_t *slot) +{ + if (!slot) { + return; + } + spp_local_param.spp_slots[slot->serial] = NULL; + if (spp_local_param.spp_mode == ESP_SPP_MODE_VFS) { + (void) esp_vfs_unregister_fd(spp_local_param.spp_vfs_id, slot->fd); + xEventGroupSetBits(spp_local_param.tx_event_group, SLOT_CLOSE_BIT(slot->serial)); + vRingbufferDelete(slot->ringbuf_write); + } else { + free_slot_data(&slot->tx); + } + free_slot_data(&slot->rx); + if (slot->close_alarm) { + osi_alarm_free(slot->close_alarm); + if (slot->alarm_arg) { + osi_free(slot->alarm_arg); + slot->alarm_arg = NULL; + } + } + osi_free(slot); +} + +static void spp_free_pending_slots(void) +{ + spp_slot_t *slot = NULL; + for (size_t i = 1; i <= MAX_RFC_PORTS; i++) { + slot = spp_local_param.spp_slots[i]; + if (slot) { + BTC_TRACE_WARNING("%s found slot(rfc_handle=0x%x) pending to close, close it now!", __func__, slot->rfc_handle); + spp_free_slot(slot); + } + } +} + +static inline void btc_spp_cb_to_app(esp_spp_cb_event_t event, esp_spp_cb_param_t *param) +{ + esp_spp_cb_t btc_spp_cb = (esp_spp_cb_t)btc_profile_cb_get(BTC_PID_SPP); + if (btc_spp_cb) { + btc_spp_cb(event, param); + } +} + +static void btc_create_server_fail_cb(void) +{ + esp_spp_cb_param_t param; + param.start.status = ESP_SPP_FAILURE; + param.start.handle = 0; + param.start.sec_id = 0; + param.start.scn = BTC_SPP_INVALID_SCN; + param.start.use_co = FALSE; + btc_spp_cb_to_app(ESP_SPP_START_EVT, ¶m); +} + +static void *btc_spp_rfcomm_inter_cb(tBTA_JV_EVT event, tBTA_JV *p_data, void *user_data) +{ + bt_status_t status; + btc_msg_t msg; + void *new_user_data = NULL; + uint32_t id = (uintptr_t)user_data, id_temp = 0; + spp_slot_t *slot = NULL, *slot_new = NULL; + + if (!is_spp_init()) { + BTC_TRACE_WARNING("%s SPP have been deinit, incoming events ignore!\n", __func__); + return new_user_data; + } + osi_mutex_lock(&spp_local_param.spp_slot_mutex, OSI_MUTEX_MAX_TIMEOUT); + switch (event) { + case BTA_JV_RFCOMM_START_EVT: + slot = spp_find_slot_by_id(id); + if (!slot) { + BTC_TRACE_ERROR("%s unable to find RFCOMM slot, event:%d!", __func__, event); + p_data->rfc_start.status = ESP_SPP_NO_CONNECTION; + break; + } + slot->rfc_handle = p_data->rfc_start.handle; + slot->rfc_port_handle = BTA_JvRfcommGetPortHdl(p_data->rfc_start.handle); + break; + case BTA_JV_RFCOMM_SRV_OPEN_EVT: + slot = p_data->rfc_srv_open.handle ? spp_find_slot_by_id(id) : spp_find_slot_by_scn((uint32_t)user_data); + if (!slot) { + BTC_TRACE_ERROR("%s unable to find RFCOMM slot, event:%d!", __func__, event); + p_data->rfc_srv_open.status = ESP_SPP_NO_CONNECTION; + break; + } + + if (!p_data->rfc_srv_open.handle) { + /* match with the exist server solt */ + slot->rfc_handle = p_data->rfc_srv_open.new_listen_handle; + slot->rfc_port_handle = BTA_JvRfcommGetPortHdl(slot->rfc_handle); + } else { + slot_new = spp_malloc_slot(); + if (!slot_new) { + BTC_TRACE_ERROR("%s unable to malloc RFCOMM slot!", __func__); + p_data->rfc_srv_open.status = ESP_SPP_NO_RESOURCE; + break; + } + slot_new->connected = true; + slot_new->security = slot->security; + slot_new->role = slot->role; + slot_new->scn = slot->scn; + slot_new->max_session = slot->max_session; + strcpy(slot_new->service_name, slot->service_name); + slot_new->sdp_handle = slot->sdp_handle; + slot_new->mtu = p_data->rfc_srv_open.peer_mtu; + slot_new->rfc_handle = p_data->rfc_srv_open.handle; + slot_new->rfc_port_handle = BTA_JvRfcommGetPortHdl(slot_new->rfc_handle); + BTA_JvSetPmProfile(p_data->rfc_srv_open.handle, BTA_JV_PM_ALL, BTA_JV_CONN_OPEN); + + if (p_data->rfc_srv_open.new_listen_handle) { + slot->rfc_handle = p_data->rfc_srv_open.new_listen_handle; + slot->rfc_port_handle = BTA_JvRfcommGetPortHdl(slot->rfc_handle); + } else { + /* means lower layer can alloc port */ + slot->rfc_handle = 0; + slot->rfc_port_handle = 0; + } + /* swap slot id */ + id_temp = slot->id; + slot->id = slot_new->id; + slot_new->id = id_temp; + } + new_user_data = (void *)(uintptr_t)slot->id; + break; + case BTA_JV_RFCOMM_CL_INIT_EVT: + slot = spp_find_slot_by_id(id); + if (!slot) { + BTC_TRACE_ERROR("%s unable to find RFCOMM slot, event:%d!", __func__, event); + p_data->rfc_cl_init.status = ESP_SPP_FAILURE; + break; + } + + if (p_data->rfc_cl_init.status == BTA_JV_SUCCESS) { + slot->rfc_handle = p_data->rfc_cl_init.handle; + } else { + spp_free_slot(slot); + } + break; + case BTA_JV_RFCOMM_OPEN_EVT: + slot = spp_find_slot_by_id(id); + if (!slot) { + BTC_TRACE_ERROR("%s unable to find RFCOMM slot, event:%d!", __func__, event); + p_data->rfc_open.status = ESP_SPP_NO_CONNECTION; + break; + } + slot->connected = true; + slot->rfc_handle = p_data->rfc_open.handle; + slot->mtu = p_data->rfc_open.peer_mtu; + slot->rfc_port_handle = BTA_JvRfcommGetPortHdl(p_data->rfc_open.handle); + BTA_JvSetPmProfile(p_data->rfc_open.handle, BTA_JV_PM_ID_1, BTA_JV_CONN_OPEN); + break; + case BTA_JV_RFCOMM_DATA_IND_EVT: + break; + case BTA_JV_FREE_SCN_EVT: + if (user_data) { + id = ((tBTA_JV_FREE_SCN_USER_DATA *)user_data)->slot_id; + slot = spp_find_slot_by_id(id); + if (slot) { + spp_free_slot(slot); + } else { + BTC_TRACE_ERROR("%s unable to find RFCOMM slot, event:%d!", __func__, event); + p_data->free_scn.status = ESP_SPP_NO_CONNECTION; + } + osi_free(user_data); + } + break; + default: + break; + } + osi_mutex_unlock(&spp_local_param.spp_slot_mutex); + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_SPP; + msg.act = event; + + + status = btc_transfer_context(&msg, p_data, + sizeof(tBTA_JV), NULL, NULL); + + if (status != BT_STATUS_SUCCESS) { + BTC_TRACE_ERROR("%s btc_transfer_context failed", __func__); + } + return new_user_data; +} + +static void btc_spp_dm_inter_cb(tBTA_JV_EVT event, tBTA_JV *p_data, void *user_data) +{ + bt_status_t status; + btc_msg_t msg; + + uint32_t id = (uintptr_t)user_data; + spp_slot_t *slot = NULL; + switch (event) { + case BTA_JV_GET_SCN_EVT: + osi_mutex_lock(&spp_local_param.spp_slot_mutex, OSI_MUTEX_MAX_TIMEOUT); + slot = spp_find_slot_by_id(id); + if (!slot) { + osi_mutex_unlock(&spp_local_param.spp_slot_mutex); + BTC_TRACE_ERROR("%s unable to find RFCOMM slot, event:%d!", __func__, event); + break; + } + if (p_data->scn == 0) { + btc_create_server_fail_cb(); + spp_free_slot(slot); + osi_mutex_unlock(&spp_local_param.spp_slot_mutex); + BTC_TRACE_ERROR("%s unable to get scn, start server fail!", __func__); + break; + } + + slot->scn = p_data->scn; + BTA_JvCreateRecordByUser(slot->service_name, slot->scn, (void *)slot->id); + osi_mutex_unlock(&spp_local_param.spp_slot_mutex); + break; + case BTA_JV_CREATE_RECORD_EVT: + osi_mutex_lock(&spp_local_param.spp_slot_mutex, OSI_MUTEX_MAX_TIMEOUT); + slot = spp_find_slot_by_id(id); + if (!slot) { + osi_mutex_unlock(&spp_local_param.spp_slot_mutex); + BTC_TRACE_ERROR("%s unable to find RFCOMM slot, event:%d!", __func__, event); + break; + } + if (p_data->create_rec.status == BTA_JV_SUCCESS) { + slot->sdp_handle = p_data->create_rec.handle; + BTA_JvRfcommStartServer(slot->security, slot->role, slot->scn, + slot->max_session, (tBTA_JV_RFCOMM_CBACK *)btc_spp_rfcomm_inter_cb, (void *)slot->id); + } else { + BTC_TRACE_ERROR("%s unable to create record, start server fail!", __func__); + btc_create_server_fail_cb(); + tBTA_JV_FREE_SCN_USER_DATA *user_data = osi_malloc(sizeof(tBTA_JV_FREE_SCN_USER_DATA)); + if (user_data) { + user_data->server_status = BTA_JV_SERVER_START_FAILED; + user_data->slot_id = slot->id; + } else { + BTC_TRACE_ERROR("%s unable to malloc user data!", __func__); + assert(0); + } + BTA_JvFreeChannel(slot->scn, BTA_JV_CONN_TYPE_RFCOMM, + (tBTA_JV_RFCOMM_CBACK *)btc_spp_rfcomm_inter_cb, (void *)user_data); + } + osi_mutex_unlock(&spp_local_param.spp_slot_mutex); + break; + default: + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_SPP; + msg.act = event; + + status = btc_transfer_context(&msg, p_data, sizeof(tBTA_JV), NULL, NULL); + + if (status != BT_STATUS_SUCCESS) { + BTC_TRACE_ERROR("%s btc_transfer_context failed\n", __func__); + } + break; + } +} + +static void btc_spp_init(btc_spp_args_t *arg) +{ + esp_spp_status_t ret = ESP_SPP_SUCCESS; + do { + if (is_spp_init()) { + BTC_TRACE_ERROR("%s SPP has been initiated, shall uninit first!", __func__); + ret = ESP_SPP_NEED_DEINIT; + break; + } + +#if SPP_DYNAMIC_MEMORY == TRUE + if ((spp_local_param_ptr = (spp_local_param_t *)osi_malloc(sizeof(spp_local_param_t))) == NULL) { + BTC_TRACE_ERROR("%s malloc failed\n", __func__); + ret = ESP_SPP_NO_RESOURCE; + break; + } + memset((void *)spp_local_param_ptr, 0, sizeof(spp_local_param_t)); +#endif + + if (osi_mutex_new(&spp_local_param.spp_slot_mutex) != 0) { + BTC_TRACE_ERROR("%s osi_mutex_new failed\n", __func__); +#if SPP_DYNAMIC_MEMORY == TRUE + osi_free(spp_local_param_ptr); + spp_local_param_ptr = NULL; +#endif + ret = ESP_SPP_NO_RESOURCE; + break; + } + if ((spp_local_param.tx_event_group = xEventGroupCreate()) == NULL) { + BTC_TRACE_ERROR("%s create tx_event_group failed\n", __func__); + osi_mutex_free(&spp_local_param.spp_slot_mutex); +#if SPP_DYNAMIC_MEMORY == TRUE + osi_free(spp_local_param_ptr); + spp_local_param_ptr = NULL; +#endif + ret = ESP_SPP_NO_RESOURCE; + break; + } + if (arg->init.mode == ESP_SPP_MODE_VFS) { + spp_local_param.spp_vfs_id = -1; + } + spp_local_param.spp_mode = arg->init.mode; + spp_local_param.spp_slot_id = 0; + spp_local_param.tx_buffer_size = arg->init.tx_buffer_size; + BTA_JvEnable((tBTA_JV_DM_CBACK *)btc_spp_dm_inter_cb); + BTA_JvRfcommConfig(arg->init.enable_l2cap_ertm); + } while (0); + + if (ret != ESP_SPP_SUCCESS) { + esp_spp_cb_param_t param; + param.init.status = ret; + btc_spp_cb_to_app(ESP_SPP_INIT_EVT, ¶m); + } +} + +static void btc_spp_uninit(void) +{ + esp_spp_status_t ret = ESP_SPP_SUCCESS; + do { + if (!is_spp_init()) { + BTC_TRACE_ERROR("%s SPP has not been initiated, shall init first!", __func__); + ret = ESP_SPP_NEED_INIT; + break; + } + osi_mutex_lock(&spp_local_param.spp_slot_mutex, OSI_MUTEX_MAX_TIMEOUT); + // first, remove all connection + for (size_t i = 1; i <= MAX_RFC_PORTS; i++) { + if (spp_local_param.spp_slots[i] != NULL && !spp_local_param.spp_slots[i]->is_server) { + BTA_JvRfcommClose(spp_local_param.spp_slots[i]->rfc_handle, (void *)spp_local_param.spp_slots[i]->id); + } + } + // second, remove all server + for (size_t i = 1; i <= MAX_RFC_PORTS; i++) { + if (spp_local_param.spp_slots[i] != NULL && spp_local_param.spp_slots[i]->is_server) { + if (spp_local_param.spp_slots[i]->sdp_handle > 0) { + BTA_JvDeleteRecord(spp_local_param.spp_slots[i]->sdp_handle); + } + + if (spp_local_param.spp_slots[i]->rfc_handle > 0) { + BTA_JvRfcommStopServer(spp_local_param.spp_slots[i]->rfc_handle, + (void *)spp_local_param.spp_slots[i]->id); + } + + tBTA_JV_FREE_SCN_USER_DATA *user_data = osi_malloc(sizeof(tBTA_JV_FREE_SCN_USER_DATA)); + if (user_data) { + user_data->server_status = BTA_JV_SERVER_RUNNING; + user_data->slot_id = spp_local_param.spp_slots[i]->id; + } else { + BTC_TRACE_ERROR("%s unable to malloc user data!", __func__); + assert(0); + } + BTA_JvFreeChannel(spp_local_param.spp_slots[i]->scn, BTA_JV_CONN_TYPE_RFCOMM, + (tBTA_JV_RFCOMM_CBACK *)btc_spp_rfcomm_inter_cb, (void *)user_data); + } + } + BTA_JvDisable((tBTA_JV_RFCOMM_CBACK *)btc_spp_rfcomm_inter_cb); + osi_mutex_unlock(&spp_local_param.spp_slot_mutex); + } while(0); + + if (ret != ESP_SPP_SUCCESS) { + esp_spp_cb_param_t param; + param.uninit.status = ret; + btc_spp_cb_to_app(ESP_SPP_UNINIT_EVT, ¶m); + } +} + +static void btc_spp_start_discovery(btc_spp_args_t *arg) +{ + esp_spp_status_t ret = ESP_SPP_SUCCESS; + do { + if (!is_spp_init()) { + BTC_TRACE_ERROR("%s SPP have not been init\n", __func__); + ret = ESP_SPP_NEED_INIT; + break; + } + BTA_JvStartDiscovery(arg->start_discovery.bd_addr, arg->start_discovery.num_uuid, arg->start_discovery.p_uuid_list, NULL); + } while (0); + + if (ret != ESP_SPP_SUCCESS) { + esp_spp_cb_param_t param; + param.disc_comp.status = ret; + param.disc_comp.scn_num = 0xff; + memset(param.disc_comp.scn, 0xff, ESP_SPP_MAX_SCN); + btc_spp_cb_to_app(ESP_SPP_DISCOVERY_COMP_EVT, ¶m); + } +} + +static void btc_spp_connect(btc_spp_args_t *arg) +{ + esp_spp_status_t ret = ESP_SPP_SUCCESS; + do { + if (!is_spp_init()) { + BTC_TRACE_ERROR("%s SPP have not been init\n", __func__); + ret = ESP_SPP_NEED_INIT; + break; + } + osi_mutex_lock(&spp_local_param.spp_slot_mutex, OSI_MUTEX_MAX_TIMEOUT); + spp_slot_t *slot = spp_malloc_slot(); + if (!slot) { + BTC_TRACE_ERROR("%s unable to malloc RFCOMM slot!", __func__); + osi_mutex_unlock(&spp_local_param.spp_slot_mutex); + ret = ESP_SPP_NO_RESOURCE; + break; + } + slot->security = arg->connect.sec_mask; + slot->role = arg->connect.role; + slot->scn = arg->connect.remote_scn; + + memcpy(slot->addr, arg->connect.peer_bd_addr, ESP_BD_ADDR_LEN); + BTA_JvRfcommConnect(arg->connect.sec_mask, arg->connect.role, arg->connect.remote_scn, + arg->connect.peer_bd_addr, (tBTA_JV_RFCOMM_CBACK *)btc_spp_rfcomm_inter_cb, (void *)slot->id); + osi_mutex_unlock(&spp_local_param.spp_slot_mutex); + } while (0); + + if (ret != ESP_SPP_SUCCESS) { + esp_spp_cb_param_t param; + param.open.status = ret; + param.open.handle = 0; + param.open.fd = -1; + memset(param.open.rem_bda, 0, ESP_BD_ADDR_LEN); + btc_spp_cb_to_app(ESP_SPP_OPEN_EVT, ¶m); + } +} + +static void btc_spp_disconnect(btc_spp_args_t *arg) +{ + esp_spp_status_t ret = ESP_SPP_SUCCESS; + do { + if (!is_spp_init()) { + BTC_TRACE_ERROR("%s SPP have not been init\n", __func__); + ret = ESP_SPP_NEED_INIT; + break; + } + spp_slot_t *slot = NULL; + osi_mutex_lock(&spp_local_param.spp_slot_mutex, OSI_MUTEX_MAX_TIMEOUT); + slot = spp_find_slot_by_handle(arg->disconnect.handle); + if (!slot || (slot && !slot->connected)) { + osi_mutex_unlock(&spp_local_param.spp_slot_mutex); + if (!slot) { + BTC_TRACE_ERROR("%s unable to find RFCOMM slot! disconnect fail!", __func__); + } else { + BTC_TRACE_ERROR("%s RFCOMM has been disconnected already!", __func__); + } + ret = ESP_SPP_NO_CONNECTION; + break; + } + BTA_JvRfcommClose(arg->disconnect.handle, (void *)slot->id); + osi_mutex_unlock(&spp_local_param.spp_slot_mutex); + } while(0); + + if (ret != ESP_SPP_SUCCESS) { + esp_spp_cb_param_t param; + param.close.status = ret; + param.close.port_status = PORT_ERR_MAX; + param.close.handle = 0; + param.close.async = FALSE; + btc_spp_cb_to_app(ESP_SPP_CLOSE_EVT, ¶m); + } +} + +static void btc_spp_start_srv(btc_spp_args_t *arg) +{ + esp_spp_status_t ret = ESP_SPP_SUCCESS; + do { + if (!is_spp_init()) { + BTC_TRACE_ERROR("%s SPP have not been init\n", __func__); + ret = ESP_SPP_NEED_INIT; + break; + } + osi_mutex_lock(&spp_local_param.spp_slot_mutex, OSI_MUTEX_MAX_TIMEOUT); + spp_slot_t *slot = spp_malloc_slot(); + if (!slot) { + BTC_TRACE_ERROR("%s unable to malloc RFCOMM slot!", __func__); + osi_mutex_unlock(&spp_local_param.spp_slot_mutex); + ret = ESP_SPP_NO_RESOURCE; + break; + } + /** + * make this slot become a listening slot + */ + slot->is_server = true; + slot->security = arg->start_srv.sec_mask; + slot->role = arg->start_srv.role; + slot->scn = arg->start_srv.local_scn; + slot->max_session = arg->start_srv.max_session; + strcpy(slot->service_name, arg->start_srv.name); + + BTA_JvGetChannelId(BTA_JV_CONN_TYPE_RFCOMM, (void *)slot->id, arg->start_srv.local_scn); + osi_mutex_unlock(&spp_local_param.spp_slot_mutex); + } while(0); + + if (ret != ESP_SPP_SUCCESS) { + esp_spp_cb_param_t param; + param.start.status = ret; + param.start.handle = 0; + param.start.sec_id = 0; + param.start.scn = BTC_SPP_INVALID_SCN; + param.start.use_co = FALSE; + btc_spp_cb_to_app(ESP_SPP_START_EVT, ¶m); + } +} + +static void btc_spp_stop_srv(btc_spp_args_t *arg) +{ + esp_spp_status_t ret = ESP_SPP_SUCCESS; + bool is_remove_all = false; + uint8_t i, j, srv_cnt = 0; + uint8_t *srv_scn_arr = NULL; + if (arg->stop_srv.scn == BTC_SPP_INVALID_SCN) { + is_remove_all = true; + } + + do { + if (!is_spp_init()) { + BTC_TRACE_ERROR("%s SPP have not been init\n", __func__); + ret = ESP_SPP_NEED_INIT; + break; + } + srv_scn_arr = osi_malloc(MAX_RFC_PORTS); + if (srv_scn_arr == NULL) { + BTC_TRACE_ERROR("%s malloc srv_scn_arr failed\n", __func__); + ret = ESP_SPP_NO_RESOURCE; + break; + } + + osi_mutex_lock(&spp_local_param.spp_slot_mutex, OSI_MUTEX_MAX_TIMEOUT); + // [1] find all unconnected server + for (i = 1; i <= MAX_RFC_PORTS; i++) { + if (spp_local_param.spp_slots[i] != NULL && !spp_local_param.spp_slots[i]->connected && + spp_local_param.spp_slots[i]->sdp_handle > 0) { + if (is_remove_all) { + srv_scn_arr[srv_cnt++] = spp_local_param.spp_slots[i]->scn; + } else if (spp_local_param.spp_slots[i]->scn == arg->stop_srv.scn) { + srv_scn_arr[srv_cnt++] = spp_local_param.spp_slots[i]->scn; + break; + } + } + } + if (srv_cnt == 0) { + if (is_remove_all) { + BTC_TRACE_ERROR("%s can not find any server!\n", __func__); + } else { + BTC_TRACE_ERROR("%s can not find server:%d!\n", __func__, arg->stop_srv.scn); + } + ret = ESP_SPP_NO_SERVER; + break; + } + + // [2] remove all local related connection + for (j = 0; j < srv_cnt; j++) { + for (i = 1; i <= MAX_RFC_PORTS; i++) { + if (spp_local_param.spp_slots[i] != NULL && !spp_local_param.spp_slots[i]->is_server && + spp_local_param.spp_slots[i]->sdp_handle > 0 && + spp_local_param.spp_slots[i]->scn == srv_scn_arr[j]) { + BTA_JvRfcommClose(spp_local_param.spp_slots[i]->rfc_handle, (void *)spp_local_param.spp_slots[i]->id); + } + } + } + + // [3] remove all server + for (j = 0; j < srv_cnt; j++) { + for (i = 1; i <= MAX_RFC_PORTS; i++) { + if (spp_local_param.spp_slots[i] != NULL && spp_local_param.spp_slots[i]->is_server && + spp_local_param.spp_slots[i]->sdp_handle > 0 && + spp_local_param.spp_slots[i]->scn == srv_scn_arr[j]) { + if (spp_local_param.spp_slots[i]->sdp_handle > 0) { + BTA_JvDeleteRecord(spp_local_param.spp_slots[i]->sdp_handle); + } + + if (spp_local_param.spp_slots[i]->rfc_handle > 0) { + BTA_JvRfcommStopServer(spp_local_param.spp_slots[i]->rfc_handle, + (void *)spp_local_param.spp_slots[i]->id); + } + + tBTA_JV_FREE_SCN_USER_DATA *user_data = osi_malloc(sizeof(tBTA_JV_FREE_SCN_USER_DATA)); + if (user_data) { + user_data->server_status = BTA_JV_SERVER_RUNNING; + user_data->slot_id = spp_local_param.spp_slots[i]->id; + } else { + BTC_TRACE_ERROR("%s unable to malloc user data!", __func__); + assert(0); + } + BTA_JvFreeChannel(spp_local_param.spp_slots[i]->scn, BTA_JV_CONN_TYPE_RFCOMM, + (tBTA_JV_RFCOMM_CBACK *)btc_spp_rfcomm_inter_cb, (void *)user_data); + } + } + } + osi_mutex_unlock(&spp_local_param.spp_slot_mutex); + } while (0); + + if (ret != ESP_SPP_SUCCESS) { + esp_spp_cb_param_t param; + param.srv_stop.status = ret; + param.srv_stop.scn = BTC_SPP_INVALID_SCN; + btc_spp_cb_to_app(ESP_SPP_SRV_STOP_EVT, ¶m); + } + + if (srv_scn_arr) { + osi_free(srv_scn_arr); + srv_scn_arr = NULL; + } +} + +static void btc_spp_write(btc_spp_args_t *arg) +{ + esp_spp_status_t ret = ESP_SPP_SUCCESS; + + do { + if (!is_spp_init()) { + BTC_TRACE_ERROR("%s SPP have not been init\n", __func__); + ret = ESP_SPP_NEED_INIT; + break; + } + spp_slot_t *slot = NULL; + osi_mutex_lock(&spp_local_param.spp_slot_mutex, OSI_MUTEX_MAX_TIMEOUT); + slot = spp_find_slot_by_handle(arg->write.handle); + if (!slot || (slot && !slot->connected)) { + osi_mutex_unlock(&spp_local_param.spp_slot_mutex); + if (!slot) { + BTC_TRACE_ERROR("%s unable to find RFCOMM slot!", __func__); + } else { + BTC_TRACE_ERROR("%s RFCOMM has been disconnected already!", __func__); + } + ret = ESP_SPP_NO_CONNECTION; + break; + } + if (spp_local_param.spp_mode == ESP_SPP_MODE_VFS) { + if (slot->is_writing) { + osi_mutex_unlock(&spp_local_param.spp_slot_mutex); + return; + } + size_t item_size = 0; + uint8_t *data = xRingbufferReceiveUpTo(slot->ringbuf_write, &item_size, 0, slot->mtu); + if (item_size > 0) { + slot->write_data = data; + slot->write_data_len = item_size; + slot->is_writing = true; + BTA_JvRfcommWrite(arg->write.handle, slot->id, item_size, data); + } + } else { + if (fixed_queue_enqueue(slot->tx.queue, arg->write.p_data, 0)) { + BTA_JvRfcommWrite(arg->write.handle, slot->id, arg->write.len, arg->write.p_data); + } else { + ret = ESP_SPP_NO_RESOURCE; + } + } + osi_mutex_unlock(&spp_local_param.spp_slot_mutex); + } while (0); + + if (ret != ESP_SPP_SUCCESS && spp_local_param.spp_mode == ESP_SPP_MODE_CB) { + esp_spp_cb_param_t param; + param.write.status = ret; + param.write.handle = 0; + param.write.len = -1; + param.write.cong = false; + btc_spp_cb_to_app(ESP_SPP_WRITE_EVT, ¶m); + } +} + + +void btc_spp_arg_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src) +{ + btc_spp_args_t *dst = (btc_spp_args_t *) p_dest; + btc_spp_args_t *src = (btc_spp_args_t *) p_src; + + switch (msg->act) { + case BTC_SPP_ACT_START_DISCOVERY: + dst->start_discovery.p_uuid_list = (tSDP_UUID *)osi_malloc(src->start_discovery.num_uuid * sizeof(tSDP_UUID)); + if (dst->start_discovery.p_uuid_list) { + memcpy(dst->start_discovery.p_uuid_list, src->start_discovery.p_uuid_list, src->start_discovery.num_uuid * sizeof(tSDP_UUID)); + } else if (src->start_discovery.num_uuid == 0) { + BTC_TRACE_ERROR("%s %d no mem\n", __func__, msg->act); + } else { + BTC_TRACE_ERROR("%s %d osi_malloc failed\n", __func__, msg->act); + } + break; + case BTC_SPP_ACT_WRITE: + dst->write.p_data = (uint8_t *)osi_malloc(src->write.len); + if (dst->write.p_data) { + memcpy(dst->write.p_data, src->write.p_data, src->write.len); + } else if (src->write.len == 0) { + BTC_TRACE_DEBUG("%s %d no mem\n", __func__, msg->act); + } else { + BTC_TRACE_ERROR("%s %d osi_malloc failed\n", __func__, msg->act); + } + break; + default: + break; + } +} + +void btc_spp_arg_deep_free(btc_msg_t *msg) +{ + btc_spp_args_t *arg = (btc_spp_args_t *)msg->arg; + + switch (msg->act) { + case BTC_SPP_ACT_START_DISCOVERY: + if (arg->start_discovery.p_uuid_list) { + osi_free(arg->start_discovery.p_uuid_list); + } + break; + default: + break; + } +} + +void btc_spp_call_handler(btc_msg_t *msg) +{ + btc_spp_args_t *arg = (btc_spp_args_t *)(msg->arg); + switch (msg->act) { + case BTC_SPP_ACT_INIT: + btc_spp_init(arg); + break; + case BTC_SPP_ACT_UNINIT: + btc_spp_uninit(); + break; + case BTC_SPP_ACT_START_DISCOVERY: + btc_spp_start_discovery(arg); + break; + case BTC_SPP_ACT_CONNECT: + btc_spp_connect(arg); + break; + case BTC_SPP_ACT_DISCONNECT: + btc_spp_disconnect(arg); + break; + case BTC_SPP_ACT_START_SRV: + btc_spp_start_srv(arg); + break; + case BTC_SPP_ACT_STOP_SRV: + btc_spp_stop_srv(arg); + break; + case BTC_SPP_ACT_WRITE: + btc_spp_write(arg); + break; + case BTC_SPP_ACT_VFS_REGISTER: + btc_spp_vfs_register(); + break; + case BTC_SPP_ACT_VFS_UNREGISTER: + btc_spp_vfs_unregister(); + break; + default: + BTC_TRACE_ERROR("%s: Unhandled event (%d)!\n", __FUNCTION__, msg->act); + break; + } + btc_spp_arg_deep_free(msg); +} + +void btc_spp_cb_handler(btc_msg_t *msg) +{ + esp_spp_cb_param_t param; + tBTA_JV *p_data = (tBTA_JV *)msg->arg; + spp_slot_t *slot = NULL; + uint8_t serial = 0; + uint8_t event = msg->act; + + switch (event) { + case BTA_JV_ENABLE_EVT: + param.init.status = p_data->status; + btc_spp_cb_to_app(ESP_SPP_INIT_EVT, ¶m); + break; + case BTA_JV_DISCOVERY_COMP_EVT: + param.disc_comp.status = p_data->disc_comp.status; + param.disc_comp.scn_num = p_data->disc_comp.scn_num; + memcpy(param.disc_comp.scn, p_data->disc_comp.scn, p_data->disc_comp.scn_num); + memcpy(param.disc_comp.service_name, p_data->disc_comp.service_name, + p_data->disc_comp.scn_num * sizeof(const char *)); + btc_spp_cb_to_app(ESP_SPP_DISCOVERY_COMP_EVT, ¶m); + break; + case BTA_JV_RFCOMM_CL_INIT_EVT: + param.cl_init.status = p_data->rfc_cl_init.status; + param.cl_init.handle = p_data->rfc_cl_init.handle; + param.cl_init.sec_id = p_data->rfc_cl_init.sec_id; + param.cl_init.use_co = p_data->rfc_cl_init.use_co; + btc_spp_cb_to_app(ESP_SPP_CL_INIT_EVT, ¶m); + break; + case BTA_JV_RFCOMM_OPEN_EVT: + do { + if (spp_local_param.spp_mode == ESP_SPP_MODE_VFS) { + osi_mutex_lock(&spp_local_param.spp_slot_mutex, OSI_MUTEX_MAX_TIMEOUT); + slot = spp_find_slot_by_handle(p_data->rfc_open.handle); + if (!slot) { + osi_mutex_unlock(&spp_local_param.spp_slot_mutex); + BTC_TRACE_ERROR("%s unable to find RFCOMM slot, event:%d!", __func__, event); + param.open.status = ESP_SPP_NO_CONNECTION; + break; + } + param.open.fd = slot->fd; + osi_mutex_unlock(&spp_local_param.spp_slot_mutex); + } else { + param.open.fd = -1; + } + param.open.status = p_data->rfc_open.status; + } while (0); + param.open.handle = p_data->rfc_open.handle; + memcpy(param.open.rem_bda, p_data->rfc_open.rem_bda, ESP_BD_ADDR_LEN); + btc_spp_cb_to_app(ESP_SPP_OPEN_EVT, ¶m); + break; + case BTA_JV_RFCOMM_START_EVT: + param.start.status = p_data->rfc_start.status; + param.start.handle = p_data->rfc_start.handle; + param.start.sec_id = p_data->rfc_start.sec_id; + param.start.scn = p_data->rfc_start.scn; + param.start.use_co = p_data->rfc_start.use_co; + btc_spp_cb_to_app(ESP_SPP_START_EVT, ¶m); + break; + case BTA_JV_RFCOMM_SRV_OPEN_EVT: + if (p_data->rfc_srv_open.handle) { + do { + if (spp_local_param.spp_mode == ESP_SPP_MODE_VFS) { + osi_mutex_lock(&spp_local_param.spp_slot_mutex, OSI_MUTEX_MAX_TIMEOUT); + slot = spp_find_slot_by_handle(p_data->rfc_srv_open.handle); + if (!slot) { + osi_mutex_unlock(&spp_local_param.spp_slot_mutex); + BTC_TRACE_ERROR("%s unable to find RFCOMM slot, event:%d!", __func__, event); + param.srv_open.status = ESP_SPP_NO_CONNECTION; + break; + } + param.srv_open.fd = slot->fd; + osi_mutex_unlock(&spp_local_param.spp_slot_mutex); + } else { + param.srv_open.fd = -1; + } + param.srv_open.status = p_data->rfc_srv_open.status; + } while (0); + param.srv_open.handle = p_data->rfc_srv_open.handle; + param.srv_open.new_listen_handle = p_data->rfc_srv_open.new_listen_handle; + memcpy(param.srv_open.rem_bda, p_data->rfc_srv_open.rem_bda, ESP_BD_ADDR_LEN); + btc_spp_cb_to_app(ESP_SPP_SRV_OPEN_EVT, ¶m); + } + break; + case BTA_JV_RFCOMM_WRITE_EVT: + osi_mutex_lock(&spp_local_param.spp_slot_mutex, OSI_MUTEX_MAX_TIMEOUT); + slot = spp_find_slot_by_handle(p_data->rfc_write.handle); + if (!slot) { + BTC_TRACE_ERROR("%s unable to find RFCOMM slot!, handle:%d", __func__, p_data->rfc_write.handle); + } + if (spp_local_param.spp_mode == ESP_SPP_MODE_CB){ + param.write.status = slot ? p_data->rfc_write.status : ESP_SPP_NO_CONNECTION; + param.write.handle = p_data->rfc_write.handle; + param.write.len = p_data->rfc_write.len; + param.write.cong = p_data->rfc_write.cong; + btc_spp_cb_to_app(ESP_SPP_WRITE_EVT, ¶m); + if (slot) { + osi_free(fixed_queue_dequeue(slot->tx.queue, FIXED_QUEUE_MAX_TIMEOUT)); + } + } else { + if (slot) { + size_t item_size = 0; + size_t items_waiting = 0; + serial = slot->serial; + if (p_data->rfc_write.status == BTA_JV_SUCCESS) { + vRingbufferReturnItem(slot->ringbuf_write,slot->write_data); + slot->write_data = NULL; + slot->is_writing = false; + slot->write_data_len = 0; + vRingbufferGetInfo(slot->ringbuf_write, NULL, NULL, NULL, NULL, &items_waiting); + if (spp_local_param.tx_buffer_size > items_waiting) { + xEventGroupSetBits(spp_local_param.tx_event_group, SLOT_WRITE_BIT(serial)); + } + if (items_waiting == 0) { + osi_mutex_unlock(&spp_local_param.spp_slot_mutex); + break; + } + if (!p_data->rfc_write.cong) { + uint8_t *data = xRingbufferReceiveUpTo(slot->ringbuf_write, &item_size, 0, slot->mtu); + if (item_size > 0) { + slot->write_data = data; + slot->write_data_len = item_size; + slot->is_writing = true; + BTA_JvRfcommWrite(p_data->rfc_write.handle, slot->id, item_size, data); + } + } + } else { + if (!p_data->rfc_write.old_cong) { + // PORT_WriteDataCO failed + BTC_TRACE_ERROR("PORT_WriteDataCO failed p_buf:%p, handle:%d\n", slot->write_data, + p_data->rfc_write.handle); + } else { + // need rewrite + if (!p_data->rfc_write.cong && slot->connected) { + slot->is_writing = true; + BTA_JvRfcommWrite(p_data->rfc_write.handle, slot->id, slot->write_data_len, slot->write_data); + } else { + slot->is_writing = false; + } + } + } + } + } + osi_mutex_unlock(&spp_local_param.spp_slot_mutex); + break; + case BTA_JV_RFCOMM_CLOSE_EVT: + param.close.status = BTA_JV_SUCCESS; + param.close.port_status = p_data->rfc_close.port_status; + param.close.handle = p_data->rfc_close.handle; + param.close.async = p_data->rfc_close.async; + if (spp_local_param.spp_mode == ESP_SPP_MODE_CB) { + osi_mutex_lock(&spp_local_param.spp_slot_mutex, OSI_MUTEX_MAX_TIMEOUT); + slot = spp_find_slot_by_handle(p_data->rfc_close.handle); + if (!slot) { + param.close.status = ESP_SPP_NO_CONNECTION; + osi_mutex_unlock(&spp_local_param.spp_slot_mutex); + BTC_TRACE_ERROR("%s unable to find RFCOMM slot, event:%d!", __func__, event); + break; + } + spp_free_slot(slot); + osi_mutex_unlock(&spp_local_param.spp_slot_mutex); + btc_spp_cb_to_app(ESP_SPP_CLOSE_EVT, ¶m); + } else { + bool need_call = true; + do { + osi_mutex_lock(&spp_local_param.spp_slot_mutex, OSI_MUTEX_MAX_TIMEOUT); + slot = spp_find_slot_by_handle(p_data->rfc_close.handle); + if (!slot) { + param.close.status = ESP_SPP_NO_CONNECTION; + osi_mutex_unlock(&spp_local_param.spp_slot_mutex); + BTC_TRACE_ERROR("%s unable to find RFCOMM slot, event:%d!", __func__, event); + break; + } + // if rx still has data, delay free slot + if (slot->close_alarm == NULL && slot->rx.queue && fixed_queue_length(slot->rx.queue) > 0) { + tBTA_JV *p_arg = NULL; + if ((p_arg = osi_malloc(sizeof(tBTA_JV))) == NULL) { + param.close.status = ESP_SPP_NO_RESOURCE; + osi_mutex_unlock(&spp_local_param.spp_slot_mutex); + BTC_TRACE_ERROR("%s unable to malloc slot close_alarm arg!", __func__); + break; + } + memcpy(p_arg, p_data, sizeof(tBTA_JV)); + slot->alarm_arg = (void *)p_arg; + if ((slot->close_alarm = + osi_alarm_new("slot", close_timeout_handler, (void *)slot, VFS_CLOSE_TIMEOUT)) == NULL) { + free(p_arg); + slot->alarm_arg = NULL; + param.close.status = ESP_SPP_NO_RESOURCE; + osi_mutex_unlock(&spp_local_param.spp_slot_mutex); + BTC_TRACE_ERROR("%s unable to malloc slot close_alarm!", __func__); + break; + } + if (osi_alarm_set(slot->close_alarm, VFS_CLOSE_TIMEOUT) != OSI_ALARM_ERR_PASS) { + free(p_arg); + slot->alarm_arg = NULL; + osi_alarm_free(slot->close_alarm); + param.close.status = ESP_SPP_BUSY; + osi_mutex_unlock(&spp_local_param.spp_slot_mutex); + BTC_TRACE_ERROR("%s set slot close_alarm failed!", __func__); + break; + } + BTC_TRACE_WARNING("%s slot rx data will be discard in %d milliseconds!", + __func__, VFS_CLOSE_TIMEOUT); + slot->connected = false; + need_call = false; + } + osi_mutex_unlock(&spp_local_param.spp_slot_mutex); + } while (0); + + if (need_call) { + btc_spp_cb_to_app(ESP_SPP_CLOSE_EVT, ¶m); + osi_mutex_lock(&spp_local_param.spp_slot_mutex, OSI_MUTEX_MAX_TIMEOUT); + spp_free_slot(slot); + osi_mutex_unlock(&spp_local_param.spp_slot_mutex); + } + } + break; + case BTA_JV_RFCOMM_CONG_EVT: + if (spp_local_param.spp_mode == ESP_SPP_MODE_CB) { + param.cong.status = p_data->rfc_cong.status; + param.cong.handle = p_data->rfc_cong.handle; + param.cong.cong = p_data->rfc_cong.cong; + btc_spp_cb_to_app(ESP_SPP_CONG_EVT, ¶m); + } else { + osi_mutex_lock(&spp_local_param.spp_slot_mutex, OSI_MUTEX_MAX_TIMEOUT); + slot = spp_find_slot_by_handle(p_data->rfc_cong.handle); + if (!slot) { + osi_mutex_unlock(&spp_local_param.spp_slot_mutex); + BTC_TRACE_ERROR("%s unable to find RFCOMM slot, event:%d!", __func__, event); + break; + } + if (!p_data->rfc_cong.cong && !slot->is_writing) { + if (slot->write_data == NULL && slot->write_data_len == 0) { + size_t item_size = 0; + uint8_t *data = xRingbufferReceiveUpTo(slot->ringbuf_write, &item_size, 0, slot->mtu); + if (item_size > 0) { + slot->write_data = data; + slot->write_data_len = item_size; + slot->is_writing = true; + BTA_JvRfcommWrite(p_data->rfc_cong.handle, slot->id, item_size, data); + } + } else { + slot->is_writing = true; + BTA_JvRfcommWrite(p_data->rfc_cong.handle, slot->id, slot->write_data_len, slot->write_data); + } + } + osi_mutex_unlock(&spp_local_param.spp_slot_mutex); + } + break; + case BTA_JV_RFCOMM_DATA_IND_EVT: + do { + BT_HDR *p_buf; + UINT16 count = 0; + osi_mutex_lock(&spp_local_param.spp_slot_mutex, OSI_MUTEX_MAX_TIMEOUT); + slot = spp_find_slot_by_handle(p_data->data_ind.handle); + if (!slot) { + osi_mutex_unlock(&spp_local_param.spp_slot_mutex); + BTC_TRACE_ERROR("%s unable to find RFCOMM slot, event:%d!", __func__, event); + break; + } + serial = slot->serial; + osi_mutex_unlock(&spp_local_param.spp_slot_mutex); + + while (1) { + // get incoming_data from slot incoming list + osi_mutex_lock(&spp_local_param.spp_slot_mutex, OSI_MUTEX_MAX_TIMEOUT); + if ((slot = spp_local_param.spp_slots[serial]) != NULL && + slot->rfc_handle == p_data->data_ind.handle && + fixed_queue_length(slot->rx.queue) > 0) { + p_buf = (BT_HDR *)fixed_queue_dequeue(slot->rx.queue, FIXED_QUEUE_MAX_TIMEOUT); + } else { + osi_mutex_unlock(&spp_local_param.spp_slot_mutex); + break; + } + osi_mutex_unlock(&spp_local_param.spp_slot_mutex); + + // invoke callback + if (p_buf) { + count += 1; + param.data_ind.status = ESP_SPP_SUCCESS; + param.data_ind.handle = p_data->data_ind.handle; + param.data_ind.len = p_buf->len; + param.data_ind.data = p_buf->data + p_buf->offset; + btc_spp_cb_to_app(ESP_SPP_DATA_IND_EVT, ¶m); + BTC_TRACE_DEBUG("data cb to app: len %d\n", p_buf->len); + osi_free(p_buf); + } + } + if (count != 0) { + osi_mutex_lock(&spp_local_param.spp_slot_mutex, OSI_MUTEX_MAX_TIMEOUT); + slot->credit_rx += count; + osi_mutex_unlock(&spp_local_param.spp_slot_mutex); + BTA_JvRfcommFlowControl(p_data->data_ind.handle, count); + BTC_TRACE_DEBUG("%s give credits:%d\n", __func__, count); + } + } while (0); + break; + case BTA_JV_FREE_SCN_EVT: + if (p_data->free_scn.server_status == BTA_JV_SERVER_RUNNING) { + param.srv_stop.status = p_data->free_scn.status; + param.srv_stop.scn = p_data->free_scn.scn; + btc_spp_cb_to_app(ESP_SPP_SRV_STOP_EVT, ¶m); + } + break; + case BTA_JV_DISABLE_EVT: + param.uninit.status = ESP_SPP_SUCCESS; + spp_free_pending_slots(); + BTA_JvFree(); + osi_mutex_free(&spp_local_param.spp_slot_mutex); + if (spp_local_param.tx_event_group) { + vEventGroupDelete(spp_local_param.tx_event_group); + spp_local_param.tx_event_group = NULL; + } + if (spp_local_param.spp_mode == ESP_SPP_MODE_VFS) { + if (spp_local_param.spp_vfs_id != -1) { + esp_vfs_unregister_with_id(spp_local_param.spp_vfs_id); + spp_local_param.spp_vfs_id = -1; + } + } +#if SPP_DYNAMIC_MEMORY == TRUE + osi_free(spp_local_param_ptr); + spp_local_param_ptr = NULL; +#endif + btc_spp_cb_to_app(ESP_SPP_UNINIT_EVT, ¶m); + break; + default: + BTC_TRACE_DEBUG("%s: Unhandled event (%d)!", __FUNCTION__, msg->act); + break; + } + +} + +int bta_co_rfc_data_incoming(void *user_data, BT_HDR *p_buf) +{ + int ret = 1; + bt_status_t status; + tBTA_JV p_data; + btc_msg_t msg; + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_SPP; + msg.act = BTA_JV_RFCOMM_DATA_IND_EVT; + + uint32_t id = (uintptr_t)user_data; + if (!is_spp_init()) { + BTC_TRACE_ERROR("%s SPP have not been init\n", __func__); + return -1; + } + osi_mutex_lock(&spp_local_param.spp_slot_mutex, OSI_MUTEX_MAX_TIMEOUT); + spp_slot_t *slot = spp_find_slot_by_id(id); + if (!slot) { + osi_mutex_unlock(&spp_local_param.spp_slot_mutex); + BTC_TRACE_ERROR("%s unable to find RFCOMM slot!", __func__); + return -1; + } + p_data.data_ind.handle = slot->rfc_handle; + p_data.data_ind.p_buf = NULL; + + if (spp_local_param.spp_mode == ESP_SPP_MODE_CB) { + size_t rx_len = fixed_queue_length(slot->rx.queue); + fixed_queue_enqueue(slot->rx.queue, p_buf, FIXED_QUEUE_MAX_TIMEOUT); + if (rx_len == 0) { + BTC_TRACE_DEBUG("%s data post! %d, %d", __func__, slot->rfc_handle, rx_len); + status = btc_transfer_context(&msg, &p_data, sizeof(tBTA_JV), NULL, NULL); + assert(status == BT_STATUS_SUCCESS); + } + } else { + fixed_queue_enqueue(slot->rx.queue, p_buf, FIXED_QUEUE_MAX_TIMEOUT); + } + if (--slot->credit_rx == 0) { + BTC_TRACE_DEBUG("%s data post stop! %d %d", __func__, slot->rfc_handle, fixed_queue_length(slot->rx.queue)); + ret = 0; // reserved for other flow control + } + if (slot->credit_rx > BTA_JV_MAX_CREDIT_NUM) { + BTC_TRACE_WARNING("%s credit %d", __func__, slot->credit_rx); + assert(0); + } + osi_mutex_unlock(&spp_local_param.spp_slot_mutex); + return ret; +} + +int bta_co_rfc_data_outgoing_size(void *user_data, int *size) +{ + return 1; +} + +int bta_co_rfc_data_outgoing(void *user_data, uint8_t *buf, uint16_t size) +{ + return 1; +} + +esp_err_t spp_send_data_to_btc(uint32_t handle, int len, uint8_t *p_data, esp_spp_mode_t spp_mode) +{ + btc_msg_t msg; + btc_spp_args_t arg; + + if (spp_local_param.spp_mode != spp_mode) { + BTC_TRACE_WARNING("The current mode used is %d\n", spp_local_param.spp_mode); + return ESP_ERR_NOT_SUPPORTED; + } + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_SPP; + msg.act = BTC_SPP_ACT_WRITE; + + arg.write.handle = handle; + arg.write.len = len; + arg.write.p_data = p_data; + + return (btc_transfer_context(&msg, &arg, sizeof(btc_spp_args_t), btc_spp_arg_deep_copy, + btc_spp_arg_deep_free) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +static ssize_t spp_vfs_write(int fd, const void * data, size_t size) +{ + assert(data != NULL); + errno = 0; + if (size == 0) { + return 0; + } + if (!is_spp_init()) { + BTC_TRACE_ERROR("%s SPP have not been init\n", __func__); + errno = ESRCH; + return -1; + } + + spp_slot_t *slot = NULL; + uint8_t serial = 0; + osi_mutex_lock(&spp_local_param.spp_slot_mutex, OSI_MUTEX_MAX_TIMEOUT); + slot = spp_find_slot_by_fd(fd); + if (!slot) { + osi_mutex_unlock(&spp_local_param.spp_slot_mutex); + BTC_TRACE_ERROR("%s unable to find RFCOMM slot!", __func__); + errno = ENOENT; + return -1; + } + serial = slot->serial; + osi_mutex_unlock(&spp_local_param.spp_slot_mutex); + + ssize_t sent = 0; + size_t items_waiting = 0; + size_t item_size = 0; + EventBits_t tx_event_group_val = 0; + BaseType_t done = false; + while (size) { + tx_event_group_val = 0; + osi_mutex_lock(&spp_local_param.spp_slot_mutex, OSI_MUTEX_MAX_TIMEOUT); + slot = spp_local_param.spp_slots[serial]; + if (slot && slot->connected) { + items_waiting = 0; + item_size = 0; + vRingbufferGetInfo(slot->ringbuf_write, NULL, NULL, NULL, NULL, &items_waiting); + if (items_waiting < spp_local_param.tx_buffer_size) { + if ((spp_local_param.tx_buffer_size - items_waiting) > size) { + item_size = size; + done = xRingbufferSend(slot->ringbuf_write, (void *)data + sent, item_size, 0); + } else { + item_size = spp_local_param.tx_buffer_size - items_waiting; + done = xRingbufferSend(slot->ringbuf_write, (void *)data + sent, item_size, 0); + } + + if (done) { + sent += item_size; + size -= item_size; + if (slot->write_data == NULL) { + spp_send_data_to_btc(slot->rfc_handle, 0, NULL, ESP_SPP_MODE_VFS); + } + osi_mutex_unlock(&spp_local_param.spp_slot_mutex); + continue; + } + } + + BTC_TRACE_DEBUG("%s items_waiting:%d, fd:%d\n", __func__, items_waiting, fd); + osi_mutex_unlock(&spp_local_param.spp_slot_mutex); + + // block untill under water level, be closed or time out + tx_event_group_val = + xEventGroupWaitBits(spp_local_param.tx_event_group, SLOT_WRITE_BIT(serial) | SLOT_CLOSE_BIT(serial), pdTRUE, + pdFALSE, VFS_WRITE_TIMEOUT / portTICK_PERIOD_MS); + if (tx_event_group_val & SLOT_CLOSE_BIT(serial)) { + BTC_TRACE_ERROR("%s exit for RFCOMM close, fd:%d!", __func__, fd); + errno = EPIPE; + sent = -1; + break; + } else if (tx_event_group_val & SLOT_WRITE_BIT(serial)) { + continue; + } else if (tx_event_group_val == 0) { + BTC_TRACE_ERROR("%s exit for time out, fd:%d!", __func__, fd); + errno = EBUSY; + sent = -1; + break; + } + } else { + osi_mutex_unlock(&spp_local_param.spp_slot_mutex); + errno = EPIPE; + sent = -1; + break; + } + } + + return sent; +} + +static int spp_vfs_close(int fd) +{ + errno = 0; + if (!is_spp_init()) { + BTC_TRACE_ERROR("%s SPP have not been init\n", __func__); + errno = ESRCH; + return -1; + } + spp_slot_t *slot = NULL; + osi_mutex_lock(&spp_local_param.spp_slot_mutex, OSI_MUTEX_MAX_TIMEOUT); + slot = spp_find_slot_by_fd(fd); + if (!slot) { + osi_mutex_unlock(&spp_local_param.spp_slot_mutex); + BTC_TRACE_ERROR("%s unable to find RFCOMM slot!", __func__); + errno = ENOENT; + return -1; + } + esp_spp_disconnect(slot->rfc_handle); + osi_mutex_unlock(&spp_local_param.spp_slot_mutex); + return 0; +} + +static ssize_t spp_vfs_read(int fd, void * dst, size_t size) +{ + assert(dst != NULL); + errno = 0; + if (!is_spp_init()) { + BTC_TRACE_ERROR("%s SPP have not been init\n", __func__); + errno = ESRCH; + return -1; + } + + spp_slot_t *slot = NULL; + uint8_t serial = 0; + osi_mutex_lock(&spp_local_param.spp_slot_mutex, OSI_MUTEX_MAX_TIMEOUT); + slot = spp_find_slot_by_fd(fd); + if (!slot) { + osi_mutex_unlock(&spp_local_param.spp_slot_mutex); + BTC_TRACE_ERROR("%s unable to find RFCOMM slot!", __func__); + errno = ENOENT; + return -1; + } + serial = slot->serial; + osi_mutex_unlock(&spp_local_param.spp_slot_mutex); + + ssize_t item_size = 0; + uint16_t count = 0; + BT_HDR *p_buf; + while (1) { + osi_mutex_lock(&spp_local_param.spp_slot_mutex, OSI_MUTEX_MAX_TIMEOUT); + if ((slot = spp_local_param.spp_slots[serial]) != NULL) { + if (fixed_queue_length(slot->rx.queue) > 0) { + // free unused p_buf + if ((p_buf = (BT_HDR *)fixed_queue_try_peek_first(slot->rx.queue)) != NULL && p_buf->len == 0) { + osi_free(fixed_queue_dequeue(slot->rx.queue, FIXED_QUEUE_MAX_TIMEOUT)); + p_buf = NULL; + count++; + } + if (size == 0 || (p_buf = (BT_HDR *)fixed_queue_try_peek_first(slot->rx.queue)) == NULL) { + osi_mutex_unlock(&spp_local_param.spp_slot_mutex); + break; + } + } else { + /** + * If close_alarm is not NULL, it means that we have received the BTA_JV_RFCOMM_CLOSE_EVT. + * And we can trigger close_alarm immediately. + */ + if (slot->close_alarm && osi_alarm_is_active(slot->close_alarm)) { + osi_alarm_cancel(slot->close_alarm); + osi_alarm_set(slot->close_alarm, 0); + } + osi_mutex_unlock(&spp_local_param.spp_slot_mutex); + break; + } + } else { + osi_mutex_unlock(&spp_local_param.spp_slot_mutex); + BTC_TRACE_ERROR("%s peer close, data will be discarded!\n", __func__); + errno = EPIPE; + item_size = -1; + break; + } + osi_mutex_unlock(&spp_local_param.spp_slot_mutex); + + if (p_buf->len <= size) { + memcpy(dst, p_buf->data + p_buf->offset, p_buf->len); + size -= p_buf->len; + item_size += p_buf->len; + dst += p_buf->len; + p_buf->offset += p_buf->len; + p_buf->len = 0; // indicate the p_buf is unused + } else { + memcpy(dst, p_buf->data + p_buf->offset, size); + item_size += size; + p_buf->offset += size; + p_buf->len -= size; + size = 0; + } + } + if (count > 0) { + osi_mutex_lock(&spp_local_param.spp_slot_mutex, OSI_MUTEX_MAX_TIMEOUT); + slot->credit_rx += count; + if ((slot = spp_local_param.spp_slots[serial]) != NULL) { + BTA_JvRfcommFlowControl(slot->rfc_handle, count); + BTC_TRACE_DEBUG("%s give credits:%d\n", __func__, count); + } + osi_mutex_unlock(&spp_local_param.spp_slot_mutex); + } + return item_size; +} + +static void btc_spp_vfs_register(void) +{ + esp_spp_status_t ret = ESP_SPP_SUCCESS; + esp_spp_cb_param_t param; + + do { + if (!is_spp_init()) { + BTC_TRACE_ERROR("%s SPP have not been init\n", __func__); + ret = ESP_SPP_NEED_INIT; + break; + } + + esp_vfs_t vfs = { + .flags = ESP_VFS_FLAG_DEFAULT, + .write = spp_vfs_write, + .open = NULL, + .fstat = NULL, + .close = spp_vfs_close, + .read = spp_vfs_read, + .fcntl = NULL + }; + + // No FD range is registered here: spp_vfs_id is used to register/unregister + // file descriptors + if (esp_vfs_register_with_id(&vfs, NULL, &spp_local_param.spp_vfs_id) != ESP_OK) { + ret = ESP_SPP_FAILURE; + break; + } + } while (0); + + param.vfs_register.status = ret; + btc_spp_cb_to_app(ESP_SPP_VFS_REGISTER_EVT, ¶m); +} + +static void btc_spp_vfs_unregister(void) +{ + esp_spp_status_t ret = ESP_SPP_SUCCESS; + esp_spp_cb_param_t param; + + do { + if (!is_spp_init()) { + BTC_TRACE_ERROR("%s SPP have not been init\n", __func__); + ret = ESP_SPP_NEED_INIT; + break; + } + + if (spp_local_param.spp_mode == ESP_SPP_MODE_VFS) { + if (spp_local_param.spp_vfs_id != -1) { + if (esp_vfs_unregister_with_id(spp_local_param.spp_vfs_id) != ESP_OK) { + ret = ESP_SPP_FAILURE; + break; + } + spp_local_param.spp_vfs_id = -1; + } + } + } while (0); + + param.vfs_unregister.status = ret; + btc_spp_cb_to_app(ESP_SPP_VFS_UNREGISTER_EVT, ¶m); +} + +#endif ///defined BTC_SPP_INCLUDED && BTC_SPP_INCLUDED == TRUE diff --git a/lib/bt/host/bluedroid/common/include/common/bluedroid_user_config.h b/lib/bt/host/bluedroid/common/include/common/bluedroid_user_config.h new file mode 100644 index 00000000..ce8e80a2 --- /dev/null +++ b/lib/bt/host/bluedroid/common/include/common/bluedroid_user_config.h @@ -0,0 +1,513 @@ +/* + * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __BLUEDROID_USER_CONFIG_H__ +#define __BLUEDROID_USER_CONFIG_H__ + +/* All the configuration from SDK defined here */ +#include "bt_common.h" +#include "bt_user_config.h" +#include "soc/soc_caps.h" + +/********************************************************** + * Thread/Task reference + **********************************************************/ +#ifdef CONFIG_BT_BTU_TASK_STACK_SIZE +#define UC_BTU_TASK_STACK_SIZE CONFIG_BT_BTU_TASK_STACK_SIZE +#else +#define UC_BTU_TASK_STACK_SIZE 4096 +#endif + + +/********************************************************** + * Profile reference + **********************************************************/ +//Classic BT reference +#ifdef CONFIG_BT_CLASSIC_ENABLED +#define UC_BT_CLASSIC_ENABLED CONFIG_BT_CLASSIC_ENABLED +#else +#define UC_BT_CLASSIC_ENABLED FALSE +#endif + +//A2DP +#ifdef CONFIG_BT_A2DP_ENABLE +#define UC_BT_A2DP_ENABLED CONFIG_BT_A2DP_ENABLE +#else +#define UC_BT_A2DP_ENABLED FALSE +#endif + +//SPP +#ifdef CONFIG_BT_SPP_ENABLED +#define UC_BT_SPP_ENABLED CONFIG_BT_SPP_ENABLED +#else +#define UC_BT_SPP_ENABLED FALSE +#endif + +//L2CAP +#ifdef CONFIG_BT_L2CAP_ENABLED +#define UC_BT_L2CAP_ENABLED CONFIG_BT_L2CAP_ENABLED +#else +#define UC_BT_L2CAP_ENABLED FALSE +#endif + +//HFP(AG) +#ifdef CONFIG_BT_HFP_AG_ENABLE +#define UC_BT_HFP_AG_ENABLED CONFIG_BT_HFP_AG_ENABLE +#else +#define UC_BT_HFP_AG_ENABLED FALSE +#endif + +//HFP(Client) +#ifdef CONFIG_BT_HFP_CLIENT_ENABLE +#define UC_BT_HFP_CLIENT_ENABLED CONFIG_BT_HFP_CLIENT_ENABLE +#else +#define UC_BT_HFP_CLIENT_ENABLED FALSE +#endif + +//HID +#ifdef CONFIG_BT_HID_ENABLED +#define UC_BT_HID_ENABLED CONFIG_BT_HID_ENABLED +#else +#define UC_BT_HID_ENABLED FALSE +#endif + +//HID HOST(BT) +#ifdef CONFIG_BT_HID_HOST_ENABLED +#define UC_BT_HID_HOST_ENABLED CONFIG_BT_HID_HOST_ENABLED +#else +#define UC_BT_HID_HOST_ENABLED FALSE +#endif + +//HID Device(BT) +#ifdef CONFIG_BT_HID_DEVICE_ENABLED +#define UC_BT_HID_DEVICE_ENABLED CONFIG_BT_HID_DEVICE_ENABLED +#else +#define UC_BT_HID_DEVICE_ENABLED FALSE +#endif + +//BQB(BT) +#ifdef CONFIG_BT_CLASSIC_BQB_ENABLED +#define UC_BT_CLASSIC_BQB_ENABLED CONFIG_BT_CLASSIC_BQB_ENABLED +#else +#define UC_BT_CLASSIC_BQB_ENABLED FALSE +#endif + +//BLE +#ifdef CONFIG_BT_BLE_ENABLED +#define UC_BT_BLE_ENABLED CONFIG_BT_BLE_ENABLED +#else +#define UC_BT_BLE_ENABLED FALSE +#endif + +#ifdef CONFIG_BT_BLE_RPA_SUPPORTED +#define UC_BT_BLE_RPA_SUPPORTED CONFIG_BT_BLE_RPA_SUPPORTED +#else +#if (CONFIG_BT_CONTROLLER_ENABLED && SOC_BLE_DEVICE_PRIVACY_SUPPORTED) +#define UC_BT_BLE_RPA_SUPPORTED TRUE +#else +#define UC_BT_BLE_RPA_SUPPORTED FALSE +#endif +#endif + +#ifdef CONFIG_BT_BLE_50_FEATURES_SUPPORTED +#define UC_BT_BLE_50_FEATURES_SUPPORTED CONFIG_BT_BLE_50_FEATURES_SUPPORTED +#else +#define UC_BT_BLE_50_FEATURES_SUPPORTED FALSE +#endif + +#ifdef CONFIG_BT_BLE_42_FEATURES_SUPPORTED +#define UC_BT_BLE_42_FEATURES_SUPPORTED CONFIG_BT_BLE_42_FEATURES_SUPPORTED +#else +#define UC_BT_BLE_42_FEATURES_SUPPORTED FALSE +#endif + +#ifdef CONFIG_BT_BLE_FEAT_PERIODIC_ADV_SYNC_TRANSFER +#define UC_BT_BLE_FEAT_PERIODIC_ADV_SYNC_TRANSFER CONFIG_BT_BLE_FEAT_PERIODIC_ADV_SYNC_TRANSFER +#else +#define UC_BT_BLE_FEAT_PERIODIC_ADV_SYNC_TRANSFER FALSE +#endif + +#ifdef CONFIG_BT_BLE_FEAT_PERIODIC_ADV_ENH +#define UC_BT_BLE_FEAT_PERIODIC_ADV_ENH CONFIG_BT_BLE_FEAT_PERIODIC_ADV_ENH +#else +#define UC_BT_BLE_FEAT_PERIODIC_ADV_ENH FALSE +#endif + +#ifdef CONFIG_BT_BLE_FEAT_CREATE_SYNC_ENH +#define UC_BT_BLE_FEAT_CREATE_SYNC_ENH CONFIG_BT_BLE_FEAT_CREATE_SYNC_ENH +#else +#define UC_BT_BLE_FEAT_CREATE_SYNC_ENH FALSE +#endif + +#ifdef CONFIG_BT_BLE_HIGH_DUTY_ADV_INTERVAL +#define UC_BT_BLE_HIGH_DUTY_ADV_INTERVAL CONFIG_BT_BLE_HIGH_DUTY_ADV_INTERVAL +#else +#define UC_BT_BLE_HIGH_DUTY_ADV_INTERVAL FALSE +#endif + +//GATTS +#ifdef CONFIG_BT_GATTS_ENABLE +#define UC_BT_GATTS_ENABLE CONFIG_BT_GATTS_ENABLE +#else +#define UC_BT_GATTS_ENABLE FALSE +#endif + +//GATTC +#ifdef CONFIG_BT_GATTC_ENABLE +#define UC_BT_GATTC_ENABLE CONFIG_BT_GATTC_ENABLE +#else +#define UC_BT_GATTC_ENABLE FALSE +#endif + +//GATTC CACHE +#ifdef CONFIG_BT_GATTC_MAX_CACHE_CHAR +#define UC_BT_GATTC_MAX_CACHE_CHAR CONFIG_BT_GATTC_MAX_CACHE_CHAR +#else +#define UC_BT_GATTC_MAX_CACHE_CHAR 40 +#endif + +//GATTC NOTIF +#ifdef CONFIG_BT_GATTC_NOTIF_REG_MAX +#define UC_BT_GATTC_NOTIF_REG_MAX CONFIG_BT_GATTC_NOTIF_REG_MAX +#else +#define UC_BT_GATTC_NOTIF_REG_MAX 5 +#endif + +#ifdef CONFIG_BT_GATTC_CACHE_NVS_FLASH +#define UC_BT_GATTC_CACHE_NVS_FLASH_ENABLED CONFIG_BT_GATTC_CACHE_NVS_FLASH +#else +#define UC_BT_GATTC_CACHE_NVS_FLASH_ENABLED FALSE +#endif + +#ifdef CONFIG_BT_GATTC_CONNECT_RETRY_COUNT +#define UC_BT_GATTC_CONNECT_RETRY_COUNT CONFIG_BT_GATTC_CONNECT_RETRY_COUNT +#else +#define UC_BT_GATTC_CONNECT_RETRY_COUNT 0 +#endif + + +//SMP +#ifdef CONFIG_BT_SMP_ENABLE +#define UC_BT_SMP_ENABLE CONFIG_BT_SMP_ENABLE +#else +#define UC_BT_SMP_ENABLE FALSE +#endif + +//SMP_SLAVE_CON_PARAMS_UPD_ENABLE +#ifdef CONFIG_BT_SMP_SLAVE_CON_PARAMS_UPD_ENABLE +#define UC_BT_SMP_SLAVE_CON_PARAMS_UPD_ENABLE CONFIG_BT_SMP_SLAVE_CON_PARAMS_UPD_ENABLE +#else +#define UC_BT_SMP_SLAVE_CON_PARAMS_UPD_ENABLE FALSE +#endif + +#ifdef CONFIG_BT_SMP_MAX_BONDS +#define UC_BT_SMP_MAX_BONDS CONFIG_BT_SMP_MAX_BONDS +#else +#define UC_BT_SMP_MAX_BONDS 8 +#endif + +//Device Nane Maximum Length +#ifdef CONFIG_BT_MAX_DEVICE_NAME_LEN +#define UC_MAX_LOC_BD_NAME_LEN CONFIG_BT_MAX_DEVICE_NAME_LEN +#else +#define UC_MAX_LOC_BD_NAME_LEN 64 +#endif + +#if CONFIG_IDF_TARGET_ESP32 + +//BTDM_BLE_ADV_REPORT_FLOW_CTRL_SUPP +#ifdef CONFIG_BTDM_BLE_ADV_REPORT_FLOW_CTRL_SUPP +#define UC_BTDM_BLE_ADV_REPORT_FLOW_CTRL_SUPP CONFIG_BTDM_BLE_ADV_REPORT_FLOW_CTRL_SUPP +#else +#define UC_BTDM_BLE_ADV_REPORT_FLOW_CTRL_SUPP FALSE +#endif + +//SMP_SLAVE_CON_PARAMS_UPD_ENABLE +#ifdef CONFIG_BTDM_BLE_ADV_REPORT_FLOW_CTRL_NUM +#define UC_BTDM_BLE_ADV_REPORT_FLOW_CTRL_NUM CONFIG_BTDM_BLE_ADV_REPORT_FLOW_CTRL_NUM +#else +#define UC_BTDM_BLE_ADV_REPORT_FLOW_CTRL_NUM 100 +#endif + +//BTDM_BLE_ADV_REPORT_FLOW_CTRL_SUPP +#ifdef CONFIG_BTDM_BLE_ADV_REPORT_DISCARD_THRSHOLD +#define UC_BTDM_BLE_ADV_REPORT_DISCARD_THRSHOLD CONFIG_BTDM_BLE_ADV_REPORT_DISCARD_THRSHOLD +#else +#define UC_BTDM_BLE_ADV_REPORT_DISCARD_THRSHOLD 20 +#endif + +#endif //CONFIG_IDF_TARGET_ESP32 + +#if (CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32C2) +//BTDM_BLE_ADV_REPORT_FLOW_CTRL_SUPP +#ifdef CONFIG_BT_CTRL_BLE_ADV_REPORT_FLOW_CTRL_SUPP +#define UC_BTDM_BLE_ADV_REPORT_FLOW_CTRL_SUPP CONFIG_BT_CTRL_BLE_ADV_REPORT_FLOW_CTRL_SUPP +#else +#define UC_BTDM_BLE_ADV_REPORT_FLOW_CTRL_SUPP FALSE +#endif + +//SMP_SLAVE_CON_PARAMS_UPD_ENABLE +#ifdef CONFIG_BT_CTRL_BLE_ADV_REPORT_FLOW_CTRL_NUM +#define UC_BTDM_BLE_ADV_REPORT_FLOW_CTRL_NUM CONFIG_BT_CTRL_BLE_ADV_REPORT_FLOW_CTRL_NUM +#else +#define UC_BTDM_BLE_ADV_REPORT_FLOW_CTRL_NUM 100 +#endif + +//BTDM_BLE_ADV_REPORT_FLOW_CTRL_SUPP +#ifdef CONFIG_BT_CTRL_BLE_ADV_REPORT_DISCARD_THRSHOLD +#define UC_BTDM_BLE_ADV_REPORT_DISCARD_THRSHOLD CONFIG_BT_CTRL_BLE_ADV_REPORT_DISCARD_THRSHOLD +#else +#define UC_BTDM_BLE_ADV_REPORT_DISCARD_THRSHOLD 20 +#endif + +#endif //(CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32C2) + +//BT ACL CONNECTIONS +#ifdef CONFIG_BT_ACL_CONNECTIONS +#define UC_BT_ACL_CONNECTIONS CONFIG_BT_ACL_CONNECTIONS +#else +#define UC_BT_ACL_CONNECTIONS 5 +#endif + +#ifdef CONFIG_BT_MULTI_CONNECTION_ENBALE +#define UC_BT_MULTI_CONNECTION_ENBALE CONFIG_BT_MULTI_CONNECTION_ENBALE +#else +#define UC_BT_MULTI_CONNECTION_ENBALE FALSE +#endif + +//BT_BLE_ESTAB_LINK_CONN_TOUT +#ifdef CONFIG_BT_BLE_ESTAB_LINK_CONN_TOUT +#define UC_BT_BLE_ESTAB_LINK_CONN_TOUT CONFIG_BT_BLE_ESTAB_LINK_CONN_TOUT +#else +#define UC_BT_BLE_ESTAB_LINK_CONN_TOUT 30 +#endif + + +//HOST QUEUE CONGEST CHECK +#ifdef CONFIG_BT_BLE_HOST_QUEUE_CONGESTION_CHECK +#define UC_BT_BLE_HOST_QUEUE_CONGESTION_CHECK CONFIG_BT_BLE_HOST_QUEUE_CONGESTION_CHECK +#else +#define UC_BT_BLE_HOST_QUEUE_CONGESTION_CHECK FALSE +#endif + +#ifdef CONFIG_BT_GATTS_PPCP_CHAR_GAP +#define UC_CONFIG_BT_GATTS_PPCP_CHAR_GAP CONFIG_BT_GATTS_PPCP_CHAR_GAP +#else +#define UC_CONFIG_BT_GATTS_PPCP_CHAR_GAP FALSE +#endif + + +#ifdef CONFIG_BT_GATT_MAX_SR_PROFILES +#define UC_CONFIG_BT_GATT_MAX_SR_PROFILES CONFIG_BT_GATT_MAX_SR_PROFILES +#else +#define UC_CONFIG_BT_GATT_MAX_SR_PROFILES 8 +#endif + +#ifdef CONFIG_BT_GATT_MAX_SR_ATTRIBUTES +#define UC_CONFIG_BT_GATT_MAX_SR_ATTRIBUTES CONFIG_BT_GATT_MAX_SR_ATTRIBUTES +#else +#define UC_CONFIG_BT_GATT_MAX_SR_ATTRIBUTES 100 +#endif + +#ifdef CONFIG_BT_GATTS_SEND_SERVICE_CHANGE_MODE +#define UC_BT_GATTS_SEND_SERVICE_CHANGE_MODE CONFIG_BT_GATTS_SEND_SERVICE_CHANGE_MODE +#else +#define UC_BT_GATTS_SEND_SERVICE_CHANGE_MODE 0 +#endif + +#ifdef CONFIG_BT_GATTS_ROBUST_CACHING_ENABLED +#define UC_BT_GATTS_ROBUST_CACHING_ENABLED CONFIG_BT_GATTS_ROBUST_CACHING_ENABLED +#else +#define UC_BT_GATTS_ROBUST_CACHING_ENABLED FALSE +#endif + +#ifdef CONFIG_BT_GATTS_DEVICE_NAME_WRITABLE +#define UC_BT_GATTS_DEVICE_NAME_WRITABLE CONFIG_BT_GATTS_DEVICE_NAME_WRITABLE +#else +#define UC_BT_GATTS_DEVICE_NAME_WRITABLE FALSE +#endif + +#ifdef CONFIG_BT_GATTS_APPEARANCE_WRITABLE +#define UC_BT_GATTS_APPEARANCE_WRITABLE CONFIG_BT_GATTS_APPEARANCE_WRITABLE +#else +#define UC_BT_GATTS_APPEARANCE_WRITABLE FALSE +#endif + +#ifdef CONFIG_BT_BLE_ACT_SCAN_REP_ADV_SCAN +#define UC_BT_BLE_ACT_SCAN_REP_ADV_SCAN CONFIG_BT_BLE_ACT_SCAN_REP_ADV_SCAN +#else +#define UC_BT_BLE_ACT_SCAN_REP_ADV_SCAN FALSE +#endif + +#ifdef CONFIG_BT_BLE_RPA_TIMEOUT +#define UC_BT_BLE_RPA_TIMEOUT CONFIG_BT_BLE_RPA_TIMEOUT +#else +#define UC_BT_BLE_RPA_TIMEOUT 900 +#endif + +//SCO VOICE OVER HCI +#ifdef CONFIG_BT_HFP_AUDIO_DATA_PATH_HCI +#define UC_BT_HFP_AUDIO_DATA_PATH_HCI CONFIG_BT_HFP_AUDIO_DATA_PATH_HCI +#else +#define UC_BT_HFP_AUDIO_DATA_PATH_HCI FALSE +#endif + +//Wide Band Speech +#ifdef CONFIG_BT_HFP_WBS_ENABLE +#define UC_BT_HFP_WBS_ENABLE CONFIG_BT_HFP_WBS_ENABLE +#else +#define UC_BT_HFP_WBS_ENABLE FALSE +#endif + +/********************************************************** + * Memory reference + **********************************************************/ + +//MEMORY ALLOCATOR +#ifdef CONFIG_BT_ALLOCATION_FROM_SPIRAM_FIRST +#define UC_HEAP_ALLOCATION_FROM_SPIRAM_FIRST CONFIG_BT_ALLOCATION_FROM_SPIRAM_FIRST +#else +#define UC_HEAP_ALLOCATION_FROM_SPIRAM_FIRST FALSE +#endif + +//MEMORY DEBUG +#ifdef CONFIG_BT_BLUEDROID_MEM_DEBUG +#define UC_BT_BLUEDROID_MEM_DEBUG CONFIG_BT_BLUEDROID_MEM_DEBUG +#else +#define UC_BT_BLUEDROID_MEM_DEBUG FALSE +#endif + +//ESP COEXIST VSC +#ifdef CONFIG_BT_BLUEDROID_ESP_COEX_VSC +#define UC_BT_BLUEDROID_ESP_COEX_VSC CONFIG_BT_BLUEDROID_ESP_COEX_VSC +#else +#define UC_BT_BLUEDROID_ESP_COEX_VSC FALSE +#endif + + +/********************************************************** + * Trace reference + **********************************************************/ + +#ifdef CONFIG_BT_LOG_HCI_TRACE_LEVEL +#define UC_BT_LOG_HCI_TRACE_LEVEL CONFIG_BT_LOG_HCI_TRACE_LEVEL +#else +#define UC_BT_LOG_HCI_TRACE_LEVEL UC_TRACE_LEVEL_WARNING +#endif + +#ifdef CONFIG_BT_LOG_BTM_TRACE_LEVEL +#define UC_BT_LOG_BTM_TRACE_LEVEL CONFIG_BT_LOG_BTM_TRACE_LEVEL +#else +#define UC_BT_LOG_BTM_TRACE_LEVEL UC_TRACE_LEVEL_WARNING +#endif + +#ifdef CONFIG_BT_LOG_L2CAP_TRACE_LEVEL +#define UC_BT_LOG_L2CAP_TRACE_LEVEL CONFIG_BT_LOG_L2CAP_TRACE_LEVEL +#else +#define UC_BT_LOG_L2CAP_TRACE_LEVEL UC_TRACE_LEVEL_WARNING +#endif + +#ifdef CONFIG_BT_LOG_RFCOMM_TRACE_LEVEL +#define UC_BT_LOG_RFCOMM_TRACE_LEVEL CONFIG_BT_LOG_RFCOMM_TRACE_LEVEL +#else +#define UC_BT_LOG_RFCOMM_TRACE_LEVEL UC_TRACE_LEVEL_WARNING +#endif + +#ifdef CONFIG_BT_LOG_SDP_TRACE_LEVEL +#define UC_BT_LOG_SDP_TRACE_LEVEL CONFIG_BT_LOG_SDP_TRACE_LEVEL +#else +#define UC_BT_LOG_SDP_TRACE_LEVEL UC_TRACE_LEVEL_WARNING +#endif + +#ifdef CONFIG_BT_LOG_GAP_TRACE_LEVEL +#define UC_BT_LOG_GAP_TRACE_LEVEL CONFIG_BT_LOG_GAP_TRACE_LEVEL +#else +#define UC_BT_LOG_GAP_TRACE_LEVEL UC_TRACE_LEVEL_WARNING +#endif + +#ifdef CONFIG_BT_LOG_BNEP_TRACE_LEVEL +#define UC_BT_LOG_BNEP_TRACE_LEVEL CONFIG_BT_LOG_BNEP_TRACE_LEVEL +#else +#define UC_BT_LOG_BNEP_TRACE_LEVEL UC_TRACE_LEVEL_WARNING +#endif + +#ifdef CONFIG_BT_LOG_PAN_TRACE_LEVEL +#define UC_BT_LOG_PAN_TRACE_LEVEL CONFIG_BT_LOG_PAN_TRACE_LEVEL +#else +#define UC_BT_LOG_PAN_TRACE_LEVEL UC_TRACE_LEVEL_WARNING +#endif + +#ifdef CONFIG_BT_LOG_A2D_TRACE_LEVEL +#define UC_BT_LOG_A2D_TRACE_LEVEL CONFIG_BT_LOG_A2D_TRACE_LEVEL +#else +#define UC_BT_LOG_A2D_TRACE_LEVEL UC_TRACE_LEVEL_WARNING +#endif + +#ifdef CONFIG_BT_LOG_AVDT_TRACE_LEVEL +#define UC_BT_LOG_AVDT_TRACE_LEVEL CONFIG_BT_LOG_AVDT_TRACE_LEVEL +#else +#define UC_BT_LOG_AVDT_TRACE_LEVEL UC_TRACE_LEVEL_WARNING +#endif + +#ifdef CONFIG_BT_LOG_AVCT_TRACE_LEVEL +#define UC_BT_LOG_AVCT_TRACE_LEVEL CONFIG_BT_LOG_AVCT_TRACE_LEVEL +#else +#define UC_BT_LOG_AVCT_TRACE_LEVEL UC_TRACE_LEVEL_WARNING +#endif + +#ifdef CONFIG_BT_LOG_AVRC_TRACE_LEVEL +#define UC_BT_LOG_AVRC_TRACE_LEVEL CONFIG_BT_LOG_AVRC_TRACE_LEVEL +#else +#define UC_BT_LOG_AVRC_TRACE_LEVEL UC_TRACE_LEVEL_WARNING +#endif + +#ifdef CONFIG_BT_LOG_MCA_TRACE_LEVEL +#define UC_BT_LOG_MCA_TRACE_LEVEL CONFIG_BT_LOG_MCA_TRACE_LEVEL +#else +#define UC_BT_LOG_MCA_TRACE_LEVEL UC_TRACE_LEVEL_WARNING +#endif + +#ifdef CONFIG_BT_LOG_HID_TRACE_LEVEL +#if UC_BT_HID_HOST_ENABLED +#define UC_BT_LOG_HIDH_TRACE_LEVEL CONFIG_BT_LOG_HID_TRACE_LEVEL +#endif +#if UC_BT_HID_DEVICE_ENABLED +#define UC_BT_LOG_HIDD_TRACE_LEVEL CONFIG_BT_LOG_HID_TRACE_LEVEL +#endif +#else +#if UC_BT_HID_HOST_ENABLED +#define UC_BT_LOG_HIDH_TRACE_LEVEL UC_TRACE_LEVEL_WARNING +#endif +#if UC_BT_HID_DEVICE_ENABLED +#define UC_BT_LOG_HIDD_TRACE_LEVEL UC_TRACE_LEVEL_WARNING +#endif +#endif + +#ifdef CONFIG_BT_LOG_APPL_TRACE_LEVEL +#define UC_BT_LOG_APPL_TRACE_LEVEL CONFIG_BT_LOG_APPL_TRACE_LEVEL +#else +#define UC_BT_LOG_APPL_TRACE_LEVEL UC_TRACE_LEVEL_WARNING +#endif + +#ifdef CONFIG_BT_LOG_GATT_TRACE_LEVEL +#define UC_BT_LOG_GATT_TRACE_LEVEL CONFIG_BT_LOG_GATT_TRACE_LEVEL +#else +#define UC_BT_LOG_GATT_TRACE_LEVEL UC_TRACE_LEVEL_WARNING +#endif + +#ifdef CONFIG_BT_LOG_SMP_TRACE_LEVEL +#define UC_BT_LOG_SMP_TRACE_LEVEL CONFIG_BT_LOG_SMP_TRACE_LEVEL +#else +#define UC_BT_LOG_SMP_TRACE_LEVEL UC_TRACE_LEVEL_WARNING +#endif + +#ifdef CONFIG_BT_LOG_BTIF_TRACE_LEVEL +#define UC_BT_LOG_BTIF_TRACE_LEVEL CONFIG_BT_LOG_BTIF_TRACE_LEVEL +#else +#define UC_BT_LOG_BTIF_TRACE_LEVEL UC_TRACE_LEVEL_WARNING +#endif + +#endif /* __BLUEDROID_USER_CONFIG_H__ */ diff --git a/lib/bt/host/bluedroid/common/include/common/bt_common_types.h b/lib/bt/host/bluedroid/common/include/common/bt_common_types.h new file mode 100644 index 00000000..7b96579e --- /dev/null +++ b/lib/bt/host/bluedroid/common/include/common/bt_common_types.h @@ -0,0 +1,35 @@ +/* + * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + + + + + +#ifndef _BT_COMMON_TYPES_H_ +#define _BT_COMMON_TYPES_H_ + +#include "common/bt_defs.h" +#include "osi/thread.h" + +typedef void (* bluedroid_init_done_cb_t)(void); + +typedef struct { + uint8_t client_if; + uint8_t filt_index; + uint8_t advertiser_state; + uint8_t advertiser_info_present; + uint8_t addr_type; + uint8_t tx_power; + int8_t rssi_value; + uint16_t time_stamp; + bt_bdaddr_t bd_addr; + uint8_t adv_pkt_len; + uint8_t *p_adv_pkt_data; + uint8_t scan_rsp_len; + uint8_t *p_scan_rsp_data; +} btgatt_track_adv_info_t; + +#endif diff --git a/lib/bt/host/bluedroid/common/include/common/bt_defs.h b/lib/bt/host/bluedroid/common/include/common/bt_defs.h new file mode 100644 index 00000000..b10e2efa --- /dev/null +++ b/lib/bt/host/bluedroid/common/include/common/bt_defs.h @@ -0,0 +1,65 @@ +/* + * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * common/bt_defs.h Defines useful API for whole Bluedroid + * + */ +#ifndef _BT_DEFS_H_ +#define _BT_DEFS_H_ + +#include +#include +#include +#include "bt_common.h" +#include "common/bt_target.h" + +#define UNUSED(x) (void)(x) + +/*Timer Related Defination*/ + +//by Snake.T +typedef void (TIMER_CBACK)(void *p_tle); +typedef struct _tle { + struct _tle *p_next; + struct _tle *p_prev; + TIMER_CBACK *p_cback; + INT32 ticks; + INT32 ticks_initial; + TIMER_PARAM_TYPE param; + TIMER_PARAM_TYPE data; + UINT16 event; + UINT8 in_use; +} TIMER_LIST_ENT; + +#define alarm_timer_t uint32_t +#define alarm_timer_setfn(timer, cb, data) \ +do { \ +} while (0) +#define alarm_timer_arm(timer, to, periodic) \ +do { \ +} while (0) +#define alarm_timer_disarm(timer) \ +do { \ +} while (0) +#define alarm_timer_now() (0) + + +/*Bluetooth Address*/ +typedef struct { + uint8_t address[6]; +} __attribute__ ((__packed__)) bt_bdaddr_t; + +/** Bluetooth 128-bit UUID */ +typedef struct { + uint8_t uu[16]; +} bt_uuid_t; + +#ifndef CPU_LITTLE_ENDIAN +#define CPU_LITTLE_ENDIAN +#endif + +#endif /* _BT_DEFS_H_ */ diff --git a/lib/bt/host/bluedroid/common/include/common/bt_target.h b/lib/bt/host/bluedroid/common/include/common/bt_target.h new file mode 100644 index 00000000..b193805d --- /dev/null +++ b/lib/bt/host/bluedroid/common/include/common/bt_target.h @@ -0,0 +1,2378 @@ +/****************************************************************************** + * + * Copyright (c) 2014 The Android Open Source Project + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +#ifndef BT_TARGET_H +#define BT_TARGET_H + +#include + +#ifndef BUILDCFG +#define BUILDCFG +#endif + +/* +#if !defined(HAS_BDROID_BUILDCFG) && !defined(HAS_NO_BDROID_BUILDCFG) +#error "An Android.mk file did not include bdroid_CFLAGS and possibly not bdorid_C_INCLUDES" +#endif +*/ + +#ifdef HAS_BDROID_BUILDCFG +#include "bdroid_buildcfg.h" +#endif + +#include "bluedroid_user_config.h" +#include "stack/bt_types.h" /* This must be defined AFTER buildcfg.h */ + +#include "stack/dyn_mem.h" /* defines static and/or dynamic memory for components */ + + +/* OS Configuration from User config (eg: sdkconfig) */ +#define BT_BTU_TASK_STACK_SIZE UC_BTU_TASK_STACK_SIZE + +#if (UC_BT_BLUEDROID_ESP_COEX_VSC == TRUE) +#define ESP_COEX_VSC_INCLUDED TRUE +#else +#define ESP_COEX_VSC_INCLUDED FALSE +#endif + +#if (UC_BT_CONTROLLER_INCLUDED == TRUE) +#define BT_CONTROLLER_INCLUDED TRUE +#else +#define BT_CONTROLLER_INCLUDED FALSE +#endif + +/****************************************************************************** +** +** Classic BT features +** +******************************************************************************/ +#if (UC_BT_CLASSIC_ENABLED == TRUE) +#define CLASSIC_BT_INCLUDED TRUE +#define BTC_SM_INCLUDED TRUE +#define BTC_PRF_QUEUE_INCLUDED TRUE +#define BTC_GAP_BT_INCLUDED TRUE +#define BTA_SDP_INCLUDED TRUE +#define BTA_DM_PM_INCLUDED TRUE +#define BTC_DM_PM_INCLUDED TRUE +#define SDP_INCLUDED TRUE +#define BTA_DM_QOS_INCLUDED TRUE + +#if (UC_BT_A2DP_ENABLED == TRUE) +#define BTA_AR_INCLUDED TRUE +#define BTA_AV_INCLUDED TRUE +#define AVDT_INCLUDED TRUE +#define A2D_INCLUDED TRUE +#define AVCT_INCLUDED TRUE +#define AVRC_INCLUDED TRUE +#define BTC_AV_INCLUDED TRUE +#define BTA_AV_SINK_INCLUDED TRUE +#define BTC_AV_SINK_INCLUDED TRUE +#define SBC_DEC_INCLUDED TRUE +#define BTC_AV_SRC_INCLUDED TRUE +#define SBC_ENC_INCLUDED TRUE +#endif /* UC_BT_A2DP_ENABLED */ + +#if (UC_BT_SPP_ENABLED == TRUE) +#define RFCOMM_INCLUDED TRUE +#define BTA_JV_INCLUDED TRUE +#define BTA_JV_RFCOMM_INCLUDED TRUE +#define BTC_SPP_INCLUDED TRUE +#endif /* UC_BT_SPP_ENABLED */ + +#if (UC_BT_L2CAP_ENABLED == TRUE) +#define BTA_JV_INCLUDED TRUE +#define BTC_L2CAP_INCLUDED TRUE +#define BTC_SDP_INCLUDED TRUE +#define VND_BT_JV_BTA_L2CAP TRUE +#endif /* UC_BT_L2CAP_ENABLED */ + +#if (UC_BT_HFP_AG_ENABLED == TRUE) || (UC_BT_HFP_CLIENT_ENABLED == TRUE) +#ifndef RFCOMM_INCLUDED +#define RFCOMM_INCLUDED TRUE +#endif +#ifndef BTM_SCO_INCLUDED +#define BTM_SCO_INCLUDED TRUE +#endif +#ifndef SBC_DEC_INCLUDED +#define SBC_DEC_INCLUDED TRUE +#endif +#ifndef SBC_ENC_INCLUDED +#define SBC_ENC_INCLUDED TRUE +#endif +#ifndef PLC_INCLUDED +#define PLC_INCLUDED TRUE +#endif + +#if (UC_BT_HFP_AG_ENABLED == TRUE) +#ifndef BTM_MAX_SCO_LINKS_AG +#define BTM_MAX_SCO_LINKS_AG (1) +#endif +#define BTC_HF_INCLUDED TRUE +#define BTA_AG_INCLUDED TRUE +#else +#ifndef BTM_MAX_SCO_LINKS_AG +#define BTM_MAX_SCO_LINKS_AG (0) +#endif +#endif /* (UC_BT_HFP_AG_ENABLED == TRUE) */ +#if (UC_BT_HFP_CLIENT_ENABLED == TRUE) +#ifndef BTM_MAX_SCO_LINKS_CLIENT +#define BTM_MAX_SCO_LINKS_CLIENT (1) +#endif +#define BTC_HF_CLIENT_INCLUDED TRUE +#define BTA_HF_INCLUDED TRUE +#else +#ifndef BTM_MAX_SCO_LINKS_CLIENT +#define BTM_MAX_SCO_LINKS_CLIENT (0) +#endif +#endif /* (UC_BT_HFP_CLIENT_ENABLED == TRUE) */ + +#ifndef BTM_MAX_SCO_LINKS +#define BTM_MAX_SCO_LINKS (BTM_MAX_SCO_LINKS_AG + BTM_MAX_SCO_LINKS_CLIENT) +#endif +#endif /* (UC_BT_HFP_AG_ENABLED == TRUE) || (UC_BT_HFP_CLIENT_ENABLED == TRUE) */ + +#if UC_BT_HID_ENABLED +#define BT_HID_INCLUDED TRUE +#endif /* UC_BT_HID_ENABLED */ + +#if UC_BT_HID_HOST_ENABLED +#define HID_HOST_INCLUDED TRUE +#define BTA_HH_INCLUDED TRUE +#define BTC_HH_INCLUDED TRUE +#endif /* UC_BT_HID_HOST_ENABLED */ + +#if UC_BT_HID_DEVICE_ENABLED +#define HID_DEV_INCLUDED TRUE +#define BTA_HD_INCLUDED TRUE +#define BTC_HD_INCLUDED TRUE +#endif /* UC_BT_HID_DEVICE_ENABLED */ + +#endif /* UC_BT_CLASSIC_ENABLED */ + +/* This is set to enable use of GAP L2CAP connections. */ +#if (VND_BT_JV_BTA_L2CAP == TRUE) +#define BTA_JV_L2CAP_INCLUDED TRUE +#define GAP_CONN_INCLUDED TRUE +#endif /* VND_BT_JV_BTA_L2CAP */ + +#ifndef CLASSIC_BT_INCLUDED +#define CLASSIC_BT_INCLUDED FALSE +#endif /* CLASSIC_BT_INCLUDED */ + +#ifndef CLASSIC_BT_GATT_INCLUDED +#define CLASSIC_BT_GATT_INCLUDED FALSE +#endif /* CLASSIC_BT_GATT_INCLUDED */ +/****************************************************************************** +** +** BLE features +** +******************************************************************************/ +#if (UC_BT_BLE_ENABLED ==TRUE) +#define BLE_INCLUDED TRUE +#else +#define BLE_INCLUDED FALSE +#endif /* UC_BT_BLE_ENABLED */ + +#if (UC_BT_BLE_50_FEATURES_SUPPORTED == TRUE) +#define BLE_50_FEATURE_SUPPORT TRUE +#else +#define BLE_50_FEATURE_SUPPORT FALSE +#endif + +#if (UC_BT_BLE_42_FEATURES_SUPPORTED == TRUE || BLE_50_FEATURE_SUPPORT == FALSE) +#define BLE_42_FEATURE_SUPPORT TRUE +#else +#define BLE_42_FEATURE_SUPPORT FALSE +#endif + +#if (UC_BT_BLE_FEAT_PERIODIC_ADV_SYNC_TRANSFER == TRUE) +#define BLE_FEAT_PERIODIC_ADV_SYNC_TRANSFER TRUE +#else +#define BLE_FEAT_PERIODIC_ADV_SYNC_TRANSFER FALSE +#endif + +#if (UC_BT_BLE_FEAT_PERIODIC_ADV_ENH == TRUE) +#define BLE_FEAT_PERIODIC_ADV_ENH TRUE +#else +#define BLE_FEAT_PERIODIC_ADV_ENH FALSE +#endif + +#if (UC_BT_BLE_FEAT_CREATE_SYNC_ENH == TRUE) +#define BLE_FEAT_CREATE_SYNC_ENH TRUE +#else +#define BLE_FEAT_CREATE_SYNC_ENH FALSE +#endif + +#if (UC_BT_BLE_HIGH_DUTY_ADV_INTERVAL == TRUE) +#define BLE_HIGH_DUTY_ADV_INTERVAL TRUE +#else +#define BLE_HIGH_DUTY_ADV_INTERVAL FALSE +#endif + +#if (UC_BT_BLE_RPA_SUPPORTED == TRUE) +#define CONTROLLER_RPA_LIST_ENABLE TRUE +#else +#define CONTROLLER_RPA_LIST_ENABLE FALSE +#endif + +#if (UC_BT_GATTS_ENABLE) +#define GATTS_INCLUDED TRUE +#else +#define GATTS_INCLUDED FALSE +#endif /* UC_BT_GATTS_ENABLE */ + +#if (UC_BT_GATTC_ENABLE) +#define GATTC_INCLUDED TRUE +#else +#define GATTC_INCLUDED FALSE +#endif /* UC_BT_GATTC_ENABLE */ + +#if (UC_BT_GATTC_ENABLE && UC_BT_GATTC_CACHE_NVS_FLASH_ENABLED) +#define GATTC_CACHE_NVS TRUE +#else +#define GATTC_CACHE_NVS FALSE +#endif /* UC_BT_GATTC_ENABLE && UC_BT_GATTC_CACHE_NVS_FLASH_ENABLED */ + +#if (UC_BT_GATTC_ENABLE && UC_BT_GATTC_CONNECT_RETRY_COUNT) +#define GATTC_CONNECT_RETRY_COUNT UC_BT_GATTC_CONNECT_RETRY_COUNT +#else +#define GATTC_CONNECT_RETRY_COUNT 0 +#endif /* UC_BT_GATTC_ENABLE && UC_BT_GATTC_CONNECT_RETRY_COUNT */ + +#if (GATTC_CONNECT_RETRY_COUNT > 0) +#define GATTC_CONNECT_RETRY_EN TRUE +#else +#define GATTC_CONNECT_RETRY_EN FALSE +#endif + +#ifdef UC_BT_GATTC_NOTIF_REG_MAX +#define BTA_GATTC_NOTIF_REG_MAX UC_BT_GATTC_NOTIF_REG_MAX +#else +#define BTA_GATTC_NOTIF_REG_MAX 5 +#endif + +#if (UC_BT_SMP_ENABLE) +#define SMP_INCLUDED TRUE +#if (BLE_INCLUDED == TRUE) +#define BLE_PRIVACY_SPT TRUE +#else +#define BLE_PRIVACY_SPT FALSE +#endif /*BLE_INCLUDED*/ +#else +#define SMP_INCLUDED FALSE +#define BLE_PRIVACY_SPT FALSE +#endif /* UC_BT_SMP_ENABLE */ + +#if(UC_BT_SMP_SLAVE_CON_PARAMS_UPD_ENABLE) +#define SMP_SLAVE_CON_PARAMS_UPD_ENABLE TRUE +#else +#define SMP_SLAVE_CON_PARAMS_UPD_ENABLE FALSE +#endif /* UC_BT_SMP_SLAVE_CON_PARAMS_UPD_ENABLE */ + +#ifdef UC_BTDM_BLE_ADV_REPORT_FLOW_CTRL_SUPP +#define BLE_ADV_REPORT_FLOW_CONTROL (UC_BTDM_BLE_ADV_REPORT_FLOW_CTRL_SUPP && BLE_INCLUDED) +#endif /* UC_BTDM_BLE_ADV_REPORT_FLOW_CTRL_SUPP */ + +#ifdef UC_BTDM_BLE_ADV_REPORT_FLOW_CTRL_NUM +#define BLE_ADV_REPORT_FLOW_CONTROL_NUM UC_BTDM_BLE_ADV_REPORT_FLOW_CTRL_NUM +#endif /* UC_BTDM_BLE_ADV_REPORT_FLOW_CTRL_NUM */ + +#ifdef UC_BTDM_BLE_ADV_REPORT_DISCARD_THRSHOLD +#define BLE_ADV_REPORT_DISCARD_THRSHOLD UC_BTDM_BLE_ADV_REPORT_DISCARD_THRSHOLD +#endif /* UC_BTDM_BLE_ADV_REPORT_DISCARD_THRSHOLD */ + +#ifdef UC_BT_ACL_CONNECTIONS +#define MAX_ACL_CONNECTIONS UC_BT_ACL_CONNECTIONS +#define GATT_MAX_PHY_CHANNEL UC_BT_ACL_CONNECTIONS +#endif /* UC_BT_ACL_CONNECTIONS */ + +#ifdef UC_BT_MULTI_CONNECTION_ENBALE +#define BT_MULTI_CONNECTION_ENBALE UC_BT_MULTI_CONNECTION_ENBALE +#endif + +#if(BT_MULTI_CONNECTION_ENBALE && (CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S3)) +#define BLE_CE_LEN_MIN 5 +#else +#define BLE_CE_LEN_MIN 0 +#endif + +#ifdef UC_BT_BLE_ESTAB_LINK_CONN_TOUT +#define BLE_ESTABLISH_LINK_CONNECTION_TIMEOUT UC_BT_BLE_ESTAB_LINK_CONN_TOUT +#endif + +//------------------Added from bdroid_buildcfg.h--------------------- +#ifndef L2CAP_EXTFEA_SUPPORTED_MASK +#define L2CAP_EXTFEA_SUPPORTED_MASK (L2CAP_EXTFEA_ENH_RETRANS | L2CAP_EXTFEA_STREAM_MODE | L2CAP_EXTFEA_NO_CRC | L2CAP_EXTFEA_FIXED_CHNLS) +#endif + +#ifndef BTUI_OPS_FORMATS +#define BTUI_OPS_FORMATS (BTA_OP_VCARD21_MASK | BTA_OP_ANY_MASK) +#endif + +#ifndef BTA_RFC_MTU_SIZE +#define BTA_RFC_MTU_SIZE (L2CAP_MTU_SIZE-L2CAP_MIN_OFFSET-RFCOMM_DATA_OVERHEAD) +#endif + +#ifndef SBC_NO_PCM_CPY_OPTION +#define SBC_NO_PCM_CPY_OPTION FALSE +#endif + +#ifndef BT_APP_DEMO +#define BT_APP_DEMO TRUE +#endif + +#ifndef BTIF_INCLUDED +#define BTIF_INCLUDED FALSE +#endif + +/****************************************************************************** +** +** BTC-layer components +** +******************************************************************************/ +#ifndef BTC_GAP_BT_INCLUDED +#define BTC_GAP_BT_INCLUDED FALSE +#endif + +#ifndef BTC_PRF_QUEUE_INCLUDED +#define BTC_PRF_QUEUE_INCLUDED FALSE +#endif + +#ifndef BTC_SM_INCLUDED +#define BTC_SM_INCLUDED FALSE +#endif + +#ifndef BTC_AV_INCLUDED +#define BTC_AV_INCLUDED FALSE +#endif + +#ifndef BTC_AV_SINK_INCLUDED +#define BTC_AV_SINK_INCLUDED FALSE +#endif + +#ifndef BTC_AV_SRC_INCLUDED +#define BTC_AV_SRC_INCLUDED FALSE +#endif + +#ifndef BTC_SPP_INCLUDED +#define BTC_SPP_INCLUDED FALSE +#endif + +#ifndef BTC_HH_INCLUDED +#define BTC_HH_INCLUDED FALSE +#endif + +#ifndef BTC_HD_INCLUDED +#define BTC_HD_INCLUDED FALSE +#endif + +#ifndef SBC_DEC_INCLUDED +#define SBC_DEC_INCLUDED FALSE +#endif + +#ifndef SBC_ENC_INCLUDED +#define SBC_ENC_INCLUDED FALSE +#endif + +/****************************************************************************** +** +** BTA-layer components +** +******************************************************************************/ +#ifndef BTA_INCLUDED +#define BTA_INCLUDED TRUE +#endif + +#ifndef BTA_DM_PM_INCLUDED +#define BTA_DM_PM_INCLUDED FALSE +#endif + +#ifndef BTA_DM_QOS_INCLUDED +#define BTA_DM_QOS_INCLUDED FALSE +#endif + +#ifndef BTA_PAN_INCLUDED +#define BTA_PAN_INCLUDED FALSE +#endif + +#ifndef BTA_HD_INCLUDED +#define BTA_HD_INCLUDED FALSE +#endif + +#ifndef BTA_HH_INCLUDED +#define BTA_HH_INCLUDED FALSE +#endif + +#ifndef BTA_HH_ROLE +#define BTA_HH_ROLE BTA_MASTER_ROLE_PREF +#endif + +#ifndef BTA_HH_LE_INCLUDED +#define BTA_HH_LE_INCLUDED FALSE +#endif + +#ifndef BTA_AR_INCLUDED +#define BTA_AR_INCLUDED FALSE +#endif + +#ifndef BTA_AV_INCLUDED +#define BTA_AV_INCLUDED FALSE +#endif + +#ifndef BTA_AV_SINK_INCLUDED +#define BTA_AV_SINK_INCLUDED FALSE +#endif + +#ifndef BTA_JV_INCLUDED +#define BTA_JV_INCLUDED FALSE +#endif + +#ifndef BTA_SDP_INCLUDED +#define BTA_SDP_INCLUDED FALSE +#endif + +/* This is set to enable use of GAP L2CAP connections. */ +#ifndef VND_BT_JV_BTA_L2CAP +#define VND_BT_JV_BTA_L2CAP FALSE +#endif + +#ifndef BTA_JV_L2CAP_INCLUDED +#define BTA_JV_L2CAP_INCLUDED FALSE +#endif + +#ifndef GAP_CONN_INCLUDED +#define GAP_CONN_INCLUDED FALSE +#endif + +/****************************************************************************** +** +** Stack-layer components +** +******************************************************************************/ +#ifndef AVCT_INCLUDED +#define AVCT_INCLUDED FALSE +#endif + +#ifndef AVDT_INCLUDED +#define AVDT_INCLUDED FALSE +#endif + +/****************************************************************************** +** +** Parameter Configurations for components +** +******************************************************************************/ +#ifndef BTA_DISABLE_DELAY +#define BTA_DISABLE_DELAY 200 /* in milliseconds */ +#endif + +#ifndef BTA_SYS_TIMER_PERIOD +#define BTA_SYS_TIMER_PERIOD 100 +#endif + +#ifndef SBC_FOR_EMBEDDED_LINUX +#define SBC_FOR_EMBEDDED_LINUX TRUE +#endif + +#ifndef AVDT_VERSION +#define AVDT_VERSION 0x0102 +#endif + +#ifndef BTA_AG_AT_MAX_LEN +#define BTA_AG_AT_MAX_LEN 512 +#endif + +#ifndef BTA_AVRCP_FF_RW_SUPPORT +#define BTA_AVRCP_FF_RW_SUPPORT FALSE +#endif + +#ifndef BTA_AG_SCO_PKT_TYPES +#define BTA_AG_SCO_PKT_TYPES (BTM_SCO_LINK_ONLY_MASK | BTM_SCO_PKT_TYPES_MASK_EV3 | BTM_SCO_PKT_TYPES_MASK_NO_3_EV3 | BTM_SCO_PKT_TYPES_MASK_NO_2_EV5 | BTM_SCO_PKT_TYPES_MASK_NO_3_EV5) +#endif + +#ifndef BTA_AV_RET_TOUT +#define BTA_AV_RET_TOUT 15 +#endif + +#ifndef PORCHE_PAIRING_CONFLICT +#define PORCHE_PAIRING_CONFLICT TRUE +#endif + +#ifndef BTA_AV_CO_CP_SCMS_T +#define BTA_AV_CO_CP_SCMS_T FALSE +#endif + +#if UC_BT_BLE_HOST_QUEUE_CONGESTION_CHECK +#define SCAN_QUEUE_CONGEST_CHECK TRUE +#else +#define SCAN_QUEUE_CONGEST_CHECK FALSE +#endif + +#ifdef UC_CONFIG_BT_GATTS_PPCP_CHAR_GAP +#define BTM_PERIPHERAL_ENABLED UC_CONFIG_BT_GATTS_PPCP_CHAR_GAP +#endif + +#ifdef UC_BT_GATTS_SEND_SERVICE_CHANGE_MODE +#define GATTS_SEND_SERVICE_CHANGE_MODE UC_BT_GATTS_SEND_SERVICE_CHANGE_MODE +#endif + +#if (UC_BT_GATTS_ROBUST_CACHING_ENABLED == TRUE) +#define GATTS_ROBUST_CACHING_ENABLED TRUE +#else +#define GATTS_ROBUST_CACHING_ENABLED FALSE +#endif + +#if (UC_BT_GATTS_DEVICE_NAME_WRITABLE == TRUE) +#define GATTS_DEVICE_NAME_WRITABLE TRUE +#else +#define GATTS_DEVICE_NAME_WRITABLE FALSE +#endif + +#if (UC_BT_GATTS_APPEARANCE_WRITABLE == TRUE) +#define GATTS_APPEARANCE_WRITABLE TRUE +#else +#define GATTS_APPEARANCE_WRITABLE FALSE +#endif + +#ifdef UC_BT_BLE_ACT_SCAN_REP_ADV_SCAN +#define BTM_BLE_ACTIVE_SCAN_REPORT_ADV_SCAN_RSP_INDIVIDUALLY UC_BT_BLE_ACT_SCAN_REP_ADV_SCAN +#endif + +#ifdef UC_BT_BLE_RPA_TIMEOUT +#define BTM_BLE_PRIVATE_ADDR_INT UC_BT_BLE_RPA_TIMEOUT +#endif + +#if (UC_BT_CLASSIC_BQB_ENABLED == TRUE) +#define BT_CLASSIC_BQB_INCLUDED TRUE +#else +#define BT_CLASSIC_BQB_INCLUDED FALSE +#endif + +/* This feature is used to eanble interleaved scan*/ +#ifndef BTA_HOST_INTERLEAVE_SEARCH +#define BTA_HOST_INTERLEAVE_SEARCH FALSE +#endif + +#ifndef BT_USE_TRACES +#define BT_USE_TRACES FALSE +#endif + +#ifndef BT_TRACE_BTIF +#define BT_TRACE_BTIF TRUE +#endif + +#ifndef BT_TRACE_VERBOSE +#define BT_TRACE_VERBOSE FALSE +#endif + +#ifndef BTA_DM_SDP_DB_SIZE +#define BTA_DM_SDP_DB_SIZE 8000 +#endif + +#ifndef HL_INCLUDED +#define HL_INCLUDED TRUE +#endif + +#ifndef AG_VOICE_SETTINGS +#define AG_VOICE_SETTINGS HCI_DEFAULT_VOICE_SETTINGS +#endif + +#ifndef BTIF_DM_OOB_TEST +#define BTIF_DM_OOB_TEST FALSE +#endif + +// How long to wait before activating sniff mode after entering the +// idle state for FTS, OPS connections +#ifndef BTA_FTS_OPS_IDLE_TO_SNIFF_DELAY_MS +#define BTA_FTS_OPS_IDLE_TO_SNIFF_DELAY_MS 7000 +#endif +#ifndef BTA_FTC_OPS_IDLE_TO_SNIFF_DELAY_MS +#define BTA_FTC_OPS_IDLE_TO_SNIFF_DELAY_MS 5000 +#endif + +//------------------End added from bdroid_buildcfg.h--------------------- + + +/****************************************************************************** +** +** Buffer Size +** +******************************************************************************/ + +#ifndef BT_DEFAULT_BUFFER_SIZE +#define BT_DEFAULT_BUFFER_SIZE (4096 + 16) +#endif + +#ifndef BT_SMALL_BUFFER_SIZE +#define BT_SMALL_BUFFER_SIZE 660 +#endif + +/* Receives HCI events from the lower-layer. */ +#ifndef HCI_CMD_BUF_SIZE +#define HCI_CMD_BUF_SIZE BT_SMALL_BUFFER_SIZE +#endif + +/* Sends SDP data packets. */ +#ifndef SDP_DATA_BUF_SIZE +#define SDP_DATA_BUF_SIZE BT_DEFAULT_BUFFER_SIZE +#endif + +/* Sends RFCOMM command packets. */ +#ifndef RFCOMM_CMD_BUF_SIZE +#define RFCOMM_CMD_BUF_SIZE BT_SMALL_BUFFER_SIZE +#endif + +/* Sends RFCOMM data packets. */ +#ifndef RFCOMM_DATA_BUF_SIZE +#define RFCOMM_DATA_BUF_SIZE BT_DEFAULT_BUFFER_SIZE +#endif + +/* Sends L2CAP packets to the peer and HCI messages to the controller. */ +#ifndef L2CAP_CMD_BUF_SIZE +#define L2CAP_CMD_BUF_SIZE BT_SMALL_BUFFER_SIZE +#endif + +#ifndef L2CAP_USER_TX_BUF_SIZE +#define L2CAP_USER_TX_BUF_SIZE BT_DEFAULT_BUFFER_SIZE +#endif + +#ifndef L2CAP_USER_RX_BUF_SIZE +#define L2CAP_USER_RX_BUF_SIZE BT_DEFAULT_BUFFER_SIZE +#endif + +/* Sends L2CAP segmented packets in ERTM mode */ +#ifndef L2CAP_FCR_TX_BUF_SIZE +#define L2CAP_FCR_TX_BUF_SIZE BT_DEFAULT_BUFFER_SIZE +#endif + +/* Receives L2CAP segmented packets in ERTM mode */ +#ifndef L2CAP_FCR_RX_BUF_SIZE +#define L2CAP_FCR_RX_BUF_SIZE BT_DEFAULT_BUFFER_SIZE +#endif + +#ifndef L2CAP_FCR_ERTM_BUF_SIZE +#define L2CAP_FCR_ERTM_BUF_SIZE (10240 + 24) +#endif + +/* Number of ACL buffers to assign to LE + if the HCI buffer pool is shared with BR/EDR */ +#ifndef L2C_DEF_NUM_BLE_BUF_SHARED +#define L2C_DEF_NUM_BLE_BUF_SHARED 1 +#endif + +/* Used by BTM when it sends HCI commands to the controller. */ +#ifndef BTM_CMD_BUF_SIZE +#define BTM_CMD_BUF_SIZE BT_SMALL_BUFFER_SIZE +#endif + +#ifndef OBX_LRG_DATA_BUF_SIZE +#define OBX_LRG_DATA_BUF_SIZE (8080 + 26) +#endif + +/* Used to send data to L2CAP. */ +#ifndef GAP_DATA_BUF_SIZE +#define GAP_DATA_BUF_SIZE BT_DEFAULT_BUFFER_SIZE +#endif + +/* BNEP data and protocol messages. */ +#ifndef BNEP_BUF_SIZE +#define BNEP_BUF_SIZE BT_DEFAULT_BUFFER_SIZE +#endif + +/* AVDTP buffer size for protocol messages */ +#ifndef AVDT_CMD_BUF_SIZE +#define AVDT_CMD_BUF_SIZE BT_SMALL_BUFFER_SIZE +#endif + +/* AVDTP buffer size for media packets in case of fragmentation */ +#ifndef AVDT_DATA_BUF_SIZE +#define AVDT_DATA_BUF_SIZE BT_DEFAULT_BUFFER_SIZE +#endif + +#ifndef PAN_BUF_SIZE +#define PAN_BUF_SIZE BT_DEFAULT_BUFFER_SIZE +#endif + +/* Maximum number of buffers to allocate for PAN */ +#ifndef PAN_BUF_MAX +#define PAN_BUF_MAX 100 +#endif + +/* AVCTP buffer size for protocol messages */ +#ifndef AVCT_CMD_BUF_SIZE +#define AVCT_CMD_BUF_SIZE 288 +#endif + +/* AVRCP buffer size for protocol messages */ +#ifndef AVRC_CMD_BUF_SIZE +#define AVRC_CMD_BUF_SIZE 288 +#endif + +/* AVRCP Metadata buffer size for protocol messages */ +#ifndef AVRC_META_CMD_BUF_SIZE +#define AVRC_META_CMD_BUF_SIZE BT_SMALL_BUFFER_SIZE +#endif + +#ifndef BTA_HL_LRG_DATA_BUF_SIZE +#define BTA_HL_LRG_DATA_BUF_SIZE (10240 + 24) +#endif + +/* GATT Server Database buffer size */ +#ifndef GATT_DB_BUF_SIZE +#define GATT_DB_BUF_SIZE 128 +#endif + +/* GATT Data sending buffer size */ +#ifndef GATT_DATA_BUF_SIZE +#define GATT_DATA_BUF_SIZE BT_DEFAULT_BUFFER_SIZE +#endif + +/****************************************************************************** +** +** HCI Services (H4) +** +******************************************************************************/ + +/* Use 2 second for low-resolution systems, override to 1 for high-resolution systems */ +#ifndef BT_1SEC_TIMEOUT +#define BT_1SEC_TIMEOUT (2) +#endif + +/* Quick Timer */ +/* if L2CAP_FCR_INCLUDED is TRUE then it should have 100 millisecond resolution */ +/* if none of them is included then QUICK_TIMER_TICKS_PER_SEC is set to 0 to exclude quick timer */ +#ifndef QUICK_TIMER_TICKS_PER_SEC +#define QUICK_TIMER_TICKS_PER_SEC 10 /* 100ms timer */ +#endif + +/****************************************************************************** +** +** BTM +** +******************************************************************************/ + +/* Cancel Inquiry on incoming SSP */ +#ifndef BTM_NO_SSP_ON_INQUIRY +#define BTM_NO_SSP_ON_INQUIRY FALSE +#endif + +/* Includes SCO if TRUE */ +#ifndef BTM_SCO_INCLUDED +#define BTM_SCO_INCLUDED FALSE /* TRUE includes SCO code */ +#endif + +/* Includes SCO if TRUE */ +#ifndef BTM_SCO_HCI_INCLUDED +#if UC_BT_HFP_AUDIO_DATA_PATH_HCI +#define BTM_SCO_HCI_INCLUDED TRUE /* TRUE includes SCO over HCI code */ +#else +#define BTM_SCO_HCI_INCLUDED FALSE +#endif /* UC_HFP_AUDIO_DATA_PATH_HCI */ +#endif + +/* Includes WBS if TRUE */ +#ifndef BTM_WBS_INCLUDED +#define BTM_WBS_INCLUDED UC_BT_HFP_WBS_ENABLE /* TRUE includes WBS code */ +#endif + +/* This is used to work around a controller bug that doesn't like Disconnect +** issued while there is a role switch in progress +*/ +#ifndef BTM_DISC_DURING_RS +#define BTM_DISC_DURING_RS TRUE +#endif + +/************************** +** Initial SCO TX credit +*************************/ +/* max TX SCO data packet size */ +#ifndef BTM_SCO_DATA_SIZE_MAX +#define BTM_SCO_DATA_SIZE_MAX 120 //240 +#endif + +/* max TX eSCO data packet size */ +#ifndef BTM_MSBC_FRAME_SIZE +#define BTM_MSBC_FRAME_SIZE 60 +#endif + +/* TX eSCO data packet size */ +#ifndef BTM_MSBC_FRAME_DATA_SIZE +#define BTM_MSBC_FRAME_DATA_SIZE 57 +#endif + +/* The size in bytes of the BTM inquiry database. 5 As Default */ +#ifndef BTM_INQ_DB_SIZE +#define BTM_INQ_DB_SIZE 5 +#endif + +/* The default scan mode */ +#ifndef BTM_DEFAULT_SCAN_TYPE +#define BTM_DEFAULT_SCAN_TYPE BTM_SCAN_TYPE_INTERLACED +#endif + +/* Should connections to unknown devices be allowed when not discoverable? */ +#ifndef BTM_ALLOW_CONN_IF_NONDISCOVER +#define BTM_ALLOW_CONN_IF_NONDISCOVER TRUE +#endif + +/* Sets the Page_Scan_Window: the length of time that the device is performing a page scan. */ +#ifndef BTM_DEFAULT_CONN_WINDOW +#define BTM_DEFAULT_CONN_WINDOW 0x0012 +#endif + +/* Sets the Page_Scan_Activity: the interval between the start of two consecutive page scans. */ +#ifndef BTM_DEFAULT_CONN_INTERVAL +#define BTM_DEFAULT_CONN_INTERVAL 0x0800 +#endif + +/* When automatic inquiry scan is enabled, this sets the inquiry scan window. */ +#ifndef BTM_DEFAULT_DISC_WINDOW +#define BTM_DEFAULT_DISC_WINDOW 0x0012 +#endif + +/* When automatic inquiry scan is enabled, this sets the inquiry scan interval. */ +#ifndef BTM_DEFAULT_DISC_INTERVAL +#define BTM_DEFAULT_DISC_INTERVAL 0x0800 +#endif + +/* +* {SERVICE_CLASS, MAJOR_CLASS, MINOR_CLASS} +* +* SERVICE_CLASS:0x5A (Bit17 -Networking,Bit19 - Capturing,Bit20 -Object Transfer,Bit22 -Telephony) +* MAJOR_CLASS:0x02 - PHONE +* MINOR_CLASS:0x0C - SMART_PHONE +* +*/ +#define BTA_DM_COD_SMARTPHONE {0x5A, 0x02, 0x0C} + +/* +* {SERVICE_CLASS, MAJOR_CLASS, MINOR_CLASS} +* +* SERVICE_CLASS:0x2C (Bit21 - Audio, Bit19 - Capturing) +* MAJOR_CLASS:0x04 - Audio/Video +* MINOR_CLASS:0x05 - LoudSpeaker +*/ +#define BTA_DM_COD_LOUDSPEAKER {0x2C, 0x04, 0x14} + +/* +* {SERVICE_CLASS, MAJOR_CLASS, MINOR_CLASS} +* +* SERVICE_CLASS:0x00 None +* MAJOR_CLASS:0x1f - Uncategorized: device code not specified +* MINOR_CLASS:0x00 - None +*/ +#define BTA_DM_COD_UNCLASSIFIED {0x00, 0x1f, 0x00} + +/* Default class of device */ +#ifndef BTA_DM_COD +#define BTA_DM_COD BTA_DM_COD_UNCLASSIFIED +#endif + +/* The number of SCO links. */ +#ifndef BTM_MAX_SCO_LINKS +#define BTM_MAX_SCO_LINKS 0 //3 +#endif + +/* The preferred type of SCO links (2-eSCO, 0-SCO). */ +#ifndef BTM_DEFAULT_SCO_MODE +#define BTM_DEFAULT_SCO_MODE 2 +#endif + +/* The number of security records for peer devices. 15 AS Default*/ +#ifndef BTM_SEC_MAX_DEVICE_RECORDS +#define BTM_SEC_MAX_DEVICE_RECORDS UC_BT_SMP_MAX_BONDS +#endif + +/* The number of security records for services. 32 AS Default*/ +#ifndef BTM_SEC_MAX_SERVICE_RECORDS +#define BTM_SEC_MAX_SERVICE_RECORDS 8 // 32 +#endif + +/* If True, force a retrieval of remote device name for each bond in case it's changed */ +#ifndef BTM_SEC_FORCE_RNR_FOR_DBOND +#define BTM_SEC_FORCE_RNR_FOR_DBOND FALSE +#endif + +/* Maximum device name length used in btm database. Up to 248*/ +#ifndef BTM_MAX_REM_BD_NAME_LEN +#define BTM_MAX_REM_BD_NAME_LEN 64 +#endif + +/* Maximum local device name length stored btm database. + '0' disables storage of the local name in BTM */ +#if UC_MAX_LOC_BD_NAME_LEN +#define BTM_MAX_LOC_BD_NAME_LEN UC_MAX_LOC_BD_NAME_LEN +#define BTC_MAX_LOC_BD_NAME_LEN BTM_MAX_LOC_BD_NAME_LEN +#else +#define BTM_MAX_LOC_BD_NAME_LEN 64 +#define BTC_MAX_LOC_BD_NAME_LEN BTM_MAX_LOC_BD_NAME_LEN +#endif + +/* Fixed Default String. When this is defined as null string, the device's + * product model name is used as the default local name. + */ +#ifndef BTM_DEF_LOCAL_NAME +#define BTM_DEF_LOCAL_NAME "" +#endif + +/* Maximum service name stored with security authorization (0 if not needed) */ +#ifndef BTM_SEC_SERVICE_NAME_LEN +#define BTM_SEC_SERVICE_NAME_LEN BT_MAX_SERVICE_NAME_LEN +#endif + +/* Maximum length of the service name. */ +#ifndef BT_MAX_SERVICE_NAME_LEN +#define BT_MAX_SERVICE_NAME_LEN 21 +#endif + +/* ACL buffer size in HCI Host Buffer Size command. */ +#ifndef BTM_ACL_BUF_SIZE +#define BTM_ACL_BUF_SIZE 0 +#endif + +/* The maximum number of clients that can register with the power manager. */ +#ifndef BTM_MAX_PM_RECORDS +#define BTM_MAX_PM_RECORDS 2 +#endif + +/* This is set to show debug trace messages for the power manager. */ +#ifndef BTM_PM_DEBUG +#define BTM_PM_DEBUG FALSE +#endif + +/* This is set to TRUE if link is to be unparked due to BTM_CreateSCO API. */ +#ifndef BTM_SCO_WAKE_PARKED_LINK +#define BTM_SCO_WAKE_PARKED_LINK FALSE +#endif + +/* If the user does not respond to security process requests within this many seconds, + * a negative response would be sent automatically. + * 30 is LMP response timeout value */ +#ifndef BTM_SEC_TIMEOUT_VALUE +#define BTM_SEC_TIMEOUT_VALUE 35 +#endif + +/* Maximum number of callbacks that can be registered using BTM_RegisterForVSEvents */ +#ifndef BTM_MAX_VSE_CALLBACKS +#define BTM_MAX_VSE_CALLBACKS 3 +#endif + +/****************************************** +** Lisbon Features +*******************************************/ +/* This is set to TRUE if the FEC is required for EIR packet. */ +#ifndef BTM_EIR_DEFAULT_FEC_REQUIRED +#define BTM_EIR_DEFAULT_FEC_REQUIRED TRUE +#endif + +/* The IO capability of the local device (for Simple Pairing) */ +#ifndef BTM_LOCAL_IO_CAPS +#define BTM_LOCAL_IO_CAPS BTM_IO_CAP_NONE +#endif + +#ifndef BTM_LOCAL_IO_CAPS_BLE +#if (BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE) +#define BTM_LOCAL_IO_CAPS_BLE BTM_IO_CAP_KBDISP +#else +#define BTM_LOCAL_IO_CAPS_BLE 4 +#endif ///BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE +#endif + +/* The default MITM Protection Requirement (for Simple Pairing) + * Possible values are BTM_AUTH_SP_YES or BTM_AUTH_SP_NO */ +#ifndef BTM_DEFAULT_AUTH_REQ +#define BTM_DEFAULT_AUTH_REQ BTM_AUTH_SP_NO +#endif + +/* The default MITM Protection Requirement for dedicated bonding using Simple Pairing + * Possible values are BTM_AUTH_AP_YES or BTM_AUTH_AP_NO */ +#ifndef BTM_DEFAULT_DD_AUTH_REQ +#define BTM_DEFAULT_DD_AUTH_REQ BTM_AUTH_AP_YES +#endif + +/* Include Out-of-Band implementation for Simple Pairing */ +#ifndef BTM_OOB_INCLUDED +#define BTM_OOB_INCLUDED TRUE +#endif + +/* TRUE to include Sniff Subrating */ +#if (BTA_DM_PM_INCLUDED == TRUE) +#ifndef BTM_SSR_INCLUDED +#define BTM_SSR_INCLUDED FALSE +#endif +#endif /* BTA_DM_PM_INCLUDED */ + +/************************* +** End of Lisbon Features +**************************/ + +/* 4.1/4.2 secure connections feature */ +#ifndef SC_MODE_INCLUDED +// Disable AES-CCM (BT 4.1) for BT Classic to workaround controller AES issue. E0 encryption (BT 4.0) will be used. +#define SC_MODE_INCLUDED FALSE +#endif + +/* Used for conformance testing ONLY */ +#ifndef BTM_BLE_CONFORMANCE_TESTING +#define BTM_BLE_CONFORMANCE_TESTING FALSE +#endif + +/****************************************************************************** +** +** CONTROLLER TO HOST FLOW CONTROL +** +******************************************************************************/ + +#define C2H_FLOW_CONTROL_INCLUDED TRUE + +/****************************************************************************** +** +** L2CAP +** +******************************************************************************/ + +#ifndef L2CAP_CLIENT_INCLUDED +#define L2CAP_CLIENT_INCLUDED FALSE +#endif + +/* The maximum number of simultaneous applications that can register with LE L2CAP. */ +#ifndef BLE_MAX_L2CAP_CLIENTS +#define BLE_MAX_L2CAP_CLIENTS 15 +#endif + +/* Support status of L2CAP connection-oriented dynamic channels over LE transport with dynamic CID */ +#ifndef BLE_L2CAP_COC_INCLUDED +#define BLE_L2CAP_COC_INCLUDED FALSE // LE COC not use by default +#endif + +/* Support status of L2CAP connection-oriented dynamic channels over LE or BR/EDR transport with dynamic CID */ +#ifndef L2CAP_COC_INCLUDED +#if (CLASSIC_BT_INCLUDED == TRUE || BLE_L2CAP_COC_INCLUDED == TRUE) +#define L2CAP_COC_INCLUDED TRUE +#else +#define L2CAP_COC_INCLUDED FALSE +#endif +#endif + +/* The maximum number of simultaneous links that L2CAP can support. Up to 7*/ +#ifndef MAX_ACL_CONNECTIONS +#define MAX_L2CAP_LINKS 5 +#else +#define MAX_L2CAP_LINKS MAX_ACL_CONNECTIONS +#endif + +/* The maximum number of simultaneous channels that L2CAP can support. Up to 16*/ +#ifndef MAX_L2CAP_CHANNELS +#if (CLASSIC_BT_INCLUDED == TRUE) +#define MAX_L2CAP_CHANNELS 16 +#else +#if (SMP_INCLUDED == FALSE) +#define MAX_L2CAP_CHANNELS MAX_ACL_CONNECTIONS //This is used in the BLE client when start connected with the peer device +#else +#define MAX_L2CAP_CHANNELS (MAX_ACL_CONNECTIONS * 2) //This is used in the BLE client when start connected with the peer device and in SMP +#endif ///SMP_INCLUDED == FALSE +#endif ///CLASSIC_BT_INCLUDED == TRUE +#endif ///MAX_L2CAP_CHANNELS + +/* The maximum number of simultaneous applications that can register with L2CAP. */ +#ifndef MAX_L2CAP_CLIENTS +#if (CLASSIC_BT_INCLUDED == TRUE) +#define MAX_L2CAP_CLIENTS 8 +#else +#define MAX_L2CAP_CLIENTS 1 //Not support to allocate a channel control block in BLE only mode +#endif ///CLASSIC_BT_INCLUDED == TRUE +#endif + +/* The number of seconds of link inactivity before a link is disconnected. */ +#ifndef L2CAP_LINK_INACTIVITY_TOUT +#define L2CAP_LINK_INACTIVITY_TOUT 4 +#endif + +/* The number of seconds of link inactivity after bonding before a link is disconnected. */ +#ifndef L2CAP_BONDING_TIMEOUT +#define L2CAP_BONDING_TIMEOUT 3 +#endif + +/* The time from the HCI connection complete to disconnect if no channel is established. */ +#ifndef L2CAP_LINK_STARTUP_TOUT +#define L2CAP_LINK_STARTUP_TOUT 60 +#endif + +/* The L2CAP MTU; must be in accord with the HCI ACL pool size. */ +#ifndef L2CAP_MTU_SIZE +#define L2CAP_MTU_SIZE 1691 +#endif + +/* The L2CAP MPS over Bluetooth; must be in accord with the FCR tx pool size and ACL down buffer size. */ +#ifndef L2CAP_MPS_OVER_BR_EDR +#define L2CAP_MPS_OVER_BR_EDR 1010 +#endif + +/* If host flow control enabled, this is the number of buffers the controller can have unacknowledged. */ +#ifndef L2CAP_HOST_FC_ACL_BUFS +#define L2CAP_HOST_FC_ACL_BUFS 20 +#endif + +/* This is set to enable L2CAP to take the ACL link out of park mode when ACL data is to be sent. */ +#ifndef L2CAP_WAKE_PARKED_LINK +#define L2CAP_WAKE_PARKED_LINK TRUE +#endif + +/* Whether link wants to be the master or the slave. */ +#ifndef L2CAP_DESIRED_LINK_ROLE +#define L2CAP_DESIRED_LINK_ROLE HCI_ROLE_SLAVE +#endif + +/* Include Non-Flushable Packet Boundary Flag feature of Lisbon */ +#ifndef L2CAP_NON_FLUSHABLE_PB_INCLUDED +#define L2CAP_NON_FLUSHABLE_PB_INCLUDED TRUE +#endif + +/* Minimum number of ACL credit for high priority link */ +#ifndef L2CAP_HIGH_PRI_MIN_XMIT_QUOTA +#define L2CAP_HIGH_PRI_MIN_XMIT_QUOTA 5 +#endif + +/* used for monitoring HCI ACL credit management */ +#ifndef L2CAP_HCI_FLOW_CONTROL_DEBUG +#define L2CAP_HCI_FLOW_CONTROL_DEBUG TRUE +#endif + +/* Used for calculating transmit buffers off of */ +#ifndef L2CAP_NUM_XMIT_BUFFS +#define L2CAP_NUM_XMIT_BUFFS HCI_ACL_BUF_MAX +#endif + +/* Unicast Connectionless Data */ +#ifndef L2CAP_UCD_INCLUDED +#define L2CAP_UCD_INCLUDED FALSE +#endif + +/* Unicast Connectionless Data MTU */ +#ifndef L2CAP_UCD_MTU +#define L2CAP_UCD_MTU L2CAP_MTU_SIZE +#endif + +/* Unicast Connectionless Data Idle Timeout */ +#ifndef L2CAP_UCD_IDLE_TIMEOUT +#define L2CAP_UCD_IDLE_TIMEOUT 2 +#endif + +/* Unicast Connectionless Data Idle Timeout */ +#ifndef L2CAP_UCD_CH_PRIORITY +#define L2CAP_UCD_CH_PRIORITY L2CAP_CHNL_PRIORITY_MEDIUM +#endif + +/* Used for features using fixed channels; set to zero if no fixed channels supported (BLE, etc.) */ +/* Excluding L2CAP signaling channel and UCD */ +#ifndef L2CAP_NUM_FIXED_CHNLS +#if (CLASSIC_BT_INCLUDED == TRUE) +#define L2CAP_NUM_FIXED_CHNLS 32 +#else +#define L2CAP_NUM_FIXED_CHNLS 3 //There are just three fix channel in the BLE only mode(gatt,signal,smp) +#endif ///CLASSIC_BT_INCLUDED == TRUE +#endif + +/* First fixed channel supported */ +#ifndef L2CAP_FIRST_FIXED_CHNL +#define L2CAP_FIRST_FIXED_CHNL 4 +#endif + +#ifndef L2CAP_LAST_FIXED_CHNL +#define L2CAP_LAST_FIXED_CHNL (L2CAP_FIRST_FIXED_CHNL + L2CAP_NUM_FIXED_CHNLS - 1) +#endif + +/* Round Robin service channels in link */ +#ifndef L2CAP_ROUND_ROBIN_CHANNEL_SERVICE +#define L2CAP_ROUND_ROBIN_CHANNEL_SERVICE TRUE +#endif + +/* Used for calculating transmit buffers off of */ +#ifndef L2CAP_NUM_XMIT_BUFFS +#define L2CAP_NUM_XMIT_BUFFS HCI_ACL_BUF_MAX +#endif + +/* used for monitoring eL2CAP data flow */ +#ifndef L2CAP_ERTM_STATS +#define L2CAP_ERTM_STATS FALSE +#endif + +/* Used for conformance testing ONLY: When TRUE lets scriptwrapper overwrite info response */ +#ifndef L2CAP_CONFORMANCE_TESTING +#define L2CAP_CONFORMANCE_TESTING FALSE +#endif + +/* + * Max bytes per connection to buffer locally before dropping the + * connection if local client does not receive it - default is 1MB + */ +#ifndef L2CAP_MAX_RX_BUFFER +#define L2CAP_MAX_RX_BUFFER 0x100000 +#endif + + +#ifndef TIMER_PARAM_TYPE +#define TIMER_PARAM_TYPE UINT32 +#endif + +/****************************************************************************** +** +** BLE +** +******************************************************************************/ + +#ifndef BLE_INCLUDED +#define BLE_INCLUDED FALSE +#endif + +#ifndef BLE_ANDROID_CONTROLLER_SCAN_FILTER +#define BLE_ANDROID_CONTROLLER_SCAN_FILTER TRUE +#endif + +#ifndef LOCAL_BLE_CONTROLLER_ID +#define LOCAL_BLE_CONTROLLER_ID (1) +#endif + +/* + * Toggles support for general LE privacy features such as remote address + * resolution, local address rotation etc. + */ +#ifndef BLE_PRIVACY_SPT +#define BLE_PRIVACY_SPT FALSE +#endif + +/* + * Enables or disables support for local privacy (ex. address rotation) + */ +#ifndef BLE_LOCAL_PRIVACY_ENABLED +#define BLE_LOCAL_PRIVACY_ENABLED TRUE +#endif + +/* + * Toggles support for vendor specific extensions such as RPA offloading, + * feature discovery, multi-adv etc. + */ +#ifndef BLE_VND_INCLUDED +#define BLE_VND_INCLUDED FALSE +#endif + +#ifndef BTM_BLE_ADV_TX_POWER +#ifdef CONFIG_IDF_TARGET_ESP32 +#define BTM_BLE_ADV_TX_POWER {-12, -9, -6, -3, 0, 3, 6, 9} +#else +#define BTM_BLE_ADV_TX_POWER {-24, -21, -18, -15, -12, -9, -6, -3, 0, 3, 6, 9, 12, 15, 18, 21} +#endif +#endif + +#ifndef BTM_TX_POWER +#ifdef CONFIG_IDF_TARGET_ESP32 +#define BTM_TX_POWER {-12, -9, -6, -3, 0, 3, 6, 9} +#else +#define BTM_TX_POWER {-24, -21, -18, -15, -12, -9, -6, -3, 0, 3, 6, 9, 12, 15, 18, 21} +#endif +#endif + +#ifndef BTM_TX_POWER_LEVEL_MAX +#ifdef CONFIG_IDF_TARGET_ESP32 +#define BTM_TX_POWER_LEVEL_MAX 7 +#else +#define BTM_TX_POWER_LEVEL_MAX 15 +#endif +#endif + + +#ifndef BLE_BATCH_SCAN_INCLUDED +#define BLE_BATCH_SCAN_INCLUDED TRUE +#endif + +/****************************************************************************** +** +** ATT/GATT Protocol/Profile Settings +** +******************************************************************************/ +#ifndef GATT_INCLUDED +#if BLE_INCLUDED == TRUE +#define GATT_INCLUDED TRUE +#else +#define GATT_INCLUDED FALSE +#endif +#endif + +#ifndef BTA_GATT_INCLUDED +#if BLE_INCLUDED == TRUE +#define BTA_GATT_INCLUDED TRUE +#else +#define BTA_GATT_INCLUDED FALSE +#endif +#endif + +#if BTA_GATT_INCLUDED == TRUE && BLE_INCLUDED == FALSE +#error "can't have GATT without BLE" +#endif + +#ifndef BLE_LLT_INCLUDED +#define BLE_LLT_INCLUDED TRUE +#endif + +/* Added this marco to fixed the android 7.0 will lead to update connection parameters + collision when the slave sent the HCI_BLE_UPD_LL_CONN_PARAMS comment to the controller + request the master to update connection parameters directly. */ +#ifndef BLE_SLAVE_UPD_CONN_PARAMS +#define BLE_SLAVE_UPD_CONN_PARAMS FALSE +#endif + +#ifndef ATT_INCLUDED +#define ATT_INCLUDED TRUE +#endif + +#ifndef ATT_DEBUG +#define ATT_DEBUG FALSE +#endif + +#ifndef BLE_PERIPHERAL_MODE_SUPPORT +#define BLE_PERIPHERAL_MODE_SUPPORT TRUE +#endif + +#ifndef BLE_DELAY_REQUEST_ENC +/* This flag is to work around IPHONE problem, We need to wait for iPhone ready + before send encryption request to iPhone */ +#define BLE_DELAY_REQUEST_ENC FALSE +#endif + +#ifndef GAP_TRANSPORT_SUPPORTED +#define GAP_TRANSPORT_SUPPORTED GATT_TRANSPORT_LE_BR_EDR +#endif + +#ifndef GATTP_TRANSPORT_SUPPORTED +#define GATTP_TRANSPORT_SUPPORTED GATT_TRANSPORT_LE_BR_EDR +#endif + +#ifndef GATT_MAX_SR_PROFILES +#define GATT_MAX_SR_PROFILES UC_CONFIG_BT_GATT_MAX_SR_PROFILES +#endif + +#ifndef GATT_MAX_APPS +#define GATT_MAX_APPS 8 /* MAX is 32 note: 2 apps used internally GATT and GAP */ +#endif + +#ifndef GATT_MAX_PHY_CHANNEL +#define GATT_MAX_PHY_CHANNEL 7 +#endif + +/* Used for conformance testing ONLY */ +#ifndef GATT_CONFORMANCE_TESTING +#define GATT_CONFORMANCE_TESTING FALSE +#endif + +/* number of background connection device allowence, ideally to be the same as WL size +*/ +#ifndef GATT_MAX_BG_CONN_DEV +#define GATT_MAX_BG_CONN_DEV 8 /*MAX is 32*/ +#endif + +/****************************************************************************** +** +** GATT +** +******************************************************************************/ +#ifndef GATTC_INCLUDED +#if BLE_INCLUDED == TRUE +#define GATTC_INCLUDED FALSE +#else +#define GATTC_INCLUDED FALSE +#endif +#endif + +#ifndef GATTS_INCLUDED +#if BLE_INCLUDED == TRUE +#define GATTS_INCLUDED TRUE +#else +#define GATTS_INCLUDED FALSE +#endif +#endif + +/****************************************************************************** +** +** SMP +** +******************************************************************************/ +#ifndef SMP_INCLUDED +#if BLE_INCLUDED == TRUE +#define SMP_INCLUDED FALSE +#else +#define SMP_INCLUDED FALSE +#endif +#endif + +#ifndef SMP_DEBUG +#define SMP_DEBUG FALSE +#endif + +#ifndef SMP_DEFAULT_AUTH_REQ +#define SMP_DEFAULT_AUTH_REQ SMP_AUTH_NB_ENC_ONLY +#endif + +#ifndef SMP_MAX_ENC_KEY_SIZE +#define SMP_MAX_ENC_KEY_SIZE 16 +#endif + +#ifndef SMP_MIN_ENC_KEY_SIZE +#define SMP_MIN_ENC_KEY_SIZE 7 +#endif + +/* minimum link timeout after SMP pairing is done, leave room for key exchange + and racing condition for the following service connection. + Prefer greater than 0 second, and no less than default inactivity link idle + timer(L2CAP_LINK_INACTIVITY_TOUT) in l2cap) */ +#ifndef SMP_LINK_TOUT_MIN +#if (L2CAP_LINK_INACTIVITY_TOUT > 0) +#define SMP_LINK_TOUT_MIN L2CAP_LINK_INACTIVITY_TOUT +#else +#define SMP_LINK_TOUT_MIN 2 +#endif +#endif + +/****************************************************************************** +** +** SDP +** +******************************************************************************/ + +#ifndef SDP_INCLUDED +#define SDP_INCLUDED FALSE +#endif + +#if (SDP_INCLUDED == TRUE) && (BTA_JV_INCLUDED == TRUE) && (BT_CLASSIC_BQB_INCLUDED == TRUE) +#define BT_SDP_BQB_INCLUDED TRUE +#else +#define BT_SDP_BQB_INCLUDED FALSE +#endif + +/* This is set to enable SDP server functionality. */ +#ifndef SDP_SERVER_ENABLED +#if SDP_INCLUDED == TRUE +#define SDP_SERVER_ENABLED TRUE +#else +#define SDP_SERVER_ENABLED FALSE +#endif +#endif + +/* This is set to enable SDP client functionality. */ +#ifndef SDP_CLIENT_ENABLED +#if SDP_INCLUDED == TRUE +#define SDP_CLIENT_ENABLED TRUE +#else +#define SDP_CLIENT_ENABLED FALSE +#endif +#endif + +/* The maximum number of SDP records the server can support. */ +#ifndef SDP_MAX_RECORDS +#define SDP_MAX_RECORDS 6 /*max is 30*/ +#endif + +/* The maximum number of attributes in each record. */ +#ifndef SDP_MAX_REC_ATTR +#if (defined(HID_DEV_INCLUDED) && (HID_DEV_INCLUDED==TRUE)) || (defined(BTC_SDP_INCLUDED) && (BTC_SDP_INCLUDED==TRUE)) +#define SDP_MAX_REC_ATTR 25 +#else +#define SDP_MAX_REC_ATTR 8 +#endif /* defined(HID_DEV_INCLUDED) && (HID_DEV_INCLUDED==TRUE) */ +#endif + +#ifndef SDP_MAX_PAD_LEN +#define SDP_MAX_PAD_LEN 300 +#endif + +/* The maximum length, in bytes, of an attribute. */ +#ifndef SDP_MAX_ATTR_LEN +#define SDP_MAX_ATTR_LEN 400 +#endif + +/* The maximum number of attribute filters supported by SDP databases. */ +#ifndef SDP_MAX_ATTR_FILTERS +#define SDP_MAX_ATTR_FILTERS 15 +#endif + +/* The maximum number of UUID filters supported by SDP databases. */ +#ifndef SDP_MAX_UUID_FILTERS +#define SDP_MAX_UUID_FILTERS 3 +#endif + +/* The maximum number of record handles retrieved in a search. */ +#ifndef SDP_MAX_DISC_SERVER_RECS +#define SDP_MAX_DISC_SERVER_RECS 21 +#endif + +/* The size of a scratchpad buffer, in bytes, for storing the response to an attribute request. */ +#ifndef SDP_MAX_LIST_BYTE_COUNT +#define SDP_MAX_LIST_BYTE_COUNT 4096 +#endif + +/* The maximum number of parameters in an SDP protocol element. */ +#ifndef SDP_MAX_PROTOCOL_PARAMS +#define SDP_MAX_PROTOCOL_PARAMS 2 +#endif + +/* The maximum number of simultaneous client and server connections. */ +#ifndef SDP_MAX_CONNECTIONS +#define SDP_MAX_CONNECTIONS 4 +#endif + +/* The MTU size for the L2CAP configuration. */ +#ifndef SDP_MTU_SIZE +#define SDP_MTU_SIZE 672 +#endif + +/* The flush timeout for the L2CAP configuration. */ +#ifndef SDP_FLUSH_TO +#define SDP_FLUSH_TO 0xFFFF +#endif + +/* The name for security authorization. */ +#ifndef SDP_SERVICE_NAME +#define SDP_SERVICE_NAME "Service Discovery" +#endif + +/* The security level for BTM. */ +#ifndef SDP_SECURITY_LEVEL +#define SDP_SECURITY_LEVEL BTM_SEC_NONE +#endif + +/****************************************************************************** +** +** RFCOMM +** +******************************************************************************/ +#ifndef RFCOMM_INCLUDED +#define RFCOMM_INCLUDED FALSE +#endif + +#if (RFCOMM_INCLUDED == TRUE) && (BT_CLASSIC_BQB_INCLUDED == TRUE) +#define BT_RFCOMM_BQB_INCLUDED TRUE +#else +#define BT_RFCOMM_BQB_INCLUDED FALSE +#endif + +#ifndef BTA_JV_RFCOMM_INCLUDED +#define BTA_JV_RFCOMM_INCLUDED FALSE +#endif + +/* The maximum number of ports supported. */ +#ifndef MAX_RFC_PORTS +#define MAX_RFC_PORTS 8 /*max is 30*/ +#endif + +/* The maximum simultaneous links to different devices. */ +#ifndef MAX_ACL_CONNECTIONS +#define MAX_BD_CONNECTIONS 3 /*max is 7*/ +#else +#define MAX_BD_CONNECTIONS MAX_ACL_CONNECTIONS +#endif + +/* The port receive queue low watermark level, in bytes. */ +#ifndef PORT_RX_LOW_WM +#define PORT_RX_LOW_WM (BTA_RFC_MTU_SIZE * PORT_RX_BUF_LOW_WM) +#endif + +/* The port receive queue high watermark level, in bytes. */ +#ifndef PORT_RX_HIGH_WM +#define PORT_RX_HIGH_WM (BTA_RFC_MTU_SIZE * PORT_RX_BUF_HIGH_WM) +#endif + +/* The port receive queue critical watermark level, in bytes. */ +#ifndef PORT_RX_CRITICAL_WM +#define PORT_RX_CRITICAL_WM (BTA_RFC_MTU_SIZE * PORT_RX_BUF_CRITICAL_WM) +#endif + +/* The port receive queue low watermark level, in number of buffers. */ +#ifndef PORT_RX_BUF_LOW_WM +#define PORT_RX_BUF_LOW_WM 4 +#endif + +/* The port receive queue high watermark level, in number of buffers. */ +#ifndef PORT_RX_BUF_HIGH_WM +#define PORT_RX_BUF_HIGH_WM 10 +#endif + +/* The port receive queue critical watermark level, in number of buffers. */ +#ifndef PORT_RX_BUF_CRITICAL_WM +#define PORT_RX_BUF_CRITICAL_WM 15 +#endif + +/* The port transmit queue high watermark level, in bytes. */ +#ifndef PORT_TX_HIGH_WM +#define PORT_TX_HIGH_WM (BTA_RFC_MTU_SIZE * PORT_TX_BUF_HIGH_WM) +#endif + +/* The port transmit queue critical watermark level, in bytes. */ +#ifndef PORT_TX_CRITICAL_WM +#define PORT_TX_CRITICAL_WM (BTA_RFC_MTU_SIZE * PORT_TX_BUF_CRITICAL_WM) +#endif + +/* The port transmit queue high watermark level, in number of buffers. */ +#ifndef PORT_TX_BUF_HIGH_WM +#define PORT_TX_BUF_HIGH_WM 10 +#endif + +/* The port transmit queue high watermark level, in number of buffers. */ +#ifndef PORT_TX_BUF_CRITICAL_WM +#define PORT_TX_BUF_CRITICAL_WM 15 +#endif + +/* The RFCOMM multiplexer preferred flow control mechanism. */ +#ifndef PORT_FC_DEFAULT +#define PORT_FC_DEFAULT PORT_FC_CREDIT +#endif + +/* The maximum number of credits receiver sends to peer when using credit-based flow control. */ +#ifndef PORT_CREDIT_RX_MAX +#define PORT_CREDIT_RX_MAX 16 +#endif + +/* The credit low watermark level. */ +#ifndef PORT_CREDIT_RX_LOW +#define PORT_CREDIT_RX_LOW 8 +#endif + +/* ERTM Tx window size */ +#ifndef RFC_FCR_OPT_TX_WINDOW_SIZE +#define RFC_FCR_OPT_TX_WINDOW_SIZE 10 +#endif + +/* ERTM Maximum transmissions before disconnecting */ +#ifndef RFC_FCR_OPT_MAX_TX_B4_DISCNT +#define RFC_FCR_OPT_MAX_TX_B4_DISCNT 20 +#endif + +/* ERTM Retransmission timeout (2 secs) */ +#ifndef RFC_FCR_OPT_RETX_TOUT +#define RFC_FCR_OPT_RETX_TOUT 2000 +#endif + +/* ERTM Monitor timeout (12 secs) */ +#ifndef RFC_FCR_OPT_MONITOR_TOUT +#define RFC_FCR_OPT_MONITOR_TOUT 12000 +#endif + +/* ERTM ERTM MPS segment size */ +#ifndef RFC_FCR_OPT_MAX_PDU_SIZE +#define RFC_FCR_OPT_MAX_PDU_SIZE 1010 +#endif + +/****************************************************************************** +** +** OBEX +** +******************************************************************************/ + +/* + * Buffer size to reassemble the SDU. + * It will allow buffers to be used that are larger than the L2CAP_MAX_MTU. + */ +#ifndef OBX_USER_RX_BUF_SIZE +#define OBX_USER_RX_BUF_SIZE OBX_LRG_DATA_BUF_SIZE +#endif + +/* + * Buffer size to hold the SDU. + * It will allow buffers to be used that are larger than the L2CAP_MAX_MTU. + */ +#ifndef OBX_USER_TX_BUF_SIZE +#define OBX_USER_TX_BUF_SIZE OBX_LRG_DATA_BUF_SIZE +#endif + +/* Buffer size used to hold MPS segments during SDU reassembly. */ +#ifndef OBX_FCR_RX_BUF_SIZE +#define OBX_FCR_RX_BUF_SIZE BT_DEFAULT_BUFFER_SIZE +#endif + +/* + * Buffer size used to hold MPS segments used in (re)transmissions. + * The size of each buffer must be able to hold the maximum MPS segment size + * passed in L2CA_SetFCROptions plus BT_HDR (8) + HCI preamble (4) + + * L2CAP_MIN_OFFSET (11 - as of BT 2.1 + EDR Spec). + */ +#ifndef OBX_FCR_TX_BUF_SIZE +#define OBX_FCR_TX_BUF_SIZE BT_DEFAULT_BUFFER_SIZE +#endif + +/* + * Size of the transmission window when using enhanced retransmission mode. Not used + * in basic and streaming modes. Range: 1 - 63 + */ +#ifndef OBX_FCR_OPT_TX_WINDOW_SIZE_BR_EDR +#define OBX_FCR_OPT_TX_WINDOW_SIZE_BR_EDR 20 +#endif + +/* + * Number of transmission attempts for a single I-Frame before taking + * Down the connection. Used In ERTM mode only. Value is Ignored in basic and + * Streaming modes. + * Range: 0, 1-0xFF + * 0 - infinite retransmissions + * 1 - single transmission + */ +#ifndef OBX_FCR_OPT_MAX_TX_B4_DISCNT +#define OBX_FCR_OPT_MAX_TX_B4_DISCNT 20 +#endif + +/* + * Retransmission Timeout + * Range: Minimum 2000 (2 secs) on BR/EDR when supporting PBF. + */ +#ifndef OBX_FCR_OPT_RETX_TOUT +#define OBX_FCR_OPT_RETX_TOUT 2000 +#endif + +/* + * Monitor Timeout + * Range: Minimum 12000 (12 secs) on BR/EDR when supporting PBF. + */ +#ifndef OBX_FCR_OPT_MONITOR_TOUT +#define OBX_FCR_OPT_MONITOR_TOUT 12000 +#endif + +/* + * Maximum PDU payload size. + * Suggestion: The maximum amount of data that will fit into a 3-DH5 packet. + * Range: 2 octets +*/ +#ifndef OBX_FCR_OPT_MAX_PDU_SIZE +#define OBX_FCR_OPT_MAX_PDU_SIZE L2CAP_MPS_OVER_BR_EDR +#endif + +/* + * Pool ID where to reassemble the SDU. + * This Pool will allow buffers to be used that are larger than + * the L2CAP_MAX_MTU. + */ +#ifndef OBX_USER_RX_POOL_ID +#define OBX_USER_RX_POOL_ID 4 +#endif + +/* + * Pool ID where to hold the SDU. + * This Pool will allow buffers to be used that are larger than + * the L2CAP_MAX_MTU. + */ +#ifndef OBX_USER_TX_POOL_ID +#define OBX_USER_TX_POOL_ID 4 +#endif + +/* + * GKI Buffer Pool ID used to hold MPS segments during SDU reassembly + */ +#ifndef OBX_FCR_RX_POOL_ID +#define OBX_FCR_RX_POOL_ID 3 +#endif + +/* + * Pool ID used to hold MPS segments used in (re)transmissions. + * L2CAP_DEFAULT_ERM_POOL_ID is specified to use the HCI ACL data pool. + * Note: This pool needs to have enough buffers to hold two times the window size negotiated + * in the L2CA_SetFCROptions (2 * tx_win_size) to allow for retransmissions. + * The size of each buffer must be able to hold the maximum MPS segment size passed in + * L2CA_SetFCROptions plus BT_HDR (8) + HCI preamble (4) + L2CAP_MIN_OFFSET (11 - as of BT 2.1 + EDR Spec). + */ +#ifndef OBX_FCR_TX_POOL_ID +#define OBX_FCR_TX_POOL_ID 3 +#endif + + +/****************************************************************************** +** +** BNEP +** +******************************************************************************/ + +#ifndef BNEP_INCLUDED +#define BNEP_INCLUDED FALSE +#endif + +/* BNEP status API call is used mainly to get the L2CAP handle */ +#ifndef BNEP_SUPPORTS_STATUS_API +#define BNEP_SUPPORTS_STATUS_API FALSE +#endif + +/* +** When BNEP connection changes roles after the connection is established +** we will do an authentication check again on the new role +*/ +#ifndef BNEP_DO_AUTH_FOR_ROLE_SWITCH +#define BNEP_DO_AUTH_FOR_ROLE_SWITCH FALSE +#endif + + +/* Maximum number of protocol filters supported. */ +#ifndef BNEP_MAX_PROT_FILTERS +#define BNEP_MAX_PROT_FILTERS 5 +#endif + +/* Maximum number of multicast filters supported. */ +#ifndef BNEP_MAX_MULTI_FILTERS +#define BNEP_MAX_MULTI_FILTERS 5 +#endif + +/* Minimum MTU size. */ +#ifndef BNEP_MIN_MTU_SIZE +#define BNEP_MIN_MTU_SIZE L2CAP_MTU_SIZE +#endif + +/* Preferred MTU size. */ +#ifndef BNEP_MTU_SIZE +#define BNEP_MTU_SIZE BNEP_MIN_MTU_SIZE +#endif + +/* Maximum number of buffers allowed in transmit data queue. */ +#ifndef BNEP_MAX_XMITQ_DEPTH +#define BNEP_MAX_XMITQ_DEPTH 20 +#endif + +/* Maximum number BNEP of connections supported. */ +#ifndef BNEP_MAX_CONNECTIONS +#define BNEP_MAX_CONNECTIONS 7 +#endif + + +/****************************************************************************** +** +** AVDTP +** +******************************************************************************/ + +#ifndef AVDT_INCLUDED +#define AVDT_INCLUDED TRUE +#endif + +/* Include reporting capability in AVDTP */ +#ifndef AVDT_REPORTING +#define AVDT_REPORTING TRUE +#endif + +/* Include multiplexing capability in AVDTP */ +#ifndef AVDT_MULTIPLEXING +#define AVDT_MULTIPLEXING TRUE +#endif + +/* Number of simultaneous links to different peer devices. */ +#ifndef AVDT_NUM_LINKS +#define AVDT_NUM_LINKS 2 +#endif + +/* Number of simultaneous stream endpoints. */ +#ifndef AVDT_NUM_SEPS +#define AVDT_NUM_SEPS 3 +#endif + +/* Number of transport channels setup per media stream(audio or video) */ +#ifndef AVDT_NUM_CHANNELS + +#if AVDT_REPORTING == TRUE +/* signaling, media and reporting channels */ +#define AVDT_NUM_CHANNELS 3 +#else +/* signaling and media channels */ +#define AVDT_NUM_CHANNELS 2 +#endif // AVDT_REPORTING + +#endif // AVDT_NUM_CHANNELS + +/* Number of transport channels setup by AVDT for all media streams + * AVDT_NUM_CHANNELS * Number of simultaneous streams. + */ +#ifndef AVDT_NUM_TC_TBL +#define AVDT_NUM_TC_TBL 6 +#endif + +/* Maximum size in bytes of the codec capabilities information element. */ +#ifndef AVDT_CODEC_SIZE +#define AVDT_CODEC_SIZE 10 +#endif + +/* Maximum size in bytes of the content protection information element. */ +#ifndef AVDT_PROTECT_SIZE +#define AVDT_PROTECT_SIZE 90 +#endif + +/* Maximum number of GKI buffers in the fragment queue (for video frames). + * Must be less than the number of buffers in the buffer pool of size AVDT_DATA_POOL_SIZE */ +#ifndef AVDT_MAX_FRAG_COUNT +#define AVDT_MAX_FRAG_COUNT 15 +#endif + +/****************************************************************************** +** +** PAN +** +******************************************************************************/ + +#ifndef PAN_INCLUDED +#define PAN_INCLUDED FALSE +#endif + +/* This will enable the PANU role */ +#ifndef PAN_SUPPORTS_ROLE_PANU +#define PAN_SUPPORTS_ROLE_PANU FALSE +#endif + +/* This will enable the GN role */ +#ifndef PAN_SUPPORTS_ROLE_GN +#define PAN_SUPPORTS_ROLE_GN FALSE +#endif + +/* This will enable the NAP role */ +#ifndef PAN_SUPPORTS_ROLE_NAP +#define PAN_SUPPORTS_ROLE_NAP FALSE +#endif + +/* This is just for debugging purposes */ +#ifndef PAN_SUPPORTS_DEBUG_DUMP +#define PAN_SUPPORTS_DEBUG_DUMP FALSE +#endif + +/* Maximum number of PAN connections allowed */ +#ifndef MAX_PAN_CONNS +#define MAX_PAN_CONNS 7 +#endif + +/* Default service name for NAP role */ +#ifndef PAN_NAP_DEFAULT_SERVICE_NAME +#define PAN_NAP_DEFAULT_SERVICE_NAME "Network Access Point Service" +#endif + +/* Default service name for GN role */ +#ifndef PAN_GN_DEFAULT_SERVICE_NAME +#define PAN_GN_DEFAULT_SERVICE_NAME "Group Network Service" +#endif + +/* Default service name for PANU role */ +#ifndef PAN_PANU_DEFAULT_SERVICE_NAME +#define PAN_PANU_DEFAULT_SERVICE_NAME "PAN User Service" +#endif + +/* Default description for NAP role service */ +#ifndef PAN_NAP_DEFAULT_DESCRIPTION +#define PAN_NAP_DEFAULT_DESCRIPTION "NAP" +#endif + +/* Default description for GN role service */ +#ifndef PAN_GN_DEFAULT_DESCRIPTION +#define PAN_GN_DEFAULT_DESCRIPTION "GN" +#endif + +/* Default description for PANU role service */ +#ifndef PAN_PANU_DEFAULT_DESCRIPTION +#define PAN_PANU_DEFAULT_DESCRIPTION "PANU" +#endif + +/* Default Security level for PANU role. */ +#ifndef PAN_PANU_SECURITY_LEVEL +#define PAN_PANU_SECURITY_LEVEL 0 +#endif + +/* Default Security level for GN role. */ +#ifndef PAN_GN_SECURITY_LEVEL +#define PAN_GN_SECURITY_LEVEL 0 +#endif + +/* Default Security level for NAP role. */ +#ifndef PAN_NAP_SECURITY_LEVEL +#define PAN_NAP_SECURITY_LEVEL 0 +#endif + +/****************************************************************************** +** +** HFP +** +******************************************************************************/ + +#if (BTC_HF_INCLUDED == TRUE) && (BT_CLASSIC_BQB_INCLUDED == TRUE) +#define BT_HF_AG_BQB_INCLUDED TRUE +#else +#define BT_HF_AG_BQB_INCLUDED FALSE +#endif + +#if (BTC_HF_CLIENT_INCLUDED == TRUE) && (BT_CLASSIC_BQB_INCLUDED == TRUE) +#define BT_HF_CLIENT_BQB_INCLUDED TRUE +#else +#define BT_HF_CLIENT_BQB_INCLUDED FALSE +#endif + +/****************************************************************************** +** +** GAP +** +******************************************************************************/ + +#ifndef GAP_INCLUDED +#define GAP_INCLUDED TRUE +#endif + +/* This is set to enable posting event for data write */ +#ifndef GAP_CONN_POST_EVT_INCLUDED +#define GAP_CONN_POST_EVT_INCLUDED FALSE +#endif + +/* The maximum number of simultaneous GAP L2CAP connections. */ +#ifndef GAP_MAX_CONNECTIONS +#define GAP_MAX_CONNECTIONS 10 // 30 +#endif + +/* keep the raw data received from SDP server in database. */ +#ifndef SDP_RAW_DATA_INCLUDED +#define SDP_RAW_DATA_INCLUDED TRUE +#endif + +/* Inquiry duration in 1.28 second units. */ +#ifndef SDP_DEBUG +#define SDP_DEBUG TRUE +#endif + +/****************************************************************************** +** +** HID +** +******************************************************************************/ +#ifndef BT_HID_INCLUDED +#define BT_HID_INCLUDED FALSE +#endif + +/* HID Device Role Included */ +#ifndef HID_DEV_INCLUDED +#define HID_DEV_INCLUDED FALSE +#endif + +#ifndef HID_DEV_SUBCLASS +#define HID_DEV_SUBCLASS COD_MINOR_POINTING +#endif + +#ifndef HID_CONTROL_BUF_SIZE +#define HID_CONTROL_BUF_SIZE BT_DEFAULT_BUFFER_SIZE +#endif + +#ifndef HID_INTERRUPT_BUF_SIZE +#define HID_INTERRUPT_BUF_SIZE BT_DEFAULT_BUFFER_SIZE +#endif + +#ifndef HID_DEV_MTU_SIZE +#define HID_DEV_MTU_SIZE 64 +#endif + +#ifndef HID_DEV_FLUSH_TO +#define HID_DEV_FLUSH_TO 0xffff +#endif + +#if (BTA_HD_INCLUDED == TRUE) && (HID_DEV_INCLUDED == TRUE) && (BT_CLASSIC_BQB_INCLUDED == TRUE) +#define BT_HID_DEVICE_BQB_INCLUDED TRUE +#else +#define BT_HID_DEVICE_BQB_INCLUDED FALSE +#endif + +/************************************************************************* +** Definitions for Both HID-Host & Device +*/ +#ifndef HID_MAX_SVC_NAME_LEN +#define HID_MAX_SVC_NAME_LEN 32 +#endif + +#ifndef HID_MAX_SVC_DESCR_LEN +#define HID_MAX_SVC_DESCR_LEN 32 +#endif + +#ifndef HID_MAX_PROV_NAME_LEN +#define HID_MAX_PROV_NAME_LEN 32 +#endif + +/************************************************************************* +** Definitions for HID-Host +*/ +#ifndef HID_HOST_INCLUDED +#define HID_HOST_INCLUDED FALSE +#endif + +#ifndef HID_HOST_MAX_DEVICES +#define HID_HOST_MAX_DEVICES 7 +#endif + +#ifndef HID_HOST_MTU +#define HID_HOST_MTU 640 +#endif + +#ifndef HID_HOST_FLUSH_TO +#define HID_HOST_FLUSH_TO 0xffff +#endif + +#ifndef HID_HOST_MAX_CONN_RETRY +#define HID_HOST_MAX_CONN_RETRY (3) +#endif + +#ifndef HID_HOST_REPAGE_WIN +#define HID_HOST_REPAGE_WIN (2) +#endif + +/************************************************************************* + * A2DP Definitions + */ +#ifndef A2D_INCLUDED +#define A2D_INCLUDED FALSE +#endif + +#if (BTC_AV_SRC_INCLUDED == TRUE) && (BT_CLASSIC_BQB_INCLUDED == TRUE) +#define A2D_SRC_BQB_INCLUDED TRUE +#else +#define A2D_SRC_BQB_INCLUDED FALSE +#endif + +/****************************************************************************** +** +** AVCTP +** +******************************************************************************/ + +/* Number of simultaneous ACL links to different peer devices. */ +#ifndef AVCT_NUM_LINKS +#define AVCT_NUM_LINKS 2 +#endif + +/* Number of simultaneous AVCTP connections. */ +#ifndef AVCT_NUM_CONN +#define AVCT_NUM_CONN 3 +#endif + +/****************************************************************************** +** +** AVRCP +** +******************************************************************************/ +#ifndef AVRC_INCLUDED +#define AVRC_INCLUDED FALSE +#endif + +#ifndef AVRC_METADATA_INCLUDED +#if AVRC_INCLUDED == TRUE +#define AVRC_METADATA_INCLUDED TRUE +#else +#define AVRC_METADATA_INCLUDED FALSE +#endif +#endif + +#ifndef AVRC_ADV_CTRL_INCLUDED +#if AVRC_INCLUDED == TRUE +#define AVRC_ADV_CTRL_INCLUDED TRUE +#else +#define AVRC_ADV_CTRL_INCLUDED FALSE +#endif +#endif + +#ifndef AVRC_CTLR_INCLUDED +#if AVRC_INCLUDED == TRUE +#define AVRC_CTLR_INCLUDED TRUE +#else +#define AVRC_CTLR_INCLUDED FALSE +#endif +#endif + +/****************************************************************************** +** +** MCAP +** +******************************************************************************/ +#ifndef MCA_INCLUDED +#define MCA_INCLUDED FALSE +#endif + +/* The MTU size for the L2CAP configuration on control channel. 48 is the minimal */ +#ifndef MCA_CTRL_MTU +#define MCA_CTRL_MTU 60 +#endif + +/* The maximum number of registered MCAP instances. */ +#ifndef MCA_NUM_REGS +#define MCA_NUM_REGS 12 +#endif + +/* The maximum number of control channels (to difference devices) per registered MCAP instances. */ +#ifndef MCA_NUM_LINKS +#define MCA_NUM_LINKS 3 +#endif + +/* The maximum number of MDEP (including HDP echo) per registered MCAP instances. */ +#ifndef MCA_NUM_DEPS +#define MCA_NUM_DEPS 13 +#endif + +/* The maximum number of MDL link per control channel. */ +#ifndef MCA_NUM_MDLS +#define MCA_NUM_MDLS 4 +#endif + +/* Buffer size to reassemble the SDU. */ +#ifndef MCA_USER_RX_BUF_SIZE +#define MCA_USER_RX_BUF_SIZE BT_DEFAULT_BUFFER_SIZE +#endif + +/* Buffer size to hold the SDU. */ +#ifndef MCA_USER_TX_BUF_SIZE +#define MCA_USER_TX_BUF_SIZE BT_DEFAULT_BUFFER_SIZE +#endif + +/* + * Buffer size used to hold MPS segments during SDU reassembly + */ +#ifndef MCA_FCR_RX_BUF_SIZE +#define MCA_FCR_RX_BUF_SIZE BT_DEFAULT_BUFFER_SIZE +#endif + +/* + * Default buffer size used to hold MPS segments used in (re)transmissions. + * The size of each buffer must be able to hold the maximum MPS segment size + * passed in tL2CAP_FCR_OPTIONS plus BT_HDR (8) + HCI preamble (4) + + * L2CAP_MIN_OFFSET (11 - as of BT 2.1 + EDR Spec). + */ +#ifndef MCA_FCR_TX_BUF_SIZE +#define MCA_FCR_TX_BUF_SIZE BT_DEFAULT_BUFFER_SIZE +#endif + +/* MCAP control channel FCR Option: +Size of the transmission window when using enhanced retransmission mode. +1 is defined by HDP specification for control channel. +*/ +#ifndef MCA_FCR_OPT_TX_WINDOW_SIZE +#define MCA_FCR_OPT_TX_WINDOW_SIZE 1 +#endif + +/* MCAP control channel FCR Option: +Number of transmission attempts for a single I-Frame before taking +Down the connection. Used In ERTM mode only. Value is Ignored in basic and +Streaming modes. +Range: 0, 1-0xFF +0 - infinite retransmissions +1 - single transmission +*/ +#ifndef MCA_FCR_OPT_MAX_TX_B4_DISCNT +#define MCA_FCR_OPT_MAX_TX_B4_DISCNT 20 +#endif + +/* MCAP control channel FCR Option: Retransmission Timeout +The AVRCP specification set a value in the range of 300 - 2000 ms +Timeout (in msecs) to detect Lost I-Frames. Only used in Enhanced retransmission mode. +Range: Minimum 2000 (2 secs) when supporting PBF. + */ +#ifndef MCA_FCR_OPT_RETX_TOUT +#define MCA_FCR_OPT_RETX_TOUT 2000 +#endif + +/* MCAP control channel FCR Option: Monitor Timeout +The AVRCP specification set a value in the range of 300 - 2000 ms +Timeout (in msecs) to detect Lost S-Frames. Only used in Enhanced retransmission mode. +Range: Minimum 12000 (12 secs) when supporting PBF. +*/ +#ifndef MCA_FCR_OPT_MONITOR_TOUT +#define MCA_FCR_OPT_MONITOR_TOUT 12000 +#endif + +/* MCAP control channel FCR Option: Maximum PDU payload size. +The maximum number of payload octets that the local device can receive in a single PDU. +*/ +#ifndef MCA_FCR_OPT_MPS_SIZE +#define MCA_FCR_OPT_MPS_SIZE 1000 +#endif + +/* Shared transport */ +#ifndef NFC_SHARED_TRANSPORT_ENABLED +#define NFC_SHARED_TRANSPORT_ENABLED FALSE +#endif + +/****************************************************************************** +** +** Sleep Mode (Low Power Mode) +** +******************************************************************************/ + +#ifndef HCILP_INCLUDED +#define HCILP_INCLUDED FALSE +#endif + +/****************************************************************************** +** +** APPL - Application Task +** +******************************************************************************/ + +#define L2CAP_FEATURE_REQ_ID 73 +#define L2CAP_FEATURE_RSP_ID 173 + +/****************************************************************************** +** +** BTA +** +******************************************************************************/ +/* BTA EIR canned UUID list (default is dynamic) */ +#ifndef BTA_EIR_CANNED_UUID_LIST +#define BTA_EIR_CANNED_UUID_LIST FALSE +#endif + +/* Number of supported customer UUID in EIR */ +#ifndef BTA_EIR_SERVER_NUM_CUSTOM_UUID +#define BTA_EIR_SERVER_NUM_CUSTOM_UUID 8 +#endif + +/* CHLD override for bluedroid */ +#ifndef BTA_AG_CHLD_VAL_ECC +#define BTA_AG_CHLD_VAL_ECC "(0,1,1x,2,2x,3)" +#endif + +#ifndef BTA_AG_CHLD_VAL +#define BTA_AG_CHLD_VAL "(0,1,2,3)" +#endif + +/* Set the CIND to match HFP 1.5 */ +#ifndef BTA_AG_CIND_INFO +#define BTA_AG_CIND_INFO "(\"call\",(0,1)),(\"callsetup\",(0-3)),(\"service\",(0-1)),(\"signal\",(0-5)),(\"roam\",(0,1)),(\"battchg\",(0-5)),(\"callheld\",(0-2))" +#endif + +#ifndef BTA_DM_AVOID_A2DP_ROLESWITCH_ON_INQUIRY +#define BTA_DM_AVOID_A2DP_ROLESWITCH_ON_INQUIRY FALSE +#endif + +#ifndef BTA_GATTC_MAX_CACHE_CHAR +#define BTA_GATTC_MAX_CACHE_CHAR UC_BT_GATTC_MAX_CACHE_CHAR +#endif + +/****************************************************************************** +** +** Tracing: Include trace header file here. +** +******************************************************************************/ + +/* Enable/disable BTSnoop memory logging */ +#ifndef BTSNOOP_MEM +#define BTSNOOP_MEM FALSE +#endif + +#if UC_HEAP_ALLOCATION_FROM_SPIRAM_FIRST +#define HEAP_ALLOCATION_FROM_SPIRAM_FIRST TRUE +#else +#define HEAP_ALLOCATION_FROM_SPIRAM_FIRST FALSE +#endif + +#include "common/bt_trace.h" + +#endif /* BT_TARGET_H */ diff --git a/lib/bt/host/bluedroid/common/include/common/bt_trace.h b/lib/bt/host/bluedroid/common/include/common/bt_trace.h new file mode 100644 index 00000000..a4f1ca74 --- /dev/null +++ b/lib/bt/host/bluedroid/common/include/common/bt_trace.h @@ -0,0 +1,581 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +#ifndef _BT_TRACE_H_ +#define _BT_TRACE_H_ + +#include +#include +#include "bluedroid_user_config.h" +#include "stack/bt_types.h" +#include "bt_common.h" + +static inline void trc_dump_buffer(const char *prefix, uint8_t *data, uint16_t len) +{ + uint16_t i; + + if (!data || !len) { + return; + } + + if (prefix) { + printf("%s: len %d\r\n", prefix, len); + } + + for (i = 0; i < len; i+=16) { + printf("%02x, %02x, %02x, %02x, %02x, %02x, %02x, %02x, %02x, %02x, %02x, %02x, %02x, %02x, %02x, %02x\r\n", + *(data + i), *(data + i + 1), *(data + i + 2), *(data + i + 3), *(data + i + 4), *(data + i + 5), *(data + i + 6), *(data + i + 7), + *(data + i + 8), *(data + i + 9), *(data + i + 10), *(data + i + 11), *(data + i + 12), *(data + i + 13), *(data + i + 14), *(data + i + 15)); + } + printf("\r\n"); +} + +#ifdef BTTRC_DUMP_BUFFER +#define BTTRC_DUMP_BUFFER(_prefix, _data, _len) trc_dump_buffer(_prefix, _data, _len) +#else +#define BTTRC_DUMP_BUFFER(_prefix, _data, _len) +#endif + +//static const char BTE_LOGMSG_MODULE[] = "bte_logmsg_module"; + +/* BTrgs);E tracing IDs for debug purposes */ +/* LayerIDs for stack */ +#define BTTRC_ID_STK_GKI 1 +#define BTTRC_ID_STK_BTU 2 +#define BTTRC_ID_STK_HCI 3 +#define BTTRC_ID_STK_L2CAP 4 +#define BTTRC_ID_STK_RFCM_MX 5 +#define BTTRC_ID_STK_RFCM_PRT 6 +#define BTTRC_ID_STK_OBEX_C 7 +#define BTTRC_ID_STK_OBEX_S 8 +#define BTTRC_ID_STK_AVCT 9 +#define BTTRC_ID_STK_AVDT 10 +#define BTTRC_ID_STK_AVRC 11 +#define BTTRC_ID_STK_BIC 12 +#define BTTRC_ID_STK_BIS 13 +#define BTTRC_ID_STK_BNEP 14 +#define BTTRC_ID_STK_BPP 15 +#define BTTRC_ID_STK_BTM_ACL 16 +#define BTTRC_ID_STK_BTM_PM 17 +#define BTTRC_ID_STK_BTM_DEV_CTRL 18 +#define BTTRC_ID_STK_BTM_SVC_DSC 19 +#define BTTRC_ID_STK_BTM_INQ 20 +#define BTTRC_ID_STK_BTM_SCO 21 +#define BTTRC_ID_STK_BTM_SEC 22 +#define BTTRC_ID_STK_HID 24 +#define BTTRC_ID_STK_HSP2 25 +#define BTTRC_ID_STK_CTP 26 +#define BTTRC_ID_STK_FTC 27 +#define BTTRC_ID_STK_FTS 28 +#define BTTRC_ID_STK_GAP 29 +#define BTTRC_ID_STK_HCRP 31 +#define BTTRC_ID_STK_ICP 32 +#define BTTRC_ID_STK_OPC 33 +#define BTTRC_ID_STK_OPS 34 +#define BTTRC_ID_STK_PAN 35 +#define BTTRC_ID_STK_SAP 36 +#define BTTRC_ID_STK_SDP 37 +#define BTTRC_ID_STK_SLIP 38 +#define BTTRC_ID_STK_SPP 39 +#define BTTRC_ID_STK_TCS 40 +#define BTTRC_ID_STK_VDP 41 +#define BTTRC_ID_STK_MCAP 42 +#define BTTRC_ID_STK_GATT 43 +#define BTTRC_ID_STK_SMP 44 +#define BTTRC_ID_STK_NFC 45 +#define BTTRC_ID_STK_NCI 46 +#define BTTRC_ID_STK_IDEP 47 +#define BTTRC_ID_STK_NDEP 48 +#define BTTRC_ID_STK_LLCP 49 +#define BTTRC_ID_STK_RW 50 +#define BTTRC_ID_STK_CE 51 +#define BTTRC_ID_STK_SNEP 52 +#define BTTRC_ID_STK_NDEF 53 +#define BTTRC_ID_STK_HIDD 54 + +/* LayerIDs for BTA */ +#define BTTRC_ID_BTA_ACC 55 /* Advanced Camera Client */ +#define BTTRC_ID_BTA_AG 56 /* audio gateway */ +#define BTTRC_ID_BTA_AV 57 /* Advanced audio */ +#define BTTRC_ID_BTA_BIC 58 /* Basic Imaging Client */ +#define BTTRC_ID_BTA_BIS 59 /* Basic Imaging Server */ +#define BTTRC_ID_BTA_BP 60 /* Basic Printing Client */ +#define BTTRC_ID_BTA_CG 61 +#define BTTRC_ID_BTA_CT 62 /* cordless telephony terminal */ +#define BTTRC_ID_BTA_DG 63 /* data gateway */ +#define BTTRC_ID_BTA_DM 64 /* device manager */ +#define BTTRC_ID_BTA_DM_SRCH 65 /* device manager search */ +#define BTTRC_ID_BTA_DM_SEC 66 /* device manager security */ +#define BTTRC_ID_BTA_FM 67 +#define BTTRC_ID_BTA_FTC 68 /* file transfer client */ +#define BTTRC_ID_BTA_FTS 69 /* file transfer server */ +#define BTTRC_ID_BTA_HIDH 70 +#define BTTRC_ID_BTA_HIDD 71 +#define BTTRC_ID_BTA_JV 72 +#define BTTRC_ID_BTA_OPC 73 /* object push client */ +#define BTTRC_ID_BTA_OPS 74 /* object push server */ +#define BTTRC_ID_BTA_PAN 75 /* Personal Area Networking */ +#define BTTRC_ID_BTA_PR 76 /* Printer client */ +#define BTTRC_ID_BTA_SC 77 /* SIM Card Access server */ +#define BTTRC_ID_BTA_SS 78 /* synchronization server */ +#define BTTRC_ID_BTA_SYS 79 /* system manager */ +#define BTTRC_ID_AVDT_SCB 80 /* avdt scb */ +#define BTTRC_ID_AVDT_CCB 81 /* avdt ccb */ + +// btla-specific ++ +/* LayerIDs added for BTL-A. Probably should modify bte_logmsg.c in future. */ +#define BTTRC_ID_STK_RFCOMM 82 +#define BTTRC_ID_STK_RFCOMM_DATA 83 +#define BTTRC_ID_STK_OBEX 84 +#define BTTRC_ID_STK_A2D 85 +#define BTTRC_ID_STK_BIP 86 + +/* LayerIDs for BT APP */ +#define BTTRC_ID_BTAPP 87 +#define BTTRC_ID_BT_PROTOCOL 88 /* this is a temporary solution to allow dynamic + enable/disable of BT_PROTOCOL_TRACE */ +#define BTTRC_ID_MAX_ID BTTRC_ID_BT_PROTOCOL +// btla-specific -- +#define BTTRC_ID_ALL_LAYERS 0xFF /* all trace layers */ +/* Parameter datatypes used in Trace APIs */ +#define BTTRC_PARAM_UINT8 1 +#define BTTRC_PARAM_UINT16 2 +#define BTTRC_PARAM_UINT32 3 + +/* Enables or disables verbose trace information. */ +#ifndef BT_TRACE_VERBOSE +#define BT_TRACE_VERBOSE FALSE +#endif + +/* Enables or disables all trace messages. */ +#ifndef BT_USE_TRACES +#define BT_USE_TRACES FALSE +#endif + +#ifndef BT_TRACE_APPL +#define BT_TRACE_APPL BT_USE_TRACES +#endif + +/****************************************************************************** +** +** Trace Levels +** +** The following values may be used for different levels: +** BT_TRACE_LEVEL_NONE 0 * No trace messages to be generated +** BT_TRACE_LEVEL_ERROR 1 * Error condition trace messages +** BT_TRACE_LEVEL_WARNING 2 * Warning condition trace messages +** BT_TRACE_LEVEL_API 3 * API traces +** BT_TRACE_LEVEL_EVENT 4 * Debug messages for events +** BT_TRACE_LEVEL_DEBUG 5 * Debug messages (general) +******************************************************************************/ + +// btla-specific ++ +/* Core Stack default trace levels */ +#define HCI_INITIAL_TRACE_LEVEL UC_BT_LOG_HCI_TRACE_LEVEL +#define BTM_INITIAL_TRACE_LEVEL UC_BT_LOG_BTM_TRACE_LEVEL +#define L2CAP_INITIAL_TRACE_LEVEL UC_BT_LOG_L2CAP_TRACE_LEVEL +#define RFCOMM_INITIAL_TRACE_LEVEL UC_BT_LOG_RFCOMM_TRACE_LEVEL +#define SDP_INITIAL_TRACE_LEVEL UC_BT_LOG_SDP_TRACE_LEVEL +#define GAP_INITIAL_TRACE_LEVEL UC_BT_LOG_GAP_TRACE_LEVEL +#define BNEP_INITIAL_TRACE_LEVEL UC_BT_LOG_BNEP_TRACE_LEVEL +#define PAN_INITIAL_TRACE_LEVEL UC_BT_LOG_PAN_TRACE_LEVEL +#define A2D_INITIAL_TRACE_LEVEL UC_BT_LOG_A2D_TRACE_LEVEL +#define AVDT_INITIAL_TRACE_LEVEL UC_BT_LOG_AVDT_TRACE_LEVEL +#define AVCT_INITIAL_TRACE_LEVEL UC_BT_LOG_AVCT_TRACE_LEVEL +#define AVRC_INITIAL_TRACE_LEVEL UC_BT_LOG_AVRC_TRACE_LEVEL +#define MCA_INITIAL_TRACE_LEVEL UC_BT_LOG_MCA_TRACE_LEVEL +#define HIDH_INITIAL_TRACE_LEVEL UC_BT_LOG_HIDH_TRACE_LEVEL +#define HIDD_INITIAL_TRACE_LEVEL UC_BT_LOG_HIDD_TRACE_LEVEL +#define APPL_INITIAL_TRACE_LEVEL UC_BT_LOG_APPL_TRACE_LEVEL +#define GATT_INITIAL_TRACE_LEVEL UC_BT_LOG_GATT_TRACE_LEVEL +#define SMP_INITIAL_TRACE_LEVEL UC_BT_LOG_SMP_TRACE_LEVEL +#define BTIF_INITIAL_TRACE_LEVEL UC_BT_LOG_BTIF_TRACE_LEVEL + +// btla-specific -- + +#if !UC_BT_STACK_NO_LOG +#define LOG_ERROR(format, ... ) {if (LOG_LOCAL_LEVEL >= ESP_LOG_ERROR) esp_log_write(ESP_LOG_ERROR, "BT_LOG", LOG_FORMAT(E, format), esp_log_timestamp(), "BT_LOG", ##__VA_ARGS__); } +#define LOG_WARN(format, ... ) {if (LOG_LOCAL_LEVEL >= ESP_LOG_WARN) esp_log_write(ESP_LOG_WARN, "BT_LOG", LOG_FORMAT(W, format), esp_log_timestamp(), "BT_LOG", ##__VA_ARGS__); } +#define LOG_INFO(format, ... ) {if (LOG_LOCAL_LEVEL >= ESP_LOG_INFO) esp_log_write(ESP_LOG_INFO, "BT_LOG", LOG_FORMAT(I, format), esp_log_timestamp(), "BT_LOG", ##__VA_ARGS__); } +#define LOG_DEBUG(format, ... ) {if (LOG_LOCAL_LEVEL >= ESP_LOG_DEBUG) esp_log_write(ESP_LOG_DEBUG, "BT_LOG", LOG_FORMAT(D, format), esp_log_timestamp(), "BT_LOG", ##__VA_ARGS__); } +#define LOG_VERBOSE(format, ... ) {if (LOG_LOCAL_LEVEL >= ESP_LOG_VERBOSE) esp_log_write(ESP_LOG_VERBOSE, "BT_LOG", LOG_FORMAT(V, format), esp_log_timestamp(), "BT_LOG", ##__VA_ARGS__); } + +/* Define tracing for BTM +*/ +#define BTM_TRACE_ERROR(fmt, args...) {if (btm_cb.trace_level >= BT_TRACE_LEVEL_ERROR && BT_LOG_LEVEL_CHECK(BTM, ERROR)) BT_PRINT_E("BT_BTM", fmt, ## args);} +#define BTM_TRACE_WARNING(fmt, args...) {if (btm_cb.trace_level >= BT_TRACE_LEVEL_WARNING && BT_LOG_LEVEL_CHECK(BTM, WARNING)) BT_PRINT_W("BT_BTM", fmt, ## args);} +#define BTM_TRACE_API(fmt, args...) {if (btm_cb.trace_level >= BT_TRACE_LEVEL_API && BT_LOG_LEVEL_CHECK(BTM,API)) BT_PRINT_I("BT_BTM", fmt, ## args);} +#define BTM_TRACE_EVENT(fmt, args...) {if (btm_cb.trace_level >= BT_TRACE_LEVEL_EVENT && BT_LOG_LEVEL_CHECK(BTM,EVENT)) BT_PRINT_D("BT_BTM", fmt, ## args);} +#define BTM_TRACE_DEBUG(fmt, args...) {if (btm_cb.trace_level >= BT_TRACE_LEVEL_DEBUG && BT_LOG_LEVEL_CHECK(BTM,DEBUG)) BT_PRINT_D("BT_BTM", fmt, ## args);} + +/* Define tracing for the L2CAP unit +*/ +#define L2CAP_TRACE_ERROR(fmt, args...) {if (l2cb.l2cap_trace_level >= BT_TRACE_LEVEL_ERROR && BT_LOG_LEVEL_CHECK(L2CAP, ERROR)) BT_PRINT_E("BT_L2CAP", fmt, ## args);} +#define L2CAP_TRACE_WARNING(fmt, args...) {if (l2cb.l2cap_trace_level >= BT_TRACE_LEVEL_WARNING && BT_LOG_LEVEL_CHECK(L2CAP, WARNING)) BT_PRINT_W("BT_L2CAP", fmt, ## args);} +#define L2CAP_TRACE_API(fmt, args...) {if (l2cb.l2cap_trace_level >= BT_TRACE_LEVEL_API && BT_LOG_LEVEL_CHECK(L2CAP,API)) BT_PRINT_I("BT_L2CAP", fmt, ## args);} +#define L2CAP_TRACE_EVENT(fmt, args...) {if (l2cb.l2cap_trace_level >= BT_TRACE_LEVEL_EVENT && BT_LOG_LEVEL_CHECK(L2CAP,EVENT)) BT_PRINT_D("BT_L2CAP", fmt, ## args);} +#define L2CAP_TRACE_DEBUG(fmt, args...) {if (l2cb.l2cap_trace_level >= BT_TRACE_LEVEL_DEBUG && BT_LOG_LEVEL_CHECK(L2CAP,DEBUG)) BT_PRINT_D("BT_L2CAP", fmt, ## args);} + +/* Define tracing for the SDP unit +*/ +#define SDP_TRACE_ERROR(fmt, args...) {if (sdp_cb.trace_level >= BT_TRACE_LEVEL_ERROR && BT_LOG_LEVEL_CHECK(SDP, ERROR)) BT_PRINT_E("BT_SDP", fmt, ## args);} +#define SDP_TRACE_WARNING(fmt, args...) {if (sdp_cb.trace_level >= BT_TRACE_LEVEL_WARNING && BT_LOG_LEVEL_CHECK(SDP, WARNING)) BT_PRINT_W("BT_SDP", fmt, ## args);} +#define SDP_TRACE_API(fmt, args...) {if (sdp_cb.trace_level >= BT_TRACE_LEVEL_API && BT_LOG_LEVEL_CHECK(SDP,API)) BT_PRINT_I("BT_SDP", fmt, ## args);} +#define SDP_TRACE_EVENT(fmt, args...) {if (sdp_cb.trace_level >= BT_TRACE_LEVEL_EVENT && BT_LOG_LEVEL_CHECK(SDP,EVENT)) BT_PRINT_D("BT_SDP", fmt, ## args);} +#define SDP_TRACE_DEBUG(fmt, args...) {if (sdp_cb.trace_level >= BT_TRACE_LEVEL_DEBUG && BT_LOG_LEVEL_CHECK(SDP,DEBUG)) BT_PRINT_D("BT_SDP", fmt, ## args);} + +/* Define tracing for the RFCOMM unit +*/ +#define RFCOMM_TRACE_ERROR(fmt, args...) {if (rfc_cb.trace_level >= BT_TRACE_LEVEL_ERROR && BT_LOG_LEVEL_CHECK(RFCOMM, ERROR)) BT_PRINT_E("BT_RFCOMM", fmt, ## args);} +#define RFCOMM_TRACE_WARNING(fmt, args...) {if (rfc_cb.trace_level >= BT_TRACE_LEVEL_WARNING && BT_LOG_LEVEL_CHECK(RFCOMM, WARNING)) BT_PRINT_W("BT_RFCOMM", fmt, ## args);} +#define RFCOMM_TRACE_API(fmt, args...) {if (rfc_cb.trace_level >=BT_TRACE_LEVEL_API && BT_LOG_LEVEL_CHECK(RFCOMM,API)) BT_PRINT_I("BT_RFCOMM", fmt, ## args);} +#define RFCOMM_TRACE_EVENT(fmt, args...) {if (rfc_cb.trace_level >= BT_TRACE_LEVEL_EVENT && BT_LOG_LEVEL_CHECK(RFCOMM,EVENT)) BT_PRINT_D("BT_RFCOMM", fmt, ## args);} +#define RFCOMM_TRACE_DEBUG(fmt, args...) {if (rfc_cb.trace_level >= BT_TRACE_LEVEL_DEBUG && BT_LOG_LEVEL_CHECK(RFCOMM,DEBUG)) BT_PRINT_D("BT_RFCOMM", fmt, ## args);} + +/* Generic Access Profile traces */ +#define GAP_TRACE_ERROR(fmt, args...) {if (gap_cb.trace_level >= BT_TRACE_LEVEL_ERROR && BT_LOG_LEVEL_CHECK(GAP, ERROR)) BT_PRINT_E("BT_GAP", fmt, ## args);} +#define GAP_TRACE_API(fmt, args...) {if (gap_cb.trace_level >= BT_TRACE_LEVEL_API && BT_LOG_LEVEL_CHECK(GAP,API)) BT_PRINT_I("BT_GAP", fmt, ## args);} +#define GAP_TRACE_EVENT(fmt, args...) {if (gap_cb.trace_level >= BT_TRACE_LEVEL_EVENT && BT_LOG_LEVEL_CHECK(GAP,EVENT)) BT_PRINT_D("BT_GAP", fmt, ## args);} +#define GAP_TRACE_WARNING(fmt, args...) {if (gap_cb.trace_level >= BT_TRACE_LEVEL_WARNING && BT_LOG_LEVEL_CHECK(GAP, WARNING)) BT_PRINT_W("BT_GAP", fmt, ## args);} + +/* define traces for HID Host */ +#define HIDH_TRACE_ERROR(fmt, args...) {if (hh_cb.trace_level >= BT_TRACE_LEVEL_ERROR && BT_LOG_LEVEL_CHECK(HIDH, ERROR)) BT_PRINT_E("BT_HIDH", fmt, ## args);} +#define HIDH_TRACE_WARNING(fmt, args...) {if (hh_cb.trace_level >= BT_TRACE_LEVEL_WARNING && BT_LOG_LEVEL_CHECK(HIDH, WARNING)) BT_PRINT_W("BT_HIDH", fmt, ## args);} +#define HIDH_TRACE_API(fmt, args...) {if (hh_cb.trace_level >= BT_TRACE_LEVEL_API && BT_LOG_LEVEL_CHECK(HIDH,API)) BT_PRINT_I("BT_HIDH", fmt, ## args);} +#define HIDH_TRACE_EVENT(fmt, args...) {if (hh_cb.trace_level >= BT_TRACE_LEVEL_EVENT && BT_LOG_LEVEL_CHECK(HIDH,EVENT)) BT_PRINT_D("BT_HIDH", fmt, ## args);} +#define HIDH_TRACE_DEBUG(fmt, args...) {if (hh_cb.trace_level >= BT_TRACE_LEVEL_DEBUG && BT_LOG_LEVEL_CHECK(HIDH,DEBUG)) BT_PRINT_D("BT_HIDH", fmt, ## args);} + +/* define traces for HID Device */ +#define HIDD_TRACE_ERROR(fmt, args...) {if (hd_cb.trace_level >= BT_TRACE_LEVEL_ERROR && BT_LOG_LEVEL_CHECK(HIDD, ERROR)) BT_PRINT_E("BT_HIDD", fmt, ## args);} +#define HIDD_TRACE_WARNING(fmt, args...) {if (hd_cb.trace_level >= BT_TRACE_LEVEL_WARNING && BT_LOG_LEVEL_CHECK(HIDD, WARNING)) BT_PRINT_W("BT_HIDD", fmt, ## args);} +#define HIDD_TRACE_API(fmt, args...) {if (hd_cb.trace_level >= BT_TRACE_LEVEL_API && BT_LOG_LEVEL_CHECK(HIDD,API)) BT_PRINT_I("BT_HIDD", fmt, ## args);} +#define HIDD_TRACE_EVENT(fmt, args...) {if (hd_cb.trace_level >= BT_TRACE_LEVEL_EVENT && BT_LOG_LEVEL_CHECK(HIDD,EVENT)) BT_PRINT_D("BT_HIDD", fmt, ## args);} +#define HIDD_TRACE_DEBUG(fmt, args...) {if (hd_cb.trace_level >= BT_TRACE_LEVEL_DEBUG && BT_LOG_LEVEL_CHECK(HIDD,DEBUG)) BT_PRINT_D("BT_HIDD", fmt, ## args);} +#define HIDD_TRACE_VERBOSE(fmt, args...) {if (hd_cb.trace_level >= BT_TRACE_LEVEL_VERBOSE && BT_LOG_LEVEL_CHECK(HIDD,VERBOSE)) BT_PRINT_D("BT_HIDD", fmt, ## args);} + +/* define traces for BNEP */ + +#define BNEP_TRACE_ERROR(fmt, args...) {if (bnep_cb.trace_level >= BT_TRACE_LEVEL_ERROR && BT_LOG_LEVEL_CHECK(BNEP, ERROR)) BT_PRINT_E("BT_BNEP", fmt, ## args);} +#define BNEP_TRACE_WARNING(fmt, args...) {if (bnep_cb.trace_level >= BT_TRACE_LEVEL_WARNING && BT_LOG_LEVEL_CHECK(BNEP, WARNING)) BT_PRINT_W("BT_BNEP", fmt, ## args);} +#define BNEP_TRACE_API(fmt, args...) {if (bnep_cb.trace_level >= BT_TRACE_LEVEL_API && BT_LOG_LEVEL_CHECK(BNEP,API)) BT_PRINT_I("BT_BNEP", fmt, ## args);} +#define BNEP_TRACE_EVENT(fmt, args...) {if (bnep_cb.trace_level >= BT_TRACE_LEVEL_EVENT && BT_LOG_LEVEL_CHECK(BNEP,EVENT)) BT_PRINT_D("BT_BNEP", fmt, ## args);} +#define BNEP_TRACE_DEBUG(fmt, args...) {if (bnep_cb.trace_level >= BT_TRACE_LEVEL_DEBUG && BT_LOG_LEVEL_CHECK(BNEP,DEBUG)) BT_PRINT_D("BT_BNEP", fmt, ## args);} + +/* define traces for PAN */ + +#define PAN_TRACE_ERROR(fmt, args...) {if (pan_cb.trace_level >= BT_TRACE_LEVEL_ERROR && BT_LOG_LEVEL_CHECK(PAN, ERROR)) BT_PRINT_E("BT_PAN", fmt, ## args);} +#define PAN_TRACE_WARNING(fmt, args...) {if (pan_cb.trace_level >= BT_TRACE_LEVEL_WARNING && BT_LOG_LEVEL_CHECK(PAN, WARNING)) BT_PRINT_W("BT_PAN", fmt, ## args);} +#define PAN_TRACE_API(fmt, args...) {if (pan_cb.trace_level >= BT_TRACE_LEVEL_API && BT_LOG_LEVEL_CHECK(PAN,API)) BT_PRINT_I("BT_PAN", fmt, ## args);} +#define PAN_TRACE_EVENT(fmt, args...) {if (pan_cb.trace_level >= BT_TRACE_LEVEL_EVENT && BT_LOG_LEVEL_CHECK(PAN,EVENT)) BT_PRINT_D("BT_PAN", fmt, ## args);} +#define PAN_TRACE_DEBUG(fmt, args...) {if (pan_cb.trace_level >= BT_TRACE_LEVEL_DEBUG && BT_LOG_LEVEL_CHECK(PAN,DEBUG)) BT_PRINT_D("BT_PAN", fmt, ## args);} + +/* Define tracing for the A2DP profile +*/ +#define A2D_TRACE_ERROR(fmt, args...) {if (a2d_cb.trace_level >= BT_TRACE_LEVEL_ERROR && BT_LOG_LEVEL_CHECK(A2D, ERROR)) BT_PRINT_E("BT_A2D", fmt, ## args);} +#define A2D_TRACE_WARNING(fmt, args...) {if (a2d_cb.trace_level >= BT_TRACE_LEVEL_WARNING && BT_LOG_LEVEL_CHECK(A2D, WARNING)) BT_PRINT_W("BT_A2D", fmt, ## args);} +#define A2D_TRACE_API(fmt, args...) {if (a2d_cb.trace_level >= BT_TRACE_LEVEL_API && BT_LOG_LEVEL_CHECK(A2D,API)) BT_PRINT_I("BT_A2D", fmt, ## args);} +#define A2D_TRACE_EVENT(fmt, args...) {if (a2d_cb.trace_level >= BT_TRACE_LEVEL_EVENT && BT_LOG_LEVEL_CHECK(A2D,EVENT)) BT_PRINT_D("BT_A2D", fmt, ## args);} +#define A2D_TRACE_DEBUG(fmt, args...) {if (a2d_cb.trace_level >= BT_TRACE_LEVEL_DEBUG && BT_LOG_LEVEL_CHECK(A2D,DEBUG)) BT_PRINT_D("BT_A2D", fmt, ## args);} + +/* AVDTP +*/ +#define AVDT_TRACE_ERROR(fmt, args...) {if (avdt_cb.trace_level >= BT_TRACE_LEVEL_ERROR && BT_LOG_LEVEL_CHECK(AVDT, ERROR)) BT_PRINT_E("BT_AVDT", fmt, ## args);} +#define AVDT_TRACE_WARNING(fmt, args...) {if (avdt_cb.trace_level >= BT_TRACE_LEVEL_WARNING && BT_LOG_LEVEL_CHECK(AVDT, WARNING)) BT_PRINT_W("BT_AVDT", fmt, ## args);} +#define AVDT_TRACE_API(fmt, args...) {if (avdt_cb.trace_level >= BT_TRACE_LEVEL_API && BT_LOG_LEVEL_CHECK(AVDT,API)) BT_PRINT_I("BT_AVDT", fmt, ## args);} +#define AVDT_TRACE_EVENT(fmt, args...) {if (avdt_cb.trace_level >= BT_TRACE_LEVEL_EVENT && BT_LOG_LEVEL_CHECK(AVDT,EVENT)) BT_PRINT_D("BT_AVDT", fmt, ## args);} +#define AVDT_TRACE_DEBUG(fmt, args...) {if (avdt_cb.trace_level >= BT_TRACE_LEVEL_DEBUG && BT_LOG_LEVEL_CHECK(AVDT,DEBUG)) BT_PRINT_D("BT_AVDT", fmt, ## args);} + +/* Define tracing for the AVCTP protocol +*/ +#define AVCT_TRACE_ERROR(fmt, args...) {if (avct_cb.trace_level >= BT_TRACE_LEVEL_ERROR && BT_LOG_LEVEL_CHECK(AVCT, ERROR)) BT_PRINT_E("BT_AVCT", fmt, ## args);} +#define AVCT_TRACE_WARNING(fmt, args...) {if (avct_cb.trace_level >= BT_TRACE_LEVEL_WARNING && BT_LOG_LEVEL_CHECK(AVCT, WARNING)) BT_PRINT_W("BT_AVCT", fmt, ## args);} +#define AVCT_TRACE_API(fmt, args...) {if (avct_cb.trace_level >= BT_TRACE_LEVEL_API && BT_LOG_LEVEL_CHECK(AVCT,API)) BT_PRINT_I("BT_AVCT", fmt, ## args);} +#define AVCT_TRACE_EVENT(fmt, args...) {if (avct_cb.trace_level >= BT_TRACE_LEVEL_EVENT && BT_LOG_LEVEL_CHECK(AVCT,EVENT)) BT_PRINT_D("BT_AVCT", fmt, ## args);} +#define AVCT_TRACE_DEBUG(fmt, args...) {if (avct_cb.trace_level >= BT_TRACE_LEVEL_DEBUG && BT_LOG_LEVEL_CHECK(AVCT,DEBUG)) BT_PRINT_D("BT_AVCT", fmt, ## args);} + +/* Define tracing for the AVRCP profile +*/ +#define AVRC_TRACE_ERROR(fmt, args...) {if (avrc_cb.trace_level >= BT_TRACE_LEVEL_ERROR && BT_LOG_LEVEL_CHECK(AVRC, ERROR)) BT_PRINT_E("BT_AVRC", fmt, ## args);} +#define AVRC_TRACE_WARNING(fmt, args...) {if (avrc_cb.trace_level >= BT_TRACE_LEVEL_WARNING && BT_LOG_LEVEL_CHECK(AVRC, WARNING)) BT_PRINT_W("BT_AVRC", fmt, ## args);} +#define AVRC_TRACE_API(fmt, args...) {if (avrc_cb.trace_level >= BT_TRACE_LEVEL_API && BT_LOG_LEVEL_CHECK(AVRC,API)) BT_PRINT_I("BT_AVRC", fmt, ## args);} +#define AVRC_TRACE_EVENT(fmt, args...) {if (avrc_cb.trace_level >= BT_TRACE_LEVEL_EVENT && BT_LOG_LEVEL_CHECK(AVRC,EVENT)) BT_PRINT_D("BT_AVRC", fmt, ## args);} +#define AVRC_TRACE_DEBUG(fmt, args...) {if (avrc_cb.trace_level >= BT_TRACE_LEVEL_DEBUG && BT_LOG_LEVEL_CHECK(AVRC,DEBUG)) BT_PRINT_D("BT_AVRC", fmt, ## args);} + +/* MCAP +*/ +#define MCA_TRACE_ERROR(fmt, args...) {if (mca_cb.trace_level >= BT_TRACE_LEVEL_ERROR && BT_LOG_LEVEL_CHECK(MCA, ERROR)) BT_PRINT_E("BT_MCA", fmt, ## args);} +#define MCA_TRACE_WARNING(fmt, args...) {if (mca_cb.trace_level >= BT_TRACE_LEVEL_WARNING && BT_LOG_LEVEL_CHECK(MCA, WARNING)) BT_PRINT_W("BT_MCA", fmt, ## args);} +#define MCA_TRACE_API(fmt, args...) {if (mca_cb.trace_level >= BT_TRACE_LEVEL_API && BT_LOG_LEVEL_CHECK(MCA,API)) BT_PRINT_I("BT_MCA", fmt, ## args);} +#define MCA_TRACE_EVENT(fmt, args...) {if (mca_cb.trace_level >= BT_TRACE_LEVEL_EVENT && BT_LOG_LEVEL_CHECK(MCA,EVENT)) BT_PRINT_D("BT_MCA", fmt, ## args);} +#define MCA_TRACE_DEBUG(fmt, args...) {if (mca_cb.trace_level >= BT_TRACE_LEVEL_DEBUG && BT_LOG_LEVEL_CHECK(MCA,DEBUG)) BT_PRINT_D("BT_MCA", fmt, ## args);} + +/* Define tracing for the ATT/GATT unit +*/ +#define GATT_TRACE_ERROR(fmt, args...) {if (gatt_cb.trace_level >= BT_TRACE_LEVEL_ERROR && BT_LOG_LEVEL_CHECK(GATT, ERROR)) BT_PRINT_E("BT_GATT", fmt, ## args);} +#define GATT_TRACE_WARNING(fmt, args...) {if (gatt_cb.trace_level >= BT_TRACE_LEVEL_WARNING && BT_LOG_LEVEL_CHECK(GATT, WARNING)) BT_PRINT_W("BT_GATT", fmt, ## args);} +#define GATT_TRACE_API(fmt, args...) {if (gatt_cb.trace_level >= BT_TRACE_LEVEL_API && BT_LOG_LEVEL_CHECK(GATT,API)) BT_PRINT_I("BT_GATT", fmt, ## args);} +#define GATT_TRACE_EVENT(fmt, args...) {if (gatt_cb.trace_level >= BT_TRACE_LEVEL_EVENT && BT_LOG_LEVEL_CHECK(GATT,EVENT)) BT_PRINT_D("BT_GATT", fmt, ## args);} +#define GATT_TRACE_DEBUG(fmt, args...) {if (gatt_cb.trace_level >= BT_TRACE_LEVEL_DEBUG && BT_LOG_LEVEL_CHECK(GATT,DEBUG)) BT_PRINT_D("BT_GATT", fmt, ## args);} + +/* Define tracing for the SMP unit +*/ +#define SMP_TRACE_ERROR(fmt, args...) {if (smp_cb.trace_level >= BT_TRACE_LEVEL_ERROR && BT_LOG_LEVEL_CHECK(SMP, ERROR)) BT_PRINT_E("BT_SMP", fmt, ## args);} +#define SMP_TRACE_WARNING(fmt, args...) {if (smp_cb.trace_level >= BT_TRACE_LEVEL_WARNING && BT_LOG_LEVEL_CHECK(SMP, WARNING)) BT_PRINT_W("BT_SMP", fmt, ## args);} +#define SMP_TRACE_API(fmt, args...) {if (smp_cb.trace_level >= BT_TRACE_LEVEL_API && BT_LOG_LEVEL_CHECK(SMP,API)) BT_PRINT_I("BT_SMP", fmt, ## args);} +#define SMP_TRACE_EVENT(fmt, args...) {if (smp_cb.trace_level >= BT_TRACE_LEVEL_EVENT && BT_LOG_LEVEL_CHECK(SMP,EVENT)) BT_PRINT_D("BT_SMP", fmt, ## args);} +#define SMP_TRACE_DEBUG(fmt, args...) {if (smp_cb.trace_level >= BT_TRACE_LEVEL_DEBUG && BT_LOG_LEVEL_CHECK(SMP,DEBUG)) BT_PRINT_D("BT_SMP", fmt, ## args);} + + +extern UINT8 btif_trace_level; + +// define traces for application +#define BTIF_TRACE_ERROR(fmt, args...) {if (btif_trace_level >= BT_TRACE_LEVEL_ERROR && BT_LOG_LEVEL_CHECK(BTIF, ERROR)) BT_PRINT_E("BT_BTIF", fmt, ## args);} +#define BTIF_TRACE_WARNING(fmt, args...) {if (btif_trace_level >= BT_TRACE_LEVEL_WARNING && BT_LOG_LEVEL_CHECK(BTIF, WARNING)) BT_PRINT_W("BT_BTIF", fmt, ## args);} +#define BTIF_TRACE_API(fmt, args...) {if (btif_trace_level >= BT_TRACE_LEVEL_API && BT_LOG_LEVEL_CHECK(BTIF,API)) BT_PRINT_I("BT_BTIF", fmt, ## args);} +#define BTIF_TRACE_EVENT(fmt, args...) {if (btif_trace_level >= BT_TRACE_LEVEL_EVENT && BT_LOG_LEVEL_CHECK(BTIF,EVENT)) BT_PRINT_D("BT_BTIF", fmt, ## args);} +#define BTIF_TRACE_DEBUG(fmt, args...) {if (btif_trace_level >= BT_TRACE_LEVEL_DEBUG && BT_LOG_LEVEL_CHECK(BTIF,DEBUG)) BT_PRINT_D("BT_BTIF", fmt, ## args);} +#define BTIF_TRACE_VERBOSE(fmt, args...) {if (btif_trace_level >= BT_TRACE_LEVEL_VERBOSE && BT_LOG_LEVEL_CHECK(BTIF,VERBOSE)) BT_PRINT_V("BT_BTIF", fmt, ## args);} + +/* define traces for application */ + +#define APPL_TRACE_ERROR(fmt, args...) {if (appl_trace_level >= BT_TRACE_LEVEL_ERROR && BT_LOG_LEVEL_CHECK(APPL, ERROR)) BT_PRINT_E("BT_APPL", fmt, ## args);} +#define APPL_TRACE_WARNING(fmt, args...) {if (appl_trace_level >= BT_TRACE_LEVEL_WARNING && BT_LOG_LEVEL_CHECK(APPL, WARNING)) BT_PRINT_W("BT_APPL", fmt, ## args);} +#define APPL_TRACE_API(fmt, args...) {if (appl_trace_level >= BT_TRACE_LEVEL_API && BT_LOG_LEVEL_CHECK(APPL,API)) BT_PRINT_I("BT_APPL", fmt, ## args);} +#define APPL_TRACE_EVENT(fmt, args...) {if (appl_trace_level >= BT_TRACE_LEVEL_EVENT && BT_LOG_LEVEL_CHECK(APPL,EVENT)) BT_PRINT_D("BT_APPL", fmt, ## args);} +#define APPL_TRACE_DEBUG(fmt, args...) {if (appl_trace_level >= BT_TRACE_LEVEL_DEBUG && BT_LOG_LEVEL_CHECK(APPL,DEBUG)) BT_PRINT_D("BT_APPL", fmt, ## args);} +#define APPL_TRACE_VERBOSE(fmt, args...) {if (appl_trace_level >= BT_TRACE_LEVEL_VERBOSE && BT_LOG_LEVEL_CHECK(APPL,VERBOSE)) BT_PRINT_V("BT_APPL", fmt, ## args);} + +/* Define tracing for the HCI unit + * Modified from `btu_cb.trace_level` to `HCI_INITIAL_TRACE_LEVEL`, + * to use HCI_TRACE_XXXX in hci_layer.c without including `btu.h` +*/ +#define HCI_TRACE_ERROR(fmt, args...) {if (HCI_INITIAL_TRACE_LEVEL >= BT_TRACE_LEVEL_ERROR && BT_LOG_LEVEL_CHECK(HCI, ERROR)) BT_PRINT_E("BT_HCI", fmt,## args);} +#define HCI_TRACE_WARNING(fmt, args...) {if (HCI_INITIAL_TRACE_LEVEL >= BT_TRACE_LEVEL_WARNING && BT_LOG_LEVEL_CHECK(HCI, WARNING)) BT_PRINT_W("BT_HCI", fmt,## args);} +#define HCI_TRACE_EVENT(fmt, args...) {if (HCI_INITIAL_TRACE_LEVEL >= BT_TRACE_LEVEL_EVENT && BT_LOG_LEVEL_CHECK(HCI,EVENT)) BT_PRINT_D("BT_HCI", fmt,## args);} +#define HCI_TRACE_DEBUG(fmt, args...) {if (HCI_INITIAL_TRACE_LEVEL >= BT_TRACE_LEVEL_DEBUG && BT_LOG_LEVEL_CHECK(HCI,DEBUG)) BT_PRINT_D("BT_HCI", fmt,## args);} + +#else +#define LOG_ERROR(fmt, args...) +#define LOG_WARN(fmt, args...) +#define LOG_INFO(fmt, args...) +#define LOG_DEBUG(fmt, args...) +#define LOG_VERBOSE(fmt, args...) + +/* Define tracing for the HCI unit +*/ +#define HCI_TRACE_ERROR(fmt, args...) +#define HCI_TRACE_WARNING(fmt, args...) +#define HCI_TRACE_EVENT(fmt, args...) +#define HCI_TRACE_DEBUG(fmt, args...) + +/* Define tracing for BTM +*/ +#define BTM_TRACE_ERROR(fmt, args...) +#define BTM_TRACE_WARNING(fmt, args...) +#define BTM_TRACE_API(fmt, args...) +#define BTM_TRACE_EVENT(fmt, args...) +#define BTM_TRACE_DEBUG(fmt, args...) + +/* Define tracing for the L2CAP unit +*/ +#define L2CAP_TRACE_ERROR(fmt, args...) +#define L2CAP_TRACE_WARNING(fmt, args...) +#define L2CAP_TRACE_API(fmt, args...) +#define L2CAP_TRACE_EVENT(fmt, args...) +#define L2CAP_TRACE_DEBUG(fmt, args...) + +/* Define tracing for the SDP unit +*/ +#define SDP_TRACE_ERROR(fmt, args...) +#define SDP_TRACE_WARNING(fmt, args...) +#define SDP_TRACE_API(fmt, args...) +#define SDP_TRACE_EVENT(fmt, args...) +#define SDP_TRACE_DEBUG(fmt, args...) + +/* Define tracing for the RFCOMM unit +*/ +#define RFCOMM_TRACE_ERROR(fmt, args...) +#define RFCOMM_TRACE_WARNING(fmt, args...) +#define RFCOMM_TRACE_API(fmt, args...) +#define RFCOMM_TRACE_EVENT(fmt, args...) +#define RFCOMM_TRACE_DEBUG(fmt, args...) + +/* Generic Access Profile traces */ +#define GAP_TRACE_ERROR(fmt, args...) +#define GAP_TRACE_EVENT(fmt, args...) +#define GAP_TRACE_API(fmt, args...) +#define GAP_TRACE_WARNING(fmt, args...) + +/* define traces for HID Host */ +#define HIDH_TRACE_ERROR(fmt, args...) +#define HIDH_TRACE_WARNING(fmt, args...) +#define HIDH_TRACE_API(fmt, args...) +#define HIDH_TRACE_EVENT(fmt, args...) +#define HIDH_TRACE_DEBUG(fmt, args...) + +/* define traces for HID Device */ +#define HIDD_TRACE_ERROR(fmt, args...) +#define HIDD_TRACE_WARNING(fmt, args...) +#define HIDD_TRACE_API(fmt, args...) +#define HIDD_TRACE_EVENT(fmt, args...) +#define HIDD_TRACE_DEBUG(fmt, args...) +#define HIDD_TRACE_VERBOSE(fmt, args...) + +/* define traces for BNEP */ + +#define BNEP_TRACE_ERROR(fmt, args...) +#define BNEP_TRACE_WARNING(fmt, args...) +#define BNEP_TRACE_API(fmt, args...) +#define BNEP_TRACE_EVENT(fmt, args...) +#define BNEP_TRACE_DEBUG(fmt, args...) + +/* define traces for PAN */ + +#define PAN_TRACE_ERROR(fmt, args...) +#define PAN_TRACE_WARNING(fmt, args...) +#define PAN_TRACE_API(fmt, args...) +#define PAN_TRACE_EVENT(fmt, args...) +#define PAN_TRACE_DEBUG(fmt, args...) + +/* Define tracing for the A2DP profile +*/ +#define A2D_TRACE_ERROR(fmt, args...) +#define A2D_TRACE_WARNING(fmt, args...) +#define A2D_TRACE_EVENT(fmt, args...) +#define A2D_TRACE_DEBUG(fmt, args...) +#define A2D_TRACE_API(fmt, args...) + +/* AVDTP +*/ +#define AVDT_TRACE_ERROR(fmt, args...) +#define AVDT_TRACE_WARNING(fmt, args...) +#define AVDT_TRACE_EVENT(fmt, args...) +#define AVDT_TRACE_DEBUG(fmt, args...) +#define AVDT_TRACE_API(fmt, args...) + +/* Define tracing for the AVCTP protocol +*/ +#define AVCT_TRACE_ERROR(fmt, args...) +#define AVCT_TRACE_WARNING(fmt, args...) +#define AVCT_TRACE_EVENT(fmt, args...) +#define AVCT_TRACE_DEBUG(fmt, args...) +#define AVCT_TRACE_API(fmt, args...) + +/* Define tracing for the AVRCP profile +*/ +#define AVRC_TRACE_ERROR(fmt, args...) +#define AVRC_TRACE_WARNING(fmt, args...) +#define AVRC_TRACE_EVENT(fmt, args...) +#define AVRC_TRACE_DEBUG(fmt, args...) +#define AVRC_TRACE_API(fmt, args...) + +/* MCAP +*/ +#define MCA_TRACE_ERROR(fmt, args...) +#define MCA_TRACE_WARNING(fmt, args...) +#define MCA_TRACE_EVENT(fmt, args...) +#define MCA_TRACE_DEBUG(fmt, args...) +#define MCA_TRACE_API(fmt, args...) + +/* Define tracing for the ATT/GATT unit +*/ +#define GATT_TRACE_ERROR(fmt, args...) +#define GATT_TRACE_WARNING(fmt, args...) +#define GATT_TRACE_API(fmt, args...) +#define GATT_TRACE_EVENT(fmt, args...) +#define GATT_TRACE_DEBUG(fmt, args...) + +/* Define tracing for the SMP unit +*/ +#define SMP_TRACE_ERROR(fmt, args...) +#define SMP_TRACE_WARNING(fmt, args...) +#define SMP_TRACE_API(fmt, args...) +#define SMP_TRACE_EVENT(fmt, args...) +#define SMP_TRACE_DEBUG(fmt, args...) + +extern UINT8 btif_trace_level; + +// define traces for application +#define BTIF_TRACE_ERROR(fmt, args...) +#define BTIF_TRACE_WARNING(fmt, args...) +#define BTIF_TRACE_API(fmt, args...) +#define BTIF_TRACE_EVENT(fmt, args...) +#define BTIF_TRACE_DEBUG(fmt, args...) +#define BTIF_TRACE_VERBOSE(fmt, args...) + +/* define traces for application */ + +#define APPL_TRACE_ERROR(fmt, args...) +#define APPL_TRACE_WARNING(fmt, args...) +#define APPL_TRACE_API(fmt, args...) +#define APPL_TRACE_EVENT(fmt, args...) +#define APPL_TRACE_DEBUG(fmt, args...) +#define APPL_TRACE_VERBOSE(fmt, args...) + +#endif ///!UC_BT_STACK_NO_LOG + + +/* Simplified Trace Helper Macro +*/ +#define bdld(fmt, args...) \ + do{\ + if((MY_LOG_LEVEL) >= BT_TRACE_LEVEL_DEBUG) \ + BT_PRINT_D(fmt, ## args); \ + }while(0) + +#define bdlw(fmt, args...) \ + do{\ + if((MY_LOG_LEVEL) >= BT_TRACE_LEVEL_WARNING) \ + BT_PRINT_W(fmt, ## args); \ + }while(0) + +#define bdle(fmt, args...) \ + do{\ + if((MY_LOG_LEVEL) >= BT_TRACE_LEVEL_ERROR) \ + BT_PRINT_E(fmt, ## args); \ + }while(0) + +#define bdla(assert_if) \ + do{\ + if(((MY_LOG_LEVEL) >= BT_TRACE_LEVEL_ERROR) && !(assert_if)) \ + BT_PRINT_E("%s: assert failed\n", #assert_if); \ + }while(0) + +typedef UINT8 tBTTRC_PARAM_TYPE; +typedef UINT8 tBTTRC_LAYER_ID; +typedef UINT8 tBTTRC_TYPE; + +typedef struct { + tBTTRC_LAYER_ID layer_id; + tBTTRC_TYPE type; /* TODO: use tBTTRC_TYPE instead of "classical level 0-5" */ +} tBTTRC_LEVEL; + +typedef UINT8 (tBTTRC_SET_TRACE_LEVEL)( UINT8 ); + +typedef struct { + const tBTTRC_LAYER_ID layer_id_start; + const tBTTRC_LAYER_ID layer_id_end; + tBTTRC_SET_TRACE_LEVEL *p_f; + const char *trc_name; + UINT8 trace_level; +} tBTTRC_FUNC_MAP; + +/* External declaration for appl_trace_level here to avoid to add the declaration in all the files using APPL_TRACExxx macros */ +extern UINT8 appl_trace_level; + +#endif /*_BT_TRACE_H_*/ diff --git a/lib/bt/host/bluedroid/common/include/common/bt_vendor_lib.h b/lib/bt/host/bluedroid/common/include/common/bt_vendor_lib.h new file mode 100644 index 00000000..63e6f9a6 --- /dev/null +++ b/lib/bt/host/bluedroid/common/include/common/bt_vendor_lib.h @@ -0,0 +1,361 @@ +/****************************************************************************** + * + * Copyright (C) 2009-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +#ifndef BT_VENDOR_LIB_H +#define BT_VENDOR_LIB_H + +#include +//#include +//#include + +/** Struct types */ + + +/** Typedefs and defines */ + +/** Vendor specific operations OPCODE */ +typedef enum { + /* [operation] + * Power on or off the BT Controller. + * [input param] + * A pointer to int type with content of bt_vendor_power_state_t. + * Typecasting conversion: (int *) param. + * [return] + * 0 - default, don't care. + * [callback] + * None. + */ + BT_VND_OP_POWER_CTRL, + + /* [operation] + * Perform any vendor specific initialization or configuration + * on the BT Controller. This is called before stack initialization. + * [input param] + * None. + * [return] + * 0 - default, don't care. + * [callback] + * Must call fwcfg_cb to notify the stack of the completion of vendor + * specific initialization once it has been done. + */ + BT_VND_OP_FW_CFG, + + /* [operation] + * Perform any vendor specific SCO/PCM configuration on the BT Controller. + * This is called after stack initialization. + * [input param] + * None. + * [return] + * 0 - default, don't care. + * [callback] + * Must call scocfg_cb to notify the stack of the completion of vendor + * specific SCO configuration once it has been done. + */ + BT_VND_OP_SCO_CFG, + + /* [operation] + * Open UART port on where the BT Controller is attached. + * This is called before stack initialization. + * [input param] + * A pointer to int array type for open file descriptors. + * The mapping of HCI channel to fd slot in the int array is given in + * bt_vendor_hci_channels_t. + * And, it requires the vendor lib to fill up the content before returning + * the call. + * Typecasting conversion: (int (*)[]) param. + * [return] + * Numbers of opened file descriptors. + * Valid number: + * 1 - CMD/EVT/ACL-In/ACL-Out via the same fd (e.g. UART) + * 2 - CMD/EVT on one fd, and ACL-In/ACL-Out on the other fd + * 4 - CMD, EVT, ACL-In, ACL-Out are on their individual fd + * [callback] + * None. + */ + BT_VND_OP_USERIAL_OPEN, + + /* [operation] + * Close the previously opened UART port. + * [input param] + * None. + * [return] + * 0 - default, don't care. + * [callback] + * None. + */ + BT_VND_OP_USERIAL_CLOSE, + + /* [operation] + * Get the LPM idle timeout in milliseconds. + * The stack uses this information to launch a timer delay before it + * attempts to de-assert LPM WAKE signal once downstream HCI packet + * has been delivered. + * [input param] + * A pointer to uint32_t type which is passed in by the stack. And, it + * requires the vendor lib to fill up the content before returning + * the call. + * Typecasting conversion: (uint32_t *) param. + * [return] + * 0 - default, don't care. + * [callback] + * None. + */ + BT_VND_OP_GET_LPM_IDLE_TIMEOUT, + + /* [operation] + * Enable or disable LPM mode on BT Controller. + * [input param] + * A pointer to uint8_t type with content of bt_vendor_lpm_mode_t. + * Typecasting conversion: (uint8_t *) param. + * [return] + * 0 - default, don't care. + * [callback] + * Must call lpm_cb to notify the stack of the completion of LPM + * disable/enable process once it has been done. + */ + BT_VND_OP_LPM_SET_MODE, + + /* [operation] + * Assert or Deassert LPM WAKE on BT Controller. + * [input param] + * A pointer to uint8_t type with content of bt_vendor_lpm_wake_state_t. + * Typecasting conversion: (uint8_t *) param. + * [return] + * 0 - default, don't care. + * [callback] + * None. + */ + BT_VND_OP_LPM_WAKE_SET_STATE, + + /* [operation] + * Perform any vendor specific commands related to audio state changes. + * [input param] + * a pointer to bt_vendor_op_audio_state_t indicating what audio state is + * set. + * [return] + * 0 - default, don't care. + * [callback] + * None. + */ + BT_VND_OP_SET_AUDIO_STATE, + + /* [operation] + * The epilog call to the vendor module so that it can perform any + * vendor-specific processes (e.g. send a HCI_RESET to BT Controller) + * before the caller calls for cleanup(). + * [input param] + * None. + * [return] + * 0 - default, don't care. + * [callback] + * Must call epilog_cb to notify the stack of the completion of vendor + * specific epilog process once it has been done. + */ + BT_VND_OP_EPILOG, +} bt_vendor_opcode_t; + +/** Power on/off control states */ +typedef enum { + BT_VND_PWR_OFF, + BT_VND_PWR_ON, +} bt_vendor_power_state_t; + +/** Define HCI channel identifier in the file descriptors array + used in BT_VND_OP_USERIAL_OPEN operation. + */ +typedef enum { + CH_CMD, // HCI Command channel + CH_EVT, // HCI Event channel + CH_ACL_OUT, // HCI ACL downstream channel + CH_ACL_IN, // HCI ACL upstream channel + + CH_MAX // Total channels +} bt_vendor_hci_channels_t; + +/** LPM disable/enable request */ +typedef enum { + BT_VND_LPM_DISABLE, + BT_VND_LPM_ENABLE, +} bt_vendor_lpm_mode_t; + +/** LPM WAKE set state request */ +typedef enum { + BT_VND_LPM_WAKE_ASSERT, + BT_VND_LPM_WAKE_DEASSERT, +} bt_vendor_lpm_wake_state_t; + +/** Callback result values */ +typedef enum { + BT_VND_OP_RESULT_SUCCESS, + BT_VND_OP_RESULT_FAIL, +} bt_vendor_op_result_t; + +/** audio (SCO) state changes triggering VS commands for configuration */ +typedef struct { + uint16_t handle; + uint16_t peer_codec; + uint16_t state; +} bt_vendor_op_audio_state_t; + +/* + * Bluetooth Host/Controller Vendor callback structure. + */ + +/* vendor initialization/configuration callback */ +typedef void (*cfg_result_cb)(bt_vendor_op_result_t result); + +/* datapath buffer allocation callback (callout) + * + * Vendor lib needs to request a buffer through the alloc callout function + * from HCI lib if the buffer is for constructing a HCI Command packet which + * will be sent through xmit_cb to BT Controller. + * + * For each buffer allocation, the requested size needs to be big enough to + * accommodate the below header plus a complete HCI packet -- + * typedef struct + * { + * uint16_t event; + * uint16_t len; + * uint16_t offset; + * uint16_t layer_specific; + * } HC_BT_HDR; + * + * HCI lib returns a pointer to the buffer where Vendor lib should use to + * construct a HCI command packet as below format: + * + * -------------------------------------------- + * | HC_BT_HDR | HCI command | + * -------------------------------------------- + * where + * HC_BT_HDR.event = 0x2000; + * HC_BT_HDR.len = Length of HCI command; + * HC_BT_HDR.offset = 0; + * HC_BT_HDR.layer_specific = 0; + * + * For example, a HCI_RESET Command will be formed as + * ------------------------ + * | HC_BT_HDR |03|0c|00| + * ------------------------ + * with + * HC_BT_HDR.event = 0x2000; + * HC_BT_HDR.len = 3; + * HC_BT_HDR.offset = 0; + * HC_BT_HDR.layer_specific = 0; + */ +typedef void *(*malloc_cb)(int size); + +/* datapath buffer deallocation callback (callout) */ +typedef void (*mdealloc_cb)(void *p_buf); + +/* define callback of the cmd_xmit_cb + * + * The callback function which HCI lib will call with the return of command + * complete packet. Vendor lib is responsible for releasing the buffer passed + * in at the p_mem parameter by calling dealloc callout function. + */ +typedef void (*tINT_CMD_CBACK)(void *p_mem); + +/* hci command packet transmit callback (callout) + * + * Vendor lib calls xmit_cb callout function in order to send a HCI Command + * packet to BT Controller. The buffer carrying HCI Command packet content + * needs to be first allocated through the alloc callout function. + * HCI lib will release the buffer for Vendor lib once it has delivered the + * packet content to BT Controller. + * + * Vendor lib needs also provide a callback function (p_cback) which HCI lib + * will call with the return of command complete packet. + * + * The opcode parameter gives the HCI OpCode (combination of OGF and OCF) of + * HCI Command packet. For example, opcode = 0x0c03 for the HCI_RESET command + * packet. + */ +typedef uint8_t (*cmd_xmit_cb)(uint16_t opcode, void *p_buf, tINT_CMD_CBACK p_cback); + +typedef struct { + /** set to sizeof(bt_vendor_callbacks_t) */ + size_t size; + + /* + * Callback and callout functions have implemented in HCI libray + * (libbt-hci.so). + */ + + /* notifies caller result of firmware configuration request */ + cfg_result_cb fwcfg_cb; + + /* notifies caller result of sco configuration request */ + cfg_result_cb scocfg_cb; + + /* notifies caller result of lpm enable/disable */ + cfg_result_cb lpm_cb; + + /* notifies the result of codec setting */ + cfg_result_cb audio_state_cb; + + /* buffer allocation request */ + malloc_cb alloc; + + /* buffer deallocation request */ + mdealloc_cb dealloc; + + /* hci command packet transmit request */ + cmd_xmit_cb xmit_cb; + + /* notifies caller completion of epilog process */ + cfg_result_cb epilog_cb; +} bt_vendor_callbacks_t; + +/* + * Bluetooth Host/Controller VENDOR Interface + */ +typedef struct { + /** Set to sizeof(bt_vndor_interface_t) */ + size_t size; + + /* + * Functions need to be implemented in Vendor libray (libbt-vendor.so). + */ + + /** + * Caller will open the interface and pass in the callback routines + * to the implemenation of this interface. + */ + int (*init)(const bt_vendor_callbacks_t *p_cb, unsigned char *local_bdaddr); + + /** Vendor specific operations */ + int (*op)(bt_vendor_opcode_t opcode, void *param); + + /** Closes the interface */ + void (*cleanup)(void); +} bt_vendor_interface_t; + + +/* + * External shared lib functions/data + */ + +/* Entry point of DLib -- + * Vendor library needs to implement the body of bt_vendor_interface_t + * structure and uses the below name as the variable name. HCI library + * will use this symbol name to get address of the object through the + * dlsym call. + */ +//extern const bt_vendor_interface_t BLUETOOTH_VENDOR_LIB_INTERFACE; + +#endif /* BT_VENDOR_LIB_H */ diff --git a/lib/bt/host/bluedroid/common/include/common/bte.h b/lib/bt/host/bluedroid/common/include/common/bte.h new file mode 100644 index 00000000..4bef635a --- /dev/null +++ b/lib/bt/host/bluedroid/common/include/common/bte.h @@ -0,0 +1,116 @@ +/****************************************************************************** + * + * Copyright (C) 2001-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * this file contains constants and definitions for the bte project + * + ******************************************************************************/ +#ifndef BTE_H +#define BTE_H + +//#include +//#include +//#include +#include "common/bt_target.h" + +/* by default on shutdown, baudrate is reset 115kbits. this should NOT be need for platforms + * that kill BTE driver and remove/reset BT chip + */ +#ifndef BTE_RESET_BAUD_ON_BT_DISABLE +#define BTE_RESET_BAUD_ON_BT_DISABLE TRUE +#endif + +/* Target Modes (based on jumper settings on hardware [see user manual]) */ +enum { + /* BTE BBY */ + /* J3 J4 SW3-3 SW3-2 SW3-1 */ + /* -------------------------------------------- */ + BTE_MODE_SERIAL_APP, /* OUT OUT OFF OFF OFF Sample serial port application */ + BTE_MODE_APPL, /* IN OUT OFF OFF ON Target used with Tester through RPC */ + BTE_MODE_RESERVED, /* OUT IN OFF ON OFF Reserved */ + BTE_MODE_SAMPLE_APPS, /* IN IN OFF ON ON Sample applications (ICP/HSP) */ + BTE_MODE_DONGLE, /* not yet supported ON OFF OFF Dongle mode */ + BTE_MODE_APPL_PROTOCOL_TRACE, /* this is a fake mode do allow protocol tracing in application without rpc */ + BTE_MODE_INVALID +}; + +extern volatile UINT8 bte_target_mode; /* indicates the mode that the board is running in */ + +/* Startup options */ +extern UINT32 bte_startup_options; /* Switch and jumper settings at startup */ +void bte_get_startup_options(UINT32 *p_options); /* Platform specific function for getting startup options */ + +#define BTE_OPTIONS_TARGET_MODE_MASK 0x00000007 /* bits 2-0 indicate target mode (QuickConnect: jp3 & jp4, BBY: SW3-1 & SW3-2)*/ + + +/**************************************************************************** + * Definitions to define which type of application gets built + ****************************************************************************/ +#define BUILD_HCITOOL FALSE +#define BUILD_L2PING FALSE + + +#define LINUX_FM_DRIVER_INCLUDED FALSE + + +/* hcisu userial operations. should probably go into bt_types to avoid collisions! */ +#define BT_EVT_TO_HCISU_USERIAL_OP (0x0080 | BT_EVT_HCISU) +/* operation for above hcisu event */ +#define BT_HCISU_USERIAL_OPEN (0) /* open serial port calling USERIAL_Open() */ +#define BT_HCISU_USERIAL_CLOSE (1) /* close userial port */ +/* options associated with close op */ +#define BT_HCISU_USERIAL_CL_NO_DIS_BT 0 /* do not touch bt_wake and power gpio */ +#define BT_HCISU_USERIAL_CL_DIS_BT 1 /* put power and bt_wake into defined off state to preserve + power */ +/* status codes for callback */ +/* +#define BTE_HCISU_USERIAL_FAIL 0 +#define BTE_HCISU_USERIAL_OK 1 +typedef void (tUSERIAL_MSG_CBACK) (int status); +typedef struct tHCISU_USERIAL_MSG_tag { + BT_HDR hdr; + tUSERIAL_MSG_CBACK *p_cback; + UINT8 port; // port number + UINT8 op; + UINT8 option; // option for operation. depends on operation +} tHCISU_USERIAL_MSG; + +extern void bte_hcisu_userial_oper( tUSERIAL_MSG_CBACK *p_cback, UINT8 port, UINT8 op, UINT8 option ); +*/ + +/* Pointer to function for sending HCI commands and data to the HCI tranport */ +extern int (*p_bte_hci_send)(UINT16 port, BT_HDR *p_msg); + + +/* Protocol trace mask */ +extern UINT32 bte_proto_trace_mask; + +typedef struct tBAUD_REG_tag { + UINT8 DHBR; + UINT8 DLBR; + UINT8 ExplicitBaudRate0; + UINT8 ExplicitBaudRate1; + UINT8 ExplicitBaudRate2; + UINT8 ExplicitBaudRate3; +} tBAUD_REG; + + +extern const tBAUD_REG baud_rate_regs[]; + +#endif /* BTE_H */ diff --git a/lib/bt/host/bluedroid/common/include/common/bte_appl.h b/lib/bt/host/bluedroid/common/include/common/bte_appl.h new file mode 100644 index 00000000..f14b4d91 --- /dev/null +++ b/lib/bt/host/bluedroid/common/include/common/bte_appl.h @@ -0,0 +1,41 @@ +/****************************************************************************** + * + * Copyright (C) 2002-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This is the interface file for the bte application task + * + ******************************************************************************/ + +#pragma once + +typedef struct { +#if ((BLE_INCLUDED == TRUE) && (SMP_INCLUDED == TRUE)) + UINT8 ble_auth_req; + UINT8 ble_io_cap; + UINT8 ble_init_key; + UINT8 ble_resp_key; + UINT8 ble_max_key_size; + UINT8 ble_min_key_size; + UINT8 ble_accept_auth_enable; + UINT8 oob_support; +#endif + +} tBTE_APPL_CFG; + +extern tBTE_APPL_CFG bte_appl_cfg; diff --git a/lib/bt/host/bluedroid/config/include/config/stack_config.h b/lib/bt/host/bluedroid/config/include/config/stack_config.h new file mode 100644 index 00000000..8b777723 --- /dev/null +++ b/lib/bt/host/bluedroid/config/include/config/stack_config.h @@ -0,0 +1,21 @@ +/* + * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include + +#include "bt_common.h" + +struct bluedroid_config { + bool (*get_ssp_enabled)(void); +}; + +bt_status_t bluedriod_config_init(esp_bluedroid_config_t *cfg); + +void bluedriod_config_deinit(void); + +const struct bluedroid_config *bluedriod_config_get(void); diff --git a/lib/bt/host/bluedroid/config/stack_config.c b/lib/bt/host/bluedroid/config/stack_config.c new file mode 100644 index 00000000..66d04a63 --- /dev/null +++ b/lib/bt/host/bluedroid/config/stack_config.c @@ -0,0 +1,55 @@ +/* + * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include "osi/allocator.h" +#include "esp_bt_main.h" + +#include "config/stack_config.h" + +struct stack_config_env_tag { + esp_bluedroid_config_t cfg; + struct bluedroid_config interface; +}; + +static struct stack_config_env_tag *s_stack_config_env = NULL; + +static bool get_ssp_enabled(void) +{ + assert(s_stack_config_env); + esp_bluedroid_config_t *cfg = &s_stack_config_env->cfg; + return cfg->ssp_en; +} + +bt_status_t bluedriod_config_init(esp_bluedroid_config_t *cfg) +{ + s_stack_config_env = osi_calloc(sizeof(struct stack_config_env_tag)); + if (!s_stack_config_env) { + return BT_STATUS_NOMEM; + } + + memcpy(&s_stack_config_env->cfg, cfg, sizeof(esp_bluedroid_config_t)); + + struct bluedroid_config *interface = &s_stack_config_env->interface; + interface->get_ssp_enabled = get_ssp_enabled; + + return BT_STATUS_SUCCESS; +} + +void bluedriod_config_deinit(void) +{ + if (s_stack_config_env) { + osi_free(s_stack_config_env); + s_stack_config_env = NULL; + } +} + +const struct bluedroid_config *bluedriod_config_get(void) +{ + assert(s_stack_config_env); + return &s_stack_config_env->interface; +} diff --git a/lib/bt/host/bluedroid/device/bdaddr.c b/lib/bt/host/bluedroid/device/bdaddr.c new file mode 100644 index 00000000..6317979a --- /dev/null +++ b/lib/bt/host/bluedroid/device/bdaddr.c @@ -0,0 +1,126 @@ +/****************************************************************************** + * + * Copyright (C) 2014 Google, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +#include +#include +#include "common/bt_trace.h" +#include "device/bdaddr.h" + +static inline bool ets_isxdigit(char c) +{ + if ((c >= '0') && (c <= '9')) { + return true; + } + if ((c >= 'a') && (c <= 'f')) { + return true; + } + return ((c >= 'A') && (c <= 'F')); +} + +bool bdaddr_is_empty(const bt_bdaddr_t *addr) +{ + assert(addr != NULL); + + uint8_t zero[sizeof(bt_bdaddr_t)] = { 0 }; + return memcmp(addr, &zero, sizeof(bt_bdaddr_t)) == 0; +} + +bool bdaddr_equals(const bt_bdaddr_t *first, const bt_bdaddr_t *second) +{ + assert(first != NULL); + assert(second != NULL); + + return memcmp(first, second, sizeof(bt_bdaddr_t)) == 0; +} + +bt_bdaddr_t *bdaddr_copy(bt_bdaddr_t *dest, const bt_bdaddr_t *src) +{ + assert(dest != NULL); + assert(src != NULL); + + return (bt_bdaddr_t *)memcpy(dest, src, sizeof(bt_bdaddr_t)); +} + +const char *bdaddr_to_string(const bt_bdaddr_t *addr, char *string, size_t size) +{ + assert(addr != NULL); + assert(string != NULL); + + if (size < 18) { + return NULL; + } + + const uint8_t *ptr = addr->address; + sprintf(string, "%02x:%02x:%02x:%02x:%02x:%02x", + ptr[0], ptr[1], ptr[2], + ptr[3], ptr[4], ptr[5]); + return string; +} + +bool string_is_bdaddr(const char *string) +{ + assert(string != NULL); + + size_t len = strlen(string); + if (len != 17) { + return false; + } + + for (size_t i = 0; i < len; ++i) { + // Every 3rd char must be ':'. + if (((i + 1) % 3) == 0 && string[i] != ':') { + return false; + } + + // All other chars must be a hex digit. + if (((i + 1) % 3) != 0 && !ets_isxdigit(string[i])) { + return false; + } + } + return true; +} + +bool string_to_bdaddr(const char *string, bt_bdaddr_t *addr) +{ + assert(string != NULL); + assert(addr != NULL); + + bt_bdaddr_t new_addr; + uint8_t *ptr = new_addr.address; + uint32_t ptr_32[6]; + bool ret = sscanf(string, "%02x:%02x:%02x:%02x:%02x:%02x", + &ptr_32[0], &ptr_32[1], &ptr_32[2], &ptr_32[3], &ptr_32[4], &ptr_32[5]) == 6; + if (ret) { + for (uint8_t i = 0; i < 6; i++){ + ptr[i] = (uint8_t) ptr_32[i]; + } + memcpy(addr, &new_addr, sizeof(bt_bdaddr_t)); + } + + return ret; +} + +hash_index_t hash_function_bdaddr(const void *key) +{ + hash_index_t hash = 5381; + const char *bytes = (const char *)key; + for (size_t i = 0; i < sizeof(bt_bdaddr_t); ++i) { + hash = ((hash << 5) + hash) + bytes[i]; + } + return hash; +} diff --git a/lib/bt/host/bluedroid/device/controller.c b/lib/bt/host/bluedroid/device/controller.c new file mode 100644 index 00000000..beb7d676 --- /dev/null +++ b/lib/bt/host/bluedroid/device/controller.c @@ -0,0 +1,600 @@ +/****************************************************************************** + * + * Copyright (C) 2014 Google, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ +#include +#include "common/bt_target.h" +#include "common/bt_trace.h" +#include "device/bdaddr.h" +#include "stack/bt_types.h" +#include "device/controller.h" +#include "device/event_mask.h" +#include "stack/hcimsgs.h" +#include "hci/hci_layer.h" +#include "hci/hci_packet_factory.h" +#include "hci/hci_packet_parser.h" +#include "stack/btm_ble_api.h" +#include "device/version.h" +#include "osi/future.h" +#include "config/stack_config.h" +#if (BLE_50_FEATURE_SUPPORT == TRUE) +const bt_event_mask_t BLE_EVENT_MASK = { "\x00\x00\x00\x00\x00\xff\xff\xff" }; +#else +const bt_event_mask_t BLE_EVENT_MASK = { "\x00\x00\x00\x00\x00\x00\x06\x7f" }; +#endif + +#if (BLE_INCLUDED) +const bt_event_mask_t CLASSIC_EVENT_MASK = { HCI_DUMO_EVENT_MASK_EXT }; +#else +const bt_event_mask_t CLASSIC_EVENT_MASK = { HCI_LISBON_EVENT_MASK_EXT }; +#endif + +// TODO(zachoverflow): factor out into common module +const uint8_t SCO_HOST_BUFFER_SIZE = 0xff; + +#define HCI_SUPPORTED_COMMANDS_ARRAY_SIZE 64 +#define MAX_FEATURES_CLASSIC_PAGE_COUNT 3 +#define BLE_SUPPORTED_STATES_SIZE 8 +#define BLE_SUPPORTED_FEATURES_SIZE 8 +#define BLE_EXT_ADV_DATA_LEN_MAX 1650 + +typedef struct { + const hci_t *hci; + const hci_packet_factory_t *packet_factory; + const hci_packet_parser_t *packet_parser; + + bt_version_t bt_version; + bt_bdaddr_t address; + + uint8_t supported_commands[HCI_SUPPORTED_COMMANDS_ARRAY_SIZE]; + uint8_t last_features_classic_page_index; + bt_device_features_t features_classic[MAX_FEATURES_CLASSIC_PAGE_COUNT]; + + uint16_t acl_data_size_classic; + uint16_t acl_data_size_ble; + uint16_t acl_buffer_count_classic; + uint8_t acl_buffer_count_ble; + + uint8_t sco_data_size; + uint16_t sco_buffer_count; + + uint8_t ble_white_list_size; + uint8_t ble_resolving_list_max_size; + uint8_t ble_supported_states[BLE_SUPPORTED_STATES_SIZE]; + bt_device_features_t features_ble; + uint16_t ble_suggested_default_data_length; + uint16_t ble_suggested_default_data_txtime; + + bool readable; + bool ble_supported; + bool simple_pairing_supported; + bool secure_connections_supported; +#if (BLE_50_FEATURE_SUPPORT == TRUE) + uint16_t ble_ext_adv_data_max_len; +#endif //#if (BLE_50_FEATURE_SUPPORT == TRUE) +} controller_local_param_t; + +#if BT_BLE_DYNAMIC_ENV_MEMORY == FALSE +static controller_local_param_t controller_param; +#else +static controller_local_param_t *controller_param_ptr; +#define controller_param (*controller_param_ptr) +#endif + +#define AWAIT_COMMAND(command) future_await(controller_param.hci->transmit_command_futured(command)) + +// Module lifecycle functions + +static void start_up(void) +{ + BT_HDR *response; + + // Send the initial reset command + response = AWAIT_COMMAND(controller_param.packet_factory->make_reset()); + controller_param.packet_parser->parse_generic_command_complete(response); + +#if (CLASSIC_BT_INCLUDED) + // Request the classic buffer size next + response = AWAIT_COMMAND(controller_param.packet_factory->make_read_buffer_size()); + controller_param.packet_parser->parse_read_buffer_size_response( + response, &controller_param.acl_data_size_classic, &controller_param.acl_buffer_count_classic, + &controller_param.sco_data_size, &controller_param.sco_buffer_count); +#endif + +#if (C2H_FLOW_CONTROL_INCLUDED == TRUE) + // Enable controller to host flow control + response = AWAIT_COMMAND(controller_param.packet_factory->make_set_c2h_flow_control(HCI_HOST_FLOW_CTRL_ACL_ON)); + controller_param.packet_parser->parse_generic_command_complete(response); +#endif ///C2H_FLOW_CONTROL_INCLUDED == TRUE +#if (BLE_ADV_REPORT_FLOW_CONTROL == TRUE) + // Enable adv flow control + response = AWAIT_COMMAND(controller_param.packet_factory->make_set_adv_report_flow_control(HCI_HOST_FLOW_CTRL_ADV_REPORT_ON, (uint16_t)BLE_ADV_REPORT_FLOW_CONTROL_NUM, (uint16_t)BLE_ADV_REPORT_DISCARD_THRSHOLD)); + controller_param.packet_parser->parse_generic_command_complete(response); +#endif + // Tell the controller about our buffer sizes and buffer counts next + // TODO(zachoverflow): factor this out. eww l2cap contamination. And why just a hardcoded 10? + response = AWAIT_COMMAND( + controller_param.packet_factory->make_host_buffer_size( + L2CAP_MTU_SIZE, + SCO_HOST_BUFFER_SIZE, + L2CAP_HOST_FC_ACL_BUFS, + 10 + ) + ); + + controller_param.packet_parser->parse_generic_command_complete(response); + + // Read the local version info off the controller next, including + // information such as manufacturer and supported HCI version + response = AWAIT_COMMAND(controller_param.packet_factory->make_read_local_version_info()); + controller_param.packet_parser->parse_read_local_version_info_response(response, &controller_param.bt_version); + + // Read the bluetooth address off the controller next + response = AWAIT_COMMAND(controller_param.packet_factory->make_read_bd_addr()); + controller_param.packet_parser->parse_read_bd_addr_response(response, &controller_param.address); + + // Request the controller's supported commands next + response = AWAIT_COMMAND(controller_param.packet_factory->make_read_local_supported_commands()); + controller_param.packet_parser->parse_read_local_supported_commands_response( + response, + controller_param.supported_commands, + HCI_SUPPORTED_COMMANDS_ARRAY_SIZE + ); + +#if (CLASSIC_BT_INCLUDED) + // Read page 0 of the controller features next + uint8_t page_number = 0; + response = AWAIT_COMMAND(controller_param.packet_factory->make_read_local_extended_features(page_number)); + controller_param.packet_parser->parse_read_local_extended_features_response( + response, + &page_number, + &controller_param.last_features_classic_page_index, + controller_param.features_classic, + MAX_FEATURES_CLASSIC_PAGE_COUNT + ); + + assert(page_number == 0); + page_number++; +#endif + + // Inform the controller what page 0 features we support, based on what + // it told us it supports. We need to do this first before we request the + // next page, because the controller's response for page 1 may be + // dependent on what we configure from page 0 and host SSP configuration + controller_param.simple_pairing_supported = HCI_SIMPLE_PAIRING_SUPPORTED( + controller_param.features_classic[0].as_array) && + (bluedriod_config_get()->get_ssp_enabled()); + if (controller_param.simple_pairing_supported) { + response = AWAIT_COMMAND(controller_param.packet_factory->make_write_simple_pairing_mode(HCI_SP_MODE_ENABLED)); + controller_param.packet_parser->parse_generic_command_complete(response); + } + +#if (BLE_INCLUDED == TRUE) + if (HCI_LE_SPT_SUPPORTED(controller_param.features_classic[0].as_array)) { + uint8_t simultaneous_le_host = HCI_SIMUL_LE_BREDR_SUPPORTED(controller_param.features_classic[0].as_array) ? BTM_BLE_SIMULTANEOUS_HOST : 0; + response = AWAIT_COMMAND( + controller_param.packet_factory->make_ble_write_host_support(BTM_BLE_HOST_SUPPORT, simultaneous_le_host) + ); + + controller_param.packet_parser->parse_generic_command_complete(response); + } +#endif + +#if (CLASSIC_BT_INCLUDED) + // Done telling the controller about what page 0 features we support + // Request the remaining feature pages + while (page_number <= controller_param.last_features_classic_page_index && + page_number < MAX_FEATURES_CLASSIC_PAGE_COUNT) { + response = AWAIT_COMMAND(controller_param.packet_factory->make_read_local_extended_features(page_number)); + controller_param.packet_parser->parse_read_local_extended_features_response( + response, + &page_number, + &controller_param.last_features_classic_page_index, + controller_param.features_classic, + MAX_FEATURES_CLASSIC_PAGE_COUNT + ); + + page_number++; + } +#endif + +#if (SC_MODE_INCLUDED == TRUE) + controller_param.secure_connections_supported = HCI_SC_CTRLR_SUPPORTED(controller_param.features_classic[2].as_array); + if (controller_param.secure_connections_supported) { + response = AWAIT_COMMAND(controller_param.packet_factory->make_write_secure_connections_host_support(HCI_SC_MODE_ENABLED)); + controller_param.packet_parser->parse_generic_command_complete(response); + } +#endif + +#if (BLE_INCLUDED == TRUE) +#if (CLASSIC_BT_INCLUDED) + controller_param.ble_supported = controller_param.last_features_classic_page_index >= 1 && HCI_LE_HOST_SUPPORTED(controller_param.features_classic[1].as_array); +#else + controller_param.ble_supported = true; +#endif + if (controller_param.ble_supported) { + // Request the ble white list size next + response = AWAIT_COMMAND(controller_param.packet_factory->make_ble_read_white_list_size()); + controller_param.packet_parser->parse_ble_read_white_list_size_response(response, &controller_param.ble_white_list_size); + + // Request the ble buffer size next + response = AWAIT_COMMAND(controller_param.packet_factory->make_ble_read_buffer_size()); + controller_param.packet_parser->parse_ble_read_buffer_size_response( + response, + &controller_param.acl_data_size_ble, + &controller_param.acl_buffer_count_ble + ); + + // Response of 0 indicates ble has the same buffer size as classic + if (controller_param.acl_data_size_ble == 0) { + controller_param.acl_data_size_ble = controller_param.acl_data_size_classic; + } + + // Request the ble supported states next + response = AWAIT_COMMAND(controller_param.packet_factory->make_ble_read_supported_states()); + controller_param.packet_parser->parse_ble_read_supported_states_response( + response, + controller_param.ble_supported_states, + sizeof(controller_param.ble_supported_states) + ); + + // Request the ble supported features next + response = AWAIT_COMMAND(controller_param.packet_factory->make_ble_read_local_supported_features()); + controller_param.packet_parser->parse_ble_read_local_supported_features_response( + response, + &controller_param.features_ble + ); + + if (HCI_LE_ENHANCED_PRIVACY_SUPPORTED(controller_param.features_ble.as_array)) { + response = AWAIT_COMMAND(controller_param.packet_factory->make_ble_read_resolving_list_size()); + controller_param.packet_parser->parse_ble_read_resolving_list_size_response( + response, + &controller_param.ble_resolving_list_max_size); + } +#if BLE_50_FEATURE_SUPPORT == TRUE + controller_param.ble_ext_adv_data_max_len = BLE_EXT_ADV_DATA_LEN_MAX; +#endif //#if (BLE_50_FEATURE_SUPPORT == TRUE) + +#if (BLE_50_FEATURE_SUPPORT == TRUE && BLE_42_FEATURE_SUPPORT == FALSE) + if (HCI_LE_ENHANCED_PRIVACY_SUPPORTED(controller_param.features_ble.as_array)) { + response = AWAIT_COMMAND(controller_param.packet_factory->make_read_max_adv_data_len()); + controller_param.packet_parser->parse_ble_read_adv_max_len_response( + response, + &controller_param.ble_ext_adv_data_max_len); + } +#endif // (BLE_50_FEATURE_SUPPORT == TRUE && BLE_42_FEATURE_SUPPORT == FALSE) + + if (HCI_LE_DATA_LEN_EXT_SUPPORTED(controller_param.features_ble.as_array)) { + /* set default tx data length to MAX 251 */ + response = AWAIT_COMMAND(controller_param.packet_factory->make_ble_write_suggested_default_data_length(BTM_BLE_DATA_SIZE_MAX, BTM_BLE_DATA_TX_TIME_MAX)); + controller_param.packet_parser->parse_generic_command_complete(response); + + response = AWAIT_COMMAND(controller_param.packet_factory->make_ble_read_suggested_default_data_length()); + controller_param.packet_parser->parse_ble_read_suggested_default_data_length_response( + response, + &controller_param.ble_suggested_default_data_length, + &controller_param.ble_suggested_default_data_txtime); + } + + // Set the ble event mask next + response = AWAIT_COMMAND(controller_param.packet_factory->make_ble_set_event_mask(&BLE_EVENT_MASK)); + controller_param.packet_parser->parse_generic_command_complete(response); + } +#endif + + response = AWAIT_COMMAND(controller_param.packet_factory->make_set_event_mask(&CLASSIC_EVENT_MASK)); + controller_param.packet_parser->parse_generic_command_complete(response); + +#if (BTM_SCO_HCI_INCLUDED == TRUE) + response = AWAIT_COMMAND(controller_param.packet_factory->make_write_sync_flow_control_enable(1)); + controller_param.packet_parser->parse_generic_command_complete(response); + + response = AWAIT_COMMAND(controller_param.packet_factory->make_write_default_erroneous_data_report(1)); + controller_param.packet_parser->parse_generic_command_complete(response); +#endif + controller_param.readable = true; + // return future_new_immediate(FUTURE_SUCCESS); + return; +} + +static void shut_down(void) +{ + controller_param.readable = false; +} + +static bool get_is_ready(void) +{ + return controller_param.readable; +} + +static const bt_bdaddr_t *get_address(void) +{ + assert(controller_param.readable); + return &controller_param.address; +} + +static const bt_version_t *get_bt_version(void) +{ + assert(controller_param.readable); + return &controller_param.bt_version; +} + +// TODO(zachoverflow): hide inside, move decoder inside too +static const bt_device_features_t *get_features_classic(int index) +{ + assert(controller_param.readable); + assert(index < MAX_FEATURES_CLASSIC_PAGE_COUNT); + return &controller_param.features_classic[index]; +} + +static uint8_t get_last_features_classic_index(void) +{ + assert(controller_param.readable); + return controller_param.last_features_classic_page_index; +} + +static const bt_device_features_t *get_features_ble(void) +{ + assert(controller_param.readable); + assert(controller_param.ble_supported); + return &controller_param.features_ble; +} + +static const uint8_t *get_ble_supported_states(void) +{ + assert(controller_param.readable); + assert(controller_param.ble_supported); + return controller_param.ble_supported_states; +} + +static bool supports_simple_pairing(void) +{ + assert(controller_param.readable); + return controller_param.simple_pairing_supported; +} + +static bool supports_secure_connections(void) +{ + assert(controller_param.readable); + return controller_param.secure_connections_supported; +} + +static bool supports_simultaneous_le_bredr(void) +{ + assert(controller_param.readable); + return HCI_SIMUL_LE_BREDR_SUPPORTED(controller_param.features_classic[0].as_array); +} + +static bool supports_reading_remote_extended_features(void) +{ + assert(controller_param.readable); + return HCI_READ_REMOTE_EXT_FEATURES_SUPPORTED(controller_param.supported_commands); +} + +static bool supports_interlaced_inquiry_scan(void) +{ + assert(controller_param.readable); + return HCI_LMP_INTERLACED_INQ_SCAN_SUPPORTED(controller_param.features_classic[0].as_array); +} + +static bool supports_rssi_with_inquiry_results(void) +{ + assert(controller_param.readable); + return HCI_LMP_INQ_RSSI_SUPPORTED(controller_param.features_classic[0].as_array); +} + +static bool supports_extended_inquiry_response(void) +{ + assert(controller_param.readable); + return HCI_EXT_INQ_RSP_SUPPORTED(controller_param.features_classic[0].as_array); +} + +static bool supports_master_slave_role_switch(void) +{ + assert(controller_param.readable); + return HCI_SWITCH_SUPPORTED(controller_param.features_classic[0].as_array); +} + +static bool supports_ble(void) +{ + assert(controller_param.readable); + return controller_param.ble_supported; +} + +static bool supports_ble_privacy(void) +{ + assert(controller_param.readable); + assert(controller_param.ble_supported); + return HCI_LE_ENHANCED_PRIVACY_SUPPORTED(controller_param.features_ble.as_array); +} + +static bool supports_ble_packet_extension(void) +{ + assert(controller_param.readable); + assert(controller_param.ble_supported); + return HCI_LE_DATA_LEN_EXT_SUPPORTED(controller_param.features_ble.as_array); +} + +static bool supports_ble_connection_parameters_request(void) +{ + assert(controller_param.readable); + assert(controller_param.ble_supported); + return HCI_LE_CONN_PARAM_REQ_SUPPORTED(controller_param.features_ble.as_array); +} + +static uint16_t get_acl_data_size_classic(void) +{ + assert(controller_param.readable); + return controller_param.acl_data_size_classic; +} + +static uint16_t get_acl_data_size_ble(void) +{ + assert(controller_param.readable); + assert(controller_param.ble_supported); + return controller_param.acl_data_size_ble; +} + +static uint16_t get_acl_packet_size_classic(void) +{ + assert(controller_param.readable); + return controller_param.acl_data_size_classic + HCI_DATA_PREAMBLE_SIZE; +} + +static uint16_t get_acl_packet_size_ble(void) +{ + assert(controller_param.readable); + return controller_param.acl_data_size_ble + HCI_DATA_PREAMBLE_SIZE; +} + +static uint16_t get_ble_suggested_default_data_length(void) +{ + assert(controller_param.readable); + assert(controller_param.ble_supported); + return controller_param.ble_suggested_default_data_length; +} + +static uint16_t get_ble_suggested_default_data_txtime(void) +{ + assert(controller_param.readable); + assert(controller_param.ble_supported); + return controller_param.ble_suggested_default_data_txtime; +} + +static uint16_t get_acl_buffer_count_classic(void) +{ + assert(controller_param.readable); + return controller_param.acl_buffer_count_classic; +} + +static uint8_t get_acl_buffer_count_ble(void) +{ + assert(controller_param.readable); + assert(controller_param.ble_supported); + return controller_param.acl_buffer_count_ble; +} + +static uint8_t get_ble_white_list_size(void) +{ + assert(controller_param.readable); + assert(controller_param.ble_supported); + return controller_param.ble_white_list_size; +} + +static uint8_t get_ble_resolving_list_max_size(void) +{ + assert(controller_param.readable); + assert(controller_param.ble_supported); + return controller_param.ble_resolving_list_max_size; +} + +static void set_ble_resolving_list_max_size(int resolving_list_max_size) +{ + assert(controller_param.readable); + assert(controller_param.ble_supported); + controller_param.ble_resolving_list_max_size = resolving_list_max_size; +} +#if (BLE_50_FEATURE_SUPPORT == TRUE) +static uint16_t ble_get_ext_adv_data_max_len(void) +{ + assert(controller_param.readable); + assert(controller_param.ble_supported); + + return controller_param.ble_ext_adv_data_max_len; +} +#endif // #if (BLE_50_FEATURE_SUPPORT == TRUE) +#if (BTM_SCO_HCI_INCLUDED == TRUE) +static uint8_t get_sco_data_size(void) +{ + assert(controller_param.readable); + return controller_param.sco_data_size; +} + +static uint8_t get_sco_buffer_count(void) +{ + assert(controller_param.readable); + return controller_param.sco_buffer_count; +} +#endif /* (BTM_SCO_HCI_INCLUDED == TRUE) */ + +static const controller_t interface = { + start_up, + shut_down, + get_is_ready, + + get_address, + get_bt_version, + + get_features_classic, + get_last_features_classic_index, + + get_features_ble, + get_ble_supported_states, + + supports_simple_pairing, + supports_secure_connections, + supports_simultaneous_le_bredr, + supports_reading_remote_extended_features, + supports_interlaced_inquiry_scan, + supports_rssi_with_inquiry_results, + supports_extended_inquiry_response, + supports_master_slave_role_switch, + + supports_ble, + supports_ble_packet_extension, + supports_ble_connection_parameters_request, + supports_ble_privacy, + + get_acl_data_size_classic, + get_acl_data_size_ble, + + get_acl_packet_size_classic, + get_acl_packet_size_ble, + get_ble_suggested_default_data_length, + get_ble_suggested_default_data_txtime, + + get_acl_buffer_count_classic, + get_acl_buffer_count_ble, + + get_ble_white_list_size, + + get_ble_resolving_list_max_size, + set_ble_resolving_list_max_size, +#if (BLE_50_FEATURE_SUPPORT == TRUE) + ble_get_ext_adv_data_max_len, +#endif // #if (BLE_50_FEATURE_SUPPORT == TRUE) +#if (BTM_SCO_HCI_INCLUDED == TRUE) + get_sco_data_size, + get_sco_buffer_count, +#endif /* (BTM_SCO_HCI_INCLUDED == TRUE) */ +}; + +const controller_t *controller_get_interface(void) +{ + static bool loaded = false; + if (!loaded) { + loaded = true; +#if (BT_BLE_DYNAMIC_ENV_MEMORY == TRUE) + controller_param_ptr = (controller_local_param_t *)osi_calloc(sizeof(controller_local_param_t)); + assert(controller_param_ptr); +#endif + controller_param.hci = hci_layer_get_interface(); + controller_param.packet_factory = hci_packet_factory_get_interface(); + controller_param.packet_parser = hci_packet_parser_get_interface(); + } + + return &interface; +} diff --git a/lib/bt/host/bluedroid/device/include/device/bdaddr.h b/lib/bt/host/bluedroid/device/include/device/bdaddr.h new file mode 100644 index 00000000..611fcf0e --- /dev/null +++ b/lib/bt/host/bluedroid/device/include/device/bdaddr.h @@ -0,0 +1,63 @@ +/****************************************************************************** + * + * Copyright (C) 2014 Google, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +#ifndef _BDADDR_H_ +#define _BDADDR_H_ + +#include +#include + +#include "common/bt_defs.h" +#include "osi/hash_map.h" + +// Note: the string representation of a bdaddr is expected to have the format +// xx:xx:xx:xx:xx:xx +// where each 'x' is a hex digit. The API presented in this header will accept +// both uppercase and lowercase digits but will only ever produce lowercase +// digits. + +// Returns true if |addr| is the empty address (00:00:00:00:00:00). +// |addr| may not be NULL. +bool bdaddr_is_empty(const bt_bdaddr_t *addr); + +// Returns true if |first| and |second| refer to the same address. Neither +// may be NULL. +bool bdaddr_equals(const bt_bdaddr_t *first, const bt_bdaddr_t *second); + +// Returns destination bdaddr |dest| after copying |src| to |dest|. +// |dest| and |src| must not be NULL. +bt_bdaddr_t *bdaddr_copy(bt_bdaddr_t *dest, const bt_bdaddr_t *src); + +// Makes a string representation of |addr| and places it into |string|. |size| +// refers to the size of |string|'s buffer and must be >= 18. On success, this +// function returns |string|, otherwise it returns NULL. Neither |addr| nor |string| +// may be NULL. +const char *bdaddr_to_string(const bt_bdaddr_t *addr, char *string, size_t size); + +// Returns true if |string| represents a Bluetooth address. |string| may not be NULL. +bool string_is_bdaddr(const char *string); + +// Converts |string| to bt_bdaddr_t and places it in |addr|. If |string| does not +// represent a Bluetooth address, |addr| is not modified and this function returns +// false. Otherwise, it returns true. Neither |string| nor |addr| may be NULL. +bool string_to_bdaddr(const char *string, bt_bdaddr_t *addr); + +// A hash function tailored for bdaddrs. +hash_index_t hash_function_bdaddr(const void *key); + +#endif diff --git a/lib/bt/host/bluedroid/device/include/device/controller.h b/lib/bt/host/bluedroid/device/include/device/controller.h new file mode 100644 index 00000000..6fc766f3 --- /dev/null +++ b/lib/bt/host/bluedroid/device/include/device/controller.h @@ -0,0 +1,96 @@ +/****************************************************************************** + * + * Copyright (C) 2014 Google, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +#ifndef _CONTROLLER_H_ +#define _CONTROLLER_H_ + +#include +#include + +#include "common/bt_target.h" +#include "device/bdaddr.h" +#include "device/device_features.h" +#include "hci/hci_layer.h" +#include "hci/hci_packet_factory.h" +#include "hci/hci_packet_parser.h" + +typedef struct controller_t { + void (*start_up)(void); + void (*shut_down)(void); + bool (*get_is_ready)(void); + + const bt_bdaddr_t *(*get_address)(void); + const bt_version_t *(*get_bt_version)(void); + + const bt_device_features_t *(*get_features_classic)(int index); + + uint8_t (*get_last_features_classic_index)(void); + + const bt_device_features_t *(*get_features_ble)(void); + const uint8_t *(*get_ble_supported_states)(void); + + bool (*supports_simple_pairing)(void); + bool (*supports_secure_connections)(void); + bool (*supports_simultaneous_le_bredr)(void); + bool (*supports_reading_remote_extended_features)(void); + bool (*supports_interlaced_inquiry_scan)(void); + bool (*supports_rssi_with_inquiry_results)(void); + bool (*supports_extended_inquiry_response)(void); + bool (*supports_master_slave_role_switch)(void); + + bool (*supports_ble)(void); + bool (*supports_ble_packet_extension)(void); + bool (*supports_ble_connection_parameters_request)(void); + bool (*supports_ble_privacy)(void); + + // Get the cached acl data sizes for the controller. + uint16_t (*get_acl_data_size_classic)(void); + uint16_t (*get_acl_data_size_ble)(void); + + // Get the cached acl packet sizes for the controller. + // This is a convenience function for the respective + // acl data size + size of the acl header. + uint16_t (*get_acl_packet_size_classic)(void); + uint16_t (*get_acl_packet_size_ble)(void); + + uint16_t (*get_ble_default_data_packet_length)(void); + uint16_t (*get_ble_default_data_packet_txtime)(void); + + // Get the number of acl packets the controller can buffer. + uint16_t (*get_acl_buffer_count_classic)(void); + uint8_t (*get_acl_buffer_count_ble)(void); + + uint8_t (*get_ble_white_list_size)(void); + + uint8_t (*get_ble_resolving_list_max_size)(void); + void (*set_ble_resolving_list_max_size)(int resolving_list_max_size); + +#if (BLE_50_FEATURE_SUPPORT == TRUE) + uint16_t (*ble_get_ext_adv_data_max_len)(void); +#endif // BLE_50_FEATURE_SUPPORT + +#if (BTM_SCO_HCI_INCLUDED == TRUE) + // Get the number of sco packets the controller can buffer + uint8_t (*get_sco_data_size)(void); + uint8_t (*get_sco_buffer_count)(void); +#endif /* #if (BTM_SCO_HCI_INCLUDED == TRUE) */ +} controller_t; + +const controller_t *controller_get_interface(void); + +#endif /*_CONTROLLER_H_*/ diff --git a/lib/bt/host/bluedroid/device/include/device/device_features.h b/lib/bt/host/bluedroid/device/include/device/device_features.h new file mode 100644 index 00000000..360d3768 --- /dev/null +++ b/lib/bt/host/bluedroid/device/include/device/device_features.h @@ -0,0 +1,29 @@ +/****************************************************************************** + * + * Copyright (C) 2014 Google, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ +#ifndef _DEVICE_FEATURES_H_ +#define _DEVICE_FEATURES_H_ + +#include + +// Represents a page of device feature enabled/disabled bits returned +// by the local controller. See the bluetooth spec for bit indexes. +typedef struct { + uint8_t as_array[8]; +} bt_device_features_t; + +#endif /*_DEVICE_FEATURES_H_*/ diff --git a/lib/bt/host/bluedroid/device/include/device/event_mask.h b/lib/bt/host/bluedroid/device/include/device/event_mask.h new file mode 100644 index 00000000..d4d036d5 --- /dev/null +++ b/lib/bt/host/bluedroid/device/include/device/event_mask.h @@ -0,0 +1,30 @@ +/****************************************************************************** + * + * Copyright (C) 2014 Google, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ +#ifndef _EVENT_MASK_H_ +#define _EVENT_MASK_H_ + +#include + +// Represents a mask which can be used to tell the controller which +// HCI events the stack wishes to be informed about. See the bluetooth +// spec for more information on what each bit means. +typedef struct { + uint8_t as_array[8]; +} bt_event_mask_t; + +#endif /*_EVENT_MASK_H_*/ diff --git a/lib/bt/host/bluedroid/device/include/device/interop.h b/lib/bt/host/bluedroid/device/include/device/interop.h new file mode 100644 index 00000000..64f27adb --- /dev/null +++ b/lib/bt/host/bluedroid/device/include/device/interop.h @@ -0,0 +1,45 @@ +/****************************************************************************** + * + * Copyright (C) 2015 Google, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +#ifndef _INTEROP_H_ +#define _INTEROP_H_ + +#include +#include "common/bt_defs.h" +#include "common/bt_target.h" + +typedef enum { + // Disable secure connections + // This is for pre BT 4.1/2 devices that do not handle secure mode + // very well. + INTEROP_DISABLE_LE_SECURE_CONNECTIONS, + + // Some devices have proven problematic during the pairing process, often + // requiring multiple retries to complete pairing. To avoid degrading the user + // experience for those devices, automatically re-try pairing if page + // timeouts are received during pairing. + INTEROP_AUTO_RETRY_PAIRING +} interop_feature_t; + +// Check if a given |addr| matches a known interoperability workaround as identified +// by the |interop_feature_t| enum. This API is used for simple address based lookups +// where more information is not available. No look-ups or random address resolution +// is performed on |addr|. +bool interop_match(const interop_feature_t feature, const bt_bdaddr_t *addr); + +#endif /*_INTEROP_H_*/ diff --git a/lib/bt/host/bluedroid/device/include/device/interop_database.h b/lib/bt/host/bluedroid/device/include/device/interop_database.h new file mode 100644 index 00000000..71224905 --- /dev/null +++ b/lib/bt/host/bluedroid/device/include/device/interop_database.h @@ -0,0 +1,50 @@ +/****************************************************************************** + * + * Copyright (C) 2015 Google, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +#ifndef _INTEROP_DATABASE_H_ +#define _INTEROP_DATABASE_H_ + +#include "device/interop.h" + +typedef struct { + bt_bdaddr_t addr; + uint8_t len; + interop_feature_t feature; +} interop_entry_t; + +static const interop_entry_t interop_database[] = { + // Nexus Remote (Spike) + // Note: May affect other Asus brand devices + {{{0x08, 0x62, 0x66, 0, 0, 0}}, 3, INTEROP_DISABLE_LE_SECURE_CONNECTIONS}, + {{{0x38, 0x2c, 0x4a, 0xc9, 0, 0}}, 4, INTEROP_DISABLE_LE_SECURE_CONNECTIONS}, + {{{0x38, 0x2c, 0x4a, 0xe6, 0, 0}}, 4, INTEROP_DISABLE_LE_SECURE_CONNECTIONS}, + {{{0x54, 0xa0, 0x50, 0xd9, 0, 0}}, 4, INTEROP_DISABLE_LE_SECURE_CONNECTIONS}, + {{{0xac, 0x9e, 0x17, 0, 0, 0}}, 3, INTEROP_DISABLE_LE_SECURE_CONNECTIONS}, + {{{0xf0, 0x79, 0x59, 0, 0, 0}}, 3, INTEROP_DISABLE_LE_SECURE_CONNECTIONS}, + + // Motorola Key Link + {{{0x1c, 0x96, 0x5a, 0, 0, 0}}, 3, INTEROP_DISABLE_LE_SECURE_CONNECTIONS}, + + // Flic smart button + {{{0x80, 0xe4, 0xda, 0x70, 0, 0}}, 4, INTEROP_DISABLE_LE_SECURE_CONNECTIONS}, + + // BMW car kits (Harman/Becker) + {{{0x9c, 0xdf, 0x03, 0, 0, 0}}, 3, INTEROP_AUTO_RETRY_PAIRING} +}; + +#endif /*_INTEROP_DATABASE_H_*/ diff --git a/lib/bt/host/bluedroid/device/include/device/version.h b/lib/bt/host/bluedroid/device/include/device/version.h new file mode 100644 index 00000000..c63b03bd --- /dev/null +++ b/lib/bt/host/bluedroid/device/include/device/version.h @@ -0,0 +1,31 @@ +/****************************************************************************** + * + * Copyright (C) 2014 Google, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ +#ifndef _VERSION_H_ +#define _VERSION_H_ + +#include + +typedef struct { + uint8_t hci_version; + uint16_t hci_revision; + uint8_t lmp_version; + uint16_t manufacturer; + uint16_t lmp_subversion; +} bt_version_t; + +#endif /*_VERSION_H_*/ diff --git a/lib/bt/host/bluedroid/device/interop.c b/lib/bt/host/bluedroid/device/interop.c new file mode 100644 index 00000000..f5601354 --- /dev/null +++ b/lib/bt/host/bluedroid/device/interop.c @@ -0,0 +1,62 @@ +/****************************************************************************** + * + * Copyright (C) 2015 Google, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ +/* +#define LOG_TAG "bt_device_interop" +*/ +#include // For memcmp +#include "common/bt_trace.h" +#include "device/bdaddr.h" +#include "device/interop.h" +#include "device/interop_database.h" + +#define CASE_RETURN_STR(const) case const: return #const; + +#if (SMP_INCLUDED == TRUE) +#if (!CONFIG_BT_STACK_NO_LOG) +static const char *interop_feature_string(const interop_feature_t feature) +{ + switch (feature) { + CASE_RETURN_STR(INTEROP_DISABLE_LE_SECURE_CONNECTIONS) + CASE_RETURN_STR(INTEROP_AUTO_RETRY_PAIRING) + } + + return "UNKNOWN"; +} +#endif // (!CONFIG_BT_STACK_NO_LOG) +// Interface functions +bool interop_match(const interop_feature_t feature, const bt_bdaddr_t *addr) +{ + assert(addr); + + const size_t db_size = sizeof(interop_database) / sizeof(interop_entry_t); + + for (size_t i = 0; i != db_size; ++i) { + if (feature == interop_database[i].feature && + memcmp(addr, &interop_database[i].addr, interop_database[i].len) == 0) { +#if (!CONFIG_BT_STACK_NO_LOG) + char bdstr[20] = {0}; +#endif + LOG_WARN("%s() Device %s is a match for interop workaround %s", __func__, + bdaddr_to_string(addr, bdstr, sizeof(bdstr)), interop_feature_string(feature)); + return true; + } + } + + return false; +} +#endif ///SMP_INCLUDED == TRUE diff --git a/lib/bt/host/bluedroid/external/sbc/decoder/include/oi_assert.h b/lib/bt/host/bluedroid/external/sbc/decoder/include/oi_assert.h new file mode 100644 index 00000000..2f20f3a0 --- /dev/null +++ b/lib/bt/host/bluedroid/external/sbc/decoder/include/oi_assert.h @@ -0,0 +1,85 @@ +/****************************************************************************** + * + * Copyright (C) 2014 The Android Open Source Project + * Copyright 2002 - 2004 Open Interface North America, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ +#ifndef _OI_ASSERT_H +#define _OI_ASSERT_H +/** @file + This file provides macros and functions for compile-time and run-time assertions. + + When the OI_DEBUG preprocessor value is defined, the macro OI_ASSERT is compiled into + the program, providing for a runtime assertion failure check. + C_ASSERT is a macro that can be used to perform compile time checks. +*/ +/********************************************************************************** + $Revision: #1 $ +***********************************************************************************/ + + +/** \addtogroup Debugging Debugging APIs */ +/**@{*/ + +#ifdef __cplusplus +extern "C" { +#endif + + +#ifdef OI_DEBUG + +/** The macro OI_ASSERT takes a condition argument. If the asserted condition + does not evaluate to true, the OI_ASSERT macro calls the host-dependent function, + OI_AssertFail(), which reports the failure and generates a runtime error. +*/ +void OI_AssertFail(char *file, int line, char *reason); + + +#define OI_ASSERT(condition) \ + { if (!(condition)) OI_AssertFail(__FILE__, __LINE__, #condition); } + +#define OI_ASSERT_FAIL(msg) \ + { OI_AssertFail(__FILE__, __LINE__, msg); } + +#else + + +#define OI_ASSERT(condition) +#define OI_ASSERT_FAIL(msg) + +#endif + + +/** + C_ASSERT() can be used to perform many compile-time assertions: type sizes, field offsets, etc. + An assertion failure results in compile time error C2118: negative subscript. + Unfortunately, this elegant macro doesn't work with GCC, so it's all commented out + for now. Perhaps later..... +*/ + +#ifndef C_ASSERT +// #define C_ASSERT(e) typedef char __C_ASSERT__[(e)?1:-1] +// #define C_ASSERT(e) +#endif + + +/*****************************************************************************/ +#ifdef __cplusplus +} +#endif + +/**@}*/ + +#endif /* _OI_ASSERT_H */ diff --git a/lib/bt/host/bluedroid/external/sbc/decoder/include/oi_bitstream.h b/lib/bt/host/bluedroid/external/sbc/decoder/include/oi_bitstream.h new file mode 100644 index 00000000..c6ce59b4 --- /dev/null +++ b/lib/bt/host/bluedroid/external/sbc/decoder/include/oi_bitstream.h @@ -0,0 +1,123 @@ +/****************************************************************************** + * + * Copyright (C) 2014 The Android Open Source Project + * Copyright 2003 - 2004 Open Interface North America, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ +#ifndef _OI_BITSTREAM_H +#define _OI_BITSTREAM_H + +/********************************************************************************** + $Revision: #1 $ +***********************************************************************************/ + + +/** +@file +Function prototypes and macro definitions for manipulating input and output +bitstreams. + +@ingroup codec_internal +*/ + +/** +@addtogroup codec_internal +@{ +*/ + +#include "oi_codec_sbc_private.h" +#include "oi_stddefs.h" + +INLINE void OI_BITSTREAM_ReadInit(OI_BITSTREAM *bs, const OI_BYTE *buffer); + +INLINE void OI_BITSTREAM_WriteInit(OI_BITSTREAM *bs, OI_BYTE *buffer); + +INLINE OI_UINT32 OI_BITSTREAM_ReadUINT(OI_BITSTREAM *bs, OI_UINT bits); + +INLINE OI_UINT8 OI_BITSTREAM_ReadUINT4Aligned(OI_BITSTREAM *bs); + +INLINE OI_UINT8 OI_BITSTREAM_ReadUINT8Aligned(OI_BITSTREAM *bs); + +INLINE void OI_BITSTREAM_WriteUINT(OI_BITSTREAM *bs, + OI_UINT16 value, + OI_UINT bits); + +/* + * Use knowledge that the bitstream is aligned to optimize the write of a byte + */ +PRIVATE void OI_BITSTREAM_WriteUINT8Aligned(OI_BITSTREAM *bs, + OI_UINT8 datum); + +/* + * Use knowledge that the bitstream is aligned to optimize the write pair of nibbles + */ +PRIVATE void OI_BITSTREAM_Write2xUINT4Aligned(OI_BITSTREAM *bs, + OI_UINT8 datum1, + OI_UINT8 datum2); + +/** Internally the bitstream looks ahead in the stream. When + * OI_SBC_ReadScalefactors() goes to temporarily break the abstraction, it will + * need to know where the "logical" pointer is in the stream. + */ +#define OI_BITSTREAM_GetWritePtr(bs) ((bs)->ptr.w - 3) +#define OI_BITSTREAM_GetReadPtr(bs) ((bs)->ptr.r - 3) + +/** This is declared here as a macro because decoder.c breaks the bitsream + * encapsulation for efficiency reasons. + */ +#define OI_BITSTREAM_READUINT(result, bits, ptr, value, bitPtr) \ +do { \ + OI_ASSERT((bits) <= 16); \ + OI_ASSERT((bitPtr) < 16); \ + OI_ASSERT((bitPtr) >= 8); \ + \ + result = (value) << (bitPtr); \ + result >>= 32 - (bits); \ + \ + bitPtr += (bits); \ + while (bitPtr >= 16) { \ + value = ((value) << 8) | *ptr++; \ + bitPtr -= 8; \ + } \ + OI_ASSERT((bits == 0) || (result < (1u << (bits)))); \ +} while (0) + + +#define OI_BITSTREAM_WRITEUINT(ptr, value, bitPtr, datum, bits) \ +do {\ + bitPtr -= bits;\ + value |= datum << bitPtr;\ + \ + while (bitPtr <= 16) {\ + bitPtr += 8;\ + *ptr++ = (OI_UINT8)(value >> 24);\ + value <<= 8;\ + }\ +} while (0) + +#define OI_BITSTREAM_WRITEFLUSH(ptr, value, bitPtr) \ +do {\ + while (bitPtr < 32) {\ + bitPtr += 8;\ + *ptr++ = (OI_UINT8)(value >> 24);\ + value <<= 8;\ + }\ +} while (0) + +/** +@} +*/ + +#endif /* _OI_BITSTREAM_H */ diff --git a/lib/bt/host/bluedroid/external/sbc/decoder/include/oi_bt_spec.h b/lib/bt/host/bluedroid/external/sbc/decoder/include/oi_bt_spec.h new file mode 100644 index 00000000..b98a5821 --- /dev/null +++ b/lib/bt/host/bluedroid/external/sbc/decoder/include/oi_bt_spec.h @@ -0,0 +1,229 @@ +/****************************************************************************** + * + * Copyright (C) 2014 The Android Open Source Project + * Copyright 2002 - 2004 Open Interface North America, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ +#ifndef _OI_BT_SPEC_H +#define _OI_BT_SPEC_H +/** + * @file + * + * This file contains common definitions from the Bluetooth specification. + * + */ + +/********************************************************************************** + $Revision: #1 $ +***********************************************************************************/ + +#include "oi_stddefs.h" + +/** \addtogroup Misc Miscellaneous APIs */ +/**@{*/ + +#ifdef __cplusplus +extern "C" { +#endif + +/** The maximum number of active slaves in a piconet. */ +#define OI_BT_MAX_ACTIVE_SLAVES 7 + +/** the number of bytes in a Bluetooth device address (BD_ADDR) */ +#define OI_BD_ADDR_BYTE_SIZE 6 + +/** + * 48-bit Bluetooth device address + * + * Because 48-bit integers may not be supported on all platforms, the + * address is defined as an array of bytes. This array is big-endian, + * meaning that + * - array[0] contains bits 47-40, + * - array[1] contains bits 39-32, + * - array[2] contains bits 31-24, + * - array[3] contains bits 23-16, + * - array[4] contains bits 15-8, and + * - array[5] contains bits 7-0. + */ +typedef struct { + OI_UINT8 addr[OI_BD_ADDR_BYTE_SIZE] ; /**< Bluetooth device address represented as an array of 8-bit values */ +} OI_BD_ADDR ; + +/** + * @name Data types for working with UUIDs + * UUIDs are 16 bytes (128 bits). + * + * To avoid having to pass around 128-bit values all the time, 32-bit and 16-bit + * UUIDs are defined, along with a mapping from the shorter versions to the full + * version. + * + * @{ + */ + +/** + * 16-bit representation of a 128-bit UUID + */ +typedef OI_UINT16 OI_UUID16; + +/** + * 32-bit representation of a 128-bit UUID + */ +typedef OI_UINT32 OI_UUID32; + +/** + * number of bytes in a 128 bit UUID + */ +#define OI_BT_UUID128_SIZE 16 + +/** + * number of bytes in IPv6 style addresses + */ +#define OI_BT_IPV6ADDR_SIZE 16 + +/** + * type definition for a 128-bit UUID + * + * To simplify conversion between 128-bit UUIDs and 16-bit and 32-bit UUIDs, + * the most significant 32 bits are stored with the same endian-ness as is + * native on the target (local) device. The remainder of the 128-bit UUID is + * stored as bytes in big-endian order. + */ +typedef struct { + OI_UINT32 ms32bits; /**< most significant 32 bits of 128-bit UUID */ + OI_UINT8 base[OI_BT_UUID128_SIZE - sizeof(OI_UINT32)]; /**< remainder of 128-bit UUID, array of 8-bit values */ +} OI_UUID128; + +/** @} */ + +/** number of bytes in a link key */ +#define OI_BT_LINK_KEY_SIZE 16 + +/** + * type definition for a baseband link key + * + * Because 128-bit integers may not be supported on all platforms, we define + * link keys as an array of bytes. Unlike the Bluetooth device address, + * the link key is stored in little-endian order, meaning that + * - array[0] contains bits 0 - 7, + * - array[1] contains bits 8 - 15, + * - array[2] contains bits 16 - 23, + * - array[3] contains bits 24 - 31, + * - array[4] contains bits 32 - 39, + * - array[5] contains bits 40 - 47, + * - array[6] contains bits 48 - 55, + * - array[7] contains bits 56 - 63, + * - array[8] contains bits 64 - 71, + * - array[9] contains bits 72 - 79, + * - array[10] contains bits 80 - 87, + * - array[11] contains bits 88 - 95, + * - array[12] contains bits 96 - 103, + * - array[13] contains bits 104- 111, + * - array[14] contains bits 112- 119, and + * - array[15] contains bits 120- 127. + */ +typedef struct { + OI_UINT8 key[OI_BT_LINK_KEY_SIZE] ; /**< link key represented as an array of 8-bit values */ +} OI_LINK_KEY ; + + +/** Out-of-band data size - C and R values are 16-bytes each */ +#define OI_BT_OOB_NUM_BYTES 16 + +typedef struct { + OI_UINT8 value[OI_BT_OOB_NUM_BYTES] ; /**< same struct used for C and R values */ +} OI_OOB_DATA ; + + +/** + * link key types + */ +typedef enum { + OI_LINK_KEY_TYPE_COMBO = 0, /**< combination key */ + OI_LINK_KEY_TYPE_LOCAL_UNIT = 1, /**< local unit key */ + OI_LINK_KEY_TYPE_REMOTE_UNIT = 2, /**< remote unit key */ + OI_LINK_KEY_TYPE_DEBUG_COMBO = 3, /**< debug combination key */ + OI_LINK_KEY_TYPE_UNAUTHENTICATED = 4, /**< Unauthenticated */ + OI_LINK_KEY_TYPE_AUTHENTICATED = 5, /**< Authenticated */ + OI_LINK_KEY_TYPE_CHANGED_COMBO = 6 /**< Changed */ + +} OI_BT_LINK_KEY_TYPE ; + + +/** amount of space allocated for a PIN (personal indentification number) in bytes */ +#define OI_BT_PIN_CODE_SIZE 16 + +/** data type for a PIN (PINs are treated as strings, so endianness does not apply.) */ +typedef struct { + OI_UINT8 pin[OI_BT_PIN_CODE_SIZE] ; /**< PIN represented as an array of 8-bit values */ +} OI_PIN_CODE ; + +/** maximum number of SCO connections per device, which is 3 as of version 2.0+EDR + of the Bluetooth specification (see sec 4.3 of vol 2 part B) */ +#define OI_BT_MAX_SCO_CONNECTIONS 3 + +/** data type for clock offset */ +typedef OI_UINT16 OI_BT_CLOCK_OFFSET ; + +/** data type for a LM handle */ +typedef OI_UINT16 OI_HCI_LM_HANDLE; + +/** opaque data type for a SCO or ACL connection handle */ +typedef struct _OI_HCI_CONNECTION *OI_HCI_CONNECTION_HANDLE; + +/** data type for HCI Error Code, as defined in oi_hcispec.h */ +typedef OI_UINT8 OI_HCI_ERROR_CODE ; + +/** + * The Bluetooth device type is indicated by a 24-bit bitfield, represented as a + * 32-bit number in the stack. The bit layout and values for device class are specified + * in the file oi_bt_assigned_nos.h and in the Bluetooth "Assigned Numbers" specification + * at http://www.bluetooth.org/assigned-numbers/. + */ +typedef OI_UINT32 OI_BT_DEVICE_CLASS ; + +#define OI_BT_DEV_CLASS_FORMAT_MASK 0x000003 /**< Bits 0-1 contain format type. */ +#define OI_BT_DEV_CLASS_MINOR_DEVICE_MASK 0x0000FC /**< Bits 2-7 contain minor device class value. */ +#define OI_BT_DEV_CLASS_MAJOR_DEVICE_MASK 0x001F00 /**< Bits 8-12 contain major device class value. */ +#define OI_BT_DEV_CLASS_MAJOR_SERVICE_MASK 0xFFE000 /**< Bits 13-23 contain major service class value. */ + +/** There is currently only one device class format defined, type 00. */ +#define OI_BT_DEV_CLASS_FORMAT_TYPE 00 + +/** Bit 13 in device class indicates limited discoverability mode (GAP v2.0+EDR, section 4.1.2.2) */ +#define OI_BT_DEV_CLASS_LIMITED_DISCO_BIT BIT13 + +/** macro to test validity of the Device Class Format */ +#define OI_BT_VALID_DEVICE_CLASS_FORMAT(class) (OI_BT_DEV_CLASS_FORMAT_TYPE == ((class) & OI_BT_DEV_CLASS_FORMAT_MASK)) + +/** the time between baseband clock ticks, currently 625 microseconds (one slot) */ +#define OI_BT_TICK 625 +/** some macros to convert to/from baseband clock ticks - use no floating point! */ +#define OI_SECONDS_TO_BT_TICKS(secs) ((secs)*1600) +#define OI_BT_TICKS_TO_SECONDS(ticks) ((ticks)/1600) +#define OI_MSECS_TO_BT_TICKS(msecs) (((msecs)*8)/5) +#define OI_BT_TICKS_TO_MSECS(ticks) (((ticks)*5)/8) + +/** EIR byte order */ +#define OI_EIR_BYTE_ORDER OI_LITTLE_ENDIAN_BYTE_ORDER + + +#ifdef __cplusplus +} +#endif + +/**@}*/ + +/*****************************************************************************/ +#endif /* _OI_BT_SPEC_H */ diff --git a/lib/bt/host/bluedroid/external/sbc/decoder/include/oi_codec_sbc.h b/lib/bt/host/bluedroid/external/sbc/decoder/include/oi_codec_sbc.h new file mode 100644 index 00000000..495fbf6b --- /dev/null +++ b/lib/bt/host/bluedroid/external/sbc/decoder/include/oi_codec_sbc.h @@ -0,0 +1,488 @@ +/****************************************************************************** + * + * Copyright (C) 2014 The Android Open Source Project + * Copyright 2003 - 2004 Open Interface North America, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/********************************************************************************** + $Revision: #1 $ +***********************************************************************************/ + +#ifndef _OI_CODEC_SBC_CORE_H +#define _OI_CODEC_SBC_CORE_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** +@file +Declarations of codec functions, data types, and macros. + +@ingroup codec_lib +*/ + +/** +@addtogroup codec_lib +@{ +*/ + +/* Non-BM3 users of of the codec must include oi_codec_sbc_bm3defs.h prior to + * including this file, or else these includes will fail because the BM3 SDK is + * not in the include path */ +#ifndef _OI_CODEC_SBC_BM3DEFS_H +#include "oi_stddefs.h" +#include "oi_status.h" +#endif + +#include + +#define SBC_MAX_CHANNELS 2 +#define SBC_MAX_BANDS 8 +#define SBC_MAX_BLOCKS 16 +#define SBC_MIN_BITPOOL 2 /**< Minimum size of the bit allocation pool used to encode the stream */ +#define SBC_MAX_BITPOOL 250 /**< Maximum size of the bit allocation pool used to encode the stream */ +#define SBC_MAX_ONE_CHANNEL_BPS 320000 +#define SBC_MAX_TWO_CHANNEL_BPS 512000 + + +#define SBC_WBS_BITRATE 62000 +#define SBC_WBS_BITPOOL 27 +#define SBC_WBS_NROF_BLOCKS 16 +#define SBC_WBS_FRAME_LEN 62 +#define SBC_WBS_SAMPLES_PER_FRAME 128 + + +#define SBC_HEADER_LEN 4 +#define SBC_MAX_FRAME_LEN (SBC_HEADER_LEN + \ + ((SBC_MAX_BANDS * SBC_MAX_CHANNELS / 2) + \ + (SBC_MAX_BANDS + SBC_MAX_BLOCKS * SBC_MAX_BITPOOL + 7)/8)) +#define SBC_MAX_SAMPLES_PER_FRAME (SBC_MAX_BANDS * SBC_MAX_BLOCKS) + +#define SBC_MAX_SCALEFACTOR_BYTES ((4*(SBC_MAX_CHANNELS * SBC_MAX_BANDS) + 7)/8) + +#define OI_SBC_SYNCWORD 0x9c +#define OI_SBC_ENHANCED_SYNCWORD 0x9d +#define OI_mSBC_SYNCWORD 0xad + +#define OI_SBC_MODE_STD 0 +#define OI_SBC_MODE_MSBC 1 + +/**@name Sampling frequencies */ +/**@{*/ +#define SBC_FREQ_16000 0 /**< The sampling frequency is 16 kHz. One possible value for the @a frequency parameter of OI_CODEC_SBC_EncoderConfigure() */ +#define SBC_FREQ_32000 1 /**< The sampling frequency is 32 kHz. One possible value for the @a frequency parameter of OI_CODEC_SBC_EncoderConfigure() */ +#define SBC_FREQ_44100 2 /**< The sampling frequency is 44.1 kHz. One possible value for the @a frequency parameter of OI_CODEC_SBC_EncoderConfigure() */ +#define SBC_FREQ_48000 3 /**< The sampling frequency is 48 kHz. One possible value for the @a frequency parameter of OI_CODEC_SBC_EncoderConfigure() */ +/**@}*/ + +/**@name Channel modes */ +/**@{*/ +#define SBC_MONO 0 /**< The mode of the encoded channel is mono. One possible value for the @a mode parameter of OI_CODEC_SBC_EncoderConfigure() */ +#define SBC_DUAL_CHANNEL 1 /**< The mode of the encoded channel is dual-channel. One possible value for the @a mode parameter of OI_CODEC_SBC_EncoderConfigure() */ +#define SBC_STEREO 2 /**< The mode of the encoded channel is stereo. One possible value for the @a mode parameter of OI_CODEC_SBC_EncoderConfigure() */ +#define SBC_JOINT_STEREO 3 /**< The mode of the encoded channel is joint stereo. One possible value for the @a mode parameter of OI_CODEC_SBC_EncoderConfigure() */ +/**@}*/ + +/**@name Subbands */ +/**@{*/ +#define SBC_SUBBANDS_4 0 /**< The encoded stream has 4 subbands. One possible value for the @a subbands parameter of OI_CODEC_SBC_EncoderConfigure()*/ +#define SBC_SUBBANDS_8 1 /**< The encoded stream has 8 subbands. One possible value for the @a subbands parameter of OI_CODEC_SBC_EncoderConfigure() */ +/**@}*/ + +/**@name Block lengths */ +/**@{*/ +#define SBC_BLOCKS_4 0 /**< A block size of 4 blocks was used to encode the stream. One possible value for the @a blocks parameter of OI_CODEC_SBC_EncoderConfigure() */ +#define SBC_BLOCKS_8 1 /**< A block size of 8 blocks was used to encode the stream is. One possible value for the @a blocks parameter of OI_CODEC_SBC_EncoderConfigure() */ +#define SBC_BLOCKS_12 2 /**< A block size of 12 blocks was used to encode the stream. One possible value for the @a blocks parameter of OI_CODEC_SBC_EncoderConfigure() */ +#define SBC_BLOCKS_16 3 /**< A block size of 16 blocks was used to encode the stream. One possible value for the @a blocks parameter of OI_CODEC_SBC_EncoderConfigure() */ +/**@}*/ + +/**@name Bit allocation methods */ +/**@{*/ +#define SBC_LOUDNESS 0 /**< The bit allocation method. One possible value for the @a loudness parameter of OI_CODEC_SBC_EncoderConfigure() */ +#define SBC_SNR 1 /**< The bit allocation method. One possible value for the @a loudness parameter of OI_CODEC_SBC_EncoderConfigure() */ +/**@}*/ + +/** +@} + +@addtogroup codec_internal +@{ +*/ + +typedef OI_INT16 SBC_BUFFER_T; + + +/** Used internally. */ +typedef struct { + OI_UINT16 frequency; /**< The sampling frequency. Input parameter. */ + OI_UINT8 freqIndex; + + OI_UINT8 nrof_blocks; /**< The block size used to encode the stream. Input parameter. */ + OI_UINT8 blocks; + + + OI_UINT8 nrof_subbands; /**< The number of subbands of the encoded stream. Input parameter. */ + OI_UINT8 subbands; + + OI_UINT8 mode; /**< The mode of the encoded channel. Input parameter. */ + OI_UINT8 nrof_channels; /**< The number of channels of the encoded stream. */ + + OI_UINT8 alloc; /**< The bit allocation method. Input parameter. */ + OI_UINT8 bitpool; /**< Size of the bit allocation pool used to encode the stream. Input parameter. */ + OI_UINT8 crc; /**< Parity check byte used for error detection. */ + OI_UINT8 join; /**< Whether joint stereo has been used. */ + OI_UINT8 enhanced; + OI_UINT8 min_bitpool; /**< This value is only used when encoding. SBC_MAX_BITPOOL if variable + bitpools are disallowed, otherwise the minimum bitpool size that will + be used by the bit allocator. */ + + OI_UINT8 cachedInfo; /**< Information about the previous frame */ +} OI_CODEC_SBC_FRAME_INFO; + +/** Used internally. */ +typedef struct { + const OI_CHAR *codecInfo; + OI_CODEC_SBC_FRAME_INFO frameInfo; + OI_INT8 scale_factor[SBC_MAX_CHANNELS * SBC_MAX_BANDS]; + OI_UINT32 frameCount; + OI_INT32 *subdata; + + SBC_BUFFER_T *filterBuffer[SBC_MAX_CHANNELS]; + OI_INT32 filterBufferLen; + OI_UINT filterBufferOffset; + + union { + OI_UINT8 uint8[SBC_MAX_CHANNELS * SBC_MAX_BANDS]; + OI_UINT32 uint32[SBC_MAX_CHANNELS * SBC_MAX_BANDS / 4]; + } bits; + OI_UINT8 maxBitneed; /**< Running maximum bitneed */ + OI_BYTE formatByte; + OI_UINT8 pcmStride; + OI_UINT8 maxChannels; +} OI_CODEC_SBC_COMMON_CONTEXT; + + +/* + * A smaller value reduces RAM usage at the expense of increased CPU usage. Values in the range + * 27..50 are recommended, beyond 50 there is a diminishing return on reduced CPU usage. + */ +#define SBC_CODEC_MIN_FILTER_BUFFERS 16 +#define SBC_CODEC_FAST_FILTER_BUFFERS 27 + +/* Expands to the number of OI_UINT32s needed to ensure enough memory to encode + * or decode streams of numChannels channels, using numBuffers buffers. + * Example: + * OI_UINT32 decoderData[CODEC_DATA_WORDS(SBC_MAX_CHANNELS, SBC_DECODER_FAST_SYNTHESIS_BUFFERS)]; + * */ +#define CODEC_DATA_WORDS(numChannels, numBuffers) \ + ((\ + (sizeof(OI_INT32) * SBC_MAX_BLOCKS * numChannels * SBC_MAX_BANDS) \ + + (sizeof(SBC_BUFFER_T) * SBC_MAX_CHANNELS * SBC_MAX_BANDS * numBuffers) \ + + (sizeof (OI_UINT32) - 1) \ + ) / sizeof(OI_UINT32)) + +/** Opaque parameter to decoding functions; maintains decoder context. */ +typedef struct { + OI_CODEC_SBC_COMMON_CONTEXT common; + OI_UINT8 limitFrameFormat; /* Boolean, set by OI_CODEC_SBC_DecoderLimit() */ + OI_UINT8 restrictSubbands; + OI_UINT8 enhancedEnabled; + OI_UINT8 bufferedBlocks; + OI_UINT8 sbc_mode; /* OI_SBC_MODE_STD or OI_SBC_MODE_MSBC */ +} OI_CODEC_SBC_DECODER_CONTEXT; + +typedef struct { + OI_UINT32 data[CODEC_DATA_WORDS(1, SBC_CODEC_FAST_FILTER_BUFFERS)]; +} OI_CODEC_SBC_CODEC_DATA_MONO; + +typedef struct { + OI_UINT32 data[CODEC_DATA_WORDS(2, SBC_CODEC_FAST_FILTER_BUFFERS)]; +} OI_CODEC_SBC_CODEC_DATA_STEREO; + +/** +@} + +@addtogroup codec_lib +@{ +*/ + +/** + * This function resets the decoder. The context must be reset when + * changing streams, or if the following stream parameters change: + * number of subbands, stereo mode, or frequency. + * + * @param context Pointer to the decoder context structure to be reset. + * + * @param enhanced If true, enhanced SBC operation is enabled. If enabled, + * the codec will recognize the alternative syncword for + * decoding an enhanced SBC stream. Enhancements should not + * be enabled unless the stream is known to be generated + * by an enhanced encoder, or there is a small possibility + * for decoding glitches if synchronization were to be lost. + */ +OI_STATUS OI_CODEC_SBC_DecoderReset(OI_CODEC_SBC_DECODER_CONTEXT *context, + OI_UINT32 *decoderData, + OI_UINT32 decoderDataBytes, + OI_UINT8 maxChannels, + OI_UINT8 pcmStride, + OI_BOOL enhanced, + OI_BOOL msbc_enable); + +/** + * This function restricts the kind of SBC frames that the Decoder will + * process. Its use is optional. If used, it must be called after + * calling OI_CODEC_SBC_DecoderReset(). After it is called, any calls + * to OI_CODEC_SBC_DecodeFrame() with SBC frames that do not conform + * to the Subband and Enhanced SBC setting will be rejected with an + * OI_STATUS_INVALID_PARAMETERS return. + * + * @param context Pointer to the decoder context structure to be limited. + * + * @param enhanced If true, all frames passed to the decoder must be + * Enhanced SBC frames. If false, all frames must be + * standard SBC frames. + * + * @param subbands May be set to SBC_SUBBANDS_4 or SBC_SUBBANDS_8. All + * frames passed to the decoder must be encoded with + * the requested number of subbands. + * + */ +OI_STATUS OI_CODEC_SBC_DecoderLimit(OI_CODEC_SBC_DECODER_CONTEXT *context, + OI_BOOL enhanced, + OI_UINT8 subbands); + +/** + * This function sets the decoder parameters for a raw decode where the decoder parameters are not + * available in the sbc data stream. OI_CODEC_SBC_DecoderReset must be called + * prior to calling this function. + * + * @param context Decoder context structure. This must be the context must be + * used each time a frame is decoded. + * + * @param enhanced Set to TRUE to enable Qualcomm proprietary + * quality enhancements. + * + * @param frequency One of SBC_FREQ_16000, SBC_FREQ_32000, SBC_FREQ_44100, + * SBC_FREQ_48000 + * + * @param mode One of SBC_MONO, SBC_DUAL_CHANNEL, SBC_STEREO, + * SBC_JOINT_STEREO + * + * @param subbands One of SBC_SUBBANDS_4, SBC_SUBBANDS_8 + * + * @param blocks One of SBC_BLOCKS_4, SBC_BLOCKS_8, SBC_BLOCKS_12, + * SBC_BLOCKS_16 + * + * @param alloc One of SBC_LOUDNESS, SBC_SNR + * + * @param maxBitpool The maximum bitpool size for this context + */ +OI_STATUS OI_CODEC_SBC_DecoderConfigureRaw(OI_CODEC_SBC_DECODER_CONTEXT *context, + OI_BOOL enhanced, + OI_UINT8 frequency, + OI_UINT8 mode, + OI_UINT8 subbands, + OI_UINT8 blocks, + OI_UINT8 alloc, + OI_UINT8 maxBitpool); + +/** + * Decode one SBC frame. The frame has no header bytes. The context must have been previously + * initialized by calling OI_CODEC_SBC_DecoderConfigureRaw(). + * + * @param context Pointer to a decoder context structure. The same context + * must be used each time when decoding from the same stream. + * + * @param bitpool The actual bitpool size for this frame. Must be <= the maxbitpool specified + * in the call to OI_CODEC_SBC_DecoderConfigureRaw(), + * + * @param frameData Address of a pointer to the SBC data to decode. This + * value will be updated to point to the next frame after + * successful decoding. + * + * @param frameBytes Pointer to a UINT32 containing the number of available + * bytes of frame data. This value will be updated to reflect + * the number of bytes remaining after a decoding operation. + * + * @param pcmData Address of an array of OI_INT16 pairs, which will be + * populated with the decoded audio data. This address + * is not updated. + * + * @param pcmBytes Pointer to a UINT32 in/out parameter. On input, it + * should contain the number of bytes available for pcm + * data. On output, it will contain the number of bytes + * written. Note that this differs from the semantics of + * frameBytes. + */ +OI_STATUS OI_CODEC_SBC_DecodeRaw(OI_CODEC_SBC_DECODER_CONTEXT *context, + OI_UINT8 bitpool, + const OI_BYTE **frameData, + OI_UINT32 *frameBytes, + OI_INT16 *pcmData, + OI_UINT32 *pcmBytes); + +/** + * Decode one SBC frame. + * + * @param context Pointer to a decoder context structure. The same context + * must be used each time when decoding from the same stream. + * + * @param frameData Address of a pointer to the SBC data to decode. This + * value will be updated to point to the next frame after + * successful decoding. + * + * @param frameBytes Pointer to a UINT32 containing the number of available + * bytes of frame data. This value will be updated to reflect + * the number of bytes remaining after a decoding operation. + * + * @param pcmData Address of an array of OI_INT16 pairs, which will be + * populated with the decoded audio data. This address + * is not updated. + * + * @param pcmBytes Pointer to a UINT32 in/out parameter. On input, it + * should contain the number of bytes available for pcm + * data. On output, it will contain the number of bytes + * written. Note that this differs from the semantics of + * frameBytes. + */ +OI_STATUS OI_CODEC_SBC_DecodeFrame(OI_CODEC_SBC_DECODER_CONTEXT *context, + const OI_BYTE **frameData, + OI_UINT32 *frameBytes, + OI_INT16 *pcmData, + OI_UINT32 *pcmBytes); + +/** + * Calculate the number of SBC frames but don't decode. CRC's are not checked, + * but the Sync word is found prior to count calculation. + * + * @param frameData Pointer to the SBC data. + * + * @param frameBytes Number of bytes avaiable in the frameData buffer + * + */ +OI_UINT8 OI_CODEC_SBC_FrameCount(OI_BYTE *frameData, + OI_UINT32 frameBytes); + +/** + * Analyze an SBC frame but don't do the decode. + * + * @param context Pointer to a decoder context structure. The same context + * must be used each time when decoding from the same stream. + * + * @param frameData Address of a pointer to the SBC data to decode. This + * value will be updated to point to the next frame after + * successful decoding. + * + * @param frameBytes Pointer to a UINT32 containing the number of available + * bytes of frame data. This value will be updated to reflect + * the number of bytes remaining after a decoding operation. + * + */ +OI_STATUS OI_CODEC_SBC_SkipFrame(OI_CODEC_SBC_DECODER_CONTEXT *context, + const OI_BYTE **frameData, + OI_UINT32 *frameBytes); + +/* Common functions */ + +/** + Calculate the frame length. + + @param frame The frame whose length to calculate + + @return the length of an individual encoded frame in + bytes + */ +OI_UINT16 OI_CODEC_SBC_CalculateFramelen(OI_CODEC_SBC_FRAME_INFO *frame); + + +/** + * Calculate the maximum bitpool size that fits within a given frame length. + * + * @param frame The frame to calculate the bitpool size for + * @param frameLen The frame length to fit the bitpool to + * + * @return the maximum bitpool that will fit in the specified frame length + */ +OI_UINT16 OI_CODEC_SBC_CalculateBitpool(OI_CODEC_SBC_FRAME_INFO *frame, + OI_UINT16 frameLen); + +/** + Calculate the bit rate. + + @param frame The frame whose bit rate to calculate + + @return the approximate bit rate in bits per second, + assuming that stream parameters are constant + */ +OI_UINT32 OI_CODEC_SBC_CalculateBitrate(OI_CODEC_SBC_FRAME_INFO *frame); + +/** + Calculate decoded audio data length for one frame. + + @param frame The frame whose audio data length to calculate + + @return length of decoded audio data for a + single frame, in bytes + */ +OI_UINT16 OI_CODEC_SBC_CalculatePcmBytes(OI_CODEC_SBC_COMMON_CONTEXT *common); + +/** + * Get the codec version text. + * + * @return pointer to text string containing codec version text + * + */ +OI_CHAR *OI_CODEC_Version(void); + + +/** +@} + +@addtogroup codec_internal +@{ +*/ + +extern const OI_CHAR *const OI_CODEC_SBC_FreqText[]; +extern const OI_CHAR *const OI_CODEC_SBC_ModeText[]; +extern const OI_CHAR *const OI_CODEC_SBC_SubbandsText[]; +extern const OI_CHAR *const OI_CODEC_SBC_BlocksText[]; +extern const OI_CHAR *const OI_CODEC_SBC_AllocText[]; + +/** +@} + +@addtogroup codec_lib +@{ +*/ + +#ifdef OI_DEBUG +void OI_CODEC_SBC_DumpConfig(OI_CODEC_SBC_FRAME_INFO *frameInfo); +#else +#define OI_CODEC_SBC_DumpConfig(f) +#endif + +/** +@} +*/ + +#ifdef __cplusplus +} +#endif + + +#endif /* _OI_CODEC_SBC_CORE_H */ diff --git a/lib/bt/host/bluedroid/external/sbc/decoder/include/oi_codec_sbc_private.h b/lib/bt/host/bluedroid/external/sbc/decoder/include/oi_codec_sbc_private.h new file mode 100644 index 00000000..62ee7106 --- /dev/null +++ b/lib/bt/host/bluedroid/external/sbc/decoder/include/oi_codec_sbc_private.h @@ -0,0 +1,229 @@ +/****************************************************************************** + * + * Copyright (C) 2014 The Android Open Source Project + * Copyright 2003 - 2004 Open Interface North America, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ +#ifndef _OI_CODEC_SBC_PRIVATE_H +#define _OI_CODEC_SBC_PRIVATE_H + +/********************************************************************************** + $Revision: #1 $ +***********************************************************************************/ + +/** +@file +Function prototypes and macro definitions used internally by the codec. + +@ingroup codec_internal +*/ + +/** +@addtogroup codec_internal +@{ +*/ + +#ifdef USE_RESTRICT_KEYWORD +#define RESTRICT restrict +#else +#define RESTRICT +#endif + +#ifdef CODEC_DEBUG +#include +#define ERROR(x) do { printf x; printf("\n"); } while (0) +#else +#define ERROR(x) +#endif + +#ifdef TRACE_EXECUTION +#define TRACE(x) do { printf x; printf("\n"); } while (0) +#else +#define TRACE(x) +#endif + +#ifndef PRIVATE +#define PRIVATE +#endif + +#ifndef INLINE +#define INLINE +#endif + +#include "oi_assert.h" +#include "oi_codec_sbc.h" + +#ifndef OI_SBC_SYNCWORD +#define OI_SBC_SYNCWORD 0x9c +#endif + +#ifndef DIVIDE +#define DIVIDE(a, b) ((a) / (b)) +#endif + +typedef union { + OI_UINT8 uint8[SBC_MAX_BANDS]; + OI_UINT32 uint32[SBC_MAX_BANDS / 4]; +} BITNEED_UNION1; + +typedef union { + OI_UINT8 uint8[2 * SBC_MAX_BANDS]; + OI_UINT32 uint32[2 * SBC_MAX_BANDS / 4]; +} BITNEED_UNION2; + +static const OI_UINT16 freq_values[] = { 16000, 32000, 44100, 48000 }; +static const OI_UINT8 block_values[] = { 4, 8, 12, 16 }; +static const OI_UINT8 channel_values[] = { 1, 2, 2, 2 }; +static const OI_UINT8 band_values[] = { 4, 8 }; + + +#define TEST_MODE_SENTINEL "OINA" +#define TEST_MODE_SENTINEL_LENGTH 4 + +/** Used internally. */ +typedef struct { + union { + const OI_UINT8 *r; + OI_UINT8 *w; + } ptr; + OI_UINT32 value; + OI_UINT bitPtr; +} OI_BITSTREAM; + + +#define VALID_INT16(x) (((x) >= OI_INT16_MIN) && ((x) <= OI_INT16_MAX)) +#define VALID_INT32(x) (((x) >= OI_INT32_MIN) && ((x) <= OI_INT32_MAX)) + +#define DCTII_8_SHIFT_IN 0 +#define DCTII_8_SHIFT_OUT 16-DCTII_8_SHIFT_IN + +#define DCTII_8_SHIFT_0 (DCTII_8_SHIFT_OUT) +#define DCTII_8_SHIFT_1 (DCTII_8_SHIFT_OUT) +#define DCTII_8_SHIFT_2 (DCTII_8_SHIFT_OUT) +#define DCTII_8_SHIFT_3 (DCTII_8_SHIFT_OUT) +#define DCTII_8_SHIFT_4 (DCTII_8_SHIFT_OUT) +#define DCTII_8_SHIFT_5 (DCTII_8_SHIFT_OUT) +#define DCTII_8_SHIFT_6 (DCTII_8_SHIFT_OUT-1) +#define DCTII_8_SHIFT_7 (DCTII_8_SHIFT_OUT-2) + +#define DCT_SHIFT 15 + +#define DCTIII_4_SHIFT_IN 2 +#define DCTIII_4_SHIFT_OUT 15 + +#define DCTIII_8_SHIFT_IN 3 +#define DCTIII_8_SHIFT_OUT 14 + +OI_UINT computeBitneed(OI_CODEC_SBC_COMMON_CONTEXT *common, + OI_UINT8 *bitneeds, + OI_UINT ch, + OI_UINT *preferredBitpool); + +void oneChannelBitAllocation(OI_CODEC_SBC_COMMON_CONTEXT *common, + BITNEED_UNION1 *bitneeds, + OI_UINT ch, + OI_UINT bitcount); + + +OI_INT adjustToFitBitpool(const OI_UINT bitpool, + OI_UINT32 *bitneeds, + const OI_UINT subbands, + OI_UINT bitcount, + OI_UINT *excess); + +OI_INT allocAdjustedBits(OI_UINT8 *dest, + OI_INT bits, + OI_INT excess); + +OI_INT allocExcessBits(OI_UINT8 *dest, + OI_INT excess); + +PRIVATE OI_UINT32 internal_CalculateBitrate(OI_CODEC_SBC_FRAME_INFO *frame); + +PRIVATE OI_UINT16 internal_CalculateFramelen(OI_CODEC_SBC_FRAME_INFO *frame); + +void monoBitAllocation(OI_CODEC_SBC_COMMON_CONTEXT *common); + +typedef void (*BIT_ALLOC)(OI_CODEC_SBC_COMMON_CONTEXT *common); + +PRIVATE OI_STATUS internal_DecodeRaw(OI_CODEC_SBC_DECODER_CONTEXT *context, + OI_UINT8 bitpool, + const OI_BYTE **frameData, + OI_UINT32 *frameBytes, + OI_INT16 *pcmData, + OI_UINT32 *pcmBytes); + +OI_STATUS internal_DecoderReset(OI_CODEC_SBC_DECODER_CONTEXT *context, + OI_UINT32 *decoderData, + OI_UINT32 decoderDataBytes, + OI_BYTE maxChannels, + OI_BYTE pcmStride, + OI_BOOL enhanced, + OI_BOOL msbc_enable); + +OI_UINT16 OI_SBC_CalculateFrameAndHeaderlen(OI_CODEC_SBC_FRAME_INFO *frame, OI_UINT *headerLen_); + +PRIVATE OI_UINT32 OI_SBC_MaxBitpool(OI_CODEC_SBC_FRAME_INFO *frame); + +PRIVATE void OI_SBC_ComputeBitAllocation(OI_CODEC_SBC_COMMON_CONTEXT *frame); +PRIVATE OI_UINT8 OI_SBC_CalculateChecksum(OI_CODEC_SBC_FRAME_INFO *frame, OI_BYTE const *data); + +/* Transform functions */ +PRIVATE void shift_buffer(SBC_BUFFER_T *dest, SBC_BUFFER_T *src, OI_UINT wordCount); +PRIVATE void cosineModulateSynth4(SBC_BUFFER_T *RESTRICT out, OI_INT32 const *RESTRICT in); +PRIVATE void SynthWindow40_int32_int32_symmetry_with_sum(OI_INT16 *pcm, SBC_BUFFER_T buffer[80], OI_UINT strideShift); + +void dct3_4(OI_INT32 *RESTRICT out, OI_INT32 const *RESTRICT in); +PRIVATE void analyze4_generated(SBC_BUFFER_T analysisBuffer[RESTRICT 40], + OI_INT16 *pcm, + OI_UINT strideShift, + OI_INT32 subband[4]); + +void dct3_8(OI_INT32 *RESTRICT out, OI_INT32 const *RESTRICT in); + +PRIVATE void analyze8_generated(SBC_BUFFER_T analysisBuffer[RESTRICT 80], + OI_INT16 *pcm, + OI_UINT strideShift, + OI_INT32 subband[8]); + +#ifdef SBC_ENHANCED +PRIVATE void analyze8_enhanced_generated(SBC_BUFFER_T analysisBuffer[RESTRICT 112], + OI_INT16 *pcm, + OI_UINT strideShift, + OI_INT32 subband[8]); +#endif + +/* Decoder functions */ + +void OI_SBC_ReadHeader(OI_CODEC_SBC_COMMON_CONTEXT *common, const OI_BYTE *data); +PRIVATE void OI_SBC_ReadScalefactors(OI_CODEC_SBC_COMMON_CONTEXT *common, const OI_BYTE *b, OI_BITSTREAM *bs); +PRIVATE void OI_SBC_ReadSamples(OI_CODEC_SBC_DECODER_CONTEXT *common, OI_BITSTREAM *ob); +PRIVATE void OI_SBC_ReadSamplesJoint(OI_CODEC_SBC_DECODER_CONTEXT *common, OI_BITSTREAM *global_bs); +PRIVATE void OI_SBC_SynthFrame(OI_CODEC_SBC_DECODER_CONTEXT *context, OI_INT16 *pcm, OI_UINT start_block, OI_UINT nrof_blocks); +OI_INT32 OI_SBC_Dequant(OI_UINT32 raw, OI_UINT scale_factor, OI_UINT bits); +PRIVATE OI_BOOL OI_SBC_ExamineCommandPacket(OI_CODEC_SBC_DECODER_CONTEXT *context, const OI_BYTE *data, OI_UINT32 len); +PRIVATE void OI_SBC_GenerateTestSignal(OI_INT16 pcmData[][2], OI_UINT32 sampleCount); + +PRIVATE void OI_SBC_ExpandFrameFields(OI_CODEC_SBC_FRAME_INFO *frame); +PRIVATE OI_STATUS OI_CODEC_SBC_Alloc(OI_CODEC_SBC_COMMON_CONTEXT *common, + OI_UINT32 *codecDataAligned, + OI_UINT32 codecDataBytes, + OI_UINT8 maxChannels, + OI_UINT8 pcmStride); +/** +@} +*/ + +#endif /* _OI_CODEC_SBC_PRIVATE_H */ diff --git a/lib/bt/host/bluedroid/external/sbc/decoder/include/oi_common.h b/lib/bt/host/bluedroid/external/sbc/decoder/include/oi_common.h new file mode 100644 index 00000000..c4169f93 --- /dev/null +++ b/lib/bt/host/bluedroid/external/sbc/decoder/include/oi_common.h @@ -0,0 +1,43 @@ +/****************************************************************************** + * + * Copyright (C) 2014 The Android Open Source Project + * Copyright 2002 - 2004 Open Interface North America, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ +#ifndef _OI_COMMON_H +#define _OI_COMMON_H +/** + * @file + * + * This file is used to group commonly used BLUEmagic 3.0 software + * header files. + * + * This file should be included in application source code along with the header + * files for the specific modules of the protocol stack being used. + */ + +/********************************************************************************** + $Revision: #1 $ +***********************************************************************************/ + +#include "oi_bt_spec.h" +#include "oi_stddefs.h" +#include "oi_status.h" +#include "oi_time.h" +#include "oi_osinterface.h" + + +/*****************************************************************************/ +#endif /* _OI_COMMON_H */ diff --git a/lib/bt/host/bluedroid/external/sbc/decoder/include/oi_cpu_dep.h b/lib/bt/host/bluedroid/external/sbc/decoder/include/oi_cpu_dep.h new file mode 100644 index 00000000..dfa52c16 --- /dev/null +++ b/lib/bt/host/bluedroid/external/sbc/decoder/include/oi_cpu_dep.h @@ -0,0 +1,505 @@ +/****************************************************************************** + * + * Copyright (C) 2014 The Android Open Source Project + * Copyright 2002 - 2004 Open Interface North America, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ +#ifndef _OI_CPU_DEP_H +#define _OI_CPU_DEP_H +/** + * @file + * This file contains definitions for characteristics of the target CPU and + * compiler, including primitive data types and endianness. + * + * This file defines the byte order and primitive data types for various + * CPU families. The preprocessor symbol 'CPU' must be defined to be an + * appropriate value or this header will generate a compile-time error. + * + * @note The documentation for this header file uses the x86 family of processors + * as an illustrative example for CPU/compiler-dependent data type definitions. + * Go to the source code of this header file to see the details of primitive type + * definitions for each platform. + * + * Additional information is available in the @ref data_types_docpage section. + */ + +/********************************************************************************** + $Revision: #1 $ +***********************************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +/** \addtogroup Misc Miscellaneous APIs */ +/**@{*/ + +/** @name Definitions indicating family of target OI_CPU_TYPE + * @{ + */ + +#define OI_CPU_X86 1 /**< x86 processor family */ +#define OI_CPU_ARM 2 /**< ARM processor family. + @deprecated Use #OI_CPU_ARM7_LEND or + #OI_CPU_ARM7_BEND. */ +#define OI_CPU_ARC 3 /**< ARC processor family. + @deprecated Use #OI_CPU_ARC_LEND or + #OI_CPU_ARC_BEND. */ +#define OI_CPU_SH3 4 /**< Hitachi SH-3 processor family */ +#define OI_CPU_H8 5 /**< Hitachi H8 processor family */ +#define OI_CPU_MIPS 6 /**< MIPS processor family */ +#define OI_CPU_SPARC 7 /**< SPARC processor family */ +#define OI_CPU_M68000 8 /**< Motorola M68000 processor family */ +#define OI_CPU_PPC 9 /**< PowerPC (PPC) processor family */ +#define OI_CPU_SH4_7750 10 /**< Hitachi SH7750 series in SH-4 processor family */ +#define OI_CPU_SH2 11 /**< Hitachi SH-2 processor family */ +#define OI_CPU_ARM7_LEND 12 /**< ARM7, little-endian */ +#define OI_CPU_ARM7_BEND 13 /**< ARM7, big-endian */ +#define OI_CPU_GDM1202 14 /**< GCT GDM1202 */ +#define OI_CPU_ARC_LEND 15 /**< ARC processor family, little-endian */ +#define OI_CPU_ARC_BEND 16 /**< ARC processor family, big-endian */ +#define OI_CPU_M30833F 17 /**< Mitsubishi M308 processor family */ +#define OI_CPU_CR16C 18 /**< National Semiconductor 16 bit processor family */ +#define OI_CPU_M64111 19 /**< Renesas M64111 processor (M32R family) */ +#define OI_CPU_ARMV5_LEND 20 //*< ARM5, little-endian */ + +#define OI_CPU_TYPE 12 + +#ifndef OI_CPU_TYPE +#error "OI_CPU_TYPE type not defined" +#endif + +/**@}*/ + + +/** @name Definitions indicating byte-wise endianness of target CPU + * @{ + */ + +#define OI_BIG_ENDIAN_BYTE_ORDER 0 /**< Multiple-byte values are stored in memory beginning with the most significant byte at the lowest address. */ +#define OI_LITTLE_ENDIAN_BYTE_ORDER 1 /**< Multiple-byte values are stored in memory beginning with the least significant byte at the lowest address. */ + +/**@}*/ + + +/** @name CPU/compiler-independent primitive data type definitions + * @{ + */ + +typedef int OI_BOOL; /**< Boolean values use native integer data type for target CPU. */ +typedef int OI_INT; /**< Integer values use native integer data type for target CPU. */ +typedef unsigned int OI_UINT; /**< Unsigned integer values use native unsigned integer data type for target CPU. */ +typedef unsigned char OI_BYTE; /**< Raw bytes type uses native character data type for target CPU. */ + +/**@}*/ + + + +/*********************************************************************************/ + +#if OI_CPU_TYPE==OI_CPU_X86 + +#define OI_CPU_BYTE_ORDER OI_LITTLE_ENDIAN_BYTE_ORDER /**< x86 platform byte ordering is little-endian */ + +/** @name CPU/compiler-dependent primitive data type definitions for x86 processor family + * @{ + */ +typedef signed char OI_INT8; /**< 8-bit signed integer values use native signed character data type for x86 processor. */ +typedef signed short OI_INT16; /**< 16-bit signed integer values use native signed short integer data type for x86 processor. */ +typedef signed long OI_INT32; /**< 32-bit signed integer values use native signed long integer data type for x86 processor. */ +typedef unsigned char OI_UINT8; /**< 8-bit unsigned integer values use native unsigned character data type for x86 processor. */ +typedef unsigned short OI_UINT16; /**< 16-bit unsigned integer values use native unsigned short integer data type for x86 processor. */ +typedef unsigned long OI_UINT32; /**< 32-bit unsigned integer values use native unsigned long integer data type for x86 processor. */ + +typedef OI_UINT32 OI_ELEMENT_UNION; /**< Type for first element of a union to support all data types up to pointer width. */ + +/**@}*/ + +#endif + +/*********************************************************************************/ + +#if OI_CPU_TYPE==OI_CPU_ARM +/* This CPU type is deprecated (removed from use). Instead, use OI_CPU_ARM7_LEND or OI_CPU_ARM7_BEND for + little-endian or big-endian configurations of the ARM7, respectively. */ +#error OI_CPU_ARM is deprecated +#endif + +/*********************************************************************************/ + +#if OI_CPU_TYPE==OI_CPU_ARC +/* This CPU type is deprecated (removed from use). Instead, use OI_CPU_ARC_LEND or OI_CPU_ARC_BEND for + little-endian or big-endian configurations of the ARC, respectively. */ +#error OI_CPU_ARC is deprecated +#endif + +/*********************************************************************************/ + +#if OI_CPU_TYPE==OI_CPU_SH3 +/* The Hitachi SH C compiler defines _LIT or _BIG, depending on the endianness + specified to the compiler on the command line. */ +#if defined(_LIT) +#define OI_CPU_BYTE_ORDER OI_LITTLE_ENDIAN_BYTE_ORDER /**< If _LIT is defined, SH-3 platform byte ordering is little-endian. */ +#elif defined(_BIG) +#define OI_CPU_BYTE_ORDER OI_BIG_ENDIAN_BYTE_ORDER /**< If _BIG is defined, SH-3 platform byte ordering is big-endian. */ +#else +#error SH compiler endianness undefined +#endif + +/** @name CPU/compiler-dependent primitive data type definitions for SH-3 processor family + * @{ + */ + +typedef signed char OI_INT8; /**< 8-bit signed integer values use native signed character data type for SH-3 processor. */ +typedef signed short OI_INT16; /**< 16-bit signed integer values use native signed short integer data type for SH-3 processor. */ +typedef signed long OI_INT32; /**< 32-bit signed integer values use native signed long integer data type for SH-3 processor. */ +typedef unsigned char OI_UINT8; /**< 8-bit unsigned integer values use native unsigned character data type for SH-3 processor. */ +typedef unsigned short OI_UINT16; /**< 16-bit unsigned integer values use native unsigned short integer data type for SH-3 processor. */ +typedef unsigned long OI_UINT32; /**< 32-bit unsigned integer values use native unsigned long integer data type for SH-3 processor. */ + +typedef OI_UINT32 OI_ELEMENT_UNION; /**< Type for first element of a union to support all data types up to pointer width. */ + +/**@}*/ + +#endif +/*********************************************************************************/ + +#if OI_CPU_TYPE==OI_CPU_SH2 + +#define OI_CPU_BYTE_ORDER OI_BIG_ENDIAN_BYTE_ORDER /**< SH-2 platform byte ordering is big-endian. */ + +/** @name CPU/compiler-dependent primitive data type definitions for SH-2 processor family + * @{ + */ + +typedef signed char OI_INT8; /**< 8-bit signed integer values use native signed character data type for SH-2 processor. */ +typedef signed short OI_INT16; /**< 16-bit signed integer values use native signed short integer data type for SH-2 processor. */ +typedef signed long OI_INT32; /**< 32-bit signed integer values use native signed long integer data type for SH-2 processor. */ +typedef unsigned char OI_UINT8; /**< 8-bit unsigned integer values use native unsigned character data type for SH-2 processor. */ +typedef unsigned short OI_UINT16; /**< 16-bit unsigned integer values use native unsigned short integer data type for SH-2 processor. */ +typedef unsigned long OI_UINT32; /**< 32-bit unsigned integer values use native unsigned long integer data type for SH-2 processor. */ + +typedef OI_UINT32 OI_ELEMENT_UNION; /**< Type for first element of a union to support all data types up to pointer width. */ + +/**@}*/ + +#endif +/*********************************************************************************/ + +#if OI_CPU_TYPE==OI_CPU_H8 +#define OI_CPU_BYTE_ORDER OI_BIG_ENDIAN_BYTE_ORDER +#error basic types not defined +#endif + +/*********************************************************************************/ + +#if OI_CPU_TYPE==OI_CPU_MIPS +#define OI_CPU_BYTE_ORDER OI_LITTLE_ENDIAN_BYTE_ORDER +/** @name CPU/compiler-dependent primitive data type definitions for MIPS processor family + * @{ + */ +typedef signed char OI_INT8; /**< 8-bit signed integer values use native signed character data type for ARM7 processor. */ +typedef signed short OI_INT16; /**< 16-bit signed integer values use native signed short integer data type for ARM7 processor. */ +typedef signed long OI_INT32; /**< 32-bit signed integer values use native signed long integer data type for ARM7 processor. */ +typedef unsigned char OI_UINT8; /**< 8-bit unsigned integer values use native unsigned character data type for ARM7 processor. */ +typedef unsigned short OI_UINT16; /**< 16-bit unsigned integer values use native unsigned short integer data type for ARM7 processor. */ +typedef unsigned long OI_UINT32; /**< 32-bit unsigned integer values use native unsigned long integer data type for ARM7 processor. */ + +typedef OI_UINT32 OI_ELEMENT_UNION; /**< Type for first element of a union to support all data types up to pointer width. */ + +/**@}*/ + +#endif + +/*********************************************************************************/ + +#if OI_CPU_TYPE==OI_CPU_SPARC +#define OI_CPU_BYTE_ORDER OI_LITTLE_ENDIAN_BYTE_ORDER +#error basic types not defined +#endif + +/*********************************************************************************/ + +#if OI_CPU_TYPE==OI_CPU_M68000 +#define OI_CPU_BYTE_ORDER OI_BIG_ENDIAN_BYTE_ORDER /**< M68000 platform byte ordering is big-endian. */ + +/** @name CPU/compiler-dependent primitive data type definitions for M68000 processor family + * @{ + */ + +typedef signed char OI_INT8; /**< 8-bit signed integer values use native signed character data type for M68000 processor. */ +typedef signed short OI_INT16; /**< 16-bit signed integer values use native signed short integer data type for M68000 processor. */ +typedef signed long OI_INT32; /**< 32-bit signed integer values use native signed long integer data type for M68000 processor. */ +typedef unsigned char OI_UINT8; /**< 8-bit unsigned integer values use native unsigned character data type for M68000 processor. */ +typedef unsigned short OI_UINT16; /**< 16-bit unsigned integer values use native unsigned short integer data type for M68000 processor. */ +typedef unsigned long OI_UINT32; /**< 32-bit unsigned integer values use native unsigned long integer data type for M68000 processor. */ + +typedef OI_UINT32 OI_ELEMENT_UNION; /**< Type for first element of a union to support all data types up to pointer width. */ + +/**@}*/ + +#endif + +/*********************************************************************************/ + +#if OI_CPU_TYPE==OI_CPU_PPC +#define OI_CPU_BYTE_ORDER OI_BIG_ENDIAN_BYTE_ORDER + + +/** @name CPU/compiler-dependent primitive data type definitions for PPC 8XX processor family + * @{ + */ + +typedef signed char OI_INT8; /**< 8-bit signed integer values use native signed character data type for PPC8XX processor. */ +typedef signed short OI_INT16; /**< 16-bit signed integer values use native signed short integer data type for PPC8XX processor. */ +typedef signed long OI_INT32; /**< 32-bit signed integer values use native signed long integer data type for PPC8XX processor. */ +typedef unsigned char OI_UINT8; /**< 8-bit unsigned integer values use native unsigned character data type for PPC8XX processor. */ +typedef unsigned short OI_UINT16; /**< 16-bit unsigned integer values use native unsigned short integer data type for PPC8XX processor. */ +typedef unsigned long OI_UINT32; /**< 32-bit unsigned integer values use native unsigned long integer data type for PPC8XX processor. */ + +typedef OI_UINT32 OI_ELEMENT_UNION; /**< Type for first element of a union to support all data types up to pointer width. */ + +/**@}*/ + +#endif + +/*********************************************************************************/ + +#if OI_CPU_TYPE==OI_CPU_SH4_7750 +#define OI_CPU_BYTE_ORDER OI_BIG_ENDIAN_BYTE_ORDER /**< SH7750 platform byte ordering is big-endian. */ + +/** @name CPU/compiler-dependent primitive data type definitions for SH7750 processor series of the SH-4 processor family + * @{ + */ + +typedef signed char OI_INT8; /**< 8-bit signed integer values use native signed character data type for SH7750 SH-4 processor. */ +typedef signed short OI_INT16; /**< 16-bit signed integer values use native signed short integer data type for SH7750 SH-4 processor. */ +typedef signed long OI_INT32; /**< 32-bit signed integer values use native signed long integer data type for SH7750 SH-4 processor. */ +typedef unsigned char OI_UINT8; /**< 8-bit unsigned integer values use native unsigned character data type for SH7750 SH-4 processor. */ +typedef unsigned short OI_UINT16; /**< 16-bit unsigned integer values use native unsigned short integer data type for SH7750 SH-4 processor. */ +typedef unsigned long OI_UINT32; /**< 32-bit unsigned integer values use native unsigned long integer data type for SH7750 SH-4 processor. */ + +typedef OI_UINT32 OI_ELEMENT_UNION; /**< Type for first element of a union to support all data types up to pointer width. */ + +/**@}*/ + +#endif + +/*********************************************************************************/ + +#if OI_CPU_TYPE==OI_CPU_ARM7_LEND +#define OI_CPU_BYTE_ORDER OI_LITTLE_ENDIAN_BYTE_ORDER + +/** @name little-endian CPU/compiler-dependent primitive data type definitions for the ARM7 processor family + * @{ + */ + +typedef signed char OI_INT8; /**< 8-bit signed integer values use native signed character data type for ARM7 processor. */ +typedef signed short OI_INT16; /**< 16-bit signed integer values use native signed short integer data type for ARM7 processor. */ +typedef signed long OI_INT32; /**< 32-bit signed integer values use native signed long integer data type for ARM7 processor. */ +typedef unsigned char OI_UINT8; /**< 8-bit unsigned integer values use native unsigned character data type for ARM7 processor. */ +typedef unsigned short OI_UINT16; /**< 16-bit unsigned integer values use native unsigned short integer data type for ARM7 processor. */ +typedef unsigned long OI_UINT32; /**< 32-bit unsigned integer values use native unsigned long integer data type for ARM7 processor. */ + +typedef void *OI_ELEMENT_UNION; /**< Type for first element of a union to support all data types up to pointer width. */ + +/**@}*/ + +#endif + +/*********************************************************************************/ + +#if OI_CPU_TYPE==OI_CPU_ARM7_BEND +#define OI_CPU_BYTE_ORDER OI_BIG_ENDIAN_BYTE_ORDER +/** @name big-endian CPU/compiler-dependent primitive data type definitions for the ARM7 processor family + * @{ + */ +typedef signed char OI_INT8; /**< 8-bit signed integer values use native signed character data type for ARM7 processor. */ +typedef signed short OI_INT16; /**< 16-bit signed integer values use native signed short integer data type for ARM7 processor. */ +typedef signed long OI_INT32; /**< 32-bit signed integer values use native signed long integer data type for ARM7 processor. */ +typedef unsigned char OI_UINT8; /**< 8-bit unsigned integer values use native unsigned character data type for ARM7 processor. */ +typedef unsigned short OI_UINT16; /**< 16-bit unsigned integer values use native unsigned short integer data type for ARM7 processor. */ +typedef unsigned long OI_UINT32; /**< 32-bit unsigned integer values use native unsigned long integer data type for ARM7 processor. */ + +typedef void *OI_ELEMENT_UNION; /**< Type for first element of a union to support all data types up to pointer width. */ + +/**@}*/ + +#endif + +/*********************************************************************************/ + +#if OI_CPU_TYPE==OI_CPU_GDM1202 +#define OI_CPU_BYTE_ORDER OI_BIG_ENDIAN_BYTE_ORDER + +typedef signed char OI_INT8; /**< 8-bit signed integer. */ +typedef signed short OI_INT16; /**< 16-bit signed integer. */ +typedef signed long OI_INT32; /**< 32-bit signed integer. */ +typedef unsigned char OI_UINT8; /**< 8-bit unsigned integer. */ +typedef unsigned short OI_UINT16; /**< 16-bit unsigned integer. */ +typedef unsigned long OI_UINT32; /**< 32-bit unsigned integer. */ + +typedef OI_UINT32 OI_ELEMENT_UNION; /**< Type for first element of a union to support all data types up to pointer width. */ + +#endif + +/*********************************************************************************/ + +#if OI_CPU_TYPE==OI_CPU_ARC_LEND + +#define OI_CPU_BYTE_ORDER OI_LITTLE_ENDIAN_BYTE_ORDER + +/** @name CPU/compiler-dependent primitive data type definitions for ARC processor family + * @{ + */ + +typedef signed char OI_INT8; /**< 8-bit signed integer values use native signed character data type for ARC processor. */ +typedef signed short OI_INT16; /**< 16-bit signed integer values use native signed short integer data type for ARC processor. */ +typedef signed long OI_INT32; /**< 32-bit signed integer values use native signed long integer data type for ARC processor. */ +typedef unsigned char OI_UINT8; /**< 8-bit unsigned integer values use native unsigned character data type for ARC processor. */ +typedef unsigned short OI_UINT16; /**< 16-bit unsigned integer values use native unsigned short integer data type for ARC processor. */ +typedef unsigned long OI_UINT32; /**< 32-bit unsigned integer values use native unsigned long integer data type for ARC processor. */ + +typedef OI_UINT32 OI_ELEMENT_UNION; /**< Type for first element of a union to support all data types up to pointer width. */ + +/**@}*/ +#endif + +/*********************************************************************************/ + +#if OI_CPU_TYPE==OI_CPU_ARC_BEND + +#define OI_CPU_BYTE_ORDER OI_BIG_ENDIAN_BYTE_ORDER + +/** @name CPU/compiler-dependent primitive data type definitions for ARC processor family + * @{ + */ + +typedef signed char OI_INT8; /**< 8-bit signed integer values use native signed character data type for ARC processor. */ +typedef signed short OI_INT16; /**< 16-bit signed integer values use native signed short integer data type for ARC processor. */ +typedef signed long OI_INT32; /**< 32-bit signed integer values use native signed long integer data type for ARC processor. */ +typedef unsigned char OI_UINT8; /**< 8-bit unsigned integer values use native unsigned character data type for ARC processor. */ +typedef unsigned short OI_UINT16; /**< 16-bit unsigned integer values use native unsigned short integer data type for ARC processor. */ +typedef unsigned long OI_UINT32; /**< 32-bit unsigned integer values use native unsigned long integer data type for ARC processor. */ + +typedef OI_UINT32 OI_ELEMENT_UNION; /**< Type for first element of a union to support all data types up to pointer width. */ + +/**@}*/ +#endif + +/*********************************************************************************/ + +#if OI_CPU_TYPE==OI_CPU_M30833F + +#define OI_CPU_BYTE_ORDER OI_LITTLE_ENDIAN_BYTE_ORDER + +/** @name CPU/compiler-dependent primitive data type definitions for Mitsubishi M308 processor family + * @{ + */ + +typedef signed char OI_INT8; /**< 8-bit signed integer values use native signed character data type for M308 processor. */ +typedef signed short OI_INT16; /**< 16-bit signed integer values use native signed short integer data type for M308 processor. */ +typedef signed long OI_INT32; /**< 32-bit signed integer values use native signed long integer data type for M308 processor. */ +typedef unsigned char OI_UINT8; /**< 8-bit unsigned integer values use native unsigned character data type for M308 processor. */ +typedef unsigned short OI_UINT16; /**< 16-bit unsigned integer values use native unsigned short integer data type for M308 processor. */ +typedef unsigned long OI_UINT32; /**< 32-bit unsigned integer values use native unsigned long integer data type for M308 processor. */ + +typedef OI_UINT32 OI_ELEMENT_UNION; /**< Type for first element of a union to support all data types up to pointer width. */ + +/**@}*/ +#endif + +/*********************************************************************************/ + +#if OI_CPU_TYPE==OI_CPU_CR16C + +#define OI_CPU_BYTE_ORDER OI_LITTLE_ENDIAN_BYTE_ORDER + +/** @name CPU/compiler-dependent primitive data type definitions for National Semicnductor processor family + * @{ + */ + +typedef signed char OI_INT8; /**< 8-bit signed integer values use native signed character data type for CR16C processor. */ +typedef signed short OI_INT16; /**< 16-bit signed integer values use native signed short integer data type for CR16C processor. */ +typedef signed long OI_INT32; /**< 32-bit signed integer values use native signed long integer data type for CR16C processor. */ +typedef unsigned char OI_UINT8; /**< 8-bit unsigned integer values use native unsigned character data type for CR16C processor. */ +typedef unsigned short OI_UINT16; /**< 16-bit unsigned integer values use native unsigned short integer data type for CR16C processor. */ +typedef unsigned long OI_UINT32; /**< 32-bit unsigned integer values use native unsigned long integer data type for CR16C processor. */ + +typedef OI_UINT32 OI_ELEMENT_UNION; /**< Type for first element of a union to support all data types up to pointer width. */ + +/**@}*/ +#endif + +/*********************************************************************************/ + +#if OI_CPU_TYPE==OI_CPU_M64111 + +#define OI_CPU_BYTE_ORDER OI_BIG_ENDIAN_BYTE_ORDER + +/** @name CPU/compiler-dependent primitive data type definitions for Renesas M32R processor family + * @{ + */ + +typedef signed char OI_INT8; /**< 8-bit signed integer values use native signed character data type for M64111 processor. */ +typedef signed short OI_INT16; /**< 16-bit signed integer values use native signed short integer data type for M64111 processor. */ +typedef signed long OI_INT32; /**< 32-bit signed integer values use native signed long integer data type for M64111 processor. */ +typedef unsigned char OI_UINT8; /**< 8-bit unsigned integer values use native unsigned character data type for M64111 processor. */ +typedef unsigned short OI_UINT16; /**< 16-bit unsigned integer values use native unsigned short integer data type for M64111 processor. */ +typedef unsigned long OI_UINT32; /**< 32-bit unsigned integer values use native unsigned long integer data type for M64111 processor. */ + +typedef OI_UINT32 OI_ELEMENT_UNION; /**< Type for first element of a union to support all data types up to pointer width. */ + +/**@}*/ +#endif + +/*********************************************************************************/ + +#if OI_CPU_TYPE==OI_CPU_ARMV5_LEND +#define OI_CPU_BYTE_ORDER OI_LITTLE_ENDIAN_BYTE_ORDER + +/** @name little-endian CPU/compiler-dependent primitive data type definitions for the ARM7 processor family + * @{ + */ + +typedef signed char OI_INT8; /**< 8-bit signed integer values use native signed character data type for ARM7 processor. */ +typedef signed short OI_INT16; /**< 16-bit signed integer values use native signed short integer data type for ARM7 processor. */ +typedef signed long OI_INT32; /**< 32-bit signed integer values use native signed long integer data type for ARM7 processor. */ +typedef unsigned char OI_UINT8; /**< 8-bit unsigned integer values use native unsigned character data type for ARM7 processor. */ +typedef unsigned short OI_UINT16; /**< 16-bit unsigned integer values use native unsigned short integer data type for ARM7 processor. */ +typedef unsigned long OI_UINT32; /**< 32-bit unsigned integer values use native unsigned long integer data type for ARM7 processor. */ + +typedef OI_UINT32 OI_ELEMENT_UNION; /**< Type for first element of a union to support all data types up to pointer width. */ + +/**@}*/ + +#endif + +/*********************************************************************************/ + + +#ifndef OI_CPU_BYTE_ORDER +#error "Byte order (endian-ness) not defined" +#endif + + +/**@}*/ + +#ifdef __cplusplus +} +#endif + +/*********************************************************************************/ +#endif /* _OI_CPU_DEP_H */ diff --git a/lib/bt/host/bluedroid/external/sbc/decoder/include/oi_modules.h b/lib/bt/host/bluedroid/external/sbc/decoder/include/oi_modules.h new file mode 100644 index 00000000..1b7c24bf --- /dev/null +++ b/lib/bt/host/bluedroid/external/sbc/decoder/include/oi_modules.h @@ -0,0 +1,170 @@ +/****************************************************************************** + * + * Copyright (C) 2014 The Android Open Source Project + * Copyright 2002 - 2004 Open Interface North America, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ +#ifndef _OI_MODULES_H +#define _OI_MODULES_H +/** + * @file + * + * Enumeration type defining the inidivual stack components. + * + */ + +/********************************************************************************** + $Revision: #1 $ +***********************************************************************************/ + + +/** \addtogroup Misc Miscellaneous APIs */ +/**@{*/ + +#ifdef __cplusplus +extern "C" { +#endif + + +/** + * This enumeration lists constants for referencing the components of + * the BLUEmagic 3.0 protocol stack, profiles, and other functionalities. + * + * In order to distinguish types of modules, items are grouped with markers to + * delineate start and end of the groups + * + * The module type is used for various purposes: + * identification in debug print statements + * access to initialization flags + * access to the configuration table + */ + +typedef enum { + /* profiles and protocols --> Updates to oi_debug.c and oi_config_table.c */ + + /* XX --> Keep Enum values up-to-date! */ + OI_MODULE_AT, /**< 00 AT command processing */ + OI_MODULE_A2DP, /**< 01 Advanced Audio Distribution Profile */ + OI_MODULE_AVCTP, /**< 02 Audio-Visual Control Transport Profile */ + OI_MODULE_AVDTP, /**< 03 Audio-Visual Distribution Protocol */ + OI_MODULE_AVRCP, /**< 04 Audio-Visual Remote Control Profile */ + OI_MODULE_BIP_CLI, /**< 05 Basic Imaging Profile protocol client */ + OI_MODULE_BIP_SRV, /**< 06 Basic Imaging Profile protocol server */ + OI_MODULE_BNEP, /**< 07 Bluetooth Network Encapsulation Protocol */ + OI_MODULE_BPP_SENDER, /**< 08 Basic Printing Profile */ + OI_MODULE_BPP_PRINTER, /**< 09 Basic Printing Profile */ + OI_MODULE_CTP, /**< 10 Cordless Telephony Profile */ + OI_MODULE_DUN, /**< 11 Dial-Up Networking Profile */ + OI_MODULE_FAX, /**< 12 Fax Profile */ + OI_MODULE_FTP_CLI, /**< 13 File Transfer Profile protocol client */ + OI_MODULE_FTP_SRV, /**< 14 File Transfer Profile protocol server */ + OI_MODULE_HANDSFREE, /**< 15 Hands-Free Profile */ + OI_MODULE_HANDSFREE_AG, /**< 16 Hands-Free Profile */ + OI_MODULE_HCRP_CLI, /**< 17 Hardcopy Cable Replacement Profile */ + OI_MODULE_HCRP_SRV, /**< 18 Hardcopy Cable Replacement Profile */ + OI_MODULE_HEADSET, /**< 19 Headset Profile */ + OI_MODULE_HEADSET_AG, /**< 20 Headset Profile */ + OI_MODULE_HID, /**< 21 Human Interface Device profile */ + OI_MODULE_INTERCOM, /**< 22 Intercom Profile */ + OI_MODULE_OBEX_CLI, /**< 23 OBEX protocol client, Generic Object Exchange Profile */ + OI_MODULE_OBEX_SRV, /**< 24 OBEX protocol server, Generic Object Exchange Profile */ + OI_MODULE_OPP_CLI, /**< 25 Object Push Profile protocol client */ + OI_MODULE_OPP_SRV, /**< 26 Object Push Profile protocol server */ + OI_MODULE_PAN, /**< 27 PAN profile */ + OI_MODULE_PBAP_CLI, /**< 28 Phonebook Access Profile client */ + OI_MODULE_PBAP_SRV, /**< 29 Phonebook Access Profile server */ + OI_MODULE_SAP_CLI, /**< 30 SIM Access Profile */ + OI_MODULE_SAP_SRV, /**< 31 SIM Access Profile */ + OI_MODULE_SPP, /**< 32 Serial Port Profile */ + OI_MODULE_SYNC_CLI, /**< 33 Synchronization Profile */ + OI_MODULE_SYNC_SRV, /**< 34 Synchronization Profile */ + OI_MODULE_SYNC_CMD_CLI, /**< 35 Synchronization Profile */ + OI_MODULE_SYNC_CMD_SRV, /**< 36 Synchronization Profile */ + OI_MODULE_SYNCML, /**< 37 SyncML Profile */ + OI_MODULE_TCS, /**< 38 TCS Binary */ + OI_MODULE_VDP, /**< 39 Video Distribution Profile */ + + /* corestack components --> Updates to oi_debug.c and oi_config_table.c */ + + OI_MODULE_COMMON_CONFIG, /**< 40 Common configuration, module has no meaning other than for config struct */ + OI_MODULE_CMDCHAIN, /**< 41 Command chaining utility */ + OI_MODULE_DISPATCH, /**< 42 Dispatcher */ + OI_MODULE_DATAELEM, /**< 43 Data Elements, marshaller */ + OI_MODULE_DEVMGR, /**< 44 Device Manager */ + OI_MODULE_DEVMGR_MODES, /**< 45 Device Manager connectability/discoverability modes */ + OI_MODULE_HCI, /**< 46 Host Controller Interface command layer */ + OI_MODULE_L2CAP, /**< 47 L2CAP */ + OI_MODULE_MEMMGR, /**< 48 modules that do memory management */ + OI_MODULE_POLICYMGR, /**< 49 Policy Manager */ + OI_MODULE_RFCOMM, /**< 50 RFCOMM */ + OI_MODULE_RFCOMM_SD, /**< 51 RFCOMM Service discovery */ + OI_MODULE_SDP_CLI, /**< 52 Service Discovery Protocol client */ + OI_MODULE_SDP_SRV, /**< 53 Service Discovery Protocol server */ + OI_MODULE_SDPDB, /**< 54 Service Discovery Protocol database */ + OI_MODULE_SECMGR, /**< 55 Security Manager */ + OI_MODULE_SNIFFLOG, /**< 56 sniff log */ + OI_MODULE_SUPPORT, /**< 57 support functions, including CThru Dispatcher, time functions, and stack initialization */ + OI_MODULE_TRANSPORT, /**< 58 transport layer between HCI command layer and driver */ + OI_MODULE_TEST, /**< 59 used to debug output from internal test programs */ + OI_MODULE_XML, /**< 60 XML/CSS parser */ + + OI_MODULE_DI, /**< 61 Device Identification Profile */ + + // bhapi components --> Updates to oi_debug.c + + OI_MODULE_BHAPI, /**< 62 BLUEmagic Host API generic */ + OI_MODULE_BHCLI, /**< 63 BLUEmagic Host API client side */ + OI_MODULE_BHSRV, /**< 64 BLUEmagic Host API server side */ + OI_MODULE_MSGQ, /**< 65 module that handles message queuing */ + OI_MODULE_BHAPI_TRANSPORT, /**< 66 module that handles message queuing */ + OI_MODULE_BLST_SRV, /**< 67 module that provides server side BHAPI Lightweight Serial Transport */ + OI_MODULE_BLST_CLI, /**< 68 module that provides client side BHAPI Lightweight Serial Transport */ + + // OEM files --> Updates to oi_debug.c + OI_MODULE_OEM, /**< 69 Application Memory allocation */ + + // Application glue --> Updates to oi_debug.c + OI_MODULE_APP, /**< 70 Application Memory allocation */ + + /* various pieces of code depend on these last 2 elements occuring in a specific order: + OI_MODULE_ALL must be the 2nd to last element + OI_MODULE_UNKNOWN must be the last element + */ + OI_MODULE_ALL, /**< 71 special value identifying all modules - used for control of debug print statements */ + OI_MODULE_UNKNOWN /**< 72 special value - used for debug print statements */ +} OI_MODULE; + +/** + * This constant is the number of actual modules in the list. ALL and UNKNOWN are + * special values that are not actually modules. + * Used for debug print and memmgr profiling + */ +#define OI_NUM_MODULES OI_MODULE_ALL + + +/** + * This constant is the number of profile and core components. It is used to size + * the initialization and configuration tables. + */ +#define OI_NUM_STACK_MODULES OI_MODULE_BHAPI + + +#ifdef __cplusplus +} +#endif + +/**@}*/ + +#endif /* _OI_MODULES_H */ diff --git a/lib/bt/host/bluedroid/external/sbc/decoder/include/oi_osinterface.h b/lib/bt/host/bluedroid/external/sbc/decoder/include/oi_osinterface.h new file mode 100644 index 00000000..f486c8bb --- /dev/null +++ b/lib/bt/host/bluedroid/external/sbc/decoder/include/oi_osinterface.h @@ -0,0 +1,196 @@ +/****************************************************************************** + * + * Copyright (C) 2014 The Android Open Source Project + * Copyright 2002 - 2004 Open Interface North America, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ +#ifndef _OI_OSINTERFACE_H +#define _OI_OSINTERFACE_H +/** + @file + * This file provides the platform-independent interface for functions for which + * implementation is platform-specific. + * + * The functions in this header file define the operating system or hardware + * services needed by the BLUEmagic 3.0 protocol stack. The + * actual implementation of these services is platform-dependent. + * + */ + +/********************************************************************************** + $Revision: #1 $ +***********************************************************************************/ + +#include "oi_stddefs.h" +#include "oi_time.h" +#include "oi_status.h" +#include "oi_modules.h" + +/** \addtogroup Misc Miscellaneous APIs */ +/**@{*/ + +#ifdef __cplusplus +extern "C" { +#endif + + +/** + * Terminates execution. + * + * @param reason Reason for termination + */ +void OI_FatalError(OI_STATUS reason); + +/** + * This function logs an error. + * + * When built for release mode, BLUEmagic 3 errors are logged to + * this function. (in debug mode, errors are logged via + * OI_Print()). + * + * @param module Module in which the error was detected (see + * oi_modules.h) + * @param lineno Line number of the C file OI_SLOG_ERROR called + * @param status Status code associated with the error event + */ +void OI_LogError(OI_MODULE module, OI_INT lineno, OI_STATUS status); + +/** + * This function initializes the debug code handling. + * + * When built for debug mode, this function performs platform + * dependent initialization to handle message codes passed in + * via OI_SetMsgCode(). + */ +void OI_InitDebugCodeHandler(void); + + +/** + * This function reads the time from the real time clock. + * + * All timing in BM3 is relative, typically a granularity + * of 5 or 10 msecs is adequate. + * + * @param[out] now Pointer to the buffer to which the current + * time will be returned + */ +void OI_Time_Now(OI_TIME *now); + +/** + * This function causes the current thread to sleep for the + * specified amount of time. This function must be called + * without the stack access token. + * + * @note BM3 corestack and profiles never suspend and never call + * OI_Sleep. The use of OI_Sleep is limited to applications and + * platform-specific code. + * + * If your port and applications never use OI_Sleep, this function can be left unimplemented. + * + * @param milliseconds Number of milliseconds to sleep + */ +void OI_Sleep(OI_UINT32 milliseconds); + + +/** + * Defines for message type codes. + */ +#define OI_MSG_CODE_APPLICATION 0 /**< Application output */ +#define OI_MSG_CODE_ERROR 1 /**< Error message output */ +#define OI_MSG_CODE_WARNING 2 /**< Warning message output */ +#define OI_MSG_CODE_TRACE 3 /**< User API function trace output */ +#define OI_MSG_CODE_PRINT1 4 /**< Catagory 1 debug print output */ +#define OI_MSG_CODE_PRINT2 5 /**< Catagory 2 debug print output */ +#define OI_MSG_CODE_HEADER 6 /**< Error/Debug output header */ + +/** + * This function is used to indicate the type of text being output with + * OI_Print(). For the Linux and Win32 platforms, it will set + * the color of the text. Other possible uses could be to insert + * HTML style tags, add some other message type indication, or + * be completely ignored altogether. + * + * @param code OI_MSG_CODE_* indicating setting the message type. + */ +void OI_SetMsgCode(OI_UINT8 code); + +/** + * All output from OI_Printf() and all debug output is sent to OI_Print. + * Typically, if the platform has a console, OI_Print() is sent to stdout. + * Embedded platforms typically send OI_Print() output to a serial port. + * + * @param str String to print + */ +void OI_Print(OI_CHAR const *str); + +/** + * In cases where OI_Print() is sending output to a logfile in addition to console, + * it is desirable to also put console input into the logfile. + * This function can be called by the console input process. + * + * @note This is an optional API which is strictly + * between the platform-specific stack_console and osinterface + * modules. This API need only be implemented on those + * platforms where is serves a useful purpose, e.g., win32. + * + * @param str Console input string + */ + +void OI_Print_ConsoleInput(OI_CHAR const *str); + +/** + * This function computes the CRC16 of the program image. + */ +OI_UINT16 OI_ProgramImageCRC16(void); + +/** + * Writes an integer to stdout in hex. This macro is intended + * for selective use when debugging in small memory + * configurations or other times when it is not possible to use + * OI_DBGPRINT. + * + * @param n the integer to print + */ + +#define OI_Print_Int(n) \ +{ \ + static const OI_CHAR _digits[] = "0123456789ABCDEF"; \ + OI_CHAR _buf[9]; \ + OI_CHAR *_str = &_buf[8]; \ + OI_UINT32 _i = n; \ + *_str = 0; \ + do { *(--_str) = _digits[(_i & 0xF)]; _i >>= 4; } while (_i); \ + OI_Print(_str); \ +} + +/** + * Application Dynamic Memory allocation. + * + * These APIs are provided for application use on those + * platforms which have no dynamic memory support. Memory is + * allocated from the pool-based heap managed by the stack's + * internal memory manager. + */ +void *OI_APP_Malloc(OI_INT32 size); +void OI_APP_Free(void *ptr); + +/*****************************************************************************/ +#ifdef __cplusplus +} +#endif + +/**@}*/ + +#endif /* _OI_OSINTERFACE_H */ diff --git a/lib/bt/host/bluedroid/external/sbc/decoder/include/oi_status.h b/lib/bt/host/bluedroid/external/sbc/decoder/include/oi_status.h new file mode 100644 index 00000000..b527ebb1 --- /dev/null +++ b/lib/bt/host/bluedroid/external/sbc/decoder/include/oi_status.h @@ -0,0 +1,578 @@ +/****************************************************************************** + * + * Copyright (C) 2014 The Android Open Source Project + * Copyright 2002 - 2004 Open Interface North America, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ +#ifndef _OI_STATUS_H +#define _OI_STATUS_H +/** + * @file + * This file contains status codes for BLUEmagic 3.0 software. + */ + +#include "oi_stddefs.h" + +/** \addtogroup Misc Miscellaneous APIs */ +/**@{*/ + +#ifdef __cplusplus +extern "C" { +#endif + + +/** test it **/ + +/** + * OI_STATUS must fit in 16 bits, so status codes can range from 0 to 66535, inclusive. + */ + +typedef enum { + OI_STATUS_SUCCESS = 0, /**< function call succeeded alias for #OI_OK */ + OI_OK = 0, /**< function call succeeded alias for #OI_STATUS_SUCCESS */ + OI_STATUS_INVALID_PARAMETERS = 101, /**< invalid function input parameters */ + OI_STATUS_NOT_IMPLEMENTED = 102, /**< attempt to use an unimplemented function */ + OI_STATUS_NOT_INITIALIZED = 103, /**< data not initialized */ + OI_STATUS_NO_RESOURCES = 104, /**< generic resource allocation failure status */ + OI_STATUS_INTERNAL_ERROR = 105, /**< internal inconsistency */ + OI_STATUS_OUT_OF_MEMORY = 106, /**< generally, OI_Malloc failed */ + OI_ILLEGAL_REENTRANT_CALL = 107, /**< violation of non-reentrant module policy */ + OI_STATUS_INITIALIZATION_FAILED = 108, /**< module initialization failed */ + OI_STATUS_INITIALIZATION_PENDING = 109, /**< inititialization not yet complete */ + OI_STATUS_NO_SCO_SUPPORT = 110, /**< SCO operation rejected; system not configured for SCO */ + OI_STATUS_OUT_OF_STATIC_MEMORY = 111, /**< static malloc failed */ + OI_TIMEOUT = 112, /**< generic timeout */ + OI_OS_ERROR = 113, /**< some operating system error */ + OI_FAIL = 114, /**< generic failure */ + OI_STRING_FORMAT_ERROR = 115, /**< error in VarString formatting string */ + OI_STATUS_PENDING = 116, /**< The operation is pending. */ + OI_STATUS_INVALID_COMMAND = 117, /**< The command was invalid. */ + OI_BUSY_FAIL = 118, /**< command rejected due to busy */ + OI_STATUS_ALREADY_REGISTERED = 119, /**< The registration has already been performed. */ + OI_STATUS_NOT_FOUND = 120, /**< The referenced resource was not found. */ + OI_STATUS_NOT_REGISTERED = 121, /**< not registered */ + OI_STATUS_NOT_CONNECTED = 122, /**< not connected */ + OI_CALLBACK_FUNCTION_REQUIRED = 123, /**< A callback function parameter was required. */ + OI_STATUS_MBUF_OVERFLOW = 124, /**< There is no room to add another buffer to an mbuf. */ + OI_STATUS_MBUF_UNDERFLOW = 125, /**< There was an attempt to pull too many bytes from an mbuf. */ + OI_STATUS_CONNECTION_EXISTS = 126, /**< connection exists */ + OI_STATUS_NOT_CONFIGURED = 127, /**< module not configured */ + OI_LOWER_STACK_ERROR = 128, /**< An error was reported by lower stack API. This is used for embedded platforms. */ + OI_STATUS_RESET_IN_PROGRESS = 129, /**< Request failed/rejected because we're busy resetting. */ + OI_STATUS_ACCESS_DENIED = 130, /**< Generic access denied error. */ + OI_STATUS_DATA_ERROR = 131, /**< Generic data error. */ + OI_STATUS_INVALID_ROLE = 132, /**< The requested role was invalid. */ + OI_STATUS_ALREADY_CONNECTED = 133, /**< The requested connection is already established. */ + OI_STATUS_PARSE_ERROR = 134, /**< Parse error */ + OI_STATUS_END_OF_FILE = 135, /**< End of file */ + OI_STATUS_READ_ERROR = 136, /**< Generic read error */ + OI_STATUS_WRITE_ERROR = 137, /**< Generic write error */ + OI_STATUS_NEGOTIATION_FAILURE = 138, /**< Error in negotiation */ + OI_STATUS_READ_IN_PROGRESS = 139, /**< A read is already in progress */ + OI_STATUS_ALREADY_INITIALIZED = 140, /**< Initialization has already been done */ + OI_STATUS_STILL_CONNECTED = 141, /**< The service cannot be shutdown because there are still active connections. */ + OI_STATUS_MTU_EXCEEDED = 142, /**< The packet is too big */ + OI_STATUS_LINK_TERMINATED = 143, /**< The link was terminated */ + OI_STATUS_PIN_CODE_TOO_LONG = 144, /**< Application gave us a pin code that is too long */ + OI_STATUS_STILL_REGISTERED = 145, /**< The service cannot be shutdown because there are still active registrations. */ + OI_STATUS_SPEC_VIOLATION = 146, /**< Some application behavior contrary to BT specifications */ + + + OI_STATUS_PSM_ALREADY_REGISTERED = 402, /**< L2CAP: The specified PSM has already been registered. */ + OI_STATUS_INVALID_CID = 403, /**< L2CAP: CID is invalid or no longer valid (connection terminated) */ + OI_STATUS_CID_NOT_FOUND = 404, /**< L2CAP: CID does not represent a current connection */ + OI_STATUS_CHANNEL_NOT_FOUND = 406, /**< L2CAP: CID does not represent a current connection */ + OI_STATUS_PSM_NOT_FOUND = 407, /**< L2CAP: PSM not found */ + OI_STATUS_INVALID_STATE = 408, /**< L2CAP: invalid state */ + OI_STATUS_WRITE_IN_PROGRESS = 410, /**< L2CAP: write in progress */ + OI_STATUS_INVALID_PACKET = 411, /**< L2CAP: invalid packet */ + OI_STATUS_SEND_COMPLETE = 412, /**< L2CAP: send is complete */ + OI_STATUS_INVALID_HANDLE = 414, /**< L2CAP: handle is invalid */ + OI_STATUS_GROUP_FULL = 418, /**< L2CAP: No more members can be added to the specified group. */ + OI_STATUS_DEVICE_ALREADY_IN_GROUP = 423, /**< L2CAP: The device already exists in the group. */ + OI_STATUS_DUPLICATE_GROUP = 425, /**< L2CAP: attempt to add more than one group */ + OI_STATUS_EMPTY_GROUP = 426, /**< L2CAP: group is empty */ + OI_STATUS_PACKET_NOT_FOUND = 427, /**< L2CAP: packet not found */ + OI_STATUS_BUFFER_TOO_SMALL = 428, /**< L2CAP: The buffer size is too small. */ + OI_STATUS_IDENTIFIER_NOT_FOUND = 429, /**< L2CAP: identifier not found */ + + OI_L2CAP_DISCONNECT_LOWER_LAYER = 430, /**< L2CAP: The lower level forced a disconnect. */ + OI_L2CAP_DISCONNECT_REMOTE_REQUEST = 431, /**< L2CAP: The remote device requested a disconnect. */ + OI_L2CAP_GROUP_ADD_CONNECT_FAIL = 433, /**< L2CAP: Group add connect faiL */ + OI_L2CAP_GROUP_REMOVE_FAILURE = 434, /**< L2CAP: Group remove failure */ + OI_L2CAP_DATA_WRITE_ERROR_LINK_TERM = 435, /**< L2CAP: Data write error LINK_TERM */ + OI_L2CAP_DISCONNECT_LOCAL_REQUEST = 436, /**< L2CAP: Disconnect local request */ + + OI_L2CAP_CONNECT_TIMEOUT = 437, /**< L2CAP: Connect timeout */ + OI_L2CAP_DISCONNECT_TIMEOUT = 439, /**< L2CAP: Disconnect timeout */ + OI_L2CAP_PING_TIMEOUT = 440, /**< L2CAP: Ping timeout */ + OI_L2CAP_GET_INFO_TIMEOUT = 441, /**< L2CAP: Get info timeout */ + OI_L2CAP_INVALID_ADDRESS = 444, /**< L2CAP: Invalid address */ + OI_L2CAP_CMD_REJECT_RCVD = 445, /**< L2CAP: remote sent us 'command reject' response */ + + OI_L2CAP_CONNECT_BASE = 450, /**< L2CAP: Connect base */ + OI_L2CAP_CONNECT_PENDING = 451, /**< L2CAP: Connect pending */ + OI_L2CAP_CONNECT_REFUSED_INVALID_PSM = 452, /**< L2CAP: Connect refused invalid PSM */ + OI_L2CAP_CONNECT_REFUSED_SECURITY = 453, /**< L2CAP: Connect refused security */ + OI_L2CAP_CONNECT_REFUSED_NO_RESOURCES = 454, /**< L2CAP: Connect refused no resources */ + + OI_L2CAP_CONFIG_BASE = 460, /**< L2CAP: Config base */ + OI_L2CAP_CONFIG_FAIL_INVALID_PARAMETERS = 461, /**< L2CAP: Config fail invalid parameters */ + OI_L2CAP_CONFIG_FAIL_NO_REASON = 462, /**< L2CAP: Config fail no reason */ + OI_L2CAP_CONFIG_FAIL_UNKNOWN_OPTIONS = 463, /**< L2CAP: Config fail unknown options */ + + OI_L2CAP_GET_INFO_BASE = 470, /**< L2CAP: Get info base */ + OI_L2CAP_GET_INFO_NOT_SUPPORTED = 471, /**< L2CAP: Get info not supported */ + OI_L2CAP_MTU_EXCEEDED = 472, /**< L2CAP: The MTU of the channel was exceeded */ + OI_L2CAP_INVALID_PSM = 482, /**< L2CAP: Invalid PSM */ + OI_L2CAP_INVALID_MTU = 483, /**< L2CAP: Invalid MTU */ + OI_L2CAP_INVALID_FLUSHTO = 484, /**< L2CAP: Invalid flush timeout */ + + OI_HCI_NO_SUCH_CONNECTION = 601, /**< HCI: caller specified a non-existent connection handle */ + OI_HCI_CB_LIST_FULL = 603, /**< HCI: callback list is full, cannot attempt to send command */ + OI_HCI_EVENT_UNDERRUN = 605, /**< HCI: parsing event packet, premature end-of-parameters */ + OI_HCI_UNKNOWN_EVENT_CODE = 607, /**< HCI: event received - event code is unknown */ + OI_HCI_BAD_EVENT_PARM_LEN = 608, /**< HCI: event - parameter length is incorrect */ + OI_HCI_CMD_QUEUE_FULL = 611, /**< HCI: command queue is full */ + OI_HCI_SHORT_EVENT = 612, /**< HCI: event received, missing event code and/or parm len */ + OI_HCI_TRANSMIT_NOT_READY = 613, /**< HCI: ACL/SCO transmit request failed - busy or no buffers available */ + OI_HCI_ORPHAN_SENT_EVENT = 614, /**< HCI: got spurious 'sent' event from transport layer */ + OI_HCI_CMD_TABLE_ERROR = 615, /**< HCI: inconsistency in the internal command table */ + OI_HCI_UNKNOWN_CMD_ID = 616, /**< HCI: HciApi Command - unknown command id */ + OI_HCI_UNEXPECTED_EVENT = 619, /**< HCI: event received which only occurs in response to our cmd */ + OI_HCI_EVENT_TABLE_ERROR = 620, /**< HCI: inconsistency in the internal event table */ + OI_HCI_EXPECTED_EVENT_TIMOUT = 621, /**< HCI: timed out waiting for an expected event */ + OI_HCI_NO_CMD_DESC_FOR_OPCODE = 622, /**< HCI: event opcode is not known */ + OI_HCI_INVALID_OPCODE_ERROR = 623, /**< HCI: command opcode is invalid */ + OI_HCI_FLOW_CONTROL_DISABLED = 624, /**< HCI: can not use host flow control APIs if disabled in configuration */ + OI_HCI_TX_COMPLETE = 625, /**< HCI: packet delivery to Host Controler complete */ + OI_HCI_TX_ERROR = 626, /**< HCI: failed to deliver packet to Host Controler */ + OI_HCI_DEVICE_NOT_INITIALIZED = 627, /**< HCI: commands from upper layers disallowed until device is up and running */ + OI_HCI_UNSUPPORTED_COMMAND = 628, /**< HCI: command requested is not supported by local device */ + OI_HCI_PASSTHROUGH_ERROR = 629, /**< HCI: Error processing passthrough command */ + OI_HCI_PASSTHROUGH_ALREADY_SET = 630, /**< HCI: Passthrough mode already enabled */ + OI_HCI_RESET_FAILURE = 631, /**< HCI: failed to reset the device/baseband */ + OI_HCI_TRANSPORT_RESET = 632, /**< HCI: some operation failed because of a reset in the transport */ + OI_HCIERR_HCIIFC_INIT_FAILURE = 633, /**< HCI: failed to initialize transport layer interface */ + + OI_HCIERR_FIRST_ERROR_VALUE = 701, /**< marker for first HCI protocol error */ + OI_HCIERR_UNKNOWN_HCI_COMMAND = 701, /**< HCI: protocol error 0x01 */ + OI_HCIERR_NO_CONNECTION = 702, /**< HCI: protocol error 0x02 */ + OI_HCIERR_HARDWARE_FAILURE = 703, /**< HCI: protocol error 0x03 */ + OI_HCIERR_PAGE_TIMEOUT = 704, /**< HCI: protocol error 0x04 */ + OI_HCIERR_AUTHENTICATION_FAILURE = 705, /**< HCI: protocol error 0x05 */ + OI_HCIERR_KEY_MISSING = 706, /**< HCI: protocol error 0x06 */ + OI_HCIERR_MEMORY_FULL = 707, /**< HCI: protocol error 0x07 */ + OI_HCIERR_CONNECTION_TIMEOUT = 708, /**< HCI: protocol error 0x08 */ + OI_HCIERR_MAX_NUM_OF_CONNECTIONS = 709, /**< HCI: protocol error 0x09 */ + OI_HCIERR_MAX_NUM_OF_SCO_CONNECTIONS = 710, /**< HCI: protocol error 0x0A */ + OI_HCIERR_ACL_CONNECTION_ALREADY_EXISTS = 711, /**< HCI: protocol error 0x0B */ + OI_HCIERR_COMMAND_DISALLOWED = 712, /**< HCI: protocol error 0x0C */ + OI_HCIERR_HOST_REJECTED_RESOURCES = 713, /**< HCI: protocol error 0x0D */ + OI_HCIERR_HOST_REJECTED_SECURITY = 714, /**< HCI: protocol error 0x0E */ + OI_HCIERR_HOST_REJECTED_PERSONAL_DEVICE = 715, /**< HCI: protocol error 0x0F */ + OI_HCIERR_HOST_TIMEOUT = 716, /**< HCI: protocol error 0x10 */ + OI_HCIERR_UNSUPPORTED = 717, /**< HCI: protocol error 0x11 */ + OI_HCIERR_INVALID_PARAMETERS = 718, /**< HCI: protocol error 0x12 */ + OI_HCIERR_OTHER_END_USER_DISCONNECT = 719, /**< HCI: protocol error 0x13 */ + OI_HCIERR_OTHER_END_LOW_RESOURCES = 720, /**< HCI: protocol error 0x14 */ + OI_HCIERR_OTHER_END_POWERING_OFF = 721, /**< HCI: protocol error 0x15 */ + OI_HCIERR_CONNECTION_TERMINATED_LOCALLY = 722, /**< HCI: protocol error 0x16 */ + OI_HCIERR_REPEATED_ATTEMPTS = 723, /**< HCI: protocol error 0x17 */ + OI_HCIERR_PAIRING_NOT_ALLOWED = 724, /**< HCI: protocol error 0x18 */ + OI_HCIERR_UNKNOWN_LMP_PDU = 725, /**< HCI: protocol error 0x19 */ + OI_HCIERR_UNSUPPORTED_REMOTE_FEATURE = 726, /**< HCI: protocol error 0x1A */ + OI_HCIERR_SCO_OFFSET_REJECTED = 727, /**< HCI: protocol error 0x1B */ + OI_HCIERR_SCO_INTERVAL_REJECTED = 728, /**< HCI: protocol error 0x1C */ + OI_HCIERR_SCO_AIR_MODE_REJECTED = 729, /**< HCI: protocol error 0x1D */ + OI_HCIERR_INVALID_LMP_PARMS = 730, /**< HCI: protocol error 0x1E */ + OI_HCIERR_UNSPECIFIED_ERROR = 731, /**< HCI: protocol error 0x1F */ + OI_HCIERR_UNSUPPORTED_LMP_PARAMETERS = 732, /**< HCI: protocol error 0x20 */ + OI_HCIERR_ROLE_CHANGE_NOT_ALLOWED = 733, /**< HCI: protocol error 0x21 */ + OI_HCIERR_LMP_RESPONSE_TIMEOUT = 734, /**< HCI: protocol error 0x22 */ + OI_HCIERR_LMP_ERROR_TRANS_COLLISION = 735, /**< HCI: protocol error 0x23 */ + OI_HCIERR_LMP_PDU_NOT_ALLOWED = 736, /**< HCI: protocol error 0x24 */ + OI_HCIERR_ENCRYPTION_MODE_NOT_ACCEPTABLE = 737, /**< HCI: protocol error 0x25 */ + OI_HCIERR_UNIT_KEY_USED = 738, /**< HCI: protocol error 0x26 */ + OI_HCIERR_QOS_NOT_SUPPORTED = 739, /**< HCI: protocol error 0x27 */ + OI_HCIERR_INSTANT_PASSED = 740, /**< HCI: protocol error 0x28 */ + OI_HCIERR_UNIT_KEY_PAIRING_UNSUPPORTED = 741, /**< HCI: protocol error 0x29 */ + OI_HCIERR_DIFFERENT_TRANS_COLLISION = 742, /**< HCI: protocol error 0x2A */ + OI_HCIERR_RESERVED_2B = 743, /**< HCI: protocol error 0x2B */ + OI_HCIERR_QOS_UNACCEPTABLE_PARAMETER = 744, /**< HCI: protocol error 0x2C */ + OI_HCIERR_QOS_REJECTED = 745, /**< HCI: protocol error 0x2D */ + OI_HCIERR_CHANNEL_CLASSIFICATION_NS = 746, /**< HCI: protocol error 0x2E */ + OI_HCIERR_INSUFFICIENT_SECURITY = 747, /**< HCI: protocol error 0x2F */ + OI_HCIERR_PARM_OUT_OF_MANDATORY_RANGE = 748, /**< HCI: protocol error 0x30 */ + OI_HCIERR_RESERVED_31 = 749, /**< HCI: protocol error 0x31 */ + OI_HCIERR_ROLE_SWITCH_PENDING = 750, /**< HCI: protocol error 0x32 */ + OI_HCIERR_RESERVED_33 = 751, /**< HCI: protocol error 0x33 */ + OI_HCIERR_RESERVED_SLOT_VIOLATION = 752, /**< HCI: protocol error 0x34 */ + OI_HCIERR_ROLE_SWITCH_FAILED = 753, /**< HCI: protocol error 0x35 */ + OI_HCIERR_EIR_TOO_LARGE = 754, /**< HCI: protocol error 0x36 */ + OI_HCIERR_SSP_NOT_SUPPORTED_BY_HOST = 755, /**< HCI: protocol error 0x37 */ + OI_HCIERR_HOST_BUSY_PAIRING = 756, /**< HCI: protocol error 0x38 */ + + OI_HCIERR_UNKNOWN_ERROR = 757, /**< HCI: unknown error code */ + OI_HCIERR_LAST_ERROR_VALUE = 757, /**< marker for last HCI protocol error */ + + OI_SDP_SPEC_ERROR = 800, /**< SDP: Base error status for mapping OI_STATUS codes to SDP errors */ + OI_SDP_INVALID_SERVICE_RECORD_HANDLE = (OI_SDP_SPEC_ERROR + 2), /**< SDP: protocol error Invalid Service Record Handle */ + OI_SDP_INVALID_REQUEST_SYNTAX = (OI_SDP_SPEC_ERROR + 3), /**< SDP: protocol error Invalid Request Syntax */ + OI_SDP_INVALID_PDU_SIZE = (OI_SDP_SPEC_ERROR + 4), /**< SDP: protocol error Invalid PDU Size */ + OI_SDP_INVALID_CONTINUATION_STATE = (OI_SDP_SPEC_ERROR + 5), /**< SDP: protocol error Invalid Continuation State */ + OI_SDP_INSUFFICIENT_RESOURCES = (OI_SDP_SPEC_ERROR + 6), /**< SDP: protocol error Insufficient Resources */ + OI_SDP_ERROR = 807, /**< SDP: server returned an error code */ + OI_SDP_CORRUPT_DATA_ELEMENT = 808, /**< SDP: Invalid or corrupt data element representation */ + OI_SDP_SERVER_NOT_CONNECTED = 810, /**< SDP: Attempt to disconnect from an unconnected server */ + OI_SDP_ACCESS_DENIED = 811, /**< SDP: Server denied access to server */ + OI_SDP_ATTRIBUTES_OUT_OF_ORDER = 812, /**< SDP: Attributes in attribute list not in ascending order */ + OI_SDP_DEVICE_DOES_NOT_SUPPORT_SDP = 813, /**< SDP: Tried to connect to a device that does not support SDP */ + OI_SDP_NO_MORE_DATA = 815, /**< SDP: Server does not have more continuation data */ + OI_SDP_REQUEST_PARAMS_TOO_LONG = 816, /**< SDP: Parameters for a request exceed the L2CAP buffer size */ + OI_SDP_REQUEST_PENDING = 817, /**< SDP: Cannot make a request when another request is being processed */ + OI_SDP_SERVER_CONNECT_FAILED = 819, /**< SDP: Failed attempt to connect to an SDP server */ + OI_SDP_SERVER_TOO_MANY_CONNECTIONS = 821, /**< SDP: Exceeded maximum number of simultaneous server connections */ + OI_SDP_NO_MATCHING_SERVICE_RECORD = 823, /**< SDP: No service record matched the UUID list */ + OI_SDP_PARTIAL_RESPONSE = 824, /**< SDP: Internal use only */ + OI_SDP_ILLEGAL_ARGUMENT = 825, /**< SDP: Illegal argument passed to an SDP function */ + OI_SDP_ATTRIBUTE_NOT_FOUND = 826, /**< SDP: A requested attribute was not found in a service record */ + OI_SDP_DATABASE_OUT_OF_RESOURCES = 827, /**< SDP: server database is out of memory */ + OI_SDP_SHORT_PDU = 829, /**< SDP: Not enough bytes in the packet */ + OI_SDP_TRANSACTION_ID_MISMATCH = 830, /**< SDP: Transaction Id was not as expected */ + OI_SDP_UNEXPECTED_RESPONSE_PDU_ID = 831, /**< SDP: Did not expect this response PDU */ + OI_SDP_REQUEST_TIMEOUT = 832, /**< SDP: Did not get a response within the timeout period */ + OI_SDP_INVALID_RESPONSE_SYNTAX = 833, /**< SDP: Response is not correctly formatted */ + OI_SDP_CONNECTION_TIMEOUT = 834, /**< SDP: Connection attempt timed out at a lower layer */ + OI_SDP_RESPONSE_DATA_ERROR = 835, /**< SDP: Response to a service request appears to be corrupt */ + OI_SDP_TOO_MANY_ATTRIBUTE_BYTES = 836, /**< SDP: Response contained more bytes than requested. */ + OI_SDP_TOO_MANY_SERVICE_RECORDS = 837, /**< SDP: Response contained more service records than requested. */ + OI_SDP_INVALID_CONNECTION_ID = 838, /**< SDP: Invalid connection ID in an SDP request */ + OI_SDP_CANNOT_SET_ATTRIBUTE = 839, /**< SDP: Attempt to set a dynamic attribute value failed */ + OI_SDP_BADLY_FORMED_ATTRIBUTE_VALUE = 840, /**< SDP: An attribute value has the wrong type or structure */ + OI_SDP_NO_ATTRIBUTE_LIST_TO_REMOVE = 841, /**< SDP: Attempt to remove a non-existent attribute list from a service record */ + OI_SDP_ATTRIBUTE_LIST_ALREADY_ADDED = 842, /**< SDP: An attribute list has already been added to the service record */ + OI_SDP_DATA_ELEMENT_TRUNCATED = 843, /**< SDP: Data element truncated (too few bytes) */ + + OI_RFCOMM_WRITE_IN_PROGRESS = 901, /**< RFCOMM: Write in progress */ + OI_RFCOMM_INVALID_BAUDRATE = 903, /**< RFCOMM: Invalid baudrate */ + OI_RFCOMM_INVALID_DATABIT = 904, /**< RFCOMM: Invalid databit */ + OI_RFCOMM_INVALID_STOPBIT = 905, /**< RFCOMM: Invalid stopbit */ + OI_RFCOMM_INVALID_PARITY = 906, /**< RFCOMM: Invalid parity */ + OI_RFCOMM_INVALID_PARITYTYPE = 907, /**< RFCOMM: Invalid paritytype */ + OI_RFCOMM_INVALID_FLOWCONTROL = 908, /**< RFCOMM: Invalid flowcontrol */ + OI_RFCOMM_SESSION_EXISTS = 909, /**< RFCOMM: Session exists */ + OI_RFCOMM_INVALID_CHANNEL = 910, /**< RFCOMM: Invalid channel */ + OI_RFCOMM_DLCI_EXISTS = 911, /**< RFCOMM: DLCI exists */ + OI_RFCOMM_LINK_NOT_FOUND = 912, /**< RFCOMM: Link not found */ + OI_RFCOMM_REMOTE_REJECT = 913, /**< RFCOMM: Remote reject */ + OI_RFCOMM_TEST_IN_PROGRESS = 915, /**< RFCOMM: Test in progress */ + OI_RFCOMM_SESSION_NOT_FOUND = 916, /**< RFCOMM: Session not found */ + OI_RFCOMM_INVALID_PACKET = 917, /**< RFCOMM: Invalid packet */ + OI_RFCOMM_FRAMESIZE_EXCEEDED = 918, /**< RFCOMM: Framesize exceeded */ + OI_RFCOMM_INVALID_DLCI = 920, /**< RFCOMM: Invalid dlci */ + OI_RFCOMM_SERVER_NOT_REGISTERED = 921, /**< RFCOMM: Server not registered */ + OI_RFCOMM_CREDIT_ERROR = 922, /**< RFCOMM: Credit error */ + OI_RFCOMM_NO_CHANNEL_NUMBER = 923, /**< RFCOMM: No channel number */ + OI_RFCOMM_QUERY_IN_PROGRESS = 924, /**< RFCOMM: Query in progress */ + OI_RFCOMM_SESSION_SHUTDOWN = 925, /**< RFCOMM: Session shutdown */ + OI_RFCOMM_LOCAL_DEVICE_DISCONNECTED = 926, /**< RFCOMM: Local device disconnected */ + OI_RFCOMM_REMOTE_DEVICE_DISCONNECTED = 927, /**< RFCOMM: Remote device disconnected */ + OI_RFCOMM_OUT_OF_SERVER_CHANNELS = 928, /**< RFCOMM: Out of server channels */ + + OI_DISPATCH_INVALID_CB_HANDLE = 1001, /**< Dispatcher was handed an invalid callback handle */ + OI_DISPATCH_TABLE_OVERFLOW = 1002, /**< Dispatcher table is full */ + + OI_TEST_UNKNOWN_TEST = 1101, /**< TEST: Unknown test */ + OI_TEST_FAIL = 1102, /**< TEST: Fail */ + + OI_HCITRANS_CANNOT_CONNECT_TO_DEVICE = 1201, /**< TRANSPORT: Cannot connect to device */ + OI_HCITRANS_BUFFER_TOO_SMALL = 1203, /**< TRANSPORT: Buffer too small */ + OI_HCITRANS_NULL_DEVICE_HANDLE = 1204, /**< TRANSPORT: Null device handle */ + OI_HCITRANS_IO_ERROR = 1205, /**< TRANSPORT: IO error */ + OI_HCITRANS_DEVICE_NOT_READY = 1206, /**< TRANSPORT: Device not ready */ + OI_HCITRANS_FUNCTION_NOT_SUPPORTED = 1207, /**< TRANSPORT: Function not supporteD */ + OI_HCITRANS_ACCESS_DENIED = 1209, /**< TRANSPORT: win32 */ + OI_HCITRANS_ACL_DATA_ERROR = 1210, /**< TRANSPORT: ACL data error */ + OI_HCITRANS_SCO_DATA_ERROR = 1211, /**< TRANSPORT: SCO data error */ + OI_HCITRANS_EVENT_DATA_ERROR = 1212, /**< TRANSPORT: HCI event data error */ + OI_HCITRANS_INTERNAL_ERROR = 1214, /**< TRANSPORT: Internal error in the transport */ + OI_HCITRANS_LINK_NOT_ACTIVE = 1215, /**< TRANSPORT: Link to the device is not currently active */ + OI_HCITRANS_INITIALIZING = 1216, /**< TRANSPORT: Transport is initializing */ + + OI_DEVMGR_NO_CONNECTION = 1301, /**< DEVMGR: No connection */ + OI_DEVMGR_HARDWARE_ERROR = 1305, /**< DEVMGR: error reported by HCI */ + OI_DEVMGR_PENDING_CONNECT_LIST_FULL = 1307, /**< DEVMGR: Pending connect list full */ + OI_DEVMGR_CONNECTION_LIST_FULL = 1309, /**< DEVMGR: Connection list full */ + OI_DEVMGR_NO_SUCH_CONNECTION = 1310, /**< DEVMGR: No such connection */ + OI_DEVMGR_INQUIRY_IN_PROGRESS = 1311, /**< DEVMGR: Inquiry in progress */ + OI_DEVMGR_PERIODIC_INQUIRY_ACTIVE = 1312, /**< DEVMGR: Periodic inquiry active */ + OI_DEVMGR_NO_INQUIRIES_ACTIVE = 1313, /**< DEVMGR: can not cancel/exit if not active */ + OI_DEVMGR_DUPLICATE_CONNECTION = 1314, /**< DEVMGR: internal error */ + OI_DEVMGR_DUPLICATE_EVENT_CALLBACK = 1316, /**< DEVMGR: attempt to register same callback twice */ + OI_DEVMGR_EVENT_CALLBACK_LIST_FULL = 1317, /**< DEVMGR: can not register event callback, list is full */ + OI_DEVMGR_EVENT_CALLBACK_NOT_FOUND = 1318, /**< DEVMGR: attempt to unregister callback failed */ + OI_DEVMGR_BUSY = 1319, /**< DEVMGR: some operations can only execute one at a time */ + OI_DEVMGR_ENUM_UNEXPECTED_INQ_COMPLETE = 1320, /**< DEVMGR: inquiry complete event in inappropriate enumeration state */ + OI_DEVMGR_ENUM_UNEXPECTED_INQ_RESULT = 1321, /**< DEVMGR: inquiry result event in inappropriate enumeration state */ + OI_DEVMGR_ENUM_DATABASE_FULL = 1322, /**< DEVMGR: device enumeration, database is full, couldn't add a new device */ + OI_DEVMGR_ENUM_INQUIRIES_OVERLAP = 1323, /**< DEVMGR: device enumeration, periodic inquiries occurring too close together */ + OI_DEVMGR_UNKNOWN_LINK_TYPE = 1324, /**< DEVMGR: HCI connect request with unkown link type */ + OI_DEVMGR_PARAM_IO_ACTIVE = 1325, /**< DEVMGR: request for parameter read/write while param read/write active */ + OI_DEVMGR_UNKNOWN_IAC_LAP = 1326, /**< DEVMGR: unrecognized IAC LAP */ + OI_DEVMGR_SCO_ALREADY_REGISTERED = 1327, /**< DEVMGR: only one application can use SCO */ + OI_DEVMGR_SCO_NOT_REGISTERED = 1328, /**< DEVMGR: SCO applications must register before using the API */ + OI_DEVMGR_SCO_WITHOUT_ACL = 1329, /**< DEVMGR: Got SCO connection but there is no underlying ACL connection */ + OI_DEVMGR_NO_SUPPORT = 1330, /**< DEVMGR: Request is not supported by the device */ + OI_DEVMGR_WRITE_POLICY_FAILED = 1331, /**< DEVMGR: connection attempt failed - unable to write link policy */ + OI_DEVMGR_NOT_IN_MASTER_MODE = 1332, /**< DEVMGR: OI_DEVMGR EndMasterMode without prior OI_DEVMGR_BeginMasterMode */ + OI_DEVMGR_POLICY_VIOLATION = 1333, /**< DEVMGR: low-power request is rejected - link policy does not allow it */ + OI_DEVMGR_BUSY_TIMEOUT = 1334, /**< DEVMGR: queued operation timed out while in the queue; \n + timeout configurable via @ref OI_CONFIG_DEVMGR::connectQueueTimeoutSecs "connectQueueTimeoutSecs" */ + OI_DEVMGR_REENCRYPT_FAILED = 1335, /**< DEVMGR: failed to re-encrypt link after role switch */ + OI_DEVMGR_ROLE_POLICY_CONFLICT = 1336, /**< DEVMGR: requested role conflicts with current policy */ + OI_DEVMGR_BAD_INTERVAL = 1337, /**< DEVMGR: current linkTO outside range of requested min/max interval */ + OI_DEVMGR_INVALID_SCO_HANDLE = 1338, /**< DEVMGR: HCI SCO event, invalid handle */ + OI_DEVMGR_CONNECTION_OVERLAP = 1339, /**< DEVMGR: Connection failed due to race condition with remote side */ + OI_DEVMGR_ORPHAN_SUBRATE_COMPLETE = 1340, /**< DEVMGR: sniff subrate complete, but no callback */ + OI_DEVMGR_EIR_RESPONSE_2_LARGE = 1341, /**< DEVMGR: eir builder, response length would exceed spec max */ + + OI_SECMGR_NO_POLICY = 1401, /**< SECMGR: no security policy has been established */ + OI_SECMGR_INTERNAL_ERROR = 1402, /**< SECMGR: internal inconsistency */ + OI_SECMGR_ORPHANED_CALLBACK = 1403, /**< SECMGR: we've been called back, but CB context is gone */ + OI_SECMGR_BUSY = 1404, /**< SECMGR: configure and access request cannot be concurrent */ + OI_SECMGR_DEVICE_NOT_TRUSTED = 1405, /**< SECMGR: l2cap access denied - device is not trusted */ + OI_SECMGR_DEVICE_ENCRYPT_FAIL = 1407, /**< SECMGR: l2cap access denied - failed to start encryption */ + OI_SECMGR_DISCONNECTED_FAIL = 1408, /**< SECMGR: l2cap access denied - disconnected */ + OI_SECMGR_ACCESS_PENDING = 1409, /**< SECMGR: l2cap access request is still pending */ + OI_SECMGR_PIN_CODE_TOO_SHORT = 1410, /**< SECMGR: Higher-layer process gave us a pin code that is too short */ + OI_SECMGR_UNKNOWN_ENCRYPT_VALUE = 1411, /**< SECMGR: got EncryptionChange event, unknown encryption enable value */ + OI_SECMGR_INVALID_POLICY = 1412, /**< SECMGR: the specified security policy is not valid for security mode */ + OI_SECMGR_AUTHORIZATION_FAILED = 1413, /**< SECMGR: device authorization failed */ + OI_SECMGR_ENCRYPTION_FAILED = 1414, /**< SECMGR: device encryption failed */ + OI_SECMGR_UNIT_KEY_UNSUPPORTED = 1415, /**< SECMGR: authentication failed due to non-support of unit keys */ + OI_SECMGR_NOT_REGISTERED = 1416, /**< SECMGR: required registrations have not yet occurred */ + OI_SECMGR_ILLEGAL_WRITE_SSP_MODE = 1417, /**< SECMGR: 2.1 HCI spec does not allow SSP mode to be disabled */ + OI_SECMGR_INVALID_SEC_LEVEL = 1418, /**< SECMGR: security level for a service is not a valid value */ + OI_SECMGR_INSUFFICIENT_LINK_KEY = 1419, /**< SECMGR: link key type is not sufficient to meet service requirements */ + OI_SECMGR_INVALID_KEY_TYPE = 1420, /**< SECMGR: link key type is not a valid value */ + OI_SECMGR_SSP_NOT_ENCRYPTED = 1421, /**< SECMGR: ssp required encryption on incoming link */ + OI_SECMGR_ORPHAN_EVENT = 1422, /**< SECMGR: some HCI security event unrelated to current processes */ + OI_SECMGR_NOT_BONDABLE = 1423, /**< SECMGR: not in bondable mode */ + + OI_TCS_INVALID_ELEMENT_TYPE = 1602, /**< TCS: element type is invalid */ + OI_TCS_INVALID_PACKET = 1603, /**< TCS: packet is invalide */ + OI_TCS_CALL_IN_PROGRESS = 1604, /**< TCS: call is in progress */ + OI_TCS_NO_CALL_IN_PROGRESS = 1605, /**< TCS: no call in progress */ + + OI_OBEX_CONTINUE = 1701, /**< OBEX: Continue processing OBEX request */ + OI_OBEX_COMMAND_ERROR = 1702, /**< OBEX: An unrecognized OBEX command opcode */ + OI_OBEX_CONNECTION_TIMEOUT = 1703, /**< OBEX: Timeout waiting for a response to a request */ + OI_OBEX_CONNECT_FAILED = 1704, /**< OBEX: An OBEX connection request did not succeed */ + OI_OBEX_DISCONNECT_FAILED = 1705, /**< OBEX: A disconnect failed probably because the connection did not exist */ + OI_OBEX_ERROR = 1706, /**< OBEX: Unspecified OBEX error */ + OI_OBEX_INCOMPLETE_PACKET = 1707, /**< OBEX: Packet too short or corrupt */ + OI_OBEX_LENGTH_REQUIRED = 1708, /**< OBEX: Length header required in OBEX command */ + OI_OBEX_NOT_CONNECTED = 1709, /**< OBEX: No connection to OBEX server */ + OI_OBEX_NO_MORE_CONNECTIONS = 1710, /**< OBEX: Reached max connections limit */ + OI_OBEX_OPERATION_IN_PROGRESS = 1711, /**< OBEX: Another operation is still in progress on a connection */ + OI_OBEX_PUT_RESPONSE_ERROR = 1712, /**< OBEX: An error in the response to a PUT command */ + OI_OBEX_GET_RESPONSE_ERROR = 1713, /**< OBEX: An error in the response to a GET command */ + OI_OBEX_REQUIRED_HEADER_NOT_FOUND = 1714, /**< OBEX: packet was missing a required header */ + OI_OBEX_SERVICE_UNAVAILABLE = 1715, /**< OBEX: Unown OBEX target or required service */ + OI_OBEX_TOO_MANY_HEADER_BYTES = 1716, /**< OBEX: Headers will not fit in single OBEX packet */ + OI_OBEX_UNKNOWN_COMMAND = 1717, /**< OBEX: Unrecognized OBEX command */ + OI_OBEX_UNSUPPORTED_VERSION = 1718, /**< OBEX: Version mismatch */ + OI_OBEX_CLIENT_ABORTED_COMMAND = 1719, /**< OBEX: server received abort command */ + OI_OBEX_BAD_PACKET = 1720, /**< OBEX: Any malformed OBEX packet */ + OI_OBEX_BAD_REQUEST = 1721, /**< OBEX: Maps to OBEX response of the same name */ + OI_OBEX_OBJECT_OVERFLOW = 1723, /**< OBEX: Too many bytes received. */ + OI_OBEX_NOT_FOUND = 1724, /**< OBEX: Maps to obex response of same name */ + OI_OBEX_ACCESS_DENIED = 1735, /**< OBEX: Object could not be read or written. */ + OI_OBEX_VALUE_NOT_ACCEPTABLE = 1736, /**< OBEX: Value in a command was not in the acceptable range. */ + OI_OBEX_PACKET_OVERFLOW = 1737, /**< OBEX: Buffer will not fit in a single OBEX packet. */ + OI_OBEX_NO_SUCH_FOLDER = 1738, /**< OBEX: Error returned by a setpath operation. */ + OI_OBEX_NAME_REQUIRED = 1739, /**< OBEX: Name must be non-null and non-empty. */ + OI_OBEX_PASSWORD_TOO_LONG = 1740, /**< OBEX: Password exceeds implementation imposed length limit. */ + OI_OBEX_PRECONDITION_FAILED = 1741, /**< OBEX: response Precondition Failed */ + OI_OBEX_UNAUTHORIZED = 1742, /**< OBEX: authentication was not successful. */ + OI_OBEX_NOT_IMPLEMENTED = 1743, /**< OBEX: Unimplemented feature. */ + OI_OBEX_INVALID_AUTH_DIGEST = 1744, /**< OBEX: An authentication digest was bad. */ + OI_OBEX_INVALID_OPERATION = 1745, /**< OBEX: Operation not allowed at this time. */ + OI_OBEX_DATABASE_FULL = 1746, /**< OBEX: Sync database full. */ + OI_OBEX_DATABASE_LOCKED = 1747, /**< OBEX: Sync database locked. */ + OI_OBEX_INTERNAL_SERVER_ERROR = 1748, /**< OBEX: response Internal Server Error */ + OI_OBEX_UNSUPPORTED_MEDIA_TYPE = 1749, /**< OBEX: response Unsupported Media Type */ + OI_OBEX_PARTIAL_CONTENT = 1750, /**< OBEX: response Partial Content */ + OI_OBEX_METHOD_NOT_ALLOWED = 1751, /**< OBEX: response Method Not Allowed */ + OI_OBEXSRV_INCOMPLETE_GET = 1752, /**< OBEX: Indicates to a GET handler that the request phase is still in progress */ + OI_OBEX_FOLDER_BROWSING_NOT_ALLOWED = 1753, /**< OBEX: Indicates that an FTP server does not allow folder browsing */ + OI_OBEX_SERVER_FORCED_DISCONNECT = 1754, /**< OBEX: connection was forcibly terminated by the server */ + OI_OBEX_OFS_ERROR = 1755, /**< OBEX: OPP object file system error occurred */ + OI_OBEX_FILEOP_ERROR = 1756, /**< OBEX: FTP/PBAP file operation system error occurred */ + OI_OBEX_USERID_TOO_LONG = 1757, /**< OBEX: User Id exceeds spec limited length limit. */ + + OI_HANDSFREE_EVENT_REPORTING_DISABLED = 1801, /**< HANDSFREE: Event reporting disabled */ + OI_HANDSFREE_NOT_CONNECTED = 1802, /**< HANDSFREE: Not connected */ + OI_HANDSFREE_SERVICE_NOT_STARTED = 1803, /**< HANDSFREE: Cannot connect to handsfree AG if handsfree service not started */ + OI_HANDSFREE_AG_SERVICE_NOT_STARTED = 1804, /**< HANDSFREE: Cannot connect to handsfree device if handsfree AG service not started */ + OI_HANDSFREE_COMMAND_IN_PROGRESS = 1805, /**< HANDSFREE: Cannot accept a command at this time */ + OI_HANDSFREE_AUDIO_ALREADY_CONNECTED = 1806, /**< HANDSFREE: Audio is already connected */ + OI_HANDSFREE_AUDIO_NOT_CONNECTED = 1807, /**< HANDSFREE: Audio is not connected */ + OI_HANDSFREE_FEATURE_NOT_SUPPORTED = 1808, /**< HANDSFREE: Local or remote feature not supported for requested command */ + + OI_HEADSET_SERVICE_NOT_STARTED = 1901, /**< HEADSET: Cannot connect to headset AG if headset service not started */ + OI_HEADSET_AG_SERVICE_NOT_STARTED = 1902, /**< HEADSET: Cannot connect to headset device if headset AG service not started */ + OI_HEADSET_COMMAND_IN_PROGRESS = 1903, /**< HEADSET: Cannot accept a command at this time */ + + OI_BNEP_INVALID_MTU = 2001, /**< BNEP: The remote device cannot support the minimum BNEP MTU */ + OI_BNEP_SETUP_TIMEOUT = 2002, /**< BNEP: The setup request timed out. */ + OI_BNEP_SERVICE_NOT_REGISTERED = 2003, /**< BNEP: The requested service was not found. */ + OI_BNEP_INVALID_HANDLE = 2004, /**< BNEP: The specified connection handle is not valid. */ + OI_BNEP_RESPONSE_TIMEOUT = 2005, /**< BNEP: The timer for receiving a response has expired. */ + OI_BNEP_INVALID_CONNECTION = 2006, /**< BNEP: Invalid connection */ + OI_BNEP_INVALID_FILTER = 2007, /**< BNEP: The supplied filter was invalid. */ + OI_BNEP_CONNECTION_EXISTS = 2008, /**< BNEP: An attempt was made to create a duplicate connection. */ + OI_BNEP_NOT_INITIALIZED = 2009, /**< BNEP: Init has not been called */ + OI_BNEP_CONNECT_BASE = 2010, /**< BNEP: connection response codes */ + OI_BNEP_CONNECT_FAILED_INVALID_DEST_UUID = 2011, /**< BNEP: connect response code Invalid Dest UUID */ + OI_BNEP_CONNECT_FAILED_INVALID_SOURCE_UUID = 2012, /**< BNEP: connect response code Invalid Source UUID */ + OI_BNEP_CONNECT_FAILED_INVALID_UUID_SIZE = 2013, /**< BNEP: connect response code Invalid UUID Size */ + OI_BNEP_CONNECT_FAILED_NOT_ALLOWED = 2014, /**< BNEP: connect response code Not Allowed */ + OI_BNEP_FILTER_NET_BASE = 2020, /**< BNEP: filter response codes */ + OI_BNEP_FILTER_NET_UNSUPPORTED_REQUEST = 2021, /**< BNEP: filter response code Unsupported Request */ + OI_BNEP_FILTER_NET_FAILED_INVALID_PROTOCOL_TYPE = 2022, /**< BNEP: filter response code Invalid Protocol Type */ + OI_BNEP_FILTER_NET_FAILED_MAX_LIMIT_REACHED = 2023, /**< BNEP: filter response code Max Limit Reached */ + OI_BNEP_FILTER_NET_FAILED_SECURITY = 2024, /**< BNEP: filter response code Security */ + OI_BNEP_FILTER_MULTI_BASE = 2030, /**< BNEP: multicast response codes */ + OI_BNEP_FILTER_MULTI_UNSUPPORTED_REQUEST = 2031, /**< BNEP: multicast response code Unsupported Request */ + OI_BNEP_FILTER_MULTI_FAILED_INVALID_ADDRESS = 2032, /**< BNEP: multicast response code Invalid Address */ + OI_BNEP_FILTER_MULTI_FAILED_MAX_LIMIT_REACHED = 2033, /**< BNEP: multicast response code Max Limit Reached */ + OI_BNEP_FILTER_MULTI_FAILED_SECURITY = 2034, /**< BNEP: multicast response code Security */ + OI_BNEP_LOCAL_DEVICE_MUST_BE_MASTER = 2040, /**< BNEP: Device must be master of the piconet for this function */ + OI_BNEP_PACKET_FILTERED_OUT = 2041, /**< BNEP: Packet did not pass current filters */ + + OI_NETIFC_UP_FAILED = 2101, /**< NETIFC: Could not bring up network interface */ + OI_NETIFC_COULD_NOT_CREATE_THREAD = 2102, /**< NETIFC: Network interface could not create a read thread */ + OI_NETIFC_INITIALIZATION_FAILED = 2103, /**< NETIFC: Error in network interface initialization */ + OI_NETIFC_INTERFACE_ALREADY_UP = 2104, /**< NETIFC: Network interface is already up */ + OI_NETIFC_INTERFACE_NOT_UP = 2105, /**< NETIFC: Network interface is not up */ + OI_NETIFC_PACKET_TOO_BIG = 2106, /**< NETIFC: The packet is too big */ + + OI_PAN_ROLE_ALREADY_REGISTERED = 2201, /**< PAN: This PAN role was already registered */ + OI_PAN_ROLE_NOT_ALLOWED = 2202, /**< PAN: The PAN role is not currently allowed */ + OI_PAN_INCOMPATIBLE_ROLES = 2203, /**< PAN: Only certain local and remote role combinations are permitted */ + OI_PAN_INVALID_ROLE = 2204, /**< PAN: Role specified is not one the defined PAN roles */ + OI_PAN_CONNECTION_IN_PROGRESS = 2205, /**< PAN: A PAN connection is currently being established */ + OI_PAN_USER_ALREADY_CONNECTED = 2206, /**< PAN: PAN user role only allows a single connection */ + OI_PAN_DEVICE_CONNECTED = 2207, /**< PAN: A PAN connection already exists to specified device */ + + OI_CODEC_SBC_NO_SYNCWORD = 2301, /**< CODEC: Couldn't find an SBC SYNCWORD */ + OI_CODEC_SBC_NOT_ENOUGH_HEADER_DATA = 2302, /**< CODEC: Not enough data provided to decode an SBC header */ + OI_CODEC_SBC_NOT_ENOUGH_BODY_DATA = 2303, /**< CODEC: Decoded the header, but not enough data to contain the rest of the frame */ + OI_CODEC_SBC_NOT_ENOUGH_AUDIO_DATA = 2304, /**< CODEC: Not enough audio data for this frame */ + OI_CODEC_SBC_CHECKSUM_MISMATCH = 2305, /**< CODEC: The frame header didn't match the checksum */ + OI_CODEC_SBC_PARTIAL_DECODE = 2306, /**< CODEC: Decoding was successful, but frame data still remains. Next call will provide audio without consuming input data. */ + + OI_FIFOQ_QUEUE_NOT_ALIGNED = 2401, /**< FIFOQ: queue must be 32-bit aligned */ + OI_FIFOQ_INVALID_Q = 2402, /**< FIFOQ: queue parameter is not a valid queue */ + OI_FIFOQ_BUF_TOO_LARGE = 2403, /**< FIFOQ: attempt to queue a buffer which is too large */ + OI_FIFOQ_FULL = 2404, /**< FIFOQ: enqueue() failed, queue is full */ + OI_FIFOQ_NOT_ALLOCATED = 2405, /**< FIFOQ: Enqueue QBuf() failed, buffer not allocated */ + OI_FIFOQ_INVALID_DATA_PTR = 2406, /**< FIFOQ: Enqueue QBuf() failed, data pointer does not match */ + + OI_HID_HOST_SERVICE_NOT_STARTED = 2601, /**< HID: Cannot connect to a HID device unless HID host is started */ + OI_HID_DEVICE_SERVICE_NOT_STARTED = 2602, /**< HID: Cannot connect to a HID host unless HID device is started */ + + OI_AT_ERROR = 2701, /**< AT: ERROR response */ + OI_AT_NO_CARRIER = 2702, /**< AT: NO CARRIER response */ + OI_AT_BUSY = 2703, /**< AT: BUSY response */ + OI_AT_NO_ANSWER = 2704, /**< AT: NO ANSWER response */ + OI_AT_DELAYED = 2705, /**< AT: DELAYED response */ + OI_AT_BLACKLISTED = 2706, /**< AT: BLACKLISTED response */ + OI_AT_CME_ERROR = 2707, /**< AT: +CME ERROR response */ + OI_AT_CMS_ERROR = 2708, /**< AT: +CMS ERROR response */ + + OI_BLST_CHARACTER_TIMEOUT = 2801, /**< BLST: Timeout expired while waiting for a character from the client. */ + OI_BLST_ACKNOWLDGE_TIMEOUT = 2802, /**< BLST: Timeout expired while waiting for event acknowledgment from the client */ + OI_BLST_TX_NOT_READY = 2803, /**< BLST: BLST is not ready to send a BHAPI message to the client. */ + OI_BLST_TX_BUSY = 2804, /**< BLST: BLST transmit buffer is in use. */ + + OI_AVDTP_CONNECTION_SEQ_ERROR = 2901, /**< AVDTP: sequencing of signalling/media channel connections broken. */ + OI_AVDTP_OUT_OF_RESOURCES = 2902, /**< AVDTP: Tried to allocate too many endpoints or signalling channels. */ + + OI_PBAP_REPOSITORY_NOT_SET = 3001, /**< PBAP: Phonebook repository must be set for operation to complete. */ + OI_PBAP_PHONEBOOK_NOT_SET = 3002, /**< PBAP: Phonebook be set for operation to complete. */ + + OI_AADP_BAD_ENDPOINT = 3101, /**< AADP: Invalid local endpoint specified */ + OI_AADP_BAD_STATE = 3102, /**< AADP: AADP State is not correct for this operation. */ + + OI_UNICODE_INVALID_SOURCE = 3200, /**< Unicode Conversion: Source string has invalid character encoding. */ + OI_UNICODE_SOURCE_EXHAUSTED = 3201, /**< Unicode Conversion: Incomplete Unicode character at end of source buffer. */ + OI_UNICODE_DESTINATION_EXHAUSTED = 3202, /**< Unicode Conversion: Destination buffer not large enough to hold resulting Unicode string. */ + + OI_AVRCP_TOO_MANY_CONNECTIONS = 3300, /**< AVRCP: Exceeded maximum number of simultaneous AVCTP connections. */ + OI_AVRCP_NOT_IMPLEMENTED = 3301, /**< AVRCP: The target does not implement the command specified by the opcode and operand. */ + OI_AVRCP_REJECTED = 3302, /**< AVRCP: The target cannot respond because of invalid operands in command packet. */ + OI_AVRCP_INVALID_RESPONSE = 3303, /**< AVRCP: The controller received the response with invalid parameters */ + OI_AVRCP_RESPONSE_PACKET_OVERFLOW = 3304, /**< AVRCP: The response message does not fir in one AVRCP packet (512 bytes), has to be fragmented. */ + OI_AVRCP_RESPONSE_INVALID_PDU = 3305, /**< AVRCP: Command rejected: target received a PDU that it did not understand. */ + OI_AVRCP_RESPONSE_INVALID_PARAMETER = 3306, /**< AVRCP: Command rejected: target received a PDU with a parameter ID that it did not understand. */ + OI_AVRCP_RESPONSE_PARAMETER_NOT_FOUND = 3307, /**< AVRCP: Command rejected: specified parameter not found, sent if the parameter ID is understood, but content is wrong or corrupted.*/ + OI_AVRCP_RESPONSE_INTERNAL_ERROR = 3308, /**< AVRCP: Command rejected: target detected other error conditions. */ + OI_MAX_BM3_STATUS_VAL, /* Maximum BM3 status code */ + + /* Status code values reserved for BM3 SDK platform-specific implementations */ + OI_STATUS_RESERVED_FOR_BCOT = 9000, + + /* Status code values reserved for BHAPI products */ + OI_STATUS_RESERVED_FOR_BHAPI = 9200, + + /* Status code values reserved for Soundabout products */ + OI_STATUS_RESERVED_FOR_SOUNDABOUT = 9400, + + /* + * Status code values greater than or equal to this value are reserved for use by applications. + * However, because of differences between compilers, and differences between 16-bit and 32-bit + * platforms custom status codes should be in the 16-bit range, so status codes can range from 0 + * to 65534, inclusive (65535 is reserved) + */ + OI_STATUS_RESERVED_FOR_APPS = 10000, + + + + OI_STATUS_NONE = 0xffff /**< Special status code to indicate that there is no status. (Only to be used for special cases involving OI_SLOG_ERROR() and OI_SLOG_WARNING().) */ + +} OI_STATUS; + + +/* Remeber to update the #define below when new reserved blocks are added to + * the list above. */ +#define OI_NUM_RESERVED_STATUS_BLOCKS 4 /**< Number of status code blocks reserved, including user apps */ + + +/** + * Test for success + */ +#define OI_SUCCESS(x) ((x) == OI_OK) + +/*****************************************************************************/ +#ifdef __cplusplus +} +#endif + +/**@}*/ + +#endif /* _OI_STATUS_H */ diff --git a/lib/bt/host/bluedroid/external/sbc/decoder/include/oi_stddefs.h b/lib/bt/host/bluedroid/external/sbc/decoder/include/oi_stddefs.h new file mode 100644 index 00000000..9ab424db --- /dev/null +++ b/lib/bt/host/bluedroid/external/sbc/decoder/include/oi_stddefs.h @@ -0,0 +1,232 @@ +/****************************************************************************** + * + * Copyright (C) 2014 The Android Open Source Project + * Copyright 2002 - 2004 Open Interface North America, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ +#ifndef OI_STDDEFS_H +#define OI_STDDEFS_H +/** + * @file + * This file contains BM3 standard type definitions. + * + */ + +/********************************************************************************** + $Revision: #1 $ +***********************************************************************************/ + +#include "oi_cpu_dep.h" + +/** \addtogroup Misc Miscellaneous APIs */ +/**@{*/ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef FALSE +#define FALSE 0 /**< This define statement sets FALSE as a preprocessor alias for 0. */ +#endif + +#ifndef TRUE +#define TRUE (!FALSE) /**< This define statement sets TRUE as a preprocessor alias for !FALSE. */ +#endif + +#ifdef HEW_TOOLCHAIN +#ifdef NULL +#undef NULL /**< Override HEW toolchain NULL definition */ +#endif +#define NULL 0 /**< HEW toolchain does not allow us to compare (void*) type to function pointer */ +#else +#ifndef NULL +#define NULL ((void*)0) /**< This define statement sets NULL as a preprocessor alias for (void*)0 */ +#endif +#endif + +/** + * @name Maximum and minimum values for basic types + * @{ + */ +#define OI_INT8_MIN ((OI_INT8)0x80) /**< decimal value: -128 */ +#define OI_INT8_MAX ((OI_INT8)0x7F) /**< decimal value: 127 */ +#define OI_INT16_MIN ((OI_INT16)0x8000) /**< decimal value: -32768 */ +#define OI_INT16_MAX ((OI_INT16)0x7FFF) /**< decimal value: 32767 */ +#define OI_INT32_MIN ((OI_INT32)0x80000000) /**< decimal value: -2,147,483,648 */ +#define OI_INT32_MAX ((OI_INT32)0x7FFFFFFF) /**< decimal value: 2,147,483,647 */ +#define OI_UINT8_MIN ((OI_UINT8)0) /**< decimal value: 0 */ +#define OI_UINT8_MAX ((OI_UINT8)0xFF) /**< decimal value: 255 */ +#define OI_UINT16_MIN ((OI_UINT16)0) /**< decimal value: 0 */ +#define OI_UINT16_MAX ((OI_UINT16)0xFFFF) /**< decimal value: 65535 */ +#define OI_UINT32_MIN ((OI_UINT32)0) /**< decimal value: 0 */ +#define OI_UINT32_MAX ((OI_UINT32)0xFFFFFFFF) /**< decimal value: 4,294,967,295 */ + +/** + * @} + */ + +/** + * @name Integer types required by the Service Discovery Protocol + * @{ + */ + +/** unsigned 64-bit integer as a structure of two unsigned 32-bit integers */ +typedef struct { + OI_UINT32 I1; /**< most significant 32 bits */ + OI_UINT32 I2; /**< least significant 32 bits */ +} OI_UINT64; + +#define OI_UINT64_MIN { (OI_UINT32)0x00000000, (OI_UINT32)0x00000000 } +#define OI_UINT64_MAX { (OI_UINT32)0XFFFFFFFF, (OI_UINT32)0XFFFFFFFF } + +/** signed 64-bit integer as a structure of one unsigned 32-bit integer and one signed 32-bit integer */ +typedef struct { + OI_INT32 I1; /**< most significant 32 bits as a signed integer */ + OI_UINT32 I2; /**< least significant 32 bits as an unsigned integer */ +} OI_INT64; + +#define OI_INT64_MIN { (OI_INT32)0x80000000, (OI_UINT32)0x00000000 } +#define OI_INT64_MAX { (OI_INT32)0X7FFFFFFF, (OI_UINT32)0XFFFFFFFF } + +/** unsigned 128-bit integer as a structure of four unsigned 32-bit integers */ +typedef struct { + OI_UINT32 I1; /**< most significant 32 bits */ + OI_UINT32 I2; /**< second-most significant 32 bits */ + OI_UINT32 I3; /**< third-most significant 32 bits */ + OI_UINT32 I4; /**< least significant 32 bits */ +} OI_UINT128; + +#define OI_UINT128_MIN { (OI_UINT32)0x00000000, (OI_UINT32)0x00000000, (OI_UINT32)0x00000000, (OI_UINT32)0x00000000 } +#define OI_UINT128_MAX { (OI_UINT32)0XFFFFFFFF, (OI_UINT32)0XFFFFFFFF, (OI_UINT32)0XFFFFFFFF, (OI_UINT32)0XFFFFFFFF } + +/** signed 128-bit integer as a structure of three unsigned 32-bit integers and one signed 32-bit integer */ +typedef struct { + OI_INT32 I1; /**< most significant 32 bits as a signed integer */ + OI_UINT32 I2; /**< second-most significant 32 bits as an unsigned integer */ + OI_UINT32 I3; /**< third-most significant 32 bits as an unsigned integer */ + OI_UINT32 I4; /**< least significant 32 bits as an unsigned integer */ +} OI_INT128; + +#define OI_INT128_MIN { (OI_UINT32)0x80000000, (OI_UINT32)0x00000000, (OI_UINT32)0x00000000, (OI_UINT32)0x00000000 } +#define OI_INT128_MAX { (OI_UINT32)0X7FFFFFFF, (OI_UINT32)0XFFFFFFFF, (OI_UINT32)0XFFFFFFFF, (OI_UINT32)0XFFFFFFFF } + +/** + * @} + */ + + +/** + * type for ASCII character data items + */ +typedef char OI_CHAR; + +/** + * type for double-byte character data items + */ +typedef OI_UINT16 OI_CHAR16; + +/** + * types for UTF encoded strings. + */ +typedef OI_UINT8 OI_UTF8; +typedef OI_UINT16 OI_UTF16; +typedef OI_UINT32 OI_UTF32; + + +/** + * @name Single-bit operation macros + * @{ + * In these macros, x is the data item for which a bit is to be tested or set and y specifies which bit + * is to be tested or set. + */ + +/** This macro's value is TRUE if the bit specified by y is set in data item x. */ +#define OI_BIT_TEST(x,y) ((x) & (y)) + +/** This macro's value is TRUE if the bit specified by y is not set in data item x. */ +#define OI_BIT_CLEAR_TEST(x,y) (((x) & (y)) == 0) + +/** This macro sets the bit specified by y in data item x. */ +#define OI_BIT_SET(x,y) ((x) |= (y)) + +/** This macro clears the bit specified by y in data item x. */ +#define OI_BIT_CLEAR(x,y) ((x) &= ~(y)) + +/** @} */ + +/** + * The OI_ARRAYSIZE macro is set to the number of elements in an array + * (instead of the number of bytes, which is returned by sizeof()). + */ + +#ifndef OI_ARRAYSIZE +#define OI_ARRAYSIZE(a) (sizeof(a)/sizeof(a[0])) +#endif + +/** + * @name Preprocessor aliases for individual bit positions + * Bits are defined here only if they are not already defined. + * @{ + */ + +#ifndef BIT0 + +#define BIT0 0x00000001 /**< preprocessor alias for 32-bit value with bit 0 set, used to specify this single bit */ +#define BIT1 0x00000002 /**< preprocessor alias for 32-bit value with bit 1 set, used to specify this single bit */ +#define BIT2 0x00000004 /**< preprocessor alias for 32-bit value with bit 2 set, used to specify this single bit */ +#define BIT3 0x00000008 /**< preprocessor alias for 32-bit value with bit 3 set, used to specify this single bit */ +#define BIT4 0x00000010 /**< preprocessor alias for 32-bit value with bit 4 set, used to specify this single bit */ +#define BIT5 0x00000020 /**< preprocessor alias for 32-bit value with bit 5 set, used to specify this single bit */ +#define BIT6 0x00000040 /**< preprocessor alias for 32-bit value with bit 6 set, used to specify this single bit */ +#define BIT7 0x00000080 /**< preprocessor alias for 32-bit value with bit 7 set, used to specify this single bit */ +#define BIT8 0x00000100 /**< preprocessor alias for 32-bit value with bit 8 set, used to specify this single bit */ +#define BIT9 0x00000200 /**< preprocessor alias for 32-bit value with bit 9 set, used to specify this single bit */ +#define BIT10 0x00000400 /**< preprocessor alias for 32-bit value with bit 10 set, used to specify this single bit */ +#define BIT11 0x00000800 /**< preprocessor alias for 32-bit value with bit 11 set, used to specify this single bit */ +#define BIT12 0x00001000 /**< preprocessor alias for 32-bit value with bit 12 set, used to specify this single bit */ +#define BIT13 0x00002000 /**< preprocessor alias for 32-bit value with bit 13 set, used to specify this single bit */ +#define BIT14 0x00004000 /**< preprocessor alias for 32-bit value with bit 14 set, used to specify this single bit */ +#define BIT15 0x00008000 /**< preprocessor alias for 32-bit value with bit 15 set, used to specify this single bit */ +#define BIT16 0x00010000 /**< preprocessor alias for 32-bit value with bit 16 set, used to specify this single bit */ +#define BIT17 0x00020000 /**< preprocessor alias for 32-bit value with bit 17 set, used to specify this single bit */ +#define BIT18 0x00040000 /**< preprocessor alias for 32-bit value with bit 18 set, used to specify this single bit */ +#define BIT19 0x00080000 /**< preprocessor alias for 32-bit value with bit 19 set, used to specify this single bit */ +#define BIT20 0x00100000 /**< preprocessor alias for 32-bit value with bit 20 set, used to specify this single bit */ +#define BIT21 0x00200000 /**< preprocessor alias for 32-bit value with bit 21 set, used to specify this single bit */ +#define BIT22 0x00400000 /**< preprocessor alias for 32-bit value with bit 22 set, used to specify this single bit */ +#define BIT23 0x00800000 /**< preprocessor alias for 32-bit value with bit 23 set, used to specify this single bit */ +#define BIT24 0x01000000 /**< preprocessor alias for 32-bit value with bit 24 set, used to specify this single bit */ +#define BIT25 0x02000000 /**< preprocessor alias for 32-bit value with bit 25 set, used to specify this single bit */ +#define BIT26 0x04000000 /**< preprocessor alias for 32-bit value with bit 26 set, used to specify this single bit */ +#define BIT27 0x08000000 /**< preprocessor alias for 32-bit value with bit 27 set, used to specify this single bit */ +#define BIT28 0x10000000 /**< preprocessor alias for 32-bit value with bit 28 set, used to specify this single bit */ +#define BIT29 0x20000000 /**< preprocessor alias for 32-bit value with bit 29 set, used to specify this single bit */ +#define BIT30 0x40000000 /**< preprocessor alias for 32-bit value with bit 30 set, used to specify this single bit */ +#define BIT31 0x80000000 /**< preprocessor alias for 32-bit value with bit 31 set, used to specify this single bit */ + +#endif /* BIT0 et al */ + + +/** @} */ + + +#ifdef __cplusplus +} +#endif + +/**@}*/ + +/*****************************************************************************/ +#endif /* OI_STDDEFS_H */ diff --git a/lib/bt/host/bluedroid/external/sbc/decoder/include/oi_string.h b/lib/bt/host/bluedroid/external/sbc/decoder/include/oi_string.h new file mode 100644 index 00000000..06768bd1 --- /dev/null +++ b/lib/bt/host/bluedroid/external/sbc/decoder/include/oi_string.h @@ -0,0 +1,207 @@ +/****************************************************************************** + * + * Copyright (C) 2014 The Android Open Source Project + * Copyright 2002 - 2004 Open Interface North America, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ +#ifndef OI_STRING_H +#define OI_STRING_H +/** + * @file + * This file contains BM3 supplied portable string.h functions + * + */ + +/********************************************************************************** + $Revision: #1 $ +***********************************************************************************/ + +#include "oi_cpu_dep.h" +#include "oi_stddefs.h" + +#if defined(USE_NATIVE_MEMCPY) || defined(USE_NATIVE_MALLOC) +#include +#endif + +/** \addtogroup Misc Miscellaneous APIs */ +/**@{*/ + +#ifdef __cplusplus +extern "C" { +#endif + + +/* + * If we are using Native malloc(), we must also use + * native Ansi string.h functions for memory manipulation. + */ +#ifdef USE_NATIVE_MALLOC +#ifndef USE_NATIVE_MEMCPY +#define USE_NATIVE_MEMCPY +#endif +#endif + +#ifdef USE_NATIVE_MEMCPY + +#define OI_MemCopy(to, from, size) memcpy((to), (from), (size)) +#define OI_MemSet(block, val, size) memset((block), (val), (size)) +#define OI_MemZero(block, size) memset((block), 0, (size)) +#define OI_MemCmp(s1, s2, n) memcmp((s1), (s2), (n)) +#define OI_Strcpy(dest, src) strcpy((dest),(src)) +#define OI_Strcat(dest, src) strcat((dest),(src)) +#define OI_StrLen(str) strlen((str)) +#define OI_Strcmp(s1, s2) strcmp((s1), (s2)) +#define OI_Strncmp(s1, s2, n) strncmp((s1), (s2), (n)) + +#else + +/* + * OI_MemCopy + * + * Copy an arbitrary number of bytes from one memory address to another. + * The underlying implementation is the ANSI memmove() or equivalant, so + * overlapping memory copies will work correctly. + */ +void OI_MemCopy(void *To, void const *From, OI_UINT32 Size); + + +/* + * OI_MemSet + * + * Sets all bytes in a block of memory to the same value + */ +void OI_MemSet(void *Block, OI_UINT8 Val, OI_UINT32 Size); + + +/* + * OI_MemZero + * + * Sets all bytes in a block of memory to zero + */ +void OI_MemZero(void *Block, OI_UINT32 Size); + + +/* + * OI_MemCmp + * + * Compare two blocks of memory + * + * Returns: + * 0, if s1 == s2 + * < 0, if s1 < s2 + * > 0, if s2 > s2 + */ +OI_INT OI_MemCmp(void const *s1, void const *s2, OI_UINT32 n); + +/* + * OI_Strcpy + * + * Copies the Null terminated string from pStr to pDest, and + * returns pDest. + */ + +OI_CHAR *OI_Strcpy(OI_CHAR *pDest, + OI_CHAR const *pStr); + +/* + * OI_Strcat + * + * Concatonates the pStr string to the end of pDest, and + * returns pDest. + */ + +OI_CHAR *OI_Strcat(OI_CHAR *pDest, + OI_CHAR const *pStr) ; + +/* + * OI_StrLen + * + * Calculates the number of OI_CHARs in pStr (not including + * the Null terminator) and returns the value. + */ +OI_UINT OI_StrLen(OI_CHAR const *pStr) ; + +/* + * OI_Strcmp + * + * Compares two Null terminated strings + * + * Returns: + * 0, if s1 == s2 + * < 0, if s1 < s2 + * > 0, if s2 > s2 + */ +OI_INT OI_Strcmp(OI_CHAR const *s1, + OI_CHAR const *s2); + +/* + * OI_Strncmp + * + * Compares the first "len" OI_CHARs of strings s1 and s2. + * + * Returns: + * 0, if s1 == s2 + * < 0, if s1 < s2 + * > 0, if s2 > s2 + */ +OI_INT OI_Strncmp(OI_CHAR const *s1, + OI_CHAR const *s2, + OI_UINT32 len); + + +#endif /* USE_NATIVE_MEMCPY */ + +/* + * OI_StrcmpInsensitive + * + * Compares two Null terminated strings, treating + * the Upper and Lower case of 'A' through 'Z' as + * equivilent. + * + * Returns: + * 0, if s1 == s2 + * < 0, if s1 < s2 + * > 0, if s2 > s2 + */ +OI_INT OI_StrcmpInsensitive(OI_CHAR const *s1, + OI_CHAR const *s2); + +/* + * OI_StrncmpInsensitive + * + * Compares the first "len" OI_CHARs of strings s1 and s2, + * treating the Upper and Lower case of 'A' through 'Z' as + * equivilent. + * + * + * Returns: + * 0, if s1 == s2 + * < 0, if s1 < s2 + * > 0, if s2 > s2 + */ +OI_INT OI_StrncmpInsensitive(OI_CHAR const *s1, + OI_CHAR const *s2, + OI_UINT len); + + + +#ifdef __cplusplus +} +#endif + +/** @} */ + +/*****************************************************************************/ +#endif /* OI_STRING_H */ diff --git a/lib/bt/host/bluedroid/external/sbc/decoder/include/oi_time.h b/lib/bt/host/bluedroid/external/sbc/decoder/include/oi_time.h new file mode 100644 index 00000000..376e6842 --- /dev/null +++ b/lib/bt/host/bluedroid/external/sbc/decoder/include/oi_time.h @@ -0,0 +1,199 @@ +/****************************************************************************** + * + * Copyright (C) 2014 The Android Open Source Project + * Copyright 2002 - 2004 Open Interface North America, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ +#ifndef _OI_TIME_H +#define _OI_TIME_H +/** @file + * + * This file provides time type definitions and interfaces to time-related functions. + * + * The stack maintains a 64-bit real-time millisecond clock. The choice of + * milliseconds is for convenience, not accuracy. + * + * Timeouts are specified as tenths of seconds in a 32-bit value. Timeout values + * specified by the Bluetooth specification are usually muliple seconds, so + * accuracy to a tenth of a second is more than adequate. + * + * This file also contains macros to convert between seconds and the Link + * Manager's 1.28-second units. + * + */ + +/********************************************************************************** + $Revision: #1 $ +***********************************************************************************/ + +#include "oi_stddefs.h" + + +/** \addtogroup Misc Miscellaneous APIs */ +/**@{*/ + +#ifdef __cplusplus +extern "C" { +#endif + + + +/** + * Within the core stack timeouts are specified in intervals of tenths of seconds + */ + +typedef OI_UINT16 OI_INTERVAL; +#define OI_INTERVALS_PER_SECOND 10 +#define MSECS_PER_OI_INTERVAL (1000 / OI_INTERVALS_PER_SECOND) + +/** maximum interval (54 min 36.7 sec) */ +#define OI_MAX_INTERVAL 0x7fff + + +/** + * Macro to convert seconds to OI_INTERVAL time units + */ + +#define OI_SECONDS(n) ((OI_INTERVAL) ((n) * OI_INTERVALS_PER_SECOND)) + +/** + * Macro to convert milliseconds to OI_INTERVAL time units (Rounded Up) + */ + +#define OI_MSECONDS(n) ((OI_INTERVAL) ((n + MSECS_PER_OI_INTERVAL - 1) / MSECS_PER_OI_INTERVAL)) + +/** + * Macro to convert minutes to OI_INTERVAL time units + */ + +#define OI_MINUTES(n) ((OI_INTERVAL) ((n) * OI_SECONDS(60))) + +/** Convert an OI_INTERVAL to milliseconds. */ +#define OI_INTERVAL_TO_MILLISECONDS(i) ((i) * MSECS_PER_OI_INTERVAL) + +/** + * The stack depends on relative not absolute time. Any mapping between the + * stack's real-time clock and absolute time and date is implementation-dependent. + */ + +typedef struct { + OI_INT32 seconds; + OI_INT16 mseconds; +} OI_TIME; + +/** + * Convert an OI_TIME to milliseconds. + * + * @param t the time to convert + * + * @return the time in milliseconds + */ +OI_UINT32 OI_Time_ToMS(OI_TIME *t); + + +/** + * This function compares two time values. + * + * @param T1 first time to compare. + * + * @param T2 second time to compare. + * + * @return + @verbatim + -1 if t1 < t2 + 0 if t1 = t2 + +1 if t1 > t2 + @endverbatim + */ + +OI_INT16 OI_Time_Compare(OI_TIME *T1, + OI_TIME *T2); + + +/** + * This function returns the interval between two times to a granularity of 0.1 seconds. + * + * @param Sooner a time value more recent that Later + * + * @param Later a time value later than Sooner + * + * @note The result is an OI_INTERVAL value so this function only works for time intervals + * that are less than about 71 minutes. + * + * @return the time interval between the two times = (Later - Sooner) + */ + +OI_INTERVAL OI_Time_Interval(OI_TIME *Sooner, + OI_TIME *Later); + + + +/** + * This function returns the interval between two times to a granularity of milliseconds. + * + * @param Sooner a time value more recent that Later + * + * @param Later a time value later than Sooner + * + * @note The result is an OI_UINT32 value so this function only works for time intervals + * that are less than about 50 days. + * + * @return the time interval between the two times = (Later - Sooner) + */ + +OI_UINT32 OI_Time_IntervalMsecs(OI_TIME *Sooner, + OI_TIME *Later); + + + +/** + * This function answers the question, Have we reached or gone past the target time? + * + * @param pTargetTime target time + * + * @return TRUE means time now is at or past target time + * FALSE means target time is still some time in the future + */ + +OI_BOOL OI_Time_NowReachedTime(OI_TIME *pTargetTime); + +/** + * Convert seconds to the Link Manager 1.28-second units + * Approximate by using 1.25 conversion factor. + */ + +#define OI_SECONDS_TO_LM_TIME_UNITS(lmUnits) ((lmUnits)<4?(lmUnits):(lmUnits)-((lmUnits)>>2)) + + +/** + * Convert Link Manager 1.28-second units to seconds. + * Approximate by using 1.25 conversion factor. + */ + +#define OI_LM_TIME_UNITS_TO_SECONDS(lmUnits) ((lmUnits) + ((lmUnits)>>2)) + +#ifdef __cplusplus +} +#endif + +/**@}*/ + +/* Include for OI_Time_Now() prototype + * Must be included at end to obtain OI_TIME typedef + */ +#include "oi_osinterface.h" + +/*****************************************************************************/ +#endif /* _OI_TIME_H */ diff --git a/lib/bt/host/bluedroid/external/sbc/decoder/include/oi_utils.h b/lib/bt/host/bluedroid/external/sbc/decoder/include/oi_utils.h new file mode 100644 index 00000000..d7e753b0 --- /dev/null +++ b/lib/bt/host/bluedroid/external/sbc/decoder/include/oi_utils.h @@ -0,0 +1,376 @@ +/****************************************************************************** + * + * Copyright (C) 2014 The Android Open Source Project + * Copyright 2002 - 2004 Open Interface North America, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ +#ifndef _OI_UTILS_H +#define _OI_UTILS_H +/** + * @file + * + * This file provides the interface for utility functions. + * Among the utilities are strlen (string length), strcmp (string compare), and + * other string manipulation functions. These are provided for those plaforms + * where this functionality is not available in stdlib. + */ + +/********************************************************************************** + $Revision: #1 $ +***********************************************************************************/ + +#include +#include "oi_common.h" +#include "oi_string.h" +#include "oi_bt_spec.h" + +/** \addtogroup Misc Miscellaneous APIs */ +/**@{*/ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Opaque type for a callback function handle. See OI_ScheduleCallbackFunction() + */ +typedef OI_UINT32 OI_CALLBACK_HANDLE; + + +/** + * Function prototype for a timed procedure callback. + * + * @param arg Value that was passed into the OI_ScheduleCallback() function + * + */ +typedef void (*OI_SCHEDULED_CALLBACK)(void *arg); + + +/** + * Registers a function to be called when a timeout expires. This API uses BLUEmagic's internal + * function dispatch mechanism, so applications that make extensive use of this facility may need to + * increase the value of DispatchTableSize in the configuration block for the dispatcher (see + * oi_bt_stack_config.h). + * + * @param callbackFunction The function that will be called when the timeout expires + * + * @param arg Value that will be returned as the parameter to the callback function. + * + * @param timeout A timeout expressed in OI_INTERVALs (tenths of seconds). This can be + * zero in which case the callback function will be called as soon as + * possible. + * + * @param handle NULL or a pointer receive the callback handle. + * + * @return OI_OK if the function was reqistered, or an error status. + */ +OI_STATUS OI_ScheduleCallbackFunction(OI_SCHEDULED_CALLBACK callbackFunction, + void *arg, + OI_INTERVAL timeout, + OI_CALLBACK_HANDLE *handle); + + +/** + * Cancels a function registered with OI_ScheduleCallbackFunction() before its timer expires. + * + * @param handle handle returned by OI_ScheduleCallbackFunction(). + * + * @return OI_OK if the function was cancelled, or an error status. + */ +OI_STATUS OI_CancelCallbackFunction(OI_CALLBACK_HANDLE handle); + + +/** + * Registers a function to be called when a timeout expires. This version does not return a handle + * so can only be canceled by calling OI_CancelCallback(). + * + * @param callbackFunction The function that will be called when the timeout expires + * + * @param arg Value that will be returned as the parameter to the callback function. + * + * @param timeout A timeout expressed in OI_INTERVALs (tenths of seconds). This can be + * zero in which case the callback function will be called as soon as + * possible. + * + * @return OI_OK if the function was reqistered, or an error status. + */ +#define OI_ScheduleCallback(f, a, t) OI_ScheduleCallbackFunction(f, a, t, NULL); + + +/** + * Cancels a function registered with OI_ScheduleCallback() before its timer expires. This + * function will cancel the first entry matches the indicated callback function pointer. + * + * @param callbackFunction The function that was originally registered + * + * @return OI_OK if the function was cancelled, or an error status. + */ +OI_STATUS OI_CancelCallback(OI_SCHEDULED_CALLBACK callbackFunction); + + +/** + * Parse a Bluetooth device address from the specified string. + * + * @param str the string to parse + * @param addr the parsed address, if successful + * + * @return TRUE if an address was successfully parsed, FALSE otherwise + */ + +OI_BOOL OI_ParseBdAddr(const OI_CHAR *str, + OI_BD_ADDR *addr) ; + +/** + * Printf function for platforms which have no stdio or printf available. + * OI_Printf supports the basic formatting types, with the exception of + * floating point types. Additionally, OI_Printf supports several formats + * specific to BLUEmagic 3.0 software: + * + * \%! prints the string for an #OI_STATUS value. + * @code OI_Printf("There was an error %!", status); @endcode + * + * \%@ prints a hex dump of a buffer. + * Requires a pointer to the buffer and a signed integer length + * (0 for default length). If the buffer is large, only an excerpt will + * be printed. + * @code OI_Printf("Contents of buffer %@", buffer, sizeof(buffer)); @endcode + * + * \%: prints a Bluetooth address in the form "HH:HH:HH:HH:HH:HH". + * Requires a pointer to an #OI_BD_ADDR. + * @code OI_Printf("Bluetooth address %:", &bdaddr); @endcode + * + * \%^ decodes and prints a data element as formatted XML. + * Requires a pointer to an #OI_DATAELEM. + * @code OI_Printf("Service attribute list is:\n%^", &attributes); @endcode + * + * \%/ prints the base file name of a path, that is, the final substring + * following a '/' or '\\' character. Requires a pointer to a null + * terminated string. + * @code OI_Printf("File %/", "c:\\dir1\\dir2\\file.txt"); @endcode + * + * \%~ prints a string, escaping characters as needed to display it in + * ASCII. Requires a pointer to an #OI_PSTR and an #OI_UNICODE_ENCODING + * parameter. + * @code OI_Printf("Identifier %~", &id, OI_UNICODE_UTF16_BE); @endcode + * + * \%[ inserts an ANSI color escape sequence. Requires a single character + * identifying the color to select. Colors are red (r/R), green (g/G), + * blue (b/B), yellow (y/Y), cyan (c/C), magenta (m/M), white (W), + * light-gray (l/L), dark-gray (d/D), and black (0). The lower case is + * dim, the upper case is bright (except in the case of light-gray and + * dark-gray, where bright and dim are identical). Any other value will + * select the default color. + * @code OI_Printf("%[red text %[black %[normal\n", 'r', '0', 0); @endcode + * + * \%a same as \%s, except '\\r' and '\\n' are output as "" and "". + * \%?a is valid, but \%la is not. + * + * \%b prints an integer in base 2. + * @code OI_Printf("Bits are %b", I); @endcode + * + * \%lb prints a long integer in base 2. + * + * \%?b prints the least significant N bits of an integer (or long integer) + * in base 2. Requires the integer and a length N. + * @code OI_Printf("Bottom 4 bits are: %?b", I, 4); @endcode + * + * \%B prints an integer as boolean text, "TRUE" or "FALSE". + * @code OI_Printf("The value 0 is %B, the value 1 is %B", 0, 1); @endcode + * + * \%?s prints a substring up to a specified maximum length. + * Requires a pointer to a string and a length parameter. + * @code OI_Printf("String prefix is %?s", str, 3); @endcode + * + * \%ls same as \%S. + * + * \%S prints a UTF16 string as UTF8 (plain ASCII, plus 8-bit char sequences + * where needed). Requires a pointer to #OI_CHAR16. \%?S is valid. The + * length parameter is in OI_CHAR16 characters. + * + * \%T prints time, formatted as "secs.msecs". + * Requires pointer to #OI_TIME struct, NULL pointer prints current time. + * @code OI_Printf("The time now is %T", NULL); @endcode + * + * @param format The format string + * + */ +void OI_Printf(const OI_CHAR *format, ...); + + +/** + * Var-args version OI_Printf + * + * @param format Same as for OI_Printf. + * + * @param argp Var-args list. + */ +void OI_VPrintf(const OI_CHAR *format, va_list argp); + + +/** + * Writes a formatted string to a buffer. This function supports the same format specifiers as + * OI_Printf(). + * + * @param buffer Destination buffer for the formatted string. + * + * @param bufLen The length of the destination buffer. + * + * @param format The format string + * + * @return Number of characters written or -1 in the case of an error. + */ +OI_INT32 OI_SNPrintf(OI_CHAR *buffer, + OI_UINT16 bufLen, + const OI_CHAR *format, ...); + + +/** + * Var-args version OI_SNPrintf + * + * @param buffer Destination buffer for the formatted string. + * + * @param bufLen The length of the destination buffer. + * + * @param format The format string + * + * @param argp Var-args list. + * + * @return Number of characters written or -1 in the case of an error. + */ +OI_INT32 OI_VSNPrintf(OI_CHAR *buffer, + OI_UINT16 bufLen, + const OI_CHAR *format, va_list argp); + + +/** + * Convert a string to an integer. + * + * @param str the string to parse + * + * @return the integer value of the string or 0 if the string could not be parsed + */ +OI_INT OI_atoi(const OI_CHAR *str); + + +/** + * Parse a signed integer in a string. + * + * Skips leading whitespace (space and tabs only) and parses a decimal or hex string. Hex string + * must be prefixed by "0x". Returns pointer to first character following the integer. Returns the + * pointer passed in if the string does not describe an integer. + * + * @param str String to parse. + * + * @param val Pointer to receive the parsed integer value. + * + * @return A pointer to the first character following the integer or the pointer passed in. + */ +const OI_CHAR *OI_ScanInt(const OI_CHAR *str, + OI_INT32 *val); + + +/** + * Parse an unsigned integer in a string. + * + * Skips leading whitespace (space and tabs only) and parses a decimal or hex string. Hex string + * must be prefixed by "0x". Returns pointer to first character following the integer. Returns the + * pointer passed in if the string does not describe an integer. + * + * @param str String to parse. + * + * @param val Pointer to receive the parsed unsigned integer value. + * + * @return A pointer to the first character following the unsigned integer or the pointer passed in. + */ +const OI_CHAR *OI_ScanUInt(const OI_CHAR *str, + OI_UINT32 *val); + +/** + * Parse a whitespace delimited substring out of a string. + * + * @param str Input string to parse. + * @param outStr Buffer to return the substring + * @param len Length of outStr + * + * + * @return A pointer to the first character following the substring or the pointer passed in. + */ +const OI_CHAR *OI_ScanStr(const OI_CHAR *str, + OI_CHAR *outStr, + OI_UINT16 len); + + +/** + * Parse a string for one of a set of alternative value. Skips leading whitespace (space and tabs + * only) and parses text matching one of the alternative strings. Returns pointer to first character + * following the matched text. + * + * @param str String to parse. + * + * @param alts Alternative matching strings separated by '|' + * + * @param index Pointer to receive the index of the matching alternative, return value is -1 if + * there is no match. + * + * @return A pointer to the first character following the matched value or the pointer passed in + * if there was no matching text. + */ +const OI_CHAR *OI_ScanAlt(const OI_CHAR *str, + const OI_CHAR *alts, + OI_INT *index); + +/** + * Parse a string for a BD Addr. Skips leading whitespace (space and tabs only) and parses a + * Bluetooth device address with nibbles optionally separated by colons. Return pointet to first + * character following the BD Addr. + * + * @param str String to parse. + * + * @param addr Pointer to receive the Bluetooth device address + * + * @return A pointer to the first character following the BD Addr or the pointer passed in. + */ +const OI_CHAR *OI_ScanBdAddr(const OI_CHAR *str, + OI_BD_ADDR *addr); + + +/** Get a character from a digit integer value (0 - 9). */ +#define OI_DigitToChar(d) ((d) + '0') + +/** + * Determine Maximum and Minimum between two arguments. + * + * @param a 1st value + * @param b 2nd value + * + * @return the max or min value between a & b + */ +#define OI_MAX(a, b) (((a) < (b)) ? (b) : (a) ) +#define OI_MIN(a, b) (((a) > (b)) ? (b) : (a) ) + +/** + * Compare two BD_ADDRs + * SAME_BD_ADDR - Boolean: TRUE if they are the same address + */ + +#define SAME_BD_ADDR(x, y) (0 == OI_MemCmp((x),(y),OI_BD_ADDR_BYTE_SIZE) ) + +#ifdef __cplusplus +} +#endif + +/**@}*/ + +#endif /* _OI_UTILS_H */ diff --git a/lib/bt/host/bluedroid/external/sbc/decoder/srce/alloc.c b/lib/bt/host/bluedroid/external/sbc/decoder/srce/alloc.c new file mode 100644 index 00000000..3ce6dd7a --- /dev/null +++ b/lib/bt/host/bluedroid/external/sbc/decoder/srce/alloc.c @@ -0,0 +1,82 @@ +/****************************************************************************** + * + * Copyright (C) 2014 The Android Open Source Project + * Copyright 2003 - 2004 Open Interface North America, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ +#include "common/bt_target.h" +#include +#include + +#if (defined(SBC_DEC_INCLUDED) && SBC_DEC_INCLUDED == TRUE) + +/********************************************************************************** + $Revision: #1 $ +***********************************************************************************/ + +PRIVATE OI_STATUS OI_CODEC_SBC_Alloc(OI_CODEC_SBC_COMMON_CONTEXT *common, + OI_UINT32 *codecDataAligned, + OI_UINT32 codecDataBytes, + OI_UINT8 maxChannels, + OI_UINT8 pcmStride) +{ + int i; + size_t filterBufferCount; + size_t subdataSize; + OI_BYTE *codecData = (OI_BYTE *)codecDataAligned; + + if (maxChannels < 1 || maxChannels > 2) { + return OI_STATUS_INVALID_PARAMETERS; + } + + if (pcmStride < 1 || pcmStride > maxChannels) { + return OI_STATUS_INVALID_PARAMETERS; + } + + common->maxChannels = maxChannels; + common->pcmStride = pcmStride; + + /* Compute sizes needed for the memory regions, and bail if we don't have + * enough memory for them. */ + subdataSize = maxChannels * sizeof(common->subdata[0]) * SBC_MAX_BANDS * SBC_MAX_BLOCKS; + if (subdataSize > codecDataBytes) { + return OI_STATUS_OUT_OF_MEMORY; + } + + filterBufferCount = (codecDataBytes - subdataSize) / (sizeof(common->filterBuffer[0][0]) * SBC_MAX_BANDS * maxChannels); + if (filterBufferCount < SBC_CODEC_MIN_FILTER_BUFFERS) { + return OI_STATUS_OUT_OF_MEMORY; + } + common->filterBufferLen = filterBufferCount * SBC_MAX_BANDS; + + /* Allocate memory for the subband data */ + common->subdata = (OI_INT32 *)codecData; + codecData += subdataSize; + OI_ASSERT(codecDataBytes >= subdataSize); + codecDataBytes -= subdataSize; + + /* Allocate memory for the synthesis buffers */ + for (i = 0; i < maxChannels; ++i) { + size_t allocSize = common->filterBufferLen * sizeof(common->filterBuffer[0][0]); + common->filterBuffer[i] = (SBC_BUFFER_T *)codecData; + OI_ASSERT(codecDataBytes >= allocSize); + codecData += allocSize; + codecDataBytes -= allocSize; + } + + return OI_OK; +} + +#endif /* #if (defined(SBC_DEC_INCLUDED) && SBC_DEC_INCLUDED == TRUE) */ diff --git a/lib/bt/host/bluedroid/external/sbc/decoder/srce/bitalloc-sbc.c b/lib/bt/host/bluedroid/external/sbc/decoder/srce/bitalloc-sbc.c new file mode 100644 index 00000000..66521630 --- /dev/null +++ b/lib/bt/host/bluedroid/external/sbc/decoder/srce/bitalloc-sbc.c @@ -0,0 +1,168 @@ +/****************************************************************************** + * + * Copyright (C) 2014 The Android Open Source Project + * Copyright 2003 - 2004 Open Interface North America, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/********************************************************************************** + $Revision: #1 $ +***********************************************************************************/ + +/** @file +@ingroup codec_internal +*/ + +/**@addgroup codec_internal*/ +/**@{*/ +#include "common/bt_target.h" +#include + +#if (defined(SBC_DEC_INCLUDED) && SBC_DEC_INCLUDED == TRUE) + +static void dualBitAllocation(OI_CODEC_SBC_COMMON_CONTEXT *common) +{ + OI_UINT bitcountL; + OI_UINT bitcountR; + OI_UINT bitpoolPreferenceL = 0; + OI_UINT bitpoolPreferenceR = 0; + BITNEED_UNION1 bitneedsL; + BITNEED_UNION1 bitneedsR; + + bitcountL = computeBitneed(common, bitneedsL.uint8, 0, &bitpoolPreferenceL); + bitcountR = computeBitneed(common, bitneedsR.uint8, 1, &bitpoolPreferenceR); + + oneChannelBitAllocation(common, &bitneedsL, 0, bitcountL); + oneChannelBitAllocation(common, &bitneedsR, 1, bitcountR); +} + +static void stereoBitAllocation(OI_CODEC_SBC_COMMON_CONTEXT *common) +{ + const OI_UINT nrof_subbands = common->frameInfo.nrof_subbands; + BITNEED_UNION2 bitneeds; + OI_UINT excess; + OI_INT bitadjust; + OI_UINT bitcount; + OI_UINT sbL; + OI_UINT sbR; + OI_UINT bitpoolPreference = 0; + + bitcount = computeBitneed(common, &bitneeds.uint8[0], 0, &bitpoolPreference); + bitcount += computeBitneed(common, &bitneeds.uint8[nrof_subbands], 1, &bitpoolPreference); + + { + OI_UINT ex; + bitadjust = adjustToFitBitpool(common->frameInfo.bitpool, bitneeds.uint32, 2 * nrof_subbands, bitcount, &ex); + /* We want the compiler to put excess into a register */ + excess = ex; + } + sbL = 0; + sbR = nrof_subbands; + while (sbL < nrof_subbands) { + excess = allocAdjustedBits(&common->bits.uint8[sbL], bitneeds.uint8[sbL] + bitadjust, excess); + ++sbL; + excess = allocAdjustedBits(&common->bits.uint8[sbR], bitneeds.uint8[sbR] + bitadjust, excess); + ++sbR; + } + sbL = 0; + sbR = nrof_subbands; + while (excess) { + excess = allocExcessBits(&common->bits.uint8[sbL], excess); + ++sbL; + if (!excess) { + break; + } + excess = allocExcessBits(&common->bits.uint8[sbR], excess); + ++sbR; + } + +} + +static const BIT_ALLOC balloc[] = { + monoBitAllocation, /* SBC_MONO */ + dualBitAllocation, /* SBC_DUAL_CHANNEL */ + stereoBitAllocation, /* SBC_STEREO */ + stereoBitAllocation /* SBC_JOINT_STEREO */ +}; + + +PRIVATE void OI_SBC_ComputeBitAllocation(OI_CODEC_SBC_COMMON_CONTEXT *common) +{ + OI_ASSERT(common->frameInfo.bitpool <= OI_SBC_MaxBitpool(&common->frameInfo)); + OI_ASSERT(common->frameInfo.mode < OI_ARRAYSIZE(balloc)); + + /* + * Using an array of function pointers prevents the compiler from creating a suboptimal + * monolithic inlined bit allocation function. + */ + balloc[common->frameInfo.mode](common); +} + +OI_UINT32 OI_CODEC_SBC_CalculateBitrate(OI_CODEC_SBC_FRAME_INFO *frame) +{ + return internal_CalculateBitrate(frame); +} + +/* + * Return the current maximum bitneed and clear it. + */ +OI_UINT8 OI_CODEC_SBC_GetMaxBitneed(OI_CODEC_SBC_COMMON_CONTEXT *common) +{ + OI_UINT8 max = common->maxBitneed; + + common->maxBitneed = 0; + return max; +} + +/* + * Calculates the bitpool size for a given frame length + */ +OI_UINT16 OI_CODEC_SBC_CalculateBitpool(OI_CODEC_SBC_FRAME_INFO *frame, + OI_UINT16 frameLen) +{ + OI_UINT16 nrof_subbands = frame->nrof_subbands; + OI_UINT16 nrof_blocks = frame->nrof_blocks; + OI_UINT16 hdr; + OI_UINT16 bits; + + if (frame->mode == SBC_JOINT_STEREO) { + hdr = 9 * nrof_subbands; + } else { + if (frame->mode == SBC_MONO) { + hdr = 4 * nrof_subbands; + } else { + hdr = 8 * nrof_subbands; + } + if (frame->mode == SBC_DUAL_CHANNEL) { + nrof_blocks *= 2; + } + } + bits = 8 * (frameLen - SBC_HEADER_LEN) - hdr; + return DIVIDE(bits, nrof_blocks); +} + +OI_UINT16 OI_CODEC_SBC_CalculatePcmBytes(OI_CODEC_SBC_COMMON_CONTEXT *common) +{ + return sizeof(OI_INT16) * common->pcmStride * common->frameInfo.nrof_subbands * common->frameInfo.nrof_blocks; +} + + +OI_UINT16 OI_CODEC_SBC_CalculateFramelen(OI_CODEC_SBC_FRAME_INFO *frame) +{ + return internal_CalculateFramelen(frame); +} + +/**@}*/ +#endif /* #if (defined(SBC_DEC_INCLUDED) && SBC_DEC_INCLUDED == TRUE) */ diff --git a/lib/bt/host/bluedroid/external/sbc/decoder/srce/bitalloc.c b/lib/bt/host/bluedroid/external/sbc/decoder/srce/bitalloc.c new file mode 100644 index 00000000..105876db --- /dev/null +++ b/lib/bt/host/bluedroid/external/sbc/decoder/srce/bitalloc.c @@ -0,0 +1,405 @@ +/****************************************************************************** + * + * Copyright (C) 2014 The Android Open Source Project + * Copyright 2003 - 2004 Open Interface North America, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/********************************************************************************** + $Revision: #1 $ + ***********************************************************************************/ + +/** +@file + +The functions in this file relate to the allocation of available bits to +subbands within the SBC/eSBC frame, along with support functions for computing +frame length and bitrate. + +@ingroup codec_internal +*/ + +/** +@addtogroup codec_internal +@{ +*/ + +#include "common/bt_target.h" +#include "oi_utils.h" +#include + +#if (defined(SBC_DEC_INCLUDED) && SBC_DEC_INCLUDED == TRUE) + +OI_UINT32 OI_SBC_MaxBitpool(OI_CODEC_SBC_FRAME_INFO *frame) +{ + switch (frame->mode) { + case SBC_MONO: + case SBC_DUAL_CHANNEL: + return 16 * frame->nrof_subbands; + case SBC_STEREO: + case SBC_JOINT_STEREO: + return 32 * frame->nrof_subbands; + } + + ERROR(("Invalid frame mode %d", frame->mode)); + OI_ASSERT(FALSE); + return 0; /* Should never be reached */ +} + + +PRIVATE OI_UINT16 internal_CalculateFramelen(OI_CODEC_SBC_FRAME_INFO *frame) +{ + OI_UINT16 nbits = frame->nrof_blocks * frame->bitpool; + OI_UINT16 nrof_subbands = frame->nrof_subbands; + OI_UINT16 result = nbits; + + if (frame->mode == SBC_JOINT_STEREO) { + result += nrof_subbands + (8 * nrof_subbands); + } else { + if (frame->mode == SBC_DUAL_CHANNEL) { + result += nbits; + } + if (frame->mode == SBC_MONO) { + result += 4 * nrof_subbands; + } else { + result += 8 * nrof_subbands; + } + } + return SBC_HEADER_LEN + (result + 7) / 8; +} + + +PRIVATE OI_UINT32 internal_CalculateBitrate(OI_CODEC_SBC_FRAME_INFO *frame) +{ + OI_UINT blocksbands; + blocksbands = frame->nrof_subbands * frame->nrof_blocks; + + return DIVIDE(8 * internal_CalculateFramelen(frame) * frame->frequency, blocksbands); +} + + +INLINE OI_UINT16 OI_SBC_CalculateFrameAndHeaderlen(OI_CODEC_SBC_FRAME_INFO *frame, OI_UINT *headerLen_) +{ + OI_UINT headerLen = SBC_HEADER_LEN + frame->nrof_subbands * frame->nrof_channels / 2; + + if (frame->mode == SBC_JOINT_STEREO) { + headerLen++; + } + + *headerLen_ = headerLen; + return internal_CalculateFramelen(frame); +} + + +#define MIN(x, y) ((x) < (y) ? (x) : (y)) + + +/* + * Computes the bit need for each sample and as also returns a counts of bit needs that are greater + * than one. This count is used in the first phase of bit allocation. + * + * We also compute a preferred bitpool value that this is the minimum bitpool needed to guarantee + * lossless representation of the audio data. The preferred bitpool may be larger than the bits + * actually required but the only input we have are the scale factors. For example, it takes 2 bits + * to represent values in the range -1 .. +1 but the scale factor is 0. To guarantee lossless + * representation we add 2 to each scale factor and sum them to come up with the preferred bitpool. + * This is not ideal because 0 requires 0 bits but we currently have no way of knowing this. + * + * @param bitneed Array to return bitneeds for each subband + * + * @param ch Channel 0 or 1 + * + * @param preferredBitpool Returns the number of reserved bits + * + * @return The SBC bit need + * + */ +OI_UINT computeBitneed(OI_CODEC_SBC_COMMON_CONTEXT *common, + OI_UINT8 *bitneeds, + OI_UINT ch, + OI_UINT *preferredBitpool) +{ + static const OI_INT8 offset4[4][4] = { + { -1, 0, 0, 0 }, + { -2, 0, 0, 1 }, + { -2, 0, 0, 1 }, + { -2, 0, 0, 1 } + }; + + static const OI_INT8 offset8[4][8] = { + { -2, 0, 0, 0, 0, 0, 0, 1 }, + { -3, 0, 0, 0, 0, 0, 1, 2 }, + { -4, 0, 0, 0, 0, 0, 1, 2 }, + { -4, 0, 0, 0, 0, 0, 1, 2 } + }; + + const OI_UINT nrof_subbands = common->frameInfo.nrof_subbands; + OI_UINT sb; + OI_INT8 *scale_factor = &common->scale_factor[ch ? nrof_subbands : 0]; + OI_UINT bitcount = 0; + OI_UINT8 maxBits = 0; + OI_UINT8 prefBits = 0; + + if (common->frameInfo.alloc == SBC_SNR) { + for (sb = 0; sb < nrof_subbands; sb++) { + OI_INT bits = scale_factor[sb]; + if (bits > maxBits) { + maxBits = bits; + } + if ((bitneeds[sb] = bits) > 1) { + bitcount += bits; + } + prefBits += 2 + bits; + } + } else { + const OI_INT8 *offset; + if (nrof_subbands == 4) { + offset = offset4[common->frameInfo.freqIndex]; + } else { + offset = offset8[common->frameInfo.freqIndex]; + } + for (sb = 0; sb < nrof_subbands; sb++) { + OI_INT bits = scale_factor[sb]; + if (bits > maxBits) { + maxBits = bits; + } + prefBits += 2 + bits; + if (bits) { + bits -= offset[sb]; + if (bits > 0) { + bits /= 2; + } + bits += 5; + } + if ((bitneeds[sb] = bits) > 1) { + bitcount += bits; + } + } + } + common->maxBitneed = OI_MAX(maxBits, common->maxBitneed); + *preferredBitpool += prefBits; + return bitcount; +} + + +/* + * Explanation of the adjustToFitBitpool inner loop. + * + * The inner loop computes the effect of adjusting the bit allocation up or + * down. Allocations must be 0 or in the range 2..16. This is accomplished by + * the following code: + * + * for (s = bands - 1; s >= 0; --s) { + * OI_INT bits = bitadjust + bitneeds[s]; + * bits = bits < 2 ? 0 : bits; + * bits = bits > 16 ? 16 : bits; + * count += bits; + * } + * + * This loop can be optimized to perform 4 operations at a time as follows: + * + * Adjustment is computed as a 7 bit signed value and added to the bitneed. + * + * Negative allocations are zeroed by masking. (n & 0x40) >> 6 puts the + * sign bit into bit 0, adding this to 0x7F give us a mask of 0x80 + * for -ve values and 0x7F for +ve values. + * + * n &= 0x7F + (n & 0x40) >> 6) + * + * Allocations greater than 16 are truncated to 16. Adjusted allocations are in + * the range 0..31 so we know that bit 4 indicates values >= 16. We use this bit + * to create a mask that zeroes bits 0 .. 3 if bit 4 is set. + * + * n &= (15 + (n >> 4)) + * + * Allocations of 1 are disallowed. Add and shift creates a mask that + * eliminates the illegal value + * + * n &= ((n + 14) >> 4) | 0x1E + * + * These operations can be performed in 8 bits without overflowing so we can + * operate on 4 values at once. + */ + + +/* + * Encoder/Decoder + * + * Computes adjustment +/- of bitneeds to fill bitpool and returns overall + * adjustment and excess bits. + * + * @param bitpool The bitpool we have to work within + * + * @param bitneeds An array of bit needs (more acturately allocation prioritities) for each + * subband across all blocks in the SBC frame + * + * @param subbands The number of subbands over which the adkustment is calculated. For mono and + * dual mode this is 4 or 8, for stereo or joint stereo this is 8 or 16. + * + * @param bitcount A starting point for the adjustment + * + * @param excess Returns the excess bits after the adjustment + * + * @return The adjustment. + */ +OI_INT adjustToFitBitpool(const OI_UINT bitpool, + OI_UINT32 *bitneeds, + const OI_UINT subbands, + OI_UINT bitcount, + OI_UINT *excess) +{ + OI_INT maxBitadjust = 0; + OI_INT bitadjust = (bitcount > bitpool) ? -8 : 8; + OI_INT chop = 8; + + /* + * This is essentially a binary search for the optimal adjustment value. + */ + while ((bitcount != bitpool) && chop) { + OI_UINT32 total = 0; + OI_UINT count; + OI_UINT32 adjust4; + OI_INT i; + + adjust4 = bitadjust & 0x7F; + adjust4 |= (adjust4 << 8); + adjust4 |= (adjust4 << 16); + + for (i = (subbands / 4 - 1); i >= 0; --i) { + OI_UINT32 mask; + OI_UINT32 n = bitneeds[i] + adjust4; + mask = 0x7F7F7F7F + ((n & 0x40404040) >> 6); + n &= mask; + mask = 0x0F0F0F0F + ((n & 0x10101010) >> 4); + n &= mask; + mask = (((n + 0x0E0E0E0E) >> 4) | 0x1E1E1E1E); + n &= mask; + total += n; + } + + count = (total & 0xFFFF) + (total >> 16); + count = (count & 0xFF) + (count >> 8); + + chop >>= 1; + if (count > bitpool) { + bitadjust -= chop; + } else { + maxBitadjust = bitadjust; + bitcount = count; + bitadjust += chop; + } + } + + *excess = bitpool - bitcount; + + return maxBitadjust; +} + + +/* + * The bit allocator trys to avoid single bit allocations except as a last resort. So in the case + * where a bitneed of 1 was passed over during the adsjustment phase 2 bits are now allocated. + */ +INLINE OI_INT allocAdjustedBits(OI_UINT8 *dest, + OI_INT bits, + OI_INT excess) +{ + if (bits < 16) { + if (bits > 1) { + if (excess) { + ++bits; + --excess; + } + } else if ((bits == 1) && (excess > 1)) { + bits = 2; + excess -= 2; + } else { + bits = 0; + } + } else { + bits = 16; + } + *dest = (OI_UINT8)bits; + return excess; +} + + +/* + * Excess bits not allocated by allocaAdjustedBits are allocated round-robin. + */ +INLINE OI_INT allocExcessBits(OI_UINT8 *dest, + OI_INT excess) +{ + if (*dest < 16) { + *dest += 1; + return excess - 1; + } else { + return excess; + } +} + +void oneChannelBitAllocation(OI_CODEC_SBC_COMMON_CONTEXT *common, + BITNEED_UNION1 *bitneeds, + OI_UINT ch, + OI_UINT bitcount) +{ + const OI_UINT8 nrof_subbands = common->frameInfo.nrof_subbands; + OI_UINT excess; + OI_UINT sb; + OI_INT bitadjust; + OI_UINT8 RESTRICT *allocBits; + + + { + OI_UINT ex; + bitadjust = adjustToFitBitpool(common->frameInfo.bitpool, bitneeds->uint32, nrof_subbands, bitcount, &ex); + /* We want the compiler to put excess into a register */ + excess = ex; + } + + /* + * Allocate adjusted bits + */ + allocBits = &common->bits.uint8[ch ? nrof_subbands : 0]; + + sb = 0; + while (sb < nrof_subbands) { + excess = allocAdjustedBits(&allocBits[sb], bitneeds->uint8[sb] + bitadjust, excess); + ++sb; + } + sb = 0; + while (excess) { + excess = allocExcessBits(&allocBits[sb], excess); + ++sb; + } +} + + +void monoBitAllocation(OI_CODEC_SBC_COMMON_CONTEXT *common) +{ + BITNEED_UNION1 bitneeds; + OI_UINT bitcount; + OI_UINT bitpoolPreference = 0; + + bitcount = computeBitneed(common, bitneeds.uint8, 0, &bitpoolPreference); + + oneChannelBitAllocation(common, &bitneeds, 0, bitcount); +} + +/** +@} +*/ + +#endif /* #if (defined(SBC_DEC_INCLUDED) && SBC_DEC_INCLUDED == TRUE) */ diff --git a/lib/bt/host/bluedroid/external/sbc/decoder/srce/bitstream-decode.c b/lib/bt/host/bluedroid/external/sbc/decoder/srce/bitstream-decode.c new file mode 100644 index 00000000..c1b9d6e9 --- /dev/null +++ b/lib/bt/host/bluedroid/external/sbc/decoder/srce/bitstream-decode.c @@ -0,0 +1,95 @@ +/****************************************************************************** + * + * Copyright (C) 2014 The Android Open Source Project + * Copyright 2003 - 2004 Open Interface North America, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/********************************************************************************** + $Revision: #1 $ +***********************************************************************************/ + +/** +@file +Functions for manipulating input bitstreams. + +@ingroup codec_internal +*/ + +/** +@addtogroup codec_internal +@{ +*/ + +#include "common/bt_target.h" +#include "oi_stddefs.h" +#include "oi_bitstream.h" +#include "oi_assert.h" + +#if (defined(SBC_DEC_INCLUDED) && SBC_DEC_INCLUDED == TRUE) + +PRIVATE void OI_BITSTREAM_ReadInit(OI_BITSTREAM *bs, + const OI_BYTE *buffer) +{ + bs->value = ((OI_INT32)buffer[0] << 16) | ((OI_INT32)buffer[1] << 8) | (buffer[2]); + bs->ptr.r = buffer + 3; + bs->bitPtr = 8; +} + +PRIVATE OI_UINT32 OI_BITSTREAM_ReadUINT(OI_BITSTREAM *bs, OI_UINT bits) +{ + OI_UINT32 result; + + OI_BITSTREAM_READUINT(result, bits, bs->ptr.r, bs->value, bs->bitPtr); + + return result; +} + +PRIVATE OI_UINT8 OI_BITSTREAM_ReadUINT4Aligned(OI_BITSTREAM *bs) +{ + OI_UINT32 result; + + OI_ASSERT(bs->bitPtr < 16); + OI_ASSERT(bs->bitPtr % 4 == 0); + + if (bs->bitPtr == 8) { + result = bs->value << 8; + bs->bitPtr = 12; + } else { + result = bs->value << 12; + bs->value = (bs->value << 8) | *bs->ptr.r++; + bs->bitPtr = 8; + } + result >>= 28; + OI_ASSERT(result < (1u << 4)); + return (OI_UINT8)result; +} + +PRIVATE OI_UINT8 OI_BITSTREAM_ReadUINT8Aligned(OI_BITSTREAM *bs) +{ + OI_UINT32 result; + OI_ASSERT(bs->bitPtr == 8); + + result = bs->value >> 16; + bs->value = (bs->value << 8) | *bs->ptr.r++; + + return (OI_UINT8)result; +} + +/** +@} +*/ + +#endif /* #if (defined(SBC_DEC_INCLUDED) && SBC_DEC_INCLUDED == TRUE) */ diff --git a/lib/bt/host/bluedroid/external/sbc/decoder/srce/decoder-oina.c b/lib/bt/host/bluedroid/external/sbc/decoder/srce/decoder-oina.c new file mode 100644 index 00000000..6673295d --- /dev/null +++ b/lib/bt/host/bluedroid/external/sbc/decoder/srce/decoder-oina.c @@ -0,0 +1,141 @@ +/****************************************************************************** + * + * Copyright (C) 2014 The Android Open Source Project + * Copyright 2006 Open Interface North America, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/********************************************************************************** + $Revision: #1 $ + ***********************************************************************************/ + +/** +@file +This file exposes OINA-specific interfaces to decoder functions. + +@ingroup codec_internal +*/ + +/** +@addtogroup codec_internal +@{ +*/ + +#include "common/bt_target.h" +#include + +#if (defined(SBC_DEC_INCLUDED) && SBC_DEC_INCLUDED == TRUE) + +OI_STATUS OI_CODEC_SBC_DecoderConfigureRaw(OI_CODEC_SBC_DECODER_CONTEXT *context, + OI_BOOL enhanced, + OI_UINT8 frequency, + OI_UINT8 mode, + OI_UINT8 subbands, + OI_UINT8 blocks, + OI_UINT8 alloc, + OI_UINT8 maxBitpool) +{ + if (frequency > SBC_FREQ_48000) { + return OI_STATUS_INVALID_PARAMETERS; + } + + if (enhanced) { +#ifdef SBC_ENHANCED + if (subbands != SBC_SUBBANDS_8) { + return OI_STATUS_INVALID_PARAMETERS; + } +#else + return OI_STATUS_INVALID_PARAMETERS; +#endif + } + + if (mode > SBC_JOINT_STEREO) { + return OI_STATUS_INVALID_PARAMETERS; + } + + if (subbands > SBC_SUBBANDS_8) { + return OI_STATUS_INVALID_PARAMETERS; + } + + if (blocks > SBC_BLOCKS_16) { + return OI_STATUS_INVALID_PARAMETERS; + } + + if (alloc > SBC_SNR) { + return OI_STATUS_INVALID_PARAMETERS; + } + +#ifdef SBC_ENHANCED + context->common.frameInfo.enhanced = enhanced; +#else + context->common.frameInfo.enhanced = FALSE; +#endif + context->common.frameInfo.freqIndex = frequency; + context->common.frameInfo.mode = mode; + context->common.frameInfo.subbands = subbands; + context->common.frameInfo.blocks = blocks; + context->common.frameInfo.alloc = alloc; + context->common.frameInfo.bitpool = maxBitpool; + + OI_SBC_ExpandFrameFields(&context->common.frameInfo); + + if (context->common.frameInfo.nrof_channels >= context->common.pcmStride) { + return OI_STATUS_INVALID_PARAMETERS; + } + + return OI_OK; +} + + + +OI_STATUS OI_CODEC_SBC_DecodeRaw(OI_CODEC_SBC_DECODER_CONTEXT *context, + OI_UINT8 bitpool, + const OI_BYTE **frameData, + OI_UINT32 *frameBytes, + OI_INT16 *pcmData, + OI_UINT32 *pcmBytes) +{ + return internal_DecodeRaw(context, + bitpool, + frameData, + frameBytes, + pcmData, + pcmBytes); +} + +OI_STATUS OI_CODEC_SBC_DecoderLimit(OI_CODEC_SBC_DECODER_CONTEXT *context, + OI_BOOL enhanced, + OI_UINT8 subbands) +{ + if (enhanced) { +#ifdef SBC_ENHANCED + context->enhancedEnabled = TRUE; +#else + context->enhancedEnabled = FALSE; +#endif + } else { + context->enhancedEnabled = FALSE; + } + context->restrictSubbands = subbands; + context->limitFrameFormat = TRUE; + return OI_OK; +} + + +/** +@} +*/ + +#endif /* #if (defined(SBC_DEC_INCLUDED) && SBC_DEC_INCLUDED == TRUE) */ diff --git a/lib/bt/host/bluedroid/external/sbc/decoder/srce/decoder-private.c b/lib/bt/host/bluedroid/external/sbc/decoder/srce/decoder-private.c new file mode 100644 index 00000000..f198f579 --- /dev/null +++ b/lib/bt/host/bluedroid/external/sbc/decoder/srce/decoder-private.c @@ -0,0 +1,259 @@ +/****************************************************************************** + * + * Copyright (C) 2014 The Android Open Source Project + * Copyright 2003 - 2004 Open Interface North America, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/********************************************************************************** + $Revision: #1 $ + ***********************************************************************************/ + +/** +@file +This file drives SBC decoding. + +@ingroup codec_internal +*/ + +/** +@addtogroup codec_internal +@{ +*/ + +#include "common/bt_target.h" +#include "oi_codec_sbc_private.h" +#include "oi_bitstream.h" +#include + +#if (defined(SBC_DEC_INCLUDED) && SBC_DEC_INCLUDED == TRUE) + +OI_CHAR *const OI_Codec_Copyright = "Copyright 2002-2007 Open Interface North America, Inc. All rights reserved"; + +INLINE OI_STATUS internal_DecoderReset(OI_CODEC_SBC_DECODER_CONTEXT *context, + OI_UINT32 *decoderData, + OI_UINT32 decoderDataBytes, + OI_BYTE maxChannels, + OI_BYTE pcmStride, + OI_BOOL enhanced, + OI_BOOL msbc_enable) +{ + OI_UINT i; + OI_STATUS status; + + for (i = 0; i < sizeof(*context); i++) { + ((char *)context)[i] = 0; + } + +#ifdef SBC_ENHANCED + context->enhancedEnabled = enhanced ? TRUE : FALSE; +#else + context->enhancedEnabled = FALSE; + if (enhanced) { + return OI_STATUS_INVALID_PARAMETERS; + } +#endif + + if (msbc_enable) { + context->sbc_mode = OI_SBC_MODE_MSBC; + } else { + context->sbc_mode = OI_SBC_MODE_STD; + } + + status = OI_CODEC_SBC_Alloc(&context->common, decoderData, decoderDataBytes, maxChannels, pcmStride); + + if (!OI_SUCCESS(status)) { + return status; + } + + context->common.codecInfo = OI_Codec_Copyright; + context->common.maxBitneed = 0; + context->limitFrameFormat = FALSE; + OI_SBC_ExpandFrameFields(&context->common.frameInfo); + + /*PLATFORM_DECODER_RESET(context);*/ + + return OI_OK; +} + +/** + * Read the SBC header up to but not including the joint stereo mask. The syncword has already been + * examined, and the enhanced mode flag set, by FindSyncword. + */ +INLINE void OI_SBC_ReadHeader(OI_CODEC_SBC_COMMON_CONTEXT *common, const OI_BYTE *data) +{ + OI_CODEC_SBC_FRAME_INFO *frame = &common->frameInfo; + OI_UINT8 d1; + + + OI_ASSERT(data[0] == OI_SBC_SYNCWORD || data[0] == OI_SBC_ENHANCED_SYNCWORD + || data[0] == OI_mSBC_SYNCWORD); + + /** + * For mSBC, just set those parameters + */ + if (data[0] == OI_mSBC_SYNCWORD){ + frame->freqIndex = 0; + frame->frequency = 16000; + + frame->blocks = 4; + frame->nrof_blocks = 15; + + frame->mode = 0; + frame->nrof_channels = 1; + + frame->alloc = SBC_LOUDNESS; + + frame->subbands = 1; + frame->nrof_subbands = 8; + + frame->cachedInfo = 0; + + frame->bitpool = 26; + frame->crc = data[3]; + return; + } + + /* Avoid filling out all these strucutures if we already remember the values + * from last time. Just in case we get a stream corresponding to data[1] == + * 0, DecoderReset is responsible for ensuring the lookup table entries have + * already been populated + */ + d1 = data[1]; + if (d1 != frame->cachedInfo) { + + frame->freqIndex = (d1 & (BIT7 | BIT6)) >> 6; + frame->frequency = freq_values[frame->freqIndex]; + + frame->blocks = (d1 & (BIT5 | BIT4)) >> 4; + frame->nrof_blocks = block_values[frame->blocks]; + + frame->mode = (d1 & (BIT3 | BIT2)) >> 2; + frame->nrof_channels = channel_values[frame->mode]; + + frame->alloc = (d1 & BIT1) >> 1; + + frame->subbands = (d1 & BIT0); + frame->nrof_subbands = band_values[frame->subbands]; + + frame->cachedInfo = d1; + } + /* + * For decode, the bit allocator needs to know the bitpool value + */ + frame->bitpool = data[2]; + frame->crc = data[3]; +} + + +#define LOW(x) ((x)& 0xf) +#define HIGH(x) ((x) >> 4) + +/* + * Read scalefactor values and prepare the bitstream for OI_SBC_ReadSamples + */ +PRIVATE void OI_SBC_ReadScalefactors(OI_CODEC_SBC_COMMON_CONTEXT *common, + const OI_BYTE *b, + OI_BITSTREAM *bs) +{ + OI_UINT i = common->frameInfo.nrof_subbands * common->frameInfo.nrof_channels; + OI_INT8 *scale_factor = common->scale_factor; + OI_UINT f; + + if (common->frameInfo.nrof_subbands == 8 || common->frameInfo.mode != SBC_JOINT_STEREO) { + if (common->frameInfo.mode == SBC_JOINT_STEREO) { + common->frameInfo.join = *b++; + } else { + common->frameInfo.join = 0; + } + i /= 2; + do { + *scale_factor++ = HIGH(f = *b++); + *scale_factor++ = LOW(f); + } while (--i); + /* + * In this case we know that the scale factors end on a byte boundary so all we need to do + * is initialize the bitstream. + */ + OI_BITSTREAM_ReadInit(bs, b); + } else { + OI_ASSERT(common->frameInfo.nrof_subbands == 4 && common->frameInfo.mode == SBC_JOINT_STEREO); + common->frameInfo.join = HIGH(f = *b++); + i = (i - 1) / 2; + do { + *scale_factor++ = LOW(f); + *scale_factor++ = HIGH(f = *b++); + } while (--i); + *scale_factor++ = LOW(f); + /* + * In 4-subband joint stereo mode, the joint stereo information ends on a half-byte + * boundary, so it's necessary to use the bitstream abstraction to read it, since + * OI_SBC_ReadSamples will need to pick up in mid-byte. + */ + OI_BITSTREAM_ReadInit(bs, b); + *scale_factor++ = OI_BITSTREAM_ReadUINT4Aligned(bs); + } +} + +/** Read quantized subband samples from the input bitstream and expand them. */ +PRIVATE void OI_SBC_ReadSamples(OI_CODEC_SBC_DECODER_CONTEXT *context, OI_BITSTREAM *global_bs) +{ + OI_CODEC_SBC_COMMON_CONTEXT *common = &context->common; + OI_UINT nrof_blocks = common->frameInfo.nrof_blocks; + OI_INT32 *RESTRICT s = common->subdata; + OI_UINT8 *ptr = global_bs->ptr.w; + OI_UINT32 value = global_bs->value; + OI_UINT bitPtr = global_bs->bitPtr; + + const OI_UINT iter_count = common->frameInfo.nrof_channels * common->frameInfo.nrof_subbands / 4; + do { + OI_UINT i; + for (i = 0; i < iter_count; ++i) { + OI_UINT32 sf_by4 = ((OI_UINT32 *)common->scale_factor)[i]; + OI_UINT32 bits_by4 = common->bits.uint32[i]; + OI_UINT n; + for (n = 0; n < 4; ++n) { + OI_INT32 dequant; + OI_UINT bits; + OI_INT sf; + + if (OI_CPU_BYTE_ORDER == OI_LITTLE_ENDIAN_BYTE_ORDER) { + bits = bits_by4 & 0xFF; + bits_by4 >>= 8; + sf = sf_by4 & 0xFF; + sf_by4 >>= 8; + } else { + bits = (bits_by4 >> 24) & 0xFF; + bits_by4 <<= 8; + sf = (sf_by4 >> 24) & 0xFF; + sf_by4 <<= 8; + } + if (bits) { + OI_UINT32 raw; + OI_BITSTREAM_READUINT(raw, bits, ptr, value, bitPtr); + dequant = OI_SBC_Dequant(raw, sf, bits); + } else { + dequant = 0; + } + *s++ = dequant; + } + } + } while (--nrof_blocks); +} + +/** +@} +*/ +#endif /* #if (defined(SBC_DEC_INCLUDED) && SBC_DEC_INCLUDED == TRUE) */ diff --git a/lib/bt/host/bluedroid/external/sbc/decoder/srce/decoder-sbc.c b/lib/bt/host/bluedroid/external/sbc/decoder/srce/decoder-sbc.c new file mode 100644 index 00000000..553b4edd --- /dev/null +++ b/lib/bt/host/bluedroid/external/sbc/decoder/srce/decoder-sbc.c @@ -0,0 +1,472 @@ +/****************************************************************************** + * + * Copyright (C) 2014 The Android Open Source Project + * Copyright 2006 Open Interface North America, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/********************************************************************************** + $Revision: #1 $ + ***********************************************************************************/ + +/** @file +@ingroup codec_internal +*/ + +/**@addtogroup codec_internal */ +/**@{*/ + +#include "common/bt_target.h" +#include "oi_codec_sbc_private.h" +#include "oi_bitstream.h" + +#if (defined(SBC_DEC_INCLUDED) && SBC_DEC_INCLUDED == TRUE) + +#define SPECIALIZE_READ_SAMPLES_JOINT + +/** + * Scans through a buffer looking for a codec syncword. If the decoder has been + * set for enhanced operation using OI_CODEC_SBC_DecoderReset(), it will search + * for both a standard and an enhanced syncword. + */ +PRIVATE OI_STATUS FindSyncword(OI_CODEC_SBC_DECODER_CONTEXT *context, + const OI_BYTE **frameData, + OI_UINT32 *frameBytes) +{ +#ifdef SBC_ENHANCED + OI_BYTE search1 = OI_SBC_SYNCWORD; + OI_BYTE search2 = OI_SBC_ENHANCED_SYNCWORD; +#endif // SBC_ENHANCED + + if (*frameBytes == 0) { + return OI_CODEC_SBC_NOT_ENOUGH_HEADER_DATA; + } + +#ifdef SBC_ENHANCED + if (context->limitFrameFormat && context->enhancedEnabled) { + /* If the context is restricted, only search for specified SYNCWORD */ + search1 = search2; + } else if (context->enhancedEnabled == FALSE) { + /* If enhanced is not enabled, only search for classic SBC SYNCWORD*/ + search2 = search1; + } + while (*frameBytes && (**frameData != search1) && (**frameData != search2)) { + (*frameBytes)--; + (*frameData)++; + } + if (*frameBytes) { + /* Syncword found, *frameData points to it, and *frameBytes correctly + * reflects the number of bytes available to read, including the + * syncword. */ + context->common.frameInfo.enhanced = (**frameData == OI_SBC_ENHANCED_SYNCWORD); + return OI_OK; + } else { + /* No syncword was found anywhere in the provided input data. + * *frameData points past the end of the original input, and + * *frameBytes is 0. */ + return OI_CODEC_SBC_NO_SYNCWORD; + } +#else // SBC_ENHANCED + while (*frameBytes + && (!(context->sbc_mode == OI_SBC_MODE_STD && **frameData == OI_SBC_SYNCWORD)) + && (!(context->sbc_mode == OI_SBC_MODE_MSBC && **frameData == OI_mSBC_SYNCWORD))) { + (*frameBytes)--; + (*frameData)++; + } + if (*frameBytes) { + /* Syncword found, *frameData points to it, and *frameBytes correctly + * reflects the number of bytes available to read, including the + * syncword. */ + context->common.frameInfo.enhanced = FALSE; + return OI_OK; + } else { + /* No syncword was found anywhere in the provided input data. + * *frameData points past the end of the original input, and + * *frameBytes is 0. */ + return OI_CODEC_SBC_NO_SYNCWORD; + } +#endif // SBC_ENHANCED +} + +static OI_STATUS DecodeBody(OI_CODEC_SBC_DECODER_CONTEXT *context, + const OI_BYTE *bodyData, + OI_INT16 *pcmData, + OI_UINT32 *pcmBytes, + OI_BOOL allowPartial) +{ + OI_BITSTREAM bs; + OI_UINT frameSamples = context->common.frameInfo.nrof_blocks * context->common.frameInfo.nrof_subbands; + OI_UINT decode_block_count; + + /* + * Based on the header data, make sure that there is enough room to write the output samples. + */ + if (*pcmBytes < (sizeof(OI_INT16) * frameSamples * context->common.pcmStride) && !allowPartial) { + /* If we're not allowing partial decodes, we need room for the entire + * codec frame */ + TRACE(("-OI_CODEC_SBC_Decode: OI_CODEC_SBC_NOT_ENOUGH_AUDIO_DATA")); + return OI_CODEC_SBC_NOT_ENOUGH_AUDIO_DATA; + } else if (*pcmBytes < sizeof (OI_INT16) * context->common.frameInfo.nrof_subbands * context->common.pcmStride) { + /* Even if we're allowing partials, we can still only decode on a frame + * boundary */ + return OI_CODEC_SBC_NOT_ENOUGH_AUDIO_DATA; + } + + if (context->bufferedBlocks == 0) { + TRACE(("Reading scalefactors")); + OI_SBC_ReadScalefactors(&context->common, bodyData, &bs); + + TRACE(("Computing bit allocation")); + OI_SBC_ComputeBitAllocation(&context->common); + + TRACE(("Reading samples")); + if (context->common.frameInfo.mode == SBC_JOINT_STEREO) { + OI_SBC_ReadSamplesJoint(context, &bs); + } else { + OI_SBC_ReadSamples(context, &bs); + } + + context->bufferedBlocks = context->common.frameInfo.nrof_blocks; + } + + if (allowPartial) { + decode_block_count = *pcmBytes / sizeof(OI_INT16) / context->common.pcmStride / context->common.frameInfo.nrof_subbands; + + if (decode_block_count > context->bufferedBlocks) { + decode_block_count = context->bufferedBlocks; + } + + } else { + decode_block_count = context->common.frameInfo.nrof_blocks; + } + + TRACE(("Synthesizing frame")); + { + OI_UINT start_block = context->common.frameInfo.nrof_blocks - context->bufferedBlocks; + OI_SBC_SynthFrame(context, pcmData, start_block, decode_block_count); + } + + OI_ASSERT(context->bufferedBlocks >= decode_block_count); + context->bufferedBlocks -= decode_block_count; + + frameSamples = decode_block_count * context->common.frameInfo.nrof_subbands; + + /* + * When decoding mono into a stride-2 array, copy pcm data to second channel + */ + if (context->common.frameInfo.nrof_channels == 1 && context->common.pcmStride == 2) { + OI_UINT i; + for (i = 0; i < frameSamples; ++i) { + pcmData[2 * i + 1] = pcmData[2 * i]; + } + } + + /* + * Return number of pcm bytes generated by the decode operation. + */ + *pcmBytes = frameSamples * sizeof(OI_INT16) * context->common.pcmStride; + if (context->bufferedBlocks > 0) { + return OI_CODEC_SBC_PARTIAL_DECODE; + } else { + return OI_OK; + } +} + +PRIVATE OI_STATUS internal_DecodeRaw(OI_CODEC_SBC_DECODER_CONTEXT *context, + OI_UINT8 bitpool, + const OI_BYTE **frameData, + OI_UINT32 *frameBytes, + OI_INT16 *pcmData, + OI_UINT32 *pcmBytes) +{ + OI_STATUS status; + OI_UINT bodyLen; + + TRACE(("+OI_CODEC_SBC_DecodeRaw")); + + if (context->bufferedBlocks == 0) { + /* + * The bitallocator needs to know the bitpool value. + */ + context->common.frameInfo.bitpool = bitpool; + /* + * Compute the frame length and check we have enough frame data to proceed + */ + bodyLen = OI_CODEC_SBC_CalculateFramelen(&context->common.frameInfo) - SBC_HEADER_LEN; + if (*frameBytes < bodyLen) { + TRACE(("-OI_CODEC_SBC_Decode: OI_CODEC_SBC_NOT_ENOUGH_BODY_DATA")); + return OI_CODEC_SBC_NOT_ENOUGH_BODY_DATA; + } + } else { + bodyLen = 0; + } + /* + * Decode the SBC data. Pass TRUE to DecodeBody to allow partial decoding of + * tones. + */ + status = DecodeBody(context, *frameData, pcmData, pcmBytes, TRUE); + if (OI_SUCCESS(status) || status == OI_CODEC_SBC_PARTIAL_DECODE) { + *frameData += bodyLen; + *frameBytes -= bodyLen; + } + TRACE(("-OI_CODEC_SBC_DecodeRaw: %d", status)); + return status; +} + +OI_STATUS OI_CODEC_SBC_DecoderReset(OI_CODEC_SBC_DECODER_CONTEXT *context, + OI_UINT32 *decoderData, + OI_UINT32 decoderDataBytes, + OI_UINT8 maxChannels, + OI_UINT8 pcmStride, + OI_BOOL enhanced, + OI_BOOL msbc_enable) +{ + return internal_DecoderReset(context, decoderData, decoderDataBytes, maxChannels, pcmStride, enhanced, msbc_enable); +} + +OI_STATUS OI_CODEC_SBC_DecodeFrame(OI_CODEC_SBC_DECODER_CONTEXT *context, + const OI_BYTE **frameData, + OI_UINT32 *frameBytes, + OI_INT16 *pcmData, + OI_UINT32 *pcmBytes) +{ + OI_STATUS status; + OI_UINT framelen; + OI_UINT8 crc; + + TRACE(("+OI_CODEC_SBC_DecodeFrame")); + + TRACE(("Finding syncword")); + status = FindSyncword(context, frameData, frameBytes); + if (!OI_SUCCESS(status)) { + return status; + } + + /* Make sure enough data remains to read the header. */ + if (*frameBytes < SBC_HEADER_LEN) { + TRACE(("-OI_CODEC_SBC_DecodeFrame: OI_CODEC_SBC_NOT_ENOUGH_HEADER_DATA")); + return OI_CODEC_SBC_NOT_ENOUGH_HEADER_DATA; + } + + TRACE(("Reading Header")); + OI_SBC_ReadHeader(&context->common, *frameData); + + /* + * Some implementations load the decoder into RAM and use overlays for 4 vs 8 subbands. We need + * to ensure that the SBC parameters for this frame are compatible with the restrictions imposed + * by the loaded overlays. + */ + if (context->limitFrameFormat && (context->common.frameInfo.subbands != context->restrictSubbands)) { + ERROR(("SBC parameters incompatible with loaded overlay")); + return OI_STATUS_INVALID_PARAMETERS; + } + + if (context->common.frameInfo.nrof_channels > context->common.maxChannels) { + ERROR(("SBC parameters incompatible with number of channels specified during reset")); + return OI_STATUS_INVALID_PARAMETERS; + } + + if (context->common.pcmStride < 1 || context->common.pcmStride > 2) { + ERROR(("PCM stride not set correctly during reset")); + return OI_STATUS_INVALID_PARAMETERS; + } + + /* + * At this point a header has been read. However, it's possible that we found a false syncword, + * so the header data might be invalid. Make sure we have enough bytes to read in the + * CRC-protected header, but don't require we have the whole frame. That way, if it turns out + * that we're acting on bogus header data, we don't stall the decoding process by waiting for + * data that we don't actually need. + */ + framelen = OI_CODEC_SBC_CalculateFramelen(&context->common.frameInfo); + if (*frameBytes < framelen) { + TRACE(("-OI_CODEC_SBC_DecodeFrame: OI_CODEC_SBC_NOT_ENOUGH_BODY_DATA")); + return OI_CODEC_SBC_NOT_ENOUGH_BODY_DATA; + } + + TRACE(("Calculating checksum")); + + crc = OI_SBC_CalculateChecksum(&context->common.frameInfo, *frameData); + if (crc != context->common.frameInfo.crc) { + TRACE(("CRC Mismatch: calc=%02x read=%02x\n", crc, context->common.frameInfo.crc)); + TRACE(("-OI_CODEC_SBC_DecodeFrame: OI_CODEC_SBC_CHECKSUM_MISMATCH")); + return OI_CODEC_SBC_CHECKSUM_MISMATCH; + } + +#ifdef OI_DEBUG + /* + * Make sure the bitpool values are sane. + */ + if ((context->common.frameInfo.bitpool < SBC_MIN_BITPOOL) && !context->common.frameInfo.enhanced) { + ERROR(("Bitpool too small: %d (must be >= 2)", context->common.frameInfo.bitpool)); + return OI_STATUS_INVALID_PARAMETERS; + } + if (context->common.frameInfo.bitpool > OI_SBC_MaxBitpool(&context->common.frameInfo)) { + ERROR(("Bitpool too large: %d (must be <= %ld)", context->common.frameInfo.bitpool, OI_SBC_MaxBitpool(&context->common.frameInfo))); + return OI_STATUS_INVALID_PARAMETERS; + } +#endif + + /* + * Now decode the SBC data. Partial decode is not yet implemented for an SBC + * stream, so pass FALSE to decode body to have it enforce the old rule that + * you have to decode a whole packet at a time. + */ + status = DecodeBody(context, *frameData + SBC_HEADER_LEN, pcmData, pcmBytes, FALSE); + if (OI_SUCCESS(status)) { + *frameData += framelen; + *frameBytes -= framelen; + } + TRACE(("-OI_CODEC_SBC_DecodeFrame: %d", status)); + + return status; +} + +OI_STATUS OI_CODEC_SBC_SkipFrame(OI_CODEC_SBC_DECODER_CONTEXT *context, + const OI_BYTE **frameData, + OI_UINT32 *frameBytes) +{ + OI_STATUS status; + OI_UINT framelen; + OI_UINT headerlen; + OI_UINT8 crc; + + status = FindSyncword(context, frameData, frameBytes); + if (!OI_SUCCESS(status)) { + return status; + } + if (*frameBytes < SBC_HEADER_LEN) { + return OI_CODEC_SBC_NOT_ENOUGH_HEADER_DATA; + } + OI_SBC_ReadHeader(&context->common, *frameData); + framelen = OI_SBC_CalculateFrameAndHeaderlen(&context->common.frameInfo, &headerlen); + if (*frameBytes < headerlen) { + return OI_CODEC_SBC_NOT_ENOUGH_HEADER_DATA; + } + crc = OI_SBC_CalculateChecksum(&context->common.frameInfo, *frameData); + if (crc != context->common.frameInfo.crc) { + return OI_CODEC_SBC_CHECKSUM_MISMATCH; + } + if (*frameBytes < framelen) { + return OI_CODEC_SBC_NOT_ENOUGH_BODY_DATA; + } + context->bufferedBlocks = 0; + *frameData += framelen; + *frameBytes -= framelen; + return OI_OK; +} + +OI_UINT8 OI_CODEC_SBC_FrameCount(OI_BYTE *frameData, + OI_UINT32 frameBytes) +{ + OI_UINT8 mode; + OI_UINT8 blocks; + OI_UINT8 subbands; + OI_UINT8 frameCount = 0; + OI_UINT frameLen; + + while (frameBytes) { + while (frameBytes && ((frameData[0] & 0xFE) != 0x9C)) { + frameData++; + frameBytes--; + } + + if (frameBytes < SBC_HEADER_LEN) { + return frameCount; + } + + /* Extract and translate required fields from Header */ + subbands = mode = blocks = frameData[1];; + mode = (mode & (BIT3 | BIT2)) >> 2; + blocks = block_values[(blocks & (BIT5 | BIT4)) >> 4]; + subbands = band_values[(subbands & BIT0)]; + + /* Inline logic to avoid corrupting context */ + frameLen = blocks * frameData[2]; + switch (mode) { + case SBC_JOINT_STEREO: + frameLen += subbands + (8 * subbands); + break; + + case SBC_DUAL_CHANNEL: + frameLen *= 2; + /* fall through */ + + default: + if (mode == SBC_MONO) { + frameLen += 4 * subbands; + } else { + frameLen += 8 * subbands; + } + } + + frameCount++; + frameLen = SBC_HEADER_LEN + (frameLen + 7) / 8; + if (frameBytes > frameLen) { + frameBytes -= frameLen; + frameData += frameLen; + } else { + frameBytes = 0; + } + } + return frameCount; +} + +/** Read quantized subband samples from the input bitstream and expand them. */ + +#ifdef SPECIALIZE_READ_SAMPLES_JOINT + +PRIVATE void OI_SBC_ReadSamplesJoint4(OI_CODEC_SBC_DECODER_CONTEXT *context, OI_BITSTREAM *global_bs) +{ +#define NROF_SUBBANDS 4 +#include "readsamplesjoint.inc" +#undef NROF_SUBBANDS +} + +PRIVATE void OI_SBC_ReadSamplesJoint8(OI_CODEC_SBC_DECODER_CONTEXT *context, OI_BITSTREAM *global_bs) +{ +#define NROF_SUBBANDS 8 +#include "readsamplesjoint.inc" +#undef NROF_SUBBANDS +} + +typedef void (*READ_SAMPLES)(OI_CODEC_SBC_DECODER_CONTEXT *context, OI_BITSTREAM *global_bs); + +static const READ_SAMPLES SpecializedReadSamples[] = { + OI_SBC_ReadSamplesJoint4, + OI_SBC_ReadSamplesJoint8 +}; + +#endif /* SPECIALIZE_READ_SAMPLES_JOINT */ + + +PRIVATE void OI_SBC_ReadSamplesJoint(OI_CODEC_SBC_DECODER_CONTEXT *context, OI_BITSTREAM *global_bs) +{ + OI_CODEC_SBC_COMMON_CONTEXT *common = &context->common; + OI_UINT nrof_subbands = common->frameInfo.nrof_subbands; +#ifdef SPECIALIZE_READ_SAMPLES_JOINT + OI_ASSERT((nrof_subbands >> 3u) <= 1u); + SpecializedReadSamples[nrof_subbands >> 3](context, global_bs); +#else + +#define NROF_SUBBANDS nrof_subbands +#include "readsamplesjoint.inc" +#undef NROF_SUBBANDS +#endif /* SPECIALIZE_READ_SAMPLES_JOINT */ +} + +/**@}*/ + +#endif /* #if (defined(SBC_DEC_INCLUDED) && SBC_DEC_INCLUDED == TRUE) */ diff --git a/lib/bt/host/bluedroid/external/sbc/decoder/srce/dequant.c b/lib/bt/host/bluedroid/external/sbc/decoder/srce/dequant.c new file mode 100644 index 00000000..d165c80a --- /dev/null +++ b/lib/bt/host/bluedroid/external/sbc/decoder/srce/dequant.c @@ -0,0 +1,215 @@ +/****************************************************************************** + * + * Copyright (C) 2014 The Android Open Source Project + * Copyright 2003 - 2004 Open Interface North America, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/********************************************************************************** + $Revision: #1 $ +***********************************************************************************/ + +/** + @file + + Dequantizer for SBC decoder; reconstructs quantized representation of subband samples. + + @ingroup codec_internal + */ + +/** +@addtogroup codec_internal +@{ +*/ + +/** + This function is a fixed-point approximation of a modification of the following + dequantization operation defined in the spec, as inferred from section 12.6.4: + + @code + dequant = 2^(scale_factor+1) * ((raw * 2.0 + 1.0) / ((2^bits) - 1) - 1) + + 2 <= bits <= 16 + 0 <= raw < (2^bits)-1 (the -1 is because quantized values with all 1's are forbidden) + + -65535 < dequant < 65535 + @endcode + + The code below computes the dequantized value divided by a scaling constant + equal to about 1.38. This constant is chosen to ensure that the entry in the + dequant_long_scaled table for 16 bits is as accurate as possible, since it has + the least relative precision available to it due to its small magnitude. + + This routine outputs in Q16.15 format. + + The helper array dequant_long is defined as follows: + + @code + dequant_long_long[bits] = round(2^31 * 1/((2^bits - 1) / 1.38...) for 2 <= bits <= 16 + @endcode + + + Additionally, the table entries have the following property: + + @code + dequant_long_scaled[bits] <= 2^31 / ((2^bits - 1)) for 2 <= bits <= 16 + @endcode + + Therefore + + @code + d = 2 * raw + 1 1 <= d <= 2^bits - 2 + + d' = d * dequant_long[bits] + + d * dequant_long_scaled[bits] <= (2^bits - 2) * (2^31 / (2^bits - 1)) + d * dequant_long_scaled[bits] <= 2^31 * (2^bits - 2)/(2^bits - 1) < 2^31 + @endcode + + Therefore, d' doesn't overflow a signed 32-bit value. + + @code + + d' =~ 2^31 * (raw * 2.0 + 1.0) / (2^bits - 1) / 1.38... + + result = d' - 2^31/1.38... =~ 2^31 * ((raw * 2.0 + 1.0) / (2^bits - 1) - 1) / 1.38... + + result is therefore a scaled approximation to dequant. It remains only to + turn 2^31 into 2^(scale_factor+1). Since we're aiming for Q16.15 format, + this is achieved by shifting right by (15-scale_factor): + + (2^31 * x) >> (15-scale_factor) =~ 2^(31-15+scale_factor) * x = 2^15 * 2^(1+scale_factor) * x + @endcode + + */ + +#include "common/bt_target.h" +#include + +#if (defined(SBC_DEC_INCLUDED) && SBC_DEC_INCLUDED == TRUE) + +#ifndef SBC_DEQUANT_LONG_SCALED_OFFSET +#define SBC_DEQUANT_LONG_SCALED_OFFSET 1555931970 +#endif + +#ifndef SBC_DEQUANT_LONG_UNSCALED_OFFSET +#define SBC_DEQUANT_LONG_UNSCALED_OFFSET 2147483648 +#endif + +#ifndef SBC_DEQUANT_SCALING_FACTOR +#define SBC_DEQUANT_SCALING_FACTOR 1.38019122262781f +#endif + +extern const OI_UINT32 dequant_long_scaled[17]; +extern const OI_UINT32 dequant_long_unscaled[17]; + +/** Scales x by y bits to the right, adding a rounding factor. + */ +#ifndef SCALE +#define SCALE(x, y) (((x) + (1 <<((y)-1))) >> (y)) +#endif + +#ifdef DEBUG_DEQUANTIZATION + +#include + +static INLINE float dequant_float(OI_UINT32 raw, OI_UINT scale_factor, OI_UINT bits) +{ + float result = (1 << (scale_factor + 1)) * ((raw * 2.0f + 1.0f) / ((1 << bits) - 1.0f) - 1.0f); + + result /= SBC_DEQUANT_SCALING_FACTOR; + + /* Unless the encoder screwed up, all correct dequantized values should + * satisfy this inequality. Non-compliant encoders which generate quantized + * values with all 1-bits set can, theoretically, trigger this assert. This + * is unlikely, however, and only an issue in debug mode. + */ + OI_ASSERT(fabs(result) < 32768 * 1.6); + + return result; +} + +#endif + + +INLINE OI_INT32 OI_SBC_Dequant(OI_UINT32 raw, OI_UINT scale_factor, OI_UINT bits) +{ + OI_UINT32 d; + OI_INT32 result; + + OI_ASSERT(scale_factor <= 15); + OI_ASSERT(bits <= 16); + + if (bits <= 1) { + return 0; + } + + d = (raw * 2) + 1; + d *= dequant_long_scaled[bits]; + result = d - SBC_DEQUANT_LONG_SCALED_OFFSET; + +#ifdef DEBUG_DEQUANTIZATION + { + OI_INT32 integerized_float_result; + float float_result; + + float_result = dequant_float(raw, scale_factor, bits); + integerized_float_result = (OI_INT32)floor(0.5f + float_result * (1 << 15)); + + /* This detects overflow */ + OI_ASSERT(((result >= 0) && (integerized_float_result >= 0)) || + ((result <= 0) && (integerized_float_result <= 0))); + } +#endif + return result >> (15 - scale_factor); +} + +/* This version of Dequant does not incorporate the scaling factor of 1.38. It + * is intended for use with implementations of the filterbank which are + * hard-coded into a DSP. Output is Q16.4 format, so that after joint stereo + * processing (which leaves the most significant bit equal to the sign bit if + * the encoder is conformant) the result will fit a 24 bit fixed point signed + * value.*/ + +INLINE OI_INT32 OI_SBC_Dequant_Unscaled(OI_UINT32 raw, OI_UINT scale_factor, OI_UINT bits) +{ + OI_UINT32 d; + OI_INT32 result; + + OI_ASSERT(scale_factor <= 15); + OI_ASSERT(bits <= 16); + + + if (bits <= 1) { + return 0; + } + if (bits == 16) { + result = (raw << 16) + raw - 0x7fff7fff; + return SCALE(result, 24 - scale_factor); + } + + + d = (raw * 2) + 1; + d *= dequant_long_unscaled[bits]; + result = d - 0x80000000; + + return SCALE(result, 24 - scale_factor); +} + +/** +@} +*/ + +#endif /* #if (defined(SBC_DEC_INCLUDED) && SBC_DEC_INCLUDED == TRUE) */ diff --git a/lib/bt/host/bluedroid/external/sbc/decoder/srce/framing-sbc.c b/lib/bt/host/bluedroid/external/sbc/decoder/srce/framing-sbc.c new file mode 100644 index 00000000..4f605601 --- /dev/null +++ b/lib/bt/host/bluedroid/external/sbc/decoder/srce/framing-sbc.c @@ -0,0 +1,59 @@ +/****************************************************************************** + * + * Copyright (C) 2014 The Android Open Source Project + * Copyright 2003 - 2004 Open Interface North America, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/********************************************************************************** + $Revision: #1 $ +***********************************************************************************/ + +/** @file +@ingroup codec_internal +*/ + +/**@addgroup codec_internal*/ +/**@{*/ + +#include "common/bt_target.h" +#include "oi_codec_sbc_private.h" + +#if (defined(SBC_DEC_INCLUDED) && SBC_DEC_INCLUDED == TRUE) + +const OI_CHAR *const OI_CODEC_SBC_FreqText[] = { "SBC_FREQ_16000", "SBC_FREQ_32000", "SBC_FREQ_44100", "SBC_FREQ_48000" }; +const OI_CHAR *const OI_CODEC_SBC_ModeText[] = { "SBC_MONO", "SBC_DUAL_CHANNEL", "SBC_STEREO", "SBC_JOINT_STEREO" }; +const OI_CHAR *const OI_CODEC_SBC_SubbandsText[] = { "SBC_SUBBANDS_4", "SBC_SUBBANDS_8" }; +const OI_CHAR *const OI_CODEC_SBC_BlocksText[] = { "SBC_BLOCKS_4", "SBC_BLOCKS_8", "SBC_BLOCKS_12", "SBC_BLOCKS_16" }; +const OI_CHAR *const OI_CODEC_SBC_AllocText[] = { "SBC_LOUDNESS", "SBC_SNR" }; + +#ifdef OI_DEBUG +void OI_CODEC_SBC_DumpConfig(OI_CODEC_SBC_FRAME_INFO *frameInfo) +{ + printf("SBC configuration\n"); + printf(" enhanced: %s\n", frameInfo->enhanced ? "TRUE" : "FALSE"); + printf(" frequency: %d\n", frameInfo->frequency); + printf(" subbands: %d\n", frameInfo->nrof_subbands); + printf(" blocks: %d\n", frameInfo->nrof_blocks); + printf(" channels: %d\n", frameInfo->nrof_channels); + printf(" mode: %s\n", OI_CODEC_SBC_ModeText[frameInfo->mode]); + printf(" alloc: %s\n", OI_CODEC_SBC_AllocText[frameInfo->alloc]); + printf(" bitpool: %d\n", frameInfo->bitpool); +} +#endif /* OI_DEBUG */ + +/**@}*/ + +#endif /* #if (defined(SBC_DEC_INCLUDED) && SBC_DEC_INCLUDED == TRUE) */ diff --git a/lib/bt/host/bluedroid/external/sbc/decoder/srce/framing.c b/lib/bt/host/bluedroid/external/sbc/decoder/srce/framing.c new file mode 100644 index 00000000..9c4eb3a4 --- /dev/null +++ b/lib/bt/host/bluedroid/external/sbc/decoder/srce/framing.c @@ -0,0 +1,253 @@ +/****************************************************************************** + * + * Copyright (C) 2014 The Android Open Source Project + * Copyright 2003 - 2004 Open Interface North America, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/********************************************************************************** + $Revision: #1 $ +***********************************************************************************/ + +/** +@file +Checksum and header-related functions. + +@ingroup codec_internal +*/ + +/** +@addtogroup codec_internal +@{ +*/ + +#include "common/bt_target.h" +#include "oi_codec_sbc_private.h" +#include "oi_assert.h" + +#if (defined(SBC_DEC_INCLUDED) && SBC_DEC_INCLUDED == TRUE) + +/* asdasd */ + +#define USE_NIBBLEWISE_CRC + +/* #define PRINT_SAMPLES */ +/* #define PRINT_SCALEFACTORS */ +/* #define DEBUG_CRC */ + +/* + * CRC-8 table for X^8 + X^4 + X^3 + X^2 + 1; byte-wise lookup + */ +#ifdef USE_WIDE_CRC +/* Save space if a char is 16 bits, such as on the C54x */ +const OI_BYTE crc8_wide[128] = { + 0x001d, 0x3a27, 0x7469, 0x4e53, 0xe8f5, 0xd2cf, 0x9c81, 0xa6bb, 0xcdd0, 0xf7ea, 0xb9a4, 0x839e, 0x2538, 0x1f02, 0x514c, 0x6b76, 0x879a, 0xbda0, 0xf3ee, 0xc9d4, 0x6f72, 0x5548, 0x1b06, 0x213c, 0x4a57, 0x706d, 0x3e23, 0x0419, 0xa2bf, 0x9885, 0xd6cb, 0xecf1, 0x130e, 0x2934, 0x677a, 0x5d40, 0xfbe6, 0xc1dc, 0x8f92, 0xb5a8, 0xdec3, 0xe4f9, 0xaab7, 0x908d, 0x362b, 0x0c11, 0x425f, 0x7865, 0x9489, 0xaeb3, 0xe0fd, 0xdac7, 0x7c61, 0x465b, 0x0815, 0x322f, 0x5944, 0x637e, 0x2d30, 0x170a, 0xb1ac, 0x8b96, 0xc5d8, 0xffe2, 0x263b, 0x1c01, 0x524f, 0x6875, 0xced3, 0xf4e9, 0xbaa7, 0x809d, 0xebf6, 0xd1cc, 0x9f82, 0xa5b8, 0x031e, 0x3924, 0x776a, 0x4d50, 0xa1bc, 0x9b86, 0xd5c8, 0xeff2, 0x4954, 0x736e, 0x3d20, 0x071a, 0x6c71, 0x564b, 0x1805, 0x223f, 0x8499, 0xbea3, 0xf0ed, 0xcad7, 0x3528, 0x0f12, 0x415c, 0x7b66, 0xddc0, 0xe7fa, 0xa9b4, 0x938e, 0xf8e5, 0xc2df, 0x8c91, 0xb6ab, 0x100d, 0x2a37, 0x6479, 0x5e43, 0xb2af, 0x8895, 0xc6db, 0xfce1, 0x5a47, 0x607d, 0x2e33, 0x1409, 0x7f62, 0x4558, 0x0b16, 0x312c, 0x978a, 0xadb0, 0xe3fe, 0xd9c4, +}; +#elif defined(USE_NIBBLEWISE_CRC) +const OI_BYTE crc8_narrow[16] = { + 0x00, 0x1d, 0x3a, 0x27, 0x74, 0x69, 0x4e, 0x53, 0xe8, 0xf5, 0xd2, 0xcf, 0x9c, 0x81, 0xa6, 0xbb +}; +#else +const OI_BYTE crc8_narrow[256] = { + 0x00, 0x1d, 0x3a, 0x27, 0x74, 0x69, 0x4e, 0x53, 0xe8, 0xf5, 0xd2, 0xcf, 0x9c, 0x81, 0xa6, 0xbb, 0xcd, 0xd0, 0xf7, 0xea, 0xb9, 0xa4, 0x83, 0x9e, 0x25, 0x38, 0x1f, 0x02, 0x51, 0x4c, 0x6b, 0x76, 0x87, 0x9a, 0xbd, 0xa0, 0xf3, 0xee, 0xc9, 0xd4, 0x6f, 0x72, 0x55, 0x48, 0x1b, 0x06, 0x21, 0x3c, 0x4a, 0x57, 0x70, 0x6d, 0x3e, 0x23, 0x04, 0x19, 0xa2, 0xbf, 0x98, 0x85, 0xd6, 0xcb, 0xec, 0xf1, 0x13, 0x0e, 0x29, 0x34, 0x67, 0x7a, 0x5d, 0x40, 0xfb, 0xe6, 0xc1, 0xdc, 0x8f, 0x92, 0xb5, 0xa8, 0xde, 0xc3, 0xe4, 0xf9, 0xaa, 0xb7, 0x90, 0x8d, 0x36, 0x2b, 0x0c, 0x11, 0x42, 0x5f, 0x78, 0x65, 0x94, 0x89, 0xae, 0xb3, 0xe0, 0xfd, 0xda, 0xc7, 0x7c, 0x61, 0x46, 0x5b, 0x08, 0x15, 0x32, 0x2f, 0x59, 0x44, 0x63, 0x7e, 0x2d, 0x30, 0x17, 0x0a, 0xb1, 0xac, 0x8b, 0x96, 0xc5, 0xd8, 0xff, 0xe2, 0x26, 0x3b, 0x1c, 0x01, 0x52, 0x4f, 0x68, 0x75, 0xce, 0xd3, 0xf4, 0xe9, 0xba, 0xa7, 0x80, 0x9d, 0xeb, 0xf6, 0xd1, 0xcc, 0x9f, 0x82, 0xa5, 0xb8, 0x03, 0x1e, 0x39, 0x24, 0x77, 0x6a, 0x4d, 0x50, 0xa1, 0xbc, 0x9b, 0x86, 0xd5, 0xc8, 0xef, 0xf2, 0x49, 0x54, 0x73, 0x6e, 0x3d, 0x20, 0x07, 0x1a, 0x6c, 0x71, 0x56, 0x4b, 0x18, 0x05, 0x22, 0x3f, 0x84, 0x99, 0xbe, 0xa3, 0xf0, 0xed, 0xca, 0xd7, 0x35, 0x28, 0x0f, 0x12, 0x41, 0x5c, 0x7b, 0x66, 0xdd, 0xc0, 0xe7, 0xfa, 0xa9, 0xb4, 0x93, 0x8e, 0xf8, 0xe5, 0xc2, 0xdf, 0x8c, 0x91, 0xb6, 0xab, 0x10, 0x0d, 0x2a, 0x37, 0x64, 0x79, 0x5e, 0x43, 0xb2, 0xaf, 0x88, 0x95, 0xc6, 0xdb, 0xfc, 0xe1, 0x5a, 0x47, 0x60, 0x7d, 0x2e, 0x33, 0x14, 0x09, 0x7f, 0x62, 0x45, 0x58, 0x0b, 0x16, 0x31, 0x2c, 0x97, 0x8a, 0xad, 0xb0, 0xe3, 0xfe, 0xd9, 0xc4 +}; +#endif +const OI_UINT32 dequant_long_scaled[17] = { + 0, + 0, + 0x1ee9e116, /* bits=2 0.24151243 1/3 * (1/1.38019122262781) (0x00000008)*/ + 0x0d3fa99c, /* bits=3 0.10350533 1/7 * (1/1.38019122262781) (0x00000013)*/ + 0x062ec69e, /* bits=4 0.04830249 1/15 * (1/1.38019122262781) (0x00000029)*/ + 0x02fddbfa, /* bits=5 0.02337217 1/31 * (1/1.38019122262781) (0x00000055)*/ + 0x0178d9f5, /* bits=6 0.01150059 1/63 * (1/1.38019122262781) (0x000000ad)*/ + 0x00baf129, /* bits=7 0.00570502 1/127 * (1/1.38019122262781) (0x0000015e)*/ + 0x005d1abe, /* bits=8 0.00284132 1/255 * (1/1.38019122262781) (0x000002bf)*/ + 0x002e760d, /* bits=9 0.00141788 1/511 * (1/1.38019122262781) (0x00000582)*/ + 0x00173536, /* bits=10 0.00070825 1/1023 * (1/1.38019122262781) (0x00000b07)*/ + 0x000b9928, /* bits=11 0.00035395 1/2047 * (1/1.38019122262781) (0x00001612)*/ + 0x0005cc37, /* bits=12 0.00017693 1/4095 * (1/1.38019122262781) (0x00002c27)*/ + 0x0002e604, /* bits=13 0.00008846 1/8191 * (1/1.38019122262781) (0x00005852)*/ + 0x000172fc, /* bits=14 0.00004422 1/16383 * (1/1.38019122262781) (0x0000b0a7)*/ + 0x0000b97d, /* bits=15 0.00002211 1/32767 * (1/1.38019122262781) (0x00016150)*/ + 0x00005cbe, /* bits=16 0.00001106 1/65535 * (1/1.38019122262781) (0x0002c2a5)*/ +}; + + +const OI_UINT32 dequant_long_unscaled[17] = { + 0, + 0, + 0x2aaaaaab, /* bits=2 0.33333333 1/3 (0x00000005)*/ + 0x12492492, /* bits=3 0.14285714 1/7 (0x0000000e)*/ + 0x08888889, /* bits=4 0.06666667 1/15 (0x0000001d)*/ + 0x04210842, /* bits=5 0.03225806 1/31 (0x0000003e)*/ + 0x02082082, /* bits=6 0.01587302 1/63 (0x0000007e)*/ + 0x01020408, /* bits=7 0.00787402 1/127 (0x000000fe)*/ + 0x00808081, /* bits=8 0.00392157 1/255 (0x000001fd)*/ + 0x00402010, /* bits=9 0.00195695 1/511 (0x000003fe)*/ + 0x00200802, /* bits=10 0.00097752 1/1023 (0x000007fe)*/ + 0x00100200, /* bits=11 0.00048852 1/2047 (0x00000ffe)*/ + 0x00080080, /* bits=12 0.00024420 1/4095 (0x00001ffe)*/ + 0x00040020, /* bits=13 0.00012209 1/8191 (0x00003ffe)*/ + 0x00020008, /* bits=14 0.00006104 1/16383 (0x00007ffe)*/ + 0x00010002, /* bits=15 0.00003052 1/32767 (0x0000fffe)*/ + 0x00008001, /* bits=16 0.00001526 1/65535 (0x0001fffc)*/ +}; + +#if defined(OI_DEBUG) || defined(PRINT_SAMPLES) || defined(PRINT_SCALEFACTORS) +#include +#endif + +#ifdef USE_WIDE_CRC +static INLINE OI_CHAR crc_iterate(OI_UINT8 oldcrc, OI_UINT8 next) +{ + OI_UINT crc; + OI_UINT idx; + idx = oldcrc ^ next; + crc = crc8_wide[idx >> 1]; + if (idx % 2) { + crc &= 0xff; + } else { + crc >>= 8; + } + + return crc; +} + +static INLINE OI_CHAR crc_iterate_top4(OI_UINT8 oldcrc, OI_UINT8 next) +{ + OI_UINT crc; + OI_UINT idx; + idx = (oldcrc ^ next) >> 4; + crc = crc8_wide[idx >> 1]; + if (idx % 2) { + crc &= 0xff; + } else { + crc >>= 8; + } + + return (oldcrc << 4) ^ crc; +} + +#else // USE_WIDE_CRC + +static INLINE OI_UINT8 crc_iterate_top4(OI_UINT8 oldcrc, OI_UINT8 next) +{ + return (oldcrc << 4) ^ crc8_narrow[(oldcrc ^ next) >> 4]; +} + +#ifdef USE_NIBBLEWISE_CRC +static INLINE OI_UINT8 crc_iterate(OI_UINT8 crc, OI_UINT8 next) +{ + crc = (crc << 4) ^ crc8_narrow[(crc ^ next) >> 4]; + crc = (crc << 4) ^ crc8_narrow[((crc >> 4)^next) & 0xf]; + + return crc; +} + +#else // USE_NIBBLEWISE_CRC +static INLINE OI_UINT8 crc_iterate(OI_UINT8 crc, OI_UINT8 next) +{ + return crc8_narrow[crc ^ next]; +} + +#endif // USE_NIBBLEWISE_CRC + +#endif // USE_WIDE_CRC + + +PRIVATE OI_UINT8 OI_SBC_CalculateChecksum(OI_CODEC_SBC_FRAME_INFO *frame, OI_BYTE const *data) +{ + OI_UINT i; + OI_UINT8 crc = 0x0f; + /* Count is the number of whole bytes subject to CRC. Actually, it's one + * more than this number, because data[3] is the CRC field itself, which is + * explicitly skipped. Since crc_iterate (should be) inlined, it's cheaper + * spacewise to include the check in the loop. This shouldn't be much of a + * bottleneck routine in the first place. */ + OI_UINT count = (frame->nrof_subbands * frame->nrof_channels / 2u) + 4; + + if (frame->mode == SBC_JOINT_STEREO && frame->nrof_subbands == 8) { + count++; + } + + for (i = 1; i < count; i++) { + if (i != 3) { + crc = crc_iterate(crc, data[i]); + } + } + + if (frame->mode == SBC_JOINT_STEREO && frame->nrof_subbands == 4) { + crc = crc_iterate_top4(crc, data[i]); + } + + return crc; +} + +void OI_SBC_ExpandFrameFields(OI_CODEC_SBC_FRAME_INFO *frame) +{ + frame->nrof_blocks = block_values[frame->blocks]; + frame->nrof_subbands = band_values[frame->subbands]; + + frame->frequency = freq_values[frame->freqIndex]; + frame->nrof_channels = channel_values[frame->mode]; +} + +/** + * Unrolled macro to copy 4 32-bit aligned 32-bit values backward in memory + */ +#define COPY4WORDS_BACK(_dest, _src) \ + do { \ + OI_INT32 _a, _b, _c, _d; \ + _a = *--_src; \ + _b = *--_src; \ + _c = *--_src; \ + _d = *--_src; \ + *--_dest = _a; \ + *--_dest = _b; \ + *--_dest = _c; \ + *--_dest = _d; \ + } while (0) + + +#if defined(USE_PLATFORM_MEMMOVE) || defined(USE_PLATFORM_MEMCPY) +#include +#endif +PRIVATE void shift_buffer(SBC_BUFFER_T *dest, SBC_BUFFER_T *src, OI_UINT wordCount) +{ +#ifdef USE_PLATFORM_MEMMOVE + memmove(dest, src, wordCount * sizeof(SBC_BUFFER_T)); +#elif defined(USE_PLATFORM_MEMCPY) + OI_ASSERT(((OI_CHAR *)(dest) - (OI_CHAR *)(src)) >= wordCount * sizeof(*dest)); + memcpy(dest, src, wordCount * sizeof(SBC_BUFFER_T)); +#else + OI_UINT n; + OI_INT32 *d; + OI_INT32 *s; + n = wordCount / 4 / (sizeof(OI_INT32) / sizeof(*dest)); + OI_ASSERT((n * 4 * (sizeof(OI_INT32) / sizeof(*dest))) == wordCount); + + d = (void *)(dest + wordCount); + s = (void *)(src + wordCount); + + do { + COPY4WORDS_BACK(d, s); + } while (--n); +#endif +} +/** +@} +*/ + +#endif /* #if (defined(SBC_DEC_INCLUDED) && SBC_DEC_INCLUDED == TRUE) */ diff --git a/lib/bt/host/bluedroid/external/sbc/decoder/srce/oi_codec_version.c b/lib/bt/host/bluedroid/external/sbc/decoder/srce/oi_codec_version.c new file mode 100644 index 00000000..95f88830 --- /dev/null +++ b/lib/bt/host/bluedroid/external/sbc/decoder/srce/oi_codec_version.c @@ -0,0 +1,61 @@ +/****************************************************************************** + * + * Copyright (C) 2014 The Android Open Source Project + * Copyright 2002 - 2004 Open Interface North America, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/** +@file +This file contains a single function, which returns a string indicating the +version number of the eSBC codec + +@ingroup codec_internal +*/ + +/** +@addtogroup codec_internal +@{ +*/ + +/********************************************************************************** + $Revision: #1 $ +***********************************************************************************/ +#include "common/bt_target.h" +#include "oi_stddefs.h" +#include "oi_codec_sbc_private.h" + +#if (defined(SBC_DEC_INCLUDED) && SBC_DEC_INCLUDED == TRUE) +/** Version string for the BLUEmagic 3.0 protocol stack and profiles */ +PRIVATE OI_CHAR *const codecVersion = "v1.5" +#ifdef OI_SBC_EVAL + " (Evaluation version)" +#endif + ; + +/** This function returns the version string for the BLUEmagic 3.0 protocol stack + and profiles */ +OI_CHAR *OI_CODEC_Version(void) +{ + return codecVersion; +} + +/**********************************************************************************/ + +/** +@} +*/ + +#endif /* #if (defined(SBC_DEC_INCLUDED) && SBC_DEC_INCLUDED == TRUE) */ diff --git a/lib/bt/host/bluedroid/external/sbc/decoder/srce/readsamplesjoint.inc b/lib/bt/host/bluedroid/external/sbc/decoder/srce/readsamplesjoint.inc new file mode 100644 index 00000000..875a3949 --- /dev/null +++ b/lib/bt/host/bluedroid/external/sbc/decoder/srce/readsamplesjoint.inc @@ -0,0 +1,111 @@ +/****************************************************************************** + * + * Copyright (C) 2014 The Android Open Source Project + * Copyright 2003 - 2004 Open Interface North America, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/******************************************************************************* + * @file readsamplesjoint.inc + * + * This is the body of the generic version of OI_SBC_ReadSamplesJoint(). + * It is designed to be \#included into a function as follows: + \code + void OI_SBC_ReadSamplesJoint4(OI_CODEC_SBC_COMMON_CONTEXT *common, OI_BITSTREAM *global_bs) + { + #define NROF_SUBBANDS 4 + #include "readsamplesjoint.inc" + #undef NROF_SUBBANDS + } + + void OI_SBC_ReadSamplesJoint8(OI_CODEC_SBC_COMMON_CONTEXT *common, OI_BITSTREAM *global_bs) + { + #define NROF_SUBBANDS 8 + #include "readsamplesjoint.inc" + #undef NROF_SUBBANDS + } + \endcode + * Or to make a generic version: + \code + void OI_SBC_ReadSamplesJoint(OI_CODEC_SBC_COMMON_CONTEXT *common, OI_BITSTREAM *global_bs) + { + OI_UINT nrof_subbands = common->frameInfo.nrof_subbands; + + #define NROF_SUBBANDS nrof_subbands + #include "readsamplesjoint.inc" + #undef NROF_SUBBANDS + } + \endcode + * @ingroup codec_internal + *******************************************************************************/ + +/********************************************************************************** + $Revision: #1 $ +***********************************************************************************/ + +{ + OI_CODEC_SBC_COMMON_CONTEXT *common = &context->common; + OI_UINT bl = common->frameInfo.nrof_blocks; + OI_INT32 * RESTRICT s = common->subdata; + OI_UINT8 *ptr = global_bs->ptr.w; + OI_UINT32 value = global_bs->value; + OI_UINT bitPtr = global_bs->bitPtr; + OI_UINT8 jmask = common->frameInfo.join << (8 - NROF_SUBBANDS); + + do { + OI_INT8 *sf_array = &common->scale_factor[0]; + OI_UINT8 *bits_array = &common->bits.uint8[0]; + OI_UINT8 joint = jmask; + OI_UINT sb; + /* + * Left channel + */ + sb = NROF_SUBBANDS; + do { + OI_UINT32 raw; + OI_INT32 dequant; + OI_UINT8 bits = *bits_array++; + OI_INT sf = *sf_array++; + + OI_BITSTREAM_READUINT(raw, bits, ptr, value, bitPtr); + dequant = OI_SBC_Dequant(raw, sf, bits); + *s++ = dequant; + } while (--sb); + /* + * Right channel + */ + sb = NROF_SUBBANDS; + do { + OI_UINT32 raw; + OI_INT32 dequant; + OI_UINT8 bits = *bits_array++; + OI_INT sf = *sf_array++; + + OI_BITSTREAM_READUINT(raw, bits, ptr, value, bitPtr); + dequant = OI_SBC_Dequant(raw, sf, bits); + /* + * Check if we need to do mid/side + */ + if (joint & 0x80) { + OI_INT32 mid = *(s - NROF_SUBBANDS); + OI_INT32 side = dequant; + *(s - NROF_SUBBANDS) = mid + side; + dequant = mid - side; + } + joint <<= 1; + *s++ = dequant; + } while (--sb); + } while (--bl); +} diff --git a/lib/bt/host/bluedroid/external/sbc/decoder/srce/synthesis-8-generated.c b/lib/bt/host/bluedroid/external/sbc/decoder/srce/synthesis-8-generated.c new file mode 100644 index 00000000..137b01e9 --- /dev/null +++ b/lib/bt/host/bluedroid/external/sbc/decoder/srce/synthesis-8-generated.c @@ -0,0 +1,139 @@ +/****************************************************************************** + * + * Copyright (C) 2014 The Android Open Source Project + * Copyright 2003 - 2004 Open Interface North America, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/** + @file + + DO NOT EDIT THIS FILE DIRECTLY + + This file is automatically generated by the "synthesis-gen.pl" script. + Any changes to this generated file will be lost when the script is re-run. + + These functions are called by functions in synthesis.c to perform the synthesis + filterbank computations for the SBC decoder. + + + */ +#include "common/bt_target.h" +#include + +#if (defined(SBC_DEC_INCLUDED) && SBC_DEC_INCLUDED == TRUE) + +#ifndef CLIP_INT16 +#define CLIP_INT16(x) do { if (x > OI_INT16_MAX) { x = OI_INT16_MAX; } else if (x < OI_INT16_MIN) { x = OI_INT16_MIN; } } while (0) +#endif + +#define MUL_16S_16S(_x, _y) ((_x) * (_y)) + +PRIVATE void SynthWindow80_generated(OI_INT16 *pcm, SBC_BUFFER_T const *RESTRICT buffer, OI_UINT strideShift) +{ + OI_INT32 pcm_a, pcm_b; + /* 1 - stage 0 */ pcm_b = 0; + /* 1 - stage 0 */ pcm_b += (MUL_16S_16S(8235, buffer[ 12])) >> 3; + /* 1 - stage 0 */ pcm_b += (MUL_16S_16S(-23167, buffer[ 20])) >> 3; + /* 1 - stage 0 */ pcm_b += (MUL_16S_16S(26479, buffer[ 28])) >> 2; + /* 1 - stage 0 */ pcm_b += (MUL_16S_16S(-17397, buffer[ 36])) << 1; + /* 1 - stage 0 */ pcm_b += (MUL_16S_16S(9399, buffer[ 44])) << 3; + /* 1 - stage 0 */ pcm_b += (MUL_16S_16S(17397, buffer[ 52])) << 1; + /* 1 - stage 0 */ pcm_b += (MUL_16S_16S(26479, buffer[ 60])) >> 2; + /* 1 - stage 0 */ pcm_b += (MUL_16S_16S(23167, buffer[ 68])) >> 3; + /* 1 - stage 0 */ pcm_b += (MUL_16S_16S(8235, buffer[ 76])) >> 3; + /* 1 - stage 0 */ pcm_b /= 32768; CLIP_INT16(pcm_b); pcm[0 << strideShift] = (OI_INT16)pcm_b; + /* 1 - stage 1 */ pcm_a = 0; + /* 1 - stage 1 */ pcm_b = 0; + /* 1 - stage 1 */ pcm_a += (MUL_16S_16S(-3263, buffer[ 5])) >> 5; + /* 1 - stage 1 */ pcm_b += (MUL_16S_16S(9293, buffer[ 5])) >> 3; + /* 1 - stage 1 */ pcm_a += (MUL_16S_16S(29293, buffer[ 11])) >> 5; + /* 1 - stage 1 */ pcm_b += (MUL_16S_16S(-6087, buffer[ 11])) >> 2; + /* 1 - stage 1 */ pcm_a += (MUL_16S_16S(-5229, buffer[ 21])); + /* 1 - stage 1 */ pcm_b += (MUL_16S_16S(1247, buffer[ 21])) << 3; + /* 1 - stage 1 */ pcm_a += (MUL_16S_16S(30835, buffer[ 27])) >> 3; + /* 1 - stage 1 */ pcm_b += (MUL_16S_16S(-2893, buffer[ 27])) << 3; + /* 1 - stage 1 */ pcm_a += (MUL_16S_16S(-27021, buffer[ 37])) << 1; + /* 1 - stage 1 */ pcm_b += (MUL_16S_16S(23671, buffer[ 37])) << 2; + /* 1 - stage 1 */ pcm_a += (MUL_16S_16S(31633, buffer[ 43])) << 1; + /* 1 - stage 1 */ pcm_b += (MUL_16S_16S(18055, buffer[ 43])) << 1; + /* 1 - stage 1 */ pcm_a += (MUL_16S_16S(17319, buffer[ 53])) << 1; + /* 1 - stage 1 */ pcm_b += (MUL_16S_16S(11537, buffer[ 53])) >> 1; + /* 1 - stage 1 */ pcm_a += (MUL_16S_16S(26663, buffer[ 59])) >> 2; + /* 1 - stage 1 */ pcm_b += (MUL_16S_16S(1747, buffer[ 59])) << 1; + /* 1 - stage 1 */ pcm_a += (MUL_16S_16S(4555, buffer[ 69])) >> 1; + /* 1 - stage 1 */ pcm_b += (MUL_16S_16S(685, buffer[ 69])) << 1; + /* 1 - stage 1 */ pcm_a += (MUL_16S_16S(12419, buffer[ 75])) >> 4; + /* 1 - stage 1 */ pcm_b += (MUL_16S_16S(8721, buffer[ 75])) >> 7; + /* 1 - stage 1 */ pcm_a /= 32768; CLIP_INT16(pcm_a); pcm[1 << strideShift] = (OI_INT16)pcm_a; + /* 1 - stage 1 */ pcm_b /= 32768; CLIP_INT16(pcm_b); pcm[7 << strideShift] = (OI_INT16)pcm_b; + /* 1 - stage 2 */ pcm_a = 0; + /* 1 - stage 2 */ pcm_b = 0; + /* 1 - stage 2 */ pcm_a += (MUL_16S_16S(-10385, buffer[ 6])) >> 6; + /* 1 - stage 2 */ pcm_b += (MUL_16S_16S(11167, buffer[ 6])) >> 4; + /* 1 - stage 2 */ pcm_a += (MUL_16S_16S(24995, buffer[ 10])) >> 5; + /* 1 - stage 2 */ pcm_b += (MUL_16S_16S(-10337, buffer[ 10])) >> 4; + /* 1 - stage 2 */ pcm_a += (MUL_16S_16S(-309, buffer[ 22])) << 4; + /* 1 - stage 2 */ pcm_b += (MUL_16S_16S(1917, buffer[ 22])) << 2; + /* 1 - stage 2 */ pcm_a += (MUL_16S_16S(9161, buffer[ 26])) >> 3; + /* 1 - stage 2 */ pcm_b += (MUL_16S_16S(-30605, buffer[ 26])) >> 1; + /* 1 - stage 2 */ pcm_a += (MUL_16S_16S(-23063, buffer[ 38])) << 1; + /* 1 - stage 2 */ pcm_b += (MUL_16S_16S(8317, buffer[ 38])) << 3; + /* 1 - stage 2 */ pcm_a += (MUL_16S_16S(27561, buffer[ 42])) << 1; + /* 1 - stage 2 */ pcm_b += (MUL_16S_16S(9553, buffer[ 42])) << 2; + /* 1 - stage 2 */ pcm_a += (MUL_16S_16S(2309, buffer[ 54])) << 3; + /* 1 - stage 2 */ pcm_b += (MUL_16S_16S(22117, buffer[ 54])) >> 4; + /* 1 - stage 2 */ pcm_a += (MUL_16S_16S(12705, buffer[ 58])) >> 1; + /* 1 - stage 2 */ pcm_b += (MUL_16S_16S(16383, buffer[ 58])) >> 2; + /* 1 - stage 2 */ pcm_a += (MUL_16S_16S(6239, buffer[ 70])) >> 3; + /* 1 - stage 2 */ pcm_b += (MUL_16S_16S(7543, buffer[ 70])) >> 3; + /* 1 - stage 2 */ pcm_a += (MUL_16S_16S(9251, buffer[ 74])) >> 4; + /* 1 - stage 2 */ pcm_b += (MUL_16S_16S(8603, buffer[ 74])) >> 6; + /* 1 - stage 2 */ pcm_a /= 32768; CLIP_INT16(pcm_a); pcm[2 << strideShift] = (OI_INT16)pcm_a; + /* 1 - stage 2 */ pcm_b /= 32768; CLIP_INT16(pcm_b); pcm[6 << strideShift] = (OI_INT16)pcm_b; + /* 1 - stage 3 */ pcm_a = 0; + /* 1 - stage 3 */ pcm_b = 0; + /* 1 - stage 3 */ pcm_a += (MUL_16S_16S(-16457, buffer[ 7])) >> 6; + /* 1 - stage 3 */ pcm_b += (MUL_16S_16S(16913, buffer[ 7])) >> 5; + /* 1 - stage 3 */ pcm_a += (MUL_16S_16S(19083, buffer[ 9])) >> 5; + /* 1 - stage 3 */ pcm_b += (MUL_16S_16S(-8443, buffer[ 9])) >> 7; + /* 1 - stage 3 */ pcm_a += (MUL_16S_16S(-23641, buffer[ 23])) >> 2; + /* 1 - stage 3 */ pcm_b += (MUL_16S_16S(3687, buffer[ 23])) << 1; + /* 1 - stage 3 */ pcm_a += (MUL_16S_16S(-29015, buffer[ 25])) >> 4; + /* 1 - stage 3 */ pcm_b += (MUL_16S_16S(-301, buffer[ 25])) << 5; + /* 1 - stage 3 */ pcm_a += (MUL_16S_16S(-12889, buffer[ 39])) << 2; + /* 1 - stage 3 */ pcm_b += (MUL_16S_16S(15447, buffer[ 39])) << 2; + /* 1 - stage 3 */ pcm_a += (MUL_16S_16S(6145, buffer[ 41])) << 3; + /* 1 - stage 3 */ pcm_b += (MUL_16S_16S(10255, buffer[ 41])) << 2; + /* 1 - stage 3 */ pcm_a += (MUL_16S_16S(24211, buffer[ 55])) >> 1; + /* 1 - stage 3 */ pcm_b += (MUL_16S_16S(-18233, buffer[ 55])) >> 3; + /* 1 - stage 3 */ pcm_a += (MUL_16S_16S(23469, buffer[ 57])) >> 2; + /* 1 - stage 3 */ pcm_b += (MUL_16S_16S(9405, buffer[ 57])) >> 1; + /* 1 - stage 3 */ pcm_a += (MUL_16S_16S(21223, buffer[ 71])) >> 8; + /* 1 - stage 3 */ pcm_b += (MUL_16S_16S(1499, buffer[ 71])) >> 1; + /* 1 - stage 3 */ pcm_a += (MUL_16S_16S(26913, buffer[ 73])) >> 6; + /* 1 - stage 3 */ pcm_b += (MUL_16S_16S(26189, buffer[ 73])) >> 7; + /* 1 - stage 3 */ pcm_a /= 32768; CLIP_INT16(pcm_a); pcm[3 << strideShift] = (OI_INT16)pcm_a; + /* 1 - stage 3 */ pcm_b /= 32768; CLIP_INT16(pcm_b); pcm[5 << strideShift] = (OI_INT16)pcm_b; + /* 1 - stage 4 */ pcm_a = 0; + /* 1 - stage 4 */ pcm_a += (MUL_16S_16S(10445, buffer[ 8])) >> 4; + /* 1 - stage 4 */ pcm_a += (MUL_16S_16S(-5297, buffer[ 24])) << 1; + /* 1 - stage 4 */ pcm_a += (MUL_16S_16S(22299, buffer[ 40])) << 2; + /* 1 - stage 4 */ pcm_a += (MUL_16S_16S(10603, buffer[ 56])); + /* 1 - stage 4 */ pcm_a += (MUL_16S_16S(9539, buffer[ 72])) >> 4; + /* 1 - stage 4 */ pcm_a /= 32768; CLIP_INT16(pcm_a); pcm[4 << strideShift] = (OI_INT16)pcm_a; +} + +#endif /* #if (defined(SBC_DEC_INCLUDED) && SBC_DEC_INCLUDED == TRUE) */ diff --git a/lib/bt/host/bluedroid/external/sbc/decoder/srce/synthesis-dct8.c b/lib/bt/host/bluedroid/external/sbc/decoder/srce/synthesis-dct8.c new file mode 100644 index 00000000..504e3c3b --- /dev/null +++ b/lib/bt/host/bluedroid/external/sbc/decoder/srce/synthesis-dct8.c @@ -0,0 +1,308 @@ +/****************************************************************************** + * + * Copyright (C) 2014 The Android Open Source Project + * Copyright 2003 - 2004 Open Interface North America, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/********************************************************************************** + $Revision: #1 $ +***********************************************************************************/ + +/** @file +@ingroup codec_internal +*/ + +/**@addgroup codec_internal*/ +/**@{*/ + +/* + * Performs an 8-point Type-II scaled DCT using the Arai-Agui-Nakajima + * factorization. The scaling factors are folded into the windowing + * constants. 29 adds and 5 16x32 multiplies per 8 samples. + */ +#include "common/bt_target.h" +#include "oi_codec_sbc_private.h" + +#if (defined(SBC_DEC_INCLUDED) && SBC_DEC_INCLUDED == TRUE) + +#define AAN_C4_FIX (759250125)/* S1.30 759250125 0.707107*/ + +#define AAN_C6_FIX (410903207)/* S1.30 410903207 0.382683*/ + +#define AAN_Q0_FIX (581104888)/* S1.30 581104888 0.541196*/ + +#define AAN_Q1_FIX (1402911301)/* S1.30 1402911301 1.306563*/ + +/** Scales x by y bits to the right, adding a rounding factor. + */ +#ifndef SCALE +#define SCALE(x, y) (((x) + (1 <<((y)-1))) >> (y)) +#endif + +/** + * Default C language implementation of a 32x32->32 multiply. This function may + * be replaced by a platform-specific version for speed. + * + * @param u A signed 32-bit multiplicand + * @param v A signed 32-bit multiplier + + * @return A signed 32-bit value corresponding to the 32 most significant bits + * of the 64-bit product of u and v. + */ +static INLINE OI_INT32 default_mul_32s_32s_hi(OI_INT32 u, OI_INT32 v) +{ + OI_UINT32 u0, v0; + OI_INT32 u1, v1, w1, w2, t; + + u0 = u & 0xFFFF; u1 = u >> 16; + v0 = v & 0xFFFF; v1 = v >> 16; + t = u0 * v0; + t = u1 * v0 + ((OI_UINT32)t >> 16); + w1 = t & 0xFFFF; + w2 = t >> 16; + w1 = u0 * v1 + w1; + return u1 * v1 + w2 + (w1 >> 16); +} + +#define MUL_32S_32S_HI(_x, _y) default_mul_32s_32s_hi(_x, _y) + + +#ifdef DEBUG_DCT +PRIVATE void float_dct2_8(float *RESTRICT out, OI_INT32 const *RESTRICT in) +{ +#define FIX(x,bits) (((int)floor(0.5f+((x)*((float)(1<= 8 ? 16 : 0) + and VSIGN(i) maps i%16 into {1, 1, 1, 1, 0, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1 } + These values correspond to the + signs of the redundant values as + shown in the explanation three + paragraphs above. +@endcode + +We saw above how V[4..8,13..15] (and by extension +V[(4..8,13..15)+16*n]) can be defined in terms of other elements +within the subblock of V. V[0..3,9..12] correspond to DCT elements. + +@code + for i=0 to 79 do + W[i] = D[i]*DSIGN(i)*DCT[remap_DCT(i)] +@endcode + +The DCT is calculated using the Arai-Agui-Nakajima factorization, +which saves some computation by producing output that needs to be +multiplied by scaling factors before being used. + +@code + for i=0 to 79 do + W[i] = D[i]*SCALE[i%8]*AAN_DCT[remap_DCT(i)] +@endcode + +D can be premultiplied with the DCT scaling factors to yield + +@code + for i=0 to 79 do + W[i] = DSCALED[i]*AAN_DCT[remap_DCT(i)] where DSCALED[i] = D[i]*SCALE[i%8] +@endcode + +The output samples X[0..7] are defined as sums of W: + +@code + X[j] = sum{i=0..9}(W[j+8*i]) +@endcode + +@ingroup codec_internal +*/ + +/** +@addtogroup codec_internal +@{ +*/ +#include "common/bt_target.h" +#include "oi_codec_sbc_private.h" + +#if (defined(SBC_DEC_INCLUDED) && SBC_DEC_INCLUDED == TRUE) + +const OI_INT32 dec_window_4[21] = { + 0, /* +0.00000000E+00 */ + 97, /* +5.36548976E-04 */ + 270, /* +1.49188357E-03 */ + 495, /* +2.73370904E-03 */ + 694, /* +3.83720193E-03 */ + 704, /* +3.89205149E-03 */ + 338, /* +1.86581691E-03 */ + -554, /* -3.06012286E-03 */ + 1974, /* +1.09137620E-02 */ + 3697, /* +2.04385087E-02 */ + 5224, /* +2.88757392E-02 */ + 5824, /* +3.21939290E-02 */ + 4681, /* +2.58767811E-02 */ + 1109, /* +6.13245186E-03 */ + -5214, /* -2.88217274E-02 */ + -14047, /* -7.76463494E-02 */ + 24529, /* +1.35593274E-01 */ + 35274, /* +1.94987841E-01 */ + 44618, /* +2.46636662E-01 */ + 50984, /* +2.81828203E-01 */ + 53243, /* +2.94315332E-01 */ +}; + +#define DCTII_4_K06_FIX ( 11585)/* S1.14 11585 0.707107*/ + +#define DCTII_4_K08_FIX ( 21407)/* S1.14 21407 1.306563*/ + +#define DCTII_4_K09_FIX (-15137)/* S1.14 -15137 -0.923880*/ + +#define DCTII_4_K10_FIX ( -8867)/* S1.14 -8867 -0.541196*/ + +/** Scales x by y bits to the right, adding a rounding factor. + */ +#ifndef SCALE +#define SCALE(x, y) (((x) + (1 <<((y)-1))) >> (y)) +#endif + +#ifndef CLIP_INT16 +#define CLIP_INT16(x) do { if (x > OI_INT16_MAX) { x = OI_INT16_MAX; } else if (x < OI_INT16_MIN) { x = OI_INT16_MIN; } } while (0) +#endif + +/** + * Default C language implementation of a 16x32->32 multiply. This function may + * be replaced by a platform-specific version for speed. + * + * @param u A signed 16-bit multiplicand + * @param v A signed 32-bit multiplier + + * @return A signed 32-bit value corresponding to the 32 most significant bits + * of the 48-bit product of u and v. + */ +static INLINE OI_INT32 default_mul_16s_32s_hi(OI_INT16 u, OI_INT32 v) +{ + OI_UINT16 v0; + OI_INT16 v1; + + OI_INT32 w, x; + + v0 = (OI_UINT16)(v & 0xffff); + v1 = (OI_INT16) (v >> 16); + + w = v1 * u; + x = u * v0; + + return w + (x >> 16); +} + +#define MUL_16S_32S_HI(_x, _y) default_mul_16s_32s_hi(_x, _y) + +#define LONG_MULT_DCT(K, sample) (MUL_16S_32S_HI(K, sample)<<2) + +PRIVATE void SynthWindow80_generated(OI_INT16 *pcm, SBC_BUFFER_T const *RESTRICT buffer, OI_UINT strideShift); +PRIVATE void SynthWindow112_generated(OI_INT16 *pcm, SBC_BUFFER_T const *RESTRICT buffer, OI_UINT strideShift); +PRIVATE void dct2_8(SBC_BUFFER_T *RESTRICT out, OI_INT32 const *RESTRICT x); + +typedef void (*SYNTH_FRAME)(OI_CODEC_SBC_DECODER_CONTEXT *context, OI_INT16 *pcm, OI_UINT blkstart, OI_UINT blkcount); + +#ifndef COPY_BACKWARD_32BIT_ALIGNED_72_HALFWORDS +#define COPY_BACKWARD_32BIT_ALIGNED_72_HALFWORDS(dest, src) do { shift_buffer(dest, src, 72); } while (0) +#endif + +#ifndef DCT2_8 +#define DCT2_8(dst, src) dct2_8(dst, src) +#endif + +#ifndef SYNTH80 +#define SYNTH80 SynthWindow80_generated +#endif + +#ifndef SYNTH112 +#define SYNTH112 SynthWindow112_generated +#endif + +PRIVATE void OI_SBC_SynthFrame_80(OI_CODEC_SBC_DECODER_CONTEXT *context, OI_INT16 *pcm, OI_UINT blkstart, OI_UINT blkcount) +{ + OI_UINT blk; + OI_UINT ch; + OI_UINT nrof_channels = context->common.frameInfo.nrof_channels; + OI_UINT pcmStrideShift = context->common.pcmStride == 1 ? 0 : 1; + OI_UINT offset = context->common.filterBufferOffset; + OI_INT32 *s = context->common.subdata + 8 * nrof_channels * blkstart; + OI_UINT blkstop = blkstart + blkcount; + + for (blk = blkstart; blk < blkstop; blk++) { + if (offset == 0) { + COPY_BACKWARD_32BIT_ALIGNED_72_HALFWORDS(context->common.filterBuffer[0] + context->common.filterBufferLen - 72, context->common.filterBuffer[0]); + if (nrof_channels == 2) { + COPY_BACKWARD_32BIT_ALIGNED_72_HALFWORDS(context->common.filterBuffer[1] + context->common.filterBufferLen - 72, context->common.filterBuffer[1]); + } + offset = context->common.filterBufferLen - 80; + } else { + offset -= 1 * 8; + } + + for (ch = 0; ch < nrof_channels; ch++) { + DCT2_8(context->common.filterBuffer[ch] + offset, s); + SYNTH80(pcm + ch, context->common.filterBuffer[ch] + offset, pcmStrideShift); + s += 8; + } + pcm += (8 << pcmStrideShift); + } + context->common.filterBufferOffset = offset; +} + +PRIVATE void OI_SBC_SynthFrame_4SB(OI_CODEC_SBC_DECODER_CONTEXT *context, OI_INT16 *pcm, OI_UINT blkstart, OI_UINT blkcount) +{ + OI_UINT blk; + OI_UINT ch; + OI_UINT nrof_channels = context->common.frameInfo.nrof_channels; + OI_UINT pcmStrideShift = context->common.pcmStride == 1 ? 0 : 1; + OI_UINT offset = context->common.filterBufferOffset; + OI_INT32 *s = context->common.subdata + 8 * nrof_channels * blkstart; + OI_UINT blkstop = blkstart + blkcount; + + for (blk = blkstart; blk < blkstop; blk++) { + if (offset == 0) { + COPY_BACKWARD_32BIT_ALIGNED_72_HALFWORDS(context->common.filterBuffer[0] + context->common.filterBufferLen - 72, context->common.filterBuffer[0]); + if (nrof_channels == 2) { + COPY_BACKWARD_32BIT_ALIGNED_72_HALFWORDS(context->common.filterBuffer[1] + context->common.filterBufferLen - 72, context->common.filterBuffer[1]); + } + offset = context->common.filterBufferLen - 80; + } else { + offset -= 8; + } + for (ch = 0; ch < nrof_channels; ch++) { + cosineModulateSynth4(context->common.filterBuffer[ch] + offset, s); + SynthWindow40_int32_int32_symmetry_with_sum(pcm + ch, + context->common.filterBuffer[ch] + offset, + pcmStrideShift); + s += 4; + } + pcm += (4 << pcmStrideShift); + } + context->common.filterBufferOffset = offset; +} + +#ifdef SBC_ENHANCED + +PRIVATE void OI_SBC_SynthFrame_Enhanced(OI_CODEC_SBC_DECODER_CONTEXT *context, OI_INT16 *pcm, OI_UINT blkstart, OI_UINT blkcount) +{ + OI_UINT blk; + OI_UINT ch; + OI_UINT nrof_channels = context->common.frameInfo.nrof_channels; + OI_UINT pcmStrideShift = context->common.pcmStride == 1 ? 0 : 1; + OI_UINT offset = context->common.filterBufferOffset; + OI_INT32 *s = context->common.subdata + 8 * nrof_channels * blkstart; + OI_UINT blkstop = blkstart + blkcount; + + for (blk = blkstart; blk < blkstop; blk++) { + if (offset == 0) { + COPY_BACKWARD_32BIT_ALIGNED_104_HALFWORDS(context->common.filterBuffer[0] + context->common.filterBufferLen - 104, context->common.filterBuffer[0]); + if (nrof_channels == 2) { + COPY_BACKWARD_32BIT_ALIGNED_104_HALFWORDS(context->common.filterBuffer[1] + context->common.filterBufferLen - 104, context->common.filterBuffer[1]); + } + offset = context->common.filterBufferLen - 112; + } else { + offset -= 8; + } + for (ch = 0; ch < nrof_channels; ++ch) { + DCT2_8(context->common.filterBuffer[ch] + offset, s); + SYNTH112(pcm + ch, context->common.filterBuffer[ch] + offset, pcmStrideShift); + s += 8; + } + pcm += (8 << pcmStrideShift); + } + context->common.filterBufferOffset = offset; +} + +static const SYNTH_FRAME SynthFrameEnhanced[] = { + NULL, /* invalid */ + OI_SBC_SynthFrame_Enhanced, /* mono */ + OI_SBC_SynthFrame_Enhanced /* stereo */ +}; + +#endif + +static const SYNTH_FRAME SynthFrame8SB[] = { + NULL, /* invalid */ + OI_SBC_SynthFrame_80, /* mono */ + OI_SBC_SynthFrame_80 /* stereo */ +}; + + +static const SYNTH_FRAME SynthFrame4SB[] = { + NULL, /* invalid */ + OI_SBC_SynthFrame_4SB, /* mono */ + OI_SBC_SynthFrame_4SB /* stereo */ +}; + +PRIVATE void OI_SBC_SynthFrame(OI_CODEC_SBC_DECODER_CONTEXT *context, OI_INT16 *pcm, OI_UINT start_block, OI_UINT nrof_blocks) +{ + OI_UINT nrof_subbands = context->common.frameInfo.nrof_subbands; + OI_UINT nrof_channels = context->common.frameInfo.nrof_channels; + + OI_ASSERT(nrof_subbands == 4 || nrof_subbands == 8); + if (nrof_subbands == 4) { + SynthFrame4SB[nrof_channels](context, pcm, start_block, nrof_blocks); +#ifdef SBC_ENHANCED + } else if (context->common.frameInfo.enhanced) { + SynthFrameEnhanced[nrof_channels](context, pcm, start_block, nrof_blocks); +#endif /* SBC_ENHANCED */ + } else { + SynthFrame8SB[nrof_channels](context, pcm, start_block, nrof_blocks); + } +} + + +void SynthWindow40_int32_int32_symmetry_with_sum(OI_INT16 *pcm, SBC_BUFFER_T buffer[80], OI_UINT strideShift) +{ + OI_INT32 pa; + OI_INT32 pb; + + /* These values should be zero, since out[2] of the 4-band cosine modulation + * is always zero. */ + OI_ASSERT(buffer[ 2] == 0); + OI_ASSERT(buffer[10] == 0); + OI_ASSERT(buffer[18] == 0); + OI_ASSERT(buffer[26] == 0); + OI_ASSERT(buffer[34] == 0); + OI_ASSERT(buffer[42] == 0); + OI_ASSERT(buffer[50] == 0); + OI_ASSERT(buffer[58] == 0); + OI_ASSERT(buffer[66] == 0); + OI_ASSERT(buffer[74] == 0); + + + pa = dec_window_4[ 4] * (buffer[12] + buffer[76]); + pa += dec_window_4[ 8] * (buffer[16] - buffer[64]); + pa += dec_window_4[12] * (buffer[28] + buffer[60]); + pa += dec_window_4[16] * (buffer[32] - buffer[48]); + pa += dec_window_4[20] * buffer[44]; + pa = SCALE(-pa, 15); + CLIP_INT16(pa); + pcm[0 << strideShift] = (OI_INT16)pa; + + + pa = dec_window_4[ 1] * buffer[ 1]; pb = dec_window_4[ 1] * buffer[79]; + pb += dec_window_4[ 3] * buffer[ 3]; pa += dec_window_4[ 3] * buffer[77]; + pa += dec_window_4[ 5] * buffer[13]; pb += dec_window_4[ 5] * buffer[67]; + pb += dec_window_4[ 7] * buffer[15]; pa += dec_window_4[ 7] * buffer[65]; + pa += dec_window_4[ 9] * buffer[17]; pb += dec_window_4[ 9] * buffer[63]; + pb += dec_window_4[11] * buffer[19]; pa += dec_window_4[11] * buffer[61]; + pa += dec_window_4[13] * buffer[29]; pb += dec_window_4[13] * buffer[51]; + pb += dec_window_4[15] * buffer[31]; pa += dec_window_4[15] * buffer[49]; + pa += dec_window_4[17] * buffer[33]; pb += dec_window_4[17] * buffer[47]; + pb += dec_window_4[19] * buffer[35]; pa += dec_window_4[19] * buffer[45]; + pa = SCALE(-pa, 15); + CLIP_INT16(pa); + pcm[1 << strideShift] = (OI_INT16)(pa); + pb = SCALE(-pb, 15); + CLIP_INT16(pb); + pcm[3 << strideShift] = (OI_INT16)(pb); + + + pa = dec_window_4[2] * (/*buffer[ 2] + */ buffer[78]); /* buffer[ 2] is always zero */ + pa += dec_window_4[6] * (buffer[14] /* + buffer[66]*/); /* buffer[66] is always zero */ + pa += dec_window_4[10] * (/*buffer[18] + */ buffer[62]); /* buffer[18] is always zero */ + pa += dec_window_4[14] * (buffer[30] /* + buffer[50]*/); /* buffer[50] is always zero */ + pa += dec_window_4[18] * (/*buffer[34] + */ buffer[46]); /* buffer[34] is always zero */ + pa = SCALE(-pa, 15); + CLIP_INT16(pa); + pcm[2 << strideShift] = (OI_INT16)(pa); +} + + +/** + This routine implements the cosine modulation matrix for 4-subband + synthesis. This is called "matrixing" in the SBC specification. This + matrix, M4, can be factored into an 8-point Type II Discrete Cosine + Transform, DCTII_4 and a matrix S4, given here: + + @code + __ __ + | 0 0 1 0 | + | 0 0 0 1 | + | 0 0 0 0 | + | 0 0 0 -1 | + S4 = | 0 0 -1 0 | + | 0 -1 0 0 | + | -1 0 0 0 | + |__ 0 -1 0 0 __| + + M4 * in = S4 * (DCTII_4 * in) + @endcode + + (DCTII_4 * in) is computed using a Fast Cosine Transform. The algorithm + here is based on an implementation computed by the SPIRAL computer + algebra system, manually converted to fixed-point arithmetic. S4 can be + implemented using only assignment and negation. + */ +PRIVATE void cosineModulateSynth4(SBC_BUFFER_T *RESTRICT out, OI_INT32 const *RESTRICT in) +{ + OI_INT32 f0, f1, f2, f3, f4, f7, f8, f9, f10; + OI_INT32 y0, y1, y2, y3; + + f0 = (in[0] - in[3]); + f1 = (in[0] + in[3]); + f2 = (in[1] - in[2]); + f3 = (in[1] + in[2]); + + f4 = f1 - f3; + + y0 = -SCALE(f1 + f3, DCT_SHIFT); + y2 = -SCALE(LONG_MULT_DCT(DCTII_4_K06_FIX, f4), DCT_SHIFT); + f7 = f0 + f2; + f8 = LONG_MULT_DCT(DCTII_4_K08_FIX, f0); + f9 = LONG_MULT_DCT(DCTII_4_K09_FIX, f7); + f10 = LONG_MULT_DCT(DCTII_4_K10_FIX, f2); + y3 = -SCALE(f8 + f9, DCT_SHIFT); + y1 = -SCALE(f10 - f9, DCT_SHIFT); + + out[0] = (OI_INT16) - y2; + out[1] = (OI_INT16) - y3; + out[2] = (OI_INT16)0; + out[3] = (OI_INT16)y3; + out[4] = (OI_INT16)y2; + out[5] = (OI_INT16)y1; + out[6] = (OI_INT16)y0; + out[7] = (OI_INT16)y1; +} + +/** +@} +*/ +#endif /* #if (defined(SBC_DEC_INCLUDED) && SBC_DEC_INCLUDED == TRUE) */ diff --git a/lib/bt/host/bluedroid/external/sbc/encoder/include/sbc_dct.h b/lib/bt/host/bluedroid/external/sbc/encoder/include/sbc_dct.h new file mode 100644 index 00000000..165a8c1c --- /dev/null +++ b/lib/bt/host/bluedroid/external/sbc/encoder/include/sbc_dct.h @@ -0,0 +1,91 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * Definitions for the fast DCT. + * + ******************************************************************************/ + +#ifndef SBC_DCT_H +#define SBC_DCT_H + +#if (SBC_ARM_ASM_OPT==TRUE) +#define SBC_MULT_32_16_SIMPLIFIED(s16In2, s32In1, s32OutLow) \ +{ \ + __asm \ +{ \ + MUL s32OutLow,(SINT32)s16In2, (s32In1>>15) \ +} \ +} +#else +#if (SBC_DSP_OPT==TRUE) +#define SBC_MULT_32_16_SIMPLIFIED(s16In2, s32In1 , s32OutLow) s32OutLow = SBC_Multiply_32_16_Simplified((SINT32)s16In2,s32In1); +#else +#if (SBC_IPAQ_OPT==TRUE) +/*#define SBC_MULT_32_16_SIMPLIFIED(s16In2, s32In1 , s32OutLow) s32OutLow=(SINT32)((SINT32)(s16In2)*(SINT32)(s32In1>>15)); */ +#define SBC_MULT_32_16_SIMPLIFIED(s16In2, s32In1 , s32OutLow) s32OutLow=(SINT32)(((SINT64)s16In2*(SINT64)s32In1)>>15); +#if (SBC_IS_64_MULT_IN_IDCT == TRUE) +#define SBC_MULT_32_32(s32In2, s32In1, s32OutLow) \ +{ \ + s64Temp = ((SINT64) s32In2) * ((SINT64) s32In1)>>31; \ + s32OutLow = (SINT32) s64Temp; \ +} +#endif +#else +#define SBC_MULT_32_16_SIMPLIFIED(s16In2, s32In1 , s32OutLow) \ +{ \ + s32In1Temp = s32In1; \ + s32In2Temp = (SINT32)s16In2; \ + \ + /* Multiply one +ve and the other -ve number */ \ + if (s32In1Temp < 0) \ + { \ + s32In1Temp ^= 0xFFFFFFFF; \ + s32In1Temp++; \ + s32OutLow = (s32In2Temp * (s32In1Temp >> 16)); \ + s32OutLow += (( s32In2Temp * (s32In1Temp & 0xFFFF)) >> 16); \ + s32OutLow ^= 0xFFFFFFFF; \ + s32OutLow++; \ + } \ + else \ + { \ + s32OutLow = (s32In2Temp * (s32In1Temp >> 16)); \ + s32OutLow += (( s32In2Temp * (s32In1Temp & 0xFFFF)) >> 16); \ + } \ + s32OutLow <<= 1; \ +} +#if (SBC_IS_64_MULT_IN_IDCT == TRUE) +#define SBC_MULT_64(s32In1, s32In2, s32OutLow, s32OutHi) \ +{\ + s32OutLow=(SINT32)(((SINT64)s32In1*(SINT64)s32In2)& 0x00000000FFFFFFFF);\ + s32OutHi=(SINT32)(((SINT64)s32In1*(SINT64)s32In2)>>32);\ +} +#define SBC_MULT_32_32(s32In2, s32In1, s32OutLow) \ +{ \ + s32HiTemp = 0; \ + SBC_MULT_64(s32In2,s32In1 , s32OutLow, s32HiTemp); \ + s32OutLow = (((s32OutLow>>15)&0x1FFFF) | (s32HiTemp << 17)); \ +} +#endif + +#endif +#endif +#endif + +#endif diff --git a/lib/bt/host/bluedroid/external/sbc/encoder/include/sbc_enc_func_declare.h b/lib/bt/host/bluedroid/external/sbc/encoder/include/sbc_enc_func_declare.h new file mode 100644 index 00000000..b863dd89 --- /dev/null +++ b/lib/bt/host/bluedroid/external/sbc/encoder/include/sbc_enc_func_declare.h @@ -0,0 +1,56 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * Function declarations. + * + ******************************************************************************/ + +#ifndef SBC_FUNCDECLARE_H +#define SBC_FUNCDECLARE_H + +/*#include "sbc_encoder.h"*/ +/* Global data */ +#if (SBC_IS_64_MULT_IN_WINDOW_ACCU == FALSE) +extern const SINT16 gas32CoeffFor4SBs[]; +extern const SINT16 gas32CoeffFor8SBs[]; +#else +extern const SINT32 gas32CoeffFor4SBs[]; +extern const SINT32 gas32CoeffFor8SBs[]; +#endif + +/* Global functions*/ + +extern void sbc_enc_bit_alloc_mono(SBC_ENC_PARAMS *CodecParams); +extern void sbc_enc_bit_alloc_ste(SBC_ENC_PARAMS *CodecParams); + +extern void SbcAnalysisInit (void); + +extern void SbcAnalysisFilter4(SBC_ENC_PARAMS *strEncParams); +extern void SbcAnalysisFilter8(SBC_ENC_PARAMS *strEncParams); + +extern void SBC_FastIDCT8 (SINT32 *pInVect, SINT32 *pOutVect); +extern void SBC_FastIDCT4 (SINT32 *x0, SINT32 *pOutVect); + +extern void EncPacking(SBC_ENC_PARAMS *strEncParams); +extern void EncQuantizer(SBC_ENC_PARAMS *); +#if (SBC_DSP_OPT==TRUE) +SINT32 SBC_Multiply_32_16_Simplified(SINT32 s32In2Temp, SINT32 s32In1Temp); +#endif +#endif diff --git a/lib/bt/host/bluedroid/external/sbc/encoder/include/sbc_encoder.h b/lib/bt/host/bluedroid/external/sbc/encoder/include/sbc_encoder.h new file mode 100644 index 00000000..b7b807f5 --- /dev/null +++ b/lib/bt/host/bluedroid/external/sbc/encoder/include/sbc_encoder.h @@ -0,0 +1,208 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains constants and structures used by Encoder. + * + ******************************************************************************/ + +#ifndef SBC_ENCODER_H +#define SBC_ENCODER_H + +#define ENCODER_VERSION "0025" + +#ifdef BUILDCFG +#include "common/bt_target.h" +#endif + +/*DEFINES*/ +#ifndef FALSE +#define FALSE 0 +#endif + +#ifndef TRUE +#define TRUE (!FALSE) +#endif + +#define SBC_MAX_NUM_OF_SUBBANDS 8 +#define SBC_MAX_NUM_OF_CHANNELS 2 +#define SBC_MAX_NUM_OF_BLOCKS 16 + +#define SBC_LOUDNESS 0 +#define SBC_SNR 1 + +#define SUB_BANDS_8 8 +#define SUB_BANDS_4 4 + +#define SBC_sf16000 0 +#define SBC_sf32000 1 +#define SBC_sf44100 2 +#define SBC_sf48000 3 + +#define SBC_MONO 0 +#define SBC_DUAL 1 +#define SBC_STEREO 2 +#define SBC_JOINT_STEREO 3 + +#define SBC_BLOCK_0 4 +#define SBC_BLOCK_1 8 +#define SBC_BLOCK_2 12 +#define SBC_BLOCK_3 16 + +#define SBC_NULL 0 + +#define SBC_MODE_STD 0 +#define SBC_MODE_MSBC 1 + +#define SBC_SYNC_WORD_STD (0x9C) +#define SBC_SYNC_WORD_MSBC (0xAD) + +#ifndef SBC_MAX_NUM_FRAME +#define SBC_MAX_NUM_FRAME 1 +#endif + +#ifndef SBC_DSP_OPT +#define SBC_DSP_OPT FALSE +#endif + +/* Set SBC_USE_ARM_PRAGMA to TRUE to use "#pragma arm section zidata" */ +#ifndef SBC_USE_ARM_PRAGMA +#define SBC_USE_ARM_PRAGMA FALSE +#endif + +/* Set SBC_ARM_ASM_OPT to TRUE in case the target is an ARM */ +/* this will replace all the 32 and 64 bit mult by in line assembly code */ +#ifndef SBC_ARM_ASM_OPT +#define SBC_ARM_ASM_OPT FALSE +#endif + +/* green hill compiler option -> Used to distinguish the syntax for inline assembly code*/ +#ifndef SBC_GHS_COMPILER +#define SBC_GHS_COMPILER FALSE +#endif + +/* ARM compiler option -> Used to distinguish the syntax for inline assembly code */ +#ifndef SBC_ARM_COMPILER +#define SBC_ARM_COMPILER TRUE +#endif + +/* Set SBC_IPAQ_OPT to TRUE in case the target is an ARM */ +/* 32 and 64 bit mult will be performed using SINT64 ( usualy __int64 ) cast that usualy give optimal performance if supported */ +#ifndef SBC_IPAQ_OPT +#define SBC_IPAQ_OPT TRUE +#endif + +/* Debug only: set SBC_IS_64_MULT_IN_WINDOW_ACCU to TRUE to use 64 bit multiplication in the windowing */ +/* -> not recomended, more MIPS for the same restitution. */ +#ifndef SBC_IS_64_MULT_IN_WINDOW_ACCU +#define SBC_IS_64_MULT_IN_WINDOW_ACCU FALSE +#endif /*SBC_IS_64_MULT_IN_WINDOW_ACCU */ + +/* Set SBC_IS_64_MULT_IN_IDCT to TRUE to use 64 bits multiplication in the DCT of Matrixing */ +/* -> more MIPS required for a better audio quality. comparasion with the SIG utilities shows a division by 10 of the RMS */ +/* CAUTION: It only apply in the if SBC_FAST_DCT is set to TRUE */ +#ifndef SBC_IS_64_MULT_IN_IDCT +#define SBC_IS_64_MULT_IN_IDCT FALSE +#endif /*SBC_IS_64_MULT_IN_IDCT */ + +/* set SBC_IS_64_MULT_IN_QUANTIZER to TRUE to use 64 bits multiplication in the quantizer */ +/* setting this flag to FALSE add whistling noise at 5.5 and 11 KHz usualy not perceptible by human's hears. */ +#ifndef SBC_IS_64_MULT_IN_QUANTIZER +#define SBC_IS_64_MULT_IN_QUANTIZER TRUE +#endif /*SBC_IS_64_MULT_IN_IDCT */ + +/* Debug only: set this flag to FALSE to disable fast DCT algorithm */ +#ifndef SBC_FAST_DCT +#define SBC_FAST_DCT TRUE +#endif /*SBC_FAST_DCT */ + +/* In case we do not use joint stereo mode the flag save some RAM and ROM in case it is set to FALSE */ +#ifndef SBC_JOINT_STE_INCLUDED +#define SBC_JOINT_STE_INCLUDED TRUE +#endif + +/* TRUE -> application should provide PCM buffer, FALSE PCM buffer reside in SBC_ENC_PARAMS */ +#ifndef SBC_NO_PCM_CPY_OPTION +#define SBC_NO_PCM_CPY_OPTION FALSE +#endif + +#define MINIMUM_ENC_VX_BUFFER_SIZE (8*10*2) +#ifndef ENC_VX_BUFFER_SIZE +#define ENC_VX_BUFFER_SIZE (MINIMUM_ENC_VX_BUFFER_SIZE + 64) +/*#define ENC_VX_BUFFER_SIZE MINIMUM_ENC_VX_BUFFER_SIZE + 1024*/ +#endif + +#ifndef SBC_FOR_EMBEDDED_LINUX +#define SBC_FOR_EMBEDDED_LINUX FALSE +#endif + +/*constants used for index calculation*/ +#define SBC_BLK (SBC_MAX_NUM_OF_CHANNELS * SBC_MAX_NUM_OF_SUBBANDS) + +#include "sbc_types.h" + +typedef struct SBC_ENC_PARAMS_TAG { + SINT16 s16SamplingFreq; /* 16k, 32k, 44.1k or 48k*/ + SINT16 s16ChannelMode; /* mono, dual, streo or joint streo*/ + SINT16 s16NumOfSubBands; /* 4 or 8 */ + SINT16 s16NumOfChannels; + SINT16 s16NumOfBlocks; /* 4, 8, 12 or 16*/ + SINT16 s16AllocationMethod; /* loudness or SNR*/ + SINT16 s16BitPool; /* 16*numOfSb for mono & dual; + 32*numOfSb for stereo & joint stereo */ + UINT16 u16BitRate; + UINT8 sbc_mode; /* SBC_MODE_STD or SBC_MODE_MSBC */ + UINT8 u8NumPacketToEncode; /* number of sbc frame to encode. Default is 1 */ +#if (SBC_JOINT_STE_INCLUDED == TRUE) + SINT16 as16Join[SBC_MAX_NUM_OF_SUBBANDS]; /*1 if JS, 0 otherwise*/ +#endif + + SINT16 s16MaxBitNeed; + SINT16 as16ScaleFactor[SBC_MAX_NUM_OF_CHANNELS * SBC_MAX_NUM_OF_SUBBANDS]; + + SINT16 *ps16NextPcmBuffer; +#if (SBC_NO_PCM_CPY_OPTION == TRUE) + SINT16 *ps16PcmBuffer; +#else + SINT16 as16PcmBuffer[SBC_MAX_NUM_FRAME * SBC_MAX_NUM_OF_BLOCKS * SBC_MAX_NUM_OF_CHANNELS * SBC_MAX_NUM_OF_SUBBANDS]; +#endif + + SINT16 s16ScartchMemForBitAlloc[16]; + + SINT32 s32SbBuffer[SBC_MAX_NUM_OF_CHANNELS * SBC_MAX_NUM_OF_SUBBANDS * SBC_MAX_NUM_OF_BLOCKS]; + + SINT16 as16Bits[SBC_MAX_NUM_OF_CHANNELS * SBC_MAX_NUM_OF_SUBBANDS]; + + UINT8 *pu8Packet; + UINT8 *pu8NextPacket; + UINT16 FrameHeader; + UINT16 u16PacketLength; + +} SBC_ENC_PARAMS; + +#ifdef __cplusplus +extern "C" +{ +#endif +extern void SBC_Encoder(SBC_ENC_PARAMS *strEncParams); +extern void SBC_Encoder_Init(SBC_ENC_PARAMS *strEncParams); +#ifdef __cplusplus +} +#endif +#endif diff --git a/lib/bt/host/bluedroid/external/sbc/encoder/include/sbc_if.h b/lib/bt/host/bluedroid/external/sbc/encoder/include/sbc_if.h new file mode 100644 index 00000000..993b0663 --- /dev/null +++ b/lib/bt/host/bluedroid/external/sbc/encoder/include/sbc_if.h @@ -0,0 +1,47 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +#ifndef _SBC_IF_H +#define _SBC_IF_H + +#define PCM_BUFFER_SIZE 512 + +/* + SBC_Init - called once for each track played + + pcm_sample_freq - 4000 to 48000 + channels - 1 mono 2 stereo + bits_per_sample - 8 or 16 + return - 0 sucess +*/ + +int SBC_init(int pcm_sample_freq, int channels, int bits_per_sample); + +/* + SBC_write - called repeatedly with pcm_in pointer + increasing by length until track is finished. + + pcm_in - pointer to PCM buffer + length - any + sbc_out - pointer to SBC output buffer + return - number of bytes written to sbc_out +*/ + +int SBC_write(unsigned char *pcm_in, int length, unsigned char *sbc_out); + +#endif diff --git a/lib/bt/host/bluedroid/external/sbc/encoder/include/sbc_types.h b/lib/bt/host/bluedroid/external/sbc/encoder/include/sbc_types.h new file mode 100644 index 00000000..c6e4575b --- /dev/null +++ b/lib/bt/host/bluedroid/external/sbc/encoder/include/sbc_types.h @@ -0,0 +1,59 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * Data type declarations. + * + ******************************************************************************/ + +#ifndef SBC_TYPES_H +#define SBC_TYPES_H + +#include + +#ifdef BUILDCFG +#include "common/bt_target.h" +#endif + +#include "stack/bt_types.h" + +typedef short SINT16; +typedef long SINT32; + +#if (SBC_IPAQ_OPT == TRUE) + +#if (SBC_FOR_EMBEDDED_LINUX == TRUE) +typedef long long SINT64; +#else +typedef int64_t SINT64; +#endif + +#elif (SBC_IS_64_MULT_IN_WINDOW_ACCU == TRUE) || (SBC_IS_64_MULT_IN_IDCT == TRUE) + +#if (SBC_FOR_EMBEDDED_LINUX == TRUE) +typedef long long SINT64; +#else +typedef int64_t SINT64; +#endif + +#endif + +#define abs32(x) ( (x >= 0) ? x : (-x) ) + +#endif diff --git a/lib/bt/host/bluedroid/external/sbc/encoder/srce/sbc_analysis.c b/lib/bt/host/bluedroid/external/sbc/encoder/srce/sbc_analysis.c new file mode 100644 index 00000000..a2d6db43 --- /dev/null +++ b/lib/bt/host/bluedroid/external/sbc/encoder/srce/sbc_analysis.c @@ -0,0 +1,1104 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains the code that performs Analysis of the input audio + * stream. + * + ******************************************************************************/ +#include "common/bt_target.h" +#include +#include "sbc_encoder.h" +#include "sbc_enc_func_declare.h" +#include "osi/allocator.h" +/*#include */ +#if (defined(SBC_ENC_INCLUDED) && SBC_ENC_INCLUDED == TRUE) + +#if (SBC_IS_64_MULT_IN_WINDOW_ACCU == TRUE) +#define WIND_4_SUBBANDS_0_1 (SINT32)0x01659F45 /* gas32CoeffFor4SBs[8] = -gas32CoeffFor4SBs[32] = 0x01659F45 */ +#define WIND_4_SUBBANDS_0_2 (SINT32)0x115B1ED2 /* gas32CoeffFor4SBs[16] = -gas32CoeffFor4SBs[24] = 0x115B1ED2 */ +#define WIND_4_SUBBANDS_1_0 (SINT32)0x001194E6 /* gas32CoeffFor4SBs[1 et 39] = 0x001194E6 */ +#define WIND_4_SUBBANDS_1_1 (SINT32)0x029DBAA3 /* gas32CoeffFor4SBs[9 et 31] = 0x029DBAA3 */ +#define WIND_4_SUBBANDS_1_2 (SINT32)0x18F55C90 /* gas32CoeffFor4SBs[17 et 23] = 0x18F55C90 */ +#define WIND_4_SUBBANDS_1_3 (SINT32)0xF60FAF37 /* gas32CoeffFor4SBs[15 et 25] = 0xF60FAF37 */ +#define WIND_4_SUBBANDS_1_4 (SINT32)0xFF9BB9D5 /* gas32CoeffFor4SBs[7 et 33] = 0xFF9BB9D5 */ +#define WIND_4_SUBBANDS_2_0 (SINT32)0x0030E2D3 /* gas32CoeffFor4SBs[2 et 38] = 0x0030E2D3 */ +#define WIND_4_SUBBANDS_2_1 (SINT32)0x03B23341 /* gas32CoeffFor4SBs[10 et 30] = 0x03B23341 */ +#define WIND_4_SUBBANDS_2_2 (SINT32)0x1F91CA46 /* gas32CoeffFor4SBs[18 et 22] = 0x1F91CA46 */ +#define WIND_4_SUBBANDS_2_3 (SINT32)0xFC4F91D4 /* gas32CoeffFor4SBs[14 et 26] = 0xFC4F91D4 */ +#define WIND_4_SUBBANDS_2_4 (SINT32)0x003D239B /* gas32CoeffFor4SBs[6 et 34] = 0x003D239B */ +#define WIND_4_SUBBANDS_3_0 (SINT32)0x00599403 /* gas32CoeffFor4SBs[3 et 37] = 0x00599403 */ +#define WIND_4_SUBBANDS_3_1 (SINT32)0x041EEE40 /* gas32CoeffFor4SBs[11 et 29] = 0x041EEE40 */ +#define WIND_4_SUBBANDS_3_2 (SINT32)0x2412F251 /* gas32CoeffFor4SBs[19 et 21] = 0x2412F251 */ +#define WIND_4_SUBBANDS_3_3 (SINT32)0x00C8F2BC /* gas32CoeffFor4SBs[13 et 27] = 0x00C8F2BC */ +#define WIND_4_SUBBANDS_3_4 (SINT32)0x007F88E4 /* gas32CoeffFor4SBs[5 et 35] = 0x007F88E4 */ +#define WIND_4_SUBBANDS_4_0 (SINT32)0x007DBCC8 /* gas32CoeffFor4SBs[4 et 36] = 0x007DBCC8 */ +#define WIND_4_SUBBANDS_4_1 (SINT32)0x034FEE2C /* gas32CoeffFor4SBs[12 et 28] = 0x034FEE2C */ +#define WIND_4_SUBBANDS_4_2 (SINT32)0x25AC1FF2 /* gas32CoeffFor4SBs[20] = 0x25AC1FF2 */ + +#define WIND_8_SUBBANDS_0_1 (SINT32)0x00B97348 /* 16 0x00B97348 */ +#define WIND_8_SUBBANDS_0_2 (SINT32)0x08B4307A /* 32 0x08B4307A */ +#define WIND_8_SUBBANDS_1_0 (SINT32)0x00052173 /* 1 et 79 = 0x00052173 */ +#define WIND_8_SUBBANDS_1_1 (SINT32)0x01071B96 /* 17 et 63 = 0x01071B96 */ +#define WIND_8_SUBBANDS_1_2 (SINT32)0x0A9F3E9A /* 33 et 47 = 0x0A9F3E9A*/ +#define WIND_8_SUBBANDS_1_3 (SINT32)0xF9312891 /* 31 et 49 = 0xF9312891 */ +#define WIND_8_SUBBANDS_1_4 (SINT32)0xFF8D6793 /* 15 et 65 = 0xFF8D6793 */ +#define WIND_8_SUBBANDS_2_0 (SINT32)0x000B3F71 /* 2 et 78 = 0x000B3F71 */ +#define WIND_8_SUBBANDS_2_1 (SINT32)0x0156B3CA /* 18 et 62 = 0x0156B3CA */ +#define WIND_8_SUBBANDS_2_2 (SINT32)0x0C7D59B6 /* 34 et 46 = 0x0C7D59B6 */ +#define WIND_8_SUBBANDS_2_3 (SINT32)0xFAFF95FC /* 30 et 50 = 0xFAFF95FC */ +#define WIND_8_SUBBANDS_2_4 (SINT32)0xFFC9F10E /* 14 et 66 = 0xFFC9F10E */ +#define WIND_8_SUBBANDS_3_0 (SINT32)0x00122C7D /* 3 et 77 = 0x00122C7D*/ +#define WIND_8_SUBBANDS_3_1 (SINT32)0x01A1B38B /* 19 et 61 = 0x01A1B38B */ +#define WIND_8_SUBBANDS_3_2 (SINT32)0x0E3BB16F /* 35 et 45 = 0x0E3BB16F */ +#define WIND_8_SUBBANDS_3_3 (SINT32)0xFCA86E7E /* 29 et 51 = 0xFCA86E7E */ +#define WIND_8_SUBBANDS_3_4 (SINT32)0xFFFA2413 /* 13 et 67 = 0xFFFA2413 */ +#define WIND_8_SUBBANDS_4_0 (SINT32)0x001AFF89 /* 4 et 66 = 0x001AFF89 */ +#define WIND_8_SUBBANDS_4_1 (SINT32)0x01E0224C /* 20 et 60 = 0x01E0224C */ +#define WIND_8_SUBBANDS_4_2 (SINT32)0x0FC721F9 /* 36 et 44 = 0x0FC721F9 */ +#define WIND_8_SUBBANDS_4_3 (SINT32)0xFE20435D /* 28 et 52 = 0xFE20435D */ +#define WIND_8_SUBBANDS_4_4 (SINT32)0x001D8FD2 /* 12 et 68 = 0x001D8FD2 */ +#define WIND_8_SUBBANDS_5_0 (SINT32)0x00255A62 /* 5 et 75 = 0x00255A62 */ +#define WIND_8_SUBBANDS_5_1 (SINT32)0x0209291F /* 21 et 59 = 0x0209291F */ +#define WIND_8_SUBBANDS_5_2 (SINT32)0x110ECEF0 /* 37 et 43 = 0x110ECEF0 */ +#define WIND_8_SUBBANDS_5_3 (SINT32)0xFF5EEB73 /* 27 et 53 = 0xFF5EEB73 */ +#define WIND_8_SUBBANDS_5_4 (SINT32)0x0034F8B6 /* 11 et 69 = 0x0034F8B6 */ +#define WIND_8_SUBBANDS_6_0 (SINT32)0x003060F4 /* 6 et 74 = 0x003060F4 */ +#define WIND_8_SUBBANDS_6_1 (SINT32)0x02138653 /* 22 et 58 = 0x02138653 */ +#define WIND_8_SUBBANDS_6_2 (SINT32)0x120435FA /* 38 et 42 = 0x120435FA */ +#define WIND_8_SUBBANDS_6_3 (SINT32)0x005FD0FF /* 26 et 54 = 0x005FD0FF */ +#define WIND_8_SUBBANDS_6_4 (SINT32)0x00415B75 /* 10 et 70 = 0x00415B75 */ +#define WIND_8_SUBBANDS_7_0 (SINT32)0x003A72E7 /* 7 et 73 = 0x003A72E7 */ +#define WIND_8_SUBBANDS_7_1 (SINT32)0x01F5F424 /* 23 et 57 = 0x01F5F424 */ +#define WIND_8_SUBBANDS_7_2 (SINT32)0x129C226F /* 39 et 41 = 0x129C226F */ +#define WIND_8_SUBBANDS_7_3 (SINT32)0x01223EBA /* 25 et 55 = 0x01223EBA */ +#define WIND_8_SUBBANDS_7_4 (SINT32)0x0044EF48 /* 9 et 71 = 0x0044EF48 */ +#define WIND_8_SUBBANDS_8_0 (SINT32)0x0041EC6A /* 8 et 72 = 0x0041EC6A */ +#define WIND_8_SUBBANDS_8_1 (SINT32)0x01A7ECEF /* 24 et 56 = 0x01A7ECEF */ +#define WIND_8_SUBBANDS_8_2 (SINT32)0x12CF6C75 /* 40 = 0x12CF6C75 */ +#else +#define WIND_4_SUBBANDS_0_1 (SINT16)0x0166 /* gas32CoeffFor4SBs[8] = -gas32CoeffFor4SBs[32] = 0x01659F45 */ +#define WIND_4_SUBBANDS_0_2 (SINT16)0x115B /* gas32CoeffFor4SBs[16] = -gas32CoeffFor4SBs[24] = 0x115B1ED2 */ +#define WIND_4_SUBBANDS_1_0 (SINT16)0x0012 /* gas32CoeffFor4SBs[1 et 39] = 0x001194E6 */ +#define WIND_4_SUBBANDS_1_1 (SINT16)0x029E /* gas32CoeffFor4SBs[9 et 31] = 0x029DBAA3 */ +#define WIND_4_SUBBANDS_1_2 (SINT16)0x18F5 /* gas32CoeffFor4SBs[17 et 23] = 0x18F55C90 */ +#define WIND_4_SUBBANDS_1_3 (SINT16)0xF610 /* gas32CoeffFor4SBs[15 et 25] = 0xF60FAF37 */ +#define WIND_4_SUBBANDS_1_4 (SINT16)0xFF9C /* gas32CoeffFor4SBs[7 et 33] = 0xFF9BB9D5 */ +#define WIND_4_SUBBANDS_2_0 (SINT16)0x0031 /* gas32CoeffFor4SBs[2 et 38] = 0x0030E2D3 */ +#define WIND_4_SUBBANDS_2_1 (SINT16)0x03B2 /* gas32CoeffFor4SBs[10 et 30] = 0x03B23341 */ +#define WIND_4_SUBBANDS_2_2 (SINT16)0x1F91 /* gas32CoeffFor4SBs[18 et 22] = 0x1F91CA46 */ +#define WIND_4_SUBBANDS_2_3 (SINT16)0xFC50 /* gas32CoeffFor4SBs[14 et 26] = 0xFC4F91D4 */ +#define WIND_4_SUBBANDS_2_4 (SINT16)0x003D /* gas32CoeffFor4SBs[6 et 34] = 0x003D239B */ +#define WIND_4_SUBBANDS_3_0 (SINT16)0x005A /* gas32CoeffFor4SBs[3 et 37] = 0x00599403 */ +#define WIND_4_SUBBANDS_3_1 (SINT16)0x041F /* gas32CoeffFor4SBs[11 et 29] = 0x041EEE40 */ +#define WIND_4_SUBBANDS_3_2 (SINT16)0x2413 /* gas32CoeffFor4SBs[19 et 21] = 0x2412F251 */ +#define WIND_4_SUBBANDS_3_3 (SINT16)0x00C9 /* gas32CoeffFor4SBs[13 et 27] = 0x00C8F2BC */ +#define WIND_4_SUBBANDS_3_4 (SINT16)0x0080 /* gas32CoeffFor4SBs[5 et 35] = 0x007F88E4 */ +#define WIND_4_SUBBANDS_4_0 (SINT16)0x007E /* gas32CoeffFor4SBs[4 et 36] = 0x007DBCC8 */ +#define WIND_4_SUBBANDS_4_1 (SINT16)0x0350 /* gas32CoeffFor4SBs[12 et 28] = 0x034FEE2C */ +#define WIND_4_SUBBANDS_4_2 (SINT16)0x25AC /* gas32CoeffFor4SBs[20] = 25AC1FF2 */ + +#define WIND_8_SUBBANDS_0_1 (SINT16)0x00B9 /* 16 0x12CF6C75 */ +#define WIND_8_SUBBANDS_0_2 (SINT16)0x08B4 /* 32 0x08B4307A */ +#define WIND_8_SUBBANDS_1_0 (SINT16)0x0005 /* 1 et 79 = 0x00052173 */ +#define WIND_8_SUBBANDS_1_1 (SINT16)0x0107 /* 17 et 63 = 0x01071B96 */ +#define WIND_8_SUBBANDS_1_2 (SINT16)0x0A9F /* 33 et 47 = 0x0A9F3E9A*/ +#define WIND_8_SUBBANDS_1_3 (SINT16)0xF931 /* 31 et 49 = 0xF9312891 */ +#define WIND_8_SUBBANDS_1_4 (SINT16)0xFF8D /* 15 et 65 = 0xFF8D6793 */ +#define WIND_8_SUBBANDS_2_0 (SINT16)0x000B /* 2 et 78 = 0x000B3F71 */ +#define WIND_8_SUBBANDS_2_1 (SINT16)0x0157 /* 18 et 62 = 0x0156B3CA */ +#define WIND_8_SUBBANDS_2_2 (SINT16)0x0C7D /* 34 et 46 = 0x0C7D59B6 */ +#define WIND_8_SUBBANDS_2_3 (SINT16)0xFB00 /* 30 et 50 = 0xFAFF95FC */ +#define WIND_8_SUBBANDS_2_4 (SINT16)0xFFCA /* 14 et 66 = 0xFFC9F10E */ +#define WIND_8_SUBBANDS_3_0 (SINT16)0x0012 /* 3 et 77 = 0x00122C7D*/ +#define WIND_8_SUBBANDS_3_1 (SINT16)0x01A2 /* 19 et 61 = 0x01A1B38B */ +#define WIND_8_SUBBANDS_3_2 (SINT16)0x0E3C /* 35 et 45 = 0x0E3BB16F */ +#define WIND_8_SUBBANDS_3_3 (SINT16)0xFCA8 /* 29 et 51 = 0xFCA86E7E */ +#define WIND_8_SUBBANDS_3_4 (SINT16)0xFFFA /* 13 et 67 = 0xFFFA2413 */ +#define WIND_8_SUBBANDS_4_0 (SINT16)0x001B /* 4 et 66 = 0x001AFF89 */ +#define WIND_8_SUBBANDS_4_1 (SINT16)0x01E0 /* 20 et 60 = 0x01E0224C */ +#define WIND_8_SUBBANDS_4_2 (SINT16)0x0FC7 /* 36 et 44 = 0x0FC721F9 */ +#define WIND_8_SUBBANDS_4_3 (SINT16)0xFE20 /* 28 et 52 = 0xFE20435D */ +#define WIND_8_SUBBANDS_4_4 (SINT16)0x001E /* 12 et 68 = 0x001D8FD2 */ +#define WIND_8_SUBBANDS_5_0 (SINT16)0x0025 /* 5 et 75 = 0x00255A62 */ +#define WIND_8_SUBBANDS_5_1 (SINT16)0x0209 /* 21 et 59 = 0x0209291F */ +#define WIND_8_SUBBANDS_5_2 (SINT16)0x110F /* 37 et 43 = 0x110ECEF0 */ +#define WIND_8_SUBBANDS_5_3 (SINT16)0xFF5F /* 27 et 53 = 0xFF5EEB73 */ +#define WIND_8_SUBBANDS_5_4 (SINT16)0x0035 /* 11 et 69 = 0x0034F8B6 */ +#define WIND_8_SUBBANDS_6_0 (SINT16)0x0030 /* 6 et 74 = 0x003060F4 */ +#define WIND_8_SUBBANDS_6_1 (SINT16)0x0214 /* 22 et 58 = 0x02138653 */ +#define WIND_8_SUBBANDS_6_2 (SINT16)0x1204 /* 38 et 42 = 0x120435FA */ +#define WIND_8_SUBBANDS_6_3 (SINT16)0x0060 /* 26 et 54 = 0x005FD0FF */ +#define WIND_8_SUBBANDS_6_4 (SINT16)0x0041 /* 10 et 70 = 0x00415B75 */ +#define WIND_8_SUBBANDS_7_0 (SINT16)0x003A /* 7 et 73 = 0x003A72E7 */ +#define WIND_8_SUBBANDS_7_1 (SINT16)0x01F6 /* 23 et 57 = 0x01F5F424 */ +#define WIND_8_SUBBANDS_7_2 (SINT16)0x129C /* 39 et 41 = 0x129C226F */ +#define WIND_8_SUBBANDS_7_3 (SINT16)0x0122 /* 25 et 55 = 0x01223EBA */ +#define WIND_8_SUBBANDS_7_4 (SINT16)0x0045 /* 9 et 71 = 0x0044EF48 */ +#define WIND_8_SUBBANDS_8_0 (SINT16)0x0042 /* 8 et 72 = 0x0041EC6A */ +#define WIND_8_SUBBANDS_8_1 (SINT16)0x01A8 /* 24 et 56 = 0x01A7ECEF */ +#define WIND_8_SUBBANDS_8_2 (SINT16)0x12CF /* 40 = 0x12CF6C75 */ +#endif + +#if (SBC_USE_ARM_PRAGMA==TRUE) +#pragma arm section zidata = "sbc_s32_analysis_section" +#endif +#if BT_BLE_DYNAMIC_ENV_MEMORY == FALSE +static SINT32 s32DCTY[16] = {0}; +static SINT32 s32X[ENC_VX_BUFFER_SIZE / 2]; +static SINT16 *s16X = (SINT16 *) s32X; /* s16X must be 32 bits aligned cf SHIFTUP_X8_2*/ +#else +static SINT32 *s32DCTY; +static SINT32 *s32X; +static SINT16 *s16X; /* s16X must be 32 bits aligned cf SHIFTUP_X8_2*/ +#endif //BT_BLE_DYNAMIC_ENV_MEMORY == FALSE + +#if (SBC_USE_ARM_PRAGMA==TRUE) +#pragma arm section zidata +#endif + +/* This macro is for 4 subbands */ +#define SHIFTUP_X4 \ +{ \ + ps32X=(SINT32 *)(s16X+EncMaxShiftCounter+38); \ + for (i=0;i<9;i++) \ + { \ + *ps32X=*(ps32X-2-(ShiftCounter>>1)); ps32X--; \ + *ps32X=*(ps32X-2-(ShiftCounter>>1)); ps32X--; \ + } \ +} +#define SHIFTUP_X4_2 \ +{ \ + ps32X=(SINT32 *)(s16X+EncMaxShiftCounter+38); \ + ps32X2=(SINT32 *)(s16X+(EncMaxShiftCounter<<1)+78); \ + for (i=0;i<9;i++) \ + { \ + *ps32X=*(ps32X-2-(ShiftCounter>>1)); *(ps32X2)=*(ps32X2-2-(ShiftCounter>>1)); ps32X--; ps32X2--; \ + *ps32X=*(ps32X-2-(ShiftCounter>>1)); *(ps32X2)=*(ps32X2-2-(ShiftCounter>>1)); ps32X--; ps32X2--; \ + } \ +} + +/* This macro is for 8 subbands */ +#define SHIFTUP_X8 \ +{ \ + ps32X=(SINT32 *)(s16X+EncMaxShiftCounter+78); \ + for (i=0;i<9;i++) \ + { \ + *ps32X=*(ps32X-4-(ShiftCounter>>1)); ps32X--; \ + *ps32X=*(ps32X-4-(ShiftCounter>>1)); ps32X--; \ + *ps32X=*(ps32X-4-(ShiftCounter>>1)); ps32X--; \ + *ps32X=*(ps32X-4-(ShiftCounter>>1)); ps32X--; \ + } \ +} +#define SHIFTUP_X8_2 \ +{ \ + ps32X=(SINT32 *)(s16X+EncMaxShiftCounter+78); \ + ps32X2=(SINT32 *)(s16X+(EncMaxShiftCounter<<1)+158); \ + for (i=0;i<9;i++) \ + { \ + *ps32X=*(ps32X-4-(ShiftCounter>>1)); *(ps32X2)=*(ps32X2-4-(ShiftCounter>>1)); ps32X--; ps32X2--; \ + *ps32X=*(ps32X-4-(ShiftCounter>>1)); *(ps32X2)=*(ps32X2-4-(ShiftCounter>>1)); ps32X--; ps32X2--; \ + *ps32X=*(ps32X-4-(ShiftCounter>>1)); *(ps32X2)=*(ps32X2-4-(ShiftCounter>>1)); ps32X--; ps32X2--; \ + *ps32X=*(ps32X-4-(ShiftCounter>>1)); *(ps32X2)=*(ps32X2-4-(ShiftCounter>>1)); ps32X--; ps32X2--; \ + } \ +} + +#if (SBC_ARM_ASM_OPT==TRUE) +#define WINDOW_ACCU_8_0 \ +{\ + __asm\ + {\ + MUL s32Hi,WIND_8_SUBBANDS_0_1,(s16X[ChOffset+16]-s16X[ChOffset+64]);\ + MLA s32Hi,WIND_8_SUBBANDS_0_2,(s16X[ChOffset+32]-s16X[ChOffset+48]),s32Hi;\ + MOV s32DCTY[0],s32Hi;\ + }\ +} +#define WINDOW_ACCU_8_1_15 \ +{\ + __asm\ + {\ + MUL s32Hi,WIND_8_SUBBANDS_1_0,s16X[ChOffset+1];\ + MUL s32Hi2,WIND_8_SUBBANDS_1_0,s16X[ChOffset+64+15];\ + MLA s32Hi,WIND_8_SUBBANDS_1_1,s16X[ChOffset+16+1],s32Hi;\ + MLA s32Hi2,WIND_8_SUBBANDS_1_1,s16X[ChOffset+48+15],s32Hi2;\ + MLA s32Hi,WIND_8_SUBBANDS_1_2,s16X[ChOffset+32+1],s32Hi;\ + MLA s32Hi2,WIND_8_SUBBANDS_1_2,s16X[ChOffset+32+15],s32Hi2;\ + MLA s32Hi,WIND_8_SUBBANDS_1_3,s16X[ChOffset+48+1],s32Hi;\ + MLA s32Hi2,WIND_8_SUBBANDS_1_3,s16X[ChOffset+16+15],s32Hi2;\ + MLA s32Hi,WIND_8_SUBBANDS_1_4,s16X[ChOffset+64+1],s32Hi;\ + MLA s32Hi2,WIND_8_SUBBANDS_1_4,s16X[ChOffset+15],s32Hi2;\ + MOV s32DCTY[1],s32Hi;\ + MOV s32DCTY[15],s32Hi2;\ + }\ +} +#define WINDOW_ACCU_8_2_14 \ +{\ + __asm\ + {\ + MUL s32Hi,WIND_8_SUBBANDS_2_0,s16X[ChOffset+2];\ + MUL s32Hi2,WIND_8_SUBBANDS_2_0,s16X[ChOffset+64+14];\ + MLA s32Hi,WIND_8_SUBBANDS_2_1,s16X[ChOffset+16+2],s32Hi;\ + MLA s32Hi2,WIND_8_SUBBANDS_2_1,s16X[ChOffset+48+14],s32Hi2;\ + MLA s32Hi,WIND_8_SUBBANDS_2_2,s16X[ChOffset+32+2],s32Hi;\ + MLA s32Hi2,WIND_8_SUBBANDS_2_2,s16X[ChOffset+32+14],s32Hi2;\ + MLA s32Hi,WIND_8_SUBBANDS_2_3,s16X[ChOffset+48+2],s32Hi;\ + MLA s32Hi2,WIND_8_SUBBANDS_2_3,s16X[ChOffset+16+14],s32Hi2;\ + MLA s32Hi,WIND_8_SUBBANDS_2_4,s16X[ChOffset+64+2],s32Hi;\ + MLA s32Hi2,WIND_8_SUBBANDS_2_4,s16X[ChOffset+14],s32Hi2;\ + MOV s32DCTY[2],s32Hi;\ + MOV s32DCTY[14],s32Hi2;\ + }\ +} +#define WINDOW_ACCU_8_3_13 \ +{\ + __asm\ + {\ + MUL s32Hi,WIND_8_SUBBANDS_3_0,s16X[ChOffset+3];\ + MUL s32Hi2,WIND_8_SUBBANDS_3_0,s16X[ChOffset+64+13];\ + MLA s32Hi,WIND_8_SUBBANDS_3_1,s16X[ChOffset+16+3],s32Hi;\ + MLA s32Hi2,WIND_8_SUBBANDS_3_1,s16X[ChOffset+48+13],s32Hi2;\ + MLA s32Hi,WIND_8_SUBBANDS_3_2,s16X[ChOffset+32+3],s32Hi;\ + MLA s32Hi2,WIND_8_SUBBANDS_3_2,s16X[ChOffset+32+13],s32Hi2;\ + MLA s32Hi,WIND_8_SUBBANDS_3_3,s16X[ChOffset+48+3],s32Hi;\ + MLA s32Hi2,WIND_8_SUBBANDS_3_3,s16X[ChOffset+16+13],s32Hi2;\ + MLA s32Hi,WIND_8_SUBBANDS_3_4,s16X[ChOffset+64+3],s32Hi;\ + MLA s32Hi2,WIND_8_SUBBANDS_3_4,s16X[ChOffset+13],s32Hi2;\ + MOV s32DCTY[3],s32Hi;\ + MOV s32DCTY[13],s32Hi2;\ + }\ +} +#define WINDOW_ACCU_8_4_12 \ +{\ + __asm\ + {\ + MUL s32Hi,WIND_8_SUBBANDS_4_0,s16X[ChOffset+4];\ + MUL s32Hi2,WIND_8_SUBBANDS_4_0,s16X[ChOffset+64+12];\ + MLA s32Hi,WIND_8_SUBBANDS_4_1,s16X[ChOffset+16+4],s32Hi;\ + MLA s32Hi2,WIND_8_SUBBANDS_4_1,s16X[ChOffset+48+12],s32Hi2;\ + MLA s32Hi,WIND_8_SUBBANDS_4_2,s16X[ChOffset+32+4],s32Hi;\ + MLA s32Hi2,WIND_8_SUBBANDS_4_2,s16X[ChOffset+32+12],s32Hi2;\ + MLA s32Hi,WIND_8_SUBBANDS_4_3,s16X[ChOffset+48+4],s32Hi;\ + MLA s32Hi2,WIND_8_SUBBANDS_4_3,s16X[ChOffset+16+12],s32Hi2;\ + MLA s32Hi,WIND_8_SUBBANDS_4_4,s16X[ChOffset+64+4],s32Hi;\ + MLA s32Hi2,WIND_8_SUBBANDS_4_4,s16X[ChOffset+12],s32Hi2;\ + MOV s32DCTY[4],s32Hi;\ + MOV s32DCTY[12],s32Hi2;\ + }\ +} +#define WINDOW_ACCU_8_5_11 \ +{\ + __asm\ + {\ + MUL s32Hi,WIND_8_SUBBANDS_5_0,s16X[ChOffset+5];\ + MUL s32Hi2,WIND_8_SUBBANDS_5_0,s16X[ChOffset+64+11];\ + MLA s32Hi,WIND_8_SUBBANDS_5_1,s16X[ChOffset+16+5],s32Hi;\ + MLA s32Hi2,WIND_8_SUBBANDS_5_1,s16X[ChOffset+48+11],s32Hi2;\ + MLA s32Hi,WIND_8_SUBBANDS_5_2,s16X[ChOffset+32+5],s32Hi;\ + MLA s32Hi2,WIND_8_SUBBANDS_5_2,s16X[ChOffset+32+11],s32Hi2;\ + MLA s32Hi,WIND_8_SUBBANDS_5_3,s16X[ChOffset+48+5],s32Hi;\ + MLA s32Hi2,WIND_8_SUBBANDS_5_3,s16X[ChOffset+16+11],s32Hi2;\ + MLA s32Hi,WIND_8_SUBBANDS_5_4,s16X[ChOffset+64+5],s32Hi;\ + MLA s32Hi2,WIND_8_SUBBANDS_5_4,s16X[ChOffset+11],s32Hi2;\ + MOV s32DCTY[5],s32Hi;\ + MOV s32DCTY[11],s32Hi2;\ + }\ +} +#define WINDOW_ACCU_8_6_10 \ +{\ + __asm\ + {\ + MUL s32Hi,WIND_8_SUBBANDS_6_0,s16X[ChOffset+6];\ + MUL s32Hi2,WIND_8_SUBBANDS_6_0,s16X[ChOffset+64+10];\ + MLA s32Hi,WIND_8_SUBBANDS_6_1,s16X[ChOffset+16+6],s32Hi;\ + MLA s32Hi2,WIND_8_SUBBANDS_6_1,s16X[ChOffset+48+10],s32Hi2;\ + MLA s32Hi,WIND_8_SUBBANDS_6_2,s16X[ChOffset+32+6],s32Hi;\ + MLA s32Hi2,WIND_8_SUBBANDS_6_2,s16X[ChOffset+32+10],s32Hi2;\ + MLA s32Hi,WIND_8_SUBBANDS_6_3,s16X[ChOffset+48+6],s32Hi;\ + MLA s32Hi2,WIND_8_SUBBANDS_6_3,s16X[ChOffset+16+10],s32Hi2;\ + MLA s32Hi,WIND_8_SUBBANDS_6_4,s16X[ChOffset+64+6],s32Hi;\ + MLA s32Hi2,WIND_8_SUBBANDS_6_4,s16X[ChOffset+10],s32Hi2;\ + MOV s32DCTY[6],s32Hi;\ + MOV s32DCTY[10],s32Hi2;\ + }\ +} +#define WINDOW_ACCU_8_7_9 \ +{\ + __asm\ + {\ + MUL s32Hi,WIND_8_SUBBANDS_7_0,s16X[ChOffset+7];\ + MUL s32Hi2,WIND_8_SUBBANDS_7_0,s16X[ChOffset+64+9];\ + MLA s32Hi,WIND_8_SUBBANDS_7_1,s16X[ChOffset+16+7],s32Hi;\ + MLA s32Hi2,WIND_8_SUBBANDS_7_1,s16X[ChOffset+48+9],s32Hi2;\ + MLA s32Hi,WIND_8_SUBBANDS_7_2,s16X[ChOffset+32+7],s32Hi;\ + MLA s32Hi2,WIND_8_SUBBANDS_7_2,s16X[ChOffset+32+9],s32Hi2;\ + MLA s32Hi,WIND_8_SUBBANDS_7_3,s16X[ChOffset+48+7],s32Hi;\ + MLA s32Hi2,WIND_8_SUBBANDS_7_3,s16X[ChOffset+16+9],s32Hi2;\ + MLA s32Hi,WIND_8_SUBBANDS_7_4,s16X[ChOffset+64+7],s32Hi;\ + MLA s32Hi2,WIND_8_SUBBANDS_7_4,s16X[ChOffset+9],s32Hi2;\ + MOV s32DCTY[7],s32Hi;\ + MOV s32DCTY[9],s32Hi2;\ + }\ +} +#define WINDOW_ACCU_8_8 \ +{\ + __asm\ + {\ + MUL s32Hi,WIND_8_SUBBANDS_8_0,(s16X[ChOffset+8]+s16X[ChOffset+8+64]);\ + MLA s32Hi,WIND_8_SUBBANDS_8_1,(s16X[ChOffset+8+16]+s16X[ChOffset+8+64]),s32Hi;\ + MLA s32Hi,WIND_8_SUBBANDS_8_2,s16X[ChOffset+8+32],s32Hi;\ + MOV s32DCTY[8],s32Hi;\ + }\ +} +#define WINDOW_ACCU_4_0 \ +{\ + __asm\ + {\ + MUL s32Hi,WIND_4_SUBBANDS_0_1,(s16X[ChOffset+8]-s16X[ChOffset+32]);\ + MLA s32Hi,WIND_4_SUBBANDS_0_2,(s16X[ChOffset+16]-s16X[ChOffset+24]),s32Hi;\ + MOV s32DCTY[0],s32Hi;\ + }\ +} +#define WINDOW_ACCU_4_1_7 \ +{\ + __asm\ + {\ + MUL s32Hi,WIND_4_SUBBANDS_1_0,s16X[ChOffset+1];\ + MUL s32Hi2,WIND_4_SUBBANDS_1_0,s16X[ChOffset+32+7];\ + MLA s32Hi,WIND_4_SUBBANDS_1_1,s16X[ChOffset+8+1],s32Hi;\ + MLA s32Hi2,WIND_4_SUBBANDS_1_1,s16X[ChOffset+24+7],s32Hi2;\ + MLA s32Hi,WIND_4_SUBBANDS_1_2,s16X[ChOffset+16+1],s32Hi;\ + MLA s32Hi2,WIND_4_SUBBANDS_1_2,s16X[ChOffset+16+7],s32Hi2;\ + MLA s32Hi,WIND_4_SUBBANDS_1_3,s16X[ChOffset+24+1],s32Hi;\ + MLA s32Hi2,WIND_4_SUBBANDS_1_3,s16X[ChOffset+8+7],s32Hi2;\ + MLA s32Hi,WIND_4_SUBBANDS_1_4,s16X[ChOffset+32+1],s32Hi;\ + MLA s32Hi2,WIND_4_SUBBANDS_1_4,s16X[ChOffset+7],s32Hi2;\ + MOV s32DCTY[1],s32Hi;\ + MOV s32DCTY[7],s32Hi2;\ + }\ +} +#define WINDOW_ACCU_4_2_6 \ +{\ + __asm\ + {\ + MUL s32Hi,WIND_4_SUBBANDS_2_0,s16X[ChOffset+2];\ + MUL s32Hi2,WIND_4_SUBBANDS_2_0,s16X[ChOffset+32+6];\ + MLA s32Hi,WIND_4_SUBBANDS_2_1,s16X[ChOffset+8+2],s32Hi;\ + MLA s32Hi2,WIND_4_SUBBANDS_2_1,s16X[ChOffset+24+6],s32Hi2;\ + MLA s32Hi,WIND_4_SUBBANDS_2_2,s16X[ChOffset+16+2],s32Hi;\ + MLA s32Hi2,WIND_4_SUBBANDS_2_2,s16X[ChOffset+16+6],s32Hi2;\ + MLA s32Hi,WIND_4_SUBBANDS_2_3,s16X[ChOffset+24+2],s32Hi;\ + MLA s32Hi2,WIND_4_SUBBANDS_2_3,s16X[ChOffset+8+6],s32Hi2;\ + MLA s32Hi,WIND_4_SUBBANDS_2_4,s16X[ChOffset+32+2],s32Hi;\ + MLA s32Hi2,WIND_4_SUBBANDS_2_4,s16X[ChOffset+6],s32Hi2;\ + MOV s32DCTY[2],s32Hi;\ + MOV s32DCTY[6],s32Hi2;\ + }\ +} +#define WINDOW_ACCU_4_3_5 \ +{\ + __asm\ + {\ + MUL s32Hi,WIND_4_SUBBANDS_3_0,s16X[ChOffset+3];\ + MUL s32Hi2,WIND_4_SUBBANDS_3_0,s16X[ChOffset+32+5];\ + MLA s32Hi,WIND_4_SUBBANDS_3_1,s16X[ChOffset+8+3],s32Hi;\ + MLA s32Hi2,WIND_4_SUBBANDS_3_1,s16X[ChOffset+24+5],s32Hi2;\ + MLA s32Hi,WIND_4_SUBBANDS_3_2,s16X[ChOffset+16+3],s32Hi;\ + MLA s32Hi2,WIND_4_SUBBANDS_3_2,s16X[ChOffset+16+5],s32Hi2;\ + MLA s32Hi,WIND_4_SUBBANDS_3_3,s16X[ChOffset+24+3],s32Hi;\ + MLA s32Hi2,WIND_4_SUBBANDS_3_3,s16X[ChOffset+8+5],s32Hi2;\ + MLA s32Hi,WIND_4_SUBBANDS_3_4,s16X[ChOffset+32+3],s32Hi;\ + MLA s32Hi2,WIND_4_SUBBANDS_3_4,s16X[ChOffset+5],s32Hi2;\ + MOV s32DCTY[3],s32Hi;\ + MOV s32DCTY[5],s32Hi2;\ + }\ +} +#define WINDOW_ACCU_4_4 \ +{\ + __asm\ + {\ + MUL s32Hi,WIND_4_SUBBANDS_4_0,(s16X[ChOffset+4]+s16X[ChOffset+4+32]);\ + MLA s32Hi,WIND_4_SUBBANDS_4_1,(s16X[ChOffset+4+8]+s16X[ChOffset+4+24]),s32Hi;\ + MLA s32Hi,WIND_4_SUBBANDS_4_2,s16X[ChOffset+4+16],s32Hi;\ + MOV s32DCTY[4],s32Hi;\ + }\ +} + +#define WINDOW_PARTIAL_4 \ +{\ + WINDOW_ACCU_4_0; WINDOW_ACCU_4_1_7;\ + WINDOW_ACCU_4_2_6; WINDOW_ACCU_4_3_5;\ + WINDOW_ACCU_4_4;\ +} + +#define WINDOW_PARTIAL_8 \ +{\ + WINDOW_ACCU_8_0; WINDOW_ACCU_8_1_15;\ + WINDOW_ACCU_8_2_14; WINDOW_ACCU_8_3_13;\ + WINDOW_ACCU_8_4_12; WINDOW_ACCU_8_5_11;\ + WINDOW_ACCU_8_6_10; WINDOW_ACCU_8_7_9;\ + WINDOW_ACCU_8_8;\ +} + +#else +#if (SBC_IPAQ_OPT==TRUE) + +#if (SBC_IS_64_MULT_IN_WINDOW_ACCU == TRUE) +#define WINDOW_ACCU_8_0 \ +{\ + s64Temp=(SINT64)WIND_8_SUBBANDS_0_1*(SINT64)(s16X[ChOffset+16]-s16X[ChOffset+64]);\ + s64Temp+=(SINT64)WIND_8_SUBBANDS_0_2*(SINT64)(s16X[ChOffset+32]-s16X[ChOffset+48]);\ + s32DCTY[0]=(SINT32)(s64Temp>>16);\ +} +#define WINDOW_ACCU_8_1_15 \ +{\ + s64Temp=(SINT64)WIND_8_SUBBANDS_1_0*(SINT64)s16X[ChOffset+1];\ + s64Temp2=(SINT64)WIND_8_SUBBANDS_1_0*(SINT64)s16X[ChOffset+64+15];\ + s64Temp+=(SINT64)WIND_8_SUBBANDS_1_1*(SINT64)s16X[ChOffset+16+1];\ + s64Temp2+=(SINT64)WIND_8_SUBBANDS_1_1*(SINT64)s16X[ChOffset+48+15];\ + s64Temp+=(SINT64)WIND_8_SUBBANDS_1_2*(SINT64)s16X[ChOffset+32+1];\ + s64Temp2+=(SINT64)WIND_8_SUBBANDS_1_2*(SINT64)s16X[ChOffset+32+15];\ + s64Temp+=(SINT64)WIND_8_SUBBANDS_1_3*(SINT64)s16X[ChOffset+48+1];\ + s64Temp2+=(SINT64)WIND_8_SUBBANDS_1_3*(SINT64)s16X[ChOffset+16+15];\ + s64Temp+=(SINT64)WIND_8_SUBBANDS_1_4*(SINT64)s16X[ChOffset+64+1];\ + s64Temp2+=(SINT64)WIND_8_SUBBANDS_1_4*(SINT64)s16X[ChOffset+15];\ + s32DCTY[1]=(SINT32)(s64Temp>>16);\ + s32DCTY[15]=(SINT32)(s64Temp2>>16);\ +} +#define WINDOW_ACCU_8_2_14 \ +{\ + s64Temp=(SINT64)WIND_8_SUBBANDS_2_0*(SINT64)s16X[ChOffset+2];\ + s64Temp2=(SINT64)WIND_8_SUBBANDS_2_0*(SINT64)s16X[ChOffset+64+14];\ + s64Temp+=(SINT64)WIND_8_SUBBANDS_2_1*(SINT64)s16X[ChOffset+16+2];\ + s64Temp2+=(SINT64)WIND_8_SUBBANDS_2_1*(SINT64)s16X[ChOffset+48+14];\ + s64Temp+=(SINT64)WIND_8_SUBBANDS_2_2*(SINT64)s16X[ChOffset+32+2];\ + s64Temp2+=(SINT64)WIND_8_SUBBANDS_2_2*(SINT64)s16X[ChOffset+32+14];\ + s64Temp+=(SINT64)WIND_8_SUBBANDS_2_3*(SINT64)s16X[ChOffset+48+2];\ + s64Temp2+=(SINT64)WIND_8_SUBBANDS_2_3*(SINT64)s16X[ChOffset+16+14];\ + s64Temp+=(SINT64)WIND_8_SUBBANDS_2_4*(SINT64)s16X[ChOffset+64+2];\ + s64Temp2+=(SINT64)WIND_8_SUBBANDS_2_4*(SINT64)s16X[ChOffset+14];\ + s32DCTY[2]=(SINT32)(s64Temp>>16);\ + s32DCTY[14]=(SINT32)(s64Temp2>>16);\ +} +#define WINDOW_ACCU_8_3_13 \ +{\ + s64Temp=(SINT64)WIND_8_SUBBANDS_3_0*(SINT64)s16X[ChOffset+3];\ + s64Temp2=(SINT64)WIND_8_SUBBANDS_3_0*(SINT64)s16X[ChOffset+64+13];\ + s64Temp+=(SINT64)WIND_8_SUBBANDS_3_1*(SINT64)s16X[ChOffset+16+3];\ + s64Temp2+=(SINT64)WIND_8_SUBBANDS_3_1*(SINT64)s16X[ChOffset+48+13];\ + s64Temp+=(SINT64)WIND_8_SUBBANDS_3_2*(SINT64)s16X[ChOffset+32+3];\ + s64Temp2+=(SINT64)WIND_8_SUBBANDS_3_2*(SINT64)s16X[ChOffset+32+13];\ + s64Temp+=(SINT64)WIND_8_SUBBANDS_3_3*(SINT64)s16X[ChOffset+48+3];\ + s64Temp2+=(SINT64)WIND_8_SUBBANDS_3_3*(SINT64)s16X[ChOffset+16+13];\ + s64Temp+=(SINT64)WIND_8_SUBBANDS_3_4*(SINT64)s16X[ChOffset+64+3];\ + s64Temp2+=(SINT64)WIND_8_SUBBANDS_3_4*(SINT64)s16X[ChOffset+13];\ + s32DCTY[3]=(SINT32)(s64Temp>>16);\ + s32DCTY[13]=(SINT32)(s64Temp2>>16);\ +} +#define WINDOW_ACCU_8_4_12 \ +{\ + s64Temp=(SINT64)WIND_8_SUBBANDS_4_0*(SINT64)s16X[ChOffset+4];\ + s64Temp2=(SINT64)WIND_8_SUBBANDS_4_0*(SINT64)s16X[ChOffset+64+12];\ + s64Temp+=(SINT64)WIND_8_SUBBANDS_4_1*(SINT64)s16X[ChOffset+16+4];\ + s64Temp2+=(SINT64)WIND_8_SUBBANDS_4_1*(SINT64)s16X[ChOffset+48+12];\ + s64Temp+=(SINT64)WIND_8_SUBBANDS_4_2*(SINT64)s16X[ChOffset+32+4];\ + s64Temp2+=(SINT64)WIND_8_SUBBANDS_4_2*(SINT64)s16X[ChOffset+32+12];\ + s64Temp+=(SINT64)WIND_8_SUBBANDS_4_3*(SINT64)s16X[ChOffset+48+4];\ + s64Temp2+=(SINT64)WIND_8_SUBBANDS_4_3*(SINT64)s16X[ChOffset+16+12];\ + s64Temp+=(SINT64)WIND_8_SUBBANDS_4_4*(SINT64)s16X[ChOffset+64+4];\ + s64Temp2+=(SINT64)WIND_8_SUBBANDS_4_4*(SINT64)s16X[ChOffset+12];\ + s32DCTY[4]=(SINT32)(s64Temp>>16);\ + s32DCTY[12]=(SINT32)(s64Temp2>>16);\ +} +#define WINDOW_ACCU_8_5_11 \ +{\ + s64Temp=(SINT64)WIND_8_SUBBANDS_5_0*(SINT64)s16X[ChOffset+5];\ + s64Temp2=(SINT64)WIND_8_SUBBANDS_5_0*(SINT64)s16X[ChOffset+64+11];\ + s64Temp+=(SINT64)WIND_8_SUBBANDS_5_1*(SINT64)s16X[ChOffset+16+5];\ + s64Temp2+=(SINT64)WIND_8_SUBBANDS_5_1*(SINT64)s16X[ChOffset+48+11];\ + s64Temp+=(SINT64)WIND_8_SUBBANDS_5_2*(SINT64)s16X[ChOffset+32+5];\ + s64Temp2+=(SINT64)WIND_8_SUBBANDS_5_2*(SINT64)s16X[ChOffset+32+11];\ + s64Temp+=(SINT64)WIND_8_SUBBANDS_5_3*(SINT64)s16X[ChOffset+48+5];\ + s64Temp2+=(SINT64)WIND_8_SUBBANDS_5_3*(SINT64)s16X[ChOffset+16+11];\ + s64Temp+=(SINT64)WIND_8_SUBBANDS_5_4*(SINT64)s16X[ChOffset+64+5];\ + s64Temp2+=(SINT64)WIND_8_SUBBANDS_5_4*(SINT64)s16X[ChOffset+11];\ + s32DCTY[5]=(SINT32)(s64Temp>>16);\ + s32DCTY[11]=(SINT32)(s64Temp2>>16);\ +} +#define WINDOW_ACCU_8_6_10 \ +{\ + s64Temp=(SINT64)WIND_8_SUBBANDS_6_0*(SINT64)s16X[ChOffset+6];\ + s64Temp2=(SINT64)WIND_8_SUBBANDS_6_0*(SINT64)s16X[ChOffset+64+10];\ + s64Temp+=(SINT64)WIND_8_SUBBANDS_6_1*(SINT64)s16X[ChOffset+16+6];\ + s64Temp2+=(SINT64)WIND_8_SUBBANDS_6_1*(SINT64)s16X[ChOffset+48+10];\ + s64Temp+=(SINT64)WIND_8_SUBBANDS_6_2*(SINT64)s16X[ChOffset+32+6];\ + s64Temp2+=(SINT64)WIND_8_SUBBANDS_6_2*(SINT64)s16X[ChOffset+32+10];\ + s64Temp+=(SINT64)WIND_8_SUBBANDS_6_3*(SINT64)s16X[ChOffset+48+6];\ + s64Temp2+=(SINT64)WIND_8_SUBBANDS_6_3*(SINT64)s16X[ChOffset+16+10];\ + s64Temp+=(SINT64)WIND_8_SUBBANDS_6_4*(SINT64)s16X[ChOffset+64+6];\ + s64Temp2+=(SINT64)WIND_8_SUBBANDS_6_4*(SINT64)s16X[ChOffset+10];\ + s32DCTY[6]=(SINT32)(s64Temp>>16);\ + s32DCTY[10]=(SINT32)(s64Temp2>>16);\ +} +#define WINDOW_ACCU_8_7_9 \ +{\ + s64Temp=(SINT64)WIND_8_SUBBANDS_7_0*(SINT64)s16X[ChOffset+7];\ + s64Temp2=(SINT64)WIND_8_SUBBANDS_7_0*(SINT64)s16X[ChOffset+64+9];\ + s64Temp+=(SINT64)WIND_8_SUBBANDS_7_1*(SINT64)s16X[ChOffset+16+7];\ + s64Temp2+=(SINT64)WIND_8_SUBBANDS_7_1*(SINT64)s16X[ChOffset+48+9];\ + s64Temp+=(SINT64)WIND_8_SUBBANDS_7_2*(SINT64)s16X[ChOffset+32+7];\ + s64Temp2+=(SINT64)WIND_8_SUBBANDS_7_2*(SINT64)s16X[ChOffset+32+9];\ + s64Temp+=(SINT64)WIND_8_SUBBANDS_7_3*(SINT64)s16X[ChOffset+48+7];\ + s64Temp2+=(SINT64)WIND_8_SUBBANDS_7_3*(SINT64)s16X[ChOffset+16+9];\ + s64Temp+=(SINT64)WIND_8_SUBBANDS_7_4*(SINT64)s16X[ChOffset+64+7];\ + s64Temp2+=(SINT64)WIND_8_SUBBANDS_7_4*(SINT64)s16X[ChOffset+9];\ + s32DCTY[7]=(SINT32)(s64Temp>>16);\ + s32DCTY[9]=(SINT32)(s64Temp2>>16);\ +} +#define WINDOW_ACCU_8_8 \ +{\ + s64Temp=(SINT64)WIND_8_SUBBANDS_8_0*(SINT64)(s16X[ChOffset+8]+s16X[ChOffset+64+8]);\ + s64Temp+=(SINT64)WIND_8_SUBBANDS_8_1*(SINT64)(s16X[ChOffset+16+8]+s16X[ChOffset+48+8]);\ + s64Temp+=(SINT64)WIND_8_SUBBANDS_8_2*(SINT64)s16X[ChOffset+32+8];\ + s32DCTY[8]=(SINT32)(s64Temp>>16);\ +} +#define WINDOW_ACCU_4_0 \ +{\ + s64Temp=(SINT64)WIND_4_SUBBANDS_0_1*(SINT64)(s16X[ChOffset+8]-s16X[ChOffset+32]);\ + s64Temp+=(SINT64)WIND_4_SUBBANDS_0_2*(SINT64)(s16X[ChOffset+16]-s16X[ChOffset+24]);\ + s32DCTY[0]=(SINT32)(s64Temp>>16);\ +} +#define WINDOW_ACCU_4_1_7 \ +{\ + s64Temp=(SINT64)WIND_4_SUBBANDS_1_0*(SINT64)s16X[ChOffset+1];\ + s64Temp2=(SINT64)WIND_4_SUBBANDS_1_0*(SINT64)s16X[ChOffset+32+7];\ + s64Temp+=(SINT64)WIND_4_SUBBANDS_1_1*(SINT64)s16X[ChOffset+8+1];\ + s64Temp2+=(SINT64)WIND_4_SUBBANDS_1_1*(SINT64)s16X[ChOffset+24+7];\ + s64Temp+=(SINT64)WIND_4_SUBBANDS_1_2*(SINT64)s16X[ChOffset+16+1];\ + s64Temp2+=(SINT64)WIND_4_SUBBANDS_1_2*(SINT64)s16X[ChOffset+16+7];\ + s64Temp+=(SINT64)WIND_4_SUBBANDS_1_3*(SINT64)s16X[ChOffset+24+1];\ + s64Temp2+=(SINT64)WIND_4_SUBBANDS_1_3*(SINT64)s16X[ChOffset+8+7];\ + s64Temp+=(SINT64)WIND_4_SUBBANDS_1_4*(SINT64)s16X[ChOffset+32+1];\ + s64Temp2+=(SINT64)WIND_4_SUBBANDS_1_4*(SINT64)s16X[ChOffset+7];\ + s32DCTY[1]=(SINT32)(s64Temp>>16);\ + s32DCTY[7]=(SINT32)(s64Temp2>>16);\ +} +#define WINDOW_ACCU_4_2_6 \ +{\ + s64Temp=(SINT64)WIND_4_SUBBANDS_2_0*(SINT64)s16X[ChOffset+2];\ + s64Temp2=(SINT64)WIND_4_SUBBANDS_2_0*(SINT64)s16X[ChOffset+32+6];\ + s64Temp+=(SINT64)WIND_4_SUBBANDS_2_1*(SINT64)s16X[ChOffset+8+2];\ + s64Temp2+=(SINT64)WIND_4_SUBBANDS_2_1*(SINT64)s16X[ChOffset+24+6];\ + s64Temp+=(SINT64)WIND_4_SUBBANDS_2_2*(SINT64)s16X[ChOffset+16+2];\ + s64Temp2+=(SINT64)WIND_4_SUBBANDS_2_2*(SINT64)s16X[ChOffset+16+6];\ + s64Temp+=(SINT64)WIND_4_SUBBANDS_2_3*(SINT64)s16X[ChOffset+24+2];\ + s64Temp2+=(SINT64)WIND_4_SUBBANDS_2_3*(SINT64)s16X[ChOffset+8+6];\ + s64Temp+=(SINT64)WIND_4_SUBBANDS_2_4*(SINT64)s16X[ChOffset+32+2];\ + s64Temp2+=(SINT64)WIND_4_SUBBANDS_2_4*(SINT64)s16X[ChOffset+6];\ + s32DCTY[2]=(SINT32)(s64Temp>>16);\ + s32DCTY[6]=(SINT32)(s64Temp2>>16);\ +} +#define WINDOW_ACCU_4_3_5 \ +{\ + s64Temp=(SINT64)WIND_4_SUBBANDS_3_0*(SINT64)s16X[ChOffset+3];\ + s64Temp2=(SINT64)WIND_4_SUBBANDS_3_0*(SINT64)s16X[ChOffset+32+5];\ + s64Temp+=(SINT64)WIND_4_SUBBANDS_3_1*(SINT64)s16X[ChOffset+8+3];\ + s64Temp2+=(SINT64)WIND_4_SUBBANDS_3_1*(SINT64)s16X[ChOffset+24+5];\ + s64Temp+=(SINT64)WIND_4_SUBBANDS_3_2*(SINT64)s16X[ChOffset+16+3];\ + s64Temp2+=(SINT64)WIND_4_SUBBANDS_3_2*(SINT64)s16X[ChOffset+16+5];\ + s64Temp+=(SINT64)WIND_4_SUBBANDS_3_3*(SINT64)s16X[ChOffset+24+3];\ + s64Temp2+=(SINT64)WIND_4_SUBBANDS_3_3*(SINT64)s16X[ChOffset+8+5];\ + s64Temp+=(SINT64)WIND_4_SUBBANDS_3_4*(SINT64)s16X[ChOffset+32+3];\ + s64Temp2+=(SINT64)WIND_4_SUBBANDS_3_4*(SINT64)s16X[ChOffset+5];\ + s32DCTY[3]=(SINT32)(s64Temp>>16);\ + s32DCTY[5]=(SINT32)(s64Temp2>>16);\ +} + +#define WINDOW_ACCU_4_4 \ +{\ + s64Temp=(SINT64)WIND_4_SUBBANDS_4_0*(SINT64)(s16X[ChOffset+4]+s16X[ChOffset+4+32]);\ + s64Temp+=(SINT64)WIND_4_SUBBANDS_4_1*(SINT64)(s16X[ChOffset+4+8]+s16X[ChOffset+4+24]);\ + s64Temp+=(SINT64)WIND_4_SUBBANDS_4_2*(SINT64)s16X[ChOffset+4+16];\ + s32DCTY[4]=(SINT32)(s64Temp>>16);\ +} +#else /* SBC_IS_64_MULT_IN_WINDOW_ACCU == FALSE */ +#define WINDOW_ACCU_8_0 \ +{\ + s32Temp=(SINT32)WIND_8_SUBBANDS_0_1*(SINT32)(s16X[ChOffset+16]-s16X[ChOffset+64]);\ + s32Temp+=(SINT32)WIND_8_SUBBANDS_0_2*(SINT32)(s16X[ChOffset+32]-s16X[ChOffset+48]);\ + s32DCTY[0]=(SINT32)s32Temp;\ +} +#define WINDOW_ACCU_8_1_15 \ +{\ + s32Temp=(SINT32)WIND_8_SUBBANDS_1_0*(SINT32)s16X[ChOffset+1];\ + s32Temp2=(SINT32)WIND_8_SUBBANDS_1_0*(SINT32)s16X[ChOffset+64+15];\ + s32Temp+=(SINT32)WIND_8_SUBBANDS_1_1*(SINT32)s16X[ChOffset+16+1];\ + s32Temp2+=(SINT32)WIND_8_SUBBANDS_1_1*(SINT32)s16X[ChOffset+48+15];\ + s32Temp+=(SINT32)WIND_8_SUBBANDS_1_2*(SINT32)s16X[ChOffset+32+1];\ + s32Temp2+=(SINT32)WIND_8_SUBBANDS_1_2*(SINT32)s16X[ChOffset+32+15];\ + s32Temp+=(SINT32)WIND_8_SUBBANDS_1_3*(SINT32)s16X[ChOffset+48+1];\ + s32Temp2+=(SINT32)WIND_8_SUBBANDS_1_3*(SINT32)s16X[ChOffset+16+15];\ + s32Temp+=(SINT32)WIND_8_SUBBANDS_1_4*(SINT32)s16X[ChOffset+64+1];\ + s32Temp2+=(SINT32)WIND_8_SUBBANDS_1_4*(SINT32)s16X[ChOffset+15];\ + s32DCTY[1]=(SINT32)s32Temp;\ + s32DCTY[15]=(SINT32)s32Temp2;\ +} +#define WINDOW_ACCU_8_2_14 \ +{\ + s32Temp=(SINT32)WIND_8_SUBBANDS_2_0*(SINT32)s16X[ChOffset+2];\ + s32Temp2=(SINT32)WIND_8_SUBBANDS_2_0*(SINT32)s16X[ChOffset+64+14];\ + s32Temp+=(SINT32)WIND_8_SUBBANDS_2_1*(SINT32)s16X[ChOffset+16+2];\ + s32Temp2+=(SINT32)WIND_8_SUBBANDS_2_1*(SINT32)s16X[ChOffset+48+14];\ + s32Temp+=(SINT32)WIND_8_SUBBANDS_2_2*(SINT32)s16X[ChOffset+32+2];\ + s32Temp2+=(SINT32)WIND_8_SUBBANDS_2_2*(SINT32)s16X[ChOffset+32+14];\ + s32Temp+=(SINT32)WIND_8_SUBBANDS_2_3*(SINT32)s16X[ChOffset+48+2];\ + s32Temp2+=(SINT32)WIND_8_SUBBANDS_2_3*(SINT32)s16X[ChOffset+16+14];\ + s32Temp+=(SINT32)WIND_8_SUBBANDS_2_4*(SINT32)s16X[ChOffset+64+2];\ + s32Temp2+=(SINT32)WIND_8_SUBBANDS_2_4*(SINT32)s16X[ChOffset+14];\ + s32DCTY[2]=(SINT32)s32Temp;\ + s32DCTY[14]=(SINT32)s32Temp2;\ +} +#define WINDOW_ACCU_8_3_13 \ +{\ + s32Temp=(SINT32)WIND_8_SUBBANDS_3_0*(SINT32)s16X[ChOffset+3];\ + s32Temp2=(SINT32)WIND_8_SUBBANDS_3_0*(SINT32)s16X[ChOffset+64+13];\ + s32Temp+=(SINT32)WIND_8_SUBBANDS_3_1*(SINT32)s16X[ChOffset+16+3];\ + s32Temp2+=(SINT32)WIND_8_SUBBANDS_3_1*(SINT32)s16X[ChOffset+48+13];\ + s32Temp+=(SINT32)WIND_8_SUBBANDS_3_2*(SINT32)s16X[ChOffset+32+3];\ + s32Temp2+=(SINT32)WIND_8_SUBBANDS_3_2*(SINT32)s16X[ChOffset+32+13];\ + s32Temp+=(SINT32)WIND_8_SUBBANDS_3_3*(SINT32)s16X[ChOffset+48+3];\ + s32Temp2+=(SINT32)WIND_8_SUBBANDS_3_3*(SINT32)s16X[ChOffset+16+13];\ + s32Temp+=(SINT32)WIND_8_SUBBANDS_3_4*(SINT32)s16X[ChOffset+64+3];\ + s32Temp2+=(SINT32)WIND_8_SUBBANDS_3_4*(SINT32)s16X[ChOffset+13];\ + s32DCTY[3]=(SINT32)s32Temp;\ + s32DCTY[13]=(SINT32)s32Temp2;\ +} +#define WINDOW_ACCU_8_4_12 \ +{\ + s32Temp=(SINT32)WIND_8_SUBBANDS_4_0*(SINT32)s16X[ChOffset+4];\ + s32Temp2=(SINT32)WIND_8_SUBBANDS_4_0*(SINT32)s16X[ChOffset+64+12];\ + s32Temp+=(SINT32)WIND_8_SUBBANDS_4_1*(SINT32)s16X[ChOffset+16+4];\ + s32Temp2+=(SINT32)WIND_8_SUBBANDS_4_1*(SINT32)s16X[ChOffset+48+12];\ + s32Temp+=(SINT32)WIND_8_SUBBANDS_4_2*(SINT32)s16X[ChOffset+32+4];\ + s32Temp2+=(SINT32)WIND_8_SUBBANDS_4_2*(SINT32)s16X[ChOffset+32+12];\ + s32Temp+=(SINT32)WIND_8_SUBBANDS_4_3*(SINT32)s16X[ChOffset+48+4];\ + s32Temp2+=(SINT32)WIND_8_SUBBANDS_4_3*(SINT32)s16X[ChOffset+16+12];\ + s32Temp+=(SINT32)WIND_8_SUBBANDS_4_4*(SINT32)s16X[ChOffset+64+4];\ + s32Temp2+=(SINT32)WIND_8_SUBBANDS_4_4*(SINT32)s16X[ChOffset+12];\ + s32DCTY[4]=(SINT32)s32Temp;\ + s32DCTY[12]=(SINT32)s32Temp2;\ +} +#define WINDOW_ACCU_8_5_11 \ +{\ + s32Temp=(SINT32)WIND_8_SUBBANDS_5_0*(SINT32)s16X[ChOffset+5];\ + s32Temp2=(SINT32)WIND_8_SUBBANDS_5_0*(SINT32)s16X[ChOffset+64+11];\ + s32Temp+=(SINT32)WIND_8_SUBBANDS_5_1*(SINT32)s16X[ChOffset+16+5];\ + s32Temp2+=(SINT32)WIND_8_SUBBANDS_5_1*(SINT32)s16X[ChOffset+48+11];\ + s32Temp+=(SINT32)WIND_8_SUBBANDS_5_2*(SINT32)s16X[ChOffset+32+5];\ + s32Temp2+=(SINT32)WIND_8_SUBBANDS_5_2*(SINT32)s16X[ChOffset+32+11];\ + s32Temp+=(SINT32)WIND_8_SUBBANDS_5_3*(SINT32)s16X[ChOffset+48+5];\ + s32Temp2+=(SINT32)WIND_8_SUBBANDS_5_3*(SINT32)s16X[ChOffset+16+11];\ + s32Temp+=(SINT32)WIND_8_SUBBANDS_5_4*(SINT32)s16X[ChOffset+64+5];\ + s32Temp2+=(SINT32)WIND_8_SUBBANDS_5_4*(SINT32)s16X[ChOffset+11];\ + s32DCTY[5]=(SINT32)s32Temp;\ + s32DCTY[11]=(SINT32)s32Temp2;\ +} +#define WINDOW_ACCU_8_6_10 \ +{\ + s32Temp=(SINT32)WIND_8_SUBBANDS_6_0*(SINT32)s16X[ChOffset+6];\ + s32Temp2=(SINT32)WIND_8_SUBBANDS_6_0*(SINT32)s16X[ChOffset+64+10];\ + s32Temp+=(SINT32)WIND_8_SUBBANDS_6_1*(SINT32)s16X[ChOffset+16+6];\ + s32Temp2+=(SINT32)WIND_8_SUBBANDS_6_1*(SINT32)s16X[ChOffset+48+10];\ + s32Temp+=(SINT32)WIND_8_SUBBANDS_6_2*(SINT32)s16X[ChOffset+32+6];\ + s32Temp2+=(SINT32)WIND_8_SUBBANDS_6_2*(SINT32)s16X[ChOffset+32+10];\ + s32Temp+=(SINT32)WIND_8_SUBBANDS_6_3*(SINT32)s16X[ChOffset+48+6];\ + s32Temp2+=(SINT32)WIND_8_SUBBANDS_6_3*(SINT32)s16X[ChOffset+16+10];\ + s32Temp+=(SINT32)WIND_8_SUBBANDS_6_4*(SINT32)s16X[ChOffset+64+6];\ + s32Temp2+=(SINT32)WIND_8_SUBBANDS_6_4*(SINT32)s16X[ChOffset+10];\ + s32DCTY[6]=(SINT32)s32Temp;\ + s32DCTY[10]=(SINT32)s32Temp2;\ +} +#define WINDOW_ACCU_8_7_9 \ +{\ + s32Temp=(SINT32)WIND_8_SUBBANDS_7_0*(SINT32)s16X[ChOffset+7];\ + s32Temp2=(SINT32)WIND_8_SUBBANDS_7_0*(SINT32)s16X[ChOffset+64+9];\ + s32Temp+=(SINT32)WIND_8_SUBBANDS_7_1*(SINT32)s16X[ChOffset+16+7];\ + s32Temp2+=(SINT32)WIND_8_SUBBANDS_7_1*(SINT32)s16X[ChOffset+48+9];\ + s32Temp+=(SINT32)WIND_8_SUBBANDS_7_2*(SINT32)s16X[ChOffset+32+7];\ + s32Temp2+=(SINT32)WIND_8_SUBBANDS_7_2*(SINT32)s16X[ChOffset+32+9];\ + s32Temp+=(SINT32)WIND_8_SUBBANDS_7_3*(SINT32)s16X[ChOffset+48+7];\ + s32Temp2+=(SINT32)WIND_8_SUBBANDS_7_3*(SINT32)s16X[ChOffset+16+9];\ + s32Temp+=(SINT32)WIND_8_SUBBANDS_7_4*(SINT32)s16X[ChOffset+64+7];\ + s32Temp2+=(SINT32)WIND_8_SUBBANDS_7_4*(SINT32)s16X[ChOffset+9];\ + s32DCTY[7]=(SINT32)s32Temp;\ + s32DCTY[9]=(SINT32)s32Temp2;\ +} +#define WINDOW_ACCU_8_8 \ +{\ + s32Temp=(SINT32)WIND_8_SUBBANDS_8_0*(SINT32)(s16X[ChOffset+8]+s16X[ChOffset+64+8]);\ + s32Temp+=(SINT32)WIND_8_SUBBANDS_8_1*(SINT32)(s16X[ChOffset+16+8]+s16X[ChOffset+48+8]);\ + s32Temp+=(SINT32)WIND_8_SUBBANDS_8_2*(SINT32)s16X[ChOffset+32+8];\ + s32DCTY[8]=(SINT32)s32Temp;\ +} +#define WINDOW_ACCU_4_0 \ +{\ + s32Temp=(SINT32)WIND_4_SUBBANDS_0_1*(SINT32)(s16X[ChOffset+8]-s16X[ChOffset+32]);\ + s32Temp+=(SINT32)WIND_4_SUBBANDS_0_2*(SINT32)(s16X[ChOffset+16]-s16X[ChOffset+24]);\ + s32DCTY[0]=(SINT32)(s32Temp);\ +} +#define WINDOW_ACCU_4_1_7 \ +{\ + s32Temp=(SINT32)WIND_4_SUBBANDS_1_0*(SINT32)s16X[ChOffset+1];\ + s32Temp2=(SINT32)WIND_4_SUBBANDS_1_0*(SINT32)s16X[ChOffset+32+7];\ + s32Temp+=(SINT32)WIND_4_SUBBANDS_1_1*(SINT32)s16X[ChOffset+8+1];\ + s32Temp2+=(SINT32)WIND_4_SUBBANDS_1_1*(SINT32)s16X[ChOffset+24+7];\ + s32Temp+=(SINT32)WIND_4_SUBBANDS_1_2*(SINT32)s16X[ChOffset+16+1];\ + s32Temp2+=(SINT32)WIND_4_SUBBANDS_1_2*(SINT32)s16X[ChOffset+16+7];\ + s32Temp+=(SINT32)WIND_4_SUBBANDS_1_3*(SINT32)s16X[ChOffset+24+1];\ + s32Temp2+=(SINT32)WIND_4_SUBBANDS_1_3*(SINT32)s16X[ChOffset+8+7];\ + s32Temp+=(SINT32)WIND_4_SUBBANDS_1_4*(SINT32)s16X[ChOffset+32+1];\ + s32Temp2+=(SINT32)WIND_4_SUBBANDS_1_4*(SINT32)s16X[ChOffset+7];\ + s32DCTY[1]=(SINT32)(s32Temp);\ + s32DCTY[7]=(SINT32)(s32Temp2);\ +} +#define WINDOW_ACCU_4_2_6 \ +{\ + s32Temp=(SINT32)WIND_4_SUBBANDS_2_0*(SINT32)s16X[ChOffset+2];\ + s32Temp2=(SINT32)WIND_4_SUBBANDS_2_0*(SINT32)s16X[ChOffset+32+6];\ + s32Temp+=(SINT32)WIND_4_SUBBANDS_2_1*(SINT32)s16X[ChOffset+8+2];\ + s32Temp2+=(SINT32)WIND_4_SUBBANDS_2_1*(SINT32)s16X[ChOffset+24+6];\ + s32Temp+=(SINT32)WIND_4_SUBBANDS_2_2*(SINT32)s16X[ChOffset+16+2];\ + s32Temp2+=(SINT32)WIND_4_SUBBANDS_2_2*(SINT32)s16X[ChOffset+16+6];\ + s32Temp+=(SINT32)WIND_4_SUBBANDS_2_3*(SINT32)s16X[ChOffset+24+2];\ + s32Temp2+=(SINT32)WIND_4_SUBBANDS_2_3*(SINT32)s16X[ChOffset+8+6];\ + s32Temp+=(SINT32)WIND_4_SUBBANDS_2_4*(SINT32)s16X[ChOffset+32+2];\ + s32Temp2+=(SINT32)WIND_4_SUBBANDS_2_4*(SINT32)s16X[ChOffset+6];\ + s32DCTY[2]=(SINT32)(s32Temp);\ + s32DCTY[6]=(SINT32)(s32Temp2);\ +} +#define WINDOW_ACCU_4_3_5 \ +{\ + s32Temp=(SINT32)WIND_4_SUBBANDS_3_0*(SINT32)s16X[ChOffset+3];\ + s32Temp2=(SINT32)WIND_4_SUBBANDS_3_0*(SINT32)s16X[ChOffset+32+5];\ + s32Temp+=(SINT32)WIND_4_SUBBANDS_3_1*(SINT32)s16X[ChOffset+8+3];\ + s32Temp2+=(SINT32)WIND_4_SUBBANDS_3_1*(SINT32)s16X[ChOffset+24+5];\ + s32Temp+=(SINT32)WIND_4_SUBBANDS_3_2*(SINT32)s16X[ChOffset+16+3];\ + s32Temp2+=(SINT32)WIND_4_SUBBANDS_3_2*(SINT32)s16X[ChOffset+16+5];\ + s32Temp+=(SINT32)WIND_4_SUBBANDS_3_3*(SINT32)s16X[ChOffset+24+3];\ + s32Temp2+=(SINT32)WIND_4_SUBBANDS_3_3*(SINT32)s16X[ChOffset+8+5];\ + s32Temp+=(SINT32)WIND_4_SUBBANDS_3_4*(SINT32)s16X[ChOffset+32+3];\ + s32Temp2+=(SINT32)WIND_4_SUBBANDS_3_4*(SINT32)s16X[ChOffset+5];\ + s32DCTY[3]=(SINT32)(s32Temp);\ + s32DCTY[5]=(SINT32)(s32Temp2);\ +} + +#define WINDOW_ACCU_4_4 \ +{\ + s32Temp=(SINT32)WIND_4_SUBBANDS_4_0*(SINT32)(s16X[ChOffset+4]+s16X[ChOffset+4+32]);\ + s32Temp+=(SINT32)WIND_4_SUBBANDS_4_1*(SINT32)(s16X[ChOffset+4+8]+s16X[ChOffset+4+24]);\ + s32Temp+=(SINT32)WIND_4_SUBBANDS_4_2*(SINT32)s16X[ChOffset+4+16];\ + s32DCTY[4]=(SINT32)(s32Temp);\ +} +#endif +#define WINDOW_PARTIAL_4 \ +{\ + WINDOW_ACCU_4_0; WINDOW_ACCU_4_1_7;\ + WINDOW_ACCU_4_2_6; WINDOW_ACCU_4_3_5;\ + WINDOW_ACCU_4_4;\ +} + +#define WINDOW_PARTIAL_8 \ +{\ + WINDOW_ACCU_8_0; WINDOW_ACCU_8_1_15;\ + WINDOW_ACCU_8_2_14; WINDOW_ACCU_8_3_13;\ + WINDOW_ACCU_8_4_12; WINDOW_ACCU_8_5_11;\ + WINDOW_ACCU_8_6_10; WINDOW_ACCU_8_7_9;\ + WINDOW_ACCU_8_8;\ +} +#else +#if (SBC_IS_64_MULT_IN_WINDOW_ACCU == TRUE) +#define WINDOW_ACCU_4(i) \ +{\ + s64Temp=((SINT64)gas32CoeffFor4SBs[i] * (SINT64)s16X[ChOffset+i]); \ + s64Temp+=((SINT64)gas32CoeffFor4SBs[(i+8)] * (SINT64)s16X[ChOffset+i+8]); \ + s64Temp+=((SINT64)gas32CoeffFor4SBs[(i+16)] * (SINT64)s16X[ChOffset+i+16]); \ + s64Temp+=((SINT64)gas32CoeffFor4SBs[(i+24)] * (SINT64)s16X[ChOffset+i+24]); \ + s64Temp+=((SINT64)gas32CoeffFor4SBs[(i+32)] * (SINT64)s16X[ChOffset+i+32]); \ + s32DCTY[i]=(SINT32)(s64Temp>>16);\ + /*printf("s32DCTY4: 0x%x \n", s32DCTY[i]);*/\ +} +#else +#define WINDOW_ACCU_4(i) \ +{\ + s32DCTY[i]=(gas32CoeffFor4SBs[i * 2] * s16X[ChOffset+i]) \ + + (((SINT32)(UINT16)(gas32CoeffFor4SBs[(i * 2) + 1]) * s16X[ChOffset+i]) >> 16); \ + s32DCTY[i]+=(gas32CoeffFor4SBs[(i+8) * 2] * s16X[ChOffset+i+8]) \ + + (((SINT32)(UINT16)(gas32CoeffFor4SBs[((i+8) * 2) + 1]) * s16X[ChOffset+i+8]) >> 16); \ + s32DCTY[i]+=(gas32CoeffFor4SBs[(i+16) * 2] * s16X[ChOffset+i+16]) \ + + (((SINT32)(UINT16)(gas32CoeffFor4SBs[((i+16) * 2) + 1]) * s16X[ChOffset+i+16]) >> 16); \ + s32DCTY[i]+=(gas32CoeffFor4SBs[(i+24) * 2] * s16X[ChOffset+i+24]) \ + + (((SINT32)(UINT16)(gas32CoeffFor4SBs[((i+24) * 2) + 1]) * s16X[ChOffset+i+24]) >> 16); \ + s32DCTY[i]+=(gas32CoeffFor4SBs[(i+32) * 2] * s16X[ChOffset+i+32]) \ + + (((SINT32)(UINT16)(gas32CoeffFor4SBs[((i+32) * 2) + 1]) * s16X[ChOffset+i+32]) >> 16); \ +} +#endif +#define WINDOW_PARTIAL_4 \ +{\ + WINDOW_ACCU_4(0); WINDOW_ACCU_4(1);\ + WINDOW_ACCU_4(2); WINDOW_ACCU_4(3);\ + WINDOW_ACCU_4(4); WINDOW_ACCU_4(5);\ + WINDOW_ACCU_4(6); WINDOW_ACCU_4(7);\ +} + +#if (SBC_IS_64_MULT_IN_WINDOW_ACCU == TRUE) +#define WINDOW_ACCU_8(i) \ +{\ + s64Temp = ((((SINT64)gas32CoeffFor8SBs[i] * (SINT64)s16X[ChOffset+i] ))); \ + s64Temp+= ((((SINT64)gas32CoeffFor8SBs[(i+16)] * (SINT64)s16X[ChOffset+i+16]))); \ + s64Temp+= ((((SINT64)gas32CoeffFor8SBs[(i+32)] * (SINT64)s16X[ChOffset+i+32]))); \ + s64Temp+= ((((SINT64)gas32CoeffFor8SBs[(i+48)] * (SINT64)s16X[ChOffset+i+48]))); \ + s64Temp+= ((((SINT64)gas32CoeffFor8SBs[(i+64)] * (SINT64)s16X[ChOffset+i+64]))); \ + /*printf("s32DCTY8: %d= 0x%x * %d\n", s32DCTY[i], gas32CoeffFor8SBs[i], s16X[ChOffset+i]);*/ \ + s32DCTY[i]=(SINT32)(s64Temp>>16);\ +} +#else +#define WINDOW_ACCU_8(i) \ +{\ + s32DCTY[i]=(gas32CoeffFor8SBs[i * 2] * s16X[ChOffset+i]) \ + + (((SINT32)(UINT16)(gas32CoeffFor8SBs[(i * 2) + 1]) * s16X[ChOffset+i]) >> 16); \ + s32DCTY[i]+=(gas32CoeffFor8SBs[(i+16) * 2] * s16X[ChOffset+i+16]) \ + + (((SINT32)(UINT16)(gas32CoeffFor8SBs[((i+16) * 2) + 1]) * s16X[ChOffset+i+16]) >> 16); \ + s32DCTY[i]+=(gas32CoeffFor8SBs[(i+32) * 2] * s16X[ChOffset+i+32]) \ + + (((SINT32)(UINT16)(gas32CoeffFor8SBs[((i+32) * 2) + 1]) * s16X[ChOffset+i+32]) >> 16); \ + s32DCTY[i]+=(gas32CoeffFor8SBs[(i+48) * 2] * s16X[ChOffset+i+48]) \ + + (((SINT32)(UINT16)(gas32CoeffFor8SBs[((i+48) * 2) + 1]) * s16X[ChOffset+i+48]) >> 16); \ + s32DCTY[i]+=(gas32CoeffFor8SBs[(i+64) * 2] * s16X[ChOffset+i+64]) \ + + (((SINT32)(UINT16)(gas32CoeffFor8SBs[((i+64) * 2) + 1]) * s16X[ChOffset+i+64]) >> 16); \ + /*printf("s32DCTY8: %d = 0x%4x%4x * %d\n", s32DCTY[i], gas32CoeffFor8SBs[i * 2], (gas32CoeffFor8SBs[(i * 2) + 1]), s16X[ChOffset+i]);*/\ + /*s32DCTY[i]=(SINT32)(s64Temp>>16);*/\ +} +#endif +#define WINDOW_PARTIAL_8 \ +{\ + WINDOW_ACCU_8(0); WINDOW_ACCU_8(1);\ + WINDOW_ACCU_8(2); WINDOW_ACCU_8(3);\ + WINDOW_ACCU_8(4); WINDOW_ACCU_8(5);\ + WINDOW_ACCU_8(6); WINDOW_ACCU_8(7);\ + WINDOW_ACCU_8(8); WINDOW_ACCU_8(9);\ + WINDOW_ACCU_8(10); WINDOW_ACCU_8(11);\ + WINDOW_ACCU_8(12); WINDOW_ACCU_8(13);\ + WINDOW_ACCU_8(14); WINDOW_ACCU_8(15);\ +} +#endif +#endif + +static SINT16 ShiftCounter = 0; +extern SINT16 EncMaxShiftCounter; +/**************************************************************************** +* SbcAnalysisFilter - performs Analysis of the input audio stream +* +* RETURNS : N/A +*/ +void SbcAnalysisFilter4(SBC_ENC_PARAMS *pstrEncParams) +{ + SINT16 *ps16PcmBuf; + SINT32 *ps32SbBuf; + SINT32 s32Blk, s32Ch; + SINT32 s32NumOfChannels, s32NumOfBlocks; + SINT32 i, *ps32X, *ps32X2; + SINT32 Offset, Offset2, ChOffset; +#if (SBC_ARM_ASM_OPT==TRUE) + register SINT32 s32Hi, s32Hi2; +#else +#if (SBC_IPAQ_OPT==TRUE) +#if (SBC_IS_64_MULT_IN_WINDOW_ACCU == TRUE) + register SINT64 s64Temp, s64Temp2; +#else + register SINT32 s32Temp, s32Temp2; +#endif +#else + +#if (SBC_IS_64_MULT_IN_WINDOW_ACCU == TRUE) + SINT64 s64Temp; +#endif + +#endif +#endif + + s32NumOfChannels = pstrEncParams->s16NumOfChannels; + s32NumOfBlocks = pstrEncParams->s16NumOfBlocks; + + ps16PcmBuf = pstrEncParams->ps16NextPcmBuffer; + + ps32SbBuf = pstrEncParams->s32SbBuffer; + Offset2 = (SINT32)(EncMaxShiftCounter + 40); + for (s32Blk = 0; s32Blk < s32NumOfBlocks; s32Blk++) { + Offset = (SINT32)(EncMaxShiftCounter - ShiftCounter); + /* Store new samples */ + if (s32NumOfChannels == 1) { + s16X[3 + Offset] = *ps16PcmBuf; ps16PcmBuf++; + s16X[2 + Offset] = *ps16PcmBuf; ps16PcmBuf++; + s16X[1 + Offset] = *ps16PcmBuf; ps16PcmBuf++; + s16X[0 + Offset] = *ps16PcmBuf; ps16PcmBuf++; + } else { + s16X[3 + Offset] = *ps16PcmBuf; ps16PcmBuf++; + s16X[Offset2 + 3 + Offset] = *ps16PcmBuf; ps16PcmBuf++; + s16X[2 + Offset] = *ps16PcmBuf; ps16PcmBuf++; + s16X[Offset2 + 2 + Offset] = *ps16PcmBuf; ps16PcmBuf++; + s16X[1 + Offset] = *ps16PcmBuf; ps16PcmBuf++; + s16X[Offset2 + 1 + Offset] = *ps16PcmBuf; ps16PcmBuf++; + s16X[0 + Offset] = *ps16PcmBuf; ps16PcmBuf++; + s16X[Offset2 + 0 + Offset] = *ps16PcmBuf; ps16PcmBuf++; + } + for (s32Ch = 0; s32Ch < s32NumOfChannels; s32Ch++) { + ChOffset = s32Ch * Offset2 + Offset; + + WINDOW_PARTIAL_4 + + SBC_FastIDCT4(s32DCTY, ps32SbBuf); + + ps32SbBuf += SUB_BANDS_4; + } + if (s32NumOfChannels == 1) { + if (ShiftCounter >= EncMaxShiftCounter) { + SHIFTUP_X4; + ShiftCounter = 0; + } else { + ShiftCounter += SUB_BANDS_4; + } + } else { + if (ShiftCounter >= EncMaxShiftCounter) { + SHIFTUP_X4_2; + ShiftCounter = 0; + } else { + ShiftCounter += SUB_BANDS_4; + } + } + } +} + +/* //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// */ +void SbcAnalysisFilter8 (SBC_ENC_PARAMS *pstrEncParams) +{ + SINT16 *ps16PcmBuf; + SINT32 *ps32SbBuf; + SINT32 s32Blk, s32Ch; /* counter for block*/ + SINT32 Offset, Offset2; + SINT32 s32NumOfChannels, s32NumOfBlocks; + SINT32 i, *ps32X, *ps32X2; + SINT32 ChOffset; +#if (SBC_ARM_ASM_OPT==TRUE) + register SINT32 s32Hi, s32Hi2; +#else +#if (SBC_IPAQ_OPT==TRUE) +#if (SBC_IS_64_MULT_IN_WINDOW_ACCU == TRUE) + register SINT64 s64Temp, s64Temp2; +#else + register SINT32 s32Temp, s32Temp2; +#endif +#else +#if (SBC_IS_64_MULT_IN_WINDOW_ACCU == TRUE) + SINT64 s64Temp; +#endif +#endif +#endif + + s32NumOfChannels = pstrEncParams->s16NumOfChannels; + s32NumOfBlocks = pstrEncParams->s16NumOfBlocks; + + ps16PcmBuf = pstrEncParams->ps16NextPcmBuffer; + + ps32SbBuf = pstrEncParams->s32SbBuffer; + Offset2 = (SINT32)(EncMaxShiftCounter + 80); + for (s32Blk = 0; s32Blk < s32NumOfBlocks; s32Blk++) { + Offset = (SINT32)(EncMaxShiftCounter - ShiftCounter); + /* Store new samples */ + if (s32NumOfChannels == 1) { + s16X[7 + Offset] = *ps16PcmBuf; ps16PcmBuf++; + s16X[6 + Offset] = *ps16PcmBuf; ps16PcmBuf++; + s16X[5 + Offset] = *ps16PcmBuf; ps16PcmBuf++; + s16X[4 + Offset] = *ps16PcmBuf; ps16PcmBuf++; + s16X[3 + Offset] = *ps16PcmBuf; ps16PcmBuf++; + s16X[2 + Offset] = *ps16PcmBuf; ps16PcmBuf++; + s16X[1 + Offset] = *ps16PcmBuf; ps16PcmBuf++; + s16X[0 + Offset] = *ps16PcmBuf; ps16PcmBuf++; + } else { + s16X[7 + Offset] = *ps16PcmBuf; ps16PcmBuf++; + s16X[Offset2 + 7 + Offset] = *ps16PcmBuf; ps16PcmBuf++; + s16X[6 + Offset] = *ps16PcmBuf; ps16PcmBuf++; + s16X[Offset2 + 6 + Offset] = *ps16PcmBuf; ps16PcmBuf++; + s16X[5 + Offset] = *ps16PcmBuf; ps16PcmBuf++; + s16X[Offset2 + 5 + Offset] = *ps16PcmBuf; ps16PcmBuf++; + s16X[4 + Offset] = *ps16PcmBuf; ps16PcmBuf++; + s16X[Offset2 + 4 + Offset] = *ps16PcmBuf; ps16PcmBuf++; + s16X[3 + Offset] = *ps16PcmBuf; ps16PcmBuf++; + s16X[Offset2 + 3 + Offset] = *ps16PcmBuf; ps16PcmBuf++; + s16X[2 + Offset] = *ps16PcmBuf; ps16PcmBuf++; + s16X[Offset2 + 2 + Offset] = *ps16PcmBuf; ps16PcmBuf++; + s16X[1 + Offset] = *ps16PcmBuf; ps16PcmBuf++; + s16X[Offset2 + 1 + Offset] = *ps16PcmBuf; ps16PcmBuf++; + s16X[0 + Offset] = *ps16PcmBuf; ps16PcmBuf++; + s16X[Offset2 + 0 + Offset] = *ps16PcmBuf; ps16PcmBuf++; + } + for (s32Ch = 0; s32Ch < s32NumOfChannels; s32Ch++) { + ChOffset = s32Ch * Offset2 + Offset; + + WINDOW_PARTIAL_8 + + SBC_FastIDCT8 (s32DCTY, ps32SbBuf); + + ps32SbBuf += SUB_BANDS_8; + } + if (s32NumOfChannels == 1) { + if (ShiftCounter >= EncMaxShiftCounter) { + SHIFTUP_X8; + ShiftCounter = 0; + } else { + ShiftCounter += SUB_BANDS_8; + } + } else { + if (ShiftCounter >= EncMaxShiftCounter) { + SHIFTUP_X8_2; + ShiftCounter = 0; + } else { + ShiftCounter += SUB_BANDS_8; + } + } + } +} + +void SbcAnalysisInit (void) +{ + static bool loaded = false; + if (!loaded) { + loaded = true; +#if BT_BLE_DYNAMIC_ENV_MEMORY == TRUE + s32X = (SINT32 *)osi_malloc(sizeof(SINT32) * (ENC_VX_BUFFER_SIZE / 2)); + s32DCTY = (SINT32 *)osi_malloc(sizeof(SINT32) * 16); + assert(s32X); + assert(s32DCTY); + memset(s32X, 0, sizeof(SINT16) * ENC_VX_BUFFER_SIZE); + memset(s32DCTY, 0, sizeof(SINT32) * 16); + s16X = (SINT16 *) s32X; +#endif + } + memset(s16X, 0, ENC_VX_BUFFER_SIZE * sizeof(SINT16)); + ShiftCounter = 0; +} + +#endif /* #if (defined(SBC_ENC_INCLUDED) && SBC_ENC_INCLUDED == TRUE) */ diff --git a/lib/bt/host/bluedroid/external/sbc/encoder/srce/sbc_dct.c b/lib/bt/host/bluedroid/external/sbc/encoder/srce/sbc_dct.c new file mode 100644 index 00000000..73c981ac --- /dev/null +++ b/lib/bt/host/bluedroid/external/sbc/encoder/srce/sbc_dct.c @@ -0,0 +1,244 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * source file for fast dct operations + * + ******************************************************************************/ +#include "common/bt_target.h" +#include "sbc_encoder.h" +#include "sbc_enc_func_declare.h" +#include "sbc_dct.h" + + +#if (defined(SBC_ENC_INCLUDED) && SBC_ENC_INCLUDED == TRUE) + +/******************************************************************************* +** +** Function SBC_FastIDCT8 +** +** Description implementation of fast DCT algorithm by Feig and Winograd +** +** +** Returns y = dct(pInVect) +** +** +*******************************************************************************/ + +#if (SBC_IS_64_MULT_IN_IDCT == FALSE) +#define SBC_COS_PI_SUR_4 (0x00005a82) /* ((0x8000) * 0.7071) = cos(pi/4) */ +#define SBC_COS_PI_SUR_8 (0x00007641) /* ((0x8000) * 0.9239) = (cos(pi/8)) */ +#define SBC_COS_3PI_SUR_8 (0x000030fb) /* ((0x8000) * 0.3827) = (cos(3*pi/8)) */ +#define SBC_COS_PI_SUR_16 (0x00007d8a) /* ((0x8000) * 0.9808)) = (cos(pi/16)) */ +#define SBC_COS_3PI_SUR_16 (0x00006a6d) /* ((0x8000) * 0.8315)) = (cos(3*pi/16)) */ +#define SBC_COS_5PI_SUR_16 (0x0000471c) /* ((0x8000) * 0.5556)) = (cos(5*pi/16)) */ +#define SBC_COS_7PI_SUR_16 (0x000018f8) /* ((0x8000) * 0.1951)) = (cos(7*pi/16)) */ +#define SBC_IDCT_MULT(a,b,c) SBC_MULT_32_16_SIMPLIFIED(a,b,c) +#else +#define SBC_COS_PI_SUR_4 (0x5A827999) /* ((0x80000000) * 0.707106781) = (cos(pi/4) ) */ +#define SBC_COS_PI_SUR_8 (0x7641AF3C) /* ((0x80000000) * 0.923879533) = (cos(pi/8) ) */ +#define SBC_COS_3PI_SUR_8 (0x30FBC54D) /* ((0x80000000) * 0.382683432) = (cos(3*pi/8) ) */ +#define SBC_COS_PI_SUR_16 (0x7D8A5F3F) /* ((0x80000000) * 0.98078528 )) = (cos(pi/16) ) */ +#define SBC_COS_3PI_SUR_16 (0x6A6D98A4) /* ((0x80000000) * 0.831469612)) = (cos(3*pi/16)) */ +#define SBC_COS_5PI_SUR_16 (0x471CECE6) /* ((0x80000000) * 0.555570233)) = (cos(5*pi/16)) */ +#define SBC_COS_7PI_SUR_16 (0x18F8B83C) /* ((0x80000000) * 0.195090322)) = (cos(7*pi/16)) */ +#define SBC_IDCT_MULT(a,b,c) SBC_MULT_32_32(a,b,c) +#endif /* SBC_IS_64_MULT_IN_IDCT */ + +#if (SBC_FAST_DCT == FALSE) +extern const SINT16 gas16AnalDCTcoeff8[]; +extern const SINT16 gas16AnalDCTcoeff4[]; +#endif + +void SBC_FastIDCT8(SINT32 *pInVect, SINT32 *pOutVect) +{ +#if (SBC_FAST_DCT == TRUE) +#if (SBC_ARM_ASM_OPT==TRUE) +#else +#if (SBC_IPAQ_OPT==TRUE) +#if (SBC_IS_64_MULT_IN_IDCT == TRUE) + SINT64 s64Temp; +#endif +#else +#if (SBC_IS_64_MULT_IN_IDCT == TRUE) + SINT32 s32HiTemp; +#else + SINT32 s32In2Temp; + register SINT32 s32In1Temp; +#endif +#endif +#endif + + register SINT32 x0, x1, x2, x3, x4, x5, x6, x7, temp; + SINT32 res_even[4], res_odd[4]; + /*x0= (pInVect[4])/2 ;*/ + SBC_IDCT_MULT(SBC_COS_PI_SUR_4, pInVect[4], x0); + /*printf("x0 0x%x = %d = %d * %d\n", x0, x0, SBC_COS_PI_SUR_4, pInVect[4]);*/ + + x1 = (pInVect[3] + pInVect[5]) >> 1; + x2 = (pInVect[2] + pInVect[6]) >> 1; + x3 = (pInVect[1] + pInVect[7]) >> 1; + x4 = (pInVect[0] + pInVect[8]) >> 1; + x5 = (pInVect[9] - pInVect[15]) >> 1; + x6 = (pInVect[10] - pInVect[14]) >> 1; + x7 = (pInVect[11] - pInVect[13]) >> 1; + + /* 2-point IDCT of x0 and x4 as in (11) */ + temp = x0 ; + SBC_IDCT_MULT(SBC_COS_PI_SUR_4, ( x0 + x4 ), x0); /*x0 = ( x0 + x4 ) * cos(1*pi/4) ; */ + SBC_IDCT_MULT(SBC_COS_PI_SUR_4, ( temp - x4 ), x4); /*x4 = ( temp - x4 ) * cos(1*pi/4) ; */ + + /* rearrangement of x2 and x6 as in (15) */ + x2 -= x6; + x6 <<= 1 ; + + /* 2-point IDCT of x2 and x6 and post-multiplication as in (15) */ + SBC_IDCT_MULT(SBC_COS_PI_SUR_4, x6, x6); /*x6 = x6 * cos(1*pi/4) ; */ + temp = x2 ; + SBC_IDCT_MULT(SBC_COS_PI_SUR_8, ( x2 + x6 ), x2); /*x2 = ( x2 + x6 ) * cos(1*pi/8) ; */ + SBC_IDCT_MULT(SBC_COS_3PI_SUR_8, ( temp - x6 ), x6); /*x6 = ( temp - x6 ) * cos(3*pi/8) ;*/ + + /* 4-point IDCT of x0,x2,x4 and x6 as in (11) */ + res_even[ 0 ] = x0 + x2 ; + res_even[ 1 ] = x4 + x6 ; + res_even[ 2 ] = x4 - x6 ; + res_even[ 3 ] = x0 - x2 ; + + + /* rearrangement of x1,x3,x5,x7 as in (15) */ + x7 <<= 1 ; + x5 = ( x5 << 1 ) - x7 ; + x3 = ( x3 << 1 ) - x5 ; + x1 -= x3 >> 1 ; + + /* two-dimensional IDCT of x1 and x5 */ + SBC_IDCT_MULT(SBC_COS_PI_SUR_4, x5, x5); /*x5 = x5 * cos(1*pi/4) ; */ + temp = x1 ; + x1 = x1 + x5 ; + x5 = temp - x5 ; + + /* rearrangement of x3 and x7 as in (15) */ + x3 -= x7; + x7 <<= 1 ; + SBC_IDCT_MULT(SBC_COS_PI_SUR_4, x7, x7); /*x7 = x7 * cos(1*pi/4) ; */ + + /* 2-point IDCT of x3 and x7 and post-multiplication as in (15) */ + temp = x3 ; + SBC_IDCT_MULT( SBC_COS_PI_SUR_8, ( x3 + x7 ), x3); /*x3 = ( x3 + x7 ) * cos(1*pi/8) ; */ + SBC_IDCT_MULT( SBC_COS_3PI_SUR_8, ( temp - x7 ), x7); /*x7 = ( temp - x7 ) * cos(3*pi/8) ;*/ + + /* 4-point IDCT of x1,x3,x5 and x7 and post multiplication by diagonal matrix as in (14) */ + SBC_IDCT_MULT((SBC_COS_PI_SUR_16), ( x1 + x3 ) , res_odd[0]); /*res_odd[ 0 ] = ( x1 + x3 ) * cos(1*pi/16) ; */ + SBC_IDCT_MULT((SBC_COS_3PI_SUR_16), ( x5 + x7 ) , res_odd[1]); /*res_odd[ 1 ] = ( x5 + x7 ) * cos(3*pi/16) ; */ + SBC_IDCT_MULT((SBC_COS_5PI_SUR_16), ( x5 - x7 ) , res_odd[2]); /*res_odd[ 2 ] = ( x5 - x7 ) * cos(5*pi/16) ; */ + SBC_IDCT_MULT((SBC_COS_7PI_SUR_16), ( x1 - x3 ) , res_odd[3]); /*res_odd[ 3 ] = ( x1 - x3 ) * cos(7*pi/16) ; */ + + /* additions and subtractions as in (9) */ + pOutVect[0] = (res_even[ 0 ] + res_odd[ 0 ]) ; + pOutVect[1] = (res_even[ 1 ] + res_odd[ 1 ]) ; + pOutVect[2] = (res_even[ 2 ] + res_odd[ 2 ]) ; + pOutVect[3] = (res_even[ 3 ] + res_odd[ 3 ]) ; + pOutVect[7] = (res_even[ 0 ] - res_odd[ 0 ]) ; + pOutVect[6] = (res_even[ 1 ] - res_odd[ 1 ]) ; + pOutVect[5] = (res_even[ 2 ] - res_odd[ 2 ]) ; + pOutVect[4] = (res_even[ 3 ] - res_odd[ 3 ]) ; +#else + UINT8 Index, k; + SINT32 temp; + /*Calculate 4 subband samples by matrixing*/ + for (Index = 0; Index < 8; Index++) { + temp = 0; + for (k = 0; k < 16; k++) { + /*temp += (SINT32)(((SINT64)M[(Index*strEncParams->numOfSubBands*2)+k] * Y[k]) >> 16 );*/ + temp += (gas16AnalDCTcoeff8[(Index * 8 * 2) + k] * (pInVect[k] >> 16)); + temp += ((gas16AnalDCTcoeff8[(Index * 8 * 2) + k] * (pInVect[k] & 0xFFFF)) >> 16); + } + pOutVect[Index] = temp; + } +#endif + /* printf("pOutVect: 0x%x;0x%x;0x%x;0x%x;0x%x;0x%x;0x%x;0x%x\n",\ + pOutVect[0],pOutVect[1],pOutVect[2],pOutVect[3],pOutVect[4],pOutVect[5],pOutVect[6],pOutVect[7]);*/ +} + +/******************************************************************************* +** +** Function SBC_FastIDCT4 +** +** Description implementation of fast DCT algorithm by Feig and Winograd +** +** +** Returns y = dct(x0) +** +** +*******************************************************************************/ +void SBC_FastIDCT4(SINT32 *pInVect, SINT32 *pOutVect) +{ +#if (SBC_FAST_DCT == TRUE) +#if (SBC_ARM_ASM_OPT==TRUE) +#else +#if (SBC_IPAQ_OPT==TRUE) +#if (SBC_IS_64_MULT_IN_IDCT == TRUE) + SINT64 s64Temp; +#endif +#else +#if (SBC_IS_64_MULT_IN_IDCT == TRUE) + SINT32 s32HiTemp; +#else + UINT16 s32In2Temp; + SINT32 s32In1Temp; +#endif +#endif +#endif + SINT32 temp, x2; + SINT32 tmp[8]; + + x2 = pInVect[2] >> 1; + temp = (pInVect[0] + pInVect[4]); + SBC_IDCT_MULT((SBC_COS_PI_SUR_4 >> 1), temp , tmp[0]); + tmp[1] = x2 - tmp[0]; + tmp[0] += x2; + temp = (pInVect[1] + pInVect[3]); + SBC_IDCT_MULT((SBC_COS_3PI_SUR_8 >> 1), temp , tmp[3]); + SBC_IDCT_MULT((SBC_COS_PI_SUR_8 >> 1), temp , tmp[2]); + temp = (pInVect[5] - pInVect[7]); + SBC_IDCT_MULT((SBC_COS_3PI_SUR_8 >> 1), temp , tmp[5]); + SBC_IDCT_MULT((SBC_COS_PI_SUR_8 >> 1), temp , tmp[4]); + tmp[6] = tmp[2] + tmp[5]; + tmp[7] = tmp[3] - tmp[4]; + pOutVect[0] = (tmp[0] + tmp[6]); + pOutVect[1] = (tmp[1] + tmp[7]); + pOutVect[2] = (tmp[1] - tmp[7]); + pOutVect[3] = (tmp[0] - tmp[6]); +#else + UINT8 Index, k; + SINT32 temp; + /*Calculate 4 subband samples by matrixing*/ + for (Index = 0; Index < 4; Index++) { + temp = 0; + for (k = 0; k < 8; k++) { + /*temp += (SINT32)(((SINT64)M[(Index*strEncParams->numOfSubBands*2)+k] * Y[k]) >> 16 ); */ + temp += (gas16AnalDCTcoeff4[(Index * 4 * 2) + k] * (pInVect[k] >> 16)); + temp += ((gas16AnalDCTcoeff4[(Index * 4 * 2) + k] * (pInVect[k] & 0xFFFF)) >> 16); + } + pOutVect[Index] = temp; + } +#endif +} + +#endif /* #if (defined(SBC_ENC_INCLUDED) && SBC_ENC_INCLUDED == TRUE) */ diff --git a/lib/bt/host/bluedroid/external/sbc/encoder/srce/sbc_dct_coeffs.c b/lib/bt/host/bluedroid/external/sbc/encoder/srce/sbc_dct_coeffs.c new file mode 100644 index 00000000..44c61629 --- /dev/null +++ b/lib/bt/host/bluedroid/external/sbc/encoder/srce/sbc_dct_coeffs.c @@ -0,0 +1,203 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains the coefficient table used for DCT computation in + * analysis. + * + ******************************************************************************/ +#include "common/bt_target.h" +#include "sbc_encoder.h" + +#if (defined(SBC_ENC_INCLUDED) && SBC_ENC_INCLUDED == TRUE) + +/*DCT coeff for 4 sub-band case.*/ +#if (SBC_FAST_DCT == FALSE) +const SINT16 gas16AnalDCTcoeff4[] = { + (SINT16)(0.7071 * 32768), + (SINT16)(0.9239 * 32768), + (SINT16)(1.0000 * 32767), + (SINT16)(0.9239 * 32768), + (SINT16)(0.7071 * 32768), + (SINT16)(0.3827 * 32768), + (SINT16)(0.0000 * 32768), + (SINT16)(-0.3827 * 32768), + + (SINT16)(-0.7071 * 32768), + (SINT16)(0.3827 * 32768), + (SINT16)(1.0000 * 32767), + (SINT16)(0.3827 * 32768), + (SINT16)(-0.7071 * 32768), + (SINT16)(-0.9239 * 32768), + (SINT16)(-0.0000 * 32768), + (SINT16)(0.9239 * 32768), + + (SINT16)(-0.7071 * 32768), + (SINT16)(-0.3827 * 32768), + (SINT16)(1.0000 * 32767), + (SINT16)(-0.3827 * 32768), + (SINT16)(-0.7071 * 32768), + (SINT16)(0.9239 * 32768), + (SINT16)(0.0000 * 32768), + (SINT16)(-0.9239 * 32768), + + (SINT16)(0.7071 * 32768), + (SINT16)(-0.9239 * 32768), + (SINT16)(1.0000 * 32767), + (SINT16)(-0.9239 * 32768), + (SINT16)(0.7071 * 32768), + (SINT16)(-0.3827 * 32768), + (SINT16)(-0.0000 * 32768), + (SINT16)(0.3827 * 32768) +}; + +/*DCT coeff for 8 sub-band case.*/ +const SINT16 gas16AnalDCTcoeff8[] = { + (SINT16)(0.7071 * 32768), + (SINT16)(0.8315 * 32768), + (SINT16)(0.9239 * 32768), + (SINT16)(0.9808 * 32768), + (SINT16)(1.0000 * 32767), + (SINT16)(0.9808 * 32768), + (SINT16)(0.9239 * 32768), + (SINT16)(0.8315 * 32768), + (SINT16)(0.7071 * 32768), + (SINT16)(0.5556 * 32768), + (SINT16)(0.3827 * 32768), + (SINT16)(0.1951 * 32768), + (SINT16)(0.0000 * 32768), + (SINT16)(-0.1951 * 32768), + (SINT16)(-0.3827 * 32768), + (SINT16)(-0.5556 * 32768), + (SINT16)(-0.7071 * 32768), + (SINT16)(-0.1951 * 32768), + (SINT16)(0.3827 * 32768), + (SINT16)(0.8315 * 32768), + (SINT16)(1.0000 * 32767), + (SINT16)(0.8315 * 32768), + (SINT16)(0.3827 * 32768), + (SINT16)(-0.1951 * 32768), + (SINT16)(-0.7071 * 32768), + (SINT16)(-0.9808 * 32768), + (SINT16)(-0.9239 * 32768), + (SINT16)(-0.5556 * 32768), + (SINT16)(-0.0000 * 32768), + (SINT16)(0.5556 * 32768), + (SINT16)(0.9239 * 32768), + (SINT16)(0.9808 * 32768), + (SINT16)(-0.7071 * 32768), + (SINT16)(-0.9808 * 32768), + (SINT16)(-0.3827 * 32768), + (SINT16)(0.5556 * 32768), + (SINT16)(1.0000 * 32767), + (SINT16)(0.5556 * 32768), + (SINT16)(-0.3827 * 32768), + (SINT16)(-0.9808 * 32768), + (SINT16)(-0.7071 * 32768), + (SINT16)(0.1951 * 32768), + (SINT16)(0.9239 * 32768), + (SINT16)(0.8315 * 32768), + (SINT16)(0.0000 * 32768), + (SINT16)(-0.8315 * 32768), + (SINT16)(-0.9239 * 32768), + (SINT16)(-0.1951 * 32768), + (SINT16)(0.7071 * 32768), + (SINT16)(-0.5556 * 32768), + (SINT16)(-0.9239 * 32768), + (SINT16)(0.1951 * 32768), + (SINT16)(1.0000 * 32767), + (SINT16)(0.1951 * 32768), + (SINT16)(-0.9239 * 32768), + (SINT16)(-0.5556 * 32768), + (SINT16)(0.7071 * 32768), + (SINT16)(0.8315 * 32768), + (SINT16)(-0.3827 * 32768), + (SINT16)(-0.9808 * 32768), + (SINT16)(-0.0000 * 32768), + (SINT16)(0.9808 * 32768), + (SINT16)(0.3827 * 32768), + (SINT16)(-0.8315 * 32768), + (SINT16)(0.7071 * 32768), + (SINT16)(0.5556 * 32768), + (SINT16)(-0.9239 * 32768), + (SINT16)(-0.1951 * 32768), + (SINT16)(1.0000 * 32767), + (SINT16)(-0.1951 * 32768), + (SINT16)(-0.9239 * 32768), + (SINT16)(0.5556 * 32768), + (SINT16)(0.7071 * 32768), + (SINT16)(-0.8315 * 32768), + (SINT16)(-0.3827 * 32768), + (SINT16)(0.9808 * 32768), + (SINT16)(0.0000 * 32768), + (SINT16)(-0.9808 * 32768), + (SINT16)(0.3827 * 32768), + (SINT16)(0.8315 * 32768), + (SINT16)(-0.7071 * 32768), + (SINT16)(0.9808 * 32768), + (SINT16)(-0.3827 * 32768), + (SINT16)(-0.5556 * 32768), + (SINT16)(1.0000 * 32767), + (SINT16)(-0.5556 * 32768), + (SINT16)(-0.3827 * 32768), + (SINT16)(0.9808 * 32768), + (SINT16)(-0.7071 * 32768), + (SINT16)(-0.1951 * 32768), + (SINT16)(0.9239 * 32768), + (SINT16)(-0.8315 * 32768), + (SINT16)(-0.0000 * 32768), + (SINT16)(0.8315 * 32768), + (SINT16)(-0.9239 * 32768), + (SINT16)(0.1951 * 32768), + (SINT16)(-0.7071 * 32768), + (SINT16)(0.1951 * 32768), + (SINT16)(0.3827 * 32768), + (SINT16)(-0.8315 * 32768), + (SINT16)(1.0000 * 32767), + (SINT16)(-0.8315 * 32768), + (SINT16)(0.3827 * 32768), + (SINT16)(0.1951 * 32768), + (SINT16)(-0.7071 * 32768), + (SINT16)(0.9808 * 32768), + (SINT16)(-0.9239 * 32768), + (SINT16)(0.5556 * 32768), + (SINT16)(-0.0000 * 32768), + (SINT16)(-0.5556 * 32768), + (SINT16)(0.9239 * 32768), + (SINT16)(-0.9808 * 32768), + (SINT16)(0.7071 * 32768), + (SINT16)(-0.8315 * 32768), + (SINT16)(0.9239 * 32768), + (SINT16)(-0.9808 * 32768), + (SINT16)(1.0000 * 32767), + (SINT16)(-0.9808 * 32768), + (SINT16)(0.9239 * 32768), + (SINT16)(-0.8315 * 32768), + (SINT16)(0.7071 * 32768), + (SINT16)(-0.5556 * 32768), + (SINT16)(0.3827 * 32768), + (SINT16)(-0.1951 * 32768), + (SINT16)(-0.0000 * 32768), + (SINT16)(0.1951 * 32768), + (SINT16)(-0.3827 * 32768), + (SINT16)(0.5556 * 32768) +}; +#endif + +#endif /* #if (defined(SBC_ENC_INCLUDED) && SBC_ENC_INCLUDED == TRUE) */ diff --git a/lib/bt/host/bluedroid/external/sbc/encoder/srce/sbc_enc_bit_alloc_mono.c b/lib/bt/host/bluedroid/external/sbc/encoder/srce/sbc_enc_bit_alloc_mono.c new file mode 100644 index 00000000..5f10819b --- /dev/null +++ b/lib/bt/host/bluedroid/external/sbc/encoder/srce/sbc_enc_bit_alloc_mono.c @@ -0,0 +1,189 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains the code for bit allocation algorithm. It calculates + * the number of bits required for the encoded stream of data. + * + ******************************************************************************/ + +/*Includes*/ +#include "common/bt_target.h" +#include "sbc_encoder.h" +#include "sbc_enc_func_declare.h" + +#if (defined(SBC_ENC_INCLUDED) && SBC_ENC_INCLUDED == TRUE) + +/*global arrays*/ +const SINT16 sbc_enc_as16Offset4[4][4] = { { -1, 0, 0, 0}, { -2, 0, 0, 1}, + { -2, 0, 0, 1}, { -2, 0, 0, 1} +}; +const SINT16 sbc_enc_as16Offset8[4][8] = { { -2, 0, 0, 0, 0, 0, 0, 1}, + { -3, 0, 0, 0, 0, 0, 1, 2}, + { -4, 0, 0, 0, 0, 0, 1, 2}, + { -4, 0, 0, 0, 0, 0, 1, 2} +}; + +/**************************************************************************** +* BitAlloc - Calculates the required number of bits for the given scale factor +* and the number of subbands. +* +* RETURNS : N/A +*/ + +void sbc_enc_bit_alloc_mono(SBC_ENC_PARAMS *pstrCodecParams) +{ + SINT32 s32MaxBitNeed; /*to store the max bits needed per sb*/ + SINT32 s32BitCount; /*the used number of bits*/ + SINT32 s32SliceCount; /*to store hwo many slices can be put in bitpool*/ + SINT32 s32BitSlice; /*number of bitslices in bitpool*/ + SINT32 s32Sb; /*counter for sub-band*/ + SINT32 s32Ch; /*counter for channel*/ + SINT16 *ps16BitNeed; /*temp memory to store required number of bits*/ + SINT32 s32Loudness; /*used in Loudness calculation*/ + SINT16 *ps16GenBufPtr; + SINT16 *ps16GenArrPtr; + SINT16 *ps16GenTabPtr; + SINT32 s32NumOfSubBands = pstrCodecParams->s16NumOfSubBands; + + ps16BitNeed = pstrCodecParams->s16ScartchMemForBitAlloc; + + for (s32Ch = 0; s32Ch < pstrCodecParams->s16NumOfChannels; s32Ch++) { + ps16GenBufPtr = ps16BitNeed + s32Ch * s32NumOfSubBands; + ps16GenArrPtr = pstrCodecParams->as16Bits + s32Ch * SBC_MAX_NUM_OF_SUBBANDS; + + /* bitneed values are derived from scale factor */ + if (pstrCodecParams->s16AllocationMethod == SBC_SNR) { + ps16BitNeed = pstrCodecParams->as16ScaleFactor; + ps16GenBufPtr = ps16BitNeed + s32Ch * s32NumOfSubBands; + } else { + ps16GenBufPtr = ps16BitNeed + s32Ch * s32NumOfSubBands; + if (s32NumOfSubBands == 4) { + ps16GenTabPtr = (SINT16 *) + sbc_enc_as16Offset4[pstrCodecParams->s16SamplingFreq]; + } else { + ps16GenTabPtr = (SINT16 *) + sbc_enc_as16Offset8[pstrCodecParams->s16SamplingFreq]; + } + for (s32Sb = 0; s32Sb < s32NumOfSubBands; s32Sb++) { + if (pstrCodecParams->as16ScaleFactor[s32Ch * s32NumOfSubBands + s32Sb] == 0) { + *(ps16GenBufPtr) = -5; + } else { + s32Loudness = + (SINT32)(pstrCodecParams->as16ScaleFactor[s32Ch * s32NumOfSubBands + s32Sb] + - *ps16GenTabPtr); + if (s32Loudness > 0) { + *(ps16GenBufPtr) = (SINT16)(s32Loudness >> 1); + } else { + *(ps16GenBufPtr) = (SINT16)s32Loudness; + } + } + ps16GenBufPtr++; + ps16GenTabPtr++; + } + + } + + /* max bitneed index is searched*/ + s32MaxBitNeed = 0; + ps16GenBufPtr = ps16BitNeed + s32Ch * s32NumOfSubBands; + for (s32Sb = 0; s32Sb < s32NumOfSubBands; s32Sb++) { + if ( *(ps16GenBufPtr) > s32MaxBitNeed) { + s32MaxBitNeed = *(ps16GenBufPtr); + } + + ps16GenBufPtr++; + } + ps16GenBufPtr = ps16BitNeed + s32Ch * s32NumOfSubBands; + /*iterative process to find hwo many bitslices fit into the bitpool*/ + s32BitSlice = s32MaxBitNeed + 1; + s32BitCount = pstrCodecParams->s16BitPool; + s32SliceCount = 0; + do { + s32BitSlice --; + s32BitCount -= s32SliceCount; + s32SliceCount = 0; + + for (s32Sb = 0; s32Sb < s32NumOfSubBands; s32Sb++) { + if ( (((*ps16GenBufPtr - s32BitSlice) < 16) && (*ps16GenBufPtr - s32BitSlice) >= 1)) { + if ((*ps16GenBufPtr - s32BitSlice) == 1) { + s32SliceCount += 2; + } else { + s32SliceCount++; + } + } + ps16GenBufPtr++; + + }/*end of for*/ + ps16GenBufPtr = ps16BitNeed + s32Ch * s32NumOfSubBands; + } while (s32BitCount - s32SliceCount > 0); + + if (s32BitCount == 0) { + s32BitCount -= s32SliceCount; + s32BitSlice --; + } + + /*Bits are distributed until the last bitslice is reached*/ + ps16GenArrPtr = pstrCodecParams->as16Bits + s32Ch * s32NumOfSubBands; + ps16GenBufPtr = ps16BitNeed + s32Ch * s32NumOfSubBands; + for (s32Sb = 0; s32Sb < s32NumOfSubBands; s32Sb++) { + if (*(ps16GenBufPtr) < s32BitSlice + 2) { + *(ps16GenArrPtr) = 0; + } else { + *(ps16GenArrPtr) = ((*(ps16GenBufPtr) - s32BitSlice) < 16) ? + (SINT16)(*(ps16GenBufPtr) - s32BitSlice) : 16; + } + + ps16GenBufPtr++; + ps16GenArrPtr++; + } + ps16GenArrPtr = pstrCodecParams->as16Bits + s32Ch * s32NumOfSubBands; + ps16GenBufPtr = ps16BitNeed + s32Ch * s32NumOfSubBands; + /*the remaining bits are allocated starting at subband 0*/ + s32Sb = 0; + while ( (s32BitCount > 0) && (s32Sb < s32NumOfSubBands) ) { + if ( (*(ps16GenArrPtr) >= 2) && (*(ps16GenArrPtr) < 16) ) { + (*(ps16GenArrPtr))++; + s32BitCount--; + } else if ( (*(ps16GenBufPtr) == s32BitSlice + 1) && + (s32BitCount > 1) ) { + *(ps16GenArrPtr) = 2; + s32BitCount -= 2; + } + s32Sb++; + ps16GenArrPtr++; + ps16GenBufPtr++; + } + ps16GenArrPtr = pstrCodecParams->as16Bits + s32Ch * s32NumOfSubBands; + + + s32Sb = 0; + while ( (s32BitCount > 0) && (s32Sb < s32NumOfSubBands) ) { + if ( *(ps16GenArrPtr) < 16) { + (*(ps16GenArrPtr))++; + s32BitCount--; + } + s32Sb++; + ps16GenArrPtr++; + } + } +} +/*End of BitAlloc() function*/ + +#endif /* #if (defined(SBC_ENC_INCLUDED) && SBC_ENC_INCLUDED == TRUE) */ diff --git a/lib/bt/host/bluedroid/external/sbc/encoder/srce/sbc_enc_bit_alloc_ste.c b/lib/bt/host/bluedroid/external/sbc/encoder/srce/sbc_enc_bit_alloc_ste.c new file mode 100644 index 00000000..0ef8e783 --- /dev/null +++ b/lib/bt/host/bluedroid/external/sbc/encoder/srce/sbc_enc_bit_alloc_ste.c @@ -0,0 +1,193 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains the code for bit allocation algorithm. It calculates + * the number of bits required for the encoded stream of data. + * + ******************************************************************************/ + +/*Includes*/ +#include "common/bt_target.h" +#include "sbc_encoder.h" +#include "sbc_enc_func_declare.h" + +#if (defined(SBC_ENC_INCLUDED) && SBC_ENC_INCLUDED == TRUE) + +/*global arrays*/ +extern const SINT16 sbc_enc_as16Offset4[4][4]; +extern const SINT16 sbc_enc_as16Offset8[4][8]; + +/**************************************************************************** +* BitAlloc - Calculates the required number of bits for the given scale factor +* and the number of subbands. +* +* RETURNS : N/A +*/ + +void sbc_enc_bit_alloc_ste(SBC_ENC_PARAMS *pstrCodecParams) +{ + /* CAUTIOM -> mips optim for arm 32 require to use SINT32 instead of SINT16 */ + /* Do not change variable type or name */ + SINT32 s32MaxBitNeed; /*to store the max bits needed per sb*/ + SINT32 s32BitCount; /*the used number of bits*/ + SINT32 s32SliceCount; /*to store hwo many slices can be put in bitpool*/ + SINT32 s32BitSlice; /*number of bitslices in bitpool*/ + SINT32 s32Sb; /*counter for sub-band*/ + SINT32 s32Ch; /*counter for channel*/ + SINT16 *ps16BitNeed; /*temp memory to store required number of bits*/ + SINT32 s32Loudness; /*used in Loudness calculation*/ + SINT16 *ps16GenBufPtr, *pas16ScaleFactor; + SINT16 *ps16GenArrPtr; + SINT16 *ps16GenTabPtr; + SINT32 s32NumOfSubBands = pstrCodecParams->s16NumOfSubBands; + SINT32 s32BitPool = pstrCodecParams->s16BitPool; + + /* bitneed values are derived from scale factor */ + if (pstrCodecParams->s16AllocationMethod == SBC_SNR) { + ps16BitNeed = pstrCodecParams->as16ScaleFactor; + s32MaxBitNeed = pstrCodecParams->s16MaxBitNeed; + } else { + ps16BitNeed = pstrCodecParams->s16ScartchMemForBitAlloc; + pas16ScaleFactor = pstrCodecParams->as16ScaleFactor; + s32MaxBitNeed = 0; + ps16GenBufPtr = ps16BitNeed; + for (s32Ch = 0; s32Ch < 2; s32Ch++) { + if (s32NumOfSubBands == 4) { + ps16GenTabPtr = (SINT16 *)sbc_enc_as16Offset4[pstrCodecParams->s16SamplingFreq]; + } else { + ps16GenTabPtr = (SINT16 *)sbc_enc_as16Offset8[pstrCodecParams->s16SamplingFreq]; + } + + for (s32Sb = 0; s32Sb < s32NumOfSubBands; s32Sb++) { + if (*pas16ScaleFactor == 0) { + *ps16GenBufPtr = -5; + } else { + s32Loudness = (SINT32)(*pas16ScaleFactor - *ps16GenTabPtr); + + if (s32Loudness > 0) { + *ps16GenBufPtr = (SINT16)(s32Loudness >> 1); + } else { + *ps16GenBufPtr = (SINT16)s32Loudness; + } + } + + if (*ps16GenBufPtr > s32MaxBitNeed) { + s32MaxBitNeed = *ps16GenBufPtr; + } + pas16ScaleFactor++; + ps16GenBufPtr++; + ps16GenTabPtr++; + } + } + } + + /* iterative process to find out hwo many bitslices fit into the bitpool */ + s32BitSlice = s32MaxBitNeed + 1; + s32BitCount = s32BitPool; + s32SliceCount = 0; + do { + s32BitSlice --; + s32BitCount -= s32SliceCount; + s32SliceCount = 0; + ps16GenBufPtr = ps16BitNeed; + + for (s32Sb = 0; s32Sb < 2 * s32NumOfSubBands; s32Sb++) { + if ( (*ps16GenBufPtr >= s32BitSlice + 1) && (*ps16GenBufPtr < s32BitSlice + 16) ) { + if (*(ps16GenBufPtr) == s32BitSlice + 1) { + s32SliceCount += 2; + } else { + s32SliceCount++; + } + } + ps16GenBufPtr++; + } + } while (s32BitCount - s32SliceCount > 0); + + if (s32BitCount - s32SliceCount == 0) { + s32BitCount -= s32SliceCount; + s32BitSlice --; + } + + /* Bits are distributed until the last bitslice is reached */ + ps16GenBufPtr = ps16BitNeed; + ps16GenArrPtr = pstrCodecParams->as16Bits; + for (s32Ch = 0; s32Ch < 2; s32Ch++) { + for (s32Sb = 0; s32Sb < s32NumOfSubBands; s32Sb++) { + if (*ps16GenBufPtr < s32BitSlice + 2) { + *ps16GenArrPtr = 0; + } else { + *ps16GenArrPtr = ((*(ps16GenBufPtr) - s32BitSlice) < 16) ? + (SINT16)(*(ps16GenBufPtr) - s32BitSlice) : 16; + } + ps16GenBufPtr++; + ps16GenArrPtr++; + } + } + + /* the remaining bits are allocated starting at subband 0 */ + s32Ch = 0; + s32Sb = 0; + ps16GenBufPtr = ps16BitNeed; + ps16GenArrPtr -= 2 * s32NumOfSubBands; + + while ( (s32BitCount > 0) && (s32Sb < s32NumOfSubBands) ) { + if ( (*(ps16GenArrPtr) >= 2) && (*(ps16GenArrPtr) < 16) ) { + (*(ps16GenArrPtr))++; + s32BitCount--; + } else if ((*ps16GenBufPtr == s32BitSlice + 1) && (s32BitCount > 1)) { + *(ps16GenArrPtr) = 2; + s32BitCount -= 2; + } + if (s32Ch == 1) { + s32Ch = 0; + s32Sb++; + ps16GenBufPtr = ps16BitNeed + s32Sb; + ps16GenArrPtr = pstrCodecParams->as16Bits + s32Sb; + + } else { + s32Ch = 1; + ps16GenBufPtr = ps16BitNeed + s32NumOfSubBands + s32Sb; + ps16GenArrPtr = pstrCodecParams->as16Bits + s32NumOfSubBands + s32Sb; + } + } + + s32Ch = 0; + s32Sb = 0; + ps16GenArrPtr = pstrCodecParams->as16Bits; + + while ((s32BitCount > 0) && (s32Sb < s32NumOfSubBands)) { + if (*(ps16GenArrPtr) < 16) { + (*(ps16GenArrPtr))++; + s32BitCount--; + } + if (s32Ch == 1) { + s32Ch = 0; + s32Sb++; + ps16GenArrPtr = pstrCodecParams->as16Bits + s32Sb; + } else { + s32Ch = 1; + ps16GenArrPtr = pstrCodecParams->as16Bits + s32NumOfSubBands + s32Sb; + } + } +} + +/*End of BitAlloc() function*/ + +#endif /* #if (defined(SBC_ENC_INCLUDED) && SBC_ENC_INCLUDED == TRUE) */ diff --git a/lib/bt/host/bluedroid/external/sbc/encoder/srce/sbc_enc_coeffs.c b/lib/bt/host/bluedroid/external/sbc/encoder/srce/sbc_enc_coeffs.c new file mode 100644 index 00000000..a3099f8a --- /dev/null +++ b/lib/bt/host/bluedroid/external/sbc/encoder/srce/sbc_enc_coeffs.c @@ -0,0 +1,318 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains the Windowing coeffs for synthesis filter + * + ******************************************************************************/ +#include "common/bt_target.h" +#include "sbc_encoder.h" + +#if (defined(SBC_ENC_INCLUDED) && SBC_ENC_INCLUDED == TRUE) + +#if (SBC_ARM_ASM_OPT==FALSE && SBC_IPAQ_OPT==FALSE) +#if (SBC_IS_64_MULT_IN_WINDOW_ACCU == FALSE) +/*Window coeff for 4 sub band case*/ +const SINT16 gas32CoeffFor4SBs[] = { + (SINT16)((SINT32)0x00000000 >> 16), (SINT16)0x00000000, + (SINT16)((SINT32)0x001194E6 >> 16), (SINT16)0x001194E6, + (SINT16)((SINT32)0x0030E2D3 >> 16), (SINT16)0x0030E2D3, + (SINT16)((SINT32)0x00599403 >> 16), (SINT16)0x00599403, + (SINT16)((SINT32)0x007DBCC8 >> 16), (SINT16)0x007DBCC8, + (SINT16)((SINT32)0x007F88E4 >> 16), (SINT16)0x007F88E4, + (SINT16)((SINT32)0x003D239B >> 16), (SINT16)0x003D239B, + (SINT16)((SINT32)0xFF9BB9D5 >> 16), (SINT16)0xFF9BB9D5, + + (SINT16)((SINT32)0x01659F45 >> 16), (SINT16)0x01659F45, + (SINT16)((SINT32)0x029DBAA3 >> 16), (SINT16)0x029DBAA3, + (SINT16)((SINT32)0x03B23341 >> 16), (SINT16)0x03B23341, + (SINT16)((SINT32)0x041EEE40 >> 16), (SINT16)0x041EEE40, + (SINT16)((SINT32)0x034FEE2C >> 16), (SINT16)0x034FEE2C, + (SINT16)((SINT32)0x00C8F2BC >> 16), (SINT16)0x00C8F2BC, + (SINT16)((SINT32)0xFC4F91D4 >> 16), (SINT16)0xFC4F91D4, + (SINT16)((SINT32)0xF60FAF37 >> 16), (SINT16)0xF60FAF37, + + (SINT16)((SINT32)0x115B1ED2 >> 16), (SINT16)0x115B1ED2, + (SINT16)((SINT32)0x18F55C90 >> 16), (SINT16)0x18F55C90, + (SINT16)((SINT32)0x1F91CA46 >> 16), (SINT16)0x1F91CA46, + (SINT16)((SINT32)0x2412F251 >> 16), (SINT16)0x2412F251, + (SINT16)((SINT32)0x25AC1FF2 >> 16), (SINT16)0x25AC1FF2, + (SINT16)((SINT32)0x2412F251 >> 16), (SINT16)0x2412F251, + (SINT16)((SINT32)0x1F91CA46 >> 16), (SINT16)0x1F91CA46, + (SINT16)((SINT32)0x18F55C90 >> 16), (SINT16)0x18F55C90, + + (SINT16)((SINT32)0xEEA4E12E >> 16), (SINT16)0xEEA4E12E, + (SINT16)((SINT32)0xF60FAF37 >> 16), (SINT16)0xF60FAF37, + (SINT16)((SINT32)0xFC4F91D4 >> 16), (SINT16)0xFC4F91D4, + (SINT16)((SINT32)0x00C8F2BC >> 16), (SINT16)0x00C8F2BC, + (SINT16)((SINT32)0x034FEE2C >> 16), (SINT16)0x034FEE2C, + (SINT16)((SINT32)0x041EEE40 >> 16), (SINT16)0x041EEE40, + (SINT16)((SINT32)0x03B23341 >> 16), (SINT16)0x03B23341, + (SINT16)((SINT32)0x029DBAA3 >> 16), (SINT16)0x029DBAA3, + + (SINT16)((SINT32)0xFE9A60BB >> 16), (SINT16)0xFE9A60BB, + (SINT16)((SINT32)0xFF9BB9D5 >> 16), (SINT16)0xFF9BB9D5, + (SINT16)((SINT32)0x003D239B >> 16), (SINT16)0x003D239B, + (SINT16)((SINT32)0x007F88E4 >> 16), (SINT16)0x007F88E4, + (SINT16)((SINT32)0x007DBCC8 >> 16), (SINT16)0x007DBCC8, + (SINT16)((SINT32)0x00599403 >> 16), (SINT16)0x00599403, + (SINT16)((SINT32)0x0030E2D3 >> 16), (SINT16)0x0030E2D3, + (SINT16)((SINT32)0x001194E6 >> 16), (SINT16)0x001194E6 +}; + +/*Window coeff for 8 sub band case*/ +const SINT16 gas32CoeffFor8SBs[] = { + (SINT16)((SINT32)0x00000000 >> 16), (SINT16)0x00000000, + (SINT16)((SINT32)0x00052173 >> 16), (SINT16)0x00052173, + (SINT16)((SINT32)0x000B3F71 >> 16), (SINT16)0x000B3F71, + (SINT16)((SINT32)0x00122C7D >> 16), (SINT16)0x00122C7D, + (SINT16)((SINT32)0x001AFF89 >> 16), (SINT16)0x001AFF89, + (SINT16)((SINT32)0x00255A62 >> 16), (SINT16)0x00255A62, + (SINT16)((SINT32)0x003060F4 >> 16), (SINT16)0x003060F4, + (SINT16)((SINT32)0x003A72E7 >> 16), (SINT16)0x003A72E7, + + (SINT16)((SINT32)0x0041EC6A >> 16), (SINT16)0x0041EC6A, /* 8 */ + (SINT16)((SINT32)0x0044EF48 >> 16), (SINT16)0x0044EF48, + (SINT16)((SINT32)0x00415B75 >> 16), (SINT16)0x00415B75, + (SINT16)((SINT32)0x0034F8B6 >> 16), (SINT16)0x0034F8B6, + (SINT16)((SINT32)0x001D8FD2 >> 16), (SINT16)0x001D8FD2, + (SINT16)((SINT32)0xFFFA2413 >> 16), (SINT16)0xFFFA2413, + (SINT16)((SINT32)0xFFC9F10E >> 16), (SINT16)0xFFC9F10E, + (SINT16)((SINT32)0xFF8D6793 >> 16), (SINT16)0xFF8D6793, + + (SINT16)((SINT32)0x00B97348 >> 16), (SINT16)0x00B97348, /* 16 */ + (SINT16)((SINT32)0x01071B96 >> 16), (SINT16)0x01071B96, + (SINT16)((SINT32)0x0156B3CA >> 16), (SINT16)0x0156B3CA, + (SINT16)((SINT32)0x01A1B38B >> 16), (SINT16)0x01A1B38B, + (SINT16)((SINT32)0x01E0224C >> 16), (SINT16)0x01E0224C, + (SINT16)((SINT32)0x0209291F >> 16), (SINT16)0x0209291F, + (SINT16)((SINT32)0x02138653 >> 16), (SINT16)0x02138653, + (SINT16)((SINT32)0x01F5F424 >> 16), (SINT16)0x01F5F424, + + (SINT16)((SINT32)0x01A7ECEF >> 16), (SINT16)0x01A7ECEF, /* 24 */ + (SINT16)((SINT32)0x01223EBA >> 16), (SINT16)0x01223EBA, + (SINT16)((SINT32)0x005FD0FF >> 16), (SINT16)0x005FD0FF, + (SINT16)((SINT32)0xFF5EEB73 >> 16), (SINT16)0xFF5EEB73, + (SINT16)((SINT32)0xFE20435D >> 16), (SINT16)0xFE20435D, + (SINT16)((SINT32)0xFCA86E7E >> 16), (SINT16)0xFCA86E7E, + (SINT16)((SINT32)0xFAFF95FC >> 16), (SINT16)0xFAFF95FC, + (SINT16)((SINT32)0xF9312891 >> 16), (SINT16)0xF9312891, + + (SINT16)((SINT32)0x08B4307A >> 16), (SINT16)0x08B4307A, /* 32 */ + (SINT16)((SINT32)0x0A9F3E9A >> 16), (SINT16)0x0A9F3E9A, + (SINT16)((SINT32)0x0C7D59B6 >> 16), (SINT16)0x0C7D59B6, + (SINT16)((SINT32)0x0E3BB16F >> 16), (SINT16)0x0E3BB16F, + (SINT16)((SINT32)0x0FC721F9 >> 16), (SINT16)0x0FC721F9, + (SINT16)((SINT32)0x110ECEF0 >> 16), (SINT16)0x110ECEF0, + (SINT16)((SINT32)0x120435FA >> 16), (SINT16)0x120435FA, + (SINT16)((SINT32)0x129C226F >> 16), (SINT16)0x129C226F, + + (SINT16)((SINT32)0x12CF6C75 >> 16), (SINT16)0x12CF6C75, /* 40 */ + (SINT16)((SINT32)0x129C226F >> 16), (SINT16)0x129C226F, + (SINT16)((SINT32)0x120435FA >> 16), (SINT16)0x120435FA, + (SINT16)((SINT32)0x110ECEF0 >> 16), (SINT16)0x110ECEF0, + (SINT16)((SINT32)0x0FC721F9 >> 16), (SINT16)0x0FC721F9, + (SINT16)((SINT32)0x0E3BB16F >> 16), (SINT16)0x0E3BB16F, + (SINT16)((SINT32)0x0C7D59B6 >> 16), (SINT16)0x0C7D59B6, + (SINT16)((SINT32)0x0A9F3E9A >> 16), (SINT16)0x0A9F3E9A, + + (SINT16)((SINT32)0xF74BCF86 >> 16), (SINT16)0xF74BCF86, /* 48 */ + (SINT16)((SINT32)0xF9312891 >> 16), (SINT16)0xF9312891, + (SINT16)((SINT32)0xFAFF95FC >> 16), (SINT16)0xFAFF95FC, + (SINT16)((SINT32)0xFCA86E7E >> 16), (SINT16)0xFCA86E7E, + (SINT16)((SINT32)0xFE20435D >> 16), (SINT16)0xFE20435D, + (SINT16)((SINT32)0xFF5EEB73 >> 16), (SINT16)0xFF5EEB73, + (SINT16)((SINT32)0x005FD0FF >> 16), (SINT16)0x005FD0FF, + (SINT16)((SINT32)0x01223EBA >> 16), (SINT16)0x01223EBA, + + (SINT16)((SINT32)0x01A7ECEF >> 16), (SINT16)0x01A7ECEF, /* 56 */ + (SINT16)((SINT32)0x01F5F424 >> 16), (SINT16)0x01F5F424, + (SINT16)((SINT32)0x02138653 >> 16), (SINT16)0x02138653, + (SINT16)((SINT32)0x0209291F >> 16), (SINT16)0x0209291F, + (SINT16)((SINT32)0x01E0224C >> 16), (SINT16)0x01E0224C, + (SINT16)((SINT32)0x01A1B38B >> 16), (SINT16)0x01A1B38B, + (SINT16)((SINT32)0x0156B3CA >> 16), (SINT16)0x0156B3CA, + (SINT16)((SINT32)0x01071B96 >> 16), (SINT16)0x01071B96, + + (SINT16)((SINT32)0xFF468CB8 >> 16), (SINT16)0xFF468CB8, /* 64 */ + (SINT16)((SINT32)0xFF8D6793 >> 16), (SINT16)0xFF8D6793, + (SINT16)((SINT32)0xFFC9F10E >> 16), (SINT16)0xFFC9F10E, + (SINT16)((SINT32)0xFFFA2413 >> 16), (SINT16)0xFFFA2413, + (SINT16)((SINT32)0x001D8FD2 >> 16), (SINT16)0x001D8FD2, + (SINT16)((SINT32)0x0034F8B6 >> 16), (SINT16)0x0034F8B6, + (SINT16)((SINT32)0x00415B75 >> 16), (SINT16)0x00415B75, + (SINT16)((SINT32)0x0044EF48 >> 16), (SINT16)0x0044EF48, + + (SINT16)((SINT32)0x0041EC6A >> 16), (SINT16)0x0041EC6A, /* 72 */ + (SINT16)((SINT32)0x003A72E7 >> 16), (SINT16)0x003A72E7, + (SINT16)((SINT32)0x003060F4 >> 16), (SINT16)0x003060F4, + (SINT16)((SINT32)0x00255A62 >> 16), (SINT16)0x00255A62, + (SINT16)((SINT32)0x001AFF89 >> 16), (SINT16)0x001AFF89, + (SINT16)((SINT32)0x00122C7D >> 16), (SINT16)0x00122C7D, + (SINT16)((SINT32)0x000B3F71 >> 16), (SINT16)0x000B3F71, + (SINT16)((SINT32)0x00052173 >> 16), (SINT16)0x00052173 +}; + +#else + +/*Window coeff for 4 sub band case*/ +const SINT32 gas32CoeffFor4SBs[] = { + (SINT32)0x00000000, + (SINT32)0x001194E6, + (SINT32)0x0030E2D3, + (SINT32)0x00599403, + (SINT32)0x007DBCC8, + (SINT32)0x007F88E4, + (SINT32)0x003D239B, + (SINT32)0xFF9BB9D5, + + (SINT32)0x01659F45, + (SINT32)0x029DBAA3, + (SINT32)0x03B23341, + (SINT32)0x041EEE40, + (SINT32)0x034FEE2C, + (SINT32)0x00C8F2BC, + (SINT32)0xFC4F91D4, + (SINT32)0xF60FAF37, + + (SINT32)0x115B1ED2, + (SINT32)0x18F55C90, + (SINT32)0x1F91CA46, + (SINT32)0x2412F251, + (SINT32)0x25AC1FF2, + (SINT32)0x2412F251, + (SINT32)0x1F91CA46, + (SINT32)0x18F55C90, + + (SINT32)0xEEA4E12E, + (SINT32)0xF60FAF37, + (SINT32)0xFC4F91D4, + (SINT32)0x00C8F2BC, + (SINT32)0x034FEE2C, + (SINT32)0x041EEE40, + (SINT32)0x03B23341, + (SINT32)0x029DBAA3, + + (SINT32)0xFE9A60BB, + (SINT32)0xFF9BB9D5, + (SINT32)0x003D239B, + (SINT32)0x007F88E4, + (SINT32)0x007DBCC8, + (SINT32)0x00599403, + (SINT32)0x0030E2D3, + (SINT32)0x001194E6 +}; + +/*Window coeff for 8 sub band case*/ +const SINT32 gas32CoeffFor8SBs[] = { + (SINT32)0x00000000, + (SINT32)0x00052173, + (SINT32)0x000B3F71, + (SINT32)0x00122C7D, + (SINT32)0x001AFF89, + (SINT32)0x00255A62, + (SINT32)0x003060F4, + (SINT32)0x003A72E7, + + (SINT32)0x0041EC6A, /* 8 */ + (SINT32)0x0044EF48, + (SINT32)0x00415B75, + (SINT32)0x0034F8B6, + (SINT32)0x001D8FD2, + (SINT32)0xFFFA2413, + (SINT32)0xFFC9F10E, + (SINT32)0xFF8D6793, + + (SINT32)0x00B97348, /* 16 */ + (SINT32)0x01071B96, + (SINT32)0x0156B3CA, + (SINT32)0x01A1B38B, + (SINT32)0x01E0224C, + (SINT32)0x0209291F, + (SINT32)0x02138653, + (SINT32)0x01F5F424, + + (SINT32)0x01A7ECEF, /* 24 */ + (SINT32)0x01223EBA, + (SINT32)0x005FD0FF, + (SINT32)0xFF5EEB73, + (SINT32)0xFE20435D, + (SINT32)0xFCA86E7E, + (SINT32)0xFAFF95FC, + (SINT32)0xF9312891, + + (SINT32)0x08B4307A, /* 32 */ + (SINT32)0x0A9F3E9A, + (SINT32)0x0C7D59B6, + (SINT32)0x0E3BB16F, + (SINT32)0x0FC721F9, + (SINT32)0x110ECEF0, + (SINT32)0x120435FA, + (SINT32)0x129C226F, + + (SINT32)0x12CF6C75, /* 40 */ + (SINT32)0x129C226F, + (SINT32)0x120435FA, + (SINT32)0x110ECEF0, + (SINT32)0x0FC721F9, + (SINT32)0x0E3BB16F, + (SINT32)0x0C7D59B6, + (SINT32)0x0A9F3E9A, + + (SINT32)0xF74BCF86, /* 48 */ + (SINT32)0xF9312891, + (SINT32)0xFAFF95FC, + (SINT32)0xFCA86E7E, + (SINT32)0xFE20435D, + (SINT32)0xFF5EEB73, + (SINT32)0x005FD0FF, + (SINT32)0x01223EBA, + + (SINT32)0x01A7ECEF, /* 56 */ + (SINT32)0x01F5F424, + (SINT32)0x02138653, + (SINT32)0x0209291F, + (SINT32)0x01E0224C, + (SINT32)0x01A1B38B, + (SINT32)0x0156B3CA, + (SINT32)0x01071B96, + + (SINT32)0xFF468CB8, /* 64 */ + (SINT32)0xFF8D6793, + (SINT32)0xFFC9F10E, + (SINT32)0xFFFA2413, + (SINT32)0x001D8FD2, + (SINT32)0x0034F8B6, + (SINT32)0x00415B75, + (SINT32)0x0044EF48, + + (SINT32)0x0041EC6A, /* 72 */ + (SINT32)0x003A72E7, + (SINT32)0x003060F4, + (SINT32)0x00255A62, + (SINT32)0x001AFF89, + (SINT32)0x00122C7D, + (SINT32)0x000B3F71, + (SINT32)0x00052173 +}; + +#endif +#endif + +#endif /* #if (defined(SBC_ENC_INCLUDED) && SBC_ENC_INCLUDED == TRUE) */ diff --git a/lib/bt/host/bluedroid/external/sbc/encoder/srce/sbc_encoder.c b/lib/bt/host/bluedroid/external/sbc/encoder/srce/sbc_encoder.c new file mode 100644 index 00000000..c0ab99b8 --- /dev/null +++ b/lib/bt/host/bluedroid/external/sbc/encoder/srce/sbc_encoder.c @@ -0,0 +1,333 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * contains code for encoder flow and initalization of encoder + * + ******************************************************************************/ + +#include +#include "common/bt_target.h" +#include "sbc_encoder.h" +#include "sbc_enc_func_declare.h" + +#if (defined(SBC_ENC_INCLUDED) && SBC_ENC_INCLUDED == TRUE) + +SINT16 EncMaxShiftCounter; + +#if (SBC_JOINT_STE_INCLUDED == TRUE) +SINT32 s32LRDiff[SBC_MAX_NUM_OF_BLOCKS] = {0}; +SINT32 s32LRSum[SBC_MAX_NUM_OF_BLOCKS] = {0}; +#endif + +void SBC_Encoder(SBC_ENC_PARAMS *pstrEncParams) +{ + SINT32 s32Ch; /* counter for ch*/ + SINT32 s32Sb; /* counter for sub-band*/ + UINT32 u32Count, maxBit = 0; /* loop count*/ + SINT32 s32MaxValue; /* temp variable to store max value */ + + SINT16 *ps16ScfL; + SINT32 *SbBuffer; + SINT32 s32Blk; /* counter for block*/ + SINT32 s32NumOfBlocks = pstrEncParams->s16NumOfBlocks; +#if (SBC_JOINT_STE_INCLUDED == TRUE) + SINT32 s32MaxValue2; + UINT32 u32CountSum, u32CountDiff; + SINT32 *pSum, *pDiff; +#endif + register SINT32 s32NumOfSubBands = pstrEncParams->s16NumOfSubBands; + + pstrEncParams->pu8NextPacket = pstrEncParams->pu8Packet; + +#if (SBC_NO_PCM_CPY_OPTION == TRUE) + pstrEncParams->ps16NextPcmBuffer = pstrEncParams->ps16PcmBuffer; +#else + pstrEncParams->ps16NextPcmBuffer = pstrEncParams->as16PcmBuffer; +#endif + do { + /* SBC ananlysis filter*/ + if (s32NumOfSubBands == 4) { + SbcAnalysisFilter4(pstrEncParams); + } else { + SbcAnalysisFilter8(pstrEncParams); + } + + /* compute the scale factor, and save the max */ + ps16ScfL = pstrEncParams->as16ScaleFactor; + s32Ch = pstrEncParams->s16NumOfChannels * s32NumOfSubBands; + + pstrEncParams->ps16NextPcmBuffer += s32Ch * s32NumOfBlocks; /* in case of multible sbc frame to encode update the pcm pointer */ + + for (s32Sb = 0; s32Sb < s32Ch; s32Sb++) { + SbBuffer = pstrEncParams->s32SbBuffer + s32Sb; + s32MaxValue = 0; + for (s32Blk = s32NumOfBlocks; s32Blk > 0; s32Blk--) { + if (s32MaxValue < abs32(*SbBuffer)) { + s32MaxValue = abs32(*SbBuffer); + } + SbBuffer += s32Ch; + } + + u32Count = (s32MaxValue > 0x800000) ? 9 : 0; + + for ( ; u32Count < 15; u32Count++) { + if (s32MaxValue <= (SINT32)(0x8000 << u32Count)) { + break; + } + } + *ps16ScfL++ = (SINT16)u32Count; + + if (u32Count > maxBit) { + maxBit = u32Count; + } + } + /* In case of JS processing,check whether to use JS */ +#if (SBC_JOINT_STE_INCLUDED == TRUE) + if (pstrEncParams->s16ChannelMode == SBC_JOINT_STEREO) { + /* Calculate sum and differance scale factors for making JS decision */ + ps16ScfL = pstrEncParams->as16ScaleFactor ; + /* calculate the scale factor of Joint stereo max sum and diff */ + for (s32Sb = 0; s32Sb < s32NumOfSubBands - 1; s32Sb++) { + SbBuffer = pstrEncParams->s32SbBuffer + s32Sb; + s32MaxValue2 = 0; + s32MaxValue = 0; + pSum = s32LRSum; + pDiff = s32LRDiff; + for (s32Blk = 0; s32Blk < s32NumOfBlocks; s32Blk++) { + *pSum = (*SbBuffer + * (SbBuffer + s32NumOfSubBands)) >> 1; + if (abs32(*pSum) > s32MaxValue) { + s32MaxValue = abs32(*pSum); + } + pSum++; + *pDiff = (*SbBuffer - * (SbBuffer + s32NumOfSubBands)) >> 1; + if (abs32(*pDiff) > s32MaxValue2) { + s32MaxValue2 = abs32(*pDiff); + } + pDiff++; + SbBuffer += s32Ch; + } + u32Count = (s32MaxValue > 0x800000) ? 9 : 0; + for ( ; u32Count < 15; u32Count++) { + if (s32MaxValue <= (SINT32)(0x8000 << u32Count)) { + break; + } + } + u32CountSum = u32Count; + u32Count = (s32MaxValue2 > 0x800000) ? 9 : 0; + for ( ; u32Count < 15; u32Count++) { + if (s32MaxValue2 <= (SINT32)(0x8000 << u32Count)) { + break; + } + } + u32CountDiff = u32Count; + if ( (*ps16ScfL + * (ps16ScfL + s32NumOfSubBands)) > (SINT16)(u32CountSum + u32CountDiff) ) { + + if (u32CountSum > maxBit) { + maxBit = u32CountSum; + } + + if (u32CountDiff > maxBit) { + maxBit = u32CountDiff; + } + + *ps16ScfL = (SINT16)u32CountSum; + *(ps16ScfL + s32NumOfSubBands) = (SINT16)u32CountDiff; + + SbBuffer = pstrEncParams->s32SbBuffer + s32Sb; + pSum = s32LRSum; + pDiff = s32LRDiff; + + for (s32Blk = 0; s32Blk < s32NumOfBlocks; s32Blk++) { + *SbBuffer = *pSum; + *(SbBuffer + s32NumOfSubBands) = *pDiff; + + SbBuffer += s32NumOfSubBands << 1; + pSum++; + pDiff++; + } + + pstrEncParams->as16Join[s32Sb] = 1; + } else { + pstrEncParams->as16Join[s32Sb] = 0; + } + ps16ScfL++; + } + pstrEncParams->as16Join[s32Sb] = 0; + } +#endif + + pstrEncParams->s16MaxBitNeed = (SINT16)maxBit; + + /* bit allocation */ + if ((pstrEncParams->s16ChannelMode == SBC_STEREO) || (pstrEncParams->s16ChannelMode == SBC_JOINT_STEREO)) { + sbc_enc_bit_alloc_ste(pstrEncParams); + } else { + sbc_enc_bit_alloc_mono(pstrEncParams); + } + + /* Quantize the encoded audio */ + EncPacking(pstrEncParams); + } while (--(pstrEncParams->u8NumPacketToEncode)); + + pstrEncParams->u8NumPacketToEncode = 1; /* default is one for retrocompatibility purpose */ + +} + +/**************************************************************************** +* InitSbcAnalysisFilt - Initalizes the input data to 0 +* +* RETURNS : N/A +*/ +void SBC_Encoder_Init(SBC_ENC_PARAMS *pstrEncParams) +{ + UINT16 s16SamplingFreq; /*temp variable to store smpling freq*/ + SINT16 s16Bitpool; /*to store bit pool value*/ + SINT16 s16BitRate; /*to store bitrate*/ + SINT16 s16FrameLen; /*to store frame length*/ + UINT16 HeaderParams; + + pstrEncParams->u8NumPacketToEncode = 1; /* default is one for retrocompatibility purpose */ + + if (pstrEncParams->sbc_mode != SBC_MODE_MSBC) { + /* Required number of channels */ + if (pstrEncParams->s16ChannelMode == SBC_MONO) { + pstrEncParams->s16NumOfChannels = 1; + } else { + pstrEncParams->s16NumOfChannels = 2; + } + + /* Bit pool calculation */ + if (pstrEncParams->s16SamplingFreq == SBC_sf16000) { + s16SamplingFreq = 16000; + } else if (pstrEncParams->s16SamplingFreq == SBC_sf32000) { + s16SamplingFreq = 32000; + } else if (pstrEncParams->s16SamplingFreq == SBC_sf44100) { + s16SamplingFreq = 44100; + } else { + s16SamplingFreq = 48000; + } + + if ( (pstrEncParams->s16ChannelMode == SBC_JOINT_STEREO) + || (pstrEncParams->s16ChannelMode == SBC_STEREO) ) { + s16Bitpool = (SINT16)( (pstrEncParams->u16BitRate * + pstrEncParams->s16NumOfSubBands * 1000 / s16SamplingFreq) + - ( (32 + (4 * pstrEncParams->s16NumOfSubBands * + pstrEncParams->s16NumOfChannels) + + ( (pstrEncParams->s16ChannelMode - 2) * + pstrEncParams->s16NumOfSubBands ) ) + / pstrEncParams->s16NumOfBlocks) ); + + s16FrameLen = 4 + (4 * pstrEncParams->s16NumOfSubBands * + pstrEncParams->s16NumOfChannels) / 8 + + ( ((pstrEncParams->s16ChannelMode - 2) * + pstrEncParams->s16NumOfSubBands) + + (pstrEncParams->s16NumOfBlocks * s16Bitpool) ) / 8; + + s16BitRate = (8 * s16FrameLen * s16SamplingFreq) + / (pstrEncParams->s16NumOfSubBands * + pstrEncParams->s16NumOfBlocks * 1000); + + if (s16BitRate > pstrEncParams->u16BitRate) { + s16Bitpool--; + } + + if (pstrEncParams->s16NumOfSubBands == 8) { + pstrEncParams->s16BitPool = (s16Bitpool > 255) ? 255 : s16Bitpool; + } else { + pstrEncParams->s16BitPool = (s16Bitpool > 128) ? 128 : s16Bitpool; + } + } else { + s16Bitpool = (SINT16)( ((pstrEncParams->s16NumOfSubBands * + pstrEncParams->u16BitRate * 1000) + / (s16SamplingFreq * pstrEncParams->s16NumOfChannels)) + - ( ( (32 / pstrEncParams->s16NumOfChannels) + + (4 * pstrEncParams->s16NumOfSubBands) ) + / pstrEncParams->s16NumOfBlocks ) ); + + pstrEncParams->s16BitPool = (s16Bitpool > + (16 * pstrEncParams->s16NumOfSubBands)) + ? (16 * pstrEncParams->s16NumOfSubBands) : s16Bitpool; + } + + if (pstrEncParams->s16BitPool < 0) { + pstrEncParams->s16BitPool = 0; + } + /* sampling freq */ + HeaderParams = ((pstrEncParams->s16SamplingFreq & 3) << 6); + + /* number of blocks*/ + HeaderParams |= (((pstrEncParams->s16NumOfBlocks - 4) & 12) << 2); + + /* channel mode: mono, dual...*/ + HeaderParams |= ((pstrEncParams->s16ChannelMode & 3) << 2); + + /* Loudness or SNR */ + HeaderParams |= ((pstrEncParams->s16AllocationMethod & 1) << 1); + HeaderParams |= ((pstrEncParams->s16NumOfSubBands >> 3) & 1); /*4 or 8*/ + + pstrEncParams->FrameHeader = HeaderParams; + } else { + // mSBC + + // Use mSBC encoding parameters to reset the control field + /* Required number of channels: 1 */ + pstrEncParams->s16ChannelMode = SBC_MONO; + pstrEncParams->s16NumOfChannels = 1; + + /* Required Sampling frequency : 16KHz */ + pstrEncParams->s16SamplingFreq = SBC_sf16000; + + /* Bit pool value: 26 */ + pstrEncParams->s16BitPool = 26; + + /* number of subbands: 8 */ + pstrEncParams->s16NumOfSubBands = 8; + + /* number of blocks: 15 */ + pstrEncParams->s16NumOfBlocks = 15; + + /* allocation method: loudness */ + pstrEncParams->s16AllocationMethod = SBC_LOUDNESS; + + /* set the header paramers, unused for mSBC */ + pstrEncParams->FrameHeader = 0; + } + + if (pstrEncParams->s16NumOfSubBands == 4) { + if (pstrEncParams->s16NumOfChannels == 1) { + EncMaxShiftCounter = ((ENC_VX_BUFFER_SIZE - 4 * 10) >> 2) << 2; + } else { + EncMaxShiftCounter = ((ENC_VX_BUFFER_SIZE - 4 * 10 * 2) >> 3) << 2; + } + } else { + if (pstrEncParams->s16NumOfChannels == 1) { + EncMaxShiftCounter = ((ENC_VX_BUFFER_SIZE - 8 * 10) >> 3) << 3; + } else { + EncMaxShiftCounter = ((ENC_VX_BUFFER_SIZE - 8 * 10 * 2) >> 4) << 3; + } + } + + APPL_TRACE_EVENT("SBC_Encoder_Init : bitrate %d, bitpool %d", + pstrEncParams->u16BitRate, pstrEncParams->s16BitPool); + + SbcAnalysisInit(); +} + +#endif /* #if (defined(SBC_ENC_INCLUDED) && SBC_ENC_INCLUDED == TRUE) */ diff --git a/lib/bt/host/bluedroid/external/sbc/encoder/srce/sbc_packing.c b/lib/bt/host/bluedroid/external/sbc/encoder/srce/sbc_packing.c new file mode 100644 index 00000000..4b1dc259 --- /dev/null +++ b/lib/bt/host/bluedroid/external/sbc/encoder/srce/sbc_packing.c @@ -0,0 +1,263 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains code for packing the Encoded data into bit streams. + * + ******************************************************************************/ +#include "common/bt_target.h" +#include "sbc_encoder.h" +#include "sbc_enc_func_declare.h" + +#if (defined(SBC_ENC_INCLUDED) && SBC_ENC_INCLUDED == TRUE) + +#if (SBC_ARM_ASM_OPT==TRUE) +#define Mult32(s32In1,s32In2,s32OutLow) \ +{ \ + __asm \ + { \ + MUL s32OutLow,s32In1,s32In2; \ + } \ +} +#define Mult64(s32In1, s32In2, s32OutLow, s32OutHi) \ +{ \ + __asm \ + { \ + SMULL s32OutLow,s32OutHi,s32In1,s32In2 \ + } \ +} +#else +#define Mult32(s32In1,s32In2,s32OutLow) s32OutLow=(SINT32)s32In1*(SINT32)s32In2; +#define Mult64(s32In1, s32In2, s32OutLow, s32OutHi) \ +{ \ + s32OutLow = ((SINT32)(UINT16)s32In1 * (UINT16)s32In2); \ + s32TempVal2 = (SINT32)((s32In1 >> 16) * (UINT16)s32In2); \ + s32Carry = ( (((UINT32)(s32OutLow)>>16)&0xFFFF) + \ + + (s32TempVal2 & 0xFFFF) ) >> 16; \ + s32OutLow += (s32TempVal2 << 16); \ + s32OutHi = (s32TempVal2 >> 16) + s32Carry; \ +} +#endif + +void EncPacking(SBC_ENC_PARAMS *pstrEncParams) +{ + UINT8 *pu8PacketPtr; /* packet ptr*/ + UINT8 Temp; + SINT32 s32Blk; /* counter for block*/ + SINT32 s32Ch; /* counter for channel*/ + SINT32 s32Sb; /* counter for sub-band*/ + SINT32 s32PresentBit; /* represents bit to be stored*/ + /*SINT32 s32LoopCountI; loop counter*/ + SINT32 s32LoopCountJ; /* loop counter*/ + UINT32 u32QuantizedSbValue, u32QuantizedSbValue0; /* temp variable to store quantized sb val*/ + SINT32 s32LoopCount; /* loop counter*/ + UINT8 u8XoredVal; /* to store XORed value in CRC calculation*/ + UINT8 u8CRC; /* to store CRC value*/ + SINT16 *ps16GenPtr; + SINT32 s32NumOfBlocks; + SINT32 s32NumOfSubBands = pstrEncParams->s16NumOfSubBands; + SINT32 s32NumOfChannels = pstrEncParams->s16NumOfChannels; + UINT32 u32SfRaisedToPow2; /*scale factor raised to power 2*/ + SINT16 *ps16ScfPtr; + SINT32 *ps32SbPtr; + UINT16 u16Levels; /*to store levels*/ + SINT32 s32Temp1; /*used in 64-bit multiplication*/ + SINT32 s32Low; /*used in 64-bit multiplication*/ +#if (SBC_IS_64_MULT_IN_QUANTIZER==TRUE) + SINT32 s32Hi1, s32Low1, s32Carry, s32TempVal2, s32Hi, s32Temp2; +#endif + + pu8PacketPtr = pstrEncParams->pu8NextPacket; /*Initialize the ptr*/ + if (pstrEncParams->sbc_mode != SBC_MODE_MSBC) { + *pu8PacketPtr++ = (UINT8)SBC_SYNC_WORD_STD; /*Sync word*/ + *pu8PacketPtr++ = (UINT8)(pstrEncParams->FrameHeader); + + *pu8PacketPtr = (UINT8)(pstrEncParams->s16BitPool & 0x00FF); + } else { + *pu8PacketPtr++ = (UINT8)SBC_SYNC_WORD_MSBC; /*Sync word*/ + // two reserved bytes + *pu8PacketPtr++ = 0; + *pu8PacketPtr = 0; + } + pu8PacketPtr += 2; /*skip for CRC*/ + + /*here it indicate if it is byte boundary or nibble boundary*/ + s32PresentBit = 8; + Temp = 0; +#if (SBC_JOINT_STE_INCLUDED == TRUE) + if (pstrEncParams->s16ChannelMode == SBC_JOINT_STEREO) { + /* pack join stero parameters */ + for (s32Sb = 0; s32Sb < s32NumOfSubBands; s32Sb++) { + Temp <<= 1; + Temp |= pstrEncParams->as16Join[s32Sb]; + } + + /* pack RFA */ + if (s32NumOfSubBands == SUB_BANDS_4) { + s32PresentBit = 4; + } else { + *(pu8PacketPtr++) = Temp; + Temp = 0; + } + } +#endif + + /* Pack Scale factor */ + ps16GenPtr = pstrEncParams->as16ScaleFactor; + s32Sb = s32NumOfChannels * s32NumOfSubBands; + /*Temp=*pu8PacketPtr;*/ + for (s32Ch = s32Sb; s32Ch > 0; s32Ch--) { + Temp <<= 4; + Temp |= *ps16GenPtr++; + + if (s32PresentBit == 4) { + s32PresentBit = 8; + *(pu8PacketPtr++) = Temp; + Temp = 0; + } else { + s32PresentBit = 4; + } + } + + /* Pack samples */ + ps32SbPtr = pstrEncParams->s32SbBuffer; + /*Temp=*pu8PacketPtr;*/ + s32NumOfBlocks = pstrEncParams->s16NumOfBlocks; + for (s32Blk = s32NumOfBlocks - 1; s32Blk >= 0; s32Blk--) { + ps16GenPtr = pstrEncParams->as16Bits; + ps16ScfPtr = pstrEncParams->as16ScaleFactor; + for (s32Ch = s32Sb - 1; s32Ch >= 0; s32Ch--) { + s32LoopCount = *ps16GenPtr++; + if (s32LoopCount != 0) { +#if (SBC_IS_64_MULT_IN_QUANTIZER==TRUE) + /* finding level from reconstruction part of decoder */ + u32SfRaisedToPow2 = ((UINT32)1 << ((*ps16ScfPtr) + 1)); + u16Levels = (UINT16)(((UINT32)1 << s32LoopCount) - 1); + + /* quantizer */ + s32Temp1 = (*ps32SbPtr >> 2) + (u32SfRaisedToPow2 << 12); + s32Temp2 = u16Levels; + + Mult64 (s32Temp1, s32Temp2, s32Low, s32Hi); + + s32Low1 = s32Low >> ((*ps16ScfPtr) + 2); + s32Low1 &= ((UINT32)1 << (32 - ((*ps16ScfPtr) + 2))) - 1; + s32Hi1 = s32Hi << (32 - ((*ps16ScfPtr) + 2)); + + u32QuantizedSbValue0 = (UINT16)((s32Low1 | s32Hi1) >> 12); +#else + /* finding level from reconstruction part of decoder */ + u32SfRaisedToPow2 = ((UINT32)1 << *ps16ScfPtr); + u16Levels = (UINT16)(((UINT32)1 << s32LoopCount) - 1); + + /* quantizer */ + s32Temp1 = (*ps32SbPtr >> 15) + u32SfRaisedToPow2; + Mult32(s32Temp1, u16Levels, s32Low); + s32Low >>= (*ps16ScfPtr + 1); + u32QuantizedSbValue0 = (UINT16)s32Low; +#endif + /*store the number of bits required and the quantized s32Sb + sample to ease the coding*/ + u32QuantizedSbValue = u32QuantizedSbValue0; + + if (s32PresentBit >= s32LoopCount) { + Temp <<= s32LoopCount; + Temp |= u32QuantizedSbValue; + s32PresentBit -= s32LoopCount; + } else { + while (s32PresentBit < s32LoopCount) { + s32LoopCount -= s32PresentBit; + u32QuantizedSbValue >>= s32LoopCount; + + /*remove the unwanted msbs*/ + /*u32QuantizedSbValue <<= 16 - s32PresentBit; + u32QuantizedSbValue >>= 16 - s32PresentBit;*/ + + Temp <<= s32PresentBit; + + Temp |= u32QuantizedSbValue ; + /*restore the original*/ + u32QuantizedSbValue = u32QuantizedSbValue0; + + *(pu8PacketPtr++) = Temp; + Temp = 0; + s32PresentBit = 8; + } + Temp <<= s32LoopCount; + + /* remove the unwanted msbs */ + /*u32QuantizedSbValue <<= 16 - s32LoopCount; + u32QuantizedSbValue >>= 16 - s32LoopCount;*/ + + Temp |= u32QuantizedSbValue; + + s32PresentBit -= s32LoopCount; + } + } + ps16ScfPtr++; + ps32SbPtr++; + } + } + + Temp <<= s32PresentBit; + *pu8PacketPtr = Temp; + pstrEncParams->u16PacketLength = pu8PacketPtr - pstrEncParams->pu8NextPacket + 1; + /*find CRC*/ + pu8PacketPtr = pstrEncParams->pu8NextPacket + 1; /*Initialize the ptr*/ + u8CRC = 0x0F; + s32LoopCount = s32Sb >> 1; + + /* + The loops is run from the start of the packet till the scale factor + parameters. In case of JS, 'join' parameter is included in the packet + so that many more bytes are included in CRC calculation. + */ + Temp = *pu8PacketPtr; + for (s32Ch = 1; s32Ch < (s32LoopCount + 4); s32Ch++) { + /* skip sync word and CRC bytes */ + if (s32Ch != 3) { + for (s32LoopCountJ = 7; s32LoopCountJ >= 0; s32LoopCountJ--) { + u8XoredVal = ((u8CRC >> 7) & 0x01) ^ ((Temp >> s32LoopCountJ) & 0x01); + u8CRC <<= 1; + u8CRC ^= (u8XoredVal * 0x1D); + u8CRC &= 0xFF; + } + } + Temp = *(++pu8PacketPtr); + } + + if (pstrEncParams->s16ChannelMode == SBC_JOINT_STEREO) { + for (s32LoopCountJ = 7; s32LoopCountJ >= (8 - s32NumOfSubBands); s32LoopCountJ--) { + u8XoredVal = ((u8CRC >> 7) & 0x01) ^ ((Temp >> s32LoopCountJ) & 0x01); + u8CRC <<= 1; + u8CRC ^= (u8XoredVal * 0x1D); + u8CRC &= 0xFF; + } + } + + /* CRC calculation ends here */ + + /* store CRC in packet */ + pu8PacketPtr = pstrEncParams->pu8NextPacket; /*Initialize the ptr*/ + pu8PacketPtr += 3; + *pu8PacketPtr = u8CRC; + pstrEncParams->pu8NextPacket += pstrEncParams->u16PacketLength; /* move the pointer to the end in case there is more than one frame to encode */ +} + +#endif /* #if (defined(SBC_ENC_INCLUDED) && SBC_ENC_INCLUDED == TRUE) */ diff --git a/lib/bt/host/bluedroid/external/sbc/plc/include/sbc_plc.h b/lib/bt/host/bluedroid/external/sbc/plc/include/sbc_plc.h new file mode 100644 index 00000000..0bd12442 --- /dev/null +++ b/lib/bt/host/bluedroid/external/sbc/plc/include/sbc_plc.h @@ -0,0 +1,83 @@ +/* + * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef _SBC_PLC_H +#define _SBC_PLC_H + +#include + +#if defined __cplusplus +extern "C" { +#endif + +/* Paramter for PLC (16 kHZ)*/ +#define SBC_FS 120 /* SBC Frame Size */ +#define SBC_N 256 /* 16ms - Window Length for pattern matching */ +#define SBC_M 64 /* 4ms - Template for matching */ +#define SBC_LHIST (SBC_N + SBC_FS - 1) /* Length of history buffer required */ +#define SBC_RT 36 /* SBC Reconvergence Time (samples) */ +#define SBC_OLAL 16 /* OverLap-Add Length (samples) */ + +/* PLC State Information */ +typedef struct sbc_plc_state { + int16_t hist[SBC_LHIST + SBC_FS + SBC_RT + SBC_OLAL]; + int16_t bestlag; + int nbf; +} sbc_plc_state_t; + +/* Prototypes */ +/** + * Perform PLC initialization of memory vectors. + * + * @param plc_state pointer to PLC state memory + */ +void sbc_plc_init(sbc_plc_state_t *plc_state); + +/** + * Perform PLC deinitialization of memory vectors. + * + * @param plc_state pointer to PLC state memory + */ +void sbc_plc_deinit(sbc_plc_state_t *plc_state); + +/** + * Perform bad frame processing. + * + * @param plc_state pointer to PLC state memory + * @param ZIRbuf pointer to the ZIR response of the SBC decoder + * @param out pointer to the output samples + */ +void sbc_plc_bad_frame(sbc_plc_state_t *plc_state, int16_t *ZIRbuf, int16_t *out); + +/** + * Perform good frame processing. Most of the time, this function + * just updates history buffers and passes the input to the output, + * but in the first good frame after frame loss, it must conceal the + * received signal as it reconverges with the true output. + * + * @param plc_state pointer to PLC state memory + * @param in pointer to the input vector + * @param out pointer to the output samples + */ +void sbc_plc_good_frame(sbc_plc_state_t *plc_state, int16_t *in, int16_t *out); + +/** + * Get a zero signal eSCO frame + * @return pointer to data buffer + */ +uint8_t * sbc_plc_zero_signal_frame(void); + +/** + * Get a zero signal eSCO pcm frame + * @return pointer to data buffer + */ +int16_t * sbc_plc_zero_signal_frame_pcm(void); + +#if defined __cplusplus +} +#endif + +#endif /// _SBC_PLC_H diff --git a/lib/bt/host/bluedroid/external/sbc/plc/sbc_plc.c b/lib/bt/host/bluedroid/external/sbc/plc/sbc_plc.c new file mode 100644 index 00000000..787a4fcb --- /dev/null +++ b/lib/bt/host/bluedroid/external/sbc/plc/sbc_plc.c @@ -0,0 +1,298 @@ +/* + * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include "common/bt_target.h" +#include "sbc_plc.h" + +#if (PLC_INCLUDED == TRUE) +/* msbc */ +static const uint8_t indices0[] = { 0xad, 0x00, 0x00, 0xc5, 0x00, 0x00, 0x00, 0x00, 0x77, 0x6d, +0xb6, 0xdd, 0xdb, 0x6d, 0xb7, 0x76, 0xdb, 0x6d, 0xdd, 0xb6, 0xdb, 0x77, 0x6d, +0xb6, 0xdd, 0xdb, 0x6d, 0xb7, 0x76, 0xdb, 0x6d, 0xdd, 0xb6, 0xdb, 0x77, 0x6d, +0xb6, 0xdd, 0xdb, 0x6d, 0xb7, 0x76, 0xdb, 0x6d, 0xdd, 0xb6, 0xdb, 0x77, 0x6d, +0xb6, 0xdd, 0xdb, 0x6d, 0xb7, 0x76, 0xdb, 0x6c}; + + +/* 8 kHZ */ +static const int16_t indices0_pcm[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + +/* Raised COSine table for OLA */ +/* 16 kHZ */ +static float rcos[SBC_OLAL] = { + 0.99148655f,0.96623611f,0.92510857f,0.86950446f, + 0.80131732f,0.72286918f,0.63683150f,0.54613418f, + 0.45386582f,0.36316850f,0.27713082f,0.19868268f, + 0.13049554f,0.07489143f,0.03376389f,0.00851345f}; + +// /* 8 kHZ */ +// static float rcos[SBC_OLAL] = { +// 0.96984631f,0.88302222f, 0.75f,0.58682409f, +// 0.41317591f, 0.25f,0.11697778f,0.09015369f}; + + +static float SqrtByCarmack(const float x){ + union { + int i; + float y; + } float_int; + + float x2; + const float threehalfs = 1.5f; + + x2 = x * 0.5f; + float_int.y = x; + + float_int.i = 0x5f375a86 - (float_int.i >> 1); + float_int.y = float_int.y * (threehalfs - (x2 * float_int.y * float_int.y)); + // float_int.y = float_int.y * (threehalfs - (x2 * float_int.y * float_int.y)); + // float_int.y = float_int.y * (threehalfs - (x2 * float_int.y * float_int.y)); + + return (x * float_int.y); +} + +static float absolute(float x){ + if (x < 0) { + x = -x; + } + + return x; +} + +/** + * Compute the cross correlation according to Eq. (4) of Goodman + * paper, except that the true correlation is used. His formula + * seems to be incorrect. + * + * @param x pointer to x input vector + * @param y pointer to y input vector + * + * @return value containing the cross-correlation of x and y + */ +static float CrossCorrelation(int16_t *x, int16_t *y){ + int m; + float num = 0; + float den = 0; + float x2 = 0; + float y2 = 0; + + for (m = 0; m < SBC_M; m++) { + num += ((float)x[m]) * y[m]; + x2 += ((float)x[m]) * x[m]; + y2 += ((float)y[m]) * y[m]; + } + den = (float)SqrtByCarmack(x2 * y2); + return num / den; +} + +/** + * Perform pattern matching to find the match of template with the + * history buffer according to Section B of Goodman paper. + * + * @param y pointer to history buffer + * + * @return the lag corresponding to the best match. The lag is + * with respect to the beginning of the history buffer. + * + */ +static int PatternMatch(int16_t *y){ + int n; + float maxCn = -999999.0; // large negative number + float Cn; + int bestmatch = 0; + + for (n = 0; n < SBC_N; n++){ + Cn = CrossCorrelation(&y[SBC_LHIST-SBC_M], &y[n]); + if (Cn > maxCn){ + bestmatch = n; + maxCn = Cn; + } + } + return bestmatch; +} + +/** + * Perform amplitude matching using mean-absolute-value according + * to Goodman paper. + * + * @param y pointer to history buffer + * @param bestmatch value of the lag to the best match + * + * @return scale factor + */ +static float AmplitudeMatch(int16_t *y, int16_t bestmatch) { + int i; + float sumx = 0; + float sumy = 0.000001f; + float sf; + + for (i = 0; i < SBC_FS; i++){ + sumx += absolute(y[SBC_LHIST - SBC_FS + i]); + sumy += absolute(y[bestmatch + i]); + } + sf = sumx / sumy; + // This is not in the paper, but limit the scaling factor to something reasonable to avoid creating artifacts + if (sf < 0.75f) { + sf = 0.75f; + } + if (sf > 1.2f) { + sf = 1.2f; + } + return sf; +} + +static int16_t crop_sample(float val){ + float croped_val = val; + if (croped_val > 32767.0) croped_val= 32767.0; + if (croped_val < -32768.0) croped_val=-32768.0; + return (int16_t) croped_val; +} + +/** + * Get a zero signal eSCO frame + * @return pointer to data buffer + */ +uint8_t * sbc_plc_zero_signal_frame(void){ + return (uint8_t *)&indices0; +} + +/** + * Get a zero signal eSCO pcm frame + * @return pointer to data buffer + */ +int16_t * sbc_plc_zero_signal_frame_pcm(void){ + return (int16_t *)&indices0_pcm; +} + +/** + * Perform PLC initialization of memory vectors. + * + * @param plc_state pointer to PLC state memory + */ +void sbc_plc_init(sbc_plc_state_t *plc_state){ + plc_state->nbf=0; + plc_state->bestlag=0; + memset(plc_state->hist, 0, sizeof(plc_state->hist)); +} + +/** + * Perform PLC deinitialization of memory vectors. + * + * @param plc_state pointer to PLC state memory + */ +void sbc_plc_deinit(sbc_plc_state_t *plc_state){ + plc_state->nbf=0; + plc_state->bestlag=0; + memset(plc_state->hist, 0, sizeof(plc_state->hist)); +} + +/** + * Perform bad frame processing. + * + * @param plc_state pointer to PLC state memory + * @param ZIRbuf pointer to the ZIR response of the SBC decoder + * @param out pointer to the output samples + */ +void sbc_plc_bad_frame(sbc_plc_state_t *plc_state, int16_t *ZIRbuf, int16_t *out){ + int i = 0; + float val; + float sf = 1; + + plc_state->nbf++; + + if (plc_state->nbf == 1){ + // Perform pattern matching to find where to replicate + plc_state->bestlag = PatternMatch(plc_state->hist); + // the replication begins after the template match + plc_state->bestlag += SBC_M; + + // Compute Scale Factor to Match Amplitude of Substitution Packet to that of Preceding Packet + sf = AmplitudeMatch(plc_state->hist, plc_state->bestlag); + + for (i = 0; i < SBC_OLAL; i++){ + val = ZIRbuf[i] * rcos[i] + + sf * plc_state->hist[plc_state->bestlag + i] * rcos[SBC_OLAL - i - 1]; + plc_state->hist[SBC_LHIST + i] = crop_sample(val); + } + + for (; i < SBC_FS; i++){ + val = sf*plc_state->hist[plc_state->bestlag + i]; + plc_state->hist[SBC_LHIST + i] = crop_sample(val); + } + + for (; i < SBC_FS + SBC_OLAL; i++){ + val = sf * plc_state->hist[plc_state->bestlag + i] * rcos[i-SBC_FS] + + plc_state->hist[plc_state->bestlag + i] * rcos[SBC_OLAL - 1 - i + SBC_FS]; + plc_state->hist[SBC_LHIST + i] = crop_sample(val); + } + + for (; i < SBC_FS + SBC_RT + SBC_OLAL; i++){ + plc_state->hist[SBC_LHIST + i] = plc_state->hist[plc_state->bestlag + i]; + } + } else { + for ( ;i < SBC_FS + SBC_RT + SBC_OLAL; i++){ + plc_state->hist[SBC_LHIST + i] = plc_state->hist[plc_state->bestlag + i]; + } + } + + for (i = 0; i < SBC_FS; i++){ + out[i] = plc_state->hist[SBC_LHIST + i]; + } + + // shift the history buffer + for (i = 0; i < SBC_LHIST + SBC_RT + SBC_OLAL; i++){ + plc_state->hist[i] = plc_state->hist[i + SBC_FS]; + } +} + +/** + * Perform good frame processing. Most of the time, this function + * just updates history buffers and passes the input to the output, + * but in the first good frame after frame loss, it must conceal the + * received signal as it reconverges with the true output. + * + * @param plc_state pointer to PLC state memory + * @param in pointer to the input vector + * @param out pointer to the output samples + */ +void sbc_plc_good_frame(sbc_plc_state_t *plc_state, int16_t *in, int16_t *out){ + int i = 0; + + if (plc_state->nbf > 0){ + for (i = 0; i < SBC_RT; i++){ + out[i] = plc_state->hist[SBC_LHIST + i]; + } + + for (i = SBC_RT; i < SBC_RT + SBC_OLAL; i++){ + out[i] = (int16_t)(plc_state->hist[SBC_LHIST + i] * rcos[i - SBC_RT] + in[i] * rcos[SBC_OLAL - 1 - i + SBC_RT]); + } + } + + for (; i < SBC_FS; i++){ + out[i] = in[i]; + } + // Copy the output to the history buffer + for (i = 0; i < SBC_FS; i++){ + plc_state->hist[SBC_LHIST + i] = out[i]; + } + // shift the history buffer + for (i = 0; i < SBC_LHIST; i++){ + plc_state->hist[i] = plc_state->hist[i + SBC_FS]; + } + + plc_state->nbf = 0; +} + +#endif ///(PLC_INCLUDED == TRUE) diff --git a/lib/bt/host/bluedroid/hci/hci_audio.c b/lib/bt/host/bluedroid/hci/hci_audio.c new file mode 100644 index 00000000..1983e7af --- /dev/null +++ b/lib/bt/host/bluedroid/hci/hci_audio.c @@ -0,0 +1,7 @@ +#include "hci/hci_audio.h" + +void set_audio_state(uint16_t handle, sco_codec_t codec, sco_state_t state) +{ + //todo + return; +} diff --git a/lib/bt/host/bluedroid/hci/hci_hal_h4.c b/lib/bt/host/bluedroid/hci/hci_hal_h4.c new file mode 100644 index 00000000..24047790 --- /dev/null +++ b/lib/bt/host/bluedroid/hci/hci_hal_h4.c @@ -0,0 +1,652 @@ +/****************************************************************************** + * + * Copyright (C) 2014 Google, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ +#include +#include "common/bt_defs.h" +#include "common/bt_trace.h" +#include "stack/bt_types.h" +#include "hci/hci_hal.h" +#include "hci/hci_internals.h" +#include "hci/hci_layer.h" +#include "hci/hci_trans_int.h" +#include "osi/thread.h" +#include "osi/pkt_queue.h" +#if (BLE_ADV_REPORT_FLOW_CONTROL == TRUE) +#include "osi/mutex.h" +#include "osi/alarm.h" +#endif +#if (BT_CONTROLLER_INCLUDED == TRUE) +#include "esp_bt.h" +#endif +#include "esp_bluedroid_hci.h" +#include "stack/hcimsgs.h" + +#if ((BT_CONTROLLER_INCLUDED == TRUE) && SOC_ESP_NIMBLE_CONTROLLER) +#include "nimble/ble_hci_trans.h" +#endif + +#if (C2H_FLOW_CONTROL_INCLUDED == TRUE) +#include "l2c_int.h" +#endif ///C2H_FLOW_CONTROL_INCLUDED == TRUE +#include "stack/hcimsgs.h" + +#define HCI_BLE_EVENT 0x3e +#define PACKET_TYPE_TO_INBOUND_INDEX(type) ((type) - 2) +#define PACKET_TYPE_TO_INDEX(type) ((type) - 1) +#define HCI_UPSTREAM_DATA_QUEUE_IDX (1) +#if (BLE_ADV_REPORT_FLOW_CONTROL == TRUE) +#define HCI_BLE_ADV_MIN_CREDITS_TO_RELEASE (10) +#define HCI_ADV_FLOW_MONITOR_PERIOD_MS (500) +#else +#define HCI_HAL_BLE_ADV_RPT_QUEUE_LEN_MAX (200) +#endif + +extern bool BTU_check_queue_is_congest(void); + + +static const uint8_t preamble_sizes[] = { + HCI_COMMAND_PREAMBLE_SIZE, + HCI_ACL_PREAMBLE_SIZE, + HCI_SCO_PREAMBLE_SIZE, + HCI_EVENT_PREAMBLE_SIZE +}; + +static const uint16_t outbound_event_types[] = { + MSG_HC_TO_STACK_HCI_ERR, + MSG_HC_TO_STACK_HCI_ACL, + MSG_HC_TO_STACK_HCI_SCO, + MSG_HC_TO_STACK_HCI_EVT +}; + +typedef struct { + fixed_queue_t *rx_q; + struct pkt_queue *adv_rpt_q; +#if (BLE_ADV_REPORT_FLOW_CONTROL == TRUE) + osi_mutex_t adv_flow_lock; + osi_alarm_t *adv_flow_monitor; + int adv_credits; + int adv_credits_to_release; + pkt_linked_item_t *adv_fc_cmd_buf; + bool cmd_buf_in_use; +#endif + hci_hal_callbacks_t *callbacks; + osi_thread_t *hci_h4_thread; + struct osi_event *upstream_data_ready; +} hci_hal_env_t; + + +static hci_hal_env_t hci_hal_env; +static const hci_hal_t interface; +static const esp_bluedroid_hci_driver_callbacks_t hci_host_cb; + +static void host_send_pkt_available_cb(void); +static int host_recv_pkt_cb(uint8_t *data, uint16_t len); +static void hci_hal_h4_hdl_rx_packet(BT_HDR *packet); +static void hci_hal_h4_hdl_rx_adv_rpt(pkt_linked_item_t *linked_pkt); +static void hci_upstream_data_handler(void *arg); +static bool hci_upstream_data_post(uint32_t timeout); + +#if (BLE_ADV_REPORT_FLOW_CONTROL == TRUE) +static void hci_adv_flow_monitor(void *context); +static void hci_adv_flow_cmd_free_cb(pkt_linked_item_t *linked_pkt); +#endif + +static bool hci_hal_env_init(const hci_hal_callbacks_t *upper_callbacks, osi_thread_t *task_thread) +{ + assert(upper_callbacks != NULL); + assert(task_thread != NULL); + + hci_hal_env.hci_h4_thread = task_thread; + hci_hal_env.callbacks = (hci_hal_callbacks_t *)upper_callbacks; + +#if (BLE_ADV_REPORT_FLOW_CONTROL == TRUE) + hci_hal_env.adv_fc_cmd_buf = osi_calloc(HCI_CMD_LINKED_BUF_SIZE(HCIC_PARAM_SIZE_BLE_UPDATE_ADV_FLOW_CONTROL)); + assert(hci_hal_env.adv_fc_cmd_buf != NULL); + osi_mutex_new(&hci_hal_env.adv_flow_lock); + osi_mutex_lock(&hci_hal_env.adv_flow_lock, OSI_MUTEX_MAX_TIMEOUT); + hci_hal_env.adv_credits = BLE_ADV_REPORT_FLOW_CONTROL_NUM; + hci_hal_env.adv_credits_to_release = 0; + hci_hal_env.cmd_buf_in_use = false; + osi_mutex_unlock(&hci_hal_env.adv_flow_lock); + hci_hal_env.adv_flow_monitor = osi_alarm_new("adv_fc_mon", hci_adv_flow_monitor, NULL, HCI_ADV_FLOW_MONITOR_PERIOD_MS); + assert (hci_hal_env.adv_flow_monitor != NULL); +#endif + + hci_hal_env.rx_q = fixed_queue_new(QUEUE_SIZE_MAX); + assert(hci_hal_env.rx_q != NULL); + + hci_hal_env.adv_rpt_q = pkt_queue_create(); + assert(hci_hal_env.adv_rpt_q != NULL); + + struct osi_event *event = osi_event_create(hci_upstream_data_handler, NULL); + assert(event != NULL); + hci_hal_env.upstream_data_ready = event; + osi_event_bind(hci_hal_env.upstream_data_ready, hci_hal_env.hci_h4_thread, HCI_UPSTREAM_DATA_QUEUE_IDX); + + return true; +} + +static void hci_hal_env_deinit(void) +{ + fixed_queue_t *rx_q = hci_hal_env.rx_q; + struct pkt_queue *adv_rpt_q = hci_hal_env.adv_rpt_q; + struct osi_event *upstream_data_ready = hci_hal_env.upstream_data_ready; + + hci_hal_env.rx_q = NULL; + hci_hal_env.adv_rpt_q = NULL; + hci_hal_env.upstream_data_ready = NULL; + + fixed_queue_free(rx_q, osi_free_func); + + pkt_queue_destroy(adv_rpt_q, NULL); + + osi_event_delete(upstream_data_ready); + +#if (BLE_ADV_REPORT_FLOW_CONTROL == TRUE) + hci_hal_env.cmd_buf_in_use = true; + osi_alarm_cancel(hci_hal_env.adv_flow_monitor); + osi_alarm_free(hci_hal_env.adv_flow_monitor); + hci_hal_env.adv_flow_monitor = NULL; + osi_mutex_free(&hci_hal_env.adv_flow_lock); + osi_free(hci_hal_env.adv_fc_cmd_buf); + hci_hal_env.adv_fc_cmd_buf = NULL; +#endif + + hci_hal_env.hci_h4_thread = NULL; + + memset(&hci_hal_env, 0, sizeof(hci_hal_env_t)); +} + +static bool hal_open(const hci_hal_callbacks_t *upper_callbacks, void *task_thread) +{ + hci_hal_env_init(upper_callbacks, (osi_thread_t *)task_thread); + + //register vhci host cb + if (hci_host_register_callback(&hci_host_cb) != ESP_OK) { + return false; + } + + return true; +} + +static void hal_close(void) +{ + hci_hal_env_deinit(); +} + +/** + * Function: transmit_data -TX data to low-layer + * It is ported from Bluedroid source code, so it is not + * needed to use write() to send data. + * TODO: Just use firmware API to send data. + */ +static uint16_t transmit_data(serial_data_type_t type, + uint8_t *data, uint16_t length) +{ + uint8_t previous_byte; + + assert(data != NULL); + assert(length > 0); + + if (type < DATA_TYPE_COMMAND || type > DATA_TYPE_SCO) { + HCI_TRACE_ERROR("%s invalid data type: %d", __func__, type); + return 0; + } + + // Write the signal byte right before the data + --data; + previous_byte = *data; + *(data) = type; + ++length; + + BTTRC_DUMP_BUFFER("Transmit Pkt", data, length); + + // TX Data to target + hci_host_send_packet(data, length); + + // Be nice and restore the old value of that byte + *(data) = previous_byte; + + return length - 1; +} + +// Internal functions +static void hci_upstream_data_handler(void *arg) +{ + fixed_queue_t *rx_q = hci_hal_env.rx_q; + struct pkt_queue *adv_rpt_q = hci_hal_env.adv_rpt_q; + size_t pkts_to_process; + + do { + pkts_to_process = fixed_queue_length(rx_q); + for (size_t i = 0; i < pkts_to_process; i++) { + BT_HDR *packet = fixed_queue_dequeue(rx_q, 0); + if (packet != NULL) { + hci_hal_h4_hdl_rx_packet(packet); + } + } + } while (0); + + do { + pkts_to_process = pkt_queue_length(adv_rpt_q); + for (size_t i = 0; i < pkts_to_process; i++) { + pkt_linked_item_t *linked_pkt = pkt_queue_dequeue(adv_rpt_q); + if (linked_pkt != NULL) { + hci_hal_h4_hdl_rx_adv_rpt(linked_pkt); + } + } + } while (0); + + if (!fixed_queue_is_empty(rx_q) || pkt_queue_length(adv_rpt_q) > 0) { + hci_upstream_data_post(OSI_THREAD_MAX_TIMEOUT); + } +} + +static bool hci_upstream_data_post(uint32_t timeout) +{ + return osi_thread_post_event(hci_hal_env.upstream_data_ready, timeout); +} + +#if (C2H_FLOW_CONTROL_INCLUDED == TRUE) +static void hci_packet_complete(BT_HDR *packet){ + uint8_t type; + uint16_t handle; + uint16_t num_packets = 1; + uint8_t *stream = packet->data + packet->offset; + + STREAM_TO_UINT8(type, stream); + if (type == DATA_TYPE_ACL/* || type == DATA_TYPE_SCO*/) { + STREAM_TO_UINT16(handle, stream); + handle = handle & HCI_DATA_HANDLE_MASK; + btsnd_hcic_host_num_xmitted_pkts(1, &handle, &num_packets); + } +} +#endif ///C2H_FLOW_CONTROL_INCLUDED == TRUE + +bool host_recv_adv_packet(uint8_t *packet) +{ + assert(packet); + if(packet[0] == DATA_TYPE_EVENT && packet[1] == HCI_BLE_EVENT) { + if(packet[3] == HCI_BLE_ADV_PKT_RPT_EVT || packet[3] == HCI_BLE_DIRECT_ADV_EVT +#if (BLE_ADV_REPORT_FLOW_CONTROL == TRUE) + || packet[3] == HCI_BLE_ADV_DISCARD_REPORT_EVT +#endif + ) { + return true; + } + } + return false; +} + +#if (BLE_ADV_REPORT_FLOW_CONTROL == TRUE) +static void hci_adv_flow_monitor(void *context) +{ + hci_adv_credits_force_release(0); +} + +static void hci_adv_credits_consumed(uint16_t num) +{ + osi_mutex_lock(&hci_hal_env.adv_flow_lock, OSI_MUTEX_MAX_TIMEOUT); + assert(hci_hal_env.adv_credits >= num); + hci_hal_env.adv_credits -= num; + osi_mutex_unlock(&hci_hal_env.adv_flow_lock); +} + +int hci_adv_credits_prep_to_release(uint16_t num) +{ + if (num == 0) { + return hci_hal_env.adv_credits_to_release; + } + + osi_mutex_lock(&hci_hal_env.adv_flow_lock, OSI_MUTEX_MAX_TIMEOUT); + int credits_to_release = hci_hal_env.adv_credits_to_release + num; + assert(hci_hal_env.adv_credits_to_release <= BLE_ADV_REPORT_FLOW_CONTROL_NUM); + hci_hal_env.adv_credits_to_release = credits_to_release; + osi_mutex_unlock(&hci_hal_env.adv_flow_lock); + + if (credits_to_release == num && num != 0) { + osi_alarm_cancel(hci_hal_env.adv_flow_monitor); + osi_alarm_set(hci_hal_env.adv_flow_monitor, HCI_ADV_FLOW_MONITOR_PERIOD_MS); + } + return credits_to_release; +} + +static int hci_adv_credits_release(void) +{ + osi_mutex_lock(&hci_hal_env.adv_flow_lock, OSI_MUTEX_MAX_TIMEOUT); + int credits_released = hci_hal_env.adv_credits_to_release; + hci_hal_env.adv_credits += credits_released; + hci_hal_env.adv_credits_to_release -= credits_released; + assert(hci_hal_env.adv_credits <= BLE_ADV_REPORT_FLOW_CONTROL_NUM); + assert(hci_hal_env.adv_credits_to_release >= 0); + osi_mutex_unlock(&hci_hal_env.adv_flow_lock); + + if (hci_hal_env.adv_credits_to_release == 0) { + osi_alarm_cancel(hci_hal_env.adv_flow_monitor); + } + return credits_released; +} + +static int hci_adv_credits_release_rollback(uint16_t num) +{ + osi_mutex_lock(&hci_hal_env.adv_flow_lock, OSI_MUTEX_MAX_TIMEOUT); + hci_hal_env.adv_credits -= num; + hci_hal_env.adv_credits_to_release += num; + assert(hci_hal_env.adv_credits >=0); + assert(hci_hal_env.adv_credits_to_release <= BLE_ADV_REPORT_FLOW_CONTROL_NUM); + osi_mutex_unlock(&hci_hal_env.adv_flow_lock); + + return num; +} + +static void hci_adv_flow_cmd_free_cb(pkt_linked_item_t *linked_pkt) +{ + osi_mutex_lock(&hci_hal_env.adv_flow_lock, OSI_MUTEX_MAX_TIMEOUT); + hci_hal_env.cmd_buf_in_use = false; + osi_mutex_unlock(&hci_hal_env.adv_flow_lock); + hci_adv_credits_try_release(0); +} + +bool hci_adv_flow_try_send_command(uint16_t credits_released) +{ + bool sent = false; + bool use_static_buffer = false; + + /* first try using static buffer, then dynamic buffer */ + if (!hci_hal_env.cmd_buf_in_use) { + osi_mutex_lock(&hci_hal_env.adv_flow_lock, OSI_MUTEX_MAX_TIMEOUT); + if (!hci_hal_env.cmd_buf_in_use) { + hci_hal_env.cmd_buf_in_use = true; + use_static_buffer = true; + } + osi_mutex_unlock(&hci_hal_env.adv_flow_lock); + } + + if (use_static_buffer) { + hci_cmd_metadata_t *metadata = (hci_cmd_metadata_t *)(hci_hal_env.adv_fc_cmd_buf->data); + BT_HDR *static_buffer = &metadata->command; + metadata->command_free_cb = hci_adv_flow_cmd_free_cb; + sent = btsnd_hcic_ble_update_adv_report_flow_control(credits_released, static_buffer); + } else { + sent = btsnd_hcic_ble_update_adv_report_flow_control(credits_released, NULL); + } + + return sent; +} + +int hci_adv_credits_try_release(uint16_t num) +{ + int credits_released = 0; + if (hci_adv_credits_prep_to_release(num) >= HCI_BLE_ADV_MIN_CREDITS_TO_RELEASE) { + credits_released = hci_adv_credits_release(); + if (credits_released > 0) { + if (!hci_adv_flow_try_send_command(credits_released)) { + hci_adv_credits_release_rollback(credits_released); + } + } else { + assert (credits_released == 0); + } + } + return credits_released; +} + +int hci_adv_credits_force_release(uint16_t num) +{ + hci_adv_credits_prep_to_release(num); + int credits_released = hci_adv_credits_release(); + if (credits_released > 0) { + if (!hci_adv_flow_try_send_command(credits_released)) { + hci_adv_credits_release_rollback(credits_released); + } + } + + return credits_released; +} +#endif + +static void hci_hal_h4_hdl_rx_packet(BT_HDR *packet) +{ + uint8_t type, hdr_size; + uint16_t length; + uint8_t *stream = NULL; + + if (!packet) { + return; + } + stream = packet->data + packet->offset; + +#if (C2H_FLOW_CONTROL_INCLUDED == TRUE) + hci_packet_complete(packet); +#endif ///C2H_FLOW_CONTROL_INCLUDED == TRUE + + STREAM_TO_UINT8(type, stream); + packet->offset++; + packet->len--; + if (type == HCI_BLE_EVENT) { +#if (!CONFIG_BT_STACK_NO_LOG) + uint8_t len = 0; + STREAM_TO_UINT8(len, stream); +#endif + HCI_TRACE_ERROR("Workround stream corrupted during LE SCAN: pkt_len=%d ble_event_len=%d\n", + packet->len, len); + osi_free(packet); + return; + } + if (type < DATA_TYPE_ACL || type > DATA_TYPE_EVENT) { + HCI_TRACE_ERROR("%s Unknown HCI message type. Dropping this byte 0x%x," + " min %x, max %x\n", __func__, type, + DATA_TYPE_ACL, DATA_TYPE_EVENT); + osi_free(packet); + return; + } + hdr_size = preamble_sizes[type - 1]; + if (packet->len < hdr_size) { + HCI_TRACE_ERROR("Wrong packet length type=%d pkt_len=%d hdr_len=%d", + type, packet->len, hdr_size); + osi_free(packet); + return; + } + if (type == DATA_TYPE_ACL) { + stream += hdr_size - 2; + STREAM_TO_UINT16(length, stream); + } else { + stream += hdr_size - 1; + STREAM_TO_UINT8(length, stream); + } + + if ((length + hdr_size) != packet->len) { + HCI_TRACE_ERROR("Wrong packet length type=%d hdr_len=%d pd_len=%d " + "pkt_len=%d", type, hdr_size, length, packet->len); + osi_free(packet); + return; + } + + packet->event = outbound_event_types[PACKET_TYPE_TO_INDEX(type)]; + hci_hal_env.callbacks->packet_ready(packet); +} + +static void hci_hal_h4_hdl_rx_adv_rpt(pkt_linked_item_t *linked_pkt) +{ + uint8_t type; + uint8_t hdr_size; + uint16_t length; + uint8_t *stream = NULL; + + if (!linked_pkt) { + return; + } + + BT_HDR* packet = (BT_HDR *)linked_pkt->data; + stream = packet->data + packet->offset; + + assert(host_recv_adv_packet(stream) == true); + + STREAM_TO_UINT8(type, stream); + packet->offset++; + packet->len--; + hdr_size = preamble_sizes[type - 1]; + + if (packet->len < hdr_size) { + HCI_TRACE_ERROR("Wrong packet length type=%d pkt_len=%d hdr_len=%d", + type, packet->len, hdr_size); + goto _discard_packet; + } + + stream += hdr_size - 1; + STREAM_TO_UINT8(length, stream); + if ((length + hdr_size) != packet->len) { + HCI_TRACE_ERROR("Wrong packet length type=%d hdr_len=%d pd_len=%d " + "pkt_len=%d", type, hdr_size, length, packet->len); + goto _discard_packet; + } + +#if SCAN_QUEUE_CONGEST_CHECK + if(BTU_check_queue_is_congest()) { + HCI_TRACE_DEBUG("BtuQueue is congested"); + goto _discard_packet; + } +#endif + + packet->event = outbound_event_types[PACKET_TYPE_TO_INDEX(type)]; + hci_hal_env.callbacks->adv_rpt_ready(linked_pkt); + + return; + +_discard_packet: + osi_free(linked_pkt); +#if (BLE_ADV_REPORT_FLOW_CONTROL == TRUE) + hci_adv_credits_prep_to_release(1); +#endif +} + +static void host_send_pkt_available_cb(void) +{ + //Controller rx cache buffer is ready for receiving new host packet + //Just Call Host main thread task to process pending packets. + hci_downstream_data_post(OSI_THREAD_MAX_TIMEOUT); +} + +static int host_recv_pkt_cb(uint8_t *data, uint16_t len) +{ + //Target has packet to host, malloc new buffer for packet + BT_HDR *pkt = NULL; + pkt_linked_item_t *linked_pkt = NULL; + size_t pkt_size; + + if (hci_hal_env.rx_q == NULL) { + return 0; + } + + bool is_adv_rpt = host_recv_adv_packet(data); + + if (!is_adv_rpt) { + pkt_size = BT_HDR_SIZE + len; + pkt = (BT_HDR *) osi_calloc(pkt_size); + if (!pkt) { + HCI_TRACE_ERROR("%s couldn't aquire memory for inbound data buffer.\n", __func__); + assert(0); + } + + pkt->offset = 0; + pkt->len = len; + pkt->layer_specific = 0; + memcpy(pkt->data, data, len); + fixed_queue_enqueue(hci_hal_env.rx_q, pkt, FIXED_QUEUE_MAX_TIMEOUT); + } else { +#if !BLE_ADV_REPORT_FLOW_CONTROL + // drop the packets if pkt_queue length goes beyond upper limit + if (pkt_queue_length(hci_hal_env.adv_rpt_q) > HCI_HAL_BLE_ADV_RPT_QUEUE_LEN_MAX) { + return 0; + } +#endif + pkt_size = BT_PKT_LINKED_HDR_SIZE + BT_HDR_SIZE + len; + linked_pkt = (pkt_linked_item_t *) osi_calloc(pkt_size); + if (!linked_pkt) { +#if (BLE_ADV_REPORT_FLOW_CONTROL == TRUE) + hci_adv_credits_consumed(1); + hci_adv_credits_prep_to_release(1); +#endif + return 0; + } + pkt = (BT_HDR *)linked_pkt->data; + pkt->offset = 0; + pkt->len = len; + pkt->layer_specific = 0; + memcpy(pkt->data, data, len); + pkt_queue_enqueue(hci_hal_env.adv_rpt_q, linked_pkt); +#if (BLE_ADV_REPORT_FLOW_CONTROL == TRUE) + hci_adv_credits_consumed(1); +#endif + } + + hci_upstream_data_post(OSI_THREAD_MAX_TIMEOUT); + + BTTRC_DUMP_BUFFER("Recv Pkt", pkt->data, len); + + return 0; +} +#if ((BT_CONTROLLER_INCLUDED == TRUE) && SOC_ESP_NIMBLE_CONTROLLER) + +int +ble_hs_hci_rx_evt(uint8_t *hci_ev, void *arg) +{ + if(esp_bluedroid_get_status() == ESP_BLUEDROID_STATUS_UNINITIALIZED) { + ble_hci_trans_buf_free(hci_ev); + return 0; + } + uint16_t len = hci_ev[1] + 3; + uint8_t *data = (uint8_t *)malloc(len); + assert(data != NULL); + data[0] = 0x04; + memcpy(&data[1], hci_ev, len - 1); + ble_hci_trans_buf_free(hci_ev); + host_recv_pkt_cb(data, len); + free(data); + return 0; +} + + +int +ble_hs_rx_data(struct os_mbuf *om, void *arg) +{ + uint16_t len = OS_MBUF_PKTHDR(om)->omp_len + 1; + uint8_t *data = (uint8_t *)malloc(len); + assert(data != NULL); + data[0] = 0x02; + os_mbuf_copydata(om, 0, len - 1, &data[1]); + host_recv_pkt_cb(data, len); + free(data); + os_mbuf_free_chain(om); + return 0; +} + +#endif +static const esp_bluedroid_hci_driver_callbacks_t hci_host_cb = { + .notify_host_send_available = host_send_pkt_available_cb, + .notify_host_recv = host_recv_pkt_cb, +}; + +static const hci_hal_t interface = { + hal_open, + hal_close, + transmit_data, +}; + +const hci_hal_t *hci_hal_h4_get_interface(void) +{ + return &interface; +} diff --git a/lib/bt/host/bluedroid/hci/hci_layer.c b/lib/bt/host/bluedroid/hci/hci_layer.c new file mode 100644 index 00000000..56ab8877 --- /dev/null +++ b/lib/bt/host/bluedroid/hci/hci_layer.c @@ -0,0 +1,610 @@ +/****************************************************************************** + * + * Copyright (C) 2014 Google, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ +#include +#include "sdkconfig.h" +#include "common/bt_target.h" +#if (BT_CONTROLLER_INCLUDED == TRUE) +#include "esp_bt.h" +#endif + +#include "common/bt_defs.h" +#include "common/bt_trace.h" +#include "stack/hcidefs.h" +#include "stack/hcimsgs.h" +#include "stack/btu.h" +#include "common/bt_vendor_lib.h" +#include "hci/hci_internals.h" +#include "hci/hci_hal.h" +#include "hci/hci_layer.h" +#include "hci/hci_trans_int.h" +#include "osi/allocator.h" +#include "hci/packet_fragmenter.h" +#include "osi/list.h" +#include "osi/alarm.h" +#include "osi/thread.h" +#include "osi/mutex.h" +#include "osi/fixed_queue.h" +#include "osi/fixed_pkt_queue.h" + +#define HCI_HOST_TASK_PINNED_TO_CORE (TASK_PINNED_TO_CORE) +#define HCI_HOST_TASK_STACK_SIZE (2048 + BT_TASK_EXTRA_STACK_SIZE) +#define HCI_HOST_TASK_PRIO (BT_TASK_MAX_PRIORITIES - 3) +#define HCI_HOST_TASK_NAME "hciT" +#define HCI_HOST_TASK_WORKQUEUE_NUM (2) +#define HCI_HOST_TASK_WORKQUEUE0_LEN (1) // for downstream datapath +#define HCI_HOST_TASK_WORKQUEUE1_LEN (1) // for upstream datapath + +#define HCI_DOWNSTREAM_DATA_QUEUE_IDX (0) + +typedef struct { + bool timer_is_set; + osi_alarm_t *command_response_timer; + list_t *commands_pending_response; + osi_mutex_t commands_pending_response_lock; +} command_waiting_response_t; + +typedef struct { + int command_credits; + fixed_pkt_queue_t *command_queue; + fixed_queue_t *packet_queue; + struct osi_event *downstream_data_ready; + command_waiting_response_t cmd_waiting_q; + + /* + non_repeating_timer_t *command_response_timer; + list_t *commands_pending_response; + osi_mutex_t commands_pending_response_lock; + */ +} hci_host_env_t; + +// Using a define here, because it can be stringified for the property lookup +static const uint32_t COMMAND_PENDING_TIMEOUT = 8000; + +// Our interface +static bool interface_created; +static hci_t interface; +static hci_host_env_t hci_host_env; +static osi_thread_t *hci_host_thread; +static bool hci_host_startup_flag; + +// Modules we import and callbacks we export +static const hci_hal_t *hal; +static const hci_hal_callbacks_t hal_callbacks; +static const packet_fragmenter_t *packet_fragmenter; +static const packet_fragmenter_callbacks_t packet_fragmenter_callbacks; + +static int hci_layer_init_env(void); +static void hci_layer_deinit_env(void); +static void hci_downstream_data_handler(void *arg); +static void event_command_ready(fixed_pkt_queue_t *queue); +static void event_packet_ready(fixed_queue_t *queue); +static void restart_command_waiting_response_timer(command_waiting_response_t *cmd_wait_q); +static void command_timed_out(void *context); +static void hal_says_packet_ready(BT_HDR *packet); +static bool filter_incoming_event(BT_HDR *packet); +static serial_data_type_t event_to_data_type(uint16_t event); +static pkt_linked_item_t *get_waiting_command(command_opcode_t opcode); +static void dispatch_reassembled(BT_HDR *packet); +static void dispatch_adv_report(pkt_linked_item_t *linked_pkt); + +// Module lifecycle functions +int hci_start_up(void) +{ + if (hci_layer_init_env()) { + goto error; + } + + const size_t workqueue_len[] = {HCI_HOST_TASK_WORKQUEUE0_LEN, HCI_HOST_TASK_WORKQUEUE1_LEN}; + hci_host_thread = osi_thread_create(HCI_HOST_TASK_NAME, HCI_HOST_TASK_STACK_SIZE, HCI_HOST_TASK_PRIO, HCI_HOST_TASK_PINNED_TO_CORE, + HCI_HOST_TASK_WORKQUEUE_NUM, workqueue_len); + if (hci_host_thread == NULL) { + return -2; + } + + osi_event_bind(hci_host_env.downstream_data_ready, hci_host_thread, HCI_DOWNSTREAM_DATA_QUEUE_IDX); + + packet_fragmenter->init(&packet_fragmenter_callbacks); + hal->open(&hal_callbacks, hci_host_thread); + + hci_host_startup_flag = true; + return 0; +error: + hci_shut_down(); + return -1; +} + +void hci_shut_down(void) +{ + hci_host_startup_flag = false; + hci_layer_deinit_env(); + + packet_fragmenter->cleanup(); + + //low_power_manager->cleanup(); + hal->close(); + + osi_thread_free(hci_host_thread); + hci_host_thread = NULL; +} + +bool hci_downstream_data_post(uint32_t timeout) +{ + return osi_thread_post_event(hci_host_env.downstream_data_ready, timeout); +} + +static int hci_layer_init_env(void) +{ + command_waiting_response_t *cmd_wait_q; + + // The host is only allowed to send at most one command initially, + // as per the Bluetooth spec, Volume 2, Part E, 4.4 (Command Flow Control) + // This value can change when you get a command complete or command status event. + hci_host_env.command_credits = 1; + hci_host_env.command_queue = fixed_pkt_queue_new(QUEUE_SIZE_MAX); + if (hci_host_env.command_queue) { + fixed_pkt_queue_register_dequeue(hci_host_env.command_queue, event_command_ready); + } else { + HCI_TRACE_ERROR("%s unable to create pending command queue.", __func__); + return -1; + } + + struct osi_event *event = osi_event_create(hci_downstream_data_handler, NULL); + assert(event != NULL); + hci_host_env.downstream_data_ready = event; + + hci_host_env.packet_queue = fixed_queue_new(QUEUE_SIZE_MAX); + if (hci_host_env.packet_queue) { + fixed_queue_register_dequeue(hci_host_env.packet_queue, event_packet_ready); + } else { + HCI_TRACE_ERROR("%s unable to create pending packet queue.", __func__); + return -1; + } + + // Init Commands waiting response list and timer + cmd_wait_q = &hci_host_env.cmd_waiting_q; + cmd_wait_q->timer_is_set = false; + cmd_wait_q->commands_pending_response = list_new(NULL); + if (!cmd_wait_q->commands_pending_response) { + HCI_TRACE_ERROR("%s unable to create list for commands pending response.", __func__); + return -1; + } + osi_mutex_new(&cmd_wait_q->commands_pending_response_lock); + cmd_wait_q->command_response_timer = osi_alarm_new("cmd_rsp_to", command_timed_out, cmd_wait_q, COMMAND_PENDING_TIMEOUT); + if (!cmd_wait_q->command_response_timer) { + HCI_TRACE_ERROR("%s unable to create command response timer.", __func__); + return -1; + } +#if (BLE_50_FEATURE_SUPPORT == TRUE) + btsnd_hcic_ble_sync_sem_init(); +#endif // #if (BLE_50_FEATURE_SUPPORT == TRUE) + + return 0; +} + +static void hci_layer_deinit_env(void) +{ + command_waiting_response_t *cmd_wait_q; + + osi_event_delete(hci_host_env.downstream_data_ready); + hci_host_env.downstream_data_ready = NULL; + + if (hci_host_env.command_queue) { + fixed_pkt_queue_free(hci_host_env.command_queue, (fixed_pkt_queue_free_cb)osi_free_func); + } + if (hci_host_env.packet_queue) { + fixed_queue_free(hci_host_env.packet_queue, osi_free_func); + } + + cmd_wait_q = &hci_host_env.cmd_waiting_q; + list_free(cmd_wait_q->commands_pending_response); + osi_mutex_free(&cmd_wait_q->commands_pending_response_lock); + osi_alarm_free(cmd_wait_q->command_response_timer); + cmd_wait_q->command_response_timer = NULL; +#if (BLE_50_FEATURE_SUPPORT == TRUE) + btsnd_hcic_ble_sync_sem_deinit(); +#endif // #if (BLE_50_FEATURE_SUPPORT == TRUE) +} + +static void hci_downstream_data_handler(void *arg) +{ + /* + * Previous task handles RX queue and two TX Queues, Since there is + * a RX Thread Task in H4 layer which receives packet from driver layer. + * Now HCI Host Task has been optimized to only process TX Queue + * including command and data queue. And command queue has high priority, + * All packets will be directly copied to single queue in driver layer with + * H4 type header added (1 byte). + */ + while (hci_host_check_send_available()) { + /*Now Target only allowed one packet per TX*/ + BT_HDR *pkt = packet_fragmenter->fragment_current_packet(); + if (pkt != NULL) { + packet_fragmenter->fragment_and_dispatch(pkt); + } else if (!fixed_pkt_queue_is_empty(hci_host_env.command_queue) && + hci_host_env.command_credits > 0) { + fixed_pkt_queue_process(hci_host_env.command_queue); + } else if (!fixed_queue_is_empty(hci_host_env.packet_queue)) { + fixed_queue_process(hci_host_env.packet_queue); + } else { + // No downstream packet to send, stop processing + break; + } + } +} + +static void transmit_command( + BT_HDR *command, + command_complete_cb complete_callback, + command_status_cb status_callback, + void *context) +{ + hci_cmd_metadata_t *metadata = HCI_GET_CMD_METAMSG(command); + pkt_linked_item_t *linked_pkt = HCI_GET_CMD_LINKED_STRUCT(metadata); + + assert(command->layer_specific == HCI_CMD_BUF_TYPE_METADATA); + metadata->flags_vnd |= HCI_CMD_MSG_F_VND_QUEUED; + + // Store the command message type in the event field + // in case the upper layer didn't already + command->event = MSG_STACK_TO_HC_HCI_CMD; + + HCI_TRACE_DEBUG("HCI Enqueue Comamnd opcode=0x%x\n", metadata->opcode); + BTTRC_DUMP_BUFFER(NULL, command->data + command->offset, command->len); + + fixed_pkt_queue_enqueue(hci_host_env.command_queue, linked_pkt, FIXED_PKT_QUEUE_MAX_TIMEOUT); + hci_downstream_data_post(OSI_THREAD_MAX_TIMEOUT); + +} + +static future_t *transmit_command_futured(BT_HDR *command) +{ + hci_cmd_metadata_t *metadata = HCI_GET_CMD_METAMSG(command); + pkt_linked_item_t *linked_pkt = HCI_GET_CMD_LINKED_STRUCT(metadata); + + assert(command->layer_specific == HCI_CMD_BUF_TYPE_METADATA); + metadata->flags_vnd |= (HCI_CMD_MSG_F_VND_QUEUED | HCI_CMD_MSG_F_VND_FUTURE); + + future_t *future = future_new(); + + metadata->complete_future = future; + + // Store the command message type in the event field + // in case the upper layer didn't already + command->event = MSG_STACK_TO_HC_HCI_CMD; + + fixed_pkt_queue_enqueue(hci_host_env.command_queue, linked_pkt, FIXED_PKT_QUEUE_MAX_TIMEOUT); + hci_downstream_data_post(OSI_THREAD_MAX_TIMEOUT); + return future; +} + +static void transmit_downward(uint16_t type, void *data) +{ + if (type == MSG_STACK_TO_HC_HCI_CMD) { + HCI_TRACE_ERROR("%s legacy transmit of command. Use transmit_command instead.\n", __func__); + assert(0); + } else { + fixed_queue_enqueue(hci_host_env.packet_queue, data, FIXED_QUEUE_MAX_TIMEOUT); + } + + hci_downstream_data_post(OSI_THREAD_MAX_TIMEOUT); +} + + +// Command/packet transmitting functions +static void event_command_ready(fixed_pkt_queue_t *queue) +{ + pkt_linked_item_t *wait_entry = NULL; + command_waiting_response_t *cmd_wait_q = &hci_host_env.cmd_waiting_q; + + wait_entry = fixed_pkt_queue_dequeue(queue, FIXED_QUEUE_MAX_TIMEOUT); + hci_cmd_metadata_t *metadata = (hci_cmd_metadata_t *)(wait_entry->data); + metadata->flags_vnd |= HCI_CMD_MSG_F_VND_SENT; + metadata->flags_vnd &= ~HCI_CMD_MSG_F_VND_QUEUED; + + if (metadata->flags_src & HCI_CMD_MSG_F_SRC_NOACK) { + packet_fragmenter->fragment_and_dispatch(&metadata->command); + hci_cmd_free_cb free_func = metadata->command_free_cb ? metadata->command_free_cb : (hci_cmd_free_cb) osi_free_func; + free_func(wait_entry); + return; + } + hci_host_env.command_credits--; + // Move it to the list of commands awaiting response + osi_mutex_lock(&cmd_wait_q->commands_pending_response_lock, OSI_MUTEX_MAX_TIMEOUT); + list_append(cmd_wait_q->commands_pending_response, wait_entry); + osi_mutex_unlock(&cmd_wait_q->commands_pending_response_lock); + + // Send it off + packet_fragmenter->fragment_and_dispatch(&metadata->command); + + restart_command_waiting_response_timer(cmd_wait_q); +} + +static void event_packet_ready(fixed_queue_t *queue) +{ + BT_HDR *packet = (BT_HDR *)fixed_queue_dequeue(queue, FIXED_QUEUE_MAX_TIMEOUT); + // The queue may be the command queue or the packet queue, we don't care + + packet_fragmenter->fragment_and_dispatch(packet); +} + +// Callback for the fragmenter to send a fragment +static void transmit_fragment(BT_HDR *packet, bool send_transmit_finished) +{ + uint16_t event = packet->event & MSG_EVT_MASK; + serial_data_type_t type = event_to_data_type(event); + + hal->transmit_data(type, packet->data + packet->offset, packet->len); + + if (event != MSG_STACK_TO_HC_HCI_CMD && send_transmit_finished) { + osi_free(packet); + } +} + +static void fragmenter_transmit_finished(BT_HDR *packet, bool all_fragments_sent) +{ + if (all_fragments_sent) { + osi_free(packet); + } else { + // This is kind of a weird case, since we're dispatching a partially sent packet + // up to a higher layer. + // TODO(zachoverflow): rework upper layer so this isn't necessary. + //osi_free(packet); + + /* dispatch_reassembled(packet) will send the packet back to the higher layer + when controller buffer is not enough. hci will send the remain packet back + to the l2cap layer and saved in the Link Queue (p_lcb->link_xmit_data_q). + The l2cap layer will resend the packet to lower layer when controller buffer + can be used. + */ + + dispatch_reassembled(packet); + //data_dispatcher_dispatch(interface.event_dispatcher, packet->event & MSG_EVT_MASK, packet); + } +} + +static void restart_command_waiting_response_timer(command_waiting_response_t *cmd_wait_q) +{ + osi_mutex_lock(&cmd_wait_q->commands_pending_response_lock, OSI_MUTEX_MAX_TIMEOUT); + if (cmd_wait_q->timer_is_set) { + osi_alarm_cancel(cmd_wait_q->command_response_timer); + cmd_wait_q->timer_is_set = false; + } + if (!list_is_empty(cmd_wait_q->commands_pending_response)) { + osi_alarm_set(cmd_wait_q->command_response_timer, COMMAND_PENDING_TIMEOUT); + cmd_wait_q->timer_is_set = true; + } + osi_mutex_unlock(&cmd_wait_q->commands_pending_response_lock); +} + +static void command_timed_out(void *context) +{ + command_waiting_response_t *cmd_wait_q = (command_waiting_response_t *)context; + pkt_linked_item_t *wait_entry; + + osi_mutex_lock(&cmd_wait_q->commands_pending_response_lock, OSI_MUTEX_MAX_TIMEOUT); + wait_entry = (list_is_empty(cmd_wait_q->commands_pending_response) ? + NULL : list_front(cmd_wait_q->commands_pending_response)); + osi_mutex_unlock(&cmd_wait_q->commands_pending_response_lock); + + if (wait_entry == NULL) { + HCI_TRACE_ERROR("%s with no commands pending response", __func__); + } else + // We shouldn't try to recover the stack from this command timeout. + // If it's caused by a software bug, fix it. If it's a hardware bug, fix it. + { + hci_cmd_metadata_t *metadata = (hci_cmd_metadata_t *)(wait_entry->data); + HCI_TRACE_ERROR("%s hci layer timeout waiting for response to a command. opcode: 0x%x", __func__, metadata->opcode); + UNUSED(metadata); + } +} + +// Event/packet receiving functions +static void hal_says_packet_ready(BT_HDR *packet) +{ + if (packet->event != MSG_HC_TO_STACK_HCI_EVT) { + packet_fragmenter->reassemble_and_dispatch(packet); + } else if (!filter_incoming_event(packet)) { + dispatch_reassembled(packet); + } +} + +static void hal_says_adv_rpt_ready(pkt_linked_item_t *linked_pkt) +{ + dispatch_adv_report(linked_pkt); +} + +// Returns true if the event was intercepted and should not proceed to +// higher layers. Also inspects an incoming event for interesting +// information, like how many commands are now able to be sent. +static bool filter_incoming_event(BT_HDR *packet) +{ + pkt_linked_item_t *wait_entry = NULL; + hci_cmd_metadata_t *metadata = NULL; + uint8_t *stream = packet->data + packet->offset; + uint8_t event_code; + command_opcode_t opcode; + + STREAM_TO_UINT8(event_code, stream); + STREAM_SKIP_UINT8(stream); // Skip the parameter total length field + + HCI_TRACE_DEBUG("Receive packet event_code=0x%x\n", event_code); + + if (event_code == HCI_COMMAND_COMPLETE_EVT) { + STREAM_TO_UINT8(hci_host_env.command_credits, stream); + STREAM_TO_UINT16(opcode, stream); + wait_entry = get_waiting_command(opcode); + metadata = (hci_cmd_metadata_t *)(wait_entry->data); + if (!wait_entry) { + HCI_TRACE_WARNING("%s command complete event with no matching command. opcode: 0x%x.", __func__, opcode); + } else if (metadata->command_complete_cb) { + metadata->command_complete_cb(packet, metadata->context); +#if (BLE_50_FEATURE_SUPPORT == TRUE) + BlE_SYNC *sync_info = btsnd_hcic_ble_get_sync_info(); + if(!sync_info) { + HCI_TRACE_WARNING("%s sync_info is NULL. opcode = 0x%x", __func__, opcode); + } else { + if (sync_info->sync_sem && sync_info->opcode == opcode) { + osi_sem_give(&sync_info->sync_sem); + sync_info->opcode = 0; + } + } +#endif // #if (BLE_50_FEATURE_SUPPORT == TRUE) + } else if (metadata->flags_vnd & HCI_CMD_MSG_F_VND_FUTURE) { + future_ready((future_t *)(metadata->complete_future), packet); + } + + goto intercepted; + } else if (event_code == HCI_COMMAND_STATUS_EVT) { + uint8_t status; + STREAM_TO_UINT8(status, stream); + STREAM_TO_UINT8(hci_host_env.command_credits, stream); + STREAM_TO_UINT16(opcode, stream); + + // If a command generates a command status event, it won't be getting a command complete event + + wait_entry = get_waiting_command(opcode); + metadata = (hci_cmd_metadata_t *)(wait_entry->data); + if (!wait_entry) { + HCI_TRACE_WARNING("%s command status event with no matching command. opcode: 0x%x", __func__, opcode); + } else if (metadata->command_status_cb) { + metadata->command_status_cb(status, &metadata->command, metadata->context); + } + + goto intercepted; + } + + return false; +intercepted: + restart_command_waiting_response_timer(&hci_host_env.cmd_waiting_q); + + /*Tell HCI Host Task to continue TX Pending commands*/ + if (hci_host_env.command_credits && + !fixed_pkt_queue_is_empty(hci_host_env.command_queue)) { + hci_downstream_data_post(OSI_THREAD_MAX_TIMEOUT); + } + + if (wait_entry) { + // If it has a callback, it's responsible for freeing the packet + if (event_code == HCI_COMMAND_STATUS_EVT || + (!metadata->command_complete_cb && !metadata->complete_future)) { + osi_free(packet); + } + + // If it has a callback, it's responsible for freeing the command + if (event_code == HCI_COMMAND_COMPLETE_EVT || !metadata->command_status_cb) { + hci_cmd_free_cb free_func = metadata->command_free_cb ? metadata->command_free_cb : (hci_cmd_free_cb) osi_free_func; + free_func(wait_entry); + } + } else { + osi_free(packet); + } + + return true; +} + +// Callback for the fragmenter to dispatch up a completely reassembled packet +static void dispatch_reassembled(BT_HDR *packet) +{ + // Events should already have been dispatched before this point + //Tell Up-layer received packet. + if (btu_task_post(SIG_BTU_HCI_MSG, packet, OSI_THREAD_MAX_TIMEOUT) == false) { + osi_free(packet); + } +} + +static void dispatch_adv_report(pkt_linked_item_t *linked_pkt) +{ + // Events should already have been dispatched before this point + //Tell Up-layer received packet. + if (btu_task_post(SIG_BTU_HCI_ADV_RPT_MSG, linked_pkt, OSI_THREAD_MAX_TIMEOUT) == false) { + osi_free(linked_pkt); +#if (BLE_ADV_REPORT_FLOW_CONTROL == TRUE) + hci_adv_credits_try_release(1); +#endif + } +} +// Misc internal functions + +// TODO(zachoverflow): we seem to do this a couple places, like the HCI inject module. #centralize +static serial_data_type_t event_to_data_type(uint16_t event) +{ + if (event == MSG_STACK_TO_HC_HCI_ACL) { + return DATA_TYPE_ACL; + } else if (event == MSG_STACK_TO_HC_HCI_SCO) { + return DATA_TYPE_SCO; + } else if (event == MSG_STACK_TO_HC_HCI_CMD) { + return DATA_TYPE_COMMAND; + } else { + HCI_TRACE_ERROR("%s invalid event type, could not translate 0x%x\n", __func__, event); + } + + return 0; +} + +static pkt_linked_item_t *get_waiting_command(command_opcode_t opcode) +{ + command_waiting_response_t *cmd_wait_q = &hci_host_env.cmd_waiting_q; + osi_mutex_lock(&cmd_wait_q->commands_pending_response_lock, OSI_MUTEX_MAX_TIMEOUT); + + for (const list_node_t *node = list_begin(cmd_wait_q->commands_pending_response); + node != list_end(cmd_wait_q->commands_pending_response); + node = list_next(node)) { + pkt_linked_item_t *wait_entry = list_node(node); + if (wait_entry) { + hci_cmd_metadata_t *metadata = (hci_cmd_metadata_t *)(wait_entry->data); + if (metadata->opcode == opcode) { + list_remove(cmd_wait_q->commands_pending_response, wait_entry); + osi_mutex_unlock(&cmd_wait_q->commands_pending_response_lock); + return wait_entry; + } + } + } + + osi_mutex_unlock(&cmd_wait_q->commands_pending_response_lock); + return NULL; +} + +static void init_layer_interface(void) +{ + if (!interface_created) { + interface.transmit_command = transmit_command; + interface.transmit_command_futured = transmit_command_futured; + interface.transmit_downward = transmit_downward; + interface_created = true; + } +} + +static const hci_hal_callbacks_t hal_callbacks = { + hal_says_packet_ready, + hal_says_adv_rpt_ready, +}; + +static const packet_fragmenter_callbacks_t packet_fragmenter_callbacks = { + transmit_fragment, + dispatch_reassembled, + fragmenter_transmit_finished +}; + +const hci_t *hci_layer_get_interface(void) +{ + hal = hci_hal_h4_get_interface(); + packet_fragmenter = packet_fragmenter_get_interface(); + + init_layer_interface(); + return &interface; +} diff --git a/lib/bt/host/bluedroid/hci/hci_packet_factory.c b/lib/bt/host/bluedroid/hci/hci_packet_factory.c new file mode 100644 index 00000000..5010e99d --- /dev/null +++ b/lib/bt/host/bluedroid/hci/hci_packet_factory.c @@ -0,0 +1,284 @@ +/****************************************************************************** + * + * Copyright (C) 2014 Google, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +#include "common/bt_defs.h" + +#include "osi/allocator.h" +#include "stack/bt_types.h" +#include "stack/hcidefs.h" +#include "stack/hcimsgs.h" +#include "hci/hci_internals.h" +#include "hci/hci_layer.h" +#include "hci/hci_packet_factory.h" + + +static BT_HDR *make_command_no_params(uint16_t opcode); +static BT_HDR *make_command(uint16_t opcode, size_t parameter_size, uint8_t **stream_out); + +// Interface functions + +static BT_HDR *make_reset(void) +{ + return make_command_no_params(HCI_RESET); +} + +static BT_HDR *make_read_buffer_size(void) +{ + return make_command_no_params(HCI_READ_BUFFER_SIZE); +} + +static BT_HDR *make_set_c2h_flow_control(uint8_t enable) +{ + uint8_t *stream; + const uint8_t parameter_size = 1; + BT_HDR *packet = make_command(HCI_SET_HC_TO_HOST_FLOW_CTRL, parameter_size, &stream); + + UINT8_TO_STREAM(stream, enable); + return packet; +} + +static BT_HDR *make_set_adv_report_flow_control(uint8_t enable, uint16_t num, uint16_t lost_threshold) +{ + uint8_t *stream; + const uint8_t parameter_size = 1 + 2 + 2; + BT_HDR *packet = make_command(HCI_VENDOR_BLE_SET_ADV_FLOW_CONTROL, parameter_size, &stream); + + UINT8_TO_STREAM(stream, enable); + UINT16_TO_STREAM(stream, num); + UINT16_TO_STREAM(stream, lost_threshold); + return packet; +} + +static BT_HDR *make_host_buffer_size(uint16_t acl_size, uint8_t sco_size, uint16_t acl_count, uint16_t sco_count) +{ + uint8_t *stream; + const uint8_t parameter_size = 2 + 1 + 2 + 2; // from each of the parameters + BT_HDR *packet = make_command(HCI_HOST_BUFFER_SIZE, parameter_size, &stream); + + UINT16_TO_STREAM(stream, acl_size); + UINT8_TO_STREAM(stream, sco_size); + UINT16_TO_STREAM(stream, acl_count); + UINT16_TO_STREAM(stream, sco_count); + return packet; +} + +static BT_HDR *make_read_local_version_info(void) +{ + return make_command_no_params(HCI_READ_LOCAL_VERSION_INFO); +} + +static BT_HDR *make_read_bd_addr(void) +{ + return make_command_no_params(HCI_READ_BD_ADDR); +} + +static BT_HDR *make_read_local_supported_commands(void) +{ + return make_command_no_params(HCI_READ_LOCAL_SUPPORTED_CMDS); +} + +static BT_HDR *make_read_local_supported_features(void) +{ + return make_command_no_params(HCI_READ_LOCAL_FEATURES); +} + +static BT_HDR *make_read_local_extended_features(uint8_t page_number) +{ + uint8_t *stream; + const uint8_t parameter_size = 1; + BT_HDR *packet = make_command(HCI_READ_LOCAL_EXT_FEATURES, parameter_size, &stream); + + UINT8_TO_STREAM(stream, page_number); + return packet; +} + +static BT_HDR *make_write_simple_pairing_mode(uint8_t mode) +{ + uint8_t *stream; + const uint8_t parameter_size = 1; + BT_HDR *packet = make_command(HCI_WRITE_SIMPLE_PAIRING_MODE, parameter_size, &stream); + + UINT8_TO_STREAM(stream, mode); + return packet; +} + +static BT_HDR *make_write_secure_connections_host_support(uint8_t mode) +{ + uint8_t *stream; + const uint8_t parameter_size = 1; + BT_HDR *packet = make_command(HCI_WRITE_SECURE_CONNS_SUPPORT, parameter_size, &stream); + + UINT8_TO_STREAM(stream, mode); + return packet; +} + +static BT_HDR *make_set_event_mask(const bt_event_mask_t *event_mask) +{ + uint8_t *stream; + uint8_t parameter_size = sizeof(bt_event_mask_t); + BT_HDR *packet = make_command(HCI_SET_EVENT_MASK, parameter_size, &stream); + + ARRAY8_TO_STREAM(stream, event_mask->as_array); + return packet; +} + +static BT_HDR *make_ble_write_host_support(uint8_t supported_host, uint8_t simultaneous_host) +{ + uint8_t *stream; + const uint8_t parameter_size = 1 + 1; + BT_HDR *packet = make_command(HCI_WRITE_LE_HOST_SUPPORT, parameter_size, &stream); + + UINT8_TO_STREAM(stream, supported_host); + UINT8_TO_STREAM(stream, simultaneous_host); + return packet; +} + +static BT_HDR *make_ble_read_white_list_size(void) +{ + return make_command_no_params(HCI_BLE_READ_WHITE_LIST_SIZE); +} + +static BT_HDR *make_ble_read_buffer_size(void) +{ + return make_command_no_params(HCI_BLE_READ_BUFFER_SIZE); +} + +static BT_HDR *make_ble_read_supported_states(void) +{ + return make_command_no_params(HCI_BLE_READ_SUPPORTED_STATES); +} + +static BT_HDR *make_ble_read_local_supported_features(void) +{ + return make_command_no_params(HCI_BLE_READ_LOCAL_SPT_FEAT); +} + +static BT_HDR *make_ble_read_resolving_list_size(void) +{ + return make_command_no_params(HCI_BLE_READ_RESOLVING_LIST_SIZE); +} + +static BT_HDR *make_ble_read_suggested_default_data_length(void) +{ + return make_command_no_params(HCI_BLE_READ_DEFAULT_DATA_LENGTH); +} + +static BT_HDR *make_ble_write_suggested_default_data_length(uint16_t SuggestedMaxTxOctets, uint16_t SuggestedMaxTxTime) +{ + uint8_t *stream; + uint8_t parameter_size = sizeof(uint16_t) + sizeof(uint16_t); + BT_HDR *packet = make_command(HCI_BLE_WRITE_DEFAULT_DATA_LENGTH, parameter_size, &stream); + + UINT16_TO_STREAM(stream, SuggestedMaxTxOctets); + UINT16_TO_STREAM(stream, SuggestedMaxTxTime); + return packet; +} + +static BT_HDR *make_ble_set_event_mask(const bt_event_mask_t *event_mask) +{ + uint8_t *stream; + uint8_t parameter_size = sizeof(bt_event_mask_t); + BT_HDR *packet = make_command(HCI_BLE_SET_EVENT_MASK, parameter_size, &stream); + + ARRAY8_TO_STREAM(stream, event_mask->as_array); + return packet; +} + +static BT_HDR *make_write_sync_flow_control_enable(uint8_t enable) +{ + uint8_t *stream; + const uint8_t parameter_size = 1; + BT_HDR *packet = make_command(HCI_WRITE_SCO_FLOW_CTRL_ENABLE, parameter_size, &stream); + + UINT8_TO_STREAM(stream, enable); + return packet; +} + +static BT_HDR *make_write_default_erroneous_data_report(uint8_t enable) +{ + uint8_t *stream; + const uint8_t parameter_size = 1; + BT_HDR *packet = make_command(HCI_WRITE_ERRONEOUS_DATA_RPT, parameter_size, &stream); + + UINT8_TO_STREAM(stream, enable); + return packet; +} +#if (BLE_50_FEATURE_SUPPORT == TRUE) +static BT_HDR *make_read_max_adv_data_len(void) +{ + return make_command_no_params(HCI_BLE_RD_MAX_ADV_DATA_LEN); +} +#endif // #if (BLE_50_FEATURE_SUPPORT == TRUE) +// Internal functions + +static BT_HDR *make_command_no_params(uint16_t opcode) +{ + return make_command(opcode, 0, NULL); +} + +static BT_HDR *make_command(uint16_t opcode, size_t parameter_size, uint8_t **stream_out) +{ + BT_HDR *packet = HCI_GET_CMD_BUF(parameter_size); + hci_cmd_metadata_t *metadata = HCI_GET_CMD_METAMSG(packet); + metadata->opcode = opcode; + + uint8_t *stream = packet->data; + UINT16_TO_STREAM(stream, opcode); + UINT8_TO_STREAM(stream, parameter_size); + + if (stream_out != NULL) { + *stream_out = stream; + } + + return packet; +} + +static const hci_packet_factory_t interface = { + make_reset, + make_read_buffer_size, + make_set_c2h_flow_control, + make_set_adv_report_flow_control, + make_host_buffer_size, + make_read_local_version_info, + make_read_bd_addr, + make_read_local_supported_commands, + make_read_local_supported_features, + make_read_local_extended_features, + make_write_simple_pairing_mode, + make_write_secure_connections_host_support, + make_set_event_mask, + make_ble_write_host_support, + make_ble_read_white_list_size, + make_ble_read_buffer_size, + make_ble_read_supported_states, + make_ble_read_local_supported_features, + make_ble_read_resolving_list_size, +#if (BLE_50_FEATURE_SUPPORT == TRUE) + make_read_max_adv_data_len, +#endif // #if (BLE_50_FEATURE_SUPPORT == TRUE) + make_ble_read_suggested_default_data_length, + make_ble_write_suggested_default_data_length, + make_ble_set_event_mask, + make_write_sync_flow_control_enable, + make_write_default_erroneous_data_report, +}; + +const hci_packet_factory_t *hci_packet_factory_get_interface(void) +{ + return &interface; +} diff --git a/lib/bt/host/bluedroid/hci/hci_packet_parser.c b/lib/bt/host/bluedroid/hci/hci_packet_parser.c new file mode 100644 index 00000000..88515376 --- /dev/null +++ b/lib/bt/host/bluedroid/hci/hci_packet_parser.c @@ -0,0 +1,285 @@ +/****************************************************************************** + * + * Copyright (C) 2014 Google, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +#include "common/bt_defs.h" + +#include "stack/bt_types.h" +#include "stack/hcimsgs.h" +#include "hci/hci_layer.h" +#include "hci/hci_packet_parser.h" + +static const command_opcode_t NO_OPCODE_CHECKING = 0; + +static uint8_t *read_command_complete_header( + BT_HDR *response, + command_opcode_t expected_opcode, + size_t minimum_bytes_after); + +static void parse_generic_command_complete(BT_HDR *response) +{ + read_command_complete_header(response, NO_OPCODE_CHECKING, 0 /* bytes after */); + + osi_free(response); +} + +static void parse_read_buffer_size_response( + BT_HDR *response, + uint16_t *acl_data_size_ptr, + uint16_t *acl_buffer_count_ptr, + uint8_t *sco_data_size_ptr, + uint16_t *sco_buffer_count_ptr) +{ + + uint8_t *stream = read_command_complete_header(response, HCI_READ_BUFFER_SIZE, 7 /* bytes after */); + assert(stream != NULL); + STREAM_TO_UINT16(*acl_data_size_ptr, stream); + STREAM_TO_UINT8(*sco_data_size_ptr, stream); + STREAM_TO_UINT16(*acl_buffer_count_ptr, stream); + STREAM_TO_UINT16(*sco_buffer_count_ptr, stream); + osi_free(response); +} + +static void parse_read_local_version_info_response( + BT_HDR *response, + bt_version_t *bt_version) +{ + + uint8_t *stream = read_command_complete_header(response, HCI_READ_LOCAL_VERSION_INFO, 8 /* bytes after */); + assert(stream != NULL); + STREAM_TO_UINT8(bt_version->hci_version, stream); + STREAM_TO_UINT16(bt_version->hci_revision, stream); + STREAM_TO_UINT8(bt_version->lmp_version, stream); + STREAM_TO_UINT16(bt_version->manufacturer, stream); + STREAM_TO_UINT16(bt_version->lmp_subversion, stream); + + osi_free(response); +} + +static void parse_read_bd_addr_response( + BT_HDR *response, + bt_bdaddr_t *address_ptr) +{ + + uint8_t *stream = read_command_complete_header(response, HCI_READ_BD_ADDR, sizeof(bt_bdaddr_t) /* bytes after */); + assert(stream != NULL); + STREAM_TO_BDADDR(address_ptr->address, stream); + + osi_free(response); +} + +static void parse_read_local_supported_commands_response( + BT_HDR *response, + uint8_t *supported_commands_ptr, + size_t supported_commands_length) +{ + + uint8_t *stream = read_command_complete_header(response, HCI_READ_LOCAL_SUPPORTED_CMDS, supported_commands_length /* bytes after */); + assert(stream != NULL); + STREAM_TO_ARRAY(supported_commands_ptr, stream, (int)supported_commands_length); + + osi_free(response); +} + +static void parse_read_local_supported_features_response( + BT_HDR *response, + bt_device_features_t *feature_pages) +{ + uint8_t *stream = read_command_complete_header(response, HCI_READ_LOCAL_FEATURES, sizeof(bt_device_features_t) /* bytes after */); + if (stream != NULL) { + STREAM_TO_ARRAY(feature_pages->as_array, stream, (int)sizeof(bt_device_features_t)); + } + osi_free(response); +} + +static void parse_read_local_extended_features_response( + BT_HDR *response, + uint8_t *page_number_ptr, + uint8_t *max_page_number_ptr, + bt_device_features_t *feature_pages, + size_t feature_pages_count) +{ + + uint8_t *stream = read_command_complete_header(response, HCI_READ_LOCAL_EXT_FEATURES, 2 + sizeof(bt_device_features_t) /* bytes after */); + if (stream != NULL) { + STREAM_TO_UINT8(*page_number_ptr, stream); + STREAM_TO_UINT8(*max_page_number_ptr, stream); + + assert(*page_number_ptr < feature_pages_count); + STREAM_TO_ARRAY(feature_pages[*page_number_ptr].as_array, stream, (int)sizeof(bt_device_features_t)); + } else { + HCI_TRACE_ERROR("%s() - WARNING: READING EXTENDED FEATURES FAILED. " + "THIS MAY INDICATE A FIRMWARE/CONTROLLER ISSUE.", __func__); + } + + osi_free(response); +} + +static void parse_ble_read_white_list_size_response( + BT_HDR *response, + uint8_t *white_list_size_ptr) +{ + + uint8_t *stream = read_command_complete_header(response, HCI_BLE_READ_WHITE_LIST_SIZE, 1 /* byte after */); + assert(stream != NULL); + STREAM_TO_UINT8(*white_list_size_ptr, stream); + + osi_free(response); +} + +static void parse_ble_read_buffer_size_response( + BT_HDR *response, + uint16_t *data_size_ptr, + uint8_t *acl_buffer_count_ptr) +{ + + uint8_t *stream = read_command_complete_header(response, HCI_BLE_READ_BUFFER_SIZE, 3 /* bytes after */); + assert(stream != NULL); + STREAM_TO_UINT16(*data_size_ptr, stream); + STREAM_TO_UINT8(*acl_buffer_count_ptr, stream); + + osi_free(response); +} + +static void parse_ble_read_supported_states_response( + BT_HDR *response, + uint8_t *supported_states, + size_t supported_states_size) +{ + + uint8_t *stream = read_command_complete_header(response, HCI_BLE_READ_SUPPORTED_STATES, supported_states_size /* bytes after */); + assert(stream != NULL); + STREAM_TO_ARRAY(supported_states, stream, (int)supported_states_size); + + osi_free(response); +} + +static void parse_ble_read_local_supported_features_response( + BT_HDR *response, + bt_device_features_t *supported_features) +{ + + uint8_t *stream = read_command_complete_header(response, HCI_BLE_READ_LOCAL_SPT_FEAT, sizeof(bt_device_features_t) /* bytes after */); + assert(stream != NULL); + STREAM_TO_ARRAY(supported_features->as_array, stream, (int)sizeof(bt_device_features_t)); + + osi_free(response); +} + +static void parse_ble_read_resolving_list_size_response( + BT_HDR *response, + uint8_t *resolving_list_size_ptr) +{ + + uint8_t *stream = read_command_complete_header(response, HCI_BLE_READ_RESOLVING_LIST_SIZE, 1 /* bytes after */); + STREAM_TO_UINT8(*resolving_list_size_ptr, stream); + + osi_free(response); +} + +static void parse_ble_read_suggested_default_data_length_response( + BT_HDR *response, + uint16_t *ble_default_packet_length_ptr, + uint16_t *ble_default_packet_txtime_ptr) +{ + + uint8_t *stream = read_command_complete_header(response, HCI_BLE_READ_DEFAULT_DATA_LENGTH, 2 /* bytes after */); + STREAM_TO_UINT16(*ble_default_packet_length_ptr, stream); + STREAM_TO_UINT16(*ble_default_packet_txtime_ptr, stream); + osi_free(response); +} +#if (BLE_50_FEATURE_SUPPORT == TRUE) +static void parse_ble_read_adv_max_len_response( + BT_HDR *response, + uint16_t *adv_max_len_ptr) +{ + + uint8_t *stream = read_command_complete_header(response, HCI_BLE_RD_MAX_ADV_DATA_LEN, 1 /* bytes after */); + // Size: 2 Octets ; Value: 0x001F – 0x0672 ; Maximum supported advertising data length + STREAM_TO_UINT16(*adv_max_len_ptr, stream); + + osi_free(response); +} +#endif // #if (BLE_50_FEATURE_SUPPORT == TRUE) + + +// Internal functions + +static uint8_t *read_command_complete_header( + BT_HDR *response, + command_opcode_t expected_opcode, + size_t minimum_bytes_after) +{ + + uint8_t *stream = response->data + response->offset; + + // Read the event header + uint8_t event_code __attribute__((unused)); + uint8_t parameter_length __attribute__((unused)); + STREAM_TO_UINT8(event_code, stream); + STREAM_TO_UINT8(parameter_length, stream); + + const size_t parameter_bytes_we_read_here __attribute__((unused)) = 4; + + // Check the event header values against what we expect + assert(event_code == HCI_COMMAND_COMPLETE_EVT); + assert(parameter_length >= (parameter_bytes_we_read_here + minimum_bytes_after)); + + // Read the command complete header + command_opcode_t opcode __attribute__((unused)); + uint8_t status; + STREAM_SKIP_UINT8(stream); // skip the number of hci command packets field + STREAM_TO_UINT16(opcode, stream); + + // Check the command complete header values against what we expect + if (expected_opcode != NO_OPCODE_CHECKING) { + assert(opcode == expected_opcode); + } + + // Assume the next field is the status field + STREAM_TO_UINT8(status, stream); + + if (status != HCI_SUCCESS) { + return NULL; + } + + return stream; +} + +static const hci_packet_parser_t interface = { + parse_generic_command_complete, + parse_read_buffer_size_response, + parse_read_local_version_info_response, + parse_read_bd_addr_response, + parse_read_local_supported_commands_response, + parse_read_local_supported_features_response, + parse_read_local_extended_features_response, + parse_ble_read_white_list_size_response, + parse_ble_read_buffer_size_response, + parse_ble_read_supported_states_response, + parse_ble_read_local_supported_features_response, + parse_ble_read_resolving_list_size_response, +#if (BLE_50_FEATURE_SUPPORT == TRUE) + parse_ble_read_adv_max_len_response, +#endif // #if (BLE_50_FEATURE_SUPPORT == TRUE) + parse_ble_read_suggested_default_data_length_response +}; + +const hci_packet_parser_t *hci_packet_parser_get_interface(void) +{ + return &interface; +} diff --git a/lib/bt/host/bluedroid/hci/include/hci/bt_vendor_lib.h b/lib/bt/host/bluedroid/hci/include/hci/bt_vendor_lib.h new file mode 100644 index 00000000..63e6f9a6 --- /dev/null +++ b/lib/bt/host/bluedroid/hci/include/hci/bt_vendor_lib.h @@ -0,0 +1,361 @@ +/****************************************************************************** + * + * Copyright (C) 2009-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +#ifndef BT_VENDOR_LIB_H +#define BT_VENDOR_LIB_H + +#include +//#include +//#include + +/** Struct types */ + + +/** Typedefs and defines */ + +/** Vendor specific operations OPCODE */ +typedef enum { + /* [operation] + * Power on or off the BT Controller. + * [input param] + * A pointer to int type with content of bt_vendor_power_state_t. + * Typecasting conversion: (int *) param. + * [return] + * 0 - default, don't care. + * [callback] + * None. + */ + BT_VND_OP_POWER_CTRL, + + /* [operation] + * Perform any vendor specific initialization or configuration + * on the BT Controller. This is called before stack initialization. + * [input param] + * None. + * [return] + * 0 - default, don't care. + * [callback] + * Must call fwcfg_cb to notify the stack of the completion of vendor + * specific initialization once it has been done. + */ + BT_VND_OP_FW_CFG, + + /* [operation] + * Perform any vendor specific SCO/PCM configuration on the BT Controller. + * This is called after stack initialization. + * [input param] + * None. + * [return] + * 0 - default, don't care. + * [callback] + * Must call scocfg_cb to notify the stack of the completion of vendor + * specific SCO configuration once it has been done. + */ + BT_VND_OP_SCO_CFG, + + /* [operation] + * Open UART port on where the BT Controller is attached. + * This is called before stack initialization. + * [input param] + * A pointer to int array type for open file descriptors. + * The mapping of HCI channel to fd slot in the int array is given in + * bt_vendor_hci_channels_t. + * And, it requires the vendor lib to fill up the content before returning + * the call. + * Typecasting conversion: (int (*)[]) param. + * [return] + * Numbers of opened file descriptors. + * Valid number: + * 1 - CMD/EVT/ACL-In/ACL-Out via the same fd (e.g. UART) + * 2 - CMD/EVT on one fd, and ACL-In/ACL-Out on the other fd + * 4 - CMD, EVT, ACL-In, ACL-Out are on their individual fd + * [callback] + * None. + */ + BT_VND_OP_USERIAL_OPEN, + + /* [operation] + * Close the previously opened UART port. + * [input param] + * None. + * [return] + * 0 - default, don't care. + * [callback] + * None. + */ + BT_VND_OP_USERIAL_CLOSE, + + /* [operation] + * Get the LPM idle timeout in milliseconds. + * The stack uses this information to launch a timer delay before it + * attempts to de-assert LPM WAKE signal once downstream HCI packet + * has been delivered. + * [input param] + * A pointer to uint32_t type which is passed in by the stack. And, it + * requires the vendor lib to fill up the content before returning + * the call. + * Typecasting conversion: (uint32_t *) param. + * [return] + * 0 - default, don't care. + * [callback] + * None. + */ + BT_VND_OP_GET_LPM_IDLE_TIMEOUT, + + /* [operation] + * Enable or disable LPM mode on BT Controller. + * [input param] + * A pointer to uint8_t type with content of bt_vendor_lpm_mode_t. + * Typecasting conversion: (uint8_t *) param. + * [return] + * 0 - default, don't care. + * [callback] + * Must call lpm_cb to notify the stack of the completion of LPM + * disable/enable process once it has been done. + */ + BT_VND_OP_LPM_SET_MODE, + + /* [operation] + * Assert or Deassert LPM WAKE on BT Controller. + * [input param] + * A pointer to uint8_t type with content of bt_vendor_lpm_wake_state_t. + * Typecasting conversion: (uint8_t *) param. + * [return] + * 0 - default, don't care. + * [callback] + * None. + */ + BT_VND_OP_LPM_WAKE_SET_STATE, + + /* [operation] + * Perform any vendor specific commands related to audio state changes. + * [input param] + * a pointer to bt_vendor_op_audio_state_t indicating what audio state is + * set. + * [return] + * 0 - default, don't care. + * [callback] + * None. + */ + BT_VND_OP_SET_AUDIO_STATE, + + /* [operation] + * The epilog call to the vendor module so that it can perform any + * vendor-specific processes (e.g. send a HCI_RESET to BT Controller) + * before the caller calls for cleanup(). + * [input param] + * None. + * [return] + * 0 - default, don't care. + * [callback] + * Must call epilog_cb to notify the stack of the completion of vendor + * specific epilog process once it has been done. + */ + BT_VND_OP_EPILOG, +} bt_vendor_opcode_t; + +/** Power on/off control states */ +typedef enum { + BT_VND_PWR_OFF, + BT_VND_PWR_ON, +} bt_vendor_power_state_t; + +/** Define HCI channel identifier in the file descriptors array + used in BT_VND_OP_USERIAL_OPEN operation. + */ +typedef enum { + CH_CMD, // HCI Command channel + CH_EVT, // HCI Event channel + CH_ACL_OUT, // HCI ACL downstream channel + CH_ACL_IN, // HCI ACL upstream channel + + CH_MAX // Total channels +} bt_vendor_hci_channels_t; + +/** LPM disable/enable request */ +typedef enum { + BT_VND_LPM_DISABLE, + BT_VND_LPM_ENABLE, +} bt_vendor_lpm_mode_t; + +/** LPM WAKE set state request */ +typedef enum { + BT_VND_LPM_WAKE_ASSERT, + BT_VND_LPM_WAKE_DEASSERT, +} bt_vendor_lpm_wake_state_t; + +/** Callback result values */ +typedef enum { + BT_VND_OP_RESULT_SUCCESS, + BT_VND_OP_RESULT_FAIL, +} bt_vendor_op_result_t; + +/** audio (SCO) state changes triggering VS commands for configuration */ +typedef struct { + uint16_t handle; + uint16_t peer_codec; + uint16_t state; +} bt_vendor_op_audio_state_t; + +/* + * Bluetooth Host/Controller Vendor callback structure. + */ + +/* vendor initialization/configuration callback */ +typedef void (*cfg_result_cb)(bt_vendor_op_result_t result); + +/* datapath buffer allocation callback (callout) + * + * Vendor lib needs to request a buffer through the alloc callout function + * from HCI lib if the buffer is for constructing a HCI Command packet which + * will be sent through xmit_cb to BT Controller. + * + * For each buffer allocation, the requested size needs to be big enough to + * accommodate the below header plus a complete HCI packet -- + * typedef struct + * { + * uint16_t event; + * uint16_t len; + * uint16_t offset; + * uint16_t layer_specific; + * } HC_BT_HDR; + * + * HCI lib returns a pointer to the buffer where Vendor lib should use to + * construct a HCI command packet as below format: + * + * -------------------------------------------- + * | HC_BT_HDR | HCI command | + * -------------------------------------------- + * where + * HC_BT_HDR.event = 0x2000; + * HC_BT_HDR.len = Length of HCI command; + * HC_BT_HDR.offset = 0; + * HC_BT_HDR.layer_specific = 0; + * + * For example, a HCI_RESET Command will be formed as + * ------------------------ + * | HC_BT_HDR |03|0c|00| + * ------------------------ + * with + * HC_BT_HDR.event = 0x2000; + * HC_BT_HDR.len = 3; + * HC_BT_HDR.offset = 0; + * HC_BT_HDR.layer_specific = 0; + */ +typedef void *(*malloc_cb)(int size); + +/* datapath buffer deallocation callback (callout) */ +typedef void (*mdealloc_cb)(void *p_buf); + +/* define callback of the cmd_xmit_cb + * + * The callback function which HCI lib will call with the return of command + * complete packet. Vendor lib is responsible for releasing the buffer passed + * in at the p_mem parameter by calling dealloc callout function. + */ +typedef void (*tINT_CMD_CBACK)(void *p_mem); + +/* hci command packet transmit callback (callout) + * + * Vendor lib calls xmit_cb callout function in order to send a HCI Command + * packet to BT Controller. The buffer carrying HCI Command packet content + * needs to be first allocated through the alloc callout function. + * HCI lib will release the buffer for Vendor lib once it has delivered the + * packet content to BT Controller. + * + * Vendor lib needs also provide a callback function (p_cback) which HCI lib + * will call with the return of command complete packet. + * + * The opcode parameter gives the HCI OpCode (combination of OGF and OCF) of + * HCI Command packet. For example, opcode = 0x0c03 for the HCI_RESET command + * packet. + */ +typedef uint8_t (*cmd_xmit_cb)(uint16_t opcode, void *p_buf, tINT_CMD_CBACK p_cback); + +typedef struct { + /** set to sizeof(bt_vendor_callbacks_t) */ + size_t size; + + /* + * Callback and callout functions have implemented in HCI libray + * (libbt-hci.so). + */ + + /* notifies caller result of firmware configuration request */ + cfg_result_cb fwcfg_cb; + + /* notifies caller result of sco configuration request */ + cfg_result_cb scocfg_cb; + + /* notifies caller result of lpm enable/disable */ + cfg_result_cb lpm_cb; + + /* notifies the result of codec setting */ + cfg_result_cb audio_state_cb; + + /* buffer allocation request */ + malloc_cb alloc; + + /* buffer deallocation request */ + mdealloc_cb dealloc; + + /* hci command packet transmit request */ + cmd_xmit_cb xmit_cb; + + /* notifies caller completion of epilog process */ + cfg_result_cb epilog_cb; +} bt_vendor_callbacks_t; + +/* + * Bluetooth Host/Controller VENDOR Interface + */ +typedef struct { + /** Set to sizeof(bt_vndor_interface_t) */ + size_t size; + + /* + * Functions need to be implemented in Vendor libray (libbt-vendor.so). + */ + + /** + * Caller will open the interface and pass in the callback routines + * to the implemenation of this interface. + */ + int (*init)(const bt_vendor_callbacks_t *p_cb, unsigned char *local_bdaddr); + + /** Vendor specific operations */ + int (*op)(bt_vendor_opcode_t opcode, void *param); + + /** Closes the interface */ + void (*cleanup)(void); +} bt_vendor_interface_t; + + +/* + * External shared lib functions/data + */ + +/* Entry point of DLib -- + * Vendor library needs to implement the body of bt_vendor_interface_t + * structure and uses the below name as the variable name. HCI library + * will use this symbol name to get address of the object through the + * dlsym call. + */ +//extern const bt_vendor_interface_t BLUETOOTH_VENDOR_LIB_INTERFACE; + +#endif /* BT_VENDOR_LIB_H */ diff --git a/lib/bt/host/bluedroid/hci/include/hci/hci_audio.h b/lib/bt/host/bluedroid/hci/include/hci/hci_audio.h new file mode 100644 index 00000000..a9234c3a --- /dev/null +++ b/lib/bt/host/bluedroid/hci/include/hci/hci_audio.h @@ -0,0 +1,42 @@ +/****************************************************************************** + * + * Copyright (C) 2015 Google, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +#ifndef _HCI_AUDIO_H_ +#define _HCI_AUDIO_H_ +#include + +// Audio state definitions. +typedef enum { + SCO_STATE_OFF = 0, // Audio is off. + SCO_STATE_OFF_TRANSFER, // Closed pending final transfer of audio. + SCO_STATE_ON, // Audio is on. + SCO_STATE_SETUP, // Open pending completion of audio setup. +} sco_state_t; + +// Codec type definitions. +typedef enum { + SCO_CODEC_NONE = 0x0000, + SCO_CODEC_CVSD = 0x0001, + SCO_CODEC_MSBC = 0x0002, +} sco_codec_t; + +// Set the audio state on the controller for SCO (PCM, WBS, ...) using the +// vendor library. +void set_audio_state(uint16_t handle, sco_codec_t codec, sco_state_t state); + +#endif /* _HCI_AUDIO_H_ */ diff --git a/lib/bt/host/bluedroid/hci/include/hci/hci_hal.h b/lib/bt/host/bluedroid/hci/include/hci/hci_hal.h new file mode 100644 index 00000000..fe5796db --- /dev/null +++ b/lib/bt/host/bluedroid/hci/include/hci/hci_hal.h @@ -0,0 +1,96 @@ +/****************************************************************************** + * + * Copyright (C) 2014 Google, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +#ifndef _HCI_HAL_H_ +#define _HCI_HAL_H_ + +#include +#include +#include "common/bt_target.h" +#include "osi/pkt_queue.h" +#include "stack/bt_types.h" +#if ((BT_CONTROLLER_INCLUDED == TRUE) && SOC_ESP_NIMBLE_CONTROLLER) +#include "os/os_mbuf.h" +#endif +typedef enum { + DATA_TYPE_COMMAND = 1, + DATA_TYPE_ACL = 2, + DATA_TYPE_SCO = 3, + DATA_TYPE_EVENT = 4 +} serial_data_type_t; + +typedef void (*packet_ready_cb)(BT_HDR *packet); +typedef void (*adv_rpt_ready_cb)(pkt_linked_item_t *linked_pkt); + +typedef struct { + // Called when the HAL detects inbound data. + // Data |type| may be ACL, SCO, or EVENT. + // Executes in the context of the thread supplied to |init|. + packet_ready_cb packet_ready; + adv_rpt_ready_cb adv_rpt_ready; + + /* + // Called when the HAL detects inbound astronauts named Dave. + // HAL will deny all requests to open the pod bay doors after this. + dave_ready_cb dave_ready; + */ +} hci_hal_callbacks_t; + +typedef struct hci_hal_t { + // Initialize the HAL, with |upper_callbacks| and |upper_thread| to run in the context of. + //bool (*init)(const hci_hal_callbacks_t *upper_callbacks); + + // Connect to the underlying hardware, and let data start flowing. + bool (*open)(const hci_hal_callbacks_t *upper_callbacks, void *task_thread); + // Disconnect from the underlying hardware, and close the HAL. + // "Daisy, Daisy..." + void (*close)(void); + + // Retrieve up to |max_size| bytes for ACL, SCO, or EVENT data packets into + // |buffer|, blocking until max_size bytes read if |block| is true. + // Only guaranteed to be correct in the context of a data_ready callback + // of the corresponding type. + //size_t (*read_data)(serial_data_type_t type, uint8_t *buffer, size_t max_size); + // The upper layer must call this to notify the HAL that it has finished + // reading a packet of the specified |type|. Underlying implementations that + // use shared channels for multiple data types depend on this to know when + // to reinterpret the data stream. + //void (*packet_finished)(serial_data_type_t type); + // Transmit COMMAND, ACL, or SCO data packets. + // |data| may not be NULL. |length| must be greater than zero. + // + // IMPORTANT NOTE: + // Depending on the underlying implementation, the byte right + // before the beginning of |data| may be borrowed during this call + // and then restored to its original value. + // This is safe in the bluetooth context, because there is always a buffer + // header that prefixes data you're sending. + uint16_t (*transmit_data)(serial_data_type_t type, uint8_t *data, uint16_t length); +} hci_hal_t; + + +// Gets the correct hal implementation, as compiled for. +const hci_hal_t *hci_hal_h4_get_interface(void); +#if ((BT_CONTROLLER_INCLUDED == TRUE) && SOC_ESP_NIMBLE_CONTROLLER) +int ble_hs_hci_rx_evt(uint8_t *hci_ev, void *arg); + +int ble_hs_rx_data(struct os_mbuf *om, void *arg); +#endif + + +#endif /* _HCI_HAL_H */ diff --git a/lib/bt/host/bluedroid/hci/include/hci/hci_internals.h b/lib/bt/host/bluedroid/hci/include/hci/hci_internals.h new file mode 100644 index 00000000..41c792cf --- /dev/null +++ b/lib/bt/host/bluedroid/hci/include/hci/hci_internals.h @@ -0,0 +1,31 @@ +/****************************************************************************** + * + * Copyright (C) 2014 Google, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +#ifndef _HCI_INTERNALS_H_ +#define _HCI_INTERNALS_H_ + +// 2 bytes for opcode, 1 byte for parameter length (Volume 2, Part E, 5.4.1) +#define HCI_COMMAND_PREAMBLE_SIZE 3 +// 2 bytes for handle, 2 bytes for data length (Volume 2, Part E, 5.4.2) +#define HCI_ACL_PREAMBLE_SIZE 4 +// 2 bytes for handle, 1 byte for data length (Volume 2, Part E, 5.4.3) +#define HCI_SCO_PREAMBLE_SIZE 3 +// 1 byte for event code, 1 byte for parameter length (Volume 2, Part E, 5.4.4) +#define HCI_EVENT_PREAMBLE_SIZE 2 + +#endif /* _HCI_INTERNALS_H_ */ diff --git a/lib/bt/host/bluedroid/hci/include/hci/hci_layer.h b/lib/bt/host/bluedroid/hci/include/hci/hci_layer.h new file mode 100644 index 00000000..8e822f0f --- /dev/null +++ b/lib/bt/host/bluedroid/hci/include/hci/hci_layer.h @@ -0,0 +1,113 @@ +/****************************************************************************** + * + * Copyright (C) 2014 Google, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +#ifndef _HCI_LAYER_H_ +#define _HCI_LAYER_H_ + +#include "common/bt_target.h" +#include "stack/bt_types.h" +#include "osi/allocator.h" +#include "osi/osi.h" +#include "osi/future.h" +#include "osi/thread.h" +#include "osi/pkt_queue.h" + +///// LEGACY DEFINITIONS ///// + +/* Message event mask across Host/Controller lib and stack */ +#define MSG_EVT_MASK 0xFF00 /* eq. BT_EVT_MASK */ +#define MSG_SUB_EVT_MASK 0x00FF /* eq. BT_SUB_EVT_MASK */ + +/* Message event ID passed from Host/Controller lib to stack */ +#define MSG_HC_TO_STACK_HCI_ERR 0x1300 /* eq. BT_EVT_TO_BTU_HCIT_ERR */ +#define MSG_HC_TO_STACK_HCI_ACL 0x1100 /* eq. BT_EVT_TO_BTU_HCI_ACL */ +#define MSG_HC_TO_STACK_HCI_SCO 0x1200 /* eq. BT_EVT_TO_BTU_HCI_SCO */ +#define MSG_HC_TO_STACK_HCI_EVT 0x1000 /* eq. BT_EVT_TO_BTU_HCI_EVT */ +#define MSG_HC_TO_STACK_L2C_SEG_XMIT 0x1900 /* eq. BT_EVT_TO_BTU_L2C_SEG_XMIT */ + +/* Message event ID passed from stack to vendor lib */ +#define MSG_STACK_TO_HC_HCI_ACL 0x2100 /* eq. BT_EVT_TO_LM_HCI_ACL */ +#define MSG_STACK_TO_HC_HCI_SCO 0x2200 /* eq. BT_EVT_TO_LM_HCI_SCO */ +#define MSG_STACK_TO_HC_HCI_CMD 0x2000 /* eq. BT_EVT_TO_LM_HCI_CMD */ + +/* Local Bluetooth Controller ID for BR/EDR */ +#define LOCAL_BR_EDR_CONTROLLER_ID 0 + +#define HCI_CMD_MSG_F_VND_FUTURE (0x01) +#define HCI_CMD_MSG_F_VND_QUEUED (0x02) +#define HCI_CMD_MSG_F_VND_SENT (0x04) +///// END LEGACY DEFINITIONS ///// + +typedef struct hci_hal_t hci_hal_t; +//typedef struct btsnoop_t btsnoop_t; +typedef struct controller_t controller_t; +//typedef struct hci_inject_t hci_inject_t; +typedef struct packet_fragmenter_t packet_fragmenter_t; +//typedef struct vendor_t vendor_t; +//typedef struct low_power_manager_t low_power_manager_t; + +//typedef unsigned char * bdaddr_t; +typedef uint16_t command_opcode_t; + +/* +typedef enum { + LPM_DISABLE, + LPM_ENABLE, + LPM_WAKE_ASSERT, + LPM_WAKE_DEASSERT +} low_power_command_t; +*/ + +typedef void (*command_complete_cb)(BT_HDR *response, void *context); +typedef void (*command_status_cb)(uint8_t status, BT_HDR *command, void *context); + +typedef struct hci_t { + // Send a low power command, if supported and the low power manager is enabled. + //void (*send_low_power_command)(low_power_command_t command); + + // Do the postload sequence (call after the rest of the BT stack initializes). + void (*do_postload)(void); + + // Send a command through the HCI layer + void (*transmit_command)( + BT_HDR *command, + command_complete_cb complete_callback, + command_status_cb status_cb, + void *context + ); + + future_t *(*transmit_command_futured)(BT_HDR *command); + + // Send some data downward through the HCI layer + void (*transmit_downward)(uint16_t type, void *data); +} hci_t; + +const hci_t *hci_layer_get_interface(void); + +int hci_start_up(void); +void hci_shut_down(void); + +bool hci_downstream_data_post(uint32_t timeout); + +#if (BLE_ADV_REPORT_FLOW_CONTROL == TRUE) +int hci_adv_credits_prep_to_release(uint16_t num); +int hci_adv_credits_try_release(uint16_t num); +int hci_adv_credits_force_release(uint16_t num); +#endif + +#endif /* _HCI_LAYER_H_ */ diff --git a/lib/bt/host/bluedroid/hci/include/hci/hci_packet_factory.h b/lib/bt/host/bluedroid/hci/include/hci/hci_packet_factory.h new file mode 100644 index 00000000..c1580fd8 --- /dev/null +++ b/lib/bt/host/bluedroid/hci/include/hci/hci_packet_factory.h @@ -0,0 +1,57 @@ +/****************************************************************************** + * + * Copyright (C) 2014 Google, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +#ifndef _HCI_PACKET_FACTORY_H_ +#define _HCI_PACKET_FACTORY_H_ + +#include "stack/bt_types.h" +#include "device/event_mask.h" + +typedef struct { + BT_HDR *(*make_reset)(void); + BT_HDR *(*make_read_buffer_size)(void); + BT_HDR *(*make_set_c2h_flow_control)(uint8_t enable); + BT_HDR *(*make_set_adv_report_flow_control)(uint8_t enable, uint16_t num, uint16_t lost_threshold); + BT_HDR *(*make_host_buffer_size)(uint16_t acl_size, uint8_t sco_size, uint16_t acl_count, uint16_t sco_count); + BT_HDR *(*make_read_local_version_info)(void); + BT_HDR *(*make_read_bd_addr)(void); + BT_HDR *(*make_read_local_supported_commands)(void); + BT_HDR *(*make_read_local_supported_features)(void); + BT_HDR *(*make_read_local_extended_features)(uint8_t page_number); + BT_HDR *(*make_write_simple_pairing_mode)(uint8_t mode); + BT_HDR *(*make_write_secure_connections_host_support)(uint8_t mode); + BT_HDR *(*make_set_event_mask)(const bt_event_mask_t *event_mask); + BT_HDR *(*make_ble_write_host_support)(uint8_t supported_host, uint8_t simultaneous_host); + BT_HDR *(*make_ble_read_white_list_size)(void); + BT_HDR *(*make_ble_read_buffer_size)(void); + BT_HDR *(*make_ble_read_supported_states)(void); + BT_HDR *(*make_ble_read_local_supported_features)(void); + BT_HDR *(*make_ble_read_resolving_list_size)(void); +#if (BLE_50_FEATURE_SUPPORT == TRUE) + BT_HDR *(*make_read_max_adv_data_len)(void); +#endif // #if (BLE_50_FEATURE_SUPPORT == TRUE) + BT_HDR *(*make_ble_read_suggested_default_data_length)(void); + BT_HDR *(*make_ble_write_suggested_default_data_length)(uint16_t SuggestedMaxTxOctets, uint16_t SuggestedMaxTxTime); + BT_HDR *(*make_ble_set_event_mask)(const bt_event_mask_t *event_mask); + BT_HDR *(*make_write_sync_flow_control_enable)(uint8_t enable); + BT_HDR *(*make_write_default_erroneous_data_report)(uint8_t enable); +} hci_packet_factory_t; + +const hci_packet_factory_t *hci_packet_factory_get_interface(void); + +#endif /*_HCI_PACKET_FACTORY_H_*/ diff --git a/lib/bt/host/bluedroid/hci/include/hci/hci_packet_parser.h b/lib/bt/host/bluedroid/hci/include/hci/hci_packet_parser.h new file mode 100644 index 00000000..e46acc17 --- /dev/null +++ b/lib/bt/host/bluedroid/hci/include/hci/hci_packet_parser.h @@ -0,0 +1,112 @@ +/****************************************************************************** + * + * Copyright (C) 2014 Google, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +#ifndef _HCI_PACKET_PARSER_H_ +#define _HCI_PACKET_PARSER_H_ + +#include + +#include "osi/allocator.h" +#include "device/bdaddr.h" +#include "stack/bt_types.h" +#include "device/device_features.h" +//#include "features.h" +#include "device/version.h" + +typedef struct { + void (*parse_generic_command_complete)(BT_HDR *response); + + void (*parse_read_buffer_size_response)( + BT_HDR *response, + uint16_t *acl_data_size_ptr, + uint16_t *acl_buffer_count_ptr, + uint8_t *sco_data_size_ptr, + uint16_t *sco_buffer_count_ptr + ); + + void (*parse_read_local_version_info_response)( + BT_HDR *response, + bt_version_t *bt_version_ptr + ); + + void (*parse_read_bd_addr_response)( + BT_HDR *response, + bt_bdaddr_t *address_ptr + ); + + void (*parse_read_local_supported_commands_response)( + BT_HDR *response, + uint8_t *supported_commands_ptr, + size_t supported_commands_length + ); + + void (*parse_read_local_supported_features_response)( + BT_HDR *response, + bt_device_features_t *feature_pages + ); + + void (*parse_read_local_extended_features_response)( + BT_HDR *response, + uint8_t *page_number_ptr, + uint8_t *max_page_number_ptr, + bt_device_features_t *feature_pages, + size_t feature_pages_count + ); + + void (*parse_ble_read_white_list_size_response)( + BT_HDR *response, + uint8_t *white_list_size_ptr + ); + + void (*parse_ble_read_buffer_size_response)( + BT_HDR *response, + uint16_t *data_size_ptr, + uint8_t *acl_buffer_count_ptr + ); + + void (*parse_ble_read_supported_states_response)( + BT_HDR *response, + uint8_t *supported_states, + size_t supported_states_size + ); + + void (*parse_ble_read_local_supported_features_response)( + BT_HDR *response, + bt_device_features_t *supported_features + ); + + void (*parse_ble_read_resolving_list_size_response) ( + BT_HDR *response, + uint8_t *resolving_list_size_ptr + ); +#if (BLE_50_FEATURE_SUPPORT == TRUE) + void (*parse_ble_read_adv_max_len_response) ( + BT_HDR *respone, + uint16_t *ble_ext_adv_data_max_len_ptr + ); +#endif // #if (BLE_50_FEATURE_SUPPORT == TRUE) + void (*parse_ble_read_suggested_default_data_length_response)( + BT_HDR *response, + uint16_t *ble_default_packet_length_ptr, + uint16_t *ble_default_packet_txtime_ptr + ); +} hci_packet_parser_t; + +const hci_packet_parser_t *hci_packet_parser_get_interface(void); + +#endif /*_HCI_PACKET_PARSER_H_*/ diff --git a/lib/bt/host/bluedroid/hci/include/hci/hci_trans_int.h b/lib/bt/host/bluedroid/hci/include/hci/hci_trans_int.h new file mode 100644 index 00000000..86d74bd4 --- /dev/null +++ b/lib/bt/host/bluedroid/hci/include/hci/hci_trans_int.h @@ -0,0 +1,47 @@ +/* + * SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __HCI_TRANS_INT_H__ +#define __HCI_TRANS_INT_H__ + +#include +#include +#include "esp_err.h" +#include "esp_bluedroid_hci.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief host checks whether it can send data to controller + * + * @return true if host can send data, false otherwise + */ +bool hci_host_check_send_available(void); + +/** + * @brief host sends packet to controller + * + * @param[in] data pointer to data buffer + * @param[in] len length of data in byte + */ +void hci_host_send_packet(uint8_t *data, uint16_t len); + +/** + * @brief register the HCI function interface + * + * @param[in] callback HCI function interface + * + * @return ESP_OK register successfully, ESP_FAIL otherwise + */ +esp_err_t hci_host_register_callback(const esp_bluedroid_hci_driver_callbacks_t *callback); + +#ifdef __cplusplus +} +#endif + +#endif /* __HCI_TRANS_INT_H__ */ diff --git a/lib/bt/host/bluedroid/hci/include/hci/packet_fragmenter.h b/lib/bt/host/bluedroid/hci/include/hci/packet_fragmenter.h new file mode 100644 index 00000000..02a13fb9 --- /dev/null +++ b/lib/bt/host/bluedroid/hci/include/hci/packet_fragmenter.h @@ -0,0 +1,62 @@ +/****************************************************************************** + * + * Copyright (C) 2014 Google, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +#ifndef _PACKET_FRAGMENTER_H_ +#define _PACKET_FRAGMENTER_H_ + +#include "osi/allocator.h" +#include "stack/bt_types.h" +#include "hci/hci_layer.h" + +typedef void (*transmit_finished_cb)(BT_HDR *packet, bool all_fragments_sent); +typedef void (*packet_reassembled_cb)(BT_HDR *packet); +typedef void (*packet_fragmented_cb)(BT_HDR *packet, bool send_transmit_finished); + +typedef struct { + // Called for every packet fragment. + packet_fragmented_cb fragmented; + + // Called for every completely reassembled packet. + packet_reassembled_cb reassembled; + + // Called when the fragmenter finishes sending all requested fragments, + // but the packet has not been entirely sent. + transmit_finished_cb transmit_finished; +} packet_fragmenter_callbacks_t; + +typedef struct packet_fragmenter_t { + // Initialize the fragmenter, specifying the |result_callbacks|. + void (*init)(const packet_fragmenter_callbacks_t *result_callbacks); + + // Release all resources associated with the fragmenter. + void (*cleanup)(void); + + // Check if Current fragmenter is ongoing + BT_HDR *(*fragment_current_packet)(void); + + // Fragments |packet| if necessary and hands off everything to the fragmented callback. + void (*fragment_and_dispatch)(BT_HDR *packet); + // If |packet| is a complete packet, forwards to the reassembled callback. Otherwise + // holds onto it until all fragments arrive, at which point the reassembled callback is called + // with the reassembled data. + void (*reassemble_and_dispatch)(BT_HDR *packet); +} packet_fragmenter_t; + +const packet_fragmenter_t *packet_fragmenter_get_interface(void); + +#endif /* _PACKET_FRAGMENTER_H_ */ diff --git a/lib/bt/host/bluedroid/hci/packet_fragmenter.c b/lib/bt/host/bluedroid/hci/packet_fragmenter.c new file mode 100644 index 00000000..78b6151e --- /dev/null +++ b/lib/bt/host/bluedroid/hci/packet_fragmenter.c @@ -0,0 +1,238 @@ +/****************************************************************************** + * + * Copyright (C) 2014 Google, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ +#include +#include "common/bt_trace.h" +#include "common/bt_defs.h" +#include "device/controller.h" +#include "hci/hci_internals.h" +#include "hci/hci_layer.h" +#include "hci/packet_fragmenter.h" + +#include "osi/hash_map.h" +#include "osi/hash_functions.h" +#include "common/bt_trace.h" + + +#define APPLY_CONTINUATION_FLAG(handle) (((handle) & 0xCFFF) | 0x1000) +#define APPLY_START_FLAG(handle) (((handle) & 0xCFFF) | 0x2000) +#define SUB_EVENT(event) ((event) & MSG_SUB_EVT_MASK) +#define GET_BOUNDARY_FLAG(handle) (((handle) >> 12) & 0x0003) + +#define HANDLE_MASK 0x0FFF +#define START_PACKET_BOUNDARY 2 +#define CONTINUATION_PACKET_BOUNDARY 1 +#define L2CAP_HEADER_SIZE 4 + +// TODO(zachoverflow): find good value for this +#define NUMBER_OF_BUCKETS 42 + +// Our interface and callbacks +static const packet_fragmenter_t interface; +static const controller_t *controller; +static const packet_fragmenter_callbacks_t *callbacks; +static hash_map_t *partial_packets; +static BT_HDR *current_fragment_packet; + +static void init(const packet_fragmenter_callbacks_t *result_callbacks) +{ + current_fragment_packet = NULL; + callbacks = result_callbacks; + partial_packets = hash_map_new(NUMBER_OF_BUCKETS, hash_function_naive, NULL, NULL, NULL); +} + +static void cleanup(void) +{ + if (partial_packets) { + hash_map_free(partial_packets); + } +} + +static BT_HDR *fragment_get_current_packet(void) +{ + return current_fragment_packet; +} + +static void fragment_and_dispatch(BT_HDR *packet) +{ + uint16_t continuation_handle; + uint16_t max_data_size, max_packet_size, remaining_length; + uint16_t event = packet->event & MSG_EVT_MASK; + uint8_t *stream = packet->data + packet->offset; + + assert(packet != NULL); + + // We only fragment ACL packets + if (event != MSG_STACK_TO_HC_HCI_ACL) { + callbacks->fragmented(packet, true); + return; + } + + max_data_size = + SUB_EVENT(packet->event) == LOCAL_BR_EDR_CONTROLLER_ID ? + controller->get_acl_data_size_classic() : + controller->get_acl_data_size_ble(); + + max_packet_size = max_data_size + HCI_ACL_PREAMBLE_SIZE; + if((packet->len > max_packet_size) && (packet->layer_specific == 0) && (event == MSG_STACK_TO_HC_HCI_ACL)) { + packet->event = MSG_HC_TO_STACK_L2C_SEG_XMIT; + current_fragment_packet = NULL; + callbacks->transmit_finished(packet, false); + return; + + } + remaining_length = packet->len; + STREAM_TO_UINT16(continuation_handle, stream); + continuation_handle = APPLY_CONTINUATION_FLAG(continuation_handle); + if (remaining_length > max_packet_size) { + current_fragment_packet = packet; + UINT16_TO_STREAM(stream, max_data_size); + packet->len = max_packet_size; + callbacks->fragmented(packet, false); + packet->offset += max_data_size; + remaining_length -= max_data_size; + packet->len = remaining_length; + + // Write the ACL header for the next fragment + stream = packet->data + packet->offset; + UINT16_TO_STREAM(stream, continuation_handle); + UINT16_TO_STREAM(stream, remaining_length - HCI_ACL_PREAMBLE_SIZE); + // Apparently L2CAP can set layer_specific to a max number of segments to transmit + if (packet->layer_specific) { + packet->layer_specific--; + if (packet->layer_specific == 0) { + packet->event = MSG_HC_TO_STACK_L2C_SEG_XMIT; + + /* The remain packet will send back to the l2cap layer when controller buffer is not enough + current_fragment_packet must be NULL, otherwise hci_host_thread_handler() will + connitue handle the remain packet. then the remain packet will be freed. + */ + + current_fragment_packet = NULL; + callbacks->transmit_finished(packet, false); + return; + } + } + } else { + current_fragment_packet = NULL; + callbacks->fragmented(packet, true); + } +} + +static void reassemble_and_dispatch(BT_HDR *packet) +{ + HCI_TRACE_DEBUG("reassemble_and_dispatch\n"); + + if ((packet->event & MSG_EVT_MASK) == MSG_HC_TO_STACK_HCI_ACL) { + uint8_t *stream = packet->data + packet->offset; + uint16_t handle; + uint16_t l2cap_length; + uint16_t acl_length __attribute__((unused)); + + STREAM_TO_UINT16(handle, stream); + STREAM_TO_UINT16(acl_length, stream); + STREAM_TO_UINT16(l2cap_length, stream); + + assert(acl_length == packet->len - HCI_ACL_PREAMBLE_SIZE); + + uint8_t boundary_flag = GET_BOUNDARY_FLAG(handle); + handle = handle & HANDLE_MASK; + + BT_HDR *partial_packet = (BT_HDR *)hash_map_get(partial_packets, (void *)(uintptr_t)handle); + + if (boundary_flag == START_PACKET_BOUNDARY) { + if (partial_packet) { + HCI_TRACE_WARNING("%s found unfinished packet for handle with start packet. Dropping old.\n", __func__); + hash_map_erase(partial_packets, (void *)(uintptr_t)handle); + osi_free(partial_packet); + } + + uint16_t full_length = l2cap_length + L2CAP_HEADER_SIZE + HCI_ACL_PREAMBLE_SIZE; + if (full_length <= packet->len) { + if (full_length < packet->len) { + HCI_TRACE_WARNING("%s found l2cap full length %d less than the hci length %d.\n", __func__, l2cap_length, packet->len); + } + + callbacks->reassembled(packet); + return; + } + partial_packet = (BT_HDR *)osi_calloc(full_length + sizeof(BT_HDR)); + partial_packet->event = packet->event; + partial_packet->len = full_length; + partial_packet->offset = packet->len; + + memcpy(partial_packet->data, packet->data + packet->offset, packet->len); + + // Update the ACL data size to indicate the full expected length + stream = partial_packet->data; + STREAM_SKIP_UINT16(stream); // skip the handle + UINT16_TO_STREAM(stream, full_length - HCI_ACL_PREAMBLE_SIZE); + + hash_map_set(partial_packets, (void *)(uintptr_t)handle, partial_packet); + // Free the old packet buffer, since we don't need it anymore + osi_free(packet); + } else { + if (!partial_packet) { + HCI_TRACE_ERROR("%s got continuation for unknown packet. Dropping it.\n", __func__); + osi_free(packet); + return; + } + + packet->offset += HCI_ACL_PREAMBLE_SIZE; // skip ACL preamble + packet->len -= HCI_ACL_PREAMBLE_SIZE; + uint16_t projected_offset = partial_packet->offset + packet->len; + if (projected_offset > partial_packet->len) { // len stores the expected length + HCI_TRACE_ERROR("%s got packet which would exceed expected length of %d. Truncating.\n", __func__, partial_packet->len); + packet->len = partial_packet->len - partial_packet->offset; + projected_offset = partial_packet->len; + } + + memcpy( + partial_packet->data + partial_packet->offset, + packet->data + packet->offset, + packet->len + ); + + // Free the old packet buffer, since we don't need it anymore + osi_free(packet); + partial_packet->offset = projected_offset; + + if (partial_packet->offset == partial_packet->len) { + hash_map_erase(partial_packets, (void *)(uintptr_t)handle); + partial_packet->offset = 0; + callbacks->reassembled(partial_packet); + } + } + } else { + callbacks->reassembled(packet); + } +} + +static const packet_fragmenter_t interface = { + init, + cleanup, + + fragment_get_current_packet, + fragment_and_dispatch, + reassemble_and_dispatch +}; + +const packet_fragmenter_t *packet_fragmenter_get_interface(void) +{ + controller = controller_get_interface(); + return &interface; +} diff --git a/lib/bt/host/bluedroid/main/bte_init.c b/lib/bt/host/bluedroid/main/bte_init.c new file mode 100644 index 00000000..80c4357b --- /dev/null +++ b/lib/bt/host/bluedroid/main/bte_init.c @@ -0,0 +1,503 @@ +/****************************************************************************** + * + * Copyright (C) 2000-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This module contains the routines that initialize the stack components. + * It must be called before the BTU task is started. + * + ******************************************************************************/ + +#include "common/bt_target.h" +#include + + +/* Stack Configuation Related Init Definaton + * TODO: Now Just Unmask these defination until stack layer is OK + */ + +#ifndef BTA_INCLUDED +#define BTA_INCLUDED FALSE +#endif + +#if (defined(RFCOMM_INCLUDED) && RFCOMM_INCLUDED == TRUE) +// Include initialization functions definitions +#include "stack/port_api.h" +#endif + +#if (defined(BNEP_INCLUDED) && BNEP_INCLUDED == TRUE) +#include "bnep_api.h" +#endif + +#if (defined(GAP_INCLUDED) && GAP_INCLUDED == TRUE) +#include "stack/gap_api.h" +#endif + +#if (defined(PAN_INCLUDED) && PAN_INCLUDED == TRUE) +#include "pan_api.h" +#endif + +#if (defined(HID_HOST_INCLUDED) && HID_HOST_INCLUDED == TRUE) +#include "stack/hidh_api.h" +#endif + +#if (defined(HID_DEV_INCLUDED) && HID_DEV_INCLUDED == TRUE) +#include "stack/hidd_api.h" +#endif + +#if (defined(AVRC_INCLUDED) && AVRC_INCLUDED == TRUE) +#include "stack/avrc_api.h" +#endif + +#if (defined(A2D_INCLUDED) && A2D_INCLUDED == TRUE) +#include "stack/a2d_api.h" +#endif + +#if (defined(AVDT_INCLUDED) && AVDT_INCLUDED == TRUE) +#include "avdt_int.h" +#endif + +#if (defined(AVCT_INCLUDED) && AVCT_INCLUDED == TRUE) +#include "avct_int.h" +#endif + +#if (defined(HID_HOST_INCLUDED) && HID_HOST_INCLUDED == TRUE) +#include "stack/hidh_api.h" +#endif + +#if (defined(MCA_INCLUDED) && MCA_INCLUDED == TRUE) +#include "mca_api.h" +#endif + +#if (defined(BLE_INCLUDED) && BLE_INCLUDED == TRUE) +#include "stack/gatt_api.h" +#if (defined(SMP_INCLUDED) && SMP_INCLUDED == TRUE) +#include "stack/smp_api.h" +#endif +#endif + +//BTA Modules +#if BTA_INCLUDED == TRUE && BTA_DYNAMIC_MEMORY == TRUE +#include "bta/bta_api.h" +#include "bta/bta_sys.h" +#include "osi/allocator.h" + +#if BTA_HF_INCLUDED == TRUE +#include "bta_hf_client_int.h" +#endif + +#if BTA_AG_INCLUDED == TRUE +#include "bta_ag_int.h" +#endif + +#if BTA_SDP_INCLUDED == TRUE +#include "bta_sdp_int.h" +#endif + +#if BTA_HS_INCLUDED == TRUE +#include "bta_hs_int.h" +#endif + +#include "bta_dm_int.h" + +#if BTA_AR_INCLUDED==TRUE +#include "bta_ar_int.h" +#endif +#if BTA_AV_INCLUDED==TRUE +#include "bta_av_int.h" +#endif + +#if BTA_HH_INCLUDED==TRUE +#include "bta_hh_int.h" +#endif + +#if BTA_HD_INCLUDED==TRUE +#include "bta_hd_int.h" +#endif + +#if BTA_JV_INCLUDED==TRUE +#include "bta_jv_int.h" +#endif + +#if BTA_HL_INCLUDED == TRUE +#include "bta_hl_int.h" +#endif + +#if BTA_GATT_INCLUDED == TRUE +#include "bta_gattc_int.h" +#include "bta_gatts_int.h" +#endif + +#if BTA_PAN_INCLUDED==TRUE +#include "bta_pan_int.h" +#endif + +#include "bta_sys_int.h" + +// control block for patch ram downloading +//#include "bta_prm_int.h" + +#endif // BTA_INCLUDED == TRUE && BTA_DYNAMIC_MEMORY == TRUE + + +/***************************************************************************** +** F U N C T I O N S * +******************************************************************************/ + +/***************************************************************************** +** +** Function BTE_DeinitStack +** +** Description Deinitialize control block memory for each component. +** +** Note: This API must be called +** after freeing the BTU Task. +** +** Returns void +** +******************************************************************************/ +void BTE_DeinitStack(void) +{ + //BTA Modules +#if (BTA_INCLUDED == TRUE && BTA_DYNAMIC_MEMORY == TRUE) +#if GATTS_INCLUDED == TRUE + if (bta_gatts_cb_ptr){ + osi_free(bta_gatts_cb_ptr); + bta_gatts_cb_ptr = NULL; + } +#endif +#if GATTC_INCLUDED==TRUE + if (bta_gattc_cb_ptr){ + osi_free(bta_gattc_cb_ptr); + bta_gattc_cb_ptr = NULL; + } +#endif +#if BTA_HD_INCLUDED==TRUE + if (bta_hd_cb_ptr){ + osi_free(bta_hd_cb_ptr); + bta_hd_cb_ptr = NULL; + } +#endif +#if BTA_HH_INCLUDED==TRUE + if (bta_hh_cb_ptr){ + osi_free(bta_hh_cb_ptr); + bta_hh_cb_ptr = NULL; + } +#endif +#if BTA_AV_INCLUDED==TRUE + if (bta_av_cb_ptr){ + osi_free(bta_av_cb_ptr); + bta_av_cb_ptr = NULL; + } + if (bta_av_sbc_ups_cb_ptr){ + osi_free(bta_av_sbc_ups_cb_ptr); + bta_av_sbc_ups_cb_ptr = NULL; + } +#endif +#if BTA_AR_INCLUDED==TRUE + if (bta_ar_cb_ptr){ + osi_free(bta_ar_cb_ptr); + bta_ar_cb_ptr = NULL; + } +#endif +#if SDP_INCLUDED == TRUE + if (g_disc_raw_data_buf){ + osi_free(g_disc_raw_data_buf); + g_disc_raw_data_buf = NULL; + } +#endif +#if BTA_SDP_INCLUDED == TRUE + if (bta_sdp_cb_ptr){ + osi_free(bta_sdp_cb_ptr); + bta_sdp_cb_ptr = NULL; + } +#endif +#if (defined BTA_JV_INCLUDED && BTA_JV_INCLUDED == TRUE) + if (bta_jv_cb_ptr){ + osi_free(bta_jv_cb_ptr); + bta_jv_cb_ptr = NULL; + } +#endif //JV +#if (defined BTA_HF_INCLUDED && BTA_HF_INCLUDED == TRUE) + if (bta_hf_client_cb_ptr){ + osi_free(bta_hf_client_cb_ptr); + bta_hf_client_cb_ptr = NULL; + } +#endif +#if (defined BTA_AG_INCLUDED && BTA_AG_INCLUDED == TRUE) + if (bta_ag_cb_ptr){ + osi_free(bta_ag_cb_ptr); + bta_ag_cb_ptr = NULL; + } +#endif + if (bta_dm_conn_srvcs_ptr){ + osi_free(bta_dm_conn_srvcs_ptr); + bta_dm_conn_srvcs_ptr = NULL; + } + if (bta_dm_di_cb_ptr){ + osi_free(bta_dm_di_cb_ptr); + bta_dm_di_cb_ptr = NULL; + } + if (bta_dm_search_cb_ptr){ + osi_free(bta_dm_search_cb_ptr); + bta_dm_search_cb_ptr = NULL; + } + if (bta_dm_cb_ptr){ + osi_free(bta_dm_cb_ptr); + bta_dm_cb_ptr = NULL; + } + if (bta_sys_cb_ptr){ + osi_free(bta_sys_cb_ptr); + bta_sys_cb_ptr = NULL; + } +#endif // BTA_INCLUDED == TRUE + +#if (defined(HID_DEV_INCLUDED) && HID_DEV_INCLUDED == TRUE) + HID_DevDeinit(); +#endif + +#if (defined(HID_HOST_INCLUDED) && HID_HOST_INCLUDED == TRUE) + HID_HostDeinit(); +#endif + +#if (defined(GAP_INCLUDED) && GAP_INCLUDED == TRUE) + GAP_Deinit(); +#endif + +#if (defined(AVCT_INCLUDED) && AVCT_INCLUDED == TRUE && AVCT_DYNAMIC_MEMORY == TRUE) + if (avct_cb_ptr){ + osi_free(avct_cb_ptr); + avct_cb_ptr = NULL; + } +#endif + +#if (defined(AVDT_INCLUDED) && AVDT_INCLUDED == TRUE && AVDT_DYNAMIC_MEMORY == TRUE) + if (avdt_cb_ptr){ + osi_free(avdt_cb_ptr); + avdt_cb_ptr = NULL; + } +#endif + +#if (defined(AVRC_INCLUDED) && AVRC_INCLUDED == TRUE) + AVRC_Deinit(); +#endif + +#if (defined(A2D_INCLUDED) && A2D_INCLUDED == TRUE) + A2D_Deinit(); +#endif + +#if (defined(RFCOMM_INCLUDED) && RFCOMM_INCLUDED == TRUE) + RFCOMM_Deinit(); +#endif +} + +/***************************************************************************** +** +** Function BTE_InitStack +** +** Description Initialize control block memory for each component. +** +** Note: The core stack components must be called +** before creating the BTU Task. The rest of the +** components can be initialized at a later time if desired +** as long as the component's init function is called +** before accessing any of its functions. +** +** Returns status +** +******************************************************************************/ +bt_status_t BTE_InitStack(void) +{ +#if (defined(RFCOMM_INCLUDED) && RFCOMM_INCLUDED == TRUE) + //Initialize the optional stack components + if (RFCOMM_Init() != BT_STATUS_SUCCESS) { + goto error_exit; + } +#endif + + //BNEP and its profiles +#if (defined(BNEP_INCLUDED) && BNEP_INCLUDED == TRUE) + BNEP_Init(); + +#if (defined(PAN_INCLUDED) && PAN_INCLUDED == TRUE) + PAN_Init(); +#endif // PAN +#endif // BNEP Included + + //AVDT and its profiles +#if (defined(A2D_INCLUDED) && A2D_INCLUDED == TRUE) + if (A2D_Init() != BT_STATUS_SUCCESS) { + goto error_exit; + } +#endif // AADP + +#if (defined(AVRC_INCLUDED) && AVRC_INCLUDED == TRUE) + if (AVRC_Init() != BT_STATUS_SUCCESS) { + goto error_exit; + } +#endif + +#if (defined(AVDT_INCLUDED) && AVDT_INCLUDED == TRUE && AVDT_DYNAMIC_MEMORY == TRUE) + if ((avdt_cb_ptr = (tAVDT_CB *)osi_malloc(sizeof(tAVDT_CB))) == NULL) { + goto error_exit; + } + memset((void *)avdt_cb_ptr, 0, sizeof(tAVDT_CB)); +#endif + +#if (defined(AVCT_INCLUDED) && AVCT_INCLUDED == TRUE && AVCT_DYNAMIC_MEMORY == TRUE) + if ((avct_cb_ptr = (tAVCT_CB *)osi_malloc(sizeof(tAVCT_CB))) == NULL) { + goto error_exit; + } + memset((void *)avct_cb_ptr, 0, sizeof(tAVCT_CB)); +#endif + +#if (defined(GAP_INCLUDED) && GAP_INCLUDED == TRUE) + if (GAP_Init() != BT_STATUS_SUCCESS) { + goto error_exit; + } +#endif + +#if (defined(HID_HOST_INCLUDED) && HID_HOST_INCLUDED == TRUE) + if (HID_HostInit() != HID_SUCCESS) { + goto error_exit; + } +#endif + +#if (defined(HID_DEV_INCLUDED) && HID_DEV_INCLUDED == TRUE) + if (HID_DevInit() != HID_SUCCESS) { + goto error_exit; + } +#endif + +#if (defined(MCA_INCLUDED) && MCA_INCLUDED == TRUE) + MCA_Init(); +#endif + + //BTA Modules +#if (BTA_INCLUDED == TRUE && BTA_DYNAMIC_MEMORY == TRUE) + if ((bta_sys_cb_ptr = (tBTA_SYS_CB *)osi_malloc(sizeof(tBTA_SYS_CB))) == NULL) { + goto error_exit; + } + if ((bta_dm_cb_ptr = (tBTA_DM_CB *)osi_malloc(sizeof(tBTA_DM_CB))) == NULL) { + goto error_exit; + } + if ((bta_dm_search_cb_ptr = (tBTA_DM_SEARCH_CB *)osi_malloc(sizeof(tBTA_DM_SEARCH_CB))) == NULL) { + goto error_exit; + } + if ((bta_dm_di_cb_ptr = (tBTA_DM_DI_CB *)osi_malloc(sizeof(tBTA_DM_DI_CB))) == NULL) { + goto error_exit; + } + if ((bta_dm_conn_srvcs_ptr = (tBTA_DM_CONNECTED_SRVCS *)osi_malloc(sizeof(tBTA_DM_CONNECTED_SRVCS))) == NULL) { + goto error_exit; + } + memset((void *)bta_sys_cb_ptr, 0, sizeof(tBTA_SYS_CB)); + memset((void *)bta_dm_cb_ptr, 0, sizeof(tBTA_DM_CB)); + memset((void *)bta_dm_search_cb_ptr, 0, sizeof(tBTA_DM_SEARCH_CB)); + memset((void *)bta_dm_di_cb_ptr, 0, sizeof(tBTA_DM_DI_CB)); + memset((void *)bta_dm_conn_srvcs_ptr, 0, sizeof(tBTA_DM_CONNECTED_SRVCS)); + //memset((void *)bta_prm_cb_ptr, 0, sizeof(tBTA_PRM_CB)); + +#if (defined BTA_HF_INCLUDED && BTA_HF_INCLUDED == TRUE) + if ((bta_hf_client_cb_ptr = (tBTA_HF_CLIENT_CB *)osi_malloc(sizeof(tBTA_HF_CLIENT_CB))) == NULL) { + goto error_exit; + } + memset((void *)bta_hf_client_cb_ptr, 0, sizeof(tBTA_HF_CLIENT_CB)); +#endif +#if (defined BTA_AG_INCLUDED && BTA_AG_INCLUDED == TRUE) + if ((bta_ag_cb_ptr = (tBTA_AG_CB *)osi_malloc(sizeof(tBTA_AG_CB))) == NULL) { + goto error_exit; + } + memset((void *)bta_ag_cb_ptr, 0, sizeof(tBTA_AG_CB)); +#endif +#if (defined BTA_JV_INCLUDED && BTA_JV_INCLUDED == TRUE) + if ((bta_jv_cb_ptr = (tBTA_JV_CB *)osi_malloc(sizeof(tBTA_JV_CB))) == NULL) { + goto error_exit; + } + memset((void *)bta_jv_cb_ptr, 0, sizeof(tBTA_JV_CB)); +#endif //JV +#if BTA_HS_INCLUDED == TRUE + memset((void *)bta_hs_cb_ptr, 0, sizeof(tBTA_HS_CB)); +#endif +#if BTA_SDP_INCLUDED == TRUE + if ((bta_sdp_cb_ptr = (tBTA_SDP_CB *)osi_malloc(sizeof(tBTA_SDP_CB))) == NULL) { + goto error_exit; + } + memset((void *)bta_sdp_cb_ptr, 0, sizeof(tBTA_SDP_CB)); +#endif +#if SDP_INCLUDED == TRUE + if ((g_disc_raw_data_buf = (UINT8 *)osi_malloc(MAX_DISC_RAW_DATA_BUF)) == NULL) { + goto error_exit; + } + memset((void *)g_disc_raw_data_buf, 0, MAX_DISC_RAW_DATA_BUF); +#endif +#if BTA_AR_INCLUDED==TRUE + if ((bta_ar_cb_ptr = (tBTA_AR_CB *)osi_malloc(sizeof(tBTA_AR_CB))) == NULL) { + goto error_exit; + } + memset((void *)bta_ar_cb_ptr, 0, sizeof(tBTA_AR_CB)); +#endif +#if BTA_AV_INCLUDED==TRUE + if ((bta_av_cb_ptr = (tBTA_AV_CB *)osi_malloc(sizeof(tBTA_AV_CB))) == NULL) { + goto error_exit; + } + memset((void *)bta_av_cb_ptr, 0, sizeof(tBTA_AV_CB)); + + if ((bta_av_sbc_ups_cb_ptr = (tBTA_AV_SBC_UPS_CB *)osi_malloc(sizeof(tBTA_AV_SBC_UPS_CB))) == NULL) { + goto error_exit; + } + memset((void *)bta_av_sbc_ups_cb_ptr, 0, sizeof(tBTA_AV_SBC_UPS_CB)); +#endif +#if BTA_HH_INCLUDED==TRUE + if ((bta_hh_cb_ptr = (tBTA_HH_CB *)osi_malloc(sizeof(tBTA_HH_CB))) == NULL) { + goto error_exit; + } + memset((void *)bta_hh_cb_ptr, 0, sizeof(tBTA_HH_CB)); +#endif +#if BTA_HD_INCLUDED==TRUE + if ((bta_hd_cb_ptr = (tBTA_HD_CB *)osi_malloc(sizeof(tBTA_HD_CB))) == NULL) { + goto error_exit; + } + memset((void *)bta_hd_cb_ptr, 0, sizeof(tBTA_HD_CB)); +#endif +#if BTA_HL_INCLUDED==TRUE + memset((void *)bta_hl_cb_ptr, 0, sizeof(tBTA_HL_CB)); +#endif +#if GATTC_INCLUDED==TRUE + if ((bta_gattc_cb_ptr = (tBTA_GATTC_CB *)osi_malloc(sizeof(tBTA_GATTC_CB))) == NULL) { + goto error_exit; + } + memset((void *)bta_gattc_cb_ptr, 0, sizeof(tBTA_GATTC_CB)); +#endif +#if GATTS_INCLUDED == TRUE + if ((bta_gatts_cb_ptr = (tBTA_GATTS_CB *)osi_malloc(sizeof(tBTA_GATTS_CB))) == NULL) { + goto error_exit; + } + memset((void *)bta_gatts_cb_ptr, 0, sizeof(tBTA_GATTS_CB)); +#endif +#if BTA_PAN_INCLUDED==TRUE + memset((void *)bta_pan_cb_ptr, 0, sizeof(tBTA_PAN_CB)); +#endif + +#endif // BTA_INCLUDED == TRUE + return BT_STATUS_SUCCESS; + +error_exit:; + LOG_ERROR("%s failed due to no memory", __func__); + BTE_DeinitStack(); + return BT_STATUS_NOMEM; +} diff --git a/lib/bt/host/bluedroid/main/bte_main.c b/lib/bt/host/bluedroid/main/bte_main.c new file mode 100644 index 00000000..c47bdc15 --- /dev/null +++ b/lib/bt/host/bluedroid/main/bte_main.c @@ -0,0 +1,247 @@ +/****************************************************************************** + * + * Copyright (C) 2009-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * Filename: bte_main.c + * + * Description: Contains BTE core stack initialization and shutdown code + * + ******************************************************************************/ + + +#include "common/bt_defs.h" +#include "common/bt_common_types.h" +#include "common/bte.h" +#include "stack/btu.h" +#include "common/bt_trace.h" +#include "osi/osi.h" +#include "osi/alarm.h" +#include "osi/hash_map.h" +#include "osi/hash_functions.h" +#include "device/controller.h" +#include "hci/hci_layer.h" +#include "bta/bta_api.h" + +/******************************************************************************* +** Constants & Macros +*******************************************************************************/ + +/****************************************************************************** +** Variables +******************************************************************************/ + +/******************************************************************************* +** Static variables +*******************************************************************************/ +static const hci_t *hci; + +/******************************************************************************* +** Static functions +*******************************************************************************/ +static void bte_main_disable(void); +static void bte_main_enable(void); + +/******************************************************************************* +** Externs +*******************************************************************************/ + +bluedroid_init_done_cb_t bluedroid_init_done_cb; + +extern void osi_mem_dbg_init(void); +/****************************************************************************** +** +** Function bte_main_boot_entry +** +** Description BTE MAIN API - Entry point for BTE chip/stack initialization +** +** Returns None +** +******************************************************************************/ +int bte_main_boot_entry(bluedroid_init_done_cb_t cb) +{ + hci = hci_layer_get_interface(); + if (!hci) { + APPL_TRACE_ERROR("%s could not get hci layer interface.\n", __func__); + return -2; + } + + bluedroid_init_done_cb = cb; + + osi_init(); + + //Enbale HCI + bte_main_enable(); + + return 0; +} + +/****************************************************************************** +** +** Function bte_main_shutdown +** +** Description BTE MAIN API - Shutdown code for BTE chip/stack +** +** Returns None +** +******************************************************************************/ +void bte_main_shutdown(void) +{ +#if (BLE_INCLUDED == TRUE) + BTA_VendorCleanup(); +#endif + bte_main_disable(); + + osi_deinit(); +} + +/****************************************************************************** +** +** Function bte_main_enable +** +** Description BTE MAIN API - Creates all the BTE tasks. Should be called +** part of the Bluetooth stack enable sequence +** +** Returns None +** +******************************************************************************/ +static void bte_main_enable(void) +{ + APPL_TRACE_DEBUG("Enable HCI\n"); + if (hci_start_up()) { + APPL_TRACE_ERROR("Start HCI Host Layer Failure\n"); + return; + } + + //Now Test Case Not Supported BTU + BTU_StartUp(); +} + +/****************************************************************************** +** +** Function bte_main_disable +** +** Description BTE MAIN API - Destroys all the BTE tasks. Should be called +** part of the Bluetooth stack disable sequence +** +** Returns None +** +******************************************************************************/ +static void bte_main_disable(void) +{ + /* + APPL_TRACE_DEBUG("%s", __FUNCTION__); + + module_shut_down(get_module(HCI_MODULE)); + module_shut_down(get_module(BTSNOOP_MODULE)); + */ + + hci_shut_down(); + + BTU_ShutDown(); +} + +/****************************************************************************** +** +** Function bte_main_postload_cfg +** +** Description BTE MAIN API - Stack postload configuration +** +** Returns None +** +******************************************************************************/ +void bte_main_postload_cfg(void) +{ + hci->do_postload(); +} + +#if (defined(HCILP_INCLUDED) && HCILP_INCLUDED == TRUE) +/****************************************************************************** +** +** Function bte_main_enable_lpm +** +** Description BTE MAIN API - Enable/Disable low power mode operation +** +** Returns None +** +******************************************************************************/ +void bte_main_enable_lpm(BOOLEAN enable) +{ + /*Enable Low power ?*/ + //hci->send_low_power_command(enable ? LPM_ENABLE : LPM_DISABLE); +} + +/****************************************************************************** +** +** Function bte_main_lpm_allow_bt_device_sleep +** +** Description BTE MAIN API - Allow BT controller goest to sleep +** +** Returns None +** +******************************************************************************/ +void bte_main_lpm_allow_bt_device_sleep(void) +{ + /**/ + //hci->send_low_power_command(LPM_WAKE_DEASSERT); +} + +/****************************************************************************** +** +** Function bte_main_lpm_wake_bt_device +** +** Description BTE MAIN API - Wake BT controller up if it is in sleep mode +** +** Returns None +** +******************************************************************************/ +void bte_main_lpm_wake_bt_device(void) +{ + //hci->send_low_power_command(LPM_WAKE_ASSERT); +} +#endif // HCILP_INCLUDED + +/****************************************************************************** +** +** Function bte_main_hci_send +** +** Description BTE MAIN API - This function is called by the upper stack to +** send an HCI message. The function displays a protocol trace +** message (if enabled), and then calls the 'transmit' function +** associated with the currently selected HCI transport +** +** Returns None +** +******************************************************************************/ +void bte_main_hci_send (BT_HDR *p_msg, UINT16 event) +{ + UINT16 sub_event = event & BT_SUB_EVT_MASK; /* local controller ID */ + + p_msg->event = event; + + //counter_add("main.tx.packets", 1); + //counter_add("main.tx.bytes", p_msg->len); + + if ((sub_event == LOCAL_BR_EDR_CONTROLLER_ID) || \ + (sub_event == LOCAL_BLE_CONTROLLER_ID)) { + hci->transmit_downward(event, p_msg); + } else { + //APPL_TRACE_ERROR("Invalid Controller ID. Discarding message."); + osi_free(p_msg); + } +} diff --git a/lib/bt/host/bluedroid/stack/a2dp/a2d_api.c b/lib/bt/host/bluedroid/stack/a2dp/a2d_api.c new file mode 100644 index 00000000..87ff14c3 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/a2dp/a2d_api.c @@ -0,0 +1,434 @@ +/****************************************************************************** + * + * Copyright (C) 2002-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * ommon API for the Advanced Audio Distribution Profile (A2DP) + * + ******************************************************************************/ +#include +#include "common/bt_target.h" +#include "stack/sdpdefs.h" +#include "stack/a2d_api.h" +#include "a2d_int.h" +#include "stack/avdt_api.h" +#include "osi/allocator.h" + +#if (defined(A2D_INCLUDED) && A2D_INCLUDED == TRUE) + +/***************************************************************************** +** Global data +*****************************************************************************/ +#if A2D_DYNAMIC_MEMORY == FALSE +tA2D_CB a2d_cb; +#else +tA2D_CB *a2d_cb_ptr; +#endif + + +/****************************************************************************** +** +** Function a2d_sdp_cback +** +** Description This is the SDP callback function used by A2D_FindService. +** This function will be executed by SDP when the service +** search is completed. If the search is successful, it +** finds the first record in the database that matches the +** UUID of the search. Then retrieves various parameters +** from the record. When it is finished it calls the +** application callback function. +** +** Returns Nothing. +** +******************************************************************************/ +static void a2d_sdp_cback(UINT16 status) +{ + tSDP_DISC_REC *p_rec = NULL; + tSDP_DISC_ATTR *p_attr; + BOOLEAN found = FALSE; + tA2D_Service a2d_svc; + tSDP_PROTOCOL_ELEM elem; + + A2D_TRACE_API("a2d_sdp_cback status: %d", status); + + if (status == SDP_SUCCESS) { + /* loop through all records we found */ + do { + /* get next record; if none found, we're done */ + if ((p_rec = SDP_FindServiceInDb(a2d_cb.find.p_db, + a2d_cb.find.service_uuid, p_rec)) == NULL) { + break; + } + memset(&a2d_svc, 0, sizeof(tA2D_Service)); + + /* get service name */ + if ((p_attr = SDP_FindAttributeInRec(p_rec, + ATTR_ID_SERVICE_NAME)) != NULL) { + a2d_svc.p_service_name = (char *) p_attr->attr_value.v.array; + a2d_svc.service_len = SDP_DISC_ATTR_LEN(p_attr->attr_len_type); + } + + /* get provider name */ + if ((p_attr = SDP_FindAttributeInRec(p_rec, + ATTR_ID_PROVIDER_NAME)) != NULL) { + a2d_svc.p_provider_name = (char *) p_attr->attr_value.v.array; + a2d_svc.provider_len = SDP_DISC_ATTR_LEN(p_attr->attr_len_type); + } + + /* get supported features */ + if ((p_attr = SDP_FindAttributeInRec(p_rec, + ATTR_ID_SUPPORTED_FEATURES)) != NULL) { + a2d_svc.features = p_attr->attr_value.v.u16; + } + + /* get AVDTP version */ + if (SDP_FindProtocolListElemInRec(p_rec, UUID_PROTOCOL_AVDTP, &elem)) { + a2d_svc.avdt_version = elem.params[0]; + A2D_TRACE_DEBUG("avdt_version: 0x%x", a2d_svc.avdt_version); + } + + /* we've got everything, we're done */ + found = TRUE; + break; + + } while (TRUE); + } + + a2d_cb.find.service_uuid = 0; + /* return info from sdp record in app callback function */ + if (a2d_cb.find.p_cback != NULL) { + (*a2d_cb.find.p_cback)(found, &a2d_svc); + } + + return; +} + +/******************************************************************************* +** +** Function a2d_set_avdt_sdp_ver +** +** Description This function allows the script wrapper to change the +** avdt version of a2dp. +** +** Returns None +** +*******************************************************************************/ +void a2d_set_avdt_sdp_ver(UINT16 avdt_sdp_ver) +{ + a2d_cb.avdt_sdp_ver = avdt_sdp_ver; +} + +/******************************************************************************* + * + * Function a2d_set_a2dp_sdp_ver + * + * Description This function allows the script wrapper to change the + * a2dp version + * + * Returns None + * + ******************************************************************************/ +void a2d_set_a2dp_sdp_ver(UINT16 a2dp_sdp_ver) +{ + a2d_cb.a2dp_sdp_ver = a2dp_sdp_ver; +} + +/****************************************************************************** +** +** Function A2D_AddRecord +** +** Description This function is called by a server application to add +** SRC or SNK information to an SDP record. Prior to +** calling this function the application must call +** SDP_CreateRecord() to create an SDP record. +** +** Input Parameters: +** service_uuid: Indicates SRC or SNK. +** +** p_service_name: Pointer to a null-terminated character +** string containing the service name. +** +** p_provider_name: Pointer to a null-terminated character +** string containing the provider name. +** +** features: Profile supported features. +** +** sdp_handle: SDP handle returned by SDP_CreateRecord(). +** +** Output Parameters: +** None. +** +** Returns A2D_SUCCESS if function execution succeeded, +** A2D_INVALID_PARAMS if bad parameters are given. +** A2D_FAIL if function execution failed. +** +******************************************************************************/ +tA2D_STATUS A2D_AddRecord(UINT16 service_uuid, char *p_service_name, char *p_provider_name, + UINT16 features, UINT32 sdp_handle) +{ + UINT16 browse_list[1]; + BOOLEAN result = TRUE; + UINT8 temp[8]; + UINT8 *p; + tSDP_PROTOCOL_ELEM proto_list [A2D_NUM_PROTO_ELEMS]; + + A2D_TRACE_API("A2D_AddRecord uuid: %x", service_uuid); + + if ( (sdp_handle == 0) || + (service_uuid != UUID_SERVCLASS_AUDIO_SOURCE && service_uuid != UUID_SERVCLASS_AUDIO_SINK) ) { + return A2D_INVALID_PARAMS; + } + + /* add service class id list */ + result &= SDP_AddServiceClassIdList(sdp_handle, 1, &service_uuid); + + memset((void *) proto_list, 0 , A2D_NUM_PROTO_ELEMS * sizeof(tSDP_PROTOCOL_ELEM)); + + /* add protocol descriptor list */ + proto_list[0].protocol_uuid = UUID_PROTOCOL_L2CAP; + proto_list[0].num_params = 1; + proto_list[0].params[0] = AVDT_PSM; + proto_list[1].protocol_uuid = UUID_PROTOCOL_AVDTP; + proto_list[1].num_params = 1; + proto_list[1].params[0] = a2d_cb.avdt_sdp_ver; + + result &= SDP_AddProtocolList(sdp_handle, A2D_NUM_PROTO_ELEMS, proto_list); + + /* add profile descriptor list */ + result &= SDP_AddProfileDescriptorList(sdp_handle, UUID_SERVCLASS_ADV_AUDIO_DISTRIBUTION, a2d_cb.a2dp_sdp_ver); + + /* add supported feature */ + if (features != 0) { + p = temp; + UINT16_TO_BE_STREAM(p, features); + result &= SDP_AddAttribute(sdp_handle, ATTR_ID_SUPPORTED_FEATURES, UINT_DESC_TYPE, + (UINT32)2, (UINT8 *)temp); + } + + /* add provider name */ + if (p_provider_name != NULL) { + result &= SDP_AddAttribute(sdp_handle, ATTR_ID_PROVIDER_NAME, TEXT_STR_DESC_TYPE, + (UINT32)(strlen(p_provider_name) + 1), (UINT8 *) p_provider_name); + } + + /* add service name */ + if (p_service_name != NULL) { + result &= SDP_AddAttribute(sdp_handle, ATTR_ID_SERVICE_NAME, TEXT_STR_DESC_TYPE, + (UINT32)(strlen(p_service_name) + 1), (UINT8 *) p_service_name); + } + + /* add browse group list */ + browse_list[0] = UUID_SERVCLASS_PUBLIC_BROWSE_GROUP; + result &= SDP_AddUuidSequence(sdp_handle, ATTR_ID_BROWSE_GROUP_LIST, 1, browse_list); + + + return (result ? A2D_SUCCESS : A2D_FAIL); +} + +/****************************************************************************** +** +** Function A2D_FindService +** +** Description This function is called by a client application to +** perform service discovery and retrieve SRC or SNK SDP +** record information from a server. Information is +** returned for the first service record found on the +** server that matches the service UUID. The callback +** function will be executed when service discovery is +** complete. There can only be one outstanding call to +** A2D_FindService() at a time; the application must wait +** for the callback before it makes another call to +** the function. +** +** Input Parameters: +** service_uuid: Indicates SRC or SNK. +** +** bd_addr: BD address of the peer device. +** +** p_db: Pointer to the information to initialize +** the discovery database. +** +** p_cback: Pointer to the A2D_FindService() +** callback function. +** +** Output Parameters: +** None. +** +** Returns A2D_SUCCESS if function execution succeeded, +** A2D_INVALID_PARAMS if bad parameters are given. +** A2D_BUSY if discovery is already in progress. +** A2D_FAIL if function execution failed. +** +******************************************************************************/ +tA2D_STATUS A2D_FindService(UINT16 service_uuid, BD_ADDR bd_addr, + tA2D_SDP_DB_PARAMS *p_db, tA2D_FIND_CBACK *p_cback) +{ + tSDP_UUID uuid_list; + BOOLEAN result = TRUE; + UINT16 a2d_attr_list[] = {ATTR_ID_SERVICE_CLASS_ID_LIST, /* update A2D_NUM_ATTR, if changed */ + ATTR_ID_BT_PROFILE_DESC_LIST, + ATTR_ID_SUPPORTED_FEATURES, + ATTR_ID_SERVICE_NAME, + ATTR_ID_PROTOCOL_DESC_LIST, + ATTR_ID_PROVIDER_NAME + }; + + A2D_TRACE_API("A2D_FindService uuid: %x", service_uuid); + if ( (service_uuid != UUID_SERVCLASS_AUDIO_SOURCE && service_uuid != UUID_SERVCLASS_AUDIO_SINK) || + p_db == NULL || p_db->p_db == NULL || p_cback == NULL) { + return A2D_INVALID_PARAMS; + } + + if ( a2d_cb.find.service_uuid == UUID_SERVCLASS_AUDIO_SOURCE || + a2d_cb.find.service_uuid == UUID_SERVCLASS_AUDIO_SINK) { + return A2D_BUSY; + } + + /* set up discovery database */ + uuid_list.len = LEN_UUID_16; + uuid_list.uu.uuid16 = service_uuid; + + if (p_db->p_attrs == NULL || p_db->num_attr == 0) { + p_db->p_attrs = a2d_attr_list; + p_db->num_attr = A2D_NUM_ATTR; + } + + result = SDP_InitDiscoveryDb(p_db->p_db, p_db->db_len, 1, &uuid_list, p_db->num_attr, + p_db->p_attrs); + + if (result == TRUE) { + /* store service_uuid and discovery db pointer */ + a2d_cb.find.p_db = p_db->p_db; + a2d_cb.find.service_uuid = service_uuid; + a2d_cb.find.p_cback = p_cback; + + /* perform service search */ + result = SDP_ServiceSearchAttributeRequest(bd_addr, p_db->p_db, a2d_sdp_cback); + if (FALSE == result) { + a2d_cb.find.service_uuid = 0; + } + } + + return (result ? A2D_SUCCESS : A2D_FAIL); +} + +/****************************************************************************** +** +** Function A2D_SetTraceLevel +** +** Description Sets the trace level for A2D. If 0xff is passed, the +** current trace level is returned. +** +** Input Parameters: +** new_level: The level to set the A2D tracing to: +** 0xff-returns the current setting. +** 0-turns off tracing. +** >= 1-Errors. +** >= 2-Warnings. +** >= 3-APIs. +** >= 4-Events. +** >= 5-Debug. +** +** Returns The new trace level or current trace level if +** the input parameter is 0xff. +** +******************************************************************************/ +UINT8 A2D_SetTraceLevel (UINT8 new_level) +{ + if (new_level != 0xFF) { + a2d_cb.trace_level = new_level; + } + + return (a2d_cb.trace_level); +} + +/****************************************************************************** +** Function A2D_BitsSet +** +** Description Check the given num for the number of bits set +** Returns A2D_SET_ONE_BIT, if one and only one bit is set +** A2D_SET_ZERO_BIT, if all bits clear +** A2D_SET_MULTL_BIT, if multiple bits are set +******************************************************************************/ +UINT8 A2D_BitsSet(UINT8 num) +{ + UINT8 count; + UINT8 res; + if (num == 0) { + res = A2D_SET_ZERO_BIT; + } else { + count = (num & (num - 1)); + res = ((count == 0) ? A2D_SET_ONE_BIT : A2D_SET_MULTL_BIT); + } + return res; +} + +/******************************************************************************* +** +** Function A2D_Init +** +** Description This function is called to initialize the control block +** for this layer. It must be called before accessing any +** other API functions for this layer. It is typically called +** once during the start up of the stack. +** +** Returns status +** +*******************************************************************************/ +bt_status_t A2D_Init(void) +{ +#if (A2D_DYNAMIC_MEMORY) + a2d_cb_ptr = (tA2D_CB *)osi_malloc(sizeof(tA2D_CB)); + if (!a2d_cb_ptr) { + return BT_STATUS_NOMEM; + } +#endif /* #if (A2D_DYNAMIC_MEMORY) */ + memset(&a2d_cb, 0, sizeof(tA2D_CB)); + + a2d_cb.avdt_sdp_ver = AVDT_VERSION; + a2d_cb.a2dp_sdp_ver = A2D_VERSION; + +#if defined(A2D_INITIAL_TRACE_LEVEL) + a2d_cb.trace_level = A2D_INITIAL_TRACE_LEVEL; +#else + a2d_cb.trace_level = BT_TRACE_LEVEL_NONE; +#endif + return BT_STATUS_SUCCESS; +} + +/******************************************************************************* +** +** Function A2D_Deinit +** +** Description This function is called to deinitialize the control block +** for this layer. +** +** Returns void +** +*******************************************************************************/ +void A2D_Deinit(void) +{ +#if (A2D_DYNAMIC_MEMORY) + if (a2d_cb_ptr) { + osi_free(a2d_cb_ptr); + a2d_cb_ptr = NULL; + } +#endif /* #if (A2D_DYNAMIC_MEMORY) */ +} + +#endif /* #if (defined(A2D_INCLUDED) && A2D_INCLUDED == TRUE) */ diff --git a/lib/bt/host/bluedroid/stack/a2dp/a2d_sbc.c b/lib/bt/host/bluedroid/stack/a2dp/a2d_sbc.c new file mode 100644 index 00000000..d02dd384 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/a2dp/a2d_sbc.c @@ -0,0 +1,228 @@ +/****************************************************************************** + * + * Copyright (C) 2002-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * Utility functions to help build and parse SBC Codec Information Element + * and Media Payload. + * + ******************************************************************************/ + +#include "common/bt_target.h" + +#include +#include "stack/a2d_api.h" +#include "a2d_int.h" +#include "stack/a2d_sbc.h" +#include "common/bt_defs.h" + +#if (defined(A2D_INCLUDED) && A2D_INCLUDED == TRUE) + +/****************************************************************************** +** +** Function A2D_BldSbcInfo +** +** Description This function is called by an application to build +** the SBC Media Codec Capabilities byte sequence +** beginning from the LOSC octet. +** Input Parameters: +** media_type: Indicates Audio, or Multimedia. +** +** p_ie: The SBC Codec Information Element information. +** +** Output Parameters: +** p_result: the resulting codec info byte sequence. +** +** Returns A2D_SUCCESS if function execution succeeded. +** Error status code, otherwise. +******************************************************************************/ +tA2D_STATUS A2D_BldSbcInfo(UINT8 media_type, tA2D_SBC_CIE *p_ie, UINT8 *p_result) +{ + tA2D_STATUS status; + + if ( p_ie == NULL || p_result == NULL || + (p_ie->samp_freq & ~A2D_SBC_IE_SAMP_FREQ_MSK) || + (p_ie->ch_mode & ~A2D_SBC_IE_CH_MD_MSK) || + (p_ie->block_len & ~A2D_SBC_IE_BLOCKS_MSK) || + (p_ie->num_subbands & ~A2D_SBC_IE_SUBBAND_MSK) || + (p_ie->alloc_mthd & ~A2D_SBC_IE_ALLOC_MD_MSK) || + (p_ie->max_bitpool < p_ie->min_bitpool) || + (p_ie->max_bitpool < A2D_SBC_IE_MIN_BITPOOL) || + (p_ie->max_bitpool > A2D_SBC_IE_MAX_BITPOOL) || + (p_ie->min_bitpool < A2D_SBC_IE_MIN_BITPOOL) || + (p_ie->min_bitpool > A2D_SBC_IE_MAX_BITPOOL) ) { + /* if any unused bit is set */ + status = A2D_INVALID_PARAMS; + } else { + status = A2D_SUCCESS; + *p_result++ = A2D_SBC_INFO_LEN; + *p_result++ = media_type; + *p_result++ = A2D_MEDIA_CT_SBC; + + /* Media Codec Specific Information Element */ + *p_result++ = p_ie->samp_freq | p_ie->ch_mode; + + *p_result++ = p_ie->block_len | p_ie->num_subbands | p_ie->alloc_mthd; + + *p_result++ = p_ie->min_bitpool; + *p_result = p_ie->max_bitpool; + } + return status; +} + +/****************************************************************************** +** +** Function A2D_ParsSbcInfo +** +** Description This function is called by an application to parse +** the SBC Media Codec Capabilities byte sequence +** beginning from the LOSC octet. +** Input Parameters: +** p_info: the byte sequence to parse. +** +** for_caps: TRUE, if the byte sequence is for get capabilities response. +** +** Output Parameters: +** p_ie: The SBC Codec Information Element information. +** +** Returns A2D_SUCCESS if function execution succeeded. +** Error status code, otherwise. +******************************************************************************/ +tA2D_STATUS A2D_ParsSbcInfo(tA2D_SBC_CIE *p_ie, UINT8 *p_info, BOOLEAN for_caps) +{ + tA2D_STATUS status = A2D_SUCCESS; + UINT8 losc; + + if ( p_ie == NULL || p_info == NULL) { + status = A2D_INVALID_PARAMS; + } else { + losc = *p_info++; + p_info++; + /* If the function is called for the wrong Media Type or Media Codec Type */ + if (losc != A2D_SBC_INFO_LEN || *p_info != A2D_MEDIA_CT_SBC) { + status = A2D_WRONG_CODEC; + } else { + p_info++; + p_ie->samp_freq = *p_info & A2D_SBC_IE_SAMP_FREQ_MSK; + p_ie->ch_mode = *p_info & A2D_SBC_IE_CH_MD_MSK; + p_info++; + p_ie->block_len = *p_info & A2D_SBC_IE_BLOCKS_MSK; + p_ie->num_subbands = *p_info & A2D_SBC_IE_SUBBAND_MSK; + p_ie->alloc_mthd = *p_info & A2D_SBC_IE_ALLOC_MD_MSK; + p_info++; + p_ie->min_bitpool = *p_info++; + p_ie->max_bitpool = *p_info; + if (p_ie->min_bitpool < A2D_SBC_IE_MIN_BITPOOL || p_ie->min_bitpool > A2D_SBC_IE_MAX_BITPOOL ) { + status = A2D_BAD_MIN_BITPOOL; + } + + if (p_ie->max_bitpool < A2D_SBC_IE_MIN_BITPOOL || p_ie->max_bitpool > A2D_SBC_IE_MAX_BITPOOL || + p_ie->max_bitpool < p_ie->min_bitpool) { + status = A2D_BAD_MAX_BITPOOL; + } + + if (for_caps == FALSE) { + if (A2D_BitsSet(p_ie->samp_freq) != A2D_SET_ONE_BIT) { + status = A2D_BAD_SAMP_FREQ; + } else if (A2D_BitsSet(p_ie->ch_mode) != A2D_SET_ONE_BIT) { + status = A2D_BAD_CH_MODE; + } else if (A2D_BitsSet(p_ie->block_len) != A2D_SET_ONE_BIT) { + status = A2D_BAD_BLOCK_LEN; + } else if (A2D_BitsSet(p_ie->num_subbands) != A2D_SET_ONE_BIT) { + status = A2D_BAD_SUBBANDS; + } else if (A2D_BitsSet(p_ie->alloc_mthd) != A2D_SET_ONE_BIT) { + status = A2D_BAD_ALLOC_MTHD; + } + } + } + } + return status; +} + +/****************************************************************************** +** +** Function A2D_BldSbcMplHdr +** +** Description This function is called by an application to parse +** the SBC Media Payload header. +** Input Parameters: +** frag: 1, if fragmented. 0, otherwise. +** +** start: 1, if the starting packet of a fragmented frame. +** +** last: 1, if the last packet of a fragmented frame. +** +** num: If frag is 1, this is the number of remaining fragments +** (including this fragment) of this frame. +** If frag is 0, this is the number of frames in this packet. +** +** Output Parameters: +** p_dst: the resulting media payload header byte sequence. +** +** Returns void. +******************************************************************************/ +void A2D_BldSbcMplHdr(UINT8 *p_dst, BOOLEAN frag, BOOLEAN start, BOOLEAN last, UINT8 num) +{ + if (p_dst) { + *p_dst = 0; + if (frag) { + *p_dst |= A2D_SBC_HDR_F_MSK; + } + if (start) { + *p_dst |= A2D_SBC_HDR_S_MSK; + } + if (last) { + *p_dst |= A2D_SBC_HDR_L_MSK; + } + *p_dst |= (A2D_SBC_HDR_NUM_MSK & num); + } +} + +/****************************************************************************** +** +** Function A2D_ParsSbcMplHdr +** +** Description This function is called by an application to parse +** the SBC Media Payload header. +** Input Parameters: +** p_src: the byte sequence to parse.. +** +** Output Parameters: +** frag: 1, if fragmented. 0, otherwise. +** +** start: 1, if the starting packet of a fragmented frame. +** +** last: 1, if the last packet of a fragmented frame. +** +** num: If frag is 1, this is the number of remaining fragments +** (including this fragment) of this frame. +** If frag is 0, this is the number of frames in this packet. +** +** Returns void. +******************************************************************************/ +void A2D_ParsSbcMplHdr(UINT8 *p_src, BOOLEAN *p_frag, BOOLEAN *p_start, BOOLEAN *p_last, UINT8 *p_num) +{ + if (p_src && p_frag && p_start && p_last && p_num) { + *p_frag = (*p_src & A2D_SBC_HDR_F_MSK) ? TRUE : FALSE; + *p_start = (*p_src & A2D_SBC_HDR_S_MSK) ? TRUE : FALSE; + *p_last = (*p_src & A2D_SBC_HDR_L_MSK) ? TRUE : FALSE; + *p_num = (*p_src & A2D_SBC_HDR_NUM_MSK); + } +} + +#endif /* #if (defined(A2D_INCLUDED) && A2D_INCLUDED == TRUE) */ diff --git a/lib/bt/host/bluedroid/stack/a2dp/include/a2d_int.h b/lib/bt/host/bluedroid/stack/a2dp/include/a2d_int.h new file mode 100644 index 00000000..e1ec7b44 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/a2dp/include/a2d_int.h @@ -0,0 +1,77 @@ +/****************************************************************************** + * + * Copyright (C) 2002-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * 2DP internal header file + * + ******************************************************************************/ +#ifndef A2D_INT_H +#define A2D_INT_H + +#include "stack/a2d_api.h" +#if (A2D_INCLUDED == TRUE) +/***************************************************************************** +** Constants +*****************************************************************************/ + +/* Number of attributes in A2D SDP record. */ +#define A2D_NUM_ATTR 6 + +/* Number of protocol elements in protocol element list. */ +#define A2D_NUM_PROTO_ELEMS 2 + +/***************************************************************************** +** Type definitions +*****************************************************************************/ + +/* Control block used by A2D_FindService(). */ +typedef struct { + tA2D_FIND_CBACK *p_cback; /* pointer to application callback */ + tSDP_DISCOVERY_DB *p_db; /* pointer to discovery database */ + UINT16 service_uuid; /* service UUID of search */ +} tA2D_FIND_CB; + +typedef struct { + tA2D_FIND_CB find; /* find service control block */ + UINT8 trace_level; + UINT16 avdt_sdp_ver; /* AVDTP version */ + UINT16 a2dp_sdp_ver; /* A2DP version */ +} tA2D_CB; + + +#ifdef __cplusplus +extern "C" +{ +#endif + +/****************************************************************************** +** Main Control Block +*******************************************************************************/ +#if A2D_DYNAMIC_MEMORY == FALSE +extern tA2D_CB a2d_cb; +#else +extern tA2D_CB *a2d_cb_ptr; +#define a2d_cb (*a2d_cb_ptr) +#endif + +#ifdef __cplusplus +} +#endif +#endif ///A2D_INCLUDED == TRUE +#endif /* A2D_INT_H */ diff --git a/lib/bt/host/bluedroid/stack/avct/avct_api.c b/lib/bt/host/bluedroid/stack/avct/avct_api.c new file mode 100644 index 00000000..7826eaa5 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/avct/avct_api.c @@ -0,0 +1,444 @@ +/****************************************************************************** + * + * Copyright (C) 2003-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This module contains API of the audio/video control transport protocol. + * + ******************************************************************************/ + +#include +#include "stack/bt_types.h" +#include "common/bt_target.h" +#include "common/bt_defs.h" +#include "stack/l2c_api.h" +#include "stack/l2cdefs.h" +#include "stack/btm_api.h" +#include "stack/avct_api.h" +#include "avct_int.h" +#include "osi/allocator.h" + +#if (defined(AVCT_INCLUDED) && AVCT_INCLUDED == TRUE) + +/* Control block for AVCT */ +#if AVCT_DYNAMIC_MEMORY == FALSE +tAVCT_CB avct_cb; +#else +tAVCT_CB *avct_cb_ptr; +#endif + +/******************************************************************************* +** +** Function AVCT_Register +** +** Description This is the system level registration function for the +** AVCTP protocol. This function initializes AVCTP and +** prepares the protocol stack for its use. This function +** must be called once by the system or platform using AVCTP +** before the other functions of the API an be used. +** +** +** Returns void +** +*******************************************************************************/ +void AVCT_Register(UINT16 mtu, UINT16 mtu_br, UINT8 sec_mask) +{ + UNUSED(mtu_br); + + AVCT_TRACE_API("AVCT_Register"); + + /* register PSM with L2CAP */ + L2CA_Register(AVCT_PSM, (tL2CAP_APPL_INFO *) &avct_l2c_appl); + + /* set security level */ + BTM_SetSecurityLevel(TRUE, "", BTM_SEC_SERVICE_AVCTP, sec_mask, AVCT_PSM, 0, 0); + BTM_SetSecurityLevel(FALSE, "", BTM_SEC_SERVICE_AVCTP, sec_mask, AVCT_PSM, 0, 0); + + /* initialize AVCTP data structures */ + memset(&avct_cb, 0, sizeof(tAVCT_CB)); + +#if (AVCT_BROWSE_INCLUDED == TRUE) + /* Include the browsing channel which uses eFCR */ + L2CA_Register(AVCT_BR_PSM, (tL2CAP_APPL_INFO *) &avct_l2c_br_appl); + + BTM_SetSecurityLevel(TRUE, "", BTM_SEC_SERVICE_AVCTP_BROWSE, sec_mask, AVCT_BR_PSM, 0, 0); + BTM_SetSecurityLevel(FALSE, "", BTM_SEC_SERVICE_AVCTP_BROWSE, sec_mask, AVCT_BR_PSM, 0, 0); + + if (mtu_br < AVCT_MIN_BROWSE_MTU) { + mtu_br = AVCT_MIN_BROWSE_MTU; + } + avct_cb.mtu_br = mtu_br; +#endif + +#if defined(AVCT_INITIAL_TRACE_LEVEL) + avct_cb.trace_level = AVCT_INITIAL_TRACE_LEVEL; +#else + avct_cb.trace_level = BT_TRACE_LEVEL_NONE; +#endif + + if (mtu < AVCT_MIN_CONTROL_MTU) { + mtu = AVCT_MIN_CONTROL_MTU; + } + /* store mtu */ + avct_cb.mtu = mtu; +} + +/******************************************************************************* +** +** Function AVCT_Deregister +** +** Description This function is called to deregister use AVCTP protocol. +** It is called when AVCTP is no longer being used by any +** application in the system. Before this function can be +** called, all connections must be removed with +** AVCT_RemoveConn(). +** +** +** Returns void +** +*******************************************************************************/ +void AVCT_Deregister(void) +{ + AVCT_TRACE_API("AVCT_Deregister"); + + /* deregister PSM with L2CAP */ + L2CA_Deregister(AVCT_PSM); +} + +/******************************************************************************* +** +** Function AVCT_CreateConn +** +** Description Create an AVCTP connection. There are two types of +** connections, initiator and acceptor, as determined by +** the p_cc->role parameter. When this function is called to +** create an initiator connection, an AVCTP connection to +** the peer device is initiated if one does not already exist. +** If an acceptor connection is created, the connection waits +** passively for an incoming AVCTP connection from a peer device. +** +** +** Returns AVCT_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +UINT16 AVCT_CreateConn(UINT8 *p_handle, tAVCT_CC *p_cc, BD_ADDR peer_addr) +{ + UINT16 result = AVCT_SUCCESS; + tAVCT_CCB *p_ccb; + tAVCT_LCB *p_lcb; + + AVCT_TRACE_API("AVCT_CreateConn: %d, control:%d", p_cc->role, p_cc->control); + + /* Allocate ccb; if no ccbs, return failure */ + if ((p_ccb = avct_ccb_alloc(p_cc)) == NULL) { + result = AVCT_NO_RESOURCES; + } else { + /* get handle */ + *p_handle = avct_ccb_to_idx(p_ccb); + + /* if initiator connection */ + if (p_cc->role == AVCT_INT) { + /* find link; if none allocate a new one */ + if ((p_lcb = avct_lcb_by_bd(peer_addr)) == NULL) { + if ((p_lcb = avct_lcb_alloc(peer_addr)) == NULL) { + /* no link resources; free ccb as well */ + avct_ccb_dealloc(p_ccb, AVCT_NO_EVT, 0, NULL); + result = AVCT_NO_RESOURCES; + } + } + /* check if PID already in use */ + else if (avct_lcb_has_pid(p_lcb, p_cc->pid)) { + avct_ccb_dealloc(p_ccb, AVCT_NO_EVT, 0, NULL); + result = AVCT_PID_IN_USE; + } + + if (result == AVCT_SUCCESS) { + /* bind lcb to ccb */ + p_ccb->p_lcb = p_lcb; + AVCT_TRACE_DEBUG("ch_state: %d", p_lcb->ch_state); + avct_lcb_event(p_lcb, AVCT_LCB_UL_BIND_EVT, (tAVCT_LCB_EVT *) &p_ccb); + } + } + } + return result; +} + +/******************************************************************************* +** +** Function AVCT_RemoveConn +** +** Description Remove an AVCTP connection. This function is called when +** the application is no longer using a connection. If this +** is the last connection to a peer the L2CAP channel for AVCTP +** will be closed. +** +** +** Returns AVCT_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +UINT16 AVCT_RemoveConn(UINT8 handle) +{ + UINT16 result = AVCT_SUCCESS; + tAVCT_CCB *p_ccb; + + AVCT_TRACE_API("AVCT_RemoveConn"); + + /* map handle to ccb */ + if ((p_ccb = avct_ccb_by_idx(handle)) == NULL) { + result = AVCT_BAD_HANDLE; + } + /* if connection not bound to lcb, dealloc */ + else if (p_ccb->p_lcb == NULL) { + avct_ccb_dealloc(p_ccb, AVCT_NO_EVT, 0, NULL); + } + /* send unbind event to lcb */ + else { + avct_lcb_event(p_ccb->p_lcb, AVCT_LCB_UL_UNBIND_EVT, (tAVCT_LCB_EVT *) &p_ccb); + } + return result; +} + +/******************************************************************************* +** +** Function AVCT_CreateBrowse +** +** Description Create an AVCTP Browse channel. There are two types of +** connections, initiator and acceptor, as determined by +** the role parameter. When this function is called to +** create an initiator connection, the Browse channel to +** the peer device is initiated if one does not already exist. +** If an acceptor connection is created, the connection waits +** passively for an incoming AVCTP connection from a peer device. +** +** +** Returns AVCT_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +UINT16 AVCT_CreateBrowse (UINT8 handle, UINT8 role) +{ +#if (AVCT_BROWSE_INCLUDED == TRUE) + UINT16 result = AVCT_SUCCESS; + tAVCT_CCB *p_ccb; + tAVCT_BCB *p_bcb; + int index; + + AVCT_TRACE_API("AVCT_CreateBrowse: %d", role); + + /* map handle to ccb */ + if ((p_ccb = avct_ccb_by_idx(handle)) == NULL) { + return AVCT_BAD_HANDLE; + } else { + /* mark this CCB as supporting browsing channel */ + if ((p_ccb->allocated & AVCT_ALOC_BCB) == 0) { + p_ccb->allocated |= AVCT_ALOC_BCB; + } + } + + /* if initiator connection */ + if (role == AVCT_INT) { + /* the link control block must exist before this function is called as INT. */ + if ((p_ccb->p_lcb == NULL) || (p_ccb->p_lcb->allocated == 0)) { + result = AVCT_NOT_OPEN; + } else { + /* find link; if none allocate a new one */ + index = p_ccb->p_lcb->allocated; + if (index > AVCT_NUM_LINKS) { + result = AVCT_BAD_HANDLE; + } else { + p_bcb = &avct_cb.bcb[index - 1]; + p_bcb->allocated = index; + } + } + + if (result == AVCT_SUCCESS) { + /* bind bcb to ccb */ + p_ccb->p_bcb = p_bcb; + AVCT_TRACE_DEBUG("ch_state: %d", p_bcb->ch_state); + avct_bcb_event(p_bcb, AVCT_LCB_UL_BIND_EVT, (tAVCT_LCB_EVT *) &p_ccb); + } + } + + return result; +#else + UNUSED(handle); + UNUSED(role); + return AVCT_NO_RESOURCES; +#endif +} + +/******************************************************************************* +** +** Function AVCT_RemoveBrowse +** +** Description Remove an AVCTP Browse channel. This function is called when +** the application is no longer using a connection. If this +** is the last connection to a peer the L2CAP channel for AVCTP +** will be closed. +** +** +** Returns AVCT_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +UINT16 AVCT_RemoveBrowse (UINT8 handle) +{ +#if (AVCT_BROWSE_INCLUDED == TRUE) + UINT16 result = AVCT_SUCCESS; + tAVCT_CCB *p_ccb; + + AVCT_TRACE_API("AVCT_RemoveBrowse"); + + /* map handle to ccb */ + if ((p_ccb = avct_ccb_by_idx(handle)) == NULL) { + result = AVCT_BAD_HANDLE; + } else if (p_ccb->p_bcb != NULL) + /* send unbind event to bcb */ + { + avct_bcb_event(p_ccb->p_bcb, AVCT_LCB_UL_UNBIND_EVT, (tAVCT_LCB_EVT *) &p_ccb); + } + return result; +#else + UNUSED(handle); + return AVCT_NO_RESOURCES; +#endif +} + +/******************************************************************************* +** +** Function AVCT_GetBrowseMtu +** +** Description Get the peer_mtu for the AVCTP Browse channel of the given +** connection. +** +** Returns the peer browsing channel MTU. +** +*******************************************************************************/ +UINT16 AVCT_GetBrowseMtu (UINT8 handle) +{ + UINT16 peer_mtu = AVCT_MIN_BROWSE_MTU; +#if (AVCT_BROWSE_INCLUDED == TRUE) + tAVCT_CCB *p_ccb; + + if ((p_ccb = avct_ccb_by_idx(handle)) != NULL && p_ccb->p_bcb != NULL) { + peer_mtu = p_ccb->p_bcb->peer_mtu; + } +#else + UNUSED(handle); +#endif + return peer_mtu; +} + +/******************************************************************************* +** +** Function AVCT_GetPeerMtu +** +** Description Get the peer_mtu for the AVCTP channel of the given +** connection. +** +** Returns the peer MTU size. +** +*******************************************************************************/ +UINT16 AVCT_GetPeerMtu (UINT8 handle) +{ + UINT16 peer_mtu = L2CAP_DEFAULT_MTU; + tAVCT_CCB *p_ccb; + + /* map handle to ccb */ + if ((p_ccb = avct_ccb_by_idx(handle)) != NULL) { + if (p_ccb->p_lcb) { + peer_mtu = p_ccb->p_lcb->peer_mtu; + } + } + + return peer_mtu; +} + +/******************************************************************************* +** +** Function AVCT_MsgReq +** +** Description Send an AVCTP message to a peer device. In calling +** AVCT_MsgReq(), the application should keep track of the +** congestion state of AVCTP as communicated with events +** AVCT_CONG_IND_EVT and AVCT_UNCONG_IND_EVT. If the +** application calls AVCT_MsgReq() when AVCTP is congested +** the message may be discarded. The application may make its +** first call to AVCT_MsgReq() after it receives an +** AVCT_CONNECT_CFM_EVT or AVCT_CONNECT_IND_EVT on control channel or +** AVCT_BROWSE_CONN_CFM_EVT or AVCT_BROWSE_CONN_IND_EVT on browsing channel. +** +** p_msg->layer_specific must be set to +** AVCT_DATA_CTRL for control channel traffic; +** AVCT_DATA_BROWSE for for browse channel traffic. +** +** Returns AVCT_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +UINT16 AVCT_MsgReq(UINT8 handle, UINT8 label, UINT8 cr, BT_HDR *p_msg) +{ + UINT16 result = AVCT_SUCCESS; + tAVCT_CCB *p_ccb; + tAVCT_UL_MSG ul_msg; + + AVCT_TRACE_API("AVCT_MsgReq"); + + /* verify p_msg parameter */ + if (p_msg == NULL) { + return AVCT_NO_RESOURCES; + } + AVCT_TRACE_API("len: %d", p_msg->len); + + /* map handle to ccb */ + if ((p_ccb = avct_ccb_by_idx(handle)) == NULL) { + result = AVCT_BAD_HANDLE; + osi_free(p_msg); + } + /* verify channel is bound to link */ + else if (p_ccb->p_lcb == NULL) { + result = AVCT_NOT_OPEN; + osi_free(p_msg); + } + + if (result == AVCT_SUCCESS) { + ul_msg.p_buf = p_msg; + ul_msg.p_ccb = p_ccb; + ul_msg.label = label; + ul_msg.cr = cr; + +#if (AVCT_BROWSE_INCLUDED == TRUE) + /* send msg event to bcb */ + if (p_msg->layer_specific == AVCT_DATA_BROWSE) { + if (p_ccb->p_bcb == NULL && (p_ccb->allocated & AVCT_ALOC_BCB) == 0) { + /* BCB channel is not open and not allocated */ + result = AVCT_BAD_HANDLE; + osi_free(p_msg); + } else { + p_ccb->p_bcb = avct_bcb_by_lcb(p_ccb->p_lcb); + avct_bcb_event(p_ccb->p_bcb, AVCT_LCB_UL_MSG_EVT, (tAVCT_LCB_EVT *) &ul_msg); + } + } + /* send msg event to lcb */ + else +#endif + { + avct_lcb_event(p_ccb->p_lcb, AVCT_LCB_UL_MSG_EVT, (tAVCT_LCB_EVT *) &ul_msg); + } + } + return result; +} + +#endif /* #if (defined(AVCT_INCLUDED) && AVCT_INCLUDED == TRUE) */ diff --git a/lib/bt/host/bluedroid/stack/avct/avct_ccb.c b/lib/bt/host/bluedroid/stack/avct/avct_ccb.c new file mode 100644 index 00000000..8cc9fc39 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/avct/avct_ccb.c @@ -0,0 +1,145 @@ +/****************************************************************************** + * + * Copyright (C) 2003-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This module contains functions which operate on the AVCTP connection + * control block. + * + ******************************************************************************/ + +#include +#include "stack/bt_types.h" +#include "common/bt_target.h" +#include "stack/avct_api.h" +#include "avct_int.h" + +#if (defined(AVCT_INCLUDED) && AVCT_INCLUDED == TRUE) + +/******************************************************************************* +** +** Function avct_ccb_alloc +** +** Description Allocate a connection control block; copy parameters to ccb. +** +** +** Returns pointer to the ccb, or NULL if none could be allocated. +** +*******************************************************************************/ +tAVCT_CCB *avct_ccb_alloc(tAVCT_CC *p_cc) +{ + tAVCT_CCB *p_ccb = &avct_cb.ccb[0]; + int i; + + for (i = 0; i < AVCT_NUM_CONN; i++, p_ccb++) { + if (!p_ccb->allocated) { + p_ccb->allocated = AVCT_ALOC_LCB; + memcpy(&p_ccb->cc, p_cc, sizeof(tAVCT_CC)); + AVCT_TRACE_DEBUG("avct_ccb_alloc %d", i); + break; + } + } + + if (i == AVCT_NUM_CONN) { + /* out of ccbs */ + p_ccb = NULL; + AVCT_TRACE_WARNING("Out of ccbs"); + } + return p_ccb; +} + +/******************************************************************************* +** +** Function avct_ccb_dealloc +** +** Description Deallocate a connection control block and call application +** callback. +** +** +** Returns void. +** +*******************************************************************************/ +void avct_ccb_dealloc(tAVCT_CCB *p_ccb, UINT8 event, UINT16 result, BD_ADDR bd_addr) +{ + tAVCT_CTRL_CBACK *p_cback = p_ccb->cc.p_ctrl_cback; + + AVCT_TRACE_DEBUG("avct_ccb_dealloc %d", avct_ccb_to_idx(p_ccb)); +#if (AVCT_BROWSE_INCLUDED == TRUE) + if (p_ccb->p_bcb == NULL) { + memset(p_ccb, 0, sizeof(tAVCT_CCB)); + } else { + /* control channel is down, but the browsing channel is still connected 0 disconnect it now */ + avct_bcb_event(p_ccb->p_bcb, AVCT_LCB_UL_UNBIND_EVT, (tAVCT_LCB_EVT *) &p_ccb); + p_ccb->p_lcb = NULL; + } +#else + memset(p_ccb, 0, sizeof(tAVCT_CCB)); +#endif + + if (event != AVCT_NO_EVT) { + (*p_cback)(avct_ccb_to_idx(p_ccb), event, result, bd_addr); + } +} + +/******************************************************************************* +** +** Function avct_ccb_to_idx +** +** Description Given a pointer to an ccb, return its index. +** +** +** Returns Index of ccb. +** +*******************************************************************************/ +UINT8 avct_ccb_to_idx(tAVCT_CCB *p_ccb) +{ + /* use array arithmetic to determine index */ + return (UINT8) (p_ccb - avct_cb.ccb); +} + +/******************************************************************************* +** +** Function avct_ccb_by_idx +** +** Description Return ccb pointer based on ccb index (or handle). +** +** +** Returns pointer to the ccb, or NULL if none found. +** +*******************************************************************************/ +tAVCT_CCB *avct_ccb_by_idx(UINT8 idx) +{ + tAVCT_CCB *p_ccb; + + /* verify index */ + if (idx < AVCT_NUM_CONN) { + p_ccb = &avct_cb.ccb[idx]; + + /* verify ccb is allocated */ + if (!p_ccb->allocated) { + p_ccb = NULL; + AVCT_TRACE_WARNING("ccb %d not allocated", idx); + } + } else { + p_ccb = NULL; + AVCT_TRACE_WARNING("No ccb for idx %d", idx); + } + return p_ccb; +} + +#endif /* #if (defined(AVCT_INCLUDED) && AVCT_INCLUDED == TRUE) */ diff --git a/lib/bt/host/bluedroid/stack/avct/avct_l2c.c b/lib/bt/host/bluedroid/stack/avct/avct_l2c.c new file mode 100644 index 00000000..a501b157 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/avct/avct_l2c.c @@ -0,0 +1,406 @@ +/****************************************************************************** + * + * Copyright (C) 2003-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This AVCTP module interfaces to L2CAP + * + ******************************************************************************/ + +#include +#include "stack/bt_types.h" +#include "common/bt_target.h" +#include "common/bt_defs.h" +#include "stack/avct_api.h" +#include "avct_int.h" +#include "stack/l2c_api.h" +#include "stack/l2cdefs.h" +#include "osi/allocator.h" + +#if (defined(AVCT_INCLUDED) && AVCT_INCLUDED == TRUE) + +/* Configuration flags. */ +#define AVCT_L2C_CFG_IND_DONE (1<<0) +#define AVCT_L2C_CFG_CFM_DONE (1<<1) + +/* callback function declarations */ +void avct_l2c_connect_ind_cback(BD_ADDR bd_addr, UINT16 lcid, UINT16 psm, UINT8 id); +void avct_l2c_connect_cfm_cback(UINT16 lcid, UINT16 result); +void avct_l2c_config_cfm_cback(UINT16 lcid, tL2CAP_CFG_INFO *p_cfg); +void avct_l2c_config_ind_cback(UINT16 lcid, tL2CAP_CFG_INFO *p_cfg); +void avct_l2c_disconnect_ind_cback(UINT16 lcid, BOOLEAN ack_needed); +void avct_l2c_disconnect_cfm_cback(UINT16 lcid, UINT16 result); +void avct_l2c_congestion_ind_cback(UINT16 lcid, BOOLEAN is_congested); +void avct_l2c_data_ind_cback(UINT16 lcid, BT_HDR *p_buf); + +/* L2CAP callback function structure */ +const tL2CAP_APPL_INFO avct_l2c_appl = { + avct_l2c_connect_ind_cback, + avct_l2c_connect_cfm_cback, + NULL, + avct_l2c_config_ind_cback, + avct_l2c_config_cfm_cback, + avct_l2c_disconnect_ind_cback, + avct_l2c_disconnect_cfm_cback, + NULL, + avct_l2c_data_ind_cback, + avct_l2c_congestion_ind_cback, + NULL /* tL2CA_TX_COMPLETE_CB */ +}; + +/******************************************************************************* +** +** Function avct_l2c_is_passive +** +** Description check is the CCB associated with the given LCB was created +** as passive +** +** Returns TRUE, if the given LCB is created as AVCT_PASSIVE +** +*******************************************************************************/ +static BOOLEAN avct_l2c_is_passive (tAVCT_LCB *p_lcb) +{ + BOOLEAN is_passive = FALSE; + tAVCT_CCB *p_ccb = &avct_cb.ccb[0]; + int i; + + for (i = 0; i < AVCT_NUM_CONN; i++, p_ccb++) { + if (p_ccb->allocated && (p_ccb->p_lcb == p_lcb)) { + AVCT_TRACE_DEBUG("avct_l2c_is_ct control:x%x", p_ccb->cc.control); + if (p_ccb->cc.control & AVCT_PASSIVE) { + is_passive = TRUE; + break; + } + } + } + return is_passive; +} + +/******************************************************************************* +** +** Function avct_l2c_connect_ind_cback +** +** Description This is the L2CAP connect indication callback function. +** +** +** Returns void +** +*******************************************************************************/ +void avct_l2c_connect_ind_cback(BD_ADDR bd_addr, UINT16 lcid, UINT16 psm, UINT8 id) +{ + tAVCT_LCB *p_lcb; + UINT16 result = L2CAP_CONN_OK; + tL2CAP_CFG_INFO cfg; + UNUSED(psm); + + /* do we already have a channel for this peer? */ + if ((p_lcb = avct_lcb_by_bd(bd_addr)) == NULL) { + /* no, allocate lcb */ + if ((p_lcb = avct_lcb_alloc(bd_addr)) == NULL) { + /* no ccb available, reject L2CAP connection */ + result = L2CAP_CONN_NO_RESOURCES; + } + } + /* else we already have a channel for this peer */ + else { + if (!avct_l2c_is_passive (p_lcb) || (p_lcb->ch_state == AVCT_CH_OPEN)) { + /* this LCB included CT role - reject */ + result = L2CAP_CONN_NO_RESOURCES; + } else { + /* TG role only - accept the connection from CT. move the channel ID to the conflict list */ + p_lcb->conflict_lcid = p_lcb->ch_lcid; + AVCT_TRACE_DEBUG("avct_l2c_connect_ind_cback conflict_lcid:0x%x", p_lcb->conflict_lcid); + } + } + + if (p_lcb) { + AVCT_TRACE_DEBUG("avct_l2c_connect_ind_cback: 0x%x, res: %d, ch_state: %d", + lcid, result, p_lcb->ch_state); + } + /* Send L2CAP connect rsp */ + L2CA_ConnectRsp(bd_addr, id, lcid, result, 0); + + /* if result ok, proceed with connection */ + if (result == L2CAP_CONN_OK) { + /* store LCID */ + p_lcb->ch_lcid = lcid; + + /* transition to configuration state */ + p_lcb->ch_state = AVCT_CH_CFG; + + /* Send L2CAP config req */ + memset(&cfg, 0, sizeof(tL2CAP_CFG_INFO)); + cfg.mtu_present = TRUE; + cfg.mtu = avct_cb.mtu; + L2CA_ConfigReq(lcid, &cfg); + AVCT_TRACE_DEBUG("avct_l2c snd Cfg Req"); + } + +#if (BT_USE_TRACES == TRUE) + if (p_lcb) { + AVCT_TRACE_DEBUG("ch_state cni: %d ", p_lcb->ch_state); + } +#endif +} + +/******************************************************************************* +** +** Function avct_l2c_connect_cfm_cback +** +** Description This is the L2CAP connect confirm callback function. +** +** +** Returns void +** +*******************************************************************************/ +void avct_l2c_connect_cfm_cback(UINT16 lcid, UINT16 result) +{ + tAVCT_LCB *p_lcb; + tL2CAP_CFG_INFO cfg; + + /* look up lcb for this channel */ + if ((p_lcb = avct_lcb_by_lcid(lcid)) != NULL) { + AVCT_TRACE_DEBUG("avct_l2c_connect_cfm_cback lcid:0x%x result: %d ch_state: %d, conflict_lcid:0x%x", + lcid, result, p_lcb->ch_state, p_lcb->conflict_lcid); + /* if in correct state */ + if (p_lcb->ch_state == AVCT_CH_CONN) { + /* if result successful */ + if (result == L2CAP_CONN_OK) { + /* set channel state */ + p_lcb->ch_state = AVCT_CH_CFG; + + /* Send L2CAP config req */ + memset(&cfg, 0, sizeof(tL2CAP_CFG_INFO)); + cfg.mtu_present = TRUE; + cfg.mtu = avct_cb.mtu; + L2CA_ConfigReq(lcid, &cfg); + AVCT_TRACE_DEBUG("avct_l2c snd Cfg Req"); + } + /* else failure */ + else { + AVCT_TRACE_DEBUG("avct_l2c_connect_cfm_cback conflict_lcid:0x%x", p_lcb->conflict_lcid); + if (p_lcb->conflict_lcid == lcid) { + p_lcb->conflict_lcid = 0; + } else { + avct_lcb_event(p_lcb, AVCT_LCB_LL_CLOSE_EVT, (tAVCT_LCB_EVT *) &result); + } + } + } else if (p_lcb->conflict_lcid == lcid) { + /* we must be in AVCT_CH_CFG state for the ch_lcid channel */ + AVCT_TRACE_DEBUG("avct_l2c_connect_cfm_cback ch_state: %d, conflict_lcid:0x%x", p_lcb->ch_state, p_lcb->conflict_lcid); + if (result == L2CAP_CONN_OK) { + /* just in case the peer also accepts our connection - Send L2CAP disconnect req */ + L2CA_DisconnectReq(lcid); + } + p_lcb->conflict_lcid = 0; + } + AVCT_TRACE_DEBUG("ch_state cnc: %d ", p_lcb->ch_state); + } +} + +/******************************************************************************* +** +** Function avct_l2c_config_cfm_cback +** +** Description This is the L2CAP config confirm callback function. +** +** +** Returns void +** +*******************************************************************************/ +void avct_l2c_config_cfm_cback(UINT16 lcid, tL2CAP_CFG_INFO *p_cfg) +{ + tAVCT_LCB *p_lcb; + + /* look up lcb for this channel */ + if ((p_lcb = avct_lcb_by_lcid(lcid)) != NULL) { + AVCT_TRACE_DEBUG("avct_l2c_config_cfm_cback: 0x%x, ch_state: %d, res: %d", + lcid, p_lcb->ch_state, p_cfg->result); + /* if in correct state */ + if (p_lcb->ch_state == AVCT_CH_CFG) { + /* if result successful */ + if (p_cfg->result == L2CAP_CFG_OK) { + /* update flags */ + p_lcb->ch_flags |= AVCT_L2C_CFG_CFM_DONE; + + /* if configuration complete */ + if (p_lcb->ch_flags & AVCT_L2C_CFG_IND_DONE) { + p_lcb->ch_state = AVCT_CH_OPEN; + avct_lcb_event(p_lcb, AVCT_LCB_LL_OPEN_EVT, NULL); + } + } + /* else failure */ + else { + AVCT_TRACE_DEBUG("ERROR avct_l2c_config_cfm_cback L2CA_DisconnectReq %d ", p_lcb->ch_state); + /* store result value */ + p_lcb->ch_result = p_cfg->result; + + /* Send L2CAP disconnect req */ + L2CA_DisconnectReq(lcid); + } + } + AVCT_TRACE_DEBUG("ch_state cfc: %d ", p_lcb->ch_state); + } +} + +/******************************************************************************* +** +** Function avct_l2c_config_ind_cback +** +** Description This is the L2CAP config indication callback function. +** +** +** Returns void +** +*******************************************************************************/ +void avct_l2c_config_ind_cback(UINT16 lcid, tL2CAP_CFG_INFO *p_cfg) +{ + tAVCT_LCB *p_lcb; + + /* look up lcb for this channel */ + if ((p_lcb = avct_lcb_by_lcid(lcid)) != NULL) { + AVCT_TRACE_DEBUG("avct_l2c_config_ind_cback: 0x%x, ch_state: %d", lcid, p_lcb->ch_state); + /* store the mtu in tbl */ + if (p_cfg->mtu_present) { + p_lcb->peer_mtu = p_cfg->mtu; + } else { + p_lcb->peer_mtu = L2CAP_DEFAULT_MTU; + } + + /* send L2CAP configure response */ + memset(p_cfg, 0, sizeof(tL2CAP_CFG_INFO)); + p_cfg->result = L2CAP_CFG_OK; + L2CA_ConfigRsp(lcid, p_cfg); + + /* if first config ind */ + if ((p_lcb->ch_flags & AVCT_L2C_CFG_IND_DONE) == 0) { + /* update flags */ + p_lcb->ch_flags |= AVCT_L2C_CFG_IND_DONE; + + /* if configuration complete */ + if (p_lcb->ch_flags & AVCT_L2C_CFG_CFM_DONE) { + p_lcb->ch_state = AVCT_CH_OPEN; + avct_lcb_event(p_lcb, AVCT_LCB_LL_OPEN_EVT, NULL); + } + } + AVCT_TRACE_DEBUG("ch_state cfi: %d ", p_lcb->ch_state); + } +} + +/******************************************************************************* +** +** Function avct_l2c_disconnect_ind_cback +** +** Description This is the L2CAP disconnect indication callback function. +** +** +** Returns void +** +*******************************************************************************/ +void avct_l2c_disconnect_ind_cback(UINT16 lcid, BOOLEAN ack_needed) +{ + tAVCT_LCB *p_lcb; + UINT16 result = AVCT_RESULT_FAIL; + + /* look up lcb for this channel */ + if ((p_lcb = avct_lcb_by_lcid(lcid)) != NULL) { + AVCT_TRACE_DEBUG("avct_l2c_disconnect_ind_cback: 0x%x, ch_state: %d", lcid, p_lcb->ch_state); + if (ack_needed) { + /* send L2CAP disconnect response */ + L2CA_DisconnectRsp(lcid); + } + + avct_lcb_event(p_lcb, AVCT_LCB_LL_CLOSE_EVT, (tAVCT_LCB_EVT *) &result); + AVCT_TRACE_DEBUG("ch_state di: %d ", p_lcb->ch_state); + } +} + +/******************************************************************************* +** +** Function avct_l2c_disconnect_cfm_cback +** +** Description This is the L2CAP disconnect confirm callback function. +** +** +** Returns void +** +*******************************************************************************/ +void avct_l2c_disconnect_cfm_cback(UINT16 lcid, UINT16 result) +{ + tAVCT_LCB *p_lcb; + UINT16 res; + + /* look up lcb for this channel */ + if ((p_lcb = avct_lcb_by_lcid(lcid)) != NULL) { + AVCT_TRACE_DEBUG("avct_l2c_disconnect_cfm_cback: 0x%x, ch_state: %d, res: %d", + lcid, p_lcb->ch_state, result); + /* result value may be previously stored */ + res = (p_lcb->ch_result != 0) ? p_lcb->ch_result : result; + p_lcb->ch_result = 0; + + avct_lcb_event(p_lcb, AVCT_LCB_LL_CLOSE_EVT, (tAVCT_LCB_EVT *) &res); + AVCT_TRACE_DEBUG("ch_state dc: %d ", p_lcb->ch_state); + } +} + +/******************************************************************************* +** +** Function avct_l2c_congestion_ind_cback +** +** Description This is the L2CAP congestion indication callback function. +** +** +** Returns void +** +*******************************************************************************/ +void avct_l2c_congestion_ind_cback(UINT16 lcid, BOOLEAN is_congested) +{ + tAVCT_LCB *p_lcb; + + AVCT_TRACE_DEBUG("avct_l2c_congestion_ind_cback: 0x%x", lcid); + /* look up lcb for this channel */ + if ((p_lcb = avct_lcb_by_lcid(lcid)) != NULL) { + avct_lcb_event(p_lcb, AVCT_LCB_LL_CONG_EVT, (tAVCT_LCB_EVT *) &is_congested); + } +} + +/******************************************************************************* +** +** Function avct_l2c_data_ind_cback +** +** Description This is the L2CAP data indication callback function. +** +** +** Returns void +** +*******************************************************************************/ +void avct_l2c_data_ind_cback(UINT16 lcid, BT_HDR *p_buf) +{ + tAVCT_LCB *p_lcb; + + AVCT_TRACE_DEBUG("avct_l2c_data_ind_cback: 0x%x", lcid); + /* look up lcb for this channel */ + if ((p_lcb = avct_lcb_by_lcid(lcid)) != NULL) { + avct_lcb_event(p_lcb, AVCT_LCB_LL_MSG_EVT, (tAVCT_LCB_EVT *) &p_buf); + } else { /* prevent buffer leak */ + AVCT_TRACE_WARNING("ERROR -> avct_l2c_data_ind_cback drop buffer"); + osi_free(p_buf); + } +} + +#endif /* #if (defined(AVCT_INCLUDED) && AVCT_INCLUDED == TRUE) */ diff --git a/lib/bt/host/bluedroid/stack/avct/avct_lcb.c b/lib/bt/host/bluedroid/stack/avct/avct_lcb.c new file mode 100644 index 00000000..61f6c9a5 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/avct/avct_lcb.c @@ -0,0 +1,445 @@ +/****************************************************************************** + * + * Copyright (C) 2003-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This module contains the link control state machine and functions which + * operate on the link control block. + * + ******************************************************************************/ + +#include +#include "stack/bt_types.h" +#include "common/bt_target.h" +#include "common/bt_defs.h" +#include "stack/avct_api.h" +#include "avct_int.h" +#include "osi/allocator.h" + +#if (defined(AVCT_INCLUDED) && AVCT_INCLUDED == TRUE) + +/***************************************************************************** +** state machine constants and types +*****************************************************************************/ + +#if BT_TRACE_VERBOSE == TRUE + +/* verbose state strings for trace */ +const char *const avct_lcb_st_str[] = { + "LCB_IDLE_ST", + "LCB_OPENING_ST", + "LCB_OPEN_ST", + "LCB_CLOSING_ST" +}; + +/* verbose event strings for trace */ +const char *const avct_lcb_evt_str[] = { + "UL_BIND_EVT", + "UL_UNBIND_EVT", + "UL_MSG_EVT", + "INT_CLOSE_EVT", + "LL_OPEN_EVT", + "LL_CLOSE_EVT", + "LL_MSG_EVT", + "LL_CONG_EVT" +}; + +#endif + +/* lcb state machine states */ +enum { + AVCT_LCB_IDLE_ST, + AVCT_LCB_OPENING_ST, + AVCT_LCB_OPEN_ST, + AVCT_LCB_CLOSING_ST +}; + +/* state machine action enumeration list */ +enum { + AVCT_LCB_CHNL_OPEN, + AVCT_LCB_CHNL_DISC, + AVCT_LCB_SEND_MSG, + AVCT_LCB_OPEN_IND, + AVCT_LCB_OPEN_FAIL, + AVCT_LCB_CLOSE_IND, + AVCT_LCB_CLOSE_CFM, + AVCT_LCB_MSG_IND, + AVCT_LCB_CONG_IND, + AVCT_LCB_BIND_CONN, + AVCT_LCB_BIND_FAIL, + AVCT_LCB_UNBIND_DISC, + AVCT_LCB_CHK_DISC, + AVCT_LCB_DISCARD_MSG, + AVCT_LCB_DEALLOC, + AVCT_LCB_FREE_MSG_IND, + AVCT_LCB_NUM_ACTIONS +}; + +#define AVCT_LCB_IGNORE AVCT_LCB_NUM_ACTIONS + +/* type for action functions */ +typedef void (*tAVCT_LCB_ACTION)(tAVCT_LCB *p_ccb, tAVCT_LCB_EVT *p_data); + +/* action function list */ +const tAVCT_LCB_ACTION avct_lcb_action[] = { + avct_lcb_chnl_open, + avct_lcb_chnl_disc, + avct_lcb_send_msg, + avct_lcb_open_ind, + avct_lcb_open_fail, + avct_lcb_close_ind, + avct_lcb_close_cfm, + avct_lcb_msg_ind, + avct_lcb_cong_ind, + avct_lcb_bind_conn, + avct_lcb_bind_fail, + avct_lcb_unbind_disc, + avct_lcb_chk_disc, + avct_lcb_discard_msg, + avct_lcb_dealloc, + avct_lcb_free_msg_ind +}; + +/* state table information */ +#define AVCT_LCB_ACTIONS 2 /* number of actions */ +#define AVCT_LCB_NEXT_STATE 2 /* position of next state */ +#define AVCT_LCB_NUM_COLS 3 /* number of columns in state tables */ + +/* state table for idle state */ +const UINT8 avct_lcb_st_idle[][AVCT_LCB_NUM_COLS] = { + /* Event Action 1 Action 2 Next state */ + /* UL_BIND_EVT */ {AVCT_LCB_CHNL_OPEN, AVCT_LCB_IGNORE, AVCT_LCB_OPENING_ST}, + /* UL_UNBIND_EVT */ {AVCT_LCB_UNBIND_DISC, AVCT_LCB_IGNORE, AVCT_LCB_IDLE_ST}, + /* UL_MSG_EVT */ {AVCT_LCB_DISCARD_MSG, AVCT_LCB_IGNORE, AVCT_LCB_IDLE_ST}, + /* INT_CLOSE_EVT */ {AVCT_LCB_IGNORE, AVCT_LCB_IGNORE, AVCT_LCB_IDLE_ST}, + /* LL_OPEN_EVT */ {AVCT_LCB_OPEN_IND, AVCT_LCB_IGNORE, AVCT_LCB_OPEN_ST}, + /* LL_CLOSE_EVT */ {AVCT_LCB_CLOSE_IND, AVCT_LCB_DEALLOC, AVCT_LCB_IDLE_ST}, + /* LL_MSG_EVT */ {AVCT_LCB_FREE_MSG_IND, AVCT_LCB_IGNORE, AVCT_LCB_IDLE_ST}, + /* LL_CONG_EVT */ {AVCT_LCB_IGNORE, AVCT_LCB_IGNORE, AVCT_LCB_IDLE_ST} +}; + +/* state table for opening state */ +const UINT8 avct_lcb_st_opening[][AVCT_LCB_NUM_COLS] = { + /* Event Action 1 Action 2 Next state */ + /* UL_BIND_EVT */ {AVCT_LCB_IGNORE, AVCT_LCB_IGNORE, AVCT_LCB_OPENING_ST}, + /* UL_UNBIND_EVT */ {AVCT_LCB_UNBIND_DISC, AVCT_LCB_IGNORE, AVCT_LCB_OPENING_ST}, + /* UL_MSG_EVT */ {AVCT_LCB_DISCARD_MSG, AVCT_LCB_IGNORE, AVCT_LCB_OPENING_ST}, + /* INT_CLOSE_EVT */ {AVCT_LCB_CHNL_DISC, AVCT_LCB_IGNORE, AVCT_LCB_CLOSING_ST}, + /* LL_OPEN_EVT */ {AVCT_LCB_OPEN_IND, AVCT_LCB_IGNORE, AVCT_LCB_OPEN_ST}, + /* LL_CLOSE_EVT */ {AVCT_LCB_OPEN_FAIL, AVCT_LCB_DEALLOC, AVCT_LCB_IDLE_ST}, + /* LL_MSG_EVT */ {AVCT_LCB_FREE_MSG_IND, AVCT_LCB_IGNORE, AVCT_LCB_OPENING_ST}, + /* LL_CONG_EVT */ {AVCT_LCB_CONG_IND, AVCT_LCB_IGNORE, AVCT_LCB_OPENING_ST} +}; + +/* state table for open state */ +const UINT8 avct_lcb_st_open[][AVCT_LCB_NUM_COLS] = { + /* Event Action 1 Action 2 Next state */ + /* UL_BIND_EVT */ {AVCT_LCB_BIND_CONN, AVCT_LCB_IGNORE, AVCT_LCB_OPEN_ST}, + /* UL_UNBIND_EVT */ {AVCT_LCB_CHK_DISC, AVCT_LCB_IGNORE, AVCT_LCB_OPEN_ST}, + /* UL_MSG_EVT */ {AVCT_LCB_SEND_MSG, AVCT_LCB_IGNORE, AVCT_LCB_OPEN_ST}, + /* INT_CLOSE_EVT */ {AVCT_LCB_CHNL_DISC, AVCT_LCB_IGNORE, AVCT_LCB_CLOSING_ST}, + /* LL_OPEN_EVT */ {AVCT_LCB_IGNORE, AVCT_LCB_IGNORE, AVCT_LCB_OPEN_ST}, + /* LL_CLOSE_EVT */ {AVCT_LCB_CLOSE_IND, AVCT_LCB_DEALLOC, AVCT_LCB_IDLE_ST}, + /* LL_MSG_EVT */ {AVCT_LCB_MSG_IND, AVCT_LCB_IGNORE, AVCT_LCB_OPEN_ST}, + /* LL_CONG_EVT */ {AVCT_LCB_CONG_IND, AVCT_LCB_IGNORE, AVCT_LCB_OPEN_ST} +}; + +/* state table for closing state */ +const UINT8 avct_lcb_st_closing[][AVCT_LCB_NUM_COLS] = { + /* Event Action 1 Action 2 Next state */ + /* UL_BIND_EVT */ {AVCT_LCB_BIND_FAIL, AVCT_LCB_IGNORE, AVCT_LCB_CLOSING_ST}, + /* UL_UNBIND_EVT */ {AVCT_LCB_IGNORE, AVCT_LCB_IGNORE, AVCT_LCB_CLOSING_ST}, + /* UL_MSG_EVT */ {AVCT_LCB_DISCARD_MSG, AVCT_LCB_IGNORE, AVCT_LCB_CLOSING_ST}, + /* INT_CLOSE_EVT */ {AVCT_LCB_IGNORE, AVCT_LCB_IGNORE, AVCT_LCB_CLOSING_ST}, + /* LL_OPEN_EVT */ {AVCT_LCB_IGNORE, AVCT_LCB_IGNORE, AVCT_LCB_CLOSING_ST}, + /* LL_CLOSE_EVT */ {AVCT_LCB_CLOSE_CFM, AVCT_LCB_DEALLOC, AVCT_LCB_IDLE_ST}, + /* LL_MSG_EVT */ {AVCT_LCB_FREE_MSG_IND, AVCT_LCB_IGNORE, AVCT_LCB_CLOSING_ST}, + /* LL_CONG_EVT */ {AVCT_LCB_IGNORE, AVCT_LCB_IGNORE, AVCT_LCB_CLOSING_ST} +}; + +/* type for state table */ +typedef const UINT8 (*tAVCT_LCB_ST_TBL)[AVCT_LCB_NUM_COLS]; + +/* state table */ +const tAVCT_LCB_ST_TBL avct_lcb_st_tbl[] = { + avct_lcb_st_idle, + avct_lcb_st_opening, + avct_lcb_st_open, + avct_lcb_st_closing +}; + +/******************************************************************************* +** +** Function avct_lcb_event +** +** Description State machine event handling function for lcb +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avct_lcb_event(tAVCT_LCB *p_lcb, UINT8 event, tAVCT_LCB_EVT *p_data) +{ + tAVCT_LCB_ST_TBL state_table; + UINT8 action; + int i; + +#if BT_TRACE_VERBOSE == TRUE + AVCT_TRACE_EVENT("LCB lcb=%d event=%s state=%s", p_lcb->allocated, avct_lcb_evt_str[event], avct_lcb_st_str[p_lcb->state]); +#else + AVCT_TRACE_EVENT("LCB lcb=%d event=%d state=%d", p_lcb->allocated, event, p_lcb->state); +#endif + + /* look up the state table for the current state */ + state_table = avct_lcb_st_tbl[p_lcb->state]; + + /* set next state */ + p_lcb->state = state_table[event][AVCT_LCB_NEXT_STATE]; + + /* execute action functions */ + for (i = 0; i < AVCT_LCB_ACTIONS; i++) { + if ((action = state_table[event][i]) != AVCT_LCB_IGNORE) { + (*avct_lcb_action[action])(p_lcb, p_data); + } else { + break; + } + } +} + +/******************************************************************************* +** +** Function avct_bcb_event +** +** Description State machine event handling function for lcb +** +** +** Returns Nothing. +** +*******************************************************************************/ +#if (AVCT_BROWSE_INCLUDED == TRUE) +void avct_bcb_event(tAVCT_BCB *p_bcb, UINT8 event, tAVCT_LCB_EVT *p_data) +{ + tAVCT_LCB_ST_TBL state_table; + UINT8 action; + int i; + +#if BT_TRACE_VERBOSE == TRUE + AVCT_TRACE_EVENT("BCB lcb=%d event=%s state=%s", p_bcb->allocated, avct_lcb_evt_str[event], avct_lcb_st_str[p_bcb->state]); +#else + AVCT_TRACE_EVENT("BCB lcb=%d event=%d state=%d", p_bcb->allocated, event, p_bcb->state); +#endif + + /* look up the state table for the current state */ + state_table = avct_lcb_st_tbl[p_bcb->state]; + + /* set next state */ + p_bcb->state = state_table[event][AVCT_LCB_NEXT_STATE]; + + /* execute action functions */ + for (i = 0; i < AVCT_LCB_ACTIONS; i++) { + if ((action = state_table[event][i]) != AVCT_LCB_IGNORE) { + (*avct_bcb_action[action])(p_bcb, p_data); + } else { + break; + } + } +} +#endif + +/******************************************************************************* +** +** Function avct_lcb_by_bd +** +** Description This lookup function finds the lcb for a BD address. +** +** +** Returns pointer to the lcb, or NULL if none found. +** +*******************************************************************************/ +tAVCT_LCB *avct_lcb_by_bd(BD_ADDR bd_addr) +{ + tAVCT_LCB *p_lcb = &avct_cb.lcb[0]; + int i; + + for (i = 0; i < AVCT_NUM_LINKS; i++, p_lcb++) { + /* if allocated lcb has matching lcb */ + if (p_lcb->allocated && (!memcmp(p_lcb->peer_addr, bd_addr, BD_ADDR_LEN))) { + break; + } + } + + if (i == AVCT_NUM_LINKS) { + /* if no lcb found */ + p_lcb = NULL; + + AVCT_TRACE_DEBUG("No lcb for addr %02x-%02x-%02x-%02x-%02x-%02x", + bd_addr[0], bd_addr[1], bd_addr[2], bd_addr[3], bd_addr[4], bd_addr[5]); + } + return p_lcb; +} + +/******************************************************************************* +** +** Function avct_lcb_alloc +** +** Description Allocate a link control block. +** +** +** Returns pointer to the lcb, or NULL if none could be allocated. +** +*******************************************************************************/ +tAVCT_LCB *avct_lcb_alloc(BD_ADDR bd_addr) +{ + tAVCT_LCB *p_lcb = &avct_cb.lcb[0]; + int i; + + for (i = 0; i < AVCT_NUM_LINKS; i++, p_lcb++) { + if (!p_lcb->allocated) { + p_lcb->allocated = (UINT8)(i + 1); + memcpy(p_lcb->peer_addr, bd_addr, BD_ADDR_LEN); + AVCT_TRACE_DEBUG("avct_lcb_alloc %d", p_lcb->allocated); + p_lcb->tx_q = fixed_queue_new(QUEUE_SIZE_MAX); + break; + } + } + + if (i == AVCT_NUM_LINKS) { + /* out of lcbs */ + p_lcb = NULL; + AVCT_TRACE_WARNING("Out of lcbs"); + } + return p_lcb; +} + +/******************************************************************************* +** +** Function avct_lcb_dealloc +** +** Description Deallocate a link control block. +** +** +** Returns void. +** +*******************************************************************************/ +void avct_lcb_dealloc(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data) +{ + UNUSED(p_data); + + AVCT_TRACE_DEBUG("%s allocated: %d", __func__, p_lcb->allocated); + + // Check if the LCB is still referenced + + tAVCT_CCB *p_ccb = &avct_cb.ccb[0]; + for (size_t i = 0; i < AVCT_NUM_CONN; i++, p_ccb++) + { + if (p_ccb->allocated && p_ccb->p_lcb == p_lcb) + { + AVCT_TRACE_DEBUG("%s LCB in use; lcb index: %d", __func__, i); + return; + } + } + + // If not, de-allocate now... + + AVCT_TRACE_DEBUG("%s Freeing LCB", __func__); + osi_free(p_lcb->p_rx_msg); + fixed_queue_free(p_lcb->tx_q, NULL); + memset(p_lcb, 0, sizeof(tAVCT_LCB)); +} + +/******************************************************************************* +** +** Function avct_lcb_by_lcid +** +** Description Find the LCB associated with the L2CAP LCID +** +** +** Returns pointer to the lcb, or NULL if none found. +** +*******************************************************************************/ +tAVCT_LCB *avct_lcb_by_lcid(UINT16 lcid) +{ + tAVCT_LCB *p_lcb = &avct_cb.lcb[0]; + int i; + + for (i = 0; i < AVCT_NUM_LINKS; i++, p_lcb++) { + if (p_lcb->allocated && ((p_lcb->ch_lcid == lcid) || (p_lcb->conflict_lcid == lcid))) { + break; + } + } + + if (i == AVCT_NUM_LINKS) { + /* out of lcbs */ + p_lcb = NULL; + AVCT_TRACE_WARNING("No lcb for lcid %x", lcid); + } + + return p_lcb; +} + +/******************************************************************************* +** +** Function avct_lcb_has_pid +** +** Description See if any ccbs on this lcb have a particular pid. +** +** +** Returns Pointer to CCB if PID found, NULL otherwise. +** +*******************************************************************************/ +tAVCT_CCB *avct_lcb_has_pid(tAVCT_LCB *p_lcb, UINT16 pid) +{ + tAVCT_CCB *p_ccb = &avct_cb.ccb[0]; + int i; + + for (i = 0; i < AVCT_NUM_CONN; i++, p_ccb++) { + if (p_ccb->allocated && (p_ccb->p_lcb == p_lcb) && (p_ccb->cc.pid == pid)) { + return p_ccb; + } + } + return NULL; +} + +/******************************************************************************* +** +** Function avct_lcb_last_ccb +** +** Description See if given ccb is only one on the lcb. +** +** +** Returns TRUE if ccb is last, FALSE otherwise. +** +*******************************************************************************/ +BOOLEAN avct_lcb_last_ccb(tAVCT_LCB *p_lcb, tAVCT_CCB *p_ccb_last) +{ + tAVCT_CCB *p_ccb = &avct_cb.ccb[0]; + int i; + + AVCT_TRACE_WARNING("avct_lcb_last_ccb"); + for (i = 0; i < AVCT_NUM_CONN; i++, p_ccb++) { + AVCT_TRACE_WARNING("%x: aloc:%d, lcb:%p/%p, ccb:%p/%p", + i, p_ccb->allocated, p_ccb->p_lcb, p_lcb, p_ccb, p_ccb_last); + if (p_ccb->allocated && (p_ccb->p_lcb == p_lcb) && (p_ccb != p_ccb_last)) { + return FALSE; + } + } + return TRUE; +} + + +#endif /* #if (defined(AVCT_INCLUDED) && AVCT_INCLUDED == TRUE) */ diff --git a/lib/bt/host/bluedroid/stack/avct/avct_lcb_act.c b/lib/bt/host/bluedroid/stack/avct/avct_lcb_act.c new file mode 100644 index 00000000..b83219dc --- /dev/null +++ b/lib/bt/host/bluedroid/stack/avct/avct_lcb_act.c @@ -0,0 +1,685 @@ +/****************************************************************************** + * + * Copyright (C) 2003-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This module contains action functions of the link control state machine. + * + ******************************************************************************/ + +#include +#include "stack/bt_types.h" +#include "common/bt_target.h" +#include "common/bt_defs.h" +#include "stack/avct_api.h" +#include "avct_int.h" +#include "stack/btm_api.h" +#include "osi/allocator.h" + +#if (defined(AVCT_INCLUDED) && AVCT_INCLUDED == TRUE) + +/* packet header length lookup table */ +const UINT8 avct_lcb_pkt_type_len[] = { + AVCT_HDR_LEN_SINGLE, + AVCT_HDR_LEN_START, + AVCT_HDR_LEN_CONT, + AVCT_HDR_LEN_END +}; + +/******************************************************************************* +** +** Function avct_lcb_msg_asmbl +** +** Description Reassemble incoming message. +** +** +** Returns Pointer to reassembled message; NULL if no message +** available. +** +*******************************************************************************/ +static BT_HDR *avct_lcb_msg_asmbl(tAVCT_LCB *p_lcb, BT_HDR *p_buf) +{ + UINT8 *p; + UINT8 pkt_type; + BT_HDR *p_ret; + UINT16 buf_len; + + /* parse the message header */ + p = (UINT8 *)(p_buf + 1) + p_buf->offset; + AVCT_PRS_PKT_TYPE(p, pkt_type); + + /* quick sanity check on length */ + if (p_buf->len < avct_lcb_pkt_type_len[pkt_type]) { + osi_free(p_buf); + AVCT_TRACE_WARNING("Bad length during reassembly"); + p_ret = NULL; + } + /* single packet */ + else if (pkt_type == AVCT_PKT_TYPE_SINGLE) { + /* if reassembly in progress drop message and process new single */ + if (p_lcb->p_rx_msg != NULL) { + AVCT_TRACE_WARNING("Got single during reassembly"); + } + osi_free(p_lcb->p_rx_msg); + p_lcb->p_rx_msg = NULL; + p_ret = p_buf; + } + /* start packet */ + else if (pkt_type == AVCT_PKT_TYPE_START) { + /* if reassembly in progress drop message and process new start */ + if (p_lcb->p_rx_msg != NULL) { + AVCT_TRACE_WARNING("Got start during reassembly"); + } + osi_free(p_lcb->p_rx_msg); + /* Allocate bigger buffer for reassembly. As lower layers are + * not aware of possible packet size after reassembly they + * would have allocated smaller buffer. + */ + p_lcb->p_rx_msg = (BT_HDR *)osi_malloc(BT_DEFAULT_BUFFER_SIZE); + if (p_lcb->p_rx_msg == NULL) { + AVCT_TRACE_ERROR ("Cannot alloc buffer for reassembly !!"); + osi_free(p_buf); + } else { + memcpy (p_lcb->p_rx_msg, p_buf, + sizeof(BT_HDR) + p_buf->offset + p_buf->len); + /* Free original buffer */ + osi_free(p_buf); + + /* update p to point to new buffer */ + p = (UINT8 *)(p_lcb->p_rx_msg + 1) + p_lcb->p_rx_msg->offset; + + /* copy first header byte over nosp */ + *(p + 1) = *p; + + /* set offset to point to where to copy next */ + p_lcb->p_rx_msg->offset += p_lcb->p_rx_msg->len; + + /* adjust length for packet header */ + p_lcb->p_rx_msg->len -= 1; + } + p_ret = NULL; + } + /* continue or end */ + else { + /* if no reassembly in progress drop message */ + if (p_lcb->p_rx_msg == NULL) { + osi_free(p_buf); + AVCT_TRACE_WARNING("Pkt type=%d out of order", pkt_type); + p_ret = NULL; + } else { + /* get size of buffer holding assembled message */ + /* + * NOTE: The buffer is allocated above at the beginning of the + * reassembly, and is always of size BT_DEFAULT_BUFFER_SIZE. + */ + buf_len = BT_DEFAULT_BUFFER_SIZE - sizeof(BT_HDR); + + /* adjust offset and len of fragment for header byte */ + p_buf->offset += AVCT_HDR_LEN_CONT; + p_buf->len -= AVCT_HDR_LEN_CONT; + + /* verify length */ + if ((p_lcb->p_rx_msg->offset + p_buf->len) > buf_len) { + /* won't fit; free everything */ + AVCT_TRACE_WARNING("%s: Fragmented message too big!", __func__); + osi_free(p_lcb->p_rx_msg); + p_lcb->p_rx_msg = NULL; + osi_free(p_buf); + p_ret = NULL; + } else { + /* copy contents of p_buf to p_rx_msg */ + memcpy((UINT8 *)(p_lcb->p_rx_msg + 1) + p_lcb->p_rx_msg->offset, + (UINT8 *)(p_buf + 1) + p_buf->offset, p_buf->len); + + if (pkt_type == AVCT_PKT_TYPE_END) { + p_lcb->p_rx_msg->offset -= p_lcb->p_rx_msg->len; + p_lcb->p_rx_msg->len += p_buf->len; + p_ret = p_lcb->p_rx_msg; + p_lcb->p_rx_msg = NULL; + } else { + p_lcb->p_rx_msg->offset += p_buf->len; + p_lcb->p_rx_msg->len += p_buf->len; + p_ret = NULL; + } + osi_free(p_buf); + } + } + } + return p_ret; +} + + +/******************************************************************************* +** +** Function avct_lcb_chnl_open +** +** Description Open L2CAP channel to peer +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avct_lcb_chnl_open(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data) +{ + UINT16 result = AVCT_RESULT_FAIL; + UNUSED(p_data); + + BTM_SetOutService(p_lcb->peer_addr, BTM_SEC_SERVICE_AVCTP, 0); + /* call l2cap connect req */ + p_lcb->ch_state = AVCT_CH_CONN; + if ((p_lcb->ch_lcid = L2CA_ConnectReq(AVCT_PSM, p_lcb->peer_addr)) == 0) { + /* if connect req failed, send ourselves close event */ + avct_lcb_event(p_lcb, AVCT_LCB_LL_CLOSE_EVT, (tAVCT_LCB_EVT *) &result); + } +} + +/******************************************************************************* +** +** Function avct_lcb_unbind_disc +** +** Description Deallocate ccb and call callback with disconnect event. +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avct_lcb_unbind_disc(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data) +{ + UNUSED(p_lcb); + + avct_ccb_dealloc(p_data->p_ccb, AVCT_DISCONNECT_CFM_EVT, 0, NULL); +} + +/******************************************************************************* +** +** Function avct_lcb_open_ind +** +** Description Handle an LL_OPEN event. For each allocated ccb already +** bound to this lcb, send a connect event. For each +** unbound ccb with a new PID, bind that ccb to this lcb and +** send a connect event. +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avct_lcb_open_ind(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data) +{ + tAVCT_CCB *p_ccb = &avct_cb.ccb[0]; + int i; + BOOLEAN bind = FALSE; + + for (i = 0; i < AVCT_NUM_CONN; i++, p_ccb++) { + /* if ccb allocated and */ + if (p_ccb->allocated) { + /* if bound to this lcb send connect confirm event */ + if (p_ccb->p_lcb == p_lcb) { + bind = TRUE; + L2CA_SetTxPriority(p_lcb->ch_lcid, L2CAP_CHNL_PRIORITY_HIGH); + p_ccb->cc.p_ctrl_cback(avct_ccb_to_idx(p_ccb), AVCT_CONNECT_CFM_EVT, + 0, p_lcb->peer_addr); + } + /* if unbound acceptor and lcb doesn't already have a ccb for this PID */ + else if ((p_ccb->p_lcb == NULL) && (p_ccb->cc.role == AVCT_ACP) && + (avct_lcb_has_pid(p_lcb, p_ccb->cc.pid) == NULL)) { + /* bind ccb to lcb and send connect ind event */ + bind = TRUE; + p_ccb->p_lcb = p_lcb; + L2CA_SetTxPriority(p_lcb->ch_lcid, L2CAP_CHNL_PRIORITY_HIGH); + p_ccb->cc.p_ctrl_cback(avct_ccb_to_idx(p_ccb), AVCT_CONNECT_IND_EVT, + 0, p_lcb->peer_addr); + } + } + } + + /* if no ccbs bound to this lcb, disconnect */ + if (bind == FALSE) { + avct_lcb_event(p_lcb, AVCT_LCB_INT_CLOSE_EVT, p_data); + } +} + +/******************************************************************************* +** +** Function avct_lcb_open_fail +** +** Description L2CAP channel open attempt failed. Deallocate any ccbs +** on this lcb and send connect confirm event with failure. +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avct_lcb_open_fail(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data) +{ + tAVCT_CCB *p_ccb = &avct_cb.ccb[0]; + int i; + + for (i = 0; i < AVCT_NUM_CONN; i++, p_ccb++) { + if (p_ccb->allocated && (p_ccb->p_lcb == p_lcb)) { + avct_ccb_dealloc(p_ccb, AVCT_CONNECT_CFM_EVT, + p_data->result, p_lcb->peer_addr); + } + } +} + +/******************************************************************************* +** +** Function avct_lcb_close_ind +** +** Description L2CAP channel closed by peer. Deallocate any initiator +** ccbs on this lcb and send disconnect ind event. +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avct_lcb_close_ind(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data) +{ + tAVCT_CCB *p_ccb = &avct_cb.ccb[0]; + int i; + UNUSED(p_data); + + for (i = 0; i < AVCT_NUM_CONN; i++, p_ccb++) { + if (p_ccb->allocated && (p_ccb->p_lcb == p_lcb)) { + if (p_ccb->cc.role == AVCT_INT) { + avct_ccb_dealloc(p_ccb, AVCT_DISCONNECT_IND_EVT, + 0, p_lcb->peer_addr); + } else { + p_ccb->p_lcb = NULL; + (*p_ccb->cc.p_ctrl_cback)(avct_ccb_to_idx(p_ccb), AVCT_DISCONNECT_IND_EVT, + 0, p_lcb->peer_addr); + } + } + } +} + +/******************************************************************************* +** +** Function avct_lcb_close_cfm +** +** Description L2CAP channel closed by us. Deallocate any initiator +** ccbs on this lcb and send disconnect ind or cfm event. +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avct_lcb_close_cfm(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data) +{ + tAVCT_CCB *p_ccb = &avct_cb.ccb[0]; + int i; + UINT8 event; + + for (i = 0; i < AVCT_NUM_CONN; i++, p_ccb++) { + if (p_ccb->allocated && (p_ccb->p_lcb == p_lcb)) { + /* if this ccb initiated close send disconnect cfm otherwise ind */ + if (p_ccb->ch_close) { + p_ccb->ch_close = FALSE; + event = AVCT_DISCONNECT_CFM_EVT; + } else { + event = AVCT_DISCONNECT_IND_EVT; + } + + if (p_ccb->cc.role == AVCT_INT) { + avct_ccb_dealloc(p_ccb, event, p_data->result, p_lcb->peer_addr); + } else { + p_ccb->p_lcb = NULL; + (*p_ccb->cc.p_ctrl_cback)(avct_ccb_to_idx(p_ccb), event, + p_data->result, p_lcb->peer_addr); + } + } + } +} + +/******************************************************************************* +** +** Function avct_lcb_bind_conn +** +** Description Bind ccb to lcb and send connect cfm event. +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avct_lcb_bind_conn(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data) +{ + p_data->p_ccb->p_lcb = p_lcb; + (*p_data->p_ccb->cc.p_ctrl_cback)(avct_ccb_to_idx(p_data->p_ccb), + AVCT_CONNECT_CFM_EVT, 0, p_lcb->peer_addr); +} + +/******************************************************************************* +** +** Function avct_lcb_chk_disc +** +** Description A ccb wants to close; if it is the last ccb on this lcb, +** close channel. Otherwise just deallocate and call +** callback. +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avct_lcb_chk_disc(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data) +{ + AVCT_TRACE_EVENT("avct_lcb_chk_disc"); +#if (AVCT_BROWSE_INCLUDED == TRUE) + avct_close_bcb(p_lcb, p_data); +#endif + if (avct_lcb_last_ccb(p_lcb, p_data->p_ccb)) { + AVCT_TRACE_EVENT("closing"); + p_data->p_ccb->ch_close = TRUE; + avct_lcb_event(p_lcb, AVCT_LCB_INT_CLOSE_EVT, p_data); + } else { + AVCT_TRACE_EVENT("dealloc ccb"); + avct_lcb_unbind_disc(p_lcb, p_data); + } +} + +/******************************************************************************* +** +** Function avct_lcb_chnl_disc +** +** Description Disconnect L2CAP channel. +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avct_lcb_chnl_disc(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data) +{ + UNUSED(p_data); + + L2CA_DisconnectReq(p_lcb->ch_lcid); +} + +/******************************************************************************* +** +** Function avct_lcb_bind_fail +** +** Description Deallocate ccb and call callback with connect event +** with failure result. +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avct_lcb_bind_fail(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data) +{ + UNUSED(p_lcb); + + avct_ccb_dealloc(p_data->p_ccb, AVCT_CONNECT_CFM_EVT, AVCT_RESULT_FAIL, NULL); +} + +/******************************************************************************* +** +** Function avct_lcb_cong_ind +** +** Description Handle congestion indication from L2CAP. +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avct_lcb_cong_ind(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data) +{ + tAVCT_CCB *p_ccb = &avct_cb.ccb[0]; + int i; + UINT8 event; + BT_HDR *p_buf; + + /* set event */ + event = (p_data->cong) ? AVCT_CONG_IND_EVT : AVCT_UNCONG_IND_EVT; + p_lcb->cong = p_data->cong; + if (p_lcb->cong == FALSE && !fixed_queue_is_empty(p_lcb->tx_q)) + { + while (!p_lcb->cong && + (p_buf = (BT_HDR *)fixed_queue_dequeue(p_lcb->tx_q, 0)) != NULL) + { + if (L2CA_DataWrite(p_lcb->ch_lcid, p_buf) == L2CAP_DW_CONGESTED) + { + p_lcb->cong = TRUE; + } + } + } + + /* send event to all ccbs on this lcb */ + for (i = 0; i < AVCT_NUM_CONN; i++, p_ccb++) { + if (p_ccb->allocated && (p_ccb->p_lcb == p_lcb)) { + (*p_ccb->cc.p_ctrl_cback)(avct_ccb_to_idx(p_ccb), event, 0, p_lcb->peer_addr); + } + } +} + +/******************************************************************************* +** +** Function avct_lcb_discard_msg +** +** Description Discard a message sent in from the API. +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avct_lcb_discard_msg(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data) +{ + UNUSED(p_lcb); + + AVCT_TRACE_WARNING("Dropping msg"); + + osi_free(p_data->ul_msg.p_buf); + p_data->ul_msg.p_buf = NULL; +} + +/******************************************************************************* +** +** Function avct_lcb_send_msg +** +** Description Build and send an AVCTP message. +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avct_lcb_send_msg(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data) +{ + UINT16 curr_msg_len; + UINT8 pkt_type; + UINT8 hdr_len; + UINT8 *p; + UINT8 nosp = 0; /* number of subsequent packets */ + UINT16 temp; + UINT16 buf_size = p_lcb->peer_mtu + L2CAP_MIN_OFFSET + BT_HDR_SIZE; + + + /* store msg len */ + curr_msg_len = p_data->ul_msg.p_buf->len; + + /* initialize packet type and other stuff */ + if (curr_msg_len <= (p_lcb->peer_mtu - AVCT_HDR_LEN_SINGLE)) { + pkt_type = AVCT_PKT_TYPE_SINGLE; + } else { + pkt_type = AVCT_PKT_TYPE_START; + temp = (curr_msg_len + AVCT_HDR_LEN_START - p_lcb->peer_mtu); + nosp = temp / (p_lcb->peer_mtu - 1) + 1; + if ( (temp % (p_lcb->peer_mtu - 1)) != 0) { + nosp++; + } + } + + /* while we haven't sent all packets */ + while (curr_msg_len != 0) { + BT_HDR *p_buf; + + /* set header len */ + hdr_len = avct_lcb_pkt_type_len[pkt_type]; + + /* if remaining msg must be fragmented */ + if (p_data->ul_msg.p_buf->len > (p_lcb->peer_mtu - hdr_len)) { + /* get a new buffer for fragment we are sending */ + if ((p_buf = (BT_HDR *) osi_malloc(buf_size)) == NULL) { + /* whoops; free original msg buf and bail */ + AVCT_TRACE_ERROR ("avct_lcb_send_msg cannot alloc buffer!!"); + osi_free(p_data->ul_msg.p_buf); + break; + } + + /* copy portion of data from current message to new buffer */ + p_buf->offset = L2CAP_MIN_OFFSET + hdr_len; + p_buf->len = p_lcb->peer_mtu - hdr_len; + + memcpy((UINT8 *)(p_buf + 1) + p_buf->offset, + (UINT8 *)(p_data->ul_msg.p_buf + 1) + p_data->ul_msg.p_buf->offset, p_buf->len); + + p_data->ul_msg.p_buf->offset += p_buf->len; + p_data->ul_msg.p_buf->len -= p_buf->len; + } else { + p_buf = p_data->ul_msg.p_buf; + } + + curr_msg_len -= p_buf->len; + + /* set up to build header */ + p_buf->len += hdr_len; + p_buf->offset -= hdr_len; + p = (UINT8 *)(p_buf + 1) + p_buf->offset; + + /* build header */ + AVCT_BLD_HDR(p, p_data->ul_msg.label, pkt_type, p_data->ul_msg.cr); + if (pkt_type == AVCT_PKT_TYPE_START) { + UINT8_TO_STREAM(p, nosp); + } + if ((pkt_type == AVCT_PKT_TYPE_START) || (pkt_type == AVCT_PKT_TYPE_SINGLE)) { + UINT16_TO_BE_STREAM(p, p_data->ul_msg.p_ccb->cc.pid); + } + + if (p_lcb->cong == TRUE) { + fixed_queue_enqueue(p_lcb->tx_q, p_buf, FIXED_QUEUE_MAX_TIMEOUT); + } + + /* send message to L2CAP */ + else { + if (L2CA_DataWrite(p_lcb->ch_lcid, p_buf) == L2CAP_DW_CONGESTED) { + p_lcb->cong = TRUE; + } + } + + /* update pkt type for next packet */ + if (curr_msg_len > (p_lcb->peer_mtu - AVCT_HDR_LEN_END)) { + pkt_type = AVCT_PKT_TYPE_CONT; + } else { + pkt_type = AVCT_PKT_TYPE_END; + } + } + AVCT_TRACE_DEBUG ("avct_lcb_send_msg tx_q_count:%d", + fixed_queue_length(p_lcb->tx_q)); + return; +} + +/******************************************************************************* +** +** Function avct_lcb_free_msg_ind +** +** Description Discard an incoming AVCTP message. +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avct_lcb_free_msg_ind(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data) +{ + UNUSED(p_lcb); + + if (p_data == NULL) { + return; + } + + osi_free(p_data->p_buf); + p_data->p_buf = NULL; +} + +/******************************************************************************* +** +** Function avct_lcb_msg_ind +** +** Description Handle an incoming AVCTP message. +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avct_lcb_msg_ind(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data) +{ + UINT8 *p; + UINT8 label, type, cr_ipid; + UINT16 pid; + tAVCT_CCB *p_ccb; + BT_HDR *p_buf; + + /* this p_buf is to be reported through p_msg_cback. The layer_specific + * needs to be set properly to indicate that it is received through + * control channel */ + p_data->p_buf->layer_specific = AVCT_DATA_CTRL; + + /* reassemble message; if no message available (we received a fragment) return */ + if ((p_data->p_buf = avct_lcb_msg_asmbl(p_lcb, p_data->p_buf)) == NULL) { + return; + } + + p = (UINT8 *)(p_data->p_buf + 1) + p_data->p_buf->offset; + + /* parse header byte */ + AVCT_PRS_HDR(p, label, type, cr_ipid); + UNUSED(type); + + /* check for invalid cr_ipid */ + if (cr_ipid == AVCT_CR_IPID_INVALID) { + AVCT_TRACE_WARNING("Invalid cr_ipid %d", cr_ipid); + osi_free(p_data->p_buf); + p_data->p_buf = NULL; + return; + } + + /* parse and lookup PID */ + BE_STREAM_TO_UINT16(pid, p); + if ((p_ccb = avct_lcb_has_pid(p_lcb, pid)) != NULL) { + /* PID found; send msg up, adjust bt hdr and call msg callback */ + p_data->p_buf->offset += AVCT_HDR_LEN_SINGLE; + p_data->p_buf->len -= AVCT_HDR_LEN_SINGLE; + (*p_ccb->cc.p_msg_cback)(avct_ccb_to_idx(p_ccb), label, cr_ipid, p_data->p_buf); + } else { + /* PID not found; drop message */ + AVCT_TRACE_WARNING("No ccb for PID=%x", pid); + osi_free(p_data->p_buf); + p_data->p_buf = NULL; + + /* if command send reject */ + if (cr_ipid == AVCT_CMD) { + if ((p_buf = (BT_HDR *) osi_malloc(AVCT_CMD_BUF_SIZE)) != NULL) { + p_buf->len = AVCT_HDR_LEN_SINGLE; + p_buf->offset = AVCT_MSG_OFFSET - AVCT_HDR_LEN_SINGLE; + p = (UINT8 *)(p_buf + 1) + p_buf->offset; + AVCT_BLD_HDR(p, label, AVCT_PKT_TYPE_SINGLE, AVCT_REJ); + UINT16_TO_BE_STREAM(p, pid); + L2CA_DataWrite(p_lcb->ch_lcid, p_buf); + } + } + } +} + +#endif /* #if (defined(AVCT_INCLUDED) && AVCT_INCLUDED == TRUE) */ diff --git a/lib/bt/host/bluedroid/stack/avct/include/avct_defs.h b/lib/bt/host/bluedroid/stack/avct/include/avct_defs.h new file mode 100644 index 00000000..30b8859f --- /dev/null +++ b/lib/bt/host/bluedroid/stack/avct/include/avct_defs.h @@ -0,0 +1,62 @@ +/****************************************************************************** + * + * Copyright (C) 2003-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This contains constants definitions and other information from the AVCTP + * specification. This file is intended for use internal to AVCT only. + * + ******************************************************************************/ +#ifndef AVCT_DEFS_H +#define AVCT_DEFS_H + +/***************************************************************************** +** constants +*****************************************************************************/ + +/* packet type */ +#define AVCT_PKT_TYPE_SINGLE 0 /* single packet */ +#define AVCT_PKT_TYPE_START 1 /* start packet */ +#define AVCT_PKT_TYPE_CONT 2 /* continue packet */ +#define AVCT_PKT_TYPE_END 3 /* end packet */ + +/* header lengths for different packet types */ +#define AVCT_HDR_LEN_SINGLE 3 +#define AVCT_HDR_LEN_START 4 +#define AVCT_HDR_LEN_CONT 1 +#define AVCT_HDR_LEN_END 1 + +/* invalid cr+ipid value */ +#define AVCT_CR_IPID_INVALID 1 + +/***************************************************************************** +** message parsing and building macros +*****************************************************************************/ + +#define AVCT_BLD_HDR(p, label, type, cr_ipid) \ + *(p)++ = ((label) << 4) | ((type) << 2) | (cr_ipid); + +#define AVCT_PRS_HDR(p, label, type, cr_ipid) \ + label = *(p) >> 4; \ + type = (*(p) >> 2) & 3; \ + cr_ipid = *(p)++ & 3; + +#define AVCT_PRS_PKT_TYPE(p, type) \ + type = (*(p) >> 2) & 3; + +#endif /* AVCT_DEFS_H */ diff --git a/lib/bt/host/bluedroid/stack/avct/include/avct_int.h b/lib/bt/host/bluedroid/stack/avct/include/avct_int.h new file mode 100644 index 00000000..0a91a96f --- /dev/null +++ b/lib/bt/host/bluedroid/stack/avct/include/avct_int.h @@ -0,0 +1,237 @@ +/****************************************************************************** + * + * Copyright (C) 2003-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains interfaces which are internal to AVCTP. + * + ******************************************************************************/ +#ifndef AVCT_INT_H +#define AVCT_INT_H + +#include "stack/avct_api.h" +#include "avct_defs.h" +#include "stack/l2c_api.h" +#include "osi/fixed_queue.h" + +/***************************************************************************** +** constants +*****************************************************************************/ + +/* lcb state machine events */ +enum { + AVCT_LCB_UL_BIND_EVT, + AVCT_LCB_UL_UNBIND_EVT, + AVCT_LCB_UL_MSG_EVT, + AVCT_LCB_INT_CLOSE_EVT, + AVCT_LCB_LL_OPEN_EVT, + AVCT_LCB_LL_CLOSE_EVT, + AVCT_LCB_LL_MSG_EVT, + AVCT_LCB_LL_CONG_EVT +}; + + +/* "states" used for L2CAP channel */ +#define AVCT_CH_IDLE 0 /* No connection */ +#define AVCT_CH_CONN 1 /* Waiting for connection confirm */ +#define AVCT_CH_CFG 2 /* Waiting for configuration complete */ +#define AVCT_CH_OPEN 3 /* Channel opened */ + +/* "no event" indicator used by ccb dealloc */ +#define AVCT_NO_EVT 0xFF + +/***************************************************************************** +** data types +*****************************************************************************/ +/* sub control block type - common data members for tAVCT_LCB and tAVCT_BCB */ +typedef struct { + UINT16 peer_mtu; /* peer l2c mtu */ + UINT16 ch_result; /* L2CAP connection result value */ + UINT16 ch_lcid; /* L2CAP channel LCID */ + UINT8 allocated; /* 0, not allocated. index+1, otherwise. */ + UINT8 state; /* The state machine state */ + UINT8 ch_state; /* L2CAP channel state */ + UINT8 ch_flags; /* L2CAP configuration flags */ +} tAVCT_SCB; + +/* link control block type */ +typedef struct { + UINT16 peer_mtu; /* peer l2c mtu */ + UINT16 ch_result; /* L2CAP connection result value */ + UINT16 ch_lcid; /* L2CAP channel LCID */ + UINT8 allocated; /* 0, not allocated. index+1, otherwise. */ + UINT8 state; /* The state machine state */ + UINT8 ch_state; /* L2CAP channel state */ + UINT8 ch_flags; /* L2CAP configuration flags */ + BT_HDR *p_rx_msg; /* Message being reassembled */ + UINT16 conflict_lcid; /* L2CAP channel LCID */ + BD_ADDR peer_addr; /* BD address of peer */ + fixed_queue_t *tx_q; /* Transmit data buffer queue */ + BOOLEAN cong; /* TRUE, if congested */ +} tAVCT_LCB; + +/* browse control block type */ +typedef struct { + UINT16 peer_mtu; /* peer l2c mtu */ + UINT16 ch_result; /* L2CAP connection result value */ + UINT16 ch_lcid; /* L2CAP channel LCID */ + UINT8 allocated; /* 0, not allocated. index+1, otherwise. */ + UINT8 state; /* The state machine state */ + UINT8 ch_state; /* L2CAP channel state */ + UINT8 ch_flags; /* L2CAP configuration flags */ + BT_HDR *p_tx_msg; /* Message to be sent - in case the browsing channel is not open when MsgReg is called */ + UINT8 ch_close; /* CCB index+1, if CCB initiated channel close */ +} tAVCT_BCB; + +#define AVCT_ALOC_LCB 0x01 +#define AVCT_ALOC_BCB 0x02 +/* connection control block */ +typedef struct { + tAVCT_CC cc; /* parameters from connection creation */ + tAVCT_LCB *p_lcb; /* Associated LCB */ + tAVCT_BCB *p_bcb; /* associated BCB */ + BOOLEAN ch_close; /* Whether CCB initiated channel close */ + UINT8 allocated; /* Whether LCB/BCB is allocated */ +} tAVCT_CCB; + +/* data type associated with UL_MSG_EVT */ +typedef struct { + BT_HDR *p_buf; + tAVCT_CCB *p_ccb; + UINT8 label; + UINT8 cr; +} tAVCT_UL_MSG; + +/* union associated with lcb state machine events */ +typedef union { + tAVCT_UL_MSG ul_msg; + BT_HDR *p_buf; + tAVCT_CCB *p_ccb; + UINT16 result; + BOOLEAN cong; + UINT8 err_code; +} tAVCT_LCB_EVT; + +/* Control block for AVCT */ +typedef struct { + tAVCT_LCB lcb[AVCT_NUM_LINKS]; /* link control blocks */ + tAVCT_BCB bcb[AVCT_NUM_LINKS]; /* browse control blocks */ + tAVCT_CCB ccb[AVCT_NUM_CONN]; /* connection control blocks */ + UINT16 mtu; /* our L2CAP MTU */ + UINT16 mtu_br; /* our L2CAP MTU for the Browsing channel */ + UINT8 trace_level; /* trace level */ +} tAVCT_CB; + +/***************************************************************************** +** function declarations +*****************************************************************************/ + +/* LCB function declarations */ +extern void avct_lcb_event(tAVCT_LCB *p_lcb, UINT8 event, tAVCT_LCB_EVT *p_data); +#if (AVCT_BROWSE_INCLUDED == TRUE) +extern void avct_bcb_event(tAVCT_BCB *p_bcb, UINT8 event, tAVCT_LCB_EVT *p_data); +extern void avct_close_bcb(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data); +extern tAVCT_LCB *avct_lcb_by_bcb(tAVCT_BCB *p_bcb); +extern tAVCT_BCB *avct_bcb_by_lcb(tAVCT_LCB *p_lcb); +extern BOOLEAN avct_bcb_last_ccb(tAVCT_BCB *p_bcb, tAVCT_CCB *p_ccb_last); +extern tAVCT_BCB *avct_bcb_by_lcid(UINT16 lcid); +#endif +extern tAVCT_LCB *avct_lcb_by_bd(BD_ADDR bd_addr); +extern tAVCT_LCB *avct_lcb_alloc(BD_ADDR bd_addr); +extern void avct_lcb_dealloc(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data); +extern tAVCT_LCB *avct_lcb_by_lcid(UINT16 lcid); +extern tAVCT_CCB *avct_lcb_has_pid(tAVCT_LCB *p_lcb, UINT16 pid); +extern BOOLEAN avct_lcb_last_ccb(tAVCT_LCB *p_lcb, tAVCT_CCB *p_ccb_last); + +/* LCB action functions */ +extern void avct_lcb_chnl_open(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data); +extern void avct_lcb_unbind_disc(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data); +extern void avct_lcb_open_ind(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data); +extern void avct_lcb_open_fail(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data); +extern void avct_lcb_close_ind(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data); +extern void avct_lcb_close_cfm(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data); +extern void avct_lcb_bind_conn(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data); +extern void avct_lcb_chk_disc(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data); +extern void avct_lcb_chnl_disc(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data); +extern void avct_lcb_bind_fail(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data); +extern void avct_lcb_cong_ind(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data); +extern void avct_lcb_discard_msg(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data); +extern void avct_lcb_send_msg(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data); +extern void avct_lcb_msg_ind(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data); +extern void avct_lcb_free_msg_ind(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data); + +/* BCB action functions */ +#if (AVCT_BROWSE_INCLUDED == TRUE) +typedef void (*tAVCT_BCB_ACTION)(tAVCT_BCB *p_bcb, tAVCT_LCB_EVT *p_data); +extern void avct_bcb_chnl_open(tAVCT_BCB *p_bcb, tAVCT_LCB_EVT *p_data); +extern void avct_bcb_unbind_disc(tAVCT_BCB *p_bcb, tAVCT_LCB_EVT *p_data); +extern void avct_bcb_open_ind(tAVCT_BCB *p_bcb, tAVCT_LCB_EVT *p_data); +extern void avct_bcb_open_fail(tAVCT_BCB *p_bcb, tAVCT_LCB_EVT *p_data); +extern void avct_bcb_close_ind(tAVCT_BCB *p_bcb, tAVCT_LCB_EVT *p_data); +extern void avct_bcb_close_cfm(tAVCT_BCB *p_bcb, tAVCT_LCB_EVT *p_data); +extern void avct_bcb_bind_conn(tAVCT_BCB *p_bcb, tAVCT_LCB_EVT *p_data); +extern void avct_bcb_chk_disc(tAVCT_BCB *p_bcb, tAVCT_LCB_EVT *p_data); +extern void avct_bcb_chnl_disc(tAVCT_BCB *p_bcb, tAVCT_LCB_EVT *p_data); +extern void avct_bcb_bind_fail(tAVCT_BCB *p_bcb, tAVCT_LCB_EVT *p_data); +extern void avct_bcb_cong_ind(tAVCT_BCB *p_bcb, tAVCT_LCB_EVT *p_data); +extern void avct_bcb_discard_msg(tAVCT_BCB *p_bcb, tAVCT_LCB_EVT *p_data); +extern void avct_bcb_send_msg(tAVCT_BCB *p_bcb, tAVCT_LCB_EVT *p_data); +extern void avct_bcb_msg_ind(tAVCT_BCB *p_bcb, tAVCT_LCB_EVT *p_data); +extern void avct_bcb_free_msg_ind(tAVCT_BCB *p_bcb, tAVCT_LCB_EVT *p_data); + +extern void avct_bcb_dealloc(tAVCT_BCB *p_bcb, tAVCT_LCB_EVT *p_data); + +extern const tAVCT_BCB_ACTION avct_bcb_action[]; +extern const UINT8 avct_lcb_pkt_type_len[]; +extern const tL2CAP_FCR_OPTS avct_l2c_br_fcr_opts_def; +#endif + +/* CCB function declarations */ +extern tAVCT_CCB *avct_ccb_alloc(tAVCT_CC *p_cc); +extern void avct_ccb_dealloc(tAVCT_CCB *p_ccb, UINT8 event, UINT16 result, BD_ADDR bd_addr); +extern UINT8 avct_ccb_to_idx(tAVCT_CCB *p_ccb); +extern tAVCT_CCB *avct_ccb_by_idx(UINT8 idx); + + +/***************************************************************************** +** global data +*****************************************************************************/ +#ifdef __cplusplus +extern "C" +{ +#endif + +/* Main control block */ +#if AVCT_DYNAMIC_MEMORY == FALSE +extern tAVCT_CB avct_cb; +#else +extern tAVCT_CB *avct_cb_ptr; +#define avct_cb (*avct_cb_ptr) +#endif + +/* L2CAP callback registration structure */ +extern const tL2CAP_APPL_INFO avct_l2c_appl; +#if (AVCT_BROWSE_INCLUDED == TRUE) +extern const tL2CAP_APPL_INFO avct_l2c_br_appl; +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* AVCT_INT_H */ diff --git a/lib/bt/host/bluedroid/stack/avdt/avdt_ad.c b/lib/bt/host/bluedroid/stack/avdt/avdt_ad.c new file mode 100644 index 00000000..b24c952d --- /dev/null +++ b/lib/bt/host/bluedroid/stack/avdt/avdt_ad.c @@ -0,0 +1,596 @@ +/****************************************************************************** + * + * Copyright (C) 2002-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This module contains the AVDTP adaption layer. + * + ******************************************************************************/ + +#include +#include "common/bt_trace.h" +#include + +#include "stack/bt_types.h" +#include "common/bt_target.h" +#include "common/bt_defs.h" +#include "stack/avdt_api.h" +#include "stack/avdtc_api.h" +#include "avdt_int.h" +#include "stack/l2c_api.h" +#include "stack/l2cdefs.h" +#include "osi/allocator.h" + +#if (defined(AVDT_INCLUDED) && AVDT_INCLUDED == TRUE) +/******************************************************************************* +** +** Function avdt_ad_type_to_tcid +** +** Description Derives the TCID from the channel type and SCB. +** +** +** Returns TCID value. +** +*******************************************************************************/ +UINT8 avdt_ad_type_to_tcid(UINT8 type, tAVDT_SCB *p_scb) +{ + UINT8 scb_idx; + + if (type == AVDT_CHAN_SIG) { + return 0; + } else { + scb_idx = avdt_scb_to_hdl(p_scb) - 1; + /* + AVDT_TRACE_DEBUG("type: %d, tcid: %d", type, ((scb_idx * (AVDT_CHAN_NUM_TYPES - 1)) + type)); + */ + return ((scb_idx * (AVDT_CHAN_NUM_TYPES - 1)) + type); + } +} + +/******************************************************************************* +** +** Function avdt_ad_tcid_to_type +** +** Description Derives the channel type from the TCID. +** +** +** Returns Channel type value. +** +*******************************************************************************/ +static UINT8 avdt_ad_tcid_to_type(UINT8 tcid) +{ + UINT8 type; + + if (tcid == 0) { + type = AVDT_CHAN_SIG; + } else { + /* tcid translates to type based on number of channels, as follows: + ** only media channel : tcid=1,2,3,4,5,6... type=1,1,1,1,1,1... + ** media and report : tcid=1,2,3,4,5,6... type=1,2,1,2,1,2... + ** media, report, recov : tcid=1,2,3,4,5,6... type=1,2,3,1,2,3... + */ + type = ((tcid + AVDT_CHAN_NUM_TYPES - 2) % (AVDT_CHAN_NUM_TYPES - 1)) + 1; + } + AVDT_TRACE_DEBUG("tcid: %d, type: %d\n", tcid, type); + return type; +} + + +/******************************************************************************* +** +** Function avdt_ad_init +** +** Description Initialize adaption layer. +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_ad_init(void) +{ + int i; + tAVDT_TC_TBL *p_tbl = avdt_cb.ad.tc_tbl; + memset(&avdt_cb.ad, 0, sizeof(tAVDT_AD)); + + /* make sure the peer_mtu is a valid value */ + for (i = 0; i < AVDT_NUM_TC_TBL; i++, p_tbl++) { + p_tbl->peer_mtu = L2CAP_DEFAULT_MTU; + } +} + + +/******************************************************************************* +** +** Function avdt_ad_tc_tbl_by_st +** +** Description Find adaption layer transport channel table entry matching +** the given state. +** +** +** Returns Pointer to matching entry. For control channel it returns +** the matching entry. For media or other it returns the +** first matching entry (there could be more than one). +** +*******************************************************************************/ +tAVDT_TC_TBL *avdt_ad_tc_tbl_by_st(UINT8 type, tAVDT_CCB *p_ccb, UINT8 state) +{ + int i; + tAVDT_TC_TBL *p_tbl = avdt_cb.ad.tc_tbl; + UINT8 ccb_idx; + + if (p_ccb == NULL) { + /* resending security req */ + for (i = 0; i < AVDT_NUM_TC_TBL; i++, p_tbl++) { + /* must be AVDT_CHAN_SIG - tcid always zero */ + if ((p_tbl->tcid == 0) && + (p_tbl->state == state)) { + break; + } + } + } else { + ccb_idx = avdt_ccb_to_idx(p_ccb); + + for (i = 0; i < AVDT_NUM_TC_TBL; i++, p_tbl++) { + if (type == AVDT_CHAN_SIG) { + /* if control channel, tcid always zero */ + if ((p_tbl->tcid == 0) && + (p_tbl->ccb_idx == ccb_idx) && + (p_tbl->state == state)) { + break; + } + } else { + /* if other channel, tcid is always > zero */ + if ((p_tbl->tcid > 0) && + (p_tbl->ccb_idx == ccb_idx) && + (p_tbl->state == state)) { + break; + } + } + } + } + + /* if nothing found return null */ + if (i == AVDT_NUM_TC_TBL) { + p_tbl = NULL; + } + + return p_tbl; +} + + +/******************************************************************************* +** +** Function avdt_ad_tc_tbl_by_lcid +** +** Description Find adaption layer transport channel table entry by LCID. +** +** +** Returns Pointer to entry. +** +*******************************************************************************/ +tAVDT_TC_TBL *avdt_ad_tc_tbl_by_lcid(UINT16 lcid) +{ + UINT8 idx; + + idx = avdt_cb.ad.lcid_tbl[lcid - L2CAP_BASE_APPL_CID]; + + if (idx < AVDT_NUM_TC_TBL) { + return &avdt_cb.ad.tc_tbl[idx]; + } else { + return NULL; + } +} + + +/******************************************************************************* +** +** Function avdt_ad_tc_tbl_by_type +** +** Description This function retrieves the transport channel table entry +** for a particular channel. +** +** +** Returns Pointer to transport channel table entry. +** +*******************************************************************************/ +tAVDT_TC_TBL *avdt_ad_tc_tbl_by_type(UINT8 type, tAVDT_CCB *p_ccb, tAVDT_SCB *p_scb) +{ + UINT8 tcid; + int i; + tAVDT_TC_TBL *p_tbl = avdt_cb.ad.tc_tbl; + UINT8 ccb_idx = avdt_ccb_to_idx(p_ccb); + + /* get tcid from type, scb */ + tcid = avdt_ad_type_to_tcid(type, p_scb); + + for (i = 0; i < AVDT_NUM_TC_TBL; i++, p_tbl++) { + if ((p_tbl->tcid == tcid) && (p_tbl->ccb_idx == ccb_idx)) { + break; + } + } + + assert(i != AVDT_NUM_TC_TBL); + + return p_tbl; +} + + +/******************************************************************************* +** +** Function avdt_ad_tc_tbl_alloc +** +** Description Allocate an entry in the traffic channel table. +** +** +** Returns Pointer to entry. +** +*******************************************************************************/ +tAVDT_TC_TBL *avdt_ad_tc_tbl_alloc(tAVDT_CCB *p_ccb) +{ + int i; + tAVDT_TC_TBL *p_tbl = avdt_cb.ad.tc_tbl; + + /* find next free entry in tc table */ + for (i = 0; i < AVDT_NUM_TC_TBL; i++, p_tbl++) { + if (p_tbl->state == AVDT_AD_ST_UNUSED) { + break; + } + } + + /* sanity check */ + assert(i != AVDT_NUM_TC_TBL); + + + /* initialize entry */ + p_tbl->peer_mtu = L2CAP_DEFAULT_MTU; + p_tbl->cfg_flags = 0; + p_tbl->ccb_idx = avdt_ccb_to_idx(p_ccb); + p_tbl->state = AVDT_AD_ST_IDLE; + return p_tbl; + +} + +/******************************************************************************* +** +** Function avdt_ad_tc_tbl_to_idx +** +** Description Convert a transport channel table entry to an index. +** +** +** Returns Index value. +** +*******************************************************************************/ +UINT8 avdt_ad_tc_tbl_to_idx(tAVDT_TC_TBL *p_tbl) +{ + AVDT_TRACE_DEBUG("avdt_ad_tc_tbl_to_idx: %d\n", (p_tbl - avdt_cb.ad.tc_tbl)); + /* use array arithmetic to determine index */ + return (UINT8) (p_tbl - avdt_cb.ad.tc_tbl); +} + +/******************************************************************************* +** +** Function avdt_ad_tc_close_ind +** +** Description This function is called by the L2CAP interface when the +** L2CAP channel is closed. It looks up the CCB or SCB for +** the channel and sends it a close event. The reason +** parameter is the same value passed by the L2CAP +** callback function. +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_ad_tc_close_ind(tAVDT_TC_TBL *p_tbl, UINT16 reason) +{ + tAVDT_CCB *p_ccb; + tAVDT_SCB *p_scb; + tAVDT_SCB_TC_CLOSE close; + // UNUSED(reason); + + close.old_tc_state = p_tbl->state; + + /* clear avdt_ad_tc_tbl entry */ + p_tbl->state = AVDT_AD_ST_UNUSED; + p_tbl->cfg_flags = 0; + p_tbl->peer_mtu = L2CAP_DEFAULT_MTU; + + AVDT_TRACE_DEBUG("avdt_ad_tc_close_ind tcid: %d, old: %d\n", + p_tbl->tcid, close.old_tc_state); + + /* if signaling channel, notify ccb that channel open */ + if (p_tbl->tcid == 0) { + p_ccb = avdt_ccb_by_idx(p_tbl->ccb_idx); + p_ccb->disc_rsn = (reason == AVDT_DISC_RSN_ABNORMAL) ? AVDT_DISC_RSN_ABNORMAL : AVDT_DISC_RSN_NORMAL; + avdt_ccb_event(p_ccb, AVDT_CCB_LL_CLOSE_EVT, NULL); + } + /* if media or other channel, notify scb that channel close */ + else { + /* look up scb in stream routing table by ccb, tcid */ + p_scb = avdt_scb_by_hdl(avdt_cb.ad.rt_tbl[p_tbl->ccb_idx][p_tbl->tcid].scb_hdl); + if (p_scb != NULL) { + close.tcid = p_tbl->tcid; + close.type = avdt_ad_tcid_to_type(p_tbl->tcid); + close.disc_rsn = (reason == AVDT_DISC_RSN_ABNORMAL) ? AVDT_DISC_RSN_ABNORMAL : AVDT_DISC_RSN_NORMAL; + avdt_scb_event(p_scb, AVDT_SCB_TC_CLOSE_EVT, (tAVDT_SCB_EVT *)&close); + } + } +} + +/******************************************************************************* +** +** Function avdt_ad_tc_open_ind +** +** Description This function is called by the L2CAP interface when +** the L2CAP channel is opened. It looks up the CCB or SCB +** for the channel and sends it an open event. +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_ad_tc_open_ind(tAVDT_TC_TBL *p_tbl) +{ + tAVDT_CCB *p_ccb; + tAVDT_SCB *p_scb; + tAVDT_OPEN open; + tAVDT_EVT_HDR evt; + + p_tbl->state = AVDT_AD_ST_OPEN; + + /* if signaling channel, notify ccb that channel open */ + if (p_tbl->tcid == 0) { + /* set the signal channel to use high priority within the ACL link */ + L2CA_SetTxPriority(avdt_cb.ad.rt_tbl[p_tbl->ccb_idx][AVDT_CHAN_SIG].lcid, L2CAP_CHNL_PRIORITY_HIGH); + + p_ccb = avdt_ccb_by_idx(p_tbl->ccb_idx); + /* use err_param to indicate the role of connection. + * AVDT_ACP, if ACP */ + evt.err_param = AVDT_INT; + if (p_tbl->cfg_flags & AVDT_L2C_CFG_CONN_ACP) { + evt.err_param = AVDT_ACP; + } + avdt_ccb_event(p_ccb, AVDT_CCB_LL_OPEN_EVT, (tAVDT_CCB_EVT *)&evt); + } + /* if media or other channel, notify scb that channel open */ + else { + /* look up scb in stream routing table by ccb, tcid */ + p_scb = avdt_scb_by_hdl(avdt_cb.ad.rt_tbl[p_tbl->ccb_idx][p_tbl->tcid].scb_hdl); + + /* put lcid in event data */ + if (p_scb != NULL) { + open.peer_mtu = p_tbl->peer_mtu; + open.lcid = avdt_cb.ad.rt_tbl[p_tbl->ccb_idx][p_tbl->tcid].lcid; + open.hdr.err_code = avdt_ad_tcid_to_type(p_tbl->tcid); + avdt_scb_event(p_scb, AVDT_SCB_TC_OPEN_EVT, (tAVDT_SCB_EVT *) &open); + } + } +} + + +/******************************************************************************* +** +** Function avdt_ad_tc_cong_ind +** +** Description This function is called by the L2CAP interface layer when +** L2CAP calls the congestion callback. It looks up the CCB +** or SCB for the channel and sends it a congestion event. +** The is_congested parameter is the same value passed by +** the L2CAP callback function. +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_ad_tc_cong_ind(tAVDT_TC_TBL *p_tbl, BOOLEAN is_congested) +{ + tAVDT_CCB *p_ccb; + tAVDT_SCB *p_scb; + + /* if signaling channel, notify ccb of congestion */ + if (p_tbl->tcid == 0) { + p_ccb = avdt_ccb_by_idx(p_tbl->ccb_idx); + avdt_ccb_event(p_ccb, AVDT_CCB_LL_CONG_EVT, (tAVDT_CCB_EVT *) &is_congested); + } + /* if media or other channel, notify scb that channel open */ + else { + /* look up scb in stream routing table by ccb, tcid */ + p_scb = avdt_scb_by_hdl(avdt_cb.ad.rt_tbl[p_tbl->ccb_idx][p_tbl->tcid].scb_hdl); + if (p_scb != NULL) { + avdt_scb_event(p_scb, AVDT_SCB_TC_CONG_EVT, (tAVDT_SCB_EVT *) &is_congested); + } + } +} + + +/******************************************************************************* +** +** Function avdt_ad_tc_data_ind +** +** Description This function is called by the L2CAP interface layer when +** incoming data is received from L2CAP. It looks up the CCB +** or SCB for the channel and routes the data accordingly. +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_ad_tc_data_ind(tAVDT_TC_TBL *p_tbl, BT_HDR *p_buf) +{ + tAVDT_CCB *p_ccb; + tAVDT_SCB *p_scb; + + /* store type (media, recovery, reporting) */ + p_buf->layer_specific = avdt_ad_tcid_to_type(p_tbl->tcid); + + + /* if signaling channel, handle control message */ + if (p_tbl->tcid == 0) { + p_ccb = avdt_ccb_by_idx(p_tbl->ccb_idx); + avdt_msg_ind(p_ccb, p_buf); + } + /* if media or other channel, send event to scb */ + else { + p_scb = avdt_scb_by_hdl(avdt_cb.ad.rt_tbl[p_tbl->ccb_idx][p_tbl->tcid].scb_hdl); + if (p_scb != NULL) { + avdt_scb_event(p_scb, AVDT_SCB_TC_DATA_EVT, (tAVDT_SCB_EVT *) &p_buf); + } else { + osi_free(p_buf); + AVDT_TRACE_ERROR(" avdt_ad_tc_data_ind buffer freed"); + } + } +} + +/******************************************************************************* +** +** Function avdt_ad_write_req +** +** Description This function is called by a CCB or SCB to send data to a +** transport channel. It looks up the LCID of the channel +** based on the type, CCB, and SCB (if present). Then it +** passes the data to L2CA_DataWrite(). +** +** +** Returns AVDT_AD_SUCCESS, if data accepted, else FALSE +** AVDT_AD_CONGESTED, if data accepted and the channel is congested +** AVDT_AD_FAILED, if error +** +*******************************************************************************/ +UINT8 avdt_ad_write_req(UINT8 type, tAVDT_CCB *p_ccb, tAVDT_SCB *p_scb, BT_HDR *p_buf) +{ + UINT8 tcid; + + /* get tcid from type, scb */ + tcid = avdt_ad_type_to_tcid(type, p_scb); + + + return L2CA_DataWrite(avdt_cb.ad.rt_tbl[avdt_ccb_to_idx(p_ccb)][tcid].lcid, p_buf); +} + + +/******************************************************************************* +** +** Function avdt_ad_open_req +** +** Description This function is called by a CCB or SCB to open a transport +** channel. This function allocates and initializes a +** transport channel table entry. The channel can be opened +** in two roles: as an initiator or acceptor. When opened +** as an initiator the function will start an L2CAP connection. +** When opened as an acceptor the function simply configures +** the table entry to listen for an incoming channel. +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_ad_open_req(UINT8 type, tAVDT_CCB *p_ccb, tAVDT_SCB *p_scb, UINT8 role) +{ + tAVDT_TC_TBL *p_tbl; + UINT16 lcid; + + if ((p_tbl = avdt_ad_tc_tbl_alloc(p_ccb)) == NULL) { + AVDT_TRACE_ERROR("avdt_ad_open_req: Cannot allocate p_tbl"); + return; + } + + + p_tbl->tcid = avdt_ad_type_to_tcid(type, p_scb); + AVDT_TRACE_DEBUG("avdt_ad_open_req: type: %d, role: %d, tcid:%d\n", + type, role, p_tbl->tcid); + + if (type == AVDT_CHAN_SIG) { + /* if signaling, get mtu from registration control block */ + p_tbl->my_mtu = avdt_cb.rcb.ctrl_mtu; + p_tbl->my_flush_to = L2CAP_DEFAULT_FLUSH_TO; + } else { + /* otherwise get mtu from scb */ + p_tbl->my_mtu = p_scb->cs.mtu; + p_tbl->my_flush_to = p_scb->cs.flush_to; + + /* also set scb_hdl in rt_tbl */ + avdt_cb.ad.rt_tbl[avdt_ccb_to_idx(p_ccb)][p_tbl->tcid].scb_hdl = avdt_scb_to_hdl(p_scb); + AVDT_TRACE_DEBUG("avdt_cb.ad.rt_tbl[%d][%d].scb_hdl = %d\n", + avdt_ccb_to_idx(p_ccb), p_tbl->tcid, + avdt_scb_to_hdl(p_scb)); + } + + /* if we're acceptor, we're done; just sit back and listen */ + if (role == AVDT_ACP) { + p_tbl->state = AVDT_AD_ST_ACP; + } + /* else we're inititator, start the L2CAP connection */ + else { + p_tbl->state = AVDT_AD_ST_CONN; + + /* call l2cap connect req */ + if ((lcid = L2CA_ConnectReq(AVDT_PSM, p_ccb->peer_addr)) != 0) { + /* if connect req ok, store tcid in lcid table */ + avdt_cb.ad.lcid_tbl[lcid - L2CAP_BASE_APPL_CID] = avdt_ad_tc_tbl_to_idx(p_tbl); + AVDT_TRACE_DEBUG("avdt_cb.ad.lcid_tbl[%d] = %d\n", + (lcid - L2CAP_BASE_APPL_CID), avdt_ad_tc_tbl_to_idx(p_tbl)); + + avdt_cb.ad.rt_tbl[avdt_ccb_to_idx(p_ccb)][p_tbl->tcid].lcid = lcid; + AVDT_TRACE_DEBUG("avdt_cb.ad.rt_tbl[%d][%d].lcid = 0x%x\n", + avdt_ccb_to_idx(p_ccb), p_tbl->tcid, + lcid); + } else { + /* if connect req failed, call avdt_ad_tc_close_ind() */ + avdt_ad_tc_close_ind(p_tbl, 0); + } + } +} + +/******************************************************************************* +** +** Function avdt_ad_close_req +** +** Description This function is called by a CCB or SCB to close a +** transport channel. The function looks up the LCID for the +** channel and calls L2CA_DisconnectReq(). +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_ad_close_req(UINT8 type, tAVDT_CCB *p_ccb, tAVDT_SCB *p_scb) +{ + UINT8 tcid; + tAVDT_TC_TBL *p_tbl; + + p_tbl = avdt_ad_tc_tbl_by_type(type, p_ccb, p_scb); + AVDT_TRACE_DEBUG("avdt_ad_close_req state: %d\n", p_tbl->state); + + switch (p_tbl->state) { + case AVDT_AD_ST_UNUSED: + /* probably for reporting */ + break; + case AVDT_AD_ST_ACP: + /* if we're listening on this channel, send ourselves a close ind */ + avdt_ad_tc_close_ind(p_tbl, 0); + break; + default: + /* get tcid from type, scb */ + tcid = avdt_ad_type_to_tcid(type, p_scb); + + /* call l2cap disconnect req */ + L2CA_DisconnectReq(avdt_cb.ad.rt_tbl[avdt_ccb_to_idx(p_ccb)][tcid].lcid); + } +} + +#endif /* #if (defined(AVDT_INCLUDED) && AVDT_INCLUDED == TRUE) */ diff --git a/lib/bt/host/bluedroid/stack/avdt/avdt_api.c b/lib/bt/host/bluedroid/stack/avdt/avdt_api.c new file mode 100644 index 00000000..f37db816 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/avdt/avdt_api.c @@ -0,0 +1,1303 @@ +/****************************************************************************** + * + * Copyright (C) 2002-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This module contains API of the audio/video distribution transport + * protocol. + * + ******************************************************************************/ + +#include +#include "stack/bt_types.h" +#include "common/bt_target.h" +#include "stack/avdt_api.h" +#include "stack/avdtc_api.h" +#include "avdt_int.h" +#include "stack/l2c_api.h" +#include "stack/btm_api.h" +#include "stack/btu.h" +#include "osi/allocator.h" + +#if (defined(AVDT_INCLUDED) && AVDT_INCLUDED == TRUE) + +/* Control block for AVDT */ +#if AVDT_DYNAMIC_MEMORY == FALSE +tAVDT_CB avdt_cb; +#else +tAVDT_CB *avdt_cb_ptr; +#endif + +/******************************************************************************* +** +** Function avdt_process_timeout +** +** Description This function is called by BTU when an AVDTP timer +** expires. The function sends a timer event to the +** appropriate CCB or SCB state machine. +** +** This function is for use internal to the stack only. +** +** +** Returns void +** +*******************************************************************************/ +void avdt_process_timeout(TIMER_LIST_ENT *p_tle) +{ + UINT8 event = 0; + UINT8 err_code = AVDT_ERR_TIMEOUT; + + switch (p_tle->event) { + case BTU_TTYPE_AVDT_SCB_DELAY_RPT: + event = AVDT_SCB_DELAY_RPT_RSP_TOUT_EVT; + break; + case BTU_TTYPE_AVDT_CCB_RET: + event = AVDT_CCB_RET_TOUT_EVT + AVDT_CCB_MKR; + break; + + case BTU_TTYPE_AVDT_CCB_RSP: + event = AVDT_CCB_RSP_TOUT_EVT + AVDT_CCB_MKR; + break; + + case BTU_TTYPE_AVDT_CCB_IDLE: + event = AVDT_CCB_IDLE_TOUT_EVT + AVDT_CCB_MKR; + break; + + case BTU_TTYPE_AVDT_SCB_TC: + event = AVDT_SCB_TC_TOUT_EVT; + break; + + default: + break; + } + + if (event & AVDT_CCB_MKR) { + avdt_ccb_event((tAVDT_CCB *) p_tle->param, (UINT8) (event & ~AVDT_CCB_MKR), + (tAVDT_CCB_EVT *) &err_code); + } else { + avdt_scb_event((tAVDT_SCB *) p_tle->param, event, NULL); + } +} + +/******************************************************************************* +** +** Function AVDT_Register +** +** Description This is the system level registration function for the +** AVDTP protocol. This function initializes AVDTP and +** prepares the protocol stack for its use. This function +** must be called once by the system or platform using AVDTP +** before the other functions of the API an be used. +** +** +** Returns void +** +*******************************************************************************/ +void AVDT_Register(tAVDT_REG *p_reg, tAVDT_CTRL_CBACK *p_cback) +{ + /* register PSM with L2CAP */ + L2CA_Register(AVDT_PSM, (tL2CAP_APPL_INFO *) &avdt_l2c_appl); + + /* set security level */ + BTM_SetSecurityLevel(TRUE, "", BTM_SEC_SERVICE_AVDTP, p_reg->sec_mask, + AVDT_PSM, BTM_SEC_PROTO_AVDT, AVDT_CHAN_SIG); + BTM_SetSecurityLevel(FALSE, "", BTM_SEC_SERVICE_AVDTP, p_reg->sec_mask, + AVDT_PSM, BTM_SEC_PROTO_AVDT, AVDT_CHAN_SIG); + + /* do not use security on the media channel */ + BTM_SetSecurityLevel(TRUE, "", BTM_SEC_SERVICE_AVDTP_NOSEC, BTM_SEC_NONE, + AVDT_PSM, BTM_SEC_PROTO_AVDT, AVDT_CHAN_MEDIA); + BTM_SetSecurityLevel(FALSE, "", BTM_SEC_SERVICE_AVDTP_NOSEC, BTM_SEC_NONE, + AVDT_PSM, BTM_SEC_PROTO_AVDT, AVDT_CHAN_MEDIA); + +#if AVDT_REPORTING == TRUE + /* do not use security on the reporting channel */ + BTM_SetSecurityLevel(TRUE, "", BTM_SEC_SERVICE_AVDTP_NOSEC, BTM_SEC_NONE, + AVDT_PSM, BTM_SEC_PROTO_AVDT, AVDT_CHAN_REPORT); + BTM_SetSecurityLevel(FALSE, "", BTM_SEC_SERVICE_AVDTP_NOSEC, BTM_SEC_NONE, + AVDT_PSM, BTM_SEC_PROTO_AVDT, AVDT_CHAN_REPORT); +#endif + + /* initialize AVDTP data structures */ + avdt_scb_init(); + avdt_ccb_init(); + avdt_ad_init(); + + /* copy registration struct */ + memcpy(&avdt_cb.rcb, p_reg, sizeof(tAVDT_REG)); + avdt_cb.p_conn_cback = p_cback; +} + +/******************************************************************************* +** +** Function AVDT_Deregister +** +** Description This function is called to deregister use AVDTP protocol. +** It is called when AVDTP is no longer being used by any +** application in the system. Before this function can be +** called, all streams must be removed with AVDT_RemoveStream(). +** +** +** Returns void +** +*******************************************************************************/ +void AVDT_Deregister(void) +{ + /* deregister PSM with L2CAP */ + L2CA_Deregister(AVDT_PSM); +} + +/******************************************************************************* +** +** Function AVDT_SINK_Activate +** +** Description Activate SEP of A2DP Sink. In Use parameter is adjusted. +** In Use will be made false in case of activation. A2DP SRC +** will receive in_use as false and can open A2DP Sink +** connection +** +** Returns void. +** +*******************************************************************************/ +void AVDT_SINK_Activate(void) +{ + tAVDT_SCB *p_scb = &avdt_cb.scb[0]; + int i; + AVDT_TRACE_DEBUG("AVDT_SINK_Activate"); + /* for all allocated scbs */ + for (i = 0; i < AVDT_NUM_SEPS; i++, p_scb++) { + if ((p_scb->allocated) && (p_scb->cs.tsep == AVDT_TSEP_SNK)) { + AVDT_TRACE_DEBUG("AVDT_SINK_Activate found scb"); + p_scb->sink_activated = TRUE; + /* update in_use */ + p_scb->in_use = FALSE; + break; + } + } +} + +/******************************************************************************* +** +** Function AVDT_SINK_Deactivate +** +** Description Deactivate SEP of A2DP Sink. In Use parameter is adjusted. +** In Use will be made TRUE in case of activation. A2DP SRC +** will receive in_use as true and will not open A2DP Sink +** connection +** +** Returns void. +** +*******************************************************************************/ +void AVDT_SINK_Deactivate(void) +{ + tAVDT_SCB *p_scb = &avdt_cb.scb[0]; + int i; + AVDT_TRACE_DEBUG("AVDT_SINK_Deactivate"); + /* for all allocated scbs */ + for (i = 0; i < AVDT_NUM_SEPS; i++, p_scb++) { + if ((p_scb->allocated) && (p_scb->cs.tsep == AVDT_TSEP_SNK)) { + AVDT_TRACE_DEBUG("AVDT_SINK_Deactivate, found scb"); + p_scb->sink_activated = FALSE; + /* update in_use */ + p_scb->in_use = TRUE; + break; + } + } +} + +void AVDT_AbortReq(UINT8 handle) +{ + AVDT_TRACE_ERROR("%s\n", __func__); + + tAVDT_SCB *p_scb = avdt_scb_by_hdl(handle); + if (p_scb != NULL) { + avdt_scb_event(p_scb, AVDT_SCB_API_ABORT_REQ_EVT, NULL); + } else { + AVDT_TRACE_ERROR("%s Improper SCB, can not abort the stream\n", __func__); + } +} + +/******************************************************************************* +** +** Function AVDT_CreateStream +** +** Description Create a stream endpoint. After a stream endpoint is +** created an application can initiate a connection between +** this endpoint and an endpoint on a peer device. In +** addition, a peer device can discover, get the capabilities, +** and connect to this endpoint. +** +** +** Returns AVDT_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +UINT16 AVDT_CreateStream(UINT8 *p_handle, tAVDT_CS *p_cs) +{ + UINT16 result = AVDT_SUCCESS; + tAVDT_SCB *p_scb; + + /* Verify parameters; if invalid, return failure */ + if (((p_cs->cfg.psc_mask & (~AVDT_PSC)) != 0) || (p_cs->p_ctrl_cback == NULL)) { + result = AVDT_BAD_PARAMS; + } + /* Allocate scb; if no scbs, return failure */ + else if ((p_scb = avdt_scb_alloc(p_cs)) == NULL) { + result = AVDT_NO_RESOURCES; + } else { + *p_handle = avdt_scb_to_hdl(p_scb); + } + return result; +} + +/******************************************************************************* +** +** Function AVDT_RemoveStream +** +** Description Remove a stream endpoint. This function is called when +** the application is no longer using a stream endpoint. +** If this function is called when the endpoint is connected +** the connection is closed and then the stream endpoint +** is removed. +** +** +** Returns AVDT_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +UINT16 AVDT_RemoveStream(UINT8 handle) +{ + UINT16 result = AVDT_SUCCESS; + tAVDT_SCB *p_scb; + + /* look up scb */ + if ((p_scb = avdt_scb_by_hdl(handle)) == NULL) { + result = AVDT_BAD_HANDLE; + } else { + /* send remove event to scb */ + avdt_scb_event(p_scb, AVDT_SCB_API_REMOVE_EVT, NULL); + } + return result; +} + +/******************************************************************************* +** +** Function AVDT_DiscoverReq +** +** Description This function initiates a connection to the AVDTP service +** on the peer device, if not already present, and discovers +** the stream endpoints on the peer device. (Please note +** that AVDTP discovery is unrelated to SDP discovery). +** This function can be called at any time regardless of whether +** there is an AVDTP connection to the peer device. +** +** When discovery is complete, an AVDT_DISCOVER_CFM_EVT +** is sent to the application via its callback function. +** The application must not call AVDT_GetCapReq() or +** AVDT_DiscoverReq() again to the same device until +** discovery is complete. +** +** The memory addressed by sep_info is allocated by the +** application. This memory is written to by AVDTP as part +** of the discovery procedure. This memory must remain +** accessible until the application receives the +** AVDT_DISCOVER_CFM_EVT. +** +** Returns AVDT_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +UINT16 AVDT_DiscoverReq(BD_ADDR bd_addr, tAVDT_SEP_INFO *p_sep_info, + UINT8 max_seps, tAVDT_CTRL_CBACK *p_cback) +{ + tAVDT_CCB *p_ccb; + UINT16 result = AVDT_SUCCESS; + tAVDT_CCB_EVT evt; + + /* find channel control block for this bd addr; if none, allocate one */ + if ((p_ccb = avdt_ccb_by_bd(bd_addr)) == NULL) { + if ((p_ccb = avdt_ccb_alloc(bd_addr)) == NULL) { + /* could not allocate channel control block */ + result = AVDT_NO_RESOURCES; + } + } + + if (result == AVDT_SUCCESS) { + /* make sure no discovery or get capabilities req already in progress */ + if (p_ccb->proc_busy) { + result = AVDT_BUSY; + } + /* send event to ccb */ + else { + evt.discover.p_sep_info = p_sep_info; + evt.discover.num_seps = max_seps; + evt.discover.p_cback = p_cback; + avdt_ccb_event(p_ccb, AVDT_CCB_API_DISCOVER_REQ_EVT, &evt); + } + } + return result; +} + +/******************************************************************************* +** +** Function avdt_get_cap_req +** +** Description internal function to serve both AVDT_GetCapReq and +** AVDT_GetAllCapReq +** +** Returns AVDT_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +static UINT16 avdt_get_cap_req(BD_ADDR bd_addr, tAVDT_CCB_API_GETCAP *p_evt) +{ + tAVDT_CCB *p_ccb = NULL; + UINT16 result = AVDT_SUCCESS; + + /* verify SEID */ + if ((p_evt->single.seid < AVDT_SEID_MIN) || (p_evt->single.seid > AVDT_SEID_MAX)) { + AVDT_TRACE_ERROR("seid: %d\n", p_evt->single.seid); + result = AVDT_BAD_PARAMS; + } + /* find channel control block for this bd addr; if none, allocate one */ + else if ((p_ccb = avdt_ccb_by_bd(bd_addr)) == NULL) { + if ((p_ccb = avdt_ccb_alloc(bd_addr)) == NULL) { + /* could not allocate channel control block */ + result = AVDT_NO_RESOURCES; + } + } + + if (result == AVDT_SUCCESS) { + /* make sure no discovery or get capabilities req already in progress */ + if (p_ccb->proc_busy) { + result = AVDT_BUSY; + } + /* send event to ccb */ + else { + avdt_ccb_event(p_ccb, AVDT_CCB_API_GETCAP_REQ_EVT, (tAVDT_CCB_EVT *)p_evt); + } + } + return result; +} + +/******************************************************************************* +** +** Function AVDT_GetCapReq +** +** Description This function initiates a connection to the AVDTP service +** on the peer device, if not already present, and gets the +** capabilities of a stream endpoint on the peer device. +** This function can be called at any time regardless of +** whether there is an AVDTP connection to the peer device. +** +** When the procedure is complete, an AVDT_GETCAP_CFM_EVT is +** sent to the application via its callback function. The +** application must not call AVDT_GetCapReq() or +** AVDT_DiscoverReq() again until the procedure is complete. +** +** The memory pointed to by p_cfg is allocated by the +** application. This memory is written to by AVDTP as part +** of the get capabilities procedure. This memory must +** remain accessible until the application receives +** the AVDT_GETCAP_CFM_EVT. +** +** Returns AVDT_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +UINT16 AVDT_GetCapReq(BD_ADDR bd_addr, UINT8 seid, tAVDT_CFG *p_cfg, tAVDT_CTRL_CBACK *p_cback) +{ + tAVDT_CCB_API_GETCAP getcap; + + getcap.single.seid = seid; + getcap.single.sig_id = AVDT_SIG_GETCAP; + getcap.p_cfg = p_cfg; + getcap.p_cback = p_cback; + return avdt_get_cap_req (bd_addr, &getcap); +} + +/******************************************************************************* +** +** Function AVDT_GetAllCapReq +** +** Description This function initiates a connection to the AVDTP service +** on the peer device, if not already present, and gets the +** capabilities of a stream endpoint on the peer device. +** This function can be called at any time regardless of +** whether there is an AVDTP connection to the peer device. +** +** When the procedure is complete, an AVDT_GETCAP_CFM_EVT is +** sent to the application via its callback function. The +** application must not call AVDT_GetCapReq() or +** AVDT_DiscoverReq() again until the procedure is complete. +** +** The memory pointed to by p_cfg is allocated by the +** application. This memory is written to by AVDTP as part +** of the get capabilities procedure. This memory must +** remain accessible until the application receives +** the AVDT_GETCAP_CFM_EVT. +** +** Returns AVDT_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +UINT16 AVDT_GetAllCapReq(BD_ADDR bd_addr, UINT8 seid, tAVDT_CFG *p_cfg, tAVDT_CTRL_CBACK *p_cback) +{ + tAVDT_CCB_API_GETCAP getcap; + + getcap.single.seid = seid; + getcap.single.sig_id = AVDT_SIG_GET_ALLCAP; + getcap.p_cfg = p_cfg; + getcap.p_cback = p_cback; + return avdt_get_cap_req (bd_addr, &getcap); +} + +/******************************************************************************* +** +** Function AVDT_DelayReport +** +** Description This functions sends a Delay Report to the peer device +** that is associated with a particular SEID. +** This function is called by SNK device. +** +** Returns AVDT_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +UINT16 AVDT_DelayReport(UINT8 handle, UINT8 seid, UINT16 delay) +{ + tAVDT_SCB *p_scb; + UINT16 result = AVDT_SUCCESS; + tAVDT_SCB_EVT evt; + UNUSED(seid); + + AVDT_TRACE_DEBUG("%s: delay value: 0x%04x\n", __func__, delay); + + /* map handle to scb */ + if ((p_scb = avdt_scb_by_hdl(handle)) == NULL) { + result = AVDT_BAD_HANDLE; + } else + /* send event to scb */ + { + if ((p_scb->cs.tsep == AVDT_TSEP_SNK) && (p_scb->curr_cfg.psc_mask & AVDT_PSC_DELAY_RPT)) { + evt.apidelay.hdr.seid = p_scb->peer_seid; + evt.apidelay.delay = delay; + avdt_scb_event(p_scb, AVDT_SCB_API_DELAY_RPT_REQ_EVT, &evt); + } else { + AVDT_TRACE_WARNING("%s: peer device does not supported\n", __func__); + result = AVDT_BAD_PARAMS; + } + } + + return result; +} + +/******************************************************************************* +** +** Function AVDT_OpenReq +** +** Description This function initiates a connection to the AVDTP service +** on the peer device, if not already present, and connects +** to a stream endpoint on a peer device. When the connection +** is completed, an AVDT_OPEN_CFM_EVT is sent to the +** application via the control callback function for this handle. +** +** Returns AVDT_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +UINT16 AVDT_OpenReq(UINT8 handle, BD_ADDR bd_addr, UINT8 seid, tAVDT_CFG *p_cfg) +{ + tAVDT_CCB *p_ccb = NULL; + tAVDT_SCB *p_scb = NULL; + UINT16 result = AVDT_SUCCESS; + tAVDT_SCB_EVT evt; + + /* verify SEID */ + if ((seid < AVDT_SEID_MIN) || (seid > AVDT_SEID_MAX)) { + result = AVDT_BAD_PARAMS; + } + /* map handle to scb */ + else if ((p_scb = avdt_scb_by_hdl(handle)) == NULL) { + result = AVDT_BAD_HANDLE; + } + /* find channel control block for this bd addr; if none, allocate one */ + else if ((p_ccb = avdt_ccb_by_bd(bd_addr)) == NULL) { + if ((p_ccb = avdt_ccb_alloc(bd_addr)) == NULL) { + /* could not allocate channel control block */ + result = AVDT_NO_RESOURCES; + } + } + + /* send event to scb */ + if (result == AVDT_SUCCESS) { + evt.msg.config_cmd.hdr.seid = seid; + evt.msg.config_cmd.hdr.ccb_idx = avdt_ccb_to_idx(p_ccb); + evt.msg.config_cmd.int_seid = handle; + evt.msg.config_cmd.p_cfg = p_cfg; + avdt_scb_event(p_scb, AVDT_SCB_API_SETCONFIG_REQ_EVT, &evt); + } + return result; +} + +/******************************************************************************* +** +** Function AVDT_ConfigRsp +** +** Description Respond to a configure request from the peer device. This +** function must be called if the application receives an +** AVDT_CONFIG_IND_EVT through its control callback. +** +** +** Returns AVDT_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +UINT16 AVDT_ConfigRsp(UINT8 handle, UINT8 label, UINT8 error_code, UINT8 category) +{ + tAVDT_SCB *p_scb; + tAVDT_SCB_EVT evt; + UINT16 result = AVDT_SUCCESS; + UINT8 event_code; + + /* map handle to scb */ + if ((p_scb = avdt_scb_by_hdl(handle)) == NULL) { + result = AVDT_BAD_HANDLE; + } + /* handle special case when this function is called but peer has not send + ** a configuration cmd; ignore and return error result + */ + else if (!p_scb->in_use) { + result = AVDT_BAD_HANDLE; + } + /* send event to scb */ + else { + evt.msg.hdr.err_code = error_code; + evt.msg.hdr.err_param = category; + evt.msg.hdr.label = label; + if (error_code == 0) { + event_code = AVDT_SCB_API_SETCONFIG_RSP_EVT; + } else { + event_code = AVDT_SCB_API_SETCONFIG_REJ_EVT; + } + avdt_scb_event(p_scb, event_code, &evt); + } + + return result; +} + +/******************************************************************************* +** +** Function AVDT_StartReq +** +** Description Start one or more stream endpoints. This initiates the +** transfer of media packets for the streams. All stream +** endpoints must previously be opened. When the streams +** are started, an AVDT_START_CFM_EVT is sent to the +** application via the control callback function for each stream. +** +** +** Returns AVDT_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +UINT16 AVDT_StartReq(UINT8 *p_handles, UINT8 num_handles) +{ + tAVDT_SCB *p_scb = NULL; + tAVDT_CCB_EVT evt; + UINT16 result = AVDT_SUCCESS; + int i; + + if ((num_handles == 0) || (num_handles > AVDT_NUM_SEPS)) { + result = AVDT_BAD_PARAMS; + } else { + /* verify handles */ + for (i = 0; i < num_handles; i++) { + if ((p_scb = avdt_scb_by_hdl(p_handles[i])) == NULL) { + result = AVDT_BAD_HANDLE; + break; + } + } + } + + if (result == AVDT_SUCCESS) { + if (p_scb->p_ccb == NULL) { + result = AVDT_BAD_HANDLE; + } else { + /* send event to ccb */ + memcpy(evt.msg.multi.seid_list, p_handles, num_handles); + evt.msg.multi.num_seps = num_handles; + avdt_ccb_event(p_scb->p_ccb, AVDT_CCB_API_START_REQ_EVT, &evt); + } + } + return result; +} + +/******************************************************************************* +** +** Function AVDT_SuspendReq +** +** Description Suspend one or more stream endpoints. This suspends the +** transfer of media packets for the streams. All stream +** endpoints must previously be open and started. When the +** streams are suspended, an AVDT_SUSPEND_CFM_EVT is sent to +** the application via the control callback function for +** each stream. +** +** +** Returns AVDT_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +UINT16 AVDT_SuspendReq(UINT8 *p_handles, UINT8 num_handles) +{ + tAVDT_SCB *p_scb = NULL; + tAVDT_CCB_EVT evt; + UINT16 result = AVDT_SUCCESS; + int i; + + if ((num_handles == 0) || (num_handles > AVDT_NUM_SEPS)) { + result = AVDT_BAD_PARAMS; + } else { + /* verify handles */ + for (i = 0; i < num_handles; i++) { + if ((p_scb = avdt_scb_by_hdl(p_handles[i])) == NULL) { + result = AVDT_BAD_HANDLE; + break; + } + } + } + + if (result == AVDT_SUCCESS) { + if (p_scb->p_ccb == NULL) { + result = AVDT_BAD_HANDLE; + } else { + /* send event to ccb */ + memcpy(evt.msg.multi.seid_list, p_handles, num_handles); + evt.msg.multi.num_seps = num_handles; + avdt_ccb_event(p_scb->p_ccb, AVDT_CCB_API_SUSPEND_REQ_EVT, &evt); + } + } + + return result; +} + +/******************************************************************************* +** +** Function AVDT_CloseReq +** +** Description Close a stream endpoint. This stops the transfer of media +** packets and closes the transport channel associated with +** this stream endpoint. When the stream is closed, an +** AVDT_CLOSE_CFM_EVT is sent to the application via the +** control callback function for this handle. +** +** +** Returns AVDT_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +UINT16 AVDT_CloseReq(UINT8 handle) +{ + tAVDT_SCB *p_scb; + UINT16 result = AVDT_SUCCESS; + + /* map handle to scb */ + if ((p_scb = avdt_scb_by_hdl(handle)) == NULL) { + result = AVDT_BAD_HANDLE; + } else + /* send event to scb */ + { + avdt_scb_event(p_scb, AVDT_SCB_API_CLOSE_REQ_EVT, NULL); + } + + return result; +} + +/******************************************************************************* +** +** Function AVDT_ReconfigReq +** +** Description Reconfigure a stream endpoint. This allows the application +** to change the codec or content protection capabilities of +** a stream endpoint after it has been opened. This function +** can only be called if the stream is opened but not started +** or if the stream has been suspended. When the procedure +** is completed, an AVDT_RECONFIG_CFM_EVT is sent to the +** application via the control callback function for this handle. +** +** +** Returns AVDT_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +UINT16 AVDT_ReconfigReq(UINT8 handle, tAVDT_CFG *p_cfg) +{ + tAVDT_SCB *p_scb; + UINT16 result = AVDT_SUCCESS; + tAVDT_SCB_EVT evt; + + /* map handle to scb */ + if ((p_scb = avdt_scb_by_hdl(handle)) == NULL) { + result = AVDT_BAD_HANDLE; + } + /* send event to scb */ + else { + /* force psc_mask to zero */ + p_cfg->psc_mask = 0; + + evt.msg.reconfig_cmd.p_cfg = p_cfg; + avdt_scb_event(p_scb, AVDT_SCB_API_RECONFIG_REQ_EVT, &evt); + } + return result; +} + +/******************************************************************************* +** +** Function AVDT_ReconfigRsp +** +** Description Respond to a reconfigure request from the peer device. +** This function must be called if the application receives +** an AVDT_RECONFIG_IND_EVT through its control callback. +** +** +** Returns AVDT_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +UINT16 AVDT_ReconfigRsp(UINT8 handle, UINT8 label, UINT8 error_code, UINT8 category) +{ + tAVDT_SCB *p_scb; + tAVDT_SCB_EVT evt; + UINT16 result = AVDT_SUCCESS; + + /* map handle to scb */ + if ((p_scb = avdt_scb_by_hdl(handle)) == NULL) { + result = AVDT_BAD_HANDLE; + } + /* send event to scb */ + else { + evt.msg.hdr.err_code = error_code; + evt.msg.hdr.err_param = category; + evt.msg.hdr.label = label; + avdt_scb_event(p_scb, AVDT_SCB_API_RECONFIG_RSP_EVT, &evt); + } + + return result; +} + +/******************************************************************************* +** +** Function AVDT_SecurityReq +** +** Description Send a security request to the peer device. When the +** security procedure is completed, an AVDT_SECURITY_CFM_EVT +** is sent to the application via the control callback function +** for this handle. (Please note that AVDTP security procedures +** are unrelated to Bluetooth link level security.) +** +** +** Returns AVDT_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +UINT16 AVDT_SecurityReq(UINT8 handle, UINT8 *p_data, UINT16 len) +{ + tAVDT_SCB *p_scb; + UINT16 result = AVDT_SUCCESS; + tAVDT_SCB_EVT evt; + + /* map handle to scb */ + if ((p_scb = avdt_scb_by_hdl(handle)) == NULL) { + result = AVDT_BAD_HANDLE; + } + /* send event to scb */ + else { + evt.msg.security_rsp.p_data = p_data; + evt.msg.security_rsp.len = len; + avdt_scb_event(p_scb, AVDT_SCB_API_SECURITY_REQ_EVT, &evt); + } + return result; +} + +/******************************************************************************* +** +** Function AVDT_SecurityRsp +** +** Description Respond to a security request from the peer device. +** This function must be called if the application receives +** an AVDT_SECURITY_IND_EVT through its control callback. +** (Please note that AVDTP security procedures are unrelated +** to Bluetooth link level security.) +** +** +** Returns AVDT_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +UINT16 AVDT_SecurityRsp(UINT8 handle, UINT8 label, UINT8 error_code, + UINT8 *p_data, UINT16 len) +{ + tAVDT_SCB *p_scb; + UINT16 result = AVDT_SUCCESS; + tAVDT_SCB_EVT evt; + + /* map handle to scb */ + if ((p_scb = avdt_scb_by_hdl(handle)) == NULL) { + result = AVDT_BAD_HANDLE; + } + /* send event to scb */ + else { + evt.msg.security_rsp.hdr.err_code = error_code; + evt.msg.security_rsp.hdr.label = label; + evt.msg.security_rsp.p_data = p_data; + evt.msg.security_rsp.len = len; + avdt_scb_event(p_scb, AVDT_SCB_API_SECURITY_RSP_EVT, &evt); + } + return result; +} + +/******************************************************************************* +** +** Function AVDT_WriteReqOpt +** +** Description Send a media packet to the peer device. The stream must +** be started before this function is called. Also, this +** function can only be called if the stream is a SRC. +** +** When AVDTP has sent the media packet and is ready for the +** next packet, an AVDT_WRITE_CFM_EVT is sent to the +** application via the control callback. The application must +** wait for the AVDT_WRITE_CFM_EVT before it makes the next +** call to AVDT_WriteReq(). If the applications calls +** AVDT_WriteReq() before it receives the event the packet +** will not be sent. The application may make its first call +** to AVDT_WriteReq() after it receives an AVDT_START_CFM_EVT +** or AVDT_START_IND_EVT. +** +** The application passes the packet using the BT_HDR structure. +** This structure is described in section 2.1. The offset +** field must be equal to or greater than AVDT_MEDIA_OFFSET +** (if NO_RTP is specified, L2CAP_MIN_OFFSET can be used). +** This allows enough space in the buffer for the L2CAP and +** AVDTP headers. +** +** The memory pointed to by p_pkt must be a GKI buffer +** allocated by the application. This buffer will be freed +** by the protocol stack; the application must not free +** this buffer. +** +** The opt parameter allows passing specific options like: +** - NO_RTP : do not add the RTP header to buffer +** +** Returns AVDT_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +UINT16 AVDT_WriteReqOpt(UINT8 handle, BT_HDR *p_pkt, UINT32 time_stamp, UINT8 m_pt, tAVDT_DATA_OPT_MASK opt) +{ + tAVDT_SCB *p_scb; + tAVDT_SCB_EVT evt; + UINT16 result = AVDT_SUCCESS; + + /* map handle to scb */ + if ((p_scb = avdt_scb_by_hdl(handle)) == NULL) { + result = AVDT_BAD_HANDLE; + } else { + evt.apiwrite.p_buf = p_pkt; + evt.apiwrite.time_stamp = time_stamp; + evt.apiwrite.m_pt = m_pt; + evt.apiwrite.opt = opt; + avdt_scb_event(p_scb, AVDT_SCB_API_WRITE_REQ_EVT, &evt); + } + + return result; +} + +/******************************************************************************* +** +** Function AVDT_WriteReq +** +** Description Send a media packet to the peer device. The stream must +** be started before this function is called. Also, this +** function can only be called if the stream is a SRC. +** +** When AVDTP has sent the media packet and is ready for the +** next packet, an AVDT_WRITE_CFM_EVT is sent to the +** application via the control callback. The application must +** wait for the AVDT_WRITE_CFM_EVT before it makes the next +** call to AVDT_WriteReq(). If the applications calls +** AVDT_WriteReq() before it receives the event the packet +** will not be sent. The application may make its first call +** to AVDT_WriteReq() after it receives an AVDT_START_CFM_EVT +** or AVDT_START_IND_EVT. +** +** The application passes the packet using the BT_HDR structure. +** This structure is described in section 2.1. The offset +** field must be equal to or greater than AVDT_MEDIA_OFFSET. +** This allows enough space in the buffer for the L2CAP and +** AVDTP headers. +** +** The memory pointed to by p_pkt must be a GKI buffer +** allocated by the application. This buffer will be freed +** by the protocol stack; the application must not free +** this buffer. +** +** +** Returns AVDT_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +UINT16 AVDT_WriteReq(UINT8 handle, BT_HDR *p_pkt, UINT32 time_stamp, UINT8 m_pt) +{ + return AVDT_WriteReqOpt(handle, p_pkt, time_stamp, m_pt, AVDT_DATA_OPT_NONE); +} + +/******************************************************************************* +** +** Function AVDT_ConnectReq +** +** Description This function initiates an AVDTP signaling connection +** to the peer device. When the connection is completed, an +** AVDT_CONNECT_IND_EVT is sent to the application via its +** control callback function. If the connection attempt fails +** an AVDT_DISCONNECT_IND_EVT is sent. The security mask +** parameter overrides the outgoing security mask set in +** AVDT_Register(). +** +** Returns AVDT_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +UINT16 AVDT_ConnectReq(BD_ADDR bd_addr, UINT8 sec_mask, tAVDT_CTRL_CBACK *p_cback) +{ + tAVDT_CCB *p_ccb = NULL; + UINT16 result = AVDT_SUCCESS; + tAVDT_CCB_EVT evt; + + /* find channel control block for this bd addr; if none, allocate one */ + if ((p_ccb = avdt_ccb_by_bd(bd_addr)) == NULL) { + if ((p_ccb = avdt_ccb_alloc(bd_addr)) == NULL) { + /* could not allocate channel control block */ + result = AVDT_NO_RESOURCES; + } + } else if (p_ccb->ll_opened == FALSE) { + AVDT_TRACE_WARNING("AVDT_ConnectReq: CCB LL is in the middle of opening"); + + /* ccb was already allocated for the incoming signalling. */ + result = AVDT_BUSY; + } + + if (result == AVDT_SUCCESS) { + /* send event to ccb */ + evt.connect.p_cback = p_cback; + evt.connect.sec_mask = sec_mask; + avdt_ccb_event(p_ccb, AVDT_CCB_API_CONNECT_REQ_EVT, &evt); + } + return result; +} + +/******************************************************************************* +** +** Function AVDT_DisconnectReq +** +** Description This function disconnect an AVDTP signaling connection +** to the peer device. When disconnected an +** AVDT_DISCONNECT_IND_EVT is sent to the application via its +** control callback function. +** +** Returns AVDT_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +UINT16 AVDT_DisconnectReq(BD_ADDR bd_addr, tAVDT_CTRL_CBACK *p_cback) +{ + tAVDT_CCB *p_ccb = NULL; + UINT16 result = AVDT_SUCCESS; + tAVDT_CCB_EVT evt; + + /* find channel control block for this bd addr; if none, error */ + if ((p_ccb = avdt_ccb_by_bd(bd_addr)) == NULL) { + result = AVDT_BAD_PARAMS; + } + + if (result == AVDT_SUCCESS) { + /* send event to ccb */ + evt.disconnect.p_cback = p_cback; + avdt_ccb_event(p_ccb, AVDT_CCB_API_DISCONNECT_REQ_EVT, &evt); + } + return result; +} + +/******************************************************************************* +** +** Function AVDT_GetL2CapChannel +** +** Description Get the L2CAP CID used by the handle. +** +** Returns CID if successful, otherwise 0. +** +*******************************************************************************/ +UINT16 AVDT_GetL2CapChannel(UINT8 handle) +{ + tAVDT_SCB *p_scb; + tAVDT_CCB *p_ccb; + UINT8 tcid; + UINT16 lcid = 0; + + /* map handle to scb */ + if (((p_scb = avdt_scb_by_hdl(handle)) != NULL) + && ((p_ccb = p_scb->p_ccb) != NULL)) { + /* get tcid from type, scb */ + tcid = avdt_ad_type_to_tcid(AVDT_CHAN_MEDIA, p_scb); + + lcid = avdt_cb.ad.rt_tbl[avdt_ccb_to_idx(p_ccb)][tcid].lcid; + } + + return (lcid); +} + +/******************************************************************************* +** +** Function AVDT_GetSignalChannel +** +** Description Get the L2CAP CID used by the signal channel of the given handle. +** +** Returns CID if successful, otherwise 0. +** +*******************************************************************************/ +UINT16 AVDT_GetSignalChannel(UINT8 handle, BD_ADDR bd_addr) +{ + tAVDT_SCB *p_scb; + tAVDT_CCB *p_ccb; + UINT8 tcid = 0; /* tcid is always 0 for signal channel */ + UINT16 lcid = 0; + + /* map handle to scb */ + if (((p_scb = avdt_scb_by_hdl(handle)) != NULL) + && ((p_ccb = p_scb->p_ccb) != NULL)) { + lcid = avdt_cb.ad.rt_tbl[avdt_ccb_to_idx(p_ccb)][tcid].lcid; + } else if ((p_ccb = avdt_ccb_by_bd(bd_addr)) != NULL) { + lcid = avdt_cb.ad.rt_tbl[avdt_ccb_to_idx(p_ccb)][tcid].lcid; + } + + return (lcid); +} + + + +#if AVDT_MULTIPLEXING == TRUE +/******************************************************************************* +** +** Function AVDT_SetMediaBuf +** +** Description Assigns buffer for media packets or forbids using of assigned +** buffer if argument p_buf is NULL. This function can only +** be called if the stream is a SNK. +** +** AVDTP uses this buffer to reassemble fragmented media packets. +** When AVDTP receives a complete media packet, it calls the +** p_media_cback assigned by AVDT_CreateStream(). +** This function can be called during callback to assign a +** different buffer for next media packet or can leave the current +** buffer for next packet. +** +** Returns AVDT_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +extern UINT16 AVDT_SetMediaBuf(UINT8 handle, UINT8 *p_buf, UINT32 buf_len) +{ + tAVDT_SCB *p_scb; + UINT16 result = AVDT_SUCCESS; + + /* map handle to scb */ + if ((p_scb = avdt_scb_by_hdl(handle)) == NULL) { + result = AVDT_BAD_HANDLE; + } else { + if (p_buf && p_scb->cs.p_media_cback == NULL) { + result = AVDT_NO_RESOURCES; + } else { + p_scb->p_media_buf = p_buf; + p_scb->media_buf_len = buf_len; + } + } + + return result; +} +#endif + +#if AVDT_REPORTING == TRUE +/******************************************************************************* +** +** Function AVDT_SendReport +** +** Description +** +** +** +** Returns +** +*******************************************************************************/ +UINT16 AVDT_SendReport(UINT8 handle, AVDT_REPORT_TYPE type, + tAVDT_REPORT_DATA *p_data) +{ + tAVDT_SCB *p_scb; + UINT16 result = AVDT_BAD_PARAMS; + BT_HDR *p_pkt; + tAVDT_TC_TBL *p_tbl; + UINT8 *p, *plen, *pm1, *p_end; +#if AVDT_MULTIPLEXING == TRUE + UINT8 *p_al = NULL, u; +#endif + UINT32 ssrc; + UINT16 len; + + /* map handle to scb && verify parameters */ + if (((p_scb = avdt_scb_by_hdl(handle)) != NULL) + && (p_scb->p_ccb != NULL) + && (((type == AVDT_RTCP_PT_SR) && (p_scb->cs.tsep == AVDT_TSEP_SRC)) || + ((type == AVDT_RTCP_PT_RR) && (p_scb->cs.tsep == AVDT_TSEP_SNK)) || + (type == AVDT_RTCP_PT_SDES)) ) { + result = AVDT_NO_RESOURCES; + + /* build SR - assume fit in one packet */ + p_tbl = avdt_ad_tc_tbl_by_type(AVDT_CHAN_REPORT, p_scb->p_ccb, p_scb); + if ((p_tbl->state == AVDT_AD_ST_OPEN) && + (p_pkt = (BT_HDR *)osi_malloc(p_tbl->peer_mtu)) != NULL) { + p_pkt->offset = L2CAP_MIN_OFFSET; + p = (UINT8 *)(p_pkt + 1) + p_pkt->offset; +#if AVDT_MULTIPLEXING == TRUE + if (p_scb->curr_cfg.psc_mask & AVDT_PSC_MUX) { + /* Adaptation Layer header later */ + p_al = p; + p += 2; + } +#endif + pm1 = p; + *p++ = AVDT_MEDIA_OCTET1 | 1; + *p++ = type; + /* save the location for length */ + plen = p; + p += 2; + ssrc = avdt_scb_gen_ssrc(p_scb); + UINT32_TO_BE_STREAM(p, ssrc); + + switch (type) { + case AVDT_RTCP_PT_SR: /* Sender Report */ + *pm1 = AVDT_MEDIA_OCTET1; + UINT32_TO_BE_STREAM(p, p_data->sr.ntp_sec); + UINT32_TO_BE_STREAM(p, p_data->sr.ntp_frac); + UINT32_TO_BE_STREAM(p, p_data->sr.rtp_time); + UINT32_TO_BE_STREAM(p, p_data->sr.pkt_count); + UINT32_TO_BE_STREAM(p, p_data->sr.octet_count); + break; + + case AVDT_RTCP_PT_RR: /* Receiver Report */ + *p++ = p_data->rr.frag_lost; + AVDT_TRACE_API("packet_lost: %d\n", p_data->rr.packet_lost); + p_data->rr.packet_lost &= 0xFFFFFF; + AVDT_TRACE_API("packet_lost: %d\n", p_data->rr.packet_lost); + UINT24_TO_BE_STREAM(p, p_data->rr.packet_lost); + UINT32_TO_BE_STREAM(p, p_data->rr.seq_num_rcvd); + UINT32_TO_BE_STREAM(p, p_data->rr.jitter); + UINT32_TO_BE_STREAM(p, p_data->rr.lsr); + UINT32_TO_BE_STREAM(p, p_data->rr.dlsr); + break; + + case AVDT_RTCP_PT_SDES: /* Source Description */ + *p++ = AVDT_RTCP_SDES_CNAME; + len = strlen((char *)p_data->cname); + if (len > AVDT_MAX_CNAME_SIZE) { + len = AVDT_MAX_CNAME_SIZE; + } + *p++ = (UINT8)len; + BCM_STRNCPY_S((char *)p, (char *)p_data->cname, AVDT_MAX_CNAME_SIZE + 1); + p += len; + break; + } + p_end = p; + len = p - pm1 - 1; + UINT16_TO_BE_STREAM(plen, len); + +#if AVDT_MULTIPLEXING == TRUE + if (p_scb->curr_cfg.psc_mask & AVDT_PSC_MUX) { + /* Adaptation Layer header */ + p = p_al; + len++; + UINT16_TO_BE_STREAM(p_al, len ); + /* TSID, no-fragment bit and coding of length(9-bit length field) */ + u = *p; + *p = (p_scb->curr_cfg.mux_tsid_report << 3) | AVDT_ALH_LCODE_9BITM0; + if (u) { + *p |= AVDT_ALH_LCODE_9BITM1; + } + } +#endif + + /* set the actual payload length */ + p_pkt->len = p_end - p; + /* send the packet */ + if (L2CAP_DW_FAILED != avdt_ad_write_req(AVDT_CHAN_REPORT, p_scb->p_ccb, p_scb, p_pkt)) { + result = AVDT_SUCCESS; + } + } + } + + return result; +} +#endif + +/****************************************************************************** +** +** Function AVDT_SetTraceLevel +** +** Description Sets the trace level for AVDT. If 0xff is passed, the +** current trace level is returned. +** +** Input Parameters: +** new_level: The level to set the AVDT tracing to: +** 0xff-returns the current setting. +** 0-turns off tracing. +** >= 1-Errors. +** >= 2-Warnings. +** >= 3-APIs. +** >= 4-Events. +** >= 5-Debug. +** +** Returns The new trace level or current trace level if +** the input parameter is 0xff. +** +******************************************************************************/ +UINT8 AVDT_SetTraceLevel (UINT8 new_level) +{ + if (new_level != 0xFF) { + avdt_cb.trace_level = new_level; + } + + return (avdt_cb.trace_level); +} + +/******************************************************************************* +** +** Function AVDT_SetDelayValue +** +** Description Set delay reporting value. +** +** Returns void +** +*******************************************************************************/ +void AVDT_SetDelayValue(UINT16 delay_value) +{ + avdt_cb.delay_value = delay_value; +} + +/******************************************************************************* +** +** Function AVDT_GetDelayValue +** +** Description Get delay reporting value. +** +** Returns delay value +** +*******************************************************************************/ +UINT16 AVDT_GetDelayValue(void) +{ + return avdt_cb.delay_value; +} + +#endif /* #if (defined(AVDT_INCLUDED) && AVDT_INCLUDED == TRUE) */ diff --git a/lib/bt/host/bluedroid/stack/avdt/avdt_ccb.c b/lib/bt/host/bluedroid/stack/avdt/avdt_ccb.c new file mode 100644 index 00000000..035488f8 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/avdt/avdt_ccb.c @@ -0,0 +1,456 @@ +/****************************************************************************** + * + * Copyright (C) 2002-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This module contains the channel control block state machine and + * functions which operate on the channel control block. + * + ******************************************************************************/ + +#include +#include "stack/bt_types.h" +#include "common/bt_target.h" +#include "common/bt_defs.h" +#include "stack/avdt_api.h" +#include "stack/avdtc_api.h" +#include "avdt_int.h" +#include "stack/btu.h" + +#if (defined(AVDT_INCLUDED) && AVDT_INCLUDED == TRUE) + +/***************************************************************************** +** state machine constants and types +*****************************************************************************/ +#if AVDT_DEBUG == TRUE + +/* verbose state strings for trace */ +const char *const avdt_ccb_st_str[] = { + "CCB_IDLE_ST", + "CCB_OPENING_ST", + "CCB_OPEN_ST", + "CCB_CLOSING_ST" +}; + +/* verbose event strings for trace */ +const char *const avdt_ccb_evt_str[] = { + "API_DISCOVER_REQ_EVT", + "API_GETCAP_REQ_EVT", + "API_START_REQ_EVT", + "API_SUSPEND_REQ_EVT", + "API_DISCOVER_RSP_EVT", + "API_GETCAP_RSP_EVT", + "API_START_RSP_EVT", + "API_SUSPEND_RSP_EVT", + "API_CONNECT_REQ_EVT", + "API_DISCONNECT_REQ_EVT", + "MSG_DISCOVER_CMD_EVT", + "MSG_GETCAP_CMD_EVT", + "MSG_START_CMD_EVT", + "MSG_SUSPEND_CMD_EVT", + "MSG_DISCOVER_RSP_EVT", + "MSG_GETCAP_RSP_EVT", + "MSG_START_RSP_EVT", + "MSG_SUSPEND_RSP_EVT", + "RCVRSP_EVT", + "SENDMSG_EVT", + "RET_TOUT_EVT", + "RSP_TOUT_EVT", + "IDLE_TOUT_EVT", + "UL_OPEN_EVT", + "UL_CLOSE_EVT", + "LL_OPEN_EVT", + "LL_CLOSE_EVT", + "LL_CONG_EVT" +}; + +#endif + + +/* action function list */ +const tAVDT_CCB_ACTION avdt_ccb_action[] = { + avdt_ccb_chan_open, + avdt_ccb_chan_close, + avdt_ccb_chk_close, + avdt_ccb_hdl_discover_cmd, + avdt_ccb_hdl_discover_rsp, + avdt_ccb_hdl_getcap_cmd, + avdt_ccb_hdl_getcap_rsp, + avdt_ccb_hdl_start_cmd, + avdt_ccb_hdl_start_rsp, + avdt_ccb_hdl_suspend_cmd, + avdt_ccb_hdl_suspend_rsp, + avdt_ccb_snd_discover_cmd, + avdt_ccb_snd_discover_rsp, + avdt_ccb_snd_getcap_cmd, + avdt_ccb_snd_getcap_rsp, + avdt_ccb_snd_start_cmd, + avdt_ccb_snd_start_rsp, + avdt_ccb_snd_suspend_cmd, + avdt_ccb_snd_suspend_rsp, + avdt_ccb_clear_cmds, + avdt_ccb_cmd_fail, + avdt_ccb_free_cmd, + avdt_ccb_cong_state, + avdt_ccb_ret_cmd, + avdt_ccb_snd_cmd, + avdt_ccb_snd_msg, + avdt_ccb_set_reconn, + avdt_ccb_clr_reconn, + avdt_ccb_chk_reconn, + avdt_ccb_chk_timer, + avdt_ccb_set_conn, + avdt_ccb_set_disconn, + avdt_ccb_do_disconn, + avdt_ccb_ll_closed, + avdt_ccb_ll_opened, + avdt_ccb_dealloc +}; + +/* state table information */ +#define AVDT_CCB_ACTIONS 2 /* number of actions */ +#define AVDT_CCB_NEXT_STATE 2 /* position of next state */ +#define AVDT_CCB_NUM_COLS 3 /* number of columns in state tables */ + +/* state table for idle state */ +const UINT8 avdt_ccb_st_idle[][AVDT_CCB_NUM_COLS] = { + /* Event Action 1 Action 2 Next state */ + /* API_DISCOVER_REQ_EVT */ {AVDT_CCB_SND_DISCOVER_CMD, AVDT_CCB_CHAN_OPEN, AVDT_CCB_OPENING_ST}, + /* API_GETCAP_REQ_EVT */ {AVDT_CCB_SND_GETCAP_CMD, AVDT_CCB_CHAN_OPEN, AVDT_CCB_OPENING_ST}, + /* API_START_REQ_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_IDLE_ST}, + /* API_SUSPEND_REQ_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_IDLE_ST}, + /* API_DISCOVER_RSP_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_IDLE_ST}, + /* API_GETCAP_RSP_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_IDLE_ST}, + /* API_START_RSP_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_IDLE_ST}, + /* API_SUSPEND_RSP_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_IDLE_ST}, + /* API_CONNECT_REQ_EVT */ {AVDT_CCB_SET_CONN, AVDT_CCB_CHAN_OPEN, AVDT_CCB_OPENING_ST}, + /* API_DISCONNECT_REQ_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_IDLE_ST}, + /* MSG_DISCOVER_CMD_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_IDLE_ST}, + /* MSG_GETCAP_CMD_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_IDLE_ST}, + /* MSG_START_CMD_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_IDLE_ST}, + /* MSG_SUSPEND_CMD_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_IDLE_ST}, + /* MSG_DISCOVER_RSP_EVT */ {AVDT_CCB_HDL_DISCOVER_RSP, AVDT_CCB_IGNORE, AVDT_CCB_IDLE_ST}, + /* MSG_GETCAP_RSP_EVT */ {AVDT_CCB_HDL_GETCAP_RSP, AVDT_CCB_IGNORE, AVDT_CCB_IDLE_ST}, + /* MSG_START_RSP_EVT */ {AVDT_CCB_HDL_START_RSP, AVDT_CCB_IGNORE, AVDT_CCB_IDLE_ST}, + /* MSG_SUSPEND_RSP_EVT */ {AVDT_CCB_HDL_SUSPEND_RSP, AVDT_CCB_IGNORE, AVDT_CCB_IDLE_ST}, + /* RCVRSP_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_IDLE_ST}, + /* SENDMSG_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_IDLE_ST}, + /* RET_TOUT_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_IDLE_ST}, + /* RSP_TOUT_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_IDLE_ST}, + /* IDLE_TOUT_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_IDLE_ST}, + /* UL_OPEN_EVT */ {AVDT_CCB_CHAN_OPEN, AVDT_CCB_IGNORE, AVDT_CCB_OPENING_ST}, + /* UL_CLOSE_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_IDLE_ST}, + /* LL_OPEN_EVT */ {AVDT_CCB_LL_OPENED, AVDT_CCB_IGNORE, AVDT_CCB_OPEN_ST}, + /* LL_CLOSE_EVT */ {AVDT_CCB_LL_CLOSED, AVDT_CCB_IGNORE, AVDT_CCB_IDLE_ST}, + /* LL_CONG_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_IDLE_ST} +}; + +/* state table for opening state */ +const UINT8 avdt_ccb_st_opening[][AVDT_CCB_NUM_COLS] = { + /* Event Action 1 Action 2 Next state */ + /* API_DISCOVER_REQ_EVT */ {AVDT_CCB_SND_DISCOVER_CMD, AVDT_CCB_IGNORE, AVDT_CCB_OPENING_ST}, + /* API_GETCAP_REQ_EVT */ {AVDT_CCB_SND_GETCAP_CMD, AVDT_CCB_IGNORE, AVDT_CCB_OPENING_ST}, + /* API_START_REQ_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_OPENING_ST}, + /* API_SUSPEND_REQ_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_OPENING_ST}, + /* API_DISCOVER_RSP_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_OPENING_ST}, + /* API_GETCAP_RSP_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_OPENING_ST}, + /* API_START_RSP_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_OPENING_ST}, + /* API_SUSPEND_RSP_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_OPENING_ST}, + /* API_CONNECT_REQ_EVT */ {AVDT_CCB_SET_CONN, AVDT_CCB_IGNORE, AVDT_CCB_OPENING_ST}, + /* API_DISCONNECT_REQ_EVT */ {AVDT_CCB_SET_DISCONN, AVDT_CCB_DO_DISCONN, AVDT_CCB_CLOSING_ST}, + /* MSG_DISCOVER_CMD_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_OPENING_ST}, + /* MSG_GETCAP_CMD_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_OPENING_ST}, + /* MSG_START_CMD_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_OPENING_ST}, + /* MSG_SUSPEND_CMD_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_OPENING_ST}, + /* MSG_DISCOVER_RSP_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_OPENING_ST}, + /* MSG_GETCAP_RSP_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_OPENING_ST}, + /* MSG_START_RSP_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_OPENING_ST}, + /* MSG_SUSPEND_RSP_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_OPENING_ST}, + /* RCVRSP_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_OPENING_ST}, + /* SENDMSG_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_OPENING_ST}, + /* RET_TOUT_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_OPENING_ST}, + /* RSP_TOUT_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_OPENING_ST}, + /* IDLE_TOUT_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_OPENING_ST}, + /* UL_OPEN_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_OPENING_ST}, + /* UL_CLOSE_EVT */ {AVDT_CCB_CLEAR_CMDS, AVDT_CCB_CHAN_CLOSE, AVDT_CCB_CLOSING_ST}, + /* LL_OPEN_EVT */ {AVDT_CCB_SND_CMD, AVDT_CCB_LL_OPENED, AVDT_CCB_OPEN_ST}, + /* LL_CLOSE_EVT */ {AVDT_CCB_LL_CLOSED, AVDT_CCB_IGNORE, AVDT_CCB_IDLE_ST}, + /* LL_CONG_EVT */ {AVDT_CCB_CONG_STATE, AVDT_CCB_IGNORE, AVDT_CCB_OPENING_ST} +}; + +/* state table for open state */ +const UINT8 avdt_ccb_st_open[][AVDT_CCB_NUM_COLS] = { + /* Event Action 1 Action 2 Next state */ + /* API_DISCOVER_REQ_EVT */ {AVDT_CCB_SND_DISCOVER_CMD, AVDT_CCB_SND_CMD, AVDT_CCB_OPEN_ST}, + /* API_GETCAP_REQ_EVT */ {AVDT_CCB_SND_GETCAP_CMD, AVDT_CCB_SND_CMD, AVDT_CCB_OPEN_ST}, + /* API_START_REQ_EVT */ {AVDT_CCB_SND_START_CMD, AVDT_CCB_SND_CMD, AVDT_CCB_OPEN_ST}, + /* API_SUSPEND_REQ_EVT */ {AVDT_CCB_SND_SUSPEND_CMD, AVDT_CCB_SND_CMD, AVDT_CCB_OPEN_ST}, + /* API_DISCOVER_RSP_EVT */ {AVDT_CCB_SND_DISCOVER_RSP, AVDT_CCB_SND_CMD, AVDT_CCB_OPEN_ST}, + /* API_GETCAP_RSP_EVT */ {AVDT_CCB_SND_GETCAP_RSP, AVDT_CCB_SND_CMD, AVDT_CCB_OPEN_ST}, + /* API_START_RSP_EVT */ {AVDT_CCB_SND_START_RSP, AVDT_CCB_SND_CMD, AVDT_CCB_OPEN_ST}, + /* API_SUSPEND_RSP_EVT */ {AVDT_CCB_SND_SUSPEND_RSP, AVDT_CCB_SND_CMD, AVDT_CCB_OPEN_ST}, + /* API_CONNECT_REQ_EVT */ {AVDT_CCB_SET_CONN, AVDT_CCB_LL_OPENED, AVDT_CCB_OPEN_ST}, + /* API_DISCONNECT_REQ_EVT */ {AVDT_CCB_SET_DISCONN, AVDT_CCB_DO_DISCONN, AVDT_CCB_CLOSING_ST}, + /* MSG_DISCOVER_CMD_EVT */ {AVDT_CCB_HDL_DISCOVER_CMD, AVDT_CCB_IGNORE, AVDT_CCB_OPEN_ST}, + /* MSG_GETCAP_CMD_EVT */ {AVDT_CCB_HDL_GETCAP_CMD, AVDT_CCB_IGNORE, AVDT_CCB_OPEN_ST}, + /* MSG_START_CMD_EVT */ {AVDT_CCB_HDL_START_CMD, AVDT_CCB_IGNORE, AVDT_CCB_OPEN_ST}, + /* MSG_SUSPEND_CMD_EVT */ {AVDT_CCB_HDL_SUSPEND_CMD, AVDT_CCB_IGNORE, AVDT_CCB_OPEN_ST}, + /* MSG_DISCOVER_RSP_EVT */ {AVDT_CCB_CHK_CLOSE, AVDT_CCB_HDL_DISCOVER_RSP, AVDT_CCB_OPEN_ST}, + /* MSG_GETCAP_RSP_EVT */ {AVDT_CCB_CHK_CLOSE, AVDT_CCB_HDL_GETCAP_RSP, AVDT_CCB_OPEN_ST}, + /* MSG_START_RSP_EVT */ {AVDT_CCB_HDL_START_RSP, AVDT_CCB_IGNORE, AVDT_CCB_OPEN_ST}, + /* MSG_SUSPEND_RSP_EVT */ {AVDT_CCB_HDL_SUSPEND_RSP, AVDT_CCB_IGNORE, AVDT_CCB_OPEN_ST}, + /* RCVRSP_EVT */ {AVDT_CCB_FREE_CMD, AVDT_CCB_SND_CMD, AVDT_CCB_OPEN_ST}, + /* SENDMSG_EVT */ {AVDT_CCB_SND_MSG, AVDT_CCB_IGNORE, AVDT_CCB_OPEN_ST}, + /* RET_TOUT_EVT */ {AVDT_CCB_RET_CMD, AVDT_CCB_IGNORE, AVDT_CCB_OPEN_ST}, + /* RSP_TOUT_EVT */ {AVDT_CCB_CMD_FAIL, AVDT_CCB_SND_CMD, AVDT_CCB_OPEN_ST}, + /* IDLE_TOUT_EVT */ {AVDT_CCB_CLEAR_CMDS, AVDT_CCB_CHAN_CLOSE, AVDT_CCB_CLOSING_ST}, + /* UL_OPEN_EVT */ {AVDT_CCB_CHK_TIMER, AVDT_CCB_IGNORE, AVDT_CCB_OPEN_ST}, + /* UL_CLOSE_EVT */ {AVDT_CCB_CHK_CLOSE, AVDT_CCB_IGNORE, AVDT_CCB_OPEN_ST}, + /* LL_OPEN_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_OPEN_ST}, + /* LL_CLOSE_EVT */ {AVDT_CCB_LL_CLOSED, AVDT_CCB_IGNORE, AVDT_CCB_IDLE_ST}, + /* LL_CONG_EVT */ {AVDT_CCB_CONG_STATE, AVDT_CCB_SND_MSG, AVDT_CCB_OPEN_ST} +}; + +/* state table for closing state */ +const UINT8 avdt_ccb_st_closing[][AVDT_CCB_NUM_COLS] = { + /* Event Action 1 Action 2 Next state */ + /* API_DISCOVER_REQ_EVT */ {AVDT_CCB_SET_RECONN, AVDT_CCB_SND_DISCOVER_CMD, AVDT_CCB_CLOSING_ST}, + /* API_GETCAP_REQ_EVT */ {AVDT_CCB_SET_RECONN, AVDT_CCB_SND_GETCAP_CMD, AVDT_CCB_CLOSING_ST}, + /* API_START_REQ_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_CLOSING_ST}, + /* API_SUSPEND_REQ_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_CLOSING_ST}, + /* API_DISCOVER_RSP_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_CLOSING_ST}, + /* API_GETCAP_RSP_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_CLOSING_ST}, + /* API_START_RSP_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_CLOSING_ST}, + /* API_SUSPEND_RSP_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_CLOSING_ST}, + /* API_CONNECT_REQ_EVT */ {AVDT_CCB_SET_RECONN, AVDT_CCB_SET_CONN, AVDT_CCB_CLOSING_ST}, + /* API_DISCONNECT_REQ_EVT */ {AVDT_CCB_CLR_RECONN, AVDT_CCB_SET_DISCONN, AVDT_CCB_CLOSING_ST}, + /* MSG_DISCOVER_CMD_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_CLOSING_ST}, + /* MSG_GETCAP_CMD_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_CLOSING_ST}, + /* MSG_START_CMD_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_CLOSING_ST}, + /* MSG_SUSPEND_CMD_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_CLOSING_ST}, + /* MSG_DISCOVER_RSP_EVT */ {AVDT_CCB_HDL_DISCOVER_RSP, AVDT_CCB_IGNORE, AVDT_CCB_CLOSING_ST}, + /* MSG_GETCAP_RSP_EVT */ {AVDT_CCB_HDL_GETCAP_RSP, AVDT_CCB_IGNORE, AVDT_CCB_CLOSING_ST}, + /* MSG_START_RSP_EVT */ {AVDT_CCB_HDL_START_RSP, AVDT_CCB_IGNORE, AVDT_CCB_CLOSING_ST}, + /* MSG_SUSPEND_RSP_EVT */ {AVDT_CCB_HDL_SUSPEND_RSP, AVDT_CCB_IGNORE, AVDT_CCB_CLOSING_ST}, + /* RCVRSP_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_CLOSING_ST}, + /* SENDMSG_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_CLOSING_ST}, + /* RET_TOUT_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_CLOSING_ST}, + /* RSP_TOUT_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_CLOSING_ST}, + /* IDLE_TOUT_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_CLOSING_ST}, + /* UL_OPEN_EVT */ {AVDT_CCB_SET_RECONN, AVDT_CCB_IGNORE, AVDT_CCB_CLOSING_ST}, + /* UL_CLOSE_EVT */ {AVDT_CCB_CLR_RECONN, AVDT_CCB_IGNORE, AVDT_CCB_CLOSING_ST}, + /* LL_OPEN_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_CLOSING_ST}, + /* LL_CLOSE_EVT */ {AVDT_CCB_CHK_RECONN, AVDT_CCB_IGNORE, AVDT_CCB_IDLE_ST}, + /* LL_CONG_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_CLOSING_ST} +}; + +/* type for state table */ +typedef const UINT8 (*tAVDT_CCB_ST_TBL)[AVDT_CCB_NUM_COLS]; + +/* state table */ +const tAVDT_CCB_ST_TBL avdt_ccb_st_tbl[] = { + avdt_ccb_st_idle, + avdt_ccb_st_opening, + avdt_ccb_st_open, + avdt_ccb_st_closing +}; + +/******************************************************************************* +** +** Function avdt_ccb_init +** +** Description Initialize channel control block module. +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_ccb_init(void) +{ + memset(&avdt_cb.ccb[0], 0, sizeof(tAVDT_CCB) * AVDT_NUM_LINKS); + avdt_cb.p_ccb_act = (tAVDT_CCB_ACTION *) avdt_ccb_action; +} + +/******************************************************************************* +** +** Function avdt_ccb_event +** +** Description State machine event handling function for ccb +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_ccb_event(tAVDT_CCB *p_ccb, UINT8 event, tAVDT_CCB_EVT *p_data) +{ + tAVDT_CCB_ST_TBL state_table; + UINT8 action; + int i; + +#if AVDT_DEBUG == TRUE + AVDT_TRACE_EVENT("CCB ccb=%d event=%s state=%s\n", avdt_ccb_to_idx(p_ccb), avdt_ccb_evt_str[event], avdt_ccb_st_str[p_ccb->state]); +#endif + + /* look up the state table for the current state */ + state_table = avdt_ccb_st_tbl[p_ccb->state]; + + /* set next state */ + if (p_ccb->state != state_table[event][AVDT_CCB_NEXT_STATE]) { + p_ccb->state = state_table[event][AVDT_CCB_NEXT_STATE]; + } + + /* execute action functions */ + for (i = 0; i < AVDT_CCB_ACTIONS; i++) { + if ((action = state_table[event][i]) != AVDT_CCB_IGNORE) { + (*avdt_cb.p_ccb_act[action])(p_ccb, p_data); + } else { + break; + } + } +} + + +/******************************************************************************* +** +** Function avdt_ccb_by_bd +** +** Description This lookup function finds the ccb for a BD address. +** +** +** Returns pointer to the ccb, or NULL if none found. +** +*******************************************************************************/ +tAVDT_CCB *avdt_ccb_by_bd(BD_ADDR bd_addr) +{ + tAVDT_CCB *p_ccb = &avdt_cb.ccb[0]; + int i; + + for (i = 0; i < AVDT_NUM_LINKS; i++, p_ccb++) { + /* if allocated ccb has matching ccb */ + if (p_ccb->allocated && (!memcmp(p_ccb->peer_addr, bd_addr, BD_ADDR_LEN))) { + break; + } + } + + if (i == AVDT_NUM_LINKS) { + /* if no ccb found */ + p_ccb = NULL; + + AVDT_TRACE_DEBUG("No ccb for addr %02x-%02x-%02x-%02x-%02x-%02x\n", + bd_addr[0], bd_addr[1], bd_addr[2], bd_addr[3], bd_addr[4], bd_addr[5]); + } + return p_ccb; +} + +/******************************************************************************* +** +** Function avdt_ccb_alloc +** +** Description Allocate a channel control block. +** +** +** Returns pointer to the ccb, or NULL if none could be allocated. +** +*******************************************************************************/ +tAVDT_CCB *avdt_ccb_alloc(BD_ADDR bd_addr) +{ + tAVDT_CCB *p_ccb = &avdt_cb.ccb[0]; + int i; + + for (i = 0; i < AVDT_NUM_LINKS; i++, p_ccb++) { + if (!p_ccb->allocated) { + p_ccb->allocated = TRUE; + memcpy(p_ccb->peer_addr, bd_addr, BD_ADDR_LEN); + p_ccb->cmd_q = fixed_queue_new(QUEUE_SIZE_MAX); + p_ccb->rsp_q = fixed_queue_new(QUEUE_SIZE_MAX); + p_ccb->timer_entry.param = (UINT32) p_ccb; + AVDT_TRACE_DEBUG("avdt_ccb_alloc %d\n", i); + break; + } + } + + if (i == AVDT_NUM_LINKS) { + /* out of ccbs */ + p_ccb = NULL; + AVDT_TRACE_WARNING("Out of ccbs"); + } + return p_ccb; +} + +/******************************************************************************* +** +** Function avdt_ccb_dealloc +** +** Description Deallocate a stream control block. +** +** +** Returns void. +** +*******************************************************************************/ +void avdt_ccb_dealloc(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data) +{ + UNUSED(p_data); + + AVDT_TRACE_DEBUG("avdt_ccb_dealloc %d\n", avdt_ccb_to_idx(p_ccb)); + btu_free_timer(&p_ccb->timer_entry); + fixed_queue_free(p_ccb->cmd_q, NULL); + fixed_queue_free(p_ccb->rsp_q, NULL); + memset(p_ccb, 0, sizeof(tAVDT_CCB)); +} + +/******************************************************************************* +** +** Function avdt_ccb_to_idx +** +** Description Given a pointer to an ccb, return its index. +** +** +** Returns Index of ccb. +** +*******************************************************************************/ +UINT8 avdt_ccb_to_idx(tAVDT_CCB *p_ccb) +{ + /* use array arithmetic to determine index */ + return (UINT8) (p_ccb - avdt_cb.ccb); +} + +/******************************************************************************* +** +** Function avdt_ccb_by_idx +** +** Description Return ccb pointer based on ccb index. +** +** +** Returns pointer to the ccb, or NULL if none found. +** +*******************************************************************************/ +tAVDT_CCB *avdt_ccb_by_idx(UINT8 idx) +{ + tAVDT_CCB *p_ccb; + + /* verify index */ + if (idx < AVDT_NUM_LINKS) { + p_ccb = &avdt_cb.ccb[idx]; + } else { + p_ccb = NULL; + AVDT_TRACE_WARNING("No ccb for idx %d\n", idx); + } + return p_ccb; +} + +#endif /* #if (defined(AVDT_INCLUDED) && AVDT_INCLUDED == TRUE) */ diff --git a/lib/bt/host/bluedroid/stack/avdt/avdt_ccb_act.c b/lib/bt/host/bluedroid/stack/avdt/avdt_ccb_act.c new file mode 100644 index 00000000..7d7e7c6e --- /dev/null +++ b/lib/bt/host/bluedroid/stack/avdt/avdt_ccb_act.c @@ -0,0 +1,1084 @@ +/****************************************************************************** + * + * Copyright (C) 2006-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This module contains the action functions associated with the channel + * control block state machine. + * + ******************************************************************************/ + +#include +#include "stack/bt_types.h" +#include "common/bt_target.h" +#include "common/bt_defs.h" +#include "stack/avdt_api.h" +#include "stack/avdtc_api.h" +#include "avdt_int.h" +#include "stack/btu.h" +#include "stack/btm_api.h" +#include "osi/allocator.h" + +#if (defined(AVDT_INCLUDED) && AVDT_INCLUDED == TRUE) + +/******************************************************************************* +** +** Function avdt_ccb_clear_ccb +** +** Description This function clears out certain buffers, queues, and +** other data elements of a ccb. +** +** +** Returns void. +** +*******************************************************************************/ +static void avdt_ccb_clear_ccb(tAVDT_CCB *p_ccb) +{ + BT_HDR *p_buf; + + /* clear certain ccb variables */ + p_ccb->cong = FALSE; + p_ccb->ret_count = 0; + + /* free message being fragmented */ + if (p_ccb->p_curr_msg != NULL) { + osi_free(p_ccb->p_curr_msg); + p_ccb->p_curr_msg = NULL; + } + + /* free message being reassembled */ + if (p_ccb->p_rx_msg != NULL) { + osi_free(p_ccb->p_rx_msg); + p_ccb->p_rx_msg = NULL; + } + + /* clear out response queue */ + while ((p_buf = (BT_HDR *) fixed_queue_dequeue(p_ccb->rsp_q, 0)) != NULL) { + osi_free(p_buf); + } +} + +/******************************************************************************* +** +** Function avdt_ccb_chan_open +** +** Description This function calls avdt_ad_open_req() to +** initiate a signaling channel connection. +** +** +** Returns void. +** +*******************************************************************************/ +void avdt_ccb_chan_open(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data) +{ + UNUSED(p_data); + + BTM_SetOutService(p_ccb->peer_addr, BTM_SEC_SERVICE_AVDTP, AVDT_CHAN_SIG); + avdt_ad_open_req(AVDT_CHAN_SIG, p_ccb, NULL, AVDT_INT); +} + +/******************************************************************************* +** +** Function avdt_ccb_chan_close +** +** Description This function calls avdt_ad_close_req() to close a +** signaling channel connection. +** +** +** Returns void. +** +*******************************************************************************/ +void avdt_ccb_chan_close(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data) +{ + UNUSED(p_data); + + /* close the transport channel used by this CCB */ + avdt_ad_close_req(AVDT_CHAN_SIG, p_ccb, NULL); +} + +/******************************************************************************* +** +** Function avdt_ccb_chk_close +** +** Description This function checks for active streams on this CCB. +** If there are none, it starts an idle timer. +** +** +** Returns void. +** +*******************************************************************************/ +void avdt_ccb_chk_close(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data) +{ + int i; + tAVDT_SCB *p_scb = &avdt_cb.scb[0]; + UNUSED(p_data); + + /* see if there are any active scbs associated with this ccb */ + for (i = 0; i < AVDT_NUM_SEPS; i++, p_scb++) { + if ((p_scb->allocated) && (p_scb->p_ccb == p_ccb)) { + break; + } + } + + /* if no active scbs start idle timer */ + if (i == AVDT_NUM_SEPS) { + btu_start_timer(&p_ccb->timer_entry, BTU_TTYPE_AVDT_CCB_IDLE, avdt_cb.rcb.idle_tout); + } +} + +/******************************************************************************* +** +** Function avdt_ccb_hdl_discover_cmd +** +** Description This function is called when a discover command is +** received from the peer. It gathers up the stream +** information for all allocated streams and initiates +** sending of a discover response. +** +** +** Returns void. +** +*******************************************************************************/ +void avdt_ccb_hdl_discover_cmd(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data) +{ + tAVDT_SEP_INFO sep_info[AVDT_NUM_SEPS]; + tAVDT_SCB *p_scb = &avdt_cb.scb[0]; + int i; + + p_data->msg.discover_rsp.p_sep_info = sep_info; + p_data->msg.discover_rsp.num_seps = 0; + + /* for all allocated scbs */ + for (i = 0; i < AVDT_NUM_SEPS; i++, p_scb++) { + if (p_scb->allocated) { + /* copy sep info */ + sep_info[p_data->msg.discover_rsp.num_seps].in_use = p_scb->in_use; + sep_info[p_data->msg.discover_rsp.num_seps].seid = i + 1; + sep_info[p_data->msg.discover_rsp.num_seps].media_type = p_scb->cs.media_type; + sep_info[p_data->msg.discover_rsp.num_seps].tsep = p_scb->cs.tsep; + + p_data->msg.discover_rsp.num_seps++; + } + } + + /* send response */ + avdt_ccb_event(p_ccb, AVDT_CCB_API_DISCOVER_RSP_EVT, p_data); +} + +/******************************************************************************* +** +** Function avdt_ccb_hdl_discover_rsp +** +** Description This function is called when a discover response or +** reject is received from the peer. It calls the application +** callback function with the results. +** +** +** Returns void. +** +*******************************************************************************/ +void avdt_ccb_hdl_discover_rsp(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data) +{ + /* we're done with procedure */ + p_ccb->proc_busy = FALSE; + + /* call app callback with results */ + (*p_ccb->proc_cback)(0, p_ccb->peer_addr, AVDT_DISCOVER_CFM_EVT, + (tAVDT_CTRL *)(&p_data->msg.discover_rsp)); +} + +/******************************************************************************* +** +** Function avdt_ccb_hdl_getcap_cmd +** +** Description This function is called when a get capabilities command +** is received from the peer. It retrieves the stream +** configuration for the requested stream and initiates +** sending of a get capabilities response. +** +** +** Returns void. +** +*******************************************************************************/ +void avdt_ccb_hdl_getcap_cmd(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data) +{ + tAVDT_SCB *p_scb; + + /* look up scb for seid sent to us */ + p_scb = avdt_scb_by_hdl(p_data->msg.single.seid); + + p_data->msg.svccap.p_cfg = &p_scb->cs.cfg; + + avdt_ccb_event(p_ccb, AVDT_CCB_API_GETCAP_RSP_EVT, p_data); +} + +/******************************************************************************* +** +** Function avdt_ccb_hdl_getcap_rsp +** +** Description This function is called with a get capabilities response +** or reject is received from the peer. It calls the +** application callback function with the results. +** +** +** Returns void. +** +*******************************************************************************/ +void avdt_ccb_hdl_getcap_rsp(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data) +{ + /* we're done with procedure */ + p_ccb->proc_busy = FALSE; + + /* call app callback with results */ + (*p_ccb->proc_cback)(0, p_ccb->peer_addr, AVDT_GETCAP_CFM_EVT, + (tAVDT_CTRL *)(&p_data->msg.svccap)); +} + +/******************************************************************************* +** +** Function avdt_ccb_hdl_start_cmd +** +** Description This function is called when a start command is received +** from the peer. It verifies that all requested streams +** are in the proper state. If so, it initiates sending of +** a start response. Otherwise it sends a start reject. +** +** +** Returns void. +** +*******************************************************************************/ +void avdt_ccb_hdl_start_cmd(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data) +{ + UINT8 err_code = 0; + + /* verify all streams in the right state */ + UINT8 seid = avdt_scb_verify(p_ccb, AVDT_VERIFY_START, p_data->msg.multi.seid_list, + p_data->msg.multi.num_seps, &err_code); + if (seid == 0 && err_code == 0) { + /* we're ok, send response */ + avdt_ccb_event(p_ccb, AVDT_CCB_API_START_RSP_EVT, p_data); + } else { + /* not ok, send reject */ + p_data->msg.hdr.err_code = err_code; + p_data->msg.hdr.err_param = seid; + avdt_msg_send_rej(p_ccb, AVDT_SIG_START, &p_data->msg); + } +} + +/******************************************************************************* +** +** Function avdt_ccb_hdl_start_rsp +** +** Description This function is called when a start response or reject +** is received from the peer. Using the SEIDs stored in the +** current command message, it sends a start response or start +** reject event to each SCB associated with the command. +** +** +** Returns void. +** +*******************************************************************************/ +void avdt_ccb_hdl_start_rsp(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data) +{ + UINT8 event; + int i; + UINT8 *p; + tAVDT_SCB *p_scb; + + /* determine rsp or rej event */ + event = (p_data->msg.hdr.err_code == 0) ? + AVDT_SCB_MSG_START_RSP_EVT : AVDT_SCB_MSG_START_REJ_EVT; + + /* get to where seid's are stashed in current cmd */ + p = (UINT8 *)(p_ccb->p_curr_cmd + 1); + + /* little trick here; length of current command equals number of streams */ + for (i = 0; i < p_ccb->p_curr_cmd->len; i++) { + if ((p_scb = avdt_scb_by_hdl(p[i])) != NULL) { + avdt_scb_event(p_scb, event, (tAVDT_SCB_EVT *) &p_data->msg); + } + } +} + +/******************************************************************************* +** +** Function avdt_ccb_hdl_suspend_cmd +** +** Description This function is called when a suspend command is received +** from the peer. It verifies that all requested streams are +** in the proper state. If so, it initiates sending of a +** suspend response. Otherwise it sends a suspend reject. + +** +** +** Returns void. +** +*******************************************************************************/ +void avdt_ccb_hdl_suspend_cmd(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data) +{ + UINT8 seid; + UINT8 err_code = 0; + + /* verify all streams in the right state */ + if ((seid = avdt_scb_verify(p_ccb, AVDT_VERIFY_SUSPEND, p_data->msg.multi.seid_list, + p_data->msg.multi.num_seps, &err_code)) == 0 && + err_code == 0) { + /* we're ok, send response */ + avdt_ccb_event(p_ccb, AVDT_CCB_API_SUSPEND_RSP_EVT, p_data); + } else { + /* not ok, send reject */ + p_data->msg.hdr.err_code = err_code; + p_data->msg.hdr.err_param = seid; + avdt_msg_send_rej(p_ccb, AVDT_SIG_SUSPEND, &p_data->msg); + } +} + +/******************************************************************************* +** +** Function avdt_ccb_hdl_suspend_rsp +** +** Description This function is called when a suspend response or reject +** is received from the peer. Using the SEIDs stored in the +** current command message, it sends a suspend response or +** suspend reject event to each SCB associated with the command. +** +** +** +** Returns void. +** +*******************************************************************************/ +void avdt_ccb_hdl_suspend_rsp(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data) +{ + UINT8 event; + int i; + UINT8 *p; + tAVDT_SCB *p_scb; + + /* determine rsp or rej event */ + event = (p_data->msg.hdr.err_code == 0) ? + AVDT_SCB_MSG_SUSPEND_RSP_EVT : AVDT_SCB_MSG_SUSPEND_REJ_EVT; + + /* get to where seid's are stashed in current cmd */ + p = (UINT8 *)(p_ccb->p_curr_cmd + 1); + + /* little trick here; length of current command equals number of streams */ + for (i = 0; i < p_ccb->p_curr_cmd->len; i++) { + if ((p_scb = avdt_scb_by_hdl(p[i])) != NULL) { + avdt_scb_event(p_scb, event, (tAVDT_SCB_EVT *) &p_data->msg); + } + } +} + +/******************************************************************************* +** +** Function avdt_ccb_snd_discover_cmd +** +** Description This function is called to send a discover command to the +** peer. It copies variables needed for the procedure from +** the event to the CCB. It marks the CCB as busy and then +** sends a discover command. +** +** +** Returns void. +** +*******************************************************************************/ +void avdt_ccb_snd_discover_cmd(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data) +{ + /* store info in ccb struct */ + p_ccb->p_proc_data = p_data->discover.p_sep_info; + p_ccb->proc_cback = p_data->discover.p_cback; + p_ccb->proc_param = p_data->discover.num_seps; + + /* we're busy */ + p_ccb->proc_busy = TRUE; + + /* build and queue discover req */ + avdt_msg_send_cmd(p_ccb, NULL, AVDT_SIG_DISCOVER, NULL); +} + +/******************************************************************************* +** +** Function avdt_ccb_snd_discover_rsp +** +** Description This function is called to send a discover response to +** the peer. It takes the stream information passed in the +** event and sends a discover response. +** +** +** Returns void. +** +*******************************************************************************/ +void avdt_ccb_snd_discover_rsp(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data) +{ + /* send response */ + avdt_msg_send_rsp(p_ccb, AVDT_SIG_DISCOVER, &p_data->msg); +} + +/******************************************************************************* +** +** Function avdt_ccb_snd_getcap_cmd +** +** Description This function is called to send a get capabilities command +** to the peer. It copies variables needed for the procedure +** from the event to the CCB. It marks the CCB as busy and +** then sends a get capabilities command. +** +** +** Returns void. +** +*******************************************************************************/ +void avdt_ccb_snd_getcap_cmd(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data) +{ + UINT8 sig_id = AVDT_SIG_GETCAP; + + /* store info in ccb struct */ + p_ccb->p_proc_data = p_data->getcap.p_cfg; + p_ccb->proc_cback = p_data->getcap.p_cback; + + /* we're busy */ + p_ccb->proc_busy = TRUE; + + /* build and queue discover req */ + if (p_data->msg.hdr.sig_id == AVDT_SIG_GET_ALLCAP) { + sig_id = AVDT_SIG_GET_ALLCAP; + } + + avdt_msg_send_cmd(p_ccb, NULL, sig_id, (tAVDT_MSG *) &p_data->getcap.single); +} + +/******************************************************************************* +** +** Function avdt_ccb_snd_getcap_rsp +** +** Description This function is called to send a get capabilities response +** to the peer. It takes the stream information passed in the +** event and sends a get capabilities response. +** +** +** Returns void. +** +*******************************************************************************/ +void avdt_ccb_snd_getcap_rsp(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data) +{ + UINT8 sig_id = AVDT_SIG_GETCAP; + + if (p_data->msg.hdr.sig_id == AVDT_SIG_GET_ALLCAP) { + sig_id = AVDT_SIG_GET_ALLCAP; + } + + /* send response */ + avdt_msg_send_rsp(p_ccb, sig_id, &p_data->msg); +} + +/******************************************************************************* +** +** Function avdt_ccb_snd_start_cmd +** +** Description This function is called to send a start command to the +** peer. It verifies that all requested streams are in the +** proper state. If so, it sends a start command. Otherwise +** send ourselves back a start reject. +** +** +** Returns void. +** +*******************************************************************************/ +void avdt_ccb_snd_start_cmd(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data) +{ + int i; + tAVDT_SCB *p_scb; + tAVDT_MSG avdt_msg; + UINT8 seid_list[AVDT_NUM_SEPS]; + + /* make copy of our seid list */ + memcpy(seid_list, p_data->msg.multi.seid_list, p_data->msg.multi.num_seps); + + /* verify all streams in the right state */ + if ((avdt_msg.hdr.err_param = avdt_scb_verify(p_ccb, AVDT_VERIFY_OPEN, p_data->msg.multi.seid_list, + p_data->msg.multi.num_seps, &avdt_msg.hdr.err_code)) == 0) { + /* set peer seid list in messsage */ + avdt_scb_peer_seid_list(&p_data->msg.multi); + + /* send command */ + avdt_msg_send_cmd(p_ccb, seid_list, AVDT_SIG_START, &p_data->msg); + } else { + /* failed; send ourselves a reject for each stream */ + for (i = 0; i < p_data->msg.multi.num_seps; i++) { + if ((p_scb = avdt_scb_by_hdl(seid_list[i])) != NULL) { + avdt_scb_event(p_scb, AVDT_SCB_MSG_START_REJ_EVT, (tAVDT_SCB_EVT *) &avdt_msg.hdr); + } + } + } +} + +/******************************************************************************* +** +** Function avdt_ccb_snd_start_rsp +** +** Description This function is called to send a start response to the +** peer. It takes the stream information passed in the event +** and sends a start response. Then it sends a start event +** to the SCB for each stream. +** +** +** Returns void. +** +*******************************************************************************/ +void avdt_ccb_snd_start_rsp(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data) +{ + tAVDT_SCB *p_scb; + int i; + + /* send response message */ + avdt_msg_send_rsp(p_ccb, AVDT_SIG_START, &p_data->msg); + + /* send start event to each scb */ + for (i = 0; i < p_data->msg.multi.num_seps; i++) { + if ((p_scb = avdt_scb_by_hdl(p_data->msg.multi.seid_list[i])) != NULL) { + avdt_scb_event(p_scb, AVDT_SCB_MSG_START_CMD_EVT, NULL); + } + } +} + +/******************************************************************************* +** +** Function avdt_ccb_snd_suspend_cmd +** +** Description This function is called to send a suspend command to the +** peer. It verifies that all requested streams are in the +** proper state. If so, it sends a suspend command. +** Otherwise it calls the callback function for each requested +** stream and sends a suspend confirmation with failure. +** +** +** Returns void. +** +*******************************************************************************/ +void avdt_ccb_snd_suspend_cmd(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data) +{ + int i; + tAVDT_SCB *p_scb; + tAVDT_MSG avdt_msg; + UINT8 seid_list[AVDT_NUM_SEPS]; + + /* make copy of our seid list */ + memcpy(seid_list, p_data->msg.multi.seid_list, p_data->msg.multi.num_seps); + + /* verify all streams in the right state */ + if ((avdt_msg.hdr.err_param = avdt_scb_verify(p_ccb, AVDT_VERIFY_STREAMING, p_data->msg.multi.seid_list, + p_data->msg.multi.num_seps, &avdt_msg.hdr.err_code)) == 0) { + /* set peer seid list in messsage */ + avdt_scb_peer_seid_list(&p_data->msg.multi); + + /* send command */ + avdt_msg_send_cmd(p_ccb, seid_list, AVDT_SIG_SUSPEND, &p_data->msg); + } else { + /* failed; send ourselves a reject for each stream */ + for (i = 0; i < p_data->msg.multi.num_seps; i++) { + if ((p_scb = avdt_scb_by_hdl(seid_list[i])) != NULL) { + avdt_scb_event(p_scb, AVDT_SCB_MSG_SUSPEND_REJ_EVT, (tAVDT_SCB_EVT *) &avdt_msg.hdr); + } + } + } +} + +/******************************************************************************* +** +** Function avdt_ccb_snd_suspend_rsp +** +** Description This function is called to send a suspend response to the +** peer. It takes the stream information passed in the event +** and sends a suspend response. Then it sends a suspend event +** to the SCB for each stream. +** +** +** Returns void. +** +*******************************************************************************/ +void avdt_ccb_snd_suspend_rsp(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data) +{ + tAVDT_SCB *p_scb; + int i; + + /* send response message */ + avdt_msg_send_rsp(p_ccb, AVDT_SIG_SUSPEND, &p_data->msg); + + /* send start event to each scb */ + for (i = 0; i < p_data->msg.multi.num_seps; i++) { + if ((p_scb = avdt_scb_by_hdl(p_data->msg.multi.seid_list[i])) != NULL) { + avdt_scb_event(p_scb, AVDT_SCB_MSG_SUSPEND_CMD_EVT, NULL); + } + } +} + +/******************************************************************************* +** +** Function avdt_ccb_clear_cmds +** +** Description This function is called when the signaling channel is +** closed to clean up any pending commands. For each pending +** command in the command queue, it frees the command and +** calls the application callback function indicating failure. +** Certain CCB variables are also initialized. +** +** +** Returns void. +** +*******************************************************************************/ +void avdt_ccb_clear_cmds(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data) +{ + int i; + tAVDT_SCB *p_scb = &avdt_cb.scb[0]; + UINT8 err_code = AVDT_ERR_CONNECT; + UNUSED(p_data); + + /* clear the ccb */ + avdt_ccb_clear_ccb(p_ccb); + + /* clear out command queue; this is a little tricky here; we need + ** to handle the case where there is a command on deck in p_curr_cmd, + ** plus we need to clear out the queue + */ + do { + /* we know p_curr_cmd = NULL after this */ + avdt_ccb_cmd_fail(p_ccb, (tAVDT_CCB_EVT *) &err_code); + + /* set up next message */ + p_ccb->p_curr_cmd = (BT_HDR *) fixed_queue_dequeue(p_ccb->cmd_q, 0); + + } while (p_ccb->p_curr_cmd != NULL); + + /* send a CC_CLOSE_EVT any active scbs associated with this ccb */ + for (i = 0; i < AVDT_NUM_SEPS; i++, p_scb++) { + if ((p_scb->allocated) && (p_scb->p_ccb == p_ccb)) { + avdt_scb_event(p_scb, AVDT_SCB_CC_CLOSE_EVT, NULL); + } + } +} + +/******************************************************************************* +** +** Function avdt_ccb_cmd_fail +** +** Description This function is called when there is a response timeout. +** The currently pending command is freed and we fake a +** reject message back to ourselves. +** +** +** Returns void. +** +*******************************************************************************/ +void avdt_ccb_cmd_fail(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data) +{ + tAVDT_MSG msg; + UINT8 evt; + tAVDT_SCB *p_scb; + + if (p_ccb->p_curr_cmd != NULL) { + /* set up data */ + msg.hdr.err_code = p_data->err_code; + msg.hdr.err_param = 0; + msg.hdr.ccb_idx = avdt_ccb_to_idx(p_ccb); + + /* pretend that we received a rej message */ + evt = avdt_msg_rej_2_evt[p_ccb->p_curr_cmd->event - 1]; + + if (evt & AVDT_CCB_MKR) { + avdt_ccb_event(p_ccb, (UINT8) (evt & ~AVDT_CCB_MKR), (tAVDT_CCB_EVT *) &msg); + } else { + /* we get the scb out of the current cmd */ + p_scb = avdt_scb_by_hdl(*((UINT8 *)(p_ccb->p_curr_cmd + 1))); + if (p_scb != NULL) { + avdt_scb_event(p_scb, evt, (tAVDT_SCB_EVT *) &msg); + } + } + + osi_free(p_ccb->p_curr_cmd); + p_ccb->p_curr_cmd = NULL; + } +} + +/******************************************************************************* +** +** Function avdt_ccb_free_cmd +** +** Description This function is called when a response is received for a +** currently pending command. The command is freed. +** +** +** Returns void. +** +*******************************************************************************/ +void avdt_ccb_free_cmd(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data) +{ + UNUSED(p_data); + + if (p_ccb->p_curr_cmd != NULL) { + osi_free(p_ccb->p_curr_cmd); + p_ccb->p_curr_cmd = NULL; + } +} + +/******************************************************************************* +** +** Function avdt_ccb_cong_state +** +** Description This function is called to set the congestion state for +** the CCB. +** +** +** Returns void. +** +*******************************************************************************/ +void avdt_ccb_cong_state(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data) +{ + p_ccb->cong = p_data->llcong; +} + +/******************************************************************************* +** +** Function avdt_ccb_ret_cmd +** +** Description This function is called to retransmit the currently +** pending command. The retransmission count is incremented. +** If the count reaches the maximum number of retransmissions, +** the event is treated as a response timeout. +** +** +** Returns void. +** +*******************************************************************************/ +void avdt_ccb_ret_cmd(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data) +{ + UINT8 err_code = AVDT_ERR_TIMEOUT; + BT_HDR *p_msg; + + p_ccb->ret_count++; + if (p_ccb->ret_count == AVDT_RET_MAX) { + /* command failed */ + p_ccb->ret_count = 0; + avdt_ccb_cmd_fail(p_ccb, (tAVDT_CCB_EVT *) &err_code); + + /* go to next queued command */ + avdt_ccb_snd_cmd(p_ccb, p_data); + } else { + /* if command pending and we're not congested and not sending a fragment */ + if ((!p_ccb->cong) && (p_ccb->p_curr_msg == NULL) && (p_ccb->p_curr_cmd != NULL)) { + /* make copy of message in p_curr_cmd and send it */ + if ((p_msg = (BT_HDR *) osi_malloc(AVDT_CMD_BUF_SIZE)) != NULL) { + memcpy(p_msg, p_ccb->p_curr_cmd, + (sizeof(BT_HDR) + p_ccb->p_curr_cmd->offset + p_ccb->p_curr_cmd->len)); + avdt_msg_send(p_ccb, p_msg); + } + } + + /* restart timer */ + btu_start_timer(&p_ccb->timer_entry, BTU_TTYPE_AVDT_CCB_RET, avdt_cb.rcb.ret_tout); + } +} + +/******************************************************************************* +** +** Function avdt_ccb_snd_cmd +** +** Description This function is called the send the next command, +** if any, in the command queue. +** +** +** Returns void. +** +*******************************************************************************/ +void avdt_ccb_snd_cmd(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data) +{ + BT_HDR *p_msg; + UNUSED(p_data); + + /* do we have commands to send? send next command; make sure we're clear; + ** not congested, not sending fragment, not waiting for response + */ + if ((!p_ccb->cong) && (p_ccb->p_curr_msg == NULL) && (p_ccb->p_curr_cmd == NULL)) { + if ((p_msg = (BT_HDR *) fixed_queue_dequeue(p_ccb->cmd_q, 0)) != NULL) { + /* make a copy of buffer in p_curr_cmd */ + if ((p_ccb->p_curr_cmd = (BT_HDR *) osi_malloc(AVDT_CMD_BUF_SIZE)) != NULL) { + memcpy(p_ccb->p_curr_cmd, p_msg, (sizeof(BT_HDR) + p_msg->offset + p_msg->len)); + + avdt_msg_send(p_ccb, p_msg); + } + } + } +} + +/******************************************************************************* +** +** Function avdt_ccb_snd_msg +** +** Description +** +** +** Returns void. +** +*******************************************************************************/ +void avdt_ccb_snd_msg(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data) +{ + BT_HDR *p_msg; + UNUSED(p_data); + + /* if not congested */ + if (!p_ccb->cong) { + /* are we sending a fragmented message? continue sending fragment */ + if (p_ccb->p_curr_msg != NULL) { + avdt_msg_send(p_ccb, NULL); + } + /* do we have responses to send? send them */ + else if (!fixed_queue_is_empty(p_ccb->rsp_q)) { + while ((p_msg = (BT_HDR *)fixed_queue_dequeue(p_ccb->rsp_q, 0)) != NULL) { + if (avdt_msg_send(p_ccb, p_msg) == TRUE) { + /* break out if congested */ + break; + } + } + } + + /* do we have commands to send? send next command */ + avdt_ccb_snd_cmd(p_ccb, NULL); + } +} + +/******************************************************************************* +** +** Function avdt_ccb_set_reconn +** +** Description This function is called to enable a reconnect attempt when +** a channel transitions from closing to idle state. It sets +** the reconn variable to TRUE. +** +** +** Returns void. +** +*******************************************************************************/ +void avdt_ccb_set_reconn(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data) +{ + UNUSED(p_data); + + p_ccb->reconn = TRUE; +} + +/******************************************************************************* +** +** Function avdt_ccb_clr_reconn +** +** Description This function is called to clear the reconn variable. +** +** +** Returns void. +** +*******************************************************************************/ +void avdt_ccb_clr_reconn(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data) +{ + UNUSED(p_data); + + p_ccb->reconn = FALSE; +} + +/******************************************************************************* +** +** Function avdt_ccb_chk_reconn +** +** Description This function is called to check if a reconnect attempt +** is enabled. If enabled, it sends an AVDT_CCB_UL_OPEN_EVT +** to the CCB. If disabled, the CCB is deallocated. +** +** +** Returns void. +** +*******************************************************************************/ +void avdt_ccb_chk_reconn(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data) +{ + UINT8 err_code = AVDT_ERR_CONNECT; + UNUSED(p_data); + + if (p_ccb->reconn) { + p_ccb->reconn = FALSE; + + /* clear out ccb */ + avdt_ccb_clear_ccb(p_ccb); + + /* clear out current command, if any */ + avdt_ccb_cmd_fail(p_ccb, (tAVDT_CCB_EVT *) &err_code); + + /* reopen the signaling channel */ + avdt_ccb_event(p_ccb, AVDT_CCB_UL_OPEN_EVT, NULL); + } else { + avdt_ccb_ll_closed(p_ccb, NULL); + } +} + +/******************************************************************************* +** +** Function avdt_ccb_chk_timer +** +** Description This function stops the CCB timer if the idle timer is +** running. +** +** +** Returns void. +** +*******************************************************************************/ +void avdt_ccb_chk_timer(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data) +{ + UNUSED(p_data); + + if (p_ccb->timer_entry.event == BTU_TTYPE_AVDT_CCB_IDLE) { + btu_stop_timer(&p_ccb->timer_entry); + } +} + +/******************************************************************************* +** +** Function avdt_ccb_set_conn +** +** Description Set CCB variables associated with AVDT_ConnectReq(). +** +** +** Returns void. +** +*******************************************************************************/ +void avdt_ccb_set_conn(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data) +{ + /* save callback */ + p_ccb->p_conn_cback = p_data->connect.p_cback; + + /* set security level */ + BTM_SetSecurityLevel(TRUE, "", BTM_SEC_SERVICE_AVDTP, p_data->connect.sec_mask, + AVDT_PSM, BTM_SEC_PROTO_AVDT, AVDT_CHAN_SIG); +} + +/******************************************************************************* +** +** Function avdt_ccb_set_disconn +** +** Description Set CCB variables associated with AVDT_DisconnectReq(). +** +** +** Returns void. +** +*******************************************************************************/ +void avdt_ccb_set_disconn(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data) +{ + /* + AVDT_TRACE_EVENT("avdt_ccb_set_disconn:conn:x%x, api:x%x", + p_ccb->p_conn_cback, p_data->disconnect.p_cback); + */ + /* save callback */ + if (p_data->disconnect.p_cback) { + p_ccb->p_conn_cback = p_data->disconnect.p_cback; + } +} + +/******************************************************************************* +** +** Function avdt_ccb_do_disconn +** +** Description Do action associated with AVDT_DisconnectReq(). +** +** +** Returns void. +** +*******************************************************************************/ +void avdt_ccb_do_disconn(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data) +{ + UNUSED(p_data); + + /* clear any pending commands */ + avdt_ccb_clear_cmds(p_ccb, NULL); + + /* close channel */ + avdt_ccb_chan_close(p_ccb, NULL); +} + +/******************************************************************************* +** +** Function avdt_ccb_ll_closed +** +** Description Clear commands from and deallocate CCB. +** +** +** Returns void. +** +*******************************************************************************/ +void avdt_ccb_ll_closed(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data) +{ + tAVDT_CTRL_CBACK *p_cback; + BD_ADDR bd_addr; + tAVDT_CTRL avdt_ctrl; + UINT8 disc_rsn; + UNUSED(p_data); + + /* clear any pending commands */ + avdt_ccb_clear_cmds(p_ccb, NULL); + + /* save callback pointer, bd addr */ + p_cback = p_ccb->p_conn_cback; + if (!p_cback) { + p_cback = avdt_cb.p_conn_cback; + } + memcpy(bd_addr, p_ccb->peer_addr, BD_ADDR_LEN); + + disc_rsn = p_ccb->disc_rsn; + + /* dealloc ccb */ + avdt_ccb_dealloc(p_ccb, NULL); + + /* call callback */ + if (p_cback) { + avdt_ctrl.hdr.err_code = 0; + avdt_ctrl.hdr.err_param = disc_rsn; + + (*p_cback)(0, bd_addr, AVDT_DISCONNECT_IND_EVT, &avdt_ctrl); + } +} + +/******************************************************************************* +** +** Function avdt_ccb_ll_opened +** +** Description Call callback on open. +** +** +** Returns void. +** +*******************************************************************************/ +void avdt_ccb_ll_opened(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data) +{ + tAVDT_CTRL avdt_ctrl; + + p_ccb->ll_opened = TRUE; + + if (!p_ccb->p_conn_cback) { + p_ccb->p_conn_cback = avdt_cb.p_conn_cback; + } + + /* call callback */ + if (p_ccb->p_conn_cback) { + avdt_ctrl.hdr.err_code = 0; + avdt_ctrl.hdr.err_param = p_data->msg.hdr.err_param; + (*p_ccb->p_conn_cback)(0, p_ccb->peer_addr, AVDT_CONNECT_IND_EVT, &avdt_ctrl); + } +} + +#endif /* #if (defined(AVDT_INCLUDED) && AVDT_INCLUDED == TRUE) */ diff --git a/lib/bt/host/bluedroid/stack/avdt/avdt_l2c.c b/lib/bt/host/bluedroid/stack/avdt/avdt_l2c.c new file mode 100644 index 00000000..c7c1e58a --- /dev/null +++ b/lib/bt/host/bluedroid/stack/avdt/avdt_l2c.c @@ -0,0 +1,501 @@ +/****************************************************************************** + * + * Copyright (C) 2002-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This AVDTP adaption layer module interfaces to L2CAP + * + ******************************************************************************/ + +#include +#include "stack/bt_types.h" +#include "common/bt_target.h" +#include "common/bt_defs.h" +#include "stack/avdt_api.h" +#include "stack/avdtc_api.h" +#include "avdt_int.h" +#include "stack/l2c_api.h" +#include "stack/l2cdefs.h" +#include "stack/btm_api.h" +#include "btm_int.h" +#include "osi/allocator.h" + +#if (defined(AVDT_INCLUDED) && AVDT_INCLUDED == TRUE) + +/* callback function declarations */ +void avdt_l2c_connect_ind_cback(BD_ADDR bd_addr, UINT16 lcid, UINT16 psm, UINT8 id); +void avdt_l2c_connect_cfm_cback(UINT16 lcid, UINT16 result); +void avdt_l2c_config_cfm_cback(UINT16 lcid, tL2CAP_CFG_INFO *p_cfg); +void avdt_l2c_config_ind_cback(UINT16 lcid, tL2CAP_CFG_INFO *p_cfg); +void avdt_l2c_disconnect_ind_cback(UINT16 lcid, BOOLEAN ack_needed); +void avdt_l2c_disconnect_cfm_cback(UINT16 lcid, UINT16 result); +void avdt_l2c_congestion_ind_cback(UINT16 lcid, BOOLEAN is_congested); +void avdt_l2c_data_ind_cback(UINT16 lcid, BT_HDR *p_buf); + +/* L2CAP callback function structure */ +const tL2CAP_APPL_INFO avdt_l2c_appl = { + avdt_l2c_connect_ind_cback, + avdt_l2c_connect_cfm_cback, + NULL, + avdt_l2c_config_ind_cback, + avdt_l2c_config_cfm_cback, + avdt_l2c_disconnect_ind_cback, + avdt_l2c_disconnect_cfm_cback, + NULL, + avdt_l2c_data_ind_cback, + avdt_l2c_congestion_ind_cback, + NULL /* tL2CA_TX_COMPLETE_CB */ +}; + +/******************************************************************************* +** +** Function avdt_sec_check_complete_term +** +** Description The function called when Security Manager finishes +** verification of the service side connection +** +** Returns void +** +*******************************************************************************/ +static void avdt_sec_check_complete_term (BD_ADDR bd_addr, tBT_TRANSPORT transport, + void *p_ref_data, UINT8 res) +{ + tAVDT_CCB *p_ccb = NULL; + tL2CAP_CFG_INFO cfg; + tAVDT_TC_TBL *p_tbl; + UNUSED(p_ref_data); + + AVDT_TRACE_DEBUG("avdt_sec_check_complete_term res: %d\n", res); + if (!bd_addr) { + AVDT_TRACE_WARNING("avdt_sec_check_complete_term: NULL BD_ADDR"); + return; + + } + p_ccb = avdt_ccb_by_bd(bd_addr); + + p_tbl = avdt_ad_tc_tbl_by_st(AVDT_CHAN_SIG, p_ccb, AVDT_AD_ST_SEC_ACP); + if (p_tbl == NULL) { + return; + } + + if (res == BTM_SUCCESS) { + /* Send response to the L2CAP layer. */ + L2CA_ConnectRsp (bd_addr, p_tbl->id, p_tbl->lcid, L2CAP_CONN_OK, L2CAP_CONN_OK); + + /* store idx in LCID table, store LCID in routing table */ + avdt_cb.ad.lcid_tbl[p_tbl->lcid - L2CAP_BASE_APPL_CID] = avdt_ad_tc_tbl_to_idx(p_tbl); + avdt_cb.ad.rt_tbl[avdt_ccb_to_idx(p_ccb)][p_tbl->tcid].lcid = p_tbl->lcid; + + /* transition to configuration state */ + p_tbl->state = AVDT_AD_ST_CFG; + + /* Send L2CAP config req */ + memset(&cfg, 0, sizeof(tL2CAP_CFG_INFO)); + cfg.mtu_present = TRUE; + cfg.mtu = p_tbl->my_mtu; + cfg.flush_to_present = TRUE; + cfg.flush_to = p_tbl->my_flush_to; + L2CA_ConfigReq(p_tbl->lcid, &cfg); + } else { + L2CA_ConnectRsp (bd_addr, p_tbl->id, p_tbl->lcid, L2CAP_CONN_SECURITY_BLOCK, L2CAP_CONN_OK); + avdt_ad_tc_close_ind(p_tbl, L2CAP_CONN_SECURITY_BLOCK); + } +} + +/******************************************************************************* +** +** Function avdt_sec_check_complete_orig +** +** Description The function called when Security Manager finishes +** verification of the service side connection +** +** Returns void +** +*******************************************************************************/ +static void avdt_sec_check_complete_orig (BD_ADDR bd_addr, tBT_TRANSPORT transport, + void *p_ref_data, UINT8 res) +{ + tAVDT_CCB *p_ccb = NULL; + tL2CAP_CFG_INFO cfg; + tAVDT_TC_TBL *p_tbl; + UNUSED(p_ref_data); + + AVDT_TRACE_DEBUG("avdt_sec_check_complete_orig res: %d\n", res); + if (bd_addr) { + p_ccb = avdt_ccb_by_bd(bd_addr); + } + p_tbl = avdt_ad_tc_tbl_by_st(AVDT_CHAN_SIG, p_ccb, AVDT_AD_ST_SEC_INT); + if (p_tbl == NULL) { + return; + } + + if ( res == BTM_SUCCESS ) { + /* set channel state */ + p_tbl->state = AVDT_AD_ST_CFG; + + /* Send L2CAP config req */ + memset(&cfg, 0, sizeof(tL2CAP_CFG_INFO)); + cfg.mtu_present = TRUE; + cfg.mtu = p_tbl->my_mtu; + cfg.flush_to_present = TRUE; + cfg.flush_to = p_tbl->my_flush_to; + L2CA_ConfigReq(p_tbl->lcid, &cfg); + } else { + L2CA_DisconnectReq (p_tbl->lcid); + avdt_ad_tc_close_ind(p_tbl, L2CAP_CONN_SECURITY_BLOCK); + } +} +/******************************************************************************* +** +** Function avdt_l2c_connect_ind_cback +** +** Description This is the L2CAP connect indication callback function. +** +** +** Returns void +** +*******************************************************************************/ +void avdt_l2c_connect_ind_cback(BD_ADDR bd_addr, UINT16 lcid, UINT16 psm, UINT8 id) +{ + tAVDT_CCB *p_ccb; + tAVDT_TC_TBL *p_tbl = NULL; + UINT16 result; + tL2CAP_CFG_INFO cfg; + tBTM_STATUS rc; + UNUSED(psm); + + /* do we already have a control channel for this peer? */ + if ((p_ccb = avdt_ccb_by_bd(bd_addr)) == NULL) { + /* no, allocate ccb */ + if ((p_ccb = avdt_ccb_alloc(bd_addr)) == NULL) { + /* no ccb available, reject L2CAP connection */ + result = L2CAP_CONN_NO_RESOURCES; + } else { + /* allocate and set up entry; first channel is always signaling */ + p_tbl = avdt_ad_tc_tbl_alloc(p_ccb); + p_tbl->my_mtu = avdt_cb.rcb.ctrl_mtu; + p_tbl->my_flush_to = L2CAP_DEFAULT_FLUSH_TO; + p_tbl->tcid = AVDT_CHAN_SIG; + p_tbl->lcid = lcid; + p_tbl->id = id; + p_tbl->state = AVDT_AD_ST_SEC_ACP; + p_tbl->cfg_flags = AVDT_L2C_CFG_CONN_ACP; + + /* Check the security */ + rc = btm_sec_mx_access_request (bd_addr, AVDT_PSM, + FALSE, BTM_SEC_PROTO_AVDT, + AVDT_CHAN_SIG, + &avdt_sec_check_complete_term, NULL); + if (rc == BTM_CMD_STARTED) { + L2CA_ConnectRsp (p_ccb->peer_addr, p_tbl->id, lcid, L2CAP_CONN_PENDING, L2CAP_CONN_OK); + } + return; + } + } + /* deal with simultaneous control channel connect case */ + else if ((p_tbl = avdt_ad_tc_tbl_by_st(AVDT_CHAN_SIG, p_ccb, AVDT_AD_ST_CONN)) != NULL) { + /* reject their connection */ + result = L2CAP_CONN_NO_RESOURCES; + } + /* this must be a traffic channel; are we accepting a traffic channel + ** for this ccb? + */ + else if ((p_tbl = avdt_ad_tc_tbl_by_st(AVDT_CHAN_MEDIA, p_ccb, AVDT_AD_ST_ACP)) != NULL) { + /* yes; proceed with connection */ + result = L2CAP_CONN_OK; + } +#if AVDT_REPORTING == TRUE + /* this must be a reporting channel; are we accepting a reporting channel + ** for this ccb? + */ + else if ((p_tbl = avdt_ad_tc_tbl_by_st(AVDT_CHAN_REPORT, p_ccb, AVDT_AD_ST_ACP)) != NULL) { + /* yes; proceed with connection */ + result = L2CAP_CONN_OK; + } +#endif + /* else we're not listening for traffic channel; reject */ + else { + result = L2CAP_CONN_NO_PSM; + } + + /* Send L2CAP connect rsp */ + L2CA_ConnectRsp(bd_addr, id, lcid, result, 0); + + /* if result ok, proceed with connection */ + if (result == L2CAP_CONN_OK) { + /* store idx in LCID table, store LCID in routing table */ + avdt_cb.ad.lcid_tbl[lcid - L2CAP_BASE_APPL_CID] = avdt_ad_tc_tbl_to_idx(p_tbl); + avdt_cb.ad.rt_tbl[avdt_ccb_to_idx(p_ccb)][p_tbl->tcid].lcid = lcid; + + /* transition to configuration state */ + p_tbl->state = AVDT_AD_ST_CFG; + + /* Send L2CAP config req */ + memset(&cfg, 0, sizeof(tL2CAP_CFG_INFO)); + cfg.mtu_present = TRUE; + cfg.mtu = p_tbl->my_mtu; + cfg.flush_to_present = TRUE; + cfg.flush_to = p_tbl->my_flush_to; + L2CA_ConfigReq(lcid, &cfg); + } +} + +/******************************************************************************* +** +** Function avdt_l2c_connect_cfm_cback +** +** Description This is the L2CAP connect confirm callback function. +** +** +** Returns void +** +*******************************************************************************/ +void avdt_l2c_connect_cfm_cback(UINT16 lcid, UINT16 result) +{ + tAVDT_TC_TBL *p_tbl; + tL2CAP_CFG_INFO cfg; + tAVDT_CCB *p_ccb; + + AVDT_TRACE_DEBUG("avdt_l2c_connect_cfm_cback lcid: %d, result: %d\n", + lcid, result); + /* look up info for this channel */ + if ((p_tbl = avdt_ad_tc_tbl_by_lcid(lcid)) != NULL) { + /* if in correct state */ + if (p_tbl->state == AVDT_AD_ST_CONN) { + /* if result successful */ + if (result == L2CAP_CONN_OK) { + if (p_tbl->tcid != AVDT_CHAN_SIG) { + /* set channel state */ + p_tbl->state = AVDT_AD_ST_CFG; + + /* Send L2CAP config req */ + memset(&cfg, 0, sizeof(tL2CAP_CFG_INFO)); + cfg.mtu_present = TRUE; + cfg.mtu = p_tbl->my_mtu; + cfg.flush_to_present = TRUE; + cfg.flush_to = p_tbl->my_flush_to; + L2CA_ConfigReq(lcid, &cfg); + } else { + p_ccb = avdt_ccb_by_idx(p_tbl->ccb_idx); + if (p_ccb == NULL) { + result = L2CAP_CONN_NO_RESOURCES; + } else { + /* set channel state */ + p_tbl->state = AVDT_AD_ST_SEC_INT; + p_tbl->lcid = lcid; + p_tbl->cfg_flags = AVDT_L2C_CFG_CONN_INT; + + /* Check the security */ + btm_sec_mx_access_request (p_ccb->peer_addr, AVDT_PSM, + TRUE, BTM_SEC_PROTO_AVDT, + AVDT_CHAN_SIG, + &avdt_sec_check_complete_orig, NULL); + } + } + } + + /* failure; notify adaption that channel closed */ + if (result != L2CAP_CONN_OK) { + avdt_ad_tc_close_ind(p_tbl, result); + } + } + } +} + +/******************************************************************************* +** +** Function avdt_l2c_config_cfm_cback +** +** Description This is the L2CAP config confirm callback function. +** +** +** Returns void +** +*******************************************************************************/ +void avdt_l2c_config_cfm_cback(UINT16 lcid, tL2CAP_CFG_INFO *p_cfg) +{ + tAVDT_TC_TBL *p_tbl; + + /* look up info for this channel */ + if ((p_tbl = avdt_ad_tc_tbl_by_lcid(lcid)) != NULL) { + p_tbl->lcid = lcid; + + /* if in correct state */ + if (p_tbl->state == AVDT_AD_ST_CFG) { + /* if result successful */ + if (p_cfg->result == L2CAP_CONN_OK) { + /* update cfg_flags */ + p_tbl->cfg_flags |= AVDT_L2C_CFG_CFM_DONE; + + /* if configuration complete */ + if (p_tbl->cfg_flags & AVDT_L2C_CFG_IND_DONE) { + avdt_ad_tc_open_ind(p_tbl); + } + } + /* else failure */ + else { + /* Send L2CAP disconnect req */ + L2CA_DisconnectReq(lcid); + } + } + } +} + +/******************************************************************************* +** +** Function avdt_l2c_config_ind_cback +** +** Description This is the L2CAP config indication callback function. +** +** +** Returns void +** +*******************************************************************************/ +void avdt_l2c_config_ind_cback(UINT16 lcid, tL2CAP_CFG_INFO *p_cfg) +{ + tAVDT_TC_TBL *p_tbl; + + /* look up info for this channel */ + if ((p_tbl = avdt_ad_tc_tbl_by_lcid(lcid)) != NULL) { + /* store the mtu in tbl */ + if (p_cfg->mtu_present) { + p_tbl->peer_mtu = p_cfg->mtu; + } else { + p_tbl->peer_mtu = L2CAP_DEFAULT_MTU; + } + AVDT_TRACE_DEBUG("peer_mtu: %d, lcid: x%x\n", p_tbl->peer_mtu, lcid); + + /* send L2CAP configure response */ + memset(p_cfg, 0, sizeof(tL2CAP_CFG_INFO)); + p_cfg->result = L2CAP_CFG_OK; + L2CA_ConfigRsp(lcid, p_cfg); + + /* if first config ind */ + if ((p_tbl->cfg_flags & AVDT_L2C_CFG_IND_DONE) == 0) { + /* update cfg_flags */ + p_tbl->cfg_flags |= AVDT_L2C_CFG_IND_DONE; + + /* if configuration complete */ + if (p_tbl->cfg_flags & AVDT_L2C_CFG_CFM_DONE) { + avdt_ad_tc_open_ind(p_tbl); + } + } + } +} + +/******************************************************************************* +** +** Function avdt_l2c_disconnect_ind_cback +** +** Description This is the L2CAP disconnect indication callback function. +** +** +** Returns void +** +*******************************************************************************/ +void avdt_l2c_disconnect_ind_cback(UINT16 lcid, BOOLEAN ack_needed) +{ + tAVDT_TC_TBL *p_tbl; + UINT16 disc_rsn = AVDT_DISC_RSN_NORMAL; + tAVDT_CCB *p_ccb; + AVDT_TRACE_DEBUG("avdt_l2c_disconnect_ind_cback lcid: %d, ack_needed: %d\n", + lcid, ack_needed); + /* look up info for this channel */ + if ((p_tbl = avdt_ad_tc_tbl_by_lcid(lcid)) != NULL) { + if (ack_needed) { + /* send L2CAP disconnect response */ + L2CA_DisconnectRsp(lcid); + } else { + if ((p_ccb = avdt_ccb_by_idx(p_tbl->ccb_idx)) != NULL) { + UINT16 rsn = L2CA_GetDisconnectReason(p_ccb->peer_addr, BT_TRANSPORT_BR_EDR); + if (rsn != 0 && rsn != HCI_ERR_PEER_USER) { + disc_rsn = AVDT_DISC_RSN_ABNORMAL; + AVDT_TRACE_EVENT("avdt link disc rsn 0x%x", rsn); + } + } + } + + avdt_ad_tc_close_ind(p_tbl, disc_rsn); + } +} + +/******************************************************************************* +** +** Function avdt_l2c_disconnect_cfm_cback +** +** Description This is the L2CAP disconnect confirm callback function. +** +** +** Returns void +** +*******************************************************************************/ +void avdt_l2c_disconnect_cfm_cback(UINT16 lcid, UINT16 result) +{ + tAVDT_TC_TBL *p_tbl; + + AVDT_TRACE_DEBUG("avdt_l2c_disconnect_cfm_cback lcid: %d, result: %d\n", + lcid, result); + /* look up info for this channel */ + if ((p_tbl = avdt_ad_tc_tbl_by_lcid(lcid)) != NULL) { + avdt_ad_tc_close_ind(p_tbl, result); + } +} + +/******************************************************************************* +** +** Function avdt_l2c_congestion_ind_cback +** +** Description This is the L2CAP congestion indication callback function. +** +** +** Returns void +** +*******************************************************************************/ +void avdt_l2c_congestion_ind_cback(UINT16 lcid, BOOLEAN is_congested) +{ + tAVDT_TC_TBL *p_tbl; + + /* look up info for this channel */ + if ((p_tbl = avdt_ad_tc_tbl_by_lcid(lcid)) != NULL) { + avdt_ad_tc_cong_ind(p_tbl, is_congested); + } +} + +/******************************************************************************* +** +** Function avdt_l2c_data_ind_cback +** +** Description This is the L2CAP data indication callback function. +** +** +** Returns void +** +*******************************************************************************/ +void avdt_l2c_data_ind_cback(UINT16 lcid, BT_HDR *p_buf) +{ + tAVDT_TC_TBL *p_tbl; + + /* look up info for this channel */ + if ((p_tbl = avdt_ad_tc_tbl_by_lcid(lcid)) != NULL) { + avdt_ad_tc_data_ind(p_tbl, p_buf); + } else { /* prevent buffer leak */ + osi_free(p_buf); + } +} + +#endif /* #if (defined(AVDT_INCLUDED) && AVDT_INCLUDED == TRUE) */ diff --git a/lib/bt/host/bluedroid/stack/avdt/avdt_msg.c b/lib/bt/host/bluedroid/stack/avdt/avdt_msg.c new file mode 100644 index 00000000..a049eee9 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/avdt/avdt_msg.c @@ -0,0 +1,1784 @@ +/****************************************************************************** + * + * Copyright (C) 2002-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This module contains functions for parsing and building AVDTP signaling + * messages. It also contains functions called by the SCB or CCB state + * machines for sending command, response, and reject messages. It also + * contains a function that processes incoming messages and dispatches them + * to the appropriate SCB or CCB. + * + ******************************************************************************/ + +#include +#include "stack/bt_types.h" +#include "common/bt_target.h" +#include "common/bt_defs.h" +#include "stack/avdt_api.h" +#include "stack/avdtc_api.h" +#include "avdt_int.h" +#include "stack/btu.h" +#include "osi/allocator.h" + +#if (defined(AVDT_INCLUDED) && AVDT_INCLUDED == TRUE) +/***************************************************************************** +** constants +*****************************************************************************/ + +/* mask of all psc values */ +#define AVDT_MSG_PSC_MASK (AVDT_PSC_TRANS | AVDT_PSC_REPORT | AVDT_PSC_DELAY_RPT | \ + AVDT_PSC_RECOV | AVDT_PSC_HDRCMP | AVDT_PSC_MUX) +#define AVDT_PSC_PROTECT (1<<4) /* Content Protection */ +#define AVDT_PSC_CODEC (1<<7) /* codec */ + + +/***************************************************************************** +** type definitions +*****************************************************************************/ + +/* type for message building functions */ +typedef void (*tAVDT_MSG_BLD)(UINT8 **p, tAVDT_MSG *p_msg); + +/* type for message parsing functions */ +typedef UINT8 (*tAVDT_MSG_PRS)(tAVDT_MSG *p_msg, UINT8 *p, UINT16 len); + + +/***************************************************************************** +** local function declarations +*****************************************************************************/ + +static void avdt_msg_bld_none(UINT8 **p, tAVDT_MSG *p_msg); +static void avdt_msg_bld_single(UINT8 **p, tAVDT_MSG *p_msg); +static void avdt_msg_bld_setconfig_cmd(UINT8 **p, tAVDT_MSG *p_msg); +static void avdt_msg_bld_reconfig_cmd(UINT8 **p, tAVDT_MSG *p_msg); +static void avdt_msg_bld_multi(UINT8 **p, tAVDT_MSG *p_msg); +static void avdt_msg_bld_security_cmd(UINT8 **p, tAVDT_MSG *p_msg); +static void avdt_msg_bld_discover_rsp(UINT8 **p, tAVDT_MSG *p_msg); +static void avdt_msg_bld_svccap(UINT8 **p, tAVDT_MSG *p_msg); +static void avdt_msg_bld_security_rsp(UINT8 **p, tAVDT_MSG *p_msg); +static void avdt_msg_bld_all_svccap(UINT8 **p, tAVDT_MSG *p_msg); +static void avdt_msg_bld_delay_rpt(UINT8 **p, tAVDT_MSG *p_msg); + +static UINT8 avdt_msg_prs_none(tAVDT_MSG *p_msg, UINT8 *p, UINT16 len); +static UINT8 avdt_msg_prs_single(tAVDT_MSG *p_msg, UINT8 *p, UINT16 len); +static UINT8 avdt_msg_prs_setconfig_cmd(tAVDT_MSG *p_msg, UINT8 *p, UINT16 len); +static UINT8 avdt_msg_prs_reconfig_cmd(tAVDT_MSG *p_msg, UINT8 *p, UINT16 len); +static UINT8 avdt_msg_prs_multi(tAVDT_MSG *p_msg, UINT8 *p, UINT16 len); +static UINT8 avdt_msg_prs_security_cmd(tAVDT_MSG *p_msg, UINT8 *p, UINT16 len); +static UINT8 avdt_msg_prs_discover_rsp(tAVDT_MSG *p_msg, UINT8 *p, UINT16 len); +static UINT8 avdt_msg_prs_svccap(tAVDT_MSG *p_msg, UINT8 *p, UINT16 len); +static UINT8 avdt_msg_prs_all_svccap(tAVDT_MSG *p_msg, UINT8 *p, UINT16 len); +static UINT8 avdt_msg_prs_security_rsp(tAVDT_MSG *p_msg, UINT8 *p, UINT16 len); +static UINT8 avdt_msg_prs_delay_rpt (tAVDT_MSG *p_msg, UINT8 *p, UINT16 len); + +/***************************************************************************** +** constants +*****************************************************************************/ + +/* table of information element minimum lengths used for parsing */ +const UINT8 avdt_msg_ie_len_min[] = { + 0, /* unused */ + AVDT_LEN_TRANS_MIN, /* media transport */ + AVDT_LEN_REPORT_MIN, /* reporting */ + AVDT_LEN_RECOV_MIN, /* recovery */ + AVDT_LEN_PROTECT_MIN, /* content protection */ + AVDT_LEN_HDRCMP_MIN, /* header compression */ + AVDT_LEN_MUX_MIN, /* multiplexing */ + AVDT_LEN_CODEC_MIN, /* codec */ + AVDT_LEN_DELAY_RPT_MIN /* delay report */ +}; + +/* table of information element minimum lengths used for parsing */ +const UINT8 avdt_msg_ie_len_max[] = { + 0, /* unused */ + AVDT_LEN_TRANS_MAX, /* media transport */ + AVDT_LEN_REPORT_MAX, /* reporting */ + AVDT_LEN_RECOV_MAX, /* recovery */ + AVDT_LEN_PROTECT_MAX, /* content protection */ + AVDT_LEN_HDRCMP_MAX, /* header compression */ + AVDT_LEN_MUX_MAX, /* multiplexing */ + AVDT_LEN_CODEC_MAX, /* codec */ + AVDT_LEN_DELAY_RPT_MAX /* delay report */ +}; + +/* table of error codes used when decoding information elements */ +const UINT8 avdt_msg_ie_err[] = { + 0, /* unused */ + AVDT_ERR_MEDIA_TRANS, /* media transport */ + AVDT_ERR_LENGTH, /* reporting */ + AVDT_ERR_RECOV_FMT, /* recovery */ + AVDT_ERR_CP_FMT, /* content protection */ + AVDT_ERR_ROHC_FMT, /* header compression */ + AVDT_ERR_MUX_FMT, /* multiplexing */ + AVDT_ERR_SERVICE, /* codec */ + AVDT_ERR_SERVICE /* delay report ?? */ +}; + +/* table of packet type minimum lengths */ +static const UINT8 avdt_msg_pkt_type_len[] = { + AVDT_LEN_TYPE_SINGLE, + AVDT_LEN_TYPE_START, + AVDT_LEN_TYPE_CONT, + AVDT_LEN_TYPE_END +}; + +/* function table for building command messages */ +const tAVDT_MSG_BLD avdt_msg_bld_cmd[] = { + avdt_msg_bld_none, /* discover */ + avdt_msg_bld_single, /* get capabilities */ + avdt_msg_bld_setconfig_cmd, /* set configuration */ + avdt_msg_bld_single, /* get configuration */ + avdt_msg_bld_reconfig_cmd, /* reconfigure */ + avdt_msg_bld_single, /* open */ + avdt_msg_bld_multi, /* start */ + avdt_msg_bld_single, /* close */ + avdt_msg_bld_multi, /* suspend */ + avdt_msg_bld_single, /* abort */ + avdt_msg_bld_security_cmd, /* security control */ + avdt_msg_bld_single, /* get all capabilities */ + avdt_msg_bld_delay_rpt /* delay report */ +}; + +/* function table for building response messages */ +const tAVDT_MSG_BLD avdt_msg_bld_rsp[] = { + avdt_msg_bld_discover_rsp, /* discover */ + avdt_msg_bld_svccap, /* get capabilities */ + avdt_msg_bld_none, /* set configuration */ + avdt_msg_bld_all_svccap, /* get configuration */ + avdt_msg_bld_none, /* reconfigure */ + avdt_msg_bld_none, /* open */ + avdt_msg_bld_none, /* start */ + avdt_msg_bld_none, /* close */ + avdt_msg_bld_none, /* suspend */ + avdt_msg_bld_none, /* abort */ + avdt_msg_bld_security_rsp, /* security control */ + avdt_msg_bld_all_svccap, /* get all capabilities */ + avdt_msg_bld_none /* delay report */ +}; + +/* function table for parsing command messages */ +const tAVDT_MSG_PRS avdt_msg_prs_cmd[] = { + avdt_msg_prs_none, /* discover */ + avdt_msg_prs_single, /* get capabilities */ + avdt_msg_prs_setconfig_cmd, /* set configuration */ + avdt_msg_prs_single, /* get configuration */ + avdt_msg_prs_reconfig_cmd, /* reconfigure */ + avdt_msg_prs_single, /* open */ + avdt_msg_prs_multi, /* start */ + avdt_msg_prs_single, /* close */ + avdt_msg_prs_multi, /* suspend */ + avdt_msg_prs_single, /* abort */ + avdt_msg_prs_security_cmd, /* security control */ + avdt_msg_prs_single, /* get all capabilities */ + avdt_msg_prs_delay_rpt /* delay report */ +}; + +/* function table for parsing response messages */ +const tAVDT_MSG_PRS avdt_msg_prs_rsp[] = { + avdt_msg_prs_discover_rsp, /* discover */ + avdt_msg_prs_svccap, /* get capabilities */ + avdt_msg_prs_none, /* set configuration */ + avdt_msg_prs_all_svccap, /* get configuration */ + avdt_msg_prs_none, /* reconfigure */ + avdt_msg_prs_none, /* open */ + avdt_msg_prs_none, /* start */ + avdt_msg_prs_none, /* close */ + avdt_msg_prs_none, /* suspend */ + avdt_msg_prs_none, /* abort */ + avdt_msg_prs_security_rsp, /* security control */ + avdt_msg_prs_all_svccap, /* get all capabilities */ + avdt_msg_prs_none /* delay report */ +}; + +/* command message-to-event lookup table */ +const UINT8 avdt_msg_cmd_2_evt[] = { + AVDT_CCB_MSG_DISCOVER_CMD_EVT + AVDT_CCB_MKR, /* discover */ + AVDT_CCB_MSG_GETCAP_CMD_EVT + AVDT_CCB_MKR, /* get capabilities */ + AVDT_SCB_MSG_SETCONFIG_CMD_EVT, /* set configuration */ + AVDT_SCB_MSG_GETCONFIG_CMD_EVT, /* get configuration */ + AVDT_SCB_MSG_RECONFIG_CMD_EVT, /* reconfigure */ + AVDT_SCB_MSG_OPEN_CMD_EVT, /* open */ + AVDT_CCB_MSG_START_CMD_EVT + AVDT_CCB_MKR, /* start */ + AVDT_SCB_MSG_CLOSE_CMD_EVT, /* close */ + AVDT_CCB_MSG_SUSPEND_CMD_EVT + AVDT_CCB_MKR, /* suspend */ + AVDT_SCB_MSG_ABORT_CMD_EVT, /* abort */ + AVDT_SCB_MSG_SECURITY_CMD_EVT, /* security control */ + AVDT_CCB_MSG_GETCAP_CMD_EVT + AVDT_CCB_MKR, /* get all capabilities */ + AVDT_SCB_MSG_DELAY_RPT_CMD_EVT /* delay report */ +}; + +/* response message-to-event lookup table */ +const UINT8 avdt_msg_rsp_2_evt[] = { + AVDT_CCB_MSG_DISCOVER_RSP_EVT + AVDT_CCB_MKR, /* discover */ + AVDT_CCB_MSG_GETCAP_RSP_EVT + AVDT_CCB_MKR, /* get capabilities */ + AVDT_SCB_MSG_SETCONFIG_RSP_EVT, /* set configuration */ + AVDT_SCB_MSG_GETCONFIG_RSP_EVT, /* get configuration */ + AVDT_SCB_MSG_RECONFIG_RSP_EVT, /* reconfigure */ + AVDT_SCB_MSG_OPEN_RSP_EVT, /* open */ + AVDT_CCB_MSG_START_RSP_EVT + AVDT_CCB_MKR, /* start */ + AVDT_SCB_MSG_CLOSE_RSP_EVT, /* close */ + AVDT_CCB_MSG_SUSPEND_RSP_EVT + AVDT_CCB_MKR, /* suspend */ + AVDT_SCB_MSG_ABORT_RSP_EVT, /* abort */ + AVDT_SCB_MSG_SECURITY_RSP_EVT, /* security control */ + AVDT_CCB_MSG_GETCAP_RSP_EVT + AVDT_CCB_MKR, /* get all capabilities */ + AVDT_SCB_MSG_DELAY_RPT_RSP_EVT /* delay report */ +}; + +/* reject message-to-event lookup table */ +const UINT8 avdt_msg_rej_2_evt[] = { + AVDT_CCB_MSG_DISCOVER_RSP_EVT + AVDT_CCB_MKR, /* discover */ + AVDT_CCB_MSG_GETCAP_RSP_EVT + AVDT_CCB_MKR, /* get capabilities */ + AVDT_SCB_MSG_SETCONFIG_REJ_EVT, /* set configuration */ + AVDT_SCB_MSG_GETCONFIG_RSP_EVT, /* get configuration */ + AVDT_SCB_MSG_RECONFIG_RSP_EVT, /* reconfigure */ + AVDT_SCB_MSG_OPEN_REJ_EVT, /* open */ + AVDT_CCB_MSG_START_RSP_EVT + AVDT_CCB_MKR, /* start */ + AVDT_SCB_MSG_CLOSE_RSP_EVT, /* close */ + AVDT_CCB_MSG_SUSPEND_RSP_EVT + AVDT_CCB_MKR, /* suspend */ + AVDT_SCB_MSG_ABORT_RSP_EVT, /* abort */ + AVDT_SCB_MSG_SECURITY_RSP_EVT, /* security control */ + AVDT_CCB_MSG_GETCAP_RSP_EVT + AVDT_CCB_MKR, /* get all capabilities */ + 0 /* delay report */ +}; + +/******************************************************************************* +** +** Function avdt_msg_bld_cfg +** +** Description This function builds the configuration parameters contained +** in a command or response message. +** +** +** Returns void. +** +*******************************************************************************/ +static void avdt_msg_bld_cfg(UINT8 **p, tAVDT_CFG *p_cfg) +{ + UINT8 len; + + /* for now, just build media transport, codec, and content protection, and multiplexing */ + + /* media transport */ + if (p_cfg->psc_mask & AVDT_PSC_TRANS) { + *(*p)++ = AVDT_CAT_TRANS; + *(*p)++ = 0; /* length */ + } + +#if AVDT_REPORTING == TRUE + /* reporting transport */ + if (p_cfg->psc_mask & AVDT_PSC_REPORT) { + *(*p)++ = AVDT_CAT_REPORT; + *(*p)++ = 0; /* length */ + } +#endif + + /* codec */ + if (p_cfg->num_codec != 0) { + *(*p)++ = AVDT_CAT_CODEC; + len = p_cfg->codec_info[0] + 1; + if ( len > AVDT_CODEC_SIZE ) { + len = AVDT_CODEC_SIZE; + } + + memcpy(*p, p_cfg->codec_info, len); + *p += len; + } + + /* content protection */ + if (p_cfg->num_protect != 0) { + *(*p)++ = AVDT_CAT_PROTECT; + len = p_cfg->protect_info[0] + 1; + if ( len > AVDT_PROTECT_SIZE ) { + len = AVDT_PROTECT_SIZE; + } + + memcpy(*p, p_cfg->protect_info, len); + *p += len; + } + +#if AVDT_MULTIPLEXING == TRUE + /* multiplexing */ + if (p_cfg->psc_mask & AVDT_PSC_MUX) { + *(*p)++ = AVDT_CAT_MUX; + /* length */ + if (p_cfg->psc_mask & AVDT_PSC_RECOV) { + *(*p)++ = 7; /* frag (1) + media + report + recovery */ + } else if (p_cfg->psc_mask & AVDT_PSC_REPORT) { + *(*p)++ = 5; /* frag (1) + media + report */ + } else { + *(*p)++ = 3; /* frag (1) + media */ + } + + /* allow fragmentation */ + if (p_cfg->mux_mask & AVDT_MUX_FRAG) { + *(*p)++ = 0x80; + } else { + *(*p)++ = 0; + } + + /* media transport session */ + *(*p)++ = p_cfg->mux_tsid_media << 3; /* TSID */ + *(*p)++ = p_cfg->mux_tcid_media << 3; /* TCID */ + + if (p_cfg->psc_mask & AVDT_PSC_RECOV) { + /* reporting transport session */ + *(*p)++ = p_cfg->mux_tsid_report << 3; /* TSID */ + *(*p)++ = p_cfg->mux_tcid_report << 3; /* TCID */ + /* recovery transport session */ + *(*p)++ = p_cfg->mux_tsid_recov << 3; /* TSID */ + *(*p)++ = p_cfg->mux_tcid_recov << 3; /* TCID */ + } else if (p_cfg->psc_mask & AVDT_PSC_REPORT) { + /* reporting transport session */ + *(*p)++ = p_cfg->mux_tsid_report << 3; /* TSID */ + *(*p)++ = p_cfg->mux_tcid_report << 3; /* TCID */ + } + } +#endif + + /* delay report */ + if (p_cfg->psc_mask & AVDT_PSC_DELAY_RPT) { + *(*p)++ = AVDT_CAT_DELAY_RPT; + *(*p)++ = 0; /* length */ + } +} + +/******************************************************************************* +** +** Function avdt_msg_bld_none +** +** Description This message building function builds an empty message. +** +** +** Returns void. +** +*******************************************************************************/ +static void avdt_msg_bld_none(UINT8 **p, tAVDT_MSG *p_msg) +{ + UNUSED(p); + UNUSED(p_msg); + return; +} + +/******************************************************************************* +** +** Function avdt_msg_bld_single +** +** Description This message building function builds a message containing +** a single SEID. +** +** +** Returns void. +** +*******************************************************************************/ +static void avdt_msg_bld_single(UINT8 **p, tAVDT_MSG *p_msg) +{ + AVDT_MSG_BLD_SEID(*p, p_msg->single.seid); +} + +/******************************************************************************* +** +** Function avdt_msg_bld_setconfig_cmd +** +** Description This message building function builds a set configuration +** command message. +** +** +** Returns void. +** +*******************************************************************************/ +static void avdt_msg_bld_setconfig_cmd(UINT8 **p, tAVDT_MSG *p_msg) +{ + AVDT_MSG_BLD_SEID(*p, p_msg->config_cmd.hdr.seid); + AVDT_MSG_BLD_SEID(*p, p_msg->config_cmd.int_seid); + avdt_msg_bld_cfg(p, p_msg->config_cmd.p_cfg); +} + +/******************************************************************************* +** +** Function avdt_msg_bld_reconfig_cmd +** +** Description This message building function builds a reconfiguration +** command message. +** +** +** Returns void. +** +*******************************************************************************/ +static void avdt_msg_bld_reconfig_cmd(UINT8 **p, tAVDT_MSG *p_msg) +{ + AVDT_MSG_BLD_SEID(*p, p_msg->reconfig_cmd.hdr.seid); + + /* force psc mask zero to build only codec and security */ + p_msg->reconfig_cmd.p_cfg->psc_mask = 0; + avdt_msg_bld_cfg(p, p_msg->reconfig_cmd.p_cfg); +} + +/******************************************************************************* +** +** Function avdt_msg_bld_multi +** +** Description This message building function builds a message containing +** multiple SEID's. +** +** +** Returns void. +** +*******************************************************************************/ +static void avdt_msg_bld_multi(UINT8 **p, tAVDT_MSG *p_msg) +{ + int i; + + for (i = 0; i < p_msg->multi.num_seps; i++) { + AVDT_MSG_BLD_SEID(*p, p_msg->multi.seid_list[i]); + } +} + +/******************************************************************************* +** +** Function avdt_msg_bld_security_cmd +** +** Description This message building function builds a security +** command message. +** +** Returns void. +** +*******************************************************************************/ +static void avdt_msg_bld_security_cmd(UINT8 **p, tAVDT_MSG *p_msg) +{ + AVDT_MSG_BLD_SEID(*p, p_msg->security_cmd.hdr.seid); + memcpy(*p, p_msg->security_cmd.p_data, p_msg->security_cmd.len); + *p += p_msg->security_cmd.len; +} + +/******************************************************************************* +** +** Function avdt_msg_bld_delay_rpt +** +** Description This message building function builds a delay report +** command message. +** +** Returns void. +** +*******************************************************************************/ +static void avdt_msg_bld_delay_rpt(UINT8 **p, tAVDT_MSG *p_msg) +{ + AVDT_MSG_BLD_SEID(*p, p_msg->delay_rpt_cmd.hdr.seid); + UINT16_TO_BE_STREAM(*p, p_msg->delay_rpt_cmd.delay); +} + +/******************************************************************************* +** +** Function avdt_msg_bld_discover_rsp +** +** Description This message building function builds a discover +** response message. +** +** +** Returns void. +** +*******************************************************************************/ +static void avdt_msg_bld_discover_rsp(UINT8 **p, tAVDT_MSG *p_msg) +{ + int i; + + for (i = 0; i < p_msg->discover_rsp.num_seps; i++) { + /* build discover rsp info */ + AVDT_MSG_BLD_DISC(*p, p_msg->discover_rsp.p_sep_info[i].seid, + p_msg->discover_rsp.p_sep_info[i].in_use, + p_msg->discover_rsp.p_sep_info[i].media_type, + p_msg->discover_rsp.p_sep_info[i].tsep); + } +} + +/******************************************************************************* +** +** Function avdt_msg_bld_svccap +** +** Description This message building function builds a message containing +** service capabilities parameters. +** +** +** Returns void. +** +*******************************************************************************/ +static void avdt_msg_bld_svccap(UINT8 **p, tAVDT_MSG *p_msg) +{ + tAVDT_CFG cfg; + + /* make sure the delay report category is not reported */ + memcpy (&cfg, p_msg->svccap.p_cfg, sizeof(tAVDT_CFG)); + cfg.psc_mask &= ~AVDT_PSC_DELAY_RPT; + avdt_msg_bld_cfg(p, &cfg); +} + +/******************************************************************************* +** +** Function avdt_msg_bld_all_svccap +** +** Description This message building function builds a message containing +** service capabilities parameters. +** +** +** Returns void. +** +*******************************************************************************/ +static void avdt_msg_bld_all_svccap(UINT8 **p, tAVDT_MSG *p_msg) +{ + avdt_msg_bld_cfg(p, p_msg->svccap.p_cfg); +} + +/******************************************************************************* +** +** Function avdt_msg_bld_security_rsp +** +** Description This message building function builds a security +** response message. +** +** +** Returns void. +** +*******************************************************************************/ +static void avdt_msg_bld_security_rsp(UINT8 **p, tAVDT_MSG *p_msg) +{ + memcpy(*p, p_msg->security_rsp.p_data, p_msg->security_rsp.len); + *p += p_msg->security_rsp.len; +} + +/******************************************************************************* +** +** Function avdt_msg_prs_cfg +** +** Description This message parsing function parses the configuration +** parameters field of a message. +** +** +** Returns Error code or zero if no error, and element that failed +** in p_elem. +** +*******************************************************************************/ +static UINT8 avdt_msg_prs_cfg(tAVDT_CFG *p_cfg, UINT8 *p, UINT16 len, UINT8 *p_elem, UINT8 sig_id) +{ + UINT8 *p_end; + UINT8 elem = 0; + UINT8 elem_len; + UINT8 tmp; + UINT8 err = 0; + UINT8 protect_offset = 0; + + if (!p_cfg) { + AVDT_TRACE_ERROR ("not expecting this cfg"); + return AVDT_ERR_BAD_STATE; + } + + p_cfg->psc_mask = 0; + p_cfg->num_codec = 0; + p_cfg->num_protect = 0; +#if AVDT_MULTIPLEXING == TRUE + p_cfg->mux_mask = 0; +#endif + + /* while there is still data to parse */ + p_end = p + len; + while ((p < p_end) && (err == 0)) { + /* verify overall length */ + if ((p_end - p) < AVDT_LEN_CFG_MIN) { + err = AVDT_ERR_PAYLOAD; + break; + } + + /* get and verify info elem id, length */ + elem = *p++; + elem_len = *p++; + + if ((elem == 0) || (elem > AVDT_CAT_MAX_CUR)) { + /* this may not be really bad. + * It may be a service category that is too new for us. + * allow these to be parsed without reporting an error. + * If this is a "capability" (as in GetCapRsp & GetConfigRsp), this is filtered out. + * If this is a Configuration (as in SetConfigCmd & ReconfigCmd), + * this will be marked as an error in the caller of this function */ + if ((sig_id == AVDT_SIG_SETCONFIG) || (sig_id == AVDT_SIG_RECONFIG)) { + /* Cannot accept unknown category. */ + err = AVDT_ERR_CATEGORY; + break; + } else { /* GETCAP or GET_ALLCAP */ + /* Skip unknown categories. */ + p += elem_len; + AVDT_TRACE_DEBUG("skipping unknown service category=%d len: %d\n", elem, elem_len); + continue; + } + } + + if ((elem_len > avdt_msg_ie_len_max[elem]) || + (elem_len < avdt_msg_ie_len_min[elem])) { + err = avdt_msg_ie_err[elem]; + break; + } + + /* add element to psc mask, but mask out codec or protect */ + p_cfg->psc_mask |= (1 << elem); + AVDT_TRACE_DEBUG("elem=%d elem_len: %d psc_mask=0x%x\n", elem, elem_len, p_cfg->psc_mask); + + /* parse individual information elements with additional parameters */ + switch (elem) { + case AVDT_CAT_RECOV: + p_cfg->recov_type = *p++; + p_cfg->recov_mrws = *p++; + p_cfg->recov_mnmp = *p++; + if (p_cfg->recov_type != AVDT_RECOV_RFC2733) { + err = AVDT_ERR_RECOV_TYPE; + } else if ((p_cfg->recov_mrws < AVDT_RECOV_MRWS_MIN) || + (p_cfg->recov_mrws > AVDT_RECOV_MRWS_MAX) || + (p_cfg->recov_mnmp < AVDT_RECOV_MNMP_MIN) || + (p_cfg->recov_mnmp > AVDT_RECOV_MNMP_MAX)) { + err = AVDT_ERR_RECOV_FMT; + } + break; + + case AVDT_CAT_PROTECT: + p_cfg->psc_mask &= ~AVDT_PSC_PROTECT; + if ((elem_len + protect_offset) < AVDT_PROTECT_SIZE) { + p_cfg->num_protect++; + p_cfg->protect_info[protect_offset] = elem_len; + protect_offset++; + memcpy(&p_cfg->protect_info[protect_offset], p, elem_len); + protect_offset += elem_len; + } + p += elem_len; + break; + + case AVDT_CAT_HDRCMP: + p_cfg->hdrcmp_mask = *p++; + break; + +#if AVDT_MULTIPLEXING == TRUE + case AVDT_CAT_MUX: + /* verify length */ + AVDT_TRACE_WARNING("psc_mask=0x%x elem_len=%d\n", p_cfg->psc_mask, elem_len); + if ( ((0 == (p_cfg->psc_mask & (AVDT_PSC_RECOV | AVDT_PSC_REPORT))) && (elem_len != 3)) + || (((p_cfg->psc_mask & AVDT_PSC_REPORT) && !(p_cfg->psc_mask & AVDT_PSC_RECOV)) + && (elem_len != 5)) + || ((!(p_cfg->psc_mask & AVDT_PSC_REPORT) && (p_cfg->psc_mask & AVDT_PSC_RECOV)) + && (elem_len != 5)) + || (((p_cfg->psc_mask & AVDT_PSC_REPORT) && (p_cfg->psc_mask & AVDT_PSC_RECOV)) + && (elem_len != 7)) ) { + err = AVDT_ERR_MUX_FMT; + break; + } + + /* parse fragmentation */ + p_cfg->mux_mask = *p++ & (UINT8)AVDT_MUX_FRAG; + + /* parse TSIDs and TCIDs */ + if (--elem_len) { + p_cfg->mux_tsid_media = (*p++) >> 3; + } else { + break; + } + + if (--elem_len) { + p_cfg->mux_tcid_media = (*p++) >> 3; + } else { + break; + } + + if (--elem_len) { + p_cfg->mux_tsid_report = (*p++) >> 3; + } else { + break; + } + + if (--elem_len) { + p_cfg->mux_tcid_report = (*p++) >> 3; + } else { + break; + } + + if (--elem_len) { + p_cfg->mux_tsid_recov = (*p++) >> 3; + } else { + break; + } + + if (--elem_len) { + p_cfg->mux_tcid_recov = (*p++) >> 3; + } else { + break; + } + break; +#endif + + case AVDT_CAT_CODEC: + p_cfg->psc_mask &= ~AVDT_PSC_CODEC; + tmp = elem_len; + if (elem_len >= AVDT_CODEC_SIZE) { + tmp = AVDT_CODEC_SIZE - 1; + } + p_cfg->num_codec++; + p_cfg->codec_info[0] = elem_len; + memcpy(&p_cfg->codec_info[1], p, tmp); + p += elem_len; + break; + + case AVDT_CAT_DELAY_RPT: + break; + + default: + p += elem_len; + break; + } /* switch */ + } /* while ! err, !end*/ + *p_elem = elem; + AVDT_TRACE_DEBUG("err=0x%x, elem:0x%x psc_mask=0x%x\n", err, elem, p_cfg->psc_mask); + + return err; +} + +/******************************************************************************* +** +** Function avdt_msg_prs_none +** +** Description This message parsing function parses a message with no parameters. + +** +** +** Returns Error code or zero if no error. +** +*******************************************************************************/ +static UINT8 avdt_msg_prs_none(tAVDT_MSG *p_msg, UINT8 *p, UINT16 len) +{ + UNUSED(p_msg); + UNUSED(p); + UNUSED(len); + return 0; +} + +/******************************************************************************* +** +** Function avdt_msg_prs_single +** +** Description This message parsing function parses a message with a +** single SEID. +** +** +** Returns Error code or zero if no error. +** +*******************************************************************************/ +static UINT8 avdt_msg_prs_single(tAVDT_MSG *p_msg, UINT8 *p, UINT16 len) +{ + UINT8 err = 0; + + /* verify len */ + if (len != AVDT_LEN_SINGLE) { + err = AVDT_ERR_LENGTH; + } else { + AVDT_MSG_PRS_SEID(p, p_msg->single.seid); + + if (avdt_scb_by_hdl(p_msg->single.seid) == NULL) { + err = AVDT_ERR_SEID; + } + } + return err; +} + +/******************************************************************************* +** +** Function avdt_msg_prs_setconfig_cmd +** +** Description This message parsing function parses a set configuration +** command message. +** +** +** Returns Error code or zero if no error. +** +*******************************************************************************/ +static UINT8 avdt_msg_prs_setconfig_cmd(tAVDT_MSG *p_msg, UINT8 *p, UINT16 len) +{ + UINT8 err = 0; + + p_msg->hdr.err_param = 0; + + /* verify len */ + if (len < AVDT_LEN_SETCONFIG_MIN) { + err = AVDT_ERR_LENGTH; + } else { + /* get seids */ + AVDT_MSG_PRS_SEID(p, p_msg->config_cmd.hdr.seid); + if (avdt_scb_by_hdl(p_msg->config_cmd.hdr.seid) == NULL) { + err = AVDT_ERR_SEID; + } + + AVDT_MSG_PRS_SEID(p, p_msg->config_cmd.int_seid); + if ((p_msg->config_cmd.int_seid < AVDT_SEID_MIN) || + (p_msg->config_cmd.int_seid > AVDT_SEID_MAX)) { + err = AVDT_ERR_SEID; + } + } + + if (!err) { + /* parse configuration parameters */ + len -= 2; + err = avdt_msg_prs_cfg(p_msg->config_cmd.p_cfg, p, len, &p_msg->hdr.err_param, AVDT_SIG_SETCONFIG); + + if (!err) { + /* verify protocol service capabilities are supported */ + if (((p_msg->config_cmd.p_cfg->psc_mask & (~AVDT_PSC)) != 0) || + (p_msg->config_cmd.p_cfg->num_codec == 0)) { + err = AVDT_ERR_INVALID_CAP; + } + } + } + + return err; +} + +/******************************************************************************* +** +** Function avdt_msg_prs_reconfig_cmd +** +** Description This message parsing function parses a reconfiguration +** command message. +** +** +** Returns Error code or zero if no error. +** +*******************************************************************************/ +static UINT8 avdt_msg_prs_reconfig_cmd(tAVDT_MSG *p_msg, UINT8 *p, UINT16 len) +{ + UINT8 err = 0; + + p_msg->hdr.err_param = 0; + + /* verify len */ + if (len < AVDT_LEN_RECONFIG_MIN) { + err = AVDT_ERR_LENGTH; + } else { + /* get seid */ + AVDT_MSG_PRS_SEID(p, p_msg->reconfig_cmd.hdr.seid); + if (avdt_scb_by_hdl(p_msg->reconfig_cmd.hdr.seid) == NULL) { + err = AVDT_ERR_SEID; + } else { + /* parse config parameters */ + len--; + err = avdt_msg_prs_cfg(p_msg->config_cmd.p_cfg, p, len, &p_msg->hdr.err_param, AVDT_SIG_RECONFIG); + + /* verify no protocol service capabilities in parameters */ + if (!err) { + AVDT_TRACE_DEBUG("avdt_msg_prs_reconfig_cmd psc_mask=0x%x/0x%x\n", p_msg->config_cmd.p_cfg->psc_mask, AVDT_MSG_PSC_MASK); + if ((p_msg->config_cmd.p_cfg->psc_mask != 0) || + (p_msg->config_cmd.p_cfg->num_codec == 0 && p_msg->config_cmd.p_cfg->num_protect == 0)) { + err = AVDT_ERR_INVALID_CAP; + } + } + } + } + return err; +} + +/******************************************************************************* +** +** Function avdt_msg_prs_multi +** +** Description This message parsing function parses a message containing +** multiple SEID's. +** +** +** Returns Error code or zero if no error. +** +*******************************************************************************/ +static UINT8 avdt_msg_prs_multi(tAVDT_MSG *p_msg, UINT8 *p, UINT16 len) +{ + int i; + UINT8 err = 0; + + p_msg->hdr.err_param = 0; + + /* verify len */ + if (len < AVDT_LEN_MULTI_MIN || (len > AVDT_NUM_SEPS)) { + err = AVDT_ERR_LENGTH; + } else { + /* get and verify all seps */ + for (i = 0; i < len; i++) { + AVDT_MSG_PRS_SEID(p, p_msg->multi.seid_list[i]); + if (avdt_scb_by_hdl(p_msg->multi.seid_list[i]) == NULL) { + err = AVDT_ERR_SEID; + p_msg->hdr.err_param = p_msg->multi.seid_list[i]; + break; + } + } + p_msg->multi.num_seps = (UINT8)i; + } + + return err; +} + +/******************************************************************************* +** +** Function avdt_msg_prs_security_cmd +** +** Description This message parsing function parses a security +** command message. +** +** +** Returns Error code or zero if no error. +** +*******************************************************************************/ +static UINT8 avdt_msg_prs_security_cmd(tAVDT_MSG *p_msg, UINT8 *p, UINT16 len) +{ + UINT8 err = 0; + + /* verify len */ + if (len < AVDT_LEN_SECURITY_MIN) { + err = AVDT_ERR_LENGTH; + } else { + /* get seid */ + AVDT_MSG_PRS_SEID(p, p_msg->security_cmd.hdr.seid); + if (avdt_scb_by_hdl(p_msg->security_cmd.hdr.seid) == NULL) { + err = AVDT_ERR_SEID; + } else { + p_msg->security_cmd.p_data = p; + p_msg->security_cmd.len = len - 1; + } + } + return err; +} + +/******************************************************************************* +** +** Function avdt_msg_prs_discover_rsp +** +** Description This message parsing function parses a discover +** response message. +** +** +** Returns Error code or zero if no error. +** +*******************************************************************************/ +static UINT8 avdt_msg_prs_discover_rsp(tAVDT_MSG *p_msg, UINT8 *p, UINT16 len) +{ + int i; + UINT8 err = 0; + + /* determine number of seps; seps in msg is len/2, but set to minimum + ** of seps app has supplied memory for and seps in msg + */ + if (p_msg->discover_rsp.num_seps > (len / 2)) { + p_msg->discover_rsp.num_seps = (len / 2); + } + + /* parse out sep info */ + for (i = 0; i < p_msg->discover_rsp.num_seps; i++) { + /* parse discover rsp info */ + AVDT_MSG_PRS_DISC(p, p_msg->discover_rsp.p_sep_info[i].seid, + p_msg->discover_rsp.p_sep_info[i].in_use, + p_msg->discover_rsp.p_sep_info[i].media_type, + p_msg->discover_rsp.p_sep_info[i].tsep); + + /* verify that seid is valid */ + if ((p_msg->discover_rsp.p_sep_info[i].seid < AVDT_SEID_MIN) || + (p_msg->discover_rsp.p_sep_info[i].seid > AVDT_SEID_MAX)) { + err = AVDT_ERR_SEID; + break; + } + } + + return err; +} + +/******************************************************************************* +** +** Function avdt_msg_prs_svccap +** +** Description This message parsing function parses a message containing +** service capabilities parameters. +** +** +** Returns Error code or zero if no error. +** +*******************************************************************************/ +static UINT8 avdt_msg_prs_svccap(tAVDT_MSG *p_msg, UINT8 *p, UINT16 len) +{ + /* parse parameters */ + UINT8 err = avdt_msg_prs_cfg(p_msg->svccap.p_cfg, p, len, &p_msg->hdr.err_param, AVDT_SIG_GETCAP); + if (p_msg->svccap.p_cfg) { + p_msg->svccap.p_cfg->psc_mask &= AVDT_LEG_PSC; + } + + return (err); +} + +/******************************************************************************* +** +** Function avdt_msg_prs_all_svccap +** +** Description This message parsing function parses a message containing +** service capabilities parameters. +** +** +** Returns Error code or zero if no error. +** +*******************************************************************************/ +static UINT8 avdt_msg_prs_all_svccap(tAVDT_MSG *p_msg, UINT8 *p, UINT16 len) +{ + UINT8 err = avdt_msg_prs_cfg(p_msg->svccap.p_cfg, p, len, &p_msg->hdr.err_param, AVDT_SIG_GET_ALLCAP); + if (p_msg->svccap.p_cfg) { + p_msg->svccap.p_cfg->psc_mask &= AVDT_MSG_PSC_MASK; + } + return (err); +} + +/******************************************************************************* +** +** Function avdt_msg_prs_security_rsp +** +** Description This message parsing function parsing a security +** response message. +** +** +** Returns Error code or zero if no error. +** +*******************************************************************************/ +static UINT8 avdt_msg_prs_security_rsp(tAVDT_MSG *p_msg, UINT8 *p, UINT16 len) +{ + p_msg->security_rsp.p_data = p; + p_msg->security_rsp.len = len; + + return 0; +} + +/******************************************************************************* +** +** Function avdt_msg_prs_rej +** +** Description +** +** +** Returns Error code or zero if no error. +** +*******************************************************************************/ +static UINT8 avdt_msg_prs_rej(tAVDT_MSG *p_msg, UINT8 *p, UINT8 sig) +{ + if ((sig == AVDT_SIG_SETCONFIG) || (sig == AVDT_SIG_RECONFIG)) { + p_msg->hdr.err_param = *p++; + p_msg->hdr.err_code = *p; + } else if ((sig == AVDT_SIG_START) || (sig == AVDT_SIG_SUSPEND)) { + AVDT_MSG_PRS_SEID(p, p_msg->hdr.err_param); + p_msg->hdr.err_code = *p; + } else { + p_msg->hdr.err_code = *p; + } + + return 0; +} + +/******************************************************************************* +** +** Function avdt_msg_prs_delay_rpt +** +** Description This message parsing function parses a security +** command message. +** +** +** Returns Error code or zero if no error. +** +*******************************************************************************/ +static UINT8 avdt_msg_prs_delay_rpt (tAVDT_MSG *p_msg, UINT8 *p, UINT16 len) +{ + UINT8 err = 0; + + /* verify len */ + if (len != AVDT_LEN_DELAY_RPT) { + AVDT_TRACE_WARNING("avdt_msg_prs_delay_rpt expected len: %u got: %u\n", AVDT_LEN_DELAY_RPT, len); + err = AVDT_ERR_LENGTH; + } else { + /* get seid */ + AVDT_MSG_PRS_SEID (p, p_msg->delay_rpt_cmd.hdr.seid); + + if (avdt_scb_by_hdl(p_msg->delay_rpt_cmd.hdr.seid) == NULL) { + err = AVDT_ERR_SEID; + } else { + BE_STREAM_TO_UINT16 (p_msg->delay_rpt_cmd.delay, p); + AVDT_TRACE_DEBUG("avdt_msg_prs_delay_rpt delay: %u\n", p_msg->delay_rpt_cmd.delay); + } + } + return err; +} + + +/******************************************************************************* +** +** Function avdt_msg_send +** +** Description Send, and if necessary fragment the next message. +** +** +** Returns Congested state; TRUE if CCB congested, FALSE if not. +** +*******************************************************************************/ +BOOLEAN avdt_msg_send(tAVDT_CCB *p_ccb, BT_HDR *p_msg) +{ + UINT16 curr_msg_len; + UINT8 pkt_type; + UINT8 hdr_len; + tAVDT_TC_TBL *p_tbl; + BT_HDR *p_buf; + UINT8 *p; + UINT8 label; + UINT8 msg; + UINT8 sig; + UINT8 nosp = 0; /* number of subsequent packets */ + + /* look up transport channel table entry to get peer mtu */ + p_tbl = avdt_ad_tc_tbl_by_type(AVDT_CHAN_SIG, p_ccb, NULL); + + /* set the current message if there is a message passed in */ + if (p_msg != NULL) { + p_ccb->p_curr_msg = p_msg; + } + + /* store copy of curr_msg->len */ + curr_msg_len = p_ccb->p_curr_msg->len; + + /* while not congested and we haven't sent it all */ + while ((!p_ccb->cong) && (p_ccb->p_curr_msg != NULL)) { + /* check what kind of message we've got here; we are using the offset + ** to indicate that a message is being fragmented + */ + + /* if message isn't being fragmented and it fits in mtu */ + if ((p_ccb->p_curr_msg->offset == AVDT_MSG_OFFSET) && + (p_ccb->p_curr_msg->len <= p_tbl->peer_mtu - AVDT_LEN_TYPE_SINGLE)) { + pkt_type = AVDT_PKT_TYPE_SINGLE; + hdr_len = AVDT_LEN_TYPE_SINGLE; + p_buf = p_ccb->p_curr_msg; + } + /* if message isn't being fragmented and it doesn't fit in mtu */ + else if ((p_ccb->p_curr_msg->offset == AVDT_MSG_OFFSET) && + (p_ccb->p_curr_msg->len > p_tbl->peer_mtu - AVDT_LEN_TYPE_SINGLE)) { + pkt_type = AVDT_PKT_TYPE_START; + hdr_len = AVDT_LEN_TYPE_START; + nosp = (p_ccb->p_curr_msg->len + AVDT_LEN_TYPE_START - p_tbl->peer_mtu) / + (p_tbl->peer_mtu - 1) + 2; + + /* get a new buffer for fragment we are sending */ + if ((p_buf = (BT_HDR *) osi_malloc(AVDT_CMD_BUF_SIZE)) == NULL) { + /* do we even want to try and recover from this? could do so + by setting retransmission timer */ + return TRUE; + } + + /* copy portion of data from current message to new buffer */ + p_buf->offset = L2CAP_MIN_OFFSET + hdr_len; + p_buf->len = p_tbl->peer_mtu - hdr_len; + memcpy((UINT8 *)(p_buf + 1) + p_buf->offset, + (UINT8 *)(p_ccb->p_curr_msg + 1) + p_ccb->p_curr_msg->offset, p_buf->len); + } + /* if message is being fragmented and remaining bytes don't fit in mtu */ + else if ((p_ccb->p_curr_msg->offset > AVDT_MSG_OFFSET) && + (p_ccb->p_curr_msg->len > (p_tbl->peer_mtu - AVDT_LEN_TYPE_CONT))) { + pkt_type = AVDT_PKT_TYPE_CONT; + hdr_len = AVDT_LEN_TYPE_CONT; + + /* get a new buffer for fragment we are sending */ + if ((p_buf = (BT_HDR *) osi_malloc(AVDT_CMD_BUF_SIZE)) == NULL) { + /* do we even want to try and recover from this? could do so + by setting retransmission timer */ + return TRUE; + } + + /* copy portion of data from current message to new buffer */ + p_buf->offset = L2CAP_MIN_OFFSET + hdr_len; + p_buf->len = p_tbl->peer_mtu - hdr_len; + memcpy((UINT8 *)(p_buf + 1) + p_buf->offset, + (UINT8 *)(p_ccb->p_curr_msg + 1) + p_ccb->p_curr_msg->offset, p_buf->len); + } + /* if message is being fragmented and remaining bytes do fit in mtu */ + else { + pkt_type = AVDT_PKT_TYPE_END; + hdr_len = AVDT_LEN_TYPE_END; + p_buf = p_ccb->p_curr_msg; + } + + /* label, sig id, msg type are in hdr of p_curr_msg */ + label = AVDT_LAYERSPEC_LABEL(p_ccb->p_curr_msg->layer_specific); + msg = AVDT_LAYERSPEC_MSG(p_ccb->p_curr_msg->layer_specific); + sig = (UINT8) p_ccb->p_curr_msg->event; + AVDT_TRACE_DEBUG("avdt_msg_send label:%d, msg:%d, sig:%d\n", label, msg, sig); + + /* keep track of how much of msg we've sent */ + curr_msg_len -= p_buf->len; + if (curr_msg_len == 0) { + /* entire message sent; mark as finished */ + p_ccb->p_curr_msg = NULL; + + /* start timer here for commands */ + if (msg == AVDT_MSG_TYPE_CMD) { + /* if retransmit timeout set to zero, sig doesn't use retransmit */ + if ((sig == AVDT_SIG_DISCOVER) || (sig == AVDT_SIG_GETCAP) || + (sig == AVDT_SIG_SECURITY) || (avdt_cb.rcb.ret_tout == 0)) { + btu_start_timer(&p_ccb->timer_entry, BTU_TTYPE_AVDT_CCB_RSP, avdt_cb.rcb.sig_tout); + } else if (sig != AVDT_SIG_DELAY_RPT) { + btu_start_timer(&p_ccb->timer_entry, BTU_TTYPE_AVDT_CCB_RET, avdt_cb.rcb.ret_tout); + } + } + } else { + /* message being fragmented and not completely sent */ + p_ccb->p_curr_msg->len -= p_buf->len; + p_ccb->p_curr_msg->offset += p_buf->len; + } + + /* set up to build header */ + p_buf->len += hdr_len; + p_buf->offset -= hdr_len; + p = (UINT8 *)(p_buf + 1) + p_buf->offset; + + /* build header */ + AVDT_MSG_BLD_HDR(p, label, pkt_type, msg); + if (pkt_type == AVDT_PKT_TYPE_START) { + AVDT_MSG_BLD_NOSP(p, nosp); + } + if ((pkt_type == AVDT_PKT_TYPE_START) || (pkt_type == AVDT_PKT_TYPE_SINGLE)) { + AVDT_MSG_BLD_SIG(p, sig); + } + + /* send msg buffer down */ + avdt_ad_write_req(AVDT_CHAN_SIG, p_ccb, NULL, p_buf); + } + return (p_ccb->cong); +} + +/******************************************************************************* +** +** Function avdt_msg_asmbl +** +** Description Reassemble incoming message. +** +** +** Returns Pointer to reassembled message; NULL if no message +** available. +** +*******************************************************************************/ +BT_HDR *avdt_msg_asmbl(tAVDT_CCB *p_ccb, BT_HDR *p_buf) +{ + UINT8 *p; + UINT8 pkt_type; + BT_HDR *p_ret; + UINT16 buf_len; + + /* parse the message header */ + p = (UINT8 *)(p_buf + 1) + p_buf->offset; + AVDT_MSG_PRS_PKT_TYPE(p, pkt_type); + + /* quick sanity check on length */ + if (p_buf->len < avdt_msg_pkt_type_len[pkt_type]) { + osi_free(p_buf); + AVDT_TRACE_WARNING("Bad length during reassembly"); + p_ret = NULL; + } + /* single packet */ + else if (pkt_type == AVDT_PKT_TYPE_SINGLE) { + /* if reassembly in progress drop message and process new single */ + if (p_ccb->p_rx_msg != NULL) { + osi_free(p_ccb->p_rx_msg); + p_ccb->p_rx_msg = NULL; + AVDT_TRACE_WARNING("Got single during reassembly"); + } + p_ret = p_buf; + } + /* start packet */ + else if (pkt_type == AVDT_PKT_TYPE_START) { + /* if reassembly in progress drop message and process new single */ + if (p_ccb->p_rx_msg != NULL) { + osi_free(p_ccb->p_rx_msg); + p_ccb->p_rx_msg = NULL; + AVDT_TRACE_WARNING("Got start during reassembly"); + } + /* + * Allocate bigger buffer for reassembly. As lower layers are + * not aware of possible packet size after reassembly, they + * would have allocated smaller buffer. + */ + p_ccb->p_rx_msg = (BT_HDR *)osi_malloc(BT_DEFAULT_BUFFER_SIZE); + memcpy(p_ccb->p_rx_msg, p_buf, + sizeof(BT_HDR) + p_buf->offset + p_buf->len); + + /* Free original buffer */ + osi_free(p_buf); + + /* update p to point to new buffer */ + p = (UINT8 *)(p_ccb->p_rx_msg + 1) + p_ccb->p_rx_msg->offset; + + /* copy first header byte over nosp */ + *(p + 1) = *p; + + /* set offset to point to where to copy next */ + p_ccb->p_rx_msg->offset += p_ccb->p_rx_msg->len; + + /* adjust length for packet header */ + p_ccb->p_rx_msg->len -= 1; + + p_ret = NULL; + } + /* continue or end */ + else { + /* if no reassembly in progress drop message */ + if (p_ccb->p_rx_msg == NULL) { + osi_free(p_buf); + AVDT_TRACE_WARNING("Pkt type=%d out of order\n", pkt_type); + p_ret = NULL; + } else { + /* get size of buffer holding assembled message */ + buf_len = BT_DEFAULT_BUFFER_SIZE - sizeof(BT_HDR); + + /* adjust offset and len of fragment for header byte */ + p_buf->offset += AVDT_LEN_TYPE_CONT; + p_buf->len -= AVDT_LEN_TYPE_CONT; + + /* verify length */ + if ((p_ccb->p_rx_msg->offset + p_buf->len) > buf_len) { + /* won't fit; free everything */ + AVDT_TRACE_WARNING("%s: Fragmented message too big!", __func__); + osi_free(p_ccb->p_rx_msg); + p_ccb->p_rx_msg = NULL; + osi_free(p_buf); + p_ret = NULL; + } else { + /* copy contents of p_buf to p_rx_msg */ + memcpy((UINT8 *)(p_ccb->p_rx_msg + 1) + p_ccb->p_rx_msg->offset, + (UINT8 *)(p_buf + 1) + p_buf->offset, p_buf->len); + + if (pkt_type == AVDT_PKT_TYPE_END) { + p_ccb->p_rx_msg->offset -= p_ccb->p_rx_msg->len; + p_ccb->p_rx_msg->len += p_buf->len; + p_ret = p_ccb->p_rx_msg; + p_ccb->p_rx_msg = NULL; + } else { + p_ccb->p_rx_msg->offset += p_buf->len; + p_ccb->p_rx_msg->len += p_buf->len; + p_ret = NULL; + } + osi_free(p_buf); + } + } + } + return p_ret; +} + +/******************************************************************************* +** +** Function avdt_msg_send_cmd +** +** Description This function is called to send a command message. The +** sig_id parameter indicates the message type, p_params +** points to the message parameters, if any. It gets a buffer +** from the AVDTP command pool, executes the message building +** function for this message type. It then queues the message +** in the command queue for this CCB. +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_msg_send_cmd(tAVDT_CCB *p_ccb, void *p_scb, UINT8 sig_id, tAVDT_MSG *p_params) +{ + BT_HDR *p_buf; + UINT8 *p; + UINT8 *p_start; + + /* get a buffer */ + p_buf = (BT_HDR *) osi_malloc(AVDT_CMD_BUF_SIZE); + if (p_buf == NULL) { + AVDT_TRACE_ERROR("avdt_msg_send_cmd out of buffer!!"); + return; + } + + /* set up gki buf pointer and offset */ + p_buf->offset = AVDT_MSG_OFFSET; + p_start = p = (UINT8 *)(p_buf + 1) + p_buf->offset; + + /* execute parameter building function to build message */ + (*avdt_msg_bld_cmd[sig_id - 1])(&p, p_params); + + /* set len */ + p_buf->len = (UINT16) (p - p_start); + + /* now store scb hdls, if any, in buf */ + if (p_scb != NULL) { + p = (UINT8 *)(p_buf + 1); + + /* for start and suspend, p_scb points to array of handles */ + if ((sig_id == AVDT_SIG_START) || (sig_id == AVDT_SIG_SUSPEND)) { + memcpy(p, (UINT8 *) p_scb, p_buf->len); + } + /* for all others, p_scb points to scb as usual */ + else { + *p = avdt_scb_to_hdl((tAVDT_SCB *) p_scb); + } + } + + /* stash sig, label, and message type in buf */ + p_buf->event = sig_id; + AVDT_BLD_LAYERSPEC(p_buf->layer_specific, AVDT_MSG_TYPE_CMD, p_ccb->label); + + /* increment label */ + p_ccb->label = (p_ccb->label + 1) % 16; + + /* queue message and trigger ccb to send it */ + fixed_queue_enqueue(p_ccb->cmd_q, p_buf, FIXED_QUEUE_MAX_TIMEOUT); + avdt_ccb_event(p_ccb, AVDT_CCB_SENDMSG_EVT, NULL); +} + + +/******************************************************************************* +** +** Function avdt_msg_send_rsp +** +** Description This function is called to send a response message. The +** sig_id parameter indicates the message type, p_params +** points to the message parameters, if any. It gets a buffer +** from the AVDTP command pool, executes the message building +** function for this message type. It then queues the message +** in the response queue for this CCB. +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_msg_send_rsp(tAVDT_CCB *p_ccb, UINT8 sig_id, tAVDT_MSG *p_params) +{ + BT_HDR *p_buf; + UINT8 *p; + UINT8 *p_start; + + /* get a buffer */ + p_buf = (BT_HDR *) osi_malloc(AVDT_CMD_BUF_SIZE); + if (p_buf == NULL) { + return; + } + + /* set up gki buf pointer and offset */ + p_buf->offset = AVDT_MSG_OFFSET; + p_start = p = (UINT8 *)(p_buf + 1) + p_buf->offset; + + /* execute parameter building function to build message */ + (*avdt_msg_bld_rsp[sig_id - 1])(&p, p_params); + + /* set length */ + p_buf->len = (UINT16) (p - p_start); + + /* stash sig, label, and message type in buf */ + p_buf->event = sig_id; + AVDT_BLD_LAYERSPEC(p_buf->layer_specific, AVDT_MSG_TYPE_RSP, p_params->hdr.label); + + /* queue message and trigger ccb to send it */ + fixed_queue_enqueue(p_ccb->rsp_q, p_buf, FIXED_QUEUE_MAX_TIMEOUT); + avdt_ccb_event(p_ccb, AVDT_CCB_SENDMSG_EVT, NULL); +} + + +/******************************************************************************* +** +** Function avdt_msg_send_rej +** +** Description This function is called to send a reject message. The +** sig_id parameter indicates the message type. It gets +** a buffer from the AVDTP command pool and builds the +** message based on the message type and the error code. +** It then queues the message in the response queue for +** this CCB. +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_msg_send_rej(tAVDT_CCB *p_ccb, UINT8 sig_id, tAVDT_MSG *p_params) +{ + BT_HDR *p_buf; + UINT8 *p; + UINT8 *p_start; + + /* get a buffer */ + p_buf = (BT_HDR *) osi_malloc(AVDT_CMD_BUF_SIZE); + if (p_buf == NULL) { + return; + } + + /* set up gki buf pointer and offset */ + p_buf->offset = AVDT_MSG_OFFSET; + p_start = p = (UINT8 *)(p_buf + 1) + p_buf->offset; + + /* if sig id included, build into message */ + if (sig_id != AVDT_SIG_NONE) { + /* if this sig has a parameter, add the parameter */ + if ((sig_id == AVDT_SIG_SETCONFIG) || + (sig_id == AVDT_SIG_RECONFIG)) { + AVDT_MSG_BLD_PARAM(p, p_params->hdr.err_param); + } else if ((sig_id == AVDT_SIG_START) || + (sig_id == AVDT_SIG_SUSPEND)) { + AVDT_MSG_BLD_SEID(p, p_params->hdr.err_param); + } + + /* add the error code */ + AVDT_MSG_BLD_ERR(p, p_params->hdr.err_code); + } + AVDT_TRACE_DEBUG("avdt_msg_send_rej"); + + /* calculate length */ + p_buf->len = (UINT16) (p - p_start); + + /* stash sig, label, and message type in buf */ + p_buf->event = sig_id; + AVDT_BLD_LAYERSPEC(p_buf->layer_specific, AVDT_MSG_TYPE_REJ, p_params->hdr.label); + + /* queue message and trigger ccb to send it */ + fixed_queue_enqueue(p_ccb->rsp_q, p_buf, FIXED_QUEUE_MAX_TIMEOUT); + avdt_ccb_event(p_ccb, AVDT_CCB_SENDMSG_EVT, NULL); +} + +/******************************************************************************* +** +** Function avdt_msg_send_grej +** +** Description This function is called to send a general reject message. The +** sig_id parameter indicates the message type. It gets +** a buffer from the AVDTP command pool and builds the +** message based on the message type and the error code. +** It then queues the message in the response queue for +** this CCB. +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_msg_send_grej(tAVDT_CCB *p_ccb, UINT8 sig_id, tAVDT_MSG *p_params) +{ + BT_HDR *p_buf; + UINT8 *p; + UINT8 *p_start; + + /* get a buffer */ + p_buf = (BT_HDR *) osi_malloc(AVDT_CMD_BUF_SIZE); + if (p_buf == NULL) { + return; + } + + /* set up gki buf pointer and offset */ + p_buf->offset = AVDT_MSG_OFFSET; + p_start = p = (UINT8 *)(p_buf + 1) + p_buf->offset; + + /* calculate length */ + p_buf->len = (UINT16) (p - p_start); + + /* stash sig, label, and message type in buf */ + p_buf->event = sig_id; + AVDT_BLD_LAYERSPEC(p_buf->layer_specific, AVDT_MSG_TYPE_GRJ, p_params->hdr.label); + AVDT_TRACE_DEBUG("avdt_msg_send_grej"); + + /* queue message and trigger ccb to send it */ + fixed_queue_enqueue(p_ccb->rsp_q, p_buf, FIXED_QUEUE_MAX_TIMEOUT); + avdt_ccb_event(p_ccb, AVDT_CCB_SENDMSG_EVT, NULL); +} + +/******************************************************************************* +** +** Function avdt_msg_ind +** +** Description This function is called by the adaption layer when an +** incoming message is received on the signaling channel. +** It parses the message and sends an event to the appropriate +** SCB or CCB for the message. +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_msg_ind(tAVDT_CCB *p_ccb, BT_HDR *p_buf) +{ + tAVDT_SCB *p_scb; + UINT8 *p; + BOOLEAN ok = TRUE; + BOOLEAN handle_rsp = FALSE; + BOOLEAN gen_rej = FALSE; + UINT8 label; + UINT8 pkt_type; + UINT8 msg_type; + UINT8 sig = 0; + tAVDT_MSG msg; + tAVDT_CFG cfg; + UINT8 err; + UINT8 evt = 0; + UINT8 scb_hdl; + + /* reassemble message; if no message available (we received a fragment) return */ + if ((p_buf = avdt_msg_asmbl(p_ccb, p_buf)) == NULL) { + return; + } + + p = (UINT8 *)(p_buf + 1) + p_buf->offset; + + /* parse the message header */ + AVDT_MSG_PRS_HDR(p, label, pkt_type, msg_type); + + UNUSED(pkt_type); + + AVDT_TRACE_DEBUG("msg_type=%d, sig=%d\n", msg_type, sig); + /* set up label and ccb_idx in message hdr */ + msg.hdr.label = label; + msg.hdr.ccb_idx = avdt_ccb_to_idx(p_ccb); + + /* verify msg type */ + if (msg_type == AVDT_MSG_TYPE_GRJ) { + AVDT_TRACE_WARNING("Dropping msg msg_type=%d\n", msg_type); + ok = FALSE; + } + /* check for general reject */ + else if ((msg_type == AVDT_MSG_TYPE_REJ) && (p_buf->len == AVDT_LEN_GEN_REJ)) { + gen_rej = TRUE; + if (p_ccb->p_curr_cmd != NULL) { + msg.hdr.sig_id = sig = (UINT8) p_ccb->p_curr_cmd->event; + evt = avdt_msg_rej_2_evt[sig - 1]; + msg.hdr.err_code = AVDT_ERR_NSC; + msg.hdr.err_param = 0; + } + } else { /* not a general reject */ + /* get and verify signal */ + AVDT_MSG_PRS_SIG(p, sig); + msg.hdr.sig_id = sig; + if ((sig == 0) || (sig > AVDT_SIG_MAX)) { + AVDT_TRACE_WARNING("Dropping msg sig=%d msg_type:%d\n", sig, msg_type); + ok = FALSE; + + /* send a general reject */ + if (msg_type == AVDT_MSG_TYPE_CMD) { + avdt_msg_send_grej(p_ccb, sig, &msg); + } + } + } + + if (ok && !gen_rej) { + /* skip over header (msg length already verified during reassembly) */ + p_buf->len -= AVDT_LEN_TYPE_SINGLE; + + /* set up to parse message */ + if ((msg_type == AVDT_MSG_TYPE_RSP) && (sig == AVDT_SIG_DISCOVER)) { + /* parse discover rsp message to struct supplied by app */ + msg.discover_rsp.p_sep_info = (tAVDT_SEP_INFO *) p_ccb->p_proc_data; + msg.discover_rsp.num_seps = p_ccb->proc_param; + } else if ((msg_type == AVDT_MSG_TYPE_RSP) && + ((sig == AVDT_SIG_GETCAP) || (sig == AVDT_SIG_GET_ALLCAP))) { + /* parse discover rsp message to struct supplied by app */ + msg.svccap.p_cfg = (tAVDT_CFG *) p_ccb->p_proc_data; + } else if ((msg_type == AVDT_MSG_TYPE_RSP) && (sig == AVDT_SIG_GETCONFIG)) { + /* parse get config rsp message to struct allocated locally */ + msg.svccap.p_cfg = &cfg; + } else if ((msg_type == AVDT_MSG_TYPE_CMD) && (sig == AVDT_SIG_SETCONFIG)) { + /* parse config cmd message to struct allocated locally */ + msg.config_cmd.p_cfg = &cfg; + } else if ((msg_type == AVDT_MSG_TYPE_CMD) && (sig == AVDT_SIG_RECONFIG)) { + /* parse reconfig cmd message to struct allocated locally */ + msg.reconfig_cmd.p_cfg = &cfg; + } + + /* parse message; while we're at it map message sig to event */ + if (msg_type == AVDT_MSG_TYPE_CMD) { + msg.hdr.err_code = err = (*avdt_msg_prs_cmd[sig - 1])(&msg, p, p_buf->len); + evt = avdt_msg_cmd_2_evt[sig - 1]; + } else if (msg_type == AVDT_MSG_TYPE_RSP) { + msg.hdr.err_code = err = (*avdt_msg_prs_rsp[sig - 1])(&msg, p, p_buf->len); + evt = avdt_msg_rsp_2_evt[sig - 1]; + } else { /* msg_type == AVDT_MSG_TYPE_REJ */ + err = avdt_msg_prs_rej(&msg, p, sig); + evt = avdt_msg_rej_2_evt[sig - 1]; + } + + /* if parsing failed */ + if (err != 0) { + AVDT_TRACE_WARNING("Parsing failed sig=%d err=0x%x\n", sig, err); + + /* if its a rsp or rej, drop it; if its a cmd, send a rej; + ** note special case for abort; never send abort reject + */ + ok = FALSE; + if ((msg_type == AVDT_MSG_TYPE_CMD) && (sig != AVDT_SIG_ABORT)) { + avdt_msg_send_rej(p_ccb, sig, &msg); + } + } + } + + /* if its a rsp or rej, check sent cmd to see if we're waiting for + ** the rsp or rej. If we didn't send a cmd for it, drop it. If + ** it does match a cmd, stop timer for the cmd. + */ + if (ok) { + if ((msg_type == AVDT_MSG_TYPE_RSP) || (msg_type == AVDT_MSG_TYPE_REJ)) { + if ((p_ccb->p_curr_cmd != NULL) && + (p_ccb->p_curr_cmd->event == sig) && + (AVDT_LAYERSPEC_LABEL(p_ccb->p_curr_cmd->layer_specific) == label)) { + /* stop timer */ + btu_stop_timer(&p_ccb->timer_entry); + + /* clear retransmission count */ + p_ccb->ret_count = 0; + + /* later in this function handle ccb event */ + handle_rsp = TRUE; + } else { + ok = FALSE; + AVDT_TRACE_WARNING("Cmd not found for rsp sig=%d label=%d\n", sig, label); + } + } + } + + if (ok) { + /* if it's a ccb event send to ccb */ + if (evt & AVDT_CCB_MKR) { + avdt_ccb_event(p_ccb, (UINT8)(evt & ~AVDT_CCB_MKR), (tAVDT_CCB_EVT *) &msg); + } + /* if it's a scb event */ + else { + /* Scb events always have a single seid. For cmd, get seid from + ** message. For rej and rsp, get seid from p_curr_cmd. + */ + if (msg_type == AVDT_MSG_TYPE_CMD) { + scb_hdl = msg.single.seid; + } else { + scb_hdl = *((UINT8 *)(p_ccb->p_curr_cmd + 1)); + } + + /* Map seid to the scb and send it the event. For cmd, seid has + ** already been verified by parsing function. + */ + if (evt && (p_scb = avdt_scb_by_hdl(scb_hdl)) != NULL) { + avdt_scb_event(p_scb, evt, (tAVDT_SCB_EVT *) &msg); + } + } + } + + /* free message buffer */ + osi_free(p_buf); + + /* if its a rsp or rej, send event to ccb to free associated + ** cmd msg buffer and handle cmd queue + */ + if (handle_rsp) { + avdt_ccb_event(p_ccb, AVDT_CCB_RCVRSP_EVT, NULL); + } +} + +#endif /* #if (defined(AVDT_INCLUDED) && AVDT_INCLUDED == TRUE) */ diff --git a/lib/bt/host/bluedroid/stack/avdt/avdt_scb.c b/lib/bt/host/bluedroid/stack/avdt/avdt_scb.c new file mode 100644 index 00000000..43bc4452 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/avdt/avdt_scb.c @@ -0,0 +1,796 @@ +/****************************************************************************** + * + * Copyright (C) 2002-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This module contains the stream control block and functions which + * operate on the stream control block. + * + ******************************************************************************/ + +#include +#include "stack/bt_types.h" +#include "common/bt_target.h" +#include "common/bt_defs.h" +#include "stack/avdt_api.h" +#include "stack/avdtc_api.h" +#include "avdt_int.h" +#include "stack/btu.h" +#include "osi/allocator.h" +#include "osi/fixed_queue.h" + +#if (defined(AVDT_INCLUDED) && AVDT_INCLUDED == TRUE) + +/***************************************************************************** +** state machine constants and types +*****************************************************************************/ +#if AVDT_DEBUG == TRUE + +/* verbose state strings for trace */ +const char *const avdt_scb_st_str[] = { + "SCB_IDLE_ST", + "SCB_CONF_ST", + "SCB_OPENING_ST", + "SCB_OPEN_ST", + "SCB_STREAM_ST", + "SCB_CLOSING_ST" +}; + +/* verbose event strings for trace */ +const char *const avdt_scb_evt_str[] = { + "API_REMOVE_EVT", + "API_WRITE_REQ_EVT", + "API_GETCONFIG_REQ_EVT", + "API_DELAY_RPT_REQ", + "API_SETCONFIG_REQ_EVT", + "API_OPEN_REQ_EVT", + "API_CLOSE_REQ_EVT", + "API_RECONFIG_REQ_EVT", + "API_SECURITY_REQ_EVT", + "API_ABORT_REQ_EVT", + "API_GETCONFIG_RSP_EVT", + "API_SETCONFIG_RSP_EVT", + "API_SETCONFIG_REJ_EVT", + "API_OPEN_RSP_EVT", + "API_CLOSE_RSP_EVT", + "API_RECONFIG_RSP_EVT", + "API_SECURITY_RSP_EVT", + "API_ABORT_RSP_EVT", + "MSG_SETCONFIG_CMD_EVT", + "MSG_GETCONFIG_CMD_EVT", + "MSG_OPEN_CMD_EVT", + "MSG_START_CMD_EVT", + "MSG_SUSPEND_CMD_EVT", + "MSG_CLOSE_CMD_EVT", + "MSG_ABORT_CMD_EVT", + "MSG_RECONFIG_CMD_EVT", + "MSG_SECURITY_CMD_EVT", + "MSG_DELAY_RPT_CMD_EVT", + "MSG_DELAY_RPT_RSP_EVT", + "MSG_SETCONFIG_RSP_EVT", + "MSG_GETCONFIG_RSP_EVT", + "MSG_OPEN_RSP_EVT", + "MSG_START_RSP_EVT", + "MSG_SUSPEND_RSP_EVT", + "MSG_CLOSE_RSP_EVT", + "MSG_ABORT_RSP_EVT", + "MSG_RECONFIG_RSP_EVT", + "MSG_SECURITY_RSP_EVT", + "MSG_SETCONFIG_REJ_EVT", + "MSG_OPEN_REJ_EVT", + "MSG_START_REJ_EVT", + "MSG_SUSPEND_REJ_EVT", + "TC_TOUT_EVT", + "TC_OPEN_EVT", + "TC_CLOSE_EVT", + "TC_CONG_EVT", + "TC_DATA_EVT", + "CC_CLOSE_EVT", + "DELAY_RPT_RSP_TOUT_EVT" +}; + +#endif + + +/* action function list */ +const tAVDT_SCB_ACTION avdt_scb_action[] = { + avdt_scb_hdl_abort_cmd, + avdt_scb_hdl_abort_rsp, + avdt_scb_hdl_close_cmd, + avdt_scb_hdl_close_rsp, + avdt_scb_hdl_getconfig_cmd, + avdt_scb_hdl_getconfig_rsp, + avdt_scb_hdl_open_cmd, + avdt_scb_hdl_open_rej, + avdt_scb_hdl_open_rsp, + avdt_scb_hdl_pkt, + avdt_scb_drop_pkt, + avdt_scb_hdl_reconfig_cmd, + avdt_scb_hdl_reconfig_rsp, + avdt_scb_hdl_security_cmd, + avdt_scb_hdl_security_rsp, + avdt_scb_hdl_setconfig_cmd, + avdt_scb_hdl_setconfig_rej, + avdt_scb_hdl_setconfig_rsp, + avdt_scb_hdl_start_cmd, + avdt_scb_hdl_start_rsp, + avdt_scb_hdl_suspend_cmd, + avdt_scb_hdl_suspend_rsp, + avdt_scb_hdl_tc_close, +#if AVDT_REPORTING == TRUE + avdt_scb_hdl_tc_close_sto, +#endif + avdt_scb_hdl_tc_open, +#if AVDT_REPORTING == TRUE + avdt_scb_hdl_tc_open_sto, +#endif + avdt_scb_snd_delay_rpt_req, + avdt_scb_hdl_delay_rpt_cmd, + avdt_scb_hdl_delay_rpt_rsp, + avdt_scb_hdl_write_req, + avdt_scb_snd_abort_req, + avdt_scb_snd_abort_rsp, + avdt_scb_snd_close_req, + avdt_scb_snd_stream_close, + avdt_scb_snd_close_rsp, + avdt_scb_snd_getconfig_req, + avdt_scb_snd_getconfig_rsp, + avdt_scb_snd_open_req, + avdt_scb_snd_open_rsp, + avdt_scb_snd_reconfig_req, + avdt_scb_snd_reconfig_rsp, + avdt_scb_snd_security_req, + avdt_scb_snd_security_rsp, + avdt_scb_snd_setconfig_req, + avdt_scb_snd_setconfig_rej, + avdt_scb_snd_setconfig_rsp, + avdt_scb_snd_tc_close, + avdt_scb_cb_err, + avdt_scb_cong_state, + avdt_scb_rej_state, + avdt_scb_rej_in_use, + avdt_scb_rej_not_in_use, + avdt_scb_set_remove, + avdt_scb_free_pkt, + avdt_scb_clr_pkt, + avdt_scb_chk_snd_pkt, + avdt_scb_tc_timer, + avdt_scb_clr_vars, + avdt_scb_dealloc, + avdt_scb_hdl_delay_rpt_tout, + avdt_scb_init_open_req, + avdt_scb_send_delay_report_cmd +}; + +/* state table information */ +#define AVDT_SCB_ACTIONS 2 /* number of actions */ +#define AVDT_SCB_NEXT_STATE 2 /* position of next state */ +#define AVDT_SCB_NUM_COLS 3 /* number of columns in state tables */ + +/* state table for idle state */ +const UINT8 avdt_scb_st_idle[][AVDT_SCB_NUM_COLS] = { + /* Event Action 1 Action 2 Next state */ + /* API_REMOVE_EVT */ {AVDT_SCB_DEALLOC, AVDT_SCB_IGNORE, AVDT_SCB_IDLE_ST}, + /* API_WRITE_REQ_EVT */ {AVDT_SCB_FREE_PKT, AVDT_SCB_IGNORE, AVDT_SCB_IDLE_ST}, + /* API_GETCONFIG_REQ_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_IDLE_ST}, + /* API_DELAY_RPT_REQ_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_IDLE_ST}, + /* API_SETCONFIG_REQ_EVT */ {AVDT_SCB_SND_SETCONFIG_REQ, AVDT_SCB_IGNORE, AVDT_SCB_IDLE_ST}, + /* API_OPEN_REQ_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_IDLE_ST}, + /* API_CLOSE_REQ_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_IDLE_ST}, + /* API_RECONFIG_REQ_EVT */ {AVDT_SCB_CB_ERR, AVDT_SCB_IGNORE, AVDT_SCB_IDLE_ST}, + /* API_SECURITY_REQ_EVT */ {AVDT_SCB_CB_ERR, AVDT_SCB_IGNORE, AVDT_SCB_IDLE_ST}, + /* API_ABORT_REQ_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_IDLE_ST}, + /* API_GETCONFIG_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_IDLE_ST}, + /* API_SETCONFIG_RSP_EVT */ {AVDT_SCB_SND_SETCONFIG_RSP, AVDT_SCB_SEND_DELAY_REPORT_CMD_EVT, AVDT_SCB_CONF_ST}, + /* API_SETCONFIG_REJ_EVT */ {AVDT_SCB_SND_SETCONFIG_REJ, AVDT_SCB_IGNORE, AVDT_SCB_IDLE_ST}, + /* API_OPEN_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_IDLE_ST}, + /* API_CLOSE_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_IDLE_ST}, + /* API_RECONFIG_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_IDLE_ST}, + /* API_SECURITY_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_IDLE_ST}, + /* API_ABORT_RSP_EVT */ {AVDT_SCB_SND_ABORT_RSP, AVDT_SCB_IGNORE, AVDT_SCB_IDLE_ST}, + /* MSG_SETCONFIG_CMD_EVT */ {AVDT_SCB_HDL_SETCONFIG_CMD, AVDT_SCB_IGNORE, AVDT_SCB_IDLE_ST}, + /* MSG_GETCONFIG_CMD_EVT */ {AVDT_SCB_REJ_STATE, AVDT_SCB_IGNORE, AVDT_SCB_IDLE_ST}, + /* MSG_OPEN_CMD_EVT */ {AVDT_SCB_REJ_STATE, AVDT_SCB_IGNORE, AVDT_SCB_IDLE_ST}, + /* MSG_START_CMD_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_IDLE_ST}, + /* MSG_SUSPEND_CMD_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_IDLE_ST}, + /* MSG_CLOSE_CMD_EVT */ {AVDT_SCB_REJ_STATE, AVDT_SCB_IGNORE, AVDT_SCB_IDLE_ST}, + /* MSG_ABORT_CMD_EVT */ {AVDT_SCB_HDL_ABORT_CMD, AVDT_SCB_IGNORE, AVDT_SCB_IDLE_ST}, + /* MSG_RECONFIG_CMD_EVT */ {AVDT_SCB_REJ_NOT_IN_USE, AVDT_SCB_IGNORE, AVDT_SCB_IDLE_ST}, + /* MSG_SECURITY_CMD_EVT */ {AVDT_SCB_REJ_STATE, AVDT_SCB_IGNORE, AVDT_SCB_IDLE_ST}, + /* MSG_DELAY_RPT_CMD_EVT */ {AVDT_SCB_REJ_STATE, AVDT_SCB_IGNORE, AVDT_SCB_IDLE_ST}, + /* MSG_DELAY_RPT_RSP_EVT */ {AVDT_SCB_HDL_DELAY_RPT_RSP, AVDT_SCB_IGNORE, AVDT_SCB_IDLE_ST}, + /* MSG_SETCONFIG_RSP_EVT */ {AVDT_SCB_HDL_SETCONFIG_RSP, AVDT_SCB_INIT_OPEN_REQ_EVT, AVDT_SCB_CONF_ST}, + /* MSG_GETCONFIG_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_IDLE_ST}, + /* MSG_OPEN_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_IDLE_ST}, + /* MSG_START_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_IDLE_ST}, + /* MSG_SUSPEND_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_IDLE_ST}, + /* MSG_CLOSE_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_IDLE_ST}, + /* MSG_ABORT_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_IDLE_ST}, + /* MSG_RECONFIG_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_IDLE_ST}, + /* MSG_SECURITY_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_IDLE_ST}, + /* MSG_SETCONFIG_REJ_EVT */ {AVDT_SCB_HDL_SETCONFIG_REJ, AVDT_SCB_IGNORE, AVDT_SCB_IDLE_ST}, + /* MSG_OPEN_REJ_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_IDLE_ST}, + /* MSG_START_REJ_EVT */ {AVDT_SCB_HDL_START_RSP, AVDT_SCB_IGNORE, AVDT_SCB_IDLE_ST}, + /* MSG_SUSPEND_REJ_EVT */ {AVDT_SCB_HDL_SUSPEND_RSP, AVDT_SCB_IGNORE, AVDT_SCB_IDLE_ST}, + /* TC_TOUT_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_IDLE_ST}, + /* TC_OPEN_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_IDLE_ST}, + /* TC_CLOSE_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_IDLE_ST}, + /* TC_CONG_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_IDLE_ST}, + /* TC_DATA_EVT */ {AVDT_SCB_DROP_PKT, AVDT_SCB_IGNORE, AVDT_SCB_IDLE_ST}, + /* CC_CLOSE_EVT */ {AVDT_SCB_CLR_VARS, AVDT_SCB_IGNORE, AVDT_SCB_IDLE_ST}, + /* DELAY_RPT_RSP_TOUT_EVT */{AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_IDLE_ST} +}; + +/* state table for configured state */ +const UINT8 avdt_scb_st_conf[][AVDT_SCB_NUM_COLS] = { + /* Event Action 1 Action 2 Next state */ + /* API_REMOVE_EVT */ {AVDT_SCB_SND_ABORT_REQ, AVDT_SCB_SET_REMOVE, AVDT_SCB_CONF_ST}, + /* API_WRITE_REQ_EVT */ {AVDT_SCB_FREE_PKT, AVDT_SCB_IGNORE, AVDT_SCB_CONF_ST}, + /* API_GETCONFIG_REQ_EVT */ {AVDT_SCB_SND_GETCONFIG_REQ, AVDT_SCB_IGNORE, AVDT_SCB_CONF_ST}, + /* API_DELAY_RPT_REQ_EVT */ {AVDT_SCB_SND_DELAY_RPT_REQ, AVDT_SCB_IGNORE, AVDT_SCB_CONF_ST}, + /* API_SETCONFIG_REQ_EVT */ {AVDT_SCB_CB_ERR, AVDT_SCB_IGNORE, AVDT_SCB_CONF_ST}, + /* API_OPEN_REQ_EVT */ {AVDT_SCB_SND_OPEN_REQ, AVDT_SCB_IGNORE, AVDT_SCB_CONF_ST}, + /* API_CLOSE_REQ_EVT */ {AVDT_SCB_SND_ABORT_REQ, AVDT_SCB_IGNORE, AVDT_SCB_CONF_ST}, + /* API_RECONFIG_REQ_EVT */ {AVDT_SCB_CB_ERR, AVDT_SCB_IGNORE, AVDT_SCB_CONF_ST}, + /* API_SECURITY_REQ_EVT */ {AVDT_SCB_SND_SECURITY_REQ, AVDT_SCB_IGNORE, AVDT_SCB_CONF_ST}, + /* API_ABORT_REQ_EVT */ {AVDT_SCB_SND_ABORT_REQ, AVDT_SCB_IGNORE, AVDT_SCB_CONF_ST}, + /* API_GETCONFIG_RSP_EVT */ {AVDT_SCB_SND_GETCONFIG_RSP, AVDT_SCB_IGNORE, AVDT_SCB_CONF_ST}, + /* API_SETCONFIG_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_CONF_ST}, + /* API_SETCONFIG_REJ_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_CONF_ST}, + /* API_OPEN_RSP_EVT */ {AVDT_SCB_SND_OPEN_RSP, AVDT_SCB_IGNORE, AVDT_SCB_OPENING_ST}, + /* API_CLOSE_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_CONF_ST}, + /* API_RECONFIG_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_CONF_ST}, + /* API_SECURITY_RSP_EVT */ {AVDT_SCB_SND_SECURITY_RSP, AVDT_SCB_IGNORE, AVDT_SCB_CONF_ST}, + /* API_ABORT_RSP_EVT */ {AVDT_SCB_SND_ABORT_RSP, AVDT_SCB_HDL_TC_CLOSE, AVDT_SCB_IDLE_ST}, + /* MSG_SETCONFIG_CMD_EVT */ {AVDT_SCB_REJ_IN_USE, AVDT_SCB_IGNORE, AVDT_SCB_CONF_ST}, + /* MSG_GETCONFIG_CMD_EVT */ {AVDT_SCB_HDL_GETCONFIG_CMD, AVDT_SCB_IGNORE, AVDT_SCB_CONF_ST}, + /* MSG_OPEN_CMD_EVT */ {AVDT_SCB_HDL_OPEN_CMD, AVDT_SCB_IGNORE, AVDT_SCB_CONF_ST}, + /* MSG_START_CMD_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_CONF_ST}, + /* MSG_SUSPEND_CMD_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_CONF_ST}, + /* MSG_CLOSE_CMD_EVT */ {AVDT_SCB_REJ_STATE, AVDT_SCB_IGNORE, AVDT_SCB_CONF_ST}, + /* MSG_ABORT_CMD_EVT */ {AVDT_SCB_HDL_ABORT_CMD, AVDT_SCB_IGNORE, AVDT_SCB_CONF_ST}, + /* MSG_RECONFIG_CMD_EVT */ {AVDT_SCB_REJ_STATE, AVDT_SCB_IGNORE, AVDT_SCB_CONF_ST}, + /* MSG_SECURITY_CMD_EVT */ {AVDT_SCB_HDL_SECURITY_CMD, AVDT_SCB_IGNORE, AVDT_SCB_CONF_ST}, + /* MSG_DELAY_RPT_CMD_EVT */ {AVDT_SCB_HDL_DELAY_RPT_CMD, AVDT_SCB_IGNORE, AVDT_SCB_CONF_ST}, + /* MSG_DELAY_RPT_RSP_EVT */ {AVDT_SCB_HDL_DELAY_RPT_RSP, AVDT_SCB_IGNORE, AVDT_SCB_CONF_ST}, + /* MSG_SETCONFIG_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_CONF_ST}, + /* MSG_GETCONFIG_RSP_EVT */ {AVDT_SCB_HDL_GETCONFIG_RSP, AVDT_SCB_IGNORE, AVDT_SCB_CONF_ST}, + /* MSG_OPEN_RSP_EVT */ {AVDT_SCB_HDL_OPEN_RSP, AVDT_SCB_IGNORE, AVDT_SCB_OPENING_ST}, + /* MSG_START_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_CONF_ST}, + /* MSG_SUSPEND_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_CONF_ST}, + /* MSG_CLOSE_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_CONF_ST}, + /* MSG_ABORT_RSP_EVT */ {AVDT_SCB_HDL_ABORT_RSP, AVDT_SCB_HDL_TC_CLOSE, AVDT_SCB_IDLE_ST}, + /* MSG_RECONFIG_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_CONF_ST}, + /* MSG_SECURITY_RSP_EVT */ {AVDT_SCB_HDL_SECURITY_RSP, AVDT_SCB_IGNORE, AVDT_SCB_CONF_ST}, + /* MSG_SETCONFIG_REJ_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_CONF_ST}, + /* MSG_OPEN_REJ_EVT */ {AVDT_SCB_HDL_OPEN_REJ, AVDT_SCB_IGNORE, AVDT_SCB_IDLE_ST}, + /* MSG_START_REJ_EVT */ {AVDT_SCB_HDL_START_RSP, AVDT_SCB_IGNORE, AVDT_SCB_CONF_ST}, + /* MSG_SUSPEND_REJ_EVT */ {AVDT_SCB_HDL_SUSPEND_RSP, AVDT_SCB_IGNORE, AVDT_SCB_CONF_ST}, + /* TC_TOUT_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_CONF_ST}, + /* TC_OPEN_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_CONF_ST}, + /* TC_CLOSE_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_CONF_ST}, + /* TC_CONG_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_CONF_ST}, + /* TC_DATA_EVT */ {AVDT_SCB_DROP_PKT, AVDT_SCB_IGNORE, AVDT_SCB_CONF_ST}, + /* CC_CLOSE_EVT */ {AVDT_SCB_HDL_TC_CLOSE, AVDT_SCB_IGNORE, AVDT_SCB_IDLE_ST}, + /* DELAY_RPT_RSP_TOUT_EVT */{AVDT_SCB_HDL_DELAY_RPT_TOUT, AVDT_SCB_IGNORE, AVDT_SCB_CONF_ST} +}; + +/* state table for opening state */ +const UINT8 avdt_scb_st_opening[][AVDT_SCB_NUM_COLS] = { + /* Event Action 1 Action 2 Next state */ + /* API_REMOVE_EVT */ {AVDT_SCB_SND_CLOSE_REQ, AVDT_SCB_SET_REMOVE, AVDT_SCB_CLOSING_ST}, + /* API_WRITE_REQ_EVT */ {AVDT_SCB_FREE_PKT, AVDT_SCB_IGNORE, AVDT_SCB_OPENING_ST}, + /* API_GETCONFIG_REQ_EVT */ {AVDT_SCB_SND_GETCONFIG_REQ, AVDT_SCB_IGNORE, AVDT_SCB_OPENING_ST}, + /* API_DELAY_RPT_REQ_EVT */ {AVDT_SCB_SND_DELAY_RPT_REQ, AVDT_SCB_IGNORE, AVDT_SCB_OPENING_ST}, + /* API_SETCONFIG_REQ_EVT */ {AVDT_SCB_CB_ERR, AVDT_SCB_IGNORE, AVDT_SCB_OPENING_ST}, + /* API_OPEN_REQ_EVT */ {AVDT_SCB_CB_ERR, AVDT_SCB_IGNORE, AVDT_SCB_OPENING_ST}, + /* API_CLOSE_REQ_EVT */ {AVDT_SCB_SND_CLOSE_REQ, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST}, + /* API_RECONFIG_REQ_EVT */ {AVDT_SCB_CB_ERR, AVDT_SCB_IGNORE, AVDT_SCB_OPENING_ST}, + /* API_SECURITY_REQ_EVT */ {AVDT_SCB_SND_SECURITY_REQ, AVDT_SCB_IGNORE, AVDT_SCB_OPENING_ST}, + /* API_ABORT_REQ_EVT */ {AVDT_SCB_SND_ABORT_REQ, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST}, + /* API_GETCONFIG_RSP_EVT */ {AVDT_SCB_SND_GETCONFIG_RSP, AVDT_SCB_IGNORE, AVDT_SCB_OPENING_ST}, + /* API_SETCONFIG_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_OPENING_ST}, + /* API_SETCONFIG_REJ_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_OPENING_ST}, + /* API_OPEN_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_OPENING_ST}, + /* API_CLOSE_RSP_EVT */ {AVDT_SCB_SND_CLOSE_RSP, AVDT_SCB_SND_TC_CLOSE, AVDT_SCB_CLOSING_ST}, + /* API_RECONFIG_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_OPENING_ST}, + /* API_SECURITY_RSP_EVT */ {AVDT_SCB_SND_SECURITY_RSP, AVDT_SCB_IGNORE, AVDT_SCB_OPENING_ST}, + /* API_ABORT_RSP_EVT */ {AVDT_SCB_SND_ABORT_RSP, AVDT_SCB_SND_TC_CLOSE, AVDT_SCB_CLOSING_ST}, + /* MSG_SETCONFIG_CMD_EVT */ {AVDT_SCB_REJ_STATE, AVDT_SCB_IGNORE, AVDT_SCB_OPENING_ST}, + /* MSG_GETCONFIG_CMD_EVT */ {AVDT_SCB_HDL_GETCONFIG_CMD, AVDT_SCB_IGNORE, AVDT_SCB_OPENING_ST}, + /* MSG_OPEN_CMD_EVT */ {AVDT_SCB_REJ_STATE, AVDT_SCB_IGNORE, AVDT_SCB_OPENING_ST}, + /* MSG_START_CMD_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_OPENING_ST}, + /* MSG_SUSPEND_CMD_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_OPENING_ST}, + /* MSG_CLOSE_CMD_EVT */ {AVDT_SCB_HDL_CLOSE_CMD, AVDT_SCB_IGNORE, AVDT_SCB_OPENING_ST}, + /* MSG_ABORT_CMD_EVT */ {AVDT_SCB_HDL_ABORT_CMD, AVDT_SCB_IGNORE, AVDT_SCB_OPENING_ST}, + /* MSG_RECONFIG_CMD_EVT */ {AVDT_SCB_REJ_STATE, AVDT_SCB_IGNORE, AVDT_SCB_OPENING_ST}, + /* MSG_SECURITY_CMD_EVT */ {AVDT_SCB_HDL_SECURITY_CMD, AVDT_SCB_IGNORE, AVDT_SCB_OPENING_ST}, + /* MSG_DELAY_RPT_CMD_EVT */ {AVDT_SCB_HDL_DELAY_RPT_CMD, AVDT_SCB_IGNORE, AVDT_SCB_OPENING_ST}, + /* MSG_DELAY_RPT_RSP_EVT */ {AVDT_SCB_HDL_DELAY_RPT_RSP, AVDT_SCB_IGNORE, AVDT_SCB_OPENING_ST}, + /* MSG_SETCONFIG_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_OPENING_ST}, + /* MSG_GETCONFIG_RSP_EVT */ {AVDT_SCB_HDL_GETCONFIG_RSP, AVDT_SCB_IGNORE, AVDT_SCB_OPENING_ST}, + /* MSG_OPEN_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_OPENING_ST}, + /* MSG_START_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_OPENING_ST}, + /* MSG_SUSPEND_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_OPENING_ST}, + /* MSG_CLOSE_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_OPENING_ST}, + /* MSG_ABORT_RSP_EVT */ {AVDT_SCB_SND_TC_CLOSE, AVDT_SCB_HDL_ABORT_RSP, AVDT_SCB_CLOSING_ST}, + /* MSG_RECONFIG_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_OPENING_ST}, + /* MSG_SECURITY_RSP_EVT */ {AVDT_SCB_HDL_SECURITY_RSP, AVDT_SCB_IGNORE, AVDT_SCB_OPENING_ST}, + /* MSG_SETCONFIG_REJ_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_OPENING_ST}, + /* MSG_OPEN_REJ_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_OPENING_ST}, + /* MSG_START_REJ_EVT */ {AVDT_SCB_HDL_START_RSP, AVDT_SCB_IGNORE, AVDT_SCB_OPENING_ST}, + /* MSG_SUSPEND_REJ_EVT */ {AVDT_SCB_HDL_SUSPEND_RSP, AVDT_SCB_IGNORE, AVDT_SCB_OPENING_ST}, + /* TC_TOUT_EVT */ {AVDT_SCB_SND_ABORT_REQ, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST}, + /* TC_OPEN_EVT */ {AVDT_SCB_HDL_TC_OPEN, AVDT_SCB_IGNORE, AVDT_SCB_OPEN_ST}, + /* TC_CLOSE_EVT */ {AVDT_SCB_HDL_TC_CLOSE, AVDT_SCB_IGNORE, AVDT_SCB_IDLE_ST}, + /* TC_CONG_EVT */ {AVDT_SCB_CONG_STATE, AVDT_SCB_IGNORE, AVDT_SCB_OPENING_ST}, + /* TC_DATA_EVT */ {AVDT_SCB_DROP_PKT, AVDT_SCB_IGNORE, AVDT_SCB_OPENING_ST}, + /* CC_CLOSE_EVT */ {AVDT_SCB_SND_TC_CLOSE, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST}, + /* DELAY_RPT_RSP_TOUT_EVT */{AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_OPENING_ST} +}; + +/* state table for open state */ +const UINT8 avdt_scb_st_open[][AVDT_SCB_NUM_COLS] = { + /* Event Action 1 Action 2 Next state */ + /* API_REMOVE_EVT */ {AVDT_SCB_SND_CLOSE_REQ, AVDT_SCB_SET_REMOVE, AVDT_SCB_CLOSING_ST}, + /* API_WRITE_REQ_EVT */ {AVDT_SCB_FREE_PKT, AVDT_SCB_IGNORE, AVDT_SCB_OPEN_ST}, + /* API_GETCONFIG_REQ_EVT */ {AVDT_SCB_SND_GETCONFIG_REQ, AVDT_SCB_IGNORE, AVDT_SCB_OPEN_ST}, + /* API_DELAY_RPT_REQ_EVT */ {AVDT_SCB_SND_DELAY_RPT_REQ, AVDT_SCB_IGNORE, AVDT_SCB_OPEN_ST}, + /* API_SETCONFIG_REQ_EVT */ {AVDT_SCB_CB_ERR, AVDT_SCB_IGNORE, AVDT_SCB_OPEN_ST}, + /* API_OPEN_REQ_EVT */ {AVDT_SCB_CB_ERR, AVDT_SCB_IGNORE, AVDT_SCB_OPEN_ST}, + /* API_CLOSE_REQ_EVT */ {AVDT_SCB_SND_CLOSE_REQ, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST}, + /* API_RECONFIG_REQ_EVT */ {AVDT_SCB_SND_RECONFIG_REQ, AVDT_SCB_IGNORE, AVDT_SCB_OPEN_ST}, + /* API_SECURITY_REQ_EVT */ {AVDT_SCB_SND_SECURITY_REQ, AVDT_SCB_IGNORE, AVDT_SCB_OPEN_ST}, + /* API_ABORT_REQ_EVT */ {AVDT_SCB_SND_ABORT_REQ, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST}, + /* API_GETCONFIG_RSP_EVT */ {AVDT_SCB_SND_GETCONFIG_RSP, AVDT_SCB_IGNORE, AVDT_SCB_OPEN_ST}, + /* API_SETCONFIG_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_OPEN_ST}, + /* API_SETCONFIG_REJ_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_OPEN_ST}, + /* API_OPEN_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_OPEN_ST}, + /* API_CLOSE_RSP_EVT */ {AVDT_SCB_SND_CLOSE_RSP, AVDT_SCB_TC_TIMER, AVDT_SCB_CLOSING_ST}, + /* API_RECONFIG_RSP_EVT */ {AVDT_SCB_SND_RECONFIG_RSP, AVDT_SCB_IGNORE, AVDT_SCB_OPEN_ST}, + /* API_SECURITY_RSP_EVT */ {AVDT_SCB_SND_SECURITY_RSP, AVDT_SCB_IGNORE, AVDT_SCB_OPEN_ST}, + /* API_ABORT_RSP_EVT */ {AVDT_SCB_SND_ABORT_RSP, AVDT_SCB_TC_TIMER, AVDT_SCB_CLOSING_ST}, + /* MSG_SETCONFIG_CMD_EVT */ {AVDT_SCB_REJ_STATE, AVDT_SCB_IGNORE, AVDT_SCB_OPEN_ST}, + /* MSG_GETCONFIG_CMD_EVT */ {AVDT_SCB_HDL_GETCONFIG_CMD, AVDT_SCB_IGNORE, AVDT_SCB_OPEN_ST}, + /* MSG_OPEN_CMD_EVT */ {AVDT_SCB_REJ_STATE, AVDT_SCB_IGNORE, AVDT_SCB_OPEN_ST}, + /* MSG_START_CMD_EVT */ {AVDT_SCB_HDL_START_CMD, AVDT_SCB_IGNORE, AVDT_SCB_STREAM_ST}, + /* MSG_SUSPEND_CMD_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_OPEN_ST}, + /* MSG_CLOSE_CMD_EVT */ {AVDT_SCB_HDL_CLOSE_CMD, AVDT_SCB_IGNORE, AVDT_SCB_OPEN_ST}, + /* MSG_ABORT_CMD_EVT */ {AVDT_SCB_HDL_ABORT_CMD, AVDT_SCB_IGNORE, AVDT_SCB_OPEN_ST}, + /* MSG_RECONFIG_CMD_EVT */ {AVDT_SCB_HDL_RECONFIG_CMD, AVDT_SCB_IGNORE, AVDT_SCB_OPEN_ST}, + /* MSG_SECURITY_CMD_EVT */ {AVDT_SCB_HDL_SECURITY_CMD, AVDT_SCB_IGNORE, AVDT_SCB_OPEN_ST}, + /* MSG_DELAY_RPT_CMD_EVT */ {AVDT_SCB_HDL_DELAY_RPT_CMD, AVDT_SCB_IGNORE, AVDT_SCB_OPEN_ST}, + /* MSG_DELAY_RPT_RSP_EVT */ {AVDT_SCB_HDL_DELAY_RPT_RSP, AVDT_SCB_IGNORE, AVDT_SCB_OPEN_ST}, + /* MSG_SETCONFIG_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_OPEN_ST}, + /* MSG_GETCONFIG_RSP_EVT */ {AVDT_SCB_HDL_GETCONFIG_RSP, AVDT_SCB_IGNORE, AVDT_SCB_OPEN_ST}, + /* MSG_OPEN_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_OPEN_ST}, + /* MSG_START_RSP_EVT */ {AVDT_SCB_HDL_START_RSP, AVDT_SCB_IGNORE, AVDT_SCB_STREAM_ST}, + /* MSG_SUSPEND_RSP_EVT */ {AVDT_SCB_HDL_SUSPEND_RSP, AVDT_SCB_IGNORE, AVDT_SCB_OPEN_ST}, + /* MSG_CLOSE_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_OPEN_ST}, + /* MSG_ABORT_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_IDLE_ST}, + /* MSG_RECONFIG_RSP_EVT */ {AVDT_SCB_HDL_RECONFIG_RSP, AVDT_SCB_IGNORE, AVDT_SCB_OPEN_ST}, + /* MSG_SECURITY_RSP_EVT */ {AVDT_SCB_HDL_SECURITY_RSP, AVDT_SCB_IGNORE, AVDT_SCB_OPEN_ST}, + /* MSG_SETCONFIG_REJ_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_OPEN_ST}, + /* MSG_OPEN_REJ_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_OPEN_ST}, + /* MSG_START_REJ_EVT */ {AVDT_SCB_HDL_START_RSP, AVDT_SCB_IGNORE, AVDT_SCB_OPEN_ST}, + /* MSG_SUSPEND_REJ_EVT */ {AVDT_SCB_HDL_SUSPEND_RSP, AVDT_SCB_IGNORE, AVDT_SCB_OPEN_ST}, + /* TC_TOUT_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_OPEN_ST}, +#if AVDT_REPORTING == TRUE + /* TC_OPEN_EVT */ {AVDT_SCB_HDL_TC_OPEN_STO, AVDT_SCB_IGNORE, AVDT_SCB_OPEN_ST}, + /* TC_CLOSE_EVT */ {AVDT_SCB_HDL_TC_CLOSE_STO, AVDT_SCB_IGNORE, AVDT_SCB_OPEN_ST}, +#else + /* TC_OPEN_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_OPEN_ST}, + /* TC_CLOSE_EVT */ {AVDT_SCB_HDL_TC_CLOSE, AVDT_SCB_IGNORE, AVDT_SCB_OPEN_ST}, +#endif + /* TC_CONG_EVT */ {AVDT_SCB_CONG_STATE, AVDT_SCB_IGNORE, AVDT_SCB_OPEN_ST}, + /* TC_DATA_EVT */ {AVDT_SCB_DROP_PKT, AVDT_SCB_IGNORE, AVDT_SCB_OPEN_ST}, + /* CC_CLOSE_EVT */ {AVDT_SCB_SND_TC_CLOSE, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST}, + /* DELAY_RPT_RSP_TOUT_EVT */{AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_OPEN_ST} +}; + +/* state table for streaming state */ +const UINT8 avdt_scb_st_stream[][AVDT_SCB_NUM_COLS] = { + /* Event Action 1 Action 2 Next state */ + /* API_REMOVE_EVT */ {AVDT_SCB_SND_STREAM_CLOSE, AVDT_SCB_SET_REMOVE, AVDT_SCB_CLOSING_ST}, + /* API_WRITE_REQ_EVT */ {AVDT_SCB_HDL_WRITE_REQ, AVDT_SCB_CHK_SND_PKT, AVDT_SCB_STREAM_ST}, + /* API_GETCONFIG_REQ_EVT */ {AVDT_SCB_SND_GETCONFIG_REQ, AVDT_SCB_IGNORE, AVDT_SCB_STREAM_ST}, + /* API_DELAY_RPT_REQ_EVT */ {AVDT_SCB_SND_DELAY_RPT_REQ, AVDT_SCB_IGNORE, AVDT_SCB_STREAM_ST}, + /* API_SETCONFIG_REQ_EVT */ {AVDT_SCB_CB_ERR, AVDT_SCB_IGNORE, AVDT_SCB_STREAM_ST}, + /* API_OPEN_REQ_EVT */ {AVDT_SCB_CB_ERR, AVDT_SCB_IGNORE, AVDT_SCB_STREAM_ST}, + /* API_CLOSE_REQ_EVT */ {AVDT_SCB_SND_STREAM_CLOSE, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST}, + /* API_RECONFIG_REQ_EVT */ {AVDT_SCB_CB_ERR, AVDT_SCB_IGNORE, AVDT_SCB_STREAM_ST}, + /* API_SECURITY_REQ_EVT */ {AVDT_SCB_SND_SECURITY_REQ, AVDT_SCB_IGNORE, AVDT_SCB_STREAM_ST}, + /* API_ABORT_REQ_EVT */ {AVDT_SCB_SND_ABORT_REQ, AVDT_SCB_CLR_PKT, AVDT_SCB_CLOSING_ST}, + /* API_GETCONFIG_RSP_EVT */ {AVDT_SCB_SND_GETCONFIG_RSP, AVDT_SCB_IGNORE, AVDT_SCB_STREAM_ST}, + /* API_SETCONFIG_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_STREAM_ST}, + /* API_SETCONFIG_REJ_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_STREAM_ST}, + /* API_OPEN_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_STREAM_ST}, + /* API_CLOSE_RSP_EVT */ {AVDT_SCB_SND_CLOSE_RSP, AVDT_SCB_TC_TIMER, AVDT_SCB_CLOSING_ST}, + /* API_RECONFIG_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_STREAM_ST}, + /* API_SECURITY_RSP_EVT */ {AVDT_SCB_SND_SECURITY_RSP, AVDT_SCB_IGNORE, AVDT_SCB_STREAM_ST}, + /* API_ABORT_RSP_EVT */ {AVDT_SCB_SND_ABORT_RSP, AVDT_SCB_TC_TIMER, AVDT_SCB_CLOSING_ST}, + /* MSG_SETCONFIG_CMD_EVT */ {AVDT_SCB_REJ_STATE, AVDT_SCB_IGNORE, AVDT_SCB_STREAM_ST}, + /* MSG_GETCONFIG_CMD_EVT */ {AVDT_SCB_HDL_GETCONFIG_CMD, AVDT_SCB_IGNORE, AVDT_SCB_STREAM_ST}, + /* MSG_OPEN_CMD_EVT */ {AVDT_SCB_REJ_STATE, AVDT_SCB_IGNORE, AVDT_SCB_STREAM_ST}, + /* MSG_START_CMD_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_STREAM_ST}, + /* MSG_SUSPEND_CMD_EVT */ {AVDT_SCB_HDL_SUSPEND_CMD, AVDT_SCB_CLR_PKT, AVDT_SCB_OPEN_ST}, + /* MSG_CLOSE_CMD_EVT */ {AVDT_SCB_HDL_CLOSE_CMD, AVDT_SCB_IGNORE, AVDT_SCB_STREAM_ST}, + /* MSG_ABORT_CMD_EVT */ {AVDT_SCB_HDL_ABORT_CMD, AVDT_SCB_CLR_PKT, AVDT_SCB_STREAM_ST}, + /* MSG_RECONFIG_CMD_EVT */ {AVDT_SCB_REJ_STATE, AVDT_SCB_IGNORE, AVDT_SCB_STREAM_ST}, + /* MSG_SECURITY_CMD_EVT */ {AVDT_SCB_HDL_SECURITY_CMD, AVDT_SCB_IGNORE, AVDT_SCB_STREAM_ST}, + /* MSG_DELAY_RPT_CMD_EVT */ {AVDT_SCB_HDL_DELAY_RPT_CMD, AVDT_SCB_IGNORE, AVDT_SCB_STREAM_ST}, + /* MSG_DELAY_RPT_RSP_EVT */ {AVDT_SCB_HDL_DELAY_RPT_RSP, AVDT_SCB_IGNORE, AVDT_SCB_STREAM_ST}, + /* MSG_SETCONFIG_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_STREAM_ST}, + /* MSG_GETCONFIG_RSP_EVT */ {AVDT_SCB_HDL_GETCONFIG_RSP, AVDT_SCB_IGNORE, AVDT_SCB_STREAM_ST}, + /* MSG_OPEN_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_STREAM_ST}, + /* MSG_START_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_STREAM_ST}, + /* MSG_SUSPEND_RSP_EVT */ {AVDT_SCB_HDL_SUSPEND_RSP, AVDT_SCB_CLR_PKT, AVDT_SCB_OPEN_ST}, + /* MSG_CLOSE_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_STREAM_ST}, + /* MSG_ABORT_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_STREAM_ST}, + /* MSG_RECONFIG_RSP_EVT */ {AVDT_SCB_HDL_RECONFIG_RSP, AVDT_SCB_IGNORE, AVDT_SCB_STREAM_ST}, + /* MSG_SECURITY_RSP_EVT */ {AVDT_SCB_HDL_SECURITY_RSP, AVDT_SCB_IGNORE, AVDT_SCB_STREAM_ST}, + /* MSG_SETCONFIG_REJ_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_STREAM_ST}, + /* MSG_OPEN_REJ_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_STREAM_ST}, + /* MSG_START_REJ_EVT */ {AVDT_SCB_HDL_START_RSP, AVDT_SCB_IGNORE, AVDT_SCB_STREAM_ST}, + /* MSG_SUSPEND_REJ_EVT */ {AVDT_SCB_HDL_SUSPEND_RSP, AVDT_SCB_IGNORE, AVDT_SCB_STREAM_ST}, + /* TC_TOUT_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_STREAM_ST}, + /* TC_OPEN_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_STREAM_ST}, + /* TC_CLOSE_EVT */ {AVDT_SCB_HDL_TC_CLOSE, AVDT_SCB_IGNORE, AVDT_SCB_IDLE_ST}, + /* TC_CONG_EVT */ {AVDT_SCB_CONG_STATE, AVDT_SCB_CHK_SND_PKT, AVDT_SCB_STREAM_ST}, + /* TC_DATA_EVT */ {AVDT_SCB_HDL_PKT, AVDT_SCB_IGNORE, AVDT_SCB_STREAM_ST}, + /* CC_CLOSE_EVT */ {AVDT_SCB_SND_TC_CLOSE, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST}, + /* DELAY_RPT_RSP_TOUT_EVT */{AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_STREAM_ST} +}; + +/* state table for closing state */ +const UINT8 avdt_scb_st_closing[][AVDT_SCB_NUM_COLS] = { + /* Event Action 1 Action 2 Next state */ + /* API_REMOVE_EVT */ {AVDT_SCB_SET_REMOVE, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST}, + /* API_WRITE_REQ_EVT */ {AVDT_SCB_FREE_PKT, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST}, + /* API_GETCONFIG_REQ_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST}, + /* API_DELAY_RPT_REQ_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST}, + /* API_SETCONFIG_REQ_EVT */ {AVDT_SCB_CB_ERR, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST}, + /* API_OPEN_REQ_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST}, + /* API_CLOSE_REQ_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST}, + /* API_RECONFIG_REQ_EVT */ {AVDT_SCB_CB_ERR, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST}, + /* API_SECURITY_REQ_EVT */ {AVDT_SCB_CB_ERR, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST}, + /* API_ABORT_REQ_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST}, + /* API_GETCONFIG_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST}, + /* API_SETCONFIG_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST}, + /* API_SETCONFIG_REJ_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST}, + /* API_OPEN_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST}, + /* API_CLOSE_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST}, + /* API_RECONFIG_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST}, + /* API_SECURITY_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST}, + /* API_ABORT_RSP_EVT */ {AVDT_SCB_SND_ABORT_RSP, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST}, + /* MSG_SETCONFIG_CMD_EVT */ {AVDT_SCB_REJ_STATE, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST}, + /* MSG_GETCONFIG_CMD_EVT */ {AVDT_SCB_REJ_STATE, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST}, + /* MSG_OPEN_CMD_EVT */ {AVDT_SCB_REJ_STATE, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST}, + /* MSG_START_CMD_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST}, + /* MSG_SUSPEND_CMD_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST}, + /* MSG_CLOSE_CMD_EVT */ {AVDT_SCB_REJ_STATE, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST}, + /* MSG_ABORT_CMD_EVT */ {AVDT_SCB_HDL_ABORT_CMD, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST}, + /* MSG_RECONFIG_CMD_EVT */ {AVDT_SCB_REJ_STATE, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST}, + /* MSG_SECURITY_CMD_EVT */ {AVDT_SCB_REJ_STATE, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST}, + /* MSG_DELAY_RPT_CMD_EVT */ {AVDT_SCB_REJ_STATE, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST}, + /* MSG_DELAY_RPT_RSP_EVT */ {AVDT_SCB_HDL_DELAY_RPT_RSP, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST}, + /* MSG_SETCONFIG_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST}, + /* MSG_GETCONFIG_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST}, + /* MSG_OPEN_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST}, + /* MSG_START_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST}, + /* MSG_SUSPEND_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST}, + /* MSG_CLOSE_RSP_EVT */ {AVDT_SCB_SND_TC_CLOSE, AVDT_SCB_HDL_CLOSE_RSP, AVDT_SCB_CLOSING_ST}, + /* MSG_ABORT_RSP_EVT */ {AVDT_SCB_SND_TC_CLOSE, AVDT_SCB_HDL_ABORT_RSP, AVDT_SCB_CLOSING_ST}, + /* MSG_RECONFIG_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST}, + /* MSG_SECURITY_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST}, + /* MSG_SETCONFIG_REJ_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST}, + /* MSG_OPEN_REJ_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST}, + /* MSG_START_REJ_EVT */ {AVDT_SCB_HDL_START_RSP, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST}, + /* MSG_SUSPEND_REJ_EVT */ {AVDT_SCB_HDL_SUSPEND_RSP, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST}, + /* TC_TOUT_EVT */ {AVDT_SCB_SND_TC_CLOSE, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST}, + /* TC_OPEN_EVT */ {AVDT_SCB_SND_TC_CLOSE, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST}, + /* TC_CLOSE_EVT */ {AVDT_SCB_HDL_TC_CLOSE, AVDT_SCB_IGNORE, AVDT_SCB_IDLE_ST}, + /* TC_CONG_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST}, + /* TC_DATA_EVT */ {AVDT_SCB_DROP_PKT, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST}, + /* CC_CLOSE_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST}, + /* DELAY_RPT_RSP_TOUT_EVT */{AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST} +}; + +/* type for state table */ +typedef const UINT8 (*tAVDT_SCB_ST_TBL)[AVDT_SCB_NUM_COLS]; + +/* state table */ +const tAVDT_SCB_ST_TBL avdt_scb_st_tbl[] = { + avdt_scb_st_idle, + avdt_scb_st_conf, + avdt_scb_st_opening, + avdt_scb_st_open, + avdt_scb_st_stream, + avdt_scb_st_closing +}; + + +/******************************************************************************* +** +** Function avdt_scb_event +** +** Description State machine event handling function for scb +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_event(tAVDT_SCB *p_scb, UINT8 event, tAVDT_SCB_EVT *p_data) +{ + tAVDT_SCB_ST_TBL state_table; + UINT8 action; + int i; + +#if AVDT_DEBUG == TRUE + AVDT_TRACE_EVENT("SCB hdl=%d event=%d/%s state=%s\n", avdt_scb_to_hdl(p_scb), event, avdt_scb_evt_str[event], avdt_scb_st_str[p_scb->state]); +#endif + /* set current event */ + p_scb->curr_evt = event; + + /* look up the state table for the current state */ + state_table = avdt_scb_st_tbl[p_scb->state]; + + /* set next state */ + if (p_scb->state != state_table[event][AVDT_SCB_NEXT_STATE]) { + p_scb->state = state_table[event][AVDT_SCB_NEXT_STATE]; + } + + /* execute action functions */ + for (i = 0; i < AVDT_SCB_ACTIONS; i++) { + if ((action = state_table[event][i]) != AVDT_SCB_IGNORE) { + (*avdt_cb.p_scb_act[action])(p_scb, p_data); + } else { + break; + } + } +} + + +/******************************************************************************* +** +** Function avdt_scb_init +** +** Description Initialize stream control block module. +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_init(void) +{ + memset(&avdt_cb.scb[0], 0, sizeof(tAVDT_SCB) * AVDT_NUM_SEPS); + avdt_cb.p_scb_act = (tAVDT_SCB_ACTION *) avdt_scb_action; + avdt_cb.delay_value = AVDT_DELAY_RPT_DFT_VALUE; +} + + +/******************************************************************************* +** +** Function avdt_scb_alloc +** +** Description Allocate a stream control block. +** +** +** Returns pointer to the scb, or NULL if none could be allocated. +** +*******************************************************************************/ +tAVDT_SCB *avdt_scb_alloc(tAVDT_CS *p_cs) +{ + tAVDT_SCB *p_scb = &avdt_cb.scb[0]; + int i; + + /* find available scb */ + for (i = 0; i < AVDT_NUM_SEPS; i++, p_scb++) { + if (!p_scb->allocated) { + memset(p_scb, 0, sizeof(tAVDT_SCB)); + p_scb->allocated = TRUE; + p_scb->p_ccb = NULL; + + /* initialize sink as activated */ + if (p_cs->tsep == AVDT_TSEP_SNK) { + p_scb->sink_activated = TRUE; + } + + memcpy(&p_scb->cs, p_cs, sizeof(tAVDT_CS)); +#if AVDT_MULTIPLEXING == TRUE + /* initialize fragments gueue */ + p_scb->frag_q = fixed_queue_new(QUEUE_SIZE_MAX); + + if (p_cs->cfg.psc_mask & AVDT_PSC_MUX) { + p_scb->cs.cfg.mux_tcid_media = avdt_ad_type_to_tcid(AVDT_CHAN_MEDIA, p_scb); +#if AVDT_REPORTING == TRUE + if (p_cs->cfg.psc_mask & AVDT_PSC_REPORT) { + p_scb->cs.cfg.mux_tcid_report = avdt_ad_type_to_tcid(AVDT_CHAN_REPORT, p_scb); + } +#endif + } +#endif + p_scb->timer_entry.param = (UINT32) p_scb; + AVDT_TRACE_DEBUG("avdt_scb_alloc hdl=%d, psc_mask:0x%x\n", i + 1, p_cs->cfg.psc_mask); + break; + } + } + + if (i == AVDT_NUM_SEPS) { + /* out of ccbs */ + p_scb = NULL; + AVDT_TRACE_WARNING("Out of scbs"); + } + + return p_scb; +} + +/******************************************************************************* +** +** Function avdt_scb_dealloc +** +** Description Deallocate a stream control block. +** +** +** Returns void. +** +*******************************************************************************/ +void avdt_scb_dealloc(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + UNUSED(p_data); + + AVDT_TRACE_DEBUG("avdt_scb_dealloc hdl=%d\n", avdt_scb_to_hdl(p_scb)); + btu_free_timer(&p_scb->timer_entry); + +#if AVDT_MULTIPLEXING == TRUE + /* free fragments we're holding, if any; it shouldn't happen */ + fixed_queue_free(p_scb->frag_q, osi_free_func); +#endif + + memset(p_scb, 0, sizeof(tAVDT_SCB)); +} + +/******************************************************************************* +** +** Function avdt_scb_to_hdl +** +** Description Given a pointer to an scb, return its handle (or seid). +** +** +** Returns Index of scb. +** +*******************************************************************************/ +UINT8 avdt_scb_to_hdl(tAVDT_SCB *p_scb) +{ + return (UINT8) (p_scb - avdt_cb.scb + 1); +} + +/******************************************************************************* +** +** Function avdt_scb_by_hdl +** +** Description Given an scb handle (or seid), return a pointer to the scb. +** +** +** Returns Pointer to scb or NULL if index is out of range or scb +** is not allocated. +** +*******************************************************************************/ +tAVDT_SCB *avdt_scb_by_hdl(UINT8 hdl) +{ + tAVDT_SCB *p_scb; + + /* verify index */ + if ((hdl > 0) && (hdl <= AVDT_NUM_SEPS)) { + p_scb = &avdt_cb.scb[hdl - 1]; + + /* verify scb is allocated */ + if (!p_scb->allocated) { + p_scb = NULL; + AVDT_TRACE_WARNING("scb hdl %d not allocated\n", hdl); + } + } else { + p_scb = NULL; + AVDT_TRACE_WARNING("scb hdl %d out of range\n", hdl); + } + return p_scb; +} + +/******************************************************************************* +** +** Function avdt_scb_verify +** +** Description Verify the condition of a list of scbs. +** +** +** Returns SEID that failed, or 0 if success. +** +*******************************************************************************/ +UINT8 avdt_scb_verify(tAVDT_CCB *p_ccb, UINT8 state, UINT8 *p_seid, UINT16 num_seid, UINT8 *p_err_code) +{ + int i; + tAVDT_SCB *p_scb; + UINT8 nsc_mask; + UINT8 ret = 0; + + AVDT_TRACE_DEBUG("avdt_scb_verify state %d\n", state); + /* set nonsupported command mask */ + /* translate public state into private state */ + nsc_mask = 0; + if (state == AVDT_VERIFY_SUSPEND) { + nsc_mask = AVDT_NSC_SUSPEND; + } + + /* verify every scb */ + for (i = 0, *p_err_code = 0; (i < num_seid) && (*p_err_code == 0) && (i < AVDT_NUM_SEPS); i++) { + if ((p_scb = avdt_scb_by_hdl(p_seid[i])) == NULL) { + *p_err_code = AVDT_ERR_BAD_STATE; + } else if (p_scb->p_ccb != p_ccb) { + *p_err_code = AVDT_ERR_BAD_STATE; + } else if (p_scb->cs.nsc_mask & nsc_mask) { + *p_err_code = AVDT_ERR_NSC; + } + + switch (state) { + case AVDT_VERIFY_OPEN: + case AVDT_VERIFY_START: + if (p_scb->state != AVDT_SCB_OPEN_ST && p_scb->state != AVDT_SCB_STREAM_ST) { + *p_err_code = AVDT_ERR_BAD_STATE; + } + break; + + case AVDT_VERIFY_SUSPEND: + case AVDT_VERIFY_STREAMING: + if (p_scb->state != AVDT_SCB_STREAM_ST) { + *p_err_code = AVDT_ERR_BAD_STATE; + } + break; + } + } + + if ((i != num_seid) && (i < AVDT_NUM_SEPS)) { + ret = p_seid[i]; + } + + return ret; +} + +/******************************************************************************* +** +** Function avdt_scb_peer_seid_list +** +** Description Given a list of SCB handles, return a list of peer SEIDs +** for the handles, copied in place into the struct passed in. +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_peer_seid_list(tAVDT_MULTI *p_multi) +{ + int i; + tAVDT_SCB *p_scb; + + for (i = 0; i < p_multi->num_seps; i++) { + if ((p_scb = avdt_scb_by_hdl(p_multi->seid_list[i])) != NULL) { + p_multi->seid_list[i] = p_scb->peer_seid; + } + } +} + +#endif /* #if (defined(AVDT_INCLUDED) && AVDT_INCLUDED == TRUE) */ diff --git a/lib/bt/host/bluedroid/stack/avdt/avdt_scb_act.c b/lib/bt/host/bluedroid/stack/avdt/avdt_scb_act.c new file mode 100644 index 00000000..c87e044f --- /dev/null +++ b/lib/bt/host/bluedroid/stack/avdt/avdt_scb_act.c @@ -0,0 +1,2187 @@ +/****************************************************************************** + * + * Copyright (C) 2002-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This module contains the action functions associated with the stream + * control block state machine. + * + ******************************************************************************/ + +#include +#include "stack/bt_types.h" +#include "common/bt_target.h" +#include "common/bt_defs.h" +#include "stack/avdt_api.h" +#include "stack/avdtc_api.h" +#include "avdt_int.h" +#include "stack/btu.h" +#include "osi/allocator.h" + +#if (defined(AVDT_INCLUDED) && AVDT_INCLUDED == TRUE) + +/* This table is used to lookup the callback event that matches a particular +** state machine API request event. Note that state machine API request +** events are at the beginning of the event list starting at zero, thus +** allowing for this table. +*/ +const UINT8 avdt_scb_cback_evt[] = { + 0, /* API_REMOVE_EVT (no event) */ + AVDT_WRITE_CFM_EVT, /* API_WRITE_REQ_EVT */ + 0, /* API_GETCONFIG_REQ_EVT (no event) */ + 0, /* API_DELAY_RPT_REQ_EVT (no event) */ + AVDT_OPEN_CFM_EVT, /* API_SETCONFIG_REQ_EVT */ + AVDT_OPEN_CFM_EVT, /* API_OPEN_REQ_EVT */ + AVDT_CLOSE_CFM_EVT, /* API_CLOSE_REQ_EVT */ + AVDT_RECONFIG_CFM_EVT, /* API_RECONFIG_REQ_EVT */ + AVDT_SECURITY_CFM_EVT, /* API_SECURITY_REQ_EVT */ + 0 /* API_ABORT_REQ_EVT (no event) */ +}; + +/* This table is used to look up the callback event based on the signaling +** role when the stream is closed. +*/ +const UINT8 avdt_scb_role_evt[] = { + AVDT_CLOSE_IND_EVT, /* AVDT_CLOSE_ACP */ + AVDT_CLOSE_CFM_EVT, /* AVDT_CLOSE_INT */ + AVDT_CLOSE_IND_EVT, /* AVDT_OPEN_ACP */ + AVDT_OPEN_CFM_EVT /* AVDT_OPEN_INT */ +}; + +/******************************************************************************* +** +** Function avdt_scb_gen_ssrc +** +** Description This function generates a SSRC number unique to the stream. +** +** Returns SSRC value. +** +*******************************************************************************/ +UINT32 avdt_scb_gen_ssrc(tAVDT_SCB *p_scb) +{ + /* combine the value of the media type and codec type of the SCB */ + return ((UINT32)(p_scb->cs.cfg.codec_info[1] | p_scb->cs.cfg.codec_info[2])); +} + +/******************************************************************************* +** +** Function avdt_scb_hdl_abort_cmd +** +** Description This function sends the SCB an AVDT_SCB_API_ABORT_RSP_EVT +** to initiate sending of an abort response message. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_hdl_abort_cmd(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + p_scb->role = AVDT_CLOSE_ACP; + avdt_scb_event(p_scb, AVDT_SCB_API_ABORT_RSP_EVT, p_data); +} + +/******************************************************************************* +** +** Function avdt_scb_hdl_abort_rsp +** +** Description This function is an empty function; it serves as a +** placeholder for a conformance API action function. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_hdl_abort_rsp(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + UNUSED(p_scb); + UNUSED(p_data); + return; +} + +/******************************************************************************* +** +** Function avdt_scb_hdl_close_cmd +** +** Description This function sends the SCB an AVDT_SCB_API_CLOSE_RSP_EVT +** to initiate sending of a close response message. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_hdl_close_cmd(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + p_scb->role = AVDT_CLOSE_ACP; + avdt_scb_event(p_scb, AVDT_SCB_API_CLOSE_RSP_EVT, p_data); +} + +/******************************************************************************* +** +** Function avdt_scb_hdl_close_rsp +** +** Description This function sets the close_code variable to the error +** code returned in the close response. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_hdl_close_rsp(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + p_scb->close_code = p_data->msg.hdr.err_code; +} + +/******************************************************************************* +** +** Function avdt_scb_hdl_getconfig_cmd +** +** Description This function retrieves the configuration parameters of +** the SCB and sends the SCB an AVDT_SCB_API_GETCONFIG_RSP_EVT +** to initiate sending of a get configuration response message. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_hdl_getconfig_cmd(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + p_data->msg.svccap.p_cfg = &p_scb->curr_cfg; + + avdt_scb_event(p_scb, AVDT_SCB_API_GETCONFIG_RSP_EVT, p_data); +} + +/******************************************************************************* +** +** Function avdt_scb_hdl_getconfig_rsp +** +** Description This function is an empty function; it serves as a +** placeholder for a conformance API action function. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_hdl_getconfig_rsp(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + UNUSED(p_scb); + UNUSED(p_data); + return; +} + +/******************************************************************************* +** +** Function avdt_scb_hdl_open_cmd +** +** Description This function sends the SCB an AVDT_SCB_API_OPEN_RSP_EVT +** to initiate sending of an open response message. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_hdl_open_cmd(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + avdt_scb_event(p_scb, AVDT_SCB_API_OPEN_RSP_EVT, p_data); +} + +/******************************************************************************* +** +** Function avdt_scb_hdl_open_rej +** +** Description This function calls the application callback function +** indicating the open request has failed. It initializes +** certain SCB variables and sends a AVDT_CCB_UL_CLOSE_EVT +** to the CCB. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_hdl_open_rej(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + /* do exactly same as setconfig reject */ + avdt_scb_hdl_setconfig_rej(p_scb, p_data); +} + +/******************************************************************************* +** +** Function avdt_scb_hdl_open_rsp +** +** Description This function calls avdt_ad_open_req() to initiate +** connection of the transport channel for this stream. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_hdl_open_rsp(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + UNUSED(p_data); + + /* initiate opening of trans channels for this SEID */ + p_scb->role = AVDT_OPEN_INT; + avdt_ad_open_req(AVDT_CHAN_MEDIA, p_scb->p_ccb, p_scb, AVDT_INT); + + /* start tc connect timer */ + btu_start_timer(&p_scb->timer_entry, BTU_TTYPE_AVDT_SCB_TC, AVDT_SCB_TC_CONN_TOUT); +} + +/******************************************************************************* +** +** Function avdt_scb_hdl_pkt_no_frag +** +** Description +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_hdl_pkt_no_frag(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + UINT8 *p, *p_start; + UINT8 o_v, o_p, o_x, o_cc; + UINT8 m_pt; + UINT8 marker; + UINT16 seq; + UINT32 time_stamp; + UINT16 offset; + UINT16 ex_len; + UINT8 pad_len = 0; + + p = p_start = (UINT8 *)(p_data->p_pkt + 1) + p_data->p_pkt->offset; + + /* parse media packet header */ + AVDT_MSG_PRS_OCTET1(p, o_v, o_p, o_x, o_cc); + AVDT_MSG_PRS_M_PT(p, m_pt, marker); + BE_STREAM_TO_UINT16(seq, p); + BE_STREAM_TO_UINT32(time_stamp, p); + p += 4; + + UNUSED(o_v); + + /* skip over any csrc's in packet */ + p += o_cc * 4; + + /* check for and skip over extension header */ + if (o_x) { + p += 2; + BE_STREAM_TO_UINT16(ex_len, p); + p += ex_len * 4; + } + + /* save our new offset */ + offset = (UINT16) (p - p_start); + + /* adjust length for any padding at end of packet */ + if (o_p) { + /* padding length in last byte of packet */ + pad_len = *(p_start + p_data->p_pkt->len); + } + + /* do sanity check */ + if ((offset > p_data->p_pkt->len) || ((pad_len + offset) > p_data->p_pkt->len)) { + AVDT_TRACE_WARNING("Got bad media packet"); + osi_free(p_data->p_pkt); + p_data->p_pkt = NULL; + } + /* adjust offset and length and send it up */ + else { + p_data->p_pkt->len -= (offset + pad_len); + p_data->p_pkt->offset += offset; + + if (p_scb->cs.p_data_cback != NULL) { + /* report sequence number */ + p_data->p_pkt->layer_specific = seq; + (*p_scb->cs.p_data_cback)(avdt_scb_to_hdl(p_scb), p_data->p_pkt, + time_stamp, (UINT8)(m_pt | (marker << 7))); + } else { +#if AVDT_MULTIPLEXING == TRUE + if ((p_scb->cs.p_media_cback != NULL) + && (p_scb->p_media_buf != NULL) + && (p_scb->media_buf_len > p_data->p_pkt->len)) { + /* media buffer enough length is assigned by application. Lets use it*/ + memcpy(p_scb->p_media_buf, (UINT8 *)(p_data->p_pkt + 1) + p_data->p_pkt->offset, + p_data->p_pkt->len); + (*p_scb->cs.p_media_cback)(avdt_scb_to_hdl(p_scb), p_scb->p_media_buf, + p_scb->media_buf_len, time_stamp, seq, m_pt, marker); + } +#endif + osi_free(p_data->p_pkt); + p_data->p_pkt = NULL; + } + } +} + +#if AVDT_REPORTING == TRUE +/******************************************************************************* +** +** Function avdt_scb_hdl_report +** +** Description +** +** Returns Nothing. +** +*******************************************************************************/ +UINT8 *avdt_scb_hdl_report(tAVDT_SCB *p_scb, UINT8 *p, UINT16 len) +{ + UINT16 result = AVDT_SUCCESS; + UINT8 *p_start = p; + UINT32 ssrc; + UINT8 o_v, o_p, o_cc; + AVDT_REPORT_TYPE pt; + tAVDT_REPORT_DATA report, *p_rpt; + + AVDT_TRACE_DEBUG( "avdt_scb_hdl_report"); + if (p_scb->cs.p_report_cback) { + p_rpt = &report; + /* parse report packet header */ + AVDT_MSG_PRS_RPT_OCTET1(p, o_v, o_p, o_cc); + pt = *p++; + p += 2; + BE_STREAM_TO_UINT32(ssrc, p); + + UNUSED(ssrc); + UNUSED(o_p); + UNUSED(o_v); + UNUSED(o_cc); + + switch (pt) { + case AVDT_RTCP_PT_SR: /* the packet type - SR (Sender Report) */ + BE_STREAM_TO_UINT32(report.sr.ntp_sec, p); + BE_STREAM_TO_UINT32(report.sr.ntp_frac, p); + BE_STREAM_TO_UINT32(report.sr.rtp_time, p); + BE_STREAM_TO_UINT32(report.sr.pkt_count, p); + BE_STREAM_TO_UINT32(report.sr.octet_count, p); + break; + + case AVDT_RTCP_PT_RR: /* the packet type - RR (Receiver Report) */ + report.rr.frag_lost = *p; + BE_STREAM_TO_UINT32(report.rr.packet_lost, p); + report.rr.packet_lost &= 0xFFFFFF; + BE_STREAM_TO_UINT32(report.rr.seq_num_rcvd, p); + BE_STREAM_TO_UINT32(report.rr.jitter, p); + BE_STREAM_TO_UINT32(report.rr.lsr, p); + BE_STREAM_TO_UINT32(report.rr.dlsr, p); + break; + + case AVDT_RTCP_PT_SDES: /* the packet type - SDES (Source Description) */ + if (*p == AVDT_RTCP_SDES_CNAME) { + p_rpt = (tAVDT_REPORT_DATA *)(p + 2); + } else { + AVDT_TRACE_WARNING( " - SDES SSRC=0x%08x sc=%d %d len=%d %s\n", + ssrc, o_cc, *p, *(p + 1), p + 2); + result = AVDT_BUSY; + } + break; + + default: + AVDT_TRACE_ERROR( "Bad Report pkt - packet type: %d\n", pt); + result = AVDT_BAD_PARAMS; + } + + if (result == AVDT_SUCCESS) { + (*p_scb->cs.p_report_cback)(avdt_scb_to_hdl(p_scb), pt, p_rpt); + } + + } + p_start += len; + return p_start; +} +#endif + +#if AVDT_MULTIPLEXING == TRUE +/******************************************************************************* +** +** Function avdt_scb_hdl_pkt_frag +** +** Description +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_hdl_pkt_frag(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + /* Fields of Adaptation Layer Header */ + UINT8 al_tsid, al_frag, al_lcode; + UINT16 al_len; + /* media header fields */ + UINT8 o_v, o_p, o_x, o_cc; + UINT8 m_pt; + UINT8 marker; + UINT16 seq; + UINT32 time_stamp; + UINT32 ssrc; + UINT16 ex_len; + UINT8 pad_len; + /* other variables */ + UINT8 *p; /* current pointer */ + UINT8 *p_end; /* end of all packet */ + UINT8 *p_payload; /* pointer to media fragment payload in the buffer */ + UINT32 payload_len; /* payload length */ + UINT16 frag_len; /* fragment length */ + + p = (UINT8 *)(p_data->p_pkt + 1) + p_data->p_pkt->offset; + p_end = p + p_data->p_pkt->len; + /* parse all fragments */ + while (p < p_end) { + if (p_end - p < 4) { /* length check. maximum length of AL header = 4 */ + AVDT_TRACE_WARNING("p_end: %p - p:%p < 4\n", p_end, p); + break; + } + + /* parse first byte */ + al_tsid = (*p) >> 3; + al_frag = ( (*p) >> 2 ) & 0x01; + al_lcode = (*p++) & AVDT_ALH_LCODE_MASK; + + /* in case of TSID=00000, a second AL header byte, before the length field, + ** is expected and contains the actual TSID, aligned with MSB */ + if (al_tsid == 0) { + al_tsid = *p++; + } + + /* get remaining media length on base of lcode */ + switch (al_lcode) { + case AVDT_ALH_LCODE_NONE: /* No length field present. Take length from l2cap */ + al_len = (UINT16)(p_end - p); + break; + case AVDT_ALH_LCODE_16BIT: /* 16 bit length field */ + BE_STREAM_TO_UINT16(al_len, p); + break; + case AVDT_ALH_LCODE_9BITM0: /* 9 bit length field, MSB = 0, 8 LSBs in 1 octet following */ + al_len = *p++; + break; + default: /* 9 bit length field, MSB = 1, 8 LSBs in 1 octet following */ + al_len = (UINT16) * p++ + 0x100; + } + + /* max fragment length */ + frag_len = (UINT16)(p_end - p); + /* if it isn't last fragment */ + if (frag_len >= al_len) { + frag_len = al_len; + } + + /* check TSID corresponds to config */ + if (al_tsid != p_scb->curr_cfg.mux_tsid_media) { +#if AVDT_REPORTING == TRUE + if ((p_scb->curr_cfg.psc_mask & AVDT_PSC_REPORT) && + (al_tsid == p_scb->curr_cfg.mux_tsid_report)) { + /* parse reporting packet */ + p = avdt_scb_hdl_report(p_scb, p, frag_len); + continue; + } else +#endif + { + AVDT_TRACE_WARNING("bad tsid: %d, mux_tsid_media:%d\n", al_tsid, p_scb->curr_cfg.mux_tsid_media); + break; + } + } + /* check are buffer for assembling and related callback set */ + else if ((p_scb->p_media_buf == NULL) || (p_scb->cs.p_media_cback == NULL)) { + AVDT_TRACE_WARNING("NULL p_media_buf or p_media_cback"); + break; + } + + + /* it is media fragment beginning */ + if (!al_frag) { /* is it first fragment of original media packet */ + AVDT_TRACE_DEBUG("al:%d media:%d\n", + al_len, p_scb->media_buf_len); + + p_scb->frag_off = 0; + p_scb->frag_org_len = al_len; /* total length of original media packet */ + /* length check: minimum length of media header is 12 */ + if (p_scb->frag_org_len < 12) { + AVDT_TRACE_WARNING("bad al_len: %d(<12)\n", al_len); + break; + } + /* check that data fit into buffer */ + if (al_len > p_scb->media_buf_len) { + AVDT_TRACE_WARNING("bad al_len: %d(>%d)\n", al_len, p_scb->media_buf_len); + break; + } + /* make sure it is the last fragment in l2cap packet */ + if (p + al_len < p_end) { + AVDT_TRACE_WARNING("bad al_len: %d(>%d)\n", al_len, p_scb->media_buf_len); + break; + } + } else { + AVDT_TRACE_DEBUG("al:%d media:%d frag_org_len:%d frag_off:%d\n", + al_len, p_scb->media_buf_len, p_scb->frag_org_len, p_scb->frag_off); + + /* check that remaining length from AL header equals to original len - length of already received fragments */ + if (al_len != p_scb->frag_org_len - p_scb->frag_off) { + AVDT_TRACE_WARNING("al_len:%d != (frag_org_len:%d - frag_off:%d) %d\n", + al_len, p_scb->frag_org_len, p_scb->frag_off, + (p_scb->frag_org_len - p_scb->frag_off)); + break; + } + + /* do sanity check */ + if (p_scb->frag_off == 0) { + AVDT_TRACE_WARNING("frag_off=0"); + break; + } + } + /* do common sanity check */ + if ((p_scb->frag_org_len <= p_scb->frag_off) || (p_scb->frag_org_len >= p_scb->media_buf_len)) { + AVDT_TRACE_WARNING("common sanity frag_off:%d frag_org_len:%d media_buf_len:%d\n", + p_scb->frag_off, p_scb->frag_org_len, p_scb->media_buf_len); + break; + } + + AVDT_TRACE_DEBUG("Received fragment org_len=%d off=%d al_len=%d frag_len=%d\n", + p_scb->frag_org_len, p_scb->frag_off, al_len, frag_len); + + /* copy fragment into buffer */ + memcpy(p_scb->p_media_buf + p_scb->frag_off, p, frag_len); + p_scb->frag_off += frag_len; + /* move to the next fragment */ + p += frag_len; + /* if it is last fragment in original media packet then process total media pocket */ + if (p_scb->frag_off == p_scb->frag_org_len) { + p_payload = p_scb->p_media_buf; + + /* media header */ + AVDT_MSG_PRS_OCTET1(p_payload, o_v, o_p, o_x, o_cc); + AVDT_MSG_PRS_M_PT(p_payload, m_pt, marker); + BE_STREAM_TO_UINT16(seq, p_payload); + BE_STREAM_TO_UINT32(time_stamp, p_payload); + BE_STREAM_TO_UINT32(ssrc, p_payload); + + UNUSED(o_v); + UNUSED(ssrc); + + /* skip over any csrc's in packet */ + p_payload += o_cc * 4; + + /* check for and skip over extension header */ + if (o_x) { + if (p_scb->p_media_buf + p_scb->frag_off - p_payload < 4) { + AVDT_TRACE_WARNING("length check frag_off:%d p_media_buf:%p p_payload:%p\n", + p_scb->frag_off, p_scb->p_media_buf, p_payload); + break;/* length check */ + } + p_payload += 2; + BE_STREAM_TO_UINT16(ex_len, p_payload); + p_payload += ex_len * 4; + } + + if (p_payload >= p_scb->p_media_buf + p_scb->frag_off) { + AVDT_TRACE_WARNING("length check2 frag_off:%d p_media_buf:%p p_payload:%p\n", + p_scb->frag_off, p_scb->p_media_buf, p_payload); + break;/* length check */ + } + + /* adjust length for any padding at end of packet */ + if (o_p) { + /* padding length in last byte of packet */ + pad_len = *(p_scb->p_media_buf + p_scb->frag_off - 1); + } else { + pad_len = 0; + } + /* payload length */ + payload_len = (UINT32)(p_scb->p_media_buf + p_scb->frag_off - pad_len - p_payload); + + AVDT_TRACE_DEBUG("Received last fragment header=%d len=%d\n", + p_payload - p_scb->p_media_buf, payload_len); + + /* send total media packet up */ + if (p_scb->cs.p_media_cback != NULL) { + (*p_scb->cs.p_media_cback)(avdt_scb_to_hdl(p_scb), p_payload, + payload_len, time_stamp, seq, m_pt, marker); + } + } + } /* while(p < p_end) */ + + if (p < p_end) { + AVDT_TRACE_WARNING("*** Got bad media packet"); + } + osi_free(p_data->p_pkt); + p_data->p_pkt = NULL; +} +#endif + +/******************************************************************************* +** +** Function avdt_scb_hdl_pkt +** +** Description +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_hdl_pkt(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ +#if AVDT_REPORTING == TRUE + UINT8 *p; +#endif + +#if AVDT_MULTIPLEXING == TRUE + /* select right function in dependance of is fragmentation supported or not */ + if ( 0 != (p_scb->curr_cfg.psc_mask & AVDT_PSC_MUX)) { + avdt_scb_hdl_pkt_frag(p_scb, p_data); + } else +#endif + { +#if AVDT_REPORTING == TRUE + if (p_data->p_pkt->layer_specific == AVDT_CHAN_REPORT) { + p = (UINT8 *)(p_data->p_pkt + 1) + p_data->p_pkt->offset; + avdt_scb_hdl_report(p_scb, p, p_data->p_pkt->len); + osi_free(p_data->p_pkt); + p_data->p_pkt = NULL; + } else +#endif + { + avdt_scb_hdl_pkt_no_frag(p_scb, p_data); + } + } +} + +/******************************************************************************* +** +** Function avdt_scb_drop_pkt +** +** Description Drop an incoming media packet. This function is called if +** a media packet is received in any state besides streaming. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_drop_pkt(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + UNUSED(p_scb); + + AVDT_TRACE_ERROR("%s dropped incoming media packet", __func__); + osi_free(p_data->p_pkt); + p_data->p_pkt = NULL; +} + +/******************************************************************************* +** +** Function avdt_scb_hdl_reconfig_cmd +** +** Description This function calls the application callback function +** with a reconfiguration indication. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_hdl_reconfig_cmd(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + /* if command not supported */ + if (p_scb->cs.nsc_mask & AVDT_NSC_RECONFIG) { + /* send reject */ + p_data->msg.hdr.err_code = AVDT_ERR_NSC; + p_data->msg.hdr.err_param = 0; + avdt_scb_event(p_scb, AVDT_SCB_API_RECONFIG_RSP_EVT, p_data); + } else { + /* store requested configuration */ + memcpy(&p_scb->req_cfg, p_data->msg.reconfig_cmd.p_cfg, sizeof(tAVDT_CFG)); + + /* call application callback */ + (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb), + NULL, + AVDT_RECONFIG_IND_EVT, + (tAVDT_CTRL *) &p_data->msg.reconfig_cmd); + } +} + +/******************************************************************************* +** +** Function avdt_scb_hdl_reconfig_rsp +** +** Description This function calls the application callback function +** with a reconfiguration confirm. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_hdl_reconfig_rsp(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + if (p_data->msg.hdr.err_code == 0) { + /* store new configuration */ + if (p_scb->req_cfg.num_codec > 0) { + p_scb->curr_cfg.num_codec = p_scb->req_cfg.num_codec; + memcpy(p_scb->curr_cfg.codec_info, p_scb->req_cfg.codec_info, AVDT_CODEC_SIZE); + } + if (p_scb->req_cfg.num_protect > 0) { + p_scb->curr_cfg.num_protect = p_scb->req_cfg.num_protect; + memcpy(p_scb->curr_cfg.protect_info, p_scb->req_cfg.protect_info, AVDT_PROTECT_SIZE); + } + } + + p_data->msg.svccap.p_cfg = &p_scb->curr_cfg; + + /* call application callback */ + (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb), + NULL, + AVDT_RECONFIG_CFM_EVT, + (tAVDT_CTRL *) &p_data->msg.svccap); +} + +/******************************************************************************* +** +** Function avdt_scb_hdl_security_cmd +** +** Description This function calls the application callback with a +** security indication. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_hdl_security_cmd(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + /* if command not supported */ + if (p_scb->cs.nsc_mask & AVDT_NSC_SECURITY) { + /* send reject */ + p_data->msg.hdr.err_code = AVDT_ERR_NSC; + avdt_scb_event(p_scb, AVDT_SCB_API_SECURITY_RSP_EVT, p_data); + } else { + /* call application callback */ + (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb), + NULL, + AVDT_SECURITY_IND_EVT, + (tAVDT_CTRL *) &p_data->msg.security_cmd); + } +} + +/******************************************************************************* +** +** Function avdt_scb_hdl_security_rsp +** +** Description This function calls the application callback with a +** security confirm. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_hdl_security_rsp(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + /* call application callback */ + (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb), + NULL, + AVDT_SECURITY_CFM_EVT, + (tAVDT_CTRL *) &p_data->msg.security_cmd); +} + +/******************************************************************************* +** +** Function avdt_scb_hdl_setconfig_cmd +** +** Description This function marks the SCB as in use and copies the +** configuration and peer SEID to the SCB. It then calls +** the application callback with a configuration indication. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_hdl_setconfig_cmd(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + tAVDT_CFG *p_cfg; + + if (!p_scb->in_use) { + p_cfg = p_data->msg.config_cmd.p_cfg; + /* set sep as in use */ + p_scb->in_use = TRUE; + + /* copy info to scb */ + p_scb->p_ccb = avdt_ccb_by_idx(p_data->msg.config_cmd.hdr.ccb_idx); + p_scb->peer_seid = p_data->msg.config_cmd.int_seid; + memcpy(&p_scb->req_cfg, p_cfg, sizeof(tAVDT_CFG)); + /* call app callback */ + (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb), /* handle of scb- which is same as sep handle of bta_av_cb.p_scb*/ + p_scb->p_ccb ? p_scb->p_ccb->peer_addr : NULL, + AVDT_CONFIG_IND_EVT, + (tAVDT_CTRL *) &p_data->msg.config_cmd); + } else { + avdt_scb_rej_in_use(p_scb, p_data); + } +} + +/******************************************************************************* +** +** Function avdt_scb_hdl_setconfig_rej +** +** Description This function marks the SCB as not in use and calls the +** application callback with an open confirm indicating failure. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_hdl_setconfig_rej(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + /* clear scb variables */ + avdt_scb_clr_vars(p_scb, p_data); + + /* tell ccb we're done with signaling channel */ + avdt_ccb_event(avdt_ccb_by_idx(p_data->msg.hdr.ccb_idx), AVDT_CCB_UL_CLOSE_EVT, NULL); + + /* call application callback */ + (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb), + NULL, + AVDT_OPEN_CFM_EVT, + (tAVDT_CTRL *) &p_data->msg.hdr); +} + +/******************************************************************************* +** +** Function avdt_scb_send_delay_report_cmd +** +** Description This function is to initiate the delay reporting command. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_send_delay_report_cmd(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + UINT16 delay_value; + UNUSED(p_data); + + if ((p_scb->cs.tsep == AVDT_TSEP_SNK) && (p_scb->curr_cfg.psc_mask & AVDT_PSC_DELAY_RPT)) { + delay_value = AVDT_GetDelayValue(); + AVDT_DelayReport(avdt_scb_to_hdl(p_scb), p_scb->peer_seid, delay_value); + } +} + +/******************************************************************************* +** +** Function avdt_scb_init_open_req +** +** Description This function sends the SCB an AVDT_SCB_API_OPEN_REQ_EVT +** to initiate sending of an open command message. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_init_open_req(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + tAVDT_EVT_HDR single; + UNUSED(p_data); + + if (p_scb->p_ccb != NULL && p_scb->role == AVDT_CONF_INT) { + if (!(p_scb->curr_cfg.psc_mask & AVDT_PSC_DELAY_RPT)) { + /* initiate open */ + single.seid = p_scb->peer_seid; + avdt_scb_event(p_scb, AVDT_SCB_API_OPEN_REQ_EVT, (tAVDT_SCB_EVT *) &single); + } else { + btu_start_timer(&p_scb->timer_entry, BTU_TTYPE_AVDT_SCB_DELAY_RPT, AVDT_SCB_TC_DELAY_RPT_TOUT); + } + } +} + +/******************************************************************************* +** +** Function avdt_scb_hdl_setconfig_rsp +** +** Description This function sends the SCB an AVDT_SCB_API_OPEN_REQ_EVT +** to initiate sending of an open command message. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_hdl_setconfig_rsp(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + UNUSED(p_data); + + if (p_scb->p_ccb != NULL) { + /* save configuration */ + memcpy(&p_scb->curr_cfg, &p_scb->req_cfg, sizeof(tAVDT_CFG)); + p_scb->role = AVDT_CONF_INT; + + /* send delay reporting command */ + avdt_scb_send_delay_report_cmd(p_scb, p_data); + } +} + +/******************************************************************************* +** +** Function avdt_scb_hdl_start_cmd +** +** Description This function calls the application callback with a +** start indication. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_hdl_start_cmd(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + UNUSED(p_data); + + (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb), + p_scb->p_ccb ? p_scb->p_ccb->peer_addr : NULL, + AVDT_START_IND_EVT, + NULL); +} + +/******************************************************************************* +** +** Function avdt_scb_hdl_start_rsp +** +** Description This function calls the application callback with a +** start confirm. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_hdl_start_rsp(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb), + p_scb->p_ccb ? p_scb->p_ccb->peer_addr : NULL, + AVDT_START_CFM_EVT, + (tAVDT_CTRL *) &p_data->msg.hdr); +} + +/******************************************************************************* +** +** Function avdt_scb_hdl_suspend_cmd +** +** Description This function calls the application callback with a suspend +** indication. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_hdl_suspend_cmd(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + UNUSED(p_data); + + (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb), + p_scb->p_ccb ? p_scb->p_ccb->peer_addr : NULL, + AVDT_SUSPEND_IND_EVT, + NULL); +} + +/******************************************************************************* +** +** Function avdt_scb_hdl_suspend_rsp +** +** Description This function calls the application callback with a suspend +** confirm. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_hdl_suspend_rsp(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb), + p_scb->p_ccb ? p_scb->p_ccb->peer_addr : NULL, + AVDT_SUSPEND_CFM_EVT, + (tAVDT_CTRL *) &p_data->msg.hdr); +} + +/******************************************************************************* +** +** Function avdt_scb_hdl_tc_close +** +** Description This function is called when the transport channel is +** closed. It marks the SCB as not in use and +** initializes certain SCB parameters. It then sends +** an AVDT_CCB_UL_CLOSE_EVT to the CCB if the SCB +** initiated the close. It then checks to see if the SCB +** is to be removed. If it is it deallocates the SCB. Finally, +** it calls the application callback with a close indication. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_hdl_tc_close(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + UINT8 hdl = avdt_scb_to_hdl(p_scb); + tAVDT_CTRL_CBACK *p_ctrl_cback = p_scb->cs.p_ctrl_cback; + tAVDT_CTRL avdt_ctrl; + UINT8 event; + tAVDT_CCB *p_ccb = p_scb->p_ccb; + BD_ADDR remote_addr; + + + memcpy (remote_addr, p_ccb->peer_addr, BD_ADDR_LEN); + + /* set up hdr */ + avdt_ctrl.hdr.err_code = p_scb->close_code; + if (p_data) { + avdt_ctrl.hdr.err_param = p_data->close.disc_rsn; + } else { + avdt_ctrl.hdr.err_param = AVDT_DISC_RSN_NORMAL; + } + + /* clear sep variables */ + avdt_scb_clr_vars(p_scb, p_data); + p_scb->media_seq = 0; + p_scb->cong = FALSE; + + /* free pkt we're holding, if any */ + if (p_scb->p_pkt != NULL) { + osi_free(p_scb->p_pkt); + p_scb->p_pkt = NULL; + } + + /* stop transport channel timer */ + btu_stop_timer(&p_scb->timer_entry); + + if ((p_scb->role == AVDT_CLOSE_INT) || (p_scb->role == AVDT_OPEN_INT)) { + /* tell ccb we're done with signaling channel */ + avdt_ccb_event(p_ccb, AVDT_CCB_UL_CLOSE_EVT, NULL); + } + event = (p_scb->role == AVDT_CLOSE_INT) ? AVDT_CLOSE_CFM_EVT : AVDT_CLOSE_IND_EVT; + p_scb->role = AVDT_CLOSE_ACP; + + if (p_scb->remove) { + avdt_scb_dealloc(p_scb, NULL); + } + + /* call app callback */ + (*p_ctrl_cback)(hdl, remote_addr, event, &avdt_ctrl); +} + +/******************************************************************************* +** +** Function avdt_scb_snd_delay_rpt_req +** +** Description This function calls the application callback with a delay +** report. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_snd_delay_rpt_req (tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + avdt_msg_send_cmd(p_scb->p_ccb, p_scb, AVDT_SIG_DELAY_RPT, (tAVDT_MSG *) &p_data->apidelay); +} + +/******************************************************************************* +** +** Function avdt_scb_hdl_delay_rpt_cmd +** +** Description This function calls the application callback with a delay +** report. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_hdl_delay_rpt_cmd (tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + tAVDT_EVT_HDR single; + + (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb), + p_scb->p_ccb ? p_scb->p_ccb->peer_addr : NULL, + AVDT_DELAY_REPORT_EVT, + (tAVDT_CTRL *) &p_data->msg.hdr); + + if (p_scb->p_ccb) { + if (p_scb->cs.cfg.psc_mask & AVDT_PSC_DELAY_RPT) { + avdt_msg_send_rsp(p_scb->p_ccb, AVDT_SIG_DELAY_RPT, &p_data->msg); + if(p_scb->role == AVDT_CONF_INT) { + btu_stop_timer(&p_scb->timer_entry); + /* initiate open */ + single.seid = p_scb->peer_seid; + avdt_scb_event(p_scb, AVDT_SCB_API_OPEN_REQ_EVT, (tAVDT_SCB_EVT *) &single); + } + } else { + p_data->msg.hdr.err_code = AVDT_ERR_NSC; + avdt_msg_send_rej(p_scb->p_ccb, AVDT_SIG_DELAY_RPT, &p_data->msg); + } + } else { + avdt_scb_rej_not_in_use(p_scb, p_data); + } +} + +/******************************************************************************* +** +** Function avdt_scb_hdl_delay_rpt_rsp +** +** Description This function calls the application callback with a delay +** report. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_hdl_delay_rpt_rsp (tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + tAVDT_EVT_HDR single; + + if ((p_scb->cs.tsep == AVDT_TSEP_SNK) && + (p_scb->state == AVDT_SCB_CONF_ST) && (p_scb->role == AVDT_CONF_INT)) { + btu_stop_timer(&p_scb->timer_entry); + /* initiate open */ + single.seid = p_scb->peer_seid; + avdt_scb_event(p_scb, AVDT_SCB_API_OPEN_REQ_EVT, (tAVDT_SCB_EVT *) &single); + } + + (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb), + p_scb->p_ccb ? p_scb->p_ccb->peer_addr : NULL, + AVDT_DELAY_REPORT_CFM_EVT, + (tAVDT_CTRL *) &p_data->msg.hdr); +} + +#if AVDT_REPORTING == TRUE +/******************************************************************************* +** +** Function avdt_scb_hdl_tc_close_sto +** +** Description This function is called when a channel is closed in OPEN +** state. Check the channel type and process accordingly. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_hdl_tc_close_sto(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + tAVDT_CTRL avdt_ctrl; + /* AVDT_CHAN_SIG does not visit this action */ + if (p_data && p_data->close.type != AVDT_CHAN_MEDIA) { + /* it's reporting or recovery channel, + * the channel close in open state means the peer does not support it */ + if (p_data->close.old_tc_state == AVDT_AD_ST_OPEN) { + avdt_ctrl.hdr.err_code = 0; + avdt_ctrl.hdr.err_param = 0; + /* call app callback */ + (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb), + p_scb->p_ccb ? p_scb->p_ccb->peer_addr : NULL, + AVDT_REPORT_DISCONN_EVT, &avdt_ctrl); + } + } else { + /* must be in OPEN state. need to go back to idle */ + avdt_scb_event(p_scb, AVDT_SCB_MSG_ABORT_RSP_EVT, NULL); + avdt_scb_hdl_tc_close(p_scb, p_data); + } +} +#endif + +/******************************************************************************* + * + * Function avdt_scb_hdl_delay_rpt_tout + * + * Description The timer triggers the sending of AVDT open_req. + * This function is theoretically not called. + * + * Returns Nothing. + * + ******************************************************************************/ +void avdt_scb_hdl_delay_rpt_tout(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + tAVDT_EVT_HDR single; + UNUSED(p_data); + + /* initiate open */ + single.seid = p_scb->peer_seid; + avdt_scb_event(p_scb, AVDT_SCB_API_OPEN_REQ_EVT, (tAVDT_SCB_EVT *) &single); +} + +/******************************************************************************* +** +** Function avdt_scb_hdl_tc_open +** +** Description This function is called when the transport channel is +** opened while in the opening state. It calls the +** application callback with an open indication or open +** confirm depending on who initiated the open procedure. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_hdl_tc_open(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + UINT8 event; +#if AVDT_REPORTING == TRUE + UINT8 role; +#endif + + /* stop transport channel connect timer */ + btu_stop_timer(&p_scb->timer_entry); + + event = (p_scb->role == AVDT_OPEN_INT) ? AVDT_OPEN_CFM_EVT : AVDT_OPEN_IND_EVT; + p_data->open.hdr.err_code = 0; + + AVDT_TRACE_DEBUG("psc_mask: cfg: 0x%x, req:0x%x, cur: 0x%x\n", + p_scb->cs.cfg.psc_mask, p_scb->req_cfg.psc_mask, p_scb->curr_cfg.psc_mask); +#if AVDT_REPORTING == TRUE + if (p_scb->curr_cfg.psc_mask & AVDT_PSC_REPORT) { + /* open the reporting channel, if both devices support it */ + role = (p_scb->role == AVDT_OPEN_INT) ? AVDT_INT : AVDT_ACP; + avdt_ad_open_req(AVDT_CHAN_REPORT, p_scb->p_ccb, p_scb, role); + } +#endif + + /* call app callback */ + (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb), + p_scb->p_ccb ? p_scb->p_ccb->peer_addr : NULL, + event, + (tAVDT_CTRL *) &p_data->open); +} + +#if AVDT_REPORTING == TRUE +/******************************************************************************* +** +** Function avdt_scb_hdl_tc_open_sto +** +** Description This function is called when the transport channel is +** opened while in the opening state. It calls the +** application callback with an open indication or open +** confirm depending on who initiated the open procedure. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_hdl_tc_open_sto(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + tAVDT_CTRL avdt_ctrl; + /* open reporting channel here, when it is implemented */ + + /* call app callback */ + if (p_data->open.hdr.err_code == AVDT_CHAN_REPORT) { + avdt_ctrl.hdr.err_code = 0; + avdt_ctrl.hdr.err_param = 1; + (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb), + p_scb->p_ccb ? p_scb->p_ccb->peer_addr : NULL, + AVDT_REPORT_CONN_EVT, &avdt_ctrl); + } +} +#endif + +/******************************************************************************* +** +** Function avdt_scb_hdl_write_req_no_frag +** +** Description This function frees the media packet currently stored in +** the SCB, if any. Then it builds a new media packet from +** with the passed in buffer and stores it in the SCB. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_hdl_write_req_no_frag(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + UINT8 *p; + UINT32 ssrc; + + /* free packet we're holding, if any; to be replaced with new */ + if (p_scb->p_pkt != NULL) { + osi_free(p_scb->p_pkt); + p_scb->p_pkt = NULL; + + /* this shouldn't be happening */ + AVDT_TRACE_WARNING("Dropped media packet; congested"); + } + + /* build a media packet */ + /* Add RTP header if required */ + if ( !(p_data->apiwrite.opt & AVDT_DATA_OPT_NO_RTP) ) { + ssrc = avdt_scb_gen_ssrc(p_scb); + + p_data->apiwrite.p_buf->len += AVDT_MEDIA_HDR_SIZE; + p_data->apiwrite.p_buf->offset -= AVDT_MEDIA_HDR_SIZE; + p_scb->media_seq++; + p = (UINT8 *)(p_data->apiwrite.p_buf + 1) + p_data->apiwrite.p_buf->offset; + + UINT8_TO_BE_STREAM(p, AVDT_MEDIA_OCTET1); + UINT8_TO_BE_STREAM(p, p_data->apiwrite.m_pt); + UINT16_TO_BE_STREAM(p, p_scb->media_seq); + UINT32_TO_BE_STREAM(p, p_data->apiwrite.time_stamp); + UINT32_TO_BE_STREAM(p, ssrc); + } + + /* store it */ + p_scb->p_pkt = p_data->apiwrite.p_buf; +} + +#if AVDT_MULTIPLEXING == TRUE +/******************************************************************************* +** +** Function avdt_scb_hdl_write_req_frag +** +** Description This function builds a new fragments of media packet from +** the passed in buffers and stores them in the SCB. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_hdl_write_req_frag(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + UINT8 *p; + UINT32 ssrc; + + /* free fragments we're holding, if any; it shouldn't happen */ + if (!fixed_queue_is_empty(p_scb->frag_q)) + { + /* this shouldn't be happening */ + AVDT_TRACE_WARNING("*** Dropped media packet; congested"); + BT_HDR *p_frag; + while ((p_frag = (BT_HDR*)fixed_queue_dequeue(p_scb->frag_q, 0)) != NULL) + osi_free(p_frag); + } + + /* build a media fragments */ + p_scb->frag_off = p_data->apiwrite.data_len; + p_scb->p_next_frag = p_data->apiwrite.p_data; + + ssrc = avdt_scb_gen_ssrc(p_scb); + + if (! fixed_queue_is_empty(p_scb->frag_q)) { + list_t *list = fixed_queue_get_list(p_scb->frag_q); + const list_node_t *node = list_begin(list); + if (node != list_end(list)) { + BT_HDR *p_frag = (BT_HDR *)list_node(node); + node = list_next(node); + + /* get first packet */ + /* posit on Adaptation Layer header */ + p_frag->len += AVDT_AL_HDR_SIZE + AVDT_MEDIA_HDR_SIZE; + p_frag->offset -= AVDT_AL_HDR_SIZE + AVDT_MEDIA_HDR_SIZE; + p = (UINT8 *)(p_frag + 1) + p_frag->offset; + + /* Adaptation Layer header */ + /* TSID, no-fragment bit and coding of length (in 2 length octets + * following) + */ + *p++ = (p_scb->curr_cfg.mux_tsid_media<<3) | AVDT_ALH_LCODE_16BIT; + + /* length of all remaining transport packet */ + UINT16_TO_BE_STREAM(p, p_frag->layer_specific + AVDT_MEDIA_HDR_SIZE ); + /* media header */ + UINT8_TO_BE_STREAM(p, AVDT_MEDIA_OCTET1); + UINT8_TO_BE_STREAM(p, p_data->apiwrite.m_pt); + UINT16_TO_BE_STREAM(p, p_scb->media_seq); + UINT32_TO_BE_STREAM(p, p_data->apiwrite.time_stamp); + UINT32_TO_BE_STREAM(p, ssrc); + p_scb->media_seq++; + } + + for ( ; node != list_end(list); node = list_next(node)) { + BT_HDR *p_frag = (BT_HDR *)list_node(node); + + /* posit on Adaptation Layer header */ + p_frag->len += AVDT_AL_HDR_SIZE; + p_frag->offset -= AVDT_AL_HDR_SIZE; + p = (UINT8 *)(p_frag + 1) + p_frag->offset; + /* Adaptation Layer header */ + /* TSID, fragment bit and coding of length (in 2 length octets + * following) + */ + *p++ = (p_scb->curr_cfg.mux_tsid_media << 3) | + (AVDT_ALH_FRAG_MASK | AVDT_ALH_LCODE_16BIT); + + /* length of all remaining transport packet */ + UINT16_TO_BE_STREAM(p, p_frag->layer_specific); + } + } +} +#endif + + +/******************************************************************************* +** +** Function avdt_scb_hdl_write_req +** +** Description This function calls one of the two versions of building functions +** for case with and without fragmentation +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_hdl_write_req(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ +#if AVDT_MULTIPLEXING == TRUE + if (fixed_queue_is_empty(p_scb->frag_q)) +#endif + { + avdt_scb_hdl_write_req_no_frag(p_scb, p_data); + } +#if AVDT_MULTIPLEXING == TRUE + else { + avdt_scb_hdl_write_req_frag(p_scb, p_data); + } +#endif +} + +/******************************************************************************* +** +** Function avdt_scb_snd_abort_req +** +** Description This function sends an abort command message. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_snd_abort_req(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + tAVDT_EVT_HDR hdr; + UNUSED(p_data); + + if (p_scb->p_ccb != NULL) { + p_scb->role = AVDT_CLOSE_INT; + + hdr.seid = p_scb->peer_seid; + + avdt_msg_send_cmd(p_scb->p_ccb, p_scb, AVDT_SIG_ABORT, (tAVDT_MSG *) &hdr); + } +} + +/******************************************************************************* +** +** Function avdt_scb_snd_abort_rsp +** +** Description This function sends an abort response message. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_snd_abort_rsp(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + UNUSED(p_scb); + + avdt_msg_send_rsp(avdt_ccb_by_idx(p_data->msg.hdr.ccb_idx), AVDT_SIG_ABORT, + &p_data->msg); +} + +/******************************************************************************* +** +** Function avdt_scb_snd_close_req +** +** Description This function sends a close command message. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_snd_close_req(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + tAVDT_EVT_HDR hdr; + UNUSED(p_data); + + p_scb->role = AVDT_CLOSE_INT; + + hdr.seid = p_scb->peer_seid; + + avdt_msg_send_cmd(p_scb->p_ccb, p_scb, AVDT_SIG_CLOSE, (tAVDT_MSG *) &hdr); +} + +/******************************************************************************* +** +** Function avdt_scb_snd_stream_close +** +** Description This function sends a close command message. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_snd_stream_close(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ +#if AVDT_MULTIPLEXING == TRUE + AVDT_TRACE_WARNING("%s c:%d, off:%d", __func__, + fixed_queue_length(p_scb->frag_q), p_scb->frag_off); + + /* clean fragments queue */ + BT_HDR *p_frag; + while ((p_frag = (BT_HDR*)fixed_queue_dequeue(p_scb->frag_q, 0)) != NULL) { + osi_free(p_frag); + } + p_scb->frag_off = 0; +#endif + if (p_scb->p_pkt) { + osi_free(p_scb->p_pkt); + p_scb->p_pkt = NULL; + } + + avdt_scb_snd_close_req(p_scb, p_data); +} + +/******************************************************************************* +** +** Function avdt_scb_snd_close_rsp +** +** Description This function sends a close response message. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_snd_close_rsp(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + avdt_msg_send_rsp(p_scb->p_ccb, AVDT_SIG_CLOSE, &p_data->msg); +} + +/******************************************************************************* +** +** Function avdt_scb_snd_getconfig_req +** +** Description This function sends a get configuration command message. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_snd_getconfig_req(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + tAVDT_EVT_HDR hdr; + UNUSED(p_data); + + hdr.seid = p_scb->peer_seid; + + avdt_msg_send_cmd(p_scb->p_ccb, p_scb, AVDT_SIG_GETCONFIG, (tAVDT_MSG *) &hdr); +} + +/******************************************************************************* +** +** Function avdt_scb_snd_getconfig_rsp +** +** Description This function sends a get configuration response message. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_snd_getconfig_rsp(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + avdt_msg_send_rsp(p_scb->p_ccb, AVDT_SIG_GETCONFIG, &p_data->msg); +} + +/******************************************************************************* +** +** Function avdt_scb_snd_open_req +** +** Description This function sends an open command message. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_snd_open_req(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + tAVDT_EVT_HDR hdr; + UNUSED(p_data); + + hdr.seid = p_scb->peer_seid; + + avdt_msg_send_cmd(p_scb->p_ccb, p_scb, AVDT_SIG_OPEN, (tAVDT_MSG *) &hdr); +} + +/******************************************************************************* +** +** Function avdt_scb_snd_open_rsp +** +** Description This function sends an open response message. It also +** calls avdt_ad_open_req() to accept a transport channel +** connection. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_snd_open_rsp(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + /* notify adaption that we're waiting for transport channel open */ + p_scb->role = AVDT_OPEN_ACP; + avdt_ad_open_req(AVDT_CHAN_MEDIA, p_scb->p_ccb, p_scb, AVDT_ACP); + + /* send response */ + avdt_msg_send_rsp(p_scb->p_ccb, AVDT_SIG_OPEN, &p_data->msg); + + /* start tc connect timer */ + btu_start_timer(&p_scb->timer_entry, BTU_TTYPE_AVDT_SCB_TC, AVDT_SCB_TC_CONN_TOUT); +} + +/******************************************************************************* +** +** Function avdt_scb_snd_reconfig_req +** +** Description This function stores the configuration parameters in the +** SCB and sends a reconfiguration command message. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_snd_reconfig_req(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + memcpy(&p_scb->req_cfg, p_data->msg.config_cmd.p_cfg, sizeof(tAVDT_CFG)); + p_data->msg.hdr.seid = p_scb->peer_seid; + avdt_msg_send_cmd(p_scb->p_ccb, p_scb, AVDT_SIG_RECONFIG, &p_data->msg); +} + +/******************************************************************************* +** +** Function avdt_scb_snd_reconfig_rsp +** +** Description This function stores the configuration parameters in the +** SCB and sends a reconfiguration response message. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_snd_reconfig_rsp(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + if (p_data->msg.hdr.err_code == 0) { + /* store new configuration */ + if (p_scb->req_cfg.num_codec > 0) { + p_scb->curr_cfg.num_codec = p_scb->req_cfg.num_codec; + memcpy(p_scb->curr_cfg.codec_info, p_scb->req_cfg.codec_info, AVDT_CODEC_SIZE); + } + if (p_scb->req_cfg.num_protect > 0) { + p_scb->curr_cfg.num_protect = p_scb->req_cfg.num_protect; + memcpy(p_scb->curr_cfg.protect_info, p_scb->req_cfg.protect_info, AVDT_PROTECT_SIZE); + } + + /* send response */ + avdt_msg_send_rsp(p_scb->p_ccb, AVDT_SIG_RECONFIG, &p_data->msg); + } else { + /* send reject */ + avdt_msg_send_rej(p_scb->p_ccb, AVDT_SIG_RECONFIG, &p_data->msg); + } +} + +/******************************************************************************* +** +** Function avdt_scb_snd_security_req +** +** Description This function sends a security command message. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_snd_security_req(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + p_data->msg.hdr.seid = p_scb->peer_seid; + avdt_msg_send_cmd(p_scb->p_ccb, p_scb, AVDT_SIG_SECURITY, &p_data->msg); +} + +/******************************************************************************* +** +** Function avdt_scb_snd_security_rsp +** +** Description This function sends a security response message. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_snd_security_rsp(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + if (p_data->msg.hdr.err_code == 0) { + avdt_msg_send_rsp(p_scb->p_ccb, AVDT_SIG_SECURITY, &p_data->msg); + } else { + avdt_msg_send_rej(p_scb->p_ccb, AVDT_SIG_SECURITY, &p_data->msg); + } +} + +/******************************************************************************* +** +** Function avdt_scb_snd_setconfig_rej +** +** Description This function marks the SCB as not in use and sends a +** set configuration reject message. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_snd_setconfig_rej(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + if (p_scb->p_ccb != NULL) { + avdt_msg_send_rej(p_scb->p_ccb, AVDT_SIG_SETCONFIG, &p_data->msg); + + /* clear scb variables */ + avdt_scb_clr_vars(p_scb, p_data); + } +} + +/******************************************************************************* +** +** Function avdt_scb_snd_setconfig_req +** +** Description This function marks the SCB as in use and copies the +** configuration parameters to the SCB. Then the function +** sends a set configuration command message and initiates +** opening of the signaling channel. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_snd_setconfig_req(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + tAVDT_CFG *p_req, *p_cfg; + + /* copy API parameters to scb, set scb as in use */ + p_scb->in_use = TRUE; + p_scb->p_ccb = avdt_ccb_by_idx(p_data->msg.config_cmd.hdr.ccb_idx); + p_scb->peer_seid = p_data->msg.config_cmd.hdr.seid; + p_req = p_data->msg.config_cmd.p_cfg; + p_cfg = &p_scb->cs.cfg; +#if AVDT_MULTIPLEXING == TRUE + p_req->mux_tsid_media = p_cfg->mux_tsid_media; + p_req->mux_tcid_media = p_cfg->mux_tcid_media; + if (p_req->psc_mask & AVDT_PSC_REPORT) { + p_req->mux_tsid_report = p_cfg->mux_tsid_report; + p_req->mux_tcid_report = p_cfg->mux_tcid_report; + } +#endif + memcpy(&p_scb->req_cfg, p_data->msg.config_cmd.p_cfg, sizeof(tAVDT_CFG)); + + avdt_msg_send_cmd(p_scb->p_ccb, p_scb, AVDT_SIG_SETCONFIG, &p_data->msg); + + /* tell ccb to open channel */ + avdt_ccb_event(p_scb->p_ccb, AVDT_CCB_UL_OPEN_EVT, NULL); +} + +/******************************************************************************* +** +** Function avdt_scb_snd_setconfig_rsp +** +** Description This function copies the requested configuration into the +** current configuration and sends a set configuration +** response message. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_snd_setconfig_rsp(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + if (p_scb->p_ccb != NULL) { + memcpy(&p_scb->curr_cfg, &p_scb->req_cfg, sizeof(tAVDT_CFG)); + p_scb->role = AVDT_CONF_ACP; + + avdt_msg_send_rsp(p_scb->p_ccb, AVDT_SIG_SETCONFIG, &p_data->msg); + } +} + +/******************************************************************************* +** +** Function avdt_scb_snd_tc_close +** +** Description This function calls avdt_ad_close_req() to close the +** transport channel for this SCB. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_snd_tc_close(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + UNUSED(p_data); + +#if AVDT_REPORTING == TRUE + if (p_scb->curr_cfg.psc_mask & AVDT_PSC_REPORT) { + avdt_ad_close_req(AVDT_CHAN_REPORT, p_scb->p_ccb, p_scb); + } +#endif + avdt_ad_close_req(AVDT_CHAN_MEDIA, p_scb->p_ccb, p_scb); +} + +/******************************************************************************* +** +** Function avdt_scb_cb_err +** +** Description This function calls the application callback function +** indicating an error. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_cb_err(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + tAVDT_CTRL avdt_ctrl; + UNUSED(p_data); + + /* set error code and parameter */ + avdt_ctrl.hdr.err_code = AVDT_ERR_BAD_STATE; + avdt_ctrl.hdr.err_param = 0; + + /* call callback, using lookup table to get callback event */ + (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb), + NULL, + avdt_scb_cback_evt[p_scb->curr_evt], + &avdt_ctrl); +} + +/******************************************************************************* +** +** Function avdt_scb_cong_state +** +** Description This function sets the congestion state of the SCB media +** transport channel. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_cong_state(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + p_scb->cong = p_data->llcong; +} + +/******************************************************************************* +** +** Function avdt_scb_rej_state +** +** Description This function sends a reject message to the peer indicating +** incorrect state for the received command message. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_rej_state(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + UNUSED(p_scb); + + p_data->msg.hdr.err_code = AVDT_ERR_BAD_STATE; + p_data->msg.hdr.err_param = 0; + avdt_msg_send_rej(avdt_ccb_by_idx(p_data->msg.hdr.ccb_idx), + p_data->msg.hdr.sig_id, &p_data->msg); +} + +/******************************************************************************* +** +** Function avdt_scb_rej_in_use +** +** Description This function sends a reject message to the peer indicating +** the stream is in use. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_rej_in_use(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + UNUSED(p_scb); + + p_data->msg.hdr.err_code = AVDT_ERR_IN_USE; + p_data->msg.hdr.err_param = 0; + avdt_msg_send_rej(avdt_ccb_by_idx(p_data->msg.hdr.ccb_idx), + p_data->msg.hdr.sig_id, &p_data->msg); +} + +/******************************************************************************* +** +** Function avdt_scb_rej_not_in_use +** +** Description This function sends a reject message to the peer indicating +** the stream is in use. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_rej_not_in_use(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + UNUSED(p_scb); + + p_data->msg.hdr.err_code = AVDT_ERR_NOT_IN_USE; + p_data->msg.hdr.err_param = 0; + avdt_msg_send_rej(avdt_ccb_by_idx(p_data->msg.hdr.ccb_idx), + p_data->msg.hdr.sig_id, &p_data->msg); +} + +/******************************************************************************* +** +** Function avdt_scb_set_remove +** +** Description This function marks an SCB to be removed. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_set_remove(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + UNUSED(p_data); + + p_scb->remove = TRUE; +} + +/******************************************************************************* +** +** Function avdt_scb_free_pkt +** +** Description This function frees the media packet passed in. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_free_pkt(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + tAVDT_CTRL avdt_ctrl; + + /* set error code and parameter */ + avdt_ctrl.hdr.err_code = AVDT_ERR_BAD_STATE; + avdt_ctrl.hdr.err_param = 0; + + /* p_buf can be NULL in case using of fragments queue frag_q */ + if (p_data->apiwrite.p_buf) { + osi_free(p_data->apiwrite.p_buf); + p_data->apiwrite.p_buf = NULL; + } + +#if AVDT_MULTIPLEXING == TRUE + /* clean fragments queue */ + BT_HDR *p_frag; + while ((p_frag = (BT_HDR*)fixed_queue_dequeue(p_scb->frag_q, 0)) != NULL) { + osi_free(p_frag); + } +#endif + + AVDT_TRACE_WARNING("Dropped media packet"); + + /* we need to call callback to keep data flow going */ + (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb), NULL, AVDT_WRITE_CFM_EVT, + &avdt_ctrl); +} + +/******************************************************************************* +** +** Function avdt_scb_clr_pkt +** +** Description This function frees the media packet stored in the SCB. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_clr_pkt(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + tAVDT_CTRL avdt_ctrl; + tAVDT_CCB *p_ccb; + UINT8 tcid; + UINT16 lcid; + UNUSED(p_data); + + /* set error code and parameter */ + avdt_ctrl.hdr.err_code = AVDT_ERR_BAD_STATE; + avdt_ctrl.hdr.err_param = 0; + /* flush the media data queued at L2CAP */ + if ((p_ccb = p_scb->p_ccb) != NULL) { + /* get tcid from type, scb */ + tcid = avdt_ad_type_to_tcid(AVDT_CHAN_MEDIA, p_scb); + + lcid = avdt_cb.ad.rt_tbl[avdt_ccb_to_idx(p_ccb)][tcid].lcid; + L2CA_FlushChannel (lcid, L2CAP_FLUSH_CHANS_ALL); + } + + if (p_scb->p_pkt != NULL) { + osi_free(p_scb->p_pkt); + p_scb->p_pkt = NULL; + + AVDT_TRACE_DEBUG("Dropped stored media packet"); + + /* we need to call callback to keep data flow going */ + (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb), NULL, AVDT_WRITE_CFM_EVT, + &avdt_ctrl); + } +#if AVDT_MULTIPLEXING == TRUE + else if (!fixed_queue_is_empty(p_scb->frag_q)) { + AVDT_TRACE_DEBUG("Dropped fragments queue"); + /* clean fragments queue */ + BT_HDR *p_frag; + while ((p_frag = (BT_HDR*)fixed_queue_dequeue(p_scb->frag_q, 0)) != NULL) { + osi_free(p_frag); + } + p_scb->frag_off = 0; + + /* we need to call callback to keep data flow going */ + (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb), NULL, AVDT_WRITE_CFM_EVT, + &avdt_ctrl); + } +#endif +} + + +/******************************************************************************* +** +** Function avdt_scb_chk_snd_pkt +** +** Description This function checks if the SCB is congested, and if not +** congested it sends a stored media packet, if any. After it +** sends the packet it calls the application callback function +** with a write confirm. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_chk_snd_pkt(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + tAVDT_CTRL avdt_ctrl; + BT_HDR *p_pkt; +#if AVDT_MULTIPLEXING == TRUE + BOOLEAN sent = FALSE; + UINT8 res = AVDT_AD_SUCCESS; + tAVDT_SCB_EVT data; +#endif + UNUSED(p_data); + + avdt_ctrl.hdr.err_code = 0; + + if (!p_scb->cong) { + if (p_scb->p_pkt != NULL) { + p_pkt = p_scb->p_pkt; + p_scb->p_pkt = NULL; + avdt_ad_write_req(AVDT_CHAN_MEDIA, p_scb->p_ccb, p_scb, p_pkt); + + (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb), NULL, AVDT_WRITE_CFM_EVT, &avdt_ctrl); + } +#if AVDT_MULTIPLEXING == TRUE + else { +#if 0 + AVDT_TRACE_DEBUG("num_q=%d\n", + L2CA_FlushChannel(avdt_cb.ad.rt_tbl[avdt_ccb_to_idx(p_scb->p_ccb)][avdt_ad_type_to_tcid(AVDT_CHAN_MEDIA, p_scb)].lcid), + L2CAP_FLUSH_CHANS_GET); +#endif + while ((p_pkt = (BT_HDR*)fixed_queue_dequeue(p_scb->frag_q, 0)) != NULL) { + sent = TRUE; + AVDT_TRACE_DEBUG("Send fragment len=%d\n", p_pkt->len); + /* fragments queue contains fragment to send */ + res = avdt_ad_write_req(AVDT_CHAN_MEDIA, p_scb->p_ccb, p_scb, p_pkt); + if (AVDT_AD_CONGESTED == res) { + p_scb->cong = TRUE; + AVDT_TRACE_DEBUG("avdt/l2c congested!!"); + break;/* exit loop if channel became congested */ + } + } + AVDT_TRACE_DEBUG("res=%d left=%d\n", res, p_scb->frag_off); + + if (p_scb->frag_off) { + if (AVDT_AD_SUCCESS == res || fixed_queue_is_empty(p_scb->frag_q)) { + /* all buffers were sent to L2CAP, compose more to queue */ + avdt_scb_queue_frags(p_scb, &p_scb->p_next_frag, &p_scb->frag_off, p_scb->frag_q); + if (!fixed_queue_is_empty(p_scb->frag_q)) { + data.llcong = p_scb->cong; + avdt_scb_event(p_scb, AVDT_SCB_TC_CONG_EVT, &data); + } + } + } + + /* Send event AVDT_WRITE_CFM_EVT if it was last fragment */ + else if (sent && fixed_queue_is_empty(p_scb->frag_q)) { + (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb), NULL, AVDT_WRITE_CFM_EVT, &avdt_ctrl); + } + } +#endif + } +} + +/******************************************************************************* +** +** Function avdt_scb_tc_timer +** +** Description This function is called to start a timer when the peer +** initiates closing of the stream. The timer verifies that +** the peer disconnects the transport channel. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_tc_timer(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + UNUSED(p_data); + + btu_start_timer(&p_scb->timer_entry, BTU_TTYPE_AVDT_SCB_TC, AVDT_SCB_TC_DISC_TOUT); +} + +/******************************************************************************* +** +** Function avdt_scb_clr_vars +** +** Description This function initializes certain SCB variables. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_clr_vars(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + UNUSED(p_data); + + if ((p_scb->cs.tsep == AVDT_TSEP_SNK) && (!p_scb->sink_activated)) { + p_scb->in_use = TRUE; + } else { + p_scb->in_use = FALSE; + } + p_scb->p_ccb = NULL; + p_scb->peer_seid = 0; +} + +#if AVDT_MULTIPLEXING == TRUE +/******************************************************************************* +** +** Function avdt_scb_queue_frags +** +** Description This function breaks media payload into fragments +** and put the fragments in the given queue. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_queue_frags(tAVDT_SCB *p_scb, UINT8 **pp_data, UINT32 *p_data_len, fixed_queue_t *pq) +{ + UINT16 lcid; + UINT16 num_frag; + UINT16 mtu_used; + UINT8 *p; + BOOLEAN al_hdr = FALSE; + UINT8 tcid; + tAVDT_TC_TBL *p_tbl; + UINT16 buf_size; + UINT16 offset = AVDT_MEDIA_OFFSET + AVDT_AL_HDR_SIZE; + UINT16 cont_offset = offset - AVDT_MEDIA_HDR_SIZE; + BT_HDR *p_frag; + + tcid = avdt_ad_type_to_tcid(AVDT_CHAN_MEDIA, p_scb); + lcid = avdt_cb.ad.rt_tbl[avdt_ccb_to_idx(p_scb->p_ccb)][tcid].lcid; + + if ( p_scb->frag_off != 0) { + /* continuing process is usually triggered by un-congest event. + * the number of buffers at L2CAP is very small (if not 0). + * we do not need to L2CA_FlushChannel() */ + offset = cont_offset; + al_hdr = TRUE; + num_frag = AVDT_MAX_FRAG_COUNT; + } else { + num_frag = L2CA_FlushChannel(lcid, L2CAP_FLUSH_CHANS_GET); + AVDT_TRACE_DEBUG("num_q=%d lcid=%d\n", num_frag, lcid); + if (num_frag >= AVDT_MAX_FRAG_COUNT) { + num_frag = 0; + } else { + num_frag = AVDT_MAX_FRAG_COUNT - num_frag; + } + } + + /* look up transport channel table entry to get peer mtu */ + p_tbl = avdt_ad_tc_tbl_by_type(AVDT_CHAN_MEDIA, p_scb->p_ccb, p_scb); + buf_size = p_tbl->peer_mtu + BT_HDR_SIZE; + AVDT_TRACE_DEBUG("peer_mtu: %d, buf_size: %d num_frag=%d\n", + p_tbl->peer_mtu, buf_size, num_frag); + + if (buf_size > AVDT_DATA_BUF_SIZE) { + buf_size = AVDT_DATA_BUF_SIZE; + } + + mtu_used = buf_size - BT_HDR_SIZE; + + while (*p_data_len && num_frag) { + /* allocate buffer for fragment */ + if (NULL == (p_frag = (BT_HDR *)osi_malloc(buf_size))) { + AVDT_TRACE_WARNING("avdt_scb_queue_frags len=%d(out of GKI buffers)\n", *p_data_len); + break; + } + /* fill fragment by chunk of media payload */ + p_frag->layer_specific = *p_data_len;/* length of all remaining transport packet */ + p_frag->offset = offset; + /* adjust packet offset for continuing packets */ + offset = cont_offset; + + p_frag->len = mtu_used - p_frag->offset; + if (p_frag->len > *p_data_len) { + p_frag->len = *p_data_len; + } + memcpy((UINT8 *)(p_frag + 1) + p_frag->offset, *pp_data, p_frag->len); + *pp_data += p_frag->len; + *p_data_len -= p_frag->len; + AVDT_TRACE_DEBUG("Prepared fragment len=%d\n", p_frag->len); + + if (al_hdr) { + /* Adaptation Layer header */ + p_frag->len += AVDT_AL_HDR_SIZE; + p_frag->offset -= AVDT_AL_HDR_SIZE; + p = (UINT8 *)(p_frag + 1) + p_frag->offset; + /* TSID, fragment bit and coding of length(in 2 length octets following) */ + *p++ = (p_scb->curr_cfg.mux_tsid_media << 3) | (AVDT_ALH_FRAG_MASK | AVDT_ALH_LCODE_16BIT); + + /* length of all remaining transport packet */ + UINT16_TO_BE_STREAM(p, p_frag->layer_specific ); + } + /* put fragment into gueue */ + fixed_queue_enqueue(p_scb->frag_q, p_frag, FIXED_QUEUE_MAX_TIMEOUT); + num_frag--; + } +} +#endif + +#endif /* #if (defined(AVDT_INCLUDED) && AVDT_INCLUDED == TRUE) */ diff --git a/lib/bt/host/bluedroid/stack/avdt/include/avdt_defs.h b/lib/bt/host/bluedroid/stack/avdt/include/avdt_defs.h new file mode 100644 index 00000000..f39e261b --- /dev/null +++ b/lib/bt/host/bluedroid/stack/avdt/include/avdt_defs.h @@ -0,0 +1,207 @@ +/****************************************************************************** + * + * Copyright (C) 2002-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This contains constants definitions and other information from the AVDTP + * specification. This file is intended for use internal to AVDT only. + * + ******************************************************************************/ +#ifndef AVDT_DEFS_H +#define AVDT_DEFS_H +#include "common/bt_target.h" + +#if (AVDT_INCLUDED == TRUE) + +/***************************************************************************** +** constants +*****************************************************************************/ + +/* signalling packet type */ +#define AVDT_PKT_TYPE_SINGLE 0 /* single packet */ +#define AVDT_PKT_TYPE_START 1 /* start packet */ +#define AVDT_PKT_TYPE_CONT 2 /* continue packet */ +#define AVDT_PKT_TYPE_END 3 /* end packet */ + +/* signalling message type */ +#define AVDT_MSG_TYPE_CMD 0 /* command */ +#define AVDT_MSG_TYPE_GRJ 1 /* general reject */ +#define AVDT_MSG_TYPE_RSP 2 /* response accept */ +#define AVDT_MSG_TYPE_REJ 3 /* response reject */ + +/* signalling messages */ +#define AVDT_SIG_DISCOVER 1 /* discover */ +#define AVDT_SIG_GETCAP 2 /* get capabilities */ +#define AVDT_SIG_SETCONFIG 3 /* set configuration */ +#define AVDT_SIG_GETCONFIG 4 /* get configuration */ +#define AVDT_SIG_RECONFIG 5 /* reconfigure */ +#define AVDT_SIG_OPEN 6 /* open */ +#define AVDT_SIG_START 7 /* start */ +#define AVDT_SIG_CLOSE 8 /* close */ +#define AVDT_SIG_SUSPEND 9 /* suspend */ +#define AVDT_SIG_ABORT 10 /* abort */ +#define AVDT_SIG_SECURITY 11 /* security control */ +#define AVDT_SIG_GET_ALLCAP 12 /* get all capabilities */ +#define AVDT_SIG_DELAY_RPT 13 /* delay report */ + +/* maximum signal value */ +#define AVDT_SIG_MAX AVDT_SIG_DELAY_RPT + +/* used for general reject */ +#define AVDT_SIG_NONE 0 + +/* some maximum and minimum sizes of signalling messages */ +#define AVDT_DISCOVER_REQ_MIN 1 +#define AVDT_DISCOVER_REQ_MAX 124 + +/* service category information element field values */ +#define AVDT_CAT_TRANS 1 /* Media Transport */ +#define AVDT_CAT_REPORT 2 /* Reporting */ +#define AVDT_CAT_RECOV 3 /* Recovery */ +#define AVDT_CAT_PROTECT 4 /* Content Protection */ +#define AVDT_CAT_HDRCMP 5 /* Header Compression */ +#define AVDT_CAT_MUX 6 /* Multiplexing */ +#define AVDT_CAT_CODEC 7 /* Media Codec */ +#define AVDT_CAT_DELAY_RPT 8 /* Delay Reporting */ +#define AVDT_CAT_MAX_CUR AVDT_CAT_DELAY_RPT + +/* min/max lengths of service category information elements */ +#define AVDT_LEN_TRANS_MIN 0 +#define AVDT_LEN_REPORT_MIN 0 +#define AVDT_LEN_RECOV_MIN 3 +#define AVDT_LEN_PROTECT_MIN 2 +#define AVDT_LEN_HDRCMP_MIN 1 +#define AVDT_LEN_MUX_MIN 3 +#define AVDT_LEN_CODEC_MIN 2 +#define AVDT_LEN_DELAY_RPT_MIN 0 + +#define AVDT_LEN_TRANS_MAX 0 +#define AVDT_LEN_REPORT_MAX 0 +#define AVDT_LEN_RECOV_MAX 3 +#define AVDT_LEN_PROTECT_MAX 255 +#define AVDT_LEN_HDRCMP_MAX 1 +#define AVDT_LEN_MUX_MAX 7 +#define AVDT_LEN_CODEC_MAX 255 +#define AVDT_LEN_DELAY_RPT_MAX 0 + +/* minimum possible size of configuration or capabilities data */ +#define AVDT_LEN_CFG_MIN 2 + +/* minimum and maximum lengths for different message types */ +#define AVDT_LEN_SINGLE 1 +#define AVDT_LEN_SETCONFIG_MIN 2 +#define AVDT_LEN_RECONFIG_MIN 1 +#define AVDT_LEN_MULTI_MIN 1 +#define AVDT_LEN_SECURITY_MIN 1 +#define AVDT_LEN_DELAY_RPT 3 + +/* header lengths for different packet types */ +#define AVDT_LEN_TYPE_SINGLE 2 /* single packet */ +#define AVDT_LEN_TYPE_START 3 /* start packet */ +#define AVDT_LEN_TYPE_CONT 1 /* continue packet */ +#define AVDT_LEN_TYPE_END 1 /* end packet */ + +/* length of general reject message */ +#define AVDT_LEN_GEN_REJ 2 + +/* recovery service capabilities information elements */ +#define AVDT_RECOV_MRWS_MIN 0x01 /* min value for maximum recovery window */ +#define AVDT_RECOV_MRWS_MAX 0x18 /* max value for maximum recovery window */ +#define AVDT_RECOV_MNMP_MIN 0x01 /* min value for maximum number of media packets */ +#define AVDT_RECOV_MNMP_MAX 0x18 /* max value for maximum number of media packets */ + +/* SEID value range */ +#define AVDT_SEID_MIN 0x01 +#define AVDT_SEID_MAX 0x3E + +/* first byte of media packet header */ +#define AVDT_MEDIA_OCTET1 0x80 + +/* for adaptation layer header */ +#define AVDT_ALH_LCODE_MASK 0x03 /* coding of length field */ +#define AVDT_ALH_LCODE_NONE 0x00 /* No length field present. Take length from l2cap */ +#define AVDT_ALH_LCODE_16BIT 0x01 /* 16bit length field */ +#define AVDT_ALH_LCODE_9BITM0 0x02 /* 9 bit length field, MSB = 0, 8 LSBs in 1 octet following */ +#define AVDT_ALH_LCODE_9BITM1 0x03 /* 9 bit length field, MSB = 1, 8 LSBs in 1 octet following */ + +#define AVDT_ALH_FRAG_MASK 0x04 /* set this for continuation packet */ + +/***************************************************************************** +** message parsing and building macros +*****************************************************************************/ + +#define AVDT_MSG_PRS_HDR(p, lbl, pkt, msg) \ + lbl = *(p) >> 4; \ + pkt = (*(p) >> 2) & 0x03; \ + msg = *(p)++ & 0x03; + +#define AVDT_MSG_PRS_DISC(p, seid, in_use, type, tsep) \ + seid = *(p) >> 2; \ + in_use = (*(p)++ >> 1) & 0x01; \ + type = *(p) >> 4; \ + tsep = (*(p)++ >> 3) & 0x01; + +#define AVDT_MSG_PRS_SIG(p, sig) \ + sig = *(p)++ & 0x3F; + +#define AVDT_MSG_PRS_SEID(p, seid) \ + seid = *(p)++ >> 2; + +#define AVDT_MSG_PRS_PKT_TYPE(p, pkt) \ + pkt = (*(p) >> 2) & 0x03; + +#define AVDT_MSG_PRS_OCTET1(p, o_v, o_p, o_x, o_cc) \ + o_v = *(p) >> 6; \ + o_p = (*(p) >> 5) & 0x01; \ + o_x = (*(p) >> 4) & 0x01; \ + o_cc = *(p)++ & 0x0F; + +#define AVDT_MSG_PRS_RPT_OCTET1(p, o_v, o_p, o_cc) \ + o_v = *(p) >> 6; \ + o_p = (*(p) >> 5) & 0x01; \ + o_cc = *(p)++ & 0x1F; + +#define AVDT_MSG_PRS_M_PT(p, m_pt, marker) \ + marker = *(p) >> 7; \ + m_pt = *(p)++ & 0x7F; + +#define AVDT_MSG_BLD_HDR(p, lbl, pkt, msg) \ + *(p)++ = (UINT8) ((lbl) << 4) | ((pkt) << 2) | (msg); + +#define AVDT_MSG_BLD_DISC(p, seid, in_use, type, tsep) \ + *(p)++ = (UINT8) (((seid) << 2) | ((in_use) << 1)); \ + *(p)++ = (UINT8) (((type) << 4) | ((tsep) << 3)); + +#define AVDT_MSG_BLD_SIG(p, sig) \ + *(p)++ = (UINT8) (sig); + +#define AVDT_MSG_BLD_SEID(p, seid) \ + *(p)++ = (UINT8) ((seid) << 2); + +#define AVDT_MSG_BLD_ERR(p, err) \ + *(p)++ = (UINT8) (err); + +#define AVDT_MSG_BLD_PARAM(p, param) \ + *(p)++ = (UINT8) (param); + +#define AVDT_MSG_BLD_NOSP(p, nosp) \ + *(p)++ = (UINT8) (nosp); + +#endif ///AVRC_INCLUDED == TRUE + +#endif /* AVDT_DEFS_H */ diff --git a/lib/bt/host/bluedroid/stack/avdt/include/avdt_int.h b/lib/bt/host/bluedroid/stack/avdt/include/avdt_int.h new file mode 100644 index 00000000..3b850c17 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/avdt/include/avdt_int.h @@ -0,0 +1,761 @@ +/****************************************************************************** + * + * Copyright (C) 2002-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains interfaces which are internal to AVDTP. + * + ******************************************************************************/ +#ifndef AVDT_INT_H +#define AVDT_INT_H + +#include "stack/avdt_api.h" +#include "stack/avdtc_api.h" +#include "avdt_defs.h" +#include "stack/l2c_api.h" +#include "stack/btm_api.h" +#include "osi/fixed_queue.h" + +#if (AVRC_INCLUDED == TRUE) + +#ifndef AVDT_DEBUG +#define AVDT_DEBUG FALSE +#endif + +/***************************************************************************** +** constants +*****************************************************************************/ + +/* channel types */ +enum { + AVDT_CHAN_SIG, /* signaling channel */ + AVDT_CHAN_MEDIA, /* media channel */ +#if AVDT_REPORTING == TRUE + AVDT_CHAN_REPORT, /* reporting channel */ +#endif + AVDT_CHAN_NUM_TYPES +}; + +/* protocol service capabilities of this AVDTP implementation */ +/* for now multiplexing will be used only for fragmentation */ +#if ((AVDT_MULTIPLEXING == TRUE) && (AVDT_REPORTING == TRUE)) +#define AVDT_PSC (AVDT_PSC_TRANS | AVDT_PSC_MUX | AVDT_PSC_REPORT | AVDT_PSC_DELAY_RPT) +#define AVDT_LEG_PSC (AVDT_PSC_TRANS | AVDT_PSC_MUX | AVDT_PSC_REPORT) +#else /* AVDT_MULTIPLEXING && AVDT_REPORTING */ + +#if (AVDT_MULTIPLEXING == TRUE) +#define AVDT_PSC (AVDT_PSC_TRANS | AVDT_PSC_MUX | AVDT_PSC_DELAY_RPT) +#define AVDT_LEG_PSC (AVDT_PSC_TRANS | AVDT_PSC_MUX) +#else /* AVDT_MULTIPLEXING */ + +#if (AVDT_REPORTING == TRUE) +#define AVDT_PSC (AVDT_PSC_TRANS | AVDT_PSC_REPORT | AVDT_PSC_DELAY_RPT) +#define AVDT_LEG_PSC (AVDT_PSC_TRANS | AVDT_PSC_REPORT) +#else /* AVDT_REPORTING */ +#define AVDT_PSC (AVDT_PSC_TRANS | AVDT_PSC_DELAY_RPT) +#define AVDT_LEG_PSC (AVDT_PSC_TRANS) +#endif /* AVDT_REPORTING */ + +#endif /* AVDT_MULTIPLEXING */ + +#endif /* AVDT_MULTIPLEXING && AVDT_REPORTING */ + +/* initiator/acceptor signaling roles */ +#define AVDT_CLOSE_ACP 0 +#define AVDT_CLOSE_INT 1 +#define AVDT_OPEN_ACP 2 +#define AVDT_OPEN_INT 3 +#define AVDT_CONF_ACP 4 +#define AVDT_CONF_INT 5 + +/* states for avdt_scb_verify */ +#define AVDT_VERIFY_OPEN 0 +#define AVDT_VERIFY_STREAMING 1 +#define AVDT_VERIFY_SUSPEND 2 +#define AVDT_VERIFY_START 3 + +/* to distinguish CCB events from SCB events */ +#define AVDT_CCB_MKR 0x80 + +/* offset where AVDTP signaling message header starts in message */ +#define AVDT_HDR_OFFSET (L2CAP_MIN_OFFSET + AVDT_NUM_SEPS) + +/* offset where AVDTP signaling message content starts; +** use the size of a start header since it's the largest possible +** layout of signaling message in a buffer is: +** +** | BT_HDR | SCB handles | L2CAP + HCI header | AVDTP header | data ... | +** +** Note that we "hide" the scb handles at the top of the message buffer. +*/ +#define AVDT_MSG_OFFSET (L2CAP_MIN_OFFSET + AVDT_NUM_SEPS + AVDT_LEN_TYPE_START) + +/* scb transport channel connect timeout value */ +#define AVDT_SCB_TC_CONN_TOUT 10 + +/* scb transport channel disconnect timeout value */ +#define AVDT_SCB_TC_DISC_TOUT 10 + +/* scb transport delay reporting command timeout value */ +#define AVDT_SCB_TC_DELAY_RPT_TOUT 5 + +/* maximum number of command retransmissions */ +#ifndef AVDT_RET_MAX +#define AVDT_RET_MAX 1 +#endif + + +/* ccb state machine states */ +enum { + AVDT_CCB_IDLE_ST, + AVDT_CCB_OPENING_ST, + AVDT_CCB_OPEN_ST, + AVDT_CCB_CLOSING_ST +}; + +/* state machine action enumeration list */ +enum { + AVDT_CCB_CHAN_OPEN, + AVDT_CCB_CHAN_CLOSE, + AVDT_CCB_CHK_CLOSE, + AVDT_CCB_HDL_DISCOVER_CMD, + AVDT_CCB_HDL_DISCOVER_RSP, + AVDT_CCB_HDL_GETCAP_CMD, + AVDT_CCB_HDL_GETCAP_RSP, + AVDT_CCB_HDL_START_CMD, + AVDT_CCB_HDL_START_RSP, + AVDT_CCB_HDL_SUSPEND_CMD, + AVDT_CCB_HDL_SUSPEND_RSP, + AVDT_CCB_SND_DISCOVER_CMD, + AVDT_CCB_SND_DISCOVER_RSP, + AVDT_CCB_SND_GETCAP_CMD, + AVDT_CCB_SND_GETCAP_RSP, + AVDT_CCB_SND_START_CMD, + AVDT_CCB_SND_START_RSP, + AVDT_CCB_SND_SUSPEND_CMD, + AVDT_CCB_SND_SUSPEND_RSP, + AVDT_CCB_CLEAR_CMDS, + AVDT_CCB_CMD_FAIL, + AVDT_CCB_FREE_CMD, + AVDT_CCB_CONG_STATE, + AVDT_CCB_RET_CMD, + AVDT_CCB_SND_CMD, + AVDT_CCB_SND_MSG, + AVDT_CCB_SET_RECONN, + AVDT_CCB_CLR_RECONN, + AVDT_CCB_CHK_RECONN, + AVDT_CCB_CHK_TIMER, + AVDT_CCB_SET_CONN, + AVDT_CCB_SET_DISCONN, + AVDT_CCB_DO_DISCONN, + AVDT_CCB_LL_CLOSED, + AVDT_CCB_LL_OPENED, + AVDT_CCB_DEALLOC, + AVDT_CCB_NUM_ACTIONS +}; + +#define AVDT_CCB_IGNORE AVDT_CCB_NUM_ACTIONS + +/* ccb state machine events */ +enum { + AVDT_CCB_API_DISCOVER_REQ_EVT, + AVDT_CCB_API_GETCAP_REQ_EVT, + AVDT_CCB_API_START_REQ_EVT, + AVDT_CCB_API_SUSPEND_REQ_EVT, + AVDT_CCB_API_DISCOVER_RSP_EVT, + AVDT_CCB_API_GETCAP_RSP_EVT, + AVDT_CCB_API_START_RSP_EVT, + AVDT_CCB_API_SUSPEND_RSP_EVT, + AVDT_CCB_API_CONNECT_REQ_EVT, + AVDT_CCB_API_DISCONNECT_REQ_EVT, + AVDT_CCB_MSG_DISCOVER_CMD_EVT, + AVDT_CCB_MSG_GETCAP_CMD_EVT, + AVDT_CCB_MSG_START_CMD_EVT, + AVDT_CCB_MSG_SUSPEND_CMD_EVT, + AVDT_CCB_MSG_DISCOVER_RSP_EVT, + AVDT_CCB_MSG_GETCAP_RSP_EVT, + AVDT_CCB_MSG_START_RSP_EVT, + AVDT_CCB_MSG_SUSPEND_RSP_EVT, + AVDT_CCB_RCVRSP_EVT, + AVDT_CCB_SENDMSG_EVT, + AVDT_CCB_RET_TOUT_EVT, + AVDT_CCB_RSP_TOUT_EVT, + AVDT_CCB_IDLE_TOUT_EVT, + AVDT_CCB_UL_OPEN_EVT, + AVDT_CCB_UL_CLOSE_EVT, + AVDT_CCB_LL_OPEN_EVT, + AVDT_CCB_LL_CLOSE_EVT, + AVDT_CCB_LL_CONG_EVT +}; + + +/* scb state machine states; these state values are private to this module so +** the scb state cannot be read or set by actions functions +*/ +enum { + AVDT_SCB_IDLE_ST, + AVDT_SCB_CONF_ST, + AVDT_SCB_OPENING_ST, + AVDT_SCB_OPEN_ST, + AVDT_SCB_STREAM_ST, + AVDT_SCB_CLOSING_ST +}; + +/* state machine action enumeration list */ +enum { + AVDT_SCB_HDL_ABORT_CMD, + AVDT_SCB_HDL_ABORT_RSP, + AVDT_SCB_HDL_CLOSE_CMD, + AVDT_SCB_HDL_CLOSE_RSP, + AVDT_SCB_HDL_GETCONFIG_CMD, + AVDT_SCB_HDL_GETCONFIG_RSP, + AVDT_SCB_HDL_OPEN_CMD, + AVDT_SCB_HDL_OPEN_REJ, + AVDT_SCB_HDL_OPEN_RSP, + AVDT_SCB_HDL_PKT, + AVDT_SCB_DROP_PKT, + AVDT_SCB_HDL_RECONFIG_CMD, + AVDT_SCB_HDL_RECONFIG_RSP, + AVDT_SCB_HDL_SECURITY_CMD, + AVDT_SCB_HDL_SECURITY_RSP, + AVDT_SCB_HDL_SETCONFIG_CMD, + AVDT_SCB_HDL_SETCONFIG_REJ, + AVDT_SCB_HDL_SETCONFIG_RSP, + AVDT_SCB_HDL_START_CMD, + AVDT_SCB_HDL_START_RSP, + AVDT_SCB_HDL_SUSPEND_CMD, + AVDT_SCB_HDL_SUSPEND_RSP, + AVDT_SCB_HDL_TC_CLOSE, +#if AVDT_REPORTING == TRUE + AVDT_SCB_HDL_TC_CLOSE_STO, +#endif + AVDT_SCB_HDL_TC_OPEN, +#if AVDT_REPORTING == TRUE + AVDT_SCB_HDL_TC_OPEN_STO, +#endif + AVDT_SCB_SND_DELAY_RPT_REQ, + AVDT_SCB_HDL_DELAY_RPT_CMD, + AVDT_SCB_HDL_DELAY_RPT_RSP, + AVDT_SCB_HDL_WRITE_REQ, + AVDT_SCB_SND_ABORT_REQ, + AVDT_SCB_SND_ABORT_RSP, + AVDT_SCB_SND_CLOSE_REQ, + AVDT_SCB_SND_STREAM_CLOSE, + AVDT_SCB_SND_CLOSE_RSP, + AVDT_SCB_SND_GETCONFIG_REQ, + AVDT_SCB_SND_GETCONFIG_RSP, + AVDT_SCB_SND_OPEN_REQ, + AVDT_SCB_SND_OPEN_RSP, + AVDT_SCB_SND_RECONFIG_REQ, + AVDT_SCB_SND_RECONFIG_RSP, + AVDT_SCB_SND_SECURITY_REQ, + AVDT_SCB_SND_SECURITY_RSP, + AVDT_SCB_SND_SETCONFIG_REQ, + AVDT_SCB_SND_SETCONFIG_REJ, + AVDT_SCB_SND_SETCONFIG_RSP, + AVDT_SCB_SND_TC_CLOSE, + AVDT_SCB_CB_ERR, + AVDT_SCB_CONG_STATE, + AVDT_SCB_REJ_STATE, + AVDT_SCB_REJ_IN_USE, + AVDT_SCB_REJ_NOT_IN_USE, + AVDT_SCB_SET_REMOVE, + AVDT_SCB_FREE_PKT, + AVDT_SCB_CLR_PKT, + AVDT_SCB_CHK_SND_PKT, + AVDT_SCB_TC_TIMER, + AVDT_SCB_CLR_VARS, + AVDT_SCB_DEALLOC, + AVDT_SCB_HDL_DELAY_RPT_TOUT, + AVDT_SCB_INIT_OPEN_REQ_EVT, + AVDT_SCB_SEND_DELAY_REPORT_CMD_EVT, + AVDT_SCB_NUM_ACTIONS +}; + +#define AVDT_SCB_IGNORE AVDT_SCB_NUM_ACTIONS + +/* scb state machine events */ +enum { + AVDT_SCB_API_REMOVE_EVT, + AVDT_SCB_API_WRITE_REQ_EVT, + AVDT_SCB_API_GETCONFIG_REQ_EVT, + AVDT_SCB_API_DELAY_RPT_REQ_EVT, + AVDT_SCB_API_SETCONFIG_REQ_EVT, + AVDT_SCB_API_OPEN_REQ_EVT, + AVDT_SCB_API_CLOSE_REQ_EVT, + AVDT_SCB_API_RECONFIG_REQ_EVT, + AVDT_SCB_API_SECURITY_REQ_EVT, + AVDT_SCB_API_ABORT_REQ_EVT, + AVDT_SCB_API_GETCONFIG_RSP_EVT, + AVDT_SCB_API_SETCONFIG_RSP_EVT, + AVDT_SCB_API_SETCONFIG_REJ_EVT, + AVDT_SCB_API_OPEN_RSP_EVT, + AVDT_SCB_API_CLOSE_RSP_EVT, + AVDT_SCB_API_RECONFIG_RSP_EVT, + AVDT_SCB_API_SECURITY_RSP_EVT, + AVDT_SCB_API_ABORT_RSP_EVT, + AVDT_SCB_MSG_SETCONFIG_CMD_EVT, + AVDT_SCB_MSG_GETCONFIG_CMD_EVT, + AVDT_SCB_MSG_OPEN_CMD_EVT, + AVDT_SCB_MSG_START_CMD_EVT, + AVDT_SCB_MSG_SUSPEND_CMD_EVT, + AVDT_SCB_MSG_CLOSE_CMD_EVT, + AVDT_SCB_MSG_ABORT_CMD_EVT, + AVDT_SCB_MSG_RECONFIG_CMD_EVT, + AVDT_SCB_MSG_SECURITY_CMD_EVT, + AVDT_SCB_MSG_DELAY_RPT_CMD_EVT, + AVDT_SCB_MSG_DELAY_RPT_RSP_EVT, + AVDT_SCB_MSG_SETCONFIG_RSP_EVT, + AVDT_SCB_MSG_GETCONFIG_RSP_EVT, + AVDT_SCB_MSG_OPEN_RSP_EVT, + AVDT_SCB_MSG_START_RSP_EVT, + AVDT_SCB_MSG_SUSPEND_RSP_EVT, + AVDT_SCB_MSG_CLOSE_RSP_EVT, + AVDT_SCB_MSG_ABORT_RSP_EVT, + AVDT_SCB_MSG_RECONFIG_RSP_EVT, + AVDT_SCB_MSG_SECURITY_RSP_EVT, + AVDT_SCB_MSG_SETCONFIG_REJ_EVT, + AVDT_SCB_MSG_OPEN_REJ_EVT, + AVDT_SCB_MSG_START_REJ_EVT, + AVDT_SCB_MSG_SUSPEND_REJ_EVT, + AVDT_SCB_TC_TOUT_EVT, + AVDT_SCB_TC_OPEN_EVT, + AVDT_SCB_TC_CLOSE_EVT, + AVDT_SCB_TC_CONG_EVT, + AVDT_SCB_TC_DATA_EVT, + AVDT_SCB_CC_CLOSE_EVT, + AVDT_SCB_DELAY_RPT_RSP_TOUT_EVT +}; + +/* adaption layer number of stream routing table entries */ +#if AVDT_REPORTING == TRUE +/* 2 channels(1 media, 1 report) for each SEP and one for signalling */ +#define AVDT_NUM_RT_TBL ((AVDT_NUM_SEPS<<1) + 1) +#else +#define AVDT_NUM_RT_TBL (AVDT_NUM_SEPS + 1) +#endif + +/* adaption layer number of transport channel table entries - moved to target.h +#define AVDT_NUM_TC_TBL (AVDT_NUM_SEPS + AVDT_NUM_LINKS) */ + +/* "states" used in transport channel table */ +#define AVDT_AD_ST_UNUSED 0 /* Unused - unallocated */ +#define AVDT_AD_ST_IDLE 1 /* No connection */ +#define AVDT_AD_ST_ACP 2 /* Waiting to accept a connection */ +#define AVDT_AD_ST_INT 3 /* Initiating a connection */ +#define AVDT_AD_ST_CONN 4 /* Waiting for connection confirm */ +#define AVDT_AD_ST_CFG 5 /* Waiting for configuration complete */ +#define AVDT_AD_ST_OPEN 6 /* Channel opened */ +#define AVDT_AD_ST_SEC_INT 7 /* Security process as INT */ +#define AVDT_AD_ST_SEC_ACP 8 /* Security process as ACP */ + +/* Configuration flags. tAVDT_TC_TBL.cfg_flags */ +#define AVDT_L2C_CFG_IND_DONE (1<<0) +#define AVDT_L2C_CFG_CFM_DONE (1<<1) +#define AVDT_L2C_CFG_CONN_INT (1<<2) +#define AVDT_L2C_CFG_CONN_ACP (1<<3) + + +/* result code for avdt_ad_write_req() (L2CA_DataWrite()) */ +#define AVDT_AD_FAILED L2CAP_DW_FAILED /* FALSE */ +#define AVDT_AD_SUCCESS L2CAP_DW_SUCCESS /* TRUE */ +#define AVDT_AD_CONGESTED L2CAP_DW_CONGESTED /* 2 */ + +/***************************************************************************** +** data types +*****************************************************************************/ + +/* msg union of all message parameter types */ +typedef union { + tAVDT_EVT_HDR hdr; + tAVDT_EVT_HDR single; + tAVDT_SETCONFIG config_cmd; + tAVDT_CONFIG reconfig_cmd; + tAVDT_MULTI multi; + tAVDT_SECURITY security_cmd; + tAVDT_DISCOVER discover_rsp; + tAVDT_CONFIG svccap; + tAVDT_SECURITY security_rsp; + tAVDT_DELAY_RPT delay_rpt_cmd; +} tAVDT_MSG; + +/* data type for AVDT_CCB_API_DISCOVER_REQ_EVT */ +typedef struct { + tAVDT_CTRL_CBACK *p_cback; + tAVDT_SEP_INFO *p_sep_info; + UINT8 num_seps; +} tAVDT_CCB_API_DISCOVER; + +/* data type for AVDT_CCB_API_GETCAP_REQ_EVT */ +typedef struct { + tAVDT_EVT_HDR single; + tAVDT_CTRL_CBACK *p_cback; + tAVDT_CFG *p_cfg; +} tAVDT_CCB_API_GETCAP; + +/* data type for AVDT_CCB_API_CONNECT_REQ_EVT */ +typedef struct { + tAVDT_CTRL_CBACK *p_cback; + UINT8 sec_mask; +} tAVDT_CCB_API_CONNECT; + +/* data type for AVDT_CCB_API_DISCONNECT_REQ_EVT */ +typedef struct { + tAVDT_CTRL_CBACK *p_cback; +} tAVDT_CCB_API_DISCONNECT; + +/* union associated with ccb state machine events */ +typedef union { + tAVDT_CCB_API_DISCOVER discover; + tAVDT_CCB_API_GETCAP getcap; + tAVDT_CCB_API_CONNECT connect; + tAVDT_CCB_API_DISCONNECT disconnect; + tAVDT_MSG msg; + BOOLEAN llcong; + UINT8 err_code; +} tAVDT_CCB_EVT; + +/* channel control block type */ +typedef struct { + BD_ADDR peer_addr; /* BD address of peer */ + TIMER_LIST_ENT timer_entry; /* CCB timer list entry */ + fixed_queue_t *cmd_q; /* Queue for outgoing command messages */ + fixed_queue_t *rsp_q; /* Queue for outgoing response and reject messages */ + tAVDT_CTRL_CBACK *proc_cback; /* Procedure callback function */ + tAVDT_CTRL_CBACK *p_conn_cback; /* Connection/disconnection callback function */ + void *p_proc_data; /* Pointer to data storage for procedure */ + BT_HDR *p_curr_cmd; /* Current command being sent awaiting response */ + BT_HDR *p_curr_msg; /* Current message being sent */ + BT_HDR *p_rx_msg; /* Current message being received */ + BOOLEAN allocated; /* Whether ccb is allocated */ + UINT8 state; /* The CCB state machine state */ + BOOLEAN ll_opened; /* TRUE if LL is opened */ + BOOLEAN proc_busy; /* TRUE when a discover or get capabilities procedure in progress */ + UINT8 proc_param; /* Procedure parameter; either SEID for get capabilities or number of SEPS for discover */ + BOOLEAN cong; /* Whether signaling channel is congested */ + UINT8 label; /* Message header "label" (sequence number) */ + BOOLEAN reconn; /* If TRUE, reinitiate connection after transitioning from CLOSING to IDLE state */ + UINT8 ret_count; /* Command retransmission count */ + UINT8 disc_rsn; /* disconnection reason */ +} tAVDT_CCB; + +/* type for action functions */ +typedef void (*tAVDT_CCB_ACTION)(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data); + +/* type for AVDT_SCB_API_WRITE_REQ_EVT */ +typedef struct { + BT_HDR *p_buf; + UINT32 time_stamp; +#if AVDT_MULTIPLEXING == TRUE + fixed_queue_t *frag_q; /* Queue for outgoing media fragments. p_buf should be 0 */ + UINT8 *p_data; + UINT32 data_len; +#endif + UINT8 m_pt; + tAVDT_DATA_OPT_MASK opt; +} tAVDT_SCB_APIWRITE; + +/* type for AVDT_SCB_TC_CLOSE_EVT */ +typedef struct { + UINT8 old_tc_state; /* channel state before closed */ + UINT8 tcid; /* TCID */ + UINT8 type; /* channel type */ + UINT8 disc_rsn; /* disconnection reason */ +} tAVDT_SCB_TC_CLOSE; + +/* type for scb event data */ +typedef union { + tAVDT_MSG msg; + tAVDT_SCB_APIWRITE apiwrite; + tAVDT_DELAY_RPT apidelay; + tAVDT_OPEN open; + tAVDT_SCB_TC_CLOSE close; + BOOLEAN llcong; + BT_HDR *p_pkt; +} tAVDT_SCB_EVT; + +/* stream control block type */ +typedef struct { + tAVDT_CS cs; /* stream creation struct */ + tAVDT_CFG curr_cfg; /* current configuration */ + tAVDT_CFG req_cfg; /* requested configuration */ + TIMER_LIST_ENT timer_entry; /* timer entry */ + BT_HDR *p_pkt; /* packet waiting to be sent */ + tAVDT_CCB *p_ccb; /* ccb associated with this scb */ + UINT16 media_seq; /* media packet sequence number */ + BOOLEAN allocated; /* whether scb is allocated or unused */ + BOOLEAN in_use; /* whether stream being used by peer */ + BOOLEAN sink_activated; /* A2DP Sink activated/de-activated from Application */ + UINT8 role; /* initiator/acceptor role in current procedure */ + BOOLEAN remove; /* whether CB is marked for removal */ + UINT8 state; /* state machine state */ + UINT8 peer_seid; /* SEID of peer stream */ + UINT8 curr_evt; /* current event; set only by state machine */ + BOOLEAN cong; /* Whether media transport channel is congested */ + UINT8 close_code; /* Error code received in close response */ +#if AVDT_MULTIPLEXING == TRUE + fixed_queue_t *frag_q; /* Queue for outgoing media fragments */ + UINT32 frag_off; /* length of already received media fragments */ + UINT32 frag_org_len; /* original length before fragmentation of receiving media packet */ + UINT8 *p_next_frag; /* next fragment to send */ + UINT8 *p_media_buf; /* buffer for media packet assigned by AVDT_SetMediaBuf */ + UINT32 media_buf_len; /* length of buffer for media packet assigned by AVDT_SetMediaBuf */ +#endif +} tAVDT_SCB; + +/* type for action functions */ +typedef void (*tAVDT_SCB_ACTION)(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); + +/* adaption layer type for transport channel table */ +typedef struct { + UINT16 peer_mtu; /* L2CAP mtu of the peer device */ + UINT16 my_mtu; /* Our MTU for this channel */ + UINT16 my_flush_to; /* Our flush timeout for this channel */ + UINT16 lcid; + UINT8 tcid; /* transport channel id */ + UINT8 ccb_idx; /* channel control block associated with this tc */ + UINT8 state; /* transport channel state */ + UINT8 cfg_flags; /* L2CAP configuration flags */ + UINT8 id; +} tAVDT_TC_TBL; + +/* adaption layer type for stream routing table */ +typedef struct { + UINT16 lcid; /* L2CAP LCID of the associated transport channel */ + UINT8 scb_hdl; /* stream control block associated with this tc */ +} tAVDT_RT_TBL; + + +/* adaption layer control block */ +typedef struct { + tAVDT_RT_TBL rt_tbl[AVDT_NUM_LINKS][AVDT_NUM_RT_TBL]; + tAVDT_TC_TBL tc_tbl[AVDT_NUM_TC_TBL]; + UINT8 lcid_tbl[MAX_L2CAP_CHANNELS]; /* map LCID to tc_tbl index */ +} tAVDT_AD; + +/* Control block for AVDT */ +typedef struct { + tAVDT_REG rcb; /* registration control block */ + tAVDT_CCB ccb[AVDT_NUM_LINKS]; /* channel control blocks */ + tAVDT_SCB scb[AVDT_NUM_SEPS]; /* stream control blocks */ + tAVDT_AD ad; /* adaption layer control block */ + tAVDTC_CTRL_CBACK *p_conf_cback; /* conformance callback function */ + tAVDT_CCB_ACTION *p_ccb_act; /* pointer to CCB action functions */ + tAVDT_SCB_ACTION *p_scb_act; /* pointer to SCB action functions */ + tAVDT_CTRL_CBACK *p_conn_cback; /* connection callback function */ + UINT8 trace_level; /* trace level */ + UINT16 delay_value; /* delay reporting value */ +} tAVDT_CB; + + +/***************************************************************************** +** function declarations +*****************************************************************************/ + +/* CCB function declarations */ +extern void avdt_ccb_init(void); +extern void avdt_ccb_event(tAVDT_CCB *p_ccb, UINT8 event, tAVDT_CCB_EVT *p_data); +extern tAVDT_CCB *avdt_ccb_by_bd(BD_ADDR bd_addr); +extern tAVDT_CCB *avdt_ccb_alloc(BD_ADDR bd_addr); +extern void avdt_ccb_dealloc(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data); +extern UINT8 avdt_ccb_to_idx(tAVDT_CCB *p_ccb); +extern tAVDT_CCB *avdt_ccb_by_idx(UINT8 idx); + +/* CCB action functions */ +extern void avdt_ccb_chan_open(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data); +extern void avdt_ccb_chan_close(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data); +extern void avdt_ccb_chk_close(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data); +extern void avdt_ccb_hdl_discover_cmd(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data); +extern void avdt_ccb_hdl_discover_rsp(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data); +extern void avdt_ccb_hdl_getcap_cmd(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data); +extern void avdt_ccb_hdl_getcap_rsp(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data); +extern void avdt_ccb_hdl_start_cmd(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data); +extern void avdt_ccb_hdl_start_rsp(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data); +extern void avdt_ccb_hdl_suspend_cmd(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data); +extern void avdt_ccb_hdl_suspend_rsp(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data); +extern void avdt_ccb_snd_discover_cmd(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data); +extern void avdt_ccb_snd_discover_rsp(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data); +extern void avdt_ccb_snd_getcap_cmd(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data); +extern void avdt_ccb_snd_getcap_rsp(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data); +extern void avdt_ccb_snd_start_cmd(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data); +extern void avdt_ccb_snd_start_rsp(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data); +extern void avdt_ccb_snd_suspend_cmd(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data); +extern void avdt_ccb_snd_suspend_rsp(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data); +extern void avdt_ccb_clear_cmds(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data); +extern void avdt_ccb_cmd_fail(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data); +extern void avdt_ccb_free_cmd(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data); +extern void avdt_ccb_cong_state(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data); +extern void avdt_ccb_ret_cmd(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data); +extern void avdt_ccb_snd_cmd(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data); +extern void avdt_ccb_snd_msg(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data); +extern void avdt_ccb_set_reconn(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data); +extern void avdt_ccb_clr_reconn(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data); +extern void avdt_ccb_chk_reconn(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data); +extern void avdt_ccb_chk_timer(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data); +extern void avdt_ccb_set_conn(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data); +extern void avdt_ccb_set_disconn(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data); +extern void avdt_ccb_do_disconn(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data); +extern void avdt_ccb_ll_closed(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data); +extern void avdt_ccb_ll_opened(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data); + +/* SCB function prototypes */ +extern void avdt_scb_event(tAVDT_SCB *p_scb, UINT8 event, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_init(void); +extern tAVDT_SCB *avdt_scb_alloc(tAVDT_CS *p_cs); +extern void avdt_scb_dealloc(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern UINT8 avdt_scb_to_hdl(tAVDT_SCB *p_scb); +extern tAVDT_SCB *avdt_scb_by_hdl(UINT8 hdl); +extern UINT8 avdt_scb_verify(tAVDT_CCB *p_ccb, UINT8 state, UINT8 *p_seid, UINT16 num_seid, UINT8 *p_err_code); +extern void avdt_scb_peer_seid_list(tAVDT_MULTI *p_multi); +extern UINT32 avdt_scb_gen_ssrc(tAVDT_SCB *p_scb); + +/* SCB action functions */ +extern void avdt_scb_hdl_abort_cmd(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_hdl_abort_rsp(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_hdl_close_cmd(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_hdl_close_rsp(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_hdl_getconfig_cmd(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_hdl_getconfig_rsp(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_hdl_open_cmd(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_hdl_open_rej(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_hdl_open_rsp(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_hdl_pkt(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_drop_pkt(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_hdl_reconfig_cmd(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_hdl_reconfig_rsp(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_hdl_security_cmd(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_hdl_security_rsp(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_hdl_setconfig_cmd(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_hdl_setconfig_rej(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_hdl_setconfig_rsp(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_hdl_start_cmd(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_hdl_start_rsp(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_hdl_suspend_cmd(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_hdl_suspend_rsp(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_snd_delay_rpt_req(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_hdl_delay_rpt_cmd(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_hdl_delay_rpt_rsp(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_hdl_tc_close(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_hdl_tc_open(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_hdl_tc_close_sto(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_hdl_tc_open_sto(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_hdl_write_req(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_snd_abort_req(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_snd_abort_rsp(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_snd_close_req(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_snd_stream_close(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_snd_close_rsp(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_snd_getconfig_req(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_snd_getconfig_rsp(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_snd_open_req(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_snd_open_rsp(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_snd_reconfig_req(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_snd_reconfig_rsp(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_snd_security_req(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_snd_security_rsp(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_snd_setconfig_req(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_snd_setconfig_rej(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_snd_setconfig_rsp(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_snd_tc_close(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_cb_err(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_cong_state(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_rej_state(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_rej_in_use(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_rej_not_in_use(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_set_remove(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_free_pkt(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_chk_snd_pkt(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_clr_pkt(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_tc_timer(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_clr_vars(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_queue_frags(tAVDT_SCB *p_scb, UINT8 **pp_data, UINT32 *p_data_len, fixed_queue_t *pq); +extern void avdt_scb_hdl_delay_rpt_tout(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_init_open_req(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_send_delay_report_cmd(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); + +/* msg function declarations */ +extern BOOLEAN avdt_msg_send(tAVDT_CCB *p_ccb, BT_HDR *p_msg); +extern void avdt_msg_send_cmd(tAVDT_CCB *p_ccb, void *p_scb, UINT8 sig_id, tAVDT_MSG *p_params); +extern void avdt_msg_send_rsp(tAVDT_CCB *p_ccb, UINT8 sig_id, tAVDT_MSG *p_params); +extern void avdt_msg_send_rej(tAVDT_CCB *p_ccb, UINT8 sig_id, tAVDT_MSG *p_params); +extern void avdt_msg_send_grej(tAVDT_CCB *p_ccb, UINT8 sig_id, tAVDT_MSG *p_params); +extern void avdt_msg_ind(tAVDT_CCB *p_ccb, BT_HDR *p_buf); + +/* adaption layer function declarations */ +extern void avdt_ad_init(void); +extern UINT8 avdt_ad_type_to_tcid(UINT8 type, tAVDT_SCB *p_scb); +extern tAVDT_TC_TBL *avdt_ad_tc_tbl_by_st(UINT8 type, tAVDT_CCB *p_ccb, UINT8 state); +extern tAVDT_TC_TBL *avdt_ad_tc_tbl_by_lcid(UINT16 lcid); +extern tAVDT_TC_TBL *avdt_ad_tc_tbl_alloc(tAVDT_CCB *p_ccb); +extern UINT8 avdt_ad_tc_tbl_to_idx(tAVDT_TC_TBL *p_tbl); +extern void avdt_ad_tc_close_ind(tAVDT_TC_TBL *p_tbl, UINT16 reason); +extern void avdt_ad_tc_open_ind(tAVDT_TC_TBL *p_tbl); +extern void avdt_ad_tc_cong_ind(tAVDT_TC_TBL *p_tbl, BOOLEAN is_congested); +extern void avdt_ad_tc_data_ind(tAVDT_TC_TBL *p_tbl, BT_HDR *p_buf); +extern tAVDT_TC_TBL *avdt_ad_tc_tbl_by_type(UINT8 type, tAVDT_CCB *p_ccb, tAVDT_SCB *p_scb); +extern UINT8 avdt_ad_write_req(UINT8 type, tAVDT_CCB *p_ccb, tAVDT_SCB *p_scb, BT_HDR *p_buf); +extern void avdt_ad_open_req(UINT8 type, tAVDT_CCB *p_ccb, tAVDT_SCB *p_scb, UINT8 role); +extern void avdt_ad_close_req(UINT8 type, tAVDT_CCB *p_ccb, tAVDT_SCB *p_scb); + +extern void avdt_process_timeout(TIMER_LIST_ENT *p_tle); + +/***************************************************************************** +** macros +*****************************************************************************/ + +/* we store the scb and the label in the layer_specific field of the +** current cmd +*/ +#define AVDT_BLD_LAYERSPEC(ls, msg, label) \ + ls = (((label) << 4) | (msg)) + +#define AVDT_LAYERSPEC_LABEL(ls) ((UINT8)((ls) >> 4)) + +#define AVDT_LAYERSPEC_MSG(ls) ((UINT8)((ls) & 0x000F)) + +/***************************************************************************** +** global data +*****************************************************************************/ +#ifdef __cplusplus +extern "C" +{ +#endif + +/****************************************************************************** +** Main Control Block +*******************************************************************************/ +#if AVDT_DYNAMIC_MEMORY == FALSE +extern tAVDT_CB avdt_cb; +#else +extern tAVDT_CB *avdt_cb_ptr; +#define avdt_cb (*avdt_cb_ptr) +#endif + + +/* L2CAP callback registration structure */ +extern const tL2CAP_APPL_INFO avdt_l2c_appl; + +/* reject message event lookup table */ +extern const UINT8 avdt_msg_rej_2_evt[]; +#ifdef __cplusplus +} +#endif + +#endif ///AVRC_INCLUDED == TRUE + +#endif /* AVDT_INT_H */ diff --git a/lib/bt/host/bluedroid/stack/avrc/avrc_api.c b/lib/bt/host/bluedroid/stack/avrc/avrc_api.c new file mode 100644 index 00000000..9972bbfd --- /dev/null +++ b/lib/bt/host/bluedroid/stack/avrc/avrc_api.c @@ -0,0 +1,1106 @@ +/****************************************************************************** + * + * Copyright (C) 2003-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * Interface to AVRCP mandatory commands + * + ******************************************************************************/ +#include +#include "common/bt_trace.h" +#include +#include "common/bt_target.h" +#include "stack/avrc_api.h" +#include "avrc_int.h" +#include "osi/allocator.h" + +#if (defined(AVRC_INCLUDED) && AVRC_INCLUDED == TRUE) + +/***************************************************************************** +** Global data +*****************************************************************************/ + + +#define AVRC_MAX_RCV_CTRL_EVT AVCT_BROWSE_UNCONG_IND_EVT + +#ifndef MAX +#define MAX(a, b) ((a) > (b) ? (a) : (b)) +#endif + +static const UINT8 avrc_ctrl_event_map[] = { + AVRC_OPEN_IND_EVT, /* AVCT_CONNECT_CFM_EVT */ + AVRC_OPEN_IND_EVT, /* AVCT_CONNECT_IND_EVT */ + AVRC_CLOSE_IND_EVT, /* AVCT_DISCONNECT_CFM_EVT */ + AVRC_CLOSE_IND_EVT, /* AVCT_DISCONNECT_IND_EVT */ + AVRC_CONG_IND_EVT, /* AVCT_CONG_IND_EVT */ + AVRC_UNCONG_IND_EVT,/* AVCT_UNCONG_IND_EVT */ + AVRC_BROWSE_OPEN_IND_EVT, /* AVCT_BROWSE_CONN_CFM_EVT */ + AVRC_BROWSE_OPEN_IND_EVT, /* AVCT_BROWSE_CONN_IND_EVT */ + AVRC_BROWSE_CLOSE_IND_EVT, /* AVCT_BROWSE_DISCONN_CFM_EVT */ + AVRC_BROWSE_CLOSE_IND_EVT, /* AVCT_BROWSE_DISCONN_IND_EVT */ + AVRC_BROWSE_CONG_IND_EVT, /* AVCT_BROWSE_CONG_IND_EVT */ + AVRC_BROWSE_UNCONG_IND_EVT /* AVCT_BROWSE_UNCONG_IND_EVT */ +}; + +#define AVRC_OP_DROP 0xFE /* use this unused opcode to indication no need to call the callback function */ +#define AVRC_OP_DROP_N_FREE 0xFD /* use this unused opcode to indication no need to call the callback function & free buffer */ + +#define AVRC_OP_UNIT_INFO_RSP_LEN 8 +#define AVRC_OP_SUB_UNIT_INFO_RSP_LEN 8 +#define AVRC_OP_REJ_MSG_LEN 11 + +/****************************************************************************** +** +** Function avrc_ctrl_cback +** +** Description This is the callback function used by AVCTP to report +** received link events. +** +** Returns Nothing. +** +******************************************************************************/ +static void avrc_ctrl_cback(UINT8 handle, UINT8 event, UINT16 result, + BD_ADDR peer_addr) +{ + UINT8 avrc_event; + + if (event <= AVRC_MAX_RCV_CTRL_EVT && avrc_cb.ccb[handle].p_ctrl_cback) { + avrc_event = avrc_ctrl_event_map[event]; + if (event == AVCT_CONNECT_CFM_EVT) { + if (result != 0) { /* failed */ + avrc_event = AVRC_CLOSE_IND_EVT; + } + } + (*avrc_cb.ccb[handle].p_ctrl_cback)(handle, avrc_event, result, peer_addr); + } + /* else drop the unknown event*/ +} + +/****************************************************************************** +** +** Function avrc_get_data_ptr +** +** Description Gets a pointer to the data payload in the packet. +** +** Returns A pointer to the data payload. +** +******************************************************************************/ +static UINT8 *avrc_get_data_ptr(BT_HDR *p_pkt) +{ + return (UINT8 *)(p_pkt + 1) + p_pkt->offset; +} + +/****************************************************************************** +** +** Function avrc_copy_packet +** +** Description Copies an AVRC packet to a new buffer. In the new buffer, +** the payload offset is at least AVCT_MSG_OFFSET octets. +** +** Returns The buffer with the copied data. +** +******************************************************************************/ +static BT_HDR *avrc_copy_packet(BT_HDR *p_pkt, int rsp_pkt_len) +{ + const int offset = MAX(AVCT_MSG_OFFSET, p_pkt->offset); + const int pkt_len = MAX(rsp_pkt_len, p_pkt->len); + BT_HDR *p_pkt_copy = + (BT_HDR *)osi_malloc((UINT16)(BT_HDR_SIZE + offset + pkt_len)); + + /* Copy the packet header, set the new offset, and copy the payload */ + if (p_pkt_copy != NULL) { + memcpy(p_pkt_copy, p_pkt, BT_HDR_SIZE); + p_pkt_copy->offset = offset; + UINT8 *p_data = avrc_get_data_ptr(p_pkt); + UINT8 *p_data_copy = avrc_get_data_ptr(p_pkt_copy); + memcpy(p_data_copy, p_data, p_pkt->len); + } + + return p_pkt_copy; +} + +#if (AVRC_METADATA_INCLUDED == TRUE) +/****************************************************************************** +** +** Function avrc_prep_end_frag +** +** Description This function prepares an end response fragment +** +** Returns Nothing. +** +******************************************************************************/ +static void avrc_prep_end_frag(UINT8 handle) +{ + tAVRC_FRAG_CB *p_fcb; + BT_HDR *p_pkt_new; + UINT8 *p_data, *p_orig_data; + UINT8 rsp_type; + + AVRC_TRACE_DEBUG ("avrc_prep_end_frag" ); + p_fcb = &avrc_cb.fcb[handle]; + + /* The response type of the end fragment should be the same as the the PDU of "End Fragment + ** Response" Errata: https://www.bluetooth.org/errata/errata_view.cfm?errata_id=4383 + */ + p_orig_data = ((UINT8 *)(p_fcb->p_fmsg + 1) + p_fcb->p_fmsg->offset); + rsp_type = ((*p_orig_data) & AVRC_CTYPE_MASK); + + p_pkt_new = p_fcb->p_fmsg; + p_pkt_new->len -= (AVRC_MAX_CTRL_DATA_LEN - AVRC_VENDOR_HDR_SIZE - AVRC_MIN_META_HDR_SIZE); + p_pkt_new->offset += (AVRC_MAX_CTRL_DATA_LEN - AVRC_VENDOR_HDR_SIZE - AVRC_MIN_META_HDR_SIZE); + p_data = (UINT8 *)(p_pkt_new + 1) + p_pkt_new->offset; + *p_data++ = rsp_type; + *p_data++ = (AVRC_SUB_PANEL << AVRC_SUBTYPE_SHIFT); + *p_data++ = AVRC_OP_VENDOR; + AVRC_CO_ID_TO_BE_STREAM(p_data, AVRC_CO_METADATA); + *p_data++ = p_fcb->frag_pdu; + *p_data++ = AVRC_PKT_END; + + /* 4=pdu, pkt_type & len */ + UINT16_TO_BE_STREAM(p_data, (p_pkt_new->len - AVRC_VENDOR_HDR_SIZE - AVRC_MIN_META_HDR_SIZE)); +} + +/****************************************************************************** +** +** Function avrc_send_continue_frag +** +** Description This function sends a continue response fragment +** +** Returns Nothing. +** +******************************************************************************/ +static void avrc_send_continue_frag(UINT8 handle, UINT8 label) +{ + tAVRC_FRAG_CB *p_fcb; + BT_HDR *p_pkt_old, *p_pkt; + UINT8 *p_old, *p_data; + UINT8 cr = AVCT_RSP; + tAVRC_RSP rej_rsp; + + p_fcb = &avrc_cb.fcb[handle]; + p_pkt = p_fcb->p_fmsg; + + AVRC_TRACE_DEBUG("%s handle = %u label = %u len = %d", + __func__, handle, label, p_pkt->len); + if (p_pkt->len > AVRC_MAX_CTRL_DATA_LEN) { + int offset_len = MAX(AVCT_MSG_OFFSET, p_pkt->offset); + p_pkt_old = p_fcb->p_fmsg; + p_pkt = (BT_HDR *)osi_malloc((UINT16)(AVRC_PACKET_LEN + offset_len + BT_HDR_SIZE)); + if (p_pkt) { + p_pkt->len = AVRC_MAX_CTRL_DATA_LEN; + p_pkt->offset = AVCT_MSG_OFFSET; + p_pkt->layer_specific = p_pkt_old->layer_specific; + p_pkt->event = p_pkt_old->event; + p_old = (UINT8 *)(p_pkt_old + 1) + p_pkt_old->offset; + p_data = (UINT8 *)(p_pkt + 1) + p_pkt->offset; + memcpy (p_data, p_old, AVRC_MAX_CTRL_DATA_LEN); + /* use AVRC continue packet type */ + p_data += AVRC_VENDOR_HDR_SIZE; + p_data++; /* pdu */ + *p_data++ = AVRC_PKT_CONTINUE; + /* 4=pdu, pkt_type & len */ + UINT16_TO_BE_STREAM(p_data, (AVRC_MAX_CTRL_DATA_LEN - AVRC_VENDOR_HDR_SIZE - 4)); + + /* prepare the left over for as an end fragment */ + avrc_prep_end_frag (handle); + } else { + /* use the current GKI buffer to send Internal error status */ + p_pkt = p_fcb->p_fmsg; + p_fcb->p_fmsg = NULL; + p_fcb->frag_enabled = FALSE; + AVRC_TRACE_ERROR ("AVRC_MsgReq no buffers for fragmentation - send internal error" ); + p_data = (UINT8 *)(p_pkt + 1) + p_pkt->offset; + *p_data++ = AVRC_PDU_REQUEST_CONTINUATION_RSP; + *p_data++ = 0; + UINT16_TO_BE_STREAM(p_data, 0); + p_pkt->len = 4; + rej_rsp.pdu = AVRC_PDU_REQUEST_CONTINUATION_RSP; + rej_rsp.status = AVRC_STS_INTERNAL_ERR; + AVRC_BldResponse( handle, (tAVRC_RESPONSE *)&rej_rsp, &p_pkt); + cr = AVCT_RSP; + } + } else { + /* end fragment. clean the control block */ + p_fcb->frag_enabled = FALSE; + p_fcb->p_fmsg = NULL; + } + AVCT_MsgReq( handle, label, cr, p_pkt); +} + +/****************************************************************************** +** +** Function avrc_proc_vendor_command +** +** Description This function processes received vendor command. +** +** Returns if not NULL, the response to send right away. +** +******************************************************************************/ +static BT_HDR *avrc_proc_vendor_command(UINT8 handle, UINT8 label, + BT_HDR *p_pkt, tAVRC_MSG_VENDOR *p_msg) +{ + BT_HDR *p_rsp = NULL; + UINT8 *p_data; + UINT8 *p_begin; + UINT8 pkt_type; + BOOLEAN abort_frag = FALSE; + tAVRC_STS status = AVRC_STS_NO_ERROR; + tAVRC_FRAG_CB *p_fcb; + + p_begin = (UINT8 *)(p_pkt + 1) + p_pkt->offset; + p_data = p_begin + AVRC_VENDOR_HDR_SIZE; + pkt_type = *(p_data + 1) & AVRC_PKT_TYPE_MASK; + + if (pkt_type != AVRC_PKT_SINGLE) { + /* reject - commands can only be in single packets at AVRCP level */ + AVRC_TRACE_ERROR ("commands must be in single packet pdu:0x%x", *p_data ); + /* use the current GKI buffer to send the reject */ + status = AVRC_STS_BAD_CMD; + } + /* check if there are fragments waiting to be sent */ + else if (avrc_cb.fcb[handle].frag_enabled) { + p_fcb = &avrc_cb.fcb[handle]; + if (p_msg->company_id == AVRC_CO_METADATA) { + switch (*p_data) { + case AVRC_PDU_ABORT_CONTINUATION_RSP: + /* aborted by CT - send accept response */ + abort_frag = TRUE; + p_begin = (UINT8 *)(p_pkt + 1) + p_pkt->offset; + *p_begin = (AVRC_RSP_ACCEPT & AVRC_CTYPE_MASK); + if (*(p_data + 4) != p_fcb->frag_pdu) { + *p_begin = (AVRC_RSP_REJ & AVRC_CTYPE_MASK); + *(p_data + 4) = AVRC_STS_BAD_PARAM; + } else { + p_data = (p_begin + AVRC_VENDOR_HDR_SIZE + 2); + UINT16_TO_BE_STREAM(p_data, 0); + p_pkt->len = (p_data - p_begin); + } + AVCT_MsgReq( handle, label, AVCT_RSP, p_pkt); + p_msg->hdr.opcode = AVRC_OP_DROP; /* used the p_pkt to send response */ + break; + + case AVRC_PDU_REQUEST_CONTINUATION_RSP: + if (*(p_data + 4) == p_fcb->frag_pdu) { + avrc_send_continue_frag(handle, label); + p_msg->hdr.opcode = AVRC_OP_DROP_N_FREE; + } else { + /* the pdu id does not match - reject the command using the current GKI buffer */ + AVRC_TRACE_ERROR("avrc_proc_vendor_command continue pdu: 0x%x does not match \ + current re-assembly pdu: 0x%x", + *(p_data + 4), p_fcb->frag_pdu); + status = AVRC_STS_BAD_PARAM; + abort_frag = TRUE; + } + break; + + default: + /* implicit abort */ + abort_frag = TRUE; + } + } else { + abort_frag = TRUE; + /* implicit abort */ + } + + if (abort_frag) { + if (p_fcb->p_fmsg) { + osi_free(p_fcb->p_fmsg); + p_fcb->p_fmsg = NULL; + } + p_fcb->frag_enabled = FALSE; + } + } + + if (status != AVRC_STS_NO_ERROR) { + /* use the current GKI buffer to build/send the reject message */ + p_data = (UINT8 *)(p_pkt + 1) + p_pkt->offset; + *p_data++ = AVRC_RSP_REJ; + p_data += AVRC_VENDOR_HDR_SIZE; /* pdu */ + *p_data++ = 0; /* pkt_type */ + UINT16_TO_BE_STREAM(p_data, 1); /* len */ + *p_data++ = status; /* error code */ + p_pkt->len = AVRC_VENDOR_HDR_SIZE + 5; + p_rsp = p_pkt; + } + + return p_rsp; +} + +/****************************************************************************** +** +** Function avrc_proc_far_msg +** +** Description This function processes metadata fragmenation +** and reassembly +** +** Returns 0, to report the message with msg_cback . +** +******************************************************************************/ +static UINT8 avrc_proc_far_msg(UINT8 handle, UINT8 label, UINT8 cr, BT_HDR **pp_pkt, + tAVRC_MSG_VENDOR *p_msg) +{ + BT_HDR *p_pkt = *pp_pkt; + UINT8 *p_data; + UINT8 drop_code = 0; + BT_HDR *p_rsp = NULL; + BT_HDR *p_cmd = NULL; + BOOLEAN req_continue = FALSE; + BT_HDR *p_pkt_new = NULL; + UINT8 pkt_type; + tAVRC_RASM_CB *p_rcb; + tAVRC_NEXT_CMD avrc_cmd; + + p_data = (UINT8 *)(p_pkt + 1) + p_pkt->offset; + + /* Skip over vendor header (ctype, subunit*, opcode, CO_ID) */ + p_data += AVRC_VENDOR_HDR_SIZE; + + pkt_type = *(p_data + 1) & AVRC_PKT_TYPE_MASK; + AVRC_TRACE_DEBUG ("pkt_type %d", pkt_type ); + p_rcb = &avrc_cb.rcb[handle]; + if (p_msg->company_id == AVRC_CO_METADATA) { + /* check if the message needs to be re-assembled */ + if (pkt_type == AVRC_PKT_SINGLE || pkt_type == AVRC_PKT_START) { + /* previous fragments need to be dropped, when received another new message */ + p_rcb->rasm_offset = 0; + if (p_rcb->p_rmsg) { + osi_free(p_rcb->p_rmsg); + p_rcb->p_rmsg = NULL; + } + } + + if (pkt_type != AVRC_PKT_SINGLE && cr == AVCT_RSP) { + /* not a single response packet - need to re-assemble metadata messages */ + if (pkt_type == AVRC_PKT_START) { + /* Allocate buffer for re-assembly */ + p_rcb->rasm_pdu = *p_data; + if ((p_rcb->p_rmsg = (BT_HDR *)osi_malloc(BT_DEFAULT_BUFFER_SIZE)) != NULL) { + /* Copy START packet to buffer for re-assembling fragments*/ + memcpy(p_rcb->p_rmsg, p_pkt, sizeof(BT_HDR)); /* Copy bt hdr */ + + /* Copy metadata message */ + memcpy((UINT8 *)(p_rcb->p_rmsg + 1), + (UINT8 *)(p_pkt + 1) + p_pkt->offset, p_pkt->len); + + /* offset of start of metadata response in reassembly buffer */ + p_rcb->p_rmsg->offset = p_rcb->rasm_offset = 0; + + /* Free original START packet, replace with pointer to reassembly buffer */ + osi_free(p_pkt); + *pp_pkt = p_rcb->p_rmsg; + } else { + /* Unable to allocate buffer for fragmented avrc message. Reuse START + buffer for reassembly (re-assembled message may fit into ACL buf) */ + AVRC_TRACE_DEBUG ("Unable to allocate buffer for fragmented avrc message, \ + reusing START buffer for reassembly"); + p_rcb->rasm_offset = p_pkt->offset; + p_rcb->p_rmsg = p_pkt; + } + + /* set offset to point to where to copy next - use the same re-asm logic as AVCT */ + p_rcb->p_rmsg->offset += p_rcb->p_rmsg->len; + req_continue = TRUE; + } else if (p_rcb->p_rmsg == NULL) { + /* Received a CONTINUE/END, but no corresponding START + (or previous fragmented response was dropped) */ + AVRC_TRACE_DEBUG ("Received a CONTINUE/END without no corresponding START \ + (or previous fragmented response was dropped)"); + drop_code = 5; + osi_free(p_pkt); + *pp_pkt = NULL; + } else { + /* get size of buffer holding assembled message */ + /* + * NOTE: The buffer is allocated above at the beginning of the + * reassembly, and is always of size BT_DEFAULT_BUFFER_SIZE. + */ + UINT16 buf_len = BT_DEFAULT_BUFFER_SIZE - sizeof(BT_HDR); + /* adjust offset and len of fragment for header byte */ + p_pkt->offset += (AVRC_VENDOR_HDR_SIZE + AVRC_MIN_META_HDR_SIZE); + p_pkt->len -= (AVRC_VENDOR_HDR_SIZE + AVRC_MIN_META_HDR_SIZE); + /* verify length */ + if ((p_rcb->p_rmsg->offset + p_pkt->len) > buf_len) { + AVRC_TRACE_WARNING("Fragmented message too big! - report the partial message"); + p_pkt->len = buf_len - p_rcb->p_rmsg->offset; + pkt_type = AVRC_PKT_END; + } + + /* copy contents of p_pkt to p_rx_msg */ + memcpy((UINT8 *)(p_rcb->p_rmsg + 1) + p_rcb->p_rmsg->offset, + (UINT8 *)(p_pkt + 1) + p_pkt->offset, p_pkt->len); + + if (pkt_type == AVRC_PKT_END) { + p_rcb->p_rmsg->offset = p_rcb->rasm_offset; + p_rcb->p_rmsg->len += p_pkt->len; + p_pkt_new = p_rcb->p_rmsg; + p_rcb->rasm_offset = 0; + p_rcb->p_rmsg = NULL; + p_msg->p_vendor_data = (UINT8 *)(p_pkt_new + 1) + p_pkt_new->offset; + p_msg->hdr.ctype = p_msg->p_vendor_data[0] & AVRC_CTYPE_MASK; + /* 6 = ctype, subunit*, opcode & CO_ID */ + p_msg->p_vendor_data += AVRC_VENDOR_HDR_SIZE; + p_msg->vendor_len = p_pkt_new->len - AVRC_VENDOR_HDR_SIZE; + p_data = p_msg->p_vendor_data + 1; /* skip pdu */ + *p_data++ = AVRC_PKT_SINGLE; + UINT16_TO_BE_STREAM(p_data, (p_msg->vendor_len - AVRC_MIN_META_HDR_SIZE)); + AVRC_TRACE_DEBUG("end frag:%d, total len:%d, offset:%d", p_pkt->len, + p_pkt_new->len, p_pkt_new->offset); + } else { + p_rcb->p_rmsg->offset += p_pkt->len; + p_rcb->p_rmsg->len += p_pkt->len; + p_pkt_new = NULL; + req_continue = TRUE; + } + osi_free(p_pkt); + *pp_pkt = p_pkt_new; + } + } + + if (cr == AVCT_CMD) { + p_rsp = avrc_proc_vendor_command(handle, label, *pp_pkt, p_msg); + if (p_rsp) { + AVCT_MsgReq( handle, label, AVCT_RSP, p_rsp); + drop_code = 3; + } else if (p_msg->hdr.opcode == AVRC_OP_DROP) { + drop_code = 1; + } else if (p_msg->hdr.opcode == AVRC_OP_DROP_N_FREE) { + drop_code = 4; + } + + } else if (cr == AVCT_RSP && req_continue == TRUE) { + avrc_cmd.pdu = AVRC_PDU_REQUEST_CONTINUATION_RSP; + avrc_cmd.status = AVRC_STS_NO_ERROR; + avrc_cmd.target_pdu = p_rcb->rasm_pdu; + if (AVRC_BldCommand ((tAVRC_COMMAND *)&avrc_cmd, &p_cmd) == AVRC_STS_NO_ERROR) { + drop_code = 2; + AVRC_MsgReq (handle, (UINT8)(label), AVRC_CMD_CTRL, p_cmd); + } + } + } + + return drop_code; +} +#endif /* (AVRC_METADATA_INCLUDED == TRUE) */ + +/****************************************************************************** +** +** Function avrc_msg_cback +** +** Description This is the callback function used by AVCTP to report +** received AV control messages. +** +** Returns Nothing. +** +******************************************************************************/ +static void avrc_msg_cback(UINT8 handle, UINT8 label, UINT8 cr, + BT_HDR *p_pkt) +{ + UINT8 opcode; + tAVRC_MSG msg; + UINT8 *p_data; + UINT8 *p_begin; + BOOLEAN drop = FALSE; + BOOLEAN do_free = TRUE; + BT_HDR *p_rsp = NULL; + UINT8 *p_rsp_data; + int xx; + BOOLEAN reject = FALSE; +#if (BT_USE_TRACES == TRUE) + char *p_drop_msg = "dropped"; +#endif + tAVRC_MSG_VENDOR *p_msg = &msg.vendor; + + if (cr == AVCT_CMD && + (p_pkt->layer_specific & AVCT_DATA_CTRL && AVRC_PACKET_LEN < sizeof(p_pkt->len))) { + /* Ignore the invalid AV/C command frame */ +#if (BT_USE_TRACES == TRUE) + p_drop_msg = "dropped - too long AV/C cmd frame size"; +#endif + osi_free(p_pkt); + return; + } + + if (cr == AVCT_REJ) { + /* The peer thinks that this PID is no longer open - remove this handle */ + /* */ + osi_free(p_pkt); + AVCT_RemoveConn(handle); + return; + } + + p_data = (UINT8 *)(p_pkt + 1) + p_pkt->offset; + memset(&msg, 0, sizeof(tAVRC_MSG) ); + { + msg.hdr.ctype = p_data[0] & AVRC_CTYPE_MASK; + AVRC_TRACE_DEBUG("avrc_msg_cback handle:%d, ctype:%d, offset:%d, len: %d", + handle, msg.hdr.ctype, p_pkt->offset, p_pkt->len); + msg.hdr.subunit_type = (p_data[1] & AVRC_SUBTYPE_MASK) >> AVRC_SUBTYPE_SHIFT; + msg.hdr.subunit_id = p_data[1] & AVRC_SUBID_MASK; + opcode = p_data[2]; + } + + if ( ((avrc_cb.ccb[handle].control & AVRC_CT_TARGET) && (cr == AVCT_CMD)) || + ((avrc_cb.ccb[handle].control & AVRC_CT_CONTROL) && (cr == AVCT_RSP)) ) { + + switch (opcode) { + case AVRC_OP_UNIT_INFO: + if (cr == AVCT_CMD) { + /* send the response to the peer */ + p_rsp = avrc_copy_packet(p_pkt, AVRC_OP_UNIT_INFO_RSP_LEN); + p_rsp_data = avrc_get_data_ptr(p_rsp); + *p_rsp_data = AVRC_RSP_IMPL_STBL; + /* check & set the offset. set response code, set subunit_type & subunit_id, + set AVRC_OP_UNIT_INFO */ + /* 3 bytes: ctype, subunit*, opcode */ + p_rsp_data += AVRC_AVC_HDR_SIZE; + *p_rsp_data++ = 7; + /* Panel subunit & id=0 */ + *p_rsp_data++ = (AVRC_SUB_PANEL << AVRC_SUBTYPE_SHIFT); + AVRC_CO_ID_TO_BE_STREAM(p_rsp_data, avrc_cb.ccb[handle].company_id); + p_rsp->len = (UINT16) (p_rsp_data - (UINT8 *)(p_rsp + 1) - p_rsp->offset); + cr = AVCT_RSP; +#if (BT_USE_TRACES == TRUE) + p_drop_msg = "auto respond"; +#endif + } else { + /* parse response */ + p_data += 4; /* 3 bytes: ctype, subunit*, opcode + octet 3 (is 7)*/ + msg.unit.unit_type = (*p_data & AVRC_SUBTYPE_MASK) >> AVRC_SUBTYPE_SHIFT; + msg.unit.unit = *p_data & AVRC_SUBID_MASK; + p_data++; + AVRC_BE_STREAM_TO_CO_ID(msg.unit.company_id, p_data); + } + break; + + case AVRC_OP_SUB_INFO: + if (cr == AVCT_CMD) { + /* send the response to the peer */ + p_rsp = avrc_copy_packet(p_pkt, AVRC_OP_SUB_UNIT_INFO_RSP_LEN); + p_rsp_data = avrc_get_data_ptr(p_rsp); + *p_rsp_data = AVRC_RSP_IMPL_STBL; + /* check & set the offset. set response code, set (subunit_type & subunit_id), + set AVRC_OP_SUB_INFO, set (page & extention code) */ + p_rsp_data += 4; + /* Panel subunit & id=0 */ + *p_rsp_data++ = (AVRC_SUB_PANEL << AVRC_SUBTYPE_SHIFT); + memset(p_rsp_data, AVRC_CMD_OPRND_PAD, AVRC_SUBRSP_OPRND_BYTES); + p_rsp_data += AVRC_SUBRSP_OPRND_BYTES; + p_rsp->len = (UINT16) (p_rsp_data - (UINT8 *)(p_rsp + 1) - p_rsp->offset); + cr = AVCT_RSP; +#if (BT_USE_TRACES == TRUE) + p_drop_msg = "auto responded"; +#endif + } else { + /* parse response */ + p_data += AVRC_AVC_HDR_SIZE; /* 3 bytes: ctype, subunit*, opcode */ + msg.sub.page = (*p_data++ >> AVRC_SUB_PAGE_SHIFT) & AVRC_SUB_PAGE_MASK; + xx = 0; + while (*p_data != AVRC_CMD_OPRND_PAD && xx < AVRC_SUB_TYPE_LEN) { + msg.sub.subunit_type[xx] = *p_data++ >> AVRC_SUBTYPE_SHIFT; + if (msg.sub.subunit_type[xx] == AVRC_SUB_PANEL) { + msg.sub.panel = TRUE; + } + xx++; + } + } + break; + + case AVRC_OP_VENDOR: + p_data = (UINT8 *)(p_pkt + 1) + p_pkt->offset; + p_begin = p_data; + if (p_pkt->len < AVRC_VENDOR_HDR_SIZE) { /* 6 = ctype, subunit*, opcode & CO_ID */ + if (cr == AVCT_CMD) { + reject = TRUE; + } else { + drop = TRUE; + } + break; + } + p_data += AVRC_AVC_HDR_SIZE; /* skip the first 3 bytes: ctype, subunit*, opcode */ + AVRC_BE_STREAM_TO_CO_ID(p_msg->company_id, p_data); + p_msg->p_vendor_data = p_data; + p_msg->vendor_len = p_pkt->len - (p_data - p_begin); + +#if (AVRC_METADATA_INCLUDED == TRUE) + UINT8 drop_code = 0; + if (p_msg->company_id == AVRC_CO_METADATA) { + /* Validate length for metadata message */ + if (p_pkt->len < (AVRC_VENDOR_HDR_SIZE + AVRC_MIN_META_HDR_SIZE)) { + if (cr == AVCT_CMD) { + reject = TRUE; + } else { + drop = TRUE; + } + break; + } + + /* Check+handle fragmented messages */ + drop_code = avrc_proc_far_msg(handle, label, cr, &p_pkt, p_msg); + if (drop_code > 0) { + drop = TRUE; + } + } + if (drop_code > 0) { + if (drop_code != 4) { + do_free = FALSE; + } +#if (BT_USE_TRACES == TRUE) + switch (drop_code) { + case 1: + p_drop_msg = "sent_frag"; + break; + case 2: + p_drop_msg = "req_cont"; + break; + case 3: + p_drop_msg = "sent_frag3"; + break; + case 4: + p_drop_msg = "sent_frag_free"; + break; + default: + p_drop_msg = "sent_fragd"; + } +#endif + } +#endif /* (AVRC_METADATA_INCLUDED == TRUE) */ + break; + + case AVRC_OP_PASS_THRU: + if (p_pkt->len < 5) { /* 3 bytes: ctype, subunit*, opcode & op_id & len */ + if (cr == AVCT_CMD) { + reject = TRUE; + } else { + drop = TRUE; + } + break; + } + p_data += AVRC_AVC_HDR_SIZE; /* skip the first 3 bytes: ctype, subunit*, opcode */ + msg.pass.op_id = (AVRC_PASS_OP_ID_MASK & *p_data); + if (AVRC_PASS_STATE_MASK & *p_data) { + msg.pass.state = TRUE; + } else { + msg.pass.state = FALSE; + } + p_data++; + msg.pass.pass_len = *p_data++; + if (msg.pass.pass_len != p_pkt->len - 5) { + msg.pass.pass_len = p_pkt->len - 5; + } + if (msg.pass.pass_len) { + msg.pass.p_pass_data = p_data; + } else { + msg.pass.p_pass_data = NULL; + } + break; + + + default: + if ((avrc_cb.ccb[handle].control & AVRC_CT_TARGET) && (cr == AVCT_CMD)) { + /* reject unsupported opcode */ + reject = TRUE; + } + drop = TRUE; + break; + } + } else { /* drop the event */ + drop = TRUE; + } + + if (reject) { + /* reject unsupported opcode */ + p_rsp = avrc_copy_packet(p_pkt, AVRC_OP_REJ_MSG_LEN); + p_rsp_data = avrc_get_data_ptr(p_rsp); + *p_rsp_data = AVRC_RSP_REJ; +#if (BT_USE_TRACES == TRUE) + p_drop_msg = "rejected"; +#endif + cr = AVCT_RSP; + drop = TRUE; + } + + if (p_rsp) { + /* set to send response right away */ + AVCT_MsgReq( handle, label, cr, p_rsp); + drop = TRUE; + } + + if (drop == FALSE) { + msg.hdr.opcode = opcode; + (*avrc_cb.ccb[handle].p_msg_cback)(handle, label, opcode, &msg); + } +#if (BT_USE_TRACES == TRUE) + else { + AVRC_TRACE_WARNING("avrc_msg_cback %s msg handle:%d, control:%d, cr:%d, opcode:x%x", + p_drop_msg, + handle, avrc_cb.ccb[handle].control, cr, opcode); + } +#endif + + + if (do_free) { + osi_free(p_pkt); + } +} + + + + +/****************************************************************************** +** +** Function avrc_pass_msg +** +** Description Compose a PASS THROUGH command according to p_msg +** +** Input Parameters: +** p_msg: Pointer to PASS THROUGH message structure. +** +** Output Parameters: +** None. +** +** Returns pointer to a valid GKI buffer if successful. +** NULL if p_msg is NULL. +** +******************************************************************************/ +static BT_HDR *avrc_pass_msg(tAVRC_MSG_PASS *p_msg) +{ + BT_HDR *p_cmd = NULL; + UINT8 *p_data; + + assert(p_msg != NULL); + assert(AVRC_CMD_BUF_SIZE > (AVRC_MIN_CMD_LEN+p_msg->pass_len)); + + if ((p_cmd = (BT_HDR *) osi_malloc(AVRC_CMD_BUF_SIZE)) != NULL) { + p_cmd->offset = AVCT_MSG_OFFSET; + p_cmd->layer_specific = AVCT_DATA_CTRL; + p_data = (UINT8 *)(p_cmd + 1) + p_cmd->offset; + *p_data++ = (p_msg->hdr.ctype & AVRC_CTYPE_MASK); + *p_data++ = (AVRC_SUB_PANEL << AVRC_SUBTYPE_SHIFT); /* Panel subunit & id=0 */ + *p_data++ = AVRC_OP_PASS_THRU; + *p_data = (AVRC_PASS_OP_ID_MASK & p_msg->op_id); + if (p_msg->state) { + *p_data |= AVRC_PASS_STATE_MASK; + } + p_data++; + + if (p_msg->op_id == AVRC_ID_VENDOR) { + *p_data++ = p_msg->pass_len; + if (p_msg->pass_len && p_msg->p_pass_data) { + memcpy(p_data, p_msg->p_pass_data, p_msg->pass_len); + p_data += p_msg->pass_len; + } + } else { /* set msg len to 0 for other op_id */ + /* set msg len to 0 for other op_id */ + *p_data++ = 0; + } + p_cmd->len = (UINT16) (p_data - (UINT8 *)(p_cmd + 1) - p_cmd->offset); + } + return p_cmd; +} + +/****************************************************************************** +** +** Function AVRC_Open +** +** Description This function is called to open a connection to AVCTP. +** The connection can be either an initiator or acceptor, as +** determined by the p_ccb->stream parameter. +** The connection can be a target, a controller or for both role, +** as determined by the p_ccb->control parameter. +** By definition, a target connection is an acceptor connection +** that waits for an incoming AVCTP connection from the peer. +** The connection remains available to the application until +** the application closes it by calling AVRC_Close(). The +** application does not need to reopen the connection after an +** AVRC_CLOSE_IND_EVT is received. +** +** Input Parameters: +** p_ccb->company_id: Company Identifier. +** +** p_ccb->p_ctrl_cback: Pointer to control callback function. +** +** p_ccb->p_msg_cback: Pointer to message callback function. +** +** p_ccb->conn: AVCTP connection role. This is set to +** AVCTP_INT for initiator connections and AVCTP_ACP +** for acceptor connections. +** +** p_ccb->control: Control role. This is set to +** AVRC_CT_TARGET for target connections, AVRC_CT_CONTROL +** for control connections or (AVRC_CT_TARGET|AVRC_CT_CONTROL) +** for connections that support both roles. +** +** peer_addr: BD address of peer device. This value is +** only used for initiator connections; for acceptor +** connections it can be set to NULL. +** +** Output Parameters: +** p_handle: Pointer to handle. This parameter is only +** valid if AVRC_SUCCESS is returned. +** +** Returns AVRC_SUCCESS if successful. +** AVRC_NO_RESOURCES if there are not enough resources to open +** the connection. +** +******************************************************************************/ +UINT16 AVRC_Open(UINT8 *p_handle, tAVRC_CONN_CB *p_ccb, BD_ADDR_PTR peer_addr) +{ + UINT16 status; + tAVCT_CC cc; + + cc.p_ctrl_cback = avrc_ctrl_cback; /* Control callback */ + cc.p_msg_cback = avrc_msg_cback; /* Message callback */ + cc.pid = UUID_SERVCLASS_AV_REMOTE_CONTROL; /* Profile ID */ + cc.role = p_ccb->conn; /* Initiator/acceptor role */ + cc.control = p_ccb->control; /* Control role (Control/Target) */ + + status = AVCT_CreateConn(p_handle, &cc, peer_addr); + if (status == AVCT_SUCCESS) { + memcpy(&avrc_cb.ccb[*p_handle], p_ccb, sizeof(tAVRC_CONN_CB)); +#if (AVRC_METADATA_INCLUDED == TRUE) + memset(&avrc_cb.fcb[*p_handle], 0, sizeof(tAVRC_FRAG_CB)); + memset(&avrc_cb.rcb[*p_handle], 0, sizeof(tAVRC_RASM_CB)); +#endif + } + AVRC_TRACE_DEBUG("AVRC_Open role: %d, control:%d status:%d, handle:%d", cc.role, cc.control, + status, *p_handle); + + return status; +} + +/****************************************************************************** +** +** Function AVRC_Close +** +** Description Close a connection opened with AVRC_Open(). +** This function is called when the +** application is no longer using a connection. +** +** Input Parameters: +** handle: Handle of this connection. +** +** Output Parameters: +** None. +** +** Returns AVRC_SUCCESS if successful. +** AVRC_BAD_HANDLE if handle is invalid. +** +******************************************************************************/ +UINT16 AVRC_Close(UINT8 handle) +{ + AVRC_TRACE_DEBUG("AVRC_Close handle:%d", handle); + return AVCT_RemoveConn(handle); +} + + +/****************************************************************************** +** +** Function AVRC_MsgReq +** +** Description This function is used to send the AVRCP byte stream in p_pkt +** down to AVCTP. +** +** It is expected that p_pkt->offset is at least AVCT_MSG_OFFSET +** p_pkt->layer_specific is AVCT_DATA_CTRL or AVCT_DATA_BROWSE +** p_pkt->event is AVRC_OP_VENDOR, AVRC_OP_PASS_THRU or AVRC_OP_BROWSE +** The above BT_HDR settings are set by the AVRC_Bld* functions. +** +** Returns AVRC_SUCCESS if successful. +** AVRC_BAD_HANDLE if handle is invalid. +** +******************************************************************************/ +UINT16 AVRC_MsgReq (UINT8 handle, UINT8 label, UINT8 ctype, BT_HDR *p_pkt) +{ +#if (AVRC_METADATA_INCLUDED == TRUE) + UINT8 *p_data; + UINT8 cr = AVCT_CMD; + BOOLEAN chk_frag = TRUE; + UINT8 *p_start = NULL; + tAVRC_FRAG_CB *p_fcb; + UINT16 len; + BT_HDR *p_pkt_new; + + if (!p_pkt) { + return AVRC_BAD_PARAM; + } + + AVRC_TRACE_DEBUG("%s handle = %u label = %u ctype = %u len = %d", + __func__, handle, label, ctype, p_pkt->len); + + if (ctype >= AVRC_RSP_NOT_IMPL) { + cr = AVCT_RSP; + } + + if (p_pkt->event == AVRC_OP_VENDOR) { + /* add AVRCP Vendor Dependent headers */ + p_start = ((UINT8 *)(p_pkt + 1) + p_pkt->offset); + p_pkt->offset -= AVRC_VENDOR_HDR_SIZE; + p_pkt->len += AVRC_VENDOR_HDR_SIZE; + p_data = (UINT8 *)(p_pkt + 1) + p_pkt->offset; + *p_data++ = (ctype & AVRC_CTYPE_MASK); + *p_data++ = (AVRC_SUB_PANEL << AVRC_SUBTYPE_SHIFT); + *p_data++ = AVRC_OP_VENDOR; + AVRC_CO_ID_TO_BE_STREAM(p_data, AVRC_CO_METADATA); + } else if (p_pkt->event == AVRC_OP_PASS_THRU) { + /* add AVRCP Pass Through headers */ + p_start = ((UINT8 *)(p_pkt + 1) + p_pkt->offset); + p_pkt->offset -= AVRC_PASS_THRU_SIZE; + p_pkt->len += AVRC_PASS_THRU_SIZE; + p_data = (UINT8 *)(p_pkt + 1) + p_pkt->offset; + *p_data++ = (ctype & AVRC_CTYPE_MASK); + *p_data++ = (AVRC_SUB_PANEL << AVRC_SUBTYPE_SHIFT); + *p_data++ = AVRC_OP_PASS_THRU;/* opcode */ + *p_data++ = AVRC_ID_VENDOR; /* operation id */ + *p_data++ = 5; /* operation data len */ + AVRC_CO_ID_TO_BE_STREAM(p_data, AVRC_CO_METADATA); + } + + /* abandon previous fragments */ + p_fcb = &avrc_cb.fcb[handle]; + if (p_fcb->frag_enabled) { + p_fcb->frag_enabled = FALSE; + } + + if (p_fcb->p_fmsg) { + osi_free(p_fcb->p_fmsg); + p_fcb->p_fmsg = NULL; + } + + /* AVRCP spec has not defined any control channel commands that needs fragmentation at this level + * check for fragmentation only on the response */ + if ((cr == AVCT_RSP) && (chk_frag == TRUE)) { + if (p_pkt->len > AVRC_MAX_CTRL_DATA_LEN) { + int offset_len = MAX(AVCT_MSG_OFFSET, p_pkt->offset); + p_pkt_new = (BT_HDR *)osi_malloc((UINT16)(AVRC_PACKET_LEN + offset_len + + BT_HDR_SIZE)); + if (p_pkt_new && (p_start != NULL)) { + p_fcb->frag_enabled = TRUE; + p_fcb->p_fmsg = p_pkt; + p_fcb->frag_pdu = *p_start; + p_pkt = p_pkt_new; + p_pkt_new = p_fcb->p_fmsg; + p_pkt->len = AVRC_MAX_CTRL_DATA_LEN; + p_pkt->offset = p_pkt_new->offset; + p_pkt->layer_specific = p_pkt_new->layer_specific; + p_pkt->event = p_pkt_new->event; + p_data = (UINT8 *)(p_pkt + 1) + p_pkt->offset; + p_start -= AVRC_VENDOR_HDR_SIZE; + memcpy (p_data, p_start, AVRC_MAX_CTRL_DATA_LEN); + /* use AVRC start packet type */ + p_data += AVRC_VENDOR_HDR_SIZE; + p_data++; /* pdu */ + *p_data++ = AVRC_PKT_START; + /* 4 pdu, pkt_type & len */ + len = (AVRC_MAX_CTRL_DATA_LEN - AVRC_VENDOR_HDR_SIZE - AVRC_MIN_META_HDR_SIZE); + UINT16_TO_BE_STREAM(p_data, len); + + /* prepare the left over for as an end fragment */ + avrc_prep_end_frag (handle); + AVRC_TRACE_DEBUG ("%s p_pkt len:%d/%d, next len:%d", __func__, + p_pkt->len, len, p_fcb->p_fmsg->len ); + } else { + AVRC_TRACE_ERROR ("AVRC_MsgReq no buffers for fragmentation" ); + osi_free(p_pkt); + return AVRC_NO_RESOURCES; + } + } + } + + return AVCT_MsgReq( handle, label, cr, p_pkt); +#else + return AVRC_NO_RESOURCES; +#endif +} + + +/****************************************************************************** +** +** Function AVRC_PassCmd +** +** Description Send a PASS THROUGH command to the peer device. This +** function can only be called for controller role connections. +** Any response message from the peer is passed back through +** the tAVRC_MSG_CBACK callback function. +** +** Input Parameters: +** handle: Handle of this connection. +** +** label: Transaction label. +** +** p_msg: Pointer to PASS THROUGH message structure. +** +** Output Parameters: +** None. +** +** Returns AVRC_SUCCESS if successful. +** AVRC_BAD_HANDLE if handle is invalid. +** +******************************************************************************/ +UINT16 AVRC_PassCmd(UINT8 handle, UINT8 label, tAVRC_MSG_PASS *p_msg) +{ + BT_HDR *p_buf; + assert(p_msg != NULL); + if (p_msg) { + p_msg->hdr.ctype = AVRC_CMD_CTRL; + p_buf = avrc_pass_msg(p_msg); + if (p_buf) { + return AVCT_MsgReq( handle, label, AVCT_CMD, p_buf); + } + } + return AVRC_NO_RESOURCES; +} + +/****************************************************************************** +** +** Function AVRC_PassRsp +** +** Description Send a PASS THROUGH response to the peer device. This +** function can only be called for target role connections. +** This function must be called when a PASS THROUGH command +** message is received from the peer through the +** tAVRC_MSG_CBACK callback function. +** +** Input Parameters: +** handle: Handle of this connection. +** +** label: Transaction label. Must be the same value as +** passed with the command message in the callback function. +** +** p_msg: Pointer to PASS THROUGH message structure. +** +** Output Parameters: +** None. +** +** Returns AVRC_SUCCESS if successful. +** AVRC_BAD_HANDLE if handle is invalid. +** +******************************************************************************/ +UINT16 AVRC_PassRsp(UINT8 handle, UINT8 label, tAVRC_MSG_PASS *p_msg) +{ + BT_HDR *p_buf; + assert(p_msg != NULL); + if (p_msg) { + p_buf = avrc_pass_msg(p_msg); + if (p_buf) { + return AVCT_MsgReq( handle, label, AVCT_RSP, p_buf); + } + } + return AVRC_NO_RESOURCES; +} + +#endif /* #if (defined(AVRC_INCLUDED) && AVRC_INCLUDED == TRUE) */ diff --git a/lib/bt/host/bluedroid/stack/avrc/avrc_bld_ct.c b/lib/bt/host/bluedroid/stack/avrc/avrc_bld_ct.c new file mode 100644 index 00000000..18106870 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/avrc/avrc_bld_ct.c @@ -0,0 +1,329 @@ +/****************************************************************************** + * + * Copyright (C) 2006-2013 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ +#include +#include "common/bt_target.h" +#include "stack/avrc_api.h" +#include "stack/avrc_defs.h" +#include "avrc_int.h" +#include "osi/allocator.h" + +#if (defined(AVRC_INCLUDED) && AVRC_INCLUDED == TRUE) + +/***************************************************************************** +** Global data +*****************************************************************************/ + + +#if (AVRC_METADATA_INCLUDED == TRUE) +/******************************************************************************* +** +** Function avrc_bld_next_cmd +** +** Description This function builds the Request Continue or Abort command. +** +** Returns AVRC_STS_NO_ERROR, if the command is built successfully +** Otherwise, the error code. +** +*******************************************************************************/ +static tAVRC_STS avrc_bld_next_cmd (tAVRC_NEXT_CMD *p_cmd, BT_HDR *p_pkt) +{ + UINT8 *p_data, *p_start; + + AVRC_TRACE_API("avrc_bld_next_cmd"); + + /* get the existing length, if any, and also the num attributes */ + p_start = (UINT8 *)(p_pkt + 1) + p_pkt->offset; + p_data = p_start + 2; /* pdu + rsvd */ + + /* add fixed lenth 1 - pdu_id (1) */ + UINT16_TO_BE_STREAM(p_data, 1); + UINT8_TO_BE_STREAM(p_data, p_cmd->target_pdu); + p_pkt->len = (p_data - p_start); + + return AVRC_STS_NO_ERROR; +} + +/***************************************************************************** +** the following commands are introduced in AVRCP 1.4 +*****************************************************************************/ + +#if (AVRC_ADV_CTRL_INCLUDED == TRUE) +/******************************************************************************* +** +** Function avrc_bld_set_abs_volume_cmd +** +** Description This function builds the Set Absolute Volume command. +** +** Returns AVRC_STS_NO_ERROR, if the command is built successfully +** Otherwise, the error code. +** +*******************************************************************************/ +static tAVRC_STS avrc_bld_set_abs_volume_cmd (tAVRC_SET_VOLUME_CMD *p_cmd, BT_HDR *p_pkt) +{ + UINT8 *p_data, *p_start; + + AVRC_TRACE_API("avrc_bld_set_abs_volume_cmd"); + /* get the existing length, if any, and also the num attributes */ + p_start = (UINT8 *)(p_pkt + 1) + p_pkt->offset; + p_data = p_start + 2; /* pdu + rsvd */ + /* add fixed lenth 1 - volume (1) */ + UINT16_TO_BE_STREAM(p_data, 1); + UINT8_TO_BE_STREAM(p_data, (AVRC_MAX_VOLUME & p_cmd->volume)); + p_pkt->len = (p_data - p_start); + return AVRC_STS_NO_ERROR; +} + +/******************************************************************************* +** +** Function avrc_bld_vol_change_notfn +** +** Description This function builds the register notification for volume change. +** +** Returns AVRC_STS_NO_ERROR, if the command is built successfully +** Otherwise, the error code. +** +*******************************************************************************/ +static tAVRC_STS avrc_bld_register_change_notfn(UINT8 event_id, UINT32 event_parameter, BT_HDR *p_pkt) +{ + UINT8 *p_data, *p_start; + + AVRC_TRACE_API("avrc_bld_vol_change"); + /* get the existing length, if any, and also the num attributes */ + // Set the notify value + p_start = (UINT8 *)(p_pkt + 1) + p_pkt->offset; + p_data = p_start + 2; /* pdu + rsvd */ + /* add fixed length 5 -*/ + UINT16_TO_BE_STREAM(p_data, 5); + UINT8_TO_BE_STREAM(p_data, event_id); + UINT32_TO_BE_STREAM(p_data, event_parameter); + p_pkt->len = (p_data - p_start); + return AVRC_STS_NO_ERROR; +} +#endif + +/******************************************************************************* +** +** Function avrc_bld_init_cmd_buffer +** +** Description This function initializes the command buffer based on PDU +** +** Returns NULL, if no GKI buffer or failure to build the message. +** Otherwise, the GKI buffer that contains the initialized message. +** +*******************************************************************************/ +static BT_HDR *avrc_bld_init_cmd_buffer(tAVRC_COMMAND *p_cmd) +{ + UINT8 opcode = avrc_opcode_from_pdu(p_cmd->pdu); + AVRC_TRACE_API("avrc_bld_init_cmd_buffer: pdu=%x, opcode=%x", p_cmd->pdu, opcode); + + UINT16 offset = 0; + switch (opcode) { + case AVRC_OP_PASS_THRU: + offset = AVRC_MSG_PASS_THRU_OFFSET; + break; + + case AVRC_OP_VENDOR: + offset = AVRC_MSG_VENDOR_OFFSET; + break; + } + + /* allocate and initialize the buffer */ + BT_HDR *p_pkt = (BT_HDR *)osi_malloc(AVRC_META_CMD_BUF_SIZE); + if (p_pkt) { + UINT8 *p_data, *p_start; + + p_pkt->layer_specific = AVCT_DATA_CTRL; + p_pkt->event = opcode; + p_pkt->offset = offset; + p_data = (UINT8 *)(p_pkt + 1) + p_pkt->offset; + p_start = p_data; + + /* pass thru - group navigation - has a two byte op_id, so dont do it here */ + if (opcode != AVRC_OP_PASS_THRU) { + *p_data++ = p_cmd->pdu; + } + + switch (opcode) { + case AVRC_OP_VENDOR: + /* reserved 0, packet_type 0 */ + UINT8_TO_BE_STREAM(p_data, 0); + /* continue to the next "case to add length */ + /* add fixed lenth - 0 */ + UINT16_TO_BE_STREAM(p_data, 0); + break; + } + + p_pkt->len = (p_data - p_start); + } + p_cmd->cmd.opcode = opcode; + return p_pkt; +} + +/******************************************************************************* +** +** Function avrc_bld_set_player_value_cmd +** +** Description This function builds the Set Player Application Value command. +** +** Returns AVRC_STS_NO_ERROR, if the command is built successfully +** Otherwise, the error code. +** +*******************************************************************************/ +static tAVRC_STS avrc_bld_set_player_value_cmd(tAVRC_SET_APP_VALUE_CMD *p_cmd, BT_HDR *p_pkt) +{ + UINT8 *p_data, *p_start; + + /* get the existing length, if any, and also the num attributes */ + p_start = (UINT8 *)(p_pkt + 1) + p_pkt->offset; + p_data = p_start + 2; /* pdu + rsvd */ + /* add length */ + UINT16_TO_BE_STREAM(p_data, 3); + /* Number of attributes */ + UINT8_TO_BE_STREAM(p_data, 1); + UINT8_TO_BE_STREAM(p_data, p_cmd->p_vals->attr_id); + UINT8_TO_BE_STREAM(p_data, p_cmd->p_vals->attr_val); + + p_pkt->len = (p_data - p_start); + return AVRC_STS_NO_ERROR; +} + +/******************************************************************************* +** +** Function avrc_bld_get_element_attr_cmd +** +** Description This function builds the Get Element Attribute command. +** +** Returns AVRC_STS_NO_ERROR, if the command is built successfully +** Otherwise, the error code. +** +*******************************************************************************/ +static tAVRC_STS avrc_bld_get_element_attr_cmd (tAVRC_GET_ELEM_ATTRS_CMD *p_cmd, BT_HDR *p_pkt) +{ + int i; + UINT8 *p_data, *p_start; + + AVRC_TRACE_API("avrc_bld_get_element_attr_cmd num_attr: %d", p_cmd->num_attr); + /* get the existing length, if any, and also the num attributes */ + p_start = (UINT8 *)(p_pkt + 1) + p_pkt->offset; + p_data = p_start + 2; /* pdu + rsvd */ + /* add length */ + UINT16_TO_BE_STREAM(p_data, 8 + 1 /* id + attr count */ + p_cmd->num_attr * sizeof(UINT32)); + /* Identifier 0x0 (PLAYING) */ + UINT64_TO_BE_STREAM(p_data, (UINT64)(0)); + /* Attribute count */ + UINT8_TO_BE_STREAM(p_data, p_cmd->num_attr); + + for (i = 0; i < p_cmd->num_attr; i++) { + AVRC_TRACE_API("avrc_bld_get_element_attr_cmd attr_id: %d", p_cmd->attrs[i]); + UINT32_TO_BE_STREAM(p_data, p_cmd->attrs[i]); + } + + p_pkt->len = (p_data - p_start); + return AVRC_STS_NO_ERROR; +} + +static tAVRC_STS avrc_bld_get_caps_cmd(tAVRC_GET_CAPS_CMD *p_cmd, BT_HDR *p_pkt) +{ + UINT8 *p_data, *p_start; + + AVRC_TRACE_API("avrc_bld_get_caps"); + /* get the existing length, if any, and also the num attributes */ + // Set the notify value + p_start = (UINT8 *)(p_pkt + 1) + p_pkt->offset; + p_data = p_start + 2; /* pdu + rsvd */ + /* add fixed length 1 */ + UINT16_TO_BE_STREAM(p_data, 1); + /* capability id */ + UINT8_TO_BE_STREAM(p_data, p_cmd->capability_id); + p_pkt->len = (p_data - p_start); + return AVRC_STS_NO_ERROR; +} +/******************************************************************************* +** +** Function AVRC_BldCommand +** +** Description This function builds the given AVRCP command to the given +** GKI buffer +** +** Returns AVRC_STS_NO_ERROR, if the command is built successfully +** Otherwise, the error code. +** +*******************************************************************************/ +tAVRC_STS AVRC_BldCommand( tAVRC_COMMAND *p_cmd, BT_HDR **pp_pkt) +{ + tAVRC_STS status = AVRC_STS_BAD_PARAM; + BT_HDR *p_pkt; + BOOLEAN alloc = FALSE; + + AVRC_TRACE_API("AVRC_BldCommand: pdu=%x status=%x", p_cmd->cmd.pdu, p_cmd->cmd.status); + if (!p_cmd || !pp_pkt) { + AVRC_TRACE_API("AVRC_BldCommand. Invalid parameters passed. p_cmd=%p, pp_pkt=%p", + p_cmd, pp_pkt); + return AVRC_STS_BAD_PARAM; + } + + if (*pp_pkt == NULL) { + if ((*pp_pkt = avrc_bld_init_cmd_buffer(p_cmd)) == NULL) { + AVRC_TRACE_API("AVRC_BldCommand: Failed to initialize command buffer"); + return AVRC_STS_INTERNAL_ERR; + } + alloc = TRUE; + } + status = AVRC_STS_NO_ERROR; + p_pkt = *pp_pkt; + + switch (p_cmd->pdu) { + case AVRC_PDU_REQUEST_CONTINUATION_RSP: /* 0x40 */ + status = avrc_bld_next_cmd(&p_cmd->continu, p_pkt); + break; + + case AVRC_PDU_ABORT_CONTINUATION_RSP: /* 0x41 */ + status = avrc_bld_next_cmd(&p_cmd->abort, p_pkt); + break; +#if (AVRC_ADV_CTRL_INCLUDED == TRUE) + case AVRC_PDU_SET_ABSOLUTE_VOLUME: /* 0x50 */ + status = avrc_bld_set_abs_volume_cmd(&p_cmd->volume, p_pkt); + break; +#endif + + case AVRC_PDU_SET_PLAYER_APP_VALUE: /* 0x14 */ + status = avrc_bld_set_player_value_cmd(&p_cmd->set_app_val, p_pkt); + break; + + case AVRC_PDU_GET_ELEMENT_ATTR: /* 0x20 */ + status = avrc_bld_get_element_attr_cmd(&p_cmd->get_elem_attrs, p_pkt); + break; + + case AVRC_PDU_REGISTER_NOTIFICATION: /* 0x31 */ + status = avrc_bld_register_change_notfn(p_cmd->reg_notif.event_id, p_cmd->reg_notif.param, p_pkt); + break; + case AVRC_PDU_GET_CAPABILITIES: + status = avrc_bld_get_caps_cmd(&p_cmd->get_caps, p_pkt); + break; + } + + if (alloc && (status != AVRC_STS_NO_ERROR) ) { + osi_free(p_pkt); + *pp_pkt = NULL; + } + AVRC_TRACE_API("AVRC_BldCommand: returning %d", status); + return status; +} +#endif /* (AVRC_METADATA_INCLUDED == TRUE) */ + +#endif /* #if (defined(AVRC_INCLUDED) && AVRC_INCLUDED == TRUE) */ diff --git a/lib/bt/host/bluedroid/stack/avrc/avrc_bld_tg.c b/lib/bt/host/bluedroid/stack/avrc/avrc_bld_tg.c new file mode 100644 index 00000000..79623829 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/avrc/avrc_bld_tg.c @@ -0,0 +1,892 @@ +/****************************************************************************** + * + * Copyright (C) 2003-2013 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ +#include +#include "common/bt_target.h" +#include "stack/avrc_api.h" +#include "stack/avrc_defs.h" +#include "avrc_int.h" +#include "common/bt_defs.h" +#include "osi/allocator.h" + +#if (defined(AVRC_INCLUDED) && AVRC_INCLUDED == TRUE) + +/***************************************************************************** +** Global data +*****************************************************************************/ +#if (AVRC_METADATA_INCLUDED == TRUE) + +/******************************************************************************* +** +** Function avrc_bld_get_capability_rsp +** +** Description This function builds the Get Capability response. +** +** Returns AVRC_STS_NO_ERROR, if the response is built successfully +** Otherwise, the error code. +** +*******************************************************************************/ +static tAVRC_STS avrc_bld_get_capability_rsp (tAVRC_GET_CAPS_RSP *p_rsp, BT_HDR *p_pkt) +{ + UINT8 *p_data, *p_start, *p_len, *p_count; + UINT16 len = 0; + UINT8 xx; + UINT32 *p_company_id; + UINT8 *p_event_id; + tAVRC_STS status = AVRC_STS_NO_ERROR; + + if (!(AVRC_IS_VALID_CAP_ID(p_rsp->capability_id))) { + AVRC_TRACE_ERROR("avrc_bld_get_capability_rsp bad parameter. p_rsp: %x", (int)p_rsp); + status = AVRC_STS_BAD_PARAM; + return status; + } + + AVRC_TRACE_API("avrc_bld_get_capability_rsp"); + /* get the existing length, if any, and also the num attributes */ + p_start = (UINT8 *)(p_pkt + 1) + p_pkt->offset; + p_data = p_len = p_start + 2; /* pdu + rsvd */ + + BE_STREAM_TO_UINT16(len, p_data); + UINT8_TO_BE_STREAM(p_data, p_rsp->capability_id); + p_count = p_data; + + if (len == 0) { + *p_count = p_rsp->count; + p_data++; + len = 2; /* move past the capability_id and count */ + } else { + p_data = p_start + p_pkt->len; + *p_count += p_rsp->count; + } + + if (p_rsp->capability_id == AVRC_CAP_COMPANY_ID) { + p_company_id = p_rsp->param.company_id; + for (xx = 0; xx < p_rsp->count; xx++) { + UINT24_TO_BE_STREAM(p_data, p_company_id[xx]); + } + len += p_rsp->count * 3; + } else { + p_event_id = p_rsp->param.event_id; + *p_count = 0; + for (xx = 0; xx < p_rsp->count; xx++) { + if (AVRC_IS_VALID_EVENT_ID(p_event_id[xx])) { + (*p_count)++; + UINT8_TO_BE_STREAM(p_data, p_event_id[xx]); + } + } + len += (*p_count); + } + UINT16_TO_BE_STREAM(p_len, len); + p_pkt->len = (p_data - p_start); + status = AVRC_STS_NO_ERROR; + + return status; +} + +/******************************************************************************* +** +** Function avrc_bld_list_app_settings_attr_rsp +** +** Description This function builds the List Application Settings Attribute +** response. +** +** Returns AVRC_STS_NO_ERROR, if the response is built successfully +** Otherwise, the error code. +** +*******************************************************************************/ +static tAVRC_STS avrc_bld_list_app_settings_attr_rsp (tAVRC_LIST_APP_ATTR_RSP *p_rsp, BT_HDR *p_pkt) +{ + UINT8 *p_data, *p_start, *p_len, *p_num; + UINT16 len = 0; + UINT8 xx; + + AVRC_TRACE_API("avrc_bld_list_app_settings_attr_rsp"); + /* get the existing length, if any, and also the num attributes */ + p_start = (UINT8 *)(p_pkt + 1) + p_pkt->offset; + p_data = p_len = p_start + 2; /* pdu + rsvd */ + + BE_STREAM_TO_UINT16(len, p_data); + p_num = p_data; + if (len == 0) { + /* first time initialize the attribute count */ + *p_num = 0; + p_data++; + } else { + p_data = p_start + p_pkt->len; + } + + for (xx = 0; xx < p_rsp->num_attr; xx++) { + if (AVRC_IsValidPlayerAttr(p_rsp->attrs[xx])) { + (*p_num)++; + UINT8_TO_BE_STREAM(p_data, p_rsp->attrs[xx]); + } + } + + len = *p_num + 1; + UINT16_TO_BE_STREAM(p_len, len); + p_pkt->len = (p_data - p_start); + + return AVRC_STS_NO_ERROR; +} + +/******************************************************************************* +** +** Function avrc_bld_list_app_settings_values_rsp +** +** Description This function builds the List Application Setting Values +** response. +** +** Returns AVRC_STS_NO_ERROR, if the response is built successfully +** Otherwise, the error code. +** +*******************************************************************************/ +static tAVRC_STS avrc_bld_list_app_settings_values_rsp (tAVRC_LIST_APP_VALUES_RSP *p_rsp, + BT_HDR *p_pkt) +{ + UINT8 *p_data, *p_start, *p_len, *p_num; + UINT8 xx; + UINT16 len; + + AVRC_TRACE_API("avrc_bld_list_app_settings_values_rsp"); + + p_start = (UINT8 *)(p_pkt + 1) + p_pkt->offset; + p_data = p_len = p_start + 2; /* pdu + rsvd */ + + /* get the existing length, if any, and also the num attributes */ + BE_STREAM_TO_UINT16(len, p_data); + p_num = p_data; + /* first time initialize the attribute count */ + if (len == 0) { + *p_num = p_rsp->num_val; + p_data++; + } else { + p_data = p_start + p_pkt->len; + *p_num += p_rsp->num_val; + } + + + for (xx = 0; xx < p_rsp->num_val; xx++) { + UINT8_TO_BE_STREAM(p_data, p_rsp->vals[xx]); + } + + len = *p_num + 1; + UINT16_TO_BE_STREAM(p_len, len); + p_pkt->len = (p_data - p_start); + return AVRC_STS_NO_ERROR; +} + +/******************************************************************************* +** +** Function avrc_bld_get_cur_app_setting_value_rsp +** +** Description This function builds the Get Current Application Setting Value +** response. +** +** Returns AVRC_STS_NO_ERROR, if the response is built successfully +** Otherwise, the error code. +** +*******************************************************************************/ +static tAVRC_STS avrc_bld_get_cur_app_setting_value_rsp (tAVRC_GET_CUR_APP_VALUE_RSP *p_rsp, + BT_HDR *p_pkt) +{ + UINT8 *p_data, *p_start, *p_len, *p_count; + UINT16 len; + UINT8 xx; + + if (!p_rsp->p_vals) { + AVRC_TRACE_ERROR("avrc_bld_get_cur_app_setting_value_rsp NULL parameter"); + return AVRC_STS_BAD_PARAM; + } + + AVRC_TRACE_API("avrc_bld_get_cur_app_setting_value_rsp"); + /* get the existing length, if any, and also the num attributes */ + p_start = (UINT8 *)(p_pkt + 1) + p_pkt->offset; + p_data = p_len = p_start + 2; /* pdu + rsvd */ + + BE_STREAM_TO_UINT16(len, p_data); + p_count = p_data; + if (len == 0) { + /* first time initialize the attribute count */ + *p_count = 0; + p_data++; + } else { + p_data = p_start + p_pkt->len; + } + + for (xx = 0; xx < p_rsp->num_val; xx++) { + if (avrc_is_valid_player_attrib_value(p_rsp->p_vals[xx].attr_id, p_rsp->p_vals[xx].attr_val)) { + (*p_count)++; + UINT8_TO_BE_STREAM(p_data, p_rsp->p_vals[xx].attr_id); + UINT8_TO_BE_STREAM(p_data, p_rsp->p_vals[xx].attr_val); + } + } + len = ((*p_count) << 1) + 1; + UINT16_TO_BE_STREAM(p_len, len); + p_pkt->len = (p_data - p_start); + + return AVRC_STS_NO_ERROR; +} + +/******************************************************************************* +** +** Function avrc_bld_set_app_setting_value_rsp +** +** Description This function builds the Set Application Setting Value +** response. +** +** Returns AVRC_STS_NO_ERROR, if the response is built successfully +** Otherwise, the error code. +** +*******************************************************************************/ +static tAVRC_STS avrc_bld_set_app_setting_value_rsp (tAVRC_RSP *p_rsp, BT_HDR *p_pkt) +{ + UNUSED(p_rsp); + UNUSED(p_pkt); + + /* nothing to be added. */ + AVRC_TRACE_API("avrc_bld_set_app_setting_value_rsp"); + return AVRC_STS_NO_ERROR; +} + +/******************************************************************************* +** +** Function avrc_bld_app_setting_text_rsp +** +** Description This function builds the Get Application Settings Attribute Text +** or Get Application Settings Value Text response. +** +** Returns AVRC_STS_NO_ERROR, if the response is built successfully +** Otherwise, the error code. +** +*******************************************************************************/ +static tAVRC_STS avrc_bld_app_setting_text_rsp (tAVRC_GET_APP_ATTR_TXT_RSP *p_rsp, BT_HDR *p_pkt) +{ + UINT8 *p_data, *p_start, *p_len, *p_count; + UINT16 len, len_left; + UINT8 xx; + tAVRC_STS sts = AVRC_STS_NO_ERROR; + UINT8 num_added = 0; + + if (!p_rsp->p_attrs) { + AVRC_TRACE_ERROR("avrc_bld_app_setting_text_rsp NULL parameter"); + return AVRC_STS_BAD_PARAM; + } + /* get the existing length, if any, and also the num attributes */ + p_start = (UINT8 *)(p_pkt + 1) + p_pkt->offset; + p_data = p_len = p_start + 2; /* pdu + rsvd */ + + /* + * NOTE: The buffer is allocated within avrc_bld_init_rsp_buffer(), and is + * always of size BT_DEFAULT_BUFFER_SIZE. + */ + len_left = BT_DEFAULT_BUFFER_SIZE - BT_HDR_SIZE - p_pkt->offset - p_pkt->len; + + BE_STREAM_TO_UINT16(len, p_data); + p_count = p_data; + + if (len == 0) { + *p_count = 0; + p_data++; + } else { + p_data = p_start + p_pkt->len; + } + + for (xx = 0; xx < p_rsp->num_attr; xx++) { + if (len_left < (p_rsp->p_attrs[xx].str_len + 4)) { + AVRC_TRACE_ERROR("avrc_bld_app_setting_text_rsp out of room %d(str_len:%d, left:%d)", + xx, p_rsp->p_attrs[xx].str_len, len_left); + p_rsp->num_attr = num_added; + sts = AVRC_STS_INTERNAL_ERR; + break; + } + if ( !p_rsp->p_attrs[xx].str_len || !p_rsp->p_attrs[xx].p_str ) { + AVRC_TRACE_ERROR("avrc_bld_app_setting_text_rsp NULL attr text[%d]", xx); + continue; + } + UINT8_TO_BE_STREAM(p_data, p_rsp->p_attrs[xx].attr_id); + UINT16_TO_BE_STREAM(p_data, p_rsp->p_attrs[xx].charset_id); + UINT8_TO_BE_STREAM(p_data, p_rsp->p_attrs[xx].str_len); + ARRAY_TO_BE_STREAM(p_data, p_rsp->p_attrs[xx].p_str, p_rsp->p_attrs[xx].str_len); + (*p_count)++; + num_added++; + } + len = p_data - p_count; + UINT16_TO_BE_STREAM(p_len, len); + p_pkt->len = (p_data - p_start); + + return sts; +} + +/******************************************************************************* +** +** Function avrc_bld_get_app_setting_attr_text_rsp +** +** Description This function builds the Get Application Setting Attribute Text +** response. +** +** Returns AVRC_STS_NO_ERROR, if the response is built successfully +** Otherwise, the error code. +** +*******************************************************************************/ +static tAVRC_STS avrc_bld_get_app_setting_attr_text_rsp (tAVRC_GET_APP_ATTR_TXT_RSP *p_rsp, + BT_HDR *p_pkt) +{ + AVRC_TRACE_API("avrc_bld_get_app_setting_attr_text_rsp"); + return avrc_bld_app_setting_text_rsp(p_rsp, p_pkt); +} + +/******************************************************************************* +** +** Function avrc_bld_get_app_setting_value_text_rsp +** +** Description This function builds the Get Application Setting Value Text +** response. +** +** Returns AVRC_STS_NO_ERROR, if the response is built successfully +** Otherwise, the error code. +** +*******************************************************************************/ +static tAVRC_STS avrc_bld_get_app_setting_value_text_rsp (tAVRC_GET_APP_ATTR_TXT_RSP *p_rsp, + BT_HDR *p_pkt) +{ + AVRC_TRACE_API("avrc_bld_get_app_setting_value_text_rsp"); + return avrc_bld_app_setting_text_rsp(p_rsp, p_pkt); +} + +/******************************************************************************* +** +** Function avrc_bld_inform_charset_rsp +** +** Description This function builds the Inform Displayable Character Set +** response. +** +** Returns AVRC_STS_NO_ERROR, if the response is built successfully +** Otherwise, the error code. +** +*******************************************************************************/ +static tAVRC_STS avrc_bld_inform_charset_rsp (tAVRC_RSP *p_rsp, BT_HDR *p_pkt) +{ + UNUSED(p_rsp); + UNUSED(p_pkt); + + /* nothing to be added. */ + AVRC_TRACE_API("avrc_bld_inform_charset_rsp"); + return AVRC_STS_NO_ERROR; +} + +/******************************************************************************* +** +** Function avrc_bld_inform_battery_status_rsp +** +** Description This function builds the Inform Battery Status +** response. +** +** Returns AVRC_STS_NO_ERROR, if the response is built successfully +** Otherwise, the error code. +** +*******************************************************************************/ +static tAVRC_STS avrc_bld_inform_battery_status_rsp (tAVRC_RSP *p_rsp, BT_HDR *p_pkt) +{ + UNUSED(p_rsp); + UNUSED(p_pkt); + + /* nothing to be added. */ + AVRC_TRACE_API("avrc_bld_inform_battery_status_rsp"); + return AVRC_STS_NO_ERROR; +} + +/******************************************************************************* +** +** Function avrc_bld_get_elem_attrs_rsp +** +** Description This function builds the Get Element Attributes +** response. +** +** Returns AVRC_STS_NO_ERROR, if the response is built successfully +** Otherwise, the error code. +** +*******************************************************************************/ +static tAVRC_STS avrc_bld_get_elem_attrs_rsp (tAVRC_GET_ELEM_ATTRS_RSP *p_rsp, BT_HDR *p_pkt) +{ + UINT8 *p_data, *p_start, *p_len, *p_count; + UINT16 len; + UINT8 xx; + + AVRC_TRACE_API("avrc_bld_get_elem_attrs_rsp"); + if (!p_rsp->p_attrs) { + AVRC_TRACE_ERROR("avrc_bld_get_elem_attrs_rsp NULL parameter"); + return AVRC_STS_BAD_PARAM; + } + + /* get the existing length, if any, and also the num attributes */ + p_start = (UINT8 *)(p_pkt + 1) + p_pkt->offset; + p_data = p_len = p_start + 2; /* pdu + rsvd */ + + BE_STREAM_TO_UINT16(len, p_data); + p_count = p_data; + + if (len == 0) { + *p_count = 0; + p_data++; + } else { + p_data = p_start + p_pkt->len; + } + + for (xx = 0; xx < p_rsp->num_attr; xx++) { + if (!AVRC_IS_VALID_MEDIA_ATTRIBUTE(p_rsp->p_attrs[xx].attr_id)) { + AVRC_TRACE_ERROR("avrc_bld_get_elem_attrs_rsp invalid attr id[%d]: %d", xx, p_rsp->p_attrs[xx].attr_id); + continue; + } + if ( !p_rsp->p_attrs[xx].name.p_str ) { + p_rsp->p_attrs[xx].name.str_len = 0; + } + UINT32_TO_BE_STREAM(p_data, p_rsp->p_attrs[xx].attr_id); + UINT16_TO_BE_STREAM(p_data, p_rsp->p_attrs[xx].name.charset_id); + UINT16_TO_BE_STREAM(p_data, p_rsp->p_attrs[xx].name.str_len); + ARRAY_TO_BE_STREAM(p_data, p_rsp->p_attrs[xx].name.p_str, p_rsp->p_attrs[xx].name.str_len); + (*p_count)++; + } + len = p_data - p_count; + UINT16_TO_BE_STREAM(p_len, len); + p_pkt->len = (p_data - p_start); + return AVRC_STS_NO_ERROR; +} + +/******************************************************************************* +** +** Function avrc_bld_get_play_status_rsp +** +** Description This function builds the Get Play Status +** response. +** +** Returns AVRC_STS_NO_ERROR, if the response is built successfully +** Otherwise, the error code. +** +*******************************************************************************/ +static tAVRC_STS avrc_bld_get_play_status_rsp (tAVRC_GET_PLAY_STATUS_RSP *p_rsp, BT_HDR *p_pkt) +{ + UINT8 *p_data, *p_start; + + AVRC_TRACE_API("avrc_bld_get_play_status_rsp"); + p_start = (UINT8 *)(p_pkt + 1) + p_pkt->offset; + p_data = p_start + 2; + + /* add fixed lenth - song len(4) + song position(4) + status(1) */ + UINT16_TO_BE_STREAM(p_data, 9); + UINT32_TO_BE_STREAM(p_data, p_rsp->song_len); + UINT32_TO_BE_STREAM(p_data, p_rsp->song_pos); + UINT8_TO_BE_STREAM(p_data, p_rsp->play_status); + p_pkt->len = (p_data - p_start); + + return AVRC_STS_NO_ERROR; +} + +/******************************************************************************* +** +** Function avrc_bld_notify_rsp +** +** Description This function builds the Notification response. +** +** Returns AVRC_STS_NO_ERROR, if the response is built successfully +** Otherwise, the error code. +** +*******************************************************************************/ +static tAVRC_STS avrc_bld_notify_rsp (tAVRC_REG_NOTIF_RSP *p_rsp, BT_HDR *p_pkt) +{ + UINT8 *p_data, *p_start; + UINT8 *p_len; + UINT16 len = 0; + UINT8 xx; + tAVRC_STS status = AVRC_STS_NO_ERROR; + + AVRC_TRACE_API("avrc_bld_notify_rsp"); + + p_start = (UINT8 *)(p_pkt + 1) + p_pkt->offset; + p_data = p_len = p_start + 2; /* pdu + rsvd */ + p_data += 2; + + UINT8_TO_BE_STREAM(p_data, p_rsp->event_id); + switch (p_rsp->event_id) { + case AVRC_EVT_PLAY_STATUS_CHANGE: /* 0x01 */ + /* p_rsp->param.play_status >= AVRC_PLAYSTATE_STOPPED is always TRUE */ + if ((p_rsp->param.play_status <= AVRC_PLAYSTATE_REV_SEEK) || + (p_rsp->param.play_status == AVRC_PLAYSTATE_ERROR) ) { + UINT8_TO_BE_STREAM(p_data, p_rsp->param.play_status); + len = 2; + } else { + AVRC_TRACE_ERROR("bad play state"); + status = AVRC_STS_BAD_PARAM; + } + break; + + case AVRC_EVT_TRACK_CHANGE: /* 0x02 */ + ARRAY_TO_BE_STREAM(p_data, p_rsp->param.track, AVRC_UID_SIZE); + len = (UINT8)(AVRC_UID_SIZE + 1); + break; + + case AVRC_EVT_TRACK_REACHED_END: /* 0x03 */ + case AVRC_EVT_TRACK_REACHED_START: /* 0x04 */ + len = 1; + break; + + case AVRC_EVT_PLAY_POS_CHANGED: /* 0x05 */ + UINT32_TO_BE_STREAM(p_data, p_rsp->param.play_pos); + len = 5; + break; + + case AVRC_EVT_BATTERY_STATUS_CHANGE: /* 0x06 */ + if (AVRC_IS_VALID_BATTERY_STATUS(p_rsp->param.battery_status)) { + UINT8_TO_BE_STREAM(p_data, p_rsp->param.battery_status); + len = 2; + } else { + AVRC_TRACE_ERROR("bad battery status"); + status = AVRC_STS_BAD_PARAM; + } + break; + + case AVRC_EVT_SYSTEM_STATUS_CHANGE: /* 0x07 */ + if (AVRC_IS_VALID_SYSTEM_STATUS(p_rsp->param.system_status)) { + UINT8_TO_BE_STREAM(p_data, p_rsp->param.system_status); + len = 2; + } else { + AVRC_TRACE_ERROR("bad system status"); + status = AVRC_STS_BAD_PARAM; + } + break; + + case AVRC_EVT_APP_SETTING_CHANGE: /* 0x08 */ + if (p_rsp->param.player_setting.num_attr > AVRC_MAX_APP_SETTINGS) { + p_rsp->param.player_setting.num_attr = AVRC_MAX_APP_SETTINGS; + } + + if (p_rsp->param.player_setting.num_attr > 0) { + UINT8_TO_BE_STREAM(p_data, p_rsp->param.player_setting.num_attr); + len = 2; + for (xx = 0; xx < p_rsp->param.player_setting.num_attr; xx++) { + if (avrc_is_valid_player_attrib_value(p_rsp->param.player_setting.attr_id[xx], + p_rsp->param.player_setting.attr_value[xx])) { + UINT8_TO_BE_STREAM(p_data, p_rsp->param.player_setting.attr_id[xx]); + UINT8_TO_BE_STREAM(p_data, p_rsp->param.player_setting.attr_value[xx]); + } else { + AVRC_TRACE_ERROR("bad player app seeting attribute or value"); + status = AVRC_STS_BAD_PARAM; + break; + } + len += 2; + } + } else { + status = AVRC_STS_BAD_PARAM; + } + break; + + case AVRC_EVT_VOLUME_CHANGE: + UINT8_TO_BE_STREAM(p_data, p_rsp->param.volume); + len = 2; + break; + default: + status = AVRC_STS_BAD_PARAM; + AVRC_TRACE_ERROR("unknown event_id"); + } + + UINT16_TO_BE_STREAM(p_len, len); + p_pkt->len = (p_data - p_start); + + return status; +} + +/******************************************************************************* +** +** Function avrc_bld_next_rsp +** +** Description This function builds the Request Continue or Abort +** response. +** +** Returns AVRC_STS_NO_ERROR, if the response is built successfully +** Otherwise, the error code. +** +*******************************************************************************/ +static tAVRC_STS avrc_bld_next_rsp (tAVRC_RSP *p_rsp, BT_HDR *p_pkt) +{ + UNUSED(p_rsp); + UNUSED(p_pkt); + + /* nothing to be added. */ + AVRC_TRACE_API("avrc_bld_next_rsp"); + return AVRC_STS_NO_ERROR; +} + +/******************************************************************************* +** +** Function avrc_bld_group_navigation_rsp +** +** Description This function builds the Group Navigation +** response. +** +** Returns AVRC_STS_NO_ERROR, if the response is built successfully +** Otherwise, the error code. +** +*******************************************************************************/ +tAVRC_STS avrc_bld_group_navigation_rsp (UINT16 navi_id, BT_HDR *p_pkt) +{ + UINT8 *p_data; + + if (!AVRC_IS_VALID_GROUP(navi_id)) { + AVRC_TRACE_ERROR("avrc_bld_group_navigation_rsp bad navigation op id: %d", navi_id); + return AVRC_STS_BAD_PARAM; + } + + AVRC_TRACE_API("avrc_bld_group_navigation_rsp"); + p_data = (UINT8 *)(p_pkt + 1) + p_pkt->offset; + UINT16_TO_BE_STREAM(p_data, navi_id); + p_pkt->len = 2; + return AVRC_STS_NO_ERROR; +} + +/******************************************************************************* +** +** Function avrc_bld_set_absolute_volume_rsp +** +** Description This function builds the Set Absolute Volume command +** response +** +** Returns AVRC_STS_NO_ERROR, if the response is built successfully +** Otherwise, the error code. +** +*******************************************************************************/ +tAVRC_STS avrc_bld_set_absolute_volume_rsp(tAVRC_SET_VOLUME_RSP *p_rsp, BT_HDR *p_pkt) +{ + UINT8 *p_data, *p_start; + + p_start = (UINT8 *)(p_pkt + 1) + p_pkt->offset; + p_data = p_start + 2; /* pdu + rsvd */ + UINT16_TO_BE_STREAM(p_data, 1); /* fixed length 1 */ + UINT8_TO_BE_STREAM(p_data, p_rsp->volume); + + p_pkt->len = (p_data - p_start); + + return AVRC_STS_NO_ERROR; +} + +/******************************************************************************* +** +** Function avrc_bld_rejected_rsp +** +** Description This function builds the General Response response. +** +** Returns AVRC_STS_NO_ERROR, if the response is built successfully +** Otherwise, the error code. +** +*******************************************************************************/ +static tAVRC_STS avrc_bld_rejected_rsp( tAVRC_RSP *p_rsp, BT_HDR *p_pkt ) +{ + UINT8 *p_data, *p_start; + + AVRC_TRACE_API("avrc_bld_rejected_rsp: status=%d, pdu:x%x", p_rsp->status, p_rsp->pdu); + + p_start = (UINT8 *)(p_pkt + 1) + p_pkt->offset; + p_data = p_start + 2; + AVRC_TRACE_DEBUG("pdu:x%x", *p_start); + + UINT16_TO_BE_STREAM(p_data, 1); + UINT8_TO_BE_STREAM(p_data, p_rsp->status); + p_pkt->len = p_data - p_start; + + return AVRC_STS_NO_ERROR; +} + +/******************************************************************************* +** +** Function avrc_bld_init_rsp_buffer +** +** Description This function initializes the response buffer based on PDU +** +** Returns NULL, if no GKI buffer or failure to build the message. +** Otherwise, the GKI buffer that contains the initialized message. +** +*******************************************************************************/ +static BT_HDR *avrc_bld_init_rsp_buffer(tAVRC_RESPONSE *p_rsp) +{ + UINT16 offset = AVRC_MSG_PASS_THRU_OFFSET; + UINT16 chnl = AVCT_DATA_CTRL; + UINT8 opcode = avrc_opcode_from_pdu(p_rsp->pdu); + + AVRC_TRACE_API("avrc_bld_init_rsp_buffer: pdu=%x, opcode=%x/%x", p_rsp->pdu, opcode, + p_rsp->rsp.opcode); + if (opcode != p_rsp->rsp.opcode && p_rsp->rsp.status != AVRC_STS_NO_ERROR && + avrc_is_valid_opcode(p_rsp->rsp.opcode)) { + opcode = p_rsp->rsp.opcode; + AVRC_TRACE_API("opcode=%x", opcode); + } + + switch (opcode) { + case AVRC_OP_PASS_THRU: + offset = AVRC_MSG_PASS_THRU_OFFSET; + break; + + case AVRC_OP_VENDOR: + offset = AVRC_MSG_VENDOR_OFFSET; + break; + } + + /* allocate and initialize the buffer */ + BT_HDR *p_pkt = (BT_HDR *)osi_malloc(BT_DEFAULT_BUFFER_SIZE); + if (p_pkt) { + UINT8 *p_data, *p_start; + + p_pkt->layer_specific = chnl; + p_pkt->event = opcode; + p_pkt->offset = offset; + p_data = (UINT8 *)(p_pkt + 1) + p_pkt->offset; + p_start = p_data; + + /* pass thru - group navigation - has a two byte op_id, so dont do it here */ + if (opcode != AVRC_OP_PASS_THRU) { + *p_data++ = p_rsp->pdu; + } + + switch (opcode) { + case AVRC_OP_VENDOR: + /* reserved 0, packet_type 0 */ + UINT8_TO_BE_STREAM(p_data, 0); + /* continue to the next "case to add length */ + /* add fixed lenth - 0 */ + UINT16_TO_BE_STREAM(p_data, 0); + break; + } + + p_pkt->len = (p_data - p_start); + } + p_rsp->rsp.opcode = opcode; + return p_pkt; +} + +/******************************************************************************* +** +** Function AVRC_BldResponse +** +** Description This function builds the given AVRCP response to the given +** GKI buffer +** +** Returns AVRC_STS_NO_ERROR, if the response is built successfully +** Otherwise, the error code. +** +*******************************************************************************/ +tAVRC_STS AVRC_BldResponse( UINT8 handle, tAVRC_RESPONSE *p_rsp, BT_HDR **pp_pkt) +{ + tAVRC_STS status = AVRC_STS_BAD_PARAM; + BT_HDR *p_pkt; + BOOLEAN alloc = FALSE; + UNUSED(handle); + + if (!p_rsp || !pp_pkt) { + AVRC_TRACE_API("AVRC_BldResponse. Invalid parameters passed. p_rsp=%p, pp_pkt=%p", + p_rsp, pp_pkt); + return AVRC_STS_BAD_PARAM; + } + + if (*pp_pkt == NULL) { + if ((*pp_pkt = avrc_bld_init_rsp_buffer(p_rsp)) == NULL) { + AVRC_TRACE_API("AVRC_BldResponse: Failed to initialize response buffer"); + return AVRC_STS_INTERNAL_ERR; + } + alloc = TRUE; + } + status = AVRC_STS_NO_ERROR; + p_pkt = *pp_pkt; + + AVRC_TRACE_API("AVRC_BldResponse: pdu=%x status=%x", p_rsp->rsp.pdu, p_rsp->rsp.status); + if (p_rsp->rsp.status != AVRC_STS_NO_ERROR) { + return ( avrc_bld_rejected_rsp(&p_rsp->rsp, p_pkt) ); + } + + switch (p_rsp->pdu) { + case AVRC_PDU_NEXT_GROUP: + case AVRC_PDU_PREV_GROUP: + status = avrc_bld_group_navigation_rsp(p_rsp->pdu, p_pkt); + break; + + case AVRC_PDU_GET_CAPABILITIES: + status = avrc_bld_get_capability_rsp(&p_rsp->get_caps, p_pkt); + break; + + case AVRC_PDU_LIST_PLAYER_APP_ATTR: + status = avrc_bld_list_app_settings_attr_rsp(&p_rsp->list_app_attr, p_pkt); + break; + + case AVRC_PDU_LIST_PLAYER_APP_VALUES: + status = avrc_bld_list_app_settings_values_rsp(&p_rsp->list_app_values, p_pkt); + break; + + case AVRC_PDU_GET_CUR_PLAYER_APP_VALUE: + status = avrc_bld_get_cur_app_setting_value_rsp(&p_rsp->get_cur_app_val, p_pkt); + break; + + case AVRC_PDU_SET_PLAYER_APP_VALUE: + status = avrc_bld_set_app_setting_value_rsp(&p_rsp->set_app_val, p_pkt); + break; + + case AVRC_PDU_GET_PLAYER_APP_ATTR_TEXT: + status = avrc_bld_get_app_setting_attr_text_rsp(&p_rsp->get_app_attr_txt, p_pkt); + break; + + case AVRC_PDU_GET_PLAYER_APP_VALUE_TEXT: + status = avrc_bld_get_app_setting_value_text_rsp(&p_rsp->get_app_val_txt, p_pkt); + break; + + case AVRC_PDU_INFORM_DISPLAY_CHARSET: + status = avrc_bld_inform_charset_rsp(&p_rsp->inform_charset, p_pkt); + break; + + case AVRC_PDU_INFORM_BATTERY_STAT_OF_CT: + status = avrc_bld_inform_battery_status_rsp(&p_rsp->inform_battery_status, p_pkt); + break; + + case AVRC_PDU_GET_ELEMENT_ATTR: + status = avrc_bld_get_elem_attrs_rsp(&p_rsp->get_elem_attrs, p_pkt); + break; + + case AVRC_PDU_GET_PLAY_STATUS: + status = avrc_bld_get_play_status_rsp(&p_rsp->get_play_status, p_pkt); + break; + + case AVRC_PDU_REGISTER_NOTIFICATION: + status = avrc_bld_notify_rsp(&p_rsp->reg_notif, p_pkt); + break; + + case AVRC_PDU_REQUEST_CONTINUATION_RSP: /* 0x40 */ + status = avrc_bld_next_rsp(&p_rsp->continu, p_pkt); + break; + + case AVRC_PDU_ABORT_CONTINUATION_RSP: /* 0x41 */ + status = avrc_bld_next_rsp(&p_rsp->abort, p_pkt); + break; + case AVRC_PDU_SET_ABSOLUTE_VOLUME: /* 0x50 */ + status = avrc_bld_set_absolute_volume_rsp(&p_rsp->volume, p_pkt); + break; + } + + if (alloc && (status != AVRC_STS_NO_ERROR) ) { + osi_free(p_pkt); + *pp_pkt = NULL; + } + AVRC_TRACE_API("AVRC_BldResponse: returning %d", status); + return status; +} + +#endif /* (AVRC_METADATA_INCLUDED == TRUE)*/ + +#endif /* #if (defined(AVRC_INCLUDED) && AVRC_INCLUDED == TRUE) */ diff --git a/lib/bt/host/bluedroid/stack/avrc/avrc_opt.c b/lib/bt/host/bluedroid/stack/avrc/avrc_opt.c new file mode 100644 index 00000000..6f0663c0 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/avrc/avrc_opt.c @@ -0,0 +1,233 @@ +/****************************************************************************** + * + * Copyright (C) 2003-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * Interface to AVRCP optional commands + * + ******************************************************************************/ +#include +#include "common/bt_target.h" +#include +#include "stack/avrc_api.h" +#include "avrc_int.h" +#include "osi/allocator.h" + +#if (defined(AVRC_INCLUDED) && AVRC_INCLUDED == TRUE) + +/****************************************************************************** +** +** Function avrc_vendor_msg +** +** Description Compose a VENDOR DEPENDENT command according to p_msg +** +** Input Parameters: +** p_msg: Pointer to VENDOR DEPENDENT message structure. +** +** Output Parameters: +** None. +** +** Returns pointer to a valid GKI buffer if successful. +** NULL if p_msg is NULL. +** +******************************************************************************/ +static BT_HDR *avrc_vendor_msg(tAVRC_MSG_VENDOR *p_msg) +{ + BT_HDR *p_cmd; + UINT8 *p_data; + + assert(p_msg != NULL); + +#if AVRC_METADATA_INCLUDED == TRUE + assert(AVRC_META_CMD_BUF_SIZE > (AVRC_MIN_CMD_LEN + p_msg->vendor_len)); + if ((p_cmd = (BT_HDR *) osi_malloc(AVRC_META_CMD_BUF_SIZE)) != NULL) +#else + assert(AVRC_CMD_BUF_SIZE > (AVRC_MIN_CMD_LEN + p_msg->vendor_len)); + if ((p_cmd = (BT_HDR *) osi_malloc(AVRC_CMD_BUF_SIZE)) != NULL) +#endif + { + p_cmd->offset = AVCT_MSG_OFFSET; + p_data = (UINT8 *)(p_cmd + 1) + p_cmd->offset; + *p_data++ = (p_msg->hdr.ctype & AVRC_CTYPE_MASK); + *p_data++ = (p_msg->hdr.subunit_type << AVRC_SUBTYPE_SHIFT) | p_msg->hdr.subunit_id; + *p_data++ = AVRC_OP_VENDOR; + AVRC_CO_ID_TO_BE_STREAM(p_data, p_msg->company_id); + if (p_msg->vendor_len && p_msg->p_vendor_data) { + memcpy(p_data, p_msg->p_vendor_data, p_msg->vendor_len); + } + p_cmd->len = (UINT16) (p_data + p_msg->vendor_len - (UINT8 *)(p_cmd + 1) - p_cmd->offset); + p_cmd->layer_specific = AVCT_DATA_CTRL; + } + return p_cmd; +} + +/****************************************************************************** +** +** Function AVRC_UnitCmd +** +** Description Send a UNIT INFO command to the peer device. This +** function can only be called for controller role connections. +** Any response message from the peer is passed back through +** the tAVRC_MSG_CBACK callback function. +** +** Input Parameters: +** handle: Handle of this connection. +** +** label: Transaction label. +** +** Output Parameters: +** None. +** +** Returns AVRC_SUCCESS if successful. +** AVRC_BAD_HANDLE if handle is invalid. +** +******************************************************************************/ +UINT16 AVRC_UnitCmd(UINT8 handle, UINT8 label) +{ + BT_HDR *p_cmd; + UINT8 *p_data; + + if ((p_cmd = (BT_HDR *) osi_malloc(AVRC_CMD_BUF_SIZE)) != NULL) { + p_cmd->offset = AVCT_MSG_OFFSET; + p_data = (UINT8 *)(p_cmd + 1) + p_cmd->offset; + *p_data++ = AVRC_CMD_STATUS; + /* unit & id ignore */ + *p_data++ = (AVRC_SUB_UNIT << AVRC_SUBTYPE_SHIFT) | AVRC_SUBID_IGNORE; + *p_data++ = AVRC_OP_UNIT_INFO; + memset(p_data, AVRC_CMD_OPRND_PAD, AVRC_UNIT_OPRND_BYTES); + p_cmd->len = p_data + AVRC_UNIT_OPRND_BYTES - (UINT8 *)(p_cmd + 1) - p_cmd->offset; + p_cmd->layer_specific = AVCT_DATA_CTRL; + } + return AVCT_MsgReq( handle, label, AVCT_CMD, p_cmd); +} + +/****************************************************************************** +** +** Function AVRC_SubCmd +** +** Description Send a SUBUNIT INFO command to the peer device. This +** function can only be called for controller role connections. +** Any response message from the peer is passed back through +** the tAVRC_MSG_CBACK callback function. +** +** Input Parameters: +** handle: Handle of this connection. +** +** label: Transaction label. +** +** page: Specifies which part of the subunit type table +** is requested. For AVRCP it is typically zero. +** Value range is 0-7. +** +** Output Parameters: +** None. +** +** Returns AVRC_SUCCESS if successful. +** AVRC_BAD_HANDLE if handle is invalid. +** +******************************************************************************/ +UINT16 AVRC_SubCmd(UINT8 handle, UINT8 label, UINT8 page) +{ + BT_HDR *p_cmd; + UINT8 *p_data; + + if ((p_cmd = (BT_HDR *) osi_malloc(AVRC_CMD_BUF_SIZE)) != NULL) { + p_cmd->offset = AVCT_MSG_OFFSET; + p_data = (UINT8 *)(p_cmd + 1) + p_cmd->offset; + *p_data++ = AVRC_CMD_STATUS; + /* unit & id ignore */ + *p_data++ = (AVRC_SUB_UNIT << AVRC_SUBTYPE_SHIFT) | AVRC_SUBID_IGNORE; + *p_data++ = AVRC_OP_SUB_INFO; + *p_data++ = ((page & AVRC_SUB_PAGE_MASK) << AVRC_SUB_PAGE_SHIFT) | AVRC_SUB_EXT_CODE; + memset(p_data, AVRC_CMD_OPRND_PAD, AVRC_SUB_OPRND_BYTES); + p_cmd->len = p_data + AVRC_SUB_OPRND_BYTES - (UINT8 *)(p_cmd + 1) - p_cmd->offset; + p_cmd->layer_specific = AVCT_DATA_CTRL; + } + return AVCT_MsgReq( handle, label, AVCT_CMD, p_cmd); +} + +/****************************************************************************** +** +** Function AVRC_VendorCmd +** +** Description Send a VENDOR DEPENDENT command to the peer device. This +** function can only be called for controller role connections. +** Any response message from the peer is passed back through +** the tAVRC_MSG_CBACK callback function. +** +** Input Parameters: +** handle: Handle of this connection. +** +** label: Transaction label. +** +** p_msg: Pointer to VENDOR DEPENDENT message structure. +** +** Output Parameters: +** None. +** +** Returns AVRC_SUCCESS if successful. +** AVRC_BAD_HANDLE if handle is invalid. +** +******************************************************************************/ +UINT16 AVRC_VendorCmd(UINT8 handle, UINT8 label, tAVRC_MSG_VENDOR *p_msg) +{ + BT_HDR *p_buf = avrc_vendor_msg(p_msg); + if (p_buf) { + return AVCT_MsgReq( handle, label, AVCT_CMD, p_buf); + } else { + return AVCT_NO_RESOURCES; + } +} + + +/****************************************************************************** +** +** Function AVRC_VendorRsp +** +** Description Send a VENDOR DEPENDENT response to the peer device. This +** function can only be called for target role connections. +** This function must be called when a VENDOR DEPENDENT +** command message is received from the peer through the +** tAVRC_MSG_CBACK callback function. +** +** Input Parameters: +** handle: Handle of this connection. +** +** label: Transaction label. Must be the same value as +** passed with the command message in the callback function. +** +** p_msg: Pointer to VENDOR DEPENDENT message structure. +** +** Output Parameters: +** None. +** +** Returns AVRC_SUCCESS if successful. +** AVRC_BAD_HANDLE if handle is invalid. +** +******************************************************************************/ +UINT16 AVRC_VendorRsp(UINT8 handle, UINT8 label, tAVRC_MSG_VENDOR *p_msg) +{ + BT_HDR *p_buf = avrc_vendor_msg(p_msg); + if (p_buf) { + return AVCT_MsgReq( handle, label, AVCT_RSP, p_buf); + } else { + return AVCT_NO_RESOURCES; + } +} + +#endif /* #if (defined(AVRC_INCLUDED) && AVRC_INCLUDED == TRUE) */ diff --git a/lib/bt/host/bluedroid/stack/avrc/avrc_pars_ct.c b/lib/bt/host/bluedroid/stack/avrc/avrc_pars_ct.c new file mode 100644 index 00000000..f185e7b5 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/avrc/avrc_pars_ct.c @@ -0,0 +1,165 @@ +/****************************************************************************** + * + * Copyright (C) 2006-2013 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ +#include +#include "common/bt_target.h" +#include "stack/avrc_api.h" +#include "stack/avrc_defs.h" +#include "avrc_int.h" +#include "common/bt_defs.h" + +#if (defined(AVRC_INCLUDED) && AVRC_INCLUDED == TRUE) + +/***************************************************************************** +** Global data +*****************************************************************************/ + +#if (AVRC_METADATA_INCLUDED == TRUE) + +/******************************************************************************* +** +** Function avrc_pars_vendor_rsp +** +** Description This function parses the vendor specific commands defined by +** Bluetooth SIG +** +** Returns AVRC_STS_NO_ERROR, if the message in p_data is parsed successfully. +** Otherwise, the error code defined by AVRCP 1.4 +** +*******************************************************************************/ +static tAVRC_STS avrc_pars_vendor_rsp(tAVRC_MSG_VENDOR *p_msg, tAVRC_RESPONSE *p_result) +{ + tAVRC_STS status = AVRC_STS_NO_ERROR; + UINT8 *p; + UINT16 len; + UINT8 eventid = 0; + + /* Check the vendor data */ + if (p_msg->vendor_len == 0) { + return AVRC_STS_NO_ERROR; + } + if (p_msg->p_vendor_data == NULL) { + return AVRC_STS_INTERNAL_ERR; + } + + p = p_msg->p_vendor_data; + BE_STREAM_TO_UINT8 (p_result->pdu, p); + p++; /* skip the reserved/packe_type byte */ + BE_STREAM_TO_UINT16 (len, p); + AVRC_TRACE_DEBUG("avrc_pars_vendor_rsp() ctype:0x%x pdu:0x%x, len:%d/0x%x", p_msg->hdr.ctype, p_result->pdu, len, len); + if (p_msg->hdr.ctype == AVRC_RSP_REJ) { + p_result->rsp.status = *p; + return p_result->rsp.status; + } + + switch (p_result->pdu) { + /* case AVRC_PDU_REQUEST_CONTINUATION_RSP: 0x40 */ + /* case AVRC_PDU_ABORT_CONTINUATION_RSP: 0x41 */ + +#if (AVRC_ADV_CTRL_INCLUDED == TRUE) + case AVRC_PDU_SET_ABSOLUTE_VOLUME: /* 0x50 */ + if (len != 1) { + status = AVRC_STS_INTERNAL_ERR; + } else { + BE_STREAM_TO_UINT8 (p_result->volume.volume, p); + } + break; +#endif /* (AVRC_ADV_CTRL_INCLUDED == TRUE) */ + + case AVRC_PDU_REGISTER_NOTIFICATION: /* 0x31 */ +#if (AVRC_ADV_CTRL_INCLUDED == TRUE) + BE_STREAM_TO_UINT8 (eventid, p); + if (AVRC_EVT_VOLUME_CHANGE == eventid + && (AVRC_RSP_CHANGED == p_msg->hdr.ctype || AVRC_RSP_INTERIM == p_msg->hdr.ctype + || AVRC_RSP_REJ == p_msg->hdr.ctype || AVRC_RSP_NOT_IMPL == p_msg->hdr.ctype)) { + p_result->reg_notif.status = p_msg->hdr.ctype; + p_result->reg_notif.event_id = eventid; + BE_STREAM_TO_UINT8 (p_result->reg_notif.param.volume, p); + } + // todo: parse the response for other event_ids + AVRC_TRACE_DEBUG("avrc_pars_vendor_rsp PDU reg notif response:event 0x%x", eventid); + break; +#endif /* (AVRC_ADV_CTRL_INCLUDED == TRUE) */ + case AVRC_PDU_GET_CAPABILITIES: /* 0x10 */ + BE_STREAM_TO_UINT8 (p_result->get_caps.capability_id, p); + BE_STREAM_TO_UINT8 (p_result->get_caps.count, p); + if (p_result->get_caps.capability_id == AVRC_CAP_EVENTS_SUPPORTED) { + if (p_result->get_caps.count > AVRC_CAP_MAX_NUM_EVT_ID) { + status = AVRC_STS_INTERNAL_ERR; + } else { + BE_STREAM_TO_ARRAY(p, p_result->get_caps.param.event_id, p_result->get_caps.count); + } + } else if (p_result->get_caps.capability_id == AVRC_CAP_COMPANY_ID) { + if (p_result->get_caps.count > AVRC_CAP_MAX_NUM_COMP_ID) { + status = AVRC_STS_INTERNAL_ERR; + } else { + for (int i = 0; i < p_result->get_caps.count; ++i) { + BE_STREAM_TO_UINT24(p_result->get_caps.param.company_id[i], p); + } + } + } + break; + default: + status = AVRC_STS_BAD_CMD; + break; + } + + return status; +} + +/******************************************************************************* +** +** Function AVRC_ParsResponse +** +** Description This function is a superset of AVRC_ParsMetadata to parse the response. +** +** Returns AVRC_STS_NO_ERROR, if the message in p_data is parsed successfully. +** Otherwise, the error code defined by AVRCP 1.4 +** +*******************************************************************************/ +tAVRC_STS AVRC_ParsResponse (tAVRC_MSG *p_msg, tAVRC_RESPONSE *p_result) +{ + tAVRC_STS status = AVRC_STS_INTERNAL_ERR; + UINT16 id; + + if (p_msg && p_result) { + switch (p_msg->hdr.opcode) { + case AVRC_OP_VENDOR: /* 0x00 Vendor-dependent commands */ + status = avrc_pars_vendor_rsp(&p_msg->vendor, p_result); + break; + + case AVRC_OP_PASS_THRU: /* 0x7C panel subunit opcode */ + status = avrc_pars_pass_thru(&p_msg->pass, &id); + if (status == AVRC_STS_NO_ERROR) { + p_result->pdu = (UINT8)id; + } + break; + + default: + AVRC_TRACE_ERROR("AVRC_ParsResponse() unknown opcode:0x%x", p_msg->hdr.opcode); + break; + } + p_result->rsp.opcode = p_msg->hdr.opcode; + p_result->rsp.status = status; + } + return status; +} + + +#endif /* (AVRC_METADATA_INCLUDED == TRUE) */ + +#endif /* #if (defined(AVRC_INCLUDED) && AVRC_INCLUDED == TRUE) */ diff --git a/lib/bt/host/bluedroid/stack/avrc/avrc_pars_tg.c b/lib/bt/host/bluedroid/stack/avrc/avrc_pars_tg.c new file mode 100644 index 00000000..c30bf94a --- /dev/null +++ b/lib/bt/host/bluedroid/stack/avrc/avrc_pars_tg.c @@ -0,0 +1,311 @@ +/****************************************************************************** + * + * Copyright (C) 2003-2013 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ +#include +#include "common/bt_target.h" +#include "stack/avrc_api.h" +#include "stack/avrc_defs.h" +#include "avrc_int.h" + +#if (defined(AVRC_INCLUDED) && AVRC_INCLUDED == TRUE) + +/***************************************************************************** +** Global data +*****************************************************************************/ +#if (AVRC_METADATA_INCLUDED == TRUE) + +/******************************************************************************* +** +** Function avrc_pars_vendor_cmd +** +** Description This function parses the vendor specific commands defined by +** Bluetooth SIG +** +** Returns AVRC_STS_NO_ERROR, if the message in p_data is parsed successfully. +** Otherwise, the error code defined by AVRCP 1.4 +** +*******************************************************************************/ +static tAVRC_STS avrc_pars_vendor_cmd(tAVRC_MSG_VENDOR *p_msg, tAVRC_COMMAND *p_result, + UINT8 *p_buf, UINT16 buf_len) +{ + tAVRC_STS status = AVRC_STS_NO_ERROR; + UINT8 *p; + UINT16 len; + UINT8 xx, yy; + UINT8 *p_u8; + UINT16 *p_u16; + UINT32 u32, u32_2, *p_u32; + tAVRC_APP_SETTING *p_app_set; + UINT16 size_needed; + + /* Check the vendor data */ + if (p_msg->vendor_len == 0) { + return AVRC_STS_NO_ERROR; + } + if (p_msg->p_vendor_data == NULL) { + return AVRC_STS_INTERNAL_ERR; + } + + p = p_msg->p_vendor_data; + p_result->pdu = *p++; + AVRC_TRACE_DEBUG("avrc_pars_vendor_cmd() pdu:0x%x", p_result->pdu); + if (!AVRC_IsValidAvcType (p_result->pdu, p_msg->hdr.ctype)) { + AVRC_TRACE_DEBUG("avrc_pars_vendor_cmd() detects wrong AV/C type!"); + status = AVRC_STS_BAD_CMD; + } + + p++; /* skip the reserved byte */ + BE_STREAM_TO_UINT16 (len, p); + if ((len + 4) != (p_msg->vendor_len)) { + status = AVRC_STS_INTERNAL_ERR; + } + + if (status != AVRC_STS_NO_ERROR) { + return status; + } + + switch (p_result->pdu) { + case AVRC_PDU_GET_CAPABILITIES: /* 0x10 */ + p_result->get_caps.capability_id = *p++; + if (!AVRC_IS_VALID_CAP_ID(p_result->get_caps.capability_id)) { + status = AVRC_STS_BAD_PARAM; + } else if (len != 1) { + status = AVRC_STS_INTERNAL_ERR; + } + break; + + case AVRC_PDU_LIST_PLAYER_APP_ATTR: /* 0x11 */ + /* no additional parameters */ + if (len != 0) { + status = AVRC_STS_INTERNAL_ERR; + } + break; + + case AVRC_PDU_LIST_PLAYER_APP_VALUES: /* 0x12 */ + p_result->list_app_values.attr_id = *p++; + if (!AVRC_IS_VALID_ATTRIBUTE(p_result->list_app_values.attr_id)) { + status = AVRC_STS_BAD_PARAM; + } else if (len != 1) { + status = AVRC_STS_INTERNAL_ERR; + } + break; + + case AVRC_PDU_GET_CUR_PLAYER_APP_VALUE: /* 0x13 */ + case AVRC_PDU_GET_PLAYER_APP_ATTR_TEXT: /* 0x15 */ + BE_STREAM_TO_UINT8 (p_result->get_cur_app_val.num_attr, p); + if (len != (p_result->get_cur_app_val.num_attr + 1)) { + status = AVRC_STS_INTERNAL_ERR; + break; + } + p_u8 = p_result->get_cur_app_val.attrs; + for (xx = 0, yy = 0; xx < p_result->get_cur_app_val.num_attr; xx++) { + /* only report the valid player app attributes */ + if (AVRC_IsValidPlayerAttr(*p)) { + p_u8[yy++] = *p; + } + p++; + } + p_result->get_cur_app_val.num_attr = yy; + if (yy == 0) { + status = AVRC_STS_BAD_PARAM; + } + break; + + case AVRC_PDU_SET_PLAYER_APP_VALUE: /* 0x14 */ + BE_STREAM_TO_UINT8 (p_result->set_app_val.num_val, p); + size_needed = sizeof(tAVRC_APP_SETTING); + if (p_buf && (len == ((p_result->set_app_val.num_val << 1) + 1))) { + p_result->set_app_val.p_vals = (tAVRC_APP_SETTING *)p_buf; + p_app_set = p_result->set_app_val.p_vals; + for (xx = 0; ((xx < p_result->set_app_val.num_val) && (buf_len > size_needed)); xx++) { + p_app_set[xx].attr_id = *p++; + p_app_set[xx].attr_val = *p++; + if (!avrc_is_valid_player_attrib_value(p_app_set[xx].attr_id, p_app_set[xx].attr_val)) { + status = AVRC_STS_BAD_PARAM; + } + } + if (xx != p_result->set_app_val.num_val) { + AVRC_TRACE_ERROR("AVRC_PDU_SET_PLAYER_APP_VALUE not enough room:%d orig num_val:%d", + xx, p_result->set_app_val.num_val); + p_result->set_app_val.num_val = xx; + } + } else { + AVRC_TRACE_ERROR("AVRC_PDU_SET_PLAYER_APP_VALUE NULL decode buffer or bad len"); + status = AVRC_STS_INTERNAL_ERR; + } + break; + + case AVRC_PDU_GET_PLAYER_APP_VALUE_TEXT:/* 0x16 */ + if (len < 3) { + status = AVRC_STS_INTERNAL_ERR; + } else { + BE_STREAM_TO_UINT8 (p_result->get_app_val_txt.attr_id, p); + if (!AVRC_IS_VALID_ATTRIBUTE(p_result->get_app_val_txt.attr_id)) { + status = AVRC_STS_BAD_PARAM; + } else { + BE_STREAM_TO_UINT8 (p_result->get_app_val_txt.num_val, p); + if ( (len - 2/* attr_id & num_val */) != p_result->get_app_val_txt.num_val) { + status = AVRC_STS_INTERNAL_ERR; + } else { + p_u8 = p_result->get_app_val_txt.vals; + for (xx = 0; xx < p_result->get_app_val_txt.num_val; xx++) { + p_u8[xx] = *p++; + if (!avrc_is_valid_player_attrib_value(p_result->get_app_val_txt.attr_id, + p_u8[xx])) { + status = AVRC_STS_BAD_PARAM; + break; + } + } + } + } + } + break; + + case AVRC_PDU_INFORM_DISPLAY_CHARSET: /* 0x17 */ + if (len < 3) { + status = AVRC_STS_INTERNAL_ERR; + } else { + BE_STREAM_TO_UINT8 (p_result->inform_charset.num_id, p); + if ( (len - 1/* num_id */) != p_result->inform_charset.num_id * 2) { + status = AVRC_STS_INTERNAL_ERR; + } else { + p_u16 = p_result->inform_charset.charsets; + if (p_result->inform_charset.num_id > AVRC_MAX_CHARSET_SIZE) { + p_result->inform_charset.num_id = AVRC_MAX_CHARSET_SIZE; + } + for (xx = 0; xx < p_result->inform_charset.num_id; xx++) { + BE_STREAM_TO_UINT16 (p_u16[xx], p); + } + } + } + break; + + case AVRC_PDU_INFORM_BATTERY_STAT_OF_CT:/* 0x18 */ + if (len != 1) { + status = AVRC_STS_INTERNAL_ERR; + } else { + p_result->inform_battery_status.battery_status = *p++; + if (!AVRC_IS_VALID_BATTERY_STATUS(p_result->inform_battery_status.battery_status)) { + status = AVRC_STS_BAD_PARAM; + } + } + break; + + case AVRC_PDU_GET_ELEMENT_ATTR: /* 0x20 */ + if (len < 9) { /* UID/8 and num_attr/1 */ + status = AVRC_STS_INTERNAL_ERR; + } else { + BE_STREAM_TO_UINT32 (u32, p); + BE_STREAM_TO_UINT32 (u32_2, p); + if (u32 == 0 && u32_2 == 0) { + BE_STREAM_TO_UINT8 (p_result->get_elem_attrs.num_attr, p); + if ( (len - 9/* UID/8 and num_attr/1 */) != (p_result->get_elem_attrs.num_attr * 4)) { + status = AVRC_STS_INTERNAL_ERR; + } else { + p_u32 = p_result->get_elem_attrs.attrs; + if (p_result->get_elem_attrs.num_attr > AVRC_MAX_ELEM_ATTR_SIZE) { + p_result->get_elem_attrs.num_attr = AVRC_MAX_ELEM_ATTR_SIZE; + } + for (xx = 0; xx < p_result->get_elem_attrs.num_attr; xx++) { + BE_STREAM_TO_UINT32 (p_u32[xx], p); + } + } + } else { + status = AVRC_STS_NOT_FOUND; + } + } + break; + + case AVRC_PDU_GET_PLAY_STATUS: /* 0x30 */ + /* no additional parameters */ + if (len != 0) { + status = AVRC_STS_INTERNAL_ERR; + } + break; + + case AVRC_PDU_REGISTER_NOTIFICATION: /* 0x31 */ + if (len != 5) { + status = AVRC_STS_INTERNAL_ERR; + } else { + BE_STREAM_TO_UINT8 (p_result->reg_notif.event_id, p); + BE_STREAM_TO_UINT32 (p_result->reg_notif.param, p); + } + break; + + case AVRC_PDU_SET_ABSOLUTE_VOLUME: { + if (len != 1) { + status = AVRC_STS_INTERNAL_ERR; + } else { + BE_STREAM_TO_UINT8 (p_result->volume.volume, p); + p_result->volume.volume &= 0x7F; // remove the top bit + } + break; + } + + /* case AVRC_PDU_REQUEST_CONTINUATION_RSP: 0x40 */ + /* case AVRC_PDU_ABORT_CONTINUATION_RSP: 0x41 */ + + default: + status = AVRC_STS_BAD_CMD; + break; + } + + return status; +} + +/******************************************************************************* +** +** Function AVRC_ParsCommand +** +** Description This function is a superset of AVRC_ParsMetadata to parse the command. +** +** Returns AVRC_STS_NO_ERROR, if the message in p_data is parsed successfully. +** Otherwise, the error code defined by AVRCP 1.4 +** +*******************************************************************************/ +tAVRC_STS AVRC_ParsCommand (tAVRC_MSG *p_msg, tAVRC_COMMAND *p_result, UINT8 *p_buf, UINT16 buf_len) +{ + tAVRC_STS status = AVRC_STS_INTERNAL_ERR; + UINT16 id; + + if (p_msg && p_result) { + switch (p_msg->hdr.opcode) { + case AVRC_OP_VENDOR: /* 0x00 Vendor-dependent commands */ + status = avrc_pars_vendor_cmd(&p_msg->vendor, p_result, p_buf, buf_len); + break; + + case AVRC_OP_PASS_THRU: /* 0x7C panel subunit opcode */ + status = avrc_pars_pass_thru(&p_msg->pass, &id); + if (status == AVRC_STS_NO_ERROR) { + p_result->pdu = (UINT8)id; + } + break; + + default: + AVRC_TRACE_ERROR("AVRC_ParsCommand() unknown opcode:0x%x", p_msg->hdr.opcode); + break; + } + p_result->cmd.opcode = p_msg->hdr.opcode; + p_result->cmd.status = status; + } + AVRC_TRACE_DEBUG("AVRC_ParsCommand() return status:0x%x", status); + return status; +} + +#endif /* (AVRC_METADATA_INCLUDED == TRUE) */ + +#endif /* #if (defined(AVRC_INCLUDED) && AVRC_INCLUDED == TRUE) */ diff --git a/lib/bt/host/bluedroid/stack/avrc/avrc_sdp.c b/lib/bt/host/bluedroid/stack/avrc/avrc_sdp.c new file mode 100644 index 00000000..fa98082e --- /dev/null +++ b/lib/bt/host/bluedroid/stack/avrc/avrc_sdp.c @@ -0,0 +1,403 @@ +/****************************************************************************** + * + * Copyright (C) 2003-2013 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * AVRCP SDP related functions + * + ******************************************************************************/ +#include +#include "common/bt_target.h" +#include "stack/avrc_api.h" +#include "avrc_int.h" +#include "osi/allocator.h" + +#if (defined(AVRC_INCLUDED) && AVRC_INCLUDED == TRUE) + +#ifndef SDP_AVRCP_1_5 +#define SDP_AVRCP_1_5 TRUE +#endif + +#ifndef SDP_AVCTP_1_4 +#define SDP_AVCTP_1_4 TRUE +#endif + +/***************************************************************************** +** Global data +*****************************************************************************/ +#if AVRC_DYNAMIC_MEMORY == FALSE +tAVRC_CB avrc_cb; +#else +tAVRC_CB *avrc_cb_ptr; +#endif + +/* update AVRC_NUM_PROTO_ELEMS if this constant is changed */ +const tSDP_PROTOCOL_ELEM avrc_proto_list [] = { + {UUID_PROTOCOL_L2CAP, 1, {AVCT_PSM, 0} }, +#if SDP_AVCTP_1_4 == TRUE + {UUID_PROTOCOL_AVCTP, 1, {AVCT_REV_1_4, 0} } +#else +#if (SDP_AVRCP_1_4 == TRUE || SDP_AVRCP_1_5 == TRUE) + {UUID_PROTOCOL_AVCTP, 1, {AVCT_REV_1_3, 0} } +#else +#if AVRC_METADATA_INCLUDED == TRUE + {UUID_PROTOCOL_AVCTP, 1, {AVCT_REV_1_2, 0} } +#else + {UUID_PROTOCOL_AVCTP, 1, {AVCT_REV_1_0, 0} } +#endif +#endif +#endif +}; + +#if SDP_AVRCP_1_5 == TRUE +const tSDP_PROTO_LIST_ELEM avrc_add_proto_list [] = { + { + AVRC_NUM_PROTO_ELEMS, + { + {UUID_PROTOCOL_L2CAP, 1, {AVCT_BR_PSM, 0} }, +#if SDP_AVCTP_1_4 == TRUE + {UUID_PROTOCOL_AVCTP, 1, {AVCT_REV_1_4, 0} } +#else + {UUID_PROTOCOL_AVCTP, 1, {AVCT_REV_1_3, 0} } +#endif + } + } +}; +#endif + + +/****************************************************************************** +** +** Function avrc_sdp_cback +** +** Description This is the SDP callback function used by A2D_FindService. +** This function will be executed by SDP when the service +** search is completed. If the search is successful, it +** finds the first record in the database that matches the +** UUID of the search. Then retrieves various parameters +** from the record. When it is finished it calls the +** application callback function. +** +** Returns Nothing. +** +******************************************************************************/ +static void avrc_sdp_cback(UINT16 status) +{ + AVRC_TRACE_API("avrc_sdp_cback status: %d", status); + + /* reset service_uuid, so can start another find service */ + avrc_cb.service_uuid = 0; + + /* return info from sdp record in app callback function */ + (*avrc_cb.p_cback) (status); + + return; +} + +/****************************************************************************** +** +** Function AVRC_FindService +** +** Description This function is called by the application to perform service +** discovery and retrieve AVRCP SDP record information from a +** peer device. Information is returned for the first service +** record found on the server that matches the service UUID. +** The callback function will be executed when service discovery +** is complete. There can only be one outstanding call to +** AVRC_FindService() at a time; the application must wait for +** the callback before it makes another call to the function. +** The application is responsible for allocating memory for the +** discovery database. It is recommended that the size of the +** discovery database be at least 300 bytes. The application +** can deallocate the memory after the callback function has +** executed. +** +** Input Parameters: +** service_uuid: Indicates TG(UUID_SERVCLASS_AV_REM_CTRL_TARGET) +** or CT(UUID_SERVCLASS_AV_REMOTE_CONTROL) +** +** bd_addr: BD address of the peer device. +** +** p_db: SDP discovery database parameters. +** +** p_cback: Pointer to the callback function. +** +** Output Parameters: +** None. +** +** Returns AVRC_SUCCESS if successful. +** AVRC_BAD_PARAMS if discovery database parameters are invalid. +** AVRC_NO_RESOURCES if there are not enough resources to +** perform the service search. +** +******************************************************************************/ +UINT16 AVRC_FindService(UINT16 service_uuid, BD_ADDR bd_addr, + tAVRC_SDP_DB_PARAMS *p_db, tAVRC_FIND_CBACK *p_cback) +{ + tSDP_UUID uuid_list; + BOOLEAN result = TRUE; + UINT16 a2d_attr_list[] = {ATTR_ID_SERVICE_CLASS_ID_LIST, /* update AVRC_NUM_ATTR, if changed */ + ATTR_ID_PROTOCOL_DESC_LIST, + ATTR_ID_BT_PROFILE_DESC_LIST, + ATTR_ID_SERVICE_NAME, + ATTR_ID_SUPPORTED_FEATURES, + ATTR_ID_PROVIDER_NAME + }; + + AVRC_TRACE_API("AVRC_FindService uuid: %x", service_uuid); + if ( (service_uuid != UUID_SERVCLASS_AV_REM_CTRL_TARGET && service_uuid != UUID_SERVCLASS_AV_REMOTE_CONTROL) || + p_db == NULL || p_db->p_db == NULL || p_cback == NULL) { + return AVRC_BAD_PARAM; + } + + /* check if it is busy */ + if ( avrc_cb.service_uuid == UUID_SERVCLASS_AV_REM_CTRL_TARGET || + avrc_cb.service_uuid == UUID_SERVCLASS_AV_REMOTE_CONTROL) { + return AVRC_NO_RESOURCES; + } + + /* set up discovery database */ + uuid_list.len = LEN_UUID_16; + uuid_list.uu.uuid16 = service_uuid; + + if (p_db->p_attrs == NULL || p_db->num_attr == 0) { + p_db->p_attrs = a2d_attr_list; + p_db->num_attr = AVRC_NUM_ATTR; + } + + result = SDP_InitDiscoveryDb(p_db->p_db, p_db->db_len, 1, &uuid_list, p_db->num_attr, + p_db->p_attrs); + + if (result == TRUE) { + /* store service_uuid and discovery db pointer */ + avrc_cb.p_db = p_db->p_db; + avrc_cb.service_uuid = service_uuid; + avrc_cb.p_cback = p_cback; + + /* perform service search */ + result = SDP_ServiceSearchAttributeRequest(bd_addr, p_db->p_db, avrc_sdp_cback); + } + + return (result ? AVRC_SUCCESS : AVRC_FAIL); +} + +/****************************************************************************** +** +** Function AVRC_AddRecord +** +** Description This function is called to build an AVRCP SDP record. +** Prior to calling this function the application must +** call SDP_CreateRecord() to create an SDP record. +** +** Input Parameters: +** service_uuid: Indicates TG(UUID_SERVCLASS_AV_REM_CTRL_TARGET) +** or CT(UUID_SERVCLASS_AV_REMOTE_CONTROL) +** +** p_service_name: Pointer to a null-terminated character +** string containing the service name. +** If service name is not used set this to NULL. +** +** p_provider_name: Pointer to a null-terminated character +** string containing the provider name. +** If provider name is not used set this to NULL. +** +** categories: Supported categories. +** +** sdp_handle: SDP handle returned by SDP_CreateRecord(). +** +** browsing_en: Supported browsing +** +** Output Parameters: +** None. +** +** Returns AVRC_SUCCESS if successful. +** AVRC_NO_RESOURCES if not enough resources to build the SDP record. +** +******************************************************************************/ +UINT16 AVRC_AddRecord(UINT16 service_uuid, char *p_service_name, char *p_provider_name, + UINT16 categories, UINT32 sdp_handle, BOOLEAN browsing_en) +{ + UINT16 browse_list[1]; + BOOLEAN result = TRUE; + UINT8 temp[8]; + UINT8 *p; + UINT16 count = 1; + UINT16 class_list[2]; + UINT16 supported_feature = categories; + BOOLEAN media_player_virtual_filesystem_supported = FALSE; + BOOLEAN add_additional_protocol_list = FALSE; + + + AVRC_TRACE_API("AVRC_AddRecord uuid: %x, browsing_en:%d", service_uuid, browsing_en); + + if ( service_uuid != UUID_SERVCLASS_AV_REM_CTRL_TARGET && service_uuid != UUID_SERVCLASS_AV_REMOTE_CONTROL ) { + return AVRC_BAD_PARAM; + } + + /* add service class id list */ + class_list[0] = service_uuid; +#if (SDP_AVCTP_1_4 == TRUE || SDP_AVRCP_1_5 == TRUE) + if ( service_uuid == UUID_SERVCLASS_AV_REMOTE_CONTROL ) { + class_list[1] = UUID_SERVCLASS_AV_REM_CTRL_CONTROL; + count = 2; + } +#endif + result &= SDP_AddServiceClassIdList(sdp_handle, count, class_list); + + /* add protocol descriptor list */ + result &= SDP_AddProtocolList(sdp_handle, AVRC_NUM_PROTO_ELEMS, (tSDP_PROTOCOL_ELEM *)avrc_proto_list); + + /* add profile descriptor list */ +#if SDP_AVRCP_1_5 == TRUE + if (browsing_en) { + add_additional_protocol_list = TRUE; + } else if (service_uuid == UUID_SERVCLASS_AV_REM_CTRL_TARGET && + (categories & (AVRC_SUPF_TG_CAT1 | AVRC_SUPF_TG_CAT3))) { + AVRC_TRACE_WARNING("For AVRCP Target Cateory 1 and 3, SDP record shall contain additional protocol list"); + add_additional_protocol_list = TRUE; + } + + if (add_additional_protocol_list) { + /* additional protocol list to include browsing channel */ + result &= SDP_AddAdditionProtoLists( sdp_handle, 1, (tSDP_PROTO_LIST_ELEM *)avrc_add_proto_list); + } + + result &= SDP_AddProfileDescriptorList(sdp_handle, UUID_SERVCLASS_AV_REMOTE_CONTROL, AVRC_REV_1_5); +#else +#if AVRC_METADATA_INCLUDED == TRUE + result &= SDP_AddProfileDescriptorList(sdp_handle, UUID_SERVCLASS_AV_REMOTE_CONTROL, AVRC_REV_1_3); +#else + result &= SDP_AddProfileDescriptorList(sdp_handle, UUID_SERVCLASS_AV_REMOTE_CONTROL, AVRC_REV_1_0); +#endif +#endif + + /* Check if browsing is supported */ + if (service_uuid == UUID_SERVCLASS_AV_REM_CTRL_CONTROL && browsing_en) { + supported_feature |= AVRC_SUPF_CT_BROWSE; + } else if (service_uuid == UUID_SERVCLASS_AV_REM_CTRL_TARGET && media_player_virtual_filesystem_supported) { + supported_feature |= AVRC_SUPF_TG_BROWSE; + } + /* add supported feature */ + p = temp; + UINT16_TO_BE_STREAM(p, supported_feature); + result &= SDP_AddAttribute(sdp_handle, ATTR_ID_SUPPORTED_FEATURES, UINT_DESC_TYPE, + (UINT32)2, (UINT8 *)temp); + + /* add provider name */ + if (p_provider_name != NULL) { + result &= SDP_AddAttribute(sdp_handle, ATTR_ID_PROVIDER_NAME, TEXT_STR_DESC_TYPE, + (UINT32)(strlen(p_provider_name) + 1), (UINT8 *) p_provider_name); + } + + /* add service name */ + if (p_service_name != NULL) { + result &= SDP_AddAttribute(sdp_handle, ATTR_ID_SERVICE_NAME, TEXT_STR_DESC_TYPE, + (UINT32)(strlen(p_service_name) + 1), (UINT8 *) p_service_name); + } + + /* add browse group list */ + browse_list[0] = UUID_SERVCLASS_PUBLIC_BROWSE_GROUP; + result &= SDP_AddUuidSequence(sdp_handle, ATTR_ID_BROWSE_GROUP_LIST, 1, browse_list); + + + return (result ? AVRC_SUCCESS : AVRC_FAIL); +} + + + +/****************************************************************************** +** +** Function AVRC_SetTraceLevel +** +** Description Sets the trace level for AVRC. If 0xff is passed, the +** current trace level is returned. +** +** Input Parameters: +** new_level: The level to set the AVRC tracing to: +** 0xff-returns the current setting. +** 0-turns off tracing. +** >= 1-Errors. +** >= 2-Warnings. +** >= 3-APIs. +** >= 4-Events. +** >= 5-Debug. +** +** Returns The new trace level or current trace level if +** the input parameter is 0xff. +** +******************************************************************************/ +UINT8 AVRC_SetTraceLevel (UINT8 new_level) +{ + if (new_level != 0xFF) { + avrc_cb.trace_level = new_level; + } + + return (avrc_cb.trace_level); +} + +/******************************************************************************* +** +** Function AVRC_Init +** +** Description This function is called at stack startup to allocate the +** control block (if using dynamic memory), and initializes the +** control block and tracing level. +** +** Returns status +** +*******************************************************************************/ +bt_status_t AVRC_Init(void) +{ +#if AVRC_DYNAMIC_MEMORY + avrc_cb_ptr = (tAVRC_CB *)osi_malloc(sizeof(tAVRC_CB)); + if (!avrc_cb_ptr) { + return BT_STATUS_NOMEM; + } +#endif /* #if AVRC_DYNAMIC_MEMORY */ + memset(&avrc_cb, 0, sizeof(tAVRC_CB)); + +#if defined(AVRC_INITIAL_TRACE_LEVEL) + avrc_cb.trace_level = AVRC_INITIAL_TRACE_LEVEL; +#else + avrc_cb.trace_level = BT_TRACE_LEVEL_NONE; +#endif + return BT_STATUS_SUCCESS; +} + +/******************************************************************************* +** +** Function AVRC_Deinit +** +** Description This function is called at stack shotdown to free the +** control block (if using dynamic memory), and deinitializes the +** control block and tracing level. +** +** Returns void +** +*******************************************************************************/ +void AVRC_Deinit(void) +{ +#if AVRC_DYNAMIC_MEMORY + if (avrc_cb_ptr){ + osi_free(avrc_cb_ptr); + avrc_cb_ptr = NULL; + } +#endif /* #if AVRC_DYNAMIC_MEMORY */ +} + +#endif /* #if (defined(AVRC_INCLUDED) && AVRC_INCLUDED == TRUE) */ diff --git a/lib/bt/host/bluedroid/stack/avrc/avrc_utils.c b/lib/bt/host/bluedroid/stack/avrc/avrc_utils.c new file mode 100644 index 00000000..addb1b68 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/avrc/avrc_utils.c @@ -0,0 +1,239 @@ +/****************************************************************************** + * + * Copyright (C) 2003-2013 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ +#include +#include "common/bt_target.h" +#include "stack/avrc_api.h" +#include "avrc_int.h" + +#if (defined(AVRC_INCLUDED) && AVRC_INCLUDED == TRUE) + +#if (AVRC_METADATA_INCLUDED == TRUE) + +/************************************************************************** +** +** Function AVRC_IsValidAvcType +** +** Description Check if correct AVC type is specified +** +** Returns returns TRUE if it is valid +** +** +*******************************************************************************/ +BOOLEAN AVRC_IsValidAvcType(UINT8 pdu_id, UINT8 avc_type) +{ + BOOLEAN result = FALSE; + + if (avc_type < AVRC_RSP_NOT_IMPL) { /* command msg */ + switch (pdu_id) { + case AVRC_PDU_GET_CAPABILITIES: /* 0x10 */ + case AVRC_PDU_LIST_PLAYER_APP_ATTR: /* 0x11 */ + case AVRC_PDU_LIST_PLAYER_APP_VALUES: /* 0x12 */ + case AVRC_PDU_GET_CUR_PLAYER_APP_VALUE: /* 0x13 */ + case AVRC_PDU_GET_PLAYER_APP_ATTR_TEXT: /* 0x15 */ + case AVRC_PDU_GET_PLAYER_APP_VALUE_TEXT: /* 0x16 */ + case AVRC_PDU_GET_ELEMENT_ATTR: /* 0x20 */ + case AVRC_PDU_GET_PLAY_STATUS: /* 0x30 */ + if (avc_type == AVRC_CMD_STATUS) { + result = TRUE; + } + break; + + case AVRC_PDU_SET_PLAYER_APP_VALUE: /* 0x14 */ + case AVRC_PDU_INFORM_DISPLAY_CHARSET: /* 0x17 */ + case AVRC_PDU_INFORM_BATTERY_STAT_OF_CT: /* 0x18 */ + case AVRC_PDU_REQUEST_CONTINUATION_RSP: /* 0x40 */ + case AVRC_PDU_ABORT_CONTINUATION_RSP: /* 0x41 */ + case AVRC_PDU_SET_ABSOLUTE_VOLUME: /* 0x50 */ + if (avc_type == AVRC_CMD_CTRL) { + result = TRUE; + } + break; + + case AVRC_PDU_REGISTER_NOTIFICATION: /* 0x31 */ + if (avc_type == AVRC_CMD_NOTIF) { + result = TRUE; + } + break; + } + } else { /* response msg */ + if (avc_type >= AVRC_RSP_NOT_IMPL && + avc_type <= AVRC_RSP_INTERIM ) { + result = TRUE; + } + } + + return result; +} + +/******************************************************************************* +** +** Function avrc_is_valid_player_attrib_value +** +** Description Check if the given attrib value is valid for its attribute +** +** Returns returns TRUE if it is valid +** +*******************************************************************************/ +BOOLEAN avrc_is_valid_player_attrib_value(UINT8 attrib, UINT8 value) +{ + BOOLEAN result = FALSE; + + switch (attrib) { + case AVRC_PLAYER_SETTING_EQUALIZER: + if ((value > 0) && + (value <= AVRC_PLAYER_VAL_ON)) { + result = TRUE; + } + break; + + case AVRC_PLAYER_SETTING_REPEAT: + if ((value > 0) && + (value <= AVRC_PLAYER_VAL_GROUP_REPEAT)) { + result = TRUE; + } + break; + + case AVRC_PLAYER_SETTING_SHUFFLE: + case AVRC_PLAYER_SETTING_SCAN: + if ((value > 0) && + (value <= AVRC_PLAYER_VAL_GROUP_SHUFFLE)) { + result = TRUE; + } + break; + } + + if (attrib >= AVRC_PLAYER_SETTING_LOW_MENU_EXT) { + result = TRUE; + } + + if (!result) { + AVRC_TRACE_ERROR( + "avrc_is_valid_player_attrib_value() found not matching attrib(x%x)-value(x%x) pair!", + attrib, value); + } + + return result; +} + +/******************************************************************************* +** +** Function AVRC_IsValidPlayerAttr +** +** Description Check if the given attrib value is a valid one +** +** Returns returns TRUE if it is valid +** +*******************************************************************************/ +BOOLEAN AVRC_IsValidPlayerAttr(UINT8 attr) +{ + BOOLEAN result = FALSE; + + if ( (attr >= AVRC_PLAYER_SETTING_EQUALIZER && attr <= AVRC_PLAYER_SETTING_SCAN) || + (attr >= AVRC_PLAYER_SETTING_LOW_MENU_EXT) ) { + result = TRUE; + } + + return result; +} + + + +/******************************************************************************* +** +** Function avrc_pars_pass_thru +** +** Description This function parses the pass thru commands defined by +** Bluetooth SIG +** +** Returns AVRC_STS_NO_ERROR, if the message in p_data is parsed successfully. +** Otherwise, the error code defined by AVRCP 1.4 +** +*******************************************************************************/ +tAVRC_STS avrc_pars_pass_thru(tAVRC_MSG_PASS *p_msg, UINT16 *p_vendor_unique_id) +{ + UINT8 *p_data; + UINT32 co_id; + UINT16 id; + tAVRC_STS status = AVRC_STS_BAD_CMD; + + if (p_msg->op_id == AVRC_ID_VENDOR && p_msg->pass_len == AVRC_PASS_THRU_GROUP_LEN) { + p_data = p_msg->p_pass_data; + AVRC_BE_STREAM_TO_CO_ID (co_id, p_data); + if (co_id == AVRC_CO_METADATA) { + BE_STREAM_TO_UINT16 (id, p_data); + if (AVRC_IS_VALID_GROUP(id)) { + *p_vendor_unique_id = id; + status = AVRC_STS_NO_ERROR; + } + } + } + return status; +} + +/******************************************************************************* +** +** Function avrc_opcode_from_pdu +** +** Description This function returns the opcode of the given pdu +** +** Returns AVRC_OP_VENDOR, AVRC_OP_PASS_THRU or AVRC_OP_BROWSE +** +*******************************************************************************/ +UINT8 avrc_opcode_from_pdu(UINT8 pdu) +{ + UINT8 opcode = 0; + + switch (pdu) { + case AVRC_PDU_NEXT_GROUP: + case AVRC_PDU_PREV_GROUP: /* pass thru */ + opcode = AVRC_OP_PASS_THRU; + break; + + default: /* vendor */ + opcode = AVRC_OP_VENDOR; + break; + } + + return opcode; +} + +/******************************************************************************* +** +** Function avrc_is_valid_opcode +** +** Description This function returns the opcode of the given pdu +** +** Returns AVRC_OP_VENDOR, AVRC_OP_PASS_THRU or AVRC_OP_BROWSE +** +*******************************************************************************/ +BOOLEAN avrc_is_valid_opcode(UINT8 opcode) +{ + BOOLEAN is_valid = FALSE; + switch (opcode) { + case AVRC_OP_BROWSE: + case AVRC_OP_PASS_THRU: + case AVRC_OP_VENDOR: + is_valid = TRUE; + break; + } + return is_valid; +} + +#endif /* (AVRC_METADATA_INCLUDED == TRUE) */ + +#endif /* #if (defined(AVRC_INCLUDED) && AVRC_INCLUDED == TRUE) */ diff --git a/lib/bt/host/bluedroid/stack/avrc/include/avrc_int.h b/lib/bt/host/bluedroid/stack/avrc/include/avrc_int.h new file mode 100644 index 00000000..f13e0ccb --- /dev/null +++ b/lib/bt/host/bluedroid/stack/avrc/include/avrc_int.h @@ -0,0 +1,158 @@ +/****************************************************************************** + * + * Copyright (C) 2003-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * VRCP internal header file. + * + ******************************************************************************/ + + +#ifndef AVRC_INT_H +#define AVRC_INT_H + +#include "avct_defs.h" +#include "stack/avrc_api.h" + +#if (AVRC_INCLUDED == TRUE) +/* DEBUG FLAGS + * + * #define META_DEBUG_ENABLED + */ +/***************************************************************************** +** Constants +*****************************************************************************/ + +/* Number of attributes in AVRC SDP record. */ +#define AVRC_NUM_ATTR 6 + +/* Number of protocol elements in protocol element list. */ +#define AVRC_NUM_PROTO_ELEMS 2 + +#ifndef AVRC_MIN_CMD_LEN +#define AVRC_MIN_CMD_LEN 20 +#endif + +#define AVRC_UNIT_OPRND_BYTES 5 +#define AVRC_SUB_OPRND_BYTES 4 +#define AVRC_SUBRSP_OPRND_BYTES 3 +#define AVRC_SUB_PAGE_MASK 7 +#define AVRC_SUB_PAGE_SHIFT 4 +#define AVRC_SUB_EXT_CODE 7 +#define AVRC_PASS_OP_ID_MASK 0x7F +#define AVRC_PASS_STATE_MASK 0x80 +#define AVRC_CMD_OPRND_PAD 0xFF + +#define AVRC_CTYPE_MASK 0x0F +#define AVRC_SUBTYPE_MASK 0xF8 +#define AVRC_SUBTYPE_SHIFT 3 +#define AVRC_SUBID_MASK 0x07 +#define AVRC_SUBID_IGNORE 0x07 + +#define AVRC_SINGLE_PARAM_SIZE 1 +#define AVRC_METADATA_PKT_TYPE_MASK 0x03 +#define AVRC_PASS_THOUGH_MSG_MASK 0x80 /* MSB of msg_type indicates the PAS THROUGH msg */ +#define AVRC_VENDOR_UNIQUE_MASK 0x70 /* vendor unique id */ + + +/* Company ID is 24-bit integer We can not use the macros in stack/bt_types.h */ +#define AVRC_CO_ID_TO_BE_STREAM(p, u32) {*(p)++ = (UINT8)((u32) >> 16); *(p)++ = (UINT8)((u32) >> 8); *(p)++ = (UINT8)(u32); } +#define AVRC_BE_STREAM_TO_CO_ID(u32, p) {u32 = (((UINT32)(*((p) + 2))) + (((UINT32)(*((p) + 1))) << 8) + (((UINT32)(*(p))) << 16)); (p) += 3;} + +#define AVRC_AVC_HDR_SIZE 3 /* ctype, subunit*, opcode */ + +#define AVRC_MIN_META_HDR_SIZE 4 /* pdu id(1), packet type(1), param len(2) */ +#define AVRC_MIN_BROWSE_HDR_SIZE 3 /* pdu id(1), param len(2) */ + +#define AVRC_VENDOR_HDR_SIZE 6 /* ctype, subunit*, opcode, CO_ID */ +#define AVRC_MSG_VENDOR_OFFSET 23 +#define AVRC_MIN_VENDOR_SIZE (AVRC_MSG_VENDOR_OFFSET + BT_HDR_SIZE + AVRC_MIN_META_HDR_SIZE) + +#define AVRC_PASS_THRU_SIZE 8 +#define AVRC_MSG_PASS_THRU_OFFSET 25 +#define AVRC_MIN_PASS_THRU_SIZE (AVRC_MSG_PASS_THRU_OFFSET + BT_HDR_SIZE + 4) + +#define AVRC_MIN_BROWSE_SIZE (AVCT_BROWSE_OFFSET + BT_HDR_SIZE + AVRC_MIN_BROWSE_HDR_SIZE) + +#define AVRC_CTRL_PKT_LEN(pf, pk) {pf = (UINT8 *)((pk) + 1) + (pk)->offset + 2;} + +#define AVRC_MAX_CTRL_DATA_LEN (AVRC_PACKET_LEN) + +/***************************************************************************** +** Type definitions +*****************************************************************************/ + +#if (AVRC_METADATA_INCLUDED == TRUE) +/* type for Metadata fragmentation control block */ +typedef struct { + BT_HDR *p_fmsg; /* the fragmented message */ + UINT8 frag_pdu; /* the PDU ID for fragmentation */ + BOOLEAN frag_enabled; /* fragmentation flag */ +} tAVRC_FRAG_CB; + +/* type for Metadata re-assembly control block */ +typedef struct { + BT_HDR *p_rmsg; /* the received message */ + UINT16 rasm_offset; /* re-assembly flag, the offset of the start fragment */ + UINT8 rasm_pdu; /* the PDU ID for re-assembly */ +} tAVRC_RASM_CB; +#endif + +typedef struct { + tAVRC_CONN_CB ccb[AVCT_NUM_CONN]; +#if (AVRC_METADATA_INCLUDED == TRUE) + tAVRC_FRAG_CB fcb[AVCT_NUM_CONN]; + tAVRC_RASM_CB rcb[AVCT_NUM_CONN]; +#endif + tAVRC_FIND_CBACK *p_cback; /* pointer to application callback */ + tSDP_DISCOVERY_DB *p_db; /* pointer to discovery database */ + UINT16 service_uuid; /* service UUID to search */ + UINT8 trace_level; +} tAVRC_CB; + + + +#ifdef __cplusplus +extern "C" +{ +#endif + +/****************************************************************************** +** Main Control Block +*******************************************************************************/ +#if AVRC_DYNAMIC_MEMORY == FALSE +extern tAVRC_CB avrc_cb; +#else +extern tAVRC_CB *avrc_cb_ptr; +#define avrc_cb (*avrc_cb_ptr) +#endif + +extern BOOLEAN avrc_is_valid_pdu_id(UINT8 pdu_id); +extern BOOLEAN avrc_is_valid_player_attrib_value(UINT8 attrib, UINT8 value); +extern BT_HDR *avrc_alloc_ctrl_pkt (UINT8 pdu); +extern tAVRC_STS avrc_pars_pass_thru(tAVRC_MSG_PASS *p_msg, UINT16 *p_vendor_unique_id); +extern UINT8 avrc_opcode_from_pdu(UINT8 pdu); +extern BOOLEAN avrc_is_valid_opcode(UINT8 opcode); + +#ifdef __cplusplus +} +#endif + +#endif ///AVRC_INCLUDED == TRUE + +#endif /* AVRC_INT_H */ diff --git a/lib/bt/host/bluedroid/stack/btm/btm_acl.c b/lib/bt/host/bluedroid/stack/btm/btm_acl.c new file mode 100644 index 00000000..4b7e7eed --- /dev/null +++ b/lib/bt/host/bluedroid/stack/btm/btm_acl.c @@ -0,0 +1,2757 @@ +/****************************************************************************** + * + * Copyright (C) 2000-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/***************************************************************************** +** +** Name: btm_acl.c +** +** Description: This file contains functions that handle ACL connections. +** This includes operations such as hold and sniff modes, +** supported packet types. +** +** This module contains both internal and external (API) +** functions. External (API) functions are distinguishable +** by their names beginning with uppercase BTM. +** +** +******************************************************************************/ + +#include +#include +//#include +#include + +#include "stack/bt_types.h" +#include "common/bt_target.h" +#include "device/controller.h" +#include "stack/hcimsgs.h" +#include "stack/btu.h" +#include "stack/btm_api.h" +#include "btm_int.h" +#include "stack/acl_hci_link_interface.h" +#include "l2c_int.h" +#include "stack/l2cap_hci_link_interface.h" +#include "stack/hcidefs.h" +//#include "bt_utils.h" +#include "osi/list.h" + +static void btm_read_remote_features (UINT16 handle); +static void btm_read_remote_ext_features (UINT16 handle, UINT8 page_number); +static void btm_process_remote_ext_features (tACL_CONN *p_acl_cb, UINT8 num_read_pages); + +#define BTM_DEV_REPLY_TIMEOUT 3 /* 3 second timeout waiting for responses */ + +/******************************************************************************* +** +** Function btm_acl_init +** +** Description This function is called at BTM startup to initialize +** +** Returns void +** +*******************************************************************************/ +void btm_acl_init (void) +{ + BTM_TRACE_DEBUG ("btm_acl_init\n"); +#if 0 /* cleared in btm_init; put back in if called from anywhere else! */ + memset (&btm_cb.acl_db, 0, sizeof (btm_cb.acl_db)); + memset (btm_cb.btm_scn, 0, BTM_MAX_SCN); /* Initialize the SCN usage to FALSE */ + btm_cb.btm_def_link_policy = 0; + btm_cb.p_bl_changed_cb = NULL; +#endif + btm_cb.p_acl_db_list = list_new(osi_free_func); + btm_cb.p_pm_mode_db_list = list_new(osi_free_func); + + /* Initialize nonzero defaults */ + btm_cb.btm_def_link_super_tout = HCI_DEFAULT_INACT_TOUT; + btm_cb.acl_disc_reason = 0xff ; +} + +/******************************************************************************* +** +** Function btm_bda_to_acl +** +** Description This function returns the FIRST acl_db entry for the passed BDA. +** +** Parameters bda : BD address of the remote device +** transport : Physical transport used for ACL connection (BR/EDR or LE) +** +** Returns Returns pointer to the ACL DB for the requested BDA if found. +** NULL if not found. +** +*******************************************************************************/ +BOOLEAN btm_get_acl_db(void *p_acl_db_node, void *context) +{ + tACL_CONN *p_acl_db =(tACL_CONN *)p_acl_db_node; + BOOLEAN ret = TRUE; + tACL_DB_PARAM *p_param = (tACL_DB_PARAM *)context; + switch(p_param->type) { + case ACL_DB_BDA: + { + UINT8 *p_bda = (UINT8 *)p_param->p_data1; +#if BLE_INCLUDED == TRUE + tBT_TRANSPORT transport = (tBT_TRANSPORT)(*((UINT8 *)p_param->p_data2)); +#endif + if (p_acl_db->in_use + && !memcmp(p_bda, p_acl_db->remote_addr, BD_ADDR_LEN) +#if BLE_INCLUDED == TRUE + && transport == p_acl_db->transport +#endif + ) { + ret = FALSE; + } + break; + } + case ACL_DB_HANDLE: + { + UINT16 handle = (UINT16) *((UINT16 *)p_param->p_data1); + if (p_acl_db->in_use && handle == p_acl_db->hci_handle) { + ret = FALSE; + } + break; + } + default: + break; + } + return ret; +} + +tACL_CONN *btm_bda_to_acl (BD_ADDR bda, tBT_TRANSPORT transport) +{ + tACL_CONN *p_acl_db = NULL; + list_node_t *p_node = NULL; + tACL_DB_PARAM acl_params; + acl_params.type = ACL_DB_BDA; + acl_params.p_data1 = (void *)bda; + acl_params.p_data2 = (void *)&transport; + p_node = list_foreach(btm_cb.p_acl_db_list, btm_get_acl_db, (void *)&acl_params); + if (p_node) { + p_acl_db = list_node(p_node); + } + + return (p_acl_db); +} + +/******************************************************************************* +** +** Function btm_handle_to_acl +** +** Description This function returns the FIRST acl_db entry for the passed hci_handle. +** +** Returns Returns pointer to the ACL DB for the requested BDA if found. +** NULL if not found. +** +*******************************************************************************/ +tACL_CONN *btm_handle_to_acl (UINT16 hci_handle) +{ + tACL_CONN *p_acl_db = NULL; + tACL_DB_PARAM acl_params; + list_node_t *p_node = NULL; + + BTM_TRACE_DEBUG ("btm_handle_to_acl_index: %d\n", hci_handle); + + acl_params.type = ACL_DB_HANDLE; + acl_params.p_data1 = (void *)&hci_handle; + acl_params.p_data2 = NULL; + p_node = list_foreach(btm_cb.p_acl_db_list, btm_get_acl_db, (void *)&acl_params); + if (p_node) { + p_acl_db = list_node(p_node); + } + + return (p_acl_db); +} + +#if BLE_PRIVACY_SPT == TRUE +/******************************************************************************* +** +** Function btm_ble_get_acl_remote_addr +** +** Description This function reads the active remote address used for the +** connection. +** +** Returns success return TRUE, otherwise FALSE. +** +*******************************************************************************/ +BOOLEAN btm_ble_get_acl_remote_addr(tBTM_SEC_DEV_REC *p_dev_rec, BD_ADDR conn_addr, + tBLE_ADDR_TYPE *p_addr_type) +{ +#if BLE_INCLUDED == TRUE + BOOLEAN st = TRUE; + + if (p_dev_rec == NULL) { + BTM_TRACE_ERROR("btm_ble_get_acl_remote_addr can not find device with matching address\n"); + return FALSE; + } + + switch (p_dev_rec->ble.active_addr_type) { + case BTM_BLE_ADDR_PSEUDO: + memcpy(conn_addr, p_dev_rec->bd_addr, BD_ADDR_LEN); + * p_addr_type = p_dev_rec->ble.ble_addr_type; + break; + + case BTM_BLE_ADDR_RRA: + memcpy(conn_addr, p_dev_rec->ble.cur_rand_addr, BD_ADDR_LEN); + * p_addr_type = BLE_ADDR_RANDOM; + break; + + case BTM_BLE_ADDR_STATIC: + memcpy(conn_addr, p_dev_rec->ble.static_addr, BD_ADDR_LEN); + * p_addr_type = p_dev_rec->ble.static_addr_type; + break; + + default: + BTM_TRACE_ERROR("Unknown active address: %d\n", p_dev_rec->ble.active_addr_type); + st = FALSE; + break; + } + + return st; +#else + UNUSED(p_dev_rec); + UNUSED(conn_addr); + UNUSED(p_addr_type); + return FALSE; +#endif +} +#endif +/******************************************************************************* +** +** Function btm_acl_created +** +** Description This function is called by L2CAP when an ACL connection +** is created. +** +** Returns void +** +*******************************************************************************/ +void btm_acl_created (BD_ADDR bda, DEV_CLASS dc, UINT8 bdn[BTM_MAX_REM_BD_NAME_LEN], + UINT16 hci_handle, UINT8 link_role, tBT_TRANSPORT transport) +{ + tBTM_SEC_DEV_REC *p_dev_rec = NULL; + tACL_CONN *p; + + BTM_TRACE_DEBUG ("btm_acl_created hci_handle=%d link_role=%d transport=%d\n", + hci_handle, link_role, transport); + /* Ensure we don't have duplicates */ + p = btm_bda_to_acl(bda, transport); + if (p != (tACL_CONN *)NULL) { + p->hci_handle = hci_handle; + p->link_role = link_role; +#if BLE_INCLUDED == TRUE + p->transport = transport; +#endif + BTM_TRACE_DEBUG ("Duplicate btm_acl_created: RemBdAddr: %02x%02x%02x%02x%02x%02x\n", + bda[0], bda[1], bda[2], bda[3], bda[4], bda[5]); + BTM_SetLinkPolicy(p->remote_addr, &btm_cb.btm_def_link_policy); + return; + } + + /* Allocate acl_db entry */ + if (list_length(btm_cb.p_acl_db_list) >= MAX_L2CAP_LINKS) { + return; + } + else { + p = (tACL_CONN *)osi_malloc(sizeof(tACL_CONN)); + if (p && list_append(btm_cb.p_acl_db_list, p)) { + memset(p, 0, sizeof(tACL_CONN)); + p->in_use = TRUE; + p->hci_handle = hci_handle; + p->link_role = link_role; + p->link_up_issued = FALSE; + memcpy (p->remote_addr, bda, BD_ADDR_LEN); + /* Set the default version of the peer device to version4.0 before exchange the version with it. + If the peer device act as a master and don't exchange the version with us, then it can only use the + legacy connect instead of secure connection in the pairing step. */ + p->lmp_version = HCI_PROTO_VERSION_4_0; +#if BLE_INCLUDED == TRUE + p->transport = transport; +#if BLE_PRIVACY_SPT == TRUE + if (transport == BT_TRANSPORT_LE) { + btm_ble_refresh_local_resolvable_private_addr(bda, + btm_cb.ble_ctr_cb.addr_mgnt_cb.private_addr); + } +#else + p->conn_addr_type = BLE_ADDR_PUBLIC; + memcpy(p->conn_addr, &controller_get_interface()->get_address()->address, BD_ADDR_LEN); + BTM_TRACE_DEBUG ("conn_addr: RemBdAddr: %02x%02x%02x%02x%02x%02x\n", + p->conn_addr[0], p->conn_addr[1], p->conn_addr[2], p->conn_addr[3], p->conn_addr[4], p->conn_addr[5]); +#endif +#endif + p->switch_role_state = BTM_ACL_SWKEY_STATE_IDLE; + + p->p_pm_mode_db = btm_pm_sm_alloc(); +#if BTM_PM_DEBUG == TRUE + BTM_TRACE_DEBUG( "btm_pm_sm_alloc handle:%d st:%d", hci_handle, p->p_pm_mode_db->state); +#endif // BTM_PM_DEBUG + +#if (CLASSIC_BT_INCLUDED == TRUE) + btm_sec_update_legacy_auth_state(p, BTM_ACL_LEGACY_AUTH_NONE); +#endif + + if (dc) { + memcpy (p->remote_dc, dc, DEV_CLASS_LEN); + } + + if (bdn) { + memcpy (p->remote_name, bdn, BTM_MAX_REM_BD_NAME_LEN); + } + + /* if BR/EDR do something more */ + if (transport == BT_TRANSPORT_BR_EDR) { + btsnd_hcic_read_rmt_clk_offset (p->hci_handle); + btsnd_hcic_rmt_ver_req (p->hci_handle); + } + p_dev_rec = btm_find_dev_by_handle (hci_handle); + +#if (BLE_INCLUDED == TRUE) + if (p_dev_rec ) { + BTM_TRACE_DEBUG ("device_type=0x%x\n", p_dev_rec->device_type); + } +#endif + + if (p_dev_rec && !(transport == BT_TRANSPORT_LE)) { + if (!p_dev_rec->remote_secure_connection_previous_state) { + /* If remote features already known, copy them and continue connection setup */ + if ((p_dev_rec->num_read_pages) && + (p_dev_rec->num_read_pages <= (HCI_EXT_FEATURES_PAGE_MAX + 1))) { + memcpy (p->peer_lmp_features, p_dev_rec->features, + (HCI_FEATURE_BYTES_PER_PAGE * p_dev_rec->num_read_pages)); + p->num_read_pages = p_dev_rec->num_read_pages; +#if (CLASSIC_BT_INCLUDED == TRUE) + const UINT8 req_pend = (p_dev_rec->sm4 & BTM_SM4_REQ_PEND); +#endif ///CLASSIC_BT_INCLUDED == TRUE + /* Store the Peer Security Capabilites (in SM4 and rmt_sec_caps) */ +#if (SMP_INCLUDED == TRUE) + btm_sec_set_peer_sec_caps(p, p_dev_rec); +#endif ///SMP_INCLUDED == TRUE +#if (CLASSIC_BT_INCLUDED == TRUE) + BTM_TRACE_API("%s: pend:%d\n", __FUNCTION__, req_pend); + if (req_pend) { + /* Request for remaining Security Features (if any) */ + l2cu_resubmit_pending_sec_req (p_dev_rec->bd_addr); + } +#endif ///CLASSIC_BT_INCLUDED == TRUE + btm_establish_continue (p); + return; + } + } else { + /* If remote features indicated secure connection (SC) mode, check the remote feautres again*/ + /* this is to prevent from BIAS attack where attacker can downgrade SC mode*/ + btm_read_remote_features (p->hci_handle); + } + } + +#if (BLE_INCLUDED == TRUE) + /* If here, features are not known yet */ + if (p_dev_rec && transport == BT_TRANSPORT_LE) { +#if BLE_PRIVACY_SPT == TRUE + btm_ble_get_acl_remote_addr (p_dev_rec, p->active_remote_addr, + &p->active_remote_addr_type); +#endif + + if (link_role == HCI_ROLE_MASTER) { + btsnd_hcic_ble_read_remote_feat(p->hci_handle); + } else if (HCI_LE_SLAVE_INIT_FEAT_EXC_SUPPORTED(controller_get_interface()->get_features_ble()->as_array) + && link_role == HCI_ROLE_SLAVE) { + btsnd_hcic_rmt_ver_req (p->hci_handle); + } else { + btm_establish_continue(p); + } + } else +#endif + { + btm_read_remote_features (p->hci_handle); + } + + /* read page 1 - on rmt feature event for buffer reasons */ + return; + } + } +} + + +/******************************************************************************* +** +** Function btm_acl_report_role_change +** +** Description This function is called when the local device is deemed +** to be down. It notifies L2CAP of the failure. +** +** Returns void +** +*******************************************************************************/ +void btm_acl_report_role_change (UINT8 hci_status, BD_ADDR bda) +{ + tBTM_ROLE_SWITCH_CMPL ref_data; + BTM_TRACE_DEBUG ("btm_acl_report_role_change\n"); + if (btm_cb.devcb.p_switch_role_cb + && (bda && (0 == memcmp(btm_cb.devcb.switch_role_ref_data.remote_bd_addr, bda, BD_ADDR_LEN)))) { + memcpy (&ref_data, &btm_cb.devcb.switch_role_ref_data, sizeof(tBTM_ROLE_SWITCH_CMPL)); + ref_data.hci_status = hci_status; + (*btm_cb.devcb.p_switch_role_cb)(&ref_data); + memset (&btm_cb.devcb.switch_role_ref_data, 0, sizeof(tBTM_ROLE_SWITCH_CMPL)); + btm_cb.devcb.p_switch_role_cb = NULL; + } +} + +/******************************************************************************* +** +** Function btm_acl_removed +** +** Description This function is called by L2CAP when an ACL connection +** is removed. Since only L2CAP creates ACL links, we use +** the L2CAP link index as our index into the control blocks. +** +** Returns void +** +*******************************************************************************/ +void btm_acl_removed (BD_ADDR bda, tBT_TRANSPORT transport) +{ + tACL_CONN *p; + tBTM_BL_EVENT_DATA evt_data; +#if (defined BLE_INCLUDED && BLE_INCLUDED == TRUE) + tBTM_SEC_DEV_REC *p_dev_rec = NULL; +#endif + BTM_TRACE_DEBUG ("btm_acl_removed\n"); + p = btm_bda_to_acl(bda, transport); + if (p != (tACL_CONN *)NULL) { + p->in_use = FALSE; + + /* if the disconnected channel has a pending role switch, clear it now */ + btm_acl_report_role_change(HCI_ERR_NO_CONNECTION, bda); + + /* Only notify if link up has had a chance to be issued */ + if (p->link_up_issued) { + p->link_up_issued = FALSE; + + /* If anyone cares, tell him database changed */ + if (btm_cb.p_bl_changed_cb) { + evt_data.event = BTM_BL_DISCN_EVT; + evt_data.discn.p_bda = bda; +#if BLE_INCLUDED == TRUE + evt_data.discn.handle = p->hci_handle; + evt_data.discn.transport = p->transport; +#endif + (*btm_cb.p_bl_changed_cb)(&evt_data); + } + + btm_acl_update_busy_level (BTM_BLI_ACL_DOWN_EVT); + } + +#if (defined BLE_INCLUDED && BLE_INCLUDED == TRUE) + + BTM_TRACE_DEBUG ("acl hci_handle=%d transport=%d connectable_mode=0x%0x link_role=%d\n", + p->hci_handle, + p->transport, + btm_cb.ble_ctr_cb.inq_var.connectable_mode, + p->link_role); + + p_dev_rec = btm_find_dev(bda); + if ( p_dev_rec) { + BTM_TRACE_DEBUG("before update p_dev_rec->sec_flags=0x%x\n", p_dev_rec->sec_flags); + if (p->transport == BT_TRANSPORT_LE) { + BTM_TRACE_DEBUG("LE link down\n"); + p_dev_rec->sec_flags &= ~(BTM_SEC_LE_ENCRYPTED | BTM_SEC_ROLE_SWITCHED); + if ( (p_dev_rec->sec_flags & BTM_SEC_LE_LINK_KEY_KNOWN) == 0) { + BTM_TRACE_DEBUG("Not Bonded\n"); + p_dev_rec->sec_flags &= ~(BTM_SEC_LE_LINK_KEY_AUTHED | BTM_SEC_LE_AUTHENTICATED); + } else { + BTM_TRACE_DEBUG("Bonded\n"); + } + } else { + BTM_TRACE_DEBUG("Bletooth link down\n"); + p_dev_rec->sec_flags &= ~(BTM_SEC_AUTHORIZED | BTM_SEC_AUTHENTICATED + | BTM_SEC_ENCRYPTED | BTM_SEC_ROLE_SWITCHED); + } + BTM_TRACE_DEBUG("after update p_dev_rec->sec_flags=0x%x\n", p_dev_rec->sec_flags); + } else { + BTM_TRACE_ERROR("Device not found\n"); + + } +#endif + + list_remove(btm_cb.p_pm_mode_db_list, p->p_pm_mode_db); + /* Clear the ACL connection data */ + memset(p, 0, sizeof(tACL_CONN)); + if (list_remove(btm_cb.p_acl_db_list, p)) { + p = NULL; + } + } +} + + +/******************************************************************************* +** +** Function btm_acl_device_down +** +** Description This function is called when the local device is deemed +** to be down. It notifies L2CAP of the failure. +** +** Returns void +** +*******************************************************************************/ +void btm_acl_device_down (void) +{ + tACL_CONN *p = NULL; + BTM_TRACE_DEBUG ("btm_acl_device_down\n"); + for (list_node_t *p_node = list_begin(btm_cb.p_acl_db_list); p_node;) { + list_node_t *p_next = list_next(p_node); + p = list_node(p_node); + if (p && p->in_use) { + BTM_TRACE_DEBUG ("hci_handle=%d HCI_ERR_HW_FAILURE \n", p->hci_handle ); + l2c_link_hci_disc_comp (p->hci_handle, HCI_ERR_HW_FAILURE); + } + p_node = p_next; + } +} +/******************************************************************************* +** +** Function btm_acl_update_busy_level +** +** Description This function is called to update the busy level of the system +** . +** +** Returns void +** +*******************************************************************************/ +void btm_acl_update_busy_level (tBTM_BLI_EVENT event) +{ + tBTM_BL_UPDATE_DATA evt; + UINT8 busy_level; + BTM_TRACE_DEBUG ("btm_acl_update_busy_level\n"); + BOOLEAN old_inquiry_state = btm_cb.is_inquiry; + switch (event) { + case BTM_BLI_ACL_UP_EVT: + BTM_TRACE_DEBUG ("BTM_BLI_ACL_UP_EVT\n"); + break; + case BTM_BLI_ACL_DOWN_EVT: + BTM_TRACE_DEBUG ("BTM_BLI_ACL_DOWN_EVT\n"); + break; + case BTM_BLI_PAGE_EVT: + BTM_TRACE_DEBUG ("BTM_BLI_PAGE_EVT\n"); + btm_cb.is_paging = TRUE; + evt.busy_level_flags = BTM_BL_PAGING_STARTED; + break; + case BTM_BLI_PAGE_DONE_EVT: + BTM_TRACE_DEBUG ("BTM_BLI_PAGE_DONE_EVT\n"); + btm_cb.is_paging = FALSE; + evt.busy_level_flags = BTM_BL_PAGING_COMPLETE; + break; + case BTM_BLI_INQ_EVT: + BTM_TRACE_DEBUG ("BTM_BLI_INQ_EVT\n"); + btm_cb.is_inquiry = TRUE; + evt.busy_level_flags = BTM_BL_INQUIRY_STARTED; + break; + case BTM_BLI_INQ_CANCEL_EVT: + BTM_TRACE_DEBUG ("BTM_BLI_INQ_CANCEL_EVT\n"); + btm_cb.is_inquiry = FALSE; + evt.busy_level_flags = BTM_BL_INQUIRY_CANCELLED; + break; + case BTM_BLI_INQ_DONE_EVT: + BTM_TRACE_DEBUG ("BTM_BLI_INQ_DONE_EVT\n"); + btm_cb.is_inquiry = FALSE; + evt.busy_level_flags = BTM_BL_INQUIRY_COMPLETE; + break; + } + + if (btm_cb.is_paging || btm_cb.is_inquiry) { + busy_level = 10; + } else { + busy_level = BTM_GetNumAclLinks(); + } + + if ((busy_level != btm_cb.busy_level) || (old_inquiry_state != btm_cb.is_inquiry)) { + evt.event = BTM_BL_UPDATE_EVT; + evt.busy_level = busy_level; + btm_cb.busy_level = busy_level; + if (btm_cb.p_bl_changed_cb && (btm_cb.bl_evt_mask & BTM_BL_UPDATE_MASK)) { + (*btm_cb.p_bl_changed_cb)((tBTM_BL_EVENT_DATA *)&evt); + } + } +} + +/******************************************************************************* +** +** Function btm_acl_link_stat_report +** +** Description This function is called when the ACL link related + events are received from controller. It reports the ACL + link status to upper layer. + +** Returns void +** +*******************************************************************************/ +void btm_acl_link_stat_report(tBTM_ACL_LINK_STAT_EVENT_DATA *p_data) +{ + BTM_TRACE_DEBUG ("btm_acl_link_stat_report\n"); + + if (btm_cb.p_acl_link_stat_cb) { + (*btm_cb.p_acl_link_stat_cb)(p_data); + } +} + +/******************************************************************************* +** +** Function BTM_GetRole +** +** Description This function is called to get the role of the local device +** for the ACL connection with the specified remote device +** +** Returns BTM_SUCCESS if connection exists. +** BTM_UNKNOWN_ADDR if no active link with bd addr specified +** +*******************************************************************************/ +tBTM_STATUS BTM_GetRole (BD_ADDR remote_bd_addr, UINT8 *p_role) +{ + tACL_CONN *p; + BTM_TRACE_DEBUG ("BTM_GetRole\n"); + if ((p = btm_bda_to_acl(remote_bd_addr, BT_TRANSPORT_BR_EDR)) == NULL) { + *p_role = BTM_ROLE_UNDEFINED; + return (BTM_UNKNOWN_ADDR); + } + + /* Get the current role */ + *p_role = p->link_role; + return (BTM_SUCCESS); +} + + +/******************************************************************************* +** +** Function BTM_SwitchRole +** +** Description This function is called to switch role between master and +** slave. If role is already set it will do nothing. If the +** command was initiated, the callback function is called upon +** completion. +** +** Returns BTM_SUCCESS if already in specified role. +** BTM_CMD_STARTED if command issued to controller. +** BTM_NO_RESOURCES if couldn't allocate memory to issue command +** BTM_UNKNOWN_ADDR if no active link with bd addr specified +** BTM_MODE_UNSUPPORTED if local device does not support role switching +** BTM_BUSY if the previous command is not completed +** +*******************************************************************************/ +tBTM_STATUS BTM_SwitchRole (BD_ADDR remote_bd_addr, UINT8 new_role, tBTM_CMPL_CB *p_cb) +{ + tACL_CONN *p; + tBTM_SEC_DEV_REC *p_dev_rec = NULL; +#if BTM_SCO_INCLUDED == TRUE + BOOLEAN is_sco_active; +#endif + tBTM_STATUS status; + tBTM_PM_MODE pwr_mode; + tBTM_PM_PWR_MD settings; +#if (BT_USE_TRACES == TRUE) + BD_ADDR_PTR p_bda; +#endif + BTM_TRACE_API ("BTM_SwitchRole BDA: %02x-%02x-%02x-%02x-%02x-%02x\n", + remote_bd_addr[0], remote_bd_addr[1], remote_bd_addr[2], + remote_bd_addr[3], remote_bd_addr[4], remote_bd_addr[5]); + + /* Make sure the local device supports switching */ + if (!controller_get_interface()->supports_master_slave_role_switch()) { + return (BTM_MODE_UNSUPPORTED); + } + + if (btm_cb.devcb.p_switch_role_cb && p_cb) { +#if (BT_USE_TRACES == TRUE) + p_bda = btm_cb.devcb.switch_role_ref_data.remote_bd_addr; + BTM_TRACE_DEBUG ("Role switch on other device is in progress 0x%02x%02x%02x%02x%02x%02x\n", + p_bda[0], p_bda[1], p_bda[2], + p_bda[3], p_bda[4], p_bda[5]); +#endif + return (BTM_BUSY); + } + + if ((p = btm_bda_to_acl(remote_bd_addr, BT_TRANSPORT_BR_EDR)) == NULL) { + return (BTM_UNKNOWN_ADDR); + } + + /* Finished if already in desired role */ + if (p->link_role == new_role) { + return (BTM_SUCCESS); + } + +#if BTM_SCO_INCLUDED == TRUE + /* Check if there is any SCO Active on this BD Address */ + is_sco_active = btm_is_sco_active_by_bdaddr(remote_bd_addr); + + if (is_sco_active == TRUE) { + return (BTM_NO_RESOURCES); + } +#endif + + /* Ignore role switch request if the previous request was not completed */ + if (p->switch_role_state != BTM_ACL_SWKEY_STATE_IDLE) { + BTM_TRACE_DEBUG ("BTM_SwitchRole busy: %d\n", + p->switch_role_state); + return (BTM_BUSY); + } + + if ((status = BTM_ReadPowerMode(p->remote_addr, &pwr_mode)) != BTM_SUCCESS) { + return (status); + } + + /* Wake up the link if in sniff or park before attempting switch */ + if (pwr_mode == BTM_PM_MD_PARK || pwr_mode == BTM_PM_MD_SNIFF) { + memset( (void *)&settings, 0, sizeof(settings)); + settings.mode = BTM_PM_MD_ACTIVE; + status = BTM_SetPowerMode (BTM_PM_SET_ONLY_ID, p->remote_addr, &settings); + if (status != BTM_CMD_STARTED) { + return (BTM_WRONG_MODE); + } + + p->switch_role_state = BTM_ACL_SWKEY_STATE_MODE_CHANGE; + } + /* some devices do not support switch while encryption is on */ + else { + p_dev_rec = btm_find_dev (remote_bd_addr); + if ((p_dev_rec != NULL) + && ((p_dev_rec->sec_flags & BTM_SEC_ENCRYPTED) != 0) + && !BTM_EPR_AVAILABLE(p)) { + /* bypass turning off encryption if change link key is already doing it */ + if (p->encrypt_state != BTM_ACL_ENCRYPT_STATE_ENCRYPT_OFF) { + if (!btsnd_hcic_set_conn_encrypt (p->hci_handle, FALSE)) { + return (BTM_NO_RESOURCES); + } else { + p->encrypt_state = BTM_ACL_ENCRYPT_STATE_ENCRYPT_OFF; + } + } + + p->switch_role_state = BTM_ACL_SWKEY_STATE_ENCRYPTION_OFF; + } else { + if (!btsnd_hcic_switch_role (remote_bd_addr, new_role)) { + return (BTM_NO_RESOURCES); + } + + p->switch_role_state = BTM_ACL_SWKEY_STATE_IN_PROGRESS; + +#if BTM_DISC_DURING_RS == TRUE + if (p_dev_rec) { + p_dev_rec->rs_disc_pending = BTM_SEC_RS_PENDING; + } +#endif + } + } + + /* Initialize return structure in case request fails */ + if (p_cb) { + memcpy (btm_cb.devcb.switch_role_ref_data.remote_bd_addr, remote_bd_addr, + BD_ADDR_LEN); + btm_cb.devcb.switch_role_ref_data.role = new_role; + /* initialized to an error code */ + btm_cb.devcb.switch_role_ref_data.hci_status = HCI_ERR_UNSUPPORTED_VALUE; + btm_cb.devcb.p_switch_role_cb = p_cb; + } + return (BTM_CMD_STARTED); +} + +/******************************************************************************* +** +** Function btm_acl_encrypt_change +** +** Description This function is when encryption of the connection is +** completed by the LM. Checks to see if a role switch or +** change of link key was active and initiates or continues +** process if needed. +** +** Returns void +** +*******************************************************************************/ +void btm_acl_encrypt_change (UINT16 handle, UINT8 status, UINT8 encr_enable) +{ + tACL_CONN *p; + tBTM_SEC_DEV_REC *p_dev_rec; + tBTM_BL_ROLE_CHG_DATA evt; + + BTM_TRACE_DEBUG ("btm_acl_encrypt_change handle=%d status=%d encr_enabl=%d\n", + handle, status, encr_enable); + p = btm_handle_to_acl(handle); + if (p == NULL) { + return; + } + /* Process Role Switch if active */ + if (p->switch_role_state == BTM_ACL_SWKEY_STATE_ENCRYPTION_OFF) { + /* if encryption turn off failed we still will try to switch role */ + if (encr_enable) { + p->switch_role_state = BTM_ACL_SWKEY_STATE_IDLE; + p->encrypt_state = BTM_ACL_ENCRYPT_STATE_IDLE; + } else { + p->switch_role_state = BTM_ACL_SWKEY_STATE_SWITCHING; + p->encrypt_state = BTM_ACL_ENCRYPT_STATE_TEMP_FUNC; + } + + if (!btsnd_hcic_switch_role (p->remote_addr, (UINT8)!p->link_role)) { + p->switch_role_state = BTM_ACL_SWKEY_STATE_IDLE; + p->encrypt_state = BTM_ACL_ENCRYPT_STATE_IDLE; + btm_acl_report_role_change(btm_cb.devcb.switch_role_ref_data.hci_status, p->remote_addr); + } +#if BTM_DISC_DURING_RS == TRUE + else { + if ((p_dev_rec = btm_find_dev (p->remote_addr)) != NULL) { + p_dev_rec->rs_disc_pending = BTM_SEC_RS_PENDING; + } + } +#endif + + } + /* Finished enabling Encryption after role switch */ + else if (p->switch_role_state == BTM_ACL_SWKEY_STATE_ENCRYPTION_ON) { + p->switch_role_state = BTM_ACL_SWKEY_STATE_IDLE; + p->encrypt_state = BTM_ACL_ENCRYPT_STATE_IDLE; + btm_acl_report_role_change(btm_cb.devcb.switch_role_ref_data.hci_status, p->remote_addr); + + /* if role change event is registered, report it now */ + if (btm_cb.p_bl_changed_cb && (btm_cb.bl_evt_mask & BTM_BL_ROLE_CHG_MASK)) { + evt.event = BTM_BL_ROLE_CHG_EVT; + evt.new_role = btm_cb.devcb.switch_role_ref_data.role; + evt.p_bda = btm_cb.devcb.switch_role_ref_data.remote_bd_addr; + evt.hci_status = btm_cb.devcb.switch_role_ref_data.hci_status; + (*btm_cb.p_bl_changed_cb)((tBTM_BL_EVENT_DATA *)&evt); + + BTM_TRACE_DEBUG("Role Switch Event: new_role 0x%02x, HCI Status 0x%02x, rs_st:%d\n", + evt.new_role, evt.hci_status, p->switch_role_state); + } + +#if BTM_DISC_DURING_RS == TRUE + /* If a disconnect is pending, issue it now that role switch has completed */ + if ((p_dev_rec = btm_find_dev (p->remote_addr)) != NULL) { + if (p_dev_rec->rs_disc_pending == BTM_SEC_DISC_PENDING) { + BTM_TRACE_WARNING("btm_acl_encrypt_change -> Issuing delayed HCI_Disconnect!!!\n"); + btsnd_hcic_disconnect(p_dev_rec->hci_handle, HCI_ERR_PEER_USER); + } + BTM_TRACE_WARNING("btm_acl_encrypt_change: tBTM_SEC_DEV:0x%x rs_disc_pending=%d\n", + (UINT32)p_dev_rec, p_dev_rec->rs_disc_pending); + p_dev_rec->rs_disc_pending = BTM_SEC_RS_NOT_PENDING; /* reset flag */ + } +#endif + } +} +/******************************************************************************* +** +** Function BTM_SetLinkPolicy +** +** Description Create and send HCI "Write Policy Set" command +** +** Returns status of the operation +** +*******************************************************************************/ +tBTM_STATUS BTM_SetLinkPolicy (BD_ADDR remote_bda, UINT16 *settings) +{ + tACL_CONN *p; + UINT8 *localFeatures = BTM_ReadLocalFeatures(); + BTM_TRACE_DEBUG ("BTM_SetLinkPolicy\n"); + /* BTM_TRACE_API ("BTM_SetLinkPolicy: requested settings: 0x%04x", *settings ); */ + + /* First, check if hold mode is supported */ + if (*settings != HCI_DISABLE_ALL_LM_MODES) { + if ( (*settings & HCI_ENABLE_MASTER_SLAVE_SWITCH) && (!HCI_SWITCH_SUPPORTED(localFeatures)) ) { + *settings &= (~HCI_ENABLE_MASTER_SLAVE_SWITCH); + BTM_TRACE_API ("BTM_SetLinkPolicy switch not supported (settings: 0x%04x)\n", *settings ); + } + if ( (*settings & HCI_ENABLE_HOLD_MODE) && (!HCI_HOLD_MODE_SUPPORTED(localFeatures)) ) { + *settings &= (~HCI_ENABLE_HOLD_MODE); + BTM_TRACE_API ("BTM_SetLinkPolicy hold not supported (settings: 0x%04x)\n", *settings ); + } + if ( (*settings & HCI_ENABLE_SNIFF_MODE) && (!HCI_SNIFF_MODE_SUPPORTED(localFeatures)) ) { + *settings &= (~HCI_ENABLE_SNIFF_MODE); + BTM_TRACE_API ("BTM_SetLinkPolicy sniff not supported (settings: 0x%04x)\n", *settings ); + } + if ( (*settings & HCI_ENABLE_PARK_MODE) && (!HCI_PARK_MODE_SUPPORTED(localFeatures)) ) { + *settings &= (~HCI_ENABLE_PARK_MODE); + BTM_TRACE_API ("BTM_SetLinkPolicy park not supported (settings: 0x%04x)\n", *settings ); + } + } + + if ((p = btm_bda_to_acl(remote_bda, BT_TRANSPORT_BR_EDR)) != NULL) { + return (btsnd_hcic_write_policy_set (p->hci_handle, *settings) ? BTM_CMD_STARTED : BTM_NO_RESOURCES); + } + + /* If here, no BD Addr found */ + return (BTM_UNKNOWN_ADDR); +} + +/******************************************************************************* +** +** Function BTM_SetDefaultLinkPolicy +** +** Description Set the default value for HCI "Write Policy Set" command +** to use when an ACL link is created. +** +** Returns void +** +*******************************************************************************/ +void BTM_SetDefaultLinkPolicy (UINT16 settings) +{ + UINT8 *localFeatures = BTM_ReadLocalFeatures(); + + BTM_TRACE_DEBUG("BTM_SetDefaultLinkPolicy setting:0x%04x\n", settings); + + if ((settings & HCI_ENABLE_MASTER_SLAVE_SWITCH) && (!HCI_SWITCH_SUPPORTED(localFeatures))) { + settings &= ~HCI_ENABLE_MASTER_SLAVE_SWITCH; + BTM_TRACE_DEBUG("BTM_SetDefaultLinkPolicy switch not supported (settings: 0x%04x)\n", settings); + } + if ((settings & HCI_ENABLE_HOLD_MODE) && (!HCI_HOLD_MODE_SUPPORTED(localFeatures))) { + settings &= ~HCI_ENABLE_HOLD_MODE; + BTM_TRACE_DEBUG("BTM_SetDefaultLinkPolicy hold not supported (settings: 0x%04x)\n", settings); + } + if ((settings & HCI_ENABLE_SNIFF_MODE) && (!HCI_SNIFF_MODE_SUPPORTED(localFeatures))) { + settings &= ~HCI_ENABLE_SNIFF_MODE; + BTM_TRACE_DEBUG("BTM_SetDefaultLinkPolicy sniff not supported (settings: 0x%04x)\n", settings); + } + if ((settings & HCI_ENABLE_PARK_MODE) && (!HCI_PARK_MODE_SUPPORTED(localFeatures))) { + settings &= ~HCI_ENABLE_PARK_MODE; + BTM_TRACE_DEBUG("BTM_SetDefaultLinkPolicy park not supported (settings: 0x%04x)\n", settings); + } + BTM_TRACE_DEBUG("Set DefaultLinkPolicy:0x%04x\n", settings); + + btm_cb.btm_def_link_policy = settings; + + /* Set the default Link Policy of the controller */ + btsnd_hcic_write_def_policy_set(settings); +} + +/******************************************************************************* +** +** Function btm_read_remote_version_complete +** +** Description This function is called when the command complete message +** is received from the HCI for the remote version info. +** +** Returns void +** +*******************************************************************************/ +void btm_read_remote_version_complete (UINT8 *p) +{ + tACL_CONN *p_acl_cb = NULL; + UINT8 status; + UINT16 handle; + BTM_TRACE_DEBUG ("btm_read_remote_version_complete\n"); + + STREAM_TO_UINT8 (status, p); + STREAM_TO_UINT16 (handle, p); + + /* Look up the connection by handle and copy features */ + p_acl_cb = btm_handle_to_acl(handle); + if (p_acl_cb) { + if (status == HCI_SUCCESS) { + STREAM_TO_UINT8 (p_acl_cb->lmp_version, p); + STREAM_TO_UINT16 (p_acl_cb->manufacturer, p); + STREAM_TO_UINT16 (p_acl_cb->lmp_subversion, p); + } +#if BLE_INCLUDED == TRUE + if (p_acl_cb->transport == BT_TRANSPORT_LE) { + if(p_acl_cb->link_role == HCI_ROLE_MASTER) { + if (HCI_LE_DATA_LEN_EXT_SUPPORTED(p_acl_cb->peer_le_features)) { + uint16_t data_length = controller_get_interface()->get_ble_default_data_packet_length(); + uint16_t data_txtime = controller_get_interface()->get_ble_default_data_packet_txtime(); + if (data_length != p_acl_cb->data_length_params.tx_len) { + p_acl_cb->data_len_updating = true; + btsnd_hcic_ble_set_data_length(p_acl_cb->hci_handle, data_length, data_txtime); + } + } + l2cble_notify_le_connection (p_acl_cb->remote_addr); + } else { + //slave role, read remote feature + btsnd_hcic_ble_read_remote_feat(p_acl_cb->hci_handle); + } + } +#endif + } +} + +/******************************************************************************* +** +** Function btm_process_remote_ext_features +** +** Description Local function called to process all extended features pages +** read from a remote device. +** +** Returns void +** +*******************************************************************************/ +void btm_process_remote_ext_features (tACL_CONN *p_acl_cb, UINT8 num_read_pages) +{ + UINT16 handle = p_acl_cb->hci_handle; + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev_by_handle (handle); + UINT8 page_idx; + + BTM_TRACE_DEBUG ("btm_process_remote_ext_features\n"); + + /* Make sure we have the record to save remote features information */ + if (p_dev_rec == NULL) { + /* Get a new device; might be doing dedicated bonding */ + p_dev_rec = btm_find_or_alloc_dev (p_acl_cb->remote_addr); + } + + p_acl_cb->num_read_pages = num_read_pages; + p_dev_rec->num_read_pages = num_read_pages; + + /* Move the pages to placeholder */ + for (page_idx = 0; page_idx < num_read_pages; page_idx++) { + if (page_idx > HCI_EXT_FEATURES_PAGE_MAX) { + BTM_TRACE_ERROR("%s: page=%d unexpected\n", __FUNCTION__, page_idx); + break; + } + memcpy (p_dev_rec->features[page_idx], p_acl_cb->peer_lmp_features[page_idx], + HCI_FEATURE_BYTES_PER_PAGE); + } + + const UINT8 req_pend = (p_dev_rec->sm4 & BTM_SM4_REQ_PEND); +#if (SMP_INCLUDED == TRUE) + /* Store the Peer Security Capabilites (in SM4 and rmt_sec_caps) */ + btm_sec_set_peer_sec_caps(p_acl_cb, p_dev_rec); +#endif ///SMP_INCLUDED == TRUE + BTM_TRACE_API("%s: pend:%d\n", __FUNCTION__, req_pend); + if (req_pend) { +#if (CLASSIC_BT_INCLUDED == TRUE) + /* Request for remaining Security Features (if any) */ + l2cu_resubmit_pending_sec_req (p_dev_rec->bd_addr); +#endif ///CLASSIC_BT_INCLUDED == TRUE + } +} + + +/******************************************************************************* +** +** Function btm_read_remote_features +** +** Description Local function called to send a read remote supported features/ +** remote extended features page[0]. +** +** Returns void +** +*******************************************************************************/ +void btm_read_remote_features (UINT16 handle) +{ + tACL_CONN *p_acl_cb; + + BTM_TRACE_DEBUG("btm_read_remote_features() handle: %d\n", handle); + + p_acl_cb = btm_handle_to_acl(handle); + if (p_acl_cb == NULL) { + BTM_TRACE_ERROR("btm_read_remote_features handle=%d invalid\n", handle); + return; + } + + p_acl_cb->num_read_pages = 0; + memset (p_acl_cb->peer_lmp_features, 0, sizeof(p_acl_cb->peer_lmp_features)); + + /* first send read remote supported features HCI command */ + /* because we don't know whether the remote support extended feature command */ + btsnd_hcic_rmt_features_req (handle); +} + + +/******************************************************************************* +** +** Function btm_read_remote_ext_features +** +** Description Local function called to send a read remote extended features +** +** Returns void +** +*******************************************************************************/ +void btm_read_remote_ext_features (UINT16 handle, UINT8 page_number) +{ + BTM_TRACE_DEBUG("btm_read_remote_ext_features() handle: %d page: %d\n", handle, page_number); + + btsnd_hcic_rmt_ext_features(handle, page_number); +} + + +/******************************************************************************* +** +** Function btm_read_remote_features_complete +** +** Description This function is called when the remote supported features +** complete event is received from the HCI. +** +** Returns void +** +*******************************************************************************/ +void btm_read_remote_features_complete (UINT8 *p) +{ + tACL_CONN *p_acl_cb; + UINT8 status; + UINT16 handle; + + BTM_TRACE_DEBUG ("btm_read_remote_features_complete\n"); + STREAM_TO_UINT8 (status, p); + + if (status != HCI_SUCCESS) { + BTM_TRACE_ERROR ("btm_read_remote_features_complete failed (status 0x%02x)\n", status); + return; + } + + STREAM_TO_UINT16 (handle, p); + + p_acl_cb = btm_handle_to_acl(handle); + if (p_acl_cb == NULL) { + BTM_TRACE_ERROR("btm_read_remote_features_complete handle=%d invalid\n", handle); + return; + } + + /* Copy the received features page */ + STREAM_TO_ARRAY(p_acl_cb->peer_lmp_features[HCI_EXT_FEATURES_PAGE_0], p, + HCI_FEATURE_BYTES_PER_PAGE); + + if ((HCI_LMP_EXTENDED_SUPPORTED(p_acl_cb->peer_lmp_features[HCI_EXT_FEATURES_PAGE_0])) && + (controller_get_interface()->supports_reading_remote_extended_features())) { + /* if the remote controller has extended features and local controller supports + ** HCI_Read_Remote_Extended_Features command then start reading these feature starting + ** with extended features page 1 */ + BTM_TRACE_DEBUG ("Start reading remote extended features\n"); + btm_read_remote_ext_features(handle, HCI_EXT_FEATURES_PAGE_1); + return; + } + + /* Remote controller has no extended features. Process remote controller supported features + (features page HCI_EXT_FEATURES_PAGE_0). */ + btm_process_remote_ext_features (p_acl_cb, 1); + + /* Continue with HCI connection establishment */ + btm_establish_continue (p_acl_cb); +} + +/******************************************************************************* +** +** Function btm_read_remote_ext_features_complete +** +** Description This function is called when the remote extended features +** complete event is received from the HCI. +** +** Returns void +** +*******************************************************************************/ +void btm_read_remote_ext_features_complete (UINT8 *p) +{ + tACL_CONN *p_acl_cb; + UINT8 page_num, max_page; + UINT16 handle; + + BTM_TRACE_DEBUG ("btm_read_remote_ext_features_complete\n"); + + ++p; + STREAM_TO_UINT16 (handle, p); + STREAM_TO_UINT8 (page_num, p); + STREAM_TO_UINT8 (max_page, p); + + /* Validate parameters */ + p_acl_cb = btm_handle_to_acl(handle); + if (p_acl_cb == NULL) { + BTM_TRACE_ERROR("btm_read_remote_ext_features_complete handle=%d invalid\n", handle); + return; + } + + if (max_page > HCI_EXT_FEATURES_PAGE_MAX) { + BTM_TRACE_ERROR("btm_read_remote_ext_features_complete page=%d unknown", max_page); + } + + + /* Copy the received features page */ + STREAM_TO_ARRAY(p_acl_cb->peer_lmp_features[page_num], p, HCI_FEATURE_BYTES_PER_PAGE); + + /* If there is the next remote features page and + * we have space to keep this page data - read this page */ + if ((page_num < max_page) && (page_num < HCI_EXT_FEATURES_PAGE_MAX)) { + page_num++; + BTM_TRACE_DEBUG("BTM reads next remote extended features page (%d)\n", page_num); + btm_read_remote_ext_features (handle, page_num); + return; + } + + /* Reading of remote feature pages is complete */ + BTM_TRACE_DEBUG("BTM reached last remote extended features page (%d)\n", page_num); + + /* Process the pages */ + btm_process_remote_ext_features (p_acl_cb, (UINT8) (page_num + 1)); + + /* Continue with HCI connection establishment */ + btm_establish_continue (p_acl_cb); +} + +/******************************************************************************* +** +** Function btm_read_remote_ext_features_failed +** +** Description This function is called when the remote extended features +** complete event returns a failed status. +** +** Returns void +** +*******************************************************************************/ +void btm_read_remote_ext_features_failed (UINT8 status, UINT16 handle) +{ + tACL_CONN *p_acl_cb; + + BTM_TRACE_WARNING ("btm_read_remote_ext_features_failed (status 0x%02x) for handle %d\n", + status, handle); + + p_acl_cb = btm_handle_to_acl(handle); + if (p_acl_cb == NULL) { + BTM_TRACE_ERROR("btm_read_remote_ext_features_failed handle=%d invalid\n", handle); + return; + } + + /* Process supported features only */ + btm_process_remote_ext_features (p_acl_cb, 1); + + /* Continue HCI connection establishment */ + btm_establish_continue (p_acl_cb); +} + +/******************************************************************************* +** +** Function btm_establish_continue +** +** Description This function is called when the command complete message +** is received from the HCI for the read local link policy request. +** +** Returns void +** +*******************************************************************************/ +void btm_establish_continue (tACL_CONN *p_acl_cb) +{ + tBTM_BL_EVENT_DATA evt_data; + BTM_TRACE_DEBUG ("btm_establish_continue\n"); +#if (!defined(BTM_BYPASS_EXTRA_ACL_SETUP) || BTM_BYPASS_EXTRA_ACL_SETUP == FALSE) +#if (defined BLE_INCLUDED && BLE_INCLUDED == TRUE) + if (p_acl_cb->transport == BT_TRANSPORT_BR_EDR) +#endif + { + /* For now there are a some devices that do not like sending */ + /* commands events and data at the same time. */ + /* Set the packet types to the default allowed by the device */ + btm_set_packet_types (p_acl_cb, btm_cb.btm_acl_pkt_types_supported); + + if (btm_cb.btm_def_link_policy) { + BTM_SetLinkPolicy (p_acl_cb->remote_addr, &btm_cb.btm_def_link_policy); + } + } +#endif + p_acl_cb->link_up_issued = TRUE; + + /* If anyone cares, tell him database changed */ + if (btm_cb.p_bl_changed_cb) { + evt_data.event = BTM_BL_CONN_EVT; + evt_data.conn.p_bda = p_acl_cb->remote_addr; + evt_data.conn.p_bdn = p_acl_cb->remote_name; + evt_data.conn.p_dc = p_acl_cb->remote_dc; + evt_data.conn.p_features = p_acl_cb->peer_lmp_features[HCI_EXT_FEATURES_PAGE_0]; + evt_data.conn.sc_downgrade = p_acl_cb->sc_downgrade; +#if BLE_INCLUDED == TRUE + evt_data.conn.handle = p_acl_cb->hci_handle; + evt_data.conn.transport = p_acl_cb->transport; +#endif + + (*btm_cb.p_bl_changed_cb)(&evt_data); + } + btm_acl_update_busy_level (BTM_BLI_ACL_UP_EVT); +} + + +/******************************************************************************* +** +** Function BTM_SetDefaultLinkSuperTout +** +** Description Set the default value for HCI "Write Link Supervision Timeout" +** command to use when an ACL link is created. +** +** Returns void +** +*******************************************************************************/ +void BTM_SetDefaultLinkSuperTout (UINT16 timeout) +{ + BTM_TRACE_DEBUG ("BTM_SetDefaultLinkSuperTout\n"); + btm_cb.btm_def_link_super_tout = timeout; +} + +/******************************************************************************* +** +** Function BTM_GetLinkSuperTout +** +** Description Read the link supervision timeout value of the connection +** +** Returns status of the operation +** +*******************************************************************************/ +tBTM_STATUS BTM_GetLinkSuperTout (BD_ADDR remote_bda, UINT16 *p_timeout) +{ + tACL_CONN *p = btm_bda_to_acl(remote_bda, BT_TRANSPORT_BR_EDR); + + BTM_TRACE_DEBUG ("BTM_GetLinkSuperTout\n"); + if (p != (tACL_CONN *)NULL) { + *p_timeout = p->link_super_tout; + return (BTM_SUCCESS); + } + /* If here, no BD Addr found */ + return (BTM_UNKNOWN_ADDR); +} + + +/******************************************************************************* +** +** Function BTM_SetLinkSuperTout +** +** Description Create and send HCI "Write Link Supervision Timeout" command +** +** Returns status of the operation +** +*******************************************************************************/ +tBTM_STATUS BTM_SetLinkSuperTout (BD_ADDR remote_bda, UINT16 timeout) +{ + tACL_CONN *p = btm_bda_to_acl(remote_bda, BT_TRANSPORT_BR_EDR); + + BTM_TRACE_DEBUG ("BTM_SetLinkSuperTout\n"); + if (p != (tACL_CONN *)NULL) { + p->link_super_tout = timeout; + + /* Only send if current role is Master; 2.0 spec requires this */ + if (p->link_role == BTM_ROLE_MASTER) { + if (!btsnd_hcic_write_link_super_tout (LOCAL_BR_EDR_CONTROLLER_ID, + p->hci_handle, timeout)) { + return (BTM_NO_RESOURCES); + } + + return (BTM_CMD_STARTED); + } else { + return (BTM_SUCCESS); + } + } + + /* If here, no BD Addr found */ + return (BTM_UNKNOWN_ADDR); +} + +/******************************************************************************* +** +** Function BTM_IsAclConnectionUp +** +** Description This function is called to check if an ACL connection exists +** to a specific remote BD Address. +** +** Returns TRUE if connection is up, else FALSE. +** +*******************************************************************************/ +BOOLEAN BTM_IsAclConnectionUp (BD_ADDR remote_bda, tBT_TRANSPORT transport) +{ + tACL_CONN *p; + + BTM_TRACE_API ("BTM_IsAclConnectionUp: RemBdAddr: %02x%02x%02x%02x%02x%02x\n", + remote_bda[0], remote_bda[1], remote_bda[2], + remote_bda[3], remote_bda[4], remote_bda[5]); + + p = btm_bda_to_acl(remote_bda, transport); + if (p != (tACL_CONN *)NULL) { + return (TRUE); + } + + /* If here, no BD Addr found */ + return (FALSE); +} + +/******************************************************************************* +** +** Function BTM_GetNumAclLinks +** +** Description This function is called to count the number of +** ACL links that are active. +** +** Returns UINT16 Number of active ACL links +** +*******************************************************************************/ +UINT16 BTM_GetNumAclLinks (void) +{ + uint16_t num_acl = 0; + + num_acl = list_length(btm_cb.p_acl_db_list); + return num_acl; +} + +/******************************************************************************* +** +** Function btm_get_acl_disc_reason_code +** +** Description This function is called to get the disconnection reason code +** returned by the HCI at disconnection complete event. +** +** Returns TRUE if connection is up, else FALSE. +** +*******************************************************************************/ +UINT16 btm_get_acl_disc_reason_code (void) +{ + UINT8 res = btm_cb.acl_disc_reason; + BTM_TRACE_DEBUG ("btm_get_acl_disc_reason_code\n"); + return (res); +} + + +/******************************************************************************* +** +** Function BTM_GetHCIConnHandle +** +** Description This function is called to get the handle for an ACL connection +** to a specific remote BD Address. +** +** Returns the handle of the connection, or 0xFFFF if none. +** +*******************************************************************************/ +UINT16 BTM_GetHCIConnHandle (BD_ADDR remote_bda, tBT_TRANSPORT transport) +{ + tACL_CONN *p; + BTM_TRACE_DEBUG ("BTM_GetHCIConnHandle\n"); + p = btm_bda_to_acl(remote_bda, transport); + if (p != (tACL_CONN *)NULL) { + return (p->hci_handle); + } + + /* If here, no BD Addr found */ + return (0xFFFF); +} + +/******************************************************************************* +** +** Function btm_process_clk_off_comp_evt +** +** Description This function is called when clock offset command completes. +** +** Input Parms hci_handle - connection handle associated with the change +** clock offset +** +** Returns void +** +*******************************************************************************/ +void btm_process_clk_off_comp_evt (UINT16 hci_handle, UINT16 clock_offset) +{ + tACL_CONN *p_acl_cb = NULL; + BTM_TRACE_DEBUG ("btm_process_clk_off_comp_evt\n"); + /* Look up the connection by handle and set the current mode */ + p_acl_cb = btm_handle_to_acl(hci_handle); + if (p_acl_cb) { + p_acl_cb->clock_offset = clock_offset; + } + +} + +/******************************************************************************* +** +** Function btm_acl_role_changed +** +** Description This function is called whan a link's master/slave role change +** event or command status event (with error) is received. +** It updates the link control block, and calls +** the registered callback with status and role (if registered). +** +** Returns void +** +*******************************************************************************/ +void btm_acl_role_changed (UINT8 hci_status, BD_ADDR bd_addr, UINT8 new_role) +{ + UINT8 *p_bda = (bd_addr) ? bd_addr : + btm_cb.devcb.switch_role_ref_data.remote_bd_addr; + tACL_CONN *p = btm_bda_to_acl(p_bda, BT_TRANSPORT_BR_EDR); + tBTM_ROLE_SWITCH_CMPL *p_data = &btm_cb.devcb.switch_role_ref_data; + tBTM_SEC_DEV_REC *p_dev_rec; + tBTM_BL_ROLE_CHG_DATA evt; + + BTM_TRACE_DEBUG ("btm_acl_role_changed\n"); + /* Ignore any stray events */ + if (p == NULL) { + /* it could be a failure */ + if (hci_status != HCI_SUCCESS) { + btm_acl_report_role_change(hci_status, bd_addr); + } + return; + } + + p_data->hci_status = hci_status; + + if (hci_status == HCI_SUCCESS) { + p_data->role = new_role; + memcpy(p_data->remote_bd_addr, p_bda, BD_ADDR_LEN); + + /* Update cached value */ + p->link_role = new_role; + + /* Reload LSTO: link supervision timeout is reset in the LM after a role switch */ + if (new_role == BTM_ROLE_MASTER) { + BTM_SetLinkSuperTout (p->remote_addr, p->link_super_tout); + } + } else { + /* so the BTM_BL_ROLE_CHG_EVT uses the old role */ + new_role = p->link_role; + } + + /* Check if any SCO req is pending for role change */ + btm_sco_chk_pend_rolechange (p->hci_handle); + + /* if switching state is switching we need to turn encryption on */ + /* if idle, we did not change encryption */ + if (p->switch_role_state == BTM_ACL_SWKEY_STATE_SWITCHING) { + if (btsnd_hcic_set_conn_encrypt (p->hci_handle, TRUE)) { + p->encrypt_state = BTM_ACL_ENCRYPT_STATE_ENCRYPT_ON; + p->switch_role_state = BTM_ACL_SWKEY_STATE_ENCRYPTION_ON; + return; + } + } + + /* Set the switch_role_state to IDLE since the reply received from HCI */ + /* regardless of its result either success or failed. */ + if (p->switch_role_state == BTM_ACL_SWKEY_STATE_IN_PROGRESS) { + p->switch_role_state = BTM_ACL_SWKEY_STATE_IDLE; + p->encrypt_state = BTM_ACL_ENCRYPT_STATE_IDLE; + } + + /* if role switch complete is needed, report it now */ + btm_acl_report_role_change(hci_status, bd_addr); + + /* if role change event is registered, report it now */ + if (btm_cb.p_bl_changed_cb && (btm_cb.bl_evt_mask & BTM_BL_ROLE_CHG_MASK)) { + evt.event = BTM_BL_ROLE_CHG_EVT; + evt.new_role = new_role; + evt.p_bda = p_bda; + evt.hci_status = hci_status; + (*btm_cb.p_bl_changed_cb)((tBTM_BL_EVENT_DATA *)&evt); + } + + BTM_TRACE_DEBUG("Role Switch Event: new_role 0x%02x, HCI Status 0x%02x, rs_st:%d\n", + p_data->role, p_data->hci_status, p->switch_role_state); + +#if BTM_DISC_DURING_RS == TRUE + /* If a disconnect is pending, issue it now that role switch has completed */ + if ((p_dev_rec = btm_find_dev (p_bda)) != NULL) { + if (p_dev_rec->rs_disc_pending == BTM_SEC_DISC_PENDING) { + BTM_TRACE_WARNING("btm_acl_role_changed -> Issuing delayed HCI_Disconnect!!!\n"); + btsnd_hcic_disconnect(p_dev_rec->hci_handle, HCI_ERR_PEER_USER); + } + BTM_TRACE_ERROR("tBTM_SEC_DEV:0x%x rs_disc_pending=%d\n", + (UINT32)p_dev_rec, p_dev_rec->rs_disc_pending); + p_dev_rec->rs_disc_pending = BTM_SEC_RS_NOT_PENDING; /* reset flag */ + } + +#endif + +} + +/******************************************************************************* +** +** Function BTM_AllocateSCN +** +** Description Look through the Server Channel Numbers for a free one. +** +** Returns Allocated SCN number or 0 if none. +** +*******************************************************************************/ +#if (CLASSIC_BT_INCLUDED == TRUE) +UINT8 BTM_AllocateSCN(void) +{ + UINT8 x; + BTM_TRACE_DEBUG ("BTM_AllocateSCN\n"); + for (x = 1; x < BTM_MAX_SCN; x++) { + if (!btm_cb.btm_scn[x - 1]) { + btm_cb.btm_scn[x - 1] = TRUE; + return x; + } + } + return (0); /* No free ports */ +} +#endif ///CLASSIC_BT_INCLUDED == TRUE + +/******************************************************************************* +** +** Function BTM_TryAllocateSCN +** +** Description Try to allocate a fixed server channel +** +** Returns Returns TRUE if server channel was available +** +*******************************************************************************/ +#if (CLASSIC_BT_INCLUDED == TRUE) +BOOLEAN BTM_TryAllocateSCN(UINT8 scn) +{ + if (scn >= BTM_MAX_SCN) { + return FALSE; + } + + /* check if this port is available */ + if (!btm_cb.btm_scn[scn - 1]) { + btm_cb.btm_scn[scn - 1] = TRUE; + return TRUE; + } + + return (FALSE); /* Port was busy */ +} + + +/******************************************************************************* +** +** Function BTM_FreeSCN +** +** Description Free the specified SCN. +** +** Returns TRUE or FALSE +** +*******************************************************************************/ +BOOLEAN BTM_FreeSCN(UINT8 scn) +{ + BTM_TRACE_DEBUG ("BTM_FreeSCN \n"); + if (scn <= BTM_MAX_SCN) { + btm_cb.btm_scn[scn - 1] = FALSE; + return (TRUE); + } else { + return (FALSE); /* Illegal SCN passed in */ + } + return (FALSE); +} +#endif ///CLASSIC_BT_INCLUDED == TRUE + +/******************************************************************************* +** +** Function btm_set_packet_types +** +** Description This function sets the packet types used for a specific +** ACL connection. It is called internally by btm_acl_created +** or by an application/profile by BTM_SetPacketTypes. +** +** Returns status of the operation +** +*******************************************************************************/ +tBTM_STATUS btm_set_packet_types (tACL_CONN *p, UINT16 pkt_types) +{ + UINT16 temp_pkt_types; + BTM_TRACE_DEBUG ("btm_set_packet_types\n"); + /* Save in the ACL control blocks, types that we support */ + temp_pkt_types = (pkt_types & BTM_ACL_SUPPORTED_PKTS_MASK & + btm_cb.btm_acl_pkt_types_supported); + + /* OR in any exception packet types if at least 2.0 version of spec */ + temp_pkt_types |= ((pkt_types & BTM_ACL_EXCEPTION_PKTS_MASK) | + (btm_cb.btm_acl_pkt_types_supported & BTM_ACL_EXCEPTION_PKTS_MASK)); + + /* Exclude packet types not supported by the peer */ + btm_acl_chk_peer_pkt_type_support (p, &temp_pkt_types); + + BTM_TRACE_DEBUG ("SetPacketType Mask -> 0x%04x\n", temp_pkt_types); + + if (!btsnd_hcic_change_conn_type (p->hci_handle, temp_pkt_types)) { + return (BTM_NO_RESOURCES); + } + + p->pkt_types_mask = temp_pkt_types; + + return (BTM_CMD_STARTED); +} + +/******************************************************************************* +** +** Function btm_get_max_packet_size +** +** Returns Returns maximum packet size that can be used for current +** connection, 0 if connection is not established +** +*******************************************************************************/ +UINT16 btm_get_max_packet_size (BD_ADDR addr) +{ + tACL_CONN *p = btm_bda_to_acl(addr, BT_TRANSPORT_BR_EDR); + UINT16 pkt_types = 0; + UINT16 pkt_size = 0; + BTM_TRACE_DEBUG ("btm_get_max_packet_size\n"); + if (p != NULL) { + pkt_types = p->pkt_types_mask; + } else { + /* Special case for when info for the local device is requested */ + if (memcmp (controller_get_interface()->get_address(), addr, BD_ADDR_LEN) == 0) { + pkt_types = btm_cb.btm_acl_pkt_types_supported; + } + } + + if (pkt_types) { + if (!(pkt_types & BTM_ACL_PKT_TYPES_MASK_NO_3_DH5)) { + pkt_size = HCI_EDR3_DH5_PACKET_SIZE; + } else if (!(pkt_types & BTM_ACL_PKT_TYPES_MASK_NO_2_DH5)) { + pkt_size = HCI_EDR2_DH5_PACKET_SIZE; + } else if (!(pkt_types & BTM_ACL_PKT_TYPES_MASK_NO_3_DH3)) { + pkt_size = HCI_EDR3_DH3_PACKET_SIZE; + } else if (pkt_types & BTM_ACL_PKT_TYPES_MASK_DH5) { + pkt_size = HCI_DH5_PACKET_SIZE; + } else if (!(pkt_types & BTM_ACL_PKT_TYPES_MASK_NO_2_DH3)) { + pkt_size = HCI_EDR2_DH3_PACKET_SIZE; + } else if (pkt_types & BTM_ACL_PKT_TYPES_MASK_DM5) { + pkt_size = HCI_DM5_PACKET_SIZE; + } else if (pkt_types & BTM_ACL_PKT_TYPES_MASK_DH3) { + pkt_size = HCI_DH3_PACKET_SIZE; + } else if (pkt_types & BTM_ACL_PKT_TYPES_MASK_DM3) { + pkt_size = HCI_DM3_PACKET_SIZE; + } else if (!(pkt_types & BTM_ACL_PKT_TYPES_MASK_NO_3_DH1)) { + pkt_size = HCI_EDR3_DH1_PACKET_SIZE; + } else if (!(pkt_types & BTM_ACL_PKT_TYPES_MASK_NO_2_DH1)) { + pkt_size = HCI_EDR2_DH1_PACKET_SIZE; + } else if (pkt_types & BTM_ACL_PKT_TYPES_MASK_DH1) { + pkt_size = HCI_DH1_PACKET_SIZE; + } else if (pkt_types & BTM_ACL_PKT_TYPES_MASK_DM1) { + pkt_size = HCI_DM1_PACKET_SIZE; + } + } + + return (pkt_size); +} + +/******************************************************************************* +** +** Function BTM_ReadRemoteVersion +** +** Returns If connected report peer device info +** +*******************************************************************************/ +tBTM_STATUS BTM_ReadRemoteVersion (BD_ADDR addr, UINT8 *lmp_version, + UINT16 *manufacturer, UINT16 *lmp_sub_version) +{ + tACL_CONN *p = btm_bda_to_acl(addr, BT_TRANSPORT_BR_EDR); + BTM_TRACE_DEBUG ("BTM_ReadRemoteVersion\n"); + if (p == NULL) { + return (BTM_UNKNOWN_ADDR); + } + + if (lmp_version) { + *lmp_version = p->lmp_version; + } + + if (manufacturer) { + *manufacturer = p->manufacturer; + } + + if (lmp_sub_version) { + *lmp_sub_version = p->lmp_subversion; + } + + return (BTM_SUCCESS); +} + +/******************************************************************************* +** +** Function BTM_ReadRemoteFeatures +** +** Returns pointer to the remote supported features mask (8 bytes) +** +*******************************************************************************/ +UINT8 *BTM_ReadRemoteFeatures (BD_ADDR addr) +{ + tACL_CONN *p = btm_bda_to_acl(addr, BT_TRANSPORT_BR_EDR); + BTM_TRACE_DEBUG ("BTM_ReadRemoteFeatures\n"); + if (p == NULL) { + return (NULL); + } + + return (p->peer_lmp_features[HCI_EXT_FEATURES_PAGE_0]); +} + +/******************************************************************************* +** +** Function BTM_ReadRemoteExtendedFeatures +** +** Returns pointer to the remote extended features mask (8 bytes) +** or NULL if bad page +** +*******************************************************************************/ +UINT8 *BTM_ReadRemoteExtendedFeatures (BD_ADDR addr, UINT8 page_number) +{ + tACL_CONN *p = btm_bda_to_acl(addr, BT_TRANSPORT_BR_EDR); + BTM_TRACE_DEBUG ("BTM_ReadRemoteExtendedFeatures\n"); + if (p == NULL) { + return (NULL); + } + + if (page_number > HCI_EXT_FEATURES_PAGE_MAX) { + BTM_TRACE_ERROR("Warning: BTM_ReadRemoteExtendedFeatures page %d unknown\n", page_number); + return NULL; + } + + return (p->peer_lmp_features[page_number]); +} + +/******************************************************************************* +** +** Function BTM_ReadNumberRemoteFeaturesPages +** +** Returns number of features pages read from the remote device. +** +*******************************************************************************/ +UINT8 BTM_ReadNumberRemoteFeaturesPages (BD_ADDR addr) +{ + tACL_CONN *p = btm_bda_to_acl(addr, BT_TRANSPORT_BR_EDR); + BTM_TRACE_DEBUG ("BTM_ReadNumberRemoteFeaturesPages\n"); + if (p == NULL) { + return (0); + } + + return (p->num_read_pages); +} + +/******************************************************************************* +** +** Function BTM_ReadAllRemoteFeatures +** +** Returns pointer to all features of the remote (24 bytes). +** +*******************************************************************************/ +UINT8 *BTM_ReadAllRemoteFeatures (BD_ADDR addr) +{ + tACL_CONN *p = btm_bda_to_acl(addr, BT_TRANSPORT_BR_EDR); + BTM_TRACE_DEBUG ("BTM_ReadAllRemoteFeatures\n"); + if (p == NULL) { + return (NULL); + } + + return (p->peer_lmp_features[HCI_EXT_FEATURES_PAGE_0]); +} + +/******************************************************************************* +** +** Function BTM_RegBusyLevelNotif +** +** Description This function is called to register a callback to receive +** busy level change events. +** +** Returns BTM_SUCCESS if successfully registered, otherwise error +** +*******************************************************************************/ +tBTM_STATUS BTM_RegBusyLevelNotif (tBTM_BL_CHANGE_CB *p_cb, UINT8 *p_level, + tBTM_BL_EVENT_MASK evt_mask) +{ + BTM_TRACE_DEBUG ("BTM_RegBusyLevelNotif\n"); + if (p_level) { + *p_level = btm_cb.busy_level; + } + + btm_cb.bl_evt_mask = evt_mask; + + if (!p_cb) { + btm_cb.p_bl_changed_cb = NULL; + } else if (btm_cb.p_bl_changed_cb) { + return (BTM_BUSY); + } else { + btm_cb.p_bl_changed_cb = p_cb; + } + + return (BTM_SUCCESS); +} + + +tBTM_STATUS BTM_RegAclLinkStatNotif(tBTM_ACL_LINK_STAT_CB *p_cb) +{ + BTM_TRACE_DEBUG ("BTM_RegAclLinkStatNotif\n"); + + if (!p_cb) { + btm_cb.p_acl_link_stat_cb = NULL; + } else if (btm_cb.p_acl_link_stat_cb) { + return BTM_BUSY; + } else { + btm_cb.p_acl_link_stat_cb = p_cb; + } + + return BTM_SUCCESS; +} + +/******************************************************************************* +** +** Function BTM_SetQoS +** +** Description This function is called to setup QoS +** +** Returns status of the operation +** +*******************************************************************************/ +tBTM_STATUS BTM_SetQoS (BD_ADDR bd, FLOW_SPEC *p_flow, tBTM_CMPL_CB *p_cb) +{ + tACL_CONN *p = NULL; + + BTM_TRACE_API ("BTM_SetQoS: BdAddr: %02x%02x%02x%02x%02x%02x\n", + bd[0], bd[1], bd[2], + bd[3], bd[4], bd[5]); + + /* If someone already waiting on the version, do not allow another */ + if (btm_cb.devcb.p_qossu_cmpl_cb) { + return (BTM_BUSY); + } + + if ( (p = btm_bda_to_acl(bd, BT_TRANSPORT_BR_EDR)) != NULL) { + btu_start_timer (&btm_cb.devcb.qossu_timer, BTU_TTYPE_BTM_QOS, BTM_DEV_REPLY_TIMEOUT); + btm_cb.devcb.p_qossu_cmpl_cb = p_cb; + + if (!btsnd_hcic_qos_setup (p->hci_handle, p_flow->qos_flags, p_flow->service_type, + p_flow->token_rate, p_flow->peak_bandwidth, + p_flow->latency, p_flow->delay_variation)) { + btm_cb.devcb.p_qossu_cmpl_cb = NULL; + btu_stop_timer(&btm_cb.devcb.qossu_timer); + return (BTM_NO_RESOURCES); + } else { + return (BTM_CMD_STARTED); + } + } + + /* If here, no BD Addr found */ + return (BTM_UNKNOWN_ADDR); +} + +/******************************************************************************* +** +** Function btm_qos_setup_complete +** +** Description This function is called when the command complete message +** is received from the HCI for the qos setup request. +** +** Returns void +** +*******************************************************************************/ +void btm_qos_setup_complete (UINT8 status, UINT16 handle, FLOW_SPEC *p_flow) +{ + tBTM_CMPL_CB *p_cb = btm_cb.devcb.p_qossu_cmpl_cb; + tBTM_QOS_SETUP_CMPL qossu; + BTM_TRACE_DEBUG ("btm_qos_setup_complete\n"); + btu_stop_timer (&btm_cb.devcb.qossu_timer); + + btm_cb.devcb.p_qossu_cmpl_cb = NULL; + + if (p_cb) { + memset(&qossu, 0, sizeof(tBTM_QOS_SETUP_CMPL)); + qossu.status = status; + qossu.handle = handle; + tACL_CONN *p = btm_handle_to_acl(handle); + if (p != NULL) { + memcpy (qossu.rem_bda, p->remote_addr, BD_ADDR_LEN); + } + if (p_flow != NULL) { + qossu.flow.qos_flags = p_flow->qos_flags; + qossu.flow.service_type = p_flow->service_type; + qossu.flow.token_rate = p_flow->token_rate; + qossu.flow.peak_bandwidth = p_flow->peak_bandwidth; + qossu.flow.latency = p_flow->latency; + qossu.flow.delay_variation = p_flow->delay_variation; + } + BTM_TRACE_DEBUG ("BTM: p_flow->delay_variation: 0x%02x\n", + qossu.flow.delay_variation); + (*p_cb)(&qossu); + } +} + +/******************************************************************************* +** +** Function btm_qos_setup_timeout +** +** Description This function processes a timeout. +** Currently, we just report an error log +** +** Returns void +** +*******************************************************************************/ +void btm_qos_setup_timeout (void *p_tle) +{ + BTM_TRACE_DEBUG ("%s\n", __func__); + + btm_qos_setup_complete (HCI_ERR_HOST_TIMEOUT, 0, NULL); +} + +/******************************************************************************* +** +** Function BTM_ReadRSSI +** +** Description This function is called to read the link policy settings. +** The address of link policy results are returned in the callback. +** (tBTM_RSSI_RESULTS) +** +** Returns BTM_CMD_STARTED if successfully initiated or error code +** +*******************************************************************************/ +tBTM_STATUS BTM_ReadRSSI (BD_ADDR remote_bda, tBT_TRANSPORT transport, tBTM_CMPL_CB *p_cb) +{ + tACL_CONN *p; + + BTM_TRACE_API ("BTM_ReadRSSI: RemBdAddr: %02x%02x%02x%02x%02x%02x\n", + remote_bda[0], remote_bda[1], remote_bda[2], + remote_bda[3], remote_bda[4], remote_bda[5]); + tBTM_RSSI_RESULTS result; + /* If someone already waiting on the version, do not allow another */ + if (btm_cb.devcb.p_rssi_cmpl_cb) { + result.status = BTM_BUSY; + (*p_cb)(&result); + return (BTM_BUSY); + } + + p = btm_bda_to_acl(remote_bda, transport); + if (p != (tACL_CONN *)NULL) { + btu_start_timer (&btm_cb.devcb.rssi_timer, BTU_TTYPE_BTM_ACL, + BTM_DEV_REPLY_TIMEOUT); + + btm_cb.devcb.p_rssi_cmpl_cb = p_cb; + + if (!btsnd_hcic_read_rssi (p->hci_handle)) { + btm_cb.devcb.p_rssi_cmpl_cb = NULL; + btu_stop_timer (&btm_cb.devcb.rssi_timer); + result.status = BTM_NO_RESOURCES; + (*p_cb)(&result); + return (BTM_NO_RESOURCES); + } else { + return (BTM_CMD_STARTED); + } + } + + /* If here, no BD Addr found */ + return (BTM_UNKNOWN_ADDR); +} + +/******************************************************************************* +** +** Function BTM_ReadLinkQuality +** +** Description This function is called to read the link qulaity. +** The value of the link quality is returned in the callback. +** (tBTM_LINK_QUALITY_RESULTS) +** +** Returns BTM_CMD_STARTED if successfully initiated or error code +** +*******************************************************************************/ +tBTM_STATUS BTM_ReadLinkQuality (BD_ADDR remote_bda, tBTM_CMPL_CB *p_cb) +{ + tACL_CONN *p; + + BTM_TRACE_API ("BTM_ReadLinkQuality: RemBdAddr: %02x%02x%02x%02x%02x%02x\n", + remote_bda[0], remote_bda[1], remote_bda[2], + remote_bda[3], remote_bda[4], remote_bda[5]); + + /* If someone already waiting on the version, do not allow another */ + if (btm_cb.devcb.p_lnk_qual_cmpl_cb) { + return (BTM_BUSY); + } + + p = btm_bda_to_acl(remote_bda, BT_TRANSPORT_BR_EDR); + if (p != (tACL_CONN *)NULL) { + btu_start_timer (&btm_cb.devcb.lnk_quality_timer, BTU_TTYPE_BTM_ACL, + BTM_DEV_REPLY_TIMEOUT); + btm_cb.devcb.p_lnk_qual_cmpl_cb = p_cb; + + if (!btsnd_hcic_get_link_quality (p->hci_handle)) { + btu_stop_timer (&btm_cb.devcb.lnk_quality_timer); + btm_cb.devcb.p_lnk_qual_cmpl_cb = NULL; + return (BTM_NO_RESOURCES); + } else { + return (BTM_CMD_STARTED); + } + } + + /* If here, no BD Addr found */ + return (BTM_UNKNOWN_ADDR); +} + +/******************************************************************************* +** +** Function BTM_ReadTxPower +** +** Description This function is called to read the current +** TX power of the connection. The tx power level results +** are returned in the callback. +** (tBTM_RSSI_RESULTS) +** +** Returns BTM_CMD_STARTED if successfully initiated or error code +** +*******************************************************************************/ +tBTM_STATUS BTM_ReadTxPower (BD_ADDR remote_bda, tBT_TRANSPORT transport, tBTM_CMPL_CB *p_cb) +{ + tACL_CONN *p; + BOOLEAN ret; +#define BTM_READ_RSSI_TYPE_CUR 0x00 +#define BTM_READ_RSSI_TYPE_MAX 0X01 + + BTM_TRACE_API ("BTM_ReadTxPower: RemBdAddr: %02x%02x%02x%02x%02x%02x\n", + remote_bda[0], remote_bda[1], remote_bda[2], + remote_bda[3], remote_bda[4], remote_bda[5]); + + /* If someone already waiting on the version, do not allow another */ + if (btm_cb.devcb.p_tx_power_cmpl_cb) { + return (BTM_BUSY); + } + + p = btm_bda_to_acl(remote_bda, transport); + if (p != (tACL_CONN *)NULL) { + btu_start_timer (&btm_cb.devcb.tx_power_timer, BTU_TTYPE_BTM_ACL, + BTM_DEV_REPLY_TIMEOUT); + + btm_cb.devcb.p_tx_power_cmpl_cb = p_cb; + +#if BLE_INCLUDED == TRUE + if (p->transport == BT_TRANSPORT_LE) { + memcpy(btm_cb.devcb.read_tx_pwr_addr, remote_bda, BD_ADDR_LEN); + ret = btsnd_hcic_ble_read_adv_chnl_tx_power(); + } else +#endif + { + ret = btsnd_hcic_read_tx_power (p->hci_handle, BTM_READ_RSSI_TYPE_CUR); + } + if (!ret) { + btm_cb.devcb.p_tx_power_cmpl_cb = NULL; + btu_stop_timer (&btm_cb.devcb.tx_power_timer); + return (BTM_NO_RESOURCES); + } else { + return (BTM_CMD_STARTED); + } + } + + /* If here, no BD Addr found */ + return (BTM_UNKNOWN_ADDR); +} + +tBTM_STATUS BTM_SetAclPktTypes(BD_ADDR remote_bda, UINT16 pkt_types, tBTM_CMPL_CB *p_cb) +{ +#if CLASSIC_BT_INCLUDED == TRUE + tBTM_STATUS ret = BTM_UNKNOWN_ADDR; + tACL_CONN *p; + tBTM_SET_ACL_PKT_TYPES_RESULTS result; + + /* If someone already waiting on the version, do not allow another */ + if (btm_cb.devcb.p_set_acl_pkt_types_cmpl_cb) { + result.status = HCI_ERR_REPEATED_ATTEMPTS; + (*p_cb)(&result); + return (BTM_BUSY);; + } + + p = btm_bda_to_acl(remote_bda, BT_TRANSPORT_BR_EDR); + + if (p != (tACL_CONN *)NULL) { + btu_start_timer (&btm_cb.devcb.set_acl_pkt_types_timer, BTU_TTYPE_BTM_ACL, + BTM_DEV_REPLY_TIMEOUT); + + btm_cb.devcb.p_set_acl_pkt_types_cmpl_cb = p_cb; + if (btm_set_packet_types(p, pkt_types) != BTM_CMD_STARTED) { + btm_cb.devcb.p_set_acl_pkt_types_cmpl_cb = NULL; + btu_stop_timer (&btm_cb.devcb.set_acl_pkt_types_timer); + result.status = HCI_ERR_MEMORY_FULL; + (*p_cb)(&result); + ret = BTM_NO_RESOURCES; + } else { + ret = BTM_CMD_STARTED; + } + } + /* If here, no BD Addr found */ + return ret; +#else + return BTM_NO_RESOURCES; +#endif +} + +void btm_acl_pkt_types_changed(UINT8 status, UINT16 handle, UINT16 pkt_types) +{ +#if CLASSIC_BT_INCLUDED == TRUE + BTM_TRACE_DEBUG ("btm_acl_pkt_types_changed\n"); + tACL_CONN *conn = NULL; + tBTM_SET_ACL_PKT_TYPES_RESULTS results; + btu_stop_timer (&btm_cb.devcb.set_acl_pkt_types_timer); + + /* If there is a callback registered for packet types changed, call it */ + if (btm_cb.devcb.p_set_acl_pkt_types_cmpl_cb) { + if (status == HCI_SUCCESS) { + results.status = BTM_SUCCESS; + } else { + results.status = BTM_BAD_VALUE_RET; + } + results.pkt_types = pkt_types; + /* Search through the list of active channels for the correct BD Addr */ + if ((conn = btm_handle_to_acl(handle)) != NULL) { + memcpy(results.rem_bda, conn->remote_addr, BD_ADDR_LEN); + (*btm_cb.devcb.p_set_acl_pkt_types_cmpl_cb)(&results); + } + btm_cb.devcb.p_set_acl_pkt_types_cmpl_cb = NULL; + } +#endif +} + +#if (BLE_INCLUDED == TRUE) +tBTM_STATUS BTM_BleReadAdvTxPower(tBTM_CMPL_CB *p_cb) +{ + BOOLEAN ret; + tBTM_TX_POWER_RESULTS result; + /* If someone already waiting on the version, do not allow another */ + if (btm_cb.devcb.p_tx_power_cmpl_cb) { + result.status = BTM_BUSY; + (*p_cb)(&result); + return (BTM_BUSY); + } + + btm_cb.devcb.p_tx_power_cmpl_cb = p_cb; + btu_start_timer (&btm_cb.devcb.tx_power_timer, BTU_TTYPE_BTM_ACL, + BTM_DEV_REPLY_TIMEOUT); + ret = btsnd_hcic_ble_read_adv_chnl_tx_power(); + + if(!ret) { + btm_cb.devcb.p_tx_power_cmpl_cb = NULL; + btu_stop_timer (&btm_cb.devcb.tx_power_timer); + result.status = BTM_NO_RESOURCES; + (*p_cb)(&result); + return (BTM_NO_RESOURCES); + } else { + return BTM_CMD_STARTED; + } +} + +void BTM_BleGetWhiteListSize(uint16_t *length) +{ + tBTM_BLE_CB *p_cb = &btm_cb.ble_ctr_cb; + if (p_cb->white_list_avail_size == 0) { + BTM_TRACE_WARNING("%s Whitelist full.", __func__); + } + *length = p_cb->white_list_avail_size; + return; +} +#endif ///BLE_INCLUDED == TRUE + +/******************************************************************************* +** +** Function btm_read_tx_power_complete +** +** Description This function is called when the command complete message +** is received from the HCI for the read tx power request. +** +** Returns void +** +*******************************************************************************/ +void btm_read_tx_power_complete (UINT8 *p, BOOLEAN is_ble) +{ + tBTM_CMPL_CB *p_cb = btm_cb.devcb.p_tx_power_cmpl_cb; + tBTM_TX_POWER_RESULTS results; + UINT16 handle; + tACL_CONN *p_acl_cb = NULL; + BTM_TRACE_DEBUG ("btm_read_tx_power_complete\n"); + btu_stop_timer (&btm_cb.devcb.tx_power_timer); + + /* If there was a callback registered for read rssi, call it */ + btm_cb.devcb.p_tx_power_cmpl_cb = NULL; + + if (p_cb) { + STREAM_TO_UINT8 (results.hci_status, p); + + if (results.hci_status == HCI_SUCCESS) { + results.status = BTM_SUCCESS; + + if (!is_ble) { + STREAM_TO_UINT16 (handle, p); + STREAM_TO_UINT8 (results.tx_power, p); + + /* Search through the list of active channels for the correct BD Addr */ + p_acl_cb = btm_handle_to_acl(handle); + if (p_acl_cb) { + memcpy (results.rem_bda, p_acl_cb->remote_addr, BD_ADDR_LEN); + } + } +#if BLE_INCLUDED == TRUE + else { + STREAM_TO_UINT8 (results.tx_power, p); + memcpy(results.rem_bda, btm_cb.devcb.read_tx_pwr_addr, BD_ADDR_LEN); + } +#endif + BTM_TRACE_DEBUG ("BTM TX power Complete: tx_power %d, hci status 0x%02x\n", + results.tx_power, results.hci_status); + } else { + results.status = BTM_ERR_PROCESSING; + } + + (*p_cb)(&results); + } +} + +/******************************************************************************* +** +** Function btm_read_rssi_complete +** +** Description This function is called when the command complete message +** is received from the HCI for the read rssi request. +** +** Returns void +** +*******************************************************************************/ +void btm_read_rssi_complete (UINT8 *p) +{ + tBTM_CMPL_CB *p_cb = btm_cb.devcb.p_rssi_cmpl_cb; + tBTM_RSSI_RESULTS results; + UINT16 handle; + tACL_CONN *p_acl_cb = NULL; + BTM_TRACE_DEBUG ("btm_read_rssi_complete\n"); + btu_stop_timer (&btm_cb.devcb.rssi_timer); + + /* If there was a callback registered for read rssi, call it */ + btm_cb.devcb.p_rssi_cmpl_cb = NULL; + + if (p_cb) { + STREAM_TO_UINT8 (results.hci_status, p); + + if (results.hci_status == HCI_SUCCESS) { + results.status = BTM_SUCCESS; + + STREAM_TO_UINT16 (handle, p); + + STREAM_TO_UINT8 (results.rssi, p); + BTM_TRACE_DEBUG ("BTM RSSI Complete: rssi %d, hci status 0x%02x\n", + results.rssi, results.hci_status); + + /* Search through the list of active channels for the correct BD Addr */ + p_acl_cb = btm_handle_to_acl(handle); + if (p_acl_cb) { + memcpy (results.rem_bda, p_acl_cb->remote_addr, BD_ADDR_LEN); + } + } else { + results.status = BTM_ERR_PROCESSING; + } + + (*p_cb)(&results); + } +} + +/******************************************************************************* +** +** Function btm_read_link_quality_complete +** +** Description This function is called when the command complete message +** is received from the HCI for the read link quality. +** +** Returns void +** +*******************************************************************************/ +void btm_read_link_quality_complete (UINT8 *p) +{ + tBTM_CMPL_CB *p_cb = btm_cb.devcb.p_lnk_qual_cmpl_cb; + tBTM_LINK_QUALITY_RESULTS results; + UINT16 handle; + tACL_CONN *p_acl_cb = NULL; + BTM_TRACE_DEBUG ("btm_read_link_quality_complete\n"); + btu_stop_timer (&btm_cb.devcb.lnk_quality_timer); + + /* If there was a callback registered for read rssi, call it */ + btm_cb.devcb.p_lnk_qual_cmpl_cb = NULL; + + if (p_cb) { + STREAM_TO_UINT8 (results.hci_status, p); + + if (results.hci_status == HCI_SUCCESS) { + results.status = BTM_SUCCESS; + + STREAM_TO_UINT16 (handle, p); + + STREAM_TO_UINT8 (results.link_quality, p); + BTM_TRACE_DEBUG ("BTM Link Quality Complete: Link Quality %d, hci status 0x%02x\n", + results.link_quality, results.hci_status); + + /* Search through the list of active channels for the correct BD Addr */ + p_acl_cb = btm_handle_to_acl(handle); + if (p_acl_cb) { + memcpy (results.rem_bda, p_acl_cb->remote_addr, BD_ADDR_LEN); + } + } else { + results.status = BTM_ERR_PROCESSING; + } + + (*p_cb)(&results); + } +} + +/******************************************************************************* +** +** Function btm_remove_acl +** +** Description This function is called to disconnect an ACL connection +** +** Returns BTM_SUCCESS if successfully initiated, otherwise BTM_NO_RESOURCES. +** +*******************************************************************************/ +tBTM_STATUS btm_remove_acl (BD_ADDR bd_addr, tBT_TRANSPORT transport) +{ + UINT16 hci_handle = BTM_GetHCIConnHandle(bd_addr, transport); + tBTM_STATUS status = BTM_SUCCESS; + + BTM_TRACE_DEBUG ("btm_remove_acl\n"); +#if BTM_DISC_DURING_RS == TRUE + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev (bd_addr); + + /* Role Switch is pending, postpone until completed */ + if (p_dev_rec && (p_dev_rec->rs_disc_pending == BTM_SEC_RS_PENDING)) { + p_dev_rec->rs_disc_pending = BTM_SEC_DISC_PENDING; + } else /* otherwise can disconnect right away */ +#endif + { + if (hci_handle != 0xFFFF && p_dev_rec && + p_dev_rec->sec_state != BTM_SEC_STATE_DISCONNECTING) { + if (!btsnd_hcic_disconnect (hci_handle, HCI_ERR_PEER_USER)) { + status = BTM_NO_RESOURCES; + } + } else { + status = BTM_UNKNOWN_ADDR; + } + } + + return status; +} + + +/******************************************************************************* +** +** Function BTM_SetTraceLevel +** +** Description This function sets the trace level for BTM. If called with +** a value of 0xFF, it simply returns the current trace level. +** +** Returns The new or current trace level +** +*******************************************************************************/ +UINT8 BTM_SetTraceLevel (UINT8 new_level) +{ + BTM_TRACE_DEBUG ("BTM_SetTraceLevel\n"); + if (new_level != 0xFF) { + btm_cb.trace_level = new_level; + } + + return (btm_cb.trace_level); +} + +/******************************************************************************* +** +** Function btm_cont_rswitch +** +** Description This function is called to continue processing an active +** role switch. It first disables encryption if enabled and +** EPR is not supported +** +** Returns void +** +*******************************************************************************/ +void btm_cont_rswitch (tACL_CONN *p, tBTM_SEC_DEV_REC *p_dev_rec, + UINT8 hci_status) +{ + BOOLEAN sw_ok = TRUE; + BTM_TRACE_DEBUG ("btm_cont_rswitch\n"); + /* Check to see if encryption needs to be turned off if pending + change of link key or role switch */ + if (p->switch_role_state == BTM_ACL_SWKEY_STATE_MODE_CHANGE) { + /* Must turn off Encryption first if necessary */ + /* Some devices do not support switch or change of link key while encryption is on */ + if (p_dev_rec != NULL && ((p_dev_rec->sec_flags & BTM_SEC_ENCRYPTED) != 0) + && !BTM_EPR_AVAILABLE(p)) { + if (btsnd_hcic_set_conn_encrypt (p->hci_handle, FALSE)) { + p->encrypt_state = BTM_ACL_ENCRYPT_STATE_ENCRYPT_OFF; + if (p->switch_role_state == BTM_ACL_SWKEY_STATE_MODE_CHANGE) { + p->switch_role_state = BTM_ACL_SWKEY_STATE_ENCRYPTION_OFF; + } + } else { + /* Error occurred; set states back to Idle */ + if (p->switch_role_state == BTM_ACL_SWKEY_STATE_MODE_CHANGE) { + sw_ok = FALSE; + } + } + } else /* Encryption not used or EPR supported, continue with switch + and/or change of link key */ + { + if (p->switch_role_state == BTM_ACL_SWKEY_STATE_MODE_CHANGE) { + p->switch_role_state = BTM_ACL_SWKEY_STATE_IN_PROGRESS; +#if BTM_DISC_DURING_RS == TRUE + if (p_dev_rec) { + p_dev_rec->rs_disc_pending = BTM_SEC_RS_PENDING; + } +#endif + sw_ok = btsnd_hcic_switch_role (p->remote_addr, (UINT8)!p->link_role); + } + } + + if (!sw_ok) { + p->switch_role_state = BTM_ACL_SWKEY_STATE_IDLE; + btm_acl_report_role_change(hci_status, p->remote_addr); + } + } +} + +/******************************************************************************* +** +** Function btm_acl_resubmit_page +** +** Description send pending page request +** +*******************************************************************************/ +void btm_acl_resubmit_page (void) +{ +#if (SMP_INCLUDED == TRUE) + tBTM_SEC_DEV_REC *p_dev_rec; + BT_HDR *p_buf; + UINT8 *pp; + BD_ADDR bda; + BTM_TRACE_DEBUG ("btm_acl_resubmit_page\n"); + /* If there were other page request schedule can start the next one */ + if ((p_buf = (BT_HDR *)fixed_queue_dequeue(btm_cb.page_queue, 0)) != NULL) { + /* skip 3 (2 bytes opcode and 1 byte len) to get to the bd_addr + * for both create_conn and rmt_name */ + pp = (UINT8 *)(p_buf + 1) + p_buf->offset + 3; + + STREAM_TO_BDADDR (bda, pp); + + p_dev_rec = btm_find_or_alloc_dev (bda); + + memcpy (btm_cb.connecting_bda, p_dev_rec->bd_addr, BD_ADDR_LEN); + memcpy (btm_cb.connecting_dc, p_dev_rec->dev_class, DEV_CLASS_LEN); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p_buf); + } else { + btm_cb.paging = FALSE; + } +#endif ///SMP_INCLUDED == TRUE +} + +/******************************************************************************* +** +** Function btm_acl_reset_paging +** +** Description set paging to FALSE and free the page queue - called at hci_reset +** +*******************************************************************************/ +void btm_acl_reset_paging (void) +{ + BT_HDR *p; + BTM_TRACE_DEBUG ("btm_acl_reset_paging\n"); + /* If we sent reset we are definitely not paging any more */ + while ((p = (BT_HDR *)fixed_queue_dequeue(btm_cb.page_queue, 0)) != NULL) { + osi_free (p); + } + + btm_cb.paging = FALSE; +} + +/******************************************************************************* +** +** Function btm_acl_paging +** +** Description send a paging command or queue it in btm_cb +** +*******************************************************************************/ +#if (SMP_INCLUDED == TRUE && CLASSIC_BT_INCLUDED == TRUE) +void btm_acl_paging (BT_HDR *p, BD_ADDR bda) +{ + tBTM_SEC_DEV_REC *p_dev_rec; + + BTM_TRACE_DEBUG ("btm_acl_paging discing:%d, paging:%d BDA: %06x%06x\n", + btm_cb.discing, btm_cb.paging, + (bda[0] << 16) + (bda[1] << 8) + bda[2], (bda[3] << 16) + (bda[4] << 8) + bda[5]); + if (btm_cb.discing) { + btm_cb.paging = TRUE; + fixed_queue_enqueue(btm_cb.page_queue, p, FIXED_QUEUE_MAX_TIMEOUT); + } else { + if (!BTM_ACL_IS_CONNECTED (bda)) { + BTM_TRACE_DEBUG ("connecting_bda: %06x%06x\n", + (btm_cb.connecting_bda[0] << 16) + (btm_cb.connecting_bda[1] << 8) + + btm_cb.connecting_bda[2], + (btm_cb.connecting_bda[3] << 16) + (btm_cb.connecting_bda[4] << 8) + + btm_cb.connecting_bda[5]); + if (btm_cb.paging && + memcmp (bda, btm_cb.connecting_bda, BD_ADDR_LEN) != 0) { + fixed_queue_enqueue(btm_cb.page_queue, p, FIXED_QUEUE_MAX_TIMEOUT); + } else { + p_dev_rec = btm_find_or_alloc_dev (bda); + memcpy (btm_cb.connecting_bda, p_dev_rec->bd_addr, BD_ADDR_LEN); + memcpy (btm_cb.connecting_dc, p_dev_rec->dev_class, DEV_CLASS_LEN); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + } + + btm_cb.paging = TRUE; + } else { /* ACL is already up */ + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + } + } +} +#endif ///SMP_INCLUDED == TRUE + +/******************************************************************************* +** +** Function btm_acl_notif_conn_collision +** +** Description Send connection collision event to upper layer if registered +** +** Returns TRUE if sent out to upper layer, +** FALSE if no one needs the notification. +** +*******************************************************************************/ +BOOLEAN btm_acl_notif_conn_collision (BD_ADDR bda) +{ + tBTM_BL_EVENT_DATA evt_data; + + /* Report possible collision to the upper layer. */ + if (btm_cb.p_bl_changed_cb) { + BTM_TRACE_DEBUG ("btm_acl_notif_conn_collision: RemBdAddr: %02x%02x%02x%02x%02x%02x\n", + bda[0], bda[1], bda[2], bda[3], bda[4], bda[5]); + + evt_data.event = BTM_BL_COLLISION_EVT; + evt_data.conn.p_bda = bda; + +#if BLE_INCLUDED == TRUE + evt_data.conn.transport = BT_TRANSPORT_BR_EDR; + evt_data.conn.handle = BTM_INVALID_HCI_HANDLE; +#endif + (*btm_cb.p_bl_changed_cb)(&evt_data); + return TRUE; + } else { + return FALSE; + } +} + + +/******************************************************************************* +** +** Function btm_acl_chk_peer_pkt_type_support +** +** Description Check if peer supports requested packets +** +*******************************************************************************/ +void btm_acl_chk_peer_pkt_type_support (tACL_CONN *p, UINT16 *p_pkt_type) +{ + /* 3 and 5 slot packets? */ + if (!HCI_3_SLOT_PACKETS_SUPPORTED(p->peer_lmp_features[HCI_EXT_FEATURES_PAGE_0])) { + *p_pkt_type &= ~(BTM_ACL_PKT_TYPES_MASK_DH3 + BTM_ACL_PKT_TYPES_MASK_DM3); + } + + if (!HCI_5_SLOT_PACKETS_SUPPORTED(p->peer_lmp_features[HCI_EXT_FEATURES_PAGE_0])) { + *p_pkt_type &= ~(BTM_ACL_PKT_TYPES_MASK_DH5 + BTM_ACL_PKT_TYPES_MASK_DM5); + } + + /* 2 and 3 MPS support? */ + if (!HCI_EDR_ACL_2MPS_SUPPORTED(p->peer_lmp_features[HCI_EXT_FEATURES_PAGE_0])) { + /* Not supported. Add 'not_supported' mask for all 2MPS packet types */ + *p_pkt_type |= (BTM_ACL_PKT_TYPES_MASK_NO_2_DH1 + BTM_ACL_PKT_TYPES_MASK_NO_2_DH3 + + BTM_ACL_PKT_TYPES_MASK_NO_2_DH5); + } + + if (!HCI_EDR_ACL_3MPS_SUPPORTED(p->peer_lmp_features[HCI_EXT_FEATURES_PAGE_0])) { + /* Not supported. Add 'not_supported' mask for all 3MPS packet types */ + *p_pkt_type |= (BTM_ACL_PKT_TYPES_MASK_NO_3_DH1 + BTM_ACL_PKT_TYPES_MASK_NO_3_DH3 + + BTM_ACL_PKT_TYPES_MASK_NO_3_DH5); + } + + /* EDR 3 and 5 slot support? */ + if (HCI_EDR_ACL_2MPS_SUPPORTED(p->peer_lmp_features[HCI_EXT_FEATURES_PAGE_0]) + || HCI_EDR_ACL_3MPS_SUPPORTED(p->peer_lmp_features[HCI_EXT_FEATURES_PAGE_0])) { + if (!HCI_3_SLOT_EDR_ACL_SUPPORTED(p->peer_lmp_features[HCI_EXT_FEATURES_PAGE_0])) + /* Not supported. Add 'not_supported' mask for all 3-slot EDR packet types */ + { + *p_pkt_type |= (BTM_ACL_PKT_TYPES_MASK_NO_2_DH3 + BTM_ACL_PKT_TYPES_MASK_NO_3_DH3); + } + + if (!HCI_5_SLOT_EDR_ACL_SUPPORTED(p->peer_lmp_features[HCI_EXT_FEATURES_PAGE_0])) + /* Not supported. Add 'not_supported' mask for all 5-slot EDR packet types */ + { + *p_pkt_type |= (BTM_ACL_PKT_TYPES_MASK_NO_2_DH5 + BTM_ACL_PKT_TYPES_MASK_NO_3_DH5); + } + } +} + +/******************************************************************************* +** +** Function btm_acl_free +** +** Description Free acl specific lists from btm control block +** +*******************************************************************************/ +void btm_acl_free(void) +{ + list_free(btm_cb.p_acl_db_list); + list_free(btm_cb.p_pm_mode_db_list); +} + +/******************************************************************************* +** +** Function btm_acl_connected +** +** Description Handle ACL connection complete event +** +*******************************************************************************/ +void btm_acl_connected(BD_ADDR bda, UINT16 handle, UINT8 link_type, UINT8 enc_mode, UINT8 status) +{ +#if BTM_SCO_INCLUDED == TRUE + tBTM_ESCO_DATA esco_data; +#endif + + if (link_type == HCI_LINK_TYPE_ACL) { +#if SMP_INCLUDED == TRUE + btm_sec_connected (bda, handle, status, enc_mode); +#endif /* SMP_INCLUDED == TRUE */ + /* report acl connection result to upper layer */ + do { + tBTM_ACL_LINK_STAT_EVENT_DATA evt_data = { + .event = BTM_ACL_CONN_CMPL_EVT, + .link_act.conn_cmpl.status = status, + .link_act.conn_cmpl.handle = handle, + }; + bdcpy(evt_data.link_act.conn_cmpl.bd_addr, bda); + btm_acl_link_stat_report(&evt_data); + } while (0); + + l2c_link_hci_conn_comp(status, handle, bda); + } +#if BTM_SCO_INCLUDED == TRUE + else { + memset(&esco_data, 0, sizeof(tBTM_ESCO_DATA)); + esco_data.link_type = HCI_LINK_TYPE_SCO; + memcpy (esco_data.bd_addr, bda, BD_ADDR_LEN); + btm_sco_connected(status, bda, handle, &esco_data); + } +#endif /* BTM_SCO_INCLUDED == TRUE */ +} + +/******************************************************************************* +** +** Function btm_acl_disconnected +** +** Description Handle ACL disconnection complete event +** +*******************************************************************************/ +void btm_acl_disconnected(UINT16 handle, UINT8 reason) +{ + + /* Report BR/EDR ACL disconnection result to upper layer */ + tACL_CONN *conn = btm_handle_to_acl(handle); + if (conn) { +#if BLE_INCLUDED == TRUE + if (conn->transport == BT_TRANSPORT_BR_EDR) +#endif + { + tBTM_ACL_LINK_STAT_EVENT_DATA evt_data = { + .event = BTM_ACL_DISCONN_CMPL_EVT, + .link_act.disconn_cmpl.reason = reason, + .link_act.disconn_cmpl.handle = handle, + }; + bdcpy(evt_data.link_act.disconn_cmpl.bd_addr, conn->remote_addr); + btm_acl_link_stat_report(&evt_data); + } + } + +#if BTM_SCO_INCLUDED == TRUE + /* If L2CAP doesn't know about it, send it to SCO */ + if (!l2c_link_hci_disc_comp (handle, reason)) { + btm_sco_removed (handle, reason); + } +#else + l2c_link_hci_disc_comp(handle, reason); +#endif /* BTM_SCO_INCLUDED */ + +#if (SMP_INCLUDED == TRUE) + /* Notify security manager */ + btm_sec_disconnected(handle, reason); +#endif /* SMP_INCLUDED == TRUE */ +} diff --git a/lib/bt/host/bluedroid/stack/btm/btm_ble.c b/lib/bt/host/bluedroid/stack/btm/btm_ble.c new file mode 100644 index 00000000..afa46b8a --- /dev/null +++ b/lib/bt/host/bluedroid/stack/btm/btm_ble.c @@ -0,0 +1,2934 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains functions for BLE device control utilities, and LE + * security functions. + * + ******************************************************************************/ +#include "common/bt_target.h" + +#include + +#include "stack/bt_types.h" +#include "stack/hcimsgs.h" +#include "stack/btu.h" +#include "btm_int.h" +#include "stack/btm_ble_api.h" +#include "stack/smp_api.h" +#include "l2c_int.h" +#include "stack/gap_api.h" +//#include "bt_utils.h" +#include "device/controller.h" + +//#define LOG_TAG "bt_btm_ble" +//#include "osi/include/log.h" +#if BLE_INCLUDED == TRUE +extern void BTM_UpdateAddrInfor(uint8_t addr_type, BD_ADDR bda); +#if SMP_INCLUDED == TRUE +// The temp variable to pass parameter between functions when in the connected event callback. +static BOOLEAN temp_enhanced = FALSE; +extern BOOLEAN aes_cipher_msg_auth_code(BT_OCTET16 key, UINT8 *input, UINT16 length, + UINT16 tlen, UINT8 *p_signature); +extern void smp_link_encrypted(BD_ADDR bda, UINT8 encr_enable); +extern BOOLEAN smp_proc_ltk_request(BD_ADDR bda); +#endif +extern void gatt_notify_enc_cmpl(BD_ADDR bd_addr); +/*******************************************************************************/ +/* External Function to be called by other modules */ +/*******************************************************************************/ +/******************************************************** +** +** Function BTM_SecAddBleDevice +** +** Description Add/modify device. This function will be normally called +** during host startup to restore all required information +** for a LE device stored in the NVRAM. +** +** Parameters: bd_addr - BD address of the peer +** bd_name - Name of the peer device. NULL if unknown. +** dev_type - Remote device's device type. +** addr_type - LE device address type. +** auth_mode - auth mode +** +** Returns TRUE if added OK, else FALSE +** +*******************************************************************************/ +#if (SMP_INCLUDED == TRUE) +BOOLEAN BTM_SecAddBleDevice (BD_ADDR bd_addr, BD_NAME bd_name, tBT_DEVICE_TYPE dev_type, + tBLE_ADDR_TYPE addr_type, UINT32 auth_mode) +{ + tBTM_SEC_DEV_REC *p_dev_rec; + tBTM_INQ_INFO *p_info = NULL; + + BTM_TRACE_DEBUG ("BTM_SecAddBleDevice dev_type=0x%x", dev_type); + p_dev_rec = btm_find_dev (bd_addr); + + if (!p_dev_rec) { + BTM_TRACE_DEBUG("Add a new device"); + + /* There is no device record, allocate one. + * If we can not find an empty spot for this one, let it fail. */ + if (list_length(btm_cb.p_sec_dev_rec_list) < BTM_SEC_MAX_DEVICE_RECORDS) { + p_dev_rec = (tBTM_SEC_DEV_REC *)osi_malloc(sizeof(tBTM_SEC_DEV_REC)); + if(p_dev_rec) { + list_append(btm_cb.p_sec_dev_rec_list, p_dev_rec); + BTM_TRACE_DEBUG ("allocate a new dev rec idx=0x%x\n", list_length(btm_cb.p_sec_dev_rec_list)); + + /* Mark this record as in use and initialize */ + memset (p_dev_rec, 0, sizeof (tBTM_SEC_DEV_REC)); + p_dev_rec->sec_flags = BTM_SEC_IN_USE; + memcpy (p_dev_rec->bd_addr, bd_addr, BD_ADDR_LEN); + p_dev_rec->hci_handle = BTM_GetHCIConnHandle (bd_addr, BT_TRANSPORT_BR_EDR); + p_dev_rec->ble_hci_handle = BTM_GetHCIConnHandle (bd_addr, BT_TRANSPORT_LE); + + /* update conn params, use default value for background connection params */ + p_dev_rec->conn_params.min_conn_int = + p_dev_rec->conn_params.max_conn_int = + p_dev_rec->conn_params.supervision_tout = + p_dev_rec->conn_params.slave_latency = BTM_BLE_CONN_PARAM_UNDEF; + + BTM_TRACE_DEBUG ("hci_handl=0x%x ", p_dev_rec->ble_hci_handle ); + } + } + + if (!p_dev_rec) { + return (FALSE); + } + } else { + BTM_TRACE_DEBUG("Device already exist"); + } + + memset(p_dev_rec->sec_bd_name, 0, sizeof(tBTM_BD_NAME)); + + if (bd_name && bd_name[0]) { + p_dev_rec->sec_flags |= BTM_SEC_NAME_KNOWN; + BCM_STRNCPY_S ((char *)p_dev_rec->sec_bd_name, (char *)bd_name, BTM_MAX_REM_BD_NAME_LEN); + } + p_dev_rec->device_type |= dev_type; + p_dev_rec->ble.ble_addr_type = addr_type; + p_dev_rec->ble.auth_mode = auth_mode; + + memcpy (p_dev_rec->ble.pseudo_addr, bd_addr, BD_ADDR_LEN); + /* sync up with the Inq Data base*/ + p_info = BTM_InqDbRead(bd_addr); + if (p_info) { + p_info->results.ble_addr_type = p_dev_rec->ble.ble_addr_type ; + p_info->results.device_type = p_dev_rec->device_type; + BTM_TRACE_DEBUG ("InqDb device_type =0x%x addr_type=0x%x", + p_info->results.device_type, p_info->results.ble_addr_type); + } + return (TRUE); +} +#endif ///SMP_INCLUDED == TRUE + + +/******************************************************************************* +** +** Function BTM_SecAddBleKey +** +** Description Add/modify LE device information. This function will be +** normally called during host startup to restore all required +** information stored in the NVRAM. +** +** Parameters: bd_addr - BD address of the peer +** p_le_key - LE key values. +** key_type - LE SMP key type. +* +** Returns TRUE if added OK, else FALSE +** +*******************************************************************************/ +#if SMP_INCLUDED == TRUE +BOOLEAN BTM_SecAddBleKey (BD_ADDR bd_addr, tBTM_LE_KEY_VALUE *p_le_key, tBTM_LE_KEY_TYPE key_type) +{ + tBTM_SEC_DEV_REC *p_dev_rec; + BTM_TRACE_DEBUG ("BTM_SecAddBleKey"); + p_dev_rec = btm_find_dev (bd_addr); + if (!p_dev_rec || !p_le_key || + (key_type != BTM_LE_KEY_PENC && key_type != BTM_LE_KEY_PID && + key_type != BTM_LE_KEY_PCSRK && key_type != BTM_LE_KEY_LENC && + key_type != BTM_LE_KEY_LCSRK && key_type != BTM_LE_KEY_LID)) { + BTM_TRACE_WARNING ("BTM_SecAddBleKey() Wrong Type, or No Device record \ + for bdaddr: %08x%04x, Type: %d", + (bd_addr[0] << 24) + (bd_addr[1] << 16) + (bd_addr[2] << 8) + bd_addr[3], + (bd_addr[4] << 8) + bd_addr[5], key_type); + return (FALSE); + } + + BTM_TRACE_DEBUG ("BTM_SecAddLeKey() BDA: %08x%04x, Type: 0x%02x", + (bd_addr[0] << 24) + (bd_addr[1] << 16) + (bd_addr[2] << 8) + bd_addr[3], + (bd_addr[4] << 8) + bd_addr[5], key_type); + + btm_sec_save_le_key (bd_addr, key_type, p_le_key, FALSE); + +#if (BLE_PRIVACY_SPT == TRUE) + if (key_type == BTM_LE_KEY_PID || key_type == BTM_LE_KEY_LID) { + btm_ble_resolving_list_load_dev (p_dev_rec); + } +#endif + + + return (TRUE); +} +#endif + + +/******************************************************************************* +** +** Function BTM_BleLoadLocalKeys +** +** Description Local local identity key, encryption root or sign counter. +** +** Parameters: key_type: type of key, can be BTM_BLE_KEY_TYPE_ID, BTM_BLE_KEY_TYPE_ER +** or BTM_BLE_KEY_TYPE_COUNTER. +** p_key: pointer to the key. +* +** Returns non2. +** +*******************************************************************************/ +void BTM_BleLoadLocalKeys(UINT8 key_type, tBTM_BLE_LOCAL_KEYS *p_key) +{ + tBTM_DEVCB *p_devcb = &btm_cb.devcb; + BTM_TRACE_DEBUG ("%s", __func__); + if (p_key != NULL) { + switch (key_type) { + case BTM_BLE_KEY_TYPE_ID: + memcpy(&p_devcb->id_keys, &p_key->id_keys, sizeof(tBTM_BLE_LOCAL_ID_KEYS)); + break; + + case BTM_BLE_KEY_TYPE_ER: + memcpy(p_devcb->ble_encryption_key_value, p_key->er, sizeof(BT_OCTET16)); + break; + + default: + BTM_TRACE_ERROR("unknow local key type: %d", key_type); + break; + } + } +} + +/******************************************************************************* +** +** Function BTM_GetDeviceEncRoot +** +** Description This function is called to read the local device encryption +** root. +** +** Returns void +** the local device ER is copied into ble_encr_key_value +** +*******************************************************************************/ +void BTM_GetDeviceEncRoot (BT_OCTET16 ble_encr_key_value) +{ + BTM_TRACE_DEBUG ("%s", __func__); + memcpy (ble_encr_key_value, btm_cb.devcb.ble_encryption_key_value, BT_OCTET16_LEN); +} + +/******************************************************************************* +** +** Function BTM_GetDeviceIDRoot +** +** Description This function is called to read the local device identity +** root. +** +** Returns void +** the local device IR is copied into irk +** +*******************************************************************************/ +void BTM_GetDeviceIDRoot (BT_OCTET16 irk) +{ + BTM_TRACE_DEBUG ("BTM_GetDeviceIDRoot "); + + memcpy (irk, btm_cb.devcb.id_keys.irk, BT_OCTET16_LEN); +} + +/******************************************************************************* +** +** Function BTM_GetDeviceDHK +** +** Description This function is called to read the local device DHK. +** +** Returns void +** the local device DHK is copied into dhk +** +*******************************************************************************/ +void BTM_GetDeviceDHK (BT_OCTET16 dhk) +{ + BTM_TRACE_DEBUG ("BTM_GetDeviceDHK"); + memcpy (dhk, btm_cb.devcb.id_keys.dhk, BT_OCTET16_LEN); +} + +/******************************************************************************* +** +** Function BTM_ReadConnectionAddr +** +** Description This function is called to get the local device address information +** . +** +** Returns void +** +*******************************************************************************/ +void BTM_ReadConnectionAddr (BD_ADDR remote_bda, BD_ADDR local_conn_addr, tBLE_ADDR_TYPE *p_addr_type) +{ + tACL_CONN *p_acl = btm_bda_to_acl(remote_bda, BT_TRANSPORT_LE); + + if (p_acl == NULL) { + BTM_TRACE_ERROR("No connection exist!"); + return; + } + memcpy(local_conn_addr, p_acl->conn_addr, BD_ADDR_LEN); + * p_addr_type = p_acl->conn_addr_type; + + BTM_TRACE_DEBUG ("BTM_ReadConnectionAddr address type: %d addr: 0x%02x", + p_acl->conn_addr_type, p_acl->conn_addr[0]); +} + +/******************************************************************************* +** +** Function BTM_IsBleConnection +** +** Description This function is called to check if the connection handle +** for an LE link +** +** Returns TRUE if connection is LE link, otherwise FALSE. +** +*******************************************************************************/ +BOOLEAN BTM_IsBleConnection (UINT16 conn_handle) +{ +#if (BLE_INCLUDED == TRUE) + tACL_CONN *p; + + BTM_TRACE_API ("BTM_IsBleConnection: conn_handle: %d", conn_handle); + + p = btm_handle_to_acl(conn_handle); + if (!p) { + return FALSE; + } + return (p->transport == BT_TRANSPORT_LE); +#else + return FALSE; +#endif +} + +/******************************************************************************* +** +** Function BTM_ReadRemoteConnectionAddr +** +** Description This function is read the remote device address currently used +** +** Parameters pseudo_addr: pseudo random address available +** conn_addr:connection address used +** p_addr_type : BD Address type, Public or Random of the address used +** +** Returns BOOLEAN , TRUE if connection to remote device exists, else FALSE +** +*******************************************************************************/ +BOOLEAN BTM_ReadRemoteConnectionAddr(BD_ADDR pseudo_addr, BD_ADDR conn_addr, + tBLE_ADDR_TYPE *p_addr_type) +{ + BOOLEAN st = TRUE; +#if (BLE_PRIVACY_SPT == TRUE) + tACL_CONN *p = btm_bda_to_acl (pseudo_addr, BT_TRANSPORT_LE); + + if (p == NULL) { + BTM_TRACE_ERROR("BTM_ReadRemoteConnectionAddr can not find connection" + " with matching address"); + return FALSE; + } + + memcpy(conn_addr, p->active_remote_addr, BD_ADDR_LEN); + *p_addr_type = p->active_remote_addr_type; +#else + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev(pseudo_addr); + + memcpy(conn_addr, pseudo_addr, BD_ADDR_LEN); + if (p_dev_rec != NULL) { + *p_addr_type = p_dev_rec->ble.ble_addr_type; + } +#endif + return st; + +} +/******************************************************************************* +** +** Function BTM_SecurityGrant +** +** Description This function is called to grant security process. +** +** Parameters bd_addr - peer device bd address. +** res - result of the operation BTM_SUCCESS if success. +** Otherwise, BTM_REPEATED_ATTEMPTS is too many attempts. +** +** Returns None +** +*******************************************************************************/ +void BTM_SecurityGrant(BD_ADDR bd_addr, UINT8 res) +{ +#if SMP_INCLUDED == TRUE + tSMP_STATUS res_smp = (res == BTM_SUCCESS) ? SMP_SUCCESS : SMP_REPEATED_ATTEMPTS; + BTM_TRACE_DEBUG ("BTM_SecurityGrant"); + SMP_SecurityGrant(bd_addr, res_smp); +#endif +} + +/******************************************************************************* +** +** Function BTM_BlePasskeyReply +** +** Description This function is called after Security Manager submitted +** passkey request to the application. +** +** Parameters: bd_addr - Address of the device for which passkey was requested +** res - result of the operation BTM_SUCCESS if success +** key_len - length in bytes of the Passkey +** p_passkey - pointer to array with the passkey +** trusted_mask - bitwise OR of trusted services (array of UINT32) +** +*******************************************************************************/ +void BTM_BlePasskeyReply (BD_ADDR bd_addr, UINT8 res, UINT32 passkey) +{ +#if SMP_INCLUDED == TRUE + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev (bd_addr); + tSMP_STATUS res_smp = (res == BTM_SUCCESS) ? SMP_SUCCESS : SMP_PASSKEY_ENTRY_FAIL; + + if (p_dev_rec == NULL) { + BTM_TRACE_ERROR("Passkey reply to Unknown device"); + return; + } + + p_dev_rec->sec_flags |= BTM_SEC_LE_AUTHENTICATED; + BTM_TRACE_DEBUG ("BTM_BlePasskeyReply"); + SMP_PasskeyReply(bd_addr, res_smp, passkey); +#endif +} + +/******************************************************************************* +** +** Function BTM_BleSetStaticPasskey +** +** Description This function is called to set static passkey +** +** +** Parameters: add - set static passkey when add is TRUE +** clear static passkey when add is FALSE +** passkey - static passkey +** +** +*******************************************************************************/ +void BTM_BleSetStaticPasskey(BOOLEAN add, UINT32 passkey) +{ +#if SMP_INCLUDED == TRUE + SMP_SetStaticPasskey(add, passkey); +#endif +} + +/******************************************************************************* +** +** Function BTM_BleConfirmReply +** +** Description This function is called after Security Manager submitted +** numeric comparison request to the application. +** +** Parameters: bd_addr - Address of the device with which numeric +** comparison was requested +** res - comparison result BTM_SUCCESS if success +** +*******************************************************************************/ +#if (SMP_INCLUDED == TRUE) +void BTM_BleConfirmReply (BD_ADDR bd_addr, UINT8 res) +{ + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev (bd_addr); + tSMP_STATUS res_smp = (res == BTM_SUCCESS) ? SMP_SUCCESS : SMP_PASSKEY_ENTRY_FAIL; + + if (p_dev_rec == NULL) { + BTM_TRACE_ERROR("Passkey reply to Unknown device"); + return; + } + + p_dev_rec->sec_flags |= BTM_SEC_LE_AUTHENTICATED; + BTM_TRACE_DEBUG ("%s\n", __func__); + SMP_ConfirmReply(bd_addr, res_smp); +} +#endif ///SMP_INCLUDED == TRUE + +/******************************************************************************* +** +** Function BTM_BleOobDataReply +** +** Description This function is called to provide the OOB data for +** SMP in response to BTM_LE_OOB_REQ_EVT +** +** Parameters: bd_addr - Address of the peer device +** res - result of the operation SMP_SUCCESS if success +** p_data - simple pairing Randomizer C. +** +*******************************************************************************/ +void BTM_BleOobDataReply(BD_ADDR bd_addr, UINT8 res, UINT8 len, UINT8 *p_data) +{ +#if SMP_INCLUDED == TRUE + tSMP_STATUS res_smp = (res == BTM_SUCCESS) ? SMP_SUCCESS : SMP_OOB_FAIL; + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev (bd_addr); + + BTM_TRACE_DEBUG ("BTM_BleOobDataReply"); + + if (p_dev_rec == NULL) { + BTM_TRACE_ERROR("BTM_BleOobDataReply() to Unknown device"); + return; + } + + p_dev_rec->sec_flags |= BTM_SEC_LE_AUTHENTICATED; + SMP_OobDataReply(bd_addr, res_smp, len, p_data); +#endif +} + +/******************************************************************************* +** +** Function BTM_BleSecureConnectionOobDataReply +** +** Description This function is called to provide the OOB data for +** SMP in response to BTM_LE_SC_OOB_REQ_EVT when secure connection +** +** Parameters: bd_addr - Address of the peer device +** p_c - pointer to Confirmation +** p_r - pointer to Randomizer +** +*******************************************************************************/ +void BTM_BleSecureConnectionOobDataReply(BD_ADDR bd_addr, UINT8 *p_c, UINT8 *p_r) +{ +#if SMP_INCLUDED == TRUE + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev (bd_addr); + + BTM_TRACE_DEBUG ("%s", __func__); + + if (p_dev_rec == NULL) { + BTM_TRACE_ERROR("%s Unknown device", __func__); + return; + } + + p_dev_rec->sec_flags |= BTM_SEC_LE_AUTHENTICATED; + + tSMP_SC_OOB_DATA oob; + memset(&oob, 0, sizeof(tSMP_SC_OOB_DATA)); + + oob.peer_oob_data.present = true; + memcpy(&oob.peer_oob_data.commitment, p_c, BT_OCTET16_LEN); + memcpy(&oob.peer_oob_data.randomizer, p_r, BT_OCTET16_LEN); + oob.peer_oob_data.addr_rcvd_from.type = p_dev_rec->ble.ble_addr_type; + memcpy(oob.peer_oob_data.addr_rcvd_from.bda, bd_addr, BD_ADDR_LEN); + + SMP_SecureConnectionOobDataReply((UINT8 *)&oob); +#endif +} + +/******************************************************************************* +** +** Function BTM_BleSecureConnectionCreateOobData +** +** Description This function is called to create the OOB data for +** SMP when secure connection +** +*******************************************************************************/ +void BTM_BleSecureConnectionCreateOobData(void) +{ +#if SMP_INCLUDED == TRUE + BTM_TRACE_DEBUG ("%s", __func__); + + SMP_CreateLocalSecureConnectionsOobData(); +#endif +} + +/****************************************************************************** +** +** Function BTM_BleSetConnScanParams +** +** Description Set scan parameter used in BLE connection request +** +** Parameters: scan_interval: scan interval +** scan_window: scan window +** +** Returns void +** +*******************************************************************************/ +void BTM_BleSetConnScanParams (UINT32 scan_interval, UINT32 scan_window) +{ +#if SMP_INCLUDED == TRUE + tBTM_BLE_CB *p_ble_cb = &btm_cb.ble_ctr_cb; + BOOLEAN new_param = FALSE; + + if (BTM_BLE_ISVALID_PARAM(scan_interval, BTM_BLE_SCAN_INT_MIN, BTM_BLE_SCAN_INT_MAX) && + BTM_BLE_ISVALID_PARAM(scan_window, BTM_BLE_SCAN_WIN_MIN, BTM_BLE_SCAN_WIN_MAX)) { + if (p_ble_cb->scan_int != scan_interval) { + p_ble_cb->scan_int = scan_interval; + new_param = TRUE; + } + + if (p_ble_cb->scan_win != scan_window) { + p_ble_cb->scan_win = scan_window; + new_param = TRUE; + } + + if (new_param && p_ble_cb->conn_state == BLE_BG_CONN) { + btm_ble_suspend_bg_conn(); + } + } else { + BTM_TRACE_ERROR("Illegal Connection Scan Parameters"); + } +#endif +} + +/******************************************************** +** +** Function BTM_BleSetPrefConnParams +** +** Description Set a peripheral's preferred connection parameters +** +** Parameters: bd_addr - BD address of the peripheral +** scan_interval: scan interval +** scan_window: scan window +** min_conn_int - minimum preferred connection interval +** max_conn_int - maximum preferred connection interval +** slave_latency - preferred slave latency +** supervision_tout - preferred supervision timeout +** +** Returns void +** +*******************************************************************************/ +void BTM_BleSetPrefConnParams (BD_ADDR bd_addr, + UINT16 min_conn_int, UINT16 max_conn_int, + UINT16 slave_latency, UINT16 supervision_tout) +{ + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_or_alloc_dev (bd_addr); + + BTM_TRACE_API ("BTM_BleSetPrefConnParams min: %u max: %u latency: %u \ + tout: %u", + min_conn_int, max_conn_int, slave_latency, supervision_tout); + + if (BTM_BLE_ISVALID_PARAM(min_conn_int, BTM_BLE_CONN_INT_MIN, BTM_BLE_CONN_INT_MAX) && + BTM_BLE_ISVALID_PARAM(max_conn_int, BTM_BLE_CONN_INT_MIN, BTM_BLE_CONN_INT_MAX) && + BTM_BLE_ISVALID_PARAM(supervision_tout, BTM_BLE_CONN_SUP_TOUT_MIN, BTM_BLE_CONN_SUP_TOUT_MAX) && + (slave_latency <= BTM_BLE_CONN_LATENCY_MAX || slave_latency == BTM_BLE_CONN_PARAM_UNDEF)) { + if (p_dev_rec) { + /* expect conn int and stout and slave latency to be updated all together */ + if (min_conn_int != BTM_BLE_CONN_PARAM_UNDEF || max_conn_int != BTM_BLE_CONN_PARAM_UNDEF) { + if (min_conn_int != BTM_BLE_CONN_PARAM_UNDEF) { + p_dev_rec->conn_params.min_conn_int = min_conn_int; + } else { + p_dev_rec->conn_params.min_conn_int = max_conn_int; + } + + if (max_conn_int != BTM_BLE_CONN_PARAM_UNDEF) { + p_dev_rec->conn_params.max_conn_int = max_conn_int; + } else { + p_dev_rec->conn_params.max_conn_int = min_conn_int; + } + + if (slave_latency != BTM_BLE_CONN_PARAM_UNDEF) { + p_dev_rec->conn_params.slave_latency = slave_latency; + } else { + p_dev_rec->conn_params.slave_latency = BTM_BLE_CONN_SLAVE_LATENCY_DEF; + } + + if (supervision_tout != BTM_BLE_CONN_PARAM_UNDEF) { + p_dev_rec->conn_params.supervision_tout = supervision_tout; + } else { + p_dev_rec->conn_params.supervision_tout = BTM_BLE_CONN_TIMEOUT_DEF; + } + + } + + } else { + BTM_TRACE_ERROR("Unknown Device, setting rejected"); + } + } else { + BTM_TRACE_ERROR("Illegal Connection Parameters"); + } +} + +/******************************************************************************* +** +** Function BTM_ReadDevInfo +** +** Description This function is called to read the device/address type +** of BD address. +** +** Parameter remote_bda: remote device address +** p_dev_type: output parameter to read the device type. +** p_addr_type: output parameter to read the address type. +** +*******************************************************************************/ +void BTM_ReadDevInfo (BD_ADDR remote_bda, tBT_DEVICE_TYPE *p_dev_type, tBLE_ADDR_TYPE *p_addr_type) +{ + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev (remote_bda); + tBTM_INQ_INFO *p_inq_info = BTM_InqDbRead(remote_bda); + tBLE_ADDR_TYPE temp_addr_type = (*p_addr_type); + + *p_addr_type = BLE_ADDR_PUBLIC; + + if (!p_dev_rec) { + *p_dev_type = BT_DEVICE_TYPE_BREDR; + /* Check with the BT manager if details about remote device are known */ + if (p_inq_info != NULL) { + *p_dev_type = p_inq_info->results.device_type ; + *p_addr_type = p_inq_info->results.ble_addr_type; + } else { + if(temp_addr_type <= BLE_ADDR_TYPE_MAX) { + *p_addr_type = temp_addr_type; + } else { + /* unknown device, assume BR/EDR */ + BTM_TRACE_DEBUG ("btm_find_dev_type - unknown device, BR/EDR assumed"); + } + } + } else { /* there is a security device record exisitng */ + /* new inquiry result, overwrite device type in security device record */ + if (p_inq_info) { + p_dev_rec->device_type = p_inq_info->results.device_type; + p_dev_rec->ble.ble_addr_type = p_inq_info->results.ble_addr_type; + } + if (memcmp(p_dev_rec->bd_addr, remote_bda, BD_ADDR_LEN) == 0 && + memcmp(p_dev_rec->ble.pseudo_addr, remote_bda, BD_ADDR_LEN) == 0) { + *p_dev_type = p_dev_rec->device_type; + *p_addr_type = p_dev_rec->ble.ble_addr_type; + } else if (memcmp(p_dev_rec->ble.pseudo_addr, remote_bda, BD_ADDR_LEN) == 0) { + *p_dev_type = BT_DEVICE_TYPE_BLE; + *p_addr_type = p_dev_rec->ble.ble_addr_type; + } else { /* matching static adddress only */ + *p_dev_type = BT_DEVICE_TYPE_BREDR; + *p_addr_type = BLE_ADDR_PUBLIC; + } + + } + + BTM_TRACE_DEBUG ("btm_find_dev_type - device_type = %d addr_type = %d", *p_dev_type , *p_addr_type); +} +#endif ///BLE_INCLUDED == TRUE + +/******************************************************************************* +** +** Function BTM_ReadConnectedTransportAddress +** +** Description This function is called to read the paired device/address type of other device paired +** corresponding to the BD_address +** +** Parameter remote_bda: remote device address, carry out the transport address +** transport: active transport +** +** Return TRUE if an active link is identified; FALSE otherwise +** +*******************************************************************************/ +BOOLEAN BTM_ReadConnectedTransportAddress(BD_ADDR remote_bda, tBT_TRANSPORT transport) +{ + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev(remote_bda); + + /* if no device can be located, return */ + if (p_dev_rec == NULL) { + memset(remote_bda, 0, BD_ADDR_LEN); + return FALSE; + } + + if (transport == BT_TRANSPORT_BR_EDR) { + if (btm_bda_to_acl(p_dev_rec->bd_addr, transport) != NULL) { + memcpy(remote_bda, p_dev_rec->bd_addr, BD_ADDR_LEN); + return TRUE; + } else if (p_dev_rec->device_type & BT_DEVICE_TYPE_BREDR) { + memcpy(remote_bda, p_dev_rec->bd_addr, BD_ADDR_LEN); + } else { + memset(remote_bda, 0, BD_ADDR_LEN); + } + return FALSE; + } +#if (BLE_INCLUDED == TRUE) + if (transport == BT_TRANSPORT_LE) { + memcpy(remote_bda, p_dev_rec->ble.pseudo_addr, BD_ADDR_LEN); + if (btm_bda_to_acl(p_dev_rec->ble.pseudo_addr, transport) != NULL) { + return TRUE; + } else { + return FALSE; + } + } +#endif ///BLE_INCLUDED == TRUE + return FALSE; +} + +#if (BLE_INCLUDED == TRUE) +/******************************************************************************* +** +** Function BTM_BleReceiverTest +** +** Description This function is called to start the LE Receiver test +** +** Parameter rx_freq - Frequency Range +** p_cmd_cmpl_cback - Command Complete callback +** +*******************************************************************************/ +void BTM_BleReceiverTest(UINT8 rx_freq, tBTM_CMPL_CB *p_cmd_cmpl_cback) +{ + btm_cb.devcb.p_le_test_cmd_cmpl_cb = p_cmd_cmpl_cback; + + if (btsnd_hcic_ble_receiver_test(rx_freq) == FALSE) { + BTM_TRACE_ERROR("%s: Unable to Trigger LE receiver test", __FUNCTION__); + } +} + +/******************************************************************************* +** +** Function BTM_BleTransmitterTest +** +** Description This function is called to start the LE Transmitter test +** +** Parameter tx_freq - Frequency Range +** test_data_len - Length in bytes of payload data in each packet +** packet_payload - Pattern to use in the payload +** p_cmd_cmpl_cback - Command Complete callback +** +*******************************************************************************/ +void BTM_BleTransmitterTest(UINT8 tx_freq, UINT8 test_data_len, + UINT8 packet_payload, tBTM_CMPL_CB *p_cmd_cmpl_cback) +{ + btm_cb.devcb.p_le_test_cmd_cmpl_cb = p_cmd_cmpl_cback; + if (btsnd_hcic_ble_transmitter_test(tx_freq, test_data_len, packet_payload) == FALSE) { + BTM_TRACE_ERROR("%s: Unable to Trigger LE transmitter test", __FUNCTION__); + } +} + +/******************************************************************************* +** +** Function BTM_BleTestEnd +** +** Description This function is called to stop the in-progress TX or RX test +** +** Parameter p_cmd_cmpl_cback - Command complete callback +** +*******************************************************************************/ +void BTM_BleTestEnd(tBTM_CMPL_CB *p_cmd_cmpl_cback) +{ + btm_cb.devcb.p_le_test_cmd_cmpl_cb = p_cmd_cmpl_cback; + + if (btsnd_hcic_ble_test_end() == FALSE) { + BTM_TRACE_ERROR("%s: Unable to End the LE TX/RX test", __FUNCTION__); + } +} + +/******************************************************************************* +** Internal Functions +*******************************************************************************/ +void btm_ble_test_command_complete(UINT8 *p) +{ + tBTM_CMPL_CB *p_cb = btm_cb.devcb.p_le_test_cmd_cmpl_cb; + + btm_cb.devcb.p_le_test_cmd_cmpl_cb = NULL; + + if (p_cb) { + (*p_cb)(p); + } +} + + +#if (BLE_50_FEATURE_SUPPORT == TRUE) +/******************************************************************************* +** +** Function BTM_BleEnhancedReceiverTest +** +** Description This function is called to start the LE Enhanced Receiver test +** +** Parameter rx_freq - Frequency Range +** phy - The type of phy that receives data +** modulation_index - modulation index +** p_cmd_cmpl_cback - Command Complete callback +** +*******************************************************************************/ +void BTM_BleEnhancedReceiverTest(UINT8 rx_freq, UINT8 phy, UINT8 modulation_index, tBTM_CMPL_CB *p_cmd_cmpl_cback) +{ + btm_cb.devcb.p_le_test_cmd_cmpl_cb = p_cmd_cmpl_cback; + + if (btsnd_hcic_ble_enhand_rx_test(rx_freq, phy, modulation_index) == FALSE) { + BTM_TRACE_ERROR("%s: Unable to Trigger LE enhanced receiver test", __FUNCTION__); + } +} + +/******************************************************************************* +** +** Function BTM_BleEnhancedTransmitterTest +** +** Description This function is called to start the LE Enhanced Transmitter test +** +** Parameter tx_freq - Frequency Range +** test_data_len - Length in bytes of payload data in each packet +** packet_payload - Pattern to use in the payload +** phy - The type of phy that sends data +** p_cmd_cmpl_cback - Command Complete callback +** +*******************************************************************************/ +void BTM_BleEnhancedTransmitterTest(UINT8 tx_freq, UINT8 test_data_len, + UINT8 packet_payload, UINT8 phy, tBTM_CMPL_CB *p_cmd_cmpl_cback) +{ + btm_cb.devcb.p_le_test_cmd_cmpl_cb = p_cmd_cmpl_cback; + if (btsnd_hcic_ble_enhand_tx_test(tx_freq, test_data_len, packet_payload, phy) == FALSE) { + BTM_TRACE_ERROR("%s: Unable to Trigger LE enhanced transmitter test", __FUNCTION__); + } +} +#endif // BLE_50_FEATURE_SUPPORT + +/******************************************************************************* +** +** Function BTM_UseLeLink +** +** Description This function is to select the underneath physical link to use. +** +** Returns TRUE to use LE, FALSE use BR/EDR. +** +*******************************************************************************/ +BOOLEAN BTM_UseLeLink (BD_ADDR bd_addr) +{ + tACL_CONN *p; + tBT_DEVICE_TYPE dev_type; + tBLE_ADDR_TYPE addr_type = 0; + BOOLEAN use_le = FALSE; + + if ((p = btm_bda_to_acl(bd_addr, BT_TRANSPORT_BR_EDR)) != NULL) { + return use_le; + } else if ((p = btm_bda_to_acl(bd_addr, BT_TRANSPORT_LE)) != NULL) { + use_le = TRUE; + } else { + BTM_ReadDevInfo(bd_addr, &dev_type, &addr_type); + use_le = (dev_type == BT_DEVICE_TYPE_BLE); + } + return use_le; +} + + +/******************************************************************************* +** +** Function BTM_SetBleDataLength +** +** Description This function is to set maximum BLE transmission packet size +** +** Returns BTM_SUCCESS if success; otherwise failed. +** +*******************************************************************************/ +tBTM_STATUS BTM_SetBleDataLength(BD_ADDR bd_addr, UINT16 tx_pdu_length) +{ + tACL_CONN *p_acl = btm_bda_to_acl(bd_addr, BT_TRANSPORT_LE); + + BTM_TRACE_DEBUG("%s: tx_pdu_length =%d", __FUNCTION__, tx_pdu_length); + + if (!controller_get_interface()->supports_ble_packet_extension()) { + BTM_TRACE_ERROR("%s failed, request not supported", __FUNCTION__); + return BTM_CONTROL_LE_DATA_LEN_UNSUPPORTED; + } + + if (p_acl != NULL) { + if (!HCI_LE_DATA_LEN_EXT_SUPPORTED(p_acl->peer_le_features)) { + BTM_TRACE_ERROR("%s failed, peer does not support request", __FUNCTION__); + return BTM_PEER_LE_DATA_LEN_UNSUPPORTED; + } + + if (tx_pdu_length > BTM_BLE_DATA_SIZE_MAX) { + tx_pdu_length = BTM_BLE_DATA_SIZE_MAX; + } else if (tx_pdu_length < BTM_BLE_DATA_SIZE_MIN) { + tx_pdu_length = BTM_BLE_DATA_SIZE_MIN; + } + + /* always set the TxTime to be max, as controller does not care for now */ + btsnd_hcic_ble_set_data_length(p_acl->hci_handle, tx_pdu_length, + BTM_BLE_DATA_TX_TIME_MAX); + + return BTM_SUCCESS; + } else { + BTM_TRACE_ERROR("%s: Wrong mode: no LE link exist or LE not supported", __FUNCTION__); + return BTM_WRONG_MODE; + } +} + +#if (SMP_INCLUDED == TRUE) +/******************************************************************************* +** +** Function btm_ble_determine_security_act +** +** Description This function checks the security of current LE link +** and returns the appropriate action that needs to be +** taken to achieve the required security. +** +** Parameter is_originator - True if outgoing connection +** bdaddr: remote device address +** security_required: Security required for the service. +** +** Returns The appropriate security action required. +** +*******************************************************************************/ +tBTM_SEC_ACTION btm_ble_determine_security_act(BOOLEAN is_originator, BD_ADDR bdaddr, UINT16 security_required) +{ + tBTM_LE_AUTH_REQ auth_req = 0x00; + + if (is_originator) + { + if ((security_required & BTM_SEC_OUT_FLAGS) == 0 && + (security_required & BTM_SEC_OUT_MITM) == 0) + { + BTM_TRACE_DEBUG ("%s No security required for outgoing connection", __func__); + return BTM_SEC_OK; + } + + if (security_required & BTM_SEC_OUT_MITM) { + auth_req |= BTM_LE_AUTH_REQ_MITM; + } + } + else + { + if ((security_required & BTM_SEC_IN_FLAGS) == 0&& (security_required & BTM_SEC_IN_MITM) == 0) + { + BTM_TRACE_DEBUG ("%s No security required for incoming connection", __func__); + return BTM_SEC_OK; + } + + if (security_required & BTM_SEC_IN_MITM) { + auth_req |= BTM_LE_AUTH_REQ_MITM; + } + } + + tBTM_BLE_SEC_REQ_ACT ble_sec_act = BTM_BLE_SEC_REQ_ACT_NONE; + btm_ble_link_sec_check(bdaddr, auth_req, &ble_sec_act); + + BTM_TRACE_DEBUG ("%s ble_sec_act %d", __func__ , ble_sec_act); + + if (ble_sec_act == BTM_BLE_SEC_REQ_ACT_DISCARD) { + return BTM_SEC_ENC_PENDING; + } + + if (ble_sec_act == BTM_BLE_SEC_REQ_ACT_NONE) { + return BTM_SEC_OK; + } + + UINT8 sec_flag = 0; + BTM_GetSecurityFlagsByTransport(bdaddr, &sec_flag, BT_TRANSPORT_LE); + + BOOLEAN is_link_encrypted = FALSE; + BOOLEAN is_key_mitm = FALSE; + if (sec_flag & (BTM_SEC_FLAG_ENCRYPTED| BTM_SEC_FLAG_LKEY_KNOWN)) + { + if (sec_flag & BTM_SEC_FLAG_ENCRYPTED) { + is_link_encrypted = TRUE; + } + + if (sec_flag & BTM_SEC_FLAG_LKEY_AUTHED) { + is_key_mitm = TRUE; + } + } + + if (auth_req & BTM_LE_AUTH_REQ_MITM) + { + if (!is_key_mitm) + { + return BTM_SEC_ENCRYPT_MITM; + } else { + if (is_link_encrypted) { + return BTM_SEC_OK; + } else { + return BTM_SEC_ENCRYPT; + } + } + } else { + if (is_link_encrypted) { + return BTM_SEC_OK; + } else { + return BTM_SEC_ENCRYPT_NO_MITM; + } + } + + return BTM_SEC_OK; +} + +/******************************************************************************* +** +** Function btm_ble_start_sec_check +** +** Description This function is to check and set the security required for +** LE link for LE COC. +** +** Parameter bdaddr: remote device address. +** psm : PSM of the LE COC sevice. +** is_originator: TRUE if outgoing connection. +** p_callback : Pointer to the callback function. +** p_ref_data : Pointer to be returned along with the callback. +** +** Returns TRUE if link already meets the required security; otherwise FALSE. +** +*******************************************************************************/ +BOOLEAN btm_ble_start_sec_check(BD_ADDR bd_addr, UINT16 psm, BOOLEAN is_originator, + tBTM_SEC_CALLBACK *p_callback, void *p_ref_data) +{ + /* Find the service record for the PSM */ + tBTM_SEC_SERV_REC *p_serv_rec = btm_sec_find_first_serv (is_originator, psm); + + /* If there is no application registered with this PSM do not allow connection */ + if (!p_serv_rec) + { + BTM_TRACE_WARNING ("%s PSM: %d no application registerd", __func__, psm); + (*p_callback) (bd_addr, BT_TRANSPORT_LE, p_ref_data, BTM_MODE_UNSUPPORTED); + return FALSE; + } + + tBTM_SEC_ACTION sec_act = btm_ble_determine_security_act(is_originator, + bd_addr, p_serv_rec->security_flags); + + tBTM_BLE_SEC_ACT ble_sec_act = BTM_BLE_SEC_NONE; + BOOLEAN status = FALSE; + + switch (sec_act) + { + case BTM_SEC_OK: + BTM_TRACE_DEBUG ("%s Security met", __func__); + p_callback(bd_addr, BT_TRANSPORT_LE, p_ref_data, BTM_SUCCESS); + status = TRUE; + break; + + case BTM_SEC_ENCRYPT: + BTM_TRACE_DEBUG ("%s Encryption needs to be done", __func__); + ble_sec_act = BTM_BLE_SEC_ENCRYPT; + break; + + case BTM_SEC_ENCRYPT_MITM: + BTM_TRACE_DEBUG ("%s Pairing with MITM needs to be done", __func__); + ble_sec_act = BTM_BLE_SEC_ENCRYPT_MITM; + break; + + case BTM_SEC_ENCRYPT_NO_MITM: + BTM_TRACE_DEBUG ("%s Pairing with No MITM needs to be done", __func__); + ble_sec_act = BTM_BLE_SEC_ENCRYPT_NO_MITM; + break; + + case BTM_SEC_ENC_PENDING: + BTM_TRACE_DEBUG ("%s Ecryption pending", __func__); + break; + } + + if (ble_sec_act == BTM_BLE_SEC_NONE) { + return status; + } + + tL2C_LCB *p_lcb = l2cu_find_lcb_by_bd_addr(bd_addr, BT_TRANSPORT_LE); + p_lcb->sec_act = sec_act; + BTM_SetEncryption(bd_addr, BT_TRANSPORT_LE, p_callback, p_ref_data); + + return FALSE; +} + + +/******************************************************************************* +** +** Function btm_ble_rand_enc_complete +** +** Description This function is the callback functions for HCI_Rand command +** and HCI_Encrypt command is completed. +** This message is received from the HCI. +** +** Returns void +** +*******************************************************************************/ +void btm_ble_rand_enc_complete (UINT8 *p, UINT16 op_code, tBTM_RAND_ENC_CB *p_enc_cplt_cback) +{ + tBTM_RAND_ENC params; + UINT8 *p_dest = params.param_buf; + + BTM_TRACE_DEBUG ("btm_ble_rand_enc_complete"); + + memset(¶ms, 0, sizeof(tBTM_RAND_ENC)); + + /* If there was a callback address for vcs complete, call it */ + if (p_enc_cplt_cback && p) { + /* Pass paramters to the callback function */ + STREAM_TO_UINT8(params.status, p); /* command status */ + + if (params.status == HCI_SUCCESS) { + params.opcode = op_code; + + if (op_code == HCI_BLE_RAND) { + params.param_len = BT_OCTET8_LEN; + } else { + params.param_len = BT_OCTET16_LEN; + } + + memcpy(p_dest, p, params.param_len); /* Fetch return info from HCI event message */ + } + if (p_enc_cplt_cback) { + (*p_enc_cplt_cback)(¶ms); /* Call the Encryption complete callback function */ + } + } +} +#endif ///SMP_INCLUDED == TRUE + + +/******************************************************************************* +** +** Function btm_ble_get_enc_key_type +** +** Description This function is to increment local sign counter +** Returns None +** +*******************************************************************************/ +#if (SMP_INCLUDED == TRUE) +void btm_ble_increment_sign_ctr(BD_ADDR bd_addr, BOOLEAN is_local ) +{ + tBTM_SEC_DEV_REC *p_dev_rec; + + BTM_TRACE_DEBUG ("btm_ble_increment_sign_ctr is_local=%d", is_local); + + if ((p_dev_rec = btm_find_dev (bd_addr)) != NULL) { + if (is_local) { + p_dev_rec->ble.keys.local_counter++; + } else { + p_dev_rec->ble.keys.counter++; + } + BTM_TRACE_DEBUG ("is_local=%d local sign counter=%d peer sign counter=%d", + is_local, + p_dev_rec->ble.keys.local_counter, + p_dev_rec->ble.keys.counter); + } +} +#endif ///SMP_INCLUDED == TRUE +#endif ///BLE_INCLUDED == TRUE + +/******************************************************************************* +** +** Function btm_ble_get_enc_key_type +** +** Description This function is to get the BLE key type that has been exchanged +** in betweem local device and peer device. +** +** Returns p_key_type: output parameter to carry the key type value. +** +*******************************************************************************/ +#if (SMP_INCLUDED == TRUE) +#if (BLE_INCLUDED == TRUE) +BOOLEAN btm_ble_get_enc_key_type(BD_ADDR bd_addr, UINT8 *p_key_types) +{ + tBTM_SEC_DEV_REC *p_dev_rec; + + BTM_TRACE_DEBUG ("btm_ble_get_enc_key_type"); + + if ((p_dev_rec = btm_find_dev (bd_addr)) != NULL) { + *p_key_types = p_dev_rec->ble.key_type; + return TRUE; + } + + return FALSE; +} + +/******************************************************************************* +** +** Function btm_get_local_div +** +** Description This function is called to read the local DIV +** +** Returns TRUE - if a valid DIV is availavle +*******************************************************************************/ +BOOLEAN btm_get_local_div (BD_ADDR bd_addr, UINT16 *p_div) +{ + tBTM_SEC_DEV_REC *p_dev_rec; + BOOLEAN status = FALSE; + BTM_TRACE_DEBUG ("btm_get_local_div"); + + BTM_TRACE_DEBUG("bd_addr:%02x-%02x-%02x-%02x-%02x-%02x", + bd_addr[0], bd_addr[1], + bd_addr[2], bd_addr[3], + bd_addr[4], bd_addr[5]); + + *p_div = 0; + p_dev_rec = btm_find_dev (bd_addr); + + if (p_dev_rec && p_dev_rec->ble.keys.div) { + status = TRUE; + *p_div = p_dev_rec->ble.keys.div; + } + BTM_TRACE_DEBUG ("btm_get_local_div status=%d (1-OK) DIV=0x%x", status, *p_div); + return status; +} + +/******************************************************************************* +** +** Function btm_sec_save_le_key +** +** Description This function is called by the SMP to update +** an BLE key. SMP is internal, whereas all the keys shall +** be sent to the application. The function is also called +** when application passes ble key stored in NVRAM to the btm_sec. +** pass_to_application parameter is false in this case. +** +** Returns void +** +*******************************************************************************/ +void btm_sec_save_le_key(BD_ADDR bd_addr, tBTM_LE_KEY_TYPE key_type, tBTM_LE_KEY_VALUE *p_keys, + BOOLEAN pass_to_application) +{ + tBTM_SEC_DEV_REC *p_rec; + tBTM_LE_EVT_DATA cb_data; + UINT8 i; + + BTM_TRACE_DEBUG ("btm_sec_save_le_key key_type=0x%x pass_to_application=%d", key_type, pass_to_application); + /* Store the updated key in the device database */ + + BTM_TRACE_DEBUG("bd_addr:%02x-%02x-%02x-%02x-%02x-%02x", + bd_addr[0], bd_addr[1], + bd_addr[2], bd_addr[3], + bd_addr[4], bd_addr[5]); + + if ((p_rec = btm_find_dev (bd_addr)) != NULL && (p_keys || key_type == BTM_LE_KEY_LID)) { + btm_ble_init_pseudo_addr (p_rec, bd_addr); + + switch (key_type) { + case BTM_LE_KEY_PENC: + memcpy(p_rec->ble.keys.pltk, p_keys->penc_key.ltk, BT_OCTET16_LEN); + memcpy(p_rec->ble.keys.rand, p_keys->penc_key.rand, BT_OCTET8_LEN); + p_rec->ble.keys.sec_level = p_keys->penc_key.sec_level; + p_rec->ble.keys.ediv = p_keys->penc_key.ediv; + p_rec->ble.keys.key_size = p_keys->penc_key.key_size; + p_rec->ble.key_type |= BTM_LE_KEY_PENC; + p_rec->sec_flags |= BTM_SEC_LE_LINK_KEY_KNOWN; + if (p_keys->penc_key.sec_level == SMP_SEC_AUTHENTICATED) { + p_rec->sec_flags |= BTM_SEC_LE_LINK_KEY_AUTHED; + } else { + p_rec->sec_flags &= ~BTM_SEC_LE_LINK_KEY_AUTHED; + } + BTM_TRACE_DEBUG("BTM_LE_KEY_PENC key_type=0x%x sec_flags=0x%x sec_leve=0x%x", + p_rec->ble.key_type, + p_rec->sec_flags, + p_rec->ble.keys.sec_level); + break; + + case BTM_LE_KEY_PID: + for (i = 0; i < BT_OCTET16_LEN; i++) { + p_rec->ble.keys.irk[i] = p_keys->pid_key.irk[i]; + } + + //memcpy( p_rec->ble.keys.irk, p_keys->pid_key, BT_OCTET16_LEN); todo will crash the system + memcpy(p_rec->ble.static_addr, p_keys->pid_key.static_addr, BD_ADDR_LEN); + p_rec->ble.static_addr_type = p_keys->pid_key.addr_type; + p_rec->ble.key_type |= BTM_LE_KEY_PID; + BTM_TRACE_DEBUG("BTM_LE_KEY_PID key_type=0x%x save peer IRK", p_rec->ble.key_type); + /* update device record address as static address */ + memcpy(p_rec->bd_addr, p_keys->pid_key.static_addr, BD_ADDR_LEN); + /* combine DUMO device security record if needed */ + btm_consolidate_dev(p_rec); + break; + + case BTM_LE_KEY_PCSRK: + memcpy(p_rec->ble.keys.pcsrk, p_keys->pcsrk_key.csrk, BT_OCTET16_LEN); + p_rec->ble.keys.srk_sec_level = p_keys->pcsrk_key.sec_level; + p_rec->ble.keys.counter = p_keys->pcsrk_key.counter; + p_rec->ble.key_type |= BTM_LE_KEY_PCSRK; + p_rec->sec_flags |= BTM_SEC_LE_LINK_KEY_KNOWN; + if ( p_keys->pcsrk_key.sec_level == SMP_SEC_AUTHENTICATED) { + p_rec->sec_flags |= BTM_SEC_LE_LINK_KEY_AUTHED; + } else { + p_rec->sec_flags &= ~BTM_SEC_LE_LINK_KEY_AUTHED; + } + + BTM_TRACE_DEBUG("BTM_LE_KEY_PCSRK key_type=0x%x sec_flags=0x%x sec_level=0x%x peer_counter=%d", + p_rec->ble.key_type, + p_rec->sec_flags, + p_rec->ble.keys.srk_sec_level, + p_rec->ble.keys.counter ); + break; + + case BTM_LE_KEY_LENC: + memcpy(p_rec->ble.keys.lltk, p_keys->lenc_key.ltk, BT_OCTET16_LEN); + p_rec->ble.keys.div = p_keys->lenc_key.div; /* update DIV */ + p_rec->ble.keys.sec_level = p_keys->lenc_key.sec_level; + p_rec->ble.keys.key_size = p_keys->lenc_key.key_size; + p_rec->ble.key_type |= BTM_LE_KEY_LENC; + + /* Set that link key is known since this shares field with BTM_SEC_FLAG_LKEY_KNOWN flag in stack/btm_api.h*/ + p_rec->sec_flags |= BTM_SEC_LE_LINK_KEY_KNOWN; + if ( p_keys->lenc_key.sec_level == SMP_SEC_AUTHENTICATED) { + p_rec->sec_flags |= BTM_SEC_LE_LINK_KEY_AUTHED; + } else { + p_rec->sec_flags &= ~BTM_SEC_LE_LINK_KEY_AUTHED; + } + + BTM_TRACE_DEBUG("BTM_LE_KEY_LENC key_type=0x%x DIV=0x%x key_size=0x%x sec_level=0x%x", + p_rec->ble.key_type, + p_rec->ble.keys.div, + p_rec->ble.keys.key_size, + p_rec->ble.keys.sec_level ); + break; + + case BTM_LE_KEY_LCSRK:/* local CSRK has been delivered */ + memcpy (p_rec->ble.keys.lcsrk, p_keys->lcsrk_key.csrk, BT_OCTET16_LEN); + p_rec->ble.keys.div = p_keys->lcsrk_key.div; /* update DIV */ + p_rec->ble.keys.local_csrk_sec_level = p_keys->lcsrk_key.sec_level; + p_rec->ble.keys.local_counter = p_keys->lcsrk_key.counter; + p_rec->ble.key_type |= BTM_LE_KEY_LCSRK; + BTM_TRACE_DEBUG("BTM_LE_KEY_LCSRK key_type=0x%x DIV=0x%x scrk_sec_level=0x%x local_counter=%d", + p_rec->ble.key_type, + p_rec->ble.keys.div, + p_rec->ble.keys.local_csrk_sec_level, + p_rec->ble.keys.local_counter ); + break; + + case BTM_LE_KEY_LID: + p_rec->ble.key_type |= BTM_LE_KEY_LID; + break; + default: + BTM_TRACE_WARNING("btm_sec_save_le_key (Bad key_type 0x%02x)", key_type); + return; + } + + BTM_TRACE_DEBUG ("BLE key type 0x%02x updated for BDA: %08x%04x (btm_sec_save_le_key)", key_type, + (bd_addr[0] << 24) + (bd_addr[1] << 16) + (bd_addr[2] << 8) + bd_addr[3], + (bd_addr[4] << 8) + bd_addr[5]); + + /* Notify the application that one of the BLE keys has been updated + If link key is in progress, it will get sent later.*/ + if (pass_to_application && btm_cb.api.p_le_callback) { + cb_data.key.p_key_value = p_keys; + cb_data.key.key_type = key_type; + + (*btm_cb.api.p_le_callback) (BTM_LE_KEY_EVT, bd_addr, &cb_data); + } + return; + } + + BTM_TRACE_WARNING ("BLE key type 0x%02x called for Unknown BDA or type: %08x%04x !! (btm_sec_save_le_key)", key_type, + (bd_addr[0] << 24) + (bd_addr[1] << 16) + (bd_addr[2] << 8) + bd_addr[3], + (bd_addr[4] << 8) + bd_addr[5]); + + if (p_rec) { + BTM_TRACE_DEBUG ("sec_flags=0x%x", p_rec->sec_flags); + } +} + +/******************************************************************************* +** +** Function btm_ble_update_sec_key_size +** +** Description update the current lin kencryption key size +** +** Returns void +** +*******************************************************************************/ +void btm_ble_update_sec_key_size(BD_ADDR bd_addr, UINT8 enc_key_size) +{ + tBTM_SEC_DEV_REC *p_rec; + + BTM_TRACE_DEBUG("btm_ble_update_sec_key_size enc_key_size = %d", enc_key_size); + + if ((p_rec = btm_find_dev (bd_addr)) != NULL ) { + p_rec->enc_key_size = enc_key_size; + } +} + + +/******************************************************************************* +** +** Function btm_ble_read_sec_key_size +** +** Description update the current lin kencryption key size +** +** Returns void +** +*******************************************************************************/ +UINT8 btm_ble_read_sec_key_size(BD_ADDR bd_addr) +{ + tBTM_SEC_DEV_REC *p_rec; + + if ((p_rec = btm_find_dev (bd_addr)) != NULL ) { + return p_rec->enc_key_size; + } else { + return 0; + } + return 0; +} + +/******************************************************************************* +** +** Function btm_ble_link_sec_check +** +** Description Check BLE link security level match. +** +** Returns TRUE: check is OK and the *p_sec_req_act contain the action +** +*******************************************************************************/ +void btm_ble_link_sec_check(BD_ADDR bd_addr, tBTM_LE_AUTH_REQ auth_req, tBTM_BLE_SEC_REQ_ACT *p_sec_req_act) +{ + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev (bd_addr); + UINT8 req_sec_level = BTM_LE_SEC_NONE, cur_sec_level = BTM_LE_SEC_NONE; + + BTM_TRACE_DEBUG ("btm_ble_link_sec_check auth_req =0x%x", auth_req); + + if (p_dev_rec == NULL) { + BTM_TRACE_ERROR ("btm_ble_link_sec_check received for unknown device"); + return; + } + + if (p_dev_rec->sec_state == BTM_SEC_STATE_ENCRYPTING || + p_dev_rec->sec_state == BTM_SEC_STATE_AUTHENTICATING) { + /* race condition: discard the security request while master is encrypting the link */ + *p_sec_req_act = BTM_BLE_SEC_REQ_ACT_DISCARD; + } else { + req_sec_level = BTM_LE_SEC_UNAUTHENTICATE; + if (auth_req & BTM_LE_AUTH_REQ_MITM) { + req_sec_level = BTM_LE_SEC_AUTHENTICATED; + } + + BTM_TRACE_DEBUG ("dev_rec sec_flags=0x%x", p_dev_rec->sec_flags); + + /* currently encrpted */ + if (p_dev_rec->sec_flags & BTM_SEC_LE_ENCRYPTED) { + if (p_dev_rec->sec_flags & BTM_SEC_LE_AUTHENTICATED) { + cur_sec_level = BTM_LE_SEC_AUTHENTICATED; + } else { + cur_sec_level = BTM_LE_SEC_UNAUTHENTICATE; + } + } else { /* unencrypted link */ + /* if bonded, get the key security level */ + if (p_dev_rec->ble.key_type & BTM_LE_KEY_PENC) { + cur_sec_level = p_dev_rec->ble.keys.sec_level; + } else { + cur_sec_level = BTM_LE_SEC_NONE; + } + } + + if (cur_sec_level >= req_sec_level) { + /* To avoid re-encryption on an encrypted link for an equal condition encryption */ + *p_sec_req_act = BTM_BLE_SEC_REQ_ACT_ENCRYPT; + } else { + *p_sec_req_act = BTM_BLE_SEC_REQ_ACT_PAIR; /* start the pariring process to upgrade the keys*/ + } + } + + BTM_TRACE_DEBUG("cur_sec_level=%d req_sec_level=%d sec_req_act=%d", + cur_sec_level, + req_sec_level, + *p_sec_req_act); + + +} +#endif ///BLE_INCLUDED == TRUE +#endif ///SMP_INCLUDED == TRUE + +#if (BLE_INCLUDED == TRUE) +/******************************************************************************* +** +** Function btm_ble_set_encryption +** +** Description This function is called to ensure that LE connection is +** encrypted. Should be called only on an open connection. +** Typically only needed for connections that first want to +** bring up unencrypted links, then later encrypt them. +** +** Returns void +** the local device ER is copied into er +** +*******************************************************************************/ +tBTM_STATUS btm_ble_set_encryption (BD_ADDR bd_addr, void *p_ref_data, UINT8 link_role) +{ + tBTM_STATUS cmd = BTM_NO_RESOURCES; +#if (SMP_INCLUDED == TRUE) + tBTM_BLE_SEC_ACT sec_act = *(tBTM_BLE_SEC_ACT *)p_ref_data ; + tBTM_SEC_DEV_REC *p_rec = btm_find_dev (bd_addr); + tBTM_BLE_SEC_REQ_ACT sec_req_act; + tBTM_LE_AUTH_REQ auth_req; + + if (p_rec == NULL) { + BTM_TRACE_WARNING ("btm_ble_set_encryption (NULL device record!! sec_act=0x%x", sec_act); + return (BTM_WRONG_MODE); + } + + BTM_TRACE_DEBUG ("btm_ble_set_encryption sec_act=0x%x role_master=%d", sec_act, p_rec->role_master); + + if (sec_act == BTM_BLE_SEC_ENCRYPT_MITM) { + p_rec->security_required |= BTM_SEC_IN_MITM; + } + + switch (sec_act) { + case BTM_BLE_SEC_ENCRYPT: + if (link_role == BTM_ROLE_MASTER && (p_rec->ble.key_type & BTM_LE_KEY_PENC)) { + /* start link layer encryption using the security info stored */ + cmd = btm_ble_start_encrypt(bd_addr, FALSE, NULL); + break; + } + /* if salve role then fall through to call SMP_Pair below which will send a + sec_request to request the master to encrypt the link */ + case BTM_BLE_SEC_ENCRYPT_NO_MITM: + case BTM_BLE_SEC_ENCRYPT_MITM: + if ((link_role == BTM_ROLE_MASTER) && (sec_act != BTM_BLE_SEC_ENCRYPT)) { + auth_req = (sec_act == BTM_BLE_SEC_ENCRYPT_NO_MITM) + ? SMP_AUTH_GEN_BOND : (SMP_AUTH_GEN_BOND | SMP_AUTH_YN_BIT); + btm_ble_link_sec_check (bd_addr, auth_req, &sec_req_act); + + if (sec_req_act == BTM_BLE_SEC_REQ_ACT_ENCRYPT) { + cmd = btm_ble_start_encrypt(bd_addr, FALSE, NULL); + break; + } + } +#if (SMP_SLAVE_CON_PARAMS_UPD_ENABLE == TRUE) + // already have encrypted information, do not need to update connection parameters + if(link_role == BTM_ROLE_SLAVE && (p_rec->ble.key_type & BTM_LE_KEY_PENC)) { + p_rec->ble.skip_update_conn_param = true; + } else { + p_rec->ble.skip_update_conn_param = false; + } +#endif + if (SMP_Pair(bd_addr) == SMP_STARTED) { + cmd = BTM_CMD_STARTED; + p_rec->sec_state = BTM_SEC_STATE_AUTHENTICATING; + } + break; + + default: + cmd = BTM_WRONG_MODE; + break; + } +#endif ///SMP_INCLUDED == TRUE + return cmd; +} + +/******************************************************************************* +** +** Function btm_ble_ltk_request +** +** Description This function is called when encryption request is received +** on a slave device. +** +** +** Returns void +** +*******************************************************************************/ +#if (SMP_INCLUDED == TRUE) +void btm_ble_ltk_request(UINT16 handle, UINT8 rand[8], UINT16 ediv) +{ + tBTM_CB *p_cb = &btm_cb; + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev_by_handle (handle); + BT_OCTET16 dummy_stk = {0}; + + BTM_TRACE_DEBUG ("btm_ble_ltk_request"); + + p_cb->ediv = ediv; + + memcpy(p_cb->enc_rand, rand, BT_OCTET8_LEN); + + if (p_dev_rec != NULL) { + if (!smp_proc_ltk_request(p_dev_rec->bd_addr)) { + btm_ble_ltk_request_reply(p_dev_rec->bd_addr, FALSE, dummy_stk); + } + } + +} +#endif ///SMP_INCLUDED == TRUE + + +/******************************************************************************* +** +** Function btm_ble_start_encrypt +** +** Description This function is called to start LE encryption. +** +** +** Returns BTM_SUCCESS if encryption was started successfully +** +*******************************************************************************/ +tBTM_STATUS btm_ble_start_encrypt(BD_ADDR bda, BOOLEAN use_stk, BT_OCTET16 stk) +{ +#if (SMP_INCLUDED == TRUE) + tBTM_CB *p_cb = &btm_cb; + tBTM_SEC_DEV_REC *p_rec = btm_find_dev (bda); + BT_OCTET8 dummy_rand = {0}; +#endif ///SMP_INCLUDED == TRUE + + tBTM_STATUS rt = BTM_NO_RESOURCES; +#if (SMP_INCLUDED == TRUE) + BTM_TRACE_DEBUG ("btm_ble_start_encrypt"); + + if (!p_rec ) { + BTM_TRACE_ERROR("Link is not active, can not encrypt!"); + return BTM_WRONG_MODE; + } + + if (p_rec->sec_state == BTM_SEC_STATE_ENCRYPTING) { + BTM_TRACE_WARNING("Link Encryption is active, Busy!"); + return BTM_BUSY; + } + + p_cb->enc_handle = p_rec->ble_hci_handle; + + if (use_stk) { + if (btsnd_hcic_ble_start_enc(p_rec->ble_hci_handle, dummy_rand, 0, stk)) { + rt = BTM_CMD_STARTED; + } + } else if (p_rec->ble.key_type & BTM_LE_KEY_PENC) { + if (btsnd_hcic_ble_start_enc(p_rec->ble_hci_handle, p_rec->ble.keys.rand, + p_rec->ble.keys.ediv, p_rec->ble.keys.pltk)) { + rt = BTM_CMD_STARTED; + } + } else { + BTM_TRACE_ERROR("No key available to encrypt the link"); + } + if (rt == BTM_CMD_STARTED) { + if (p_rec->sec_state == BTM_SEC_STATE_IDLE) { + p_rec->sec_state = BTM_SEC_STATE_ENCRYPTING; + } + } +#endif ///SMP_INCLUDED == TRUE + return rt; +} + +/******************************************************************************* +** +** Function btm_ble_link_encrypted +** +** Description This function is called when LE link encrption status is changed. +** +** Returns void +** +*******************************************************************************/ +#if (SMP_INCLUDED == TRUE) +void btm_ble_link_encrypted(BD_ADDR bd_addr, UINT8 encr_enable) +{ + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev (bd_addr); + BOOLEAN enc_cback; + + if (!p_dev_rec) { + BTM_TRACE_WARNING ("btm_ble_link_encrypted (No Device Found!) encr_enable=%d", encr_enable); + return; + } + + BTM_TRACE_DEBUG ("btm_ble_link_encrypted encr_enable=%d", encr_enable); + + enc_cback = (p_dev_rec->sec_state == BTM_SEC_STATE_ENCRYPTING); + + smp_link_encrypted(bd_addr, encr_enable); + + BTM_TRACE_DEBUG(" p_dev_rec->sec_flags=0x%x", p_dev_rec->sec_flags); + + if (encr_enable && p_dev_rec->enc_key_size == 0) { + p_dev_rec->enc_key_size = p_dev_rec->ble.keys.key_size; + } + + p_dev_rec->sec_state = BTM_SEC_STATE_IDLE; + if (p_dev_rec->p_callback && enc_cback) { + if (encr_enable) { + btm_sec_dev_rec_cback_event(p_dev_rec, BTM_SUCCESS, TRUE); + } else if (p_dev_rec->role_master) { + btm_sec_dev_rec_cback_event(p_dev_rec, BTM_ERR_PROCESSING, TRUE); + } + + } + /* to notify GATT to send data if any request is pending */ + gatt_notify_enc_cmpl(p_dev_rec->ble.pseudo_addr); +} +#endif ///SMP_INCLUDED == TRUE + + +/******************************************************************************* +** +** Function btm_ble_ltk_request_reply +** +** Description This function is called to send a LTK request reply on a slave +** device. +** +** Returns void +** +*******************************************************************************/ +#if (SMP_INCLUDED == TRUE) +void btm_ble_ltk_request_reply(BD_ADDR bda, BOOLEAN use_stk, BT_OCTET16 stk) +{ + tBTM_SEC_DEV_REC *p_rec = btm_find_dev (bda); + tBTM_CB *p_cb = &btm_cb; + + if (p_rec == NULL) { + BTM_TRACE_ERROR("btm_ble_ltk_request_reply received for unknown device"); + return; + } + + BTM_TRACE_DEBUG ("btm_ble_ltk_request_reply"); + p_cb->enc_handle = p_rec->ble_hci_handle; + p_cb->key_size = p_rec->ble.keys.key_size; + + BTM_TRACE_DEBUG("key size = %d", p_rec->ble.keys.key_size); + if (use_stk) { + btsnd_hcic_ble_ltk_req_reply(btm_cb.enc_handle, stk); + } else { /* calculate LTK using peer device */ + if (p_rec->ble.key_type & BTM_LE_KEY_LENC) { + btsnd_hcic_ble_ltk_req_reply(btm_cb.enc_handle, p_rec->ble.keys.lltk); + } else { + btsnd_hcic_ble_ltk_req_neg_reply(btm_cb.enc_handle); + } + } +} + +/******************************************************************************* +** +** Function btm_ble_io_capabilities_req +** +** Description This function is called to handle SMP get IO capability request. +** +** Returns void +** +*******************************************************************************/ +UINT8 btm_ble_io_capabilities_req(tBTM_SEC_DEV_REC *p_dev_rec, tBTM_LE_IO_REQ *p_data) +{ + UINT8 callback_rc = BTM_SUCCESS; + BTM_TRACE_DEBUG ("btm_ble_io_capabilities_req"); + if (btm_cb.api.p_le_callback) { + /* the callback function implementation may change the IO capability... */ + callback_rc = (*btm_cb.api.p_le_callback) (BTM_LE_IO_REQ_EVT, p_dev_rec->bd_addr, (tBTM_LE_EVT_DATA *)p_data); + } +#if BTM_OOB_INCLUDED == TRUE + if ((callback_rc == BTM_SUCCESS) || (BTM_OOB_UNKNOWN != p_data->oob_data)) +#else + if (callback_rc == BTM_SUCCESS) +#endif + { +#if BTM_BLE_CONFORMANCE_TESTING == TRUE + if (btm_cb.devcb.keep_rfu_in_auth_req) { + BTM_TRACE_DEBUG ("btm_ble_io_capabilities_req keep_rfu_in_auth_req = %u", + btm_cb.devcb.keep_rfu_in_auth_req); + p_data->auth_req &= BTM_LE_AUTH_REQ_MASK_KEEP_RFU; + btm_cb.devcb.keep_rfu_in_auth_req = FALSE; + } else { + /* default */ + p_data->auth_req &= BTM_LE_AUTH_REQ_MASK; + } +#else + p_data->auth_req &= BTM_LE_AUTH_REQ_MASK; +#endif + + BTM_TRACE_DEBUG ("btm_ble_io_capabilities_req 1: p_dev_rec->security_required = %d auth_req:%d", + p_dev_rec->security_required, p_data->auth_req); + BTM_TRACE_DEBUG ("btm_ble_io_capabilities_req 2: i_keys=0x%x r_keys=0x%x (bit 0-LTK 1-IRK 2-CSRK)", + p_data->init_keys, + p_data->resp_keys); + + /* if authentication requires MITM protection, put on the mask */ + if (p_dev_rec->security_required & BTM_SEC_IN_MITM) { + p_data->auth_req |= BTM_LE_AUTH_REQ_MITM; + } + + if (!(p_data->auth_req & SMP_AUTH_BOND)) { + BTM_TRACE_DEBUG("Non bonding: No keys should be exchanged"); + p_data->init_keys = 0; + p_data->resp_keys = 0; + } + + BTM_TRACE_DEBUG ("btm_ble_io_capabilities_req 3: auth_req:%d\n", p_data->auth_req); + BTM_TRACE_DEBUG ("btm_ble_io_capabilities_req 4: i_keys=0x%x r_keys=0x%x\n", + p_data->init_keys, + p_data->resp_keys); + + BTM_TRACE_DEBUG ("btm_ble_io_capabilities_req 5: p_data->io_cap = %d auth_req:%d\n", + p_data->io_cap, p_data->auth_req); + + /* remove MITM protection requirement if IO cap does not allow it */ + if ((p_data->io_cap == BTM_IO_CAP_NONE) && p_data->oob_data == SMP_OOB_NONE) { + p_data->auth_req &= ~BTM_LE_AUTH_REQ_MITM; + } + + if (!(p_data->auth_req & SMP_SC_SUPPORT_BIT)) { + /* if Secure Connections are not supported then remove LK derivation, + ** and keypress notifications. + */ + BTM_TRACE_DEBUG("%s-SC not supported -> No LK derivation, no keypress notifications", + __func__); + p_data->auth_req &= ~SMP_KP_SUPPORT_BIT; + p_data->init_keys &= ~SMP_SEC_KEY_TYPE_LK; + p_data->resp_keys &= ~SMP_SEC_KEY_TYPE_LK; + } + + BTM_TRACE_DEBUG ("btm_ble_io_capabilities_req 6: IO_CAP:%d oob_data:%d auth_req:0x%02x\n", + p_data->io_cap, p_data->oob_data, p_data->auth_req); + } + return callback_rc; +} +#endif ///SMP_INCLUDED == TRUE + + +/******************************************************************************* +** +** Function btm_ble_br_keys_req +** +** Description This function is called to handle SMP request for keys sent +** over BR/EDR. +** +** Returns void +** +*******************************************************************************/ +#if (SMP_INCLUDED == TRUE) +UINT8 btm_ble_br_keys_req(tBTM_SEC_DEV_REC *p_dev_rec, tBTM_LE_IO_REQ *p_data) +{ + UINT8 callback_rc = BTM_SUCCESS; + BTM_TRACE_DEBUG ("%s\n", __func__); + if (btm_cb.api.p_le_callback) { + /* the callback function implementation may change the IO capability... */ + callback_rc = (*btm_cb.api.p_le_callback) (BTM_LE_IO_REQ_EVT, p_dev_rec->bd_addr, + (tBTM_LE_EVT_DATA *)p_data); + } + return callback_rc; +} +#endif ///SMP_INCLUDED + + +#if (BLE_PRIVACY_SPT == TRUE ) +/******************************************************************************* +** +** Function btm_ble_resolve_random_addr_on_conn_cmpl +** +** Description resolve random address complete on connection complete event. +** +** Returns void +** +*******************************************************************************/ +static void btm_ble_resolve_random_addr_on_conn_cmpl(void *p_rec, void *p_data) +{ + UINT8 *p = (UINT8 *)p_data; + tBTM_SEC_DEV_REC *match_rec = (tBTM_SEC_DEV_REC *) p_rec; + UINT8 role, bda_type; + UINT16 handle; + BD_ADDR bda, local_rpa, peer_rpa; + UINT16 conn_interval, conn_latency, conn_timeout; + BOOLEAN match = FALSE; + + ++p; + STREAM_TO_UINT16 (handle, p); + STREAM_TO_UINT8 (role, p); + STREAM_TO_UINT8 (bda_type, p); + STREAM_TO_BDADDR (bda, p); + // if the enhanced is true, means the connection is enhanced connect, + // so the packet should include the local Resolvable Private Address and Peer Resolvable Private Address + if(temp_enhanced) { + STREAM_TO_BDADDR(local_rpa, p); + STREAM_TO_BDADDR(peer_rpa, p); + } + STREAM_TO_UINT16 (conn_interval, p); + STREAM_TO_UINT16 (conn_latency, p); + STREAM_TO_UINT16 (conn_timeout, p); + + handle = HCID_GET_HANDLE (handle); + BTM_TRACE_EVENT ("%s\n", __func__); + + if (match_rec) { + BTM_TRACE_DEBUG("%s matched and resolved random address", __func__); + match = TRUE; + match_rec->ble.active_addr_type = BTM_BLE_ADDR_RRA; + memcpy(match_rec->ble.cur_rand_addr, bda, BD_ADDR_LEN); + if (!btm_ble_init_pseudo_addr (match_rec, bda)) { + /* assign the original address to be the current report address */ + memcpy(bda, match_rec->ble.pseudo_addr, BD_ADDR_LEN); + } else { + memcpy(bda, match_rec->bd_addr, BD_ADDR_LEN); + } + } else { + BTM_TRACE_DEBUG("%s unable to match and resolve random address", __func__); + } + + btm_ble_connected(bda, handle, HCI_ENCRYPT_MODE_DISABLED, role, bda_type, match); + + l2cble_conn_comp (handle, role, bda, bda_type, conn_interval, + conn_latency, conn_timeout); + + return; +} +#endif + +/******************************************************************************* +** +** Function btm_ble_connected +** +** Description This function is when a LE connection to the peer device is +** establsihed +** +** Returns void +** +*******************************************************************************/ +void btm_ble_connected (UINT8 *bda, UINT16 handle, UINT8 enc_mode, UINT8 role, + tBLE_ADDR_TYPE addr_type, BOOLEAN addr_matched) +{ + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev (bda); + tBTM_BLE_CB *p_cb = &btm_cb.ble_ctr_cb; + UNUSED(addr_matched); + + BTM_TRACE_EVENT ("btm_ble_connected"); + + /* Commenting out trace due to obf/compilation problems. + */ +#if (BT_USE_TRACES == TRUE) + if (p_dev_rec) { + BTM_TRACE_EVENT ("Security Manager: btm_ble_connected : handle:%d enc_mode:%d bda:%x RName:%s", + handle, enc_mode, + (bda[2] << 24) + (bda[3] << 16) + (bda[4] << 8) + bda[5], + p_dev_rec->sec_bd_name); + + BTM_TRACE_DEBUG ("btm_ble_connected sec_flags=0x%x", p_dev_rec->sec_flags); + } else { + BTM_TRACE_EVENT ("Security Manager: btm_ble_connected: handle:%d enc_mode:%d bda:%x ", + handle, enc_mode, + (bda[2] << 24) + (bda[3] << 16) + (bda[4] << 8) + bda[5]); + } +#endif + + if (!p_dev_rec) { + /* There is no device record for new connection. Allocate one */ + if ((p_dev_rec = btm_sec_alloc_dev (bda)) == NULL) { + return; + } + } else { /* Update the timestamp for this device */ + p_dev_rec->timestamp = btm_cb.dev_rec_count++; + } + + /* update device information */ + p_dev_rec->device_type |= BT_DEVICE_TYPE_BLE; + p_dev_rec->ble_hci_handle = handle; + p_dev_rec->ble.ble_addr_type = addr_type; + /* update pseudo address */ + memcpy(p_dev_rec->ble.pseudo_addr, bda, BD_ADDR_LEN); + + p_dev_rec->role_master = FALSE; + if (role == HCI_ROLE_MASTER) { + p_dev_rec->role_master = TRUE; + } + +#if (defined BLE_PRIVACY_SPT && BLE_PRIVACY_SPT == TRUE) + if (!addr_matched) { + p_dev_rec->ble.active_addr_type = BTM_BLE_ADDR_PSEUDO; + } + + if (p_dev_rec->ble.ble_addr_type == BLE_ADDR_RANDOM && !addr_matched) { + memcpy(p_dev_rec->ble.cur_rand_addr, bda, BD_ADDR_LEN); + } +#endif + + p_cb->inq_var.directed_conn = BTM_BLE_CONNECT_EVT; + + return; +} + +/***************************************************************************** +** Function btm_ble_conn_complete +** +** Description LE connection complete. +** +******************************************************************************/ +void btm_ble_conn_complete(UINT8 *p, UINT16 evt_len, BOOLEAN enhanced) +{ +#if (BLE_PRIVACY_SPT == TRUE ) + UINT8 *p_data = p, peer_addr_type; +#endif ///BLE_PRIVACY_SPT == TRUE + UINT8 role, status, bda_type; + UINT16 handle; + BD_ADDR bda; + BD_ADDR local_rpa, peer_rpa; + UINT16 conn_interval, conn_latency, conn_timeout; + BOOLEAN match = FALSE; + UNUSED(evt_len); + STREAM_TO_UINT8 (status, p); + STREAM_TO_UINT16 (handle, p); + STREAM_TO_UINT8 (role, p); + STREAM_TO_UINT8 (bda_type, p); + STREAM_TO_BDADDR (bda, p); + BTM_TRACE_DEBUG("status = %d, handle = %d, role = %d, bda_type = %d",status,handle,role,bda_type); + if (status == 0) { + if (enhanced) { + STREAM_TO_BDADDR (local_rpa, p); + STREAM_TO_BDADDR (peer_rpa, p); +#if (CONTROLLER_RPA_LIST_ENABLE == TRUE) + BD_ADDR dummy_bda = {0}; + /* For controller generates RPA, if resolving list contains no matching entry, it use identity address. + * So we should update own addr type in Host */ + if (memcmp(local_rpa, dummy_bda, BD_ADDR_LEN)) { + btm_cb.ble_ctr_cb.addr_mgnt_cb.own_addr_type |= (BLE_ADDR_TYPE_ID_BIT); + BTM_UpdateAddrInfor(btm_cb.ble_ctr_cb.addr_mgnt_cb.own_addr_type, local_rpa); + } else { + btm_cb.ble_ctr_cb.addr_mgnt_cb.own_addr_type &= (~BLE_ADDR_TYPE_ID_BIT); + } +#endif + } +#if (BLE_PRIVACY_SPT == TRUE ) + peer_addr_type = bda_type; + match = btm_identity_addr_to_random_pseudo (bda, &bda_type, FALSE); + + /* possiblly receive connection complete with resolvable random on + slave role while the device has been paired */ + + /* It will cause that scanner doesn't send scan request to advertiser + * which has sent IRK to us and we have stored the IRK in controller. + * It is a hardware limitation. The preliminary solution is not to + * send key to the controller, but to resolve the random address in host. + * so we need send the real address information to controller to connect. + * Once the connection is successful, resolve device address whether it is + * slave or master*/ + +#if CONTROLLER_RPA_LIST_ENABLE + if (!match && role == HCI_ROLE_SLAVE && bda_type != BLE_ADDR_PUBLIC && BTM_BLE_IS_RESOLVE_BDA(bda)) { +#else + if (!match && bda_type != BLE_ADDR_PUBLIC && BTM_BLE_IS_RESOLVE_BDA(bda)) { +#endif + // save the enhanced value to used in btm_ble_resolve_random_addr_on_conn_cmpl func. + temp_enhanced = enhanced; + btm_ble_resolve_random_addr(bda, btm_ble_resolve_random_addr_on_conn_cmpl, p_data); + // set back the temp enhanced to default after used. + temp_enhanced = FALSE; + } else +#endif + { + STREAM_TO_UINT16 (conn_interval, p); + STREAM_TO_UINT16 (conn_latency, p); + STREAM_TO_UINT16 (conn_timeout, p); + handle = HCID_GET_HANDLE (handle); + + btm_ble_connected(bda, handle, HCI_ENCRYPT_MODE_DISABLED, role, bda_type, match); + l2cble_conn_comp (handle, role, bda, bda_type, conn_interval, + conn_latency, conn_timeout); + +#if (BLE_PRIVACY_SPT == TRUE) + if (enhanced) { + btm_ble_refresh_local_resolvable_private_addr(bda, local_rpa); + + if (peer_addr_type & BLE_ADDR_TYPE_ID_BIT) { + btm_ble_refresh_peer_resolvable_private_addr(bda, peer_rpa, BLE_ADDR_RANDOM); + } + } +#endif + } + } else { + role = HCI_ROLE_UNKNOWN; + if (status != HCI_ERR_DIRECTED_ADVERTISING_TIMEOUT) { + btm_ble_set_conn_st(BLE_CONN_IDLE); +#if (defined BLE_PRIVACY_SPT && BLE_PRIVACY_SPT == TRUE) + btm_ble_disable_resolving_list(BTM_BLE_RL_INIT, TRUE); +#endif + } else { +#if (defined BLE_PRIVACY_SPT && BLE_PRIVACY_SPT == TRUE) + btm_cb.ble_ctr_cb.inq_var.adv_mode = BTM_BLE_ADV_DISABLE; + btm_ble_disable_resolving_list(BTM_BLE_RL_ADV, TRUE); +#endif + } + + } + + BOOLEAN bg_con = btm_ble_update_mode_operation(role, bda, status); + if (status != HCI_SUCCESS && !bg_con) { + // notify connection failed + l2c_link_hci_disc_comp (handle, status); +#if (SMP_INCLUDED == TRUE) + /* Notify security manager */ + btm_sec_disconnected (handle, status); +#endif ///SMP_INCLUDED == TRUE + } +} + + + +/***************************************************************************** +** Function btm_ble_create_ll_conn_complete +** +** Description LE connection complete. +** +******************************************************************************/ +void btm_ble_create_ll_conn_complete (UINT8 status) +{ + if (status != HCI_SUCCESS) { + btm_ble_set_conn_st(BLE_CONN_IDLE); + btm_ble_update_mode_operation(HCI_ROLE_UNKNOWN, NULL, status); + } +} + +/***************************************************************************** +** Function btm_ble_create_conn_cancel_complete +** +** Description LE connection cancel complete. +** +******************************************************************************/ +void btm_ble_create_conn_cancel_complete (UINT8 *p) +{ + UINT8 status; + + STREAM_TO_UINT8 (status, p); + + switch (status) { + case HCI_SUCCESS: + if (btm_ble_get_conn_st() == BLE_CONN_CANCEL) { + btm_ble_set_conn_st (BLE_CONN_IDLE); + } + break; + default: + break; + } +} + +/***************************************************************************** +** Function btm_proc_smp_cback +** +** Description This function is the SMP callback handler. +** +******************************************************************************/ +#if (SMP_INCLUDED == TRUE) +UINT8 btm_proc_smp_cback(tSMP_EVT event, BD_ADDR bd_addr, tSMP_EVT_DATA *p_data) +{ + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev (bd_addr); + UINT8 res = 0; + + BTM_TRACE_DEBUG ("btm_proc_smp_cback event = %d", event); + + if (p_dev_rec != NULL) { + switch (event) { + case SMP_IO_CAP_REQ_EVT: + btm_ble_io_capabilities_req(p_dev_rec, (tBTM_LE_IO_REQ *)&p_data->io_req); + break; + + case SMP_BR_KEYS_REQ_EVT: + btm_ble_br_keys_req(p_dev_rec, (tBTM_LE_IO_REQ *)&p_data->io_req); + break; + + case SMP_PASSKEY_REQ_EVT: + case SMP_PASSKEY_NOTIF_EVT: + case SMP_OOB_REQ_EVT: + case SMP_NC_REQ_EVT: + case SMP_SC_OOB_REQ_EVT: + /* fall through */ + p_dev_rec->sec_flags |= BTM_SEC_LE_AUTHENTICATED; + + case SMP_SEC_REQUEST_EVT: + if (event == SMP_SEC_REQUEST_EVT && btm_cb.pairing_state != BTM_PAIR_STATE_IDLE) { + BTM_TRACE_DEBUG("%s: Ignoring SMP Security request", __func__); + break; + } + memcpy (btm_cb.pairing_bda, bd_addr, BD_ADDR_LEN); + p_dev_rec->sec_state = BTM_SEC_STATE_AUTHENTICATING; + btm_cb.pairing_flags |= BTM_PAIR_FLAGS_LE_ACTIVE; + /* fall through */ + + case SMP_COMPLT_EVT: + if (btm_cb.api.p_le_callback) { + /* the callback function implementation may change the IO capability... */ + BTM_TRACE_DEBUG ("btm_cb.api.p_le_callback=%p", btm_cb.api.p_le_callback ); + (*btm_cb.api.p_le_callback) (event, bd_addr, (tBTM_LE_EVT_DATA *)p_data); + } + + if (event == SMP_COMPLT_EVT) { + BTM_TRACE_DEBUG ("evt=SMP_COMPLT_EVT before update sec_level=0x%x sec_flags=0x%x", p_data->cmplt.sec_level , p_dev_rec->sec_flags ); + + res = (p_data->cmplt.reason == SMP_SUCCESS) ? BTM_SUCCESS : BTM_ERR_PROCESSING; + + BTM_TRACE_DEBUG ("after update result=%d sec_level=0x%x sec_flags=0x%x", + res, p_data->cmplt.sec_level , p_dev_rec->sec_flags ); + + if (p_data->cmplt.is_pair_cancel && btm_cb.api.p_bond_cancel_cmpl_callback ) { + BTM_TRACE_DEBUG ("Pairing Cancel completed"); + (*btm_cb.api.p_bond_cancel_cmpl_callback)(BTM_SUCCESS); + } +#if BTM_BLE_CONFORMANCE_TESTING == TRUE + if (res != BTM_SUCCESS) { + if (!btm_cb.devcb.no_disc_if_pair_fail && p_data->cmplt.reason != SMP_CONN_TOUT) { + BTM_TRACE_DEBUG ("Pairing failed - prepare to remove ACL"); + l2cu_start_post_bond_timer(p_dev_rec->ble_hci_handle); + } else { + BTM_TRACE_DEBUG ("Pairing failed - Not Removing ACL"); + p_dev_rec->sec_state = BTM_SEC_STATE_IDLE; + } + } +#else + if (res != BTM_SUCCESS && p_data->cmplt.reason != SMP_CONN_TOUT) { + BTM_TRACE_DEBUG ("Pairing failed - prepare to remove ACL"); + l2cu_start_post_bond_timer(p_dev_rec->ble_hci_handle); + } +#endif + + BTM_TRACE_DEBUG ("btm_cb pairing_state=%x pairing_flags=%x", + btm_cb.pairing_state, + btm_cb.pairing_flags); + BTM_TRACE_DEBUG ("btm_cb.pairing_bda %02x:%02x:%02x:%02x:%02x:%02x", + btm_cb.pairing_bda[0], btm_cb.pairing_bda[1], btm_cb.pairing_bda[2], + btm_cb.pairing_bda[3], btm_cb.pairing_bda[4], btm_cb.pairing_bda[5]); + + /* Reset btm state only if the callback address matches pairing address*/ + if (memcmp(bd_addr, btm_cb.pairing_bda, BD_ADDR_LEN) == 0) { + memset (btm_cb.pairing_bda, 0xff, BD_ADDR_LEN); + btm_cb.pairing_state = BTM_PAIR_STATE_IDLE; + btm_cb.pairing_flags = 0; + } + + if (res == BTM_SUCCESS) { + p_dev_rec->sec_state = BTM_SEC_STATE_IDLE; +#if (defined BLE_PRIVACY_SPT && BLE_PRIVACY_SPT == TRUE) + /* add all bonded device into resolving list if IRK is available*/ + btm_ble_resolving_list_load_dev(p_dev_rec); +#endif + } + + btm_sec_dev_rec_cback_event(p_dev_rec, res, TRUE); + } + break; + + default: + BTM_TRACE_DEBUG ("unknown event = %d", event); + break; + + + } + } else { + if (event == SMP_SC_LOC_OOB_DATA_UP_EVT) { + tBTM_LE_EVT_DATA evt_data; + memcpy(&evt_data.local_oob_data, &p_data->loc_oob_data, sizeof(tSMP_LOC_OOB_DATA)); + if (btm_cb.api.p_le_callback) { + (*btm_cb.api.p_le_callback)(event, bd_addr, &evt_data); + } + } else { + BTM_TRACE_ERROR("btm_proc_smp_cback received for unknown device"); + } + } + return 0; +} +#endif ///SMP_INCLUDED == TRUE + + +/******************************************************************************* +** +** Function BTM_BleDataSignature +** +** Description This function is called to sign the data using AES128 CMAC +** algorith. +** +** Parameter bd_addr: target device the data to be signed for. +** p_text: singing data +** len: length of the data to be signed. +** signature: output parameter where data signature is going to +** be stored. +** +** Returns TRUE if signing sucessul, otherwise FALSE. +** +*******************************************************************************/ +#if (SMP_INCLUDED == TRUE) +BOOLEAN BTM_BleDataSignature (BD_ADDR bd_addr, UINT8 *p_text, UINT16 len, + BLE_SIGNATURE signature) +{ + tBTM_SEC_DEV_REC *p_rec = btm_find_dev (bd_addr); + + BTM_TRACE_DEBUG ("%s", __func__); +#endif ///SMP_INCLUDED == TRUE + BOOLEAN ret = FALSE; +#if (SMP_INCLUDED == TRUE) + if (p_rec == NULL) { + BTM_TRACE_ERROR("%s-data signing can not be done from unknown device", __func__); + } else { + UINT8 *p_mac = (UINT8 *)signature; + UINT8 *p_buf, *pp; + if ((p_buf = (UINT8 *)osi_malloc((UINT16)(len + 4))) != NULL) { + BTM_TRACE_DEBUG("%s-Start to generate Local CSRK", __func__); + pp = p_buf; + /* prepare plain text */ + if (p_text) { + memcpy(p_buf, p_text, len); + pp = (p_buf + len); + } + + UINT32_TO_STREAM(pp, p_rec->ble.keys.local_counter); + UINT32_TO_STREAM(p_mac, p_rec->ble.keys.local_counter); + + if ((ret = aes_cipher_msg_auth_code(p_rec->ble.keys.lcsrk, p_buf, (UINT16)(len + 4), + BTM_CMAC_TLEN_SIZE, p_mac)) == TRUE) { + btm_ble_increment_sign_ctr(bd_addr, TRUE); + } + + BTM_TRACE_DEBUG("%s p_mac = %p", __func__, p_mac); + BTM_TRACE_DEBUG("p_mac[0] = 0x%02x p_mac[1] = 0x%02x p_mac[2] = 0x%02x p_mac[3] = 0x%02x", + *p_mac, *(p_mac + 1), *(p_mac + 2), *(p_mac + 3)); + BTM_TRACE_DEBUG("p_mac[4] = 0x%02x p_mac[5] = 0x%02x p_mac[6] = 0x%02x p_mac[7] = 0x%02x", + *(p_mac + 4), *(p_mac + 5), *(p_mac + 6), *(p_mac + 7)); + osi_free(p_buf); + } + } + return ret; +} + +/******************************************************************************* +** +** Function BTM_BleVerifySignature +** +** Description This function is called to verify the data signature +** +** Parameter bd_addr: target device the data to be signed for. +** p_orig: original data before signature. +** len: length of the signing data +** counter: counter used when doing data signing +** p_comp: signature to be compared against. + +** Returns TRUE if signature verified correctly; otherwise FALSE. +** +*******************************************************************************/ +BOOLEAN BTM_BleVerifySignature (BD_ADDR bd_addr, UINT8 *p_orig, UINT16 len, UINT32 counter, + UINT8 *p_comp) +{ + BOOLEAN verified = FALSE; + tBTM_SEC_DEV_REC *p_rec = btm_find_dev (bd_addr); + UINT8 p_mac[BTM_CMAC_TLEN_SIZE]; + + if (p_rec == NULL || (p_rec && !(p_rec->ble.key_type & BTM_LE_KEY_PCSRK))) { + BTM_TRACE_ERROR("can not verify signature for unknown device"); + } else if (counter < p_rec->ble.keys.counter) { + BTM_TRACE_ERROR("signature received with out dated sign counter"); + } else if (p_orig == NULL) { + BTM_TRACE_ERROR("No signature to verify"); + } else { + BTM_TRACE_DEBUG ("%s rcv_cnt=%d >= expected_cnt=%d", __func__, counter, + p_rec->ble.keys.counter); + + if (aes_cipher_msg_auth_code(p_rec->ble.keys.pcsrk, p_orig, len, BTM_CMAC_TLEN_SIZE, p_mac)) { + if (memcmp(p_mac, p_comp, BTM_CMAC_TLEN_SIZE) == 0) { + btm_ble_increment_sign_ctr(bd_addr, FALSE); + verified = TRUE; + } + } + } + return verified; +} +#endif /* SMP_INCLUDED */ + + +/******************************************************************************* +** +** Function BTM_GetLeSecurityState +** +** Description This function is called to get security mode 1 flags and +** encryption key size for LE peer. +** +** Returns BOOLEAN TRUE if LE device is found, FALSE otherwise. +** +*******************************************************************************/ +BOOLEAN BTM_GetLeSecurityState (BD_ADDR bd_addr, UINT8 *p_le_dev_sec_flags, UINT8 *p_le_key_size) +{ +#if (BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE) + tBTM_SEC_DEV_REC *p_dev_rec; + UINT16 dev_rec_sec_flags; +#endif + + *p_le_dev_sec_flags = 0; + *p_le_key_size = 0; + +#if (BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE) + if ((p_dev_rec = btm_find_dev (bd_addr)) == NULL) { + BTM_TRACE_ERROR ("%s fails", __func__); + return (FALSE); + } + + if (p_dev_rec->ble_hci_handle == BTM_SEC_INVALID_HANDLE) { + BTM_TRACE_ERROR ("%s-this is not LE device", __func__); + return (FALSE); + } + + dev_rec_sec_flags = p_dev_rec->sec_flags; + + if (dev_rec_sec_flags & BTM_SEC_LE_ENCRYPTED) { + /* link is encrypted with LTK or STK */ + *p_le_key_size = p_dev_rec->enc_key_size; + *p_le_dev_sec_flags |= BTM_SEC_LE_LINK_ENCRYPTED; + + *p_le_dev_sec_flags |= (dev_rec_sec_flags & BTM_SEC_LE_AUTHENTICATED) + ? BTM_SEC_LE_LINK_PAIRED_WITH_MITM /* set auth LTK flag */ + : BTM_SEC_LE_LINK_PAIRED_WITHOUT_MITM; /* set unauth LTK flag */ + } else if (p_dev_rec->ble.key_type & BTM_LE_KEY_PENC) { + /* link is unencrypted, still LTK is available */ + *p_le_key_size = p_dev_rec->ble.keys.key_size; + + *p_le_dev_sec_flags |= (dev_rec_sec_flags & BTM_SEC_LE_LINK_KEY_AUTHED) + ? BTM_SEC_LE_LINK_PAIRED_WITH_MITM /* set auth LTK flag */ + : BTM_SEC_LE_LINK_PAIRED_WITHOUT_MITM; /* set unauth LTK flag */ + } + + BTM_TRACE_DEBUG ("%s - le_dev_sec_flags: 0x%02x, le_key_size: %d", + __func__, *p_le_dev_sec_flags, *p_le_key_size); + + return TRUE; +#else + return FALSE; +#endif +} + +/******************************************************************************* +** +** Function BTM_BleSecurityProcedureIsRunning +** +** Description This function indicates if LE security procedure is +** currently running with the peer. +** +** Returns BOOLEAN TRUE if security procedure is running, FALSE otherwise. +** +*******************************************************************************/ +BOOLEAN BTM_BleSecurityProcedureIsRunning(BD_ADDR bd_addr) +{ +#if (BLE_INCLUDED == TRUE) + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev (bd_addr); + + if (p_dev_rec == NULL) { + BTM_TRACE_ERROR ("%s device with BDA: %08x%04x is not found", + __func__, (bd_addr[0] << 24) + (bd_addr[1] << 16) + (bd_addr[2] << 8) + bd_addr[3], + (bd_addr[4] << 8) + bd_addr[5]); + return FALSE; + } + + return (p_dev_rec->sec_state == BTM_SEC_STATE_ENCRYPTING || + p_dev_rec->sec_state == BTM_SEC_STATE_AUTHENTICATING); +#else + return FALSE; +#endif +} + +/******************************************************************************* +** +** Function BTM_BleGetSupportedKeySize +** +** Description This function gets the maximum encryption key size in bytes +** the local device can suport. +** record. +** +** Returns the key size or 0 if the size can't be retrieved. +** +*******************************************************************************/ +extern UINT8 BTM_BleGetSupportedKeySize (BD_ADDR bd_addr) +{ +#ifndef L2CAP_LE_COC_INCLUDED +#define L2CAP_LE_COC_INCLUDED FALSE +#endif +#if ((BLE_INCLUDED == TRUE) && (L2CAP_LE_COC_INCLUDED == TRUE)) + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev (bd_addr); + tBTM_LE_IO_REQ dev_io_cfg; + UINT8 callback_rc; + + if (!p_dev_rec) { + BTM_TRACE_ERROR ("%s device with BDA: %08x%04x is not found", + __func__, (bd_addr[0] << 24) + (bd_addr[1] << 16) + (bd_addr[2] << 8) + bd_addr[3], + (bd_addr[4] << 8) + bd_addr[5]); + return 0; + } + + if (btm_cb.api.p_le_callback == NULL) { + BTM_TRACE_ERROR ("%s can't access supported key size", __func__); + return 0; + } + + callback_rc = (*btm_cb.api.p_le_callback) (BTM_LE_IO_REQ_EVT, p_dev_rec->bd_addr, + (tBTM_LE_EVT_DATA *) &dev_io_cfg); + + if (callback_rc != BTM_SUCCESS) { + BTM_TRACE_ERROR ("%s can't access supported key size", __func__); + return 0; + } + + BTM_TRACE_DEBUG ("%s device supports key size = %d", __func__, dev_io_cfg.max_key_size); + return (dev_io_cfg.max_key_size); +#else + return 0; +#endif +} + +/******************************************************************************* +** Utility functions for LE device IR/ER generation +*******************************************************************************/ +/******************************************************************************* +** +** Function btm_notify_new_key +** +** Description This function is to notify application new keys have been +** generated. +** +** Returns void +** +*******************************************************************************/ +#if (SMP_INCLUDED == TRUE) +static void btm_notify_new_key(UINT8 key_type) +{ + tBTM_BLE_LOCAL_KEYS *p_locak_keys = NULL; + + BTM_TRACE_DEBUG ("btm_notify_new_key key_type=%d", key_type); + + if (btm_cb.api.p_le_key_callback) { + switch (key_type) { + case BTM_BLE_KEY_TYPE_ID: + BTM_TRACE_DEBUG ("BTM_BLE_KEY_TYPE_ID"); + p_locak_keys = (tBTM_BLE_LOCAL_KEYS *)&btm_cb.devcb.id_keys; + break; + + case BTM_BLE_KEY_TYPE_ER: + BTM_TRACE_DEBUG ("BTM_BLE_KEY_TYPE_ER"); + p_locak_keys = (tBTM_BLE_LOCAL_KEYS *)&btm_cb.devcb.ble_encryption_key_value; + break; + + default: + BTM_TRACE_ERROR("unknown key type: %d", key_type); + break; + } + if (p_locak_keys != NULL) { + (*btm_cb.api.p_le_key_callback) (key_type, p_locak_keys); + } + } +} + +/******************************************************************************* +** +** Function btm_ble_process_er2 +** +** Description This function is called when ER is generated, store it in +** local control block. +** +** Returns void +** +*******************************************************************************/ +static void btm_ble_process_er2(tBTM_RAND_ENC *p) +{ + BTM_TRACE_DEBUG ("btm_ble_process_er2"); + + if (p && p->opcode == HCI_BLE_RAND) { + memcpy(&btm_cb.devcb.ble_encryption_key_value[8], p->param_buf, BT_OCTET8_LEN); + btm_notify_new_key(BTM_BLE_KEY_TYPE_ER); + } else { + BTM_TRACE_ERROR("Generating ER2 exception."); + memset(&btm_cb.devcb.ble_encryption_key_value, 0, sizeof(BT_OCTET16)); + } +} + +/******************************************************************************* +** +** Function btm_ble_process_er +** +** Description This function is called when ER is generated, store it in +** local control block. +** +** Returns void +** +*******************************************************************************/ +static void btm_ble_process_er(tBTM_RAND_ENC *p) +{ + BTM_TRACE_DEBUG ("btm_ble_process_er"); + + if (p && p->opcode == HCI_BLE_RAND) { + memcpy(&btm_cb.devcb.ble_encryption_key_value[0], p->param_buf, BT_OCTET8_LEN); + + if (!btsnd_hcic_ble_rand((void *)btm_ble_process_er2)) { + memset(&btm_cb.devcb.ble_encryption_key_value, 0, sizeof(BT_OCTET16)); + BTM_TRACE_ERROR("Generating ER2 failed."); + } + } else { + BTM_TRACE_ERROR("Generating ER1 exception."); + } +} + +/******************************************************************************* +** +** Function btm_ble_process_irk +** +** Description This function is called when IRK is generated, store it in +** local control block. +** +** Returns void +** +*******************************************************************************/ +static void btm_ble_process_irk(tSMP_ENC *p) +{ + BTM_TRACE_DEBUG ("btm_ble_process_irk"); + if (p && p->opcode == HCI_BLE_ENCRYPT) { + memcpy(btm_cb.devcb.id_keys.irk, p->param_buf, BT_OCTET16_LEN); + btm_notify_new_key(BTM_BLE_KEY_TYPE_ID); + +#if (CONTROLLER_RPA_LIST_ENABLE == TRUE) + btm_ble_add_default_entry_to_resolving_list(); +#endif + +#if (BLE_PRIVACY_SPT == TRUE) && (CONTROLLER_RPA_LIST_ENABLE == FALSE) + /* if privacy is enabled, new RPA should be calculated */ + if (btm_cb.ble_ctr_cb.privacy_mode != BTM_PRIVACY_NONE) { + btm_gen_resolvable_private_addr((void *)btm_gen_resolve_paddr_low); + } +#endif + } else { + BTM_TRACE_ERROR("Generating IRK exception."); + } + + /* proceed generate ER */ + if (!btsnd_hcic_ble_rand((void *)btm_ble_process_er)) { + BTM_TRACE_ERROR("Generating ER failed."); + } +} + + +/******************************************************************************* +** +** Function btm_ble_process_dhk +** +** Description This function is called when DHK is calculated, store it in +** local control block, and proceed to generate ER, a 128-bits +** random number. +** +** Returns void +** +*******************************************************************************/ +static void btm_ble_process_dhk(tSMP_ENC *p) +{ + UINT8 btm_ble_irk_pt = 0x01; + tSMP_ENC output; + + BTM_TRACE_DEBUG ("btm_ble_process_dhk"); + + if (p && p->opcode == HCI_BLE_ENCRYPT) { + memcpy(btm_cb.devcb.id_keys.dhk, p->param_buf, BT_OCTET16_LEN); + BTM_TRACE_DEBUG("BLE DHK generated."); + + /* IRK = D1(IR, 1) */ + if (!SMP_Encrypt(btm_cb.devcb.id_keys.ir, BT_OCTET16_LEN, &btm_ble_irk_pt, + 1, &output)) { + /* reset all identity root related key */ + memset(&btm_cb.devcb.id_keys, 0, sizeof(tBTM_BLE_LOCAL_ID_KEYS)); + } else { + btm_ble_process_irk(&output); + } + } else { + /* reset all identity root related key */ + memset(&btm_cb.devcb.id_keys, 0, sizeof(tBTM_BLE_LOCAL_ID_KEYS)); + } +} + +/******************************************************************************* +** +** Function btm_ble_process_ir2 +** +** Description This function is called when IR is generated, proceed to calculate +** DHK = Eir({0x03, 0, 0 ...}) +** +** +** Returns void +** +*******************************************************************************/ +static void btm_ble_process_ir2(tBTM_RAND_ENC *p) +{ + UINT8 btm_ble_dhk_pt = 0x03; + tSMP_ENC output; + + BTM_TRACE_DEBUG ("btm_ble_process_ir2"); + + if (p && p->opcode == HCI_BLE_RAND) { + /* remembering in control block */ + memcpy(&btm_cb.devcb.id_keys.ir[8], p->param_buf, BT_OCTET8_LEN); + /* generate DHK= Eir({0x03, 0x00, 0x00 ...}) */ + + + SMP_Encrypt(btm_cb.devcb.id_keys.ir, BT_OCTET16_LEN, &btm_ble_dhk_pt, + 1, &output); + btm_ble_process_dhk(&output); + + BTM_TRACE_DEBUG("BLE IR generated."); + } else { + memset(&btm_cb.devcb.id_keys, 0, sizeof(tBTM_BLE_LOCAL_ID_KEYS)); + } +} + +/******************************************************************************* +** +** Function btm_ble_process_ir +** +** Description This function is called when IR is generated, proceed to calculate +** DHK = Eir({0x02, 0, 0 ...}) +** +** +** Returns void +** +*******************************************************************************/ +static void btm_ble_process_ir(tBTM_RAND_ENC *p) +{ + BTM_TRACE_DEBUG ("btm_ble_process_ir"); + + if (p && p->opcode == HCI_BLE_RAND) { + /* remembering in control block */ + memcpy(btm_cb.devcb.id_keys.ir, p->param_buf, BT_OCTET8_LEN); + + if (!btsnd_hcic_ble_rand((void *)btm_ble_process_ir2)) { + BTM_TRACE_ERROR("Generating IR2 failed."); + memset(&btm_cb.devcb.id_keys, 0, sizeof(tBTM_BLE_LOCAL_ID_KEYS)); + } + } +} + +/******************************************************************************* +** +** Function btm_ble_reset_id +** +** Description This function is called to reset LE device identity. +** +** Returns void +** +*******************************************************************************/ +void btm_ble_reset_id( void ) +{ + BTM_TRACE_DEBUG ("btm_ble_reset_id"); + + /* regenrate Identity Root*/ + if (!btsnd_hcic_ble_rand((void *)btm_ble_process_ir)) { + BTM_TRACE_DEBUG("Generating IR failed."); + } +} +#endif ///SMP_INCLUDED == TRUE + +#if BTM_BLE_CONFORMANCE_TESTING == TRUE +/******************************************************************************* +** +** Function btm_ble_set_no_disc_if_pair_fail +** +** Description This function indicates that whether no disconnect of the ACL +** should be used if pairing failed +** +** Returns void +** +*******************************************************************************/ +void btm_ble_set_no_disc_if_pair_fail(BOOLEAN disable_disc ) +{ + BTM_TRACE_DEBUG ("btm_ble_set_disc_enable_if_pair_fail disable_disc=%d", disable_disc); + btm_cb.devcb.no_disc_if_pair_fail = disable_disc; +} + +/******************************************************************************* +** +** Function btm_ble_set_test_mac_value +** +** Description This function set test MAC value +** +** Returns void +** +*******************************************************************************/ +void btm_ble_set_test_mac_value(BOOLEAN enable, UINT8 *p_test_mac_val ) +{ + BTM_TRACE_DEBUG ("btm_ble_set_test_mac_value enable=%d", enable); + btm_cb.devcb.enable_test_mac_val = enable; + memcpy(btm_cb.devcb.test_mac, p_test_mac_val, BT_OCTET8_LEN); +} + +/******************************************************************************* +** +** Function btm_ble_set_test_local_sign_cntr_value +** +** Description This function set test local sign counter value +** +** Returns void +** +*******************************************************************************/ +void btm_ble_set_test_local_sign_cntr_value(BOOLEAN enable, UINT32 test_local_sign_cntr ) +{ + BTM_TRACE_DEBUG ("btm_ble_set_test_local_sign_cntr_value enable=%d local_sign_cntr=%d", + enable, test_local_sign_cntr); + btm_cb.devcb.enable_test_local_sign_cntr = enable; + btm_cb.devcb.test_local_sign_cntr = test_local_sign_cntr; +} + +/******************************************************************************* +** +** Function btm_set_random_address +** +** Description This function set a random address to local controller. +** +** Returns void +** +*******************************************************************************/ +void btm_set_random_address(BD_ADDR random_bda) +{ + tBTM_LE_RANDOM_CB *p_cb = &btm_cb.ble_ctr_cb.addr_mgnt_cb; + BOOLEAN adv_mode = btm_cb.ble_ctr_cb.inq_var.adv_mode ; + + BTM_TRACE_DEBUG ("btm_set_random_address"); + + if (adv_mode == BTM_BLE_ADV_ENABLE) { + btsnd_hcic_ble_set_adv_enable (BTM_BLE_ADV_DISABLE); + } + + memcpy(p_cb->private_addr, random_bda, BD_ADDR_LEN); + btsnd_hcic_ble_set_random_addr(p_cb->private_addr); + + if (adv_mode == BTM_BLE_ADV_ENABLE) { + btsnd_hcic_ble_set_adv_enable (BTM_BLE_ADV_ENABLE); + } + + +} + +/******************************************************************************* +** +** Function btm_ble_set_keep_rfu_in_auth_req +** +** Description This function indicates if RFU bits have to be kept as is +** (by default they have to be set to 0 by the sender). +** +** Returns void +** +*******************************************************************************/ +void btm_ble_set_keep_rfu_in_auth_req(BOOLEAN keep_rfu) +{ + BTM_TRACE_DEBUG ("btm_ble_set_keep_rfu_in_auth_req keep_rfus=%d", keep_rfu); + btm_cb.devcb.keep_rfu_in_auth_req = keep_rfu; +} + +#endif /* BTM_BLE_CONFORMANCE_TESTING */ + +/******************************************************************************* +** +** Function btm_get_current_conn_params +** +** Description This function is called to get current connection parameters +** information of the device +** +** Returns TRUE if the information is geted, else FALSE +** +*******************************************************************************/ + +BOOLEAN btm_get_current_conn_params(BD_ADDR bda, UINT16 *interval, UINT16 *latency, UINT16 *timeout) +{ + if( (interval == NULL) || (latency == NULL) || (timeout == NULL) ) { + BTM_TRACE_ERROR("%s invalid parameters ", __func__); + return FALSE; + } + + tL2C_LCB *p_lcb = l2cu_find_lcb_by_bd_addr(bda, BT_TRANSPORT_LE); + if(p_lcb != NULL) { + (*interval) = p_lcb->current_used_conn_interval; + (*latency) = p_lcb->current_used_conn_latency; + (*timeout) = p_lcb->current_used_conn_timeout; + return TRUE; + } + BTM_TRACE_WARNING("%s Device is not connected", __func__); + + return FALSE; +} + +uint8_t btm_ble_adv_active_count(void) +{ + uint8_t count = 0; + tBTM_BLE_INQ_CB *p_cb = &btm_cb.ble_ctr_cb.inq_var; + + if (p_cb->state & BTM_BLE_ADVERTISING) { + count++; + } + + return count; +} + +uint8_t btm_ble_scan_active_count(void) +{ + uint8_t count = 0; + tBTM_BLE_INQ_CB *p_cb = &btm_cb.ble_ctr_cb.inq_var; + + if (p_cb->state & BTM_BLE_SCANNING) { + count++; + } + + return count; +} + +#endif /* BLE_INCLUDED */ diff --git a/lib/bt/host/bluedroid/stack/btm/btm_ble_5_gap.c b/lib/bt/host/bluedroid/stack/btm/btm_ble_5_gap.c new file mode 100644 index 00000000..e5dc3b42 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/btm/btm_ble_5_gap.c @@ -0,0 +1,1455 @@ +/* + * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "btm_int.h" +#include "stack/hcimsgs.h" +#include "osi/allocator.h" +#include "device/controller.h" +#include +#include "l2c_int.h" +#if (BLE_50_FEATURE_SUPPORT == TRUE) +#define SET_BIT(t, n) (t |= 1UL << (n)) +tBTM_BLE_EXTENDED_CB extend_adv_cb; + +tBTM_BLE_5_HCI_CBACK ble_5_hci_cb; + +#define INVALID_VALUE 0XFF +extern BOOLEAN BTM_GetLocalResolvablePrivateAddr(BD_ADDR bda); +extern void BTM_UpdateAddrInfor(uint8_t addr_type, BD_ADDR bda); +extern void BTM_BleSetStaticAddr(BD_ADDR rand_addr); +extern uint32_t BTM_BleUpdateOwnType(uint8_t *own_bda_type, tBTM_START_ADV_CMPL_CBACK *cb); +static tBTM_STATUS btm_ble_ext_adv_params_validate(tBTM_BLE_GAP_EXT_ADV_PARAMS *params); +static tBTM_STATUS btm_ble_ext_adv_set_data_validate(UINT8 instance, UINT16 len, UINT8 *data); + +typedef struct { + uint16_t ter_con_handle; + bool invalid; + UINT8 instance; + int duration; + int max_events; + uint8_t retry_count; +} tBTM_EXT_ADV_RECORD; + +tBTM_EXT_ADV_RECORD adv_record[MAX_BLE_ADV_INSTANCE] = {0}; +extern void btm_ble_inter_set(bool extble_inter); + +#if !UC_BT_STACK_NO_LOG +static const char *btm_ble_hci_status_to_str(tHCI_STATUS status) +{ + switch(status) { + case HCI_SUCCESS: + return "HCI_SUCCESS"; + case HCI_ERR_ILLEGAL_COMMAND: + return "HCI_ERR_ILLEGAL_COMMAND"; + case HCI_ERR_NO_CONNECTION: + return "HCI_ERR_NO_CONNECTION"; + case HCI_ERR_HW_FAILURE: + return "HCI_ERR_HW_FAILURE"; + case HCI_ERR_PAGE_TIMEOUT: + return "HCI_ERR_PAGE_TIMEOUT"; + case HCI_ERR_AUTH_FAILURE: + return "HCI_ERR_AUTH_FAILURE"; + case HCI_ERR_KEY_MISSING: + return "HCI_ERR_KEY_MISSING"; + case HCI_ERR_MEMORY_FULL: + return "HCI_ERR_MEMORY_FULL"; + case HCI_ERR_CONNECTION_TOUT: + return "HCI_ERR_CONNECTION_TOUT"; + case HCI_ERR_MAX_NUM_OF_CONNECTIONS: + return "HCI_ERR_MAX_NUM_OF_CONNECTIONS"; + case HCI_ERR_MAX_NUM_OF_SCOS: + return "HCI_ERR_MAX_NUM_OF_SCOS"; + case HCI_ERR_CONNECTION_EXISTS: + return "HCI_ERR_CONNECTION_EXISTS"; + case HCI_ERR_COMMAND_DISALLOWED: + return "HCI_ERR_COMMAND_DISALLOWED"; + case HCI_ERR_HOST_REJECT_RESOURCES: + return "HCI_ERR_HOST_REJECT_RESOURCES"; + case HCI_ERR_HOST_REJECT_SECURITY: + return "HCI_ERR_HOST_REJECT_SECURITY"; + case HCI_ERR_HOST_REJECT_DEVICE: + return "HCI_ERR_HOST_REJECT_DEVICE"; + case HCI_ERR_HOST_TIMEOUT: + return "HCI_ERR_HOST_TIMEOUT"; + case HCI_ERR_UNSUPPORTED_VALUE: + return "HCI_ERR_UNSUPPORTED_VALUE"; + case HCI_ERR_ILLEGAL_PARAMETER_FMT: + return "HCI_ERR_ILLEGAL_PARAMETER_FMT"; + case HCI_ERR_PEER_USER: + return "HCI_ERR_PEER_USER"; + case HCI_ERR_PEER_LOW_RESOURCES: + return "HCI_ERR_PEER_LOW_RESOURCES"; + case HCI_ERR_PEER_POWER_OFF: + return "HCI_ERR_PEER_POWER_OFF"; + case HCI_ERR_CONN_CAUSE_LOCAL_HOST: + return "HCI_ERR_CONN_CAUSE_LOCAL_HOST"; + case HCI_ERR_REPEATED_ATTEMPTS: + return "HCI_ERR_REPEATED_ATTEMPTS"; + case HCI_ERR_PAIRING_NOT_ALLOWED: + return "HCI_ERR_PAIRING_NOT_ALLOWED"; + case HCI_ERR_UNKNOWN_LMP_PDU: + return "HCI_ERR_UNKNOWN_LMP_PDU"; + case HCI_ERR_UNSUPPORTED_REM_FEATURE: + return "HCI_ERR_UNSUPPORTED_REM_FEATURE"; + case HCI_ERR_SCO_OFFSET_REJECTED: + return "HCI_ERR_SCO_OFFSET_REJECTED"; + case HCI_ERR_SCO_INTERVAL_REJECTED: + return "HCI_ERR_SCO_INTERVAL_REJECTED"; + case HCI_ERR_SCO_AIR_MODE: + return "HCI_ERR_SCO_AIR_MODE"; + case HCI_ERR_INVALID_LMP_PARAM: + return "HCI_ERR_INVALID_LMP_PARAM"; + case HCI_ERR_UNSPECIFIED: + return "HCI_ERR_UNSPECIFIED"; + case HCI_ERR_UNSUPPORTED_LMP_PARAMETERS: + return "HCI_ERR_UNSUPPORTED_LMP_PARAMETERS"; + case HCI_ERR_ROLE_CHANGE_NOT_ALLOWED: + return "HCI_ERR_ROLE_CHANGE_NOT_ALLOWED"; + case HCI_ERR_LMP_RESPONSE_TIMEOUT: + return "HCI_ERR_LMP_RESPONSE_TIMEOUT"; + case HCI_ERR_LMP_ERR_TRANS_COLLISION: + return "HCI_ERR_LMP_ERR_TRANS_COLLISION"; + case HCI_ERR_LMP_PDU_NOT_ALLOWED: + return "HCI_ERR_LMP_PDU_NOT_ALLOWED"; + case HCI_ERR_ENCRY_MODE_NOT_ACCEPTABLE: + return "HCI_ERR_ENCRY_MODE_NOT_ACCEPTABLE"; + case HCI_ERR_UNIT_KEY_USED: + return "HCI_ERR_UNIT_KEY_USED"; + case HCI_ERR_QOS_NOT_SUPPORTED: + return "HCI_ERR_QOS_NOT_SUPPORTED"; + case HCI_ERR_INSTANT_PASSED: + return "HCI_ERR_INSTANT_PASSED"; + case HCI_ERR_PAIRING_WITH_UNIT_KEY_NOT_SUPPORTED: + return "HCI_ERR_PAIRING_WITH_UNIT_KEY_NOT_SUPPORTED"; + case HCI_ERR_DIFF_TRANSACTION_COLLISION: + return "HCI_ERR_DIFF_TRANSACTION_COLLISION"; + case HCI_ERR_UNDEFINED_0x2B: + return "HCI_ERR_UNDEFINED_0x2B"; + case HCI_ERR_QOS_UNACCEPTABLE_PARAM: + return "HCI_ERR_QOS_UNACCEPTABLE_PARAM"; + case HCI_ERR_QOS_REJECTED: + return "HCI_ERR_QOS_REJECTED"; + case HCI_ERR_CHAN_CLASSIF_NOT_SUPPORTED: + return "HCI_ERR_CHAN_CLASSIF_NOT_SUPPORTED"; + case HCI_ERR_INSUFFCIENT_SECURITY: + return "HCI_ERR_INSUFFCIENT_SECURITY"; + case HCI_ERR_PARAM_OUT_OF_RANGE: + return "HCI_ERR_PARAM_OUT_OF_RANGE"; + case HCI_ERR_UNDEFINED_0x31: + return "HCI_ERR_UNDEFINED_0x31"; + case HCI_ERR_ROLE_SWITCH_PENDING: + return "HCI_ERR_ROLE_SWITCH_PENDING"; + case HCI_ERR_UNDEFINED_0x33: + return "HCI_ERR_UNDEFINED_0x33"; + case HCI_ERR_RESERVED_SLOT_VIOLATION: + return "HCI_ERR_RESERVED_SLOT_VIOLATION"; + case HCI_ERR_ROLE_SWITCH_FAILED: + return "HCI_ERR_ROLE_SWITCH_FAILED"; + case HCI_ERR_INQ_RSP_DATA_TOO_LARGE: + return "HCI_ERR_INQ_RSP_DATA_TOO_LARGE"; + case HCI_ERR_SIMPLE_PAIRING_NOT_SUPPORTED: + return "HCI_ERR_SIMPLE_PAIRING_NOT_SUPPORTED"; + case HCI_ERR_HOST_BUSY_PAIRING: + return "HCI_ERR_HOST_BUSY_PAIRING"; + case HCI_ERR_REJ_NO_SUITABLE_CHANNEL: + return "HCI_ERR_REJ_NO_SUITABLE_CHANNEL"; + case HCI_ERR_CONTROLLER_BUSY: + return "HCI_ERR_CONTROLLER_BUSY"; + case HCI_ERR_UNACCEPT_CONN_INTERVAL: + return "HCI_ERR_UNACCEPT_CONN_INTERVAL"; + case HCI_ERR_DIRECTED_ADVERTISING_TIMEOUT: + return "HCI_ERR_DIRECTED_ADVERTISING_TIMEOUT"; + case HCI_ERR_CONN_TOUT_DUE_TO_MIC_FAILURE: + return "HCI_ERR_CONN_TOUT_DUE_TO_MIC_FAILURE"; + case HCI_ERR_CONN_FAILED_ESTABLISHMENT: + return "HCI_ERR_CONN_FAILED_ESTABLISHMENT"; + case HCI_ERR_MAC_CONNECTION_FAILED: + return "HCI_ERR_MAC_CONNECTION_FAILED"; + case HCI_ERR_LT_ADDR_ALREADY_IN_USE: + return "HCI_ERR_LT_ADDR_ALREADY_IN_USE"; + case HCI_ERR_LT_ADDR_NOT_ALLOCATED: + return "HCI_ERR_LT_ADDR_NOT_ALLOCATED"; + case HCI_ERR_CLB_NOT_ENABLED: + return "HCI_ERR_CLB_NOT_ENABLED"; + case HCI_ERR_MAX_ERR: + return "HCI_ERR_MAX_ERR"; + case HCI_ERR_ESP_VENDOR_FAIL: + return "HCI_ERR_ESP_VENDOR_FAIL"; + case HCI_HINT_TO_RECREATE_AMP_PHYS_LINK: + return "HCI_HINT_TO_RECREATE_AMP_PHYS_LINK"; + default: + return "Invalid HCI status code."; + } + + return NULL; +} +#endif /* !UC_BT_STACK_NO_LOG */ + +void btm_ble_extendadvcb_init(void) +{ + memset(&extend_adv_cb, 0, sizeof(tBTM_BLE_EXTENDED_CB)); +} + +void btm_ble_advrecod_init(void) +{ + memset(&adv_record[0], 0, sizeof(tBTM_EXT_ADV_RECORD)*MAX_BLE_ADV_INSTANCE); +} + +void BTM_BleGapRegisterCallback(tBTM_BLE_5_HCI_CBACK cb) +{ + if (cb) { + ble_5_hci_cb = cb; + } else { + BTM_TRACE_ERROR("%s, register fail, the cb function is NULL.", __func__); + } +} + +void BTM_ExtBleCallbackTrigger(tBTM_BLE_5_GAP_EVENT event, tBTM_BLE_5_GAP_CB_PARAMS *params) +{ + if(params && params->status == BTM_SUCCESS) { + btm_ble_inter_set(true); + } + if (ble_5_hci_cb) { + ble_5_hci_cb(event, params); + } +} + +tBTM_STATUS BTM_BleReadPhy(BD_ADDR bd_addr, UINT8 *tx_phy, UINT8 *rx_phy) +{ + tL2C_LCB *p_lcb = l2cu_find_lcb_by_bd_addr(bd_addr, BT_TRANSPORT_LE); + tBTM_BLE_5_GAP_CB_PARAMS cb_params = {0}; + + if (!tx_phy || !rx_phy || !p_lcb) { + cb_params.read_phy.status = BTM_ILLEGAL_VALUE; + memcpy(cb_params.read_phy.addr, bd_addr, BD_ADDR_LEN); + + if (ble_5_hci_cb) { + ble_5_hci_cb(BTM_BLE_5_GAP_READ_PHY_COMPLETE_EVT, &cb_params); + } + BTM_TRACE_ERROR("%s, invalid parameters", __func__); + return BTM_ILLEGAL_VALUE; + } + + btsnd_hcic_ble_read_phy(p_lcb->handle); + + return BTM_SUCCESS; +} + +tBTM_STATUS BTM_BleSetPreferDefaultPhy(UINT8 tx_phy_mask, UINT8 rx_phy_mask) +{ + UINT8 all_phys = 0; + tBTM_STATUS status = BTM_SUCCESS; + tHCI_STATUS err = HCI_SUCCESS; + tBTM_BLE_5_GAP_CB_PARAMS cb_params = {0}; + + if ((err = btsnd_hcic_ble_set_prefered_default_phy(all_phys, tx_phy_mask, rx_phy_mask)) != HCI_SUCCESS) { + BTM_TRACE_ERROR("%s, fail to send the hci command, the error code = %s(0x%x)", + __func__, btm_ble_hci_status_to_str(err), err); + status = BTM_ILLEGAL_VALUE; + } + + cb_params.set_perf_def_phy.status = err; + + BTM_ExtBleCallbackTrigger(BTM_BLE_5_GAP_SET_PREFERED_DEFAULT_PHY_COMPLETE_EVT, &cb_params); + + return status; + +} + +tBTM_STATUS BTM_BleSetPreferPhy(BD_ADDR bd_addr, UINT8 all_phys, UINT8 tx_phy_mask, + UINT8 rx_phy_mask, UINT16 phy_options) +{ + tL2C_LCB *p_lcb = l2cu_find_lcb_by_bd_addr(bd_addr, BT_TRANSPORT_LE); + tBTM_BLE_5_GAP_CB_PARAMS cb_params = {0}; + + if (!p_lcb) { + cb_params.status = BTM_ILLEGAL_VALUE; + if (ble_5_hci_cb) { + ble_5_hci_cb(BTM_BLE_5_GAP_SET_PREFERED_PHY_COMPLETE_EVT, &cb_params); + } + BTM_TRACE_ERROR("%s, invalid parameters", __func__); + return BTM_ILLEGAL_VALUE; + } + + + + if (!btsnd_hcic_ble_set_phy(p_lcb->handle, all_phys, tx_phy_mask, rx_phy_mask, phy_options)) { + cb_params.status = BTM_ILLEGAL_VALUE; + BTM_ExtBleCallbackTrigger(BTM_BLE_5_GAP_SET_PREFERED_PHY_COMPLETE_EVT, &cb_params); + } + + + return BTM_SUCCESS; +} + +tBTM_STATUS BTM_BleSetExtendedAdvRandaddr(UINT8 instance, BD_ADDR rand_addr) +{ + tBTM_STATUS status = BTM_SUCCESS; + tHCI_STATUS err = HCI_SUCCESS; + tBTM_BLE_5_GAP_CB_PARAMS cb_params = {0}; + + if (instance >= MAX_BLE_ADV_INSTANCE || rand_addr == NULL) { + status = BTM_ILLEGAL_VALUE; + goto end; + } + + /* + A static address is a 48-bit randomly generated address and shall meet the following requirements: + • The two most significant bits of the address shall be equal to 1 + • All bits of the random part of the address shall not be equal to 1 + • All bits of the random part of the address shall not be equal to 0 + */ + BD_ADDR invalid_rand_addr_a, invalid_rand_addr_b; + memset(invalid_rand_addr_a, 0xff, sizeof(BD_ADDR)); + memset(invalid_rand_addr_b, 0x00, sizeof(BD_ADDR)); + if((rand_addr[0] & BT_STATIC_RAND_ADDR_MASK) == BT_STATIC_RAND_ADDR_MASK) { + invalid_rand_addr_b[0] = invalid_rand_addr_b[0] | BT_STATIC_RAND_ADDR_MASK; + if (memcmp(invalid_rand_addr_a, rand_addr, BD_ADDR_LEN) == 0 + || memcmp(invalid_rand_addr_b, rand_addr, BD_ADDR_LEN) == 0) { + status = BTM_ILLEGAL_VALUE; + goto end; + } + } else if ((rand_addr[0] | BT_NON_RPA_MASK) == BT_NON_RPA_MASK) { + invalid_rand_addr_a[0] = invalid_rand_addr_a[0] & BT_NON_RPA_MASK; + if (memcmp(invalid_rand_addr_a, rand_addr, BD_ADDR_LEN) == 0 + || memcmp(invalid_rand_addr_b, rand_addr, BD_ADDR_LEN) == 0) { + status = BTM_ILLEGAL_VALUE; + goto end; + } + } else { + BTM_TRACE_ERROR("%s invalid random address", __func__); + status = BTM_ILLEGAL_VALUE; + goto end; + } + + // set random address + if((err = btsnd_hcic_ble_set_extend_rand_address(instance, rand_addr)) != HCI_SUCCESS) { + BTM_TRACE_ERROR("%s, fail to send the hci command, the error code = %s(0x%x)", + __func__, btm_ble_hci_status_to_str(err), err); + status = BTM_ILLEGAL_VALUE; + } else { + // set random address success, update address infor + if(extend_adv_cb.inst[instance].configured && extend_adv_cb.inst[instance].connetable) { + BTM_BleSetStaticAddr(rand_addr); + BTM_UpdateAddrInfor(BLE_ADDR_RANDOM, rand_addr); + } + } + +end: + cb_params.status = status; + + BTM_ExtBleCallbackTrigger(BTM_BLE_5_GAP_EXT_ADV_SET_RAND_ADDR_COMPLETE_EVT, &cb_params); + + return status; + +} +tBTM_STATUS BTM_BleSetExtendedAdvParams(UINT8 instance, tBTM_BLE_GAP_EXT_ADV_PARAMS *params) +{ + tBTM_STATUS status = BTM_SUCCESS; + tHCI_STATUS err = HCI_SUCCESS; + tBTM_BLE_5_GAP_CB_PARAMS cb_params = {0}; + bool use_rpa_addr = false; + BD_ADDR rand_addr; + + if (instance >= MAX_BLE_ADV_INSTANCE) { + status = BTM_ILLEGAL_VALUE; + BTM_TRACE_ERROR("%s invalid instance %d", __func__, instance); + goto end; + } + + if ((status = btm_ble_ext_adv_params_validate(params)) != BTM_SUCCESS) { + BTM_TRACE_ERROR("%s, invalid extend adv params.", __func__); + } + + if (params->type & BTM_BLE_GAP_SET_EXT_ADV_PROP_CONNECTABLE) { + extend_adv_cb.inst[instance].connetable = true; + } else { + extend_adv_cb.inst[instance].connetable = false; + } + + if (params->type & BTM_BLE_GAP_SET_EXT_ADV_PROP_SCANNABLE) { + extend_adv_cb.inst[instance].scannable = true; + } else { + extend_adv_cb.inst[instance].scannable = false; + } + + if (params->type & BTM_BLE_GAP_SET_EXT_ADV_PROP_LEGACY) { + extend_adv_cb.inst[instance].legacy_pdu = true; + } else { + extend_adv_cb.inst[instance].legacy_pdu = false; + } + +#if (CONTROLLER_RPA_LIST_ENABLE == FALSE) + // if own_addr_type == BLE_ADDR_PUBLIC_ID or BLE_ADDR_RANDOM_ID, + if((params->own_addr_type == BLE_ADDR_PUBLIC_ID || params->own_addr_type == BLE_ADDR_RANDOM_ID) && BTM_GetLocalResolvablePrivateAddr(rand_addr)) { + params->own_addr_type = BLE_ADDR_RANDOM; + use_rpa_addr = true; + } else if(params->own_addr_type == BLE_ADDR_PUBLIC_ID){ + params->own_addr_type = BLE_ADDR_PUBLIC; + } else if (params->own_addr_type == BLE_ADDR_RANDOM_ID) { + params->own_addr_type = BLE_ADDR_RANDOM; + } +#else + btm_cb.ble_ctr_cb.addr_mgnt_cb.own_addr_type = params->own_addr_type; +#endif + + if ((err = btsnd_hcic_ble_set_ext_adv_params(instance, params->type, params->interval_min, params->interval_max, + params->channel_map, params->own_addr_type, params->peer_addr_type, + params->peer_addr, params->filter_policy, params->tx_power, + params->primary_phy, params->max_skip, + params->secondary_phy, params->sid, params->scan_req_notif)) != HCI_SUCCESS) { + BTM_TRACE_ERROR("LE EA SetParams: cmd err=0x%x", err); + status = BTM_ILLEGAL_VALUE; + goto end; + } + + extend_adv_cb.inst[instance].configured = true; + +end: + if(use_rpa_addr) { + // update RPA address + if((err = btsnd_hcic_ble_set_extend_rand_address(instance, rand_addr)) != HCI_SUCCESS) { + BTM_TRACE_ERROR("LE EA SetParams: cmd err=0x%x", err); + status = BTM_ILLEGAL_VALUE; + } else { + // set addr success, update address infor + BTM_UpdateAddrInfor(BLE_ADDR_RANDOM, rand_addr); + } + } + cb_params.status = status; + BTM_ExtBleCallbackTrigger(BTM_BLE_5_GAP_EXT_ADV_SET_PARAMS_COMPLETE_EVT, &cb_params); + + return status; +} + +tBTM_STATUS BTM_BleConfigExtendedAdvDataRaw(BOOLEAN is_scan_rsp, UINT8 instance, UINT16 len, UINT8 *data) +{ + tBTM_STATUS status = BTM_SUCCESS; + tHCI_STATUS err = HCI_SUCCESS; + tBTM_BLE_5_GAP_CB_PARAMS cb_params = {0}; + UINT16 rem_len = len; + UINT8 operation = 0; + UINT16 data_offset = 0; + + + if ((status = btm_ble_ext_adv_set_data_validate(instance, len, data)) != BTM_SUCCESS) { + BTM_TRACE_ERROR("%s, invalid extend adv data.", __func__); + goto end; + } + + do { + UINT8 send_data_len = (rem_len > BTM_BLE_EXT_ADV_DATA_LEN_MAX) ? BTM_BLE_EXT_ADV_DATA_LEN_MAX : rem_len; + if (len <= BTM_BLE_EXT_ADV_DATA_LEN_MAX) { + operation = BTM_BLE_ADV_DATA_OP_COMPLETE; + } else { + if (rem_len == len) { + operation = BTM_BLE_ADV_DATA_OP_FIRST_FRAG; + } else if (rem_len <= BTM_BLE_EXT_ADV_DATA_LEN_MAX) { + operation = BTM_BLE_ADV_DATA_OP_LAST_FRAG; + } else { + operation = BTM_BLE_ADV_DATA_OP_INTERMEDIATE_FRAG; + } + } + if (!is_scan_rsp) { + if ((err = btsnd_hcic_ble_set_ext_adv_data(instance, operation, 0, send_data_len, &data[data_offset])) != HCI_SUCCESS) { + BTM_TRACE_ERROR("LE EA SetAdvData: cmd err=0x%x", err); + status = BTM_ILLEGAL_VALUE; + } + } else { + if ((err = btsnd_hcic_ble_set_ext_adv_scan_rsp_data(instance, operation, 0, send_data_len, &data[data_offset])) != HCI_SUCCESS) { + BTM_TRACE_ERROR("LE EA SetScanRspData: cmd err=0x%x", err); + status = BTM_ILLEGAL_VALUE; + } + } + + rem_len -= send_data_len; + data_offset += send_data_len; + } while (rem_len); + +end: + cb_params.status = status; + BTM_ExtBleCallbackTrigger(is_scan_rsp ? BTM_BLE_5_GAP_EXT_SCAN_RSP_DATA_SET_COMPLETE_EVT : BTM_BLE_5_GAP_EXT_ADV_DATA_SET_COMPLETE_EVT, &cb_params); + + return status; +} + +tBTM_STATUS BTM_BleStartExtAdv(BOOLEAN enable, UINT8 num, tBTM_BLE_EXT_ADV *ext_adv) +{ + tBTM_STATUS status = BTM_SUCCESS; + tHCI_STATUS err = HCI_SUCCESS; + tBTM_BLE_5_GAP_CB_PARAMS cb_params = {0}; + + UINT8 *instance = NULL; + UINT16 *duration = NULL; + UINT8 *max_events = NULL; + + // when enable = true, ext_adv = NULL or num = 0, goto end + if ((!ext_adv || num == 0) && enable) { + status = BTM_ILLEGAL_VALUE; + BTM_TRACE_ERROR("%s invalid parameters", __func__); + goto end; + } + + if (num != 0 && ext_adv != NULL) { + instance = osi_malloc(num); + duration = osi_malloc(num*sizeof(UINT16)); + max_events = osi_malloc(num*sizeof(UINT8)); + + if (!instance || !duration || !max_events) { + status = BTM_NO_RESOURCES; + BTM_TRACE_ERROR("%s invalid parameters", __func__); + goto end; + } + + for (int i = 0; i < num; i++) { + instance[i] = ext_adv[i].instance; + duration[i] = ext_adv[i].duration; + max_events[i] = ext_adv[i].max_events; + } + + if ((err = btsnd_hcic_ble_ext_adv_enable(enable, num, instance, + duration, max_events)) != HCI_SUCCESS) { + BTM_TRACE_ERROR("LE EA En=%d: cmd err=0x%x", enable, err); + status = BTM_ILLEGAL_VALUE; + } + + osi_free(instance); + osi_free(duration); + osi_free(max_events); + } else { + // enable = false, num == 0 or ext_adv = NULL + + if ((err = btsnd_hcic_ble_ext_adv_enable(enable, num, NULL, NULL, NULL)) != HCI_SUCCESS) { + BTM_TRACE_ERROR("LE EA En=%d: cmd err=0x%x", enable, err); + status = BTM_ILLEGAL_VALUE; + } + goto end; + } + + + +end: + + if (!enable && status == BTM_SUCCESS) { + // disable all ext adv + if(num == 0) { + + for (uint8_t i = 0; i < MAX_BLE_ADV_INSTANCE; i++) + { + adv_record[i].invalid = false; + adv_record[i].instance = INVALID_VALUE; + adv_record[i].duration = INVALID_VALUE; + adv_record[i].max_events = INVALID_VALUE; + adv_record[i].retry_count = 0; + } + } else { + for (uint8_t i = 0; i < num; i++) + { + uint8_t index = ext_adv[i].instance; + adv_record[index].invalid = false; + adv_record[index].instance = INVALID_VALUE; + adv_record[index].duration = INVALID_VALUE; + adv_record[index].max_events = INVALID_VALUE; + adv_record[index].retry_count = 0; + } + } + } + // start extend adv success, save the adv information + if(enable && status == BTM_SUCCESS) { + for (uint8_t i = 0; i < num; i++) + { + uint8_t index = ext_adv[i].instance; + adv_record[index].invalid = true; + adv_record[index].instance = ext_adv[i].instance; + adv_record[index].duration = ext_adv[i].duration; + adv_record[index].max_events = ext_adv[i].max_events; + adv_record[index].retry_count = 0; + } + } + + cb_params.status = status; + BTM_ExtBleCallbackTrigger(enable ? BTM_BLE_5_GAP_EXT_ADV_START_COMPLETE_EVT : BTM_BLE_5_GAP_EXT_ADV_STOP_COMPLETE_EVT, &cb_params); + + return status; +} + +tBTM_STATUS BTM_BleStartExtAdvRestart(uint8_t con_handle) +{ + tBTM_BLE_EXT_ADV ext_adv; + uint8_t index = INVALID_VALUE; + for (uint8_t i = 0; i < MAX_BLE_ADV_INSTANCE; i++) + { + if(adv_record[i].ter_con_handle == con_handle) { + index = i; + break; + } + } + + if((index >= MAX_BLE_ADV_INSTANCE) || (!adv_record[index].invalid) || (adv_record[index].retry_count > GATTC_CONNECT_RETRY_COUNT)) { + return BTM_WRONG_MODE; + } + + adv_record[index].retry_count ++; + BTM_TRACE_DEBUG("remote device did not reveive aux connect response, retatrt the extend adv to reconnect, adv handle %d con_handle %d\n", index, con_handle); + ext_adv.instance = adv_record[index].instance; + ext_adv.duration = adv_record[index].duration; + ext_adv.max_events = adv_record[index].max_events; + return BTM_BleStartExtAdv(true, 1, &ext_adv); +} + +tBTM_STATUS BTM_BleExtAdvSetRemove(UINT8 instance) +{ + tBTM_STATUS status = BTM_SUCCESS; + tHCI_STATUS err = HCI_SUCCESS; + tBTM_BLE_5_GAP_CB_PARAMS cb_params = {0}; + + if (instance >= MAX_BLE_ADV_INSTANCE) { + status = BTM_ILLEGAL_VALUE; + BTM_TRACE_ERROR("%s invalid instance %d", __func__, instance); + goto end; + } + + if ((err = btsnd_hcic_ble_remove_adv_set(instance)) != HCI_SUCCESS) { + BTM_TRACE_ERROR("LE EAS Rm: cmd err=0x%x", err); + status = BTM_ILLEGAL_VALUE; + } else { + extend_adv_cb.inst[instance].configured = false; + extend_adv_cb.inst[instance].legacy_pdu = false; + extend_adv_cb.inst[instance].directed = false; + extend_adv_cb.inst[instance].scannable = false; + extend_adv_cb.inst[instance].connetable = false; + } + +end: + + cb_params.status = status; + + BTM_ExtBleCallbackTrigger(BTM_BLE_5_GAP_EXT_ADV_SET_REMOVE_COMPLETE_EVT, &cb_params); + + return status; +} + +tBTM_STATUS BTM_BleExtAdvSetClear(void) +{ + tBTM_STATUS status = BTM_SUCCESS; + tHCI_STATUS err = HCI_SUCCESS; + tBTM_BLE_5_GAP_CB_PARAMS cb_params = {0}; + + if ((err = btsnd_hcic_ble_clear_adv_set()) != HCI_SUCCESS) { + BTM_TRACE_ERROR("LE EAS Clr: cmd err=0x%x", err); + status = BTM_ILLEGAL_VALUE; + } else { + for (uint8_t i = 0; i < MAX_BLE_ADV_INSTANCE; i++) { + extend_adv_cb.inst[i].configured = false; + extend_adv_cb.inst[i].legacy_pdu = false; + extend_adv_cb.inst[i].directed = false; + extend_adv_cb.inst[i].scannable = false; + extend_adv_cb.inst[i].connetable = false; + } + } + + cb_params.status = status; + + BTM_ExtBleCallbackTrigger(BTM_BLE_5_GAP_EXT_ADV_SET_CLEAR_COMPLETE_EVT, &cb_params); + + return status; +} + +tBTM_STATUS BTM_BlePeriodicAdvSetParams(UINT8 instance, tBTM_BLE_Periodic_Adv_Params *params) +{ + tBTM_STATUS status = BTM_SUCCESS; + tHCI_STATUS err = HCI_SUCCESS; + tBTM_BLE_5_GAP_CB_PARAMS cb_params = {0}; + //ext_adv_flag = true; + + if (instance >= MAX_BLE_ADV_INSTANCE) { + status = BTM_ILLEGAL_VALUE; + BTM_TRACE_ERROR("%s invalid instance %d", __func__, instance); + goto end; + } + + if (!extend_adv_cb.inst[instance].configured || + extend_adv_cb.inst[instance].scannable || + extend_adv_cb.inst[instance].connetable || + extend_adv_cb.inst[instance].legacy_pdu) { + BTM_TRACE_ERROR("%s, instance = %d, Before set the periodic adv parameters, please configure the the \ + extend adv to nonscannable and nonconnectable first, and it shouldn't include the legacy bit.", __func__, instance); + status = BTM_ILLEGAL_VALUE; + goto end; + } + + if ((err= btsnd_hcic_ble_set_periodic_adv_params(instance, params->interval_min, + params->interval_max, params->properties)) != HCI_SUCCESS) { + BTM_TRACE_ERROR("LE PA SetParams: cmd err=0x%x", err); + status = BTM_ILLEGAL_VALUE; + } + +end: + + cb_params.status = status; + + BTM_ExtBleCallbackTrigger(BTM_BLE_5_GAP_PERIODIC_ADV_SET_PARAMS_COMPLETE_EVT, &cb_params); + + return status; +} + +tBTM_STATUS BTM_BlePeriodicAdvCfgDataRaw(UINT8 instance, UINT16 len, UINT8 *data,BOOLEAN only_update_did) +{ + tBTM_STATUS status = BTM_SUCCESS; + tHCI_STATUS err = HCI_SUCCESS; + uint16_t rem_len = len; + UINT8 operation = 0; + UINT16 data_offset = 0; + tBTM_BLE_5_GAP_CB_PARAMS cb_params = {0}; + if (only_update_did) + { + len = 0; + data = NULL; + rem_len = 0; + operation = BTM_BLE_ADV_DATA_OP_UNCHANGED_DATA; + } + + if ((status = btm_ble_ext_adv_set_data_validate(instance, len, data)) != BTM_SUCCESS) { + BTM_TRACE_ERROR("%s, invalid extend adv data.", __func__); + goto end; + } + + do { + UINT8 send_data_len = (rem_len > BTM_BLE_PERIODIC_ADV_DATA_LEN_MAX) ? BTM_BLE_PERIODIC_ADV_DATA_LEN_MAX : rem_len; + + if (len <= BTM_BLE_PERIODIC_ADV_DATA_LEN_MAX) { + if (!only_update_did) { + operation = BTM_BLE_ADV_DATA_OP_COMPLETE; + } + } else { + if (rem_len == len) { + operation = BTM_BLE_ADV_DATA_OP_FIRST_FRAG; + } else if (rem_len <= BTM_BLE_PERIODIC_ADV_DATA_LEN_MAX) { + operation = BTM_BLE_ADV_DATA_OP_LAST_FRAG; + } else { + operation = BTM_BLE_ADV_DATA_OP_INTERMEDIATE_FRAG; + } + } + + if ((err = btsnd_hcic_ble_set_periodic_adv_data(instance, operation, send_data_len, &data[data_offset])) != HCI_SUCCESS) { + BTM_TRACE_ERROR("LE PA SetData: cmd err=0x%x", err); + status = BTM_ILLEGAL_VALUE; + } + rem_len -= send_data_len; + data_offset += send_data_len; + } while(rem_len); + +end: + cb_params.status = status; + BTM_ExtBleCallbackTrigger(BTM_BLE_5_GAP_PERIODIC_ADV_DATA_SET_COMPLETE_EVT, &cb_params); + + return status; +} + +tBTM_STATUS BTM_BlePeriodicAdvEnable(UINT8 instance, UINT8 enable) +{ + tBTM_STATUS status = BTM_SUCCESS; + tHCI_STATUS err = HCI_SUCCESS; + tBTM_BLE_5_GAP_CB_PARAMS cb_params = {0}; + + if (instance >= MAX_BLE_ADV_INSTANCE) { + BTM_TRACE_ERROR("%s, invalid instance %d", __func__, instance); + status = BTM_ILLEGAL_VALUE; + goto end; + } + + if ((err = btsnd_hcic_ble_periodic_adv_enable(enable, instance)) != HCI_SUCCESS) { + BTM_TRACE_ERROR("LE PA En=%d: cmd err=0x%x", enable, err); + status = BTM_ILLEGAL_VALUE; + } + +end: + + cb_params.status = status; + + BTM_ExtBleCallbackTrigger(enable ? BTM_BLE_5_GAP_PERIODIC_ADV_START_COMPLETE_EVT : BTM_BLE_5_GAP_PERIODIC_ADV_STOP_COMPLETE_EVT, &cb_params); + + return status; + +} + +tBTM_STATUS BTM_BlePeriodicAdvCreateSync(tBTM_BLE_Periodic_Sync_Params *params) +{ + //tHCI_STATUS err = HCI_SUCCESS; + tBTM_STATUS status = BTM_SUCCESS; + tBTM_BLE_5_GAP_CB_PARAMS cb_params = {0}; + + if (!params) { + status = BTM_ILLEGAL_VALUE; + BTM_TRACE_ERROR("%s, the parameter is NULL.", __func__); + goto end; + } + + if ((params->sync_timeout < 0x0a || params->sync_timeout > 0x4000) + || (params->filter_policy > 0x01) + #if (CONFIG_BT_BLE_FEAT_CREATE_SYNC_ENH) + || (params->reports_disabled > 0x01) + || (params->filter_duplicates > 0x01) + #endif + || (params->addr_type > 0x01) || + (params->sid > 0xf) || (params->skip > 0x01F3)) { + status = BTM_ILLEGAL_VALUE; + BTM_TRACE_ERROR("%s, The sync parameters is invalid.", __func__); + goto end; + } + uint8_t option = 0x00; + if (params->filter_policy) { + SET_BIT(option, 0); + } + + #if (CONFIG_BT_BLE_FEAT_CREATE_SYNC_ENH) + if (params->reports_disabled) { + SET_BIT(option, 1); + } + if (params->filter_duplicates) { + SET_BIT(option, 2); + } + #endif + + if (!btsnd_hcic_ble_periodic_adv_create_sync(option, params->sid, params->addr_type, + params->addr, params->sync_timeout, 0)) { + BTM_TRACE_ERROR("LE PA CreateSync cmd failed"); + status = BTM_ILLEGAL_VALUE; + } + +end: + if(status != BTM_SUCCESS) { + cb_params.status = status; + BTM_ExtBleCallbackTrigger(BTM_BLE_5_GAP_PERIODIC_ADV_CREATE_SYNC_COMPLETE_EVT, &cb_params); + } + + return status; +} +void btm_set_phy_callback(UINT8 status) +{ + tBTM_BLE_5_GAP_CB_PARAMS cb_params = {0}; + cb_params.status = status; + + BTM_ExtBleCallbackTrigger(BTM_BLE_5_GAP_SET_PREFERED_PHY_COMPLETE_EVT, &cb_params); + +} +void btm_create_sync_callback(UINT8 status) +{ + tBTM_BLE_5_GAP_CB_PARAMS cb_params = {0}; + cb_params.status = status; + + BTM_ExtBleCallbackTrigger(BTM_BLE_5_GAP_PERIODIC_ADV_CREATE_SYNC_COMPLETE_EVT, &cb_params); +} + +void btm_read_phy_callback(uint8_t hci_status, uint16_t conn_handle, uint8_t tx_phy, uint8_t rx_phy) +{ + tBTM_STATUS status = BTM_SUCCESS; + tL2C_LCB *p_lcb = l2cu_find_lcb_by_handle(conn_handle); + if(hci_status != HCI_SUCCESS) { + status = BTM_ILLEGAL_VALUE; + BTM_TRACE_ERROR("%s error status %d", __func__, hci_status); + } + tBTM_BLE_5_GAP_CB_PARAMS cb_params = {0}; + if(p_lcb) { + memcpy(cb_params.read_phy.addr, p_lcb->remote_bd_addr, BD_ADDR_LEN); + } + cb_params.read_phy.status = status; + cb_params.read_phy.tx_phy = tx_phy; + cb_params.read_phy.rx_phy = rx_phy; + + BTM_ExtBleCallbackTrigger(BTM_BLE_5_GAP_READ_PHY_COMPLETE_EVT, &cb_params); +} + +tBTM_STATUS BTM_BlePeriodicAdvSyncCancel(void) +{ + tHCI_STATUS err = HCI_SUCCESS; + tBTM_STATUS status = BTM_SUCCESS; + tBTM_BLE_5_GAP_CB_PARAMS cb_params = {0}; + + if ((err = btsnd_hcic_ble_periodic_adv_create_sync_cancel()) != HCI_SUCCESS) { + BTM_TRACE_ERROR("LE PA SyncCancel, cmd err=0x%x", err); + status = BTM_ILLEGAL_VALUE; + } + + cb_params.status = status; + + BTM_ExtBleCallbackTrigger(BTM_BLE_5_GAP_PERIODIC_ADV_SYNC_CANCEL_COMPLETE_EVT, &cb_params); + + return status; +} + +tBTM_STATUS BTM_BlePeriodicAdvSyncTerm(UINT16 sync_handle) +{ + tHCI_STATUS err = HCI_SUCCESS; + tBTM_STATUS status = BTM_SUCCESS; + tBTM_BLE_5_GAP_CB_PARAMS cb_params = {0}; + + if (( err = btsnd_hcic_ble_periodic_adv_term_sync(sync_handle)) != HCI_SUCCESS) { + BTM_TRACE_ERROR("LE PA SyncTerm: cmd err=0x%x", err); + status = BTM_ILLEGAL_VALUE; + } + + cb_params.status = status; + + BTM_ExtBleCallbackTrigger(BTM_BLE_5_GAP_PERIODIC_ADV_SYNC_TERMINATE_COMPLETE_EVT, &cb_params); + + return status; +} + +tBTM_STATUS BTM_BlePeriodicAdvAddDevToList(tBLE_ADDR_TYPE addr_type, BD_ADDR addr, UINT16 sid) +{ + tHCI_STATUS err = HCI_SUCCESS; + tBTM_STATUS status = BTM_SUCCESS; + tBTM_BLE_5_GAP_CB_PARAMS cb_params = {0}; + + if (addr_type > BLE_ADDR_TYPE_MAX) { + status = BTM_ILLEGAL_VALUE; + BTM_TRACE_ERROR("%s invalid addr_type %d", __func__, addr_type); + goto end; + } + + if ((err = btsnd_hcic_ble_add_dev_to_periodic_adv_list(addr_type, addr, sid)) != HCI_SUCCESS) { + BTM_TRACE_ERROR("LE PA AddDevToList: cmd err=0x%x", err); + status = BTM_ILLEGAL_VALUE; + } + +end: + cb_params.status = status; + BTM_ExtBleCallbackTrigger(BTM_BLE_5_GAP_PERIODIC_ADV_ADD_DEV_COMPLETE_EVT, &cb_params); + + return status; +} + +tBTM_STATUS BTM_BlePeriodicAdvRemoveDevFromList(tBLE_ADDR_TYPE addr_type, BD_ADDR addr, UINT16 sid) +{ + tHCI_STATUS err = HCI_SUCCESS; + tBTM_STATUS status = BTM_SUCCESS; + tBTM_BLE_5_GAP_CB_PARAMS cb_params = {0}; + + if (addr_type > BLE_ADDR_TYPE_MAX) { + status = BTM_ILLEGAL_VALUE; + BTM_TRACE_ERROR("%s invalid addr_type %d", __func__, addr_type); + goto end; + } + + if ((err = btsnd_hcic_ble_rm_dev_from_periodic_adv_list(addr_type, addr, sid)) != HCI_SUCCESS) { + BTM_TRACE_ERROR("LE PA RmDevFromList: cmd err=0x%x", err); + status = BTM_ILLEGAL_VALUE; + } + +end: + + cb_params.status = status; + BTM_ExtBleCallbackTrigger(BTM_BLE_5_GAP_PERIODIC_ADV_REMOVE_DEV_COMPLETE_EVT, &cb_params); + return status; +} + +tBTM_STATUS BTM_BlePeriodicAdvClearDev(void) +{ + tHCI_STATUS err = HCI_SUCCESS; + tBTM_STATUS status = BTM_SUCCESS; + tBTM_BLE_5_GAP_CB_PARAMS cb_params = {0}; + + if ((err = btsnd_hcic_ble_clear_periodic_adv_list()) != HCI_SUCCESS) { + BTM_TRACE_ERROR("LE PA ClrDev: cmd err=0x%x", err); + status = BTM_ILLEGAL_VALUE; + } + + cb_params.status = status; + BTM_ExtBleCallbackTrigger(BTM_BLE_5_GAP_PERIODIC_ADV_CLEAR_DEV_COMPLETE_EVT, &cb_params); + + return status; +} + +tBTM_STATUS BTM_BleSetExtendedScanParams(tBTM_BLE_EXT_SCAN_PARAMS *params) +{ + UINT8 phy_mask = 0; + tHCI_STATUS err = HCI_SUCCESS; + tBTM_STATUS status = BTM_SUCCESS; + tHCI_EXT_SCAN_PARAMS hci_params[2]; + int phy_count = 0; + tBTM_BLE_5_GAP_CB_PARAMS cb_params = {0}; + + if (!params) { + status = BTM_ILLEGAL_VALUE; + BTM_TRACE_ERROR("%s invalid parameters", __func__); + goto end; + } + + if (params->own_addr_type > BLE_ADDR_TYPE_MAX) { + status = BTM_ILLEGAL_VALUE; + goto end; + } + + if (params->cfg_mask & BTM_BLE_GAP_EXT_SCAN_UNCODE_MASK) { + phy_mask |= 0x01; + memcpy(&hci_params[phy_count], ¶ms->uncoded_cfg, sizeof(tHCI_EXT_SCAN_PARAMS)); + phy_count++; + } + + if (params->cfg_mask & BTM_BLE_GAP_EXT_SCAN_CODE_MASK) { + phy_mask |= 0x04; + memcpy(&hci_params[phy_count], ¶ms->coded_cfg, sizeof(tHCI_EXT_SCAN_PARAMS)); + phy_count++; + } + + if (BTM_BleUpdateOwnType(¶ms->own_addr_type, NULL) != 0 ) { + status = BTM_ILLEGAL_VALUE; + BTM_TRACE_ERROR("LE UpdateOwnType err"); + goto end; + } + + extend_adv_cb.scan_duplicate = params->scan_duplicate; + + if ((err = btsnd_hcic_ble_set_ext_scan_params(params->own_addr_type, params->filter_policy, phy_mask, phy_count, + hci_params)) != HCI_SUCCESS) { + BTM_TRACE_ERROR("LE ES SetParams: cmd err=0x%x", err); + status = BTM_ILLEGAL_VALUE; + } + +end: + + cb_params.status = status; + + BTM_ExtBleCallbackTrigger(BTM_BLE_5_GAP_SET_EXT_SCAN_PARAMS_COMPLETE_EVT, &cb_params); + + return cb_params.status; +} + +tBTM_STATUS BTM_BleExtendedScan(BOOLEAN enable, UINT16 duration, UINT16 period) +{ + tHCI_STATUS err = HCI_SUCCESS; + tBTM_BLE_5_GAP_CB_PARAMS cb_params = {0}; + tBTM_STATUS status = BTM_SUCCESS; + + if (extend_adv_cb.scan_duplicate > 0x03) { + status = BTM_ILLEGAL_VALUE; + BTM_TRACE_ERROR("%s invalid scan_duplicate %d", __func__, extend_adv_cb.scan_duplicate); + goto end; + } + + if ((err = btsnd_hcic_ble_ext_scan_enable(enable, extend_adv_cb.scan_duplicate, duration, period)) != HCI_SUCCESS) { + BTM_TRACE_ERROR("LE ES En=%d: cmd err=0x%x", enable, err); + status = BTM_ILLEGAL_VALUE; + } + +end: + + cb_params.status = status; + + BTM_ExtBleCallbackTrigger(enable ? BTM_BLE_5_GAP_EXT_SCAN_START_COMPLETE_EVT : BTM_BLE_5_GAP_EXT_SCAN_STOP_COMPLETE_EVT, &cb_params); + + return status; +} + +void BTM_BleSetPreferExtenedConnParams (BD_ADDR bd_addr, tBTM_EXT_CONN_PARAMS *params) +{ + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_or_alloc_dev (bd_addr); + + if (p_dev_rec) { + if (params) { + memcpy(&p_dev_rec->ext_conn_params, params, sizeof(tBTM_EXT_CONN_PARAMS)); + } else { + BTM_TRACE_ERROR("Invalid Extand connection parameters"); + } + } else { + BTM_TRACE_ERROR("Unknown Device, setting rejected"); + } + + return; +} + +void btm_ble_extended_init(void) +{ + +} + +void btm_ble_extended_cleanup(void) +{ + +} + +static tBTM_STATUS btm_ble_ext_adv_params_validate(tBTM_BLE_GAP_EXT_ADV_PARAMS *params) +{ + if (!params) { + return BTM_ILLEGAL_VALUE; + } + + if (params->own_addr_type > BLE_ADDR_TYPE_MAX) { + BTM_TRACE_ERROR("%s, invalid own address type, line %d, addr type %d", __func__, __LINE__, params->own_addr_type); + return BTM_ILLEGAL_VALUE; + } + + if (params->type & BTM_BLE_GAP_SET_EXT_ADV_PROP_LEGACY) { + /* Not allowed for legacy PDUs. */ + if (params->type & BTM_BLE_GAP_SET_EXT_ADV_PROP_INCLUDE_TX_PWR) { + BTM_TRACE_ERROR("%s, The Legacy adv can't include tx power bit, line %d", __func__, __LINE__); + return BTM_ILLEGAL_VALUE; + } + } + + if (!(params->type & BTM_BLE_GAP_SET_EXT_ADV_PROP_LEGACY)) { + /* Not allowed for extended advertising PDUs */ + if ((params->type & BTM_BLE_GAP_SET_EXT_ADV_PROP_CONNECTABLE) && + (params->type & BTM_BLE_GAP_SET_EXT_ADV_PROP_SCANNABLE)) { + BTM_TRACE_ERROR("%s, For the Extend adv, the properties can't be connectable and scannable at the same time, line %d", __func__, __LINE__); + return BTM_ILLEGAL_VALUE; + } + + /* HD directed advertising allowed only for legacy PDUs */ + if (params->type & BTM_BLE_GAP_SET_EXT_ADV_PROP_HD_DIRECTED) { + BTM_TRACE_ERROR("%s, HD directed advertising allowed only for legacy PDUs. line %d", __func__, __LINE__); + return BTM_ILLEGAL_VALUE; + } + } + + return BTM_SUCCESS; +} + +static tBTM_STATUS btm_ble_ext_adv_set_data_validate(UINT8 instance, UINT16 len, UINT8 *data) +{ + if (data == NULL && len > 0) { + BTM_TRACE_ERROR("%s, the extend adv data is NULL. line %d", __func__, __LINE__); + return BTM_ILLEGAL_VALUE; + } + + if (instance >= MAX_BLE_ADV_INSTANCE) { + BTM_TRACE_ERROR("%s, adv instance is %d, Exceeded the maximum. line %d", __func__, instance, __LINE__); + return BTM_ILLEGAL_VALUE; + } + + if (!extend_adv_cb.inst[instance].configured) { + BTM_TRACE_ERROR("%s, The extend adv hasn't configured, please use the set_ext_adv_params API to set the ext adv parameters first. line %d", __func__, __LINE__); + return BTM_ILLEGAL_VALUE; + } + + /* Not allowed with the direted advertising for legacy */ + if (extend_adv_cb.inst[instance].legacy_pdu && extend_adv_cb.inst[instance].directed) { + BTM_TRACE_ERROR("%s, Not allowed with the direted advertising for legacy. line %d", __func__, __LINE__); + return BTM_ILLEGAL_VALUE; + } + + /* Always allowed with legacy PDU but limited to legacy length */ + if (extend_adv_cb.inst[instance].legacy_pdu) { + if (len > 31) { + BTM_TRACE_ERROR("%s, for the legacy adv, the adv data length can't exceed 31. line %d", __func__, __LINE__); + return BTM_ILLEGAL_VALUE; + } + } else { + if (len > controller_get_interface()->ble_get_ext_adv_data_max_len()) { + BTM_TRACE_ERROR("%s, The adv data len(%d) is longer then the controller adv max len(%d)", + __func__, len, controller_get_interface()->ble_get_ext_adv_data_max_len()); + return BTM_ILLEGAL_VALUE; + } + } + + return BTM_SUCCESS; +} + +void btm_ble_update_phy_evt(tBTM_BLE_UPDATE_PHY *params) +{ + tBTM_BLE_5_GAP_CB_PARAMS cb_params = {0}; + + if (!params) { + BTM_TRACE_ERROR("%s, Invalid params.", __func__); + return; + } + tL2C_LCB *p_lcb = l2cu_find_lcb_by_handle(params->conn_idx); + if(!p_lcb) { + BTM_TRACE_ERROR("%s, Invalid params.", __func__); + return; + } + + cb_params.phy_update.status = params->status; + cb_params.phy_update.tx_phy = params->tx_phy; + cb_params.phy_update.rx_phy = params->rx_phy; + memcpy(cb_params.phy_update.addr, p_lcb->remote_bd_addr, BD_ADDR_LEN); + + // If the user has register the callback function, should callback it to the application. + + BTM_ExtBleCallbackTrigger(BTM_BLE_5_GAP_PHY_UPDATE_COMPLETE_EVT, &cb_params); + + return; +} + +void btm_ble_scan_timeout_evt(void) +{ + BTM_ExtBleCallbackTrigger(BTM_BLE_5_GAP_SCAN_TIMEOUT_EVT, NULL); +} + +void btm_ble_adv_set_terminated_evt(tBTM_BLE_ADV_TERMINAT *params) +{ + tBTM_BLE_5_GAP_CB_PARAMS cb_params = {0}; + + if (!params) { + BTM_TRACE_ERROR("%s, Invalid params.", __func__); + return; + } + + // adv terminated due to connection, save the adv handle and connection handle + if(params->completed_event == 0x00) { + adv_record[params->adv_handle].ter_con_handle = params->conn_handle; + } else { + adv_record[params->adv_handle].ter_con_handle = INVALID_VALUE; + adv_record[params->adv_handle].invalid = false; + } + + memcpy(&cb_params.adv_term, params, sizeof(tBTM_BLE_ADV_TERMINAT)); + + // If the user has register the callback function, should callback it to the application. + BTM_ExtBleCallbackTrigger(BTM_BLE_5_GAP_ADV_TERMINATED_EVT, &cb_params); + + return; +} + +void btm_ble_ext_adv_report_evt(tBTM_BLE_EXT_ADV_REPORT *params) +{ + tBTM_BLE_5_GAP_CB_PARAMS cb_params = {0}; + + if (!params) { + BTM_TRACE_ERROR("%s, Invalid params.", __func__); + return; + } + + memcpy(&cb_params.ext_adv_report, params, sizeof(tBTM_BLE_EXT_ADV_REPORT)); + + // If the user has register the callback function, should callback it to the application. + BTM_ExtBleCallbackTrigger(BTM_BLE_5_GAP_EXT_ADV_REPORT_EVT, &cb_params); + + return; + +} + +void btm_ble_scan_req_received_evt(tBTM_BLE_SCAN_REQ_RECEIVED *params) +{ + tBTM_BLE_5_GAP_CB_PARAMS cb_params = {0}; + + if (!params) { + BTM_TRACE_ERROR("%s, Invalid params.", __func__); + return; + } + + memcpy(&cb_params.scan_req, params, sizeof(tBTM_BLE_SCAN_REQ_RECEIVED)); + + // If the user has register the callback function, should callback it to the application. + BTM_ExtBleCallbackTrigger(BTM_BLE_5_GAP_SCAN_REQ_RECEIVED_EVT, &cb_params); + + return; +} + +void btm_ble_channel_select_algorithm_evt(tBTM_BLE_CHANNEL_SEL_ALG *params) +{ + tBTM_BLE_5_GAP_CB_PARAMS cb_params = {0}; + + if (!params) { + BTM_TRACE_ERROR("%s, Invalid params.", __func__); + return; + } + + memcpy(&cb_params.channel_sel, params, sizeof(tBTM_BLE_CHANNEL_SEL_ALG)); + + // If the user has register the callback function, should callback it to the application. + BTM_ExtBleCallbackTrigger(BTM_BLE_5_GAP_CHANNEL_SELETE_ALGORITHM_EVT, &cb_params); + + return; +} + +void btm_ble_periodic_adv_report_evt(tBTM_PERIOD_ADV_REPORT *params) +{ + tBTM_BLE_5_GAP_CB_PARAMS cb_params = {0}; + + if (!params) { + BTM_TRACE_ERROR("%s, Invalid params.", __func__); + return; + } + + memcpy(&cb_params.period_adv_report, params, sizeof(tBTM_PERIOD_ADV_REPORT)); + + // If the user has register the callback function, should callback it to the application. + BTM_ExtBleCallbackTrigger(BTM_BLE_5_GAP_PERIODIC_ADV_REPORT_EVT, &cb_params); + + return; + +} + +void btm_ble_periodic_adv_sync_lost_evt(tBTM_BLE_PERIOD_ADV_SYNC_LOST *params) +{ + tBTM_BLE_5_GAP_CB_PARAMS cb_params = {0}; + + if (!params) { + BTM_TRACE_ERROR("%s, Invalid params.", __func__); + return; + } + + memcpy(&cb_params.sync_lost, params, sizeof(tBTM_BLE_PERIOD_ADV_SYNC_LOST)); + + // If the user has register the callback function, should callback it to the application. + BTM_ExtBleCallbackTrigger(BTM_BLE_5_GAP_PERIODIC_ADV_SYNC_LOST_EVT, &cb_params); + + return; + +} + +void btm_ble_periodic_adv_sync_establish_evt(tBTM_BLE_PERIOD_ADV_SYNC_ESTAB *params) +{ + tBTM_BLE_5_GAP_CB_PARAMS cb_params = {0}; + + if (!params) { + BTM_TRACE_ERROR("%s, Invalid params.", __func__); + return; + } + + memcpy(&cb_params.sync_estab, params, sizeof(tBTM_BLE_PERIOD_ADV_SYNC_ESTAB)); + + // If the user has register the callback function, should callback it to the application. + BTM_ExtBleCallbackTrigger(BTM_BLE_5_GAP_PERIODIC_ADV_SYNC_ESTAB_EVT, &cb_params); + + return; + +} + +#endif // #if (BLE_50_FEATURE_SUPPORT == TRUE) + +#if (BLE_FEAT_PERIODIC_ADV_SYNC_TRANSFER == TRUE) +void btm_ble_periodic_adv_sync_trans_complete(UINT16 op_code, UINT8 hci_status, UINT16 conn_handle) +{ + tBTM_BLE_5_GAP_CB_PARAMS cb_params = {0}; + UINT8 evt = BTM_BLE_5_GAP_UNKNOWN_EVT; + tL2C_LCB *p_lcb = l2cu_find_lcb_by_handle(conn_handle); + + switch (op_code) { + case HCI_BLE_PERIOD_ADV_SYNC_TRANS: + evt = BTM_BLE_GAP_PERIODIC_ADV_SYNC_TRANS_COMPLETE_EVT; + break; + case HCI_BLE_PERIOD_ADV_SET_INFO_TRANS: + evt = BTM_BLE_GAP_PERIODIC_ADV_SET_INFO_TRANS_COMPLETE_EVT; + break; + case HCI_BLE_SET_PAST_PARAMS: + evt = BTM_BLE_GAP_SET_PAST_PARAMS_COMPLETE_EVT; + break; + default: + return; + } + + cb_params.per_adv_sync_trans.status = BTM_SUCCESS; + if(hci_status != HCI_SUCCESS) { + cb_params.per_adv_sync_trans.status = BTM_ILLEGAL_VALUE; + BTM_TRACE_ERROR("%s error status %d", __func__, hci_status); + } + + if(p_lcb) { + memcpy(cb_params.per_adv_sync_trans.addr, p_lcb->remote_bd_addr, BD_ADDR_LEN); + } + + BTM_ExtBleCallbackTrigger(evt, &cb_params); +} + +void BTM_BlePeriodicAdvRecvEnable(UINT16 sync_handle, UINT8 enable) +{ + tHCI_STATUS err = HCI_SUCCESS; + tBTM_STATUS status = BTM_SUCCESS; + tBTM_BLE_5_GAP_CB_PARAMS cb_params = {0}; + + if ((err = btsnd_hcic_ble_set_periodic_adv_recv_enable(sync_handle, enable)) != HCI_SUCCESS) { + BTM_TRACE_ERROR("%s cmd err=0x%x", __func__, err); + status = BTM_ILLEGAL_VALUE; + } + + cb_params.status = status; + BTM_ExtBleCallbackTrigger(BTM_BLE_GAP_PERIODIC_ADV_RECV_ENABLE_COMPLETE_EVT, &cb_params); +} + +void BTM_BlePeriodicAdvSyncTrans(BD_ADDR bd_addr, UINT16 service_data, UINT16 sync_handle) +{ + tBTM_STATUS status = BTM_SUCCESS; + tBTM_BLE_5_GAP_CB_PARAMS cb_params = {0}; + + tL2C_LCB *p_lcb = l2cu_find_lcb_by_bd_addr(bd_addr, BT_TRANSPORT_LE); + if (!p_lcb) { + BTM_TRACE_ERROR("%s, invalid parameters", __func__); + status = BTM_ILLEGAL_VALUE; + } + + if (status != BTM_SUCCESS) { + cb_params.per_adv_sync_trans.status = status; + memcpy(cb_params.per_adv_sync_trans.addr, bd_addr, sizeof(BD_ADDR)); + BTM_ExtBleCallbackTrigger(BTM_BLE_GAP_PERIODIC_ADV_SYNC_TRANS_COMPLETE_EVT, &cb_params); + return; + } + + btsnd_hcic_ble_periodic_adv_sync_trans(p_lcb->handle, service_data, sync_handle); +} + +void BTM_BlePeriodicAdvSetInfoTrans(BD_ADDR bd_addr, UINT16 service_data, UINT8 adv_handle) +{ + tBTM_STATUS status = BTM_SUCCESS; + tBTM_BLE_5_GAP_CB_PARAMS cb_params = {0}; + tL2C_LCB *p_lcb = l2cu_find_lcb_by_bd_addr(bd_addr, BT_TRANSPORT_LE); + + if (!p_lcb) { + BTM_TRACE_ERROR("%s, invalid parameters", __func__); + status = BTM_ILLEGAL_VALUE; + } + + if (status != BTM_SUCCESS) { + cb_params.per_adv_sync_trans.status = status; + memcpy(cb_params.per_adv_sync_trans.addr, bd_addr, sizeof(BD_ADDR)); + BTM_ExtBleCallbackTrigger(BTM_BLE_GAP_PERIODIC_ADV_SET_INFO_TRANS_COMPLETE_EVT, &cb_params); + return; + } + + btsnd_hcic_ble_periodic_adv_set_info_trans(p_lcb->handle, service_data, adv_handle); +} + +void BTM_BleSetPeriodicAdvSyncTransParams(BD_ADDR bd_addr, UINT8 mode, UINT16 skip, UINT16 sync_timeout, UINT8 cte_type) +{ + tBTM_STATUS status = BTM_SUCCESS; + tBTM_BLE_5_GAP_CB_PARAMS cb_params = {0}; + + // Set default past params + if (bdaddr_is_empty((bt_bdaddr_t *)bd_addr)) { + tHCI_STATUS err = HCI_SUCCESS; + if ((err = btsnd_hcic_ble_set_default_periodic_adv_sync_trans_params(mode, skip, sync_timeout, cte_type)) != HCI_SUCCESS) { + BTM_TRACE_ERROR("%s cmd err=0x%x", __func__, err); + status = BTM_ILLEGAL_VALUE; + } + + cb_params.set_past_params.status = status; + memset(cb_params.set_past_params.addr, 0, sizeof(BD_ADDR)); + BTM_ExtBleCallbackTrigger(BTM_BLE_GAP_SET_PAST_PARAMS_COMPLETE_EVT, &cb_params); + return; + } + + tL2C_LCB *p_lcb = l2cu_find_lcb_by_bd_addr(bd_addr, BT_TRANSPORT_LE); + if (!p_lcb) { + BTM_TRACE_ERROR("%s, invalid parameters", __func__); + status = BTM_ILLEGAL_VALUE; + } + + if (status != BTM_SUCCESS) { + cb_params.set_past_params.status = status; + memcpy(cb_params.set_past_params.addr, bd_addr, sizeof(BD_ADDR)); + BTM_ExtBleCallbackTrigger(BTM_BLE_GAP_SET_PAST_PARAMS_COMPLETE_EVT, &cb_params); + return; + } + + btsnd_hcic_ble_set_periodic_adv_sync_trans_params(p_lcb->handle, mode, skip, sync_timeout, cte_type); +} + +void btm_ble_periodic_adv_sync_trans_recv_evt(tBTM_BLE_PERIOD_ADV_SYNC_TRANS_RECV *params) +{ + tBTM_BLE_5_GAP_CB_PARAMS cb_params = {0}; + + if (!params) { + BTM_TRACE_ERROR("%s, Invalid params.", __func__); + return; + } + + memcpy(&cb_params.past_recv, params, sizeof(tBTM_BLE_PERIOD_ADV_SYNC_TRANS_RECV)); + + BTM_ExtBleCallbackTrigger(BTM_BLE_GAP_PERIODIC_ADV_SYNC_TRANS_RECV_EVT, &cb_params); +} +#endif // #if (BLE_FEAT_PERIODIC_ADV_SYNC_TRANSFER == TRUE) diff --git a/lib/bt/host/bluedroid/stack/btm/btm_ble_addr.c b/lib/bt/host/bluedroid/stack/btm/btm_ble_addr.c new file mode 100644 index 00000000..d7ed2095 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/btm/btm_ble_addr.c @@ -0,0 +1,608 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains functions for BLE address management. + * + ******************************************************************************/ + +#include + +#include "stack/bt_types.h" +#include "stack/hcimsgs.h" +#include "stack/btu.h" +#include "btm_int.h" +#include "stack/gap_api.h" +#include "device/controller.h" + +#if (defined BLE_INCLUDED && BLE_INCLUDED == TRUE) +#include "btm_ble_int.h" +#include "stack/smp_api.h" + + +/******************************************************************************* +** +** Function btm_gen_resolve_paddr_cmpl +** +** Description This is callback functioin when resolvable private address +** generation is complete. +** +** Returns void +** +*******************************************************************************/ +static void btm_gen_resolve_paddr_cmpl(tSMP_ENC *p) +{ + tBTM_LE_RANDOM_CB *p_cb = &btm_cb.ble_ctr_cb.addr_mgnt_cb; + BTM_TRACE_EVENT ("btm_gen_resolve_paddr_cmpl"); + + if (p) { + /* set hash to be LSB of rpAddress */ + p_cb->private_addr[5] = p->param_buf[0]; + p_cb->private_addr[4] = p->param_buf[1]; + p_cb->private_addr[3] = p->param_buf[2]; + + /* set it to controller */ + btm_ble_set_random_addr(p_cb->private_addr); + + p_cb->exist_addr_bit |= BTM_BLE_GAP_ADDR_BIT_RESOLVABLE; + memcpy(p_cb->resolvale_addr, p_cb->private_addr, BD_ADDR_LEN); + if (p_cb->set_local_privacy_cback){ + (*p_cb->set_local_privacy_cback)(BTM_SET_PRIVACY_SUCCESS); + p_cb->set_local_privacy_cback = NULL; + } + + /* start a periodical timer to refresh random addr */ + btu_stop_timer_oneshot(&p_cb->raddr_timer_ent); +#if (BTM_BLE_CONFORMANCE_TESTING == TRUE) + btu_start_timer_oneshot(&p_cb->raddr_timer_ent, BTU_TTYPE_BLE_RANDOM_ADDR, + btm_cb.ble_ctr_cb.rpa_tout); +#else + btu_start_timer_oneshot(&p_cb->raddr_timer_ent, BTU_TTYPE_BLE_RANDOM_ADDR, + BTM_BLE_PRIVATE_ADDR_INT); +#endif + } else { + /* random address set failure */ + BTM_TRACE_DEBUG("set random address failed"); + if (p_cb->set_local_privacy_cback){ + (*p_cb->set_local_privacy_cback)(BTM_SET_PRIVACY_FAIL); + p_cb->set_local_privacy_cback = NULL; + } + } +} +/******************************************************************************* +** +** Function btm_gen_resolve_paddr_low +** +** Description This function is called when random address has generate the +** random number base for low 3 byte bd address. +** +** Returns void +** +*******************************************************************************/ +void btm_gen_resolve_paddr_low(tBTM_RAND_ENC *p) +{ +#if (BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE) + tBTM_LE_RANDOM_CB *p_cb = &btm_cb.ble_ctr_cb.addr_mgnt_cb; + tSMP_ENC output; + + BTM_TRACE_EVENT ("btm_gen_resolve_paddr_low"); + if (p) { + p->param_buf[2] &= (~BLE_RESOLVE_ADDR_MASK); + p->param_buf[2] |= BLE_RESOLVE_ADDR_MSB; + + p_cb->private_addr[2] = p->param_buf[0]; + p_cb->private_addr[1] = p->param_buf[1]; + p_cb->private_addr[0] = p->param_buf[2]; + + /* encrypt with ur IRK */ + if (!SMP_Encrypt(btm_cb.devcb.id_keys.irk, BT_OCTET16_LEN, p->param_buf, 3, &output)) { + btm_gen_resolve_paddr_cmpl(NULL); + } else { + btm_gen_resolve_paddr_cmpl(&output); + } + } +#endif +} +/******************************************************************************* +** +** Function btm_gen_resolvable_private_addr +** +** Description This function generate a resolvable private address. +** +** Returns void +** +*******************************************************************************/ +void btm_gen_resolvable_private_addr (void *p_cmd_cplt_cback) +{ + BTM_TRACE_EVENT ("btm_gen_resolvable_private_addr"); + /* generate 3B rand as BD LSB, SRK with it, get BD MSB */ + if (!btsnd_hcic_ble_rand((void *)p_cmd_cplt_cback)) { + btm_gen_resolve_paddr_cmpl(NULL); + } +} +/******************************************************************************* +** +** Function btm_gen_non_resolve_paddr_cmpl +** +** Description This is the callback function when non-resolvable private +** function is generated and write to controller. +** +** Returns void +** +*******************************************************************************/ +static void btm_gen_non_resolve_paddr_cmpl(tBTM_RAND_ENC *p) +{ + tBTM_LE_RANDOM_CB *p_cb = &btm_cb.ble_ctr_cb.addr_mgnt_cb; + tBTM_BLE_ADDR_CBACK *p_cback = p_cb->p_generate_cback; + void *p_data = p_cb->p; + UINT8 *pp; + BD_ADDR static_random; + + BTM_TRACE_EVENT ("btm_gen_non_resolve_paddr_cmpl"); + + p_cb->p_generate_cback = NULL; + if (p) { + + pp = p->param_buf; + STREAM_TO_BDADDR(static_random, pp); + /* mask off the 2 MSB */ + static_random[0] &= BLE_STATIC_PRIVATE_MSB_MASK; + + /* report complete */ + if (p_cback) { + (* p_cback)(static_random, p_data); + } + } else { + BTM_TRACE_DEBUG("btm_gen_non_resolvable_private_addr failed"); + if (p_cback) { + (* p_cback)(NULL, p_data); + } + } +} +/******************************************************************************* +** +** Function btm_gen_non_resolvable_private_addr +** +** Description This function generate a non-resolvable private address. +** +** +** Returns void +** +*******************************************************************************/ +void btm_gen_non_resolvable_private_addr (tBTM_BLE_ADDR_CBACK *p_cback, void *p) +{ + tBTM_LE_RANDOM_CB *p_mgnt_cb = &btm_cb.ble_ctr_cb.addr_mgnt_cb; + + BTM_TRACE_EVENT ("btm_gen_non_resolvable_private_addr"); + + if (p_mgnt_cb->p_generate_cback != NULL) { + return; + } + + p_mgnt_cb->p_generate_cback = p_cback; + p_mgnt_cb->p = p; + if (!btsnd_hcic_ble_rand((void *)btm_gen_non_resolve_paddr_cmpl)) { + btm_gen_non_resolve_paddr_cmpl(NULL); + } + +} + +/******************************************************************************* +** Utility functions for Random address resolving +*******************************************************************************/ +/******************************************************************************* +** +** Function btm_ble_resolve_address_cmpl +** +** Description This function sends the random address resolving complete +** callback. +** +** Returns None. +** +*******************************************************************************/ +#if SMP_INCLUDED == TRUE +static void btm_ble_resolve_address_cmpl(void) +{ + tBTM_LE_RANDOM_CB *p_mgnt_cb = &btm_cb.ble_ctr_cb.addr_mgnt_cb; + + BTM_TRACE_EVENT ("btm_ble_resolve_address_cmpl p_mgnt_cb->p_dev_rec = 0x%08x", (uint32_t)p_mgnt_cb->p_dev_rec); + + p_mgnt_cb->busy = FALSE; + + (* p_mgnt_cb->p_resolve_cback)(p_mgnt_cb->p_dev_rec, p_mgnt_cb->p); +} +/******************************************************************************* +** +** Function btm_ble_proc_resolve_x +** +** Description This function compares the X with random address 3 MSO bytes +** to find a match, if not match, continue for next record. +** +** Returns None. +** +*******************************************************************************/ +static BOOLEAN btm_ble_proc_resolve_x(tSMP_ENC *p) +{ + tBTM_LE_RANDOM_CB *p_mgnt_cb = &btm_cb.ble_ctr_cb.addr_mgnt_cb; + UINT8 comp[3]; + BTM_TRACE_EVENT ("btm_ble_proc_resolve_x"); + /* compare the hash with 3 LSB of bd address */ + comp[0] = p_mgnt_cb->random_bda[5]; + comp[1] = p_mgnt_cb->random_bda[4]; + comp[2] = p_mgnt_cb->random_bda[3]; + + if (p) { + if (!memcmp(p->param_buf, &comp[0], 3)) { + /* match is found */ + BTM_TRACE_EVENT ("match is found"); + btm_ble_resolve_address_cmpl(); + return TRUE; + } + } + + return FALSE; +} +#endif ///SMP_INCLUDED == TRUE + +/******************************************************************************* +** +** Function btm_ble_init_pseudo_addr +** +** Description This function is used to initialize pseudo address. +** If pseudo address is not available, use dummy address +** +** Returns TRUE is updated; FALSE otherwise. +** +*******************************************************************************/ +BOOLEAN btm_ble_init_pseudo_addr (tBTM_SEC_DEV_REC *p_dev_rec, BD_ADDR new_pseudo_addr) +{ +#if (SMP_INCLUDED == TRUE) + BD_ADDR dummy_bda = {0}; + + if (memcmp(p_dev_rec->ble.pseudo_addr, dummy_bda, BD_ADDR_LEN) == 0) { + memcpy(p_dev_rec->ble.pseudo_addr, new_pseudo_addr, BD_ADDR_LEN); + return TRUE; + } +#endif ///SMP_INCLUDED == TRUE + return FALSE; +} + +/******************************************************************************* +** +** Function btm_ble_addr_resolvable +** +** Description This function checks if a RPA is resolvable by the device key. +** +** Returns TRUE is resolvable; FALSE otherwise. +** +*******************************************************************************/ +BOOLEAN btm_ble_addr_resolvable (BD_ADDR rpa, tBTM_SEC_DEV_REC *p_dev_rec) +{ + BOOLEAN rt = FALSE; +#if (SMP_INCLUDED == TRUE) + if (!BTM_BLE_IS_RESOLVE_BDA(rpa)) { + return rt; + } + + UINT8 rand[3]; + tSMP_ENC output; + if ((p_dev_rec->device_type & BT_DEVICE_TYPE_BLE) && + (p_dev_rec->ble.key_type & BTM_LE_KEY_PID)) { + BTM_TRACE_DEBUG("%s try to resolve", __func__); + /* use the 3 MSB of bd address as prand */ + rand[0] = rpa[2]; + rand[1] = rpa[1]; + rand[2] = rpa[0]; + + /* generate X = E irk(R0, R1, R2) and R is random address 3 LSO */ + SMP_Encrypt(p_dev_rec->ble.keys.irk, BT_OCTET16_LEN, + &rand[0], 3, &output); + + rand[0] = rpa[5]; + rand[1] = rpa[4]; + rand[2] = rpa[3]; + + if (!memcmp(output.param_buf, &rand[0], 3)) { + btm_ble_init_pseudo_addr (p_dev_rec, rpa); + rt = TRUE; + } + } +#endif ///SMP_INCLUDED == TRUE + return rt; +} + +#if (BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE) +/******************************************************************************* +** +** Function btm_ble_match_random_bda +** +** Description This function match the random address to the appointed device +** record, starting from calculating IRK. If record index exceed +** the maximum record number, matching failed and send callback. +** +** Returns None. +** +*******************************************************************************/ +static BOOLEAN btm_ble_match_random_bda(tBTM_SEC_DEV_REC *p_dev_rec) +{ + /* use the 3 MSB of bd address as prand */ + + tBTM_LE_RANDOM_CB *p_mgnt_cb = &btm_cb.ble_ctr_cb.addr_mgnt_cb; + UINT8 rand[3]; + rand[0] = p_mgnt_cb->random_bda[2]; + rand[1] = p_mgnt_cb->random_bda[1]; + rand[2] = p_mgnt_cb->random_bda[0]; + + BTM_TRACE_EVENT("%s p_dev_rec = 0x%08x", __func__, (uint32_t)p_dev_rec); + + { + tSMP_ENC output; + + BTM_TRACE_DEBUG("sec_flags = %02x device_type = %d", p_dev_rec->sec_flags, + p_dev_rec->device_type); + + if ((p_dev_rec->device_type & BT_DEVICE_TYPE_BLE) && + (p_dev_rec->ble.key_type & BTM_LE_KEY_PID)) { + /* generate X = E irk(R0, R1, R2) and R is random address 3 LSO */ + SMP_Encrypt(p_dev_rec->ble.keys.irk, BT_OCTET16_LEN, + &rand[0], 3, &output); + return btm_ble_proc_resolve_x(&output); + } else { + // not completed + return FALSE; + } + } +} +#endif ///BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE + +/******************************************************************************* +** +** Function btm_ble_resolve_random_addr +** +** Description This function is called to resolve a random address. +** +** Returns pointer to the security record of the device whom a random +** address is matched to. +** +*******************************************************************************/ +void btm_ble_resolve_random_addr(BD_ADDR random_bda, tBTM_BLE_RESOLVE_CBACK *p_cback, void *p) +{ +#if (SMP_INCLUDED == TRUE) + tBTM_LE_RANDOM_CB *p_mgnt_cb = &btm_cb.ble_ctr_cb.addr_mgnt_cb; + list_node_t *p_node = NULL; + tBTM_SEC_DEV_REC *p_dev_rec = NULL; + + BTM_TRACE_EVENT ("btm_ble_resolve_random_addr"); + if ( !p_mgnt_cb->busy) { + p_mgnt_cb->p = p; + p_mgnt_cb->busy = TRUE; + p_mgnt_cb->p_dev_rec = NULL; + p_mgnt_cb->p_resolve_cback = p_cback; + memcpy(p_mgnt_cb->random_bda, random_bda, BD_ADDR_LEN); + /* start to resolve random address */ + /* check for next security record */ + for (p_node = list_begin(btm_cb.p_sec_dev_rec_list); p_node; p_node = list_next(p_node)) { + p_dev_rec = list_node(p_node); + p_mgnt_cb->p_dev_rec = p_dev_rec; + if (btm_ble_match_random_bda(p_dev_rec)) { + break; + } + p_mgnt_cb->p_dev_rec = NULL; + } + btm_ble_resolve_address_cmpl(); + } else { + (*p_cback)(NULL, p); + } +#endif + +} + + +/******************************************************************************* +** address mapping between pseudo address and real connection address +*******************************************************************************/ +/******************************************************************************* +** +** Function btm_find_dev_by_identity_addr +** +** Description find the security record whose LE static address is matching +** +*******************************************************************************/ +tBTM_SEC_DEV_REC *btm_find_dev_by_identity_addr(BD_ADDR bd_addr, UINT8 addr_type) +{ +#if BLE_PRIVACY_SPT == TRUE + tBTM_SEC_DEV_REC *p_dev_rec = NULL; + list_node_t *p_node = NULL; + tSecDevContext context; + context.type = SEC_DEV_ID_ADDR; + context.context.p_bd_addr = bd_addr; + context.free_check = FALSE; + p_node = list_foreach(btm_cb.p_sec_dev_rec_list, btm_find_sec_dev_in_list, &context); + if (p_node) { + p_dev_rec = list_node(p_node); + if ((p_dev_rec->ble.static_addr_type & (~BLE_ADDR_TYPE_ID_BIT)) != + (addr_type & (~BLE_ADDR_TYPE_ID_BIT))) { + BTM_TRACE_WARNING("%s find pseudo->random match with diff addr type: %d vs %d", + __func__, p_dev_rec->ble.static_addr_type, addr_type); + } + } + return p_dev_rec; +#endif + return NULL; +} + +/******************************************************************************* +** +** Function btm_identity_addr_to_random_pseudo +** +** Description This function map a static BD address to a pseudo random address +** in security database. +** +*******************************************************************************/ +BOOLEAN btm_identity_addr_to_random_pseudo(BD_ADDR bd_addr, UINT8 *p_addr_type, BOOLEAN refresh) +{ +#if BLE_PRIVACY_SPT == TRUE + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev_by_identity_addr(bd_addr, *p_addr_type); + + BTM_TRACE_EVENT ("%s", __func__); + /* evt reported on static address, map static address to random pseudo */ + if (p_dev_rec != NULL) { + /* if RPA offloading is supported, or 4.2 controller, do RPA refresh */ + if (refresh && controller_get_interface()->get_ble_resolving_list_max_size() != 0) { + btm_ble_read_resolving_list_entry(p_dev_rec); + } + + /* assign the original address to be the current report address */ + if (!btm_ble_init_pseudo_addr (p_dev_rec, bd_addr)) { + memcpy(bd_addr, p_dev_rec->ble.pseudo_addr, BD_ADDR_LEN); + } + + *p_addr_type = p_dev_rec->ble.ble_addr_type; + return TRUE; + } +#endif + return FALSE; +} + +/******************************************************************************* +** +** Function btm_random_pseudo_to_identity_addr +** +** Description This function map a random pseudo address to a public address +** random_pseudo is input and output parameter +** +*******************************************************************************/ +BOOLEAN btm_random_pseudo_to_identity_addr(BD_ADDR random_pseudo, UINT8 *p_static_addr_type) +{ +#if BLE_PRIVACY_SPT == TRUE + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev (random_pseudo); + + if (p_dev_rec != NULL) { + if (p_dev_rec->ble.in_controller_list & BTM_RESOLVING_LIST_BIT) { + * p_static_addr_type = p_dev_rec->ble.static_addr_type; + memcpy(random_pseudo, p_dev_rec->ble.static_addr, BD_ADDR_LEN); + if (controller_get_interface()->supports_ble_privacy() && p_dev_rec->ble.ble_addr_type != BLE_ADDR_PUBLIC) { + *p_static_addr_type |= BLE_ADDR_TYPE_ID_BIT; + } + return TRUE; + } + } +#endif + return FALSE; +} + +/******************************************************************************* +** +** Function btm_ble_refresh_peer_resolvable_private_addr +** +** Description This function refresh the currently used resolvable remote private address into security +** database and set active connection address. +** +*******************************************************************************/ +void btm_ble_refresh_peer_resolvable_private_addr(BD_ADDR pseudo_bda, BD_ADDR rpa, + UINT8 rra_type) +{ +#if BLE_PRIVACY_SPT == TRUE + UINT8 rra_dummy = FALSE; + BD_ADDR dummy_bda = {0}; + + if (memcmp(dummy_bda, rpa, BD_ADDR_LEN) == 0) { + rra_dummy = TRUE; + } + + /* update security record here, in adv event or connection complete process */ + tBTM_SEC_DEV_REC *p_sec_rec = btm_find_dev(pseudo_bda); + if (p_sec_rec != NULL) { + memcpy(p_sec_rec->ble.cur_rand_addr, rpa, BD_ADDR_LEN); + + /* unknown, if dummy address, set to static */ + if (rra_type == BTM_BLE_ADDR_PSEUDO) { + p_sec_rec->ble.active_addr_type = rra_dummy ? BTM_BLE_ADDR_STATIC : BTM_BLE_ADDR_RRA; + } else { + p_sec_rec->ble.active_addr_type = rra_type; + } + } else { + BTM_TRACE_ERROR("No matching known device in record"); + return; + } + + BTM_TRACE_DEBUG("%s: active_addr_type: %d ", + __func__, p_sec_rec->ble.active_addr_type); + + /* connection refresh remote address */ + tACL_CONN *p_acl = btm_bda_to_acl(p_sec_rec->bd_addr, BT_TRANSPORT_LE); + if (p_acl == NULL) { + p_acl = btm_bda_to_acl(p_sec_rec->ble.pseudo_addr, BT_TRANSPORT_LE); + } + + if (p_acl != NULL) { + if (rra_type == BTM_BLE_ADDR_PSEUDO) { + /* use static address, resolvable_private_addr is empty */ + if (rra_dummy) { + p_acl->active_remote_addr_type = p_sec_rec->ble.static_addr_type; + memcpy(p_acl->active_remote_addr, p_sec_rec->ble.static_addr, BD_ADDR_LEN); + } else { + p_acl->active_remote_addr_type = BLE_ADDR_RANDOM; + memcpy(p_acl->active_remote_addr, rpa, BD_ADDR_LEN); + } + } else { + p_acl->active_remote_addr_type = rra_type; + memcpy(p_acl->active_remote_addr, rpa, BD_ADDR_LEN); + } + + BTM_TRACE_DEBUG("p_acl->active_remote_addr_type: %d ", p_acl->active_remote_addr_type); + BTM_TRACE_DEBUG("%s conn_addr: %02x:%02x:%02x:%02x:%02x:%02x", + __func__, p_acl->active_remote_addr[0], p_acl->active_remote_addr[1], + p_acl->active_remote_addr[2], p_acl->active_remote_addr[3], + p_acl->active_remote_addr[4], p_acl->active_remote_addr[5]); + } +#endif +} + +/******************************************************************************* +** +** Function btm_ble_refresh_local_resolvable_private_addr +** +** Description This function refresh the currently used resolvable private address for the +** active link to the remote device +** +*******************************************************************************/ +void btm_ble_refresh_local_resolvable_private_addr(BD_ADDR pseudo_addr, + BD_ADDR local_rpa) +{ +#if BLE_PRIVACY_SPT == TRUE + tACL_CONN *p = btm_bda_to_acl(pseudo_addr, BT_TRANSPORT_LE); + BD_ADDR dummy_bda = {0}; + + if (p != NULL) { + if (btm_cb.ble_ctr_cb.addr_mgnt_cb.own_addr_type >= BLE_ADDR_RANDOM) { + p->conn_addr_type = BLE_ADDR_RANDOM; + if (memcmp(local_rpa, dummy_bda, BD_ADDR_LEN)) { + memcpy(p->conn_addr, local_rpa, BD_ADDR_LEN); + } else { + memcpy(p->conn_addr, btm_cb.ble_ctr_cb.addr_mgnt_cb.private_addr, BD_ADDR_LEN); + } + } else { + p->conn_addr_type = BLE_ADDR_PUBLIC; + memcpy(p->conn_addr, &controller_get_interface()->get_address()->address, BD_ADDR_LEN); + } + } +#endif +} +#endif diff --git a/lib/bt/host/bluedroid/stack/btm/btm_ble_adv_filter.c b/lib/bt/host/bluedroid/stack/btm/btm_ble_adv_filter.c new file mode 100644 index 00000000..c0ae0aaa --- /dev/null +++ b/lib/bt/host/bluedroid/stack/btm/btm_ble_adv_filter.c @@ -0,0 +1,1306 @@ +/****************************************************************************** + * + * Copyright (C) 2014 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +//#define LOG_TAG "bt_btm_ble" + +#include +#include "common/bt_target.h" + +#if (BLE_INCLUDED == TRUE) +#include "stack/bt_types.h" +#include "stack/hcimsgs.h" +#include "stack/btu.h" +#include "btm_int.h" +#include "osi/allocator.h" +#include "stack/hcidefs.h" +#include "stack/btm_ble_api.h" +#include "device/controller.h" + +#define BTM_BLE_ADV_FILT_META_HDR_LENGTH 3 +#define BTM_BLE_ADV_FILT_FEAT_SELN_LEN 13 +#define BTM_BLE_ADV_FILT_TRACK_NUM 2 + +#define BTM_BLE_PF_SELECT_NONE 0 + +/* BLE meta vsc header: 1 bytes of sub_code, 1 byte of PCF action */ +#define BTM_BLE_META_HDR_LENGTH 3 +#define BTM_BLE_PF_FEAT_SEL_LEN 18 +#define BTM_BLE_PCF_ENABLE_LEN 2 + +#define BTM_BLE_META_ADDR_LEN 7 +#define BTM_BLE_META_UUID_LEN 40 + +#define BTM_BLE_PF_BIT_TO_MASK(x) (UINT16)(1 << (x)) + + +#if BTM_DYNAMIC_MEMORY == FALSE +tBTM_BLE_ADV_FILTER_CB btm_ble_adv_filt_cb; +tBTM_BLE_VSC_CB cmn_ble_adv_vsc_cb; +#else +tBTM_BLE_ADV_FILTER_CB *btm_ble_adv_filt_cb_ptr; +tBTM_BLE_VSC_CB *cmn_ble_adv_vsc_cb_ptr; +#define btm_ble_adv_filt_cb (*btm_ble_adv_filt_cb_ptr) +#define cmn_ble_adv_vsc_cb (*cmn_ble_adv_vsc_cb_ptr) +#endif + +static const BD_ADDR na_bda = {0}; + +static UINT8 btm_ble_cs_update_pf_counter(tBTM_BLE_SCAN_COND_OP action, + UINT8 cond_type, tBLE_BD_ADDR *p_bd_addr, UINT8 num_available); + +#define BTM_BLE_SET_SCAN_PF_OPCODE(x, y) (((x)<<4)|y) +#define BTM_BLE_GET_SCAN_PF_SUBCODE(x) ((x) >> 4) +#define BTM_BLE_GET_SCAN_PF_ACTION(x) ((x) & 0x0f) +#define BTM_BLE_INVALID_COUNTER 0xff + + +/* length of each multi adv sub command */ +#define BTM_BLE_ADV_FILTER_ENB_LEN 3 + +/* length of each batch scan command */ +#define BTM_BLE_ADV_FILTER_CLEAR_LEN 3 +#define BTM_BLE_ADV_FILTER_LEN 2 + +#define BTM_BLE_ADV_FILT_CB_EVT_MASK 0xF0 +#define BTM_BLE_ADV_FILT_SUBCODE_MASK 0x0F + +/******************************************************************************* +** +** Function btm_ble_obtain_vsc_details +** +** Description This function obtains the VSC details +** +** Parameters +** +** Returns status +** +*******************************************************************************/ +tBTM_STATUS btm_ble_obtain_vsc_details(void) +{ + tBTM_STATUS st = BTM_SUCCESS; + +#if BLE_VND_INCLUDED == TRUE + BTM_BleGetVendorCapabilities(&cmn_ble_adv_vsc_cb); + if (0 == cmn_ble_adv_vsc_cb.max_filter) { + st = BTM_MODE_UNSUPPORTED; + return st; + } +#else + cmn_ble_adv_vsc_cb.max_filter = BTM_BLE_MAX_FILTER_COUNTER; +#endif + return st; +} + +/******************************************************************************* +** +** Function btm_ble_advfilt_enq_op_q +** +** Description enqueue an adv filter operation in q to check command complete +** status +** +** Returns void +** +*******************************************************************************/ +void btm_ble_advfilt_enq_op_q(UINT8 action, UINT8 ocf, tBTM_BLE_FILT_CB_EVT cb_evt, + tBTM_BLE_REF_VALUE ref, tBTM_BLE_PF_CFG_CBACK *p_cmpl_cback, + tBTM_BLE_PF_PARAM_CBACK *p_filt_param_cback) +{ + btm_ble_adv_filt_cb.op_q.action_ocf[btm_ble_adv_filt_cb.op_q.next_idx] = (action | (ocf << 4)); + btm_ble_adv_filt_cb.op_q.ref_value[btm_ble_adv_filt_cb.op_q.next_idx] = ref; + btm_ble_adv_filt_cb.op_q.cb_evt[btm_ble_adv_filt_cb.op_q.next_idx] = cb_evt; + btm_ble_adv_filt_cb.op_q.p_scan_cfg_cback[btm_ble_adv_filt_cb.op_q.next_idx] = p_cmpl_cback; + btm_ble_adv_filt_cb.op_q.p_filt_param_cback[btm_ble_adv_filt_cb.op_q.next_idx] + = p_filt_param_cback; + BTM_TRACE_DEBUG("btm_ble_advfilt_enq_op_q: act_ocf:%d, action:%d, ocf:%d,cb_evt;%d, cback:%p", + btm_ble_adv_filt_cb.op_q.action_ocf[btm_ble_adv_filt_cb.op_q.next_idx], action, + ocf, cb_evt, p_cmpl_cback); + btm_ble_adv_filt_cb.op_q.next_idx = (btm_ble_adv_filt_cb.op_q.next_idx + 1) + % BTM_BLE_PF_TYPE_MAX; +} + +/******************************************************************************* +** +** Function btm_ble_advfilt_deq_op_q +** +** Description dequeue an adv filter operation from q when command complete +** is received +** +** Returns void +** +*******************************************************************************/ +void btm_ble_advfilt_deq_op_q(UINT8 *p_action, UINT8 *p_ocf, tBTM_BLE_FILT_CB_EVT *p_cb_evt, + tBTM_BLE_REF_VALUE *p_ref, tBTM_BLE_PF_CFG_CBACK **p_cmpl_cback, + tBTM_BLE_PF_PARAM_CBACK **p_filt_param_cback) +{ + *p_ocf = (btm_ble_adv_filt_cb.op_q.action_ocf[btm_ble_adv_filt_cb.op_q.pending_idx] >> 4); + *p_action = (btm_ble_adv_filt_cb.op_q.action_ocf[btm_ble_adv_filt_cb.op_q.pending_idx] + & BTM_BLE_ADV_FILT_SUBCODE_MASK); + *p_ref = btm_ble_adv_filt_cb.op_q.ref_value[btm_ble_adv_filt_cb.op_q.pending_idx]; + *p_cb_evt = btm_ble_adv_filt_cb.op_q.cb_evt[btm_ble_adv_filt_cb.op_q.pending_idx]; + *p_cmpl_cback = btm_ble_adv_filt_cb.op_q.p_scan_cfg_cback[btm_ble_adv_filt_cb.op_q.pending_idx]; + *p_filt_param_cback = + btm_ble_adv_filt_cb.op_q.p_filt_param_cback[btm_ble_adv_filt_cb.op_q.pending_idx]; + + btm_ble_adv_filt_cb.op_q.pending_idx = (btm_ble_adv_filt_cb.op_q.pending_idx + 1) + % BTM_BLE_PF_TYPE_MAX; + BTM_TRACE_DEBUG("btm_ble_advfilt_deq_op_q: ocf:%d, action:%d, ref_value:%d, cb_evt:%x", + *p_ocf, *p_action, *p_ref, *p_cb_evt); +} + +/******************************************************************************* +** +** Function btm_ble_condtype_to_ocf +** +** Description Convert cond_type to OCF +** +** Returns Returns ocf value +** +*******************************************************************************/ +UINT8 btm_ble_condtype_to_ocf(UINT8 cond_type) +{ + UINT8 ocf = 0; + + switch (cond_type) { + case BTM_BLE_PF_ADDR_FILTER: + ocf = BTM_BLE_META_PF_ADDR; + break; + case BTM_BLE_PF_SRVC_UUID: + ocf = BTM_BLE_META_PF_UUID; + break; + case BTM_BLE_PF_SRVC_SOL_UUID: + ocf = BTM_BLE_META_PF_SOL_UUID; + break; + case BTM_BLE_PF_LOCAL_NAME: + ocf = BTM_BLE_META_PF_LOCAL_NAME; + break; + case BTM_BLE_PF_MANU_DATA: + ocf = BTM_BLE_META_PF_MANU_DATA; + break; + case BTM_BLE_PF_SRVC_DATA_PATTERN: + ocf = BTM_BLE_META_PF_SRVC_DATA; + break; + case BTM_BLE_PF_TYPE_ALL: + ocf = BTM_BLE_META_PF_ALL; + break; + default: + ocf = BTM_BLE_PF_TYPE_MAX; + break; + } + return ocf; +} + +/******************************************************************************* +** +** Function btm_ble_ocf_to_condtype +** +** Description Convert OCF to cond type +** +** Returns Returns condtype value +** +*******************************************************************************/ +UINT8 btm_ble_ocf_to_condtype(UINT8 ocf) +{ + UINT8 cond_type = 0; + + switch (ocf) { + case BTM_BLE_META_PF_FEAT_SEL: + cond_type = BTM_BLE_META_PF_FEAT_SEL; + break; + case BTM_BLE_META_PF_ADDR: + cond_type = BTM_BLE_PF_ADDR_FILTER; + break; + case BTM_BLE_META_PF_UUID: + cond_type = BTM_BLE_PF_SRVC_UUID; + break; + case BTM_BLE_META_PF_SOL_UUID: + cond_type = BTM_BLE_PF_SRVC_SOL_UUID; + break; + case BTM_BLE_META_PF_LOCAL_NAME: + cond_type = BTM_BLE_PF_LOCAL_NAME; + break; + case BTM_BLE_META_PF_MANU_DATA: + cond_type = BTM_BLE_PF_MANU_DATA; + break; + case BTM_BLE_META_PF_SRVC_DATA: + cond_type = BTM_BLE_PF_SRVC_DATA_PATTERN; + break; + case BTM_BLE_META_PF_ALL: + cond_type = BTM_BLE_PF_TYPE_ALL; + break; + default: + cond_type = BTM_BLE_PF_TYPE_MAX; + break; + } + return cond_type; +} + +/******************************************************************************* +** +** Function btm_ble_scan_pf_cmpl_cback +** +** Description the BTM BLE customer feature VSC complete callback for ADV PF filtering +** +** Returns pointer to the counter if found; NULL otherwise. +** +*******************************************************************************/ +void btm_ble_scan_pf_cmpl_cback(tBTM_VSC_CMPL *p_params) +{ + UINT8 status = 0; + UINT8 *p = p_params->p_param_buf, op_subcode = 0, action = 0xff; + UINT16 evt_len = p_params->param_len; + UINT8 ocf = BTM_BLE_META_PF_ALL, cond_type = 0; + UINT8 num_avail = 0, cb_evt = 0; + tBTM_BLE_REF_VALUE ref_value = 0; + tBTM_BLE_PF_CFG_CBACK *p_scan_cfg_cback = NULL; + tBTM_BLE_PF_PARAM_CBACK *p_filt_param_cback = NULL; + + if (evt_len < 3 || evt_len > 4) { + BTM_TRACE_ERROR("%s cannot interpret APCF callback status = %d, length = %d", + __func__, status, evt_len); + btm_ble_advfilt_deq_op_q(&action, &ocf, &cb_evt, &ref_value, &p_scan_cfg_cback, + &p_filt_param_cback); + return; + } + + btm_ble_advfilt_deq_op_q(&action, &ocf, &cb_evt, &ref_value, &p_scan_cfg_cback, + &p_filt_param_cback); + + STREAM_TO_UINT8(status, p); + STREAM_TO_UINT8(op_subcode, p); + STREAM_TO_UINT8(action, p); + + /* Ignore the event, if it is not the same one expected */ + if (3 == evt_len) { + if (ocf != op_subcode) { + BTM_TRACE_ERROR("btm_ble_scan_pf_cmpl_cback:3-Incorrect opcode :%d, %d, %d, %d, %d, %d", + ocf, op_subcode, action, evt_len, ref_value, status); + return; + } else { + if (NULL != btm_ble_adv_filt_cb.p_filt_stat_cback) { + btm_ble_adv_filt_cb.p_filt_stat_cback(action, status, ref_value); + } + BTM_TRACE_DEBUG("btm_ble_scan_pf_cmpl_cback enabled/disabled, %d, %d, %d, %d", + ocf, action, status, ref_value); + return; + } + } + + if (4 == evt_len && ocf != op_subcode) { + BTM_TRACE_ERROR("btm_ble_scan_pf_cmpl_cback:4-Incorrect opcode: %d, %d, %d, %d, %d", + ocf, op_subcode, action, status, ref_value); + return; + } + + STREAM_TO_UINT8(num_avail, p); + switch (op_subcode) { + case BTM_BLE_META_PF_ADDR: + case BTM_BLE_META_PF_UUID: + case BTM_BLE_META_PF_SOL_UUID: + case BTM_BLE_META_PF_LOCAL_NAME: + case BTM_BLE_META_PF_MANU_DATA: + case BTM_BLE_META_PF_SRVC_DATA: + cond_type = btm_ble_ocf_to_condtype(ocf); + BTM_TRACE_DEBUG("btm_ble_scan_pf_cmpl_cback Recd: %d, %d, %d, %d, %d, %d", op_subcode, + ocf, action, status, ref_value, num_avail); + if (HCI_SUCCESS == status) { + if (memcmp(&btm_ble_adv_filt_cb.cur_filter_target.bda, &na_bda, BD_ADDR_LEN) == 0) { + btm_ble_cs_update_pf_counter(action, cond_type, NULL, num_avail); + } else { + btm_ble_cs_update_pf_counter(action, cond_type, + &btm_ble_adv_filt_cb.cur_filter_target, num_avail); + } + } + + /* send ADV PF operation complete */ + btm_ble_adv_filt_cb.op_type = 0; + break; + + case BTM_BLE_META_PF_FEAT_SEL: + BTM_TRACE_DEBUG("btm_ble_scan_pf_cmpl_cback-Feat sel event: %d, %d, %d, %d", + action, status, ref_value, num_avail); + break; + + default: + BTM_TRACE_ERROR("btm_ble_scan_pf_cmpl_cback: unknown operation: %d", op_subcode); + break; + } + + BTM_TRACE_DEBUG("btm_ble_scan_pf_cmpl_cback: calling the cback: %d", cb_evt); + switch (cb_evt) { + case BTM_BLE_FILT_CFG: + if (NULL != p_scan_cfg_cback) { + p_scan_cfg_cback(action, cond_type, num_avail, status, ref_value); + } + break; + case BTM_BLE_FILT_ADV_PARAM: + if (NULL != p_filt_param_cback) { + p_filt_param_cback(action, num_avail, ref_value, status); + } + break; + default: + break; + } +} + +/******************************************************************************* +** +** Function btm_ble_find_addr_filter_counter +** +** Description find the per bd address ADV payload filter counter by BD_ADDR. +** +** Returns pointer to the counter if found; NULL otherwise. +** +*******************************************************************************/ +tBTM_BLE_PF_COUNT *btm_ble_find_addr_filter_counter(tBLE_BD_ADDR *p_le_bda) +{ + UINT8 i; + tBTM_BLE_PF_COUNT *p_addr_filter = &btm_ble_adv_filt_cb.p_addr_filter_count[1]; + + if (p_le_bda == NULL) { + return &btm_ble_adv_filt_cb.p_addr_filter_count[0]; + } + + for (i = 0; i < cmn_ble_adv_vsc_cb.max_filter; i ++, p_addr_filter ++) { + if (p_addr_filter->in_use && + memcmp(p_le_bda->bda, p_addr_filter->bd_addr, BD_ADDR_LEN) == 0) { + return p_addr_filter; + } + } + return NULL; +} + +/******************************************************************************* +** +** Function btm_ble_alloc_addr_filter_counter +** +** Description allocate the per device adv payload filter counter. +** +** Returns pointer to the counter if allocation succeed; NULL otherwise. +** +*******************************************************************************/ +tBTM_BLE_PF_COUNT *btm_ble_alloc_addr_filter_counter(BD_ADDR bd_addr) +{ + UINT8 i; + tBTM_BLE_PF_COUNT *p_addr_filter = &btm_ble_adv_filt_cb.p_addr_filter_count[1]; + + for (i = 0; i < cmn_ble_adv_vsc_cb.max_filter; i ++, p_addr_filter ++) { + if (memcmp(na_bda, p_addr_filter->bd_addr, BD_ADDR_LEN) == 0) { + memcpy(p_addr_filter->bd_addr, bd_addr, BD_ADDR_LEN); + p_addr_filter->in_use = TRUE; + return p_addr_filter; + } + } + return NULL; +} +/******************************************************************************* +** +** Function btm_ble_dealloc_addr_filter_counter +** +** Description de-allocate the per device adv payload filter counter. +** +** Returns TRUE if deallocation succeed; FALSE otherwise. +** +*******************************************************************************/ +BOOLEAN btm_ble_dealloc_addr_filter_counter(tBLE_BD_ADDR *p_bd_addr, UINT8 filter_type) +{ + UINT8 i; + tBTM_BLE_PF_COUNT *p_addr_filter = &btm_ble_adv_filt_cb.p_addr_filter_count[1]; + BOOLEAN found = FALSE; + + if (BTM_BLE_PF_TYPE_ALL == filter_type && NULL == p_bd_addr) { + memset(&btm_ble_adv_filt_cb.p_addr_filter_count[0], 0, sizeof(tBTM_BLE_PF_COUNT)); + } + + for (i = 0; i < cmn_ble_adv_vsc_cb.max_filter; i ++, p_addr_filter ++) { + if ((p_addr_filter->in_use) && (NULL == p_bd_addr || + (NULL != p_bd_addr && + memcmp(p_bd_addr->bda, p_addr_filter->bd_addr, BD_ADDR_LEN) == 0))) { + found = TRUE; + memset(p_addr_filter, 0, sizeof(tBTM_BLE_PF_COUNT)); + + if (NULL != p_bd_addr) { + break; + } + } + } + return found; +} + +/******************************************************************************* +** +** Function btm_ble_update_pf_local_name +** +** Description this function update(add,delete or clear) the adv lcoal name filtering condition. +** +** +** Returns BTM_SUCCESS if sucessful, +** BTM_ILLEGAL_VALUE if paramter is not valid. +** +*******************************************************************************/ +tBTM_STATUS btm_ble_update_pf_local_name(tBTM_BLE_SCAN_COND_OP action, + tBTM_BLE_PF_FILT_INDEX filt_index, + tBTM_BLE_PF_COND_PARAM *p_cond) +{ + tBTM_BLE_PF_LOCAL_NAME_COND *p_local_name = (p_cond == NULL) ? NULL : &p_cond->local_name; + UINT8 param[BTM_BLE_PF_STR_LEN_MAX + BTM_BLE_ADV_FILT_META_HDR_LENGTH], + *p = param, + len = BTM_BLE_ADV_FILT_META_HDR_LENGTH; + tBTM_STATUS st = BTM_ILLEGAL_VALUE; + + memset(param, 0, BTM_BLE_PF_STR_LEN_MAX + BTM_BLE_ADV_FILT_META_HDR_LENGTH); + + UINT8_TO_STREAM(p, BTM_BLE_META_PF_LOCAL_NAME); + UINT8_TO_STREAM(p, action); + + /* Filter index */ + UINT8_TO_STREAM(p, filt_index); + + if (BTM_BLE_SCAN_COND_ADD == action || + BTM_BLE_SCAN_COND_DELETE == action) { + if (NULL == p_local_name) { + return st; + } + + if (p_local_name->data_len > BTM_BLE_PF_STR_LEN_MAX) { + p_local_name->data_len = BTM_BLE_PF_STR_LEN_MAX; + } + + ARRAY_TO_STREAM(p, p_local_name->p_data, p_local_name->data_len); + len += p_local_name->data_len; + } + + /* send local name filter */ + if ((st = BTM_VendorSpecificCommand (HCI_BLE_ADV_FILTER_OCF, + len, + param, + btm_ble_scan_pf_cmpl_cback)) + != BTM_NO_RESOURCES) { + memset(&btm_ble_adv_filt_cb.cur_filter_target, 0, sizeof(tBLE_BD_ADDR)); + } else { + BTM_TRACE_ERROR("Local Name PF filter update failed"); + } + + return st; +} + + +/******************************************************************************* +** +** Function btm_ble_update_srvc_data_change +** +** Description this function update(add/remove) service data change filter. +** +** +** Returns BTM_SUCCESS if sucessful, +** BTM_ILLEGAL_VALUE if paramter is not valid. +** +*******************************************************************************/ +tBTM_STATUS btm_ble_update_srvc_data_change(tBTM_BLE_SCAN_COND_OP action, + tBTM_BLE_PF_FILT_INDEX filt_index, + tBTM_BLE_PF_COND_PARAM *p_cond) +{ + tBTM_STATUS st = BTM_ILLEGAL_VALUE; + tBLE_BD_ADDR *p_bd_addr = p_cond ? &p_cond->target_addr : NULL; + UINT8 num_avail = (action == BTM_BLE_SCAN_COND_ADD) ? 0 : 1; + + if (btm_ble_cs_update_pf_counter (action, BTM_BLE_PF_SRVC_DATA, p_bd_addr, num_avail) + != BTM_BLE_INVALID_COUNTER) { + st = BTM_SUCCESS; + } + + return st; +} + +/******************************************************************************* +** +** Function btm_ble_update_pf_manu_data +** +** Description this function update(add,delete or clear) the adv manufacturer +** data filtering condition. +** +** +** Returns BTM_SUCCESS if sucessful, +** BTM_ILLEGAL_VALUE if paramter is not valid. +** +*******************************************************************************/ +tBTM_STATUS btm_ble_update_pf_manu_data(tBTM_BLE_SCAN_COND_OP action, + tBTM_BLE_PF_FILT_INDEX filt_index, + tBTM_BLE_PF_COND_PARAM *p_data, + tBTM_BLE_PF_COND_TYPE cond_type, + tBTM_BLE_FILT_CB_EVT cb_evt, + tBTM_BLE_REF_VALUE ref_value) +{ + tBTM_BLE_PF_MANU_COND *p_manu_data = (p_data == NULL) ? NULL : &p_data->manu_data; + tBTM_BLE_PF_SRVC_PATTERN_COND *p_srvc_data = (p_data == NULL) ? NULL : &p_data->srvc_data; + + UINT8 param[BTM_BLE_PF_STR_LEN_MAX + BTM_BLE_PF_STR_LEN_MAX + BTM_BLE_ADV_FILT_META_HDR_LENGTH], + *p = param, + len = BTM_BLE_ADV_FILT_META_HDR_LENGTH; + tBTM_STATUS st = BTM_ILLEGAL_VALUE; + + if (NULL == p_data) { + return st; + } + + memset(param, 0, BTM_BLE_PF_STR_LEN_MAX + BTM_BLE_PF_STR_LEN_MAX + + BTM_BLE_ADV_FILT_META_HDR_LENGTH); + + if (BTM_BLE_PF_SRVC_DATA_PATTERN == cond_type) { + UINT8_TO_STREAM(p, BTM_BLE_META_PF_SRVC_DATA); + } else { + UINT8_TO_STREAM(p, BTM_BLE_META_PF_MANU_DATA); + } + + UINT8_TO_STREAM(p, action); + + /* Filter index */ + UINT8_TO_STREAM(p, filt_index); + + if (BTM_BLE_SCAN_COND_ADD == action || BTM_BLE_SCAN_COND_DELETE == action) { + if (BTM_BLE_PF_SRVC_DATA_PATTERN == cond_type) { + if (NULL == p_srvc_data) { + return st; + } + if (p_srvc_data->data_len > (BTM_BLE_PF_STR_LEN_MAX - 2)) { + p_srvc_data->data_len = (BTM_BLE_PF_STR_LEN_MAX - 2); + } + + if (p_srvc_data->data_len > 0) { + ARRAY_TO_STREAM(p, p_srvc_data->p_pattern, p_srvc_data->data_len); + len += (p_srvc_data->data_len); + ARRAY_TO_STREAM(p, p_srvc_data->p_pattern_mask, p_srvc_data->data_len); + } + + len += (p_srvc_data->data_len); + BTM_TRACE_DEBUG("Service data length: %d", len); + } else { + if (NULL == p_manu_data) { + BTM_TRACE_ERROR("btm_ble_update_pf_manu_data - No manuf data"); + return st; + } + BTM_TRACE_EVENT("btm_ble_update_pf_manu_data length: %d", + p_manu_data->data_len); + if (p_manu_data->data_len > (BTM_BLE_PF_STR_LEN_MAX - 2)) { + p_manu_data->data_len = (BTM_BLE_PF_STR_LEN_MAX - 2); + } + + UINT16_TO_STREAM(p, p_manu_data->company_id); + if (p_manu_data->data_len > 0 && p_manu_data->p_pattern_mask != NULL) { + ARRAY_TO_STREAM(p, p_manu_data->p_pattern, p_manu_data->data_len); + len += (p_manu_data->data_len + 2); + } else { + len += 2; + } + + if (p_manu_data->company_id_mask != 0) { + UINT16_TO_STREAM (p, p_manu_data->company_id_mask); + } else { + memset(p, 0xff, 2); + p += 2; + } + len += 2; + + if (p_manu_data->data_len > 0 && p_manu_data->p_pattern_mask != NULL) { + ARRAY_TO_STREAM(p, p_manu_data->p_pattern_mask, p_manu_data->data_len); + len += (p_manu_data->data_len); + } + + BTM_TRACE_DEBUG("Manuf data length: %d", len); + } + } + + /* send manufacturer*/ + if ((st = BTM_VendorSpecificCommand (HCI_BLE_ADV_FILTER_OCF, + len, + param, + btm_ble_scan_pf_cmpl_cback)) != BTM_NO_RESOURCES) { + memset(&btm_ble_adv_filt_cb.cur_filter_target, 0, sizeof(tBLE_BD_ADDR)); + } else { + BTM_TRACE_ERROR("manufacturer data PF filter update failed"); + } + + return st; +} + +/******************************************************************************* +** +** Function btm_ble_cs_update_pf_counter +** +** Description this function is to update the adv data payload filter counter +** +** Returns current number of the counter; BTM_BLE_INVALID_COUNTER if +** counter update failed. +** +*******************************************************************************/ +UINT8 btm_ble_cs_update_pf_counter(tBTM_BLE_SCAN_COND_OP action, + UINT8 cond_type, tBLE_BD_ADDR *p_bd_addr, + UINT8 num_available) +{ + tBTM_BLE_PF_COUNT *p_addr_filter = NULL; + UINT8 *p_counter = NULL; + + btm_ble_obtain_vsc_details(); + + if (cond_type > BTM_BLE_PF_TYPE_ALL) { + BTM_TRACE_ERROR("unknown PF filter condition type %d", cond_type); + return BTM_BLE_INVALID_COUNTER; + } + + /* for these three types of filter, always generic */ + if (BTM_BLE_PF_ADDR_FILTER == cond_type || + BTM_BLE_PF_MANU_DATA == cond_type || + BTM_BLE_PF_LOCAL_NAME == cond_type || + BTM_BLE_PF_SRVC_DATA_PATTERN == cond_type) { + p_bd_addr = NULL; + } + + if ((p_addr_filter = btm_ble_find_addr_filter_counter(p_bd_addr)) == NULL && + BTM_BLE_SCAN_COND_ADD == action) { + p_addr_filter = btm_ble_alloc_addr_filter_counter(p_bd_addr->bda); + } + + if (NULL != p_addr_filter) { + /* all filter just cleared */ + if ((BTM_BLE_PF_TYPE_ALL == cond_type && BTM_BLE_SCAN_COND_CLEAR == action) || + /* or bd address filter been deleted */ + (BTM_BLE_PF_ADDR_FILTER == cond_type && + (BTM_BLE_SCAN_COND_DELETE == action || BTM_BLE_SCAN_COND_CLEAR == action))) { + btm_ble_dealloc_addr_filter_counter(p_bd_addr, cond_type); + } + /* if not feature selection, update new addition/reduction of the filter counter */ + else if (cond_type != BTM_BLE_PF_TYPE_ALL) { + p_counter = p_addr_filter->pf_counter; + if (num_available > 0) { + p_counter[cond_type] += 1; + } + + BTM_TRACE_DEBUG("counter = %d, maxfilt = %d, num_avbl=%d", + p_counter[cond_type], cmn_ble_adv_vsc_cb.max_filter, num_available); + return p_counter[cond_type]; + } + } else { + BTM_TRACE_ERROR("no matching filter counter found"); + } + /* no matching filter located and updated */ + return BTM_BLE_INVALID_COUNTER; +} + + +/******************************************************************************* +** +** Function btm_ble_update_addr_filter +** +** Description this function update(add,delete or clear) the address filter of adv. +** +** +** Returns BTM_SUCCESS if sucessful, +** BTM_ILLEGAL_VALUE if paramter is not valid. +** +*******************************************************************************/ +tBTM_STATUS btm_ble_update_addr_filter(tBTM_BLE_SCAN_COND_OP action, + tBTM_BLE_PF_FILT_INDEX filt_index, + tBTM_BLE_PF_COND_PARAM *p_cond) +{ + UINT8 param[BTM_BLE_META_ADDR_LEN + BTM_BLE_ADV_FILT_META_HDR_LENGTH], + * p = param; + tBTM_STATUS st = BTM_ILLEGAL_VALUE; + tBLE_BD_ADDR *p_addr = (p_cond == NULL) ? NULL : &p_cond->target_addr; + + memset(param, 0, BTM_BLE_META_ADDR_LEN + BTM_BLE_ADV_FILT_META_HDR_LENGTH); + + UINT8_TO_STREAM(p, BTM_BLE_META_PF_ADDR); + UINT8_TO_STREAM(p, action); + + /* Filter index */ + UINT8_TO_STREAM(p, filt_index); + + if (BTM_BLE_SCAN_COND_ADD == action || + BTM_BLE_SCAN_COND_DELETE == action) { + if (NULL == p_addr) { + return st; + } + + BDADDR_TO_STREAM(p, p_addr->bda); + UINT8_TO_STREAM(p, p_addr->type); + } + /* send address filter */ + if ((st = BTM_VendorSpecificCommand (HCI_BLE_ADV_FILTER_OCF, + (UINT8)(BTM_BLE_ADV_FILT_META_HDR_LENGTH + BTM_BLE_META_ADDR_LEN), + param, + btm_ble_scan_pf_cmpl_cback)) != BTM_NO_RESOURCES) { + memset(&btm_ble_adv_filt_cb.cur_filter_target, 0, sizeof(tBLE_BD_ADDR)); + } else { + BTM_TRACE_ERROR("Broadcaster Address Filter Update failed"); + } + return st; +} + +/******************************************************************************* +** +** Function btm_ble_update_uuid_filter +** +** Description this function update(add,delete or clear) service UUID filter. +** +** +** Returns BTM_SUCCESS if sucessful, +** BTM_ILLEGAL_VALUE if paramter is not valid. +** +*******************************************************************************/ +tBTM_STATUS btm_ble_update_uuid_filter(tBTM_BLE_SCAN_COND_OP action, + tBTM_BLE_PF_FILT_INDEX filt_index, + tBTM_BLE_PF_COND_TYPE filter_type, + tBTM_BLE_PF_COND_PARAM *p_cond, + tBTM_BLE_FILT_CB_EVT cb_evt, + tBTM_BLE_REF_VALUE ref_value) +{ + UINT8 param[BTM_BLE_META_UUID_LEN + BTM_BLE_ADV_FILT_META_HDR_LENGTH], + * p = param, + len = BTM_BLE_ADV_FILT_META_HDR_LENGTH; + tBTM_STATUS st = BTM_ILLEGAL_VALUE; + tBTM_BLE_PF_UUID_COND *p_uuid_cond; + UINT8 evt_type; + + memset(param, 0, BTM_BLE_META_UUID_LEN + BTM_BLE_ADV_FILT_META_HDR_LENGTH); + + if (BTM_BLE_PF_SRVC_UUID == filter_type) { + evt_type = BTM_BLE_META_PF_UUID; + p_uuid_cond = p_cond ? &p_cond->srvc_uuid : NULL; + } else { + evt_type = BTM_BLE_META_PF_SOL_UUID; + p_uuid_cond = p_cond ? &p_cond->solicitate_uuid : NULL; + } + + if (NULL == p_uuid_cond && action != BTM_BLE_SCAN_COND_CLEAR) { + BTM_TRACE_ERROR("Illegal param for add/delete UUID filter"); + return st; + } + + /* need to add address filter first, if adding per bda UUID filter without address filter */ + if (BTM_BLE_SCAN_COND_ADD == action && NULL != p_uuid_cond && + p_uuid_cond->p_target_addr && + btm_ble_find_addr_filter_counter(p_uuid_cond->p_target_addr) == NULL) { + UINT8_TO_STREAM(p, BTM_BLE_META_PF_ADDR); + UINT8_TO_STREAM(p, action); + + /* Filter index */ + UINT8_TO_STREAM(p, filt_index); + + BDADDR_TO_STREAM(p, p_uuid_cond->p_target_addr->bda); + UINT8_TO_STREAM(p, p_uuid_cond->p_target_addr->type); + + /* send address filter */ + if ((st = BTM_VendorSpecificCommand (HCI_BLE_ADV_FILTER_OCF, + (UINT8)(BTM_BLE_ADV_FILT_META_HDR_LENGTH + BTM_BLE_META_ADDR_LEN), + param, + btm_ble_scan_pf_cmpl_cback)) == BTM_NO_RESOURCES) { + BTM_TRACE_ERROR("Update Address filter into controller failed."); + return st; + } + + btm_ble_advfilt_enq_op_q(action, BTM_BLE_META_PF_ADDR, cb_evt, ref_value, NULL, NULL); + BTM_TRACE_DEBUG("Updated Address filter"); + } + + p = param; + UINT8_TO_STREAM(p, evt_type); + UINT8_TO_STREAM(p, action); + + /* Filter index */ + UINT8_TO_STREAM(p, filt_index); + + if ((BTM_BLE_SCAN_COND_ADD == action || + BTM_BLE_SCAN_COND_DELETE == action) && + NULL != p_uuid_cond) { + if (p_uuid_cond->uuid.len == LEN_UUID_16) { + UINT16_TO_STREAM(p, p_uuid_cond->uuid.uu.uuid16); + len += LEN_UUID_16; + } else if (p_uuid_cond->uuid.len == LEN_UUID_32) { /*4 bytes */ + UINT32_TO_STREAM(p, p_uuid_cond->uuid.uu.uuid32); + len += LEN_UUID_32; + } else if (p_uuid_cond->uuid.len == LEN_UUID_128) { + ARRAY_TO_STREAM (p, p_uuid_cond->uuid.uu.uuid128, LEN_UUID_128); + len += LEN_UUID_128; + } else { + BTM_TRACE_ERROR("illegal UUID length: %d", p_uuid_cond->uuid.len); + return BTM_ILLEGAL_VALUE; + } + + if (NULL != p_uuid_cond->p_uuid_mask) { + if (p_uuid_cond->uuid.len == LEN_UUID_16) { + UINT16_TO_STREAM(p, p_uuid_cond->p_uuid_mask->uuid16_mask); + len += LEN_UUID_16; + } else if (p_uuid_cond->uuid.len == LEN_UUID_32) { /*4 bytes */ + UINT32_TO_STREAM(p, p_uuid_cond->p_uuid_mask->uuid32_mask); + len += LEN_UUID_32; + } else if (p_uuid_cond->uuid.len == LEN_UUID_128) { + ARRAY_TO_STREAM (p, p_uuid_cond->p_uuid_mask->uuid128_mask, LEN_UUID_128); + len += LEN_UUID_128; + } + } else { + memset(p, 0xff, p_uuid_cond->uuid.len); + len += p_uuid_cond->uuid.len; + } + BTM_TRACE_DEBUG("btm_ble_update_uuid_filter : %d, %d, %d, %d", filter_type, evt_type, + p_uuid_cond->uuid.len, len); + } + + /* send UUID filter update */ + if ((st = BTM_VendorSpecificCommand (HCI_BLE_ADV_FILTER_OCF, + len, + param, + btm_ble_scan_pf_cmpl_cback)) != BTM_NO_RESOURCES) { + if (p_uuid_cond && p_uuid_cond->p_target_addr) { + memcpy(&btm_ble_adv_filt_cb.cur_filter_target, p_uuid_cond->p_target_addr, + sizeof(tBLE_BD_ADDR)); + } + else { + memset(&btm_ble_adv_filt_cb.cur_filter_target, 0, sizeof(tBLE_BD_ADDR)); + } + } else { + BTM_TRACE_ERROR("UUID filter udpating failed"); + } + + return st; +} + + +/******************************************************************************* +** +** Function btm_ble_clear_scan_pf_filter +** +** Description clear all adv payload filter by de-select all the adv pf feature bits +** +** +** Returns BTM_SUCCESS if sucessful, +** BTM_ILLEGAL_VALUE if paramter is not valid. +** +*******************************************************************************/ +tBTM_STATUS btm_ble_clear_scan_pf_filter(tBTM_BLE_SCAN_COND_OP action, + tBTM_BLE_PF_FILT_INDEX filt_index, + tBTM_BLE_PF_COND_PARAM *p_cond, + tBTM_BLE_PF_CFG_CBACK *p_cmpl_cback, + tBTM_BLE_FILT_CB_EVT cb_evt, + tBTM_BLE_REF_VALUE ref_value) +{ + tBLE_BD_ADDR *p_target = (p_cond == NULL) ? NULL : &p_cond->target_addr; + tBTM_BLE_PF_COUNT *p_bda_filter; + tBTM_STATUS st = BTM_WRONG_MODE; + UINT8 param[20], *p; + + if (BTM_BLE_SCAN_COND_CLEAR != action) { + BTM_TRACE_ERROR("unable to perform action:%d for generic adv filter type", action); + return BTM_ILLEGAL_VALUE; + } + + p = param; + memset(param, 0, 20); + + p_bda_filter = btm_ble_find_addr_filter_counter(p_target); + + if (NULL == p_bda_filter || + /* not a generic filter */ + (p_target != NULL && p_bda_filter)) { + BTM_TRACE_ERROR("Error: Can not clear filter, No PF filter has been configured!"); + return st; + } + + /* clear the general filter entry */ + if (NULL == p_target) { + /* clear manufacturer data filter */ + st = btm_ble_update_pf_manu_data(BTM_BLE_SCAN_COND_CLEAR, filt_index, NULL, + BTM_BLE_PF_MANU_DATA, cb_evt, ref_value); + if (BTM_CMD_STARTED == st) { + btm_ble_advfilt_enq_op_q(action, BTM_BLE_META_PF_MANU_DATA, cb_evt, + ref_value, NULL, NULL); + } + + /* clear local name filter */ + st = btm_ble_update_pf_local_name(BTM_BLE_SCAN_COND_CLEAR, filt_index, NULL); + if (BTM_CMD_STARTED == st) { + btm_ble_advfilt_enq_op_q(action, BTM_BLE_META_PF_LOCAL_NAME, cb_evt, + ref_value, NULL, NULL); + } + + /* update the counter for service data */ + st = btm_ble_update_srvc_data_change(BTM_BLE_SCAN_COND_CLEAR, filt_index, NULL); + + /* clear UUID filter */ + st = btm_ble_update_uuid_filter(BTM_BLE_SCAN_COND_CLEAR, filt_index, + BTM_BLE_PF_SRVC_UUID, NULL, cb_evt, ref_value); + if (BTM_CMD_STARTED == st) { + btm_ble_advfilt_enq_op_q(action, BTM_BLE_META_PF_UUID, cb_evt, ref_value, NULL, NULL); + } + + st = btm_ble_update_uuid_filter(BTM_BLE_SCAN_COND_CLEAR, filt_index, + BTM_BLE_PF_SRVC_SOL_UUID, NULL, cb_evt, ref_value); + if (BTM_CMD_STARTED == st) { + btm_ble_advfilt_enq_op_q(action, BTM_BLE_META_PF_SOL_UUID, cb_evt, + ref_value, NULL, NULL); + } + + /* clear service data filter */ + st = btm_ble_update_pf_manu_data(BTM_BLE_SCAN_COND_CLEAR, filt_index, NULL, + BTM_BLE_PF_SRVC_DATA_PATTERN, cb_evt, ref_value); + if (BTM_CMD_STARTED == st) { + btm_ble_advfilt_enq_op_q(action, BTM_BLE_META_PF_SRVC_DATA, cb_evt, + ref_value, NULL, NULL); + } + } + + /* select feature based on control block settings */ + UINT8_TO_STREAM(p, BTM_BLE_META_PF_FEAT_SEL); + UINT8_TO_STREAM(p, BTM_BLE_SCAN_COND_CLEAR); + + /* Filter index */ + UINT8_TO_STREAM(p, filt_index); + + /* set PCF selection */ + UINT32_TO_STREAM(p, BTM_BLE_PF_SELECT_NONE); + /* set logic condition as OR as default */ + UINT8_TO_STREAM(p, BTM_BLE_PF_LOGIC_OR); + + if ((st = BTM_VendorSpecificCommand (HCI_BLE_ADV_FILTER_OCF, + (UINT8)(BTM_BLE_ADV_FILT_META_HDR_LENGTH + BTM_BLE_PF_FEAT_SEL_LEN), + param, + btm_ble_scan_pf_cmpl_cback)) + != BTM_NO_RESOURCES) { + if (p_target) { + memcpy(&btm_ble_adv_filt_cb.cur_filter_target, p_target, sizeof(tBLE_BD_ADDR)); + } else { + memset(&btm_ble_adv_filt_cb.cur_filter_target, 0, sizeof(tBLE_BD_ADDR)); + } + } + return st; +} + +/******************************************************************************* +** +** Function BTM_BleAdvFilterParamSetup +** +** Description This function is called to setup the adv data payload filter +** condition. +** +** Parameters action - Type of action to be performed +** filt_index - Filter index +** p_filt_params - Filter parameters +** p_target - Target device +** p_cmpl_back - Callback pointer +** ref_value - reference value +** +** Returns void +** +*******************************************************************************/ +tBTM_STATUS BTM_BleAdvFilterParamSetup(int action, tBTM_BLE_PF_FILT_INDEX filt_index, + tBTM_BLE_PF_FILT_PARAMS *p_filt_params, + tBLE_BD_ADDR *p_target, tBTM_BLE_PF_PARAM_CBACK *p_cmpl_cback, + tBTM_BLE_REF_VALUE ref_value) +{ + tBTM_STATUS st = BTM_WRONG_MODE; + tBTM_BLE_PF_COUNT *p_bda_filter = NULL; + UINT8 len = BTM_BLE_ADV_FILT_META_HDR_LENGTH + BTM_BLE_ADV_FILT_FEAT_SELN_LEN + + BTM_BLE_ADV_FILT_TRACK_NUM; + UINT8 param[len], *p; + + if (BTM_SUCCESS != btm_ble_obtain_vsc_details()) { + return st; + } + + p = param; + memset(param, 0, len); + BTM_TRACE_EVENT (" BTM_BleAdvFilterParamSetup"); + + if (BTM_BLE_SCAN_COND_ADD == action) { + p_bda_filter = btm_ble_find_addr_filter_counter(p_target); + if (NULL == p_bda_filter) { + BTM_TRACE_ERROR("BD Address not found!"); + return st; + } + + BTM_TRACE_DEBUG("BTM_BleAdvFilterParamSetup : Feat mask:%d", p_filt_params->feat_seln); + /* select feature based on control block settings */ + UINT8_TO_STREAM(p, BTM_BLE_META_PF_FEAT_SEL); + UINT8_TO_STREAM(p, BTM_BLE_SCAN_COND_ADD); + + /* Filter index */ + UINT8_TO_STREAM(p, filt_index); + + /* set PCF selection */ + UINT16_TO_STREAM(p, p_filt_params->feat_seln); + /* set logic type */ + UINT16_TO_STREAM(p, p_filt_params->logic_type); + /* set logic condition */ + UINT8_TO_STREAM(p, p_filt_params->filt_logic_type); + /* set RSSI high threshold */ + UINT8_TO_STREAM(p, p_filt_params->rssi_high_thres); + /* set delivery mode */ + UINT8_TO_STREAM(p, p_filt_params->dely_mode); + + if (0x01 == p_filt_params->dely_mode) { + /* set onfound timeout */ + UINT16_TO_STREAM(p, p_filt_params->found_timeout); + /* set onfound timeout count*/ + UINT8_TO_STREAM(p, p_filt_params->found_timeout_cnt); + /* set RSSI low threshold */ + UINT8_TO_STREAM(p, p_filt_params->rssi_low_thres); + /* set onlost timeout */ + UINT16_TO_STREAM(p, p_filt_params->lost_timeout); + /* set num_of_track_entries for firmware greater than L-release version */ + if (cmn_ble_adv_vsc_cb.version_supported > BTM_VSC_CHIP_CAPABILITY_L_VERSION) { + UINT16_TO_STREAM(p, p_filt_params->num_of_tracking_entries); + } + } + + if (cmn_ble_adv_vsc_cb.version_supported == BTM_VSC_CHIP_CAPABILITY_L_VERSION) { + len = BTM_BLE_ADV_FILT_META_HDR_LENGTH + BTM_BLE_ADV_FILT_FEAT_SELN_LEN; + } else { + len = BTM_BLE_ADV_FILT_META_HDR_LENGTH + BTM_BLE_ADV_FILT_FEAT_SELN_LEN + + BTM_BLE_ADV_FILT_TRACK_NUM; + } + + if ((st = BTM_VendorSpecificCommand (HCI_BLE_ADV_FILTER_OCF, + (UINT8)len, + param, + btm_ble_scan_pf_cmpl_cback)) + == BTM_NO_RESOURCES) { + return st; + } + btm_ble_advfilt_enq_op_q(action, BTM_BLE_META_PF_FEAT_SEL, BTM_BLE_FILT_ADV_PARAM, + ref_value, NULL, p_cmpl_cback); + } else if (BTM_BLE_SCAN_COND_DELETE == action) { + /* select feature based on control block settings */ + UINT8_TO_STREAM(p, BTM_BLE_META_PF_FEAT_SEL); + UINT8_TO_STREAM(p, BTM_BLE_SCAN_COND_DELETE); + /* Filter index */ + UINT8_TO_STREAM(p, filt_index); + + if ((st = BTM_VendorSpecificCommand (HCI_BLE_ADV_FILTER_OCF, + (UINT8)(BTM_BLE_ADV_FILT_META_HDR_LENGTH), + param, + btm_ble_scan_pf_cmpl_cback)) + == BTM_NO_RESOURCES) { + return st; + } + btm_ble_advfilt_enq_op_q(action, BTM_BLE_META_PF_FEAT_SEL, BTM_BLE_FILT_ADV_PARAM, + ref_value, NULL, p_cmpl_cback); + } else if (BTM_BLE_SCAN_COND_CLEAR == action) { + /* Deallocate all filters here */ + btm_ble_dealloc_addr_filter_counter(NULL, BTM_BLE_PF_TYPE_ALL); + + /* select feature based on control block settings */ + UINT8_TO_STREAM(p, BTM_BLE_META_PF_FEAT_SEL); + UINT8_TO_STREAM(p, BTM_BLE_SCAN_COND_CLEAR); + + if ((st = BTM_VendorSpecificCommand (HCI_BLE_ADV_FILTER_OCF, + (UINT8)(BTM_BLE_ADV_FILT_META_HDR_LENGTH - 1), + param, + btm_ble_scan_pf_cmpl_cback)) + == BTM_NO_RESOURCES) { + return st; + } + btm_ble_advfilt_enq_op_q(action, BTM_BLE_META_PF_FEAT_SEL, BTM_BLE_FILT_ADV_PARAM, + ref_value, NULL, p_cmpl_cback); + } + + return st; +} + +/******************************************************************************* +** +** Function BTM_BleEnableDisableFilterFeature +** +** Description This function is called to enable / disable the APCF feature +** +** Parameters enable the generic scan condition. +** enable: enable or disable the filter condition +** p_stat_cback - Status callback pointer +** ref_value - Ref value +** Returns void +** +*******************************************************************************/ +tBTM_STATUS BTM_BleEnableDisableFilterFeature(UINT8 enable, + tBTM_BLE_PF_STATUS_CBACK *p_stat_cback, + tBTM_BLE_REF_VALUE ref_value) +{ + UINT8 param[20], *p; + tBTM_STATUS st = BTM_WRONG_MODE; + + if (BTM_SUCCESS != btm_ble_obtain_vsc_details()) { + return st; + } + + p = param; + memset(param, 0, 20); + + /* enable the content filter in controller */ + p = param; + UINT8_TO_STREAM(p, BTM_BLE_META_PF_ENABLE); + /* enable adv data payload filtering */ + UINT8_TO_STREAM(p, enable); + + if ((st = BTM_VendorSpecificCommand (HCI_BLE_ADV_FILTER_OCF, + BTM_BLE_PCF_ENABLE_LEN, param, + btm_ble_scan_pf_cmpl_cback)) == BTM_CMD_STARTED) { + btm_ble_adv_filt_cb.p_filt_stat_cback = p_stat_cback; + btm_ble_advfilt_enq_op_q(enable, BTM_BLE_META_PF_ENABLE, BTM_BLE_FILT_ENABLE_DISABLE, + ref_value, NULL, NULL); + } + return st; +} + +/******************************************************************************* +** +** Function BTM_BleCfgFilterCondition +** +** Description This function is called to configure the adv data payload filter +** condition. +** +** Parameters action: to read/write/clear +** cond_type: filter condition type. +** filt_index - Filter index +** p_cond: filter condition parameter +** p_cmpl_cback - Config callback pointer +** ref_value - Reference value +** +** Returns void +** +*******************************************************************************/ +tBTM_STATUS BTM_BleCfgFilterCondition(tBTM_BLE_SCAN_COND_OP action, + tBTM_BLE_PF_COND_TYPE cond_type, + tBTM_BLE_PF_FILT_INDEX filt_index, + tBTM_BLE_PF_COND_PARAM *p_cond, + tBTM_BLE_PF_CFG_CBACK *p_cmpl_cback, + tBTM_BLE_REF_VALUE ref_value) +{ + tBTM_STATUS st = BTM_ILLEGAL_VALUE; + UINT8 ocf = 0; + BTM_TRACE_EVENT (" BTM_BleCfgFilterCondition action:%d, cond_type:%d, index:%d", action, + cond_type, filt_index); + + if (BTM_SUCCESS != btm_ble_obtain_vsc_details()) { + return st; + } + + switch (cond_type) { + /* write service data filter */ + case BTM_BLE_PF_SRVC_DATA_PATTERN: + /* write manufacturer data filter */ + case BTM_BLE_PF_MANU_DATA: + st = btm_ble_update_pf_manu_data(action, filt_index, p_cond, cond_type, 0, ref_value); + break; + + /* write local name filter */ + case BTM_BLE_PF_LOCAL_NAME: + st = btm_ble_update_pf_local_name(action, filt_index, p_cond); + break; + + /* filter on advertiser address */ + case BTM_BLE_PF_ADDR_FILTER: + st = btm_ble_update_addr_filter(action, filt_index, p_cond); + break; + + /* filter on service/solicited UUID */ + case BTM_BLE_PF_SRVC_UUID: + case BTM_BLE_PF_SRVC_SOL_UUID: + st = btm_ble_update_uuid_filter(action, filt_index, cond_type, p_cond, 0, ref_value); + break; + + case BTM_BLE_PF_SRVC_DATA: + st = btm_ble_update_srvc_data_change(action, filt_index, p_cond); + break; + + case BTM_BLE_PF_TYPE_ALL: /* only used to clear filter */ + st = btm_ble_clear_scan_pf_filter(action, filt_index, p_cond, p_cmpl_cback, + 0, ref_value); + break; + + default: + BTM_TRACE_WARNING("condition type [%d] not supported currently.", cond_type); + break; + } + + if (BTM_CMD_STARTED == st && cond_type != BTM_BLE_PF_TYPE_ALL) { + ocf = btm_ble_condtype_to_ocf(cond_type); + btm_ble_advfilt_enq_op_q(action, ocf, BTM_BLE_FILT_CFG, ref_value, p_cmpl_cback, NULL); + } else if (BTM_CMD_STARTED == st && BTM_BLE_PF_TYPE_ALL == cond_type) { + btm_ble_advfilt_enq_op_q(action, BTM_BLE_META_PF_FEAT_SEL, BTM_BLE_FILT_CFG, + ref_value, p_cmpl_cback, NULL); + } + return st; +} + +/******************************************************************************* +** +** Function btm_ble_adv_filter_init +** +** Description This function initializes the adv filter control block +** +** Parameters +** +** Returns status +** +*******************************************************************************/ +void btm_ble_adv_filter_init(void) +{ +#if BTM_DYNAMIC_MEMORY == TRUE + btm_ble_adv_filt_cb_ptr = (tBTM_BLE_ADV_FILTER_CB *)osi_malloc(sizeof(tBTM_BLE_ADV_FILTER_CB)); + cmn_ble_adv_vsc_cb_ptr = (tBTM_BLE_VSC_CB *)osi_malloc(sizeof(tBTM_BLE_VSC_CB)); + if (btm_ble_adv_filt_cb_ptr == NULL || cmn_ble_adv_vsc_cb_ptr == NULL) { + BTM_TRACE_ERROR("%s malloc failed", __func__); + return; + } + memset((void *)btm_ble_adv_filt_cb_ptr, 0, sizeof(tBTM_BLE_ADV_FILTER_CB)); + memset((void *)cmn_ble_adv_vsc_cb_ptr, 0, sizeof(tBTM_BLE_VSC_CB)); +#endif + memset(&btm_ble_adv_filt_cb, 0, sizeof(tBTM_BLE_ADV_FILTER_CB)); + if (BTM_SUCCESS != btm_ble_obtain_vsc_details()) { + return; + } + + if (cmn_ble_adv_vsc_cb.max_filter > 0) { + btm_ble_adv_filt_cb.p_addr_filter_count = + (tBTM_BLE_PF_COUNT *) osi_malloc( sizeof(tBTM_BLE_PF_COUNT) * cmn_ble_adv_vsc_cb.max_filter); + } +} + +/******************************************************************************* +** +** Function btm_ble_adv_filter_cleanup +** +** Description This function de-initializes the adv filter control block +** +** Parameters +** +** Returns status +** +*******************************************************************************/ +void btm_ble_adv_filter_cleanup(void) +{ + if (btm_ble_adv_filt_cb.p_addr_filter_count) { + osi_free(btm_ble_adv_filt_cb.p_addr_filter_count); + btm_ble_adv_filt_cb.p_addr_filter_count = NULL; + } + +#if BTM_DYNAMIC_MEMORY == TRUE + osi_free(btm_ble_adv_filt_cb_ptr); + btm_ble_adv_filt_cb_ptr = NULL; + osi_free(cmn_ble_adv_vsc_cb_ptr); + cmn_ble_adv_vsc_cb_ptr = NULL; +#endif +} + +#endif diff --git a/lib/bt/host/bluedroid/stack/btm/btm_ble_batchscan.c b/lib/bt/host/bluedroid/stack/btm/btm_ble_batchscan.c new file mode 100644 index 00000000..5b0fcd8f --- /dev/null +++ b/lib/bt/host/bluedroid/stack/btm/btm_ble_batchscan.c @@ -0,0 +1,953 @@ +/****************************************************************************** + * + * Copyright (C) 2014 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ +#include +//#include +#include +#include "common/bt_target.h" + +#include "stack/btm_ble_api.h" +#include "stack/bt_types.h" +//#include "bt_utils.h" +#include "stack/btu.h" +#include "btm_int.h" +#include "device/controller.h" +#include "stack/hcimsgs.h" + +#if (BLE_INCLUDED == TRUE) + +#if BTM_DYNAMIC_MEMORY == FALSE +tBTM_BLE_BATCH_SCAN_CB ble_batchscan_cb; +tBTM_BLE_ADV_TRACK_CB ble_advtrack_cb; +#else +tBTM_BLE_BATCH_SCAN_CB *ble_batchscan_cb_ptr; +tBTM_BLE_ADV_TRACK_CB *ble_advtrack_cb_ptr; +#define ble_batchscan_cb (*ble_batchscan_cb_ptr) +#define ble_advtrack_cb (*ble_advtrack_cb_ptr) +#endif + +/* length of each batch scan command */ +#define BTM_BLE_BATCH_SCAN_STORAGE_CFG_LEN 4 +#define BTM_BLE_BATCH_SCAN_PARAM_CONFIG_LEN 12 +#define BTM_BLE_BATCH_SCAN_ENB_DISB_LEN 2 +#define BTM_BLE_BATCH_SCAN_READ_RESULTS_LEN 2 + +#define BTM_BLE_BATCH_SCAN_CB_EVT_MASK 0xF0 +#define BTM_BLE_BATCH_SCAN_SUBCODE_MASK 0x0F + +/******************************************************************************* +** Local functions +*******************************************************************************/ +void btm_ble_batchscan_vsc_cmpl_cback (tBTM_VSC_CMPL *p_params); +void btm_ble_batchscan_cleanup(void); + +/******************************************************************************* +** +** Function btm_ble_batchscan_filter_track_adv_vse_cback +** +** Description VSE callback for batch scan, filter, and tracking events. +** +** Returns None +** +*******************************************************************************/ +void btm_ble_batchscan_filter_track_adv_vse_cback(UINT8 len, UINT8 *p) +{ + tBTM_BLE_TRACK_ADV_DATA adv_data; + + UINT8 sub_event = 0; + tBTM_BLE_VSC_CB cmn_ble_vsc_cb; + STREAM_TO_UINT8(sub_event, p); + + BTM_TRACE_EVENT("btm_ble_batchscan_filter_track_adv_vse_cback called with event:%x", sub_event); + if (HCI_VSE_SUBCODE_BLE_THRESHOLD_SUB_EVT == sub_event && + NULL != ble_batchscan_cb.p_thres_cback) { + ble_batchscan_cb.p_thres_cback(ble_batchscan_cb.ref_value); + return; + } + + if (HCI_VSE_SUBCODE_BLE_TRACKING_SUB_EVT == sub_event && NULL != ble_advtrack_cb.p_track_cback) { + if (len < 10) { + return; + } + + memset(&adv_data, 0 , sizeof(tBTM_BLE_TRACK_ADV_DATA)); + BTM_BleGetVendorCapabilities(&cmn_ble_vsc_cb); + adv_data.client_if = (UINT8)ble_advtrack_cb.ref_value; + if (cmn_ble_vsc_cb.version_supported > BTM_VSC_CHIP_CAPABILITY_L_VERSION) { + STREAM_TO_UINT8(adv_data.filt_index, p); + STREAM_TO_UINT8(adv_data.advertiser_state, p); + STREAM_TO_UINT8(adv_data.advertiser_info_present, p); + STREAM_TO_BDADDR(adv_data.bd_addr.address, p); + STREAM_TO_UINT8(adv_data.addr_type, p); + + /* Extract the adv info details */ + if (ADV_INFO_PRESENT == adv_data.advertiser_info_present) { + STREAM_TO_UINT8(adv_data.tx_power, p); + STREAM_TO_UINT8(adv_data.rssi_value, p); + STREAM_TO_UINT16(adv_data.time_stamp, p); + + STREAM_TO_UINT8(adv_data.adv_pkt_len, p); + if (adv_data.adv_pkt_len > 0) { + adv_data.p_adv_pkt_data = osi_malloc(adv_data.adv_pkt_len); + memcpy(adv_data.p_adv_pkt_data, p, adv_data.adv_pkt_len); + } + + STREAM_TO_UINT8(adv_data.scan_rsp_len, p); + if (adv_data.scan_rsp_len > 0) { + adv_data.p_scan_rsp_data = osi_malloc(adv_data.scan_rsp_len); + memcpy(adv_data.p_scan_rsp_data, p, adv_data.scan_rsp_len); + } + } + } else { + /* Based on L-release version */ + STREAM_TO_UINT8(adv_data.filt_index, p); + STREAM_TO_UINT8(adv_data.addr_type, p); + STREAM_TO_BDADDR(adv_data.bd_addr.address, p); + STREAM_TO_UINT8(adv_data.advertiser_state, p); + } + + BTM_TRACE_EVENT("track_adv_vse_cback called: %d, %d, %d", adv_data.filt_index, + adv_data.addr_type, adv_data.advertiser_state); + ble_advtrack_cb.p_track_cback(&adv_data); + return; + } +} + +/******************************************************************************* +** +** Function btm_ble_batchscan_enq_op_q +** +** Description enqueue a batchscan operation in q to check command complete +** status +** +** Returns void +** +*******************************************************************************/ +void btm_ble_batchscan_enq_op_q(UINT8 opcode, tBTM_BLE_BATCH_SCAN_STATE cur_state, + UINT8 cb_evt, tBTM_BLE_REF_VALUE ref_value) +{ + ble_batchscan_cb.op_q.sub_code[ble_batchscan_cb.op_q.next_idx] = (opcode | (cb_evt << 4)); + ble_batchscan_cb.op_q.cur_state[ble_batchscan_cb.op_q.next_idx] = cur_state; + ble_batchscan_cb.op_q.ref_value[ble_batchscan_cb.op_q.next_idx] = ref_value; + BTM_TRACE_DEBUG("btm_ble_batchscan_enq_op_q: subcode:%d, Cur_state:%d, ref_value:%d", + ble_batchscan_cb.op_q.sub_code[ble_batchscan_cb.op_q.next_idx], + ble_batchscan_cb.op_q.cur_state[ble_batchscan_cb.op_q.next_idx], + ble_batchscan_cb.op_q.ref_value[ble_batchscan_cb.op_q.next_idx]); + ble_batchscan_cb.op_q.next_idx = (ble_batchscan_cb.op_q.next_idx + 1) + % BTM_BLE_BATCH_SCAN_MAX; +} + +/******************************************************************************* +** +** Function btm_ble_batchscan_enq_rep_q +** +** Description enqueue a batchscan report operation in q to check command complete +** status +** +** Returns void +** +*******************************************************************************/ +tBTM_STATUS btm_ble_batchscan_enq_rep_q(UINT8 report_format, tBTM_BLE_REF_VALUE ref_value) +{ + int i = 0; + for (i = 0; i < BTM_BLE_BATCH_REP_MAIN_Q_SIZE; i++) { + if (report_format == ble_batchscan_cb.main_rep_q.rep_mode[i]) { + return BTM_ILLEGAL_VALUE; + } + } + + ble_batchscan_cb.main_rep_q.rep_mode[ble_batchscan_cb.main_rep_q.next_idx] = report_format; + ble_batchscan_cb.main_rep_q.ref_value[ble_batchscan_cb.main_rep_q.next_idx] = ref_value; + ble_batchscan_cb.main_rep_q.num_records[ble_batchscan_cb.main_rep_q.next_idx] = 0; + ble_batchscan_cb.main_rep_q.data_len[ble_batchscan_cb.main_rep_q.next_idx] = 0; + ble_batchscan_cb.main_rep_q.p_data[ble_batchscan_cb.main_rep_q.next_idx] = NULL; + BTM_TRACE_DEBUG("btm_ble_batchscan_enq_rep_q: index:%d, rep %d, ref %d", + ble_batchscan_cb.main_rep_q.next_idx, report_format, ref_value); + + ble_batchscan_cb.main_rep_q.next_idx = (ble_batchscan_cb.main_rep_q.next_idx + 1) + % BTM_BLE_BATCH_REP_MAIN_Q_SIZE; + return BTM_SUCCESS; +} + +/******************************************************************************* +** +** Function btm_ble_batchscan_enq_rep_data +** +** Description setup the data in the main report queue +** +** Returns void +** +*******************************************************************************/ +void btm_ble_batchscan_enq_rep_data(UINT8 report_format, UINT8 num_records, UINT8 *p_data, + UINT8 data_len) +{ + int index = 0, len = 0; + UINT8 *p_orig_data = NULL, *p_app_data = NULL; + + for (index = 0; index < BTM_BLE_BATCH_REP_MAIN_Q_SIZE; index++) { + if (report_format == ble_batchscan_cb.main_rep_q.rep_mode[index]) { + break; + } + } + + BTM_TRACE_DEBUG("btm_ble_batchscan_enq_rep_data: index:%d, rep %d, num %d len : %d", + index, report_format, num_records, data_len); + + if (index < BTM_BLE_BATCH_REP_MAIN_Q_SIZE && data_len > 0 && num_records > 0) { + len = ble_batchscan_cb.main_rep_q.data_len[index]; + p_orig_data = ble_batchscan_cb.main_rep_q.p_data[index]; + if (NULL != p_orig_data) { + p_app_data = osi_malloc(len + data_len); + memcpy(p_app_data, p_orig_data, len); + memcpy(p_app_data + len, p_data, data_len); + osi_free(p_orig_data); + ble_batchscan_cb.main_rep_q.p_data[index] = p_app_data; + ble_batchscan_cb.main_rep_q.num_records[index] += num_records; + ble_batchscan_cb.main_rep_q.data_len[index] += data_len; + } else { + p_app_data = osi_malloc(data_len); + memcpy(p_app_data, p_data, data_len); + ble_batchscan_cb.main_rep_q.p_data[index] = p_app_data; + ble_batchscan_cb.main_rep_q.num_records[index] = num_records; + ble_batchscan_cb.main_rep_q.data_len[index] = data_len; + } + } +} + +/******************************************************************************* +** +** Function btm_ble_batchscan_deq_rep_q +** +** Description dequeue a batchscan report in q when command complete +** is received +** +** Returns void +** +*******************************************************************************/ +void btm_ble_batchscan_deq_rep_data(UINT8 report_format, tBTM_BLE_REF_VALUE *p_ref_value, + UINT8 *p_num_records, UINT8 **p_data, UINT16 *p_data_len) +{ + int index = 0; + + for (index = 0; index < BTM_BLE_BATCH_REP_MAIN_Q_SIZE; index++) { + if (report_format == ble_batchscan_cb.main_rep_q.rep_mode[index]) { + break; + } + } + + if (BTM_BLE_BATCH_REP_MAIN_Q_SIZE == index) { + BTM_TRACE_ERROR("btm_ble_batchscan_deq_rep_data: rep_format:%d not found", report_format); + return; + } + + *p_num_records = ble_batchscan_cb.main_rep_q.num_records[index]; + *p_ref_value = ble_batchscan_cb.main_rep_q.ref_value[index]; + *p_data = ble_batchscan_cb.main_rep_q.p_data[index]; + *p_data_len = ble_batchscan_cb.main_rep_q.data_len[index]; + + ble_batchscan_cb.main_rep_q.p_data[index] = NULL; + ble_batchscan_cb.main_rep_q.data_len[index] = 0; + ble_batchscan_cb.main_rep_q.rep_mode[index] = 0; + ble_batchscan_cb.main_rep_q.ref_value[index] = 0; + ble_batchscan_cb.main_rep_q.num_records[index] = 0; + + BTM_TRACE_DEBUG("btm_ble_batchscan_deq_rep_data: index:%d, rep %d, num %d, data_len %d", + index, report_format, *p_num_records, *p_data_len); + + ble_batchscan_cb.main_rep_q.pending_idx = (ble_batchscan_cb.main_rep_q.pending_idx + 1) + % BTM_BLE_BATCH_SCAN_MAX; +} + +/******************************************************************************* +** +** Function btm_ble_batchscan_deq_op_q +** +** Description dequeue a batch scan operation from q when command complete +** is received +** +** Returns void +** +*******************************************************************************/ +void btm_ble_batchscan_deq_op_q(UINT8 *p_opcode, tBTM_BLE_BATCH_SCAN_STATE *cur_state, + UINT8 *p_cb_evt, tBTM_BLE_REF_VALUE *p_ref) +{ + *p_cb_evt = (ble_batchscan_cb.op_q.sub_code[ble_batchscan_cb.op_q.pending_idx] >> 4); + *p_opcode = (ble_batchscan_cb.op_q.sub_code[ble_batchscan_cb.op_q.pending_idx] + & BTM_BLE_BATCH_SCAN_SUBCODE_MASK); + *p_ref = ble_batchscan_cb.op_q.ref_value[ble_batchscan_cb.op_q.pending_idx]; + *cur_state = (ble_batchscan_cb.op_q.cur_state[ble_batchscan_cb.op_q.pending_idx]); + ble_batchscan_cb.op_q.pending_idx = (ble_batchscan_cb.op_q.pending_idx + 1) + % BTM_BLE_BATCH_SCAN_MAX; +} + +/******************************************************************************* +** +** Function btm_ble_read_batchscan_reports +** +** Description This function reads the reports from controller +** +** Parameters scan_mode - The mode for which the reports are to be read out from the controller +** ref_value - Reference value +** +** Returns status +** +*******************************************************************************/ +tBTM_STATUS btm_ble_read_batchscan_reports(tBTM_BLE_BATCH_SCAN_MODE scan_mode, + tBTM_BLE_REF_VALUE ref_value) +{ + tBTM_STATUS status = BTM_NO_RESOURCES; + UINT8 param[BTM_BLE_BATCH_SCAN_READ_RESULTS_LEN], *pp; + pp = param; + + memset(param, 0, BTM_BLE_BATCH_SCAN_READ_RESULTS_LEN); + + UINT8_TO_STREAM (pp, BTM_BLE_BATCH_SCAN_READ_RESULTS); + UINT8_TO_STREAM (pp, scan_mode); + + if ((status = BTM_VendorSpecificCommand (HCI_BLE_BATCH_SCAN_OCF, + BTM_BLE_BATCH_SCAN_READ_RESULTS_LEN, param, btm_ble_batchscan_vsc_cmpl_cback)) + != BTM_CMD_STARTED) { + BTM_TRACE_ERROR("btm_ble_read_batchscan_reports %d", status); + return BTM_ILLEGAL_VALUE; + } + + if (BTM_CMD_STARTED == status) { + /* The user needs to be provided scan read reports event */ + btm_ble_batchscan_enq_op_q(BTM_BLE_BATCH_SCAN_READ_RESULTS, ble_batchscan_cb.cur_state, + BTM_BLE_BATCH_SCAN_READ_REPTS_EVT, ref_value); + } + + return status; +} + +/******************************************************************************* +** +** Function btm_ble_batchscan_vsc_cmpl_cback +** +** Description Batch scan VSC complete callback +** +** Parameters p_params - VSC completed callback parameters +** +** Returns void +** +*******************************************************************************/ +void btm_ble_batchscan_vsc_cmpl_cback (tBTM_VSC_CMPL *p_params) +{ + UINT8 *p = p_params->p_param_buf; + UINT16 len = p_params->param_len; + tBTM_BLE_REF_VALUE ref_value = 0; + + UINT8 status = 0, subcode = 0, opcode = 0; + UINT8 report_format = 0, num_records = 0, cb_evt = 0; + UINT16 data_len = 0; + tBTM_BLE_BATCH_SCAN_STATE cur_state = 0; + tBTM_STATUS btm_status = 0; + UINT8 *p_data = NULL; + + if (len < 2) { + BTM_TRACE_ERROR("wrong length for btm_ble_batch_scan_vsc_cmpl_cback"); + btm_ble_batchscan_deq_op_q(&opcode, &cur_state, &cb_evt, &ref_value); + return; + } + + STREAM_TO_UINT8(status, p); + STREAM_TO_UINT8(subcode, p); + + btm_ble_batchscan_deq_op_q(&opcode, &cur_state, &cb_evt, &ref_value); + + BTM_TRACE_DEBUG("btm_ble_batchscan op_code = %02x state = %02x cb_evt = %02x,ref_value=%d", + opcode, cur_state, cb_evt, ref_value); + + if (opcode != subcode) { + BTM_TRACE_ERROR("Got unexpected VSC cmpl, expected: %d got: %d", subcode, opcode); + return; + } + + switch (subcode) { + case BTM_BLE_BATCH_SCAN_ENB_DISAB_CUST_FEATURE: { + if (BTM_SUCCESS == status && BTM_BLE_SCAN_ENABLE_CALLED == cur_state) { + ble_batchscan_cb.cur_state = BTM_BLE_SCAN_ENABLED_STATE; + } else if (BTM_BLE_SCAN_ENABLE_CALLED == cur_state) { + BTM_TRACE_ERROR("SCAN_ENB_DISAB_CUST_FEATURE - Invalid state after enb"); + ble_batchscan_cb.cur_state = BTM_BLE_SCAN_INVALID_STATE; + } + + BTM_TRACE_DEBUG("BTM_BLE_BATCH_SCAN_ENB_DISAB_CUST_FEAT status = %d, state: %d,evt=%d", + status, ble_batchscan_cb.cur_state, cb_evt); + + if (cb_evt != 0 && NULL != ble_batchscan_cb.p_setup_cback) { + ble_batchscan_cb.p_setup_cback(cb_evt, ref_value, status); + } + break; + } + + case BTM_BLE_BATCH_SCAN_SET_STORAGE_PARAM: { + BTM_TRACE_DEBUG("BTM_BLE_BATCH_SCAN_SET_STORAGE_PARAM status = %d, evt=%d", + status, cb_evt); + if (cb_evt != 0 && NULL != ble_batchscan_cb.p_setup_cback) { + ble_batchscan_cb.p_setup_cback(cb_evt, ref_value, status); + } + break; + } + + case BTM_BLE_BATCH_SCAN_SET_PARAMS: { + BTM_TRACE_DEBUG("BTM_BLE_BATCH_SCAN_SET_PARAMS status = %d,evt=%d", status, cb_evt); + + if (BTM_BLE_SCAN_DISABLE_CALLED == cur_state) { + if (BTM_SUCCESS == status) { + ble_batchscan_cb.cur_state = BTM_BLE_SCAN_DISABLED_STATE; + } else { + BTM_TRACE_ERROR("BTM_BLE_BATCH_SCAN_SET_PARAMS - Invalid state after disabled"); + ble_batchscan_cb.cur_state = BTM_BLE_SCAN_INVALID_STATE; + } + } + + if (cb_evt != 0 && NULL != ble_batchscan_cb.p_setup_cback) { + ble_batchscan_cb.p_setup_cback(cb_evt, ref_value, status); + } + break; + } + + case BTM_BLE_BATCH_SCAN_READ_RESULTS: { + if (cb_evt != 0 && NULL != ble_batchscan_cb.p_scan_rep_cback) { + STREAM_TO_UINT8(report_format, p); + STREAM_TO_UINT8(num_records, p); + p = (uint8_t *)(p_params->p_param_buf + 4); + BTM_TRACE_DEBUG("BTM_BLE_BATCH_SCAN_READ_RESULTS status=%d,len=%d,rec=%d", + status, len - 4, num_records); + + if (0 == num_records) { + btm_ble_batchscan_deq_rep_data(report_format, &ref_value, &num_records, + &p_data, &data_len); + if (NULL != ble_batchscan_cb.p_scan_rep_cback) { + ble_batchscan_cb.p_scan_rep_cback(ref_value, report_format, num_records, + data_len, p_data, status); + } + } else { + if ((len - 4) > 0) { + btm_ble_batchscan_enq_rep_data(report_format, num_records, p, len - 4); + /* More records could be in the buffer and needs to be pulled out */ + btm_status = btm_ble_read_batchscan_reports(report_format, ref_value); + if (BTM_CMD_STARTED != btm_status) { + btm_ble_batchscan_deq_rep_data(report_format, &ref_value, &num_records, + &p_data, &data_len); + /* Send whatever is available, in case of a command failure */ + if (NULL != ble_batchscan_cb.p_scan_rep_cback && NULL != p_data) { + ble_batchscan_cb.p_scan_rep_cback(ref_value, report_format, + num_records, data_len, p_data, status); + } + } + } + } + } + break; + } + + default: + break; + } + + return; +} + +/******************************************************************************* +** +** Function btm_ble_set_storage_config +** +** Description This function writes the storage configuration in controller +** +** Parameters batch_scan_full_max -Max storage space (in %) allocated to full scanning +** batch_scan_trunc_max -Max storage space (in %) allocated to truncated scanning +** batch_scan_notify_threshold - Setup notification level based on total space +** +** Returns status +** +*******************************************************************************/ +tBTM_STATUS btm_ble_set_storage_config(UINT8 batch_scan_full_max, UINT8 batch_scan_trunc_max, + UINT8 batch_scan_notify_threshold) +{ + tBTM_STATUS status = BTM_NO_RESOURCES; + UINT8 param[BTM_BLE_BATCH_SCAN_STORAGE_CFG_LEN], *pp; + + pp = param; + memset(param, 0, BTM_BLE_BATCH_SCAN_STORAGE_CFG_LEN); + + UINT8_TO_STREAM (pp, BTM_BLE_BATCH_SCAN_SET_STORAGE_PARAM); + UINT8_TO_STREAM (pp, batch_scan_full_max); + UINT8_TO_STREAM (pp, batch_scan_trunc_max); + UINT8_TO_STREAM (pp, batch_scan_notify_threshold); + + if ((status = BTM_VendorSpecificCommand (HCI_BLE_BATCH_SCAN_OCF, + BTM_BLE_BATCH_SCAN_STORAGE_CFG_LEN, param, + btm_ble_batchscan_vsc_cmpl_cback)) != BTM_CMD_STARTED) { + BTM_TRACE_ERROR("btm_ble_set_storage_config %d", status); + return BTM_ILLEGAL_VALUE; + } + + return status; +} + +/******************************************************************************* +** +** Function btm_ble_set_batchscan_param +** +** Description This function writes the batch scan params in controller +** +** Parameters scan_mode -Batch scan mode +** scan_interval - Scan interval +** scan_window - Scan window +** discard_rule -Discard rules +** addr_type - Address type +** +** Returns status +** +*******************************************************************************/ +tBTM_STATUS btm_ble_set_batchscan_param(tBTM_BLE_BATCH_SCAN_MODE scan_mode, + UINT32 scan_interval, UINT32 scan_window, tBLE_ADDR_TYPE addr_type, + tBTM_BLE_DISCARD_RULE discard_rule) +{ + tBTM_STATUS status = BTM_NO_RESOURCES; + UINT8 scan_param[BTM_BLE_BATCH_SCAN_PARAM_CONFIG_LEN], *pp_scan; + + pp_scan = scan_param; + memset(scan_param, 0, BTM_BLE_BATCH_SCAN_PARAM_CONFIG_LEN); + + // Override param and decide addr_type based on own addr type + // TODO: Remove upper layer parameter? + addr_type = btm_cb.ble_ctr_cb.addr_mgnt_cb.own_addr_type; + + UINT8_TO_STREAM (pp_scan, BTM_BLE_BATCH_SCAN_SET_PARAMS); + UINT8_TO_STREAM (pp_scan, scan_mode); + UINT32_TO_STREAM (pp_scan, scan_window); + UINT32_TO_STREAM (pp_scan, scan_interval); + UINT8_TO_STREAM (pp_scan, addr_type); + UINT8_TO_STREAM (pp_scan, discard_rule); + + if ((status = BTM_VendorSpecificCommand (HCI_BLE_BATCH_SCAN_OCF, + BTM_BLE_BATCH_SCAN_PARAM_CONFIG_LEN, + scan_param, btm_ble_batchscan_vsc_cmpl_cback)) != BTM_CMD_STARTED) { + BTM_TRACE_ERROR("btm_ble_set_batchscan_param %d", status); + return BTM_ILLEGAL_VALUE; + } + + return status; +} + +/******************************************************************************* +** +** Function btm_ble_enable_disable_batchscan +** +** Description This function enables the customer specific feature in controller +** +** Parameters enable_disable: true - enable, false - disable +** +** Returns status +** +*******************************************************************************/ +tBTM_STATUS btm_ble_enable_disable_batchscan(BOOLEAN should_enable) +{ + tBTM_STATUS status = BTM_NO_RESOURCES; + UINT8 shld_enable = 0x01; + UINT8 enable_param[BTM_BLE_BATCH_SCAN_ENB_DISB_LEN], *pp_enable; + + if (!should_enable) { + shld_enable = 0x00; + } + + if (should_enable) { + pp_enable = enable_param; + memset(enable_param, 0, BTM_BLE_BATCH_SCAN_ENB_DISB_LEN); + + UINT8_TO_STREAM (pp_enable, BTM_BLE_BATCH_SCAN_ENB_DISAB_CUST_FEATURE); + UINT8_TO_STREAM (pp_enable, shld_enable); + + if ((status = BTM_VendorSpecificCommand(HCI_BLE_BATCH_SCAN_OCF, + BTM_BLE_BATCH_SCAN_ENB_DISB_LEN, enable_param, + btm_ble_batchscan_vsc_cmpl_cback)) != BTM_CMD_STARTED) { + status = BTM_MODE_UNSUPPORTED; + BTM_TRACE_ERROR("btm_ble_enable_disable_batchscan %d", status); + return BTM_ILLEGAL_VALUE; + } + } else if ((status = btm_ble_set_batchscan_param(BTM_BLE_BATCH_SCAN_MODE_DISABLE, + ble_batchscan_cb.scan_interval, ble_batchscan_cb.scan_window, + ble_batchscan_cb.addr_type, ble_batchscan_cb.discard_rule)) != BTM_CMD_STARTED) { + status = BTM_MODE_UNSUPPORTED; + BTM_TRACE_ERROR("btm_ble_enable_disable_batchscan %d", status); + return BTM_ILLEGAL_VALUE; + } + + if (should_enable) { + ble_batchscan_cb.cur_state = BTM_BLE_SCAN_ENABLE_CALLED; + } else { + ble_batchscan_cb.cur_state = BTM_BLE_SCAN_DISABLE_CALLED; + } + return status; +} + +/******************************************************************************* +** +** Function BTM_BleSetStorageConfig +** +** Description This function is called to write storage config params. +** +** Parameters: batch_scan_full_max - Max storage space (in %) allocated to full style +** batch_scan_trunc_max - Max storage space (in %) allocated to trunc style +** batch_scan_notify_threshold - Setup notification level based on total space +** p_setup_cback - Setup callback pointer +** p_thres_cback - Threshold callback pointer +** p_rep_cback - Reports callback pointer +** ref_value - Reference value +** +** Returns tBTM_STATUS +** +*******************************************************************************/ +tBTM_STATUS BTM_BleSetStorageConfig(UINT8 batch_scan_full_max, UINT8 batch_scan_trunc_max, + UINT8 batch_scan_notify_threshold, + tBTM_BLE_SCAN_SETUP_CBACK *p_setup_cback, + tBTM_BLE_SCAN_THRESHOLD_CBACK *p_thres_cback, + tBTM_BLE_SCAN_REP_CBACK *p_rep_cback, + tBTM_BLE_REF_VALUE ref_value) +{ + tBTM_STATUS status = BTM_NO_RESOURCES; + tBTM_BLE_VSC_CB cmn_ble_vsc_cb; + + BTM_TRACE_EVENT (" BTM_BleSetStorageConfig: %d, %d, %d, %d, %d", + ble_batchscan_cb.cur_state, ref_value, batch_scan_full_max, batch_scan_trunc_max, + batch_scan_notify_threshold); + + if (!controller_get_interface()->supports_ble()) { + return BTM_ILLEGAL_VALUE; + } + + BTM_BleGetVendorCapabilities(&cmn_ble_vsc_cb); + + if (0 == cmn_ble_vsc_cb.tot_scan_results_strg) { + BTM_TRACE_ERROR("Controller does not support batch scan"); + return BTM_ERR_PROCESSING; + } + + ble_batchscan_cb.p_setup_cback = p_setup_cback; + ble_batchscan_cb.p_thres_cback = p_thres_cback; + ble_batchscan_cb.p_scan_rep_cback = p_rep_cback; + ble_batchscan_cb.ref_value = ref_value; + + if (batch_scan_full_max > BTM_BLE_ADV_SCAN_FULL_MAX || + batch_scan_trunc_max > BTM_BLE_ADV_SCAN_TRUNC_MAX || + batch_scan_notify_threshold > BTM_BLE_ADV_SCAN_THR_MAX) { + BTM_TRACE_ERROR("Illegal set storage config params"); + return BTM_ILLEGAL_VALUE; + } + + if (BTM_BLE_SCAN_INVALID_STATE == ble_batchscan_cb.cur_state || + BTM_BLE_SCAN_DISABLED_STATE == ble_batchscan_cb.cur_state || + BTM_BLE_SCAN_DISABLE_CALLED == ble_batchscan_cb.cur_state) { + status = btm_ble_enable_disable_batchscan(TRUE); + if (BTM_CMD_STARTED != status) { + return status; + } + + ble_batchscan_cb.cur_state = BTM_BLE_SCAN_ENABLE_CALLED; + btm_ble_batchscan_enq_op_q(BTM_BLE_BATCH_SCAN_ENB_DISAB_CUST_FEATURE, + BTM_BLE_SCAN_ENABLE_CALLED, 0, ref_value); + } + + status = btm_ble_set_storage_config(batch_scan_full_max, batch_scan_trunc_max, + batch_scan_notify_threshold); + if (BTM_CMD_STARTED != status) { + return status; + } + /* The user needs to be provided scan config storage event */ + btm_ble_batchscan_enq_op_q(BTM_BLE_BATCH_SCAN_SET_STORAGE_PARAM, ble_batchscan_cb.cur_state, + BTM_BLE_BATCH_SCAN_CFG_STRG_EVT, ref_value); + + return status; +} + + +/******************************************************************************* +** +** Function BTM_BleEnableBatchScan +** +** Description This function is called to configure and enable batch scanning +** +** Parameters: scan_mode -Batch scan mode +** scan_interval - Scan interval value +** scan_window - Scan window value +** discard_rule - Data discard rule +** ref_value - Reference value +** +** Returns tBTM_STATUS +** +*******************************************************************************/ +tBTM_STATUS BTM_BleEnableBatchScan(tBTM_BLE_BATCH_SCAN_MODE scan_mode, + UINT32 scan_interval, UINT32 scan_window, tBLE_ADDR_TYPE addr_type, + tBTM_BLE_DISCARD_RULE discard_rule, tBTM_BLE_REF_VALUE ref_value) +{ + tBTM_STATUS status = BTM_NO_RESOURCES; + tBTM_BLE_VSC_CB cmn_ble_vsc_cb; + BTM_TRACE_EVENT (" BTM_BleEnableBatchScan: %d, %d, %d, %d, %d, %d", + scan_mode, scan_interval, scan_window, addr_type, discard_rule, ref_value); + + if (!controller_get_interface()->supports_ble()) { + return BTM_ILLEGAL_VALUE; + } + + BTM_BleGetVendorCapabilities(&cmn_ble_vsc_cb); + + if (0 == cmn_ble_vsc_cb.tot_scan_results_strg) { + BTM_TRACE_ERROR("Controller does not support batch scan"); + return BTM_ERR_PROCESSING; + } + + BTM_TRACE_DEBUG("BTM_BleEnableBatchScan: %d, %x, %x, %d, %d", scan_mode, scan_interval, + scan_window, discard_rule, ble_batchscan_cb.cur_state); + + /* Only 16 bits will be used for scan interval and scan window as per agreement with Google */ + /* So the standard LE range would suffice for scan interval and scan window */ + if ((BTM_BLE_ISVALID_PARAM(scan_interval, BTM_BLE_SCAN_INT_MIN, BTM_BLE_SCAN_INT_MAX) || + BTM_BLE_ISVALID_PARAM(scan_window, BTM_BLE_SCAN_WIN_MIN, BTM_BLE_SCAN_WIN_MAX)) + && (BTM_BLE_BATCH_SCAN_MODE_PASS == scan_mode || BTM_BLE_BATCH_SCAN_MODE_ACTI == scan_mode + || BTM_BLE_BATCH_SCAN_MODE_PASS_ACTI == scan_mode) + && (BTM_BLE_DISCARD_OLD_ITEMS == discard_rule || + BTM_BLE_DISCARD_LOWER_RSSI_ITEMS == discard_rule)) { + if (BTM_BLE_SCAN_INVALID_STATE == ble_batchscan_cb.cur_state || + BTM_BLE_SCAN_DISABLED_STATE == ble_batchscan_cb.cur_state || + BTM_BLE_SCAN_DISABLE_CALLED == ble_batchscan_cb.cur_state) { + status = btm_ble_enable_disable_batchscan(TRUE); + if (BTM_CMD_STARTED != status) { + return status; + } + btm_ble_batchscan_enq_op_q(BTM_BLE_BATCH_SCAN_ENB_DISAB_CUST_FEATURE, + BTM_BLE_SCAN_ENABLE_CALLED, 0, ref_value); + } + + ble_batchscan_cb.scan_mode = scan_mode; + ble_batchscan_cb.scan_interval = scan_interval; + ble_batchscan_cb.scan_window = scan_window; + ble_batchscan_cb.addr_type = addr_type; + ble_batchscan_cb.discard_rule = discard_rule; + /* This command starts batch scanning, if enabled */ + status = btm_ble_set_batchscan_param(scan_mode, scan_interval, scan_window, addr_type, + discard_rule); + if (BTM_CMD_STARTED != status) { + return status; + } + + /* The user needs to be provided scan enable event */ + btm_ble_batchscan_enq_op_q(BTM_BLE_BATCH_SCAN_SET_PARAMS, ble_batchscan_cb.cur_state, + BTM_BLE_BATCH_SCAN_ENABLE_EVT, ref_value); + } else { + BTM_TRACE_ERROR("Illegal enable scan params"); + return BTM_ILLEGAL_VALUE; + } + return status; +} + +/******************************************************************************* +** +** Function BTM_BleDisableBatchScan +** +** Description This function is called to disable batch scanning +** +** Parameters: ref_value - Reference value +** +** Returns tBTM_STATUS +** +*******************************************************************************/ +tBTM_STATUS BTM_BleDisableBatchScan(tBTM_BLE_REF_VALUE ref_value) +{ + tBTM_STATUS status = BTM_NO_RESOURCES; + tBTM_BLE_VSC_CB cmn_ble_vsc_cb; + BTM_TRACE_EVENT (" BTM_BleDisableBatchScan"); + + if (!controller_get_interface()->supports_ble()) { + return BTM_ILLEGAL_VALUE; + } + + BTM_BleGetVendorCapabilities(&cmn_ble_vsc_cb); + + if (0 == cmn_ble_vsc_cb.tot_scan_results_strg) { + BTM_TRACE_ERROR("Controller does not support batch scan"); + return BTM_ERR_PROCESSING; + } + + status = btm_ble_enable_disable_batchscan(FALSE); + if (BTM_CMD_STARTED == status) { + /* The user needs to be provided scan disable event */ + btm_ble_batchscan_enq_op_q(BTM_BLE_BATCH_SCAN_SET_PARAMS, + BTM_BLE_SCAN_DISABLE_CALLED, BTM_BLE_BATCH_SCAN_DISABLE_EVT, + ref_value); + } + + return status; +} + +/******************************************************************************* +** +** Function BTM_BleReadScanReports +** +** Description This function is called to start reading batch scan reports +** +** Parameters: scan_mode - Batch scan mode +** ref_value - Reference value +** +** Returns tBTM_STATUS +** +*******************************************************************************/ +tBTM_STATUS BTM_BleReadScanReports(tBTM_BLE_BATCH_SCAN_MODE scan_mode, + tBTM_BLE_REF_VALUE ref_value) +{ + tBTM_STATUS status = BTM_NO_RESOURCES; + tBTM_BLE_VSC_CB cmn_ble_vsc_cb; + UINT8 read_scan_mode = 0; + UINT8 *p_data = NULL, num_records = 0; + UINT16 data_len = 0; + + BTM_TRACE_EVENT (" BTM_BleReadScanReports; %d, %d", scan_mode, ref_value); + + if (!controller_get_interface()->supports_ble()) { + return BTM_ILLEGAL_VALUE; + } + + BTM_BleGetVendorCapabilities(&cmn_ble_vsc_cb); + + if (0 == cmn_ble_vsc_cb.tot_scan_results_strg) { + BTM_TRACE_ERROR("Controller does not support batch scan"); + return BTM_ERR_PROCESSING; + } + + /* Check if the requested scan mode has already been setup by the user */ + read_scan_mode = ble_batchscan_cb.scan_mode & BTM_BLE_BATCH_SCAN_MODE_ACTI; + if (0 == read_scan_mode) { + read_scan_mode = ble_batchscan_cb.scan_mode & BTM_BLE_BATCH_SCAN_MODE_PASS; + } + + /* Check only for modes, as scan reports can be called after disabling batch scan */ + if (read_scan_mode > 0 && (BTM_BLE_BATCH_SCAN_MODE_PASS == scan_mode || + BTM_BLE_BATCH_SCAN_MODE_ACTI == scan_mode)) { + status = btm_ble_batchscan_enq_rep_q(scan_mode, ref_value); + if (BTM_SUCCESS == status) { + status = btm_ble_read_batchscan_reports(scan_mode, ref_value); + if (BTM_CMD_STARTED != status) { + btm_ble_batchscan_deq_rep_data(scan_mode, &ref_value, + &num_records, &p_data, &data_len); + } + } + } else { + BTM_TRACE_ERROR("Illegal read scan params: %d, %d, %d", read_scan_mode, scan_mode, + ble_batchscan_cb.cur_state); + return BTM_ILLEGAL_VALUE; + } + return status; +} + + +/******************************************************************************* +** +** Function BTM_BleTrackAdvertiser +** +** Description This function is called to setup the callback for tracking advertisers +** +** Parameters: p_track_cback - Tracking callback pointer +** ref_value - Reference value +** +** Returns tBTM_STATUS +** +*******************************************************************************/ +tBTM_STATUS BTM_BleTrackAdvertiser(tBTM_BLE_TRACK_ADV_CBACK *p_track_cback, + tBTM_BLE_REF_VALUE ref_value) +{ + tBTM_BLE_VSC_CB cmn_ble_vsc_cb; + BTM_TRACE_EVENT (" BTM_BleTrackAdvertiser"); + if (!controller_get_interface()->supports_ble()) { + return BTM_ILLEGAL_VALUE; + } + + BTM_BleGetVendorCapabilities(&cmn_ble_vsc_cb); + + if (0 == cmn_ble_vsc_cb.tot_scan_results_strg) { + BTM_TRACE_ERROR("Controller does not support scan storage"); + return BTM_ERR_PROCESSING; + } + + ble_advtrack_cb.p_track_cback = p_track_cback; + ble_advtrack_cb.ref_value = ref_value; + return BTM_CMD_STARTED; +} + +/******************************************************************************* +** +** Function btm_ble_batchscan_init +** +** Description This function initialize the batch scan control block. +** +** Parameters None +** +** Returns status +** +*******************************************************************************/ +void btm_ble_batchscan_init(void) +{ +#if BTM_DYNAMIC_MEMORY == TRUE + ble_batchscan_cb_ptr = (tBTM_BLE_BATCH_SCAN_CB *)osi_malloc(sizeof(tBTM_BLE_BATCH_SCAN_CB)); + ble_advtrack_cb_ptr = (tBTM_BLE_ADV_TRACK_CB *)osi_malloc(sizeof(tBTM_BLE_ADV_TRACK_CB)); + if (ble_batchscan_cb_ptr == NULL || ble_advtrack_cb_ptr == NULL) { + BTM_TRACE_ERROR("%s malloc failed", __func__); + return; + } +#endif + BTM_TRACE_EVENT (" btm_ble_batchscan_init"); + memset(&ble_batchscan_cb, 0, sizeof(tBTM_BLE_BATCH_SCAN_CB)); + memset(&ble_advtrack_cb, 0, sizeof(tBTM_BLE_ADV_TRACK_CB)); + BTM_RegisterForVSEvents(btm_ble_batchscan_filter_track_adv_vse_cback, TRUE); +} + +/******************************************************************************* +** +** Function btm_ble_batchscan_cleanup +** +** Description This function cleans the batch scan control block. +** +** Parameters None +** +** Returns void +** +*******************************************************************************/ +void btm_ble_batchscan_cleanup(void) +{ + int index = 0; + BTM_TRACE_EVENT (" btm_ble_batchscan_cleanup"); + + for (index = 0; index < BTM_BLE_BATCH_REP_MAIN_Q_SIZE; index++) { + if (NULL != ble_batchscan_cb.main_rep_q.p_data[index]) { + osi_free(ble_batchscan_cb.main_rep_q.p_data[index]); + ble_batchscan_cb.main_rep_q.p_data[index] = NULL; + } + } + + memset(&ble_batchscan_cb, 0, sizeof(tBTM_BLE_BATCH_SCAN_CB)); + memset(&ble_advtrack_cb, 0, sizeof(tBTM_BLE_ADV_TRACK_CB)); + +#if BTM_DYNAMIC_MEMORY == TRUE + osi_free(ble_batchscan_cb_ptr); + osi_free(ble_advtrack_cb_ptr); + ble_batchscan_cb_ptr = NULL; + ble_advtrack_cb_ptr = NULL; +#endif +} + +#endif diff --git a/lib/bt/host/bluedroid/stack/btm/btm_ble_bgconn.c b/lib/bt/host/bluedroid/stack/btm/btm_ble_bgconn.c new file mode 100644 index 00000000..4cb282b7 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/btm/btm_ble_bgconn.c @@ -0,0 +1,836 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains functions for BLE whitelist operation. + * + ******************************************************************************/ + +#include +#include "common/bt_trace.h" +#include "device/controller.h" +#include "osi/allocator.h" +#include "osi/hash_map.h" +#include "stack/bt_types.h" +#include "stack/btu.h" +#include "btm_int.h" +#include "l2c_int.h" +#include "stack/hcimsgs.h" +//#include "bt_utils.h" + +#ifndef BTM_BLE_SCAN_PARAM_TOUT +#define BTM_BLE_SCAN_PARAM_TOUT 50 /* 50 seconds */ +#endif + +#if (BLE_INCLUDED == TRUE) + +static void btm_suspend_wl_activity(tBTM_BLE_WL_STATE wl_state); +static void btm_wl_update_to_controller(void); + +// Unfortunately (for now?) we have to maintain a copy of the device whitelist +// on the host to determine if a device is pending to be connected or not. This +// controls whether the host should keep trying to scan for whitelisted +// peripherals or not. +// TODO: Move all of this to controller/le/background_list or similar? +static const size_t background_connection_buckets = 42; +static hash_map_t *background_connections = NULL; + +typedef struct background_connection_t { + bt_bdaddr_t address; +} background_connection_t; + +static bool bdaddr_equality_fn(const void *x, const void *y) +{ + return bdaddr_equals((bt_bdaddr_t *)x, (bt_bdaddr_t *)y); +} + +static void background_connections_lazy_init(void) +{ + if (!background_connections) { + background_connections = hash_map_new(background_connection_buckets, + hash_function_bdaddr, NULL, osi_free_func, bdaddr_equality_fn); + assert(background_connections); + } +} + +static BOOLEAN background_connection_add(bt_bdaddr_t *address) +{ + assert(address); + background_connections_lazy_init(); + background_connection_t *connection = hash_map_get(background_connections, address); + if (!connection) { + connection = osi_calloc(sizeof(background_connection_t)); + connection->address = *address; + hash_map_set(background_connections, &(connection->address), connection); + return TRUE; + } + return FALSE; +} + +static BOOLEAN background_connection_remove(bt_bdaddr_t *address) +{ + if (address && background_connections) { + return hash_map_erase(background_connections, address); + } + return FALSE; +} + +static void background_connections_clear(void) +{ + if (background_connections) { + hash_map_clear(background_connections); + } +} + +static bool background_connections_pending_cb(hash_map_entry_t *hash_entry, void *context) +{ + bool *pending_connections = context; + background_connection_t *connection = hash_entry->data; + const bool connected = BTM_IsAclConnectionUp(connection->address.address, BT_TRANSPORT_LE); + if (!connected) { + *pending_connections = true; + return false; + } + return true; +} + +static bool background_connections_pending(void) +{ + bool pending_connections = false; + if (background_connections) { + hash_map_foreach(background_connections, background_connections_pending_cb, &pending_connections); + } + return pending_connections; +} + +/******************************************************************************* +** +** Function btm_update_scanner_filter_policy +** +** Description This function updates the filter policy of scanner +*******************************************************************************/ +void btm_update_scanner_filter_policy(tBTM_BLE_SFP scan_policy) +{ + tBTM_BLE_INQ_CB *p_inq = &btm_cb.ble_ctr_cb.inq_var; + + UINT32 scan_interval = !p_inq->scan_interval ? BTM_BLE_GAP_DISC_SCAN_INT : p_inq->scan_interval; + UINT32 scan_window = !p_inq->scan_window ? BTM_BLE_GAP_DISC_SCAN_WIN : p_inq->scan_window; + + BTM_TRACE_EVENT ("%s\n", __func__); + + p_inq->sfp = scan_policy; + p_inq->scan_type = p_inq->scan_type == BTM_BLE_SCAN_MODE_NONE ? BTM_BLE_SCAN_MODE_ACTI : p_inq->scan_type; + + if (btm_cb.cmn_ble_vsc_cb.extended_scan_support == 0) { + btsnd_hcic_ble_set_scan_params(p_inq->scan_type, (UINT16)scan_interval, + (UINT16)scan_window, + btm_cb.ble_ctr_cb.addr_mgnt_cb.own_addr_type, + scan_policy); + } else { + btm_ble_send_extended_scan_params(p_inq->scan_type, scan_interval, scan_window, + btm_cb.ble_ctr_cb.addr_mgnt_cb.own_addr_type, + scan_policy); + } +} +/******************************************************************************* +** +** Function btm_add_dev_to_controller +** +** Description This function load the device into controller white list +*******************************************************************************/ +BOOLEAN btm_add_dev_to_controller (BOOLEAN to_add, BD_ADDR bd_addr, tBLE_ADDR_TYPE wl_addr_type) +{ + /* + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev (bd_addr); + tBLE_ADDR_TYPE addr_type = BLE_ADDR_PUBLIC; + BOOLEAN started = FALSE; + BD_ADDR dummy_bda = {0}; + tBT_DEVICE_TYPE dev_type; + + if (p_dev_rec != NULL && + p_dev_rec->device_type & BT_DEVICE_TYPE_BLE) { + if (to_add) { + if (p_dev_rec->ble.ble_addr_type == BLE_ADDR_PUBLIC || !BTM_BLE_IS_RESOLVE_BDA(bd_addr)) { + started = btsnd_hcic_ble_add_white_list (p_dev_rec->ble.ble_addr_type, bd_addr); + p_dev_rec->ble.in_controller_list |= BTM_WHITE_LIST_BIT; + } else if (memcmp(p_dev_rec->ble.static_addr, bd_addr, BD_ADDR_LEN) != 0 && + memcmp(p_dev_rec->ble.static_addr, dummy_bda, BD_ADDR_LEN) != 0) { + started = btsnd_hcic_ble_add_white_list (p_dev_rec->ble.static_addr_type, + p_dev_rec->ble.static_addr); + p_dev_rec->ble.in_controller_list |= BTM_WHITE_LIST_BIT; + } + } else { + if (p_dev_rec->ble.ble_addr_type == BLE_ADDR_PUBLIC || !BTM_BLE_IS_RESOLVE_BDA(bd_addr)) { + started = btsnd_hcic_ble_remove_from_white_list (p_dev_rec->ble.ble_addr_type, bd_addr); + } + if (memcmp(p_dev_rec->ble.static_addr, dummy_bda, BD_ADDR_LEN) != 0 && + memcmp(p_dev_rec->ble.static_addr, bd_addr, BD_ADDR_LEN) != 0) { + started = btsnd_hcic_ble_remove_from_white_list (p_dev_rec->ble.static_addr_type, p_dev_rec->ble.static_addr); + } + p_dev_rec->ble.in_controller_list &= ~BTM_WHITE_LIST_BIT; + } + } // if not a known device, shall we add it? + else { + BTM_ReadDevInfo(bd_addr, &dev_type, &addr_type); + + if (to_add) { + started = btsnd_hcic_ble_add_white_list (addr_type, bd_addr); + }else{ + started = btsnd_hcic_ble_remove_from_white_list (addr_type, bd_addr); + } + } + + return started; + */ + + /* Controller do not support resolvable address now, only support public address and static random address */ + BOOLEAN started = FALSE; + if(wl_addr_type > BLE_ADDR_RANDOM) { + BTM_TRACE_ERROR("wl_addr_type is error\n"); + return started; + } + + if (to_add) { + started = btsnd_hcic_ble_add_white_list (wl_addr_type, bd_addr); + }else{ + started = btsnd_hcic_ble_remove_from_white_list (wl_addr_type, bd_addr); + } + + return started; + + +} +/******************************************************************************* +** +** Function btm_execute_wl_dev_operation +** +** Description execute the pending whitelist device operation(loading or removing) +*******************************************************************************/ +BOOLEAN btm_execute_wl_dev_operation(void) +{ + tBTM_BLE_WL_OP *p_dev_op = btm_cb.ble_ctr_cb.wl_op_q; + UINT8 i = 0; + BOOLEAN rt = TRUE; + + for (i = 0; i < BTM_BLE_MAX_BG_CONN_DEV_NUM && rt; i ++, p_dev_op ++) { + if (p_dev_op->in_use) { + rt = btm_add_dev_to_controller(p_dev_op->to_add, p_dev_op->bd_addr, p_dev_op->addr_type); + memset(p_dev_op, 0, sizeof(tBTM_BLE_WL_OP)); + } else { + break; + } + } + return rt; +} +/******************************************************************************* +** +** Function btm_enq_wl_dev_operation +** +** Description enqueue the pending whitelist device operation(loading or removing). +*******************************************************************************/ +void btm_enq_wl_dev_operation(BOOLEAN to_add, BD_ADDR bd_addr, tBLE_ADDR_TYPE addr_type) +{ + tBTM_BLE_WL_OP *p_dev_op = btm_cb.ble_ctr_cb.wl_op_q; + UINT8 i = 0; + + for (i = 0; i < BTM_BLE_MAX_BG_CONN_DEV_NUM; i ++, p_dev_op ++) { + if (p_dev_op->in_use && p_dev_op->addr_type == addr_type && !memcmp(p_dev_op->bd_addr, bd_addr, BD_ADDR_LEN)) { + p_dev_op->to_add = to_add; + return; + } else if (!p_dev_op->in_use) { + break; + } + } + if (i != BTM_BLE_MAX_BG_CONN_DEV_NUM) { + p_dev_op->in_use = TRUE; + p_dev_op->to_add = to_add; + p_dev_op->addr_type = addr_type; + memcpy(p_dev_op->bd_addr, bd_addr, BD_ADDR_LEN); + } else { + BTM_TRACE_ERROR("max pending WL operation reached, discard"); + } + return; +} + +/******************************************************************************* +** +** Function btm_update_dev_to_white_list +** +** Description This function adds or removes a device into/from +** the white list. +** +*******************************************************************************/ +BOOLEAN btm_update_dev_to_white_list(BOOLEAN to_add, BD_ADDR bd_addr, tBLE_ADDR_TYPE addr_type, tBTM_UPDATE_WHITELIST_CBACK *update_wl_cb) +{ + if(addr_type > BLE_ADDR_RANDOM) { + BTM_TRACE_ERROR("%s address type is error, unable to add device", __func__); + if (update_wl_cb){ + update_wl_cb(HCI_ERR_ILLEGAL_PARAMETER_FMT,to_add); + } + return FALSE; + } + + BD_ADDR invalid_rand_addr_a, invalid_rand_addr_b; + memset(invalid_rand_addr_a, 0xff, sizeof(BD_ADDR)); + memset(invalid_rand_addr_b, 0x00, sizeof(BD_ADDR)); + + // look for public address information + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev(bd_addr); + // p_dev_rec is created at bluetooth initialization, p_dev_rec->ble.static_addr maybe be all 0 before pairing + if(p_dev_rec && memcmp(invalid_rand_addr_b, p_dev_rec->ble.static_addr, BD_ADDR_LEN) != 0) { + memcpy(bd_addr, p_dev_rec->ble.static_addr, BD_ADDR_LEN); + addr_type = p_dev_rec->ble.static_addr_type; + } + + // The device to be added to white list must be public address or random address + if(addr_type == BLE_ADDR_RANDOM) { + /* + A static address is a 48-bit randomly generated address and shall meet the following requirements: + • The two most significant bits of the address shall be equal to 1 + • All bits of the random part of the address shall not be equal to 1 + • All bits of the random part of the address shall not be equal to 0 + */ + invalid_rand_addr_b[0] = invalid_rand_addr_b[0] | BT_STATIC_RAND_ADDR_MASK; + if(memcmp(invalid_rand_addr_a, bd_addr, BD_ADDR_LEN) != 0 + && memcmp(invalid_rand_addr_b, bd_addr, BD_ADDR_LEN) != 0){ + // do nothing + } else { + BTC_TRACE_ERROR(" controller not support resolvable address"); + if (update_wl_cb){ + update_wl_cb(HCI_ERR_ILLEGAL_PARAMETER_FMT,to_add); + } + return FALSE; + } + + } + + tBTM_BLE_CB *p_cb = &btm_cb.ble_ctr_cb; + + if (to_add && p_cb->white_list_avail_size == 0) { + BTM_TRACE_ERROR("%s Whitelist full, unable to add device", __func__); + if (update_wl_cb){ + update_wl_cb(HCI_ERR_MEMORY_FULL,to_add); + } + return FALSE; + } + + if (to_add) { + /* added the bd_addr to the connection hash map queue */ + if(!background_connection_add((bt_bdaddr_t *)bd_addr)) { + /* if the bd_addr already exist in whitelist, just callback return TRUE */ + if (update_wl_cb){ + update_wl_cb(HCI_SUCCESS,to_add); + } + return TRUE; + } + } else { + /* remove the bd_addr to the connection hash map queue */ + if(!background_connection_remove((bt_bdaddr_t *)bd_addr)){ + /* if the bd_addr don't exist in whitelist, just callback return TRUE */ + if (update_wl_cb){ + update_wl_cb(HCI_SUCCESS,to_add); + } + return TRUE; + } + } + + if (update_wl_cb){ + //save add whitelist complete callback + p_cb->update_wl_cb = update_wl_cb; + } + /* stop the auto connect */ + btm_suspend_wl_activity(p_cb->wl_state); + /* save the bd_addr to the btm_cb env */ + btm_enq_wl_dev_operation(to_add, bd_addr, addr_type); + /* save the ba_addr to the controller white list */ + btm_wl_update_to_controller(); + return TRUE; +} + +/******************************************************************************* +** +** Function btm_ble_clear_white_list +** +** Description This function clears the white list. +** +*******************************************************************************/ +void btm_ble_clear_white_list (tBTM_UPDATE_WHITELIST_CBACK *update_wl_cb) +{ + tBTM_BLE_CB *p_cb = &btm_cb.ble_ctr_cb; + + BTM_TRACE_EVENT ("btm_ble_clear_white_list"); + btsnd_hcic_ble_clear_white_list(); + background_connections_clear(); + + if (update_wl_cb) { + p_cb->update_wl_cb = update_wl_cb; + } +} + +/******************************************************************************* +** +** Function btm_ble_clear_white_list_complete +** +** Description Indicates white list cleared. +** +*******************************************************************************/ +void btm_ble_clear_white_list_complete(UINT8 *p_data, UINT16 evt_len) +{ + tBTM_BLE_CB *p_cb = &btm_cb.ble_ctr_cb; + UINT8 status; + UNUSED(evt_len); + + BTM_TRACE_EVENT ("btm_ble_clear_white_list_complete"); + STREAM_TO_UINT8 (status, p_data); + + if (status == HCI_SUCCESS) { + p_cb->white_list_avail_size = controller_get_interface()->get_ble_white_list_size(); + } else { + BTM_TRACE_ERROR ("%s failed, status 0x%x\n", __func__, status); + } + + if (p_cb->update_wl_cb) { + (*p_cb->update_wl_cb)(status, BTM_WHITELIST_CLEAR); + } +} + +/******************************************************************************* +** +** Function btm_ble_white_list_init +** +** Description Initialize white list size +** +*******************************************************************************/ +void btm_ble_white_list_init(UINT8 white_list_size) +{ + BTM_TRACE_DEBUG("%s white_list_size = %d", __func__, white_list_size); + btm_cb.ble_ctr_cb.white_list_avail_size = white_list_size; +} + +/******************************************************************************* +** +** Function btm_ble_add_2_white_list_complete +** +** Description White list element added +** +*******************************************************************************/ +void btm_ble_add_2_white_list_complete(UINT8 status) +{ + BTM_TRACE_EVENT("%s status=%d", __func__, status); + tBTM_BLE_CB *p_cb = &btm_cb.ble_ctr_cb; + if (status == HCI_SUCCESS) { + --btm_cb.ble_ctr_cb.white_list_avail_size; + } + // add whitelist complete callback + if (p_cb->update_wl_cb) + { + (*p_cb->update_wl_cb)(status, BTM_WHITELIST_ADD); + } + +} + +/******************************************************************************* +** +** Function btm_ble_remove_from_white_list_complete +** +** Description White list element removal complete +** +*******************************************************************************/ +void btm_ble_remove_from_white_list_complete(UINT8 *p, UINT16 evt_len) +{ + tBTM_BLE_CB *p_cb = &btm_cb.ble_ctr_cb; + UNUSED(evt_len); + BTM_TRACE_EVENT ("%s status=%d", __func__, *p); + if (*p == HCI_SUCCESS) { + ++btm_cb.ble_ctr_cb.white_list_avail_size; + } + if (p_cb->update_wl_cb) + { + (*p_cb->update_wl_cb)(*p, BTM_WHITELIST_REMOVE); + } +} + +/******************************************************************************* +** +** Function btm_ble_start_auto_conn +** +** Description This function is to start/stop auto connection procedure. +** +** Parameters start: TRUE to start; FALSE to stop. +** +** Returns void +** +*******************************************************************************/ +BOOLEAN btm_ble_start_auto_conn(BOOLEAN start) +{ + tBTM_BLE_CB *p_cb = &btm_cb.ble_ctr_cb; + BD_ADDR dummy_bda = {0}; + BOOLEAN exec = TRUE; + UINT16 scan_int; + UINT16 scan_win; + UINT8 own_addr_type = p_cb->addr_mgnt_cb.own_addr_type; + UINT8 peer_addr_type = BLE_ADDR_PUBLIC; + + if (start) { + if (p_cb->conn_state == BLE_CONN_IDLE && background_connections_pending() + && btm_ble_topology_check(BTM_BLE_STATE_INIT)) { + p_cb->wl_state |= BTM_BLE_WL_INIT; + + btm_execute_wl_dev_operation(); + +#if BLE_PRIVACY_SPT == TRUE + btm_ble_enable_resolving_list_for_platform(BTM_BLE_RL_INIT); +#endif + scan_int = (p_cb->scan_int == BTM_BLE_SCAN_PARAM_UNDEF) ? + BTM_BLE_SCAN_SLOW_INT_1 : p_cb->scan_int; + scan_win = (p_cb->scan_win == BTM_BLE_SCAN_PARAM_UNDEF) ? + BTM_BLE_SCAN_SLOW_WIN_1 : p_cb->scan_win; + +#if BLE_PRIVACY_SPT == TRUE + if (btm_cb.ble_ctr_cb.rl_state != BTM_BLE_RL_IDLE + && controller_get_interface()->supports_ble_privacy()) { + own_addr_type |= BLE_ADDR_TYPE_ID_BIT; + peer_addr_type |= BLE_ADDR_TYPE_ID_BIT; + } +#endif + + if (!btsnd_hcic_ble_create_ll_conn (scan_int, /* UINT16 scan_int */ + scan_win, /* UINT16 scan_win */ + 0x01, /* UINT8 white_list */ + peer_addr_type, /* UINT8 addr_type_peer */ + dummy_bda, /* BD_ADDR bda_peer */ + own_addr_type, /* UINT8 addr_type_own */ + BTM_BLE_CONN_INT_MIN_DEF, /* UINT16 conn_int_min */ + BTM_BLE_CONN_INT_MAX_DEF, /* UINT16 conn_int_max */ + BTM_BLE_CONN_SLAVE_LATENCY_DEF, /* UINT16 conn_latency */ + BTM_BLE_CONN_TIMEOUT_DEF, /* UINT16 conn_timeout */ + 0, /* UINT16 min_len */ + 0)) { /* UINT16 max_len */ + /* start auto connection failed */ + exec = FALSE; + p_cb->wl_state &= ~BTM_BLE_WL_INIT; + } else { + btm_ble_set_conn_st (BLE_BG_CONN); + } + } else { + exec = FALSE; + } + } else { + if (p_cb->conn_state == BLE_BG_CONN) { + btsnd_hcic_ble_create_conn_cancel(); + btm_ble_set_conn_st (BLE_CONN_CANCEL); + p_cb->wl_state &= ~BTM_BLE_WL_INIT; + } else { + BTM_TRACE_DEBUG("conn_st = %d, not in auto conn state, cannot stop", p_cb->conn_state); + exec = FALSE; + } + } + return exec; +} + +/******************************************************************************* +** +** Function btm_ble_start_select_conn +** +** Description This function is to start/stop selective connection procedure. +** +** Parameters start: TRUE to start; FALSE to stop. +** p_select_cback: callback function to return application +** selection. +** +** Returns BOOLEAN: selective connection procedure is started. +** +*******************************************************************************/ +BOOLEAN btm_ble_start_select_conn(BOOLEAN start, tBTM_BLE_SEL_CBACK *p_select_cback) +{ + tBTM_BLE_CB *p_cb = &btm_cb.ble_ctr_cb; + UINT32 scan_int = p_cb->scan_int == BTM_BLE_SCAN_PARAM_UNDEF ? BTM_BLE_SCAN_FAST_INT : p_cb->scan_int; + UINT32 scan_win = p_cb->scan_win == BTM_BLE_SCAN_PARAM_UNDEF ? BTM_BLE_SCAN_FAST_WIN : p_cb->scan_win; + + BTM_TRACE_EVENT ("%s", __func__); + + if (start) { + if (!BTM_BLE_IS_SCAN_ACTIVE(p_cb->scan_activity)) { + if (p_select_cback != NULL) { + btm_cb.ble_ctr_cb.p_select_cback = p_select_cback; + } + + btm_execute_wl_dev_operation(); + + btm_update_scanner_filter_policy(SP_ADV_WL); + btm_cb.ble_ctr_cb.inq_var.scan_type = BTM_BLE_SCAN_MODE_PASS; + + /* Process advertising packets only from devices in the white list */ + if (btm_cb.cmn_ble_vsc_cb.extended_scan_support == 0) { + /* use passive scan by default */ + if (!btsnd_hcic_ble_set_scan_params(BTM_BLE_SCAN_MODE_PASS, + scan_int, + scan_win, + p_cb->addr_mgnt_cb.own_addr_type, + SP_ADV_WL)) { + return FALSE; + } + } else { + if (!btm_ble_send_extended_scan_params(BTM_BLE_SCAN_MODE_PASS, + scan_int, + scan_win, + p_cb->addr_mgnt_cb.own_addr_type, + SP_ADV_WL)) { + return FALSE; + } + } + + if (!btm_ble_topology_check(BTM_BLE_STATE_PASSIVE_SCAN)) { + BTM_TRACE_ERROR("peripheral device cannot initiate passive scan for a selective connection"); + return FALSE; + } else if (background_connections_pending()) { +#if BLE_PRIVACY_SPT == TRUE + btm_ble_enable_resolving_list_for_platform(BTM_BLE_RL_SCAN); +#endif + if (!btsnd_hcic_ble_set_scan_enable(TRUE, TRUE)) { /* duplicate filtering enabled */ + return FALSE; + } + /* mark up inquiry status flag */ + p_cb->scan_activity |= BTM_LE_SELECT_CONN_ACTIVE; + p_cb->wl_state |= BTM_BLE_WL_SCAN; + } + } else { + BTM_TRACE_ERROR("scan active, can not start selective connection procedure"); + return FALSE; + } + } else { /* disable selective connection mode */ + p_cb->scan_activity &= ~BTM_LE_SELECT_CONN_ACTIVE; + p_cb->p_select_cback = NULL; + p_cb->wl_state &= ~BTM_BLE_WL_SCAN; + + /* stop scanning */ + if (!BTM_BLE_IS_SCAN_ACTIVE(p_cb->scan_activity)) { + btm_ble_stop_scan(); /* duplicate filtering enabled */ + } + } + return TRUE; +} +/******************************************************************************* +** +** Function btm_ble_initiate_select_conn +** +** Description This function is to start/stop selective connection procedure. +** +** Parameters start: TRUE to start; FALSE to stop. +** p_select_cback: callback function to return application +** selection. +** +** Returns BOOLEAN: selective connection procedure is started. +** +*******************************************************************************/ +void btm_ble_initiate_select_conn(BD_ADDR bda) +{ + BTM_TRACE_EVENT ("btm_ble_initiate_select_conn"); + + /* use direct connection procedure to initiate connection */ + if (!L2CA_ConnectFixedChnl(L2CAP_ATT_CID, bda, BLE_ADDR_UNKNOWN_TYPE, FALSE)) { + BTM_TRACE_ERROR("btm_ble_initiate_select_conn failed"); + } +} +/******************************************************************************* +** +** Function btm_ble_suspend_bg_conn +** +** Description This function is to suspend an active background connection +** procedure. +** +** Parameters none. +** +** Returns none. +** +*******************************************************************************/ +BOOLEAN btm_ble_suspend_bg_conn(void) +{ + BTM_TRACE_EVENT ("%s\n", __func__); + + if (btm_cb.ble_ctr_cb.bg_conn_type == BTM_BLE_CONN_AUTO) { + return btm_ble_start_auto_conn(FALSE); + } else if (btm_cb.ble_ctr_cb.bg_conn_type == BTM_BLE_CONN_SELECTIVE) { + return btm_ble_start_select_conn(FALSE, NULL); + } + + return FALSE; +} +/******************************************************************************* +** +** Function btm_suspend_wl_activity +** +** Description This function is to suspend white list related activity +** +** Returns none. +** +*******************************************************************************/ +static void btm_suspend_wl_activity(tBTM_BLE_WL_STATE wl_state) +{ + if (wl_state & BTM_BLE_WL_INIT) { + btm_ble_start_auto_conn(FALSE); + } + if (wl_state & BTM_BLE_WL_SCAN) { + btm_ble_start_select_conn(FALSE, NULL); + } + if (wl_state & BTM_BLE_WL_ADV) { + btm_ble_stop_adv(); + } + +} +/******************************************************************************* +** +** Function btm_resume_wl_activity +** +** Description This function is to resume white list related activity +** +** Returns none. +** +*******************************************************************************/ +void btm_resume_wl_activity(tBTM_BLE_WL_STATE wl_state) +{ + btm_ble_resume_bg_conn(); + if (wl_state & BTM_BLE_WL_ADV) { + btm_ble_start_adv(); + } + +} + +/******************************************************************************* +** +** Function btm_wl_update_to_controller +** +** Description This function is to update white list to controller +** +** Returns none. +** +*******************************************************************************/ +static void btm_wl_update_to_controller(void) +{ + /* whitelist will be added in the btm_ble_resume_bg_conn(), we do not + support background connection now, so we nedd to use btm_execute_wl_dev_operation + to add whitelist directly ,if we support background connection in the future, + please delete btm_execute_wl_dev_operation(). */ + btm_execute_wl_dev_operation(); + +} +/******************************************************************************* +** +** Function btm_ble_resume_bg_conn +** +** Description This function is to resume a background auto connection +** procedure. +** +** Parameters none. +** +** Returns none. +** +*******************************************************************************/ +BOOLEAN btm_ble_resume_bg_conn(void) +{ + tBTM_BLE_CB *p_cb = &btm_cb.ble_ctr_cb; + BOOLEAN ret = FALSE; + + if (p_cb->bg_conn_type != BTM_BLE_CONN_NONE) { + if (p_cb->bg_conn_type == BTM_BLE_CONN_AUTO) { + ret = btm_ble_start_auto_conn(TRUE); + } + + if (p_cb->bg_conn_type == BTM_BLE_CONN_SELECTIVE) { + ret = btm_ble_start_select_conn(TRUE, btm_cb.ble_ctr_cb.p_select_cback); + } + } + + return ret; +} +/******************************************************************************* +** +** Function btm_ble_get_conn_st +** +** Description This function get BLE connection state +** +** Returns connection state +** +*******************************************************************************/ +tBTM_BLE_CONN_ST btm_ble_get_conn_st(void) +{ + return btm_cb.ble_ctr_cb.conn_state; +} +/******************************************************************************* +** +** Function btm_ble_set_conn_st +** +** Description This function set BLE connection state +** +** Returns None. +** +*******************************************************************************/ +void btm_ble_set_conn_st(tBTM_BLE_CONN_ST new_st) +{ + btm_cb.ble_ctr_cb.conn_state = new_st; + + if (new_st == BLE_BG_CONN || new_st == BLE_DIR_CONN) { + btm_ble_set_topology_mask(BTM_BLE_STATE_INIT_BIT); + } else { + btm_ble_clear_topology_mask(BTM_BLE_STATE_INIT_BIT); + } +} + +/******************************************************************************* +** +** Function btm_ble_enqueue_direct_conn_req +** +** Description This function enqueue the direct connection request +** +** Returns None. +** +*******************************************************************************/ +void btm_ble_enqueue_direct_conn_req(void *p_param) +{ + tBTM_BLE_CONN_REQ *p = (tBTM_BLE_CONN_REQ *)osi_malloc(sizeof(tBTM_BLE_CONN_REQ)); + + p->p_param = p_param; + + fixed_queue_enqueue(btm_cb.ble_ctr_cb.conn_pending_q, p, FIXED_QUEUE_MAX_TIMEOUT); +} +/******************************************************************************* +** +** Function btm_send_pending_direct_conn +** +** Description This function send the pending direct connection request in queue +** +** Returns TRUE if started, FALSE otherwise +** +*******************************************************************************/ +BOOLEAN btm_send_pending_direct_conn(void) +{ + tBTM_BLE_CONN_REQ *p_req; + BOOLEAN rt = FALSE; + + p_req = (tBTM_BLE_CONN_REQ*)fixed_queue_dequeue(btm_cb.ble_ctr_cb.conn_pending_q, 0); + if (p_req != NULL) { + rt = l2cble_init_direct_conn((tL2C_LCB *)(p_req->p_param)); + + osi_free((void *)p_req); + } + + return rt; +} + +#endif diff --git a/lib/bt/host/bluedroid/stack/btm/btm_ble_cont_energy.c b/lib/bt/host/bluedroid/stack/btm/btm_ble_cont_energy.c new file mode 100644 index 00000000..dd23d3e9 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/btm/btm_ble_cont_energy.c @@ -0,0 +1,108 @@ +/****************************************************************************** + * + * Copyright (C) 2014 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +#include +#include "common/bt_target.h" + +#if (BLE_INCLUDED == TRUE) +#include "stack/bt_types.h" +#include "stack/hcimsgs.h" +#include "stack/btu.h" +#include "btm_int.h" +//#include "bt_utils.h" +#include "stack/hcidefs.h" +#include "stack/btm_ble_api.h" + +tBTM_BLE_ENERGY_INFO_CB ble_energy_info_cb; + +/******************************************************************************* +** +** Function btm_ble_cont_energy_cmpl_cback +** +** Description Controller VSC complete callback +** +** Parameters +** +** Returns void +** +*******************************************************************************/ +void btm_ble_cont_energy_cmpl_cback (tBTM_VSC_CMPL *p_params) +{ + UINT8 *p = p_params->p_param_buf; + UINT16 len = p_params->param_len; + UINT8 status = 0; + UINT32 total_tx_time = 0, total_rx_time = 0, total_idle_time = 0, total_energy_used = 0; + + if (len < 17) { + BTM_TRACE_ERROR("wrong length for btm_ble_cont_energy_cmpl_cback"); + return; + } + + STREAM_TO_UINT8(status, p); + STREAM_TO_UINT32(total_tx_time, p); + STREAM_TO_UINT32(total_rx_time, p); + STREAM_TO_UINT32(total_idle_time, p); + STREAM_TO_UINT32(total_energy_used, p); + + BTM_TRACE_DEBUG("energy_info status=%d,tx_t=%u, rx_t=%u, ener_used=%u, idle_t=%u", + status, total_tx_time, total_rx_time, total_energy_used, total_idle_time); + + if (NULL != ble_energy_info_cb.p_ener_cback) { + ble_energy_info_cb.p_ener_cback(total_tx_time, total_rx_time, total_idle_time, + total_energy_used, status); + } + + return; +} + +/******************************************************************************* +** +** Function BTM_BleGetEnergyInfo +** +** Description This function obtains the energy info +** +** Parameters p_ener_cback - Callback pointer +** +** Returns status +** +*******************************************************************************/ +tBTM_STATUS BTM_BleGetEnergyInfo(tBTM_BLE_ENERGY_INFO_CBACK *p_ener_cback) +{ + tBTM_STATUS status = BTM_ILLEGAL_VALUE; + tBTM_BLE_VSC_CB cmn_ble_vsc_cb; + + BTM_BleGetVendorCapabilities(&cmn_ble_vsc_cb); + + BTM_TRACE_EVENT("BTM_BleGetEnergyInfo\n"); + + if (0 == cmn_ble_vsc_cb.energy_support) { + BTM_TRACE_ERROR("Controller does not support get energy info\n"); + return BTM_ERR_PROCESSING; + } + + ble_energy_info_cb.p_ener_cback = p_ener_cback; + if ((status = BTM_VendorSpecificCommand (HCI_BLE_ENERGY_INFO_OCF, 0, NULL, + btm_ble_cont_energy_cmpl_cback)) != BTM_CMD_STARTED) { + BTM_TRACE_ERROR("BTM_BleGetEnergyInfo status: %d", status); + return BTM_ILLEGAL_VALUE; + } + + return status; +} + +#endif diff --git a/lib/bt/host/bluedroid/stack/btm/btm_ble_gap.c b/lib/bt/host/bluedroid/stack/btm/btm_ble_gap.c new file mode 100644 index 00000000..fb27ba0f --- /dev/null +++ b/lib/bt/host/bluedroid/stack/btm/btm_ble_gap.c @@ -0,0 +1,4724 @@ +/****************************************************************************** + * + * Copyright (C) 2008-2014 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains functions for BLE GAP. + * + ******************************************************************************/ + +#include +//#include +#include + +#include "stack/bt_types.h" +//#include "bt_utils.h" +#include "btm_int.h" +#include "stack/btm_ble_api.h" +#include "stack/btu.h" +#include "device/controller.h" +#include "stack/hcimsgs.h" +#include "stack/gap_api.h" +#include "hci/hci_layer.h" +#if BLE_INCLUDED == TRUE +#include "l2c_int.h" + +#include "stack/gattdefs.h" +#include "gatt_int.h" + +#include "btm_ble_int.h" +//#define LOG_TAG "bt_btm_ble" +//#include "osi/include/log.h" +#include "osi/osi.h" +#include "osi/mutex.h" + +#define BTM_BLE_NAME_SHORT 0x01 +#define BTM_BLE_NAME_CMPL 0x02 + +#define BTM_BLE_FILTER_TARGET_UNKNOWN 0xff +#define BTM_BLE_POLICY_UNKNOWN 0xff + +#define BTM_EXT_BLE_RMT_NAME_TIMEOUT 30 +#define MIN_ADV_LENGTH 2 +#define BTM_VSC_CHIP_CAPABILITY_RSP_LEN_L_RELEASE 9 + +#define BTM_BLE_GAP_ADV_RPT_BATCH_SIZE (10) + +#if BTM_DYNAMIC_MEMORY == FALSE +static tBTM_BLE_VSC_CB cmn_ble_gap_vsc_cb; +#else +static tBTM_BLE_VSC_CB *cmn_ble_gap_vsc_cb_ptr; +#define cmn_ble_gap_vsc_cb (*cmn_ble_gap_vsc_cb_ptr) +#endif + +#if BLE_VND_INCLUDED == TRUE +static tBTM_BLE_CTRL_FEATURES_CBACK *p_ctrl_le_feature_rd_cmpl_cback = NULL; +#endif + +tBTM_CallbackFunc conn_param_update_cb; +/******************************************************************************* +** Local functions +*******************************************************************************/ +static void btm_ble_update_adv_flag(UINT8 flag); +static void btm_ble_process_adv_pkt_cont(BD_ADDR bda, UINT8 addr_type, UINT8 evt_type, UINT8 *p); +UINT8 *btm_ble_build_adv_data(tBTM_BLE_AD_MASK *p_data_mask, UINT8 **p_dst, + tBTM_BLE_ADV_DATA *p_data); +static UINT8 btm_set_conn_mode_adv_init_addr(tBTM_BLE_INQ_CB *p_cb, + BD_ADDR_PTR p_peer_addr_ptr, + tBLE_ADDR_TYPE *p_peer_addr_type, + tBLE_ADDR_TYPE *p_own_addr_type); +static void btm_ble_stop_observe(void); +static void btm_ble_stop_discover(void); +static void btm_adv_pkt_handler(void *arg); +uint32_t BTM_BleUpdateOwnType(uint8_t *own_bda_type, tBTM_START_ADV_CMPL_CBACK *cb); + +#define BTM_BLE_INQ_RESULT 0x01 +#define BTM_BLE_OBS_RESULT 0x02 +#define BTM_BLE_SEL_CONN_RESULT 0x04 +#define BTM_BLE_DISCO_RESULT 0x08 + +static bool is_ble50_inter = false; + +void btm_ble_inter_set(bool extble_inter) +{ + is_ble50_inter = extble_inter; +} + +bool btm_ble_inter_get(void) +{ + return is_ble50_inter; +} + +/* LE states combo bit to check */ +const UINT8 btm_le_state_combo_tbl[BTM_BLE_STATE_MAX][BTM_BLE_STATE_MAX][2] = { + {/* single state support */ + {HCI_SUPP_LE_STATES_CONN_ADV_MASK, HCI_SUPP_LE_STATES_CONN_ADV_OFF}, /* conn_adv */ + {HCI_SUPP_LE_STATES_INIT_MASK, HCI_SUPP_LE_STATES_INIT_OFF}, /* init */ + {HCI_SUPP_LE_STATES_INIT_MASK, HCI_SUPP_LE_STATES_INIT_OFF}, /* master */ + {HCI_SUPP_LE_STATES_SLAVE_MASK, HCI_SUPP_LE_STATES_SLAVE_OFF}, /* slave */ + {HCI_SUPP_LE_STATES_LO_DUTY_DIR_ADV_MASK, HCI_SUPP_LE_STATES_LO_DUTY_DIR_ADV_OFF}, /* lo du dir adv */ + {HCI_SUPP_LE_STATES_HI_DUTY_DIR_ADV_MASK, HCI_SUPP_LE_STATES_HI_DUTY_DIR_ADV_OFF}, /* hi duty dir adv */ + {HCI_SUPP_LE_STATES_NON_CONN_ADV_MASK, HCI_SUPP_LE_STATES_NON_CONN_ADV_OFF}, /* non connectable adv */ + {HCI_SUPP_LE_STATES_PASS_SCAN_MASK, HCI_SUPP_LE_STATES_PASS_SCAN_OFF}, /* passive scan */ + {HCI_SUPP_LE_STATES_ACTIVE_SCAN_MASK, HCI_SUPP_LE_STATES_ACTIVE_SCAN_OFF}, /* active scan */ + {HCI_SUPP_LE_STATES_SCAN_ADV_MASK, HCI_SUPP_LE_STATESSCAN_ADV_OFF} /* scanable adv */ + }, + { /* conn_adv =0 */ + {0, 0}, /* conn_adv */ + {HCI_SUPP_LE_STATES_CONN_ADV_INIT_MASK, HCI_SUPP_LE_STATES_CONN_ADV_INIT_OFF}, /* init: 32 */ + {HCI_SUPP_LE_STATES_CONN_ADV_MASTER_MASK, HCI_SUPP_LE_STATES_CONN_ADV_MASTER_OFF}, /* master: 35 */ + {HCI_SUPP_LE_STATES_CONN_ADV_SLAVE_MASK, HCI_SUPP_LE_STATES_CONN_ADV_SLAVE_OFF}, /* slave: 38,*/ + {0, 0}, /* lo du dir adv */ + {0, 0}, /* hi duty dir adv */ + {0, 0}, /* non connectable adv */ + {HCI_SUPP_LE_STATES_CONN_ADV_PASS_SCAN_MASK, HCI_SUPP_LE_STATES_CONN_ADV_PASS_SCAN_OFF}, /* passive scan */ + {HCI_SUPP_LE_STATES_CONN_ADV_ACTIVE_SCAN_MASK, HCI_SUPP_LE_STATES_CONN_ADV_ACTIVE_SCAN_OFF}, /* active scan */ + {0, 0} /* scanable adv */ + }, + { /* init */ + {HCI_SUPP_LE_STATES_CONN_ADV_INIT_MASK, HCI_SUPP_LE_STATES_CONN_ADV_INIT_OFF}, /* conn_adv: 32 */ + {0, 0}, /* init */ + {HCI_SUPP_LE_STATES_INIT_MASTER_MASK, HCI_SUPP_LE_STATES_INIT_MASTER_OFF}, /* master 28 */ + {HCI_SUPP_LE_STATES_INIT_MASTER_SLAVE_MASK, HCI_SUPP_LE_STATES_INIT_MASTER_SLAVE_OFF}, /* slave 41 */ + {HCI_SUPP_LE_STATES_LO_DUTY_DIR_ADV_INIT_MASK, HCI_SUPP_LE_STATES_LO_DUTY_DIR_ADV_INIT_OFF} ,/* lo du dir adv 34 */ + {HCI_SUPP_LE_STATES_HI_DUTY_DIR_ADV_INIT_MASK, HCI_SUPP_LE_STATES_HI_DUTY_DIR_ADV_INIT_OFF}, /* hi duty dir adv 33 */ + {HCI_SUPP_LE_STATES_NON_CONN_INIT_MASK, HCI_SUPP_LE_STATES_NON_CONN_INIT_OFF}, /* non connectable adv */ + {HCI_SUPP_LE_STATES_PASS_SCAN_INIT_MASK, HCI_SUPP_LE_STATES_PASS_SCAN_INIT_OFF}, /* passive scan */ + {HCI_SUPP_LE_STATES_ACTIVE_SCAN_INIT_MASK, HCI_SUPP_LE_STATES_ACTIVE_SCAN_INIT_OFF}, /* active scan */ + {HCI_SUPP_LE_STATES_SCAN_ADV_INIT_MASK, HCI_SUPP_LE_STATES_SCAN_ADV_INIT_OFF} /* scanable adv */ + + }, + { /* master */ + {HCI_SUPP_LE_STATES_CONN_ADV_MASTER_MASK, HCI_SUPP_LE_STATES_CONN_ADV_MASTER_OFF}, /* conn_adv: 35 */ + {HCI_SUPP_LE_STATES_INIT_MASTER_MASK, HCI_SUPP_LE_STATES_INIT_MASTER_OFF}, /* init 28 */ + {HCI_SUPP_LE_STATES_INIT_MASTER_MASK, HCI_SUPP_LE_STATES_INIT_MASTER_OFF}, /* master 28 */ + {HCI_SUPP_LE_STATES_CONN_ADV_INIT_MASK, HCI_SUPP_LE_STATES_CONN_ADV_INIT_OFF}, /* slave: 32 */ + {HCI_SUPP_LE_STATES_LO_DUTY_DIR_ADV_MASTER_MASK, HCI_SUPP_LE_STATES_LO_DUTY_DIR_ADV_MASTER_OFF}, /* lo duty cycle adv 37 */ + {HCI_SUPP_LE_STATES_HI_DUTY_DIR_ADV_MASTER_MASK, HCI_SUPP_LE_STATES_HI_DUTY_DIR_ADV_MASTER_OFF}, /* hi duty cycle adv 36 */ + {HCI_SUPP_LE_STATES_NON_CONN_ADV_MASTER_MASK, HCI_SUPP_LE_STATES_NON_CONN_ADV_MASTER_OFF}, /* non connectable adv */ + {HCI_SUPP_LE_STATES_PASS_SCAN_MASTER_MASK, HCI_SUPP_LE_STATES_PASS_SCAN_MASTER_OFF}, /* passive scan */ + {HCI_SUPP_LE_STATES_ACTIVE_SCAN_MASTER_MASK, HCI_SUPP_LE_STATES_ACTIVE_SCAN_MASTER_OFF}, /* active scan */ + {HCI_SUPP_LE_STATES_SCAN_ADV_MASTER_MASK, HCI_SUPP_LE_STATES_SCAN_ADV_MASTER_OFF} /* scanable adv */ + + }, + { /* slave */ + {HCI_SUPP_LE_STATES_CONN_ADV_SLAVE_MASK, HCI_SUPP_LE_STATES_CONN_ADV_SLAVE_OFF}, /* conn_adv: 38,*/ + {HCI_SUPP_LE_STATES_INIT_MASTER_SLAVE_MASK, HCI_SUPP_LE_STATES_INIT_MASTER_SLAVE_OFF}, /* init 41 */ + {HCI_SUPP_LE_STATES_INIT_MASTER_SLAVE_MASK, HCI_SUPP_LE_STATES_INIT_MASTER_SLAVE_OFF}, /* master 41 */ + {HCI_SUPP_LE_STATES_CONN_ADV_SLAVE_MASK, HCI_SUPP_LE_STATES_CONN_ADV_SLAVE_OFF}, /* slave: 38,*/ + {HCI_SUPP_LE_STATES_LO_DUTY_DIR_ADV_SLAVE_MASK, HCI_SUPP_LE_STATES_LO_DUTY_DIR_ADV_SLAVE_OFF}, /* lo duty cycle adv 40 */ + {HCI_SUPP_LE_STATES_HI_DUTY_DIR_ADV_SLAVE_MASK, HCI_SUPP_LE_STATES_HI_DUTY_DIR_ADV_SLAVE_OFF}, /* hi duty cycle adv 39 */ + {HCI_SUPP_LE_STATES_NON_CONN_ADV_SLAVE_MASK, HCI_SUPP_LE_STATES_NON_CONN_ADV_SLAVE_OFF}, /* non connectable adv */ + {HCI_SUPP_LE_STATES_PASS_SCAN_SLAVE_MASK, HCI_SUPP_LE_STATES_PASS_SCAN_SLAVE_OFF}, /* passive scan */ + {HCI_SUPP_LE_STATES_ACTIVE_SCAN_SLAVE_MASK, HCI_SUPP_LE_STATES_ACTIVE_SCAN_SLAVE_OFF}, /* active scan */ + {HCI_SUPP_LE_STATES_SCAN_ADV_SLAVE_MASK, HCI_SUPP_LE_STATES_SCAN_ADV_SLAVE_OFF} /* scanable adv */ + + }, + { /* lo duty cycle adv */ + {0, 0}, /* conn_adv: 38,*/ + {HCI_SUPP_LE_STATES_LO_DUTY_DIR_ADV_INIT_MASK, HCI_SUPP_LE_STATES_LO_DUTY_DIR_ADV_INIT_OFF} ,/* init 34 */ + {HCI_SUPP_LE_STATES_LO_DUTY_DIR_ADV_MASTER_MASK, HCI_SUPP_LE_STATES_LO_DUTY_DIR_ADV_MASTER_OFF}, /* master 37 */ + {HCI_SUPP_LE_STATES_LO_DUTY_DIR_ADV_SLAVE_MASK, HCI_SUPP_LE_STATES_LO_DUTY_DIR_ADV_SLAVE_OFF}, /* slave: 40 */ + {0, 0}, /* lo duty cycle adv 40 */ + {0, 0}, /* hi duty cycle adv 39 */ + {0, 0}, /* non connectable adv */ + {HCI_SUPP_LE_STATES_LO_DUTY_DIR_ADV_PASS_SCAN_MASK, HCI_SUPP_LE_STATES_LO_DUTY_DIR_ADV_PASS_SCAN_OFF}, /* passive scan */ + {HCI_SUPP_LE_STATES_LO_DUTY_DIR_ADV_ACTIVE_SCAN_MASK, HCI_SUPP_LE_STATES_LO_DUTY_DIR_ADV_ACTIVE_SCAN_OFF}, /* active scan */ + {0, 0} /* scanable adv */ + }, + { /* hi duty cycle adv */ + {0, 0}, /* conn_adv: 38,*/ + {HCI_SUPP_LE_STATES_HI_DUTY_DIR_ADV_INIT_MASK, HCI_SUPP_LE_STATES_HI_DUTY_DIR_ADV_INIT_OFF}, /* init 33 */ + {HCI_SUPP_LE_STATES_HI_DUTY_DIR_ADV_MASTER_MASK, HCI_SUPP_LE_STATES_HI_DUTY_DIR_ADV_MASTER_OFF}, /* master 36 */ + {HCI_SUPP_LE_STATES_HI_DUTY_DIR_ADV_SLAVE_MASK, HCI_SUPP_LE_STATES_HI_DUTY_DIR_ADV_SLAVE_OFF}, /* slave: 39*/ + {0, 0}, /* lo duty cycle adv 40 */ + {0, 0}, /* hi duty cycle adv 39 */ + {0, 0}, /* non connectable adv */ + {HCI_SUPP_LE_STATES_HI_DUTY_DIR_ADV_PASS_SCAN_MASK, HCI_SUPP_LE_STATES_HI_DUTY_DIR_ADV_PASS_SCAN_OFF}, /* passive scan */ + {HCI_SUPP_LE_STATES_HI_DUTY_DIR_ADV_ACTIVE_SCAN_MASK, HCI_SUPP_LE_STATES_HI_DUTY_DIR_ADV_ACTIVE_SCAN_OFF}, /* active scan */ + {0, 0} /* scanable adv */ + }, + { /* non connectable adv */ + {0, 0}, /* conn_adv: */ + {HCI_SUPP_LE_STATES_NON_CONN_INIT_MASK, HCI_SUPP_LE_STATES_NON_CONN_INIT_OFF}, /* init */ + {HCI_SUPP_LE_STATES_NON_CONN_ADV_MASTER_MASK, HCI_SUPP_LE_STATES_NON_CONN_ADV_MASTER_OFF}, /* master */ + {HCI_SUPP_LE_STATES_NON_CONN_ADV_SLAVE_MASK, HCI_SUPP_LE_STATES_NON_CONN_ADV_SLAVE_OFF}, /* slave: */ + {0, 0}, /* lo duty cycle adv */ + {0, 0}, /* hi duty cycle adv */ + {0, 0}, /* non connectable adv */ + {HCI_SUPP_LE_STATES_NON_CONN_ADV_PASS_SCAN_MASK, HCI_SUPP_LE_STATES_NON_CONN_ADV_PASS_SCAN_OFF}, /* passive scan */ + {HCI_SUPP_LE_STATES_NON_CONN_ADV_ACTIVE_SCAN_MASK, HCI_SUPP_LE_STATES_NON_CONN_ADV_ACTIVE_SCAN_OFF}, /* active scan */ + {0, 0} /* scanable adv */ + }, + { /* passive scan */ + {HCI_SUPP_LE_STATES_CONN_ADV_PASS_SCAN_MASK, HCI_SUPP_LE_STATES_CONN_ADV_PASS_SCAN_OFF}, /* conn_adv: */ + {HCI_SUPP_LE_STATES_PASS_SCAN_INIT_MASK, HCI_SUPP_LE_STATES_PASS_SCAN_INIT_OFF}, /* init */ + {HCI_SUPP_LE_STATES_PASS_SCAN_MASTER_MASK, HCI_SUPP_LE_STATES_PASS_SCAN_MASTER_OFF}, /* master */ + {HCI_SUPP_LE_STATES_PASS_SCAN_SLAVE_MASK, HCI_SUPP_LE_STATES_PASS_SCAN_SLAVE_OFF}, /* slave: */ + {0, 0}, /* lo duty cycle adv */ + {HCI_SUPP_LE_STATES_HI_DUTY_DIR_ADV_PASS_SCAN_MASK, HCI_SUPP_LE_STATES_HI_DUTY_DIR_ADV_PASS_SCAN_OFF}, /* hi duty cycle adv */ + {HCI_SUPP_LE_STATES_NON_CONN_ADV_PASS_SCAN_MASK, HCI_SUPP_LE_STATES_NON_CONN_ADV_PASS_SCAN_OFF}, /* non connectable adv */ + {0, 0}, /* passive scan */ + {0, 0}, /* active scan */ + {HCI_SUPP_LE_STATES_SCAN_ADV_PASS_SCAN_MASK, HCI_SUPP_LE_STATES_SCAN_ADV_PASS_SCAN_OFF} /* scanable adv */ + }, + { /* active scan */ + {HCI_SUPP_LE_STATES_CONN_ADV_ACTIVE_SCAN_MASK, HCI_SUPP_LE_STATES_CONN_ADV_ACTIVE_SCAN_OFF}, /* conn_adv: */ + {HCI_SUPP_LE_STATES_ACTIVE_SCAN_INIT_MASK, HCI_SUPP_LE_STATES_ACTIVE_SCAN_INIT_OFF}, /* init */ + {HCI_SUPP_LE_STATES_ACTIVE_SCAN_MASTER_MASK, HCI_SUPP_LE_STATES_ACTIVE_SCAN_MASTER_OFF}, /* master */ + {HCI_SUPP_LE_STATES_ACTIVE_SCAN_SLAVE_MASK, HCI_SUPP_LE_STATES_ACTIVE_SCAN_SLAVE_OFF}, /* slave: */ + {0, 0}, /* lo duty cycle adv */ + {HCI_SUPP_LE_STATES_HI_DUTY_DIR_ADV_ACTIVE_SCAN_MASK, HCI_SUPP_LE_STATES_HI_DUTY_DIR_ADV_ACTIVE_SCAN_OFF}, /* hi duty cycle adv */ + {HCI_SUPP_LE_STATES_NON_CONN_ADV_ACTIVE_SCAN_MASK, HCI_SUPP_LE_STATES_NON_CONN_ADV_ACTIVE_SCAN_OFF}, /* non connectable adv */ + {0, 0}, /* TODO: passive scan */ + {0, 0}, /* TODO: active scan */ + {HCI_SUPP_LE_STATES_SCAN_ADV_ACTIVE_SCAN_MASK, HCI_SUPP_LE_STATES_SCAN_ADV_ACTIVE_SCAN_OFF} /* scanable adv */ + }, + { /* scanable adv */ + {0, 0}, /* conn_adv: */ + {HCI_SUPP_LE_STATES_SCAN_ADV_INIT_MASK, HCI_SUPP_LE_STATES_SCAN_ADV_INIT_OFF}, /* init */ + {HCI_SUPP_LE_STATES_SCAN_ADV_MASTER_MASK, HCI_SUPP_LE_STATES_SCAN_ADV_MASTER_OFF}, /* master */ + {HCI_SUPP_LE_STATES_SCAN_ADV_SLAVE_MASK, HCI_SUPP_LE_STATES_SCAN_ADV_SLAVE_OFF}, /* slave: */ + {0, 0}, /* lo duty cycle adv */ + {0, 0}, /* hi duty cycle adv */ + {0, 0}, /* non connectable adv */ + {HCI_SUPP_LE_STATES_SCAN_ADV_PASS_SCAN_MASK, HCI_SUPP_LE_STATES_SCAN_ADV_PASS_SCAN_OFF}, /* passive scan */ + {HCI_SUPP_LE_STATES_SCAN_ADV_ACTIVE_SCAN_MASK, HCI_SUPP_LE_STATES_SCAN_ADV_ACTIVE_SCAN_OFF}, /* active scan */ + {0, 0} /* scanable adv */ + } + +}; +/* check LE combo state supported */ +#define BTM_LE_STATES_SUPPORTED(x, y, z) ((x)[(z)] & (y)) + +static osi_mutex_t adv_enable_lock; +static osi_mutex_t adv_data_lock; +static osi_mutex_t adv_param_lock; +static osi_mutex_t scan_enable_lock; +static osi_mutex_t scan_param_lock; +osi_sem_t adv_enable_sem; +osi_sem_t adv_data_sem; +osi_sem_t adv_param_sem; +osi_sem_t scan_enable_sem; +osi_sem_t scan_param_sem; +uint8_t adv_enable_status = 0; +uint8_t adv_data_status = 0; +uint8_t adv_param_status = 0; +uint8_t scan_enable_status = 0; +uint8_t scan_param_status = 0; + +void btm_ble_lock_init(void) +{ + osi_mutex_new(&adv_enable_lock); + osi_mutex_new(&adv_data_lock); + osi_mutex_new(&adv_param_lock); + osi_mutex_new(&scan_enable_lock); + osi_mutex_new(&scan_param_lock); +} + +void btm_ble_lock_free(void) +{ + osi_mutex_free(&adv_enable_lock); + osi_mutex_free(&adv_data_lock); + osi_mutex_free(&adv_param_lock); + osi_mutex_free(&scan_enable_lock); + osi_mutex_free(&scan_param_lock); +} + +void btm_ble_sem_init(void) +{ + osi_sem_new(&adv_enable_sem, 1, 0); + osi_sem_new(&adv_data_sem, 1, 0); + osi_sem_new(&adv_param_sem, 1, 0); + osi_sem_new(&scan_enable_sem, 1, 0); + osi_sem_new(&scan_param_sem, 1, 0); +} + +void btm_ble_sem_free(void) +{ + osi_sem_free(&adv_enable_sem); + osi_sem_free(&adv_data_sem); + osi_sem_free(&adv_param_sem); + osi_sem_free(&scan_enable_sem); + osi_sem_free(&scan_param_sem); +} + +/******************************************************************************* +** +** Function BTM_BleRegiseterConnParamCallback +** +** Description register connection parameters update callback func +** +** Returns void +** +*******************************************************************************/ +void BTM_BleRegiseterConnParamCallback(tBTM_UPDATE_CONN_PARAM_CBACK *update_conn_param_cb) +{ + conn_param_update_cb.update_conn_param_cb = update_conn_param_cb; +} + +/******************************************************************************* +** +** Function BTM_BleUpdateAdvWhitelist +** +** Description Add or remove device from advertising white list +** +** Returns void +** +*******************************************************************************/ +BOOLEAN BTM_BleUpdateAdvWhitelist(BOOLEAN add_remove, BD_ADDR remote_bda, tBLE_ADDR_TYPE addr_type, tBTM_UPDATE_WHITELIST_CBACK *update_wl_cb) +{ + return btm_update_dev_to_white_list(add_remove, remote_bda, addr_type, update_wl_cb); +} + +/******************************************************************************* +** +** Function BTM_BleUpdateAdvWhitelist +** +** Description Add or remove device from advertising white list +** +** Returns void +** +*******************************************************************************/ +void BTM_BleClearWhitelist(tBTM_UPDATE_WHITELIST_CBACK *update_wl_cb) +{ + btm_ble_clear_white_list(update_wl_cb); +} + +/******************************************************************************* +** +** Function BTM_BleUpdateAdvFilterPolicy +** +** Description This function update the filter policy of advertiser. +** +** Parameter adv_policy: advertising filter policy +** +** Return void +*******************************************************************************/ +void BTM_BleUpdateAdvFilterPolicy(tBTM_BLE_AFP adv_policy) +{ + tBTM_BLE_INQ_CB *p_cb = &btm_cb.ble_ctr_cb.inq_var; + tBLE_ADDR_TYPE init_addr_type = BLE_ADDR_PUBLIC; + BD_ADDR p_addr_ptr = {0}; + UINT8 adv_mode = p_cb->adv_mode; + + BTM_TRACE_EVENT ("BTM_BleUpdateAdvFilterPolicy\n"); + + if (!controller_get_interface()->supports_ble()) { + return; + } + + if (p_cb->afp != adv_policy) { + p_cb->afp = adv_policy; + + /* if adv active, stop and restart */ + btm_ble_stop_adv (); + + if (p_cb->connectable_mode & BTM_BLE_CONNECTABLE) { + p_cb->evt_type = btm_set_conn_mode_adv_init_addr(p_cb, p_addr_ptr, &init_addr_type, + &p_cb->adv_addr_type); + } + + btsnd_hcic_ble_write_adv_params ((UINT16)(p_cb->adv_interval_min ? p_cb->adv_interval_min : + BTM_BLE_GAP_ADV_SLOW_INT), + (UINT16)(p_cb->adv_interval_max ? p_cb->adv_interval_max : + BTM_BLE_GAP_ADV_SLOW_INT), + p_cb->evt_type, + p_cb->adv_addr_type, + init_addr_type, + p_addr_ptr, + p_cb->adv_chnl_map, + p_cb->afp); + + if (adv_mode == BTM_BLE_ADV_ENABLE) { + btm_ble_start_adv (); + } + + } +} + +/******************************************************************************* +** +** Function btm_ble_send_extended_scan_params +** +** Description This function sends out the extended scan parameters command to the controller +** +** Parameters scan_type - Scan type +** scan_int - Scan interval +** scan_win - Scan window +** addr_type_own - Own address type +** scan_filter_policy - Scan filter policy +** +** Returns TRUE or FALSE +** +*******************************************************************************/ +BOOLEAN btm_ble_send_extended_scan_params(UINT8 scan_type, UINT32 scan_int, + UINT32 scan_win, UINT8 addr_type_own, + UINT8 scan_filter_policy) +{ + UINT8 scan_param[HCIC_PARAM_SIZE_BLE_WRITE_EXTENDED_SCAN_PARAM]; + UINT8 *pp_scan = scan_param; + + memset(scan_param, 0, HCIC_PARAM_SIZE_BLE_WRITE_EXTENDED_SCAN_PARAM); + + UINT8_TO_STREAM(pp_scan, scan_type); + UINT32_TO_STREAM(pp_scan, scan_int); + UINT32_TO_STREAM(pp_scan, scan_win); + UINT8_TO_STREAM(pp_scan, addr_type_own); + UINT8_TO_STREAM(pp_scan, scan_filter_policy); + + BTM_TRACE_DEBUG("%s, %d, %d", __func__, scan_int, scan_win); + if ((BTM_VendorSpecificCommand(HCI_BLE_EXTENDED_SCAN_PARAMS_OCF, + HCIC_PARAM_SIZE_BLE_WRITE_EXTENDED_SCAN_PARAM, scan_param, NULL)) != BTM_SUCCESS) { + BTM_TRACE_ERROR("%s error sending extended scan parameters", __func__); + return FALSE; + } + return TRUE; +} + +/******************************************************************************* +** +** Function BTM_BleObserve +** +** Description This procedure keep the device listening for advertising +** events from a broadcast device. +** +** Parameters start: start or stop observe. +** white_list: use white list in observer mode or not. +** +** Returns void +** +*******************************************************************************/ +tBTM_STATUS BTM_BleObserve(BOOLEAN start, UINT32 duration, + tBTM_INQ_RESULTS_CB *p_results_cb, tBTM_CMPL_CB *p_cmpl_cb) +{ + tBTM_BLE_INQ_CB *p_inq = &btm_cb.ble_ctr_cb.inq_var; + tBTM_STATUS status = BTM_WRONG_MODE; + + UINT32 scan_interval = !p_inq->scan_interval ? BTM_BLE_GAP_DISC_SCAN_INT : p_inq->scan_interval; + UINT32 scan_window = !p_inq->scan_window ? BTM_BLE_GAP_DISC_SCAN_WIN : p_inq->scan_window; + + BTM_TRACE_EVENT ("%s : scan_type:%d, %d, %d\n", __func__, btm_cb.btm_inq_vars.scan_type, + p_inq->scan_interval, p_inq->scan_window); + + if (!controller_get_interface()->supports_ble()) { + return BTM_ILLEGAL_VALUE; + } + + if (start) { + /* shared inquiry database, do not allow observe if any inquiry is active */ + if (BTM_BLE_IS_OBS_ACTIVE(btm_cb.ble_ctr_cb.scan_activity)) { + BTM_TRACE_ERROR("%s Observe Already Active", __func__); + return status; + } + + btm_cb.ble_ctr_cb.p_obs_results_cb = p_results_cb; + btm_cb.ble_ctr_cb.p_obs_cmpl_cb = p_cmpl_cb; + status = BTM_CMD_STARTED; + + /* scan is not started */ + if (!BTM_BLE_IS_SCAN_ACTIVE(btm_cb.ble_ctr_cb.scan_activity)) { + /* allow config of scan type */ + p_inq->scan_type = (p_inq->scan_type == BTM_BLE_SCAN_MODE_NONE) ? + BTM_BLE_SCAN_MODE_ACTI : p_inq->scan_type; + /* assume observe always not using white list */ +#if (defined BLE_PRIVACY_SPT && BLE_PRIVACY_SPT == TRUE) + /* enable resolving list */ + //btm_ble_enable_resolving_list_for_platform(BTM_BLE_RL_SCAN); +#endif + + if (cmn_ble_gap_vsc_cb.extended_scan_support == 0) { + btsnd_hcic_ble_set_scan_params(p_inq->scan_type, (UINT16)scan_interval, + (UINT16)scan_window, + btm_cb.ble_ctr_cb.addr_mgnt_cb.own_addr_type, + BTM_BLE_DEFAULT_SFP); + } else { + btm_ble_send_extended_scan_params(p_inq->scan_type, scan_interval, scan_window, + btm_cb.ble_ctr_cb.addr_mgnt_cb.own_addr_type, + BTM_BLE_DEFAULT_SFP); + } + + status = btm_ble_start_scan(); + } + + if (status == BTM_CMD_STARTED) { + btm_cb.ble_ctr_cb.scan_activity |= BTM_LE_OBSERVE_ACTIVE; + if (duration != 0) + /* start observer timer */ + { + btu_start_timer (&btm_cb.ble_ctr_cb.obs_timer_ent, BTU_TTYPE_BLE_OBSERVE, duration); + } + } + } else if (BTM_BLE_IS_OBS_ACTIVE(btm_cb.ble_ctr_cb.scan_activity)) { + status = BTM_CMD_STARTED; + btm_ble_stop_observe(); + } else { + BTM_TRACE_ERROR("%s Observe not active\n", __func__); + } + + return status; + +} + +/******************************************************************************* +** +** Function BTM_BleScan +** +** Description This procedure keep the device listening for advertising +** events from a broadcast device. +** +** Parameters start: start or stop scan. +** white_list: use white list in observer mode or not. +** +** Returns void +** +*******************************************************************************/ +tBTM_STATUS BTM_BleScan(BOOLEAN start, UINT32 duration, + tBTM_INQ_RESULTS_CB *p_results_cb, tBTM_CMPL_CB *p_cmpl_cb, tBTM_INQ_DIS_CB *p_discard_cb) +{ + tBTM_BLE_INQ_CB *p_inq = &btm_cb.ble_ctr_cb.inq_var; + tBTM_STATUS status = BTM_WRONG_MODE; + + if (!controller_get_interface()->supports_ble()) { + return BTM_ILLEGAL_VALUE; + } + + if (start) { + /* shared inquiry database, do not allow scan if any inquiry is active */ + if (BTM_BLE_IS_DISCO_ACTIVE(btm_cb.ble_ctr_cb.scan_activity)) { + BTM_TRACE_ERROR("%s scan already active", __func__); + return status; + } + + btm_cb.ble_ctr_cb.p_scan_results_cb = p_results_cb; + btm_cb.ble_ctr_cb.p_scan_cmpl_cb = p_cmpl_cb; + btm_cb.ble_ctr_cb.p_obs_discard_cb = p_discard_cb; + status = BTM_CMD_STARTED; + + /* scan is not started */ + if (!BTM_BLE_IS_SCAN_ACTIVE(btm_cb.ble_ctr_cb.scan_activity)) { + /* assume observe always not using white list */ +#if (defined BLE_PRIVACY_SPT && BLE_PRIVACY_SPT == TRUE) + /* enable resolving list */ + //btm_ble_enable_resolving_list_for_platform(BTM_BLE_RL_SCAN); +#endif + // if not set scan params, set default scan params + if (!p_inq->scan_params_set) { + /* allow config of scan type */ + p_inq->scan_type = BTM_BLE_SCAN_MODE_ACTI; + p_inq->scan_interval = BTM_BLE_GAP_DISC_SCAN_INT; + p_inq->scan_window = BTM_BLE_GAP_DISC_SCAN_WIN; + p_inq->sfp = BTM_BLE_DEFAULT_SFP; + p_inq->scan_params_set = TRUE; + p_inq->scan_duplicate_filter = BTM_BLE_DUPLICATE_DISABLE; + btsnd_hcic_ble_set_scan_params(p_inq->scan_type, p_inq->scan_interval, + p_inq->scan_window, + btm_cb.ble_ctr_cb.addr_mgnt_cb.own_addr_type, + p_inq->sfp); + } + status = btm_ble_start_scan(); + } + + if (status == BTM_CMD_STARTED) { + btm_cb.ble_ctr_cb.scan_activity |= BTM_LE_DISCOVER_ACTIVE; + if (duration != 0) + /* start observer timer */ + { + btu_start_timer (&btm_cb.ble_ctr_cb.scan_timer_ent, BTU_TTYPE_BLE_SCAN, duration); + } + } + } else if (BTM_BLE_IS_DISCO_ACTIVE(btm_cb.ble_ctr_cb.scan_activity)) { + status = BTM_CMD_STARTED; + btm_ble_stop_discover(); + } else { + BTM_TRACE_ERROR("%s scan not active\n", __func__); + } + + return status; + +} + +/******************************************************************************* +** +** Function BTM_BleBroadcast +** +** Description This function is to start or stop broadcasting. +** +** Parameters start: start or stop broadcasting. +** +** Returns status. +** +*******************************************************************************/ +tBTM_STATUS BTM_BleBroadcast(BOOLEAN start, tBTM_START_STOP_ADV_CMPL_CBACK *p_stop_adv_cback) +{ + tBTM_STATUS status = BTM_NO_RESOURCES; + tBTM_LE_RANDOM_CB *p_addr_cb = &btm_cb.ble_ctr_cb.addr_mgnt_cb; + tBTM_BLE_INQ_CB *p_cb = &btm_cb.ble_ctr_cb.inq_var; + UINT8 evt_type = p_cb->scan_rsp ? BTM_BLE_DISCOVER_EVT : BTM_BLE_NON_CONNECT_EVT; + + if (!controller_get_interface()->supports_ble()) { + return BTM_ILLEGAL_VALUE; + } + +#ifdef BTM_BLE_PC_ADV_TEST_MODE + if (BTM_BLE_PC_ADV_TEST_MODE) { + evt_type = p_cb->scan_rsp ? BTM_BLE_CONNECT_EVT : BTM_BLE_NON_CONNECT_EVT; + } +#endif + + if (start) { + /* update adv params */ + if (!btsnd_hcic_ble_write_adv_params ((UINT16)(p_cb->adv_interval_min ? p_cb->adv_interval_min : + BTM_BLE_GAP_ADV_INT), + (UINT16)(p_cb->adv_interval_max ? p_cb->adv_interval_max : + BTM_BLE_GAP_ADV_INT), + evt_type, + p_addr_cb->own_addr_type, + p_cb->direct_bda.type, + p_cb->direct_bda.bda, + p_cb->adv_chnl_map, + p_cb->afp)) + + { + status = BTM_NO_RESOURCES; + } else { + p_cb->evt_type = evt_type; + } + + status = btm_ble_start_adv (); + } else { + //save the stop adv callback to the BTM env. + p_cb->p_stop_adv_cb = p_stop_adv_cback; + status = btm_ble_stop_adv(); +#if BLE_PRIVACY_SPT == TRUE + btm_ble_disable_resolving_list(BTM_BLE_RL_ADV, TRUE); +#endif + } + return status; +} + +#if BLE_VND_INCLUDED == TRUE +/******************************************************************************* +** +** Function btm_vsc_brcm_features_complete +** +** Description Command Complete callback for HCI_BLE_VENDOR_CAP_OCF +** +** Returns void +** +*******************************************************************************/ +static void btm_ble_vendor_capability_vsc_cmpl_cback (tBTM_VSC_CMPL *p_vcs_cplt_params) +{ + UINT8 status = 0xFF; + UINT8 *p; + + BTM_TRACE_DEBUG("%s", __func__); + + /* Check status of command complete event */ + if ((p_vcs_cplt_params->opcode == HCI_BLE_VENDOR_CAP_OCF) && + (p_vcs_cplt_params->param_len > 0)) { + p = p_vcs_cplt_params->p_param_buf; + STREAM_TO_UINT8(status, p); + } + + if (status == HCI_SUCCESS) { + STREAM_TO_UINT8(btm_cb.cmn_ble_vsc_cb.adv_inst_max, p); + STREAM_TO_UINT8(btm_cb.cmn_ble_vsc_cb.rpa_offloading, p); + STREAM_TO_UINT16(btm_cb.cmn_ble_vsc_cb.tot_scan_results_strg, p); + STREAM_TO_UINT8(btm_cb.cmn_ble_vsc_cb.max_irk_list_sz, p); + STREAM_TO_UINT8(btm_cb.cmn_ble_vsc_cb.filter_support, p); + STREAM_TO_UINT8(btm_cb.cmn_ble_vsc_cb.max_filter, p); + STREAM_TO_UINT8(btm_cb.cmn_ble_vsc_cb.energy_support, p); + + if (p_vcs_cplt_params->param_len > BTM_VSC_CHIP_CAPABILITY_RSP_LEN_L_RELEASE) { + STREAM_TO_UINT16(btm_cb.cmn_ble_vsc_cb.version_supported, p); + } else { + btm_cb.cmn_ble_vsc_cb.version_supported = BTM_VSC_CHIP_CAPABILITY_L_VERSION; + } + + if (btm_cb.cmn_ble_vsc_cb.version_supported >= BTM_VSC_CHIP_CAPABILITY_M_VERSION) { + STREAM_TO_UINT16(btm_cb.cmn_ble_vsc_cb.total_trackable_advertisers, p); + STREAM_TO_UINT16(btm_cb.cmn_ble_vsc_cb.extended_scan_support, p); + STREAM_TO_UINT16(btm_cb.cmn_ble_vsc_cb.debug_logging_supported, p); + } + btm_cb.cmn_ble_vsc_cb.values_read = TRUE; + } + + BTM_TRACE_DEBUG("%s: stat=%d, irk=%d, ADV ins:%d, rpa=%d, ener=%d, ext_scan=%d", + __func__, status, btm_cb.cmn_ble_vsc_cb.max_irk_list_sz, + btm_cb.cmn_ble_vsc_cb.adv_inst_max, btm_cb.cmn_ble_vsc_cb.rpa_offloading, + btm_cb.cmn_ble_vsc_cb.energy_support, btm_cb.cmn_ble_vsc_cb.extended_scan_support); + + if (BTM_BleMaxMultiAdvInstanceCount() > 0) { + btm_ble_multi_adv_init(); + } + + if (btm_cb.cmn_ble_vsc_cb.max_filter > 0) { + btm_ble_adv_filter_init(); + } + +#if (defined BLE_PRIVACY_SPT && BLE_PRIVACY_SPT == TRUE) + /* VS capability included and non-4.2 device */ + if (btm_cb.cmn_ble_vsc_cb.max_irk_list_sz > 0 && + controller_get_interface()->get_ble_resolving_list_max_size() == 0) { + btm_ble_resolving_list_init(btm_cb.cmn_ble_vsc_cb.max_irk_list_sz); + } +#endif + + if (btm_cb.cmn_ble_vsc_cb.tot_scan_results_strg > 0) { + btm_ble_batchscan_init(); + } + + if (p_ctrl_le_feature_rd_cmpl_cback != NULL) { + p_ctrl_le_feature_rd_cmpl_cback(status); + } +} +#endif + +/******************************************************************************* +** +** Function BTM_BleGetVendorCapabilities +** +** Description This function reads local LE features +** +** Parameters p_cmn_vsc_cb : Locala LE capability structure +** +** Returns void +** +*******************************************************************************/ +extern void BTM_BleGetVendorCapabilities(tBTM_BLE_VSC_CB *p_cmn_vsc_cb) +{ + BTM_TRACE_DEBUG("BTM_BleGetVendorCapabilities"); + + if (NULL != p_cmn_vsc_cb) { + *p_cmn_vsc_cb = btm_cb.cmn_ble_vsc_cb; + } +} + +/****************************************************************************** +** +** Function BTM_BleReadControllerFeatures +** +** Description Reads BLE specific controller features +** +** Parameters: tBTM_BLE_CTRL_FEATURES_CBACK : Callback to notify when features are read +** +** Returns void +** +*******************************************************************************/ +extern void BTM_BleReadControllerFeatures(tBTM_BLE_CTRL_FEATURES_CBACK *p_vsc_cback) +{ + if (TRUE == btm_cb.cmn_ble_vsc_cb.values_read) { + return; + } + +#if BLE_VND_INCLUDED == TRUE + BTM_TRACE_DEBUG("BTM_BleReadControllerFeatures"); + + p_ctrl_le_feature_rd_cmpl_cback = p_vsc_cback; + if ( BTM_VendorSpecificCommand (HCI_BLE_VENDOR_CAP_OCF, + 0, + NULL, + btm_ble_vendor_capability_vsc_cmpl_cback) + != BTM_CMD_STARTED) { + BTM_TRACE_ERROR("LE Get_Vendor Capabilities Command Failed."); + } +#else + UNUSED(p_vsc_cback); +#endif + return ; +} + +void BTM_VendorHciEchoCmdCallback(tBTM_VSC_CMPL *p1) +{ +#if (!CONFIG_BT_STACK_NO_LOG) + if (!p1) { + return; + } + uint8_t *p = p1->p_param_buf; + uint8_t status, echo; + STREAM_TO_UINT8 (status, p); + STREAM_TO_UINT8 (echo, p); +#endif + BTM_TRACE_DEBUG("%s status 0x%x echo 0x%x", __func__, status, echo); +} + +/****************************************************************************** +** +** Function BTM_VendorHciEchoCmdTest +** +** Description vendor common echo hci cmd test, controller will return status and echo +** +** Parameters: echo : echo value +** +** Returns void +** +*******************************************************************************/ +void BTM_VendorHciEchoCmdTest(uint8_t echo) +{ + BTM_VendorSpecificCommand (HCI_VENDOR_COMMON_ECHO_CMD_OPCODE, + 1, + &echo, + BTM_VendorHciEchoCmdCallback); +} + +/******************************************************************************* +** +** Function BTM_BleEnableMixedPrivacyMode +** +** Description This function is called to enabled Mixed mode if privacy 1.2 +** is applicable in controller. +** +** Parameters mixed_on: mixed mode to be used or not. +** +** Returns void +** +*******************************************************************************/ +void BTM_BleEnableMixedPrivacyMode(BOOLEAN mixed_on) +{ + +#if BLE_PRIVACY_SPT == TRUE + btm_cb.ble_ctr_cb.mixed_mode = mixed_on; + + /* TODO: send VSC to enabled mixed mode */ +#endif +} + +/******************************************************************************* +** +** Function BTM_BleConfigPrivacy +** +** Description This function is called to enable or disable the privacy in +** LE channel of the local device. +** +** Parameters privacy_mode: privacy mode on or off. +** +** Returns BOOLEAN privacy mode set success; otherwise failed. +** +*******************************************************************************/ +BOOLEAN BTM_BleConfigPrivacy(BOOLEAN privacy_mode, tBTM_SET_LOCAL_PRIVACY_CBACK *set_local_privacy_cback) +{ +#if BLE_PRIVACY_SPT == TRUE + tBTM_BLE_CB *p_cb = &btm_cb.ble_ctr_cb; + tBTM_LE_RANDOM_CB *random_cb = &btm_cb.ble_ctr_cb.addr_mgnt_cb; + if (random_cb){ + random_cb->set_local_privacy_cback = set_local_privacy_cback; + }else{ + BTM_TRACE_ERROR("%s,random_cb = NULL", __func__); + } + + BTM_TRACE_EVENT ("%s\n", __func__); + + /* if LE is not supported, return error */ + if (!controller_get_interface()->supports_ble()) { + return FALSE; + } + +#if (defined(GAP_INCLUDED) && GAP_INCLUDED == TRUE && GATTS_INCLUDED == TRUE) + uint8_t addr_resolution = 0; +#endif /* defined(GAP_INCLUDED) && GAP_INCLUDED == TRUE && GATTS_INCLUDED == TRUE */ + if (!privacy_mode) { /* if privacy disabled, always use public address */ + p_cb->addr_mgnt_cb.exist_addr_bit &= (~BTM_BLE_GAP_ADDR_BIT_RESOLVABLE); + memset(p_cb->addr_mgnt_cb.resolvale_addr, 0, BD_ADDR_LEN); + p_cb->addr_mgnt_cb.own_addr_type = BLE_ADDR_PUBLIC; + p_cb->privacy_mode = BTM_PRIVACY_NONE; + // Disable RPA function + btsnd_hcic_ble_set_addr_resolution_enable(FALSE); + } else { /* privacy is turned on*/ +#if (CONTROLLER_RPA_LIST_ENABLE == FALSE) + /* always set host random address, used when privacy 1.1 or priavcy 1.2 is disabled */ + btm_gen_resolvable_private_addr((void *)btm_gen_resolve_paddr_low); +#endif + + if (BTM_BleMaxMultiAdvInstanceCount() > 0) { + btm_ble_multi_adv_enb_privacy(privacy_mode); + } + + /* 4.2 controller only allow privacy 1.2 or mixed mode, resolvable private address in controller */ + if (controller_get_interface()->supports_ble_privacy()) { +#if (defined(GAP_INCLUDED) && GAP_INCLUDED == TRUE && GATTS_INCLUDED == TRUE) + addr_resolution = 1; +#endif /* defined(GAP_INCLUDED) && GAP_INCLUDED == TRUE && GATTS_INCLUDED == TRUE */ + /* check vendor specific capability */ + p_cb->privacy_mode = btm_cb.ble_ctr_cb.mixed_mode ? BTM_PRIVACY_MIXED : BTM_PRIVACY_1_2; + } else { /* 4.1/4.0 controller */ + p_cb->privacy_mode = BTM_PRIVACY_1_1; + } + // Enable RPA function + btsnd_hcic_ble_set_addr_resolution_enable(TRUE); + } + +#if (defined(GAP_INCLUDED) && GAP_INCLUDED == TRUE && GATTS_INCLUDED == TRUE) + GAP_BleAttrDBUpdate (GATT_UUID_GAP_CENTRAL_ADDR_RESOL, (tGAP_BLE_ATTR_VALUE *)&addr_resolution); +#endif + + return TRUE; +#else + return FALSE; +#endif +} + +/******************************************************************************* +** +** Function BTMGetLocalResolvablePrivateAddr +** +** Description This function is called to get local RPA address +** +** Parameters bda: address pointer. +** +** +*******************************************************************************/ + +BOOLEAN BTM_GetLocalResolvablePrivateAddr(BD_ADDR bda) +{ + tBTM_LE_RANDOM_CB *p_cb = &btm_cb.ble_ctr_cb.addr_mgnt_cb; + BTM_TRACE_DEBUG ("get owm resolvable random address"); + + if (bda) { + /* if privacy disabled, return false */ + if ((p_cb->exist_addr_bit & BTM_BLE_GAP_ADDR_BIT_RESOLVABLE) == BTM_BLE_GAP_ADDR_BIT_RESOLVABLE) { + memcpy(bda, p_cb->resolvale_addr, BD_ADDR_LEN); + BTM_TRACE_DEBUG("own resolvable random address: 0x%02x:%02x:%02x:%02x:%02x:%02x", + p_cb->resolvale_addr[0], p_cb->resolvale_addr[1], + p_cb->resolvale_addr[2], p_cb->resolvale_addr[3], + p_cb->resolvale_addr[4], p_cb->resolvale_addr[5]); + return TRUE; + } + + return FALSE; + } + + return FALSE; +} + +/******************************************************************************* +** +** Function BTM_UpdateAddrInfor +** +** Description This function is called to update address information +** +** Parameters addr_type: address type +** bda: address pointer. +** +** +*******************************************************************************/ +void BTM_UpdateAddrInfor(uint8_t addr_type, BD_ADDR bda) +{ + btm_cb.ble_ctr_cb.addr_mgnt_cb.own_addr_type = addr_type; + memcpy(btm_cb.ble_ctr_cb.addr_mgnt_cb.private_addr, bda, BD_ADDR_LEN); +} + +/******************************************************************************* +** +** Function BTM_BleSetStaticAddr +** +** Description This function is called to save random address +** +** Parameters rand_addr: address pointer. +** +** +*******************************************************************************/ +void BTM_BleSetStaticAddr(BD_ADDR rand_addr) +{ + memcpy(btm_cb.ble_ctr_cb.addr_mgnt_cb.static_rand_addr, rand_addr, BD_ADDR_LEN); + btm_cb.ble_ctr_cb.addr_mgnt_cb.exist_addr_bit |= BTM_BLE_GAP_ADDR_BIT_RANDOM; +} + +#if (CONTROLLER_RPA_LIST_ENABLE == FALSE) +uint32_t BTM_BleUpdateOwnType(uint8_t *own_bda_type, tBTM_START_ADV_CMPL_CBACK *cb) +{ + if(*own_bda_type == BLE_ADDR_RANDOM) { + if((btm_cb.ble_ctr_cb.addr_mgnt_cb.exist_addr_bit & BTM_BLE_GAP_ADDR_BIT_RANDOM) == BTM_BLE_GAP_ADDR_BIT_RANDOM) { + //close privacy + #if BLE_PRIVACY_SPT == TRUE + if (btm_cb.ble_ctr_cb.privacy_mode != BTM_PRIVACY_NONE) { + BTM_BleConfigPrivacy(FALSE, NULL); + } + #endif + btm_cb.ble_ctr_cb.addr_mgnt_cb.own_addr_type = BLE_ADDR_RANDOM; + memcpy(btm_cb.ble_ctr_cb.addr_mgnt_cb.private_addr, btm_cb.ble_ctr_cb.addr_mgnt_cb.static_rand_addr, BD_ADDR_LEN); + // set address to controller + btsnd_hcic_ble_set_random_addr(btm_cb.ble_ctr_cb.addr_mgnt_cb.static_rand_addr); + + } else if((btm_cb.ble_ctr_cb.addr_mgnt_cb.exist_addr_bit & BTM_BLE_GAP_ADDR_BIT_RESOLVABLE) == BTM_BLE_GAP_ADDR_BIT_RESOLVABLE) { + btm_cb.ble_ctr_cb.addr_mgnt_cb.own_addr_type = BLE_ADDR_RANDOM; + memcpy(btm_cb.ble_ctr_cb.addr_mgnt_cb.private_addr, btm_cb.ble_ctr_cb.addr_mgnt_cb.resolvale_addr, BD_ADDR_LEN); + btsnd_hcic_ble_set_random_addr(btm_cb.ble_ctr_cb.addr_mgnt_cb.resolvale_addr); + }else { + BTM_TRACE_ERROR ("No random address yet, please set random address using API \"esp_ble_gap_set_rand_addr\" and retry\n"); + if(cb) { + (* cb)(HCI_ERR_ESP_VENDOR_FAIL); + } + return BTM_ILLEGAL_VALUE; + } + } else if(*own_bda_type == BLE_ADDR_PUBLIC_ID || *own_bda_type == BLE_ADDR_RANDOM_ID) { + if((btm_cb.ble_ctr_cb.addr_mgnt_cb.exist_addr_bit & BTM_BLE_GAP_ADDR_BIT_RESOLVABLE) == BTM_BLE_GAP_ADDR_BIT_RESOLVABLE) { + *own_bda_type = BLE_ADDR_RANDOM; + btm_cb.ble_ctr_cb.addr_mgnt_cb.own_addr_type = BLE_ADDR_RANDOM; + memcpy(btm_cb.ble_ctr_cb.addr_mgnt_cb.private_addr, btm_cb.ble_ctr_cb.addr_mgnt_cb.resolvale_addr, BD_ADDR_LEN); + btsnd_hcic_ble_set_random_addr(btm_cb.ble_ctr_cb.addr_mgnt_cb.resolvale_addr); + } else { + #if BLE_PRIVACY_SPT == TRUE + if(btm_cb.ble_ctr_cb.privacy_mode != BTM_PRIVACY_NONE) { + BTM_TRACE_ERROR ("Error state\n"); + if(cb) { + (* cb)(HCI_ERR_ESP_VENDOR_FAIL); + } + return BTM_ILLEGAL_VALUE; + } + #endif + if(*own_bda_type == BLE_ADDR_PUBLIC_ID) { + *own_bda_type = BLE_ADDR_PUBLIC; + btm_cb.ble_ctr_cb.addr_mgnt_cb.own_addr_type = BLE_ADDR_PUBLIC; + } else { //own_bda_type == BLE_ADDR_RANDOM_ID + if((btm_cb.ble_ctr_cb.addr_mgnt_cb.exist_addr_bit & BTM_BLE_GAP_ADDR_BIT_RANDOM) == BTM_BLE_GAP_ADDR_BIT_RANDOM) { + *own_bda_type = BLE_ADDR_RANDOM; + btm_cb.ble_ctr_cb.addr_mgnt_cb.own_addr_type = BLE_ADDR_RANDOM; + memcpy(btm_cb.ble_ctr_cb.addr_mgnt_cb.private_addr, btm_cb.ble_ctr_cb.addr_mgnt_cb.static_rand_addr, BD_ADDR_LEN); + btsnd_hcic_ble_set_random_addr(btm_cb.ble_ctr_cb.addr_mgnt_cb.static_rand_addr); + } else { + BTM_TRACE_ERROR ("No RPA and no random address yet, please set RPA or random address and try\n"); + if(cb) { + (* cb)(HCI_ERR_ESP_VENDOR_FAIL); + } + return BTM_ILLEGAL_VALUE; + } + } + } + } else { + btm_cb.ble_ctr_cb.addr_mgnt_cb.own_addr_type = BLE_ADDR_PUBLIC; + } + + return BTM_SUCCESS; +} +#else +uint32_t BTM_BleUpdateOwnType(uint8_t *own_bda_type, tBTM_START_ADV_CMPL_CBACK *cb) +{ + tBTM_LE_RANDOM_CB *p_cb = &btm_cb.ble_ctr_cb.addr_mgnt_cb; + + if((*own_bda_type == BLE_ADDR_RANDOM) || (*own_bda_type == BLE_ADDR_RANDOM_ID)) { + if((p_cb->exist_addr_bit & BTM_BLE_GAP_ADDR_BIT_RANDOM) != BTM_BLE_GAP_ADDR_BIT_RANDOM) { + BTM_TRACE_ERROR("No random address yet, please set random address and try\n"); + if(cb) { + (* cb)(HCI_ERR_ESP_VENDOR_FAIL); + } + return BTM_ILLEGAL_VALUE; + } + + // If a device is using RPA, it shall also have an Identity Address + if ((*own_bda_type == BLE_ADDR_RANDOM_ID) && BTM_BLE_IS_NON_RESLVE_BDA(p_cb->static_rand_addr)) { + BTM_TRACE_ERROR("No identity address yet, please set static random address and try\n"); + if (cb) { + (* cb)(HCI_ERR_ESP_VENDOR_FAIL); + } + return BTM_ILLEGAL_VALUE; + } + } + + p_cb->own_addr_type = *own_bda_type; + + return BTM_SUCCESS; +} +#endif + + +/******************************************************************************* +** +** Function BTM_BleConfigLocalIcon +** +** Description This function is called to set local icon +** +** Parameters icon: appearance value. +** +** +*******************************************************************************/ +void BTM_BleConfigLocalIcon(uint16_t icon) +{ +#if (defined(GAP_INCLUDED) && GAP_INCLUDED == TRUE && GATTS_INCLUDED == TRUE) + tGAP_BLE_ATTR_VALUE p_value; + p_value.icon = icon; + GAP_BleAttrDBUpdate(GATT_UUID_GAP_ICON, &p_value); +#else + BTM_TRACE_ERROR("%s\n", __func__); +#endif +} + +/******************************************************************************* +** +** Function BTM_BleConfigConnParams +** +** Description This function is called to set the connection parameters +** +** Parameters int_min: minimum connection interval +** int_max: maximum connection interval +** latency: slave latency +** timeout: supervision timeout +** +*******************************************************************************/ +void BTM_BleConfigConnParams(uint16_t int_min, uint16_t int_max, uint16_t latency, uint16_t timeout) +{ +#if (defined(GAP_INCLUDED) && GAP_INCLUDED == TRUE && GATTS_INCLUDED == TRUE) + tGAP_BLE_ATTR_VALUE p_value; + + p_value.conn_param.int_min = int_min; + p_value.conn_param.int_max = int_max; + p_value.conn_param.latency = latency; + p_value.conn_param.sp_tout = timeout; + GAP_BleAttrDBUpdate(GATT_UUID_GAP_PREF_CONN_PARAM, &p_value); +#else + BTM_TRACE_ERROR("%s\n", __func__); +#endif +} + +/******************************************************************************* +** +** Function BTM_BleMaxMultiAdvInstanceCount +** +** Description Returns max number of multi adv instances supported by controller +** +** Returns Max multi adv instance count +** +*******************************************************************************/ +extern UINT8 BTM_BleMaxMultiAdvInstanceCount(void) +{ + return btm_cb.cmn_ble_vsc_cb.adv_inst_max < BTM_BLE_MULTI_ADV_MAX ? + btm_cb.cmn_ble_vsc_cb.adv_inst_max : BTM_BLE_MULTI_ADV_MAX; +} + +#if BLE_PRIVACY_SPT == TRUE +/******************************************************************************* +** +** Function btm_ble_resolve_random_addr_on_adv +** +** Description resolve random address complete callback. +** +** Returns void +** +*******************************************************************************/ +static void btm_ble_resolve_random_addr_on_adv(void *p_rec, void *p) +{ + tBTM_SEC_DEV_REC *match_rec = (tBTM_SEC_DEV_REC *) p_rec; + UINT8 addr_type = BLE_ADDR_RANDOM; + BD_ADDR bda; + UINT8 *pp = (UINT8 *)p + 1; + UINT8 evt_type; + + BTM_TRACE_EVENT ("btm_ble_resolve_random_addr_on_adv "); + + STREAM_TO_UINT8 (evt_type, pp); + STREAM_TO_UINT8 (addr_type, pp); + STREAM_TO_BDADDR (bda, pp); + + if (match_rec) { + BTM_TRACE_DEBUG("Random match"); + match_rec->ble.active_addr_type = BTM_BLE_ADDR_RRA; + memcpy(match_rec->ble.cur_rand_addr, bda, BD_ADDR_LEN); + + if (btm_ble_init_pseudo_addr(match_rec, bda)) { + memcpy(bda, match_rec->bd_addr, BD_ADDR_LEN); + } else { + // Assign the original address to be the current report address + memcpy(bda, match_rec->ble.pseudo_addr, BD_ADDR_LEN); + } + } + + btm_ble_process_adv_pkt_cont(bda, addr_type, evt_type, pp); + + return; +} +#endif + +/******************************************************************************* +** +** Function BTM_BleLocalPrivacyEnabled +** +** Description Checks if local device supports private address +** +** Returns Return TRUE if local privacy is enabled else FALSE +** +*******************************************************************************/ +BOOLEAN BTM_BleLocalPrivacyEnabled(void) +{ +#if BLE_PRIVACY_SPT == TRUE + return (btm_cb.ble_ctr_cb.privacy_mode != BTM_PRIVACY_NONE); +#else + return false; +#endif +} + +/******************************************************************************* +** +** Function BTM_BleSetBgConnType +** +** Description This function is called to set BLE connectable mode for a +** peripheral device. +** +** Parameters bg_conn_type: it can be auto connection, or selective connection. +** p_select_cback: callback function when selective connection procedure +** is being used. +** +** Returns void +** +*******************************************************************************/ +BOOLEAN BTM_BleSetBgConnType(tBTM_BLE_CONN_TYPE bg_conn_type, + tBTM_BLE_SEL_CBACK *p_select_cback) +{ + BOOLEAN started = TRUE; + + BTM_TRACE_EVENT ("BTM_BleSetBgConnType "); + if (!controller_get_interface()->supports_ble()) { + return FALSE; + } + + if (btm_cb.ble_ctr_cb.bg_conn_type != bg_conn_type) { + switch (bg_conn_type) { + case BTM_BLE_CONN_AUTO: + btm_ble_start_auto_conn(TRUE); + break; + + case BTM_BLE_CONN_SELECTIVE: + if (btm_cb.ble_ctr_cb.bg_conn_type == BTM_BLE_CONN_AUTO) { + btm_ble_start_auto_conn(FALSE); + } + btm_ble_start_select_conn(TRUE, p_select_cback); + break; + + case BTM_BLE_CONN_NONE: + if (btm_cb.ble_ctr_cb.bg_conn_type == BTM_BLE_CONN_AUTO) { + btm_ble_start_auto_conn(FALSE); + } else if (btm_cb.ble_ctr_cb.bg_conn_type == BTM_BLE_CONN_SELECTIVE) { + btm_ble_start_select_conn(FALSE, NULL); + } + started = TRUE; + break; + + default: + BTM_TRACE_ERROR("invalid bg connection type : %d ", bg_conn_type); + started = FALSE; + break; + } + + if (started) { + btm_cb.ble_ctr_cb.bg_conn_type = bg_conn_type; + } + } + return started; +} + +/******************************************************************************* +** +** Function BTM_BleClearBgConnDev +** +** Description This function is called to clear the whitelist, +** end any pending whitelist connections, +* and reset the local bg device list. +** +** Parameters void +** +** Returns void +** +*******************************************************************************/ +void BTM_BleClearBgConnDev(void) +{ + btm_ble_start_auto_conn(FALSE); + btm_ble_clear_white_list(NULL); + gatt_reset_bgdev_list(); +} + +/******************************************************************************* +** +** Function BTM_BleUpdateBgConnDev +** +** Description This function is called to add or remove a device into/from +** background connection procedure. The background connection +* procedure is decided by the background connection type, it can be +* auto connection, or selective connection. +** +** Parameters add_remove: TRUE to add; FALSE to remove. +** remote_bda: device address to add/remove. +** +** Returns void +** +*******************************************************************************/ +BOOLEAN BTM_BleUpdateBgConnDev(BOOLEAN add_remove, BD_ADDR remote_bda) +{ + BTM_TRACE_EVENT("%s() add=%d", __func__, add_remove); + return btm_update_dev_to_white_list(add_remove, remote_bda, 0, NULL); +} + +/******************************************************************************* +** +** Function BTM_BleSetConnectableMode +** +** Description This function is called to set BLE connectable mode for a +** peripheral device. +** +** Parameters conn_mode: directed connectable mode, or non-directed.It can +** be BTM_BLE_CONNECT_EVT, BTM_BLE_CONNECT_DIR_EVT or +** BTM_BLE_CONNECT_LO_DUTY_DIR_EVT +** +** Returns BTM_ILLEGAL_VALUE if controller does not support BLE. +** BTM_SUCCESS is status set successfully; otherwise failure. +** +*******************************************************************************/ +tBTM_STATUS BTM_BleSetConnectableMode(tBTM_BLE_CONN_MODE connectable_mode) +{ + tBTM_BLE_INQ_CB *p_cb = &btm_cb.ble_ctr_cb.inq_var; + + BTM_TRACE_EVENT ("%s connectable_mode = %d ", __func__, connectable_mode); + if (!controller_get_interface()->supports_ble()) { + return BTM_ILLEGAL_VALUE; + } + + p_cb->directed_conn = connectable_mode; + return btm_ble_set_connectability( p_cb->connectable_mode); +} + +/******************************************************************************* +** +** Function btm_set_conn_mode_adv_init_addr +** +** Description set initator address type and local address type based on adv +** mode. +** +** +*******************************************************************************/ +static UINT8 btm_set_conn_mode_adv_init_addr(tBTM_BLE_INQ_CB *p_cb, + BD_ADDR_PTR p_peer_addr_ptr, + tBLE_ADDR_TYPE *p_peer_addr_type, + tBLE_ADDR_TYPE *p_own_addr_type) +{ + UINT8 evt_type; +#if BLE_PRIVACY_SPT == TRUE + UINT8 i = BTM_SEC_MAX_DEVICE_RECORDS; + tBTM_SEC_DEV_REC *p_dev_rec; + list_node_t *p_node = NULL; +#endif ///BLE_PRIVACY_SPT == TRUE + evt_type = (p_cb->connectable_mode == BTM_BLE_NON_CONNECTABLE) ? \ + ((p_cb->scan_rsp) ? BTM_BLE_DISCOVER_EVT : BTM_BLE_NON_CONNECT_EVT )\ + : BTM_BLE_CONNECT_EVT; + + if (evt_type == BTM_BLE_CONNECT_EVT) { + evt_type = p_cb->directed_conn; + + if ( p_cb->directed_conn == BTM_BLE_CONNECT_DIR_EVT || + p_cb->directed_conn == BTM_BLE_CONNECT_LO_DUTY_DIR_EVT) { + +#if BLE_PRIVACY_SPT == TRUE + /* for privacy 1.2, convert peer address as static, own address set as ID addr */ + if (btm_cb.ble_ctr_cb.privacy_mode == BTM_PRIVACY_1_2 || + btm_cb.ble_ctr_cb.privacy_mode == BTM_PRIVACY_MIXED) { + /* only do so for bonded device */ + if ((p_dev_rec = btm_find_or_alloc_dev (p_cb->direct_bda.bda)) != NULL && + p_dev_rec->ble.in_controller_list & BTM_RESOLVING_LIST_BIT) { + //btm_ble_enable_resolving_list(BTM_BLE_RL_ADV); + memcpy(p_peer_addr_ptr, p_dev_rec->ble.static_addr, BD_ADDR_LEN); + *p_peer_addr_type = p_dev_rec->ble.static_addr_type; + *p_own_addr_type = BLE_ADDR_RANDOM_ID; + return evt_type; + } + /* otherwise fall though as normal directed adv */ + else { + btm_ble_disable_resolving_list(BTM_BLE_RL_ADV, TRUE); + } + } +#endif + /* direct adv mode does not have privacy, if privacy is not enabled */ + *p_peer_addr_type = p_cb->direct_bda.type; + memcpy(p_peer_addr_ptr, p_cb->direct_bda.bda, BD_ADDR_LEN); + return evt_type; + } + } + + /* undirect adv mode or non-connectable mode*/ +#if BLE_PRIVACY_SPT == TRUE + /* when privacy 1.2 privacy only mode is used, or mixed mode */ + if ((btm_cb.ble_ctr_cb.privacy_mode == BTM_PRIVACY_1_2 && p_cb->afp != AP_SCAN_CONN_ALL) || + btm_cb.ble_ctr_cb.privacy_mode == BTM_PRIVACY_MIXED) { + /* if enhanced privacy is required, set Identity address and matching IRK peer */ + for (p_node = list_begin(btm_cb.p_sec_dev_rec_list); p_node; p_node = list_next(p_node)) { + p_dev_rec = list_node(p_node); + if ((p_dev_rec->sec_flags & BTM_SEC_IN_USE) != 0 && + (p_dev_rec->ble.in_controller_list & BTM_RESOLVING_LIST_BIT) != 0) { + memcpy(p_peer_addr_ptr, p_dev_rec->ble.static_addr, BD_ADDR_LEN); + *p_peer_addr_type = p_dev_rec->ble.static_addr_type; + break; + } + } + + if (i != BTM_SEC_MAX_DEVICE_RECORDS) { + *p_own_addr_type = BLE_ADDR_RANDOM_ID; + } else + /* resolving list is empty, not enabled */ + { + *p_own_addr_type = BLE_ADDR_RANDOM; + } + } + /* privacy 1.1, or privacy 1.2, general discoverable/connectable mode, disable privacy in */ + /* controller fall back to host based privacy */ + else if (btm_cb.ble_ctr_cb.privacy_mode != BTM_PRIVACY_NONE) { + *p_own_addr_type = BLE_ADDR_RANDOM; + } +#endif + + /* if no privacy,do not set any peer address,*/ + /* local address type go by global privacy setting */ + return evt_type; +} + +/******************************************************************************* +** +** Function BTM_BleSetAdvParams +** +** Description This function is called to set advertising parameters. +** +** Parameters adv_int_min: minimum advertising interval +** adv_int_max: maximum advertising interval +** p_dir_bda: connectable direct initiator's LE device address +** chnl_map: advertising channel map. +** +** Returns void +** +*******************************************************************************/ +tBTM_STATUS BTM_BleSetAdvParams(UINT16 adv_int_min, UINT16 adv_int_max, + tBLE_BD_ADDR *p_dir_bda, + tBTM_BLE_ADV_CHNL_MAP chnl_map) +{ + tBTM_LE_RANDOM_CB *p_addr_cb = &btm_cb.ble_ctr_cb.addr_mgnt_cb; + tBTM_BLE_INQ_CB *p_cb = &btm_cb.ble_ctr_cb.inq_var; + tBTM_STATUS status = BTM_SUCCESS; + BD_ADDR p_addr_ptr = {0}; + tBLE_ADDR_TYPE init_addr_type = BLE_ADDR_PUBLIC; + tBLE_ADDR_TYPE own_addr_type = p_addr_cb->own_addr_type; + UINT8 adv_mode = p_cb->adv_mode; + + BTM_TRACE_EVENT ("BTM_BleSetAdvParams"); + + if (!controller_get_interface()->supports_ble()) { + return BTM_ILLEGAL_VALUE; + } + + if (!BTM_BLE_ISVALID_PARAM(adv_int_min, BTM_BLE_ADV_INT_MIN, BTM_BLE_ADV_INT_MAX) || + !BTM_BLE_ISVALID_PARAM(adv_int_max, BTM_BLE_ADV_INT_MIN, BTM_BLE_ADV_INT_MAX)) { + return BTM_ILLEGAL_VALUE; + } + + p_cb->adv_interval_min = adv_int_min; + p_cb->adv_interval_max = adv_int_max; + p_cb->adv_chnl_map = chnl_map; + + if (p_dir_bda) { + memcpy(&p_cb->direct_bda, p_dir_bda, sizeof(tBLE_BD_ADDR)); + } + + BTM_TRACE_EVENT ("update params for an active adv\n"); + + btm_ble_stop_adv(); + + p_cb->evt_type = btm_set_conn_mode_adv_init_addr(p_cb, p_addr_ptr, &init_addr_type, + &own_addr_type); + + /* update adv params */ + btsnd_hcic_ble_write_adv_params (p_cb->adv_interval_min, + p_cb->adv_interval_max, + p_cb->evt_type, + own_addr_type, + init_addr_type, + p_addr_ptr, + p_cb->adv_chnl_map, + p_cb->afp); + + if (adv_mode == BTM_BLE_ADV_ENABLE) { + btm_ble_start_adv(); + } + + return status; +} + + +/******************************************************************************* +** +** Function BTM_BleSetAdvParamsAll +** +** Description This function is called to set all of the advertising parameters. +** +** Parameters: None. +** +** Returns void +** +*******************************************************************************/ +tBTM_STATUS BTM_BleSetAdvParamsAll(UINT16 adv_int_min, UINT16 adv_int_max, UINT8 adv_type, + tBLE_ADDR_TYPE own_bda_type, tBLE_BD_ADDR *p_dir_bda, + tBTM_BLE_ADV_CHNL_MAP chnl_map, tBTM_BLE_AFP afp, tBTM_START_ADV_CMPL_CBACK *adv_cb) +{ + tBTM_LE_RANDOM_CB *p_addr_cb = &btm_cb.ble_ctr_cb.addr_mgnt_cb; + tBTM_BLE_INQ_CB *p_cb = &btm_cb.ble_ctr_cb.inq_var; + + BTM_TRACE_EVENT ("BTM_BleSetAdvParamsAll\n"); + + if (!controller_get_interface()->supports_ble()) { + return BTM_ILLEGAL_VALUE; + } + if (BTM_BleUpdateOwnType(&own_bda_type, adv_cb) != 0) { + return BTM_ILLEGAL_VALUE; + } + if (!BTM_BLE_ISVALID_PARAM(adv_int_min, BTM_BLE_ADV_INT_MIN, BTM_BLE_ADV_INT_MAX) || + !BTM_BLE_ISVALID_PARAM(adv_int_max, BTM_BLE_ADV_INT_MIN, BTM_BLE_ADV_INT_MAX)) { + BTM_TRACE_ERROR ("adv_int_min or adv_int_max is invalid\n"); + if(adv_cb) { + (* adv_cb)(HCI_ERR_ESP_VENDOR_FAIL); + } + return BTM_ILLEGAL_VALUE; + } + + btm_ble_stop_adv(); + + osi_mutex_lock(&adv_param_lock, OSI_MUTEX_MAX_TIMEOUT); + if(adv_type == BTM_BLE_CONNECT_DIR_EVT){ + btm_ble_set_topology_mask(BTM_BLE_STATE_HI_DUTY_DIR_ADV_BIT); + }else if(adv_type == BTM_BLE_CONNECT_LO_DUTY_DIR_EVT){ + btm_ble_set_topology_mask(BTM_BLE_STATE_LO_DUTY_DIR_ADV_BIT); + }else if(adv_type == BTM_BLE_NON_CONNECT_EVT){ + btm_ble_set_topology_mask(BTM_BLE_STATE_NON_CONN_ADV_BIT); + } + + p_cb->adv_interval_min = adv_int_min; + p_cb->adv_interval_max = adv_int_max; + p_cb->adv_chnl_map = chnl_map; + p_addr_cb->own_addr_type = own_bda_type; + p_cb->evt_type = adv_type; + p_cb->afp = afp; + p_cb->p_adv_cb = adv_cb; + + if (p_dir_bda) { + memcpy(&p_cb->direct_bda, p_dir_bda, sizeof(tBLE_BD_ADDR)); + } else { + return BTM_ILLEGAL_VALUE; + } + + BTM_TRACE_EVENT ("update params for an active adv\n"); + + tBTM_STATUS status = BTM_SUCCESS; + /* update adv params */ + if (btsnd_hcic_ble_write_adv_params (adv_int_min, + adv_int_max, + adv_type, + own_bda_type, + p_dir_bda->type, + p_dir_bda->bda, + chnl_map, + p_cb->afp)) { + osi_sem_take(&adv_param_sem, OSI_SEM_MAX_TIMEOUT); + status = adv_param_status; + } else { + status = BTM_NO_RESOURCES; + } + osi_mutex_unlock(&adv_param_lock); + return status; +} + +tBTM_STATUS BTM_BleStartAdv(void) +{ + tBTM_STATUS status = BTM_SUCCESS; + if (!controller_get_interface()->supports_ble()) { + return BTM_ILLEGAL_VALUE; + } + + btm_ble_stop_adv(); + + status = btm_ble_start_adv(); + + return status; +} +/******************************************************************************* +** +** Function BTM_BleReadAdvParams +** +** Description This function is called to set advertising parameters. +** +** Parameters adv_int_min: minimum advertising interval +** adv_int_max: maximum advertising interval +** p_dir_bda: connectable direct initiator's LE device address +** chnl_map: advertising channel map. +** +** Returns void +** +*******************************************************************************/ +void BTM_BleReadAdvParams (UINT16 *adv_int_min, UINT16 *adv_int_max, + tBLE_BD_ADDR *p_dir_bda, tBTM_BLE_ADV_CHNL_MAP *p_chnl_map) +{ + tBTM_BLE_INQ_CB *p_cb = &btm_cb.ble_ctr_cb.inq_var; + + BTM_TRACE_EVENT ("BTM_BleReadAdvParams "); + if (!controller_get_interface()->supports_ble()) { + return ; + } + + *adv_int_min = p_cb->adv_interval_min; + *adv_int_max = p_cb->adv_interval_max; + *p_chnl_map = p_cb->adv_chnl_map; + + if (p_dir_bda != NULL) { + memcpy(p_dir_bda, &p_cb->direct_bda, sizeof(tBLE_BD_ADDR)); + } +} + +/******************************************************************************* +** +** Function BTM_BleSetScanParams +** +** Description This function is called to set scan parameters. +** +** Parameters client_if - Client IF +** scan_interval - Scan interval +** scan_window - Scan window +** scan_mode - Scan mode +** scan_setup_status_cback - Scan param setup status callback +** +** Returns void +** +*******************************************************************************/ +void BTM_BleSetScanParams(tGATT_IF client_if, UINT32 scan_interval, UINT32 scan_window, + tBLE_SCAN_MODE scan_mode, + tBLE_SCAN_PARAM_SETUP_CBACK scan_setup_status_cback) +{ + tBTM_BLE_INQ_CB *p_cb = &btm_cb.ble_ctr_cb.inq_var; + UINT32 max_scan_interval; + UINT32 max_scan_window; + + BTM_TRACE_EVENT ("%s\n", __func__); + if (!controller_get_interface()->supports_ble()) { + return; + } + + /* If not supporting extended scan support, use the older range for checking */ + if (btm_cb.cmn_ble_vsc_cb.extended_scan_support == 0) { + max_scan_interval = BTM_BLE_SCAN_INT_MAX; + max_scan_window = BTM_BLE_SCAN_WIN_MAX; + } else { + /* If supporting extended scan support, use the new extended range for checking */ + max_scan_interval = BTM_BLE_EXT_SCAN_INT_MAX; + max_scan_window = BTM_BLE_EXT_SCAN_WIN_MAX; + } + + if (BTM_BLE_ISVALID_PARAM(scan_interval, BTM_BLE_SCAN_INT_MIN, max_scan_interval) && + BTM_BLE_ISVALID_PARAM(scan_window, BTM_BLE_SCAN_WIN_MIN, max_scan_window) && + (scan_mode == BTM_BLE_SCAN_MODE_ACTI || scan_mode == BTM_BLE_SCAN_MODE_PASS)) { + p_cb->scan_type = scan_mode; + p_cb->scan_interval = scan_interval; + p_cb->scan_window = scan_window; + + if (scan_setup_status_cback != NULL) { + scan_setup_status_cback(client_if, BTM_SUCCESS); + } + } else { + if (scan_setup_status_cback != NULL) { + scan_setup_status_cback(client_if, BTM_ILLEGAL_VALUE); + } + + BTM_TRACE_ERROR("Illegal params: scan_interval = %d scan_window = %d\n", + scan_interval, scan_window); + } + +} + +tBTM_STATUS BTM_BleSetScanFilterParams(tGATT_IF client_if, UINT32 scan_interval, UINT32 scan_window, + tBLE_SCAN_MODE scan_mode, UINT8 addr_type_own, UINT8 scan_duplicate_filter, tBTM_BLE_SFP scan_filter_policy, + tBLE_SCAN_PARAM_SETUP_CBACK scan_setup_status_cback) +{ + tBTM_BLE_INQ_CB *p_cb = &btm_cb.ble_ctr_cb.inq_var; + UINT32 max_scan_interval; + UINT32 max_scan_window; + tBTM_STATUS ret = BTM_SUCCESS; + + BTM_TRACE_EVENT ("%s\n", __func__); + if (!controller_get_interface()->supports_ble()) { + return BTM_ILLEGAL_VALUE; + } + if (BTM_BleUpdateOwnType(&addr_type_own, NULL) != 0) { + return BTM_ILLEGAL_VALUE; + } + /* If not supporting extended scan support, use the older range for checking */ + if (btm_cb.cmn_ble_vsc_cb.extended_scan_support == 0) { + max_scan_interval = BTM_BLE_SCAN_INT_MAX; + max_scan_window = BTM_BLE_SCAN_WIN_MAX; + } else { + /* If supporting extended scan support, use the new extended range for checking */ + max_scan_interval = BTM_BLE_EXT_SCAN_INT_MAX; + max_scan_window = BTM_BLE_EXT_SCAN_WIN_MAX; + } + + osi_mutex_lock(&scan_param_lock, OSI_MUTEX_MAX_TIMEOUT); + + if (BTM_BLE_ISVALID_PARAM(scan_interval, BTM_BLE_SCAN_INT_MIN, max_scan_interval) && + BTM_BLE_ISVALID_PARAM(scan_window, BTM_BLE_SCAN_WIN_MIN, max_scan_window) && + (scan_mode == BTM_BLE_SCAN_MODE_ACTI || scan_mode == BTM_BLE_SCAN_MODE_PASS) && + (scan_duplicate_filter < BTM_BLE_SCAN_DUPLICATE_MAX) && (scan_window <= scan_interval)) { + p_cb->scan_type = scan_mode; + p_cb->scan_interval = scan_interval; + p_cb->scan_window = scan_window; + p_cb->sfp = scan_filter_policy; + p_cb->scan_params_set = TRUE; + p_cb->scan_duplicate_filter = scan_duplicate_filter; + + + if (btsnd_hcic_ble_set_scan_params(p_cb->scan_type, (UINT16)scan_interval, + (UINT16)scan_window, + addr_type_own, + scan_filter_policy)) { + osi_sem_take(&scan_param_sem, OSI_SEM_MAX_TIMEOUT); + ret = scan_param_status; + } + } else { + ret = BTM_ILLEGAL_VALUE; + BTM_TRACE_ERROR("Illegal params: scan_interval = %d scan_window = %d\n", + scan_interval, scan_window); + } + osi_mutex_unlock(&scan_param_lock); + return ret; +} + + +/******************************************************************************* +** +** Function BTM_BleWriteScanRsp +** +** Description This function is called to write LE scan response. +** +** Parameters: p_scan_rsp: scan response information. +** +** Returns void +** +*******************************************************************************/ +tBTM_STATUS BTM_BleWriteScanRsp(tBTM_BLE_AD_MASK data_mask, tBTM_BLE_ADV_DATA *p_data) +{ + tBTM_STATUS ret; + UINT8 rsp_data[BTM_BLE_AD_DATA_LEN], + *p = rsp_data; + + BTM_TRACE_EVENT (" BTM_BleWriteScanRsp"); + + if (!controller_get_interface()->supports_ble()) { + return BTM_ILLEGAL_VALUE; + } + + osi_mutex_lock(&adv_data_lock, OSI_MUTEX_MAX_TIMEOUT); + memset(rsp_data, 0, BTM_BLE_AD_DATA_LEN); + btm_ble_build_adv_data(&data_mask, &p, p_data); + if (data_mask != 0) { + //data length should not exceed 31 bytes + BTM_TRACE_WARNING("%s, Partial data write into ADV", __func__); + } + + if (btsnd_hcic_ble_set_scan_rsp_data((UINT8)(p - rsp_data), rsp_data)) { + osi_sem_take(&adv_data_sem, OSI_SEM_MAX_TIMEOUT); + ret = adv_data_status; + + if (adv_data_status == BTM_SUCCESS && data_mask != 0) { + btm_cb.ble_ctr_cb.inq_var.scan_rsp = TRUE; + } else { + btm_cb.ble_ctr_cb.inq_var.scan_rsp = FALSE; + } + } else { + ret = BTM_ILLEGAL_VALUE; + } + + osi_mutex_unlock(&adv_data_lock); + return ret; +} + +/******************************************************************************* +** +** Function BTM_BleWriteScanRspRaw +** +** Description This function is called to write raw scan response data +** +** Parameters: None. +** +** Returns void +** +*******************************************************************************/ +tBTM_STATUS BTM_BleWriteScanRspRaw(UINT8 *p_raw_scan_rsp, UINT32 raw_scan_rsp_len) +{ + tBTM_STATUS ret; + + osi_mutex_lock(&adv_data_lock, OSI_MUTEX_MAX_TIMEOUT); + if (btsnd_hcic_ble_set_scan_rsp_data((UINT8)raw_scan_rsp_len, p_raw_scan_rsp)) { + osi_sem_take(&adv_data_sem, OSI_SEM_MAX_TIMEOUT); + ret = adv_data_status; + } else { + ret = BTM_NO_RESOURCES; + } + osi_mutex_unlock(&adv_data_lock); + + return ret; +} + +/******************************************************************************* +** +** Function BTM_UpdateBleDuplicateExceptionalList +** +** Description This function is called to update duplicate scan exceptional list. +** +** Parameters: subcode: add, remove or clean duplicate scan exceptional list. +** type: device info type +** device_info: device information +** update_exceptional_list_cmp_cb: complete callback +** +** Returns status +** +*******************************************************************************/ +tBTM_STATUS BTM_UpdateBleDuplicateExceptionalList(uint8_t subcode, uint32_t type, BD_ADDR device_info, + tBTM_UPDATE_DUPLICATE_EXCEPTIONAL_LIST_CMPL_CBACK update_exceptional_list_cmp_cb) +{ + tBTM_BLE_CB *ble_cb = &btm_cb.ble_ctr_cb; + tBTM_STATUS status = BTM_NO_RESOURCES; + + ble_cb->update_exceptional_list_cmp_cb = update_exceptional_list_cmp_cb; + + if (!controller_get_interface()->supports_ble()) { + return BTM_ILLEGAL_VALUE; + } + if(!device_info) { + return BTM_ILLEGAL_VALUE; + } + // subcoe + type + device info + uint8_t device_info_array[1 + 4 + BD_ADDR_LEN] = {0}; + device_info_array[0] = subcode; + device_info_array[1] = type & 0xff; + device_info_array[2] = (type >> 8) & 0xff; + device_info_array[3] = (type >> 16) & 0xff; + device_info_array[4] = (type >> 24) & 0xff; + switch (type) + { + case BTM_DUPLICATE_SCAN_EXCEPTIONAL_INFO_ADV_ADDR: + bt_rcopy(&device_info_array[5], device_info, BD_ADDR_LEN); + break; + case BTM_DUPLICATE_SCAN_EXCEPTIONAL_INFO_MESH_LINK_ID: + memcpy(&device_info_array[5], device_info, 4); + break; + case BTM_DUPLICATE_SCAN_EXCEPTIONAL_INFO_MESH_BEACON_TYPE: + //do nothing + break; + case BTM_DUPLICATE_SCAN_EXCEPTIONAL_INFO_MESH_PROV_SRV_ADV: + //do nothing + break; + case BTM_DUPLICATE_SCAN_EXCEPTIONAL_INFO_MESH_PROXY_SRV_ADV: + //do nothing + break; + default: + //do nothing + break; + } + + status = BTM_VendorSpecificCommand(HCI_VENDOR_BLE_UPDATE_DUPLICATE_EXCEPTIONAL_LIST, 1 + 4 + BD_ADDR_LEN, device_info_array, NULL); + if(status == BTM_CMD_STARTED) { + status = BTM_SUCCESS; + } + + return status; +} + +/******************************************************************************* +** +** Function BTM_BleWriteAdvData +** +** Description This function is called to write advertising data. +** +** Parameters: None. +** +** Returns void +** +*******************************************************************************/ +tBTM_STATUS BTM_BleWriteAdvData(tBTM_BLE_AD_MASK data_mask, tBTM_BLE_ADV_DATA *p_data) +{ + tBTM_BLE_LOCAL_ADV_DATA *p_cb_data = &btm_cb.ble_ctr_cb.inq_var.adv_data; + UINT8 *p; + tBTM_BLE_AD_MASK mask = data_mask; + tBTM_STATUS ret; + + BTM_TRACE_EVENT ("BTM_BleWriteAdvData "); + + if (!controller_get_interface()->supports_ble()) { + return BTM_ILLEGAL_VALUE; + } + osi_mutex_lock(&adv_data_lock, OSI_MUTEX_MAX_TIMEOUT); + memset(p_cb_data, 0, sizeof(tBTM_BLE_LOCAL_ADV_DATA)); + p = p_cb_data->ad_data; + p_cb_data->data_mask = data_mask; + + p_cb_data->p_flags = btm_ble_build_adv_data(&mask, &p, p_data); + + p_cb_data->p_pad = p; + + if (mask != 0) { + //data length should not exceed 31 bytes + BTM_TRACE_WARNING("%s, Partial data write into ADV", __func__); + } + + p_cb_data->data_mask &= ~mask; + + if (btsnd_hcic_ble_set_adv_data((UINT8)(p_cb_data->p_pad - p_cb_data->ad_data), + p_cb_data->ad_data)) { + osi_sem_take(&adv_data_sem, OSI_SEM_MAX_TIMEOUT); + ret = adv_data_status; + } else { + ret = BTM_NO_RESOURCES; + } + osi_mutex_unlock(&adv_data_lock); + return ret; +} + +/******************************************************************************* +** +** Function BTM_BleWriteLongAdvData +** +** Description This function is called to write long advertising data. +** +** Parameters: adv_data: long advertising data +** adv_data_len: the length of long advertising data +** +** Returns void +** +*******************************************************************************/ +tBTM_STATUS BTM_BleWriteLongAdvData(uint8_t *adv_data, uint8_t adv_data_len) +{ + tBTM_STATUS status = BTM_NO_RESOURCES; + if (!controller_get_interface()->supports_ble()) { + return BTM_ILLEGAL_VALUE; + } + if(!adv_data || adv_data_len <= 0 || adv_data_len > BTM_BLE_LONG_ADV_MAX_LEN) { + return BTM_ILLEGAL_VALUE; + } + uint8_t long_adv[BTM_BLE_LONG_ADV_MAX_LEN + 1] = {0}; + long_adv[0] = adv_data_len; + memcpy(&long_adv[1], adv_data, adv_data_len); + status = BTM_VendorSpecificCommand(HCI_VENDOR_BLE_LONG_ADV_DATA, BTM_BLE_LONG_ADV_MAX_LEN + 1, long_adv, NULL); + if(status == BTM_CMD_STARTED) { + status = BTM_SUCCESS; + } + + return status; +} + +/******************************************************************************* +** +** Function BTM_BleWriteAdvDataRaw +** +** Description This function is called to write raw advertising data. +** +** Parameters: None. +** +** Returns void +** +*******************************************************************************/ +tBTM_STATUS BTM_BleWriteAdvDataRaw(UINT8 *p_raw_adv, UINT32 raw_adv_len) +{ + tBTM_STATUS ret; + osi_mutex_lock(&adv_data_lock, OSI_MUTEX_MAX_TIMEOUT); + if (btsnd_hcic_ble_set_adv_data((UINT8)raw_adv_len, p_raw_adv)) { + osi_sem_take(&adv_data_sem, OSI_SEM_MAX_TIMEOUT); + ret = adv_data_status; + } else { + ret = BTM_NO_RESOURCES; + } + osi_mutex_unlock(&adv_data_lock); + + return ret; +} + + +/******************************************************************************* +** +** Function BTM_BleSetRandAddress +** +** Description This function is called to set the LE random address. +** +** Parameters: None. +** +** Returns void +** +*******************************************************************************/ +tBTM_STATUS BTM_BleSetRandAddress(BD_ADDR rand_addr) +{ + if (rand_addr == NULL) { + return BTM_SET_STATIC_RAND_ADDR_FAIL; + } + + if (btm_cb.ble_ctr_cb.inq_var.state != BTM_BLE_IDLE) { + BTM_TRACE_ERROR("Advertising or scaning now, can't set randaddress %d", btm_cb.ble_ctr_cb.inq_var.state); + return BTM_SET_STATIC_RAND_ADDR_FAIL; + } + memcpy(btm_cb.ble_ctr_cb.addr_mgnt_cb.private_addr, rand_addr, BD_ADDR_LEN); + memcpy(btm_cb.ble_ctr_cb.addr_mgnt_cb.static_rand_addr, rand_addr, BD_ADDR_LEN); + //send the set random address to the controller + if(btsnd_hcic_ble_set_random_addr(rand_addr)) { + btm_cb.ble_ctr_cb.addr_mgnt_cb.exist_addr_bit |= BTM_BLE_GAP_ADDR_BIT_RANDOM; + return BTM_SUCCESS; + } else { + return BTM_SET_STATIC_RAND_ADDR_FAIL; + } +} + +/******************************************************************************* +** +** Function BTM_BleClearRandAddress +** +** Description This function is called to clear the LE random address. +** +** Parameters: None. +** +** Returns void +** +*******************************************************************************/ +void BTM_BleClearRandAddress(void) +{ + tBTM_BLE_CB *p_cb = &btm_cb.ble_ctr_cb; + if (btm_cb.ble_ctr_cb.addr_mgnt_cb.own_addr_type == BLE_ADDR_RANDOM && (p_cb->inq_var.state != BTM_BLE_IDLE)) { + BTM_TRACE_ERROR("Advertising or scaning now, can't restore public address "); + return; + } + memset(btm_cb.ble_ctr_cb.addr_mgnt_cb.static_rand_addr, 0, BD_ADDR_LEN); + btm_cb.ble_ctr_cb.addr_mgnt_cb.exist_addr_bit &= (~BTM_BLE_GAP_ADDR_BIT_RANDOM); + btm_cb.ble_ctr_cb.addr_mgnt_cb.own_addr_type = BLE_ADDR_PUBLIC; +} +/******************************************************************************* +** +** Function BTM_BleGetCurrentAddress +** +** Description This function is called to get local used BLE address. +** +** Parameters: None. +** +** Returns success or fail +** +*******************************************************************************/ +BOOLEAN BTM_BleGetCurrentAddress(BD_ADDR addr, uint8_t *addr_type) +{ + if(addr == NULL || addr_type == NULL) { + BTM_TRACE_ERROR("%s addr or addr_type is NULL\n", __func__); + return FALSE; + } + if(btm_cb.ble_ctr_cb.addr_mgnt_cb.own_addr_type == BLE_ADDR_RANDOM) { + *addr_type = BLE_ADDR_RANDOM; + memcpy(addr, btm_cb.ble_ctr_cb.addr_mgnt_cb.private_addr, BD_ADDR_LEN); + } else if(btm_cb.ble_ctr_cb.addr_mgnt_cb.own_addr_type == BLE_ADDR_PUBLIC) { + *addr_type = BLE_ADDR_PUBLIC; + memcpy(addr, &controller_get_interface()->get_address()->address, BD_ADDR_LEN); + } else { + BTM_TRACE_ERROR("%s\n", __func__); + memset(addr, 0, BD_ADDR_LEN); + return FALSE; + } + return TRUE; +} + +/******************************************************************************* +** +** Function BTM_CheckAdvData +** +** Description This function is called to get ADV data for a specific type. +** +** Parameters p_adv - pointer of ADV data +** type - finding ADV data type +** p_length - return the length of ADV data not including type +** +** Returns pointer of ADV data +** +*******************************************************************************/ +UINT8 *BTM_CheckAdvData( UINT8 *p_adv, UINT8 type, UINT8 *p_length) +{ + UINT8 *p = p_adv; + UINT8 length; + UINT8 adv_type; + BTM_TRACE_API("BTM_CheckAdvData type=0x%02X", type); + + STREAM_TO_UINT8(length, p); + + while ( length && (p - p_adv < BTM_BLE_CACHE_ADV_DATA_MAX)) { + STREAM_TO_UINT8(adv_type, p); + + if ( adv_type == type ) { + /* length doesn't include itself */ + *p_length = length - 1; /* minus the length of type */ + return p; + } + + p += length - 1; /* skip the length of data */ + + /* Break loop if advertising data is in an incorrect format, + as it may lead to memory overflow */ + if (p >= p_adv + BTM_BLE_CACHE_ADV_DATA_MAX) { + break; + } + + STREAM_TO_UINT8(length, p); + } + + *p_length = 0; + return NULL; +} + +/******************************************************************************* +** +** Function BTM__BLEReadDiscoverability +** +** Description This function is called to read the current LE discoverability +** mode of the device. +** +** Returns BTM_BLE_NON_DISCOVERABLE ,BTM_BLE_LIMITED_DISCOVERABLE or +** BTM_BLE_GENRAL_DISCOVERABLE +** +*******************************************************************************/ +UINT16 BTM_BleReadDiscoverability(void) +{ + BTM_TRACE_API("%s\n", __FUNCTION__); + + return (btm_cb.ble_ctr_cb.inq_var.discoverable_mode); +} + +/******************************************************************************* +** +** Function BTM__BLEReadConnectability +** +** Description This function is called to read the current LE connectibility +** mode of the device. +** +** Returns BTM_BLE_NON_CONNECTABLE or BTM_BLE_CONNECTABLE +** +*******************************************************************************/ +UINT16 BTM_BleReadConnectability(void) +{ + BTM_TRACE_API ("%s\n", __FUNCTION__); + + return (btm_cb.ble_ctr_cb.inq_var.connectable_mode); +} + +void BTM_Recovery_Pre_State(void) +{ + tBTM_BLE_INQ_CB *ble_inq_cb = &btm_cb.ble_ctr_cb.inq_var; + + if (ble_inq_cb->state & BTM_BLE_ADVERTISING) { + btm_ble_stop_adv(); + btm_ble_start_adv(); + } + if (ble_inq_cb->state & BTM_BLE_SCANNING) { + btm_ble_start_scan(); + } + + return; +} + +/******************************************************************************* +** +** Function BTM_GetCurrentConnParams +** +** Description This function is called to read the current connection parameters +** of the device +** +** Returns TRUE or FALSE +** +*******************************************************************************/ + +BOOLEAN BTM_GetCurrentConnParams(BD_ADDR bda, uint16_t *interval, uint16_t *latency, uint16_t *timeout) +{ + if( (interval == NULL) || (latency == NULL) || (timeout == NULL) ) { + BTM_TRACE_ERROR("%s error ", __func__); + return FALSE; + } + + if(btm_get_current_conn_params(bda, interval, latency, timeout)) { + return TRUE; + } + + return FALSE; +} + +/******************************************************************************* +** +** Function btm_ble_build_adv_data +** +** Description This function is called build the adv data and rsp data. +*******************************************************************************/ +UINT8 *btm_ble_build_adv_data(tBTM_BLE_AD_MASK *p_data_mask, UINT8 **p_dst, + tBTM_BLE_ADV_DATA *p_data) +{ + UINT32 data_mask = *p_data_mask; + UINT8 *p = *p_dst, + *p_flag = NULL; + UINT16 len = BTM_BLE_AD_DATA_LEN, cp_len = 0; + UINT8 i = 0; + tBTM_BLE_PROP_ELEM *p_elem; + + BTM_TRACE_EVENT (" btm_ble_build_adv_data"); + + /* build the adv data structure and build the data string */ + if (data_mask) { + /* flags */ + if (data_mask & BTM_BLE_AD_BIT_FLAGS) { + *p++ = MIN_ADV_LENGTH; + *p++ = BTM_BLE_AD_TYPE_FLAG; + p_flag = p; + if (p_data) { + *p++ = p_data->flag; + } else { + *p++ = 0; + } + + len -= 3; + + data_mask &= ~BTM_BLE_AD_BIT_FLAGS; + } + /* appearance data */ + if (len > 3 && data_mask & BTM_BLE_AD_BIT_APPEARANCE) { + *p++ = 3; /* length */ + *p++ = BTM_BLE_AD_TYPE_APPEARANCE; + UINT16_TO_STREAM(p, p_data->appearance); + len -= 4; + + data_mask &= ~BTM_BLE_AD_BIT_APPEARANCE; + } + /* device name */ +#if BTM_MAX_LOC_BD_NAME_LEN > 0 + if (len > MIN_ADV_LENGTH && data_mask & BTM_BLE_AD_BIT_DEV_NAME) { + if (strlen(btm_cb.cfg.bd_name) > (UINT16)(len - MIN_ADV_LENGTH)) { + cp_len = (UINT16)(len - MIN_ADV_LENGTH); + *p++ = cp_len + 1; + *p++ = BTM_BLE_AD_TYPE_NAME_SHORT; + ARRAY_TO_STREAM(p, btm_cb.cfg.bd_name, cp_len); + } else { + cp_len = (UINT16)strlen(btm_cb.cfg.bd_name); + *p++ = cp_len + 1; + *p++ = BTM_BLE_AD_TYPE_NAME_CMPL; + ARRAY_TO_STREAM(p, btm_cb.cfg.bd_name, cp_len); + } + len -= (cp_len + MIN_ADV_LENGTH); + data_mask &= ~BTM_BLE_AD_BIT_DEV_NAME; + } +#endif + /* manufacturer data */ + if (len > MIN_ADV_LENGTH && data_mask & BTM_BLE_AD_BIT_MANU && + p_data && p_data->p_manu && + p_data->p_manu->len != 0 && p_data->p_manu->p_val) { + if (p_data->p_manu->len > (len - MIN_ADV_LENGTH)) { + cp_len = len - MIN_ADV_LENGTH; + } else { + cp_len = p_data->p_manu->len; + } + BTM_TRACE_DEBUG("cp_len = %d\n,p_data->p_manu->len=%d\n", cp_len, p_data->p_manu->len); + for (int i = 0; i < p_data->p_manu->len; i++) { + BTM_TRACE_DEBUG("p_data->p_manu->p_val[%d] = %x\n", i, p_data->p_manu->p_val[i]); + } + *p++ = cp_len + 1; + *p++ = BTM_BLE_AD_TYPE_MANU; + ARRAY_TO_STREAM(p, p_data->p_manu->p_val, cp_len); + BTM_TRACE_DEBUG("p_addr = %p\n,p_data->p_manu->p_val = %p\n", p, p_data->p_manu->p_val); + len -= (cp_len + MIN_ADV_LENGTH); + data_mask &= ~BTM_BLE_AD_BIT_MANU; + } + /* TX power */ + if (len > MIN_ADV_LENGTH && data_mask & BTM_BLE_AD_BIT_TX_PWR) { + *p++ = MIN_ADV_LENGTH; + *p++ = BTM_BLE_AD_TYPE_TX_PWR; + if (p_data->tx_power > BTM_BLE_ADV_TX_POWER_MAX) { + p_data->tx_power = BTM_BLE_ADV_TX_POWER_MAX; + } + *p++ = btm_ble_map_adv_tx_power(p_data->tx_power); + len -= 3; + data_mask &= ~BTM_BLE_AD_BIT_TX_PWR; + } + /* 16 bits services */ + if (len > MIN_ADV_LENGTH && data_mask & BTM_BLE_AD_BIT_SERVICE && + p_data && p_data->p_services && + p_data->p_services->num_service != 0 && + p_data->p_services->p_uuid) { + if (p_data->p_services->num_service * LEN_UUID_16 > (len - MIN_ADV_LENGTH)) { + cp_len = (len - MIN_ADV_LENGTH) / LEN_UUID_16; + *p ++ = 1 + cp_len * LEN_UUID_16; + *p++ = BTM_BLE_AD_TYPE_16SRV_PART; + } else { + cp_len = p_data->p_services->num_service; + *p++ = 1 + cp_len * LEN_UUID_16; + *p++ = BTM_BLE_AD_TYPE_16SRV_CMPL; + } + for (i = 0; i < cp_len; i ++) { + UINT16_TO_STREAM(p, *(p_data->p_services->p_uuid + i)); + } + + len -= (cp_len * MIN_ADV_LENGTH + MIN_ADV_LENGTH); + data_mask &= ~BTM_BLE_AD_BIT_SERVICE; + } + /* 32 bits service uuid */ + if (len > MIN_ADV_LENGTH && data_mask & BTM_BLE_AD_BIT_SERVICE_32 && + p_data && p_data->p_service_32b && + p_data->p_service_32b->num_service != 0 && + p_data->p_service_32b->p_uuid) { + if ((p_data->p_service_32b->num_service * LEN_UUID_32) > (len - MIN_ADV_LENGTH)) { + cp_len = (len - MIN_ADV_LENGTH) / LEN_UUID_32; + *p ++ = 1 + cp_len * LEN_UUID_32; + *p++ = BTM_BLE_AD_TYPE_32SRV_PART; + } else { + cp_len = p_data->p_service_32b->num_service; + *p++ = 1 + cp_len * LEN_UUID_32; + *p++ = BTM_BLE_AD_TYPE_32SRV_CMPL; + } + for (i = 0; i < cp_len; i ++) { + UINT32_TO_STREAM(p, *(p_data->p_service_32b->p_uuid + i)); + } + + len -= (cp_len * LEN_UUID_32 + MIN_ADV_LENGTH); + data_mask &= ~BTM_BLE_AD_BIT_SERVICE_32; + } + /* 128 bits services */ + if (len >= (MAX_UUID_SIZE + 2) && data_mask & BTM_BLE_AD_BIT_SERVICE_128 && + p_data && p_data->p_services_128b) { + *p ++ = 1 + MAX_UUID_SIZE; + if (!p_data->p_services_128b->list_cmpl) { + *p++ = BTM_BLE_AD_TYPE_128SRV_PART; + } else { + *p++ = BTM_BLE_AD_TYPE_128SRV_CMPL; + } + + ARRAY_TO_STREAM(p, p_data->p_services_128b->uuid128, MAX_UUID_SIZE); + + len -= (MAX_UUID_SIZE + MIN_ADV_LENGTH); + data_mask &= ~BTM_BLE_AD_BIT_SERVICE_128; + } + /* 32 bits Service Solicitation UUIDs */ + if (len > MIN_ADV_LENGTH && data_mask & BTM_BLE_AD_BIT_SERVICE_32SOL && + p_data && p_data->p_sol_service_32b && + p_data->p_sol_service_32b->num_service != 0 && + p_data->p_sol_service_32b->p_uuid) { + if ((p_data->p_sol_service_32b->num_service * LEN_UUID_32) > (len - MIN_ADV_LENGTH)) { + cp_len = (len - MIN_ADV_LENGTH) / LEN_UUID_32; + *p ++ = 1 + cp_len * LEN_UUID_32; + } else { + cp_len = p_data->p_sol_service_32b->num_service; + *p++ = 1 + cp_len * LEN_UUID_32; + } + + *p++ = BTM_BLE_AD_TYPE_32SOL_SRV_UUID; + for (i = 0; i < cp_len; i ++) { + UINT32_TO_STREAM(p, *(p_data->p_sol_service_32b->p_uuid + i)); + } + + len -= (cp_len * LEN_UUID_32 + MIN_ADV_LENGTH); + data_mask &= ~BTM_BLE_AD_BIT_SERVICE_32SOL; + } + /* 128 bits Solicitation services UUID */ + if (len >= (MAX_UUID_SIZE + MIN_ADV_LENGTH) && data_mask & BTM_BLE_AD_BIT_SERVICE_128SOL && + p_data && p_data->p_sol_service_128b) { + *p ++ = 1 + MAX_UUID_SIZE; + *p++ = BTM_BLE_AD_TYPE_128SOL_SRV_UUID; + ARRAY_TO_STREAM(p, p_data->p_sol_service_128b->uuid128, MAX_UUID_SIZE); + len -= (MAX_UUID_SIZE + MIN_ADV_LENGTH); + data_mask &= ~BTM_BLE_AD_BIT_SERVICE_128SOL; + } + /* 16bits/32bits/128bits Service Data */ + if (len > MIN_ADV_LENGTH && data_mask & BTM_BLE_AD_BIT_SERVICE_DATA && + p_data && p_data->p_service_data->len != 0 && p_data->p_service_data->p_val) { + if (len > (p_data->p_service_data->service_uuid.len + MIN_ADV_LENGTH)) { + if (p_data->p_service_data->len > (len - MIN_ADV_LENGTH)) { + cp_len = len - MIN_ADV_LENGTH - p_data->p_service_data->service_uuid.len; + } else { + cp_len = p_data->p_service_data->len; + } + + *p++ = cp_len + 1 + p_data->p_service_data->service_uuid.len; + if (p_data->p_service_data->service_uuid.len == LEN_UUID_16) { + *p++ = BTM_BLE_AD_TYPE_SERVICE_DATA; + UINT16_TO_STREAM(p, p_data->p_service_data->service_uuid.uu.uuid16); + } else if (p_data->p_service_data->service_uuid.len == LEN_UUID_32) { + *p++ = BTM_BLE_AD_TYPE_32SERVICE_DATA; + UINT32_TO_STREAM(p, p_data->p_service_data->service_uuid.uu.uuid32); + } else { + *p++ = BTM_BLE_AD_TYPE_128SERVICE_DATA; + ARRAY_TO_STREAM(p, p_data->p_service_data->service_uuid.uu.uuid128, + LEN_UUID_128); + } + + ARRAY_TO_STREAM(p, p_data->p_service_data->p_val, cp_len); + + len -= (cp_len + MIN_ADV_LENGTH + p_data->p_service_data->service_uuid.len); + data_mask &= ~BTM_BLE_AD_BIT_SERVICE_DATA; + } else { + BTM_TRACE_WARNING("service data does not fit"); + } + } + + if (len >= 6 && data_mask & BTM_BLE_AD_BIT_INT_RANGE && + p_data) { + *p++ = 5; + *p++ = BTM_BLE_AD_TYPE_INT_RANGE; + UINT16_TO_STREAM(p, p_data->int_range.low); + UINT16_TO_STREAM(p, p_data->int_range.hi); + len -= 6; + data_mask &= ~BTM_BLE_AD_BIT_INT_RANGE; + } + if (data_mask & BTM_BLE_AD_BIT_PROPRIETARY && p_data && p_data->p_proprietary) { + for (i = 0; i < p_data->p_proprietary->num_elem ; i ++) { + p_elem = p_data->p_proprietary->p_elem + i; + + if (len >= (MIN_ADV_LENGTH + p_elem->len))/* len byte(1) + ATTR type(1) + Uuid len(2) + + value length */ + { + *p ++ = p_elem->len + 1; /* Uuid len + value length */ + *p ++ = p_elem->adv_type; + ARRAY_TO_STREAM(p, p_elem->p_val, p_elem->len); + + len -= (MIN_ADV_LENGTH + p_elem->len); + } else { + BTM_TRACE_WARNING("data exceed max adv packet length"); + break; + } + } + data_mask &= ~BTM_BLE_AD_BIT_PROPRIETARY; + } + } + + *p_data_mask = data_mask; + *p_dst = p; + + return p_flag; +} +/******************************************************************************* +** +** Function btm_ble_select_adv_interval +** +** Description select adv interval based on device mode +** +** Returns void +** +*******************************************************************************/ +void btm_ble_select_adv_interval(tBTM_BLE_INQ_CB *p_cb, UINT8 evt_type, UINT16 *p_adv_int_min, UINT16 *p_adv_int_max) +{ + if (p_cb->adv_interval_min && p_cb->adv_interval_max) { + *p_adv_int_min = p_cb->adv_interval_min; + *p_adv_int_max = p_cb->adv_interval_max; + } else { + switch (evt_type) { + case BTM_BLE_CONNECT_EVT: + case BTM_BLE_CONNECT_LO_DUTY_DIR_EVT: + *p_adv_int_min = *p_adv_int_max = BTM_BLE_GAP_ADV_FAST_INT_1; + break; + + case BTM_BLE_NON_CONNECT_EVT: + case BTM_BLE_DISCOVER_EVT: + *p_adv_int_min = *p_adv_int_max = BTM_BLE_GAP_ADV_FAST_INT_2; + break; + + /* connectable directed event */ + case BTM_BLE_CONNECT_DIR_EVT: + *p_adv_int_min = BTM_BLE_GAP_ADV_DIR_MIN_INT; + *p_adv_int_max = BTM_BLE_GAP_ADV_DIR_MAX_INT; + break; + + default: + *p_adv_int_min = *p_adv_int_max = BTM_BLE_GAP_ADV_SLOW_INT; + break; + } + } + return; +} + +/******************************************************************************* +** +** Function btm_ble_update_dmt_flag_bits +** +** Description Obtain updated adv flag value based on connect and discoverability mode. +** Also, setup DMT support value in the flag based on whether the controller +** supports both LE and BR/EDR. +** +** Parameters: flag_value (Input / Output) - flag value +** connect_mode (Input) - Connect mode value +** disc_mode (Input) - discoverability mode +** +** Returns void +** +*******************************************************************************/ +void btm_ble_update_dmt_flag_bits(UINT8 *adv_flag_value, const UINT16 connect_mode, + const UINT16 disc_mode) +{ + /* BR/EDR non-discoverable , non-connectable */ + if ((disc_mode & BTM_DISCOVERABLE_MASK) == 0 && + (connect_mode & BTM_CONNECTABLE_MASK) == 0) { + *adv_flag_value |= BTM_BLE_BREDR_NOT_SPT; + } else { + *adv_flag_value &= ~BTM_BLE_BREDR_NOT_SPT; + } + + /* if local controller support, mark both controller and host support in flag */ + if (controller_get_interface()->supports_simultaneous_le_bredr()) { + *adv_flag_value |= (BTM_BLE_DMT_CONTROLLER_SPT | BTM_BLE_DMT_HOST_SPT); + } else { + *adv_flag_value &= ~(BTM_BLE_DMT_CONTROLLER_SPT | BTM_BLE_DMT_HOST_SPT); + } +} + +/******************************************************************************* +** +** Function btm_ble_set_adv_flag +** +** Description Set adv flag in adv data. +** +** Parameters: connect_mode (Input)- Connect mode value +** disc_mode (Input) - discoverability mode +** +** Returns void +** +*******************************************************************************/ +void btm_ble_set_adv_flag(UINT16 connect_mode, UINT16 disc_mode) +{ + UINT8 flag = 0, old_flag = 0; + tBTM_BLE_LOCAL_ADV_DATA *p_adv_data = &btm_cb.ble_ctr_cb.inq_var.adv_data; + + if (p_adv_data->p_flags != NULL) { + flag = old_flag = *(p_adv_data->p_flags); + } + + btm_ble_update_dmt_flag_bits (&flag, connect_mode, disc_mode); + + BTM_TRACE_DEBUG("disc_mode %04x", disc_mode); + /* update discoverable flag */ + if (disc_mode & BTM_BLE_LIMITED_DISCOVERABLE) { + flag &= ~BTM_BLE_GEN_DISC_FLAG; + flag |= BTM_BLE_LIMIT_DISC_FLAG; + } else if (disc_mode & BTM_BLE_GENERAL_DISCOVERABLE) { + flag |= BTM_BLE_GEN_DISC_FLAG; + flag &= ~BTM_BLE_LIMIT_DISC_FLAG; + } else { /* remove all discoverable flags */ + flag &= ~(BTM_BLE_LIMIT_DISC_FLAG | BTM_BLE_GEN_DISC_FLAG); + } + + if (flag != old_flag) { + BTM_TRACE_ERROR("flag = 0x%x,old_flag = 0x%x", flag, old_flag); + btm_ble_update_adv_flag(flag); + } +} +/******************************************************************************* +** +** Function btm_ble_set_discoverability +** +** Description This function is called to set BLE discoverable mode. +** +** Parameters: combined_mode: discoverability mode. +** +** Returns BTM_SUCCESS is status set successfully; otherwise failure. +** +*******************************************************************************/ +tBTM_STATUS btm_ble_set_discoverability(UINT16 combined_mode) +{ + tBTM_LE_RANDOM_CB *p_addr_cb = &btm_cb.ble_ctr_cb.addr_mgnt_cb; + tBTM_BLE_INQ_CB *p_cb = &btm_cb.ble_ctr_cb.inq_var; + UINT16 mode = (combined_mode & BTM_BLE_DISCOVERABLE_MASK); + UINT8 new_mode = BTM_BLE_ADV_ENABLE; + UINT8 evt_type; + tBTM_STATUS status = BTM_SUCCESS; + BD_ADDR p_addr_ptr = {0}; + tBLE_ADDR_TYPE init_addr_type = BLE_ADDR_PUBLIC, + own_addr_type = p_addr_cb->own_addr_type; + UINT16 adv_int_min, adv_int_max; + + BTM_TRACE_EVENT ("%s mode=0x%0x combined_mode=0x%x\n", __FUNCTION__, mode, combined_mode); + + /*** Check mode parameter ***/ + if (mode > BTM_BLE_MAX_DISCOVERABLE) { + return (BTM_ILLEGAL_VALUE); + } + + p_cb->discoverable_mode = mode; + + evt_type = btm_set_conn_mode_adv_init_addr(p_cb, p_addr_ptr, &init_addr_type, &own_addr_type); + + if (p_cb->connectable_mode == BTM_BLE_NON_CONNECTABLE && mode == BTM_BLE_NON_DISCOVERABLE) { + new_mode = BTM_BLE_ADV_DISABLE; + } + + btm_ble_select_adv_interval(p_cb, evt_type, &adv_int_min, &adv_int_max); + + btu_stop_timer(&p_cb->fast_adv_timer); + + /* update adv params if start advertising */ + BTM_TRACE_EVENT ("evt_type=0x%x p-cb->evt_type=0x%x\n ", evt_type, p_cb->evt_type); + + if (new_mode == BTM_BLE_ADV_ENABLE) { + btm_ble_set_adv_flag (btm_cb.btm_inq_vars.connectable_mode, combined_mode); + + if (evt_type != p_cb->evt_type || p_cb->adv_addr_type != own_addr_type + || !p_cb->fast_adv_on) { + btm_ble_stop_adv(); + + /* update adv params */ + if (!btsnd_hcic_ble_write_adv_params (adv_int_min, + adv_int_max, + evt_type, + own_addr_type, + init_addr_type, + p_addr_ptr, + p_cb->adv_chnl_map, + p_cb->afp)) { + status = BTM_NO_RESOURCES; + } else { + p_cb->evt_type = evt_type; + p_cb->adv_addr_type = own_addr_type; + } + } + } + + if (status == BTM_SUCCESS && p_cb->adv_mode != new_mode) { + if (new_mode == BTM_BLE_ADV_ENABLE) { + status = btm_ble_start_adv(); + } else { + status = btm_ble_stop_adv(); + } + } + + if (p_cb->adv_mode == BTM_BLE_ADV_ENABLE) { + p_cb->fast_adv_on = TRUE; + /* start initial GAP mode adv timer */ + btu_start_timer (&p_cb->fast_adv_timer, BTU_TTYPE_BLE_GAP_FAST_ADV, + BTM_BLE_GAP_FAST_ADV_TOUT); + } else { +#if BLE_PRIVACY_SPT == TRUE + btm_ble_disable_resolving_list(BTM_BLE_RL_ADV, TRUE); +#endif + } + + /* set up stop advertising timer */ + if (status == BTM_SUCCESS && mode == BTM_BLE_LIMITED_DISCOVERABLE) { + BTM_TRACE_EVENT ("start timer for limited disc mode duration=%d (180 secs)", BTM_BLE_GAP_LIM_TOUT); + /* start Tgap(lim_timeout) */ + btu_start_timer (&p_cb->inq_timer_ent, BTU_TTYPE_BLE_GAP_LIM_DISC, + BTM_BLE_GAP_LIM_TOUT); + } + return status; +} + +/******************************************************************************* +** +** Function btm_ble_set_connectability +** +** Description This function is called to set BLE connectability mode. +** +** Parameters: combined_mode: connectability mode. +** +** Returns BTM_SUCCESS is status set successfully; otherwise failure. +** +*******************************************************************************/ +tBTM_STATUS btm_ble_set_connectability(UINT16 combined_mode) +{ + tBTM_LE_RANDOM_CB *p_addr_cb = &btm_cb.ble_ctr_cb.addr_mgnt_cb; + tBTM_BLE_INQ_CB *p_cb = &btm_cb.ble_ctr_cb.inq_var; + UINT16 mode = (combined_mode & BTM_BLE_CONNECTABLE_MASK); + UINT8 new_mode = BTM_BLE_ADV_ENABLE; + UINT8 evt_type; + tBTM_STATUS status = BTM_SUCCESS; + BD_ADDR p_addr_ptr = {0}; + tBLE_ADDR_TYPE peer_addr_type = BLE_ADDR_PUBLIC, + own_addr_type = p_addr_cb->own_addr_type; + UINT16 adv_int_min, adv_int_max; + + BTM_TRACE_EVENT ("%s mode=0x%0x combined_mode=0x%x\n", __FUNCTION__, mode, combined_mode); + + /*** Check mode parameter ***/ + if (mode > BTM_BLE_MAX_CONNECTABLE) { + return (BTM_ILLEGAL_VALUE); + } + + p_cb->connectable_mode = mode; + + evt_type = btm_set_conn_mode_adv_init_addr(p_cb, p_addr_ptr, &peer_addr_type, &own_addr_type); + + if (mode == BTM_BLE_NON_CONNECTABLE && p_cb->discoverable_mode == BTM_BLE_NON_DISCOVERABLE) { + new_mode = BTM_BLE_ADV_DISABLE; + } + + btm_ble_select_adv_interval(p_cb, evt_type, &adv_int_min, &adv_int_max); + + btu_stop_timer(&p_cb->fast_adv_timer); + /* update adv params if needed */ + if (new_mode == BTM_BLE_ADV_ENABLE) { + btm_ble_set_adv_flag (combined_mode, btm_cb.btm_inq_vars.discoverable_mode); + if (p_cb->evt_type != evt_type || p_cb->adv_addr_type != p_addr_cb->own_addr_type + || !p_cb->fast_adv_on) { + btm_ble_stop_adv(); + + if (!btsnd_hcic_ble_write_adv_params (adv_int_min, + adv_int_max, + evt_type, + own_addr_type, + peer_addr_type, + p_addr_ptr, + p_cb->adv_chnl_map, + p_cb->afp)) { + status = BTM_NO_RESOURCES; + } else { + p_cb->evt_type = evt_type; + p_cb->adv_addr_type = own_addr_type; + } + } + } + + /* update advertising mode */ + if (status == BTM_SUCCESS && new_mode != p_cb->adv_mode) { + if (new_mode == BTM_BLE_ADV_ENABLE) { + status = btm_ble_start_adv(); + } else { + status = btm_ble_stop_adv(); + } + } + + if (p_cb->adv_mode == BTM_BLE_ADV_ENABLE) { + p_cb->fast_adv_on = TRUE; + /* start initial GAP mode adv timer */ + btu_start_timer (&p_cb->fast_adv_timer, BTU_TTYPE_BLE_GAP_FAST_ADV, + BTM_BLE_GAP_FAST_ADV_TOUT); + } else { +#if BLE_PRIVACY_SPT == TRUE + btm_ble_disable_resolving_list(BTM_BLE_RL_ADV, TRUE); +#endif + } + return status; +} + + +/******************************************************************************* +** +** Function btm_ble_start_inquiry +** +** Description This function is called to start BLE inquiry procedure. +** If the duration is zero, the periodic inquiry mode is cancelled. +** +** Parameters: mode - GENERAL or LIMITED inquiry +** p_inq_params - pointer to the BLE inquiry parameter. +** p_results_cb - callback returning pointer to results (tBTM_INQ_RESULTS) +** p_cmpl_cb - callback indicating the end of an inquiry +** +** +** +** Returns BTM_CMD_STARTED if successfully started +** BTM_NO_RESOURCES if could not allocate a message buffer +** BTM_BUSY - if an inquiry is already active +** +*******************************************************************************/ +tBTM_STATUS btm_ble_start_inquiry (UINT8 mode, UINT8 duration) +{ + tBTM_STATUS status = BTM_CMD_STARTED; + tBTM_BLE_CB *p_ble_cb = &btm_cb.ble_ctr_cb; + tBTM_INQUIRY_VAR_ST *p_inq = &btm_cb.btm_inq_vars; + + BTM_TRACE_DEBUG("btm_ble_start_inquiry: mode = %02x inq_active = 0x%02x", mode, btm_cb.btm_inq_vars.inq_active); + + /* if selective connection is active, or inquiry is already active, reject it */ + if (BTM_BLE_IS_INQ_ACTIVE(p_ble_cb->scan_activity) || + BTM_BLE_IS_SEL_CONN_ACTIVE (p_ble_cb->scan_activity)) { + BTM_TRACE_ERROR("LE Inquiry is active, can not start inquiry"); + return (BTM_BUSY); + } + + if (!BTM_BLE_IS_SCAN_ACTIVE(p_ble_cb->scan_activity)) { + btsnd_hcic_ble_set_scan_params(BTM_BLE_SCAN_MODE_ACTI, + BTM_BLE_LOW_LATENCY_SCAN_INT, + BTM_BLE_LOW_LATENCY_SCAN_WIN, + btm_cb.ble_ctr_cb.addr_mgnt_cb.own_addr_type, + SP_ADV_ALL); +#if (defined BLE_PRIVACY_SPT && BLE_PRIVACY_SPT == TRUE) + /* enable IRK list */ + //btm_ble_enable_resolving_list_for_platform(BTM_BLE_RL_SCAN); +#endif + status = btm_ble_start_scan(); + } else if ((p_ble_cb->inq_var.scan_interval != BTM_BLE_LOW_LATENCY_SCAN_INT) || + (p_ble_cb->inq_var.scan_window != BTM_BLE_LOW_LATENCY_SCAN_WIN)) { + BTM_TRACE_DEBUG("%s, restart LE scan with low latency scan params", __FUNCTION__); + btsnd_hcic_ble_set_scan_enable(BTM_BLE_SCAN_DISABLE, BTM_BLE_DUPLICATE_ENABLE); + btsnd_hcic_ble_set_scan_params(BTM_BLE_SCAN_MODE_ACTI, + BTM_BLE_LOW_LATENCY_SCAN_INT, + BTM_BLE_LOW_LATENCY_SCAN_WIN, + btm_cb.ble_ctr_cb.addr_mgnt_cb.own_addr_type, + SP_ADV_ALL); + btsnd_hcic_ble_set_scan_enable(BTM_BLE_SCAN_ENABLE, BTM_BLE_DUPLICATE_DISABLE); + } + + if (status == BTM_CMD_STARTED) { + p_inq->inq_active |= mode; + p_ble_cb->scan_activity |= mode; + + BTM_TRACE_DEBUG("btm_ble_start_inquiry inq_active = 0x%02x", p_inq->inq_active); + + if (duration != 0) { + /* start inquiry timer */ + btu_start_timer (&p_ble_cb->inq_var.inq_timer_ent, BTU_TTYPE_BLE_INQUIRY, duration); + } + } + + return status; + +} + +/******************************************************************************* +** +** Function btm_ble_read_remote_name_cmpl +** +** Description This function is called when BLE remote name is received. +** +** Returns void +** +*******************************************************************************/ +void btm_ble_read_remote_name_cmpl(BOOLEAN status, BD_ADDR bda, UINT16 length, char *p_name) +{ + UINT8 hci_status = HCI_SUCCESS; + BD_NAME bd_name; + + memset(bd_name, 0, (BD_NAME_LEN + 1)); + if (length > BD_NAME_LEN) { + length = BD_NAME_LEN; + } + memcpy((UINT8 *)bd_name, p_name, length); + + if ((!status) || (length == 0)) { + hci_status = HCI_ERR_HOST_TIMEOUT; + } + + btm_process_remote_name(bda, bd_name, length + 1, hci_status); +#if (SMP_INCLUDED == TRUE) + btm_sec_rmt_name_request_complete (bda, (UINT8 *)p_name, hci_status); +#endif ///SMP_INCLUDED == TRUE +} + +/******************************************************************************* +** +** Function btm_ble_read_remote_name +** +** Description This function read remote LE device name using GATT read +** procedure. +** +** Parameters: None. +** +** Returns void +** +*******************************************************************************/ +tBTM_STATUS btm_ble_read_remote_name(BD_ADDR remote_bda, tBTM_INQ_INFO *p_cur, tBTM_CMPL_CB *p_cb) +{ + tBTM_INQUIRY_VAR_ST *p_inq = &btm_cb.btm_inq_vars; + + if (!controller_get_interface()->supports_ble()) { + return BTM_ERR_PROCESSING; + } + + if (p_cur && + p_cur->results.ble_evt_type != BTM_BLE_EVT_CONN_ADV && + p_cur->results.ble_evt_type != BTM_BLE_EVT_CONN_DIR_ADV) { + BTM_TRACE_DEBUG("name request to non-connectable device failed."); + return BTM_ERR_PROCESSING; + } + + /* read remote device name using GATT procedure */ + if (p_inq->remname_active) { + return BTM_BUSY; + } + +#if (defined(GAP_INCLUDED) && GAP_INCLUDED == TRUE && GATTS_INCLUDED == TRUE) + if (!GAP_BleReadPeerDevName(remote_bda, btm_ble_read_remote_name_cmpl)) { + return BTM_BUSY; + } +#endif + + p_inq->p_remname_cmpl_cb = p_cb; + p_inq->remname_active = TRUE; + + memcpy(p_inq->remname_bda, remote_bda, BD_ADDR_LEN); + + btu_start_timer (&p_inq->rmt_name_timer_ent, + BTU_TTYPE_BTM_RMT_NAME, + BTM_EXT_BLE_RMT_NAME_TIMEOUT); + + return BTM_CMD_STARTED; +} + +/******************************************************************************* +** +** Function btm_ble_cancel_remote_name +** +** Description This function cancel read remote LE device name. +** +** Parameters: None. +** +** Returns void +** +*******************************************************************************/ +BOOLEAN btm_ble_cancel_remote_name(BD_ADDR remote_bda) +{ + tBTM_INQUIRY_VAR_ST *p_inq = &btm_cb.btm_inq_vars; + BOOLEAN status = TRUE; + +#if (defined(GAP_INCLUDED) && GAP_INCLUDED == TRUE && GATTS_INCLUDED == TRUE) + status = GAP_BleCancelReadPeerDevName(remote_bda); +#endif + + p_inq->remname_active = FALSE; + memset(p_inq->remname_bda, 0, BD_ADDR_LEN); + btu_stop_timer(&p_inq->rmt_name_timer_ent); + + return status; +} + +/******************************************************************************* +** +** Function btm_ble_update_adv_flag +** +** Description This function update the limited discoverable flag in the adv +** data. +** +** Parameters: None. +** +** Returns void +** +*******************************************************************************/ +static void btm_ble_update_adv_flag(UINT8 flag) +{ + tBTM_BLE_LOCAL_ADV_DATA *p_adv_data = &btm_cb.ble_ctr_cb.inq_var.adv_data; + UINT8 *p; + + BTM_TRACE_DEBUG ("btm_ble_update_adv_flag new=0x%x", flag); + + if (p_adv_data->p_flags != NULL) { + BTM_TRACE_DEBUG ("btm_ble_update_adv_flag old=0x%x", *p_adv_data->p_flags); + *p_adv_data->p_flags = flag; + } else { /* no FLAGS in ADV data*/ + p = (p_adv_data->p_pad == NULL) ? p_adv_data->ad_data : p_adv_data->p_pad; + /* need 3 bytes space to stuff in the flags, if not */ + /* erase all written data, just for flags */ + if ((BTM_BLE_AD_DATA_LEN - (p - p_adv_data->ad_data)) < 3) { + p = p_adv_data->p_pad = p_adv_data->ad_data; + memset(p_adv_data->ad_data, 0, BTM_BLE_AD_DATA_LEN); + } + + *p++ = 2; + *p++ = BTM_BLE_AD_TYPE_FLAG; + p_adv_data->p_flags = p; + *p++ = flag; + p_adv_data->p_pad = p; + } + + if (btsnd_hcic_ble_set_adv_data((UINT8)(p_adv_data->p_pad - p_adv_data->ad_data), + p_adv_data->ad_data)) { + p_adv_data->data_mask |= BTM_BLE_AD_BIT_FLAGS; + } + +} + +#if 0 +/******************************************************************************* +** +** Function btm_ble_parse_adv_data +** +** Description This function parse the adv data into a structure. +** +** Returns pointer to entry, or NULL if not found +** +*******************************************************************************/ +static void btm_ble_parse_adv_data(tBTM_INQ_INFO *p_info, UINT8 *p_data, + UINT8 len, tBTM_BLE_INQ_DATA *p_adv_data, UINT8 *p_buf) +{ + UINT8 *p_cur = p_data; + UINT8 ad_len, ad_type, ad_flag; + + BTM_TRACE_EVENT (" btm_ble_parse_adv_data"); + + while (len > 0) { + BTM_TRACE_DEBUG("btm_ble_parse_adv_data: len = %d", len); + if ((ad_len = *p_cur ++) == 0) { + break; + } + + ad_type = *p_cur ++; + + BTM_TRACE_DEBUG(" ad_type = %02x ad_len = %d", ad_type, ad_len); + + switch (ad_type) { + case BTM_BLE_AD_TYPE_NAME_SHORT: + + case BTM_BLE_AD_TYPE_NAME_CMPL: + p_adv_data->ad_mask |= BTM_BLE_AD_BIT_DEV_NAME; + if (p_info) { + p_info->remote_name_type = (ad_type == BTM_BLE_AD_TYPE_NAME_SHORT) ? + BTM_BLE_NAME_SHORT : BTM_BLE_NAME_CMPL; + memcpy(p_info->remote_name, p_cur, ad_len - 1); + p_info->remote_name[ad_len] = 0; + p_adv_data->p_remote_name = p_info->remote_name; + p_info->remote_name_len = p_adv_data->remote_name_len = ad_len - 1; + BTM_TRACE_DEBUG("BTM_BLE_AD_TYPE_NAME name = %s", p_adv_data->p_remote_name); + } + p_cur += (ad_len - 1); + + break; + + case BTM_BLE_AD_TYPE_FLAG: + p_adv_data->ad_mask |= BTM_BLE_AD_BIT_FLAGS; + ad_flag = *p_cur ++; + p_adv_data->flag = (UINT8)(ad_flag & BTM_BLE_ADV_FLAG_MASK) ; + BTM_TRACE_DEBUG("BTM_BLE_AD_TYPE_FLAG flag = %s | %s | %s", + (p_adv_data->flag & BTM_BLE_LIMIT_DISC_FLAG) ? "LE_LIMIT_DISC" : "", + (p_adv_data->flag & BTM_BLE_GEN_DISC_FLAG) ? "LE_GENERAL_DISC" : "", + (p_adv_data->flag & BTM_BLE_BREDR_NOT_SPT) ? "LE Only device" : ""); + break; + + case BTM_BLE_AD_TYPE_TX_PWR: + p_adv_data->ad_mask |= BTM_BLE_AD_BIT_TX_PWR; + p_adv_data->tx_power_level = (INT8) * p_cur ++; + BTM_TRACE_DEBUG("BTM_BLE_AD_TYPE_TX_PWR tx_level = %d", p_adv_data->tx_power_level); + break; + + case BTM_BLE_AD_TYPE_MANU: + + case BTM_BLE_AD_TYPE_16SRV_PART: + case BTM_BLE_AD_TYPE_16SRV_CMPL: + p_adv_data->ad_mask |= BTM_BLE_AD_BIT_SERVICE; + /* need allocate memory to store UUID list */ + p_adv_data->service.num_service = (ad_len - 1) / 2; + BTM_TRACE_DEBUG("service UUID list, num = %d", p_adv_data->service.num_service); + p_cur += (ad_len - 1); + break; + + case BTM_BLE_AD_TYPE_SOL_SRV_UUID: + p_adv_data->ad_mask |= BTM_BLE_AD_BIT_SERVICE_SOL; + /* need allocate memory to store UUID list */ + p_adv_data->service.num_service = (ad_len - 1) / 2; + BTM_TRACE_DEBUG("service UUID list, num = %d", p_adv_data->service.num_service); + p_cur += (ad_len - 1); + break; + + case BTM_BLE_AD_TYPE_128SOL_SRV_UUID: + p_adv_data->ad_mask |= BTM_BLE_AD_BIT_SERVICE_128SOL; + /* need allocate memory to store UUID list */ + p_adv_data->service.num_service = (ad_len - 1) / 16; + BTM_TRACE_DEBUG("service UUID list, num = %d", p_adv_data->service.num_service); + p_cur += (ad_len - 1); + break; + + case BTM_BLE_AD_TYPE_APPEARANCE: + case BTM_BLE_AD_TYPE_PUBLIC_TARGET: + case BTM_BLE_AD_TYPE_RANDOM_TARGET: + default: + break; + } + len -= (ad_len + 1); + } +} +#endif + +/******************************************************************************* +** +** Function btm_ble_cache_adv_data +** +** Description Update advertising cache data. +** +** Returns void +** +*******************************************************************************/ +void btm_ble_cache_adv_data(BD_ADDR bda, tBTM_INQ_RESULTS *p_cur, UINT8 data_len, UINT8 *p, UINT8 evt_type) +{ + tBTM_BLE_INQ_CB *p_le_inq_cb = &btm_cb.ble_ctr_cb.inq_var; + UINT8 *p_cache; + //UINT8 length; + + /* cache adv report/scan response data */ + if (evt_type != BTM_BLE_SCAN_RSP_EVT) { + p_le_inq_cb->adv_len = 0; + memset(p_le_inq_cb->adv_data_cache, 0, BTM_BLE_CACHE_ADV_DATA_MAX); + p_cur->adv_data_len = 0; + p_cur->scan_rsp_len = 0; + } + + //Clear the adv cache if the addresses are not equal + if(memcmp(bda, p_le_inq_cb->adv_addr, BD_ADDR_LEN) != 0) { + p_le_inq_cb->adv_len = 0; + memcpy(p_le_inq_cb->adv_addr, bda, BD_ADDR_LEN); + memset(p_le_inq_cb->adv_data_cache, 0, BTM_BLE_CACHE_ADV_DATA_MAX); + p_cur->adv_data_len = 0; + p_cur->scan_rsp_len = 0; + } + + if (data_len > 0) { + p_cache = &p_le_inq_cb->adv_data_cache[p_le_inq_cb->adv_len]; + if((data_len + p_le_inq_cb->adv_len) <= BTM_BLE_CACHE_ADV_DATA_MAX) { + memcpy(p_cache, p, data_len); + p_le_inq_cb->adv_len += data_len; + } + } + + if (evt_type != BTM_BLE_SCAN_RSP_EVT) { + p_cur->adv_data_len = p_le_inq_cb->adv_len; + } + else { + p_cur->scan_rsp_len = p_le_inq_cb->adv_len - p_cur->adv_data_len; + } + + /* parse service UUID from adv packet and save it in inq db eir_uuid */ + /* TODO */ +} + +/******************************************************************************* +** +** Function btm_ble_is_discoverable +** +** Description check ADV flag to make sure device is discoverable and match +** the search condition +** +** Parameters +** +** Returns void +** +*******************************************************************************/ +UINT8 btm_ble_is_discoverable(BD_ADDR bda, UINT8 evt_type, UINT8 *p) +{ + UINT8 *p_flag, flag = 0, rt = 0; + UINT8 data_len; + tBTM_INQ_PARMS *p_cond = &btm_cb.btm_inq_vars.inqparms; + tBTM_BLE_INQ_CB *p_le_inq_cb = &btm_cb.ble_ctr_cb.inq_var; + + UNUSED(p); + + /* for observer, always "discoverable */ + if (BTM_BLE_IS_OBS_ACTIVE(btm_cb.ble_ctr_cb.scan_activity)) { + rt |= BTM_BLE_OBS_RESULT; + } + /* for discover, always "discoverable */ + if (BTM_BLE_IS_DISCO_ACTIVE(btm_cb.ble_ctr_cb.scan_activity)) { + rt |= BTM_BLE_DISCO_RESULT; + } + + if (BTM_BLE_IS_SEL_CONN_ACTIVE(btm_cb.ble_ctr_cb.scan_activity) && + (evt_type == BTM_BLE_CONNECT_EVT || evt_type == BTM_BLE_CONNECT_DIR_EVT)) { + rt |= BTM_BLE_SEL_CONN_RESULT; + } + + /* does not match filter condition */ + if (p_cond->filter_cond_type == BTM_FILTER_COND_BD_ADDR && + memcmp(bda, p_cond->filter_cond.bdaddr_cond, BD_ADDR_LEN) != 0) { + BTM_TRACE_DEBUG("BD ADDR does not meet filter condition"); + return rt; + } + + if (p_le_inq_cb->adv_len != 0) { + if ((p_flag = BTM_CheckAdvData(p_le_inq_cb->adv_data_cache, + BTM_BLE_AD_TYPE_FLAG, &data_len)) != NULL) { + flag = * p_flag; + + if ((btm_cb.btm_inq_vars.inq_active & BTM_BLE_GENERAL_INQUIRY) && + (flag & (BTM_BLE_LIMIT_DISC_FLAG | BTM_BLE_GEN_DISC_FLAG)) != 0) { + BTM_TRACE_DEBUG("Find Generable Discoverable device"); + rt |= BTM_BLE_INQ_RESULT; + } + + else if (btm_cb.btm_inq_vars.inq_active & BTM_BLE_LIMITED_INQUIRY && + (flag & BTM_BLE_LIMIT_DISC_FLAG) != 0) { + BTM_TRACE_DEBUG("Find limited discoverable device"); + rt |= BTM_BLE_INQ_RESULT; + } + } + } + return rt; +} + +static void btm_ble_appearance_to_cod(UINT16 appearance, UINT8 *dev_class) +{ + dev_class[0] = 0; + + switch (appearance) { + case BTM_BLE_APPEARANCE_GENERIC_PHONE: + dev_class[1] = BTM_COD_MAJOR_PHONE; + dev_class[2] = BTM_COD_MINOR_UNCLASSIFIED; + break; + case BTM_BLE_APPEARANCE_GENERIC_COMPUTER: + dev_class[1] = BTM_COD_MAJOR_COMPUTER; + dev_class[2] = BTM_COD_MINOR_UNCLASSIFIED; + break; + case BTM_BLE_APPEARANCE_GENERIC_REMOTE: + dev_class[1] = BTM_COD_MAJOR_PERIPHERAL; + dev_class[2] = BTM_COD_MINOR_REMOTE_CONTROL; + break; + case BTM_BLE_APPEARANCE_GENERIC_THERMOMETER: + case BTM_BLE_APPEARANCE_THERMOMETER_EAR: + dev_class[1] = BTM_COD_MAJOR_HEALTH; + dev_class[2] = BTM_COD_MINOR_THERMOMETER; + break; + case BTM_BLE_APPEARANCE_GENERIC_HEART_RATE: + case BTM_BLE_APPEARANCE_HEART_RATE_BELT: + dev_class[1] = BTM_COD_MAJOR_HEALTH; + dev_class[2] = BTM_COD_MINOR_HEART_PULSE_MONITOR; + break; + case BTM_BLE_APPEARANCE_GENERIC_BLOOD_PRESSURE: + case BTM_BLE_APPEARANCE_BLOOD_PRESSURE_ARM: + case BTM_BLE_APPEARANCE_BLOOD_PRESSURE_WRIST: + dev_class[1] = BTM_COD_MAJOR_HEALTH; + dev_class[2] = BTM_COD_MINOR_BLOOD_MONITOR; + break; + case BTM_BLE_APPEARANCE_GENERIC_PULSE_OXIMETER: + case BTM_BLE_APPEARANCE_PULSE_OXIMETER_FINGERTIP: + case BTM_BLE_APPEARANCE_PULSE_OXIMETER_WRIST: + dev_class[1] = BTM_COD_MAJOR_HEALTH; + dev_class[2] = BTM_COD_MINOR_PULSE_OXIMETER; + break; + case BTM_BLE_APPEARANCE_GENERIC_GLUCOSE: + dev_class[1] = BTM_COD_MAJOR_HEALTH; + dev_class[2] = BTM_COD_MINOR_GLUCOSE_METER; + break; + case BTM_BLE_APPEARANCE_GENERIC_WEIGHT: + dev_class[1] = BTM_COD_MAJOR_HEALTH; + dev_class[2] = BTM_COD_MINOR_WEIGHING_SCALE; + break; + case BTM_BLE_APPEARANCE_GENERIC_WALKING: + case BTM_BLE_APPEARANCE_WALKING_IN_SHOE: + case BTM_BLE_APPEARANCE_WALKING_ON_SHOE: + case BTM_BLE_APPEARANCE_WALKING_ON_HIP: + dev_class[1] = BTM_COD_MAJOR_HEALTH; + dev_class[2] = BTM_COD_MINOR_STEP_COUNTER; + break; + case BTM_BLE_APPEARANCE_GENERIC_WATCH: + case BTM_BLE_APPEARANCE_SPORTS_WATCH: + dev_class[1] = BTM_COD_MAJOR_WEARABLE; + dev_class[2] = BTM_COD_MINOR_WRIST_WATCH; + break; + case BTM_BLE_APPEARANCE_GENERIC_EYEGLASSES: + dev_class[1] = BTM_COD_MAJOR_WEARABLE; + dev_class[2] = BTM_COD_MINOR_GLASSES; + break; + case BTM_BLE_APPEARANCE_GENERIC_DISPLAY: + dev_class[1] = BTM_COD_MAJOR_IMAGING; + dev_class[2] = BTM_COD_MINOR_DISPLAY; + break; + case BTM_BLE_APPEARANCE_GENERIC_MEDIA_PLAYER: + dev_class[1] = BTM_COD_MAJOR_AUDIO; + dev_class[2] = BTM_COD_MINOR_UNCLASSIFIED; + break; + case BTM_BLE_APPEARANCE_GENERIC_BARCODE_SCANNER: + case BTM_BLE_APPEARANCE_HID_BARCODE_SCANNER: + case BTM_BLE_APPEARANCE_GENERIC_HID: + dev_class[1] = BTM_COD_MAJOR_PERIPHERAL; + dev_class[2] = BTM_COD_MINOR_UNCLASSIFIED; + break; + case BTM_BLE_APPEARANCE_HID_KEYBOARD: + dev_class[1] = BTM_COD_MAJOR_PERIPHERAL; + dev_class[2] = BTM_COD_MINOR_KEYBOARD; + break; + case BTM_BLE_APPEARANCE_HID_MOUSE: + dev_class[1] = BTM_COD_MAJOR_PERIPHERAL; + dev_class[2] = BTM_COD_MINOR_POINTING; + break; + case BTM_BLE_APPEARANCE_HID_JOYSTICK: + dev_class[1] = BTM_COD_MAJOR_PERIPHERAL; + dev_class[2] = BTM_COD_MINOR_JOYSTICK; + break; + case BTM_BLE_APPEARANCE_HID_GAMEPAD: + dev_class[1] = BTM_COD_MAJOR_PERIPHERAL; + dev_class[2] = BTM_COD_MINOR_GAMEPAD; + break; + case BTM_BLE_APPEARANCE_HID_DIGITIZER_TABLET: + dev_class[1] = BTM_COD_MAJOR_PERIPHERAL; + dev_class[2] = BTM_COD_MINOR_DIGITIZING_TABLET; + break; + case BTM_BLE_APPEARANCE_HID_CARD_READER: + dev_class[1] = BTM_COD_MAJOR_PERIPHERAL; + dev_class[2] = BTM_COD_MINOR_CARD_READER; + break; + case BTM_BLE_APPEARANCE_HID_DIGITAL_PEN: + dev_class[1] = BTM_COD_MAJOR_PERIPHERAL; + dev_class[2] = BTM_COD_MINOR_DIGITAL_PAN; + break; + case BTM_BLE_APPEARANCE_UNKNOWN: + case BTM_BLE_APPEARANCE_GENERIC_CLOCK: + case BTM_BLE_APPEARANCE_GENERIC_TAG: + case BTM_BLE_APPEARANCE_GENERIC_KEYRING: + case BTM_BLE_APPEARANCE_GENERIC_CYCLING: + case BTM_BLE_APPEARANCE_CYCLING_COMPUTER: + case BTM_BLE_APPEARANCE_CYCLING_SPEED: + case BTM_BLE_APPEARANCE_CYCLING_CADENCE: + case BTM_BLE_APPEARANCE_CYCLING_POWER: + case BTM_BLE_APPEARANCE_CYCLING_SPEED_CADENCE: + case BTM_BLE_APPEARANCE_GENERIC_OUTDOOR_SPORTS: + case BTM_BLE_APPEARANCE_OUTDOOR_SPORTS_LOCATION: + case BTM_BLE_APPEARANCE_OUTDOOR_SPORTS_LOCATION_AND_NAV: + case BTM_BLE_APPEARANCE_OUTDOOR_SPORTS_LOCATION_POD: + case BTM_BLE_APPEARANCE_OUTDOOR_SPORTS_LOCATION_POD_AND_NAV: + default: + dev_class[1] = BTM_COD_MAJOR_UNCLASSIFIED; + dev_class[2] = BTM_COD_MINOR_UNCLASSIFIED; + }; +} + +/******************************************************************************* +** +** Function btm_ble_update_inq_result +** +** Description Update adv packet information into inquiry result. +** +** Parameters +** +** Returns void +** +*******************************************************************************/ +BOOLEAN btm_ble_update_inq_result(BD_ADDR bda, tINQ_DB_ENT *p_i, UINT8 addr_type, UINT8 evt_type, UINT8 *p) +{ + BOOLEAN to_report = TRUE; + tBTM_INQ_RESULTS *p_cur = &p_i->inq_info.results; + UINT8 len; + UINT8 *p_flag; + tBTM_INQUIRY_VAR_ST *p_inq = &btm_cb.btm_inq_vars; + UINT8 data_len, rssi; + tBTM_BLE_INQ_CB *p_le_inq_cb = &btm_cb.ble_ctr_cb.inq_var; + UINT8 *p1; + UINT8 *p_uuid16; + + STREAM_TO_UINT8 (data_len, p); + + if (data_len > BTM_BLE_ADV_DATA_LEN_MAX) { + BTM_TRACE_WARNING("EIR data too long %d. discard", data_len); + return FALSE; + } + btm_ble_cache_adv_data(bda, p_cur, data_len, p, evt_type); + + p1 = (p + data_len); + STREAM_TO_UINT8 (rssi, p1); + + /* Save the info */ + p_cur->inq_result_type = BTM_INQ_RESULT_BLE; + p_cur->ble_addr_type = addr_type; + p_cur->rssi = rssi; + + /* active scan, always wait until get scan_rsp to report the result */ + if ((btm_cb.ble_ctr_cb.inq_var.scan_type == BTM_BLE_SCAN_MODE_ACTI && + (evt_type == BTM_BLE_CONNECT_EVT || evt_type == BTM_BLE_DISCOVER_EVT))) { + BTM_TRACE_DEBUG("btm_ble_update_inq_result scan_rsp=false, to_report=false,\ + scan_type_active=%d", btm_cb.ble_ctr_cb.inq_var.scan_type); + p_i->scan_rsp = FALSE; +#if BTM_BLE_ACTIVE_SCAN_REPORT_ADV_SCAN_RSP_INDIVIDUALLY == FALSE + to_report = FALSE; +#endif + } else { + p_i->scan_rsp = TRUE; + } + + if (p_i->inq_count != p_inq->inq_counter) { + p_cur->device_type = BT_DEVICE_TYPE_BLE; + } else { + p_cur->device_type |= BT_DEVICE_TYPE_BLE; + } + + if (evt_type != BTM_BLE_SCAN_RSP_EVT) { + p_cur->ble_evt_type = evt_type; + } +#if BTM_BLE_ACTIVE_SCAN_REPORT_ADV_SCAN_RSP_INDIVIDUALLY + if (evt_type == BTM_BLE_SCAN_RSP_EVT) { + p_cur->ble_evt_type = evt_type; + } +#endif + p_i->inq_count = p_inq->inq_counter; /* Mark entry for current inquiry */ + + if (p_le_inq_cb->adv_len != 0) { + if ((p_flag = BTM_CheckAdvData(p_le_inq_cb->adv_data_cache, BTM_BLE_AD_TYPE_FLAG, &len)) != NULL) { + p_cur->flag = * p_flag; + } + } + + if (p_le_inq_cb->adv_len != 0) { + /* Check to see the BLE device has the Appearance UUID in the advertising data. If it does + * then try to convert the appearance value to a class of device value Bluedroid can use. + * Otherwise fall back to trying to infer if it is a HID device based on the service class. + */ + p_uuid16 = BTM_CheckAdvData(p_le_inq_cb->adv_data_cache, BTM_BLE_AD_TYPE_APPEARANCE, &len); + if (p_uuid16 && len == 2) { + btm_ble_appearance_to_cod((UINT16)p_uuid16[0] | (p_uuid16[1] << 8), p_cur->dev_class); + } else { + if ((p_uuid16 = BTM_CheckAdvData(p_le_inq_cb->adv_data_cache, + BTM_BLE_AD_TYPE_16SRV_CMPL, &len)) != NULL) { + UINT8 i; + for (i = 0; i + 2 <= len; i = i + 2) { +#if BTA_HH_LE_INCLUDED == TRUE + /* if this BLE device support HID over LE, set HID Major in class of device */ + if ((p_uuid16[i] | (p_uuid16[i + 1] << 8)) == UUID_SERVCLASS_LE_HID) { + p_cur->dev_class[0] = 0; + p_cur->dev_class[1] = BTM_COD_MAJOR_PERIPHERAL; + p_cur->dev_class[2] = 0; + break; + } +#endif /* BTA_HH_LE_INCLUDED */ + } + } + } + } + + /* if BR/EDR not supported is not set, assume is a DUMO device */ + if ((p_cur->flag & BTM_BLE_BREDR_NOT_SPT) == 0 && + evt_type != BTM_BLE_CONNECT_DIR_EVT) { + if (p_cur->ble_addr_type != BLE_ADDR_RANDOM) { + BTM_TRACE_DEBUG("BR/EDR NOT support bit not set, treat as DUMO"); + p_cur->device_type |= BT_DEVICE_TYPE_DUMO; + } else { + BTM_TRACE_DEBUG("Random address, treating device as LE only"); + } + } else { + BTM_TRACE_DEBUG("BR/EDR NOT SUPPORT bit set, LE only device"); + } + + return to_report; + +} + +/******************************************************************************* +** +** Function btm_clear_all_pending_le_entry +** +** Description This function is called to clear all LE pending entry in +** inquiry database. +** +** Returns void +** +*******************************************************************************/ +void btm_clear_all_pending_le_entry(void) +{ + UINT16 xx; + tINQ_DB_ENT *p_ent = btm_cb.btm_inq_vars.inq_db; + + for (xx = 0; xx < BTM_INQ_DB_SIZE; xx++, p_ent++) { + /* mark all pending LE entry as unused if an LE only device has scan response outstanding */ + if ((p_ent->in_use) && + (p_ent->inq_info.results.device_type == BT_DEVICE_TYPE_BLE) && + !p_ent->scan_rsp) { + p_ent->in_use = FALSE; + } + } +} + +/******************************************************************************* +** +** Function btm_send_sel_conn_callback +** +** Description send selection connection request callback. +** +** Parameters +** +** Returns void +** +*******************************************************************************/ +void btm_send_sel_conn_callback(BD_ADDR remote_bda, UINT8 evt_type, UINT8 *p_data, UINT8 addr_type) +{ + UINT8 data_len, len; + UINT8 *p_dev_name, remname[31] = {0}; + UNUSED(addr_type); + + if (btm_cb.ble_ctr_cb.p_select_cback == NULL || + /* non-connectable device */ + (evt_type != BTM_BLE_EVT_CONN_ADV && evt_type != BTM_BLE_EVT_CONN_DIR_ADV)) { + return; + } + + STREAM_TO_UINT8 (data_len, p_data); + + /* get the device name if exist in ADV data */ + if (data_len != 0) { + p_dev_name = BTM_CheckAdvData(p_data, BTM_BLE_AD_TYPE_NAME_CMPL, &len); + + if (p_dev_name == NULL) { + p_dev_name = BTM_CheckAdvData(p_data, BTM_BLE_AD_TYPE_NAME_SHORT, &len); + } + + if (p_dev_name) { + memcpy(remname, p_dev_name, len); + } + } + /* allow connection */ + if ((* btm_cb.ble_ctr_cb.p_select_cback)(remote_bda, remname)) { + /* terminate selective connection, initiate connection */ + btm_ble_initiate_select_conn(remote_bda); + } +} + +static void btm_adv_pkt_handler(void *arg) +{ + UINT8 hci_evt_code, hci_evt_len; + UINT8 ble_sub_code; + + tBTM_BLE_CB *p_cb = &btm_cb.ble_ctr_cb; + size_t pkts_to_process = pkt_queue_length(p_cb->adv_rpt_queue); + if (pkts_to_process > BTM_BLE_GAP_ADV_RPT_BATCH_SIZE) { + pkts_to_process = BTM_BLE_GAP_ADV_RPT_BATCH_SIZE; + } + + for (size_t i = 0; i < pkts_to_process; i++) { + pkt_linked_item_t *linked_pkt = pkt_queue_dequeue(p_cb->adv_rpt_queue); + assert(linked_pkt != NULL); + BT_HDR *packet = (BT_HDR *)linked_pkt->data; + uint8_t *p = packet->data + packet->offset; + STREAM_TO_UINT8 (hci_evt_code, p); + STREAM_TO_UINT8 (hci_evt_len, p); + STREAM_TO_UINT8 (ble_sub_code, p); + if (ble_sub_code == HCI_BLE_ADV_PKT_RPT_EVT) { + btm_ble_process_adv_pkt(p); + } else if (ble_sub_code == HCI_BLE_ADV_DISCARD_REPORT_EVT) { + btm_ble_process_adv_discard_evt(p); + } else if (ble_sub_code == HCI_BLE_DIRECT_ADV_EVT) { + btm_ble_process_direct_adv_pkt(p); + } else { + assert (0); + } + + osi_free(linked_pkt); +#if (BLE_ADV_REPORT_FLOW_CONTROL == TRUE) + hci_adv_credits_try_release(1); +#endif + } + + if (pkt_queue_length(p_cb->adv_rpt_queue) != 0) { + btu_task_post(SIG_BTU_HCI_ADV_RPT_MSG, NULL, OSI_THREAD_MAX_TIMEOUT); + } + + UNUSED(hci_evt_code); + UNUSED(hci_evt_len); +} + +/******************************************************************************* +** +** Function btm_ble_process_adv_pkt +** +** Description This function is called when adv packet report events are +** received from the device. It updates the inquiry database. +** If the inquiry database is full, the oldest entry is discarded. +** +** Parameters +** +** Returns void +** +*******************************************************************************/ +void btm_ble_process_adv_pkt (UINT8 *p_data) +{ + BD_ADDR bda; + UINT8 evt_type = 0, *p = p_data; + UINT8 addr_type = 0; + UINT8 num_reports; + UINT8 data_len; +#if (defined BLE_PRIVACY_SPT && BLE_PRIVACY_SPT == TRUE) + BOOLEAN match = FALSE; +#if (!CONTROLLER_RPA_LIST_ENABLE) + BD_ADDR temp_bda; + UINT8 temp_addr_type = 0; +#endif // (!CONTROLLER_RPA_LIST_ENABLE) +#endif//(defined BLE_PRIVACY_SPT && BLE_PRIVACY_SPT == TRUE) + + /* Only process the results if the inquiry is still active */ + if (!BTM_BLE_IS_SCAN_ACTIVE(btm_cb.ble_ctr_cb.scan_activity)) { + return; + } + + /* Extract the number of reports in this event. */ + STREAM_TO_UINT8(num_reports, p); + + while (num_reports--) { + /* Extract inquiry results */ + STREAM_TO_UINT8 (evt_type, p); + STREAM_TO_UINT8 (addr_type, p); + STREAM_TO_BDADDR (bda, p); + //BTM_TRACE_ERROR("btm_ble_process_adv_pkt:bda= %0x:%0x:%0x:%0x:%0x:%0x\n", + // bda[0],bda[1],bda[2],bda[3],bda[4],bda[5]); +#if (defined BLE_PRIVACY_SPT && BLE_PRIVACY_SPT == TRUE) + +#if (!CONTROLLER_RPA_LIST_ENABLE) + temp_addr_type = addr_type; + memcpy(temp_bda, bda, BD_ADDR_LEN); +#endif // (!CONTROLLER_RPA_LIST_ENABLE) + + /* map address to security record */ + match = btm_identity_addr_to_random_pseudo(bda, &addr_type, FALSE); + + // BTM_TRACE_ERROR("btm_ble_process_adv_pkt:bda= %0x:%0x:%0x:%0x:%0x:%0x\n", + // bda[0],bda[1],bda[2],bda[3],bda[4],bda[5]); + /* always do RRA resolution on host */ + if (!match && BTM_BLE_IS_RESOLVE_BDA(bda)) { + btm_ble_resolve_random_addr(bda, btm_ble_resolve_random_addr_on_adv, p_data); + } else +#endif + btm_ble_process_adv_pkt_cont(bda, addr_type, evt_type, p); +#if (defined BLE_PRIVACY_SPT && BLE_PRIVACY_SPT == TRUE && (!CONTROLLER_RPA_LIST_ENABLE)) + //save current adv addr information if p_dev_rec!= NULL + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev (bda); + if(p_dev_rec) { + p_dev_rec->ble.current_addr_type = temp_addr_type; + memcpy(p_dev_rec->ble.current_addr, temp_bda, BD_ADDR_LEN); + p_dev_rec->ble.current_addr_valid = true; + } +#endif + STREAM_TO_UINT8(data_len, p); + + /* Advance to the next event data_len + rssi byte */ + p += data_len + 1; + } +} + +/******************************************************************************* +** +** Function btm_ble_process_last_adv_pkt +** +** Description This function is called to report last adv packet +** +** Parameters +** +** Returns void +** +*******************************************************************************/ + +static void btm_ble_process_last_adv_pkt(void) +{ + UINT8 result = 0; + UINT8 null_bda[6] = {0}; + tBTM_INQUIRY_VAR_ST *p_inq = &btm_cb.btm_inq_vars; + tBTM_INQ_RESULTS_CB *p_inq_results_cb = p_inq->p_inq_results_cb; + tBTM_INQ_RESULTS_CB *p_obs_results_cb = btm_cb.ble_ctr_cb.p_obs_results_cb; + tBTM_INQ_RESULTS_CB *p_scan_results_cb = btm_cb.ble_ctr_cb.p_scan_results_cb; + tBTM_BLE_INQ_CB *p_le_inq_cb = &btm_cb.ble_ctr_cb.inq_var; + tINQ_DB_ENT *p_i = btm_inq_db_find (p_le_inq_cb->adv_addr); + + if(memcmp(null_bda, p_le_inq_cb->adv_addr, BD_ADDR_LEN) == 0) { + return; + } + + if(p_i == NULL) { + BTM_TRACE_DEBUG("no last adv"); + return; + } + + if ((result = btm_ble_is_discoverable(p_le_inq_cb->adv_addr, p_i->inq_info.results.ble_evt_type, NULL)) == 0) { + BTM_TRACE_WARNING("%s device is no longer discoverable so discarding advertising packet pkt", + __func__); + return; + } + /* background connection in selective connection mode */ + if (btm_cb.ble_ctr_cb.bg_conn_type == BTM_BLE_CONN_SELECTIVE) { + //do nothing + } else { + if (p_inq_results_cb && (result & BTM_BLE_INQ_RESULT)) { + (p_inq_results_cb)((tBTM_INQ_RESULTS *) &p_i->inq_info.results, p_le_inq_cb->adv_data_cache); + p_le_inq_cb->adv_len = 0; + memset(p_le_inq_cb->adv_addr, 0, BD_ADDR_LEN); + p_i->inq_info.results.adv_data_len = 0; + p_i->inq_info.results.scan_rsp_len = 0; + } + if (p_obs_results_cb && (result & BTM_BLE_OBS_RESULT)) { + (p_obs_results_cb)((tBTM_INQ_RESULTS *) &p_i->inq_info.results, p_le_inq_cb->adv_data_cache); + p_le_inq_cb->adv_len = 0; + memset(p_le_inq_cb->adv_addr, 0, BD_ADDR_LEN); + p_i->inq_info.results.adv_data_len = 0; + p_i->inq_info.results.scan_rsp_len = 0; + } + if (p_scan_results_cb && (result & BTM_BLE_DISCO_RESULT)) { + (p_scan_results_cb)((tBTM_INQ_RESULTS *) &p_i->inq_info.results, p_le_inq_cb->adv_data_cache); + p_le_inq_cb->adv_len = 0; + memset(p_le_inq_cb->adv_addr, 0, BD_ADDR_LEN); + p_i->inq_info.results.adv_data_len = 0; + p_i->inq_info.results.scan_rsp_len = 0; + } + } +} + +/******************************************************************************* +** +** Function btm_ble_process_adv_pkt_cont +** +** Description This function is called after random address resolution is +** done, and proceed to process adv packet. +** +** Parameters +** +** Returns void +** +*******************************************************************************/ +static void btm_ble_process_adv_pkt_cont(BD_ADDR bda, UINT8 addr_type, UINT8 evt_type, UINT8 *p) +{ + + tINQ_DB_ENT *p_i; + tBTM_INQUIRY_VAR_ST *p_inq = &btm_cb.btm_inq_vars; + tBTM_INQ_RESULTS_CB *p_inq_results_cb = p_inq->p_inq_results_cb; + tBTM_INQ_RESULTS_CB *p_obs_results_cb = btm_cb.ble_ctr_cb.p_obs_results_cb; + tBTM_INQ_RESULTS_CB *p_scan_results_cb = btm_cb.ble_ctr_cb.p_scan_results_cb; + tBTM_BLE_INQ_CB *p_le_inq_cb = &btm_cb.ble_ctr_cb.inq_var; + BOOLEAN update = TRUE; + UINT8 result = 0; + + /* Event_Type: + 0x00 Connectable undirected advertising (ADV_IND). + 0x01 Connectable directed advertising (ADV_DIRECT_IND) + 0x02 Scannable undirected advertising (ADV_SCAN_IND) + 0x03 Non connectable undirected advertising (ADV_NONCONN_IND) + 0x04 Scan Response (SCAN_RSP) + 0x05-0xFF Reserved for future use + */ + // The adv packet without scan response is allowed to report to higher layer + /* + Bluedroid will put the advertising packet and scan response into a packet and send it to the higher layer. + If two advertising packets are not with the same address, or can't be combined into a packet, then the first advertising + packet will be discarded. So we added the following judgment: + 1. For different addresses, send the last advertising packet to higher layer + 2. For same address and same advertising type (not scan response), send the last advertising packet to higher layer + 3. For same address and scan response, do nothing + */ + int same_addr = memcmp(bda, p_le_inq_cb->adv_addr, BD_ADDR_LEN); + if (same_addr != 0 || (same_addr == 0 && evt_type != BTM_BLE_SCAN_RSP_EVT)) { + btm_ble_process_last_adv_pkt(); + } + + + p_i = btm_inq_db_find (bda); + + /* Check if this address has already been processed for this inquiry */ + if (btm_inq_find_bdaddr(bda)) { + /* never been report as an LE device */ + if (p_i && + (!(p_i->inq_info.results.device_type & BT_DEVICE_TYPE_BLE) || + /* scan repsonse to be updated */ + (!p_i->scan_rsp))) { + update = TRUE; + } else if (BTM_BLE_IS_DISCO_ACTIVE(btm_cb.ble_ctr_cb.scan_activity)) { + update = FALSE; + } else { + /* if yes, skip it */ + return; /* assumption: one result per event */ + } + } + /* If existing entry, use that, else get a new one (possibly reusing the oldest) */ + if (p_i == NULL) { + if ((p_i = btm_inq_db_new (bda)) != NULL) { + p_inq->inq_cmpl_info.num_resp++; + } else { + return; + } + } else if (p_i->inq_count != p_inq->inq_counter) { /* first time seen in this inquiry */ + p_inq->inq_cmpl_info.num_resp++; + } + /* update the LE device information in inquiry database */ + if (!btm_ble_update_inq_result(bda, p_i, addr_type, evt_type, p)) { + return; + } + + if ((result = btm_ble_is_discoverable(bda, evt_type, p)) == 0) { + BTM_TRACE_WARNING("%s device is no longer discoverable so discarding advertising packet pkt", + __func__); + return; + } + if (!update) { + result &= ~BTM_BLE_INQ_RESULT; + } + /* If the number of responses found and limited, issue a cancel inquiry */ + if (p_inq->inqparms.max_resps && + p_inq->inq_cmpl_info.num_resp == p_inq->inqparms.max_resps) { + /* new device */ + if (p_i == NULL || + /* assume a DUMO device, BR/EDR inquiry is always active */ + (p_i && + (p_i->inq_info.results.device_type & BT_DEVICE_TYPE_BLE) == BT_DEVICE_TYPE_BLE && + p_i->scan_rsp)) { + BTM_TRACE_WARNING("INQ RES: Extra Response Received...cancelling inquiry.."); + + /* if is non-periodic inquiry active, cancel now */ + if ((p_inq->inq_active & BTM_BR_INQ_ACTIVE_MASK) != 0 && + (p_inq->inq_active & BTM_PERIODIC_INQUIRY_ACTIVE) == 0) { + btsnd_hcic_inq_cancel(); + } + + btm_ble_stop_inquiry(); + + btm_acl_update_busy_level (BTM_BLI_INQ_DONE_EVT); + } + } + /* background connection in selective connection mode */ + if (btm_cb.ble_ctr_cb.bg_conn_type == BTM_BLE_CONN_SELECTIVE) { + if (result & BTM_BLE_SEL_CONN_RESULT) { + btm_send_sel_conn_callback(bda, evt_type, p, addr_type); + } else { + BTM_TRACE_DEBUG("None LE device, can not initiate selective connection\n"); + } + } else { + if (p_inq_results_cb && (result & BTM_BLE_INQ_RESULT)) { + (p_inq_results_cb)((tBTM_INQ_RESULTS *) &p_i->inq_info.results, p_le_inq_cb->adv_data_cache); + p_le_inq_cb->adv_len = 0; + memset(p_le_inq_cb->adv_addr, 0, BD_ADDR_LEN); + p_i->inq_info.results.adv_data_len = 0; + p_i->inq_info.results.scan_rsp_len = 0; + } + if (p_obs_results_cb && (result & BTM_BLE_OBS_RESULT)) { + (p_obs_results_cb)((tBTM_INQ_RESULTS *) &p_i->inq_info.results, p_le_inq_cb->adv_data_cache); + p_le_inq_cb->adv_len = 0; + memset(p_le_inq_cb->adv_addr, 0, BD_ADDR_LEN); + p_i->inq_info.results.adv_data_len = 0; + p_i->inq_info.results.scan_rsp_len = 0; + } + if (p_scan_results_cb && (result & BTM_BLE_DISCO_RESULT)) { + (p_scan_results_cb)((tBTM_INQ_RESULTS *) &p_i->inq_info.results, p_le_inq_cb->adv_data_cache); + p_le_inq_cb->adv_len = 0; + memset(p_le_inq_cb->adv_addr, 0, BD_ADDR_LEN); + p_i->inq_info.results.adv_data_len = 0; + p_i->inq_info.results.scan_rsp_len = 0; + } + } +} + +void btm_ble_process_adv_discard_evt(UINT8 *p) +{ +#if (BLE_ADV_REPORT_FLOW_CONTROL == TRUE) + uint32_t num_dis = 0; + STREAM_TO_UINT32 (num_dis, p); + tBTM_INQ_DIS_CB *p_obs_discard_cb = btm_cb.ble_ctr_cb.p_obs_discard_cb; + if(p_obs_discard_cb) { + (p_obs_discard_cb)(num_dis); + } +#endif +} + +void btm_ble_process_direct_adv_pkt(UINT8 *p) +{ + // TODO +} + +/******************************************************************************* +** +** Function btm_ble_start_scan +** +** Description Start the BLE scan. +** +** Returns void +** +*******************************************************************************/ +tBTM_STATUS btm_ble_start_scan(void) +{ + tBTM_BLE_INQ_CB *p_inq = &btm_cb.ble_ctr_cb.inq_var; + tBTM_STATUS status = BTM_CMD_STARTED; + + osi_mutex_lock(&scan_enable_lock, OSI_MUTEX_MAX_TIMEOUT); + + if(p_inq->scan_duplicate_filter > BTM_BLE_DUPLICATE_MAX) { + p_inq->scan_duplicate_filter = BTM_BLE_DUPLICATE_DISABLE; + } + /* start scan, disable duplicate filtering */ + if (!btsnd_hcic_ble_set_scan_enable (BTM_BLE_SCAN_ENABLE, p_inq->scan_duplicate_filter)) { + status = BTM_NO_RESOURCES; + } else { + osi_sem_take(&scan_enable_sem, OSI_SEM_MAX_TIMEOUT); + if(scan_enable_status != BTM_SUCCESS) { + status = BTM_NO_RESOURCES; + } + btm_cb.ble_ctr_cb.inq_var.state |= BTM_BLE_SCANNING; + if (p_inq->scan_type == BTM_BLE_SCAN_MODE_ACTI) { + btm_ble_set_topology_mask(BTM_BLE_STATE_ACTIVE_SCAN_BIT); + } else { + btm_ble_set_topology_mask(BTM_BLE_STATE_PASSIVE_SCAN_BIT); + } + } + osi_mutex_unlock(&scan_enable_lock); + return status; +} + +/******************************************************************************* +** +** Function btm_ble_stop_scan +** +** Description Stop the BLE scan. +** +** Returns void +** +*******************************************************************************/ +void btm_ble_stop_scan(void) +{ + BTM_TRACE_EVENT ("btm_ble_stop_scan "); + + /* Clear the inquiry callback if set */ + btm_cb.ble_ctr_cb.inq_var.scan_type = BTM_BLE_SCAN_MODE_NONE; + btm_cb.ble_ctr_cb.inq_var.state &= ~BTM_BLE_SCANNING; + /* stop discovery now */ + btsnd_hcic_ble_set_scan_enable (BTM_BLE_SCAN_DISABLE, BTM_BLE_DUPLICATE_ENABLE); + + btm_update_scanner_filter_policy(SP_ADV_ALL); + + btm_cb.ble_ctr_cb.wl_state &= ~BTM_BLE_WL_SCAN; +} + +/******************************************************************************* +** +** Function btm_ble_stop_inquiry +** +** Description Stop the BLE Inquiry. +** +** Returns void +** +*******************************************************************************/ +void btm_ble_stop_inquiry(void) +{ + tBTM_INQUIRY_VAR_ST *p_inq = &btm_cb.btm_inq_vars; + tBTM_BLE_CB *p_ble_cb = &btm_cb.ble_ctr_cb; + + btu_stop_timer (&p_ble_cb->inq_var.inq_timer_ent); + + p_ble_cb->scan_activity &= ~BTM_BLE_INQUIRY_MASK; + + /* If no more scan activity, stop LE scan now */ + if (!BTM_BLE_IS_SCAN_ACTIVE(p_ble_cb->scan_activity)) { + btm_ble_stop_scan(); + } else if ((p_ble_cb->inq_var.scan_interval != BTM_BLE_LOW_LATENCY_SCAN_INT) || + (p_ble_cb->inq_var.scan_window != BTM_BLE_LOW_LATENCY_SCAN_WIN)) { + BTM_TRACE_DEBUG("%s: setting default params for ongoing observe", __FUNCTION__); + btm_ble_stop_scan(); + btm_ble_start_scan(); + } + + /* If we have a callback registered for inquiry complete, call it */ + BTM_TRACE_DEBUG ("BTM Inq Compl Callback: status 0x%02x, num results %d", + p_inq->inq_cmpl_info.status, p_inq->inq_cmpl_info.num_resp); + + btm_process_inq_complete(HCI_SUCCESS, (UINT8)(p_inq->inqparms.mode & BTM_BLE_INQUIRY_MASK)); +} + +/******************************************************************************* +** +** Function btm_ble_stop_observe +** +** Description Stop the BLE Observe. +** +** Returns void +** +*******************************************************************************/ +static void btm_ble_stop_observe(void) +{ + tBTM_BLE_CB *p_ble_cb = & btm_cb.ble_ctr_cb; + tBTM_CMPL_CB *p_obs_cb = p_ble_cb->p_obs_cmpl_cb; + + btu_stop_timer (&p_ble_cb->obs_timer_ent); + + p_ble_cb->scan_activity &= ~BTM_LE_OBSERVE_ACTIVE; + + p_ble_cb->p_obs_results_cb = NULL; + p_ble_cb->p_obs_cmpl_cb = NULL; + + if (!BTM_BLE_IS_SCAN_ACTIVE(p_ble_cb->scan_activity)) { + btm_ble_stop_scan(); + } + + if (p_obs_cb) { + (p_obs_cb)((tBTM_INQUIRY_CMPL *) &btm_cb.btm_inq_vars.inq_cmpl_info); + } +} + +/******************************************************************************* +** +** Function btm_ble_stop_observe +** +** Description Stop the BLE Observe. +** +** Returns void +** +*******************************************************************************/ +static void btm_ble_stop_discover(void) +{ + tBTM_BLE_CB *p_ble_cb = & btm_cb.ble_ctr_cb; + tBTM_CMPL_CB *p_scan_cb = p_ble_cb->p_scan_cmpl_cb; + btu_stop_timer (&p_ble_cb->scan_timer_ent); + + osi_mutex_lock(&scan_enable_lock, OSI_MUTEX_MAX_TIMEOUT); + p_ble_cb->scan_activity &= ~BTM_LE_DISCOVER_ACTIVE; + + p_ble_cb->p_scan_results_cb = NULL; + p_ble_cb->p_scan_cmpl_cb = NULL; + + if (!BTM_BLE_IS_SCAN_ACTIVE(p_ble_cb->scan_activity)) { + /* Clear the inquiry callback if set */ + btm_cb.ble_ctr_cb.inq_var.scan_type = BTM_BLE_SCAN_MODE_NONE; + btm_cb.ble_ctr_cb.inq_var.state &= ~BTM_BLE_SCANNING; + /* stop discovery now */ + if(btsnd_hcic_ble_set_scan_enable (BTM_BLE_SCAN_DISABLE, BTM_BLE_DUPLICATE_ENABLE)) { + osi_sem_take(&scan_enable_sem, OSI_SEM_MAX_TIMEOUT); + } + } + + if (p_scan_cb) { + (p_scan_cb)((tBTM_INQUIRY_CMPL *) &btm_cb.btm_inq_vars.inq_cmpl_info); + } + osi_mutex_unlock(&scan_enable_lock); +} + +/******************************************************************************* +** +** Function btm_ble_adv_states_operation +** +** Description Set or clear adv states in topology mask +** +** Returns operation status. TRUE if sucessful, FALSE otherwise. +** +*******************************************************************************/ +typedef BOOLEAN (BTM_TOPOLOGY_FUNC_PTR)(tBTM_BLE_STATE_MASK); +static BOOLEAN btm_ble_adv_states_operation(BTM_TOPOLOGY_FUNC_PTR *p_handler, UINT8 adv_evt) +{ + BOOLEAN rt = FALSE; + + switch (adv_evt) { + case BTM_BLE_CONNECT_EVT: + rt = (*p_handler)(BTM_BLE_STATE_CONN_ADV_BIT); + break; + + case BTM_BLE_NON_CONNECT_EVT: + rt = (*p_handler) (BTM_BLE_STATE_NON_CONN_ADV_BIT); + break; + case BTM_BLE_CONNECT_DIR_EVT: + rt = (*p_handler) (BTM_BLE_STATE_HI_DUTY_DIR_ADV_BIT); + break; + + case BTM_BLE_DISCOVER_EVT: + rt = (*p_handler) (BTM_BLE_STATE_SCAN_ADV_BIT); + break; + + case BTM_BLE_CONNECT_LO_DUTY_DIR_EVT: + rt = (*p_handler) (BTM_BLE_STATE_LO_DUTY_DIR_ADV_BIT); + break; + + default: + BTM_TRACE_ERROR("unknown adv event : %d", adv_evt); + break; + } + + return rt; +} + + +/******************************************************************************* +** +** Function btm_ble_start_adv +** +** Description start the BLE advertising. +** +** Returns void +** +*******************************************************************************/ +tBTM_STATUS btm_ble_start_adv(void) +{ + tBTM_BLE_INQ_CB *p_cb = &btm_cb.ble_ctr_cb.inq_var; + tBTM_STATUS rt = BTM_NO_RESOURCES; + BTM_TRACE_EVENT ("btm_ble_start_adv\n"); + + + if (!btm_ble_adv_states_operation (btm_ble_topology_check, p_cb->evt_type)) { + return BTM_WRONG_MODE; + } + + osi_mutex_lock(&adv_enable_lock, OSI_MUTEX_MAX_TIMEOUT); + +#if (defined BLE_PRIVACY_SPT && BLE_PRIVACY_SPT == TRUE) + /* To relax resolving list, always have resolving list enabled, unless directed adv */ + if (p_cb->evt_type != BTM_BLE_CONNECT_LO_DUTY_DIR_EVT && + p_cb->evt_type != BTM_BLE_CONNECT_DIR_EVT) + /* enable resolving list is desired */ + { + //btm_ble_enable_resolving_list_for_platform(BTM_BLE_RL_ADV); + } +#endif + if (p_cb->afp != AP_SCAN_CONN_ALL) { + //find the device in the btm dev buffer and write it to the controller white list + btm_execute_wl_dev_operation(); + btm_cb.ble_ctr_cb.wl_state |= BTM_BLE_WL_ADV; + } + /* The complete event comes up immediately after the 'btsnd_hcic_ble_set_adv_enable' being called in dual core, + this causes the 'adv_mode' and 'state' not be set yet, so we set the state first */ + tBTM_BLE_GAP_STATE temp_state = p_cb->state; + UINT8 adv_mode = p_cb->adv_mode; + p_cb->adv_mode = BTM_BLE_ADV_ENABLE; + p_cb->state |= BTM_BLE_ADVERTISING; + btm_ble_adv_states_operation(btm_ble_set_topology_mask, p_cb->evt_type); + if (btsnd_hcic_ble_set_adv_enable (BTM_BLE_ADV_ENABLE)) { + osi_sem_take(&adv_enable_sem, OSI_SEM_MAX_TIMEOUT); + rt = adv_enable_status; + BTM_TRACE_EVENT ("BTM_SUCCESS\n"); + } else { + p_cb->adv_mode = BTM_BLE_ADV_DISABLE; + p_cb->state = temp_state; + p_cb->adv_mode = adv_mode; + btm_ble_adv_states_operation(btm_ble_clear_topology_mask, p_cb->evt_type); + btm_cb.ble_ctr_cb.wl_state &= ~BTM_BLE_WL_ADV; + } + + if(adv_enable_status != HCI_SUCCESS) { + p_cb->adv_mode = adv_mode; + } + osi_mutex_unlock(&adv_enable_lock); + return rt; +} + +/******************************************************************************* +** +** Function btm_ble_stop_adv +** +** Description Stop the BLE advertising. +** +** Returns void +** +*******************************************************************************/ +tBTM_STATUS btm_ble_stop_adv(void) +{ + tBTM_BLE_INQ_CB *p_cb = &btm_cb.ble_ctr_cb.inq_var; + tBTM_STATUS rt = BTM_SUCCESS; + if (p_cb) { + osi_mutex_lock(&adv_enable_lock, OSI_MUTEX_MAX_TIMEOUT); + UINT8 temp_adv_mode = p_cb->adv_mode; + BOOLEAN temp_fast_adv_on = p_cb->fast_adv_on; + tBTM_BLE_GAP_STATE temp_state = p_cb->state; + tBTM_BLE_WL_STATE temp_wl_state = btm_cb.ble_ctr_cb.wl_state; + tBTM_BLE_STATE_MASK temp_mask = btm_ble_get_topology_mask (); + + p_cb->fast_adv_on = FALSE; + p_cb->adv_mode = BTM_BLE_ADV_DISABLE; + p_cb->state &= ~BTM_BLE_ADVERTISING; + btm_cb.ble_ctr_cb.wl_state &= ~BTM_BLE_WL_ADV; + + /* clear all adv states */ + btm_ble_clear_topology_mask (BTM_BLE_STATE_ALL_ADV_MASK); + + if (btsnd_hcic_ble_set_adv_enable (BTM_BLE_ADV_DISABLE)) { + osi_sem_take(&adv_enable_sem, OSI_SEM_MAX_TIMEOUT); + rt = adv_enable_status; + } else { + // reset state + p_cb->fast_adv_on = temp_fast_adv_on; + p_cb->adv_mode = temp_adv_mode; + p_cb->state = temp_state; + btm_cb.ble_ctr_cb.wl_state = temp_wl_state; + btm_ble_set_topology_mask (temp_mask); + + rt = BTM_NO_RESOURCES; + } + if(adv_enable_status != HCI_SUCCESS) { + p_cb->adv_mode = temp_adv_mode; + } + osi_mutex_unlock(&adv_enable_lock); + } + return rt; +} + +tBTM_STATUS btm_ble_set_random_addr(BD_ADDR random_bda) +{ + tBTM_STATUS rt = BTM_SUCCESS; + + osi_mutex_lock(&adv_enable_lock, OSI_MUTEX_MAX_TIMEOUT); + osi_mutex_lock(&scan_enable_lock, OSI_MUTEX_MAX_TIMEOUT); + + if (btm_cb.ble_ctr_cb.inq_var.adv_mode == BTM_BLE_ADV_ENABLE) { + if (btsnd_hcic_ble_set_adv_enable (BTM_BLE_ADV_DISABLE)) { + osi_sem_take(&adv_enable_sem, OSI_SEM_MAX_TIMEOUT); + rt = adv_enable_status; + } else { + rt = BTM_BAD_VALUE_RET; + } + } + + if (BTM_BLE_IS_DISCO_ACTIVE(btm_cb.ble_ctr_cb.scan_activity)) { + if (btsnd_hcic_ble_set_scan_enable (BTM_BLE_SCAN_DISABLE, BTM_BLE_SCAN_DUPLICATE_DISABLE)) { + osi_sem_take(&scan_enable_sem, OSI_SEM_MAX_TIMEOUT); + rt = scan_enable_status; + } else { + rt = BTM_BAD_VALUE_RET; + } + } + + if (rt == BTM_SUCCESS) { + btsnd_hcic_ble_set_random_addr(random_bda); + } + + if (btm_cb.ble_ctr_cb.inq_var.adv_mode == BTM_BLE_ADV_ENABLE) { + if (btsnd_hcic_ble_set_adv_enable (BTM_BLE_ADV_ENABLE)) { + osi_sem_take(&adv_enable_sem, OSI_SEM_MAX_TIMEOUT); + rt = adv_enable_status; + } else { + rt = BTM_BAD_VALUE_RET; + } + } + + if (BTM_BLE_IS_DISCO_ACTIVE(btm_cb.ble_ctr_cb.scan_activity)) { + if (btsnd_hcic_ble_set_scan_enable (BTM_BLE_SCAN_ENABLE, btm_cb.ble_ctr_cb.inq_var.scan_duplicate_filter)) { + osi_sem_take(&scan_enable_sem, OSI_SEM_MAX_TIMEOUT); + rt = scan_enable_status; + } else { + rt = BTM_BAD_VALUE_RET; + } + } + + osi_mutex_unlock(&adv_enable_lock); + osi_mutex_unlock(&scan_enable_lock); + + return rt; +} + + +/******************************************************************************* +** +** Function btm_ble_start_slow_adv +** +** Description Restart adv with slow adv interval +** +** Returns void +** +*******************************************************************************/ +static void btm_ble_start_slow_adv (void) +{ + tBTM_BLE_INQ_CB *p_cb = &btm_cb.ble_ctr_cb.inq_var; + + if (p_cb->adv_mode == BTM_BLE_ADV_ENABLE) { + tBTM_LE_RANDOM_CB *p_addr_cb = &btm_cb.ble_ctr_cb.addr_mgnt_cb; + BD_ADDR p_addr_ptr = {0}; + tBLE_ADDR_TYPE init_addr_type = BLE_ADDR_PUBLIC; + tBLE_ADDR_TYPE own_addr_type = p_addr_cb->own_addr_type; + + btm_ble_stop_adv(); + + p_cb->evt_type = btm_set_conn_mode_adv_init_addr(p_cb, p_addr_ptr, &init_addr_type, + &own_addr_type); + + /* slow adv mode never goes into directed adv */ + btsnd_hcic_ble_write_adv_params (BTM_BLE_GAP_ADV_SLOW_INT, BTM_BLE_GAP_ADV_SLOW_INT, + p_cb->evt_type, own_addr_type, + init_addr_type, p_addr_ptr, + p_cb->adv_chnl_map, p_cb->afp); + + btm_ble_start_adv(); + } +} +/******************************************************************************* +** +** Function btm_ble_timeout +** +** Description Called when BTM BLE inquiry timer expires +** +** Returns void +** +*******************************************************************************/ +void btm_ble_timeout(TIMER_LIST_ENT *p_tle) +{ + BTM_TRACE_EVENT ("btm_ble_timeout"); + + switch (p_tle->event) { + case BTU_TTYPE_BLE_OBSERVE: + btm_ble_stop_observe(); + break; + case BTU_TTYPE_BLE_SCAN: + btm_ble_stop_discover(); + break; + case BTU_TTYPE_BLE_INQUIRY: + btm_ble_stop_inquiry(); + break; + + case BTU_TTYPE_BLE_GAP_LIM_DISC: + /* lim_timeout expiried, limited discovery should exit now */ + btm_cb.btm_inq_vars.discoverable_mode &= ~BTM_BLE_LIMITED_DISCOVERABLE; + btm_ble_set_adv_flag(btm_cb.btm_inq_vars.connectable_mode, btm_cb.btm_inq_vars.discoverable_mode); + break; + + case BTU_TTYPE_BLE_RANDOM_ADDR: + if (btm_cb.ble_ctr_cb.addr_mgnt_cb.own_addr_type == BLE_ADDR_RANDOM) { + if (NULL == (void *)(p_tle->param)) { +#if (CONTROLLER_RPA_LIST_ENABLE == FALSE) + /* refresh the random addr */ + btm_gen_resolvable_private_addr((void *)btm_gen_resolve_paddr_low); +#endif + } else { + if (BTM_BleMaxMultiAdvInstanceCount() > 0) { + btm_ble_multi_adv_configure_rpa((tBTM_BLE_MULTI_ADV_INST *)p_tle->param); + } + } + } + break; + + case BTU_TTYPE_BLE_GAP_FAST_ADV: + /* fast adv is completed, fall back to slow adv interval */ + btm_ble_start_slow_adv(); + break; + + default: + break; + + } +} + + +/******************************************************************************* +** +** Function btm_ble_read_remote_features_complete +** +** Description This function is called when the command complete message +** is received from the HCI for the read LE remote feature supported +** complete event. +** +** Returns void +** +*******************************************************************************/ +void btm_ble_read_remote_features_complete(UINT8 *p) +{ + tACL_CONN *p_acl_cb = NULL; + UINT16 handle; + UINT8 status; + + BTM_TRACE_EVENT ("btm_ble_read_remote_features_complete "); + + STREAM_TO_UINT8(status, p); + + // if LE read remote feature failed for HCI_ERR_CONN_FAILED_ESTABLISHMENT, + // expect disconnect complete to be received + if (status != HCI_ERR_CONN_FAILED_ESTABLISHMENT) { + STREAM_TO_UINT16 (handle, p); + + /* Look up the connection by handle and copy features */ + p_acl_cb = btm_handle_to_acl(handle); + if (p_acl_cb) { + { + STREAM_TO_ARRAY(p_acl_cb->peer_le_features, p, BD_FEATURES_LEN); +#if BLE_INCLUDED == TRUE + /* In the original Bluedroid version, slave need to send LL_VERSION_IND(call btsnd_hcic_rmt_ver_req) + * to remote device if it has not received ll_version_ind. + * Delete it to resolve Android 7.0 incompatible problem. But it may cause that slave host + * can't get remote device's version.*/ + if (p_acl_cb->link_role == HCI_ROLE_MASTER){ + btsnd_hcic_rmt_ver_req (p_acl_cb->hci_handle); + } + else{ + uint16_t data_length = controller_get_interface()->get_ble_default_data_packet_length(); + uint16_t data_txtime = controller_get_interface()->get_ble_default_data_packet_txtime(); + if (p_acl_cb->transport == BT_TRANSPORT_LE) { + if (HCI_LE_DATA_LEN_EXT_SUPPORTED(p_acl_cb->peer_le_features) && + (p_acl_cb->data_length_params.tx_len != data_length)) { + p_acl_cb->data_len_updating = true; + btsnd_hcic_ble_set_data_length(p_acl_cb->hci_handle, data_length, data_txtime); + } + l2cble_notify_le_connection (p_acl_cb->remote_addr); + } + } +#endif + } + } + } + +} + +/******************************************************************************* +** +** Function btm_ble_write_adv_enable_complete +** +** Description This function process the write adv enable command complete. +** +** Returns void +** +*******************************************************************************/ +void btm_ble_write_adv_enable_complete(UINT8 *p) +{ + /* if write adv enable/disbale not succeed */ + if (*p != HCI_SUCCESS) { + BTM_TRACE_ERROR("%s failed", __func__); + } +} + +/******************************************************************************* +** +** Function btm_ble_dir_adv_tout +** +** Description when directed adv time out +** +** Returns void +** +*******************************************************************************/ +void btm_ble_dir_adv_tout(void) +{ + btm_cb.ble_ctr_cb.inq_var.adv_mode = BTM_BLE_ADV_DISABLE; + + /* make device fall back into undirected adv mode by default */ + btm_cb.ble_ctr_cb.inq_var.directed_conn = FALSE; +} + +/******************************************************************************* +** +** Function btm_ble_set_topology_mask +** +** Description set BLE topology mask +** +** Returns TRUE is request is allowed, FALSE otherwise. +** +*******************************************************************************/ +BOOLEAN btm_ble_set_topology_mask(tBTM_BLE_STATE_MASK request_state_mask) +{ + request_state_mask &= BTM_BLE_STATE_ALL_MASK; + btm_cb.ble_ctr_cb.cur_states |= (request_state_mask & BTM_BLE_STATE_ALL_MASK); + return TRUE; +} + +/******************************************************************************* +** +** Function btm_ble_clear_topology_mask +** +** Description Clear BLE topology bit mask +** +** Returns TRUE is request is allowed, FALSE otherwise. +** +*******************************************************************************/ +BOOLEAN btm_ble_clear_topology_mask (tBTM_BLE_STATE_MASK request_state_mask) +{ + request_state_mask &= BTM_BLE_STATE_ALL_MASK; + btm_cb.ble_ctr_cb.cur_states &= ~request_state_mask; + return TRUE; +} + +/******************************************************************************* +** +** Function btm_ble_get_topology_mask +** +** Description Get BLE topology bit mask +** +** Returns state mask. +** +*******************************************************************************/ +tBTM_BLE_STATE_MASK btm_ble_get_topology_mask (void) +{ + return btm_cb.ble_ctr_cb.cur_states; +} + +/******************************************************************************* +** +** Function btm_ble_update_link_topology_mask +** +** Description This function update the link topology mask +** +** Returns void +** +*******************************************************************************/ +void btm_ble_update_link_topology_mask(UINT8 link_role, BOOLEAN increase) +{ + btm_ble_clear_topology_mask (BTM_BLE_STATE_ALL_CONN_MASK); + + if (increase) { + btm_cb.ble_ctr_cb.link_count[link_role]++; + } else if (btm_cb.ble_ctr_cb.link_count[link_role] > 0) { + btm_cb.ble_ctr_cb.link_count[link_role]--; + } + + if (btm_cb.ble_ctr_cb.link_count[HCI_ROLE_MASTER]) { + btm_ble_set_topology_mask (BTM_BLE_STATE_MASTER_BIT); + } + + if (btm_cb.ble_ctr_cb.link_count[HCI_ROLE_SLAVE]) { + btm_ble_set_topology_mask(BTM_BLE_STATE_SLAVE_BIT); + } + + if (link_role == HCI_ROLE_SLAVE && increase) { + btm_cb.ble_ctr_cb.inq_var.adv_mode = BTM_BLE_ADV_DISABLE; + /* make device fall back into undirected adv mode by default */ + btm_cb.ble_ctr_cb.inq_var.directed_conn = BTM_BLE_CONNECT_EVT; + /* clear all adv states */ + btm_ble_clear_topology_mask(BTM_BLE_STATE_ALL_ADV_MASK); + } +} + +/******************************************************************************* +** +** Function btm_ble_update_mode_operation +** +** Description This function update the GAP role operation when a link status +** is updated. +** +** Returns void +** +*******************************************************************************/ +BOOLEAN btm_ble_update_mode_operation(UINT8 link_role, BD_ADDR bd_addr, UINT8 status) +{ + BOOLEAN bg_con = FALSE; + if (status == HCI_ERR_DIRECTED_ADVERTISING_TIMEOUT) { + btm_cb.ble_ctr_cb.inq_var.adv_mode = BTM_BLE_ADV_DISABLE; + /* make device fall back into undirected adv mode by default */ + btm_cb.ble_ctr_cb.inq_var.directed_conn = BTM_BLE_CONNECT_EVT; + /* clear all adv states */ + btm_ble_clear_topology_mask (BTM_BLE_STATE_ALL_ADV_MASK); + } + + if (btm_cb.ble_ctr_cb.inq_var.connectable_mode == BTM_BLE_CONNECTABLE) { + btm_ble_set_connectability(btm_cb.btm_inq_vars.connectable_mode | + btm_cb.ble_ctr_cb.inq_var.connectable_mode); + } + + /* when no connection is attempted, and controller is not rejecting last request + due to resource limitation, start next direct connection or background connection + now in order */ + if (btm_ble_get_conn_st() == BLE_CONN_IDLE && status != HCI_ERR_HOST_REJECT_RESOURCES && + !btm_send_pending_direct_conn()) { + bg_con = btm_ble_resume_bg_conn(); + } + + return bg_con; +} + +/******************************************************************************* +** +** Function btm_ble_init +** +** Description Initialize the control block variable values. +** +** Returns void +** +*******************************************************************************/ +void btm_ble_init (void) +{ + BTM_TRACE_DEBUG("%s", __func__); + +#if BTM_DYNAMIC_MEMORY == TRUE + cmn_ble_gap_vsc_cb_ptr = (tBTM_BLE_VSC_CB *)osi_malloc(sizeof(tBTM_BLE_VSC_CB)); + if (cmn_ble_gap_vsc_cb_ptr == NULL) { + BTM_TRACE_ERROR("%s malloc failed", __func__); + return; + } +#endif + + tBTM_BLE_CB *p_cb = &btm_cb.ble_ctr_cb; + + btu_free_timer(&p_cb->obs_timer_ent); + btu_free_timer(&p_cb->scan_timer_ent); + btu_free_timer(&p_cb->inq_var.fast_adv_timer); + memset(p_cb, 0, sizeof(tBTM_BLE_CB)); + memset(&(btm_cb.cmn_ble_vsc_cb), 0 , sizeof(tBTM_BLE_VSC_CB)); + btm_cb.cmn_ble_vsc_cb.values_read = FALSE; + p_cb->cur_states = 0; + + p_cb->conn_pending_q = fixed_queue_new(QUEUE_SIZE_MAX); + + p_cb->inq_var.adv_mode = BTM_BLE_ADV_DISABLE; + p_cb->inq_var.scan_type = BTM_BLE_SCAN_MODE_NONE; + p_cb->inq_var.adv_chnl_map = BTM_BLE_DEFAULT_ADV_CHNL_MAP; + p_cb->inq_var.afp = BTM_BLE_DEFAULT_AFP; + p_cb->inq_var.sfp = BTM_BLE_DEFAULT_SFP; + p_cb->inq_var.connectable_mode = BTM_BLE_NON_CONNECTABLE; + p_cb->inq_var.discoverable_mode = BTM_BLE_NON_DISCOVERABLE; + + /* for background connection, reset connection params to be undefined */ + p_cb->scan_int = p_cb->scan_win = BTM_BLE_SCAN_PARAM_UNDEF; + + p_cb->inq_var.evt_type = BTM_BLE_NON_CONNECT_EVT; + + p_cb->adv_rpt_queue = pkt_queue_create(); + assert(p_cb->adv_rpt_queue != NULL); + + p_cb->adv_rpt_ready = osi_event_create(btm_adv_pkt_handler, NULL); + assert(p_cb->adv_rpt_ready != NULL); + osi_event_bind(p_cb->adv_rpt_ready, btu_get_current_thread(), 0); + +#if BLE_VND_INCLUDED == FALSE + btm_ble_adv_filter_init(); +#endif +} + +/******************************************************************************* +** +** Function btm_ble_free +** +** Description free the control block variable values. +** +** Returns void +** +*******************************************************************************/ +void btm_ble_free (void) +{ + tBTM_BLE_CB *p_cb = &btm_cb.ble_ctr_cb; + + BTM_TRACE_DEBUG("%s", __func__); + + fixed_queue_free(p_cb->conn_pending_q, osi_free_func); + + pkt_queue_destroy(p_cb->adv_rpt_queue, NULL); + p_cb->adv_rpt_queue = NULL; + + osi_event_delete(p_cb->adv_rpt_ready); + p_cb->adv_rpt_ready = NULL; + +#if BTM_DYNAMIC_MEMORY == TRUE + osi_free(cmn_ble_gap_vsc_cb_ptr); + cmn_ble_gap_vsc_cb_ptr = NULL; +#endif +} + +/******************************************************************************* +** +** Function btm_ble_topology_check +** +** Description check to see requested state is supported. One state check at +** a time is supported +** +** Returns TRUE is request is allowed, FALSE otherwise. +** +*******************************************************************************/ +BOOLEAN btm_ble_topology_check(tBTM_BLE_STATE_MASK request_state_mask) +{ + BOOLEAN rt = FALSE; + + UINT8 state_offset = 0; + UINT16 cur_states = btm_cb.ble_ctr_cb.cur_states; + UINT8 mask, offset; + UINT8 request_state = 0; + + /* check only one bit is set and within valid range */ + if (request_state_mask == BTM_BLE_STATE_INVALID || + request_state_mask > BTM_BLE_STATE_SCAN_ADV_BIT || + (request_state_mask & (request_state_mask - 1 )) != 0) { + BTM_TRACE_ERROR("illegal state requested: %d", request_state_mask); + return rt; + } + + while (request_state_mask) { + request_state_mask >>= 1; + request_state ++; + } + + /* check if the requested state is supported or not */ + mask = btm_le_state_combo_tbl[0][request_state - 1][0]; + offset = btm_le_state_combo_tbl[0][request_state - 1][1]; + + const uint8_t *ble_supported_states = controller_get_interface()->get_ble_supported_states(); + + if (!BTM_LE_STATES_SUPPORTED(ble_supported_states, mask, offset)) { + BTM_TRACE_ERROR("state requested not supported: %d", request_state); + return rt; + } + + rt = TRUE; + /* make sure currently active states are all supported in conjunction with the requested + state. If the bit in table is not set, the combination is not supported */ + while (cur_states != 0) { + if (cur_states & 0x01) { + mask = btm_le_state_combo_tbl[request_state][state_offset][0]; + offset = btm_le_state_combo_tbl[request_state][state_offset][1]; + + if (mask != 0 && offset != 0) { + if (!BTM_LE_STATES_SUPPORTED(ble_supported_states, mask, offset)) { + rt = FALSE; + break; + } + } + } + cur_states >>= 1; + state_offset ++; + } + return rt; +} + +/******************************************************************************* + ** + ** Function BTM_Ble_Authorization + ** + ** Description This function is used to authorize a specified device + ** + ** Returns TRUE or FALSE + ** + *******************************************************************************/ +BOOLEAN BTM_Ble_Authorization(BD_ADDR bd_addr, BOOLEAN authorize) +{ + if (bd_addr == NULL) { + BTM_TRACE_ERROR("bd_addr is NULL"); + return FALSE; + } + + if (btm_sec_dev_authorization(bd_addr, authorize)) { + return TRUE; + } + + BTM_TRACE_ERROR("Authorization fail"); + return FALSE; +} + +/******************************************************************************* +** +** Function BTM_BleClearAdv +** +** Description This function is called to clear legacy advertising +** +** Parameter p_clear_adv_cback - Command complete callback +** +*******************************************************************************/ +BOOLEAN BTM_BleClearAdv(tBTM_CLEAR_ADV_CMPL_CBACK *p_clear_adv_cback) +{ + tBTM_BLE_CB *p_cb = &btm_cb.ble_ctr_cb; + + if (btsnd_hcic_ble_clear_adv() == FALSE) { + BTM_TRACE_ERROR("%s: Unable to Clear Advertising", __FUNCTION__); + return FALSE; + } + + p_cb->inq_var.p_clear_adv_cb = p_clear_adv_cback; + return TRUE; +} + +bool btm_ble_adv_pkt_ready(void) +{ + tBTM_BLE_CB *p_cb = &btm_cb.ble_ctr_cb; + osi_thread_post_event(p_cb->adv_rpt_ready, OSI_THREAD_MAX_TIMEOUT); + + return true; +} + +bool btm_ble_adv_pkt_post(pkt_linked_item_t *pkt) +{ + if (pkt == NULL) { + return false; + } + + tBTM_BLE_CB *p_cb = &btm_cb.ble_ctr_cb; + pkt_queue_enqueue(p_cb->adv_rpt_queue, pkt); + return true; +} +#endif /* BLE_INCLUDED */ diff --git a/lib/bt/host/bluedroid/stack/btm/btm_ble_multi_adv.c b/lib/bt/host/bluedroid/stack/btm/btm_ble_multi_adv.c new file mode 100644 index 00000000..443dd64e --- /dev/null +++ b/lib/bt/host/bluedroid/stack/btm/btm_ble_multi_adv.c @@ -0,0 +1,878 @@ +/****************************************************************************** + * + * Copyright (C) 2014 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +#include + +#include "common/bt_target.h" +#include "device/controller.h" + +#if (BLE_INCLUDED == TRUE) +#include "stack/bt_types.h" +#include "stack/hcimsgs.h" +#include "stack/btu.h" +#include "btm_int.h" +//#include "bt_utils.h" +#include "stack/hcidefs.h" +#include "stack/btm_ble_api.h" + +/************************************************************************************ +** Constants & Macros +************************************************************************************/ +/* length of each multi adv sub command */ +#define BTM_BLE_MULTI_ADV_ENB_LEN 3 +#define BTM_BLE_MULTI_ADV_SET_PARAM_LEN 24 +#define BTM_BLE_MULTI_ADV_WRITE_DATA_LEN (BTM_BLE_AD_DATA_LEN + 3) +#define BTM_BLE_MULTI_ADV_SET_RANDOM_ADDR_LEN 8 + +#define BTM_BLE_MULTI_ADV_CB_EVT_MASK 0xF0 +#define BTM_BLE_MULTI_ADV_SUBCODE_MASK 0x0F + +/************************************************************************************ +** Static variables +************************************************************************************/ +#if BTM_DYNAMIC_MEMORY == FALSE +tBTM_BLE_MULTI_ADV_CB btm_multi_adv_cb; +tBTM_BLE_MULTI_ADV_INST_IDX_Q btm_multi_adv_idx_q; +#else +tBTM_BLE_MULTI_ADV_CB *btm_multi_adv_cb_ptr; +tBTM_BLE_MULTI_ADV_INST_IDX_Q *btm_multi_adv_idx_q_ptr; +#define btm_multi_adv_cb (*btm_multi_adv_cb_ptr) +#define btm_multi_adv_idx_q (*btm_multi_adv_idx_q_ptr) +#endif + +/************************************************************************************ +** Externs +************************************************************************************/ +extern void btm_ble_update_dmt_flag_bits(UINT8 *flag_value, + const UINT16 connect_mode, const UINT16 disc_mode); + +/******************************************************************************* +** +** Function btm_ble_multi_adv_enq_op_q +** +** Description enqueue a multi adv operation in q to check command complete +** status. +** +** Returns void +** +*******************************************************************************/ +void btm_ble_multi_adv_enq_op_q(UINT8 opcode, UINT8 inst_id, UINT8 cb_evt) +{ + tBTM_BLE_MULTI_ADV_OPQ *p_op_q = &btm_multi_adv_cb.op_q; + + p_op_q->p_inst_id[p_op_q->next_idx] = inst_id; + + p_op_q->p_sub_code[p_op_q->next_idx] = (opcode | (cb_evt << 4)); + + p_op_q->next_idx = (p_op_q->next_idx + 1) % BTM_BleMaxMultiAdvInstanceCount(); +} + +/******************************************************************************* +** +** Function btm_ble_multi_adv_deq_op_q +** +** Description dequeue a multi adv operation from q when command complete +** is received. +** +** Returns void +** +*******************************************************************************/ +void btm_ble_multi_adv_deq_op_q(UINT8 *p_opcode, UINT8 *p_inst_id, UINT8 *p_cb_evt) +{ + tBTM_BLE_MULTI_ADV_OPQ *p_op_q = &btm_multi_adv_cb.op_q; + + *p_inst_id = p_op_q->p_inst_id[p_op_q->pending_idx] & 0x7F; + *p_cb_evt = (p_op_q->p_sub_code[p_op_q->pending_idx] >> 4); + *p_opcode = (p_op_q->p_sub_code[p_op_q->pending_idx] & BTM_BLE_MULTI_ADV_SUBCODE_MASK); + + p_op_q->pending_idx = (p_op_q->pending_idx + 1) % BTM_BleMaxMultiAdvInstanceCount(); +} + +/******************************************************************************* +** +** Function btm_ble_multi_adv_vsc_cmpl_cback +** +** Description Multi adv VSC complete callback +** +** Parameters +** +** Returns void +** +*******************************************************************************/ +void btm_ble_multi_adv_vsc_cmpl_cback (tBTM_VSC_CMPL *p_params) +{ + UINT8 status, subcode; + UINT8 *p = p_params->p_param_buf, inst_id; + UINT16 len = p_params->param_len; + tBTM_BLE_MULTI_ADV_INST *p_inst ; + UINT8 cb_evt = 0, opcode; + + if (len < 2) { + BTM_TRACE_ERROR("wrong length for btm_ble_multi_adv_vsc_cmpl_cback"); + return; + } + + STREAM_TO_UINT8(status, p); + STREAM_TO_UINT8(subcode, p); + + btm_ble_multi_adv_deq_op_q(&opcode, &inst_id, &cb_evt); + + BTM_TRACE_DEBUG("op_code = %02x inst_id = %d cb_evt = %02x", opcode, inst_id, cb_evt); + + if (opcode != subcode || inst_id == 0) { + BTM_TRACE_ERROR("get unexpected VSC cmpl, expect: %d get: %d", subcode, opcode); + return; + } + + p_inst = &btm_multi_adv_cb.p_adv_inst[inst_id - 1]; + + switch (subcode) { + case BTM_BLE_MULTI_ADV_ENB: { + BTM_TRACE_DEBUG("BTM_BLE_MULTI_ADV_ENB status = %d", status); + + /* Mark as not in use here, if instance cannot be enabled */ + if (HCI_SUCCESS != status && BTM_BLE_MULTI_ADV_ENB_EVT == cb_evt) { + btm_multi_adv_cb.p_adv_inst[inst_id - 1].in_use = FALSE; + } + break; + } + + case BTM_BLE_MULTI_ADV_SET_PARAM: { + BTM_TRACE_DEBUG("BTM_BLE_MULTI_ADV_SET_PARAM status = %d", status); + break; + } + + case BTM_BLE_MULTI_ADV_WRITE_ADV_DATA: { + BTM_TRACE_DEBUG("BTM_BLE_MULTI_ADV_WRITE_ADV_DATA status = %d", status); + break; + } + + case BTM_BLE_MULTI_ADV_WRITE_SCAN_RSP_DATA: { + BTM_TRACE_DEBUG("BTM_BLE_MULTI_ADV_WRITE_SCAN_RSP_DATA status = %d", status); + break; + } + + case BTM_BLE_MULTI_ADV_SET_RANDOM_ADDR: { + BTM_TRACE_DEBUG("BTM_BLE_MULTI_ADV_SET_RANDOM_ADDR status = %d", status); + break; + } + + default: + break; + } + + if (cb_evt != 0 && p_inst->p_cback != NULL) { + (p_inst->p_cback)(cb_evt, inst_id, p_inst->p_ref, status); + } + return; +} + +/******************************************************************************* +** +** Function btm_ble_enable_multi_adv +** +** Description This function enable the customer specific feature in controller +** +** Parameters enable: enable or disable +** inst_id: adv instance ID, can not be 0 +** +** Returns status +** +*******************************************************************************/ +tBTM_STATUS btm_ble_enable_multi_adv (BOOLEAN enable, UINT8 inst_id, UINT8 cb_evt) +{ + UINT8 param[BTM_BLE_MULTI_ADV_ENB_LEN], *pp; + UINT8 enb = enable ? 1 : 0; + tBTM_STATUS rt; + + pp = param; + memset(param, 0, BTM_BLE_MULTI_ADV_ENB_LEN); + + UINT8_TO_STREAM (pp, BTM_BLE_MULTI_ADV_ENB); + UINT8_TO_STREAM (pp, enb); + UINT8_TO_STREAM (pp, inst_id); + + BTM_TRACE_EVENT (" btm_ble_enable_multi_adv: enb %d, Inst ID %d", enb, inst_id); + + if ((rt = BTM_VendorSpecificCommand (HCI_BLE_MULTI_ADV_OCF, + BTM_BLE_MULTI_ADV_ENB_LEN, + param, + btm_ble_multi_adv_vsc_cmpl_cback)) + == BTM_CMD_STARTED) { + btm_ble_multi_adv_enq_op_q(BTM_BLE_MULTI_ADV_ENB, inst_id, cb_evt); + } + return rt; +} +/******************************************************************************* +** +** Function btm_ble_map_adv_tx_power +** +** Description return the actual power in dBm based on the mapping in config file +** +** Parameters advertise parameters used for this instance. +** +** Returns tx power in dBm +** +*******************************************************************************/ +static const int btm_ble_tx_power[BTM_BLE_ADV_TX_POWER_MAX + 1] = BTM_BLE_ADV_TX_POWER; +char btm_ble_map_adv_tx_power(int tx_power_index) +{ + if (0 <= tx_power_index && tx_power_index <= BTM_BLE_ADV_TX_POWER_MAX) { + return (char)btm_ble_tx_power[tx_power_index]; + } + return 0; +} +/******************************************************************************* +** +** Function btm_ble_multi_adv_set_params +** +** Description This function enable the customer specific feature in controller +** +** Parameters advertise parameters used for this instance. +** +** Returns status +** +*******************************************************************************/ +tBTM_STATUS btm_ble_multi_adv_set_params (tBTM_BLE_MULTI_ADV_INST *p_inst, + tBTM_BLE_ADV_PARAMS *p_params, + UINT8 cb_evt) +{ + UINT8 param[BTM_BLE_MULTI_ADV_SET_PARAM_LEN], *pp; + tBTM_STATUS rt; + BD_ADDR dummy = {0, 0, 0, 0, 0, 0}; + + pp = param; + memset(param, 0, BTM_BLE_MULTI_ADV_SET_PARAM_LEN); + + UINT8_TO_STREAM(pp, BTM_BLE_MULTI_ADV_SET_PARAM); + + UINT16_TO_STREAM (pp, p_params->adv_int_min); + UINT16_TO_STREAM (pp, p_params->adv_int_max); + UINT8_TO_STREAM (pp, p_params->adv_type); + +#if (defined BLE_PRIVACY_SPT && BLE_PRIVACY_SPT == TRUE) + if (btm_cb.ble_ctr_cb.privacy_mode != BTM_PRIVACY_NONE) { + UINT8_TO_STREAM (pp, BLE_ADDR_RANDOM); + BDADDR_TO_STREAM (pp, p_inst->rpa); + } else +#endif + { + UINT8_TO_STREAM (pp, BLE_ADDR_PUBLIC); + BDADDR_TO_STREAM (pp, controller_get_interface()->get_address()->address); + } + + BTM_TRACE_EVENT (" btm_ble_multi_adv_set_params,Min %d, Max %d,adv_type %d", + p_params->adv_int_min, p_params->adv_int_max, p_params->adv_type); + + UINT8_TO_STREAM (pp, 0); + BDADDR_TO_STREAM (pp, dummy); + + if (p_params->channel_map == 0 || p_params->channel_map > BTM_BLE_DEFAULT_ADV_CHNL_MAP) { + p_params->channel_map = BTM_BLE_DEFAULT_ADV_CHNL_MAP; + } + UINT8_TO_STREAM (pp, p_params->channel_map); + + if (p_params->adv_filter_policy >= AP_SCAN_CONN_POLICY_MAX) { + p_params->adv_filter_policy = AP_SCAN_CONN_ALL; + } + UINT8_TO_STREAM (pp, p_params->adv_filter_policy); + + UINT8_TO_STREAM (pp, p_inst->inst_id); + + if (p_params->tx_power > BTM_BLE_ADV_TX_POWER_MAX) { + p_params->tx_power = BTM_BLE_ADV_TX_POWER_MAX; + } + UINT8_TO_STREAM (pp, btm_ble_map_adv_tx_power(p_params->tx_power)); + + BTM_TRACE_EVENT("set_params:Chnl Map %d,adv_fltr policy %d,ID:%d, TX Power%d", + p_params->channel_map, p_params->adv_filter_policy, p_inst->inst_id, p_params->tx_power); + + if ((rt = BTM_VendorSpecificCommand (HCI_BLE_MULTI_ADV_OCF, + BTM_BLE_MULTI_ADV_SET_PARAM_LEN, + param, + btm_ble_multi_adv_vsc_cmpl_cback)) + == BTM_CMD_STARTED) { + p_inst->adv_evt = p_params->adv_type; + +#if (defined BLE_PRIVACY_SPT && BLE_PRIVACY_SPT == TRUE) + if (btm_cb.ble_ctr_cb.privacy_mode != BTM_PRIVACY_NONE) { + /* start timer */ + p_inst->raddr_timer_ent.param = (TIMER_PARAM_TYPE) p_inst; + btu_start_timer_oneshot(&p_inst->raddr_timer_ent, BTU_TTYPE_BLE_RANDOM_ADDR, + BTM_BLE_PRIVATE_ADDR_INT); + } +#endif + btm_ble_multi_adv_enq_op_q(BTM_BLE_MULTI_ADV_SET_PARAM, p_inst->inst_id, cb_evt); + } + return rt; +} + +/******************************************************************************* +** +** Function btm_ble_multi_adv_write_rpa +** +** Description This function write the random address for the adv instance into +** controller +** +** Parameters +** +** Returns status +** +*******************************************************************************/ +tBTM_STATUS btm_ble_multi_adv_write_rpa (tBTM_BLE_MULTI_ADV_INST *p_inst, BD_ADDR random_addr) +{ + UINT8 param[BTM_BLE_MULTI_ADV_SET_RANDOM_ADDR_LEN], *pp = param; + tBTM_STATUS rt; + + BTM_TRACE_EVENT ("%s-BD_ADDR:%02x-%02x-%02x-%02x-%02x-%02x,inst_id:%d", + __FUNCTION__, random_addr[5], random_addr[4], random_addr[3], random_addr[2], + random_addr[1], random_addr[0], p_inst->inst_id); + + memset(param, 0, BTM_BLE_MULTI_ADV_SET_RANDOM_ADDR_LEN); + + UINT8_TO_STREAM (pp, BTM_BLE_MULTI_ADV_SET_RANDOM_ADDR); + BDADDR_TO_STREAM(pp, random_addr); + UINT8_TO_STREAM(pp, p_inst->inst_id); + + if ((rt = BTM_VendorSpecificCommand (HCI_BLE_MULTI_ADV_OCF, + BTM_BLE_MULTI_ADV_SET_RANDOM_ADDR_LEN, + param, + btm_ble_multi_adv_vsc_cmpl_cback)) == BTM_CMD_STARTED) { + /* start a periodical timer to refresh random addr */ + btu_stop_timer_oneshot(&p_inst->raddr_timer_ent); + p_inst->raddr_timer_ent.param = (TIMER_PARAM_TYPE) p_inst; + btu_start_timer_oneshot(&p_inst->raddr_timer_ent, BTU_TTYPE_BLE_RANDOM_ADDR, + BTM_BLE_PRIVATE_ADDR_INT); + + btm_ble_multi_adv_enq_op_q(BTM_BLE_MULTI_ADV_SET_RANDOM_ADDR, p_inst->inst_id, 0); + } + return rt; +} + +/******************************************************************************* +** +** Function btm_ble_multi_adv_gen_rpa_cmpl +** +** Description RPA generation completion callback for each adv instance. Will +** continue write the new RPA into controller. +** +** Returns none. +** +*******************************************************************************/ +void btm_ble_multi_adv_gen_rpa_cmpl(tBTM_RAND_ENC *p) +{ +#if (SMP_INCLUDED == TRUE) + tSMP_ENC output; + UINT8 index = 0; + tBTM_BLE_MULTI_ADV_INST *p_inst = NULL; + + /* Retrieve the index of adv instance from stored Q */ + if (btm_multi_adv_idx_q.front == -1) { + BTM_TRACE_ERROR(" %s can't locate advertise instance", __FUNCTION__); + return; + } else { + index = btm_multi_adv_idx_q.inst_index_queue[btm_multi_adv_idx_q.front]; + if (btm_multi_adv_idx_q.front == btm_multi_adv_idx_q.rear) { + btm_multi_adv_idx_q.front = -1; + btm_multi_adv_idx_q.rear = -1; + } else { + btm_multi_adv_idx_q.front = (btm_multi_adv_idx_q.front + 1) % BTM_BLE_MULTI_ADV_MAX; + } + } + + p_inst = &(btm_multi_adv_cb.p_adv_inst[index]); + + BTM_TRACE_EVENT ("btm_ble_multi_adv_gen_rpa_cmpl inst_id = %d", p_inst->inst_id); + if (p) { + p->param_buf[2] &= (~BLE_RESOLVE_ADDR_MASK); + p->param_buf[2] |= BLE_RESOLVE_ADDR_MSB; + + p_inst->rpa[2] = p->param_buf[0]; + p_inst->rpa[1] = p->param_buf[1]; + p_inst->rpa[0] = p->param_buf[2]; + + if (!SMP_Encrypt(btm_cb.devcb.id_keys.irk, BT_OCTET16_LEN, p->param_buf, 3, &output)) { + BTM_TRACE_DEBUG("generate random address failed"); + } else { + /* set hash to be LSB of rpAddress */ + p_inst->rpa[5] = output.param_buf[0]; + p_inst->rpa[4] = output.param_buf[1]; + p_inst->rpa[3] = output.param_buf[2]; + } + + if (p_inst->inst_id != BTM_BLE_MULTI_ADV_DEFAULT_STD && + p_inst->inst_id < BTM_BleMaxMultiAdvInstanceCount()) { + /* set it to controller */ + btm_ble_multi_adv_write_rpa(p_inst, p_inst->rpa); + } + } +#endif +} + +/******************************************************************************* +** +** Function btm_ble_multi_adv_configure_rpa +** +** Description This function set the random address for the adv instance +** +** Parameters advertise parameters used for this instance. +** +** Returns none +** +*******************************************************************************/ +void btm_ble_multi_adv_configure_rpa (tBTM_BLE_MULTI_ADV_INST *p_inst) +{ + if (btm_multi_adv_idx_q.front == (btm_multi_adv_idx_q.rear + 1) % BTM_BLE_MULTI_ADV_MAX) { + BTM_TRACE_ERROR("outstanding rand generation exceeded max allowed "); + return; + } else { + if (btm_multi_adv_idx_q.front == -1) { + btm_multi_adv_idx_q.front = 0; + btm_multi_adv_idx_q.rear = 0; + } else { + btm_multi_adv_idx_q.rear = (btm_multi_adv_idx_q.rear + 1) % BTM_BLE_MULTI_ADV_MAX; + } + btm_multi_adv_idx_q.inst_index_queue[btm_multi_adv_idx_q.rear] = p_inst->index; + } + btm_gen_resolvable_private_addr((void *)btm_ble_multi_adv_gen_rpa_cmpl); +} + +/******************************************************************************* +** +** Function btm_ble_multi_adv_reenable +** +** Description This function re-enable adv instance upon a connection establishment. +** +** Parameters advertise parameters used for this instance. +** +** Returns none. +** +*******************************************************************************/ +void btm_ble_multi_adv_reenable(UINT8 inst_id) +{ + tBTM_BLE_MULTI_ADV_INST *p_inst = &btm_multi_adv_cb.p_adv_inst[inst_id - 1]; + + if (TRUE == p_inst->in_use) { + if (p_inst->adv_evt != BTM_BLE_CONNECT_DIR_EVT) { + btm_ble_enable_multi_adv (TRUE, p_inst->inst_id, 0); + } else + /* mark directed adv as disabled if adv has been stopped */ + { + (p_inst->p_cback)(BTM_BLE_MULTI_ADV_DISABLE_EVT, p_inst->inst_id, p_inst->p_ref, 0); + p_inst->in_use = FALSE; + } + } +} + +/******************************************************************************* +** +** Function btm_ble_multi_adv_enb_privacy +** +** Description This function enable/disable privacy setting in multi adv +** +** Parameters enable: enable or disable the adv instance. +** +** Returns none. +** +*******************************************************************************/ +void btm_ble_multi_adv_enb_privacy(BOOLEAN enable) +{ + UINT8 i; + tBTM_BLE_MULTI_ADV_INST *p_inst = &btm_multi_adv_cb.p_adv_inst[0]; + + for (i = 0; i < BTM_BleMaxMultiAdvInstanceCount() - 1; i ++, p_inst++) { + p_inst->in_use = FALSE; + if (enable) { + btm_ble_multi_adv_configure_rpa (p_inst); + } else { + btu_stop_timer_oneshot(&p_inst->raddr_timer_ent); + } + } +} + +/******************************************************************************* +** +** Function BTM_BleEnableAdvInstance +** +** Description This function enable a Multi-ADV instance with the specified +** adv parameters +** +** Parameters p_params: pointer to the adv parameter structure, set as default +** adv parameter when the instance is enabled. +** p_cback: callback function for the adv instance. +** p_ref: reference data attach to the adv instance to be enabled. +** +** Returns status +** +*******************************************************************************/ +tBTM_STATUS BTM_BleEnableAdvInstance (tBTM_BLE_ADV_PARAMS *p_params, + tBTM_BLE_MULTI_ADV_CBACK *p_cback, void *p_ref) +{ + UINT8 i; + tBTM_STATUS rt = BTM_NO_RESOURCES; + tBTM_BLE_MULTI_ADV_INST *p_inst = &btm_multi_adv_cb.p_adv_inst[0]; + + BTM_TRACE_EVENT("BTM_BleEnableAdvInstance called"); + + if (0 == btm_cb.cmn_ble_vsc_cb.adv_inst_max) { + BTM_TRACE_ERROR("Controller does not support Multi ADV"); + return BTM_ERR_PROCESSING; + } + + if (NULL == p_inst) { + BTM_TRACE_ERROR("Invalid instance in BTM_BleEnableAdvInstance"); + return BTM_ERR_PROCESSING; + } + + for (i = 0; i < BTM_BleMaxMultiAdvInstanceCount() - 1; i ++, p_inst++) { + if (FALSE == p_inst->in_use) { + p_inst->in_use = TRUE; + /* configure adv parameter */ + if (p_params) { + rt = btm_ble_multi_adv_set_params(p_inst, p_params, 0); + } else { + rt = BTM_CMD_STARTED; + } + + /* enable adv */ + BTM_TRACE_EVENT("btm_ble_enable_multi_adv being called with inst_id:%d", + p_inst->inst_id); + + if (BTM_CMD_STARTED == rt) { + if ((rt = btm_ble_enable_multi_adv (TRUE, p_inst->inst_id, + BTM_BLE_MULTI_ADV_ENB_EVT)) == BTM_CMD_STARTED) { + p_inst->p_cback = p_cback; + p_inst->p_ref = p_ref; + } + } + + if (BTM_CMD_STARTED != rt) { + p_inst->in_use = FALSE; + BTM_TRACE_ERROR("BTM_BleEnableAdvInstance failed"); + } + break; + } + } + return rt; +} + +/******************************************************************************* +** +** Function BTM_BleUpdateAdvInstParam +** +** Description This function update a Multi-ADV instance with the specified +** adv parameters. +** +** Parameters inst_id: adv instance ID +** p_params: pointer to the adv parameter structure. +** +** Returns status +** +*******************************************************************************/ +tBTM_STATUS BTM_BleUpdateAdvInstParam (UINT8 inst_id, tBTM_BLE_ADV_PARAMS *p_params) +{ + tBTM_STATUS rt = BTM_ILLEGAL_VALUE; + tBTM_BLE_MULTI_ADV_INST *p_inst = &btm_multi_adv_cb.p_adv_inst[inst_id - 1]; + + BTM_TRACE_EVENT("BTM_BleUpdateAdvInstParam called with inst_id:%d", inst_id); + + if (0 == btm_cb.cmn_ble_vsc_cb.adv_inst_max) { + BTM_TRACE_ERROR("Controller does not support Multi ADV"); + return BTM_ERR_PROCESSING; + } + + if (inst_id < BTM_BleMaxMultiAdvInstanceCount() && + inst_id != BTM_BLE_MULTI_ADV_DEFAULT_STD && + p_params != NULL) { + if (FALSE == p_inst->in_use) { + BTM_TRACE_DEBUG("adv instance %d is not active", inst_id); + return BTM_WRONG_MODE; + } else { + btm_ble_enable_multi_adv(FALSE, inst_id, 0); + } + + if (BTM_CMD_STARTED == btm_ble_multi_adv_set_params(p_inst, p_params, 0)) { + rt = btm_ble_enable_multi_adv(TRUE, inst_id, BTM_BLE_MULTI_ADV_PARAM_EVT); + } + } + return rt; +} + +/******************************************************************************* +** +** Function BTM_BleCfgAdvInstData +** +** Description This function configure a Multi-ADV instance with the specified +** adv data or scan response data. +** +** Parameters inst_id: adv instance ID +** is_scan_rsp: is this scan response. if no, set as adv data. +** data_mask: adv data mask. +** p_data: pointer to the adv data structure. +** +** Returns status +** +*******************************************************************************/ +tBTM_STATUS BTM_BleCfgAdvInstData (UINT8 inst_id, BOOLEAN is_scan_rsp, + tBTM_BLE_AD_MASK data_mask, + tBTM_BLE_ADV_DATA *p_data) +{ + UINT8 param[BTM_BLE_MULTI_ADV_WRITE_DATA_LEN], *pp = param; + UINT8 sub_code = (is_scan_rsp) ? + BTM_BLE_MULTI_ADV_WRITE_SCAN_RSP_DATA : BTM_BLE_MULTI_ADV_WRITE_ADV_DATA; + UINT8 *p_len; + tBTM_STATUS rt; + UINT8 *pp_temp = (UINT8 *)(param + BTM_BLE_MULTI_ADV_WRITE_DATA_LEN - 1); + tBTM_BLE_VSC_CB cmn_ble_vsc_cb; + + BTM_BleGetVendorCapabilities(&cmn_ble_vsc_cb); + if (0 == cmn_ble_vsc_cb.adv_inst_max) { + BTM_TRACE_ERROR("Controller does not support Multi ADV"); + return BTM_ERR_PROCESSING; + } + + btm_ble_update_dmt_flag_bits(&p_data->flag, btm_cb.btm_inq_vars.connectable_mode, + btm_cb.btm_inq_vars.discoverable_mode); + + BTM_TRACE_EVENT("BTM_BleCfgAdvInstData called with inst_id:%d", inst_id); + if (inst_id > BTM_BLE_MULTI_ADV_MAX || inst_id == BTM_BLE_MULTI_ADV_DEFAULT_STD) { + return BTM_ILLEGAL_VALUE; + } + + memset(param, 0, BTM_BLE_MULTI_ADV_WRITE_DATA_LEN); + + UINT8_TO_STREAM(pp, sub_code); + p_len = pp ++; + btm_ble_build_adv_data(&data_mask, &pp, p_data); + *p_len = (UINT8)(pp - param - 2); + UINT8_TO_STREAM(pp_temp, inst_id); + + if ((rt = BTM_VendorSpecificCommand (HCI_BLE_MULTI_ADV_OCF, + (UINT8)BTM_BLE_MULTI_ADV_WRITE_DATA_LEN, + param, + btm_ble_multi_adv_vsc_cmpl_cback)) + == BTM_CMD_STARTED) { + btm_ble_multi_adv_enq_op_q(sub_code, inst_id, BTM_BLE_MULTI_ADV_DATA_EVT); + } + return rt; +} + +/******************************************************************************* +** +** Function BTM_BleDisableAdvInstance +** +** Description This function disables a Multi-ADV instance. +** +** Parameters inst_id: adv instance ID +** +** Returns status +** +*******************************************************************************/ +tBTM_STATUS BTM_BleDisableAdvInstance (UINT8 inst_id) +{ + tBTM_STATUS rt = BTM_ILLEGAL_VALUE; + tBTM_BLE_VSC_CB cmn_ble_vsc_cb; + + BTM_TRACE_EVENT("BTM_BleDisableAdvInstance with inst_id:%d", inst_id); + + BTM_BleGetVendorCapabilities(&cmn_ble_vsc_cb); + + if (0 == cmn_ble_vsc_cb.adv_inst_max) { + BTM_TRACE_ERROR("Controller does not support Multi ADV"); + return BTM_ERR_PROCESSING; + } + + if (inst_id < BTM_BleMaxMultiAdvInstanceCount() && + inst_id != BTM_BLE_MULTI_ADV_DEFAULT_STD) { + if ((rt = btm_ble_enable_multi_adv(FALSE, inst_id, BTM_BLE_MULTI_ADV_DISABLE_EVT)) + == BTM_CMD_STARTED) { + btm_ble_multi_adv_configure_rpa(&btm_multi_adv_cb.p_adv_inst[inst_id - 1]); + btu_stop_timer_oneshot(&btm_multi_adv_cb.p_adv_inst[inst_id - 1].raddr_timer_ent); + btm_multi_adv_cb.p_adv_inst[inst_id - 1].in_use = FALSE; + } + } + return rt; +} +/******************************************************************************* +** +** Function btm_ble_multi_adv_vse_cback +** +** Description VSE callback for multi adv events. +** +** Returns +** +*******************************************************************************/ +void btm_ble_multi_adv_vse_cback(UINT8 len, UINT8 *p) +{ + UINT8 sub_event; + UINT8 adv_inst; + UINT16 conn_handle; + tACL_CONN *p_acl_cb = NULL; + /* Check if this is a BLE RSSI vendor specific event */ + STREAM_TO_UINT8(sub_event, p); + len--; + + BTM_TRACE_EVENT("btm_ble_multi_adv_vse_cback called with event:%d", sub_event); + if ((sub_event == HCI_VSE_SUBCODE_BLE_MULTI_ADV_ST_CHG) && (len >= 4)) { + STREAM_TO_UINT8(adv_inst, p); + ++p; + STREAM_TO_UINT16(conn_handle, p); + + if ((p_acl_cb = btm_handle_to_acl(conn_handle)) != NULL) { +#if (defined BLE_PRIVACY_SPT && BLE_PRIVACY_SPT == TRUE) + if (btm_cb.ble_ctr_cb.privacy_mode != BTM_PRIVACY_NONE && + adv_inst <= BTM_BLE_MULTI_ADV_MAX && adv_inst != BTM_BLE_MULTI_ADV_DEFAULT_STD) { + memcpy(p_acl_cb->conn_addr, btm_multi_adv_cb.p_adv_inst[adv_inst - 1].rpa, + BD_ADDR_LEN); + } +#endif + } + + if (adv_inst < BTM_BleMaxMultiAdvInstanceCount() && + adv_inst != BTM_BLE_MULTI_ADV_DEFAULT_STD) { + BTM_TRACE_EVENT("btm_ble_multi_adv_reenable called"); + btm_ble_multi_adv_reenable(adv_inst); + } + /* re-enable connectibility */ + else if (adv_inst == BTM_BLE_MULTI_ADV_DEFAULT_STD) { + if (btm_cb.ble_ctr_cb.inq_var.connectable_mode == BTM_BLE_CONNECTABLE) { + btm_ble_set_connectability ( btm_cb.ble_ctr_cb.inq_var.connectable_mode ); + } + } + + } + +} +/******************************************************************************* +** +** Function btm_ble_multi_adv_init +** +** Description This function initialize the multi adv control block. +** +** Parameters None +** +** Returns void +** +*******************************************************************************/ +void btm_ble_multi_adv_init(void) +{ +#if BTM_DYNAMIC_MEMORY == TRUE + btm_multi_adv_cb_ptr = (tBTM_BLE_MULTI_ADV_CB *)osi_malloc(sizeof(tBTM_BLE_MULTI_ADV_CB)); + btm_multi_adv_idx_q_ptr = (tBTM_BLE_MULTI_ADV_INST_IDX_Q *)osi_malloc(sizeof(tBTM_BLE_MULTI_ADV_INST_IDX_Q)); + if (btm_multi_adv_cb_ptr == NULL || btm_multi_adv_idx_q_ptr == NULL) { + BTM_TRACE_ERROR("%s malloc failed", __func__); + return; + } +#endif + + UINT8 i = 0; + memset(&btm_multi_adv_cb, 0, sizeof(tBTM_BLE_MULTI_ADV_CB)); + memset (&btm_multi_adv_idx_q, 0, sizeof (tBTM_BLE_MULTI_ADV_INST_IDX_Q)); + btm_multi_adv_idx_q.front = -1; + btm_multi_adv_idx_q.rear = -1; + + if (btm_cb.cmn_ble_vsc_cb.adv_inst_max > 0) { + btm_multi_adv_cb.p_adv_inst = osi_malloc( sizeof(tBTM_BLE_MULTI_ADV_INST) * + (btm_cb.cmn_ble_vsc_cb.adv_inst_max)); + memset(btm_multi_adv_cb.p_adv_inst, 0, sizeof(tBTM_BLE_MULTI_ADV_INST) * + (btm_cb.cmn_ble_vsc_cb.adv_inst_max)); + + btm_multi_adv_cb.op_q.p_sub_code = osi_malloc( sizeof(UINT8) * + (btm_cb.cmn_ble_vsc_cb.adv_inst_max)); + memset(btm_multi_adv_cb.op_q.p_sub_code, 0, + sizeof(UINT8) * (btm_cb.cmn_ble_vsc_cb.adv_inst_max)); + + btm_multi_adv_cb.op_q.p_inst_id = osi_malloc( sizeof(UINT8) * + (btm_cb.cmn_ble_vsc_cb.adv_inst_max)); + memset(btm_multi_adv_cb.op_q.p_inst_id, 0, + sizeof(UINT8) * (btm_cb.cmn_ble_vsc_cb.adv_inst_max)); + } + + /* Initialize adv instance indices and IDs. */ + for (i = 0; i < btm_cb.cmn_ble_vsc_cb.adv_inst_max; i++) { + btm_multi_adv_cb.p_adv_inst[i].index = i; + btm_multi_adv_cb.p_adv_inst[i].inst_id = i + 1; + } + + BTM_RegisterForVSEvents(btm_ble_multi_adv_vse_cback, TRUE); +} + +/******************************************************************************* +** +** Function btm_ble_multi_adv_cleanup +** +** Description This function cleans up multi adv control block. +** +** Parameters +** Returns void +** +*******************************************************************************/ +void btm_ble_multi_adv_cleanup(void) +{ + if (btm_multi_adv_cb.p_adv_inst) { + osi_free(btm_multi_adv_cb.p_adv_inst); + btm_multi_adv_cb.p_adv_inst = NULL; + } + + if (btm_multi_adv_cb.op_q.p_sub_code) { + osi_free(btm_multi_adv_cb.op_q.p_sub_code); + btm_multi_adv_cb.op_q.p_sub_code = NULL; + } + + if (btm_multi_adv_cb.op_q.p_inst_id) { + osi_free(btm_multi_adv_cb.op_q.p_inst_id); + btm_multi_adv_cb.op_q.p_inst_id = NULL; + } + +#if BTM_DYNAMIC_MEMORY == TRUE + if(btm_multi_adv_cb_ptr) { + osi_free(btm_multi_adv_cb_ptr); + btm_multi_adv_cb_ptr = NULL; + } + if(btm_multi_adv_idx_q_ptr) { + osi_free(btm_multi_adv_idx_q_ptr); + btm_multi_adv_idx_q_ptr = NULL; + } +#endif +} + +/******************************************************************************* +** +** Function btm_ble_multi_adv_get_ref +** +** Description This function obtains the reference pointer for the instance ID provided +** +** Parameters inst_id - Instance ID +** +** Returns void* +** +*******************************************************************************/ +void *btm_ble_multi_adv_get_ref(UINT8 inst_id) +{ + tBTM_BLE_MULTI_ADV_INST *p_inst = NULL; + + if (inst_id < BTM_BleMaxMultiAdvInstanceCount()) { + p_inst = &btm_multi_adv_cb.p_adv_inst[inst_id - 1]; + if (NULL != p_inst) { + return p_inst->p_ref; + } + } + + return NULL; +} +#endif diff --git a/lib/bt/host/bluedroid/stack/btm/btm_ble_privacy.c b/lib/bt/host/bluedroid/stack/btm/btm_ble_privacy.c new file mode 100644 index 00000000..7058df20 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/btm/btm_ble_privacy.c @@ -0,0 +1,1086 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains functions for BLE controller based privacy. + * + ******************************************************************************/ +#include +#include "common/bt_target.h" + +#if (BLE_INCLUDED == TRUE && BLE_PRIVACY_SPT == TRUE) +#include "stack/bt_types.h" +#include "stack/hcimsgs.h" +#include "stack/btu.h" +//#include "vendor_hcidefs.h" +#include "btm_int.h" +#include "device/controller.h" +#include "stack/hcidefs.h" + +#define HCI_VENDOR_BLE_RPA_VSC (0x0155 | HCI_GRP_VENDOR_SPECIFIC) + +/* RPA offload VSC specifics */ +#define BTM_BLE_META_IRK_ENABLE 0x01 +#define BTM_BLE_META_ADD_IRK_ENTRY 0x02 +#define BTM_BLE_META_REMOVE_IRK_ENTRY 0x03 +#define BTM_BLE_META_CLEAR_IRK_LIST 0x04 +#define BTM_BLE_META_READ_IRK_ENTRY 0x05 +#define BTM_BLE_META_CS_RESOLVE_ADDR 0x00000001 +#define BTM_BLE_IRK_ENABLE_LEN 2 + +#define BTM_BLE_META_ADD_IRK_LEN 24 +#define BTM_BLE_META_REMOVE_IRK_LEN 8 +#define BTM_BLE_META_CLEAR_IRK_LEN 1 +#define BTM_BLE_META_READ_IRK_LEN 2 +#define BTM_BLE_META_ADD_WL_ATTR_LEN 9 + +/******************************************************************************* +** Functions implemented controller based privacy using Resolving List +*******************************************************************************/ +/******************************************************************************* +** +** Function btm_ble_enq_resolving_list_pending +** +** Description add target address into resolving pending operation queue +** +** Parameters target_bda: target device address +** add_entry: TRUE for add entry, FALSE for remove entry +** +** Returns void +** +*******************************************************************************/ +void btm_ble_enq_resolving_list_pending(BD_ADDR pseudo_bda, UINT8 op_code) +{ + tBTM_BLE_RESOLVE_Q *p_q = &btm_cb.ble_ctr_cb.resolving_list_pend_q; + + memcpy(p_q->resolve_q_random_pseudo[p_q->q_next], pseudo_bda, BD_ADDR_LEN); + p_q->resolve_q_action[p_q->q_next] = op_code; + p_q->q_next ++; + p_q->q_next %= controller_get_interface()->get_ble_resolving_list_max_size(); +} + +/******************************************************************************* +** +** Function btm_ble_brcm_find_resolving_pending_entry +** +** Description check to see if the action is in pending list +** +** Parameters TRUE: action pending; +** FALSE: new action +** +** Returns void +** +*******************************************************************************/ +BOOLEAN btm_ble_brcm_find_resolving_pending_entry(BD_ADDR pseudo_addr, UINT8 action) +{ + tBTM_BLE_RESOLVE_Q *p_q = &btm_cb.ble_ctr_cb.resolving_list_pend_q; + + for (UINT8 i = p_q->q_pending; i != p_q->q_next;) { + if (memcmp(p_q->resolve_q_random_pseudo[i], pseudo_addr, BD_ADDR_LEN) == 0 && + action == p_q->resolve_q_action[i]) { + return TRUE; + } + + i ++; + i %= controller_get_interface()->get_ble_resolving_list_max_size(); + } + return FALSE; +} + +/******************************************************************************* +** +** Function btm_ble_deq_resolving_pending +** +** Description dequeue target address from resolving pending operation queue +** +** Parameters pseudo_addr: pseudo_addr device address +** +** Returns void +** +*******************************************************************************/ +BOOLEAN btm_ble_deq_resolving_pending(BD_ADDR pseudo_addr) +{ + tBTM_BLE_RESOLVE_Q *p_q = &btm_cb.ble_ctr_cb.resolving_list_pend_q; + + if (p_q->q_next != p_q->q_pending) { + memcpy(pseudo_addr, p_q->resolve_q_random_pseudo[p_q->q_pending], BD_ADDR_LEN); + memset(p_q->resolve_q_random_pseudo[p_q->q_pending], 0, BD_ADDR_LEN); + p_q->q_pending ++; + p_q->q_pending %= controller_get_interface()->get_ble_resolving_list_max_size(); + return TRUE; + } + + return FALSE; +} + +/******************************************************************************* +** +** Function btm_ble_clear_irk_index +** +** Description clear IRK list index mask for availability +** +** Returns none +** +*******************************************************************************/ +void btm_ble_clear_irk_index(UINT8 index) +{ + UINT8 byte; + UINT8 bit; + + if (index < controller_get_interface()->get_ble_resolving_list_max_size()) { + byte = index / 8; + bit = index % 8; + btm_cb.ble_ctr_cb.irk_list_mask[byte] &= (~(1 << bit)); + } +} + +/******************************************************************************* +** +** Function btm_ble_find_irk_index +** +** Description find the first available IRK list index +** +** Returns index from 0 ~ max (127 default) +** +*******************************************************************************/ +UINT8 btm_ble_find_irk_index(void) +{ + UINT8 i = 0; + UINT8 byte; + UINT8 bit; + + while (i < controller_get_interface()->get_ble_resolving_list_max_size()) { + byte = i / 8; + bit = i % 8; + + if ((btm_cb.ble_ctr_cb.irk_list_mask[byte] & (1 << bit)) == 0) { + btm_cb.ble_ctr_cb.irk_list_mask[byte] |= (1 << bit); + return i; + } + i++; + } + + BTM_TRACE_ERROR ("%s failed, list full", __func__); + return i; +} + +/******************************************************************************* +** +** Function btm_ble_update_resolving_list +** +** Description update resolving list entry in host maintained record +** +** Returns void +** +*******************************************************************************/ +void btm_ble_update_resolving_list(BD_ADDR pseudo_bda, BOOLEAN add) +{ + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev(pseudo_bda); + if (p_dev_rec == NULL) { + return; + } + + if (add) { + p_dev_rec->ble.in_controller_list |= BTM_RESOLVING_LIST_BIT; + if (!controller_get_interface()->supports_ble_privacy()) { + p_dev_rec->ble.resolving_list_index = btm_ble_find_irk_index(); + } + } else { + p_dev_rec->ble.in_controller_list &= ~BTM_RESOLVING_LIST_BIT; + if (!controller_get_interface()->supports_ble_privacy()) { + /* clear IRK list index mask */ + btm_ble_clear_irk_index(p_dev_rec->ble.resolving_list_index); + p_dev_rec->ble.resolving_list_index = 0; + } + } +} + +/******************************************************************************* +** +** Function btm_ble_clear_resolving_list_complete +** +** Description This function is called when command complete for +** clear resolving list +** +** Returns void +** +*******************************************************************************/ +void btm_ble_clear_resolving_list_complete(UINT8 *p, UINT16 evt_len) +{ + UINT8 status = 0; + STREAM_TO_UINT8(status, p); + + BTM_TRACE_DEBUG("%s status=%d", __func__, status); + + if (status == HCI_SUCCESS) { + if (evt_len >= 3) { + /* VSC complete has one extra byte for op code and list size, skip it here */ + p ++; + + /* updated the available list size, and current list size */ + uint8_t irk_list_sz_max = 0; + STREAM_TO_UINT8(irk_list_sz_max, p); + + if (controller_get_interface()->get_ble_resolving_list_max_size() == 0) { + btm_ble_resolving_list_init(irk_list_sz_max); + } + + uint8_t irk_mask_size = (irk_list_sz_max % 8) ? + (irk_list_sz_max / 8 + 1) : (irk_list_sz_max / 8); + memset(btm_cb.ble_ctr_cb.irk_list_mask, 0, irk_mask_size); + } + + btm_cb.ble_ctr_cb.resolving_list_avail_size = + controller_get_interface()->get_ble_resolving_list_max_size(); + + BTM_TRACE_DEBUG("%s resolving_list_avail_size=%d", + __func__, btm_cb.ble_ctr_cb.resolving_list_avail_size); + + list_node_t *p_node = NULL; + tBTM_SEC_DEV_REC *p_dev_rec = NULL; + for (p_node = list_begin(btm_cb.p_sec_dev_rec_list); p_node; p_node = list_next(p_node)) { + p_dev_rec = list_node(p_node); + p_dev_rec->ble.in_controller_list &= ~BTM_RESOLVING_LIST_BIT; + } + } +} + +/******************************************************************************* +** +** Function btm_ble_add_resolving_list_entry_complete +** +** Description This function is called when command complete for +** add resolving list entry +** +** Returns void +** +*******************************************************************************/ +void btm_ble_add_resolving_list_entry_complete(UINT8 *p, UINT16 evt_len) +{ + UINT8 status; + STREAM_TO_UINT8(status, p); + + BTM_TRACE_DEBUG("%s status = %d", __func__, status); + + BD_ADDR pseudo_bda; + if (!btm_ble_deq_resolving_pending(pseudo_bda)) { + BTM_TRACE_DEBUG("no pending resolving list operation"); + return; + } + + if (status == HCI_SUCCESS) { + /* privacy 1.2 command complete does not have these extra byte */ + if (evt_len > 2) { + /* VSC complete has one extra byte for op code, skip it here */ + p ++; + STREAM_TO_UINT8(btm_cb.ble_ctr_cb.resolving_list_avail_size, p); + } else { + btm_cb.ble_ctr_cb.resolving_list_avail_size --; + } + } else if (status == HCI_ERR_MEMORY_FULL) { /* BT_ERROR_CODE_MEMORY_CAPACITY_EXCEEDED */ + btm_cb.ble_ctr_cb.resolving_list_avail_size = 0; + BTM_TRACE_ERROR("%s Resolving list Full ", __func__); + } else { + BTM_TRACE_ERROR("%s Add resolving list error %d ", __func__, status); + } + +} + +/******************************************************************************* +** +** Function btm_ble_remove_resolving_list_entry_complete +** +** Description This function is called when command complete for +** remove resolving list entry +** +** Returns void +** +*******************************************************************************/ +void btm_ble_remove_resolving_list_entry_complete(UINT8 *p, UINT16 evt_len) +{ + BD_ADDR pseudo_bda; + UINT8 status; + + STREAM_TO_UINT8(status, p); + + BTM_TRACE_DEBUG("%s status = %d", __func__, status); + + if (!btm_ble_deq_resolving_pending(pseudo_bda)) { + BTM_TRACE_ERROR("%s no pending resolving list operation", __func__); + return; + } + + if (status == HCI_SUCCESS) { + /* proprietary: spec does not have these extra bytes */ + if (evt_len > 2) { + p ++; /* skip opcode */ + STREAM_TO_UINT8(btm_cb.ble_ctr_cb.resolving_list_avail_size, p); + } else { + btm_cb.ble_ctr_cb.resolving_list_avail_size++; + } + } else { + BTM_TRACE_ERROR("%s remove resolving list error 0x%x", __func__, status); + } +} + +/******************************************************************************* +** +** Function btm_ble_read_resolving_list_entry_complete +** +** Description This function is called when command complete for +** remove resolving list entry +** +** Returns void +** +*******************************************************************************/ +void btm_ble_read_resolving_list_entry_complete(UINT8 *p, UINT16 evt_len) +{ + UINT8 status, rra_type = BTM_BLE_ADDR_PSEUDO; + BD_ADDR rra, pseudo_bda; + + STREAM_TO_UINT8 (status, p); + + BTM_TRACE_DEBUG("%s status = %d", __func__, status); + + if (!btm_ble_deq_resolving_pending(pseudo_bda)) { + BTM_TRACE_ERROR("no pending resolving list operation"); + return; + } + + if (status == HCI_SUCCESS) { + /* proprietary spec has extra bytes */ + if (evt_len > 8) { + p += (2 + 16 + 1 + 6); /* skip subcode, index, IRK value, address type, identity addr type */ + STREAM_TO_BDADDR(rra, p); + + BTM_TRACE_ERROR("%s peer_addr: %02x:%02x:%02x:%02x:%02x:%02x", + __func__, rra[0], rra[1], rra[2], rra[3], rra[4], rra[5]); + } else { + STREAM_TO_BDADDR(rra, p); + } + btm_ble_refresh_peer_resolvable_private_addr(pseudo_bda, rra, rra_type); + } +} + +/******************************************************************************* +** +** Function btm_ble_set_addr_resolution_enable_complete +** +** Description This function is called when the command to set address +** resolution enable completes. +** +** Parameters p: Pointer to the command complete event data. +** evt_len: Length of the event data. +** +** Returns void +** +*******************************************************************************/ +void btm_ble_set_addr_resolution_enable_complete(UINT8 *p, UINT16 evt_len) +{ + UINT8 status; + + STREAM_TO_UINT8(status, p); + + BTM_TRACE_DEBUG("%s status = %d", __func__, status); + + tBTM_LE_RANDOM_CB *random_cb = &btm_cb.ble_ctr_cb.addr_mgnt_cb; + + if (!(random_cb && random_cb->set_local_privacy_cback)) { + return; + } + + if (status == HCI_SUCCESS) { + random_cb->set_local_privacy_cback(BTM_SUCCESS); + return; + } else if (status == HCI_ERR_COMMAND_DISALLOWED) { + BTM_TRACE_ERROR("a non-connected activity is ongoing, such as advertising and scanning"); + } else { + BTM_TRACE_ERROR("set local privacy failed"); + } + random_cb->set_local_privacy_cback(BTM_ILLEGAL_VALUE); +} + +/******************************************************************************* + VSC that implement controller based privacy +********************************************************************************/ +/******************************************************************************* +** +** Function btm_ble_resolving_list_vsc_op_cmpl +** +** Description IRK operation VSC complete handler +** +** Parameters +** +** Returns void +** +*******************************************************************************/ +void btm_ble_resolving_list_vsc_op_cmpl (tBTM_VSC_CMPL *p_params) +{ + UINT8 *p = p_params->p_param_buf, op_subcode; + UINT16 evt_len = p_params->param_len; + + op_subcode = *(p + 1); + + BTM_TRACE_DEBUG("%s op_subcode = %d", __func__, op_subcode); + + if (op_subcode == BTM_BLE_META_CLEAR_IRK_LIST) { + btm_ble_clear_resolving_list_complete(p, evt_len); + } else if (op_subcode == BTM_BLE_META_ADD_IRK_ENTRY) { + btm_ble_add_resolving_list_entry_complete(p, evt_len); + } else if (op_subcode == BTM_BLE_META_REMOVE_IRK_ENTRY) { + btm_ble_remove_resolving_list_entry_complete(p, evt_len); + } else if (op_subcode == BTM_BLE_META_READ_IRK_ENTRY) { + btm_ble_read_resolving_list_entry_complete(p, evt_len); + } else if (op_subcode == BTM_BLE_META_IRK_ENABLE) { + /* RPA offloading enable/disabled */ + } +} + +/******************************************************************************* +** +** Function btm_ble_remove_resolving_list_entry +** +** Description This function to remove an IRK entry from the list +** +** Parameters ble_addr_type: address type +** ble_addr: LE adddress +** +** Returns status +** +*******************************************************************************/ +tBTM_STATUS btm_ble_remove_resolving_list_entry(tBTM_SEC_DEV_REC *p_dev_rec) +{ + /* if controller does not support RPA offloading or privacy 1.2, skip */ + if (controller_get_interface()->get_ble_resolving_list_max_size() == 0) { + return BTM_WRONG_MODE; + } + + tBTM_STATUS st = BTM_NO_RESOURCES; + if (controller_get_interface()->supports_ble_privacy()) { + #if CONTROLLER_RPA_LIST_ENABLE + if (btsnd_hcic_ble_rm_device_resolving_list(p_dev_rec->ble.static_addr_type, + p_dev_rec->ble.static_addr)) { + st = BTM_CMD_STARTED; + } + #else + // do nothing + /* It will cause that scanner doesn't send scan request to advertiser + * which has sent IRK to us and we have stored the IRK in controller. + * It is a hardware limitation. The preliminary solution is not to + * send key to the controller, but to resolve the random address in host. */ + #endif + } else { + UINT8 param[20] = {0}; + UINT8 *p = param; + + UINT8_TO_STREAM(p, BTM_BLE_META_REMOVE_IRK_ENTRY); + UINT8_TO_STREAM(p, p_dev_rec->ble.static_addr_type); + BDADDR_TO_STREAM(p, p_dev_rec->ble.static_addr); + + st = BTM_VendorSpecificCommand(HCI_VENDOR_BLE_RPA_VSC, + BTM_BLE_META_REMOVE_IRK_LEN, + param, + btm_ble_resolving_list_vsc_op_cmpl); + } + + if (st == BTM_CMD_STARTED) { + btm_ble_enq_resolving_list_pending( p_dev_rec->bd_addr, BTM_BLE_META_REMOVE_IRK_ENTRY); + } + + return st; +} + +/******************************************************************************* +** +** Function btm_ble_clear_resolving_list +** +** Description This function clears the resolving list +** +** Parameters None. +** +** Returns status +** +*******************************************************************************/ +tBTM_STATUS btm_ble_clear_resolving_list(void) +{ + tBTM_STATUS st = BTM_NO_RESOURCES; + + if (controller_get_interface()->supports_ble_privacy()) { + if (btsnd_hcic_ble_clear_resolving_list()) { + st = BTM_SUCCESS; + } + } else { + UINT8 param[20] = {0}; + UINT8 *p = param; + + UINT8_TO_STREAM(p, BTM_BLE_META_CLEAR_IRK_LIST); + st = BTM_VendorSpecificCommand (HCI_VENDOR_BLE_RPA_VSC, + BTM_BLE_META_CLEAR_IRK_LEN, + param, + btm_ble_resolving_list_vsc_op_cmpl); + } + + return st; +} + +/******************************************************************************* +** +** Function btm_ble_read_resolving_list_entry +** +** Description This function read an IRK entry by index +** +** Parameters entry index. +** +** Returns status +** +*******************************************************************************/ +tBTM_STATUS btm_ble_read_resolving_list_entry(tBTM_SEC_DEV_REC *p_dev_rec) +{ + tBTM_STATUS st = BTM_NO_RESOURCES; + + if (!(p_dev_rec->ble.in_controller_list & BTM_RESOLVING_LIST_BIT)) { + return BTM_WRONG_MODE; + } + + if (controller_get_interface()->supports_ble_privacy()) { + if (btsnd_hcic_ble_read_resolvable_addr_peer(p_dev_rec->ble.static_addr_type, + p_dev_rec->ble.static_addr)) { + st = BTM_CMD_STARTED; + } + } else { + UINT8 param[20] = {0}; + UINT8 *p = param; + + UINT8_TO_STREAM(p, BTM_BLE_META_READ_IRK_ENTRY); + UINT8_TO_STREAM(p, p_dev_rec->ble.resolving_list_index); + + st = BTM_VendorSpecificCommand (HCI_VENDOR_BLE_RPA_VSC, + BTM_BLE_META_READ_IRK_LEN, + param, + btm_ble_resolving_list_vsc_op_cmpl); + } + + if (st == BTM_CMD_STARTED) { + btm_ble_enq_resolving_list_pending(p_dev_rec->bd_addr, + BTM_BLE_META_READ_IRK_ENTRY); + } + + return st; +} + + +/******************************************************************************* +** +** Function btm_ble_suspend_resolving_list_activity +** +** Description This function suspends all resolving list activity, including +** scan, initiating, and advertising, if resolving list is being +** enabled. +** +** Parameters +** +** Returns TRUE if suspended; FALSE otherwise +** +*******************************************************************************/ +BOOLEAN btm_ble_suspend_resolving_list_activity(void) +{ + tBTM_BLE_CB *p_ble_cb = &btm_cb.ble_ctr_cb; + + /* if resolving list is not enabled, do not need to terminate any activity */ + /* if asking for stop all activity */ + /* if already suspended */ + if (p_ble_cb->suspended_rl_state != BTM_BLE_RL_IDLE) { + return TRUE; + } + + /* direct connection active, wait until it completed */ + if (btm_ble_get_conn_st() == BLE_DIR_CONN) { + BTM_TRACE_ERROR("resolving list can not be edited, EnQ now"); + return FALSE; + } + + p_ble_cb->suspended_rl_state = BTM_BLE_RL_IDLE; + + if (p_ble_cb->inq_var.adv_mode == BTM_BLE_ADV_ENABLE) { + btm_ble_stop_adv(); + p_ble_cb->suspended_rl_state |= BTM_BLE_RL_ADV; + } + + if (BTM_BLE_IS_SCAN_ACTIVE(p_ble_cb->scan_activity)) { + btm_ble_stop_scan(); + p_ble_cb->suspended_rl_state |= BTM_BLE_RL_SCAN; + } + + if (btm_ble_suspend_bg_conn()) { + p_ble_cb->suspended_rl_state |= BTM_BLE_RL_INIT; + } + + return TRUE; +} + +/******************************************************************************* +** +** Function btm_ble_resume_resolving_list_activity +** +** Description This function resumes the resolving list activity, including +** scanning, initiating, and advertising, if any of these +** activities has been suspended earlier. +** +** Returns none +** +*******************************************************************************/ +void btm_ble_resume_resolving_list_activity(void) +{ + tBTM_BLE_CB *p_ble_cb = &btm_cb.ble_ctr_cb; + + if (p_ble_cb->suspended_rl_state & BTM_BLE_RL_ADV) { + btm_ble_start_adv(); + } + + if (p_ble_cb->suspended_rl_state & BTM_BLE_RL_SCAN) { + btm_ble_start_scan(); + } + + if (p_ble_cb->suspended_rl_state & BTM_BLE_RL_INIT) { + btm_ble_resume_bg_conn(); + } + + p_ble_cb->suspended_rl_state = BTM_BLE_RL_IDLE; +} + +/******************************************************************************* +** +** Function btm_ble_vendor_enable_irk_feature +** +** Description This function is called to enable or disable the RRA +** offloading feature. +** +** Parameters enable: enable or disable the RRA offloading feature +** +** Returns BTM_SUCCESS if successful +** +*******************************************************************************/ +tBTM_STATUS btm_ble_vendor_enable_irk_feature(BOOLEAN enable) +{ + UINT8 param[20], *p; + tBTM_STATUS st = BTM_MODE_UNSUPPORTED; + + p = param; + memset(param, 0, 20); + + /* select feature based on control block settings */ + UINT8_TO_STREAM(p, BTM_BLE_META_IRK_ENABLE); + UINT8_TO_STREAM(p, enable ? 0x01 : 0x00); + + st = BTM_VendorSpecificCommand (HCI_VENDOR_BLE_RPA_VSC, BTM_BLE_IRK_ENABLE_LEN, + param, btm_ble_resolving_list_vsc_op_cmpl); + + return st; +} + +/******************************************************************************* +** +** Function btm_ble_exe_disable_resolving_list +** +** Description execute resolving list disable +** +** Returns none +** +*******************************************************************************/ +BOOLEAN btm_ble_exe_disable_resolving_list(void) +{ + if (!btm_ble_suspend_resolving_list_activity()) { + return FALSE; + } + + if (!controller_get_interface()->supports_ble_privacy()) { + btm_ble_vendor_enable_irk_feature(FALSE); + } else { + //btsnd_hcic_ble_set_addr_resolution_enable(FALSE); + } + + return TRUE; +} + +/******************************************************************************* +** +** Function btm_ble_exe_enable_resolving_list +** +** Description enable LE resolve address list +** +** Returns none +** +*******************************************************************************/ +void btm_ble_exe_enable_resolving_list(void) +{ + if (!btm_ble_suspend_resolving_list_activity()) { + return; + } + + if (!controller_get_interface()->supports_ble_privacy()) { + btm_ble_vendor_enable_irk_feature(TRUE); + } else { + //btsnd_hcic_ble_set_addr_resolution_enable(TRUE); + } +} + +/******************************************************************************* +** +** Function btm_ble_disable_resolving_list +** +** Description Disable LE Address resolution +** +** Returns none +** +*******************************************************************************/ +BOOLEAN btm_ble_disable_resolving_list(UINT8 rl_mask, BOOLEAN to_resume ) +{ + UINT8 rl_state = btm_cb.ble_ctr_cb.rl_state; + + /* if controller does not support RPA offloading or privacy 1.2, skip */ + if (controller_get_interface()->get_ble_resolving_list_max_size() == 0) { + return FALSE; + } + + btm_cb.ble_ctr_cb.rl_state &= ~rl_mask; + + if (rl_state != BTM_BLE_RL_IDLE && btm_cb.ble_ctr_cb.rl_state == BTM_BLE_RL_IDLE) { + if (btm_ble_exe_disable_resolving_list()) { + if (to_resume) { + btm_ble_resume_resolving_list_activity(); + } + + return TRUE; + } else { + return FALSE; + } + } + + return TRUE; +} + +/******************************************************************************* +** +** Function btm_ble_resolving_list_load_dev +** +** Description This function add a device which is using RPA into white list +** +** Parameters pointer to device security record +** +** Returns TRUE if device added, otherwise falase. +** +*******************************************************************************/ +BOOLEAN btm_ble_resolving_list_load_dev(tBTM_SEC_DEV_REC *p_dev_rec) +{ + BOOLEAN rt = FALSE; +#if (SMP_INCLUDED == TRUE) + UINT8 rl_mask = btm_cb.ble_ctr_cb.rl_state; + + BTM_TRACE_DEBUG("%s btm_cb.ble_ctr_cb.privacy_mode = %d\n", __func__, + btm_cb.ble_ctr_cb.privacy_mode); + + /* if controller does not support RPA offloading or privacy 1.2, skip */ + if (controller_get_interface()->get_ble_resolving_list_max_size() == 0) { + return FALSE; + } + + BTM_TRACE_DEBUG("%s btm_cb.ble_ctr_cb.privacy_mode = %d\n", + __func__, btm_cb.ble_ctr_cb.privacy_mode); + + /* only add RPA enabled device into resolving list */ + if (p_dev_rec != NULL && /* RPA is being used and PID is known */ + (p_dev_rec->sec_flags & BTM_SEC_IN_USE) != 0 && + ((p_dev_rec->ble.key_type & BTM_LE_KEY_PID) != 0 || + (p_dev_rec->ble.key_type & BTM_LE_KEY_LID) != 0)) { + if (!(p_dev_rec->ble.in_controller_list & BTM_RESOLVING_LIST_BIT) && + btm_ble_brcm_find_resolving_pending_entry(p_dev_rec->bd_addr, + BTM_BLE_META_ADD_IRK_ENTRY) == FALSE) { + if (btm_cb.ble_ctr_cb.resolving_list_avail_size > 0) { + if (rl_mask) { + if (!btm_ble_disable_resolving_list (rl_mask, FALSE)) { + return FALSE; + } + } + + btm_ble_update_resolving_list(p_dev_rec->bd_addr, TRUE); + if (controller_get_interface()->supports_ble_privacy()) { + BD_ADDR dummy_bda = {0}; + if (memcmp(p_dev_rec->ble.static_addr, dummy_bda, BD_ADDR_LEN) == 0) { + memcpy(p_dev_rec->ble.static_addr, p_dev_rec->bd_addr, BD_ADDR_LEN); + p_dev_rec->ble.static_addr_type = p_dev_rec->ble.ble_addr_type; + } + +#if CONTROLLER_RPA_LIST_ENABLE + BTM_TRACE_DEBUG("%s:adding device to controller resolving list\n", __func__); + UINT8 *peer_irk = p_dev_rec->ble.keys.irk; + UINT8 *local_irk = btm_cb.devcb.id_keys.irk; + //use identical IRK for now + rt = btsnd_hcic_ble_add_device_resolving_list(p_dev_rec->ble.static_addr_type, + p_dev_rec->ble.static_addr, peer_irk, local_irk); +#else + // do nothing + /* It will cause that scanner doesn't send scan request to advertiser + * which has sent IRK to us and we have stored the IRK in controller. + * It is a hardware limitation. The preliminary solution is not to + * send key to the controller, but to resolve the random address in host. */ + +#endif + + } else { + UINT8 param[40] = {0}; + UINT8 *p = param; + + UINT8_TO_STREAM(p, BTM_BLE_META_ADD_IRK_ENTRY); + ARRAY_TO_STREAM(p, p_dev_rec->ble.keys.irk, BT_OCTET16_LEN); + UINT8_TO_STREAM(p, p_dev_rec->ble.static_addr_type); + BDADDR_TO_STREAM(p, p_dev_rec->ble.static_addr); + + if (BTM_VendorSpecificCommand (HCI_VENDOR_BLE_RPA_VSC, + BTM_BLE_META_ADD_IRK_LEN, + param, + btm_ble_resolving_list_vsc_op_cmpl) + == BTM_CMD_STARTED) { + rt = TRUE; + } + } + + if (rt) { + btm_ble_enq_resolving_list_pending(p_dev_rec->bd_addr, + BTM_BLE_META_ADD_IRK_ENTRY); + } + + /* if resolving list has been turned on, re-enable it */ + // if (rl_mask) { + // btm_ble_enable_resolving_list(rl_mask); + // } else { + // btm_ble_enable_resolving_list(BTM_BLE_RL_INIT); + // } + } else { + BTM_TRACE_WARNING("%s Resolving list full ", __func__); + } + } else { + BTM_TRACE_DEBUG("Device already in Resolving list\n"); + rt = TRUE; + } + } else { + BTM_TRACE_DEBUG("Device not a RPA enabled device\n"); + } +#endif ///SMP_INCLUDED == TRUE + return rt; +} + +/******************************************************************************* +** +** Function btm_ble_resolving_list_remove_dev +** +** Description This function removes the device from resolving list +** +** Parameters +** +** Returns status +** +*******************************************************************************/ +void btm_ble_resolving_list_remove_dev(tBTM_SEC_DEV_REC *p_dev_rec) +{ + UINT8 rl_mask = btm_cb.ble_ctr_cb.rl_state; + + BTM_TRACE_EVENT ("%s\n", __func__); + if (rl_mask) { + if (!btm_ble_disable_resolving_list (rl_mask, FALSE)) { + return; + } + } + + if ((p_dev_rec->ble.in_controller_list & BTM_RESOLVING_LIST_BIT) && + btm_ble_brcm_find_resolving_pending_entry(p_dev_rec->bd_addr, + BTM_BLE_META_REMOVE_IRK_ENTRY) == FALSE) { + btm_ble_update_resolving_list( p_dev_rec->bd_addr, FALSE); + btm_ble_remove_resolving_list_entry(p_dev_rec); + } else { + BTM_TRACE_DEBUG("Device not in resolving list\n"); + } + + /* if resolving list has been turned on, re-enable it */ + // if (rl_mask) { + // btm_ble_enable_resolving_list(rl_mask); + // } +} + +/******************************************************************************* +** +** Function btm_ble_enable_resolving_list +** +** Description enable LE resolve address list +** +** Returns none +** +*******************************************************************************/ +void btm_ble_enable_resolving_list(UINT8 rl_mask) +{ + UINT8 rl_state = btm_cb.ble_ctr_cb.rl_state; + + btm_cb.ble_ctr_cb.rl_state |= rl_mask; + if (rl_state == BTM_BLE_RL_IDLE && + btm_cb.ble_ctr_cb.rl_state != BTM_BLE_RL_IDLE && + controller_get_interface()->get_ble_resolving_list_max_size() != 0) { + btm_ble_exe_enable_resolving_list(); + btm_ble_resume_resolving_list_activity(); + } +} + +#if 0 //Unused +/******************************************************************************* +** +** Function btm_ble_resolving_list_empty +** +** Description check to see if resoving list is empty or not +** +** Returns TRUE: empty; FALSE non-empty +** +*******************************************************************************/ +BOOLEAN btm_ble_resolving_list_empty(void) +{ + return (controller_get_interface()->get_ble_resolving_list_max_size() == + btm_cb.ble_ctr_cb.resolving_list_avail_size); +} +#endif + +/******************************************************************************* +** +** Function btm_ble_enable_resolving_list_for_platform +** +** Description enable/disable resolving list feature depending on if any +** resolving list is empty and whitelist is involoved in the +** operation. +** +** Returns none +** +*******************************************************************************/ +void btm_ble_enable_resolving_list_for_platform (UINT8 rl_mask) +{ + /* if controller does not support, skip */ + if (controller_get_interface()->get_ble_resolving_list_max_size() == 0) { + return; + } + + if (btm_cb.ble_ctr_cb.wl_state == BTM_BLE_WL_IDLE) { + if (controller_get_interface()->get_ble_resolving_list_max_size() > + btm_cb.ble_ctr_cb.resolving_list_avail_size) { + btm_ble_enable_resolving_list(rl_mask); + } else { + btm_ble_disable_resolving_list(rl_mask, TRUE); + } + return; + } + + tBTM_SEC_DEV_REC *p_dev = NULL; + list_node_t *p_node = NULL; + for (p_node = list_begin(btm_cb.p_sec_dev_rec_list); p_node; p_node = list_next(p_node)) { + p_dev = list_node(p_node); + if ((p_dev->ble.in_controller_list & BTM_RESOLVING_LIST_BIT) && + (p_dev->ble.in_controller_list & BTM_WHITE_LIST_BIT)) { + btm_ble_enable_resolving_list(rl_mask); + return; + } + } + btm_ble_disable_resolving_list(rl_mask, TRUE); +} + +/******************************************************************************* +** +** Function btm_ble_resolving_list_init +** +** Description Initialize resolving list in host stack +** +** Parameters Max resolving list size +** +** Returns void +** +*******************************************************************************/ +void btm_ble_resolving_list_init(UINT8 max_irk_list_sz) +{ + tBTM_BLE_RESOLVE_Q *p_q = &btm_cb.ble_ctr_cb.resolving_list_pend_q; + UINT8 irk_mask_size = (max_irk_list_sz % 8) ? + (max_irk_list_sz / 8 + 1) : (max_irk_list_sz / 8); + + if (max_irk_list_sz > 0) { + p_q->resolve_q_random_pseudo = (BD_ADDR *)osi_malloc(sizeof(BD_ADDR) * max_irk_list_sz); + p_q->resolve_q_action = (UINT8 *)osi_malloc(max_irk_list_sz); + + /* RPA offloading feature */ + if (btm_cb.ble_ctr_cb.irk_list_mask == NULL) { + btm_cb.ble_ctr_cb.irk_list_mask = (UINT8 *)osi_malloc(irk_mask_size); + } + + BTM_TRACE_DEBUG ("%s max_irk_list_sz = %d", __func__, max_irk_list_sz); + } + + controller_get_interface()->set_ble_resolving_list_max_size(max_irk_list_sz); + btm_ble_clear_resolving_list(); + btm_cb.ble_ctr_cb.resolving_list_avail_size = max_irk_list_sz; +} + +/******************************************************************************* +** +** Function btm_ble_resolving_list_cleanup +** +** Description Cleanup resolving list dynamic memory +** +** Parameters +** +** Returns void +** +*******************************************************************************/ +void btm_ble_resolving_list_cleanup(void) +{ + tBTM_BLE_RESOLVE_Q *p_q = &btm_cb.ble_ctr_cb.resolving_list_pend_q; + + if (p_q->resolve_q_random_pseudo) { + osi_free(p_q->resolve_q_random_pseudo); + p_q->resolve_q_random_pseudo = NULL; + } + + if (p_q->resolve_q_action) { + osi_free(p_q->resolve_q_action); + p_q->resolve_q_action = NULL; + } + + controller_get_interface()->set_ble_resolving_list_max_size(0); + if (btm_cb.ble_ctr_cb.irk_list_mask) { + osi_free(btm_cb.ble_ctr_cb.irk_list_mask); + btm_cb.ble_ctr_cb.irk_list_mask = NULL; + } + +} + +void btm_ble_add_default_entry_to_resolving_list(void) +{ + /* + * Add local IRK entry with 00:00:00:00:00:00 address. This entry will + * be used to generate RPA for non-directed advertising if own_addr_type + * is set to rpa_pub since we use all-zero address as peer addres in + * such case. Peer IRK should be left all-zero since this is not for an + * actual peer. + */ + BD_ADDR peer_addr = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0}; + BT_OCTET16 peer_irk = {0x0}; + + btsnd_hcic_ble_add_device_resolving_list (BLE_ADDR_PUBLIC, peer_addr, peer_irk, btm_cb.devcb.id_keys.irk); +} +#endif diff --git a/lib/bt/host/bluedroid/stack/btm/btm_dev.c b/lib/bt/host/bluedroid/stack/btm/btm_dev.c new file mode 100644 index 00000000..f9e3ed2b --- /dev/null +++ b/lib/bt/host/bluedroid/stack/btm/btm_dev.c @@ -0,0 +1,741 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains functions for the Bluetooth Device Manager + * + ******************************************************************************/ + +#include +#include +//#include +#include + +#include "stack/bt_types.h" +#include "device/controller.h" +#include "stack/hcimsgs.h" +#include "stack/btu.h" +#include "stack/btm_api.h" +#include "btm_int.h" +#include "stack/hcidefs.h" +#include "stack/l2c_api.h" + +static tBTM_SEC_DEV_REC *btm_find_oldest_dev (void); + +/******************************************************************************* +** +** Function BTM_SecAddDevice +** +** Description Add/modify device. This function will be normally called +** during host startup to restore all required information +** stored in the NVRAM. +** +** Parameters: bd_addr - BD address of the peer +** dev_class - Device Class +** bd_name - Name of the peer device. NULL if unknown. +** features - Remote device's features (up to 3 pages). NULL if not known +** trusted_mask - Bitwise OR of services that do not +** require authorization. (array of UINT32) +** link_key - Connection link key. NULL if unknown. +** +** Returns TRUE if added OK, else FALSE +** +*******************************************************************************/ +BOOLEAN BTM_SecAddDevice (BD_ADDR bd_addr, DEV_CLASS dev_class, BD_NAME bd_name, + UINT8 *features, UINT32 trusted_mask[], + LINK_KEY link_key, UINT8 key_type, tBTM_IO_CAP io_cap, + UINT8 pin_length, UINT8 sc_support) +{ +#if (SMP_INCLUDED == TRUE) + tBTM_SEC_DEV_REC *p_dev_rec; + int i, j; + BOOLEAN found = FALSE; + + BTM_TRACE_API("%s, link key type:%x\n", __FUNCTION__, key_type); + p_dev_rec = btm_find_dev (bd_addr); + if (!p_dev_rec) { + /* There is no device record, allocate one. + * If we can not find an empty spot for this one, let it fail. */ + if (list_length(btm_cb.p_sec_dev_rec_list) < BTM_SEC_MAX_DEVICE_RECORDS) { + p_dev_rec = (tBTM_SEC_DEV_REC *)osi_malloc(sizeof(tBTM_SEC_DEV_REC)); + if(p_dev_rec) { + list_append(btm_cb.p_sec_dev_rec_list, p_dev_rec); + /* Mark this record as in use and initialize */ + memset (p_dev_rec, 0, sizeof (tBTM_SEC_DEV_REC)); + p_dev_rec->sec_flags = BTM_SEC_IN_USE; + memcpy (p_dev_rec->bd_addr, bd_addr, BD_ADDR_LEN); + p_dev_rec->hci_handle = BTM_GetHCIConnHandle (bd_addr, BT_TRANSPORT_BR_EDR); + p_dev_rec->ble_hci_handle = BTM_GetHCIConnHandle (bd_addr, BT_TRANSPORT_LE); + p_dev_rec->enc_mode = BTM_ENC_MODE_UNKNOWN; + +#if BLE_INCLUDED == TRUE + /* use default value for background connection params */ + /* update conn params, use default value for background connection params */ + memset(&p_dev_rec->conn_params, 0xff, sizeof(tBTM_LE_CONN_PRAMS)); +#endif + } + } + + if (!p_dev_rec) { + return (FALSE); + } + } + p_dev_rec->bond_type = BOND_TYPE_UNKNOWN; /* Default value */ + p_dev_rec->timestamp = btm_cb.dev_rec_count++; + p_dev_rec->remote_secure_connection_previous_state = sc_support; + + if (dev_class) { + memcpy (p_dev_rec->dev_class, dev_class, DEV_CLASS_LEN); + } + + memset(p_dev_rec->sec_bd_name, 0, sizeof(tBTM_BD_NAME)); + + if (bd_name && bd_name[0]) { + p_dev_rec->sec_flags |= BTM_SEC_NAME_KNOWN; + BCM_STRNCPY_S ((char *)p_dev_rec->sec_bd_name, (char *)bd_name, BTM_MAX_REM_BD_NAME_LEN); + } + + p_dev_rec->num_read_pages = 0; + if (features) { + memcpy (p_dev_rec->features, features, sizeof (p_dev_rec->features)); + for (i = HCI_EXT_FEATURES_PAGE_MAX; i >= 0; i--) { + for (j = 0; j < HCI_FEATURE_BYTES_PER_PAGE; j++) { + if (p_dev_rec->features[i][j] != 0) { + found = TRUE; + break; + } + } + if (found) { + p_dev_rec->num_read_pages = i + 1; + break; + } + } + } else { + memset (p_dev_rec->features, 0, sizeof (p_dev_rec->features)); + } + + BTM_SEC_COPY_TRUSTED_DEVICE(trusted_mask, p_dev_rec->trusted_mask); + + if (link_key) { + BTM_TRACE_EVENT ("BTM_SecAddDevice() BDA: %02x:%02x:%02x:%02x:%02x:%02x\n", + bd_addr[0], bd_addr[1], bd_addr[2], + bd_addr[3], bd_addr[4], bd_addr[5]); + p_dev_rec->sec_flags |= BTM_SEC_LINK_KEY_KNOWN; + memcpy (p_dev_rec->link_key, link_key, LINK_KEY_LEN); + p_dev_rec->link_key_type = key_type; + p_dev_rec->pin_code_length = pin_length; + + if (pin_length >= 16 || + key_type == BTM_LKEY_TYPE_AUTH_COMB || + key_type == BTM_LKEY_TYPE_AUTH_COMB_P_256) { + // Set the fiag if the link key was made by using either a 16 digit + // pin or MITM. + p_dev_rec->sec_flags |= BTM_SEC_16_DIGIT_PIN_AUTHED; + } + } + +#if defined(BTIF_MIXED_MODE_INCLUDED) && (BTIF_MIXED_MODE_INCLUDED == TRUE) + if (key_type < BTM_MAX_PRE_SM4_LKEY_TYPE) { + p_dev_rec->sm4 = BTM_SM4_KNOWN; + } else { + p_dev_rec->sm4 = BTM_SM4_TRUE; + } +#endif + + p_dev_rec->rmt_io_caps = io_cap; + p_dev_rec->device_type |= BT_DEVICE_TYPE_BREDR; +#endif ///SMP_INCLUDED == TRUE + return (TRUE); +} + + +/******************************************************************************* +** +** Function BTM_SecDeleteDevice +** +** Description Free resources associated with the device. +** +** Parameters: bd_addr - BD address of the peer +** transport - BT_TRANSPORT_BR_EDR or BT_TRANSPORT_LE +** +** Returns TRUE if removed OK, FALSE if not found or ACL link is active +** +*******************************************************************************/ +BOOLEAN BTM_SecDeleteDevice (BD_ADDR bd_addr, tBT_TRANSPORT transport) +{ + + tBTM_SEC_DEV_REC *p_dev_rec; + + if (BTM_IsAclConnectionUp(bd_addr, transport)) { + BTM_TRACE_WARNING("%s FAILED: Cannot Delete when connection is active\n", __func__); + return FALSE; + } + if ((p_dev_rec = btm_find_dev(bd_addr)) != NULL) { + /* Tell controller to get rid of the link key, if it has one stored */ + BTM_DeleteStoredLinkKey (p_dev_rec->bd_addr, NULL); + + btm_sec_free_dev(p_dev_rec, transport); + } + + return TRUE; +} + +/******************************************************************************* +** +** Function BTM_SecClearSecurityFlags +** +** Description Reset the security flags (mark as not-paired) for a given +** remove device. +** +*******************************************************************************/ +extern void BTM_SecClearSecurityFlags (BD_ADDR bd_addr) +{ + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev(bd_addr); + if (p_dev_rec == NULL) { + return; + } + + p_dev_rec->sec_flags = 0; + p_dev_rec->sec_state = BTM_SEC_STATE_IDLE; + p_dev_rec->sm4 = BTM_SM4_UNKNOWN; +} + + +/******************************************************************************* +** +** Function BTM_SecReadDevName +** +** Description Looks for the device name in the security database for the +** specified BD address. +** +** Returns Pointer to the name or NULL +** +*******************************************************************************/ +char *BTM_SecReadDevName (BD_ADDR bd_addr) +{ + char *p_name = NULL; + tBTM_SEC_DEV_REC *p_srec; + + if ((p_srec = btm_find_dev(bd_addr)) != NULL) { + p_name = (char *)p_srec->sec_bd_name; + } + + return (p_name); +} + + +/******************************************************************************* +** +** Function btm_find_sec_dev_in_list +** +** Description Look for the record in the device database for the record +** with specified address +** +** Returns Pointer to the record or NULL +** +*******************************************************************************/ +BOOLEAN btm_find_sec_dev_in_list (void *p_node_data, void *context) +{ + tBTM_SEC_DEV_REC *p_sec_dev = (tBTM_SEC_DEV_REC *)p_node_data; + BOOLEAN ret = TRUE; + BOOLEAN dev_free = !(p_sec_dev->sec_flags & BTM_SEC_IN_USE); + tSecDevContext *p_context = (tSecDevContext *)context; + + if (dev_free == p_context->free_check) { + switch (p_context->type) { + case SEC_DEV_BDA: + if (!memcmp(p_context->context.p_bd_addr, p_sec_dev->bd_addr, BD_ADDR_LEN)) { + ret = FALSE; + } + break; + case SEC_DEV_HDL: + if (p_context->context.handle == p_sec_dev->hci_handle +#if BLE_INCLUDED == TRUE + || (p_context->context.handle == p_sec_dev->ble_hci_handle) +#endif + ) { + ret = FALSE; + } + break; +#if BLE_PRIVACY_SPT == TRUE + case SEC_DEV_ID_ADDR: + if (!memcmp(p_context->context.p_bd_addr, p_sec_dev->ble.static_addr, BD_ADDR_LEN)) { + ret = FALSE; + } + break; +#endif //BLE_PRIVACY_SPT == TRUE + case SEC_DEV_BTDM_BDA: + if (!memcmp(p_context->context.p_bd_addr, p_sec_dev->bd_addr, BD_ADDR_LEN)) { + ret = FALSE; + } +#if BLE_INCLUDED == TRUE + // If a LE random address is looking for device record + if (!memcmp(p_sec_dev->ble.pseudo_addr, p_context->context.p_bd_addr, BD_ADDR_LEN)) { + ret = FALSE; + } + + if (btm_ble_addr_resolvable(p_context->context.p_bd_addr, p_sec_dev)) { + ret = FALSE; + } +#endif + break; + default: + break; + } + } + return ret; +} + +/******************************************************************************* +** +** Function btm_sec_alloc_dev +** +** Description Look for the record in the device database for the record +** with specified address +** +** Returns Pointer to the record or NULL +** +*******************************************************************************/ +tBTM_SEC_DEV_REC *btm_sec_alloc_dev (BD_ADDR bd_addr) +{ + tBTM_SEC_DEV_REC *p_dev_rec = NULL; + tBTM_SEC_DEV_REC *p_dev_new_rec = NULL; + tBTM_SEC_DEV_REC *p_dev_old_rec = NULL; + tBTM_INQ_INFO *p_inq_info; + list_node_t *p_node = NULL; + BOOLEAN new_entry_found = FALSE; + BOOLEAN old_entry_found = FALSE; + BOOLEAN malloc_new_entry = FALSE; + BTM_TRACE_EVENT ("btm_sec_alloc_dev\n"); + for (p_node = list_begin(btm_cb.p_sec_dev_rec_list); p_node; p_node = list_next(p_node)) { + p_dev_old_rec = list_node(p_node); + /* look for old entry which match the bd_addr and the BTM_SEC_IN_USE is cleared */ + if (!(p_dev_old_rec->sec_flags & BTM_SEC_IN_USE) && + (!memcmp (p_dev_old_rec->bd_addr, bd_addr, BD_ADDR_LEN))) { + old_entry_found = TRUE; + BTM_TRACE_EVENT ("btm_sec_alloc_dev old device found\n"); + break; + } + } + for (p_node = list_begin(btm_cb.p_sec_dev_rec_list); p_node; p_node = list_next(p_node)) { + p_dev_new_rec = list_node(p_node); + /* find the first entry whose BTM_SEC_IN_USE is cleared */ + if (!(p_dev_new_rec->sec_flags & BTM_SEC_IN_USE)) { + new_entry_found = TRUE; + break; + } + } + if (!new_entry_found) { + /* We can not find new device. We need malloc a new one if p_sec_dev_rec_list is not full */ + if (list_length(btm_cb.p_sec_dev_rec_list) < BTM_SEC_MAX_DEVICE_RECORDS){ + p_dev_new_rec = (tBTM_SEC_DEV_REC *)osi_malloc(sizeof(tBTM_SEC_DEV_REC)); + if (p_dev_new_rec) { + new_entry_found = TRUE; + malloc_new_entry = TRUE; + } else { + return NULL; + } + } + } + if (!new_entry_found) { + p_dev_rec = btm_find_oldest_dev(); + } else { + /* if the old device entry not present go with new entry */ + if (old_entry_found) { + p_dev_rec = p_dev_old_rec; + if (malloc_new_entry) { + osi_free(p_dev_new_rec); + } + } else { + if (malloc_new_entry) { + list_append(btm_cb.p_sec_dev_rec_list, p_dev_new_rec); + } + p_dev_rec = p_dev_new_rec; + } + } + memset (p_dev_rec, 0, sizeof (tBTM_SEC_DEV_REC)); + + p_dev_rec->bond_type = BOND_TYPE_UNKNOWN; /* Default value */ + p_dev_rec->sec_flags = BTM_SEC_IN_USE; + + /* Check with the BT manager if details about remote device are known */ + /* outgoing connection */ + if ((p_inq_info = BTM_InqDbRead(bd_addr)) != NULL) { + memcpy (p_dev_rec->dev_class, p_inq_info->results.dev_class, DEV_CLASS_LEN); + +#if BLE_INCLUDED == TRUE + p_dev_rec->device_type = p_inq_info->results.device_type; + p_dev_rec->ble.ble_addr_type = p_inq_info->results.ble_addr_type; + + /* update conn params, use default value for background connection params */ + memset(&p_dev_rec->conn_params, 0xff, sizeof(tBTM_LE_CONN_PRAMS)); +#endif + } else { +#if BLE_INCLUDED == TRUE + /* update conn params, use default value for background connection params */ + memset(&p_dev_rec->conn_params, 0xff, sizeof(tBTM_LE_CONN_PRAMS)); +#endif + + if (!memcmp (bd_addr, btm_cb.connecting_bda, BD_ADDR_LEN)) { + memcpy (p_dev_rec->dev_class, btm_cb.connecting_dc, DEV_CLASS_LEN); + } + } + + memcpy (p_dev_rec->bd_addr, bd_addr, BD_ADDR_LEN); + +#if BLE_INCLUDED == TRUE + p_dev_rec->ble_hci_handle = BTM_GetHCIConnHandle (bd_addr, BT_TRANSPORT_LE); +#endif + p_dev_rec->hci_handle = BTM_GetHCIConnHandle (bd_addr, BT_TRANSPORT_BR_EDR); + p_dev_rec->timestamp = btm_cb.dev_rec_count++; + + return (p_dev_rec); +} + + +/******************************************************************************* +** +** Function btm_sec_free_dev +** +** Description Mark device record as not used +** +*******************************************************************************/ +void btm_sec_free_dev (tBTM_SEC_DEV_REC *p_dev_rec, tBT_TRANSPORT transport) +{ + if (transport == BT_TRANSPORT_BR_EDR) { + memset(p_dev_rec->link_key, 0, LINK_KEY_LEN); + p_dev_rec->sec_flags &= ~(BTM_SEC_AUTHORIZED | BTM_SEC_AUTHENTICATED + | BTM_SEC_ENCRYPTED | BTM_SEC_NAME_KNOWN + | BTM_SEC_LINK_KEY_KNOWN | BTM_SEC_LINK_KEY_AUTHED + | BTM_SEC_ROLE_SWITCHED | BTM_SEC_16_DIGIT_PIN_AUTHED); + } else if (transport == BT_TRANSPORT_LE) { + p_dev_rec->bond_type = BOND_TYPE_UNKNOWN; + p_dev_rec->sec_flags &= ~(BTM_SEC_LE_AUTHENTICATED | BTM_SEC_LE_ENCRYPTED + | BTM_SEC_LE_NAME_KNOWN | BTM_SEC_LE_LINK_KEY_KNOWN + | BTM_SEC_LE_LINK_KEY_AUTHED | BTM_SEC_ROLE_SWITCHED); +#if BLE_INCLUDED == TRUE + /* Clear out any saved BLE keys */ + btm_sec_clear_ble_keys (p_dev_rec); +#endif + } else { + p_dev_rec->bond_type = BOND_TYPE_UNKNOWN; + memset(p_dev_rec->link_key, 0, LINK_KEY_LEN); + p_dev_rec->sec_flags = 0; + +#if BLE_INCLUDED == TRUE + /* Clear out any saved BLE keys */ + btm_sec_clear_ble_keys (p_dev_rec); +#endif + } + /* No BLE keys and BT keys, clear the sec_flags */ + if(p_dev_rec->sec_flags == BTM_SEC_IN_USE) { + p_dev_rec->sec_flags = 0; + } + list_remove(btm_cb.p_sec_dev_rec_list, p_dev_rec); +} + +/******************************************************************************* +** +** Function btm_dev_support_switch +** +** Description This function is called by the L2CAP to check if remote +** device supports role switch +** +** Parameters: bd_addr - Address of the peer device +** +** Returns TRUE if device is known and role switch is supported +** +*******************************************************************************/ +BOOLEAN btm_dev_support_switch (BD_ADDR bd_addr) +{ + tBTM_SEC_DEV_REC *p_dev_rec; + UINT8 xx; + BOOLEAN feature_empty = TRUE; + +#if BTM_SCO_INCLUDED == TRUE + /* Role switch is not allowed if a SCO is up */ + if (btm_is_sco_active_by_bdaddr(bd_addr)) { + return (FALSE); + } +#endif + p_dev_rec = btm_find_dev (bd_addr); + if (p_dev_rec && controller_get_interface()->supports_master_slave_role_switch()) { + if (HCI_SWITCH_SUPPORTED(p_dev_rec->features[HCI_EXT_FEATURES_PAGE_0])) { + BTM_TRACE_DEBUG("btm_dev_support_switch return TRUE (feature found)\n"); + return (TRUE); + } + + /* If the feature field is all zero, we never received them */ + for (xx = 0 ; xx < BD_FEATURES_LEN ; xx++) { + if (p_dev_rec->features[HCI_EXT_FEATURES_PAGE_0][xx] != 0x00) { + feature_empty = FALSE; /* at least one is != 0 */ + break; + } + } + + /* If we don't know peer's capabilities, assume it supports Role-switch */ + if (feature_empty) { + BTM_TRACE_DEBUG("btm_dev_support_switch return TRUE (feature empty)\n"); + return (TRUE); + } + } + + BTM_TRACE_DEBUG("btm_dev_support_switch return FALSE\n"); + return (FALSE); +} + +/******************************************************************************* +** +** Function btm_find_dev_by_handle +** +** Description Look for the record in the device database for the record +** with specified handle +** +** Returns Pointer to the record or NULL +** +*******************************************************************************/ +tBTM_SEC_DEV_REC *btm_find_dev_by_handle (UINT16 handle) +{ + tBTM_SEC_DEV_REC *p_dev_rec = NULL; + list_node_t *p_node = NULL; + tSecDevContext context; + context.type = SEC_DEV_HDL; + context.context.handle = handle; + context.free_check = FALSE; + + p_node = list_foreach(btm_cb.p_sec_dev_rec_list, btm_find_sec_dev_in_list, &context); + if (p_node) { + p_dev_rec = list_node(p_node); + } + return (p_dev_rec); +} +/******************************************************************************* +** +** Function btm_find_dev +** +** Description Look for the record in the device database for the record +** with specified BD address +** +** Returns Pointer to the record or NULL +** +*******************************************************************************/ +tBTM_SEC_DEV_REC *btm_find_dev(BD_ADDR bd_addr) +{ + if(bd_addr) { + list_node_t *p_node = NULL; + tSecDevContext context; + context.type = SEC_DEV_BTDM_BDA; + context.context.p_bd_addr = bd_addr; + context.free_check = FALSE; + p_node = list_foreach(btm_cb.p_sec_dev_rec_list, btm_find_sec_dev_in_list, &context); + if (p_node) { + return(list_node(p_node)); + } + } + return (NULL); +} +/******************************************************************************* +** +** Function btm_consolidate_dev +** +** Description combine security records if identified as same peer +** +** Returns none +** +*******************************************************************************/ +void btm_consolidate_dev(tBTM_SEC_DEV_REC *p_target_rec) +{ +#if BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE + tBTM_SEC_DEV_REC *p_dev_rec = NULL; + tBTM_SEC_DEV_REC temp_rec = *p_target_rec; + list_node_t *p_node = NULL; + BTM_TRACE_DEBUG("%s\n", __func__); + + for (p_node = list_begin(btm_cb.p_sec_dev_rec_list); p_node; p_node = list_next(p_node)) { + p_dev_rec = list_node(p_node); + if (p_target_rec != p_dev_rec && p_dev_rec->sec_flags & BTM_SEC_IN_USE) { + if (!memcmp (p_dev_rec->bd_addr, p_target_rec->bd_addr, BD_ADDR_LEN)) { + memcpy(p_target_rec, p_dev_rec, sizeof(tBTM_SEC_DEV_REC)); + p_target_rec->ble = temp_rec.ble; + p_target_rec->ble_hci_handle = temp_rec.ble_hci_handle; + p_target_rec->enc_key_size = temp_rec.enc_key_size; + p_target_rec->conn_params = temp_rec.conn_params; + p_target_rec->device_type |= temp_rec.device_type; + p_target_rec->sec_flags |= temp_rec.sec_flags; + + p_target_rec->new_encryption_key_is_p256 = temp_rec.new_encryption_key_is_p256; + p_target_rec->no_smp_on_br = temp_rec.no_smp_on_br; + p_target_rec->bond_type = temp_rec.bond_type; + /* Remove the unused device from the list */ + list_remove(btm_cb.p_sec_dev_rec_list, p_dev_rec); + break; + } + + /* an RPA device entry is a duplicate of the target record */ + if (btm_ble_addr_resolvable(p_dev_rec->bd_addr, p_target_rec)) { + if (memcmp(p_target_rec->ble.pseudo_addr, p_dev_rec->bd_addr, BD_ADDR_LEN) == 0) { + p_target_rec->ble.ble_addr_type = p_dev_rec->ble.ble_addr_type; + p_target_rec->device_type |= p_dev_rec->device_type; + /* Remove the unused device from the list */ + list_remove(btm_cb.p_sec_dev_rec_list, p_dev_rec); + } + break; + } + } + } +#endif +} + +/******************************************************************************* +** +** Function btm_find_or_alloc_dev +** +** Description Look for the record in the device database for the record +** with specified BD address +** +** Returns Pointer to the record or NULL +** +*******************************************************************************/ +tBTM_SEC_DEV_REC *btm_find_or_alloc_dev (BD_ADDR bd_addr) +{ + tBTM_SEC_DEV_REC *p_dev_rec; + BTM_TRACE_EVENT ("btm_find_or_alloc_dev\n"); + if ((p_dev_rec = btm_find_dev (bd_addr)) == NULL) { + + /* Allocate a new device record or reuse the oldest one */ + p_dev_rec = btm_sec_alloc_dev (bd_addr); + } + return (p_dev_rec); +} + +/******************************************************************************* +** +** Function btm_find_oldest_dev +** +** Description Locates the oldest device in use. It first looks for +** the oldest non-paired device. If all devices are paired it +** deletes the oldest paired device. +** +** Returns Pointer to the record or NULL +** +*******************************************************************************/ +tBTM_SEC_DEV_REC *btm_find_oldest_dev (void) +{ + tBTM_SEC_DEV_REC *p_dev_rec = NULL; + tBTM_SEC_DEV_REC *p_oldest = NULL; + list_node_t *p_node = NULL; + UINT32 ot = 0xFFFFFFFF; + + /* First look for the non-paired devices for the oldest entry */ + for (p_node = list_begin(btm_cb.p_sec_dev_rec_list); p_node; p_node = list_next(p_node)) { + p_dev_rec = list_node(p_node); + if (((p_dev_rec->sec_flags & BTM_SEC_IN_USE) == 0) + || ((p_dev_rec->sec_flags & (BTM_SEC_LINK_KEY_KNOWN | BTM_SEC_LE_LINK_KEY_KNOWN)) != 0)) { + continue; /* Device is paired so skip it */ + } + + if (p_dev_rec->timestamp < ot) { + p_oldest = p_dev_rec; + ot = p_dev_rec->timestamp; + } + } + + if (ot != 0xFFFFFFFF) { + return (p_oldest); + } + + /* All devices are paired; find the oldest */ + for (p_node = list_begin(btm_cb.p_sec_dev_rec_list); p_node; p_node = list_next(p_node)) { + if ((p_dev_rec->sec_flags & BTM_SEC_IN_USE) == 0) { + continue; + } + + if (p_dev_rec->timestamp < ot) { + p_oldest = p_dev_rec; + ot = p_dev_rec->timestamp; + } + } + return (p_oldest); +} +/******************************************************************************* +** +** Function btm_get_bond_type_dev +** +** Description Get the bond type for a device in the device database +** with specified BD address +** +** Returns The device bond type if known, otherwise BOND_TYPE_UNKNOWN +** +*******************************************************************************/ +tBTM_BOND_TYPE btm_get_bond_type_dev(BD_ADDR bd_addr) +{ + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev(bd_addr); + + if (p_dev_rec == NULL) { + return BOND_TYPE_UNKNOWN; + } + + return p_dev_rec->bond_type; +} + +/******************************************************************************* +** +** Function btm_set_bond_type_dev +** +** Description Set the bond type for a device in the device database +** with specified BD address +** +** Returns TRUE on success, otherwise FALSE +** +*******************************************************************************/ +BOOLEAN btm_set_bond_type_dev(BD_ADDR bd_addr, tBTM_BOND_TYPE bond_type) +{ + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev(bd_addr); + + if (p_dev_rec == NULL) { + return FALSE; + } + + p_dev_rec->bond_type = bond_type; + return TRUE; +} + +/******************************************************************************* +** +** Function btm_sec_dev_init +** +** Description Create new linked list for dynamic allocation on sec_dev_rec +** +*******************************************************************************/ +void btm_sec_dev_init(void) +{ + btm_cb.p_sec_dev_rec_list = list_new(osi_free_func); +} + +/******************************************************************************* +** +** Function btm_sec_dev_free +** +** Description Delete sec_dev_rec list when btm_cb is being released +** +*******************************************************************************/ +void btm_sec_dev_free(void) +{ + list_free(btm_cb.p_sec_dev_rec_list); +} diff --git a/lib/bt/host/bluedroid/stack/btm/btm_devctl.c b/lib/bt/host/bluedroid/stack/btm/btm_devctl.c new file mode 100644 index 00000000..bffb5bbe --- /dev/null +++ b/lib/bt/host/bluedroid/stack/btm/btm_devctl.c @@ -0,0 +1,1294 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains functions that handle BTM interface functions for the + * Bluetooth device including Rest, HCI buffer size and others + * + ******************************************************************************/ + +#include +#include +//#include +#include +#include "common/bt_trace.h" +#include "stack/bt_types.h" +//#include "bt_utils.h" +#include "btm_int.h" +#include "stack/btu.h" +#include "device/controller.h" +#include "hci/hci_layer.h" +#include "stack/hcimsgs.h" +#include "l2c_int.h" +//#include "btcore/include/module.h" +//#include "osi/include/osi/thread.h" + +#if BLE_INCLUDED == TRUE +#include "gatt_int.h" +#endif /* BLE_INCLUDED */ + +//extern thread_t *bt_workqueue_thread; + +/********************************************************************************/ +/* L O C A L D A T A D E F I N I T I O N S */ +/********************************************************************************/ + +#ifndef BTM_DEV_RESET_TIMEOUT +#define BTM_DEV_RESET_TIMEOUT 4 +#endif + +#define BTM_DEV_REPLY_TIMEOUT 2 /* 1 second expiration time is not good. Timer may start between 0 and 1 second. */ +/* if it starts at the very end of the 0 second, timer will expire really easily. */ + +#define BTM_INFO_TIMEOUT 5 /* 5 seconds for info response */ + +/********************************************************************************/ +/* L O C A L F U N C T I O N P R O T O T Y P E S */ +/********************************************************************************/ + +static void btm_decode_ext_features_page (UINT8 page_number, const BD_FEATURES p_features); + +/******************************************************************************* +** +** Function btm_dev_init +** +** Description This function is on the BTM startup +** +** Returns void +** +*******************************************************************************/ +void btm_dev_init (void) +{ +#if 0 /* cleared in btm_init; put back in if called from anywhere else! */ + memset (&btm_cb.devcb, 0, sizeof (tBTM_DEVCB)); +#endif + + /* Initialize nonzero defaults */ +#if (BTM_MAX_LOC_BD_NAME_LEN > 0) + memset(btm_cb.cfg.bd_name, 0, sizeof(tBTM_LOC_BD_NAME)); +#endif + + btm_cb.devcb.reset_timer.param = (TIMER_PARAM_TYPE)TT_DEV_RESET; + btm_cb.devcb.rln_timer.param = (TIMER_PARAM_TYPE)TT_DEV_RLN; + + btm_cb.btm_acl_pkt_types_supported = BTM_ACL_PKT_TYPES_MASK_DH1 + BTM_ACL_PKT_TYPES_MASK_DM1 + + BTM_ACL_PKT_TYPES_MASK_DH3 + BTM_ACL_PKT_TYPES_MASK_DM3 + + BTM_ACL_PKT_TYPES_MASK_DH5 + BTM_ACL_PKT_TYPES_MASK_DM5; + + btm_cb.btm_sco_pkt_types_supported = BTM_SCO_PKT_TYPES_MASK_HV1 + + BTM_SCO_PKT_TYPES_MASK_HV2 + + BTM_SCO_PKT_TYPES_MASK_HV3 + + BTM_SCO_PKT_TYPES_MASK_EV3 + + BTM_SCO_PKT_TYPES_MASK_EV4 + + BTM_SCO_PKT_TYPES_MASK_EV5; +} + + +/******************************************************************************* +** +** Function btm_db_reset +** +** Description This function is called by BTM_DeviceReset and clears out any +** pending callbacks for inquiries, discoveries, other pending +** functions that may be in progress. +** +** Returns void +** +*******************************************************************************/ +static void btm_db_reset (void) +{ + tBTM_CMPL_CB *p_cb; + tBTM_STATUS status = BTM_DEV_RESET; + + btm_inq_db_reset(); + + if (btm_cb.devcb.p_rln_cmpl_cb) { + p_cb = btm_cb.devcb.p_rln_cmpl_cb; + btm_cb.devcb.p_rln_cmpl_cb = NULL; + + if (p_cb) { + (*p_cb)((void *) NULL); + } + } + + if (btm_cb.devcb.p_rssi_cmpl_cb) { + p_cb = btm_cb.devcb.p_rssi_cmpl_cb; + btm_cb.devcb.p_rssi_cmpl_cb = NULL; + + if (p_cb) { + (*p_cb)((tBTM_RSSI_RESULTS *) &status); + } + } +} + +static void reset_complete(void) +{ + const controller_t *controller = controller_get_interface(); + + /* Tell L2CAP that all connections are gone */ + l2cu_device_reset (); +#if (SMP_INCLUDED == TRUE) + /* Clear current security state */ + { + list_node_t *p_node = NULL; + for (p_node = list_begin(btm_cb.p_sec_dev_rec_list); p_node; p_node = list_next(p_node)) { + tBTM_SEC_DEV_REC *p_dev_rec = (tBTM_SEC_DEV_REC *) list_node(p_node); + p_dev_rec->sec_state = BTM_SEC_STATE_IDLE; + } + } +#endif ///SMP_INCLUDED == TRUE + /* After the reset controller should restore all parameters to defaults. */ + btm_cb.btm_inq_vars.inq_counter = 1; + btm_cb.btm_inq_vars.inq_scan_window = HCI_DEF_INQUIRYSCAN_WINDOW; + btm_cb.btm_inq_vars.inq_scan_period = HCI_DEF_INQUIRYSCAN_INTERVAL; + btm_cb.btm_inq_vars.inq_scan_type = HCI_DEF_SCAN_TYPE; + + btm_cb.btm_inq_vars.page_scan_window = HCI_DEF_PAGESCAN_WINDOW; + btm_cb.btm_inq_vars.page_scan_period = HCI_DEF_PAGESCAN_INTERVAL; + btm_cb.btm_inq_vars.page_scan_type = HCI_DEF_SCAN_TYPE; + btm_cb.btm_inq_vars.page_timeout = HCI_DEFAULT_PAGE_TOUT; + +#if (BLE_INCLUDED == TRUE) + btm_cb.ble_ctr_cb.conn_state = BLE_CONN_IDLE; + btm_cb.ble_ctr_cb.bg_conn_type = BTM_BLE_CONN_NONE; + btm_cb.ble_ctr_cb.p_select_cback = NULL; + gatt_reset_bgdev_list(); + btm_ble_multi_adv_init(); +#endif + + btm_pm_reset(); + + l2c_link_processs_num_bufs(controller->get_acl_buffer_count_classic()); +#if BTM_SCO_HCI_INCLUDED == TRUE + btm_sco_process_num_bufs(controller->get_sco_buffer_count()); +#endif +#if (BLE_INCLUDED == TRUE) + +#if (defined BLE_PRIVACY_SPT && BLE_PRIVACY_SPT == TRUE) + /* Set up the BLE privacy settings */ + if (controller->supports_ble() && controller->supports_ble_privacy() && + controller->get_ble_resolving_list_max_size() > 0) { + btm_ble_resolving_list_init(controller->get_ble_resolving_list_max_size()); + /* set the default random private address timeout */ + btsnd_hcic_ble_set_rand_priv_addr_timeout(BTM_BLE_PRIVATE_ADDR_INT); + } +#endif + + if (controller->supports_ble()) { + btm_ble_white_list_init(controller->get_ble_white_list_size()); + l2c_link_processs_ble_num_bufs(controller->get_acl_buffer_count_ble()); + } +#endif +#if (SMP_INCLUDED == TRUE && CLASSIC_BT_INCLUDED == TRUE) + BTM_SetPinType (btm_cb.cfg.pin_type, btm_cb.cfg.pin_code, btm_cb.cfg.pin_code_len); +#endif ///SMP_INCLUDED == TRUE && CLASSIC_BT_INCLUDED == TRUE +#if (CLASSIC_BT_INCLUDED == TRUE) + BTM_WritePageTimeout(btm_cb.btm_inq_vars.page_timeout, NULL); +#endif ///CLASSIC_BT_INCLUDED == TRUE + for (int i = 0; i <= controller->get_last_features_classic_index(); i++) { + btm_decode_ext_features_page(i, controller->get_features_classic(i)->as_array); + } + + btm_report_device_status(BTM_DEV_STATUS_UP); +} + +// TODO(zachoverflow): remove this function +void BTM_DeviceReset (UNUSED_ATTR tBTM_CMPL_CB *p_cb) +{ + /* Flush all ACL connections */ + btm_acl_device_down(); + + /* Clear the callback, so application would not hang on reset */ + btm_db_reset(); + + /* todo: review the below logic; start_up executes under another task context + * reset_complete runs in btu task */ + controller_get_interface()->start_up(); + reset_complete(); +} + +/******************************************************************************* +** +** Function BTM_IsDeviceUp +** +** Description This function is called to check if the device is up. +** +** Returns TRUE if device is up, else FALSE +** +*******************************************************************************/ +BOOLEAN BTM_IsDeviceUp (void) +{ + return controller_get_interface()->get_is_ready(); +} + +/******************************************************************************* +** +** Function btm_dev_timeout +** +** Description This function is called when a timer list entry expires. +** +** Returns void +** +*******************************************************************************/ +void btm_dev_timeout (TIMER_LIST_ENT *p_tle) +{ + TIMER_PARAM_TYPE timer_type = (TIMER_PARAM_TYPE)p_tle->param; + + if (timer_type == (TIMER_PARAM_TYPE)TT_DEV_RLN) { + tBTM_CMPL_CB *p_cb = btm_cb.devcb.p_rln_cmpl_cb; + + btm_cb.devcb.p_rln_cmpl_cb = NULL; + + if (p_cb) { + (*p_cb)((void *) NULL); + } + } +} + +/******************************************************************************* +** +** Function btm_decode_ext_features_page +** +** Description This function is decodes a features page. +** +** Returns void +** +*******************************************************************************/ +static void btm_decode_ext_features_page (UINT8 page_number, const BD_FEATURES p_features) +{ + BTM_TRACE_DEBUG ("btm_decode_ext_features_page page: %d", page_number); + switch (page_number) { + /* Extended (Legacy) Page 0 */ + case HCI_EXT_FEATURES_PAGE_0: + + /* Create ACL supported packet types mask */ + btm_cb.btm_acl_pkt_types_supported = (BTM_ACL_PKT_TYPES_MASK_DH1 + + BTM_ACL_PKT_TYPES_MASK_DM1); + + if (HCI_3_SLOT_PACKETS_SUPPORTED(p_features)) { + btm_cb.btm_acl_pkt_types_supported |= (BTM_ACL_PKT_TYPES_MASK_DH3 + + BTM_ACL_PKT_TYPES_MASK_DM3); + } + + if (HCI_5_SLOT_PACKETS_SUPPORTED(p_features)) { + btm_cb.btm_acl_pkt_types_supported |= (BTM_ACL_PKT_TYPES_MASK_DH5 + + BTM_ACL_PKT_TYPES_MASK_DM5); + } + + /* Add in EDR related ACL types */ + if (!HCI_EDR_ACL_2MPS_SUPPORTED(p_features)) { + btm_cb.btm_acl_pkt_types_supported |= (BTM_ACL_PKT_TYPES_MASK_NO_2_DH1 + + BTM_ACL_PKT_TYPES_MASK_NO_2_DH3 + + BTM_ACL_PKT_TYPES_MASK_NO_2_DH5); + } + + if (!HCI_EDR_ACL_3MPS_SUPPORTED(p_features)) { + btm_cb.btm_acl_pkt_types_supported |= (BTM_ACL_PKT_TYPES_MASK_NO_3_DH1 + + BTM_ACL_PKT_TYPES_MASK_NO_3_DH3 + + BTM_ACL_PKT_TYPES_MASK_NO_3_DH5); + } + + /* Check to see if 3 and 5 slot packets are available */ + if (HCI_EDR_ACL_2MPS_SUPPORTED(p_features) || + HCI_EDR_ACL_3MPS_SUPPORTED(p_features)) { + if (!HCI_3_SLOT_EDR_ACL_SUPPORTED(p_features)) { + btm_cb.btm_acl_pkt_types_supported |= (BTM_ACL_PKT_TYPES_MASK_NO_2_DH3 + + BTM_ACL_PKT_TYPES_MASK_NO_3_DH3); + } + + if (!HCI_5_SLOT_EDR_ACL_SUPPORTED(p_features)) { + btm_cb.btm_acl_pkt_types_supported |= (BTM_ACL_PKT_TYPES_MASK_NO_2_DH5 + + BTM_ACL_PKT_TYPES_MASK_NO_3_DH5); + } + } + + BTM_TRACE_DEBUG("Local supported ACL packet types: 0x%04x", + btm_cb.btm_acl_pkt_types_supported); + + /* Create (e)SCO supported packet types mask */ + btm_cb.btm_sco_pkt_types_supported = 0; +#if BTM_SCO_INCLUDED == TRUE + btm_cb.sco_cb.esco_supported = FALSE; +#endif + if (HCI_SCO_LINK_SUPPORTED(p_features)) { + btm_cb.btm_sco_pkt_types_supported = BTM_SCO_PKT_TYPES_MASK_HV1; + + if (HCI_HV2_PACKETS_SUPPORTED(p_features)) { + btm_cb.btm_sco_pkt_types_supported |= BTM_SCO_PKT_TYPES_MASK_HV2; + } + + if (HCI_HV3_PACKETS_SUPPORTED(p_features)) { + btm_cb.btm_sco_pkt_types_supported |= BTM_SCO_PKT_TYPES_MASK_HV3; + } + } + + if (HCI_ESCO_EV3_SUPPORTED(p_features)) { + btm_cb.btm_sco_pkt_types_supported |= BTM_SCO_PKT_TYPES_MASK_EV3; + } + + if (HCI_ESCO_EV4_SUPPORTED(p_features)) { + btm_cb.btm_sco_pkt_types_supported |= BTM_SCO_PKT_TYPES_MASK_EV4; + } + + if (HCI_ESCO_EV5_SUPPORTED(p_features)) { + btm_cb.btm_sco_pkt_types_supported |= BTM_SCO_PKT_TYPES_MASK_EV5; + } +#if BTM_SCO_INCLUDED == TRUE + if (btm_cb.btm_sco_pkt_types_supported & BTM_ESCO_LINK_ONLY_MASK) { + btm_cb.sco_cb.esco_supported = TRUE; + + /* Add in EDR related eSCO types */ + if (HCI_EDR_ESCO_2MPS_SUPPORTED(p_features)) { + if (!HCI_3_SLOT_EDR_ESCO_SUPPORTED(p_features)) { + btm_cb.btm_sco_pkt_types_supported |= BTM_SCO_PKT_TYPES_MASK_NO_2_EV5; + } + } else { + btm_cb.btm_sco_pkt_types_supported |= (BTM_SCO_PKT_TYPES_MASK_NO_2_EV3 + + BTM_SCO_PKT_TYPES_MASK_NO_2_EV5); + } + + if (HCI_EDR_ESCO_3MPS_SUPPORTED(p_features)) { + if (!HCI_3_SLOT_EDR_ESCO_SUPPORTED(p_features)) { + btm_cb.btm_sco_pkt_types_supported |= BTM_SCO_PKT_TYPES_MASK_NO_3_EV5; + } + } else { + btm_cb.btm_sco_pkt_types_supported |= (BTM_SCO_PKT_TYPES_MASK_NO_3_EV3 + + BTM_SCO_PKT_TYPES_MASK_NO_3_EV5); + } + } +#endif + + BTM_TRACE_DEBUG("Local supported SCO packet types: 0x%04x", + btm_cb.btm_sco_pkt_types_supported); + + /* Create Default Policy Settings */ + if (HCI_SWITCH_SUPPORTED(p_features)) { + btm_cb.btm_def_link_policy |= HCI_ENABLE_MASTER_SLAVE_SWITCH; + } else { + btm_cb.btm_def_link_policy &= ~HCI_ENABLE_MASTER_SLAVE_SWITCH; + } + + if (HCI_HOLD_MODE_SUPPORTED(p_features)) { + btm_cb.btm_def_link_policy |= HCI_ENABLE_HOLD_MODE; + } else { + btm_cb.btm_def_link_policy &= ~HCI_ENABLE_HOLD_MODE; + } + + if (HCI_SNIFF_MODE_SUPPORTED(p_features)) { + btm_cb.btm_def_link_policy |= HCI_ENABLE_SNIFF_MODE; + } else { + btm_cb.btm_def_link_policy &= ~HCI_ENABLE_SNIFF_MODE; + } + + if (HCI_PARK_MODE_SUPPORTED(p_features)) { + btm_cb.btm_def_link_policy |= HCI_ENABLE_PARK_MODE; + } else { + btm_cb.btm_def_link_policy &= ~HCI_ENABLE_PARK_MODE; + } + + btm_sec_dev_reset (); + + if (HCI_LMP_INQ_RSSI_SUPPORTED(p_features)) { + if (HCI_EXT_INQ_RSP_SUPPORTED(p_features)) { + BTM_SetInquiryMode (BTM_INQ_RESULT_EXTENDED); + } else { + BTM_SetInquiryMode (BTM_INQ_RESULT_WITH_RSSI); + } + } + +#if L2CAP_NON_FLUSHABLE_PB_INCLUDED == TRUE + if ( HCI_NON_FLUSHABLE_PB_SUPPORTED(p_features)) { + l2cu_set_non_flushable_pbf(TRUE); + } else { + l2cu_set_non_flushable_pbf(FALSE); + } +#endif + BTM_SetPageScanType (BTM_DEFAULT_SCAN_TYPE); + BTM_SetInquiryScanType (BTM_DEFAULT_SCAN_TYPE); + + break; + + /* Extended Page 1 */ + case HCI_EXT_FEATURES_PAGE_1: + /* Nothing to do for page 1 */ + break; + + /* Extended Page 2 */ + case HCI_EXT_FEATURES_PAGE_2: + /* Nothing to do for page 2 */ + break; + + default: + BTM_TRACE_ERROR("btm_decode_ext_features_page page=%d unknown", page_number); + break; + } +} + +/******************************************************************************* +** +** Function BTM_SetLocalDeviceName +** +** Description This function is called to set the local device name. +** +** Returns status of the operation +** +*******************************************************************************/ +tBTM_STATUS BTM_SetLocalDeviceName (char *p_name) +{ + UINT8 *p; + + if (!p_name || !p_name[0] || (strlen ((char *)p_name) > BD_NAME_LEN)) { + return (BTM_ILLEGAL_VALUE); + } + + if (!controller_get_interface()->get_is_ready()) { + return (BTM_DEV_RESET); + } + +#if BTM_MAX_LOC_BD_NAME_LEN > 0 + /* Save the device name if local storage is enabled */ + p = (UINT8 *)btm_cb.cfg.bd_name; + if (p != (UINT8 *)p_name) { + BCM_STRNCPY_S(btm_cb.cfg.bd_name, p_name, BTM_MAX_LOC_BD_NAME_LEN); + btm_cb.cfg.bd_name[BTM_MAX_LOC_BD_NAME_LEN] = '\0'; + } +#else + p = (UINT8 *)p_name; +#endif +#if CLASSIC_BT_INCLUDED + if (btsnd_hcic_change_name(p)) { + return (BTM_CMD_STARTED); + } else +#endif + { + return (BTM_NO_RESOURCES); + } +} + + + +/******************************************************************************* +** +** Function BTM_ReadLocalDeviceName +** +** Description This function is called to read the local device name. +** +** Returns status of the operation +** If success, BTM_SUCCESS is returned and p_name points stored +** local device name +** If BTM doesn't store local device name, BTM_NO_RESOURCES is +** is returned and p_name is set to NULL +** +*******************************************************************************/ +tBTM_STATUS BTM_ReadLocalDeviceName (char **p_name) +{ +#if BTM_MAX_LOC_BD_NAME_LEN > 0 + *p_name = btm_cb.cfg.bd_name; + return (BTM_SUCCESS); +#else + *p_name = NULL; + return (BTM_NO_RESOURCES); +#endif +} + + +/******************************************************************************* +** +** Function BTM_ReadLocalDeviceNameFromController +** +** Description Get local device name from controller. Do not use cached +** name (used to get chip-id prior to btm reset complete). +** +** Returns BTM_CMD_STARTED if successful, otherwise an error +** +*******************************************************************************/ +tBTM_STATUS BTM_ReadLocalDeviceNameFromController (tBTM_CMPL_CB *p_rln_cmpl_cback) +{ + /* Check if rln already in progress */ + if (btm_cb.devcb.p_rln_cmpl_cb) { + return (BTM_NO_RESOURCES); + } + + /* Save callback */ + btm_cb.devcb.p_rln_cmpl_cb = p_rln_cmpl_cback; + + btsnd_hcic_read_name(); + btu_start_timer (&btm_cb.devcb.rln_timer, BTU_TTYPE_BTM_DEV_CTL, BTM_DEV_REPLY_TIMEOUT); + + return BTM_CMD_STARTED; +} + +/******************************************************************************* +** +** Function btm_read_local_name_complete +** +** Description This function is called when local name read complete. +** message is received from the HCI. +** +** Returns void +** +*******************************************************************************/ +void btm_read_local_name_complete (UINT8 *p, UINT16 evt_len) +{ + tBTM_CMPL_CB *p_cb = btm_cb.devcb.p_rln_cmpl_cb; + UINT8 status; + UNUSED(evt_len); + + btu_free_timer (&btm_cb.devcb.rln_timer); + + /* If there was a callback address for read local name, call it */ + btm_cb.devcb.p_rln_cmpl_cb = NULL; + + if (p_cb) { + STREAM_TO_UINT8 (status, p); + + if (status == HCI_SUCCESS) { + (*p_cb)(p); + } else { + (*p_cb)(NULL); + } + } +} + +/******************************************************************************* +** +** Function BTM_SetDeviceClass +** +** Description This function is called to set the local device class +** +** Returns status of the operation +** +*******************************************************************************/ +tBTM_STATUS BTM_SetDeviceClass (DEV_CLASS dev_class) +{ + if (!memcmp (btm_cb.devcb.dev_class, dev_class, DEV_CLASS_LEN)) { + return (BTM_SUCCESS); + } + + memcpy (btm_cb.devcb.dev_class, dev_class, DEV_CLASS_LEN); + + if (!controller_get_interface()->get_is_ready()) { + return (BTM_DEV_RESET); + } + + if (!btsnd_hcic_write_dev_class (dev_class)) { + return (BTM_NO_RESOURCES); + } + + return (BTM_SUCCESS); +} + + +/******************************************************************************* +** +** Function BTM_ReadDeviceClass +** +** Description This function is called to read the local device class +** +** Returns pointer to the device class +** +*******************************************************************************/ +UINT8 *BTM_ReadDeviceClass (void) +{ + return ((UINT8 *)btm_cb.devcb.dev_class); +} + + +/******************************************************************************* +** +** Function BTM_ReadLocalFeatures +** +** Description This function is called to read the local features +** +** Returns pointer to the local features string +** +*******************************************************************************/ +// TODO(zachoverflow): get rid of this function +UINT8 *BTM_ReadLocalFeatures (void) +{ + // Discarding const modifier for now, until this function dies + return (UINT8 *)controller_get_interface()->get_features_classic(0)->as_array; +} + +/******************************************************************************* +** +** Function BTM_RegisterForDeviceStatusNotif +** +** Description This function is called to register for device status +** change notifications. +** +** If one registration is already there calling function should +** save the pointer to the function that is return and +** call it when processing of the event is complete +** +** Returns status of the operation +** +*******************************************************************************/ +tBTM_DEV_STATUS_CB *BTM_RegisterForDeviceStatusNotif (tBTM_DEV_STATUS_CB *p_cb) +{ + tBTM_DEV_STATUS_CB *p_prev = btm_cb.devcb.p_dev_status_cb; + + btm_cb.devcb.p_dev_status_cb = p_cb; + return (p_prev); +} + +/******************************************************************************* +** +** Function BTM_VendorSpecificCommand +** +** Description Send a vendor specific HCI command to the controller. +** +** Returns +** BTM_SUCCESS Command sent. Does not expect command complete +** event. (command cmpl callback param is NULL) +** BTM_CMD_STARTED Command sent. Waiting for command cmpl event. +** +** Notes +** Opcode will be OR'd with HCI_GRP_VENDOR_SPECIFIC. +** +*******************************************************************************/ +tBTM_STATUS BTM_VendorSpecificCommand(UINT16 opcode, UINT8 param_len, + UINT8 *p_param_buf, tBTM_VSC_CMPL_CB *p_cb) +{ + BT_HDR *p_buf; + + BTM_TRACE_EVENT ("BTM: BTM_VendorSpecificCommand: Opcode: 0x%04X, ParamLen: %i.", + opcode, param_len); + + /* Allocate a buffer to hold HCI command plus the callback function */ + if ((p_buf = HCI_GET_CMD_BUF(param_len)) != NULL) { + /* Send the HCI command (opcode will be OR'd with HCI_GRP_VENDOR_SPECIFIC) */ + btsnd_hcic_vendor_spec_cmd (p_buf, opcode, param_len, p_param_buf, (void *)p_cb); + + /* Return value */ + if (p_cb != NULL) { + return (BTM_CMD_STARTED); + } else { + return (BTM_SUCCESS); + } + } else { + return (BTM_NO_RESOURCES); + } + +} + +#if (ESP_COEX_VSC_INCLUDED == TRUE) +tBTM_STATUS BTM_ConfigCoexStatus(tBTM_COEX_OPERATION op, tBTM_COEX_TYPE type, UINT8 status) +{ + UINT8 param[3]; + UINT8 *p = (UINT8 *)param; + + UINT8_TO_STREAM(p, type); + UINT8_TO_STREAM(p, op); + UINT8_TO_STREAM(p, status); + + return BTM_VendorSpecificCommand(HCI_VENDOR_COMMON_COEX_STATUS_CMD_OPCODE, 3, param, NULL); +} +#endif + +/******************************************************************************* +** +** Function btm_vsc_complete +** +** Description This function is called when local HCI Vendor Specific +** Command complete message is received from the HCI. +** +** Returns void +** +*******************************************************************************/ +void btm_vsc_complete (UINT8 *p, UINT16 opcode, UINT16 evt_len, + tBTM_CMPL_CB *p_vsc_cplt_cback) +{ +#if (BLE_INCLUDED == TRUE) + tBTM_BLE_CB *ble_cb = &btm_cb.ble_ctr_cb; + switch(opcode) { + case HCI_VENDOR_BLE_LONG_ADV_DATA: + BTM_TRACE_EVENT("Set long adv data complete\n"); + break; + case HCI_VENDOR_BLE_UPDATE_DUPLICATE_EXCEPTIONAL_LIST: { + uint8_t subcode, status; uint32_t length; + STREAM_TO_UINT8(status, p); + STREAM_TO_UINT8(subcode, p); + STREAM_TO_UINT32(length, p); + if(ble_cb && ble_cb->update_exceptional_list_cmp_cb) { + (*ble_cb->update_exceptional_list_cmp_cb)(status, subcode, length, p); + } + break; + } + case HCI_VENDOR_BLE_CLEAR_ADV: { + uint8_t status; + STREAM_TO_UINT8(status, p); + if (ble_cb && ble_cb->inq_var.p_clear_adv_cb) { + ble_cb->inq_var.p_clear_adv_cb(status); + } + break; + } + default: + break; + } + tBTM_VSC_CMPL vcs_cplt_params; + + /* If there was a callback address for vcs complete, call it */ + if (p_vsc_cplt_cback) { + /* Pass paramters to the callback function */ + vcs_cplt_params.opcode = opcode; /* Number of bytes in return info */ + vcs_cplt_params.param_len = evt_len; /* Number of bytes in return info */ + vcs_cplt_params.p_param_buf = p; + (*p_vsc_cplt_cback)(&vcs_cplt_params); /* Call the VSC complete callback function */ + } +#endif +} + +/******************************************************************************* +** +** Function BTM_RegisterForVSEvents +** +** Description This function is called to register/deregister for vendor +** specific HCI events. +** +** If is_register=TRUE, then the function will be registered; +** if is_register=FALSE, then the function will be deregistered. +** +** Returns BTM_SUCCESS if successful, +** BTM_BUSY if maximum number of callbacks have already been +** registered. +** +*******************************************************************************/ +tBTM_STATUS BTM_RegisterForVSEvents (tBTM_VS_EVT_CB *p_cb, BOOLEAN is_register) +{ + tBTM_STATUS retval = BTM_SUCCESS; + UINT8 i, free_idx = BTM_MAX_VSE_CALLBACKS; + + /* See if callback is already registered */ + for (i = 0; i < BTM_MAX_VSE_CALLBACKS; i++) { + if (btm_cb.devcb.p_vend_spec_cb[i] == NULL) { + /* Found a free slot. Store index */ + free_idx = i; + } else if (btm_cb.devcb.p_vend_spec_cb[i] == p_cb) { + /* Found callback in lookup table. If deregistering, clear the entry. */ + if (is_register == FALSE) { + btm_cb.devcb.p_vend_spec_cb[i] = NULL; + BTM_TRACE_EVENT("BTM Deregister For VSEvents is successfully"); + } + return (BTM_SUCCESS); + } + } + + /* Didn't find callback. Add callback to free slot if registering */ + if (is_register) { + if (free_idx < BTM_MAX_VSE_CALLBACKS) { + btm_cb.devcb.p_vend_spec_cb[free_idx] = p_cb; + BTM_TRACE_EVENT("BTM Register For VSEvents is successfully"); + } else { + /* No free entries available */ + BTM_TRACE_ERROR ("BTM_RegisterForVSEvents: too many callbacks registered"); + + retval = BTM_NO_RESOURCES; + } + } + + return (retval); +} + +/******************************************************************************* +** +** Function btm_vendor_specific_evt +** +** Description Process event HCI_VENDOR_SPECIFIC_EVT +** +** Note: Some controllers do not send command complete, so +** the callback and busy flag are cleared here also. +** +** Returns void +** +*******************************************************************************/ +void btm_vendor_specific_evt (UINT8 *p, UINT8 evt_len) +{ + UINT8 i; + +#if (CLASSIC_BT_INCLUDED == TRUE) + UINT8 sub_event; + UINT8 *p_evt = p; + + STREAM_TO_UINT8(sub_event, p_evt); + /* Check in subevent if authentication is through Legacy Authentication. */ + if (sub_event == ESP_VS_REM_LEGACY_AUTH_CMP) { + UINT16 hci_handle; + STREAM_TO_UINT16(hci_handle, p_evt); + btm_sec_handle_remote_legacy_auth_cmp(hci_handle); + } +#endif /// (CLASSIC_BT_INCLUDED == TRUE) + for (i = 0; i < BTM_MAX_VSE_CALLBACKS; i++) { + if (btm_cb.devcb.p_vend_spec_cb[i]) { + (*btm_cb.devcb.p_vend_spec_cb[i])(evt_len, p); + } + } + BTM_TRACE_DEBUG ("BTM Event: Vendor Specific event from controller"); +} + +#if (CLASSIC_BT_INCLUDED == TRUE) +/******************************************************************************* +** +** Function BTM_WritePageTimeout +** +** Description Send HCI Write Page Timeout. +** +** Returns +** BTM_SUCCESS Command sent. +** BTM_NO_RESOURCES If out of resources to send the command. +** +** +*******************************************************************************/ +tBTM_STATUS BTM_WritePageTimeout(UINT16 timeout, tBTM_CMPL_CB *p_cb) +{ + BTM_TRACE_EVENT ("BTM: BTM_WritePageTimeout: Timeout: %d.", timeout); + + if (timeout >= HCI_MIN_PAGE_TOUT) { + btm_cb.btm_inq_vars.page_timeout = timeout; + } + btm_cb.devcb.p_page_to_set_cmpl_cb = p_cb; + + /* Send the HCI command */ + if (!btsnd_hcic_write_page_tout (timeout)) { + return (BTM_NO_RESOURCES); + } + + if (p_cb) { + btu_start_timer(&btm_cb.devcb.page_timeout_set_timer, BTU_TTYPE_BTM_SET_PAGE_TO, BTM_DEV_REPLY_TIMEOUT); + } + + return (BTM_CMD_STARTED); +} + +/******************************************************************************* +** +** Function btm_set_page_timeout_complete +** +** Description This function is called when setting page timeout complete. +** message is received from the HCI. +** +** Returns void +** +*******************************************************************************/ +void btm_set_page_timeout_complete (const UINT8 *p) +{ + tBTM_SET_PAGE_TIMEOUT_RESULTS results; + + tBTM_CMPL_CB *p_cb = btm_cb.devcb.p_page_to_set_cmpl_cb; + btm_cb.devcb.p_page_to_set_cmpl_cb = NULL; + btu_free_timer (&btm_cb.devcb.page_timeout_set_timer); + + /* If there is a callback address for setting page timeout, call it */ + if (p_cb) { + if (p) { + STREAM_TO_UINT8 (results.hci_status, p); + switch (results.hci_status) { + case HCI_SUCCESS: + results.status = BTM_SUCCESS; + break; + case HCI_ERR_UNSUPPORTED_VALUE: + case HCI_ERR_ILLEGAL_PARAMETER_FMT: + results.status = BTM_ILLEGAL_VALUE; + break; + default: + results.status = BTM_NO_RESOURCES; + break; + } + } else { + results.hci_status = HCI_ERR_HOST_TIMEOUT; + results.status = BTM_DEVICE_TIMEOUT; + } + (*p_cb)(&results); + } +} +#endif /// CLASSIC_BT_INCLUDED == TRUE + +/******************************************************************************* +** +** Function btm_page_to_setup_timeout +** +** Description This function processes a timeout. +** Currently, we just report an error log +** +** Returns void +** +*******************************************************************************/ +void btm_page_to_setup_timeout (void *p_tle) +{ + UNUSED(p_tle); + BTM_TRACE_DEBUG ("%s\n", __func__); + +#if (CLASSIC_BT_INCLUDED == TRUE) + btm_set_page_timeout_complete (NULL); +#endif /// CLASSIC_BT_INCLUDED == TRUE +} + +/******************************************************************************* +** +** Function BTM_ReadPageTimeout +** +** Description Send HCI Read Page Timeout. +** +** Returns +** BTM_SUCCESS Command sent. +** BTM_NO_RESOURCES If out of resources to send the command. +** +*******************************************************************************/ +//extern +tBTM_STATUS BTM_ReadPageTimeout(tBTM_CMPL_CB *p_cb) +{ + BTM_TRACE_EVENT ("BTM: BTM_ReadPageTimeout"); + + /* Get the page timeout */ + tBTM_GET_PAGE_TIMEOUT_RESULTS results; + + if (p_cb) { + results.hci_status = HCI_SUCCESS; + results.status = BTM_SUCCESS; + results.page_to = btm_cb.btm_inq_vars.page_timeout; + + (*p_cb)(&results); + return (BTM_SUCCESS); + } else { + return (BTM_NO_RESOURCES); + } +} + +/******************************************************************************* +** +** Function BTM_WriteVoiceSettings +** +** Description Send HCI Write Voice Settings command. +** See stack/hcidefs.h for settings bitmask values. +** +** Returns +** BTM_SUCCESS Command sent. +** BTM_NO_RESOURCES If out of resources to send the command. +** +** +*******************************************************************************/ +tBTM_STATUS BTM_WriteVoiceSettings(UINT16 settings) +{ + BTM_TRACE_EVENT ("BTM: BTM_WriteVoiceSettings: Settings: 0x%04x.", settings); + + /* Send the HCI command */ + if (btsnd_hcic_write_voice_settings ((UINT16)(settings & 0x03ff))) { + return (BTM_SUCCESS); + } + + return (BTM_NO_RESOURCES); +} + +/******************************************************************************* +** +** Function BTM_EnableTestMode +** +** Description Send HCI the enable device under test command. +** +** Note: Controller can only be taken out of this mode by +** resetting the controller. +** +** Returns +** BTM_SUCCESS Command sent. +** BTM_NO_RESOURCES If out of resources to send the command. +** +** +*******************************************************************************/ +tBTM_STATUS BTM_EnableTestMode(void) +{ + UINT8 cond; + + BTM_TRACE_EVENT ("BTM: BTM_EnableTestMode"); + + /* set auto accept connection as this is needed during test mode */ + /* Allocate a buffer to hold HCI command */ + cond = HCI_DO_AUTO_ACCEPT_CONNECT; + if (!btsnd_hcic_set_event_filter(HCI_FILTER_CONNECTION_SETUP, + HCI_FILTER_COND_NEW_DEVICE, + &cond, sizeof(cond))) { + return (BTM_NO_RESOURCES); + } + + /* put device to connectable mode */ + if (BTM_SetConnectability(BTM_CONNECTABLE, BTM_DEFAULT_CONN_WINDOW, + BTM_DEFAULT_CONN_INTERVAL) != BTM_SUCCESS) { + return BTM_NO_RESOURCES; + } + + /* put device to discoverable mode */ + if (BTM_SetDiscoverability(BTM_GENERAL_DISCOVERABLE, BTM_DEFAULT_DISC_WINDOW, + BTM_DEFAULT_DISC_INTERVAL) != BTM_SUCCESS) { + return BTM_NO_RESOURCES; + } + + /* mask off all of event from controller */ + hci_layer_get_interface()->transmit_command( + hci_packet_factory_get_interface()->make_set_event_mask((const bt_event_mask_t *)("\x00\x00\x00\x00\x00\x00\x00\x00")), + NULL, + NULL, + NULL); + + /* Send the HCI command */ + if (btsnd_hcic_enable_test_mode ()) { + return (BTM_SUCCESS); + } else { + return (BTM_NO_RESOURCES); + } +} + +/******************************************************************************* +** +** Function BTM_DeleteStoredLinkKey +** +** Description This function is called to delete link key for the specified +** device addresses from the NVRAM storage attached to the Bluetooth +** controller. +** +** Parameters: bd_addr - Addresses of the devices +** p_cb - Call back function to be called to return +** the results +** +*******************************************************************************/ +tBTM_STATUS BTM_DeleteStoredLinkKey(BD_ADDR bd_addr, tBTM_CMPL_CB *p_cb) +{ + BD_ADDR local_bd_addr = {0}; + BOOLEAN delete_all_flag = FALSE; + + /* Check if the previous command is completed */ + if (btm_cb.devcb.p_stored_link_key_cmpl_cb) { + return (BTM_BUSY); + } + + if (!bd_addr) { + /* This is to delete all link keys */ + delete_all_flag = TRUE; + + /* We don't care the BD address. Just pass a non zero pointer */ + bd_addr = local_bd_addr; + } + + BTM_TRACE_EVENT ("BTM: BTM_DeleteStoredLinkKey: delete_all_flag: %s", + delete_all_flag ? "TRUE" : "FALSE"); + + /* Send the HCI command */ + btm_cb.devcb.p_stored_link_key_cmpl_cb = p_cb; + if (!btsnd_hcic_delete_stored_key (bd_addr, delete_all_flag)) { + return (BTM_NO_RESOURCES); + } else { + return (BTM_SUCCESS); + } +} + +/******************************************************************************* +** +** Function btm_delete_stored_link_key_complete +** +** Description This function is called when the command complete message +** is received from the HCI for the delete stored link key command. +** +** Returns void +** +*******************************************************************************/ +void btm_delete_stored_link_key_complete (UINT8 *p) +{ + tBTM_CMPL_CB *p_cb = btm_cb.devcb.p_stored_link_key_cmpl_cb; + tBTM_DELETE_STORED_LINK_KEY_COMPLETE result; + + /* If there was a callback registered for read stored link key, call it */ + btm_cb.devcb.p_stored_link_key_cmpl_cb = NULL; + + if (p_cb) { + /* Set the call back event to indicate command complete */ + result.event = BTM_CB_EVT_DELETE_STORED_LINK_KEYS; + + /* Extract the result fields from the HCI event */ + STREAM_TO_UINT8 (result.status, p); + STREAM_TO_UINT16 (result.num_keys, p); + + /* Call the call back and pass the result */ + (*p_cb)(&result); + } +} + +/******************************************************************************* +** +** Function btm_report_device_status +** +** Description This function is called when there is a change in the device +** status. This function will report the new device status to +** the application +** +** Returns void +** +*******************************************************************************/ +void btm_report_device_status (tBTM_DEV_STATUS status) +{ + tBTM_DEV_STATUS_CB *p_cb = btm_cb.devcb.p_dev_status_cb; + + /* Call the call back to pass the device status to application */ + if (p_cb) { + (*p_cb)(status); + } +} + +#if (CLASSIC_BT_INCLUDED == TRUE) +/******************************************************************************* +** +** Function BTM_SetAfhChannels +** +** Description This function is called to set AFH channels +** +** Returns status of the operation +** +*******************************************************************************/ +tBTM_STATUS BTM_SetAfhChannels (AFH_CHANNELS channels, tBTM_CMPL_CB *p_afh_channels_cmpl_cback) +{ + if (!controller_get_interface()->get_is_ready()) { + return (BTM_DEV_RESET); + } + + /* Check if set afh already in progress */ + if (btm_cb.devcb.p_afh_channels_cmpl_cb) { + return (BTM_NO_RESOURCES); + } + + /* Save callback */ + btm_cb.devcb.p_afh_channels_cmpl_cb = p_afh_channels_cmpl_cback; + + if (!btsnd_hcic_set_afh_channels (channels)) { + return (BTM_NO_RESOURCES); + } + + btu_start_timer (&btm_cb.devcb.afh_channels_timer, BTU_TTYPE_BTM_ACL, BTM_DEV_REPLY_TIMEOUT); + + return BTM_CMD_STARTED; +} + +/******************************************************************************* +** +** Function btm_set_afh_channels_complete +** +** Description This function is called when setting AFH channels complete. +** message is received from the HCI. +** +** Returns void +** +*******************************************************************************/ +void btm_set_afh_channels_complete (UINT8 *p) +{ + tBTM_CMPL_CB *p_cb = btm_cb.devcb.p_afh_channels_cmpl_cb; + tBTM_SET_AFH_CHANNELS_RESULTS results; + + btu_free_timer (&btm_cb.devcb.afh_channels_timer); + + /* If there is a callback address for setting AFH channels, call it */ + btm_cb.devcb.p_afh_channels_cmpl_cb = NULL; + + if (p_cb) { + STREAM_TO_UINT8 (results.hci_status, p); + + switch (results.hci_status){ + case HCI_SUCCESS: + results.status = BTM_SUCCESS; + break; + case HCI_ERR_UNSUPPORTED_VALUE: + case HCI_ERR_ILLEGAL_PARAMETER_FMT: + results.status = BTM_ILLEGAL_VALUE; + break; + default: + results.status = BTM_ERR_PROCESSING; + break; + } + (*p_cb)(&results); + } +} +#endif /// CLASSIC_BT_INCLUDED == TRUE + +#if (BLE_INCLUDED == TRUE) +/******************************************************************************* +** +** Function BTM_BleSetChannels +** +** Description This function is called to set BLE channels +** +** Returns status of the operation +** +*******************************************************************************/ +tBTM_STATUS BTM_BleSetChannels (BLE_CHANNELS channels, tBTM_CMPL_CB *p_ble_channels_cmpl_cback) +{ + if (!controller_get_interface()->get_is_ready()) { + return (BTM_DEV_RESET); + } + + /* Check if set afh already in progress */ + if (btm_cb.devcb.p_ble_channels_cmpl_cb) { + return (BTM_NO_RESOURCES); + } + + /* Save callback */ + btm_cb.devcb.p_ble_channels_cmpl_cb = p_ble_channels_cmpl_cback; + + if (!btsnd_hcic_ble_set_channels (channels)) { + return (BTM_NO_RESOURCES); + } + + btu_start_timer (&btm_cb.devcb.ble_channels_timer, BTU_TTYPE_BTM_ACL, BTM_DEV_REPLY_TIMEOUT); + + return BTM_CMD_STARTED; +} + +/******************************************************************************* +** +** Function btm_ble_set_channels_complete +** +** Description This function is called when setting AFH channels complete. +** message is received from the HCI. +** +** Returns void +** +*******************************************************************************/ +void btm_ble_set_channels_complete (UINT8 *p) +{ + tBTM_CMPL_CB *p_cb = btm_cb.devcb.p_ble_channels_cmpl_cb; + tBTM_BLE_SET_CHANNELS_RESULTS results; + + btu_free_timer (&btm_cb.devcb.ble_channels_timer); + + /* If there is a callback address for setting AFH channels, call it */ + btm_cb.devcb.p_ble_channels_cmpl_cb = NULL; + + if (p_cb) { + STREAM_TO_UINT8 (results.hci_status, p); + + switch (results.hci_status){ + case HCI_SUCCESS: + results.status = BTM_SUCCESS; + break; + case HCI_ERR_UNSUPPORTED_VALUE: + case HCI_ERR_ILLEGAL_PARAMETER_FMT: + results.status = BTM_ILLEGAL_VALUE; + break; + default: + results.status = BTM_ERR_PROCESSING; + break; + } + (*p_cb)(&results); + } +} +#endif /// BLE_INCLUDED == TRUE diff --git a/lib/bt/host/bluedroid/stack/btm/btm_inq.c b/lib/bt/host/bluedroid/stack/btm/btm_inq.c new file mode 100644 index 00000000..b8da2327 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/btm/btm_inq.c @@ -0,0 +1,2973 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2014 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains functions that handle inquiries. These include + * setting discoverable mode, controlling the mode of the Baseband, and + * maintaining a small database of inquiry responses, with API for people + * to browse it. + * + ******************************************************************************/ + +#include +#include +#include + +#include "osi/alarm.h" +#include "stack/bt_types.h" +#include "device/controller.h" +#include "stack/hcimsgs.h" +#include "stack/btu.h" +#include "stack/btm_api.h" +#include "btm_int.h" +#include "stack/hcidefs.h" +#if (defined(SDP_INCLUDED) && SDP_INCLUDED == TRUE) +#include "stack/sdpdefs.h" +#endif + +#define BTM_INQ_REPLY_TIMEOUT 3 /* 3 second timeout waiting for responses */ + +/* TRUE to enable DEBUG traces for btm_inq */ +#ifndef BTM_INQ_DEBUG +#define BTM_INQ_DEBUG FALSE +#endif +/********************************************************************************/ +/* L O C A L D A T A D E F I N I T I O N S */ +/********************************************************************************/ +static const LAP general_inq_lap = {0x9e, 0x8b, 0x33}; +static const LAP limited_inq_lap = {0x9e, 0x8b, 0x00}; + +#if (defined(SDP_INCLUDED) && SDP_INCLUDED == TRUE) +static const UINT16 BTM_EIR_UUID_LKUP_TBL[BTM_EIR_MAX_SERVICES] = { + UUID_SERVCLASS_SERVICE_DISCOVERY_SERVER, + /* UUID_SERVCLASS_BROWSE_GROUP_DESCRIPTOR, */ + /* UUID_SERVCLASS_PUBLIC_BROWSE_GROUP, */ + UUID_SERVCLASS_SERIAL_PORT, + UUID_SERVCLASS_LAN_ACCESS_USING_PPP, + UUID_SERVCLASS_DIALUP_NETWORKING, + UUID_SERVCLASS_IRMC_SYNC, + UUID_SERVCLASS_OBEX_OBJECT_PUSH, + UUID_SERVCLASS_OBEX_FILE_TRANSFER, + UUID_SERVCLASS_IRMC_SYNC_COMMAND, + UUID_SERVCLASS_HEADSET, + UUID_SERVCLASS_CORDLESS_TELEPHONY, + UUID_SERVCLASS_AUDIO_SOURCE, + UUID_SERVCLASS_AUDIO_SINK, + UUID_SERVCLASS_AV_REM_CTRL_TARGET, + /* UUID_SERVCLASS_ADV_AUDIO_DISTRIBUTION, */ + UUID_SERVCLASS_AV_REMOTE_CONTROL, + /* UUID_SERVCLASS_VIDEO_CONFERENCING, */ + UUID_SERVCLASS_INTERCOM, + UUID_SERVCLASS_FAX, + UUID_SERVCLASS_HEADSET_AUDIO_GATEWAY, + /* UUID_SERVCLASS_WAP, */ + /* UUID_SERVCLASS_WAP_CLIENT, */ + UUID_SERVCLASS_PANU, + UUID_SERVCLASS_NAP, + UUID_SERVCLASS_GN, + UUID_SERVCLASS_DIRECT_PRINTING, + /* UUID_SERVCLASS_REFERENCE_PRINTING, */ + UUID_SERVCLASS_IMAGING, + UUID_SERVCLASS_IMAGING_RESPONDER, + UUID_SERVCLASS_IMAGING_AUTO_ARCHIVE, + UUID_SERVCLASS_IMAGING_REF_OBJECTS, + UUID_SERVCLASS_HF_HANDSFREE, + UUID_SERVCLASS_AG_HANDSFREE, + UUID_SERVCLASS_DIR_PRT_REF_OBJ_SERVICE, + /* UUID_SERVCLASS_REFLECTED_UI, */ + UUID_SERVCLASS_BASIC_PRINTING, + UUID_SERVCLASS_PRINTING_STATUS, + UUID_SERVCLASS_HUMAN_INTERFACE, + UUID_SERVCLASS_CABLE_REPLACEMENT, + UUID_SERVCLASS_HCRP_PRINT, + UUID_SERVCLASS_HCRP_SCAN, + /* UUID_SERVCLASS_COMMON_ISDN_ACCESS, */ + /* UUID_SERVCLASS_VIDEO_CONFERENCING_GW, */ + /* UUID_SERVCLASS_UDI_MT, */ + /* UUID_SERVCLASS_UDI_TA, */ + /* UUID_SERVCLASS_VCP, */ + UUID_SERVCLASS_SAP, + UUID_SERVCLASS_PBAP_PCE, + UUID_SERVCLASS_PBAP_PSE, + UUID_SERVCLASS_PHONE_ACCESS, + UUID_SERVCLASS_HEADSET_HS, + UUID_SERVCLASS_PNP_INFORMATION, + /* UUID_SERVCLASS_GENERIC_NETWORKING, */ + /* UUID_SERVCLASS_GENERIC_FILETRANSFER, */ + /* UUID_SERVCLASS_GENERIC_AUDIO, */ + /* UUID_SERVCLASS_GENERIC_TELEPHONY, */ + /* UUID_SERVCLASS_UPNP_SERVICE, */ + /* UUID_SERVCLASS_UPNP_IP_SERVICE, */ + /* UUID_SERVCLASS_ESDP_UPNP_IP_PAN, */ + /* UUID_SERVCLASS_ESDP_UPNP_IP_LAP, */ + /* UUID_SERVCLASS_ESDP_UPNP_IP_L2CAP, */ + UUID_SERVCLASS_VIDEO_SOURCE, + UUID_SERVCLASS_VIDEO_SINK, + /* UUID_SERVCLASS_VIDEO_DISTRIBUTION */ + UUID_SERVCLASS_MESSAGE_ACCESS, + UUID_SERVCLASS_MESSAGE_NOTIFICATION, + UUID_SERVCLASS_HDP_SOURCE, + UUID_SERVCLASS_HDP_SINK +}; +#else +static const UINT16 BTM_EIR_UUID_LKUP_TBL[BTM_EIR_MAX_SERVICES]; +#endif + +/********************************************************************************/ +/* L O C A L F U N C T I O N P R O T O T Y P E S */ +/********************************************************************************/ +static void btm_initiate_inquiry (tBTM_INQUIRY_VAR_ST *p_inq); +static tBTM_STATUS btm_set_inq_event_filter (UINT8 filter_cond_type, tBTM_INQ_FILT_COND *p_filt_cond); +static void btm_clr_inq_result_flt (void); + +static UINT8 btm_convert_uuid_to_eir_service( UINT16 uuid16 ); +static void btm_set_eir_uuid( UINT8 *p_eir, tBTM_INQ_RESULTS *p_results ); +static UINT8 *btm_eir_get_uuid_list( UINT8 *p_eir, UINT8 uuid_size, + UINT8 *p_num_uuid, UINT8 *p_uuid_list_type ); +static UINT16 btm_convert_uuid_to_uuid16( UINT8 *p_uuid, UINT8 uuid_size ); + +/******************************************************************************* +** +** Function BTM_SetDiscoverability +** +** Description This function is called to set the device into or out of +** discoverable mode. Discoverable mode means inquiry +** scans are enabled. If a value of '0' is entered for window or +** interval, the default values are used. +** +** Returns BTM_SUCCESS if successful +** BTM_BUSY if a setting of the filter is already in progress +** BTM_NO_RESOURCES if couldn't get a memory pool buffer +** BTM_ILLEGAL_VALUE if a bad parameter was detected +** BTM_WRONG_MODE if the device is not up. +** +*******************************************************************************/ +tBTM_STATUS BTM_SetDiscoverability (UINT16 inq_mode, UINT16 window, UINT16 interval) +{ + UINT8 scan_mode = 0; + UINT16 service_class; + UINT8 *p_cod; + UINT8 major, minor; + DEV_CLASS cod; + LAP temp_lap[2]; + BOOLEAN is_limited; + BOOLEAN cod_limited; + + BTM_TRACE_API ("BTM_SetDiscoverability\n"); + + /*** Check mode parameter ***/ + if (inq_mode > BTM_MAX_DISCOVERABLE) { + return (BTM_ILLEGAL_VALUE); + } + + /* Make sure the controller is active */ + if (!controller_get_interface()->get_is_ready()) { + return (BTM_DEV_RESET); + } + + /* If the window and/or interval is '0', set to default values */ + if (!window) { + window = BTM_DEFAULT_DISC_WINDOW; + } + + if (!interval) { + interval = BTM_DEFAULT_DISC_INTERVAL; + } + + BTM_TRACE_API ("BTM_SetDiscoverability: mode %d [NonDisc-0, Lim-1, Gen-2], window 0x%04x, interval 0x%04x\n", + inq_mode, window, interval); + + /*** Check for valid window and interval parameters ***/ + /*** Only check window and duration if mode is connectable ***/ + if (inq_mode != BTM_NON_DISCOVERABLE) { + /* window must be less than or equal to interval */ + if (window < HCI_MIN_INQUIRYSCAN_WINDOW || + window > HCI_MAX_INQUIRYSCAN_WINDOW || + interval < HCI_MIN_INQUIRYSCAN_INTERVAL || + interval > HCI_MAX_INQUIRYSCAN_INTERVAL || + window > interval) { + return (BTM_ILLEGAL_VALUE); + } + } + + /* Set the IAC if needed */ + if (inq_mode != BTM_NON_DISCOVERABLE) { + if (inq_mode & BTM_LIMITED_DISCOVERABLE) { + /* Use the GIAC and LIAC codes for limited discoverable mode */ + memcpy (temp_lap[0], limited_inq_lap, LAP_LEN); + memcpy (temp_lap[1], general_inq_lap, LAP_LEN); + + if (!btsnd_hcic_write_cur_iac_lap (2, (LAP * const) temp_lap)) { + return (BTM_NO_RESOURCES); /* Cannot continue */ + } + } else { + if (!btsnd_hcic_write_cur_iac_lap (1, (LAP * const) &general_inq_lap)) { + return (BTM_NO_RESOURCES); /* Cannot continue */ + } + } + + scan_mode |= HCI_INQUIRY_SCAN_ENABLED; + } + + /* Send down the inquiry scan window and period if changed */ + if ((window != btm_cb.btm_inq_vars.inq_scan_window) || + (interval != btm_cb.btm_inq_vars.inq_scan_period)) { + if (btsnd_hcic_write_inqscan_cfg (interval, window)) { + btm_cb.btm_inq_vars.inq_scan_window = window; + btm_cb.btm_inq_vars.inq_scan_period = interval; + } else { + return (BTM_NO_RESOURCES); + } + } + + if (btm_cb.btm_inq_vars.connectable_mode & BTM_CONNECTABLE_MASK) { + scan_mode |= HCI_PAGE_SCAN_ENABLED; + } + + if (btsnd_hcic_write_scan_enable (scan_mode)) { + btm_cb.btm_inq_vars.discoverable_mode &= (~BTM_DISCOVERABLE_MASK); + btm_cb.btm_inq_vars.discoverable_mode |= inq_mode; + } else { + return (BTM_NO_RESOURCES); + } + + /* Change the service class bit if mode has changed */ + p_cod = BTM_ReadDeviceClass(); + BTM_COD_SERVICE_CLASS(service_class, p_cod); + is_limited = (inq_mode & BTM_LIMITED_DISCOVERABLE) ? TRUE : FALSE; + cod_limited = (service_class & BTM_COD_SERVICE_LMTD_DISCOVER) ? TRUE : FALSE; + if (is_limited ^ cod_limited) { + BTM_COD_MINOR_CLASS(minor, p_cod ); + BTM_COD_MAJOR_CLASS(major, p_cod ); + if (is_limited) { + service_class |= BTM_COD_SERVICE_LMTD_DISCOVER; + } else { + service_class &= ~BTM_COD_SERVICE_LMTD_DISCOVER; + } + + FIELDS_TO_COD(cod, minor, major, service_class); + (void) BTM_SetDeviceClass (cod); + } + + return (BTM_SUCCESS); +} + +/******************************************************************************* +** +** Function BTM_SetInquiryScanType +** +** Description This function is called to set the iquiry scan-type to +** standard or interlaced. +** +** Returns BTM_SUCCESS if successful +** BTM_MODE_UNSUPPORTED if not a 1.2 device +** BTM_WRONG_MODE if the device is not up. +** +*******************************************************************************/ +tBTM_STATUS BTM_SetInquiryScanType (UINT16 scan_type) +{ + + BTM_TRACE_API ("BTM_SetInquiryScanType\n"); + if (scan_type != BTM_SCAN_TYPE_STANDARD && scan_type != BTM_SCAN_TYPE_INTERLACED) { + return (BTM_ILLEGAL_VALUE); + } + + /* whatever app wants if device is not 1.2 scan type should be STANDARD */ + if (!controller_get_interface()->supports_interlaced_inquiry_scan()) { + return (BTM_MODE_UNSUPPORTED); + } + + /* Check for scan type if configuration has been changed */ + if (scan_type != btm_cb.btm_inq_vars.inq_scan_type) { + if (BTM_IsDeviceUp()) { + if (btsnd_hcic_write_inqscan_type ((UINT8)scan_type)) { + btm_cb.btm_inq_vars.inq_scan_type = scan_type; + } else { + return (BTM_NO_RESOURCES); + } + } else { + return (BTM_WRONG_MODE); + } + } + return (BTM_SUCCESS); +} + +/******************************************************************************* +** +** Function BTM_SetPageScanType +** +** Description This function is called to set the page scan-type to +** standard or interlaced. +** +** Returns BTM_SUCCESS if successful +** BTM_MODE_UNSUPPORTED if not a 1.2 device +** BTM_WRONG_MODE if the device is not up. +** +*******************************************************************************/ +tBTM_STATUS BTM_SetPageScanType (UINT16 scan_type) +{ + BTM_TRACE_API ("BTM_SetPageScanType\n"); + if (scan_type != BTM_SCAN_TYPE_STANDARD && scan_type != BTM_SCAN_TYPE_INTERLACED) { + return (BTM_ILLEGAL_VALUE); + } + + /* whatever app wants if device is not 1.2 scan type should be STANDARD */ + if (!controller_get_interface()->supports_interlaced_inquiry_scan()) { + return (BTM_MODE_UNSUPPORTED); + } + + /* Check for scan type if configuration has been changed */ + if (scan_type != btm_cb.btm_inq_vars.page_scan_type) { + if (BTM_IsDeviceUp()) { + if (btsnd_hcic_write_pagescan_type ((UINT8)scan_type)) { + btm_cb.btm_inq_vars.page_scan_type = scan_type; + } else { + return (BTM_NO_RESOURCES); + } + } else { + return (BTM_WRONG_MODE); + } + } + return (BTM_SUCCESS); +} + + +/******************************************************************************* +** +** Function BTM_SetInquiryMode +** +** Description This function is called to set standard or with RSSI +** mode of the inquiry for local device. +** +** Output Params: mode - standard, with RSSI, extended +** +** Returns BTM_SUCCESS if successful +** BTM_NO_RESOURCES if couldn't get a memory pool buffer +** BTM_ILLEGAL_VALUE if a bad parameter was detected +** BTM_WRONG_MODE if the device is not up. +** +*******************************************************************************/ +tBTM_STATUS BTM_SetInquiryMode (UINT8 mode) +{ + const controller_t *controller = controller_get_interface(); + BTM_TRACE_API ("BTM_SetInquiryMode\n"); + if (mode == BTM_INQ_RESULT_STANDARD) { + /* mandatory mode */ + } else if (mode == BTM_INQ_RESULT_WITH_RSSI) { + if (!controller->supports_rssi_with_inquiry_results()) { + return (BTM_MODE_UNSUPPORTED); + } + } else if (mode == BTM_INQ_RESULT_EXTENDED) { + if (!controller->supports_extended_inquiry_response()) { + return (BTM_MODE_UNSUPPORTED); + } + } else { + return (BTM_ILLEGAL_VALUE); + } + + if (!BTM_IsDeviceUp()) { + return (BTM_WRONG_MODE); + } + + if (!btsnd_hcic_write_inquiry_mode (mode)) { + return (BTM_NO_RESOURCES); + } + + return (BTM_SUCCESS); +} + +/******************************************************************************* +** +** Function BTM_ReadDiscoverability +** +** Description This function is called to read the current discoverability +** mode of the device. +** +** Output Params: p_window - current inquiry scan duration +** p_interval - current inquiry scan interval +** +** Returns BTM_NON_DISCOVERABLE, BTM_LIMITED_DISCOVERABLE, or +** BTM_GENERAL_DISCOVERABLE +** +*******************************************************************************/ +UINT16 BTM_ReadDiscoverability (UINT16 *p_window, UINT16 *p_interval) +{ + BTM_TRACE_API ("BTM_ReadDiscoverability\n"); + if (p_window) { + *p_window = btm_cb.btm_inq_vars.inq_scan_window; + } + + if (p_interval) { + *p_interval = btm_cb.btm_inq_vars.inq_scan_period; + } + + return (btm_cb.btm_inq_vars.discoverable_mode); +} + + +/******************************************************************************* +** +** Function BTM_SetPeriodicInquiryMode +** +** Description This function is called to set the device periodic inquiry mode. +** If the duration is zero, the periodic inquiry mode is cancelled. +** +** Note: We currently do not allow concurrent inquiry and periodic inquiry. +** +** Parameters: p_inqparms - pointer to the inquiry information +** mode - GENERAL or LIMITED inquiry +** duration - length in 1.28 sec intervals (If '0', the inquiry is CANCELLED) +** max_resps - maximum amount of devices to search for before ending the inquiry +** filter_cond_type - BTM_CLR_INQUIRY_FILTER, BTM_FILTER_COND_DEVICE_CLASS, or +** BTM_FILTER_COND_BD_ADDR +** filter_cond - value for the filter (based on filter_cond_type) +** +** max_delay - maximum amount of time between successive inquiries +** min_delay - minimum amount of time between successive inquiries +** p_results_cb - callback returning pointer to results (tBTM_INQ_RESULTS) +** +** Returns BTM_CMD_STARTED if successfully started +** BTM_ILLEGAL_VALUE if a bad parameter is detected +** BTM_NO_RESOURCES if could not allocate a message buffer +** BTM_SUCCESS - if cancelling the periodic inquiry +** BTM_BUSY - if an inquiry is already active +** BTM_WRONG_MODE if the device is not up. +** +*******************************************************************************/ +tBTM_STATUS BTM_SetPeriodicInquiryMode (tBTM_INQ_PARMS *p_inqparms, UINT16 max_delay, + UINT16 min_delay, tBTM_INQ_RESULTS_CB *p_results_cb) +{ + tBTM_STATUS status; + tBTM_INQUIRY_VAR_ST *p_inq = &btm_cb.btm_inq_vars; + + BTM_TRACE_API ("BTM_SetPeriodicInquiryMode: mode: %d, dur: %d, rsps: %d, flt: %d, min: %d, max: %d\n", + p_inqparms->mode, p_inqparms->duration, p_inqparms->max_resps, + p_inqparms->filter_cond_type, min_delay, max_delay); + + /*** Make sure the device is ready ***/ + if (!BTM_IsDeviceUp()) { + return (BTM_WRONG_MODE); + } + + /* Only one active inquiry is allowed in this implementation. + Also do not allow an inquiry if the inquiry filter is being updated */ + if (p_inq->inq_active || p_inq->inqfilt_active) { + return (BTM_BUSY); + } + + /* If illegal parameters return FALSE */ + if (p_inqparms->mode != BTM_GENERAL_INQUIRY && + p_inqparms->mode != BTM_LIMITED_INQUIRY) { + return (BTM_ILLEGAL_VALUE); + } + + /* Verify the parameters for this command */ + if (p_inqparms->duration < BTM_MIN_INQUIRY_LEN || + p_inqparms->duration > BTM_MAX_INQUIRY_LENGTH || + min_delay <= p_inqparms->duration || + min_delay < BTM_PER_INQ_MIN_MIN_PERIOD || + min_delay > BTM_PER_INQ_MAX_MIN_PERIOD || + max_delay <= min_delay || + max_delay < BTM_PER_INQ_MIN_MAX_PERIOD) + /* max_delay > BTM_PER_INQ_MAX_MAX_PERIOD)*/ + /* BTM_PER_INQ_MAX_MAX_PERIOD set to 1's in all bits. Condition resulting in false always*/ + { + return (BTM_ILLEGAL_VALUE); + } + + /* Save the inquiry parameters to be used upon the completion of setting/clearing the inquiry filter */ + p_inq->inqparms = *p_inqparms; + p_inq->per_min_delay = min_delay; + p_inq->per_max_delay = max_delay; + p_inq->inq_cmpl_info.num_resp = 0; /* Clear the results counter */ + p_inq->p_inq_results_cb = p_results_cb; + + p_inq->inq_active = (UINT8)((p_inqparms->mode == BTM_LIMITED_INQUIRY) ? + (BTM_LIMITED_INQUIRY_ACTIVE | BTM_PERIODIC_INQUIRY_ACTIVE) : + (BTM_GENERAL_INQUIRY_ACTIVE | BTM_PERIODIC_INQUIRY_ACTIVE)); + + /* If a filter is specified, then save it for later and clear the current filter. + The setting of the filter is done upon completion of clearing of the previous + filter. + */ + if (p_inqparms->filter_cond_type != BTM_CLR_INQUIRY_FILTER) { + p_inq->state = BTM_INQ_CLR_FILT_STATE; + p_inqparms->filter_cond_type = BTM_CLR_INQUIRY_FILTER; + } else { /* The filter is not being used so simply clear it; the inquiry can start after this operation */ + p_inq->state = BTM_INQ_SET_FILT_STATE; + } + + /* Before beginning the inquiry the current filter must be cleared, so initiate the command */ + if ((status = btm_set_inq_event_filter (p_inqparms->filter_cond_type, &p_inqparms->filter_cond)) != BTM_CMD_STARTED) { + /* If set filter command is not succesful reset the state */ + p_inq->p_inq_results_cb = NULL; + p_inq->state = BTM_INQ_INACTIVE_STATE; + + } + + return (status); +} + + +/******************************************************************************* +** +** Function BTM_CancelPeriodicInquiry +** +** Description This function cancels a periodic inquiry +** +** Returns +** BTM_NO_RESOURCES if could not allocate a message buffer +** BTM_SUCCESS - if cancelling the periodic inquiry +** BTM_WRONG_MODE if the device is not up. +** +*******************************************************************************/ +tBTM_STATUS BTM_CancelPeriodicInquiry(void) +{ + tBTM_INQUIRY_VAR_ST *p_inq = &btm_cb.btm_inq_vars; + tBTM_STATUS status = BTM_SUCCESS; + BTM_TRACE_API ("BTM_CancelPeriodicInquiry called\n"); + + /*** Make sure the device is ready ***/ + if (!BTM_IsDeviceUp()) { + return (BTM_WRONG_MODE); + } + + /* Only cancel if one is active */ + if (btm_cb.btm_inq_vars.inq_active & BTM_PERIODIC_INQUIRY_ACTIVE) { + btm_cb.btm_inq_vars.inq_active = BTM_INQUIRY_INACTIVE; + btm_cb.btm_inq_vars.p_inq_results_cb = (tBTM_INQ_RESULTS_CB *) NULL; + + if (!btsnd_hcic_exit_per_inq ()) { + status = BTM_NO_RESOURCES; + } + + /* If the event filter is in progress, mark it so that the processing of the return + event will be ignored */ + if (p_inq->inqfilt_active) { + p_inq->pending_filt_complete_event++; + } + + p_inq->inqfilt_active = FALSE; + p_inq->inq_counter++; + } + + return (status); +} + + +/******************************************************************************* +** +** Function BTM_SetConnectability +** +** Description This function is called to set the device into or out of +** connectable mode. Discoverable mode means page scans enabled. +** +** Returns BTM_SUCCESS if successful +** BTM_ILLEGAL_VALUE if a bad parameter is detected +** BTM_NO_RESOURCES if could not allocate a message buffer +** BTM_WRONG_MODE if the device is not up. +** +*******************************************************************************/ +tBTM_STATUS BTM_SetConnectability (UINT16 page_mode, UINT16 window, UINT16 interval) +{ + UINT8 scan_mode = 0; + tBTM_INQUIRY_VAR_ST *p_inq = &btm_cb.btm_inq_vars; + + BTM_TRACE_API ("BTM_SetConnectability\n"); + + /*** Check mode parameter ***/ + if (page_mode != BTM_NON_CONNECTABLE && page_mode != BTM_CONNECTABLE) { + return (BTM_ILLEGAL_VALUE); + } + + /* Make sure the controller is active */ + if (!controller_get_interface()->get_is_ready()) { + return (BTM_DEV_RESET); + } + + /* If the window and/or interval is '0', set to default values */ + if (!window) { + window = BTM_DEFAULT_CONN_WINDOW; + } + + if (!interval) { + interval = BTM_DEFAULT_CONN_INTERVAL; + } + + BTM_TRACE_API ("BTM_SetConnectability: mode %d [NonConn-0, Conn-1], window 0x%04x, interval 0x%04x\n", + page_mode, window, interval); + + /*** Check for valid window and interval parameters ***/ + /*** Only check window and duration if mode is connectable ***/ + if (page_mode == BTM_CONNECTABLE) { + /* window must be less than or equal to interval */ + if (window < HCI_MIN_PAGESCAN_WINDOW || + window > HCI_MAX_PAGESCAN_WINDOW || + interval < HCI_MIN_PAGESCAN_INTERVAL || + interval > HCI_MAX_PAGESCAN_INTERVAL || + window > interval) { + return (BTM_ILLEGAL_VALUE); + } + + scan_mode |= HCI_PAGE_SCAN_ENABLED; + } + + if ((window != p_inq->page_scan_window) || + (interval != p_inq->page_scan_period)) { + p_inq->page_scan_window = window; + p_inq->page_scan_period = interval; + if (!btsnd_hcic_write_pagescan_cfg (interval, window)) { + return (BTM_NO_RESOURCES); + } + } + + /* Keep the inquiry scan as previouosly set */ + if (p_inq->discoverable_mode & BTM_DISCOVERABLE_MASK) { + scan_mode |= HCI_INQUIRY_SCAN_ENABLED; + } + + if (btsnd_hcic_write_scan_enable (scan_mode)) { + p_inq->connectable_mode &= (~BTM_CONNECTABLE_MASK); + p_inq->connectable_mode |= page_mode; + + return (BTM_SUCCESS); + } + + return (BTM_NO_RESOURCES); +} + + +/******************************************************************************* +** +** Function BTM_ReadConnectability +** +** Description This function is called to read the current discoverability +** mode of the device. +** Output Params p_window - current page scan duration +** p_interval - current time between page scans +** +** Returns BTM_NON_CONNECTABLE or BTM_CONNECTABLE +** +*******************************************************************************/ +UINT16 BTM_ReadConnectability (UINT16 *p_window, UINT16 *p_interval) +{ + BTM_TRACE_API ("BTM_ReadConnectability\n"); + if (p_window) { + *p_window = btm_cb.btm_inq_vars.page_scan_window; + } + + if (p_interval) { + *p_interval = btm_cb.btm_inq_vars.page_scan_period; + } + + return (btm_cb.btm_inq_vars.connectable_mode); +} + + + +/******************************************************************************* +** +** Function BTM_IsInquiryActive +** +** Description This function returns a bit mask of the current inquiry state +** +** Returns BTM_INQUIRY_INACTIVE if inactive (0) +** BTM_LIMITED_INQUIRY_ACTIVE if a limted inquiry is active +** BTM_GENERAL_INQUIRY_ACTIVE if a general inquiry is active +** BTM_PERIODIC_INQUIRY_ACTIVE if a periodic inquiry is active +** +*******************************************************************************/ +UINT16 BTM_IsInquiryActive (void) +{ + BTM_TRACE_API ("BTM_IsInquiryActive\n"); + + return (btm_cb.btm_inq_vars.inq_active); +} + + + +/******************************************************************************* +** +** Function BTM_CancelInquiry +** +** Description This function cancels an inquiry if active +** +** Returns BTM_SUCCESS if successful +** BTM_NO_RESOURCES if could not allocate a message buffer +** BTM_WRONG_MODE if the device is not up. +** +*******************************************************************************/ +tBTM_STATUS BTM_CancelInquiry(void) +{ + tBTM_STATUS status = BTM_SUCCESS; + tBTM_INQUIRY_VAR_ST *p_inq = &btm_cb.btm_inq_vars; +#if (defined(BTA_HOST_INTERLEAVE_SEARCH) && BTA_HOST_INTERLEAVE_SEARCH == TRUE) + UINT8 active_mode = p_inq->inq_active; +#endif + BTM_TRACE_API ("BTM_CancelInquiry called\n"); + + /*** Make sure the device is ready ***/ + if (!BTM_IsDeviceUp()) { + return (BTM_WRONG_MODE); + } + + /* Only cancel if not in periodic mode, otherwise the caller should call BTM_CancelPeriodicMode */ + if ((p_inq->inq_active & BTM_INQUIRY_ACTIVE_MASK) != 0 && + (!(p_inq->inq_active & BTM_PERIODIC_INQUIRY_ACTIVE))) { + p_inq->inq_active = BTM_INQUIRY_INACTIVE; + p_inq->state = BTM_INQ_INACTIVE_STATE; + p_inq->p_inq_results_cb = (tBTM_INQ_RESULTS_CB *) NULL; /* Do not notify caller anymore */ + p_inq->p_inq_cmpl_cb = (tBTM_CMPL_CB *) NULL; /* Do not notify caller anymore */ + + /* If the event filter is in progress, mark it so that the processing of the return + event will be ignored */ + if (p_inq->inqfilt_active) { + p_inq->inqfilt_active = FALSE; + p_inq->pending_filt_complete_event++; + } + /* Initiate the cancel inquiry */ + else { + if (((p_inq->inqparms.mode & BTM_BR_INQUIRY_MASK) != 0) +#if (defined(BTA_HOST_INTERLEAVE_SEARCH) && BTA_HOST_INTERLEAVE_SEARCH == TRUE) + && (active_mode & BTM_BR_INQUIRY_MASK) +#endif + ) { + if (!btsnd_hcic_inq_cancel()) { + status = BTM_NO_RESOURCES; + } + } +#if BLE_INCLUDED == TRUE + if (((p_inq->inqparms.mode & BTM_BLE_INQUIRY_MASK) != 0) +#if (defined(BTA_HOST_INTERLEAVE_SEARCH) && BTA_HOST_INTERLEAVE_SEARCH == TRUE) + && (active_mode & BTM_BLE_INQ_ACTIVE_MASK) +#endif + ) { + btm_ble_stop_inquiry(); + } +#endif + } + + /* Do not send the BUSY_LEVEL event yet. Wait for the cancel_complete event + * and then send the BUSY_LEVEL event + * btm_acl_update_busy_level (BTM_BLI_INQ_DONE_EVT); + */ + + p_inq->inq_counter++; + btm_clr_inq_result_flt(); + } + + return (status); +} + + +/******************************************************************************* +** +** Function BTM_StartInquiry +** +** Description This function is called to start an inquiry. +** +** Parameters: p_inqparms - pointer to the inquiry information +** mode - GENERAL or LIMITED inquiry, BR/LE bit mask seperately +** duration - length in 1.28 sec intervals (If '0', the inquiry is CANCELLED) +** max_resps - maximum amount of devices to search for before ending the inquiry +** filter_cond_type - BTM_CLR_INQUIRY_FILTER, BTM_FILTER_COND_DEVICE_CLASS, or +** BTM_FILTER_COND_BD_ADDR +** filter_cond - value for the filter (based on filter_cond_type) +** +** p_results_cb - Pointer to the callback routine which gets called +** upon receipt of an inquiry result. If this field is +** NULL, the application is not notified. +** +** p_cmpl_cb - Pointer to the callback routine which gets called +** upon completion. If this field is NULL, the +** application is not notified when completed. +** Returns tBTM_STATUS +** BTM_CMD_STARTED if successfully initiated +** BTM_BUSY if already in progress +** BTM_ILLEGAL_VALUE if parameter(s) are out of range +** BTM_NO_RESOURCES if could not allocate resources to start the command +** BTM_WRONG_MODE if the device is not up. +** +*******************************************************************************/ +tBTM_STATUS BTM_StartInquiry (tBTM_INQ_PARMS *p_inqparms, tBTM_INQ_RESULTS_CB *p_results_cb, + tBTM_CMPL_CB *p_cmpl_cb) +{ + tBTM_STATUS status = BTM_CMD_STARTED; + tBTM_INQUIRY_VAR_ST *p_inq = &btm_cb.btm_inq_vars; + + BTM_TRACE_API ("BTM_StartInquiry: mode: %d, dur: %d, rsps: %d, flt: %d\n", + p_inqparms->mode, p_inqparms->duration, p_inqparms->max_resps, + p_inqparms->filter_cond_type); + + /* Only one active inquiry is allowed in this implementation. + Also do not allow an inquiry if the inquiry filter is being updated */ + if (p_inq->inq_active || p_inq->inqfilt_active) { +#if (defined BLE_INCLUDED && BLE_INCLUDED == TRUE) + /*check if LE observe is already running*/ + if (p_inq->scan_type == INQ_LE_OBSERVE && p_inq->p_inq_ble_results_cb != NULL) { + BTM_TRACE_API("BTM_StartInquiry: LE observe in progress"); + p_inq->scan_type = INQ_GENERAL; + p_inq->inq_active = BTM_INQUIRY_INACTIVE; + btm_cb.ble_ctr_cb.inq_var.scan_type = BTM_BLE_SCAN_MODE_NONE; + btsnd_hcic_ble_set_scan_enable (BTM_BLE_SCAN_DISABLE, BTM_BLE_DUPLICATE_ENABLE); + } else +#endif + { + return (BTM_BUSY); + BTM_TRACE_API("BTM_StartInquiry: return BUSY\n"); + } + } else { + p_inq->scan_type = INQ_GENERAL; + } + + /*** Make sure the device is ready ***/ + if (!BTM_IsDeviceUp()) { + return (BTM_WRONG_MODE); + } + + if ((p_inqparms->mode & BTM_BR_INQUIRY_MASK) != BTM_GENERAL_INQUIRY && + (p_inqparms->mode & BTM_BR_INQUIRY_MASK) != BTM_LIMITED_INQUIRY +#if (BLE_INCLUDED == TRUE) + && (p_inqparms->mode & BTM_BLE_INQUIRY_MASK) != BTM_BLE_GENERAL_INQUIRY + && (p_inqparms->mode & BTM_BLE_INQUIRY_MASK) != BTM_BLE_LIMITED_INQUIRY +#endif + ) { + return (BTM_ILLEGAL_VALUE); + } + +#if (defined(BTA_HOST_INTERLEAVE_SEARCH) && BTA_HOST_INTERLEAVE_SEARCH == TRUE) + if (p_inq->next_state == BTM_FINISH) { + return BTM_ILLEGAL_VALUE; + } +#endif + + + /* Save the inquiry parameters to be used upon the completion of setting/clearing the inquiry filter */ + p_inq->inqparms = *p_inqparms; + + /* Initialize the inquiry variables */ + p_inq->state = BTM_INQ_ACTIVE_STATE; + p_inq->p_inq_cmpl_cb = p_cmpl_cb; + p_inq->p_inq_results_cb = p_results_cb; + p_inq->inq_cmpl_info.num_resp = 0; /* Clear the results counter */ + p_inq->inq_active = p_inqparms->mode; + + BTM_TRACE_DEBUG("BTM_StartInquiry: p_inq->inq_active = 0x%02x\n", p_inq->inq_active); + + /* interleave scan minimal conditions */ +#if (BLE_INCLUDED==TRUE && (defined(BTA_HOST_INTERLEAVE_SEARCH) && BTA_HOST_INTERLEAVE_SEARCH == TRUE)) + + /* check if both modes are present */ + if ((p_inqparms->mode & BTM_BLE_INQUIRY_MASK) && (p_inqparms->mode & BTM_BR_INQUIRY_MASK)) { + BTM_TRACE_API("BTM:Interleave Inquiry Mode Set\n"); + p_inqparms->duration = p_inqparms->intl_duration[p_inq->next_state]; + p_inq->inqparms.duration = p_inqparms->duration; + } else { + BTM_TRACE_API("BTM:Single Mode: No interleaving, Mode:0x%02x\n", p_inqparms->mode); + p_inq->next_state = BTM_NO_INTERLEAVING; + } +#endif + + + + /* start LE inquiry here if requested */ +#if BLE_INCLUDED == TRUE + if ((p_inqparms->mode & BTM_BLE_INQUIRY_MASK) +#if (defined(BTA_HOST_INTERLEAVE_SEARCH) && BTA_HOST_INTERLEAVE_SEARCH == TRUE) + && (p_inq->next_state == BTM_BLE_ONE || p_inq->next_state == BTM_BLE_TWO || + p_inq->next_state == BTM_NO_INTERLEAVING) +#endif + ) + + { +#if (defined(BTA_HOST_INTERLEAVE_SEARCH) && BTA_HOST_INTERLEAVE_SEARCH == TRUE) + p_inq->inq_active = (p_inqparms->mode & BTM_BLE_INQUIRY_MASK); + BTM_TRACE_API("BTM:Starting LE Scan with duration %d and activeMode:0x%02x\n", + p_inqparms->duration, (p_inqparms->mode & BTM_BLE_INQUIRY_MASK)); +#endif + if (!controller_get_interface()->supports_ble()) { + p_inq->inqparms.mode &= ~ BTM_BLE_INQUIRY_MASK; + status = BTM_ILLEGAL_VALUE; + } + /* BLE for now does not support filter condition for inquiry */ + else if ((status = btm_ble_start_inquiry((UINT8)(p_inqparms->mode & BTM_BLE_INQUIRY_MASK), + p_inqparms->duration)) != BTM_CMD_STARTED) { + BTM_TRACE_ERROR("Err Starting LE Inquiry.\n"); + p_inq->inqparms.mode &= ~ BTM_BLE_INQUIRY_MASK; + } +#if (!defined(BTA_HOST_INTERLEAVE_SEARCH) || BTA_HOST_INTERLEAVE_SEARCH == FALSE) + p_inqparms->mode &= ~BTM_BLE_INQUIRY_MASK; +#endif + +#if (defined(BTA_HOST_INTERLEAVE_SEARCH) && BTA_HOST_INTERLEAVE_SEARCH == TRUE) + if (p_inq->next_state == BTM_NO_INTERLEAVING) { + p_inq->next_state = BTM_FINISH; + } else { + BTM_TRACE_API("BTM:Interleaving: started LE scan, Advancing to next state: %d\n", + p_inq->next_state + 1); + p_inq->next_state += 1; + } + /* reset next_state if status <> BTM_Started */ + if (status != BTM_CMD_STARTED) { + p_inq->next_state = BTM_BR_ONE; + } + + /* if interleave scan..return here */ + return status; +#endif + + + BTM_TRACE_DEBUG("BTM_StartInquiry: mode = %02x\n", p_inqparms->mode); + } +#endif /* end of BLE_INCLUDED */ + + /* we're done with this routine if BR/EDR inquiry is not desired. */ + if ((p_inqparms->mode & BTM_BR_INQUIRY_MASK) == BTM_INQUIRY_NONE) { + return status; + } + + /* BR/EDR inquiry portion */ +#if (defined(BTA_HOST_INTERLEAVE_SEARCH) && BTA_HOST_INTERLEAVE_SEARCH == TRUE) + if ((p_inq->next_state == BTM_BR_ONE || p_inq->next_state == BTM_BR_TWO || + p_inq->next_state == BTM_NO_INTERLEAVING )) { + p_inq->inq_active = (p_inqparms->mode & BTM_BR_INQUIRY_MASK); +#endif + /* If a filter is specified, then save it for later and clear the current filter. + The setting of the filter is done upon completion of clearing of the previous + filter. + */ + switch (p_inqparms->filter_cond_type) { + case BTM_CLR_INQUIRY_FILTER: + p_inq->state = BTM_INQ_SET_FILT_STATE; + break; + + case BTM_FILTER_COND_DEVICE_CLASS: + case BTM_FILTER_COND_BD_ADDR: + /* The filter is not being used so simply clear it; + the inquiry can start after this operation */ + p_inq->state = BTM_INQ_CLR_FILT_STATE; + p_inqparms->filter_cond_type = BTM_CLR_INQUIRY_FILTER; + /* =============>>>> adding LE filtering here ????? */ + break; + + default: + return (BTM_ILLEGAL_VALUE); + } + + /* Before beginning the inquiry the current filter must be cleared, so initiate the command */ + if ((status = btm_set_inq_event_filter (p_inqparms->filter_cond_type, + &p_inqparms->filter_cond)) != BTM_CMD_STARTED) { + p_inq->state = BTM_INQ_INACTIVE_STATE; + } + +#if (defined(BTA_HOST_INTERLEAVE_SEARCH) && BTA_HOST_INTERLEAVE_SEARCH == TRUE) + if (p_inq->next_state == BTM_NO_INTERLEAVING) { + p_inq->next_state = BTM_FINISH; + } else { + BTM_TRACE_API("BTM:Interleaving: Started BTM inq, Advancing to next state: %d\n", + p_inq->next_state + 1); + p_inq->next_state += 1; + } + } + if (status != BTM_CMD_STARTED) { + /* Some error beginning the scan process. + Reset the next_state parameter.. Do we need to reset the inq_active also? + */ + BTM_TRACE_API("BTM:Interleaving: Error in Starting inquiry, status: 0x%02x\n", status); + p_inq->next_state = BTM_BR_ONE; + } +#endif + + + return (status); +} + + +/******************************************************************************* +** +** Function BTM_ReadRemoteDeviceName +** +** Description This function initiates a remote device HCI command to the +** controller and calls the callback when the process has completed. +** +** Input Params: remote_bda - device address of name to retrieve +** p_cb - callback function called when BTM_CMD_STARTED +** is returned. +** A pointer to tBTM_REMOTE_DEV_NAME is passed to the +** callback. +** +** Returns +** BTM_CMD_STARTED is returned if the request was successfully sent +** to HCI. +** BTM_BUSY if already in progress +** BTM_UNKNOWN_ADDR if device address is bad +** BTM_NO_RESOURCES if could not allocate resources to start the command +** BTM_WRONG_MODE if the device is not up. +** +*******************************************************************************/ +tBTM_STATUS BTM_ReadRemoteDeviceName (BD_ADDR remote_bda, tBTM_CMPL_CB *p_cb + , tBT_TRANSPORT transport) +{ + tBTM_INQ_INFO *p_cur = NULL; + tINQ_DB_ENT *p_i; + + BTM_TRACE_API ("BTM_ReadRemoteDeviceName: bd addr [%02x%02x%02x%02x%02x%02x]\n", + remote_bda[0], remote_bda[1], remote_bda[2], + remote_bda[3], remote_bda[4], remote_bda[5]); + + /* Use the remote device's clock offset if it is in the local inquiry database */ + if ((p_i = btm_inq_db_find (remote_bda)) != NULL) { + p_cur = &p_i->inq_info; + } + BTM_TRACE_API ("no device found in inquiry db\n"); + +#if (BLE_INCLUDED == TRUE) + if (transport == BT_TRANSPORT_LE) { + return btm_ble_read_remote_name(remote_bda, p_cur, p_cb); + } else +#endif + { + return (btm_initiate_rem_name (remote_bda, p_cur, BTM_RMT_NAME_EXT, + BTM_EXT_RMT_NAME_TIMEOUT, p_cb)); + } +} + +/******************************************************************************* +** +** Function BTM_CancelRemoteDeviceName +** +** Description This function initiates the cancel request for the specified +** remote device. +** +** Input Params: None +** +** Returns +** BTM_CMD_STARTED is returned if the request was successfully sent +** to HCI. +** BTM_NO_RESOURCES if could not allocate resources to start the command +** BTM_WRONG_MODE if there is not an active remote name request. +** +*******************************************************************************/ +tBTM_STATUS BTM_CancelRemoteDeviceName (void) +{ + tBTM_INQUIRY_VAR_ST *p_inq = &btm_cb.btm_inq_vars; + + BTM_TRACE_API ("BTM_CancelRemoteDeviceName()\n"); + + /* Make sure there is not already one in progress */ + if (p_inq->remname_active) { +#if BLE_INCLUDED == TRUE + if (BTM_UseLeLink(p_inq->remname_bda)) { + if (btm_ble_cancel_remote_name(p_inq->remname_bda)) { + return (BTM_CMD_STARTED); + } else { + return (BTM_UNKNOWN_ADDR); + } + } else +#endif + { + if (btsnd_hcic_rmt_name_req_cancel (p_inq->remname_bda)) { + return (BTM_CMD_STARTED); + } else { + return (BTM_NO_RESOURCES); + } + } + } else { + return (BTM_WRONG_MODE); + } +} + +/******************************************************************************* +** +** Function BTM_InqDbRead +** +** Description This function looks through the inquiry database for a match +** based on Bluetooth Device Address. This is the application's +** interface to get the inquiry details of a specific BD address. +** +** Returns pointer to entry, or NULL if not found +** +*******************************************************************************/ +tBTM_INQ_INFO *BTM_InqDbRead (BD_ADDR p_bda) +{ + BTM_TRACE_API ("BTM_InqDbRead: bd addr [%02x%02x%02x%02x%02x%02x]\n", + p_bda[0], p_bda[1], p_bda[2], p_bda[3], p_bda[4], p_bda[5]); + + tINQ_DB_ENT *p_ent = btm_inq_db_find(p_bda); + if (!p_ent) { + return NULL; + } + + return &p_ent->inq_info; +} + + +/******************************************************************************* +** +** Function BTM_InqDbFirst +** +** Description This function looks through the inquiry database for the first +** used entry, and returns that. This is used in conjunction with +** BTM_InqDbNext by applications as a way to walk through the +** inquiry database. +** +** Returns pointer to first in-use entry, or NULL if DB is empty +** +*******************************************************************************/ +tBTM_INQ_INFO *BTM_InqDbFirst (void) +{ + UINT16 xx; + tINQ_DB_ENT *p_ent = btm_cb.btm_inq_vars.inq_db; + + for (xx = 0; xx < BTM_INQ_DB_SIZE; xx++, p_ent++) { + if (p_ent->in_use) { + return (&p_ent->inq_info); + } + } + + /* If here, no used entry found */ + return ((tBTM_INQ_INFO *)NULL); +} + + +/******************************************************************************* +** +** Function BTM_InqDbNext +** +** Description This function looks through the inquiry database for the next +** used entry, and returns that. If the input parameter is NULL, +** the first entry is returned. +** +** Returns pointer to next in-use entry, or NULL if no more found. +** +*******************************************************************************/ +tBTM_INQ_INFO *BTM_InqDbNext (tBTM_INQ_INFO *p_cur) +{ + tINQ_DB_ENT *p_ent; + UINT16 inx; + + if (p_cur) { + p_ent = (tINQ_DB_ENT *) ((UINT8 *)p_cur - offsetof (tINQ_DB_ENT, inq_info)); + inx = (UINT16)((p_ent - btm_cb.btm_inq_vars.inq_db) + 1); + + for (p_ent = &btm_cb.btm_inq_vars.inq_db[inx]; inx < BTM_INQ_DB_SIZE; inx++, p_ent++) { + if (p_ent->in_use) { + return (&p_ent->inq_info); + } + } + + /* If here, more entries found */ + return ((tBTM_INQ_INFO *)NULL); + } else { + return (BTM_InqDbFirst()); + } +} + + +/******************************************************************************* +** +** Function BTM_ClearInqDb +** +** Description This function is called to clear out a device or all devices +** from the inquiry database. +** +** Parameter p_bda - (input) BD_ADDR -> Address of device to clear +** (NULL clears all entries) +** +** Returns BTM_BUSY if an inquiry, get remote name, or event filter +** is active, otherwise BTM_SUCCESS +** +*******************************************************************************/ +tBTM_STATUS BTM_ClearInqDb (BD_ADDR p_bda) +{ + tBTM_INQUIRY_VAR_ST *p_inq = &btm_cb.btm_inq_vars; + + /* If an inquiry or remote name is in progress return busy */ + if (p_inq->inq_active != BTM_INQUIRY_INACTIVE || + p_inq->inqfilt_active) { + return (BTM_BUSY); + } + + btm_clr_inq_db(p_bda); + + return (BTM_SUCCESS); +} + +/******************************************************************************* +** +** Function BTM_ReadInquiryRspTxPower +** +** Description This command will read the inquiry Transmit Power level used +** to transmit the FHS and EIR data packets. +** This can be used directly in the Tx Power Level EIR data type. +** +** Returns BTM_SUCCESS if successful +** +*******************************************************************************/ +tBTM_STATUS BTM_ReadInquiryRspTxPower (tBTM_CMPL_CB *p_cb) +{ + if (btm_cb.devcb.p_txpwer_cmpl_cb) { + return (BTM_BUSY); + } + + btu_start_timer (&btm_cb.devcb.txpwer_timer, BTU_TTYPE_BTM_ACL, BTM_INQ_REPLY_TIMEOUT ); + + + btm_cb.devcb.p_txpwer_cmpl_cb = p_cb; + + if (!btsnd_hcic_read_inq_tx_power ()) { + btm_cb.devcb.p_txpwer_cmpl_cb = NULL; + btu_stop_timer (&btm_cb.devcb.txpwer_timer); + return (BTM_NO_RESOURCES); + } else { + return (BTM_CMD_STARTED); + } +} + +/********************************************************************************* +********************************************************************************** +** ** +** BTM Internal Inquiry Functions ** +** ** +********************************************************************************** +*********************************************************************************/ +/******************************************************************************* +** +** Function btm_inq_db_reset +** +** Description This function is called at at reset to clear the inquiry +** database & pending callback. +** +** Returns void +** +*******************************************************************************/ +void btm_inq_db_reset (void) +{ + tBTM_REMOTE_DEV_NAME rem_name; + tBTM_INQUIRY_VAR_ST *p_inq = &btm_cb.btm_inq_vars; + UINT8 num_responses; + UINT8 temp_inq_active; + tBTM_STATUS status; + + btu_stop_timer (&p_inq->inq_timer_ent); + + /* If an inquiry or periodic inquiry is active, reset the mode to inactive */ + if (p_inq->inq_active != BTM_INQUIRY_INACTIVE) { + temp_inq_active = p_inq->inq_active; /* Save so state can change BEFORE + callback is called */ + p_inq->inq_active = BTM_INQUIRY_INACTIVE; + + /* If not a periodic inquiry, the complete callback must be called to notify caller */ + if (temp_inq_active == BTM_LIMITED_INQUIRY_ACTIVE || + temp_inq_active == BTM_GENERAL_INQUIRY_ACTIVE) { + if (p_inq->p_inq_cmpl_cb) { + num_responses = 0; + (*p_inq->p_inq_cmpl_cb)(&num_responses); + } + } + } + + /* Cancel a remote name request if active, and notify the caller (if waiting) */ + if (p_inq->remname_active ) { + btu_stop_timer (&p_inq->rmt_name_timer_ent); + p_inq->remname_active = FALSE; + memset(p_inq->remname_bda, 0, BD_ADDR_LEN); + + if (p_inq->p_remname_cmpl_cb) { + rem_name.status = BTM_DEV_RESET; + + (*p_inq->p_remname_cmpl_cb)(&rem_name); + p_inq->p_remname_cmpl_cb = NULL; + } + } + + /* Cancel an inquiry filter request if active, and notify the caller (if waiting) */ + if (p_inq->inqfilt_active) { + p_inq->inqfilt_active = FALSE; + + if (p_inq->p_inqfilter_cmpl_cb) { + status = BTM_DEV_RESET; + (*p_inq->p_inqfilter_cmpl_cb)(&status); + } + } + + p_inq->state = BTM_INQ_INACTIVE_STATE; + p_inq->pending_filt_complete_event = 0; + p_inq->p_inq_results_cb = NULL; + btm_clr_inq_db(NULL); /* Clear out all the entries in the database */ + btm_clr_inq_result_flt(); + + p_inq->discoverable_mode = BTM_NON_DISCOVERABLE; + p_inq->connectable_mode = BTM_NON_CONNECTABLE; + p_inq->page_scan_type = BTM_SCAN_TYPE_STANDARD; + p_inq->inq_scan_type = BTM_SCAN_TYPE_STANDARD; + +#if BLE_INCLUDED == TRUE + p_inq->discoverable_mode |= BTM_BLE_NON_DISCOVERABLE; + p_inq->connectable_mode |= BTM_BLE_NON_CONNECTABLE; +#endif + return; +} + + +/********************************************************************************* +** +** Function btm_inq_db_init +** +** Description This function is called at startup to initialize the inquiry +** database. +** +** Returns void +** +*******************************************************************************/ +void btm_inq_db_init (void) +{ +#if 0 /* cleared in btm_init; put back in if called from anywhere else! */ + memset (&btm_cb.btm_inq_vars, 0, sizeof (tBTM_INQUIRY_VAR_ST)); +#endif + + btu_free_timer(&btm_cb.btm_inq_vars.rmt_name_timer_ent); + memset(&btm_cb.btm_inq_vars.rmt_name_timer_ent, 0, sizeof(TIMER_LIST_ENT)); + btu_free_timer(&btm_cb.btm_inq_vars.inq_timer_ent); + memset(&btm_cb.btm_inq_vars.inq_timer_ent, 0, sizeof(TIMER_LIST_ENT)); + + btm_cb.btm_inq_vars.no_inc_ssp = BTM_NO_SSP_ON_INQUIRY; +} + +/********************************************************************************* +** +** Function btm_inq_stop_on_ssp +** +** Description This function is called on incoming SSP +** +** Returns void +** +*******************************************************************************/ +void btm_inq_stop_on_ssp(void) +{ + UINT8 normal_active = (BTM_GENERAL_INQUIRY_ACTIVE | BTM_LIMITED_INQUIRY_ACTIVE); + +#if (BTM_INQ_DEBUG == TRUE) + BTM_TRACE_DEBUG ("btm_inq_stop_on_ssp: no_inc_ssp=%d inq_active:0x%x state:%d inqfilt_active:%d\n", + btm_cb.btm_inq_vars.no_inc_ssp, btm_cb.btm_inq_vars.inq_active, btm_cb.btm_inq_vars.state, btm_cb.btm_inq_vars.inqfilt_active); +#endif + if (btm_cb.btm_inq_vars.no_inc_ssp) { + if (btm_cb.btm_inq_vars.state == BTM_INQ_ACTIVE_STATE) { + if (btm_cb.btm_inq_vars.inq_active & BTM_PERIODIC_INQUIRY_ACTIVE) { + BTM_CancelPeriodicInquiry(); + } else if (btm_cb.btm_inq_vars.inq_active & normal_active) { + /* can not call BTM_CancelInquiry() here. We need to report inquiry complete evt */ + btsnd_hcic_inq_cancel(); + } + } + /* do not allow inquiry to start */ + btm_cb.btm_inq_vars.inq_active |= BTM_SSP_INQUIRY_ACTIVE; + } +} + +/********************************************************************************* +** +** Function btm_inq_clear_ssp +** +** Description This function is called when pairing_state becomes idle +** +** Returns void +** +*******************************************************************************/ +void btm_inq_clear_ssp(void) +{ + btm_cb.btm_inq_vars.inq_active &= ~BTM_SSP_INQUIRY_ACTIVE; +} + +/********************************************************************************* +** +** Function btm_clr_inq_db +** +** Description This function is called to clear out a device or all devices +** from the inquiry database. +** +** Parameter p_bda - (input) BD_ADDR -> Address of device to clear +** (NULL clears all entries) +** +** Returns void +** +*******************************************************************************/ +void btm_clr_inq_db (BD_ADDR p_bda) +{ + tBTM_INQUIRY_VAR_ST *p_inq = &btm_cb.btm_inq_vars; + tINQ_DB_ENT *p_ent = p_inq->inq_db; + UINT16 xx; + +#if (BTM_INQ_DEBUG == TRUE) + BTM_TRACE_DEBUG ("btm_clr_inq_db: inq_active:0x%x state:%d\n", + btm_cb.btm_inq_vars.inq_active, btm_cb.btm_inq_vars.state); +#endif + for (xx = 0; xx < BTM_INQ_DB_SIZE; xx++, p_ent++) { + if (p_ent->in_use) { + /* If this is the specified BD_ADDR or clearing all devices */ + if (p_bda == NULL || + (!memcmp (p_ent->inq_info.results.remote_bd_addr, p_bda, BD_ADDR_LEN))) { + p_ent->in_use = FALSE; + } + } + } +#if (BTM_INQ_DEBUG == TRUE) + BTM_TRACE_DEBUG ("inq_active:0x%x state:%d\n", + btm_cb.btm_inq_vars.inq_active, btm_cb.btm_inq_vars.state); +#endif +} + + +/******************************************************************************* +** +** Function btm_clr_inq_result_flt +** +** Description This function looks through the bdaddr database for a match +** based on Bluetooth Device Address +** +** Returns TRUE if found, else FALSE (new entry) +** +*******************************************************************************/ +static void btm_clr_inq_result_flt (void) +{ + tBTM_INQUIRY_VAR_ST *p_inq = &btm_cb.btm_inq_vars; + + if (p_inq->p_bd_db) { + osi_free(p_inq->p_bd_db); + p_inq->p_bd_db = NULL; + } + p_inq->num_bd_entries = 0; + p_inq->max_bd_entries = 0; +} + +/******************************************************************************* +** +** Function btm_inq_find_bdaddr +** +** Description This function looks through the bdaddr database for a match +** based on Bluetooth Device Address +** +** Returns TRUE if found, else FALSE (new entry) +** +*******************************************************************************/ +BOOLEAN btm_inq_find_bdaddr (BD_ADDR p_bda) +{ + tBTM_INQUIRY_VAR_ST *p_inq = &btm_cb.btm_inq_vars; + tINQ_BDADDR *p_db = &p_inq->p_bd_db[0]; + UINT16 xx; + + /* Don't bother searching, database doesn't exist or periodic mode */ + if ((p_inq->inq_active & BTM_PERIODIC_INQUIRY_ACTIVE) || !p_db) { + return (FALSE); + } + + for (xx = 0; xx < p_inq->num_bd_entries; xx++, p_db++) { + if (!memcmp(p_db->bd_addr, p_bda, BD_ADDR_LEN) + && p_db->inq_count == p_inq->inq_counter) { + return (TRUE); + } + } + + if (xx < p_inq->max_bd_entries) { + p_db->inq_count = p_inq->inq_counter; + memcpy(p_db->bd_addr, p_bda, BD_ADDR_LEN); + p_inq->num_bd_entries++; + } + + /* If here, New Entry */ + return (FALSE); +} + +/******************************************************************************* +** +** Function btm_inq_db_find +** +** Description This function looks through the inquiry database for a match +** based on Bluetooth Device Address +** +** Returns pointer to entry, or NULL if not found +** +*******************************************************************************/ +tINQ_DB_ENT *btm_inq_db_find (BD_ADDR p_bda) +{ + UINT16 xx; + tINQ_DB_ENT *p_ent = btm_cb.btm_inq_vars.inq_db; + + for (xx = 0; xx < BTM_INQ_DB_SIZE; xx++, p_ent++) { + if ((p_ent->in_use) && (!memcmp (p_ent->inq_info.results.remote_bd_addr, p_bda, BD_ADDR_LEN))) { + return (p_ent); + } + } + + /* If here, not found */ + return (NULL); +} + + +/******************************************************************************* +** +** Function btm_inq_db_new +** +** Description This function looks through the inquiry database for an unused +** entry. If no entry is free, it allocates the oldest entry. +** +** Returns pointer to entry +** +*******************************************************************************/ +tINQ_DB_ENT *btm_inq_db_new (BD_ADDR p_bda) +{ + UINT16 xx; + tINQ_DB_ENT *p_ent = btm_cb.btm_inq_vars.inq_db; + tINQ_DB_ENT *p_old = btm_cb.btm_inq_vars.inq_db; + UINT32 ot = 0xFFFFFFFF; + + for (xx = 0; xx < BTM_INQ_DB_SIZE; xx++, p_ent++) { + if (!p_ent->in_use) { + memset (p_ent, 0, sizeof (tINQ_DB_ENT)); + memcpy (p_ent->inq_info.results.remote_bd_addr, p_bda, BD_ADDR_LEN); + p_ent->in_use = TRUE; + + return (p_ent); + } + + if (p_ent->time_of_resp < ot) { + p_old = p_ent; + ot = p_ent->time_of_resp; + } + } + + /* If here, no free entry found. Return the oldest. */ + + memset (p_old, 0, sizeof (tINQ_DB_ENT)); + memcpy (p_old->inq_info.results.remote_bd_addr, p_bda, BD_ADDR_LEN); + p_old->in_use = TRUE; + + return (p_old); +} + + +/******************************************************************************* +** +** Function btm_set_inq_event_filter +** +** Description This function is called to set the inquiry event filter. +** It is called by either internally, or by the external API function +** (BTM_SetInqEventFilter). It is used internally as part of the +** inquiry processing. +** +** Input Params: +** filter_cond_type - this is the type of inquiry filter to apply: +** BTM_FILTER_COND_DEVICE_CLASS, +** BTM_FILTER_COND_BD_ADDR, or +** BTM_CLR_INQUIRY_FILTER +** +** p_filt_cond - this is either a BD_ADDR or DEV_CLASS depending on the +** filter_cond_type (See section 4.7.3 of Core Spec 1.0b). +** +** Returns BTM_CMD_STARTED if successfully initiated +** BTM_NO_RESOURCES if couldn't get a memory pool buffer +** BTM_ILLEGAL_VALUE if a bad parameter was detected +** +*******************************************************************************/ +static tBTM_STATUS btm_set_inq_event_filter (UINT8 filter_cond_type, + tBTM_INQ_FILT_COND *p_filt_cond) +{ + UINT8 condition_length = DEV_CLASS_LEN * 2; + UINT8 condition_buf[DEV_CLASS_LEN * 2]; + UINT8 *p_cond = condition_buf; /* points to the condition to pass to HCI */ + +#if (BTM_INQ_DEBUG == TRUE) + BTM_TRACE_DEBUG ("btm_set_inq_event_filter: filter type %d [Clear-0, COD-1, BDADDR-2]\n", + filter_cond_type); + BTM_TRACE_DEBUG (" condition [%02x%02x%02x %02x%02x%02x]\n", + p_filt_cond->bdaddr_cond[0], p_filt_cond->bdaddr_cond[1], p_filt_cond->bdaddr_cond[2], + p_filt_cond->bdaddr_cond[3], p_filt_cond->bdaddr_cond[4], p_filt_cond->bdaddr_cond[5]); +#endif + + /* Load the correct filter condition to pass to the lower layer */ + switch (filter_cond_type) { + case BTM_FILTER_COND_DEVICE_CLASS: + /* copy the device class and device class fields into contiguous memory to send to HCI */ + memcpy (condition_buf, p_filt_cond->cod_cond.dev_class, DEV_CLASS_LEN); + memcpy (&condition_buf[DEV_CLASS_LEN], + p_filt_cond->cod_cond.dev_class_mask, DEV_CLASS_LEN); + + /* condition length should already be set as the default */ + break; + + case BTM_FILTER_COND_BD_ADDR: + p_cond = p_filt_cond->bdaddr_cond; + + /* condition length should already be set as the default */ + break; + + case BTM_CLR_INQUIRY_FILTER: + condition_length = 0; + break; + + default: + return (BTM_ILLEGAL_VALUE); /* Bad parameter was passed in */ + } + + btm_cb.btm_inq_vars.inqfilt_active = TRUE; + + /* Filter the inquiry results for the specified condition type and value */ + if (btsnd_hcic_set_event_filter(HCI_FILTER_INQUIRY_RESULT, filter_cond_type, + p_cond, condition_length)) + + { + return (BTM_CMD_STARTED); + } else { + return (BTM_NO_RESOURCES); + } +} + + +/******************************************************************************* +** +** Function btm_event_filter_complete +** +** Description This function is called when a set event filter has completed. +** Note: This routine currently only handles inquiry filters. +** Connection filters are ignored for now. +** +** Returns void +** +*******************************************************************************/ +void btm_event_filter_complete (UINT8 *p) +{ + UINT8 hci_status; + tBTM_STATUS status; + tBTM_INQUIRY_VAR_ST *p_inq = &btm_cb.btm_inq_vars; + tBTM_CMPL_CB *p_cb = p_inq->p_inqfilter_cmpl_cb; + +#if (BTM_INQ_DEBUG == TRUE) + BTM_TRACE_DEBUG ("btm_event_filter_complete: inq_active:0x%x state:%d inqfilt_active:%d\n", + btm_cb.btm_inq_vars.inq_active, btm_cb.btm_inq_vars.state, btm_cb.btm_inq_vars.inqfilt_active); +#endif + /* If the filter complete event is from an old or cancelled request, ignore it */ + if (p_inq->pending_filt_complete_event) { + p_inq->pending_filt_complete_event--; + return; + } + + /* Only process the inquiry filter; Ignore the connection filter until it + is used by the upper layers */ + if (p_inq->inqfilt_active == TRUE ) { + /* Extract the returned status from the buffer */ + STREAM_TO_UINT8 (hci_status, p); + if (hci_status != HCI_SUCCESS) { + /* If standalone operation, return the error status; if embedded in the inquiry, continue the inquiry */ + BTM_TRACE_WARNING ("BTM Warning: Set Event Filter Failed (HCI returned 0x%x)\n", hci_status); + status = BTM_ERR_PROCESSING; + } else { + status = BTM_SUCCESS; + } + + /* If the set filter was initiated externally (via BTM_SetInqEventFilter), call the + callback function to notify the initiator that it has completed */ + if (p_inq->state == BTM_INQ_INACTIVE_STATE) { + p_inq->inqfilt_active = FALSE; + if (p_cb) { + (*p_cb) (&status); + } + } else /* An inquiry is active (the set filter command was internally generated), + process the next state of the process (Set a new filter or start the inquiry). */ + { + if (status != BTM_SUCCESS) { + /* Process the inquiry complete (Error Status) */ + btm_process_inq_complete (BTM_ERR_PROCESSING, (UINT8)(p_inq->inqparms.mode & BTM_BR_INQUIRY_MASK)); + + /* btm_process_inq_complete() does not restore the following settings on periodic inquiry */ + p_inq->inqfilt_active = FALSE; + p_inq->inq_active = BTM_INQUIRY_INACTIVE; + p_inq->state = BTM_INQ_INACTIVE_STATE; + + return; + } + + /* Check to see if a new filter needs to be set up */ + if (p_inq->state == BTM_INQ_CLR_FILT_STATE) { + if ((status = btm_set_inq_event_filter (p_inq->inqparms.filter_cond_type, &p_inq->inqparms.filter_cond)) == BTM_CMD_STARTED) { + p_inq->state = BTM_INQ_SET_FILT_STATE; + } else { /* Error setting the filter: Call the initiator's callback function to indicate a failure */ + p_inq->inqfilt_active = FALSE; + + /* Process the inquiry complete (Error Status) */ + btm_process_inq_complete (BTM_ERR_PROCESSING, (UINT8)(p_inq->inqparms.mode & BTM_BR_INQUIRY_MASK)); + } + } else { /* Initiate the Inquiry or Periodic Inquiry */ + p_inq->state = BTM_INQ_ACTIVE_STATE; + p_inq->inqfilt_active = FALSE; + btm_initiate_inquiry (p_inq); + } + } + } +} + + +/******************************************************************************* +** +** Function btm_initiate_inquiry +** +** Description This function is called to start an inquiry or periodic inquiry +** upon completion of the setting and/or clearing of the inquiry filter. +** +** Inputs: p_inq (btm_cb.btm_inq_vars) - pointer to saved inquiry information +** mode - GENERAL or LIMITED inquiry +** duration - length in 1.28 sec intervals (If '0', the inquiry is CANCELLED) +** max_resps - maximum amount of devices to search for before ending the inquiry +** filter_cond_type - BTM_CLR_INQUIRY_FILTER, BTM_FILTER_COND_DEVICE_CLASS, or +** BTM_FILTER_COND_BD_ADDR +** filter_cond - value for the filter (based on filter_cond_type) +** +** Returns If an error occurs the initiator's callback is called with the error status. +** +*******************************************************************************/ +static void btm_initiate_inquiry (tBTM_INQUIRY_VAR_ST *p_inq) +{ + const LAP *lap; + tBTM_INQ_PARMS *p_inqparms = &p_inq->inqparms; + +#if (BTM_INQ_DEBUG == TRUE) + BTM_TRACE_DEBUG ("btm_initiate_inquiry: inq_active:0x%x state:%d inqfilt_active:%d\n", + btm_cb.btm_inq_vars.inq_active, btm_cb.btm_inq_vars.state, btm_cb.btm_inq_vars.inqfilt_active); +#endif + btm_acl_update_busy_level (BTM_BLI_INQ_EVT); + + if (p_inq->inq_active & BTM_SSP_INQUIRY_ACTIVE) { + btm_process_inq_complete (BTM_NO_RESOURCES, (UINT8)(p_inqparms->mode & BTM_BR_INQUIRY_MASK)); + return; + } + + /* Make sure the number of responses doesn't overflow the database configuration */ + p_inqparms->max_resps = (UINT8)((p_inqparms->max_resps <= BTM_INQ_DB_SIZE) ? p_inqparms->max_resps : BTM_INQ_DB_SIZE); + + lap = (p_inq->inq_active & BTM_LIMITED_INQUIRY_ACTIVE) ? &limited_inq_lap : &general_inq_lap; + + if (p_inq->inq_active & BTM_PERIODIC_INQUIRY_ACTIVE) { + if (!btsnd_hcic_per_inq_mode (p_inq->per_max_delay, + p_inq->per_min_delay, + *lap, p_inqparms->duration, + p_inqparms->max_resps)) { + btm_process_inq_complete (BTM_NO_RESOURCES, (UINT8)(p_inqparms->mode & BTM_BR_INQUIRY_MASK)); + } + } else { + btm_clr_inq_result_flt(); + + /* Allocate memory to hold bd_addrs responding */ + if ((p_inq->p_bd_db = (tINQ_BDADDR *)osi_calloc(BT_DEFAULT_BUFFER_SIZE)) != NULL) { + p_inq->max_bd_entries = (UINT16)(BT_DEFAULT_BUFFER_SIZE / sizeof(tINQ_BDADDR)); + /* BTM_TRACE_DEBUG("btm_initiate_inquiry: memory allocated for %d bdaddrs", + p_inq->max_bd_entries); */ + } + + if (!btsnd_hcic_inquiry(*lap, p_inqparms->duration, 0)) { + btm_process_inq_complete (BTM_NO_RESOURCES, (UINT8)(p_inqparms->mode & BTM_BR_INQUIRY_MASK)); + } + } +} + +/******************************************************************************* +** +** Function btm_process_inq_results +** +** Description This function is called when inquiry results are received from +** the device. It updates the inquiry database. If the inquiry +** database is full, the oldest entry is discarded. +** +** Parameters inq_res_mode - BTM_INQ_RESULT_STANDARD +** BTM_INQ_RESULT_WITH_RSSI +** BTM_INQ_RESULT_EXTENDED +** +** Returns void +** +*******************************************************************************/ +void btm_process_inq_results (UINT8 *p, UINT8 inq_res_mode) +{ + UINT8 num_resp, xx; + BD_ADDR bda; + tINQ_DB_ENT *p_i; + tBTM_INQ_RESULTS *p_cur = NULL; + BOOLEAN is_new = TRUE; + BOOLEAN update = FALSE; + INT8 i_rssi; + tBTM_INQUIRY_VAR_ST *p_inq = &btm_cb.btm_inq_vars; + tBTM_INQ_RESULTS_CB *p_inq_results_cb = p_inq->p_inq_results_cb; + UINT8 page_scan_rep_mode = 0; + UINT8 page_scan_per_mode = 0; + UINT8 page_scan_mode = 0; + UINT8 rssi = 0; + DEV_CLASS dc; + UINT16 clock_offset; + UINT8 *p_eir_data = NULL; + +#if (BTM_INQ_DEBUG == TRUE) + BTM_TRACE_DEBUG ("btm_process_inq_results inq_active:0x%x state:%d inqfilt_active:%d inq_res_mode=%d\n", + btm_cb.btm_inq_vars.inq_active, btm_cb.btm_inq_vars.state, btm_cb.btm_inq_vars.inqfilt_active, inq_res_mode); +#endif + /* Only process the results if the BR inquiry is still active */ + if (!(p_inq->inq_active & BTM_BR_INQ_ACTIVE_MASK)) { + return; + } + + STREAM_TO_UINT8 (num_resp, p); + + for (xx = 0; xx < num_resp; xx++) { + update = FALSE; + /* Extract inquiry results */ + STREAM_TO_BDADDR (bda, p); + STREAM_TO_UINT8 (page_scan_rep_mode, p); + STREAM_TO_UINT8 (page_scan_per_mode, p); + + if (inq_res_mode == BTM_INQ_RESULT_STANDARD) { + STREAM_TO_UINT8(page_scan_mode, p); + } + + STREAM_TO_DEVCLASS (dc, p); + STREAM_TO_UINT16 (clock_offset, p); + if (inq_res_mode != BTM_INQ_RESULT_STANDARD) { + STREAM_TO_UINT8(rssi, p); + } + + p_i = btm_inq_db_find (bda); + + /* Only process the num_resp is smaller than max_resps. + If results are queued to BTU task while canceling inquiry, + or when more than one result is in this response, > max_resp + responses could be processed which can confuse some apps + */ + if (p_inq->inqparms.max_resps && + p_inq->inq_cmpl_info.num_resp >= p_inq->inqparms.max_resps +#if BLE_INCLUDED == TRUE + /* new device response */ + && ( p_i == NULL || + /* exisiting device with BR/EDR info */ + (p_i && (p_i->inq_info.results.device_type & BT_DEVICE_TYPE_BREDR) != 0) + ) +#endif + + ) { + BTM_TRACE_WARNING("INQ RES: Extra Response Received...ignoring\n"); + return; + } + + /* Check if this address has already been processed for this inquiry */ + if (btm_inq_find_bdaddr(bda)) { + BTM_TRACE_DEBUG("BDA seen before [%02x%02x %02x%02x %02x%02x]\n", + bda[0], bda[1], bda[2], bda[3], bda[4], bda[5]); + /* By default suppose no update needed */ + i_rssi = (INT8)rssi; + + /* If this new RSSI is higher than the last one */ + if (p_inq->inqparms.report_dup && (rssi != 0) && + p_i && (i_rssi > p_i->inq_info.results.rssi || p_i->inq_info.results.rssi == 0 +#if BLE_INCLUDED == TRUE + /* BR/EDR inquiry information update */ + || (p_i->inq_info.results.device_type & BT_DEVICE_TYPE_BREDR) != 0 +#endif + )) { + p_cur = &p_i->inq_info.results; + BTM_TRACE_DEBUG("update RSSI new:%d, old:%d\n", i_rssi, p_cur->rssi); + p_cur->rssi = i_rssi; + update = TRUE; + } + /* If we received a second Extended Inq Event for an already */ + /* discovered device, this is because for the first one EIR was not received */ + else if ((inq_res_mode == BTM_INQ_RESULT_EXTENDED) && (p_i)) { + p_cur = &p_i->inq_info.results; + update = TRUE; + } + /* If no update needed continue with next response (if any) */ + else { + continue; + } + } + + /* If existing entry, use that, else get a new one (possibly reusing the oldest) */ + if (p_i == NULL) { + p_i = btm_inq_db_new (bda); + is_new = TRUE; + } + + /* If an entry for the device already exists, overwrite it ONLY if it is from + a previous inquiry. (Ignore it if it is a duplicate response from the same + inquiry. + */ + else if (p_i->inq_count == p_inq->inq_counter +#if (BLE_INCLUDED == TRUE ) + && (p_i->inq_info.results.device_type == BT_DEVICE_TYPE_BREDR) +#endif + ) { + is_new = FALSE; + } + + /* keep updating RSSI to have latest value */ + if ( inq_res_mode != BTM_INQ_RESULT_STANDARD ) { + p_i->inq_info.results.rssi = (INT8)rssi; + } else { + p_i->inq_info.results.rssi = BTM_INQ_RES_IGNORE_RSSI; + } + + if (is_new == TRUE) { + /* Save the info */ + p_cur = &p_i->inq_info.results; + p_cur->page_scan_rep_mode = page_scan_rep_mode; + p_cur->page_scan_per_mode = page_scan_per_mode; + p_cur->page_scan_mode = page_scan_mode; + p_cur->dev_class[0] = dc[0]; + p_cur->dev_class[1] = dc[1]; + p_cur->dev_class[2] = dc[2]; + p_cur->clock_offset = clock_offset | BTM_CLOCK_OFFSET_VALID; + + p_i->time_of_resp = osi_time_get_os_boottime_ms(); + + if (p_i->inq_count != p_inq->inq_counter) { + p_inq->inq_cmpl_info.num_resp++; /* A new response was found */ + } + +#if (defined BLE_INCLUDED && BLE_INCLUDED == TRUE) + p_cur->inq_result_type = BTM_INQ_RESULT_BR; + if (p_i->inq_count != p_inq->inq_counter) { + p_cur->device_type = BT_DEVICE_TYPE_BREDR; + p_i->scan_rsp = FALSE; + } else { + p_cur->device_type |= BT_DEVICE_TYPE_BREDR; + } +#endif + p_i->inq_count = p_inq->inq_counter; /* Mark entry for current inquiry */ + + /* If the number of responses found and not unlimited, issue a cancel inquiry */ + if (!(p_inq->inq_active & BTM_PERIODIC_INQUIRY_ACTIVE) && + p_inq->inqparms.max_resps && + p_inq->inq_cmpl_info.num_resp == p_inq->inqparms.max_resps +#if BLE_INCLUDED == TRUE + /* BLE scanning is active and received adv */ + && ((((p_inq->inqparms.mode & BTM_BLE_INQUIRY_MASK) != 0) && + p_cur->device_type == BT_DEVICE_TYPE_DUMO && p_i->scan_rsp) || + (p_inq->inqparms.mode & BTM_BLE_INQUIRY_MASK) == 0) +#endif + ) { + /* BTM_TRACE_DEBUG("BTMINQ: Found devices, cancelling inquiry..."); */ + btsnd_hcic_inq_cancel(); + +#if BLE_INCLUDED == TRUE + if ((p_inq->inqparms.mode & BTM_BLE_INQUIRY_MASK) != 0) { + btm_ble_stop_inquiry(); + } +#endif + btm_acl_update_busy_level (BTM_BLI_INQ_DONE_EVT); + } + /* Initialize flag to FALSE. This flag is set/used by application */ + p_i->inq_info.appl_knows_rem_name = FALSE; + } + + if (is_new || update) { + if ( inq_res_mode == BTM_INQ_RESULT_EXTENDED ) { + memset( p_cur->eir_uuid, 0, + BTM_EIR_SERVICE_ARRAY_SIZE * (BTM_EIR_ARRAY_BITS / 8)); + /* set bit map of UUID list from received EIR */ + btm_set_eir_uuid( p, p_cur ); + p_eir_data = p; + } else { + p_eir_data = NULL; + } + + /* If a callback is registered, call it with the results */ + if (p_inq_results_cb) { + (p_inq_results_cb)((tBTM_INQ_RESULTS *) p_cur, p_eir_data); + } + } + } +} + +/******************************************************************************* +** +** Function btm_sort_inq_result +** +** Description This function is called when inquiry complete is received +** from the device to sort inquiry results based on rssi. +** +** Returns void +** +*******************************************************************************/ +void btm_sort_inq_result(void) +{ + UINT8 xx, yy, num_resp; + tINQ_DB_ENT *p_tmp = NULL; + tINQ_DB_ENT *p_ent = btm_cb.btm_inq_vars.inq_db; + tINQ_DB_ENT *p_next = btm_cb.btm_inq_vars.inq_db + 1; + int size; + + num_resp = (btm_cb.btm_inq_vars.inq_cmpl_info.num_resp < BTM_INQ_DB_SIZE) ? + btm_cb.btm_inq_vars.inq_cmpl_info.num_resp : BTM_INQ_DB_SIZE; + + if ((p_tmp = (tINQ_DB_ENT *)osi_malloc(sizeof(tINQ_DB_ENT))) != NULL) { + size = sizeof(tINQ_DB_ENT); + for (xx = 0; xx < num_resp - 1; xx++, p_ent++) { + for (yy = xx + 1, p_next = p_ent + 1; yy < num_resp; yy++, p_next++) { + if (p_ent->inq_info.results.rssi < p_next->inq_info.results.rssi) { + memcpy (p_tmp, p_next, size); + memcpy (p_next, p_ent, size); + memcpy (p_ent, p_tmp, size); + } + } + } + + osi_free(p_tmp); + } +} + +/******************************************************************************* +** +** Function btm_process_inq_complete +** +** Description This function is called when inquiry complete is received +** from the device. Call the callback if not in periodic inquiry +** mode AND it is not NULL (The caller wants the event). +** +** The callback pass back the status and the number of responses +** +** Returns void +** +*******************************************************************************/ +void btm_process_inq_complete (UINT8 status, UINT8 mode) +{ + tBTM_CMPL_CB *p_inq_cb = btm_cb.btm_inq_vars.p_inq_cmpl_cb; + tBTM_INQUIRY_VAR_ST *p_inq = &btm_cb.btm_inq_vars; + +#if (defined(BTA_HOST_INTERLEAVE_SEARCH) && BTA_HOST_INTERLEAVE_SEARCH == TRUE) + /* inquiry inactive case happens when inquiry is cancelled. + Make mode 0 for no further inquiries from the current inquiry process + */ + if (status != HCI_SUCCESS || p_inq->next_state == BTM_FINISH || !p_inq->inq_active) { + /* re-initialize for next inquiry request */ + p_inq->next_state = BTM_BR_ONE; + /* make the mode 0 here */ + p_inq->inqparms.mode &= ~(p_inq->inqparms.mode); + + } +#endif + +#if (!defined(BTA_HOST_INTERLEAVE_SEARCH) || BTA_HOST_INTERLEAVE_SEARCH == FALSE) + p_inq->inqparms.mode &= ~(mode); +#endif + + if (p_inq->scan_type == INQ_LE_OBSERVE && !p_inq->inq_active) { + /*end of LE observe*/ + p_inq->p_inq_ble_results_cb = (tBTM_INQ_RESULTS_CB *) NULL; + p_inq->p_inq_ble_cmpl_cb = (tBTM_CMPL_CB *) NULL; + p_inq->scan_type = INQ_NONE; + } + + +#if (BTM_INQ_DEBUG == TRUE) + BTM_TRACE_DEBUG ("btm_process_inq_complete inq_active:0x%x state:%d inqfilt_active:%d\n", + btm_cb.btm_inq_vars.inq_active, btm_cb.btm_inq_vars.state, btm_cb.btm_inq_vars.inqfilt_active); +#endif + btm_acl_update_busy_level (BTM_BLI_INQ_DONE_EVT); + /* Ignore any stray or late complete messages if the inquiry is not active */ + if (p_inq->inq_active) { + p_inq->inq_cmpl_info.status = (tBTM_STATUS)((status == HCI_SUCCESS) ? BTM_SUCCESS : BTM_ERR_PROCESSING); + + /* Notify caller that the inquiry has completed; (periodic inquiries do not send completion events */ + if (!(p_inq->inq_active & BTM_PERIODIC_INQUIRY_ACTIVE) && p_inq->inqparms.mode == 0) { +#if BLE_INCLUDED == TRUE + btm_clear_all_pending_le_entry(); +#endif + p_inq->state = BTM_INQ_INACTIVE_STATE; + + /* Increment so the start of a next inquiry has a new count */ + p_inq->inq_counter++; + + btm_clr_inq_result_flt(); + + if ((p_inq->inq_cmpl_info.status == BTM_SUCCESS) && + controller_get_interface()->supports_rssi_with_inquiry_results()) { + btm_sort_inq_result(); + } + + /* Clear the results callback if set */ + p_inq->p_inq_results_cb = (tBTM_INQ_RESULTS_CB *) NULL; + p_inq->inq_active = BTM_INQUIRY_INACTIVE; + p_inq->p_inq_cmpl_cb = (tBTM_CMPL_CB *) NULL; + + /* If we have a callback registered for inquiry complete, call it */ + BTM_TRACE_DEBUG ("BTM Inq Compl Callback: status 0x%02x, num results %d\n", + p_inq->inq_cmpl_info.status, p_inq->inq_cmpl_info.num_resp); + + if (p_inq_cb) { + (p_inq_cb)((tBTM_INQUIRY_CMPL *) &p_inq->inq_cmpl_info); + } + } +#if (defined(BTA_HOST_INTERLEAVE_SEARCH) && BTA_HOST_INTERLEAVE_SEARCH == TRUE) + if (p_inq->inqparms.mode != 0 && !(p_inq->inq_active & BTM_PERIODIC_INQUIRY_ACTIVE)) { + /* make inquiry inactive for next iteration */ + p_inq->inq_active = BTM_INQUIRY_INACTIVE; + /* call the inquiry again */ + BTM_StartInquiry(&p_inq->inqparms, p_inq->p_inq_results_cb, p_inq->p_inq_cmpl_cb); + } +#endif + } + if (p_inq->inqparms.mode == 0 && p_inq->scan_type == INQ_GENERAL) { //this inquiry is complete + p_inq->scan_type = INQ_NONE; +#if (defined BLE_INCLUDED && BLE_INCLUDED == TRUE) + /* check if the LE observe is pending */ + if (p_inq->p_inq_ble_results_cb != NULL) { + BTM_TRACE_DEBUG("BTM Inq Compl: resuming a pending LE scan"); + BTM_BleObserve(1, 0, p_inq->p_inq_ble_results_cb, p_inq->p_inq_ble_cmpl_cb); + } +#endif + } +#if (BTM_INQ_DEBUG == TRUE) + BTM_TRACE_DEBUG ("inq_active:0x%x state:%d inqfilt_active:%d\n", + btm_cb.btm_inq_vars.inq_active, btm_cb.btm_inq_vars.state, btm_cb.btm_inq_vars.inqfilt_active); +#endif +} + +/******************************************************************************* +** +** Function btm_process_cancel_complete +** +** Description This function is called when inquiry cancel complete is received +** from the device.This function will also call the btm_process_inq_complete +** This function is needed to differentiate a cancel_cmpl_evt from the +** inq_cmpl_evt +** +** Returns void +** +*******************************************************************************/ +void btm_process_cancel_complete(UINT8 status, UINT8 mode) +{ + btm_acl_update_busy_level (BTM_BLI_INQ_CANCEL_EVT); + btm_process_inq_complete(status, mode); +} +/******************************************************************************* +** +** Function btm_initiate_rem_name +** +** Description This function looks initiates a remote name request. It is called +** either by GAP or by the API call BTM_ReadRemoteDeviceName. +** +** Input Params: p_cur - pointer to an inquiry result structure (NULL if nonexistent) +** p_cb - callback function called when BTM_CMD_STARTED +** is returned. +** A pointer to tBTM_REMOTE_DEV_NAME is passed to the +** callback. +** +** Returns +** BTM_CMD_STARTED is returned if the request was sent to HCI. +** BTM_BUSY if already in progress +** BTM_NO_RESOURCES if could not allocate resources to start the command +** BTM_WRONG_MODE if the device is not up. +** +*******************************************************************************/ +tBTM_STATUS btm_initiate_rem_name (BD_ADDR remote_bda, tBTM_INQ_INFO *p_cur, + UINT8 origin, UINT32 timeout, tBTM_CMPL_CB *p_cb) +{ + tBTM_INQUIRY_VAR_ST *p_inq = &btm_cb.btm_inq_vars; + BOOLEAN cmd_ok; + + /*** Make sure the device is ready ***/ + if (!BTM_IsDeviceUp()) { + return (BTM_WRONG_MODE); + } + + if (origin == BTM_RMT_NAME_SEC) { + cmd_ok = btsnd_hcic_rmt_name_req (remote_bda, HCI_PAGE_SCAN_REP_MODE_R1, + HCI_MANDATARY_PAGE_SCAN_MODE, 0); + if (cmd_ok) { + return BTM_CMD_STARTED; + } else { + return BTM_NO_RESOURCES; + } + } + /* Make sure there are no two remote name requests from external API in progress */ + else if (origin == BTM_RMT_NAME_EXT) { + if (p_inq->remname_active) { + return (BTM_BUSY); + } else { + /* If there is no remote name request running,call the callback function and start timer */ + p_inq->p_remname_cmpl_cb = p_cb; + memcpy(p_inq->remname_bda, remote_bda, BD_ADDR_LEN); + btu_start_timer (&p_inq->rmt_name_timer_ent, + BTU_TTYPE_BTM_RMT_NAME, + timeout); + + /* If the database entry exists for the device, use its clock offset */ + if (p_cur) { + cmd_ok = btsnd_hcic_rmt_name_req (remote_bda, + p_cur->results.page_scan_rep_mode, + p_cur->results.page_scan_mode, + (UINT16)(p_cur->results.clock_offset | + BTM_CLOCK_OFFSET_VALID)); + } else { /* Otherwise use defaults and mark the clock offset as invalid */ + cmd_ok = btsnd_hcic_rmt_name_req (remote_bda, HCI_PAGE_SCAN_REP_MODE_R1, + HCI_MANDATARY_PAGE_SCAN_MODE, 0); + } + if (cmd_ok) { + p_inq->remname_active = TRUE; + return BTM_CMD_STARTED; + } else { + return BTM_NO_RESOURCES; + } + } + } else { + return BTM_ILLEGAL_VALUE; + } +} + +/******************************************************************************* +** +** Function btm_process_remote_name +** +** Description This function is called when a remote name is received from +** the device. If remote names are cached, it updates the inquiry +** database. +** +** Returns void +** +*******************************************************************************/ +void btm_process_remote_name (BD_ADDR bda, BD_NAME bdn, UINT16 evt_len, UINT8 hci_status) +{ + tBTM_REMOTE_DEV_NAME rem_name; + tBTM_INQUIRY_VAR_ST *p_inq = &btm_cb.btm_inq_vars; + tBTM_CMPL_CB *p_cb = p_inq->p_remname_cmpl_cb; + UINT8 *p_n1; + + UINT16 temp_evt_len; + + if (bda != NULL) { + BTM_TRACE_EVENT("BDA %02x:%02x:%02x:%02x:%02x:%02x\n", bda[0], bda[1], + bda[2], bda[3], + bda[4], bda[5]); + } + + BTM_TRACE_EVENT("Inquire BDA %02x:%02x:%02x:%02x:%02x:%02x\n", p_inq->remname_bda[0], p_inq->remname_bda[1], + p_inq->remname_bda[2], p_inq->remname_bda[3], + p_inq->remname_bda[4], p_inq->remname_bda[5]); + + + + /* If the inquire BDA and remote DBA are the same, then stop the timer and set the active to false */ + if ((p_inq->remname_active == TRUE) && + (((bda != NULL) && + (memcmp(bda, p_inq->remname_bda, BD_ADDR_LEN) == 0)) || bda == NULL)) + + { +#if BLE_INCLUDED == TRUE + if (BTM_UseLeLink(p_inq->remname_bda)) { + if (hci_status == HCI_ERR_UNSPECIFIED) { + btm_ble_cancel_remote_name(p_inq->remname_bda); + } + } +#endif + btu_stop_timer (&p_inq->rmt_name_timer_ent); + p_inq->remname_active = FALSE; + /* Clean up and return the status if the command was not successful */ + /* Note: If part of the inquiry, the name is not stored, and the */ + /* inquiry complete callback is called. */ + + if (hci_status == HCI_SUCCESS) { + /* Copy the name from the data stream into the return structure */ + /* Note that even if it is not being returned, it is used as a */ + /* temporary buffer. */ + p_n1 = (UINT8 *)rem_name.remote_bd_name; + rem_name.length = (evt_len < BD_NAME_LEN) ? evt_len : BD_NAME_LEN; + rem_name.remote_bd_name[rem_name.length] = 0; + rem_name.status = BTM_SUCCESS; + temp_evt_len = rem_name.length; + + while (temp_evt_len > 0) { + *p_n1++ = *bdn++; + temp_evt_len--; + } + rem_name.remote_bd_name[rem_name.length] = 0; + } + /* If processing a stand alone remote name then report the error in the callback */ + else { + rem_name.status = BTM_BAD_VALUE_RET; + rem_name.length = 0; + rem_name.remote_bd_name[0] = '\0'; + } + memcpy(rem_name.bd_addr, p_inq->remname_bda, BD_ADDR_LEN); + /* Reset the remote BAD to zero and call callback if possible */ + memset(p_inq->remname_bda, 0, BD_ADDR_LEN); + + p_inq->p_remname_cmpl_cb = NULL; + if (p_cb) { + (p_cb)((tBTM_REMOTE_DEV_NAME *)&rem_name); + } + } +} + +/******************************************************************************* +** +** Function btm_inq_rmt_name_failed +** +** Description This function is if timeout expires while getting remote +** name. This is done for devices that incorrectly do not +** report operation failure +** +** Returns void +** +*******************************************************************************/ +void btm_inq_rmt_name_failed (void) +{ + BTM_TRACE_ERROR ("btm_inq_rmt_name_failed() remname_active=%d\n", btm_cb.btm_inq_vars.remname_active); + + if (btm_cb.btm_inq_vars.remname_active) { + btm_process_remote_name (btm_cb.btm_inq_vars.remname_bda, NULL, 0, HCI_ERR_UNSPECIFIED); + } else { + btm_process_remote_name (NULL, NULL, 0, HCI_ERR_UNSPECIFIED); + } +#if (SMP_INCLUDED == TRUE) + btm_sec_rmt_name_request_complete (NULL, NULL, HCI_ERR_UNSPECIFIED); +#endif ///SMP_INCLUDED == TRUE +} +/******************************************************************************* +** +** Function btm_read_linq_tx_power_complete +** +** Description read inquiry tx power level complete callback function. +** +** Returns void +** +*******************************************************************************/ +void btm_read_linq_tx_power_complete(UINT8 *p) +{ + tBTM_CMPL_CB *p_cb = btm_cb.devcb.p_txpwer_cmpl_cb; + tBTM_INQ_TXPWR_RESULTS results; + + btu_stop_timer (&btm_cb.devcb.txpwer_timer); + /* If there was a callback registered for read inq tx power, call it */ + btm_cb.devcb.p_txpwer_cmpl_cb = NULL; + + if (p_cb) { + STREAM_TO_UINT8 (results.hci_status, p); + + if (results.hci_status == HCI_SUCCESS) { + results.status = BTM_SUCCESS; + + STREAM_TO_UINT8 (results.tx_power, p); + BTM_TRACE_EVENT ("BTM INQ TX POWER Complete: tx_power %d, hci status 0x%02x\n", + results.tx_power, results.hci_status); + } else { + results.status = BTM_ERR_PROCESSING; + } + + (*p_cb)(&results); + } + +} +/******************************************************************************* +** +** Function BTM_WriteEIR +** +** Description This function is called to write EIR data to controller. +** +** Parameters p_buff - allocated HCI command buffer including extended +** inquriry response +** fec_required - FEC is required or not +** +** Returns BTM_SUCCESS - if successful +** BTM_MODE_UNSUPPORTED - if local device cannot support it +** +*******************************************************************************/ +tBTM_STATUS BTM_WriteEIR( BT_HDR *p_buff, BOOLEAN fec_required) +{ + if (controller_get_interface()->supports_extended_inquiry_response()) { + BTM_TRACE_API("Write Extended Inquiry Response to controller\n"); + btsnd_hcic_write_ext_inquiry_response (p_buff, fec_required); + osi_free(p_buff); + return BTM_SUCCESS; + } else { + osi_free(p_buff); + return BTM_MODE_UNSUPPORTED; + } +} + +/******************************************************************************* +** +** Function BTM_CheckEirData +** +** Description This function is called to get EIR data from significant part. +** +** Parameters p_eir - pointer of EIR significant part +** type - finding EIR data type +** p_length - return the length of EIR data not including type +** +** Returns pointer of EIR data +** +*******************************************************************************/ +UINT8 *BTM_CheckEirData( UINT8 *p_eir, UINT8 type, UINT8 *p_length ) +{ + UINT8 *p = p_eir; + UINT8 length; + UINT8 eir_type; + BTM_TRACE_API("BTM_CheckEirData type=0x%02X\n", type); + + STREAM_TO_UINT8(length, p); + while ( length && (p - p_eir <= HCI_EXT_INQ_RESPONSE_LEN)) { + STREAM_TO_UINT8(eir_type, p); + if ( eir_type == type ) { + /* length doesn't include itself */ + *p_length = length - 1; /* minus the length of type */ + return p; + } + p += length - 1; /* skip the length of data */ + STREAM_TO_UINT8(length, p); + } + + *p_length = 0; + return NULL; +} + +/******************************************************************************* +** +** Function btm_convert_uuid_to_eir_service +** +** Description This function is called to get the bit position of UUID. +** +** Parameters uuid16 - UUID 16-bit +** +** Returns BTM EIR service ID if found +** BTM_EIR_MAX_SERVICES - if not found +** +*******************************************************************************/ +static UINT8 btm_convert_uuid_to_eir_service( UINT16 uuid16 ) +{ + UINT8 xx; + + for ( xx = 0; xx < BTM_EIR_MAX_SERVICES; xx++ ) { + if ( uuid16 == BTM_EIR_UUID_LKUP_TBL[xx]) { + return xx; + } + } + return BTM_EIR_MAX_SERVICES; +} + +/******************************************************************************* +** +** Function BTM_HasEirService +** +** Description This function is called to know if UUID in bit map of UUID. +** +** Parameters p_eir_uuid - bit map of UUID list +** uuid16 - UUID 16-bit +** +** Returns TRUE - if found +** FALSE - if not found +** +*******************************************************************************/ +BOOLEAN BTM_HasEirService( UINT32 *p_eir_uuid, UINT16 uuid16 ) +{ + UINT8 service_id; + + service_id = btm_convert_uuid_to_eir_service(uuid16); + if ( service_id < BTM_EIR_MAX_SERVICES ) { + return ( BTM_EIR_HAS_SERVICE( p_eir_uuid, service_id )); + } else { + return ( FALSE ); + } +} + +/******************************************************************************* +** +** Function BTM_HasInquiryEirService +** +** Description This function is called to know if UUID in bit map of UUID list. +** +** Parameters p_results - inquiry results +** uuid16 - UUID 16-bit +** +** Returns BTM_EIR_FOUND - if found +** BTM_EIR_NOT_FOUND - if not found and it is complete list +** BTM_EIR_UNKNOWN - if not found and it is not complete list +** +*******************************************************************************/ +tBTM_EIR_SEARCH_RESULT BTM_HasInquiryEirService( tBTM_INQ_RESULTS *p_results, UINT16 uuid16 ) +{ + if ( BTM_HasEirService( p_results->eir_uuid, uuid16 )) { + return BTM_EIR_FOUND; + } else if ( p_results->eir_complete_list ) { + return BTM_EIR_NOT_FOUND; + } else { + return BTM_EIR_UNKNOWN; + } +} + +/******************************************************************************* +** +** Function BTM_AddEirService +** +** Description This function is called to add a service in bit map of UUID list. +** +** Parameters p_eir_uuid - bit mask of UUID list for EIR +** uuid16 - UUID 16-bit +** +** Returns None +** +*******************************************************************************/ +void BTM_AddEirService( UINT32 *p_eir_uuid, UINT16 uuid16 ) +{ + UINT8 service_id; + + service_id = btm_convert_uuid_to_eir_service(uuid16); + if ( service_id < BTM_EIR_MAX_SERVICES ) { + BTM_EIR_SET_SERVICE( p_eir_uuid, service_id ); + } +} + + +/******************************************************************************* +** +** Function btm_compare_uuid +** +** Description Helper function for custom service managing routines. +** +** Parameters uuid1 - pointer to the first tBT_UUID struct +** uuid2 - pointer to the second tBT_UUID struct +** +** Returns true if UUID structs are identical +** +*******************************************************************************/ +static bool btm_compare_uuid(tBT_UUID *uuid1, tBT_UUID *uuid2) +{ + if (uuid1->len != uuid2->len) { + return FALSE; + } + + return (memcmp(&uuid1->uu, &uuid2->uu, uuid1->len) == 0); +} + +/******************************************************************************* +** +** Function btm_find_empty_custom_uuid_slot +** +** Description Helper function for custom service managing routines. +** +** Parameters custom_uuid - pointer to custom_uuid array in tBTA_DM_CB +** uuid - UUID struct +** +** Returns Slot number if there is empty slot, +** otherwise - BTA_EIR_SERVER_NUM_CUSTOM_UUID +** +*******************************************************************************/ +static UINT8 btm_find_empty_custom_uuid_slot(tBT_UUID *custom_uuid, tBT_UUID uuid) +{ + for (UINT8 xx = 0; xx < BTA_EIR_SERVER_NUM_CUSTOM_UUID; xx++) { + if (custom_uuid[xx].len == 0) { + return xx; + } + } + return BTA_EIR_SERVER_NUM_CUSTOM_UUID; +} + +/******************************************************************************* +** +** Function btm_find_match_custom_uuid_slot +** +** Description Helper function for custom service managing routines. +** +** Parameters custom_uuid - pointer to custom_uuid array in tBTA_DM_CB +** uuid - UUID struct +** +** Returns Slot number if given UUID is already in slots array, +** otherwise - BTA_EIR_SERVER_NUM_CUSTOM_UUID +** +*******************************************************************************/ +static UINT8 btm_find_match_custom_uuid_slot(tBT_UUID *custom_uuid, tBT_UUID uuid) +{ + for (UINT8 xx = 0; xx < BTA_EIR_SERVER_NUM_CUSTOM_UUID; xx++) { + if (btm_compare_uuid(&custom_uuid[xx], &uuid)) { + return xx; + } + } + return BTA_EIR_SERVER_NUM_CUSTOM_UUID; +} + +/******************************************************************************* +** +** Function BTM_HasCustomEirService +** +** Description This function is called to know if UUID is already in custom +** UUID list. +** +** Parameters custom_uuid - pointer to custom_uuid array in tBTA_DM_CB +** uuid - UUID struct +** +** Returns TRUE - if found +** FALSE - if not found +** +*******************************************************************************/ +BOOLEAN BTM_HasCustomEirService(tBT_UUID *custom_uuid, tBT_UUID uuid) +{ + UINT8 match_slot = btm_find_match_custom_uuid_slot(custom_uuid, uuid); + + if (match_slot == BTA_EIR_SERVER_NUM_CUSTOM_UUID) { + return FALSE; + } + return TRUE; +} + +/******************************************************************************* +** +** Function BTM_AddCustomEirService +** +** Description This function is called to add a custom UUID. +** +** Parameters custom_uuid - pointer to custom_uuid array in tBTA_DM_CB +** uuid - UUID struct +** +** Returns None +** +*******************************************************************************/ +void BTM_AddCustomEirService(tBT_UUID *custom_uuid, tBT_UUID uuid) +{ + UINT8 empty_slot = btm_find_empty_custom_uuid_slot(custom_uuid, uuid); + + if (empty_slot == BTA_EIR_SERVER_NUM_CUSTOM_UUID) { + BTM_TRACE_WARNING("No space to add UUID for EIR"); + } else { + memcpy(&(custom_uuid[empty_slot]), &(uuid), sizeof(tBT_UUID)); + BTM_TRACE_EVENT("UUID saved in %d slot", empty_slot); + } +} + +/******************************************************************************* +** +** Function BTM_RemoveCustomEirService +** +** Description This function is called to remove a service in bit map of UUID list. +** +** Parameters p_eir_uuid - bit mask of UUID list for EIR +** uuid16 - UUID 16-bit +** +** Returns None +** +*******************************************************************************/ +void BTM_RemoveEirService( UINT32 *p_eir_uuid, UINT16 uuid16 ) +{ + UINT8 service_id; + + service_id = btm_convert_uuid_to_eir_service(uuid16); + if ( service_id < BTM_EIR_MAX_SERVICES ) { + BTM_EIR_CLR_SERVICE( p_eir_uuid, service_id ); + } +} + +/******************************************************************************* +** +** Function BTM_RemoveCustomEirService +** +** Description This function is called to remove a a custom UUID. +** +** Parameters custom_uuid - pointer to custom_uuid array in tBTA_DM_CB +** uuid - UUID struct +** +** Returns None +** +*******************************************************************************/ +void BTM_RemoveCustomEirService(tBT_UUID *custom_uuid, tBT_UUID uuid) +{ + UINT8 match_slot = btm_find_match_custom_uuid_slot(custom_uuid, uuid); + + if (match_slot == BTA_EIR_SERVER_NUM_CUSTOM_UUID) { + BTM_TRACE_WARNING("UUID is not found for EIR"); + return; + } else { + memset(&(custom_uuid[match_slot]), 0, sizeof(tBT_UUID)); + } +} + +/******************************************************************************* +** +** Function BTM_GetEirSupportedServices +** +** Description This function is called to get UUID list from bit map of UUID list. +** +** Parameters p_eir_uuid - bit mask of UUID list for EIR +** p - reference of current pointer of EIR +** max_num_uuid16 - max number of UUID can be written in EIR +** num_uuid16 - number of UUID have been written in EIR +** +** Returns BTM_EIR_MORE_16BITS_UUID_TYPE, if it has more than max +** BTM_EIR_COMPLETE_16BITS_UUID_TYPE, otherwise +** +*******************************************************************************/ +UINT8 BTM_GetEirSupportedServices( UINT32 *p_eir_uuid, UINT8 **p, + UINT8 max_num_uuid16, UINT8 *p_num_uuid16) +{ + UINT8 service_index; + + *p_num_uuid16 = 0; + + for (service_index = 0; service_index < BTM_EIR_MAX_SERVICES; service_index++) { + if ( BTM_EIR_HAS_SERVICE( p_eir_uuid, service_index )) { + if ( *p_num_uuid16 < max_num_uuid16 ) { + UINT16_TO_STREAM(*p, BTM_EIR_UUID_LKUP_TBL[service_index]); + (*p_num_uuid16)++; + } + /* if max number of UUIDs are stored and found one more */ + else { + return BTM_EIR_MORE_16BITS_UUID_TYPE; + } + } + } + return BTM_EIR_COMPLETE_16BITS_UUID_TYPE; +} + +/******************************************************************************* +** +** Function BTM_GetEirUuidList +** +** Description This function parses EIR and returns UUID list. +** +** Parameters p_eir - EIR +** uuid_size - LEN_UUID_16, LEN_UUID_32, LEN_UUID_128 +** p_num_uuid - return number of UUID in found list +** p_uuid_list - return UUID list +** max_num_uuid - maximum number of UUID to be returned +** +** Returns 0 - if not found +** BTM_EIR_COMPLETE_16BITS_UUID_TYPE +** BTM_EIR_MORE_16BITS_UUID_TYPE +** BTM_EIR_COMPLETE_32BITS_UUID_TYPE +** BTM_EIR_MORE_32BITS_UUID_TYPE +** BTM_EIR_COMPLETE_128BITS_UUID_TYPE +** BTM_EIR_MORE_128BITS_UUID_TYPE +** +*******************************************************************************/ +UINT8 BTM_GetEirUuidList( UINT8 *p_eir, UINT8 uuid_size, UINT8 *p_num_uuid, + UINT8 *p_uuid_list, UINT8 max_num_uuid) +{ + UINT8 *p_uuid_data; + UINT8 type; + UINT8 yy, xx; + UINT16 *p_uuid16 = (UINT16 *)p_uuid_list; + UINT32 *p_uuid32 = (UINT32 *)p_uuid_list; + char buff[LEN_UUID_128 * 2 + 1]; + + p_uuid_data = btm_eir_get_uuid_list( p_eir, uuid_size, p_num_uuid, &type ); + if ( p_uuid_data == NULL ) { + return 0x00; + } + + if ( *p_num_uuid > max_num_uuid ) { + BTM_TRACE_WARNING("BTM_GetEirUuidList number of uuid in EIR = %d, size of uuid list = %d\n", + *p_num_uuid, max_num_uuid ); + *p_num_uuid = max_num_uuid; + } + + BTM_TRACE_DEBUG("BTM_GetEirUuidList type = %02X, number of uuid = %d\n", type, *p_num_uuid ); + + if ( uuid_size == LEN_UUID_16 ) { + for ( yy = 0; yy < *p_num_uuid; yy++ ) { + STREAM_TO_UINT16(*(p_uuid16 + yy), p_uuid_data); + BTM_TRACE_DEBUG(" 0x%04X\n", *(p_uuid16 + yy)); + } + } else if ( uuid_size == LEN_UUID_32 ) { + for ( yy = 0; yy < *p_num_uuid; yy++ ) { + STREAM_TO_UINT32(*(p_uuid32 + yy), p_uuid_data); + BTM_TRACE_DEBUG(" 0x%08X\n", *(p_uuid32 + yy)); + } + } else if ( uuid_size == LEN_UUID_128 ) { + for ( yy = 0; yy < *p_num_uuid; yy++ ) { + STREAM_TO_ARRAY16(p_uuid_list + yy * LEN_UUID_128, p_uuid_data); + for ( xx = 0; xx < LEN_UUID_128; xx++ ) { + sprintf(buff + xx * 2, "%02X", *(p_uuid_list + yy * LEN_UUID_128 + xx)); + } + BTM_TRACE_DEBUG(" 0x%s\n", buff); + } + } + + return type; +} + + +/******************************************************************************* +** +** Function btm_eir_get_uuid_list +** +** Description This function searches UUID list in EIR. +** +** Parameters p_eir - address of EIR +** uuid_size - size of UUID to find +** p_num_uuid - number of UUIDs found +** p_uuid_list_type - EIR data type +** +** Returns NULL - if UUID list with uuid_size is not found +** beginning of UUID list in EIR - otherwise +** +*******************************************************************************/ +static UINT8 *btm_eir_get_uuid_list( UINT8 *p_eir, UINT8 uuid_size, + UINT8 *p_num_uuid, UINT8 *p_uuid_list_type ) +{ + UINT8 *p_uuid_data; + UINT8 complete_type, more_type; + UINT8 uuid_len; + + switch ( uuid_size ) { + case LEN_UUID_16: + complete_type = BTM_EIR_COMPLETE_16BITS_UUID_TYPE; + more_type = BTM_EIR_MORE_16BITS_UUID_TYPE; + break; + case LEN_UUID_32: + complete_type = BTM_EIR_COMPLETE_32BITS_UUID_TYPE; + more_type = BTM_EIR_MORE_32BITS_UUID_TYPE; + break; + case LEN_UUID_128: + complete_type = BTM_EIR_COMPLETE_128BITS_UUID_TYPE; + more_type = BTM_EIR_MORE_128BITS_UUID_TYPE; + break; + default: + *p_num_uuid = 0; + return NULL; + break; + } + + p_uuid_data = BTM_CheckEirData( p_eir, complete_type, &uuid_len ); + if (p_uuid_data == NULL) { + p_uuid_data = BTM_CheckEirData( p_eir, more_type, &uuid_len ); + *p_uuid_list_type = more_type; + } else { + *p_uuid_list_type = complete_type; + } + + *p_num_uuid = uuid_len / uuid_size; + return p_uuid_data; +} + +/******************************************************************************* +** +** Function btm_convert_uuid_to_uuid16 +** +** Description This function converts UUID to UUID 16-bit. +** +** Parameters p_uuid - address of UUID +** uuid_size - size of UUID +** +** Returns 0 - if UUID cannot be converted to UUID 16-bit +** UUID 16-bit - otherwise +** +*******************************************************************************/ +static UINT16 btm_convert_uuid_to_uuid16( UINT8 *p_uuid, UINT8 uuid_size ) +{ + static const UINT8 base_uuid[LEN_UUID_128] = {0xFB, 0x34, 0x9B, 0x5F, 0x80, 0x00, 0x00, 0x80, + 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + UINT16 uuid16 = 0; + UINT32 uuid32; + BOOLEAN is_base_uuid; + UINT8 xx; + + switch (uuid_size) { + case LEN_UUID_16: + STREAM_TO_UINT16 (uuid16, p_uuid); + break; + case LEN_UUID_32: + STREAM_TO_UINT32 (uuid32, p_uuid); + if (uuid32 < 0x10000) { + uuid16 = (UINT16) uuid32; + } + break; + case LEN_UUID_128: + /* See if we can compress his UUID down to 16 or 32bit UUIDs */ + is_base_uuid = TRUE; + for (xx = 0; xx < LEN_UUID_128 - 4; xx++) { + if (p_uuid[xx] != base_uuid[xx]) { + is_base_uuid = FALSE; + break; + } + } + if (is_base_uuid) { + if ((p_uuid[LEN_UUID_128 - 1] == 0) && (p_uuid[LEN_UUID_128 - 2] == 0)) { + p_uuid += (LEN_UUID_128 - 4); + STREAM_TO_UINT16(uuid16, p_uuid); + } + } + break; + default: + BTM_TRACE_WARNING("btm_convert_uuid_to_uuid16 invalid uuid size\n"); + break; + } + + return ( uuid16); +} + +/******************************************************************************* +** +** Function btm_set_eir_uuid +** +** Description This function is called to store received UUID into inquiry result. +** +** Parameters p_eir - pointer of EIR significant part +** p_results - pointer of inquiry result +** +** Returns None +** +*******************************************************************************/ +void btm_set_eir_uuid( UINT8 *p_eir, tBTM_INQ_RESULTS *p_results ) +{ + UINT8 *p_uuid_data; + UINT8 num_uuid; + UINT16 uuid16; + UINT8 yy; + UINT8 type = BTM_EIR_MORE_16BITS_UUID_TYPE; + + p_uuid_data = btm_eir_get_uuid_list( p_eir, LEN_UUID_16, &num_uuid, &type ); + + if (type == BTM_EIR_COMPLETE_16BITS_UUID_TYPE) { + p_results->eir_complete_list = TRUE; + } else { + p_results->eir_complete_list = FALSE; + } + + BTM_TRACE_API("btm_set_eir_uuid eir_complete_list=0x%02X\n", p_results->eir_complete_list); + + if ( p_uuid_data ) { + for ( yy = 0; yy < num_uuid; yy++ ) { + STREAM_TO_UINT16(uuid16, p_uuid_data); + BTM_AddEirService( p_results->eir_uuid, uuid16 ); + } + } + + p_uuid_data = btm_eir_get_uuid_list( p_eir, LEN_UUID_32, &num_uuid, &type ); + if ( p_uuid_data ) { + for ( yy = 0; yy < num_uuid; yy++ ) { + uuid16 = btm_convert_uuid_to_uuid16( p_uuid_data, LEN_UUID_32 ); + p_uuid_data += LEN_UUID_32; + if ( uuid16 ) { + BTM_AddEirService( p_results->eir_uuid, uuid16 ); + } + } + } + + p_uuid_data = btm_eir_get_uuid_list( p_eir, LEN_UUID_128, &num_uuid, &type ); + if ( p_uuid_data ) { + for ( yy = 0; yy < num_uuid; yy++ ) { + uuid16 = btm_convert_uuid_to_uuid16( p_uuid_data, LEN_UUID_128 ); + p_uuid_data += LEN_UUID_128; + if ( uuid16 ) { + BTM_AddEirService( p_results->eir_uuid, uuid16 ); + } + } + } +} diff --git a/lib/bt/host/bluedroid/stack/btm/btm_main.c b/lib/bt/host/bluedroid/stack/btm/btm_main.c new file mode 100644 index 00000000..05ef1837 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/btm/btm_main.c @@ -0,0 +1,152 @@ +/****************************************************************************** + * + * Copyright (C) 2002-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains the definition of the btm control block when + * BTM_DYNAMIC_MEMORY is used. + * + ******************************************************************************/ + +#include "stack/bt_types.h" +#include "common/bt_target.h" +#include +#include "btm_int.h" +#include "osi/allocator.h" + +/* Global BTM control block structure +*/ +#if BTM_DYNAMIC_MEMORY == FALSE +tBTM_CB btm_cb; +#else +tBTM_CB *btm_cb_ptr; +#endif + +#if (BLE_50_FEATURE_SUPPORT == TRUE) +extern void btm_ble_extendadvcb_init(void); +extern void btm_ble_advrecod_init(void); +#endif + + +/******************************************************************************* +** +** Function btm_init +** +** Description This function is called at BTM startup to allocate the +** control block (if using dynamic memory), and initializes the +** tracing level. It then initializes the various components of +** btm. +** +** Returns void +** +*******************************************************************************/ +void btm_init (void) +{ +#if BTM_DYNAMIC_MEMORY + btm_cb_ptr = (tBTM_CB *)osi_malloc(sizeof(tBTM_CB)); +#endif /* #if BTM_DYNAMIC_MEMORY */ + /* All fields are cleared; nonzero fields are reinitialized in appropriate function */ + memset(&btm_cb, 0, sizeof(tBTM_CB)); + btm_cb.page_queue = fixed_queue_new(QUEUE_SIZE_MAX); + btm_cb.sec_pending_q = fixed_queue_new(QUEUE_SIZE_MAX); + +#if defined(BTM_INITIAL_TRACE_LEVEL) + btm_cb.trace_level = BTM_INITIAL_TRACE_LEVEL; +#else + btm_cb.trace_level = BT_TRACE_LEVEL_NONE; +#endif + /* Initialize BTM component structures */ + btm_inq_db_init(); /* Inquiry Database and Structures */ + btm_acl_init(); /* ACL Database and Structures */ +#if (SMP_INCLUDED == TRUE) + btm_sec_init(BTM_SEC_MODE_SP); /* Security Manager Database and Structures */ +#endif ///SMP_INCLUDED == TRUE +#if BTM_SCO_INCLUDED == TRUE + btm_sco_init(); /* SCO Database and Structures (If included) */ +#endif + + btm_dev_init(); /* Device Manager Structures & HCI_Reset */ +#if BLE_INCLUDED == TRUE + btm_ble_lock_init(); + btm_ble_sem_init(); +#endif + btm_sec_dev_init(); +#if (BLE_50_FEATURE_SUPPORT == TRUE) + btm_ble_extendadvcb_init(); + btm_ble_advrecod_init(); +#endif + +} + + +/******************************************************************************* +** +** Function btm_free +** +** Description This function is called at btu core free the fixed queue +** +** Returns void +** +*******************************************************************************/ +void btm_free(void) +{ + fixed_queue_free(btm_cb.page_queue, osi_free_func); + fixed_queue_free(btm_cb.sec_pending_q, osi_free_func); + btm_acl_free(); + btm_sec_dev_free(); +#if BTM_DYNAMIC_MEMORY + FREE_AND_RESET(btm_cb_ptr); +#endif +#if BLE_INCLUDED == TRUE + btm_ble_lock_free(); + btm_ble_sem_free(); +#endif +} + +uint8_t btm_acl_active_count(void) +{ + list_node_t *p_node = NULL; + tACL_CONN *p_acl_conn = NULL; + uint8_t count = 0; + + for (p_node = list_begin(btm_cb.p_acl_db_list); p_node; p_node = list_next(p_node)) { + p_acl_conn = list_node(p_node); + if (p_acl_conn && p_acl_conn->in_use) { + count++; + } + } + + return count; +} + +uint8_t btdm_sec_dev_active_count(void) +{ + tBTM_SEC_DEV_REC *p_dev_rec = NULL; + list_node_t *p_node = NULL; + uint8_t count = 0; + + /* First look for the non-paired devices for the oldest entry */ + for (p_node = list_begin(btm_cb.p_sec_dev_rec_list); p_node; p_node = list_next(p_node)) { + p_dev_rec = list_node(p_node); + if (p_dev_rec && (p_dev_rec->sec_flags & BTM_SEC_IN_USE)) { + count++; + } + } + + return count; +} diff --git a/lib/bt/host/bluedroid/stack/btm/btm_pm.c b/lib/bt/host/bluedroid/stack/btm/btm_pm.c new file mode 100644 index 00000000..4c52348e --- /dev/null +++ b/lib/bt/host/bluedroid/stack/btm/btm_pm.c @@ -0,0 +1,965 @@ +/****************************************************************************** + * + * Copyright (C) 2000-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/***************************************************************************** + * + * This file contains functions that manages ACL link modes. + * This includes operations such as active, hold, + * park and sniff modes. + * + * This module contains both internal and external (API) + * functions. External (API) functions are distinguishable + * by their names beginning with uppercase BTM. + * + *****************************************************************************/ + +//#define LOG_TAG "bt_btm_pm" + +#include +#include +//#include +#include + +#include "stack/bt_types.h" +#include "stack/hcimsgs.h" +#include "stack/btu.h" +#include "stack/btm_api.h" +#include "btm_int.h" +#include "l2c_int.h" +#include "stack/hcidefs.h" +//#include "bt_utils.h" +//#include "osi/include/log.h" +#include "osi/allocator.h" +/*****************************************************************************/ +/* to handle different modes */ +/*****************************************************************************/ +#define BTM_PM_STORED_MASK 0x80 /* set this mask if the command is stored */ +#define BTM_PM_NUM_SET_MODES 3 /* only hold, sniff & park */ + +/* Usage: (ptr_features[ offset ] & mask )?TRUE:FALSE */ +/* offset to supported feature */ +const UINT8 btm_pm_mode_off[BTM_PM_NUM_SET_MODES] = {0, 0, 1}; +/* mask to supported feature */ +const UINT8 btm_pm_mode_msk[BTM_PM_NUM_SET_MODES] = {0x40, 0x80, 0x01}; + +#define BTM_PM_GET_MD1 1 +#define BTM_PM_GET_MD2 2 +#define BTM_PM_GET_COMP 3 + +const UINT8 btm_pm_md_comp_matrix[BTM_PM_NUM_SET_MODES * BTM_PM_NUM_SET_MODES] = { + BTM_PM_GET_COMP, + BTM_PM_GET_MD2, + BTM_PM_GET_MD2, + + BTM_PM_GET_MD1, + BTM_PM_GET_COMP, + BTM_PM_GET_MD1, + + BTM_PM_GET_MD1, + BTM_PM_GET_MD2, + BTM_PM_GET_COMP +}; + +/* function prototype */ +static tBTM_STATUS btm_pm_snd_md_req( UINT8 pm_id, UINT16 link_hdl, tBTM_PM_PWR_MD *p_mode ); +#if (!CONFIG_BT_STACK_NO_LOG) +static const char *mode_to_string(tBTM_PM_MODE mode); +#endif + +/* +#ifdef BTM_PM_DEBUG +#undef BTM_PM_DEBUG +#define BTM_PM_DEBUG TRUE +#endif +*/ + +#if BTM_PM_DEBUG == TRUE +const char *btm_pm_state_str[] = { + "pm_active_state", + "pm_hold_state", + "pm_sniff_state", + "pm_park_state", + "pm_pend_state" +}; + +const char *btm_pm_event_str[] = { + "pm_set_mode_event", + "pm_hci_sts_event", + "pm_mod_chg_event", + "pm_update_event" +}; + +const char *btm_pm_action_str[] = { + "pm_set_mode_action", + "pm_update_db_action", + "pm_mod_chg_action", + "pm_hci_sts_action", + "pm_update_action" +}; +#endif // BTM_PM_DEBUG + +/*****************************************************************************/ +/* P U B L I C F U N C T I O N S */ +/*****************************************************************************/ +/******************************************************************************* +** +** Function BTM_PmRegister +** +** Description register or deregister with power manager +** +** Returns BTM_SUCCESS if successful, +** BTM_NO_RESOURCES if no room to hold registration +** BTM_ILLEGAL_VALUE +** +*******************************************************************************/ +tBTM_STATUS BTM_PmRegister (UINT8 mask, UINT8 *p_pm_id, tBTM_PM_STATUS_CBACK *p_cb) +{ + int xx; + + /* de-register */ + if (mask & BTM_PM_DEREG) { + if (*p_pm_id >= BTM_MAX_PM_RECORDS) { + return BTM_ILLEGAL_VALUE; + } + btm_cb.pm_reg_db[*p_pm_id].mask = BTM_PM_REC_NOT_USED; + return BTM_SUCCESS; + } + + for (xx = 0; xx < BTM_MAX_PM_RECORDS; xx++) { + /* find an unused entry */ + if (btm_cb.pm_reg_db[xx].mask == BTM_PM_REC_NOT_USED) { + /* if register for notification, should provide callback routine */ + if (mask & BTM_PM_REG_NOTIF) { + if (p_cb == NULL) { + return BTM_ILLEGAL_VALUE; + } + btm_cb.pm_reg_db[xx].cback = p_cb; + } + btm_cb.pm_reg_db[xx].mask = mask; + *p_pm_id = xx; + return BTM_SUCCESS; + } + } + + return BTM_NO_RESOURCES; +} + +/******************************************************************************* +** +** Function BTM_SetPowerMode +** +** Description store the mode in control block or +** alter ACL connection behavior. +** +** Returns BTM_SUCCESS if successful, +** BTM_UNKNOWN_ADDR if bd addr is not active or bad +** +*******************************************************************************/ +tBTM_STATUS BTM_SetPowerMode (UINT8 pm_id, BD_ADDR remote_bda, tBTM_PM_PWR_MD *p_mode) +{ + UINT8 *p_features; + int ind; + tBTM_PM_MCB *p_cb = NULL; /* per ACL link */ + tBTM_PM_MODE mode; + int temp_pm_id; + tACL_CONN *p_acl_cb; + + if (pm_id >= BTM_MAX_PM_RECORDS) { + pm_id = BTM_PM_SET_ONLY_ID; + } + + if (p_mode == NULL) { + return BTM_ILLEGAL_VALUE; + } + + BTM_TRACE_API( "BTM_SetPowerMode: pm_id %d BDA: %08x mode:0x%x", pm_id, + (remote_bda[2] << 24) + (remote_bda[3] << 16) + (remote_bda[4] << 8) + remote_bda[5], p_mode->mode); + + /* take out the force bit */ + mode = p_mode->mode & ~BTM_PM_MD_FORCE; + + p_acl_cb = btm_bda_to_acl(remote_bda, BT_TRANSPORT_BR_EDR); + if (p_acl_cb == NULL){ + return BTM_UNKNOWN_ADDR; + } + + p_cb = p_acl_cb->p_pm_mode_db; + if (mode != BTM_PM_MD_ACTIVE) { + /* check if the requested mode is supported */ + ind = mode - BTM_PM_MD_HOLD; /* make it base 0 */ + p_features = BTM_ReadLocalFeatures(); + if ( !(p_features[ btm_pm_mode_off[ind] ] & btm_pm_mode_msk[ind] ) ) { + return BTM_MODE_UNSUPPORTED; + } + } + + if (mode == p_cb->state) { /* the requested mode is current mode */ + /* already in the requested mode and the current interval has less latency than the max */ + if ( (mode == BTM_PM_MD_ACTIVE) || + ((p_mode->mode & BTM_PM_MD_FORCE) && (p_mode->max >= p_cb->interval) && (p_mode->min <= p_cb->interval)) || + ((p_mode->mode & BTM_PM_MD_FORCE) == 0 && (p_mode->max >= p_cb->interval)) ) { + BTM_TRACE_DEBUG( "BTM_SetPowerMode: mode:0x%x interval %d max:%d, min:%d", p_mode->mode, p_cb->interval, p_mode->max, p_mode->min); + return BTM_SUCCESS; + } + } + + temp_pm_id = pm_id; + if (pm_id == BTM_PM_SET_ONLY_ID) { + temp_pm_id = BTM_MAX_PM_RECORDS; + } + + /* update mode database */ + if ( ((pm_id != BTM_PM_SET_ONLY_ID) && + (btm_cb.pm_reg_db[pm_id].mask & BTM_PM_REG_SET)) + || ((pm_id == BTM_PM_SET_ONLY_ID) + && (btm_cb.pm_pend_link_hdl != BTM_INVALID_HANDLE)) ) { +#if BTM_PM_DEBUG == TRUE + BTM_TRACE_DEBUG( "BTM_SetPowerMode: Saving cmd acl handle %d temp_pm_id %d", p_acl_cb->hci_handle, temp_pm_id); +#endif // BTM_PM_DEBUG + /* Make sure mask is set to BTM_PM_REG_SET */ + btm_cb.pm_reg_db[temp_pm_id].mask |= BTM_PM_REG_SET; + *(&p_cb->req_mode[temp_pm_id]) = *((tBTM_PM_PWR_MD *)p_mode); + p_cb->chg_ind = TRUE; + } + +#if BTM_PM_DEBUG == TRUE + BTM_TRACE_DEBUG( "btm_pm state:0x%x, pm_pend_link_hdl: %d", p_cb->state, btm_cb.pm_pend_link_hdl); +#endif // BTM_PM_DEBUG + /* if mode == hold or pending, return */ + if ( (p_cb->state == BTM_PM_STS_HOLD) || + (p_cb->state == BTM_PM_STS_PENDING) || + (btm_cb.pm_pend_link_hdl != BTM_INVALID_HANDLE) || + (p_cb->state & BTM_PM_STORED_MASK) ) { /* command pending */ + if (p_acl_cb->hci_handle != btm_cb.pm_pend_link_hdl) { + /* set the stored mask */ + p_cb->state |= BTM_PM_STORED_MASK; + BTM_TRACE_DEBUG( "btm_pm state stored:%d", p_acl_cb->hci_handle); + } + return BTM_CMD_STORED; + } + + + + return btm_pm_snd_md_req(pm_id, p_acl_cb->hci_handle, p_mode); +} + +/******************************************************************************* +** +** Function BTM_ReadPowerMode +** +** Description This returns the current mode for a specific +** ACL connection. +** +** Input Param remote_bda - device address of desired ACL connection +** +** Output Param p_mode - address where the current mode is copied into. +** BTM_ACL_MODE_NORMAL +** BTM_ACL_MODE_HOLD +** BTM_ACL_MODE_SNIFF +** BTM_ACL_MODE_PARK +** (valid only if return code is BTM_SUCCESS) +** +** Returns BTM_SUCCESS if successful, +** BTM_UNKNOWN_ADDR if bd addr is not active or bad +** +*******************************************************************************/ +tBTM_STATUS BTM_ReadPowerMode (BD_ADDR remote_bda, tBTM_PM_MODE *p_mode) +{ + tACL_CONN *p_acl_cb = btm_bda_to_acl(remote_bda, BT_TRANSPORT_BR_EDR); + if (!p_acl_cb) { + return (BTM_UNKNOWN_ADDR); + } + + *p_mode = p_acl_cb->p_pm_mode_db->state; + return BTM_SUCCESS; +} + +/******************************************************************************* +** +** Function BTM_SetSsrParams +** +** Description This sends the given SSR parameters for the given ACL +** connection if it is in ACTIVE mode. +** +** Input Param remote_bda - device address of desired ACL connection +** max_lat - maximum latency (in 0.625ms)(0-0xFFFE) +** min_rmt_to - minimum remote timeout +** min_loc_to - minimum local timeout +** +** +** Returns BTM_SUCCESS if the HCI command is issued successful, +** BTM_UNKNOWN_ADDR if bd addr is not active or bad +** BTM_CMD_STORED if the command is stored +** +*******************************************************************************/ +tBTM_STATUS BTM_SetSsrParams (BD_ADDR remote_bda, UINT16 max_lat, + UINT16 min_rmt_to, UINT16 min_loc_to) +{ +#if (BTM_SSR_INCLUDED == TRUE) + tBTM_PM_MCB *p_cb; + tACL_CONN *p_acl_cb = NULL; + + p_acl_cb = btm_bda_to_acl(remote_bda, BT_TRANSPORT_BR_EDR); + if (!p_acl_cb) { + return (BTM_UNKNOWN_ADDR); + } + p_cb = p_acl_cb->p_pm_mode_db; + + if (BTM_PM_STS_ACTIVE == p_cb->state || + BTM_PM_STS_SNIFF == p_cb->state) { + if (btsnd_hcic_sniff_sub_rate(p_acl_cb->hci_handle, max_lat, + min_rmt_to, min_loc_to)) { + return BTM_SUCCESS; + } else { + return BTM_NO_RESOURCES; + } + } + p_cb->max_lat = max_lat; + p_cb->min_rmt_to = min_rmt_to; + p_cb->min_loc_to = min_loc_to; + return BTM_CMD_STORED; +#else + return BTM_ILLEGAL_ACTION; +#endif // BTM_SSR_INCLUDED +} + +/******************************************************************************* +** +** Function btm_pm_reset +** +** Description as a part of the BTM reset process. +** +** Returns void +** +*******************************************************************************/ +void btm_pm_reset(void) +{ + int xx; + tBTM_PM_STATUS_CBACK *cb = NULL; + + /* clear the pending request for application */ + if ( (btm_cb.pm_pend_id != BTM_PM_SET_ONLY_ID) && + (btm_cb.pm_reg_db[btm_cb.pm_pend_id].mask & BTM_PM_REG_NOTIF) ) { + cb = btm_cb.pm_reg_db[btm_cb.pm_pend_id].cback; + } + + + /* clear the register record */ + for (xx = 0; xx < BTM_MAX_PM_RECORDS; xx++) { + btm_cb.pm_reg_db[xx].mask = BTM_PM_REC_NOT_USED; + } + + if (cb != NULL && btm_cb.pm_pend_link_hdl != BTM_INVALID_HANDLE) { + (*cb)((btm_handle_to_acl(btm_cb.pm_pend_link_hdl))->remote_addr, BTM_PM_STS_ERROR, BTM_DEV_RESET, 0); + } + + /* no command pending */ + btm_cb.pm_pend_link_hdl = BTM_INVALID_HANDLE; +} + +/******************************************************************************* +** +** Function btm_pm_sm_alloc +** +** Description This function initializes the control block of an ACL link. +** It is called when an ACL connection is created. +** +** Returns void +** +*******************************************************************************/ +tBTM_PM_MCB *btm_pm_sm_alloc(void) +{ + tBTM_PM_MCB *p_db = (tBTM_PM_MCB *) osi_malloc(sizeof(tBTM_PM_MCB)); /* per ACL link */ + if (p_db) { + memset (p_db, 0, sizeof(tBTM_PM_MCB)); + p_db->state = BTM_PM_ST_ACTIVE; + if (list_length(btm_cb.p_pm_mode_db_list) >= MAX_L2CAP_LINKS) { + osi_free(p_db); + p_db = NULL; + } + if (!list_append(btm_cb.p_pm_mode_db_list, p_db)) { + osi_free(p_db); + p_db = NULL; + } + } + return p_db; +} +/******************************************************************************* +** +** Function btm_pm_find_acl_ind +** +** Description This function initializes the control block of an ACL link. +** It is called when an ACL connection is created. +** +** Returns void +** +*******************************************************************************/ + +/******************************************************************************* +** +** Function btm_pm_compare_modes +** Description get the "more active" mode of the 2 +** Returns void +** +*******************************************************************************/ +static tBTM_PM_PWR_MD *btm_pm_compare_modes(tBTM_PM_PWR_MD *p_md1, tBTM_PM_PWR_MD *p_md2, tBTM_PM_PWR_MD *p_res) +{ + UINT8 res; + + if (p_md1 == NULL) { + *p_res = *p_md2; + p_res->mode &= ~BTM_PM_MD_FORCE; + + return p_md2; + } + + if (p_md2->mode == BTM_PM_MD_ACTIVE || p_md1->mode == BTM_PM_MD_ACTIVE) { + return NULL; + } + + /* check if force bit is involved */ + if (p_md1->mode & BTM_PM_MD_FORCE) { + *p_res = *p_md1; + p_res->mode &= ~BTM_PM_MD_FORCE; + return p_res; + } + + if (p_md2->mode & BTM_PM_MD_FORCE) { + *p_res = *p_md2; + p_res->mode &= ~BTM_PM_MD_FORCE; + return p_res; + } + + res = (p_md1->mode - 1) * BTM_PM_NUM_SET_MODES + (p_md2->mode - 1); + res = btm_pm_md_comp_matrix[res]; + switch (res) { + case BTM_PM_GET_MD1: + *p_res = *p_md1; + return p_md1; + + case BTM_PM_GET_MD2: + *p_res = *p_md2; + return p_md2; + + case BTM_PM_GET_COMP: + p_res->mode = p_md1->mode; + /* min of the two */ + p_res->max = (p_md1->max < p_md2->max) ? (p_md1->max) : (p_md2->max); + /* max of the two */ + p_res->min = (p_md1->min > p_md2->min) ? (p_md1->min) : (p_md2->min); + + /* the intersection is NULL */ + if ( p_res->max < p_res->min) { + return NULL; + } + + if (p_res->mode == BTM_PM_MD_SNIFF) { + /* max of the two */ + p_res->attempt = (p_md1->attempt > p_md2->attempt) ? (p_md1->attempt) : (p_md2->attempt); + p_res->timeout = (p_md1->timeout > p_md2->timeout) ? (p_md1->timeout) : (p_md2->timeout); + } + return p_res; + } + return NULL; +} + +/******************************************************************************* +** +** Function btm_pm_get_set_mode +** Description get the resulting mode from the registered parties, then compare it +** with the requested mode, if the command is from an unregistered party. +** Returns void +** +*******************************************************************************/ +static tBTM_PM_MODE btm_pm_get_set_mode(UINT8 pm_id, tBTM_PM_MCB *p_cb, tBTM_PM_PWR_MD *p_mode, tBTM_PM_PWR_MD *p_res) +{ + int xx, loop_max; + tBTM_PM_PWR_MD *p_md = NULL; + + if (p_mode != NULL && p_mode->mode & BTM_PM_MD_FORCE) { + *p_res = *p_mode; + p_res->mode &= ~BTM_PM_MD_FORCE; + return p_res->mode; + } + + if (!p_mode) { + loop_max = BTM_MAX_PM_RECORDS + 1; + } else { + loop_max = BTM_MAX_PM_RECORDS; + } + + for ( xx = 0; xx < loop_max; xx++) { + /* g through all the registered "set" parties */ + if (btm_cb.pm_reg_db[xx].mask & BTM_PM_REG_SET) { + if (p_cb->req_mode[xx].mode == BTM_PM_MD_ACTIVE) { + /* if at least one registered (SET) party says ACTIVE, stay active */ + return BTM_PM_MD_ACTIVE; + } else { + /* if registered parties give conflicting information, stay active */ + if ( (btm_pm_compare_modes(p_md, &p_cb->req_mode[xx], p_res)) == NULL) { + return BTM_PM_MD_ACTIVE; + } + p_md = p_res; + } + } + } + + /* if the resulting mode is NULL(nobody registers SET), use the requested mode */ + if (p_md == NULL) { + if (p_mode) { + *p_res = *((tBTM_PM_PWR_MD *)p_mode); + } else { /* p_mode is NULL when btm_pm_snd_md_req is called from btm_pm_proc_mode_change */ + return BTM_PM_MD_ACTIVE; + } + } else { + /* if the command is from unregistered party, + compare the resulting mode from registered party*/ + if ( (pm_id == BTM_PM_SET_ONLY_ID) && + ((btm_pm_compare_modes(p_mode, p_md, p_res)) == NULL) ) { + return BTM_PM_MD_ACTIVE; + } + } + + return p_res->mode; +} + +/******************************************************************************* +** +** Function btm_pm_snd_md_req +** Description get the resulting mode and send the resuest to host controller +** Returns tBTM_STATUS +**, BOOLEAN *p_chg_ind +*******************************************************************************/ +static tBTM_STATUS btm_pm_snd_md_req(UINT8 pm_id, UINT16 link_hdl, tBTM_PM_PWR_MD *p_mode) +{ + tBTM_PM_PWR_MD md_res; + tBTM_PM_MODE mode; + tACL_CONN *p_acl_cb = btm_handle_to_acl(link_hdl); + tBTM_PM_MCB *p_cb = p_acl_cb->p_pm_mode_db; + BOOLEAN chg_ind = FALSE; + + mode = btm_pm_get_set_mode(pm_id, p_cb, p_mode, &md_res); + md_res.mode = mode; + +#if BTM_PM_DEBUG == TRUE + BTM_TRACE_DEBUG( "btm_pm_snd_md_req link_hdl:%d, mode: %d", + link_hdl, mode); +#endif // BTM_PM_DEBUG + + if ( p_cb->state == mode) { + /* already in the resulting mode */ + if ( (mode == BTM_PM_MD_ACTIVE) || + ((md_res.max >= p_cb->interval) && (md_res.min <= p_cb->interval)) ) { + return BTM_CMD_STORED; + } + /* Otherwise, needs to wake, then sleep */ + chg_ind = TRUE; + } + p_cb->chg_ind = chg_ind; + + /* cannot go directly from current mode to resulting mode. */ + if ( mode != BTM_PM_MD_ACTIVE && p_cb->state != BTM_PM_MD_ACTIVE) { + p_cb->chg_ind = TRUE; /* needs to wake, then sleep */ + } + + if (p_cb->chg_ind == TRUE) { /* needs to wake first */ + md_res.mode = BTM_PM_MD_ACTIVE; + } +#if (BTM_SSR_INCLUDED == TRUE) + else if (BTM_PM_MD_SNIFF == md_res.mode && p_cb->max_lat) { + btsnd_hcic_sniff_sub_rate(link_hdl, p_cb->max_lat, + p_cb->min_rmt_to, p_cb->min_loc_to); + p_cb->max_lat = 0; + } +#endif // BTM_SSR_INCLUDED + /* Default is failure */ + btm_cb.pm_pend_link_hdl = BTM_INVALID_HANDLE; + + /* send the appropriate HCI command */ + btm_cb.pm_pend_id = pm_id; + +#if BTM_PM_DEBUG == TRUE + BTM_TRACE_DEBUG("btm_pm_snd_md_req state:0x%x, link_hdl: %d", p_cb->state, link_hdl); +#endif // BTM_PM_DEBUG + + BTM_TRACE_DEBUG("%s switching from %s to %s.", __func__, mode_to_string(p_cb->state), mode_to_string(md_res.mode)); + switch (md_res.mode) { + case BTM_PM_MD_ACTIVE: + switch (p_cb->state) { + case BTM_PM_MD_SNIFF: + if (btsnd_hcic_exit_sniff_mode(link_hdl)) { + btm_cb.pm_pend_link_hdl = link_hdl; + } + break; + case BTM_PM_MD_PARK: + if (btsnd_hcic_exit_park_mode(link_hdl)) { + btm_cb.pm_pend_link_hdl = link_hdl; + } + break; + default: + /* Failure btm_cb.pm_pend_link = MAX_L2CAP_LINKS */ + break; + } + break; + + case BTM_PM_MD_HOLD: + if (btsnd_hcic_hold_mode (link_hdl, + md_res.max, md_res.min)) { + btm_cb.pm_pend_link_hdl = link_hdl; + } + break; + + case BTM_PM_MD_SNIFF: + if (btsnd_hcic_sniff_mode (link_hdl, + md_res.max, md_res.min, md_res.attempt, + md_res.timeout)) { + btm_cb.pm_pend_link_hdl = link_hdl; + } + break; + + case BTM_PM_MD_PARK: + if (btsnd_hcic_park_mode (link_hdl, + md_res.max, md_res.min)) { + btm_cb.pm_pend_link_hdl = link_hdl; + } + break; + default: + /* Failure btm_cb.pm_pend_link = MAX_L2CAP_LINKS */ + break; + } + + if (btm_cb.pm_pend_link_hdl == BTM_INVALID_HANDLE) { + /* the command was not sent */ +#if BTM_PM_DEBUG == TRUE + BTM_TRACE_DEBUG( "pm_pend_link_hdl: %d", btm_cb.pm_pend_link_hdl); +#endif // BTM_PM_DEBUG + return (BTM_NO_RESOURCES); + } + + return BTM_CMD_STARTED; +} + +/******************************************************************************* +** +** Function btm_pm_check_stored +** +** Description This function is called when an HCI command status event occurs +** to check if there's any PM command issued while waiting for +** HCI command status. +** +** Returns none. +** +*******************************************************************************/ +static void btm_pm_check_stored(void) +{ + tACL_CONN *p_acl_cb = NULL; + list_node_t *p_node = NULL; + for (p_node = list_begin(btm_cb.p_acl_db_list); p_node; p_node = list_next(p_node)) { + p_acl_cb = list_node(p_node); + if (p_acl_cb->p_pm_mode_db->state & BTM_PM_STORED_MASK) { + p_acl_cb->p_pm_mode_db->state &= ~BTM_PM_STORED_MASK; + BTM_TRACE_DEBUG( "btm_pm_check_stored :%d", p_acl_cb->hci_handle); + btm_pm_snd_md_req(BTM_PM_SET_ONLY_ID, p_acl_cb->hci_handle, NULL); + break; + } + } + +} + + +/******************************************************************************* +** +** Function btm_pm_proc_cmd_status +** +** Description This function is called when an HCI command status event occurs +** for power manager related commands. +** +** Input Parms status - status of the event (HCI_SUCCESS if no errors) +** +** Returns none. +** +*******************************************************************************/ +void btm_pm_proc_cmd_status(UINT8 status) +{ + tBTM_PM_MCB *p_cb; + tBTM_PM_STATUS pm_status; + tACL_CONN *p_acl_cb; + + if (btm_cb.pm_pend_link_hdl == BTM_INVALID_HANDLE) { + return; + } + + + p_acl_cb = btm_handle_to_acl(btm_cb.pm_pend_link_hdl); + if (p_acl_cb == NULL) { + return; + } + p_cb = p_acl_cb->p_pm_mode_db; + + if (status == HCI_SUCCESS) { + p_cb->state = BTM_PM_ST_PENDING; + pm_status = BTM_PM_STS_PENDING; +#if BTM_PM_DEBUG == TRUE + BTM_TRACE_DEBUG( "btm_pm_proc_cmd_status new state:0x%x", p_cb->state); +#endif // BTM_PM_DEBUG + } else { /* the command was not successfull. Stay in the same state */ + pm_status = BTM_PM_STS_ERROR; + } + + /* notify the caller is appropriate */ + if ( (btm_cb.pm_pend_id != BTM_PM_SET_ONLY_ID) && + (btm_cb.pm_reg_db[btm_cb.pm_pend_id].mask & BTM_PM_REG_NOTIF) ) { + (*btm_cb.pm_reg_db[btm_cb.pm_pend_id].cback)(p_acl_cb->remote_addr, pm_status, 0, status); + } + + /* no pending cmd now */ +#if BTM_PM_DEBUG == TRUE + BTM_TRACE_DEBUG( "btm_pm_proc_cmd_status state:0x%x, pm_pend_link: %d(new: %d)", + p_cb->state, btm_cb.pm_pend_link_hdl, MAX_L2CAP_LINKS); +#endif // BTM_PM_DEBUG + btm_cb.pm_pend_link_hdl = BTM_INVALID_HANDLE; + + btm_pm_check_stored(); +} + +/******************************************************************************* +** +** Function btm_process_mode_change +** +** Description This function is called when an HCI mode change event occurs. +** +** Input Parms hci_status - status of the event (HCI_SUCCESS if no errors) +** hci_handle - connection handle associated with the change +** mode - HCI_MODE_ACTIVE, HCI_MODE_HOLD, HCI_MODE_SNIFF, or HCI_MODE_PARK +** interval - number of baseband slots (meaning depends on mode) +** +** Returns none. +** +*******************************************************************************/ +void btm_pm_proc_mode_change (UINT8 hci_status, UINT16 hci_handle, UINT8 mode, UINT16 interval) +{ + tACL_CONN *p; + tBTM_PM_MCB *p_cb = NULL; + int yy; + tBTM_PM_STATE old_state; + tL2C_LCB *p_lcb; + + /* get the index to acl_db */ + p = btm_handle_to_acl(hci_handle); + if (!p) { + return; + } + + /* update control block */ + p_cb = p->p_pm_mode_db; + old_state = p_cb->state; + p_cb->state = mode; + p_cb->interval = interval; + + BTM_TRACE_DEBUG("%s switched from %s to %s.", __func__, mode_to_string(old_state), mode_to_string(p_cb->state)); + + if ((p_lcb = l2cu_find_lcb_by_bd_addr(p->remote_addr, BT_TRANSPORT_BR_EDR)) != NULL) { + if ((p_cb->state == BTM_PM_ST_ACTIVE) || (p_cb->state == BTM_PM_ST_SNIFF)) { + /* There might be any pending packets due to SNIFF or PENDING state */ + /* Trigger L2C to start transmission of the pending packets. */ + BTM_TRACE_DEBUG("btm mode change to active; check l2c_link for outgoing packets"); + l2c_link_check_send_pkts(p_lcb, NULL, NULL); + } + } + + /* notify registered parties */ + for (yy = 0; yy <= BTM_MAX_PM_RECORDS; yy++) { + /* set req_mode HOLD mode->ACTIVE */ + if ( (mode == BTM_PM_MD_ACTIVE) && (p_cb->req_mode[yy].mode == BTM_PM_MD_HOLD) ) { + p_cb->req_mode[yy].mode = BTM_PM_MD_ACTIVE; + } + } + + /* new request has been made. - post a message to BTU task */ + if (old_state & BTM_PM_STORED_MASK) { +#if BTM_PM_DEBUG == TRUE + BTM_TRACE_DEBUG( "btm_pm_proc_mode_change: Sending stored req:%d", xx); +#endif // BTM_PM_DEBUG + btm_pm_snd_md_req(BTM_PM_SET_ONLY_ID, hci_handle, NULL); + } else { + list_node_t *p_node = NULL; + + for (p_node =(list_begin(btm_cb.p_pm_mode_db_list)); p_node; p_node = (list_next(p_node))) { + p_cb = (tBTM_PM_MCB *)list_node(p_node); + if (p_cb->chg_ind == TRUE) { +#if BTM_PM_DEBUG == TRUE + BTM_TRACE_DEBUG( "btm_pm_proc_mode_change: Sending PM req :%d", zz); +#endif // BTM_PM_DEBUG + btm_pm_snd_md_req(BTM_PM_SET_ONLY_ID, hci_handle, NULL); + break; + } + } + } + + + /* notify registered parties */ + for (yy = 0; yy < BTM_MAX_PM_RECORDS; yy++) { + if (btm_cb.pm_reg_db[yy].mask & BTM_PM_REG_NOTIF) { + (*btm_cb.pm_reg_db[yy].cback)( p->remote_addr, mode, interval, hci_status); + } + } + + /* If mode change was because of an active role switch or change link key */ + btm_cont_rswitch(p, btm_find_dev(p->remote_addr), hci_status); +} + +/******************************************************************************* +** +** Function btm_pm_proc_ssr_evt +** +** Description This function is called when an HCI sniff subrating event occurs. +** +** Returns none. +** +*******************************************************************************/ +#if (BTM_SSR_INCLUDED == TRUE) +void btm_pm_proc_ssr_evt (UINT8 *p, UINT16 evt_len) +{ + UINT8 status; + UINT16 handle; + UINT16 max_rx_lat; + int xx; + tBTM_PM_MCB *p_cb; + tACL_CONN *p_acl = NULL; + UINT16 use_ssr = TRUE; + UNUSED(evt_len); + + STREAM_TO_UINT8 (status, p); + + STREAM_TO_UINT16 (handle, p); + /* get the index to acl_db */ + + p += 2; + STREAM_TO_UINT16 (max_rx_lat, p); + p_acl = btm_handle_to_acl(handle); + if (!p_acl) { + return; + } + p_cb = p_acl->p_pm_mode_db; + if (p_cb->interval == max_rx_lat) { + /* using legacy sniff */ + use_ssr = FALSE; + } + + /* notify registered parties */ + for (xx = 0; xx < BTM_MAX_PM_RECORDS; xx++) { + if (btm_cb.pm_reg_db[xx].mask & BTM_PM_REG_NOTIF) { + if ( p_acl) { + (*btm_cb.pm_reg_db[xx].cback)( p_acl->remote_addr, BTM_PM_STS_SSR, use_ssr, status); + } + } + } +} +#endif // BTM_SSR_INCLUDED + +/******************************************************************************* +** +** Function btm_pm_device_in_active_or_sniff_mode +** +** Description This function is called to check if in active or sniff mode +** +** Returns TRUE, if in active or sniff mode +** +*******************************************************************************/ +BOOLEAN btm_pm_device_in_active_or_sniff_mode(void) +{ + /* The active state is the highest state-includes connected device and sniff mode*/ + + /* Covers active and sniff modes */ + if (BTM_GetNumAclLinks() > 0) { + BTM_TRACE_DEBUG("%s - ACL links: %d", __func__, BTM_GetNumAclLinks()); + return TRUE; + } + +#if ((defined BLE_INCLUDED) && (BLE_INCLUDED == TRUE)) + /* Check BLE states */ + if (btm_ble_get_conn_st() != BLE_CONN_IDLE) { + BTM_TRACE_DEBUG("%s - BLE state: %x", __func__, btm_ble_get_conn_st()); + return TRUE; + } +#endif + + return FALSE; +} + +/******************************************************************************* +** +** Function btm_pm_device_in_scan_state +** +** Description This function is called to check if in paging, inquiry or connecting mode +** +** Returns TRUE, if in paging, inquiry or connecting mode +** +*******************************************************************************/ +BOOLEAN btm_pm_device_in_scan_state(void) +{ + /* Scan state-paging, inquiry, and trying to connect */ + + /* Check for paging */ + if (btm_cb.is_paging || (!fixed_queue_is_empty(btm_cb.page_queue)) || + BTM_BL_PAGING_STARTED == btm_cb.busy_level) { + BTM_TRACE_DEBUG("btm_pm_device_in_scan_state- paging"); + return TRUE; + } + + /* Check for inquiry */ + if ((btm_cb.btm_inq_vars.inq_active & (BTM_BR_INQ_ACTIVE_MASK | BTM_BLE_INQ_ACTIVE_MASK)) != 0) { + BTM_TRACE_DEBUG("btm_pm_device_in_scan_state- Inq active"); + return TRUE; + } + + return FALSE; +} + +/******************************************************************************* +** +** Function BTM_PM_ReadControllerState +** +** Description This function is called to obtain the controller state +** +** Returns Controller State-BTM_CONTRL_ACTIVE, BTM_CONTRL_SCAN, and BTM_CONTRL_IDLE +** +*******************************************************************************/ +tBTM_CONTRL_STATE BTM_PM_ReadControllerState(void) +{ + if (TRUE == btm_pm_device_in_active_or_sniff_mode()) { + return BTM_CONTRL_ACTIVE; + } else if (TRUE == btm_pm_device_in_scan_state()) { + return BTM_CONTRL_SCAN; + } else { + return BTM_CONTRL_IDLE; + } +} + +#if (!CONFIG_BT_STACK_NO_LOG) +static const char *mode_to_string(tBTM_PM_MODE mode) +{ + switch (mode) { + case BTM_PM_MD_ACTIVE: return "ACTIVE"; + case BTM_PM_MD_SNIFF: return "SNIFF"; + case BTM_PM_MD_PARK: return "PARK"; + case BTM_PM_MD_HOLD: return "HOLD"; + default: return "UNKNOWN"; + } +} +#endif diff --git a/lib/bt/host/bluedroid/stack/btm/btm_sco.c b/lib/bt/host/bluedroid/stack/btm/btm_sco.c new file mode 100644 index 00000000..53a9768a --- /dev/null +++ b/lib/bt/host/bluedroid/stack/btm/btm_sco.c @@ -0,0 +1,1907 @@ +/****************************************************************************** + * + * Copyright (C) 2000-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains functions that handle SCO connections. This includes + * operations such as connect, disconnect, change supported packet types. + * + ******************************************************************************/ + +#include +#include "stack/bt_types.h" +#include "common/bt_target.h" +#include "stack/bt_types.h" +#include "stack/hcimsgs.h" +#include "stack/btu.h" +#include "stack/btm_api.h" +#include "osi/allocator.h" +#include "btm_int.h" +#include "stack/hcidefs.h" +//#include "bt_utils.h" + +#if BTM_SCO_INCLUDED == TRUE + +/********************************************************************************/ +/* L O C A L D A T A D E F I N I T I O N S */ +/********************************************************************************/ + +#define SCO_ST_UNUSED 0 +#define SCO_ST_LISTENING 1 +#define SCO_ST_W4_CONN_RSP 2 +#define SCO_ST_CONNECTING 3 +#define SCO_ST_CONNECTED 4 +#define SCO_ST_DISCONNECTING 5 +#define SCO_ST_PEND_UNPARK 6 +#define SCO_ST_PEND_ROLECHANGE 7 + +/********************************************************************************/ +/* L O C A L F U N C T I O N P R O T O T Y P E S */ +/********************************************************************************/ + +static const tBTM_ESCO_PARAMS btm_esco_defaults = { + BTM_64KBITS_RATE, /* TX Bandwidth (64 kbits/sec) */ + BTM_64KBITS_RATE, /* RX Bandwidth (64 kbits/sec) */ + 0x000a, /* 10 ms (HS/HF can use EV3, 2-EV3, 3-EV3) */ + 0x0060, /* Inp Linear, Air CVSD, 2s Comp, 16bit */ + (BTM_SCO_PKT_TYPES_MASK_HV1 + /* Packet Types */ + BTM_SCO_PKT_TYPES_MASK_HV2 + + BTM_SCO_PKT_TYPES_MASK_HV3 + + BTM_SCO_PKT_TYPES_MASK_EV3 + + BTM_SCO_PKT_TYPES_MASK_EV4 + + BTM_SCO_PKT_TYPES_MASK_EV5), + BTM_ESCO_RETRANS_POWER /* Retransmission Effort (Power) */ +}; + +/******************************************************************************* +** +** Function btm_sco_flush_sco_data +** +** Description This function is called to flush the SCO data for this channel. +** +** Returns void +** +*******************************************************************************/ +void btm_sco_flush_sco_data(UINT16 sco_inx) +{ +#if BTM_SCO_HCI_INCLUDED == TRUE +#if (BTM_MAX_SCO_LINKS>0) + tSCO_CONN *p ; + BT_HDR *p_buf; + + if (sco_inx < BTM_MAX_SCO_LINKS) { + p = &btm_cb.sco_cb.sco_db[sco_inx]; + while ((p_buf = (BT_HDR *)fixed_queue_dequeue(p->xmit_data_q, 0)) != NULL) { + osi_free(p_buf); + } + } +#else + UNUSED(sco_inx); +#endif +#else + UNUSED(sco_inx); +#endif +} +/******************************************************************************* +** +** Function btm_sco_init +** +** Description This function is called at BTM startup to initialize +** +** Returns void +** +*******************************************************************************/ +void btm_sco_init (void) +{ +#if 0 /* cleared in btm_init; put back in if called from anywhere else! */ + memset (&btm_cb.sco_cb, 0, sizeof(tSCO_CB)); +#endif +#if (BTM_SCO_HCI_INCLUDED == TRUE) + for (int i = 0; i < BTM_MAX_SCO_LINKS; i++) { + btm_cb.sco_cb.sco_db[i].xmit_data_q = fixed_queue_new(QUEUE_SIZE_MAX); + } +#endif + /* Initialize nonzero defaults */ + btm_cb.sco_cb.sco_disc_reason = BTM_INVALID_SCO_DISC_REASON; + + btm_cb.sco_cb.def_esco_parms = btm_esco_defaults; /* Initialize with defaults */ + btm_cb.sco_cb.desired_sco_mode = BTM_DEFAULT_SCO_MODE; +} + +/******************************************************************************* +** +** Function btm_esco_conn_rsp +** +** Description This function is called upon receipt of an (e)SCO connection +** request event (BTM_ESCO_CONN_REQ_EVT) to accept or reject +** the request. Parameters used to negotiate eSCO links. +** If p_parms is NULL, then default values are used. +** If the link type of the incoming request is SCO, then only +** the tx_bw, max_latency, content format, and packet_types are +** valid. The hci_status parameter should be +** ([0x0] to accept, [0x0d..0x0f] to reject) +** +** Returns void +** +*******************************************************************************/ +static void btm_esco_conn_rsp (UINT16 sco_inx, UINT8 hci_status, BD_ADDR bda, + tBTM_ESCO_PARAMS *p_parms) +{ +#if (BTM_MAX_SCO_LINKS>0) + tSCO_CONN *p_sco = NULL; + tBTM_ESCO_PARAMS *p_setup; + UINT16 temp_pkt_types; + + if (sco_inx < BTM_MAX_SCO_LINKS) { + p_sco = &btm_cb.sco_cb.sco_db[sco_inx]; + } + + /* Reject the connect request if refused by caller or wrong state */ + if (hci_status != HCI_SUCCESS || p_sco == NULL) { + if (p_sco) { + p_sco->state = (p_sco->state == SCO_ST_W4_CONN_RSP) ? SCO_ST_LISTENING + : SCO_ST_UNUSED; + } + + if (!btm_cb.sco_cb.esco_supported) { + if (!btsnd_hcic_reject_conn (bda, hci_status)) { + BTM_TRACE_ERROR("Could not reject (e)SCO conn: No Buffer!!!"); + } + } else { + if (!btsnd_hcic_reject_esco_conn (bda, hci_status)) { + BTM_TRACE_ERROR("Could not reject (e)SCO conn: No Buffer!!!"); + } + } + } else { /* Connection is being accepted */ + p_sco->state = SCO_ST_CONNECTING; + p_setup = &p_sco->esco.setup; + /* If parameters not specified use the default */ + if (p_parms) { + *p_setup = *p_parms; + } else { /* Use the last setup passed thru BTM_SetEscoMode (or defaults) */ + *p_setup = btm_cb.sco_cb.def_esco_parms; + } + + temp_pkt_types = (p_setup->packet_types & + BTM_SCO_SUPPORTED_PKTS_MASK & + btm_cb.btm_sco_pkt_types_supported); + + /* Make sure at least one eSCO packet type is sent, else might confuse peer */ + /* Taking this out to confirm with BQB tests + ** Real application would like to include this though, as many devices + ** do not retry with SCO only if an eSCO connection fails. + if (!(temp_pkt_types & BTM_ESCO_LINK_ONLY_MASK)) + { + temp_pkt_types |= BTM_SCO_PKT_TYPES_MASK_EV3; + } + */ + /* If SCO request, remove eSCO packet types (conformance) */ + if (p_sco->esco.data.link_type == BTM_LINK_TYPE_SCO) { + temp_pkt_types &= BTM_SCO_LINK_ONLY_MASK; + temp_pkt_types |= BTM_SCO_EXCEPTION_PKTS_MASK; + } else { + /* OR in any exception packet types */ + temp_pkt_types |= ((p_setup->packet_types & BTM_SCO_EXCEPTION_PKTS_MASK) | + (btm_cb.btm_sco_pkt_types_supported & BTM_SCO_EXCEPTION_PKTS_MASK)); + } + + if (btsnd_hcic_accept_esco_conn (bda, p_setup->tx_bw, p_setup->rx_bw, + p_setup->max_latency, p_setup->voice_contfmt, + p_setup->retrans_effort, temp_pkt_types)) { + p_setup->packet_types = temp_pkt_types; + } else { + BTM_TRACE_ERROR("Could not accept SCO conn: No Buffer!!!"); + } + } +#endif +} + + +#if BTM_SCO_HCI_INCLUDED == TRUE +void btm_sco_process_num_bufs (UINT16 num_lm_sco_bufs) +{ + BTM_TRACE_DEBUG("%s, %d", __FUNCTION__, num_lm_sco_bufs); + btm_cb.sco_cb.num_lm_sco_bufs = btm_cb.sco_cb.xmit_window_size = num_lm_sco_bufs; +} + +/******************************************************************************* +** +** Function BTM_ConfigScoPath +** +** Description This function enable/disable SCO over HCI and registers SCO +** data callback if SCO over HCI is enabled. +** +** Parameter path: SCO or HCI +** p_sco_data_cb: callback function or SCO data if path is set +** to transport. +** p_pcm_param: pointer to the PCM interface parameter. If a NULL +** pointer is used, PCM parameter maintained in +** the control block will be used; otherwise update +** control block value. +** err_data_rpt: Lisbon feature to enable the erronous data report +** or not. +** +** Returns BTM_SUCCESS if the successful. +** BTM_NO_RESOURCES: no rsource to start the command. +** BTM_ILLEGAL_VALUE: invalid callback function pointer. +** BTM_CMD_STARTED :Command sent. Waiting for command cmpl event. +** +** +*******************************************************************************/ +//extern +tBTM_STATUS BTM_ConfigScoPath (tBTM_SCO_ROUTE_TYPE path, + tBTM_SCO_DATA_CB *p_sco_data_cb, + tBTM_SCO_PCM_PARAM *p_pcm_param, + BOOLEAN err_data_rpt) +{ + UNUSED(err_data_rpt); + UNUSED(p_pcm_param); + btm_cb.sco_cb.sco_path = path; + if (path == BTM_SCO_ROUTE_PCM) { + return BTM_SUCCESS; + } else if (path == BTM_SCO_ROUTE_HCI) { + if (p_sco_data_cb) { + btm_cb.sco_cb.p_data_cb = p_sco_data_cb; + } + } + + return BTM_SUCCESS; +} + +static void hci_sco_data_to_lower(BT_HDR *p_buf) +{ + p_buf->event = BT_EVT_TO_LM_HCI_SCO; + if (p_buf->offset == 0) { + BTM_TRACE_ERROR("offset cannot be 0"); + osi_free(p_buf); + } + + bte_main_hci_send(p_buf, (UINT16)(BT_EVT_TO_LM_HCI_SCO | LOCAL_BLE_CONTROLLER_ID)); +} +/******************************************************************************* +** +** Function btm_sco_check_send_pkts +** +** Description This function is called to check if it can send packets +** to the Host Controller. +** +** Returns void +** +*******************************************************************************/ +void btm_sco_check_send_pkts (UINT16 sco_inx) +{ + tSCO_CB *p_cb = &btm_cb.sco_cb; + tSCO_CONN *p_ccb = &p_cb->sco_db[sco_inx]; + + /* If there is data to send, send it now */ + BT_HDR *p_buf; + while (p_cb->xmit_window_size != 0) + { + if ((p_buf = (BT_HDR *)fixed_queue_dequeue(p_ccb->xmit_data_q, 0)) == NULL) { + break; + } +#if BTM_SCO_HCI_DEBUG + BTM_TRACE_DEBUG("btm: [%d] buf in xmit_data_q", + fixed_queue_length(p_ccb->xmit_data_q) + 1); +#endif + /* Don't go negative */ + p_cb->xmit_window_size -= 1; + p_ccb->sent_not_acked += 1; + + // HCI_SCO_DATA_TO_LOWER(p_buf); + hci_sco_data_to_lower(p_buf); + } +} + +void btm_sco_process_num_completed_pkts (UINT8 *p) +{ + UINT8 num_handles, xx; + UINT16 handle; + UINT16 num_sent; + UINT16 sco_inx; + tSCO_CB *p_cb = &btm_cb.sco_cb; + tSCO_CONN * p_ccb; + STREAM_TO_UINT8 (num_handles, p); + for (xx = 0; xx < num_handles; xx++) { + STREAM_TO_UINT16 (handle, p); + STREAM_TO_UINT16 (num_sent, p); + if ((sco_inx = btm_find_scb_by_handle(handle)) == BTM_MAX_SCO_LINKS) { + continue; + } + BTM_TRACE_DEBUG("%s, %d, %u", __FUNCTION__, handle, p_cb->xmit_window_size); //debug + p_ccb = &p_cb->sco_db[sco_inx]; + p_ccb->sent_not_acked -= num_sent; + // don't go negative + if (p_ccb->sent_not_acked < 0) { + BTM_TRACE_WARNING("SCO: un-acked underf: %u", p_ccb->sent_not_acked); + p_ccb->sent_not_acked = 0; + } + p_cb->xmit_window_size += num_sent; + if (p_cb->xmit_window_size > p_cb->num_lm_sco_bufs) { + BTM_TRACE_WARNING("SCO xwind: %d, max %d", p_cb->xmit_window_size, p_cb->num_lm_sco_bufs); + p_cb->xmit_window_size = p_cb->num_lm_sco_bufs; + } + btm_sco_check_send_pkts (sco_inx); + } + + return; +} + +/******************************************************************************* +** +** Function btm_pkt_stat_nums_update +** +** Description Update the number of received SCO data packet status. +** +** Returns void +** +*******************************************************************************/ +static void btm_pkt_stat_nums_update(uint16_t sco_inx, uint8_t pkt_status) +{ + tSCO_CONN *p_ccb = &btm_cb.sco_cb.sco_db[sco_inx]; + p_ccb->pkt_stat_nums.rx_total++; + if (pkt_status == BTM_SCO_DATA_CORRECT) { + p_ccb->pkt_stat_nums.rx_correct++; + } else if (pkt_status == BTM_SCO_DATA_PAR_ERR) { + p_ccb->pkt_stat_nums.rx_err++; + } else if (pkt_status == BTM_SCO_DATA_NONE) { + p_ccb->pkt_stat_nums.rx_none++; + } else { + p_ccb->pkt_stat_nums.rx_lost++; + } +} + +/******************************************************************************* +** +** Function btm_pkt_stat_send_nums_update +** +** Description Update the number of send packet status. +** +** Returns void +** +*******************************************************************************/ +static void btm_pkt_stat_send_nums_update(uint16_t sco_inx, uint8_t pkt_status) +{ + tSCO_CONN *p_ccb = &btm_cb.sco_cb.sco_db[sco_inx]; + p_ccb->pkt_stat_nums.tx_total++; + if (pkt_status != BTM_SUCCESS && pkt_status != BTM_NO_RESOURCES && pkt_status != BTM_SCO_BAD_LENGTH) { + p_ccb->pkt_stat_nums.tx_discarded++; + } +} + +/******************************************************************************* +** +** Function btm_pkt_stat_nums_reset +** +** Description This function is called to reset the number of packet status struct +** +** Returns void +** +*******************************************************************************/ +static void btm_pkt_stat_nums_reset(uint16_t sco_inx) +{ + memset(&btm_cb.sco_cb.sco_db[sco_inx].pkt_stat_nums, 0, sizeof(tBTM_SCO_PKT_STAT_NUMS)); +} + +/******************************************************************************* +** +** Function BTM_PktStatNumsGet +** +** Description This function is called to get the number of packet status struct +** +** Returns void +** +*******************************************************************************/ +void BTM_PktStatNumsGet(uint16_t sync_conn_handle, tBTM_SCO_PKT_STAT_NUMS *p_pkt_nums) +{ + uint16_t sco_inx = btm_find_scb_by_handle(sync_conn_handle); + if (sco_inx < BTM_MAX_SCO_LINKS) { + memcpy(p_pkt_nums, &btm_cb.sco_cb.sco_db[sco_inx].pkt_stat_nums, sizeof(tBTM_SCO_PKT_STAT_NUMS)); + } else { + memset(p_pkt_nums, 0, sizeof(tBTM_SCO_PKT_STAT_NUMS)); + } +} + +#endif /* BTM_SCO_HCI_INCLUDED == TRUE */ + +/******************************************************************************* +** +** Function btm_route_sco_data +** +** Description Route received SCO data. +** +** Returns void +** +*******************************************************************************/ +void btm_route_sco_data(BT_HDR *p_msg) +{ +#if BTM_SCO_HCI_INCLUDED == TRUE + UINT16 sco_inx, handle; + UINT8 *p = (UINT8 *)(p_msg + 1) + p_msg->offset; + UINT8 pkt_size = 0; + UINT8 pkt_status = 0; + + /* Extract Packet_Status_Flag and handle */ + STREAM_TO_UINT16 (handle, p); + pkt_status = HCID_GET_EVENT(handle); + handle = HCID_GET_HANDLE (handle); + + STREAM_TO_UINT8 (pkt_size, p); + UNUSED(pkt_size); + if ((sco_inx = btm_find_scb_by_handle(handle)) != BTM_MAX_SCO_LINKS ) { + /* send data callback */ + if (!btm_cb.sco_cb.p_data_cb ) + /* if no data callback registered, just free the buffer */ + { + osi_free (p_msg); + } else { + btm_pkt_stat_nums_update(sco_inx, pkt_status); + (*btm_cb.sco_cb.p_data_cb)(sco_inx, p_msg, (tBTM_SCO_DATA_FLAG) pkt_status); + } + } else { /* no mapping handle SCO connection is active, free the buffer */ + osi_free (p_msg); + } + BTM_TRACE_DEBUG ("SCO: hdl %x, len %d, pkt_sz %d\n", handle, p_msg->len, pkt_size); +#else + osi_free(p_msg); +#endif +} + +/******************************************************************************* +** +** Function BTM_WriteScoData +** +** Description This function write SCO data to a specified instance. The data +** to be written p_buf needs to carry an offset of +** HCI_SCO_PREAMBLE_SIZE bytes, and the data length can not +** exceed BTM_SCO_DATA_SIZE_MAX bytes, whose default value is set +** to 60 and is configurable. Data longer than the maximum bytes +** will be truncated. +** +** Returns BTM_SUCCESS: data write is successful +** BTM_ILLEGAL_VALUE: SCO data contains illegal offset value. +** BTM_SCO_BAD_LENGTH: SCO data length exceeds the max SCO packet +** size. +** BTM_NO_RESOURCES: no resources. +** BTM_UNKNOWN_ADDR: unknown SCO connection handle, or SCO is not +** routed via HCI. +** BTM_ERR_PROCESSING: transmit queue overflow +** +** +*******************************************************************************/ +tBTM_STATUS BTM_WriteScoData (UINT16 sco_inx, BT_HDR *p_buf) +{ + APPL_TRACE_DEBUG("%s", __FUNCTION__); +#if (BTM_SCO_HCI_INCLUDED == TRUE) && (BTM_MAX_SCO_LINKS>0) + tSCO_CONN *p_ccb = &btm_cb.sco_cb.sco_db[sco_inx]; + UINT8 *p; + tBTM_STATUS status = BTM_SUCCESS; + + if (sco_inx < BTM_MAX_SCO_LINKS && btm_cb.sco_cb.p_data_cb && + p_ccb->state == SCO_ST_CONNECTED) { + /* Ensure we have enough space in the buffer for the SCO and HCI headers */ + if (p_buf->offset < HCI_SCO_PREAMBLE_SIZE) { + BTM_TRACE_ERROR ("BTM SCO - cannot send buffer, offset: %d", p_buf->offset); + status = BTM_ILLEGAL_VALUE; + } else { /* write HCI header */ + /* Step back 3 bytes to add the headers */ + p_buf->offset -= HCI_SCO_PREAMBLE_SIZE; + /* Set the pointer to the beginning of the data */ + p = (UINT8 *)(p_buf + 1) + p_buf->offset; + /* add HCI handle */ + UINT16_TO_STREAM (p, p_ccb->hci_handle); + /* only sent the first BTM_SCO_DATA_SIZE_MAX bytes data if more than max, + and set warning status */ + if (p_buf->len > BTM_SCO_DATA_SIZE_MAX) { + BTM_TRACE_WARNING ("BTM SCO hdl %x, bad len %u", p_ccb->hci_handle, p_buf->len); + p_buf->len = BTM_SCO_DATA_SIZE_MAX; + status = BTM_SCO_BAD_LENGTH; + } + + UINT8_TO_STREAM (p, (UINT8)p_buf->len); + + p_buf->len += HCI_SCO_PREAMBLE_SIZE; + + if (fixed_queue_length(p_ccb->xmit_data_q) < BTM_SCO_XMIT_QUEUE_THRS) { + if (fixed_queue_length(p_ccb->xmit_data_q) >= BTM_SCO_XMIT_QUEUE_HIGH_WM) { + status = BTM_NO_RESOURCES; + } + fixed_queue_enqueue(p_ccb->xmit_data_q, p_buf, FIXED_QUEUE_MAX_TIMEOUT); + btm_sco_check_send_pkts (sco_inx); + } else { + BTM_TRACE_WARNING ("SCO xmit Q overflow, pkt dropped"); + status = BTM_ERR_PROCESSING; + } + } + } else { + BTM_TRACE_WARNING ("BTM_WriteScoData, invalid sco index: %d at state [%d]", + sco_inx, btm_cb.sco_cb.sco_db[sco_inx].state); + status = BTM_UNKNOWN_ADDR; + } + + if (status != BTM_SUCCESS && status!= BTM_NO_RESOURCES && status != BTM_SCO_BAD_LENGTH) { + BTM_TRACE_WARNING ("stat %d", status); + osi_free(p_buf); + } + btm_pkt_stat_send_nums_update(sco_inx, status); + return (status); + +#else + UNUSED(sco_inx); + UNUSED(p_buf); + return (BTM_NO_RESOURCES); +#endif +} + +#if (BTM_MAX_SCO_LINKS>0) +/******************************************************************************* +** +** Function btm_send_connect_request +** +** Description This function is called to respond to SCO connect indications +** +** Returns void +** +*******************************************************************************/ +static tBTM_STATUS btm_send_connect_request(UINT16 acl_handle, + tBTM_ESCO_PARAMS *p_setup) +{ + UINT16 temp_pkt_types; + tACL_CONN *p_acl; + + /* Send connect request depending on version of spec */ + if (!btm_cb.sco_cb.esco_supported) { + if (!btsnd_hcic_add_SCO_conn (acl_handle, BTM_ESCO_2_SCO(p_setup->packet_types))) { + return (BTM_NO_RESOURCES); + } + } else { + temp_pkt_types = (p_setup->packet_types & BTM_SCO_SUPPORTED_PKTS_MASK & + btm_cb.btm_sco_pkt_types_supported); + + /* OR in any exception packet types */ + temp_pkt_types |= ((p_setup->packet_types & BTM_SCO_EXCEPTION_PKTS_MASK) | + (btm_cb.btm_sco_pkt_types_supported & BTM_SCO_EXCEPTION_PKTS_MASK)); + + /* Finally, remove EDR eSCO if the remote device doesn't support it */ + /* UPF25: Only SCO was brought up in this case */ + p_acl = btm_handle_to_acl(acl_handle); + if (p_acl) { + if (!HCI_EDR_ESCO_2MPS_SUPPORTED(p_acl->peer_lmp_features[HCI_EXT_FEATURES_PAGE_0])) { + + BTM_TRACE_WARNING("BTM Remote does not support 2-EDR eSCO"); + temp_pkt_types |= (HCI_ESCO_PKT_TYPES_MASK_NO_2_EV3 | + HCI_ESCO_PKT_TYPES_MASK_NO_2_EV5); + } + if (!HCI_EDR_ESCO_3MPS_SUPPORTED(p_acl->peer_lmp_features[HCI_EXT_FEATURES_PAGE_0])) { + + BTM_TRACE_WARNING("BTM Remote does not support 3-EDR eSCO"); + temp_pkt_types |= (HCI_ESCO_PKT_TYPES_MASK_NO_3_EV3 | + HCI_ESCO_PKT_TYPES_MASK_NO_3_EV5); + } + + /* Check to see if BR/EDR Secure Connections is being used + ** If so, we cannot use SCO-only packet types (HFP 1.7) + */ + if (BTM_BothEndsSupportSecureConnections(p_acl->remote_addr)) { + temp_pkt_types &= ~(BTM_SCO_PKT_TYPE_MASK); + BTM_TRACE_DEBUG("%s: SCO Conn: pkt_types after removing SCO (0x%04x)", __FUNCTION__, + temp_pkt_types); + + /* Return error if no packet types left */ + if (temp_pkt_types == 0) { + BTM_TRACE_ERROR("%s: SCO Conn (BR/EDR SC): No packet types available",__FUNCTION__); + return (BTM_WRONG_MODE); + } + } else { + BTM_TRACE_DEBUG("%s: SCO Conn(BR/EDR SC):local or peer does not support BR/EDR SC",__FUNCTION__); + } + } + + + BTM_TRACE_API("txbw 0x%x, rxbw 0x%x, lat 0x%x, voice 0x%x, retrans 0x%02x, pkt 0x%04x", + p_setup->tx_bw, p_setup->rx_bw, + p_setup->max_latency, p_setup->voice_contfmt, + p_setup->retrans_effort, temp_pkt_types); + + if (!btsnd_hcic_setup_esco_conn(acl_handle, + p_setup->tx_bw, + p_setup->rx_bw, + p_setup->max_latency, + p_setup->voice_contfmt, + p_setup->retrans_effort, + temp_pkt_types)) { + return (BTM_NO_RESOURCES); + } else { + p_setup->packet_types = temp_pkt_types; + } + } + + return (BTM_CMD_STARTED); +} +#endif + +/******************************************************************************* +** +** Function btm_set_sco_ind_cback +** +** Description This function is called to register for TCS SCO connect +** indications. +** +** Returns void +** +*******************************************************************************/ +void btm_set_sco_ind_cback( tBTM_SCO_IND_CBACK *sco_ind_cb ) +{ + btm_cb.sco_cb.app_sco_ind_cb = sco_ind_cb; +} + +/******************************************************************************* +** +** Function btm_accept_sco_link +** +** Description This function is called to respond to TCS SCO connect +** indications +** +** Returns void +** +*******************************************************************************/ +void btm_accept_sco_link(UINT16 sco_inx, tBTM_ESCO_PARAMS *p_setup, + tBTM_SCO_CB *p_conn_cb, tBTM_SCO_CB *p_disc_cb) +{ +#if (BTM_MAX_SCO_LINKS>0) + tSCO_CONN *p_sco; + + if (sco_inx >= BTM_MAX_SCO_LINKS) { + BTM_TRACE_ERROR("btm_accept_sco_link: Invalid sco_inx(%d)", sco_inx); + return; + } + + /* Link role is ignored in for this message */ + p_sco = &btm_cb.sco_cb.sco_db[sco_inx]; + p_sco->p_conn_cb = p_conn_cb; + p_sco->p_disc_cb = p_disc_cb; + p_sco->esco.data.link_type = BTM_LINK_TYPE_ESCO; /* Accept with all supported types */ + + BTM_TRACE_DEBUG("TCS accept SCO: Packet Types 0x%04x", p_setup->packet_types); + + btm_esco_conn_rsp(sco_inx, HCI_SUCCESS, p_sco->esco.data.bd_addr, p_setup); +#else + btm_reject_sco_link(sco_inx); +#endif +} + +/******************************************************************************* +** +** Function btm_reject_sco_link +** +** Description This function is called to respond to SCO connect indications +** +** Returns void +** +*******************************************************************************/ +void btm_reject_sco_link( UINT16 sco_inx ) +{ + btm_esco_conn_rsp(sco_inx, HCI_ERR_HOST_REJECT_RESOURCES, + btm_cb.sco_cb.sco_db[sco_inx].esco.data.bd_addr, NULL); +} + +/******************************************************************************* +** +** Function BTM_CreateSco +** +** Description This function is called to create an SCO connection. If the +** "is_orig" flag is TRUE, the connection will be originated, +** otherwise BTM will wait for the other side to connect. +** +** NOTE: If BTM_IGNORE_SCO_PKT_TYPE is passed in the pkt_types +** parameter the default packet types is used. +** +** Returns BTM_UNKNOWN_ADDR if the ACL connection is not up +** BTM_BUSY if another SCO being set up to +** the same BD address +** BTM_NO_RESOURCES if the max SCO limit has been reached +** BTM_CMD_STARTED if the connection establishment is started. +** In this case, "*p_sco_inx" is filled in +** with the sco index used for the connection. +** +*******************************************************************************/ +tBTM_STATUS BTM_CreateSco (BD_ADDR remote_bda, BOOLEAN is_orig, UINT16 pkt_types, + UINT16 *p_sco_inx, tBTM_SCO_CB *p_conn_cb, + tBTM_SCO_CB *p_disc_cb) +{ +#if (BTM_MAX_SCO_LINKS > 0) + tBTM_ESCO_PARAMS *p_setup; + tSCO_CONN *p = &btm_cb.sco_cb.sco_db[0]; + UINT16 xx; + UINT16 acl_handle = 0; + UINT16 temp_pkt_types; + tACL_CONN *p_acl; + +#if (BTM_SCO_WAKE_PARKED_LINK == TRUE) + tBTM_PM_MODE md; + tBTM_PM_PWR_MD pm; +#else // BTM_SCO_WAKE_PARKED_LINK + UINT8 mode; +#endif // BTM_SCO_WAKE_PARKED_LINK + + *p_sco_inx = BTM_INVALID_SCO_INDEX; + + /* If originating, ensure that there is an ACL connection to the BD Address */ + if (is_orig) { + if ((!remote_bda) || ((acl_handle = BTM_GetHCIConnHandle (remote_bda, BT_TRANSPORT_BR_EDR)) == 0xFFFF)) { + return (BTM_UNKNOWN_ADDR); + } + } + + if (remote_bda) { + /* If any SCO is being established to the remote BD address, refuse this */ + for (xx = 0; xx < BTM_MAX_SCO_LINKS; xx++, p++) { + if (((p->state == SCO_ST_CONNECTING) || (p->state == SCO_ST_LISTENING) + || (p->state == SCO_ST_PEND_UNPARK)) + && (!memcmp (p->esco.data.bd_addr, remote_bda, BD_ADDR_LEN))) { + return (BTM_BUSY); + } + } + } else { + /* Support only 1 wildcard BD address at a time */ + for (xx = 0; xx < BTM_MAX_SCO_LINKS; xx++, p++) { + if ((p->state == SCO_ST_LISTENING) && (!p->rem_bd_known)) { + return (BTM_BUSY); + } + } + } + + /* Now, try to find an unused control block, and kick off the SCO establishment */ + for (xx = 0, p = &btm_cb.sco_cb.sco_db[0]; xx < BTM_MAX_SCO_LINKS; xx++, p++) { + if (p->state == SCO_ST_UNUSED) { + if (remote_bda) { + if (is_orig) { + /* can not create SCO link if in park mode */ +#if BTM_SCO_WAKE_PARKED_LINK == TRUE + if (BTM_ReadPowerMode(remote_bda, &md) == BTM_SUCCESS) { + if (md == BTM_PM_MD_PARK || md == BTM_PM_MD_SNIFF) { + memset( (void *)&pm, 0, sizeof(pm)); + pm.mode = BTM_PM_MD_ACTIVE; + BTM_SetPowerMode(BTM_PM_SET_ONLY_ID, remote_bda, &pm); + p->state = SCO_ST_PEND_UNPARK; + } + } +#else // BTM_SCO_WAKE_PARKED_LINK + if ( (BTM_ReadPowerMode(remote_bda, &mode) == BTM_SUCCESS) && (mode == BTM_PM_MD_PARK) ) { + return (BTM_WRONG_MODE); + } +#endif // BTM_SCO_WAKE_PARKED_LINK + } + memcpy (p->esco.data.bd_addr, remote_bda, BD_ADDR_LEN); + p->rem_bd_known = TRUE; + } else { + p->rem_bd_known = FALSE; + } + + /* Link role is ignored in for this message */ + if (pkt_types == BTM_IGNORE_SCO_PKT_TYPE) { + pkt_types = btm_cb.sco_cb.def_esco_parms.packet_types; + } + + p_setup = &p->esco.setup; + *p_setup = btm_cb.sco_cb.def_esco_parms; + p_setup->packet_types = (btm_cb.sco_cb.desired_sco_mode == BTM_LINK_TYPE_SCO) + ? (pkt_types & BTM_SCO_LINK_ONLY_MASK) : pkt_types; + + temp_pkt_types = (p_setup->packet_types & BTM_SCO_SUPPORTED_PKTS_MASK & + btm_cb.btm_sco_pkt_types_supported); + + /* OR in any exception packet types */ + if (btm_cb.sco_cb.desired_sco_mode == HCI_LINK_TYPE_ESCO) { + temp_pkt_types |= ((p_setup->packet_types & BTM_SCO_EXCEPTION_PKTS_MASK) | + (btm_cb.btm_sco_pkt_types_supported & BTM_SCO_EXCEPTION_PKTS_MASK)); + } else { /* Only using SCO packet types; turn off EDR also */ + temp_pkt_types |= BTM_SCO_EXCEPTION_PKTS_MASK; + } + + p_setup->packet_types = temp_pkt_types; + p->p_conn_cb = p_conn_cb; + p->p_disc_cb = p_disc_cb; + p->hci_handle = BTM_INVALID_HCI_HANDLE; + p->is_orig = is_orig; + + if ( p->state != SCO_ST_PEND_UNPARK ) { + if (is_orig) { + /* If role change is in progress, do not proceed with SCO setup + * Wait till role change is complete */ + p_acl = btm_bda_to_acl(remote_bda, BT_TRANSPORT_BR_EDR); + if (p_acl && p_acl->switch_role_state != BTM_ACL_SWKEY_STATE_IDLE) { + BTM_TRACE_API("Role Change is in progress for ACL handle 0x%04x", acl_handle); + p->state = SCO_ST_PEND_ROLECHANGE; + + } + } + } + + if ( p->state != SCO_ST_PEND_UNPARK && p->state != SCO_ST_PEND_ROLECHANGE ) { + if (is_orig) { + BTM_TRACE_API("BTM_CreateSco -> (e)SCO Link for ACL handle 0x%04x, Desired Type %d", + acl_handle, btm_cb.sco_cb.desired_sco_mode); + + if ((btm_send_connect_request(acl_handle, p_setup)) != BTM_CMD_STARTED) { + return (BTM_NO_RESOURCES); + } + + p->state = SCO_ST_CONNECTING; + } else { + p->state = SCO_ST_LISTENING; + } + } + + *p_sco_inx = xx; + + return (BTM_CMD_STARTED); + } + } + +#endif + /* If here, all SCO blocks in use */ + return (BTM_NO_RESOURCES); +} + +#if (BTM_SCO_WAKE_PARKED_LINK == TRUE) +/******************************************************************************* +** +** Function btm_sco_chk_pend_unpark +** +** Description This function is called by BTIF when there is a mode change +** event to see if there are SCO commands waiting for the unpark. +** +** Returns void +** +*******************************************************************************/ +void btm_sco_chk_pend_unpark (UINT8 hci_status, UINT16 hci_handle) +{ +#if (BTM_MAX_SCO_LINKS>0) + UINT16 xx; + UINT16 acl_handle; + tSCO_CONN *p = &btm_cb.sco_cb.sco_db[0]; + + for (xx = 0; xx < BTM_MAX_SCO_LINKS; xx++, p++) { + if ((p->state == SCO_ST_PEND_UNPARK) && + ((acl_handle = BTM_GetHCIConnHandle (p->esco.data.bd_addr, BT_TRANSPORT_BR_EDR)) == hci_handle)) + + { + BTM_TRACE_API("btm_sco_chk_pend_unpark -> (e)SCO Link for ACL handle 0x%04x, Desired Type %d, hci_status 0x%02x", + acl_handle, btm_cb.sco_cb.desired_sco_mode, hci_status); + + if ((btm_send_connect_request(acl_handle, &p->esco.setup)) == BTM_CMD_STARTED) { + p->state = SCO_ST_CONNECTING; + } + } + } +#endif // BTM_MAX_SCO_LINKS +} +#endif // BTM_SCO_WAKE_PARKED_LINK + +/******************************************************************************* +** +** Function btm_sco_chk_pend_rolechange +** +** Description This function is called by BTIF when there is a role change +** event to see if there are SCO commands waiting for the role change. +** +** Returns void +** +*******************************************************************************/ +void btm_sco_chk_pend_rolechange (UINT16 hci_handle) +{ +#if (BTM_MAX_SCO_LINKS>0) + UINT16 xx; + UINT16 acl_handle; + tSCO_CONN *p = &btm_cb.sco_cb.sco_db[0]; + + for (xx = 0; xx < BTM_MAX_SCO_LINKS; xx++, p++) { + if ((p->state == SCO_ST_PEND_ROLECHANGE) && + ((acl_handle = BTM_GetHCIConnHandle (p->esco.data.bd_addr, BT_TRANSPORT_BR_EDR)) == hci_handle)) + + { + BTM_TRACE_API("btm_sco_chk_pend_rolechange -> (e)SCO Link for ACL handle 0x%04x", acl_handle); + + if ((btm_send_connect_request(acl_handle, &p->esco.setup)) == BTM_CMD_STARTED) { + p->state = SCO_ST_CONNECTING; + } + } + } +#endif +} + +/******************************************************************************* +** +** Function btm_sco_conn_req +** +** Description This function is called by BTIF when an SCO connection +** request is received from a remote. +** +** Returns void +** +*******************************************************************************/ +void btm_sco_conn_req (BD_ADDR bda, DEV_CLASS dev_class, UINT8 link_type) +{ +#if (BTM_MAX_SCO_LINKS>0) + tSCO_CB *p_sco = &btm_cb.sco_cb; + tSCO_CONN *p = &p_sco->sco_db[0]; + UINT16 xx; + tBTM_ESCO_CONN_REQ_EVT_DATA evt_data; + + for (xx = 0; xx < BTM_MAX_SCO_LINKS; xx++, p++) { + /* + * If the sco state is in the SCO_ST_CONNECTING state, we still need + * to return accept sco to avoid race conditon for sco creation + */ + int rem_bd_matches = p->rem_bd_known && + !memcmp (p->esco.data.bd_addr, bda, BD_ADDR_LEN); + if (((p->state == SCO_ST_CONNECTING) && rem_bd_matches) || + ((p->state == SCO_ST_LISTENING) && (rem_bd_matches || !p->rem_bd_known))) { + /* If this guy was a wildcard, he is not one any more */ + p->rem_bd_known = TRUE; + p->esco.data.link_type = link_type; + p->state = SCO_ST_W4_CONN_RSP; + memcpy (p->esco.data.bd_addr, bda, BD_ADDR_LEN); + + /* If no callback, auto-accept the connection if packet types match */ + if (!p->esco.p_esco_cback) { + /* If requesting eSCO reject if default parameters are SCO only */ + if ((link_type == BTM_LINK_TYPE_ESCO + && !(p_sco->def_esco_parms.packet_types & BTM_ESCO_LINK_ONLY_MASK) + && ((p_sco->def_esco_parms.packet_types & BTM_SCO_EXCEPTION_PKTS_MASK) + == BTM_SCO_EXCEPTION_PKTS_MASK)) + + /* Reject request if SCO is desired but no SCO packets delected */ + || (link_type == BTM_LINK_TYPE_SCO + && !(p_sco->def_esco_parms.packet_types & BTM_SCO_LINK_ONLY_MASK))) { + btm_esco_conn_rsp(xx, HCI_ERR_HOST_REJECT_RESOURCES, bda, NULL); + } else { /* Accept the request */ + btm_esco_conn_rsp(xx, HCI_SUCCESS, bda, NULL); + } + } else { /* Notify upper layer of connect indication */ + memcpy(evt_data.bd_addr, bda, BD_ADDR_LEN); + memcpy(evt_data.dev_class, dev_class, DEV_CLASS_LEN); + evt_data.link_type = link_type; + evt_data.sco_inx = xx; + p->esco.p_esco_cback(BTM_ESCO_CONN_REQ_EVT, (tBTM_ESCO_EVT_DATA *)&evt_data); + } + + return; + } + } + + /* TCS usage */ + if (btm_cb.sco_cb.app_sco_ind_cb) { + /* Now, try to find an unused control block */ + for (xx = 0, p = &btm_cb.sco_cb.sco_db[0]; xx < BTM_MAX_SCO_LINKS; xx++, p++) { + if (p->state == SCO_ST_UNUSED) { + p->is_orig = FALSE; + p->state = SCO_ST_LISTENING; + + p->esco.data.link_type = link_type; + memcpy (p->esco.data.bd_addr, bda, BD_ADDR_LEN); + p->rem_bd_known = TRUE; + break; + } + } + if ( xx < BTM_MAX_SCO_LINKS) { + btm_cb.sco_cb.app_sco_ind_cb(xx); + return; + } + } + +#endif + /* If here, no one wants the SCO connection. Reject it */ + BTM_TRACE_WARNING("btm_sco_conn_req: No one wants this SCO connection; rejecting it"); + btm_esco_conn_rsp(BTM_MAX_SCO_LINKS, HCI_ERR_HOST_REJECT_RESOURCES, bda, NULL); +} + +/******************************************************************************* +** +** Function btm_sco_connected +** +** Description This function is called by BTIF when an (e)SCO connection +** is connected. +** +** Returns void +** +*******************************************************************************/ +void btm_sco_connected (UINT8 hci_status, BD_ADDR bda, UINT16 hci_handle, + tBTM_ESCO_DATA *p_esco_data) +{ +#if (BTM_MAX_SCO_LINKS>0) + tSCO_CONN *p = &btm_cb.sco_cb.sco_db[0]; + UINT16 xx; + BOOLEAN spt = FALSE; + tBTM_CHG_ESCO_PARAMS parms; +#endif + + btm_cb.sco_cb.sco_disc_reason = hci_status; + BTM_TRACE_API("%s, handle %x", __FUNCTION__, hci_handle); +#if (BTM_MAX_SCO_LINKS>0) + for (xx = 0; xx < BTM_MAX_SCO_LINKS; xx++, p++) { + if (((p->state == SCO_ST_CONNECTING) || + (p->state == SCO_ST_LISTENING) || + (p->state == SCO_ST_W4_CONN_RSP)) + && (p->rem_bd_known) + && (!bda || !memcmp (p->esco.data.bd_addr, bda, BD_ADDR_LEN))) { + if (hci_status != HCI_SUCCESS) { + /* Report the error if originator, otherwise remain in Listen mode */ + if (p->is_orig) { + /* If role switch is pending, we need try again after role switch is complete */ + if (hci_status == HCI_ERR_ROLE_SWITCH_PENDING) { + BTM_TRACE_API("Role Change pending for HCI handle 0x%04x", hci_handle); + p->state = SCO_ST_PEND_ROLECHANGE; + } + /* avoid calling disconnect callback because of sco creation race */ + else if (hci_status != HCI_ERR_LMP_ERR_TRANS_COLLISION) { + p->state = SCO_ST_UNUSED; + (*p->p_disc_cb)(xx); + } + } else { + /* Notify the upper layer that incoming sco connection has failed. */ + if (p->state == SCO_ST_CONNECTING) { + p->state = SCO_ST_UNUSED; + (*p->p_disc_cb)(xx); + } else { + p->state = SCO_ST_LISTENING; + } + } + + return; + } + + if (p->state == SCO_ST_LISTENING) { + spt = TRUE; + } +#if BTM_SCO_HCI_INCLUDED == TRUE + p->sent_not_acked = 0; + btm_pkt_stat_nums_reset(xx); +#endif + p->state = SCO_ST_CONNECTED; + p->hci_handle = hci_handle; + + if (!btm_cb.sco_cb.esco_supported) { + p->esco.data.link_type = BTM_LINK_TYPE_SCO; + if (spt) { + parms.packet_types = p->esco.setup.packet_types; + /* Keep the other parameters the same for SCO */ + parms.max_latency = p->esco.setup.max_latency; + parms.retrans_effort = p->esco.setup.retrans_effort; + + BTM_ChangeEScoLinkParms(xx, &parms); + } + } else { + if (p_esco_data) { + p->esco.data = *p_esco_data; + } + } + + (*p->p_conn_cb)(xx); + + return; + } + } +#endif +} + + +/******************************************************************************* +** +** Function btm_find_scb_by_handle +** +** Description Look through all active SCO connection for a match based on the +** HCI handle. +** +** Returns index to matched SCO connection CB, or BTM_MAX_SCO_LINKS if +** no match. +** +*******************************************************************************/ +UINT16 btm_find_scb_by_handle (UINT16 handle) +{ + int xx; + tSCO_CONN *p = &btm_cb.sco_cb.sco_db[0]; + + for (xx = 0; xx < BTM_MAX_SCO_LINKS; xx++, p++) { + if ((p->state == SCO_ST_CONNECTED) && (p->hci_handle == handle)) { + return (xx); + } + } + + /* If here, no match found */ + return (xx); +} + +/******************************************************************************* +** +** Function BTM_RemoveSco +** +** Description This function is called to remove a specific SCO connection. +** +** Returns status of the operation +** +*******************************************************************************/ +tBTM_STATUS BTM_RemoveSco (UINT16 sco_inx) +{ +#if (BTM_MAX_SCO_LINKS>0) + tSCO_CONN *p = &btm_cb.sco_cb.sco_db[sco_inx]; + UINT16 tempstate; + + /* Validity check */ + if ((sco_inx >= BTM_MAX_SCO_LINKS) || (p->state == SCO_ST_UNUSED)) { + return (BTM_UNKNOWN_ADDR); + } + + /* If no HCI handle, simply drop the connection and return */ + if (p->hci_handle == BTM_INVALID_HCI_HANDLE || p->state == SCO_ST_PEND_UNPARK) { + p->hci_handle = BTM_INVALID_HCI_HANDLE; + p->state = SCO_ST_UNUSED; + p->esco.p_esco_cback = NULL; /* Deregister the eSCO event callback */ + return (BTM_SUCCESS); + } + + tempstate = p->state; + p->state = SCO_ST_DISCONNECTING; + + if (!btsnd_hcic_disconnect (p->hci_handle, HCI_ERR_PEER_USER)) { + p->state = tempstate; + return (BTM_NO_RESOURCES); + } + + return (BTM_CMD_STARTED); +#else + return (BTM_NO_RESOURCES); +#endif +} + +/******************************************************************************* +** +** Function btm_remove_sco_links +** +** Description This function is called to remove all sco links for an ACL link. +** +** Returns void +** +*******************************************************************************/ +void btm_remove_sco_links (BD_ADDR bda) +{ +#if (BTM_MAX_SCO_LINKS>0) + tSCO_CONN *p = &btm_cb.sco_cb.sco_db[0]; + UINT16 xx; + + for (xx = 0; xx < BTM_MAX_SCO_LINKS; xx++, p++) { + if (p->rem_bd_known && (!memcmp (p->esco.data.bd_addr, bda, BD_ADDR_LEN))) { + BTM_RemoveSco(xx); + } + } +#endif +} + +/******************************************************************************* +** +** Function btm_sco_removed +** +** Description This function is called by BTIF when an SCO connection +** is removed. +** +** Returns void +** +*******************************************************************************/ +void btm_sco_removed (UINT16 hci_handle, UINT8 reason) +{ +#if (BTM_MAX_SCO_LINKS>0) + tSCO_CONN *p = &btm_cb.sco_cb.sco_db[0]; + UINT16 xx; +#endif + + btm_cb.sco_cb.sco_disc_reason = reason; + +#if (BTM_MAX_SCO_LINKS>0) + p = &btm_cb.sco_cb.sco_db[0]; + for (xx = 0; xx < BTM_MAX_SCO_LINKS; xx++, p++) { + if ((p->state != SCO_ST_UNUSED) && (p->state != SCO_ST_LISTENING) && (p->hci_handle == hci_handle)) { + btm_sco_flush_sco_data(xx); + + p->state = SCO_ST_UNUSED; +#if BTM_SCO_HCI_INCLUDED == TRUE + btm_cb.sco_cb.xmit_window_size += p->sent_not_acked; + /* avoid overflow */ + if (btm_cb.sco_cb.xmit_window_size > btm_cb.sco_cb.num_lm_sco_bufs) { + btm_cb.sco_cb.xmit_window_size = btm_cb.sco_cb.num_lm_sco_bufs; + } + p->sent_not_acked = 0; +#endif + p->hci_handle = BTM_INVALID_HCI_HANDLE; + p->rem_bd_known = FALSE; + p->esco.p_esco_cback = NULL; /* Deregister eSCO callback */ + (*p->p_disc_cb)(xx); + + return; + } + } +#endif +} + + +/******************************************************************************* +** +** Function btm_sco_acl_removed +** +** Description This function is called when an ACL connection is +** removed. If the BD address is NULL, it is assumed that +** the local device is down, and all SCO links are removed. +** If a specific BD address is passed, only SCO connections +** to that BD address are removed. +** +** Returns void +** +*******************************************************************************/ +void btm_sco_acl_removed (BD_ADDR bda) +{ +#if (BTM_MAX_SCO_LINKS>0) + tSCO_CONN *p = &btm_cb.sco_cb.sco_db[0]; + UINT16 xx; + + for (xx = 0; xx < BTM_MAX_SCO_LINKS; xx++, p++) { + if (p->state != SCO_ST_UNUSED) { + if ((!bda) || (!memcmp (p->esco.data.bd_addr, bda, BD_ADDR_LEN) && p->rem_bd_known)) { + btm_sco_flush_sco_data(xx); + + p->state = SCO_ST_UNUSED; + p->esco.p_esco_cback = NULL; /* Deregister eSCO callback */ + (*p->p_disc_cb)(xx); + } + } + } +#endif +} + + +/******************************************************************************* +** +** Function BTM_SetScoPacketTypes +** +** Description This function is called to set the packet types used for +** a specific SCO connection, +** +** Parameters pkt_types - One or more of the following +** BTM_SCO_PKT_TYPES_MASK_HV1 +** BTM_SCO_PKT_TYPES_MASK_HV2 +** BTM_SCO_PKT_TYPES_MASK_HV3 +** BTM_SCO_PKT_TYPES_MASK_EV3 +** BTM_SCO_PKT_TYPES_MASK_EV4 +** BTM_SCO_PKT_TYPES_MASK_EV5 +** BTM_SCO_PKT_TYPES_MASK_NO_2_EV3 +** BTM_SCO_PKT_TYPES_MASK_NO_3_EV3 +** BTM_SCO_PKT_TYPES_MASK_NO_2_EV5 +** BTM_SCO_PKT_TYPES_MASK_NO_3_EV5 +** +** BTM_SCO_LINK_ALL_MASK - enables all supported types +** +** Returns status of the operation +** +*******************************************************************************/ +tBTM_STATUS BTM_SetScoPacketTypes (UINT16 sco_inx, UINT16 pkt_types) +{ +#if (BTM_MAX_SCO_LINKS>0) + tBTM_CHG_ESCO_PARAMS parms; + tSCO_CONN *p; + + /* Validity check */ + if (sco_inx >= BTM_MAX_SCO_LINKS) { + return (BTM_UNKNOWN_ADDR); + } + + p = &btm_cb.sco_cb.sco_db[sco_inx]; + parms.packet_types = pkt_types; + + /* Keep the other parameters the same for SCO */ + parms.max_latency = p->esco.setup.max_latency; + parms.retrans_effort = p->esco.setup.retrans_effort; + + return (BTM_ChangeEScoLinkParms(sco_inx, &parms)); +#else + return (BTM_UNKNOWN_ADDR); +#endif +} + + +/******************************************************************************* +** +** Function BTM_ReadScoPacketTypes +** +** Description This function is read the packet types used for a specific +** SCO connection. +** +** Returns Packet types supported for the connection +** One or more of the following (bitmask): +** BTM_SCO_PKT_TYPES_MASK_HV1 +** BTM_SCO_PKT_TYPES_MASK_HV2 +** BTM_SCO_PKT_TYPES_MASK_HV3 +** BTM_SCO_PKT_TYPES_MASK_EV3 +** BTM_SCO_PKT_TYPES_MASK_EV4 +** BTM_SCO_PKT_TYPES_MASK_EV5 +** BTM_SCO_PKT_TYPES_MASK_NO_2_EV3 +** BTM_SCO_PKT_TYPES_MASK_NO_3_EV3 +** BTM_SCO_PKT_TYPES_MASK_NO_2_EV5 +** BTM_SCO_PKT_TYPES_MASK_NO_3_EV5 +** +*******************************************************************************/ +UINT16 BTM_ReadScoPacketTypes (UINT16 sco_inx) +{ +#if (BTM_MAX_SCO_LINKS>0) + tSCO_CONN *p = &btm_cb.sco_cb.sco_db[sco_inx]; + + /* Validity check */ + if ((sco_inx < BTM_MAX_SCO_LINKS) && (p->state == SCO_ST_CONNECTED)) { + return (p->esco.setup.packet_types); + } else { + return (0); + } +#else + return (0); +#endif +} + +/******************************************************************************* +** +** Function BTM_ReadScoDiscReason +** +** Description This function is returns the reason why an (e)SCO connection +** has been removed. It contains the value until read, or until +** another (e)SCO connection has disconnected. +** +** Returns HCI reason or BTM_INVALID_SCO_DISC_REASON if not set. +** +*******************************************************************************/ +UINT16 BTM_ReadScoDiscReason (void) +{ + UINT16 res = btm_cb.sco_cb.sco_disc_reason; + btm_cb.sco_cb.sco_disc_reason = BTM_INVALID_SCO_DISC_REASON; + return (res); +} + +/******************************************************************************* +** +** Function BTM_ReadDeviceScoPacketTypes +** +** Description This function is read the SCO packet types that +** the device supports. +** +** Returns Packet types supported by the device. +** One or more of the following (bitmask): +** BTM_SCO_PKT_TYPES_MASK_HV1 +** BTM_SCO_PKT_TYPES_MASK_HV2 +** BTM_SCO_PKT_TYPES_MASK_HV3 +** BTM_SCO_PKT_TYPES_MASK_EV3 +** BTM_SCO_PKT_TYPES_MASK_EV4 +** BTM_SCO_PKT_TYPES_MASK_EV5 +** BTM_SCO_PKT_TYPES_MASK_NO_2_EV3 +** BTM_SCO_PKT_TYPES_MASK_NO_3_EV3 +** BTM_SCO_PKT_TYPES_MASK_NO_2_EV5 +** BTM_SCO_PKT_TYPES_MASK_NO_3_EV5 +** +*******************************************************************************/ +UINT16 BTM_ReadDeviceScoPacketTypes (void) +{ + return (btm_cb.btm_sco_pkt_types_supported); +} + +/******************************************************************************* +** +** Function BTM_ReadScoHandle +** +** Description This function is used to read the HCI handle used for a specific +** SCO connection, +** +** Returns handle for the connection, or 0xFFFF if invalid SCO index. +** +*******************************************************************************/ +UINT16 BTM_ReadScoHandle (UINT16 sco_inx) +{ +#if (BTM_MAX_SCO_LINKS>0) + tSCO_CONN *p = &btm_cb.sco_cb.sco_db[sco_inx]; + + /* Validity check */ + if ((sco_inx < BTM_MAX_SCO_LINKS) && (p->state == SCO_ST_CONNECTED)) { + return (p->hci_handle); + } else { + return (BTM_INVALID_HCI_HANDLE); + } +#else + return (BTM_INVALID_HCI_HANDLE); +#endif +} + +/******************************************************************************* +** +** Function BTM_ReadScoBdAddr +** +** Description This function is read the remote BD Address for a specific +** SCO connection, +** +** Returns pointer to BD address or NULL if not known +** +*******************************************************************************/ +UINT8 *BTM_ReadScoBdAddr (UINT16 sco_inx) +{ +#if (BTM_MAX_SCO_LINKS>0) + tSCO_CONN *p = &btm_cb.sco_cb.sco_db[sco_inx]; + + /* Validity check */ + if ((sco_inx < BTM_MAX_SCO_LINKS) && (p->rem_bd_known)) { + return (p->esco.data.bd_addr); + } else { + return (NULL); + } +#else + return (NULL); +#endif +} + +/******************************************************************************* +** +** Function BTM_SetEScoMode +** +** Description This function sets up the negotiated parameters for SCO or +** eSCO, and sets as the default mode used for outgoing calls to +** BTM_CreateSco. It does not change any currently active (e)SCO links. +** Note: Incoming (e)SCO connections will always use packet types +** supported by the controller. If eSCO is not desired the +** feature should be disabled in the controller's feature mask. +** +** Returns BTM_SUCCESS if the successful. +** BTM_BUSY if there are one or more active (e)SCO links. +** +*******************************************************************************/ +tBTM_STATUS BTM_SetEScoMode (tBTM_SCO_TYPE sco_mode, tBTM_ESCO_PARAMS *p_parms) +{ + tSCO_CB *p_esco = &btm_cb.sco_cb; + tBTM_ESCO_PARAMS *p_def = &p_esco->def_esco_parms; + + if (p_esco->esco_supported) { + if (p_parms) { + if (sco_mode == BTM_LINK_TYPE_ESCO) { + *p_def = *p_parms; /* Save as the default parameters */ + } else { /* Load only the SCO packet types */ + p_def->packet_types = p_parms->packet_types; + p_def->tx_bw = BTM_64KBITS_RATE; + p_def->rx_bw = BTM_64KBITS_RATE; + p_def->max_latency = 0x000a; + p_def->voice_contfmt = 0x0060; + p_def->retrans_effort = 0; + + /* OR in any exception packet types */ + p_def->packet_types |= BTM_SCO_EXCEPTION_PKTS_MASK; + } + } + p_esco->desired_sco_mode = sco_mode; + BTM_TRACE_API("BTM_SetEScoMode -> mode %d", sco_mode); + } else { + p_esco->desired_sco_mode = BTM_LINK_TYPE_SCO; + p_def->packet_types &= BTM_SCO_LINK_ONLY_MASK; + p_def->retrans_effort = 0; + BTM_TRACE_API("BTM_SetEScoMode -> mode SCO (eSCO not supported)"); + } + + BTM_TRACE_DEBUG(" txbw 0x%08x, rxbw 0x%08x, max_lat 0x%04x, voice 0x%04x, pkt 0x%04x, rtx effort 0x%02x", + p_def->tx_bw, p_def->rx_bw, p_def->max_latency, + p_def->voice_contfmt, p_def->packet_types, + p_def->retrans_effort); + + return (BTM_SUCCESS); +} + + + +/******************************************************************************* +** +** Function BTM_RegForEScoEvts +** +** Description This function registers a SCO event callback with the +** specified instance. It should be used to received +** connection indication events and change of link parameter +** events. +** +** Returns BTM_SUCCESS if the successful. +** BTM_ILLEGAL_VALUE if there is an illegal sco_inx +** BTM_MODE_UNSUPPORTED if controller version is not BT1.2 or +** later or does not support eSCO. +** +*******************************************************************************/ +tBTM_STATUS BTM_RegForEScoEvts (UINT16 sco_inx, tBTM_ESCO_CBACK *p_esco_cback) +{ +#if (BTM_MAX_SCO_LINKS>0) + if (!btm_cb.sco_cb.esco_supported) { + btm_cb.sco_cb.sco_db[sco_inx].esco.p_esco_cback = NULL; + return (BTM_MODE_UNSUPPORTED); + } + + if (sco_inx < BTM_MAX_SCO_LINKS && + btm_cb.sco_cb.sco_db[sco_inx].state != SCO_ST_UNUSED) { + btm_cb.sco_cb.sco_db[sco_inx].esco.p_esco_cback = p_esco_cback; + return (BTM_SUCCESS); + } + return (BTM_ILLEGAL_VALUE); +#else + return (BTM_MODE_UNSUPPORTED); +#endif +} + +/******************************************************************************* +** +** Function BTM_ReadEScoLinkParms +** +** Description This function returns the current eSCO link parameters for +** the specified handle. This can be called anytime a connection +** is active, but is typically called after receiving the SCO +** opened callback. +** +** Note: If called over a 1.1 controller, only the packet types +** field has meaning. +** +** Returns BTM_SUCCESS if returned data is valid connection. +** BTM_WRONG_MODE if no connection with a peer device or bad sco_inx. +** +*******************************************************************************/ +tBTM_STATUS BTM_ReadEScoLinkParms (UINT16 sco_inx, tBTM_ESCO_DATA *p_parms) +{ +#if (BTM_MAX_SCO_LINKS>0) + UINT8 index; + + BTM_TRACE_API("BTM_ReadEScoLinkParms -> sco_inx 0x%04x", sco_inx); + + if (sco_inx < BTM_MAX_SCO_LINKS && + btm_cb.sco_cb.sco_db[sco_inx].state >= SCO_ST_CONNECTED) { + *p_parms = btm_cb.sco_cb.sco_db[sco_inx].esco.data; + return (BTM_SUCCESS); + } + + if (sco_inx == BTM_FIRST_ACTIVE_SCO_INDEX) { + for (index = 0; index < BTM_MAX_SCO_LINKS; index++) { + if (btm_cb.sco_cb.sco_db[index].state >= SCO_ST_CONNECTED) { + BTM_TRACE_API("BTM_ReadEScoLinkParms the first active SCO index is %d", index); + *p_parms = btm_cb.sco_cb.sco_db[index].esco.data; + return (BTM_SUCCESS); + } + } + } + +#endif + + BTM_TRACE_API("BTM_ReadEScoLinkParms cannot find the SCO index!"); + memset(p_parms, 0, sizeof(tBTM_ESCO_DATA)); + return (BTM_WRONG_MODE); +} + +/******************************************************************************* +** +** Function BTM_ChangeEScoLinkParms +** +** Description This function requests renegotiation of the parameters on +** the current eSCO Link. If any of the changes are accepted +** by the controllers, the BTM_ESCO_CHG_EVT event is sent in +** the tBTM_ESCO_CBACK function with the current settings of +** the link. The callback is registered through the call to +** BTM_SetEScoMode. +** +** Note: If called over a SCO link (including 1.1 controller), +** a change packet type request is sent out instead. +** +** Returns BTM_CMD_STARTED if command is successfully initiated. +** BTM_NO_RESOURCES - not enough resources to initiate command. +** BTM_WRONG_MODE if no connection with a peer device or bad sco_inx. +** +*******************************************************************************/ +tBTM_STATUS BTM_ChangeEScoLinkParms (UINT16 sco_inx, tBTM_CHG_ESCO_PARAMS *p_parms) +{ +#if (BTM_MAX_SCO_LINKS>0) + tBTM_ESCO_PARAMS *p_setup; + tSCO_CONN *p_sco; + UINT16 temp_pkt_types; + + /* Make sure sco handle is valid and on an active link */ + if (sco_inx >= BTM_MAX_SCO_LINKS || + btm_cb.sco_cb.sco_db[sco_inx].state != SCO_ST_CONNECTED) { + return (BTM_WRONG_MODE); + } + + p_sco = &btm_cb.sco_cb.sco_db[sco_inx]; + p_setup = &p_sco->esco.setup; + + /* If SCO connection OR eSCO not supported just send change packet types */ + if (p_sco->esco.data.link_type == BTM_LINK_TYPE_SCO || + !btm_cb.sco_cb.esco_supported) { + p_setup->packet_types = p_parms->packet_types & + (btm_cb.btm_sco_pkt_types_supported & BTM_SCO_LINK_ONLY_MASK); + + + BTM_TRACE_API("BTM_ChangeEScoLinkParms -> SCO Link for handle 0x%04x, pkt 0x%04x", + p_sco->hci_handle, p_setup->packet_types); + + if (!btsnd_hcic_change_conn_type (p_sco->hci_handle, + BTM_ESCO_2_SCO(p_setup->packet_types))) { + return (BTM_NO_RESOURCES); + } + } else { + temp_pkt_types = (p_parms->packet_types & BTM_SCO_SUPPORTED_PKTS_MASK & + btm_cb.btm_sco_pkt_types_supported); + + /* OR in any exception packet types */ + temp_pkt_types |= ((p_parms->packet_types & BTM_SCO_EXCEPTION_PKTS_MASK) | + (btm_cb.btm_sco_pkt_types_supported & BTM_SCO_EXCEPTION_PKTS_MASK)); + + BTM_TRACE_API("BTM_ChangeEScoLinkParms -> eSCO Link for handle 0x%04x", p_sco->hci_handle); + BTM_TRACE_API(" txbw 0x%x, rxbw 0x%x, lat 0x%x, voice 0x%x, retrans 0x%02x, pkt 0x%04x", + p_setup->tx_bw, p_setup->rx_bw, p_parms->max_latency, + p_setup->voice_contfmt, p_parms->retrans_effort, temp_pkt_types); + + /* When changing an existing link, only change latency, retrans, and pkts */ + if (!btsnd_hcic_setup_esco_conn(p_sco->hci_handle, p_setup->tx_bw, + p_setup->rx_bw, p_parms->max_latency, + p_setup->voice_contfmt, + p_parms->retrans_effort, + temp_pkt_types)) { + return (BTM_NO_RESOURCES); + } else { + p_parms->packet_types = temp_pkt_types; + } + } + + return (BTM_CMD_STARTED); +#else + return (BTM_WRONG_MODE); +#endif +} + +/******************************************************************************* +** +** Function BTM_EScoConnRsp +** +** Description This function is called upon receipt of an (e)SCO connection +** request event (BTM_ESCO_CONN_REQ_EVT) to accept or reject +** the request. Parameters used to negotiate eSCO links. +** If p_parms is NULL, then values set through BTM_SetEScoMode +** are used. +** If the link type of the incoming request is SCO, then only +** the tx_bw, max_latency, content format, and packet_types are +** valid. The hci_status parameter should be +** ([0x0] to accept, [0x0d..0x0f] to reject) +** +** +** Returns void +** +*******************************************************************************/ +void BTM_EScoConnRsp (UINT16 sco_inx, UINT8 hci_status, tBTM_ESCO_PARAMS *p_parms) +{ +#if (BTM_MAX_SCO_LINKS>0) + if (sco_inx < BTM_MAX_SCO_LINKS && + btm_cb.sco_cb.sco_db[sco_inx].state == SCO_ST_W4_CONN_RSP) { + btm_esco_conn_rsp(sco_inx, hci_status, + btm_cb.sco_cb.sco_db[sco_inx].esco.data.bd_addr, + p_parms); + } +#endif +} + +/******************************************************************************* +** +** Function btm_read_def_esco_mode +** +** Description This function copies the current default esco settings into +** the return buffer. +** +** Returns tBTM_SCO_TYPE +** +*******************************************************************************/ +tBTM_SCO_TYPE btm_read_def_esco_mode (tBTM_ESCO_PARAMS *p_parms) +{ +#if (BTM_MAX_SCO_LINKS>0) + *p_parms = btm_cb.sco_cb.def_esco_parms; + return btm_cb.sco_cb.desired_sco_mode; +#else + return BTM_LINK_TYPE_SCO; +#endif +} + +/******************************************************************************* +** +** Function btm_esco_proc_conn_chg +** +** Description This function is called by BTIF when an SCO connection +** is changed. +** +** Returns void +** +*******************************************************************************/ +void btm_esco_proc_conn_chg (UINT8 status, UINT16 handle, UINT8 tx_interval, + UINT8 retrans_window, UINT16 rx_pkt_len, + UINT16 tx_pkt_len) +{ +#if (BTM_MAX_SCO_LINKS>0) + tSCO_CONN *p = &btm_cb.sco_cb.sco_db[0]; + tBTM_CHG_ESCO_EVT_DATA data; + UINT16 xx; + + BTM_TRACE_EVENT("btm_esco_proc_conn_chg -> handle 0x%04x, status 0x%02x", + handle, status); + + for (xx = 0; xx < BTM_MAX_SCO_LINKS; xx++, p++) { + if (p->state == SCO_ST_CONNECTED && handle == p->hci_handle) { + /* If upper layer wants notification */ + if (p->esco.p_esco_cback) { + memcpy(data.bd_addr, p->esco.data.bd_addr, BD_ADDR_LEN); + data.hci_status = status; + data.sco_inx = xx; + data.rx_pkt_len = p->esco.data.rx_pkt_len = rx_pkt_len; + data.tx_pkt_len = p->esco.data.tx_pkt_len = tx_pkt_len; + data.tx_interval = p->esco.data.tx_interval = tx_interval; + data.retrans_window = p->esco.data.retrans_window = retrans_window; + + (*p->esco.p_esco_cback)(BTM_ESCO_CHG_EVT, + (tBTM_ESCO_EVT_DATA *)&data); + } + return; + } + } +#endif +} + +/******************************************************************************* +** +** Function btm_is_sco_active +** +** Description This function is called to see if a SCO handle is already in +** use. +** +** Returns BOOLEAN +** +*******************************************************************************/ +BOOLEAN btm_is_sco_active (UINT16 handle) +{ +#if (BTM_MAX_SCO_LINKS>0) + UINT16 xx; + tSCO_CONN *p = &btm_cb.sco_cb.sco_db[0]; + + for (xx = 0; xx < BTM_MAX_SCO_LINKS; xx++, p++) { + if (handle == p->hci_handle && p->state == SCO_ST_CONNECTED) { + return (TRUE); + } + } +#endif + return (FALSE); +} + +/******************************************************************************* +** +** Function BTM_GetNumScoLinks +** +** Description This function returns the number of active sco links. +** +** Returns UINT8 +** +*******************************************************************************/ +UINT8 BTM_GetNumScoLinks (void) +{ +#if (BTM_MAX_SCO_LINKS>0) + tSCO_CONN *p = &btm_cb.sco_cb.sco_db[0]; + UINT16 xx; + UINT8 num_scos = 0; + + for (xx = 0; xx < BTM_MAX_SCO_LINKS; xx++, p++) { + switch (p->state) { + case SCO_ST_W4_CONN_RSP: + case SCO_ST_CONNECTING: + case SCO_ST_CONNECTED: + case SCO_ST_DISCONNECTING: + case SCO_ST_PEND_UNPARK: + num_scos++; + } + } + return (num_scos); +#else + return (0); +#endif +} + + +/******************************************************************************* +** +** Function btm_is_sco_active_by_bdaddr +** +** Description This function is called to see if a SCO active to a bd address. +** +** Returns BOOLEAN +** +*******************************************************************************/ +BOOLEAN btm_is_sco_active_by_bdaddr (BD_ADDR remote_bda) +{ +#if (BTM_MAX_SCO_LINKS>0) + UINT8 xx; + tSCO_CONN *p = &btm_cb.sco_cb.sco_db[0]; + + /* If any SCO is being established to the remote BD address, refuse this */ + for (xx = 0; xx < BTM_MAX_SCO_LINKS; xx++, p++) { + if ((!memcmp (p->esco.data.bd_addr, remote_bda, BD_ADDR_LEN)) && (p->state == SCO_ST_CONNECTED)) { + return (TRUE); + } + } +#endif + return (FALSE); +} +#else /* SCO_EXCLUDED == TRUE (Link in stubs) */ + +tBTM_STATUS BTM_CreateSco (BD_ADDR remote_bda, BOOLEAN is_orig, + UINT16 pkt_types, UINT16 *p_sco_inx, + tBTM_SCO_CB *p_conn_cb, + tBTM_SCO_CB *p_disc_cb) +{ + return (BTM_NO_RESOURCES); +} +tBTM_STATUS BTM_RemoveSco (UINT16 sco_inx) +{ + return (BTM_NO_RESOURCES); +} +tBTM_STATUS BTM_SetScoPacketTypes (UINT16 sco_inx, UINT16 pkt_types) +{ + return (BTM_NO_RESOURCES); +} +UINT16 BTM_ReadScoPacketTypes (UINT16 sco_inx) +{ + return (0); +} +UINT16 BTM_ReadDeviceScoPacketTypes (void) +{ + return (0); +} +UINT16 BTM_ReadScoHandle (UINT16 sco_inx) +{ + return (BTM_INVALID_HCI_HANDLE); +} +UINT8 *BTM_ReadScoBdAddr(UINT16 sco_inx) +{ + return ((UINT8 *) NULL); +} +UINT16 BTM_ReadScoDiscReason (void) +{ + return (BTM_INVALID_SCO_DISC_REASON); +} +tBTM_STATUS BTM_SetEScoMode (tBTM_SCO_TYPE sco_mode, tBTM_ESCO_PARAMS *p_parms) +{ + return (BTM_MODE_UNSUPPORTED); +} +tBTM_STATUS BTM_RegForEScoEvts (UINT16 sco_inx, tBTM_ESCO_CBACK *p_esco_cback) +{ + return (BTM_ILLEGAL_VALUE); +} +tBTM_STATUS BTM_ReadEScoLinkParms (UINT16 sco_inx, tBTM_ESCO_DATA *p_parms) +{ + return (BTM_MODE_UNSUPPORTED); +} +tBTM_STATUS BTM_ChangeEScoLinkParms (UINT16 sco_inx, tBTM_CHG_ESCO_PARAMS *p_parms) +{ + return (BTM_MODE_UNSUPPORTED); +} +void BTM_EScoConnRsp (UINT16 sco_inx, UINT8 hci_status, tBTM_ESCO_PARAMS *p_parms) {} +UINT8 BTM_GetNumScoLinks (void) +{ + return (0); +} + +#endif /* If SCO is being used */ diff --git a/lib/bt/host/bluedroid/stack/btm/btm_sec.c b/lib/bt/host/bluedroid/stack/btm/btm_sec.c new file mode 100644 index 00000000..7d06a45e --- /dev/null +++ b/lib/bt/host/bluedroid/stack/btm/btm_sec.c @@ -0,0 +1,6382 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains functions for the Bluetooth Security Manager + * + ******************************************************************************/ + +//#define LOG_TAG "bt_btm_sec" + +#include +#include + +#include "stack/bt_types.h" +#include "device/controller.h" +#include "stack/hcimsgs.h" +#include "stack/btu.h" +#include "btm_int.h" +#include "l2c_int.h" +#include "osi/fixed_queue.h" +#include "osi/alarm.h" +#include "stack/btm_ble_api.h" + +#if (BT_USE_TRACES == TRUE && BT_TRACE_VERBOSE == FALSE) +/* needed for sprintf() */ +#include +#endif + +#if BLE_INCLUDED == TRUE +#include "gatt_int.h" +#endif + +#define BTM_SEC_MAX_COLLISION_DELAY (5000) + +#ifdef APPL_AUTH_WRITE_EXCEPTION +BOOLEAN (APPL_AUTH_WRITE_EXCEPTION)(BD_ADDR bd_addr); +#endif + + +/******************************************************************************** +** L O C A L F U N C T I O N P R O T O T Y P E S * +*********************************************************************************/ +#if (SMP_INCLUDED == TRUE) +static tBTM_SEC_SERV_REC *btm_sec_find_next_serv (tBTM_SEC_SERV_REC *p_cur); +static tBTM_SEC_SERV_REC *btm_sec_find_mx_serv (UINT8 is_originator, UINT16 psm, + UINT32 mx_proto_id, + UINT32 mx_chan_id); +static tBTM_STATUS btm_sec_execute_procedure (tBTM_SEC_DEV_REC *p_dev_rec); +static BOOLEAN btm_sec_start_get_name (tBTM_SEC_DEV_REC *p_dev_rec); +static BOOLEAN btm_sec_start_authentication (tBTM_SEC_DEV_REC *p_dev_rec); +static BOOLEAN btm_sec_start_encryption (tBTM_SEC_DEV_REC *p_dev_rec); +static void btm_sec_collision_timeout (TIMER_LIST_ENT *p_tle); +static void btm_restore_mode(void); +static void btm_sec_pairing_timeout (TIMER_LIST_ENT *p_tle); +static tBTM_STATUS btm_sec_dd_create_conn (tBTM_SEC_DEV_REC *p_dev_rec); +static void btm_sec_change_pairing_state (tBTM_PAIRING_STATE new_state); +#endif ///SMP_INCLUDED == TRUE +#if (BT_USE_TRACES == TRUE && SMP_INCLUDED == TRUE) +static char *btm_pair_state_descr (tBTM_PAIRING_STATE state); +#endif +#if (SMP_INCLUDED == TRUE) +static void btm_sec_check_pending_reqs(void); +static BOOLEAN btm_sec_queue_mx_request (BD_ADDR bd_addr, UINT16 psm, BOOLEAN is_orig, + UINT32 mx_proto_id, UINT32 mx_chan_id, + tBTM_SEC_CALLBACK *p_callback, void *p_ref_data); +static void btm_sec_bond_cancel_complete (void); +static void btm_send_link_key_notif (tBTM_SEC_DEV_REC *p_dev_rec); +static BOOLEAN btm_sec_check_prefetch_pin (tBTM_SEC_DEV_REC *p_dev_rec); +static UINT8 btm_sec_start_authorization (tBTM_SEC_DEV_REC *p_dev_rec); +#endif ///SMP_INCLUDED == TRUE +BOOLEAN btm_sec_are_all_trusted(UINT32 p_mask[]); +#if (SMP_INCLUDED == TRUE) +static tBTM_STATUS btm_sec_send_hci_disconnect (tBTM_SEC_DEV_REC *p_dev_rec, UINT8 reason, UINT16 conn_handle); +#endif ///SMP_INCLUDED == TRUE +UINT8 btm_sec_start_role_switch (tBTM_SEC_DEV_REC *p_dev_rec); +tBTM_SEC_DEV_REC *btm_sec_find_dev_by_sec_state (UINT8 state); + +static BOOLEAN btm_sec_set_security_level ( CONNECTION_TYPE conn_type, const char *p_name, UINT8 service_id, + UINT16 sec_level, UINT16 psm, UINT32 mx_proto_id, + UINT32 mx_chan_id); +#if (SMP_INCLUDED == TRUE) +static BOOLEAN btm_dev_authenticated(tBTM_SEC_DEV_REC *p_dev_rec); +static BOOLEAN btm_dev_encrypted(tBTM_SEC_DEV_REC *p_dev_rec); +static BOOLEAN btm_dev_authorized(tBTM_SEC_DEV_REC *p_dev_rec); +static BOOLEAN btm_serv_trusted(tBTM_SEC_DEV_REC *p_dev_rec, tBTM_SEC_SERV_REC *p_serv_rec); +static BOOLEAN btm_sec_is_serv_level0 (UINT16 psm); +static UINT16 btm_sec_set_serv_level4_flags (UINT16 cur_security, BOOLEAN is_originator); + +static BOOLEAN btm_sec_queue_encrypt_request (BD_ADDR bd_addr, tBT_TRANSPORT transport, + tBTM_SEC_CALLBACK *p_callback, void *p_ref_data); +static void btm_sec_check_pending_enc_req (tBTM_SEC_DEV_REC *p_dev_rec, tBT_TRANSPORT transport, + UINT8 encr_enable); +static BOOLEAN btm_sec_use_smp_br_chnl(tBTM_SEC_DEV_REC *p_dev_rec); +static BOOLEAN btm_sec_is_master(tBTM_SEC_DEV_REC *p_dev_rec); +#endif ///SMP_INCLUDED == TRUE + +/* TRUE - authenticated link key is possible */ +static const BOOLEAN btm_sec_io_map [BTM_IO_CAP_MAX][BTM_IO_CAP_MAX] = { + /* OUT, IO, IN, NONE */ + /* OUT */ {FALSE, FALSE, TRUE, FALSE}, + /* IO */ {FALSE, TRUE, TRUE, FALSE}, + /* IN */ {TRUE, TRUE, TRUE, FALSE}, + /* NONE */ {FALSE, FALSE, FALSE, FALSE} +}; +/* BTM_IO_CAP_OUT 0 DisplayOnly */ +/* BTM_IO_CAP_IO 1 DisplayYesNo */ +/* BTM_IO_CAP_IN 2 KeyboardOnly */ +/* BTM_IO_CAP_NONE 3 NoInputNoOutput */ + +/******************************************************************************* +** +** Function btm_dev_authenticated +** +** Description check device is authenticated +** +** Returns BOOLEAN TRUE or FALSE +** +*******************************************************************************/ +#if (SMP_INCLUDED == TRUE) +static BOOLEAN btm_dev_authenticated (tBTM_SEC_DEV_REC *p_dev_rec) +{ + if (p_dev_rec->sec_flags & BTM_SEC_AUTHENTICATED) { + return (TRUE); + } + return (FALSE); +} + +/******************************************************************************* +** +** Function btm_dev_encrypted +** +** Description check device is encrypted +** +** Returns BOOLEAN TRUE or FALSE +** +*******************************************************************************/ +static BOOLEAN btm_dev_encrypted (tBTM_SEC_DEV_REC *p_dev_rec) +{ + if (p_dev_rec->sec_flags & BTM_SEC_ENCRYPTED) { + return (TRUE); + } + return (FALSE); +} + +/******************************************************************************* +** +** Function btm_dev_authorized +** +** Description check device is authorized +** +** Returns BOOLEAN TRUE or FALSE +** +*******************************************************************************/ +static BOOLEAN btm_dev_authorized (tBTM_SEC_DEV_REC *p_dev_rec) +{ + if (p_dev_rec->sec_flags & BTM_SEC_AUTHORIZED) { + return (TRUE); + } + return (FALSE); +} + +/******************************************************************************* +** +** Function btm_dev_16_digit_authenticated +** +** Description check device is authenticated by using 16 digit pin or MITM +** +** Returns BOOLEAN TRUE or FALSE +** +*******************************************************************************/ +static BOOLEAN btm_dev_16_digit_authenticated(tBTM_SEC_DEV_REC *p_dev_rec) +{ + // BTM_SEC_16_DIGIT_PIN_AUTHED is set if MITM or 16 digit pin is used + if (p_dev_rec->sec_flags & BTM_SEC_16_DIGIT_PIN_AUTHED) { + return (TRUE); + } + return (FALSE); +} +#endif ///SMP_INCLUDED == TRUE + +/******************************************************************************* +** +** Function btm_serv_trusted +** +** Description check service is trusted +** +** Returns BOOLEAN TRUE or FALSE +** +*******************************************************************************/ +#if (SMP_INCLUDED == TRUE) +static BOOLEAN btm_serv_trusted(tBTM_SEC_DEV_REC *p_dev_rec, tBTM_SEC_SERV_REC *p_serv_rec) +{ + if (BTM_SEC_IS_SERVICE_TRUSTED(p_dev_rec->trusted_mask, p_serv_rec->service_id)) { + return (TRUE); + } + return (FALSE); +} +#endif ///SMP_INCLUDED == TRUE +/******************************************************************************* +** +** Function BTM_SecRegister +** +** Description Application manager calls this function to register for +** security services. There can be one and only one application +** saving link keys. BTM allows only first registration. +** +** Returns TRUE if registered OK, else FALSE +** +*******************************************************************************/ +BOOLEAN BTM_SecRegister(tBTM_APPL_INFO *p_cb_info) +{ +#if BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE + BT_OCTET16 temp_value = {0}; +#endif + + BTM_TRACE_EVENT("%s application registered\n", __func__); + +#if BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE + BTM_TRACE_DEBUG("%s p_cb_info->p_le_callback == 0x%p\n", __func__, p_cb_info->p_le_callback); + if (p_cb_info->p_le_callback) { + BTM_TRACE_EVENT("%s SMP_Register( btm_proc_smp_cback )\n", __func__); + SMP_Register(btm_proc_smp_cback); + /* if no IR is loaded, need to regenerate all the keys */ + if (memcmp(btm_cb.devcb.id_keys.ir, &temp_value, sizeof(BT_OCTET16)) == 0) { + btm_ble_reset_id(); + } +#if (CONTROLLER_RPA_LIST_ENABLE == TRUE) + else { + btm_ble_add_default_entry_to_resolving_list(); + } +#endif + } else { + BTM_TRACE_WARNING("%s p_cb_info->p_le_callback == NULL\n", __func__); + } +#endif + + btm_cb.api = *p_cb_info; +#if BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE + BTM_TRACE_DEBUG("%s btm_cb.api.p_le_callback = 0x%p\n", __func__, btm_cb.api.p_le_callback); +#endif + BTM_TRACE_EVENT("%s application registered\n", __func__); + return (TRUE); +} + +/******************************************************************************* +** +** Function BTM_SecRegisterLinkKeyNotificationCallback +** +** Description Application manager calls this function to register for +** link key notification. When there is nobody registered +** we should avoid changing link key +** +** Returns TRUE if registered OK, else FALSE +** +*******************************************************************************/ +BOOLEAN BTM_SecRegisterLinkKeyNotificationCallback (tBTM_LINK_KEY_CALLBACK *p_callback) +{ + btm_cb.api.p_link_key_callback = p_callback; + return TRUE; +} + +/******************************************************************************* +** +** Function BTM_SecAddRmtNameNotifyCallback +** +** Description Any profile can register to be notified when name of the +** remote device is resolved. +** +** Returns TRUE if registered OK, else FALSE +** +*******************************************************************************/ +#if (SMP_INCLUDED == TRUE) || (CLASSIC_BT_INCLUDED == TRUE) +BOOLEAN BTM_SecAddRmtNameNotifyCallback (tBTM_RMT_NAME_CALLBACK *p_callback) +{ + int i; + + for (i = 0; i < BTM_SEC_MAX_RMT_NAME_CALLBACKS; i++) { + if (btm_cb.p_rmt_name_callback[i] == NULL) { + btm_cb.p_rmt_name_callback[i] = p_callback; + return (TRUE); + } + } + return (FALSE); +} +#endif ///SMP_INCLUDED == TRUE + +/******************************************************************************* +** +** Function BTM_SecDeleteRmtNameNotifyCallback +** +** Description Any profile can deregister notification when a new Link Key +** is generated per connection. +** +** Returns TRUE if OK, else FALSE +** +*******************************************************************************/ +#if (SMP_INCLUDED == TRUE) || (CLASSIC_BT_INCLUDED == TRUE) +BOOLEAN BTM_SecDeleteRmtNameNotifyCallback (tBTM_RMT_NAME_CALLBACK *p_callback) +{ + int i; + + for (i = 0; i < BTM_SEC_MAX_RMT_NAME_CALLBACKS; i++) { + if (btm_cb.p_rmt_name_callback[i] == p_callback) { + btm_cb.p_rmt_name_callback[i] = NULL; + return (TRUE); + } + } + return (FALSE); +} +#endif ///SMP_INCLUDED == TRUE + +/******************************************************************************* +** +** Function BTM_GetSecurityFlags +** +** Description Get security flags for the device +** +** Returns BOOLEAN TRUE or FALSE is device found +** +*******************************************************************************/ +BOOLEAN BTM_GetSecurityFlags (BD_ADDR bd_addr, UINT8 *p_sec_flags) +{ + tBTM_SEC_DEV_REC *p_dev_rec; + + if ((p_dev_rec = btm_find_dev (bd_addr)) != NULL) { + *p_sec_flags = (UINT8) p_dev_rec->sec_flags; + return (TRUE); + } + BTM_TRACE_ERROR ("BTM_GetSecurityFlags false"); + return (FALSE); +} + +/******************************************************************************* +** +** Function BTM_GetSecurityFlagsByTransport +** +** Description Get security flags for the device on a particular transport +** +** Returns BOOLEAN TRUE or FALSE is device found +** +*******************************************************************************/ +BOOLEAN BTM_GetSecurityFlagsByTransport (BD_ADDR bd_addr, UINT8 *p_sec_flags, + tBT_TRANSPORT transport) +{ + tBTM_SEC_DEV_REC *p_dev_rec; + + if ((p_dev_rec = btm_find_dev (bd_addr)) != NULL) { + if (transport == BT_TRANSPORT_BR_EDR) { + *p_sec_flags = (UINT8) p_dev_rec->sec_flags; + } else { + *p_sec_flags = (UINT8) (p_dev_rec->sec_flags >> 8); + } + + return (TRUE); + } + BTM_TRACE_ERROR ("BTM_GetSecurityFlags false\n"); + return (FALSE); +} + +#if (CLASSIC_BT_INCLUDED == TRUE) +/******************************************************************************* +** +** Function BTM_SetPinType +** +** Description Set PIN type for the device. +** +** Returns void +** +*******************************************************************************/ +void BTM_SetPinType (UINT8 pin_type, PIN_CODE pin_code, UINT8 pin_code_len) +{ + BTM_TRACE_API ("BTM_SetPinType: pin type %d [variable-0, fixed-1], code %s, length %d\n", + pin_type, (char *) pin_code, pin_code_len); + + /* If device is not up security mode will be set as a part of startup */ + if ( (btm_cb.cfg.pin_type != pin_type) + && controller_get_interface()->get_is_ready() ) { + btsnd_hcic_write_pin_type (pin_type); + } + + btm_cb.cfg.pin_type = pin_type; + btm_cb.cfg.pin_code_len = pin_code_len; + memcpy (btm_cb.cfg.pin_code, pin_code, pin_code_len); +} +#endif ///CLASSIC_BT_INCLUDED == TRUE + +/******************************************************************************* +** +** Function BTM_SetPairableMode +** +** Description Enable or disable pairing +** +** Parameters allow_pairing - (TRUE or FALSE) whether or not the device +** allows pairing. +** connect_only_paired - (TRUE or FALSE) whether or not to +** only allow paired devices to connect. +** +** Returns void +** +*******************************************************************************/ +void BTM_SetPairableMode (BOOLEAN allow_pairing, BOOLEAN connect_only_paired) +{ + BTM_TRACE_API ("BTM_SetPairableMode() allow_pairing: %u connect_only_paired: %u\n", allow_pairing, connect_only_paired); + + btm_cb.pairing_disabled = !allow_pairing; + btm_cb.connect_only_paired = connect_only_paired; +} + +/******************************************************************************* +** +** Function BTM_SetSecureConnectionsOnly +** +** Description Enable or disable default treatment for Mode 4 Level 0 services +** +** Parameter secure_connections_only_mode - (TRUE or FALSE) whether or not the device +** TRUE means that the device should treat Mode 4 Level 0 services as +** services of other levels. (Secure_connections_only_mode) +** FALSE means that the device should provide default treatment for +** Mode 4 Level 0 services. +** +** Returns void +** +*******************************************************************************/ +void BTM_SetSecureConnectionsOnly (BOOLEAN secure_connections_only_mode) +{ + BTM_TRACE_API("%s: Mode : %u\n", __FUNCTION__, + secure_connections_only_mode); + + btm_cb.devcb.secure_connections_only = secure_connections_only_mode; + btm_cb.security_mode = BTM_SEC_MODE_SC; +} +#define BTM_NO_AVAIL_SEC_SERVICES ((UINT16) 0xffff) + +/******************************************************************************* +** +** Function BTM_SetSecurityLevel +** +** Description Register service security level with Security Manager +** +** Parameters: is_originator - TRUE if originating the connection, FALSE if not +** p_name - Name of the service relevant only if +** authorization will show this name to user. ignored +** if BTM_SEC_SERVICE_NAME_LEN is 0. +** service_id - service ID for the service passed to authorization callback +** sec_level - bit mask of the security features +** psm - L2CAP PSM +** mx_proto_id - protocol ID of multiplexing proto below +** mx_chan_id - channel ID of multiplexing proto below +** +** Returns TRUE if registered OK, else FALSE +** +*******************************************************************************/ +BOOLEAN BTM_SetSecurityLevel (BOOLEAN is_originator, const char *p_name, UINT8 service_id, + UINT16 sec_level, UINT16 psm, UINT32 mx_proto_id, + UINT32 mx_chan_id) +{ +#if (L2CAP_UCD_INCLUDED == TRUE) + CONNECTION_TYPE conn_type; + + if (is_originator) { + conn_type = CONN_ORIENT_ORIG; + } else { + conn_type = CONN_ORIENT_TERM; + } + + return (btm_sec_set_security_level (conn_type, p_name, service_id, + sec_level, psm, mx_proto_id, mx_chan_id)); +#else + return (btm_sec_set_security_level (is_originator, p_name, service_id, + sec_level, psm, mx_proto_id, mx_chan_id)); +#endif +} + +/******************************************************************************* +** +** Function btm_sec_set_security_level +** +** Description Register service security level with Security Manager +** +** Parameters: conn_type - TRUE if originating the connection, FALSE if not +** p_name - Name of the service relevant only if +** authorization will show this name to user. ignored +** if BTM_SEC_SERVICE_NAME_LEN is 0. +** service_id - service ID for the service passed to authorization callback +** sec_level - bit mask of the security features +** psm - L2CAP PSM +** mx_proto_id - protocol ID of multiplexing proto below +** mx_chan_id - channel ID of multiplexing proto below +** +** Returns TRUE if registered OK, else FALSE +** +*******************************************************************************/ +static BOOLEAN btm_sec_set_security_level (CONNECTION_TYPE conn_type, const char *p_name, UINT8 service_id, + UINT16 sec_level, UINT16 psm, UINT32 mx_proto_id, + UINT32 mx_chan_id) +{ +#if (SMP_INCLUDED == TRUE) + tBTM_SEC_SERV_REC *p_srec; + UINT16 index; + UINT16 first_unused_record = BTM_NO_AVAIL_SEC_SERVICES; + BOOLEAN record_allocated = FALSE; + BOOLEAN is_originator; +#if (L2CAP_UCD_INCLUDED == TRUE) + BOOLEAN is_ucd; + + if (conn_type & CONNECTION_TYPE_ORIG_MASK) { + is_originator = TRUE; + } else { + is_originator = FALSE; + } + + if (conn_type & CONNECTION_TYPE_CONNLESS_MASK ) { + is_ucd = TRUE; + } else { + is_ucd = FALSE; + } +#else + is_originator = conn_type; +#endif + + BTM_TRACE_API("%s : sec: 0x%x\n", __func__, sec_level); + + /* See if the record can be reused (same service name, psm, mx_proto_id, + service_id, and mx_chan_id), or obtain the next unused record */ + + p_srec = &btm_cb.sec_serv_rec[0]; + + + for (index = 0; index < BTM_SEC_MAX_SERVICE_RECORDS; index++, p_srec++) { + /* Check if there is already a record for this service */ + if (p_srec->security_flags & BTM_SEC_IN_USE) { +#if BTM_SEC_SERVICE_NAME_LEN > 0 + if (p_srec->psm == psm && + p_srec->mx_proto_id == mx_proto_id && + service_id == p_srec->service_id && + (!strncmp (p_name, (char *) p_srec->orig_service_name, + BTM_SEC_SERVICE_NAME_LEN) || + !strncmp (p_name, (char *) p_srec->term_service_name, + BTM_SEC_SERVICE_NAME_LEN))) +#else + if (p_srec->psm == psm && + p_srec->mx_proto_id == mx_proto_id && + service_id == p_srec->service_id) +#endif + { + record_allocated = TRUE; + break; + } + } + /* Mark the first available service record */ + else if (!record_allocated) { + memset (p_srec, 0, sizeof(tBTM_SEC_SERV_REC)); + record_allocated = TRUE; + first_unused_record = index; + } + } + + if (!record_allocated) { + BTM_TRACE_WARNING("BTM_SEC_REG: Out of Service Records (%d)\n", BTM_SEC_MAX_SERVICE_RECORDS); + return (record_allocated); + } + + /* Process the request if service record is valid */ + /* If a duplicate service wasn't found, use the first available */ + if (index >= BTM_SEC_MAX_SERVICE_RECORDS) { + index = first_unused_record; + p_srec = &btm_cb.sec_serv_rec[index]; + } + + p_srec->psm = psm; + p_srec->service_id = service_id; + p_srec->mx_proto_id = mx_proto_id; + + if (is_originator) { + p_srec->orig_mx_chan_id = mx_chan_id; +#if BTM_SEC_SERVICE_NAME_LEN > 0 + BCM_STRNCPY_S ((char *)p_srec->orig_service_name, p_name, BTM_SEC_SERVICE_NAME_LEN); + p_srec->orig_service_name[BTM_SEC_SERVICE_NAME_LEN] = '\0'; +#endif + /* clear out the old setting, just in case it exists */ +#if (L2CAP_UCD_INCLUDED == TRUE) + if ( is_ucd ) { + p_srec->ucd_security_flags &= + ~(BTM_SEC_OUT_AUTHORIZE | BTM_SEC_OUT_ENCRYPT | BTM_SEC_OUT_AUTHENTICATE | BTM_SEC_OUT_MITM | + BTM_SEC_FORCE_MASTER | BTM_SEC_ATTEMPT_MASTER | BTM_SEC_FORCE_SLAVE | BTM_SEC_ATTEMPT_SLAVE); + } else +#endif + { + p_srec->security_flags &= + ~(BTM_SEC_OUT_AUTHORIZE | BTM_SEC_OUT_ENCRYPT | BTM_SEC_OUT_AUTHENTICATE | BTM_SEC_OUT_MITM | + BTM_SEC_FORCE_MASTER | BTM_SEC_ATTEMPT_MASTER | BTM_SEC_FORCE_SLAVE | BTM_SEC_ATTEMPT_SLAVE); + } + + /* Parameter validation. Originator should not set requirements for incoming connections */ + sec_level &= ~(BTM_SEC_IN_AUTHORIZE | BTM_SEC_IN_ENCRYPT | BTM_SEC_IN_AUTHENTICATE + | BTM_SEC_IN_MITM | BTM_SEC_IN_MIN_16_DIGIT_PIN ); + + if (btm_cb.security_mode == BTM_SEC_MODE_SP || + btm_cb.security_mode == BTM_SEC_MODE_SP_DEBUG || + btm_cb.security_mode == BTM_SEC_MODE_SC) { + if (sec_level & BTM_SEC_OUT_AUTHENTICATE) { + sec_level |= BTM_SEC_OUT_MITM; + } + } + + /* Make sure the authenticate bit is set, when encrypt bit is set */ + if (sec_level & BTM_SEC_OUT_ENCRYPT) { + sec_level |= BTM_SEC_OUT_AUTHENTICATE; + } + + /* outgoing connections usually set the security level right before + * the connection is initiated. + * set it to be the outgoing service */ +#if (L2CAP_UCD_INCLUDED == TRUE) + if ( is_ucd == FALSE ) +#endif + { + btm_cb.p_out_serv = p_srec; + } + } else { + p_srec->term_mx_chan_id = mx_chan_id; +#if BTM_SEC_SERVICE_NAME_LEN > 0 + BCM_STRNCPY_S ((char *)p_srec->term_service_name, p_name, BTM_SEC_SERVICE_NAME_LEN); + p_srec->term_service_name[BTM_SEC_SERVICE_NAME_LEN] = '\0'; +#endif + /* clear out the old setting, just in case it exists */ +#if (L2CAP_UCD_INCLUDED == TRUE) + if ( is_ucd ) { + p_srec->ucd_security_flags &= + ~(BTM_SEC_IN_AUTHORIZE | BTM_SEC_IN_ENCRYPT | BTM_SEC_IN_AUTHENTICATE | BTM_SEC_IN_MITM | + BTM_SEC_FORCE_MASTER | BTM_SEC_ATTEMPT_MASTER | BTM_SEC_FORCE_SLAVE | BTM_SEC_ATTEMPT_SLAVE + | BTM_SEC_IN_MIN_16_DIGIT_PIN); + } else +#endif + { + p_srec->security_flags &= + ~(BTM_SEC_IN_AUTHORIZE | BTM_SEC_IN_ENCRYPT | BTM_SEC_IN_AUTHENTICATE | BTM_SEC_IN_MITM | + BTM_SEC_FORCE_MASTER | BTM_SEC_ATTEMPT_MASTER | BTM_SEC_FORCE_SLAVE | BTM_SEC_ATTEMPT_SLAVE + | BTM_SEC_IN_MIN_16_DIGIT_PIN); + } + + /* Parameter validation. Acceptor should not set requirements for outgoing connections */ + sec_level &= ~(BTM_SEC_OUT_AUTHORIZE | BTM_SEC_OUT_ENCRYPT | BTM_SEC_OUT_AUTHENTICATE | BTM_SEC_OUT_MITM); + + if (btm_cb.security_mode == BTM_SEC_MODE_SP || + btm_cb.security_mode == BTM_SEC_MODE_SP_DEBUG || + btm_cb.security_mode == BTM_SEC_MODE_SC) { + if (sec_level & BTM_SEC_IN_AUTHENTICATE) { + sec_level |= BTM_SEC_IN_MITM; + } + } + + /* Make sure the authenticate bit is set, when encrypt bit is set */ + if (sec_level & BTM_SEC_IN_ENCRYPT) { + sec_level |= BTM_SEC_IN_AUTHENTICATE; + } + } + +#if (L2CAP_UCD_INCLUDED == TRUE) + if ( is_ucd ) { + p_srec->security_flags |= (UINT16)(BTM_SEC_IN_USE); + p_srec->ucd_security_flags |= (UINT16)(sec_level | BTM_SEC_IN_USE); + } else { + p_srec->security_flags |= (UINT16)(sec_level | BTM_SEC_IN_USE); + } + + BTM_TRACE_API("BTM_SEC_REG[%d]: id %d, conn_type 0x%x, psm 0x%04x, proto_id %d, chan_id %d\n", + index, service_id, conn_type, psm, mx_proto_id, mx_chan_id); + + BTM_TRACE_API(" : security_flags: 0x%04x, ucd_security_flags: 0x%04x\n", + p_srec->security_flags, p_srec->ucd_security_flags); + +#if BTM_SEC_SERVICE_NAME_LEN > 0 + BTM_TRACE_API(" : service name [%s] (up to %d chars saved)\n", + p_name, BTM_SEC_SERVICE_NAME_LEN); +#endif +#else + p_srec->security_flags |= (UINT16)(sec_level | BTM_SEC_IN_USE); + + BTM_TRACE_API("BTM_SEC_REG[%d]: id %d, is_orig %d, psm 0x%04x, proto_id %d, chan_id %d\n", + index, service_id, is_originator, psm, mx_proto_id, mx_chan_id); + +#if BTM_SEC_SERVICE_NAME_LEN > 0 + BTM_TRACE_API(" : sec: 0x%x, service name [%s] (up to %d chars saved)\n", + p_srec->security_flags, p_name, BTM_SEC_SERVICE_NAME_LEN); +#endif +#endif + + + return (record_allocated); +#else + return FALSE; +#endif ///SMP_INCLUDED == TRUE +} + +/******************************************************************************* +** +** Function BTM_SecClrService +** +** Description Removes specified service record(s) from the security database. +** All service records with the specified name are removed. +** Typically used only by devices with limited RAM so that it can +** reuse an old security service record. +** +** Note: Unpredictable results may occur if a service is cleared +** that is still in use by an application/profile. +** +** Parameters Service ID - Id of the service to remove. ('0' removes all service +** records (except SDP). +** +** Returns Number of records that were freed. +** +*******************************************************************************/ +#if (SDP_INCLUDED == TRUE) +UINT8 BTM_SecClrService (UINT8 service_id) +{ + tBTM_SEC_SERV_REC *p_srec = &btm_cb.sec_serv_rec[0]; + UINT8 num_freed = 0; + int i; + + for (i = 0; i < BTM_SEC_MAX_SERVICE_RECORDS; i++, p_srec++) { + /* Delete services with specified name (if in use and not SDP) */ + if ((p_srec->security_flags & BTM_SEC_IN_USE) && (p_srec->psm != BT_PSM_SDP) && + (!service_id || (service_id == p_srec->service_id))) { + BTM_TRACE_API("BTM_SEC_CLR[%d]: id %d\n", i, service_id); + p_srec->security_flags = 0; +#if (L2CAP_UCD_INCLUDED == TRUE) + p_srec->ucd_security_flags = 0; +#endif + num_freed++; + } + } + + return (num_freed); +} +#endif ///SDP_INCLUDED == TRUE + +/******************************************************************************* +** +** Function btm_sec_clr_service_by_psm +** +** Description Removes specified service record from the security database. +** All service records with the specified psm are removed. +** Typically used by L2CAP to free up the service record used +** by dynamic PSM clients when the channel is closed. +** The given psm must be a virtual psm. +** +** Parameters Service ID - Id of the service to remove. ('0' removes all service +** records (except SDP). +** +** Returns Number of records that were freed. +** +*******************************************************************************/ +#if (SDP_INCLUDED== TRUE) +UINT8 btm_sec_clr_service_by_psm (UINT16 psm) +{ + tBTM_SEC_SERV_REC *p_srec = &btm_cb.sec_serv_rec[0]; + UINT8 num_freed = 0; + int i; + + for (i = 0; i < BTM_SEC_MAX_SERVICE_RECORDS; i++, p_srec++) { + /* Delete services with specified name (if in use and not SDP) */ + if ((p_srec->security_flags & BTM_SEC_IN_USE) && (p_srec->psm == psm) ) { + BTM_TRACE_API("BTM_SEC_CLR[%d]: id %d\n", i, p_srec->service_id); + p_srec->security_flags = 0; + num_freed++; + } + } + BTM_TRACE_API("btm_sec_clr_service_by_psm psm:0x%x num_freed:%d\n", psm, num_freed); + + return (num_freed); +} +#endif ///SDP_INCLUDED== TRUE + +/******************************************************************************* +** +** Function btm_sec_clr_temp_auth_service +** +** Description Removes specified device record's temporary authorization +** flag from the security database. +** +** Parameters Device address to be cleared +** +** Returns void. +** +*******************************************************************************/ +void btm_sec_clr_temp_auth_service (BD_ADDR bda) +{ + tBTM_SEC_DEV_REC *p_dev_rec; + + if ((p_dev_rec = btm_find_dev (bda)) == NULL) { + BTM_TRACE_WARNING ("btm_sec_clr_temp_auth_service() - no dev CB\n"); + return; + } + + /* Reset the temporary authorized flag so that next time (untrusted) service is accessed autorization will take place */ + if (p_dev_rec->last_author_service_id != BTM_SEC_NO_LAST_SERVICE_ID && p_dev_rec->p_cur_service) { + BTM_TRACE_DEBUG ("btm_sec_clr_auth_service_by_psm [clearing device: %02x:%02x:%02x:%02x:%02x:%02x]\n", + bda[0], bda[1], bda[2], bda[3], bda[4], bda[5]); + + p_dev_rec->last_author_service_id = BTM_SEC_NO_LAST_SERVICE_ID; + } +} + +/******************************************************************************* +** +** Function BTM_PINCodeReply +** +** Description This function is called after Security Manager submitted +** PIN code request to the UI. +** +** Parameters: bd_addr - Address of the device for which PIN was requested +** res - result of the operation BTM_SUCCESS if success +** pin_len - length in bytes of the PIN Code +** p_pin - pointer to array with the PIN Code +** trusted_mask - bitwise OR of trusted services (array of UINT32) +** +*******************************************************************************/ +#if (SMP_INCLUDED == TRUE) +#if (CLASSIC_BT_INCLUDED == TRUE) +void BTM_PINCodeReply (BD_ADDR bd_addr, UINT8 res, UINT8 pin_len, UINT8 *p_pin, UINT32 trusted_mask[]) +{ + tBTM_SEC_DEV_REC *p_dev_rec; +#if (BT_USE_TRACES == TRUE && SMP_INCLUDED == TRUE) + BTM_TRACE_API ("BTM_PINCodeReply(): PairState: %s PairFlags: 0x%02x PinLen:%d Result:%d\n", + btm_pair_state_descr(btm_cb.pairing_state), btm_cb.pairing_flags, pin_len, res); +#endif ///BT_USE_TRACES == TRUE && SMP_INCLUDED == TRUE + /* If timeout already expired or has been canceled, ignore the reply */ + if (btm_cb.pairing_state != BTM_PAIR_STATE_WAIT_LOCAL_PIN) { + BTM_TRACE_WARNING ("BTM_PINCodeReply() - Wrong State: %d\n", btm_cb.pairing_state); + return; + } + + if (memcmp (bd_addr, btm_cb.pairing_bda, BD_ADDR_LEN) != 0) { + BTM_TRACE_ERROR ("BTM_PINCodeReply() - Wrong BD Addr\n"); + return; + } + + if ((p_dev_rec = btm_find_dev (bd_addr)) == NULL) { + BTM_TRACE_ERROR ("BTM_PINCodeReply() - no dev CB\n"); + return; + } + + if ( (pin_len > PIN_CODE_LEN) || (pin_len == 0) || (p_pin == NULL) ) { + res = BTM_ILLEGAL_VALUE; + } + + if (res != BTM_SUCCESS) { + /* if peer started dd OR we started dd and pre-fetch pin was not used send negative reply */ + if ((btm_cb.pairing_flags & BTM_PAIR_FLAGS_PEER_STARTED_DD) || + ((btm_cb.pairing_flags & BTM_PAIR_FLAGS_WE_STARTED_DD) && + (btm_cb.pairing_flags & BTM_PAIR_FLAGS_DISC_WHEN_DONE)) ) { + /* use BTM_PAIR_STATE_WAIT_AUTH_COMPLETE to report authentication failed event */ + btm_sec_change_pairing_state (BTM_PAIR_STATE_WAIT_AUTH_COMPLETE); + btm_cb.acl_disc_reason = HCI_ERR_HOST_REJECT_SECURITY; + + btsnd_hcic_pin_code_neg_reply (bd_addr); + } else { + p_dev_rec->security_required = BTM_SEC_NONE; + btm_sec_change_pairing_state (BTM_PAIR_STATE_IDLE); + } + return; + } + if (trusted_mask) { + BTM_SEC_COPY_TRUSTED_DEVICE(trusted_mask, p_dev_rec->trusted_mask); + } + p_dev_rec->sec_flags |= BTM_SEC_LINK_KEY_AUTHED; + if (pin_len >= 16) { + p_dev_rec->sec_flags |= BTM_SEC_16_DIGIT_PIN_AUTHED; + } + + if ( (btm_cb.pairing_flags & BTM_PAIR_FLAGS_WE_STARTED_DD) + && (p_dev_rec->hci_handle == BTM_SEC_INVALID_HANDLE) + && (btm_cb.security_mode_changed == FALSE) ) { + /* This is start of the dedicated bonding if local device is 2.0 */ + btm_cb.pin_code_len = pin_len; + p_dev_rec->pin_code_length = pin_len; + memcpy (btm_cb.pin_code, p_pin, pin_len); + + btm_cb.security_mode_changed = TRUE; +#ifdef APPL_AUTH_WRITE_EXCEPTION + if (!(APPL_AUTH_WRITE_EXCEPTION)(p_dev_rec->bd_addr)) +#endif + { + btsnd_hcic_write_auth_enable (TRUE); + } + + btm_cb.acl_disc_reason = 0xff ; + + /* if we rejected incoming connection request, we have to wait HCI_Connection_Complete event */ + /* before originating */ + if (btm_cb.pairing_flags & BTM_PAIR_FLAGS_REJECTED_CONNECT) { + BTM_TRACE_WARNING ("BTM_PINCodeReply(): waiting HCI_Connection_Complete after rejected incoming connection\n"); + /* we change state little bit early so btm_sec_connected() will originate connection */ + /* when existing ACL link is down completely */ + btm_sec_change_pairing_state (BTM_PAIR_STATE_WAIT_PIN_REQ); + } + /* if we already accepted incoming connection from pairing device */ + else if (p_dev_rec->sm4 & BTM_SM4_CONN_PEND) { + BTM_TRACE_WARNING ("BTM_PINCodeReply(): link is connecting so wait pin code request from peer\n"); + btm_sec_change_pairing_state (BTM_PAIR_STATE_WAIT_PIN_REQ); + } else if (btm_sec_dd_create_conn(p_dev_rec) != BTM_CMD_STARTED) { + btm_sec_change_pairing_state (BTM_PAIR_STATE_IDLE); + p_dev_rec->sec_flags &= ~BTM_SEC_LINK_KEY_AUTHED; + + if (btm_cb.api.p_auth_complete_callback) { + (*btm_cb.api.p_auth_complete_callback) (p_dev_rec->bd_addr, p_dev_rec->dev_class, + p_dev_rec->sec_bd_name, HCI_ERR_AUTH_FAILURE); + } + } + return; + } + + btm_sec_change_pairing_state (BTM_PAIR_STATE_WAIT_AUTH_COMPLETE); + btm_cb.acl_disc_reason = HCI_SUCCESS; + +#ifdef PORCHE_PAIRING_CONFLICT + BTM_TRACE_EVENT("BTM_PINCodeReply(): Saving pin_len: %d btm_cb.pin_code_len: %d\n", pin_len, btm_cb.pin_code_len); + /* if this was not pre-fetched, save the PIN */ + if (btm_cb.pin_code_len == 0) { + memcpy (btm_cb.pin_code, p_pin, pin_len); + } + btm_cb.pin_code_len_saved = pin_len; +#endif + btsnd_hcic_pin_code_req_reply (bd_addr, pin_len, p_pin); +} +#endif ///CLASSIC_BT_INCLUDED == TRUE +#endif ///SMP_INCLUDED == TRUE + + +/******************************************************************************* +** +** Function btm_sec_bond_by_transport +** +** Description this is the bond function that will start either SSP or SMP. +** +** Parameters: bd_addr - Address of the device to bond +** pin_len - length in bytes of the PIN Code +** p_pin - pointer to array with the PIN Code +** trusted_mask - bitwise OR of trusted services (array of UINT32) +** +** Note: After 2.1 parameters are not used and preserved here not to change API +*******************************************************************************/ +#if (SMP_INCLUDED == TRUE) +tBTM_STATUS btm_sec_bond_by_transport (BD_ADDR bd_addr, tBT_TRANSPORT transport, + UINT8 pin_len, UINT8 *p_pin, UINT32 trusted_mask[]) +{ + tBTM_SEC_DEV_REC *p_dev_rec; + tBTM_STATUS status; +#if (!CONFIG_BT_STACK_NO_LOG) + UINT8 *p_features; +#endif + UINT8 ii; + tACL_CONN *p = btm_bda_to_acl(bd_addr, transport); + BTM_TRACE_API ("btm_sec_bond_by_transport BDA: %02x:%02x:%02x:%02x:%02x:%02x\n", + bd_addr[0], bd_addr[1], bd_addr[2], bd_addr[3], bd_addr[4], bd_addr[5]); + + BTM_TRACE_DEBUG("btm_sec_bond_by_transport: Transport used %d\n" , transport); + + + /* Other security process is in progress */ + if (btm_cb.pairing_state != BTM_PAIR_STATE_IDLE) { +#if (BT_USE_TRACES == TRUE && SMP_INCLUDED == TRUE) + BTM_TRACE_ERROR ("BTM_SecBond: already busy in state: %s\n", btm_pair_state_descr(btm_cb.pairing_state)); +#endif ///BT_USE_TRACES == TRUE && SMP_INCLUDED == TRUE + return (BTM_WRONG_MODE); + } + + if ((p_dev_rec = btm_find_or_alloc_dev (bd_addr)) == NULL) { + return (BTM_NO_RESOURCES); + } + + BTM_TRACE_DEBUG ("before update sec_flags=0x%x\n", p_dev_rec->sec_flags); + + /* Finished if connection is active and already paired */ + if ( ((p_dev_rec->hci_handle != BTM_SEC_INVALID_HANDLE) && transport == BT_TRANSPORT_BR_EDR + && (p_dev_rec->sec_flags & BTM_SEC_AUTHENTICATED)) +#if (BLE_INCLUDED == TRUE) + || ((p_dev_rec->ble_hci_handle != BTM_SEC_INVALID_HANDLE) && transport == BT_TRANSPORT_LE + && (p_dev_rec->sec_flags & BTM_SEC_LE_AUTHENTICATED)) +#endif + + ) { + BTM_TRACE_WARNING("BTM_SecBond -> Already Paired\n"); + return (BTM_SUCCESS); + } + + /* Tell controller to get rid of the link key if it has one stored */ + if ((BTM_DeleteStoredLinkKey (bd_addr, NULL)) != BTM_SUCCESS) { + return (BTM_NO_RESOURCES); + } + +#if (CLASSIC_BT_INCLUDED == TRUE) + /* Save the PIN code if we got a valid one */ + if (p_pin && (pin_len <= PIN_CODE_LEN) && (pin_len != 0)) { + btm_cb.pin_code_len = pin_len; + p_dev_rec->pin_code_length = pin_len; + memcpy (btm_cb.pin_code, p_pin, PIN_CODE_LEN); + } +#endif ///CLASSIC_BT_INCLUDED == TRUE + + memcpy (btm_cb.pairing_bda, bd_addr, BD_ADDR_LEN); + + btm_cb.pairing_flags = BTM_PAIR_FLAGS_WE_STARTED_DD; + + p_dev_rec->security_required = BTM_SEC_OUT_AUTHENTICATE; + p_dev_rec->is_originator = TRUE; + if (trusted_mask) { + BTM_SEC_COPY_TRUSTED_DEVICE(trusted_mask, p_dev_rec->trusted_mask); + } + +#if BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE + if (transport == BT_TRANSPORT_LE) { + btm_ble_init_pseudo_addr (p_dev_rec, bd_addr); + p_dev_rec->sec_flags &= ~ BTM_SEC_LE_MASK; + + if (SMP_Pair(bd_addr) == SMP_STARTED) { + btm_cb.pairing_flags |= BTM_PAIR_FLAGS_LE_ACTIVE; + p_dev_rec->sec_state = BTM_SEC_STATE_AUTHENTICATING; + btm_sec_change_pairing_state (BTM_PAIR_STATE_WAIT_AUTH_COMPLETE); + return BTM_CMD_STARTED; + } + + btm_cb.pairing_flags = 0; + return (BTM_NO_RESOURCES); + } +#endif + + p_dev_rec->sec_flags &= ~(BTM_SEC_LINK_KEY_KNOWN | BTM_SEC_AUTHENTICATED | BTM_SEC_ENCRYPTED + | BTM_SEC_ROLE_SWITCHED | BTM_SEC_LINK_KEY_AUTHED); + + + BTM_TRACE_DEBUG ("after update sec_flags=0x%x\n", p_dev_rec->sec_flags); + +#if (CLASSIC_BT_INCLUDED == TRUE) + if (!controller_get_interface()->supports_simple_pairing()) { + /* The special case when we authenticate keyboard. Set pin type to fixed */ + /* It would be probably better to do it from the application, but it is */ + /* complicated */ + if (((p_dev_rec->dev_class[1] & BTM_COD_MAJOR_CLASS_MASK) == BTM_COD_MAJOR_PERIPHERAL) + && (p_dev_rec->dev_class[2] & BTM_COD_MINOR_KEYBOARD) + && (btm_cb.cfg.pin_type != HCI_PIN_TYPE_FIXED)) { + btm_cb.pin_type_changed = TRUE; + btsnd_hcic_write_pin_type (HCI_PIN_TYPE_FIXED); + } + } +#endif ///CLASSIC_BT_INCLUDED == TRUE + + for (ii = 0; ii <= HCI_EXT_FEATURES_PAGE_MAX; ii++) { +#if (!CONFIG_BT_STACK_NO_LOG) + p_features = p_dev_rec->features[ii]; +#endif + BTM_TRACE_EVENT(" remote_features page[%1d] = %02x-%02x-%02x-%02x\n", + ii, p_features[0], p_features[1], p_features[2], p_features[3]); + BTM_TRACE_EVENT(" %02x-%02x-%02x-%02x\n", + p_features[4], p_features[5], p_features[6], p_features[7]); + } + + BTM_TRACE_EVENT ("BTM_SecBond: Remote sm4: 0x%x HCI Handle: 0x%04x\n", p_dev_rec->sm4, p_dev_rec->hci_handle); + +#if BTM_SEC_FORCE_RNR_FOR_DBOND == TRUE + p_dev_rec->sec_flags &= ~BTM_SEC_NAME_KNOWN; +#endif + + /* If connection already exists... */ + if (p && p->hci_handle != BTM_SEC_INVALID_HANDLE) { + if (!btm_sec_start_authentication (p_dev_rec)) { + return (BTM_NO_RESOURCES); + } + + btm_sec_change_pairing_state (BTM_PAIR_STATE_WAIT_PIN_REQ); + + /* Mark lcb as bonding */ + l2cu_update_lcb_4_bonding (bd_addr, TRUE); + return (BTM_CMD_STARTED); + } + + BTM_TRACE_DEBUG ("sec mode: %d sm4:x%x\n", btm_cb.security_mode, p_dev_rec->sm4); + if (!controller_get_interface()->supports_simple_pairing() + || (p_dev_rec->sm4 == BTM_SM4_KNOWN)) { + if ( btm_sec_check_prefetch_pin (p_dev_rec) ) { + return (BTM_CMD_STARTED); + } + } + if ((btm_cb.security_mode == BTM_SEC_MODE_SP || + btm_cb.security_mode == BTM_SEC_MODE_SP_DEBUG || + btm_cb.security_mode == BTM_SEC_MODE_SC) && + BTM_SEC_IS_SM4_UNKNOWN(p_dev_rec->sm4)) { + /* local is 2.1 and peer is unknown */ + if ((p_dev_rec->sm4 & BTM_SM4_CONN_PEND) == 0) { + /* we are not accepting connection request from peer + * -> RNR (to learn if peer is 2.1) + * RNR when no ACL causes HCI_RMT_HOST_SUP_FEAT_NOTIFY_EVT */ + btm_sec_change_pairing_state (BTM_PAIR_STATE_GET_REM_NAME); + BTM_ReadRemoteDeviceName(bd_addr, NULL, BT_TRANSPORT_BR_EDR); + } else { + /* We are accepting connection request from peer */ + btm_sec_change_pairing_state (BTM_PAIR_STATE_WAIT_PIN_REQ); + } +#if (BT_USE_TRACES == TRUE && SMP_INCLUDED == TRUE) + BTM_TRACE_DEBUG ("State:%s sm4: 0x%x sec_state:%d\n", + btm_pair_state_descr (btm_cb.pairing_state), p_dev_rec->sm4, p_dev_rec->sec_state); +#endif ///BT_USE_TRACES == TRUE && SMP_INCLUDED == TRUE + return BTM_CMD_STARTED; + } + + /* both local and peer are 2.1 */ + status = btm_sec_dd_create_conn(p_dev_rec); + + if (status != BTM_CMD_STARTED) { + btm_sec_change_pairing_state (BTM_PAIR_STATE_IDLE); + } + + return status; +} + +/******************************************************************************* +** +** Function BTM_SecBondByTransport +** +** Description This function is called to perform bonding with peer device. +** If the connection is already up, but not secure, pairing +** is attempted. If already paired BTM_SUCCESS is returned. +** +** Parameters: bd_addr - Address of the device to bond +** transport - doing SSP over BR/EDR or SMP over LE +** pin_len - length in bytes of the PIN Code +** p_pin - pointer to array with the PIN Code +** trusted_mask - bitwise OR of trusted services (array of UINT32) +** +** Note: After 2.1 parameters are not used and preserved here not to change API +*******************************************************************************/ +tBTM_STATUS BTM_SecBondByTransport (BD_ADDR bd_addr, tBT_TRANSPORT transport, + UINT8 pin_len, UINT8 *p_pin, UINT32 trusted_mask[]) +{ +#if (BLE_INCLUDED == TRUE) + tBT_DEVICE_TYPE dev_type; + tBLE_ADDR_TYPE addr_type = 0; + + BTM_ReadDevInfo(bd_addr, &dev_type, &addr_type); + /* LE device, do SMP pairing */ + if ((transport == BT_TRANSPORT_LE && (dev_type & BT_DEVICE_TYPE_BLE) == 0) || + (transport == BT_TRANSPORT_BR_EDR && (dev_type & BT_DEVICE_TYPE_BREDR) == 0)) { + return BTM_ILLEGAL_ACTION; + } +#endif ///BLE_INCLUDED == TRUE + + return btm_sec_bond_by_transport(bd_addr, transport, pin_len, p_pin, trusted_mask); +} +#endif ///SMP_INCLUDED == TRUE + +/******************************************************************************* +** +** Function BTM_SecBond +** +** Description This function is called to perform bonding with peer device. +** If the connection is already up, but not secure, pairing +** is attempted. If already paired BTM_SUCCESS is returned. +** +** Parameters: bd_addr - Address of the device to bond +** pin_len - length in bytes of the PIN Code +** p_pin - pointer to array with the PIN Code +** trusted_mask - bitwise OR of trusted services (array of UINT32) +** +** Note: After 2.1 parameters are not used and preserved here not to change API +*******************************************************************************/ +#if (SMP_INCLUDED == TRUE) +tBTM_STATUS BTM_SecBond (BD_ADDR bd_addr, UINT8 pin_len, UINT8 *p_pin, UINT32 trusted_mask[]) +{ + tBT_TRANSPORT transport = BT_TRANSPORT_BR_EDR; +#if (BLE_INCLUDED == TRUE) + if (BTM_UseLeLink(bd_addr)) { + transport = BT_TRANSPORT_LE; + } +#endif ///BLE_INCLUDED == TRUE + return btm_sec_bond_by_transport(bd_addr, transport, pin_len, p_pin, trusted_mask); +} +/******************************************************************************* +** +** Function BTM_SecBondCancel +** +** Description This function is called to cancel ongoing bonding process +** with peer device. +** +** Parameters: bd_addr - Address of the peer device +** transport - FALSE for BR/EDR link; TRUE for LE link +** +*******************************************************************************/ +tBTM_STATUS BTM_SecBondCancel (BD_ADDR bd_addr) +{ + tBTM_SEC_DEV_REC *p_dev_rec; +#if (BT_USE_TRACES == TRUE && SMP_INCLUDED == TRUE) + BTM_TRACE_API ("BTM_SecBondCancel() State: %s flags:0x%x\n", + btm_pair_state_descr (btm_cb.pairing_state), btm_cb.pairing_flags); +#endif ///BT_USE_TRACES == TRUE && SMP_INCLUDED == TRUE + if (((p_dev_rec = btm_find_dev (bd_addr)) == NULL) + || (memcmp (btm_cb.pairing_bda, bd_addr, BD_ADDR_LEN) != 0) ) { + return BTM_UNKNOWN_ADDR; + } + +#if SMP_INCLUDED == TRUE + if (btm_cb.pairing_flags & BTM_PAIR_FLAGS_LE_ACTIVE) { + if (p_dev_rec->sec_state == BTM_SEC_STATE_AUTHENTICATING) { + BTM_TRACE_DEBUG ("Cancel LE pairing\n"); + if (SMP_PairCancel(bd_addr)) { + return BTM_CMD_STARTED; + } + } + return BTM_WRONG_MODE; + } + +#endif + BTM_TRACE_DEBUG ("hci_handle:0x%x sec_state:%d\n", p_dev_rec->hci_handle, p_dev_rec->sec_state ); + if (BTM_PAIR_STATE_WAIT_LOCAL_PIN == btm_cb.pairing_state && + BTM_PAIR_FLAGS_WE_STARTED_DD & btm_cb.pairing_flags) { + /* pre-fetching pin for dedicated bonding */ + btm_sec_bond_cancel_complete(); + return BTM_SUCCESS; + } + + /* If this BDA is in a bonding procedure */ + if ( (btm_cb.pairing_state != BTM_PAIR_STATE_IDLE) + && (btm_cb.pairing_flags & BTM_PAIR_FLAGS_WE_STARTED_DD)) { + /* If the HCI link is up */ + if (p_dev_rec->hci_handle != BTM_SEC_INVALID_HANDLE) { + /* If some other thread disconnecting, we do not send second command */ + if ((p_dev_rec->sec_state == BTM_SEC_STATE_DISCONNECTING) || + (p_dev_rec->sec_state == BTM_SEC_STATE_DISCONNECTING_BOTH)) { + return (BTM_CMD_STARTED); + } + + /* If the HCI link was set up by Bonding process */ + if (btm_cb.pairing_flags & BTM_PAIR_FLAGS_DISC_WHEN_DONE) { + return btm_sec_send_hci_disconnect(p_dev_rec, HCI_ERR_PEER_USER, p_dev_rec->hci_handle); + } else { + l2cu_update_lcb_4_bonding(bd_addr, FALSE); + } + + return BTM_NOT_AUTHORIZED; + } else { /*HCI link is not up */ + /* If the HCI link creation was started by Bonding process */ + if (btm_cb.pairing_flags & BTM_PAIR_FLAGS_DISC_WHEN_DONE) { + if (btsnd_hcic_create_conn_cancel(bd_addr)) { + return BTM_CMD_STARTED; + } + + return BTM_NO_RESOURCES; + } + if (btm_cb.pairing_state == BTM_PAIR_STATE_GET_REM_NAME) { + BTM_CancelRemoteDeviceName(); + btm_cb.pairing_flags |= BTM_PAIR_FLAGS_WE_CANCEL_DD; + return BTM_CMD_STARTED; + } + return BTM_NOT_AUTHORIZED; + } + } + return BTM_WRONG_MODE; +} + + +/******************************************************************************* +** +** Function BTM_SecGetDeviceLinkKey +** +** Description This function is called to obtain link key for the device +** it returns BTM_SUCCESS if link key is available, or +** BTM_UNKNOWN_ADDR if Security Manager does not know about +** the device or device record does not contain link key info +** +** Parameters: bd_addr - Address of the device +** link_key - Link Key is copied into this array +** +*******************************************************************************/ +tBTM_STATUS BTM_SecGetDeviceLinkKey (BD_ADDR bd_addr, LINK_KEY link_key) +{ + tBTM_SEC_DEV_REC *p_dev_rec; + + if (((p_dev_rec = btm_find_dev (bd_addr)) != NULL) + && (p_dev_rec->sec_flags & BTM_SEC_LINK_KEY_KNOWN)) { + memcpy (link_key, p_dev_rec->link_key, LINK_KEY_LEN); + return (BTM_SUCCESS); + } + return (BTM_UNKNOWN_ADDR); +} +#endif ///SMP_INCLUDED == TRUE + +/******************************************************************************* +** +** Function BTM_SecGetDeviceLinkKeyType +** +** Description This function is called to obtain link key type for the +** device. +** it returns BTM_SUCCESS if link key is available, or +** BTM_UNKNOWN_ADDR if Security Manager does not know about +** the device or device record does not contain link key info +** +** Returns BTM_LKEY_TYPE_IGNORE if link key is unknown, link type +** otherwise. +** +*******************************************************************************/ +tBTM_LINK_KEY_TYPE BTM_SecGetDeviceLinkKeyType (BD_ADDR bd_addr) +{ + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev (bd_addr); + + if ((p_dev_rec != NULL) && (p_dev_rec->sec_flags & BTM_SEC_LINK_KEY_KNOWN)) { + return p_dev_rec->link_key_type; + } + return BTM_LKEY_TYPE_IGNORE; +} + +/******************************************************************************* +** +** Function BTM_SetEncryption +** +** Description This function is called to ensure that connection is +** encrypted. Should be called only on an open connection. +** Typically only needed for connections that first want to +** bring up unencrypted links, then later encrypt them. +** +** Parameters: bd_addr - Address of the peer device +** p_callback - Pointer to callback function called if +** this function returns PENDING after required +** procedures are completed. Can be set to NULL +** if status is not desired. +** p_ref_data - pointer to any data the caller wishes to receive +** in the callback function upon completion. +* can be set to NULL if not used. +** transport - TRUE to encryption the link over LE transport +** or FALSE for BR/EDR transport +** +** Returns BTM_SUCCESS - already encrypted +** BTM_PENDING - command will be returned in the callback +** BTM_WRONG_MODE- connection not up. +** BTM_BUSY - security procedures are currently active +** BTM_MODE_UNSUPPORTED - if security manager not linked in. +** +*******************************************************************************/ +#if (SMP_INCLUDED == TRUE) +tBTM_STATUS BTM_SetEncryption (BD_ADDR bd_addr, tBT_TRANSPORT transport, tBTM_SEC_CBACK *p_callback, + void *p_ref_data) +{ + tBTM_STATUS rc = 0; + + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev (bd_addr); + if (!p_dev_rec || + (transport == BT_TRANSPORT_BR_EDR && p_dev_rec->hci_handle == BTM_SEC_INVALID_HANDLE) +#if BLE_INCLUDED == TRUE + || (transport == BT_TRANSPORT_LE && p_dev_rec->ble_hci_handle == BTM_SEC_INVALID_HANDLE) +#endif + ) { + /* Connection should be up and runnning */ + BTM_TRACE_WARNING ("Security Manager: BTM_SetEncryption not connected\n"); + + if (p_callback) { + (*p_callback) (bd_addr, transport, p_ref_data, BTM_WRONG_MODE); + } + + return (BTM_WRONG_MODE); + } + + if ((transport == BT_TRANSPORT_BR_EDR && + (p_dev_rec->sec_flags & BTM_SEC_ENCRYPTED)) +#if BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE + || (transport == BT_TRANSPORT_LE && + (p_dev_rec->sec_flags & BTM_SEC_LE_ENCRYPTED)) +#endif + ) { + BTM_TRACE_EVENT ("Security Manager: BTM_SetEncryption already encrypted\n"); + + if (p_callback) { + (*p_callback) (bd_addr, transport, p_ref_data, BTM_SUCCESS); + } + + return (BTM_SUCCESS); + } + p_dev_rec->enc_init_by_we = TRUE; + /* enqueue security request if security is active */ + if (p_dev_rec->p_callback || (p_dev_rec->sec_state != BTM_SEC_STATE_IDLE)) { + BTM_TRACE_WARNING ("Security Manager: BTM_SetEncryption busy, enqueue request\n"); + + if (btm_sec_queue_encrypt_request(bd_addr, transport, p_callback, p_ref_data)) { + return BTM_CMD_STARTED; + } else { + if (p_callback) { + (*p_callback) (bd_addr, transport, p_ref_data, BTM_NO_RESOURCES); + } + return BTM_NO_RESOURCES; + } + } + + p_dev_rec->p_callback = p_callback; + p_dev_rec->p_ref_data = p_ref_data; + p_dev_rec->security_required |= (BTM_SEC_IN_AUTHENTICATE | BTM_SEC_IN_ENCRYPT); + p_dev_rec->is_originator = FALSE; + + BTM_TRACE_API ("Security Manager: BTM_SetEncryption Handle:%d State:%d Flags:0x%x Required:0x%x\n", + p_dev_rec->hci_handle, p_dev_rec->sec_state, p_dev_rec->sec_flags, + p_dev_rec->security_required); + +#if BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE + if (transport == BT_TRANSPORT_LE) { + tACL_CONN *p = btm_bda_to_acl(bd_addr, transport); + if (p) { + rc = btm_ble_set_encryption(bd_addr, p_ref_data, p->link_role); + } else { + rc = BTM_WRONG_MODE; + BTM_TRACE_WARNING("%s: cannot call btm_ble_set_encryption, p is NULL\n", __FUNCTION__); + } + } else +#endif + { + rc = btm_sec_execute_procedure (p_dev_rec); + } + + if (rc != BTM_CMD_STARTED && rc != BTM_BUSY) { + if (p_callback) { + p_dev_rec->p_callback = NULL; + (*p_callback) (bd_addr, transport, p_dev_rec->p_ref_data, rc); + } + } + + return (rc); +} + +/******************************************************************************* + * disconnect the ACL link, if it's not done yet. +*******************************************************************************/ +static tBTM_STATUS btm_sec_send_hci_disconnect (tBTM_SEC_DEV_REC *p_dev_rec, UINT8 reason, UINT16 conn_handle) +{ + UINT8 old_state = p_dev_rec->sec_state; + tBTM_STATUS status = BTM_CMD_STARTED; + + BTM_TRACE_EVENT ("btm_sec_send_hci_disconnect: handle:0x%x, reason=0x%x\n", + conn_handle, reason); + + /* send HCI_Disconnect on a transport only once */ + switch (old_state) { + case BTM_SEC_STATE_DISCONNECTING: + if (conn_handle == p_dev_rec->hci_handle) { + return status; + } + + p_dev_rec->sec_state = BTM_SEC_STATE_DISCONNECTING_BOTH; + break; + +#if BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE + case BTM_SEC_STATE_DISCONNECTING_BLE: + if (conn_handle == p_dev_rec->ble_hci_handle) { + return status; + } + + p_dev_rec->sec_state = BTM_SEC_STATE_DISCONNECTING_BOTH; + break; + + case BTM_SEC_STATE_DISCONNECTING_BOTH: + return status; +#endif + + default: + p_dev_rec->sec_state = (conn_handle == p_dev_rec->hci_handle) ? + BTM_SEC_STATE_DISCONNECTING : BTM_SEC_STATE_DISCONNECTING_BLE; + + break; + } + + /* If a role switch is in progress, delay the HCI Disconnect to avoid controller problem */ + if (p_dev_rec->rs_disc_pending == BTM_SEC_RS_PENDING && p_dev_rec->hci_handle == conn_handle) { + BTM_TRACE_DEBUG("RS in progress - Set DISC Pending flag in btm_sec_send_hci_disconnect to delay disconnect\n"); + p_dev_rec->rs_disc_pending = BTM_SEC_DISC_PENDING; + status = BTM_SUCCESS; + } + /* Tear down the HCI link */ + else if (!btsnd_hcic_disconnect (conn_handle, reason)) { + /* could not send disconnect. restore old state */ + p_dev_rec->sec_state = old_state; + status = BTM_NO_RESOURCES; + } + + return status; +} +#endif ///SMP_INCLUDED == TRUE +/******************************************************************************* +** +** Function BTM_ConfirmReqReply +** +** Description This function is called to confirm the numeric value for +** Simple Pairing in response to BTM_SP_CFM_REQ_EVT +** +** Parameters: res - result of the operation BTM_SUCCESS if success +** bd_addr - Address of the peer device +** +*******************************************************************************/ +#if (SMP_INCLUDED == TRUE) +void BTM_ConfirmReqReply(tBTM_STATUS res, BD_ADDR bd_addr) +{ + tBTM_SEC_DEV_REC *p_dev_rec; +#if (BT_USE_TRACES == TRUE && SMP_INCLUDED == TRUE) + BTM_TRACE_EVENT ("BTM_ConfirmReqReply() State: %s Res: %u", + btm_pair_state_descr(btm_cb.pairing_state), res); +#endif ///BT_USE_TRACES == TRUE && SMP_INCLUDED == TRUE + /* If timeout already expired or has been canceled, ignore the reply */ + if ( (btm_cb.pairing_state != BTM_PAIR_STATE_WAIT_NUMERIC_CONFIRM) + || (memcmp (btm_cb.pairing_bda, bd_addr, BD_ADDR_LEN) != 0) ) { + return; + } + + btm_sec_change_pairing_state (BTM_PAIR_STATE_WAIT_AUTH_COMPLETE); + + if ( (res == BTM_SUCCESS) || (res == BTM_SUCCESS_NO_SECURITY) ) { + btm_cb.acl_disc_reason = HCI_SUCCESS; + + if (res == BTM_SUCCESS) { + if ((p_dev_rec = btm_find_dev (bd_addr)) != NULL) { + p_dev_rec->sec_flags |= BTM_SEC_LINK_KEY_AUTHED; + p_dev_rec->sec_flags |= BTM_SEC_16_DIGIT_PIN_AUTHED; + } + } + + btsnd_hcic_user_conf_reply (bd_addr, TRUE); + } else { + /* Report authentication failed event from state BTM_PAIR_STATE_WAIT_AUTH_COMPLETE */ + btm_cb.acl_disc_reason = HCI_ERR_HOST_REJECT_SECURITY; + btsnd_hcic_user_conf_reply (bd_addr, FALSE); + } +} +#endif ///SMP_INCLUDED == TRUE + +/******************************************************************************* +** +** Function BTM_PasskeyReqReply +** +** Description This function is called to provide the passkey for +** Simple Pairing in response to BTM_SP_KEY_REQ_EVT +** +** Parameters: res - result of the operation BTM_SUCCESS if success +** bd_addr - Address of the peer device +** passkey - numeric value in the range of +** BTM_MIN_PASSKEY_VAL(0) - BTM_MAX_PASSKEY_VAL(999999(0xF423F)). +** +*******************************************************************************/ +#if (CLASSIC_BT_INCLUDED == TRUE) +void BTM_PasskeyReqReply(tBTM_STATUS res, BD_ADDR bd_addr, UINT32 passkey) +{ +#if (BT_USE_TRACES == TRUE && SMP_INCLUDED == TRUE) + BTM_TRACE_API ("BTM_PasskeyReqReply: State: %s res:%d\n", + btm_pair_state_descr(btm_cb.pairing_state), res); +#endif ///BT_USE_TRACES == TRUE && SMP_INCLUDED == TRUE + if ( (btm_cb.pairing_state == BTM_PAIR_STATE_IDLE) + || (memcmp (btm_cb.pairing_bda, bd_addr, BD_ADDR_LEN) != 0) ) { + return; + } + + /* If timeout already expired or has been canceled, ignore the reply */ + if ( (btm_cb.pairing_state == BTM_PAIR_STATE_WAIT_AUTH_COMPLETE) && (res != BTM_SUCCESS) ) { + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev (bd_addr); + if (p_dev_rec != NULL) { + btm_cb.acl_disc_reason = HCI_ERR_HOST_REJECT_SECURITY; + + if (p_dev_rec->hci_handle != BTM_SEC_INVALID_HANDLE) { + btm_sec_send_hci_disconnect (p_dev_rec, HCI_ERR_AUTH_FAILURE, p_dev_rec->hci_handle); + } else { + BTM_SecBondCancel(bd_addr); + } + + p_dev_rec->sec_flags &= ~(BTM_SEC_LINK_KEY_AUTHED | BTM_SEC_LINK_KEY_KNOWN); + + btm_sec_change_pairing_state (BTM_PAIR_STATE_IDLE); + return; + } + } else if (btm_cb.pairing_state != BTM_PAIR_STATE_KEY_ENTRY) { + return; + } + + if (passkey > BTM_MAX_PASSKEY_VAL) { + res = BTM_ILLEGAL_VALUE; + } + + btm_sec_change_pairing_state (BTM_PAIR_STATE_WAIT_AUTH_COMPLETE); + + if (res != BTM_SUCCESS) { + /* use BTM_PAIR_STATE_WAIT_AUTH_COMPLETE to report authentication failed event */ + btm_cb.acl_disc_reason = HCI_ERR_HOST_REJECT_SECURITY; + btsnd_hcic_user_passkey_neg_reply (bd_addr); + } else { + btm_cb.acl_disc_reason = HCI_SUCCESS; + btsnd_hcic_user_passkey_reply (bd_addr, passkey); + } +} +#endif /* (CLASSIC_BT_INCLUDED == TRUE) */ + +/******************************************************************************* +** +** Function BTM_SendKeypressNotif +** +** Description This function is used during the passkey entry model +** by a device with KeyboardOnly IO capabilities +** (very likely to be a HID Device). +** It is called by a HID Device to inform the remote device when +** a key has been entered or erased. +** +** Parameters: bd_addr - Address of the peer device +** type - notification type +** +*******************************************************************************/ +#if (CLASSIC_BT_INCLUDED == TRUE) +void BTM_SendKeypressNotif(BD_ADDR bd_addr, tBTM_SP_KEY_TYPE type) +{ + /* This API only make sense between PASSKEY_REQ and SP complete */ + if (btm_cb.pairing_state == BTM_PAIR_STATE_KEY_ENTRY) { + btsnd_hcic_send_keypress_notif (bd_addr, type); + } +} +#endif /* (CLASSIC_BT_INCLUDED == TRUE) */ + +#if BTM_OOB_INCLUDED == TRUE && SMP_INCLUDED == TRUE +/******************************************************************************* +** +** Function BTM_IoCapRsp +** +** Description This function is called in response to BTM_SP_IO_REQ_EVT +** When the event data io_req.oob_data is set to BTM_OOB_UNKNOWN +** by the tBTM_SP_CALLBACK implementation, this function is +** called to provide the actual response +** +** Parameters: bd_addr - Address of the peer device +** io_cap - The IO capability of local device. +** oob - BTM_OOB_NONE or BTM_OOB_PRESENT. +** auth_req- MITM protection required or not. +** +*******************************************************************************/ +void BTM_IoCapRsp(BD_ADDR bd_addr, tBTM_IO_CAP io_cap, tBTM_OOB_DATA oob, tBTM_AUTH_REQ auth_req) +{ +#if (BT_USE_TRACES == TRUE && SMP_INCLUDED == TRUE) + BTM_TRACE_EVENT ("BTM_IoCapRsp: state: %s oob: %d io_cap: %d\n", + btm_pair_state_descr(btm_cb.pairing_state), oob, io_cap); +#endif ///BT_USE_TRACES == TRUE && SMP_INCLUDED == TRUE + if ( (btm_cb.pairing_state != BTM_PAIR_STATE_WAIT_LOCAL_IOCAPS) + || (memcmp (btm_cb.pairing_bda, bd_addr, BD_ADDR_LEN) != 0) ) { + return; + } + + if (oob < BTM_OOB_UNKNOWN && io_cap < BTM_IO_CAP_MAX) { + btm_cb.devcb.loc_auth_req = auth_req; + btm_cb.devcb.loc_io_caps = io_cap; + + if (btm_cb.pairing_flags & BTM_PAIR_FLAGS_WE_STARTED_DD) { + auth_req = (BTM_AUTH_DD_BOND | (auth_req & BTM_AUTH_YN_BIT)); + } + + btsnd_hcic_io_cap_req_reply (bd_addr, io_cap, oob, auth_req); + } +} + +/******************************************************************************* +** +** Function BTM_ReadLocalOobData +** +** Description This function is called to read the local OOB data from +** LM +** +*******************************************************************************/ +tBTM_STATUS BTM_ReadLocalOobData(void) +{ + tBTM_STATUS status = BTM_SUCCESS; + + if (btsnd_hcic_read_local_oob_data() == FALSE) { + status = BTM_NO_RESOURCES; + } + + return status; +} + +/******************************************************************************* +** +** Function BTM_RemoteOobDataReply +** +** Description This function is called to provide the remote OOB data for +** Simple Pairing in response to BTM_SP_RMT_OOB_EVT +** +** Parameters: bd_addr - Address of the peer device +** c - simple pairing Hash C. +** r - simple pairing Randomizer C. +** +*******************************************************************************/ +void BTM_RemoteOobDataReply(tBTM_STATUS res, BD_ADDR bd_addr, BT_OCTET16 c, BT_OCTET16 r) +{ +#if (BT_USE_TRACES == TRUE && SMP_INCLUDED == TRUE) + BTM_TRACE_EVENT ("%s() - State: %s res: %d\n", __func__, + btm_pair_state_descr(btm_cb.pairing_state), res); +#endif ///BT_USE_TRACES == TRUE && SMP_INCLUDED == TRUE + /* If timeout already expired or has been canceled, ignore the reply */ + if (btm_cb.pairing_state != BTM_PAIR_STATE_WAIT_LOCAL_OOB_RSP) { + return; + } + + btm_sec_change_pairing_state (BTM_PAIR_STATE_WAIT_AUTH_COMPLETE); + + if (res != BTM_SUCCESS) { + /* use BTM_PAIR_STATE_WAIT_AUTH_COMPLETE to report authentication failed event */ + btm_cb.acl_disc_reason = HCI_ERR_HOST_REJECT_SECURITY; + btsnd_hcic_rem_oob_neg_reply (bd_addr); + } else { + btm_cb.acl_disc_reason = HCI_SUCCESS; + btsnd_hcic_rem_oob_reply (bd_addr, c, r); + } +} + +/******************************************************************************* +** +** Function BTM_BuildOobData +** +** Description This function is called to build the OOB data payload to +** be sent over OOB (non-Bluetooth) link +** +** Parameters: p_data - the location for OOB data +** max_len - p_data size. +** c - simple pairing Hash C. +** r - simple pairing Randomizer C. +** name_len- 0, local device name would not be included. +** otherwise, the local device name is included for +** up to this specified length +** +** Returns Number of bytes in p_data. +** +*******************************************************************************/ +UINT16 BTM_BuildOobData(UINT8 *p_data, UINT16 max_len, BT_OCTET16 c, + BT_OCTET16 r, UINT8 name_len) +{ + UINT8 *p = p_data; + UINT16 len = 0; +#if BTM_MAX_LOC_BD_NAME_LEN > 0 + UINT16 name_size; + UINT8 name_type = BTM_EIR_SHORTENED_LOCAL_NAME_TYPE; +#endif + + if (p_data && max_len >= BTM_OOB_MANDATORY_SIZE) { + /* add mandatory part */ + UINT16_TO_STREAM(p, len); + BDADDR_TO_STREAM(p, controller_get_interface()->get_address()->address); + + len = BTM_OOB_MANDATORY_SIZE; + max_len -= len; + + /* now optional part */ + + /* add Hash C */ + UINT16 delta = BTM_OOB_HASH_C_SIZE + 2; + if (max_len >= delta) { + *p++ = BTM_OOB_HASH_C_SIZE + 1; + *p++ = BTM_EIR_OOB_SSP_HASH_C_TYPE; + ARRAY_TO_STREAM(p, c, BTM_OOB_HASH_C_SIZE); + len += delta; + max_len -= delta; + } + + /* add Rand R */ + delta = BTM_OOB_RAND_R_SIZE + 2; + if (max_len >= delta) { + *p++ = BTM_OOB_RAND_R_SIZE + 1; + *p++ = BTM_EIR_OOB_SSP_RAND_R_TYPE; + ARRAY_TO_STREAM(p, r, BTM_OOB_RAND_R_SIZE); + len += delta; + max_len -= delta; + } + + /* add class of device */ + delta = BTM_OOB_COD_SIZE + 2; + if (max_len >= delta) { + *p++ = BTM_OOB_COD_SIZE + 1; + *p++ = BTM_EIR_OOB_COD_TYPE; + DEVCLASS_TO_STREAM(p, btm_cb.devcb.dev_class); + len += delta; + max_len -= delta; + } +#if BTM_MAX_LOC_BD_NAME_LEN > 0 + name_size = name_len; + if (name_size > strlen(btm_cb.cfg.bd_name)) { + name_type = BTM_EIR_COMPLETE_LOCAL_NAME_TYPE; + name_size = (UINT16)strlen(btm_cb.cfg.bd_name); + } + delta = name_size + 2; + if (max_len >= delta) { + *p++ = name_size + 1; + *p++ = name_type; + ARRAY_TO_STREAM (p, btm_cb.cfg.bd_name, name_size); + len += delta; + max_len -= delta; + } +#endif + /* update len */ + p = p_data; + UINT16_TO_STREAM(p, len); + } + return len; +} + +/******************************************************************************* +** +** Function BTM_ReadOobData +** +** Description This function is called to parse the OOB data payload +** received over OOB (non-Bluetooth) link +** +** Parameters: p_data - the location for OOB data +** eir_tag - The associated EIR tag to read the data. +** *p_len(output) - the length of the data with the given tag. +** +** Returns the beginning of the data with the given tag. +** NULL, if the tag is not found. +** +*******************************************************************************/ +UINT8 *BTM_ReadOobData(UINT8 *p_data, UINT8 eir_tag, UINT8 *p_len) +{ + UINT8 *p = p_data; + UINT16 max_len; + UINT8 len, type; + UINT8 *p_ret = NULL; + UINT8 ret_len = 0; + + if (p_data) { + STREAM_TO_UINT16(max_len, p); + if (max_len >= BTM_OOB_MANDATORY_SIZE) { + if (BTM_EIR_OOB_BD_ADDR_TYPE == eir_tag) { + p_ret = p; /* the location for bd_addr */ + ret_len = BTM_OOB_BD_ADDR_SIZE; + } else { + p += BD_ADDR_LEN; + max_len -= BTM_OOB_MANDATORY_SIZE; + /* now the optional data in EIR format */ + while (max_len > 0) { + len = *p++; /* tag data len + 1 */ + type = *p++; + if (eir_tag == type) { + p_ret = p; + ret_len = len - 1; + break; + } + /* the data size of this tag is len + 1 (tag data len + 2) */ + if (max_len > len) { + max_len -= len; + max_len--; + len--; + p += len; + } else { + max_len = 0; + } + } + } + } + } + + if (p_len) { + *p_len = ret_len; + } + + return p_ret; +} +#endif ///BTM_OOB_INCLUDED == TRUE && SMP_INCLUDED == TRUE + +#if (CLASSIC_BT_INCLUDED == TRUE) +/******************************************************************************* +** +** Function BTM_BothEndsSupportSecureConnections +** +** Description This function is called to check if both the local device and the peer device +** specified by bd_addr support BR/EDR Secure Connections. +** +** Parameters: bd_addr - address of the peer +** +** Returns TRUE if BR/EDR Secure Connections are supported by both local +** and the remote device. +** else FALSE. +** +*******************************************************************************/ +BOOLEAN BTM_BothEndsSupportSecureConnections(BD_ADDR bd_addr) +{ + return ((controller_get_interface()->supports_secure_connections()) && + (BTM_PeerSupportsSecureConnections(bd_addr))); +} + +/******************************************************************************* +** +** Function BTM_PeerSupportsSecureConnections +** +** Description This function is called to check if the peer supports +** BR/EDR Secure Connections. +** +** Parameters: bd_addr - address of the peer +** +** Returns TRUE if BR/EDR Secure Connections are supported by the peer, +** else FALSE. +** +*******************************************************************************/ +BOOLEAN BTM_PeerSupportsSecureConnections(BD_ADDR bd_addr) +{ + tBTM_SEC_DEV_REC *p_dev_rec; + + if ((p_dev_rec = btm_find_dev(bd_addr)) == NULL) { + BTM_TRACE_WARNING("%s: unknown BDA: %08x%04x\n", __FUNCTION__, + (bd_addr[0] << 24) + (bd_addr[1] << 16) + (bd_addr[2] << 8) + bd_addr[3], + (bd_addr[4] << 8) + bd_addr[5]); + return FALSE; + } + + return (p_dev_rec->remote_supports_secure_connections); +} + +/******************************************************************************* +** +** Function BTM_SetOutService +** +** Description This function is called to set the service for +** outgoing connections. +** +** If the profile/application calls BTM_SetSecurityLevel +** before initiating a connection, this function does not +** need to be called. +** +** Returns void +** +*******************************************************************************/ +void BTM_SetOutService(BD_ADDR bd_addr, UINT8 service_id, UINT32 mx_chan_id) +{ + tBTM_SEC_DEV_REC *p_dev_rec; + tBTM_SEC_SERV_REC *p_serv_rec = &btm_cb.sec_serv_rec[0]; + + btm_cb.p_out_serv = p_serv_rec; + p_dev_rec = btm_find_dev (bd_addr); + + for (int i = 0; i < BTM_SEC_MAX_SERVICE_RECORDS; i++, p_serv_rec++) { + if ((p_serv_rec->security_flags & BTM_SEC_IN_USE) + && (p_serv_rec->service_id == service_id) + && (p_serv_rec->orig_mx_chan_id == mx_chan_id)) { + BTM_TRACE_API("BTM_SetOutService p_out_serv id %d, psm 0x%04x, proto_id %d, chan_id %d\n", + p_serv_rec->service_id, p_serv_rec->psm, p_serv_rec->mx_proto_id, p_serv_rec->orig_mx_chan_id); + btm_cb.p_out_serv = p_serv_rec; + if (p_dev_rec) { + p_dev_rec->p_cur_service = p_serv_rec; + } + break; + } + } +} +#endif ///CLASSIC_BT_INCLUDED == TRUE + + +/************************************************************************ +** I N T E R N A L F U N C T I O N S +*************************************************************************/ +/******************************************************************************* +** +** Function btm_sec_is_upgrade_possible +** +** Description This function returns TRUE if the existing link key +** can be upgraded or if the link key does not exist. +** +** Returns BOOLEAN +** +*******************************************************************************/ +#if (SMP_INCLUDED == TRUE) +static BOOLEAN btm_sec_is_upgrade_possible(tBTM_SEC_DEV_REC *p_dev_rec, BOOLEAN is_originator) +{ + UINT16 mtm_check = is_originator ? BTM_SEC_OUT_MITM : BTM_SEC_IN_MITM; + BOOLEAN is_possible = TRUE; + + if (p_dev_rec->sec_flags & BTM_SEC_LINK_KEY_KNOWN) { + is_possible = FALSE; + if (p_dev_rec->p_cur_service) { + BTM_TRACE_DEBUG ("%s() id: %d, link_key_typet: %d, rmt_io_caps: %d, chk flags: 0x%x, flags: 0x%x\n", + __func__, p_dev_rec->p_cur_service->service_id, p_dev_rec->link_key_type, + p_dev_rec->rmt_io_caps, mtm_check, p_dev_rec->p_cur_service->security_flags); + } else { + BTM_TRACE_DEBUG ("%s() link_key_typet: %d, rmt_io_caps: %d, chk flags: 0x%x\n", + __func__, p_dev_rec->link_key_type, p_dev_rec->rmt_io_caps, mtm_check); + } + /* Already have a link key to the connected peer. Is the link key secure enough? + ** Is a link key upgrade even possible? + */ + if ((p_dev_rec->security_required & mtm_check) /* needs MITM */ + && ((p_dev_rec->link_key_type == BTM_LKEY_TYPE_UNAUTH_COMB) || + (p_dev_rec->link_key_type == BTM_LKEY_TYPE_UNAUTH_COMB_P_256)) + /* has unauthenticated + link key */ + && (p_dev_rec->rmt_io_caps < BTM_IO_CAP_MAX) /* a valid peer IO cap */ + && (btm_sec_io_map[p_dev_rec->rmt_io_caps][btm_cb.devcb.loc_io_caps])) + /* authenticated + link key is possible */ + { + /* upgrade is possible: check if the application wants the upgrade. + * If the application is configured to use a global MITM flag, + * it probably would not want to upgrade the link key based on the security level database */ + is_possible = TRUE; + } + } + BTM_TRACE_DEBUG ("%s() is_possible: %d sec_flags: 0x%x\n", __func__, is_possible, p_dev_rec->sec_flags); + return is_possible; +} +#endif ///SMP_INCLUDED == TRUE + +/******************************************************************************* +** +** Function btm_sec_check_upgrade +** +** Description This function is called to check if the existing link key +** needs to be upgraded. +** +** Returns void +** +*******************************************************************************/ +#if (SMP_INCLUDED == TRUE) +static void btm_sec_check_upgrade(tBTM_SEC_DEV_REC *p_dev_rec, BOOLEAN is_originator) +{ + + BTM_TRACE_DEBUG ("%s()\n", __func__); + + /* Only check if link key already exists */ + if (!(p_dev_rec->sec_flags & BTM_SEC_LINK_KEY_KNOWN)) { + return; + } + + if (btm_sec_is_upgrade_possible (p_dev_rec, is_originator) == TRUE) { + BTM_TRACE_DEBUG ("need upgrade!! sec_flags:0x%x\n", p_dev_rec->sec_flags); + /* upgrade is possible: check if the application wants the upgrade. + * If the application is configured to use a global MITM flag, + * it probably would not want to upgrade the link key based on the security level database */ + tBTM_SP_UPGRADE evt_data; + memcpy (evt_data.bd_addr, p_dev_rec->bd_addr, BD_ADDR_LEN); + evt_data.upgrade = TRUE; + if (btm_cb.api.p_sp_callback) { + (*btm_cb.api.p_sp_callback) (BTM_SP_UPGRADE_EVT, (tBTM_SP_EVT_DATA *)&evt_data); + } + + BTM_TRACE_DEBUG ("evt_data.upgrade:0x%x\n", evt_data.upgrade); + if (evt_data.upgrade) { + /* if the application confirms the upgrade, set the upgrade bit */ + p_dev_rec->sm4 |= BTM_SM4_UPGRADE; + + /* Clear the link key known to go through authentication/pairing again */ + p_dev_rec->sec_flags &= ~(BTM_SEC_LINK_KEY_KNOWN | BTM_SEC_LINK_KEY_AUTHED); + p_dev_rec->sec_flags &= ~BTM_SEC_AUTHENTICATED; + BTM_TRACE_DEBUG ("sec_flags:0x%x\n", p_dev_rec->sec_flags); + } + } +} +#endif ///SMP_INCLUDED == TRUE + +/******************************************************************************* +** +** Function btm_sec_l2cap_access_req +** +** Description This function is called by the L2CAP to grant permission to +** establish L2CAP connection to or from the peer device. +** +** Parameters: bd_addr - Address of the peer device +** psm - L2CAP PSM +** is_originator - TRUE if protocol above L2CAP originates +** connection +** p_callback - Pointer to callback function called if +** this function returns PENDING after required +** procedures are complete. MUST NOT BE NULL. +** +** Returns tBTM_STATUS +** +*******************************************************************************/ +tBTM_STATUS btm_sec_l2cap_access_req (BD_ADDR bd_addr, UINT16 psm, UINT16 handle, + CONNECTION_TYPE conn_type, + tBTM_SEC_CALLBACK *p_callback, + void *p_ref_data) +{ +#if (SMP_INCLUDED == TRUE) + tBTM_SEC_DEV_REC *p_dev_rec; + tBTM_SEC_SERV_REC *p_serv_rec; + UINT16 security_required; + UINT16 old_security_required; + BOOLEAN old_is_originator; + tBTM_STATUS rc = BTM_SUCCESS; + BOOLEAN chk_acp_auth_done = FALSE; + BOOLEAN is_originator; + BOOLEAN transport = FALSE; /* should check PSM range in LE connection oriented L2CAP connection */ + +#if (L2CAP_UCD_INCLUDED == TRUE) + if (conn_type & CONNECTION_TYPE_ORIG_MASK) { + is_originator = TRUE; + } else { + is_originator = FALSE; + } + + BTM_TRACE_DEBUG ("%s() conn_type: 0x%x, %p\n", __func__, conn_type, p_ref_data); +#else + is_originator = conn_type; + + BTM_TRACE_DEBUG ("%s() is_originator:%d, %p\n", __func__, is_originator, p_ref_data); +#endif + + /* Find or get oldest record */ + p_dev_rec = btm_find_or_alloc_dev (bd_addr); + + p_dev_rec->hci_handle = handle; + + /* Find the service record for the PSM */ + p_serv_rec = btm_sec_find_first_serv (conn_type, psm); + + /* If there is no application registered with this PSM do not allow connection */ + if (!p_serv_rec) { + BTM_TRACE_WARNING ("%s() PSM: %d no application registerd\n", __func__, psm); + (*p_callback) (bd_addr, transport, p_ref_data, BTM_MODE_UNSUPPORTED); + return (BTM_MODE_UNSUPPORTED); + } + + /* Services level0 by default have no security */ + if ((btm_sec_is_serv_level0(psm)) && (!btm_cb.devcb.secure_connections_only)) { + (*p_callback) (bd_addr, transport, p_ref_data, BTM_SUCCESS_NO_SECURITY); + return (BTM_SUCCESS); + } +#if (L2CAP_UCD_INCLUDED == TRUE) + if ( conn_type & CONNECTION_TYPE_CONNLESS_MASK ) { + if (btm_cb.security_mode == BTM_SEC_MODE_SC) { + security_required = btm_sec_set_serv_level4_flags (p_serv_rec->ucd_security_flags, + is_originator); + } else { + security_required = p_serv_rec->ucd_security_flags; + } + + rc = BTM_CMD_STARTED; + if (is_originator) { + if (((security_required & BTM_SEC_OUT_FLAGS) == 0) || + ((((security_required & BTM_SEC_OUT_FLAGS) == BTM_SEC_OUT_AUTHENTICATE) && (p_dev_rec->sec_flags & BTM_SEC_AUTHENTICATED))) || + ((((security_required & BTM_SEC_OUT_FLAGS) == (BTM_SEC_OUT_AUTHENTICATE | BTM_SEC_OUT_ENCRYPT)) && (p_dev_rec->sec_flags & BTM_SEC_ENCRYPTED))) || + ((((security_required & BTM_SEC_OUT_FLAGS) == BTM_SEC_OUT_FLAGS) && (p_dev_rec->sec_flags & BTM_SEC_AUTHORIZED))) ) { + rc = BTM_SUCCESS; + } + } else { + if (((security_required & BTM_SEC_IN_FLAGS) == 0) || + ((((security_required & BTM_SEC_IN_FLAGS) == BTM_SEC_IN_AUTHENTICATE) && (p_dev_rec->sec_flags & BTM_SEC_AUTHENTICATED))) || + ((((security_required & BTM_SEC_IN_FLAGS) == (BTM_SEC_IN_AUTHENTICATE | BTM_SEC_IN_ENCRYPT)) && (p_dev_rec->sec_flags & BTM_SEC_ENCRYPTED))) || + ((((security_required & BTM_SEC_IN_FLAGS) == BTM_SEC_IN_FLAGS) && (p_dev_rec->sec_flags & BTM_SEC_AUTHORIZED))) ) { + // Check for 16 digits (or MITM) + if (((security_required & BTM_SEC_IN_MIN_16_DIGIT_PIN) == 0) || + (((security_required & BTM_SEC_IN_MIN_16_DIGIT_PIN) == BTM_SEC_IN_MIN_16_DIGIT_PIN) && + btm_dev_16_digit_authenticated(p_dev_rec))) { + rc = BTM_SUCCESS; + } + } + } + + if ((rc == BTM_SUCCESS) && (security_required & BTM_SEC_MODE4_LEVEL4) && + (p_dev_rec->link_key_type != BTM_LKEY_TYPE_AUTH_COMB_P_256)) { + rc = BTM_CMD_STARTED; + } + + if (rc == BTM_SUCCESS) { + if (p_callback) { + (*p_callback) (bd_addr, transport, (void *)p_ref_data, BTM_SUCCESS); + } + + return (BTM_SUCCESS); + } + } else +#endif + { + if (btm_cb.security_mode == BTM_SEC_MODE_SC) { + security_required = btm_sec_set_serv_level4_flags (p_serv_rec->security_flags, + is_originator); + } else { + security_required = p_serv_rec->security_flags; + } + } + + BTM_TRACE_DEBUG("%s: security_required 0x%04x, is_originator 0x%02x, psm 0x%04x\n", + __FUNCTION__, security_required, is_originator, psm); + + if ((!is_originator) && (security_required & BTM_SEC_MODE4_LEVEL4)) { + BOOLEAN local_supports_sc = controller_get_interface()->supports_secure_connections(); + /* acceptor receives L2CAP Channel Connect Request for Secure Connections Only service */ + if (!(local_supports_sc) || !(p_dev_rec->remote_supports_secure_connections)) { + BTM_TRACE_DEBUG("%s: SC only service, local_support_for_sc %d\n" + "rmt_support_for_sc : %d -> fail pairing\n", __FUNCTION__, + local_supports_sc, + p_dev_rec->remote_supports_secure_connections); + if (p_callback) { + (*p_callback) (bd_addr, transport, (void *)p_ref_data, + BTM_MODE4_LEVEL4_NOT_SUPPORTED); + } + + return (BTM_MODE4_LEVEL4_NOT_SUPPORTED); + } + } + + /* there are some devices (moto KRZR) which connects to several services at the same time */ + /* we will process one after another */ + if ( (p_dev_rec->p_callback) || (btm_cb.pairing_state != BTM_PAIR_STATE_IDLE) ) { +#if (BT_USE_TRACES == TRUE && SMP_INCLUDED == TRUE) + BTM_TRACE_EVENT ("%s() - busy - PSM:%d delayed state: %s mode:%d, sm4:0x%x\n", __func__, + psm, btm_pair_state_descr(btm_cb.pairing_state), btm_cb.security_mode, p_dev_rec->sm4); +#endif ///BT_USE_TRACES == TRUE && SMP_INCLUDED == TRUE + BTM_TRACE_EVENT ("security_flags:x%x, sec_flags:x%x\n", security_required, p_dev_rec->sec_flags); + rc = BTM_CMD_STARTED; + if ((btm_cb.security_mode == BTM_SEC_MODE_UNDEFINED || + btm_cb.security_mode == BTM_SEC_MODE_NONE || + btm_cb.security_mode == BTM_SEC_MODE_SERVICE || + btm_cb.security_mode == BTM_SEC_MODE_LINK) || + (BTM_SM4_KNOWN == p_dev_rec->sm4) || (BTM_SEC_IS_SM4(p_dev_rec->sm4) && + (btm_sec_is_upgrade_possible(p_dev_rec, is_originator) == FALSE))) { + /* legacy mode - local is legacy or local is lisbon/peer is legacy + * or SM4 with no possibility of link key upgrade */ + if (is_originator) { + if (((security_required & BTM_SEC_OUT_FLAGS) == 0) || + ((((security_required & BTM_SEC_OUT_FLAGS) == BTM_SEC_OUT_AUTHENTICATE) && btm_dev_authenticated(p_dev_rec))) || + ((((security_required & BTM_SEC_OUT_FLAGS) == (BTM_SEC_OUT_AUTHENTICATE | BTM_SEC_OUT_ENCRYPT)) && btm_dev_encrypted(p_dev_rec))) || + ((((security_required & BTM_SEC_OUT_FLAGS) == BTM_SEC_OUT_FLAGS) && btm_dev_authorized(p_dev_rec) && btm_dev_encrypted(p_dev_rec))) ) { + rc = BTM_SUCCESS; + } + } else { + if (((security_required & BTM_SEC_IN_FLAGS) == 0) || + (((security_required & BTM_SEC_IN_FLAGS) == BTM_SEC_IN_AUTHENTICATE) && btm_dev_authenticated(p_dev_rec)) || + (((security_required & BTM_SEC_IN_FLAGS) == (BTM_SEC_IN_AUTHENTICATE | BTM_SEC_IN_ENCRYPT)) && btm_dev_encrypted(p_dev_rec)) || + (((security_required & BTM_SEC_IN_FLAGS) == BTM_SEC_IN_AUTHORIZE) && (btm_dev_authorized(p_dev_rec) || btm_serv_trusted(p_dev_rec, p_serv_rec))) || + (((security_required & BTM_SEC_IN_FLAGS) == (BTM_SEC_IN_AUTHENTICATE | BTM_SEC_IN_AUTHORIZE)) && ((btm_dev_authorized(p_dev_rec) || btm_serv_trusted(p_dev_rec, p_serv_rec)) && btm_dev_authenticated(p_dev_rec))) || + (((security_required & BTM_SEC_IN_FLAGS) == (BTM_SEC_IN_ENCRYPT | BTM_SEC_IN_AUTHORIZE)) && ((btm_dev_authorized(p_dev_rec) || btm_serv_trusted(p_dev_rec, p_serv_rec)) && btm_dev_encrypted(p_dev_rec))) || + (((security_required & BTM_SEC_IN_FLAGS) == BTM_SEC_IN_FLAGS) && btm_dev_encrypted(p_dev_rec) && (btm_dev_authorized(p_dev_rec) || btm_serv_trusted(p_dev_rec, p_serv_rec)))) { + // Check for 16 digits (or MITM) + if (((security_required & BTM_SEC_IN_MIN_16_DIGIT_PIN) == 0) || + (((security_required & BTM_SEC_IN_MIN_16_DIGIT_PIN) == BTM_SEC_IN_MIN_16_DIGIT_PIN) && btm_dev_16_digit_authenticated(p_dev_rec))) { + rc = BTM_SUCCESS; + } + } + } + + if ((rc == BTM_SUCCESS) && (security_required & BTM_SEC_MODE4_LEVEL4) && + (p_dev_rec->link_key_type != BTM_LKEY_TYPE_AUTH_COMB_P_256)) { + rc = BTM_CMD_STARTED; + } + + if (rc == BTM_SUCCESS) { + if (p_callback) { + (*p_callback) (bd_addr, transport, (void *)p_ref_data, BTM_SUCCESS); + } + return (BTM_SUCCESS); + } + } + + btm_cb.sec_req_pending = TRUE; + return (BTM_CMD_STARTED); + } + + /* Save pointer to service record */ + p_dev_rec->p_cur_service = p_serv_rec; + + /* Modify security_required in btm_sec_l2cap_access_req for Lisbon */ + if (btm_cb.security_mode == BTM_SEC_MODE_SP || + btm_cb.security_mode == BTM_SEC_MODE_SP_DEBUG || + btm_cb.security_mode == BTM_SEC_MODE_SC) { + if (BTM_SEC_IS_SM4(p_dev_rec->sm4)) { + if (is_originator) { + /* SM4 to SM4 -> always authenticate & encrypt */ + security_required |= (BTM_SEC_OUT_AUTHENTICATE | BTM_SEC_OUT_ENCRYPT); + } else { /* acceptor */ + /* SM4 to SM4: the acceptor needs to make sure the authentication is already done */ + chk_acp_auth_done = TRUE; + /* SM4 to SM4 -> always authenticate & encrypt */ + security_required |= (BTM_SEC_IN_AUTHENTICATE | BTM_SEC_IN_ENCRYPT); + } + } else if (!(BTM_SM4_KNOWN & p_dev_rec->sm4)) { + /* the remote features are not known yet */ + BTM_TRACE_ERROR("%s: (%s) remote features unknown!!sec_flags:0x%02x\n", __FUNCTION__, + (is_originator) ? "initiator" : "acceptor", p_dev_rec->sec_flags); + + p_dev_rec->sm4 |= BTM_SM4_REQ_PEND; + return (BTM_CMD_STARTED); + } + } + + BTM_TRACE_DEBUG ("%s() sm4:0x%x, sec_flags:0x%x, security_required:0x%x chk:%d\n", __func__, + p_dev_rec->sm4, p_dev_rec->sec_flags, security_required, chk_acp_auth_done); + + old_security_required = p_dev_rec->security_required; + old_is_originator = p_dev_rec->is_originator; + p_dev_rec->security_required = security_required; + p_dev_rec->p_ref_data = p_ref_data; + p_dev_rec->is_originator = is_originator; + +#if (L2CAP_UCD_INCLUDED == TRUE) + if ( conn_type & CONNECTION_TYPE_CONNLESS_MASK ) { + p_dev_rec->is_ucd = TRUE; + } else { + p_dev_rec->is_ucd = FALSE; + } +#endif + + /* If there are multiple service records used through the same PSM */ + /* leave security decision for the multiplexor on the top */ +#if (L2CAP_UCD_INCLUDED == TRUE) + if (((btm_sec_find_next_serv (p_serv_rec)) != NULL) + && (!( conn_type & CONNECTION_TYPE_CONNLESS_MASK ))) /* if not UCD */ +#else + if ((btm_sec_find_next_serv (p_serv_rec)) != NULL) +#endif + { + BTM_TRACE_DEBUG ("no next_serv sm4:0x%x, chk:%d\n", p_dev_rec->sm4, chk_acp_auth_done); + if (!BTM_SEC_IS_SM4(p_dev_rec->sm4)) { + BTM_TRACE_EVENT ("Security Manager: l2cap_access_req PSM:%d postponed for multiplexer\n", psm); + /* pre-Lisbon: restore the old settings */ + p_dev_rec->security_required = old_security_required; + p_dev_rec->is_originator = old_is_originator; + + (*p_callback) (bd_addr, transport, p_ref_data, BTM_SUCCESS); + + return (BTM_SUCCESS); + } + } + + /* if the originator is using dynamic PSM in legacy mode, do not start any security process now + * The layer above L2CAP needs to carry out the security requirement after L2CAP connect + * response is received */ + if (is_originator && + ((btm_cb.security_mode == BTM_SEC_MODE_UNDEFINED || + btm_cb.security_mode == BTM_SEC_MODE_NONE || + btm_cb.security_mode == BTM_SEC_MODE_SERVICE || + btm_cb.security_mode == BTM_SEC_MODE_LINK) || + !BTM_SEC_IS_SM4(p_dev_rec->sm4)) && (psm >= 0x1001)) { + BTM_TRACE_EVENT ("dynamic PSM:0x%x in legacy mode - postponed for upper layer\n", psm); + /* restore the old settings */ + p_dev_rec->security_required = old_security_required; + p_dev_rec->is_originator = old_is_originator; + + (*p_callback) (bd_addr, transport, p_ref_data, BTM_SUCCESS); + + return (BTM_SUCCESS); + } + + if (chk_acp_auth_done) { + BTM_TRACE_DEBUG ("(SM4 to SM4) btm_sec_l2cap_access_req rspd. authenticated: x%x, enc: x%x\n", + (p_dev_rec->sec_flags & BTM_SEC_AUTHENTICATED), (p_dev_rec->sec_flags & BTM_SEC_ENCRYPTED)); + /* SM4, but we do not know for sure which level of security we need. + * as long as we have a link key, it's OK */ + if ((0 == (p_dev_rec->sec_flags & BTM_SEC_AUTHENTICATED)) + || (0 == (p_dev_rec->sec_flags & BTM_SEC_ENCRYPTED))) { + rc = BTM_DELAY_CHECK; + /* + 2046 may report HCI_Encryption_Change and L2C Connection Request out of sequence + because of data path issues. Delay this disconnect a little bit + */ + BTM_TRACE_API("%s peer should have initiated security process by now (SM4 to SM4)\n", __func__); + p_dev_rec->p_callback = p_callback; + p_dev_rec->sec_state = BTM_SEC_STATE_DELAY_FOR_ENC; + (*p_callback) (bd_addr, transport, p_ref_data, rc); + + return BTM_SUCCESS; + } + } + + p_dev_rec->p_callback = p_callback; + + if (p_dev_rec->last_author_service_id == BTM_SEC_NO_LAST_SERVICE_ID + || p_dev_rec->last_author_service_id != p_dev_rec->p_cur_service->service_id) { + /* Although authentication and encryption are per connection + ** authorization is per access request. For example when serial connection + ** is up and authorized and client requests to read file (access to other + ** scn), we need to request user's permission again. + */ + p_dev_rec->sec_flags &= ~BTM_SEC_AUTHORIZED; + } + + if (BTM_SEC_IS_SM4(p_dev_rec->sm4)) { + if ((p_dev_rec->security_required & BTM_SEC_MODE4_LEVEL4) && + (p_dev_rec->link_key_type != BTM_LKEY_TYPE_AUTH_COMB_P_256)) { + /* BTM_LKEY_TYPE_AUTH_COMB_P_256 is the only acceptable key in this case */ + if ((p_dev_rec->sec_flags & BTM_SEC_LINK_KEY_KNOWN) != 0) { + p_dev_rec->sm4 |= BTM_SM4_UPGRADE; + } + p_dev_rec->sec_flags &= ~(BTM_SEC_LINK_KEY_KNOWN | BTM_SEC_LINK_KEY_AUTHED | + BTM_SEC_AUTHENTICATED); + BTM_TRACE_DEBUG ("%s: sec_flags:0x%x", __FUNCTION__, p_dev_rec->sec_flags); + } else { + /* If we already have a link key to the connected peer, is it secure enough? */ + btm_sec_check_upgrade(p_dev_rec, is_originator); + } + } + + BTM_TRACE_EVENT ("%s() PSM:%d Handle:%d State:%d Flags: 0x%x Required: 0x%x Service ID:%d\n", + __func__, psm, handle, p_dev_rec->sec_state, p_dev_rec->sec_flags, + p_dev_rec->security_required, p_dev_rec->p_cur_service->service_id); + + if ((rc = btm_sec_execute_procedure (p_dev_rec)) != BTM_CMD_STARTED) { + p_dev_rec->p_callback = NULL; + (*p_callback) (bd_addr, transport, p_dev_rec->p_ref_data, (UINT8)rc); + } + + return (rc); +#else + return BTM_MODE_UNSUPPORTED; +#endif ///SMP_INCLUDED == TRUE +} + +/******************************************************************************* +** +** Function btm_sec_mx_access_request +** +** Description This function is called by all Multiplexing Protocols during +** establishing connection to or from peer device to grant +** permission to establish application connection. +** +** Parameters: bd_addr - Address of the peer device +** psm - L2CAP PSM +** is_originator - TRUE if protocol above L2CAP originates +** connection +** mx_proto_id - protocol ID of the multiplexer +** mx_chan_id - multiplexer channel to reach application +** p_callback - Pointer to callback function called if +** this function returns PENDING after required +** procedures are completed +** p_ref_data - Pointer to any reference data needed by the +** the callback function. +** +** Returns BTM_CMD_STARTED +** +*******************************************************************************/ +tBTM_STATUS btm_sec_mx_access_request (BD_ADDR bd_addr, UINT16 psm, BOOLEAN is_originator, + UINT32 mx_proto_id, UINT32 mx_chan_id, + tBTM_SEC_CALLBACK *p_callback, void *p_ref_data) +{ +#if (SMP_INCLUDED == TRUE) + tBTM_SEC_DEV_REC *p_dev_rec; + tBTM_SEC_SERV_REC *p_serv_rec; + tBTM_STATUS rc; + UINT16 security_required; + BOOLEAN transport = FALSE;/* should check PSM range in LE connection oriented L2CAP connection */ + + BTM_TRACE_DEBUG ("%s() is_originator: %d\n", __func__, is_originator); + /* Find or get oldest record */ + p_dev_rec = btm_find_or_alloc_dev (bd_addr); + + /* Find the service record for the PSM */ + p_serv_rec = btm_sec_find_mx_serv (is_originator, psm, mx_proto_id, mx_chan_id); + + /* If there is no application registered with this PSM do not allow connection */ + if (!p_serv_rec) { + if (p_callback) { + (*p_callback) (bd_addr, transport, p_ref_data, BTM_MODE_UNSUPPORTED); + } + + BTM_TRACE_ERROR ("Security Manager: MX service not found PSM:%d Proto:%d SCN:%d\n", + psm, mx_proto_id, mx_chan_id); + return BTM_NO_RESOURCES; + } + + if ((btm_cb.security_mode == BTM_SEC_MODE_SC) && (!btm_sec_is_serv_level0(psm))) { + security_required = btm_sec_set_serv_level4_flags (p_serv_rec->security_flags, + is_originator); + } else { + security_required = p_serv_rec->security_flags; + } + + /* there are some devices (moto phone) which connects to several services at the same time */ + /* we will process one after another */ + if ( (p_dev_rec->p_callback) || (btm_cb.pairing_state != BTM_PAIR_STATE_IDLE) ) { +#if (BT_USE_TRACES == TRUE && SMP_INCLUDED == TRUE) + BTM_TRACE_EVENT ("%s() service PSM:%d Proto:%d SCN:%d delayed state: %s\n", __func__, + psm, mx_proto_id, mx_chan_id, btm_pair_state_descr(btm_cb.pairing_state)); +#endif ///BT_USE_TRACES == TRUE && SMP_INCLUDED == TRUE + rc = BTM_CMD_STARTED; + + if ((btm_cb.security_mode == BTM_SEC_MODE_UNDEFINED || + btm_cb.security_mode == BTM_SEC_MODE_NONE || + btm_cb.security_mode == BTM_SEC_MODE_SERVICE || + btm_cb.security_mode == BTM_SEC_MODE_LINK) || + (BTM_SM4_KNOWN == p_dev_rec->sm4) || (BTM_SEC_IS_SM4(p_dev_rec->sm4) && + (btm_sec_is_upgrade_possible(p_dev_rec, is_originator) == FALSE))) { + /* legacy mode - local is legacy or local is lisbon/peer is legacy + * or SM4 with no possibility of link key upgrade */ + if (is_originator) { + if (((security_required & BTM_SEC_OUT_FLAGS) == 0) || + ((((security_required & BTM_SEC_OUT_FLAGS) == BTM_SEC_OUT_AUTHENTICATE) && btm_dev_authenticated(p_dev_rec))) || + ((((security_required & BTM_SEC_OUT_FLAGS) == (BTM_SEC_OUT_AUTHENTICATE | BTM_SEC_OUT_ENCRYPT)) && btm_dev_encrypted(p_dev_rec))) + ) { + rc = BTM_SUCCESS; + } + } else { + if (((security_required & BTM_SEC_IN_FLAGS) == 0) || + ((((security_required & BTM_SEC_IN_FLAGS) == BTM_SEC_IN_AUTHENTICATE) && btm_dev_authenticated(p_dev_rec))) || + (((security_required & BTM_SEC_IN_FLAGS) == BTM_SEC_IN_AUTHORIZE) && (btm_dev_authorized(p_dev_rec) || btm_serv_trusted(p_dev_rec, p_serv_rec))) || + (((security_required & BTM_SEC_IN_FLAGS) == (BTM_SEC_IN_AUTHORIZE | BTM_SEC_IN_AUTHENTICATE)) && ((btm_dev_authorized(p_dev_rec) || btm_serv_trusted(p_dev_rec, p_serv_rec)) && btm_dev_authenticated(p_dev_rec))) || + (((security_required & BTM_SEC_IN_FLAGS) == (BTM_SEC_IN_AUTHORIZE | BTM_SEC_IN_ENCRYPT)) && ((btm_dev_authorized(p_dev_rec) || btm_serv_trusted(p_dev_rec, p_serv_rec)) && btm_dev_encrypted(p_dev_rec))) || + ((((security_required & BTM_SEC_IN_FLAGS) == (BTM_SEC_IN_AUTHENTICATE | BTM_SEC_IN_ENCRYPT)) && btm_dev_encrypted(p_dev_rec))) + ) { + // Check for 16 digits (or MITM) + if (((security_required & BTM_SEC_IN_MIN_16_DIGIT_PIN) == 0) || + (((security_required & BTM_SEC_IN_MIN_16_DIGIT_PIN) == BTM_SEC_IN_MIN_16_DIGIT_PIN) && btm_dev_16_digit_authenticated(p_dev_rec))) { + rc = BTM_SUCCESS; + } + } + } + if ((rc == BTM_SUCCESS) && (security_required & BTM_SEC_MODE4_LEVEL4) && + (p_dev_rec->link_key_type != BTM_LKEY_TYPE_AUTH_COMB_P_256)) { + rc = BTM_CMD_STARTED; + } + } + + if (rc == BTM_SUCCESS) { + BTM_TRACE_EVENT("%s: allow to bypass, checking authorization\n", __FUNCTION__); + /* the security in BTM_SEC_IN_FLAGS is fullfilled so far, check the requirements in */ + /* btm_sec_execute_procedure */ + if ((is_originator && (p_serv_rec->security_flags & BTM_SEC_OUT_AUTHORIZE)) || + (!is_originator && (p_serv_rec->security_flags & BTM_SEC_IN_AUTHORIZE))) { + BTM_TRACE_EVENT("%s: still need authorization\n", __FUNCTION__); + rc = BTM_CMD_STARTED; + } + } + + /* Check whether there is a pending security procedure, if so we should always queue */ + /* the new security request */ + if (p_dev_rec->sec_state != BTM_SEC_STATE_IDLE) { + BTM_TRACE_EVENT("%s: There is a pending security procedure\n", __FUNCTION__); + rc = BTM_CMD_STARTED; + } + if (rc == BTM_CMD_STARTED) { + BTM_TRACE_EVENT("%s: call btm_sec_queue_mx_request\n", __FUNCTION__); + btm_sec_queue_mx_request (bd_addr, psm, is_originator, mx_proto_id, + mx_chan_id, p_callback, p_ref_data); + } else { /* rc == BTM_SUCCESS */ + /* access granted */ + if (p_callback) { + (*p_callback) (bd_addr, transport, p_ref_data, (UINT8)rc); + } + } +#if (BT_USE_TRACES == TRUE && SMP_INCLUDED == TRUE) + BTM_TRACE_EVENT("%s: return with rc = 0x%02x in delayed state %s\n", __FUNCTION__, rc, + btm_pair_state_descr(btm_cb.pairing_state)); +#endif ///BT_USE_TRACES == TRUE && SMP_INCLUDED == TRUE + return rc; + } + + if ((!is_originator) && ((security_required & BTM_SEC_MODE4_LEVEL4) || + (btm_cb.security_mode == BTM_SEC_MODE_SC))) { + BOOLEAN local_supports_sc = controller_get_interface()->supports_secure_connections(); + /* acceptor receives service connection establishment Request for */ + /* Secure Connections Only service */ + if (!(local_supports_sc) || !(p_dev_rec->remote_supports_secure_connections)) { + BTM_TRACE_DEBUG("%s: SC only service,local_support_for_sc %d,\n" + "remote_support_for_sc %d: fail pairing\n", __FUNCTION__, + local_supports_sc, p_dev_rec->remote_supports_secure_connections); + + if (p_callback) { + (*p_callback) (bd_addr, transport, (void *)p_ref_data, + BTM_MODE4_LEVEL4_NOT_SUPPORTED); + } + + return (BTM_MODE4_LEVEL4_NOT_SUPPORTED); + } + } + + p_dev_rec->p_cur_service = p_serv_rec; + p_dev_rec->security_required = security_required; + + if (btm_cb.security_mode == BTM_SEC_MODE_SP || + btm_cb.security_mode == BTM_SEC_MODE_SP_DEBUG || + btm_cb.security_mode == BTM_SEC_MODE_SC) { + if (BTM_SEC_IS_SM4(p_dev_rec->sm4)) { + if ((p_dev_rec->security_required & BTM_SEC_MODE4_LEVEL4) && + (p_dev_rec->link_key_type != BTM_LKEY_TYPE_AUTH_COMB_P_256)) { + /* BTM_LKEY_TYPE_AUTH_COMB_P_256 is the only acceptable key in this case */ + if ((p_dev_rec->sec_flags & BTM_SEC_LINK_KEY_KNOWN) != 0) { + p_dev_rec->sm4 |= BTM_SM4_UPGRADE; + } + + p_dev_rec->sec_flags &= ~(BTM_SEC_LINK_KEY_KNOWN | BTM_SEC_LINK_KEY_AUTHED | + BTM_SEC_AUTHENTICATED); + BTM_TRACE_DEBUG("%s: sec_flags:0x%x\n", __FUNCTION__, p_dev_rec->sec_flags); + } else { + /* If we already have a link key, check if that link key is good enough */ + btm_sec_check_upgrade(p_dev_rec, is_originator); + } + } + } + + p_dev_rec->is_originator = is_originator; + p_dev_rec->p_callback = p_callback; + p_dev_rec->p_ref_data = p_ref_data; + + /* Although authentication and encryption are per connection */ + /* authorization is per access request. For example when serial connection */ + /* is up and authorized and client requests to read file (access to other */ + /* scn, we need to request user's permission again. */ + p_dev_rec->sec_flags &= ~(BTM_SEC_AUTHORIZED); + + BTM_TRACE_EVENT ("%s() proto_id:%d chan_id:%d State:%d Flags:0x%x Required:0x%x Service ID:%d\n", + __func__, mx_proto_id, mx_chan_id, p_dev_rec->sec_state, p_dev_rec->sec_flags, + p_dev_rec->security_required, p_dev_rec->p_cur_service->service_id); + + if ((rc = btm_sec_execute_procedure (p_dev_rec)) != BTM_CMD_STARTED) { + if (p_callback) { + p_dev_rec->p_callback = NULL; + (*p_callback) (bd_addr, transport, p_ref_data, (UINT8)rc); + } + } + + return rc; +#else + return BTM_MODE_UNSUPPORTED; +#endif ///SMP_INCLUDED == TRUE +} + +/******************************************************************************* +** +** Function btm_sec_conn_req +** +** Description This function is when the peer device is requesting +** connection +** +** Returns void +** +*******************************************************************************/ +#if (SMP_INCLUDED == TRUE) +void btm_sec_conn_req (UINT8 *bda, UINT8 *dc) +{ + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev (bda); + /* Some device may request a connection before we are done with the HCI_Reset sequence */ + if (!controller_get_interface()->get_is_ready()) { + BTM_TRACE_ERROR ("Security Manager: connect request when device not ready\n"); + btsnd_hcic_reject_conn (bda, HCI_ERR_HOST_REJECT_DEVICE); + return; + } + + /* Security guys wants us not to allow connection from not paired devices */ + + /* Check if connection is allowed for only paired devices */ + if (btm_cb.connect_only_paired) { + if (!p_dev_rec || !(p_dev_rec->sec_flags & BTM_SEC_LINK_KEY_AUTHED)) { + BTM_TRACE_ERROR ("Security Manager: connect request from non-paired device\n"); + btsnd_hcic_reject_conn (bda, HCI_ERR_HOST_REJECT_DEVICE); + return; + } + } + +#if BTM_ALLOW_CONN_IF_NONDISCOVER == FALSE + /* If non-discoverable, only allow known devices to connect */ + if (btm_cb.btm_inq_vars.discoverable_mode == BTM_NON_DISCOVERABLE) { + if (!p_dev_rec) { + BTM_TRACE_ERROR ("Security Manager: connect request from not paired device\n"); + btsnd_hcic_reject_conn (bda, HCI_ERR_HOST_REJECT_DEVICE); + return; + } + } +#endif + + if ((btm_cb.pairing_state != BTM_PAIR_STATE_IDLE) + && (btm_cb.pairing_flags & BTM_PAIR_FLAGS_WE_STARTED_DD) + && (!memcmp (btm_cb.pairing_bda, bda, BD_ADDR_LEN))) { + BTM_TRACE_ERROR ("Security Manager: reject connect request from bonding device\n"); + + /* incoming connection from bonding device is rejected */ + btm_cb.pairing_flags |= BTM_PAIR_FLAGS_REJECTED_CONNECT; + btsnd_hcic_reject_conn (bda, HCI_ERR_HOST_REJECT_DEVICE); + return; + } + + /* Host is not interested or approved connection. Save BDA and DC and */ + /* pass request to L2CAP */ + memcpy (btm_cb.connecting_bda, bda, BD_ADDR_LEN); + memcpy (btm_cb.connecting_dc, dc, DEV_CLASS_LEN); + + if (l2c_link_hci_conn_req (bda)) { + if (!p_dev_rec) { + /* accept the connection -> allocate a device record */ + p_dev_rec = btm_sec_alloc_dev (bda); + } + if (p_dev_rec) { + p_dev_rec->sm4 |= BTM_SM4_CONN_PEND; + } + } +} +#endif ///SMP_INCLUDED == TRUE + +/******************************************************************************* +** +** Function btm_sec_bond_cancel_complete +** +** Description This function is called to report bond cancel complete +** event. +** +** Returns void +** +*******************************************************************************/ +#if (SMP_INCLUDED == TRUE) +static void btm_sec_bond_cancel_complete (void) +{ + tBTM_SEC_DEV_REC *p_dev_rec; + + if ((btm_cb.pairing_flags & BTM_PAIR_FLAGS_DISC_WHEN_DONE) || + (BTM_PAIR_STATE_WAIT_LOCAL_PIN == btm_cb.pairing_state && + BTM_PAIR_FLAGS_WE_STARTED_DD & btm_cb.pairing_flags) || + (btm_cb.pairing_state == BTM_PAIR_STATE_GET_REM_NAME && + BTM_PAIR_FLAGS_WE_CANCEL_DD & btm_cb.pairing_flags)) { + /* for dedicated bonding in legacy mode, authentication happens at "link level" + * btm_sec_connected is called with failed status. + * In theory, the code that handles is_pairing_device/TRUE should clean out security related code. + * However, this function may clean out the security related flags and btm_sec_connected would not know + * this function also needs to do proper clean up. + */ + if ((p_dev_rec = btm_find_dev (btm_cb.pairing_bda)) != NULL) { + p_dev_rec->security_required = BTM_SEC_NONE; + } + btm_sec_change_pairing_state (BTM_PAIR_STATE_IDLE); + + /* Notify application that the cancel succeeded */ + if (btm_cb.api.p_bond_cancel_cmpl_callback) { + btm_cb.api.p_bond_cancel_cmpl_callback(BTM_SUCCESS); + } + } +} +#endif ///SMP_INCLUDED == TRUE + + +/******************************************************************************* +** +** Function btm_create_conn_cancel_complete +** +** Description This function is called when the command complete message +** is received from the HCI for the create connection cancel +** command. +** +** Returns void +** +*******************************************************************************/ +void btm_create_conn_cancel_complete (UINT8 *p) +{ + UINT8 status; + + STREAM_TO_UINT8 (status, p); + //BTM_TRACE_EVENT ("btm_create_conn_cancel_complete(): in State: %s status:%d\n", + // btm_pair_state_descr(btm_cb.pairing_state), status); + + /* if the create conn cancel cmd was issued by the bond cancel, + ** the application needs to be notified that bond cancel succeeded + */ + switch (status) { + case HCI_SUCCESS: +#if (SMP_INCLUDED == TRUE) + btm_sec_bond_cancel_complete(); +#endif ///SMP_INCLUDED == TRUE + break; + case HCI_ERR_CONNECTION_EXISTS: + case HCI_ERR_NO_CONNECTION: + default: + /* Notify application of the error */ + if (btm_cb.api.p_bond_cancel_cmpl_callback) { + btm_cb.api.p_bond_cancel_cmpl_callback(BTM_ERR_PROCESSING); + } + break; + } +} + +/******************************************************************************* +** +** Function btm_sec_check_pending_reqs +** +** Description This function is called at the end of the security procedure +** to let L2CAP and RFCOMM know to re-submit any pending requests +** +** Returns void +** +*******************************************************************************/ +#if (SMP_INCLUDED == TRUE) +void btm_sec_check_pending_reqs (void) +{ + tBTM_SEC_QUEUE_ENTRY *p_e; + fixed_queue_t *bq; + + if (btm_cb.pairing_state == BTM_PAIR_STATE_IDLE) { + /* First, resubmit L2CAP requests */ + if (btm_cb.sec_req_pending) { + btm_cb.sec_req_pending = FALSE; +#if (CLASSIC_BT_INCLUDED == TRUE) + l2cu_resubmit_pending_sec_req (NULL); +#endif ///SMP_INCLUDED == TRUE + } + + /* Now, re-submit anything in the mux queue */ + bq = btm_cb.sec_pending_q; + + btm_cb.sec_pending_q = fixed_queue_new(QUEUE_SIZE_MAX); + + + while ((p_e = (tBTM_SEC_QUEUE_ENTRY *)fixed_queue_dequeue(bq, 0)) != NULL) { + /* Check that the ACL is still up before starting security procedures */ + if (btm_bda_to_acl(p_e->bd_addr, p_e->transport) != NULL) { + if (p_e->psm != 0) { + BTM_TRACE_EVENT("%s PSM:0x%04x Is_Orig:%u mx_proto_id:%u mx_chan_id:%u\n", + __FUNCTION__, p_e->psm, p_e->is_orig, + p_e->mx_proto_id, p_e->mx_chan_id); + + btm_sec_mx_access_request (p_e->bd_addr, p_e->psm, p_e->is_orig, + p_e->mx_proto_id, p_e->mx_chan_id, + p_e->p_callback, p_e->p_ref_data); + } else { + BTM_SetEncryption(p_e->bd_addr, p_e->transport, p_e->p_callback, + p_e->p_ref_data); + } + } + + osi_free (p_e); + } + fixed_queue_free(bq, NULL); + } +} +#endif ///SMP_INCLUDED == TRUE + + +/******************************************************************************* +** +** Function btm_sec_init +** +** Description This function is on the SEC startup +** +** Returns void +** +*******************************************************************************/ +#if (SMP_INCLUDED == TRUE) +void btm_sec_init (UINT8 sec_mode) +{ + btm_cb.security_mode = sec_mode; + memset (btm_cb.pairing_bda, 0xff, BD_ADDR_LEN); + btm_cb.max_collision_delay = BTM_SEC_MAX_COLLISION_DELAY; +} +#endif ///SMP_INCLUDED == TRUE + +/******************************************************************************* +** +** Function btm_sec_device_down +** +** Description This function should be called when device is disabled or +** turned off +** +** Returns void +** +*******************************************************************************/ +#if (SMP_INCLUDED == TRUE) +void btm_sec_device_down (void) +{ +#if (BT_USE_TRACES == TRUE && SMP_INCLUDED == TRUE) + BTM_TRACE_EVENT ("%s() State: %s\n", __func__, btm_pair_state_descr(btm_cb.pairing_state)); +#endif ///BT_USE_TRACES == TRUE && SMP_INCLUDED == TRUE + btm_sec_change_pairing_state (BTM_PAIR_STATE_IDLE); +} +#endif ///SMP_INCLUDED == TRUE + + +/******************************************************************************* +** +** Function btm_sec_dev_reset +** +** Description This function should be called after device reset +** +** Returns void +** +*******************************************************************************/ +void btm_sec_dev_reset (void) +{ + if (controller_get_interface()->supports_simple_pairing()) { + /* set the default IO capabilities */ + btm_cb.devcb.loc_io_caps = BTM_LOCAL_IO_CAPS; + /* add mx service to use no security */ + BTM_SetSecurityLevel(FALSE, "RFC_MUX\n", BTM_SEC_SERVICE_RFC_MUX, + BTM_SEC_NONE, BT_PSM_RFCOMM, BTM_SEC_PROTO_RFCOMM, 0); + } else { + btm_cb.security_mode = BTM_SEC_MODE_SERVICE; + } + + BTM_TRACE_DEBUG ("btm_sec_dev_reset sec mode: %d\n", btm_cb.security_mode); +} + +/******************************************************************************* +** +** Function btm_sec_abort_access_req +** +** Description This function is called by the L2CAP or RFCOMM to abort +** the pending operation. +** +** Parameters: bd_addr - Address of the peer device +** +** Returns void +** +*******************************************************************************/ +void btm_sec_abort_access_req (BD_ADDR bd_addr) +{ + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev (bd_addr); + + if (!p_dev_rec) { + return; + } + + if ((p_dev_rec->sec_state != BTM_SEC_STATE_AUTHORIZING) + && (p_dev_rec->sec_state != BTM_SEC_STATE_AUTHENTICATING)) { + return; + } + + p_dev_rec->sec_state = BTM_SEC_STATE_IDLE; + p_dev_rec->p_callback = NULL; +} + +/******************************************************************************* +** +** Function btm_sec_dd_create_conn +** +** Description This function is called to create the ACL connection for +** the dedicated boding process +** +** Returns void +** +*******************************************************************************/ +#if (SMP_INCLUDED == TRUE) +static tBTM_STATUS btm_sec_dd_create_conn (tBTM_SEC_DEV_REC *p_dev_rec) +{ + tL2C_LCB *p_lcb = l2cu_find_lcb_by_bd_addr(p_dev_rec->bd_addr, BT_TRANSPORT_BR_EDR); + if (p_lcb && (p_lcb->link_state == LST_CONNECTED || p_lcb->link_state == LST_CONNECTING)) { + BTM_TRACE_WARNING("%s Connection already exists\n", __func__); + return BTM_CMD_STARTED; + } + + /* Make sure an L2cap link control block is available */ + if (!p_lcb && (p_lcb = l2cu_allocate_lcb (p_dev_rec->bd_addr, TRUE, BT_TRANSPORT_BR_EDR)) == NULL) { + BTM_TRACE_WARNING ("Security Manager: failed allocate LCB [%02x%02x%02x%02x%02x%02x]\n", + p_dev_rec->bd_addr[0], p_dev_rec->bd_addr[1], p_dev_rec->bd_addr[2], + p_dev_rec->bd_addr[3], p_dev_rec->bd_addr[4], p_dev_rec->bd_addr[5]); + + return (BTM_NO_RESOURCES); + } + + /* set up the control block to indicated dedicated bonding */ + btm_cb.pairing_flags |= BTM_PAIR_FLAGS_DISC_WHEN_DONE; + + if (l2cu_create_conn(p_lcb, BT_TRANSPORT_BR_EDR) == FALSE) { + BTM_TRACE_WARNING ("Security Manager: failed create [%02x%02x%02x%02x%02x%02x]\n", + p_dev_rec->bd_addr[0], p_dev_rec->bd_addr[1], p_dev_rec->bd_addr[2], + p_dev_rec->bd_addr[3], p_dev_rec->bd_addr[4], p_dev_rec->bd_addr[5]); + + l2cu_release_lcb(p_lcb); + return (BTM_NO_RESOURCES); + } + + btm_acl_update_busy_level (BTM_BLI_PAGE_EVT); + + BTM_TRACE_DEBUG ("Security Manager: btm_sec_dd_create_conn [%02x%02x%02x%02x%02x%02x]\n", + p_dev_rec->bd_addr[0], p_dev_rec->bd_addr[1], p_dev_rec->bd_addr[2], + p_dev_rec->bd_addr[3], p_dev_rec->bd_addr[4], p_dev_rec->bd_addr[5]); + + btm_sec_change_pairing_state (BTM_PAIR_STATE_WAIT_PIN_REQ); + return (BTM_CMD_STARTED); +} +#endif ///SMP_INCLUDED == TRUE + +/******************************************************************************* +** +** Function btm_sec_rmt_name_request_complete +** +** Description This function is called when remote name was obtained from +** the peer device +** +** Returns void +** +*******************************************************************************/ +#if (SMP_INCLUDED == TRUE) +void btm_sec_rmt_name_request_complete (UINT8 *p_bd_addr, UINT8 *p_bd_name, UINT8 status) +{ + tBTM_SEC_DEV_REC *p_dev_rec = NULL; + int i; + DEV_CLASS dev_class; + UINT8 old_sec_state; + UINT8 res; + + BTM_TRACE_EVENT ("btm_sec_rmt_name_request_complete\n"); + if (((p_bd_addr == NULL) && !BTM_ACL_IS_CONNECTED(btm_cb.connecting_bda)) + || ((p_bd_addr != NULL) && !BTM_ACL_IS_CONNECTED(p_bd_addr))) { + btm_acl_resubmit_page(); + } + + /* If remote name request failed, p_bd_addr is null and we need to search */ + /* based on state assuming that we are doing 1 at a time */ + if (p_bd_addr) { + p_dev_rec = btm_find_dev (p_bd_addr); + } else { + list_node_t *p_node = NULL; + for (p_node = list_begin(btm_cb.p_sec_dev_rec_list); p_node; p_node = list_next(p_node)) { + p_dev_rec = list_node(p_node); + if ((p_dev_rec->sec_flags & BTM_SEC_IN_USE) + && (p_dev_rec->sec_state == BTM_SEC_STATE_GETTING_NAME)) { + p_bd_addr = p_dev_rec->bd_addr; + break; + } + } + if (!p_bd_addr) { + p_dev_rec = NULL; + } + } + + + /* Commenting out trace due to obf/compilation problems. + */ +#if (BT_USE_TRACES == TRUE && SMP_INCLUDED == TRUE) + if (!p_bd_name) { + p_bd_name = (UINT8 *)""; + } + + if (p_dev_rec) { + BTM_TRACE_EVENT ("Security Manager: rmt_name_complete PairState: %s RemName: %s status: %d State:%d p_dev_rec: %p \n", + btm_pair_state_descr (btm_cb.pairing_state), p_bd_name, + status, p_dev_rec->sec_state, p_dev_rec); + } else { + BTM_TRACE_EVENT ("Security Manager: rmt_name_complete PairState: %s RemName: %s status: %d\n", + btm_pair_state_descr (btm_cb.pairing_state), p_bd_name, + status); + } +#endif + + if (p_dev_rec) { + old_sec_state = p_dev_rec->sec_state; + if (status == HCI_SUCCESS) { + BCM_STRNCPY_S ((char *)p_dev_rec->sec_bd_name, (char *)p_bd_name, BTM_MAX_REM_BD_NAME_LEN); + p_dev_rec->sec_bd_name[BTM_MAX_REM_BD_NAME_LEN] = '\0'; + p_dev_rec->sec_flags |= BTM_SEC_NAME_KNOWN; + BTM_TRACE_EVENT ("setting BTM_SEC_NAME_KNOWN sec_flags:0x%x\n", p_dev_rec->sec_flags); + } else { + /* Notify all clients waiting for name to be resolved even if it failed so clients can continue */ + p_dev_rec->sec_bd_name[0] = '\0'; + } + + if (p_dev_rec->sec_state == BTM_SEC_STATE_GETTING_NAME) { + p_dev_rec->sec_state = BTM_SEC_STATE_IDLE; + } + + /* Notify all clients waiting for name to be resolved */ + for (i = 0; i < BTM_SEC_MAX_RMT_NAME_CALLBACKS; i++) { + if (btm_cb.p_rmt_name_callback[i] && p_bd_addr) { + (*btm_cb.p_rmt_name_callback[i])(p_bd_addr, p_dev_rec->dev_class, + p_dev_rec->sec_bd_name); + } + } + } else { + dev_class[0] = 0; + dev_class[1] = 0; + dev_class[2] = 0; + + /* Notify all clients waiting for name to be resolved even if not found so clients can continue */ + for (i = 0; i < BTM_SEC_MAX_RMT_NAME_CALLBACKS; i++) { + if (btm_cb.p_rmt_name_callback[i] && p_bd_addr) { + (*btm_cb.p_rmt_name_callback[i])(p_bd_addr, dev_class, (UINT8 *)""); + } + } + + return; + } + + /* If we were delaying asking UI for a PIN because name was not resolved, ask now */ + if ( (btm_cb.pairing_state == BTM_PAIR_STATE_WAIT_LOCAL_PIN) && p_bd_addr + && (memcmp (btm_cb.pairing_bda, p_bd_addr, BD_ADDR_LEN) == 0) ) { + BTM_TRACE_EVENT ("btm_sec_rmt_name_request_complete() delayed pin now being requested flags:0x%x, (p_pin_callback=%p)\n", btm_cb.pairing_flags, btm_cb.api.p_pin_callback); + + if (((btm_cb.pairing_flags & BTM_PAIR_FLAGS_WE_STARTED_DD) == 0) && + ((btm_cb.pairing_flags & BTM_PAIR_FLAGS_PIN_REQD) == 0) && + btm_cb.api.p_pin_callback) { + BTM_TRACE_EVENT ("btm_sec_rmt_name_request_complete() calling pin_callback\n"); + btm_cb.pairing_flags |= BTM_PAIR_FLAGS_PIN_REQD; + (*btm_cb.api.p_pin_callback) (p_dev_rec->bd_addr, p_dev_rec->dev_class, p_bd_name, + (p_dev_rec->p_cur_service == NULL) ? FALSE + : (p_dev_rec->p_cur_service->security_flags & BTM_SEC_IN_MIN_16_DIGIT_PIN)); + } + + /* Set the same state again to force the timer to be restarted */ + btm_sec_change_pairing_state (BTM_PAIR_STATE_WAIT_LOCAL_PIN); + return; + } + + /* Check if we were delaying bonding because name was not resolved */ + if ( btm_cb.pairing_state == BTM_PAIR_STATE_GET_REM_NAME) { + if (p_bd_addr && memcmp (btm_cb.pairing_bda, p_bd_addr, BD_ADDR_LEN) == 0) { + BTM_TRACE_EVENT ("btm_sec_rmt_name_request_complete() continue bonding sm4: 0x%04x, status:0x%x\n", p_dev_rec->sm4, status); + if (btm_cb.pairing_flags & BTM_PAIR_FLAGS_WE_CANCEL_DD) { + btm_sec_bond_cancel_complete(); + return; + } + + if (status != HCI_SUCCESS) { + btm_sec_change_pairing_state (BTM_PAIR_STATE_IDLE); + + if (btm_cb.api.p_auth_complete_callback) + (*btm_cb.api.p_auth_complete_callback) (p_dev_rec->bd_addr, p_dev_rec->dev_class, + p_dev_rec->sec_bd_name, status); + return; + } + + /* if peer is very old legacy devices, HCI_RMT_HOST_SUP_FEAT_NOTIFY_EVT is not reported */ + if (BTM_SEC_IS_SM4_UNKNOWN(p_dev_rec->sm4)) { + /* set the KNOWN flag only if BTM_PAIR_FLAGS_REJECTED_CONNECT is not set.*/ + /* If it is set, there may be a race condition */ + BTM_TRACE_DEBUG ("btm_sec_rmt_name_request_complete IS_SM4_UNKNOWN Flags:0x%04x\n", + btm_cb.pairing_flags); + if ((btm_cb.pairing_flags & BTM_PAIR_FLAGS_REJECTED_CONNECT) == 0) { + p_dev_rec->sm4 |= BTM_SM4_KNOWN; + } + } + + BTM_TRACE_DEBUG("%s, SM4 Value: %x, Legacy:%d,IS SM4:%d, Unknown:%d\n", __FUNCTION__, + p_dev_rec->sm4, BTM_SEC_IS_SM4_LEGACY(p_dev_rec->sm4), + BTM_SEC_IS_SM4(p_dev_rec->sm4), BTM_SEC_IS_SM4_UNKNOWN(p_dev_rec->sm4)); + + /* BT 2.1 or carkit, bring up the connection to force the peer to request PIN. + ** Else prefetch (btm_sec_check_prefetch_pin will do the prefetching if needed) + */ + if ((p_dev_rec->sm4 != BTM_SM4_KNOWN) || !btm_sec_check_prefetch_pin(p_dev_rec)) { + /* if we rejected incoming connection request, we have to wait HCI_Connection_Complete event */ + /* before originating */ + if (btm_cb.pairing_flags & BTM_PAIR_FLAGS_REJECTED_CONNECT) { + BTM_TRACE_WARNING ("btm_sec_rmt_name_request_complete: waiting HCI_Connection_Complete after rejecting connection\n"); + } + /* Both we and the peer are 2.1 - continue to create connection */ + else if (btm_sec_dd_create_conn(p_dev_rec) != BTM_CMD_STARTED) { + BTM_TRACE_WARNING ("btm_sec_rmt_name_request_complete: failed to start connection\n"); + + btm_sec_change_pairing_state (BTM_PAIR_STATE_IDLE); + + if (btm_cb.api.p_auth_complete_callback) { + (*btm_cb.api.p_auth_complete_callback) (p_dev_rec->bd_addr, p_dev_rec->dev_class, + p_dev_rec->sec_bd_name, HCI_ERR_MEMORY_FULL); + } + } + } + return; + } else { + BTM_TRACE_WARNING ("btm_sec_rmt_name_request_complete: wrong BDA, retry with pairing BDA\n"); + + BTM_ReadRemoteDeviceName (btm_cb.pairing_bda, NULL, BT_TRANSPORT_BR_EDR); + return; + } + } + + /* check if we were delaying link_key_callback because name was not resolved */ + if (p_dev_rec->link_key_not_sent) { + /* If HCI connection complete has not arrived, wait for it */ + if (p_dev_rec->hci_handle == BTM_SEC_INVALID_HANDLE) { + return; + } + + p_dev_rec->link_key_not_sent = FALSE; + btm_send_link_key_notif(p_dev_rec); + + /* If its not us who perform authentication, we should tell stackserver */ + /* that some authentication has been completed */ + /* This is required when different entities receive link notification and auth complete */ + if (!(p_dev_rec->security_required & BTM_SEC_OUT_AUTHENTICATE)) { + if (btm_cb.api.p_auth_complete_callback) { + res = (*btm_cb.api.p_auth_complete_callback) (p_dev_rec->bd_addr, + p_dev_rec->dev_class, + p_dev_rec->sec_bd_name, HCI_SUCCESS); + if (res == BTM_SEC_DEV_REC_REMOVED) { + p_dev_rec = NULL; + } + } + + } + } + + if(!p_dev_rec) { + return; + } + + /* If this is a bonding procedure can disconnect the link now */ + if ((btm_cb.pairing_flags & BTM_PAIR_FLAGS_WE_STARTED_DD) + && (p_dev_rec->sec_flags & BTM_SEC_AUTHENTICATED)) { + BTM_TRACE_WARNING ("btm_sec_rmt_name_request_complete (none/ce)\n"); + p_dev_rec->security_required &= ~(BTM_SEC_OUT_AUTHENTICATE); + l2cu_start_post_bond_timer(p_dev_rec->hci_handle); + return; + } + + if (old_sec_state != BTM_SEC_STATE_GETTING_NAME) { + return; + } + + /* If get name failed, notify the waiting layer */ + if (status != HCI_SUCCESS) { + btm_sec_dev_rec_cback_event (p_dev_rec, BTM_ERR_PROCESSING, FALSE); + return; + } + + if (p_dev_rec->sm4 & BTM_SM4_REQ_PEND) { + BTM_TRACE_EVENT ("waiting for remote features!!\n"); + return; + } + + /* Remote Name succeeded, execute the next security procedure, if any */ + status = (UINT8)btm_sec_execute_procedure (p_dev_rec); + + /* If result is pending reply from the user or from the device is pending */ + if (status == BTM_CMD_STARTED) { + return; + } + + /* There is no next procedure or start of procedure failed, notify the waiting layer */ + btm_sec_dev_rec_cback_event (p_dev_rec, status, FALSE); +} +#endif ///SMP_INCLUDED == TRUE + + +/******************************************************************************* +** +** Function btm_sec_rmt_host_support_feat_evt +** +** Description This function is called when the +** HCI_RMT_HOST_SUP_FEAT_NOTIFY_EVT is received +** +** Returns void +** +*******************************************************************************/ +void btm_sec_rmt_host_support_feat_evt (UINT8 *p) +{ + tBTM_SEC_DEV_REC *p_dev_rec; + BD_ADDR bd_addr; /* peer address */ + BD_FEATURES features; + + STREAM_TO_BDADDR (bd_addr, p); + p_dev_rec = btm_find_or_alloc_dev (bd_addr); + + BTM_TRACE_EVENT ("btm_sec_rmt_host_support_feat_evt sm4: 0x%x p[0]: 0x%x\n", p_dev_rec->sm4, p[0]); + + if (BTM_SEC_IS_SM4_UNKNOWN(p_dev_rec->sm4)) { + p_dev_rec->sm4 = BTM_SM4_KNOWN; + STREAM_TO_ARRAY(features, p, HCI_FEATURE_BYTES_PER_PAGE); + if (HCI_SSP_HOST_SUPPORTED(features)) { + p_dev_rec->sm4 = BTM_SM4_TRUE; + } + BTM_TRACE_EVENT ("btm_sec_rmt_host_support_feat_evt sm4: 0x%x features[0]: 0x%x\n", p_dev_rec->sm4, features[0]); + } +} + +/******************************************************************************* +** +** Function btm_io_capabilities_req +** +** Description This function is called when LM request for the IO +** capability of the local device and +** if the OOB data is present for the device in the event +** +** Returns void +** +*******************************************************************************/ +#if (SMP_INCLUDED == TRUE) +void btm_io_capabilities_req (UINT8 *p) +{ + tBTM_SP_IO_REQ evt_data; + UINT8 err_code = 0; + tBTM_SEC_DEV_REC *p_dev_rec; + BOOLEAN is_orig = TRUE; + UINT8 callback_rc = BTM_SUCCESS; + + STREAM_TO_BDADDR (evt_data.bd_addr, p); + + /* setup the default response according to compile options */ + /* assume that the local IO capability does not change + * loc_io_caps is initialized with the default value */ + evt_data.io_cap = btm_cb.devcb.loc_io_caps; + evt_data.oob_data = BTM_OOB_NONE; + evt_data.auth_req = BTM_DEFAULT_AUTH_REQ; +#if (BT_USE_TRACES == TRUE && SMP_INCLUDED == TRUE) + BTM_TRACE_EVENT("%s: State: %s\n", __FUNCTION__, btm_pair_state_descr(btm_cb.pairing_state)); +#endif ///BT_USE_TRACES == TRUE && SMP_INCLUDED == TRUE + p_dev_rec = btm_find_or_alloc_dev (evt_data.bd_addr); + + BTM_TRACE_DEBUG("%s:Security mode: %d, Num Read Remote Feat pages: %d\n", __FUNCTION__, + btm_cb.security_mode, p_dev_rec->num_read_pages); + + if ((btm_cb.security_mode == BTM_SEC_MODE_SC) && (p_dev_rec->num_read_pages == 0)) { + BTM_TRACE_EVENT("%s: Device security mode is SC only.\n" + "To continue need to know remote features.\n", __FUNCTION__); + + p_dev_rec->remote_features_needed = TRUE; + return; + } + + p_dev_rec->sm4 |= BTM_SM4_TRUE; +#if (BT_USE_TRACES == TRUE && SMP_INCLUDED == TRUE) + BTM_TRACE_EVENT("%s: State: %s Flags: 0x%04x p_cur_service: %p\n", + __FUNCTION__, btm_pair_state_descr(btm_cb.pairing_state), + btm_cb.pairing_flags, p_dev_rec->p_cur_service); +#endif ///BT_USE_TRACES == TRUE && SMP_INCLUDED == TRUE + if (p_dev_rec->p_cur_service) { + BTM_TRACE_EVENT("%s: cur_service psm: 0x%04x, security_flags: 0x%04x\n", + __FUNCTION__, p_dev_rec->p_cur_service->psm, + p_dev_rec->p_cur_service->security_flags); + } + + switch (btm_cb.pairing_state) { + /* initiator connecting */ + case BTM_PAIR_STATE_IDLE: + //TODO: Handle Idle pairing state + //security_required = p_dev_rec->security_required; + break; + + /* received IO capability response already->acceptor */ + case BTM_PAIR_STATE_INCOMING_SSP: + is_orig = FALSE; + + if (btm_cb.pairing_flags & BTM_PAIR_FLAGS_PEER_STARTED_DD) { + /* acceptor in dedicated bonding */ + evt_data.auth_req = BTM_DEFAULT_DD_AUTH_REQ; + } + break; + + /* initiator, at this point it is expected to be dedicated bonding + initiated by local device */ + case BTM_PAIR_STATE_WAIT_PIN_REQ: + if (!memcmp (evt_data.bd_addr, btm_cb.pairing_bda, BD_ADDR_LEN)) { + evt_data.auth_req = BTM_DEFAULT_DD_AUTH_REQ; + } else { + err_code = HCI_ERR_HOST_BUSY_PAIRING; + } + break; + + /* any other state is unexpected */ + default: + err_code = HCI_ERR_HOST_BUSY_PAIRING; + BTM_TRACE_ERROR("%s: Unexpected Pairing state received %d\n", __FUNCTION__, + btm_cb.pairing_state); + break; + } + + if (btm_cb.pairing_disabled) { + /* pairing is not allowed */ + BTM_TRACE_DEBUG("%s: Pairing is not allowed -> fail pairing.\n", __FUNCTION__); + err_code = HCI_ERR_PAIRING_NOT_ALLOWED; + } else if (btm_cb.security_mode == BTM_SEC_MODE_SC) { + BOOLEAN local_supports_sc = controller_get_interface()->supports_secure_connections(); + /* device in Secure Connections Only mode */ + if (!(local_supports_sc) || !(p_dev_rec->remote_supports_secure_connections)) { + BTM_TRACE_DEBUG("%s: SC only service, local_support_for_sc %d,\n" + " remote_support_for_sc 0x%02x -> fail pairing\n", __FUNCTION__, + local_supports_sc, p_dev_rec->remote_supports_secure_connections); + + err_code = HCI_ERR_PAIRING_NOT_ALLOWED; + } + } + + if (err_code != 0) { + /* coverity[uninit_use_in_call] + Event uninit_use_in_call: Using uninitialized element of array "evt_data.bd_addr" in call to function "memcmp" + False-positive: evt_data.bd_addr is set at the beginning with: STREAM_TO_BDADDR (evt_data.bd_addr, p); + */ + btsnd_hcic_io_cap_req_neg_reply(evt_data.bd_addr, err_code); + return; + } + + evt_data.is_orig = is_orig; + + if (is_orig) { + /* local device initiated the pairing non-bonding -> use p_cur_service */ + if (!(btm_cb.pairing_flags & BTM_PAIR_FLAGS_WE_STARTED_DD) && + p_dev_rec->p_cur_service && + (p_dev_rec->p_cur_service->security_flags & BTM_SEC_OUT_AUTHENTICATE)) { + if (btm_cb.security_mode == BTM_SEC_MODE_SC) { + /* SC only mode device requires MITM protection */ + evt_data.auth_req = BTM_AUTH_SP_YES; + } else { + evt_data.auth_req = (p_dev_rec->p_cur_service->security_flags & + BTM_SEC_OUT_MITM) ? BTM_AUTH_SP_YES : BTM_AUTH_SP_NO; + } + } + } + + /* Notify L2CAP to increase timeout */ + l2c_pin_code_request (evt_data.bd_addr); + + memcpy (btm_cb.pairing_bda, evt_data.bd_addr, BD_ADDR_LEN); + + /* coverity[uninit_use_in_call] + Event uninit_use_in_call: Using uninitialized element of array "evt_data.bd_addr" in call to function "memcmp" + False-positive: False-positive: evt_data.bd_addr is set at the beginning with: STREAM_TO_BDADDR (evt_data.bd_addr, p); + */ + if (!memcmp (evt_data.bd_addr, btm_cb.connecting_bda, BD_ADDR_LEN)) { + memcpy (p_dev_rec->dev_class, btm_cb.connecting_dc, DEV_CLASS_LEN); + } + + btm_sec_change_pairing_state (BTM_PAIR_STATE_WAIT_LOCAL_IOCAPS); + + callback_rc = BTM_SUCCESS; + if (p_dev_rec->sm4 & BTM_SM4_UPGRADE) { + p_dev_rec->sm4 &= ~BTM_SM4_UPGRADE; + + /* link key upgrade: always use SPGB_YES - assuming we want to save the link key */ + evt_data.auth_req = BTM_AUTH_SPGB_YES; + } else if (btm_cb.api.p_sp_callback) { + /* the callback function implementation may change the IO capability... */ + callback_rc = (*btm_cb.api.p_sp_callback) (BTM_SP_IO_REQ_EVT, (tBTM_SP_EVT_DATA *)&evt_data); + } + +#if BTM_OOB_INCLUDED == TRUE + if ((callback_rc == BTM_SUCCESS) || (BTM_OOB_UNKNOWN != evt_data.oob_data)) +#else + if (callback_rc == BTM_SUCCESS) +#endif + { + if ((btm_cb.pairing_flags & BTM_PAIR_FLAGS_WE_STARTED_DD)) { + evt_data.auth_req = (BTM_AUTH_DD_BOND | (evt_data.auth_req & BTM_AUTH_YN_BIT)); + } + + if (btm_cb.security_mode == BTM_SEC_MODE_SC) { + /* At this moment we know that both sides are SC capable, device in */ + /* SC only mode requires MITM for any service so let's set MITM bit */ + evt_data.auth_req |= BTM_AUTH_YN_BIT; + BTM_TRACE_DEBUG("%s: for device in \"SC only\" mode set auth_req to 0x%02x\n", + __FUNCTION__, evt_data.auth_req); + } + + /* if the user does not indicate "reply later" by setting the oob_data to unknown */ + /* send the response right now. Save the current IO capability in the control block */ + btm_cb.devcb.loc_auth_req = evt_data.auth_req; + btm_cb.devcb.loc_io_caps = evt_data.io_cap; +#if (BT_USE_TRACES == TRUE && SMP_INCLUDED == TRUE) + BTM_TRACE_EVENT("%s: State: %s IO_CAP:%d oob_data:%d auth_req:%d", + __FUNCTION__, btm_pair_state_descr(btm_cb.pairing_state), evt_data.io_cap, + evt_data.oob_data, evt_data.auth_req); +#endif ///BT_USE_TRACES == TRUE && SMP_INCLUDED == TRUE + btsnd_hcic_io_cap_req_reply(evt_data.bd_addr, evt_data.io_cap, + evt_data.oob_data, evt_data.auth_req); + } +} + +/******************************************************************************* +** +** Function btm_io_capabilities_rsp +** +** Description This function is called when the IO capability of the +** specified device is received +** +** Returns void +** +*******************************************************************************/ +void btm_io_capabilities_rsp (UINT8 *p) +{ + tBTM_SEC_DEV_REC *p_dev_rec; + tBTM_SP_IO_RSP evt_data; + + STREAM_TO_BDADDR (evt_data.bd_addr, p); + STREAM_TO_UINT8 (evt_data.io_cap, p); + STREAM_TO_UINT8 (evt_data.oob_data, p); + STREAM_TO_UINT8 (evt_data.auth_req, p); + + /* Allocate a new device record or reuse the oldest one */ + p_dev_rec = btm_find_or_alloc_dev (evt_data.bd_addr); + + /* If no security is in progress, this indicates incoming security */ + if (btm_cb.pairing_state == BTM_PAIR_STATE_IDLE) { + memcpy (btm_cb.pairing_bda, evt_data.bd_addr, BD_ADDR_LEN); + + btm_sec_change_pairing_state (BTM_PAIR_STATE_INCOMING_SSP); + + /* Make sure we reset the trusted mask to help against attacks */ + BTM_SEC_CLR_TRUSTED_DEVICE(p_dev_rec->trusted_mask); + + /* work around for FW bug */ + btm_inq_stop_on_ssp(); + } + + /* Notify L2CAP to increase timeout */ + l2c_pin_code_request (evt_data.bd_addr); + + /* We must have a device record here. + * Use the connecting device's CoD for the connection */ + /* coverity[uninit_use_in_call] + Event uninit_use_in_call: Using uninitialized element of array "evt_data.bd_addr" in call to function "memcmp" + FALSE-POSITIVE error from Coverity test-tool. evt_data.bd_addr is set at the beginning with: STREAM_TO_BDADDR (evt_data.bd_addr, p); + */ + if (!memcmp (evt_data.bd_addr, btm_cb.connecting_bda, BD_ADDR_LEN)) { + memcpy (p_dev_rec->dev_class, btm_cb.connecting_dc, DEV_CLASS_LEN); + } + + /* peer sets dedicated bonding bit and we did not initiate dedicated bonding */ + if (btm_cb.pairing_state == BTM_PAIR_STATE_INCOMING_SSP /* peer initiated bonding */ + && (evt_data.auth_req & BTM_AUTH_DD_BOND) ) { /* and dedicated bonding bit is set */ + btm_cb.pairing_flags |= BTM_PAIR_FLAGS_PEER_STARTED_DD; + } + + /* save the IO capability in the device record */ + p_dev_rec->rmt_io_caps = evt_data.io_cap; + p_dev_rec->rmt_auth_req = evt_data.auth_req; + + if (btm_cb.api.p_sp_callback) { + (*btm_cb.api.p_sp_callback) (BTM_SP_IO_RSP_EVT, (tBTM_SP_EVT_DATA *)&evt_data); + } +} + +/******************************************************************************* +** +** Function btm_proc_sp_req_evt +** +** Description This function is called to process/report +** HCI_USER_CONFIRMATION_REQUEST_EVT +** or HCI_USER_PASSKEY_REQUEST_EVT +** or HCI_USER_PASSKEY_NOTIFY_EVT +** +** Returns void +** +*******************************************************************************/ +#if (CLASSIC_BT_INCLUDED == TRUE) +void btm_proc_sp_req_evt (tBTM_SP_EVT event, UINT8 *p) +{ + tBTM_STATUS status = BTM_ERR_PROCESSING; + tBTM_SP_EVT_DATA evt_data; + UINT8 *p_bda = evt_data.cfm_req.bd_addr; + tBTM_SEC_DEV_REC *p_dev_rec; + + /* All events start with bd_addr */ + STREAM_TO_BDADDR (p_bda, p); +#if (BT_USE_TRACES == TRUE && SMP_INCLUDED == TRUE) + BTM_TRACE_EVENT ("btm_proc_sp_req_evt() BDA: %08x%04x event: 0x%x, State: %s\n", + (p_bda[0] << 24) + (p_bda[1] << 16) + (p_bda[2] << 8) + p_bda[3], (p_bda[4] << 8) + p_bda[5], + event, btm_pair_state_descr(btm_cb.pairing_state)); +#endif ///BT_USE_TRACES == TRUE && SMP_INCLUDED == TRUE + if ( ((p_dev_rec = btm_find_dev (p_bda)) != NULL) + && (btm_cb.pairing_state != BTM_PAIR_STATE_IDLE) + && (memcmp (btm_cb.pairing_bda, p_bda, BD_ADDR_LEN) == 0) ) { + memcpy (evt_data.cfm_req.bd_addr, p_dev_rec->bd_addr, BD_ADDR_LEN); + memcpy (evt_data.cfm_req.dev_class, p_dev_rec->dev_class, DEV_CLASS_LEN); + + BCM_STRCPY_S ((char *)evt_data.cfm_req.bd_name,(char *)p_dev_rec->sec_bd_name); + + switch (event) { + case BTM_SP_CFM_REQ_EVT: + /* Numeric confirmation. Need user to conf the passkey */ + btm_sec_change_pairing_state (BTM_PAIR_STATE_WAIT_NUMERIC_CONFIRM); + + /* The device record must be allocated in the "IO cap exchange" step */ + STREAM_TO_UINT32 (evt_data.cfm_req.num_val, p); + + evt_data.cfm_req.just_works = TRUE; + + /* process user confirm req in association with the auth_req param */ +// #if (BTM_LOCAL_IO_CAPS == BTM_IO_CAP_IO) + if ( (p_dev_rec->rmt_io_caps == BTM_IO_CAP_IO) + && (btm_cb.devcb.loc_io_caps == BTM_IO_CAP_IO) + && ((p_dev_rec->rmt_auth_req & BTM_AUTH_SP_YES) || (btm_cb.devcb.loc_auth_req & BTM_AUTH_SP_YES)) ) { + /* Both devices are DisplayYesNo and one or both devices want to authenticate + -> use authenticated link key */ + evt_data.cfm_req.just_works = FALSE; + } +// #endif + BTM_TRACE_DEBUG ("btm_proc_sp_req_evt() just_works:%d, io loc:%d, rmt:%d, auth loc:%d, rmt:%d\n", + evt_data.cfm_req.just_works, btm_cb.devcb.loc_io_caps, p_dev_rec->rmt_io_caps, + btm_cb.devcb.loc_auth_req, p_dev_rec->rmt_auth_req); + + evt_data.cfm_req.loc_auth_req = btm_cb.devcb.loc_auth_req; + evt_data.cfm_req.rmt_auth_req = p_dev_rec->rmt_auth_req; + evt_data.cfm_req.loc_io_caps = btm_cb.devcb.loc_io_caps; + evt_data.cfm_req.rmt_io_caps = p_dev_rec->rmt_io_caps; + break; + + case BTM_SP_KEY_NOTIF_EVT: + /* Passkey notification (other side is a keyboard) */ + STREAM_TO_UINT32 (evt_data.key_notif.passkey, p); + + BTM_TRACE_DEBUG ("BTM_SP_KEY_NOTIF_EVT: passkey: %u\n", evt_data.key_notif.passkey); + + btm_sec_change_pairing_state (BTM_PAIR_STATE_WAIT_AUTH_COMPLETE); + break; + + case BTM_SP_KEY_REQ_EVT: + /* HCI_USER_PASSKEY_REQUEST_EVT */ + btm_sec_change_pairing_state (BTM_PAIR_STATE_KEY_ENTRY); + break; + } + + if (btm_cb.api.p_sp_callback) { + status = (*btm_cb.api.p_sp_callback) (event, (tBTM_SP_EVT_DATA *)&evt_data); + if (status != BTM_NOT_AUTHORIZED) { + return; + } + /* else BTM_NOT_AUTHORIZED means when the app wants to reject the req right now */ + } else if ( (event == BTM_SP_CFM_REQ_EVT) && (evt_data.cfm_req.just_works == TRUE) ) { + /* automatically reply with just works if no sp_cback */ + status = BTM_SUCCESS; + } + + if (event == BTM_SP_CFM_REQ_EVT) { + BTM_TRACE_DEBUG ("calling BTM_ConfirmReqReply with status: %d\n", status); + BTM_ConfirmReqReply (status, p_bda); + } else if (event == BTM_SP_KEY_REQ_EVT) { + BTM_PasskeyReqReply(status, p_bda, 0); + } + return; + } + /* Something bad. we can only fail this connection */ + btm_cb.acl_disc_reason = HCI_ERR_HOST_REJECT_SECURITY; + + if (BTM_SP_CFM_REQ_EVT == event) { + btsnd_hcic_user_conf_reply (p_bda, FALSE); + } else if (BTM_SP_KEY_NOTIF_EVT == event) { + /* do nothing -> it very unlikely to happen. + This event is most likely to be received by a HID host when it first connects to a HID device. + Usually the Host initiated the connection in this case. + On Mobile platforms, if there's a security process happening, + the host probably can not initiate another connection. + BTW (PC) is another story. */ + if (NULL != (p_dev_rec = btm_find_dev (p_bda)) ) { + btm_sec_disconnect (p_dev_rec->hci_handle, HCI_ERR_AUTH_FAILURE); + } + } else { + btsnd_hcic_user_passkey_neg_reply(p_bda); + } +} + +/******************************************************************************* +** +** Function btm_keypress_notif_evt +** +** Description This function is called when a key press notification is +** received +** +** Returns void +** +*******************************************************************************/ +void btm_keypress_notif_evt (UINT8 *p) +{ + tBTM_SP_KEYPRESS evt_data; + UINT8 *p_bda; + + /* parse & report BTM_SP_KEYPRESS_EVT */ + if (btm_cb.api.p_sp_callback) { + p_bda = evt_data.bd_addr; + + STREAM_TO_BDADDR (p_bda, p); + evt_data.notif_type = *p; + + (*btm_cb.api.p_sp_callback) (BTM_SP_KEYPRESS_EVT, (tBTM_SP_EVT_DATA *)&evt_data); + } +} + +/******************************************************************************* +** +** Function btm_simple_pair_complete +** +** Description This function is called when simple pairing process is +** complete +** +** Returns void +** +*******************************************************************************/ +void btm_simple_pair_complete (UINT8 *p) +{ + tBTM_SP_COMPLT evt_data; + tBTM_SEC_DEV_REC *p_dev_rec; + UINT8 status; + BOOLEAN disc = FALSE; + + status = *p++; + STREAM_TO_BDADDR (evt_data.bd_addr, p); + + if ((p_dev_rec = btm_find_dev (evt_data.bd_addr)) == NULL) { + BTM_TRACE_ERROR ("btm_simple_pair_complete() with unknown BDA: %08x%04x\n", + (evt_data.bd_addr[0] << 24) + (evt_data.bd_addr[1] << 16) + (evt_data.bd_addr[2] << 8) + evt_data.bd_addr[3], + (evt_data.bd_addr[4] << 8) + evt_data.bd_addr[5]); + return; + } +#if (BT_USE_TRACES == TRUE && SMP_INCLUDED == TRUE) + BTM_TRACE_EVENT ("btm_simple_pair_complete() Pair State: %s Status:%d sec_state: %u\n", + btm_pair_state_descr(btm_cb.pairing_state), status, p_dev_rec->sec_state); +#endif ///BT_USE_TRACES == TRUE && SMP_INCLUDED == TRUE + evt_data.status = BTM_ERR_PROCESSING; + if (status == HCI_SUCCESS) { + evt_data.status = BTM_SUCCESS; + p_dev_rec->sec_flags |= BTM_SEC_AUTHENTICATED; + } else { + if (status == HCI_ERR_PAIRING_NOT_ALLOWED) { + /* The test spec wants the peer device to get this failure code. */ + btm_sec_change_pairing_state (BTM_PAIR_STATE_WAIT_DISCONNECT); + + /* Change the timer to 1 second */ + btu_start_timer (&btm_cb.pairing_tle, BTU_TTYPE_USER_FUNC, BT_1SEC_TIMEOUT); + } else if (memcmp (btm_cb.pairing_bda, evt_data.bd_addr, BD_ADDR_LEN) == 0) { + /* stop the timer */ + btu_stop_timer (&btm_cb.pairing_tle); + + if (p_dev_rec->sec_state != BTM_SEC_STATE_AUTHENTICATING) { + /* the initiating side: will receive auth complete event. disconnect ACL at that time */ + disc = TRUE; + } + } else { + disc = TRUE; + } + } + + /* Let the pairing state stay active, p_auth_complete_callback will report the failure */ + memcpy (evt_data.bd_addr, p_dev_rec->bd_addr, BD_ADDR_LEN); + memcpy (evt_data.dev_class, p_dev_rec->dev_class, DEV_CLASS_LEN); + + if (btm_cb.api.p_sp_callback) { + (*btm_cb.api.p_sp_callback) (BTM_SP_COMPLT_EVT, (tBTM_SP_EVT_DATA *)&evt_data); + } + + if (disc) { + /* simple pairing failed */ + /* Avoid sending disconnect on HCI_ERR_PEER_USER */ + if ((status != HCI_ERR_PEER_USER) && (status != HCI_ERR_CONN_CAUSE_LOCAL_HOST)) { + btm_sec_send_hci_disconnect (p_dev_rec, HCI_ERR_AUTH_FAILURE, p_dev_rec->hci_handle); + } + } +} +#endif /* (CLASSIC_BT_INCLUDED == TRUE) */ +#endif ///SMP_INCLUDED == TRUE + + +#if BTM_OOB_INCLUDED == TRUE && SMP_INCLUDED == TRUE +/******************************************************************************* +** +** Function btm_rem_oob_req +** +** Description This function is called to process/report +** HCI_REMOTE_OOB_DATA_REQUEST_EVT +** +** Returns void +** +*******************************************************************************/ +void btm_rem_oob_req (UINT8 *p) +{ + UINT8 *p_bda; + tBTM_SP_RMT_OOB evt_data; + tBTM_SEC_DEV_REC *p_dev_rec; + BT_OCTET16 c; + BT_OCTET16 r; + + p_bda = evt_data.bd_addr; + + STREAM_TO_BDADDR (p_bda, p); + + BTM_TRACE_EVENT ("btm_rem_oob_req() BDA: %02x:%02x:%02x:%02x:%02x:%02x\n", + p_bda[0], p_bda[1], p_bda[2], p_bda[3], p_bda[4], p_bda[5]); + + if ( (NULL != (p_dev_rec = btm_find_dev (p_bda))) && + btm_cb.api.p_sp_callback) { + memcpy (evt_data.bd_addr, p_dev_rec->bd_addr, BD_ADDR_LEN); + memcpy (evt_data.dev_class, p_dev_rec->dev_class, DEV_CLASS_LEN); + BCM_STRNCPY_S((char *)evt_data.bd_name, (char *)p_dev_rec->sec_bd_name, BTM_MAX_REM_BD_NAME_LEN); + evt_data.bd_name[BTM_MAX_REM_BD_NAME_LEN] = '\0'; + + btm_sec_change_pairing_state(BTM_PAIR_STATE_WAIT_LOCAL_OOB_RSP); + if ((*btm_cb.api.p_sp_callback) (BTM_SP_RMT_OOB_EVT, (tBTM_SP_EVT_DATA *)&evt_data) == BTM_NOT_AUTHORIZED) { + BTM_RemoteOobDataReply(TRUE, p_bda, c, r); + } + return; + } + + /* something bad. we can only fail this connection */ + btm_cb.acl_disc_reason = HCI_ERR_HOST_REJECT_SECURITY; + btsnd_hcic_rem_oob_neg_reply (p_bda); +} + +/******************************************************************************* +** +** Function btm_read_local_oob_complete +** +** Description This function is called when read local oob data is +** completed by the LM +** +** Returns void +** +*******************************************************************************/ +void btm_read_local_oob_complete (UINT8 *p) +{ + tBTM_SP_LOC_OOB evt_data; + UINT8 status = *p++; + + BTM_TRACE_EVENT ("btm_read_local_oob_complete:%d\n", status); + if (status == HCI_SUCCESS) { + evt_data.status = BTM_SUCCESS; + STREAM_TO_ARRAY16(evt_data.c, p); + STREAM_TO_ARRAY16(evt_data.r, p); + } else { + evt_data.status = BTM_ERR_PROCESSING; + } + + if (btm_cb.api.p_sp_callback) { + (*btm_cb.api.p_sp_callback) (BTM_SP_LOC_OOB_EVT, (tBTM_SP_EVT_DATA *)&evt_data); + } +} +#endif /* BTM_OOB_INCLUDED */ + +/******************************************************************************* +** +** Function btm_sec_auth_collision +** +** Description This function is called when authentication or encryption +** needs to be retried at a later time. +** +** Returns void +** +*******************************************************************************/ +#if (SMP_INCLUDED == TRUE) +static void btm_sec_auth_collision (UINT16 handle) +{ + tBTM_SEC_DEV_REC *p_dev_rec; + + if (!btm_cb.collision_start_time) { + btm_cb.collision_start_time = osi_time_get_os_boottime_ms(); + } + + if ((osi_time_get_os_boottime_ms() - btm_cb.collision_start_time) < btm_cb.max_collision_delay) + { + if (handle == BTM_SEC_INVALID_HANDLE) + { + if ((p_dev_rec = btm_sec_find_dev_by_sec_state (BTM_SEC_STATE_AUTHENTICATING)) == NULL) { + p_dev_rec = btm_sec_find_dev_by_sec_state (BTM_SEC_STATE_ENCRYPTING); + } + } else { + p_dev_rec = btm_find_dev_by_handle (handle); + } + + if (p_dev_rec != NULL) { + BTM_TRACE_DEBUG ("btm_sec_auth_collision: state %d (retrying in a moment...)\n", p_dev_rec->sec_state); + /* We will restart authentication after timeout */ + if (p_dev_rec->sec_state == BTM_SEC_STATE_AUTHENTICATING || p_dev_rec->sec_state == BTM_SEC_STATE_ENCRYPTING) { + p_dev_rec->sec_state = 0; + } + + btm_cb.p_collided_dev_rec = p_dev_rec; + btm_cb.sec_collision_tle.param = (UINT32) btm_sec_collision_timeout; + btu_start_timer (&btm_cb.sec_collision_tle, BTU_TTYPE_USER_FUNC, BT_1SEC_TIMEOUT); + } + } +} +#endif ///SMP_INCLUDED == TRUE + +/******************************************************************************* +** +** Function btm_sec_auth_complete +** +** Description This function is when authentication of the connection is +** completed by the LM +** +** Returns void +** +*******************************************************************************/ +#if (SMP_INCLUDED == TRUE) +void btm_sec_auth_complete (UINT16 handle, UINT8 status) +{ + UINT8 res; + UINT8 old_sm4; + tBTM_PAIRING_STATE old_state = btm_cb.pairing_state; + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev_by_handle (handle); + BOOLEAN are_bonding = FALSE; + + /* Commenting out trace due to obf/compilation problems. + */ +#if (BT_USE_TRACES == TRUE && SMP_INCLUDED == TRUE) + if (p_dev_rec) { + BTM_TRACE_EVENT ("Security Manager: auth_complete PairState: %s handle:%u status:%d dev->sec_state: %u Bda:%08x, RName:%s\n", + btm_pair_state_descr (btm_cb.pairing_state), + handle, status, + p_dev_rec->sec_state, + (p_dev_rec->bd_addr[2] << 24) + (p_dev_rec->bd_addr[3] << 16) + (p_dev_rec->bd_addr[4] << 8) + p_dev_rec->bd_addr[5], + p_dev_rec->sec_bd_name); + } else { + BTM_TRACE_EVENT ("Security Manager: auth_complete PairState: %s handle:%u status:%d\n", + btm_pair_state_descr (btm_cb.pairing_state), + handle, status); + } +#endif + + /* For transaction collision we need to wait and repeat. There is no need */ + /* for random timeout because only slave should receive the result */ + if ((status == HCI_ERR_LMP_ERR_TRANS_COLLISION) || (status == HCI_ERR_DIFF_TRANSACTION_COLLISION)) { + btm_sec_auth_collision(handle); + return; + } + btm_cb.collision_start_time = 0; + + btm_restore_mode(); + + /* Check if connection was made just to do bonding. If we authenticate + the connection that is up, this is the last event received. + */ + if (p_dev_rec + && (btm_cb.pairing_flags & BTM_PAIR_FLAGS_WE_STARTED_DD) + && !(btm_cb.pairing_flags & BTM_PAIR_FLAGS_DISC_WHEN_DONE)) { + p_dev_rec->security_required &= ~BTM_SEC_OUT_AUTHENTICATE; + + l2cu_start_post_bond_timer (p_dev_rec->hci_handle); + } + + if (!p_dev_rec) { + return; + } + + /* keep the old sm4 flag and clear the retry bit in control block */ + old_sm4 = p_dev_rec->sm4; + p_dev_rec->sm4 &= ~BTM_SM4_RETRY; + + if ( (btm_cb.pairing_state != BTM_PAIR_STATE_IDLE) + && (btm_cb.pairing_flags & BTM_PAIR_FLAGS_WE_STARTED_DD) + && (memcmp (p_dev_rec->bd_addr, btm_cb.pairing_bda, BD_ADDR_LEN) == 0) ) { + are_bonding = TRUE; + } + + if ( (btm_cb.pairing_state != BTM_PAIR_STATE_IDLE) + && (memcmp (p_dev_rec->bd_addr, btm_cb.pairing_bda, BD_ADDR_LEN) == 0) ) { + btm_sec_change_pairing_state (BTM_PAIR_STATE_IDLE); + } + + if (p_dev_rec->sec_state != BTM_SEC_STATE_AUTHENTICATING) { + if ( (btm_cb.api.p_auth_complete_callback && status != HCI_SUCCESS) + && (old_state != BTM_PAIR_STATE_IDLE) ) { + (*btm_cb.api.p_auth_complete_callback) (p_dev_rec->bd_addr, + p_dev_rec->dev_class, + p_dev_rec->sec_bd_name, status); + } + return; + } + + /* There can be a race condition, when we are starting authentication and + ** the peer device is doing encryption. + ** If first we receive encryption change up, then initiated authentication + ** can not be performed. According to the spec we can not do authentication + ** on the encrypted link, so device is correct. + */ + if ((status == HCI_ERR_COMMAND_DISALLOWED) + && ((p_dev_rec->sec_flags & (BTM_SEC_AUTHENTICATED | BTM_SEC_ENCRYPTED)) == + (BTM_SEC_AUTHENTICATED | BTM_SEC_ENCRYPTED))) { + status = HCI_SUCCESS; + } + + /* Currently we do not notify user if it is a keyboard which connects */ + /* User probably Disabled the keyboard while it was asleep. Let her try */ + if (btm_cb.api.p_auth_complete_callback) { + /* report the authentication status */ + if (old_state != BTM_PAIR_STATE_IDLE) { + res = (*btm_cb.api.p_auth_complete_callback) (p_dev_rec->bd_addr, + p_dev_rec->dev_class, + p_dev_rec->sec_bd_name, status); + if (res == BTM_SEC_DEV_REC_REMOVED) { + p_dev_rec = NULL; + } + } + } + + if(!p_dev_rec) { + return; + } + + p_dev_rec->sec_state = BTM_SEC_STATE_IDLE; + +#if (CLASSIC_BT_INCLUDED == TRUE) + btm_sec_update_legacy_auth_state(btm_bda_to_acl(p_dev_rec->bd_addr, BT_TRANSPORT_BR_EDR), BTM_ACL_LEGACY_AUTH_SELF); +#endif + /* If this is a bonding procedure can disconnect the link now */ + if (are_bonding) { + p_dev_rec->security_required &= ~BTM_SEC_OUT_AUTHENTICATE; + + if (status != HCI_SUCCESS) { + if (((status != HCI_ERR_PEER_USER) && (status != HCI_ERR_CONN_CAUSE_LOCAL_HOST))) { + btm_sec_send_hci_disconnect (p_dev_rec, HCI_ERR_PEER_USER, p_dev_rec->hci_handle); + } + } else { + BTM_TRACE_DEBUG ("TRYING TO DECIDE IF CAN USE SMP_BR_CHNL\n"); + if (p_dev_rec->new_encryption_key_is_p256 && (btm_sec_use_smp_br_chnl(p_dev_rec)) + /* no LE keys are available, do deriving */ + && (!(p_dev_rec->sec_flags & BTM_SEC_LE_LINK_KEY_KNOWN) || + /* or BR key is higher security than existing LE keys */ + (!(p_dev_rec->sec_flags & BTM_SEC_LE_LINK_KEY_AUTHED) && + (p_dev_rec->sec_flags & BTM_SEC_LINK_KEY_AUTHED)))) { + BTM_TRACE_DEBUG ("link encrypted afer dedic bonding can use SMP_BR_CHNL\n"); + + if (btm_sec_is_master(p_dev_rec)) { + // Encryption is required to start SM over BR/EDR + // indicate that this is encryption after authentication + BTM_SetEncryption(p_dev_rec->bd_addr, BT_TRANSPORT_BR_EDR, NULL, NULL); + } + } + l2cu_start_post_bond_timer (p_dev_rec->hci_handle); + } + + return; + } + + /* If authentication failed, notify the waiting layer */ + if (status != HCI_SUCCESS) { + if ((old_sm4 & BTM_SM4_RETRY) == 0) { + /* allow retry only once */ + if (status == HCI_ERR_LMP_ERR_TRANS_COLLISION) { + /* not retried yet. set the retry bit */ + p_dev_rec->sm4 |= BTM_SM4_RETRY; + BTM_TRACE_DEBUG ("Collision retry sm4:x%x sec_flags:0x%x\n", p_dev_rec->sm4, p_dev_rec->sec_flags); + } + /* this retry for missing key is for Lisbon or later only. + * Legacy device do not need this. the controller will drive the retry automatically */ + else if (HCI_ERR_KEY_MISSING == status && BTM_SEC_IS_SM4(p_dev_rec->sm4)) { + /* not retried yet. set the retry bit */ + p_dev_rec->sm4 |= BTM_SM4_RETRY; + p_dev_rec->sec_flags &= ~BTM_SEC_LINK_KEY_KNOWN; + BTM_TRACE_DEBUG ("Retry for missing key sm4:x%x sec_flags:0x%x\n", p_dev_rec->sm4, p_dev_rec->sec_flags); + + /* With BRCM controller, we do not need to delete the stored link key in controller. + If the stack may sit on top of other controller, we may need this + BTM_DeleteStoredLinkKey (bd_addr, NULL); */ + } + + if (p_dev_rec->sm4 & BTM_SM4_RETRY) { + btm_sec_execute_procedure (p_dev_rec); + return; + } + } + + btm_sec_dev_rec_cback_event (p_dev_rec, BTM_ERR_PROCESSING, FALSE); + + if (btm_cb.pairing_flags & BTM_PAIR_FLAGS_DISC_WHEN_DONE) { + btm_sec_send_hci_disconnect (p_dev_rec, HCI_ERR_AUTH_FAILURE, p_dev_rec->hci_handle); + } + return; + } + + p_dev_rec->sec_flags |= BTM_SEC_AUTHENTICATED; + + if (p_dev_rec->pin_code_length >= 16 || + p_dev_rec->link_key_type == BTM_LKEY_TYPE_AUTH_COMB || + p_dev_rec->link_key_type == BTM_LKEY_TYPE_AUTH_COMB_P_256) { + // If we have MITM protection we have a higher level of security than + // provided by 16 digits PIN + p_dev_rec->sec_flags |= BTM_SEC_16_DIGIT_PIN_AUTHED; + } + + /* Authentication succeeded, execute the next security procedure, if any */ + status = btm_sec_execute_procedure (p_dev_rec); + + /* If there is no next procedure, or procedure failed to start, notify the caller */ + if (status != BTM_CMD_STARTED) { + btm_sec_dev_rec_cback_event (p_dev_rec, status, FALSE); + } +} +#endif ///SMP_INCLUDED == TRUE + + +/******************************************************************************* +** +** Function btm_sec_encrypt_change +** +** Description This function is when encryption of the connection is +** completed by the LM +** +** Returns void +** +*******************************************************************************/ +#if (SMP_INCLUDED == TRUE) +void btm_sec_encrypt_change (UINT16 handle, UINT8 status, UINT8 encr_enable) +{ + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev_by_handle (handle); +#if BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE + tACL_CONN *p_acl = NULL; +#endif + BTM_TRACE_EVENT ("Security Manager: encrypt_change status:%d State:%d, encr_enable = %d\n", + status, (p_dev_rec) ? p_dev_rec->sec_state : 0, encr_enable); + BTM_TRACE_DEBUG ("before update p_dev_rec->sec_flags=0x%x\n", (p_dev_rec) ? p_dev_rec->sec_flags : 0 ); + + /* For transaction collision we need to wait and repeat. There is no need */ + /* for random timeout because only slave should receive the result */ + if ((status == HCI_ERR_LMP_ERR_TRANS_COLLISION) || + (status == HCI_ERR_DIFF_TRANSACTION_COLLISION)) { + btm_sec_auth_collision(handle); + return; + } + btm_cb.collision_start_time = 0; + + if (!p_dev_rec) { + return; + } + + if ((status == HCI_SUCCESS) && encr_enable) { + if (p_dev_rec->hci_handle == handle) { + p_dev_rec->sec_flags |= (BTM_SEC_AUTHENTICATED | BTM_SEC_ENCRYPTED); + if (p_dev_rec->pin_code_length >= 16 || + p_dev_rec->link_key_type == BTM_LKEY_TYPE_AUTH_COMB || + p_dev_rec->link_key_type == BTM_LKEY_TYPE_AUTH_COMB_P_256) { + p_dev_rec->sec_flags |= BTM_SEC_16_DIGIT_PIN_AUTHED; + } + if (p_dev_rec->enc_mode != encr_enable) { + p_dev_rec->enc_mode = encr_enable; + /* Report the encryption change state of BR/EDR to upper layer */ + if (btm_cb.api.p_enc_change_callback) { + (*btm_cb.api.p_enc_change_callback) (p_dev_rec->bd_addr, encr_enable); + } + } + } else { + p_dev_rec->sec_flags |= BTM_SEC_LE_ENCRYPTED; + } + } + + /* It is possible that we decrypted the link to perform role switch */ + /* mark link not to be encrypted, so that when we execute security next time it will kick in again */ + if ((status == HCI_SUCCESS) && !encr_enable) { + if (p_dev_rec->hci_handle == handle) { + p_dev_rec->sec_flags &= ~BTM_SEC_ENCRYPTED; + if (p_dev_rec->enc_mode != encr_enable) { + p_dev_rec->enc_mode = encr_enable; + /* Report the encryption change state of BR/EDR to upper layer */ + if (btm_cb.api.p_enc_change_callback) { + (*btm_cb.api.p_enc_change_callback) (p_dev_rec->bd_addr, encr_enable); + } + } + } else { + p_dev_rec->sec_flags &= ~BTM_SEC_LE_ENCRYPTED; + } + } + + BTM_TRACE_DEBUG ("after update p_dev_rec->sec_flags=0x%x\n", p_dev_rec->sec_flags ); + +#if BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE + p_acl = btm_handle_to_acl(handle); + + if (p_acl != NULL) { + btm_sec_check_pending_enc_req(p_dev_rec, p_acl->transport, encr_enable); + } + + if (p_acl && p_acl->transport == BT_TRANSPORT_LE) { + if (status == HCI_ERR_KEY_MISSING || status == HCI_ERR_AUTH_FAILURE || + status == HCI_ERR_ENCRY_MODE_NOT_ACCEPTABLE) { + p_dev_rec->sec_flags &= ~ (BTM_SEC_LE_LINK_KEY_KNOWN); + p_dev_rec->ble.key_type = BTM_LE_KEY_NONE; + } + btm_ble_link_encrypted(p_dev_rec->ble.pseudo_addr, encr_enable); + return; + } else { + /* BR/EDR connection, update the encryption key size to be 16 as always */ + p_dev_rec->enc_key_size = 16; + } + + BTM_TRACE_DEBUG ("in %s new_encr_key_256 is %d\n", + __func__, p_dev_rec->new_encryption_key_is_p256); + + if ((status == HCI_SUCCESS) && encr_enable && (p_dev_rec->hci_handle == handle)) { + if (p_dev_rec->new_encryption_key_is_p256) { + if (btm_sec_use_smp_br_chnl(p_dev_rec) && + btm_sec_is_master(p_dev_rec) && + /* if LE key is not known, do deriving */ + (!(p_dev_rec->sec_flags & BTM_SEC_LE_LINK_KEY_KNOWN) || + /* or BR key is higher security than existing LE keys */ + (!(p_dev_rec->sec_flags & BTM_SEC_LE_LINK_KEY_AUTHED) + && (p_dev_rec->sec_flags & BTM_SEC_LINK_KEY_AUTHED)))) { + /* BR/EDR is encrypted with LK that can be used to derive LE LTK */ + p_dev_rec->new_encryption_key_is_p256 = FALSE; + + if (p_dev_rec->no_smp_on_br) { + BTM_TRACE_DEBUG ("%s NO SM over BR/EDR\n", __func__); + } else { +#if (CLASSIC_BT_INCLUDED == TRUE) + BTM_TRACE_DEBUG ("%s start SM over BR/EDR\n", __func__); + SMP_BR_PairWith(p_dev_rec->bd_addr); +#endif ///CLASSIC_BT_INCLUDED == TRUE + } + } + } else { + // BR/EDR is successfully encrypted. Correct LK type if needed + // (BR/EDR LK derived from LE LTK was used for encryption) + if ((encr_enable == 1) && /* encryption is ON for SSP */ + /* LK type is for BR/EDR SC */ + (p_dev_rec->link_key_type == BTM_LKEY_TYPE_UNAUTH_COMB_P_256 || + p_dev_rec->link_key_type == BTM_LKEY_TYPE_AUTH_COMB_P_256)) { + if (p_dev_rec->link_key_type == BTM_LKEY_TYPE_UNAUTH_COMB_P_256) { + p_dev_rec->link_key_type = BTM_LKEY_TYPE_UNAUTH_COMB; + } else { /* BTM_LKEY_TYPE_AUTH_COMB_P_256 */ + p_dev_rec->link_key_type = BTM_LKEY_TYPE_AUTH_COMB; + } + + BTM_TRACE_DEBUG("updated link key type to %d\n", p_dev_rec->link_key_type); + btm_send_link_key_notif(p_dev_rec); + } + } + } +#else + btm_sec_check_pending_enc_req (p_dev_rec, BT_TRANSPORT_BR_EDR, encr_enable); +#endif /* BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE */ + + /* If this encryption was started by peer do not need to do anything */ + if (p_dev_rec->sec_state != BTM_SEC_STATE_ENCRYPTING) { + if (BTM_SEC_STATE_DELAY_FOR_ENC == p_dev_rec->sec_state) { + p_dev_rec->sec_state = BTM_SEC_STATE_IDLE; + p_dev_rec->p_callback = NULL; +#if (CLASSIC_BT_INCLUDED == TRUE) + l2cu_resubmit_pending_sec_req (p_dev_rec->bd_addr); +#endif ///CLASSIC_BT_INCLUDED == TRUE + } + return; + } + + p_dev_rec->sec_state = BTM_SEC_STATE_IDLE; + /* If encryption setup failed, notify the waiting layer */ + if (status != HCI_SUCCESS) { + btm_sec_dev_rec_cback_event (p_dev_rec, BTM_ERR_PROCESSING, FALSE); + return; + } + + /* Encryption setup succeeded, execute the next security procedure, if any */ + status = (UINT8)btm_sec_execute_procedure (p_dev_rec); + /* If there is no next procedure, or procedure failed to start, notify the caller */ + if (status != BTM_CMD_STARTED) { + btm_sec_dev_rec_cback_event (p_dev_rec, status, FALSE); + } +} +#endif ///SMP_INCLUDED == TRUE + +/******************************************************************************* +** +** Function btm_sec_connect_after_reject_timeout +** +** Description Connection for bonding could not start because of the collision +** Initiate outgoing connection +** +** Returns Pointer to the TLE struct +** +*******************************************************************************/ +#if (SMP_INCLUDED == TRUE) +static void btm_sec_connect_after_reject_timeout (TIMER_LIST_ENT *p_tle) +{ + tBTM_SEC_DEV_REC *p_dev_rec = btm_cb.p_collided_dev_rec; + UNUSED(p_tle); + + BTM_TRACE_EVENT ("btm_sec_connect_after_reject_timeout()\n"); + btm_cb.sec_collision_tle.param = 0; + btm_cb.p_collided_dev_rec = 0; + + if (btm_sec_dd_create_conn(p_dev_rec) != BTM_CMD_STARTED) { + BTM_TRACE_WARNING ("Security Manager: btm_sec_connect_after_reject_timeout: failed to start connection\n"); + + btm_sec_change_pairing_state (BTM_PAIR_STATE_IDLE); + + if (btm_cb.api.p_auth_complete_callback) { + (*btm_cb.api.p_auth_complete_callback) (p_dev_rec->bd_addr, p_dev_rec->dev_class, + p_dev_rec->sec_bd_name, HCI_ERR_MEMORY_FULL); + } + } +} +#endif ///SMP_INCLUDED == TRUE + +/******************************************************************************* +** +** Function btm_sec_connected +** +** Description This function is when a connection to the peer device is +** establsihed +** +** Returns void +** +*******************************************************************************/ +#if (SMP_INCLUDED == TRUE) +void btm_sec_connected (UINT8 *bda, UINT16 handle, UINT8 status, UINT8 enc_mode) +{ + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev (bda); + UINT8 res; + UINT8 sec_dev_rec_status; + BOOLEAN is_pairing_device = FALSE; + tACL_CONN *p_acl_cb; + UINT8 bit_shift = 0; + + btm_acl_resubmit_page(); + + /* Commenting out trace due to obf/compilation problems. + */ +#if (BT_USE_TRACES == TRUE && SMP_INCLUDED == TRUE) + if (p_dev_rec) { + BTM_TRACE_EVENT ("Security Manager: btm_sec_connected in state: %s handle:%d status:%d enc_mode:%d bda:%x RName:%s\n", + btm_pair_state_descr(btm_cb.pairing_state), handle, status, enc_mode, + (bda[2] << 24) + (bda[3] << 16) + (bda[4] << 8) + bda[5], + p_dev_rec->sec_bd_name); + } else { + BTM_TRACE_EVENT ("Security Manager: btm_sec_connected in state: %s handle:%d status:%d enc_mode:%d bda:%x \n", + btm_pair_state_descr(btm_cb.pairing_state), handle, status, enc_mode, + (bda[2] << 24) + (bda[3] << 16) + (bda[4] << 8) + bda[5]); + } +#endif + + if (!p_dev_rec) { + /* There is no device record for new connection. Allocate one */ + if (status == HCI_SUCCESS) { + p_dev_rec = btm_sec_alloc_dev (bda); + } else { + /* If the device matches with stored paring address + * reset the paring state to idle */ + if ((btm_cb.pairing_state != BTM_PAIR_STATE_IDLE) && + (memcmp (btm_cb.pairing_bda, bda, BD_ADDR_LEN) == 0)) { + btm_sec_change_pairing_state(BTM_PAIR_STATE_IDLE); + } + + /* can not find the device record and the status is error, + * just ignore it */ + return; + } + } else { /* Update the timestamp for this device */ + +#if BLE_INCLUDED == TRUE + bit_shift = (handle == p_dev_rec->ble_hci_handle) ? 8 : 0; +#endif + p_dev_rec->timestamp = btm_cb.dev_rec_count++; + if (p_dev_rec->sm4 & BTM_SM4_CONN_PEND) { + /* tell L2CAP it's a bonding connection. */ + if ( (btm_cb.pairing_state != BTM_PAIR_STATE_IDLE) + && (memcmp (btm_cb.pairing_bda, p_dev_rec->bd_addr, BD_ADDR_LEN) == 0) + && (btm_cb.pairing_flags & BTM_PAIR_FLAGS_WE_STARTED_DD) ) { + /* if incoming connection failed while pairing, then try to connect and continue */ + /* Motorola S9 disconnects without asking pin code */ + if ((status != HCI_SUCCESS) && (btm_cb.pairing_state == BTM_PAIR_STATE_WAIT_PIN_REQ)) { + BTM_TRACE_WARNING ("Security Manager: btm_sec_connected: incoming connection failed without asking PIN\n"); + + p_dev_rec->sm4 &= ~BTM_SM4_CONN_PEND; + if (p_dev_rec->sec_flags & BTM_SEC_NAME_KNOWN) { + /* Start timer with 0 to initiate connection with new LCB */ + /* because L2CAP will delete current LCB with this event */ + btm_cb.p_collided_dev_rec = p_dev_rec; + btm_cb.sec_collision_tle.param = (UINT32) btm_sec_connect_after_reject_timeout; + btu_start_timer (&btm_cb.sec_collision_tle, BTU_TTYPE_USER_FUNC, 0); + } else { + btm_sec_change_pairing_state (BTM_PAIR_STATE_GET_REM_NAME); + BTM_ReadRemoteDeviceName(p_dev_rec->bd_addr, NULL, BT_TRANSPORT_BR_EDR); + } +#if BTM_DISC_DURING_RS == TRUE + p_dev_rec->rs_disc_pending = BTM_SEC_RS_NOT_PENDING; /* reset flag */ +#endif + return; + } else { + l2cu_update_lcb_4_bonding(p_dev_rec->bd_addr, TRUE); + } + } + /* always clear the pending flag */ + p_dev_rec->sm4 &= ~BTM_SM4_CONN_PEND; + } + } + +#if BLE_INCLUDED == TRUE + p_dev_rec->device_type |= BT_DEVICE_TYPE_BREDR; +#endif + +#if BTM_DISC_DURING_RS == TRUE + p_dev_rec->rs_disc_pending = BTM_SEC_RS_NOT_PENDING; /* reset flag */ +#endif + + p_dev_rec->rs_disc_pending = BTM_SEC_RS_NOT_PENDING; /* reset flag */ + + if ( (btm_cb.pairing_state != BTM_PAIR_STATE_IDLE) + && (memcmp (btm_cb.pairing_bda, bda, BD_ADDR_LEN) == 0) ) { + /* if we rejected incoming connection from bonding device */ + if ((status == HCI_ERR_HOST_REJECT_DEVICE) + && (btm_cb.pairing_flags & BTM_PAIR_FLAGS_REJECTED_CONNECT)) { + BTM_TRACE_WARNING ("Security Manager: btm_sec_connected: HCI_Conn_Comp Flags:0x%04x, sm4: 0x%x\n", + btm_cb.pairing_flags, p_dev_rec->sm4); + + btm_cb.pairing_flags &= ~BTM_PAIR_FLAGS_REJECTED_CONNECT; + if (BTM_SEC_IS_SM4_UNKNOWN(p_dev_rec->sm4)) { + /* Try again: RNR when no ACL causes HCI_RMT_HOST_SUP_FEAT_NOTIFY_EVT */ + btm_sec_change_pairing_state (BTM_PAIR_STATE_GET_REM_NAME); + BTM_ReadRemoteDeviceName(bda, NULL, BT_TRANSPORT_BR_EDR); + return; + } + + /* if we already have pin code */ + if (btm_cb.pairing_state != BTM_PAIR_STATE_WAIT_LOCAL_PIN) { + /* Start timer with 0 to initiate connection with new LCB */ + /* because L2CAP will delete current LCB with this event */ + btm_cb.p_collided_dev_rec = p_dev_rec; + btm_cb.sec_collision_tle.param = (UINT32) btm_sec_connect_after_reject_timeout; + btu_start_timer (&btm_cb.sec_collision_tle, BTU_TTYPE_USER_FUNC, 0); + } + + return; + } + /* wait for incoming connection without resetting pairing state */ + else if (status == HCI_ERR_CONNECTION_EXISTS) { + BTM_TRACE_WARNING ("Security Manager: btm_sec_connected: Wait for incoming connection\n"); + return; + } + + is_pairing_device = TRUE; + } + + /* If connection was made to do bonding restore link security if changed */ + btm_restore_mode(); + + /* if connection fails during pin request, notify application */ + if (status != HCI_SUCCESS) { + /* If connection failed because of during pairing, need to tell user */ + if (is_pairing_device) { + p_dev_rec->security_required &= ~BTM_SEC_OUT_AUTHENTICATE; + p_dev_rec->sec_flags &= ~((BTM_SEC_LINK_KEY_KNOWN | BTM_SEC_LINK_KEY_AUTHED) << bit_shift); + BTM_TRACE_DEBUG ("security_required:%x \n", p_dev_rec->security_required ); + + btm_sec_change_pairing_state (BTM_PAIR_STATE_IDLE); + + /* We need to notify host that the key is not known any more */ + if (btm_cb.api.p_auth_complete_callback) { + sec_dev_rec_status = (*btm_cb.api.p_auth_complete_callback) (p_dev_rec->bd_addr, + p_dev_rec->dev_class, + p_dev_rec->sec_bd_name, status); + if (sec_dev_rec_status == BTM_SEC_DEV_REC_REMOVED) { + p_dev_rec = NULL; + } + } + } + /* + Do not send authentication failure, if following conditions hold good + 1. BTM Sec Pairing state is idle + 2. Link key for the remote device is present. + 3. Remote is SSP capable. + */ + else if ((p_dev_rec->link_key_type <= BTM_LKEY_TYPE_REMOTE_UNIT) && + (((status == HCI_ERR_AUTH_FAILURE) || + (status == HCI_ERR_KEY_MISSING) || + (status == HCI_ERR_HOST_REJECT_SECURITY) || + (status == HCI_ERR_PAIRING_NOT_ALLOWED) || + (status == HCI_ERR_UNIT_KEY_USED) || + (status == HCI_ERR_PAIRING_WITH_UNIT_KEY_NOT_SUPPORTED) || + (status == HCI_ERR_ENCRY_MODE_NOT_ACCEPTABLE) || + (status == HCI_ERR_REPEATED_ATTEMPTS)))) { + p_dev_rec->security_required &= ~BTM_SEC_OUT_AUTHENTICATE; + p_dev_rec->sec_flags &= ~ (BTM_SEC_LE_LINK_KEY_KNOWN << bit_shift); + + +#ifdef BRCM_NOT_4_BTE + /* If we rejected pairing, pass this special result code */ + if (btm_cb.acl_disc_reason == HCI_ERR_HOST_REJECT_SECURITY) { + status = HCI_ERR_HOST_REJECT_SECURITY; + } +#endif + + /* We need to notify host that the key is not known any more */ + if (btm_cb.api.p_auth_complete_callback) { + sec_dev_rec_status = (*btm_cb.api.p_auth_complete_callback) (p_dev_rec->bd_addr, + p_dev_rec->dev_class, + p_dev_rec->sec_bd_name, status); + if (sec_dev_rec_status == BTM_SEC_DEV_REC_REMOVED) { + p_dev_rec = NULL; + } + } + } + + if (status == HCI_ERR_CONNECTION_TOUT || status == HCI_ERR_LMP_RESPONSE_TIMEOUT || + status == HCI_ERR_UNSPECIFIED || status == HCI_ERR_PAGE_TIMEOUT) { + btm_sec_dev_rec_cback_event (p_dev_rec, BTM_DEVICE_TIMEOUT, FALSE); + } else { + btm_sec_dev_rec_cback_event (p_dev_rec, BTM_ERR_PROCESSING, FALSE); + } + + return; + } + + /* If initiated dedicated bonding, return the link key now, and initiate disconnect */ + /* If dedicated bonding, and we now have a link key, we are all done */ + if ( is_pairing_device + && (p_dev_rec->sec_flags & BTM_SEC_LINK_KEY_KNOWN) ) { + if (p_dev_rec->link_key_not_sent) { + p_dev_rec->link_key_not_sent = FALSE; + btm_send_link_key_notif(p_dev_rec); + } + + p_dev_rec->security_required &= ~BTM_SEC_OUT_AUTHENTICATE; + + /* remember flag before it is initialized */ + if (btm_cb.pairing_flags & BTM_PAIR_FLAGS_WE_STARTED_DD) { + res = TRUE; + } else { + res = FALSE; + } + + if (btm_cb.api.p_auth_complete_callback) { + sec_dev_rec_status = (*btm_cb.api.p_auth_complete_callback) (p_dev_rec->bd_addr, + p_dev_rec->dev_class, + p_dev_rec->sec_bd_name, HCI_SUCCESS); + if (sec_dev_rec_status == BTM_SEC_DEV_REC_REMOVED) { + p_dev_rec = NULL; + } + } + + btm_sec_change_pairing_state (BTM_PAIR_STATE_IDLE); + + if ( res ) { + /* Let l2cap start bond timer */ + l2cu_update_lcb_4_bonding (p_dev_rec->bd_addr, TRUE); + } + + return; + } + + p_dev_rec->hci_handle = handle; + + /* role may not be correct here, it will be updated by l2cap, but we need to */ + /* notify btm_acl that link is up, so starting of rmt name request will not */ + /* set paging flag up */ + p_acl_cb = btm_bda_to_acl(bda, BT_TRANSPORT_BR_EDR); + if (p_acl_cb) { + /* whatever is in btm_establish_continue() without reporting the BTM_BL_CONN_EVT event */ +#if (!defined(BTM_BYPASS_EXTRA_ACL_SETUP) || BTM_BYPASS_EXTRA_ACL_SETUP == FALSE) + /* For now there are a some devices that do not like sending */ + /* commands events and data at the same time. */ + /* Set the packet types to the default allowed by the device */ + btm_set_packet_types (p_acl_cb, btm_cb.btm_acl_pkt_types_supported); + + if (btm_cb.btm_def_link_policy) { + BTM_SetLinkPolicy (p_acl_cb->remote_addr, &btm_cb.btm_def_link_policy); + } +#endif + } + btm_acl_created (bda, p_dev_rec->dev_class, p_dev_rec->sec_bd_name, handle, HCI_ROLE_SLAVE, BT_TRANSPORT_BR_EDR); + + /* Initialize security flags. We need to do that because some */ + /* authorization complete could have come after the connection is dropped */ + /* and that would set wrong flag that link has been authorized already */ + p_dev_rec->sec_flags &= ~((BTM_SEC_AUTHORIZED | BTM_SEC_AUTHENTICATED | + BTM_SEC_ENCRYPTED | BTM_SEC_ROLE_SWITCHED) << bit_shift); + + if (enc_mode != HCI_ENCRYPT_MODE_DISABLED) { + p_dev_rec->sec_flags |= ((BTM_SEC_AUTHENTICATED | BTM_SEC_ENCRYPTED) << bit_shift); + } + + if (btm_cb.security_mode == BTM_SEC_MODE_LINK) { + p_dev_rec->sec_flags |= (BTM_SEC_AUTHENTICATED << bit_shift); + } + + if (p_dev_rec->pin_code_length >= 16 || + p_dev_rec->link_key_type == BTM_LKEY_TYPE_AUTH_COMB || + p_dev_rec->link_key_type == BTM_LKEY_TYPE_AUTH_COMB_P_256) { + p_dev_rec->sec_flags |= (BTM_SEC_16_DIGIT_PIN_AUTHED << bit_shift); + } + + p_dev_rec->link_key_changed = FALSE; + + /* After connection is established we perform security if we do not know */ + /* the name, or if we are originator because some procedure can have */ + /* been scheduled while connection was down */ + BTM_TRACE_DEBUG ("is_originator:%d \n", p_dev_rec->is_originator); + if (!(p_dev_rec->sec_flags & BTM_SEC_NAME_KNOWN) || p_dev_rec->is_originator) { + if ((res = btm_sec_execute_procedure (p_dev_rec)) != BTM_CMD_STARTED) { + btm_sec_dev_rec_cback_event (p_dev_rec, res, FALSE); + } + } + return; +} + +/******************************************************************************* +** +** Function btm_sec_disconnect +** +** Description This function is called to disconnect HCI link +** +** Returns btm status +** +*******************************************************************************/ +tBTM_STATUS btm_sec_disconnect (UINT16 handle, UINT8 reason) +{ + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev_by_handle (handle); + + /* In some weird race condition we may not have a record */ + if (!p_dev_rec) { + btsnd_hcic_disconnect (handle, reason); + return (BTM_SUCCESS); + } + + /* If we are in the process of bonding we need to tell client that auth failed */ + if ( (btm_cb.pairing_state != BTM_PAIR_STATE_IDLE) + && (memcmp (btm_cb.pairing_bda, p_dev_rec->bd_addr, BD_ADDR_LEN) == 0) + && (btm_cb.pairing_flags & BTM_PAIR_FLAGS_WE_STARTED_DD) ) { + /* we are currently doing bonding. Link will be disconnected when done */ + btm_cb.pairing_flags |= BTM_PAIR_FLAGS_DISC_WHEN_DONE; + return (BTM_BUSY); + } + + return (btm_sec_send_hci_disconnect(p_dev_rec, reason, handle)); +} + +/******************************************************************************* +** +** Function btm_sec_disconnected +** +** Description This function is when a connection to the peer device is +** dropped +** +** Returns void +** +*******************************************************************************/ +void btm_sec_disconnected (UINT16 handle, UINT8 reason) +{ + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev_by_handle (handle); + UINT8 old_pairing_flags = btm_cb.pairing_flags; + int result = HCI_ERR_AUTH_FAILURE; + tBTM_SEC_CALLBACK *p_callback = NULL; + tBT_TRANSPORT transport = BT_TRANSPORT_BR_EDR; + + /* If page was delayed for disc complete, can do it now */ + btm_cb.discing = FALSE; + +#if (CLASSIC_BT_INCLUDED == TRUE) + btm_acl_resubmit_page(); +#endif + + if (!p_dev_rec) { + return; + } + p_dev_rec->enc_init_by_we = FALSE; + transport = (handle == p_dev_rec->hci_handle) ? BT_TRANSPORT_BR_EDR : BT_TRANSPORT_LE; + + p_dev_rec->rs_disc_pending = BTM_SEC_RS_NOT_PENDING; /* reset flag */ + +#if BTM_DISC_DURING_RS == TRUE + p_dev_rec->rs_disc_pending = BTM_SEC_RS_NOT_PENDING; /* reset flag */ +#endif + + /* clear unused flags */ + p_dev_rec->sm4 &= BTM_SM4_TRUE; +#if (BT_USE_TRACES == TRUE && SMP_INCLUDED == TRUE) + uint8_t *bd_addr = (uint8_t *)p_dev_rec->bd_addr; + BTM_TRACE_EVENT("%s sec_req:x%x state:%s reason:%d bd_addr:%02x:%02x:%02x:%02x:%02x:%02x" + " remote_name:%s\n", __func__, p_dev_rec->security_required, btm_pair_state_descr(btm_cb.pairing_state), + reason, bd_addr[0], bd_addr[1], bd_addr[2], bd_addr[3], bd_addr[4], bd_addr[5], p_dev_rec->sec_bd_name); +#endif ///BT_USE_TRACES == TRUE && SMP_INCLUDED == TRUE + BTM_TRACE_EVENT("%s before update sec_flags=0x%x\n", __func__, p_dev_rec->sec_flags); + +#if BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE + btm_ble_update_mode_operation(HCI_ROLE_UNKNOWN, p_dev_rec->bd_addr, HCI_SUCCESS); + /* see sec_flags processing in btm_acl_removed */ + + if (transport == BT_TRANSPORT_LE) { + p_dev_rec->ble_hci_handle = BTM_SEC_INVALID_HANDLE; + p_dev_rec->sec_flags &= ~(BTM_SEC_LE_AUTHENTICATED | BTM_SEC_LE_ENCRYPTED); + p_dev_rec->enc_key_size = 0; + } else +#endif + { + p_dev_rec->hci_handle = BTM_SEC_INVALID_HANDLE; + p_dev_rec->sec_flags &= ~(BTM_SEC_AUTHORIZED | BTM_SEC_AUTHENTICATED | BTM_SEC_ENCRYPTED + | BTM_SEC_ROLE_SWITCHED | BTM_SEC_16_DIGIT_PIN_AUTHED); + } + +#if BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE + if (p_dev_rec->sec_state == BTM_SEC_STATE_DISCONNECTING_BOTH) { + p_dev_rec->sec_state = (transport == BT_TRANSPORT_LE) ? + BTM_SEC_STATE_DISCONNECTING : BTM_SEC_STATE_DISCONNECTING_BLE; + return; + } +#endif + p_dev_rec->sec_state = BTM_SEC_STATE_IDLE; + p_dev_rec->security_required = BTM_SEC_NONE; + + p_callback = p_dev_rec->p_callback; + + /* if security is pending, send callback to clean up the security state */ + if (p_callback) { + p_dev_rec->p_callback = NULL; /* when the peer device time out the authentication before + we do, this call back must be reset here */ + (*p_callback) (p_dev_rec->bd_addr, transport, p_dev_rec->p_ref_data, BTM_ERR_PROCESSING); + } + + BTM_TRACE_EVENT("%s after update sec_flags=0x%x\n", __func__, p_dev_rec->sec_flags); + + /* If we are in the process of bonding we need to tell client that auth failed */ + if ( (btm_cb.pairing_state != BTM_PAIR_STATE_IDLE) + && (memcmp (btm_cb.pairing_bda, p_dev_rec->bd_addr, BD_ADDR_LEN) == 0)) { + btm_sec_change_pairing_state (BTM_PAIR_STATE_IDLE); + p_dev_rec->sec_flags &= ~BTM_SEC_LINK_KEY_KNOWN; + if (btm_cb.api.p_auth_complete_callback) { + /* If the disconnection reason is REPEATED_ATTEMPTS, + send this error message to complete callback function + to display the error message of Repeated attempts. + All others, send HCI_ERR_AUTH_FAILURE. */ + if (reason == HCI_ERR_REPEATED_ATTEMPTS) { + result = HCI_ERR_REPEATED_ATTEMPTS; + } else if (old_pairing_flags & BTM_PAIR_FLAGS_WE_STARTED_DD) { + result = HCI_ERR_HOST_REJECT_SECURITY; + } + (*btm_cb.api.p_auth_complete_callback) (p_dev_rec->bd_addr, p_dev_rec->dev_class, + p_dev_rec->sec_bd_name, result); + } + } +} + +/******************************************************************************* +** +** Function btm_sec_link_key_notification +** +** Description This function is called when a new connection link key is +** generated +** +** Returns Pointer to the record or NULL +** +*******************************************************************************/ +void btm_sec_link_key_notification (UINT8 *p_bda, UINT8 *p_link_key, UINT8 key_type) +{ + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_or_alloc_dev (p_bda); + BOOLEAN we_are_bonding = FALSE; + BOOLEAN ltk_derived_lk = FALSE; + UINT8 res; + + BTM_TRACE_EVENT ("btm_sec_link_key_notification() BDA:%04x%08x, TYPE: %d\n", + (p_bda[0] << 8) + p_bda[1], (p_bda[2] << 24) + (p_bda[3] << 16) + (p_bda[4] << 8) + p_bda[5], + key_type); + + if ((key_type >= BTM_LTK_DERIVED_LKEY_OFFSET + BTM_LKEY_TYPE_COMBINATION) && + (key_type <= BTM_LTK_DERIVED_LKEY_OFFSET + BTM_LKEY_TYPE_AUTH_COMB_P_256)) { + ltk_derived_lk = TRUE; + key_type -= BTM_LTK_DERIVED_LKEY_OFFSET; + } + /* If connection was made to do bonding restore link security if changed */ + btm_restore_mode(); + + /* Store the previous state of secure connection as current state. Since + * this is the first encounter with the remote device, whatever the remote + * device's SC state is, it cannot lower the SC level from this. */ + p_dev_rec->remote_secure_connection_previous_state = p_dev_rec->remote_supports_secure_connections; + if (p_dev_rec->remote_supports_secure_connections) { + BTM_TRACE_EVENT ("Remote device supports Secure Connection"); + } else { + BTM_TRACE_EVENT ("Remote device does not support Secure Connection"); + } + if (key_type != BTM_LKEY_TYPE_CHANGED_COMB) { + p_dev_rec->link_key_type = key_type; + } + + p_dev_rec->sec_flags |= BTM_SEC_LINK_KEY_KNOWN; + +#if (CLASSIC_BT_INCLUDED == TRUE) + btm_sec_update_legacy_auth_state(btm_bda_to_acl(p_dev_rec->bd_addr, BT_TRANSPORT_BR_EDR), BTM_ACL_LEGACY_AUTH_NONE); +#endif + /* + * Until this point in time, we do not know if MITM was enabled, hence we + * add the extended security flag here. + */ + if (p_dev_rec->pin_code_length >= 16 || + p_dev_rec->link_key_type == BTM_LKEY_TYPE_AUTH_COMB || + p_dev_rec->link_key_type == BTM_LKEY_TYPE_AUTH_COMB_P_256) { + p_dev_rec->sec_flags |= BTM_SEC_16_DIGIT_PIN_AUTHED; + } + +#if (BLE_INCLUDED == TRUE) + /* BR/EDR connection, update the encryption key size to be 16 as always */ + p_dev_rec->enc_key_size = 16; +#endif + memcpy (p_dev_rec->link_key, p_link_key, LINK_KEY_LEN); + + if ( (btm_cb.pairing_state != BTM_PAIR_STATE_IDLE) + && (memcmp (btm_cb.pairing_bda, p_bda, BD_ADDR_LEN) == 0) ) { + if (btm_cb.pairing_flags & BTM_PAIR_FLAGS_WE_STARTED_DD) { + we_are_bonding = TRUE; + } else { + btm_sec_change_pairing_state (BTM_PAIR_STATE_IDLE); + } + } + + /* save LTK derived LK no matter what */ + if (ltk_derived_lk) { + if (btm_cb.api.p_link_key_callback) { + BTM_TRACE_DEBUG ("%s() Save LTK derived LK (key_type = %d)\n", + __FUNCTION__, p_dev_rec->link_key_type); + (*btm_cb.api.p_link_key_callback) (p_bda, p_dev_rec->dev_class, + p_dev_rec->sec_bd_name, + p_link_key, p_dev_rec->link_key_type, + p_dev_rec->remote_supports_secure_connections); + } + } else { + if ((p_dev_rec->link_key_type == BTM_LKEY_TYPE_UNAUTH_COMB_P_256) || + (p_dev_rec->link_key_type == BTM_LKEY_TYPE_AUTH_COMB_P_256)) { + p_dev_rec->new_encryption_key_is_p256 = TRUE; + BTM_TRACE_DEBUG ("%s set new_encr_key_256 to %d\n", + __func__, p_dev_rec->new_encryption_key_is_p256); + } + } + + /* If name is not known at this point delay calling callback until the name is */ + /* resolved. Unless it is a HID Device and we really need to send all link keys. */ + if ((!(p_dev_rec->sec_flags & BTM_SEC_NAME_KNOWN) + && ((p_dev_rec->dev_class[1] & BTM_COD_MAJOR_CLASS_MASK) != BTM_COD_MAJOR_PERIPHERAL)) + && !ltk_derived_lk) { + BTM_TRACE_EVENT ("btm_sec_link_key_notification() Delayed BDA: %08x%04x Type:%d\n", + (p_bda[0] << 24) + (p_bda[1] << 16) + (p_bda[2] << 8) + p_bda[3], + (p_bda[4] << 8) + p_bda[5], key_type); + + p_dev_rec->link_key_not_sent = TRUE; + + /* If it is for bonding nothing else will follow, so we need to start name resolution */ + if (we_are_bonding) { + if (!(btsnd_hcic_rmt_name_req (p_bda, HCI_PAGE_SCAN_REP_MODE_R1, HCI_MANDATARY_PAGE_SCAN_MODE, 0))) { + btm_inq_rmt_name_failed(); + } + } + + BTM_TRACE_EVENT ("rmt_io_caps:%d, sec_flags:x%x, dev_class[1]:x%02x\n", p_dev_rec->rmt_io_caps, p_dev_rec->sec_flags, p_dev_rec->dev_class[1]) + return; + } + + /* If its not us who perform authentication, we should tell stackserver */ + /* that some authentication has been completed */ + /* This is required when different entities receive link notification and auth complete */ + if (!(p_dev_rec->security_required & BTM_SEC_OUT_AUTHENTICATE) + /* for derived key, always send authentication callback for BR channel */ + || ltk_derived_lk) { + if (btm_cb.api.p_auth_complete_callback) { + res = (*btm_cb.api.p_auth_complete_callback) (p_dev_rec->bd_addr, p_dev_rec->dev_class, + p_dev_rec->sec_bd_name, HCI_SUCCESS); + if (res == BTM_SEC_DEV_REC_REMOVED) { + p_dev_rec = NULL; + } + } + } + + if(!p_dev_rec) { + return; + } + + /* We will save link key only if the user authorized it - BTE report link key in all cases */ +#ifdef BRCM_NONE_BTE + if (p_dev_rec->sec_flags & BTM_SEC_LINK_KEY_AUTHED) +#endif + { + if (btm_cb.api.p_link_key_callback) { + if (ltk_derived_lk) { + BTM_TRACE_DEBUG ("btm_sec_link_key_notification() LTK derived LK is saved already" + " (key_type = %d)\n", p_dev_rec->link_key_type); + } else { + (*btm_cb.api.p_link_key_callback) (p_bda, p_dev_rec->dev_class, + p_dev_rec->sec_bd_name, + p_link_key, p_dev_rec->link_key_type, + p_dev_rec->remote_supports_secure_connections); + } + } + } +} + +/******************************************************************************* +** +** Function btm_sec_link_key_request +** +** Description This function is called when controller requests link key +** +** Returns Pointer to the record or NULL +** +*******************************************************************************/ +void btm_sec_link_key_request (UINT8 *p_bda) +{ + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_or_alloc_dev (p_bda); + + BTM_TRACE_EVENT ("btm_sec_link_key_request() BDA: %02x:%02x:%02x:%02x:%02x:%02x\n", + p_bda[0], p_bda[1], p_bda[2], p_bda[3], p_bda[4], p_bda[5]); + + if ( (btm_cb.pairing_state == BTM_PAIR_STATE_WAIT_PIN_REQ) && + (btm_cb.collision_start_time != 0) && + (memcmp (btm_cb.p_collided_dev_rec->bd_addr, p_bda, BD_ADDR_LEN) == 0) ) { + BTM_TRACE_EVENT ("btm_sec_link_key_request() rejecting link key req " + "State: %d START_TIMEOUT : %d\n", + btm_cb.pairing_state, btm_cb.collision_start_time); + btsnd_hcic_link_key_neg_reply (p_bda); + return; + } + if (p_dev_rec->sec_flags & BTM_SEC_LINK_KEY_KNOWN) { + btsnd_hcic_link_key_req_reply (p_bda, p_dev_rec->link_key); + return; + } + + /* Notify L2CAP to increase timeout */ + l2c_pin_code_request (p_bda); + + /* The link key is not in the database and it is not known to the manager */ + btsnd_hcic_link_key_neg_reply (p_bda); +} +#endif ///SMP_INCLUDED == TRUE + +/******************************************************************************* +** +** Function btm_sec_pairing_timeout +** +** Description This function is called when host does not provide PIN +** within requested time +** +** Returns Pointer to the TLE struct +** +*******************************************************************************/ +#if (SMP_INCLUDED == TRUE) +static void btm_sec_pairing_timeout (TIMER_LIST_ENT *p_tle) +{ + tBTM_CB *p_cb = &btm_cb; + tBTM_SEC_DEV_REC *p_dev_rec; +#if BTM_OOB_INCLUDED == TRUE +#if (BTM_LOCAL_IO_CAPS == BTM_IO_CAP_NONE) + tBTM_AUTH_REQ auth_req = BTM_AUTH_AP_NO; +#else + tBTM_AUTH_REQ auth_req = BTM_AUTH_AP_YES; +#endif +#endif + UINT8 name[2]; + UNUSED(p_tle); + + p_cb->pairing_tle.param = 0; + /* Coverity: FALSE-POSITIVE error from Coverity tool. Please do NOT remove following comment. */ + /* coverity[UNUSED_VALUE] pointer p_dev_rec is actually used several times... This is a Coverity false-positive, i.e. a fake issue. + */ + p_dev_rec = btm_find_dev (p_cb->pairing_bda); +#if (BT_USE_TRACES == TRUE && SMP_INCLUDED == TRUE) + BTM_TRACE_EVENT ("btm_sec_pairing_timeout() State: %s Flags: %u\n", + btm_pair_state_descr(p_cb->pairing_state), p_cb->pairing_flags); +#endif ///BT_USE_TRACES == TRUE && SMP_INCLUDED == TRUE + switch (p_cb->pairing_state) { + case BTM_PAIR_STATE_WAIT_PIN_REQ: + btm_sec_bond_cancel_complete(); + break; + + case BTM_PAIR_STATE_WAIT_LOCAL_PIN: + if ( (btm_cb.pairing_flags & BTM_PAIR_FLAGS_PRE_FETCH_PIN) == 0) { + btsnd_hcic_pin_code_neg_reply (p_cb->pairing_bda); + } + btm_sec_change_pairing_state (BTM_PAIR_STATE_IDLE); + /* We need to notify the UI that no longer need the PIN */ + if (btm_cb.api.p_auth_complete_callback) { + if (p_dev_rec == NULL) { + name[0] = '\0'; + (*btm_cb.api.p_auth_complete_callback) (p_cb->pairing_bda, + NULL, + name, HCI_ERR_CONNECTION_TOUT); + } else { + (*btm_cb.api.p_auth_complete_callback) (p_dev_rec->bd_addr, + p_dev_rec->dev_class, + p_dev_rec->sec_bd_name, HCI_ERR_CONNECTION_TOUT); + } + } + break; + + case BTM_PAIR_STATE_WAIT_NUMERIC_CONFIRM: + btsnd_hcic_user_conf_reply (p_cb->pairing_bda, FALSE); + /* btm_sec_change_pairing_state (BTM_PAIR_STATE_IDLE); */ + break; + +#if (CLASSIC_BT_INCLUDED == TRUE) + case BTM_PAIR_STATE_KEY_ENTRY: + btsnd_hcic_user_passkey_neg_reply(p_cb->pairing_bda); + /* btm_sec_change_pairing_state (BTM_PAIR_STATE_IDLE); */ + break; +#endif /* (CLASSIC_BT_INCLUDED == TRUE) */ + +#if BTM_OOB_INCLUDED == TRUE + case BTM_PAIR_STATE_WAIT_LOCAL_IOCAPS: + if (btm_cb.pairing_flags & BTM_PAIR_FLAGS_WE_STARTED_DD) { + auth_req |= BTM_AUTH_DD_BOND; + } + + btsnd_hcic_io_cap_req_reply (p_cb->pairing_bda, btm_cb.devcb.loc_io_caps, + BTM_OOB_NONE, auth_req); + btm_sec_change_pairing_state (BTM_PAIR_STATE_IDLE); + break; + + case BTM_PAIR_STATE_WAIT_LOCAL_OOB_RSP: + btsnd_hcic_rem_oob_neg_reply (p_cb->pairing_bda); + btm_sec_change_pairing_state (BTM_PAIR_STATE_IDLE); + break; +#endif /* BTM_OOB_INCLUDED */ + + case BTM_PAIR_STATE_WAIT_DISCONNECT: + /* simple pairing failed. Started a 1-sec timer at simple pairing complete. + * now it's time to tear down the ACL link*/ + if (p_dev_rec == NULL) { + BTM_TRACE_ERROR ("btm_sec_pairing_timeout() BTM_PAIR_STATE_WAIT_DISCONNECT unknown BDA: %08x%04x\n", + (p_cb->pairing_bda[0] << 24) + (p_cb->pairing_bda[1] << 16) + (p_cb->pairing_bda[2] << 8) + p_cb->pairing_bda[3], + (p_cb->pairing_bda[4] << 8) + p_cb->pairing_bda[5]); + break; + } + btm_sec_send_hci_disconnect (p_dev_rec, HCI_ERR_AUTH_FAILURE, p_dev_rec->hci_handle); + btm_sec_change_pairing_state (BTM_PAIR_STATE_IDLE); + break; + + case BTM_PAIR_STATE_WAIT_AUTH_COMPLETE: + case BTM_PAIR_STATE_GET_REM_NAME: + /* We need to notify the UI that timeout has happened while waiting for authentication*/ + btm_sec_change_pairing_state (BTM_PAIR_STATE_IDLE); + if (btm_cb.api.p_auth_complete_callback) { + if (p_dev_rec == NULL) { + name[0] = '\0'; + (*btm_cb.api.p_auth_complete_callback) (p_cb->pairing_bda, + NULL, + name, HCI_ERR_CONNECTION_TOUT); + } else { + (*btm_cb.api.p_auth_complete_callback) (p_dev_rec->bd_addr, + p_dev_rec->dev_class, + p_dev_rec->sec_bd_name, HCI_ERR_CONNECTION_TOUT); + } + } + break; + + default: +#if (BT_USE_TRACES == TRUE && SMP_INCLUDED == TRUE) + BTM_TRACE_WARNING ("btm_sec_pairing_timeout() not processed state: %s\n", btm_pair_state_descr(btm_cb.pairing_state)); +#endif ///BT_USE_TRACES == TRUE && SMP_INCLUDED == TRUE + btm_sec_change_pairing_state (BTM_PAIR_STATE_IDLE); + break; + } +} + +#if (CLASSIC_BT_INCLUDED == TRUE) +/******************************************************************************* +** +** Function btm_sec_pin_code_request +** +** Description This function is called when controller requests PIN code +** +** Returns Pointer to the record or NULL +** +*******************************************************************************/ +void btm_sec_pin_code_request (UINT8 *p_bda) +{ + tBTM_SEC_DEV_REC *p_dev_rec; + tBTM_CB *p_cb = &btm_cb; + +#ifdef PORCHE_PAIRING_CONFLICT + UINT8 default_pin_code_len = 4; + PIN_CODE default_pin_code = {0x30, 0x30, 0x30, 0x30}; +#endif +#if (BT_USE_TRACES == TRUE && SMP_INCLUDED == TRUE) + BTM_TRACE_EVENT ("btm_sec_pin_code_request() State: %s, BDA:%04x%08x\n", + btm_pair_state_descr(btm_cb.pairing_state), + (p_bda[0] << 8) + p_bda[1], (p_bda[2] << 24) + (p_bda[3] << 16) + (p_bda[4] << 8) + p_bda[5] ); +#endif ///BT_USE_TRACES == TRUE && SMP_INCLUDED == TRUE + if (btm_cb.pairing_state != BTM_PAIR_STATE_IDLE) { + if ( (memcmp (p_bda, btm_cb.pairing_bda, BD_ADDR_LEN) == 0) && + (btm_cb.pairing_state == BTM_PAIR_STATE_WAIT_AUTH_COMPLETE) ) { + /* fake this out - porshe carkit issue - */ +// btm_cb.pairing_state = BTM_PAIR_STATE_IDLE; + if (! btm_cb.pin_code_len_saved) { + btsnd_hcic_pin_code_neg_reply (p_bda); + return; + } else { + btsnd_hcic_pin_code_req_reply (p_bda, btm_cb.pin_code_len_saved, p_cb->pin_code); + return; + } + } else if ((btm_cb.pairing_state != BTM_PAIR_STATE_WAIT_PIN_REQ) + || memcmp (p_bda, btm_cb.pairing_bda, BD_ADDR_LEN) != 0) { +#if (BT_USE_TRACES == TRUE && SMP_INCLUDED == TRUE) + BTM_TRACE_WARNING ("btm_sec_pin_code_request() rejected - state: %s\n", + btm_pair_state_descr(btm_cb.pairing_state)); +#endif ///BT_USE_TRACES == TRUE && SMP_INCLUDED == TRUE +#ifdef PORCHE_PAIRING_CONFLICT + /* reply pin code again due to counter in_rand when local initiates pairing */ + BTM_TRACE_EVENT ("btm_sec_pin_code_request from remote dev. for local initiated pairing\n"); + if (! btm_cb.pin_code_len_saved) { + btm_sec_change_pairing_state (BTM_PAIR_STATE_WAIT_AUTH_COMPLETE); + btsnd_hcic_pin_code_req_reply (p_bda, default_pin_code_len, default_pin_code); + } else { + btm_sec_change_pairing_state (BTM_PAIR_STATE_WAIT_AUTH_COMPLETE); + btsnd_hcic_pin_code_req_reply (p_bda, btm_cb.pin_code_len_saved, p_cb->pin_code); + } +#else + btsnd_hcic_pin_code_neg_reply (p_bda); +#endif + return; + } + } + + p_dev_rec = btm_find_or_alloc_dev (p_bda); + /* received PIN code request. must be non-sm4 */ + p_dev_rec->sm4 = BTM_SM4_KNOWN; + + if (btm_cb.pairing_state == BTM_PAIR_STATE_IDLE) { + memcpy (btm_cb.pairing_bda, p_bda, BD_ADDR_LEN); + + btm_cb.pairing_flags = BTM_PAIR_FLAGS_PEER_STARTED_DD; + /* Make sure we reset the trusted mask to help against attacks */ + BTM_SEC_CLR_TRUSTED_DEVICE(p_dev_rec->trusted_mask); + } + + if (!p_cb->pairing_disabled && (p_cb->cfg.pin_type == HCI_PIN_TYPE_FIXED)) { + BTM_TRACE_EVENT ("btm_sec_pin_code_request fixed pin replying\n"); + btm_sec_change_pairing_state (BTM_PAIR_STATE_WAIT_AUTH_COMPLETE); + btsnd_hcic_pin_code_req_reply (p_bda, p_cb->cfg.pin_code_len, p_cb->cfg.pin_code); + return; + } + + /* Use the connecting device's CoD for the connection */ + if ( (!memcmp (p_bda, p_cb->connecting_bda, BD_ADDR_LEN)) + && (p_cb->connecting_dc[0] || p_cb->connecting_dc[1] || p_cb->connecting_dc[2]) ) { + memcpy (p_dev_rec->dev_class, p_cb->connecting_dc, DEV_CLASS_LEN); + } + + /* We could have started connection after asking user for the PIN code */ + if (btm_cb.pin_code_len != 0) { + BTM_TRACE_EVENT ("btm_sec_pin_code_request bonding sending reply\n"); + btsnd_hcic_pin_code_req_reply (p_bda, btm_cb.pin_code_len, p_cb->pin_code); + +#ifdef PORCHE_PAIRING_CONFLICT + btm_cb.pin_code_len_saved = btm_cb.pin_code_len; +#endif + + /* Mark that we forwarded received from the user PIN code */ + btm_cb.pin_code_len = 0; + + /* We can change mode back right away, that other connection being established */ + /* is not forced to be secure - found a FW issue, so we can not do this + btm_restore_mode(); */ + + btm_sec_change_pairing_state (BTM_PAIR_STATE_WAIT_AUTH_COMPLETE); + } + + /* If pairing disabled OR (no PIN callback and not bonding) */ + /* OR we could not allocate entry in the database reject pairing request */ + else if (p_cb->pairing_disabled + || (p_cb->api.p_pin_callback == NULL) + + /* OR Microsoft keyboard can for some reason try to establish connection */ + /* the only thing we can do here is to shut it up. Normally we will be originator */ + /* for keyboard bonding */ + || (!p_dev_rec->is_originator + && ((p_dev_rec->dev_class[1] & BTM_COD_MAJOR_CLASS_MASK) == BTM_COD_MAJOR_PERIPHERAL) + && (p_dev_rec->dev_class[2] & BTM_COD_MINOR_KEYBOARD)) ) { + BTM_TRACE_WARNING("btm_sec_pin_code_request(): Pairing disabled:%d; PIN callback:%p, Dev Rec:%p!\n", + p_cb->pairing_disabled, p_cb->api.p_pin_callback, p_dev_rec); + + btsnd_hcic_pin_code_neg_reply (p_bda); + } + /* Notify upper layer of PIN request and start expiration timer */ + else { + btm_cb.pin_code_len_saved = 0; + btm_sec_change_pairing_state (BTM_PAIR_STATE_WAIT_LOCAL_PIN); + /* Pin code request can not come at the same time as connection request */ + memcpy (p_cb->connecting_bda, p_bda, BD_ADDR_LEN); + memcpy (p_cb->connecting_dc, p_dev_rec->dev_class, DEV_CLASS_LEN); + + BTM_TRACE_EVENT ("btm_sec_pin_code_request going for callback\n"); + btm_cb.pairing_flags |= BTM_PAIR_FLAGS_PIN_REQD; + if (p_cb->api.p_pin_callback) { + (*p_cb->api.p_pin_callback) (p_bda, p_dev_rec->dev_class, p_dev_rec->sec_bd_name, + (p_dev_rec->p_cur_service == NULL) ? FALSE + : (p_dev_rec->p_cur_service->security_flags + & BTM_SEC_IN_MIN_16_DIGIT_PIN)); + } + } + return; +} +#endif ///CLASSIC_BT_INCLUDED == TRUE +#endif ///SMP_INCLUDED == TRUE + + +/******************************************************************************* +** +** Function btm_sec_update_clock_offset +** +** Description This function is called to update clock offset +** +** Returns void +** +*******************************************************************************/ +void btm_sec_update_clock_offset (UINT16 handle, UINT16 clock_offset) +{ + tBTM_SEC_DEV_REC *p_dev_rec; + tBTM_INQ_INFO *p_inq_info; + + if ((p_dev_rec = btm_find_dev_by_handle (handle)) == NULL) { + return; + } + + p_dev_rec->clock_offset = clock_offset | BTM_CLOCK_OFFSET_VALID; + + if ((p_inq_info = BTM_InqDbRead(p_dev_rec->bd_addr)) == NULL) { + return; + } + + p_inq_info->results.clock_offset = clock_offset | BTM_CLOCK_OFFSET_VALID; +} + + +/****************************************************************** +** S T A T I C F U N C T I O N S +*******************************************************************/ + +/******************************************************************************* +** +** Function btm_sec_execute_procedure +** +** Description This function is called to start required security +** procedure. There is a case when multiplexing protocol +** calls this function on the originating side, connection to +** the peer will not be established. This function in this +** case performs only authorization. +** +** Returns BTM_SUCCESS - permission is granted +** BTM_CMD_STARTED - in process +** BTM_NO_RESOURCES - permission declined +** +*******************************************************************************/ +#if (SMP_INCLUDED == TRUE) +static tBTM_STATUS btm_sec_execute_procedure (tBTM_SEC_DEV_REC *p_dev_rec) +{ + BTM_TRACE_EVENT ("btm_sec_execute_procedure: Required:0x%x Flags:0x%x State:%d\n", + p_dev_rec->security_required, p_dev_rec->sec_flags, p_dev_rec->sec_state); + + /* There is a chance that we are getting name. Wait until done. */ + if (p_dev_rec->sec_state != 0) { + return (BTM_CMD_STARTED); + } + + /* If any security is required, get the name first */ + if (!(p_dev_rec->sec_flags & BTM_SEC_NAME_KNOWN) + && (p_dev_rec->hci_handle != BTM_SEC_INVALID_HANDLE)) { + BTM_TRACE_EVENT ("Security Manager: Start get name\n"); + if (!btm_sec_start_get_name (p_dev_rec)) { + return (BTM_NO_RESOURCES); + } + return (BTM_CMD_STARTED); + } + +#if (CLASSIC_BT_INCLUDED == TRUE) + tACL_CONN *p_acl_cb = btm_handle_to_acl(p_dev_rec->hci_handle); + /* + * To prevent a remote device from doing a Bluetooth Impersonation Attack, a suggested fix by SIG is: + * + * "Hosts performing legacy (non-mutual) authentication must ensure a remote device is authenticated + * prior to proceeding with encryption establishment, regardless of role." + * + * As an implementation, we enforce mutual authentication when devices use Legacy Authentication. + */ + if ((p_acl_cb != NULL) && (BTM_BothEndsSupportSecureConnections(p_acl_cb->remote_addr) == 0) && + ((p_acl_cb->legacy_auth_state & BTM_ACL_LEGACY_AUTH_SELF) == 0)) { + p_dev_rec->sec_flags &= ~BTM_SEC_AUTHENTICATED; + } +#endif + + /* If connection is not authenticated and authentication is required */ + /* start authentication and return PENDING to the caller */ + if ((((!(p_dev_rec->sec_flags & BTM_SEC_AUTHENTICATED)) + && (( p_dev_rec->is_originator && (p_dev_rec->security_required & BTM_SEC_OUT_AUTHENTICATE)) + || (!p_dev_rec->is_originator && (p_dev_rec->security_required & BTM_SEC_IN_AUTHENTICATE)))) + || (!(p_dev_rec->sec_flags & BTM_SEC_16_DIGIT_PIN_AUTHED) + && (!p_dev_rec->is_originator + && (p_dev_rec->security_required & BTM_SEC_IN_MIN_16_DIGIT_PIN)))) + && (p_dev_rec->hci_handle != BTM_SEC_INVALID_HANDLE)) { + /* + * We rely on BTM_SEC_16_DIGIT_PIN_AUTHED being set if MITM is in use, + * as 16 DIGIT is only needed if MITM is not used. Unfortunately, the + * BTM_SEC_AUTHENTICATED is used for both MITM and non-MITM + * authenticated connections, hence we cannot distinguish here. + */ + +#if (L2CAP_UCD_INCLUDED == TRUE) + /* if incoming UCD packet, discard it */ + if ( !p_dev_rec->is_originator && (p_dev_rec->is_ucd == TRUE )) { + return (BTM_FAILED_ON_SECURITY); + } +#endif + + BTM_TRACE_EVENT ("Security Manager: Start authentication\n"); + + /* + * If we do have a link-key, but we end up here because we need an + * upgrade, then clear the link-key known and authenticated flag before + * restarting authentication. + * WARNING: If the controller has link-key, it is optional and + * recommended for the controller to send a Link_Key_Request. + * In case we need an upgrade, the only alternative would be to delete + * the existing link-key. That could lead to very bad user experience + * or even IOP issues, if a reconnect causes a new connection that + * requires an upgrade. + */ + if ((p_dev_rec->sec_flags & BTM_SEC_LINK_KEY_KNOWN) + && (!(p_dev_rec->sec_flags & BTM_SEC_16_DIGIT_PIN_AUTHED) + && (!p_dev_rec->is_originator && (p_dev_rec->security_required + & BTM_SEC_IN_MIN_16_DIGIT_PIN)))) { + p_dev_rec->sec_flags &= ~(BTM_SEC_LINK_KEY_KNOWN | BTM_SEC_LINK_KEY_AUTHED + | BTM_SEC_AUTHENTICATED); + } + + if (!btm_sec_start_authentication (p_dev_rec)) { + return (BTM_NO_RESOURCES); + } + return (BTM_CMD_STARTED); + } + + /* If connection is not encrypted and encryption is required */ + /* start encryption and return PENDING to the caller */ + if (!(p_dev_rec->sec_flags & BTM_SEC_ENCRYPTED) + && (( p_dev_rec->is_originator && (p_dev_rec->security_required & BTM_SEC_OUT_ENCRYPT)) + || (!p_dev_rec->is_originator && (p_dev_rec->security_required & BTM_SEC_IN_ENCRYPT))) + && (p_dev_rec->hci_handle != BTM_SEC_INVALID_HANDLE)) { +#if (L2CAP_UCD_INCLUDED == TRUE) + /* if incoming UCD packet, discard it */ + if ( !p_dev_rec->is_originator && (p_dev_rec->is_ucd == TRUE )) { + return (BTM_FAILED_ON_SECURITY); + } +#endif + + BTM_TRACE_EVENT ("Security Manager: Start encryption\n"); + + if (!btm_sec_start_encryption (p_dev_rec)) { + return (BTM_NO_RESOURCES); + } + return (BTM_CMD_STARTED); + } + + if ((p_dev_rec->security_required & BTM_SEC_MODE4_LEVEL4) && + (p_dev_rec->link_key_type != BTM_LKEY_TYPE_AUTH_COMB_P_256)) { + BTM_TRACE_EVENT("%s: Security Manager: SC only service, but link key type is 0x%02x -" + "security failure\n", __FUNCTION__, p_dev_rec->link_key_type); + return (BTM_FAILED_ON_SECURITY); + } + + /* If connection is not authorized and authorization is required */ + /* start authorization and return PENDING to the caller */ + if (!(p_dev_rec->sec_flags & BTM_SEC_AUTHORIZED) + && (( p_dev_rec->is_originator && (p_dev_rec->security_required & BTM_SEC_OUT_AUTHORIZE)) + || (!p_dev_rec->is_originator && (p_dev_rec->security_required & BTM_SEC_IN_AUTHORIZE)))) { + BTM_TRACE_EVENT ("service id:%d, is trusted:%d\n", + p_dev_rec->p_cur_service->service_id, + (BTM_SEC_IS_SERVICE_TRUSTED(p_dev_rec->trusted_mask, + p_dev_rec->p_cur_service->service_id))); + if ((btm_sec_are_all_trusted(p_dev_rec->trusted_mask) == FALSE) && + (p_dev_rec->p_cur_service->service_id < BTM_SEC_MAX_SERVICES) && + (BTM_SEC_IS_SERVICE_TRUSTED(p_dev_rec->trusted_mask, + p_dev_rec->p_cur_service->service_id) == FALSE)) { + BTM_TRACE_EVENT ("Security Manager: Start authorization\n"); + return (btm_sec_start_authorization (p_dev_rec)); + } + } + + /* All required security procedures already established */ + p_dev_rec->security_required &= ~(BTM_SEC_OUT_AUTHORIZE | BTM_SEC_IN_AUTHORIZE | + BTM_SEC_OUT_AUTHENTICATE | BTM_SEC_IN_AUTHENTICATE | + BTM_SEC_OUT_ENCRYPT | BTM_SEC_IN_ENCRYPT | + BTM_SEC_FORCE_MASTER | BTM_SEC_ATTEMPT_MASTER | + BTM_SEC_FORCE_SLAVE | BTM_SEC_ATTEMPT_SLAVE); + + BTM_TRACE_EVENT ("Security Manager: trusted:0x%04x%04x\n", p_dev_rec->trusted_mask[1], p_dev_rec->trusted_mask[0]); + BTM_TRACE_EVENT ("Security Manager: access granted\n"); + + return (BTM_SUCCESS); +} + +/******************************************************************************* +** +** Function btm_sec_start_get_name +** +** Description This function is called to start get name procedure +** +** Returns TRUE if started +** +*******************************************************************************/ +static BOOLEAN btm_sec_start_get_name (tBTM_SEC_DEV_REC *p_dev_rec) +{ + UINT8 tempstate = p_dev_rec->sec_state; + + p_dev_rec->sec_state = BTM_SEC_STATE_GETTING_NAME; + + /* Device should be connected, no need to provide correct page params */ + /* 0 and NULL are as timeout and callback params because they are not used in security get name case */ + if ((btm_initiate_rem_name (p_dev_rec->bd_addr, NULL, BTM_RMT_NAME_SEC, + 0, NULL)) != BTM_CMD_STARTED) { + p_dev_rec->sec_state = tempstate; + return (FALSE); + } + + return (TRUE); +} + +/******************************************************************************* +** +** Function btm_sec_start_authentication +** +** Description This function is called to start authentication +** +** Returns TRUE if started +** +*******************************************************************************/ +static BOOLEAN btm_sec_start_authentication (tBTM_SEC_DEV_REC *p_dev_rec) +{ + p_dev_rec->sec_state = BTM_SEC_STATE_AUTHENTICATING; + + return (btsnd_hcic_auth_request (p_dev_rec->hci_handle)); +} + +/******************************************************************************* +** +** Function btm_sec_start_encryption +** +** Description This function is called to start encryption +** +** Returns TRUE if started +** +*******************************************************************************/ +static BOOLEAN btm_sec_start_encryption (tBTM_SEC_DEV_REC *p_dev_rec) +{ + if (!btsnd_hcic_set_conn_encrypt (p_dev_rec->hci_handle, TRUE)) { + return (FALSE); + } + + p_dev_rec->sec_state = BTM_SEC_STATE_ENCRYPTING; + return (TRUE); +} + + +/******************************************************************************* +** +** Function btm_sec_start_authorization +** +** Description This function is called to start authorization +** +** Returns TRUE if started +** +*******************************************************************************/ +static UINT8 btm_sec_start_authorization (tBTM_SEC_DEV_REC *p_dev_rec) +{ + UINT8 result; + UINT8 *p_service_name = NULL; + UINT8 service_id; + + if ((p_dev_rec->sec_flags & BTM_SEC_NAME_KNOWN) + || (p_dev_rec->hci_handle == BTM_SEC_INVALID_HANDLE)) { + if (!btm_cb.api.p_authorize_callback) { + return (BTM_MODE_UNSUPPORTED); + } + + if (p_dev_rec->p_cur_service) { +#if BTM_SEC_SERVICE_NAME_LEN > 0 + if (p_dev_rec->is_originator) { + p_service_name = p_dev_rec->p_cur_service->orig_service_name; + } else { + p_service_name = p_dev_rec->p_cur_service->term_service_name; + } +#endif + service_id = p_dev_rec->p_cur_service->service_id; + } else { + service_id = 0; + } + + /* Send authorization request if not already sent during this service connection */ + if (p_dev_rec->last_author_service_id == BTM_SEC_NO_LAST_SERVICE_ID + || p_dev_rec->last_author_service_id != service_id) { + p_dev_rec->sec_state = BTM_SEC_STATE_AUTHORIZING; + result = (*btm_cb.api.p_authorize_callback) (p_dev_rec->bd_addr, + p_dev_rec->dev_class, + p_dev_rec->sec_bd_name, + p_service_name, + service_id, + p_dev_rec->is_originator); + } + + else { /* Already authorized once for this L2CAP bringup */ + BTM_TRACE_DEBUG ("btm_sec_start_authorization: (Ignoring extra Authorization prompt for service %d)\n", service_id); + return (BTM_SUCCESS); + } + + if (result == BTM_SUCCESS) { + p_dev_rec->sec_flags |= BTM_SEC_AUTHORIZED; + + /* Save the currently authorized service in case we are asked again by another multiplexer layer */ + if (!p_dev_rec->is_originator) { + p_dev_rec->last_author_service_id = service_id; + } + + p_dev_rec->sec_state = BTM_SEC_STATE_IDLE; + } + return (result); + } + btm_sec_start_get_name (p_dev_rec); + return (BTM_CMD_STARTED); +} +#endif ///SMP_INCLUDED == TRUE + +/******************************************************************************* +** +** Function btm_sec_are_all_trusted +** +** Description This function is called check if all services are trusted +** +** Returns TRUE if all are trusted, otherwise FALSE +** +*******************************************************************************/ +BOOLEAN btm_sec_are_all_trusted(UINT32 p_mask[]) +{ + UINT32 trusted_inx; + for (trusted_inx = 0; trusted_inx < BTM_SEC_SERVICE_ARRAY_SIZE; trusted_inx++) { + if (p_mask[trusted_inx] != BTM_SEC_TRUST_ALL) { + return (FALSE); + } + } + + return (TRUE); +} + +/******************************************************************************* +** +** Function btm_sec_find_first_serv +** +** Description Look for the first record in the service database +** with specified PSM +** +** Returns Pointer to the record or NULL +** +*******************************************************************************/ +#if (SMP_INCLUDED == TRUE) +tBTM_SEC_SERV_REC *btm_sec_find_first_serv (CONNECTION_TYPE conn_type, UINT16 psm) +{ + tBTM_SEC_SERV_REC *p_serv_rec = &btm_cb.sec_serv_rec[0]; + int i; + BOOLEAN is_originator; + +#if (L2CAP_UCD_INCLUDED == TRUE) + + if ( conn_type & CONNECTION_TYPE_ORIG_MASK ) { + is_originator = TRUE; + } else { + is_originator = FALSE; + } +#else + is_originator = conn_type; +#endif + + if (is_originator && btm_cb.p_out_serv && btm_cb.p_out_serv->psm == psm) { + /* If this is outgoing connection and the PSM matches p_out_serv, + * use it as the current service */ + return btm_cb.p_out_serv; + } + + /* otherwise, just find the first record with the specified PSM */ + for (i = 0; i < BTM_SEC_MAX_SERVICE_RECORDS; i++, p_serv_rec++) { + if ( (p_serv_rec->security_flags & BTM_SEC_IN_USE) && (p_serv_rec->psm == psm) ) { + return (p_serv_rec); + } + } + return (NULL); +} + + +/******************************************************************************* +** +** Function btm_sec_find_next_serv +** +** Description Look for the next record in the service database +** with specified PSM +** +** Returns Pointer to the record or NULL +** +*******************************************************************************/ +static tBTM_SEC_SERV_REC *btm_sec_find_next_serv (tBTM_SEC_SERV_REC *p_cur) +{ + tBTM_SEC_SERV_REC *p_serv_rec = &btm_cb.sec_serv_rec[0]; + int i; + + for (i = 0; i < BTM_SEC_MAX_SERVICE_RECORDS; i++, p_serv_rec++) { + if ((p_serv_rec->security_flags & BTM_SEC_IN_USE) + && (p_serv_rec->psm == p_cur->psm) ) { + if (p_cur != p_serv_rec) { + return (p_serv_rec); + } + } + } + return (NULL); +} + +/******************************************************************************* +** +** Function btm_sec_find_mx_serv +** +** Description Look for the record in the service database with specified +** PSM and multiplexor channel information +** +** Returns Pointer to the record or NULL +** +*******************************************************************************/ +static tBTM_SEC_SERV_REC *btm_sec_find_mx_serv (UINT8 is_originator, UINT16 psm, + UINT32 mx_proto_id, UINT32 mx_chan_id) +{ + tBTM_SEC_SERV_REC *p_out_serv = btm_cb.p_out_serv; + tBTM_SEC_SERV_REC *p_serv_rec = &btm_cb.sec_serv_rec[0]; + int i; + + BTM_TRACE_DEBUG ("%s()\n", __func__); + if (is_originator && p_out_serv && p_out_serv->psm == psm + && p_out_serv->mx_proto_id == mx_proto_id + && p_out_serv->orig_mx_chan_id == mx_chan_id) { + /* If this is outgoing connection and the parameters match p_out_serv, + * use it as the current service */ + return btm_cb.p_out_serv; + } + + /* otherwise, the old way */ + for (i = 0; i < BTM_SEC_MAX_SERVICE_RECORDS; i++, p_serv_rec++) { + if ((p_serv_rec->security_flags & BTM_SEC_IN_USE) + && (p_serv_rec->psm == psm) + && (p_serv_rec->mx_proto_id == mx_proto_id) + && (( is_originator && (p_serv_rec->orig_mx_chan_id == mx_chan_id)) + || (!is_originator && (p_serv_rec->term_mx_chan_id == mx_chan_id)))) { + return (p_serv_rec); + } + } + return (NULL); +} +#endif ///SMP_INCLUDED == TRUE + +/******************************************************************************* +** +** Function btm_sec_collision_timeout +** +** Description Encryption could not start because of the collision +** try to do it again +** +** Returns Pointer to the TLE struct +** +*******************************************************************************/ +#if (SMP_INCLUDED == TRUE) +static void btm_sec_collision_timeout (TIMER_LIST_ENT *p_tle) +{ + UNUSED(p_tle); + + BTM_TRACE_EVENT ("%s()\n", __func__); + btm_cb.sec_collision_tle.param = 0; + + tBTM_STATUS status = btm_sec_execute_procedure (btm_cb.p_collided_dev_rec); + + /* If result is pending reply from the user or from the device is pending */ + if (status != BTM_CMD_STARTED) { + /* There is no next procedure or start of procedure failed, notify the waiting layer */ + btm_sec_dev_rec_cback_event (btm_cb.p_collided_dev_rec, status, FALSE); + } +} + +/******************************************************************************* +** +** Function btm_send_link_key_notif +** +** Description This function is called when controller requests link key +** +** Returns Pointer to the record or NULL +** +*******************************************************************************/ +static void btm_send_link_key_notif (tBTM_SEC_DEV_REC *p_dev_rec) +{ + if (btm_cb.api.p_link_key_callback) { + (*btm_cb.api.p_link_key_callback) (p_dev_rec->bd_addr, p_dev_rec->dev_class, + p_dev_rec->sec_bd_name, p_dev_rec->link_key, + p_dev_rec->link_key_type, + p_dev_rec->remote_supports_secure_connections); + + } +} +#endif ///SMP_INCLUDED == TRUE + +/******************************************************************************* +** +** Function BTM_ReadTrustedMask +** +** Description Get trusted mask for the peer device +** +** Parameters: bd_addr - Address of the device +** +** Returns NULL, if the device record is not found. +** otherwise, the trusted mask +** +*******************************************************************************/ +UINT32 *BTM_ReadTrustedMask (BD_ADDR bd_addr) +{ + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev (bd_addr); + if (p_dev_rec != NULL) { + return (p_dev_rec->trusted_mask); + } + return NULL; +} + +/******************************************************************************* +** +** Function btm_restore_mode +** +** Description This function returns the security mode to previous setting +** if it was changed during bonding. +** +** +** Parameters: void +** +*******************************************************************************/ +#if (SMP_INCLUDED == TRUE) +static void btm_restore_mode(void) +{ + if (btm_cb.security_mode_changed) { + btm_cb.security_mode_changed = FALSE; + BTM_TRACE_DEBUG("%s() Auth enable -> %d\n", __func__, (btm_cb.security_mode == BTM_SEC_MODE_LINK)); + btsnd_hcic_write_auth_enable ((UINT8)(btm_cb.security_mode == BTM_SEC_MODE_LINK)); + } + +#if (CLASSIC_BT_INCLUDED == TRUE) + if (btm_cb.pin_type_changed) { + btm_cb.pin_type_changed = FALSE; + btsnd_hcic_write_pin_type (btm_cb.cfg.pin_type); + } +#endif ///CLASSIC_BT_INCLUDED == TRUE +} +#endif ///SMP_INCLUDED == TRUE + +/******************************************************************************* +** +** Function btm_sec_find_dev_by_sec_state +** +** Description Look for the record in the device database for the device +** which is being authenticated or encrypted +** +** Returns Pointer to the record or NULL +** +*******************************************************************************/ +tBTM_SEC_DEV_REC *btm_sec_find_dev_by_sec_state (UINT8 state) +{ +#if (SMP_INCLUDED == TRUE) + tBTM_SEC_DEV_REC *p_dev_rec = NULL; + list_node_t *p_node = NULL; + for (p_node = list_begin(btm_cb.p_sec_dev_rec_list); p_node; p_node = list_next(p_node)) { + p_dev_rec = list_node(p_node); + if ((p_dev_rec->sec_flags & BTM_SEC_IN_USE) + && (p_dev_rec->sec_state == state)) { + return (p_dev_rec); + } + } +#endif ///SMP_INCLUDED == TRUE + return (NULL); +} +/******************************************************************************* +** +** Function btm_sec_change_pairing_state +** +** Description This function is called to change pairing state +** +*******************************************************************************/ +#if (SMP_INCLUDED == TRUE) +static void btm_sec_change_pairing_state (tBTM_PAIRING_STATE new_state) +{ + tBTM_PAIRING_STATE old_state = btm_cb.pairing_state; +#if (BT_USE_TRACES == TRUE && SMP_INCLUDED == TRUE) + BTM_TRACE_EVENT ("%s() Old: %s\n", __func__, btm_pair_state_descr(btm_cb.pairing_state)); + BTM_TRACE_EVENT ("%s() New: %s pairing_flags:0x%x\n\n", __func__, + btm_pair_state_descr(new_state), btm_cb.pairing_flags); +#endif ///BT_USE_TRACES == TRUE && SMP_INCLUDED == TRUE + + btm_cb.pairing_state = new_state; + + if (new_state == BTM_PAIR_STATE_IDLE) { + btu_stop_timer (&btm_cb.pairing_tle); + + btm_cb.pairing_flags = 0; +#if (CLASSIC_BT_INCLUDED == TRUE) + btm_cb.pin_code_len = 0; +#endif ///CLASSIC_BT_INCLUDED == TRUE + + /* Make sure the the lcb shows we are not bonding */ + l2cu_update_lcb_4_bonding (btm_cb.pairing_bda, FALSE); + + btm_restore_mode(); + btm_sec_check_pending_reqs(); + btm_inq_clear_ssp(); + + memset (btm_cb.pairing_bda, 0xFF, BD_ADDR_LEN); + } else { + /* If transitionng out of idle, mark the lcb as bonding */ + if (old_state == BTM_PAIR_STATE_IDLE) { + l2cu_update_lcb_4_bonding (btm_cb.pairing_bda, TRUE); + } + + btm_cb.pairing_tle.param = (TIMER_PARAM_TYPE)btm_sec_pairing_timeout; + + btu_start_timer (&btm_cb.pairing_tle, BTU_TTYPE_USER_FUNC, BTM_SEC_TIMEOUT_VALUE); + } +} +#endif ///SMP_INCLUDED == TRUE + + +/******************************************************************************* +** +** Function btm_pair_state_descr +** +** Description Return state description for tracing +** +*******************************************************************************/ +#if (BT_USE_TRACES == TRUE && SMP_INCLUDED == TRUE) +static char *btm_pair_state_descr (tBTM_PAIRING_STATE state) +{ +#if (BT_TRACE_VERBOSE == TRUE) + switch (state) { + case BTM_PAIR_STATE_IDLE: return ("IDLE"); + case BTM_PAIR_STATE_GET_REM_NAME: return ("GET_REM_NAME"); + case BTM_PAIR_STATE_WAIT_PIN_REQ: return ("WAIT_PIN_REQ"); + case BTM_PAIR_STATE_WAIT_LOCAL_PIN: return ("WAIT_LOCAL_PIN"); + case BTM_PAIR_STATE_WAIT_NUMERIC_CONFIRM: return ("WAIT_NUM_CONFIRM"); + case BTM_PAIR_STATE_KEY_ENTRY: return ("KEY_ENTRY"); + case BTM_PAIR_STATE_WAIT_LOCAL_OOB_RSP: return ("WAIT_LOCAL_OOB_RSP"); + case BTM_PAIR_STATE_WAIT_LOCAL_IOCAPS: return ("WAIT_LOCAL_IOCAPS"); + case BTM_PAIR_STATE_INCOMING_SSP: return ("INCOMING_SSP"); + case BTM_PAIR_STATE_WAIT_AUTH_COMPLETE: return ("WAIT_AUTH_COMPLETE"); + case BTM_PAIR_STATE_WAIT_DISCONNECT: return ("WAIT_DISCONNECT"); + } + + return ("???"); +#else + sprintf(btm_cb.state_temp_buffer, "%d", state); + + return (btm_cb.state_temp_buffer); +#endif +} +#endif + +/******************************************************************************* +** +** Function btm_sec_dev_rec_cback_event +** +** Description This function calls the callback function with the given +** result and clear the callback function. +** +** Parameters: void +** +*******************************************************************************/ +void btm_sec_dev_rec_cback_event (tBTM_SEC_DEV_REC *p_dev_rec, UINT8 res, BOOLEAN is_le_transport) +{ + tBTM_SEC_CALLBACK *p_callback; + + if (p_dev_rec && p_dev_rec->p_callback) { + p_callback = p_dev_rec->p_callback; + p_dev_rec->p_callback = NULL; + +#if BLE_INCLUDED == TRUE + if (is_le_transport) { + (*p_callback) (p_dev_rec->ble.pseudo_addr, BT_TRANSPORT_LE, p_dev_rec->p_ref_data, res); + } else +#endif + { + (*p_callback) (p_dev_rec->bd_addr, BT_TRANSPORT_BR_EDR, p_dev_rec->p_ref_data, res); + } + } +#if (SMP_INCLUDED == TRUE) + btm_sec_check_pending_reqs(); +#endif ///SMP_INCLUDED == TRUE +} + +/******************************************************************************* +** +** Function btm_sec_queue_mx_request +** +** Description Return state description for tracing +** +*******************************************************************************/ +#if (SMP_INCLUDED == TRUE) +static BOOLEAN btm_sec_queue_mx_request (BD_ADDR bd_addr, UINT16 psm, BOOLEAN is_orig, + UINT32 mx_proto_id, UINT32 mx_chan_id, + tBTM_SEC_CALLBACK *p_callback, void *p_ref_data) +{ + tBTM_SEC_QUEUE_ENTRY *p_e = (tBTM_SEC_QUEUE_ENTRY *)osi_malloc (sizeof(tBTM_SEC_QUEUE_ENTRY)); + + if (p_e) { + p_e->psm = psm; + p_e->is_orig = is_orig; + p_e->p_callback = p_callback; + p_e->p_ref_data = p_ref_data; + p_e->mx_proto_id = mx_proto_id; + p_e->mx_chan_id = mx_chan_id; + p_e->transport = BT_TRANSPORT_BR_EDR; + + memcpy (p_e->bd_addr, bd_addr, BD_ADDR_LEN); + + BTM_TRACE_EVENT ("%s() PSM: 0x%04x Is_Orig: %u mx_proto_id: %u mx_chan_id: %u\n", + __func__, psm, is_orig, mx_proto_id, mx_chan_id); + + fixed_queue_enqueue(btm_cb.sec_pending_q, p_e, FIXED_QUEUE_MAX_TIMEOUT); + + return (TRUE); + } + + return (FALSE); +} +static BOOLEAN btm_sec_check_prefetch_pin (tBTM_SEC_DEV_REC *p_dev_rec) +{ + BOOLEAN rv = FALSE; +#if (CLASSIC_BT_INCLUDED == TRUE) + UINT8 major = (UINT8)(p_dev_rec->dev_class[1] & BTM_COD_MAJOR_CLASS_MASK); + UINT8 minor = (UINT8)(p_dev_rec->dev_class[2] & BTM_COD_MINOR_CLASS_MASK); + rv = TRUE; + + if ((major == BTM_COD_MAJOR_AUDIO) + && ((minor == BTM_COD_MINOR_CONFM_HANDSFREE) || (minor == BTM_COD_MINOR_CAR_AUDIO)) ) { + BTM_TRACE_EVENT ("%s() Skipping pre-fetch PIN for carkit COD Major: 0x%02x Minor: 0x%02x\n", + __func__, major, minor); + + if (btm_cb.security_mode_changed == FALSE) { + btm_cb.security_mode_changed = TRUE; +#ifdef APPL_AUTH_WRITE_EXCEPTION + if (!(APPL_AUTH_WRITE_EXCEPTION)(p_dev_rec->bd_addr)) +#endif + { + btsnd_hcic_write_auth_enable (TRUE); + } + } + } else { + btm_sec_change_pairing_state (BTM_PAIR_STATE_WAIT_LOCAL_PIN); + + /* If we got a PIN, use that, else try to get one */ + if (btm_cb.pin_code_len) { + BTM_PINCodeReply (p_dev_rec->bd_addr, BTM_SUCCESS, btm_cb.pin_code_len, btm_cb.pin_code, p_dev_rec->trusted_mask); + } else { + /* pin was not supplied - pre-fetch pin code now */ + if (btm_cb.api.p_pin_callback && ((btm_cb.pairing_flags & BTM_PAIR_FLAGS_PIN_REQD) == 0)) { + BTM_TRACE_DEBUG("%s() PIN code callback called\n", __func__); + if (btm_bda_to_acl(p_dev_rec->bd_addr, BT_TRANSPORT_BR_EDR) == NULL) { + btm_cb.pairing_flags |= BTM_PAIR_FLAGS_PIN_REQD; + } + (btm_cb.api.p_pin_callback) (p_dev_rec->bd_addr, p_dev_rec->dev_class, + p_dev_rec->sec_bd_name, (p_dev_rec->p_cur_service == NULL) ? FALSE + : (p_dev_rec->p_cur_service->security_flags + & BTM_SEC_IN_MIN_16_DIGIT_PIN)); + } + } + + rv = TRUE; + } +#endif ///CLASSIC_BT_INCLUDED == TRUE +# + return rv; +} + +/******************************************************************************* +** +** Function btm_sec_auth_payload_tout +** +** Description Processes the HCI Autheniticated Payload Timeout Event +** indicating that a packet containing a valid MIC on the +** connection handle was not received within the programmed +** timeout value. (Spec Default is 30 secs, but can be +** changed via the BTM_SecSetAuthPayloadTimeout() function. +** +*******************************************************************************/ +void btm_sec_auth_payload_tout (UINT8 *p, UINT16 hci_evt_len) +{ + UINT16 handle; + + STREAM_TO_UINT16 (handle, p); + handle = HCID_GET_HANDLE (handle); + + /* Will be exposed to upper layers in the future if/when determined necessary */ + BTM_TRACE_ERROR ("%s on handle 0x%02x\n", __func__, handle); +} + +/******************************************************************************* +** +** Function btm_sec_queue_encrypt_request +** +** Description encqueue encryption request when device has active security +** process pending. +** +*******************************************************************************/ +static BOOLEAN btm_sec_queue_encrypt_request (BD_ADDR bd_addr, tBT_TRANSPORT transport, + tBTM_SEC_CALLBACK *p_callback, void *p_ref_data) +{ + tBTM_SEC_QUEUE_ENTRY *p_e; + p_e = (tBTM_SEC_QUEUE_ENTRY *)osi_malloc(sizeof(tBTM_SEC_QUEUE_ENTRY) + 1); + + if (p_e) { + p_e->psm = 0; /* if PSM 0, encryption request */ + p_e->p_callback = p_callback; + p_e->p_ref_data = (void *)(p_e + 1); + *(UINT8 *)p_e->p_ref_data = *(UINT8 *)(p_ref_data); + p_e->transport = transport; + memcpy(p_e->bd_addr, bd_addr, BD_ADDR_LEN); + fixed_queue_enqueue(btm_cb.sec_pending_q, p_e, FIXED_QUEUE_MAX_TIMEOUT); + return TRUE; + } + + return FALSE; +} + +/******************************************************************************* +** +** Function btm_sec_set_peer_sec_caps +** +** Description This function is called to set sm4 and rmt_sec_caps fields +** based on the available peer device features. +** +** Returns void +** +*******************************************************************************/ +void btm_sec_set_peer_sec_caps(tACL_CONN *p_acl_cb, tBTM_SEC_DEV_REC *p_dev_rec) +{ + BD_ADDR rem_bd_addr; + UINT8 *p_rem_bd_addr; + + if ((btm_cb.security_mode == BTM_SEC_MODE_SP || + btm_cb.security_mode == BTM_SEC_MODE_SP_DEBUG || + btm_cb.security_mode == BTM_SEC_MODE_SC) && + HCI_SSP_HOST_SUPPORTED(p_acl_cb->peer_lmp_features[HCI_EXT_FEATURES_PAGE_1])) { + p_dev_rec->sm4 = BTM_SM4_TRUE; + p_dev_rec->remote_supports_secure_connections = + (HCI_SC_HOST_SUPPORTED(p_acl_cb->peer_lmp_features[HCI_EXT_FEATURES_PAGE_1])); + } else { + p_dev_rec->sm4 = BTM_SM4_KNOWN; + p_dev_rec->remote_supports_secure_connections = FALSE; + } + + BTM_TRACE_API("%s: sm4: 0x%02x, rmt_support_for_secure_connections %d\n", __FUNCTION__, + p_dev_rec->sm4, p_dev_rec->remote_supports_secure_connections); + + /* Store previous state of remote device to check if peer device downgraded + * it's secure connection state. */ +#if (CLASSIC_BT_INCLUDED == TRUE) + if (p_dev_rec->remote_supports_secure_connections >= p_dev_rec->remote_secure_connection_previous_state) { + p_dev_rec->remote_secure_connection_previous_state = p_dev_rec->remote_supports_secure_connections; + } else { + BTM_TRACE_ERROR("Remote Device downgraded security from SC, deleting Link Key"); + + /* Mark in ACL packet that secure connection is downgraded. */ + p_acl_cb->sc_downgrade = 1; + p_dev_rec->remote_secure_connection_previous_state = 0; + + /* As peer device downgraded it's security, peer device is a suspicious + * device. Hence remove pairing information by removing link key + * information. */ + memset(p_dev_rec->link_key, 0, LINK_KEY_LEN); + p_dev_rec->sec_flags &= ~(BTM_SEC_AUTHORIZED | BTM_SEC_AUTHENTICATED + | BTM_SEC_ENCRYPTED | BTM_SEC_NAME_KNOWN + | BTM_SEC_LINK_KEY_KNOWN | BTM_SEC_LINK_KEY_AUTHED + | BTM_SEC_ROLE_SWITCHED | BTM_SEC_16_DIGIT_PIN_AUTHED); + return; + } +#endif + + if (p_dev_rec->remote_features_needed) { + BTM_TRACE_EVENT("%s: Now device in SC Only mode, waiting for peer remote features!\n", + __FUNCTION__); + p_rem_bd_addr = (UINT8 *) rem_bd_addr; + BDADDR_TO_STREAM(p_rem_bd_addr, p_dev_rec->bd_addr); + p_rem_bd_addr = (UINT8 *) rem_bd_addr; + btm_io_capabilities_req(p_rem_bd_addr); + p_dev_rec->remote_features_needed = FALSE; + } +} + +/******************************************************************************* +** +** Function btm_sec_is_serv_level0 +** +** Description This function is called to check if the service corresponding +** to PSM is security mode 4 level 0 service. +** +** Returns TRUE if the service is security mode 4 level 0 service +** +*******************************************************************************/ +static BOOLEAN btm_sec_is_serv_level0(UINT16 psm) +{ + if (psm == BT_PSM_SDP) { + BTM_TRACE_DEBUG("%s: PSM: 0x%04x -> mode 4 level 0 service\n", __FUNCTION__, psm); + return TRUE; + } + return FALSE; +} + +/******************************************************************************* +** +** Function btm_sec_check_pending_enc_req +** +** Description This function is called to send pending encryption callback if +** waiting +** +** Returns void +** +*******************************************************************************/ +static void btm_sec_check_pending_enc_req (tBTM_SEC_DEV_REC *p_dev_rec, tBT_TRANSPORT transport, + UINT8 encr_enable) +{ + if (fixed_queue_is_empty(btm_cb.sec_pending_q)) { + return; + } + + UINT8 res = encr_enable ? BTM_SUCCESS : BTM_ERR_PROCESSING; + list_t *list = fixed_queue_get_list(btm_cb.sec_pending_q); + for (const list_node_t *node = list_begin(list); node != list_end(list); ) { + tBTM_SEC_QUEUE_ENTRY *p_e = (tBTM_SEC_QUEUE_ENTRY *)list_node(node); + node = list_next(node); + + if (memcmp(p_e->bd_addr, p_dev_rec->bd_addr, BD_ADDR_LEN) == 0 && p_e->psm == 0 +#if BLE_INCLUDED == TRUE + && p_e->transport == transport +#endif + ) { +#if BLE_INCLUDED == TRUE + UINT8 sec_act = *(UINT8 *)(p_e->p_ref_data); +#endif + + if (encr_enable == 0 || transport == BT_TRANSPORT_BR_EDR +#if BLE_INCLUDED == TRUE + || (sec_act == BTM_BLE_SEC_ENCRYPT || sec_act == BTM_BLE_SEC_ENCRYPT_NO_MITM) + || (sec_act == BTM_BLE_SEC_ENCRYPT_MITM && p_dev_rec->sec_flags + & BTM_SEC_LE_AUTHENTICATED) +#endif + ) { + if (p_e->p_callback) { + (*p_e->p_callback) (p_dev_rec->bd_addr, transport, p_e->p_ref_data, res); + } + + fixed_queue_try_remove_from_queue(btm_cb.sec_pending_q, (void *)p_e); + } + } + } +} +#endif ///SMP_INCLUDED == TRUE + +/******************************************************************************* +** +** Function btm_sec_set_serv_level4_flags +** +** Description This function is called to set security mode 4 level 4 flags. +** +** Returns service security requirements updated to include secure +** connections only mode. +** +*******************************************************************************/ +#if (SMP_INCLUDED == TRUE) +static UINT16 btm_sec_set_serv_level4_flags(UINT16 cur_security, BOOLEAN is_originator) +{ + UINT16 sec_level4_flags = is_originator ? BTM_SEC_OUT_LEVEL4_FLAGS : BTM_SEC_IN_LEVEL4_FLAGS; + + return cur_security | sec_level4_flags; +} +#endif ///SMP_INCLUDED == TRUE + +/******************************************************************************* +** +** Function btm_sec_clear_ble_keys +** +** Description This function is called to clear out the BLE keys. +** Typically when devices are removed in BTM_SecDeleteDevice, +** or when a new BT Link key is generated. +** +** Returns void +** +*******************************************************************************/ +#if (BLE_INCLUDED == TRUE) +void btm_sec_clear_ble_keys (tBTM_SEC_DEV_REC *p_dev_rec) +{ + + BTM_TRACE_DEBUG ("%s() Clearing BLE Keys\n", __func__); +#if (SMP_INCLUDED== TRUE) + p_dev_rec->ble.key_type = BTM_LE_KEY_NONE; + memset (&p_dev_rec->ble.keys, 0, sizeof(tBTM_SEC_BLE_KEYS)); + +#if (BLE_PRIVACY_SPT == TRUE) + btm_ble_resolving_list_remove_dev(p_dev_rec); +#endif +#endif +} +#endif ///BLE_INCLUDED == TRUE +/******************************************************************************* +** +** Function btm_sec_is_a_bonded_dev +** +** Description Is the specified device is a bonded device +** +** Returns TRUE - dev is bonded +** +*******************************************************************************/ +BOOLEAN btm_sec_is_a_bonded_dev (BD_ADDR bda) +{ + + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev (bda); + BOOLEAN is_bonded = FALSE; + + if (p_dev_rec && +#if (SMP_INCLUDED == TRUE && BLE_INCLUDED == TRUE) + ((p_dev_rec->ble.key_type && (p_dev_rec->sec_flags & BTM_SEC_LE_LINK_KEY_KNOWN)) || +#else + ( +#endif + (p_dev_rec->sec_flags & BTM_SEC_LINK_KEY_KNOWN))) { + is_bonded = TRUE; + } + BTM_TRACE_DEBUG ("%s() is_bonded=%d\n", __func__, is_bonded); + return (is_bonded); +} + +/******************************************************************************* +** +** Function btm_sec_is_le_capable_dev +** +** Description Is the specified device is dual mode or LE only device +** +** Returns TRUE - dev is a dual mode +** +*******************************************************************************/ +BOOLEAN btm_sec_is_le_capable_dev (BD_ADDR bda) +{ + BOOLEAN le_capable = FALSE; + +#if (BLE_INCLUDED== TRUE) + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev (bda); + if (p_dev_rec && (p_dev_rec->device_type & BT_DEVICE_TYPE_BLE) == BT_DEVICE_TYPE_BLE) { + le_capable = TRUE; + } +#endif + return le_capable; +} + +/******************************************************************************* +** +** Function btm_sec_find_bonded_dev +** +** Description Find a bonded device starting from the specified index +** +** Returns TRUE - found a bonded device +** +*******************************************************************************/ +#if (BLE_INCLUDED == TRUE) +BOOLEAN btm_sec_find_bonded_dev (UINT8 start_idx, UINT16 *p_found_handle, tBTM_SEC_DEV_REC **p_rec) +{ + BOOLEAN found = FALSE; + +#if (SMP_INCLUDED== TRUE) + tBTM_SEC_DEV_REC *p_dev_rec; + list_node_t *p_node = NULL; + for (p_node = list_begin(btm_cb.p_sec_dev_rec_list); p_node; p_node = list_next(p_node)) { + p_dev_rec = list_node(p_node); + if (p_dev_rec->ble.key_type || (p_dev_rec->sec_flags & BTM_SEC_LINK_KEY_KNOWN)) { + *p_found_handle = p_dev_rec->hci_handle; + *p_rec = p_dev_rec; + break; + } + } + BTM_TRACE_DEBUG ("%s() found=%d\n", __func__, found); +#endif + return (found); +} +#endif ///BLE_INCLUDED == TRUE +/******************************************************************************* +** +** Function btm_sec_use_smp_br_chnl +** +** Description The function checks if SMP BR connection can be used with +** the peer. +** Is called when authentication for dedicated bonding is +** successfully completed. +** +** Returns TRUE - if SMP BR connection can be used (the link key is +** generated from P-256 and the peer supports Security +** Manager over BR). +** +*******************************************************************************/ +#if (SMP_INCLUDED == TRUE) +static BOOLEAN btm_sec_use_smp_br_chnl(tBTM_SEC_DEV_REC *p_dev_rec) +{ + UINT32 ext_feat; + UINT8 chnl_mask[L2CAP_FIXED_CHNL_ARRAY_SIZE]; + + BTM_TRACE_DEBUG ("%s() link_key_type = 0x%x\n", __func__, + p_dev_rec->link_key_type); + + if ((p_dev_rec->link_key_type != BTM_LKEY_TYPE_UNAUTH_COMB_P_256) && + (p_dev_rec->link_key_type != BTM_LKEY_TYPE_AUTH_COMB_P_256)) { + return FALSE; + } + + if (!L2CA_GetPeerFeatures (p_dev_rec->bd_addr, &ext_feat, chnl_mask)) { + return FALSE; + } + + if (!(chnl_mask[0] & L2CAP_FIXED_CHNL_SMP_BR_BIT)) { + return FALSE; + } + + return TRUE; +} + +/******************************************************************************* +** +** Function btm_sec_is_master +** +** Description The function checks if the device is BR/EDR master after +** pairing is completed. +** +** Returns TRUE - if the device is master. +** +*******************************************************************************/ +static BOOLEAN btm_sec_is_master(tBTM_SEC_DEV_REC *p_dev_rec) +{ + tACL_CONN *p = btm_bda_to_acl(p_dev_rec->bd_addr, BT_TRANSPORT_BR_EDR); + return (p && (p->link_role == BTM_ROLE_MASTER)); +} + +#if (CLASSIC_BT_INCLUDED == TRUE) +/******************************************************************************* +** +** Function btm_sec_legacy_authentication_mutual +** +** Description This function is called when legacy authentication is used +** and only remote device has completed the authentication +** +** Returns TRUE if aunthentication command sent successfully +** +*******************************************************************************/ +BOOLEAN btm_sec_legacy_authentication_mutual (tBTM_SEC_DEV_REC *p_dev_rec) +{ + return (btm_sec_start_authentication (p_dev_rec)); +} + +/******************************************************************************* +** +** Function btm_sec_update_legacy_auth_state +** +** Description This function updates the legacy authentication state +** +** Returns void +** +*******************************************************************************/ +void btm_sec_update_legacy_auth_state(tACL_CONN *p_acl_cb, UINT8 legacy_auth_state) +{ + if (p_acl_cb) { + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev_by_handle (p_acl_cb->hci_handle); + if (p_dev_rec) { + if ((BTM_BothEndsSupportSecureConnections(p_dev_rec->bd_addr) == 0) && + (legacy_auth_state != BTM_ACL_LEGACY_AUTH_NONE)) { + p_acl_cb->legacy_auth_state |= legacy_auth_state; + } else { + p_acl_cb->legacy_auth_state = BTM_ACL_LEGACY_AUTH_NONE; + } + } + } +} + +/******************************************************************************* +** +** Function btm_sec_handle_remote_legacy_auth_cmp +** +** Description This function updates the legacy authneticaiton state +** to indicate that remote device has completed the authentication +** +** Returns void +** +*******************************************************************************/ +void btm_sec_handle_remote_legacy_auth_cmp(UINT16 handle) +{ + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev_by_handle (handle); + tACL_CONN *p_acl_cb = btm_bda_to_acl(p_dev_rec->bd_addr, BT_TRANSPORT_BR_EDR); + btm_sec_update_legacy_auth_state(p_acl_cb, BTM_ACL_LEGACY_AUTH_REMOTE); +} +#endif /// (CLASSIC_BT_INCLUDED == TRUE) +#endif ///SMP_INCLUDED == TRUE + +/****************************************************************************** + ** + ** Function btm_sec_dev_authorization + ** + ** Description This function is used to authorize a specified device(BLE) + ** + ****************************************************************************** + */ +#if (BLE_INCLUDED == TRUE) +BOOLEAN btm_sec_dev_authorization(BD_ADDR bd_addr, BOOLEAN authorized) +{ +#if (SMP_INCLUDED == TRUE) + UINT8 sec_flag = 0; + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev(bd_addr); + if (p_dev_rec) { + sec_flag = (UINT8)(p_dev_rec->sec_flags >> 8); + if (!(sec_flag & BTM_SEC_LINK_KEY_AUTHED)) { + BTM_TRACE_ERROR("Authorized should after successful Authentication(MITM protection)\n"); + return FALSE; + } + + if (authorized) { + p_dev_rec->sec_flags |= BTM_SEC_LE_AUTHORIZATION; + } else { + p_dev_rec->sec_flags &= ~(BTM_SEC_LE_AUTHORIZATION); + } + } else { + BTM_TRACE_ERROR("%s, can't find device\n", __func__); + return FALSE; + } + return TRUE; +#endif ///SMP_INCLUDED == TRUE + return FALSE; +} +#endif /// BLE_INCLUDE == TRUE diff --git a/lib/bt/host/bluedroid/stack/btm/include/btm_ble_int.h b/lib/bt/host/bluedroid/stack/btm/include/btm_ble_int.h new file mode 100644 index 00000000..1f55289c --- /dev/null +++ b/lib/bt/host/bluedroid/stack/btm/include/btm_ble_int.h @@ -0,0 +1,548 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * this file contains the main Bluetooth Manager (BTM) internal + * definitions. + * + ******************************************************************************/ + +#ifndef BTM_BLE_INT_H +#define BTM_BLE_INT_H + +#include "common/bt_target.h" +#include "osi/fixed_queue.h" +#include "osi/pkt_queue.h" +#include "osi/thread.h" +#include "stack/hcidefs.h" +#include "stack/btm_ble_api.h" +#include "btm_int.h" + +#if BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE +#include "stack/smp_api.h" +#endif + + +/* scanning enable status */ +#define BTM_BLE_SCAN_ENABLE 0x01 +#define BTM_BLE_SCAN_DISABLE 0x00 + +/* advertising enable status */ +#define BTM_BLE_ADV_ENABLE 0x01 +#define BTM_BLE_ADV_DISABLE 0x00 + +/* use the high 4 bits unused by inquiry mode */ +#define BTM_BLE_SELECT_SCAN 0x20 +#define BTM_BLE_NAME_REQUEST 0x40 +#define BTM_BLE_OBSERVE 0x80 + +#define BTM_BLE_MAX_WL_ENTRY 1 +#define BTM_BLE_AD_DATA_LEN 31 + +#define BTM_BLE_ENC_MASK 0x03 + +#define BTM_BLE_DUPLICATE_DISABLE 0 +#define BTM_BLE_DUPLICATE_ENABLE 1 +#define BTM_BLE_DUPLICATE_MAX BTM_BLE_DUPLICATE_ENABLE + +#define BTM_BLE_GAP_DISC_SCAN_INT 18 /* Interval(scan_int) = 11.25 ms= 0x0010 * 0.625 ms */ +#define BTM_BLE_GAP_DISC_SCAN_WIN 18 /* scan_window = 11.25 ms= 0x0010 * 0.625 ms */ +#define BTM_BLE_GAP_ADV_INT 512 /* Tgap(gen_disc) = 1.28 s= 512 * 0.625 ms */ +#define BTM_BLE_GAP_LIM_TOUT 180 /* Tgap(lim_timeout) = 180s max */ +#define BTM_BLE_LOW_LATENCY_SCAN_INT 8000 /* Interval(scan_int) = 5s= 8000 * 0.625 ms */ +#define BTM_BLE_LOW_LATENCY_SCAN_WIN 8000 /* scan_window = 5s= 8000 * 0.625 ms */ + + +#define BTM_BLE_GAP_ADV_FAST_INT_1 48 /* TGAP(adv_fast_interval1) = 30(used) ~ 60 ms = 48 *0.625 */ +#define BTM_BLE_GAP_ADV_FAST_INT_2 160 /* TGAP(adv_fast_interval2) = 100(used) ~ 150 ms = 160 * 0.625 ms */ +#define BTM_BLE_GAP_ADV_SLOW_INT 2048 /* Tgap(adv_slow_interval) = 1.28 s= 512 * 0.625 ms */ +#define BTM_BLE_GAP_ADV_DIR_MAX_INT 800 /* Tgap(dir_conn_adv_int_max) = 500 ms = 800 * 0.625 ms */ +#define BTM_BLE_GAP_ADV_DIR_MIN_INT 400 /* Tgap(dir_conn_adv_int_min) = 250 ms = 400 * 0.625 ms */ + +#define BTM_BLE_GAP_FAST_ADV_TOUT 30 + +#define BTM_BLE_SEC_REQ_ACT_NONE 0 +#define BTM_BLE_SEC_REQ_ACT_ENCRYPT 1 /* encrypt the link using current key or key refresh */ +#define BTM_BLE_SEC_REQ_ACT_PAIR 2 +#define BTM_BLE_SEC_REQ_ACT_DISCARD 3 /* discard the sec request while encryption is started but not completed */ +typedef UINT8 tBTM_BLE_SEC_REQ_ACT; + +#define BLE_STATIC_PRIVATE_MSB_MASK 0x3f +#define BLE_NON_RESOLVE_ADDR_MSB 0x00 /* most significant bit, bit7, bit6 is 00 to be non-resolvable random */ +#define BLE_RESOLVE_ADDR_MSB 0x40 /* most significant bit, bit7, bit6 is 01 to be resolvable random */ +#define BLE_RESOLVE_ADDR_MASK 0xc0 /* bit 6, and bit7 */ +#define BTM_BLE_IS_NON_RESLVE_BDA(x) ((x[0] & BLE_RESOLVE_ADDR_MASK) == BLE_NON_RESOLVE_ADDR_MSB) +#define BTM_BLE_IS_RESOLVE_BDA(x) ((x[0] & BLE_RESOLVE_ADDR_MASK) == BLE_RESOLVE_ADDR_MSB) + +/* LE scan activity bit mask, continue with LE inquiry bits */ +#define BTM_LE_SELECT_CONN_ACTIVE 0x0040 /* selection connection is in progress */ +#define BTM_LE_OBSERVE_ACTIVE 0x0080 /* observe is in progress */ +#define BTM_LE_DISCOVER_ACTIVE 0x0100 /* scan is in progress */ + +/* BLE scan activity mask checking */ +#define BTM_BLE_IS_SCAN_ACTIVE(x) ((x) & BTM_BLE_SCAN_ACTIVE_MASK) +#define BTM_BLE_IS_INQ_ACTIVE(x) ((x) & BTM_BLE_INQUIRY_MASK) +#define BTM_BLE_IS_OBS_ACTIVE(x) ((x) & BTM_LE_OBSERVE_ACTIVE) +#define BTM_BLE_IS_DISCO_ACTIVE(x) ((x) & BTM_LE_DISCOVER_ACTIVE) +#define BTM_BLE_IS_SEL_CONN_ACTIVE(x) ((x) & BTM_LE_SELECT_CONN_ACTIVE) + +/* BLE ADDR type ID bit */ +#define BLE_ADDR_TYPE_ID_BIT 0x02 + +#define BTM_VSC_CHIP_CAPABILITY_L_VERSION 55 +#define BTM_VSC_CHIP_CAPABILITY_M_VERSION 95 + +typedef enum { + BTM_BLE_IDLE = 0, + BTM_BLE_SCANNING = 1, + BTM_BLE_ADVERTISING = 2, +}tBTM_BLE_GAP_STATE; + +typedef struct { + UINT16 data_mask; + UINT8 *p_flags; + UINT8 ad_data[BTM_BLE_AD_DATA_LEN]; + UINT8 *p_pad; +} tBTM_BLE_LOCAL_ADV_DATA; + +typedef struct { + UINT32 inq_count; /* Used for determining if a response has already been */ + /* received for the current inquiry operation. (We do not */ + /* want to flood the caller with multiple responses from */ + /* the same device. */ + BOOLEAN scan_rsp; + tBLE_BD_ADDR le_bda; +} tINQ_LE_BDADDR; + +#define BTM_BLE_ADV_DATA_LEN_MAX 31 +#define BTM_BLE_CACHE_ADV_DATA_MAX 62 +#if (BLE_50_FEATURE_SUPPORT == TRUE) +#define BTM_BLE_EXT_ADV_DATA_LEN_MAX 251 +#define BTM_BLE_PERIODIC_ADV_DATA_LEN_MAX 252 + +#define BTM_BLE_ADV_DATA_OP_INTERMEDIATE_FRAG 0 +#define BTM_BLE_ADV_DATA_OP_FIRST_FRAG 1 +#define BTM_BLE_ADV_DATA_OP_LAST_FRAG 2 +#define BTM_BLE_ADV_DATA_OP_COMPLETE 3 +#define BTM_BLE_ADV_DATA_OP_UNCHANGED_DATA 4 +#endif // #if (BLE_50_FEATURE_SUPPORT == TRUE) + +#define BTM_BLE_ISVALID_PARAM(x, min, max) (((x) >= (min) && (x) <= (max)) || ((x) == BTM_BLE_CONN_PARAM_UNDEF)) + +typedef struct { + UINT16 discoverable_mode; + UINT16 connectable_mode; + BOOLEAN scan_params_set; + UINT32 scan_window; + UINT32 scan_interval; + UINT8 scan_type; /* current scan type: active or passive */ + UINT8 scan_duplicate_filter; /* duplicate filter enabled for scan */ + UINT16 adv_interval_min; + UINT16 adv_interval_max; + tBTM_BLE_AFP afp; /* advertising filter policy */ + tBTM_BLE_SFP sfp; /* scanning filter policy */ + tBTM_START_ADV_CMPL_CBACK *p_adv_cb; + tBTM_START_STOP_ADV_CMPL_CBACK *p_stop_adv_cb; + tBTM_CLEAR_ADV_CMPL_CBACK *p_clear_adv_cb; + tBLE_ADDR_TYPE adv_addr_type; + UINT8 evt_type; + UINT8 adv_mode; + tBLE_BD_ADDR direct_bda; + tBTM_BLE_EVT directed_conn; + BOOLEAN fast_adv_on; + TIMER_LIST_ENT fast_adv_timer; + + UINT8 adv_len; + UINT8 adv_data_cache[BTM_BLE_CACHE_ADV_DATA_MAX]; + BD_ADDR adv_addr; + /* inquiry BD addr database */ + UINT8 num_bd_entries; + UINT8 max_bd_entries; + tBTM_BLE_LOCAL_ADV_DATA adv_data; + tBTM_BLE_ADV_CHNL_MAP adv_chnl_map; + + TIMER_LIST_ENT inq_timer_ent; + BOOLEAN scan_rsp; + tBTM_BLE_GAP_STATE state; /* Current state that the adv or scan process is in */ + INT8 tx_power; +} tBTM_BLE_INQ_CB; + + +/* random address resolving complete callback */ +typedef void (tBTM_BLE_RESOLVE_CBACK) (void *match_rec, void *p); + +typedef void (tBTM_BLE_ADDR_CBACK) (BD_ADDR_PTR static_random, void *p); + +#define BTM_BLE_GAP_ADDR_BIT_RANDOM (1<<0) +#define BTM_BLE_GAP_ADDR_BIT_RESOLVABLE (1<<1) + +/* random address management control block */ +typedef struct { + tBLE_ADDR_TYPE own_addr_type; /* local device LE address type */ + UINT8 exist_addr_bit; + BD_ADDR static_rand_addr; + BD_ADDR resolvale_addr; + BD_ADDR private_addr; + BD_ADDR random_bda; + BOOLEAN busy; + tBTM_SEC_DEV_REC *p_dev_rec; + tBTM_BLE_RESOLVE_CBACK *p_resolve_cback; + tBTM_BLE_ADDR_CBACK *p_generate_cback; + void *p; + TIMER_LIST_ENT raddr_timer_ent; + tBTM_SET_LOCAL_PRIVACY_CBACK *set_local_privacy_cback; +} tBTM_LE_RANDOM_CB; + +#define BTM_BLE_MAX_BG_CONN_DEV_NUM 10 + +typedef struct { + UINT16 min_conn_int; + UINT16 max_conn_int; + UINT16 slave_latency; + UINT16 supervision_tout; + +} tBTM_LE_CONN_PRAMS; + + +typedef struct { + BD_ADDR bd_addr; + UINT8 attr; + BOOLEAN is_connected; + BOOLEAN in_use; +} tBTM_LE_BG_CONN_DEV; + +/* white list using state as a bit mask */ +#define BTM_BLE_WL_IDLE 0 +#define BTM_BLE_WL_INIT 1 +#define BTM_BLE_WL_SCAN 2 +#define BTM_BLE_WL_ADV 4 +typedef UINT8 tBTM_BLE_WL_STATE; + +/* resolving list using state as a bit mask */ +#define BTM_BLE_RL_IDLE 0 +#define BTM_BLE_RL_INIT 1 +#define BTM_BLE_RL_SCAN 2 +#define BTM_BLE_RL_ADV 4 +typedef UINT8 tBTM_BLE_RL_STATE; + +/* BLE connection state */ +#define BLE_CONN_IDLE 0 +#define BLE_DIR_CONN 1 +#define BLE_BG_CONN 2 +#define BLE_CONN_CANCEL 3 +typedef UINT8 tBTM_BLE_CONN_ST; + +typedef struct { + void *p_param; +} tBTM_BLE_CONN_REQ; + +/* LE state request */ +#define BTM_BLE_STATE_INVALID 0 +#define BTM_BLE_STATE_CONN_ADV 1 +#define BTM_BLE_STATE_INIT 2 +#define BTM_BLE_STATE_MASTER 3 +#define BTM_BLE_STATE_SLAVE 4 +#define BTM_BLE_STATE_LO_DUTY_DIR_ADV 5 +#define BTM_BLE_STATE_HI_DUTY_DIR_ADV 6 +#define BTM_BLE_STATE_NON_CONN_ADV 7 +#define BTM_BLE_STATE_PASSIVE_SCAN 8 +#define BTM_BLE_STATE_ACTIVE_SCAN 9 +#define BTM_BLE_STATE_SCAN_ADV 10 +#define BTM_BLE_STATE_MAX 11 +typedef UINT8 tBTM_BLE_STATE; + +#define BTM_BLE_STATE_CONN_ADV_BIT 0x0001 +#define BTM_BLE_STATE_INIT_BIT 0x0002 +#define BTM_BLE_STATE_MASTER_BIT 0x0004 +#define BTM_BLE_STATE_SLAVE_BIT 0x0008 +#define BTM_BLE_STATE_LO_DUTY_DIR_ADV_BIT 0x0010 +#define BTM_BLE_STATE_HI_DUTY_DIR_ADV_BIT 0x0020 +#define BTM_BLE_STATE_NON_CONN_ADV_BIT 0x0040 +#define BTM_BLE_STATE_PASSIVE_SCAN_BIT 0x0080 +#define BTM_BLE_STATE_ACTIVE_SCAN_BIT 0x0100 +#define BTM_BLE_STATE_SCAN_ADV_BIT 0x0200 +typedef UINT16 tBTM_BLE_STATE_MASK; + +#define BTM_BLE_STATE_ALL_MASK 0x03ff +#define BTM_BLE_STATE_ALL_ADV_MASK (BTM_BLE_STATE_CONN_ADV_BIT|BTM_BLE_STATE_LO_DUTY_DIR_ADV_BIT|BTM_BLE_STATE_HI_DUTY_DIR_ADV_BIT|BTM_BLE_STATE_SCAN_ADV_BIT) +#define BTM_BLE_STATE_ALL_SCAN_MASK (BTM_BLE_STATE_PASSIVE_SCAN_BIT|BTM_BLE_STATE_ACTIVE_SCAN_BIT) +#define BTM_BLE_STATE_ALL_CONN_MASK (BTM_BLE_STATE_MASTER_BIT|BTM_BLE_STATE_SLAVE_BIT) + +#ifndef BTM_LE_RESOLVING_LIST_MAX +#define BTM_LE_RESOLVING_LIST_MAX 0x20 +#endif + +#define BTM_DUPLICATE_SCAN_EXCEPTIONAL_INFO_ADV_ADDR 0 +#define BTM_DUPLICATE_SCAN_EXCEPTIONAL_INFO_MESH_LINK_ID 1 +#define BTM_DUPLICATE_SCAN_EXCEPTIONAL_INFO_MESH_BEACON_TYPE 2 +#define BTM_DUPLICATE_SCAN_EXCEPTIONAL_INFO_MESH_PROV_SRV_ADV 3 +#define BTM_DUPLICATE_SCAN_EXCEPTIONAL_INFO_MESH_PROXY_SRV_ADV 4 + +typedef struct { + BD_ADDR *resolve_q_random_pseudo; + UINT8 *resolve_q_action; + UINT8 q_next; + UINT8 q_pending; +} tBTM_BLE_RESOLVE_Q; + +typedef struct { + BOOLEAN in_use; + BOOLEAN to_add; + BD_ADDR bd_addr; + tBLE_ADDR_TYPE addr_type; + UINT8 attr; +} tBTM_BLE_WL_OP; + +/* BLE privacy mode */ +#define BTM_PRIVACY_NONE 0 /* BLE no privacy */ +#define BTM_PRIVACY_1_1 1 /* BLE privacy 1.1, do not support privacy 1.0 */ +#define BTM_PRIVACY_1_2 2 /* BLE privacy 1.2 */ +#define BTM_PRIVACY_MIXED 3 /* BLE privacy mixed mode, broadcom propietary mode */ +typedef UINT8 tBTM_PRIVACY_MODE; + +/* data length change event callback */ +typedef void (tBTM_DATA_LENGTH_CHANGE_CBACK) (UINT16 max_tx_length, UINT16 max_rx_length); + +/* Define BLE Device Management control structure +*/ +typedef struct { + UINT16 scan_activity; /* LE scan activity mask */ + + /***************************************************** + ** BLE Inquiry + *****************************************************/ + tBTM_BLE_INQ_CB inq_var; + + /* observer callback and timer */ + tBTM_INQ_RESULTS_CB *p_obs_results_cb; + tBTM_CMPL_CB *p_obs_cmpl_cb; + tBTM_INQ_DIS_CB *p_obs_discard_cb; + TIMER_LIST_ENT obs_timer_ent; + + /* scan callback and timer */ + tBTM_INQ_RESULTS_CB *p_scan_results_cb; + tBTM_CMPL_CB *p_scan_cmpl_cb; + TIMER_LIST_ENT scan_timer_ent; + + struct pkt_queue *adv_rpt_queue; + struct osi_event *adv_rpt_ready; + + /* background connection procedure cb value */ + tBTM_BLE_CONN_TYPE bg_conn_type; + UINT32 scan_int; + UINT32 scan_win; + tBTM_BLE_SEL_CBACK *p_select_cback; + /* white list information */ + UINT8 white_list_avail_size; + tBTM_UPDATE_WHITELIST_CBACK *update_wl_cb; + tBTM_BLE_WL_STATE wl_state; + + fixed_queue_t *conn_pending_q; + tBTM_BLE_CONN_ST conn_state; + + /* random address management control block */ + tBTM_LE_RANDOM_CB addr_mgnt_cb; + + BOOLEAN enabled; + +#if BLE_PRIVACY_SPT == TRUE + BOOLEAN mixed_mode; /* privacy 1.2 mixed mode is on or not */ + tBTM_PRIVACY_MODE privacy_mode; /* privacy mode */ + UINT8 resolving_list_avail_size; /* resolving list available size */ + tBTM_BLE_RESOLVE_Q resolving_list_pend_q; /* Resolving list queue */ + tBTM_BLE_RL_STATE suspended_rl_state; /* Suspended resolving list state */ + UINT8 *irk_list_mask; /* IRK list availability mask, up to max entry bits */ + tBTM_BLE_RL_STATE rl_state; /* Resolving list state */ +#endif + + tBTM_BLE_WL_OP wl_op_q[BTM_BLE_MAX_BG_CONN_DEV_NUM]; + + /* current BLE link state */ + tBTM_BLE_STATE_MASK cur_states; /* bit mask of tBTM_BLE_STATE */ + UINT8 link_count[2]; /* total link count master and slave*/ + tBTM_UPDATE_DUPLICATE_EXCEPTIONAL_LIST_CMPL_CBACK *update_exceptional_list_cmp_cb; +} tBTM_BLE_CB; + +#ifdef __cplusplus +extern "C" { +#endif + +void btm_ble_timeout(TIMER_LIST_ENT *p_tle); +void btm_ble_process_adv_pkt (UINT8 *p); +void btm_ble_process_adv_discard_evt(UINT8 *p); +void btm_ble_process_direct_adv_pkt (UINT8 *p); +bool btm_ble_adv_pkt_ready(void); +bool btm_ble_adv_pkt_post(pkt_linked_item_t *pkt); +void btm_ble_proc_scan_rsp_rpt (UINT8 *p); +tBTM_STATUS btm_ble_read_remote_name(BD_ADDR remote_bda, tBTM_INQ_INFO *p_cur, tBTM_CMPL_CB *p_cb); +BOOLEAN btm_ble_cancel_remote_name(BD_ADDR remote_bda); + +tBTM_STATUS btm_ble_set_discoverability(UINT16 combined_mode); +tBTM_STATUS btm_ble_set_connectability(UINT16 combined_mode); +tBTM_STATUS btm_ble_start_inquiry (UINT8 mode, UINT8 duration); +void btm_ble_stop_scan(void); +void btm_clear_all_pending_le_entry(void); + +BOOLEAN btm_ble_send_extended_scan_params(UINT8 scan_type, UINT32 scan_int, UINT32 scan_win, UINT8 addr_type_own, UINT8 scan_filter_policy); +void btm_ble_stop_inquiry(void); +void btm_ble_init (void); +void btm_ble_free (void); +void btm_ble_connected (UINT8 *bda, UINT16 handle, UINT8 enc_mode, UINT8 role, tBLE_ADDR_TYPE addr_type, BOOLEAN addr_matched); +void btm_ble_read_remote_features_complete(UINT8 *p); +void btm_ble_write_adv_enable_complete(UINT8 *p); +void btm_ble_conn_complete(UINT8 *p, UINT16 evt_len, BOOLEAN enhanced); +void btm_read_ble_local_supported_states_complete(UINT8 *p, UINT16 evt_len); +tBTM_BLE_CONN_ST btm_ble_get_conn_st(void); +void btm_ble_set_conn_st(tBTM_BLE_CONN_ST new_st); +UINT8 *btm_ble_build_adv_data(tBTM_BLE_AD_MASK *p_data_mask, UINT8 **p_dst, tBTM_BLE_ADV_DATA *p_data); +tBTM_STATUS btm_ble_start_adv(void); +tBTM_STATUS btm_ble_stop_adv(void); +tBTM_STATUS btm_ble_start_scan(void); +void btm_ble_create_ll_conn_complete (UINT8 status); +void btm_ble_create_conn_cancel_complete (UINT8 *p); +tBTM_STATUS btm_ble_set_random_addr(BD_ADDR random_bda); + +/* LE security function from btm_sec.c */ +#if SMP_INCLUDED == TRUE +void btm_ble_link_sec_check(BD_ADDR bd_addr, tBTM_LE_AUTH_REQ auth_req, tBTM_BLE_SEC_REQ_ACT *p_sec_req_act); +void btm_ble_ltk_request_reply(BD_ADDR bda, BOOLEAN use_stk, BT_OCTET16 stk); +UINT8 btm_proc_smp_cback(tSMP_EVT event, BD_ADDR bd_addr, tSMP_EVT_DATA *p_data); +tBTM_STATUS btm_ble_set_encryption (BD_ADDR bd_addr, void *p_ref_data, UINT8 link_role); +void btm_ble_ltk_request(UINT16 handle, UINT8 rand[8], UINT16 ediv); +tBTM_STATUS btm_ble_start_encrypt(BD_ADDR bda, BOOLEAN use_stk, BT_OCTET16 stk); +void btm_ble_link_encrypted(BD_ADDR bd_addr, UINT8 encr_enable); +#endif + +/* LE device management functions */ +void btm_ble_reset_id( void ); + +/* security related functions */ +void btm_ble_increment_sign_ctr(BD_ADDR bd_addr, BOOLEAN is_local ); +BOOLEAN btm_get_local_div (BD_ADDR bd_addr, UINT16 *p_div); +BOOLEAN btm_ble_get_enc_key_type(BD_ADDR bd_addr, UINT8 *p_key_types); + +void btm_ble_test_command_complete(UINT8 *p); +void btm_ble_rand_enc_complete (UINT8 *p, UINT16 op_code, tBTM_RAND_ENC_CB *p_enc_cplt_cback); + +void btm_sec_save_le_key(BD_ADDR bd_addr, tBTM_LE_KEY_TYPE key_type, tBTM_LE_KEY_VALUE *p_keys, BOOLEAN pass_to_application); +void btm_ble_update_sec_key_size(BD_ADDR bd_addr, UINT8 enc_key_size); +UINT8 btm_ble_read_sec_key_size(BD_ADDR bd_addr); + +/* white list function */ +BOOLEAN btm_update_dev_to_white_list(BOOLEAN to_add, BD_ADDR bd_addr, tBLE_ADDR_TYPE addr_type, tBTM_UPDATE_WHITELIST_CBACK *update_wl_cb); +void btm_update_scanner_filter_policy(tBTM_BLE_SFP scan_policy); +void btm_update_adv_filter_policy(tBTM_BLE_AFP adv_policy); +void btm_ble_clear_white_list (tBTM_UPDATE_WHITELIST_CBACK *update_wl_cb); +void btm_read_white_list_size_complete(UINT8 *p, UINT16 evt_len); +void btm_ble_add_2_white_list_complete(UINT8 status); +void btm_ble_remove_from_white_list_complete(UINT8 *p, UINT16 evt_len); +void btm_ble_clear_white_list_complete(UINT8 *p, UINT16 evt_len); +void btm_ble_white_list_init(UINT8 white_list_size); + +/* background connection function */ +BOOLEAN btm_ble_suspend_bg_conn(void); +BOOLEAN btm_ble_resume_bg_conn(void); +void btm_ble_initiate_select_conn(BD_ADDR bda); +BOOLEAN btm_ble_start_auto_conn(BOOLEAN start); +BOOLEAN btm_ble_start_select_conn(BOOLEAN start, tBTM_BLE_SEL_CBACK *p_select_cback); +BOOLEAN btm_ble_renew_bg_conn_params(BOOLEAN add, BD_ADDR bd_addr); +void btm_write_dir_conn_wl(BD_ADDR target_addr); +BOOLEAN btm_ble_update_mode_operation(UINT8 link_role, BD_ADDR bda, UINT8 status); +BOOLEAN btm_execute_wl_dev_operation(void); +void btm_ble_update_link_topology_mask(UINT8 role, BOOLEAN increase); + +/* direct connection utility */ +BOOLEAN btm_send_pending_direct_conn(void); +void btm_ble_enqueue_direct_conn_req(void *p_param); + +/* BLE address management */ +void btm_gen_resolvable_private_addr (void *p_cmd_cplt_cback); +void btm_gen_non_resolvable_private_addr (tBTM_BLE_ADDR_CBACK *p_cback, void *p); +void btm_ble_resolve_random_addr(BD_ADDR random_bda, tBTM_BLE_RESOLVE_CBACK *p_cback, void *p); +void btm_gen_resolve_paddr_low(tBTM_RAND_ENC *p); + +/* privacy function */ +#if (defined BLE_PRIVACY_SPT && BLE_PRIVACY_SPT == TRUE) +/* BLE address mapping with CS feature */ +BOOLEAN btm_identity_addr_to_random_pseudo(BD_ADDR bd_addr, UINT8 *p_addr_type, BOOLEAN refresh); +BOOLEAN btm_random_pseudo_to_identity_addr(BD_ADDR random_pseudo, UINT8 *p_static_addr_type); +void btm_ble_refresh_peer_resolvable_private_addr(BD_ADDR pseudo_bda, BD_ADDR rra, UINT8 rra_type); +void btm_ble_refresh_local_resolvable_private_addr(BD_ADDR pseudo_addr, BD_ADDR local_rpa); +void btm_ble_read_resolving_list_entry_complete(UINT8 *p, UINT16 evt_len) ; +void btm_ble_set_addr_resolution_enable_complete(UINT8 *p, UINT16 evt_len) ; +void btm_ble_remove_resolving_list_entry_complete(UINT8 *p, UINT16 evt_len); +void btm_ble_add_resolving_list_entry_complete(UINT8 *p, UINT16 evt_len); +void btm_ble_clear_resolving_list_complete(UINT8 *p, UINT16 evt_len); +void btm_read_ble_resolving_list_size_complete (UINT8 *p, UINT16 evt_len); +void btm_ble_enable_resolving_list(UINT8); +BOOLEAN btm_ble_disable_resolving_list(UINT8 rl_mask, BOOLEAN to_resume); +void btm_ble_enable_resolving_list_for_platform (UINT8 rl_mask); +void btm_ble_resolving_list_init(UINT8 max_irk_list_sz); +void btm_ble_resolving_list_cleanup(void); +void btm_ble_add_default_entry_to_resolving_list(void); +#endif + +void btm_ble_multi_adv_configure_rpa (tBTM_BLE_MULTI_ADV_INST *p_inst); +void btm_ble_multi_adv_init(void); +void *btm_ble_multi_adv_get_ref(UINT8 inst_id); +void btm_ble_multi_adv_cleanup(void); +void btm_ble_multi_adv_reenable(UINT8 inst_id); +void btm_ble_multi_adv_enb_privacy(BOOLEAN enable); +char btm_ble_map_adv_tx_power(int tx_power_index); +void btm_ble_batchscan_init(void); +void btm_ble_batchscan_cleanup(void); +void btm_ble_adv_filter_init(void); +void btm_ble_adv_filter_cleanup(void); +BOOLEAN btm_ble_topology_check(tBTM_BLE_STATE_MASK request); +BOOLEAN btm_ble_clear_topology_mask(tBTM_BLE_STATE_MASK request_state); +BOOLEAN btm_ble_set_topology_mask(tBTM_BLE_STATE_MASK request_state); +tBTM_BLE_STATE_MASK btm_ble_get_topology_mask(void); + +#if BTM_BLE_CONFORMANCE_TESTING == TRUE +void btm_ble_set_no_disc_if_pair_fail (BOOLEAN disble_disc); +void btm_ble_set_test_mac_value (BOOLEAN enable, UINT8 *p_test_mac_val); +void btm_ble_set_test_local_sign_cntr_value(BOOLEAN enable, UINT32 test_local_sign_cntr); +void btm_set_random_address(BD_ADDR random_bda); +void btm_ble_set_keep_rfu_in_auth_req(BOOLEAN keep_rfu); +#endif + +BOOLEAN btm_get_current_conn_params(BD_ADDR bda, UINT16 *interval, UINT16 *latency, UINT16 *timeout); + +#if (BLE_50_FEATURE_SUPPORT == TRUE) +void btm_ble_update_phy_evt(tBTM_BLE_UPDATE_PHY *params); +void btm_ble_scan_timeout_evt(void); +void btm_ble_adv_set_terminated_evt(tBTM_BLE_ADV_TERMINAT *params); +void btm_ble_ext_adv_report_evt(tBTM_BLE_EXT_ADV_REPORT *params); +void btm_ble_scan_req_received_evt(tBTM_BLE_SCAN_REQ_RECEIVED *params); +void btm_ble_channel_select_algorithm_evt(tBTM_BLE_CHANNEL_SEL_ALG *params); +void btm_ble_periodic_adv_report_evt(tBTM_PERIOD_ADV_REPORT *params); +void btm_ble_periodic_adv_sync_lost_evt(tBTM_BLE_PERIOD_ADV_SYNC_LOST *params); +void btm_ble_periodic_adv_sync_establish_evt(tBTM_BLE_PERIOD_ADV_SYNC_ESTAB *params); +#endif // #if (BLE_50_FEATURE_SUPPORT == TRUE) + +#if (BLE_FEAT_PERIODIC_ADV_SYNC_TRANSFER == TRUE) +void btm_ble_periodic_adv_sync_trans_recv_evt(tBTM_BLE_PERIOD_ADV_SYNC_TRANS_RECV *params); +#endif // #if (BLE_FEAT_PERIODIC_ADV_SYNC_TRANSFER == TRUE) + +/* +#ifdef __cplusplus +} +#endif +*/ +#endif diff --git a/lib/bt/host/bluedroid/stack/btm/include/btm_int.h b/lib/bt/host/bluedroid/stack/btm/include/btm_int.h new file mode 100644 index 00000000..c22423a1 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/btm/include/btm_int.h @@ -0,0 +1,1258 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * this file contains the main Bluetooth Manager (BTM) internal + * definitions. + * + ******************************************************************************/ +#ifndef BTM_INT_H +#define BTM_INT_H + +typedef struct tBTM_SEC_DEV_REC tBTM_SEC_DEV_REC; + +#include "common/bt_defs.h" +#include "common/bt_target.h" +#include "stack/hcidefs.h" + +#include "stack/rfcdefs.h" + +#include "stack/btm_api.h" +#include "osi/fixed_queue.h" + +#if (BLE_INCLUDED == TRUE) +#include "btm_ble_int.h" +#endif +#if (SMP_INCLUDED == TRUE) +#include "stack/smp_api.h" +#endif + +#define ESP_VS_REM_LEGACY_AUTH_CMP 0x03 + +#if BTM_MAX_LOC_BD_NAME_LEN > 0 +typedef char tBTM_LOC_BD_NAME[BTM_MAX_LOC_BD_NAME_LEN + 1]; +#endif + +#define BTM_ACL_IS_CONNECTED(bda) (btm_bda_to_acl (bda, BT_TRANSPORT_BR_EDR) != NULL) + +/* Definitions for Server Channel Number (SCN) management +*/ +#define BTM_MAX_SCN PORT_MAX_RFC_PORTS + +/* Define masks for supported and exception 2.0 ACL packet types +*/ +#define BTM_ACL_SUPPORTED_PKTS_MASK (HCI_PKT_TYPES_MASK_DM1 | \ + HCI_PKT_TYPES_MASK_DH1 | \ + HCI_PKT_TYPES_MASK_DM3 | \ + HCI_PKT_TYPES_MASK_DH3 | \ + HCI_PKT_TYPES_MASK_DM5 | \ + HCI_PKT_TYPES_MASK_DH5) + +#define BTM_ACL_EXCEPTION_PKTS_MASK (HCI_PKT_TYPES_MASK_NO_2_DH1 | \ + HCI_PKT_TYPES_MASK_NO_3_DH1 | \ + HCI_PKT_TYPES_MASK_NO_2_DH3 | \ + HCI_PKT_TYPES_MASK_NO_3_DH3 | \ + HCI_PKT_TYPES_MASK_NO_2_DH5 | \ + HCI_PKT_TYPES_MASK_NO_3_DH5) + +#define BTM_EPR_AVAILABLE(p) ((HCI_ATOMIC_ENCRYPT_SUPPORTED((p)->peer_lmp_features[HCI_EXT_FEATURES_PAGE_0]) && \ + HCI_ATOMIC_ENCRYPT_SUPPORTED(controller_get_interface()->get_features_classic(0)->as_array)) \ + ? TRUE : FALSE) + +#define BTM_IS_BRCM_CONTROLLER() (controller_get_interface()->get_bt_version()->manufacturer == LMP_COMPID_BROADCOM) + +typedef struct t_acl_db_param{ +#define ACL_DB_HANDLE 0x00 +#define ACL_DB_BDA 0x01 + UINT8 type; + void *p_data1; + void *p_data2; +}tACL_DB_PARAM; + +enum { + BTM_PM_ST_ACTIVE = BTM_PM_STS_ACTIVE, + BTM_PM_ST_HOLD = BTM_PM_STS_HOLD, + BTM_PM_ST_SNIFF = BTM_PM_STS_SNIFF, + BTM_PM_ST_PARK = BTM_PM_STS_PARK, + BTM_PM_ST_PENDING = BTM_PM_STS_PENDING +}; +typedef UINT8 tBTM_PM_STATE; + +typedef struct { + tBTM_PM_PWR_MD req_mode[BTM_MAX_PM_RECORDS + 1]; /* the desired mode and parameters of the connection*/ + tBTM_PM_PWR_MD set_mode; /* the mode and parameters sent down to the host controller. */ + UINT16 interval; /* the interval from last mode change event. */ +#if (BTM_SSR_INCLUDED == TRUE) + UINT16 max_lat; /* stored SSR maximum latency */ + UINT16 min_rmt_to;/* stored SSR minimum remote timeout */ + UINT16 min_loc_to;/* stored SSR minimum local timeout */ +#endif + tBTM_PM_STATE state; /* contains the current mode of the connection */ + BOOLEAN chg_ind; /* a request change indication */ +} tBTM_PM_MCB; + +/* Define the ACL Management control structure +*/ +typedef struct { +UINT16 hci_handle; +UINT16 pkt_types_mask; +UINT16 clock_offset; +BD_ADDR remote_addr; +DEV_CLASS remote_dc; +BD_NAME remote_name; + +UINT16 manufacturer; +UINT16 lmp_subversion; +UINT16 link_super_tout; +BD_FEATURES peer_lmp_features[HCI_EXT_FEATURES_PAGE_MAX + 1]; /* Peer LMP Extended features mask table for the device */ +UINT8 num_read_pages; +UINT8 lmp_version; + +BOOLEAN in_use; +UINT8 link_role; +BOOLEAN link_up_issued; /* True if busy_level link up has been issued */ +BOOLEAN sc_downgrade; /* Store if security is downgraded or not. */ + +#define BTM_ACL_LEGACY_AUTH_NONE (0) +#define BTM_ACL_LEGACY_AUTH_SELF (1<<0) +#define BTM_ACL_LEGACY_AUTH_REMOTE (1<<1) +#define BTM_ACL_LEGACY_AUTH_MUTUAL (1<<2) +UINT8 legacy_auth_state; + +#define BTM_ACL_SWKEY_STATE_IDLE 0 +#define BTM_ACL_SWKEY_STATE_MODE_CHANGE 1 +#define BTM_ACL_SWKEY_STATE_ENCRYPTION_OFF 2 +#define BTM_ACL_SWKEY_STATE_SWITCHING 3 +#define BTM_ACL_SWKEY_STATE_ENCRYPTION_ON 4 +#define BTM_ACL_SWKEY_STATE_IN_PROGRESS 5 +UINT8 switch_role_state; + +#define BTM_ACL_ENCRYPT_STATE_IDLE 0 +#define BTM_ACL_ENCRYPT_STATE_ENCRYPT_OFF 1 /* encryption turning off */ +#define BTM_ACL_ENCRYPT_STATE_TEMP_FUNC 2 /* temporarily off for change link key or role switch */ +#define BTM_ACL_ENCRYPT_STATE_ENCRYPT_ON 3 /* encryption turning on */ +UINT8 encrypt_state; /* overall BTM encryption state */ + +#if BLE_INCLUDED == TRUE +tBT_TRANSPORT transport; +BD_ADDR conn_addr; /* local device address used for this connection */ +UINT8 conn_addr_type; /* local device address type for this connection */ +BD_ADDR active_remote_addr; /* remote address used on this connection */ +UINT8 active_remote_addr_type; /* local device address type for this connection */ +BD_FEATURES peer_le_features; /* Peer LE Used features mask for the device */ +tBTM_SET_PKT_DATA_LENGTH_CBACK *p_set_pkt_data_cback; +tBTM_LE_SET_PKT_DATA_LENGTH_PARAMS data_length_params; +BOOLEAN data_len_updating; +// data len update cmd cache +BOOLEAN data_len_waiting; +tBTM_SET_PKT_DATA_LENGTH_CBACK *p_set_data_len_cback_waiting; +UINT16 tx_len_waiting; +#endif +tBTM_PM_MCB *p_pm_mode_db; /* Pointer to PM mode control block per ACL link */ + +} tACL_CONN; + +/***************************************************** +** TIMER Definitions +******************************************************/ +#define TT_DEV_RESET 1 +#define TT_DEV_RLN 2 +#define TT_DEV_RLNKP 4 /* Read Link Policy Settings */ + +/* Define the Device Management control structure +*/ +typedef struct { +tBTM_DEV_STATUS_CB *p_dev_status_cb; /* Device status change callback */ +tBTM_VS_EVT_CB *p_vend_spec_cb[BTM_MAX_VSE_CALLBACKS]; /* Register for vendor specific events */ + +tBTM_CMPL_CB *p_stored_link_key_cmpl_cb; /* Read/Write/Delete stored link key */ + +TIMER_LIST_ENT reset_timer; +tBTM_CMPL_CB *p_reset_cmpl_cb; + +TIMER_LIST_ENT rln_timer; +tBTM_CMPL_CB *p_rln_cmpl_cb; /* Callback function to be called when */ +/* read local name function complete */ +TIMER_LIST_ENT rssi_timer; +tBTM_CMPL_CB *p_rssi_cmpl_cb; /* Callback function to be called when */ +/* read rssi function completes */ +TIMER_LIST_ENT lnk_quality_timer; +tBTM_CMPL_CB *p_lnk_qual_cmpl_cb;/* Callback function to be called when */ +/* read link quality function completes */ +TIMER_LIST_ENT txpwer_timer; +tBTM_CMPL_CB *p_txpwer_cmpl_cb; /* Callback function to be called when */ +/* read inq tx power function completes */ + +TIMER_LIST_ENT qossu_timer; +tBTM_CMPL_CB *p_qossu_cmpl_cb; /* Callback function to be called when */ +/* qos setup function completes */ + +tBTM_ROLE_SWITCH_CMPL switch_role_ref_data; +tBTM_CMPL_CB *p_switch_role_cb; /* Callback function to be called when */ +/* requested switch role is completed */ + +TIMER_LIST_ENT tx_power_timer; +tBTM_CMPL_CB *p_tx_power_cmpl_cb;/* Callback function to be called */ + +#if CLASSIC_BT_INCLUDED == TRUE +TIMER_LIST_ENT afh_channels_timer; +tBTM_CMPL_CB *p_afh_channels_cmpl_cb; /* Callback function to be called When */ +/* set AFH channels is completed */ + +TIMER_LIST_ENT page_timeout_set_timer; +tBTM_CMPL_CB *p_page_to_set_cmpl_cb; /* Callback function to be called when */ +/* set page timeout is completed */ +TIMER_LIST_ENT set_acl_pkt_types_timer; +tBTM_CMPL_CB *p_set_acl_pkt_types_cmpl_cb; /* Callback function to be called when */ +/* set ACL packet types is completed */ +#endif + +DEV_CLASS dev_class; /* Local device class */ + +#if BLE_INCLUDED == TRUE + +TIMER_LIST_ENT ble_channels_timer; +tBTM_CMPL_CB *p_ble_channels_cmpl_cb; /* Callback function to be called When + ble set host channels is completed */ + +tBTM_CMPL_CB *p_le_test_cmd_cmpl_cb; /* Callback function to be called when + LE test mode command has been sent successfully */ + +BD_ADDR read_tx_pwr_addr; /* read TX power target address */ + +#define BTM_LE_SUPPORT_STATE_SIZE 8 +UINT8 le_supported_states[BTM_LE_SUPPORT_STATE_SIZE]; + +tBTM_BLE_LOCAL_ID_KEYS id_keys; /* local BLE ID keys */ +BT_OCTET16 ble_encryption_key_value; /* BLE encryption key */ + +#if BTM_BLE_CONFORMANCE_TESTING == TRUE +BOOLEAN no_disc_if_pair_fail; +BOOLEAN enable_test_mac_val; +BT_OCTET8 test_mac; +BOOLEAN enable_test_local_sign_cntr; +UINT32 test_local_sign_cntr; +#endif + +#endif /* BLE_INCLUDED */ + +tBTM_IO_CAP loc_io_caps; /* IO capability of the local device */ +tBTM_AUTH_REQ loc_auth_req; /* the auth_req flag */ +BOOLEAN secure_connections_only; /* Rejects service level 0 connections if */ +/* itself or peer device doesn't support */ +/* secure connections */ +} tBTM_DEVCB; + + +/* Define the structures and constants used for inquiry +*/ + +/* Definitions of limits for inquiries */ +#define BTM_PER_INQ_MIN_MAX_PERIOD HCI_PER_INQ_MIN_MAX_PERIOD +#define BTM_PER_INQ_MAX_MAX_PERIOD HCI_PER_INQ_MAX_MAX_PERIOD +#define BTM_PER_INQ_MIN_MIN_PERIOD HCI_PER_INQ_MIN_MIN_PERIOD +#define BTM_PER_INQ_MAX_MIN_PERIOD HCI_PER_INQ_MAX_MIN_PERIOD +#define BTM_MAX_INQUIRY_LENGTH HCI_MAX_INQUIRY_LENGTH +#define BTM_MIN_INQUIRY_LEN 0x01 + +#define BTM_MIN_INQ_TX_POWER -70 +#define BTM_MAX_INQ_TX_POWER 20 + +typedef struct { +UINT32 inq_count; /* Used for determining if a response has already been */ +/* received for the current inquiry operation. (We do not */ +/* want to flood the caller with multiple responses from */ +/* the same device. */ +BD_ADDR bd_addr; +} tINQ_BDADDR; + +typedef struct { +UINT32 time_of_resp; +UINT32 inq_count; /* "timestamps" the entry with a particular inquiry count */ +/* Used for determining if a response has already been */ +/* received for the current inquiry operation. (We do not */ +/* want to flood the caller with multiple responses from */ +/* the same device. */ +tBTM_INQ_INFO inq_info; +BOOLEAN in_use; + +#if (BLE_INCLUDED == TRUE) +BOOLEAN scan_rsp; +#endif +} tINQ_DB_ENT; + + +enum { +INQ_NONE, +INQ_LE_OBSERVE, +INQ_GENERAL +}; +typedef UINT8 tBTM_INQ_TYPE; + +typedef struct { + tBTM_CMPL_CB *p_remname_cmpl_cb; + +#define BTM_EXT_RMT_NAME_TIMEOUT 40 + + + TIMER_LIST_ENT rmt_name_timer_ent; + + UINT16 discoverable_mode; + UINT16 connectable_mode; + UINT16 page_scan_window; + UINT16 page_scan_period; + UINT16 inq_scan_window; + UINT16 inq_scan_period; + UINT16 inq_scan_type; + UINT16 page_scan_type; /* current page scan type */ + tBTM_INQ_TYPE scan_type; + UINT16 page_timeout; + + BD_ADDR remname_bda; /* Name of bd addr for active remote name request */ +#define BTM_RMT_NAME_INACTIVE 0 +#define BTM_RMT_NAME_EXT 0x1 /* Initiated through API */ +#define BTM_RMT_NAME_SEC 0x2 /* Initiated internally by security manager */ +#define BTM_RMT_NAME_INQ 0x4 /* Remote name initiated internally by inquiry */ + BOOLEAN remname_active; /* State of a remote name request by external API */ + + tBTM_CMPL_CB *p_inq_cmpl_cb; + tBTM_INQ_RESULTS_CB *p_inq_results_cb; + tBTM_CMPL_CB *p_inq_ble_cmpl_cb; /*completion callback exclusively for LE Observe*/ + tBTM_INQ_RESULTS_CB *p_inq_ble_results_cb;/*results callback exclusively for LE observe*/ + tBTM_CMPL_CB *p_inqfilter_cmpl_cb; /* Called (if not NULL) after inquiry filter completed */ + UINT32 inq_counter; /* Counter incremented each time an inquiry completes */ + /* Used for determining whether or not duplicate devices */ + /* have responded to the same inquiry */ + TIMER_LIST_ENT inq_timer_ent; + tINQ_BDADDR *p_bd_db; /* Pointer to memory that holds bdaddrs */ + UINT16 num_bd_entries; /* Number of entries in database */ + UINT16 max_bd_entries; /* Maximum number of entries that can be stored */ + tINQ_DB_ENT inq_db[BTM_INQ_DB_SIZE]; + tBTM_INQ_PARMS inqparms; /* Contains the parameters for the current inquiry */ + tBTM_INQUIRY_CMPL inq_cmpl_info; /* Status and number of responses from the last inquiry */ + + UINT16 per_min_delay; /* Current periodic minimum delay */ + UINT16 per_max_delay; /* Current periodic maximum delay */ + BOOLEAN inqfilt_active; + UINT8 pending_filt_complete_event; /* to take care of btm_event_filter_complete corresponding to */ + /* inquiry that has been cancelled*/ + UINT8 inqfilt_type; /* Contains the inquiry filter type (BD ADDR, COD, or Clear) */ + +#define BTM_INQ_INACTIVE_STATE 0 +#define BTM_INQ_CLR_FILT_STATE 1 /* Currently clearing the inquiry filter preceeding the inquiry request */ + /* (bypassed if filtering is not used) */ +#define BTM_INQ_SET_FILT_STATE 2 /* Sets the new filter (or turns off filtering) in this state */ +#define BTM_INQ_ACTIVE_STATE 3 /* Actual inquiry or periodic inquiry is in progress */ +#define BTM_INQ_REMNAME_STATE 4 /* Remote name requests are active */ + + UINT8 state; /* Current state that the inquiry process is in */ + UINT8 inq_active; /* Bit Mask indicating type of inquiry is active */ + BOOLEAN no_inc_ssp; /* TRUE, to stop inquiry on incoming SSP */ +#if (defined(BTA_HOST_INTERLEAVE_SEARCH) && BTA_HOST_INTERLEAVE_SEARCH == TRUE) + btm_inq_state next_state; /*interleaving state to determine next mode to be inquired*/ +#endif +} tBTM_INQUIRY_VAR_ST; + +/* The MSB of the clock offset field indicates that the offset is valid if TRUE */ +#define BTM_CLOCK_OFFSET_VALID 0x8000 + +/* Define the structures needed by security management +*/ + +#define BTM_SEC_INVALID_HANDLE 0xFFFF + +typedef UINT8 *BTM_BD_NAME_PTR; /* Pointer to Device name */ + +/* Security callback is called by this unit when security +** procedures are completed. Parameters are +** BD Address of remote +** Result of the operation +*/ +typedef tBTM_SEC_CBACK tBTM_SEC_CALLBACK; + +#define BTM_DATA_HANDLE_MASK 0x0FFF + +#define BTMD_GET_HANDLE(u16) (UINT16)((u16) & BTM_DATA_HANDLE_MASK) + +typedef void (tBTM_SCO_IND_CBACK) (UINT16 sco_inx) ; + +/* MACROs to convert from SCO packet types mask to ESCO and back */ +#define BTM_SCO_PKT_TYPE_MASK ( HCI_PKT_TYPES_MASK_HV1 \ + | HCI_PKT_TYPES_MASK_HV2 \ + | HCI_PKT_TYPES_MASK_HV3) + +/* Mask defining only the SCO types of an esco packet type */ +#define BTM_ESCO_PKT_TYPE_MASK ( HCI_ESCO_PKT_TYPES_MASK_HV1 \ + | HCI_ESCO_PKT_TYPES_MASK_HV2 \ + | HCI_ESCO_PKT_TYPES_MASK_HV3) + +#define BTM_SCO_2_ESCO(scotype) ((UINT16)(((scotype) & BTM_SCO_PKT_TYPE_MASK) >> 5)) +#define BTM_ESCO_2_SCO(escotype) ((UINT16)(((escotype) & BTM_ESCO_PKT_TYPE_MASK) << 5)) + +/* Define masks for supported and exception 2.0 SCO packet types +*/ +#define BTM_SCO_SUPPORTED_PKTS_MASK (HCI_ESCO_PKT_TYPES_MASK_HV1 | \ + HCI_ESCO_PKT_TYPES_MASK_HV2 | \ + HCI_ESCO_PKT_TYPES_MASK_HV3 | \ + HCI_ESCO_PKT_TYPES_MASK_EV3 | \ + HCI_ESCO_PKT_TYPES_MASK_EV4 | \ + HCI_ESCO_PKT_TYPES_MASK_EV5) + +#define BTM_SCO_EXCEPTION_PKTS_MASK (HCI_ESCO_PKT_TYPES_MASK_NO_2_EV3 | \ + HCI_ESCO_PKT_TYPES_MASK_NO_3_EV3 | \ + HCI_ESCO_PKT_TYPES_MASK_NO_2_EV5 | \ + HCI_ESCO_PKT_TYPES_MASK_NO_3_EV5) + + +#define BTM_SCO_ROUTE_UNKNOWN 0xff + +/* Define the structure that contains (e)SCO data */ +typedef struct { + tBTM_ESCO_CBACK *p_esco_cback; /* Callback for eSCO events */ + tBTM_ESCO_PARAMS setup; + tBTM_ESCO_DATA data; /* Connection complete information */ + UINT8 hci_status; +} tBTM_ESCO_INFO; + +/* Define the structure used for SCO Management +*/ +typedef struct { + tBTM_ESCO_INFO esco; /* Current settings */ +#if BTM_SCO_HCI_INCLUDED == TRUE +#define BTM_SCO_XMIT_QUEUE_THRS 30 +#define BTM_SCO_XMIT_QUEUE_HIGH_WM 20 + fixed_queue_t *xmit_data_q; /* SCO data transmitting queue */ + INT16 sent_not_acked; + tBTM_SCO_PKT_STAT_NUMS pkt_stat_nums; +#endif + tBTM_SCO_CB *p_conn_cb; /* Callback for when connected */ + tBTM_SCO_CB *p_disc_cb; /* Callback for when disconnect */ + UINT16 state; /* The state of the SCO link */ + UINT16 hci_handle; /* HCI Handle */ + BOOLEAN is_orig; /* TRUE if the originator */ + BOOLEAN rem_bd_known; /* TRUE if remote BD addr known */ +} tSCO_CONN; + +/* SCO Management control block */ +typedef struct { + tBTM_SCO_IND_CBACK *app_sco_ind_cb; +#if BTM_SCO_HCI_INCLUDED == TRUE + tBTM_SCO_DATA_CB *p_data_cb; /* Callback for SCO data over HCI */ + UINT32 xmit_window_size; /* Total SCO window in bytes */ + UINT16 num_lm_sco_bufs; +#endif + tSCO_CONN sco_db[BTM_MAX_SCO_LINKS]; + tBTM_ESCO_PARAMS def_esco_parms; + BD_ADDR xfer_addr; + UINT16 sco_disc_reason; + BOOLEAN esco_supported; /* TRUE if 1.2 cntlr AND supports eSCO links */ + tBTM_SCO_TYPE desired_sco_mode; + tBTM_SCO_TYPE xfer_sco_type; + tBTM_SCO_PCM_PARAM sco_pcm_param; + tBTM_SCO_CODEC_TYPE codec_in_use; /* None, CVSD, MSBC, etc. */ +#if BTM_SCO_HCI_INCLUDED == TRUE + tBTM_SCO_ROUTE_TYPE sco_path; +#endif + +} tSCO_CB; + + +#if BTM_SCO_INCLUDED == TRUE +void btm_set_sco_ind_cback( tBTM_SCO_IND_CBACK *sco_ind_cb ); +void btm_accept_sco_link(UINT16 sco_inx, tBTM_ESCO_PARAMS *p_setup, + tBTM_SCO_CB *p_conn_cb, tBTM_SCO_CB *p_disc_cb); +void btm_reject_sco_link(UINT16 sco_inx ); +void btm_sco_chk_pend_rolechange (UINT16 hci_handle); +#else +#define btm_accept_sco_link(sco_inx, p_setup, p_conn_cb, p_disc_cb) +#define btm_reject_sco_link(sco_inx) +#define btm_set_sco_ind_cback(sco_ind_cb) +#define btm_sco_chk_pend_rolechange(hci_handle) +#endif /* BTM_SCO_INCLUDED */ + +/* +** Define structure for Security Service Record. +** A record exists for each service registered with the Security Manager +*/ +#define BTM_SEC_OUT_FLAGS (BTM_SEC_OUT_AUTHENTICATE | BTM_SEC_OUT_ENCRYPT | BTM_SEC_OUT_AUTHORIZE) +#define BTM_SEC_IN_FLAGS (BTM_SEC_IN_AUTHENTICATE | BTM_SEC_IN_ENCRYPT | BTM_SEC_IN_AUTHORIZE) + +#define BTM_SEC_OUT_LEVEL4_FLAGS (BTM_SEC_OUT_AUTHENTICATE | BTM_SEC_OUT_ENCRYPT | \ + BTM_SEC_OUT_MITM | BTM_SEC_MODE4_LEVEL4) + +#define BTM_SEC_IN_LEVEL4_FLAGS (BTM_SEC_IN_AUTHENTICATE | BTM_SEC_IN_ENCRYPT | \ + BTM_SEC_IN_MITM | BTM_SEC_MODE4_LEVEL4) + +typedef struct { + UINT32 mx_proto_id; /* Service runs over this multiplexer protocol */ + UINT32 orig_mx_chan_id; /* Channel on the multiplexer protocol */ + UINT32 term_mx_chan_id; /* Channel on the multiplexer protocol */ + UINT16 psm; /* L2CAP PSM value */ + UINT16 security_flags; /* Bitmap of required security features */ + UINT8 service_id; /* Passed in authorization callback */ +#if (L2CAP_UCD_INCLUDED == TRUE) + UINT16 ucd_security_flags; /* Bitmap of required security features for UCD */ +#endif +#if BTM_SEC_SERVICE_NAME_LEN > 0 + UINT8 orig_service_name[BTM_SEC_SERVICE_NAME_LEN + 1]; + UINT8 term_service_name[BTM_SEC_SERVICE_NAME_LEN + 1]; +#endif +} tBTM_SEC_SERV_REC; + +#if BLE_INCLUDED == TRUE +/* LE Security information of device in Slave Role */ +typedef struct { + BT_OCTET16 irk; /* peer diverified identity root */ + BT_OCTET16 pltk; /* peer long term key */ + BT_OCTET16 pcsrk; /* peer SRK peer device used to secured sign local data */ + + BT_OCTET16 lltk; /* local long term key */ + BT_OCTET16 lcsrk; /* local SRK peer device used to secured sign local data */ + + BT_OCTET8 rand; /* random vector for LTK generation */ + UINT16 ediv; /* LTK diversifier of this slave device */ + UINT16 div; /* local DIV to generate local LTK=d1(ER,DIV,0) and CSRK=d1(ER,DIV,1) */ + UINT8 sec_level; /* local pairing security level */ + UINT8 key_size; /* key size of the LTK delivered to peer device */ + UINT8 srk_sec_level; /* security property of peer SRK for this device */ + UINT8 local_csrk_sec_level; /* security property of local CSRK for this device */ + + UINT32 counter; /* peer sign counter for verifying rcv signed cmd */ + UINT32 local_counter; /* local sign counter for sending signed write cmd*/ +} tBTM_SEC_BLE_KEYS; + +typedef struct { + BD_ADDR pseudo_addr; /* LE pseudo address of the device if different from device address */ + tBLE_ADDR_TYPE ble_addr_type; /* LE device type: public or random address */ + tBLE_ADDR_TYPE static_addr_type; /* static address type */ + BD_ADDR static_addr; /* static address */ + +#define BTM_WHITE_LIST_BIT 0x01 +#define BTM_RESOLVING_LIST_BIT 0x02 + UINT8 in_controller_list; /* in controller resolving list or not */ + UINT8 resolving_list_index; +#if BLE_PRIVACY_SPT == TRUE + BD_ADDR cur_rand_addr; /* current random address */ + +#define BTM_BLE_ADDR_PSEUDO 0 /* address index device record */ +#define BTM_BLE_ADDR_RRA 1 /* cur_rand_addr */ +#define BTM_BLE_ADDR_STATIC 2 /* static_addr */ + UINT8 active_addr_type; +#endif + +#if SMP_INCLUDED == TRUE + tBTM_LE_KEY_TYPE key_type; /* bit mask of valid key types in record */ + tBTM_SEC_BLE_KEYS keys; /* LE device security info in slave rode */ +#if (SMP_SLAVE_CON_PARAMS_UPD_ENABLE == TRUE) + bool skip_update_conn_param; /* skip update connection paraams or not*/ +#endif + UINT16 auth_mode; /* Authentication mode */ +#endif +#if (BLE_PRIVACY_SPT == TRUE && (!CONTROLLER_RPA_LIST_ENABLE)) + tBLE_ADDR_TYPE current_addr_type; /* current adv addr type*/ + BD_ADDR current_addr; /* current adv addr*/ + bool current_addr_valid; /* current addr info is valid or not*/ +#endif +} tBTM_SEC_BLE; + + +#endif /* BLE_INCLUDED */ + +/* Peering bond type */ +enum { + BOND_TYPE_UNKNOWN, + BOND_TYPE_PERSISTENT, + BOND_TYPE_TEMPORARY +}; +typedef UINT8 tBTM_BOND_TYPE; + +/* +** Define structure for Security Device Record. +** A record exists for each device authenticated with this device +*/ +struct tBTM_SEC_DEV_REC{ + tBTM_SEC_SERV_REC *p_cur_service; + tBTM_SEC_CALLBACK *p_callback; + void *p_ref_data; + UINT32 timestamp; /* Timestamp of the last connection */ + UINT32 trusted_mask[BTM_SEC_SERVICE_ARRAY_SIZE]; /* Bitwise OR of trusted services */ + UINT16 hci_handle; /* Handle to connection when exists */ + UINT16 clock_offset; /* Latest known clock offset */ + BD_ADDR bd_addr; /* BD_ADDR of the device */ + DEV_CLASS dev_class; /* DEV_CLASS of the device */ + LINK_KEY link_key; /* Device link key */ + UINT8 pin_code_length; /* Length of the pin_code used for paring */ + +#define BTM_SEC_AUTHORIZED BTM_SEC_FLAG_AUTHORIZED /* 0x01 */ +#define BTM_SEC_AUTHENTICATED BTM_SEC_FLAG_AUTHENTICATED /* 0x02 */ +#define BTM_SEC_ENCRYPTED BTM_SEC_FLAG_ENCRYPTED /* 0x04 */ +#define BTM_SEC_NAME_KNOWN 0x08 +#define BTM_SEC_LINK_KEY_KNOWN BTM_SEC_FLAG_LKEY_KNOWN /* 0x10 */ +#define BTM_SEC_LINK_KEY_AUTHED BTM_SEC_FLAG_LKEY_AUTHED /* 0x20 */ +#define BTM_SEC_ROLE_SWITCHED 0x40 +#define BTM_SEC_IN_USE 0x80 + /* LE link security flag */ +#define BTM_SEC_LE_AUTHORIZATION 0x0100 /* LE link is authorized */ +#define BTM_SEC_LE_AUTHENTICATED 0x0200 /* LE link is encrypted after pairing with MITM */ +#define BTM_SEC_LE_ENCRYPTED 0x0400 /* LE link is encrypted */ +#define BTM_SEC_LE_NAME_KNOWN 0x0800 /* not used */ +#define BTM_SEC_LE_LINK_KEY_KNOWN 0x1000 /* bonded with peer (peer LTK and/or SRK is saved) */ +#define BTM_SEC_LE_LINK_KEY_AUTHED 0x2000 /* pairing is done with MITM */ +#define BTM_SEC_16_DIGIT_PIN_AUTHED 0x4000 /* pairing is done with 16 digit pin */ + + UINT16 sec_flags; /* Current device security state */ + + tBTM_BD_NAME sec_bd_name; /* User friendly name of the device. (may be truncated to save space in dev_rec table) */ + BD_FEATURES features[HCI_EXT_FEATURES_PAGE_MAX + 1]; /* Features supported by the device */ + UINT8 num_read_pages; + +#define BTM_SEC_STATE_IDLE 0 +#define BTM_SEC_STATE_AUTHENTICATING 1 +#define BTM_SEC_STATE_ENCRYPTING 2 +#define BTM_SEC_STATE_GETTING_NAME 3 +#define BTM_SEC_STATE_AUTHORIZING 4 +#define BTM_SEC_STATE_SWITCHING_ROLE 5 +#define BTM_SEC_STATE_DISCONNECTING 6 /* disconnecting BR/EDR */ +#define BTM_SEC_STATE_DELAY_FOR_ENC 7 /* delay to check for encryption to work around */ + /* controller problems */ +#define BTM_SEC_STATE_DISCONNECTING_BLE 8 /* disconnecting BLE */ +#define BTM_SEC_STATE_DISCONNECTING_BOTH 9 /* disconnecting BR/EDR and BLE */ + + UINT8 sec_state; /* Operating state */ + BOOLEAN is_originator; /* TRUE if device is originating connection */ +#if (L2CAP_UCD_INCLUDED == TRUE) + BOOLEAN is_ucd; /* TRUE if device is sending or receiving UCD */ + /* if incoming security failed, received UCD will be discarded */ +#endif + BOOLEAN role_master; /* TRUE if current mode is master */ + UINT16 security_required; /* Security required for connection */ + BOOLEAN link_key_not_sent; /* link key notification has not been sent waiting for name */ + UINT8 link_key_type; /* Type of key used in pairing */ + BOOLEAN link_key_changed; /* Changed link key during current connection */ + +#define BTM_MAX_PRE_SM4_LKEY_TYPE BTM_LKEY_TYPE_REMOTE_UNIT /* the link key type used by legacy pairing */ + +#define BTM_SM4_UNKNOWN 0x00 +#define BTM_SM4_KNOWN 0x10 +#define BTM_SM4_TRUE 0x11 +#define BTM_SM4_REQ_PEND 0x08 /* set this bit when getting remote features */ +#define BTM_SM4_UPGRADE 0x04 /* set this bit when upgrading link key */ +#define BTM_SM4_RETRY 0x02 /* set this bit to retry on HCI_ERR_KEY_MISSING or HCI_ERR_LMP_ERR_TRANS_COLLISION */ +#define BTM_SM4_DD_ACP 0x20 /* set this bit to indicate peer initiated dedicated bonding */ +#define BTM_SM4_CONN_PEND 0x40 /* set this bit to indicate accepting acl conn; to be cleared on btm_acl_created */ + UINT8 sm4; /* BTM_SM4_TRUE, if the peer supports SM4 */ + tBTM_IO_CAP rmt_io_caps; /* IO capability of the peer device */ + tBTM_AUTH_REQ rmt_auth_req; /* the auth_req flag as in the IO caps rsp evt */ + BOOLEAN remote_supports_secure_connections; + BOOLEAN remote_features_needed; /* set to true if the local device is in */ + /* "Secure Connections Only" mode and it receives */ + /* HCI_IO_CAPABILITY_REQUEST_EVT from the peer before */ + /* it knows peer's support for Secure Connections */ + BOOLEAN remote_secure_connection_previous_state; /* Stores if peer ever supported + secure connection. This will be helpful to know when peer device downgrades it's security. */ + + UINT16 ble_hci_handle; /* use in DUMO connection */ + +#define BTM_ENC_MODE_UNKNOWN 0xff + UINT8 enc_mode; /* encryption mode of current link */ + UINT8 enc_key_size; /* current link encryption key size */ + tBT_DEVICE_TYPE device_type; + BOOLEAN new_encryption_key_is_p256; /* Set to TRUE when the newly generated LK + ** is generated from P-256. + ** Link encrypted with such LK can be used + ** for SM over BR/EDR. + */ + BOOLEAN no_smp_on_br; /* if set to TRUE then SMP on BR/EDR doesn't */ + /* work, i.e. link keys crosspairing */ + /* SC BR/EDR->SC LE doesn't happen */ + tBTM_BOND_TYPE bond_type; /* peering bond type */ + +#if BLE_INCLUDED == TRUE + tBTM_SEC_BLE ble; + tBTM_LE_CONN_PRAMS conn_params; +#if (BLE_50_FEATURE_SUPPORT == TRUE) + tBTM_EXT_CONN_PARAMS ext_conn_params; +#endif // #if (BLE_50_FEATURE_SUPPORT == TRUE) +#endif + +// btla-specific ++ +#if BTM_DISC_DURING_RS == TRUE +#define BTM_SEC_RS_NOT_PENDING 0 /* Role Switch not in progress */ +#define BTM_SEC_RS_PENDING 1 /* Role Switch in progress */ +#define BTM_SEC_DISC_PENDING 2 /* Disconnect is pending */ + UINT8 rs_disc_pending; +#endif +// btla-specific -- +#define BTM_SEC_NO_LAST_SERVICE_ID 0 + UINT8 last_author_service_id; /* ID of last serviced authorized: Reset after each l2cap connection */ + BOOLEAN enc_init_by_we; +}; + +#define BTM_SEC_IS_SM4(sm) ((BOOLEAN)(BTM_SM4_TRUE == ((sm)&BTM_SM4_TRUE))) +#define BTM_SEC_IS_SM4_LEGACY(sm) ((BOOLEAN)(BTM_SM4_KNOWN == ((sm)&BTM_SM4_TRUE))) +#define BTM_SEC_IS_SM4_UNKNOWN(sm) ((BOOLEAN)(BTM_SM4_UNKNOWN == ((sm)&BTM_SM4_TRUE))) + +#define BTM_SEC_LE_MASK (BTM_SEC_LE_AUTHENTICATED|BTM_SEC_LE_ENCRYPTED|BTM_SEC_LE_LINK_KEY_KNOWN|BTM_SEC_LE_LINK_KEY_AUTHED) + +/* +** Define device configuration structure +*/ +typedef struct { +#if BTM_MAX_LOC_BD_NAME_LEN > 0 + tBTM_LOC_BD_NAME bd_name; /* local Bluetooth device name */ +#endif + BOOLEAN pin_type; /* TRUE if PIN type is fixed */ + UINT8 pin_code_len; /* Bonding information */ + PIN_CODE pin_code; /* PIN CODE if pin type is fixed */ + BOOLEAN connectable; /* If TRUE page scan should be enabled */ + UINT8 def_inq_scan_mode; /* ??? limited/general/none */ +} tBTM_CFG; + +enum { + BTM_PM_SET_MODE_EVT, /* Set power mode API is called. */ + BTM_PM_UPDATE_EVT, + BTM_PM_RD_MODE_EVT /* Read power mode API is called. */ +}; +typedef UINT8 tBTM_PM_EVENT; + +typedef struct { + UINT16 event; + UINT16 len; + UINT8 link_ind; +} tBTM_PM_MSG_DATA; + +typedef struct { + UINT8 hci_status; + UINT8 mode; + UINT16 interval; +} tBTM_PM_MD_CHG_DATA; + +typedef struct { + UINT8 pm_id; /* the entity that calls SetPowerMode API */ + tBTM_PM_PWR_MD *p_pmd; +} tBTM_PM_SET_MD_DATA; + +typedef struct { + void *p_data; + UINT8 link_ind; +} tBTM_PM_SM_DATA; + +#define BTM_PM_REC_NOT_USED 0 +typedef struct { + tBTM_PM_STATUS_CBACK *cback;/* to notify the registered party of mode change event */ + UINT8 mask; /* registered request mask. 0, if this entry is not used */ +} tBTM_PM_RCB; + +enum { + BTM_BLI_ACL_UP_EVT, + BTM_BLI_ACL_DOWN_EVT, + BTM_BLI_PAGE_EVT, + BTM_BLI_PAGE_DONE_EVT, + BTM_BLI_INQ_EVT, + BTM_BLI_INQ_CANCEL_EVT, + BTM_BLI_INQ_DONE_EVT +}; +typedef UINT8 tBTM_BLI_EVENT; + +/* Pairing State */ +enum { + BTM_PAIR_STATE_IDLE, /* Idle */ + BTM_PAIR_STATE_GET_REM_NAME, /* Getting the remote name (to check for SM4) */ + BTM_PAIR_STATE_WAIT_PIN_REQ, /* Started authentication, waiting for PIN req (PIN is pre-fetched) */ + BTM_PAIR_STATE_WAIT_LOCAL_PIN, /* Waiting for local PIN code */ + BTM_PAIR_STATE_WAIT_NUMERIC_CONFIRM, /* Waiting user 'yes' to numeric confirmation */ + BTM_PAIR_STATE_KEY_ENTRY, /* Key entry state (we are a keyboard) */ + BTM_PAIR_STATE_WAIT_LOCAL_OOB_RSP, /* Waiting for local response to peer OOB data */ + BTM_PAIR_STATE_WAIT_LOCAL_IOCAPS, /* Waiting for local IO capabilities and OOB data */ + BTM_PAIR_STATE_INCOMING_SSP, /* Incoming SSP (got peer IO caps when idle) */ + BTM_PAIR_STATE_WAIT_AUTH_COMPLETE, /* All done, waiting authentication complete */ + BTM_PAIR_STATE_WAIT_DISCONNECT /* Waiting to disconnect the ACL */ +}; +typedef UINT8 tBTM_PAIRING_STATE; + +#define BTM_PAIR_FLAGS_WE_STARTED_DD 0x01 /* We want to do dedicated bonding */ +#define BTM_PAIR_FLAGS_PEER_STARTED_DD 0x02 /* Peer initiated dedicated bonding */ +#define BTM_PAIR_FLAGS_DISC_WHEN_DONE 0x04 /* Disconnect when done */ +#define BTM_PAIR_FLAGS_PIN_REQD 0x08 /* set this bit when pin_callback is called */ +#define BTM_PAIR_FLAGS_PRE_FETCH_PIN 0x10 /* set this bit when pre-fetch pin */ +#define BTM_PAIR_FLAGS_REJECTED_CONNECT 0x20 /* set this bit when rejected incoming connection */ +#define BTM_PAIR_FLAGS_WE_CANCEL_DD 0x40 /* set this bit when cancelling a bonding procedure */ +#define BTM_PAIR_FLAGS_LE_ACTIVE 0x80 /* use this bit when SMP pairing is active */ + + +typedef struct { + BOOLEAN is_mux; + BD_ADDR bd_addr; + UINT16 psm; + BOOLEAN is_orig; + tBTM_SEC_CALLBACK *p_callback; + void *p_ref_data; + UINT32 mx_proto_id; + UINT32 mx_chan_id; + tBT_TRANSPORT transport; +} tBTM_SEC_QUEUE_ENTRY; + +#if (L2CAP_UCD_INCLUDED == TRUE) + +#define CONN_ORIENT_TERM 0x00 /* incoming connection oriented */ +#define CONN_ORIENT_ORIG 0x01 /* outgoing connection oriented */ +#define CONNLESS_TERM 0x02 /* incoming connectionless */ +#define CONNLESS_ORIG 0x03 /* outgoing connectionless */ +#define CONNECTION_TYPE_ORIG_MASK 0x01 /* mask for direction */ +#define CONNECTION_TYPE_CONNLESS_MASK 0x02 /* mask for connectionless or not */ +typedef UINT8 CONNECTION_TYPE; + +#else + +#define CONN_ORIENT_TERM FALSE +#define CONN_ORIENT_ORIG TRUE +typedef BOOLEAN CONNECTION_TYPE; + +#endif /* (L2CAP_UCD_INCLUDED == TRUE) */ + +/* Define a structure to hold all the BTM data +*/ + +#define BTM_STATE_BUFFER_SIZE 5 /* size of state buffer */ + +#define BTM_INVALID_HANDLE 0xFFFF + +typedef struct { + tBTM_CFG cfg; /* Device configuration */ + + /**************************************************** + ** ACL Management + ****************************************************/ + list_t *p_acl_db_list; +#if (CLASSIC_BT_INCLUDED == TRUE) + UINT8 btm_scn[BTM_MAX_SCN]; /* current SCNs: TRUE if SCN is in use */ +#endif ///CLASSIC_BT_INCLUDED == TRUE + UINT16 btm_def_link_policy; + UINT16 btm_def_link_super_tout; + + tBTM_ACL_LINK_STAT_CB *p_acl_link_stat_cb; /* Callback for when ACL link related events came */ + + tBTM_BL_EVENT_MASK bl_evt_mask; + tBTM_BL_CHANGE_CB *p_bl_changed_cb; /* Callback for when Busy Level changed */ + + /**************************************************** + ** Power Management + ****************************************************/ + list_t *p_pm_mode_db_list; + tBTM_PM_RCB pm_reg_db[BTM_MAX_PM_RECORDS + 1]; /* per application/module */ + UINT16 pm_pend_link_hdl; /* the index of acl_db, which has a pending PM cmd */ + UINT8 pm_pend_id; /* the id pf the module, which has a pending PM cmd */ + + /***************************************************** + ** Device control + *****************************************************/ + tBTM_DEVCB devcb; + + /***************************************************** + ** BLE Device controllers + *****************************************************/ +#if (BLE_INCLUDED == TRUE) + tBTM_BLE_CB ble_ctr_cb; + + UINT16 enc_handle; + BT_OCTET8 enc_rand; /* received rand value from LTK request*/ + UINT16 ediv; /* received ediv value from LTK request */ + UINT8 key_size; + tBTM_BLE_VSC_CB cmn_ble_vsc_cb; +#endif + + /* Packet types supported by the local device */ + UINT16 btm_acl_pkt_types_supported; + UINT16 btm_sco_pkt_types_supported; + + + /***************************************************** + ** Inquiry + *****************************************************/ + tBTM_INQUIRY_VAR_ST btm_inq_vars; + + /***************************************************** + ** SCO Management + *****************************************************/ +#if BTM_SCO_INCLUDED == TRUE + tSCO_CB sco_cb; +#endif + + /***************************************************** + ** Security Management + *****************************************************/ + tBTM_APPL_INFO api; + +#define BTM_SEC_MAX_RMT_NAME_CALLBACKS 2 + + tBTM_RMT_NAME_CALLBACK *p_rmt_name_callback[BTM_SEC_MAX_RMT_NAME_CALLBACKS]; +#if (SMP_INCLUDED == TRUE) + tBTM_SEC_DEV_REC *p_collided_dev_rec; +#endif ///SMP_INCLUDED == TRUE + TIMER_LIST_ENT sec_collision_tle; + UINT32 collision_start_time; + UINT32 max_collision_delay; + UINT32 dev_rec_count; /* Counter used for device record timestamp */ + UINT8 security_mode; + BOOLEAN pairing_disabled; + BOOLEAN connect_only_paired; + BOOLEAN security_mode_changed; /* mode changed during bonding */ + BOOLEAN sec_req_pending; /* TRUE if a request is pending */ +#if (CLASSIC_BT_INCLUDED == TRUE) + BOOLEAN pin_type_changed; /* pin type changed during bonding */ +#endif ///CLASSIC_BT_INCLUDED == TRUE +#if (SMP_INCLUDED == TRUE) +#if (CLASSIC_BT_INCLUDED == TRUE) +// btla-specific ++ +#ifdef PORCHE_PAIRING_CONFLICT + UINT8 pin_code_len_saved; /* for legacy devices */ +#endif +// btla-specific -- + + UINT8 pin_code_len; /* for legacy devices */ + PIN_CODE pin_code; /* for legacy devices */ + UINT8 disc_reason; /* for legacy devices */ + UINT16 disc_handle; /* for legacy devices */ +#endif ///CLASSIC_BT_INCLUDED == TRUE + tBTM_PAIRING_STATE pairing_state; /* The current pairing state */ + UINT8 pairing_flags; /* The current pairing flags */ + BD_ADDR pairing_bda; /* The device currently pairing */ + TIMER_LIST_ENT pairing_tle; /* Timer for pairing process */ + +#endif ///SMP_INCLUDED == TRUE +#if SMP_INCLUDED == TRUE || CLASSIC_BT_INCLUDED == TRUE + tBTM_SEC_SERV_REC sec_serv_rec[BTM_SEC_MAX_SERVICE_RECORDS]; +#endif // SMP_INCLUDED == TRUE || BT_CLASSIC_ENABLED == TRUE + list_t *p_sec_dev_rec_list; + tBTM_SEC_SERV_REC *p_out_serv; + tBTM_MKEY_CALLBACK *mkey_cback; + + BD_ADDR connecting_bda; + DEV_CLASS connecting_dc; + + UINT8 acl_disc_reason; + UINT8 trace_level; + UINT8 busy_level; /* the current busy level */ + BOOLEAN is_paging; /* TRUE, if paging is in progess */ + BOOLEAN is_inquiry; /* TRUE, if inquiry is in progess */ + fixed_queue_t *page_queue; + BOOLEAN paging; + BOOLEAN discing; + fixed_queue_t *sec_pending_q; /* pending sequrity requests in tBTM_SEC_QUEUE_ENTRY format */ +#if (!defined(BT_TRACE_VERBOSE) || (BT_TRACE_VERBOSE == FALSE)) + char state_temp_buffer[BTM_STATE_BUFFER_SIZE]; +#endif +} tBTM_CB; + +typedef struct{ + //connection parameters update callback + tBTM_UPDATE_CONN_PARAM_CBACK *update_conn_param_cb; +}tBTM_CallbackFunc; + +extern tBTM_CallbackFunc conn_param_update_cb; +/* security action for L2CAP COC channels */ +#define BTM_SEC_OK 1 +#define BTM_SEC_ENCRYPT 2 /* encrypt the link with current key */ +#define BTM_SEC_ENCRYPT_NO_MITM 3 /* unauthenticated encryption or better */ +#define BTM_SEC_ENCRYPT_MITM 4 /* authenticated encryption */ +#define BTM_SEC_ENC_PENDING 5 /* wait for link encryption pending */ + +typedef UINT8 tBTM_SEC_ACTION; + +#ifdef __cplusplus +extern "C" +{ +#endif + +#if BTM_DYNAMIC_MEMORY == FALSE +extern tBTM_CB btm_cb; +#else +extern tBTM_CB *btm_cb_ptr; +#define btm_cb (*btm_cb_ptr) +#endif + +typedef struct tSecDevContext { +#define SEC_DEV_BTDM_BDA 0x01 +#define SEC_DEV_BDA 0x02 +#define SEC_DEV_HDL 0x03 +#define SEC_DEV_ID_ADDR 0x04 + UINT8 type; + BOOLEAN free_check; + union { + BD_ADDR_PTR p_bd_addr; + UINT16 handle; + }context; +}tSecDevContext; + +/* Internal functions provided by btm_main.c +******************************************** +*/ +void btm_init (void); +void btm_free (void); + +/* Internal functions provided by btm_inq.c +******************************************* +*/ +tBTM_STATUS btm_initiate_rem_name (BD_ADDR remote_bda, + tBTM_INQ_INFO *p_cur, + UINT8 origin, UINT32 timeout, + tBTM_CMPL_CB *p_cb); + +void btm_process_remote_name (BD_ADDR bda, BD_NAME name, UINT16 evt_len, + UINT8 hci_status); +void btm_inq_rmt_name_failed(void); + +/* Inquiry related functions */ +void btm_clr_inq_db (BD_ADDR p_bda); +void btm_inq_db_init (void); +void btm_process_inq_results (UINT8 *p, UINT8 inq_res_mode); +void btm_process_inq_complete (UINT8 status, UINT8 mode); +void btm_process_cancel_complete(UINT8 status, UINT8 mode); +void btm_event_filter_complete (UINT8 *p); +void btm_inq_stop_on_ssp(void); +void btm_inq_clear_ssp(void); +tINQ_DB_ENT *btm_inq_db_find (BD_ADDR p_bda); +BOOLEAN btm_inq_find_bdaddr (BD_ADDR p_bda); + +BOOLEAN btm_lookup_eir(BD_ADDR_PTR p_rem_addr); + +/* Internal functions provided by btm_acl.c +******************************************** +*/ +void btm_acl_free(void); +void btm_acl_init (void); +void btm_acl_created (BD_ADDR bda, DEV_CLASS dc, UINT8 bdn[BTM_MAX_REM_BD_NAME_LEN], + UINT16 hci_handle, UINT8 link_role, tBT_TRANSPORT transport); +void btm_acl_removed (BD_ADDR bda, tBT_TRANSPORT transport); +void btm_acl_device_down (void); +void btm_acl_update_busy_level (tBTM_BLI_EVENT event); +void btm_acl_link_stat_report(tBTM_ACL_LINK_STAT_EVENT_DATA *p_data); + +void btm_cont_rswitch (tACL_CONN *p, + tBTM_SEC_DEV_REC *p_dev_rec, + UINT8 hci_status); + +tACL_CONN *btm_handle_to_acl (UINT16 hci_handle); +void btm_read_link_policy_complete (UINT8 *p); +void btm_read_rssi_complete (UINT8 *p); +void btm_read_tx_power_complete (UINT8 *p, BOOLEAN is_ble); +void btm_acl_pkt_types_changed(UINT8 status, UINT16 handle, UINT16 pkt_types); +void btm_read_link_quality_complete (UINT8 *p); +tBTM_STATUS btm_set_packet_types (tACL_CONN *p, UINT16 pkt_types); +void btm_process_clk_off_comp_evt (UINT16 hci_handle, UINT16 clock_offset); +void btm_acl_role_changed (UINT8 hci_status, BD_ADDR bd_addr, UINT8 new_role); +void btm_acl_encrypt_change (UINT16 handle, UINT8 status, UINT8 encr_enable); +UINT16 btm_get_acl_disc_reason_code (void); +tBTM_STATUS btm_remove_acl (BD_ADDR bd_addr, tBT_TRANSPORT transport); +void btm_read_remote_features_complete (UINT8 *p); +void btm_read_remote_ext_features_complete (UINT8 *p); +void btm_read_remote_ext_features_failed (UINT8 status, UINT16 handle); +void btm_read_remote_version_complete (UINT8 *p); +void btm_establish_continue (tACL_CONN *p_acl_cb); + +// btla-specific ++ +void btm_acl_chk_peer_pkt_type_support (tACL_CONN *p, UINT16 *p_pkt_type); +// btla-specific -- +/* Read maximum data packet that can be sent over current connection */ +UINT16 btm_get_max_packet_size (BD_ADDR addr); +tACL_CONN *btm_bda_to_acl (BD_ADDR bda, tBT_TRANSPORT transport); +BOOLEAN btm_acl_notif_conn_collision (BD_ADDR bda); + +void btm_pm_reset(void); +tBTM_PM_MCB *btm_pm_sm_alloc(void); +void btm_pm_proc_cmd_status(UINT8 status); +void btm_pm_proc_mode_change (UINT8 hci_status, UINT16 hci_handle, UINT8 mode, + UINT16 interval); +void btm_pm_proc_ssr_evt (UINT8 *p, UINT16 evt_len); +#if BTM_SCO_INCLUDED == TRUE +void btm_sco_chk_pend_unpark (UINT8 hci_status, UINT16 hci_handle); +#if (BTM_SCO_HCI_INCLUDED == TRUE ) +void btm_sco_process_num_bufs (UINT16 num_lm_sco_bufs); +void btm_sco_process_num_completed_pkts (UINT8 *p); +#endif /* (BTM_SCO_HCI_INCLUDED == TRUE ) */ +#else +#define btm_sco_chk_pend_unpark(hci_status, hci_handle) +#endif /* BTM_SCO_INCLUDED */ +void btm_qos_setup_complete (UINT8 status, UINT16 handle, FLOW_SPEC *p_flow); +void btm_qos_setup_timeout (void *p_tle); + + +#if (BLE_50_FEATURE_SUPPORT == TRUE) +void btm_create_sync_callback(UINT8 status); +void btm_set_phy_callback(UINT8 status); +void btm_read_phy_callback(uint8_t hci_status, uint16_t conn_handle, uint8_t tx_phy, uint8_t rx_phy); +#endif +#if (BLE_FEAT_PERIODIC_ADV_SYNC_TRANSFER == TRUE) +void btm_ble_periodic_adv_sync_trans_complete(UINT16 op_code, UINT8 hci_status, UINT16 conn_handle); +#endif +/* Internal functions provided by btm_sco.c +******************************************** +*/ +void btm_sco_init (void); +void btm_sco_connected (UINT8 hci_status, BD_ADDR bda, UINT16 hci_handle, + tBTM_ESCO_DATA *p_esco_data); +void btm_esco_proc_conn_chg (UINT8 status, UINT16 handle, UINT8 tx_interval, + UINT8 retrans_window, UINT16 rx_pkt_len, + UINT16 tx_pkt_len); +void btm_sco_conn_req (BD_ADDR bda, DEV_CLASS dev_class, UINT8 link_type); +void btm_sco_removed (UINT16 hci_handle, UINT8 reason); +void btm_sco_acl_removed (BD_ADDR bda); +void btm_route_sco_data (BT_HDR *p_msg); +BOOLEAN btm_is_sco_active (UINT16 handle); +void btm_remove_sco_links (BD_ADDR bda); +BOOLEAN btm_is_sco_active_by_bdaddr (BD_ADDR remote_bda); + +tBTM_SCO_TYPE btm_read_def_esco_mode (tBTM_ESCO_PARAMS *p_parms); +UINT16 btm_find_scb_by_handle (UINT16 handle); +void btm_sco_flush_sco_data(UINT16 sco_inx); + +/* Internal functions provided by btm_devctl.c +********************************************** +*/ +void btm_dev_init (void); +void btm_dev_timeout (TIMER_LIST_ENT *p_tle); +void btm_read_local_name_complete (UINT8 *p, UINT16 evt_len); + +#if (BLE_INCLUDED == TRUE) +void btm_ble_add_2_white_list_complete(UINT8 status); +void btm_ble_remove_from_white_list_complete(UINT8 *p, UINT16 evt_len); +void btm_ble_clear_white_list_complete(UINT8 *p, UINT16 evt_len); +BOOLEAN btm_ble_addr_resolvable(BD_ADDR rpa, tBTM_SEC_DEV_REC *p_dev_rec); +tBTM_STATUS btm_ble_read_resolving_list_entry(tBTM_SEC_DEV_REC *p_dev_rec); +BOOLEAN btm_ble_resolving_list_load_dev(tBTM_SEC_DEV_REC *p_dev_rec); +void btm_ble_resolving_list_remove_dev(tBTM_SEC_DEV_REC *p_dev_rec); +#endif /* BLE_INCLUDED */ + +/* Vendor Specific Command complete evt handler */ +void btm_vsc_complete (UINT8 *p, UINT16 cc_opcode, UINT16 evt_len, + tBTM_CMPL_CB *p_vsc_cplt_cback); +void btm_inq_db_reset (void); +void btm_vendor_specific_evt (UINT8 *p, UINT8 evt_len); +void btm_delete_stored_link_key_complete (UINT8 *p); +void btm_report_device_status (tBTM_DEV_STATUS status); +void btm_set_afh_channels_complete (UINT8 *p); +void btm_ble_set_channels_complete (UINT8 *p); +void btm_set_page_timeout_complete (const UINT8 *p); +void btm_page_to_setup_timeout (void *p_tle); + +/* Internal functions provided by btm_dev.c +********************************************** +*/ +BOOLEAN btm_dev_support_switch (BD_ADDR bd_addr); + +tBTM_SEC_DEV_REC *btm_sec_alloc_dev (BD_ADDR bd_addr); +void btm_sec_free_dev (tBTM_SEC_DEV_REC *p_dev_rec, tBT_TRANSPORT transport); +tBTM_SEC_DEV_REC *btm_find_dev (BD_ADDR bd_addr); +tBTM_SEC_DEV_REC *btm_find_or_alloc_dev (BD_ADDR bd_addr); +tBTM_SEC_DEV_REC *btm_find_dev_by_handle (UINT16 handle); +tBTM_BOND_TYPE btm_get_bond_type_dev(BD_ADDR bd_addr); +BOOLEAN btm_set_bond_type_dev(BD_ADDR bd_addr, + tBTM_BOND_TYPE bond_type); +void btm_sec_dev_init(void); +void btm_sec_dev_free(void); + +/* Internal functions provided by btm_sec.c +********************************************** +*/ +BOOLEAN btm_dev_support_switch (BD_ADDR bd_addr); +tBTM_STATUS btm_sec_l2cap_access_req (BD_ADDR bd_addr, UINT16 psm, + UINT16 handle, CONNECTION_TYPE conn_type, + tBTM_SEC_CALLBACK *p_callback, void *p_ref_data); +tBTM_STATUS btm_sec_mx_access_request (BD_ADDR bd_addr, UINT16 psm, BOOLEAN is_originator, + UINT32 mx_proto_id, UINT32 mx_chan_id, + tBTM_SEC_CALLBACK *p_callback, void *p_ref_data); +void btm_sec_conn_req (UINT8 *bda, UINT8 *dc); +void btm_create_conn_cancel_complete (UINT8 *p); +void btm_read_linq_tx_power_complete (UINT8 *p); + +void btm_sec_init (UINT8 sec_mode); +void btm_sec_dev_reset (void); +void btm_sec_abort_access_req (BD_ADDR bd_addr); +void btm_sec_auth_complete (UINT16 handle, UINT8 status); +void btm_sec_encrypt_change (UINT16 handle, UINT8 status, UINT8 encr_enable); +void btm_sec_connected (UINT8 *bda, UINT16 handle, UINT8 status, UINT8 enc_mode); +tBTM_STATUS btm_sec_disconnect (UINT16 handle, UINT8 reason); +void btm_sec_disconnected (UINT16 handle, UINT8 reason); +void btm_sec_rmt_name_request_complete (UINT8 *bd_addr, UINT8 *bd_name, UINT8 status); +void btm_sec_rmt_host_support_feat_evt (UINT8 *p); +void btm_io_capabilities_req (UINT8 *p); +void btm_io_capabilities_rsp (UINT8 *p); +#if (CLASSIC_BT_INCLUDED == TRUE) +void btm_proc_sp_req_evt (tBTM_SP_EVT event, UINT8 *p); +void btm_keypress_notif_evt (UINT8 *p); +void btm_simple_pair_complete (UINT8 *p); +#endif /* (CLASSIC_BT_INCLUDED == TRUE) */ +void btm_sec_link_key_notification (UINT8 *p_bda, UINT8 *p_link_key, UINT8 key_type); +void btm_sec_link_key_request (UINT8 *p_bda); +void btm_sec_pin_code_request (UINT8 *p_bda); +void btm_sec_update_clock_offset (UINT16 handle, UINT16 clock_offset); +void btm_sec_dev_rec_cback_event (tBTM_SEC_DEV_REC *p_dev_rec, UINT8 res, BOOLEAN is_le_transport); +void btm_sec_set_peer_sec_caps (tACL_CONN *p_acl_cb, tBTM_SEC_DEV_REC *p_dev_rec); + +#if BLE_INCLUDED == TRUE +void btm_sec_clear_ble_keys (tBTM_SEC_DEV_REC *p_dev_rec); +BOOLEAN btm_sec_find_bonded_dev (UINT8 start_idx, UINT16 *p_found_handle, tBTM_SEC_DEV_REC **p_rec); +BOOLEAN btm_sec_is_a_bonded_dev (BD_ADDR bda); +void btm_consolidate_dev(tBTM_SEC_DEV_REC *p_target_rec); +BOOLEAN btm_sec_is_le_capable_dev (BD_ADDR bda); +BOOLEAN btm_ble_init_pseudo_addr (tBTM_SEC_DEV_REC *p_dev_rec, BD_ADDR new_pseudo_addr); +extern BOOLEAN btm_ble_start_sec_check(BD_ADDR bd_addr, UINT16 psm, BOOLEAN is_originator, + tBTM_SEC_CALLBACK *p_callback, void *p_ref_data); +#endif /* BLE_INCLUDED */ + +extern tBTM_SEC_SERV_REC *btm_sec_find_first_serv (CONNECTION_TYPE conn_type, UINT16 psm); + +tINQ_DB_ENT *btm_inq_db_new (BD_ADDR p_bda); + +#if BTM_OOB_INCLUDED == TRUE +void btm_rem_oob_req (UINT8 *p); +void btm_read_local_oob_complete (UINT8 *p); +#else +#define btm_rem_oob_req(p) +#define btm_read_local_oob_complete(p) +#endif + +void btm_acl_resubmit_page (void); +void btm_acl_reset_paging (void); +void btm_acl_paging (BT_HDR *p, BD_ADDR dest); +UINT8 btm_sec_clr_service_by_psm (UINT16 psm); +void btm_sec_clr_temp_auth_service (BD_ADDR bda); + +void btm_ble_lock_init(void); + +void btm_ble_sem_init(void); + +void btm_ble_sem_free(void); + +void btm_ble_lock_free(void); + +void btm_sec_handle_remote_legacy_auth_cmp(UINT16 handle); +void btm_sec_update_legacy_auth_state(tACL_CONN *p_acl_cb, UINT8 legacy_auth_state); +BOOLEAN btm_sec_legacy_authentication_mutual (tBTM_SEC_DEV_REC *p_dev_rec); +BOOLEAN btm_find_sec_dev_in_list (void *p_node_data, void *context); + +BOOLEAN btm_sec_dev_authorization(BD_ADDR bd_addr, BOOLEAN authorized); + +/* +#ifdef __cplusplus +} +#endif +*/ + +#endif diff --git a/lib/bt/host/bluedroid/stack/btu/btu_hcif.c b/lib/bt/host/bluedroid/stack/btu/btu_hcif.c new file mode 100644 index 00000000..1b841154 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/btu/btu_hcif.c @@ -0,0 +1,2385 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains functions that interface with the HCI transport. On + * the receive side, it routes events to the appropriate handler, e.g. + * L2CAP, ScoMgr. On the transmit side, it manages the command + * transmission. + * + ******************************************************************************/ + +//#define LOG_TAG "bt_btu_hcif" + +//#include +#include + +#include "stack/bt_types.h" +#include "stack/hcimsgs.h" +#include "stack/btu.h" +#include "l2c_int.h" +#include "stack/btm_api.h" +#include "btm_int.h" +#include "stack/acl_hci_link_interface.h" +//#include "bt_utils.h" +#include "device/controller.h" +#include "osi/osi.h" +#include "hci/hci_layer.h" +#include "common/bt_trace.h" + +#include "osi/thread.h" +#include "osi/pkt_queue.h" +//#include "osi/mutex.h" +// TODO(zachoverflow): remove this horrible hack +#include "stack/btu.h" + +extern void btm_process_cancel_complete(UINT8 status, UINT8 mode); +extern void btm_ble_test_command_complete(UINT8 *p); + +/********************************************************************************/ +/* L O C A L F U N C T I O N P R O T O T Y P E S */ +/********************************************************************************/ +static void btu_hcif_inquiry_comp_evt (UINT8 *p); +static void btu_hcif_inquiry_result_evt (UINT8 *p); +static void btu_hcif_inquiry_rssi_result_evt (UINT8 *p); +static void btu_hcif_extended_inquiry_result_evt (UINT8 *p); + +static void btu_hcif_connection_comp_evt (UINT8 *p); +static void btu_hcif_connection_request_evt (UINT8 *p); +static void btu_hcif_disconnection_comp_evt (UINT8 *p); +#if (SMP_INCLUDED == TRUE) +static void btu_hcif_authentication_comp_evt (UINT8 *p); +#endif ///SMP_INCLUDED == TRUE +static void btu_hcif_rmt_name_request_comp_evt (UINT8 *p, UINT16 evt_len); +#if (SMP_INCLUDED == TRUE) +static void btu_hcif_encryption_change_evt (UINT8 *p); +#endif ///SMP_INCLUDED == TRUE +static void btu_hcif_read_rmt_features_comp_evt (UINT8 *p); +static void btu_hcif_read_rmt_ext_features_comp_evt (UINT8 *p); +static void btu_hcif_read_rmt_version_comp_evt (UINT8 *p); +static void btu_hcif_qos_setup_comp_evt (UINT8 *p); +static void btu_hcif_command_complete_evt (BT_HDR *response, void *context); +static void btu_hcif_command_status_evt (uint8_t status, BT_HDR *command, void *context); +static void btu_hcif_hardware_error_evt (UINT8 *p); +static void btu_hcif_flush_occured_evt (void); +static void btu_hcif_role_change_evt (UINT8 *p); +static void btu_hcif_num_compl_data_pkts_evt (UINT8 *p); +static void btu_hcif_mode_change_evt (UINT8 *p); +#if (SMP_INCLUDED == TRUE) +static void btu_hcif_pin_code_request_evt (UINT8 *p); +static void btu_hcif_link_key_request_evt (UINT8 *p); +static void btu_hcif_link_key_notification_evt (UINT8 *p); +#endif ///SMP_INCLUDED == TRUE +static void btu_hcif_loopback_command_evt (void); +static void btu_hcif_data_buf_overflow_evt (void); +static void btu_hcif_max_slots_changed_evt (void); +static void btu_hcif_read_clock_off_comp_evt (UINT8 *p); +static void btu_hcif_conn_pkt_type_change_evt (UINT8 *p); +static void btu_hcif_qos_violation_evt (UINT8 *p); +static void btu_hcif_page_scan_mode_change_evt (void); +static void btu_hcif_page_scan_rep_mode_chng_evt (void); +static void btu_hcif_esco_connection_comp_evt(UINT8 *p); +static void btu_hcif_esco_connection_chg_evt(UINT8 *p); + +static void btu_hcif_host_support_evt (UINT8 *p); +/* Simple Pairing Events */ +#if (CLASSIC_BT_INCLUDED == TRUE) +static void btu_hcif_io_cap_request_evt (UINT8 *p); +static void btu_hcif_io_cap_response_evt (UINT8 *p); +static void btu_hcif_user_conf_request_evt (UINT8 *p); +static void btu_hcif_user_passkey_request_evt (UINT8 *p); +static void btu_hcif_simple_pair_complete_evt (UINT8 *p); +static void btu_hcif_user_passkey_notif_evt (UINT8 *p); +static void btu_hcif_keypress_notif_evt (UINT8 *p); +#endif /* (CLASSIC_BT_INCLUDED == TRUE) */ +#if BTM_OOB_INCLUDED == TRUE && SMP_INCLUDED == TRUE +static void btu_hcif_rem_oob_request_evt (UINT8 *p); +#endif +static void btu_hcif_link_supv_to_changed_evt (UINT8 *p); +#if L2CAP_NON_FLUSHABLE_PB_INCLUDED == TRUE +static void btu_hcif_enhanced_flush_complete_evt (void); +#endif + +static void btu_hcif_ssr_evt (UINT8 *p, UINT16 evt_len); + +#if BLE_INCLUDED == TRUE +static void btu_ble_ll_conn_complete_evt (UINT8 *p, UINT16 evt_len); +static void btu_ble_read_remote_feat_evt (UINT8 *p); +static void btu_ble_ll_conn_param_upd_evt (UINT8 *p, UINT16 evt_len); +static void btu_ble_ll_get_conn_param_format_err_from_contoller (UINT8 status, UINT16 handle); +#if (SMP_INCLUDED == TRUE) +static void btu_ble_proc_ltk_req (UINT8 *p); +static void btu_hcif_encryption_key_refresh_cmpl_evt (UINT8 *p); +#endif ///SMP_INCLUDED == TRUE +static void btu_ble_data_length_change_evt (UINT8 *p, UINT16 evt_len); +#if (BLE_LLT_INCLUDED == TRUE) +static void btu_ble_rc_param_req_evt(UINT8 *p); +#endif +//#if (defined BLE_PRIVACY_SPT && BLE_PRIVACY_SPT == TRUE) +static void btu_ble_proc_enhanced_conn_cmpl (UINT8 *p, UINT16 evt_len); +//#endif +#if (BLE_50_FEATURE_SUPPORT == TRUE) +static void btu_ble_phy_update_complete_evt(UINT8 *p); +static void btu_ble_ext_adv_report_evt(UINT8 *p, UINT16 evt_len); +static void btu_ble_periodic_adv_sync_establish_evt(UINT8 *p); +static void btu_ble_periodic_adv_report_evt(UINT8 *p, UINT8 evt_len); +static void btu_ble_periodic_adv_sync_lost_evt(UINT8 *p); +static void btu_ble_scan_timeout_evt(UINT8 *p); +static void btu_ble_adv_set_terminate_evt(UINT8 *p); +static void btu_ble_scan_req_received_evt(UINT8 *p); +#endif // #if (BLE_50_FEATURE_SUPPORT == TRUE) +#if (BLE_FEAT_PERIODIC_ADV_SYNC_TRANSFER == TRUE) +static void btu_ble_periodic_adv_sync_trans_recv(UINT8 *p); +#endif // #if (BLE_FEAT_PERIODIC_ADV_SYNC_TRANSFER == TRUE) + +extern osi_sem_t adv_enable_sem; +extern osi_sem_t adv_data_sem; +extern osi_sem_t adv_param_sem; +extern osi_sem_t scan_enable_sem; +extern osi_sem_t scan_param_sem; +extern uint8_t adv_enable_status; +extern uint8_t adv_data_status; +extern uint8_t adv_param_status; +extern uint8_t scan_enable_status; +extern uint8_t scan_param_status; + +#endif + +/******************************************************************************* +** +** Function btu_hcif_process_event +** +** Description This function is called when an event is received from +** the Host Controller. +** +** Returns void +** +*******************************************************************************/ +void btu_hcif_process_event (UNUSED_ATTR UINT8 controller_id, BT_HDR *p_msg) +{ + UINT8 *p = (UINT8 *)(p_msg + 1) + p_msg->offset; + UINT8 hci_evt_code, hci_evt_len; +#if BLE_INCLUDED == TRUE + UINT8 ble_sub_code; +#endif + STREAM_TO_UINT8 (hci_evt_code, p); + STREAM_TO_UINT8 (hci_evt_len, p); + + switch (hci_evt_code) { + case HCI_INQUIRY_COMP_EVT: + btu_hcif_inquiry_comp_evt (p); + break; + case HCI_INQUIRY_RESULT_EVT: + btu_hcif_inquiry_result_evt (p); + break; + case HCI_INQUIRY_RSSI_RESULT_EVT: + btu_hcif_inquiry_rssi_result_evt (p); + break; + case HCI_EXTENDED_INQUIRY_RESULT_EVT: + btu_hcif_extended_inquiry_result_evt (p); + break; + case HCI_CONNECTION_COMP_EVT: + btu_hcif_connection_comp_evt (p); + break; + case HCI_CONNECTION_REQUEST_EVT: + btu_hcif_connection_request_evt (p); + break; + case HCI_DISCONNECTION_COMP_EVT: + btu_hcif_disconnection_comp_evt (p); + break; + case HCI_AUTHENTICATION_COMP_EVT: +#if (SMP_INCLUDED == TRUE) + btu_hcif_authentication_comp_evt (p); +#endif ///SMP_INCLUDED == TRUE + break; + case HCI_RMT_NAME_REQUEST_COMP_EVT: + btu_hcif_rmt_name_request_comp_evt (p, hci_evt_len); + break; + case HCI_ENCRYPTION_CHANGE_EVT: +#if (SMP_INCLUDED == TRUE) + btu_hcif_encryption_change_evt (p); +#endif ///SMP_INCLUDED == TRUE + break; +#if BLE_INCLUDED == TRUE + case HCI_ENCRYPTION_KEY_REFRESH_COMP_EVT: +#if (SMP_INCLUDED == TRUE) + btu_hcif_encryption_key_refresh_cmpl_evt(p); +#endif ///SMP_INCLUDED == TRUE + break; +#endif + case HCI_READ_RMT_FEATURES_COMP_EVT: + btu_hcif_read_rmt_features_comp_evt (p); + break; + case HCI_READ_RMT_EXT_FEATURES_COMP_EVT: + btu_hcif_read_rmt_ext_features_comp_evt (p); + break; + case HCI_READ_RMT_VERSION_COMP_EVT: + btu_hcif_read_rmt_version_comp_evt (p); + break; + case HCI_QOS_SETUP_COMP_EVT: + btu_hcif_qos_setup_comp_evt (p); + break; + case HCI_COMMAND_COMPLETE_EVT: + //HCI_TRACE_ERROR("%s should not have received a command complete event. " + // "Someone didn't go through the hci transmit_command function.", __func__); + break; + case HCI_COMMAND_STATUS_EVT: + //HCI_TRACE_ERROR("%s should not have received a command status event. " + // "Someone didn't go through the hci transmit_command function.", __func__); + break; + case HCI_HARDWARE_ERROR_EVT: + btu_hcif_hardware_error_evt (p); + break; + case HCI_FLUSH_OCCURED_EVT: + btu_hcif_flush_occured_evt (); + break; + case HCI_ROLE_CHANGE_EVT: + btu_hcif_role_change_evt (p); + break; + case HCI_NUM_COMPL_DATA_PKTS_EVT: + btu_hcif_num_compl_data_pkts_evt (p); + break; + case HCI_MODE_CHANGE_EVT: + btu_hcif_mode_change_evt (p); + break; +#if (SMP_INCLUDED == TRUE) + case HCI_PIN_CODE_REQUEST_EVT: + btu_hcif_pin_code_request_evt (p); + break; + case HCI_LINK_KEY_REQUEST_EVT: + btu_hcif_link_key_request_evt (p); + break; + case HCI_LINK_KEY_NOTIFICATION_EVT: + btu_hcif_link_key_notification_evt (p); + break; +#endif ///SMP_INCLUDED == TRUE + case HCI_LOOPBACK_COMMAND_EVT: + btu_hcif_loopback_command_evt (); + break; + case HCI_DATA_BUF_OVERFLOW_EVT: + btu_hcif_data_buf_overflow_evt (); + break; + case HCI_MAX_SLOTS_CHANGED_EVT: + btu_hcif_max_slots_changed_evt (); + break; + case HCI_READ_CLOCK_OFF_COMP_EVT: + btu_hcif_read_clock_off_comp_evt (p); + break; + case HCI_CONN_PKT_TYPE_CHANGE_EVT: + btu_hcif_conn_pkt_type_change_evt (p); + break; + case HCI_QOS_VIOLATION_EVT: + btu_hcif_qos_violation_evt (p); + break; + case HCI_PAGE_SCAN_MODE_CHANGE_EVT: + btu_hcif_page_scan_mode_change_evt (); + break; + case HCI_PAGE_SCAN_REP_MODE_CHNG_EVT: + btu_hcif_page_scan_rep_mode_chng_evt (); + break; + case HCI_ESCO_CONNECTION_COMP_EVT: + btu_hcif_esco_connection_comp_evt (p); + break; + case HCI_ESCO_CONNECTION_CHANGED_EVT: + btu_hcif_esco_connection_chg_evt (p); + break; + case HCI_SNIFF_SUB_RATE_EVT: + btu_hcif_ssr_evt (p, hci_evt_len); + break; + case HCI_RMT_HOST_SUP_FEAT_NOTIFY_EVT: + btu_hcif_host_support_evt (p); + break; +#if (CLASSIC_BT_INCLUDED == TRUE) + case HCI_IO_CAPABILITY_REQUEST_EVT: + btu_hcif_io_cap_request_evt (p); + break; + case HCI_IO_CAPABILITY_RESPONSE_EVT: + btu_hcif_io_cap_response_evt (p); + break; + case HCI_USER_CONFIRMATION_REQUEST_EVT: + btu_hcif_user_conf_request_evt (p); + break; + case HCI_USER_PASSKEY_REQUEST_EVT: + btu_hcif_user_passkey_request_evt (p); + break; +#endif /* (CLASSIC_BT_INCLUDED == TRUE) */ +#if BTM_OOB_INCLUDED == TRUE && SMP_INCLUDED == TRUE + case HCI_REMOTE_OOB_DATA_REQUEST_EVT: + btu_hcif_rem_oob_request_evt (p); + break; +#endif +#if (CLASSIC_BT_INCLUDED == TRUE) + case HCI_SIMPLE_PAIRING_COMPLETE_EVT: + btu_hcif_simple_pair_complete_evt (p); + break; + case HCI_USER_PASSKEY_NOTIFY_EVT: + btu_hcif_user_passkey_notif_evt (p); + break; + case HCI_KEYPRESS_NOTIFY_EVT: + btu_hcif_keypress_notif_evt (p); + break; +#endif /* (CLASSIC_BT_INCLUDED == TRUE) */ + case HCI_LINK_SUPER_TOUT_CHANGED_EVT: + btu_hcif_link_supv_to_changed_evt (p); + break; +#if L2CAP_NON_FLUSHABLE_PB_INCLUDED == TRUE + case HCI_ENHANCED_FLUSH_COMPLETE_EVT: + btu_hcif_enhanced_flush_complete_evt (); + break; +#endif + +#if (BLE_INCLUDED == TRUE) + case HCI_BLE_EVENT: + STREAM_TO_UINT8 (ble_sub_code, p); + hci_evt_len--; + HCI_TRACE_DEBUG("BLE HCI(id=%d) event = 0x%02x)", hci_evt_code, ble_sub_code); + + switch (ble_sub_code) { + case HCI_BLE_ADV_PKT_RPT_EVT: /* result of inquiry */ + case HCI_BLE_ADV_DISCARD_REPORT_EVT: + case HCI_BLE_DIRECT_ADV_EVT: + // These three events are directed to another specialized processing path + HCI_TRACE_ERROR("Unexpected HCI BLE event = 0x%02x", ble_sub_code); + break; + case HCI_BLE_CONN_COMPLETE_EVT: + btu_ble_ll_conn_complete_evt(p, hci_evt_len); + break; + case HCI_BLE_LL_CONN_PARAM_UPD_EVT: + btu_ble_ll_conn_param_upd_evt(p, hci_evt_len); + break; + case HCI_BLE_READ_REMOTE_FEAT_CMPL_EVT: + btu_ble_read_remote_feat_evt(p); + break; + case HCI_BLE_LTK_REQ_EVT: /* received only at slave device */ +#if (SMP_INCLUDED == TRUE) + btu_ble_proc_ltk_req(p); +#endif ///SMP_INCLUDED == TRUE + break; +//#if (defined BLE_PRIVACY_SPT && BLE_PRIVACY_SPT == TRUE) + case HCI_BLE_ENHANCED_CONN_COMPLETE_EVT: + btu_ble_proc_enhanced_conn_cmpl(p, hci_evt_len); + break; +//#endif +#if (BLE_LLT_INCLUDED == TRUE) + case HCI_BLE_RC_PARAM_REQ_EVT: + btu_ble_rc_param_req_evt(p); + break; +#endif + case HCI_BLE_DATA_LENGTH_CHANGE_EVT: + btu_ble_data_length_change_evt(p, hci_evt_len); + break; +#if (BLE_50_FEATURE_SUPPORT == TRUE) + case HCI_BLE_PHY_UPDATE_COMPLETE_EVT: + btu_ble_phy_update_complete_evt(p); + break; + case HCI_BLE_EXT_ADV_REPORT_EVT: + //HCI_TRACE_ERROR("%s, HCI_BLE_EXT_ADV_REPORT_EVT.", __func__); + btu_ble_ext_adv_report_evt(p, hci_evt_len); + break; + case HCI_BLE_PERIOD_ADV_SYNC_ESTAB_EVT: + btu_ble_periodic_adv_sync_establish_evt(p); + break; + case HCI_BLE_PERIOD_ADV_REPORT_EVT: + btu_ble_periodic_adv_report_evt(p, hci_evt_len); + break; + case HCI_BLE_PERIOD_ADV_SYNC_LOST_EVT: + btu_ble_periodic_adv_sync_lost_evt(p); + break; + case HCI_BLE_SCAN_TIMEOUT_EVT: + btu_ble_scan_timeout_evt(p); + break; + case HCI_BLE_ADV_SET_TERMINATED_EVT: + btu_ble_adv_set_terminate_evt(p); + break; + case HCI_BLE_SCAN_REQ_RECEIVED_EVT: + btu_ble_scan_req_received_evt(p); + break; + case HCI_BLE_CHANNEL_SELECT_ALG: + break; +#endif // #if (BLE_50_FEATURE_SUPPORT == TRUE) +#if (BLE_FEAT_PERIODIC_ADV_SYNC_TRANSFER == TRUE) + case HCI_BLE_PERIOD_ADV_SYNC_TRANS_RECV_EVT: + btu_ble_periodic_adv_sync_trans_recv(p); + break; +#endif // #if (BLE_FEAT_PERIODIC_ADV_SYNC_TRANSFER == TRUE) + } + break; +#endif /* BLE_INCLUDED */ + case HCI_VENDOR_SPECIFIC_EVT: + btm_vendor_specific_evt (p, hci_evt_len); + break; + } +} + + +/******************************************************************************* +** +** Function btu_hcif_send_cmd +** +** Description This function is called to send commands to the Host Controller. +** +** Returns void +** +*******************************************************************************/ +void btu_hcif_send_cmd (UNUSED_ATTR UINT8 controller_id, BT_HDR *p_buf) +{ + if (!p_buf) { + HCI_TRACE_ERROR("%s p_buf is NULL", __func__); + return; + } + + uint16_t opcode; + uint8_t *stream = p_buf->data + p_buf->offset; + void *vsc_callback = NULL; + + STREAM_TO_UINT16(opcode, stream); + + assert (p_buf->layer_specific == HCI_CMD_BUF_TYPE_METADATA); + hci_cmd_metadata_t *metadata = HCI_GET_CMD_METAMSG(p_buf); + metadata->command_complete_cb = btu_hcif_command_complete_evt; + metadata->command_status_cb = btu_hcif_command_status_evt; + metadata->opcode = opcode; + + vsc_callback = metadata->context; + /* If command is not a VSC, then the context field should be empty */ + if ((opcode & HCI_GRP_VENDOR_SPECIFIC) != HCI_GRP_VENDOR_SPECIFIC +#if BLE_INCLUDED == TRUE + && (opcode != HCI_BLE_RAND) + && (opcode != HCI_BLE_ENCRYPT) +#endif + ) { + assert (vsc_callback == NULL); + } + + hci_layer_get_interface()->transmit_command( + p_buf, + btu_hcif_command_complete_evt, + btu_hcif_command_status_evt, + vsc_callback); + +#if (defined(HCILP_INCLUDED) && HCILP_INCLUDED == TRUE) + btu_check_bt_sleep (); +#endif +} + +#if (BLE_50_FEATURE_SUPPORT == TRUE) +UINT8 btu_hcif_send_cmd_sync (UINT8 controller_id, BT_HDR *p_buf) +{ + if (!p_buf) { + HCI_TRACE_ERROR("%s p_buf is NULL", __func__); + return HCI_ERR_ILLEGAL_PARAMETER_FMT; + } + BlE_SYNC *sync_info = btsnd_hcic_ble_get_sync_info(); + if((sync_info == NULL) || (sync_info->sync_sem == NULL)) { + HCI_TRACE_ERROR("%s sync_info error", __func__); + return HCI_ERR_ILLEGAL_PARAMETER_FMT; + } + uint16_t opcode; + uint8_t *stream = p_buf->data + p_buf->offset; + void *vsc_callback = NULL; + + STREAM_TO_UINT16(opcode, stream); + + sync_info->opcode = opcode; + + assert (p_buf->layer_specific == HCI_CMD_BUF_TYPE_METADATA); + hci_cmd_metadata_t *metadata = HCI_GET_CMD_METAMSG(p_buf); + metadata->command_complete_cb = btu_hcif_command_complete_evt; + metadata->command_status_cb = btu_hcif_command_status_evt; + metadata->command_free_cb = NULL; + metadata->opcode = opcode; + + vsc_callback = metadata->context; + /* If command is not a VSC, then the context field should be empty */ + if ((opcode & HCI_GRP_VENDOR_SPECIFIC) != HCI_GRP_VENDOR_SPECIFIC +#if BLE_INCLUDED == TRUE + && (opcode != HCI_BLE_RAND) + && (opcode != HCI_BLE_ENCRYPT) +#endif + ) { + assert (vsc_callback == NULL); + } + + hci_layer_get_interface()->transmit_command( + p_buf, + btu_hcif_command_complete_evt, + btu_hcif_command_status_evt, + vsc_callback); + + osi_sem_take(&sync_info->sync_sem, OSI_SEM_MAX_TIMEOUT); + +#if (defined(HCILP_INCLUDED) && HCILP_INCLUDED == TRUE) + btu_check_bt_sleep (); +#endif + return btsnd_hcic_ble_get_status(); +} +#endif // #if (BLE_50_FEATURE_SUPPORT == TRUE) + + +/******************************************************************************* +** +** Function btu_hcif_send_host_rdy_for_data +** +** Description This function is called to check if it can send commands +** to the Host Controller. It may be passed the address of +** a packet to send. +** +** Returns void +** +*******************************************************************************/ +void btu_hcif_send_host_rdy_for_data(void) +{ + UINT16 num_pkts[MAX_L2CAP_LINKS + 4]; /* 3 SCO connections */ + UINT16 handles[MAX_L2CAP_LINKS + 4]; + UINT8 num_ents; + + /* Get the L2CAP numbers */ + num_ents = l2c_link_pkts_rcvd (num_pkts, handles); + + /* Get the SCO numbers */ + /* No SCO for now ?? */ + + if (num_ents) { + btsnd_hcic_host_num_xmitted_pkts (num_ents, handles, num_pkts); + } +} + +/******************************************************************************* +** +** Function btu_hcif_inquiry_comp_evt +** +** Description Process event HCI_INQUIRY_COMP_EVT +** +** Returns void +** +*******************************************************************************/ +static void btu_hcif_inquiry_comp_evt (UINT8 *p) +{ + UINT8 status; + + STREAM_TO_UINT8 (status, p); + + /* Tell inquiry processing that we are done */ + btm_process_inq_complete(status, BTM_BR_INQUIRY_MASK); +} + + +/******************************************************************************* +** +** Function btu_hcif_inquiry_result_evt +** +** Description Process event HCI_INQUIRY_RESULT_EVT +** +** Returns void +** +*******************************************************************************/ +static void btu_hcif_inquiry_result_evt (UINT8 *p) +{ + /* Store results in the cache */ + btm_process_inq_results (p, BTM_INQ_RESULT_STANDARD); +} + +/******************************************************************************* +** +** Function btu_hcif_inquiry_rssi_result_evt +** +** Description Process event HCI_INQUIRY_RSSI_RESULT_EVT +** +** Returns void +** +*******************************************************************************/ +static void btu_hcif_inquiry_rssi_result_evt (UINT8 *p) +{ + /* Store results in the cache */ + btm_process_inq_results (p, BTM_INQ_RESULT_WITH_RSSI); +} + +/******************************************************************************* +** +** Function btu_hcif_extended_inquiry_result_evt +** +** Description Process event HCI_EXTENDED_INQUIRY_RESULT_EVT +** +** Returns void +** +*******************************************************************************/ +static void btu_hcif_extended_inquiry_result_evt (UINT8 *p) +{ + /* Store results in the cache */ + btm_process_inq_results (p, BTM_INQ_RESULT_EXTENDED); +} + +/******************************************************************************* +** +** Function btu_hcif_connection_comp_evt +** +** Description Process event HCI_CONNECTION_COMP_EVT +** +** Returns void +** +*******************************************************************************/ +static void btu_hcif_connection_comp_evt (UINT8 *p) +{ + UINT8 status; + UINT16 handle; + BD_ADDR bda; + UINT8 link_type; + UINT8 enc_mode; + + STREAM_TO_UINT8 (status, p); + STREAM_TO_UINT16 (handle, p); + STREAM_TO_BDADDR (bda, p); + STREAM_TO_UINT8 (link_type, p); + STREAM_TO_UINT8 (enc_mode, p); + + handle = HCID_GET_HANDLE (handle); + + btm_acl_connected(bda, handle, link_type, enc_mode, status); + + HCI_TRACE_WARNING("hcif conn complete: hdl 0x%x, st 0x%x", handle, status); +} + + +/******************************************************************************* +** +** Function btu_hcif_connection_request_evt +** +** Description Process event HCI_CONNECTION_REQUEST_EVT +** +** Returns void +** +*******************************************************************************/ +static void btu_hcif_connection_request_evt (UINT8 *p) +{ + BD_ADDR bda; + DEV_CLASS dc; + UINT8 link_type; + + STREAM_TO_BDADDR (bda, p); + STREAM_TO_DEVCLASS (dc, p); + STREAM_TO_UINT8 (link_type, p); + /* Pass request to security manager to check connect filters before */ + /* passing request to l2cap */ + if (link_type == HCI_LINK_TYPE_ACL) { +#if (SMP_INCLUDED == TRUE) + btm_sec_conn_req (bda, dc); +#endif ///SMP_INCLUDED == TRUE + } +#if BTM_SCO_INCLUDED == TRUE + else { + btm_sco_conn_req (bda, dc, link_type); + } +#endif /* BTM_SCO_INCLUDED */ +} + + +/******************************************************************************* +** +** Function btu_hcif_disconnection_comp_evt +** +** Description Process event HCI_DISCONNECTION_COMP_EVT +** +** Returns void +** +*******************************************************************************/ +static void btu_hcif_disconnection_comp_evt (UINT8 *p) +{ + UINT16 handle; + UINT8 reason; + + ++p; + STREAM_TO_UINT16 (handle, p); + STREAM_TO_UINT8 (reason, p); + + handle = HCID_GET_HANDLE (handle); + + btm_acl_disconnected(handle, reason); + + HCI_TRACE_WARNING("hcif disc complete: hdl 0x%x, rsn 0x%x", handle, reason); +} + +/******************************************************************************* +** +** Function btu_hcif_authentication_comp_evt +** +** Description Process event HCI_AUTHENTICATION_COMP_EVT +** +** Returns void +** +*******************************************************************************/ +#if (SMP_INCLUDED == TRUE) +static void btu_hcif_authentication_comp_evt (UINT8 *p) +{ + UINT8 status; + UINT16 handle; + + STREAM_TO_UINT8 (status, p); + STREAM_TO_UINT16 (handle, p); + + btm_sec_auth_complete (handle, status); +} +#endif ///SMP_INCLUDED == TRUE + +/******************************************************************************* +** +** Function btu_hcif_rmt_name_request_comp_evt +** +** Description Process event HCI_RMT_NAME_REQUEST_COMP_EVT +** +** Returns void +** +*******************************************************************************/ +static void btu_hcif_rmt_name_request_comp_evt (UINT8 *p, UINT16 evt_len) +{ + UINT8 status; + BD_ADDR bd_addr; + + STREAM_TO_UINT8 (status, p); + STREAM_TO_BDADDR (bd_addr, p); + + evt_len -= (1 + BD_ADDR_LEN); + + btm_process_remote_name (bd_addr, p, evt_len, status); +#if (SMP_INCLUDED == TRUE) + btm_sec_rmt_name_request_complete (bd_addr, p, status); +#endif ///SMP_INCLUDED == TRUE +} + + +/******************************************************************************* +** +** Function btu_hcif_encryption_change_evt +** +** Description Process event HCI_ENCRYPTION_CHANGE_EVT +** +** Returns void +** +*******************************************************************************/ +#if (SMP_INCLUDED == TRUE) +static void btu_hcif_encryption_change_evt (UINT8 *p) +{ + UINT8 status; + UINT16 handle; + UINT8 encr_enable; + + STREAM_TO_UINT8 (status, p); + STREAM_TO_UINT16 (handle, p); + STREAM_TO_UINT8 (encr_enable, p); + + btm_acl_encrypt_change (handle, status, encr_enable); + btm_sec_encrypt_change (handle, status, encr_enable); +} +#endif ///SMP_INCLUDED == TRUE + +/******************************************************************************* +** +** Function btu_hcif_read_rmt_features_comp_evt +** +** Description Process event HCI_READ_RMT_FEATURES_COMP_EVT +** +** Returns void +** +*******************************************************************************/ +static void btu_hcif_read_rmt_features_comp_evt (UINT8 *p) +{ + btm_read_remote_features_complete(p); +} + +/******************************************************************************* +** +** Function btu_hcif_read_rmt_ext_features_comp_evt +** +** Description Process event HCI_READ_RMT_EXT_FEATURES_COMP_EVT +** +** Returns void +** +*******************************************************************************/ +static void btu_hcif_read_rmt_ext_features_comp_evt (UINT8 *p) +{ + UINT8 *p_cur = p; + UINT8 status; + UINT16 handle; + + STREAM_TO_UINT8 (status, p_cur); + + if (status == HCI_SUCCESS) { + btm_read_remote_ext_features_complete(p); + } else { + STREAM_TO_UINT16 (handle, p_cur); + btm_read_remote_ext_features_failed(status, handle); + } +} + +/******************************************************************************* +** +** Function btu_hcif_read_rmt_version_comp_evt +** +** Description Process event HCI_READ_RMT_VERSION_COMP_EVT +** +** Returns void +** +*******************************************************************************/ +static void btu_hcif_read_rmt_version_comp_evt (UINT8 *p) +{ + btm_read_remote_version_complete (p); +} + + +/******************************************************************************* +** +** Function btu_hcif_qos_setup_comp_evt +** +** Description Process event HCI_QOS_SETUP_COMP_EVT +** +** Returns void +** +*******************************************************************************/ +static void btu_hcif_qos_setup_comp_evt (UINT8 *p) +{ + UINT8 status; + UINT16 handle; + FLOW_SPEC flow; + + STREAM_TO_UINT8 (status, p); + STREAM_TO_UINT16 (handle, p); + STREAM_TO_UINT8 (flow.qos_flags, p); + STREAM_TO_UINT8 (flow.service_type, p); + STREAM_TO_UINT32 (flow.token_rate, p); + STREAM_TO_UINT32 (flow.peak_bandwidth, p); + STREAM_TO_UINT32 (flow.latency, p); + STREAM_TO_UINT32 (flow.delay_variation, p); + + btm_qos_setup_complete(status, handle, &flow); +} + + +/******************************************************************************* +** +** Function btu_hcif_esco_connection_comp_evt +** +** Description Process event HCI_ESCO_CONNECTION_COMP_EVT +** +** Returns void +** +*******************************************************************************/ +static void btu_hcif_esco_connection_comp_evt (UINT8 *p) +{ +#if BTM_SCO_INCLUDED == TRUE + tBTM_ESCO_DATA data; + UINT16 handle; + BD_ADDR bda; + UINT8 status; + + STREAM_TO_UINT8 (status, p); + STREAM_TO_UINT16 (handle, p); + STREAM_TO_BDADDR (bda, p); + + STREAM_TO_UINT8 (data.link_type, p); + STREAM_TO_UINT8 (data.tx_interval, p); + STREAM_TO_UINT8 (data.retrans_window, p); + STREAM_TO_UINT16 (data.rx_pkt_len, p); + STREAM_TO_UINT16 (data.tx_pkt_len, p); + STREAM_TO_UINT8 (data.air_mode, p); + + memcpy (data.bd_addr, bda, BD_ADDR_LEN); + btm_sco_connected (status, bda, handle, &data); +#endif +} + + +/******************************************************************************* +** +** Function btu_hcif_esco_connection_chg_evt +** +** Description Process event HCI_ESCO_CONNECTION_CHANGED_EVT +** +** Returns void +** +*******************************************************************************/ +static void btu_hcif_esco_connection_chg_evt (UINT8 *p) +{ +#if BTM_SCO_INCLUDED == TRUE + UINT16 handle; + UINT16 tx_pkt_len; + UINT16 rx_pkt_len; + UINT8 status; + UINT8 tx_interval; + UINT8 retrans_window; + + STREAM_TO_UINT8 (status, p); + STREAM_TO_UINT16 (handle, p); + + STREAM_TO_UINT8 (tx_interval, p); + STREAM_TO_UINT8 (retrans_window, p); + STREAM_TO_UINT16 (rx_pkt_len, p); + STREAM_TO_UINT16 (tx_pkt_len, p); + + btm_esco_proc_conn_chg (status, handle, tx_interval, retrans_window, + rx_pkt_len, tx_pkt_len); +#endif +} + +/******************************************************************************* +** +** Function btu_hcif_hdl_command_complete +** +** Description Handle command complete event +** +** Returns void +** +*******************************************************************************/ +static void btu_hcif_hdl_command_complete (UINT16 opcode, UINT8 *p, UINT16 evt_len, + void *p_cplt_cback) +{ + uint8_t status; + switch (opcode) { + case HCI_INQUIRY_CANCEL: + /* Tell inquiry processing that we are done */ + btm_process_cancel_complete(HCI_SUCCESS, BTM_BR_INQUIRY_MASK); + break; + case HCI_SET_EVENT_FILTER: + btm_event_filter_complete (p); + break; + + case HCI_DELETE_STORED_LINK_KEY: + btm_delete_stored_link_key_complete (p); + break; + + case HCI_READ_LOCAL_NAME: + btm_read_local_name_complete (p, evt_len); + break; + + case HCI_GET_LINK_QUALITY: + btm_read_link_quality_complete (p); + break; + + case HCI_READ_RSSI: + btm_read_rssi_complete (p); + break; + + case HCI_READ_TRANSMIT_POWER_LEVEL: + btm_read_tx_power_complete(p, FALSE); + break; + + case HCI_CREATE_CONNECTION_CANCEL: + btm_create_conn_cancel_complete(p); + break; + + case HCI_READ_LOCAL_OOB_DATA: +#if BTM_OOB_INCLUDED == TRUE && SMP_INCLUDED == TRUE + btm_read_local_oob_complete(p); +#endif + break; + + + case HCI_READ_INQ_TX_POWER_LEVEL: + btm_read_linq_tx_power_complete (p); + break; +#if (CLASSIC_BT_INCLUDED == TRUE) + case HCI_SET_AFH_CHANNELS: + btm_set_afh_channels_complete(p); + break; + case HCI_WRITE_PAGE_TOUT: + btm_set_page_timeout_complete(p); + break; +#endif + +#if (BLE_INCLUDED == TRUE) + /* BLE Commands Complete*/ + case HCI_BLE_SET_HOST_CHNL_CLASS: + btm_ble_set_channels_complete(p); + break; + case HCI_BLE_ADD_WHITE_LIST: + btm_ble_add_2_white_list_complete(*p); + break; + + case HCI_BLE_CLEAR_WHITE_LIST: + btm_ble_clear_white_list_complete(p, evt_len); + break; + case HCI_BLE_WRITE_ADV_PARAMS: { + STREAM_TO_UINT8 (status, p); + if(status != HCI_SUCCESS) { + HCI_TRACE_ERROR("hci write adv params error 0x%x", status); + } + break; + } + case HCI_BLE_RC_PARAM_REQ_REPLY: { + STREAM_TO_UINT8 (status, p); + if(status != HCI_SUCCESS) { + HCI_TRACE_ERROR("hci connection params reply command error 0x%x", status); + } + break; + } + case HCI_BLE_RC_PARAM_REQ_NEG_REPLY: { + STREAM_TO_UINT8 (status, p); + if(status != HCI_SUCCESS) { + HCI_TRACE_ERROR("hci connection params neg reply command error %x", status); + } + break; + } + case HCI_BLE_REMOVE_WHITE_LIST: + btm_ble_remove_from_white_list_complete(p, evt_len); + break; + + case HCI_BLE_RAND: + case HCI_BLE_ENCRYPT: +#if (SMP_INCLUDED == TRUE) + btm_ble_rand_enc_complete (p, opcode, (tBTM_RAND_ENC_CB *)p_cplt_cback); +#endif ///SMP_INCLUDED == TRUE + break; + + case HCI_BLE_READ_ADV_CHNL_TX_POWER: + btm_read_tx_power_complete(p, TRUE); + break; + + case HCI_BLE_WRITE_ADV_ENABLE: + btm_ble_write_adv_enable_complete(p); + break; + + case HCI_BLE_CREATE_LL_CONN: + btm_ble_create_ll_conn_complete(*p); + break; + + case HCI_BLE_TRANSMITTER_TEST: + case HCI_BLE_RECEIVER_TEST: + case HCI_BLE_TEST_END: + btm_ble_test_command_complete(p); + break; + case HCI_BLE_CREATE_CONN_CANCEL: + btm_ble_create_conn_cancel_complete(p); + break; + +#if (defined BLE_PRIVACY_SPT && BLE_PRIVACY_SPT == TRUE) + case HCI_BLE_ADD_DEV_RESOLVING_LIST: + btm_ble_add_resolving_list_entry_complete(p, evt_len); + break; + + case HCI_BLE_RM_DEV_RESOLVING_LIST: + btm_ble_remove_resolving_list_entry_complete(p, evt_len); + break; + + case HCI_BLE_CLEAR_RESOLVING_LIST: + btm_ble_clear_resolving_list_complete(p, evt_len); + break; + + case HCI_BLE_READ_RESOLVABLE_ADDR_PEER: + btm_ble_read_resolving_list_entry_complete(p, evt_len); + break; + + case HCI_BLE_READ_RESOLVABLE_ADDR_LOCAL: + break; + case HCI_BLE_SET_ADDR_RESOLUTION_ENABLE: + btm_ble_set_addr_resolution_enable_complete(p, evt_len); + break; + case HCI_BLE_SET_RAND_PRIV_ADDR_TIMOUT: + break; +#if (BLE_50_FEATURE_SUPPORT == TRUE) + case HCI_BLE_SET_EXT_ADV_PARAM: + case HCI_BLE_SET_EXT_ADV_DATA: + case HCI_BLE_SET_EXT_SCAN_RSP_DATA: + case HCI_BLE_SET_EXT_ADV_ENABLE: { + STREAM_TO_UINT8 (status, p); + HCI_TRACE_EVENT("%s opcode 0x%x status 0x%x", __func__, opcode, status); + break; + } + case HCI_BLE_READ_PHY: { + uint16_t conn_handle; + uint8_t tx_phy; + uint8_t rx_phy; + STREAM_TO_UINT8(status, p); + STREAM_TO_UINT16(conn_handle, p); + STREAM_TO_UINT8(tx_phy, p); + STREAM_TO_UINT8(rx_phy, p); + btm_read_phy_callback(status, conn_handle, tx_phy, rx_phy); + break; + } + case HCI_BLE_ENH_RX_TEST: + case HCI_BLE_ENH_TX_TEST: + btm_ble_test_command_complete(p); + break; +#endif // #if (BLE_50_FEATURE_SUPPORT == TRUE) +#if (BLE_FEAT_PERIODIC_ADV_SYNC_TRANSFER == TRUE) + case HCI_BLE_SET_PERIOD_ADV_RECV_ENABLE: + case HCI_BLE_SET_DEFAULT_PAST_PARAMS: + break; + case HCI_BLE_PERIOD_ADV_SYNC_TRANS: + case HCI_BLE_PERIOD_ADV_SET_INFO_TRANS: + case HCI_BLE_SET_PAST_PARAMS: { + UINT16 conn_handle; + STREAM_TO_UINT8(status, p); + STREAM_TO_UINT16(conn_handle, p); + btm_ble_periodic_adv_sync_trans_complete(opcode, status, conn_handle); + break; + } +#endif // #if (BLE_FEAT_PERIODIC_ADV_SYNC_TRANSFER == TRUE) +#endif +#endif /* (BLE_INCLUDED == TRUE) */ + + default: { + if ((opcode & HCI_GRP_VENDOR_SPECIFIC) == HCI_GRP_VENDOR_SPECIFIC) { + btm_vsc_complete (p, opcode, evt_len, (tBTM_CMPL_CB *)p_cplt_cback); + } + STREAM_TO_UINT8 (status, p); + if(status != HCI_SUCCESS) { + HCI_TRACE_ERROR("CC evt: op=0x%x, status=0x%x", opcode, status); + } + break; + } + } +} + +/******************************************************************************* +** +** Function btu_hcif_command_complete_evt +** +** Description Process event HCI_COMMAND_COMPLETE_EVT +** +** Returns void +** +*******************************************************************************/ +static void btu_hcif_command_complete_evt_on_task(BT_HDR *event) +{ + command_complete_hack_t *hack = (command_complete_hack_t *)&event->data[0]; + + command_opcode_t opcode; + uint8_t *stream = hack->response->data + hack->response->offset + 3; // 2 to skip the event headers, 1 to skip the command credits + STREAM_TO_UINT16(opcode, stream); + + btu_hcif_hdl_command_complete( + opcode, + stream, + hack->response->len - 5, // 3 for the command complete headers, 2 for the event headers + hack->context); + + osi_free(hack->response); + osi_free(event); +} + +static void btu_hcif_command_complete_evt(BT_HDR *response, void *context) +{ +#if (BLE_INCLUDED == TRUE) + command_opcode_t opcode; + uint8_t *stream = response->data + response->offset + 3; + STREAM_TO_UINT16(opcode, stream); + switch (opcode) { + case HCI_BLE_WRITE_ADV_DATA: + adv_data_status = *stream; + osi_sem_give(&adv_data_sem); + break; + case HCI_BLE_WRITE_SCAN_RSP_DATA: + adv_data_status = *stream; + osi_sem_give(&adv_data_sem); + break; + case HCI_BLE_WRITE_ADV_ENABLE: { + adv_enable_status = *stream; + osi_sem_give(&adv_enable_sem); + break; + } + case HCI_BLE_WRITE_ADV_PARAMS: + adv_param_status = *stream; + osi_sem_give(&adv_param_sem); + break; + case HCI_BLE_WRITE_SCAN_PARAMS: + scan_param_status = *stream; + osi_sem_give(&scan_param_sem); + break; + case HCI_BLE_WRITE_SCAN_ENABLE: + scan_enable_status = *stream; + osi_sem_give(&scan_enable_sem); + break; + default: + break; + } +#endif + BT_HDR *event = osi_calloc(sizeof(BT_HDR) + sizeof(command_complete_hack_t)); + command_complete_hack_t *hack = (command_complete_hack_t *)&event->data[0]; +#if (BLE_50_FEATURE_SUPPORT == TRUE) + UINT8 status = 0; + stream = response->data + response->offset + 3 + 2; // 2 to skip the event headers, 1 to skip the command credits, 2 to opcode. + STREAM_TO_UINT8(status, stream); + btsnd_hci_ble_set_status(status); +#endif // #if (BLE_50_FEATURE_SUPPORT == TRUE) + HCI_TRACE_DEBUG("btu_hcif_command_complete_evt\n"); + + hack->callback = btu_hcif_command_complete_evt_on_task; + hack->response = response; + hack->context = context; + + event->event = BTU_POST_TO_TASK_NO_GOOD_HORRIBLE_HACK; + + if (btu_task_post(SIG_BTU_HCI_MSG, event, OSI_THREAD_MAX_TIMEOUT) == false) { + osi_free(event); + } +} + + +/******************************************************************************* +** +** Function btu_hcif_hdl_command_status +** +** Description Handle a command status event +** +** Returns void +** +*******************************************************************************/ +static void btu_hcif_hdl_command_status (UINT16 opcode, UINT8 status, UINT8 *p_cmd, + void *p_vsc_status_cback) +{ + BD_ADDR bd_addr; + UINT16 handle; +#if BTM_SCO_INCLUDED == TRUE + tBTM_ESCO_DATA esco_data; +#endif + + switch (opcode) { + case HCI_EXIT_SNIFF_MODE: + case HCI_EXIT_PARK_MODE: +#if BTM_SCO_WAKE_PARKED_LINK == TRUE + if (status != HCI_SUCCESS) { + /* Allow SCO initiation to continue if waiting for change mode event */ + if (p_cmd != NULL) { + p_cmd++; /* bypass length field */ + STREAM_TO_UINT16 (handle, p_cmd); + btm_sco_chk_pend_unpark (status, handle); + } + } +#endif + /* Case Falls Through */ + + case HCI_HOLD_MODE: + case HCI_SNIFF_MODE: + case HCI_PARK_MODE: + btm_pm_proc_cmd_status(status); + break; +#if (BLE_50_FEATURE_SUPPORT == TRUE) + case HCI_BLE_PERIOD_ADV_CREATE_SYNC: + { + uint8_t btm_status = BTM_SUCCESS; + if(status != HCI_SUCCESS) { + HCI_TRACE_ERROR("CS evt: LE PA CreateSync status=0x%x", status); + btm_status = BTM_ILLEGAL_VALUE; + } + btm_create_sync_callback(btm_status); + break; + } + case HCI_BLE_SET_PHY: + { + uint8_t btm_status = BTM_SUCCESS; + if(status != HCI_SUCCESS) { + HCI_TRACE_ERROR("CS evt: LE SetPhy status=0x%x", status); + btm_status = BTM_ILLEGAL_VALUE; + } + btm_set_phy_callback(btm_status); + break; + } +#endif // #if (BLE_50_FEATURE_SUPPORT == TRUE) + default: + /* If command failed to start, we may need to tell BTM */ + if (status != HCI_SUCCESS) { + switch (opcode) { + case HCI_INQUIRY: + /* Tell inquiry processing that we are done */ + btm_process_inq_complete(status, BTM_BR_INQUIRY_MASK); + break; + + case HCI_RMT_NAME_REQUEST: + /* Tell inquiry processing that we are done */ + btm_process_remote_name (NULL, NULL, 0, status); +#if (SMP_INCLUDED == TRUE) + btm_sec_rmt_name_request_complete (NULL, NULL, status); +#endif ///SMP_INCLUDED == TRUE + break; + + case HCI_QOS_SETUP_COMP_EVT: + /* Tell qos setup that we are done */ + btm_qos_setup_complete(status, 0, NULL); + break; + + case HCI_SWITCH_ROLE: + /* Tell BTM that the command failed */ + /* read bd addr out of stored command */ + if (p_cmd != NULL) { + p_cmd++; + STREAM_TO_BDADDR (bd_addr, p_cmd); + btm_acl_role_changed(status, bd_addr, BTM_ROLE_UNDEFINED); + } else { + btm_acl_role_changed(status, NULL, BTM_ROLE_UNDEFINED); + } + l2c_link_role_changed (NULL, BTM_ROLE_UNDEFINED, HCI_ERR_COMMAND_DISALLOWED); + break; + + case HCI_CREATE_CONNECTION: + /* read bd addr out of stored command */ + if (p_cmd != NULL) { + p_cmd++; + STREAM_TO_BDADDR (bd_addr, p_cmd); +#if (SMP_INCLUDED == TRUE) + btm_sec_connected (bd_addr, HCI_INVALID_HANDLE, status, 0); +#endif ///SMP_INCLUDED == TRUE + l2c_link_hci_conn_comp (status, HCI_INVALID_HANDLE, bd_addr); + } + break; + + case HCI_READ_RMT_EXT_FEATURES: + if (p_cmd != NULL) { + p_cmd++; /* skip command length */ + STREAM_TO_UINT16 (handle, p_cmd); + } else { + handle = HCI_INVALID_HANDLE; + } + + btm_read_remote_ext_features_failed(status, handle); + break; + + case HCI_AUTHENTICATION_REQUESTED: +#if (SMP_INCLUDED == TRUE) + /* Device refused to start authentication. That should be treated as authentication failure. */ + btm_sec_auth_complete (BTM_INVALID_HCI_HANDLE, status); +#endif ///SMP_INCLUDED == TRUE + break; + + case HCI_SET_CONN_ENCRYPTION: +#if (SMP_INCLUDED == TRUE) + /* Device refused to start encryption. That should be treated as encryption failure. */ + btm_sec_encrypt_change (BTM_INVALID_HCI_HANDLE, status, FALSE); +#endif ///SMP_INCLUDED == TRUE + break; + +#if BLE_INCLUDED == TRUE + case HCI_BLE_CREATE_LL_CONN: + btm_ble_create_ll_conn_complete(status); + break; + case HCI_BLE_UPD_LL_CONN_PARAMS: + if (p_cmd != NULL){ + p_cmd++; + STREAM_TO_UINT16 (handle, p_cmd); + btu_ble_ll_get_conn_param_format_err_from_contoller(status, handle); + } + break; +#endif + +#if BTM_SCO_INCLUDED == TRUE + case HCI_SETUP_ESCO_CONNECTION: + /* read handle out of stored command */ + if (p_cmd != NULL) { + p_cmd++; + STREAM_TO_UINT16 (handle, p_cmd); + + /* Determine if initial connection failed or is a change of setup */ + if (btm_is_sco_active(handle)) { + btm_esco_proc_conn_chg (status, handle, 0, 0, 0, 0); + } else { + btm_sco_connected (status, NULL, handle, &esco_data); + } + } + break; +#endif + + /* This is commented out until an upper layer cares about returning event + #if L2CAP_NON_FLUSHABLE_PB_INCLUDED == TRUE + case HCI_ENHANCED_FLUSH: + break; + #endif + */ + default: + if ((opcode & HCI_GRP_VENDOR_SPECIFIC) == HCI_GRP_VENDOR_SPECIFIC) { + btm_vsc_complete (&status, opcode, 1, (tBTM_CMPL_CB *)p_vsc_status_cback); + } + break; + } + + } else { + if ((opcode & HCI_GRP_VENDOR_SPECIFIC) == HCI_GRP_VENDOR_SPECIFIC) { + btm_vsc_complete (&status, opcode, 1, (tBTM_CMPL_CB *)p_vsc_status_cback); + } + } + } +} + +/******************************************************************************* +** +** Function btu_hcif_command_status_evt +** +** Description Process event HCI_COMMAND_STATUS_EVT +** +** Returns void +** +*******************************************************************************/ +static void btu_hcif_command_status_evt_on_task(BT_HDR *event) +{ + command_status_hack_t *hack = (command_status_hack_t *)&event->data[0]; + + command_opcode_t opcode; + uint8_t *stream = hack->command->data + hack->command->offset; + STREAM_TO_UINT16(opcode, stream); + + btu_hcif_hdl_command_status( + opcode, + hack->status, + stream, + hack->context); + + // check the HCI command integrity: opcode + hci_cmd_metadata_t *metadata = HCI_GET_CMD_METAMSG(hack->command); + assert(metadata->opcode == opcode); + + HCI_FREE_CMD_BUF(hack->command); + osi_free(event); +} + +static void btu_hcif_command_status_evt(uint8_t status, BT_HDR *command, void *context) +{ + BT_HDR *event = osi_calloc(sizeof(BT_HDR) + sizeof(command_status_hack_t)); + command_status_hack_t *hack = (command_status_hack_t *)&event->data[0]; + + hack->callback = btu_hcif_command_status_evt_on_task; + hack->status = status; + hack->command = command; + hack->context = context; + + event->event = BTU_POST_TO_TASK_NO_GOOD_HORRIBLE_HACK; + + if (btu_task_post(SIG_BTU_HCI_MSG, event, OSI_THREAD_MAX_TIMEOUT) == false) { + osi_free(event); + } +} + +/******************************************************************************* +** +** Function btu_hcif_hardware_error_evt +** +** Description Process event HCI_HARDWARE_ERROR_EVT +** +** Returns void +** +*******************************************************************************/ +static void btu_hcif_hardware_error_evt (UINT8 *p) +{ + HCI_TRACE_ERROR("Ctlr H/w error event - code:0x%x\n", *p); + + /* If anyone wants device status notifications, give him one. */ + btm_report_device_status (BTM_DEV_STATUS_DOWN); + + /* Reset the controller */ + if (BTM_IsDeviceUp()) { + BTM_DeviceReset (NULL); + } +} + + +/******************************************************************************* +** +** Function btu_hcif_flush_occured_evt +** +** Description Process event HCI_FLUSH_OCCURED_EVT +** +** Returns void +** +*******************************************************************************/ +static void btu_hcif_flush_occured_evt (void) +{ +} + + +/******************************************************************************* +** +** Function btu_hcif_role_change_evt +** +** Description Process event HCI_ROLE_CHANGE_EVT +** +** Returns void +** +*******************************************************************************/ +static void btu_hcif_role_change_evt (UINT8 *p) +{ + UINT8 status; + BD_ADDR bda; + UINT8 role; + + STREAM_TO_UINT8 (status, p); + STREAM_TO_BDADDR (bda, p); + STREAM_TO_UINT8 (role, p); + + l2c_link_role_changed (bda, role, status); + btm_acl_role_changed(status, bda, role); +} + + +/******************************************************************************* +** +** Function btu_hcif_num_compl_data_pkts_evt +** +** Description Process event HCI_NUM_COMPL_DATA_PKTS_EVT +** +** Returns void +** +*******************************************************************************/ +static void btu_hcif_num_compl_data_pkts_evt (UINT8 *p) +{ + /* Process for L2CAP and SCO */ + l2c_link_process_num_completed_pkts (p); + + /* Send on to SCO */ +#if (BTM_SCO_HCI_INCLUDED == TRUE) && (BTM_SCO_INCLUDED == TRUE) + btm_sco_process_num_completed_pkts (p); +#endif +} + +/******************************************************************************* +** +** Function btu_hcif_mode_change_evt +** +** Description Process event HCI_MODE_CHANGE_EVT +** +** Returns void +** +*******************************************************************************/ +static void btu_hcif_mode_change_evt (UINT8 *p) +{ + UINT8 status; + UINT16 handle; + UINT8 current_mode; + UINT16 interval; + + STREAM_TO_UINT8 (status, p); + + STREAM_TO_UINT16 (handle, p); + STREAM_TO_UINT8 (current_mode, p); + STREAM_TO_UINT16 (interval, p); +#if BTM_SCO_WAKE_PARKED_LINK == TRUE + btm_sco_chk_pend_unpark (status, handle); +#endif + btm_pm_proc_mode_change (status, handle, current_mode, interval); + HCI_TRACE_WARNING("hcif mode change: hdl 0x%x, mode %d, intv %d, status 0x%x", + handle, current_mode, interval, status); + + /* + #if (HID_DEV_INCLUDED == TRUE) && (HID_DEV_PM_INCLUDED == TRUE) + hidd_pm_proc_mode_change( status, current_mode, interval ) ; + #endif + */ +} + +/******************************************************************************* +** +** Function btu_hcif_ssr_evt +** +** Description Process event HCI_SNIFF_SUB_RATE_EVT +** +** Returns void +** +*******************************************************************************/ +static void btu_hcif_ssr_evt (UINT8 *p, UINT16 evt_len) +{ +#if (BTM_SSR_INCLUDED == TRUE) + btm_pm_proc_ssr_evt(p, evt_len); +#endif + + UINT8 status; + UINT16 handle; + UINT16 max_tx_lat; + UINT16 max_rx_lat; + + STREAM_TO_UINT8 (status, p); + STREAM_TO_UINT16 (handle, p); + STREAM_TO_UINT16 (max_tx_lat, p); + STREAM_TO_UINT16 (max_rx_lat, p); + + UNUSED(status); + UNUSED(handle); + UNUSED(max_tx_lat); + UNUSED(max_rx_lat); + + HCI_TRACE_WARNING("hcif ssr evt: st 0x%x, hdl 0x%x, tx_lat %d rx_lat %d", status, handle, max_tx_lat, max_rx_lat); +} + +/******************************************************************************* +** +** Function btu_hcif_pin_code_request_evt +** +** Description Process event HCI_PIN_CODE_REQUEST_EVT +** +** Returns void +** +*******************************************************************************/ +#if (SMP_INCLUDED == TRUE) +static void btu_hcif_pin_code_request_evt (UINT8 *p) +{ +#if (CLASSIC_BT_INCLUDED == TRUE) + BD_ADDR bda; + + STREAM_TO_BDADDR (bda, p); + + /* Tell L2CAP that there was a PIN code request, */ + /* it may need to stretch timeouts */ + l2c_pin_code_request (bda); + + btm_sec_pin_code_request (bda); +#endif ///CLASSIC_BT_INCLUDED == TRUE +} + + +/******************************************************************************* +** +** Function btu_hcif_link_key_request_evt +** +** Description Process event HCI_LINK_KEY_REQUEST_EVT +** +** Returns void +** +*******************************************************************************/ +static void btu_hcif_link_key_request_evt (UINT8 *p) +{ + BD_ADDR bda; + + STREAM_TO_BDADDR (bda, p); + btm_sec_link_key_request (bda); +} + +/******************************************************************************* +** +** Function btu_hcif_link_key_notification_evt +** +** Description Process event HCI_LINK_KEY_NOTIFICATION_EVT +** +** Returns void +** +*******************************************************************************/ +static void btu_hcif_link_key_notification_evt (UINT8 *p) +{ + BD_ADDR bda; + LINK_KEY key; + UINT8 key_type; + + STREAM_TO_BDADDR (bda, p); + STREAM_TO_ARRAY16 (key, p); + STREAM_TO_UINT8 (key_type, p); + + btm_sec_link_key_notification (bda, key, key_type); +} +#endif ///SMP_INCLUDED == TRUE + + +/******************************************************************************* +** +** Function btu_hcif_loopback_command_evt +** +** Description Process event HCI_LOOPBACK_COMMAND_EVT +** +** Returns void +** +*******************************************************************************/ +static void btu_hcif_loopback_command_evt (void) +{ +} + + +/******************************************************************************* +** +** Function btu_hcif_data_buf_overflow_evt +** +** Description Process event HCI_DATA_BUF_OVERFLOW_EVT +** +** Returns void +** +*******************************************************************************/ +static void btu_hcif_data_buf_overflow_evt (void) +{ +} + + +/******************************************************************************* +** +** Function btu_hcif_max_slots_changed_evt +** +** Description Process event HCI_MAX_SLOTS_CHANGED_EVT +** +** Returns void +** +*******************************************************************************/ +static void btu_hcif_max_slots_changed_evt (void) +{ +} + + +/******************************************************************************* +** +** Function btu_hcif_read_clock_off_comp_evt +** +** Description Process event HCI_READ_CLOCK_OFF_COMP_EVT +** +** Returns void +** +*******************************************************************************/ +static void btu_hcif_read_clock_off_comp_evt (UINT8 *p) +{ + UINT8 status; + UINT16 handle; + UINT16 clock_offset; + + STREAM_TO_UINT8 (status, p); + + /* If failed to get clock offset just drop the result */ + if (status != HCI_SUCCESS) { + return; + } + + STREAM_TO_UINT16 (handle, p); + STREAM_TO_UINT16 (clock_offset, p); + + handle = HCID_GET_HANDLE (handle); + + btm_process_clk_off_comp_evt (handle, clock_offset); + btm_sec_update_clock_offset (handle, clock_offset); +} + + +/******************************************************************************* +** +** Function btu_hcif_conn_pkt_type_change_evt +** +** Description Process event HCI_CONN_PKT_TYPE_CHANGE_EVT +** +** Returns void +** +*******************************************************************************/ +static void btu_hcif_conn_pkt_type_change_evt (UINT8 *p) +{ + UINT8 status; + UINT16 handle; + UINT16 pkt_types; + + STREAM_TO_UINT8 (status, p); + STREAM_TO_UINT16 (handle, p); + STREAM_TO_UINT16 (pkt_types, p); + + handle = HCID_GET_HANDLE (handle); + + btm_acl_pkt_types_changed(status, handle, pkt_types); +} + + +/******************************************************************************* +** +** Function btu_hcif_qos_violation_evt +** +** Description Process event HCI_QOS_VIOLATION_EVT +** +** Returns void +** +*******************************************************************************/ +static void btu_hcif_qos_violation_evt (UINT8 *p) +{ + UINT16 handle; + + STREAM_TO_UINT16 (handle, p); + + handle = HCID_GET_HANDLE (handle); + + + l2c_link_hci_qos_violation (handle); +} + + +/******************************************************************************* +** +** Function btu_hcif_page_scan_mode_change_evt +** +** Description Process event HCI_PAGE_SCAN_MODE_CHANGE_EVT +** +** Returns void +** +*******************************************************************************/ +static void btu_hcif_page_scan_mode_change_evt (void) +{ +} + + +/******************************************************************************* +** +** Function btu_hcif_page_scan_rep_mode_chng_evt +** +** Description Process event HCI_PAGE_SCAN_REP_MODE_CHNG_EVT +** +** Returns void +** +*******************************************************************************/ +static void btu_hcif_page_scan_rep_mode_chng_evt (void) +{ +} + +/********************************************** +** Simple Pairing Events +***********************************************/ + +/******************************************************************************* +** +** Function btu_hcif_host_support_evt +** +** Description Process event HCI_RMT_HOST_SUP_FEAT_NOTIFY_EVT +** +** Returns void +** +*******************************************************************************/ +static void btu_hcif_host_support_evt (UINT8 *p) +{ + btm_sec_rmt_host_support_feat_evt(p); +} + +/******************************************************************************* +** +** Function btu_hcif_io_cap_request_evt +** +** Description Process event HCI_IO_CAPABILITY_REQUEST_EVT +** +** Returns void +** +*******************************************************************************/ +#if (CLASSIC_BT_INCLUDED == TRUE) +static void btu_hcif_io_cap_request_evt (UINT8 *p) +{ + btm_io_capabilities_req(p); +} + + +/******************************************************************************* +** +** Function btu_hcif_io_cap_response_evt +** +** Description Process event HCI_IO_CAPABILITY_RESPONSE_EVT +** +** Returns void +** +*******************************************************************************/ +static void btu_hcif_io_cap_response_evt (UINT8 *p) +{ + btm_io_capabilities_rsp(p); +} + + +/******************************************************************************* +** +** Function btu_hcif_user_conf_request_evt +** +** Description Process event HCI_USER_CONFIRMATION_REQUEST_EVT +** +** Returns void +** +*******************************************************************************/ +static void btu_hcif_user_conf_request_evt (UINT8 *p) +{ + btm_proc_sp_req_evt(BTM_SP_CFM_REQ_EVT, p); +} + +/******************************************************************************* +** +** Function btu_hcif_user_passkey_request_evt +** +** Description Process event HCI_USER_PASSKEY_REQUEST_EVT +** +** Returns void +** +*******************************************************************************/ +static void btu_hcif_user_passkey_request_evt (UINT8 *p) +{ + btm_proc_sp_req_evt(BTM_SP_KEY_REQ_EVT, p); +} + +/******************************************************************************* +** +** Function btu_hcif_simple_pair_complete_evt +** +** Description Process event HCI_SIMPLE_PAIRING_COMPLETE_EVT +** +** Returns void +** +*******************************************************************************/ +static void btu_hcif_simple_pair_complete_evt (UINT8 *p) +{ + btm_simple_pair_complete(p); +} + +/******************************************************************************* +** +** Function btu_hcif_user_passkey_notif_evt +** +** Description Process event HCI_USER_PASSKEY_NOTIFY_EVT +** +** Returns void +** +*******************************************************************************/ +static void btu_hcif_user_passkey_notif_evt (UINT8 *p) +{ + btm_proc_sp_req_evt(BTM_SP_KEY_NOTIF_EVT, p); +} + +/******************************************************************************* +** +** Function btu_hcif_keypress_notif_evt +** +** Description Process event HCI_KEYPRESS_NOTIFY_EVT +** +** Returns void +** +*******************************************************************************/ +static void btu_hcif_keypress_notif_evt (UINT8 *p) +{ + btm_keypress_notif_evt(p); +} +#endif /* (CLASSIC_BT_INCLUDED == TRUE) */ + +/******************************************************************************* +** +** Function btu_hcif_rem_oob_request_evt +** +** Description Process event HCI_REMOTE_OOB_DATA_REQUEST_EVT +** +** Returns void +** +*******************************************************************************/ +#if BTM_OOB_INCLUDED == TRUE && SMP_INCLUDED == TRUE +static void btu_hcif_rem_oob_request_evt (UINT8 *p) +{ + btm_rem_oob_req(p); +} +#endif + +/******************************************************************************* +** +** Function btu_hcif_link_supv_to_changed_evt +** +** Description Process event HCI_LINK_SUPER_TOUT_CHANGED_EVT +** +** Returns void +** +*******************************************************************************/ +static void btu_hcif_link_supv_to_changed_evt (UINT8 *p) +{ + UINT16 handle; + UINT16 supv_to; + + STREAM_TO_UINT16(handle, p); + STREAM_TO_UINT16(supv_to, p); + + UNUSED(handle); + UNUSED(supv_to); + + HCI_TRACE_WARNING("hcif link supv_to changed: hdl 0x%x, supv_to %d", handle, supv_to); +} + +/******************************************************************************* +** +** Function btu_hcif_enhanced_flush_complete_evt +** +** Description Process event HCI_ENHANCED_FLUSH_COMPLETE_EVT +** +** Returns void +** +*******************************************************************************/ +#if L2CAP_NON_FLUSHABLE_PB_INCLUDED == TRUE +static void btu_hcif_enhanced_flush_complete_evt (void) +{ + /* This is empty until an upper layer cares about returning event */ +} +#endif +/********************************************** +** End of Simple Pairing Events +***********************************************/ + + +/********************************************** +** BLE Events +***********************************************/ +#if (defined BLE_INCLUDED) && (BLE_INCLUDED == TRUE) +#if (SMP_INCLUDED == TRUE) +static void btu_hcif_encryption_key_refresh_cmpl_evt (UINT8 *p) +{ + UINT8 status; + UINT8 enc_enable = 0; + UINT16 handle; + + STREAM_TO_UINT8 (status, p); + STREAM_TO_UINT16 (handle, p); + + if (status == HCI_SUCCESS) { + enc_enable = 1; + } + + btm_sec_encrypt_change (handle, status, enc_enable); +} +#endif ///SMP_INCLUDED == TRUE + +static void btu_ble_ll_conn_complete_evt ( UINT8 *p, UINT16 evt_len) +{ + btm_ble_conn_complete(p, evt_len, FALSE); +} +//#if (defined BLE_PRIVACY_SPT && BLE_PRIVACY_SPT == TRUE) +static void btu_ble_proc_enhanced_conn_cmpl( UINT8 *p, UINT16 evt_len) +{ + btm_ble_conn_complete(p, evt_len, TRUE); +} +//#endif +static void btu_ble_ll_conn_param_upd_evt (UINT8 *p, UINT16 evt_len) +{ + /* LE connection update has completed successfully as a master. */ + /* We can enable the update request if the result is a success. */ + /* extract the HCI handle first */ + UINT8 status; + UINT16 handle, conn_interval, conn_latency, conn_timeout; + STREAM_TO_UINT8 (status, p); + STREAM_TO_UINT16 (handle, p); + STREAM_TO_UINT16 (conn_interval, p); + STREAM_TO_UINT16 (conn_latency, p); + STREAM_TO_UINT16 (conn_timeout, p); + + l2cble_process_conn_update_evt(handle, status, conn_interval, + conn_latency, conn_timeout); +} + +static void btu_ble_ll_get_conn_param_format_err_from_contoller (UINT8 status, UINT16 handle) +{ + /* host send illegal connection parameters format, controller would send + back HCI_ERR_ILLEGAL_PARAMETER_FMT */ + l2cble_get_conn_param_format_err_from_contoller(status, handle); + +} + +static void btu_ble_read_remote_feat_evt (UINT8 *p) +{ + btm_ble_read_remote_features_complete(p); +} + +#if BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE +static void btu_ble_proc_ltk_req (UINT8 *p) +{ + + UINT16 ediv, handle; + UINT8 *pp; + + STREAM_TO_UINT16(handle, p); + pp = p + 8; + STREAM_TO_UINT16(ediv, pp); + + btm_ble_ltk_request(handle, p, ediv); + /* This is empty until an upper layer cares about returning event */ +} +#endif ///BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE + + +static void btu_ble_data_length_change_evt(UINT8 *p, UINT16 evt_len) +{ + UINT16 handle; + UINT16 tx_data_len; + UINT16 rx_data_len; + + if (!controller_get_interface()->supports_ble_packet_extension()) { + HCI_TRACE_WARNING("%s, request not supported", __FUNCTION__); + return; + } + + STREAM_TO_UINT16(handle, p); + STREAM_TO_UINT16(tx_data_len, p); + p += 2; /* Skip the TxTimer */ + STREAM_TO_UINT16(rx_data_len, p); + + l2cble_process_data_length_change_event(handle, tx_data_len, rx_data_len); +} +#if (BLE_50_FEATURE_SUPPORT == TRUE) +static void btu_ble_phy_update_complete_evt(UINT8 *p) +{ + if (!p) { + HCI_TRACE_ERROR("%s, Invalid params.", __func__); + return; + } + tBTM_BLE_UPDATE_PHY update_phy = {0}; + STREAM_TO_UINT8(update_phy.status, p); + STREAM_TO_UINT16(update_phy.conn_idx, p); + STREAM_TO_UINT8(update_phy.tx_phy, p); + STREAM_TO_UINT8(update_phy.rx_phy, p); + + btm_ble_update_phy_evt(&update_phy); +} + +#if BLE_PRIVACY_SPT == TRUE +/******************************************************************************* +** +** Function btm_ble_resolve_random_addr_adv_ext +** +** Description resolve random address complete callback. +** +** Returns void +** +*******************************************************************************/ +static void btm_ble_resolve_random_addr_adv_ext(void *p_rec, void *p) +{ + tBTM_SEC_DEV_REC *match_rec = (tBTM_SEC_DEV_REC *) p_rec; + BD_ADDR bda; + UINT8 *pp = (UINT8 *)p+4; //jump to the location of bd addr + if (match_rec) { + // Assign the original address to be the current report address + memcpy(bda, match_rec->ble.pseudo_addr, BD_ADDR_LEN); + BDADDR_TO_STREAM(pp,bda); + } +} +#endif + +static void btu_ble_ext_adv_report_evt(UINT8 *p, UINT16 evt_len) +{ + tBTM_BLE_EXT_ADV_REPORT ext_adv_report = {0}; + UINT8 num_reports = {0}; + UINT8 *pp = p; + //UINT8 legacy_event_type = 0; + UINT16 evt_type = 0; + uint8_t addr_type; + BD_ADDR bda; + #if (defined BLE_PRIVACY_SPT && BLE_PRIVACY_SPT == TRUE) + BOOLEAN match = FALSE; + #endif + + if (!p) { + HCI_TRACE_ERROR("%s, Invalid params.", __func__); + return; + } + + STREAM_TO_UINT8(num_reports, p); + + if (num_reports == 0) { + HCI_TRACE_ERROR("%s, Invalid number reports is 0", __func__); + } + + while (num_reports--) { + STREAM_TO_UINT16(evt_type, p); + ext_adv_report.event_type = evt_type & 0x1F; + if(ext_adv_report.event_type & BTM_BLE_ADV_LEGACY_MASK) { + ext_adv_report.data_status = BTM_BLE_EXT_ADV_DATA_COMPLETE; + } else { + switch(evt_type & BTM_BLE_ADV_DATA_STATUS_MASK) { + case BTM_BLE_ADV_DATA_COMPLETE_MASK: + ext_adv_report.data_status = BTM_BLE_EXT_ADV_DATA_COMPLETE; + break; + case BTM_BLE_ADV_DATA_INCOMPLETE_MASK: + ext_adv_report.data_status = BTM_BLE_EXT_ADV_DATA_INCOMPLETE; + break; + case BTM_BLE_ADV_DATA_TRUNCATED_MASK: + ext_adv_report.data_status = BTM_BLE_EXT_ADV_DATA_TRUNCATED; + break; + } + } + + STREAM_TO_UINT8(addr_type, p); + STREAM_TO_BDADDR(bda, p); +#if (defined BLE_PRIVACY_SPT && BLE_PRIVACY_SPT == TRUE) + if(addr_type != 0xFF) { + match = btm_identity_addr_to_random_pseudo(bda, &addr_type, FALSE); + if (!match && BTM_BLE_IS_RESOLVE_BDA(bda)) { + btm_ble_resolve_random_addr(bda, btm_ble_resolve_random_addr_adv_ext, pp); + //the BDADDR may be updated, so read it again + p = p - sizeof(bda); + STREAM_TO_BDADDR(bda, p); + } + } +#endif + ext_adv_report.addr_type = addr_type; + memcpy(ext_adv_report.addr, bda, 6); + STREAM_TO_UINT8(ext_adv_report.primary_phy, p); + STREAM_TO_UINT8(ext_adv_report.secondry_phy, p); + STREAM_TO_UINT8(ext_adv_report.sid, p); + STREAM_TO_UINT8(ext_adv_report.tx_power, p); + STREAM_TO_UINT8(ext_adv_report.rssi, p); + STREAM_TO_UINT16(ext_adv_report.per_adv_interval, p); + STREAM_TO_UINT8(ext_adv_report.dir_addr_type, p); + STREAM_TO_BDADDR(ext_adv_report.dir_addr, p); + STREAM_TO_UINT8(ext_adv_report.adv_data_len, p); + if (ext_adv_report.adv_data_len) { + ext_adv_report.adv_data = p; + } else { + ext_adv_report.adv_data = NULL; + } + + btm_ble_ext_adv_report_evt(&ext_adv_report); + p += ext_adv_report.adv_data_len; + } + +} + +static void btu_ble_periodic_adv_sync_establish_evt(UINT8 *p) +{ + tBTM_BLE_PERIOD_ADV_SYNC_ESTAB sync_estab = {0}; + + if (!p) { + HCI_TRACE_ERROR("%s, Invalid params.", __func__); + return; + } + + STREAM_TO_UINT8(sync_estab.status, p); + STREAM_TO_UINT16(sync_estab.sync_handle, p); + STREAM_TO_UINT8(sync_estab.sid, p); + STREAM_TO_UINT8(sync_estab.adv_addr_type, p); + STREAM_TO_BDADDR(sync_estab.adv_addr, p); + STREAM_TO_UINT8(sync_estab.adv_phy, p); + STREAM_TO_UINT16(sync_estab.period_adv_interval, p); + STREAM_TO_UINT8(sync_estab.adv_clk_accuracy, p); + + btm_ble_periodic_adv_sync_establish_evt(&sync_estab); +} + +static void btu_ble_periodic_adv_report_evt(UINT8 *p, UINT8 evt_len) +{ + tBTM_PERIOD_ADV_REPORT adv_report = {0}; + /* This parameter is intended to be used in a future feature. */ + UINT8 unused = 0; + + if (!p) { + HCI_TRACE_ERROR("%s, Invalid params.", __func__); + return; + } + + if (evt_len < MIN_BLE_PERIODIC_ADV_REPORT_LEN) { + HCI_TRACE_ERROR("%s, Invalid params, the adv len is too short.", __func__); + return; + } + + STREAM_TO_UINT16(adv_report.sync_handle, p); + STREAM_TO_UINT8(adv_report.tx_power, p); + STREAM_TO_UINT8(adv_report.rssi, p); + STREAM_TO_UINT8(unused, p); + STREAM_TO_UINT8(adv_report.data_status, p); + STREAM_TO_UINT8(adv_report.data_length, p); + + if ((evt_len - MIN_BLE_PERIODIC_ADV_REPORT_LEN) != adv_report.data_length) { + HCI_TRACE_ERROR("%s, Invalid ev_len = %d is less than adv len = %d", __func__, evt_len, adv_report.data_length); + return; + } + + if (adv_report.data_length) { + adv_report.data = p; + } else { + adv_report.data = NULL; + } + + btm_ble_periodic_adv_report_evt(&adv_report); + + UNUSED(unused); +} + +static void btu_ble_periodic_adv_sync_lost_evt(UINT8 *p) +{ + tBTM_BLE_PERIOD_ADV_SYNC_LOST sync_lost = {0}; + if (!p) { + HCI_TRACE_ERROR("%s, Invalid params.", __func__); + return; + } + + STREAM_TO_UINT16(sync_lost.sync_handle, p); + + btm_ble_periodic_adv_sync_lost_evt(&sync_lost); +} + +static void btu_ble_scan_timeout_evt(UINT8 *p) +{ + UNUSED(p); + + btm_ble_scan_timeout_evt(); +} + +static void btu_ble_adv_set_terminate_evt(UINT8 *p) +{ + tBTM_BLE_ADV_TERMINAT adv_term = {0}; + + if (!p) { + HCI_TRACE_ERROR("%s, Invalid params.", __func__); + return; + } + + STREAM_TO_UINT8(adv_term.status, p); + STREAM_TO_UINT8(adv_term.adv_handle, p); + STREAM_TO_UINT16(adv_term.conn_handle, p); + STREAM_TO_UINT8(adv_term.completed_event, p); + + btm_ble_adv_set_terminated_evt(&adv_term); +} + +static void btu_ble_scan_req_received_evt(UINT8 *p) +{ + tBTM_BLE_SCAN_REQ_RECEIVED req_received = {0}; + + if (!p) { + HCI_TRACE_ERROR("%s, Invalid params.", __func__); + return; + } + + STREAM_TO_UINT8(req_received.adv_handle, p); + STREAM_TO_UINT8(req_received.scan_addr_type, p); + STREAM_TO_BDADDR(req_received.scan_addr, p); + + btm_ble_scan_req_received_evt(&req_received); +} +#endif // #if (BLE_50_FEATURE_SUPPORT == TRUE) + +#if (BLE_FEAT_PERIODIC_ADV_SYNC_TRANSFER == TRUE) +static void btu_ble_periodic_adv_sync_trans_recv(UINT8 *p) +{ + UINT16 conn_handle; + tL2C_LCB *p_lcb = NULL; + tBTM_BLE_PERIOD_ADV_SYNC_TRANS_RECV past_recv = {0}; + + if (!p) { + HCI_TRACE_ERROR("%s, Invalid params.", __func__); + return; + } + + STREAM_TO_UINT8(past_recv.status, p); + STREAM_TO_UINT16(conn_handle, p); + STREAM_TO_UINT16(past_recv.service_data, p); + STREAM_TO_UINT16(past_recv.sync_handle, p); + STREAM_TO_UINT8(past_recv.adv_sid, p); + STREAM_TO_UINT8(past_recv.adv_addr_type, p); + STREAM_TO_BDADDR(past_recv.adv_addr, p); + STREAM_TO_UINT8(past_recv.adv_phy, p); + STREAM_TO_UINT16(past_recv.adv_interval, p); + STREAM_TO_UINT8(past_recv.adv_clk_accuracy, p); + + p_lcb = l2cu_find_lcb_by_handle(conn_handle); + if(p_lcb) { + memcpy(past_recv.addr, p_lcb->remote_bd_addr, BD_ADDR_LEN); + } + + btm_ble_periodic_adv_sync_trans_recv_evt(&past_recv); +} +#endif // #if (BLE_FEAT_PERIODIC_ADV_SYNC_TRANSFER == TRUE) + +/********************************************** +** End of BLE Events Handler +***********************************************/ +#if (defined BLE_LLT_INCLUDED) && (BLE_LLT_INCLUDED == TRUE) +static void btu_ble_rc_param_req_evt(UINT8 *p) +{ + UINT16 handle; + UINT16 int_min, int_max, latency, timeout; + + STREAM_TO_UINT16(handle, p); + STREAM_TO_UINT16(int_min, p); + STREAM_TO_UINT16(int_max, p); + STREAM_TO_UINT16(latency, p); + STREAM_TO_UINT16(timeout, p); + + l2cble_process_rc_param_request_evt(handle, int_min, int_max, latency, timeout); +} +#endif /* BLE_LLT_INCLUDED */ +#endif /* BLE_INCLUDED */ diff --git a/lib/bt/host/bluedroid/stack/btu/btu_init.c b/lib/bt/host/bluedroid/stack/btu/btu_init.c new file mode 100644 index 00000000..615fbf80 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/btu/btu_init.c @@ -0,0 +1,276 @@ +/****************************************************************************** + * + * Copyright (C) 2000-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ +#include + +#include "common/bt_defs.h" +#include "common/bt_target.h" +#include "common/bt_trace.h" +#include "device/controller.h" +#include "osi/alarm.h" +#include "osi/hash_map.h" +#include "osi/hash_functions.h" +#include "osi/thread.h" +#include "osi/mutex.h" + +#include "l2c_int.h" +#include "stack/dyn_mem.h" +#include "stack/btu.h" +#include "btm_int.h" + +#if SDP_INCLUDED == TRUE +#include "sdpint.h" +#endif + +#if (BLE_INCLUDED == TRUE) +#include "stack/gatt_api.h" +#include "gatt_int.h" +#if SMP_INCLUDED == TRUE +#include "smp_int.h" +#endif +#endif + +#define BTU_TASK_PINNED_TO_CORE (TASK_PINNED_TO_CORE) +#define BTU_TASK_STACK_SIZE (BT_BTU_TASK_STACK_SIZE + BT_TASK_EXTRA_STACK_SIZE) +#define BTU_TASK_PRIO (BT_TASK_MAX_PRIORITIES - 5) +#define BTU_TASK_NAME "BTU_TASK" +#define BTU_TASK_WORKQUEUE_NUM (1) +#define BTU_TASK_WORKQUEUE0_LEN (0) + +hash_map_t *btu_general_alarm_hash_map; +osi_mutex_t btu_general_alarm_lock; +static const size_t BTU_GENERAL_ALARM_HASH_MAP_SIZE = 34; + +hash_map_t *btu_oneshot_alarm_hash_map; +osi_mutex_t btu_oneshot_alarm_lock; +static const size_t BTU_ONESHOT_ALARM_HASH_MAP_SIZE = 34; + +hash_map_t *btu_l2cap_alarm_hash_map; +osi_mutex_t btu_l2cap_alarm_lock; +static const size_t BTU_L2CAP_ALARM_HASH_MAP_SIZE = 34; + +osi_thread_t *btu_thread = NULL; + +extern void PLATFORM_DisableHciTransport(UINT8 bDisable); + +extern void btu_task_thread_handler(void *arg); +void btu_task_start_up(void * param); +void btu_task_shut_down(void); + +/***************************************************************************** +** V A R I A B L E S * +******************************************************************************/ +// TODO(cmanton) Move this out of this file +const BD_ADDR BT_BD_ANY = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; +/***************************************************************************** +** +** Function btu_init_core +** +** Description Initialize control block memory for each core component. +** +** +** Returns void +** +******************************************************************************/ +void btu_init_core(void) +{ + /* Initialize the mandatory core stack components */ + btm_init(); + + l2c_init(); + +#if (defined(SDP_INCLUDED) && SDP_INCLUDED == TRUE) + sdp_init(); +#endif + +#if BLE_INCLUDED == TRUE +#if (defined(GATT_INCLUDED) && GATT_INCLUDED == true) + gatt_init(); +#endif +#if (defined(SMP_INCLUDED) && SMP_INCLUDED == TRUE) + SMP_Init(); +#endif + btm_ble_init(); +#endif +} + +/***************************************************************************** +** +** Function btu_free_core +** +** Description Releases control block memory for each core component. +** +** +** Returns void +** +******************************************************************************/ +void btu_free_core(void) +{ + // Free the mandatory core stack components + l2c_free(); + +#if (defined(SDP_INCLUDED) && SDP_INCLUDED == TRUE) + sdp_deinit(); +#endif + +#if BLE_INCLUDED == TRUE +#if (defined(GATT_INCLUDED) && GATT_INCLUDED == true) + gatt_free(); +#endif +#if SMP_INCLUDED == TRUE + SMP_Free(); +#endif + btm_ble_free(); +#endif + btm_free(); +} + +/***************************************************************************** +** +** Function BTU_StartUp +** +** Description Initializes the BTU control block. +** +** NOTE: Must be called before creating any tasks +** (RPC, BTU, HCIT, APPL, etc.) +** +** Returns void +** +******************************************************************************/ +void BTU_StartUp(void) +{ +#if BTU_DYNAMIC_MEMORY + btu_cb_ptr = (tBTU_CB *)osi_malloc(sizeof(tBTU_CB)); +#endif /* #if BTU_DYNAMIC_MEMORY */ + memset (&btu_cb, 0, sizeof (tBTU_CB)); + btu_cb.trace_level = HCI_INITIAL_TRACE_LEVEL; + + btu_general_alarm_hash_map = hash_map_new(BTU_GENERAL_ALARM_HASH_MAP_SIZE, + hash_function_pointer, NULL, (data_free_fn)osi_alarm_free, NULL); + if (btu_general_alarm_hash_map == NULL) { + goto error_exit; + } + + osi_mutex_new(&btu_general_alarm_lock); + + btu_oneshot_alarm_hash_map = hash_map_new(BTU_ONESHOT_ALARM_HASH_MAP_SIZE, + hash_function_pointer, NULL, (data_free_fn)osi_alarm_free, NULL); + if (btu_oneshot_alarm_hash_map == NULL) { + goto error_exit; + } + + osi_mutex_new(&btu_oneshot_alarm_lock); + + btu_l2cap_alarm_hash_map = hash_map_new(BTU_L2CAP_ALARM_HASH_MAP_SIZE, + hash_function_pointer, NULL, (data_free_fn)osi_alarm_free, NULL); + if (btu_l2cap_alarm_hash_map == NULL) { + goto error_exit; + } + + osi_mutex_new(&btu_l2cap_alarm_lock); + + const size_t workqueue_len[] = {BTU_TASK_WORKQUEUE0_LEN}; + btu_thread = osi_thread_create(BTU_TASK_NAME, BTU_TASK_STACK_SIZE, BTU_TASK_PRIO, BTU_TASK_PINNED_TO_CORE, + BTU_TASK_WORKQUEUE_NUM, workqueue_len); + if (btu_thread == NULL) { + goto error_exit; + } + + if (btu_task_post(SIG_BTU_START_UP, NULL, OSI_THREAD_MAX_TIMEOUT) == false) { + goto error_exit; + } + + return; + +error_exit:; + LOG_ERROR("%s Unable to allocate resources for bt_workqueue", __func__); + BTU_ShutDown(); +} + +/***************************************************************************** +** +** Function BTU_ShutDown +** +** Description Deinitializes the BTU control block. +** +** Returns void +** +******************************************************************************/ +void BTU_ShutDown(void) +{ +#if BTU_DYNAMIC_MEMORY + FREE_AND_RESET(btu_cb_ptr); +#endif + btu_task_shut_down(); + + hash_map_free(btu_general_alarm_hash_map); + osi_mutex_free(&btu_general_alarm_lock); + + hash_map_free(btu_oneshot_alarm_hash_map); + osi_mutex_free(&btu_oneshot_alarm_lock); + + hash_map_free(btu_l2cap_alarm_hash_map); + osi_mutex_free(&btu_l2cap_alarm_lock); + + if (btu_thread) { + osi_thread_free(btu_thread); + btu_thread = NULL; + } + + btu_general_alarm_hash_map = NULL; + btu_oneshot_alarm_hash_map = NULL; + btu_l2cap_alarm_hash_map = NULL; +} + +/***************************************************************************** +** +** Function BTU_BleAclPktSize +** +** Description export the BLE ACL packet size. +** +** Returns UINT16 +** +******************************************************************************/ +UINT16 BTU_BleAclPktSize(void) +{ +#if BLE_INCLUDED == TRUE + return controller_get_interface()->get_acl_packet_size_ble(); +#else + return 0; +#endif +} + +#if SCAN_QUEUE_CONGEST_CHECK +bool BTU_check_queue_is_congest(void) +{ + if (osi_thread_queue_wait_size(btu_thread, 0) >= BT_QUEUE_CONGEST_SIZE) { + return true; + } + + return false; +} +#endif + +int get_btu_work_queue_size(void) +{ + return osi_thread_queue_wait_size(btu_thread, 0); +} + +osi_thread_t *btu_get_current_thread(void) +{ + return btu_thread; +} diff --git a/lib/bt/host/bluedroid/stack/btu/btu_task.c b/lib/bt/host/bluedroid/stack/btu/btu_task.c new file mode 100644 index 00000000..9311a36d --- /dev/null +++ b/lib/bt/host/bluedroid/stack/btu/btu_task.c @@ -0,0 +1,693 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ +#include + + +#include "osi/alarm.h" +#include "osi/thread.h" +#include "common/bt_target.h" +#include "common/bt_trace.h" +#include "stack/bt_types.h" +#include "osi/allocator.h" +#include "osi/mutex.h" +#include "stack/btm_api.h" +#include "btm_int.h" +#include "stack/btu.h" +#include "osi/hash_map.h" +#include "stack/hcimsgs.h" +#include "l2c_int.h" +#include "osi/osi.h" +#if (defined(SDP_INCLUDED) && SDP_INCLUDED == TRUE) +#include "sdpint.h" +#endif + +#if (defined(RFCOMM_INCLUDED) && RFCOMM_INCLUDED == TRUE) +#include "stack/port_api.h" +#include "stack/port_ext.h" +#endif + +#if (defined(GAP_INCLUDED) && GAP_INCLUDED == TRUE) +#include "gap_int.h" +#endif + +#if (defined(BNEP_INCLUDED) && BNEP_INCLUDED == TRUE) +#include "bnep_int.h" +#endif + +#if (defined(PAN_INCLUDED) && PAN_INCLUDED == TRUE) +#include "pan_int.h" +#endif + +#if (defined(HID_HOST_INCLUDED) && HID_HOST_INCLUDED == TRUE ) +#include "hid_int.h" +#endif + +#if (defined(AVDT_INCLUDED) && AVDT_INCLUDED == TRUE) +#include "avdt_int.h" +#else +extern void avdt_rcv_sync_info (BT_HDR *p_buf); +#endif + +#if (defined(MCA_INCLUDED) && MCA_INCLUDED == TRUE) +#include "mca_api.h" +#include "mca_defs.h" +#include "mca_int.h" +#endif + +#if (defined(BTA_INCLUDED) && BTA_INCLUDED == TRUE) +#include "bta/bta_sys.h" +#endif + +#if (BLE_INCLUDED == TRUE) +#include "gatt_int.h" +#if (SMP_INCLUDED == TRUE) +#include "smp_int.h" +#endif +#include "btm_ble_int.h" +#endif + +typedef struct { + uint32_t sig; + void *param; +} btu_thread_evt_t; + +//#if (defined(BT_APP_DEMO) && BT_APP_DEMO == TRUE) +//#include "bt_app_common.h" +//#endif + +extern bt_status_t BTE_InitStack(void); +extern void BTE_DeinitStack(void); + +/* Define BTU storage area +*/ +#if BTU_DYNAMIC_MEMORY == FALSE +tBTU_CB btu_cb; +#else +tBTU_CB *btu_cb_ptr; +#endif + +extern hash_map_t *btu_general_alarm_hash_map; +extern osi_mutex_t btu_general_alarm_lock; + +// Oneshot timer queue. +extern hash_map_t *btu_oneshot_alarm_hash_map; +extern osi_mutex_t btu_oneshot_alarm_lock; + +// l2cap timer queue. +extern hash_map_t *btu_l2cap_alarm_hash_map; +extern osi_mutex_t btu_l2cap_alarm_lock; + +extern void *btu_thread; + +extern bluedroid_init_done_cb_t bluedroid_init_done_cb; + +/* Define a function prototype to allow a generic timeout handler */ +typedef void (tUSER_TIMEOUT_FUNC) (TIMER_LIST_ENT *p_tle); + +static void btu_l2cap_alarm_process(void *param); +static void btu_general_alarm_process(void *param); +static void btu_hci_msg_process(void *param); + +#if (defined(BTA_INCLUDED) && BTA_INCLUDED == TRUE) +static void btu_bta_alarm_process(void *param); +#endif + +static void btu_hci_msg_process(void *param) +{ + /* Determine the input message type. */ + BT_HDR *p_msg = (BT_HDR *)param; + + switch (p_msg->event & BT_EVT_MASK) { + case BTU_POST_TO_TASK_NO_GOOD_HORRIBLE_HACK: // TODO(zachoverflow): remove this + { + post_to_task_hack_t *ph = (post_to_task_hack_t *) &p_msg->data[0]; + ph->callback(p_msg); + break; + } + case BT_EVT_TO_BTU_HCI_ACL: + /* All Acl Data goes to L2CAP */ + l2c_rcv_acl_data (p_msg); + break; + + case BT_EVT_TO_BTU_L2C_SEG_XMIT: + /* L2CAP segment transmit complete */ + l2c_link_segments_xmitted (p_msg); + break; + + case BT_EVT_TO_BTU_HCI_SCO: +#if BTM_SCO_INCLUDED == TRUE + btm_route_sco_data (p_msg); + break; +#endif + + case BT_EVT_TO_BTU_HCI_EVT: + btu_hcif_process_event ((UINT8)(p_msg->event & BT_SUB_EVT_MASK), p_msg); + osi_free(p_msg); + +#if (defined(HCILP_INCLUDED) && HCILP_INCLUDED == TRUE) + /* If host receives events which it doesn't response to, */ + /* host should start idle timer to enter sleep mode. */ + btu_check_bt_sleep (); +#endif + break; + + case BT_EVT_TO_BTU_HCI_CMD: + btu_hcif_send_cmd ((UINT8)(p_msg->event & BT_SUB_EVT_MASK), p_msg); + break; + + default:; + int i = 0; + uint16_t mask = (UINT16) (p_msg->event & BT_EVT_MASK); + BOOLEAN handled = FALSE; + + for (; !handled && i < BTU_MAX_REG_EVENT; i++) { + if (btu_cb.event_reg[i].event_cb == NULL) { + continue; + } + + if (mask == btu_cb.event_reg[i].event_range) { + if (btu_cb.event_reg[i].event_cb) { + btu_cb.event_reg[i].event_cb(p_msg); + handled = TRUE; + } + } + } + + if (handled == FALSE) { + osi_free (p_msg); + } + + break; + } + +} + +#if (defined(BTA_INCLUDED) && BTA_INCLUDED == TRUE) +static void btu_bta_alarm_process(void *param) +{ + TIMER_LIST_ENT *p_tle = (TIMER_LIST_ENT *)param; + // call timer callback + if (p_tle->p_cback) { + (*p_tle->p_cback)(p_tle); + } else if (p_tle->event) { + BT_HDR *p_msg; + if ((p_msg = (BT_HDR *) osi_malloc(sizeof(BT_HDR))) != NULL) { + p_msg->event = p_tle->event; + p_msg->layer_specific = 0; + //osi_free(p_msg); + bta_sys_sendmsg(p_msg); + } + } +} +#endif + +bool btu_task_post(uint32_t sig, void *param, uint32_t timeout) +{ + bool status = false; + + switch (sig) { + case SIG_BTU_START_UP: + status = osi_thread_post(btu_thread, btu_task_start_up, param, 0, timeout); + break; + case SIG_BTU_HCI_MSG: + status = osi_thread_post(btu_thread, btu_hci_msg_process, param, 0, timeout); + break; + case SIG_BTU_HCI_ADV_RPT_MSG: +#if BLE_INCLUDED == TRUE + if (param != NULL) { + btm_ble_adv_pkt_post(param); + } + btm_ble_adv_pkt_ready(); + status = true; +#else + osi_free(param); + status = false; +#endif + break; +#if (defined(BTA_INCLUDED) && BTA_INCLUDED == TRUE) + case SIG_BTU_BTA_MSG: + status = osi_thread_post(btu_thread, bta_sys_event, param, 0, timeout); + break; + case SIG_BTU_BTA_ALARM: + status = osi_thread_post(btu_thread, btu_bta_alarm_process, param, 0, timeout); + break; +#endif + case SIG_BTU_GENERAL_ALARM: + case SIG_BTU_ONESHOT_ALARM: + status = osi_thread_post(btu_thread, btu_general_alarm_process, param, 0, timeout); + break; + case SIG_BTU_L2CAP_ALARM: + status = osi_thread_post(btu_thread, btu_l2cap_alarm_process, param, 0, timeout); + break; + default: + break; + } + + return status; +} + +void btu_task_start_up(void *param) +{ + UNUSED(param); + /* Initialize the mandatory core stack control blocks + (BTU, BTM, L2CAP, and SDP) + */ + btu_init_core(); + + /* Initialize any optional stack components */ + BTE_InitStack(); + +#if (defined(BTA_INCLUDED) && BTA_INCLUDED == TRUE) + bta_sys_init(); +#endif + + // Inform the bt jni thread initialization is ok. + // btif_transfer_context(btif_init_ok, 0, NULL, 0, NULL); +#if(defined(BT_APP_DEMO) && BT_APP_DEMO == TRUE) + if (bluedroid_init_done_cb) { + bluedroid_init_done_cb(); + } +#endif +} + +void btu_task_shut_down(void) +{ +#if (defined(BTA_INCLUDED) && BTA_INCLUDED == TRUE) + bta_sys_free(); +#endif + BTE_DeinitStack(); + + btu_free_core(); +} + +/******************************************************************************* +** +** Function btu_start_timer +** +** Description Start a timer for the specified amount of time. +** NOTE: The timeout resolution is in SECONDS! (Even +** though the timer structure field is ticks) +** +** Returns void +** +*******************************************************************************/ +static void btu_general_alarm_process(void *param) +{ + TIMER_LIST_ENT *p_tle = (TIMER_LIST_ENT *)param; + assert(p_tle != NULL); + + switch (p_tle->event) { + case BTU_TTYPE_BTM_DEV_CTL: + btm_dev_timeout(p_tle); + break; + + case BTU_TTYPE_L2CAP_LINK: + case BTU_TTYPE_L2CAP_CHNL: + case BTU_TTYPE_L2CAP_HOLD: + case BTU_TTYPE_L2CAP_INFO: + case BTU_TTYPE_L2CAP_FCR_ACK: + case BTU_TTYPE_L2CAP_UPDA_CONN_PARAMS: + l2c_process_timeout (p_tle); + break; +#if (defined(SDP_INCLUDED) && SDP_INCLUDED == TRUE) + case BTU_TTYPE_SDP: + sdp_conn_timeout ((tCONN_CB *)p_tle->param); + break; +#endif + case BTU_TTYPE_BTM_RMT_NAME: + btm_inq_rmt_name_failed(); + break; +#if (defined(RFCOMM_INCLUDED) && RFCOMM_INCLUDED == TRUE) + case BTU_TTYPE_RFCOMM_MFC: + case BTU_TTYPE_RFCOMM_PORT: + rfcomm_process_timeout (p_tle); + break; +#endif +#if ((defined(BNEP_INCLUDED) && BNEP_INCLUDED == TRUE)) + case BTU_TTYPE_BNEP: + bnep_process_timeout(p_tle); + break; +#endif + + +#if (defined(AVDT_INCLUDED) && AVDT_INCLUDED == TRUE) + case BTU_TTYPE_AVDT_SCB_DELAY_RPT: + case BTU_TTYPE_AVDT_CCB_RET: + case BTU_TTYPE_AVDT_CCB_RSP: + case BTU_TTYPE_AVDT_CCB_IDLE: + case BTU_TTYPE_AVDT_SCB_TC: + avdt_process_timeout(p_tle); + break; +#endif + +#if (defined(HID_HOST_INCLUDED) && HID_HOST_INCLUDED == TRUE) + case BTU_TTYPE_HID_HOST_REPAGE_TO : + hidh_proc_repage_timeout(p_tle); + break; +#endif + +#if (defined(BLE_INCLUDED) && BLE_INCLUDED == TRUE) + case BTU_TTYPE_BLE_INQUIRY: + case BTU_TTYPE_BLE_GAP_LIM_DISC: + case BTU_TTYPE_BLE_RANDOM_ADDR: + case BTU_TTYPE_BLE_GAP_FAST_ADV: + case BTU_TTYPE_BLE_SCAN: + case BTU_TTYPE_BLE_OBSERVE: + btm_ble_timeout(p_tle); + break; + + case BTU_TTYPE_ATT_WAIT_FOR_RSP: + gatt_rsp_timeout(p_tle); + break; + + case BTU_TTYPE_ATT_WAIT_FOR_IND_ACK: + gatt_ind_ack_timeout(p_tle); + break; + +#if (defined(SMP_INCLUDED) && SMP_INCLUDED == TRUE) + case BTU_TTYPE_SMP_PAIRING_CMD: + smp_rsp_timeout(p_tle); + break; +#endif + +#endif + +#if (MCA_INCLUDED == TRUE) + case BTU_TTYPE_MCA_CCB_RSP: + mca_process_timeout(p_tle); + break; +#endif + case BTU_TTYPE_USER_FUNC: { + tUSER_TIMEOUT_FUNC *p_uf = (tUSER_TIMEOUT_FUNC *)p_tle->param; + (*p_uf)(p_tle); + } + break; + + case BTU_TTYPE_BTM_QOS: + btm_qos_setup_timeout(p_tle); + break; + case BTU_TTYPE_BTM_SET_PAGE_TO: + btm_page_to_setup_timeout(p_tle); + break; + default: + for (int i = 0; i < BTU_MAX_REG_TIMER; i++) { + if (btu_cb.timer_reg[i].timer_cb == NULL) { + continue; + } + if (btu_cb.timer_reg[i].p_tle == p_tle) { + btu_cb.timer_reg[i].timer_cb(p_tle); + break; + } + } + break; + } +} + +void btu_general_alarm_cb(void *data) +{ + assert(data != NULL); + TIMER_LIST_ENT *p_tle = (TIMER_LIST_ENT *)data; + + btu_task_post(SIG_BTU_GENERAL_ALARM, p_tle, OSI_THREAD_MAX_TIMEOUT); +} + +void btu_start_timer(TIMER_LIST_ENT *p_tle, UINT16 type, UINT32 timeout_sec) +{ + osi_alarm_t *alarm = NULL; + + assert(p_tle != NULL); + + // Get the alarm for the timer list entry. + osi_mutex_lock(&btu_general_alarm_lock, OSI_MUTEX_MAX_TIMEOUT); + if (!hash_map_has_key(btu_general_alarm_hash_map, p_tle)) { + alarm = osi_alarm_new("btu_gen", btu_general_alarm_cb, (void *)p_tle, 0); + hash_map_set(btu_general_alarm_hash_map, p_tle, alarm); + } + osi_mutex_unlock(&btu_general_alarm_lock); + + alarm = hash_map_get(btu_general_alarm_hash_map, p_tle); + if (alarm == NULL) { + HCI_TRACE_ERROR("%s Unable to create alarm", __func__); + return; + } + osi_alarm_cancel(alarm); + + p_tle->event = type; + // NOTE: This value is in seconds but stored in a ticks field. + p_tle->ticks = timeout_sec; + p_tle->in_use = TRUE; + osi_alarm_set(alarm, (period_ms_t)(timeout_sec * 1000)); +} + + +/******************************************************************************* +** +** Function btu_stop_timer +** +** Description Stop a timer. +** +** Returns void +** +*******************************************************************************/ +void btu_stop_timer(TIMER_LIST_ENT *p_tle) +{ + assert(p_tle != NULL); + + if (p_tle->in_use == FALSE) { + return; + } + p_tle->in_use = FALSE; + + // Get the alarm for the timer list entry. + osi_alarm_t *alarm = hash_map_get(btu_general_alarm_hash_map, p_tle); + if (alarm == NULL) { + HCI_TRACE_WARNING("%s Unable to find expected alarm in hashmap", __func__); + return; + } + osi_alarm_cancel(alarm); +} + +/******************************************************************************* +** +** Function btu_free_timer +** +** Description Stop and free a timer. +** +** Returns void +** +*******************************************************************************/ +void btu_free_timer(TIMER_LIST_ENT *p_tle) +{ + assert(p_tle != NULL); + + p_tle->in_use = FALSE; + + // Get the alarm for the timer list entry. + osi_alarm_t *alarm = hash_map_get(btu_general_alarm_hash_map, p_tle); + if (alarm == NULL) { + HCI_TRACE_DEBUG("%s Unable to find expected alarm in hashmap", __func__); + return; + } + osi_alarm_cancel(alarm); + hash_map_erase(btu_general_alarm_hash_map, p_tle); +} + +#if defined(QUICK_TIMER_TICKS_PER_SEC) && (QUICK_TIMER_TICKS_PER_SEC > 0) +/******************************************************************************* +** +** Function btu_start_quick_timer +** +** Description Start a timer for the specified amount of time in ticks. +** +** Returns void +** +*******************************************************************************/ +static void btu_l2cap_alarm_process(void *param) +{ + TIMER_LIST_ENT *p_tle = (TIMER_LIST_ENT *)param; + assert(p_tle != NULL); + + switch (p_tle->event) { + case BTU_TTYPE_L2CAP_CHNL: /* monitor or retransmission timer */ + case BTU_TTYPE_L2CAP_FCR_ACK: /* ack timer */ + l2c_process_timeout (p_tle); + break; + + default: + break; + } +} + +static void btu_l2cap_alarm_cb(void *data) +{ + assert(data != NULL); + TIMER_LIST_ENT *p_tle = (TIMER_LIST_ENT *)data; + + btu_task_post(SIG_BTU_L2CAP_ALARM, p_tle, OSI_THREAD_MAX_TIMEOUT); +} + +void btu_start_quick_timer(TIMER_LIST_ENT *p_tle, UINT16 type, UINT32 timeout_ticks) +{ + osi_alarm_t *alarm = NULL; + + assert(p_tle != NULL); + + // Get the alarm for the timer list entry. + osi_mutex_lock(&btu_l2cap_alarm_lock, OSI_MUTEX_MAX_TIMEOUT); + if (!hash_map_has_key(btu_l2cap_alarm_hash_map, p_tle)) { + alarm = osi_alarm_new("btu_l2cap", btu_l2cap_alarm_cb, (void *)p_tle, 0); + hash_map_set(btu_l2cap_alarm_hash_map, p_tle, (void *)alarm); + } + osi_mutex_unlock(&btu_l2cap_alarm_lock); + + alarm = hash_map_get(btu_l2cap_alarm_hash_map, p_tle); + if (alarm == NULL) { + HCI_TRACE_ERROR("%s Unable to create alarm", __func__); + return; + } + osi_alarm_cancel(alarm); + + p_tle->event = type; + p_tle->ticks = timeout_ticks; + p_tle->in_use = TRUE; + // The quick timer ticks are 100ms long. + osi_alarm_set(alarm, (period_ms_t)(timeout_ticks * 100)); +} + +/******************************************************************************* +** +** Function btu_stop_quick_timer +** +** Description Stop a timer. +** +** Returns void +** +*******************************************************************************/ +void btu_stop_quick_timer(TIMER_LIST_ENT *p_tle) +{ + assert(p_tle != NULL); + + if (p_tle->in_use == FALSE) { + return; + } + p_tle->in_use = FALSE; + + // Get the alarm for the timer list entry. + osi_alarm_t *alarm = hash_map_get(btu_l2cap_alarm_hash_map, p_tle); + if (alarm == NULL) { + HCI_TRACE_WARNING("%s Unable to find expected alarm in hashmap", __func__); + return; + } + osi_alarm_cancel(alarm); +} + +void btu_free_quick_timer(TIMER_LIST_ENT *p_tle) +{ + assert(p_tle != NULL); + + p_tle->in_use = FALSE; + + // Get the alarm for the timer list entry. + osi_alarm_t *alarm = hash_map_get(btu_l2cap_alarm_hash_map, p_tle); + if (alarm == NULL) { + HCI_TRACE_DEBUG("%s Unable to find expected alarm in hashmap", __func__); + return; + } + osi_alarm_cancel(alarm); + hash_map_erase(btu_l2cap_alarm_hash_map, p_tle); +} + +#endif /* defined(QUICK_TIMER_TICKS_PER_SEC) && (QUICK_TIMER_TICKS_PER_SEC > 0) */ + +void btu_oneshot_alarm_cb(void *data) +{ + assert(data != NULL); + TIMER_LIST_ENT *p_tle = (TIMER_LIST_ENT *)data; + + btu_stop_timer_oneshot(p_tle); + + btu_task_post(SIG_BTU_ONESHOT_ALARM, p_tle, OSI_THREAD_MAX_TIMEOUT); +} + +/* + * Starts a oneshot timer with a timeout in seconds. + */ +void btu_start_timer_oneshot(TIMER_LIST_ENT *p_tle, UINT16 type, UINT32 timeout_sec) +{ + osi_alarm_t *alarm = NULL; + + assert(p_tle != NULL); + + // Get the alarm for the timer list entry. + osi_mutex_lock(&btu_oneshot_alarm_lock, OSI_MUTEX_MAX_TIMEOUT); + if (!hash_map_has_key(btu_oneshot_alarm_hash_map, p_tle)) { + alarm = osi_alarm_new("btu_oneshot", btu_oneshot_alarm_cb, (void *)p_tle, 0); + hash_map_set(btu_oneshot_alarm_hash_map, p_tle, alarm); + } + osi_mutex_unlock(&btu_oneshot_alarm_lock); + + alarm = hash_map_get(btu_oneshot_alarm_hash_map, p_tle); + if (alarm == NULL) { + HCI_TRACE_ERROR("%s Unable to create alarm", __func__); + return; + } + osi_alarm_cancel(alarm); + + p_tle->event = type; + p_tle->in_use = TRUE; + // NOTE: This value is in seconds but stored in a ticks field. + p_tle->ticks = timeout_sec; + osi_alarm_set(alarm, (period_ms_t)(timeout_sec * 1000)); +} + +void btu_stop_timer_oneshot(TIMER_LIST_ENT *p_tle) +{ + assert(p_tle != NULL); + + if (p_tle->in_use == FALSE) { + return; + } + p_tle->in_use = FALSE; + + // Get the alarm for the timer list entry. + osi_alarm_t *alarm = hash_map_get(btu_oneshot_alarm_hash_map, p_tle); + if (alarm == NULL) { + HCI_TRACE_WARNING("%s Unable to find expected alarm in hashmap", __func__); + return; + } + osi_alarm_cancel(alarm); +} + +#if (defined(HCILP_INCLUDED) && HCILP_INCLUDED == TRUE) +/******************************************************************************* +** +** Function btu_check_bt_sleep +** +** Description This function is called to check if controller can go to sleep. +** +** Returns void +** +*******************************************************************************/ +void btu_check_bt_sleep (void) +{ + // TODO(zachoverflow) take pending commands into account? + if (l2cb.controller_xmit_window == l2cb.num_lm_acl_bufs) { + bte_main_lpm_allow_bt_device_sleep(); + } +} +#endif diff --git a/lib/bt/host/bluedroid/stack/gap/gap_api.c b/lib/bt/host/bluedroid/stack/gap/gap_api.c new file mode 100644 index 00000000..75d5b5d1 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/gap/gap_api.c @@ -0,0 +1,109 @@ +/****************************************************************************** + * + * Copyright (C) 2009-2013 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +#include + +#include "common/bt_target.h" +//#include "bt_utils.h" +#include "gap_int.h" +#include "osi/allocator.h" + +#if GAP_DYNAMIC_MEMORY == FALSE +tGAP_CB gap_cb; +#else +tGAP_CB *gap_cb_ptr; +#endif + +/******************************************************************************* +** +** Function GAP_SetTraceLevel +** +** Description This function sets the trace level for GAP. If called with +** a value of 0xFF, it simply returns the current trace level. +** +** Returns The new or current trace level +** +*******************************************************************************/ +UINT8 GAP_SetTraceLevel (UINT8 new_level) +{ + if (new_level != 0xFF) { + gap_cb.trace_level = new_level; + } + + return (gap_cb.trace_level); +} + +/******************************************************************************* +** +** Function GAP_Init +** +** Description Initializes the control blocks used by GAP. +** +** This routine should not be called except once per +** stack invocation. +** +** Returns status +** +*******************************************************************************/ +bt_status_t GAP_Init(void) +{ +#if GAP_DYNAMIC_MEMORY == TRUE + gap_cb_ptr = (tGAP_CB *)osi_malloc(sizeof(tGAP_CB)); + if (!gap_cb_ptr) { + return BT_STATUS_NOMEM; + } +#endif + + memset (&gap_cb, 0, sizeof (tGAP_CB)); + +#if defined(GAP_INITIAL_TRACE_LEVEL) + gap_cb.trace_level = GAP_INITIAL_TRACE_LEVEL; +#else + gap_cb.trace_level = BT_TRACE_LEVEL_NONE; /* No traces */ +#endif + +#if GAP_CONN_INCLUDED == TRUE + gap_conn_init(); +#endif + +#if BLE_INCLUDED == TRUE && GATTS_INCLUDED == TRUE + gap_attr_db_init(); +#endif + + return BT_STATUS_SUCCESS; +} + +/******************************************************************************* +** +** Function GAP_Deinit +** +** Description This function is called to deinitialize the control block +** for this layer. +** +** Returns void +** +*******************************************************************************/ +void GAP_Deinit(void) +{ +#if GAP_DYNAMIC_MEMORY == TRUE + if (gap_cb_ptr) { + osi_free(gap_cb_ptr); + gap_cb_ptr = NULL; + } +#endif +} diff --git a/lib/bt/host/bluedroid/stack/gap/gap_ble.c b/lib/bt/host/bluedroid/stack/gap/gap_ble.c new file mode 100644 index 00000000..5bac86f0 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/gap/gap_ble.c @@ -0,0 +1,823 @@ +/****************************************************************************** + * + * Copyright (C) 2009-2013 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ +#include "common/bt_target.h" + +#if (defined BLE_INCLUDED && BLE_INCLUDED == TRUE && GATTS_INCLUDED == TRUE) + +#include "common/bt_defs.h" +#include "osi/allocator.h" +#include +#include "gap_int.h" +#include "stack/gap_api.h" +#include "stack/gattdefs.h" +#include "stack/gatt_api.h" +#include "gatt_int.h" +#include "btm_int.h" +#include "stack/hcimsgs.h" +#include "stack/sdpdefs.h" + +#define GAP_CHAR_ICON_SIZE 2 +#define GAP_CHAR_DEV_NAME_SIZE 248 +#define GAP_MAX_NUM_INC_SVR 0 +#define GAP_MAX_ATTR_NUM (2 * GAP_MAX_CHAR_NUM + GAP_MAX_NUM_INC_SVR + 1) +#define GAP_MAX_CHAR_VALUE_SIZE (30 + GAP_CHAR_DEV_NAME_SIZE) + + +#ifndef GAP_ATTR_DB_SIZE +#define GAP_ATTR_DB_SIZE GATT_DB_MEM_SIZE(GAP_MAX_NUM_INC_SVR, GAP_MAX_CHAR_NUM, GAP_MAX_CHAR_VALUE_SIZE) +#endif + +static void gap_ble_s_attr_request_cback (UINT16 conn_id, UINT32 trans_id, tGATTS_REQ_TYPE op_code, tGATTS_DATA *p_data); + +/* client connection callback */ +static void gap_ble_c_connect_cback (tGATT_IF gatt_if, BD_ADDR bda, UINT16 conn_id, BOOLEAN connected, + tGATT_DISCONN_REASON reason, tGATT_TRANSPORT transport); +static void gap_ble_c_cmpl_cback (UINT16 conn_id, tGATTC_OPTYPE op, tGATT_STATUS status, tGATT_CL_COMPLETE *p_data); + +static const tGATT_CBACK gap_cback = { + gap_ble_c_connect_cback, + gap_ble_c_cmpl_cback, + NULL, + NULL, + gap_ble_s_attr_request_cback, + NULL, + NULL +}; + + + +/******************************************************************************* +** +** Function gap_find_clcb_by_bd_addr +** +** Description The function searches all LCB with macthing bd address +** +** Returns total number of clcb found. +** +*******************************************************************************/ +tGAP_CLCB *gap_find_clcb_by_bd_addr(BD_ADDR bda) +{ + UINT8 i_clcb; + tGAP_CLCB *p_clcb = NULL; + + for (i_clcb = 0, p_clcb = gap_cb.clcb; i_clcb < GAP_MAX_CL; i_clcb++, p_clcb++) { + if (p_clcb->in_use && !memcmp(p_clcb->bda, bda, BD_ADDR_LEN)) { + return p_clcb; + } + } + + return NULL; +} + +/******************************************************************************* +** +** Function gap_ble_find_clcb_by_conn_id +** +** Description The function searches all LCB with macthing connection ID +** +** Returns total number of clcb found. +** +*******************************************************************************/ +tGAP_CLCB *gap_ble_find_clcb_by_conn_id(UINT16 conn_id) +{ + UINT8 i_clcb; + tGAP_CLCB *p_clcb = NULL; + + for (i_clcb = 0, p_clcb = gap_cb.clcb; i_clcb < GAP_MAX_CL; i_clcb++, p_clcb++) { + if (p_clcb->in_use && p_clcb->connected && p_clcb->conn_id == conn_id) { + return p_clcb; + } + } + + return p_clcb; +} + +/******************************************************************************* +** +** Function gap_clcb_alloc +** +** Description The function allocates a GAP connection link control block +** +** Returns NULL if not found. Otherwise pointer to the connection link block. +** +*******************************************************************************/ +tGAP_CLCB *gap_clcb_alloc (BD_ADDR bda) +{ + UINT8 i_clcb = 0; + tGAP_CLCB *p_clcb = NULL; + + for (i_clcb = 0, p_clcb = gap_cb.clcb; i_clcb < GAP_MAX_CL; i_clcb++, p_clcb++) { + if (!p_clcb->in_use) { + memset(p_clcb, 0, sizeof(tGAP_CLCB)); + p_clcb->in_use = TRUE; + memcpy (p_clcb->bda, bda, BD_ADDR_LEN); + break; + } + } + return p_clcb; +} + +/******************************************************************************* +** +** Function gap_ble_dealloc_clcb +** +** Description The function clean up the pending request queue in GAP +** +** Returns none +** +*******************************************************************************/ +void gap_ble_dealloc_clcb(tGAP_CLCB *p_clcb) +{ + tGAP_BLE_REQ *p_q; + + while ((p_q = (tGAP_BLE_REQ *)fixed_queue_dequeue(p_clcb->pending_req_q, 0)) != NULL) { + /* send callback to all pending requests if being removed*/ + if (p_q->p_cback != NULL) { + (*p_q->p_cback)(FALSE, p_clcb->bda, 0, NULL); + } + + osi_free (p_q); + } + + memset(p_clcb, 0, sizeof(tGAP_CLCB)); +} + +/******************************************************************************* +** +** Function gap_ble_enqueue_request +** +** Description The function enqueue a GAP client request +** +** Returns TRUE is successul; FALSE otherwise +** +*******************************************************************************/ +BOOLEAN gap_ble_enqueue_request (tGAP_CLCB *p_clcb, UINT16 uuid, tGAP_BLE_CMPL_CBACK *p_cback) +{ + tGAP_BLE_REQ *p_q = (tGAP_BLE_REQ *)osi_malloc(sizeof(tGAP_BLE_REQ)); + + if (p_q != NULL) { + p_q->p_cback = p_cback; + p_q->uuid = uuid; + fixed_queue_enqueue(p_clcb->pending_req_q, p_q, FIXED_QUEUE_MAX_TIMEOUT); + return TRUE; + } + + return FALSE; +} +/******************************************************************************* +** +** Function gap_ble_dequeue_request +** +** Description The function dequeue a GAP client request if any +** +** Returns TRUE is successul; FALSE otherwise +** +*******************************************************************************/ +BOOLEAN gap_ble_dequeue_request (tGAP_CLCB *p_clcb, UINT16 *p_uuid, tGAP_BLE_CMPL_CBACK **p_cback) +{ + tGAP_BLE_REQ *p_q = (tGAP_BLE_REQ *)fixed_queue_dequeue(p_clcb->pending_req_q, 0);; + + if (p_q != NULL) { + *p_cback = p_q->p_cback; + *p_uuid = p_q->uuid; + osi_free((void *)p_q); + return TRUE; + } + + return FALSE; +} + +/******************************************************************************* +** GAP Attributes Database Request callback +*******************************************************************************/ +tGATT_STATUS gap_read_attr_value (UINT16 handle, tGATT_VALUE *p_value, BOOLEAN is_long) +{ + tGAP_ATTR *p_db_attr = gap_cb.gap_attr; + UINT8 *p = p_value->value, i; + UINT16 offset = p_value->offset; + UINT8 *p_dev_name = NULL; + + for (i = 0; i < GAP_MAX_CHAR_NUM; i ++, p_db_attr ++) { + if (handle == p_db_attr->handle) { + if (p_db_attr->uuid != GATT_UUID_GAP_DEVICE_NAME && + is_long == TRUE) { + return GATT_NOT_LONG; + } + + switch (p_db_attr->uuid) { + case GATT_UUID_GAP_DEVICE_NAME: + BTM_ReadLocalDeviceName((char **)&p_dev_name); + if (strlen ((char *)p_dev_name) > GATT_MAX_ATTR_LEN) { + p_value->len = GATT_MAX_ATTR_LEN; + } else { + p_value->len = (UINT16)strlen ((char *)p_dev_name); + } + + if (offset > p_value->len) { + return GATT_INVALID_OFFSET; + } else { + p_value->len -= offset; + p_dev_name += offset; + ARRAY_TO_STREAM(p, p_dev_name, p_value->len); + GAP_TRACE_EVENT("GATT_UUID_GAP_DEVICE_NAME len=0x%04x", p_value->len); + } + break; + + case GATT_UUID_GAP_ICON: + UINT16_TO_STREAM(p, p_db_attr->attr_value.icon); + p_value->len = 2; + break; + + case GATT_UUID_GAP_PREF_CONN_PARAM: + UINT16_TO_STREAM(p, p_db_attr->attr_value.conn_param.int_min); /* int_min */ + UINT16_TO_STREAM(p, p_db_attr->attr_value.conn_param.int_max); /* int_max */ + UINT16_TO_STREAM(p, p_db_attr->attr_value.conn_param.latency); /* latency */ + UINT16_TO_STREAM(p, p_db_attr->attr_value.conn_param.sp_tout); /* sp_tout */ + p_value->len = 8; + break; + + /* address resolution */ + case GATT_UUID_GAP_CENTRAL_ADDR_RESOL: + UINT8_TO_STREAM(p, p_db_attr->attr_value.addr_resolution); + p_value->len = 1; + break; + } + return GATT_SUCCESS; + } + } + return GATT_NOT_FOUND; +} + +/******************************************************************************* +** GAP Attributes Database Read/Read Blob Request process +*******************************************************************************/ +tGATT_STATUS gap_proc_read (tGATTS_REQ_TYPE type, tGATT_READ_REQ *p_data, tGATTS_RSP *p_rsp) +{ + tGATT_STATUS status = GATT_NO_RESOURCES; + UNUSED(type); + + if (p_data->is_long) { + p_rsp->attr_value.offset = p_data->offset; + } + + p_rsp->attr_value.handle = p_data->handle; + + status = gap_read_attr_value(p_data->handle, &p_rsp->attr_value, p_data->is_long); + + return status; +} + +/****************************************************************************** +** +** Function gap_proc_write_req +** +** Description GAP ATT server process a write request. +** +** Returns void. +** +*******************************************************************************/ +UINT8 gap_proc_write_req( tGATTS_REQ_TYPE type, tGATT_WRITE_REQ *p_data) +{ + tGAP_ATTR *p_db_attr = gap_cb.gap_attr; + UINT8 i; + UNUSED(type); + + for (i = 0; i < GAP_MAX_CHAR_NUM; i ++, p_db_attr ++) { + if (p_data->handle == p_db_attr->handle) { + switch (p_db_attr->uuid) { + #if (GATTS_DEVICE_NAME_WRITABLE == TRUE) + case GATT_UUID_GAP_DEVICE_NAME: { + UINT8 *p_val = p_data->value; + p_val[p_data->len] = '\0'; + BTM_SetLocalDeviceName((char *)p_val); + return GATT_SUCCESS; + } + #endif + #if (GATTS_APPEARANCE_WRITABLE == TRUE) + case GATT_UUID_GAP_ICON: { + UINT8 *p_val = p_data->value; + if (p_data->len != sizeof(UINT16)) { + return GATT_INVALID_ATTR_LEN; + } + STREAM_TO_UINT16(p_db_attr->attr_value.icon, p_val); + return GATT_SUCCESS; + } + #endif + default: + break; + } + return GATT_WRITE_NOT_PERMIT; + } + } + + return GATT_NOT_FOUND; +} + +/****************************************************************************** +** +** Function gap_ble_s_attr_request_cback +** +** Description GAP ATT server attribute access request callback. +** +** Returns void. +** +*******************************************************************************/ +void gap_ble_s_attr_request_cback (UINT16 conn_id, UINT32 trans_id, + tGATTS_REQ_TYPE type, tGATTS_DATA *p_data) +{ + UINT8 status = GATT_INVALID_PDU; + tGATTS_RSP rsp_msg; + BOOLEAN ignore = FALSE; + + GAP_TRACE_EVENT("gap_ble_s_attr_request_cback : recv type (0x%02x)", type); + + memset(&rsp_msg, 0, sizeof(tGATTS_RSP)); + + switch (type) { + case GATTS_REQ_TYPE_READ: + status = gap_proc_read(type, &p_data->read_req, &rsp_msg); + break; + + case GATTS_REQ_TYPE_WRITE: + if (!p_data->write_req.need_rsp) { + ignore = TRUE; + } + + status = gap_proc_write_req(type, &p_data->write_req); + break; + + case GATTS_REQ_TYPE_WRITE_EXEC: + ignore = TRUE; + GAP_TRACE_EVENT("Ignore GATTS_REQ_TYPE_WRITE_EXEC" ); + break; + + case GATTS_REQ_TYPE_MTU: + GAP_TRACE_EVENT("Get MTU exchange new mtu size: %d", p_data->mtu); + ignore = TRUE; + break; + + default: + GAP_TRACE_EVENT("Unknown/unexpected LE GAP ATT request: 0x%02x", type); + break; + } + + if (!ignore) { + GATTS_SendRsp (conn_id, trans_id, status, &rsp_msg); + } +} + +/******************************************************************************* +** +** Function btm_ble_att_db_init +** +** Description GAP ATT database initalization. +** +** Returns void. +** +*******************************************************************************/ +void gap_attr_db_init(void) +{ + tBT_UUID app_uuid = {LEN_UUID_128, {0}}; + tBT_UUID uuid = {LEN_UUID_16, {UUID_SERVCLASS_GAP_SERVER}}; + UINT16 service_handle; + tGAP_ATTR *p_db_attr = &gap_cb.gap_attr[0]; + tGATT_STATUS status; + + /* Fill our internal UUID with a fixed pattern 0x82 */ + memset (&app_uuid.uu.uuid128, 0x82, LEN_UUID_128); + memset(gap_cb.gap_attr, 0, sizeof(tGAP_ATTR) *GAP_MAX_CHAR_NUM); + + gap_cb.gatt_if = GATT_Register(&app_uuid, &gap_cback); + + GATT_StartIf(gap_cb.gatt_if); + + /* Create a GAP service */ + service_handle = GATTS_CreateService (gap_cb.gatt_if, &uuid, 0, GAP_MAX_ATTR_NUM, TRUE); + + GAP_TRACE_EVENT ("gap_attr_db_init service_handle = %d", service_handle); + + /* add Device Name Characteristic + */ + uuid.len = LEN_UUID_16; + uuid.uu.uuid16 = p_db_attr->uuid = GATT_UUID_GAP_DEVICE_NAME; + p_db_attr->handle = GATTS_AddCharacteristic(service_handle, &uuid, + #if (GATTS_DEVICE_NAME_WRITABLE == TRUE) + GATT_PERM_READ | GATT_PERM_WRITE, + GATT_CHAR_PROP_BIT_READ | GATT_CHAR_PROP_BIT_WRITE_NR, + #else + GATT_PERM_READ, GATT_CHAR_PROP_BIT_READ, + #endif + NULL, NULL); + p_db_attr ++; + + /* add Icon characteristic + */ + uuid.uu.uuid16 = p_db_attr->uuid = GATT_UUID_GAP_ICON; + p_db_attr->handle = GATTS_AddCharacteristic(service_handle, &uuid, + #if (GATTS_APPEARANCE_WRITABLE == TRUE) + GATT_PERM_READ | GATT_PERM_WRITE, + GATT_CHAR_PROP_BIT_READ | GATT_CHAR_PROP_BIT_WRITE_NR, + #else + GATT_PERM_READ, GATT_CHAR_PROP_BIT_READ, + #endif + NULL, NULL); + p_db_attr ++; + +#if ((defined BTM_PERIPHERAL_ENABLED) && (BTM_PERIPHERAL_ENABLED == TRUE)) + /* Only needed for peripheral testing */ + /* add preferred connection parameter characteristic + */ + uuid.uu.uuid16 = p_db_attr->uuid = GATT_UUID_GAP_PREF_CONN_PARAM; + p_db_attr->attr_value.conn_param.int_max = GAP_PREFER_CONN_INT_MAX; /* 6 */ + p_db_attr->attr_value.conn_param.int_min = GAP_PREFER_CONN_INT_MIN; /* 0 */ + p_db_attr->attr_value.conn_param.latency = GAP_PREFER_CONN_LATENCY; /* 0 */ + p_db_attr->attr_value.conn_param.sp_tout = GAP_PREFER_CONN_SP_TOUT; /* 2000 */ + p_db_attr->handle = GATTS_AddCharacteristic(service_handle, + &uuid, + GATT_PERM_READ, + GATT_CHAR_PROP_BIT_READ, + NULL, NULL); + p_db_attr ++; +#endif + + /* add Central address resolution Characteristic */ + uuid.len = LEN_UUID_16; + uuid.uu.uuid16 = p_db_attr->uuid = GATT_UUID_GAP_CENTRAL_ADDR_RESOL; + p_db_attr->handle = GATTS_AddCharacteristic(service_handle, &uuid, + GATT_PERM_READ, GATT_CHAR_PROP_BIT_READ, + NULL, NULL); + p_db_attr->attr_value.addr_resolution = 0; + p_db_attr++; + + /* start service now */ + memset (&app_uuid.uu.uuid128, 0x81, LEN_UUID_128); + + status = GATTS_StartService(gap_cb.gatt_if, service_handle, GAP_TRANSPORT_SUPPORTED ); +#if (CONFIG_BT_STACK_NO_LOG) + (void) status; +#endif + + GAP_TRACE_EVENT ("GAP App gatt_if: %d s_hdl = %d start_status=%d", + gap_cb.gatt_if, service_handle, status); + + + +} + +/******************************************************************************* +** +** Function GAP_BleAttrDBUpdate +** +** Description GAP ATT database update. +** +** Returns void. +** +*******************************************************************************/ +void GAP_BleAttrDBUpdate(UINT16 attr_uuid, tGAP_BLE_ATTR_VALUE *p_value) +{ + tGAP_ATTR *p_db_attr = gap_cb.gap_attr; + UINT8 i = 0; + + GAP_TRACE_EVENT("GAP_BleAttrDBUpdate attr_uuid=0x%04x\n", attr_uuid); + + for (i = 0; i < GAP_MAX_CHAR_NUM; i ++, p_db_attr ++) { + if (p_db_attr->uuid == attr_uuid) { + GAP_TRACE_EVENT("Found attr_uuid=0x%04x\n", attr_uuid); + + switch (attr_uuid) { + case GATT_UUID_GAP_ICON: + p_db_attr->attr_value.icon = p_value->icon; + break; + + case GATT_UUID_GAP_PREF_CONN_PARAM: + memcpy((void *)&p_db_attr->attr_value.conn_param, + (const void *)&p_value->conn_param, sizeof(tGAP_BLE_PREF_PARAM)); + break; + + case GATT_UUID_GAP_DEVICE_NAME: + BTM_SetLocalDeviceName((char *)p_value->p_dev_name); + break; + + case GATT_UUID_GAP_CENTRAL_ADDR_RESOL: + p_db_attr->attr_value.addr_resolution = p_value->addr_resolution; + break; + + } + break; + } + } + + return; +} + +/******************************************************************************* +** +** Function gap_ble_send_cl_read_request +** +** Description utility function to send a read request for a GAP charactersitic +** +** Returns TRUE if read started, else FALSE if GAP is busy +** +*******************************************************************************/ +BOOLEAN gap_ble_send_cl_read_request(tGAP_CLCB *p_clcb) +{ + tGATT_READ_PARAM param; + UINT16 uuid = 0; + BOOLEAN started = FALSE; + + if (gap_ble_dequeue_request(p_clcb, &uuid, &p_clcb->p_cback)) { + memset(¶m, 0, sizeof(tGATT_READ_PARAM)); + + param.service.uuid.len = LEN_UUID_16; + param.service.uuid.uu.uuid16 = uuid; + param.service.s_handle = 1; + param.service.e_handle = 0xFFFF; + param.service.auth_req = 0; +#if (GATTC_INCLUDED == TRUE) + if (GATTC_Read(p_clcb->conn_id, GATT_READ_BY_TYPE, ¶m) == GATT_SUCCESS) { + p_clcb->cl_op_uuid = uuid; + started = TRUE; + } +#endif ///GATTC_INCLUDED == TRUE + } + + return started; +} + +/******************************************************************************* +** +** Function gap_ble_cl_op_cmpl +** +** Description GAP client operation complete callback +** +** Returns void +** +*******************************************************************************/ +void gap_ble_cl_op_cmpl(tGAP_CLCB *p_clcb, BOOLEAN status, UINT16 len, UINT8 *p_name) +{ + tGAP_BLE_CMPL_CBACK *p_cback = p_clcb->p_cback; + UINT16 op = p_clcb->cl_op_uuid; + + GAP_TRACE_EVENT("gap_ble_cl_op_cmpl status: %d", status); + + p_clcb->cl_op_uuid = 0; + p_clcb->p_cback = NULL; + + if (p_cback && op) { + GAP_TRACE_EVENT("calling gap_ble_cl_op_cmpl"); + (* p_cback)(status, p_clcb->bda, len, (char *)p_name); + } + + /* if no further activity is requested in callback, drop the link */ + if (p_clcb->connected) { + if (!gap_ble_send_cl_read_request(p_clcb)) { + GATT_Disconnect(p_clcb->conn_id); + gap_ble_dealloc_clcb(p_clcb); + } + } +} + +/******************************************************************************* +** +** Function gap_ble_c_connect_cback +** +** Description Client connection callback. +** +** Returns void +** +*******************************************************************************/ +static void gap_ble_c_connect_cback (tGATT_IF gatt_if, BD_ADDR bda, UINT16 conn_id, + BOOLEAN connected, tGATT_DISCONN_REASON reason, + tGATT_TRANSPORT transport) +{ + tGAP_CLCB *p_clcb = gap_find_clcb_by_bd_addr (bda); + + UNUSED(gatt_if); + UNUSED(transport); + + if (p_clcb != NULL) { + if (connected) { + p_clcb->conn_id = conn_id; + p_clcb->connected = TRUE; + /* start operation is pending */ + gap_ble_send_cl_read_request(p_clcb); + } else { + p_clcb->connected = FALSE; + gap_ble_cl_op_cmpl(p_clcb, FALSE, 0, NULL); + /* clean up clcb */ + gap_ble_dealloc_clcb(p_clcb); + } + } +} + +/******************************************************************************* +** +** Function gap_ble_c_cmpl_cback +** +** Description Client operation complete callback. +** +** Returns void +** +*******************************************************************************/ +static void gap_ble_c_cmpl_cback (UINT16 conn_id, tGATTC_OPTYPE op, tGATT_STATUS status, tGATT_CL_COMPLETE *p_data) + +{ + tGAP_CLCB *p_clcb = gap_ble_find_clcb_by_conn_id(conn_id); + UINT16 op_type; + UINT16 min, max, latency, tout; + UINT16 len; + UINT8 *pp; + + if (p_clcb == NULL) { + return; + } + + op_type = p_clcb->cl_op_uuid; + + GAP_TRACE_EVENT ("gap_ble_c_cmpl_cback() - op_code: 0x%02x status: 0x%02x read_type: 0x%04x\n", op, status, op_type); + /* Currently we only issue read commands */ + if (op != GATTC_OPTYPE_READ) { + return; + } + + if (status != GATT_SUCCESS) { + gap_ble_cl_op_cmpl(p_clcb, FALSE, 0, NULL); + return; + } + + pp = p_data->att_value.value; + + switch (op_type) { + case GATT_UUID_GAP_PREF_CONN_PARAM: + GAP_TRACE_EVENT ("GATT_UUID_GAP_PREF_CONN_PARAM"); + /* Extract the peripheral preferred connection parameters and save them */ + + STREAM_TO_UINT16 (min, pp); + STREAM_TO_UINT16 (max, pp); + STREAM_TO_UINT16 (latency, pp); + STREAM_TO_UINT16 (tout, pp); + + BTM_BleSetPrefConnParams (p_clcb->bda, min, max, latency, tout); + /* release the connection here */ + gap_ble_cl_op_cmpl(p_clcb, TRUE, 0, NULL); + break; + + case GATT_UUID_GAP_DEVICE_NAME: + GAP_TRACE_EVENT ("GATT_UUID_GAP_DEVICE_NAME\n"); + len = (UINT16)strlen((char *)pp); + if (len > GAP_CHAR_DEV_NAME_SIZE) { + len = GAP_CHAR_DEV_NAME_SIZE; + } + gap_ble_cl_op_cmpl(p_clcb, TRUE, len, pp); + break; + + case GATT_UUID_GAP_CENTRAL_ADDR_RESOL: + gap_ble_cl_op_cmpl(p_clcb, TRUE, 1, pp); + break; + } +} + + +/******************************************************************************* +** +** Function gap_ble_accept_cl_operation +** +** Description Start a process to read peer address resolution capability +** +** Returns TRUE if request accepted +** +*******************************************************************************/ +BOOLEAN gap_ble_accept_cl_operation(BD_ADDR peer_bda, UINT16 uuid, tGAP_BLE_CMPL_CBACK *p_cback) +{ + tGAP_CLCB *p_clcb; + BOOLEAN started = FALSE; + + if (p_cback == NULL && uuid != GATT_UUID_GAP_PREF_CONN_PARAM) { + return (started); + } + + if ((p_clcb = gap_find_clcb_by_bd_addr (peer_bda)) == NULL) { + if ((p_clcb = gap_clcb_alloc(peer_bda)) == NULL) { + GAP_TRACE_ERROR("gap_ble_accept_cl_operation max connection reached"); + return started; + } + } + + GAP_TRACE_EVENT ("%s() - BDA: %08x%04x cl_op_uuid: 0x%04x", + __FUNCTION__, + (peer_bda[0] << 24) + (peer_bda[1] << 16) + (peer_bda[2] << 8) + peer_bda[3], + (peer_bda[4] << 8) + peer_bda[5], uuid); + + if (GATT_GetConnIdIfConnected(gap_cb.gatt_if, peer_bda, &p_clcb->conn_id, BT_TRANSPORT_LE)) { + p_clcb->connected = TRUE; + } + + /* hold the link here */ + if (!GATT_Connect(gap_cb.gatt_if, p_clcb->bda, BLE_ADDR_UNKNOWN_TYPE, TRUE, BT_TRANSPORT_LE, FALSE)) { + return started; + } + + /* enqueue the request */ + gap_ble_enqueue_request(p_clcb, uuid, p_cback); + + if (p_clcb->connected && p_clcb->cl_op_uuid == 0) { + started = gap_ble_send_cl_read_request(p_clcb); + } else { /* wait for connection up or pending operation to finish */ + started = TRUE; + } + + return started; +} +/******************************************************************************* +** +** Function GAP_BleReadPeerPrefConnParams +** +** Description Start a process to read a connected peripheral's preferred +** connection parameters +** +** Returns TRUE if read started, else FALSE if GAP is busy +** +*******************************************************************************/ +BOOLEAN GAP_BleReadPeerPrefConnParams (BD_ADDR peer_bda) +{ + return gap_ble_accept_cl_operation (peer_bda, GATT_UUID_GAP_PREF_CONN_PARAM, NULL); +} + +/******************************************************************************* +** +** Function GAP_BleReadPeerDevName +** +** Description Start a process to read a connected peripheral's device name. +** +** Returns TRUE if request accepted +** +*******************************************************************************/ +BOOLEAN GAP_BleReadPeerDevName (BD_ADDR peer_bda, tGAP_BLE_CMPL_CBACK *p_cback) +{ + return gap_ble_accept_cl_operation (peer_bda, GATT_UUID_GAP_DEVICE_NAME, p_cback); +} + +/******************************************************************************* +** +** Function GAP_BleReadPeerAddressResolutionCap +** +** Description Start a process to read peer address resolution capability +** +** Returns TRUE if request accepted +** +*******************************************************************************/ +BOOLEAN GAP_BleReadPeerAddressResolutionCap (BD_ADDR peer_bda, tGAP_BLE_CMPL_CBACK *p_cback) +{ + return gap_ble_accept_cl_operation(peer_bda, GATT_UUID_GAP_CENTRAL_ADDR_RESOL, p_cback); +} + +/******************************************************************************* +** +** Function GAP_BleCancelReadPeerDevName +** +** Description Cancel reading a peripheral's device name. +** +** Returns TRUE if request accepted +** +*******************************************************************************/ +BOOLEAN GAP_BleCancelReadPeerDevName (BD_ADDR peer_bda) +{ + tGAP_CLCB *p_clcb = gap_find_clcb_by_bd_addr (peer_bda); + + GAP_TRACE_EVENT ("GAP_BleCancelReadPeerDevName() - BDA: %08x%04x cl_op_uuid: 0x%04x", + (peer_bda[0] << 24) + (peer_bda[1] << 16) + (peer_bda[2] << 8) + peer_bda[3], + (peer_bda[4] << 8) + peer_bda[5], (p_clcb == NULL) ? 0 : p_clcb->cl_op_uuid); + + if (p_clcb == NULL) { + GAP_TRACE_ERROR ("Cannot cancel current op is not get dev name"); + return FALSE; + } + + if (!p_clcb->connected) { + if (!GATT_CancelConnect(gap_cb.gatt_if, peer_bda, TRUE)) { + GAP_TRACE_ERROR ("Cannot cancel where No connection id"); + return FALSE; + } + } + + gap_ble_cl_op_cmpl(p_clcb, FALSE, 0, NULL); + + return (TRUE); +} + +#endif /* BLE_INCLUDED == TRUE && GATTS_INCLUDED == TRUE*/ diff --git a/lib/bt/host/bluedroid/stack/gap/gap_conn.c b/lib/bt/host/bluedroid/stack/gap/gap_conn.c new file mode 100644 index 00000000..db9065de --- /dev/null +++ b/lib/bt/host/bluedroid/stack/gap/gap_conn.c @@ -0,0 +1,1211 @@ +/****************************************************************************** + * + * Copyright (C) 2009-2013 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + + +#include "common/bt_target.h" +#include "common/bt_defs.h" +#include "stack/btu.h" +#include "gap_int.h" +#include "stack/l2cdefs.h" +#include "l2c_int.h" +#include +#include "osi/mutex.h" +#include "osi/allocator.h" + +#if GAP_CONN_INCLUDED == TRUE +#include "btm_int.h" + +/********************************************************************************/ +/* L O C A L F U N C T I O N P R O T O T Y P E S */ +/********************************************************************************/ +static void gap_connect_ind (BD_ADDR bd_addr, UINT16 l2cap_cid, UINT16 psm, UINT8 l2cap_id); +static void gap_connect_cfm (UINT16 l2cap_cid, UINT16 result); +static void gap_config_ind (UINT16 l2cap_cid, tL2CAP_CFG_INFO *p_cfg); +static void gap_config_cfm (UINT16 l2cap_cid, tL2CAP_CFG_INFO *p_cfg); +static void gap_disconnect_ind (UINT16 l2cap_cid, BOOLEAN ack_needed); +static void gap_data_ind (UINT16 l2cap_cid, BT_HDR *p_msg); +static void gap_congestion_ind (UINT16 lcid, BOOLEAN is_congested); + +static tGAP_CCB *gap_find_ccb_by_cid (UINT16 cid); +static tGAP_CCB *gap_find_ccb_by_handle (UINT16 handle); +static tGAP_CCB *gap_allocate_ccb (void); +static void gap_release_ccb (tGAP_CCB *p_ccb); + +/******************************************************************************* +** +** Function gap_conn_init +** +** Description This function is called to initialize GAP connection management +** +** Returns void +** +*******************************************************************************/ +void gap_conn_init (void) +{ +#if ((defined AMP_INCLUDED) && (AMP_INCLUDED == TRUE)) + gap_cb.conn.reg_info.pAMP_ConnectInd_Cb = gap_connect_ind; + gap_cb.conn.reg_info.pAMP_ConnectCfm_Cb = gap_connect_cfm; + gap_cb.conn.reg_info.pAMP_ConnectPnd_Cb = NULL; + gap_cb.conn.reg_info.pAMP_ConfigInd_Cb = gap_config_ind; + gap_cb.conn.reg_info.pAMP_ConfigCfm_Cb = gap_config_cfm; + gap_cb.conn.reg_info.pAMP_DisconnectInd_Cb = gap_disconnect_ind; + gap_cb.conn.reg_info.pAMP_DisconnectCfm_Cb = NULL; + gap_cb.conn.reg_info.pAMP_QoSViolationInd_Cb = NULL; + gap_cb.conn.reg_info.pAMP_DataInd_Cb = gap_data_ind; + gap_cb.conn.reg_info.pAMP_CongestionStatus_Cb = gap_congestion_ind; + gap_cb.conn.reg_info.pAMP_TxComplete_Cb = NULL; + gap_cb.conn.reg_info.pAMP_MoveInd_Cb = NULL; + gap_cb.conn.reg_info.pAMP_MoveRsp_Cb = NULL; + gap_cb.conn.reg_info.pAMP_MoveCfm_Cb = NULL; //gap_move_cfm + gap_cb.conn.reg_info.pAMP_MoveCfmRsp_Cb = NULL; //gap_move_cfm_rsp + +#else + gap_cb.conn.reg_info.pL2CA_ConnectInd_Cb = gap_connect_ind; + gap_cb.conn.reg_info.pL2CA_ConnectCfm_Cb = gap_connect_cfm; + gap_cb.conn.reg_info.pL2CA_ConnectPnd_Cb = NULL; + gap_cb.conn.reg_info.pL2CA_ConfigInd_Cb = gap_config_ind; + gap_cb.conn.reg_info.pL2CA_ConfigCfm_Cb = gap_config_cfm; + gap_cb.conn.reg_info.pL2CA_DisconnectInd_Cb = gap_disconnect_ind; + gap_cb.conn.reg_info.pL2CA_DisconnectCfm_Cb = NULL; + gap_cb.conn.reg_info.pL2CA_QoSViolationInd_Cb = NULL; + gap_cb.conn.reg_info.pL2CA_DataInd_Cb = gap_data_ind; + gap_cb.conn.reg_info.pL2CA_CongestionStatus_Cb = gap_congestion_ind; + gap_cb.conn.reg_info.pL2CA_TxComplete_Cb = NULL; +#endif +} + + +/******************************************************************************* +** +** Function GAP_ConnOpen +** +** Description This function is called to open an L2CAP connection. +** +** Parameters: is_server - If TRUE, the connection is not created +** but put into a "listen" mode waiting for +** the remote side to connect. +** +** service_id - Unique service ID from +** BTM_SEC_SERVICE_FIRST_EMPTY (6) +** to BTM_SEC_MAX_SERVICE_RECORDS (32) +** +** p_rem_bda - Pointer to remote BD Address. +** If a server, and we don't care about the +** remote BD Address, then NULL should be passed. +** +** psm - the PSM used for the connection +** +** p_config - Optional pointer to configuration structure. +** If NULL, the default GAP configuration will +** be used. +** +** security - security flags +** chan_mode_mask - (GAP_FCR_CHAN_OPT_BASIC, GAP_FCR_CHAN_OPT_ERTM, +** GAP_FCR_CHAN_OPT_STREAM) +** +** p_cb - Pointer to callback function for events. +** +** Returns handle of the connection if successful, else GAP_INVALID_HANDLE +** +*******************************************************************************/ +UINT16 GAP_ConnOpen (const char *p_serv_name, UINT8 service_id, BOOLEAN is_server, + BD_ADDR p_rem_bda, UINT16 psm, tL2CAP_CFG_INFO *p_cfg, + tL2CAP_ERTM_INFO *ertm_info, UINT16 security, UINT8 chan_mode_mask, + tGAP_CONN_CALLBACK *p_cb) +{ + tGAP_CCB *p_ccb; + UINT16 cid; + //tBT_UUID bt_uuid = {2, {GAP_PROTOCOL_ID}}; + + GAP_TRACE_EVENT ("GAP_CONN - Open Request"); + + /* Allocate a new CCB. Return if none available. */ + if ((p_ccb = gap_allocate_ccb()) == NULL) { + return (GAP_INVALID_HANDLE); + } + + /* If caller specified a BD address, save it */ + if (p_rem_bda) { + /* the bd addr is not BT_BD_ANY, then a bd address was specified */ + if (memcmp (p_rem_bda, BT_BD_ANY, BD_ADDR_LEN)) { + p_ccb->rem_addr_specified = TRUE; + } + + memcpy (&p_ccb->rem_dev_address[0], p_rem_bda, BD_ADDR_LEN); + } else if (!is_server) { + /* remore addr is not specified and is not a server -> bad */ + return (GAP_INVALID_HANDLE); + } + + /* A client MUST have specified a bd addr to connect with */ + if (!p_ccb->rem_addr_specified && !is_server) { + gap_release_ccb (p_ccb); + GAP_TRACE_ERROR ("GAP ERROR: Client must specify a remote BD ADDR to connect to!"); + return (GAP_INVALID_HANDLE); + } + + /* Check if configuration was specified */ + if (p_cfg) { + p_ccb->cfg = *p_cfg; + } + + p_ccb->p_callback = p_cb; + + /* If originator, use a dynamic PSM */ +#if ((defined AMP_INCLUDED) && (AMP_INCLUDED == TRUE)) + if (!is_server) { + gap_cb.conn.reg_info.pAMP_ConnectInd_Cb = NULL; + } else { + gap_cb.conn.reg_info.pAMP_ConnectInd_Cb = gap_connect_ind; + } +#else + if (!is_server) { + gap_cb.conn.reg_info.pL2CA_ConnectInd_Cb = NULL; + } else { + gap_cb.conn.reg_info.pL2CA_ConnectInd_Cb = gap_connect_ind; + } +#endif + + /* Register the PSM with L2CAP */ + if ((p_ccb->psm = L2CA_REGISTER (psm, &gap_cb.conn.reg_info, + AMP_AUTOSWITCH_ALLOWED | AMP_USE_AMP_IF_POSSIBLE)) == 0) { + GAP_TRACE_ERROR ("GAP_ConnOpen: Failure registering PSM 0x%04x", psm); + gap_release_ccb (p_ccb); + return (GAP_INVALID_HANDLE); + } + + /* Register with Security Manager for the specific security level */ + p_ccb->service_id = service_id; + if (!BTM_SetSecurityLevel ((UINT8)!is_server, p_serv_name, + p_ccb->service_id, security, p_ccb->psm, 0, 0)) { + GAP_TRACE_ERROR ("GAP_CONN - Security Error"); + gap_release_ccb (p_ccb); + return (GAP_INVALID_HANDLE); + } + + /* Fill in eL2CAP parameter data */ + if ( p_ccb->cfg.fcr_present ) { + if (ertm_info == NULL) { + p_ccb->ertm_info.preferred_mode = p_ccb->cfg.fcr.mode; + p_ccb->ertm_info.user_rx_buf_size = GAP_DATA_BUF_SIZE; + p_ccb->ertm_info.user_tx_buf_size = GAP_DATA_BUF_SIZE; + p_ccb->ertm_info.fcr_rx_buf_size = L2CAP_INVALID_ERM_BUF_SIZE; + p_ccb->ertm_info.fcr_tx_buf_size = L2CAP_INVALID_ERM_BUF_SIZE; + } else { + p_ccb->ertm_info = *ertm_info; + } + } + + /* optional FCR channel modes */ + if (ertm_info != NULL) { + p_ccb->ertm_info.allowed_modes = + (chan_mode_mask) ? chan_mode_mask : (UINT8)L2CAP_FCR_CHAN_OPT_BASIC; + } + + if (is_server) { + p_ccb->con_flags |= GAP_CCB_FLAGS_SEC_DONE; /* assume btm/l2cap would handle it */ + p_ccb->con_state = GAP_CCB_STATE_LISTENING; + return (p_ccb->gap_handle); + } else { + /* We are the originator of this connection */ + p_ccb->con_flags = GAP_CCB_FLAGS_IS_ORIG; + + /* Transition to the next appropriate state, waiting for connection confirm. */ + p_ccb->con_state = GAP_CCB_STATE_CONN_SETUP; + + /* mark security done flag, when security is not required */ + if ((security & (BTM_SEC_OUT_AUTHORIZE | BTM_SEC_OUT_AUTHENTICATE | BTM_SEC_OUT_ENCRYPT) ) == 0) { + p_ccb->con_flags |= GAP_CCB_FLAGS_SEC_DONE; + } + + /* Check if L2CAP started the connection process */ + if (p_rem_bda && ((cid = L2CA_CONNECT_REQ (p_ccb->psm, p_rem_bda, &p_ccb->ertm_info, &bt_uuid)) != 0)) { + p_ccb->connection_id = cid; + return (p_ccb->gap_handle); + } else { + gap_release_ccb (p_ccb); + return (GAP_INVALID_HANDLE); + } + } +} + + +/******************************************************************************* +** +** Function GAP_ConnClose +** +** Description This function is called to close a connection. +** +** Parameters: handle - Handle of the connection returned by GAP_ConnOpen +** +** Returns BT_PASS - closed OK +** GAP_ERR_BAD_HANDLE - invalid handle +** +*******************************************************************************/ +UINT16 GAP_ConnClose (UINT16 gap_handle) +{ + tGAP_CCB *p_ccb = gap_find_ccb_by_handle (gap_handle); + + GAP_TRACE_EVENT ("GAP_CONN - close handle: 0x%x", gap_handle); + + if (p_ccb) { + /* Check if we have a connection ID */ + if (p_ccb->con_state != GAP_CCB_STATE_LISTENING) { + L2CA_DISCONNECT_REQ (p_ccb->connection_id); + } + + gap_release_ccb (p_ccb); + + return (BT_PASS); + } + + return (GAP_ERR_BAD_HANDLE); +} + + + +/******************************************************************************* +** +** Function GAP_ConnReadData +** +** Description Normally not GKI aware application will call this function +** after receiving GAP_EVT_RXDATA event. +** +** Parameters: handle - Handle of the connection returned in the Open +** p_data - Data area +** max_len - Byte count requested +** p_len - Byte count received +** +** Returns BT_PASS - data read +** GAP_ERR_BAD_HANDLE - invalid handle +** GAP_NO_DATA_AVAIL - no data available +** +*******************************************************************************/ +UINT16 GAP_ConnReadData (UINT16 gap_handle, UINT8 *p_data, UINT16 max_len, UINT16 *p_len) +{ + tGAP_CCB *p_ccb = gap_find_ccb_by_handle (gap_handle); + UINT16 copy_len; + + if (!p_ccb) { + return (GAP_ERR_BAD_HANDLE); + } + + *p_len = 0; + + if (fixed_queue_is_empty(p_ccb->rx_queue)) { + return (GAP_NO_DATA_AVAIL); + } + + osi_mutex_global_lock(); + + while (max_len) { + BT_HDR *p_buf = fixed_queue_try_peek_first(p_ccb->rx_queue); + if (p_buf == NULL) { + break; + } + + copy_len = (p_buf->len > max_len)?max_len:p_buf->len; + max_len -= copy_len; + *p_len += copy_len; + if (p_data) { + memcpy (p_data, (UINT8 *)(p_buf + 1) + p_buf->offset, copy_len); + p_data += copy_len; + } + + if (p_buf->len > copy_len) { + p_buf->offset += copy_len; + p_buf->len -= copy_len; + break; + } + osi_free(fixed_queue_dequeue(p_ccb->rx_queue, 0)); + } + + p_ccb->rx_queue_size -= *p_len; + + osi_mutex_global_unlock(); + + GAP_TRACE_EVENT ("GAP_ConnReadData - rx_queue_size left=%d, *p_len=%d", + p_ccb->rx_queue_size, *p_len); + + return (BT_PASS); +} + +/******************************************************************************* +** +** Function GAP_GetRxQueueCnt +** +** Description This function return number of bytes on the rx queue. +** +** Parameters: handle - Handle returned in the GAP_ConnOpen +** p_rx_queue_count - Pointer to return queue count in. +** +** +*******************************************************************************/ +int GAP_GetRxQueueCnt (UINT16 handle, UINT32 *p_rx_queue_count) +{ + tGAP_CCB *p_ccb; + int rc = BT_PASS; + + /* Check that handle is valid */ + if (handle < GAP_MAX_CONNECTIONS) { + p_ccb = &gap_cb.conn.ccb_pool[handle]; + + if (p_ccb->con_state == GAP_CCB_STATE_CONNECTED) { + *p_rx_queue_count = p_ccb->rx_queue_size; + } else { + rc = GAP_INVALID_HANDLE; + } + } else { + rc = GAP_INVALID_HANDLE; + } + + GAP_TRACE_EVENT ("GAP_GetRxQueueCnt - rc = 0x%04x, rx_queue_count=%d", + rc , *p_rx_queue_count); + + return (rc); +} + +/******************************************************************************* +** +** Function GAP_ConnBTRead +** +** Description Bluetooth aware applications will call this function after receiving +** GAP_EVT_RXDATA event. +** +** Parameters: handle - Handle of the connection returned in the Open +** pp_buf - pointer to address of buffer with data, +** +** Returns BT_PASS - data read +** GAP_ERR_BAD_HANDLE - invalid handle +** GAP_NO_DATA_AVAIL - no data available +** +*******************************************************************************/ +UINT16 GAP_ConnBTRead (UINT16 gap_handle, BT_HDR **pp_buf) +{ + tGAP_CCB *p_ccb = gap_find_ccb_by_handle (gap_handle); + BT_HDR *p_buf; + + if (!p_ccb) { + return (GAP_ERR_BAD_HANDLE); + } + + p_buf = (BT_HDR *)fixed_queue_dequeue(p_ccb->rx_queue, 0); + + if (p_buf) { + *pp_buf = p_buf; + + p_ccb->rx_queue_size -= p_buf->len; + return (BT_PASS); + } else { + *pp_buf = NULL; + return (GAP_NO_DATA_AVAIL); + } +} + + +/******************************************************************************* +** +** Function GAP_ConnBTWrite +** +** Description Bluetooth Aware applications can call this function to write data. +** +** Parameters: handle - Handle of the connection returned in the Open +** p_buf - pointer to address of buffer with data, +** +** Returns BT_PASS - data read +** GAP_ERR_BAD_HANDLE - invalid handle +** GAP_ERR_BAD_STATE - connection not established +** GAP_INVALID_BUF_OFFSET - buffer offset is invalid +*******************************************************************************/ +UINT16 GAP_ConnBTWrite (UINT16 gap_handle, BT_HDR *p_buf) +{ + tGAP_CCB *p_ccb = gap_find_ccb_by_handle (gap_handle); + + if (!p_ccb) { + osi_free (p_buf); + return (GAP_ERR_BAD_HANDLE); + } + + if (p_ccb->con_state != GAP_CCB_STATE_CONNECTED) { + osi_free (p_buf); + return (GAP_ERR_BAD_STATE); + } + + if (p_buf->offset < L2CAP_MIN_OFFSET) { + osi_free (p_buf); + return (GAP_ERR_BUF_OFFSET); + } + + fixed_queue_enqueue(p_ccb->tx_queue, p_buf, FIXED_QUEUE_MAX_TIMEOUT); + + if (p_ccb->is_congested) { + return (BT_PASS); + } + + /* Send the buffer through L2CAP */ +#if (GAP_CONN_POST_EVT_INCLUDED == TRUE) + gap_send_event (gap_handle); +#else + while ((p_buf = (BT_HDR *)fixed_queue_dequeue(p_ccb->tx_queue, 0)) != NULL) { + UINT8 status = L2CA_DATA_WRITE (p_ccb->connection_id, p_buf); + + if (status == L2CAP_DW_CONGESTED) { + p_ccb->is_congested = TRUE; + break; + } else if (status != L2CAP_DW_SUCCESS) { + return (GAP_ERR_BAD_STATE); + } + } +#endif + return (BT_PASS); +} + + +/******************************************************************************* +** +** Function GAP_ConnWriteData +** +** Description Normally not GKI aware application will call this function +** to send data to the connection. +** +** Parameters: handle - Handle of the connection returned in the Open +** p_data - Data area +** max_len - Byte count requested +** p_len - Byte count received +** +** Returns BT_PASS - data read +** GAP_ERR_BAD_HANDLE - invalid handle +** GAP_ERR_BAD_STATE - connection not established +** GAP_CONGESTION - system is congested +** +*******************************************************************************/ +UINT16 GAP_ConnWriteData (UINT16 gap_handle, UINT8 *p_data, UINT16 max_len, UINT16 *p_len) +{ + tGAP_CCB *p_ccb = gap_find_ccb_by_handle (gap_handle); + BT_HDR *p_buf; + + *p_len = 0; + + if (!p_ccb) { + return (GAP_ERR_BAD_HANDLE); + } + + if (p_ccb->con_state != GAP_CCB_STATE_CONNECTED) { + return (GAP_ERR_BAD_STATE); + } + + while (max_len) { + if (p_ccb->cfg.fcr.mode == L2CAP_FCR_ERTM_MODE) { + if ((p_buf = (BT_HDR *)osi_malloc(L2CAP_FCR_ERTM_BUF_SIZE)) == NULL) { + return (GAP_ERR_CONGESTED); + } + } else { + if ((p_buf = (BT_HDR *)osi_malloc(GAP_DATA_BUF_SIZE)) == NULL) { + return (GAP_ERR_CONGESTED); + } + } + + p_buf->offset = L2CAP_MIN_OFFSET; + p_buf->len = (p_ccb->rem_mtu_size < max_len) ? p_ccb->rem_mtu_size : max_len; + p_buf->event = BT_EVT_TO_BTU_SP_DATA; + + memcpy ((UINT8 *)(p_buf + 1) + p_buf->offset, p_data, p_buf->len); + + *p_len += p_buf->len; + max_len -= p_buf->len; + p_data += p_buf->len; + + GAP_TRACE_EVENT ("GAP_WriteData %d bytes", p_buf->len); + + fixed_queue_enqueue(p_ccb->tx_queue, p_buf, FIXED_QUEUE_MAX_TIMEOUT); + } + + if (p_ccb->is_congested) { + return (BT_PASS); + } + + /* Send the buffer through L2CAP */ +#if (GAP_CONN_POST_EVT_INCLUDED == TRUE) + gap_send_event (gap_handle); +#else + while ((p_buf = (BT_HDR *)fixed_queue_dequeue(p_ccb->tx_queue, 0)) != NULL) + { + UINT8 status = L2CA_DATA_WRITE (p_ccb->connection_id, p_buf); + + if (status == L2CAP_DW_CONGESTED) { + p_ccb->is_congested = TRUE; + break; + } else if (status != L2CAP_DW_SUCCESS) { + return (GAP_ERR_BAD_STATE); + } + } +#endif + return (BT_PASS); +} + +/******************************************************************************* +** +** Function GAP_ConnReconfig +** +** Description Applications can call this function to reconfigure the connection. +** +** Parameters: handle - Handle of the connection +** p_cfg - Pointer to new configuration +** +** Returns BT_PASS - config process started +** GAP_ERR_BAD_HANDLE - invalid handle +** +*******************************************************************************/ +UINT16 GAP_ConnReconfig (UINT16 gap_handle, tL2CAP_CFG_INFO *p_cfg) +{ + tGAP_CCB *p_ccb = gap_find_ccb_by_handle (gap_handle); + + if (!p_ccb) { + return (GAP_ERR_BAD_HANDLE); + } + + p_ccb->cfg = *p_cfg; + + if (p_ccb->con_state == GAP_CCB_STATE_CONNECTED) { + L2CA_CONFIG_REQ (p_ccb->connection_id, p_cfg); + } + + return (BT_PASS); +} + + + +/******************************************************************************* +** +** Function GAP_ConnSetIdleTimeout +** +** Description Higher layers call this function to set the idle timeout for +** a connection, or for all future connections. The "idle timeout" +** is the amount of time that a connection can remain up with +** no L2CAP channels on it. A timeout of zero means that the +** connection will be torn down immediately when the last channel +** is removed. A timeout of 0xFFFF means no timeout. Values are +** in seconds. +** +** Parameters: handle - Handle of the connection +** timeout - in secs +** 0 = immediate disconnect when last channel is removed +** 0xFFFF = no idle timeout +** +** Returns BT_PASS - config process started +** GAP_ERR_BAD_HANDLE - invalid handle +** +*******************************************************************************/ +UINT16 GAP_ConnSetIdleTimeout (UINT16 gap_handle, UINT16 timeout) +{ + tGAP_CCB *p_ccb; + + if ((p_ccb = gap_find_ccb_by_handle (gap_handle)) == NULL) { + return (GAP_ERR_BAD_HANDLE); + } + + if (L2CA_SetIdleTimeout (p_ccb->connection_id, timeout, FALSE)) { + return (BT_PASS); + } else { + return (GAP_ERR_BAD_HANDLE); + } +} + + + +/******************************************************************************* +** +** Function GAP_ConnGetRemoteAddr +** +** Description This function is called to get the remote BD address +** of a connection. +** +** Parameters: handle - Handle of the connection returned by GAP_ConnOpen +** +** Returns BT_PASS - closed OK +** GAP_ERR_BAD_HANDLE - invalid handle +** +*******************************************************************************/ +UINT8 *GAP_ConnGetRemoteAddr (UINT16 gap_handle) +{ + tGAP_CCB *p_ccb = gap_find_ccb_by_handle (gap_handle); + + GAP_TRACE_EVENT ("GAP_ConnGetRemoteAddr gap_handle = %d", gap_handle); + + if ((p_ccb) && (p_ccb->con_state > GAP_CCB_STATE_LISTENING)) { + GAP_TRACE_EVENT("GAP_ConnGetRemoteAddr bda :0x%02x:0x%02x:0x%02x:0x%02x:0x%02x:0x%02x\n", \ + p_ccb->rem_dev_address[0], p_ccb->rem_dev_address[1], p_ccb->rem_dev_address[2], + p_ccb->rem_dev_address[3], p_ccb->rem_dev_address[4], p_ccb->rem_dev_address[5]); + return (p_ccb->rem_dev_address); + } else { + GAP_TRACE_EVENT ("GAP_ConnGetRemoteAddr return Error "); + return (NULL); + } +} + + +/******************************************************************************* +** +** Function GAP_ConnGetRemMtuSize +** +** Description Returns the remote device's MTU size +** +** Parameters: handle - Handle of the connection +** +** Returns UINT16 - maximum size buffer that can be transmitted to the peer +** +*******************************************************************************/ +UINT16 GAP_ConnGetRemMtuSize (UINT16 gap_handle) +{ + tGAP_CCB *p_ccb; + + if ((p_ccb = gap_find_ccb_by_handle (gap_handle)) == NULL) { + return (0); + } + + return (p_ccb->rem_mtu_size); +} + +/******************************************************************************* +** +** Function GAP_ConnGetL2CAPCid +** +** Description Returns the L2CAP channel id +** +** Parameters: handle - Handle of the connection +** +** Returns UINT16 - The L2CAP channel id +** 0, if error +** +*******************************************************************************/ +UINT16 GAP_ConnGetL2CAPCid (UINT16 gap_handle) +{ + tGAP_CCB *p_ccb; + + if ((p_ccb = gap_find_ccb_by_handle (gap_handle)) == NULL) { + return (0); + } + + return (p_ccb->connection_id); +} + + +/******************************************************************************* +** +** Function gap_connect_ind +** +** Description This function handles an inbound connection indication +** from L2CAP. This is the case where we are acting as a +** server. +** +** Returns void +** +*******************************************************************************/ +static void gap_connect_ind (BD_ADDR bd_addr, UINT16 l2cap_cid, UINT16 psm, UINT8 l2cap_id) +{ + UINT16 xx; + tGAP_CCB *p_ccb; + //tBT_UUID bt_uuid = {2, {GAP_PROTOCOL_ID}}; + + /* See if we have a CCB listening for the connection */ + for (xx = 0, p_ccb = gap_cb.conn.ccb_pool; xx < GAP_MAX_CONNECTIONS; xx++, p_ccb++) { + if ((p_ccb->con_state == GAP_CCB_STATE_LISTENING) + && (p_ccb->psm == psm) + && ((p_ccb->rem_addr_specified == FALSE) + || (!memcmp (bd_addr, p_ccb->rem_dev_address, BD_ADDR_LEN)))) { + break; + } + } + + if (xx == GAP_MAX_CONNECTIONS) { + GAP_TRACE_WARNING("*******"); + GAP_TRACE_WARNING("WARNING: GAP Conn Indication for Unexpected Bd Addr...Disconnecting"); + GAP_TRACE_WARNING("*******"); + + /* Disconnect because it is an unexpected connection */ + L2CA_DISCONNECT_REQ (l2cap_cid); + return; + } + + /* Transition to the next appropriate state, waiting for config setup. */ + p_ccb->con_state = GAP_CCB_STATE_CFG_SETUP; + + /* Save the BD Address and Channel ID. */ + memcpy (&p_ccb->rem_dev_address[0], bd_addr, BD_ADDR_LEN); + p_ccb->connection_id = l2cap_cid; + + /* Send response to the L2CAP layer. */ + L2CA_CONNECT_RSP (bd_addr, l2cap_id, l2cap_cid, L2CAP_CONN_OK, L2CAP_CONN_OK, &p_ccb->ertm_info, &bt_uuid); + + GAP_TRACE_EVENT("GAP_CONN - Rcvd L2CAP conn ind, CID: 0x%x", p_ccb->connection_id); + + /* Send a Configuration Request. */ + L2CA_CONFIG_REQ (l2cap_cid, &p_ccb->cfg); +} + +/******************************************************************************* +** +** Function gap_checks_con_flags +** +** Description This function processes the L2CAP configuration indication +** event. +** +** Returns void +** +*******************************************************************************/ +static void gap_checks_con_flags (tGAP_CCB *p_ccb) +{ + GAP_TRACE_EVENT ("gap_checks_con_flags conn_flags:0x%x, ", p_ccb->con_flags); + /* if all the required con_flags are set, report the OPEN event now */ + if ((p_ccb->con_flags & GAP_CCB_FLAGS_CONN_DONE) == GAP_CCB_FLAGS_CONN_DONE) { + p_ccb->con_state = GAP_CCB_STATE_CONNECTED; + + p_ccb->p_callback (p_ccb->gap_handle, GAP_EVT_CONN_OPENED); + } +} + +/******************************************************************************* +** +** Function gap_sec_check_complete +** +** Description The function called when Security Manager finishes +** verification of the service side connection +** +** Returns void +** +*******************************************************************************/ +static void gap_sec_check_complete (BD_ADDR bd_addr, tBT_TRANSPORT transport, void *p_ref_data, UINT8 res) +{ + tGAP_CCB *p_ccb = (tGAP_CCB *)p_ref_data; + UNUSED(bd_addr); + UNUSED (transport); + + GAP_TRACE_EVENT ("gap_sec_check_complete conn_state:%d, conn_flags:0x%x, status:%d", + p_ccb->con_state, p_ccb->con_flags, res); + if (p_ccb->con_state == GAP_CCB_STATE_IDLE) { + return; + } + + if (res == BTM_SUCCESS) { + p_ccb->con_flags |= GAP_CCB_FLAGS_SEC_DONE; + gap_checks_con_flags (p_ccb); + } else { + /* security failed - disconnect the channel */ + L2CA_DISCONNECT_REQ (p_ccb->connection_id); + } +} + +/******************************************************************************* +** +** Function gap_connect_cfm +** +** Description This function handles the connect confirm events +** from L2CAP. This is the case when we are acting as a +** client and have sent a connect request. +** +** Returns void +** +*******************************************************************************/ +static void gap_connect_cfm (UINT16 l2cap_cid, UINT16 result) +{ + tGAP_CCB *p_ccb; + + /* Find CCB based on CID */ + if ((p_ccb = gap_find_ccb_by_cid (l2cap_cid)) == NULL) { + return; + } + + /* initiate security process, if needed */ + if ( (p_ccb->con_flags & GAP_CCB_FLAGS_SEC_DONE) == 0) { + btm_sec_mx_access_request (p_ccb->rem_dev_address, p_ccb->psm, TRUE, + 0, 0, &gap_sec_check_complete, p_ccb); + } + + /* If the connection response contains success status, then */ + /* Transition to the next state and startup the timer. */ + if ((result == L2CAP_CONN_OK) && (p_ccb->con_state == GAP_CCB_STATE_CONN_SETUP)) { + p_ccb->con_state = GAP_CCB_STATE_CFG_SETUP; + + /* Send a Configuration Request. */ + L2CA_CONFIG_REQ (l2cap_cid, &p_ccb->cfg); + } else { + /* Tell the user if he has a callback */ + if (p_ccb->p_callback) { + (*p_ccb->p_callback) (p_ccb->gap_handle, GAP_EVT_CONN_CLOSED); + } + + gap_release_ccb (p_ccb); + } +} + +/******************************************************************************* +** +** Function gap_config_ind +** +** Description This function processes the L2CAP configuration indication +** event. +** +** Returns void +** +*******************************************************************************/ +static void gap_config_ind (UINT16 l2cap_cid, tL2CAP_CFG_INFO *p_cfg) +{ + tGAP_CCB *p_ccb; + UINT16 local_mtu_size; + + /* Find CCB based on CID */ + if ((p_ccb = gap_find_ccb_by_cid (l2cap_cid)) == NULL) { + return; + } + + /* Remember the remote MTU size */ + + if (p_ccb->cfg.fcr.mode == L2CAP_FCR_ERTM_MODE) { + local_mtu_size = p_ccb->ertm_info.user_tx_buf_size + - sizeof(BT_HDR) - L2CAP_MIN_OFFSET; + } else { + local_mtu_size = L2CAP_MTU_SIZE; + } + + if ((!p_cfg->mtu_present) || (p_cfg->mtu > local_mtu_size)) { + p_ccb->rem_mtu_size = local_mtu_size; + } else { + p_ccb->rem_mtu_size = p_cfg->mtu; + } + + /* For now, always accept configuration from the other side */ + p_cfg->flush_to_present = FALSE; + p_cfg->mtu_present = FALSE; + p_cfg->result = L2CAP_CFG_OK; + p_cfg->fcs_present = FALSE; + + L2CA_CONFIG_RSP (l2cap_cid, p_cfg); + + p_ccb->con_flags |= GAP_CCB_FLAGS_HIS_CFG_DONE; + + gap_checks_con_flags (p_ccb); +} + + +/******************************************************************************* +** +** Function gap_config_cfm +** +** Description This function processes the L2CAP configuration confirmation +** event. +** +** Returns void +** +*******************************************************************************/ +static void gap_config_cfm (UINT16 l2cap_cid, tL2CAP_CFG_INFO *p_cfg) +{ + tGAP_CCB *p_ccb; + + /* Find CCB based on CID */ + if ((p_ccb = gap_find_ccb_by_cid (l2cap_cid)) == NULL) { + return; + } + + if (p_cfg->result == L2CAP_CFG_OK) { + p_ccb->con_flags |= GAP_CCB_FLAGS_MY_CFG_DONE; + + + if (p_ccb->cfg.fcr_present) { + p_ccb->cfg.fcr.mode = p_cfg->fcr.mode; + } else { + p_ccb->cfg.fcr.mode = L2CAP_FCR_BASIC_MODE; + } + + gap_checks_con_flags (p_ccb); + } else { + p_ccb->p_callback (p_ccb->gap_handle, GAP_EVT_CONN_CLOSED); + gap_release_ccb (p_ccb); + } +} + + +/******************************************************************************* +** +** Function gap_disconnect_ind +** +** Description This function handles a disconnect event from L2CAP. If +** requested to, we ack the disconnect before dropping the CCB +** +** Returns void +** +*******************************************************************************/ +static void gap_disconnect_ind (UINT16 l2cap_cid, BOOLEAN ack_needed) +{ + tGAP_CCB *p_ccb; + + GAP_TRACE_EVENT ("GAP_CONN - Rcvd L2CAP disc, CID: 0x%x", l2cap_cid); + + /* Find CCB based on CID */ + if ((p_ccb = gap_find_ccb_by_cid (l2cap_cid)) == NULL) { + return; + } + + if (ack_needed) { + L2CA_DISCONNECT_RSP (l2cap_cid); + } + + p_ccb->p_callback (p_ccb->gap_handle, GAP_EVT_CONN_CLOSED); + gap_release_ccb (p_ccb); +} + + +/******************************************************************************* +** +** Function gap_data_ind +** +** Description This function is called when data is received from L2CAP. +** +** Returns void +** +*******************************************************************************/ +static void gap_data_ind (UINT16 l2cap_cid, BT_HDR *p_msg) +{ + tGAP_CCB *p_ccb; + + /* Find CCB based on CID */ + if ((p_ccb = gap_find_ccb_by_cid (l2cap_cid)) == NULL) { + osi_free (p_msg); + return; + } + + if (p_ccb->con_state == GAP_CCB_STATE_CONNECTED) { + fixed_queue_enqueue(p_ccb->rx_queue, p_msg, FIXED_QUEUE_MAX_TIMEOUT); + + p_ccb->rx_queue_size += p_msg->len; + /* + GAP_TRACE_EVENT ("gap_data_ind - rx_queue_size=%d, msg len=%d", + p_ccb->rx_queue_size, p_msg->len); + */ + + p_ccb->p_callback (p_ccb->gap_handle, GAP_EVT_CONN_DATA_AVAIL); + } else { + osi_free (p_msg); + } +} + + +/******************************************************************************* +** +** Function gap_congestion_ind +** +** Description This is a callback function called by L2CAP when +** data L2CAP congestion status changes +** +*******************************************************************************/ +static void gap_congestion_ind (UINT16 lcid, BOOLEAN is_congested) +{ + tGAP_CCB *p_ccb; + UINT16 event; + BT_HDR *p_buf; + UINT8 status; + + GAP_TRACE_EVENT ("GAP_CONN - Rcvd L2CAP Is Congested (%d), CID: 0x%x", + is_congested, lcid); + + /* Find CCB based on CID */ + if ((p_ccb = gap_find_ccb_by_cid (lcid)) == NULL) { + return; + } + + p_ccb->is_congested = is_congested; + + event = (is_congested) ? GAP_EVT_CONN_CONGESTED : GAP_EVT_CONN_UNCONGESTED; + p_ccb->p_callback (p_ccb->gap_handle, event); + + if (!is_congested) { + while ((p_buf = (BT_HDR *)fixed_queue_dequeue(p_ccb->tx_queue, 0)) != NULL) { + status = L2CA_DATA_WRITE (p_ccb->connection_id, p_buf); + + if (status == L2CAP_DW_CONGESTED) { + p_ccb->is_congested = TRUE; + break; + } else if (status != L2CAP_DW_SUCCESS) { + break; + } + } + } +} + + +/******************************************************************************* +** +** Function gap_find_ccb_by_cid +** +** Description This function searches the CCB table for an entry with the +** passed CID. +** +** Returns the CCB address, or NULL if not found. +** +*******************************************************************************/ +static tGAP_CCB *gap_find_ccb_by_cid (UINT16 cid) +{ + UINT16 xx; + tGAP_CCB *p_ccb; + + /* Look through each connection control block */ + for (xx = 0, p_ccb = gap_cb.conn.ccb_pool; xx < GAP_MAX_CONNECTIONS; xx++, p_ccb++) { + if ((p_ccb->con_state != GAP_CCB_STATE_IDLE) && (p_ccb->connection_id == cid)) { + return (p_ccb); + } + } + + /* If here, not found */ + return (NULL); +} + + +/******************************************************************************* +** +** Function gap_find_ccb_by_handle +** +** Description This function searches the CCB table for an entry with the +** passed handle. +** +** Returns the CCB address, or NULL if not found. +** +*******************************************************************************/ +static tGAP_CCB *gap_find_ccb_by_handle (UINT16 handle) +{ + tGAP_CCB *p_ccb; + + /* Check that handle is valid */ + if (handle < GAP_MAX_CONNECTIONS) { + p_ccb = &gap_cb.conn.ccb_pool[handle]; + + if (p_ccb->con_state != GAP_CCB_STATE_IDLE) { + return (p_ccb); + } + } + + /* If here, handle points to invalid connection */ + return (NULL); +} + + +/******************************************************************************* +** +** Function gap_allocate_ccb +** +** Description This function allocates a new CCB. +** +** Returns CCB address, or NULL if none available. +** +*******************************************************************************/ +static tGAP_CCB *gap_allocate_ccb (void) +{ + UINT16 xx; + tGAP_CCB *p_ccb; + + /* Look through each connection control block for a free one */ + for (xx = 0, p_ccb = gap_cb.conn.ccb_pool; xx < GAP_MAX_CONNECTIONS; xx++, p_ccb++) { + if (p_ccb->con_state == GAP_CCB_STATE_IDLE) { + memset (p_ccb, 0, sizeof (tGAP_CCB)); + p_ccb->tx_queue = fixed_queue_new(QUEUE_SIZE_MAX); + p_ccb->rx_queue = fixed_queue_new(QUEUE_SIZE_MAX); + + p_ccb->gap_handle = xx; + p_ccb->rem_mtu_size = L2CAP_MTU_SIZE; + + return (p_ccb); + } + } + + /* If here, no free CCB found */ + return (NULL); +} + + +/******************************************************************************* +** +** Function gap_release_ccb +** +** Description This function releases a CCB. +** +** Returns void +** +*******************************************************************************/ +static void gap_release_ccb (tGAP_CCB *p_ccb) +{ + UINT16 xx; + UINT16 psm = p_ccb->psm; + UINT8 service_id = p_ccb->service_id; + + /* Drop any buffers we may be holding */ + p_ccb->rx_queue_size = 0; + + while (!fixed_queue_is_empty(p_ccb->rx_queue)) { + osi_free(fixed_queue_dequeue(p_ccb->rx_queue, 0)); + } + fixed_queue_free(p_ccb->rx_queue, NULL); + p_ccb->rx_queue = NULL; + + while (!fixed_queue_is_empty(p_ccb->tx_queue)) { + osi_free(fixed_queue_dequeue(p_ccb->tx_queue, 0)); + } + fixed_queue_free(p_ccb->tx_queue, NULL); + p_ccb->tx_queue = NULL; + + p_ccb->con_state = GAP_CCB_STATE_IDLE; + + /* If no-one else is using the PSM, deregister from L2CAP */ + for (xx = 0, p_ccb = gap_cb.conn.ccb_pool; xx < GAP_MAX_CONNECTIONS; xx++, p_ccb++) { + if ((p_ccb->con_state != GAP_CCB_STATE_IDLE) && (p_ccb->psm == psm)) { + return; + } + } +#if (SDP_INCLUDED == TRUE) + /* Free the security record for this PSM */ + BTM_SecClrService(service_id); +#endif ///SDP_INCLUDED == TRUE + L2CA_DEREGISTER (psm); +} + +#if (GAP_CONN_POST_EVT_INCLUDED == TRUE) + +/******************************************************************************* +** +** Function gap_send_event +** +** Description Send BT_EVT_TO_GAP_MSG event to BTU task +** +** Returns None +** +*******************************************************************************/ +void gap_send_event (UINT16 gap_handle) +{ + BT_HDR *p_msg; + + if ((p_msg = (BT_HDR *)osi_malloc(BT_HDR_SIZE)) != NULL) { + p_msg->event = BT_EVT_TO_GAP_MSG; + p_msg->len = 0; + p_msg->offset = 0; + p_msg->layer_specific = gap_handle; + + GKI_send_msg(BTU_TASK, BTU_HCI_RCV_MBOX, p_msg); + } else { + GAP_TRACE_ERROR("Unable to allocate message buffer for event."); + } +} + +#endif /* (GAP_CONN_POST_EVT_INCLUDED == TRUE) */ +#endif /* GAP_CONN_INCLUDED */ diff --git a/lib/bt/host/bluedroid/stack/gap/gap_utils.c b/lib/bt/host/bluedroid/stack/gap/gap_utils.c new file mode 100644 index 00000000..b358021a --- /dev/null +++ b/lib/bt/host/bluedroid/stack/gap/gap_utils.c @@ -0,0 +1,141 @@ +/****************************************************************************** + * + * Copyright (C) 2009-2013 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +#include +#include "common/bt_target.h" +//#include "bt_utils.h" +#include "gap_int.h" + +#if (CLASSIC_BT_INCLUDED == TRUE) +/******************************************************************************* +** +** Function gap_allocate_cb +** +** Description Look through the GAP Control Blocks for a free one. +** +** Returns Pointer to the control block or NULL if not found +** +*******************************************************************************/ +tGAP_INFO *gap_allocate_cb (void) +{ + tGAP_INFO *p_cb = &gap_cb.blk[0]; + UINT8 x; + + for (x = 0; x < GAP_MAX_BLOCKS; x++, p_cb++) { + if (!p_cb->in_use) { + memset (p_cb, 0, sizeof (tGAP_INFO)); + + p_cb->in_use = TRUE; + p_cb->index = x; + p_cb->p_data = (void *)NULL; + return (p_cb); + } + } + + /* If here, no free control blocks found */ + return (NULL); +} + + +/******************************************************************************* +** +** Function gap_free_cb +** +** Description Release GAP control block. +** +** Returns Pointer to the control block or NULL if not found +** +*******************************************************************************/ +void gap_free_cb (tGAP_INFO *p_cb) +{ + if (p_cb) { + p_cb->gap_cback = NULL; + p_cb->in_use = FALSE; + } +} + + +/******************************************************************************* +** +** Function gap_is_service_busy +** +** Description Look through the GAP Control Blocks that are in use +** and check to see if the event waiting for is the command +** requested. +** +** Returns TRUE if already in use +** FALSE if not busy +** +*******************************************************************************/ +BOOLEAN gap_is_service_busy (UINT16 request) +{ + tGAP_INFO *p_cb = &gap_cb.blk[0]; + UINT8 x; + + for (x = 0; x < GAP_MAX_BLOCKS; x++, p_cb++) { + if (p_cb->in_use && p_cb->event == request) { + return (TRUE); + } + } + + /* If here, service is not busy */ + return (FALSE); +} + + +/******************************************************************************* +** +** Function gap_convert_btm_status +** +** Description Converts a BTM error status into a GAP error status +** +** +** Returns GAP_UNKNOWN_BTM_STATUS is returned if not recognized +** +*******************************************************************************/ +UINT16 gap_convert_btm_status (tBTM_STATUS btm_status) +{ + switch (btm_status) { + case BTM_SUCCESS: + return (BT_PASS); + + case BTM_CMD_STARTED: + return (GAP_CMD_INITIATED); + + case BTM_BUSY: + return (GAP_ERR_BUSY); + + case BTM_MODE_UNSUPPORTED: + case BTM_ILLEGAL_VALUE: + return (GAP_ERR_ILL_PARM); + + case BTM_WRONG_MODE: + return (GAP_DEVICE_NOT_UP); + + case BTM_UNKNOWN_ADDR: + return (GAP_BAD_BD_ADDR); + + case BTM_DEVICE_TIMEOUT: + return (GAP_ERR_TIMEOUT); + + default: + return (GAP_ERR_PROCESSING); + } +} + +#endif ///CLASSIC_BT_INCLUDED == TRUE diff --git a/lib/bt/host/bluedroid/stack/gap/include/gap_int.h b/lib/bt/host/bluedroid/stack/gap/include/gap_int.h new file mode 100644 index 00000000..8a3ae0e2 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/gap/include/gap_int.h @@ -0,0 +1,159 @@ +/****************************************************************************** + * + * Copyright (C) 2009-2013 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + + +#ifndef GAP_INT_H +#define GAP_INT_H + +#include "common/bt_target.h" +#include "osi/fixed_queue.h" +#include "stack/gap_api.h" +#include "stack/gatt_api.h" +#define GAP_MAX_BLOCKS 2 /* Concurrent GAP commands pending at a time*/ +/* Define the Generic Access Profile control structure */ +typedef struct { + void *p_data; /* Pointer to any data returned in callback */ + tGAP_CALLBACK *gap_cback; /* Pointer to users callback function */ + tGAP_CALLBACK *gap_inq_rslt_cback; /* Used for inquiry results */ + UINT16 event; /* Passed back in the callback */ + UINT8 index; /* Index of this control block and callback */ + BOOLEAN in_use; /* True when structure is allocated */ +} tGAP_INFO; + +/* Define the control block for the FindAddrByName operation (Only 1 active at a time) */ +typedef struct { + tGAP_CALLBACK *p_cback; + tBTM_INQ_INFO *p_cur_inq; /* Pointer to the current inquiry database entry */ + tGAP_FINDADDR_RESULTS results; + BOOLEAN in_use; +} tGAP_FINDADDR_CB; + +/* Define the GAP Connection Control Block. +*/ +typedef struct { +#define GAP_CCB_STATE_IDLE 0 +#define GAP_CCB_STATE_LISTENING 1 +#define GAP_CCB_STATE_CONN_SETUP 2 +#define GAP_CCB_STATE_CFG_SETUP 3 +#define GAP_CCB_STATE_WAIT_SEC 4 +#define GAP_CCB_STATE_CONNECTED 5 + UINT8 con_state; + +#define GAP_CCB_FLAGS_IS_ORIG 0x01 +#define GAP_CCB_FLAGS_HIS_CFG_DONE 0x02 +#define GAP_CCB_FLAGS_MY_CFG_DONE 0x04 +#define GAP_CCB_FLAGS_SEC_DONE 0x08 +#define GAP_CCB_FLAGS_CONN_DONE 0x0E + UINT8 con_flags; + + UINT8 service_id; /* Used by BTM */ + UINT16 gap_handle; /* GAP handle */ + UINT16 connection_id; /* L2CAP CID */ + BOOLEAN rem_addr_specified; + UINT8 chan_mode_mask; /* Supported channel modes (FCR) */ + BD_ADDR rem_dev_address; + UINT16 psm; + UINT16 rem_mtu_size; + + BOOLEAN is_congested; + fixed_queue_t *tx_queue; /* Queue of buffers waiting to be sent */ + fixed_queue_t *rx_queue; /* Queue of buffers waiting to be read */ + + UINT32 rx_queue_size; /* Total data count in rx_queue */ + + tGAP_CONN_CALLBACK *p_callback; /* Users callback function */ + + tL2CAP_CFG_INFO cfg; /* Configuration */ + tL2CAP_ERTM_INFO ertm_info; /* Pools and modes for ertm */ +} tGAP_CCB; + +typedef struct { +#if ((defined AMP_INCLUDED) && (AMP_INCLUDED == TRUE)) + tAMP_APPL_INFO reg_info; +#else + tL2CAP_APPL_INFO reg_info; /* L2CAP Registration info */ +#endif + tGAP_CCB ccb_pool[GAP_MAX_CONNECTIONS]; +} tGAP_CONN; + + +#if BLE_INCLUDED == TRUE +#define GAP_MAX_CHAR_NUM 4 + +typedef struct { + UINT16 handle; + UINT16 uuid; + tGAP_BLE_ATTR_VALUE attr_value; +} tGAP_ATTR; +#endif +/********************************************************************** +** M A I N C O N T R O L B L O C K +***********************************************************************/ + +#define GAP_MAX_CL GATT_CL_MAX_LCB + +typedef struct { + UINT16 uuid; + tGAP_BLE_CMPL_CBACK *p_cback; +} tGAP_BLE_REQ; + +typedef struct { + BD_ADDR bda; + tGAP_BLE_CMPL_CBACK *p_cback; + UINT16 conn_id; + UINT16 cl_op_uuid; + BOOLEAN in_use; + BOOLEAN connected; + fixed_queue_t *pending_req_q; + +} tGAP_CLCB; + +typedef struct { + tGAP_INFO blk[GAP_MAX_BLOCKS]; + tBTM_CMPL_CB *btm_cback[GAP_MAX_BLOCKS]; + UINT8 trace_level; + //tGAP_FINDADDR_CB findaddr_cb; /* Contains the control block for finding a device addr */ + //tBTM_INQ_INFO *cur_inqptr; + +#if GAP_CONN_INCLUDED == TRUE + tGAP_CONN conn; +#endif + + /* LE GAP attribute database */ +#if BLE_INCLUDED == TRUE && GATTS_INCLUDED == TRUE + tGAP_ATTR gap_attr[GAP_MAX_CHAR_NUM]; + tGAP_CLCB clcb[GAP_MAX_CL]; /* connection link*/ + tGATT_IF gatt_if; +#endif +} tGAP_CB; + +#if GAP_DYNAMIC_MEMORY == FALSE +extern tGAP_CB gap_cb; +#else +extern tGAP_CB *gap_cb_ptr; +#define gap_cb (*gap_cb_ptr) +#endif + +#if (GAP_CONN_INCLUDED == TRUE) +extern void gap_conn_init(void); +#endif +#if (BLE_INCLUDED == TRUE && GATTS_INCLUDED == TRUE) +extern void gap_attr_db_init(void); +#endif + +#endif diff --git a/lib/bt/host/bluedroid/stack/gatt/att_protocol.c b/lib/bt/host/bluedroid/stack/gatt/att_protocol.c new file mode 100644 index 00000000..310a9756 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/gatt/att_protocol.c @@ -0,0 +1,638 @@ +/****************************************************************************** + * + * Copyright (C) 2008-2014 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * this file contains ATT protocol functions + * + ******************************************************************************/ + +#include "common/bt_target.h" +#include "osi/allocator.h" + +#if BLE_INCLUDED == TRUE + +#include "gatt_int.h" +#include "stack/l2c_api.h" + +#define GATT_HDR_FIND_TYPE_VALUE_LEN 21 +#define GATT_OP_CODE_SIZE 1 +/********************************************************************** +** ATT protocl message building utility * +***********************************************************************/ +/******************************************************************************* +** +** Function attp_build_mtu_exec_cmd +** +** Description Build a exchange MTU request +** +** Returns None. +** +*******************************************************************************/ +BT_HDR *attp_build_mtu_cmd(UINT8 op_code, UINT16 rx_mtu) +{ + BT_HDR *p_buf = NULL; + UINT8 *p; + + if ((p_buf = (BT_HDR *)osi_malloc(sizeof(BT_HDR) + GATT_HDR_SIZE + L2CAP_MIN_OFFSET)) != NULL) { + p = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET; + + UINT8_TO_STREAM (p, op_code); + UINT16_TO_STREAM (p, rx_mtu); + + p_buf->offset = L2CAP_MIN_OFFSET; + p_buf->len = GATT_HDR_SIZE; /* opcode + 2 bytes mtu */ + } + return p_buf; +} +/******************************************************************************* +** +** Function attp_build_exec_write_cmd +** +** Description Build a execute write request or response. +** +** Returns None. +** +*******************************************************************************/ +BT_HDR *attp_build_exec_write_cmd (UINT8 op_code, UINT8 flag) +{ + BT_HDR *p_buf = NULL; + UINT8 *p; + + if ((p_buf = (BT_HDR *)osi_malloc(GATT_DATA_BUF_SIZE)) != NULL) { + p = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET; + + p_buf->offset = L2CAP_MIN_OFFSET; + p_buf->len = GATT_OP_CODE_SIZE; + + UINT8_TO_STREAM (p, op_code); + + if (op_code == GATT_REQ_EXEC_WRITE) { + flag &= GATT_PREP_WRITE_EXEC; + UINT8_TO_STREAM (p, flag); + p_buf->len += 1; + } + + } + + return p_buf; +} + +/******************************************************************************* +** +** Function attp_build_err_cmd +** +** Description Build a exchange MTU request +** +** Returns None. +** +*******************************************************************************/ +BT_HDR *attp_build_err_cmd(UINT8 cmd_code, UINT16 err_handle, UINT8 reason) +{ + BT_HDR *p_buf = NULL; + UINT8 *p; + + if ((p_buf = (BT_HDR *)osi_malloc(sizeof(BT_HDR) + L2CAP_MIN_OFFSET + 5)) != NULL) { + p = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET; + + UINT8_TO_STREAM (p, GATT_RSP_ERROR); + UINT8_TO_STREAM (p, cmd_code); + UINT16_TO_STREAM(p, err_handle); + UINT8_TO_STREAM (p, reason); + + p_buf->offset = L2CAP_MIN_OFFSET; + /* GATT_HDR_SIZE (1B ERR_RSP op code+ 2B handle) + 1B cmd_op_code + 1B status */ + p_buf->len = GATT_HDR_SIZE + 1 + 1; + } + return p_buf; +} +/******************************************************************************* +** +** Function attp_build_browse_cmd +** +** Description Build a read information request or read by type request +** +** Returns None. +** +*******************************************************************************/ +BT_HDR *attp_build_browse_cmd(UINT8 op_code, UINT16 s_hdl, UINT16 e_hdl, tBT_UUID uuid) +{ + BT_HDR *p_buf = NULL; + UINT8 *p; + /* length of ATT_READ_BY_TYPE_REQ PDU: opcode(1) + start_handle (2) + end_handle (2) + uuid (2 or 16) */ + const UINT8 payload_size = 1 + 2 + 2 + ((uuid.len == LEN_UUID_16) ? LEN_UUID_16 : LEN_UUID_128); + + if ((p_buf = (BT_HDR *)osi_malloc(sizeof(BT_HDR) + payload_size + L2CAP_MIN_OFFSET)) != NULL) { + p = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET; + /* Describe the built message location and size */ + p_buf->offset = L2CAP_MIN_OFFSET; + p_buf->len = GATT_OP_CODE_SIZE + 4; + + UINT8_TO_STREAM (p, op_code); + UINT16_TO_STREAM (p, s_hdl); + UINT16_TO_STREAM (p, e_hdl); + p_buf->len += gatt_build_uuid_to_stream(&p, uuid); + } + + return p_buf; +} +/******************************************************************************* +** +** Function attp_build_read_handles_cmd +** +** Description Build a read by type and value request. +** +** Returns pointer to the command buffer. +** +*******************************************************************************/ +BT_HDR *attp_build_read_by_type_value_cmd (UINT16 payload_size, tGATT_FIND_TYPE_VALUE *p_value_type) +{ + BT_HDR *p_buf = NULL; + UINT8 *p; + UINT16 len = p_value_type->value_len; + + if ((p_buf = (BT_HDR *)osi_malloc((UINT16)(sizeof(BT_HDR) + payload_size + L2CAP_MIN_OFFSET))) != NULL) { + p = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET; + + p_buf->offset = L2CAP_MIN_OFFSET; + p_buf->len = 5; /* opcode + s_handle + e_handle */ + + UINT8_TO_STREAM (p, GATT_REQ_FIND_TYPE_VALUE); + UINT16_TO_STREAM (p, p_value_type->s_handle); + UINT16_TO_STREAM (p, p_value_type->e_handle); + + p_buf->len += gatt_build_uuid_to_stream(&p, p_value_type->uuid); + + if (p_value_type->value_len + p_buf->len > payload_size ) { + len = payload_size - p_buf->len; + } + + memcpy (p, p_value_type->value, len); + p_buf->len += len; + } + + return p_buf; +} +/******************************************************************************* +** +** Function attp_build_read_multi_cmd +** +** Description Build a read multiple request +** +** Returns None. +** +*******************************************************************************/ +BT_HDR *attp_build_read_multi_cmd(UINT8 op_code, UINT16 payload_size, UINT16 num_handle, UINT16 *p_handle) +{ + BT_HDR *p_buf = NULL; + UINT8 *p, i = 0; + + if ((p_buf = (BT_HDR *)osi_malloc((UINT16)(sizeof(BT_HDR) + num_handle * 2 + 1 + L2CAP_MIN_OFFSET))) != NULL) { + p = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET; + + p_buf->offset = L2CAP_MIN_OFFSET; + p_buf->len = 1; + + UINT8_TO_STREAM (p, op_code); + + for (i = 0; i < num_handle && p_buf->len + 2 <= payload_size; i ++) { + UINT16_TO_STREAM (p, *(p_handle + i)); + p_buf->len += 2; + } + } + + return p_buf; +} +/******************************************************************************* +** +** Function attp_build_handle_cmd +** +** Description Build a read /read blob request +** +** Returns None. +** +*******************************************************************************/ +BT_HDR *attp_build_handle_cmd(UINT8 op_code, UINT16 handle, UINT16 offset) +{ + BT_HDR *p_buf = NULL; + UINT8 *p; + + if ((p_buf = (BT_HDR *)osi_malloc(sizeof(BT_HDR) + 5 + L2CAP_MIN_OFFSET)) != NULL) { + p = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET; + + p_buf->offset = L2CAP_MIN_OFFSET; + + UINT8_TO_STREAM (p, op_code); + p_buf->len = 1; + + UINT16_TO_STREAM (p, handle); + p_buf->len += 2; + + if (op_code == GATT_REQ_READ_BLOB) { + UINT16_TO_STREAM (p, offset); + p_buf->len += 2; + } + + } + + return p_buf; +} +/******************************************************************************* +** +** Function attp_build_opcode_cmd +** +** Description Build a request/response with opcode only. +** +** Returns None. +** +*******************************************************************************/ +BT_HDR *attp_build_opcode_cmd(UINT8 op_code) +{ + BT_HDR *p_buf = NULL; + UINT8 *p; + + if ((p_buf = (BT_HDR *)osi_malloc(sizeof(BT_HDR) + 1 + L2CAP_MIN_OFFSET)) != NULL) { + p = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET; + p_buf->offset = L2CAP_MIN_OFFSET; + + UINT8_TO_STREAM (p, op_code); + p_buf->len = 1; + } + + return p_buf; +} +/******************************************************************************* +** +** Function attp_build_value_cmd +** +** Description Build a attribute value request +** +** Returns None. +** +*******************************************************************************/ +BT_HDR *attp_build_value_cmd (UINT16 payload_size, UINT8 op_code, UINT16 handle, + UINT16 offset, UINT16 len, UINT8 *p_data) +{ + BT_HDR *p_buf = NULL; + UINT8 *p, *pp, pair_len, *p_pair_len; + + if ((p_buf = (BT_HDR *)osi_malloc((UINT16)(sizeof(BT_HDR) + payload_size + L2CAP_MIN_OFFSET))) != NULL) { + p = pp = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET; + + UINT8_TO_STREAM (p, op_code); + p_buf->offset = L2CAP_MIN_OFFSET; + p_buf->len = 1; + + if (op_code == GATT_RSP_READ_BY_TYPE) { + p_pair_len = p; + pair_len = len + 2; + UINT8_TO_STREAM (p, pair_len); + p_buf->len += 1; + } + if (op_code != GATT_RSP_READ_BLOB && op_code != GATT_RSP_READ && op_code != GATT_HANDLE_MULTI_VALUE_NOTIF) { + UINT16_TO_STREAM (p, handle); + p_buf->len += 2; + } + + if (op_code == GATT_REQ_PREPARE_WRITE || op_code == GATT_RSP_PREPARE_WRITE ) { + UINT16_TO_STREAM (p, offset); + p_buf->len += 2; + } + + if(payload_size < GATT_DEF_BLE_MTU_SIZE || payload_size > GATT_MAX_MTU_SIZE) { + GATT_TRACE_ERROR("invalid payload_size %d", payload_size); + osi_free(p_buf); + return NULL; + } + + if (len > 0 && p_data != NULL) { + /* ensure data not exceed MTU size */ + if (payload_size - p_buf->len < len) { + len = payload_size - p_buf->len; + /* update handle value pair length */ + if (op_code == GATT_RSP_READ_BY_TYPE) { + *p_pair_len = (len + 2); + } + + GATT_TRACE_WARNING("attribute value too long, to be truncated to %d", len); + } + + ARRAY_TO_STREAM (p, p_data, len); + p_buf->len += len; + } + } + return p_buf; +} + +/******************************************************************************* +** +** Function attp_send_msg_to_l2cap +** +** Description Send message to L2CAP. +** +*******************************************************************************/ +tGATT_STATUS attp_send_msg_to_l2cap(tGATT_TCB *p_tcb, BT_HDR *p_toL2CAP) +{ + UINT16 l2cap_ret; + + + if (p_tcb->att_lcid == L2CAP_ATT_CID) { + l2cap_ret = L2CA_SendFixedChnlData (L2CAP_ATT_CID, p_tcb->peer_bda, p_toL2CAP); + } else { +#if (CLASSIC_BT_INCLUDED == TRUE) + l2cap_ret = (UINT16) L2CA_DataWrite (p_tcb->att_lcid, p_toL2CAP); +#else + l2cap_ret = L2CAP_DW_FAILED; +#endif ///CLASSIC_BT_INCLUDED == TRUE + } + + if (l2cap_ret == L2CAP_DW_FAILED) { + GATT_TRACE_DEBUG("ATT failed to pass msg:0x%0x to L2CAP", + *((UINT8 *)(p_toL2CAP + 1) + p_toL2CAP->offset)); + return GATT_INTERNAL_ERROR; + } else if (l2cap_ret == L2CAP_DW_CONGESTED) { + GATT_TRACE_DEBUG("ATT congested, message accepted"); + return GATT_CONGESTED; + } + return GATT_SUCCESS; +} + +/******************************************************************************* +** +** Function attp_build_sr_msg +** +** Description Build ATT Server PDUs. +** +*******************************************************************************/ +BT_HDR *attp_build_sr_msg(tGATT_TCB *p_tcb, UINT8 op_code, tGATT_SR_MSG *p_msg) +{ + BT_HDR *p_cmd = NULL; + UINT16 offset = 0; + + switch (op_code) { + case GATT_RSP_READ_BLOB: + case GATT_RSP_PREPARE_WRITE: + case GATT_RSP_READ_BY_TYPE: + case GATT_RSP_READ: + case GATT_HANDLE_VALUE_NOTIF: + case GATT_HANDLE_VALUE_IND: + case GATT_HANDLE_MULTI_VALUE_NOTIF: + case GATT_RSP_ERROR: + case GATT_RSP_MTU: + /* Need to check the validation of parameter p_msg*/ + if (p_msg == NULL) { + GATT_TRACE_ERROR("Invalid parameters in %s, op_code=0x%x, the p_msg should not be NULL.", __func__, op_code); + return NULL; + } + break; + + default: + break; + } + + switch (op_code) { + case GATT_RSP_READ_BLOB: + case GATT_RSP_PREPARE_WRITE: + GATT_TRACE_EVENT ("ATT_RSP_READ_BLOB/GATT_RSP_PREPARE_WRITE: len = %d offset = %d", + p_msg->attr_value.len, p_msg->attr_value.offset); + offset = p_msg->attr_value.offset; + /* Coverity: [FALSE-POSITIVE error] intended fall through */ + /* Missing break statement between cases in switch statement */ + /* fall through */ + case GATT_RSP_READ_BY_TYPE: + case GATT_RSP_READ: + case GATT_HANDLE_VALUE_NOTIF: + case GATT_HANDLE_VALUE_IND: + case GATT_HANDLE_MULTI_VALUE_NOTIF: + p_cmd = attp_build_value_cmd(p_tcb->payload_size, + op_code, + p_msg->attr_value.handle, + offset, + p_msg->attr_value.len, + p_msg->attr_value.value); + break; + + case GATT_RSP_WRITE: + p_cmd = attp_build_opcode_cmd(op_code); + break; + + case GATT_RSP_ERROR: + p_cmd = attp_build_err_cmd(p_msg->error.cmd_code, p_msg->error.handle, p_msg->error.reason); + break; + + case GATT_RSP_EXEC_WRITE: + p_cmd = attp_build_exec_write_cmd(op_code, 0); + break; + + case GATT_RSP_MTU: + p_cmd = attp_build_mtu_cmd(op_code, p_msg->mtu); + break; + + default: + GATT_TRACE_DEBUG("attp_build_sr_msg: unknown op code = %d", op_code); + break; + } + + if (!p_cmd) { + GATT_TRACE_ERROR("No resources"); + } + + return p_cmd; +} + +/******************************************************************************* +** +** Function attp_send_sr_msg +** +** Description This function sends the server response or indication message +** to client. +** +** Parameter p_tcb: pointer to the connecton control block. +** p_msg: pointer to message parameters structure. +** +** Returns GATT_SUCCESS if successfully sent; otherwise error code. +** +** +*******************************************************************************/ +tGATT_STATUS attp_send_sr_msg (tGATT_TCB *p_tcb, BT_HDR *p_msg) +{ + tGATT_STATUS cmd_sent = GATT_NO_RESOURCES; + + if (p_tcb != NULL) { + if (p_msg != NULL) { + p_msg->offset = L2CAP_MIN_OFFSET; + cmd_sent = attp_send_msg_to_l2cap (p_tcb, p_msg); + } + } + return cmd_sent; +} + +/******************************************************************************* +** +** Function attp_cl_send_cmd +** +** Description Send a ATT command or enqueue it. +** +** Returns GATT_SUCCESS if command sent +** GATT_CONGESTED if command sent but channel congested +** GATT_CMD_STARTED if command queue up in GATT +** GATT_ERROR if command sending failure +** +*******************************************************************************/ +tGATT_STATUS attp_cl_send_cmd(tGATT_TCB *p_tcb, UINT16 clcb_idx, UINT8 cmd_code, BT_HDR *p_cmd) +{ + tGATT_STATUS att_ret = GATT_SUCCESS; + + if (p_tcb != NULL) { + cmd_code &= ~GATT_AUTH_SIGN_MASK; + + /* no pending request or value confirmation */ + if (p_tcb->pending_cl_req == p_tcb->next_slot_inq || + cmd_code == GATT_HANDLE_VALUE_CONF) { + att_ret = attp_send_msg_to_l2cap(p_tcb, p_cmd); + if (att_ret == GATT_CONGESTED || att_ret == GATT_SUCCESS) { + /* do not enq cmd if handle value confirmation or set request */ + if (cmd_code != GATT_HANDLE_VALUE_CONF && cmd_code != GATT_CMD_WRITE) { + gatt_start_rsp_timer (clcb_idx); + gatt_cmd_enq(p_tcb, clcb_idx, FALSE, cmd_code, NULL); + } + } else { + att_ret = GATT_INTERNAL_ERROR; + } + } else { + att_ret = GATT_CMD_STARTED; + gatt_cmd_enq(p_tcb, clcb_idx, TRUE, cmd_code, p_cmd); + } + } else { + att_ret = GATT_ERROR; + } + + return att_ret; +} +/******************************************************************************* +** +** Function attp_send_cl_msg +** +** Description This function sends the client request or confirmation message +** to server. +** +** Parameter p_tcb: pointer to the connection control block. +** clcb_idx: clcb index +** op_code: message op code. +** p_msg: pointer to message parameters structure. +** +** Returns GATT_SUCCESS if successfully sent; otherwise error code. +** +** +*******************************************************************************/ +tGATT_STATUS attp_send_cl_msg (tGATT_TCB *p_tcb, UINT16 clcb_idx, UINT8 op_code, tGATT_CL_MSG *p_msg) +{ + tGATT_STATUS status = GATT_NO_RESOURCES; + BT_HDR *p_cmd = NULL; + UINT16 offset = 0, handle; + + if (p_tcb != NULL) { + switch (op_code) { + case GATT_REQ_MTU: + if (p_msg->mtu <= GATT_MAX_MTU_SIZE) { + p_tcb->payload_size = p_msg->mtu; + p_cmd = attp_build_mtu_cmd(GATT_REQ_MTU, p_msg->mtu); + } else { + status = GATT_ILLEGAL_PARAMETER; + } + break; + + case GATT_REQ_FIND_INFO: + case GATT_REQ_READ_BY_TYPE: + case GATT_REQ_READ_BY_GRP_TYPE: + if (GATT_HANDLE_IS_VALID (p_msg->browse.s_handle) && + GATT_HANDLE_IS_VALID (p_msg->browse.e_handle) && + p_msg->browse.s_handle <= p_msg->browse.e_handle) { + p_cmd = attp_build_browse_cmd(op_code, + p_msg->browse.s_handle, + p_msg->browse.e_handle, + p_msg->browse.uuid); + } else { + status = GATT_ILLEGAL_PARAMETER; + } + break; + + case GATT_REQ_READ_BLOB: + offset = p_msg->read_blob.offset; + /* fall through */ + case GATT_REQ_READ: + handle = (op_code == GATT_REQ_READ) ? p_msg->handle : p_msg->read_blob.handle; + /* handle checking */ + if (GATT_HANDLE_IS_VALID (handle)) { + p_cmd = attp_build_handle_cmd(op_code, handle, offset); + } else { + status = GATT_ILLEGAL_PARAMETER; + } + break; + + case GATT_HANDLE_VALUE_CONF: + p_cmd = attp_build_opcode_cmd(op_code); + break; + + case GATT_REQ_PREPARE_WRITE: + offset = p_msg->attr_value.offset; + /* fall through */ + case GATT_REQ_WRITE: + case GATT_CMD_WRITE: + case GATT_SIGN_CMD_WRITE: + if (GATT_HANDLE_IS_VALID (p_msg->attr_value.handle)) { + p_cmd = attp_build_value_cmd (p_tcb->payload_size, + op_code, p_msg->attr_value.handle, + offset, + p_msg->attr_value.len, + p_msg->attr_value.value); + } else { + status = GATT_ILLEGAL_PARAMETER; + } + break; + + case GATT_REQ_EXEC_WRITE: + p_cmd = attp_build_exec_write_cmd(op_code, p_msg->exec_write); + break; + + case GATT_REQ_FIND_TYPE_VALUE: + p_cmd = attp_build_read_by_type_value_cmd(p_tcb->payload_size, &p_msg->find_type_value); + break; + + case GATT_REQ_READ_MULTI: + case GATT_REQ_READ_MULTI_VAR: + p_cmd = attp_build_read_multi_cmd(op_code, p_tcb->payload_size, + p_msg->read_multi.num_handles, + p_msg->read_multi.handles); + break; + + default: + break; + } + + if (p_cmd != NULL) { + status = attp_cl_send_cmd(p_tcb, clcb_idx, op_code, p_cmd); + } + + } else { + GATT_TRACE_ERROR("Peer device not connected"); + } + + return status; +} +#endif diff --git a/lib/bt/host/bluedroid/stack/gatt/gatt_api.c b/lib/bt/host/bluedroid/stack/gatt/gatt_api.c new file mode 100644 index 00000000..8d8056f6 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/gatt/gatt_api.c @@ -0,0 +1,1805 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * this file contains GATT interface functions + * + ******************************************************************************/ +#include "common/bt_target.h" + + +#if defined(BTA_GATT_INCLUDED) && (BTA_GATT_INCLUDED == TRUE) + +#include "osi/allocator.h" +#include +#include "stack/gatt_api.h" +#include "gatt_int.h" +#include "stack/l2c_api.h" +#include "btm_int.h" +#include "stack/sdpdefs.h" +#include "stack/sdp_api.h" + +/******************************************************************************* +** +** Function GATT_SetTraceLevel +** +** Description This function sets the trace level. If called with +** a value of 0xFF, it simply returns the current trace level. +** +** Input Parameters: +** level: The level to set the GATT tracing to: +** 0xff-returns the current setting. +** 0-turns off tracing. +** >= 1-Errors. +** >= 2-Warnings. +** >= 3-APIs. +** >= 4-Events. +** >= 5-Debug. +** +** Returns The new or current trace level +** +*******************************************************************************/ +UINT8 GATT_SetTraceLevel (UINT8 new_level) +{ + if (new_level != 0xFF) { + gatt_cb.trace_level = new_level; + } + + return (gatt_cb.trace_level); +} + + +#if (GATTS_INCLUDED == TRUE) +/***************************************************************************** +** +** GATT SERVER API +** +******************************************************************************/ +/******************************************************************************* +** +** Function GATTS_AddHandleRange +** +** Description This function add the allocated handles range for the specifed +** application UUID, service UUID and service instance +** +** Parameter p_hndl_range: pointer to allocated handles information +** +** Returns TRUE if handle range is added successfully; otherwise FALSE. +** +*******************************************************************************/ + +BOOLEAN GATTS_AddHandleRange(tGATTS_HNDL_RANGE *p_hndl_range) +{ + tGATT_HDL_LIST_ELEM *p_buf; + BOOLEAN status = FALSE; + + if ((p_buf = gatt_alloc_hdl_buffer()) != NULL) { + p_buf->asgn_range = *p_hndl_range; + status = gatt_add_an_item_to_list(&gatt_cb.hdl_list_info, p_buf); + } + return status; +} + + +/******************************************************************************* +** +** Function GATTS_NVRegister +** +** Description Application manager calls this function to register for +** NV save callback function. There can be one and only one +** NV save callback function. +** +** Parameter p_cb_info : callback informaiton +** +** Returns TRUE if registered OK, else FALSE +** +*******************************************************************************/ +BOOLEAN GATTS_NVRegister (const tGATT_APPL_INFO *p_cb_info) +{ + BOOLEAN status = FALSE; + if (p_cb_info) { + gatt_cb.cb_info = *p_cb_info; + status = TRUE; + gatt_init_srv_chg(); + } + + return status; +} + +#if GATTS_ROBUST_CACHING_ENABLED +static void gatt_update_for_database_change(void) +{ + UINT8 i; + + gatts_calculate_datebase_hash(gatt_cb.database_hash); + + for (i = 0; i < GATT_MAX_PHY_CHANNEL; i++) { + tGATT_TCB *p_tcb = gatt_get_tcb_by_idx(i); + if (p_tcb && p_tcb->in_use) { + gatt_sr_update_cl_status(p_tcb, false); + } + } +} +#endif /* GATTS_ROBUST_CACHING_ENABLED */ +/******************************************************************************* +** +** Function GATTS_CreateService +** +** Description This function is called to reserve a block of handles for a service. +** +** *** It should be called only once per service instance *** +** +** Parameter gatt_if : application if +** p_svc_uuid : service UUID +** svc_inst : instance of the service inside the application +** num_handles : number of handles needed by the service. +** is_pri : is a primary service or not. +** +** Returns service handle if sucessful, otherwise 0. +** +*******************************************************************************/ +UINT16 GATTS_CreateService (tGATT_IF gatt_if, tBT_UUID *p_svc_uuid, + UINT16 svc_inst, UINT16 num_handles, BOOLEAN is_pri) +{ + + tGATT_HDL_LIST_INFO *p_list_info = &gatt_cb.hdl_list_info; + tGATT_HDL_LIST_ELEM *p_list = NULL; + UINT16 s_hdl = 0; + BOOLEAN save_hdl = FALSE; + tGATTS_PENDING_NEW_SRV_START *p_buf = NULL; + tGATT_REG *p_reg = gatt_get_regcb(gatt_if); + tBT_UUID *p_app_uuid128; + + + GATT_TRACE_API ("GATTS_CreateService\n" ); + + if (p_reg == NULL) { + GATT_TRACE_ERROR ("Inavlid gatt_if=%d\n", gatt_if); + return (0); + } + + p_app_uuid128 = &p_reg->app_uuid128; + + if ((p_list = gatt_find_hdl_buffer_by_app_id(p_app_uuid128, p_svc_uuid, svc_inst)) != NULL) { + s_hdl = p_list->asgn_range.s_handle; + GATT_TRACE_DEBUG ("Service already been created!!\n"); + } else { + if ( (p_svc_uuid->len == LEN_UUID_16) && (p_svc_uuid->uu.uuid16 == UUID_SERVCLASS_GATT_SERVER)) { + s_hdl = gatt_cb.hdl_cfg.gatt_start_hdl; + save_hdl = TRUE; + } else if ((p_svc_uuid->len == LEN_UUID_16) && (p_svc_uuid->uu.uuid16 == UUID_SERVCLASS_GAP_SERVER)) { + s_hdl = gatt_cb.hdl_cfg.gap_start_hdl; + save_hdl = TRUE; + } else { + p_list = p_list_info->p_first; + + if (p_list) { + s_hdl = p_list->asgn_range.e_handle + 1; + } + + if (s_hdl < gatt_cb.hdl_cfg.app_start_hdl) { + + s_hdl = gatt_cb.hdl_cfg.app_start_hdl; + } + save_hdl = TRUE; + } + + /* check for space */ + if (num_handles > (0xFFFF - s_hdl + 1)) { + GATT_TRACE_ERROR ("GATTS_ReserveHandles: no handles, s_hdl: %u needed: %u\n", s_hdl, num_handles); + return (0); + } + + if ( (p_list = gatt_alloc_hdl_buffer()) == NULL) { + /* No free entry */ + GATT_TRACE_ERROR ("GATTS_ReserveHandles: no free handle blocks\n"); + return (0); + } + + p_list->asgn_range.app_uuid128 = *p_app_uuid128; + p_list->asgn_range.svc_uuid = *p_svc_uuid; + p_list->asgn_range.svc_inst = svc_inst; + p_list->asgn_range.s_handle = s_hdl; + p_list->asgn_range.e_handle = s_hdl + num_handles - 1; + p_list->asgn_range.is_primary = is_pri; + + gatt_add_an_item_to_list(p_list_info, p_list); + + if (save_hdl) { + if (gatt_cb.cb_info.p_nv_save_callback) { + (*gatt_cb.cb_info.p_nv_save_callback)(TRUE, &p_list->asgn_range); + } + /* add a pending new service change item to the list */ + if ( (p_buf = gatt_add_pending_new_srv_start(&p_list->asgn_range)) == NULL) { + /* No free entry */ + GATT_TRACE_ERROR ("gatt_add_pending_new_srv_start: no free blocks\n"); + + if (p_list) { + gatt_remove_an_item_from_list(p_list_info, p_list); + gatt_free_attr_value_buffer(p_list); + gatt_free_hdl_buffer(p_list); + } + return (0); + } + + GATT_TRACE_DEBUG ("Add a new srv chg item\n"); + } + } + + if (!gatts_init_service_db(&p_list->svc_db, p_svc_uuid, is_pri, s_hdl , num_handles)) { + GATT_TRACE_ERROR ("GATTS_ReserveHandles: service DB initialization failed\n"); + if (p_list) { + gatt_remove_an_item_from_list(p_list_info, p_list); + gatt_free_attr_value_buffer(p_list); + gatt_free_hdl_buffer(p_list); + } + + if (p_buf) { + osi_free(fixed_queue_try_remove_from_queue(gatt_cb.pending_new_srv_start_q, p_buf)); + } + return (0); + } + + return (s_hdl); +} + +/******************************************************************************* +** +** Function GATTS_AddIncludeService +** +** Description This function is called to add an included service. +** +** Parameter service_handle : To which service this included service is added to. +** include_svc_handle : included service handle. +** +** Returns included service attribute handle. If 0, add included service +** fail. +** +*******************************************************************************/ +UINT16 GATTS_AddIncludeService (UINT16 service_handle, UINT16 include_svc_handle) + +{ + tGATT_HDL_LIST_ELEM *p_decl, *p_incl_decl; + + if ((p_decl = gatt_find_hdl_buffer_by_handle(service_handle)) == NULL) { + GATT_TRACE_DEBUG("Service not created"); + return 0; + } + if ((p_incl_decl = gatt_find_hdl_buffer_by_handle(include_svc_handle)) == NULL) { + GATT_TRACE_DEBUG("Included Service not created"); + return 0; + } + + return gatts_add_included_service(&p_decl->svc_db, + p_incl_decl->asgn_range.s_handle, + p_incl_decl->asgn_range.e_handle, + p_incl_decl->asgn_range.svc_uuid); +} +/******************************************************************************* +** +** Function GATTS_AddCharacteristic +** +** Description This function is called to add a characteristic into a service. +** It will add a characteristic declaration and characteristic +** value declaration into the service database identified by the +** service handle. +** +** Parameter service_handle : To which service this included service is added to. +** char_uuid : Characteristic UUID. +** perm : Characteristic value declaration attribute permission. +** property : Characteristic Properties +** +** Returns Characteristic value declaration attribute handle. 0 if failed. +** +*******************************************************************************/ +UINT16 GATTS_AddCharacteristic (UINT16 service_handle, tBT_UUID *p_char_uuid, + tGATT_PERM perm, tGATT_CHAR_PROP property, + tGATT_ATTR_VAL *attr_val, tGATTS_ATTR_CONTROL *control) +{ + tGATT_HDL_LIST_ELEM *p_decl; + + if ((p_decl = gatt_find_hdl_buffer_by_handle(service_handle)) == NULL) { + GATT_TRACE_DEBUG("Service not created\n"); + return 0; + } + /* data validity checking */ + if ( ((property & GATT_CHAR_PROP_BIT_AUTH) && !(perm & GATT_WRITE_SIGNED_PERM)) || + ((perm & GATT_WRITE_SIGNED_PERM) && !(property & GATT_CHAR_PROP_BIT_AUTH)) ) { + GATT_TRACE_DEBUG("Invalid configuration property=0x%x perm=0x%x\n ", property, perm); + return 0; + } + + return gatts_add_characteristic(&p_decl->svc_db, + perm, + property, + p_char_uuid, + attr_val, control); +} +/******************************************************************************* +** +** Function GATTS_AddCharDescriptor +** +** Description This function is called to add a characteristic descriptor +** into a service database. Add descriptor should follow add char +** to which it belongs, and next add char should be done only +** after all add descriptors for the previous char. +** +** Parameter service_handle : To which service this characteristic descriptor +** is added to. +** perm : Characteristic value declaration attribute +** permission. +** p_descr_uuid : Characteristic descriptor UUID +** +** Returns Characteristic descriptor attribute handle. 0 if add +** characteristic descriptor failed. +** +*******************************************************************************/ +UINT16 GATTS_AddCharDescriptor (UINT16 service_handle, + tGATT_PERM perm, + tBT_UUID *p_descr_uuid, tGATT_ATTR_VAL *attr_val, tGATTS_ATTR_CONTROL *control) +{ + tGATT_HDL_LIST_ELEM *p_decl; + + if ((p_decl = gatt_find_hdl_buffer_by_handle(service_handle)) == NULL) { + GATT_TRACE_DEBUG("Service not created"); + return 0; + } + if (p_descr_uuid == NULL || + (p_descr_uuid->len != LEN_UUID_128 && p_descr_uuid->len != LEN_UUID_16 + && p_descr_uuid->len != LEN_UUID_32)) { + GATT_TRACE_DEBUG("Illegal parameter"); + return 0; + } + + return gatts_add_char_descr(&p_decl->svc_db, + perm, + p_descr_uuid, + attr_val, control); + +} +/******************************************************************************* +** +** Function GATTS_DeleteService +** +** Description This function is called to delete a service. +** +** Parameter gatt_if : application interface +** p_svc_uuid : service UUID +** svc_inst : instance of the service inside the application +** +** Returns TRUE if operation succeed, FALSE if handle block was not found. +** +*******************************************************************************/ +BOOLEAN GATTS_DeleteService (tGATT_IF gatt_if, tBT_UUID *p_svc_uuid, UINT16 svc_inst) +{ + + tGATT_HDL_LIST_INFO *p_list_info = &gatt_cb.hdl_list_info; + tGATT_HDL_LIST_ELEM *p_list = NULL; + UINT8 i_sreg; + tGATTS_PENDING_NEW_SRV_START *p_buf; + tGATT_REG *p_reg = gatt_get_regcb(gatt_if); + tBT_UUID *p_app_uuid128; + + GATT_TRACE_DEBUG ("GATTS_DeleteService"); + + if (p_reg == NULL) { + GATT_TRACE_ERROR ("Application not foud"); + return (FALSE); + } + p_app_uuid128 = &p_reg->app_uuid128; + + if ((p_list = gatt_find_hdl_buffer_by_app_id(p_app_uuid128, p_svc_uuid, svc_inst)) == NULL) { + GATT_TRACE_ERROR ("No Service found"); + return (FALSE); + } + + if ( (p_buf = gatt_sr_is_new_srv_chg(&p_list->asgn_range.app_uuid128, + &p_list->asgn_range.svc_uuid, + p_list->asgn_range.svc_inst)) != NULL) { + GATT_TRACE_DEBUG ("Delete a new service changed item - the service has not yet started"); + osi_free(fixed_queue_try_remove_from_queue(gatt_cb.pending_new_srv_start_q, p_buf)); + } else { +#if GATTS_ROBUST_CACHING_ENABLED + gatt_update_for_database_change(); +#endif /* GATTS_ROBUST_CACHING_ENABLED */ + if (gatt_cb.srv_chg_mode == GATTS_SEND_SERVICE_CHANGE_AUTO) { + gatt_proc_srv_chg(); + } + } + + if ((i_sreg = gatt_sr_find_i_rcb_by_app_id (p_app_uuid128, + p_svc_uuid, + svc_inst)) != GATT_MAX_SR_PROFILES) { + GATTS_StopService(gatt_cb.sr_reg[i_sreg].s_hdl); + } + + GATT_TRACE_DEBUG ("released handles s_hdl=%u e_hdl=%u", + p_list->asgn_range.s_handle , p_list->asgn_range.e_handle ); + + if ( (p_list->asgn_range.s_handle >= gatt_cb.hdl_cfg.app_start_hdl) + && gatt_cb.cb_info.p_nv_save_callback) { + (*gatt_cb.cb_info.p_nv_save_callback)(FALSE, &p_list->asgn_range); + } + + gatt_remove_an_item_from_list(p_list_info, p_list); + gatt_free_attr_value_buffer(p_list); + gatt_free_hdl_buffer(p_list); + + return (TRUE); +} + +/******************************************************************************* +** +** Function GATTS_StartService +** +** Description This function is called to start a service with GATT +** +** Parameter gatt_if : service handle. +** p_cback : application service callback functions. +** sup_transport : supported transport(s) for this primary service +** +** return GATT_SUCCESS if successfully started; otherwise error code. +** +*******************************************************************************/ +tGATT_STATUS GATTS_StartService (tGATT_IF gatt_if, UINT16 service_handle, + tGATT_TRANSPORT sup_transport) +{ + tGATT_SR_REG *p_sreg; + tGATT_HDL_LIST_ELEM *p_list = NULL; + UINT8 i_sreg; +#if (SDP_INCLUDED == TRUE && CLASSIC_BT_GATT_INCLUDED == TRUE) + tBT_UUID *p_uuid; +#endif ///SDP_INCLUDED == TRUE && CLASSIC_BT_GATT_INCLUDED == TRUE + tGATT_REG *p_reg = gatt_get_regcb(gatt_if); + + tGATTS_PENDING_NEW_SRV_START *p_buf; + + GATT_TRACE_API ("GATTS_StartService"); + + if (p_reg == NULL) { + /* Not found */ + GATT_TRACE_ERROR ("Application not found "); + return GATT_NOT_FOUND; + } + + if ((p_list = gatt_find_hdl_buffer_by_handle(service_handle)) == NULL) { + /* Not found */ + GATT_TRACE_ERROR ("no service found"); + return GATT_NOT_FOUND; + } + + if (gatt_sr_find_i_rcb_by_app_id (&p_list->asgn_range.app_uuid128, + &p_list->asgn_range.svc_uuid, + p_list->asgn_range.svc_inst) != GATT_MAX_SR_PROFILES) { + GATT_TRACE_ERROR ("Duplicate Service start - Service already started"); + return GATT_SERVICE_STARTED; + } + + /*this is a new application servoce start */ + if ((i_sreg = gatt_sr_alloc_rcb(p_list)) == GATT_MAX_SR_PROFILES) { + GATT_TRACE_ERROR ("GATTS_StartService: no free server registration block"); + return GATT_NO_RESOURCES; + } + + p_sreg = &gatt_cb.sr_reg[i_sreg]; + p_sreg->gatt_if = gatt_if; + + switch (sup_transport) { + case GATT_TRANSPORT_BR_EDR: + case GATT_TRANSPORT_LE_BR_EDR: + if (p_sreg->type == GATT_UUID_PRI_SERVICE) { +#if (SDP_INCLUDED == TRUE && CLASSIC_BT_GATT_INCLUDED == TRUE) + p_uuid = gatts_get_service_uuid (p_sreg->p_db); + p_sreg->sdp_handle = gatt_add_sdp_record(p_uuid, p_sreg->s_hdl, p_sreg->e_hdl); +#endif ///SDP_INCLUDED == TRUE && CLASSIC_BT_GATT_INCLUDED == TRUE + } + break; + default: + break; + } + + gatts_update_srv_list_elem(i_sreg, p_sreg->s_hdl, + p_list->asgn_range.is_primary); + + gatt_add_a_srv_to_list(&gatt_cb.srv_list_info, &gatt_cb.srv_list[i_sreg]); + + GATT_TRACE_DEBUG ("allocated i_sreg=%d\n", i_sreg); + + GATT_TRACE_DEBUG ("s_hdl=%d e_hdl=%d type=0x%x svc_inst=%d sdp_hdl=0x%x\n", + p_sreg->s_hdl, p_sreg->e_hdl, + p_sreg->type, p_sreg->service_instance, + p_sreg->sdp_handle); + + + if ( (p_buf = gatt_sr_is_new_srv_chg(&p_list->asgn_range.app_uuid128, + &p_list->asgn_range.svc_uuid, + p_list->asgn_range.svc_inst)) != NULL) { + + #if GATTS_ROBUST_CACHING_ENABLED + gatt_update_for_database_change(); + #endif /* GATTS_ROBUST_CACHING_ENABLED */ + + if (gatt_cb.srv_chg_mode == GATTS_SEND_SERVICE_CHANGE_AUTO) { + gatt_proc_srv_chg(); + } + /* remove the new service element after the srv changed processing is completed*/ + + osi_free(fixed_queue_try_remove_from_queue(gatt_cb.pending_new_srv_start_q, p_buf)); + } + return GATT_SUCCESS; +} + +/******************************************************************************* +** +** Function GATTS_StopService +** +** Description This function is called to stop a service +** +** Parameter service_handle : this is the start handle of a service +** +** Returns None. +** +*******************************************************************************/ +void GATTS_StopService (UINT16 service_handle) +{ + UINT8 ii = gatt_sr_find_i_rcb_by_handle(service_handle); + + GATT_TRACE_API("GATTS_StopService %u", service_handle); + + /* Index 0 is reserved for GATT, and is never stopped */ + if ( (ii > 0) && (ii < GATT_MAX_SR_PROFILES) && (gatt_cb.sr_reg[ii].in_use) ) { +#if(SDP_INCLUDED == TRUE && CLASSIC_BT_GATT_INCLUDED == TRUE) + if (gatt_cb.sr_reg[ii].sdp_handle) { + SDP_DeleteRecord(gatt_cb.sr_reg[ii].sdp_handle); + } +#endif ///SDP_INCLUDED == TRUE && CLASSIC_BT_GATT_INCLUDED == TRUE + gatt_remove_a_srv_from_list(&gatt_cb.srv_list_info, &gatt_cb.srv_list[ii]); + gatt_cb.srv_list[ii].in_use = FALSE; + memset (&gatt_cb.sr_reg[ii], 0, sizeof(tGATT_SR_REG)); + } else { + GATT_TRACE_ERROR("GATTS_StopService service_handle: %u is not in use", service_handle); + } +} +/******************************************************************************* +** +** Function GATTs_HandleValueIndication +** +** Description This function sends a handle value indication to a client. +** +** Parameter conn_id: connection identifier. +** attr_handle: Attribute handle of this handle value indication. +** val_len: Length of the indicated attribute value. +** p_val: Pointer to the indicated attribute value data. +** +** Returns GATT_SUCCESS if successfully sent or queued; otherwise error code. +** +*******************************************************************************/ +tGATT_STATUS GATTS_HandleValueIndication (UINT16 conn_id, UINT16 attr_handle, UINT16 val_len, UINT8 *p_val) +{ + tGATT_STATUS cmd_status = GATT_NO_RESOURCES; + + tGATT_VALUE indication; + BT_HDR *p_msg; + tGATT_IF gatt_if = GATT_GET_GATT_IF(conn_id); + UINT8 tcb_idx = GATT_GET_TCB_IDX(conn_id); + tGATT_REG *p_reg = gatt_get_regcb(gatt_if); + tGATT_TCB *p_tcb = gatt_get_tcb_by_idx(tcb_idx); + + + GATT_TRACE_API ("GATTS_HandleValueIndication"); + if ( (p_reg == NULL) || (p_tcb == NULL)) { + GATT_TRACE_ERROR ("GATTS_HandleValueIndication Unknown conn_id: %u ", conn_id); + return (tGATT_STATUS) GATT_INVALID_CONN_ID; + } + + if (!gatt_check_connection_state_by_tcb(p_tcb)) { + GATT_TRACE_ERROR("connection not established\n"); + return GATT_WRONG_STATE; + } + + if (! GATT_HANDLE_IS_VALID (attr_handle)) { + return GATT_ILLEGAL_PARAMETER; + } + + indication.conn_id = conn_id; + indication.handle = attr_handle; + indication.len = val_len; + memcpy (indication.value, p_val, val_len); + indication.auth_req = GATT_AUTH_REQ_NONE; + + if (GATT_HANDLE_IS_VALID(p_tcb->indicate_handle)) { + /* TODO: need to further check whether deleting pending queue here cause reducing transport performance */ + /* + GATT_TRACE_DEBUG ("Add a pending indication"); + if ((p_buf = gatt_add_pending_ind(p_tcb, &indication)) != NULL) { + cmd_status = GATT_SUCCESS; + } else { + cmd_status = GATT_NO_RESOURCES; + } + */ + return GATT_BUSY; + } else { + + if ( (p_msg = attp_build_sr_msg (p_tcb, GATT_HANDLE_VALUE_IND, (tGATT_SR_MSG *)&indication)) != NULL) { + cmd_status = attp_send_sr_msg (p_tcb, p_msg); + + if (cmd_status == GATT_SUCCESS || cmd_status == GATT_CONGESTED) { + p_tcb->indicate_handle = indication.handle; + gatt_start_conf_timer(p_tcb); + } + } + } + return cmd_status; +} + +/******************************************************************************* +** +** Function GATTS_HandleValueNotification +** +** Description This function sends a handle value notification to a client. +** +** Parameter conn_id: connection identifier. +** attr_handle: Attribute handle of this handle value indication. +** val_len: Length of the indicated attribute value. +** p_val: Pointer to the indicated attribute value data. +** +** Returns GATT_SUCCESS if successfully sent; otherwise error code. +** +*******************************************************************************/ +tGATT_STATUS GATTS_HandleValueNotification (UINT16 conn_id, UINT16 attr_handle, + UINT16 val_len, UINT8 *p_val) +{ + tGATT_STATUS cmd_sent = GATT_ILLEGAL_PARAMETER; + BT_HDR *p_buf; + tGATT_VALUE notif; + tGATT_IF gatt_if = GATT_GET_GATT_IF(conn_id); + UINT8 tcb_idx = GATT_GET_TCB_IDX(conn_id); + tGATT_REG *p_reg = gatt_get_regcb(gatt_if); + tGATT_TCB *p_tcb = gatt_get_tcb_by_idx(tcb_idx); + + GATT_TRACE_API ("GATTS_HandleValueNotification"); + + if ( (p_reg == NULL) || (p_tcb == NULL)) { + GATT_TRACE_ERROR ("GATTS_HandleValueNotification Unknown conn_id: %u \n", conn_id); + return (tGATT_STATUS) GATT_INVALID_CONN_ID; + } + + if (!gatt_check_connection_state_by_tcb(p_tcb)) { + GATT_TRACE_ERROR("connection not established\n"); + return GATT_WRONG_STATE; + } + + if (GATT_HANDLE_IS_VALID (attr_handle)) { + notif.handle = attr_handle; + notif.len = val_len; + memcpy (notif.value, p_val, val_len); + notif.auth_req = GATT_AUTH_REQ_NONE; + + if ((p_buf = attp_build_sr_msg (p_tcb, GATT_HANDLE_VALUE_NOTIF, (tGATT_SR_MSG *)¬if)) + != NULL) { + cmd_sent = attp_send_sr_msg (p_tcb, p_buf); + } else { + cmd_sent = GATT_NO_RESOURCES; + } + } + return cmd_sent; +} + +/******************************************************************************* +** +** Function GATTS_SendRsp +** +** Description This function sends the server response to client. +** +** Parameter conn_id: connection identifier. +** trans_id: transaction id +** status: response status +** p_msg: pointer to message parameters structure. +** +** Returns GATT_SUCCESS if successfully sent; otherwise error code. +** +*******************************************************************************/ +tGATT_STATUS GATTS_SendRsp (UINT16 conn_id, UINT32 trans_id, + tGATT_STATUS status, tGATTS_RSP *p_msg) +{ + tGATT_STATUS cmd_sent = GATT_ILLEGAL_PARAMETER; + tGATT_IF gatt_if = GATT_GET_GATT_IF(conn_id); + UINT8 tcb_idx = GATT_GET_TCB_IDX(conn_id); + tGATT_REG *p_reg = gatt_get_regcb(gatt_if); + tGATT_TCB *p_tcb = gatt_get_tcb_by_idx(tcb_idx); + + GATT_TRACE_API ("GATTS_SendRsp: conn_id: %u trans_id: %u Status: 0x%04x\n", + conn_id, trans_id, status); + + if ( (p_reg == NULL) || (p_tcb == NULL)) { + GATT_TRACE_ERROR ("GATTS_SendRsp Unknown conn_id: %u\n", conn_id); + return (tGATT_STATUS) GATT_INVALID_CONN_ID; + } + + if (!gatt_check_connection_state_by_tcb(p_tcb)) { + GATT_TRACE_ERROR("connection not established\n"); + return GATT_WRONG_STATE; + } + + if (p_tcb->sr_cmd.trans_id != trans_id) { + GATT_TRACE_ERROR ("GATTS_SendRsp conn_id: %u waiting for op_code = %02x\n", + conn_id, p_tcb->sr_cmd.op_code); + + return (GATT_WRONG_STATE); + } + /* Process App response */ + cmd_sent = gatt_sr_process_app_rsp (p_tcb, gatt_if, trans_id, p_tcb->sr_cmd.op_code, status, p_msg); + + return cmd_sent; +} + + +/******************************************************************************* +** +** Function GATTS_SetAttributeValue +** +** Description This function sends to set the attribute value . +** +** Parameter attr_handle:the attribute handle +** length: the attribute length +** value: the value to be set to the attribute in the database +** +** Returns GATT_SUCCESS if successfully sent; otherwise error code. +** +*******************************************************************************/ +tGATT_STATUS GATTS_SetAttributeValue(UINT16 attr_handle, UINT16 length, UINT8 *value) +{ + tGATT_STATUS status; + tGATT_HDL_LIST_ELEM *p_decl = NULL; + + GATT_TRACE_DEBUG("GATTS_SetAttributeValue: attr_handle: %u length: %u \n", + attr_handle, length); + if (length <= 0){ + return GATT_INVALID_ATTR_LEN; + } + if ((p_decl = gatt_find_hdl_buffer_by_attr_handle(attr_handle)) == NULL) { + GATT_TRACE_DEBUG("Service not created\n"); + return GATT_INVALID_HANDLE; + } + + status = gatts_set_attribute_value(&p_decl->svc_db, attr_handle, length, value); + return status; + +} + + +/******************************************************************************* +** +** Function GATTS_GetAttributeValue +** +** Description This function sends to get the attribute value . +** +** Parameter attr_handle: the attribute handle +** length:the attribute value length in the database +** value: the attribute value out put +** +** Returns GATT_SUCCESS if successfully sent; otherwise error code. +** +*******************************************************************************/ +tGATT_STATUS GATTS_GetAttributeValue(UINT16 attr_handle, UINT16 *length, UINT8 **value) +{ + tGATT_STATUS status; + tGATT_HDL_LIST_ELEM *p_decl; + + GATT_TRACE_DEBUG("GATTS_GetAttributeValue: attr_handle: %u\n", + attr_handle); + + if ((p_decl = gatt_find_hdl_buffer_by_attr_handle(attr_handle)) == NULL) { + GATT_TRACE_ERROR("Service not created\n"); + *length = 0; + return GATT_INVALID_HANDLE; + } + + status = gatts_get_attribute_value(&p_decl->svc_db, attr_handle, length, value); + return status; +} + +/******************************************************************************* +** +** Function GATTS_GetAttributeValueInternal +** +** Description This function sends to get the attribute value of internal gatt and gap service. +** +** Parameter attr_handle: the attribute handle +** length:the attribute value length in the database +** value: the attribute value out put +* +** +** Returns tGATT_STATUS - GATT status indicating success or failure in +** retrieving the attribute value. +** +*******************************************************************************/ +tGATT_STATUS GATTS_GetAttributeValueInternal(UINT16 attr_handle, UINT16 *length, UINT8 **value) +{ + return gatts_get_attr_value_internal(attr_handle, length, value); +} +#endif ///GATTS_INCLUDED == TRUE + + +#if (GATTC_INCLUDED == TRUE) +/*******************************************************************************/ +/* GATT Profile Srvr Functions */ +/*******************************************************************************/ + +/*******************************************************************************/ +/* */ +/* GATT CLIENT APIs */ +/* */ +/*******************************************************************************/ + + +/******************************************************************************* +** +** Function GATTC_ConfigureMTU +** +** Description This function is called to configure the ATT MTU size. +** +** Parameters conn_id: connection identifier. +** mtu - attribute MTU size.. +** +** Returns GATT_SUCCESS if command started successfully. +** +*******************************************************************************/ +tGATT_STATUS GATTC_ConfigureMTU (UINT16 conn_id) +{ + UINT8 ret = GATT_NO_RESOURCES; + tGATT_IF gatt_if = GATT_GET_GATT_IF(conn_id); + UINT8 tcb_idx = GATT_GET_TCB_IDX(conn_id); + tGATT_TCB *p_tcb = gatt_get_tcb_by_idx(tcb_idx); + tGATT_REG *p_reg = gatt_get_regcb(gatt_if); + + tGATT_CLCB *p_clcb; + uint16_t mtu = gatt_get_local_mtu(); + + GATT_TRACE_API ("GATTC_ConfigureMTU conn_id=%d mtu=%d", conn_id, mtu ); + + if ( (p_tcb == NULL) || (p_reg == NULL) || (mtu < GATT_DEF_BLE_MTU_SIZE) || (mtu > GATT_MAX_MTU_SIZE)) { + return GATT_ILLEGAL_PARAMETER; + } + + if (!gatt_check_connection_state_by_tcb(p_tcb)) { + GATT_TRACE_ERROR("connection not established\n"); + return GATT_ERROR; + } + + /* Validate that the link is BLE, not BR/EDR */ + if (p_tcb->transport != BT_TRANSPORT_LE) { + return GATT_ERROR; + } + + if (gatt_is_clcb_allocated(conn_id)) { + GATT_TRACE_ERROR("GATTC_ConfigureMTU GATT_BUSY conn_id = %d", conn_id); + return GATT_BUSY; + } + + if ((p_clcb = gatt_clcb_alloc(conn_id)) != NULL) { + p_clcb->p_tcb->payload_size = mtu; + p_clcb->operation = GATTC_OPTYPE_CONFIG; + + ret = attp_send_cl_msg (p_clcb->p_tcb, p_clcb->clcb_idx, GATT_REQ_MTU, (tGATT_CL_MSG *)&mtu); + } + + return ret; +} + +/******************************************************************************* +** +** Function GATTC_Discover +** +** Description This function is called to do a discovery procedure on ATT server. +** +** Parameters conn_id: connection identifier. +** disc_type:discovery type. +** p_param: parameters of discovery requirement. +** +** Returns GATT_SUCCESS if command received/sent successfully. +** +*******************************************************************************/ +tGATT_STATUS GATTC_Discover (UINT16 conn_id, tGATT_DISC_TYPE disc_type, + tGATT_DISC_PARAM *p_param) +{ + tGATT_STATUS status = GATT_SUCCESS; + tGATT_CLCB *p_clcb; + tGATT_IF gatt_if = GATT_GET_GATT_IF(conn_id); + UINT8 tcb_idx = GATT_GET_TCB_IDX(conn_id); + tGATT_TCB *p_tcb = gatt_get_tcb_by_idx(tcb_idx); + tGATT_REG *p_reg = gatt_get_regcb(gatt_if); + + + GATT_TRACE_API ("GATTC_Discover conn_id=%d disc_type=%d", conn_id, disc_type); + + if ( (p_tcb == NULL) || (p_reg == NULL) || (p_param == NULL) || + (disc_type >= GATT_DISC_MAX)) { + GATT_TRACE_ERROR("GATTC_Discover Illegal param: disc_type %d conn_id = %d", disc_type, conn_id); + return GATT_ILLEGAL_PARAMETER; + } + + if (!gatt_check_connection_state_by_tcb(p_tcb)) { + GATT_TRACE_ERROR("connection not established\n"); + return GATT_ERROR; + } + + if (gatt_is_clcb_allocated(conn_id)) { + GATT_TRACE_ERROR("GATTC_Discover GATT_BUSY conn_id = %d", conn_id); + return GATT_BUSY; + } + + + if ((p_clcb = gatt_clcb_alloc(conn_id)) != NULL ) { + if (!GATT_HANDLE_IS_VALID(p_param->s_handle) || + !GATT_HANDLE_IS_VALID(p_param->e_handle) || + /* search by type does not have a valid UUID param */ + (disc_type == GATT_DISC_SRVC_BY_UUID && + p_param->service.len == 0)) { + gatt_clcb_dealloc(p_clcb); + return GATT_ILLEGAL_PARAMETER; + } + + p_clcb->operation = GATTC_OPTYPE_DISCOVERY; + p_clcb->op_subtype = disc_type; + p_clcb->s_handle = p_param->s_handle; + p_clcb->e_handle = p_param->e_handle; + p_clcb->uuid = p_param->service; + + gatt_act_discovery(p_clcb); + } else { + status = GATT_NO_RESOURCES; + } + return status; +} + +/******************************************************************************* +** +** Function GATTC_Read +** +** Description This function is called to read the value of an attribute from +** the server. +** +** Parameters conn_id: connection identifier. +** type - attribute read type. +** p_read - read operation parameters. +** +** Returns GATT_SUCCESS if command started successfully. +** +*******************************************************************************/ +tGATT_STATUS GATTC_Read (UINT16 conn_id, tGATT_READ_TYPE type, tGATT_READ_PARAM *p_read) +{ + tGATT_STATUS status = GATT_SUCCESS; + tGATT_CLCB *p_clcb; + tGATT_READ_MULTI *p_read_multi; + tGATT_IF gatt_if = GATT_GET_GATT_IF(conn_id); + UINT8 tcb_idx = GATT_GET_TCB_IDX(conn_id); + tGATT_TCB *p_tcb = gatt_get_tcb_by_idx(tcb_idx); + tGATT_REG *p_reg = gatt_get_regcb(gatt_if); + + + GATT_TRACE_API ("GATTC_Read conn_id=%d type=%d", conn_id, type); + + if ( (p_tcb == NULL) || (p_reg == NULL) || (p_read == NULL) || ((type >= GATT_READ_MAX) || (type == 0))) { + GATT_TRACE_ERROR("GATT_Read Illegal param: conn_id %d, type 0%d,", conn_id, type); + return GATT_ILLEGAL_PARAMETER; + } + + if (!gatt_check_connection_state_by_tcb(p_tcb)) { + GATT_TRACE_ERROR("connection not established\n"); + return GATT_ERROR; + } + + if (gatt_is_clcb_allocated(conn_id)) { + GATT_TRACE_ERROR("GATTC_Read GATT_BUSY conn_id = %d", conn_id); + return GATT_BUSY; + } + + if ( (p_clcb = gatt_clcb_alloc(conn_id)) != NULL ) { + p_clcb->operation = GATTC_OPTYPE_READ; + p_clcb->op_subtype = type; + p_clcb->auth_req = p_read->by_handle.auth_req; + p_clcb->counter = 0; + + switch (type) { + case GATT_READ_BY_TYPE: + case GATT_READ_CHAR_VALUE: + p_clcb->s_handle = p_read->service.s_handle; + p_clcb->e_handle = p_read->service.e_handle; + memcpy(&p_clcb->uuid, &p_read->service.uuid, sizeof(tBT_UUID)); + break; + case GATT_READ_MULTIPLE: + case GATT_READ_MULTIPLE_VAR: + p_clcb->s_handle = 0; + /* copy multiple handles in CB */ + p_read_multi = (tGATT_READ_MULTI *)osi_malloc(sizeof(tGATT_READ_MULTI)); + p_clcb->p_attr_buf = (UINT8 *)p_read_multi; + memcpy (p_read_multi, &p_read->read_multiple, sizeof(tGATT_READ_MULTI)); + case GATT_READ_BY_HANDLE: + case GATT_READ_PARTIAL: + memset(&p_clcb->uuid, 0, sizeof(tBT_UUID)); + p_clcb->s_handle = p_read->by_handle.handle; + + if (type == GATT_READ_PARTIAL) { + p_clcb->counter = p_read->partial.offset; + } + + break; + default: + break; + } + /* start security check */ + if (gatt_security_check_start(p_clcb) == FALSE) { + status = GATT_NO_RESOURCES; + gatt_clcb_dealloc(p_clcb); + } + } else { + status = GATT_NO_RESOURCES; + } + return status; +} + +/******************************************************************************* +** +** Function GATTC_Write +** +** Description This function is called to write the value of an attribute to +** the server. +** +** Parameters conn_id: connection identifier. +** type - attribute write type. +** p_write - write operation parameters. +** +** Returns GATT_SUCCESS if command started successfully. +** +*******************************************************************************/ +tGATT_STATUS GATTC_Write (UINT16 conn_id, tGATT_WRITE_TYPE type, tGATT_VALUE *p_write) +{ + tGATT_STATUS status = GATT_SUCCESS; + tGATT_CLCB *p_clcb; + tGATT_VALUE *p; + tGATT_IF gatt_if = GATT_GET_GATT_IF(conn_id); + UINT8 tcb_idx = GATT_GET_TCB_IDX(conn_id); + tGATT_TCB *p_tcb = gatt_get_tcb_by_idx(tcb_idx); + tGATT_REG *p_reg = gatt_get_regcb(gatt_if); + + if ( (p_tcb == NULL) || (p_reg == NULL) || (p_write == NULL) || + ((type != GATT_WRITE) && (type != GATT_WRITE_PREPARE) && (type != GATT_WRITE_NO_RSP)) ) { + GATT_TRACE_ERROR("GATT_Write Illegal param: conn_id %d, type 0%d,", conn_id, type); + return GATT_ILLEGAL_PARAMETER; + } + + if (!gatt_check_connection_state_by_tcb(p_tcb)) { + GATT_TRACE_ERROR("connection not established\n"); + return GATT_ERROR; + } + + if (gatt_is_clcb_allocated(conn_id)) { + GATT_TRACE_ERROR("GATTC_Write GATT_BUSY conn_id = %d", conn_id); + return GATT_BUSY; + } + + if ((p_clcb = gatt_clcb_alloc(conn_id)) != NULL ) { + p_clcb->operation = GATTC_OPTYPE_WRITE; + p_clcb->op_subtype = type; + p_clcb->auth_req = p_write->auth_req; + + if (( p_clcb->p_attr_buf = (UINT8 *)osi_malloc((UINT16)sizeof(tGATT_VALUE))) != NULL) { + memcpy(p_clcb->p_attr_buf, (void *)p_write, sizeof(tGATT_VALUE)); + + p = (tGATT_VALUE *)p_clcb->p_attr_buf; + if (type == GATT_WRITE_PREPARE) { + p_clcb->start_offset = p_write->offset; + p->offset = 0; + } + + if (gatt_security_check_start(p_clcb) == FALSE) { + status = GATT_NO_RESOURCES; + } + } else { + status = GATT_NO_RESOURCES; + } + + if (status == GATT_NO_RESOURCES) { + gatt_clcb_dealloc(p_clcb); + } + } else { + status = GATT_NO_RESOURCES; + } + return status; +} + + +/******************************************************************************* +** +** Function GATTC_ExecuteWrite +** +** Description This function is called to send an Execute write request to +** the server. +** +** Parameters conn_id: connection identifier. +** is_execute - to execute or cancel the prepare write request(s) +** +** Returns GATT_SUCCESS if command started successfully. +** +*******************************************************************************/ +tGATT_STATUS GATTC_ExecuteWrite (UINT16 conn_id, BOOLEAN is_execute) +{ + tGATT_STATUS status = GATT_SUCCESS; + tGATT_CLCB *p_clcb; + tGATT_EXEC_FLAG flag; + tGATT_IF gatt_if = GATT_GET_GATT_IF(conn_id); + UINT8 tcb_idx = GATT_GET_TCB_IDX(conn_id); + tGATT_TCB *p_tcb = gatt_get_tcb_by_idx(tcb_idx); + tGATT_REG *p_reg = gatt_get_regcb(gatt_if); + + GATT_TRACE_API ("GATTC_ExecuteWrite conn_id=%d is_execute=%d", conn_id, is_execute); + + if ( (p_tcb == NULL) || (p_reg == NULL) ) { + GATT_TRACE_ERROR("GATTC_ExecuteWrite Illegal param: conn_id %d", conn_id); + return GATT_ILLEGAL_PARAMETER; + } + + if (!gatt_check_connection_state_by_tcb(p_tcb)) { + GATT_TRACE_ERROR("connection not established\n"); + return GATT_ERROR; + } + + if (gatt_is_clcb_allocated(conn_id)) { + GATT_TRACE_ERROR("GATTC_Write GATT_BUSY conn_id = %d", conn_id); + return GATT_BUSY; + } + + if ((p_clcb = gatt_clcb_alloc(conn_id)) != NULL) { + p_clcb->operation = GATTC_OPTYPE_EXE_WRITE; + flag = is_execute ? GATT_PREP_WRITE_EXEC : GATT_PREP_WRITE_CANCEL; + gatt_send_queue_write_cancel (p_clcb->p_tcb, p_clcb, flag); + } else { + GATT_TRACE_ERROR("Unable to allocate client CB for conn_id %d ", conn_id); + status = GATT_NO_RESOURCES; + } + return status; +} + +/******************************************************************************* +** +** Function GATTC_SendHandleValueConfirm +** +** Description This function is called to send a handle value confirmation +** as response to a handle value notification from server. +** +** Parameters conn_id: connection identifier. +** handle: the handle of the attribute confirmation. +** +** Returns GATT_SUCCESS if command started successfully. +** +*******************************************************************************/ +tGATT_STATUS GATTC_SendHandleValueConfirm (UINT16 conn_id, UINT16 handle) +{ + tGATT_STATUS ret = GATT_ILLEGAL_PARAMETER; + tGATT_TCB *p_tcb = gatt_get_tcb_by_idx(GATT_GET_TCB_IDX(conn_id)); + + GATT_TRACE_API ("GATTC_SendHandleValueConfirm conn_id=%d handle=0x%x", conn_id, handle); + + if (p_tcb) { + if (p_tcb->ind_count > 0 ) { + btu_stop_timer (&p_tcb->ind_ack_timer_ent); + + GATT_TRACE_DEBUG ("notif_count=%d ", p_tcb->ind_count); + /* send confirmation now */ + ret = attp_send_cl_msg(p_tcb, 0, GATT_HANDLE_VALUE_CONF, (tGATT_CL_MSG *)&handle); + + p_tcb->ind_count = 0; + + } else { + GATT_TRACE_DEBUG ("GATTC_SendHandleValueConfirm - conn_id: %u - ignored not waiting for indicaiton ack", conn_id); + ret = GATT_SUCCESS; + } + } else { + GATT_TRACE_ERROR ("GATTC_SendHandleValueConfirm - Unknown conn_id: %u", conn_id); + } + return ret; +} + +tGATT_STATUS GATTC_AutoDiscoverEnable(UINT8 enable) +{ + gatt_cb.auto_disc = (enable > 0) ? TRUE : FALSE; + return GATT_SUCCESS; +} + +#endif ///GATTC_INCLUDED == TRUE + +/*******************************************************************************/ +/* */ +/* GATT APIs */ +/* */ +/*******************************************************************************/ +/******************************************************************************* +** +** Function GATT_SetIdleTimeout +** +** Description This function (common to both client and server) sets the idle +** timeout for a tansport connection +** +** Parameter bd_addr: target device bd address. +** idle_tout: timeout value in seconds. +** +** Returns void +** +*******************************************************************************/ +void GATT_SetIdleTimeout (BD_ADDR bd_addr, UINT16 idle_tout, tBT_TRANSPORT transport) +{ + tGATT_TCB *p_tcb; + BOOLEAN status = FALSE; + + if ((p_tcb = gatt_find_tcb_by_addr (bd_addr, transport)) != NULL) { + if (p_tcb->att_lcid == L2CAP_ATT_CID) { + status = L2CA_SetFixedChannelTout (bd_addr, L2CAP_ATT_CID, idle_tout); + + if (idle_tout == GATT_LINK_IDLE_TIMEOUT_WHEN_NO_APP) { + L2CA_SetIdleTimeoutByBdAddr(p_tcb->peer_bda, + GATT_LINK_IDLE_TIMEOUT_WHEN_NO_APP, BT_TRANSPORT_LE); + } + } else { + status = L2CA_SetIdleTimeout (p_tcb->att_lcid, idle_tout, FALSE); + } + } + +#if (CONFIG_BT_STACK_NO_LOG) + (void) status; +#endif + + GATT_TRACE_API ("GATT_SetIdleTimeout idle_tout=%d status=%d(1-OK 0-not performed)", + idle_tout, status); +} + + +/******************************************************************************* +** +** Function GATT_Register +** +** Description This function is called to register an application +** with GATT +** +** Parameter p_app_uuid128: Application UUID +** p_cb_info: callback functions. +** +** Returns 0 for error, otherwise the index of the client registered with GATT +** +*******************************************************************************/ +tGATT_IF GATT_Register (tBT_UUID *p_app_uuid128, const tGATT_CBACK *p_cb_info) +{ + tGATT_REG *p_reg; + UINT8 i_gatt_if = 0; + tGATT_IF gatt_if = 0; + + GATT_TRACE_API ("GATT_Register"); + gatt_dbg_display_uuid(*p_app_uuid128); + + for (i_gatt_if = 0, p_reg = gatt_cb.cl_rcb; i_gatt_if < GATT_MAX_APPS; i_gatt_if++, p_reg++) { + if (p_reg->in_use && !memcmp(p_app_uuid128->uu.uuid128, p_reg->app_uuid128.uu.uuid128, LEN_UUID_128)) { + GATT_TRACE_ERROR("application already registered."); + return 0; + } + } + + for (i_gatt_if = 0, p_reg = gatt_cb.cl_rcb; i_gatt_if < GATT_MAX_APPS; i_gatt_if++, p_reg++) { + if (!p_reg->in_use) { + memset(p_reg, 0 , sizeof(tGATT_REG)); + i_gatt_if++; /* one based number */ + p_reg->app_uuid128 = *p_app_uuid128; + gatt_if = + p_reg->gatt_if = (tGATT_IF)i_gatt_if; + p_reg->app_cb = *p_cb_info; + p_reg->in_use = TRUE; + + break; + } + } + GATT_TRACE_API ("allocated gatt_if=%d\n", gatt_if); + return gatt_if; +} + + +/******************************************************************************* +** +** Function GATT_Deregister +** +** Description This function deregistered the application from GATT. +** +** Parameters gatt_if: application interface. +** +** Returns None. +** +*******************************************************************************/ +void GATT_Deregister (tGATT_IF gatt_if) +{ + tGATT_REG *p_reg = gatt_get_regcb(gatt_if); + tGATT_TCB *p_tcb; + tGATT_CLCB *p_clcb; + list_node_t *p_node = NULL; + list_node_t *p_next = NULL; +#if (GATTS_INCLUDED == TRUE) + UINT8 ii; + tGATT_SR_REG *p_sreg; +#endif ///GATTS_INCLUDED == TRUE + GATT_TRACE_API ("GATT_Deregister gatt_if=%d", gatt_if); + /* Index 0 is GAP and is never deregistered */ + if ( (gatt_if == 0) || (p_reg == NULL) ) { + GATT_TRACE_ERROR ("GATT_Deregister with invalid gatt_if: %u", gatt_if); + return; + } + + /* stop all services */ + /* todo an applcaiton can not be deregistered if its services is also used by other application + deregisteration need to bed performed in an orderly fashion + no check for now */ +#if (GATTS_INCLUDED == TRUE) + for (ii = 0, p_sreg = gatt_cb.sr_reg; ii < GATT_MAX_SR_PROFILES; ii++, p_sreg++) { + if (p_sreg->in_use && (p_sreg->gatt_if == gatt_if)) { + GATTS_StopService(p_sreg->s_hdl); + } + } + /* free all services db buffers if owned by this application */ + gatt_free_srvc_db_buffer_app_id(&p_reg->app_uuid128); +#endif ///GATTS_INCLUDED == TRUE + /* When an application deregisters, check remove the link associated with the app */ + + for(p_node = list_begin(gatt_cb.p_tcb_list); p_node; p_node = p_next) { + p_tcb = list_node(p_node); + p_next = list_next(p_node); + if (p_tcb->in_use) { + if (gatt_get_ch_state(p_tcb) != GATT_CH_CLOSE) { + gatt_update_app_use_link_flag(gatt_if, p_tcb, FALSE, FALSE); + if (!gatt_num_apps_hold_link(p_tcb)) { + /* this will disconnect the link or cancel the pending connect request at lower layer*/ + gatt_disconnect(p_tcb); + } + } + + list_node_t *p_node_clcb = NULL; + list_node_t *p_node_next = NULL; + for(p_node_clcb = list_begin(gatt_cb.p_clcb_list); p_node_clcb; p_node_clcb = p_node_next) { + p_clcb = list_node(p_node_clcb); + p_node_next = list_next(p_node_clcb); + if (p_clcb->in_use && + (p_clcb->p_reg->gatt_if == gatt_if) && + (p_clcb->p_tcb->tcb_idx == p_tcb->tcb_idx)) { + btu_stop_timer(&p_clcb->rsp_timer_ent); + gatt_clcb_dealloc (p_clcb); + break; + } + } + } + } + + gatt_deregister_bgdev_list(gatt_if); + /* update the listen mode */ +#if (defined(BLE_PERIPHERAL_MODE_SUPPORT) && (BLE_PERIPHERAL_MODE_SUPPORT == TRUE)) + GATT_Listen(gatt_if, FALSE, NULL); +#endif + + memset (p_reg, 0, sizeof(tGATT_REG)); +} + + +/******************************************************************************* +** +** Function GATT_StartIf +** +** Description This function is called after registration to start receiving +** callbacks for registered interface. Function may call back +** with connection status and queued notifications +** +** Parameter gatt_if: application interface. +** +** Returns None. +** +*******************************************************************************/ +void GATT_StartIf (tGATT_IF gatt_if) +{ + tGATT_REG *p_reg; + tGATT_TCB *p_tcb; + BD_ADDR bda; + UINT8 start_idx, found_idx; + UINT16 conn_id; + tGATT_TRANSPORT transport ; + + GATT_TRACE_API ("GATT_StartIf gatt_if=%d", gatt_if); + if ((p_reg = gatt_get_regcb(gatt_if)) != NULL) { + start_idx = 0; + while (gatt_find_the_connected_bda(start_idx, bda, &found_idx, &transport)) { + p_tcb = gatt_find_tcb_by_addr(bda, transport); + if (p_reg->app_cb.p_conn_cb && p_tcb) { + conn_id = GATT_CREATE_CONN_ID(p_tcb->tcb_idx, gatt_if); + (*p_reg->app_cb.p_conn_cb)(gatt_if, bda, conn_id, TRUE, 0, transport); + } + start_idx = ++found_idx; + } + } +} + + +/******************************************************************************* +** +** Function GATT_Connect +** +** Description This function initiate a connection to a remote device on GATT +** channel. +** +** Parameters gatt_if: application interface +** bd_addr: peer device address. +** bd_addr_type: peer device address type. +** is_direct: is a direct connection or a background auto connection +** +** Returns TRUE if connection started; FALSE if connection start failure. +** +*******************************************************************************/ +BOOLEAN GATT_Connect (tGATT_IF gatt_if, BD_ADDR bd_addr, tBLE_ADDR_TYPE bd_addr_type, + BOOLEAN is_direct, tBT_TRANSPORT transport, BOOLEAN is_aux) +{ + tGATT_REG *p_reg; + BOOLEAN status = FALSE; + + GATT_TRACE_API ("GATT_Connect gatt_if=%d", gatt_if); + + /* Make sure app is registered */ + if ((p_reg = gatt_get_regcb(gatt_if)) == NULL) { + GATT_TRACE_ERROR("GATT_Connect - gatt_if =%d is not registered", gatt_if); + return (FALSE); + } + + if (is_direct) { + status = gatt_act_connect (p_reg, bd_addr, bd_addr_type, transport, is_aux); + } else { + if (transport == BT_TRANSPORT_LE) { + status = gatt_update_auto_connect_dev(gatt_if, TRUE, bd_addr, TRUE); + } else { + GATT_TRACE_ERROR("Unsupported transport for background connection"); + } + } + + return status; + +} + +/******************************************************************************* +** +** Function GATT_CancelConnect +** +** Description This function terminate the connection initaition to a remote +** device on GATT channel. +** +** Parameters gatt_if: client interface. If 0 used as unconditionally disconnect, +** typically used for direct connection cancellation. +** bd_addr: peer device address. +** +** Returns TRUE if connection started; FALSE if connection start failure. +** +*******************************************************************************/ +BOOLEAN GATT_CancelConnect (tGATT_IF gatt_if, BD_ADDR bd_addr, BOOLEAN is_direct) +{ + tGATT_REG *p_reg; + tGATT_TCB *p_tcb; + BOOLEAN status = TRUE; + tGATT_IF temp_gatt_if; + UINT8 start_idx, found_idx; + + GATT_TRACE_API ("GATT_CancelConnect gatt_if=%d", gatt_if); + + if ((gatt_if != 0) && ((p_reg = gatt_get_regcb(gatt_if)) == NULL)) { + GATT_TRACE_ERROR("GATT_CancelConnect - gatt_if =%d is not registered", gatt_if); + return (FALSE); + } + + if (is_direct) { + if (!gatt_if) { + GATT_TRACE_DEBUG("GATT_CancelConnect - unconditional"); + start_idx = 0; + /* only LE connection can be cancelled */ + p_tcb = gatt_find_tcb_by_addr(bd_addr, BT_TRANSPORT_LE); + if (p_tcb && gatt_num_apps_hold_link(p_tcb)) { + while (status && gatt_find_app_hold_link(p_tcb, start_idx, &found_idx, &temp_gatt_if)) { + status = gatt_cancel_open(temp_gatt_if, bd_addr); + start_idx = ++found_idx; + } + } else { + GATT_TRACE_ERROR("GATT_CancelConnect - no app found"); + status = FALSE; + } + } else { + status = gatt_cancel_open(gatt_if, bd_addr); + } + } else { + if (!gatt_if) { + if (gatt_get_num_apps_for_bg_dev(bd_addr)) { + while (gatt_find_app_for_bg_dev(bd_addr, &temp_gatt_if)) { + gatt_remove_bg_dev_for_app(temp_gatt_if, bd_addr); + } + } else { + GATT_TRACE_ERROR("GATT_CancelConnect -no app associated with the bg device for unconditional removal"); + status = FALSE; + } + } else { + status = gatt_remove_bg_dev_for_app(gatt_if, bd_addr); + } + } + + return status; +} + +/******************************************************************************* +** +** Function GATT_Disconnect +** +** Description This function disconnect the GATT channel for this registered +** application. +** +** Parameters conn_id: connection identifier. +** +** Returns GATT_SUCCESS if disconnected. +** +*******************************************************************************/ +tGATT_STATUS GATT_Disconnect (UINT16 conn_id) +{ + tGATT_STATUS ret = GATT_ILLEGAL_PARAMETER; + tGATT_TCB *p_tcb = NULL; + tGATT_IF gatt_if = GATT_GET_GATT_IF(conn_id); + UINT8 tcb_idx = GATT_GET_TCB_IDX(conn_id); + + GATT_TRACE_API ("GATT_Disconnect conn_id=%d ", conn_id); + + p_tcb = gatt_get_tcb_by_idx(tcb_idx); + + if (p_tcb) { + gatt_update_app_use_link_flag(gatt_if, p_tcb, FALSE, FALSE); + if (!gatt_num_apps_hold_link(p_tcb)) { + gatt_disconnect(p_tcb); + } + ret = GATT_SUCCESS; + } + return ret; +} + +/******************************************************************************* +** +** Function GATT_SendServiceChangeIndication +** +** Description This function is to send a service change indication +** +** Parameters bd_addr: peer device address. +** +** Returns None. +** +*******************************************************************************/ +tGATT_STATUS GATT_SendServiceChangeIndication (BD_ADDR bd_addr) +{ + UINT8 start_idx, found_idx; + BOOLEAN srv_chg_ind_pending = FALSE; + tGATT_TCB *p_tcb; + tBT_TRANSPORT transport; + tGATT_STATUS status = GATT_NOT_FOUND; + + if (gatt_cb.srv_chg_mode == GATTS_SEND_SERVICE_CHANGE_AUTO) { + status = GATT_WRONG_STATE; + GATT_TRACE_ERROR ("%s can't send service change indication manually, please configure the option through menuconfig", __func__); + return status; + } + + if(bd_addr) { + status = gatt_send_srv_chg_ind(bd_addr); + } else { + start_idx = 0; + BD_ADDR addr; + while (gatt_find_the_connected_bda(start_idx, addr, &found_idx, &transport)) { + p_tcb = gatt_get_tcb_by_idx(found_idx); + srv_chg_ind_pending = gatt_is_srv_chg_ind_pending(p_tcb); + + if (!srv_chg_ind_pending) { + status = gatt_send_srv_chg_ind(addr); + } else { + status = GATT_BUSY; + GATT_TRACE_DEBUG("discard srv chg - already has one in the queue"); + } + start_idx = ++found_idx; + } + } + + return status; +} + +/******************************************************************************* +** +** Function GATT_GetConnectionInfor +** +** Description This function use conn_id to find its associated BD address and applciation +** interface +** +** Parameters conn_id: connection id (input) +** p_gatt_if: application interface (output) +** bd_addr: peer device address. (output) +** +** Returns TRUE the logical link information is found for conn_id +** +*******************************************************************************/ +BOOLEAN GATT_GetConnectionInfor(UINT16 conn_id, tGATT_IF *p_gatt_if, BD_ADDR bd_addr, + tBT_TRANSPORT *p_transport) +{ + + tGATT_IF gatt_if = GATT_GET_GATT_IF(conn_id); + tGATT_REG *p_reg = gatt_get_regcb(gatt_if); + UINT8 tcb_idx = GATT_GET_TCB_IDX(conn_id); + tGATT_TCB *p_tcb = gatt_get_tcb_by_idx(tcb_idx); + BOOLEAN status = FALSE; + + GATT_TRACE_API ("GATT_GetConnectionInfor conn_id=%d", conn_id); + + if (p_tcb && p_reg ) { + memcpy(bd_addr, p_tcb->peer_bda, BD_ADDR_LEN); + *p_gatt_if = gatt_if; + *p_transport = p_tcb->transport; + status = TRUE; + } + return status; +} + + +/******************************************************************************* +** +** Function GATT_GetConnIdIfConnected +** +** Description This function find the conn_id if the logical link for BD address +** and applciation interface is connected +** +** Parameters gatt_if: application interface (input) +** bd_addr: peer device address. (input) +** p_conn_id: connection id (output) +** transport: transport option +** +** Returns TRUE the logical link is connected +** +*******************************************************************************/ +BOOLEAN GATT_GetConnIdIfConnected(tGATT_IF gatt_if, BD_ADDR bd_addr, UINT16 *p_conn_id, + tBT_TRANSPORT transport) +{ + tGATT_REG *p_reg = gatt_get_regcb(gatt_if); + tGATT_TCB *p_tcb = gatt_find_tcb_by_addr(bd_addr, transport); + BOOLEAN status = FALSE; + + if (p_reg && p_tcb && (gatt_get_ch_state(p_tcb) == GATT_CH_OPEN) ) { + *p_conn_id = GATT_CREATE_CONN_ID(p_tcb->tcb_idx, gatt_if); + status = TRUE; + } + + GATT_TRACE_API ("GATT_GetConnIdIfConnected status=%d\n", status); + return status; +} + + +/******************************************************************************* +** +** Function GATT_Listen +** +** Description This function start or stop LE advertisement and listen for +** connection. +** +** Parameters gatt_if: application interface +** p_bd_addr: listen for specific address connection, or NULL for +** listen to all device connection. +** start: start or stop listening. +** +** Returns TRUE if advertisement is started; FALSE if adv start failure. +** +*******************************************************************************/ +BOOLEAN GATT_Listen (tGATT_IF gatt_if, BOOLEAN start, BD_ADDR_PTR bd_addr) +{ + tGATT_REG *p_reg; + + GATT_TRACE_API ("GATT_Listen gatt_if=%d", gatt_if); + + /* Make sure app is registered */ + if ((p_reg = gatt_get_regcb(gatt_if)) == NULL) { + GATT_TRACE_ERROR("GATT_Listen - gatt_if =%d is not registered", gatt_if); + return (FALSE); + } + + if (bd_addr != NULL) { + gatt_update_auto_connect_dev(gatt_if, start, bd_addr, FALSE); + } else { + p_reg->listening = start ? GATT_LISTEN_TO_ALL : GATT_LISTEN_TO_NONE; + } + + return gatt_update_listen_mode(); +} + +tGATT_STATUS GATTS_SetServiceChangeMode(UINT8 mode) +{ + if (mode > GATTS_SEND_SERVICE_CHANGE_MANUAL) { + GATT_TRACE_ERROR("%s invalid service change mode %u", __func__, mode); + return GATT_VALUE_NOT_ALLOWED; + } + + gatt_cb.srv_chg_mode = mode; + return GATT_SUCCESS; +} + +tGATT_STATUS GATTS_HandleMultiValueNotification (UINT16 conn_id, tGATT_HLV *tuples, UINT16 num_tuples) +{ + tGATT_STATUS cmd_sent = GATT_ILLEGAL_PARAMETER; + BT_HDR *p_buf; + tGATT_VALUE notif; + tGATT_IF gatt_if = GATT_GET_GATT_IF(conn_id); + UINT8 tcb_idx = GATT_GET_TCB_IDX(conn_id); + tGATT_REG *p_reg = gatt_get_regcb(gatt_if); + tGATT_TCB *p_tcb = gatt_get_tcb_by_idx(tcb_idx); + UINT8 *p = notif.value; + tGATT_HLV *p_hlv = tuples; + + GATT_TRACE_API ("GATTS_HandleMultiValueNotification"); + + if ( (p_reg == NULL) || (p_tcb == NULL)) { + GATT_TRACE_ERROR ("GATTS_HandleMultiValueNotification Unknown conn_id: %u \n", conn_id); + return (tGATT_STATUS) GATT_INVALID_CONN_ID; + } + + if (!gatt_check_connection_state_by_tcb(p_tcb)) { + GATT_TRACE_ERROR("connection not established\n"); + return GATT_WRONG_STATE; + } + + if (tuples == NULL) { + return GATT_ILLEGAL_PARAMETER; + } + + notif.len = 0; + + while (num_tuples) { + if (!GATT_HANDLE_IS_VALID (p_hlv->handle)) { + return GATT_ILLEGAL_PARAMETER; + } + + UINT16_TO_STREAM(p, p_hlv->handle); //handle + UINT16_TO_STREAM(p, p_hlv->length); //length + memcpy (p, p_hlv->value, p_hlv->length); //value + GATT_TRACE_DEBUG("%s handle %x, length %u", __func__, p_hlv->handle, p_hlv->length); + p += p_hlv->length; + notif.len += 4 + p_hlv->length; + num_tuples--; + p_hlv++; + } + + notif.auth_req = GATT_AUTH_REQ_NONE; + + p_buf = attp_build_sr_msg (p_tcb, GATT_HANDLE_MULTI_VALUE_NOTIF, (tGATT_SR_MSG *)¬if); + if (p_buf != NULL) { + cmd_sent = attp_send_sr_msg (p_tcb, p_buf); + } else { + cmd_sent = GATT_NO_RESOURCES; + } + + return cmd_sent; +} + +tGATT_STATUS GATTS_ShowLocalDatabase(void) +{ + gatts_show_local_database(); + return GATT_SUCCESS; +} + +#endif diff --git a/lib/bt/host/bluedroid/stack/gatt/gatt_attr.c b/lib/bt/host/bluedroid/stack/gatt/gatt_attr.c new file mode 100644 index 00000000..3ab57342 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/gatt/gatt_attr.c @@ -0,0 +1,797 @@ +/****************************************************************************** + * + * Copyright (C) 2008-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * this file contains the main GATT server attributes access request + * handling functions. + * + ******************************************************************************/ + +#include "common/bt_target.h" +//#include "bt_utils.h" + +#include "stack/gatt_api.h" +#include "gatt_int.h" +#include "stack/sdpdefs.h" +#include "bta/bta_gatts_co.h" + +#if (BLE_INCLUDED == TRUE && GATTS_INCLUDED == TRUE) + +#define BLE_GATT_SR_SUPP_FEAT_EATT_BITMASK 0x01 +#define BLE_GATT_CL_SUPP_FEAT_ROBUST_CACHING_BITMASK 0x01 +#define BLE_GATT_CL_SUPP_FEAT_EATT_BITMASK 0x02 +#define BLE_GATT_CL_SUPP_FEAT_MULTI_NOTIF_BITMASK 0x04 +#define BLE_GATT_CL_SUPP_FEAT_BITMASK 0x07 + +#define GATTP_MAX_NUM_INC_SVR 0 + +#if GATTS_ROBUST_CACHING_ENABLED +#define GATTP_MAX_CHAR_NUM 5 +#else +#define GATTP_MAX_CHAR_NUM 2 +#endif /* GATTS_ROBUST_CACHING_ENABLED */ + +#define GATTP_MAX_ATTR_NUM (GATTP_MAX_CHAR_NUM * 2 + GATTP_MAX_NUM_INC_SVR + 1) +#define GATTP_MAX_CHAR_VALUE_SIZE 50 + +#ifndef GATTP_ATTR_DB_SIZE +#define GATTP_ATTR_DB_SIZE GATT_DB_MEM_SIZE(GATTP_MAX_NUM_INC_SVR, GATTP_MAX_CHAR_NUM, GATTP_MAX_CHAR_VALUE_SIZE) +#endif + +static void gatt_request_cback(UINT16 conn_id, UINT32 trans_id, UINT8 op_code, tGATTS_DATA *p_data); +static void gatt_connect_cback(tGATT_IF gatt_if, BD_ADDR bda, UINT16 conn_id, BOOLEAN connected, + tGATT_DISCONN_REASON reason, tBT_TRANSPORT transport); +static void gatt_disc_res_cback(UINT16 conn_id, tGATT_DISC_TYPE disc_type, tGATT_DISC_RES *p_data); +static void gatt_disc_cmpl_cback(UINT16 conn_id, tGATT_DISC_TYPE disc_type, tGATT_STATUS status); +static void gatt_cl_op_cmpl_cback(UINT16 conn_id, tGATTC_OPTYPE op, tGATT_STATUS status, + tGATT_CL_COMPLETE *p_data); + +static void gatt_cl_start_config_ccc(tGATT_PROFILE_CLCB *p_clcb); + + +static const tGATT_CBACK gatt_profile_cback = { + gatt_connect_cback, + gatt_cl_op_cmpl_cback, + gatt_disc_res_cback, + gatt_disc_cmpl_cback, + gatt_request_cback, + NULL, + NULL +} ; + +/******************************************************************************* +** +** Function gatt_profile_find_conn_id_by_bd_addr +** +** Description Find the connection ID by remote address +** +** Returns Connection ID +** +*******************************************************************************/ +#if (GATTS_INCLUDED == TRUE) +UINT16 gatt_profile_find_conn_id_by_bd_addr(BD_ADDR remote_bda) +{ + UINT16 conn_id = GATT_INVALID_CONN_ID; + GATT_GetConnIdIfConnected (gatt_cb.gatt_if, remote_bda, &conn_id, BT_TRANSPORT_LE); + return conn_id; +} +#endif ///GATTS_INCLUDED == TRUE +/******************************************************************************* +** +** Function gatt_profile_find_clcb_by_conn_id +** +** Description find clcb by Connection ID +** +** Returns Pointer to the found link conenction control block. +** +*******************************************************************************/ +static tGATT_PROFILE_CLCB *gatt_profile_find_clcb_by_conn_id(UINT16 conn_id) +{ + UINT8 i_clcb; + tGATT_PROFILE_CLCB *p_clcb = NULL; + + for (i_clcb = 0, p_clcb = gatt_cb.profile_clcb; i_clcb < GATT_MAX_APPS; i_clcb++, p_clcb++) { + if (p_clcb->in_use && p_clcb->conn_id == conn_id) { + return p_clcb; + } + } + + return NULL; +} + +/******************************************************************************* +** +** Function gatt_profile_find_clcb_by_bd_addr +** +** Description The function searches all LCBs with macthing bd address. +** +** Returns Pointer to the found link conenction control block. +** +*******************************************************************************/ +static tGATT_PROFILE_CLCB *gatt_profile_find_clcb_by_bd_addr(BD_ADDR bda, tBT_TRANSPORT transport) +{ + UINT8 i_clcb; + tGATT_PROFILE_CLCB *p_clcb = NULL; + + for (i_clcb = 0, p_clcb = gatt_cb.profile_clcb; i_clcb < GATT_MAX_APPS; i_clcb++, p_clcb++) { + if (p_clcb->in_use && p_clcb->transport == transport && + p_clcb->connected && !memcmp(p_clcb->bda, bda, BD_ADDR_LEN)) { + return p_clcb; + } + } + + return NULL; +} + +/******************************************************************************* +** +** Function gatt_profile_clcb_alloc +** +** Description The function allocates a GATT profile connection link control block +** +** Returns NULL if not found. Otherwise pointer to the connection link block. +** +*******************************************************************************/ +tGATT_PROFILE_CLCB *gatt_profile_clcb_alloc (UINT16 conn_id, BD_ADDR bda, tBT_TRANSPORT tranport) +{ + UINT8 i_clcb = 0; + tGATT_PROFILE_CLCB *p_clcb = NULL; + + for (i_clcb = 0, p_clcb = gatt_cb.profile_clcb; i_clcb < GATT_MAX_APPS; i_clcb++, p_clcb++) { + if (!p_clcb->in_use) { + p_clcb->in_use = TRUE; + p_clcb->conn_id = conn_id; + p_clcb->connected = TRUE; + p_clcb->transport = tranport; + memcpy (p_clcb->bda, bda, BD_ADDR_LEN); + break; + } + } + if (i_clcb < GATT_MAX_APPS) { + return p_clcb; + } + + return NULL; +} + +/******************************************************************************* +** +** Function gatt_profile_clcb_dealloc +** +** Description The function deallocates a GATT profile connection link control block +** +** Returns void +** +*******************************************************************************/ +void gatt_profile_clcb_dealloc (tGATT_PROFILE_CLCB *p_clcb) +{ + memset(p_clcb, 0, sizeof(tGATT_PROFILE_CLCB)); +} + +/******************************************************************************* +** +** Function gatt_proc_read +** +** Description GATT Attributes Database Read/Read Blob Request process +** +** Returns GATT_SUCCESS if successfully sent; otherwise error code. +** +*******************************************************************************/ +tGATT_STATUS gatt_proc_read (UINT16 conn_id, tGATTS_REQ_TYPE type, tGATT_READ_REQ *p_data, tGATTS_RSP *p_rsp) +{ + tGATT_STATUS status = GATT_NO_RESOURCES; + UINT16 len = 0; + UINT8 *value; + UNUSED(type); + + GATT_TRACE_DEBUG("%s handle %x", __func__, p_data->handle); + + if (p_data->is_long) { + p_rsp->attr_value.offset = p_data->offset; + } + + p_rsp->attr_value.handle = p_data->handle; +#if GATTS_ROBUST_CACHING_ENABLED + + UINT8 tcb_idx = GATT_GET_TCB_IDX(conn_id); + tGATT_TCB *tcb = gatt_get_tcb_by_idx(tcb_idx); + + /* handle request for reading client supported features */ + if (p_data->handle == gatt_cb.handle_of_cl_supported_feat) { + if (tcb == NULL) { + return GATT_INSUF_RESOURCE; + } + p_rsp->attr_value.len = 1; + memcpy(p_rsp->attr_value.value, &tcb->cl_supp_feat, 1); + return GATT_SUCCESS; + } + + /* handle request for reading database hash */ + if (p_data->handle == gatt_cb.handle_of_database_hash) { + p_rsp->attr_value.len = BT_OCTET16_LEN; + memcpy(p_rsp->attr_value.value, gatt_cb.database_hash, BT_OCTET16_LEN); + gatt_sr_update_cl_status(tcb, true); + return GATT_SUCCESS; + } + + /* handle request for reading server supported features */ + if (p_data->handle == gatt_cb.handle_of_sr_supported_feat) { + p_rsp->attr_value.len = 1; + memcpy(p_rsp->attr_value.value, &gatt_cb.gatt_sr_supported_feat_mask, 1); + return GATT_SUCCESS; + } +#endif /* GATTS_ROBUST_CACHING_ENABLED */ + /* handle request for reading service changed des and the others */ + status = GATTS_GetAttributeValue(p_data->handle, &len, &value); + if(status == GATT_SUCCESS && len > 0 && value) { + if(len > GATT_MAX_ATTR_LEN) { + len = GATT_MAX_ATTR_LEN; + } + p_rsp->attr_value.len = len; + memcpy(p_rsp->attr_value.value, value, len); + } + return status; +} +#if GATTS_ROBUST_CACHING_ENABLED +static tGATT_STATUS gatt_sr_write_cl_supp_feat(UINT16 conn_id, tGATT_WRITE_REQ *p_data) +{ + UINT8 val_new; + UINT8 val_old; + UINT8 val_xor; + UINT8 val_and; + UINT8 *p = p_data->value; + UINT8 tcb_idx = GATT_GET_TCB_IDX(conn_id); + tGATT_TCB *p_tcb = gatt_get_tcb_by_idx(tcb_idx); + + GATT_TRACE_DEBUG("%s len %u, feat %x", __func__, p_data->len, *p); + + if (p_tcb == NULL) { + GATT_TRACE_ERROR("%s no conn", __func__); + return GATT_NOT_FOUND; + } + + if (p_data->len != 1) { + GATT_TRACE_ERROR("%s len %u", __func__, p_data->len); + return GATT_INVALID_PDU; + } + + STREAM_TO_UINT8(val_new, p); + val_new = (val_new & BLE_GATT_CL_SUPP_FEAT_BITMASK); + + if (val_new == 0) { + GATT_TRACE_ERROR("%s bit cannot be all zero", __func__); + return GATT_VALUE_NOT_ALLOWED; + } + + val_old = p_tcb->cl_supp_feat; + val_xor = val_old ^ val_new; + val_and = val_xor & val_new; + if (val_and != val_xor) { + GATT_TRACE_ERROR("%s bit cannot be reset", __func__); + return GATT_VALUE_NOT_ALLOWED; + } + + p_tcb->cl_supp_feat = val_new; +#if (SMP_INCLUDED == TRUE) + bta_gatts_co_cl_feat_save(p_tcb->peer_bda, &p_tcb->cl_supp_feat); +#endif + return GATT_SUCCESS; +} +#endif /* GATTS_ROBUST_CACHING_ENABLED */ +/****************************************************************************** +** +** Function gatt_proc_write_req +** +** Description GATT server process a write request. +** +** Returns GATT_SUCCESS if successfully sent; otherwise error code. +** +*******************************************************************************/ +tGATT_STATUS gatt_proc_write_req(UINT16 conn_id, tGATTS_REQ_TYPE type, tGATT_WRITE_REQ *p_data) +{ + if(p_data->len > GATT_MAX_ATTR_LEN) { + p_data->len = GATT_MAX_ATTR_LEN; + } +#if GATTS_ROBUST_CACHING_ENABLED + if (p_data->handle == gatt_cb.handle_of_h_r) { + return GATT_WRITE_NOT_PERMIT; + } + + if (p_data->handle == gatt_cb.handle_of_cl_supported_feat) { + return gatt_sr_write_cl_supp_feat(conn_id, p_data); + } + + if (p_data->handle == gatt_cb.handle_of_database_hash) { + return GATT_WRITE_NOT_PERMIT; + } + + if (p_data->handle == gatt_cb.handle_of_sr_supported_feat) { + return GATT_WRITE_NOT_PERMIT; + } +#endif /* GATTS_ROBUST_CACHING_ENABLED */ + return GATTS_SetAttributeValue(p_data->handle, + p_data->len, + p_data->value); + +} + +/******************************************************************************* +** +** Function gatt_request_cback +** +** Description GATT profile attribute access request callback. +** +** Returns void. +** +*******************************************************************************/ +static void gatt_request_cback (UINT16 conn_id, UINT32 trans_id, tGATTS_REQ_TYPE type, + tGATTS_DATA *p_data) +{ + UINT8 status = GATT_INVALID_PDU; + tGATTS_RSP rsp_msg ; + BOOLEAN ignore = FALSE; + GATT_TRACE_DEBUG("%s",__func__); + memset(&rsp_msg, 0, sizeof(tGATTS_RSP)); + + switch (type) { + case GATTS_REQ_TYPE_READ: + status = gatt_proc_read(conn_id, type, &p_data->read_req, &rsp_msg); + break; + + case GATTS_REQ_TYPE_WRITE: + if (!p_data->write_req.need_rsp) { + ignore = TRUE; + } + status = gatt_proc_write_req(conn_id, type, &p_data->write_req); + break; + + case GATTS_REQ_TYPE_WRITE_EXEC: + case GATT_CMD_WRITE: + ignore = TRUE; + GATT_TRACE_EVENT("Ignore GATT_REQ_EXEC_WRITE/WRITE_CMD" ); + break; + + case GATTS_REQ_TYPE_MTU: + GATT_TRACE_EVENT("Get MTU exchange new mtu size: %d", p_data->mtu); + ignore = TRUE; + break; + + default: + GATT_TRACE_EVENT("Unknown/unexpected LE GAP ATT request: 0x%02x", type); + break; + } + + if (!ignore) { + GATTS_SendRsp (conn_id, trans_id, status, &rsp_msg); + } + +} + +/******************************************************************************* +** +** Function gatt_connect_cback +** +** Description Gatt profile connection callback. +** +** Returns void +** +*******************************************************************************/ +static void gatt_connect_cback (tGATT_IF gatt_if, BD_ADDR bda, UINT16 conn_id, + BOOLEAN connected, tGATT_DISCONN_REASON reason, + tBT_TRANSPORT transport) +{ + UNUSED(gatt_if); + + GATT_TRACE_DEBUG ("%s: from %08x%04x connected:%d conn_id=%d reason = 0x%04x", __FUNCTION__, + (bda[0] << 24) + (bda[1] << 16) + (bda[2] << 8) + bda[3], + (bda[4] << 8) + bda[5], connected, conn_id, reason); + + tGATT_PROFILE_CLCB *p_clcb = gatt_profile_find_clcb_by_bd_addr(bda, transport); + if (p_clcb == NULL) { + p_clcb = gatt_profile_clcb_alloc (conn_id, bda, transport); + } + + if (p_clcb == NULL) { + return; + } + + if (GATT_GetConnIdIfConnected (gatt_cb.gatt_if, bda, &p_clcb->conn_id, transport)) { + p_clcb->connected = TRUE; + p_clcb->conn_id = conn_id; + } + + + if (!p_clcb->connected) { + /* wait for connection */ + return; + } + + if (connected) { + p_clcb->conn_id = conn_id; + p_clcb->connected = TRUE; + + } else { + gatt_profile_clcb_dealloc(p_clcb); + } +} + +/******************************************************************************* +** +** Function gatt_profile_db_init +** +** Description Initializa the GATT profile attribute database. +** +*******************************************************************************/ +void gatt_profile_db_init (void) +{ + tBT_UUID app_uuid = {LEN_UUID_128, {0}}; + tBT_UUID uuid = {LEN_UUID_16, {UUID_SERVCLASS_GATT_SERVER}}; + UINT16 service_handle = 0; + tGATT_STATUS status; + + /* Fill our internal UUID with a fixed pattern 0x81 */ + memset (&app_uuid.uu.uuid128, 0x81, LEN_UUID_128); + + + /* Create a GATT profile service */ + gatt_cb.gatt_if = GATT_Register(&app_uuid, &gatt_profile_cback); + GATT_StartIf(gatt_cb.gatt_if); + + service_handle = GATTS_CreateService (gatt_cb.gatt_if , &uuid, 0, GATTP_MAX_ATTR_NUM, TRUE); + GATT_TRACE_DEBUG ("GATTS_CreateService: handle of service handle%x", service_handle); + + /* add Service Changed characteristic + */ + uuid.uu.uuid16 = gatt_cb.gattp_attr.uuid = GATT_UUID_GATT_SRV_CHGD; + gatt_cb.gattp_attr.service_change = 0; + gatt_cb.gattp_attr.handle = + gatt_cb.handle_of_h_r = GATTS_AddCharacteristic(service_handle, &uuid, 0, GATT_CHAR_PROP_BIT_INDICATE, + NULL, NULL); + + GATT_TRACE_DEBUG ("gatt_profile_db_init: handle of service changed%d\n", + gatt_cb.handle_of_h_r); + + tBT_UUID descr_uuid = {LEN_UUID_16, {GATT_UUID_CHAR_CLIENT_CONFIG}}; + uint8_t ccc_value[2] ={ 0x00, 0x00}; + + tGATT_ATTR_VAL attr_val = { + .attr_max_len = sizeof(UINT16), + .attr_len = sizeof(UINT16), + .attr_val = ccc_value, + }; + + GATTS_AddCharDescriptor (service_handle, GATT_PERM_READ | GATT_PERM_WRITE , &descr_uuid, &attr_val, NULL); +#if GATTS_ROBUST_CACHING_ENABLED + /* add Client Supported Features characteristic */ + uuid.uu.uuid16 = GATT_UUID_CLIENT_SUP_FEAT; + gatt_cb.handle_of_cl_supported_feat = GATTS_AddCharacteristic(service_handle, &uuid, GATT_PERM_READ | GATT_PERM_WRITE, + GATT_CHAR_PROP_BIT_READ | GATT_CHAR_PROP_BIT_WRITE, NULL, NULL); + + /* add Database Hash characteristic */ + uuid.uu.uuid16 = GATT_UUID_GATT_DATABASE_HASH; + gatt_cb.handle_of_database_hash = GATTS_AddCharacteristic(service_handle, &uuid, GATT_PERM_READ, GATT_CHAR_PROP_BIT_READ, NULL, NULL); + + /* add Server Supported Features characteristic */ + uuid.uu.uuid16 = GATT_UUID_SERVER_SUP_FEAT; + gatt_cb.handle_of_sr_supported_feat = GATTS_AddCharacteristic(service_handle, &uuid, GATT_PERM_READ, GATT_CHAR_PROP_BIT_READ, NULL, NULL); +#endif /* GATTS_ROBUST_CACHING_ENABLED */ + /* start service */ + status = GATTS_StartService (gatt_cb.gatt_if, service_handle, GATTP_TRANSPORT_SUPPORTED ); + +#if (CONFIG_BT_STACK_NO_LOG) + (void) status; +#endif + + GATT_TRACE_DEBUG ("gatt_profile_db_init: gatt_if=%d start status%d\n", + gatt_cb.gatt_if, status); +} + +/******************************************************************************* +** +** Function gatt_disc_res_cback +** +** Description Gatt profile discovery result callback +** +** Returns void +** +*******************************************************************************/ +static void gatt_disc_res_cback (UINT16 conn_id, tGATT_DISC_TYPE disc_type, tGATT_DISC_RES *p_data) +{ + GATT_TRACE_DEBUG("%s, disc_type = %d",__func__, disc_type); + tGATT_PROFILE_CLCB *p_clcb = gatt_profile_find_clcb_by_conn_id(conn_id); + + if (p_clcb == NULL) { + return; + } + + switch (disc_type) { + case GATT_DISC_SRVC_BY_UUID:/* stage 1 */ + p_clcb->e_handle = p_data->value.group_value.e_handle; + p_clcb->ccc_result ++; + break; + + case GATT_DISC_CHAR:/* stage 2 */ + p_clcb->s_handle = p_data->value.dclr_value.val_handle; + p_clcb->ccc_result ++; + break; + + case GATT_DISC_CHAR_DSCPT: /* stage 3 */ + if (p_data->type.uu.uuid16 == GATT_UUID_CHAR_CLIENT_CONFIG) { + p_clcb->s_handle = p_data->handle; + p_clcb->ccc_result ++; + } + break; + } +} + +/******************************************************************************* +** +** Function gatt_disc_cmpl_cback +** +** Description Gatt profile discovery complete callback +** +** Returns void +** +*******************************************************************************/ +static void gatt_disc_cmpl_cback (UINT16 conn_id, tGATT_DISC_TYPE disc_type, tGATT_STATUS status) +{ + GATT_TRACE_DEBUG("%s",__func__); + tGATT_PROFILE_CLCB *p_clcb = gatt_profile_find_clcb_by_conn_id(conn_id); + + if (p_clcb == NULL) { + return; + } + + if (status == GATT_SUCCESS && p_clcb->ccc_result > 0) { + p_clcb->ccc_result = 0; + p_clcb->ccc_stage ++; + gatt_cl_start_config_ccc(p_clcb); + } else { + GATT_TRACE_ERROR("%s() - Register for service changed indication failure", __FUNCTION__); + } +} + +/******************************************************************************* +** +** Function gatt_cl_op_cmpl_cback +** +** Description Gatt profile client operation complete callback +** +** Returns void +** +*******************************************************************************/ +static void gatt_cl_op_cmpl_cback (UINT16 conn_id, tGATTC_OPTYPE op, + tGATT_STATUS status, tGATT_CL_COMPLETE *p_data) +{ + GATT_TRACE_DEBUG("%s",__func__); + tGATT_PROFILE_CLCB *p_clcb = gatt_profile_find_clcb_by_conn_id(conn_id); + + if (p_clcb == NULL) { + return; + } + + if (op == GATTC_OPTYPE_WRITE) { + GATT_TRACE_DEBUG("%s() - ccc write status : %d", __FUNCTION__, status); + } + +} + +/******************************************************************************* +** +** Function gatt_cl_start_config_ccc +** +** Description Gatt profile start configure service change CCC +** +** Returns void +** +*******************************************************************************/ +static void gatt_cl_start_config_ccc(tGATT_PROFILE_CLCB *p_clcb) +{ + tGATT_DISC_PARAM srvc_disc_param; + tGATT_VALUE ccc_value; + + GATT_TRACE_DEBUG("%s() - stage: %d", __FUNCTION__, p_clcb->ccc_stage); + + memset (&srvc_disc_param, 0 , sizeof(tGATT_DISC_PARAM)); + memset (&ccc_value, 0 , sizeof(tGATT_VALUE)); + + switch (p_clcb->ccc_stage) { + case GATT_SVC_CHANGED_SERVICE: /* discover GATT service */ + srvc_disc_param.s_handle = 1; + srvc_disc_param.e_handle = 0xffff; + srvc_disc_param.service.len = 2; + srvc_disc_param.service.uu.uuid16 = UUID_SERVCLASS_GATT_SERVER; +#if (GATTC_INCLUDED == TRUE) + if (GATTC_Discover (p_clcb->conn_id, GATT_DISC_SRVC_BY_UUID, &srvc_disc_param) != GATT_SUCCESS) { + GATT_TRACE_ERROR("%s() - ccc service error", __FUNCTION__); + } +#endif ///GATTC_INCLUDED == TRUE + break; + + case GATT_SVC_CHANGED_CHARACTERISTIC: /* discover service change char */ + srvc_disc_param.s_handle = 1; + srvc_disc_param.e_handle = p_clcb->e_handle; + srvc_disc_param.service.len = 2; + srvc_disc_param.service.uu.uuid16 = GATT_UUID_GATT_SRV_CHGD; +#if (GATTC_INCLUDED == TRUE) + if (GATTC_Discover (p_clcb->conn_id, GATT_DISC_CHAR, &srvc_disc_param) != GATT_SUCCESS) { + GATT_TRACE_ERROR("%s() - ccc char error", __FUNCTION__); + } +#endif ///GATTC_INCLUDED == TRUE + break; + + case GATT_SVC_CHANGED_DESCRIPTOR: /* discover service change ccc */ + srvc_disc_param.s_handle = p_clcb->s_handle; + srvc_disc_param.e_handle = p_clcb->e_handle; +#if (GATTC_INCLUDED == TRUE) + if (GATTC_Discover (p_clcb->conn_id, GATT_DISC_CHAR_DSCPT, &srvc_disc_param) != GATT_SUCCESS) { + GATT_TRACE_ERROR("%s() - ccc char descriptor error", __FUNCTION__); + } +#endif ///GATTC_INCLUDED == TRUE + break; + + case GATT_SVC_CHANGED_CONFIGURE_CCCD: /* write ccc */ + ccc_value.handle = p_clcb->s_handle; + ccc_value.len = 2; + ccc_value.value[0] = GATT_CLT_CONFIG_INDICATION; +#if (GATTC_INCLUDED == TRUE) + if (GATTC_Write (p_clcb->conn_id, GATT_WRITE, &ccc_value) != GATT_SUCCESS) { + GATT_TRACE_ERROR("%s() - write ccc error", __FUNCTION__); + } +#endif ///GATTC_INCLUDED == TRUE + break; + } +} + +/******************************************************************************* +** +** Function GATT_ConfigServiceChangeCCC +** +** Description Configure service change indication on remote device +** +** Returns none +** +*******************************************************************************/ +void GATT_ConfigServiceChangeCCC (BD_ADDR remote_bda, BOOLEAN enable, tBT_TRANSPORT transport) +{ + tGATT_PROFILE_CLCB *p_clcb = gatt_profile_find_clcb_by_bd_addr (remote_bda, transport); + + if (p_clcb == NULL) { + p_clcb = gatt_profile_clcb_alloc (0, remote_bda, transport); + } + + if (p_clcb == NULL) { + return; + } + + if (GATT_GetConnIdIfConnected (gatt_cb.gatt_if, remote_bda, &p_clcb->conn_id, transport)) { + p_clcb->connected = TRUE; + } + /* hold the link here */ + GATT_Connect(gatt_cb.gatt_if, remote_bda, BLE_ADDR_UNKNOWN_TYPE, TRUE, transport, FALSE); + p_clcb->ccc_stage = GATT_SVC_CHANGED_CONNECTING; + + if (!p_clcb->connected) { + /* wait for connection */ + return; + } + + p_clcb->ccc_stage ++; + gatt_cl_start_config_ccc(p_clcb); +} + +#if GATTS_ROBUST_CACHING_ENABLED +/******************************************************************************* +** +** Function gatt_sr_is_cl_robust_caching_supported +** +** Description Check if Robust Caching is supported for the connection +** +** Returns true if enabled by client side, otherwise false +** +*******************************************************************************/ +static BOOLEAN gatt_sr_is_cl_robust_caching_supported(tGATT_TCB *p_tcb) +{ + return (p_tcb->cl_supp_feat & BLE_GATT_CL_SUPP_FEAT_ROBUST_CACHING_BITMASK); +} +/******************************************************************************* +** +** Function gatt_sr_is_cl_change_aware +** +** Description Check if the connection is change-aware +** +** Returns true if change aware, otherwise false +** +*******************************************************************************/ +BOOLEAN gatt_sr_is_cl_change_aware(tGATT_TCB *p_tcb) +{ + // If robust caching is not supported, should always return true by default + if (!gatt_sr_is_cl_robust_caching_supported(p_tcb)) { + return true; + } + + return p_tcb->is_robust_cache_change_aware; +} + +/******************************************************************************* +** +** Function gatt_sr_init_cl_status +** +** Description Restore status for trusted device +** +** Returns none +** +*******************************************************************************/ +void gatt_sr_init_cl_status(tGATT_TCB *p_tcb) +{ +#if (SMP_INCLUDED == TRUE) + bta_gatts_co_cl_feat_load(p_tcb->peer_bda, &p_tcb->cl_supp_feat); +#endif + + // This is used to reset bit when robust caching is disabled + if (!GATTS_ROBUST_CACHING_ENABLED) { + p_tcb->cl_supp_feat &= ~BLE_GATT_CL_SUPP_FEAT_ROBUST_CACHING_BITMASK; + } + + if (gatt_sr_is_cl_robust_caching_supported(p_tcb)) { + BT_OCTET16 stored_hash = {0}; +#if (SMP_INCLUDED == TRUE) + bta_gatts_co_db_hash_load(p_tcb->peer_bda, stored_hash); +#endif + p_tcb->is_robust_cache_change_aware = (memcmp(stored_hash, gatt_cb.database_hash, BT_OCTET16_LEN) == 0); + } else { + p_tcb->is_robust_cache_change_aware = true; + } + + GATT_TRACE_DEBUG("%s feat %x aware %d", __func__, p_tcb->cl_supp_feat, p_tcb->is_robust_cache_change_aware); +} + +/******************************************************************************* +** +** Function gatt_sr_update_cl_status +** +** Description Update change-aware status for the remote device +** +** Returns none +** +*******************************************************************************/ +void gatt_sr_update_cl_status(tGATT_TCB *p_tcb, BOOLEAN chg_aware) +{ + if (p_tcb == NULL) { + return; + } + + // if robust caching is not supported, do nothing + if (!gatt_sr_is_cl_robust_caching_supported(p_tcb)) { + return; + } + + // only when client status is changed from unaware to aware, we should store database hash + if (!p_tcb->is_robust_cache_change_aware && chg_aware) { +#if (SMP_INCLUDED == TRUE) + bta_gatts_co_db_hash_save(p_tcb->peer_bda, gatt_cb.database_hash); +#endif + } + + p_tcb->is_robust_cache_change_aware = chg_aware; + + GATT_TRACE_DEBUG("%s status %d", __func__, chg_aware); +} +#endif /* GATTS_ROBUST_CACHING_ENABLED */ +#endif /* BLE_INCLUDED == TRUE && GATTS_INCLUDED == TRUE */ diff --git a/lib/bt/host/bluedroid/stack/gatt/gatt_auth.c b/lib/bt/host/bluedroid/stack/gatt/gatt_auth.c new file mode 100644 index 00000000..4db0a554 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/gatt/gatt_auth.c @@ -0,0 +1,522 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * this file contains GATT authentication handling functions + * + ******************************************************************************/ +#include "common/bt_target.h" +#include "osi/allocator.h" + +#if BLE_INCLUDED == TRUE +#include + +#include "gatt_int.h" +#include "stack/gatt_api.h" +#include "btm_int.h" + +/******************************************************************************* +** +** Function gatt_sign_data +** +** Description This function sign the data for write command. +** +** Returns TRUE if encrypted, otherwise FALSE. +** +*******************************************************************************/ +#if (SMP_INCLUDED == TRUE) +static BOOLEAN gatt_sign_data (tGATT_CLCB *p_clcb) +{ + tGATT_VALUE *p_attr = (tGATT_VALUE *)p_clcb->p_attr_buf; + UINT8 *p_data = NULL, *p; + UINT16 payload_size = p_clcb->p_tcb->payload_size; + BOOLEAN status = FALSE; + UINT8 *p_signature; + + /* do not need to mark channel securoty activity for data signing */ + gatt_set_sec_act(p_clcb->p_tcb, GATT_SEC_OK); + + p_data = (UINT8 *)osi_malloc((UINT16)(p_attr->len + 3)); /* 3 = 2 byte handle + opcode */ + + if (p_data != NULL) { + p = p_data; + UINT8_TO_STREAM(p, GATT_SIGN_CMD_WRITE); + UINT16_TO_STREAM(p, p_attr->handle); + ARRAY_TO_STREAM(p, p_attr->value, p_attr->len); + + /* sign data length should be attribulte value length plus 2B handle + 1B op code */ + if ((payload_size - GATT_AUTH_SIGN_LEN - 3) < p_attr->len) { + p_attr->len = payload_size - GATT_AUTH_SIGN_LEN - 3; + } + + p_signature = p_attr->value + p_attr->len; + if (BTM_BleDataSignature(p_clcb->p_tcb->peer_bda, + p_data, + (UINT16)(p_attr->len + 3), /* 3 = 2 byte handle + opcode */ + p_signature)) { + p_attr->len += BTM_BLE_AUTH_SIGN_LEN; + gatt_set_ch_state(p_clcb->p_tcb, GATT_CH_OPEN); +#if (GATTC_INCLUDED == TRUE) + gatt_act_write(p_clcb, GATT_SEC_SIGN_DATA); +#endif ///GATTC_INCLUDED == TRUE + } else { + gatt_end_operation(p_clcb, GATT_INTERNAL_ERROR, NULL); + } + + osi_free(p_data); + } + + return status; +} +#endif ///SMP_INCLUDED == TRUE + +/******************************************************************************* +** +** Function gatt_verify_signature +** +** Description This function start to verify the sign data when receiving +** the data from peer device. +** +** Returns +** +*******************************************************************************/ +#if (SMP_INCLUDED == TRUE) +void gatt_verify_signature(tGATT_TCB *p_tcb, BT_HDR *p_buf) +{ + UINT16 cmd_len; +#if (GATTS_INCLUDED == TRUE) + UINT8 op_code; +#endif ///GATTS_INCLUDED == TRUE + UINT8 *p, *p_orig = (UINT8 *)(p_buf + 1) + p_buf->offset; + UINT32 counter; + + if (p_buf->len < GATT_AUTH_SIGN_LEN + 4) { + GATT_TRACE_ERROR("%s: Data length %u less than expected %u", + __func__, p_buf->len, GATT_AUTH_SIGN_LEN + 4); + return; + } + cmd_len = p_buf->len - GATT_AUTH_SIGN_LEN + 4; + p = p_orig + cmd_len - 4; + STREAM_TO_UINT32(counter, p); + + if (BTM_BleVerifySignature(p_tcb->peer_bda, p_orig, cmd_len, counter, p)) { +#if (GATTS_INCLUDED == TRUE) + STREAM_TO_UINT8(op_code, p_orig); + gatt_server_handle_client_req (p_tcb, op_code, (UINT16)(p_buf->len - 1), p_orig); +#endif ///GATTS_INCLUDED == TRUE + } else { + /* if this is a bad signature, assume from attacker, ignore it */ + GATT_TRACE_ERROR("Signature Verification Failed, data ignored"); + } + + return; +} +#endif ///SMP_INCLUDED == TRUE + +/******************************************************************************* +** +** Function gatt_sec_check_complete +** +** Description security check complete and proceed to data sending action. +** +** Returns void. +** +*******************************************************************************/ +void gatt_sec_check_complete(BOOLEAN sec_check_ok, tGATT_CLCB *p_clcb, UINT8 sec_act) +{ + if (p_clcb && p_clcb->p_tcb) { + if (fixed_queue_is_empty(p_clcb->p_tcb->pending_enc_clcb)) { + gatt_set_sec_act(p_clcb->p_tcb, GATT_SEC_NONE); + } +#if (GATTC_INCLUDED == TRUE) + if (!sec_check_ok) { + gatt_end_operation(p_clcb, GATT_AUTH_FAIL, NULL); + } else if (p_clcb->operation == GATTC_OPTYPE_WRITE) { + gatt_act_write(p_clcb, sec_act); + } else if (p_clcb->operation == GATTC_OPTYPE_READ) { + gatt_act_read(p_clcb, p_clcb->counter); + } +#endif ///GATTC_INCLUDED == TRUE + } +} +/******************************************************************************* +** +** Function gatt_enc_cmpl_cback +** +** Description link encryption complete callback. +** +** Returns +** +*******************************************************************************/ +void gatt_enc_cmpl_cback(BD_ADDR bd_addr, tBT_TRANSPORT transport, void *p_ref_data, tBTM_STATUS result) +{ + tGATT_TCB *p_tcb; + UINT8 sec_flag; + BOOLEAN status = FALSE; + UNUSED(p_ref_data); + + GATT_TRACE_DEBUG("gatt_enc_cmpl_cback"); + if ((p_tcb = gatt_find_tcb_by_addr(bd_addr, transport)) != NULL) { + if (gatt_get_sec_act(p_tcb) == GATT_SEC_ENC_PENDING) { + return; + } + tGATT_PENDING_ENC_CLCB *p_buf = + (tGATT_PENDING_ENC_CLCB *)fixed_queue_dequeue(p_tcb->pending_enc_clcb, 0); + if (p_buf != NULL) { + if (result == BTM_SUCCESS) { + if (gatt_get_sec_act(p_tcb) == GATT_SEC_ENCRYPT_MITM ) { + BTM_GetSecurityFlagsByTransport(bd_addr, &sec_flag, transport); + + if (sec_flag & BTM_SEC_FLAG_LKEY_AUTHED) { + status = TRUE; + } + } else { + status = TRUE; + } + } + gatt_sec_check_complete(status , p_buf->p_clcb, p_tcb->sec_act); + osi_free(p_buf); + /* start all other pending operation in queue */ + for (size_t count = fixed_queue_length(p_tcb->pending_enc_clcb); + count > 0; count--) { + p_buf = (tGATT_PENDING_ENC_CLCB *)fixed_queue_dequeue(p_tcb->pending_enc_clcb, 0); + if (p_buf != NULL) { + gatt_security_check_start(p_buf->p_clcb); + osi_free(p_buf); + } else { + break; + } + } + } else { + GATT_TRACE_ERROR("Unknown operation encryption completed"); + } + } else { + GATT_TRACE_ERROR("enc callback for unknown bd_addr"); + } +} + +/******************************************************************************* +** +** Function gatt_notify_enc_cmpl +** +** Description link encryption complete notification for all encryption process +** initiated outside GATT. +** +** Returns +** +*******************************************************************************/ +void gatt_notify_enc_cmpl(BD_ADDR bd_addr) +{ + tGATT_TCB *p_tcb; + UINT8 i = 0; + + if ((p_tcb = gatt_find_tcb_by_addr(bd_addr, BT_TRANSPORT_LE)) != NULL) { + for (i = 0; i < GATT_MAX_APPS; i++) { + if (gatt_cb.cl_rcb[i].in_use && gatt_cb.cl_rcb[i].app_cb.p_enc_cmpl_cb) { + (*gatt_cb.cl_rcb[i].app_cb.p_enc_cmpl_cb)(gatt_cb.cl_rcb[i].gatt_if, bd_addr); + } + } + + if (gatt_get_sec_act(p_tcb) == GATT_SEC_ENC_PENDING) { + gatt_set_sec_act(p_tcb, GATT_SEC_NONE); + + size_t count = fixed_queue_length(p_tcb->pending_enc_clcb); + for (; count > 0; count--) { + tGATT_PENDING_ENC_CLCB *p_buf = + (tGATT_PENDING_ENC_CLCB *)fixed_queue_dequeue(p_tcb->pending_enc_clcb, 0); + if (p_buf != NULL) { + gatt_security_check_start(p_buf->p_clcb); + osi_free(p_buf); + } else { + break; + } + } + } + } else { + GATT_TRACE_DEBUG("notify GATT for encryption completion of unknown device"); + } + return; +} +/******************************************************************************* +** +** Function gatt_set_sec_act +** +** Description This function set the sec_act in clcb +** +** Returns none +** +*******************************************************************************/ +void gatt_set_sec_act(tGATT_TCB *p_tcb, tGATT_SEC_ACTION sec_act) +{ + if (p_tcb) { + p_tcb->sec_act = sec_act; + } +} +/******************************************************************************* +** +** Function gatt_get_sec_act +** +** Description This function get the sec_act in clcb +** +** Returns none +** +*******************************************************************************/ +tGATT_SEC_ACTION gatt_get_sec_act(tGATT_TCB *p_tcb) +{ + tGATT_SEC_ACTION sec_act = GATT_SEC_NONE; + if (p_tcb) { + sec_act = p_tcb->sec_act; + } + return sec_act; +} +/******************************************************************************* +** +** Function gatt_determine_sec_act +** +** Description This routine determine the security action based on auth_request and +** current link status +** +** Returns tGATT_SEC_ACTION security action +** +*******************************************************************************/ +tGATT_SEC_ACTION gatt_determine_sec_act(tGATT_CLCB *p_clcb ) +{ + tGATT_SEC_ACTION act = GATT_SEC_OK; + UINT8 sec_flag; + tGATT_TCB *p_tcb = p_clcb->p_tcb; + tGATT_AUTH_REQ auth_req = p_clcb->auth_req; + BOOLEAN is_link_encrypted = FALSE; + BOOLEAN is_link_key_known = FALSE; + BOOLEAN is_key_mitm = FALSE; +#if (SMP_INCLUDED == TRUE) + UINT8 key_type; + tBTM_BLE_SEC_REQ_ACT sec_act = BTM_LE_SEC_NONE; +#endif ///SMP_INCLUDED == TRUE + if (auth_req == GATT_AUTH_REQ_NONE ) { + return act; + } + + BTM_GetSecurityFlagsByTransport(p_tcb->peer_bda, &sec_flag, p_clcb->p_tcb->transport); +#if (SMP_INCLUDED == TRUE) + btm_ble_link_sec_check(p_tcb->peer_bda, auth_req, &sec_act); +#endif ///SMP_INCLUDED == TRUE + /* if a encryption is pending, need to wait */ + if ( +#if (SMP_INCLUDED == TRUE) + sec_act == BTM_BLE_SEC_REQ_ACT_DISCARD && +#endif ///SMP_INCLUDED == TRUE + auth_req != GATT_AUTH_REQ_NONE) { + return GATT_SEC_ENC_PENDING; + } + + if (sec_flag & (BTM_SEC_FLAG_ENCRYPTED | BTM_SEC_FLAG_LKEY_KNOWN)) { + if (sec_flag & BTM_SEC_FLAG_ENCRYPTED) { + is_link_encrypted = TRUE; + } + + is_link_key_known = TRUE; + + if (sec_flag & BTM_SEC_FLAG_LKEY_AUTHED) { + is_key_mitm = TRUE; + } + } + + /* first check link key upgrade required or not */ + switch (auth_req) { + case GATT_AUTH_REQ_MITM: + case GATT_AUTH_REQ_SIGNED_MITM: + if (!is_key_mitm) { + act = GATT_SEC_ENCRYPT_MITM; + } + break; + + case GATT_AUTH_REQ_NO_MITM: + case GATT_AUTH_REQ_SIGNED_NO_MITM: + if (!is_link_key_known) { + act = GATT_SEC_ENCRYPT_NO_MITM; + } + break; + default: + break; + } + + /* now check link needs to be encrypted or not if the link key upgrade is not required */ + if (act == GATT_SEC_OK) { + if (p_tcb->transport == BT_TRANSPORT_LE && + (p_clcb->operation == GATTC_OPTYPE_WRITE) && + (p_clcb->op_subtype == GATT_WRITE_NO_RSP)) { + /* this is a write command request + check data signing required or not */ + if (!is_link_encrypted) { +#if (SMP_INCLUDED == TRUE) + btm_ble_get_enc_key_type(p_tcb->peer_bda, &key_type); +#endif ///SMP_INCLUDED == TRUE + if ( +#if (SMP_INCLUDED == TRUE) + (key_type & BTM_LE_KEY_LCSRK) && +#endif ///SMP_INCLUDED == TRUE + ((auth_req == GATT_AUTH_REQ_SIGNED_NO_MITM) || + (auth_req == GATT_AUTH_REQ_SIGNED_MITM))) { + act = GATT_SEC_SIGN_DATA; + } else { + act = GATT_SEC_ENCRYPT; + } + } + } else { + if (!is_link_encrypted) { + act = GATT_SEC_ENCRYPT; + } + } + + } + + return act ; + +} + + + +/******************************************************************************* +** +** Function gatt_get_link_encrypt_status +** +** Description This routine get the encryption status of the specified link +** +** +** Returns tGATT_STATUS link encryption status +** +*******************************************************************************/ +tGATT_STATUS gatt_get_link_encrypt_status(tGATT_TCB *p_tcb) +{ + tGATT_STATUS encrypt_status = GATT_NOT_ENCRYPTED; + UINT8 sec_flag = 0; + + BTM_GetSecurityFlagsByTransport(p_tcb->peer_bda, &sec_flag, p_tcb->transport); + + if ((sec_flag & BTM_SEC_FLAG_ENCRYPTED) && (sec_flag & BTM_SEC_FLAG_LKEY_KNOWN)) { + encrypt_status = GATT_ENCRYPED_NO_MITM; + if (sec_flag & BTM_SEC_FLAG_LKEY_AUTHED) { + encrypt_status = GATT_ENCRYPED_MITM; + } + } + + GATT_TRACE_DEBUG("gatt_get_link_encrypt_status status=0x%x", encrypt_status); + return encrypt_status ; +} + + +/******************************************************************************* +** +** Function gatt_convert_sec_action +** +** Description Convert GATT security action enum into equivalent BTM BLE security action enum +** +** Returns BOOLEAN TRUE - conversation is successful +** +*******************************************************************************/ +static BOOLEAN gatt_convert_sec_action(tGATT_SEC_ACTION gatt_sec_act, tBTM_BLE_SEC_ACT *p_btm_sec_act ) +{ + BOOLEAN status = TRUE; + switch (gatt_sec_act) { + case GATT_SEC_ENCRYPT: + *p_btm_sec_act = BTM_BLE_SEC_ENCRYPT; + break; + case GATT_SEC_ENCRYPT_NO_MITM: + *p_btm_sec_act = BTM_BLE_SEC_ENCRYPT_NO_MITM; + break; + case GATT_SEC_ENCRYPT_MITM: + *p_btm_sec_act = BTM_BLE_SEC_ENCRYPT_MITM; + break; + default: + status = FALSE; + break; + } + + return status; +} +/******************************************************************************* +** +** Function gatt_check_enc_req +** +** Description check link security. +** +** Returns TRUE if encrypted, otherwise FALSE. +** +*******************************************************************************/ +BOOLEAN gatt_security_check_start(tGATT_CLCB *p_clcb) +{ + tGATT_TCB *p_tcb = p_clcb->p_tcb; + tGATT_SEC_ACTION gatt_sec_act; + tBTM_BLE_SEC_ACT btm_ble_sec_act; + BOOLEAN status = TRUE; +#if (SMP_INCLUDED == TRUE) + tBTM_STATUS btm_status; +#endif ///SMP_INCLUDED == TRUE + tGATT_SEC_ACTION sec_act_old = gatt_get_sec_act(p_tcb); + + gatt_sec_act = gatt_determine_sec_act(p_clcb); + + if (sec_act_old == GATT_SEC_NONE) { + gatt_set_sec_act(p_tcb, gatt_sec_act); + } + + switch (gatt_sec_act ) { + case GATT_SEC_SIGN_DATA: +#if (SMP_INCLUDED == TRUE) + GATT_TRACE_DEBUG("gatt_security_check_start: Do data signing"); + gatt_sign_data(p_clcb); +#endif ///SMP_INCLUDED == TRUE + break; + case GATT_SEC_ENCRYPT: + case GATT_SEC_ENCRYPT_NO_MITM: + case GATT_SEC_ENCRYPT_MITM: + if (sec_act_old < GATT_SEC_ENCRYPT) { + GATT_TRACE_DEBUG("gatt_security_check_start: Encrypt now or key upgreade first"); + gatt_convert_sec_action(gatt_sec_act, &btm_ble_sec_act); +#if (SMP_INCLUDED == TRUE) + btm_status = BTM_SetEncryption(p_tcb->peer_bda, p_tcb->transport , gatt_enc_cmpl_cback, &btm_ble_sec_act); + if ( (btm_status != BTM_SUCCESS) && (btm_status != BTM_CMD_STARTED)) { + GATT_TRACE_ERROR("gatt_security_check_start BTM_SetEncryption failed btm_status=%d", btm_status); + status = FALSE; + } +#endif ///SMP_INCLUDED == TRUE + } + if (status) { + gatt_add_pending_enc_channel_clcb (p_tcb, p_clcb); + } + break; + case GATT_SEC_ENC_PENDING: + gatt_add_pending_enc_channel_clcb (p_tcb, p_clcb); + /* wait for link encrypotion to finish */ + break; + default: + gatt_sec_check_complete(TRUE, p_clcb, gatt_sec_act); + break; + } + + if (status == FALSE) { + gatt_set_sec_act(p_tcb, GATT_SEC_NONE); + gatt_set_ch_state(p_tcb, GATT_CH_OPEN); + } + + return status; +} + + +#endif /* BLE_INCLUDED */ diff --git a/lib/bt/host/bluedroid/stack/gatt/gatt_cl.c b/lib/bt/host/bluedroid/stack/gatt/gatt_cl.c new file mode 100644 index 00000000..12168dc0 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/gatt/gatt_cl.c @@ -0,0 +1,1241 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * this file contains the main GATT client functions + * + ******************************************************************************/ + +#include "common/bt_target.h" + +#if BLE_INCLUDED == TRUE && GATTC_INCLUDED == TRUE + +#include +#include "osi/allocator.h" +#include "gatt_int.h" +#include "l2c_int.h" + +#define GATT_WRITE_LONG_HDR_SIZE 5 /* 1 opcode + 2 handle + 2 offset */ +#define GATT_READ_CHAR_VALUE_HDL (GATT_READ_CHAR_VALUE | 0x80) +#define GATT_READ_INC_SRV_UUID128 (GATT_DISC_INC_SRVC | 0x90) + +#define GATT_PREP_WRITE_RSP_MIN_LEN 4 +#define GATT_NOTIFICATION_MIN_LEN 2 +#define GATT_WRITE_RSP_MIN_LEN 2 +#define GATT_INFO_RSP_MIN_LEN 1 +#define GATT_MTU_RSP_MIN_LEN 2 +#define GATT_READ_BY_TYPE_RSP_MIN_LEN 1 + +/******************************************************************************** +** G L O B A L G A T T D A T A * +*********************************************************************************/ +void gatt_send_prepare_write(tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb); + +static const UINT8 disc_type_to_att_opcode[GATT_DISC_MAX] = { + 0, + GATT_REQ_READ_BY_GRP_TYPE, /* GATT_DISC_SRVC_ALL = 1, */ + GATT_REQ_FIND_TYPE_VALUE, /* GATT_DISC_SRVC_BY_UUID, */ + GATT_REQ_READ_BY_TYPE, /* GATT_DISC_INC_SRVC, */ + GATT_REQ_READ_BY_TYPE, /* GATT_DISC_CHAR, */ + GATT_REQ_READ_BY_TYPE, /* GATT_DISC_CHAR_BY_UUID, */ + GATT_REQ_FIND_INFO /* GATT_DISC_CHAR_DSCPT, */ +}; + +static const UINT16 disc_type_to_uuid[GATT_DISC_MAX] = { + 0, /* reserved */ + GATT_UUID_PRI_SERVICE, /* DISC_SRVC_ALL */ + GATT_UUID_PRI_SERVICE, /* for DISC_SERVC_BY_UUID */ + GATT_UUID_INCLUDE_SERVICE, /* for DISC_INC_SRVC */ + GATT_UUID_CHAR_DECLARE, /* for DISC_CHAR */ + 0 /* no type filtering for DISC_CHAR_DSCPT */ +}; + +// Use for GATTC discover infomation print +#define GATT_DISC_INFO(fmt, args...) {if (gatt_cb.auto_disc == FALSE) BT_PRINT_I("BT_GATT", fmt, ## args);} + +/******************************************************************************* +** +** Function gatt_act_discovery +** +** Description GATT discovery operation. +** +** Returns void. +** +*******************************************************************************/ +void gatt_act_discovery(tGATT_CLCB *p_clcb) +{ + UINT8 op_code = disc_type_to_att_opcode[p_clcb->op_subtype]; + tGATT_CL_MSG cl_req; + tGATT_STATUS st; + + if (p_clcb->s_handle <= p_clcb->e_handle && p_clcb->s_handle != 0) { + memset(&cl_req, 0, sizeof(tGATT_CL_MSG)); + + cl_req.browse.s_handle = p_clcb->s_handle; + cl_req.browse.e_handle = p_clcb->e_handle; + + if (disc_type_to_uuid[p_clcb->op_subtype] != 0) { + cl_req.browse.uuid.len = 2; + cl_req.browse.uuid.uu.uuid16 = disc_type_to_uuid[p_clcb->op_subtype]; + } + + if (p_clcb->op_subtype == GATT_DISC_SRVC_BY_UUID) { /* fill in the FindByTypeValue request info*/ + cl_req.find_type_value.uuid.len = 2; + cl_req.find_type_value.uuid.uu.uuid16 = disc_type_to_uuid[p_clcb->op_subtype]; + cl_req.find_type_value.s_handle = p_clcb->s_handle; + cl_req.find_type_value.e_handle = p_clcb->e_handle; + cl_req.find_type_value.value_len = p_clcb->uuid.len; + /* if service type is 32 bits UUID, convert it now */ + if (p_clcb->uuid.len == LEN_UUID_32) { + cl_req.find_type_value.value_len = LEN_UUID_128; + gatt_convert_uuid32_to_uuid128(cl_req.find_type_value.value, p_clcb->uuid.uu.uuid32); + } else { + memcpy (cl_req.find_type_value.value, &p_clcb->uuid.uu, p_clcb->uuid.len); + } + } + + if (p_clcb->op_subtype == GATT_DISC_CHAR_BY_UUID) { + memcpy(&cl_req.browse.uuid, &p_clcb->uuid, sizeof(tBT_UUID)); + } + + st = attp_send_cl_msg(p_clcb->p_tcb, p_clcb->clcb_idx, op_code, &cl_req); + + if (st != GATT_SUCCESS && st != GATT_CMD_STARTED) { + gatt_end_operation(p_clcb, GATT_ERROR, NULL); + } + } else { /* end of handle range */ + gatt_end_operation(p_clcb, GATT_SUCCESS, NULL); + } +} + +/******************************************************************************* +** +** Function gatt_act_read +** +** Description GATT read operation. +** +** Returns void. +** +*******************************************************************************/ +void gatt_act_read (tGATT_CLCB *p_clcb, UINT16 offset) +{ + tGATT_TCB *p_tcb = p_clcb->p_tcb; + UINT8 rt = GATT_INTERNAL_ERROR; + tGATT_CL_MSG msg; + UINT8 op_code = 0; + + memset (&msg, 0, sizeof(tGATT_CL_MSG)); + + switch (p_clcb->op_subtype) { + case GATT_READ_CHAR_VALUE: + case GATT_READ_BY_TYPE: + op_code = GATT_REQ_READ_BY_TYPE; + msg.browse.s_handle = p_clcb->s_handle; + msg.browse.e_handle = p_clcb->e_handle; + if (p_clcb->op_subtype == GATT_READ_BY_TYPE) { + memcpy(&msg.browse.uuid, &p_clcb->uuid, sizeof(tBT_UUID)); + } else { + msg.browse.uuid.len = LEN_UUID_16; + msg.browse.uuid.uu.uuid16 = GATT_UUID_CHAR_DECLARE; + } + break; + + case GATT_READ_CHAR_VALUE_HDL: + case GATT_READ_BY_HANDLE: + if (!p_clcb->counter) { + op_code = GATT_REQ_READ; + msg.handle = p_clcb->s_handle; + } else { + if (!p_clcb->first_read_blob_after_read) { + p_clcb->first_read_blob_after_read = TRUE; + } else { + p_clcb->first_read_blob_after_read = FALSE; + } + + GATT_TRACE_DEBUG("gatt_act_read first_read_blob_after_read=%d", + p_clcb->first_read_blob_after_read); + op_code = GATT_REQ_READ_BLOB; + msg.read_blob.offset = offset; + msg.read_blob.handle = p_clcb->s_handle; + } + p_clcb->op_subtype &= ~ 0x80; + break; + + case GATT_READ_PARTIAL: + op_code = GATT_REQ_READ_BLOB; + msg.read_blob.handle = p_clcb->s_handle; + msg.read_blob.offset = offset; + break; + + case GATT_READ_MULTIPLE: + op_code = GATT_REQ_READ_MULTI; + memcpy (&msg.read_multi, p_clcb->p_attr_buf, sizeof(tGATT_READ_MULTI)); + break; + + case GATT_READ_MULTIPLE_VAR: + op_code = GATT_REQ_READ_MULTI_VAR; + memcpy (&msg.read_multi, p_clcb->p_attr_buf, sizeof(tGATT_READ_MULTI)); + break; + + case GATT_READ_INC_SRV_UUID128: + op_code = GATT_REQ_READ; + msg.handle = p_clcb->s_handle; + p_clcb->op_subtype &= ~ 0x90; + break; + + default: + GATT_TRACE_ERROR("Unknown read type: %d", p_clcb->op_subtype); + break; + } + + if (op_code != 0) { + rt = attp_send_cl_msg(p_tcb, p_clcb->clcb_idx, op_code, &msg); + } + + if ( op_code == 0 || (rt != GATT_SUCCESS && rt != GATT_CMD_STARTED)) { + gatt_end_operation(p_clcb, rt, NULL); + } +} + +/******************************************************************************* +** +** Function gatt_act_write +** +** Description GATT write operation. +** +** Returns void. +** +*******************************************************************************/ +void gatt_act_write (tGATT_CLCB *p_clcb, UINT8 sec_act) +{ + tGATT_TCB *p_tcb = p_clcb->p_tcb; + UINT8 rt = GATT_SUCCESS, op_code = 0; + tGATT_VALUE *p_attr = (tGATT_VALUE *)p_clcb->p_attr_buf; + + if (p_attr) { + switch (p_clcb->op_subtype) { + case GATT_WRITE_NO_RSP: + l2ble_update_att_acl_pkt_num(L2CA_DECREASE_BTU_NUM, NULL); + p_clcb->s_handle = p_attr->handle; + op_code = (sec_act == GATT_SEC_SIGN_DATA) ? GATT_SIGN_CMD_WRITE : GATT_CMD_WRITE; + rt = gatt_send_write_msg(p_tcb, + p_clcb->clcb_idx, + op_code, + p_attr->handle, + p_attr->len, + 0, + p_attr->value); + break; + + case GATT_WRITE: + if (p_attr->len <= (p_tcb->payload_size - GATT_HDR_SIZE)) { + p_clcb->s_handle = p_attr->handle; + + rt = gatt_send_write_msg(p_tcb, + p_clcb->clcb_idx, + GATT_REQ_WRITE, + p_attr->handle, + p_attr->len, + 0, + p_attr->value); + } else { /* prepare write for long attribute */ + gatt_send_prepare_write(p_tcb, p_clcb); + } + break; + + case GATT_WRITE_PREPARE: + gatt_send_prepare_write(p_tcb, p_clcb); + break; + + default: + rt = GATT_INTERNAL_ERROR; + GATT_TRACE_ERROR("Unknown write type: %d", p_clcb->op_subtype); + break; + } + } else { + rt = GATT_INTERNAL_ERROR; + } + + if ((rt != GATT_SUCCESS && rt != GATT_CMD_STARTED && rt != GATT_CONGESTED) + || (rt != GATT_CMD_STARTED && p_clcb->op_subtype == GATT_WRITE_NO_RSP)) { + if (rt != GATT_SUCCESS) { + GATT_TRACE_DEBUG("gatt_act_write() failed op_code=0x%x rt=%d", op_code, rt); + } + gatt_end_operation(p_clcb, rt, NULL); + } +} +/******************************************************************************* +** +** Function gatt_send_queue_write_cancel +** +** Description send queue write cancel +** +** Returns void. +** +*******************************************************************************/ +void gatt_send_queue_write_cancel (tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb, tGATT_EXEC_FLAG flag) +{ + UINT8 rt ; + + GATT_TRACE_DEBUG("gatt_send_queue_write_cancel "); + + rt = attp_send_cl_msg(p_tcb, p_clcb->clcb_idx, GATT_REQ_EXEC_WRITE, (tGATT_CL_MSG *)&flag); + + if (rt != GATT_SUCCESS) { + gatt_end_operation(p_clcb, rt, NULL); + } +} +/******************************************************************************* +** +** Function gatt_check_write_long_terminate +** +** Description To terminate write long or not. +** +** Returns TRUE: write long is terminated; FALSE keep sending. +** +*******************************************************************************/ +BOOLEAN gatt_check_write_long_terminate(tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb, tGATT_VALUE *p_rsp_value) +{ + tGATT_VALUE *p_attr = (tGATT_VALUE *)p_clcb->p_attr_buf; + BOOLEAN exec = FALSE; + tGATT_EXEC_FLAG flag = GATT_PREP_WRITE_EXEC; + + GATT_TRACE_DEBUG("gatt_check_write_long_terminate "); + /* check the first write response status */ + if (p_rsp_value != NULL) { + if (p_rsp_value->handle != p_attr->handle || + p_rsp_value->len != p_clcb->counter || + memcmp(p_rsp_value->value, p_attr->value + p_attr->offset, p_rsp_value->len)) { + /* data does not match */ + p_clcb->status = GATT_ERROR; + flag = GATT_PREP_WRITE_CANCEL; + exec = TRUE; + } else { /* response checking is good */ + p_clcb->status = GATT_SUCCESS; + /* update write offset and check if end of attribute value */ + if ((p_attr->offset += p_rsp_value->len) >= p_attr->len) { + exec = TRUE; + } + } + } + if (exec) { + gatt_send_queue_write_cancel (p_tcb, p_clcb, flag); + return TRUE; + } + return FALSE; +} +/******************************************************************************* +** +** Function gatt_send_prepare_write +** +** Description Send prepare write. +** +** Returns void. +** +*******************************************************************************/ +void gatt_send_prepare_write(tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb) +{ + tGATT_VALUE *p_attr = (tGATT_VALUE *)p_clcb->p_attr_buf; + UINT16 to_send, offset; + UINT8 rt = GATT_SUCCESS; + UINT8 type = p_clcb->op_subtype; + + GATT_TRACE_DEBUG("gatt_send_prepare_write type=0x%x", type ); + to_send = p_attr->len - p_attr->offset; + + if (to_send > (p_tcb->payload_size - GATT_WRITE_LONG_HDR_SIZE)) { /* 2 = UINT16 offset bytes */ + to_send = p_tcb->payload_size - GATT_WRITE_LONG_HDR_SIZE; + } + + p_clcb->s_handle = p_attr->handle; + + offset = p_attr->offset; + if (type == GATT_WRITE_PREPARE) { + offset += p_clcb->start_offset; + } + + GATT_TRACE_DEBUG("offset =0x%x len=%d", offset, to_send ); + + rt = gatt_send_write_msg(p_tcb, + p_clcb->clcb_idx, + GATT_REQ_PREPARE_WRITE, + p_attr->handle, + to_send, /* length */ + offset, /* used as offset */ + p_attr->value + p_attr->offset); /* data */ + + /* remember the write long attribute length */ + p_clcb->counter = to_send; + + if (rt != GATT_SUCCESS && rt != GATT_CMD_STARTED) { + gatt_end_operation(p_clcb, rt, NULL); + } +} + + +/******************************************************************************* +** +** Function gatt_process_find_type_value_rsp +** +** Description This function is called to handle find by type value response. +** +** +** Returns void +** +*******************************************************************************/ +void gatt_process_find_type_value_rsp (tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb, UINT16 len, UINT8 *p_data) +{ + tGATT_DISC_RES result; + UINT8 *p = p_data; + + UNUSED(p_tcb); + + GATT_TRACE_DEBUG("gatt_process_find_type_value_rsp "); + /* unexpected response */ + if (p_clcb->operation != GATTC_OPTYPE_DISCOVERY || p_clcb->op_subtype != GATT_DISC_SRVC_BY_UUID) { + return; + } + + memset (&result, 0, sizeof(tGATT_DISC_RES)); + result.type.len = 2; + result.type.uu.uuid16 = GATT_UUID_PRI_SERVICE; + + /* returns a series of handle ranges */ + while (len >= 4) { + STREAM_TO_UINT16 (result.handle, p); + STREAM_TO_UINT16 (result.value.group_value.e_handle, p); + GATT_DISC_INFO("%s handle %x, end handle %x", __func__, result.handle, result.value.group_value.e_handle); + memcpy (&result.value.group_value.service_type, &p_clcb->uuid, sizeof(tBT_UUID)); + + len -= 4; + + if (p_clcb->p_reg->app_cb.p_disc_res_cb) { + (*p_clcb->p_reg->app_cb.p_disc_res_cb)(p_clcb->conn_id, p_clcb->op_subtype, &result); + } + } + + /* last handle + 1 */ + p_clcb->s_handle = (result.value.group_value.e_handle == 0) ? 0 : (result.value.group_value.e_handle + 1); + /* initiate another request */ + gatt_act_discovery(p_clcb) ; +} +/******************************************************************************* +** +** Function gatt_process_read_info_rsp +** +** Description This function is called to handle the read information +** response. +** +** +** Returns void +** +*******************************************************************************/ +void gatt_process_read_info_rsp(tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb, UINT8 op_code, + UINT16 len, UINT8 *p_data) +{ + tGATT_DISC_RES result; + UINT8 *p = p_data, uuid_len = 0, type; + + UNUSED(p_tcb); + UNUSED(op_code); + + if (len < GATT_INFO_RSP_MIN_LEN) { + GATT_TRACE_ERROR("invalid Info Response PDU received, discard."); + gatt_end_operation(p_clcb, GATT_INVALID_PDU, NULL); + return; + } + /* unexpected response */ + if (p_clcb->operation != GATTC_OPTYPE_DISCOVERY || p_clcb->op_subtype != GATT_DISC_CHAR_DSCPT) { + return; + } + + STREAM_TO_UINT8(type, p); + len -= 1; + + if (type == GATT_INFO_TYPE_PAIR_16) { + uuid_len = LEN_UUID_16; + } else if (type == GATT_INFO_TYPE_PAIR_128) { + uuid_len = LEN_UUID_128; + } + + while (len >= uuid_len + 2) { + STREAM_TO_UINT16 (result.handle, p); + + if (uuid_len > 0) { + if (!gatt_parse_uuid_from_cmd(&result.type, uuid_len, &p)) { + break; + } + } else { + memcpy (&result.type, &p_clcb->uuid, sizeof(tBT_UUID)); + } + + len -= (uuid_len + 2); + + GATT_DISC_INFO("%s handle %x, uuid %s", __func__, result.handle, gatt_uuid_to_str(&result.type)); + + if (p_clcb->p_reg->app_cb.p_disc_res_cb) { + (*p_clcb->p_reg->app_cb.p_disc_res_cb)(p_clcb->conn_id, p_clcb->op_subtype, &result); + } + } + + p_clcb->s_handle = (result.handle == 0) ? 0 : (result.handle + 1); + /* initiate another request */ + gatt_act_discovery(p_clcb) ; +} +/******************************************************************************* +** +** Function gatt_proc_disc_error_rsp +** +** Description This function process the read by type response and send another +** request if needed. +** +** Returns void. +** +*******************************************************************************/ +void gatt_proc_disc_error_rsp(tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb, UINT8 opcode, + UINT16 handle, UINT8 reason) +{ + tGATT_STATUS status = (tGATT_STATUS) reason; + + UNUSED(p_tcb); + UNUSED(handle); + + GATT_TRACE_DEBUG("gatt_proc_disc_error_rsp reason: %02x cmd_code %04x", reason, opcode); + + switch (opcode) { + case GATT_REQ_READ_BY_GRP_TYPE: + case GATT_REQ_FIND_TYPE_VALUE: + case GATT_REQ_READ_BY_TYPE: + case GATT_REQ_FIND_INFO: + if (reason == GATT_NOT_FOUND) { + status = GATT_SUCCESS; + GATT_DISC_INFO("Discovery completed"); + } + break; + default: + GATT_TRACE_ERROR("Incorrect discovery opcode %04x", opcode); + break; + } + + gatt_end_operation(p_clcb, status, NULL); +} + +/******************************************************************************* +** +** Function gatt_process_error_rsp +** +** Description This function is called to handle the error response +** +** +** Returns void +** +*******************************************************************************/ +void gatt_process_error_rsp(tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb, UINT8 op_code, + UINT16 len, UINT8 *p_data) +{ + UINT8 opcode, reason, * p = p_data; + UINT16 handle; + tGATT_VALUE *p_attr = (tGATT_VALUE *)p_clcb->p_attr_buf; + + UNUSED(op_code); + UNUSED(len); + + GATT_TRACE_DEBUG("%s", __func__); + STREAM_TO_UINT8(opcode, p); + STREAM_TO_UINT16(handle, p); + STREAM_TO_UINT8(reason, p); + + if (p_clcb->operation == GATTC_OPTYPE_DISCOVERY) { + gatt_proc_disc_error_rsp(p_tcb, p_clcb, opcode, handle, reason); + } else { + if ( (p_clcb->operation == GATTC_OPTYPE_WRITE) && + (p_clcb->op_subtype == GATT_WRITE) && + (opcode == GATT_REQ_PREPARE_WRITE) && + (p_attr) && + (handle == p_attr->handle) ) { + if (reason == GATT_SUCCESS){ + reason = GATT_ERROR; + } + p_clcb->status = reason; + gatt_send_queue_write_cancel(p_tcb, p_clcb, GATT_PREP_WRITE_CANCEL); + } else if ((p_clcb->operation == GATTC_OPTYPE_READ) && + ((p_clcb->op_subtype == GATT_READ_CHAR_VALUE_HDL) || + (p_clcb->op_subtype == GATT_READ_BY_HANDLE)) && + (opcode == GATT_REQ_READ_BLOB) && + p_clcb->first_read_blob_after_read && + (reason == GATT_NOT_LONG)) { + gatt_end_operation(p_clcb, GATT_SUCCESS, (void *)p_clcb->p_attr_buf); + } else { + gatt_end_operation(p_clcb, reason, NULL); + } + } +} +/******************************************************************************* +** +** Function gatt_process_prep_write_rsp +** +** Description This function is called to handle the read response +** +** +** Returns void +** +*******************************************************************************/ +void gatt_process_prep_write_rsp (tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb, UINT8 op_code, + UINT16 len, UINT8 *p_data) +{ + tGATT_VALUE value = {0}; + UINT8 *p = p_data; + + GATT_TRACE_DEBUG("value resp op_code = %s len = %d", gatt_dbg_op_name(op_code), len); + + if (len < GATT_PREP_WRITE_RSP_MIN_LEN) { + GATT_TRACE_ERROR("illegal prepare write response length, discard"); + gatt_end_operation(p_clcb, GATT_INVALID_PDU, &value); + return; + } + + STREAM_TO_UINT16 (value.handle, p); + STREAM_TO_UINT16 (value.offset, p); + + value.len = len - 4; + + memcpy (value.value, p, value.len); + + if (p_clcb->op_subtype == GATT_WRITE_PREPARE) { + p_clcb->status = GATT_SUCCESS; + /* application should verify handle offset + and value are matched or not */ + + gatt_end_operation(p_clcb, p_clcb->status, &value); + } else if (p_clcb->op_subtype == GATT_WRITE ) { + if (!gatt_check_write_long_terminate(p_tcb, p_clcb, &value)) { + gatt_send_prepare_write(p_tcb, p_clcb); + } + } + +} +/******************************************************************************* +** +** Function gatt_process_notification +** +** Description This function is called to handle the handle value indication +** or handle value notification. +** +** +** Returns void +** +*******************************************************************************/ +void gatt_process_notification(tGATT_TCB *p_tcb, UINT8 op_code, + UINT16 len, UINT8 *p_data) +{ + tGATT_VALUE value = {0}; + tGATT_REG *p_reg; + UINT16 conn_id; + tGATT_STATUS encrypt_status; + UINT8 *p = p_data, i, + event = (op_code == GATT_HANDLE_VALUE_IND) ? GATTC_OPTYPE_INDICATION: GATTC_OPTYPE_NOTIFICATION; + + GATT_TRACE_DEBUG("gatt_process_notification "); + + if (len < GATT_NOTIFICATION_MIN_LEN) { + GATT_TRACE_ERROR("illegal notification PDU length, discard"); + return; + } + + STREAM_TO_UINT16 (value.handle, p); + + if (!GATT_HANDLE_IS_VALID(value.handle)) { + /* illegal handle, send ack now */ + if (op_code == GATT_HANDLE_VALUE_IND) { + attp_send_cl_msg(p_tcb, 0, GATT_HANDLE_VALUE_CONF, NULL); + } + return; + } + + if (op_code == GATT_HANDLE_MULTI_VALUE_NOTIF) { + if (len < GATT_NOTIFICATION_MIN_LEN + 2) { + GATT_TRACE_ERROR("illegal notification PDU length, discard"); + return; + } + + STREAM_TO_UINT16(value.len, p); + if (value.len > len - 4) { + return; + } + } else { + value.len = len - 2; + } + + if (value.len > GATT_MAX_ATTR_LEN) { + GATT_TRACE_ERROR("value length larger than GATT_MAX_ATTR_LEN, discard"); + return; + } + + memcpy(value.value, p, value.len); + p += value.len; + + if (event == GATTC_OPTYPE_INDICATION) { + if (p_tcb->ind_count) { + /* this is an error case that receiving an indication but we + still has an indication not being acked yet. + For now, just log the error reset the counter. + Later we need to disconnect the link unconditionally. + */ + GATT_TRACE_ERROR("gatt_process_notification rcv Ind. but ind_count=%d (will reset ind_count)", p_tcb->ind_count); + } + p_tcb->ind_count = 0; + + /* should notify all registered client with the handle value notificaion/indication + Note: need to do the indication count and start timer first then do callback + */ + for (i = 0, p_reg = gatt_cb.cl_rcb; i < GATT_MAX_APPS; i++, p_reg++) { + if (p_reg->in_use && p_reg->app_cb.p_cmpl_cb && (event == GATTC_OPTYPE_INDICATION)) { + p_tcb->ind_count++; + } + } + + /* start a timer for app confirmation */ + if (p_tcb->ind_count > 0) { + gatt_start_ind_ack_timer(p_tcb); + } else { /* no app to indicate, or invalid handle */ + attp_send_cl_msg(p_tcb, 0, GATT_HANDLE_VALUE_CONF, NULL); + } + } + + encrypt_status = gatt_get_link_encrypt_status(p_tcb); + for (i = 0, p_reg = gatt_cb.cl_rcb; i < GATT_MAX_APPS; i++, p_reg++) { + if (p_reg->in_use && p_reg->app_cb.p_cmpl_cb) { + conn_id = GATT_CREATE_CONN_ID(p_tcb->tcb_idx, p_reg->gatt_if); + (*p_reg->app_cb.p_cmpl_cb) (conn_id, event, encrypt_status, (tGATT_CL_COMPLETE *)&value); + } + } + + if (op_code != GATT_HANDLE_MULTI_VALUE_NOTIF) { + return; + } + + if (len < (4 + value.len)) { + GATT_TRACE_ERROR("no remain data for multi notification"); + return; + } + + len -= (4 + value.len); + + while (len > 4) { + STREAM_TO_UINT16(value.handle, p); + STREAM_TO_UINT16(value.len, p); + len -= 4; + value.len = MIN(len, value.len); + memcpy(value.value, p, value.len); + p += value.len; + len -= value.len; + + for (i = 0, p_reg = gatt_cb.cl_rcb; i < GATT_MAX_APPS; i++, p_reg++) { + if (p_reg->in_use && p_reg->app_cb.p_cmpl_cb) { + conn_id = GATT_CREATE_CONN_ID(p_tcb->tcb_idx, p_reg->gatt_if); + (*p_reg->app_cb.p_cmpl_cb) (conn_id, event, encrypt_status, (tGATT_CL_COMPLETE *)&value); + } + } + } +} + +/******************************************************************************* +** +** Function gatt_process_read_by_type_rsp +** +** Description This function is called to handle the read by type response. +** read by type can be used for discovery, or read by type or +** read characteristic value. +** +** Returns void +** +*******************************************************************************/ +void gatt_process_read_by_type_rsp (tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb, UINT8 op_code, + UINT16 len, UINT8 *p_data) +{ + tGATT_DISC_RES result; + tGATT_DISC_VALUE record_value; + UINT8 *p = p_data, value_len, handle_len = 2; + UINT16 handle = 0; + + /* discovery procedure and no callback function registered */ + if (((!p_clcb->p_reg) || (!p_clcb->p_reg->app_cb.p_disc_res_cb)) && (p_clcb->operation == GATTC_OPTYPE_DISCOVERY)) { + return; + } + + if (len < GATT_READ_BY_TYPE_RSP_MIN_LEN) { + GATT_TRACE_ERROR("Illegal ReadByType/ReadByGroupType Response length, discard"); + gatt_end_operation(p_clcb, GATT_INVALID_PDU, NULL); + return; + } + + STREAM_TO_UINT8(value_len, p); + + if ((value_len > (p_tcb->payload_size - 2)) || (value_len > (len - 1)) ) { + /* this is an error case that server's response containing a value length which is larger than MTU-2 + or value_len > message total length -1 */ + GATT_TRACE_ERROR("gatt_process_read_by_type_rsp: Discard response op_code=%d vale_len=%d > (MTU-2=%d or msg_len-1=%d)", + op_code, value_len, (p_tcb->payload_size - 2), (len - 1)); + gatt_end_operation(p_clcb, GATT_ERROR, NULL); + return; + } + + if (op_code == GATT_RSP_READ_BY_GRP_TYPE) { + handle_len = 4; + } + + value_len -= handle_len; /* substract the handle pairs bytes */ + len -= 1; + + while (len >= (handle_len + value_len)) { + STREAM_TO_UINT16(handle, p); + GATT_DISC_INFO("%s op %x, handle %x", __func__, op_code, handle); + + if (!GATT_HANDLE_IS_VALID(handle)) { + gatt_end_operation(p_clcb, GATT_INVALID_HANDLE, NULL); + return; + } + + memset(&result, 0, sizeof(tGATT_DISC_RES)); + memset(&record_value, 0, sizeof(tGATT_DISC_VALUE)); + + result.handle = handle; + result.type.len = 2; + result.type.uu.uuid16 = disc_type_to_uuid[p_clcb->op_subtype]; + + /* discover all services */ + if (p_clcb->operation == GATTC_OPTYPE_DISCOVERY && + p_clcb->op_subtype == GATT_DISC_SRVC_ALL && + op_code == GATT_RSP_READ_BY_GRP_TYPE) { + STREAM_TO_UINT16(handle, p); + + if (!GATT_HANDLE_IS_VALID(handle)) { + gatt_end_operation(p_clcb, GATT_INVALID_HANDLE, NULL); + return; + } else { + record_value.group_value.e_handle = handle; + if (!gatt_parse_uuid_from_cmd(&record_value.group_value.service_type, value_len, &p)) { + GATT_TRACE_ERROR("discover all service response parsing failure"); + break; + } + } + GATT_DISC_INFO("DISC ALL SVC end handle %x, uuid %s", record_value.group_value.e_handle, gatt_uuid_to_str(&record_value.group_value.service_type)); + } + /* discover included service */ + else if (p_clcb->operation == GATTC_OPTYPE_DISCOVERY && p_clcb->op_subtype == GATT_DISC_INC_SRVC) { + STREAM_TO_UINT16(record_value.incl_service.s_handle, p); + STREAM_TO_UINT16(record_value.incl_service.e_handle, p); + + if (!GATT_HANDLE_IS_VALID(record_value.incl_service.s_handle) || + !GATT_HANDLE_IS_VALID(record_value.incl_service.e_handle)) { + gatt_end_operation(p_clcb, GATT_INVALID_HANDLE, NULL); + return; + } + + if (value_len == 6) { + STREAM_TO_UINT16(record_value.incl_service.service_type.uu.uuid16, p); + record_value.incl_service.service_type.len = LEN_UUID_16; + GATT_DISC_INFO("DISC INC SVC start handle %x, end handle %x, uuid %s", + record_value.incl_service.s_handle, record_value.incl_service.e_handle, gatt_uuid_to_str(&record_value.incl_service.service_type)); + } else if (value_len == 4) { + p_clcb->s_handle = record_value.incl_service.s_handle; + p_clcb->read_uuid128.wait_for_read_rsp = TRUE; + p_clcb->read_uuid128.next_disc_start_hdl = handle + 1; + memcpy(&p_clcb->read_uuid128.result, &result, sizeof(result)); + memcpy(&p_clcb->read_uuid128.result.value, &record_value, sizeof (result.value)); + p_clcb->op_subtype |= 0x90; + gatt_act_read(p_clcb, 0); // read 128-bit uuid of include service + return; + } else { + GATT_TRACE_ERROR("gatt_process_read_by_type_rsp INCL_SRVC failed with invalid data value_len=%d", value_len); + gatt_end_operation(p_clcb, GATT_INVALID_PDU, (void *)p); + return; + } + } + /* read by type */ + else if (p_clcb->operation == GATTC_OPTYPE_READ && p_clcb->op_subtype == GATT_READ_BY_TYPE) { + p_clcb->counter = len - 2; + p_clcb->s_handle = handle; + if ( p_clcb->counter == (p_clcb->p_tcb->payload_size - 4)) { + p_clcb->op_subtype = GATT_READ_BY_HANDLE; + if (!p_clcb->p_attr_buf) { + p_clcb->p_attr_buf = (UINT8 *)osi_malloc(GATT_MAX_ATTR_LEN); + } + if (p_clcb->p_attr_buf && p_clcb->counter <= GATT_MAX_ATTR_LEN) { + memcpy(p_clcb->p_attr_buf, p, p_clcb->counter); + gatt_act_read(p_clcb, p_clcb->counter); + } else { + gatt_end_operation(p_clcb, GATT_INTERNAL_ERROR, (void *)p); + } + } else { + gatt_end_operation(p_clcb, GATT_SUCCESS, (void *)p); + } + return; + } else { /* discover characterisitic */ + STREAM_TO_UINT8 (record_value.dclr_value.char_prop, p); + STREAM_TO_UINT16(record_value.dclr_value.val_handle, p); + if (!GATT_HANDLE_IS_VALID(record_value.dclr_value.val_handle)) { + gatt_end_operation(p_clcb, GATT_INVALID_HANDLE, NULL); + return; + } + if (!gatt_parse_uuid_from_cmd(&record_value.dclr_value.char_uuid, (UINT16)(value_len - 3), &p)) { + gatt_end_operation(p_clcb, GATT_SUCCESS, NULL); + /* invalid format, and skip the result */ + return; + } + + /* UUID not matching */ + if (!gatt_uuid_compare(record_value.dclr_value.char_uuid, p_clcb->uuid)) { + len -= (value_len + 2); + continue; /* skip the result, and look for next one */ + } else if (p_clcb->operation == GATTC_OPTYPE_READ) + /* UUID match for read characteristic value */ + { + /* only read the first matching UUID characteristic value, and + discard the rest results */ + p_clcb->s_handle = record_value.dclr_value.val_handle; + p_clcb->op_subtype |= 0x80; + gatt_act_read(p_clcb, 0); + return; + } + } + len -= (value_len + handle_len); + + /* result is (handle, 16bits UUID) pairs */ + memcpy (&result.value, &record_value, sizeof (result.value)); + + /* send callback if is discover procedure */ + if (p_clcb->operation == GATTC_OPTYPE_DISCOVERY && p_clcb->p_reg->app_cb.p_disc_res_cb) { + (*p_clcb->p_reg->app_cb.p_disc_res_cb)(p_clcb->conn_id, p_clcb->op_subtype, &result); + } + } + + p_clcb->s_handle = (handle == 0) ? 0 : (handle + 1); + + if (p_clcb->operation == GATTC_OPTYPE_DISCOVERY) { + /* initiate another request */ + gatt_act_discovery(p_clcb) ; + } else { /* read characteristic value */ + gatt_act_read(p_clcb, 0); + } +} + +/******************************************************************************* +** +** Function gatt_process_read_rsp +** +** Description This function is called to handle the read BLOB response +** +** +** Returns void +** +*******************************************************************************/ +void gatt_process_read_rsp(tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb, UINT8 op_code, + UINT16 len, UINT8 *p_data) +{ + UINT16 offset = p_clcb->counter; + UINT8 *p = p_data; + + UNUSED(op_code); + + if (p_clcb->operation == GATTC_OPTYPE_READ) { + if (p_clcb->op_subtype != GATT_READ_BY_HANDLE) { + p_clcb->counter = len; + gatt_end_operation(p_clcb, GATT_SUCCESS, (void *)p); + } else { + + /* allocate GKI buffer holding up long attribute value */ + if (!p_clcb->p_attr_buf) { + p_clcb->p_attr_buf = (UINT8 *)osi_malloc(GATT_MAX_ATTR_LEN); + } + + /* copy attrobute value into cb buffer */ + if (p_clcb->p_attr_buf && offset < GATT_MAX_ATTR_LEN) { + if ((len + offset) > GATT_MAX_ATTR_LEN) { + len = GATT_MAX_ATTR_LEN - offset; + } + + p_clcb->counter += len; + + memcpy(p_clcb->p_attr_buf + offset, p, len); + + /* send next request if needed */ + + if (len == (p_tcb->payload_size - 1) && /* full packet for read or read blob rsp */ + len + offset < GATT_MAX_ATTR_LEN) { + GATT_TRACE_DEBUG("full pkt issue read blob for remianing bytes old offset=%d len=%d new offset=%d", + offset, len, p_clcb->counter); + gatt_act_read(p_clcb, p_clcb->counter); + } else { /* end of request, send callback */ + gatt_end_operation(p_clcb, GATT_SUCCESS, (void *)p_clcb->p_attr_buf); + } + } else { /* exception, should not happen */ + GATT_TRACE_ERROR("attr offset = %d p_attr_buf = %p ", offset, p_clcb->p_attr_buf); + gatt_end_operation(p_clcb, GATT_NO_RESOURCES, (void *)p_clcb->p_attr_buf); + } + } + } else { + if (p_clcb->operation == GATTC_OPTYPE_DISCOVERY && + p_clcb->op_subtype == GATT_DISC_INC_SRVC && + p_clcb->read_uuid128.wait_for_read_rsp ) { + p_clcb->s_handle = p_clcb->read_uuid128.next_disc_start_hdl; + p_clcb->read_uuid128.wait_for_read_rsp = FALSE; + if (len == LEN_UUID_128) { + + memcpy(p_clcb->read_uuid128.result.value.incl_service.service_type.uu.uuid128, p, len); + p_clcb->read_uuid128.result.value.incl_service.service_type.len = LEN_UUID_128; + tGATT_INCL_SRVC *inc_srvc = &p_clcb->read_uuid128.result.value.incl_service; + GATT_DISC_INFO("DISC INC SRVC start handle %x, end handle %x, uuid %s", + inc_srvc->s_handle, inc_srvc->e_handle, gatt_uuid_to_str(&inc_srvc->service_type)); + if ( p_clcb->p_reg->app_cb.p_disc_res_cb) { + (*p_clcb->p_reg->app_cb.p_disc_res_cb)(p_clcb->conn_id, p_clcb->op_subtype, &p_clcb->read_uuid128.result); + } + gatt_act_discovery(p_clcb) ; + } else { + gatt_end_operation(p_clcb, GATT_INVALID_PDU, (void *)p); + } + } + } + +} + + +/******************************************************************************* +** +** Function gatt_process_handle_rsp +** +** Description This function is called to handle the write response +** +** +** Returns void +** +*******************************************************************************/ +void gatt_process_handle_rsp(tGATT_CLCB *p_clcb) +{ + gatt_end_operation(p_clcb, GATT_SUCCESS, NULL); +} +/******************************************************************************* +** +** Function gatt_process_mtu_rsp +** +** Description This function is called to process the configure MTU response. +** +** +** Returns void +** +*******************************************************************************/ +void gatt_process_mtu_rsp(tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb, UINT16 len, UINT8 *p_data) +{ + UINT16 mtu; + tGATT_STATUS status = GATT_SUCCESS; + + if (len < GATT_MTU_RSP_MIN_LEN) { + GATT_TRACE_ERROR("invalid MTU response PDU received, discard."); + status = GATT_INVALID_PDU; + } else { + STREAM_TO_UINT16(mtu, p_data); + + if (mtu < p_tcb->payload_size && mtu >= GATT_DEF_BLE_MTU_SIZE) { + p_tcb->payload_size = mtu; + } + } + /* host will set packet data length to 251 automatically if remote device support set packet data length, + so l2cble_set_fixed_channel_tx_data_length() is not necessary. + l2cble_set_fixed_channel_tx_data_length(p_tcb->peer_bda, L2CAP_ATT_CID, p_tcb->payload_size); + */ + gatt_end_operation(p_clcb, status, NULL); +} +/******************************************************************************* +** +** Function gatt_cmd_to_rsp_code +** +** Description The function convert a ATT command op code into the corresponding +** response code assume no error occurs. +** +** Returns response code. +** +*******************************************************************************/ +UINT8 gatt_cmd_to_rsp_code (UINT8 cmd_code) +{ + UINT8 rsp_code = 0; + + if (cmd_code > 1 && cmd_code != GATT_CMD_WRITE) { + rsp_code = cmd_code + 1; + } + return rsp_code; +} +/******************************************************************************* +** +** Function gatt_cl_send_next_cmd_inq +** +** Description Find next command in queue and sent to server +** +** Returns TRUE if command sent, otherwise FALSE. +** +*******************************************************************************/ +BOOLEAN gatt_cl_send_next_cmd_inq(tGATT_TCB *p_tcb) +{ + tGATT_CMD_Q *p_cmd = &p_tcb->cl_cmd_q[p_tcb->pending_cl_req]; + BOOLEAN sent = FALSE; + UINT8 rsp_code; + tGATT_CLCB *p_clcb = NULL; + tGATT_STATUS att_ret = GATT_SUCCESS; + + while (!sent && + p_tcb->pending_cl_req != p_tcb->next_slot_inq && + p_cmd->to_send && p_cmd->p_cmd != NULL) { + att_ret = attp_send_msg_to_l2cap(p_tcb, p_cmd->p_cmd); + + if (att_ret == GATT_SUCCESS || att_ret == GATT_CONGESTED) { + sent = TRUE; + p_cmd->to_send = FALSE; + if(p_cmd->p_cmd) { + osi_free(p_cmd->p_cmd); + p_cmd->p_cmd = NULL; + } + + /* dequeue the request if is write command or sign write */ + if (p_cmd->op_code != GATT_CMD_WRITE && p_cmd->op_code != GATT_SIGN_CMD_WRITE) { + gatt_start_rsp_timer (p_cmd->clcb_idx); + } else { + p_clcb = gatt_cmd_dequeue(p_tcb, &rsp_code); + + /* if no ack needed, keep sending */ + if (att_ret == GATT_SUCCESS) { + sent = FALSE; + } + + p_cmd = &p_tcb->cl_cmd_q[p_tcb->pending_cl_req]; + /* send command complete callback here */ + gatt_end_operation(p_clcb, att_ret, NULL); + } + } else { + GATT_TRACE_ERROR("gatt_cl_send_next_cmd_inq: L2CAP sent error"); + + memset(p_cmd, 0, sizeof(tGATT_CMD_Q)); + p_tcb->pending_cl_req ++; + p_cmd = &p_tcb->cl_cmd_q[p_tcb->pending_cl_req]; + } + + } + return sent; +} + +/******************************************************************************* +** +** Function gatt_client_handle_server_rsp +** +** Description This function is called to handle the server response to +** client. +** +** +** Returns void +** +*******************************************************************************/ +void gatt_client_handle_server_rsp (tGATT_TCB *p_tcb, UINT8 op_code, + UINT16 len, UINT8 *p_data) +{ + tGATT_CLCB *p_clcb = NULL; + UINT8 rsp_code; + + if (op_code != GATT_HANDLE_VALUE_IND && op_code != GATT_HANDLE_VALUE_NOTIF && + op_code != GATT_HANDLE_MULTI_VALUE_NOTIF) { + p_clcb = gatt_cmd_dequeue(p_tcb, &rsp_code); + + rsp_code = gatt_cmd_to_rsp_code(rsp_code); + + if (p_clcb == NULL || (rsp_code != op_code && op_code != GATT_RSP_ERROR)) { + GATT_TRACE_WARNING ("ATT - Ignore wrong response. Receives (%02x) \ + Request(%02x) Ignored", op_code, rsp_code); + + return; + } else { + btu_stop_timer (&p_clcb->rsp_timer_ent); + p_clcb->retry_count = 0; + } + } + /* the size of the message may not be bigger than the local max PDU size*/ + /* The message has to be smaller than the agreed MTU, len does not count op_code */ + if (len >= p_tcb->payload_size) { + GATT_TRACE_ERROR("invalid response/indicate pkt size: %d, PDU size: %d", len + 1, p_tcb->payload_size); + if (op_code != GATT_HANDLE_VALUE_NOTIF && op_code != GATT_HANDLE_VALUE_IND && + op_code != GATT_HANDLE_MULTI_VALUE_NOTIF) { + gatt_end_operation(p_clcb, GATT_ERROR, NULL); + } + } else { + switch (op_code) { + case GATT_RSP_ERROR: + gatt_process_error_rsp(p_tcb, p_clcb, op_code, len, p_data); + break; + + case GATT_RSP_MTU: /* 2 bytes mtu */ + gatt_process_mtu_rsp(p_tcb, p_clcb, len , p_data); + break; + + case GATT_RSP_FIND_INFO: + gatt_process_read_info_rsp(p_tcb, p_clcb, op_code, len, p_data); + break; + + case GATT_RSP_READ_BY_TYPE: + case GATT_RSP_READ_BY_GRP_TYPE: + gatt_process_read_by_type_rsp(p_tcb, p_clcb, op_code, len, p_data); + break; + + case GATT_RSP_READ: + case GATT_RSP_READ_BLOB: + case GATT_RSP_READ_MULTI: + case GATT_RSP_READ_MULTI_VAR: + gatt_process_read_rsp(p_tcb, p_clcb, op_code, len, p_data); + break; + + case GATT_RSP_FIND_TYPE_VALUE: /* disc service with UUID */ + gatt_process_find_type_value_rsp(p_tcb, p_clcb, len, p_data); + break; + + case GATT_RSP_WRITE: + gatt_process_handle_rsp(p_clcb); + break; + + case GATT_RSP_PREPARE_WRITE: + gatt_process_prep_write_rsp(p_tcb, p_clcb, op_code, len, p_data); + break; + + case GATT_RSP_EXEC_WRITE: + gatt_end_operation(p_clcb, p_clcb->status, NULL); + break; + + case GATT_HANDLE_VALUE_NOTIF: + case GATT_HANDLE_VALUE_IND: + case GATT_HANDLE_MULTI_VALUE_NOTIF: + gatt_process_notification(p_tcb, op_code, len, p_data); + break; + + default: + GATT_TRACE_ERROR("Unknown opcode = %d", op_code); + break; + } + } + + if (op_code != GATT_HANDLE_VALUE_IND && op_code != GATT_HANDLE_VALUE_NOTIF && + op_code != GATT_HANDLE_MULTI_VALUE_NOTIF) { + gatt_cl_send_next_cmd_inq(p_tcb); + } +} + +#endif /* BLE_INCLUDED == TRUE && GATTC_INCLUDED == TRUE */ diff --git a/lib/bt/host/bluedroid/stack/gatt/gatt_db.c b/lib/bt/host/bluedroid/stack/gatt/gatt_db.c new file mode 100644 index 00000000..e84580bc --- /dev/null +++ b/lib/bt/host/bluedroid/stack/gatt/gatt_db.c @@ -0,0 +1,1620 @@ +/****************************************************************************** + * + * Copyright (C) 2009-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * this file contains GATT database building and query functions + * + ******************************************************************************/ + +#include "common/bt_target.h" + +#if BLE_INCLUDED == TRUE && GATTS_INCLUDED == TRUE + +#include "common/bt_trace.h" +#include "osi/allocator.h" + +//#include +#include +#include "gatt_int.h" +#include "stack/l2c_api.h" +#include "btm_int.h" + +extern tGATT_STATUS gap_proc_read(tGATTS_REQ_TYPE type, tGATT_READ_REQ *p_data, tGATTS_RSP *p_rsp); +extern tGATT_STATUS gatt_proc_read(UINT16 conn_id, tGATTS_REQ_TYPE type, tGATT_READ_REQ *p_data, tGATTS_RSP *p_rsp); + +/******************************************************************************** +** L O C A L F U N C T I O N P R O T O T Y P E S * +*********************************************************************************/ +static BOOLEAN allocate_svc_db_buf(tGATT_SVC_DB *p_db); +static void *allocate_attr_in_db(tGATT_SVC_DB *p_db, tBT_UUID *p_uuid, tGATT_PERM perm); +static BOOLEAN deallocate_attr_in_db(tGATT_SVC_DB *p_db, void *p_attr); +static BOOLEAN copy_extra_byte_in_db(tGATT_SVC_DB *p_db, void **p_dst, UINT16 len); + +static BOOLEAN gatts_db_add_service_declaration(tGATT_SVC_DB *p_db, tBT_UUID *p_service, BOOLEAN is_pri); +static tGATT_STATUS gatts_send_app_read_request(tGATT_TCB *p_tcb, UINT8 op_code, + UINT16 handle, UINT16 offset, UINT32 trans_id, BOOLEAN need_rsp); +static BOOLEAN gatts_add_char_desc_value_check (tGATT_ATTR_VAL *attr_val, tGATTS_ATTR_CONTROL *control); + +/******************************************************************************* +** +** Function gatts_init_service_db +** +** Description This function initialize a memory space to be a service database. +** +** Parameter p_db: database pointer. +** len: size of the memory space. +** +** Returns Status of te operation. +** +*******************************************************************************/ +BOOLEAN gatts_init_service_db (tGATT_SVC_DB *p_db, tBT_UUID *p_service, BOOLEAN is_pri, + UINT16 s_hdl, UINT16 num_handle) +{ + if (p_db->svc_buffer == NULL) { //in case already alloc + p_db->svc_buffer = fixed_queue_new(QUEUE_SIZE_MAX); + } + + if (!allocate_svc_db_buf(p_db)) { + GATT_TRACE_ERROR("gatts_init_service_db failed, no resources\n"); + return FALSE; + } + + GATT_TRACE_DEBUG("gatts_init_service_db\n"); + GATT_TRACE_DEBUG("s_hdl = %d num_handle = %d\n", s_hdl, num_handle ); + + /* update service database information */ + p_db->next_handle = s_hdl; + p_db->end_handle = s_hdl + num_handle; + + return gatts_db_add_service_declaration(p_db, p_service, is_pri); +} + +/******************************************************************************* +** +** Function gatts_init_service_db +** +** Description This function initialize a memory space to be a service database. +** +** Parameter p_db: database pointer. +** len: size of the memory space. +** +** Returns Status of te operation. +** +*******************************************************************************/ +tBT_UUID *gatts_get_service_uuid (tGATT_SVC_DB *p_db) +{ + if (!p_db || !p_db->p_attr_list) { + GATT_TRACE_ERROR("service DB empty\n"); + + return NULL; + } else { + return &((tGATT_ATTR16 *)p_db->p_attr_list)->p_value->uuid; + } +} + +/******************************************************************************* +** +** Function gatts_check_attr_readability +** +** Description check attribute readability +** +** Returns status of operation. +** +*******************************************************************************/ +static tGATT_STATUS gatts_check_attr_readability(tGATT_ATTR16 *p_attr, + UINT16 offset, + BOOLEAN read_long, + tGATT_SEC_FLAG sec_flag, + UINT8 key_size) +{ + UINT16 min_key_size; + tGATT_PERM perm = p_attr->permission; + + UNUSED(offset); + min_key_size = (((perm & GATT_ENCRYPT_KEY_SIZE_MASK) >> 12)); + if (min_key_size != 0 ) { + min_key_size += 6; + } + + if (!(perm & GATT_READ_ALLOWED)) { + GATT_TRACE_ERROR( "GATT_READ_NOT_PERMIT\n"); + return GATT_READ_NOT_PERMIT; + } + + if ((perm & GATT_READ_AUTH_REQUIRED ) && !(sec_flag & GATT_SEC_FLAG_LKEY_UNAUTHED) && + !(sec_flag & BTM_SEC_FLAG_ENCRYPTED)) { + GATT_TRACE_ERROR( "GATT_INSUF_AUTHENTICATION\n"); + return GATT_INSUF_AUTHENTICATION; + } + + if ((perm & GATT_READ_MITM_REQUIRED ) && !(sec_flag & GATT_SEC_FLAG_LKEY_AUTHED)) { + GATT_TRACE_ERROR( "GATT_INSUF_AUTHENTICATION: MITM Required\n"); + return GATT_INSUF_AUTHENTICATION; + } + + if ((perm & GATT_READ_ENCRYPTED_REQUIRED ) && !(sec_flag & GATT_SEC_FLAG_ENCRYPTED)) { + GATT_TRACE_ERROR( "GATT_INSUF_ENCRYPTION\n"); + return GATT_INSUF_ENCRYPTION; + } + + if ( (perm & GATT_READ_ENCRYPTED_REQUIRED) && (sec_flag & GATT_SEC_FLAG_ENCRYPTED) && (key_size < min_key_size)) { + GATT_TRACE_ERROR( "GATT_INSUF_KEY_SIZE\n"); + return GATT_INSUF_KEY_SIZE; + } + /* LE Authorization check*/ + if ((perm & GATT_READ_AUTHORIZATION) && (!(sec_flag & GATT_SEC_FLAG_LKEY_AUTHED) || !(sec_flag & GATT_SEC_FLAG_AUTHORIZATION))) { + GATT_TRACE_ERROR( "GATT_INSUF_AUTHORIZATION\n"); + return GATT_INSUF_AUTHORIZATION; + } + + if (read_long) { + switch (p_attr->uuid) { + case GATT_UUID_PRI_SERVICE: + case GATT_UUID_SEC_SERVICE: + case GATT_UUID_CHAR_DECLARE: + case GATT_UUID_INCLUDE_SERVICE: + case GATT_UUID_CHAR_EXT_PROP: + case GATT_UUID_CHAR_CLIENT_CONFIG: + case GATT_UUID_CHAR_SRVR_CONFIG: + case GATT_UUID_CHAR_PRESENT_FORMAT: + GATT_TRACE_ERROR("GATT_NOT_LONG\n"); + return GATT_NOT_LONG; + + default: + break; + } + } + + return GATT_SUCCESS; +} + +/******************************************************************************* +** +** Function read_attr_value +** +** Description Utility function to read an attribute value. +** +** Parameter p_attr: pointer to the attribute to read. +** offset: read offset. +** p_value: output parameter to carry out the attribute value. +** p_len: output parameter to carry out the attribute length. +** read_long: this is a read blob request. +** mtu: MTU +** sec_flag: current link security status. +** key_size: encryption key size. +** +** Returns status of operation. +** +*******************************************************************************/ +static tGATT_STATUS read_attr_value (void *p_attr, + UINT16 offset, + UINT8 **p_data, + BOOLEAN read_long, + UINT16 mtu, + UINT16 *p_len, + tGATT_SEC_FLAG sec_flag, + UINT8 key_size) +{ + UINT16 len = 0, uuid16 = 0; + UINT8 *p = *p_data; + tGATT_STATUS status; + tGATT_ATTR16 *p_attr16 = (tGATT_ATTR16 *)p_attr; + + GATT_TRACE_DEBUG("read_attr_value uuid=0x%04x perm=0x%0x sec_flag=0x%x offset=%d read_long=%d\n", + p_attr16->uuid, + p_attr16->permission, + sec_flag, + offset, + read_long); + + status = gatts_check_attr_readability((tGATT_ATTR16 *)p_attr, offset, read_long, sec_flag, key_size); + + if (status != GATT_SUCCESS) { + return status; + } + + if (p_attr16->uuid_type == GATT_ATTR_UUID_TYPE_16) { + uuid16 = p_attr16->uuid; + } + + status = GATT_NO_RESOURCES; + + if (uuid16 == GATT_UUID_PRI_SERVICE || uuid16 == GATT_UUID_SEC_SERVICE) { + len = p_attr16->p_value->uuid.len; + if (mtu >= p_attr16->p_value->uuid.len) { + gatt_build_uuid_to_stream(&p, p_attr16->p_value->uuid); + status = GATT_SUCCESS; + } + } else if (uuid16 == GATT_UUID_CHAR_DECLARE) { + len = (((tGATT_ATTR16 *)(p_attr16->p_next))->uuid_type == GATT_ATTR_UUID_TYPE_16) ? 5 : 19; + + if (mtu >= len) { + UINT8_TO_STREAM(p, p_attr16->p_value->char_decl.property); + UINT16_TO_STREAM(p, p_attr16->p_value->char_decl.char_val_handle); + + if (((tGATT_ATTR16 *)(p_attr16->p_next))->uuid_type == GATT_ATTR_UUID_TYPE_16) { + UINT16_TO_STREAM(p, ((tGATT_ATTR16 *)(p_attr16->p_next))->uuid); + } + /* convert a 32bits UUID to 128 bits */ + else if (((tGATT_ATTR32 *)(p_attr16->p_next))->uuid_type == GATT_ATTR_UUID_TYPE_32) { + gatt_convert_uuid32_to_uuid128 (p, ((tGATT_ATTR32 *)(p_attr16->p_next))->uuid); + p += LEN_UUID_128; + } else { + ARRAY_TO_STREAM (p, ((tGATT_ATTR128 *)(p_attr16->p_next))->uuid, LEN_UUID_128); + } + status = GATT_SUCCESS; + } + + } else if (uuid16 == GATT_UUID_INCLUDE_SERVICE) { + if (p_attr16->p_value->incl_handle.service_type.len == LEN_UUID_16) { + len = 6; + } else { + len = 4; + } + + if (mtu >= len) { + UINT16_TO_STREAM(p, p_attr16->p_value->incl_handle.s_handle); + UINT16_TO_STREAM(p, p_attr16->p_value->incl_handle.e_handle); + + if (p_attr16->p_value->incl_handle.service_type.len == LEN_UUID_16) { + UINT16_TO_STREAM(p, p_attr16->p_value->incl_handle.service_type.uu.uuid16); + } + status = GATT_SUCCESS; + } + } else { /* characteristic description or characteristic value */ + if (p_attr16->control.auto_rsp == GATT_RSP_BY_STACK) { + if (p_attr16->p_value == NULL || p_attr16->p_value->attr_val.attr_val == NULL) { + status = GATT_UNKNOWN_ERROR; + } + else if (offset > p_attr16->p_value->attr_val.attr_len){ + /*if offset equal to max_len, should respond with zero byte value + //if offset is greater than max_len, should respond with an error*/ + status = GATT_INVALID_OFFSET; + } else { + UINT8 *value = (UINT8 *)(p_attr16->p_value->attr_val.attr_val) + offset; + UINT16 len_left = p_attr16->p_value->attr_val.attr_len - offset; + len = (mtu >= len_left) ? (len_left) : mtu; + ARRAY_TO_STREAM(p, value, len); + status = GATT_STACK_RSP; + } + + } else { + status = GATT_PENDING; + } + } + + *p_len = len; + *p_data = p; + return status; +} + +/******************************************************************************* +** +** Function gatts_db_read_attr_value_by_type +** +** Description Query attribute value by attribute type. +** +** Parameter p_db: pointer to the attribute database. +** p_rsp: Read By type response data. +** s_handle: starting handle of the range we are looking for. +** e_handle: ending handle of the range we are looking for. +** type: Attribute type. +** mtu: MTU. +** sec_flag: current link security status. +** key_size: encryption key size. +** +** Returns Status of the operation. +** +*******************************************************************************/ +tGATT_STATUS gatts_db_read_attr_value_by_type (tGATT_TCB *p_tcb, + tGATT_SVC_DB *p_db, + UINT8 op_code, + BT_HDR *p_rsp, + UINT16 s_handle, + UINT16 e_handle, + tBT_UUID type, + UINT16 *p_len, + tGATT_SEC_FLAG sec_flag, + UINT8 key_size, + UINT32 trans_id, + UINT16 *p_cur_handle) +{ + tGATT_STATUS status = GATT_NOT_FOUND; + tGATT_ATTR16 *p_attr; + UINT16 len = 0; + UINT8 *p = (UINT8 *)(p_rsp + 1) + p_rsp->len + L2CAP_MIN_OFFSET; + tBT_UUID attr_uuid; +#if (defined(BLE_DELAY_REQUEST_ENC) && (BLE_DELAY_REQUEST_ENC == TRUE)) + UINT8 flag; +#endif + BOOLEAN need_rsp; + BOOLEAN have_send_request = false; + + if (p_db && p_db->p_attr_list) { + p_attr = (tGATT_ATTR16 *)p_db->p_attr_list; + + while (p_attr && p_attr->handle <= e_handle) { + if (p_attr->uuid_type == GATT_ATTR_UUID_TYPE_16) { + attr_uuid.len = LEN_UUID_16; + attr_uuid.uu.uuid16 = p_attr->uuid; + } else if (p_attr->uuid_type == GATT_ATTR_UUID_TYPE_32) { + attr_uuid.len = LEN_UUID_32; + attr_uuid.uu.uuid32 = ((tGATT_ATTR32 *)p_attr)->uuid; + } else { + attr_uuid.len = LEN_UUID_128; + memcpy(attr_uuid.uu.uuid128, ((tGATT_ATTR128 *)p_attr)->uuid, LEN_UUID_128); + } + + if (p_attr->handle >= s_handle && gatt_uuid_compare(type, attr_uuid)) { + if (*p_len <= 2) { + status = GATT_NO_RESOURCES; + break; + } + + UINT16_TO_STREAM (p, p_attr->handle); + + status = read_attr_value ((void *)p_attr, 0, &p, FALSE, (UINT16)(*p_len - 2), &len, sec_flag, key_size); + if (status == GATT_PENDING) { + + + need_rsp = TRUE; + status = gatts_send_app_read_request(p_tcb, op_code, p_attr->handle, 0, trans_id, need_rsp); + + /* one callback at a time */ + break; + } else if (status == GATT_SUCCESS || status == GATT_STACK_RSP) { + if (status == GATT_STACK_RSP){ + need_rsp = FALSE; + status = gatts_send_app_read_request(p_tcb, op_code, p_attr->handle, 0, trans_id, need_rsp); + if(status == GATT_BUSY) + break; + + if (!have_send_request){ + have_send_request = true; + trans_id = p_tcb->sr_cmd.trans_id; + } + } + + if (p_rsp->offset == 0) { + p_rsp->offset = len + 2; + } + + if (p_rsp->offset == len + 2) { + p_rsp->len += (len + 2); + *p_len -= (len + 2); + } else { + GATT_TRACE_WARNING("format mismatch"); + status = GATT_NO_RESOURCES; + break; + } + } else { + *p_cur_handle = p_attr->handle; + break; + } + } + p_attr = (tGATT_ATTR16 *)p_attr->p_next; + } + } + +#if (defined(BLE_DELAY_REQUEST_ENC) && (BLE_DELAY_REQUEST_ENC == TRUE)) + if (BTM_GetSecurityFlags(p_tcb->peer_bda, &flag)) { + if ((p_tcb->att_lcid == L2CAP_ATT_CID) && (status == GATT_PENDING) && + (type.uu.uuid16 == GATT_UUID_GAP_DEVICE_NAME)) { + if ((flag & (BTM_SEC_LINK_KEY_KNOWN | BTM_SEC_FLAG_ENCRYPTED)) == + BTM_SEC_LINK_KEY_KNOWN) { + tACL_CONN *p; + p = btm_bda_to_acl(p_tcb->peer_bda, BT_TRANSPORT_LE); + if ((p != NULL) && (p->link_role == BTM_ROLE_MASTER)) { + tBTM_BLE_SEC_ACT sec_act = BTM_BLE_SEC_ENCRYPT; + btm_ble_set_encryption(p_tcb->peer_bda, &sec_act, p->link_role); + } + } + } + } +#endif + return status; +} + +/******************************************************************************* +** +** Function gatts_add_included_service +** +** Description This function adds an included service into a database. +** +** Parameter p_db: database pointer. +** inc_srvc_type: included service type. +** +** Returns Status of the operation. +** +*******************************************************************************/ +UINT16 gatts_add_included_service (tGATT_SVC_DB *p_db, UINT16 s_handle, UINT16 e_handle, + tBT_UUID service) +{ + tGATT_ATTR16 *p_attr; + tBT_UUID uuid = {LEN_UUID_16, {GATT_UUID_INCLUDE_SERVICE}}; + + GATT_TRACE_DEBUG("gatts_add_included_service: s_hdl = 0x%04x e_hdl = 0x%04x uuid = 0x%04x", + s_handle, e_handle, service.uu.uuid16); + + if (service.len == 0 || s_handle == 0 || e_handle == 0) { + GATT_TRACE_ERROR("gatts_add_included_service Illegal Params."); + return 0; + } + + BOOLEAN is_include_service_allowed = TRUE; + // service declaration + tGATT_ATTR16 *first_attr = (tGATT_ATTR16 *)p_db->p_attr_list; + if (p_db->p_attr_list != NULL) { + tGATT_ATTR16 *next_attr = (tGATT_ATTR16 *)first_attr->p_next; + /* This service already has other attributes */ + while (next_attr != NULL) { + if (!(next_attr->uuid_type == GATT_ATTR_UUID_TYPE_16 && next_attr->uuid == GATT_UUID_INCLUDE_SERVICE)) { + is_include_service_allowed = FALSE; + break; + } + next_attr = (tGATT_ATTR16 *)next_attr->p_next; + } + + } + if (!is_include_service_allowed) { + GATT_TRACE_ERROR("%s error, The include service should be added before adding the characteristics", __func__); + return 0; + } + + if ((p_attr = (tGATT_ATTR16 *) allocate_attr_in_db(p_db, &uuid, GATT_PERM_READ)) != NULL) { + if (copy_extra_byte_in_db(p_db, (void **)&p_attr->p_value, sizeof(tGATT_INCL_SRVC))) { + p_attr->p_value->incl_handle.s_handle = s_handle; + p_attr->p_value->incl_handle.e_handle = e_handle; + memcpy(&p_attr->p_value->incl_handle.service_type, &service, sizeof(tBT_UUID)); + + return p_attr->handle; + } else { + deallocate_attr_in_db(p_db, p_attr); + } + } + + return 0; +} + +/******************************************************************************* +** +** Function gatts_add_characteristic +** +** Description This function add a characteristics and its descriptor into +** a servce identified by the service database pointer. +** +** Parameter p_db: database pointer. +** perm: permission (authentication and key size requirements) +** property: property of the characteristic. +** p_char: characteristic value information. +** +** Returns Status of te operation. +** +*******************************************************************************/ +UINT16 gatts_add_characteristic (tGATT_SVC_DB *p_db, tGATT_PERM perm, + tGATT_CHAR_PROP property, + tBT_UUID *p_char_uuid, tGATT_ATTR_VAL *attr_val, tGATTS_ATTR_CONTROL *control) +{ + tGATT_ATTR16 *p_char_decl, *p_char_val; + tBT_UUID uuid = {LEN_UUID_16, {GATT_UUID_CHAR_DECLARE}}; + BOOLEAN status; + + GATT_TRACE_DEBUG("gatts_add_characteristic perm=0x%0x property=0x%0x\n", perm, property); + /* parameter validation check */ + status = gatts_add_char_desc_value_check(attr_val, control); + if (status == FALSE){ + return 0; + } + + + if ((p_char_decl = (tGATT_ATTR16 *)allocate_attr_in_db(p_db, &uuid, GATT_PERM_READ)) != NULL) { + if (!copy_extra_byte_in_db(p_db, (void **)&p_char_decl->p_value, sizeof(tGATT_CHAR_DECL))) { + deallocate_attr_in_db(p_db, p_char_decl); + return 0; + } + + p_char_val = (tGATT_ATTR16 *)allocate_attr_in_db(p_db, p_char_uuid, perm); + + if (p_char_val == NULL) { + deallocate_attr_in_db(p_db, p_char_decl); + return 0; + } + + p_char_decl->p_value->char_decl.property = property; + p_char_decl->p_value->char_decl.char_val_handle = p_char_val->handle; + if (control != NULL) { + p_char_val->control.auto_rsp = control->auto_rsp; + } else { + p_char_val->control.auto_rsp = GATT_RSP_DEFAULT; + } + + if (attr_val != NULL) { + if (!copy_extra_byte_in_db(p_db, (void **)&p_char_val->p_value, sizeof(tGATT_ATTR_VAL))) { + deallocate_attr_in_db(p_db, p_char_val); + return 0; + } + GATT_TRACE_DEBUG("attr_val->attr_len = %x, attr_val->attr_max_len = %x\n", attr_val->attr_len, attr_val->attr_max_len); + GATT_TRACE_DEBUG("attribute handle = %x\n", p_char_val->handle); + p_char_val->p_value->attr_val.attr_len = attr_val->attr_len; + p_char_val->p_value->attr_val.attr_max_len = attr_val->attr_max_len; + p_char_val->p_value->attr_val.attr_val = osi_malloc(attr_val->attr_max_len); + if (p_char_val->p_value->attr_val.attr_val == NULL) { + deallocate_attr_in_db(p_db, p_char_decl); + deallocate_attr_in_db(p_db, p_char_val); + GATT_TRACE_WARNING("Warning in %s, line=%d, insufficient resource to allocate for attribute value\n", __func__, __LINE__); + return 0; + } + else { + //add mask to indicate that p_value->attr_val.attr_val is dynamic allocated + p_char_val->mask |= GATT_ATTR_VALUE_ALLOCATED; + } + + //initiate characteristic attribute value part + memset(p_char_val->p_value->attr_val.attr_val, 0, attr_val->attr_max_len); + if (attr_val->attr_val != NULL) { + if (attr_val->attr_max_len < attr_val->attr_len){ + GATT_TRACE_ERROR("Error in %s, Line=%d, attribute actual length (%d) should not larger than max size (%d)\n", + __func__, __LINE__, attr_val->attr_len, attr_val->attr_max_len); + } + UINT16 actual_len = (attr_val->attr_max_len < attr_val->attr_len) ? (attr_val->attr_max_len) : (attr_val->attr_len); + memcpy(p_char_val->p_value->attr_val.attr_val, attr_val->attr_val, actual_len); + } + } + + return p_char_val->handle; + } + + return 0; +} + +/******************************************************************************* +** +** Function gatt_convertchar_descr_type +** +** Description This function convert a char descript UUID into descriptor type. +** +** Returns descriptor type. +** +*******************************************************************************/ +UINT8 gatt_convertchar_descr_type(tBT_UUID *p_descr_uuid) +{ + tBT_UUID std_descr = {LEN_UUID_16, {GATT_UUID_CHAR_EXT_PROP}}; + + if (gatt_uuid_compare(std_descr, * p_descr_uuid)) { + return GATT_DESCR_EXT_DSCPTOR; + } + + std_descr.uu.uuid16 ++; + if (gatt_uuid_compare(std_descr, * p_descr_uuid)) { + return GATT_DESCR_USER_DSCPTOR; + } + + std_descr.uu.uuid16 ++; + if (gatt_uuid_compare(std_descr, * p_descr_uuid)) { + return GATT_DESCR_CLT_CONFIG; + } + + std_descr.uu.uuid16 ++; + if (gatt_uuid_compare(std_descr, * p_descr_uuid)) { + return GATT_DESCR_SVR_CONFIG; + } + + std_descr.uu.uuid16 ++; + if (gatt_uuid_compare(std_descr, * p_descr_uuid)) { + return GATT_DESCR_PRES_FORMAT; + } + + std_descr.uu.uuid16 ++; + if (gatt_uuid_compare(std_descr, * p_descr_uuid)) { + return GATT_DESCR_AGGR_FORMAT; + } + + std_descr.uu.uuid16 ++; + if (gatt_uuid_compare(std_descr, * p_descr_uuid)) { + return GATT_DESCR_VALID_RANGE; + } + + + return GATT_DESCR_UNKNOWN; +} + +/******************************************************************************* +** +** Function gatts_add_char_descr +** +** Description This function add a characteristics descriptor. +** +** Parameter p_db: database pointer. +** perm: characteristic descriptor permission type. +** char_dscp_tpye: the characteristic descriptor masks. +** p_dscp_params: characteristic descriptors values. +** +** Returns Status of the operation. +** +*******************************************************************************/ +UINT16 gatts_add_char_descr (tGATT_SVC_DB *p_db, tGATT_PERM perm, + tBT_UUID *p_descr_uuid, tGATT_ATTR_VAL *attr_val, tGATTS_ATTR_CONTROL *control) +{ + tGATT_ATTR16 *p_char_dscptr; + BOOLEAN status; + + GATT_TRACE_DEBUG("gatts_add_char_descr uuid=0x%04x\n", p_descr_uuid->uu.uuid16); + + /* parameter validation check */ + status = gatts_add_char_desc_value_check(attr_val, control); + if (status == FALSE){ + return 0; + } + + /* Add characteristic descriptors */ + if ((p_char_dscptr = (tGATT_ATTR16 *)allocate_attr_in_db(p_db, p_descr_uuid, perm)) == NULL) { + deallocate_attr_in_db(p_db, p_char_dscptr); + GATT_TRACE_DEBUG("gatts_add_char_descr Fail for adding char descriptors."); + return 0; + } else { + p_char_dscptr->control.auto_rsp = (control == NULL) ? GATT_RSP_DEFAULT : (control->auto_rsp); + if (attr_val != NULL) { + if (!copy_extra_byte_in_db(p_db, (void **)&p_char_dscptr->p_value, sizeof(tGATT_ATTR_VAL))) { + deallocate_attr_in_db(p_db, p_char_dscptr); + return 0; + } + p_char_dscptr->p_value->attr_val.attr_len = attr_val->attr_len; + p_char_dscptr->p_value->attr_val.attr_max_len = attr_val->attr_max_len; + if (attr_val->attr_max_len != 0) { + p_char_dscptr->p_value->attr_val.attr_val = osi_malloc(attr_val->attr_max_len); + if (p_char_dscptr->p_value->attr_val.attr_val == NULL) { + deallocate_attr_in_db(p_db, p_char_dscptr); + GATT_TRACE_WARNING("Warning in %s, line=%d, insufficient resource to allocate for descriptor value\n", __func__, __LINE__); + return 0; + } + else { + //add mask to indicate that p_value->attr_val.attr_val is dynamic allocated + p_char_dscptr->mask |= GATT_ATTR_VALUE_ALLOCATED; + } + + //initiate characteristic attribute value part + memset(p_char_dscptr->p_value->attr_val.attr_val, 0, attr_val->attr_max_len); + if(attr_val->attr_val != NULL) { + memcpy(p_char_dscptr->p_value->attr_val.attr_val, attr_val->attr_val, attr_val->attr_len); + } + } + } + return p_char_dscptr->handle; + } +} + + +/******************************************************************************* +** +** Function gatts_set_attribute_value +** +** Description This function add the attribute value in the database +** +** Parameter p_db: database pointer. +** attr_handle: the attribute handle +** length: the attribute value length +** value: the pointer to the data to be set to the attribute value in the database +** +** Returns Status of the operation. +** +*******************************************************************************/ +tGATT_STATUS gatts_set_attribute_value(tGATT_SVC_DB *p_db, UINT16 attr_handle, + UINT16 length, UINT8 *value) +{ + tGATT_ATTR16 *p_cur; + + if (p_db == NULL) { + GATT_TRACE_DEBUG("gatts_set_attribute_value Fail:p_db is NULL.\n"); + return GATT_INVALID_PDU; + } + if (p_db->p_attr_list == NULL) { + GATT_TRACE_DEBUG("gatts_set_attribute_value Fail:p_db->p_attr_list is NULL.\n"); + return GATT_INVALID_PDU; + } + if ((length > 0) && (value == NULL)){ + GATT_TRACE_ERROR("Error in %s, line=%d, value should not be NULL here\n",__func__, __LINE__); + return GATT_INVALID_PDU; + } + + p_cur = (tGATT_ATTR16 *) p_db->p_attr_list; + + while (p_cur != NULL) { + if (p_cur->handle == attr_handle) { + /* for characteristic should not be set, return GATT_NOT_FOUND */ + if (p_cur->uuid_type == GATT_ATTR_UUID_TYPE_16) { + switch (p_cur->uuid) { + case GATT_UUID_PRI_SERVICE: + case GATT_UUID_SEC_SERVICE: + case GATT_UUID_CHAR_DECLARE: + return GATT_NOT_FOUND; + break; + } + } + + /* in other cases, value can be set*/ + if ((p_cur->p_value == NULL) || (p_cur->p_value->attr_val.attr_val == NULL) \ + || (p_cur->p_value->attr_val.attr_max_len == 0)){ + GATT_TRACE_ERROR("Error in %s, line=%d, attribute value should not be NULL here\n", __func__, __LINE__); + return GATT_NOT_FOUND; + } else if (p_cur->p_value->attr_val.attr_max_len < length) { + GATT_TRACE_ERROR("gatts_set_attribute_value failed:Invalid value length"); + return GATT_INVALID_ATTR_LEN; + } else{ + memcpy(p_cur->p_value->attr_val.attr_val, value, length); + p_cur->p_value->attr_val.attr_len = length; + } + break; + } + p_cur = p_cur->p_next; + } + + return GATT_SUCCESS; +} + +/******************************************************************************* +** +** Function gatts_get_attr_value_internal +** +** Description This function get the attribute value in gap service and gatt service +** +** Parameter attr_handle: the attribute handle +** length: the attribute value length +** value: the pointer to the data to be get to the attribute value in the database +** +** Returns Status of the operation. +** +*******************************************************************************/ +tGATT_STATUS gatts_get_attr_value_internal(UINT16 attr_handle, UINT16 *length, UINT8 **value) +{ + UINT8 i; + tGATT_READ_REQ read_req; + tGATT_STATUS status = GATT_NOT_FOUND; + tGATT_SR_REG *p_rcb = gatt_cb.sr_reg; + UINT8 service_uuid[LEN_UUID_128] = {0}; + + if (length == NULL){ + GATT_TRACE_ERROR("gatts_get_attr_value_internal Fail:length is NULL.\n"); + return GATT_INVALID_PDU; + } + + if (value == NULL){ + GATT_TRACE_ERROR("gatts_get_attr_value_internal Fail:value is NULL.\n"); + *length = 0; + return GATT_INVALID_PDU; + } + + // find the service by handle + for (i = 0; i < GATT_MAX_SR_PROFILES; i++, p_rcb++) { + if (p_rcb->in_use && p_rcb->s_hdl <= attr_handle && p_rcb->e_hdl >= attr_handle) { + break; + } + } + + // service cb not found + if (i == GATT_MAX_SR_PROFILES) { + return status; + } + + if (p_rcb->app_uuid.len != LEN_UUID_128) { + return status; + } + + memset(&read_req, 0, sizeof(tGATT_READ_REQ)); + read_req.handle = attr_handle; + + // read gatt service attribute value + memset(service_uuid, 0x81, LEN_UUID_128); + if (!memcmp(p_rcb->app_uuid.uu.uuid128, service_uuid, LEN_UUID_128)) { + status = gatt_proc_read(0, GATTS_REQ_TYPE_READ, &read_req, &gatt_cb.rsp); + } + + // read gap service attribute value + memset(service_uuid, 0x82, LEN_UUID_128); + if (!memcmp(p_rcb->app_uuid.uu.uuid128, service_uuid, LEN_UUID_128)) { + status = gap_proc_read(GATTS_REQ_TYPE_READ, &read_req, &gatt_cb.rsp); + } + + if (status == GATT_SUCCESS) { + *length = gatt_cb.rsp.attr_value.len; + *value = gatt_cb.rsp.attr_value.value; + } + + return status; +} + +/******************************************************************************* +** +** Function gatts_get_attribute_value +** +** Description This function get the attribute value in the database +** +** Parameter p_db: database pointer. +** attr_handle: the attribute handle +** length: the attribute value length +** value: the pointer to the data to be get to the attribute value in the database +** +** Returns Status of the operation. +** +*******************************************************************************/ +tGATT_STATUS gatts_get_attribute_value(tGATT_SVC_DB *p_db, UINT16 attr_handle, + UINT16 *length, UINT8 **value) +{ + tGATT_ATTR16 *p_cur; + + GATT_TRACE_DEBUG("attr_handle = %x\n", attr_handle); + + if (p_db == NULL) { + GATT_TRACE_ERROR("gatts_get_attribute_value Fail:p_db is NULL.\n"); + *length = 0; + return GATT_INVALID_PDU; + } + if (p_db->p_attr_list == NULL) { + GATT_TRACE_ERROR("gatts_get_attribute_value Fail:p_db->p_attr_list is NULL.\n"); + *length = 0; + return GATT_INVALID_PDU; + } + if (length == NULL){ + GATT_TRACE_ERROR("gatts_get_attribute_value Fail:length is NULL.\n"); + return GATT_INVALID_PDU; + } + if (value == NULL){ + GATT_TRACE_ERROR("gatts_get_attribute_value Fail:value is NULL.\n"); + *length = 0; + return GATT_INVALID_PDU; + } + + p_cur = (tGATT_ATTR16 *) p_db->p_attr_list; + + while (p_cur != NULL) { + if (p_cur->handle == attr_handle) { + + if (p_cur->uuid_type == GATT_ATTR_UUID_TYPE_16) { + switch (p_cur->uuid) { + case GATT_UUID_CHAR_DECLARE: + case GATT_UUID_INCLUDE_SERVICE: + break; + default: + if (p_cur->p_value && p_cur->p_value->attr_val.attr_len != 0) { + *length = p_cur->p_value->attr_val.attr_len; + *value = p_cur->p_value->attr_val.attr_val; + return GATT_SUCCESS; + } else { + *length = 0; + return GATT_SUCCESS; + } + break; + } + } else { + if (p_cur->p_value && p_cur->p_value->attr_val.attr_len != 0) { + *length = p_cur->p_value->attr_val.attr_len; + *value = p_cur->p_value->attr_val.attr_val; + return GATT_SUCCESS; + } else { + *length = 0; + return GATT_SUCCESS; + } + + } + + break; + + } + + p_cur = p_cur->p_next; + } + + return GATT_NOT_FOUND; +} + +BOOLEAN gatts_is_auto_response(UINT16 attr_handle) +{ + tGATT_HDL_LIST_ELEM *p_decl = NULL; + BOOLEAN rsp = FALSE; + tGATT_SVC_DB *p_db = NULL; + if ((p_decl = gatt_find_hdl_buffer_by_attr_handle(attr_handle)) == NULL) { + GATT_TRACE_DEBUG("Service not created\n"); + return rsp; + } + + p_db = &p_decl->svc_db; + + tGATT_ATTR16 *p_cur, *p_next; + + if (p_db == NULL) { + GATT_TRACE_DEBUG("gatts_get_attribute_value Fail:p_db is NULL.\n"); + return rsp; + } + if (p_db->p_attr_list == NULL) { + GATT_TRACE_DEBUG("gatts_get_attribute_value Fail:p_db->p_attr_list is NULL.\n"); + return rsp; + } + + p_cur = (tGATT_ATTR16 *) p_db->p_attr_list; + p_next = (tGATT_ATTR16 *) p_cur->p_next; + + for (; p_cur != NULL && p_next != NULL; + p_cur = p_next, p_next = (tGATT_ATTR16 *)p_next->p_next) { + if (p_cur->handle == attr_handle) { + if (p_cur->p_value != NULL && p_cur->control.auto_rsp == GATT_RSP_BY_STACK) { + rsp = true; + return rsp; + } + + } + + } + + return rsp; + +} + +/*******************************************************************************/ +/* Service Attribute Database Query Utility Functions */ +/*******************************************************************************/ +/******************************************************************************* +** +** Function gatts_read_attr_value_by_handle +** +** Description Query attribute value by attribute handle. +** +** Parameter p_db: pointer to the attribute database. +** handle: Attribute handle to read. +** offset: Read offset. +** p_value: output parameter to carry out the attribute value. +** p_len: output parameter as attribute length read. +** read_long: this is a read blob request. +** mtu: MTU. +** sec_flag: current link security status. +** key_size: encryption key size +** +** Returns Status of operation. +** +*******************************************************************************/ +tGATT_STATUS gatts_read_attr_value_by_handle(tGATT_TCB *p_tcb, + tGATT_SVC_DB *p_db, + UINT8 op_code, + UINT16 handle, UINT16 offset, + UINT8 *p_value, UINT16 *p_len, + UINT16 mtu, + tGATT_SEC_FLAG sec_flag, + UINT8 key_size, + UINT32 trans_id) +{ + tGATT_STATUS status = GATT_NOT_FOUND; + tGATT_ATTR16 *p_attr; + UINT8 *pp = p_value; + + if (p_db && p_db->p_attr_list) { + p_attr = (tGATT_ATTR16 *)p_db->p_attr_list; + + while (p_attr && handle >= p_attr->handle) { + if (p_attr->handle == handle) { + status = read_attr_value (p_attr, offset, &pp, + (BOOLEAN)(op_code == GATT_REQ_READ_BLOB), + mtu, p_len, sec_flag, key_size); + + if ((status == GATT_PENDING) || (status == GATT_STACK_RSP)) { + BOOLEAN need_rsp = (status != GATT_STACK_RSP); + status = gatts_send_app_read_request(p_tcb, op_code, p_attr->handle, offset, trans_id, need_rsp); + } + break; + } + p_attr = (tGATT_ATTR16 *)p_attr->p_next; + } + } + + + return status; +} + +tGATT_STATUS gatts_write_attr_value_by_handle(tGATT_SVC_DB *p_db, + UINT16 handle, UINT16 offset, + UINT8 *p_value, UINT16 len) +{ + tGATT_STATUS status = GATT_NOT_FOUND; + tGATT_ATTR16 *p_attr; + + if (p_db && p_db->p_attr_list) { + p_attr = (tGATT_ATTR16 *)p_db->p_attr_list; + + while (p_attr && handle >= p_attr->handle) { + if (p_attr->handle == handle ) { + if (p_attr->control.auto_rsp == GATT_RSP_BY_APP) { + return GATT_APP_RSP; + } + + if ((p_attr->p_value != NULL) && + (p_attr->p_value->attr_val.attr_max_len >= offset + len) && + p_attr->p_value->attr_val.attr_val != NULL) { + memcpy(p_attr->p_value->attr_val.attr_val + offset, p_value, len); + p_attr->p_value->attr_val.attr_len = len + offset; + return GATT_SUCCESS; + } else if (p_attr->p_value && p_attr->p_value->attr_val.attr_max_len < offset + len){ + GATT_TRACE_DEBUG("Remote device try to write with a length larger then attribute's max length\n"); + return GATT_INVALID_ATTR_LEN; + } else if ((p_attr->p_value == NULL) || (p_attr->p_value->attr_val.attr_val == NULL)){ + GATT_TRACE_ERROR("Error in %s, line=%d, %s should not be NULL here\n", __func__, __LINE__, \ + (p_attr->p_value == NULL) ? "p_value" : "attr_val.attr_val"); + return GATT_UNKNOWN_ERROR; + } + } + + p_attr = (tGATT_ATTR16 *)p_attr->p_next; + + } + } + + return status; +} + +/******************************************************************************* +** +** Function gatts_read_attr_perm_check +** +** Description Check attribute readability. +** +** Parameter p_db: pointer to the attribute database. +** handle: Attribute handle to read. +** offset: Read offset. +** p_value: output parameter to carry out the attribute value. +** p_len: output parameter as attribute length read. +** read_long: this is a read blob request. +** mtu: MTU. +** sec_flag: current link security status. +** key_size: encryption key size +** +** Returns Status of operation. +** +*******************************************************************************/ +tGATT_STATUS gatts_read_attr_perm_check(tGATT_SVC_DB *p_db, + BOOLEAN is_long, + UINT16 handle, + tGATT_SEC_FLAG sec_flag, + UINT8 key_size) +{ + tGATT_STATUS status = GATT_NOT_FOUND; + tGATT_ATTR16 *p_attr; + + if (p_db && p_db->p_attr_list) { + p_attr = (tGATT_ATTR16 *)p_db->p_attr_list; + + while (p_attr && handle >= p_attr->handle) { + if (p_attr->handle == handle) { + status = gatts_check_attr_readability (p_attr, 0, + is_long, + sec_flag, key_size); + break; + } + p_attr = (tGATT_ATTR16 *) p_attr->p_next; + } + } + + return status; +} + + +/******************************************************************************* +** +** Function gatts_write_attr_perm_check +** +** Description Write attribute value into database. +** +** Parameter p_db: pointer to the attribute database. +** op_code:op code of this write. +** handle: handle of the attribute to write. +** offset: Write offset if write op code is write blob. +** p_data: Attribute value to write. +** len: attribute data length. +** sec_flag: current link security status. +** key_size: encryption key size +** +** Returns Status of the operation. +** +*******************************************************************************/ +tGATT_STATUS gatts_write_attr_perm_check (tGATT_SVC_DB *p_db, UINT8 op_code, + UINT16 handle, UINT16 offset, UINT8 *p_data, + UINT16 len, tGATT_SEC_FLAG sec_flag, UINT8 key_size) +{ + tGATT_STATUS status = GATT_NOT_FOUND; + tGATT_ATTR16 *p_attr; + UINT16 max_size = 0; + tGATT_PERM perm; + UINT16 min_key_size; + + GATT_TRACE_DEBUG( "gatts_write_attr_perm_check op_code=0x%0x handle=0x%04x offset=%d len=%d sec_flag=0x%0x key_size=%d", + op_code, handle, offset, len, sec_flag, key_size); + + if (p_db != NULL) { + p_attr = (tGATT_ATTR16 *) p_db->p_attr_list; + + while (p_attr != NULL) { + if (p_attr->handle == handle) { + perm = p_attr->permission; + min_key_size = (((perm & GATT_ENCRYPT_KEY_SIZE_MASK) >> 12)); + if (min_key_size != 0 ) { + min_key_size += 6; + } + GATT_TRACE_DEBUG( "gatts_write_attr_perm_check p_attr->permission =0x%04x min_key_size==0x%04x", + p_attr->permission, + min_key_size); + + if ((op_code == GATT_CMD_WRITE || op_code == GATT_REQ_WRITE) + && (perm & GATT_WRITE_SIGNED_PERM)) { + /* use the rules for the mixed security see section 10.2.3*/ + /* use security mode 1 level 2 when the following condition follows */ + /* LE security mode 2 level 1 and LE security mode 1 level 2 */ + if ((perm & GATT_PERM_WRITE_SIGNED) && (perm & GATT_PERM_WRITE_ENCRYPTED)) { + perm = GATT_PERM_WRITE_ENCRYPTED; + } + /* use security mode 1 level 3 when the following condition follows */ + /* LE security mode 2 level 2 and security mode 1 and LE */ + else if (((perm & GATT_PERM_WRITE_SIGNED_MITM) && (perm & GATT_PERM_WRITE_ENCRYPTED)) || + /* LE security mode 2 and security mode 1 level 3 */ + ((perm & GATT_WRITE_SIGNED_PERM) && (perm & GATT_PERM_WRITE_ENC_MITM))) { + perm = GATT_PERM_WRITE_ENC_MITM; + } + } + + if ((op_code == GATT_SIGN_CMD_WRITE) && !(perm & GATT_WRITE_SIGNED_PERM)) { + status = GATT_WRITE_NOT_PERMIT; + GATT_TRACE_DEBUG( "gatts_write_attr_perm_check - sign cmd write not allowed,handle:0x%04x",handle); + } + if ((op_code == GATT_SIGN_CMD_WRITE) && (sec_flag & GATT_SEC_FLAG_ENCRYPTED)) { + status = GATT_INVALID_PDU; + GATT_TRACE_ERROR( "gatts_write_attr_perm_check - Error!! sign cmd write sent on a encypted link,handle:0x%04x",handle); + } else if (!(perm & GATT_WRITE_ALLOWED)) { + status = GATT_WRITE_NOT_PERMIT; + GATT_TRACE_ERROR( "gatts_write_attr_perm_check - GATT_WRITE_NOT_PERMIT,handle:0x%04x",handle); + } + /* require authentication, but not been authenticated */ + else if ((perm & GATT_WRITE_AUTH_REQUIRED ) && !(sec_flag & GATT_SEC_FLAG_LKEY_UNAUTHED)) { + status = GATT_INSUF_AUTHENTICATION; + GATT_TRACE_ERROR( "gatts_write_attr_perm_check - GATT_INSUF_AUTHENTICATION,handle:0x%04x",handle); + } else if ((perm & GATT_WRITE_MITM_REQUIRED ) && !(sec_flag & GATT_SEC_FLAG_LKEY_AUTHED)) { + status = GATT_INSUF_AUTHENTICATION; + GATT_TRACE_ERROR( "gatts_write_attr_perm_check - GATT_INSUF_AUTHENTICATION: MITM required,handle:0x%04x",handle); + } else if ((perm & GATT_WRITE_ENCRYPTED_PERM ) && !(sec_flag & GATT_SEC_FLAG_ENCRYPTED)) { + status = GATT_INSUF_ENCRYPTION; + GATT_TRACE_ERROR( "gatts_write_attr_perm_check - GATT_INSUF_ENCRYPTION,handle:0x%04x",handle); + } else if ((perm & GATT_WRITE_ENCRYPTED_PERM ) && (sec_flag & GATT_SEC_FLAG_ENCRYPTED) && (key_size < min_key_size)) { + status = GATT_INSUF_KEY_SIZE; + GATT_TRACE_ERROR( "gatts_write_attr_perm_check - GATT_INSUF_KEY_SIZE,handle:0x%04x",handle); + } + /* LE Authorization check*/ + else if ((perm & GATT_WRITE_AUTHORIZATION) && (!(sec_flag & GATT_SEC_FLAG_LKEY_AUTHED) || !(sec_flag & GATT_SEC_FLAG_AUTHORIZATION))){ + status = GATT_INSUF_AUTHORIZATION; + GATT_TRACE_ERROR( "gatts_write_attr_perm_check - GATT_INSUF_AUTHORIZATION,handle:0x%04x",handle); + } + /* LE security mode 2 attribute */ + else if (perm & GATT_WRITE_SIGNED_PERM && op_code != GATT_SIGN_CMD_WRITE && !(sec_flag & GATT_SEC_FLAG_ENCRYPTED) + && (perm & GATT_WRITE_ALLOWED) == 0) { + status = GATT_INSUF_AUTHENTICATION; + GATT_TRACE_ERROR( "gatts_write_attr_perm_check - GATT_INSUF_AUTHENTICATION: LE security mode 2 required,handle:0x%04x",handle); + } else { /* writable: must be char value declaration or char descritpors */ + if (p_attr->uuid_type == GATT_ATTR_UUID_TYPE_16) { + switch (p_attr->uuid) { + case GATT_UUID_CHAR_PRESENT_FORMAT:/* should be readable only */ + case GATT_UUID_CHAR_EXT_PROP:/* should be readable only */ + case GATT_UUID_CHAR_AGG_FORMAT: /* should be readable only */ + case GATT_UUID_CHAR_VALID_RANGE: + status = GATT_WRITE_NOT_PERMIT; + break; + + case GATT_UUID_CHAR_CLIENT_CONFIG: + /* coverity[MISSING_BREAK] */ + /* intnended fall through, ignored */ + /* fall through */ + case GATT_UUID_CHAR_SRVR_CONFIG: + max_size = 2; + case GATT_UUID_CHAR_DESCRIPTION: + default: /* any other must be character value declaration */ + status = GATT_SUCCESS; + break; + } + } else if (p_attr->uuid_type == GATT_ATTR_UUID_TYPE_128 || + p_attr->uuid_type == GATT_ATTR_UUID_TYPE_32) { + status = GATT_SUCCESS; + } else { + status = GATT_INVALID_PDU; + } + + if (p_data == NULL && len > 0) { + status = GATT_INVALID_PDU; + } + /* these attribute does not allow write blob */ +// btla-specific ++ + else if ( (p_attr->uuid_type == GATT_ATTR_UUID_TYPE_16) && + (p_attr->uuid == GATT_UUID_CHAR_CLIENT_CONFIG || + p_attr->uuid == GATT_UUID_CHAR_SRVR_CONFIG || + p_attr->uuid == GATT_UUID_CLIENT_SUP_FEAT || + p_attr->uuid == GATT_UUID_GAP_ICON + ) ) +// btla-specific -- + { + if (op_code == GATT_REQ_PREPARE_WRITE) { /* does not allow write blob */ + status = GATT_REQ_NOT_SUPPORTED; + GATT_TRACE_ERROR( "gatts_write_attr_perm_check - GATT_REQ_NOT_SUPPORTED,handle:0x%04x",handle); + } else if (len != max_size) { /* data does not match the required format */ + status = GATT_INVALID_ATTR_LEN; + GATT_TRACE_ERROR( "gatts_write_attr_perm_check - GATT_INVALID_ATTR_LEN,handle:0x%04x",handle); + } else { + status = GATT_SUCCESS; + } + } + } + break; + } else { + p_attr = (tGATT_ATTR16 *)p_attr->p_next; + } + } + } + + return status; +} + +/******************************************************************************* +** +** Function allocate_attr_in_db +** +** Description Allocate a memory space for a new attribute, and link this +** attribute into the database attribute list. +** +** +** Parameter p_db : database pointer. +** p_uuid: pointer to attribute UUID +** service : type of attribute to be added. +** +** Returns pointer to the newly allocated attribute. +** +*******************************************************************************/ +static void *allocate_attr_in_db(tGATT_SVC_DB *p_db, tBT_UUID *p_uuid, tGATT_PERM perm) +{ + tGATT_ATTR16 *p_attr16 = NULL, *p_last; + tGATT_ATTR32 *p_attr32 = NULL; + tGATT_ATTR128 *p_attr128 = NULL; + UINT16 len = sizeof(tGATT_ATTR128); + + if (p_uuid == NULL) { + GATT_TRACE_ERROR("illegal UUID\n"); + return NULL; + } + + if (p_uuid->len == LEN_UUID_16) { + len = sizeof(tGATT_ATTR16); + } else if (p_uuid->len == LEN_UUID_32) { + len = sizeof(tGATT_ATTR32); + } + + GATT_TRACE_DEBUG("allocate attr %d bytes\n", len); + + if (p_db->end_handle <= p_db->next_handle) { + GATT_TRACE_DEBUG("handle space full. handle_max = %d next_handle = %d\n", + p_db->end_handle, p_db->next_handle); + return NULL; + } + + if (p_db->mem_free < len) { + if (!allocate_svc_db_buf(p_db)) { + GATT_TRACE_ERROR("allocate_attr_in_db failed, no resources\n"); + return NULL; + } + } + memset(p_db->p_free_mem, 0, len); + p_attr16 = (tGATT_ATTR16 *) p_db->p_free_mem; + + if (p_uuid->len == LEN_UUID_16 && p_uuid->uu.uuid16 != GATT_ILLEGAL_UUID) { + p_attr16->uuid_type = GATT_ATTR_UUID_TYPE_16; + p_attr16->uuid = p_uuid->uu.uuid16; + } else if (p_uuid->len == LEN_UUID_32) { + p_attr32 = (tGATT_ATTR32 *) p_db->p_free_mem; + p_attr32->uuid_type = GATT_ATTR_UUID_TYPE_32; + p_attr32->uuid = p_uuid->uu.uuid32; + } else if (p_uuid->len == LEN_UUID_128) { + p_attr128 = (tGATT_ATTR128 *) p_db->p_free_mem; + p_attr128->uuid_type = GATT_ATTR_UUID_TYPE_128; + memcpy(p_attr128->uuid, p_uuid->uu.uuid128, LEN_UUID_128); + } + + p_db->p_free_mem += len; + p_db->mem_free -= len; + + p_attr16->handle = p_db->next_handle++; + p_attr16->permission = perm; + p_attr16->p_next = NULL; + + /* link the attribute record into the end of DB */ + if (p_db->p_attr_list == NULL) { + p_db->p_attr_list = p_attr16; + } else { + p_last = (tGATT_ATTR16 *)p_db->p_attr_list; + + while (p_last != NULL && p_last->p_next != NULL) { + p_last = (tGATT_ATTR16 *)p_last->p_next; + } + + p_last->p_next = p_attr16; + } + + if (p_attr16->uuid_type == GATT_ATTR_UUID_TYPE_16) { + GATT_TRACE_DEBUG("=====> handle = [0x%04x] uuid16 = [0x%04x] perm=0x%02x\n", + p_attr16->handle, p_attr16->uuid, p_attr16->permission); + } else if (p_attr16->uuid_type == GATT_ATTR_UUID_TYPE_32) { + GATT_TRACE_DEBUG("=====> handle = [0x%04x] uuid32 = [0x%08x] perm=0x%02x\n", + p_attr32->handle, p_attr32->uuid, p_attr32->permission); + } else { + GATT_TRACE_DEBUG("=====> handle = [0x%04x] uuid128 = [0x%02x:0x%02x] perm=0x%02x\n", + p_attr128->handle, p_attr128->uuid[0], p_attr128->uuid[1], + p_attr128->permission); + } + return (void *)p_attr16; +} + + + +/******************************************************************************* +** +** Function deallocate_attr_in_db +** +** Description Free an attribute within the database. +** +** Parameter p_db: database pointer. +** p_attr: pointer to the attribute record to be freed. +** +** Returns BOOLEAN: success +** +*******************************************************************************/ +static BOOLEAN deallocate_attr_in_db(tGATT_SVC_DB *p_db, void *p_attr) +{ + tGATT_ATTR16 *p_cur, *p_next; + BOOLEAN found = FALSE; + + if (p_db->p_attr_list == NULL) { + return found; + } + + p_cur = (tGATT_ATTR16 *) p_db->p_attr_list; + p_next = (tGATT_ATTR16 *) p_cur->p_next; + + for (; p_cur != NULL && p_next != NULL; + p_cur = p_next, p_next = (tGATT_ATTR16 *)p_next->p_next) { + if (p_next == p_attr) { + p_cur->p_next = p_next->p_next; + found = TRUE; + } + } + if (p_cur == p_attr && p_cur == p_db->p_attr_list) { + p_db->p_attr_list = p_cur->p_next; + found = TRUE; + } + /* else attr not found */ + if ( found) { + p_db->next_handle --; + } + + return found; +} + +/******************************************************************************* +** +** Function copy_extra_byte_in_db +** +** Description Utility function to allocate extra bytes memory in DB and copy +** the value from a source place. +** +** +** Parameter p_db: database pointer. +** p_dst: destination data pointer. +** p_src: source data pointer. +** len: data length to be copied. +** +** Returns None. +** +*******************************************************************************/ +static BOOLEAN copy_extra_byte_in_db(tGATT_SVC_DB *p_db, void **p_dst, UINT16 len) +{ + UINT8 *p = (UINT8 *)*p_dst; + + if (p_db->mem_free < len) { + if (!allocate_svc_db_buf(p_db)) { + GATT_TRACE_ERROR("copy_extra_byte_in_db failed, no resources\n"); + return FALSE; + } + } + + p = p_db->p_free_mem; + p_db->p_free_mem += len; + p_db->mem_free -= len; + memset((void *)p, 0, len); + *p_dst = (void *)p; + + return TRUE; +} + +/******************************************************************************* +** +** Function allocate_svc_db_buf +** +** Description Utility function to allocate extra buffer for service database. +** +** Returns TRUE if allocation succeed, otherwise FALSE. +** +*******************************************************************************/ +static BOOLEAN allocate_svc_db_buf(tGATT_SVC_DB *p_db) +{ + BT_HDR *p_buf; + + GATT_TRACE_DEBUG("allocate_svc_db_buf allocating extra buffer"); + + if ((p_buf = (BT_HDR *)osi_calloc(GATT_DB_BUF_SIZE)) == NULL) { + GATT_TRACE_ERROR("allocate_svc_db_buf failed, no resources"); + return FALSE; + } + + p_db->p_free_mem = (UINT8 *) p_buf; + p_db->mem_free = GATT_DB_BUF_SIZE; + + fixed_queue_enqueue(p_db->svc_buffer, p_buf, FIXED_QUEUE_MAX_TIMEOUT); + + return TRUE; + +} + +/******************************************************************************* +** +** Function gatts_send_app_read_request +** +** Description Send application read request callback +** +** Returns status of operation. +** +*******************************************************************************/ +static tGATT_STATUS gatts_send_app_read_request(tGATT_TCB *p_tcb, UINT8 op_code, + UINT16 handle, UINT16 offset, UINT32 trans_id, BOOLEAN need_rsp) +{ + tGATTS_DATA sr_data; + UINT8 i_rcb; + tGATT_SR_REG *p_sreg; + UINT16 conn_id; + + i_rcb = gatt_sr_find_i_rcb_by_handle(handle); + if (i_rcb == GATT_MAX_SR_PROFILES) { + GATT_TRACE_ERROR("Failed to find i_rcb,Error in %s, line=%d, \n", __func__, __LINE__); + return (tGATT_STATUS) GATT_ERROR; + } + p_sreg = &gatt_cb.sr_reg[i_rcb]; + conn_id = GATT_CREATE_CONN_ID(p_tcb->tcb_idx, p_sreg->gatt_if); + + if (trans_id == 0) { + trans_id = gatt_sr_enqueue_cmd(p_tcb, op_code, handle); + gatt_sr_update_cback_cnt(p_tcb, p_sreg->gatt_if, TRUE, TRUE); + } + + if (trans_id != 0 ) { + memset(&sr_data, 0, sizeof(tGATTS_DATA)); + + sr_data.read_req.handle = handle; + sr_data.read_req.is_long = (BOOLEAN)(op_code == GATT_REQ_READ_BLOB); + sr_data.read_req.offset = offset; + sr_data.read_req.need_rsp = need_rsp; + + gatt_sr_send_req_callback(conn_id, + trans_id, GATTS_REQ_TYPE_READ, &sr_data); + + if (need_rsp) { + return (tGATT_STATUS) GATT_PENDING; + } + else{ + return (tGATT_STATUS) GATT_STACK_RSP; + } + } else { + return (tGATT_STATUS) GATT_BUSY; /* max pending command, application error */ + } + +} + +/******************************************************************************* +** +** Function gatts_db_add_service_declaration +** +** Description Update a service database service declaration record. +** +** Parameter p_db: database pointer. +** service: UUID of the service. +** +** Returns void +** +*******************************************************************************/ +static BOOLEAN gatts_db_add_service_declaration(tGATT_SVC_DB *p_db, tBT_UUID *p_service, BOOLEAN is_pri) +{ + tGATT_ATTR16 *p_attr; + tBT_UUID uuid = {LEN_UUID_16, {0}}; + BOOLEAN rt = FALSE; + + GATT_TRACE_DEBUG( "add_service_declaration"); + + if (is_pri) { + uuid.uu.uuid16 = GATT_UUID_PRI_SERVICE; + } else { + uuid.uu.uuid16 = GATT_UUID_SEC_SERVICE; + } + + /* add service declration record */ + if ((p_attr = (tGATT_ATTR16 *)(allocate_attr_in_db(p_db, &uuid, GATT_PERM_READ))) != NULL) { + if (copy_extra_byte_in_db (p_db, (void **)&p_attr->p_value, sizeof(tBT_UUID))) { + if (p_service->len == LEN_UUID_16) { + p_attr->p_value->uuid.len = LEN_UUID_16; + p_attr->p_value->uuid.uu.uuid16 = p_service->uu.uuid16; + } else if (p_service->len == LEN_UUID_32) { + p_attr->p_value->uuid.len = LEN_UUID_128; + gatt_convert_uuid32_to_uuid128(p_attr->p_value->uuid.uu.uuid128, p_service->uu.uuid32); + } else { + p_attr->p_value->uuid.len = LEN_UUID_128; + memcpy(p_attr->p_value->uuid.uu.uuid128, p_service->uu.uuid128, LEN_UUID_128); + } + rt = TRUE; + } + + } + return rt; +} + +/******************************************************************************* +** +** Function gatts_add_char_desc_value_check +** +** Description parameters validation check for gatts add char/descriptor functions +** +** Parameter attr_val: attribute value for char/descriptor. +** control: control variable for char/descriptor. +** +** Returns void +** +*******************************************************************************/ +static BOOLEAN gatts_add_char_desc_value_check (tGATT_ATTR_VAL *attr_val, tGATTS_ATTR_CONTROL *control) +{ + if ((control != NULL) && ((control->auto_rsp != GATT_RSP_BY_APP) && (control->auto_rsp != GATT_RSP_BY_STACK))){ + GATT_TRACE_ERROR("Error in %s, line=%d, control->auto_rsp should be set to GATT_RSP_BY_APP or GATT_RSP_BY_STACK here\n",\ + __func__, __LINE__); + return FALSE; + } + + if ((control != NULL) && (control->auto_rsp == GATT_RSP_BY_STACK)){ + if (attr_val == NULL){ + GATT_TRACE_ERROR("Error in %s, line=%d, for stack respond attribute, attr_val should not be NULL here\n",\ + __func__, __LINE__); + return FALSE; + } else if (attr_val->attr_max_len == 0){ + GATT_TRACE_ERROR("Error in %s, line=%d, for stack respond attribute, attribute max length should not be 0\n",\ + __func__, __LINE__); + return FALSE; + } + } + + if (attr_val != NULL){ + if (attr_val->attr_len > attr_val->attr_max_len){ + GATT_TRACE_ERROR("Error in %s, line=%d,attribute actual length should not be larger than max length\n",\ + __func__, __LINE__); + return FALSE; + } + } + + return TRUE ; +} + +#endif /* BLE_INCLUDED == TRUE && GATTS_INCLUDED == TRUE */ diff --git a/lib/bt/host/bluedroid/stack/gatt/gatt_main.c b/lib/bt/host/bluedroid/stack/gatt/gatt_main.c new file mode 100644 index 00000000..d76aefe9 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/gatt/gatt_main.c @@ -0,0 +1,1250 @@ +/****************************************************************************** + * + * Copyright (C) 2008-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * this file contains the main ATT functions + * + ******************************************************************************/ + +#include "common/bt_target.h" + +#if BLE_INCLUDED == TRUE + +#include "gatt_int.h" +#include "stack/l2c_api.h" +#include "btm_int.h" +#include "btm_ble_int.h" +#include "osi/allocator.h" + +/* Configuration flags. */ +#define GATT_L2C_CFG_IND_DONE (1<<0) +#define GATT_L2C_CFG_CFM_DONE (1<<1) + +/* minimum GATT MTU size over BR/EDR link +*/ +#define GATT_MIN_BR_MTU_SIZE 48 + +/********************************************************************************/ +/* L O C A L F U N C T I O N P R O T O T Y P E S */ +/********************************************************************************/ +static void gatt_le_connect_cback (UINT16 chan, BD_ADDR bd_addr, BOOLEAN connected, + UINT16 reason, tBT_TRANSPORT transport); +static void gatt_le_data_ind (UINT16 chan, BD_ADDR bd_addr, BT_HDR *p_buf); +static void gatt_le_cong_cback(BD_ADDR remote_bda, BOOLEAN congest); +#if (CLASSIC_BT_GATT_INCLUDED == TRUE) +static void gatt_l2cif_connect_ind_cback (BD_ADDR bd_addr, UINT16 l2cap_cid, + UINT16 psm, UINT8 l2cap_id); +static void gatt_l2cif_connect_cfm_cback (UINT16 l2cap_cid, UINT16 result); +static void gatt_l2cif_config_ind_cback (UINT16 l2cap_cid, tL2CAP_CFG_INFO *p_cfg); +static void gatt_l2cif_config_cfm_cback (UINT16 l2cap_cid, tL2CAP_CFG_INFO *p_cfg); +static void gatt_l2cif_disconnect_ind_cback (UINT16 l2cap_cid, BOOLEAN ack_needed); +static void gatt_l2cif_disconnect_cfm_cback (UINT16 l2cap_cid, UINT16 result); +static void gatt_l2cif_data_ind_cback (UINT16 l2cap_cid, BT_HDR *p_msg); +#endif ///CLASSIC_BT_GATT_INCLUDED == TRUE +static void gatt_send_conn_cback (tGATT_TCB *p_tcb); +#if (CLASSIC_BT_GATT_INCLUDED == TRUE) +static void gatt_l2cif_congest_cback (UINT16 cid, BOOLEAN congested); +static const tL2CAP_APPL_INFO dyn_info = { + gatt_l2cif_connect_ind_cback, + gatt_l2cif_connect_cfm_cback, + NULL, + gatt_l2cif_config_ind_cback, + gatt_l2cif_config_cfm_cback, + gatt_l2cif_disconnect_ind_cback, + gatt_l2cif_disconnect_cfm_cback, + NULL, + gatt_l2cif_data_ind_cback, + gatt_l2cif_congest_cback, + NULL +} ; +#endif ///CLASSIC_BT_GATT_INCLUDED == TRUE + +#if GATT_DYNAMIC_MEMORY == FALSE +tGATT_CB gatt_cb; +#else +tGATT_CB *gatt_cb_ptr; +#endif + +tGATT_DEFAULT gatt_default; + +/******************************************************************************* +** +** Function gatt_init +** +** Description This function is enable the GATT profile on the device. +** It clears out the control blocks, and registers with L2CAP. +** +** Returns void +** +*******************************************************************************/ +void gatt_init (void) +{ + tL2CAP_FIXED_CHNL_REG fixed_reg; + +#if GATT_DYNAMIC_MEMORY + gatt_cb_ptr = (tGATT_CB *)osi_malloc(sizeof(tGATT_CB)); +#endif /* #if GATT_DYNAMIC_MEMORY */ + memset (&gatt_cb, 0, sizeof(tGATT_CB)); + memset (&fixed_reg, 0, sizeof(tL2CAP_FIXED_CHNL_REG)); + + gatt_cb.auto_disc = TRUE; + gatt_cb.p_clcb_list = list_new(osi_free_func); + gatt_cb.p_tcb_list = list_new(osi_free_func); +#if defined(GATT_INITIAL_TRACE_LEVEL) + gatt_cb.trace_level = GATT_INITIAL_TRACE_LEVEL; +#else + gatt_cb.trace_level = BT_TRACE_LEVEL_NONE; /* No traces */ +#endif + gatt_cb.def_mtu_size = GATT_DEF_BLE_MTU_SIZE; + gatt_cb.sign_op_queue = fixed_queue_new(QUEUE_SIZE_MAX); + gatt_cb.srv_chg_clt_q = fixed_queue_new(QUEUE_SIZE_MAX); + gatt_cb.pending_new_srv_start_q = fixed_queue_new(QUEUE_SIZE_MAX); + gatt_cb.srv_chg_mode = GATTS_SEND_SERVICE_CHANGE_MODE; + + /* First, register fixed L2CAP channel for ATT over BLE */ + fixed_reg.fixed_chnl_opts.mode = L2CAP_FCR_BASIC_MODE; + fixed_reg.fixed_chnl_opts.max_transmit = 0xFF; + fixed_reg.fixed_chnl_opts.rtrans_tout = 2000; + fixed_reg.fixed_chnl_opts.mon_tout = 12000; + fixed_reg.fixed_chnl_opts.mps = 670; + fixed_reg.fixed_chnl_opts.tx_win_sz = 1; + + fixed_reg.pL2CA_FixedConn_Cb = gatt_le_connect_cback; + fixed_reg.pL2CA_FixedData_Cb = gatt_le_data_ind; + fixed_reg.pL2CA_FixedCong_Cb = gatt_le_cong_cback; /* congestion callback */ + fixed_reg.default_idle_tout = 0xffff; /* 0xffff default idle timeout */ + + L2CA_RegisterFixedChannel (L2CAP_ATT_CID, &fixed_reg); + +#if (CLASSIC_BT_GATT_INCLUDED == TRUE) + /* Now, register with L2CAP for ATT PSM over BR/EDR */ + if (!L2CA_Register (BT_PSM_ATT, (tL2CAP_APPL_INFO *) &dyn_info)) { + GATT_TRACE_ERROR ("ATT Dynamic Registration failed"); + } +#endif ///CLASSIC_BT_GATT_INCLUDED == TRUE + BTM_SetSecurityLevel(TRUE, "", BTM_SEC_SERVICE_ATT, BTM_SEC_NONE, BT_PSM_ATT, 0, 0); + BTM_SetSecurityLevel(FALSE, "", BTM_SEC_SERVICE_ATT, BTM_SEC_NONE, BT_PSM_ATT, 0, 0); + + gatt_cb.hdl_cfg.gatt_start_hdl = GATT_GATT_START_HANDLE; + gatt_cb.hdl_cfg.gap_start_hdl = GATT_GAP_START_HANDLE; + gatt_cb.hdl_cfg.app_start_hdl = GATT_APP_START_HANDLE; +#if (GATTS_INCLUDED == TRUE) + gatt_profile_db_init(); +#endif ///GATTS_INCLUDED == TRUE + //init local MTU size + gatt_default.local_mtu = GATT_MAX_MTU_SIZE; +} + + +/******************************************************************************* +** +** Function gatt_free +** +** Description This function frees resources used by the GATT profile. +** +** Returns void +** +*******************************************************************************/ +#if (GATT_INCLUDED == TRUE) +void gatt_free(void) +{ + GATT_TRACE_DEBUG("gatt_free()"); + fixed_queue_free(gatt_cb.sign_op_queue, NULL); + gatt_cb.sign_op_queue = NULL; + fixed_queue_free(gatt_cb.srv_chg_clt_q, NULL); + gatt_cb.srv_chg_clt_q = NULL; + fixed_queue_free(gatt_cb.pending_new_srv_start_q, NULL); + gatt_cb.pending_new_srv_start_q = NULL; + + list_node_t *p_node = NULL; + tGATT_TCB *p_tcb = NULL; + for(p_node = list_begin(gatt_cb.p_tcb_list); p_node; p_node = list_next(p_node)) { + p_tcb = list_node(p_node); + fixed_queue_free(p_tcb->pending_enc_clcb, NULL); + p_tcb->pending_enc_clcb = NULL; + + fixed_queue_free(p_tcb->pending_ind_q, NULL); + p_tcb->pending_ind_q = NULL; + + btu_free_timer(&p_tcb->conf_timer_ent); + memset(&p_tcb->conf_timer_ent, 0, sizeof(TIMER_LIST_ENT)); + + btu_free_timer(&p_tcb->ind_ack_timer_ent); + memset(&p_tcb->ind_ack_timer_ent, 0, sizeof(TIMER_LIST_ENT)); + +#if (GATTS_INCLUDED == TRUE) + fixed_queue_free(p_tcb->sr_cmd.multi_rsp_q, NULL); + p_tcb->sr_cmd.multi_rsp_q = NULL; +#endif /* #if (GATTS_INCLUDED == TRUE) */ + } + list_free(gatt_cb.p_tcb_list); + list_free(gatt_cb.p_clcb_list); + +#if (GATTS_INCLUDED == TRUE) + for (int i = 0; i < GATT_MAX_SR_PROFILES; i++) { + gatt_remove_an_item_from_list(&gatt_cb.hdl_list_info, &gatt_cb.hdl_list[i]); + gatt_free_attr_value_buffer(&gatt_cb.hdl_list[i]); + gatt_free_hdl_buffer(&gatt_cb.hdl_list[i]); + } +#endif /* #if (GATTS_INCLUDED == TRUE) */ +#if GATT_DYNAMIC_MEMORY + FREE_AND_RESET(gatt_cb_ptr); +#endif /* #if GATT_DYNAMIC_MEMORY */ +} +#endif ///GATTS_INCLUDED == TRUE + +/******************************************************************************* +** +** Function gatt_connect +** +** Description This function is called to initiate a connection to a peer device. +** +** Parameter rem_bda: remote device address to connect to. +** bd_addr_type: emote device address type. +** Returns TRUE if connection is started, otherwise return FALSE. +** +*******************************************************************************/ +BOOLEAN gatt_connect (BD_ADDR rem_bda, tBLE_ADDR_TYPE bd_addr_type, tGATT_TCB *p_tcb, tBT_TRANSPORT transport, BOOLEAN is_aux) +{ + BOOLEAN gatt_ret = FALSE; + + if (gatt_get_ch_state(p_tcb) != GATT_CH_OPEN) { + gatt_set_ch_state(p_tcb, GATT_CH_CONN); + } + + if (transport == BT_TRANSPORT_LE) { + p_tcb->att_lcid = L2CAP_ATT_CID; + gatt_ret = L2CA_ConnectFixedChnl (L2CAP_ATT_CID, rem_bda, bd_addr_type, is_aux); +#if (CLASSIC_BT_GATT_INCLUDED == TRUE) + } else { + if ((p_tcb->att_lcid = L2CA_ConnectReq(BT_PSM_ATT, rem_bda)) != 0) { + gatt_ret = TRUE; + } +#endif ///CLASSIC_BT_GATT_INCLUDED == TRUE + + } + + return gatt_ret; +} + +/******************************************************************************* +** +** Function gatt_disconnect +** +** Description This function is called to disconnect to an ATT device. +** +** Parameter p_tcb: pointer to the TCB to disconnect. +** +** Returns TRUE: if connection found and to be disconnected; otherwise +** return FALSE. +** +*******************************************************************************/ +BOOLEAN gatt_disconnect (tGATT_TCB *p_tcb) +{ + BOOLEAN ret = FALSE; + tGATT_CH_STATE ch_state; + GATT_TRACE_DEBUG ("gatt_disconnect "); + + if (p_tcb != NULL) { + ret = TRUE; + if ( (ch_state = gatt_get_ch_state(p_tcb)) != GATT_CH_CLOSING ) { + if (p_tcb->att_lcid == L2CAP_ATT_CID) { + if (ch_state == GATT_CH_OPEN) { + /* only LCB exist between remote device and local */ + ret = L2CA_RemoveFixedChnl (L2CAP_ATT_CID, p_tcb->peer_bda); + } else { + gatt_set_ch_state(p_tcb, GATT_CH_CLOSING); + ret = L2CA_CancelBleConnectReq (p_tcb->peer_bda); + } +#if (CLASSIC_BT_GATT_INCLUDED == TRUE) + } else { + ret = L2CA_DisconnectReq(p_tcb->att_lcid); +#endif ///CLASSIC_BT_GATT_INCLUDED == TRUE + } + } else { + GATT_TRACE_DEBUG ("gatt_disconnect already in closing state"); + } + } + + return ret; +} + +/******************************************************************************* +** +** Function gatt_update_app_hold_link_status +** +** Description Update the application use link status +** +** Returns void. +** +*******************************************************************************/ +void gatt_update_app_hold_link_status (tGATT_IF gatt_if, tGATT_TCB *p_tcb, BOOLEAN is_add) +{ + UINT8 i; + BOOLEAN found = FALSE; + + if (p_tcb == NULL) { + GATT_TRACE_ERROR("gatt_update_app_hold_link_status p_tcb=NULL"); + return; + } + + + for (i = 0; i < GATT_MAX_APPS; i++) { + if (p_tcb->app_hold_link[i] == gatt_if) { + found = TRUE; + if (!is_add) { + p_tcb->app_hold_link[i] = 0; + break; + } + } + } + + if (!found && is_add) { + for (i = 0; i < GATT_MAX_APPS; i++) { + if (p_tcb->app_hold_link[i] == 0) { + p_tcb->app_hold_link[i] = gatt_if; + found = TRUE; + break; + } + } + } + + GATT_TRACE_DEBUG("gatt_update_app_hold_link_status found=%d[1-found] idx=%d gatt_if=%d is_add=%d", found, i, gatt_if, is_add); + +} + +/******************************************************************************* +** +** Function gatt_update_app_use_link_flag +** +** Description Update the application use link flag and optional to check the acl link +** if the link is up then set the idle time out accordingly +** +** Returns void. +** +*******************************************************************************/ +void gatt_update_app_use_link_flag (tGATT_IF gatt_if, tGATT_TCB *p_tcb, BOOLEAN is_add, BOOLEAN check_acl_link) +{ + GATT_TRACE_DEBUG("gatt_update_app_use_link_flag is_add=%d chk_link=%d", + is_add, check_acl_link); + + gatt_update_app_hold_link_status(gatt_if, p_tcb, is_add); + + if (check_acl_link && + p_tcb && + p_tcb->att_lcid == L2CAP_ATT_CID && /* only update link idle timer for fixed channel */ + (BTM_GetHCIConnHandle(p_tcb->peer_bda, p_tcb->transport) != GATT_INVALID_ACL_HANDLE)) { + if (is_add) { + GATT_TRACE_DEBUG("GATT disables link idle timer"); + /* acl link is connected disable the idle timeout */ + GATT_SetIdleTimeout(p_tcb->peer_bda, GATT_LINK_NO_IDLE_TIMEOUT, p_tcb->transport); + } else { + if (!gatt_num_apps_hold_link(p_tcb)) { + /* acl link is connected but no application needs to use the link + so set the timeout value to GATT_LINK_IDLE_TIMEOUT_WHEN_NO_APP seconds */ + GATT_TRACE_DEBUG("GATT starts link idle timer =%d sec", GATT_LINK_IDLE_TIMEOUT_WHEN_NO_APP); + GATT_SetIdleTimeout(p_tcb->peer_bda, GATT_LINK_IDLE_TIMEOUT_WHEN_NO_APP, p_tcb->transport); + } + + } + } +} + +/******************************************************************************* +** +** Function gatt_act_connect +** +** Description GATT connection initiation. +** +** Returns void. +** +*******************************************************************************/ +BOOLEAN gatt_act_connect (tGATT_REG *p_reg, BD_ADDR bd_addr, + tBLE_ADDR_TYPE bd_addr_type, tBT_TRANSPORT transport, BOOLEAN is_aux) +{ + BOOLEAN ret = FALSE; + tGATT_TCB *p_tcb; + UINT8 st; + + if ((p_tcb = gatt_find_tcb_by_addr(bd_addr, transport)) != NULL) { + ret = TRUE; + st = gatt_get_ch_state(p_tcb); + + /* before link down, another app try to open a GATT connection */ + if (st == GATT_CH_OPEN && gatt_num_apps_hold_link(p_tcb) == 0 && + transport == BT_TRANSPORT_LE ) { + if (!gatt_connect(bd_addr, bd_addr_type, p_tcb, transport, is_aux)) { + ret = FALSE; + } + } else if (st == GATT_CH_CLOSING) { + /* need to complete the closing first */ + ret = FALSE; + } else { + GATT_TRACE_WARNING("gatt_connect wrong state %d", st); + } + } else { + if ((p_tcb = gatt_allocate_tcb_by_bdaddr(bd_addr, transport)) != NULL) { + if (!gatt_connect(bd_addr, bd_addr_type, p_tcb, transport, is_aux)) { + GATT_TRACE_ERROR("gatt_connect failed"); + + // code enter here if create connection failed. if disconnect after connection, code will not enter here + + // p_tcb, p_tcb->pending_enc_clcb, and p_tcb->pending_ind_q have been freed in gatt_cleanup_upon_disc(), + // but here p_tcb is get from gatt_allocate_tcb_by_bdaddr(), is too old, so we get p_tcb again + p_tcb = gatt_find_tcb_by_addr(bd_addr, transport); + if(p_tcb != NULL) { + if(p_tcb->pending_enc_clcb != NULL) { + fixed_queue_free(p_tcb->pending_enc_clcb, NULL); + } + if(p_tcb->pending_ind_q != NULL) { + fixed_queue_free(p_tcb->pending_ind_q, NULL); + } + gatt_tcb_free(p_tcb); + } + + } else { + ret = TRUE; + } + } else { + ret = 0; + GATT_TRACE_ERROR("Max TCB for gatt_if [%d] reached.", p_reg->gatt_if); + } + } + + if (ret) { + gatt_update_app_use_link_flag(p_reg->gatt_if, p_tcb, TRUE, FALSE); + } + + return ret; +} + +/******************************************************************************* +** +** Function gatt_le_connect_cback +** +** Description This callback function is called by L2CAP to indicate that +** the ATT fixed channel for LE is +** connected (conn = TRUE)/disconnected (conn = FALSE). +** +*******************************************************************************/ +static void gatt_le_connect_cback (UINT16 chan, BD_ADDR bd_addr, BOOLEAN connected, + UINT16 reason, tBT_TRANSPORT transport) +{ + + tGATT_TCB *p_tcb = gatt_find_tcb_by_addr(bd_addr, transport); + BOOLEAN check_srv_chg = FALSE; + tGATTS_SRV_CHG *p_srv_chg_clt = NULL; + + /* ignore all fixed channel connect/disconnect on BR/EDR link for GATT */ + if (transport == BT_TRANSPORT_BR_EDR) { + return; + } + + GATT_TRACE_DEBUG ("GATT ATT protocol channel with BDA: %08x%04x is %s", + (bd_addr[0] << 24) + (bd_addr[1] << 16) + (bd_addr[2] << 8) + bd_addr[3], + (bd_addr[4] << 8) + bd_addr[5], (connected) ? "connected" : "disconnected"); + + if ((p_srv_chg_clt = gatt_is_bda_in_the_srv_chg_clt_list(bd_addr)) != NULL) { + check_srv_chg = TRUE; + } else { + if (btm_sec_is_a_bonded_dev(bd_addr)) { + gatt_add_a_bonded_dev_for_srv_chg(bd_addr); + } + } + + if (connected) { + /* do we have a channel initiating a connection? */ + if (p_tcb) { + /* we are initiating connection */ + if ( gatt_get_ch_state(p_tcb) == GATT_CH_CONN) { + /* send callback */ + gatt_set_ch_state(p_tcb, GATT_CH_OPEN); + p_tcb->payload_size = GATT_DEF_BLE_MTU_SIZE; + + gatt_send_conn_cback(p_tcb); + } + if (check_srv_chg) { +#if (GATTS_INCLUDED == TRUE) + gatt_chk_srv_chg (p_srv_chg_clt); +#endif ///GATTS_INCLUDED == TRUE + } + } + /* this is incoming connection or background connection callback */ + + else { + if ((p_tcb = gatt_allocate_tcb_by_bdaddr(bd_addr, BT_TRANSPORT_LE)) != NULL) { + p_tcb->att_lcid = L2CAP_ATT_CID; + + gatt_set_ch_state(p_tcb, GATT_CH_OPEN); + + p_tcb->payload_size = GATT_DEF_BLE_MTU_SIZE; + + gatt_send_conn_cback (p_tcb); + if (check_srv_chg) { +#if (GATTS_INCLUDED == TRUE) + gatt_chk_srv_chg (p_srv_chg_clt); +#endif ///GATTS_INCLUDED == TRUE + } + } else { + GATT_TRACE_ERROR("CCB max out, no rsources"); + } + } + } else { + gatt_cleanup_upon_disc(bd_addr, reason, transport); + GATT_TRACE_DEBUG ("ATT disconnected"); + } +} + +/******************************************************************************* +** +** Function gatt_channel_congestion +** +** Description This function is called to process the congestion callback +** from lcb +** +** Returns void +** +*******************************************************************************/ +static void gatt_channel_congestion(tGATT_TCB *p_tcb, BOOLEAN congested) +{ + UINT8 i = 0; + tGATT_REG *p_reg = NULL; + UINT16 conn_id; +#if (GATTC_INCLUDED == TRUE) + /* if uncongested, check to see if there is any more pending data */ + if (p_tcb != NULL && congested == FALSE) { + gatt_cl_send_next_cmd_inq(p_tcb); + } +#endif ///GATTC_INCLUDED == TRUE + /* notifying all applications for the connection up event */ + for (i = 0, p_reg = gatt_cb.cl_rcb ; i < GATT_MAX_APPS; i++, p_reg++) { + if (p_reg->in_use) { + if (p_reg->app_cb.p_congestion_cb) { + conn_id = GATT_CREATE_CONN_ID(p_tcb->tcb_idx, p_reg->gatt_if); + (*p_reg->app_cb.p_congestion_cb)(conn_id, congested); + } + } + } +} + +/******************************************************************************* +** +** Function gatt_le_cong_cback +** +** Description This function is called when GATT fixed channel is congested +** or uncongested. +** +** Returns void +** +*******************************************************************************/ +static void gatt_le_cong_cback(BD_ADDR remote_bda, BOOLEAN congested) +{ + tGATT_TCB *p_tcb = gatt_find_tcb_by_addr(remote_bda, BT_TRANSPORT_LE); + + /* if uncongested, check to see if there is any more pending data */ + if (p_tcb != NULL) { + gatt_channel_congestion(p_tcb, congested); + } +} + +/******************************************************************************* +** +** Function gatt_le_data_ind +** +** Description This function is called when data is received from L2CAP. +** if we are the originator of the connection, we are the ATT +** client, and the received message is queued up for the client. +** +** If we are the destination of the connection, we are the ATT +** server, so the message is passed to the server processing +** function. +** +** Returns void +** +*******************************************************************************/ +static void gatt_le_data_ind (UINT16 chan, BD_ADDR bd_addr, BT_HDR *p_buf) +{ + tGATT_TCB *p_tcb; + + /* Find CCB based on bd addr */ + if ((p_tcb = gatt_find_tcb_by_addr (bd_addr, BT_TRANSPORT_LE)) != NULL && + gatt_get_ch_state(p_tcb) >= GATT_CH_OPEN) { + gatt_data_process(p_tcb, p_buf); + } else { + osi_free (p_buf); + + if (p_tcb != NULL) { + GATT_TRACE_WARNING ("ATT - Ignored L2CAP data while in state: %d\n", + gatt_get_ch_state(p_tcb)); + } + } +} + +/******************************************************************************* +** +** Function gatt_l2cif_connect_ind +** +** Description This function handles an inbound connection indication +** from L2CAP. This is the case where we are acting as a +** server. +** +** Returns void +** +*******************************************************************************/ +#if (CLASSIC_BT_GATT_INCLUDED == TRUE) +static void gatt_l2cif_connect_ind_cback (BD_ADDR bd_addr, UINT16 lcid, UINT16 psm, UINT8 id) +{ + /* do we already have a control channel for this peer? */ + UINT8 result = L2CAP_CONN_OK; + tL2CAP_CFG_INFO cfg; + tGATT_TCB *p_tcb = gatt_find_tcb_by_addr(bd_addr, BT_TRANSPORT_BR_EDR); + UNUSED(psm); + + GATT_TRACE_ERROR("Connection indication cid = %d", lcid); + /* new connection ? */ + if (p_tcb == NULL) { + /* allocate tcb */ + if ((p_tcb = gatt_allocate_tcb_by_bdaddr(bd_addr, BT_TRANSPORT_BR_EDR)) == NULL) { + /* no tcb available, reject L2CAP connection */ + result = L2CAP_CONN_NO_RESOURCES; + } else { + p_tcb->att_lcid = lcid; + } + + } else { /* existing connection , reject it */ + result = L2CAP_CONN_NO_RESOURCES; + } + + /* Send L2CAP connect rsp */ + L2CA_ConnectRsp(bd_addr, id, lcid, result, 0); + + /* if result ok, proceed with connection */ + if (result == L2CAP_CONN_OK) { + /* transition to configuration state */ + gatt_set_ch_state(p_tcb, GATT_CH_CFG); + + /* Send L2CAP config req */ + memset(&cfg, 0, sizeof(tL2CAP_CFG_INFO)); + cfg.mtu_present = TRUE; + cfg.mtu = gatt_default.local_mtu; + + L2CA_ConfigReq(lcid, &cfg); + } + +} + +/******************************************************************************* +** +** Function gatt_l2c_connect_cfm_cback +** +** Description This is the L2CAP connect confirm callback function. +** +** +** Returns void +** +*******************************************************************************/ +static void gatt_l2cif_connect_cfm_cback(UINT16 lcid, UINT16 result) +{ + tGATT_TCB *p_tcb; + tL2CAP_CFG_INFO cfg; + + /* look up clcb for this channel */ + if ((p_tcb = gatt_find_tcb_by_cid(lcid)) != NULL) { + GATT_TRACE_DEBUG("gatt_l2c_connect_cfm_cback result: %d ch_state: %d, lcid:0x%x", result, gatt_get_ch_state(p_tcb), p_tcb->att_lcid); + + /* if in correct state */ + if (gatt_get_ch_state(p_tcb) == GATT_CH_CONN) { + /* if result successful */ + if (result == L2CAP_CONN_OK) { + /* set channel state */ + gatt_set_ch_state(p_tcb, GATT_CH_CFG); + + /* Send L2CAP config req */ + memset(&cfg, 0, sizeof(tL2CAP_CFG_INFO)); + cfg.mtu_present = TRUE; + cfg.mtu = gatt_default.local_mtu; + L2CA_ConfigReq(lcid, &cfg); + } + /* else initiating connection failure */ + else { + gatt_cleanup_upon_disc(p_tcb->peer_bda, result, GATT_TRANSPORT_BR_EDR); + } + } else { /* wrong state, disconnect it */ + if (result == L2CAP_CONN_OK) { + /* just in case the peer also accepts our connection - Send L2CAP disconnect req */ + L2CA_DisconnectReq(lcid); + } + } + } +} + +/******************************************************************************* +** +** Function gatt_l2cif_config_cfm_cback +** +** Description This is the L2CAP config confirm callback function. +** +** +** Returns void +** +*******************************************************************************/ +void gatt_l2cif_config_cfm_cback(UINT16 lcid, tL2CAP_CFG_INFO *p_cfg) +{ + tGATT_TCB *p_tcb; + tGATTS_SRV_CHG *p_srv_chg_clt = NULL; + + /* look up clcb for this channel */ + if ((p_tcb = gatt_find_tcb_by_cid(lcid)) != NULL) { + /* if in correct state */ + if ( gatt_get_ch_state(p_tcb) == GATT_CH_CFG) { + /* if result successful */ + if (p_cfg->result == L2CAP_CFG_OK) { + /* update flags */ + p_tcb->ch_flags |= GATT_L2C_CFG_CFM_DONE; + + /* if configuration complete */ + if (p_tcb->ch_flags & GATT_L2C_CFG_IND_DONE) { + gatt_set_ch_state(p_tcb, GATT_CH_OPEN); + + if ((p_srv_chg_clt = gatt_is_bda_in_the_srv_chg_clt_list(p_tcb->peer_bda)) != NULL) { +#if (GATTS_INCLUDED == TRUE) + gatt_chk_srv_chg(p_srv_chg_clt); +#endif ///GATTS_INCLUDED == TRUE + } else { + if (btm_sec_is_a_bonded_dev(p_tcb->peer_bda)) { + gatt_add_a_bonded_dev_for_srv_chg(p_tcb->peer_bda); + } + } + + /* send callback */ + gatt_send_conn_cback(p_tcb); + } + } + /* else failure */ + else { + /* Send L2CAP disconnect req */ + L2CA_DisconnectReq(lcid); + } + } + } +} + +/******************************************************************************* +** +** Function gatt_l2cif_config_ind_cback +** +** Description This is the L2CAP config indication callback function. +** +** +** Returns void +** +*******************************************************************************/ +void gatt_l2cif_config_ind_cback(UINT16 lcid, tL2CAP_CFG_INFO *p_cfg) +{ + tGATT_TCB *p_tcb; + tGATTS_SRV_CHG *p_srv_chg_clt = NULL; + /* look up clcb for this channel */ + if ((p_tcb = gatt_find_tcb_by_cid(lcid)) != NULL) { + /* GATT uses the smaller of our MTU and peer's MTU */ + if ( p_cfg->mtu_present && + (p_cfg->mtu >= GATT_MIN_BR_MTU_SIZE && p_cfg->mtu < L2CAP_DEFAULT_MTU)) { + p_tcb->payload_size = p_cfg->mtu; + } else { + p_tcb->payload_size = L2CAP_DEFAULT_MTU; + } + + /* send L2CAP configure response */ + memset(p_cfg, 0, sizeof(tL2CAP_CFG_INFO)); + p_cfg->result = L2CAP_CFG_OK; + L2CA_ConfigRsp(lcid, p_cfg); + + /* if first config ind */ + if ((p_tcb->ch_flags & GATT_L2C_CFG_IND_DONE) == 0) { + /* update flags */ + p_tcb->ch_flags |= GATT_L2C_CFG_IND_DONE; + + /* if configuration complete */ + if (p_tcb->ch_flags & GATT_L2C_CFG_CFM_DONE) { + gatt_set_ch_state(p_tcb, GATT_CH_OPEN); + if ((p_srv_chg_clt = gatt_is_bda_in_the_srv_chg_clt_list(p_tcb->peer_bda)) != NULL) { +#if (GATTS_INCLUDED == TRUE) + gatt_chk_srv_chg(p_srv_chg_clt); +#endif ///GATTS_INCLUDED == TRUE + } else { + if (btm_sec_is_a_bonded_dev(p_tcb->peer_bda)) { + gatt_add_a_bonded_dev_for_srv_chg(p_tcb->peer_bda); + } + } + + /* send callback */ + gatt_send_conn_cback(p_tcb); + } + } + } +} + +/******************************************************************************* +** +** Function gatt_l2cif_disconnect_ind_cback +** +** Description This is the L2CAP disconnect indication callback function. +** +** +** Returns void +** +*******************************************************************************/ +void gatt_l2cif_disconnect_ind_cback(UINT16 lcid, BOOLEAN ack_needed) +{ + tGATT_TCB *p_tcb; + UINT16 reason; + + /* look up clcb for this channel */ + if ((p_tcb = gatt_find_tcb_by_cid(lcid)) != NULL) { + if (ack_needed) { + /* send L2CAP disconnect response */ + L2CA_DisconnectRsp(lcid); + } + if (gatt_is_bda_in_the_srv_chg_clt_list(p_tcb->peer_bda) == NULL) { + if (btm_sec_is_a_bonded_dev(p_tcb->peer_bda)) { + gatt_add_a_bonded_dev_for_srv_chg(p_tcb->peer_bda); + } + } + /* if ACL link is still up, no reason is logged, l2cap is disconnect from peer */ + if ((reason = L2CA_GetDisconnectReason(p_tcb->peer_bda, p_tcb->transport)) == 0) { + reason = GATT_CONN_TERMINATE_PEER_USER; + } + + /* send disconnect callback */ + gatt_cleanup_upon_disc(p_tcb->peer_bda, reason, GATT_TRANSPORT_BR_EDR); + } +} + +/******************************************************************************* +** +** Function gatt_l2cif_disconnect_cfm_cback +** +** Description This is the L2CAP disconnect confirm callback function. +** +** +** Returns void +** +*******************************************************************************/ +static void gatt_l2cif_disconnect_cfm_cback(UINT16 lcid, UINT16 result) +{ + tGATT_TCB *p_tcb; + UINT16 reason; + UNUSED(result); + + /* look up clcb for this channel */ + if ((p_tcb = gatt_find_tcb_by_cid(lcid)) != NULL) { + /* If the device is not in the service changed client list, add it... */ + if (gatt_is_bda_in_the_srv_chg_clt_list(p_tcb->peer_bda) == NULL) { + if (btm_sec_is_a_bonded_dev(p_tcb->peer_bda)) { + gatt_add_a_bonded_dev_for_srv_chg(p_tcb->peer_bda); + } + } + + /* send disconnect callback */ + /* if ACL link is still up, no reason is logged, l2cap is disconnect from peer */ + if ((reason = L2CA_GetDisconnectReason(p_tcb->peer_bda, p_tcb->transport)) == 0) { + reason = GATT_CONN_TERMINATE_LOCAL_HOST; + } + + gatt_cleanup_upon_disc(p_tcb->peer_bda, reason, GATT_TRANSPORT_BR_EDR); + } +} + +/******************************************************************************* +** +** Function gatt_l2cif_data_ind_cback +** +** Description This is the L2CAP data indication callback function. +** +** +** Returns void +** +*******************************************************************************/ +static void gatt_l2cif_data_ind_cback(UINT16 lcid, BT_HDR *p_buf) +{ + tGATT_TCB *p_tcb; + + /* look up clcb for this channel */ + if ((p_tcb = gatt_find_tcb_by_cid(lcid)) != NULL && + gatt_get_ch_state(p_tcb) == GATT_CH_OPEN) { + /* process the data */ + gatt_data_process(p_tcb, p_buf); + } else { /* prevent buffer leak */ + osi_free(p_buf); + } + +} + +/******************************************************************************* +** +** Function gatt_l2cif_congest_cback +** +** Description L2CAP congestion callback +** +** Returns void +** +*******************************************************************************/ +static void gatt_l2cif_congest_cback (UINT16 lcid, BOOLEAN congested) +{ + tGATT_TCB *p_tcb = gatt_find_tcb_by_cid(lcid); + + if (p_tcb != NULL) { + gatt_channel_congestion(p_tcb, congested); + } + +} +#endif ///CLASSIC_BT_GATT_INCLUDED == TRUE + +/******************************************************************************* +** +** Function gatt_send_conn_cback +** +** Description Callback used to notify layer above about a connection. +** +** +** Returns void +** +*******************************************************************************/ +static void gatt_send_conn_cback(tGATT_TCB *p_tcb) +{ + UINT8 i; + tGATT_REG *p_reg; + tGATT_BG_CONN_DEV *p_bg_dev = NULL; + UINT16 conn_id; + + p_bg_dev = gatt_find_bg_dev(p_tcb->peer_bda); + + /* notifying all applications for the connection up event */ + for (i = 0, p_reg = gatt_cb.cl_rcb ; i < GATT_MAX_APPS; i++, p_reg++) { + if (p_reg->in_use) { + if (p_bg_dev && gatt_is_bg_dev_for_app(p_bg_dev, p_reg->gatt_if)) { + gatt_update_app_use_link_flag(p_reg->gatt_if, p_tcb, TRUE, TRUE); + } + + if (p_reg->app_cb.p_conn_cb) { + conn_id = GATT_CREATE_CONN_ID(p_tcb->tcb_idx, p_reg->gatt_if); + (*p_reg->app_cb.p_conn_cb)(p_reg->gatt_if, p_tcb->peer_bda, conn_id, + TRUE, 0, p_tcb->transport); + } + } + } + + + if (gatt_num_apps_hold_link(p_tcb) && p_tcb->att_lcid == L2CAP_ATT_CID ) { + /* disable idle timeout if one or more clients are holding the link disable the idle timer */ + GATT_SetIdleTimeout(p_tcb->peer_bda, GATT_LINK_NO_IDLE_TIMEOUT, p_tcb->transport); + } +} + +/******************************************************************************* +** +** Function gatt_le_data_ind +** +** Description This function is called when data is received from L2CAP. +** if we are the originator of the connection, we are the ATT +** client, and the received message is queued up for the client. +** +** If we are the destination of the connection, we are the ATT +** server, so the message is passed to the server processing +** function. +** +** Returns void +** +*******************************************************************************/ +void gatt_data_process (tGATT_TCB *p_tcb, BT_HDR *p_buf) +{ + UINT8 *p = (UINT8 *)(p_buf + 1) + p_buf->offset; + UINT8 op_code, pseudo_op_code; +#if (GATTS_INCLUDED == TRUE) || (GATTC_INCLUDED == TRUE) + UINT16 msg_len; +#endif ///(GATTS_INCLUDED == TRUE) || (GATTC_INCLUDED == TRUE) + + + if (p_buf->len > 0) { +#if (GATTS_INCLUDED == TRUE) || (GATTC_INCLUDED == TRUE) + msg_len = p_buf->len - 1; +#endif ///(GATTS_INCLUDED == TRUE) || (GATTC_INCLUDED == TRUE) + STREAM_TO_UINT8(op_code, p); + + /* remove the two MSBs associated with sign write and write cmd */ + pseudo_op_code = op_code & (~GATT_WRITE_CMD_MASK); + + if (pseudo_op_code < GATT_OP_CODE_MAX) { + if (op_code == GATT_SIGN_CMD_WRITE) { +#if (SMP_INCLUDED == TRUE) + gatt_verify_signature(p_tcb, p_buf); +#endif ///SMP_INCLUDED == TRUE + } else { + /* message from client */ + if ((op_code % 2) == 0) { +#if (GATTS_INCLUDED == TRUE) + gatt_server_handle_client_req (p_tcb, op_code, msg_len, p); +#endif ///GATTS_INCLUDED == TRUE + } else { +#if (GATTC_INCLUDED == TRUE) + gatt_client_handle_server_rsp (p_tcb, op_code, msg_len, p); +#endif ///GATTC_INCLUDED == TRUE + } + } + } else { + if (op_code & GATT_COMMAND_FLAG) { + GATT_TRACE_ERROR ("ATT - Rcvd L2CAP data, unknown cmd: 0x%x\n", op_code); + } else { + GATT_TRACE_ERROR ("ATT - Rcvd L2CAP data, unknown req: 0x%x\n", op_code); + gatt_send_error_rsp (p_tcb, GATT_REQ_NOT_SUPPORTED, op_code, 0, FALSE); + } + } + } else { + GATT_TRACE_ERROR ("invalid data length, ignore\n"); + } + + osi_free (p_buf); +} + +/******************************************************************************* +** +** Function gatt_add_a_bonded_dev_for_srv_chg +** +** Description Add a bonded dev to the service changed client list +** +** Returns void +** +*******************************************************************************/ +void gatt_add_a_bonded_dev_for_srv_chg (BD_ADDR bda) +{ + tGATTS_SRV_CHG_REQ req; + tGATTS_SRV_CHG srv_chg_clt; + + memcpy(srv_chg_clt.bda, bda, BD_ADDR_LEN); + srv_chg_clt.srv_changed = FALSE; + if (gatt_add_srv_chg_clt(&srv_chg_clt) != NULL) { + memcpy(req.srv_chg.bda, bda, BD_ADDR_LEN); + req.srv_chg.srv_changed = FALSE; + if (gatt_cb.cb_info.p_srv_chg_callback) { + (*gatt_cb.cb_info.p_srv_chg_callback)(GATTS_SRV_CHG_CMD_ADD_CLIENT, &req, NULL); + } + } +} + +/******************************************************************************* +** +** Function gatt_send_srv_chg_ind +** +** Description This function is called to send a service changed indication to +** the specified bd address +** +** Returns GATT_SUCCESS if successfully sent; otherwise error code +** +*******************************************************************************/ +#if (GATTS_INCLUDED == TRUE) +tGATT_STATUS gatt_send_srv_chg_ind (BD_ADDR peer_bda) +{ + UINT8 handle_range[GATT_SIZE_OF_SRV_CHG_HNDL_RANGE]; + UINT8 *p = handle_range; + UINT16 conn_id; + tGATT_STATUS status = GATT_ERROR; + GATT_TRACE_DEBUG("gatt_send_srv_chg_ind"); + + if (gatt_cb.handle_of_h_r) { + if ((conn_id = gatt_profile_find_conn_id_by_bd_addr(peer_bda)) != GATT_INVALID_CONN_ID) { + UINT16_TO_STREAM (p, 1); + UINT16_TO_STREAM (p, 0xFFFF); + status = GATTS_HandleValueIndication (conn_id, + gatt_cb.handle_of_h_r, + GATT_SIZE_OF_SRV_CHG_HNDL_RANGE, + handle_range); + } else { + status = GATT_NOT_FOUND; + GATT_TRACE_ERROR("Unable to find conn_id for %02x%02x%02x%02x%02x%02x ", + peer_bda[0], peer_bda[1], peer_bda[2], peer_bda[3], peer_bda[4], peer_bda[5]); + } + } + return status; +} + + +/******************************************************************************* +** +** Function gatt_chk_srv_chg +** +** Description Check sending service changed Indication is required or not +** if required then send the Indication +** +** Returns void +** +*******************************************************************************/ +void gatt_chk_srv_chg(tGATTS_SRV_CHG *p_srv_chg_clt) +{ + GATT_TRACE_DEBUG("gatt_chk_srv_chg srv_changed=%d", p_srv_chg_clt->srv_changed ); + + if (p_srv_chg_clt->srv_changed) { + gatt_send_srv_chg_ind(p_srv_chg_clt->bda); + } +} +#endif ///GATTS_INCLUDED == TRUE + + +/******************************************************************************* +** +** Function gatt_init_srv_chg +** +** Description This function is used to initialize the service changed +** attribute value +** +** Returns void +** +*******************************************************************************/ +void gatt_init_srv_chg (void) +{ + tGATTS_SRV_CHG_REQ req; + tGATTS_SRV_CHG_RSP rsp; + BOOLEAN status; + UINT8 num_clients, i; + tGATTS_SRV_CHG srv_chg_clt; + + GATT_TRACE_DEBUG("gatt_init_srv_chg"); + if (gatt_cb.cb_info.p_srv_chg_callback) { + status = (*gatt_cb.cb_info.p_srv_chg_callback)(GATTS_SRV_CHG_CMD_READ_NUM_CLENTS, NULL, &rsp); + + if (status && rsp.num_clients) { + GATT_TRACE_DEBUG("gatt_init_srv_chg num_srv_chg_clt_clients=%d", rsp.num_clients); + num_clients = rsp.num_clients; + i = 1; /* use one based index */ + while ((i <= num_clients) && status) { + req.client_read_index = i; + if ((status = (*gatt_cb.cb_info.p_srv_chg_callback)(GATTS_SRV_CHG_CMD_READ_CLENT, &req, &rsp)) == TRUE) { + memcpy(&srv_chg_clt, &rsp.srv_chg , sizeof(tGATTS_SRV_CHG)); + if (gatt_add_srv_chg_clt(&srv_chg_clt) == NULL) { + GATT_TRACE_ERROR("Unable to add a service change client"); + status = FALSE; + } + } + i++; + } + } + } else { + GATT_TRACE_DEBUG("gatt_init_srv_chg callback not registered yet"); + } +} + +/******************************************************************************* +** +** Function gatt_proc_srv_chg +** +** Description This function is process the service changed request +** +** Returns void +** +*******************************************************************************/ +#if (GATTS_INCLUDED == TRUE) +void gatt_proc_srv_chg (void) +{ + UINT8 start_idx, found_idx; + BD_ADDR bda; + BOOLEAN srv_chg_ind_pending = FALSE; + tGATT_TCB *p_tcb; + tBT_TRANSPORT transport; + + GATT_TRACE_DEBUG ("gatt_proc_srv_chg"); + + if (gatt_cb.cb_info.p_srv_chg_callback && gatt_cb.handle_of_h_r) { + gatt_set_srv_chg(); + start_idx = 0; + while (gatt_find_the_connected_bda(start_idx, bda, &found_idx, &transport)) { + p_tcb = gatt_get_tcb_by_idx(found_idx); + srv_chg_ind_pending = gatt_is_srv_chg_ind_pending(p_tcb); + + if (!srv_chg_ind_pending) { + gatt_send_srv_chg_ind(bda); + } else { + GATT_TRACE_DEBUG ("discard srv chg - already has one in the queue"); + } + start_idx = ++found_idx; + } + } +} +#endif ///GATTS_INCLUDED == TRUE + +/******************************************************************************* +** +** Function gatt_set_ch_state +** +** Description This function set the ch_state in tcb +** +** Returns none +** +*******************************************************************************/ +void gatt_set_ch_state(tGATT_TCB *p_tcb, tGATT_CH_STATE ch_state) +{ + if (p_tcb) { + GATT_TRACE_DEBUG ("gatt_set_ch_state: old=%d new=%d", p_tcb->ch_state, ch_state); + p_tcb->ch_state = ch_state; + } +} + +/******************************************************************************* +** +** Function gatt_get_ch_state +** +** Description This function get the ch_state in tcb +** +** Returns none +** +*******************************************************************************/ +tGATT_CH_STATE gatt_get_ch_state(tGATT_TCB *p_tcb) +{ + tGATT_CH_STATE ch_state = GATT_CH_CLOSE; + if (p_tcb) { + GATT_TRACE_DEBUG ("gatt_get_ch_state: ch_state=%d", p_tcb->ch_state); + ch_state = p_tcb->ch_state; + } + return ch_state; +} + +uint16_t gatt_get_local_mtu(void) +{ + return gatt_default.local_mtu; +} + +void gatt_set_local_mtu(uint16_t mtu) +{ + gatt_default.local_mtu = mtu; +} + +uint8_t gatt_tcb_active_count(void) +{ + tGATT_TCB *p_tcb = NULL; + list_node_t *p_node = NULL; + uint8_t count = 0; + + for(p_node = list_begin(gatt_cb.p_tcb_list); p_node; p_node = list_next(p_node)) { + p_tcb = list_node(p_node); + if (p_tcb && p_tcb->in_use && (p_tcb->ch_state != GATT_CH_CLOSE)) { + count++; + } + } + + return count; +} + +#endif /* BLE_INCLUDED */ diff --git a/lib/bt/host/bluedroid/stack/gatt/gatt_sr.c b/lib/bt/host/bluedroid/stack/gatt/gatt_sr.c new file mode 100644 index 00000000..79e161c1 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/gatt/gatt_sr.c @@ -0,0 +1,1900 @@ +/****************************************************************************** + * + * Copyright (C) 2008-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * this file contains the GATT server functions + * + ******************************************************************************/ + +#include "common/bt_target.h" +#include "osi/allocator.h" + +#if BLE_INCLUDED == TRUE && GATTS_INCLUDED == TRUE +#include +#include "gatt_int.h" +#include "stack/l2c_api.h" +#include "l2c_int.h" +#define GATT_MTU_REQ_MIN_LEN 2 + + + +/******************************************************************************* +** +** Function gatt_send_packet +** +** Description This function is called to send gatt packets directly + +** +** Returns status +** +*******************************************************************************/ +tGATT_STATUS gatt_send_packet (tGATT_TCB *p_tcb, UINT8 *p_data, UINT16 len) +{ + BT_HDR *p_msg = NULL; + UINT8 *p_m = NULL; + UINT16 buf_len; + tGATT_STATUS status; + + if (len > p_tcb->payload_size){ + return GATT_ILLEGAL_PARAMETER; + } + + buf_len = (UINT16)(sizeof(BT_HDR) + p_tcb->payload_size + L2CAP_MIN_OFFSET); + if ((p_msg = (BT_HDR *)osi_malloc(buf_len)) == NULL) { + return GATT_NO_RESOURCES; + } + + memset(p_msg, 0, buf_len); + p_msg->len = len; + p_m = (UINT8 *)(p_msg + 1) + L2CAP_MIN_OFFSET; + memcpy(p_m, p_data, len); + + status = attp_send_sr_msg(p_tcb, p_msg); + return status; +} + +/******************************************************************************* +** +** Function gatt_sr_enqueue_cmd +** +** Description This function enqueue the request from client which needs a +** application response, and update the transaction ID. +** +** Returns void +** +*******************************************************************************/ +UINT32 gatt_sr_enqueue_cmd (tGATT_TCB *p_tcb, UINT8 op_code, UINT16 handle) +{ + tGATT_SR_CMD *p_cmd = &p_tcb->sr_cmd; + UINT32 trans_id = 0; + + if ( (p_cmd->op_code == 0) || + (op_code == GATT_HANDLE_VALUE_CONF)) { /* no pending request */ + if (op_code == GATT_CMD_WRITE || + op_code == GATT_SIGN_CMD_WRITE || + op_code == GATT_REQ_MTU || + op_code == GATT_HANDLE_VALUE_CONF) { + trans_id = ++p_tcb->trans_id; + } else { + p_cmd->trans_id = ++p_tcb->trans_id; + p_cmd->op_code = op_code; + p_cmd->handle = handle; + p_cmd->status = GATT_NOT_FOUND; + p_tcb->trans_id %= GATT_TRANS_ID_MAX; + trans_id = p_cmd->trans_id; + } + } + + return trans_id; +} + +/******************************************************************************* +** +** Function gatt_sr_cmd_empty +** +** Description This function check the server command queue is empty or not. +** +** Returns TRUE if empty, FALSE if there is pending command. +** +*******************************************************************************/ +BOOLEAN gatt_sr_cmd_empty (tGATT_TCB *p_tcb) +{ + return (p_tcb->sr_cmd.op_code == 0); +} + +/******************************************************************************* +** +** Function gatt_dequeue_sr_cmd +** +** Description This function dequeue the request from command queue. +** +** Returns void +** +*******************************************************************************/ +void gatt_dequeue_sr_cmd (tGATT_TCB *p_tcb) +{ + /* Double check in case any buffers are queued */ + GATT_TRACE_DEBUG("gatt_dequeue_sr_cmd" ); + if (p_tcb->sr_cmd.p_rsp_msg) { + GATT_TRACE_ERROR("%s free msg %p", __func__, p_tcb->sr_cmd.p_rsp_msg); + + osi_free(p_tcb->sr_cmd.p_rsp_msg); + p_tcb->sr_cmd.p_rsp_msg = NULL; + } + + if (p_tcb->sr_cmd.multi_rsp_q) { + while (!fixed_queue_is_empty(p_tcb->sr_cmd.multi_rsp_q)) { + osi_free(fixed_queue_dequeue(p_tcb->sr_cmd.multi_rsp_q, 0)); + } + fixed_queue_free(p_tcb->sr_cmd.multi_rsp_q, NULL); + } + + memset( &p_tcb->sr_cmd, 0, sizeof(tGATT_SR_CMD)); +} + +/******************************************************************************* +** +** Function process_read_multi_rsp +** +** Description This function check the read multiple response. +** +** Returns BOOLEAN if all replies have been received +** +*******************************************************************************/ +static BOOLEAN process_read_multi_rsp (tGATT_SR_CMD *p_cmd, tGATT_STATUS status, + tGATTS_RSP *p_msg, UINT16 mtu) +{ + UINT16 ii, total_len, len; + UINT8 *p; + BOOLEAN is_overflow = FALSE; + + GATT_TRACE_DEBUG ("process_read_multi_rsp status=%d mtu=%d", status, mtu); + + if (p_cmd->multi_rsp_q == NULL) { + p_cmd->multi_rsp_q = fixed_queue_new(QUEUE_SIZE_MAX); + } + + /* Enqueue the response */ + BT_HDR *p_buf = (BT_HDR *)osi_malloc(sizeof(tGATTS_RSP)); + if (p_buf == NULL) { + p_cmd->status = GATT_INSUF_RESOURCE; + return FALSE; + } + memcpy((void *)p_buf, (const void *)p_msg, sizeof(tGATTS_RSP)); + + fixed_queue_enqueue(p_cmd->multi_rsp_q, p_buf, FIXED_QUEUE_MAX_TIMEOUT); + + p_cmd->status = status; + if (status == GATT_SUCCESS) { + GATT_TRACE_DEBUG ("Multi read count=%d num_hdls=%d", + fixed_queue_length(p_cmd->multi_rsp_q), + p_cmd->multi_req.num_handles); + /* Wait till we get all the responses */ + if (fixed_queue_length(p_cmd->multi_rsp_q) == p_cmd->multi_req.num_handles) { + len = sizeof(BT_HDR) + L2CAP_MIN_OFFSET + mtu; + if ((p_buf = (BT_HDR *)osi_calloc(len)) == NULL) { + p_cmd->status = GATT_INSUF_RESOURCE; + return (TRUE); + } + + p_buf->offset = L2CAP_MIN_OFFSET; + p = (UINT8 *)(p_buf + 1) + p_buf->offset; + + /* First byte in the response is the opcode */ + *p++ = GATT_RSP_READ_MULTI; + p_buf->len = 1; + + /* Now walk through the buffers puting the data into the response in order */ + list_t *list = NULL; + const list_node_t *node = NULL; + if (! fixed_queue_is_empty(p_cmd->multi_rsp_q)) { + list = fixed_queue_get_list(p_cmd->multi_rsp_q); + } + for (ii = 0; ii < p_cmd->multi_req.num_handles; ii++) { + tGATTS_RSP *p_rsp = NULL; + if (list != NULL) { + if (ii == 0) { + node = list_begin(list); + } else { + node = list_next(node); + } + if (node != list_end(list)) { + p_rsp = (tGATTS_RSP *)list_node(node); + } + } + + if (p_rsp != NULL) { + + total_len = (p_buf->len + p_rsp->attr_value.len); + + if (total_len > mtu) { + /* just send the partial response for the overflow case */ + len = p_rsp->attr_value.len - (total_len - mtu); + is_overflow = TRUE; + GATT_TRACE_DEBUG ("multi read overflow available len=%d val_len=%d", len, p_rsp->attr_value.len ); + } else { + len = p_rsp->attr_value.len; + } + + if (p_rsp->attr_value.handle == p_cmd->multi_req.handles[ii]) { + memcpy (p, p_rsp->attr_value.value, len); + if (!is_overflow) { + p += len; + } + p_buf->len += len; + } else { + p_cmd->status = GATT_NOT_FOUND; + break; + } + + if (is_overflow) { + break; + } + + } else { + p_cmd->status = GATT_NOT_FOUND; + break; + } + + } /* loop through all handles*/ + + + /* Sanity check on the buffer length */ + if (p_buf->len == 0) { + GATT_TRACE_ERROR("process_read_multi_rsp - nothing found!!"); + p_cmd->status = GATT_NOT_FOUND; + osi_free (p_buf); + GATT_TRACE_DEBUG(" osi_free (p_buf)"); + } else if (p_cmd->p_rsp_msg != NULL) { + osi_free (p_buf); + } else { + p_cmd->p_rsp_msg = p_buf; + } + + return (TRUE); + } + } else { /* any handle read exception occurs, return error */ + return (TRUE); + } + + /* If here, still waiting */ + return (FALSE); +} + +static BOOLEAN process_read_multi_var_rsp (tGATT_SR_CMD *p_cmd, tGATT_STATUS status, + tGATTS_RSP *p_msg, UINT16 mtu) +{ + UINT16 ii; + UINT16 total_len; + UINT16 len; + UINT8 *p; + + GATT_TRACE_DEBUG ("process_read_multi_var rsp status=%d mtu=%d", status, mtu); + + if (p_cmd->multi_rsp_q == NULL) { + p_cmd->multi_rsp_q = fixed_queue_new(QUEUE_SIZE_MAX); + } + + /* Enqueue the response */ + BT_HDR *p_buf = (BT_HDR *)osi_malloc(sizeof(tGATTS_RSP)); + if (p_buf == NULL) { + p_cmd->status = GATT_INSUF_RESOURCE; + return FALSE; + } + memcpy((void *)p_buf, (const void *)p_msg, sizeof(tGATTS_RSP)); + + fixed_queue_enqueue(p_cmd->multi_rsp_q, p_buf, FIXED_QUEUE_MAX_TIMEOUT); + + p_cmd->status = status; + if (status == GATT_SUCCESS) { + GATT_TRACE_DEBUG ("Multi var read count=%d num_hdls=%d", + fixed_queue_length(p_cmd->multi_rsp_q), + p_cmd->multi_req.num_handles); + /* Wait till we get all the responses */ + if (fixed_queue_length(p_cmd->multi_rsp_q) == p_cmd->multi_req.num_handles) { + len = sizeof(BT_HDR) + L2CAP_MIN_OFFSET + mtu; + if ((p_buf = (BT_HDR *)osi_calloc(len)) == NULL) { + p_cmd->status = GATT_INSUF_RESOURCE; + return (TRUE); + } + + p_buf->offset = L2CAP_MIN_OFFSET; + p = (UINT8 *)(p_buf + 1) + p_buf->offset; + + /* First byte in the response is the opcode */ + *p++ = GATT_RSP_READ_MULTI_VAR; + p_buf->len = 1; + + /* Now walk through the buffers puting the data into the response in order */ + list_t *list = NULL; + const list_node_t *node = NULL; + if (! fixed_queue_is_empty(p_cmd->multi_rsp_q)) { + list = fixed_queue_get_list(p_cmd->multi_rsp_q); + } + for (ii = 0; ii < p_cmd->multi_req.num_handles; ii++) { + tGATTS_RSP *p_rsp = NULL; + if (list != NULL) { + if (ii == 0) { + node = list_begin(list); + } else { + node = list_next(node); + } + if (node != list_end(list)) { + p_rsp = (tGATTS_RSP *)list_node(node); + } + } + + if (p_rsp != NULL) { + + total_len = (p_buf->len + 2); // value length + + if (total_len > mtu) { + GATT_TRACE_DEBUG ("multi read variable overflow available len=%d val_len=%d", len, p_rsp->attr_value.len ); + break; + } + len = MIN(p_rsp->attr_value.len, (mtu - total_len)); // attribute value length + + if (p_rsp->attr_value.handle == p_cmd->multi_req.handles[ii]) { + GATT_TRACE_DEBUG("%s handle %x len %u", __func__, p_rsp->attr_value.handle, p_rsp->attr_value.len); + UINT16_TO_STREAM(p, p_rsp->attr_value.len); + memcpy (p, p_rsp->attr_value.value, len); + p += len; + p_buf->len += (2+len); + } else { + p_cmd->status = GATT_NOT_FOUND; + break; + } + } else { + p_cmd->status = GATT_NOT_FOUND; + break; + } + + } /* loop through all handles*/ + + /* Sanity check on the buffer length */ + if (p_buf->len == 0) { + GATT_TRACE_ERROR("%s - nothing found!!", __func__); + p_cmd->status = GATT_NOT_FOUND; + osi_free (p_buf); + } else if (p_cmd->p_rsp_msg != NULL) { + osi_free (p_buf); + } else { + p_cmd->p_rsp_msg = p_buf; + } + + return (TRUE); + } + } else { /* any handle read exception occurs, return error */ + return (TRUE); + } + + /* If here, still waiting */ + return (FALSE); +} + +/******************************************************************************* +** +** Function gatt_sr_process_app_rsp +** +** Description This function checks whether the response message from application +** match any pending request or not. +** +** Returns void +** +*******************************************************************************/ +tGATT_STATUS gatt_sr_process_app_rsp (tGATT_TCB *p_tcb, tGATT_IF gatt_if, + UINT32 trans_id, UINT8 op_code, + tGATT_STATUS status, tGATTS_RSP *p_msg) +{ + tGATT_STATUS ret_code = GATT_SUCCESS; + UNUSED(trans_id); + + GATT_TRACE_DEBUG("gatt_sr_process_app_rsp gatt_if=%d\n", gatt_if); + + gatt_sr_update_cback_cnt(p_tcb, gatt_if, FALSE, FALSE); + + if (op_code == GATT_REQ_READ_MULTI) { + /* If no error and still waiting, just return */ + if (!process_read_multi_rsp (&p_tcb->sr_cmd, status, p_msg, p_tcb->payload_size)) { + return (GATT_SUCCESS); + } + } else if (op_code == GATT_REQ_READ_MULTI_VAR) { + if (!process_read_multi_var_rsp(&p_tcb->sr_cmd, status, p_msg, p_tcb->payload_size)) { + return (GATT_SUCCESS); + } + } else { + if (op_code == GATT_REQ_PREPARE_WRITE && status == GATT_SUCCESS) { + gatt_sr_update_prep_cnt(p_tcb, gatt_if, TRUE, FALSE); + } + + if (op_code == GATT_REQ_EXEC_WRITE && status != GATT_SUCCESS) { + gatt_sr_reset_cback_cnt(p_tcb); + } + + p_tcb->sr_cmd.status = status; + + if (gatt_sr_is_cback_cnt_zero(p_tcb) + && status == GATT_SUCCESS) { + if (p_tcb->sr_cmd.p_rsp_msg == NULL) { + p_tcb->sr_cmd.p_rsp_msg = attp_build_sr_msg (p_tcb, (UINT8)(op_code + 1), (tGATT_SR_MSG *)p_msg); + } else { + GATT_TRACE_ERROR("Exception!!! already has respond message\n"); + } + } + } + if (gatt_sr_is_cback_cnt_zero(p_tcb)) { + if ( (p_tcb->sr_cmd.status == GATT_SUCCESS) && (p_tcb->sr_cmd.p_rsp_msg) ) { + ret_code = attp_send_sr_msg (p_tcb, p_tcb->sr_cmd.p_rsp_msg); + p_tcb->sr_cmd.p_rsp_msg = NULL; + } else { + if (p_tcb->sr_cmd.status == GATT_SUCCESS){ + status = GATT_UNKNOWN_ERROR; + } + ret_code = gatt_send_error_rsp (p_tcb, status, op_code, p_tcb->sr_cmd.handle, FALSE); + } + + gatt_dequeue_sr_cmd(p_tcb); + } + + GATT_TRACE_DEBUG("gatt_sr_process_app_rsp ret_code=%d\n", ret_code); + + return ret_code; +} + +/******************************************************************************* +** +** Function gatt_process_exec_write_req +** +** Description This function is called to process the execute write request +** from client. +** +** Returns void +** +*******************************************************************************/ +void gatt_process_exec_write_req (tGATT_TCB *p_tcb, UINT8 op_code, UINT16 len, UINT8 *p_data) +{ + UINT8 *p = p_data, flag, i = 0; + UINT32 trans_id = 0; + tGATT_IF gatt_if; + UINT16 conn_id; + UINT16 queue_num = 0; + BOOLEAN is_first = TRUE; + BOOLEAN is_prepare_write_valid = FALSE; + BOOLEAN is_need_dequeue_sr_cmd = FALSE; + tGATT_PREPARE_WRITE_RECORD *prepare_record = NULL; + tGATT_PREPARE_WRITE_QUEUE_DATA * queue_data = NULL; + UNUSED(len); + +#if GATT_CONFORMANCE_TESTING == TRUE + if (gatt_cb.enable_err_rsp && gatt_cb.req_op_code == op_code) { + GATT_TRACE_DEBUG("Conformance tst: forced err rspv for Execute Write: error status=%d", + gatt_cb.err_status); + + gatt_send_error_rsp (p_tcb, gatt_cb.err_status, gatt_cb.req_op_code, gatt_cb.handle, FALSE); + + return; + } +#endif + + STREAM_TO_UINT8(flag, p); + + /* mask the flag */ + flag &= GATT_PREP_WRITE_EXEC; + + prepare_record = &(p_tcb->prepare_write_record); + queue_num = fixed_queue_length(prepare_record->queue); + + //if received prepare_write packets include stack_rsp and app_rsp, + //stack respond to execute_write only when stack_rsp handle has invalid_offset + //or invalid_length error; + //app need to respond to execute_write if it has received app_rsp handle packets + if (((prepare_record->error_code_app == GATT_SUCCESS) && + (prepare_record->total_num == queue_num)) + || (flag == GATT_PREP_WRITE_CANCEL)){ + tGATT_EXEC_WRITE_RSP gatt_exec_write_rsp; + gatt_exec_write_rsp.op_code = GATT_RSP_EXEC_WRITE; + gatt_send_packet(p_tcb, (UINT8 *)(&gatt_exec_write_rsp), sizeof(gatt_exec_write_rsp)); + gatt_dequeue_sr_cmd(p_tcb); + if (flag != GATT_PREP_WRITE_CANCEL){ + is_prepare_write_valid = TRUE; + } + GATT_TRACE_DEBUG("Send execute_write_rsp\n"); + } else if ((prepare_record->error_code_app == GATT_SUCCESS) && + (prepare_record->total_num > queue_num)){ + //No error for stack_rsp's handles and there exist some app_rsp's handles, + //so exec_write_rsp depends to app's response; but stack_rsp's data is valid + //TODO: there exist problem if stack_rsp's data is valid but app_rsp's data is not valid. + is_prepare_write_valid = TRUE; + } else if(prepare_record->total_num < queue_num) { + GATT_TRACE_ERROR("Error in %s, line=%d, prepare write total number (%d) \ + should not smaller than prepare queue number (%d)\n", \ + __func__, __LINE__,prepare_record->total_num, queue_num); + } else if (prepare_record->error_code_app != GATT_SUCCESS){ + GATT_TRACE_DEBUG("Send error code for execute_write, code=0x%x\n", prepare_record->error_code_app); + is_need_dequeue_sr_cmd = (prepare_record->total_num == queue_num) ? TRUE : FALSE; + gatt_send_error_rsp(p_tcb, prepare_record->error_code_app, GATT_REQ_EXEC_WRITE, 0, is_need_dequeue_sr_cmd); + } + + //dequeue prepare write data + while(fixed_queue_try_peek_first(prepare_record->queue)) { + queue_data = fixed_queue_dequeue(prepare_record->queue, FIXED_QUEUE_MAX_TIMEOUT); + if (is_prepare_write_valid){ + if((queue_data->p_attr->p_value != NULL) && (queue_data->p_attr->p_value->attr_val.attr_val != NULL)){ + if(is_first) { + //clear attr_val.attr_len before handle prepare write data + queue_data->p_attr->p_value->attr_val.attr_len = 0; + is_first = FALSE; + } + memcpy(queue_data->p_attr->p_value->attr_val.attr_val+queue_data->offset, queue_data->value, queue_data->len); + //don't forget to increase the attribute value length in the gatts database. + queue_data->p_attr->p_value->attr_val.attr_len += queue_data->len; + } + } + osi_free(queue_data); + } + fixed_queue_free(prepare_record->queue, NULL); + prepare_record->queue = NULL; + + /* according to ble spec, even if there is no prep write queued, + * need to respond execute_write_response + * Note: exec_write_rsp callback should be called after all data has been written*/ + if (!gatt_sr_is_prep_cnt_zero(p_tcb)) { + if (prepare_record->total_num > queue_num){ + trans_id = gatt_sr_enqueue_cmd(p_tcb, op_code, 0); + gatt_sr_copy_prep_cnt_to_cback_cnt(p_tcb); + } + + for (i = 0; i < GATT_MAX_APPS; i++) { + if (p_tcb->prep_cnt[i]) { + gatt_if = (tGATT_IF) (i + 1); + conn_id = GATT_CREATE_CONN_ID(p_tcb->tcb_idx, gatt_if); + gatt_sr_send_req_callback(conn_id, + trans_id, + GATTS_REQ_TYPE_WRITE_EXEC, + (tGATTS_DATA *)&flag); + p_tcb->prep_cnt[i] = 0; + } + } + } + + prepare_record->total_num = 0; + prepare_record->error_code_app = GATT_SUCCESS; +} + +/******************************************************************************* +** +** Function gatt_process_read_multi_req +** +** Description This function is called to process the read multiple request +** from client. +** +** Returns void +** +*******************************************************************************/ +void gatt_process_read_multi_req (tGATT_TCB *p_tcb, UINT8 op_code, UINT16 len, UINT8 *p_data) +{ + UINT32 trans_id; + UINT16 handle = 0, ll = len; + UINT8 *p = p_data, i_rcb; + tGATT_STATUS err = GATT_SUCCESS; + UINT8 sec_flag, key_size; + tGATTS_RSP *p_msg; + + GATT_TRACE_DEBUG("gatt_process_read_multi_req" ); + p_tcb->sr_cmd.multi_req.num_handles = 0; + + gatt_sr_get_sec_info(p_tcb->peer_bda, + p_tcb->transport, + &sec_flag, + &key_size); + +#if GATT_CONFORMANCE_TESTING == TRUE + if (gatt_cb.enable_err_rsp && gatt_cb.req_op_code == op_code) { + GATT_TRACE_DEBUG("Conformance tst: forced err rspvofr ReadMultiple: error status=%d\n", gatt_cb.err_status); + + STREAM_TO_UINT16(handle, p); + + gatt_send_error_rsp (p_tcb, gatt_cb.err_status, gatt_cb.req_op_code, handle, FALSE); + + return; + } +#endif + + while (ll >= 2 && p_tcb->sr_cmd.multi_req.num_handles < GATT_MAX_READ_MULTI_HANDLES) { + STREAM_TO_UINT16(handle, p); + + if ((i_rcb = gatt_sr_find_i_rcb_by_handle(handle)) < GATT_MAX_SR_PROFILES) { + p_tcb->sr_cmd.multi_req.handles[p_tcb->sr_cmd.multi_req.num_handles++] = handle; + + /* check read permission */ + if ((err = gatts_read_attr_perm_check( gatt_cb.sr_reg[i_rcb].p_db, + FALSE, + handle, + sec_flag, + key_size)) + != GATT_SUCCESS) { + GATT_TRACE_ERROR("read permission denied : 0x%02x", err); + break; + } + } else { + /* invalid handle */ + err = GATT_INVALID_HANDLE; + break; + } + ll -= 2; + } + + if (err == GATT_SUCCESS) { + if (ll != 0) { + GATT_TRACE_ERROR("max attribute handle reached in ReadMultiple Request."); + err = GATT_INVALID_HANDLE; + } + + if (p_tcb->sr_cmd.multi_req.num_handles == 0) { + err = GATT_INVALID_HANDLE; + } + } + + if (err == GATT_SUCCESS) { + if ((trans_id = gatt_sr_enqueue_cmd (p_tcb, op_code, p_tcb->sr_cmd.multi_req.handles[0])) != 0) { + gatt_sr_reset_cback_cnt(p_tcb); /* read multiple use multi_rsp_q's count*/ + + for (ll = 0; ll < p_tcb->sr_cmd.multi_req.num_handles; ll ++) { + if ((p_msg = (tGATTS_RSP *)osi_malloc(sizeof(tGATTS_RSP))) != NULL) { + memset(p_msg, 0, sizeof(tGATTS_RSP)) + ; + handle = p_tcb->sr_cmd.multi_req.handles[ll]; + i_rcb = gatt_sr_find_i_rcb_by_handle(handle); + + p_msg->attr_value.handle = handle; + err = gatts_read_attr_value_by_handle(p_tcb, + gatt_cb.sr_reg[i_rcb].p_db, + op_code, + handle, + 0, + p_msg->attr_value.value, + &p_msg->attr_value.len, + GATT_MAX_ATTR_LEN, + sec_flag, + key_size, + trans_id); + + if (err == GATT_SUCCESS || err == GATT_STACK_RSP) { + gatt_sr_process_app_rsp(p_tcb, gatt_cb.sr_reg[i_rcb].gatt_if , trans_id, op_code, GATT_SUCCESS, p_msg); + } + /* either not using or done using the buffer, release it now */ + osi_free(p_msg); + } else { + err = GATT_NO_RESOURCES; + gatt_dequeue_sr_cmd(p_tcb); + break; + } + } + } else { + err = GATT_NO_RESOURCES; + } + } + /* in theroy BUSY is not possible(should already been checked), protected check */ + if (err != GATT_SUCCESS && err != GATT_STACK_RSP && err != GATT_PENDING && err != GATT_BUSY) { + gatt_send_error_rsp(p_tcb, err, op_code, handle, FALSE); + } +} + +/******************************************************************************* +** +** Function gatt_build_primary_service_rsp +** +** Description Primamry service request processed internally. Theretically +** only deal with ReadByTypeVAlue and ReadByGroupType. +** +** Returns void +** +*******************************************************************************/ +static tGATT_STATUS gatt_build_primary_service_rsp (BT_HDR *p_msg, tGATT_TCB *p_tcb, + UINT8 op_code, UINT16 s_hdl, + UINT16 e_hdl, UINT8 *p_data, tBT_UUID value) +{ + tGATT_STATUS status = GATT_NOT_FOUND; + UINT8 handle_len = 4, *p ; + tGATT_SR_REG *p_rcb; + tGATT_SRV_LIST_INFO *p_list = &gatt_cb.srv_list_info; + tGATT_SRV_LIST_ELEM *p_srv = NULL; + tBT_UUID *p_uuid; + + UNUSED(p_data); + + p = (UINT8 *)(p_msg + 1) + L2CAP_MIN_OFFSET; + + p_srv = p_list->p_first; + + while (p_srv) { + p_rcb = GATT_GET_SR_REG_PTR(p_srv->i_sreg); + + if (p_rcb->in_use && + p_rcb->s_hdl >= s_hdl && + p_rcb->s_hdl <= e_hdl && + p_rcb->type == GATT_UUID_PRI_SERVICE) { + if ((p_uuid = gatts_get_service_uuid (p_rcb->p_db)) != NULL) { + if (op_code == GATT_REQ_READ_BY_GRP_TYPE) { + handle_len = 4 + p_uuid->len; + } + + /* get the length byte in the repsonse */ + if (p_msg->offset == 0) { + *p ++ = op_code + 1; + p_msg->len ++; + p_msg->offset = handle_len; + + if (op_code == GATT_REQ_READ_BY_GRP_TYPE) { + *p ++ = (UINT8)p_msg->offset; /* length byte */ + p_msg->len ++; + } + } + + if (p_msg->len + p_msg->offset <= p_tcb->payload_size && + handle_len == p_msg->offset) { + if (op_code != GATT_REQ_FIND_TYPE_VALUE || + gatt_uuid_compare(value, *p_uuid)) { + UINT16_TO_STREAM(p, p_rcb->s_hdl); + + if (p_list->p_last_primary == p_srv && + p_list->p_last_primary == p_list->p_last) { + GATT_TRACE_DEBUG("Use 0xFFFF for the last primary attribute"); + UINT16_TO_STREAM(p, 0xFFFF); /* see GATT ERRATA 4065, 4063, ATT ERRATA 4062 */ + } else { + UINT16_TO_STREAM(p, p_rcb->e_hdl); + } + + if (op_code == GATT_REQ_READ_BY_GRP_TYPE) { + gatt_build_uuid_to_stream(&p, *p_uuid); + } + + status = GATT_SUCCESS; + p_msg->len += p_msg->offset; + } + } else { + break; + } + } + } + p_srv = p_srv->p_next; + } + p_msg->offset = L2CAP_MIN_OFFSET; + + return status; +} + +/******************************************************************************* +** +** Function gatt_build_find_info_rsp +** +** Description fill the find information response information in the given +** buffer. +** +** Returns TRUE: if data filled successfully. +** FALSE: packet full, or format mismatch. +** +*******************************************************************************/ +static tGATT_STATUS gatt_build_find_info_rsp(tGATT_SR_REG *p_rcb, BT_HDR *p_msg, UINT16 *p_len, + UINT16 s_hdl, UINT16 e_hdl) +{ + tGATT_STATUS status = GATT_NOT_FOUND; + UINT8 *p; + UINT16 len = *p_len; + tGATT_ATTR16 *p_attr = NULL; + UINT8 info_pair_len[2] = {4, 18}; + + if (!p_rcb->p_db || !p_rcb->p_db->p_attr_list) { + return status; + } + + /* check the attribute database */ + p_attr = (tGATT_ATTR16 *) p_rcb->p_db->p_attr_list; + + p = (UINT8 *)(p_msg + 1) + L2CAP_MIN_OFFSET + p_msg->len; + + while (p_attr) { + if (p_attr->handle > e_hdl) { + break; + } + + if (p_attr->handle >= s_hdl) { + if (p_msg->offset == 0) { + p_msg->offset = (p_attr->uuid_type == GATT_ATTR_UUID_TYPE_16) ? GATT_INFO_TYPE_PAIR_16 : GATT_INFO_TYPE_PAIR_128; + } + + if (len >= info_pair_len[p_msg->offset - 1]) { + if (p_msg->offset == GATT_INFO_TYPE_PAIR_16 && p_attr->uuid_type == GATT_ATTR_UUID_TYPE_16) { + UINT16_TO_STREAM(p, p_attr->handle); + UINT16_TO_STREAM(p, p_attr->uuid); + } else if (p_msg->offset == GATT_INFO_TYPE_PAIR_128 && p_attr->uuid_type == GATT_ATTR_UUID_TYPE_128 ) { + UINT16_TO_STREAM(p, p_attr->handle); + ARRAY_TO_STREAM (p, ((tGATT_ATTR128 *) p_attr)->uuid, LEN_UUID_128); + } else if (p_msg->offset == GATT_INFO_TYPE_PAIR_128 && p_attr->uuid_type == GATT_ATTR_UUID_TYPE_32) { + UINT16_TO_STREAM(p, p_attr->handle); + gatt_convert_uuid32_to_uuid128(p, ((tGATT_ATTR32 *) p_attr)->uuid); + p += LEN_UUID_128; + } else { + GATT_TRACE_ERROR("format mismatch"); + status = GATT_NO_RESOURCES; + break; + /* format mismatch */ + } + p_msg->len += info_pair_len[p_msg->offset - 1]; + len -= info_pair_len[p_msg->offset - 1]; + status = GATT_SUCCESS; + + } else { + status = GATT_NO_RESOURCES; + break; + } + } + p_attr = (tGATT_ATTR16 *)p_attr->p_next; + } + + *p_len = len; + return status; +} + +/******************************************************************************* +** +** Function gatts_internal_read_by_type_req +** +** Description check to see if the ReadByType request can be handled internally. +** +** Returns void +** +*******************************************************************************/ +static tGATT_STATUS gatts_validate_packet_format(UINT8 op_code, UINT16 *p_len, + UINT8 **p_data, tBT_UUID *p_uuid_filter, + UINT16 *p_s_hdl, UINT16 *p_e_hdl) +{ + tGATT_STATUS reason = GATT_SUCCESS; + UINT16 uuid_len, s_hdl = 0, e_hdl = 0; + UINT16 len = *p_len; + UINT8 *p = *p_data; + + if (len >= 4) { + /* obtain starting handle, and ending handle */ + STREAM_TO_UINT16(s_hdl, p); + STREAM_TO_UINT16(e_hdl, p); + len -= 4; + + if (s_hdl > e_hdl || !GATT_HANDLE_IS_VALID(s_hdl) || !GATT_HANDLE_IS_VALID(e_hdl)) { + reason = GATT_INVALID_HANDLE; + } + /* for these PDUs, uuid filter must present */ + else if (op_code == GATT_REQ_READ_BY_GRP_TYPE || + op_code == GATT_REQ_FIND_TYPE_VALUE || + op_code == GATT_REQ_READ_BY_TYPE) { + if (len >= 2 && p_uuid_filter != NULL) { + uuid_len = (op_code == GATT_REQ_FIND_TYPE_VALUE) ? 2 : len; + + /* parse uuid now */ + if (gatt_parse_uuid_from_cmd (p_uuid_filter, uuid_len, &p) == FALSE || + p_uuid_filter->len == 0) { + GATT_TRACE_DEBUG("UUID filter does not exsit"); + reason = GATT_INVALID_PDU; + } else { + len -= p_uuid_filter->len; + } + } else { + reason = GATT_INVALID_PDU; + } + } + } else { + reason = GATT_INVALID_PDU; + } + + *p_data = p; + *p_len = len; + *p_s_hdl = s_hdl; + *p_e_hdl = e_hdl; + + return reason; +} + +/******************************************************************************* +** +** Function gatts_process_primary_service_req +** +** Description process ReadByGroupType/ReadByTypeValue request, for discover +** all primary services or discover primary service by UUID request. +** +** Returns void +** +*******************************************************************************/ +void gatts_process_primary_service_req(tGATT_TCB *p_tcb, UINT8 op_code, UINT16 len, UINT8 *p_data) +{ + UINT8 reason = GATT_INVALID_PDU; + UINT16 s_hdl = 0, e_hdl = 0; + tBT_UUID uuid, value, primary_service = {LEN_UUID_16, {GATT_UUID_PRI_SERVICE}}; + BT_HDR *p_msg = NULL; + UINT16 msg_len = (UINT16)(sizeof(BT_HDR) + p_tcb->payload_size + L2CAP_MIN_OFFSET); + + memset (&value, 0, sizeof(tBT_UUID)); + reason = gatts_validate_packet_format(op_code, &len, &p_data, &uuid, &s_hdl, &e_hdl); + + if (reason == GATT_SUCCESS) { + if (gatt_uuid_compare(uuid, primary_service)) { + if (op_code == GATT_REQ_FIND_TYPE_VALUE) { + if (gatt_parse_uuid_from_cmd(&value, len, &p_data) == FALSE) { + reason = GATT_INVALID_PDU; + } + } + + if (reason == GATT_SUCCESS) { + if ((p_msg = (BT_HDR *)osi_calloc(msg_len)) == NULL) { + GATT_TRACE_ERROR("gatts_process_primary_service_req failed. no resources."); + reason = GATT_NO_RESOURCES; + } else { + reason = gatt_build_primary_service_rsp (p_msg, p_tcb, op_code, s_hdl, e_hdl, p_data, value); + } + } + } else { + if (op_code == GATT_REQ_READ_BY_GRP_TYPE) { + reason = GATT_UNSUPPORT_GRP_TYPE; + GATT_TRACE_DEBUG("unexpected ReadByGrpType Group: 0x%04x", uuid.uu.uuid16); + } else { + /* we do not support ReadByTypeValue with any non-primamry_service type */ + reason = GATT_NOT_FOUND; + GATT_TRACE_DEBUG("unexpected ReadByTypeValue type: 0x%04x", uuid.uu.uuid16); + } + } + } + + if (reason != GATT_SUCCESS) { + if (p_msg) { + osi_free(p_msg); + } + gatt_send_error_rsp (p_tcb, reason, op_code, s_hdl, FALSE); + } else { + attp_send_sr_msg(p_tcb, p_msg); + } + +} + +/******************************************************************************* +** +** Function gatts_process_find_info +** +** Description process find information request, for discover character +** descriptors. +** +** Returns void +** +*******************************************************************************/ +static void gatts_process_find_info(tGATT_TCB *p_tcb, UINT8 op_code, UINT16 len, UINT8 *p_data) +{ + UINT8 reason = GATT_INVALID_PDU, *p; + UINT16 s_hdl = 0, e_hdl = 0, buf_len; + BT_HDR *p_msg = NULL; + tGATT_SR_REG *p_rcb; + tGATT_SRV_LIST_INFO *p_list = &gatt_cb.srv_list_info; + tGATT_SRV_LIST_ELEM *p_srv = NULL; + + reason = gatts_validate_packet_format(op_code, &len, &p_data, NULL, &s_hdl, &e_hdl); + + if (reason == GATT_SUCCESS) { + buf_len = (UINT16)(sizeof(BT_HDR) + p_tcb->payload_size + L2CAP_MIN_OFFSET); + + if ((p_msg = (BT_HDR *)osi_calloc(buf_len)) == NULL) { + reason = GATT_NO_RESOURCES; + } else { + reason = GATT_NOT_FOUND; + + p = (UINT8 *)(p_msg + 1) + L2CAP_MIN_OFFSET; + *p ++ = op_code + 1; + p_msg->len = 2; + + buf_len = p_tcb->payload_size - 2; + + p_srv = p_list->p_first; + + while (p_srv) { + p_rcb = GATT_GET_SR_REG_PTR(p_srv->i_sreg); + + if (p_rcb->in_use && + !(p_rcb->s_hdl > e_hdl || + p_rcb->e_hdl < s_hdl)) { + reason = gatt_build_find_info_rsp(p_rcb, p_msg, &buf_len, s_hdl, e_hdl); + if (reason == GATT_NO_RESOURCES) { + reason = GATT_SUCCESS; + break; + } + } + p_srv = p_srv->p_next; + } + *p = (UINT8)p_msg->offset; + + p_msg->offset = L2CAP_MIN_OFFSET; + } + } + + if (reason != GATT_SUCCESS) { + if (p_msg) { + osi_free(p_msg); + } + gatt_send_error_rsp (p_tcb, reason, op_code, s_hdl, FALSE); + } else { + attp_send_sr_msg(p_tcb, p_msg); + } + +} + +/******************************************************************************* +** +** Function gatts_process_mtu_req +** +** Description This function is called to process excahnge MTU request. +** Only used on LE. +** +** Returns void +** +*******************************************************************************/ +static void gatts_process_mtu_req (tGATT_TCB *p_tcb, UINT16 len, UINT8 *p_data) +{ + UINT16 mtu = 0; + UINT8 *p = p_data, i; + BT_HDR *p_buf; + UINT16 conn_id; + + /* BR/EDR conenction, send error response */ + if (p_tcb->att_lcid != L2CAP_ATT_CID) { + gatt_send_error_rsp (p_tcb, GATT_REQ_NOT_SUPPORTED, GATT_REQ_MTU, 0, FALSE); + } else if (len < GATT_MTU_REQ_MIN_LEN) { + GATT_TRACE_ERROR("invalid MTU request PDU received.\n"); + gatt_send_error_rsp (p_tcb, GATT_INVALID_PDU, GATT_REQ_MTU, 0, FALSE); + } else { + STREAM_TO_UINT16 (mtu, p); + /* mtu must be greater than default MTU which is 23/48 */ + if (mtu < GATT_DEF_BLE_MTU_SIZE) { + p_tcb->payload_size = GATT_DEF_BLE_MTU_SIZE; + } else if (mtu > gatt_default.local_mtu) { + p_tcb->payload_size = gatt_default.local_mtu; + } else { + p_tcb->payload_size = mtu; + } + + /* host will set packet data length to 251 automatically if remote device support set packet data length, + so l2cble_set_fixed_channel_tx_data_length() is not necessary. + l2cble_set_fixed_channel_tx_data_length(p_tcb->peer_bda, L2CAP_ATT_CID, p_tcb->payload_size); + */ + + if ((p_buf = attp_build_sr_msg(p_tcb, GATT_RSP_MTU, (tGATT_SR_MSG *) &p_tcb->payload_size)) != NULL) { + attp_send_sr_msg (p_tcb, p_buf); + + /* Notify all registered application with new MTU size. Us a transaction ID */ + /* of 0, as no response is allowed from applcations */ + + for (i = 0; i < GATT_MAX_APPS; i ++) { + if (gatt_cb.cl_rcb[i].in_use ) { + conn_id = GATT_CREATE_CONN_ID(p_tcb->tcb_idx, gatt_cb.cl_rcb[i].gatt_if); + gatt_sr_send_req_callback(conn_id, 0, GATTS_REQ_TYPE_MTU, + (tGATTS_DATA *)&p_tcb->payload_size); + } + } + + } + } +} + +/******************************************************************************* +** +** Function gatts_process_read_by_type_req +** +** Description process Read By type request. +** This PDU can be used to perform: +** - read characteristic value +** - read characteristic descriptor value +** - discover characteristic +** - discover characteristic by UUID +** - relationship discovery +** +** Returns void +** +*******************************************************************************/ +void gatts_process_read_by_type_req(tGATT_TCB *p_tcb, UINT8 op_code, UINT16 len, UINT8 *p_data) +{ + tBT_UUID uuid; + tGATT_SR_REG *p_rcb; + UINT16 msg_len = (UINT16)(sizeof(BT_HDR) + p_tcb->payload_size + L2CAP_MIN_OFFSET), + buf_len, + s_hdl, e_hdl, err_hdl = 0; + BT_HDR *p_msg = NULL; + tGATT_STATUS reason, ret; + UINT8 *p; + UINT8 sec_flag, key_size; + tGATT_SRV_LIST_INFO *p_list = &gatt_cb.srv_list_info; + tGATT_SRV_LIST_ELEM *p_srv = NULL; + + reason = gatts_validate_packet_format(op_code, &len, &p_data, &uuid, &s_hdl, &e_hdl); + GATT_TRACE_DEBUG("%s, op_code =%x, len = %x\n", __func__, op_code, len); +#if GATT_CONFORMANCE_TESTING == TRUE + if (gatt_cb.enable_err_rsp && gatt_cb.req_op_code == op_code) { + GATT_TRACE_DEBUG("Conformance tst: forced err rsp for ReadByType: error status=%d\n", gatt_cb.err_status); + + gatt_send_error_rsp (p_tcb, gatt_cb.err_status, gatt_cb.req_op_code, s_hdl, FALSE); + + return; + } +#endif + + if (reason == GATT_SUCCESS) { + if ((p_msg = (BT_HDR *)osi_calloc(msg_len)) == NULL) { + GATT_TRACE_ERROR("gatts_process_find_info failed. no resources.\n"); + + reason = GATT_NO_RESOURCES; + } else { + p = (UINT8 *)(p_msg + 1) + L2CAP_MIN_OFFSET; + + *p ++ = op_code + 1; + /* reserve length byte */ + p_msg->len = 2; + buf_len = p_tcb->payload_size - 2; + + reason = GATT_NOT_FOUND; + + p_srv = p_list->p_first; + + while (p_srv) { + p_rcb = GATT_GET_SR_REG_PTR(p_srv->i_sreg); + + if (p_rcb->in_use && + !(p_rcb->s_hdl > e_hdl || + p_rcb->e_hdl < s_hdl)) { + gatt_sr_get_sec_info(p_tcb->peer_bda, + p_tcb->transport, + &sec_flag, + &key_size); + + ret = gatts_db_read_attr_value_by_type(p_tcb, + p_rcb->p_db, + op_code, + p_msg, + s_hdl, + e_hdl, + uuid, + &buf_len, + sec_flag, + key_size, + 0, + &err_hdl); + if (ret != GATT_NOT_FOUND) { + reason = ret; + + if (ret == GATT_NO_RESOURCES) { + reason = GATT_SUCCESS; + } + } + if (ret != GATT_SUCCESS && ret != GATT_NOT_FOUND) { + s_hdl = err_hdl; + break; + } + } + p_srv = p_srv->p_next; + } + *p = (UINT8)p_msg->offset; + p_msg->offset = L2CAP_MIN_OFFSET; + } + } + if (reason != GATT_SUCCESS && reason != GATT_STACK_RSP) { + if (p_msg) { + osi_free(p_msg); + } + + /* in theroy BUSY is not possible(should already been checked), protected check */ + if (reason != GATT_PENDING && reason != GATT_BUSY) { + gatt_send_error_rsp (p_tcb, reason, op_code, s_hdl, FALSE); + } + } else { + attp_send_sr_msg(p_tcb, p_msg); + gatt_dequeue_sr_cmd(p_tcb); + } + +} + +/******************************************************************************* +** +** Function gatts_process_write_req +** +** Description This function is called to process the write request +** from client. +** +** Returns void +** +*******************************************************************************/ +void gatts_process_write_req (tGATT_TCB *p_tcb, UINT8 i_rcb, UINT16 handle, + UINT8 op_code, UINT16 len, UINT8 *p_data) +{ + tGATTS_DATA sr_data; + UINT32 trans_id; + tGATT_STATUS status; + UINT8 sec_flag, key_size, *p = p_data; + tGATT_SR_REG *p_sreg; + UINT16 conn_id, offset = 0; + + memset(&sr_data, 0, sizeof(tGATTS_DATA)); + sr_data.write_req.need_rsp = FALSE; + + switch (op_code) { + case GATT_SIGN_CMD_WRITE: + if (op_code == GATT_SIGN_CMD_WRITE) { + GATT_TRACE_DEBUG("Write CMD with data signing" ); + len -= GATT_AUTH_SIGN_LEN; + } + /* fall through */ + case GATT_CMD_WRITE: + case GATT_REQ_WRITE: + sr_data.write_req.handle = handle; + sr_data.write_req.len = len; + if (len != 0 && p != NULL) { + memcpy (sr_data.write_req.value, p, len); + } + break; + } + + gatt_sr_get_sec_info(p_tcb->peer_bda, + p_tcb->transport, + &sec_flag, + &key_size); + + status = gatts_write_attr_perm_check (gatt_cb.sr_reg[i_rcb].p_db, + op_code, + handle, + sr_data.write_req.offset, + p, + len, + sec_flag, + key_size); + + if (status == GATT_SUCCESS) { + if ((trans_id = gatt_sr_enqueue_cmd(p_tcb, op_code, handle)) != 0) { + p_sreg = &gatt_cb.sr_reg[i_rcb]; + conn_id = GATT_CREATE_CONN_ID(p_tcb->tcb_idx, p_sreg->gatt_if); + status = gatts_write_attr_value_by_handle(gatt_cb.sr_reg[i_rcb].p_db, + handle, offset, p, len); + if((op_code == GATT_REQ_WRITE) && (status == GATT_APP_RSP)){ + sr_data.write_req.need_rsp = TRUE; + status = GATT_PENDING; + } + + gatt_sr_send_req_callback(conn_id, + trans_id, + GATTS_REQ_TYPE_WRITE, + &sr_data); + } else { + GATT_TRACE_ERROR("Error in %s, line=%d, max pending command, send error\n", __func__, __LINE__); + status = GATT_BUSY; /* max pending command, application error */ + } + } + + /* response should be sent only for write_request */ + if ((op_code == GATT_REQ_WRITE) && (sr_data.write_req.need_rsp == FALSE)){ + if (status == GATT_SUCCESS){ + tGATT_WRITE_REQ_RSP gatt_write_req_rsp; + gatt_write_req_rsp.op_code = GATT_RSP_WRITE; + gatt_send_packet(p_tcb, (UINT8 *)(&gatt_write_req_rsp), sizeof(gatt_write_req_rsp)); + gatt_dequeue_sr_cmd(p_tcb); + } else if (status != GATT_PENDING){ + /* note: in case of GATT_BUSY, will respond this application error to remote device */ + gatt_send_error_rsp (p_tcb, status, op_code, handle, TRUE); + } + } + + return; +} + + +/******************************************************************************* + ** + ** Function gatts_attr_process_preapre_write + ** + ** Description This function is called to process the prepare write request + ** from client. + ** + ** Returns void + ** + *******************************************************************************/ +void gatt_attr_process_prepare_write (tGATT_TCB *p_tcb, UINT8 i_rcb, UINT16 handle, + UINT8 op_code, UINT16 len, UINT8 *p_data) +{ + tGATT_STATUS status; + tGATT_PREPARE_WRITE_QUEUE_DATA * queue_data = NULL; + tGATT_ATTR16 *p_attr; + tGATT_ATTR16 *p_attr_temp; + tGATTS_DATA sr_data; + UINT32 trans_id = 0; + UINT8 sec_flag, key_size, *p = p_data; + tGATT_SR_REG *p_sreg; + UINT16 conn_id, offset = 0; + tGATT_SVC_DB *p_db; + BOOLEAN is_need_prepare_write_rsp = FALSE; + BOOLEAN is_need_queue_data = FALSE; + tGATT_PREPARE_WRITE_RECORD *prepare_record = NULL; + memset(&sr_data, 0, sizeof(tGATTS_DATA)); + + if (len < 2) { + GATT_TRACE_ERROR("%s: Prepare write request was invalid - missing offset, sending error response", __func__); + gatt_send_error_rsp(p_tcb, GATT_INVALID_PDU, op_code, handle, FALSE); + return; + } + //get offset from p_data + STREAM_TO_UINT16(offset, p); + len -= 2; + p_sreg = &gatt_cb.sr_reg[i_rcb]; + conn_id = GATT_CREATE_CONN_ID(p_tcb->tcb_idx, p_sreg->gatt_if); + //prepare_record = &(prepare_write_record); + prepare_record = &(p_tcb->prepare_write_record); + + gatt_sr_get_sec_info(p_tcb->peer_bda, + p_tcb->transport, + &sec_flag, + &key_size); + + status = gatts_write_attr_perm_check (gatt_cb.sr_reg[i_rcb].p_db, + op_code, + handle, + sr_data.write_req.offset, + p, + len, + sec_flag, + key_size); + + if (status == GATT_SUCCESS){ + if ((trans_id = gatt_sr_enqueue_cmd(p_tcb, op_code, handle)) != 0) { + p_db = gatt_cb.sr_reg[i_rcb].p_db; + if (p_db && p_db->p_attr_list) { + p_attr = (tGATT_ATTR16 *)p_db->p_attr_list; + while (p_attr && handle >= p_attr->handle) { + if (p_attr->handle == handle ) { + p_attr_temp = p_attr; + if (p_attr->control.auto_rsp == GATT_RSP_BY_APP) { + status = GATT_APP_RSP; + } else if (p_attr->p_value != NULL && + offset > p_attr->p_value->attr_val.attr_max_len) { + status = GATT_INVALID_OFFSET; + is_need_prepare_write_rsp = TRUE; + is_need_queue_data = TRUE; + } else if (p_attr->p_value != NULL && + ((offset + len) > p_attr->p_value->attr_val.attr_max_len)){ + status = GATT_INVALID_ATTR_LEN; + is_need_prepare_write_rsp = TRUE; + is_need_queue_data = TRUE; + } else if (p_attr->p_value == NULL) { + GATT_TRACE_ERROR("Error in %s, attribute of handle 0x%x not allocate value buffer\n", + __func__, handle); + status = GATT_UNKNOWN_ERROR; + } else { + //valid prepare write request, need to send response and queue the data + //status: GATT_SUCCESS + is_need_prepare_write_rsp = TRUE; + is_need_queue_data = TRUE; + } + } + p_attr = (tGATT_ATTR16 *)p_attr->p_next; + } + } + } else{ + status = GATT_UNKNOWN_ERROR; + GATT_TRACE_ERROR("Error in %s, Line %d: GATT BUSY\n", __func__, __LINE__); + } + } + + if (is_need_queue_data){ + queue_data = (tGATT_PREPARE_WRITE_QUEUE_DATA *)osi_malloc(len + sizeof(tGATT_PREPARE_WRITE_QUEUE_DATA)); + if (queue_data == NULL){ + status = GATT_PREPARE_Q_FULL; + } else { + queue_data->p_attr = p_attr_temp; + queue_data->len = len; + queue_data->handle = handle; + queue_data->offset = offset; + memcpy(queue_data->value, p, len); + if (prepare_record->queue == NULL) { + prepare_record->queue = fixed_queue_new(QUEUE_SIZE_MAX); + } + fixed_queue_enqueue(prepare_record->queue, queue_data, FIXED_QUEUE_MAX_TIMEOUT); + } + } + + if (is_need_prepare_write_rsp){ + //send prepare write response + if (queue_data != NULL){ + queue_data->op_code = op_code + 1; + //5: op_code 1 + handle 2 + offset 2 + tGATT_STATUS rsp_send_status = gatt_send_packet(p_tcb, &(queue_data->op_code), queue_data->len + 5); + gatt_sr_update_prep_cnt(p_tcb, p_sreg->gatt_if, TRUE, FALSE); + gatt_dequeue_sr_cmd(p_tcb); + + if (rsp_send_status != GATT_SUCCESS){ + GATT_TRACE_ERROR("Error in %s, line=%d, fail to send prepare_write_rsp, status=0x%x\n", + __func__, __LINE__, rsp_send_status); + } + } else{ + GATT_TRACE_ERROR("Error in %s, line=%d, queue_data should not be NULL here, fail to send prepare_write_rsp\n", + __func__, __LINE__); + } + } + + if ((status == GATT_APP_RSP) || (is_need_prepare_write_rsp)){ + prepare_record->total_num++; + memset(&sr_data, 0, sizeof(sr_data)); + sr_data.write_req.is_prep = TRUE; + sr_data.write_req.handle = handle; + sr_data.write_req.offset = offset; + sr_data.write_req.len = len; + sr_data.write_req.need_rsp = (status == GATT_APP_RSP) ? TRUE : FALSE; + memcpy(sr_data.write_req.value, p, len); + gatt_sr_send_req_callback(conn_id, trans_id, GATTS_REQ_TYPE_WRITE, &sr_data); + } else{ + gatt_send_error_rsp(p_tcb, status, GATT_REQ_PREPARE_WRITE, handle, TRUE); + } + + if ((prepare_record->error_code_app == GATT_SUCCESS) + // update prepare write status for excute write request + && (status == GATT_INVALID_OFFSET || status == GATT_INVALID_ATTR_LEN || status == GATT_REQ_NOT_SUPPORTED)) { + prepare_record->error_code_app = status; + } + +} + +/******************************************************************************* + ** + ** Function gatts_process_read_req + ** + ** Description This function is called to process the read request + ** from client. + ** + ** Returns void + ** + *******************************************************************************/ +static void gatts_process_read_req(tGATT_TCB *p_tcb, tGATT_SR_REG *p_rcb, UINT8 op_code, + UINT16 handle, UINT16 len, UINT8 *p_data) +{ + UINT16 buf_len = (UINT16)(sizeof(BT_HDR) + p_tcb->payload_size + L2CAP_MIN_OFFSET); + tGATT_STATUS reason; + BT_HDR *p_msg = NULL; + UINT8 sec_flag, key_size, *p; + UINT16 offset = 0, value_len = 0; + + UNUSED (len); + if ((p_msg = (BT_HDR *)osi_calloc(buf_len)) == NULL) { + GATT_TRACE_ERROR("gatts_process_find_info failed. no resources.\n"); + + reason = GATT_NO_RESOURCES; + } else { + if (op_code == GATT_REQ_READ_BLOB) { + STREAM_TO_UINT16(offset, p_data); + } + + p = (UINT8 *)(p_msg + 1) + L2CAP_MIN_OFFSET; + *p ++ = op_code + 1; + p_msg->len = 1; + buf_len = p_tcb->payload_size - 1; + + gatt_sr_get_sec_info(p_tcb->peer_bda, + p_tcb->transport, + &sec_flag, + &key_size); + + reason = gatts_read_attr_value_by_handle(p_tcb, + p_rcb->p_db, + op_code, + handle, + offset, + p, + &value_len, + buf_len, + sec_flag, + key_size, + 0); + + p_msg->len += value_len; + } + + + if (reason != GATT_SUCCESS && reason != GATT_PENDING && reason != GATT_STACK_RSP) { + if (p_msg) { + osi_free(p_msg); + } + + /* in theroy BUSY is not possible(should already been checked), protected check */ + if (reason != GATT_BUSY) { + gatt_send_error_rsp (p_tcb, reason, op_code, handle, FALSE); + gatt_dequeue_sr_cmd(p_tcb); + } + } else if (reason == GATT_SUCCESS || reason == GATT_STACK_RSP) { + attp_send_sr_msg(p_tcb, p_msg); + gatt_dequeue_sr_cmd(p_tcb); + } else { + if (p_msg) { + osi_free(p_msg); + } + } + +} + +/******************************************************************************* +** +** Function gatts_process_attribute_req +** +** Description This function is called to process the per attribute handle request +** from client. +** +** Returns void +** +*******************************************************************************/ +void gatts_process_attribute_req (tGATT_TCB *p_tcb, UINT8 op_code, + UINT16 len, UINT8 *p_data) +{ + UINT16 handle = 0; + UINT8 *p = p_data, i; + tGATT_SR_REG *p_rcb = gatt_cb.sr_reg; + tGATT_STATUS status = GATT_INVALID_HANDLE; + tGATT_ATTR16 *p_attr; + + if (len < 2) { + GATT_TRACE_ERROR("Illegal PDU length, discard request\n"); + status = GATT_INVALID_PDU; + } else { + STREAM_TO_UINT16(handle, p); + len -= 2; + } + +#if GATT_CONFORMANCE_TESTING == TRUE + gatt_cb.handle = handle; + if (gatt_cb.enable_err_rsp && gatt_cb.req_op_code == op_code) { + GATT_TRACE_DEBUG("Conformance tst: forced err rsp: error status=%d\n", gatt_cb.err_status); + + gatt_send_error_rsp (p_tcb, gatt_cb.err_status, gatt_cb.req_op_code, handle, FALSE); + + return; + } +#endif + + if (GATT_HANDLE_IS_VALID(handle)) { + for (i = 0; i < GATT_MAX_SR_PROFILES; i ++, p_rcb ++) { + if (p_rcb->in_use && p_rcb->s_hdl <= handle && p_rcb->e_hdl >= handle) { + p_attr = (tGATT_ATTR16 *)p_rcb->p_db->p_attr_list; + + while (p_attr) { + if (p_attr->handle == handle) { + switch (op_code) { + case GATT_REQ_READ: /* read char/char descriptor value */ + case GATT_REQ_READ_BLOB: + gatts_process_read_req(p_tcb, p_rcb, op_code, handle, len, p); + break; + + case GATT_REQ_WRITE: /* write char/char descriptor value */ + case GATT_CMD_WRITE: + case GATT_SIGN_CMD_WRITE: + gatts_process_write_req(p_tcb, i, handle, op_code, len, p); + break; + + case GATT_REQ_PREPARE_WRITE: + gatt_attr_process_prepare_write (p_tcb, i, handle, op_code, len, p); + default: + break; + } + status = GATT_SUCCESS; + break; + } + p_attr = (tGATT_ATTR16 *)p_attr->p_next; + } + break; + } + } + } + + if (status != GATT_SUCCESS && op_code != GATT_CMD_WRITE && op_code != GATT_SIGN_CMD_WRITE) { + gatt_send_error_rsp (p_tcb, status, op_code, handle, FALSE); + } +} + +/******************************************************************************* +** +** Function gatts_proc_srv_chg_ind_ack +** +** Description This function process the service changed indicaiton ACK +** +** Returns void +** +*******************************************************************************/ +static void gatts_proc_srv_chg_ind_ack(tGATT_TCB *p_tcb ) +{ + tGATTS_SRV_CHG_REQ req; + tGATTS_SRV_CHG *p_buf = NULL; + + GATT_TRACE_DEBUG("gatts_proc_srv_chg_ind_ack"); + + if ((p_buf = gatt_is_bda_in_the_srv_chg_clt_list(p_tcb->peer_bda)) != NULL) { + GATT_TRACE_DEBUG("NV update set srv chg = FALSE"); + p_buf->srv_changed = FALSE; + memcpy(&req.srv_chg, p_buf, sizeof(tGATTS_SRV_CHG)); + if (gatt_cb.cb_info.p_srv_chg_callback) { + (*gatt_cb.cb_info.p_srv_chg_callback)(GATTS_SRV_CHG_CMD_UPDATE_CLIENT, &req, NULL); + } + } +} + +/******************************************************************************* +** +** Function gatts_chk_pending_ind +** +** Description This function check any pending indication needs to be sent if +** there is a pending indication then sent the indication +** +** Returns void +** +*******************************************************************************/ +static void gatts_chk_pending_ind(tGATT_TCB *p_tcb ) +{ +#if (GATTS_INCLUDED == TRUE) + tGATT_VALUE *p_buf = (tGATT_VALUE *)fixed_queue_try_peek_first(p_tcb->pending_ind_q); + GATT_TRACE_DEBUG("gatts_chk_pending_ind"); + + if (p_buf ) { + GATTS_HandleValueIndication (p_buf->conn_id, + p_buf->handle, + p_buf->len, + p_buf->value); + osi_free(fixed_queue_try_remove_from_queue(p_tcb->pending_ind_q, + p_buf)); + } +#endif ///GATTS_INCLUDED == TRUE +} + +/******************************************************************************* +** +** Function gatts_proc_ind_ack +** +** Description This function process the Indication ack +** +** Returns TRUE continue to process the indication ack by the aaplication +** if the ACk is not a Service Changed Indication Ack +** +*******************************************************************************/ +static BOOLEAN gatts_proc_ind_ack(tGATT_TCB *p_tcb, UINT16 ack_handle) +{ + BOOLEAN continue_processing = TRUE; + + GATT_TRACE_DEBUG ("gatts_proc_ind_ack ack handle=%d", ack_handle); + + if (ack_handle == gatt_cb.handle_of_h_r) { + gatts_proc_srv_chg_ind_ack(p_tcb); + /* there is no need to inform the application since srv chg is handled internally by GATT */ + continue_processing = FALSE; +#if GATTS_ROBUST_CACHING_ENABLED + /* after receiving ack of svc_chg_ind, reset client status */ + gatt_sr_update_cl_status(p_tcb, true); +#endif /* GATTS_ROBUST_CACHING_ENABLED */ + } + + gatts_chk_pending_ind(p_tcb); + return continue_processing; +} + +/******************************************************************************* +** +** Function gatts_process_value_conf +** +** Description This function is called to process the handle value confirmation. +** +** Returns void +** +*******************************************************************************/ +void gatts_process_value_conf(tGATT_TCB *p_tcb, UINT8 op_code) +{ + UINT16 handle = p_tcb->indicate_handle; + UINT32 trans_id; + UINT8 i; + tGATT_SR_REG *p_rcb = gatt_cb.sr_reg; + BOOLEAN continue_processing; + UINT16 conn_id; + + btu_stop_timer (&p_tcb->conf_timer_ent); + if (GATT_HANDLE_IS_VALID(handle)) { + p_tcb->indicate_handle = 0; + continue_processing = gatts_proc_ind_ack(p_tcb, handle); + + if (continue_processing) { + for (i = 0; i < GATT_MAX_SR_PROFILES; i ++, p_rcb ++) { + if (p_rcb->in_use && p_rcb->s_hdl <= handle && p_rcb->e_hdl >= handle) { + trans_id = gatt_sr_enqueue_cmd(p_tcb, op_code, handle); + conn_id = GATT_CREATE_CONN_ID(p_tcb->tcb_idx, p_rcb->gatt_if); + tGATTS_DATA p_data = {0}; + p_data.handle = handle; + gatt_sr_send_req_callback(conn_id, + trans_id, GATTS_REQ_TYPE_CONF, &p_data); + } + } + } + } else { + GATT_TRACE_ERROR("unexpected handle value confirmation"); + } +} + +#if GATTS_ROBUST_CACHING_ENABLED +static BOOLEAN gatts_handle_db_out_of_sync(tGATT_TCB *p_tcb, UINT8 op_code, + UINT16 len, UINT8 *p_data) +{ + if (gatt_sr_is_cl_change_aware(p_tcb)) { + return false; + } + + bool should_ignore = true; + bool should_rsp = true; + + switch (op_code) { + case GATT_REQ_READ_BY_TYPE: + { + tBT_UUID uuid; + UINT16 s_hdl = 0; + UINT16 e_hdl = 0; + UINT16 db_hash_handle = gatt_cb.handle_of_database_hash; + tGATT_STATUS reason = gatts_validate_packet_format(op_code, &len, &p_data, &uuid, &s_hdl, &e_hdl); + if (reason == GATT_SUCCESS && + (s_hdl <= db_hash_handle && db_hash_handle <= e_hdl) && + (uuid.uu.uuid16 == GATT_UUID_GATT_DATABASE_HASH)) { + should_ignore = false; + } + break; + } + case GATT_REQ_READ: + // for pts don't process read request + #if 0 + { + UINT16 handle = 0; + UINT8 *p = p_data; + tGATT_STATUS status = GATT_SUCCESS; + + if (len < 2) { + status = GATT_INVALID_PDU; + } else { + STREAM_TO_UINT16(handle, p); + len -= 2; + } + + if (status == GATT_SUCCESS && handle == gatt_cb.handle_of_database_hash) { + should_ignore = false; + } + break; + } + #endif + case GATT_REQ_READ_BY_GRP_TYPE: + case GATT_REQ_FIND_TYPE_VALUE: + case GATT_REQ_FIND_INFO: + case GATT_REQ_READ_BLOB: + case GATT_REQ_READ_MULTI: + case GATT_REQ_READ_MULTI_VAR: + case GATT_REQ_WRITE: + case GATT_REQ_PREPARE_WRITE: + break; + case GATT_CMD_WRITE: + case GATT_SIGN_CMD_WRITE: + should_rsp = false; + break; + case GATT_REQ_MTU: + case GATT_REQ_EXEC_WRITE: + case GATT_HANDLE_VALUE_CONF: + default: + should_ignore = false; + break; + } + + if (should_ignore) { + if (should_rsp) { + gatt_send_error_rsp(p_tcb, GATT_DATABASE_OUT_OF_SYNC, op_code, 0x0000, false); + } + + GATT_TRACE_ERROR("database out of sync op_code %x, should_rsp %d", op_code, should_rsp); + gatt_sr_update_cl_status(p_tcb, should_rsp); + } + + return should_ignore; +} + +#endif /* GATTS_ROBUST_CACHING_ENABLED */ +/******************************************************************************* +** +** Function gatt_server_handle_client_req +** +** Description This function is called to handle the client requests to +** server. +** +** +** Returns void +** +*******************************************************************************/ +void gatt_server_handle_client_req (tGATT_TCB *p_tcb, UINT8 op_code, + UINT16 len, UINT8 *p_data) +{ + /* there is pending command, discard this one */ + if (!gatt_sr_cmd_empty(p_tcb) && op_code != GATT_HANDLE_VALUE_CONF) { + return; + } + + /* the size of the message may not be bigger than the local max PDU size*/ + /* The message has to be smaller than the agreed MTU, len does not include op code */ + if (len >= p_tcb->payload_size) { + GATT_TRACE_ERROR("server receive invalid PDU size:%d pdu size:%d", len + 1, p_tcb->payload_size ); + /* for invalid request expecting response, send it now */ + if (op_code != GATT_CMD_WRITE && + op_code != GATT_SIGN_CMD_WRITE && + op_code != GATT_HANDLE_VALUE_CONF) { + gatt_send_error_rsp (p_tcb, GATT_INVALID_PDU, op_code, 0, FALSE); + } + /* otherwise, ignore the pkt */ + } else { +#if GATTS_ROBUST_CACHING_ENABLED + // handle database out of sync + if (gatts_handle_db_out_of_sync(p_tcb, op_code, len, p_data)) { + return; + } +#endif /* GATTS_ROBUST_CACHING_ENABLED */ + switch (op_code) { + case GATT_REQ_READ_BY_GRP_TYPE: /* discover primary services */ + case GATT_REQ_FIND_TYPE_VALUE: /* discover service by UUID */ + gatts_process_primary_service_req (p_tcb, op_code, len, p_data); + break; + + case GATT_REQ_FIND_INFO: /* discover char descrptor */ + gatts_process_find_info(p_tcb, op_code, len, p_data); + break; + + case GATT_REQ_READ_BY_TYPE: /* read characteristic value, char descriptor value */ + /* discover characteristic, discover char by UUID */ + gatts_process_read_by_type_req(p_tcb, op_code, len, p_data); + break; + + + case GATT_REQ_READ: /* read char/char descriptor value */ + case GATT_REQ_READ_BLOB: + case GATT_REQ_WRITE: /* write char/char descriptor value */ + case GATT_CMD_WRITE: + case GATT_SIGN_CMD_WRITE: + case GATT_REQ_PREPARE_WRITE: + gatts_process_attribute_req (p_tcb, op_code, len, p_data); + break; + + case GATT_HANDLE_VALUE_CONF: + gatts_process_value_conf (p_tcb, op_code); + break; + + case GATT_REQ_MTU: + gatts_process_mtu_req (p_tcb, len, p_data); + break; + + case GATT_REQ_EXEC_WRITE: + gatt_process_exec_write_req (p_tcb, op_code, len, p_data); + break; + + case GATT_REQ_READ_MULTI: + case GATT_REQ_READ_MULTI_VAR: + gatt_process_read_multi_req (p_tcb, op_code, len, p_data); + break; + + default: + break; + } + } +} + +#endif /* BLE_INCLUDED == TRUE && GATTS_INCLUDED == TRUE */ diff --git a/lib/bt/host/bluedroid/stack/gatt/gatt_sr_hash.c b/lib/bt/host/bluedroid/stack/gatt/gatt_sr_hash.c new file mode 100644 index 00000000..79d09d7a --- /dev/null +++ b/lib/bt/host/bluedroid/stack/gatt/gatt_sr_hash.c @@ -0,0 +1,256 @@ +#include "common/bt_target.h" +#include "osi/allocator.h" + +#include +#include "gatt_int.h" +#include "stack/l2c_api.h" +#include "l2c_int.h" +#include "smp_int.h" + +#if (BLE_INCLUDED == TRUE && GATTS_INCLUDED == TRUE) + +const char *const gatt_attr_name[] = { + "primary service", + "secondary service", + "included service", + "characteristic", +}; + +const char *const gatt_char_desc_name[] = { + "characteristic extended properties", + "characteristic user description", + "client characteristic configuration", + "server characteristic configuration", + "characteristic presentation format", + "characteristic aggregate format", +}; + +static const char *gatt_get_attr_name(UINT16 uuid) +{ + if (uuid >= GATT_UUID_PRI_SERVICE && uuid <= GATT_UUID_CHAR_DECLARE) { + return gatt_attr_name[uuid - GATT_UUID_PRI_SERVICE]; + } + + if (uuid >= GATT_UUID_CHAR_EXT_PROP && uuid <= GATT_UUID_CHAR_AGG_FORMAT) { + return gatt_char_desc_name[uuid - GATT_UUID_CHAR_EXT_PROP]; + } + + return "Unknown Attribute"; +} + +static void attr_uuid_to_bt_uuid(void *p_attr, tBT_UUID *p_uuid) +{ + tGATT_ATTR16 *p_attr16 = (tGATT_ATTR16 *)p_attr; + + if (p_attr16->uuid_type == GATT_ATTR_UUID_TYPE_16) { + p_uuid->len = LEN_UUID_16; + p_uuid->uu.uuid16 = p_attr16->uuid; + } else if (p_attr16->uuid_type == GATT_ATTR_UUID_TYPE_32) { + tGATT_ATTR32 *p_attr32 = (tGATT_ATTR32 *)p_attr; + p_uuid->len = LEN_UUID_32; + p_uuid->uu.uuid32 = p_attr32->uuid; + } else if (p_attr16->uuid_type == GATT_ATTR_UUID_TYPE_128) { + tGATT_ATTR128 *p_attr128 = (tGATT_ATTR128 *)p_attr; + p_uuid->len = LEN_UUID_128; + memcpy(p_uuid->uu.uuid128, p_attr128->uuid, LEN_UUID_128); + } +} + +static size_t calculate_database_info_size(void) +{ + UINT8 i; + tGATT_SVC_DB *p_db; + tGATT_ATTR16 *p_attr; + size_t len = 0; + + for (i = 0; i < GATT_MAX_SR_PROFILES; i++) { + p_db = gatt_cb.sr_reg[i].p_db; + if (p_db && p_db->p_attr_list) { + p_attr = (tGATT_ATTR16 *)p_db->p_attr_list; + while (p_attr) { + if (p_attr->uuid == GATT_UUID_PRI_SERVICE || + p_attr->uuid == GATT_UUID_SEC_SERVICE) { + // Service declaration + len += 4 + p_attr->p_value->uuid.len; + } else if (p_attr->uuid == GATT_UUID_INCLUDE_SERVICE) { + // Included service declaration + len += 8 + p_attr->p_value->incl_handle.service_type.len; + } else if (p_attr->uuid == GATT_UUID_CHAR_DECLARE) { + tBT_UUID char_uuid = {0}; + // Characteristic declaration + p_attr = (tGATT_ATTR16 *)p_attr->p_next; + attr_uuid_to_bt_uuid((void *)p_attr, &char_uuid); + // Increment 1 to fetch characteristic uuid from value declaration attribute + len += 7 + char_uuid.len; + } else if (p_attr->uuid == GATT_UUID_CHAR_DESCRIPTION || + p_attr->uuid == GATT_UUID_CHAR_CLIENT_CONFIG || + p_attr->uuid == GATT_UUID_CHAR_SRVR_CONFIG || + p_attr->uuid == GATT_UUID_CHAR_PRESENT_FORMAT || + p_attr->uuid == GATT_UUID_CHAR_AGG_FORMAT) { + // Descriptor + len += 4; + } else if (p_attr->uuid == GATT_UUID_CHAR_EXT_PROP) { + // Descriptor + len += 6; + } + p_attr = (tGATT_ATTR16 *) p_attr->p_next; + } + } + } + + return len; +} + +static void fill_database_info(UINT8 *p_data) +{ + UINT8 i; + tGATT_SVC_DB *p_db; + tGATT_ATTR16 *p_attr; + + for (i = 0; i < GATT_MAX_SR_PROFILES; i++) { + p_db = gatt_cb.sr_reg[i].p_db; + if (p_db && p_db->p_attr_list) { + p_attr = (tGATT_ATTR16 *)p_db->p_attr_list; + while (p_attr) { + if (p_attr->uuid == GATT_UUID_PRI_SERVICE || + p_attr->uuid == GATT_UUID_SEC_SERVICE) { + // Service declaration + UINT16_TO_STREAM(p_data, p_attr->handle); + UINT16_TO_STREAM(p_data, p_attr->uuid); + gatt_build_uuid_to_stream(&p_data, p_attr->p_value->uuid); + } else if (p_attr->uuid == GATT_UUID_INCLUDE_SERVICE) { + // Included service declaration + UINT16_TO_STREAM(p_data, p_attr->handle); + UINT16_TO_STREAM(p_data, GATT_UUID_INCLUDE_SERVICE); + UINT16_TO_STREAM(p_data, p_attr->p_value->incl_handle.s_handle); + UINT16_TO_STREAM(p_data, p_attr->p_value->incl_handle.e_handle); + gatt_build_uuid_to_stream(&p_data, p_attr->p_value->incl_handle.service_type); + } else if (p_attr->uuid == GATT_UUID_CHAR_DECLARE) { + tBT_UUID char_uuid = {0}; + // Characteristic declaration + UINT16_TO_STREAM(p_data, p_attr->handle); + UINT16_TO_STREAM(p_data, GATT_UUID_CHAR_DECLARE); + UINT8_TO_STREAM(p_data, p_attr->p_value->char_decl.property); + UINT16_TO_STREAM(p_data, p_attr->p_value->char_decl.char_val_handle); + p_attr = (tGATT_ATTR16 *)p_attr->p_next; + attr_uuid_to_bt_uuid((void *)p_attr, &char_uuid); + // Increment 1 to fetch characteristic uuid from value declaration attribute + gatt_build_uuid_to_stream(&p_data, char_uuid); + } else if (p_attr->uuid == GATT_UUID_CHAR_DESCRIPTION || + p_attr->uuid == GATT_UUID_CHAR_CLIENT_CONFIG || + p_attr->uuid == GATT_UUID_CHAR_SRVR_CONFIG || + p_attr->uuid == GATT_UUID_CHAR_PRESENT_FORMAT || + p_attr->uuid == GATT_UUID_CHAR_AGG_FORMAT) { + // Descriptor + UINT16_TO_STREAM(p_data, p_attr->handle); + UINT16_TO_STREAM(p_data, p_attr->uuid); + } else if (p_attr->uuid == GATT_UUID_CHAR_EXT_PROP) { + // Descriptor + UINT16_TO_STREAM(p_data, p_attr->handle); + UINT16_TO_STREAM(p_data, p_attr->uuid); + // TODO: process extended properties descriptor + if (p_attr->p_value->attr_val.attr_len == 2) { + memcpy(p_data, p_attr->p_value->attr_val.attr_val, 2); + } else { + UINT16_TO_STREAM(p_data, 0x0000); + } + } + p_attr = (tGATT_ATTR16 *) p_attr->p_next; + } + } + } +} + +tGATT_STATUS gatts_calculate_datebase_hash(BT_OCTET16 hash) +{ + UINT8 tmp; + UINT16 i; + UINT16 j; + size_t len; + UINT8 *data_buf = NULL; + + len = calculate_database_info_size(); + + data_buf = (UINT8 *)osi_malloc(len); + if (data_buf == NULL) { + GATT_TRACE_ERROR ("%s failed to allocate buffer (%u)\n", __func__, len); + return GATT_NO_RESOURCES; + } + + fill_database_info(data_buf); + + // reverse database info + for (i = 0, j = len-1; i < j; i++, j--) { + tmp = data_buf[i]; + data_buf[i] = data_buf[j]; + data_buf[j] = tmp; + } + +#if SMP_INCLUDED == TRUE + BT_OCTET16 key = {0}; + aes_cipher_msg_auth_code(key, data_buf, len, 16, hash); + //ESP_LOG_BUFFER_HEX("db hash", hash, BT_OCTET16_LEN); +#endif + + osi_free(data_buf); + return GATT_SUCCESS; +} + +void gatts_show_local_database(void) +{ + UINT8 i; + tGATT_SVC_DB *p_db; + tGATT_ATTR16 *p_attr; + + printf("\n================= GATTS DATABASE DUMP START =================\n"); + for (i = 0; i < GATT_MAX_SR_PROFILES; i++) { + p_db = gatt_cb.sr_reg[i].p_db; + if (p_db && p_db->p_attr_list) { + p_attr = (tGATT_ATTR16 *)p_db->p_attr_list; + while (p_attr) { + switch (p_attr->uuid) { + case GATT_UUID_PRI_SERVICE: + case GATT_UUID_SEC_SERVICE: + // Service declaration + printf("%s\n", gatt_get_attr_name(p_attr->uuid)); + printf("\tuuid %s\n", gatt_uuid_to_str(&p_attr->p_value->uuid)); + printf("\thandle %d\n", p_attr->handle); + printf("\tend_handle %d\n",p_db->end_handle-1); + break; + case GATT_UUID_INCLUDE_SERVICE: + // Included service declaration + printf("%s\n", gatt_get_attr_name(p_attr->uuid)); + printf("\tuuid %s\t", gatt_uuid_to_str(&p_attr->p_value->incl_handle.service_type)); + printf("\thandle %d\n", p_attr->p_value->incl_handle.s_handle); + printf("\tend_handle %d\n", p_attr->p_value->incl_handle.e_handle); + break; + case GATT_UUID_CHAR_DECLARE: { + tBT_UUID char_uuid = {0}; + tGATT_ATTR16 *p_char_val; + p_char_val = (tGATT_ATTR16 *)p_attr->p_next; + attr_uuid_to_bt_uuid((void *)p_char_val, &char_uuid); + + printf("%s\n", gatt_get_attr_name(p_attr->uuid)); + printf("\tuuid %s\n", gatt_uuid_to_str(&char_uuid)); + printf("\tdef_handle %d\n", p_attr->handle); + printf("\tval_handle %d\n", p_attr->p_value->char_decl.char_val_handle); + printf("\tperm 0x%04x, prop 0x%02x\n", p_char_val->permission, p_attr->p_value->char_decl.property); + break; + } + case GATT_UUID_CHAR_EXT_PROP: + case GATT_UUID_CHAR_DESCRIPTION: + case GATT_UUID_CHAR_CLIENT_CONFIG: + case GATT_UUID_CHAR_SRVR_CONFIG: + case GATT_UUID_CHAR_PRESENT_FORMAT: + case GATT_UUID_CHAR_AGG_FORMAT: + printf("%s\n", gatt_get_attr_name(p_attr->uuid)); + printf("\thandle %d\n", p_attr->handle); + break; + } + p_attr = (tGATT_ATTR16 *) p_attr->p_next; + } + } + } + printf("================= GATTS DATABASE DUMP END =================\n"); +} +#endif /* BLE_INCLUDED == TRUE && GATTS_INCLUDED == TRUE */ diff --git a/lib/bt/host/bluedroid/stack/gatt/gatt_utils.c b/lib/bt/host/bluedroid/stack/gatt/gatt_utils.c new file mode 100644 index 00000000..621b2468 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/gatt/gatt_utils.c @@ -0,0 +1,2948 @@ +/****************************************************************************** + * + * Copyright (C) 2009-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * this file contains GATT utility functions + * + ******************************************************************************/ +#include "common/bt_target.h" +#include "osi/allocator.h" + +#if BLE_INCLUDED == TRUE +#include +#include + +#include "stack/l2cdefs.h" +#include "gatt_int.h" +#include "stack/gatt_api.h" +#include "stack/gattdefs.h" +#include "stack/sdp_api.h" +#include "btm_int.h" +/* check if [x, y] and [a, b] have overlapping range */ +#define GATT_VALIDATE_HANDLE_RANGE(x, y, a, b) (y >= a && x <= b) + +#define GATT_GET_NEXT_VALID_HANDLE(x) (((x)/10 + 1) * 10) + +const char *const op_code_name[] = { + "UNKNOWN", + "ATT_RSP_ERROR", + "ATT_REQ_MTU", + "ATT_RSP_MTU", + "ATT_REQ_READ_INFO", + "ATT_RSP_READ_INFO", + "ATT_REQ_FIND_TYPE_VALUE", + "ATT_RSP_FIND_TYPE_VALUE", + "ATT_REQ_READ_BY_TYPE", + "ATT_RSP_READ_BY_TYPE", + "ATT_REQ_READ", + "ATT_RSP_READ", + "ATT_REQ_READ_BLOB", + "ATT_RSP_READ_BLOB", + "GATT_REQ_READ_MULTI", + "GATT_RSP_READ_MULTI", + "GATT_REQ_READ_BY_GRP_TYPE", + "GATT_RSP_READ_BY_GRP_TYPE", + "ATT_REQ_WRITE", + "ATT_RSP_WRITE", + "ATT_CMD_WRITE", + "ATT_SIGN_CMD_WRITE", + "ATT_REQ_PREPARE_WRITE", + "ATT_RSP_PREPARE_WRITE", + "ATT_REQ_EXEC_WRITE", + "ATT_RSP_EXEC_WRITE", + "Reserved", + "ATT_HANDLE_VALUE_NOTIF", + "Reserved", + "ATT_HANDLE_VALUE_IND", + "ATT_HANDLE_VALUE_CONF", + "ATT_OP_CODE_MAX" +}; + +static const UINT8 base_uuid[LEN_UUID_128] = {0xFB, 0x34, 0x9B, 0x5F, 0x80, 0x00, 0x00, 0x80, + 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + +static UINT32 gatt_tcb_id; + +/******************************************************************************* +** +** Function gatt_free_pending_ind +** +** Description Free all pending indications +** +** Returns None +** +*******************************************************************************/ +void gatt_free_pending_ind(tGATT_TCB *p_tcb) +{ + GATT_TRACE_DEBUG("gatt_free_pending_ind"); + if (p_tcb->pending_ind_q == NULL) { + return; + } + + /* release all queued indications */ + while (!fixed_queue_is_empty(p_tcb->pending_ind_q)) { + osi_free(fixed_queue_dequeue(p_tcb->pending_ind_q, 0)); + } + fixed_queue_free(p_tcb->pending_ind_q, NULL); + p_tcb->pending_ind_q = NULL; +} + +/******************************************************************************* +** +** Function gatt_free_pending_enc_queue +** +** Description Free all buffers in pending encyption queue +** +** Returns None +** +*******************************************************************************/ +void gatt_free_pending_enc_queue(tGATT_TCB *p_tcb) +{ + GATT_TRACE_DEBUG("gatt_free_pending_enc_queue"); + if (p_tcb->pending_enc_clcb == NULL) { + return; + } + + /* release all queued indications */ + while (!fixed_queue_is_empty(p_tcb->pending_enc_clcb)) { + osi_free(fixed_queue_dequeue(p_tcb->pending_enc_clcb, 0)); + } + fixed_queue_free(p_tcb->pending_enc_clcb, NULL); + p_tcb->pending_enc_clcb = NULL; +} + +/******************************************************************************* +** +** Function gatt_free_pending_prepare_write_queue +** +** Description Free all buffers in pending prepare write packets queue +** +** Returns None +** +*******************************************************************************/ +void gatt_free_pending_prepare_write_queue(tGATT_TCB *p_tcb) +{ + GATT_TRACE_DEBUG("gatt_free_pending_prepare_write_queue"); + + if (p_tcb->prepare_write_record.queue) { + /* release all queued prepare write packets */ + while (!fixed_queue_is_empty(p_tcb->prepare_write_record.queue)) { + osi_free(fixed_queue_dequeue(p_tcb->prepare_write_record.queue, FIXED_QUEUE_MAX_TIMEOUT)); + } + fixed_queue_free(p_tcb->prepare_write_record.queue, NULL); + p_tcb->prepare_write_record.queue = NULL; + } + + p_tcb->prepare_write_record.total_num = 0; + p_tcb->prepare_write_record.error_code_app = GATT_SUCCESS; +} + +/******************************************************************************* +** +** Function gatt_delete_dev_from_srv_chg_clt_list +** +** Description Delete a device from the service changed client lit +** +** Returns None +** +*******************************************************************************/ +void gatt_delete_dev_from_srv_chg_clt_list(BD_ADDR bd_addr) +{ + tGATTS_SRV_CHG *p_buf; + tGATTS_SRV_CHG_REQ req; + + GATT_TRACE_DEBUG ("gatt_delete_dev_from_srv_chg_clt_list"); + if ((p_buf = gatt_is_bda_in_the_srv_chg_clt_list(bd_addr)) != NULL) { + if (gatt_cb.cb_info.p_srv_chg_callback) { + /* delete from NV */ + memcpy(req.srv_chg.bda, bd_addr, BD_ADDR_LEN); + (*gatt_cb.cb_info.p_srv_chg_callback)(GATTS_SRV_CHG_CMD_REMOVE_CLIENT, &req, NULL); + } + osi_free(fixed_queue_try_remove_from_queue(gatt_cb.srv_chg_clt_q, + p_buf)); + } + +} + +/******************************************************************************* +** +** Function gatt_set_srv_chg +** +** Description Set the service changed flag to TRUE +** +** Returns None +** +*******************************************************************************/ +void gatt_set_srv_chg(void) +{ + GATT_TRACE_DEBUG ("gatt_set_srv_chg"); + + if (fixed_queue_is_empty(gatt_cb.srv_chg_clt_q)) { + return; + } + + list_t *list = fixed_queue_get_list(gatt_cb.srv_chg_clt_q); + for (const list_node_t *node = list_begin(list); node != list_end(list); + node = list_next(node)) { + GATT_TRACE_DEBUG ("found a srv_chg clt"); + + tGATTS_SRV_CHG *p_buf = (tGATTS_SRV_CHG *)list_node(node); + if (!p_buf->srv_changed) { + GATT_TRACE_DEBUG("set srv_changed to TRUE"); + p_buf->srv_changed = TRUE; + tGATTS_SRV_CHG_REQ req; + memcpy(&req.srv_chg, p_buf, sizeof(tGATTS_SRV_CHG)); + if (gatt_cb.cb_info.p_srv_chg_callback) { + (*gatt_cb.cb_info.p_srv_chg_callback)(GATTS_SRV_CHG_CMD_UPDATE_CLIENT,&req, NULL); + } + } + } +} + +/******************************************************************************* +** +** Function gatt_sr_is_new_srv_chg +** +** Description Find the app id in on the new service changed list +** +** Returns Pointer to the found new service changed item othwerwise NULL +** +*******************************************************************************/ +tGATTS_PENDING_NEW_SRV_START *gatt_sr_is_new_srv_chg(tBT_UUID *p_app_uuid128, tBT_UUID *p_svc_uuid, UINT16 svc_inst) +{ + tGATTS_PENDING_NEW_SRV_START *p_buf = NULL; + + if (fixed_queue_is_empty(gatt_cb.pending_new_srv_start_q)) { + return NULL; + } + + list_t *list = fixed_queue_get_list(gatt_cb.pending_new_srv_start_q); + for (const list_node_t *node = list_begin(list); node != list_end(list); + node = list_next(node)) { + p_buf = (tGATTS_PENDING_NEW_SRV_START *)list_node(node); + tGATTS_HNDL_RANGE *p = p_buf->p_new_srv_start; + if (gatt_uuid_compare(*p_app_uuid128, p->app_uuid128) + && gatt_uuid_compare (*p_svc_uuid, p->svc_uuid) + && (svc_inst == p->svc_inst)) { + GATT_TRACE_DEBUG("gatt_sr_is_new_srv_chg: Yes"); + break; + } + } + + return p_buf; +} + + +/******************************************************************************* +** +** Function gatt_add_pending_ind +** +** Description Add a pending indication +** +** Returns Pointer to the current pending indication buffer, NULL no buffer available +** +*******************************************************************************/ +tGATT_VALUE *gatt_add_pending_ind(tGATT_TCB *p_tcb, tGATT_VALUE *p_ind) +{ + tGATT_VALUE *p_buf; + GATT_TRACE_DEBUG ("gatt_add_pending_ind"); + if ((p_buf = (tGATT_VALUE *)osi_malloc((UINT16)sizeof(tGATT_VALUE))) != NULL) { + GATT_TRACE_DEBUG ("enqueue a pending indication"); + memcpy(p_buf, p_ind, sizeof(tGATT_VALUE)); + fixed_queue_enqueue(p_tcb->pending_ind_q, p_buf, FIXED_QUEUE_MAX_TIMEOUT); + } + return p_buf; +} + + +/******************************************************************************* +** +** Function gatt_add_pending_new_srv_start +** +** Description Add a pending new srv start to the new service start queue +** +** Returns Pointer to the new service start buffer, NULL no buffer available +** +*******************************************************************************/ +tGATTS_PENDING_NEW_SRV_START *gatt_add_pending_new_srv_start(tGATTS_HNDL_RANGE *p_new_srv_start) +{ + tGATTS_PENDING_NEW_SRV_START *p_buf; + + GATT_TRACE_DEBUG ("gatt_add_pending_new_srv_start"); + if ((p_buf = (tGATTS_PENDING_NEW_SRV_START *)osi_malloc((UINT16)sizeof(tGATTS_PENDING_NEW_SRV_START))) != NULL) { + GATT_TRACE_DEBUG ("enqueue a new pending new srv start"); + p_buf->p_new_srv_start = p_new_srv_start; + fixed_queue_enqueue(gatt_cb.pending_new_srv_start_q, p_buf, FIXED_QUEUE_MAX_TIMEOUT); + } + return p_buf; +} + + +/******************************************************************************* +** +** Function gatt_add_srv_chg_clt +** +** Description Add a service chnage client to the service change client queue +** +** Returns Pointer to the service change client buffer; Null no buffer available +** +*******************************************************************************/ +tGATTS_SRV_CHG *gatt_add_srv_chg_clt(tGATTS_SRV_CHG *p_srv_chg) +{ + tGATTS_SRV_CHG *p_buf; + GATT_TRACE_DEBUG ("gatt_add_srv_chg_clt"); + if ((p_buf = (tGATTS_SRV_CHG *)osi_malloc((UINT16)sizeof(tGATTS_SRV_CHG))) != NULL) { + GATT_TRACE_DEBUG ("enqueue a srv chg client"); + memcpy(p_buf, p_srv_chg, sizeof(tGATTS_SRV_CHG)); + fixed_queue_enqueue(gatt_cb.srv_chg_clt_q, p_buf, FIXED_QUEUE_MAX_TIMEOUT); + } + + return p_buf; +} + + +/******************************************************************************* +** +** Function gatt_alloc_hdl_buffer +** +** Description Allocate a handle buufer +** +** Returns Pointer to the allocated buffer, NULL no buffer available +** +*******************************************************************************/ +#if (GATTS_INCLUDED == TRUE) +tGATT_HDL_LIST_ELEM *gatt_alloc_hdl_buffer(void) +{ + UINT8 i; + tGATT_CB *p_cb = &gatt_cb; + tGATT_HDL_LIST_ELEM *p_elem = &p_cb->hdl_list[0]; + + for (i = 0; i < GATT_MAX_SR_PROFILES; i++, p_elem ++) { + if (!p_cb->hdl_list[i].in_use) { + memset(p_elem, 0, sizeof(tGATT_HDL_LIST_ELEM)); + p_elem->in_use = TRUE; + p_elem->svc_db.svc_buffer = fixed_queue_new(QUEUE_SIZE_MAX); + return p_elem; + } + } + + return NULL; +} + +/******************************************************************************* +** +** Function gatt_find_hdl_buffer_by_handle +** +** Description Find handle range buffer by service handle. +** +** Returns Pointer to the buffer, NULL no buffer available +** +*******************************************************************************/ +tGATT_HDL_LIST_ELEM *gatt_find_hdl_buffer_by_handle(UINT16 handle) +{ + tGATT_HDL_LIST_INFO *p_list_info = &gatt_cb.hdl_list_info; + tGATT_HDL_LIST_ELEM *p_list = NULL; + + p_list = p_list_info->p_first; + + while (p_list != NULL) { + if (p_list->in_use && p_list->asgn_range.s_handle == handle) { + return (p_list); + } + p_list = p_list->p_next; + } + return NULL; +} + +/******************************************************************************* +** +** Function gatt_find_hdl_buffer_by_attr_handle +** +** Description Find handle range buffer by attribute handle. +** +** Returns Pointer to the buffer, NULL no buffer available +** +*******************************************************************************/ +tGATT_HDL_LIST_ELEM *gatt_find_hdl_buffer_by_attr_handle(UINT16 attr_handle) +{ + tGATT_HDL_LIST_INFO *p_list_info = &gatt_cb.hdl_list_info; + tGATT_HDL_LIST_ELEM *p_list = NULL; + + p_list = p_list_info->p_first; + + while (p_list != NULL) { + if (p_list->in_use && (p_list->asgn_range.s_handle <= attr_handle) + && (p_list->asgn_range.e_handle >= attr_handle)) { + return (p_list); + } + p_list = p_list->p_next; + } + return NULL; +} + + +/******************************************************************************* +** +** Function gatt_find_hdl_buffer_by_app_id +** +** Description Find handle range buffer by app ID, service and service instance ID. +** +** Returns Pointer to the buffer, NULL no buffer available +** +*******************************************************************************/ +tGATT_HDL_LIST_ELEM *gatt_find_hdl_buffer_by_app_id (tBT_UUID *p_app_uuid128, + tBT_UUID *p_svc_uuid, + UINT16 svc_inst) +{ + tGATT_HDL_LIST_INFO *p_list_info = &gatt_cb.hdl_list_info; + tGATT_HDL_LIST_ELEM *p_list = NULL; + + p_list = p_list_info->p_first; + + while (p_list != NULL) { + if ( gatt_uuid_compare (*p_app_uuid128, p_list->asgn_range.app_uuid128) + && gatt_uuid_compare (*p_svc_uuid, p_list->asgn_range.svc_uuid) + && (svc_inst == p_list->asgn_range.svc_inst) ) { + GATT_TRACE_DEBUG ("Already allocated handles for this service before!!"); + return (p_list); + } + p_list = p_list->p_next; + } + return NULL; +} +#endif ///GATTS_INCLUDED == TRUE + +/******************************************************************************* +** +** Function gatt_free_attr_value_buffer +** +** Description free characteristic attribute value buffer in a service +** +** Returns None +** +*******************************************************************************/ +void gatt_free_attr_value_buffer(tGATT_HDL_LIST_ELEM *p) +{ + if (p){ + tGATT_SVC_DB *p_db = &(p->svc_db); + tGATT_ATTR16 *p_attr = p_db->p_attr_list; + tGATT_ATTR_VALUE *p_value = NULL; + + while(p_attr){ + if (p_attr->mask & GATT_ATTR_VALUE_ALLOCATED){ + p_value = p_attr->p_value; + if ((p_value != NULL) && (p_value->attr_val.attr_val != NULL)){ + osi_free(p_value->attr_val.attr_val); + } + } + p_attr = p_attr->p_next; + } + } +} +/******************************************************************************* +** +** Function gatt_free_hdl_buffer +** +** Description free a handle buffer +** +** Returns None +** +*******************************************************************************/ +void gatt_free_hdl_buffer(tGATT_HDL_LIST_ELEM *p) +{ + + if (p) { + while (!fixed_queue_is_empty(p->svc_db.svc_buffer)) { + osi_free(fixed_queue_dequeue(p->svc_db.svc_buffer, 0)); + } + fixed_queue_free(p->svc_db.svc_buffer, NULL); + memset(p, 0, sizeof(tGATT_HDL_LIST_ELEM)); + } +} +/******************************************************************************* +** +** Function gatt_free_srvc_db_buffer_app_id +** +** Description free the service attribute database buffers by the owner of the +** service app ID. +** +** Returns None +** +*******************************************************************************/ +#if (GATTS_INCLUDED == TRUE) +void gatt_free_srvc_db_buffer_app_id(tBT_UUID *p_app_id) +{ + tGATT_HDL_LIST_ELEM *p_elem = &gatt_cb.hdl_list[0]; + UINT8 i; + + for (i = 0; i < GATT_MAX_SR_PROFILES; i ++, p_elem ++) { + if (memcmp(p_app_id, &p_elem->asgn_range.app_uuid128, sizeof(tBT_UUID)) == 0) { + gatt_free_attr_value_buffer(p_elem); + while (!fixed_queue_is_empty(p_elem->svc_db.svc_buffer)) { + osi_free(fixed_queue_dequeue(p_elem->svc_db.svc_buffer, 0)); + } + fixed_queue_free(p_elem->svc_db.svc_buffer, NULL); + p_elem->svc_db.svc_buffer = NULL; + + p_elem->svc_db.mem_free = 0; + p_elem->svc_db.p_attr_list = p_elem->svc_db.p_free_mem = NULL; + } + } +} +/******************************************************************************* +** +** Function gatt_is_last_attribute +** +** Description Check this is the last attribute of the specified value or not +** +** Returns TRUE - yes this is the last attribute +** +*******************************************************************************/ +BOOLEAN gatt_is_last_attribute(tGATT_SRV_LIST_INFO *p_list, tGATT_SRV_LIST_ELEM *p_start, tBT_UUID value) +{ + tGATT_SRV_LIST_ELEM *p_srv = p_start->p_next; + BOOLEAN is_last_attribute = TRUE; + tGATT_SR_REG *p_rcb = NULL; + tBT_UUID *p_svc_uuid; + + p_list->p_last_primary = NULL; + + while (p_srv) { + p_rcb = GATT_GET_SR_REG_PTR(p_srv->i_sreg); + + p_svc_uuid = gatts_get_service_uuid (p_rcb->p_db); + + if (gatt_uuid_compare(value, *p_svc_uuid)) { + is_last_attribute = FALSE; + break; + + } + p_srv = p_srv->p_next; + } + + return is_last_attribute; + +} +/******************************************************************************* +** +** Function gatt_update_last_pri_srv_info +** +** Description Update the the last primary info for the service list info +** +** Returns None +** +*******************************************************************************/ +void gatt_update_last_pri_srv_info(tGATT_SRV_LIST_INFO *p_list) +{ + tGATT_SRV_LIST_ELEM *p_srv = p_list->p_first; + + p_list->p_last_primary = NULL; + + while (p_srv) { + if (p_srv->is_primary) { + p_list->p_last_primary = p_srv; + } + p_srv = p_srv->p_next; + } + +} +/******************************************************************************* +** +** Function gatts_update_srv_list_elem +** +** Description update an element in the service list. +** +** Returns None. +** +*******************************************************************************/ +void gatts_update_srv_list_elem(UINT8 i_sreg, UINT16 handle, BOOLEAN is_primary) +{ + UNUSED(handle); + + gatt_cb.srv_list[i_sreg].in_use = TRUE; + gatt_cb.srv_list[i_sreg].i_sreg = i_sreg; + gatt_cb.srv_list[i_sreg].s_hdl = gatt_cb.sr_reg[i_sreg].s_hdl; + gatt_cb.srv_list[i_sreg].is_primary = is_primary; + + return; +} +#endif ///GATTS_INCLUDED == TRUE + +/******************************************************************************* +** +** Function gatt_add_a_srv_to_list +** +** Description add an service to the list in ascending +** order of the start handle +** +** Returns BOOLEAN TRUE-if add is successful +** +*******************************************************************************/ +BOOLEAN gatt_add_a_srv_to_list(tGATT_SRV_LIST_INFO *p_list, tGATT_SRV_LIST_ELEM *p_new) +{ + tGATT_SRV_LIST_ELEM *p_old; + + if (!p_new) { + GATT_TRACE_DEBUG("p_new==NULL"); + return FALSE; + } + + if (!p_list->p_first) { + /* this is an empty list */ + p_list->p_first = + p_list->p_last = p_new; + p_new->p_next = + p_new->p_prev = NULL; + } else { + p_old = p_list->p_first; + while (1) { + if (p_old == NULL) { + p_list->p_last->p_next = p_new; + p_new->p_prev = p_list->p_last; + p_new->p_next = NULL; + p_list->p_last = p_new; + break; + } else { + if (p_new->s_hdl < p_old->s_hdl) { + /* if not the first in list */ + if (p_old->p_prev != NULL) { + p_old->p_prev->p_next = p_new; + } else { + p_list->p_first = p_new; + } + + p_new->p_prev = p_old->p_prev; + p_new->p_next = p_old; + p_old->p_prev = p_new; + break; + } + } + p_old = p_old->p_next; + } + } + p_list->count++; + + gatt_update_last_pri_srv_info(p_list); + return TRUE; + +} + +/******************************************************************************* +** +** Function gatt_remove_a_srv_from_list +** +** Description Remove a service from the list +** +** Returns BOOLEAN TRUE-if remove is successful +** +*******************************************************************************/ +BOOLEAN gatt_remove_a_srv_from_list(tGATT_SRV_LIST_INFO *p_list, tGATT_SRV_LIST_ELEM *p_remove) +{ + if (!p_remove || !p_list->p_first) { + GATT_TRACE_DEBUG("p_remove==NULL || p_list->p_first==NULL"); + return FALSE; + } + + if (p_remove->p_prev == NULL) { + p_list->p_first = p_remove->p_next; + if (p_remove->p_next) { + p_remove->p_next->p_prev = NULL; + } + } else if (p_remove->p_next == NULL) { + p_list->p_last = p_remove->p_prev; + p_remove->p_prev->p_next = NULL; + } else { + p_remove->p_next->p_prev = p_remove->p_prev; + p_remove->p_prev->p_next = p_remove->p_next; + } + p_list->count--; + gatt_update_last_pri_srv_info(p_list); + return TRUE; + +} + +/******************************************************************************* +** +** Function gatt_add_an_item_to_list +** +** Description add an service handle range to the list in decending +** order of the start handle +** +** Returns BOOLEAN TRUE-if add is successful +** +*******************************************************************************/ +BOOLEAN gatt_add_an_item_to_list(tGATT_HDL_LIST_INFO *p_list, tGATT_HDL_LIST_ELEM *p_new) +{ + tGATT_HDL_LIST_ELEM *p_old; + if (!p_new) { + GATT_TRACE_DEBUG("p_new==NULL"); + return FALSE; + } + + if (!p_list->p_first) { + /* this is an empty list */ + p_list->p_first = + p_list->p_last = p_new; + p_new->p_next = + p_new->p_prev = NULL; + } else { + p_old = p_list->p_first; + while (1) { + if (p_old == NULL) { + p_list->p_last->p_next = p_new; + p_new->p_prev = p_list->p_last; + p_new->p_next = NULL; + p_list->p_last = p_new; + + break; + + } else { + if (p_new->asgn_range.s_handle > p_old->asgn_range.s_handle) { + if (p_old == p_list->p_first) { + p_list->p_first = p_new; + } + + p_new->p_prev = p_old->p_prev; + p_new->p_next = p_old; + + + p_old->p_prev = p_new; + break; + } + } + p_old = p_old->p_next; + } + } + p_list->count++; + return TRUE; + +} + +/******************************************************************************* +** +** Function gatt_remove_an_item_from_list +** +** Description Remove an service handle range from the list +** +** Returns BOOLEAN TRUE-if remove is successful +** +*******************************************************************************/ +BOOLEAN gatt_remove_an_item_from_list(tGATT_HDL_LIST_INFO *p_list, tGATT_HDL_LIST_ELEM *p_remove) +{ + if (!p_remove || !p_list->p_first) { + GATT_TRACE_DEBUG("p_remove==NULL || p_list->p_first==NULL"); + return FALSE; + } + + if (p_remove->p_prev == NULL) { + p_list->p_first = p_remove->p_next; + if (p_remove->p_next) { + p_remove->p_next->p_prev = NULL; + } + } else if (p_remove->p_next == NULL) { + p_list->p_last = p_remove->p_prev; + p_remove->p_prev->p_next = NULL; + } else { + p_remove->p_next->p_prev = p_remove->p_prev; + p_remove->p_prev->p_next = p_remove->p_next; + } + p_list->count--; + return TRUE; + +} + +/******************************************************************************* +** +** Function gatt_find_the_connected_bda +** +** Description This function find the connected bda +** +** Returns TRUE if found +** +*******************************************************************************/ +BOOLEAN gatt_find_the_connected_bda(UINT8 start_idx, BD_ADDR bda, UINT8 *p_found_idx, + tBT_TRANSPORT *p_transport) +{ + BOOLEAN found = FALSE; + GATT_TRACE_DEBUG("gatt_find_the_connected_bda start_idx=%d", start_idx); + tGATT_TCB *p_tcb = NULL; + list_node_t *p_node = NULL; + p_tcb = gatt_get_tcb_by_idx(start_idx); + if (p_tcb) { + for(p_node = list_get_node(gatt_cb.p_tcb_list, p_tcb); p_node; p_node = list_next(p_node)) { + p_tcb = list_node(p_node); + if (p_tcb->in_use && p_tcb->ch_state == GATT_CH_OPEN) { + memcpy( bda, p_tcb->peer_bda, BD_ADDR_LEN); + *p_found_idx = p_tcb->tcb_idx; + *p_transport = p_tcb->transport; + found = TRUE; + GATT_TRACE_DEBUG("gatt_find_the_connected_bda bda :%02x-%02x-%02x-%02x-%02x-%02x", + bda[0], bda[1], bda[2], bda[3], bda[4], bda[5]); + break; + } + } + GATT_TRACE_DEBUG("gatt_find_the_connected_bda found=%d found_idx=%d", found, p_tcb->tcb_idx); + } + return found; +} + + +/******************************************************************************* +** +** Function gatt_is_srv_chg_ind_pending +** +** Description Check whether a service chnaged is in the indication pending queue +** or waiting for an Ack already +** +** Returns BOOLEAN +** +*******************************************************************************/ +BOOLEAN gatt_is_srv_chg_ind_pending (tGATT_TCB *p_tcb) +{ + BOOLEAN srv_chg_ind_pending = FALSE; + + GATT_TRACE_DEBUG("gatt_is_srv_chg_ind_pending is_queue_empty=%d", + fixed_queue_is_empty(p_tcb->pending_ind_q)); + + if (p_tcb->indicate_handle == gatt_cb.handle_of_h_r) { + srv_chg_ind_pending = TRUE; + } else if (! fixed_queue_is_empty(p_tcb->pending_ind_q)) { + list_t *list = fixed_queue_get_list(p_tcb->pending_ind_q); + for (const list_node_t *node = list_begin(list); + node != list_end(list); + node = list_next(node)) { + tGATT_VALUE *p_buf = (tGATT_VALUE *)list_node(node); + if (p_buf->handle == gatt_cb.handle_of_h_r) + { + srv_chg_ind_pending = TRUE; + break; + } + } + } + + GATT_TRACE_DEBUG("srv_chg_ind_pending = %d", srv_chg_ind_pending); + return srv_chg_ind_pending; +} + + +/******************************************************************************* +** +** Function gatt_is_bda_in_the_srv_chg_clt_list +** +** Description This function check the specified bda is in the srv chg clinet list or not +** +** Returns pointer to the found elemenet otherwise NULL +** +*******************************************************************************/ +tGATTS_SRV_CHG *gatt_is_bda_in_the_srv_chg_clt_list (BD_ADDR bda) +{ + tGATTS_SRV_CHG *p_buf = NULL; + + GATT_TRACE_DEBUG("gatt_is_bda_in_the_srv_chg_clt_list :%02x-%02x-%02x-%02x-%02x-%02x", + bda[0], bda[1], bda[2], bda[3], bda[4], bda[5]); + + if (fixed_queue_is_empty(gatt_cb.srv_chg_clt_q)) { + return NULL; + } + + list_t *list = fixed_queue_get_list(gatt_cb.srv_chg_clt_q); + for (const list_node_t *node = list_begin(list); node != list_end(list); + node = list_next(node)) { + p_buf = (tGATTS_SRV_CHG *)list_node(node); + if (!memcmp( bda, p_buf->bda, BD_ADDR_LEN)) { + GATT_TRACE_DEBUG("bda is in the srv chg clt list"); + break; + } + } + + return p_buf; +} + + +/******************************************************************************* +** +** Function gatt_is_bda_connected +** +** Description +** +** Returns GATT_INDEX_INVALID if not found. Otherwise index to the tcb. +** +*******************************************************************************/ +BOOLEAN gatt_is_bda_connected(BD_ADDR bda) +{ + BOOLEAN connected = FALSE; + tGATT_TCB *p_tcb = NULL; + list_node_t *p_node = NULL; + for(p_node = list_begin(gatt_cb.p_tcb_list); p_node; p_node = list_next(p_node)) { + p_tcb = list_node(p_node); + if (p_tcb->in_use && + !memcmp(p_tcb->peer_bda, bda, BD_ADDR_LEN)) { + connected = TRUE; + break; + } + } + return connected; +} + +/******************************************************************************* +** +** Function gatt_check_connection_state_by_tcb +** +** Description +** +** Returns TRUE if connected. Otherwise connection not established. +** +*******************************************************************************/ +BOOLEAN gatt_check_connection_state_by_tcb(tGATT_TCB *p_tcb) +{ + BOOLEAN connected = FALSE; + + if(p_tcb && gatt_get_ch_state(p_tcb) == GATT_CH_OPEN) { + connected = TRUE; + } + + return connected; +} + +/******************************************************************************* +** +** Function gatt_find_i_tcb_by_addr +** +** Description The function searches for an empty tcb entry, and return the index. +** +** Returns GATT_INDEX_INVALID if not found. Otherwise index to the tcb. +** +*******************************************************************************/ +UINT8 gatt_find_i_tcb_by_addr(BD_ADDR bda, tBT_TRANSPORT transport) +{ + UINT8 i = 0; + list_node_t *p_node = NULL; + tGATT_TCB *p_tcb = NULL; + for(p_node = list_begin(gatt_cb.p_tcb_list); p_node; p_node = list_next(p_node)) { + p_tcb = list_node(p_node); + if (!memcmp(p_tcb->peer_bda, bda, BD_ADDR_LEN) && + p_tcb->transport == transport) { + i = p_tcb->tcb_idx; + return i; + } + } + return GATT_INDEX_INVALID; +} + +/******************************************************************************* +** +** Function gatt_get_tcb_by_idx +** +** Description The function get TCB using the TCB index +** +** Returns NULL if not found. Otherwise index to the tcb. +** +*******************************************************************************/ +tGATT_TCB *gatt_get_tcb_by_idx(UINT8 tcb_idx) +{ + tGATT_TCB *p_tcb = NULL; + list_node_t *p_node = NULL; + for(p_node = list_begin(gatt_cb.p_tcb_list); p_node; p_node = list_next(p_node)) { + p_tcb = list_node(p_node); + if ( (tcb_idx < GATT_MAX_PHY_CHANNEL) && p_tcb->in_use && p_tcb->tcb_idx == tcb_idx ) { + break; + } else { + p_tcb = NULL; + } + } + + return p_tcb; +} + +/******************************************************************************* +** +** Function gatt_find_tcb_by_addr +** +** Description The function searches for an empty tcb entry, and return pointer. +** +** Returns NULL if not found. Otherwise index to the tcb. +** +*******************************************************************************/ +tGATT_TCB *gatt_find_tcb_by_addr(BD_ADDR bda, tBT_TRANSPORT transport) +{ + tGATT_TCB *p_tcb = NULL; + UINT8 i = 0; + + if ((i = gatt_find_i_tcb_by_addr(bda, transport)) != GATT_INDEX_INVALID) { + p_tcb = gatt_get_tcb_by_idx(i); + } + + return p_tcb; +} +/******************************************************************************* +** +** Function gatt_find_i_tcb_free +** +** Description The function searches for an empty tcb entry, and return the index. +** +** Returns GATT_INDEX_INVALID if not found. Otherwise index to the tcb. +** +*******************************************************************************/ +UINT8 gatt_find_i_tcb_free(void) +{ + UINT8 i = 0, j = GATT_INDEX_INVALID; + + for (i = 0; i < GATT_MAX_PHY_CHANNEL; i ++) { + if (!((1 << i) & gatt_tcb_id)) { + j = i; + break; + } + } + return j; +} +/******************************************************************************* +** +** Function gatt_tcb_alloc +** +** Description The function allocates tcb for given tcb_idx and update tcb_id +** +** Returns Allocated tcb block +** +*******************************************************************************/ +tGATT_TCB *gatt_tcb_alloc(UINT8 tcb_idx) +{ + /* Allocate tcb block */ + tGATT_TCB *p_tcb = (tGATT_TCB *)osi_malloc(sizeof(tGATT_TCB)); + if (p_tcb && list_length(gatt_cb.p_tcb_list) < GATT_MAX_PHY_CHANNEL) { + memset(p_tcb, 0, sizeof(tGATT_TCB)); + /* Add tcb block to list in gatt_cb */ + list_append(gatt_cb.p_tcb_list, p_tcb); + /* Update tcb id */ + gatt_tcb_id |= 1 << tcb_idx; + } else if(p_tcb) { + osi_free(p_tcb); + p_tcb = NULL; + } + return p_tcb; +} + +/******************************************************************************* +** +** Function gatt_tcb_free +** +** Description The function free the given tcb block and update tcb id +** +** Returns void +** +*******************************************************************************/ +void gatt_tcb_free( tGATT_TCB *p_tcb) +{ + UINT8 tcb_idx = p_tcb->tcb_idx; + if (list_remove(gatt_cb.p_tcb_list, p_tcb)) { + gatt_tcb_id &= ~(1 << tcb_idx); + } +} +/******************************************************************************* +** +** Function gatt_allocate_tcb_by_bdaddr +** +** Description The function locate or allocate new tcb entry for matching bda. +** +** Returns GATT_INDEX_INVALID if not found. Otherwise index to the tcb. +** +*******************************************************************************/ +tGATT_TCB *gatt_allocate_tcb_by_bdaddr(BD_ADDR bda, tBT_TRANSPORT transport) +{ + UINT8 i = 0; + BOOLEAN allocated = FALSE; + tGATT_TCB *p_tcb = NULL; + + /* search for existing tcb with matching bda */ + i = gatt_find_i_tcb_by_addr(bda, transport); + /* find free tcb */ + if (i == GATT_INDEX_INVALID) { + i = gatt_find_i_tcb_free(); + allocated = TRUE; + } + if (i != GATT_INDEX_INVALID) { + p_tcb = gatt_tcb_alloc(i); + if (!p_tcb) { + return NULL; + } + if (allocated) { + memset(p_tcb, 0, sizeof(tGATT_TCB)); + p_tcb->pending_enc_clcb = fixed_queue_new(QUEUE_SIZE_MAX); + p_tcb->pending_ind_q = fixed_queue_new(QUEUE_SIZE_MAX); + p_tcb->in_use = TRUE; + p_tcb->tcb_idx = i; + p_tcb->transport = transport; + } + memcpy(p_tcb->peer_bda, bda, BD_ADDR_LEN); +#if GATTS_ROBUST_CACHING_ENABLED + gatt_sr_init_cl_status(p_tcb); +#endif /* GATTS_ROBUST_CACHING_ENABLED */ + } + return p_tcb; +} + +/******************************************************************************* +** +** Function gatt_convert_uuid16_to_uuid128 +** +** Description Convert a 16 bits UUID to be an standard 128 bits one. +** +** Returns TRUE if two uuid match; FALSE otherwise. +** +*******************************************************************************/ +void gatt_convert_uuid16_to_uuid128(UINT8 uuid_128[LEN_UUID_128], UINT16 uuid_16) +{ + UINT8 *p = &uuid_128[LEN_UUID_128 - 4]; + + memcpy (uuid_128, base_uuid, LEN_UUID_128); + + UINT16_TO_STREAM(p, uuid_16); +} + +/******************************************************************************* +** +** Function gatt_convert_uuid32_to_uuid128 +** +** Description Convert a 32 bits UUID to be an standard 128 bits one. +** +** Returns TRUE if two uuid match; FALSE otherwise. +** +*******************************************************************************/ +void gatt_convert_uuid32_to_uuid128(UINT8 uuid_128[LEN_UUID_128], UINT32 uuid_32) +{ + UINT8 *p = &uuid_128[LEN_UUID_128 - 4]; + + memcpy (uuid_128, base_uuid, LEN_UUID_128); + + UINT32_TO_STREAM(p, uuid_32); +} +/******************************************************************************* +** +** Function gatt_uuid_compare +** +** Description Compare two UUID to see if they are the same. +** +** Returns TRUE if two uuid match; FALSE otherwise. +** +*******************************************************************************/ +BOOLEAN gatt_uuid_compare (tBT_UUID src, tBT_UUID tar) +{ + UINT8 su[LEN_UUID_128], tu[LEN_UUID_128]; + UINT8 *ps, *pt; + + /* any of the UUID is unspecified */ + if (src.len == 0 || tar.len == 0) { + return TRUE; + } + + /* If both are 16-bit, we can do a simple compare */ + if (src.len == LEN_UUID_16 && tar.len == LEN_UUID_16) { + return src.uu.uuid16 == tar.uu.uuid16; + } + + /* If both are 32-bit, we can do a simple compare */ + if (src.len == LEN_UUID_32 && tar.len == LEN_UUID_32) { + return src.uu.uuid32 == tar.uu.uuid32; + } + + /* One or both of the UUIDs is 128-bit */ + if (src.len == LEN_UUID_16) { + /* convert a 16 bits UUID to 128 bits value */ + gatt_convert_uuid16_to_uuid128(su, src.uu.uuid16); + ps = su; + } else if (src.len == LEN_UUID_32) { + gatt_convert_uuid32_to_uuid128(su, src.uu.uuid32); + ps = su; + } else { + ps = src.uu.uuid128; + } + + if (tar.len == LEN_UUID_16) { + /* convert a 16 bits UUID to 128 bits value */ + gatt_convert_uuid16_to_uuid128(tu, tar.uu.uuid16); + pt = tu; + } else if (tar.len == LEN_UUID_32) { + /* convert a 32 bits UUID to 128 bits value */ + gatt_convert_uuid32_to_uuid128(tu, tar.uu.uuid32); + pt = tu; + } else { + pt = tar.uu.uuid128; + } + + return (memcmp(ps, pt, LEN_UUID_128) == 0); +} + +/******************************************************************************* +** +** Function gatt_build_uuid_to_stream +** +** Description Add UUID into stream. +** +** Returns UUID length. +** +*******************************************************************************/ +UINT8 gatt_build_uuid_to_stream(UINT8 **p_dst, tBT_UUID uuid) +{ + UINT8 *p = *p_dst; + UINT8 len = 0; + + if (uuid.len == LEN_UUID_16) { + UINT16_TO_STREAM (p, uuid.uu.uuid16); + len = LEN_UUID_16; + } else if (uuid.len == LEN_UUID_32) { /* always convert 32 bits into 128 bits as alwats */ + gatt_convert_uuid32_to_uuid128(p, uuid.uu.uuid32); + p += LEN_UUID_128; + len = LEN_UUID_128; + } else if (uuid.len == LEN_UUID_128) { + ARRAY_TO_STREAM (p, uuid.uu.uuid128, LEN_UUID_128); + len = LEN_UUID_128; + } + + *p_dst = p; + return len; +} + +/******************************************************************************* +** +** Function gatt_parse_uuid_from_cmd +** +** Description Convert a 128 bits UUID into a 16 bits UUID. +** +** Returns TRUE if command sent, otherwise FALSE. +** +*******************************************************************************/ +BOOLEAN gatt_parse_uuid_from_cmd(tBT_UUID *p_uuid_rec, UINT16 uuid_size, UINT8 **p_data) +{ + BOOLEAN is_base_uuid, ret = TRUE; + UINT8 xx; + UINT8 *p_uuid = *p_data; + + memset(p_uuid_rec, 0, sizeof(tBT_UUID)); + + switch (uuid_size) { + case LEN_UUID_16: + p_uuid_rec->len = uuid_size; + STREAM_TO_UINT16 (p_uuid_rec->uu.uuid16, p_uuid); + *p_data += LEN_UUID_16; + break; + + case LEN_UUID_128: + /* See if we can compress his UUID down to 16 or 32bit UUIDs */ + is_base_uuid = TRUE; + for (xx = 0; xx < LEN_UUID_128 - 4; xx++) { + if (p_uuid[xx] != base_uuid[xx]) { + is_base_uuid = FALSE; + break; + } + } + if (is_base_uuid) { + if ((p_uuid[LEN_UUID_128 - 1] == 0) && (p_uuid[LEN_UUID_128 - 2] == 0)) { + p_uuid += (LEN_UUID_128 - 4); + p_uuid_rec->len = LEN_UUID_16; + STREAM_TO_UINT16(p_uuid_rec->uu.uuid16, p_uuid); + } else { + p_uuid += (LEN_UUID_128 - LEN_UUID_32); + p_uuid_rec->len = LEN_UUID_32; + STREAM_TO_UINT32(p_uuid_rec->uu.uuid32, p_uuid); + } + } + if (!is_base_uuid) { + p_uuid_rec->len = LEN_UUID_128; + memcpy(p_uuid_rec->uu.uuid128, p_uuid, LEN_UUID_128); + } + *p_data += LEN_UUID_128; + break; + + /* do not allow 32 bits UUID in ATT PDU now */ + case LEN_UUID_32: + GATT_TRACE_ERROR("DO NOT ALLOW 32 BITS UUID IN ATT PDU"); + case 0: + default: + if (uuid_size != 0) { + ret = FALSE; + } + GATT_TRACE_WARNING("gatt_parse_uuid_from_cmd invalid uuid size"); + break; + } + + return ( ret); +} + +/******************************************************************************* +** +** Function gatt_start_rsp_timer +** +** Description Start a wait_for_response timer. +** +** Returns TRUE if command sent, otherwise FALSE. +** +*******************************************************************************/ +void gatt_start_rsp_timer(UINT16 clcb_idx) +{ + tGATT_CLCB *p_clcb = gatt_clcb_find_by_idx(clcb_idx); + UINT32 timeout = GATT_WAIT_FOR_RSP_TOUT; + p_clcb->rsp_timer_ent.param = (TIMER_PARAM_TYPE)p_clcb; + if (p_clcb->operation == GATTC_OPTYPE_DISCOVERY && + p_clcb->op_subtype == GATT_DISC_SRVC_ALL) { + timeout = GATT_WAIT_FOR_DISC_RSP_TOUT; + } + btu_start_timer (&p_clcb->rsp_timer_ent, BTU_TTYPE_ATT_WAIT_FOR_RSP, + timeout); +} +/******************************************************************************* +** +** Function gatt_start_conf_timer +** +** Description Start a wait_for_confirmation timer. +** +** Returns TRUE if command sent, otherwise FALSE. +** +*******************************************************************************/ +void gatt_start_conf_timer(tGATT_TCB *p_tcb) +{ + p_tcb->conf_timer_ent.param = (TIMER_PARAM_TYPE)p_tcb; + btu_start_timer (&p_tcb->conf_timer_ent, BTU_TTYPE_ATT_WAIT_FOR_RSP, + GATT_WAIT_FOR_RSP_TOUT); +} +/******************************************************************************* +** +** Function gatt_start_ind_ack_timer +** +** Description start the application ack timer +** +** Returns void +** +*******************************************************************************/ +void gatt_start_ind_ack_timer(tGATT_TCB *p_tcb) +{ + p_tcb->ind_ack_timer_ent.param = (TIMER_PARAM_TYPE)p_tcb; + /* start notification cache timer */ + btu_start_timer (&p_tcb->ind_ack_timer_ent, BTU_TTYPE_ATT_WAIT_FOR_IND_ACK, + GATT_WAIT_FOR_IND_ACK_TOUT); + +} +/******************************************************************************* +** +** Function gatt_rsp_timeout +** +** Description Called when GATT wait for ATT command response timer expires +** +** Returns void +** +*******************************************************************************/ +void gatt_rsp_timeout(TIMER_LIST_ENT *p_tle) +{ + tGATT_CLCB *p_clcb = (tGATT_CLCB *)p_tle->param; + if (p_clcb == NULL || p_clcb->p_tcb == NULL) { + GATT_TRACE_WARNING("gatt_rsp_timeout clcb is already deleted"); + return; + } + if (p_clcb->operation == GATTC_OPTYPE_DISCOVERY && + p_clcb->op_subtype == GATT_DISC_SRVC_ALL && + p_clcb->retry_count < GATT_REQ_RETRY_LIMIT) { + UINT8 rsp_code; + GATT_TRACE_WARNING("gatt_rsp_timeout retry discovery primary service"); + if (p_clcb != gatt_cmd_dequeue(p_clcb->p_tcb, &rsp_code)) { + GATT_TRACE_ERROR("gatt_rsp_timeout command queue out of sync, disconnect"); + } else { + p_clcb->retry_count++; +#if (GATTC_INCLUDED == TRUE) + gatt_act_discovery(p_clcb); +#endif ///GATTC_INCLUDED == TRUE + return; + } + } + + GATT_TRACE_WARNING("gatt_rsp_timeout disconnecting..."); + gatt_disconnect (p_clcb->p_tcb); +} + +/******************************************************************************* +** +** Function gatt_ind_ack_timeout +** +** Description Called when GATT wait for ATT handle confirmation timeout +** +** Returns void +** +*******************************************************************************/ +void gatt_ind_ack_timeout(TIMER_LIST_ENT *p_tle) +{ + tGATT_TCB *p_tcb = (tGATT_TCB *)p_tle->param; + + GATT_TRACE_WARNING("gatt_ind_ack_timeout send ack now"); + + if (p_tcb != NULL) { + p_tcb->ind_count = 0; + } + + attp_send_cl_msg(((tGATT_TCB *)p_tle->param), 0, GATT_HANDLE_VALUE_CONF, NULL); +} +/******************************************************************************* +** +** Function gatt_sr_find_i_rcb_by_handle +** +** Description The function searches for a service that owns a specific handle. +** +** Returns GATT_MAX_SR_PROFILES if not found. Otherwise index of th eservice. +** +*******************************************************************************/ +UINT8 gatt_sr_find_i_rcb_by_handle(UINT16 handle) +{ + UINT8 i_rcb = 0; + + for ( ; i_rcb < GATT_MAX_SR_PROFILES; i_rcb++) { + if (gatt_cb.sr_reg[i_rcb].in_use && + gatt_cb.sr_reg[i_rcb].s_hdl <= handle && + gatt_cb.sr_reg[i_rcb].e_hdl >= handle ) { + break; + } + } + return i_rcb; +} + +/******************************************************************************* +** +** Function gatt_sr_find_i_rcb_by_handle +** +** Description The function searches for a service that owns a specific handle. +** +** Returns GATT_MAX_SR_PROFILES if not found. Otherwise index of th eservice. +** +*******************************************************************************/ +#if (GATTS_INCLUDED == TRUE) +UINT8 gatt_sr_find_i_rcb_by_app_id(tBT_UUID *p_app_uuid128, tBT_UUID *p_svc_uuid, UINT16 svc_inst) +{ + UINT8 i_rcb = 0; + tGATT_SR_REG *p_sreg; + tBT_UUID *p_this_uuid; + + for (i_rcb = 0, p_sreg = gatt_cb.sr_reg; i_rcb < GATT_MAX_SR_PROFILES; i_rcb++, p_sreg++) { + if ( p_sreg->in_use ) { + p_this_uuid = gatts_get_service_uuid (p_sreg->p_db); + + if (p_this_uuid && + gatt_uuid_compare (*p_app_uuid128, p_sreg->app_uuid ) && + gatt_uuid_compare (*p_svc_uuid, *p_this_uuid) && + (svc_inst == p_sreg->service_instance)) { + GATT_TRACE_ERROR ("Active Service Found "); + gatt_dbg_display_uuid(*p_svc_uuid); + + break; + } + } + } + return i_rcb; +} +#endif ///GATTS_INCLUDED == TRUE +/******************************************************************************* +** +** Function gatt_sr_find_i_rcb_by_handle +** +** Description The function searches for a service that owns a specific handle. +** +** Returns 0 if not found. Otherwise index of th eservice. +** +*******************************************************************************/ +UINT8 gatt_sr_alloc_rcb(tGATT_HDL_LIST_ELEM *p_list ) +{ + UINT8 ii = 0; + tGATT_SR_REG *p_sreg = NULL; + + /*this is a new application servoce start */ + for (ii = 0, p_sreg = gatt_cb.sr_reg; ii < GATT_MAX_SR_PROFILES; ii++, p_sreg++) { + if (!p_sreg->in_use) { + memset (p_sreg, 0, sizeof(tGATT_SR_REG)); + + p_sreg->in_use = TRUE; + memcpy (&p_sreg->app_uuid, &p_list->asgn_range.app_uuid128, sizeof(tBT_UUID)); + + p_sreg->service_instance = p_list->asgn_range.svc_inst; + p_sreg->type = p_list->asgn_range.is_primary ? GATT_UUID_PRI_SERVICE : GATT_UUID_SEC_SERVICE; + p_sreg->s_hdl = p_list->asgn_range.s_handle; + p_sreg->e_hdl = p_list->asgn_range.e_handle; + p_sreg->p_db = &p_list->svc_db; + + GATT_TRACE_DEBUG ("total buffer in db [%d]", fixed_queue_length(p_sreg->p_db->svc_buffer)); + break; + } + } + + return ii; +} +/******************************************************************************* +** +** Function gatt_sr_get_sec_info +** +** Description Get the security flag and key size information for the peer +** device. +** +** Returns void +** +*******************************************************************************/ +void gatt_sr_get_sec_info(BD_ADDR rem_bda, tBT_TRANSPORT transport, UINT8 *p_sec_flag, UINT8 *p_key_size) +{ + UINT8 sec_flag = 0; + + BTM_GetSecurityFlagsByTransport(rem_bda, &sec_flag, transport); + + sec_flag &= (GATT_SEC_FLAG_LKEY_UNAUTHED | GATT_SEC_FLAG_LKEY_AUTHED | GATT_SEC_FLAG_ENCRYPTED | GATT_SEC_FLAG_AUTHORIZATION); +#if (SMP_INCLUDED == TRUE) + *p_key_size = btm_ble_read_sec_key_size(rem_bda); +#endif ///SMP_INCLUDED == TRUE + *p_sec_flag = sec_flag; +} +/******************************************************************************* +** +** Function gatt_sr_send_req_callback +** +** Description +** +** +** Returns void +** +*******************************************************************************/ +void gatt_sr_send_req_callback(UINT16 conn_id, + UINT32 trans_id, + tGATTS_REQ_TYPE type, tGATTS_DATA *p_data) +{ + tGATT_IF gatt_if = GATT_GET_GATT_IF(conn_id); + tGATT_REG *p_reg = gatt_get_regcb(gatt_if); + + if (!p_reg ) { + GATT_TRACE_ERROR ("p_reg not found discard request"); + return; + } + + if ( p_reg->in_use && + p_reg->app_cb.p_req_cb) { + (*p_reg->app_cb.p_req_cb)(conn_id, trans_id, type, p_data); + } else { + GATT_TRACE_WARNING("Call back not found for application conn_id=%d", conn_id); + } + +} + +/******************************************************************************* +** +** Function gatt_send_error_rsp +** +** Description This function sends an error response. +** +** Returns void +** +*******************************************************************************/ +tGATT_STATUS gatt_send_error_rsp (tGATT_TCB *p_tcb, UINT8 err_code, UINT8 op_code, + UINT16 handle, BOOLEAN deq) +{ + tGATT_ERROR error; + tGATT_STATUS status; + BT_HDR *p_buf; + + error.cmd_code = op_code; + error.reason = err_code; + error.handle = handle; + + if ((p_buf = attp_build_sr_msg(p_tcb, GATT_RSP_ERROR, (tGATT_SR_MSG *)&error)) != NULL) { + status = attp_send_sr_msg (p_tcb, p_buf); + } else { + status = GATT_INSUF_RESOURCE; + } +#if (GATTS_INCLUDED == TRUE) + if (deq) { + gatt_dequeue_sr_cmd(p_tcb); + } +#endif ///GATTS_INCLUDED == TRUE + return status; +} + +#if (SDP_INCLUDED == TRUE && CLASSIC_BT_GATT_INCLUDED == TRUE) +/******************************************************************************* +** +** Function gatt_add_sdp_record +** +** Description This function add a SDP record for a GATT primary service +** +** Returns 0 if error else sdp handle for the record. +** +*******************************************************************************/ +UINT32 gatt_add_sdp_record (tBT_UUID *p_uuid, UINT16 start_hdl, UINT16 end_hdl) +{ + tSDP_PROTOCOL_ELEM proto_elem_list[2]; + UINT32 sdp_handle; + UINT16 list = UUID_SERVCLASS_PUBLIC_BROWSE_GROUP; + UINT8 buff[60]; + UINT8 *p = buff; + + GATT_TRACE_DEBUG("gatt_add_sdp_record s_hdl=0x%x s_hdl=0x%x", start_hdl, end_hdl); + + if ((sdp_handle = SDP_CreateRecord()) == 0) { + return 0; + } + + switch (p_uuid->len) { + case LEN_UUID_16: + SDP_AddServiceClassIdList(sdp_handle, 1, &p_uuid->uu.uuid16); + break; + + case LEN_UUID_32: + UINT8_TO_BE_STREAM (p, (UUID_DESC_TYPE << 3) | SIZE_FOUR_BYTES); + UINT32_TO_BE_STREAM (p, p_uuid->uu.uuid32); + SDP_AddAttribute (sdp_handle, ATTR_ID_SERVICE_CLASS_ID_LIST, DATA_ELE_SEQ_DESC_TYPE, + (UINT32) (p - buff), buff); + break; + + case LEN_UUID_128: + UINT8_TO_BE_STREAM (p, (UUID_DESC_TYPE << 3) | SIZE_SIXTEEN_BYTES); + ARRAY_TO_BE_STREAM_REVERSE (p, p_uuid->uu.uuid128, LEN_UUID_128); + SDP_AddAttribute (sdp_handle, ATTR_ID_SERVICE_CLASS_ID_LIST, DATA_ELE_SEQ_DESC_TYPE, + (UINT32) (p - buff), buff); + break; + + default: + GATT_TRACE_ERROR("inavlid UUID len=%d", p_uuid->len); + SDP_DeleteRecord(sdp_handle); + return 0; + break; + } + + /*** Fill out the protocol element sequence for SDP ***/ + proto_elem_list[0].protocol_uuid = UUID_PROTOCOL_L2CAP; + proto_elem_list[0].num_params = 1; + proto_elem_list[0].params[0] = BT_PSM_ATT; + proto_elem_list[1].protocol_uuid = UUID_PROTOCOL_ATT; + proto_elem_list[1].num_params = 2; + proto_elem_list[1].params[0] = start_hdl; + proto_elem_list[1].params[1] = end_hdl; + + SDP_AddProtocolList(sdp_handle, 2, proto_elem_list); + + /* Make the service browseable */ + SDP_AddUuidSequence (sdp_handle, ATTR_ID_BROWSE_GROUP_LIST, 1, &list); + + return (sdp_handle); +} +#endif ///SDP_INCLUDED == TRUE && CLASSIC_BT_GATT_INCLUDED == TRUE + +#if GATT_CONFORMANCE_TESTING == TRUE +/******************************************************************************* +** +** Function gatt_set_err_rsp +** +** Description This function is called to set the test confirm value +** +** Returns void +** +*******************************************************************************/ +void gatt_set_err_rsp(BOOLEAN enable, UINT8 req_op_code, UINT8 err_status) +{ + GATT_TRACE_DEBUG("gatt_set_err_rsp enable=%d op_code=%d, err_status=%d", enable, req_op_code, err_status); + gatt_cb.enable_err_rsp = enable; + gatt_cb.req_op_code = req_op_code; + gatt_cb.err_status = err_status; +} +#endif + + + +/******************************************************************************* +** +** Function gatt_get_regcb +** +** Description The function returns the registration control block. +** +** Returns pointer to the registration control block or NULL +** +*******************************************************************************/ +tGATT_REG *gatt_get_regcb (tGATT_IF gatt_if) +{ + UINT8 ii = (UINT8)gatt_if; + tGATT_REG *p_reg = NULL; + + if (ii < 1 || ii > GATT_MAX_APPS) { + GATT_TRACE_WARNING("gatt_if out of range [ = %d]", ii); + return NULL; + } + + // Index for cl_rcb is always 1 less than gatt_if. + p_reg = &gatt_cb.cl_rcb[ii - 1]; + + if (!p_reg->in_use) { + GATT_TRACE_WARNING("gatt_if found but not in use.\n"); + return NULL; + } + + return p_reg; +} + + +/******************************************************************************* +** +** Function gatt_is_clcb_allocated +** +** Description The function check clcb for conn_id is allocated or not +** +** Returns True already allocated +** +*******************************************************************************/ + +BOOLEAN gatt_is_clcb_allocated (UINT16 conn_id) +{ + BOOLEAN is_allocated = FALSE; + + tGATT_CLCB *p_clcb = gatt_clcb_find_by_conn_id(conn_id); + if (p_clcb) { + is_allocated = TRUE; + } + return is_allocated; +} + +/******************************************************************************* +** +** Function gatt_clcb_find_by_conn_id +** +** Description Find clcb block using clcb_idx stored at the time of alloc +** +** Returns pointer to clcb corresponding to conn_id +** +*******************************************************************************/ + +tGATT_CLCB *gatt_clcb_find_by_conn_id(UINT16 conn_id) +{ + list_node_t *p_node = NULL; + tGATT_CLCB *p_clcb = NULL; + tGATT_CLCB *p_clcb_ret = NULL; + for(p_node = list_begin(gatt_cb.p_clcb_list); p_node; p_node = list_next(p_node)) { + p_clcb = list_node(p_node); + if (p_clcb->conn_id == conn_id) { + p_clcb_ret = p_clcb; + break; + } + } + return p_clcb_ret; +} + +/******************************************************************************* +** +** Function gatt_clcb_find_by_idx +** +** Description Find clcb block using clcb_idx stored at the time of alloc +** +** Returns pointer to clcb corresponding to clcb_idx +** +*******************************************************************************/ + +tGATT_CLCB *gatt_clcb_find_by_idx(UINT16 clcb_idx) +{ + list_node_t *p_node = NULL; + tGATT_CLCB *p_clcb = NULL; + tGATT_CLCB *p_clcb_ret = NULL; + for(p_node = list_begin(gatt_cb.p_clcb_list); p_node; p_node = list_next(p_node)) { + p_clcb = list_node(p_node); + if (p_clcb->clcb_idx == clcb_idx) { + p_clcb_ret = p_clcb; + break; + } + } + return p_clcb_ret; +} + +/******************************************************************************* +** +** Function gatt_clcb_alloc +** +** Description The function allocates a GATT connection link control block +** +** Returns NULL if not found. Otherwise pointer to the connection link block. +** +*******************************************************************************/ +tGATT_CLCB *gatt_clcb_alloc (UINT16 conn_id) +{ + tGATT_CLCB *p_clcb = NULL; + tGATT_IF gatt_if = GATT_GET_GATT_IF(conn_id); + UINT8 tcb_idx = GATT_GET_TCB_IDX(conn_id); + tGATT_TCB *p_tcb = gatt_get_tcb_by_idx(tcb_idx); + tGATT_REG *p_reg = gatt_get_regcb(gatt_if); + + if (list_length(gatt_cb.p_clcb_list) < GATT_CL_MAX_LCB) { + p_clcb = (tGATT_CLCB *)osi_malloc(sizeof(tGATT_CLCB)); + if (p_clcb) { + list_append(gatt_cb.p_clcb_list, p_clcb); + memset(p_clcb, 0, sizeof(tGATT_CLCB)); + p_clcb->in_use = TRUE; + p_clcb->conn_id = conn_id; + //Add index of the clcb same as conn_id + p_clcb->clcb_idx = conn_id; + p_clcb->p_reg = p_reg; + p_clcb->p_tcb = p_tcb; + } + } + return p_clcb; +} + +/******************************************************************************* +** +** Function gatt_clcb_dealloc +** +** Description The function de allocates a GATT connection link control block +** +** Returns None +** +*******************************************************************************/ +void gatt_clcb_dealloc (tGATT_CLCB *p_clcb) +{ + if (p_clcb && p_clcb->in_use) { + btu_free_timer(&p_clcb->rsp_timer_ent); + memset(p_clcb, 0, sizeof(tGATT_CLCB)); + list_remove(gatt_cb.p_clcb_list, p_clcb); + p_clcb = NULL; + } +} + + + +/******************************************************************************* +** +** Function gatt_find_tcb_by_cid +** +** Description The function searches for an empty entry +** in registration info table for GATT client +** +** Returns NULL if not found. Otherwise pointer to the rcb. +** +*******************************************************************************/ +tGATT_TCB *gatt_find_tcb_by_cid (UINT16 lcid) +{ + tGATT_TCB *p_tcb = NULL; + list_node_t *p_node = NULL; + for(p_node = list_begin(gatt_cb.p_tcb_list); p_node; p_node = list_next(p_node)) { + p_tcb = list_node(p_node); + if (p_tcb->in_use && p_tcb->att_lcid == lcid) { + break; + } + } + return p_tcb; +} + +/******************************************************************************* +** +** Function gatt_num_apps_hold_link +** +** Description The function find the number of applcaitions is holding the link +** +** Returns total number of applications holding this acl link. +** +*******************************************************************************/ +UINT8 gatt_num_apps_hold_link(tGATT_TCB *p_tcb) +{ + UINT8 i, num = 0; + + for (i = 0; i < GATT_MAX_APPS; i ++) { + if (p_tcb->app_hold_link[i]) { + num ++; + } + } + + GATT_TRACE_DEBUG("gatt_num_apps_hold_link num=%d", num); + return num; +} + + +/******************************************************************************* +** +** Function gatt_num_clcb_by_bd_addr +** +** Description The function searches all LCB with macthing bd address +** +** Returns total number of clcb found. +** +*******************************************************************************/ +UINT8 gatt_num_clcb_by_bd_addr(BD_ADDR bda) +{ + UINT8 num = 0; + + list_node_t *p_node = NULL; + tGATT_CLCB *p_clcb = NULL; + for(p_node = list_begin(gatt_cb.p_clcb_list); p_node; p_node = list_next(p_node)) { + p_clcb = list_node(p_node); + if (memcmp(p_clcb->p_tcb->peer_bda, bda, BD_ADDR_LEN) == 0) { + num++; + } + } + return num; +} + +/******************************************************************************* +** +** Function gatt_sr_update_cback_cnt +** +** Description The function searches all LCB with macthing bd address +** +** Returns total number of clcb found. +** +*******************************************************************************/ +void gatt_sr_copy_prep_cnt_to_cback_cnt(tGATT_TCB *p_tcb ) +{ +#if (GATTS_INCLUDED == TRUE) + UINT8 i; + + if (p_tcb) { + for (i = 0; i < GATT_MAX_APPS; i ++) { + if (p_tcb->prep_cnt[i]) { + p_tcb->sr_cmd.cback_cnt[i] = 1; + } + } + } +#endif ///GATTS_INCLUDED == TRUE +} + +/******************************************************************************* +** +** Function gatt_sr_is_cback_cnt_zero +** +** Description The function searches all LCB with macthing bd address +** +** Returns True if thetotal application callback count is zero +** +*******************************************************************************/ +BOOLEAN gatt_sr_is_cback_cnt_zero(tGATT_TCB *p_tcb ) +{ + BOOLEAN status = TRUE; +#if (GATTS_INCLUDED == TRUE) + UINT8 i; + + if (p_tcb) { + for (i = 0; i < GATT_MAX_APPS; i ++) { + if (p_tcb->sr_cmd.cback_cnt[i]) { + status = FALSE; + break; + } + } + } else { + status = FALSE; + } +#endif ///GATTS_INCLUDED == TRUE + return status; +} + +/******************************************************************************* +** +** Function gatt_sr_is_prep_cnt_zero +** +** Description Check the prepare write request count is zero or not +** +** Returns True no prepare write request +** +*******************************************************************************/ +BOOLEAN gatt_sr_is_prep_cnt_zero(tGATT_TCB *p_tcb) +{ + BOOLEAN status = TRUE; + UINT8 i; + + if (p_tcb) { + for (i = 0; i < GATT_MAX_APPS; i ++) { + if (p_tcb->prep_cnt[i]) { + status = FALSE; + break; + } + } + } else { + status = FALSE; + } + return status; +} + + +/******************************************************************************* +** +** Function gatt_sr_reset_cback_cnt +** +** Description Reset the application callback count to zero +** +** Returns None +** +*******************************************************************************/ +void gatt_sr_reset_cback_cnt(tGATT_TCB *p_tcb ) +{ +#if (GATTS_INCLUDED == TRUE) + UINT8 i; + + if (p_tcb) { + for (i = 0; i < GATT_MAX_APPS; i ++) { + p_tcb->sr_cmd.cback_cnt[i] = 0; + } + } +#endif ///GATTS_INCLUDED == TRUE +} + +/******************************************************************************* +** +** Function gatt_sr_reset_prep_cnt +** +** Description Reset the prep write count to zero +** +** Returns None +** +*******************************************************************************/ +void gatt_sr_reset_prep_cnt(tGATT_TCB *p_tcb ) +{ + UINT8 i; + if (p_tcb) { + for (i = 0; i < GATT_MAX_APPS; i ++) { + p_tcb->prep_cnt[i] = 0; + } + } +} + + +/******************************************************************************* +** +** Function gatt_sr_update_cback_cnt +** +** Description Update the teh application callback count +** +** Returns None +** +*******************************************************************************/ +void gatt_sr_update_cback_cnt(tGATT_TCB *p_tcb, tGATT_IF gatt_if, BOOLEAN is_inc, BOOLEAN is_reset_first) +{ +#if (GATTS_INCLUDED == TRUE) + UINT8 idx = ((UINT8) gatt_if) - 1 ; + + if (p_tcb) { + if (is_reset_first) { + gatt_sr_reset_cback_cnt(p_tcb); + } + if (is_inc) { + p_tcb->sr_cmd.cback_cnt[idx]++; + } else { + if ( p_tcb->sr_cmd.cback_cnt[idx]) { + p_tcb->sr_cmd.cback_cnt[idx]--; + } + } + } +#endif ///GATTS_INCLUDED == TRUE +} + + +/******************************************************************************* +** +** Function gatt_sr_update_prep_cnt +** +** Description Update the teh prepare write request count +** +** Returns None +** +*******************************************************************************/ +void gatt_sr_update_prep_cnt(tGATT_TCB *p_tcb, tGATT_IF gatt_if, BOOLEAN is_inc, BOOLEAN is_reset_first) +{ + UINT8 idx = ((UINT8) gatt_if) - 1 ; + + GATT_TRACE_DEBUG("gatt_sr_update_prep_cnt tcb idx=%d gatt_if=%d is_inc=%d is_reset_first=%d", + p_tcb->tcb_idx, gatt_if, is_inc, is_reset_first); + + if (p_tcb) { + if (is_reset_first) { + gatt_sr_reset_prep_cnt(p_tcb); + } + if (is_inc) { + p_tcb->prep_cnt[idx]++; + } else { + if (p_tcb->prep_cnt[idx]) { + p_tcb->prep_cnt[idx]--; + } + } + } +} +/******************************************************************************* +** +** Function gatt_cancel_open +** +** Description Cancel open request +** +** Returns Boolean +** +*******************************************************************************/ +BOOLEAN gatt_cancel_open(tGATT_IF gatt_if, BD_ADDR bda) +{ + tGATT_TCB *p_tcb = NULL; + BOOLEAN status = TRUE; + + p_tcb = gatt_find_tcb_by_addr(bda, BT_TRANSPORT_LE); + + if (p_tcb) { + if (gatt_get_ch_state(p_tcb) == GATT_CH_OPEN) { + GATT_TRACE_ERROR("GATT_CancelConnect - link connected Too late to cancel"); + status = FALSE; + } else { + gatt_update_app_use_link_flag(gatt_if, p_tcb, FALSE, FALSE); + if (!gatt_num_apps_hold_link(p_tcb)) { + gatt_disconnect(p_tcb); + } + } + } + + return status; +} + +/******************************************************************************* +** +** Function gatt_find_app_hold_link +** +** Description find the application that is holding the specified link +** +** Returns Boolean +** +*******************************************************************************/ +BOOLEAN gatt_find_app_hold_link(tGATT_TCB *p_tcb, UINT8 start_idx, UINT8 *p_found_idx, tGATT_IF *p_gatt_if) +{ + UINT8 i; + BOOLEAN found = FALSE; + + for (i = start_idx; i < GATT_MAX_APPS; i ++) { + if (p_tcb->app_hold_link[i]) { + *p_gatt_if = p_tcb->app_hold_link[i]; + *p_found_idx = i; + found = TRUE; + break; + } + } + return found; +} + +/******************************************************************************* +** +** Function gatt_find_specific_app_in_hold_link +** +** Description find the specific application that is holding the specified link +** +** Returns Boolean +** +*******************************************************************************/ +BOOLEAN gatt_find_specific_app_in_hold_link(tGATT_TCB *p_tcb, tGATT_IF p_gatt_if) +{ + UINT8 i; + BOOLEAN found = FALSE; + + for (i = 0; i < GATT_MAX_APPS; i ++) { + if (p_tcb->app_hold_link[i] && p_tcb->app_hold_link[i] == p_gatt_if) { + found = TRUE; + break; + } + } + return found; +} + +/******************************************************************************* +** +** Function gatt_cmd_enq +** +** Description Enqueue this command. +** +** Returns None. +** +*******************************************************************************/ +BOOLEAN gatt_cmd_enq(tGATT_TCB *p_tcb, UINT16 clcb_idx, BOOLEAN to_send, UINT8 op_code, BT_HDR *p_buf) +{ + tGATT_CMD_Q *p_cmd = &p_tcb->cl_cmd_q[p_tcb->next_slot_inq]; + + p_cmd->to_send = to_send; /* waiting to be sent */ + p_cmd->op_code = op_code; + p_cmd->p_cmd = p_buf; + p_cmd->clcb_idx = clcb_idx; + + if (!to_send) { + p_tcb->pending_cl_req = p_tcb->next_slot_inq; + } + + p_tcb->next_slot_inq ++; + p_tcb->next_slot_inq %= GATT_CL_MAX_LCB; + + return TRUE; +} + +/******************************************************************************* +** +** Function gatt_cmd_dequeue +** +** Description dequeue the command in the client CCB command queue. +** +** Returns total number of clcb found. +** +*******************************************************************************/ +tGATT_CLCB *gatt_cmd_dequeue(tGATT_TCB *p_tcb, UINT8 *p_op_code) +{ + tGATT_CMD_Q *p_cmd = &p_tcb->cl_cmd_q[p_tcb->pending_cl_req]; + tGATT_CLCB *p_clcb = NULL; + + if (p_tcb->pending_cl_req != p_tcb->next_slot_inq) { + p_clcb = gatt_clcb_find_by_idx(p_cmd->clcb_idx); + + *p_op_code = p_cmd->op_code; + + p_tcb->pending_cl_req ++; + p_tcb->pending_cl_req %= GATT_CL_MAX_LCB; + } + + return p_clcb; +} + +/******************************************************************************* +** +** Function gatt_send_write_msg +** +** Description This real function send out the ATT message for write. +** +** Returns status code +** +*******************************************************************************/ +UINT8 gatt_send_write_msg (tGATT_TCB *p_tcb, UINT16 clcb_idx, UINT8 op_code, + UINT16 handle, UINT16 len, + UINT16 offset, UINT8 *p_data) +{ + tGATT_CL_MSG msg; + + msg.attr_value.handle = handle; + msg.attr_value.len = len; + msg.attr_value.offset = offset; + + memcpy (msg.attr_value.value, p_data, len); + + /* write by handle */ + return attp_send_cl_msg(p_tcb, clcb_idx, op_code, &msg); +} + +/******************************************************************************* +** +** Function gatt_act_send_browse +** +** Description This function ends a browse command request, including read +** information request and read by type request. +** +** Returns status code +** +*******************************************************************************/ +UINT8 gatt_act_send_browse(tGATT_TCB *p_tcb, UINT16 index, UINT8 op, UINT16 s_handle, + UINT16 e_handle, tBT_UUID uuid) +{ + tGATT_CL_MSG msg; + + msg.browse.s_handle = s_handle; + msg.browse.e_handle = e_handle; + memcpy(&msg.browse.uuid, &uuid, sizeof(tBT_UUID)); + + /* write by handle */ + return attp_send_cl_msg(p_tcb, index, op, &msg); +} + +/******************************************************************************* +** +** Function gatt_end_operation +** +** Description This function ends a discovery, send callback and finalize +** some control value. +** +** Returns 16 bits uuid. +** +*******************************************************************************/ +void gatt_end_operation(tGATT_CLCB *p_clcb, tGATT_STATUS status, void *p_data) +{ + tGATT_CL_COMPLETE cb_data; + tGATT_CMPL_CBACK *p_cmpl_cb = (p_clcb->p_reg) ? p_clcb->p_reg->app_cb.p_cmpl_cb : NULL; + UINT8 op = p_clcb->operation, disc_type = GATT_DISC_MAX; + tGATT_DISC_CMPL_CB *p_disc_cmpl_cb = (p_clcb->p_reg) ? p_clcb->p_reg->app_cb.p_disc_cmpl_cb : NULL; + UINT16 conn_id; +#if (!CONFIG_BT_STACK_NO_LOG) + UINT8 operation; +#endif + + GATT_TRACE_DEBUG ("gatt_end_operation status=%d op=%d subtype=%d", + status, p_clcb->operation, p_clcb->op_subtype); + memset(&cb_data.att_value, 0, sizeof(tGATT_VALUE)); + + if (p_cmpl_cb != NULL && p_clcb->operation != 0) { + if (p_clcb->operation == GATTC_OPTYPE_READ) { + cb_data.att_value.handle = p_clcb->s_handle; + cb_data.att_value.len = p_clcb->counter; + + if (p_data && p_clcb->counter) { + memcpy (cb_data.att_value.value, p_data, cb_data.att_value.len); + } + } + + if (p_clcb->operation == GATTC_OPTYPE_WRITE) { + memset(&cb_data.att_value, 0, sizeof(tGATT_VALUE)); + cb_data.handle = + cb_data.att_value.handle = p_clcb->s_handle; + if (p_clcb->op_subtype == GATT_WRITE_PREPARE) { + if (p_data) { + cb_data.att_value = *((tGATT_VALUE *) p_data); + } else { + GATT_TRACE_DEBUG("Rcv Prepare write rsp but no data"); + } + } + } + + if (p_clcb->operation == GATTC_OPTYPE_CONFIG) { + cb_data.mtu = p_clcb->p_tcb->payload_size; + } + + if (p_clcb->operation == GATTC_OPTYPE_DISCOVERY) { + disc_type = p_clcb->op_subtype; + } + } + + if (p_clcb->p_attr_buf) { + osi_free(p_clcb->p_attr_buf); + } + +#if !CONFIG_BT_STACK_NO_LOG + operation = p_clcb->operation; +#endif + + conn_id = p_clcb->conn_id; + btu_stop_timer(&p_clcb->rsp_timer_ent); + + gatt_clcb_dealloc(p_clcb); + + if (p_disc_cmpl_cb && (op == GATTC_OPTYPE_DISCOVERY)) { + (*p_disc_cmpl_cb)(conn_id, disc_type, status); + } else if (p_cmpl_cb && op) { + (*p_cmpl_cb)(conn_id, op, status, &cb_data); + } else { + GATT_TRACE_WARNING ("gatt_end_operation not sent out op=%d p_disc_cmpl_cb:%p p_cmpl_cb:%p", + operation, p_disc_cmpl_cb, p_cmpl_cb); + } +} + +/******************************************************************************* +** +** Function gatt_cleanup_upon_disc +** +** Description This function cleans up the control blocks when L2CAP channel +** disconnect. +** +** Returns 16 bits uuid. +** +*******************************************************************************/ +void gatt_cleanup_upon_disc(BD_ADDR bda, UINT16 reason, tBT_TRANSPORT transport) +{ + tGATT_TCB *p_tcb = NULL; + tGATT_CLCB *p_clcb; + UINT8 i; + UINT16 conn_id; + tGATT_REG *p_reg = NULL; + + + GATT_TRACE_DEBUG ("gatt_cleanup_upon_disc "); + + if ((p_tcb = gatt_find_tcb_by_addr(bda, transport)) != NULL) { + GATT_TRACE_DEBUG ("found p_tcb "); + gatt_set_ch_state(p_tcb, GATT_CH_CLOSE); + list_node_t *p_node = NULL; + list_node_t *p_node_next = NULL; + for(p_node = list_begin(gatt_cb.p_clcb_list); p_node; p_node = p_node_next) { + p_clcb = list_node(p_node); + p_node_next = list_next(p_node); + if (p_clcb->in_use && p_clcb->p_tcb == p_tcb) { + btu_stop_timer(&p_clcb->rsp_timer_ent); + GATT_TRACE_DEBUG ("found p_clcb conn_id=%d clcb_idx=%d", p_clcb->conn_id, p_clcb->clcb_idx); + if (p_clcb->operation != GATTC_OPTYPE_NONE) { + gatt_end_operation(p_clcb, GATT_ERROR, NULL); + p_clcb = NULL; + } + gatt_clcb_dealloc(p_clcb); + } + } + + btu_free_timer (&p_tcb->ind_ack_timer_ent); + btu_free_timer (&p_tcb->conf_timer_ent); + gatt_free_pending_ind(p_tcb); + gatt_free_pending_enc_queue(p_tcb); + gatt_free_pending_prepare_write_queue(p_tcb); +#if (GATTS_INCLUDED) + fixed_queue_free(p_tcb->sr_cmd.multi_rsp_q, osi_free_func); + p_tcb->sr_cmd.multi_rsp_q = NULL; +#endif /* #if (GATTS_INCLUDED) */ + for (i = 0; i < GATT_MAX_APPS; i ++) { + p_reg = &gatt_cb.cl_rcb[i]; + if (p_reg->in_use && p_reg->app_cb.p_conn_cb) { + conn_id = GATT_CREATE_CONN_ID(p_tcb->tcb_idx, p_reg->gatt_if); + GATT_TRACE_DEBUG ("found p_reg tcb_idx=%d gatt_if=%d conn_id=0x%x", p_tcb->tcb_idx, p_reg->gatt_if, conn_id); + (*p_reg->app_cb.p_conn_cb)(p_reg->gatt_if, bda, conn_id, FALSE, reason, transport); + } + } + gatt_tcb_free(p_tcb); + } else { + GATT_TRACE_DEBUG ("exit gatt_cleanup_upon_disc "); + BTM_Recovery_Pre_State(); + } + gatt_delete_dev_from_srv_chg_clt_list(bda); +} +/******************************************************************************* +** +** Function gatt_dbg_req_op_name +** +** Description Get op code description name, for debug information. +** +** Returns UINT8 *: name of the operation. +** +*******************************************************************************/ +UINT8 *gatt_dbg_op_name(UINT8 op_code) +{ + UINT8 pseduo_op_code_idx = op_code & (~GATT_WRITE_CMD_MASK); + + if (op_code == GATT_CMD_WRITE ) { + pseduo_op_code_idx = 0x14; /* just an index to op_code_name */ + + } + + if (op_code == GATT_SIGN_CMD_WRITE) { + pseduo_op_code_idx = 0x15; /* just an index to op_code_name */ + } + + if (pseduo_op_code_idx <= GATT_OP_CODE_MAX) { + return (UINT8 *) op_code_name[pseduo_op_code_idx]; + } else { + return (UINT8 *)"Op Code Exceed Max"; + } +} + +/******************************************************************************* +** +** Function gatt_dbg_display_uuid +** +** Description Disaplay the UUID +** +** Returns None +** +*******************************************************************************/ +void gatt_dbg_display_uuid(tBT_UUID bt_uuid) +{ + char str_buf[50]; + int x = 0; + + if (bt_uuid.len == LEN_UUID_16) { + sprintf(str_buf, "0x%04x", bt_uuid.uu.uuid16); + } else if (bt_uuid.len == LEN_UUID_32) { + sprintf(str_buf, "0x%08x", (unsigned int)bt_uuid.uu.uuid32); + } else if (bt_uuid.len == LEN_UUID_128) { + x += sprintf(&str_buf[x], "0x%02x%02x%02x%02x%02x%02x%02x%02x", + bt_uuid.uu.uuid128[15], bt_uuid.uu.uuid128[14], + bt_uuid.uu.uuid128[13], bt_uuid.uu.uuid128[12], + bt_uuid.uu.uuid128[11], bt_uuid.uu.uuid128[10], + bt_uuid.uu.uuid128[9], bt_uuid.uu.uuid128[8]); + sprintf(&str_buf[x], "%02x%02x%02x%02x%02x%02x%02x%02x", + bt_uuid.uu.uuid128[7], bt_uuid.uu.uuid128[6], + bt_uuid.uu.uuid128[5], bt_uuid.uu.uuid128[4], + bt_uuid.uu.uuid128[3], bt_uuid.uu.uuid128[2], + bt_uuid.uu.uuid128[1], bt_uuid.uu.uuid128[0]); + } else { + BCM_STRNCPY_S(str_buf, "Unknown UUID 0", 15); + } + + GATT_TRACE_DEBUG ("UUID=[%s]", str_buf); + +} + + +/******************************************************************************* +** +** Function gatt_is_bg_dev_for_app +** +** Description find is this one of the background devices for the application +** +** Returns TRUE this is one of the background devices for the application +** +*******************************************************************************/ +BOOLEAN gatt_is_bg_dev_for_app(tGATT_BG_CONN_DEV *p_dev, tGATT_IF gatt_if) +{ + UINT8 i; + + for (i = 0; i < GATT_MAX_APPS; i ++ ) { + if (p_dev->in_use && (p_dev->gatt_if[i] == gatt_if)) { + return TRUE; + } + } + return FALSE; +} +/******************************************************************************* +** +** Function gatt_find_bg_dev +** +** Description find background connection device from the list. +** +** Returns pointer to the device record +** +*******************************************************************************/ +tGATT_BG_CONN_DEV *gatt_find_bg_dev(BD_ADDR remote_bda) +{ + tGATT_BG_CONN_DEV *p_dev_list = &gatt_cb.bgconn_dev[0]; + UINT8 i; + + for (i = 0; i < GATT_MAX_BG_CONN_DEV; i ++, p_dev_list ++) { + if (p_dev_list->in_use && !memcmp(p_dev_list->remote_bda, remote_bda, BD_ADDR_LEN)) { + return p_dev_list; + } + } + return NULL; +} +/******************************************************************************* +** +** Function gatt_alloc_bg_dev +** +** Description allocate a background connection device record +** +** Returns pointer to the device record +** +*******************************************************************************/ +tGATT_BG_CONN_DEV *gatt_alloc_bg_dev(BD_ADDR remote_bda) +{ + tGATT_BG_CONN_DEV *p_dev_list = &gatt_cb.bgconn_dev[0]; + UINT8 i; + + for (i = 0; i < GATT_MAX_BG_CONN_DEV; i ++, p_dev_list ++) { + if (!p_dev_list->in_use) { + p_dev_list->in_use = TRUE; + memcpy(p_dev_list->remote_bda, remote_bda, BD_ADDR_LEN); + + return p_dev_list; + } + } + return NULL; +} + +/******************************************************************************* +** +** Function gatt_add_bg_dev_list +** +** Description add/remove device from the background connection device list +** +** Returns TRUE if device added to the list; FALSE failed +** +*******************************************************************************/ +BOOLEAN gatt_add_bg_dev_list(tGATT_REG *p_reg, BD_ADDR bd_addr, BOOLEAN is_initator) +{ + tGATT_IF gatt_if = p_reg->gatt_if; + tGATT_BG_CONN_DEV *p_dev = NULL; + UINT8 i; + BOOLEAN ret = FALSE; + + if ((p_dev = gatt_find_bg_dev(bd_addr)) == NULL) { + p_dev = gatt_alloc_bg_dev(bd_addr); + } + + if (p_dev) { + for (i = 0; i < GATT_MAX_APPS; i ++) { + if (is_initator) { + if (p_dev->gatt_if[i] == gatt_if) { + GATT_TRACE_ERROR("device already in iniator white list"); + return TRUE; + } else if (p_dev->gatt_if[i] == 0) { + p_dev->gatt_if[i] = gatt_if; + if (i == 0) { + ret = BTM_BleUpdateBgConnDev(TRUE, bd_addr); + } else { + ret = TRUE; + } + break; + } + } else { + if (p_dev->listen_gif[i] == gatt_if) { + GATT_TRACE_ERROR("device already in adv white list"); + return TRUE; + } else if (p_dev->listen_gif[i] == 0) { + if (p_reg->listening == GATT_LISTEN_TO_ALL) { + p_reg->listening = GATT_LISTEN_TO_NONE; + } + + p_reg->listening ++; + p_dev->listen_gif[i] = gatt_if; + + if (i == 0) { + // To check, we do not support background connection, code will not be called here + ret = BTM_BleUpdateAdvWhitelist(TRUE, bd_addr, 0, NULL); + } else { + ret = TRUE; + } + break; + } + } + } + } else { + GATT_TRACE_ERROR("no device record available"); + } + + return ret; +} + +/******************************************************************************* +** +** Function gatt_remove_bg_dev_for_app +** +** Description Remove the application interface for the specified background device +** +** Returns Boolean +** +*******************************************************************************/ +BOOLEAN gatt_remove_bg_dev_for_app(tGATT_IF gatt_if, BD_ADDR bd_addr) +{ + tGATT_TCB *p_tcb = gatt_find_tcb_by_addr(bd_addr, BT_TRANSPORT_LE); + BOOLEAN status; + + if (p_tcb) { + gatt_update_app_use_link_flag(gatt_if, p_tcb, FALSE, FALSE); + } + status = gatt_update_auto_connect_dev(gatt_if, FALSE, bd_addr, TRUE); + return status; +} + + +/******************************************************************************* +** +** Function gatt_get_num_apps_for_bg_dev +** +** Description Gte the number of applciations for the specified background device +** +** Returns UINT8 total number fo applications +** +*******************************************************************************/ +UINT8 gatt_get_num_apps_for_bg_dev(BD_ADDR bd_addr) +{ + tGATT_BG_CONN_DEV *p_dev = NULL; + UINT8 i; + UINT8 cnt = 0; + + if ((p_dev = gatt_find_bg_dev(bd_addr)) != NULL) { + for (i = 0; i < GATT_MAX_APPS; i ++) { + if (p_dev->gatt_if[i]) { + cnt++; + } + } + } + return cnt; +} + +/******************************************************************************* +** +** Function gatt_find_app_for_bg_dev +** +** Description find the application interface for the specified background device +** +** Returns Boolean +** +*******************************************************************************/ +BOOLEAN gatt_find_app_for_bg_dev(BD_ADDR bd_addr, tGATT_IF *p_gatt_if) +{ + tGATT_BG_CONN_DEV *p_dev = NULL; + UINT8 i; + BOOLEAN ret = FALSE; + + if ((p_dev = gatt_find_bg_dev(bd_addr)) == NULL) { + return ret; + } + + for (i = 0; i < GATT_MAX_APPS; i ++) { + if (p_dev->gatt_if[i] != 0 ) { + *p_gatt_if = p_dev->gatt_if[i]; + ret = TRUE; + break; + } + } + return ret; +} + + +/******************************************************************************* +** +** Function gatt_remove_bg_dev_from_list +** +** Description add/remove device from the background connection device list or +** listening to advertising list. +** +** Returns pointer to the device record +** +*******************************************************************************/ +BOOLEAN gatt_remove_bg_dev_from_list(tGATT_REG *p_reg, BD_ADDR bd_addr, BOOLEAN is_initiator) +{ + tGATT_IF gatt_if = p_reg->gatt_if; + tGATT_BG_CONN_DEV *p_dev = NULL; + UINT8 i, j; + BOOLEAN ret = FALSE; + + if ((p_dev = gatt_find_bg_dev(bd_addr)) == NULL) { + return ret; + } + + for (i = 0; i < GATT_MAX_APPS && (p_dev->gatt_if[i] > 0 || p_dev->listen_gif[i]); i ++) { + if (is_initiator) { + if (p_dev->gatt_if[i] == gatt_if) { + p_dev->gatt_if[i] = 0; + /* move all element behind one forward */ + for (j = i + 1; j < GATT_MAX_APPS; j ++) { + p_dev->gatt_if[j - 1] = p_dev->gatt_if[j]; + } + + if (p_dev->gatt_if[0] == 0) { + ret = BTM_BleUpdateBgConnDev(FALSE, p_dev->remote_bda); + } else { + ret = TRUE; + } + + break; + } + } else { + if (p_dev->listen_gif[i] == gatt_if) { + p_dev->listen_gif[i] = 0; + p_reg->listening --; + /* move all element behind one forward */ + for (j = i + 1; j < GATT_MAX_APPS; j ++) { + p_dev->listen_gif[j - 1] = p_dev->listen_gif[j]; + } + + if (p_dev->listen_gif[0] == 0) { + // To check, we do not support background connection, code will not be called here + ret = BTM_BleUpdateAdvWhitelist(FALSE, p_dev->remote_bda, 0, NULL); + } else { + ret = TRUE; + } + break; + } + } + } + + if (i != GATT_MAX_APPS && p_dev->gatt_if[0] == 0 && p_dev->listen_gif[0] == 0) { + memset(p_dev, 0, sizeof(tGATT_BG_CONN_DEV)); + } + + return ret; +} +/******************************************************************************* +** +** Function gatt_deregister_bgdev_list +** +** Description deregister all related background connection device. +** +** Returns pointer to the device record +** +*******************************************************************************/ +void gatt_deregister_bgdev_list(tGATT_IF gatt_if) +{ + tGATT_BG_CONN_DEV *p_dev_list = &gatt_cb.bgconn_dev[0]; + UINT8 i , j, k; + tGATT_REG *p_reg = gatt_get_regcb(gatt_if); + + /* update the BG conn device list */ + for (i = 0 ; i < GATT_MAX_BG_CONN_DEV; i ++, p_dev_list ++ ) { + if (p_dev_list->in_use) { + for (j = 0; j < GATT_MAX_APPS; j ++) { + if (p_dev_list->gatt_if[j] == 0 && p_dev_list->listen_gif[j] == 0) { + break; + } + + if (p_dev_list->gatt_if[j] == gatt_if) { + for (k = j + 1; k < GATT_MAX_APPS; k ++) { + p_dev_list->gatt_if[k - 1] = p_dev_list->gatt_if[k]; + } + + if (p_dev_list->gatt_if[0] == 0) { + BTM_BleUpdateBgConnDev(FALSE, p_dev_list->remote_bda); + } + } + + if (p_dev_list->listen_gif[j] == gatt_if) { + p_dev_list->listen_gif[j] = 0; + + if (p_reg != NULL && p_reg->listening > 0) { + p_reg->listening --; + } + + /* move all element behind one forward */ + for (k = j + 1; k < GATT_MAX_APPS; k ++) { + p_dev_list->listen_gif[k - 1] = p_dev_list->listen_gif[k]; + } + + if (p_dev_list->listen_gif[0] == 0) { + // To check, we do not support background connection, code will not be called here + BTM_BleUpdateAdvWhitelist(FALSE, p_dev_list->remote_bda, 0, NULL); + } + } + } + } + } +} + + +/******************************************************************************* +** +** Function gatt_reset_bgdev_list +** +** Description reset bg device list +** +** Returns pointer to the device record +** +*******************************************************************************/ +void gatt_reset_bgdev_list(void) +{ + memset(&gatt_cb.bgconn_dev, 0 , sizeof(tGATT_BG_CONN_DEV)*GATT_MAX_BG_CONN_DEV); + +} +/******************************************************************************* +** +** Function gatt_update_auto_connect_dev +** +** Description This function add or remove a device for background connection +** procedure. +** +** Parameters gatt_if: Application ID. +** add: add peer device +** bd_addr: peer device address. +** +** Returns TRUE if connection started; FALSE if connection start failure. +** +*******************************************************************************/ +BOOLEAN gatt_update_auto_connect_dev (tGATT_IF gatt_if, BOOLEAN add, BD_ADDR bd_addr, BOOLEAN is_initator) +{ + BOOLEAN ret = FALSE; + tGATT_REG *p_reg; + tGATT_TCB *p_tcb = gatt_find_tcb_by_addr(bd_addr, BT_TRANSPORT_LE); + + GATT_TRACE_API ("gatt_update_auto_connect_dev "); + /* Make sure app is registered */ + if ((p_reg = gatt_get_regcb(gatt_if)) == NULL) { + GATT_TRACE_ERROR("gatt_update_auto_connect_dev - gatt_if %d is not registered", gatt_if); + return (FALSE); + } + + if (add) { + ret = gatt_add_bg_dev_list(p_reg, bd_addr, is_initator); + + if (ret && p_tcb != NULL) { + /* if a connected device, update the link holding number */ + gatt_update_app_use_link_flag(gatt_if, p_tcb, TRUE, TRUE); + } + } else { + ret = gatt_remove_bg_dev_from_list(p_reg, bd_addr, is_initator); + } + return ret; +} + + + +/******************************************************************************* +** +** Function gatt_add_pending_new_srv_start +** +** Description Add a pending new srv start to the new service start queue +** +** Returns Pointer to the new service start buffer, NULL no buffer available +** +*******************************************************************************/ +tGATT_PENDING_ENC_CLCB *gatt_add_pending_enc_channel_clcb(tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb ) +{ + tGATT_PENDING_ENC_CLCB *p_buf; + + GATT_TRACE_DEBUG ("gatt_add_pending_new_srv_start"); + if ((p_buf = (tGATT_PENDING_ENC_CLCB *)osi_malloc((UINT16)sizeof(tGATT_PENDING_ENC_CLCB))) != NULL) { + GATT_TRACE_DEBUG ("enqueue a new pending encryption channel clcb"); + p_buf->p_clcb = p_clcb; + fixed_queue_enqueue(p_tcb->pending_enc_clcb, p_buf, FIXED_QUEUE_MAX_TIMEOUT); + } + return p_buf; +} +/******************************************************************************* +** +** Function gatt_update_listen_mode +** +** Description update peripheral role listening mode +** +** Returns Pointer to the new service start buffer, NULL no buffer available +** +*******************************************************************************/ +BOOLEAN gatt_update_listen_mode(void) +{ + UINT8 ii = 0; + tGATT_REG *p_reg = &gatt_cb.cl_rcb[0]; + UINT8 listening = 0; + UINT16 connectability, window, interval; + BOOLEAN rt = TRUE; + + for (; ii < GATT_MAX_APPS; ii ++, p_reg ++) { + if ( p_reg->in_use && p_reg->listening > listening) { + listening = p_reg->listening; + } + } + + if (listening == GATT_LISTEN_TO_ALL || + listening == GATT_LISTEN_TO_NONE) { + BTM_BleUpdateAdvFilterPolicy (AP_SCAN_CONN_ALL); + } else { + BTM_BleUpdateAdvFilterPolicy (AP_SCAN_CONN_WL); + } + + if (rt) { + connectability = BTM_ReadConnectability (&window, &interval); + + if (listening != GATT_LISTEN_TO_NONE) { + connectability |= BTM_BLE_CONNECTABLE; + } else { + if ((connectability & BTM_BLE_CONNECTABLE) == 0) { + connectability &= ~BTM_BLE_CONNECTABLE; + } + } + /* turning on the adv now */ + btm_ble_set_connectability(connectability); + } + + return rt; + +} + +char *gatt_uuid_to_str(const tBT_UUID *uuid) +{ + static char dst[48] = {0}; + const UINT8 *u8p; + + memset(dst, 0, sizeof(dst)); + + switch (uuid->len) { + case LEN_UUID_16: + sprintf(dst, "0x%04x", uuid->uu.uuid16); + break; + case LEN_UUID_32: + sprintf(dst, "0x%08x", uuid->uu.uuid32); + break; + case LEN_UUID_128: + u8p = uuid->uu.uuid128; + + sprintf(dst, "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-" + "%02x%02x%02x%02x%02x%02x", + u8p[15], u8p[14], u8p[13], u8p[12], + u8p[11], u8p[10], u8p[9], u8p[8], + u8p[7], u8p[6], u8p[5], u8p[4], + u8p[3], u8p[2], u8p[1], u8p[0]); + break; + default: + dst[0] = '\0'; + break; + } + + return dst; +} +#endif diff --git a/lib/bt/host/bluedroid/stack/gatt/include/gatt_int.h b/lib/bt/host/bluedroid/stack/gatt/include/gatt_int.h new file mode 100644 index 00000000..1161da62 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/gatt/include/gatt_int.h @@ -0,0 +1,788 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +#ifndef GATT_INT_H +#define GATT_INT_H + +#include "common/bt_target.h" +#include "common/bt_trace.h" +#include "stack/gatt_api.h" +#include "stack/btm_ble_api.h" +#include "stack/btu.h" +#include "osi/fixed_queue.h" + +#include + + +#define GATT_CREATE_CONN_ID(tcb_idx, gatt_if) ((UINT16) ((((UINT8)(tcb_idx) ) << 8) | ((UINT8) (gatt_if)))) +#define GATT_GET_TCB_IDX(conn_id) ((UINT8) (((UINT16) (conn_id)) >> 8)) +#define GATT_GET_GATT_IF(conn_id) ((tGATT_IF)((UINT8) (conn_id))) + +#define GATT_GET_SR_REG_PTR(index) (&gatt_cb.sr_reg[(UINT8) (index)]); +#define GATT_TRANS_ID_MAX 0x0fffffff /* 4 MSB is reserved */ +#define GATT_RSP_BY_APP 0x00 +#define GATT_RSP_BY_STACK 0x01 +#define GATT_RSP_DEFAULT GATT_RSP_BY_APP //need to rsp by the app. + +/* security action for GATT write and read request */ +#define GATT_SEC_NONE 0 +#define GATT_SEC_OK 1 +#define GATT_SEC_SIGN_DATA 2 /* compute the signature for the write cmd */ +#define GATT_SEC_ENCRYPT 3 /* encrypt the link with current key */ +#define GATT_SEC_ENCRYPT_NO_MITM 4 /* unauthenticated encryption or better */ +#define GATT_SEC_ENCRYPT_MITM 5 /* authenticated encryption */ +#define GATT_SEC_ENC_PENDING 6 /* wait for link encryption pending */ +typedef UINT8 tGATT_SEC_ACTION; + + +#define GATT_ATTR_OP_SPT_MTU (0x00000001 << 0) +#define GATT_ATTR_OP_SPT_FIND_INFO (0x00000001 << 1) +#define GATT_ATTR_OP_SPT_FIND_BY_TYPE (0x00000001 << 2) +#define GATT_ATTR_OP_SPT_READ_BY_TYPE (0x00000001 << 3) +#define GATT_ATTR_OP_SPT_READ (0x00000001 << 4) +#define GATT_ATTR_OP_SPT_MULT_READ (0x00000001 << 5) +#define GATT_ATTR_OP_SPT_READ_BLOB (0x00000001 << 6) +#define GATT_ATTR_OP_SPT_READ_BY_GRP_TYPE (0x00000001 << 7) +#define GATT_ATTR_OP_SPT_WRITE (0x00000001 << 8) +#define GATT_ATTR_OP_SPT_WRITE_CMD (0x00000001 << 9) +#define GATT_ATTR_OP_SPT_PREP_WRITE (0x00000001 << 10) +#define GATT_ATTR_OP_SPT_EXE_WRITE (0x00000001 << 11) +#define GATT_ATTR_OP_SPT_HDL_VALUE_CONF (0x00000001 << 12) +#define GATT_ATTR_OP_SP_SIGN_WRITE (0x00000001 << 13) + +#define GATT_INDEX_INVALID 0xff + +#define GATT_PENDING_REQ_NONE 0 + + +#define GATT_WRITE_CMD_MASK 0xc0 /*0x1100-0000*/ +#define GATT_AUTH_SIGN_MASK 0x80 /*0x1000-0000*/ +#define GATT_AUTH_SIGN_LEN 12 + +#define GATT_HDR_SIZE 3 /* 1B opcode + 2B handle */ + +/* wait for ATT cmd response timeout value */ +#define GATT_WAIT_FOR_RSP_TOUT 30 +#define GATT_WAIT_FOR_DISC_RSP_TOUT 15 +#define GATT_REQ_RETRY_LIMIT 2 +#define GATT_WAIT_FOR_IND_ACK_TOUT 5 + +/* characteristic descriptor type */ +#define GATT_DESCR_EXT_DSCPTOR 1 /* Characteristic Extended Properties */ +#define GATT_DESCR_USER_DSCPTOR 2 /* Characteristic User Description */ +#define GATT_DESCR_CLT_CONFIG 3 /* Client Characteristic Configuration */ +#define GATT_DESCR_SVR_CONFIG 4 /* Server Characteristic Configuration */ +#define GATT_DESCR_PRES_FORMAT 5 /* Characteristic Presentation Format */ +#define GATT_DESCR_AGGR_FORMAT 6 /* Characteristic Aggregate Format */ +#define GATT_DESCR_VALID_RANGE 7 /* Characteristic Valid Range */ +#define GATT_DESCR_UNKNOWN 0xff + +#define GATT_SEC_FLAG_LKEY_UNAUTHED BTM_SEC_FLAG_LKEY_KNOWN +#define GATT_SEC_FLAG_LKEY_AUTHED BTM_SEC_FLAG_LKEY_AUTHED +#define GATT_SEC_FLAG_ENCRYPTED BTM_SEC_FLAG_ENCRYPTED +#define GATT_SEC_FLAG_AUTHORIZATION BTM_SEC_FLAG_AUTHORIZED +typedef UINT8 tGATT_SEC_FLAG; + +/* Find Information Response Type +*/ +#define GATT_INFO_TYPE_PAIR_16 0x01 +#define GATT_INFO_TYPE_PAIR_128 0x02 + +#define GATTS_SEND_SERVICE_CHANGE_AUTO 0 +#define GATTS_SEND_SERVICE_CHANGE_MANUAL 1 + +/* GATT client FIND_TYPE_VALUE_Request data */ +typedef struct { + tBT_UUID uuid; /* type of attribute to be found */ + UINT16 s_handle; /* starting handle */ + UINT16 e_handle; /* ending handle */ + UINT16 value_len; /* length of the attribute value */ + UINT8 value[GATT_MAX_MTU_SIZE]; /* pointer to the attribute value to be found */ +} tGATT_FIND_TYPE_VALUE; + +/* client request message to ATT protocol +*/ +typedef union { + tGATT_READ_BY_TYPE browse; /* read by type request */ + tGATT_FIND_TYPE_VALUE find_type_value;/* find by type value */ + tGATT_READ_MULTI read_multi; /* read multiple request */ + tGATT_READ_PARTIAL read_blob; /* read blob */ + tGATT_VALUE attr_value; /* write request */ + /* prepare write */ + /* write blob */ + UINT16 handle; /* read, handle value confirmation */ + UINT16 mtu; + tGATT_EXEC_FLAG exec_write; /* execute write */ +} tGATT_CL_MSG; + +/* error response strucutre */ +typedef struct { + UINT16 handle; + UINT8 cmd_code; + UINT8 reason; +} tGATT_ERROR; + +/* Execute write response structure */ +typedef struct { + UINT8 op_code; +}__attribute__((packed)) tGATT_EXEC_WRITE_RSP; + +/* Write request response structure */ +typedef struct { + UINT8 op_code; +}__attribute__((packed)) tGATT_WRITE_REQ_RSP; + +/* server response message to ATT protocol +*/ +typedef union { + /* data type member event */ + tGATT_VALUE attr_value; /* READ, HANDLE_VALUE_IND, PREPARE_WRITE */ + /* READ_BLOB, READ_BY_TYPE */ + tGATT_ERROR error; /* ERROR_RSP */ + UINT16 handle; /* WRITE, WRITE_BLOB */ + UINT16 mtu; /* exchange MTU request */ +} tGATT_SR_MSG; + +/* Characteristic declaration attribute value +*/ +typedef struct { + tGATT_CHAR_PROP property; + UINT16 char_val_handle; +} tGATT_CHAR_DECL; + +/* attribute value maintained in the server database +*/ +typedef union { + tBT_UUID uuid; /* service declaration */ + tGATT_CHAR_DECL char_decl; /* characteristic declaration */ + tGATT_INCL_SRVC incl_handle; /* included service */ + tGATT_ATTR_VAL attr_val; +} tGATT_ATTR_VALUE; + +/* Attribute UUID type +*/ +#define GATT_ATTR_UUID_TYPE_16 0 +#define GATT_ATTR_UUID_TYPE_128 1 +#define GATT_ATTR_UUID_TYPE_32 2 +typedef UINT8 tGATT_ATTR_UUID_TYPE; + +/* 16 bits UUID Attribute in server database +*/ +typedef struct { + void *p_next; /* pointer to the next attribute, either tGATT_ATTR16 or tGATT_ATTR128 */ + tGATT_ATTR_VALUE *p_value; + tGATT_ATTR_UUID_TYPE uuid_type; + tGATT_PERM permission; + tGATTS_ATTR_CONTROL control; + tGATT_ATTR_MASK mask; + UINT16 handle; + UINT16 uuid; +} tGATT_ATTR16; + +/* 32 bits UUID Attribute in server database +*/ +typedef struct { + void *p_next; /* pointer to the next attribute, either tGATT_ATTR16, tGATT_ATTR32 or tGATT_ATTR128 */ + tGATT_ATTR_VALUE *p_value; + tGATT_ATTR_UUID_TYPE uuid_type; + tGATT_PERM permission; + tGATTS_ATTR_CONTROL control; + tGATT_ATTR_MASK mask; + UINT16 handle; + UINT32 uuid; +} tGATT_ATTR32; + + +/* 128 bits UUID Attribute in server database +*/ +typedef struct { + void *p_next; /* pointer to the next attribute, either tGATT_ATTR16 or tGATT_ATTR128 */ + tGATT_ATTR_VALUE *p_value; + tGATT_ATTR_UUID_TYPE uuid_type; + tGATT_PERM permission; + tGATTS_ATTR_CONTROL control; + tGATT_ATTR_MASK mask; + UINT16 handle; + UINT8 uuid[LEN_UUID_128]; +} tGATT_ATTR128; + +/* Service Database definition +*/ +typedef struct { + void *p_attr_list; /* pointer to the first attribute, either tGATT_ATTR16 or tGATT_ATTR128 */ + UINT8 *p_free_mem; /* Pointer to free memory */ + fixed_queue_t *svc_buffer; /* buffer queue used for service database */ + UINT32 mem_free; /* Memory still available */ + UINT16 end_handle; /* Last handle number */ + UINT16 next_handle; /* Next usable handle value */ +} tGATT_SVC_DB; + +/* Data Structure used for GATT server */ +/* A GATT registration record consists of a handle, and 1 or more attributes */ +/* A service registration information record consists of beginning and ending */ +/* attribute handle, service UUID and a set of GATT server callback. */ +typedef struct { + tGATT_SVC_DB *p_db; /* pointer to the service database */ + tBT_UUID app_uuid; /* applicatino UUID */ + UINT32 sdp_handle; /* primamry service SDP handle */ + UINT16 service_instance; /* service instance number */ + UINT16 type; /* service type UUID, primary or secondary */ + UINT16 s_hdl; /* service starting handle */ + UINT16 e_hdl; /* service ending handle */ + tGATT_IF gatt_if; /* this service is belong to which application */ + BOOLEAN in_use; +} tGATT_SR_REG; + +#define GATT_LISTEN_TO_ALL 0xff +#define GATT_LISTEN_TO_NONE 0 + +/* Data Structure used for GATT server */ +/* An GATT registration record consists of a handle, and 1 or more attributes */ +/* A service registration information record consists of beginning and ending */ +/* attribute handle, service UUID and a set of GATT server callback. */ + +typedef struct { + tBT_UUID app_uuid128; + tGATT_CBACK app_cb; + tGATT_IF gatt_if; /* one based */ + BOOLEAN in_use; + UINT8 listening; /* if adv for all has been enabled */ +} tGATT_REG; + + + + +/* command queue for each connection */ +typedef struct { + BT_HDR *p_cmd; + UINT16 clcb_idx; + UINT8 op_code; + BOOLEAN to_send; +} tGATT_CMD_Q; + + +#if GATT_MAX_SR_PROFILES <= 8 +typedef UINT8 tGATT_APP_MASK; +#elif GATT_MAX_SR_PROFILES <= 16 +typedef UINT16 tGATT_APP_MASK; +#elif GATT_MAX_SR_PROFILES <= 32 +typedef UINT32 tGATT_APP_MASK; +#endif + +/* command details for each connection */ +typedef struct { + BT_HDR *p_rsp_msg; + UINT32 trans_id; + tGATT_READ_MULTI multi_req; + fixed_queue_t *multi_rsp_q; + UINT16 handle; + UINT8 op_code; + UINT8 status; + UINT8 cback_cnt[GATT_MAX_APPS]; +} tGATT_SR_CMD; + +#define GATT_CH_CLOSE 0 +#define GATT_CH_CLOSING 1 +#define GATT_CH_CONN 2 +#define GATT_CH_CFG 3 +#define GATT_CH_OPEN 4 + +typedef UINT8 tGATT_CH_STATE; + +#define GATT_GATT_START_HANDLE 1 +#define GATT_GAP_START_HANDLE 20 +#define GATT_APP_START_HANDLE 40 + +typedef struct hdl_cfg { + UINT16 gatt_start_hdl; + UINT16 gap_start_hdl; + UINT16 app_start_hdl; +} tGATT_HDL_CFG; + +typedef struct hdl_list_elem { + struct hdl_list_elem *p_next; + struct hdl_list_elem *p_prev; + tGATTS_HNDL_RANGE asgn_range; /* assigned handle range */ + tGATT_SVC_DB svc_db; + BOOLEAN in_use; +} tGATT_HDL_LIST_ELEM; + +typedef struct { + tGATT_HDL_LIST_ELEM *p_first; + tGATT_HDL_LIST_ELEM *p_last; + UINT16 count; +} tGATT_HDL_LIST_INFO; + + +typedef struct srv_list_elem { + struct srv_list_elem *p_next; + struct srv_list_elem *p_prev; + UINT16 s_hdl; + UINT8 i_sreg; + BOOLEAN in_use; + BOOLEAN is_primary; +} tGATT_SRV_LIST_ELEM; + + +typedef struct { + tGATT_SRV_LIST_ELEM *p_last_primary; + tGATT_SRV_LIST_ELEM *p_first; + tGATT_SRV_LIST_ELEM *p_last; + UINT16 count; +} tGATT_SRV_LIST_INFO; + +/* prepare write queue data */ +typedef struct{ + //len: length of value + tGATT_ATTR16 *p_attr; + UINT16 len; + UINT8 op_code; + UINT16 handle; + UINT16 offset; + UINT8 value[2]; +}__attribute__((packed)) tGATT_PREPARE_WRITE_QUEUE_DATA; + +/* structure to store prepare write packts information */ +typedef struct{ + //only store prepare write packets which need + //to be responded by stack (not by application) + fixed_queue_t *queue; + + //store the total number of prepare write packets + //including that should be responded by stack or by application + UINT16 total_num; + + //store application error code for prepare write, + //invalid offset && invalid length + UINT8 error_code_app; +}tGATT_PREPARE_WRITE_RECORD; + +typedef struct { + fixed_queue_t *pending_enc_clcb; /* pending encryption channel q */ + tGATT_SEC_ACTION sec_act; + BD_ADDR peer_bda; + tBT_TRANSPORT transport; + UINT32 trans_id; + + UINT16 att_lcid; /* L2CAP channel ID for ATT */ + UINT16 payload_size; + + tGATT_CH_STATE ch_state; + UINT8 ch_flags; + + tGATT_IF app_hold_link[GATT_MAX_APPS]; + + /* server needs */ + /* server response data */ +#if (GATTS_INCLUDED == TRUE) + tGATT_SR_CMD sr_cmd; +#endif ///GATTS_INCLUDED == TRUE + UINT16 indicate_handle; + fixed_queue_t *pending_ind_q; + + TIMER_LIST_ENT conf_timer_ent; /* peer confirm to indication timer */ + + UINT8 prep_cnt[GATT_MAX_APPS]; + UINT8 ind_count; + + tGATT_CMD_Q cl_cmd_q[GATT_CL_MAX_LCB]; + TIMER_LIST_ENT ind_ack_timer_ent; /* local app confirm to indication timer */ + UINT8 pending_cl_req; + UINT8 next_slot_inq; /* index of next available slot in queue */ + + /* client supported feature */ + UINT8 cl_supp_feat; + /* server supported feature */ + UINT8 sr_supp_feat; + /* if false, should handle database out of sync */ + BOOLEAN is_robust_cache_change_aware; + + BOOLEAN in_use; + UINT8 tcb_idx; + tGATT_PREPARE_WRITE_RECORD prepare_write_record; /* prepare write packets record */ +} tGATT_TCB; + + +/* logic channel */ +typedef struct { + UINT16 next_disc_start_hdl; /* starting handle for the next inc srvv discovery */ + tGATT_DISC_RES result; + BOOLEAN wait_for_read_rsp; +} tGATT_READ_INC_UUID128; +typedef struct { + tGATT_TCB *p_tcb; /* associated TCB of this CLCB */ + tGATT_REG *p_reg; /* owner of this CLCB */ + UINT8 sccb_idx; + UINT8 *p_attr_buf; /* attribute buffer for read multiple, prepare write */ + tBT_UUID uuid; + UINT16 conn_id; /* connection handle */ + UINT16 clcb_idx; + UINT16 s_handle; /* starting handle of the active request */ + UINT16 e_handle; /* ending handle of the active request */ + UINT16 counter; /* used as offset, attribute length, num of prepare write */ + UINT16 start_offset; + tGATT_AUTH_REQ auth_req; /* authentication requirement */ + UINT8 operation; /* one logic channel can have one operation active */ + UINT8 op_subtype; /* operation subtype */ + UINT8 status; /* operation status */ + BOOLEAN first_read_blob_after_read; + tGATT_READ_INC_UUID128 read_uuid128; + BOOLEAN in_use; + TIMER_LIST_ENT rsp_timer_ent; /* peer response timer */ + UINT8 retry_count; + +} tGATT_CLCB; + +typedef struct { + tGATT_CLCB *p_clcb; +} tGATT_PENDING_ENC_CLCB; + + +#define GATT_SIGN_WRITE 1 +#define GATT_VERIFY_SIGN_DATA 2 + +typedef struct { + BT_HDR hdr; + tGATT_CLCB *p_clcb; +} tGATT_SIGN_WRITE_OP; + +typedef struct { + BT_HDR hdr; + tGATT_TCB *p_tcb; + BT_HDR *p_data; + +} tGATT_VERIFY_SIGN_OP; + + +typedef struct { + UINT16 clcb_idx; + BOOLEAN in_use; +} tGATT_SCCB; + +typedef struct { + UINT16 handle; + UINT16 uuid; + UINT32 service_change; +} tGATT_SVC_CHG; + +typedef struct { + tGATT_IF gatt_if[GATT_MAX_APPS]; + tGATT_IF listen_gif[GATT_MAX_APPS]; + BD_ADDR remote_bda; + BOOLEAN in_use; +} tGATT_BG_CONN_DEV; + +#define GATT_SVC_CHANGED_CONNECTING 1 /* wait for connection */ +#define GATT_SVC_CHANGED_SERVICE 2 /* GATT service discovery */ +#define GATT_SVC_CHANGED_CHARACTERISTIC 3 /* service change char discovery */ +#define GATT_SVC_CHANGED_DESCRIPTOR 4 /* service change CCC discoery */ +#define GATT_SVC_CHANGED_CONFIGURE_CCCD 5 /* config CCC */ + +typedef struct { + UINT16 conn_id; + BOOLEAN in_use; + BOOLEAN connected; + BD_ADDR bda; + tBT_TRANSPORT transport; + + /* GATT service change CCC related variables */ + UINT8 ccc_stage; + UINT8 ccc_result; + UINT16 s_handle; + UINT16 e_handle; +} tGATT_PROFILE_CLCB; + +typedef struct { + list_t *p_tcb_list; + fixed_queue_t *sign_op_queue; + + tGATT_SR_REG sr_reg[GATT_MAX_SR_PROFILES]; + UINT16 next_handle; /* next available handle */ + tGATT_SVC_CHG gattp_attr; /* GATT profile attribute service change */ + tGATT_IF gatt_if; +#if (GATTS_INCLUDED == TRUE) + tGATT_HDL_LIST_INFO hdl_list_info; + tGATT_HDL_LIST_ELEM hdl_list[GATT_MAX_SR_PROFILES]; + tGATT_SRV_LIST_INFO srv_list_info; + tGATT_SRV_LIST_ELEM srv_list[GATT_MAX_SR_PROFILES]; +#endif ///GATTS_INCLUDED == TRUE + fixed_queue_t *srv_chg_clt_q; /* service change clients queue */ + fixed_queue_t *pending_new_srv_start_q; /* pending new service start queue */ + tGATT_REG cl_rcb[GATT_MAX_APPS]; + list_t *p_clcb_list; /* connection link control block*/ + tGATT_SCCB sccb[GATT_MAX_SCCB]; /* sign complete callback function GATT_MAX_SCCB <= GATT_CL_MAX_LCB */ + UINT8 trace_level; + UINT16 def_mtu_size; + +#if GATT_CONFORMANCE_TESTING == TRUE + BOOLEAN enable_err_rsp; + UINT8 req_op_code; + UINT8 err_status; + UINT16 handle; +#endif +#if (GATTS_INCLUDED == TRUE) + tGATT_PROFILE_CLCB profile_clcb[GATT_MAX_APPS]; +#endif ///GATTS_INCLUDED == TRUE + UINT16 handle_of_h_r; /* Handle of the handles reused characteristic value */ +#if GATTS_ROBUST_CACHING_ENABLED + UINT16 handle_of_database_hash; + UINT16 handle_of_cl_supported_feat; + UINT16 handle_of_sr_supported_feat; + BT_OCTET16 database_hash; + UINT8 gatt_sr_supported_feat_mask; + UINT8 gatt_cl_supported_feat_mask; + +#endif + tGATT_APPL_INFO cb_info; + + + + tGATT_HDL_CFG hdl_cfg; + tGATT_BG_CONN_DEV bgconn_dev[GATT_MAX_BG_CONN_DEV]; + + BOOLEAN auto_disc; /* internal use: true for auto discovering after connected */ + UINT8 srv_chg_mode; /* internal use: service change mode */ + tGATTS_RSP rsp; /* use to read internal service attribute */ +} tGATT_CB; + +typedef struct{ + UINT16 local_mtu; +} tGATT_DEFAULT; + +#define GATT_SIZE_OF_SRV_CHG_HNDL_RANGE 4 + +#ifdef __cplusplus +extern "C" { +#endif + +extern tGATT_DEFAULT gatt_default; + +/* Global GATT data */ +#if GATT_DYNAMIC_MEMORY == FALSE +extern tGATT_CB gatt_cb; +#else +extern tGATT_CB *gatt_cb_ptr; +#define gatt_cb (*gatt_cb_ptr) +#endif + +#if GATT_CONFORMANCE_TESTING == TRUE +extern void gatt_set_err_rsp(BOOLEAN enable, UINT8 req_op_code, UINT8 err_status); +#endif + +#ifdef __cplusplus +} +#endif + +/* internal functions */ +extern void gatt_init (void); +extern void gatt_free(void); + +/* from gatt_main.c */ +extern BOOLEAN gatt_disconnect (tGATT_TCB *p_tcb); +extern BOOLEAN gatt_act_connect (tGATT_REG *p_reg, BD_ADDR bd_addr, tBLE_ADDR_TYPE bd_addr_type, tBT_TRANSPORT transport, BOOLEAN is_aux); +extern BOOLEAN gatt_connect (BD_ADDR rem_bda, tBLE_ADDR_TYPE bd_addr_type, tGATT_TCB *p_tcb, tBT_TRANSPORT transport, BOOLEAN is_aux); +extern void gatt_data_process (tGATT_TCB *p_tcb, BT_HDR *p_buf); +extern void gatt_update_app_use_link_flag ( tGATT_IF gatt_if, tGATT_TCB *p_tcb, BOOLEAN is_add, BOOLEAN check_acl_link); + +extern void gatt_profile_db_init(void); +extern void gatt_set_ch_state(tGATT_TCB *p_tcb, tGATT_CH_STATE ch_state); +extern tGATT_CH_STATE gatt_get_ch_state(tGATT_TCB *p_tcb); +extern void gatt_init_srv_chg(void); +extern void gatt_proc_srv_chg (void); +extern tGATT_STATUS gatt_send_srv_chg_ind (BD_ADDR peer_bda); +extern void gatt_chk_srv_chg(tGATTS_SRV_CHG *p_srv_chg_clt); +extern void gatt_add_a_bonded_dev_for_srv_chg (BD_ADDR bda); + +/* from gatt_attr.c */ +extern UINT16 gatt_profile_find_conn_id_by_bd_addr(BD_ADDR bda); + + +/* Functions provided by att_protocol.c */ +extern tGATT_STATUS attp_send_cl_msg (tGATT_TCB *p_tcb, UINT16 clcb_idx, UINT8 op_code, tGATT_CL_MSG *p_msg); +extern BT_HDR *attp_build_sr_msg(tGATT_TCB *p_tcb, UINT8 op_code, tGATT_SR_MSG *p_msg); +extern tGATT_STATUS attp_send_sr_msg (tGATT_TCB *p_tcb, BT_HDR *p_msg); +extern tGATT_STATUS attp_send_msg_to_l2cap(tGATT_TCB *p_tcb, BT_HDR *p_toL2CAP); + +/* utility functions */ +extern UINT8 *gatt_dbg_op_name(UINT8 op_code); +#if (SDP_INCLUDED == TRUE && CLASSIC_BT_GATT_INCLUDED == TRUE) +extern UINT32 gatt_add_sdp_record (tBT_UUID *p_uuid, UINT16 start_hdl, UINT16 end_hdl); +#endif ///SDP_INCLUDED == TRUE && CLASSIC_BT_GATT_INCLUDED == TRUE +extern BOOLEAN gatt_parse_uuid_from_cmd(tBT_UUID *p_uuid, UINT16 len, UINT8 **p_data); +extern UINT8 gatt_build_uuid_to_stream(UINT8 **p_dst, tBT_UUID uuid); +extern BOOLEAN gatt_uuid_compare(tBT_UUID src, tBT_UUID tar); +extern void gatt_convert_uuid32_to_uuid128(UINT8 uuid_128[LEN_UUID_128], UINT32 uuid_32); +extern char *gatt_uuid_to_str(const tBT_UUID *uuid); +extern void gatt_sr_get_sec_info(BD_ADDR rem_bda, tBT_TRANSPORT transport, UINT8 *p_sec_flag, UINT8 *p_key_size); +extern void gatt_start_rsp_timer(UINT16 clcb_idx); +extern void gatt_start_conf_timer(tGATT_TCB *p_tcb); +extern void gatt_rsp_timeout(TIMER_LIST_ENT *p_tle); +extern void gatt_ind_ack_timeout(TIMER_LIST_ENT *p_tle); +extern void gatt_start_ind_ack_timer(tGATT_TCB *p_tcb); +extern tGATT_STATUS gatt_send_error_rsp(tGATT_TCB *p_tcb, UINT8 err_code, UINT8 op_code, UINT16 handle, BOOLEAN deq); +extern void gatt_dbg_display_uuid(tBT_UUID bt_uuid); +extern tGATT_PENDING_ENC_CLCB *gatt_add_pending_enc_channel_clcb(tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb ); + +extern tGATTS_PENDING_NEW_SRV_START *gatt_sr_is_new_srv_chg(tBT_UUID *p_app_uuid128, tBT_UUID *p_svc_uuid, UINT16 svc_inst); + +extern BOOLEAN gatt_is_srv_chg_ind_pending (tGATT_TCB *p_tcb); +extern tGATTS_SRV_CHG *gatt_is_bda_in_the_srv_chg_clt_list (BD_ADDR bda); + +extern BOOLEAN gatt_find_the_connected_bda(UINT8 start_idx, BD_ADDR bda, UINT8 *p_found_idx, tBT_TRANSPORT *p_transport); +extern void gatt_set_srv_chg(void); +extern void gatt_delete_dev_from_srv_chg_clt_list(BD_ADDR bd_addr); +extern tGATT_VALUE *gatt_add_pending_ind(tGATT_TCB *p_tcb, tGATT_VALUE *p_ind); +extern tGATTS_PENDING_NEW_SRV_START *gatt_add_pending_new_srv_start( tGATTS_HNDL_RANGE *p_new_srv_start); +extern void gatt_free_srvc_db_buffer_app_id(tBT_UUID *p_app_id); +extern BOOLEAN gatt_update_listen_mode(void); +extern BOOLEAN gatt_cl_send_next_cmd_inq(tGATT_TCB *p_tcb); + +/* reserved handle list */ +extern tGATT_HDL_LIST_ELEM *gatt_find_hdl_buffer_by_app_id (tBT_UUID *p_app_uuid128, tBT_UUID *p_svc_uuid, UINT16 svc_inst); +extern tGATT_HDL_LIST_ELEM *gatt_find_hdl_buffer_by_handle(UINT16 handle); +extern tGATT_HDL_LIST_ELEM *gatt_find_hdl_buffer_by_attr_handle(UINT16 attr_handle); +extern tGATT_HDL_LIST_ELEM *gatt_alloc_hdl_buffer(void); +extern void gatt_free_hdl_buffer(tGATT_HDL_LIST_ELEM *p); +extern void gatt_free_attr_value_buffer(tGATT_HDL_LIST_ELEM *p); +extern BOOLEAN gatt_is_last_attribute(tGATT_SRV_LIST_INFO *p_list, tGATT_SRV_LIST_ELEM *p_start, tBT_UUID value); +extern void gatt_update_last_pri_srv_info(tGATT_SRV_LIST_INFO *p_list); +extern BOOLEAN gatt_add_a_srv_to_list(tGATT_SRV_LIST_INFO *p_list, tGATT_SRV_LIST_ELEM *p_new); +extern BOOLEAN gatt_remove_a_srv_from_list(tGATT_SRV_LIST_INFO *p_list, tGATT_SRV_LIST_ELEM *p_remove); +extern BOOLEAN gatt_add_an_item_to_list(tGATT_HDL_LIST_INFO *p_list, tGATT_HDL_LIST_ELEM *p_new); +extern BOOLEAN gatt_remove_an_item_from_list(tGATT_HDL_LIST_INFO *p_list, tGATT_HDL_LIST_ELEM *p_remove); +extern tGATTS_SRV_CHG *gatt_add_srv_chg_clt(tGATTS_SRV_CHG *p_srv_chg); + +/* for background connection */ +extern BOOLEAN gatt_update_auto_connect_dev (tGATT_IF gatt_if, BOOLEAN add, BD_ADDR bd_addr, BOOLEAN is_initiator); +extern BOOLEAN gatt_is_bg_dev_for_app(tGATT_BG_CONN_DEV *p_dev, tGATT_IF gatt_if); +extern BOOLEAN gatt_remove_bg_dev_for_app(tGATT_IF gatt_if, BD_ADDR bd_addr); +extern UINT8 gatt_get_num_apps_for_bg_dev(BD_ADDR bd_addr); +extern BOOLEAN gatt_find_app_for_bg_dev(BD_ADDR bd_addr, tGATT_IF *p_gatt_if); +extern tGATT_BG_CONN_DEV *gatt_find_bg_dev(BD_ADDR remote_bda); +extern void gatt_deregister_bgdev_list(tGATT_IF gatt_if); +extern void gatt_reset_bgdev_list(void); + +/* server function */ +extern UINT8 gatt_sr_find_i_rcb_by_handle(UINT16 handle); +extern UINT8 gatt_sr_find_i_rcb_by_app_id(tBT_UUID *p_app_uuid128, tBT_UUID *p_svc_uuid, UINT16 svc_inst); +extern UINT8 gatt_sr_alloc_rcb(tGATT_HDL_LIST_ELEM *p_list); +extern tGATT_STATUS gatt_sr_process_app_rsp (tGATT_TCB *p_tcb, tGATT_IF gatt_if, UINT32 trans_id, UINT8 op_code, tGATT_STATUS status, tGATTS_RSP *p_msg); +extern void gatt_server_handle_client_req (tGATT_TCB *p_tcb, UINT8 op_code, + UINT16 len, UINT8 *p_data); +extern void gatt_sr_send_req_callback(UINT16 conn_id, UINT32 trans_id, + UINT8 op_code, tGATTS_DATA *p_req_data); +extern UINT32 gatt_sr_enqueue_cmd (tGATT_TCB *p_tcb, UINT8 op_code, UINT16 handle); +extern BOOLEAN gatt_cancel_open(tGATT_IF gatt_if, BD_ADDR bda); + +/* */ + +extern tGATT_REG *gatt_get_regcb (tGATT_IF gatt_if); +extern BOOLEAN gatt_is_clcb_allocated (UINT16 conn_id); +extern tGATT_CLCB *gatt_clcb_alloc (UINT16 conn_id); +extern void gatt_clcb_dealloc (tGATT_CLCB *p_clcb); +extern tGATT_CLCB *gatt_clcb_find_by_conn_id(UINT16 conn_id); +extern tGATT_CLCB *gatt_clcb_find_by_idx(UINT16 cclcb_idx); + +extern void gatt_sr_copy_prep_cnt_to_cback_cnt(tGATT_TCB *p_tcb ); +extern BOOLEAN gatt_sr_is_cback_cnt_zero(tGATT_TCB *p_tcb ); +extern BOOLEAN gatt_sr_is_prep_cnt_zero(tGATT_TCB *p_tcb ); +extern void gatt_sr_reset_cback_cnt(tGATT_TCB *p_tcb ); +extern void gatt_sr_reset_prep_cnt(tGATT_TCB *p_tcb ); +extern void gatt_sr_update_cback_cnt(tGATT_TCB *p_tcb, tGATT_IF gatt_if, BOOLEAN is_inc, BOOLEAN is_reset_first); +extern void gatt_sr_update_prep_cnt(tGATT_TCB *p_tcb, tGATT_IF gatt_if, BOOLEAN is_inc, BOOLEAN is_reset_first); + +extern BOOLEAN gatt_find_app_hold_link(tGATT_TCB *p_tcb, UINT8 start_idx, UINT8 *p_found_idx, tGATT_IF *p_gatt_if); +extern BOOLEAN gatt_find_specific_app_in_hold_link(tGATT_TCB *p_tcb, tGATT_IF p_gatt_if); +extern UINT8 gatt_num_apps_hold_link(tGATT_TCB *p_tcb); +extern UINT8 gatt_num_clcb_by_bd_addr(BD_ADDR bda); +extern tGATT_TCB *gatt_find_tcb_by_cid(UINT16 lcid); +extern tGATT_TCB *gatt_allocate_tcb_by_bdaddr(BD_ADDR bda, tBT_TRANSPORT transport); +extern tGATT_TCB *gatt_get_tcb_by_idx(UINT8 tcb_idx); +extern tGATT_TCB *gatt_find_tcb_by_addr(BD_ADDR bda, tBT_TRANSPORT transport); +extern BOOLEAN gatt_send_ble_burst_data (BD_ADDR remote_bda, BT_HDR *p_buf); +extern void gatt_tcb_free( tGATT_TCB *p_tcb); + +/* GATT client functions */ +extern void gatt_dequeue_sr_cmd (tGATT_TCB *p_tcb); +extern UINT8 gatt_send_write_msg(tGATT_TCB *p_tcb, UINT16 clcb_idx, UINT8 op_code, UINT16 handle, + UINT16 len, UINT16 offset, UINT8 *p_data); +extern void gatt_cleanup_upon_disc(BD_ADDR bda, UINT16 reason, tBT_TRANSPORT transport); +extern void gatt_end_operation(tGATT_CLCB *p_clcb, tGATT_STATUS status, void *p_data); + +extern void gatt_act_discovery(tGATT_CLCB *p_clcb); +extern void gatt_act_read(tGATT_CLCB *p_clcb, UINT16 offset); +extern void gatt_act_write(tGATT_CLCB *p_clcb, UINT8 sec_act); +extern UINT8 gatt_act_send_browse(tGATT_TCB *p_tcb, UINT16 index, UINT8 op, UINT16 s_handle, UINT16 e_handle, + tBT_UUID uuid); +extern tGATT_CLCB *gatt_cmd_dequeue(tGATT_TCB *p_tcb, UINT8 *p_opcode); +extern BOOLEAN gatt_cmd_enq(tGATT_TCB *p_tcb, UINT16 clcb_idx, BOOLEAN to_send, UINT8 op_code, BT_HDR *p_buf); +extern void gatt_client_handle_server_rsp (tGATT_TCB *p_tcb, UINT8 op_code, + UINT16 len, UINT8 *p_data); +extern void gatt_send_queue_write_cancel (tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb, tGATT_EXEC_FLAG flag); + +/* gatt_auth.c */ +extern BOOLEAN gatt_security_check_start(tGATT_CLCB *p_clcb); +extern void gatt_verify_signature(tGATT_TCB *p_tcb, BT_HDR *p_buf); +extern tGATT_SEC_ACTION gatt_determine_sec_act(tGATT_CLCB *p_clcb ); +extern tGATT_STATUS gatt_get_link_encrypt_status(tGATT_TCB *p_tcb); +extern tGATT_SEC_ACTION gatt_get_sec_act(tGATT_TCB *p_tcb); +extern void gatt_set_sec_act(tGATT_TCB *p_tcb, tGATT_SEC_ACTION sec_act); + +/* gatt_db.c */ +extern BOOLEAN gatts_init_service_db (tGATT_SVC_DB *p_db, tBT_UUID *p_service, BOOLEAN is_pri, UINT16 s_hdl, UINT16 num_handle); +extern UINT16 gatts_add_included_service (tGATT_SVC_DB *p_db, UINT16 s_handle, UINT16 e_handle, tBT_UUID service); +extern UINT16 gatts_add_characteristic (tGATT_SVC_DB *p_db, tGATT_PERM perm, + tGATT_CHAR_PROP property, + tBT_UUID *p_char_uuid, tGATT_ATTR_VAL *attr_val, + tGATTS_ATTR_CONTROL *control); +extern UINT16 gatts_add_char_descr (tGATT_SVC_DB *p_db, tGATT_PERM perm, + tBT_UUID *p_dscp_uuid, tGATT_ATTR_VAL *attr_val, + tGATTS_ATTR_CONTROL *control); + +extern tGATT_STATUS gatts_set_attribute_value(tGATT_SVC_DB *p_db, UINT16 attr_handle, + UINT16 length, UINT8 *value); +extern tGATT_STATUS gatts_get_attr_value_internal(UINT16 attr_handle, UINT16 *length, UINT8 **value); +extern tGATT_STATUS gatts_get_attribute_value(tGATT_SVC_DB *p_db, UINT16 attr_handle, + UINT16 *length, UINT8 **value); +extern BOOLEAN gatts_is_auto_response(UINT16 attr_handle); +extern tGATT_STATUS gatts_db_read_attr_value_by_type (tGATT_TCB *p_tcb, tGATT_SVC_DB *p_db, UINT8 op_code, BT_HDR *p_rsp, UINT16 s_handle, + UINT16 e_handle, tBT_UUID type, UINT16 *p_len, tGATT_SEC_FLAG sec_flag, UINT8 key_size, UINT32 trans_id, UINT16 *p_cur_handle); +extern tGATT_STATUS gatts_read_attr_value_by_handle(tGATT_TCB *p_tcb, tGATT_SVC_DB *p_db, UINT8 op_code, UINT16 handle, UINT16 offset, + UINT8 *p_value, UINT16 *p_len, UINT16 mtu, tGATT_SEC_FLAG sec_flag, UINT8 key_size, UINT32 trans_id); +extern tGATT_STATUS gatts_write_attr_value_by_handle(tGATT_SVC_DB *p_db, + UINT16 handle, UINT16 offset, + UINT8 *p_value, UINT16 len); +extern tGATT_STATUS gatts_write_attr_perm_check (tGATT_SVC_DB *p_db, UINT8 op_code, UINT16 handle, UINT16 offset, UINT8 *p_data, + UINT16 len, tGATT_SEC_FLAG sec_flag, UINT8 key_size); +extern tGATT_STATUS gatts_read_attr_perm_check(tGATT_SVC_DB *p_db, BOOLEAN is_long, UINT16 handle, tGATT_SEC_FLAG sec_flag, UINT8 key_size); +extern void gatts_update_srv_list_elem(UINT8 i_sreg, UINT16 handle, BOOLEAN is_primary); +extern tBT_UUID *gatts_get_service_uuid (tGATT_SVC_DB *p_db); + +extern BOOLEAN gatt_check_connection_state_by_tcb(tGATT_TCB *p_tcb); + +extern void gatt_reset_bgdev_list(void); +extern uint16_t gatt_get_local_mtu(void); +extern void gatt_set_local_mtu(uint16_t mtu); + +extern tGATT_STATUS gatts_calculate_datebase_hash(BT_OCTET16 hash); +extern void gatts_show_local_database(void); + +extern BOOLEAN gatt_sr_is_cl_change_aware(tGATT_TCB *p_tcb); +extern void gatt_sr_init_cl_status(tGATT_TCB *p_tcb); +extern void gatt_sr_update_cl_status(tGATT_TCB *tcb, BOOLEAN chg_aware); +#endif diff --git a/lib/bt/host/bluedroid/stack/hcic/hciblecmds.c b/lib/bt/host/bluedroid/stack/hcic/hciblecmds.c new file mode 100644 index 00000000..0f6408f0 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/hcic/hciblecmds.c @@ -0,0 +1,1912 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains function of the HCIC unit to format and send HCI + * commands. + * + ******************************************************************************/ + +#include "common/bt_target.h" +#include "osi/allocator.h" +#include "stack/hcidefs.h" +#include "stack/hcimsgs.h" +#include "stack/hcidefs.h" +#include "stack/btu.h" + +#include +#include + +#if (BLE_50_FEATURE_SUPPORT == TRUE) +static BlE_SYNC ble_sync_info; + +void btsnd_hcic_ble_sync_sem_init(void) +{ + ble_sync_info.opcode = 0; + osi_sem_new(&ble_sync_info.sync_sem, 1, 0); +} + +void btsnd_hcic_ble_sync_sem_deinit(void) +{ + ble_sync_info.opcode = 0; + osi_sem_free(&ble_sync_info.sync_sem); +} + +BlE_SYNC *btsnd_hcic_ble_get_sync_info(void) +{ + return &ble_sync_info; +} + +uint8_t btsnd_hcic_ble_get_status(void) +{ + return ble_sync_info.status; +} + +void btsnd_hci_ble_set_status(UINT8 hci_status) +{ + ble_sync_info.status = hci_status; + return; +} +#endif // #if (BLE_50_FEATURE_SUPPORT == TRUE) + +#if (defined BLE_INCLUDED) && (BLE_INCLUDED == TRUE) + +BOOLEAN btsnd_hcic_ble_set_local_used_feat (UINT8 feat_set[8]) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_SET_USED_FEAT_CMD)) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_SET_USED_FEAT_CMD; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_BLE_WRITE_LOCAL_SPT_FEAT); + ARRAY_TO_STREAM (pp, feat_set, HCIC_PARAM_SIZE_SET_USED_FEAT_CMD); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_ble_set_random_addr (BD_ADDR random_bda) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_WRITE_RANDOM_ADDR_CMD)) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_WRITE_RANDOM_ADDR_CMD; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_BLE_WRITE_RANDOM_ADDR); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_WRITE_RANDOM_ADDR_CMD); + + BDADDR_TO_STREAM (pp, random_bda); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_ble_write_adv_params (UINT16 adv_int_min, UINT16 adv_int_max, + UINT8 adv_type, UINT8 addr_type_own, + UINT8 addr_type_dir, BD_ADDR direct_bda, + UINT8 channel_map, UINT8 adv_filter_policy) +{ + BT_HDR *p; + UINT8 *pp; + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_BLE_WRITE_ADV_PARAMS)) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_BLE_WRITE_ADV_PARAMS ; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_BLE_WRITE_ADV_PARAMS); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_BLE_WRITE_ADV_PARAMS ); + + UINT16_TO_STREAM (pp, adv_int_min); + UINT16_TO_STREAM (pp, adv_int_max); + UINT8_TO_STREAM (pp, adv_type); + UINT8_TO_STREAM (pp, addr_type_own); + UINT8_TO_STREAM (pp, addr_type_dir); + BDADDR_TO_STREAM (pp, direct_bda); + UINT8_TO_STREAM (pp, channel_map); + UINT8_TO_STREAM (pp, adv_filter_policy); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} +BOOLEAN btsnd_hcic_ble_read_adv_chnl_tx_power (void) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_READ_CMD)) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_READ_CMD; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_BLE_READ_ADV_CHNL_TX_POWER); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_READ_CMD); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); + +} + +BOOLEAN btsnd_hcic_ble_set_adv_data (UINT8 data_len, UINT8 *p_data) +{ + BT_HDR *p; + UINT8 *pp; + + for (int i = 0; i < data_len; i++) { + HCI_TRACE_DEBUG("p_data[%d] = %x\n", i, p_data[i]); + } + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_BLE_WRITE_ADV_DATA + 1)) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_BLE_WRITE_ADV_DATA + 1; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_BLE_WRITE_ADV_DATA); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_BLE_WRITE_ADV_DATA + 1); + + memset(pp, 0, HCIC_PARAM_SIZE_BLE_WRITE_ADV_DATA); + + if (p_data != NULL && data_len > 0) { + if (data_len > HCIC_PARAM_SIZE_BLE_WRITE_ADV_DATA) { + data_len = HCIC_PARAM_SIZE_BLE_WRITE_ADV_DATA; + HCI_TRACE_WARNING("Data length exceeds 31 bytes, only the first 31 bytes are used.\n"); + } + + UINT8_TO_STREAM (pp, data_len); + + ARRAY_TO_STREAM (pp, p_data, data_len); + } + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + + return (TRUE); +} +BOOLEAN btsnd_hcic_ble_set_scan_rsp_data (UINT8 data_len, UINT8 *p_scan_rsp) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_BLE_WRITE_SCAN_RSP + 1)) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_BLE_WRITE_SCAN_RSP + 1; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_BLE_WRITE_SCAN_RSP_DATA); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_BLE_WRITE_SCAN_RSP + 1); + + memset(pp, 0, HCIC_PARAM_SIZE_BLE_WRITE_SCAN_RSP); + + if (p_scan_rsp != NULL && data_len > 0) { + + if (data_len > HCIC_PARAM_SIZE_BLE_WRITE_SCAN_RSP ) { + data_len = HCIC_PARAM_SIZE_BLE_WRITE_SCAN_RSP; + HCI_TRACE_WARNING("Data length exceeds 31 bytes, only the first 31 bytes are used.\n"); + } + + UINT8_TO_STREAM (pp, data_len); + + ARRAY_TO_STREAM (pp, p_scan_rsp, data_len); + } + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + + return (TRUE); +} + +BOOLEAN btsnd_hcic_ble_set_adv_enable (UINT8 adv_enable) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_WRITE_ADV_ENABLE)) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_WRITE_ADV_ENABLE; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_BLE_WRITE_ADV_ENABLE); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_WRITE_ADV_ENABLE); + + UINT8_TO_STREAM (pp, adv_enable); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} +BOOLEAN btsnd_hcic_ble_set_scan_params (UINT8 scan_type, + UINT16 scan_int, UINT16 scan_win, + UINT8 addr_type_own, UINT8 scan_filter_policy) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_BLE_WRITE_SCAN_PARAM)) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_BLE_WRITE_SCAN_PARAM; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_BLE_WRITE_SCAN_PARAMS); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_BLE_WRITE_SCAN_PARAM); + + UINT8_TO_STREAM (pp, scan_type); + UINT16_TO_STREAM (pp, scan_int); + UINT16_TO_STREAM (pp, scan_win); + UINT8_TO_STREAM (pp, addr_type_own); + UINT8_TO_STREAM (pp, scan_filter_policy); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_ble_set_scan_enable (UINT8 scan_enable, UINT8 duplicate) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_BLE_WRITE_SCAN_ENABLE)) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_BLE_WRITE_SCAN_ENABLE; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_BLE_WRITE_SCAN_ENABLE); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_BLE_WRITE_SCAN_ENABLE); + + UINT8_TO_STREAM (pp, scan_enable); + UINT8_TO_STREAM (pp, duplicate); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +/* link layer connection management commands */ +BOOLEAN btsnd_hcic_ble_create_ll_conn (UINT16 scan_int, UINT16 scan_win, + UINT8 init_filter_policy, + UINT8 addr_type_peer, BD_ADDR bda_peer, + UINT8 addr_type_own, + UINT16 conn_int_min, UINT16 conn_int_max, + UINT16 conn_latency, UINT16 conn_timeout, + UINT16 min_ce_len, UINT16 max_ce_len) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_BLE_CREATE_LL_CONN)) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_BLE_CREATE_LL_CONN; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_BLE_CREATE_LL_CONN); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_BLE_CREATE_LL_CONN); + + UINT16_TO_STREAM (pp, scan_int); + UINT16_TO_STREAM (pp, scan_win); + UINT8_TO_STREAM (pp, init_filter_policy); + + UINT8_TO_STREAM (pp, addr_type_peer); + BDADDR_TO_STREAM (pp, bda_peer); + UINT8_TO_STREAM (pp, addr_type_own); + + UINT16_TO_STREAM (pp, conn_int_min); + UINT16_TO_STREAM (pp, conn_int_max); + UINT16_TO_STREAM (pp, conn_latency); + UINT16_TO_STREAM (pp, conn_timeout); + + UINT16_TO_STREAM (pp, min_ce_len); + UINT16_TO_STREAM (pp, max_ce_len); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_ble_create_conn_cancel (void) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_BLE_CREATE_CONN_CANCEL)) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_BLE_CREATE_CONN_CANCEL; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_BLE_CREATE_CONN_CANCEL); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_BLE_CREATE_CONN_CANCEL); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_ble_clear_white_list (void) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_CLEAR_WHITE_LIST)) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_CLEAR_WHITE_LIST; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_BLE_CLEAR_WHITE_LIST); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_CLEAR_WHITE_LIST); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_ble_add_white_list (UINT8 addr_type, BD_ADDR bda) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_ADD_WHITE_LIST)) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_ADD_WHITE_LIST; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_BLE_ADD_WHITE_LIST); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_ADD_WHITE_LIST); + + UINT8_TO_STREAM (pp, addr_type); + BDADDR_TO_STREAM (pp, bda); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_ble_remove_from_white_list (UINT8 addr_type, BD_ADDR bda) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_REMOVE_WHITE_LIST)) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_REMOVE_WHITE_LIST; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_BLE_REMOVE_WHITE_LIST); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_REMOVE_WHITE_LIST); + + UINT8_TO_STREAM (pp, addr_type); + BDADDR_TO_STREAM (pp, bda); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_ble_upd_ll_conn_params (UINT16 handle, + UINT16 conn_int_min, UINT16 conn_int_max, + UINT16 conn_latency, UINT16 conn_timeout, + UINT16 min_ce_len, UINT16 max_ce_len) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_BLE_UPD_LL_CONN_PARAMS)) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_BLE_UPD_LL_CONN_PARAMS; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_BLE_UPD_LL_CONN_PARAMS); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_BLE_UPD_LL_CONN_PARAMS); + + UINT16_TO_STREAM (pp, handle); + + UINT16_TO_STREAM (pp, conn_int_min); + UINT16_TO_STREAM (pp, conn_int_max); + UINT16_TO_STREAM (pp, conn_latency); + UINT16_TO_STREAM (pp, conn_timeout); + UINT16_TO_STREAM (pp, min_ce_len); + UINT16_TO_STREAM (pp, max_ce_len); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_ble_set_host_chnl_class (UINT8 chnl_map[HCIC_BLE_CHNL_MAP_SIZE]) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_SET_HOST_CHNL_CLASS)) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_SET_HOST_CHNL_CLASS; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_BLE_SET_HOST_CHNL_CLASS); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_SET_HOST_CHNL_CLASS); + + ARRAY_TO_STREAM (pp, chnl_map, HCIC_BLE_CHNL_MAP_SIZE); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_ble_read_chnl_map (UINT16 handle) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_READ_CHNL_MAP)) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_READ_CHNL_MAP; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_BLE_READ_CHNL_MAP); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_READ_CHNL_MAP); + + UINT16_TO_STREAM (pp, handle); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_ble_read_remote_feat (UINT16 handle) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_BLE_READ_REMOTE_FEAT)) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_BLE_READ_REMOTE_FEAT; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_BLE_READ_REMOTE_FEAT); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_BLE_READ_REMOTE_FEAT); + + UINT16_TO_STREAM (pp, handle); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +/* security management commands */ +BOOLEAN btsnd_hcic_ble_encrypt (UINT8 *key, UINT8 key_len, + UINT8 *plain_text, UINT8 pt_len, + void *p_cmd_cplt_cback) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_BLE_ENCRYPT)) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_BLE_ENCRYPT; + p->offset = 0; + + hci_cmd_metadata_t *metadata = HCI_GET_CMD_METAMSG(p); + metadata->context = p_cmd_cplt_cback; + + UINT16_TO_STREAM (pp, HCI_BLE_ENCRYPT); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_BLE_ENCRYPT); + + memset(pp, 0, HCIC_PARAM_SIZE_BLE_ENCRYPT); + + if (key_len > HCIC_BLE_ENCRYT_KEY_SIZE) { + key_len = HCIC_BLE_ENCRYT_KEY_SIZE; + } + if (pt_len > HCIC_BLE_ENCRYT_KEY_SIZE) { + pt_len = HCIC_BLE_ENCRYT_KEY_SIZE; + } + + ARRAY_TO_STREAM (pp, key, key_len); + pp += (HCIC_BLE_ENCRYT_KEY_SIZE - key_len); + ARRAY_TO_STREAM (pp, plain_text, pt_len); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_ble_rand (void *p_cmd_cplt_cback) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_BLE_RAND)) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_BLE_RAND; + p->offset = 0; + + hci_cmd_metadata_t *metadata = HCI_GET_CMD_METAMSG(p); + metadata->context = p_cmd_cplt_cback; + + UINT16_TO_STREAM (pp, HCI_BLE_RAND); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_BLE_RAND); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_ble_start_enc (UINT16 handle, UINT8 rand[HCIC_BLE_RAND_DI_SIZE], + UINT16 ediv, UINT8 ltk[HCIC_BLE_ENCRYT_KEY_SIZE]) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_BLE_START_ENC)) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_BLE_START_ENC; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_BLE_START_ENC); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_BLE_START_ENC); + + UINT16_TO_STREAM (pp, handle); + ARRAY_TO_STREAM (pp, rand, HCIC_BLE_RAND_DI_SIZE); + UINT16_TO_STREAM (pp, ediv); + ARRAY_TO_STREAM (pp, ltk, HCIC_BLE_ENCRYT_KEY_SIZE); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_ble_ltk_req_reply (UINT16 handle, UINT8 ltk[HCIC_BLE_ENCRYT_KEY_SIZE]) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_LTK_REQ_REPLY)) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_LTK_REQ_REPLY; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_BLE_LTK_REQ_REPLY); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_LTK_REQ_REPLY); + + UINT16_TO_STREAM (pp, handle); + ARRAY_TO_STREAM (pp, ltk, HCIC_BLE_ENCRYT_KEY_SIZE); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_ble_ltk_req_neg_reply (UINT16 handle) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_LTK_REQ_NEG_REPLY)) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_LTK_REQ_NEG_REPLY; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_BLE_LTK_REQ_NEG_REPLY); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_LTK_REQ_NEG_REPLY); + + UINT16_TO_STREAM (pp, handle); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_ble_receiver_test(UINT8 rx_freq) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_WRITE_PARAM1)) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_WRITE_PARAM1; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_BLE_RECEIVER_TEST); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_WRITE_PARAM1); + + UINT8_TO_STREAM (pp, rx_freq); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_ble_transmitter_test(UINT8 tx_freq, UINT8 test_data_len, UINT8 payload) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_WRITE_PARAM3)) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_WRITE_PARAM3; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_BLE_TRANSMITTER_TEST); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_WRITE_PARAM3); + + UINT8_TO_STREAM (pp, tx_freq); + UINT8_TO_STREAM (pp, test_data_len); + UINT8_TO_STREAM (pp, payload); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_ble_test_end(void) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_READ_CMD)) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_READ_CMD; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_BLE_TEST_END); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_READ_CMD); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_ble_read_host_supported (void) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_READ_CMD)) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_READ_CMD; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_READ_LE_HOST_SUPPORT); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_READ_CMD); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +#if (defined BLE_LLT_INCLUDED) && (BLE_LLT_INCLUDED == TRUE) + +BOOLEAN btsnd_hcic_ble_rc_param_req_reply( UINT16 handle, + UINT16 conn_int_min, UINT16 conn_int_max, + UINT16 conn_latency, UINT16 conn_timeout, + UINT16 min_ce_len, UINT16 max_ce_len ) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_BLE_RC_PARAM_REQ_REPLY)) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_BLE_RC_PARAM_REQ_REPLY; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_BLE_RC_PARAM_REQ_REPLY); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_BLE_RC_PARAM_REQ_REPLY); + + UINT16_TO_STREAM (pp, handle); + UINT16_TO_STREAM (pp, conn_int_min); + UINT16_TO_STREAM (pp, conn_int_max); + UINT16_TO_STREAM (pp, conn_latency); + UINT16_TO_STREAM (pp, conn_timeout); + UINT16_TO_STREAM (pp, min_ce_len); + UINT16_TO_STREAM (pp, max_ce_len); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_ble_rc_param_req_neg_reply(UINT16 handle, UINT8 reason) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_BLE_RC_PARAM_REQ_NEG_REPLY)) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_BLE_RC_PARAM_REQ_NEG_REPLY; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_BLE_RC_PARAM_REQ_NEG_REPLY); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_BLE_RC_PARAM_REQ_NEG_REPLY); + + UINT16_TO_STREAM (pp, handle); + UINT8_TO_STREAM (pp, reason); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} +#endif + +BOOLEAN btsnd_hcic_ble_add_device_resolving_list (UINT8 addr_type_peer, BD_ADDR bda_peer, + UINT8 irk_peer[HCIC_BLE_IRK_SIZE], + UINT8 irk_local[HCIC_BLE_IRK_SIZE]) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF (HCIC_PARAM_SIZE_BLE_ADD_DEV_RESOLVING_LIST)) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_BLE_ADD_DEV_RESOLVING_LIST; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_BLE_ADD_DEV_RESOLVING_LIST); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_BLE_ADD_DEV_RESOLVING_LIST); + UINT8_TO_STREAM (pp, addr_type_peer); + BDADDR_TO_STREAM (pp, bda_peer); + ARRAY_TO_STREAM (pp, irk_peer, HCIC_BLE_ENCRYT_KEY_SIZE); + ARRAY_TO_STREAM (pp, irk_local, HCIC_BLE_ENCRYT_KEY_SIZE); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + + return (TRUE); +} + +BOOLEAN btsnd_hcic_ble_rm_device_resolving_list (UINT8 addr_type_peer, BD_ADDR bda_peer) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF (HCIC_PARAM_SIZE_BLE_RM_DEV_RESOLVING_LIST)) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_BLE_RM_DEV_RESOLVING_LIST; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_BLE_RM_DEV_RESOLVING_LIST); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_BLE_RM_DEV_RESOLVING_LIST); + UINT8_TO_STREAM (pp, addr_type_peer); + BDADDR_TO_STREAM (pp, bda_peer); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + + return (TRUE); +} + +BOOLEAN btsnd_hcic_ble_clear_resolving_list (void) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF (HCIC_PARAM_SIZE_BLE_CLEAR_RESOLVING_LIST)) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_BLE_CLEAR_RESOLVING_LIST; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_BLE_CLEAR_RESOLVING_LIST); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_BLE_CLEAR_RESOLVING_LIST); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + + return (TRUE); +} + +BOOLEAN btsnd_hcic_ble_read_resolvable_addr_peer (UINT8 addr_type_peer, BD_ADDR bda_peer) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF (HCIC_PARAM_SIZE_BLE_READ_RESOLVABLE_ADDR_PEER)) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_BLE_READ_RESOLVABLE_ADDR_PEER; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_BLE_READ_RESOLVABLE_ADDR_PEER); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_BLE_READ_RESOLVABLE_ADDR_PEER); + UINT8_TO_STREAM (pp, addr_type_peer); + BDADDR_TO_STREAM (pp, bda_peer); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + + return (TRUE); +} + +BOOLEAN btsnd_hcic_ble_read_resolvable_addr_local (UINT8 addr_type_peer, BD_ADDR bda_peer) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF (HCIC_PARAM_SIZE_BLE_READ_RESOLVABLE_ADDR_LOCAL)) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_BLE_READ_RESOLVABLE_ADDR_LOCAL; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_BLE_READ_RESOLVABLE_ADDR_LOCAL); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_BLE_READ_RESOLVABLE_ADDR_LOCAL); + UINT8_TO_STREAM (pp, addr_type_peer); + BDADDR_TO_STREAM (pp, bda_peer); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + + return (TRUE); +} + +BOOLEAN btsnd_hcic_ble_set_addr_resolution_enable (UINT8 addr_resolution_enable) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF (HCIC_PARAM_SIZE_BLE_SET_ADDR_RESOLUTION_ENABLE)) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_BLE_SET_ADDR_RESOLUTION_ENABLE; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_BLE_SET_ADDR_RESOLUTION_ENABLE); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_BLE_SET_ADDR_RESOLUTION_ENABLE); + UINT8_TO_STREAM (pp, addr_resolution_enable); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + + return (TRUE); +} + +BOOLEAN btsnd_hcic_ble_set_rand_priv_addr_timeout (UINT16 rpa_timout) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF (HCIC_PARAM_SIZE_BLE_SET_RAND_PRIV_ADDR_TIMOUT)) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_BLE_SET_RAND_PRIV_ADDR_TIMOUT; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_BLE_SET_RAND_PRIV_ADDR_TIMOUT); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_BLE_SET_RAND_PRIV_ADDR_TIMOUT); + UINT16_TO_STREAM (pp, rpa_timout); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + + return (TRUE); +} + +BOOLEAN btsnd_hcic_ble_set_data_length(UINT16 conn_handle, UINT16 tx_octets, UINT16 tx_time) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_BLE_SET_DATA_LENGTH)) == NULL) { + return FALSE; + } + + pp = p->data; + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_BLE_SET_DATA_LENGTH; + p->offset = 0; + + UINT16_TO_STREAM(pp, HCI_BLE_SET_DATA_LENGTH); + UINT8_TO_STREAM(pp, HCIC_PARAM_SIZE_BLE_SET_DATA_LENGTH); + + UINT16_TO_STREAM(pp, conn_handle); + UINT16_TO_STREAM(pp, tx_octets); + UINT16_TO_STREAM(pp, tx_time); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return TRUE; +} + +BOOLEAN btsnd_hcic_ble_update_adv_report_flow_control (UINT16 num, BT_HDR *static_buf) +{ + BT_HDR *p; + UINT8 *pp; + + if (static_buf != NULL) { + p = static_buf; + } else { + if ((p = HCI_GET_CMD_BUF (HCIC_PARAM_SIZE_BLE_UPDATE_ADV_FLOW_CONTROL)) == NULL) { + return (FALSE); + } + } + + hci_cmd_metadata_t *metadata = HCI_GET_CMD_METAMSG(p); + metadata->flags_src = HCI_CMD_MSG_F_SRC_NOACK; + if (static_buf == p) { + assert(metadata->command_free_cb != NULL); + } + p->layer_specific = HCI_CMD_BUF_TYPE_METADATA; + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_BLE_UPDATE_ADV_FLOW_CONTROL; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_VENDOR_BLE_ADV_REPORT_FLOW_CONTROL); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_BLE_UPDATE_ADV_FLOW_CONTROL); + UINT16_TO_STREAM (pp, num); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return TRUE; +} + +BOOLEAN btsnd_hcic_ble_set_channels (BLE_CHANNELS channels) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_BLE_SET_CHANNELS)) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_BLE_SET_CHANNELS; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_BLE_SET_HOST_CHNL_CLASS); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_BLE_SET_CHANNELS); + + ARRAY_TO_STREAM (pp, channels, HCIC_PARAM_SIZE_BLE_SET_CHANNELS); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_ble_clear_adv (void) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF (HCIC_PARAM_SIZE_BLE_CLEAR_ADV)) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_BLE_CLEAR_ADV; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_VENDOR_BLE_CLEAR_ADV); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_BLE_CLEAR_ADV); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return TRUE; +} + +#define HCIC_BLE_CMD_CREATED(p, pp, size) do{\ + if ((p = HCI_GET_CMD_BUF(size)) == NULL) { \ + return FALSE; \ + } \ + pp = p->data; \ + p->len = HCIC_PREAMBLE_SIZE + size;\ + p->offset = 0; \ +} while(0) + +#if (BLE_50_FEATURE_SUPPORT == TRUE) + +BOOLEAN btsnd_hcic_ble_read_phy(UINT16 conn_handle) +{ + BT_HDR *p; + UINT8 *pp; + + HCI_TRACE_EVENT("%s, conn_handle = %d", __func__, conn_handle); + HCIC_BLE_CMD_CREATED(p, pp, HCIC_PARAM_SIZE_BLE_READ_PHY); + + UINT16_TO_STREAM(pp, HCI_BLE_READ_PHY); + UINT8_TO_STREAM(pp, HCIC_PARAM_SIZE_BLE_READ_PHY); + UINT16_TO_STREAM(pp, conn_handle); + + btu_hcif_send_cmd(LOCAL_BR_EDR_CONTROLLER_ID, p); + return TRUE; +} + +UINT8 btsnd_hcic_ble_set_prefered_default_phy(UINT8 all_phys, + UINT8 tx_phys, + UINT8 rx_phys) +{ + BT_HDR *p; + UINT8 *pp; + + HCI_TRACE_EVENT("%s, all_phys = %d, tx_phys = %d, rx_phys = %d", __func__, all_phys, tx_phys, rx_phys); + HCIC_BLE_CMD_CREATED(p, pp, HCIC_PARAM_SIZE_BLE_SET_DEF_PHY); + + UINT16_TO_STREAM(pp, HCI_BLE_SET_DEFAULT_PHY); + UINT8_TO_STREAM(pp, HCIC_PARAM_SIZE_BLE_SET_DEF_PHY); + + UINT8_TO_STREAM(pp, all_phys); + UINT8_TO_STREAM(pp, tx_phys); + UINT8_TO_STREAM(pp, rx_phys); + + return btu_hcif_send_cmd_sync(LOCAL_BR_EDR_CONTROLLER_ID, p); +} + +BOOLEAN btsnd_hcic_ble_set_phy(UINT16 conn_handle, + UINT8 all_phys, UINT8 tx_phys, + UINT8 rx_phys, UINT16 phy_options) +{ + BT_HDR *p; + UINT8 *pp; + HCI_TRACE_EVENT("%s, conn_handle = %d, all_phys = %d, tx_phys = %d, rx_phys = %d, phy_options = %d", __func__, + conn_handle, all_phys, tx_phys, rx_phys, phy_options); + HCIC_BLE_CMD_CREATED(p, pp, HCIC_PARAM_SIZE_BLE_SET_PHY); + + UINT16_TO_STREAM(pp, HCI_BLE_SET_PHY); + UINT8_TO_STREAM(pp, HCIC_PARAM_SIZE_BLE_SET_PHY); + UINT16_TO_STREAM(pp, conn_handle); + UINT8_TO_STREAM(pp, all_phys); + UINT8_TO_STREAM(pp, tx_phys); + UINT8_TO_STREAM(pp, rx_phys); + UINT16_TO_STREAM(pp, phy_options); + + btu_hcif_send_cmd(LOCAL_BR_EDR_CONTROLLER_ID, p); + return TRUE; +} + +UINT8 btsnd_hcic_ble_enhand_rx_test(UINT8 rx_channel, UINT8 phy, + UINT8 modulation_idx) +{ + BT_HDR *p; + UINT8 *pp; + + HCI_TRACE_EVENT("%s, rx_channel = %d, phy = %d, modulation_idx = %d", __func__, + rx_channel, phy, modulation_idx); + HCIC_BLE_CMD_CREATED(p, pp, HCIC_PARAM_SIZE_ENH_RX_TEST); + + UINT16_TO_STREAM(pp, HCI_BLE_ENH_RX_TEST); + UINT8_TO_STREAM(pp, HCIC_PARAM_SIZE_ENH_RX_TEST); + UINT8_TO_STREAM(pp, rx_channel); + UINT8_TO_STREAM(pp, phy); + UINT8_TO_STREAM(pp, modulation_idx); + + btu_hcif_send_cmd(LOCAL_BR_EDR_CONTROLLER_ID, p); + + return TRUE; +} + +UINT8 btsnd_hcic_ble_enhand_tx_test(UINT8 tx_channel, UINT8 len, + UINT8 packect, + UINT8 phy) +{ + BT_HDR *p; + UINT8 *pp; + + HCI_TRACE_EVENT("%s, tx_channel = %d, len = %d, packect = %d, phy = %d", __func__, + tx_channel, len, packect, phy); + + HCIC_BLE_CMD_CREATED(p, pp, HCIC_PARAM_SIZE_ENH_TX_TEST); + + UINT16_TO_STREAM(pp, HCI_BLE_ENH_TX_TEST); + UINT8_TO_STREAM(pp, HCIC_PARAM_SIZE_ENH_TX_TEST); + UINT8_TO_STREAM(pp, tx_channel); + UINT8_TO_STREAM(pp, len); + UINT8_TO_STREAM(pp, packect); + UINT8_TO_STREAM(pp, phy); + + btu_hcif_send_cmd(LOCAL_BR_EDR_CONTROLLER_ID, p); + + return TRUE; +} + +UINT8 btsnd_hcic_ble_set_extend_rand_address(UINT8 adv_handle, BD_ADDR rand_addr) +{ + BT_HDR *p; + UINT8 *pp; + HCI_TRACE_EVENT("%s, adv_handle = %d", __func__, adv_handle); + + HCIC_BLE_CMD_CREATED(p, pp, HCIC_PARAM_SIZE_EXT_RAND_ADDR); + + UINT16_TO_STREAM (pp, HCI_BLE_SET_ADV_RAND_ADDR); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_EXT_RAND_ADDR); + UINT8_TO_STREAM(pp, adv_handle); + BDADDR_TO_STREAM (pp, rand_addr); + + return btu_hcif_send_cmd_sync (LOCAL_BR_EDR_CONTROLLER_ID, p); + +} + +UINT8 btsnd_hcic_ble_set_ext_adv_params(UINT8 adv_handle, UINT16 properties, UINT32 interval_min, + UINT32 interval_max, UINT8 channel_map, UINT8 own_addr_type, + UINT8 peer_addr_type, BD_ADDR peer_addr, + UINT8 adv_filter_policy, INT8 adv_tx_power, + UINT8 primary_adv_phy, UINT8 secondary_adv_max_skip, + UINT8 secondary_adv_phy, + UINT8 adv_sid, UINT8 scan_req_ntf_enable) +{ + BT_HDR *p; + UINT8 *pp; + + + HCI_TRACE_EVENT("%s, adv_handle = %d, properties = %d, interval_min = %d, interval_max = %d, channel_map = %d,\n\ + own_addr_type = %d, peer_addr_type = %d, adv_filter_policy = %d,\n\ + adv_tx_power = %d, primary_adv_phy = %d, secondary_adv_max_skip = %d, secondary_adv_phy = %d,\n\ + adv_sid = %d, scan_req_ntf_enable = %d", __func__, adv_handle, properties, interval_min, interval_max, + channel_map, own_addr_type, peer_addr_type, adv_filter_policy, adv_tx_power, + primary_adv_phy, secondary_adv_max_skip, secondary_adv_phy, adv_sid, scan_req_ntf_enable); + + HCIC_BLE_CMD_CREATED(p, pp, HCIC_PARAM_SIZE_EXT_ADV_SET_PARAMS); + + UINT16_TO_STREAM(pp, HCI_BLE_SET_EXT_ADV_PARAM); + UINT8_TO_STREAM(pp, HCIC_PARAM_SIZE_EXT_ADV_SET_PARAMS); + + UINT8_TO_STREAM(pp, adv_handle); + UINT16_TO_STREAM(pp, properties); + UINT24_TO_STREAM(pp, interval_min); + UINT24_TO_STREAM(pp, interval_max); + UINT8_TO_STREAM(pp, channel_map); + UINT8_TO_STREAM(pp, own_addr_type); + UINT8_TO_STREAM(pp, peer_addr_type); + BDADDR_TO_STREAM (pp, peer_addr); + UINT8_TO_STREAM(pp, adv_filter_policy); + INT8_TO_STREAM(pp, adv_tx_power); + UINT8_TO_STREAM(pp, primary_adv_phy); + UINT8_TO_STREAM(pp, secondary_adv_max_skip); + UINT8_TO_STREAM(pp, secondary_adv_phy); + UINT8_TO_STREAM(pp, adv_sid); + UINT8_TO_STREAM(pp, scan_req_ntf_enable); + + return btu_hcif_send_cmd_sync (LOCAL_BR_EDR_CONTROLLER_ID, p); +} + +bool ext_adv_flag = false; + +UINT8 btsnd_hcic_ble_set_ext_adv_data(UINT8 adv_handle, + UINT8 operation, UINT8 fragment_prefrence, + UINT8 data_len, UINT8 *p_data) +{ + BT_HDR *p; + UINT8 *pp; + HCI_TRACE_EVENT("%s, adv_handle = %d, operation = %d, fragment_prefrence = %d,\ + data_len = %d", __func__, adv_handle, operation, fragment_prefrence, data_len); + ext_adv_flag = true; + + HCIC_BLE_CMD_CREATED(p, pp, data_len + 4); + UINT16_TO_STREAM(pp, HCI_BLE_SET_EXT_ADV_DATA); + UINT8_TO_STREAM(pp, data_len + 4); + UINT8_TO_STREAM(pp, adv_handle); + UINT8_TO_STREAM(pp, operation); + UINT8_TO_STREAM(pp, fragment_prefrence); + + if (data_len > HCIC_PARAM_SIZE_EXT_ADV_WRITE_DATA) { + data_len = HCIC_PARAM_SIZE_EXT_ADV_WRITE_DATA; + } + + UINT8_TO_STREAM (pp, data_len); + + if (p_data != NULL && data_len > 0){ + ARRAY_TO_STREAM (pp, p_data, data_len); + } + + uint8_t status = btu_hcif_send_cmd_sync (LOCAL_BR_EDR_CONTROLLER_ID, p); + return status; + +} + +UINT8 btsnd_hcic_ble_set_ext_adv_scan_rsp_data(UINT8 adv_handle, + UINT8 operation, UINT8 fragment_prefrence, + UINT8 data_len, UINT8 *p_data) +{ + BT_HDR *p; + UINT8 *pp; + + HCI_TRACE_EVENT("%s, adv_handle = %d, operation = %d, fragment_prefrence = %d,\n\ + data_len = %d", __func__, adv_handle, operation, fragment_prefrence, data_len); + + HCIC_BLE_CMD_CREATED(p, pp, data_len + 4); + + UINT16_TO_STREAM(pp, HCI_BLE_SET_EXT_SCAN_RSP_DATA); + UINT8_TO_STREAM(pp, data_len + 4); + UINT8_TO_STREAM(pp, adv_handle); + UINT8_TO_STREAM(pp, operation); + UINT8_TO_STREAM(pp, fragment_prefrence); + + memset(pp, 0, data_len); + + if (data_len > HCIC_PARAM_SIZE_EXT_ADV_WRITE_DATA) { + data_len = HCIC_PARAM_SIZE_EXT_ADV_WRITE_DATA; + } + + UINT8_TO_STREAM (pp, data_len); + if (p_data != NULL && data_len > 0) { + ARRAY_TO_STREAM (pp, p_data, data_len); + } + + return btu_hcif_send_cmd_sync (LOCAL_BR_EDR_CONTROLLER_ID, p); + +} + +UINT8 btsnd_hcic_ble_ext_adv_enable(UINT8 enable, UINT8 num_of_sets, UINT8 *adv_handle, + UINT16 *duration, UINT8 *max_adv_evt) +{ + BT_HDR *p; + UINT8 *pp; + UINT8 ext_adv_size = num_of_sets*4 + 2; + HCIC_BLE_CMD_CREATED(p, pp, ext_adv_size); + + HCI_TRACE_EVENT("%s, enable = %d, num_of_sets = %d", __func__, enable, num_of_sets); + + for (int k = 0; k < num_of_sets; k++) { + HCI_TRACE_EVENT("adv_handle[%d] = %d, duration[%d] = %d, max_adv_evt[%d] = %d", k, adv_handle[k], + k, duration[k], k, max_adv_evt[k]); + } + + UINT16_TO_STREAM(pp, HCI_BLE_SET_EXT_ADV_ENABLE); + UINT8_TO_STREAM(pp, ext_adv_size); + UINT8_TO_STREAM(pp, enable); + UINT8_TO_STREAM(pp, num_of_sets); + + for (int i = 0; i < num_of_sets; i++) { + UINT8_TO_STREAM(pp, adv_handle[i]); + UINT16_TO_STREAM(pp, duration[i]); + UINT8_TO_STREAM(pp, max_adv_evt[i]); + } + + return btu_hcif_send_cmd_sync (LOCAL_BR_EDR_CONTROLLER_ID, p); +} + +UINT8 btsnd_hcic_ble_read_max_adv_len(void) +{ + BT_HDR *p; + UINT8 *pp; + HCI_TRACE_EVENT("%s", __func__); + + HCIC_BLE_CMD_CREATED(p, pp, HCIC_PARAM_SIZE_READ_MAX_ADV_SIZE + 1); + + UINT16_TO_STREAM(pp, HCI_BLE_RD_MAX_ADV_DATA_LEN); + UINT8_TO_STREAM(pp, HCIC_PARAM_SIZE_READ_MAX_ADV_SIZE); + + return btu_hcif_send_cmd_sync (LOCAL_BR_EDR_CONTROLLER_ID, p); +} + +UINT8 btsnd_hcic_ble_read_num_support_adv_set(void) +{ + BT_HDR *p; + UINT8 *pp; + HCI_TRACE_EVENT("%s", __func__); + + HCIC_BLE_CMD_CREATED(p, pp, HCIC_PARAM_SIZE_NUM_SUPPORT_ADV_SET + 1); + + UINT16_TO_STREAM(pp, HCI_BLE_RD_NUM_OF_ADV_SETS); + UINT8_TO_STREAM(pp, HCIC_PARAM_SIZE_NUM_SUPPORT_ADV_SET); + + return btu_hcif_send_cmd_sync (LOCAL_BR_EDR_CONTROLLER_ID, p); +} + +UINT8 btsnd_hcic_ble_remove_adv_set(UINT8 adv_handle) +{ + BT_HDR *p; + UINT8 *pp; + HCI_TRACE_EVENT("%s, adv_handle = %d", __func__, adv_handle); + + HCIC_BLE_CMD_CREATED(p, pp, HCIC_PARAM_SIZE_REMOVE_ADV_SET + 1); + + UINT16_TO_STREAM(pp, HCI_BLE_REMOVE_ADV_SET); + UINT8_TO_STREAM(pp, HCIC_PARAM_SIZE_REMOVE_ADV_SET); + UINT8_TO_STREAM(pp, adv_handle); + + return btu_hcif_send_cmd_sync(LOCAL_BR_EDR_CONTROLLER_ID, p); +} + +UINT8 btsnd_hcic_ble_clear_adv_set(void) +{ + BT_HDR *p; + UINT8 *pp; + HCI_TRACE_EVENT("%s", __func__); + + HCIC_BLE_CMD_CREATED(p, pp, HCIC_PARAM_SIZE_CLEAR_ADV_SET + 1); + + UINT16_TO_STREAM(pp, HCI_BLE_CLEAR_ADV_SETS); + UINT8_TO_STREAM(pp, HCIC_PARAM_SIZE_CLEAR_ADV_SET); + + return btu_hcif_send_cmd_sync(LOCAL_BR_EDR_CONTROLLER_ID, p); + +} + +UINT8 btsnd_hcic_ble_set_periodic_adv_params(UINT8 adv_handle, + UINT16 interval_min, + UINT16 interval_max, + UINT8 propertics) +{ + BT_HDR *p; + UINT8 *pp; + HCI_TRACE_EVENT("%s, adv_handle = %d, interval_min = %d, interval_max = %d, propertics = %d", + __func__, adv_handle, interval_min, interval_max, propertics); + + HCIC_BLE_CMD_CREATED(p, pp, HCIC_PARAM_SIZE_SET_PERIODIC_ADV_PARAMS); + + UINT16_TO_STREAM(pp, HCI_BLE_SET_PERIOD_ADV_PARAMS); + UINT8_TO_STREAM(pp, HCIC_PARAM_SIZE_SET_PERIODIC_ADV_PARAMS); + + UINT8_TO_STREAM(pp, adv_handle); + UINT16_TO_STREAM(pp, interval_min); + UINT16_TO_STREAM(pp, interval_max); + UINT16_TO_STREAM(pp, propertics); + + return btu_hcif_send_cmd_sync(LOCAL_BR_EDR_CONTROLLER_ID, p); + +} + +UINT8 btsnd_hcic_ble_set_periodic_adv_data(UINT8 adv_handle, + UINT8 operation, + UINT8 len, + UINT8 *p_data) +{ + BT_HDR *p; + UINT8 *pp; + HCI_TRACE_EVENT("%s, adv_handle = %d, operation = %d, len = %d", + __func__, adv_handle, operation, len); + + HCIC_BLE_CMD_CREATED(p, pp, len + 3); + + UINT16_TO_STREAM(pp, HCI_BLE_SET_PERIOD_ADV_DATA); + UINT8_TO_STREAM(pp, len + 3); + UINT8_TO_STREAM(pp, adv_handle); + UINT8_TO_STREAM(pp, operation); + + //memset(pp, 0, len); + + if (len > HCIC_PARAM_SIZE_WRITE_PERIODIC_ADV_DATA) { + len = HCIC_PARAM_SIZE_WRITE_PERIODIC_ADV_DATA; + } + + UINT8_TO_STREAM (pp, len); + + if (p_data != NULL && len > 0) { + ARRAY_TO_STREAM (pp, p_data, len); + } + + return btu_hcif_send_cmd_sync(LOCAL_BR_EDR_CONTROLLER_ID, p); + +} + +UINT8 btsnd_hcic_ble_periodic_adv_enable(UINT8 enable, UINT8 adv_handle) +{ + BT_HDR *p; + UINT8 *pp; + HCI_TRACE_EVENT("%s, enable = %d, adv_handle = %d", + __func__, enable, adv_handle); + + HCIC_BLE_CMD_CREATED(p, pp, HCIC_PARAM_SIZE_PERIODIC_ADV_ENABLE); + + UINT16_TO_STREAM(pp, HCI_BLE_SET_PERIOD_ADV_ENABLE); + UINT8_TO_STREAM(pp, HCIC_PARAM_SIZE_PERIODIC_ADV_ENABLE); + UINT8_TO_STREAM(pp, enable); + UINT8_TO_STREAM(pp, adv_handle); + + return btu_hcif_send_cmd_sync(LOCAL_BR_EDR_CONTROLLER_ID, p); + +} + +UINT8 btsnd_hcic_ble_set_ext_scan_params(UINT8 own_addr_type, UINT8 filter_policy, + UINT8 phy_mask, UINT8 phy_count, + tHCI_EXT_SCAN_PARAMS *params) +{ + BT_HDR *p; + UINT8 *pp; + HCI_TRACE_EVENT("%s, own_addr_type = %d, filter_policy = %d, phy_mask = %d, phy_count = %d", + __func__, own_addr_type, filter_policy, phy_mask, phy_count); + UINT8 params_size = HCIC_PARAM_SIZE_SET_EXT_SCAN_PARAMS + phy_count*5; + + HCIC_BLE_CMD_CREATED(p, pp, params_size); + + UINT16_TO_STREAM(pp, HCI_BLE_SET_EXT_SCAN_PARAMS); + UINT8_TO_STREAM(pp, params_size); + UINT8_TO_STREAM(pp, own_addr_type); + UINT8_TO_STREAM(pp, filter_policy); + UINT8_TO_STREAM(pp, phy_mask); + + for (int i = 0; i < phy_count; i++) { + UINT8_TO_STREAM(pp, params[i].scan_type); + UINT16_TO_STREAM(pp, params[i].scan_interval); + UINT16_TO_STREAM(pp, params[i].scan_window); + } + + return btu_hcif_send_cmd_sync(LOCAL_BR_EDR_CONTROLLER_ID, p); + +} + +UINT8 btsnd_hcic_ble_ext_scan_enable(UINT8 enable, UINT8 filter_dups, + UINT16 duration, UINT16 period) +{ + BT_HDR *p; + UINT8 *pp; + HCI_TRACE_EVENT("%s, enable = %d, filter_dups = %d, duration = %d, period = %d", + __func__, enable, filter_dups, duration, period); + + HCIC_BLE_CMD_CREATED(p, pp, HCIC_PARAM_SIZE_EXT_SCAN_ENABLE); + + UINT16_TO_STREAM(pp, HCI_BLE_SET_EXT_SCAN_ENABLE); + UINT8_TO_STREAM(pp, HCIC_PARAM_SIZE_EXT_SCAN_ENABLE); + UINT8_TO_STREAM(pp, enable); + UINT8_TO_STREAM(pp, filter_dups); + UINT16_TO_STREAM(pp, duration); + UINT16_TO_STREAM(pp, period); + + return btu_hcif_send_cmd_sync(LOCAL_BR_EDR_CONTROLLER_ID, p); +} + +BOOLEAN btsnd_hcic_ble_create_ext_conn(tHCI_CreatExtConn *p_conn) +{ + BT_HDR *p; + UINT8 *pp; + tHCI_ExtConnParams *params; + HCI_TRACE_EVENT("%s", __func__); + uint8_t size = HCIC_PARAM_SIZE_EXT_CONN_CREATE_BASE; + + if (p_conn->init_phy_mask & 0x01) { + size += sizeof(tHCI_ExtConnParams); + } + + if (p_conn->init_phy_mask & 0x02) { + size += sizeof(tHCI_ExtConnParams); + } + + if (p_conn->init_phy_mask & 0x04) { + size += sizeof(tHCI_ExtConnParams); + } + + HCIC_BLE_CMD_CREATED(p, pp, size); + + UINT16_TO_STREAM(pp, HCI_BLE_EXT_CREATE_CONN); + UINT8_TO_STREAM(pp, size); + UINT8_TO_STREAM(pp, p_conn->filter_policy); + UINT8_TO_STREAM(pp, p_conn->own_addr_type); + UINT8_TO_STREAM(pp, p_conn->peer_addr_type); + BDADDR_TO_STREAM(pp, p_conn->peer_addr); + UINT8_TO_STREAM(pp, p_conn->init_phy_mask); + + if (p_conn->init_phy_mask & 0x01) { + params = &p_conn->params[0]; + UINT16_TO_STREAM(pp, params->scan_interval); + UINT16_TO_STREAM(pp, params->scan_window); + UINT16_TO_STREAM(pp, params->conn_interval_min); + UINT16_TO_STREAM(pp, params->conn_interval_max); + UINT16_TO_STREAM(pp, params->conn_latency); + UINT16_TO_STREAM(pp, params->sup_timeout); + UINT16_TO_STREAM(pp, BLE_CE_LEN_MIN); + UINT16_TO_STREAM(pp, BLE_CE_LEN_MIN); + } + + if (p_conn->init_phy_mask & 0x02) { + params = &p_conn->params[1]; + UINT16_TO_STREAM(pp, params->scan_interval); + UINT16_TO_STREAM(pp, params->scan_window); + UINT16_TO_STREAM(pp, params->conn_interval_min); + UINT16_TO_STREAM(pp, params->conn_interval_max); + UINT16_TO_STREAM(pp, params->conn_latency); + UINT16_TO_STREAM(pp, params->sup_timeout); + UINT16_TO_STREAM(pp, BLE_CE_LEN_MIN); + UINT16_TO_STREAM(pp, BLE_CE_LEN_MIN); + } + + if (p_conn->init_phy_mask & 0x04) { + params = &p_conn->params[2]; + UINT16_TO_STREAM(pp, params->scan_interval); + UINT16_TO_STREAM(pp, params->scan_window); + UINT16_TO_STREAM(pp, params->conn_interval_min); + UINT16_TO_STREAM(pp, params->conn_interval_max); + UINT16_TO_STREAM(pp, params->conn_latency); + UINT16_TO_STREAM(pp, params->sup_timeout); + UINT16_TO_STREAM(pp, BLE_CE_LEN_MIN); + UINT16_TO_STREAM(pp, BLE_CE_LEN_MIN); + } + + btu_hcif_send_cmd(LOCAL_BR_EDR_CONTROLLER_ID, p); + return TRUE; + +} + +BOOLEAN btsnd_hcic_ble_periodic_adv_create_sync(UINT8 option, UINT8 adv_sid, + UINT8 adv_addr_type, BD_ADDR adv_addr, + UINT16 sync_timeout, UINT8 unused) +{ + BT_HDR *p; + UINT8 *pp; + HCI_TRACE_EVENT("%s, option = %d, adv_sid = %d, adv_addr_type = %d, sync_timeout = %d, unused = %d", + __func__, option, adv_sid, adv_addr_type, sync_timeout, unused); + + HCI_TRACE_EVENT("addr %02x %02x %02x %02x %02x %02x", adv_addr[0], adv_addr[1], adv_addr[2], adv_addr[3], adv_addr[4], adv_addr[5]); + uint16_t skip = 0; + HCIC_BLE_CMD_CREATED(p, pp, HCIC_PARAM_SIZE_PERIODIC_ADV_CREATE_SYNC + 2); + + UINT16_TO_STREAM(pp, HCI_BLE_PERIOD_ADV_CREATE_SYNC); + UINT8_TO_STREAM(pp, HCIC_PARAM_SIZE_PERIODIC_ADV_CREATE_SYNC + 2); + UINT8_TO_STREAM(pp, option); + UINT8_TO_STREAM(pp, adv_sid); + UINT8_TO_STREAM(pp, adv_addr_type); + BDADDR_TO_STREAM(pp, adv_addr); + UINT16_TO_STREAM(pp, skip); + UINT16_TO_STREAM(pp, sync_timeout); + UINT8_TO_STREAM(pp, unused); + + btu_hcif_send_cmd(LOCAL_BR_EDR_CONTROLLER_ID, p); + return TRUE; +} + +UINT8 btsnd_hcic_ble_periodic_adv_create_sync_cancel(void) +{ + BT_HDR *p; + UINT8 *pp; + HCI_TRACE_EVENT("%s", __func__); + + HCIC_BLE_CMD_CREATED(p, pp, HCIC_PARAM_SIZE_PERIODIC_ADV_CREATE_SYNC_CANCEL); + + UINT16_TO_STREAM(pp, HCI_BLE_PERIOD_ADV_CREATE_SYNC_CANCEL); + UINT8_TO_STREAM(pp, HCIC_PARAM_SIZE_PERIODIC_ADV_CREATE_SYNC_CANCEL); + + return btu_hcif_send_cmd_sync(LOCAL_BR_EDR_CONTROLLER_ID, p); +} + +UINT8 btsnd_hcic_ble_periodic_adv_term_sync(UINT16 sync_handle) +{ + BT_HDR *p; + UINT8 *pp; + HCI_TRACE_EVENT("%s, sync_handle = %d", __func__, sync_handle); + + HCIC_BLE_CMD_CREATED(p, pp, HCIC_PARAM_SIZE_PERIODIC_ADV_TERM_SYNC); + + UINT16_TO_STREAM(pp, HCI_BLE_PERIOD_ADV_TERM_SYNC); + UINT8_TO_STREAM(pp, HCIC_PARAM_SIZE_PERIODIC_ADV_TERM_SYNC); + + UINT16_TO_STREAM(pp, sync_handle); + + return btu_hcif_send_cmd_sync(LOCAL_BR_EDR_CONTROLLER_ID, p); +} + +UINT8 btsnd_hcic_ble_add_dev_to_periodic_adv_list(UINT8 adv_addr_type, BD_ADDR adv_addr, + UINT8 adv_sid) +{ + BT_HDR *p; + UINT8 *pp; + HCI_TRACE_EVENT("%s, adv_addr_type = %d, adv_sid = %d", __func__, adv_addr_type, adv_sid); + esp_log_buffer_hex_internal("addr", adv_addr, sizeof(BD_ADDR), ESP_LOG_WARN); + + HCIC_BLE_CMD_CREATED(p, pp, HCIC_PARAM_SIZE_ADD_DEV_TO_PERIODIC_ADV_LIST); + + UINT16_TO_STREAM(pp, HCI_BLE_ADV_DEV_TO_PERIOD_ADV_LIST); + UINT8_TO_STREAM(pp, HCIC_PARAM_SIZE_ADD_DEV_TO_PERIODIC_ADV_LIST); + UINT8_TO_STREAM(pp, adv_addr_type); + BDADDR_TO_STREAM(pp, adv_addr); + UINT8_TO_STREAM(pp, adv_sid); + + return btu_hcif_send_cmd_sync(LOCAL_BR_EDR_CONTROLLER_ID, p); +} + +UINT8 btsnd_hcic_ble_rm_dev_from_periodic_adv_list(UINT8 adv_addr_type, BD_ADDR adv_addr, + UINT8 adv_sid) +{ + BT_HDR *p; + UINT8 *pp; + HCI_TRACE_EVENT("%s, adv_addr_type = %d, adv_sid = %d", __func__, adv_addr_type, adv_sid); + esp_log_buffer_hex_internal("addr", adv_addr, sizeof(BD_ADDR), ESP_LOG_WARN); + + HCIC_BLE_CMD_CREATED(p, pp, HCIC_PARAM_SIZE_RM_DEV_FROM_PERIODIC_ADV_LIST); + + UINT16_TO_STREAM(pp, HCI_BLE_REMOVE_DEV_FROM_PERIOD_ADV_LIST); + UINT8_TO_STREAM(pp, HCIC_PARAM_SIZE_RM_DEV_FROM_PERIODIC_ADV_LIST); + UINT8_TO_STREAM(pp, adv_addr_type); + BDADDR_TO_STREAM(pp, adv_addr); + UINT8_TO_STREAM(pp, adv_sid); + + return btu_hcif_send_cmd_sync(LOCAL_BR_EDR_CONTROLLER_ID, p); + +} + +UINT8 btsnd_hcic_ble_clear_periodic_adv_list(void) +{ + BT_HDR *p; + UINT8 *pp; + HCI_TRACE_EVENT("%s", __func__); + + HCIC_BLE_CMD_CREATED(p, pp, HCIC_PARAM_SIZE_CLEAR_PERIODIC_ADV_LIST); + + UINT16_TO_STREAM(pp, HCI_BLE_CLEAR_PERIOD_ADV_LIST); + UINT8_TO_STREAM(pp, HCIC_PARAM_SIZE_CLEAR_PERIODIC_ADV_LIST); + + return btu_hcif_send_cmd_sync(LOCAL_BR_EDR_CONTROLLER_ID, p); +} + +UINT8 btsnd_hcic_ble_read_periodic_adv_list_size(void) +{ + BT_HDR *p; + UINT8 *pp; + HCI_TRACE_EVENT("%s", __func__); + + HCIC_BLE_CMD_CREATED(p, pp, HCIC_PARAM_SIZE_READ_PERIODIC_ADV_LIST); + + UINT16_TO_STREAM(pp, HCI_BLE_RD_PERIOD_ADV_LIST_SIZE); + UINT8_TO_STREAM(pp, HCIC_PARAM_SIZE_READ_PERIODIC_ADV_LIST); + + return btu_hcif_send_cmd_sync(LOCAL_BR_EDR_CONTROLLER_ID, p); +} + +UINT8 btsnd_hcic_ble_read_trans_power(void) +{ + BT_HDR *p; + UINT8 *pp; + HCI_TRACE_EVENT("%s", __func__); + + HCIC_BLE_CMD_CREATED(p, pp, HCIC_PARAM_SIZE_READ_TRANS_POWER); + + UINT16_TO_STREAM(pp, HCI_BLE_RD_TRANSMIT_POWER); + UINT8_TO_STREAM(pp, HCIC_PARAM_SIZE_READ_TRANS_POWER); + + return btu_hcif_send_cmd_sync(LOCAL_BR_EDR_CONTROLLER_ID, p); +} + +UINT8 btsnd_hcic_ble_read_rf_path_compensation(void) +{ + BT_HDR *p; + UINT8 *pp; + HCI_TRACE_EVENT("%s", __func__); + + HCIC_BLE_CMD_CREATED(p, pp, HCIC_PARAM_SIZE_READ_RF_PATH_COMPENSATION); + + UINT16_TO_STREAM(pp, HCI_BLE_RD_RF_PATH_COMPENSATION); + UINT8_TO_STREAM(pp, HCIC_PARAM_SIZE_READ_RF_PATH_COMPENSATION); + + return btu_hcif_send_cmd_sync(LOCAL_BR_EDR_CONTROLLER_ID, p); +} + +UINT8 btsnd_hcic_ble_write_rf_path_compensation(UINT16 rf_tx_path, UINT16 rf_rx_path) +{ + BT_HDR *p; + UINT8 *pp; + HCI_TRACE_EVENT("%s, rf_tx_path = %d, rf_rx_path = %d", __func__, rf_tx_path, rf_rx_path); + + HCIC_BLE_CMD_CREATED(p, pp, HCIC_PARAM_SIZE_WRITE_RF_PATH_COMPENSATION); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_WRITE_RF_PATH_COMPENSATION; + p->offset = 0; + + UINT16_TO_STREAM(pp, HCI_BLE_WR_RF_PATH_COMPENSATION); + UINT8_TO_STREAM(pp, HCIC_PARAM_SIZE_WRITE_RF_PATH_COMPENSATION); + + UINT16_TO_STREAM(pp, rf_tx_path); + UINT16_TO_STREAM(pp, rf_tx_path); + + return btu_hcif_send_cmd_sync(LOCAL_BR_EDR_CONTROLLER_ID, p); +} +#endif // #if (BLE_50_FEATURE_SUPPORT == TRUE) + +#if (BLE_FEAT_PERIODIC_ADV_SYNC_TRANSFER == TRUE) +UINT8 btsnd_hcic_ble_set_periodic_adv_recv_enable(UINT16 sync_handle, UINT8 enable) +{ + BT_HDR *p; + UINT8 *pp; + + HCIC_BLE_CMD_CREATED(p, pp, HCIC_PARAM_SIZE_PERIODIC_ADV_RECV_ENABLE); + + pp = (UINT8 *)(p + 1); + + UINT16_TO_STREAM(pp, HCI_BLE_SET_PERIOD_ADV_RECV_ENABLE); + UINT8_TO_STREAM(pp, HCIC_PARAM_SIZE_PERIODIC_ADV_RECV_ENABLE); + + UINT16_TO_STREAM(pp, sync_handle); + UINT8_TO_STREAM(pp, enable); + + return btu_hcif_send_cmd_sync(LOCAL_BR_EDR_CONTROLLER_ID, p); +} + +BOOLEAN btsnd_hcic_ble_periodic_adv_sync_trans(UINT16 conn_handle, UINT16 service_data, UINT16 sync_handle) +{ + BT_HDR *p; + UINT8 *pp; + + HCIC_BLE_CMD_CREATED(p, pp, HCIC_PARAM_SIZE_PERIODIC_ADV_SYNC_TRANS); + + pp = (UINT8 *)(p + 1); + + UINT16_TO_STREAM(pp, HCI_BLE_PERIOD_ADV_SYNC_TRANS); + UINT8_TO_STREAM(pp, HCIC_PARAM_SIZE_PERIODIC_ADV_SYNC_TRANS); + + UINT16_TO_STREAM(pp, conn_handle); + UINT16_TO_STREAM(pp, service_data); + UINT16_TO_STREAM(pp, sync_handle); + + btu_hcif_send_cmd(LOCAL_BR_EDR_CONTROLLER_ID, p); + return TRUE; +} + +BOOLEAN btsnd_hcic_ble_periodic_adv_set_info_trans(UINT16 conn_handle, UINT16 service_data, UINT8 adv_handle) +{ + BT_HDR *p; + UINT8 *pp; + + HCI_TRACE_DEBUG("%s conn handle %x, adv handle %x", __func__, conn_handle, adv_handle); + + HCIC_BLE_CMD_CREATED(p, pp, HCIC_PARAM_SIZE_PERIODIC_ADV_SET_INFO_TRANS); + + pp = (UINT8 *)(p + 1); + + UINT16_TO_STREAM(pp, HCI_BLE_PERIOD_ADV_SET_INFO_TRANS); + UINT8_TO_STREAM(pp, HCIC_PARAM_SIZE_PERIODIC_ADV_SET_INFO_TRANS); + + UINT16_TO_STREAM(pp, conn_handle); + UINT16_TO_STREAM(pp, service_data); + UINT8_TO_STREAM(pp, adv_handle); + + btu_hcif_send_cmd(LOCAL_BR_EDR_CONTROLLER_ID, p); + return TRUE; +} + +BOOLEAN btsnd_hcic_ble_set_periodic_adv_sync_trans_params(UINT16 conn_handle, UINT8 mode, UINT16 skip, UINT16 sync_timeout, UINT8 cte_type) +{ + BT_HDR *p; + UINT8 *pp; + + HCI_TRACE_DEBUG("%s conn handle %x, mode %x, sync timeout %x", __func__, conn_handle, mode, sync_timeout); + + HCIC_BLE_CMD_CREATED(p, pp, HCIC_PARAM_SIZE_SET_PAST_PARAMS); + + pp = (UINT8 *)(p + 1); + + UINT16_TO_STREAM(pp, HCI_BLE_SET_PAST_PARAMS); + UINT8_TO_STREAM(pp, HCIC_PARAM_SIZE_SET_PAST_PARAMS); + + UINT16_TO_STREAM(pp, conn_handle); + UINT8_TO_STREAM(pp, mode); + UINT16_TO_STREAM(pp, skip); + UINT16_TO_STREAM(pp, sync_timeout); + UINT8_TO_STREAM(pp, cte_type); + + btu_hcif_send_cmd(LOCAL_BR_EDR_CONTROLLER_ID, p); + return TRUE; +} + +UINT8 btsnd_hcic_ble_set_default_periodic_adv_sync_trans_params(UINT8 mode, UINT16 skip, UINT16 sync_timeout, UINT8 cte_type) +{ + BT_HDR *p; + UINT8 *pp; + + HCI_TRACE_DEBUG("%s mode %x, skip %x, sync timeout %x", __func__, mode, skip, sync_timeout); + + HCIC_BLE_CMD_CREATED(p, pp, HCIC_PARAM_SIZE_SET_DEFAULT_PAST_PARAMS); + + pp = (UINT8 *)(p + 1); + + UINT16_TO_STREAM(pp, HCI_BLE_SET_DEFAULT_PAST_PARAMS); + UINT8_TO_STREAM(pp, HCIC_PARAM_SIZE_SET_DEFAULT_PAST_PARAMS); + + UINT8_TO_STREAM(pp, mode); + UINT16_TO_STREAM(pp, skip); + UINT16_TO_STREAM(pp, sync_timeout); + UINT8_TO_STREAM(pp, cte_type); + + return btu_hcif_send_cmd_sync(LOCAL_BR_EDR_CONTROLLER_ID, p); +} +#endif // #if (BLE_FEAT_PERIODIC_ADV_SYNC_TRANSFER == TRUE) +#endif diff --git a/lib/bt/host/bluedroid/stack/hcic/hcicmds.c b/lib/bt/host/bluedroid/stack/hcic/hcicmds.c new file mode 100644 index 00000000..89995eba --- /dev/null +++ b/lib/bt/host/bluedroid/stack/hcic/hcicmds.c @@ -0,0 +1,1913 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains function of the HCIC unit to format and send HCI + * commands. + * + ******************************************************************************/ + +#include "common/bt_target.h" +#include "osi/allocator.h" +#include "stack/hcidefs.h" +#include "stack/hcimsgs.h" +#include "stack/hcidefs.h" +#include "stack/btu.h" + +#include +#include + +#include "btm_int.h" /* Included for UIPC_* macro definitions */ + +BOOLEAN btsnd_hcic_inquiry(const LAP inq_lap, UINT8 duration, UINT8 response_cnt) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_INQUIRY)) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_INQUIRY; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_INQUIRY); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_INQUIRY); + + LAP_TO_STREAM (pp, inq_lap); + UINT8_TO_STREAM (pp, duration); + UINT8_TO_STREAM (pp, response_cnt); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_inq_cancel(void) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_INQ_CANCEL)) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_INQ_CANCEL; + p->offset = 0; + UINT16_TO_STREAM (pp, HCI_INQUIRY_CANCEL); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_INQ_CANCEL); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_per_inq_mode (UINT16 max_period, UINT16 min_period, + const LAP inq_lap, UINT8 duration, UINT8 response_cnt) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_PER_INQ_MODE)) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_PER_INQ_MODE; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_PERIODIC_INQUIRY_MODE); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_PER_INQ_MODE); + + UINT16_TO_STREAM (pp, max_period); + UINT16_TO_STREAM (pp, min_period); + LAP_TO_STREAM (pp, inq_lap); + UINT8_TO_STREAM (pp, duration); + UINT8_TO_STREAM (pp, response_cnt); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_exit_per_inq (void) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_EXIT_PER_INQ)) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_EXIT_PER_INQ; + p->offset = 0; + UINT16_TO_STREAM (pp, HCI_EXIT_PERIODIC_INQUIRY_MODE); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_EXIT_PER_INQ); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + + +BOOLEAN btsnd_hcic_create_conn(BD_ADDR dest, UINT16 packet_types, + UINT8 page_scan_rep_mode, UINT8 page_scan_mode, + UINT16 clock_offset, UINT8 allow_switch) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_CREATE_CONN)) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + +#ifndef BT_10A + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_CREATE_CONN; +#else + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_CREATE_CONN - 1; +#endif + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_CREATE_CONNECTION); +#ifndef BT_10A + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_CREATE_CONN); +#else + UINT8_TO_STREAM (pp, (HCIC_PARAM_SIZE_CREATE_CONN - 1)); +#endif + BDADDR_TO_STREAM (pp, dest); + UINT16_TO_STREAM (pp, packet_types); + UINT8_TO_STREAM (pp, page_scan_rep_mode); + UINT8_TO_STREAM (pp, page_scan_mode); + UINT16_TO_STREAM (pp, clock_offset); +#if !defined (BT_10A) + UINT8_TO_STREAM (pp, allow_switch); +#endif +#if (SMP_INCLUDED == TRUE && CLASSIC_BT_INCLUDED == TRUE) + btm_acl_paging (p, dest); +#endif ///SMP_INCLUDED == TRUE && CLASSIC_BT_INCLUDED == TRUE + return (TRUE); +} + +BOOLEAN btsnd_hcic_disconnect (UINT16 handle, UINT8 reason) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_DISCONNECT)) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_DISCONNECT; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_DISCONNECT); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_DISCONNECT); + UINT16_TO_STREAM (pp, handle); + UINT8_TO_STREAM (pp, reason); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + HCI_TRACE_WARNING("hci cmd send: disconnect: hdl 0x%x, rsn:0x%x", handle, reason); + return (TRUE); +} + +#if BTM_SCO_INCLUDED == TRUE +BOOLEAN btsnd_hcic_add_SCO_conn (UINT16 handle, UINT16 packet_types) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_ADD_SCO_CONN)) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_ADD_SCO_CONN; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_ADD_SCO_CONNECTION); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_ADD_SCO_CONN); + + UINT16_TO_STREAM (pp, handle); + UINT16_TO_STREAM (pp, packet_types); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} +#endif /* BTM_SCO_INCLUDED */ + +BOOLEAN btsnd_hcic_create_conn_cancel(BD_ADDR dest) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_CREATE_CONN_CANCEL)) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_CREATE_CONN_CANCEL; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_CREATE_CONNECTION_CANCEL); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_CREATE_CONN_CANCEL); + + BDADDR_TO_STREAM (pp, dest); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_accept_conn (BD_ADDR dest, UINT8 role) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_ACCEPT_CONN)) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_ACCEPT_CONN; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_ACCEPT_CONNECTION_REQUEST); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_ACCEPT_CONN); + BDADDR_TO_STREAM (pp, dest); + UINT8_TO_STREAM (pp, role); + + //counter_add("hci.conn.accept", 1); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_reject_conn (BD_ADDR dest, UINT8 reason) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_REJECT_CONN)) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_REJECT_CONN; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_REJECT_CONNECTION_REQUEST); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_REJECT_CONN); + + BDADDR_TO_STREAM (pp, dest); + UINT8_TO_STREAM (pp, reason); + + //counter_add("hci.conn.reject", 1); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_link_key_req_reply (BD_ADDR bd_addr, LINK_KEY link_key) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_LINK_KEY_REQ_REPLY)) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_LINK_KEY_REQ_REPLY; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_LINK_KEY_REQUEST_REPLY); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_LINK_KEY_REQ_REPLY); + + BDADDR_TO_STREAM (pp, bd_addr); + ARRAY16_TO_STREAM (pp, link_key); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_link_key_neg_reply (BD_ADDR bd_addr) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_LINK_KEY_NEG_REPLY)) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_LINK_KEY_NEG_REPLY; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_LINK_KEY_REQUEST_NEG_REPLY); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_LINK_KEY_NEG_REPLY); + + BDADDR_TO_STREAM (pp, bd_addr); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_pin_code_req_reply (BD_ADDR bd_addr, UINT8 pin_code_len, + PIN_CODE pin_code) +{ + BT_HDR *p; + UINT8 *pp; + int i; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_PIN_CODE_REQ_REPLY)) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_PIN_CODE_REQ_REPLY; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_PIN_CODE_REQUEST_REPLY); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_PIN_CODE_REQ_REPLY); + + BDADDR_TO_STREAM (pp, bd_addr); + UINT8_TO_STREAM (pp, pin_code_len); + + for (i = 0; i < pin_code_len; i++) { + *pp++ = *pin_code++; + } + + for (; i < PIN_CODE_LEN; i++) { + *pp++ = 0; + } + + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_pin_code_neg_reply (BD_ADDR bd_addr) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_PIN_CODE_NEG_REPLY)) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_PIN_CODE_NEG_REPLY; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_PIN_CODE_REQUEST_NEG_REPLY); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_PIN_CODE_NEG_REPLY); + + BDADDR_TO_STREAM (pp, bd_addr); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_change_conn_type (UINT16 handle, UINT16 packet_types) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_CHANGE_CONN_TYPE)) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_CHANGE_CONN_TYPE; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_CHANGE_CONN_PACKET_TYPE); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_CHANGE_CONN_TYPE); + + UINT16_TO_STREAM (pp, handle); + UINT16_TO_STREAM (pp, packet_types); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_auth_request (UINT16 handle) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_CMD_HANDLE)) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_CMD_HANDLE; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_AUTHENTICATION_REQUESTED); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_CMD_HANDLE); + + UINT16_TO_STREAM (pp, handle); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_set_conn_encrypt (UINT16 handle, BOOLEAN enable) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_SET_CONN_ENCRYPT)) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_SET_CONN_ENCRYPT; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_SET_CONN_ENCRYPTION); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_SET_CONN_ENCRYPT); + + UINT16_TO_STREAM (pp, handle); + UINT8_TO_STREAM (pp, enable); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_rmt_name_req (BD_ADDR bd_addr, UINT8 page_scan_rep_mode, + UINT8 page_scan_mode, UINT16 clock_offset) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_RMT_NAME_REQ)) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_RMT_NAME_REQ; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_RMT_NAME_REQUEST); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_RMT_NAME_REQ); + + BDADDR_TO_STREAM (pp, bd_addr); + UINT8_TO_STREAM (pp, page_scan_rep_mode); + UINT8_TO_STREAM (pp, page_scan_mode); + UINT16_TO_STREAM (pp, clock_offset); +#if (SMP_INCLUDED == TRUE && CLASSIC_BT_INCLUDED == TRUE) + btm_acl_paging (p, bd_addr); +#endif ///SMP_INCLUDED == TRUE && CLASSIC_BT_INCLUDED == TRUE + return (TRUE); +} + +BOOLEAN btsnd_hcic_rmt_name_req_cancel (BD_ADDR bd_addr) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_RMT_NAME_REQ_CANCEL)) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_RMT_NAME_REQ_CANCEL; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_RMT_NAME_REQUEST_CANCEL); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_RMT_NAME_REQ_CANCEL); + + BDADDR_TO_STREAM (pp, bd_addr); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_rmt_features_req (UINT16 handle) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_CMD_HANDLE)) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_CMD_HANDLE; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_READ_RMT_FEATURES); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_CMD_HANDLE); + + UINT16_TO_STREAM (pp, handle); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_rmt_ext_features (UINT16 handle, UINT8 page_num) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_RMT_EXT_FEATURES)) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_RMT_EXT_FEATURES; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_READ_RMT_EXT_FEATURES); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_RMT_EXT_FEATURES); + + UINT16_TO_STREAM (pp, handle); + UINT8_TO_STREAM (pp, page_num); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_rmt_ver_req (UINT16 handle) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_CMD_HANDLE)) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_CMD_HANDLE; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_READ_RMT_VERSION_INFO); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_CMD_HANDLE); + + UINT16_TO_STREAM (pp, handle); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_read_rmt_clk_offset (UINT16 handle) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_CMD_HANDLE)) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_CMD_HANDLE; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_READ_RMT_CLOCK_OFFSET); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_CMD_HANDLE); + + UINT16_TO_STREAM (pp, handle); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_read_lmp_handle (UINT16 handle) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_CMD_HANDLE)) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_CMD_HANDLE; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_READ_LMP_HANDLE); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_CMD_HANDLE); + + UINT16_TO_STREAM (pp, handle); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_setup_esco_conn (UINT16 handle, UINT32 tx_bw, + UINT32 rx_bw, UINT16 max_latency, UINT16 voice, + UINT8 retrans_effort, UINT16 packet_types) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_SETUP_ESCO)) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_SETUP_ESCO; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_SETUP_ESCO_CONNECTION); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_SETUP_ESCO); + + UINT16_TO_STREAM (pp, handle); + UINT32_TO_STREAM (pp, tx_bw); + UINT32_TO_STREAM (pp, rx_bw); + UINT16_TO_STREAM (pp, max_latency); + UINT16_TO_STREAM (pp, voice); + UINT8_TO_STREAM (pp, retrans_effort); + UINT16_TO_STREAM (pp, packet_types); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_accept_esco_conn (BD_ADDR bd_addr, UINT32 tx_bw, + UINT32 rx_bw, UINT16 max_latency, + UINT16 content_fmt, UINT8 retrans_effort, + UINT16 packet_types) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_ACCEPT_ESCO)) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_ACCEPT_ESCO; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_ACCEPT_ESCO_CONNECTION); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_ACCEPT_ESCO); + + BDADDR_TO_STREAM (pp, bd_addr); + UINT32_TO_STREAM (pp, tx_bw); + UINT32_TO_STREAM (pp, rx_bw); + UINT16_TO_STREAM (pp, max_latency); + UINT16_TO_STREAM (pp, content_fmt); + UINT8_TO_STREAM (pp, retrans_effort); + UINT16_TO_STREAM (pp, packet_types); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_reject_esco_conn (BD_ADDR bd_addr, UINT8 reason) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_REJECT_ESCO)) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_REJECT_ESCO; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_REJECT_ESCO_CONNECTION); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_REJECT_ESCO); + + BDADDR_TO_STREAM (pp, bd_addr); + UINT8_TO_STREAM (pp, reason); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_hold_mode (UINT16 handle, UINT16 max_hold_period, + UINT16 min_hold_period) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_HOLD_MODE)) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_HOLD_MODE; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_HOLD_MODE); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_HOLD_MODE); + + UINT16_TO_STREAM (pp, handle); + UINT16_TO_STREAM (pp, max_hold_period); + UINT16_TO_STREAM (pp, min_hold_period); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_sniff_mode (UINT16 handle, UINT16 max_sniff_period, + UINT16 min_sniff_period, UINT16 sniff_attempt, + UINT16 sniff_timeout) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_SNIFF_MODE)) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_SNIFF_MODE; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_SNIFF_MODE); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_SNIFF_MODE); + + UINT16_TO_STREAM (pp, handle); + UINT16_TO_STREAM (pp, max_sniff_period); + UINT16_TO_STREAM (pp, min_sniff_period); + UINT16_TO_STREAM (pp, sniff_attempt); + UINT16_TO_STREAM (pp, sniff_timeout); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + HCI_TRACE_WARNING("hci cmd send: sniff: hdl 0x%x, intv(%d %d)", + handle, min_sniff_period, max_sniff_period); + return (TRUE); +} + +BOOLEAN btsnd_hcic_exit_sniff_mode (UINT16 handle) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_CMD_HANDLE)) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_CMD_HANDLE; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_EXIT_SNIFF_MODE); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_CMD_HANDLE); + + UINT16_TO_STREAM (pp, handle); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + HCI_TRACE_WARNING("hci cmd send: unsniff: hdl 0x%x", handle); + return TRUE; +} + +BOOLEAN btsnd_hcic_park_mode (UINT16 handle, UINT16 beacon_max_interval, + UINT16 beacon_min_interval) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_PARK_MODE)) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_PARK_MODE; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_PARK_MODE); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_PARK_MODE); + + UINT16_TO_STREAM (pp, handle); + UINT16_TO_STREAM (pp, beacon_max_interval); + UINT16_TO_STREAM (pp, beacon_min_interval); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_exit_park_mode (UINT16 handle) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_CMD_HANDLE)) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_CMD_HANDLE; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_EXIT_PARK_MODE); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_CMD_HANDLE); + + UINT16_TO_STREAM (pp, handle); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return TRUE; +} + +BOOLEAN btsnd_hcic_qos_setup (UINT16 handle, UINT8 flags, UINT8 service_type, + UINT32 token_rate, UINT32 peak, UINT32 latency, + UINT32 delay_var) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_QOS_SETUP)) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_QOS_SETUP; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_QOS_SETUP); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_QOS_SETUP); + + UINT16_TO_STREAM (pp, handle); + UINT8_TO_STREAM (pp, flags); + UINT8_TO_STREAM (pp, service_type); + UINT32_TO_STREAM (pp, token_rate); + UINT32_TO_STREAM (pp, peak); + UINT32_TO_STREAM (pp, latency); + UINT32_TO_STREAM (pp, delay_var); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_switch_role (BD_ADDR bd_addr, UINT8 role) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_SWITCH_ROLE)) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_SWITCH_ROLE; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_SWITCH_ROLE); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_SWITCH_ROLE); + + BDADDR_TO_STREAM (pp, bd_addr); + UINT8_TO_STREAM (pp, role); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_write_policy_set (UINT16 handle, UINT16 settings) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_WRITE_POLICY_SET)) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_WRITE_POLICY_SET; + p->offset = 0; + UINT16_TO_STREAM (pp, HCI_WRITE_POLICY_SETTINGS); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_WRITE_POLICY_SET); + + UINT16_TO_STREAM (pp, handle); + UINT16_TO_STREAM (pp, settings); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_write_def_policy_set (UINT16 settings) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_WRITE_DEF_POLICY_SET)) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_WRITE_DEF_POLICY_SET; + p->offset = 0; + UINT16_TO_STREAM (pp, HCI_WRITE_DEF_POLICY_SETTINGS); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_WRITE_DEF_POLICY_SET); + + UINT16_TO_STREAM (pp, settings); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_set_event_filter (UINT8 filt_type, UINT8 filt_cond_type, + UINT8 *filt_cond, UINT8 filt_cond_len) +{ + BT_HDR *p; + UINT8 *pp; + + /* Use buffer large enough to hold all sizes in this command */ + if ((p = HCI_GET_CMD_BUF(2 + filt_cond_len)) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_SET_EVENT_FILTER); + + if (filt_type) { + p->len = (UINT16)(HCIC_PREAMBLE_SIZE + 2 + filt_cond_len); + UINT8_TO_STREAM (pp, (UINT8)(2 + filt_cond_len)); + + UINT8_TO_STREAM (pp, filt_type); + UINT8_TO_STREAM (pp, filt_cond_type); + + if (filt_cond_type == HCI_FILTER_COND_DEVICE_CLASS) { + DEVCLASS_TO_STREAM (pp, filt_cond); + filt_cond += DEV_CLASS_LEN; + DEVCLASS_TO_STREAM (pp, filt_cond); + filt_cond += DEV_CLASS_LEN; + + filt_cond_len -= (2 * DEV_CLASS_LEN); + } else if (filt_cond_type == HCI_FILTER_COND_BD_ADDR) { + BDADDR_TO_STREAM (pp, filt_cond); + filt_cond += BD_ADDR_LEN; + + filt_cond_len -= BD_ADDR_LEN; + } + + if (filt_cond_len) { + ARRAY_TO_STREAM (pp, filt_cond, filt_cond_len); + } + } else { + p->len = (UINT16)(HCIC_PREAMBLE_SIZE + 1); + UINT8_TO_STREAM (pp, 1); + + UINT8_TO_STREAM (pp, filt_type); + } + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_write_pin_type (UINT8 type) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_WRITE_PARAM1)) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_WRITE_PARAM1; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_WRITE_PIN_TYPE); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_WRITE_PARAM1); + + UINT8_TO_STREAM (pp, type); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_delete_stored_key (BD_ADDR bd_addr, BOOLEAN delete_all_flag) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_DELETE_STORED_KEY)) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_DELETE_STORED_KEY; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_DELETE_STORED_LINK_KEY); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_DELETE_STORED_KEY); + + BDADDR_TO_STREAM (pp, bd_addr); + UINT8_TO_STREAM (pp, delete_all_flag); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_change_name (const UINT8 *name) +{ + BT_HDR *p; + UINT8 *pp; + UINT16 len = strlen ((char *)name) + 1; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_CHANGE_NAME)) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + memset(pp, 0, HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_CHANGE_NAME); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_CHANGE_NAME; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_CHANGE_LOCAL_NAME); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_CHANGE_NAME); + + if (len > HCIC_PARAM_SIZE_CHANGE_NAME) { + len = HCIC_PARAM_SIZE_CHANGE_NAME; + } + + ARRAY_TO_STREAM (pp, name, len); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_read_name (void) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_READ_CMD)) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_READ_CMD; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_READ_LOCAL_NAME); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_READ_CMD); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_write_page_tout (UINT16 timeout) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_WRITE_PARAM2)) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_WRITE_PARAM2; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_WRITE_PAGE_TOUT); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_WRITE_PARAM2); + + UINT16_TO_STREAM (pp, timeout); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_write_scan_enable (UINT8 flag) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_WRITE_PARAM1)) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_WRITE_PARAM1; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_WRITE_SCAN_ENABLE); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_WRITE_PARAM1); + + UINT8_TO_STREAM (pp, flag); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_write_pagescan_cfg(UINT16 interval, UINT16 window) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_WRITE_PAGESCAN_CFG)) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_WRITE_PAGESCAN_CFG; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_WRITE_PAGESCAN_CFG); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_WRITE_PAGESCAN_CFG); + + UINT16_TO_STREAM (pp, interval); + UINT16_TO_STREAM (pp, window); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_write_inqscan_cfg(UINT16 interval, UINT16 window) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_WRITE_INQSCAN_CFG)) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_WRITE_INQSCAN_CFG; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_WRITE_INQUIRYSCAN_CFG); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_WRITE_INQSCAN_CFG); + + UINT16_TO_STREAM (pp, interval); + UINT16_TO_STREAM (pp, window); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_write_auth_enable (UINT8 flag) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_WRITE_PARAM1)) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_WRITE_PARAM1; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_WRITE_AUTHENTICATION_ENABLE); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_WRITE_PARAM1); + + UINT8_TO_STREAM (pp, flag); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_write_dev_class(DEV_CLASS dev_class) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_WRITE_PARAM3)) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_WRITE_PARAM3; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_WRITE_CLASS_OF_DEVICE); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_WRITE_PARAM3); + + DEVCLASS_TO_STREAM (pp, dev_class); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_write_voice_settings(UINT16 flags) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_WRITE_PARAM2)) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_WRITE_PARAM2; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_WRITE_VOICE_SETTINGS); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_WRITE_PARAM2); + + UINT16_TO_STREAM (pp, flags); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_write_auto_flush_tout (UINT16 handle, UINT16 tout) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_WRITE_AUTO_FLUSH_TOUT)) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_WRITE_AUTO_FLUSH_TOUT; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_WRITE_AUTO_FLUSH_TOUT); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_WRITE_AUTO_FLUSH_TOUT); + + UINT16_TO_STREAM (pp, handle); + UINT16_TO_STREAM (pp, tout); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_read_tx_power (UINT16 handle, UINT8 type) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_READ_TX_POWER)) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_READ_TX_POWER; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_READ_TRANSMIT_POWER_LEVEL); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_READ_TX_POWER); + + UINT16_TO_STREAM (pp, handle); + UINT8_TO_STREAM (pp, type); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_host_num_xmitted_pkts (UINT8 num_handles, UINT16 *handle, + UINT16 *num_pkts) +{ + BT_HDR *p; + UINT8 *pp; + int j; + + if ((p = HCI_GET_CMD_BUF(1 + (num_handles * 4))) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + 1 + (num_handles * 4); + p->offset = 0; + + hci_cmd_metadata_t *metadata = HCI_GET_CMD_METAMSG(p); + metadata->flags_src |= HCI_CMD_MSG_F_SRC_NOACK; + + UINT16_TO_STREAM (pp, HCI_HOST_NUM_PACKETS_DONE); + UINT8_TO_STREAM (pp, p->len - HCIC_PREAMBLE_SIZE); + + UINT8_TO_STREAM (pp, num_handles); + + for (j = 0; j < num_handles; j++) { + UINT16_TO_STREAM (pp, handle[j]); + UINT16_TO_STREAM (pp, num_pkts[j]); + } + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_write_link_super_tout (UINT8 local_controller_id, UINT16 handle, UINT16 timeout) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_WRITE_LINK_SUPER_TOUT)) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_WRITE_LINK_SUPER_TOUT; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_WRITE_LINK_SUPER_TOUT); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_WRITE_LINK_SUPER_TOUT); + + UINT16_TO_STREAM (pp, handle); + UINT16_TO_STREAM (pp, timeout); + + btu_hcif_send_cmd (local_controller_id, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_write_cur_iac_lap (UINT8 num_cur_iac, LAP *const iac_lap) +{ + BT_HDR *p; + UINT8 *pp; + int i; + + if ((p = HCI_GET_CMD_BUF(1 + (LAP_LEN * num_cur_iac))) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + 1 + (LAP_LEN * num_cur_iac); + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_WRITE_CURRENT_IAC_LAP); + UINT8_TO_STREAM (pp, p->len - HCIC_PREAMBLE_SIZE); + + UINT8_TO_STREAM (pp, num_cur_iac); + + for (i = 0; i < num_cur_iac; i++) { + LAP_TO_STREAM (pp, iac_lap[i]); + } + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +/****************************************** +** Lisbon Features +*******************************************/ +#if BTM_SSR_INCLUDED == TRUE + +BOOLEAN btsnd_hcic_sniff_sub_rate(UINT16 handle, UINT16 max_lat, + UINT16 min_remote_lat, UINT16 min_local_lat) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_SNIFF_SUB_RATE)) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_SNIFF_SUB_RATE; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_SNIFF_SUB_RATE); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_SNIFF_SUB_RATE); + + UINT16_TO_STREAM (pp, handle); + UINT16_TO_STREAM (pp, max_lat); + UINT16_TO_STREAM (pp, min_remote_lat); + UINT16_TO_STREAM (pp, min_local_lat); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} +#endif /* BTM_SSR_INCLUDED */ + +/**** Extended Inquiry Response Commands ****/ +void btsnd_hcic_write_ext_inquiry_response (BT_HDR *buffer, UINT8 fec_req) +{ + BT_HDR *p; + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_EXT_INQ_RESP)) == NULL) { + return; + } + + UINT8 *pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_EXT_INQ_RESP; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_WRITE_EXT_INQ_RESPONSE); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_EXT_INQ_RESP); + UINT8_TO_STREAM (pp, fec_req); + + memcpy(pp, buffer->data + 4, p->len - 4); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); +} + +BOOLEAN btsnd_hcic_io_cap_req_reply (BD_ADDR bd_addr, UINT8 capability, + UINT8 oob_present, UINT8 auth_req) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_IO_CAP_RESP)) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_IO_CAP_RESP; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_IO_CAPABILITY_REQUEST_REPLY); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_IO_CAP_RESP); + + BDADDR_TO_STREAM (pp, bd_addr); + UINT8_TO_STREAM (pp, capability); + UINT8_TO_STREAM (pp, oob_present); + UINT8_TO_STREAM (pp, auth_req); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_io_cap_req_neg_reply (BD_ADDR bd_addr, UINT8 err_code) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_IO_CAP_NEG_REPLY)) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_IO_CAP_NEG_REPLY; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_IO_CAP_REQ_NEG_REPLY); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_IO_CAP_NEG_REPLY); + + BDADDR_TO_STREAM (pp, bd_addr); + UINT8_TO_STREAM (pp, err_code); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_read_local_oob_data (void) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_R_LOCAL_OOB)) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_R_LOCAL_OOB; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_READ_LOCAL_OOB_DATA); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_R_LOCAL_OOB); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_user_conf_reply (BD_ADDR bd_addr, BOOLEAN is_yes) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_UCONF_REPLY)) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_UCONF_REPLY; + p->offset = 0; + + if (!is_yes) { + /* Negative reply */ + UINT16_TO_STREAM (pp, HCI_USER_CONF_VALUE_NEG_REPLY); + } else { + /* Confirmation */ + UINT16_TO_STREAM (pp, HCI_USER_CONF_REQUEST_REPLY); + } + + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_UCONF_REPLY); + + BDADDR_TO_STREAM (pp, bd_addr); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_user_passkey_reply (BD_ADDR bd_addr, UINT32 value) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_U_PKEY_REPLY)) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_U_PKEY_REPLY; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_USER_PASSKEY_REQ_REPLY); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_U_PKEY_REPLY); + + BDADDR_TO_STREAM (pp, bd_addr); + UINT32_TO_STREAM (pp, value); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_user_passkey_neg_reply (BD_ADDR bd_addr) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_U_PKEY_NEG_REPLY)) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_U_PKEY_NEG_REPLY; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_USER_PASSKEY_REQ_NEG_REPLY); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_U_PKEY_NEG_REPLY); + + BDADDR_TO_STREAM (pp, bd_addr); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_rem_oob_reply (BD_ADDR bd_addr, UINT8 *p_c, UINT8 *p_r) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_REM_OOB_REPLY)) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_REM_OOB_REPLY; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_REM_OOB_DATA_REQ_REPLY); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_REM_OOB_REPLY); + + BDADDR_TO_STREAM (pp, bd_addr); + ARRAY16_TO_STREAM (pp, p_c); + ARRAY16_TO_STREAM (pp, p_r); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_rem_oob_neg_reply (BD_ADDR bd_addr) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_REM_OOB_NEG_REPLY)) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_REM_OOB_NEG_REPLY; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_REM_OOB_DATA_REQ_NEG_REPLY); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_REM_OOB_NEG_REPLY); + + BDADDR_TO_STREAM (pp, bd_addr); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + + +BOOLEAN btsnd_hcic_read_inq_tx_power (void) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_R_TX_POWER)) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_R_TX_POWER; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_READ_INQ_TX_POWER_LEVEL); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_R_TX_POWER); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_send_keypress_notif (BD_ADDR bd_addr, UINT8 notif) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_SEND_KEYPRESS_NOTIF)) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_SEND_KEYPRESS_NOTIF; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_SEND_KEYPRESS_NOTIF); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_SEND_KEYPRESS_NOTIF); + + BDADDR_TO_STREAM (pp, bd_addr); + UINT8_TO_STREAM (pp, notif); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +/**** end of Simple Pairing Commands ****/ + +#if L2CAP_NON_FLUSHABLE_PB_INCLUDED == TRUE +BOOLEAN btsnd_hcic_enhanced_flush (UINT16 handle, UINT8 packet_type) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_ENHANCED_FLUSH)) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_ENHANCED_FLUSH; + p->offset = 0; + UINT16_TO_STREAM (pp, HCI_ENHANCED_FLUSH); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_ENHANCED_FLUSH); + + UINT16_TO_STREAM (pp, handle); + UINT8_TO_STREAM (pp, packet_type); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} +#endif + +/************************* +** End of Lisbon Commands +**************************/ + +BOOLEAN btsnd_hcic_get_link_quality (UINT16 handle) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_CMD_HANDLE)) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_CMD_HANDLE; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_GET_LINK_QUALITY); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_CMD_HANDLE); + + UINT16_TO_STREAM (pp, handle); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_read_rssi (UINT16 handle) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_CMD_HANDLE)) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_CMD_HANDLE; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_READ_RSSI); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_CMD_HANDLE); + + UINT16_TO_STREAM (pp, handle); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_enable_test_mode (void) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_READ_CMD)) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_READ_CMD; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_ENABLE_DEV_UNDER_TEST_MODE); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_READ_CMD); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_write_inqscan_type (UINT8 type) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_WRITE_PARAM1)) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_WRITE_PARAM1; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_WRITE_INQSCAN_TYPE); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_WRITE_PARAM1); + + UINT8_TO_STREAM (pp, type); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_write_inquiry_mode (UINT8 mode) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_WRITE_PARAM1)) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_WRITE_PARAM1; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_WRITE_INQUIRY_MODE); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_WRITE_PARAM1); + + UINT8_TO_STREAM (pp, mode); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_write_pagescan_type (UINT8 type) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_WRITE_PARAM1)) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_WRITE_PARAM1; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_WRITE_PAGESCAN_TYPE); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_WRITE_PARAM1); + + UINT8_TO_STREAM (pp, type); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +/* Must have room to store BT_HDR + max VSC length + callback pointer */ +#if (HCI_CMD_BUF_SIZE < 268) +#error "HCI_CMD_POOL_BUF_SIZE must be larger than 268" +#endif + +void btsnd_hcic_vendor_spec_cmd (BT_HDR *buffer, UINT16 opcode, UINT8 len, + UINT8 *p_data, void *p_cmd_cplt_cback) +{ + BT_HDR *p = buffer; + UINT8 *pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + len; + p->offset = 0; + + hci_cmd_metadata_t * metadata = HCI_GET_CMD_METAMSG(p); + metadata->context = p_cmd_cplt_cback; + + UINT16_TO_STREAM (pp, HCI_GRP_VENDOR_SPECIFIC | opcode); + UINT8_TO_STREAM (pp, len); + ARRAY_TO_STREAM (pp, p_data, len); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); +} + +#if (CLASSIC_BT_INCLUDED == TRUE) +BOOLEAN btsnd_hcic_set_afh_channels (AFH_CHANNELS channels) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_SET_AFH_CHANNELS)) == NULL) { + return (FALSE); + } + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_SET_AFH_CHANNELS; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_SET_AFH_CHANNELS); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_SET_AFH_CHANNELS); + + ARRAY_TO_STREAM (pp, channels, HCIC_PARAM_SIZE_SET_AFH_CHANNELS); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} +#endif /// CLASSIC_BT_INCLUDED == TRUE diff --git a/lib/bt/host/bluedroid/stack/hid/hidd_api.c b/lib/bt/host/bluedroid/stack/hid/hidd_api.c new file mode 100644 index 00000000..347ef7eb --- /dev/null +++ b/lib/bt/host/bluedroid/stack/hid/hidd_api.c @@ -0,0 +1,586 @@ +/****************************************************************************** + * + * Copyright (C) 2016 The Android Open Source Project + * Copyright (C) 2002-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ +/****************************************************************************** + * + * This file contains the HID Device API entry points + * + ******************************************************************************/ +//#include +//#include +//#include +#include "stack/hidd_api.h" +#include "esp_hidd_api.h" +#include "hid_int.h" +#include "osi/allocator.h" +#include "stack/btm_api.h" +#include "stack/btu.h" +#include "stack/hiddefs.h" +#include +#include +#include + +#if (HID_DEV_INCLUDED == TRUE) + +#if HID_DYNAMIC_MEMORY == FALSE +tHID_DEV_CTB hd_cb; +#else +tHID_DEV_CTB *hidd_cb_ptr = NULL; +#endif + +/******************************************************************************* + * + * Function HID_DevInit + * + * Description Initializes control block + * + * Returns tHID_STATUS + * + ******************************************************************************/ +tHID_STATUS HID_DevInit(void) +{ +#if (HID_DYNAMIC_MEMORY) + if (!hidd_cb_ptr) { + hidd_cb_ptr = (tHID_DEV_CTB *)osi_malloc(sizeof(tHID_DEV_CTB)); + if (!hidd_cb_ptr) { + return HID_ERR_NO_RESOURCES; + } + } +#endif /* #if (HID_DYNAMIC_MEMORY) */ + memset(&hd_cb, 0, sizeof(tHID_DEV_CTB)); +#if defined(HIDD_INITIAL_TRACE_LEVEL) + hd_cb.trace_level = HIDD_INITIAL_TRACE_LEVEL; +#else + hd_cb.trace_level = BT_TRACE_LEVEL_NONE; +#endif + return HID_SUCCESS; +} + +/******************************************************************************* + * + * Function HID_DevDeinit + * + * Description Deinitializes control block + * + * Returns void + * + ******************************************************************************/ +void HID_DevDeinit(void) +{ +#if (HID_DYNAMIC_MEMORY) + if (hidd_cb_ptr) { + osi_free(hidd_cb_ptr); + hidd_cb_ptr = NULL; + } +#endif /* #if (HID_DYNAMIC_MEMORY) */ +} + +/******************************************************************************* + * + * Function HID_DevSetTraceLevel + * + * Description This function sets the trace level for HID Dev. If called + * with + * a value of 0xFF, it simply reads the current trace level. + * + * Returns the new (current) trace level + * + ******************************************************************************/ +uint8_t HID_DevSetTraceLevel(uint8_t new_level) +{ + if (new_level != 0xFF) { + hd_cb.trace_level = new_level; + } + + return (hd_cb.trace_level); +} + +/******************************************************************************* + * + * Function HID_DevRegister + * + * Description Registers HID device with lower layers + * + * Returns tHID_STATUS + * + ******************************************************************************/ +tHID_STATUS HID_DevRegister(tHID_DEV_HOST_CALLBACK *host_cback) +{ + tHID_STATUS st; + HIDD_TRACE_API("%s", __func__); + + if (hd_cb.reg_flag) { + return HID_ERR_ALREADY_REGISTERED; + } + + if (host_cback == NULL) { + return HID_ERR_INVALID_PARAM; + } + /* Register with L2CAP */ + if ((st = hidd_conn_reg()) != HID_SUCCESS) { + return st; + } + + hd_cb.callback = host_cback; + hd_cb.reg_flag = TRUE; + + if (hd_cb.pending_data) { + osi_free(hd_cb.pending_data); + hd_cb.pending_data = NULL; + } + return (HID_SUCCESS); +} + +/******************************************************************************* + * + * Function HID_DevDeregister + * + * Description Deregisters HID device with lower layers + * + * Returns tHID_STATUS + * + ******************************************************************************/ +tHID_STATUS HID_DevDeregister(void) +{ + HIDD_TRACE_API("%s", __func__); + + if (!hd_cb.reg_flag) + return (HID_ERR_NOT_REGISTERED); + hidd_conn_dereg(); + hd_cb.reg_flag = FALSE; + + return (HID_SUCCESS); +} + +tHID_STATUS HID_DevSetSecurityLevel(uint8_t sec_lvl) +{ + HIDD_TRACE_API("%s", __func__); + if (!BTM_SetSecurityLevel(FALSE, "", BTM_SEC_SERVICE_HIDD_SEC_CTRL, sec_lvl, HID_PSM_CONTROL, BTM_SEC_PROTO_HID, + HIDD_SEC_CHN)) { + HIDD_TRACE_ERROR("Security Registration 1 failed"); + return (HID_ERR_NO_RESOURCES); + } + if (!BTM_SetSecurityLevel(TRUE, "", BTM_SEC_SERVICE_HIDD_SEC_CTRL, sec_lvl, HID_PSM_CONTROL, BTM_SEC_PROTO_HID, + HIDD_SEC_CHN)) { + HIDD_TRACE_ERROR("Security Registration 2 failed"); + return (HID_ERR_NO_RESOURCES); + } + if (!BTM_SetSecurityLevel(FALSE, "", BTM_SEC_SERVICE_HIDD_NOSEC_CTRL, BTM_SEC_NONE, HID_PSM_CONTROL, + BTM_SEC_PROTO_HID, HIDD_NOSEC_CHN)) { + HIDD_TRACE_ERROR("Security Registration 3 failed"); + return (HID_ERR_NO_RESOURCES); + } + if (!BTM_SetSecurityLevel(TRUE, "", BTM_SEC_SERVICE_HIDD_NOSEC_CTRL, BTM_SEC_NONE, HID_PSM_CONTROL, + BTM_SEC_PROTO_HID, HIDD_NOSEC_CHN)) { + HIDD_TRACE_ERROR("Security Registration 4 failed"); + return (HID_ERR_NO_RESOURCES); + } + if (!BTM_SetSecurityLevel(TRUE, "", BTM_SEC_SERVICE_HIDD_INTR, BTM_SEC_NONE, HID_PSM_INTERRUPT, BTM_SEC_PROTO_HID, + 0)) { + HIDD_TRACE_ERROR("Security Registration 5 failed"); + return (HID_ERR_NO_RESOURCES); + } + if (!BTM_SetSecurityLevel(FALSE, "", BTM_SEC_SERVICE_HIDD_INTR, BTM_SEC_NONE, HID_PSM_INTERRUPT, BTM_SEC_PROTO_HID, + 0)) { + HIDD_TRACE_ERROR("Security Registration 6 failed"); + return (HID_ERR_NO_RESOURCES); + } + return (HID_SUCCESS); +} + +/******************************************************************************* + * + * Function HID_DevAddRecord + * + * Description Creates SDP record for HID device + * + * Returns tHID_STATUS + * + ******************************************************************************/ +tHID_STATUS HID_DevAddRecord(uint32_t handle, char *p_name, char *p_description, char *p_provider, uint16_t subclass, + uint16_t desc_len, uint8_t *p_desc_data) +{ + bool result = TRUE; + + HIDD_TRACE_API("%s", __func__); + + // Service Class ID List + if (result) { + uint16_t uuid = UUID_SERVCLASS_HUMAN_INTERFACE; + result &= SDP_AddServiceClassIdList(handle, 1, &uuid); + } + // Protocol Descriptor List + if (result) { + tSDP_PROTOCOL_ELEM proto_list[2]; + proto_list[0].protocol_uuid = UUID_PROTOCOL_L2CAP; + proto_list[0].num_params = 1; + proto_list[0].params[0] = BT_PSM_HIDC; + proto_list[1].protocol_uuid = UUID_PROTOCOL_HIDP; + proto_list[1].num_params = 0; + result &= SDP_AddProtocolList(handle, 2, proto_list); + } + // Language Base Attribute ID List + if (result) { + result &= + SDP_AddLanguageBaseAttrIDList(handle, LANG_ID_CODE_ENGLISH, LANG_ID_CHAR_ENCODE_UTF8, LANGUAGE_BASE_ID); + } + // Additional Protocol Descriptor List + if (result) { + tSDP_PROTO_LIST_ELEM add_proto_list; + add_proto_list.num_elems = 2; + add_proto_list.list_elem[0].protocol_uuid = UUID_PROTOCOL_L2CAP; + add_proto_list.list_elem[0].num_params = 1; + add_proto_list.list_elem[0].params[0] = BT_PSM_HIDI; + add_proto_list.list_elem[1].protocol_uuid = UUID_PROTOCOL_HIDP; + add_proto_list.list_elem[1].num_params = 0; + result &= SDP_AddAdditionProtoLists(handle, 1, &add_proto_list); + } + // Service Name (O) + // Service Description (O) + // Provider Name (O) + if (result) { + const char *srv_name = p_name; + const char *srv_desc = p_description; + const char *provider_name = p_provider; + result &= SDP_AddAttribute(handle, ATTR_ID_SERVICE_NAME, TEXT_STR_DESC_TYPE, strlen(srv_name) + 1, + (uint8_t *)srv_name); + result &= SDP_AddAttribute(handle, ATTR_ID_SERVICE_DESCRIPTION, TEXT_STR_DESC_TYPE, strlen(srv_desc) + 1, + (uint8_t *)srv_desc); + result &= SDP_AddAttribute(handle, ATTR_ID_PROVIDER_NAME, TEXT_STR_DESC_TYPE, strlen(provider_name) + 1, + (uint8_t *)provider_name); + } + // Bluetooth Profile Descriptor List + if (result) { + const uint16_t profile_uuid = UUID_SERVCLASS_HUMAN_INTERFACE; + const uint16_t version = 0x0100; + result &= SDP_AddProfileDescriptorList(handle, profile_uuid, version); + } + // HID Parser Version + if (result) { + uint8_t *p; + const uint16_t rel_num = 0x0100; + const uint16_t parser_version = 0x0111; + const uint16_t prof_ver = 0x0100; + const uint8_t dev_subclass = subclass; + const uint8_t country_code = 0x21; + const uint8_t bool_false = 0x00; + const uint8_t bool_true = 0x01; + uint16_t temp; + p = (uint8_t *)&temp; + UINT16_TO_BE_STREAM(p, rel_num); + result &= SDP_AddAttribute(handle, ATTR_ID_HID_DEVICE_RELNUM, UINT_DESC_TYPE, 2, (uint8_t *)&temp); + p = (uint8_t *)&temp; + UINT16_TO_BE_STREAM(p, parser_version); + result &= SDP_AddAttribute(handle, ATTR_ID_HID_PARSER_VERSION, UINT_DESC_TYPE, 2, (uint8_t *)&temp); + result &= SDP_AddAttribute(handle, ATTR_ID_HID_DEVICE_SUBCLASS, UINT_DESC_TYPE, 1, (uint8_t *)&dev_subclass); + result &= SDP_AddAttribute(handle, ATTR_ID_HID_COUNTRY_CODE, UINT_DESC_TYPE, 1, (uint8_t *)&country_code); + result &= SDP_AddAttribute(handle, ATTR_ID_HID_VIRTUAL_CABLE, BOOLEAN_DESC_TYPE, 1, (uint8_t *)&bool_true); + result &= SDP_AddAttribute(handle, ATTR_ID_HID_RECONNECT_INITIATE, BOOLEAN_DESC_TYPE, 1, (uint8_t *)&bool_true); + { + static uint8_t cdt = 0x22; + uint8_t *p_buf; + uint8_t seq_len = 4 + desc_len; + p_buf = (uint8_t *)osi_malloc(2048); + if (p_buf == NULL) { + HIDD_TRACE_ERROR("%s: Buffer allocation failure for size = 2048 ", __func__); + return HID_ERR_NOT_REGISTERED; + } + p = p_buf; + UINT8_TO_BE_STREAM(p, (DATA_ELE_SEQ_DESC_TYPE << 3) | SIZE_IN_NEXT_BYTE); + UINT8_TO_BE_STREAM(p, seq_len); + UINT8_TO_BE_STREAM(p, (UINT_DESC_TYPE << 3) | SIZE_ONE_BYTE); + UINT8_TO_BE_STREAM(p, cdt); + UINT8_TO_BE_STREAM(p, (TEXT_STR_DESC_TYPE << 3) | SIZE_IN_NEXT_BYTE); + UINT8_TO_BE_STREAM(p, desc_len); + ARRAY_TO_BE_STREAM(p, p_desc_data, (int)desc_len); + result &= SDP_AddAttribute(handle, ATTR_ID_HID_DESCRIPTOR_LIST, DATA_ELE_SEQ_DESC_TYPE, p - p_buf, p_buf); + osi_free(p_buf); + } + { + uint8_t lang_buf[8]; + p = lang_buf; + uint8_t seq_len = 6; + uint16_t lang_english = 0x0409; + UINT8_TO_BE_STREAM(p, (DATA_ELE_SEQ_DESC_TYPE << 3) | SIZE_IN_NEXT_BYTE); + UINT8_TO_BE_STREAM(p, seq_len); + UINT8_TO_BE_STREAM(p, (UINT_DESC_TYPE << 3) | SIZE_TWO_BYTES); + UINT16_TO_BE_STREAM(p, lang_english); + UINT8_TO_BE_STREAM(p, (UINT_DESC_TYPE << 3) | SIZE_TWO_BYTES); + UINT16_TO_BE_STREAM(p, LANGUAGE_BASE_ID); + result &= + SDP_AddAttribute(handle, ATTR_ID_HID_LANGUAGE_ID_BASE, DATA_ELE_SEQ_DESC_TYPE, p - lang_buf, lang_buf); + } + result &= SDP_AddAttribute(handle, ATTR_ID_HID_BATTERY_POWER, BOOLEAN_DESC_TYPE, 1, (uint8_t *)&bool_true); + result &= SDP_AddAttribute(handle, ATTR_ID_HID_REMOTE_WAKE, BOOLEAN_DESC_TYPE, 1, (uint8_t *)&bool_false); + result &= + SDP_AddAttribute(handle, ATTR_ID_HID_NORMALLY_CONNECTABLE, BOOLEAN_DESC_TYPE, 1, (uint8_t *)&bool_true); + result &= SDP_AddAttribute(handle, ATTR_ID_HID_BOOT_DEVICE, BOOLEAN_DESC_TYPE, 1, (uint8_t *)&bool_true); + p = (uint8_t *)&temp; + UINT16_TO_BE_STREAM(p, prof_ver); + result &= SDP_AddAttribute(handle, ATTR_ID_HID_PROFILE_VERSION, UINT_DESC_TYPE, 2, (uint8_t *)&temp); + } + if (result) { + uint16_t browse_group = UUID_SERVCLASS_PUBLIC_BROWSE_GROUP; + result &= SDP_AddUuidSequence(handle, ATTR_ID_BROWSE_GROUP_LIST, 1, &browse_group); + } + if (!result) { + HIDD_TRACE_ERROR("%s: failed to complete SDP record", __func__); + return HID_ERR_NOT_REGISTERED; + } + return HID_SUCCESS; +} + +/******************************************************************************* + * + * Function HID_DevSendReport + * + * Description Sends report + * + * Returns tHID_STATUS + * + ******************************************************************************/ +tHID_STATUS HID_DevSendReport(uint8_t channel, uint8_t type, uint8_t id, uint16_t len, uint8_t *p_data) +{ + HIDD_TRACE_VERBOSE("%s: channel=%d type=%d id=%d len=%d", __func__, channel, type, id, len); + + if (channel == HID_CHANNEL_CTRL) { + return hidd_conn_send_data(HID_CHANNEL_CTRL, HID_TRANS_DATA, type, id, len, p_data); + } + + if (channel == HID_CHANNEL_INTR && type == HID_PAR_REP_TYPE_INPUT) { + // on INTR we can only send INPUT + return hidd_conn_send_data(HID_CHANNEL_INTR, HID_TRANS_DATA, HID_PAR_REP_TYPE_INPUT, id, len, p_data); + } + + return HID_ERR_INVALID_PARAM; +} + +/******************************************************************************* + * + * Function HID_DevVirtualCableUnplug + * + * Description Sends Virtual Cable Unplug + * + * Returns tHID_STATUS + * + ******************************************************************************/ +tHID_STATUS HID_DevVirtualCableUnplug(void) +{ + HIDD_TRACE_API("%s", __func__); + + return hidd_conn_send_data(HID_CHANNEL_CTRL, HID_TRANS_CONTROL, HID_PAR_CONTROL_VIRTUAL_CABLE_UNPLUG, 0, 0, NULL); +} + +/******************************************************************************* + * + * Function HID_DevPlugDevice + * + * Description Establishes virtual cable to given host + * + * Returns tHID_STATUS + * + ******************************************************************************/ +tHID_STATUS HID_DevPlugDevice(BD_ADDR addr) +{ + hd_cb.device.in_use = TRUE; + memcpy(hd_cb.device.addr, addr, sizeof(BD_ADDR)); + + return HID_SUCCESS; +} + +/******************************************************************************* + * + * Function HID_DevUnplugDevice + * + * Description Unplugs virtual cable from given host + * + * Returns tHID_STATUS + * + ******************************************************************************/ +tHID_STATUS HID_DevUnplugDevice(BD_ADDR addr) +{ + if (!memcmp(hd_cb.device.addr, addr, sizeof(BD_ADDR))) { + hd_cb.device.in_use = FALSE; + hd_cb.device.conn.conn_state = HID_CONN_STATE_UNUSED; + hd_cb.device.conn.ctrl_cid = 0; + hd_cb.device.conn.intr_cid = 0; + } + return HID_SUCCESS; +} + +/******************************************************************************* + * + * Function HID_DevConnect + * + * Description Connects to device + * + * Returns tHID_STATUS + * + ******************************************************************************/ +tHID_STATUS HID_DevConnect(void) +{ + if (!hd_cb.reg_flag) { + return HID_ERR_NOT_REGISTERED; + } + if (!hd_cb.device.in_use) { + return HID_ERR_INVALID_PARAM; + } + if (hd_cb.device.state != HIDD_DEV_NO_CONN) { + return HID_ERR_ALREADY_CONN; + } + return hidd_conn_initiate(); +} + +/******************************************************************************* + * + * Function HID_DevDisconnect + * + * Description Disconnects from device + * + * Returns tHID_STATUS + * + ******************************************************************************/ +tHID_STATUS HID_DevDisconnect(void) +{ + if (!hd_cb.reg_flag) { + return HID_ERR_NOT_REGISTERED; + } + if (!hd_cb.device.in_use) { + return HID_ERR_INVALID_PARAM; + } + if (hd_cb.device.state == HIDD_DEV_NO_CONN) { + return HID_ERR_NO_CONNECTION; + } + return hidd_conn_disconnect(); +} + +/******************************************************************************* + * + * Function HID_DevSetIncomingPolicy + * + * Description Sets policy for incoming connections (allowed/disallowed) + * + * Returns tHID_STATUS + * + ******************************************************************************/ +tHID_STATUS HID_DevSetIncomingPolicy(bool allow) +{ + hd_cb.allow_incoming = allow; + return HID_SUCCESS; +} + +/******************************************************************************* + * + * Function HID_DevReportError + * + * Description Reports error for Set Report via HANDSHAKE + * + * Returns tHID_STATUS + * + ******************************************************************************/ +tHID_STATUS HID_DevReportError(uint8_t error) +{ + uint8_t handshake_param; + + HIDD_TRACE_API("%s: error = %d", __func__, error); + + switch (error) { + case HID_PAR_HANDSHAKE_RSP_SUCCESS: + case HID_PAR_HANDSHAKE_RSP_NOT_READY: + case HID_PAR_HANDSHAKE_RSP_ERR_INVALID_REP_ID: + case HID_PAR_HANDSHAKE_RSP_ERR_UNSUPPORTED_REQ: + case HID_PAR_HANDSHAKE_RSP_ERR_INVALID_PARAM: + case HID_PAR_HANDSHAKE_RSP_ERR_UNKNOWN: + case HID_PAR_HANDSHAKE_RSP_ERR_FATAL: + handshake_param = error; + break; + default: + handshake_param = HID_PAR_HANDSHAKE_RSP_ERR_UNKNOWN; + break; + } + + return hidd_conn_send_data(0, HID_TRANS_HANDSHAKE, handshake_param, 0, 0, NULL); +} + +/******************************************************************************* + * + * Function HID_DevGetDevice + * + * Description Returns the BD Address of virtually cabled device + * + * Returns tHID_STATUS + * + ******************************************************************************/ +tHID_STATUS HID_DevGetDevice(BD_ADDR *addr) +{ + HIDD_TRACE_API("%s", __func__); + + if (hd_cb.device.in_use) { + memcpy(addr, hd_cb.device.addr, sizeof(BD_ADDR)); + } else { + return HID_ERR_NOT_REGISTERED; + } + + return HID_SUCCESS; +} + +/******************************************************************************* + * + * Function HID_DevSetIncomingQos + * + * Description Sets Incoming QoS values for Interrupt L2CAP Channel + * + * Returns tHID_STATUS + * + ******************************************************************************/ +tHID_STATUS HID_DevSetIncomingQos(uint8_t service_type, uint32_t token_rate, uint32_t token_bucket_size, + uint32_t peak_bandwidth, uint32_t latency, uint32_t delay_variation) +{ + HIDD_TRACE_API("%s", __func__); + hd_cb.use_in_qos = TRUE; + hd_cb.in_qos.service_type = service_type; + hd_cb.in_qos.token_rate = token_rate; + hd_cb.in_qos.token_bucket_size = token_bucket_size; + hd_cb.in_qos.peak_bandwidth = peak_bandwidth; + hd_cb.in_qos.latency = latency; + hd_cb.in_qos.delay_variation = delay_variation; + return HID_SUCCESS; +} +/******************************************************************************* + * + * Function HID_DevSetOutgoingQos + * + * Description Sets Outgoing QoS values for Interrupt L2CAP Channel + * + * Returns tHID_STATUS + * + ******************************************************************************/ +tHID_STATUS HID_DevSetOutgoingQos(uint8_t service_type, uint32_t token_rate, uint32_t token_bucket_size, + uint32_t peak_bandwidth, uint32_t latency, uint32_t delay_variation) +{ + HIDD_TRACE_API("%s", __func__); + hd_cb.l2cap_intr_cfg.qos_present = TRUE; + hd_cb.l2cap_intr_cfg.qos.service_type = service_type; + hd_cb.l2cap_intr_cfg.qos.token_rate = token_rate; + hd_cb.l2cap_intr_cfg.qos.token_bucket_size = token_bucket_size; + hd_cb.l2cap_intr_cfg.qos.peak_bandwidth = peak_bandwidth; + hd_cb.l2cap_intr_cfg.qos.latency = latency; + hd_cb.l2cap_intr_cfg.qos.delay_variation = delay_variation; + return HID_SUCCESS; +} +#endif diff --git a/lib/bt/host/bluedroid/stack/hid/hidd_conn.c b/lib/bt/host/bluedroid/stack/hid/hidd_conn.c new file mode 100644 index 00000000..3b44402e --- /dev/null +++ b/lib/bt/host/bluedroid/stack/hid/hidd_conn.c @@ -0,0 +1,784 @@ +/****************************************************************************** + * + * Copyright (C) 2016 The Android Open Source Project + * Copyright (C) 2002-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ +/****************************************************************************** + * + * this file contains the connection interface functions + * + ******************************************************************************/ +#include "btm_int.h" +#include "hid_conn.h" +#include "hid_int.h" +#include "osi/allocator.h" +#include "osi/osi.h" +#include "stack/btm_api.h" +#include "stack/btu.h" +#include "stack/hidd_api.h" +#include "stack/hiddefs.h" +#include "stack/l2c_api.h" +#include "stack/l2cdefs.h" +#include +#include +#include + +#if (HID_DEV_INCLUDED == TRUE) + +static void hidd_l2cif_connect_ind(BD_ADDR bd_addr, uint16_t cid, uint16_t psm, uint8_t id); +static void hidd_l2cif_connect_cfm(uint16_t cid, uint16_t result); +static void hidd_l2cif_config_ind(uint16_t cid, tL2CAP_CFG_INFO *p_cfg); +static void hidd_l2cif_config_cfm(uint16_t cid, tL2CAP_CFG_INFO *p_cfg); +static void hidd_l2cif_disconnect_ind(uint16_t cid, bool ack_needed); +static void hidd_l2cif_disconnect_cfm(uint16_t cid, uint16_t result); +static void hidd_l2cif_data_ind(uint16_t cid, BT_HDR *p_msg); +static void hidd_l2cif_cong_ind(uint16_t cid, bool congested); + +static const tL2CAP_APPL_INFO dev_reg_info = {hidd_l2cif_connect_ind, + hidd_l2cif_connect_cfm, + NULL, + hidd_l2cif_config_ind, + hidd_l2cif_config_cfm, + hidd_l2cif_disconnect_ind, + hidd_l2cif_disconnect_cfm, + NULL, + hidd_l2cif_data_ind, + hidd_l2cif_cong_ind, + NULL}; +/******************************************************************************* + * + * Function hidd_check_config_done + * + * Description Checks if connection is configured and callback can be fired + * + * Returns void + * + ******************************************************************************/ +static void hidd_check_config_done(void) +{ + tHID_CONN *p_hcon; + p_hcon = &hd_cb.device.conn; + if (((p_hcon->conn_flags & HID_CONN_FLAGS_ALL_CONFIGURED) == HID_CONN_FLAGS_ALL_CONFIGURED) && + (p_hcon->conn_state == HID_CONN_STATE_CONFIG)) { + p_hcon->conn_state = HID_CONN_STATE_CONNECTED; + hd_cb.device.state = HIDD_DEV_CONNECTED; + hd_cb.callback(hd_cb.device.addr, HID_DHOST_EVT_OPEN, 0, NULL); + // send outstanding data on intr + if (hd_cb.pending_data) { + L2CA_DataWrite(p_hcon->intr_cid, hd_cb.pending_data); + hd_cb.pending_data = NULL; + } + } +} +/******************************************************************************* + * + * Function hidh_sec_check_complete_term + * + * Description HID security check complete callback function. + * + * Returns Send L2CA_ConnectRsp OK if secutiry check succeed; otherwise + * send security block L2C connection response. + * + ******************************************************************************/ +static void hidd_sec_check_complete(UNUSED_ATTR BD_ADDR bd_addr, UNUSED_ATTR tBT_TRANSPORT transport, void *p_ref_data, + uint8_t res) +{ + tHID_DEV_DEV_CTB *p_dev = (tHID_DEV_DEV_CTB *)p_ref_data; + if (res == BTM_SUCCESS && p_dev->conn.conn_state == HID_CONN_STATE_SECURITY) { + p_dev->conn.disc_reason = HID_SUCCESS; + p_dev->conn.conn_state = HID_CONN_STATE_CONNECTING_INTR; + L2CA_ConnectRsp(p_dev->addr, p_dev->conn.ctrl_id, p_dev->conn.ctrl_cid, L2CAP_CONN_OK, L2CAP_CONN_OK); + L2CA_ConfigReq(p_dev->conn.ctrl_cid, &hd_cb.l2cap_cfg); + } else if (res != BTM_SUCCESS) { + HIDD_TRACE_WARNING("%s: connection rejected by security", __func__); + p_dev->conn.disc_reason = HID_ERR_AUTH_FAILED; + p_dev->conn.conn_state = HID_CONN_STATE_UNUSED; + L2CA_ConnectRsp(p_dev->addr, p_dev->conn.ctrl_id, p_dev->conn.ctrl_cid, L2CAP_CONN_SECURITY_BLOCK, + L2CAP_CONN_OK); + return; + } +} +/******************************************************************************* + * + * Function hidd_sec_check_complete_orig + * + * Description HID security check complete callback function (device + *originated) + * + * Returns void + * + ******************************************************************************/ +void hidd_sec_check_complete_orig(UNUSED_ATTR BD_ADDR bd_addr, UNUSED_ATTR tBT_TRANSPORT transport, void *p_ref_data, + uint8_t res) +{ + tHID_DEV_DEV_CTB *p_dev = (tHID_DEV_DEV_CTB *)p_ref_data; + if (p_dev->conn.conn_state != HID_CONN_STATE_SECURITY) { + HIDD_TRACE_WARNING("%s: invalid state (%02x)", __func__, p_dev->conn.conn_state); + return; + } + if (res == BTM_SUCCESS) { + HIDD_TRACE_EVENT("%s: security ok", __func__); + p_dev->conn.disc_reason = HID_SUCCESS; + p_dev->conn.conn_state = HID_CONN_STATE_CONFIG; + L2CA_ConfigReq(p_dev->conn.ctrl_cid, &hd_cb.l2cap_cfg); + } else { + HIDD_TRACE_WARNING("%s: security check failed (%02x)", __func__, res); + p_dev->conn.disc_reason = HID_ERR_AUTH_FAILED; + hidd_conn_disconnect(); + } +} +/******************************************************************************* + * + * Function hidd_l2cif_connect_ind + * + * Description Handles incoming L2CAP connection (we act as server) + * + * Returns void + * + ******************************************************************************/ +static void hidd_l2cif_connect_ind(BD_ADDR bd_addr, uint16_t cid, uint16_t psm, uint8_t id) +{ + tHID_CONN *p_hcon; + tHID_DEV_DEV_CTB *p_dev; + bool accept = TRUE; // accept by default + HIDD_TRACE_EVENT("%s: psm=%04x cid=%04x id=%02x", __func__, psm, cid, id); + p_dev = &hd_cb.device; + if (!hd_cb.allow_incoming) { + HIDD_TRACE_WARNING("%s: incoming connections not allowed, rejecting", __func__); + L2CA_ConnectRsp(bd_addr, id, cid, L2CAP_CONN_NO_RESOURCES, 0); + return; + } + if (p_dev->in_use && memcmp(bd_addr, p_dev->addr, sizeof(BD_ADDR))) { + HIDD_TRACE_WARNING("%s: incoming connections from different device, rejecting", __func__); + L2CA_ConnectRsp(bd_addr, id, cid, L2CAP_CONN_NO_RESOURCES, 0); + return; + } else if (!p_dev->in_use) { + p_dev->in_use = TRUE; + memcpy(p_dev->addr, bd_addr, sizeof(BD_ADDR)); + p_dev->state = HIDD_DEV_NO_CONN; + } + p_hcon = &hd_cb.device.conn; + switch (psm) { + case HID_PSM_INTERRUPT: + if (p_hcon->ctrl_cid == 0) { + accept = FALSE; + HIDD_TRACE_WARNING("%s: incoming INTR without CTRL, rejecting", __func__); + } + if (p_hcon->conn_state != HID_CONN_STATE_CONNECTING_INTR) { + accept = FALSE; + HIDD_TRACE_WARNING("%s: incoming INTR in invalid state (%d), rejecting", __func__, p_hcon->conn_state); + } + break; + case HID_PSM_CONTROL: + if (p_hcon->conn_state != HID_CONN_STATE_UNUSED) { + accept = FALSE; + HIDD_TRACE_WARNING("%s: incoming CTRL in invalid state (%d), rejecting", __func__, p_hcon->conn_state); + } + break; + default: + accept = FALSE; + HIDD_TRACE_ERROR("%s: received invalid PSM, rejecting", __func__); + break; + } + if (!accept) { + L2CA_ConnectRsp(bd_addr, id, cid, L2CAP_CONN_NO_RESOURCES, 0); + return; + } + // for CTRL we need to go through security and we reply in callback from there + if (psm == HID_PSM_CONTROL) { + p_hcon->conn_flags = 0; + p_hcon->ctrl_cid = cid; + p_hcon->ctrl_id = id; + p_hcon->disc_reason = HID_L2CAP_CONN_FAIL; + p_hcon->conn_state = HID_CONN_STATE_SECURITY; + if (btm_sec_mx_access_request(p_dev->addr, HID_PSM_CONTROL, FALSE, BTM_SEC_PROTO_HID, HIDD_NOSEC_CHN, + &hidd_sec_check_complete, p_dev) == BTM_CMD_STARTED) { + L2CA_ConnectRsp(bd_addr, id, cid, L2CAP_CONN_PENDING, L2CAP_CONN_OK); + } + return; + } + // for INTR we go directly to config state + p_hcon->conn_state = HID_CONN_STATE_CONFIG; + p_hcon->intr_cid = cid; + L2CA_ConnectRsp(bd_addr, id, cid, L2CAP_CONN_OK, L2CAP_CONN_OK); + L2CA_ConfigReq(cid, &hd_cb.l2cap_intr_cfg); +} +/******************************************************************************* + * + * Function hidd_l2cif_connect_cfm + * + * Description Handles L2CAP connection response (we act as client) + * + * Returns void + * + ******************************************************************************/ +static void hidd_l2cif_connect_cfm(uint16_t cid, uint16_t result) +{ + tHID_DEV_DEV_CTB *p_dev = &hd_cb.device; + tHID_CONN *p_hcon = &hd_cb.device.conn; + HIDD_TRACE_EVENT("%s: cid=%04x result=%d, conn_state=%d", __func__, cid, result, p_hcon->conn_state); + if (p_hcon->ctrl_cid != cid && p_hcon->intr_cid != cid) { + HIDD_TRACE_WARNING("%s: unknown cid", __func__); + return; + } + if (!(p_hcon->conn_flags & HID_CONN_FLAGS_IS_ORIG) || + ((cid == p_hcon->ctrl_cid) && (p_hcon->conn_state != HID_CONN_STATE_CONNECTING_CTRL && (p_hcon->conn_state != HID_CONN_STATE_DISCONNECTING_INTR))) || + ((cid == p_hcon->intr_cid) && (p_hcon->conn_state != HID_CONN_STATE_CONNECTING_INTR) && (p_hcon->conn_state != HID_CONN_STATE_DISCONNECTING_CTRL))) { + HIDD_TRACE_WARNING("%s: unexpected, cid:0x%04x, ctrl_cid:0x%04x, intr_cid:0x%04x, conn_state:%d", __func__, cid, + p_hcon->ctrl_cid, p_hcon->intr_cid, p_hcon->conn_state); + return; + } + if (result != L2CAP_CONN_OK) { + HIDD_TRACE_WARNING("%s: connection failed, now disconnect", __func__); + if (cid == p_hcon->ctrl_cid) + p_hcon->ctrl_cid = 0; + else + p_hcon->intr_cid = 0; + hidd_conn_disconnect(); + hd_cb.callback(hd_cb.device.addr, HID_DHOST_EVT_CLOSE, HID_L2CAP_CONN_FAIL | (uint32_t)result, NULL); + return; + } + /* CTRL connect conf */ + if (cid == p_hcon->ctrl_cid) { + p_hcon->conn_state = HID_CONN_STATE_SECURITY; + p_hcon->disc_reason = HID_L2CAP_CONN_FAIL; /* in case disconnected before sec completed */ + btm_sec_mx_access_request(p_dev->addr, HID_PSM_CONTROL, TRUE, BTM_SEC_PROTO_HID, HIDD_SEC_CHN, + &hidd_sec_check_complete_orig, p_dev); + } else { + p_hcon->conn_state = HID_CONN_STATE_CONFIG; + L2CA_ConfigReq(cid, &hd_cb.l2cap_intr_cfg); + } + return; +} +/******************************************************************************* + * + * Function hidd_l2cif_config_ind + * + * Description Handles incoming L2CAP configuration request + * + * Returns void + * + ******************************************************************************/ +static void hidd_l2cif_config_ind(uint16_t cid, tL2CAP_CFG_INFO *p_cfg) +{ + tHID_CONN *p_hcon; + HIDD_TRACE_EVENT("%s: cid=%04x", __func__, cid); + p_hcon = &hd_cb.device.conn; + if (p_hcon->ctrl_cid != cid && p_hcon->intr_cid != cid) { + HIDD_TRACE_WARNING("%s: unknown cid", __func__); + return; + } + if ((!p_cfg->mtu_present) || (p_cfg->mtu > HID_DEV_MTU_SIZE)) + p_hcon->rem_mtu_size = HID_DEV_MTU_SIZE; + else + p_hcon->rem_mtu_size = p_cfg->mtu; + // accept without changes + p_cfg->flush_to_present = FALSE; + p_cfg->mtu_present = FALSE; + p_cfg->result = L2CAP_CFG_OK; + if (cid == p_hcon->intr_cid && hd_cb.use_in_qos && !p_cfg->qos_present) { + p_cfg->qos_present = TRUE; + memcpy(&p_cfg->qos, &hd_cb.in_qos, sizeof(FLOW_SPEC)); + } + L2CA_ConfigRsp(cid, p_cfg); + // update flags + if (cid == p_hcon->ctrl_cid) { + p_hcon->conn_flags |= HID_CONN_FLAGS_HIS_CTRL_CFG_DONE; + if ((p_hcon->conn_flags & HID_CONN_FLAGS_IS_ORIG) && (p_hcon->conn_flags & HID_CONN_FLAGS_MY_CTRL_CFG_DONE)) { + p_hcon->disc_reason = HID_L2CAP_CONN_FAIL; + if ((p_hcon->intr_cid = L2CA_ConnectReq(HID_PSM_INTERRUPT, hd_cb.device.addr)) == 0) { + p_hcon->conn_state = HID_CONN_STATE_UNUSED; + hidd_conn_disconnect(); + HIDD_TRACE_WARNING("%s: could not start L2CAP connection for INTR", __func__); + hd_cb.callback(hd_cb.device.addr, HID_DHOST_EVT_CLOSE, HID_ERR_L2CAP_FAILED, NULL); + return; + } else { + p_hcon->conn_state = HID_CONN_STATE_CONNECTING_INTR; + } + } + } else { + p_hcon->conn_flags |= HID_CONN_FLAGS_HIS_INTR_CFG_DONE; + } + hidd_check_config_done(); +} +/******************************************************************************* + * + * Function hidd_l2cif_config_cfm + * + * Description Handles incoming L2CAP configuration response + * + * Returns void + * + ******************************************************************************/ +static void hidd_l2cif_config_cfm(uint16_t cid, tL2CAP_CFG_INFO *p_cfg) +{ + tHID_CONN *p_hcon; + uint32_t reason; + HIDD_TRACE_EVENT("%s: cid=%04x pcfg->result=%d", __func__, cid, p_cfg->result); + p_hcon = &hd_cb.device.conn; + if (p_hcon->ctrl_cid != cid && p_hcon->intr_cid != cid) { + HIDD_TRACE_WARNING("%s: unknown cid", __func__); + return; + } + if (p_hcon->intr_cid == cid && p_cfg->result == L2CAP_CFG_UNACCEPTABLE_PARAMS && p_cfg->qos_present) { + tL2CAP_CFG_INFO new_qos; + // QoS parameters not accepted for intr, try again with host proposal + memcpy(&new_qos, &hd_cb.l2cap_intr_cfg, sizeof(new_qos)); + memcpy(&new_qos.qos, &p_cfg->qos, sizeof(FLOW_SPEC)); + new_qos.qos_present = TRUE; + HIDD_TRACE_WARNING("%s: config failed, retry", __func__); + L2CA_ConfigReq(cid, &new_qos); + return; + } else if (p_hcon->intr_cid == cid && p_cfg->result == L2CAP_CFG_UNKNOWN_OPTIONS) { + // QoS not understood by remote device, try configuring without QoS + HIDD_TRACE_WARNING("%s: config failed, retry without QoS", __func__); + L2CA_ConfigReq(cid, &hd_cb.l2cap_cfg); + return; + } else if (p_cfg->result != L2CAP_CFG_OK) { + HIDD_TRACE_WARNING("%s: config failed, disconnecting", __func__); + hidd_conn_disconnect(); + reason = HID_L2CAP_CFG_FAIL | (uint32_t)p_cfg->result; + hd_cb.callback(hd_cb.device.addr, HID_DHOST_EVT_CLOSE, reason, NULL); + return; + } + // update flags + if (cid == p_hcon->ctrl_cid) { + p_hcon->conn_flags |= HID_CONN_FLAGS_MY_CTRL_CFG_DONE; + if ((p_hcon->conn_flags & HID_CONN_FLAGS_IS_ORIG) && (p_hcon->conn_flags & HID_CONN_FLAGS_HIS_CTRL_CFG_DONE)) { + p_hcon->disc_reason = HID_L2CAP_CONN_FAIL; + if ((p_hcon->intr_cid = L2CA_ConnectReq(HID_PSM_INTERRUPT, hd_cb.device.addr)) == 0) { + p_hcon->conn_state = HID_CONN_STATE_UNUSED; + hidd_conn_disconnect(); + HIDD_TRACE_WARNING("%s: could not start L2CAP connection for INTR", __func__); + hd_cb.callback(hd_cb.device.addr, HID_DHOST_EVT_CLOSE, HID_ERR_L2CAP_FAILED, NULL); + return; + } else { + p_hcon->conn_state = HID_CONN_STATE_CONNECTING_INTR; + } + } + } else { + p_hcon->conn_flags |= HID_CONN_FLAGS_MY_INTR_CFG_DONE; + } + hidd_check_config_done(); +} +/******************************************************************************* + * + * Function hidd_l2cif_disconnect_ind + * + * Description Handler incoming L2CAP disconnection request + * + * Returns void + * + ******************************************************************************/ +static void hidd_l2cif_disconnect_ind(uint16_t cid, bool ack_needed) +{ + tHID_CONN *p_hcon; + HIDD_TRACE_EVENT("%s: cid=%04x ack_needed=%d", __func__, cid, ack_needed); + p_hcon = &hd_cb.device.conn; + if (p_hcon->conn_state == HID_CONN_STATE_UNUSED || (p_hcon->ctrl_cid != cid && p_hcon->intr_cid != cid)) { + HIDD_TRACE_WARNING("%s: unknown cid", __func__); + return; + } + if (ack_needed) + L2CA_DisconnectRsp(cid); + if (cid == p_hcon->ctrl_cid) { + p_hcon->ctrl_cid = 0; + p_hcon->conn_state = HID_CONN_STATE_DISCONNECTING_CTRL; + } else { + p_hcon->intr_cid = 0; + p_hcon->conn_state = HID_CONN_STATE_DISCONNECTING_INTR; + } + if ((p_hcon->ctrl_cid == 0) && (p_hcon->intr_cid == 0)) { + HIDD_TRACE_EVENT("%s: INTR and CTRL disconnected", __func__); + // clean any outstanding data on intr + if (hd_cb.pending_data) { + osi_free(hd_cb.pending_data); + hd_cb.pending_data = NULL; + } + hd_cb.device.state = HIDD_DEV_NO_CONN; + p_hcon->conn_state = HID_CONN_STATE_UNUSED; + hd_cb.callback(hd_cb.device.addr, HID_DHOST_EVT_CLOSE, p_hcon->disc_reason, NULL); + } +} +/******************************************************************************* + * + * Function hidd_l2cif_disconnect_cfm + * + * Description Handles L2CAP disconection response + * + * Returns void + * + ******************************************************************************/ +static void hidd_l2cif_disconnect_cfm(uint16_t cid, uint16_t result) +{ + tHID_CONN *p_hcon; + HIDD_TRACE_EVENT("%s: cid=%04x result=%d", __func__, cid, result); + p_hcon = &hd_cb.device.conn; + if (p_hcon->conn_state == HID_CONN_STATE_UNUSED || (p_hcon->ctrl_cid != cid && p_hcon->intr_cid != cid)) { + HIDD_TRACE_WARNING("%s: unknown cid", __func__); + return; + } + if (cid == p_hcon->ctrl_cid) { + p_hcon->ctrl_cid = 0; + } else { + p_hcon->intr_cid = 0; + // now disconnect CTRL + L2CA_DisconnectReq(p_hcon->ctrl_cid); + } + if ((p_hcon->ctrl_cid == 0) && (p_hcon->intr_cid == 0)) { + HIDD_TRACE_EVENT("%s: INTR and CTRL disconnected", __func__); + hd_cb.device.state = HIDD_DEV_NO_CONN; + p_hcon->conn_state = HID_CONN_STATE_UNUSED; + if (hd_cb.pending_vc_unplug) { + hd_cb.callback(hd_cb.device.addr, HID_DHOST_EVT_VC_UNPLUG, p_hcon->disc_reason, NULL); + hd_cb.pending_vc_unplug = FALSE; + } else { + hd_cb.callback(hd_cb.device.addr, HID_DHOST_EVT_CLOSE, p_hcon->disc_reason, NULL); + } + } +} +/******************************************************************************* + * + * Function hidd_l2cif_cong_ind + * + * Description Handles L2CAP congestion status event + * + * Returns void + * + ******************************************************************************/ +static void hidd_l2cif_cong_ind(uint16_t cid, bool congested) +{ + tHID_CONN *p_hcon; + HIDD_TRACE_EVENT("%s: cid=%04x congested=%d", __func__, cid, congested); + p_hcon = &hd_cb.device.conn; + if (p_hcon->conn_state == HID_CONN_STATE_UNUSED || (p_hcon->ctrl_cid != cid && p_hcon->intr_cid != cid)) { + HIDD_TRACE_WARNING("%s: unknown cid", __func__); + return; + } + if (congested) { + p_hcon->conn_flags |= HID_CONN_FLAGS_CONGESTED; + } else { + p_hcon->conn_flags &= ~HID_CONN_FLAGS_CONGESTED; + } +} +/******************************************************************************* + * + * Function hidd_l2cif_data_ind + * + * Description Handler incoming data on L2CAP channel + * + * Returns void + * + ******************************************************************************/ +static void hidd_l2cif_data_ind(uint16_t cid, BT_HDR *p_msg) +{ + tHID_CONN *p_hcon; + uint8_t *p_data = (uint8_t *)(p_msg + 1) + p_msg->offset; + uint8_t msg_type, param; + bool err = FALSE; + HIDD_TRACE_EVENT("%s: cid=%04x", __func__, cid); + p_hcon = &hd_cb.device.conn; + if (p_hcon->conn_state == HID_CONN_STATE_UNUSED || (p_hcon->ctrl_cid != cid && p_hcon->intr_cid != cid)) { + HIDD_TRACE_WARNING("%s: unknown cid", __func__); + osi_free(p_msg); + return; + } + msg_type = HID_GET_TRANS_FROM_HDR(*p_data); + param = HID_GET_PARAM_FROM_HDR(*p_data); + if (msg_type == HID_TRANS_DATA && cid == p_hcon->intr_cid) { + // skip HID header + p_msg->offset++; + p_msg->len--; + hd_cb.callback(hd_cb.device.addr, HID_DHOST_EVT_INTR_DATA, 0, p_msg); + return; + } + switch (msg_type) { + case HID_TRANS_GET_REPORT: + // at this stage we don't know if Report Id shall be included in request + // so we pass complete packet in callback and let other code analyze this + hd_cb.callback(hd_cb.device.addr, HID_DHOST_EVT_GET_REPORT, !!(param & HID_PAR_GET_REP_BUFSIZE_FOLLOWS), p_msg); + break; + case HID_TRANS_SET_REPORT: + // as above + hd_cb.callback(hd_cb.device.addr, HID_DHOST_EVT_SET_REPORT, 0, p_msg); + break; + case HID_TRANS_GET_IDLE: + hidd_conn_send_data(HID_CHANNEL_CTRL, HID_TRANS_DATA, HID_PAR_REP_TYPE_OTHER, hd_cb.device.idle_time, 0, NULL); + osi_free(p_msg); + break; + case HID_TRANS_SET_IDLE: + if (p_msg->len != 2) { + HIDD_TRACE_ERROR("%s: invalid len (%d) set idle request received", __func__, p_msg->len); + err = TRUE; + } else { + hd_cb.device.idle_time = p_data[1]; + HIDD_TRACE_DEBUG("%s: idle_time = %d", __func__, hd_cb.device.idle_time); + if (hd_cb.device.idle_time) { + HIDD_TRACE_WARNING("%s: idle_time of %d ms not supported by HID Device", __func__, + (hd_cb.device.idle_time * 4)); + err = TRUE; + } + } + if (!err) { + hidd_conn_send_data(0, HID_TRANS_HANDSHAKE, HID_PAR_HANDSHAKE_RSP_SUCCESS, 0, 0, NULL); + } else { + hidd_conn_send_data(0, HID_TRANS_HANDSHAKE, HID_PAR_HANDSHAKE_RSP_ERR_INVALID_PARAM, 0, 0, NULL); + } + osi_free(p_msg); + break; + case HID_TRANS_GET_PROTOCOL: + hidd_conn_send_data(HID_CHANNEL_CTRL, HID_TRANS_DATA, HID_PAR_REP_TYPE_OTHER, !hd_cb.device.boot_mode, 0, NULL); + osi_free(p_msg); + break; + case HID_TRANS_SET_PROTOCOL: + hd_cb.device.boot_mode = !(param & HID_PAR_PROTOCOL_MASK); + hd_cb.callback(hd_cb.device.addr, HID_DHOST_EVT_SET_PROTOCOL, param & HID_PAR_PROTOCOL_MASK, NULL); + hidd_conn_send_data(0, HID_TRANS_HANDSHAKE, HID_PAR_HANDSHAKE_RSP_SUCCESS, 0, 0, NULL); + osi_free(p_msg); + break; + case HID_TRANS_CONTROL: + switch (param) { + case HID_PAR_CONTROL_SUSPEND: + hd_cb.callback(hd_cb.device.addr, HID_DHOST_EVT_SUSPEND, 0, NULL); + break; + case HID_PAR_CONTROL_EXIT_SUSPEND: + hd_cb.callback(hd_cb.device.addr, HID_DHOST_EVT_EXIT_SUSPEND, 0, NULL); + break; + case HID_PAR_CONTROL_VIRTUAL_CABLE_UNPLUG: + hidd_conn_disconnect(); + // set flag so we can notify properly when disconnected + hd_cb.pending_vc_unplug = TRUE; + break; + } + osi_free(p_msg); + break; + case HID_TRANS_DATA: + default: + HIDD_TRACE_WARNING("%s: got unsupported msg (%d)", __func__, msg_type); + hidd_conn_send_data(0, HID_TRANS_HANDSHAKE, HID_PAR_HANDSHAKE_RSP_ERR_UNSUPPORTED_REQ, 0, 0, NULL); + osi_free(p_msg); + break; + } +} +/******************************************************************************* + * + * Function hidd_conn_reg + * + * Description Registers L2CAP channels + * + * Returns void + * + ******************************************************************************/ +tHID_STATUS hidd_conn_reg(void) +{ + HIDD_TRACE_API("%s", __func__); + memset(&hd_cb.l2cap_cfg, 0, sizeof(tL2CAP_CFG_INFO)); + hd_cb.l2cap_cfg.mtu_present = TRUE; + hd_cb.l2cap_cfg.mtu = HID_DEV_MTU_SIZE; + hd_cb.l2cap_cfg.flush_to_present = TRUE; + hd_cb.l2cap_cfg.flush_to = HID_DEV_FLUSH_TO; + memset(&hd_cb.l2cap_intr_cfg, 0, sizeof(tL2CAP_CFG_INFO)); + hd_cb.l2cap_intr_cfg.mtu_present = TRUE; + hd_cb.l2cap_intr_cfg.mtu = HID_DEV_MTU_SIZE; + hd_cb.l2cap_intr_cfg.flush_to_present = TRUE; + hd_cb.l2cap_intr_cfg.flush_to = HID_DEV_FLUSH_TO; + if (!L2CA_Register(HID_PSM_CONTROL, (tL2CAP_APPL_INFO *)&dev_reg_info)) { + HIDD_TRACE_ERROR("HID Control (device) registration failed"); + return (HID_ERR_L2CAP_FAILED); + } + if (!L2CA_Register(HID_PSM_INTERRUPT, (tL2CAP_APPL_INFO *)&dev_reg_info)) { + L2CA_Deregister(HID_PSM_CONTROL); + HIDD_TRACE_ERROR("HID Interrupt (device) registration failed"); + return (HID_ERR_L2CAP_FAILED); + } + return (HID_SUCCESS); +} +/******************************************************************************* + * + * Function hidd_conn_dereg + * + * Description Deregisters L2CAP channels + * + * Returns void + * + ******************************************************************************/ +void hidd_conn_dereg(void) +{ + HIDD_TRACE_API("%s", __func__); + L2CA_Deregister(HID_PSM_CONTROL); + L2CA_Deregister(HID_PSM_INTERRUPT); +} +/******************************************************************************* + * + * Function hidd_conn_initiate + * + * Description Initiates HID connection to plugged device + * + * Returns HID_SUCCESS + * + ******************************************************************************/ +tHID_STATUS hidd_conn_initiate(void) +{ + tHID_DEV_DEV_CTB *p_dev = &hd_cb.device; + HIDD_TRACE_API("%s", __func__); + if (!p_dev->in_use) { + HIDD_TRACE_WARNING("%s: no virtual cable established", __func__); + return (HID_ERR_NOT_REGISTERED); + } + if (p_dev->conn.conn_state != HID_CONN_STATE_UNUSED) { + HIDD_TRACE_WARNING("%s: connection already in progress", __func__); + return (HID_ERR_CONN_IN_PROCESS); + } + p_dev->conn.ctrl_cid = 0; + p_dev->conn.intr_cid = 0; + p_dev->conn.disc_reason = HID_L2CAP_CONN_FAIL; + p_dev->conn.conn_flags = HID_CONN_FLAGS_IS_ORIG; + BTM_SetOutService(p_dev->addr, BTM_SEC_SERVICE_HIDD_SEC_CTRL, HIDD_SEC_CHN); + /* Check if L2CAP started the connection process */ + if ((p_dev->conn.ctrl_cid = L2CA_ConnectReq(HID_PSM_CONTROL, p_dev->addr)) == 0) { + HIDD_TRACE_WARNING("%s: could not start L2CAP connection", __func__); + hd_cb.callback(hd_cb.device.addr, HID_DHOST_EVT_CLOSE, HID_ERR_L2CAP_FAILED, NULL); + } else { + p_dev->conn.conn_state = HID_CONN_STATE_CONNECTING_CTRL; + } + return (HID_SUCCESS); +} +/******************************************************************************* + * + * Function hidd_conn_disconnect + * + * Description Disconnects existing HID connection + * + * Returns HID_SUCCESS + * + ******************************************************************************/ +tHID_STATUS hidd_conn_disconnect(void) +{ + tHID_CONN *p_hcon; + HIDD_TRACE_API("%s", __func__); + // clean any outstanding data on intr + if (hd_cb.pending_data) { + osi_free(hd_cb.pending_data); + hd_cb.pending_data = NULL; + } + p_hcon = &hd_cb.device.conn; + if ((p_hcon->ctrl_cid != 0) || (p_hcon->intr_cid != 0)) { + /* Set l2cap idle timeout to 0 (so ACL link is disconnected + * immediately after last channel is closed) */ + L2CA_SetIdleTimeoutByBdAddr(hd_cb.device.addr, 0, BT_TRANSPORT_BR_EDR); + if (p_hcon->intr_cid) { + p_hcon->conn_state = HID_CONN_STATE_DISCONNECTING_INTR; + L2CA_DisconnectReq(p_hcon->intr_cid); + } else if (p_hcon->ctrl_cid) { + p_hcon->conn_state = HID_CONN_STATE_DISCONNECTING_CTRL; + L2CA_DisconnectReq(p_hcon->ctrl_cid); + } + } else { + HIDD_TRACE_WARNING("%s: already disconnected", __func__); + p_hcon->conn_state = HID_CONN_STATE_UNUSED; + } + return (HID_SUCCESS); +} +/******************************************************************************* + * + * Function hidd_conn_send_data + * + * Description Sends data to host + * + * Returns tHID_STATUS + * + ******************************************************************************/ +tHID_STATUS hidd_conn_send_data(uint8_t channel, uint8_t msg_type, uint8_t param, uint8_t data, uint16_t len, + uint8_t *p_data) +{ + tHID_CONN *p_hcon; + BT_HDR *p_buf; + uint8_t *p_out; + uint16_t cid; + uint16_t buf_size; + + HIDD_TRACE_VERBOSE("%s: channel(%d), msg_type(%d), len(%d)", __func__, channel, msg_type, len); + + p_hcon = &hd_cb.device.conn; + if (p_hcon->conn_flags & HID_CONN_FLAGS_CONGESTED) { + return HID_ERR_CONGESTED; + } + + switch (msg_type) { + case HID_TRANS_HANDSHAKE: + case HID_TRANS_CONTROL: + cid = p_hcon->ctrl_cid; + buf_size = HID_CONTROL_BUF_SIZE; + break; + case HID_TRANS_DATA: + if (channel == HID_CHANNEL_CTRL) { + cid = p_hcon->ctrl_cid; + buf_size = HID_CONTROL_BUF_SIZE; + } else { + cid = p_hcon->intr_cid; + buf_size = HID_INTERRUPT_BUF_SIZE; + } + break; + default: + return (HID_ERR_INVALID_PARAM); + } + p_buf = (BT_HDR *)osi_malloc(buf_size); + if (p_buf == NULL) + return (HID_ERR_NO_RESOURCES); + p_buf->offset = L2CAP_MIN_OFFSET; + p_out = (uint8_t *)(p_buf + 1) + p_buf->offset; + *p_out = HID_BUILD_HDR(msg_type, param); + p_out++; + p_buf->len = 1; // start with header only + // add report id prefix only if non-zero (which is reserved) + if (msg_type == HID_TRANS_DATA && (data || param == HID_PAR_REP_TYPE_OTHER)) { + *p_out = data; // report_id + p_out++; + p_buf->len++; + } + if (len > 0 && p_data != NULL) { + memcpy(p_out, p_data, len); + p_buf->len += len; + } + // check if connected + if (hd_cb.device.state != HIDD_DEV_CONNECTED) { + // for DATA on intr we hold transfer and try to reconnect + if (msg_type == HID_TRANS_DATA && cid == p_hcon->intr_cid) { + // drop previous data, we do not queue it for now + if (hd_cb.pending_data) { + osi_free(hd_cb.pending_data); + } + hd_cb.pending_data = p_buf; + if (hd_cb.device.conn.conn_state == HID_CONN_STATE_UNUSED) { + HIDD_TRACE_WARNING("%s: try to reconnect!", __func__); + return hidd_conn_initiate(); + } + return HID_SUCCESS; + } + return HID_ERR_NO_CONNECTION; + } +#ifdef REPORT_TRANSFER_TIMESTAMP + if (report_transfer) { + HIDD_TRACE_ERROR("%s: report sent", __func__); + } +#endif + HIDD_TRACE_VERBOSE("%s: report sent", __func__); + if (L2CA_DataWrite(cid, p_buf) == L2CAP_DW_FAILED) + return (HID_ERR_CONGESTED); + return (HID_SUCCESS); +} + +#endif diff --git a/lib/bt/host/bluedroid/stack/hid/hidh_api.c b/lib/bt/host/bluedroid/stack/hid/hidh_api.c new file mode 100644 index 00000000..ea8e73a7 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/hid/hidh_api.c @@ -0,0 +1,654 @@ +/****************************************************************************** + * + * Copyright (C) 2002-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains the HID HOST API entry points + * + ******************************************************************************/ + +#include +#include +#include + +#include "common/bt_target.h" +#include "osi/allocator.h" +#include "stack/bt_types.h" +#include "stack/hiddefs.h" +#include "stack/hidh_api.h" +#include "hid_int.h" +#include "stack/btm_api.h" +#include "stack/btu.h" +#include "btm_int.h" + +#if (HID_HOST_INCLUDED == TRUE) + +#if HID_DYNAMIC_MEMORY == FALSE +tHID_HOST_CTB hh_cb; +#else +tHID_HOST_CTB *hidh_cb_ptr = NULL; +#endif + +static void hidh_search_callback (UINT16 sdp_result); + +/******************************************************************************* +** +** Function HID_HostGetSDPRecord +** +** Description This function reads the device SDP record +** +** Returns tHID_STATUS +** +*******************************************************************************/ +tHID_STATUS HID_HostGetSDPRecord ( BD_ADDR addr, tSDP_DISCOVERY_DB *p_db, UINT32 db_len, + tHID_HOST_SDP_CALLBACK *sdp_cback ) +{ + tSDP_UUID uuid_list; + + if ( hh_cb.sdp_busy ) { + return HID_ERR_SDP_BUSY; + } + + uuid_list.len = 2; + uuid_list.uu.uuid16 = UUID_SERVCLASS_HUMAN_INTERFACE; + + hh_cb.p_sdp_db = p_db; + SDP_InitDiscoveryDb (p_db, db_len, 1, &uuid_list, 0, NULL); + + if (SDP_ServiceSearchRequest (addr, p_db, hidh_search_callback)) { + hh_cb.sdp_cback = sdp_cback ; + hh_cb.sdp_busy = TRUE; + return HID_SUCCESS; + } else { + return HID_ERR_NO_RESOURCES; + } +} + +void hidh_get_str_attr( tSDP_DISC_REC *p_rec, UINT16 attr_id, UINT16 max_len, char *str ) +{ + tSDP_DISC_ATTR *p_attr; + UINT16 name_len; + + if ((p_attr = SDP_FindAttributeInRec(p_rec, attr_id)) != NULL) { + if ((name_len = SDP_DISC_ATTR_LEN(p_attr->attr_len_type)) < max_len ) { + memcpy( str, (char *) p_attr->attr_value.v.array, name_len ); + str[name_len] = '\0'; + } else { + memcpy( str, (char *) p_attr->attr_value.v.array, max_len - 1 ); + str[max_len - 1] = '\0'; + } + } else { + str[0] = '\0'; + } +} + + +static void hidh_search_callback (UINT16 sdp_result) +{ + tSDP_DISCOVERY_DB *p_db = hh_cb.p_sdp_db; + tSDP_DISC_REC *p_rec; + tSDP_DISC_ATTR *p_attr, *p_subattr1, *p_subattr2, *p_repdesc; + tBT_UUID hid_uuid; + tHID_DEV_SDP_INFO *p_nvi = &hh_cb.sdp_rec; + UINT16 attr_mask = 0; + + hid_uuid.len = LEN_UUID_16; + hid_uuid.uu.uuid16 = UUID_SERVCLASS_HUMAN_INTERFACE; + + hh_cb.sdp_busy = FALSE; + + if (sdp_result != SDP_SUCCESS) { + hh_cb.sdp_cback(sdp_result, 0, NULL); + return; + } + + if ((p_rec = SDP_FindServiceUUIDInDb (p_db, &hid_uuid, NULL)) == NULL) { + hh_cb.sdp_cback(HID_SDP_NO_SERV_UUID, 0, NULL); + return; + } + + memset (&hh_cb.sdp_rec, 0, sizeof( tHID_DEV_SDP_INFO )); + + /* First, verify the mandatory fields we care about */ + if (((p_attr = SDP_FindAttributeInRec (p_rec, ATTR_ID_HID_DESCRIPTOR_LIST)) == NULL) + || (SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) != DATA_ELE_SEQ_DESC_TYPE) + || ((p_subattr1 = p_attr->attr_value.v.p_sub_attr) == NULL) + || (SDP_DISC_ATTR_TYPE(p_subattr1->attr_len_type) != DATA_ELE_SEQ_DESC_TYPE) + || ((p_subattr2 = p_subattr1->attr_value.v.p_sub_attr) == NULL) + || ((p_repdesc = p_subattr2->p_next_attr) == NULL) + || (SDP_DISC_ATTR_TYPE(p_repdesc->attr_len_type) != TEXT_STR_DESC_TYPE)) { + hh_cb.sdp_cback(HID_SDP_MANDATORY_MISSING, 0, NULL); + return; + } + + if ((p_nvi->dscp_info.dl_len = SDP_DISC_ATTR_LEN(p_repdesc->attr_len_type)) != 0) { + p_nvi->dscp_info.dsc_list = (UINT8 *) &p_repdesc->attr_value; + } + + if (((p_attr = SDP_FindAttributeInRec (p_rec, ATTR_ID_HID_VIRTUAL_CABLE)) != NULL) && + (p_attr->attr_value.v.u8) ) { + attr_mask |= HID_VIRTUAL_CABLE; + } + + if (((p_attr = SDP_FindAttributeInRec (p_rec, ATTR_ID_HID_RECONNECT_INITIATE)) != NULL) && + (p_attr->attr_value.v.u8) ) { + attr_mask |= HID_RECONN_INIT; + } + + if (((p_attr = SDP_FindAttributeInRec (p_rec, ATTR_ID_HID_NORMALLY_CONNECTABLE)) != NULL) && + (p_attr->attr_value.v.u8) ) { + attr_mask |= HID_NORMALLY_CONNECTABLE; + } + + if (((p_attr = SDP_FindAttributeInRec (p_rec, ATTR_ID_HID_SDP_DISABLE)) != NULL) && + (p_attr->attr_value.v.u8) ) { + attr_mask |= HID_SDP_DISABLE; + } + + if (((p_attr = SDP_FindAttributeInRec (p_rec, ATTR_ID_HID_BATTERY_POWER)) != NULL) && + (p_attr->attr_value.v.u8) ) { + attr_mask |= HID_BATTERY_POWER; + } + + if (((p_attr = SDP_FindAttributeInRec (p_rec, ATTR_ID_HID_REMOTE_WAKE)) != NULL) && + (p_attr->attr_value.v.u8) ) { + attr_mask |= HID_REMOTE_WAKE; + } + + hidh_get_str_attr( p_rec, ATTR_ID_SERVICE_NAME, HID_MAX_SVC_NAME_LEN, p_nvi->svc_name ); + hidh_get_str_attr( p_rec, ATTR_ID_SERVICE_DESCRIPTION, HID_MAX_SVC_DESCR_LEN, p_nvi->svc_descr ); + hidh_get_str_attr( p_rec, ATTR_ID_PROVIDER_NAME, HID_MAX_PROV_NAME_LEN, p_nvi->prov_name ); + + if (((p_attr = SDP_FindAttributeInRec (p_rec, ATTR_ID_HID_DEVICE_RELNUM)) != NULL)) { + p_nvi->rel_num = p_attr->attr_value.v.u16; + } + + if (((p_attr = SDP_FindAttributeInRec (p_rec, ATTR_ID_HID_COUNTRY_CODE)) != NULL)) { + p_nvi->ctry_code = p_attr->attr_value.v.u8; + } + + if (((p_attr = SDP_FindAttributeInRec (p_rec, ATTR_ID_HID_DEVICE_SUBCLASS)) != NULL)) { + p_nvi->sub_class = p_attr->attr_value.v.u8; + } + + if (((p_attr = SDP_FindAttributeInRec (p_rec, ATTR_ID_HID_PARSER_VERSION)) != NULL)) { + p_nvi->hpars_ver = p_attr->attr_value.v.u16; + } + + if (((p_attr = SDP_FindAttributeInRec (p_rec, ATTR_ID_HID_LINK_SUPERVISION_TO)) != NULL)) { + attr_mask |= HID_SUP_TOUT_AVLBL; + p_nvi->sup_timeout = p_attr->attr_value.v.u16; + } + + if (((p_attr = SDP_FindAttributeInRec (p_rec, ATTR_ID_HID_SSR_HOST_MAX_LAT)) != NULL)) { + attr_mask |= HID_SSR_MAX_LATENCY; + p_nvi->ssr_max_latency = p_attr->attr_value.v.u16; + } else { + p_nvi->ssr_max_latency = HID_SSR_PARAM_INVALID; + } + + if (((p_attr = SDP_FindAttributeInRec (p_rec, ATTR_ID_HID_SSR_HOST_MIN_TOUT)) != NULL)) { + attr_mask |= HID_SSR_MIN_TOUT; + p_nvi->ssr_min_tout = p_attr->attr_value.v.u16; + } else { + p_nvi->ssr_min_tout = HID_SSR_PARAM_INVALID; + } + + hh_cb.sdp_rec.p_sdp_layer_rec = p_rec; + hh_cb.sdp_cback(SDP_SUCCESS, attr_mask, &hh_cb.sdp_rec); +} + + +/******************************************************************************* +** +** Function HID_HostInit +** +** Description This function initializes the control block and trace variable +** +** Returns tHID_STATUS +** +*******************************************************************************/ +tHID_STATUS HID_HostInit (void) +{ +#if (HID_DYNAMIC_MEMORY) + if (!hidh_cb_ptr) { + hidh_cb_ptr = (tHID_HOST_CTB *)osi_malloc(sizeof(tHID_HOST_CTB)); + if (!hidh_cb_ptr) { + return HID_ERR_NO_RESOURCES; + } + } +#endif /* #if (HID_DYNAMIC_MEMORY) */ + memset(&hh_cb, 0, sizeof(tHID_HOST_CTB)); + +#if defined(HIDH_INITIAL_TRACE_LEVEL) + hh_cb.trace_level = HIDH_INITIAL_TRACE_LEVEL; +#else + hh_cb.trace_level = BT_TRACE_LEVEL_NONE; +#endif + return HID_SUCCESS; +} + +/******************************************************************************* +** +** Function HID_HostInit +** +** Description This function deinitializes the control block +** +** Returns void +** +*******************************************************************************/ +void HID_HostDeinit (void) +{ +#if (HID_DYNAMIC_MEMORY) + if (hidh_cb_ptr) { + osi_free(hidh_cb_ptr); + hidh_cb_ptr = NULL; + } +#endif /* #if (HID_DYNAMIC_MEMORY) */ +} + +/******************************************************************************* +** +** Function HID_HostSetTraceLevel +** +** Description This function sets the trace level for HID Host. If called with +** a value of 0xFF, it simply reads the current trace level. +** +** Returns the new (current) trace level +** +*******************************************************************************/ +UINT8 HID_HostSetTraceLevel (UINT8 new_level) +{ + if (new_level != 0xFF) { + hh_cb.trace_level = new_level; + } + + return (hh_cb.trace_level); +} + +/******************************************************************************* +** +** Function HID_HostRegister +** +** Description This function registers HID-Host with lower layers +** +** Returns tHID_STATUS +** +*******************************************************************************/ +tHID_STATUS HID_HostRegister (tHID_HOST_DEV_CALLBACK *dev_cback) +{ + tHID_STATUS st; + + if ( hh_cb.reg_flag ) { + return HID_ERR_ALREADY_REGISTERED; + } + + if ( dev_cback == NULL ) { + return HID_ERR_INVALID_PARAM; + } + + /* Register with L2CAP */ + if ( (st = hidh_conn_reg()) != HID_SUCCESS ) { + return st; + } + + hh_cb.callback = dev_cback ; + hh_cb.reg_flag = TRUE; + + return (HID_SUCCESS); +} + +/******************************************************************************* +** +** Function HID_HostDeregister +** +** Description This function is called when the host is about power down. +** +** Returns tHID_STATUS +** +*******************************************************************************/ +tHID_STATUS HID_HostDeregister(void) +{ + UINT8 i; + + if ( !hh_cb.reg_flag ) { + return (HID_ERR_NOT_REGISTERED); + } + + for ( i = 0; i < HID_HOST_MAX_DEVICES; i++ ) { + HID_HostRemoveDev( i ) ; + } + + hidh_conn_dereg(); + hh_cb.reg_flag = FALSE; + + return (HID_SUCCESS) ; +} + +/******************************************************************************* +** +** Function HID_HostAddDev +** +** Description This is called so HID-host may manage this device. +** +** Returns tHID_STATUS +** +*******************************************************************************/ +tHID_STATUS HID_HostAddDev ( BD_ADDR addr, UINT16 attr_mask, UINT8 *handle ) +{ + int i; + /* Find an entry for this device in hh_cb.devices array */ + if ( !hh_cb.reg_flag ) { + return (HID_ERR_NOT_REGISTERED); + } + + for ( i = 0; i < HID_HOST_MAX_DEVICES; i++) { + if ((hh_cb.devices[i].in_use) && + (!memcmp(addr, hh_cb.devices[i].addr, BD_ADDR_LEN))) { + break; + } + } + + if (i == HID_HOST_MAX_DEVICES ) { + for ( i = 0; i < HID_HOST_MAX_DEVICES; i++) { + if ( !hh_cb.devices[i].in_use) { + break; + } + } + } + + if ( i == HID_HOST_MAX_DEVICES ) { + return HID_ERR_NO_RESOURCES; + } + + if (!hh_cb.devices[i].in_use) { + hh_cb.devices[i].in_use = TRUE; + hh_cb.devices[i].delay_remove = FALSE; + memcpy( hh_cb.devices[i].addr, addr, sizeof( BD_ADDR ) ) ; + hh_cb.devices[i].state = HID_DEV_NO_CONN; + hh_cb.devices[i].conn_tries = 0 ; + } + + if (attr_mask != HID_ATTR_MASK_IGNORE) { + hh_cb.devices[i].attr_mask = attr_mask; + } + + *handle = i; + + return (HID_SUCCESS); +} + +/******************************************************************************* +** +** Function HID_HostGetDev +** +** Description This is called so HID-host can find this device. +** +** Returns tHID_STATUS +** +*******************************************************************************/ +tHID_STATUS HID_HostGetDev(BD_ADDR addr, UINT8 *handle) +{ + int i; + /* Find an entry for this device in hh_cb.devices array */ + if (!hh_cb.reg_flag) { + return (HID_ERR_NOT_REGISTERED); + } + + for (i = 0; i < HID_HOST_MAX_DEVICES; i++) { + if ((hh_cb.devices[i].in_use) && (!memcmp(addr, hh_cb.devices[i].addr, BD_ADDR_LEN))) { + break; + } + } + + if (i == HID_HOST_MAX_DEVICES) { + *handle = 0xff; + } else { + *handle = i; + } + return (HID_SUCCESS); +} + +/******************************************************************************* +** +** Function HID_HostRemoveDev +** +** Description This removes the device from list devices that host has to manage. +** +** Returns tHID_STATUS +** +*******************************************************************************/ +tHID_STATUS HID_HostRemoveDev ( UINT8 dev_handle ) +{ + if ( !hh_cb.reg_flag ) { + return (HID_ERR_NOT_REGISTERED); + } + + if ( (dev_handle >= HID_HOST_MAX_DEVICES) || (!hh_cb.devices[dev_handle].in_use) ) { + return HID_ERR_INVALID_PARAM; + } + + HID_HostCloseDev( dev_handle ) ; + + if (hh_cb.devices[dev_handle].conn.conn_state == HID_CONN_STATE_DISCONNECTING_INTR || + hh_cb.devices[dev_handle].conn.conn_state == HID_CONN_STATE_DISCONNECTING_CTRL) { + // delay the remove action, to close the control and the interrupt channel + hh_cb.devices[dev_handle].delay_remove = TRUE; + } else { + HIDH_TRACE_WARNING("%s dev_handle:%d conn_state:%d", __func__, dev_handle, + hh_cb.devices[dev_handle].conn.conn_state); + hh_cb.devices[dev_handle].in_use = FALSE; + hh_cb.devices[dev_handle].conn.conn_state = HID_CONN_STATE_UNUSED; + hh_cb.devices[dev_handle].conn.ctrl_cid = hh_cb.devices[dev_handle].conn.intr_cid = 0; + hh_cb.devices[dev_handle].attr_mask = 0; + } + + return HID_SUCCESS; +} + +/******************************************************************************* +** +** Function HID_HostOpenDev +** +** Description This function is called when the user wants to initiate a +** connection attempt to a device. +** +** Returns void +** +*******************************************************************************/ +tHID_STATUS HID_HostOpenDev ( UINT8 dev_handle ) +{ + if ( !hh_cb.reg_flag ) { + return (HID_ERR_NOT_REGISTERED); + } + + if ( (dev_handle >= HID_HOST_MAX_DEVICES) || (!hh_cb.devices[dev_handle].in_use) ) { + return HID_ERR_INVALID_PARAM; + } + + if ( hh_cb.devices[dev_handle].state != HID_DEV_NO_CONN ) { + return HID_ERR_ALREADY_CONN; + } + + hh_cb.devices[dev_handle].conn_tries = 1; + return hidh_conn_initiate( dev_handle ); +} + +/******************************************************************************* +** +** Function HID_HostWriteDev +** +** Description This function is called when the host has a report to send. +** +** report_id: is only used on GET_REPORT transaction if is specified. +** only valid when it's a non-zero value. +** +** Returns void +** +*******************************************************************************/ +tHID_STATUS HID_HostWriteDev( UINT8 dev_handle, UINT8 t_type, + UINT8 param, UINT16 data, UINT8 report_id, BT_HDR *pbuf ) +{ + tHID_STATUS status = HID_SUCCESS; + + if ( !hh_cb.reg_flag ) { + HIDH_TRACE_ERROR("HID_ERR_NOT_REGISTERED"); + status = HID_ERR_NOT_REGISTERED; + } + + if ( (dev_handle >= HID_HOST_MAX_DEVICES) || (!hh_cb.devices[dev_handle].in_use) ) { + HIDH_TRACE_ERROR("HID_ERR_INVALID_PARAM"); + status = HID_ERR_INVALID_PARAM; + } + + else if ( hh_cb.devices[dev_handle].state != HID_DEV_CONNECTED ) { + HIDH_TRACE_ERROR("HID_ERR_NO_CONNECTION dev_handle %d", dev_handle); + status = HID_ERR_NO_CONNECTION; + } + + if (status != HID_SUCCESS) { + if (pbuf) { + osi_free ((void *)pbuf); + } + } else { + status = hidh_conn_snd_data( dev_handle, t_type, param, data, report_id, pbuf ) ; + } + + return status; +} + +/******************************************************************************* +** +** Function HID_HostCloseDev +** +** Description This function disconnects the device. +** +** Returns void +** +*******************************************************************************/ +tHID_STATUS HID_HostCloseDev( UINT8 dev_handle ) +{ + if ( !hh_cb.reg_flag ) { + return (HID_ERR_NOT_REGISTERED); + } + + if ( (dev_handle >= HID_HOST_MAX_DEVICES) || (!hh_cb.devices[dev_handle].in_use) ) { + return HID_ERR_INVALID_PARAM; + } + + hh_cb.devices[dev_handle].conn_tries = HID_HOST_MAX_CONN_RETRY + 1; + btu_stop_timer( &(hh_cb.devices[dev_handle].conn.timer_entry) ) ; + + if ( hh_cb.devices[dev_handle].state != HID_DEV_CONNECTED ) { + return HID_ERR_NO_CONNECTION; + } + + hh_cb.devices[dev_handle].conn_tries = HID_HOST_MAX_CONN_RETRY + 1; + return hidh_conn_disconnect( dev_handle ); +} + +tHID_STATUS HID_HostSetSecurityLevel( char serv_name[], UINT8 sec_lvl ) +{ + if (!BTM_SetSecurityLevel (FALSE, serv_name, BTM_SEC_SERVICE_HIDH_SEC_CTRL, + sec_lvl, HID_PSM_CONTROL, BTM_SEC_PROTO_HID, HID_SEC_CHN)) { + HIDH_TRACE_ERROR ("Security Registration 1 failed"); + return (HID_ERR_NO_RESOURCES); + } + + if (!BTM_SetSecurityLevel (TRUE, serv_name, BTM_SEC_SERVICE_HIDH_SEC_CTRL, + sec_lvl, HID_PSM_CONTROL, BTM_SEC_PROTO_HID, HID_SEC_CHN)) { + HIDH_TRACE_ERROR ("Security Registration 2 failed"); + return (HID_ERR_NO_RESOURCES); + } + + if (!BTM_SetSecurityLevel (FALSE, serv_name, BTM_SEC_SERVICE_HIDH_NOSEC_CTRL, + BTM_SEC_NONE, HID_PSM_CONTROL, BTM_SEC_PROTO_HID, HID_NOSEC_CHN)) { + HIDH_TRACE_ERROR ("Security Registration 3 failed"); + return (HID_ERR_NO_RESOURCES); + } + + if (!BTM_SetSecurityLevel (TRUE, serv_name, BTM_SEC_SERVICE_HIDH_NOSEC_CTRL, + BTM_SEC_NONE, HID_PSM_CONTROL, BTM_SEC_PROTO_HID, HID_NOSEC_CHN)) { + HIDH_TRACE_ERROR ("Security Registration 4 failed"); + return (HID_ERR_NO_RESOURCES); + } + + if (!BTM_SetSecurityLevel (TRUE, serv_name, BTM_SEC_SERVICE_HIDH_INTR, + BTM_SEC_NONE, HID_PSM_INTERRUPT, BTM_SEC_PROTO_HID, 0)) { + HIDH_TRACE_ERROR ("Security Registration 5 failed"); + return (HID_ERR_NO_RESOURCES); + } + + if (!BTM_SetSecurityLevel (FALSE, serv_name, BTM_SEC_SERVICE_HIDH_INTR, + BTM_SEC_NONE, HID_PSM_INTERRUPT, BTM_SEC_PROTO_HID, 0)) { + HIDH_TRACE_ERROR ("Security Registration 6 failed"); + return (HID_ERR_NO_RESOURCES); + } + + return ( HID_SUCCESS ); +} + +/****************************************************************************** +** +** Function hid_known_hid_device +** +** Description check if this device is of type HID Device +** +** Returns TRUE if device is HID Device else FALSE +** +*******************************************************************************/ +BOOLEAN hid_known_hid_device (BD_ADDR bd_addr) +{ + UINT8 i; + tBTM_INQ_INFO *p_inq_info = BTM_InqDbRead(bd_addr); + + if ( !hh_cb.reg_flag ) { + return FALSE; + } + + /* First check for class of device , if Inq DB has information about this device*/ + if (p_inq_info != NULL) { + /* Check if remote major device class is of type BTM_COD_MAJOR_PERIPHERAL */ + if ((p_inq_info->results.dev_class[1] & BTM_COD_MAJOR_CLASS_MASK) + == BTM_COD_MAJOR_PERIPHERAL ) { + HIDH_TRACE_DEBUG("hid_known_hid_device:dev found in InqDB & COD matches HID dev"); + return TRUE; + } + } else { + /* Look for this device in security device DB */ + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev (bd_addr); + if ((p_dev_rec != NULL) && + ((p_dev_rec->dev_class[1] & BTM_COD_MAJOR_CLASS_MASK) == BTM_COD_MAJOR_PERIPHERAL )) { + HIDH_TRACE_DEBUG("hid_known_hid_device:dev found in SecDevDB & COD matches HID dev"); + return TRUE; + } + } + + /* Find an entry for this device in hh_cb.devices array */ + for ( i = 0; i < HID_HOST_MAX_DEVICES; i++) { + if ((hh_cb.devices[i].in_use) && + (memcmp(bd_addr, hh_cb.devices[i].addr, BD_ADDR_LEN) == 0)) { + return TRUE; + } + } + /* Check if this device is marked as HID Device in IOP Dev */ + HIDH_TRACE_DEBUG("hid_known_hid_device:remote is not HID device"); + return FALSE; +} + +#endif //HID_HOST_INCLUDED diff --git a/lib/bt/host/bluedroid/stack/hid/hidh_conn.c b/lib/bt/host/bluedroid/stack/hid/hidh_conn.c new file mode 100644 index 00000000..801f087c --- /dev/null +++ b/lib/bt/host/bluedroid/stack/hid/hidh_conn.c @@ -0,0 +1,1044 @@ +/****************************************************************************** + * + * Copyright (C) 2002-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains HID HOST internal definitions + * + ******************************************************************************/ + +#include +#include +#include + + +#include "common/bt_target.h" +#include "osi/allocator.h" +#include "stack/bt_types.h" + +#include "stack/l2cdefs.h" +#include "stack/l2c_api.h" + +#include "stack/btu.h" +#include "stack/btm_api.h" +#include "btm_int.h" + +#include "stack/hiddefs.h" + +#include "stack/hidh_api.h" +#include "hid_int.h" +#include "osi/osi.h" + +#if (HID_HOST_INCLUDED == TRUE) + +static UINT8 find_conn_by_cid (UINT16 cid); +static void hidh_conn_retry (UINT8 dhandle); + +/********************************************************************************/ +/* L O C A L F U N C T I O N P R O T O T Y P E S */ +/********************************************************************************/ +static void hidh_l2cif_connect_ind (BD_ADDR bd_addr, UINT16 l2cap_cid, + UINT16 psm, UINT8 l2cap_id); +static void hidh_l2cif_connect_cfm (UINT16 l2cap_cid, UINT16 result); +static void hidh_l2cif_config_ind (UINT16 l2cap_cid, tL2CAP_CFG_INFO *p_cfg); +static void hidh_l2cif_config_cfm (UINT16 l2cap_cid, tL2CAP_CFG_INFO *p_cfg); +static void hidh_l2cif_disconnect_ind (UINT16 l2cap_cid, BOOLEAN ack_needed); +static void hidh_l2cif_data_ind (UINT16 l2cap_cid, BT_HDR *p_msg); +static void hidh_l2cif_disconnect_cfm (UINT16 l2cap_cid, UINT16 result); +static void hidh_l2cif_cong_ind (UINT16 l2cap_cid, BOOLEAN congested); + +static const tL2CAP_APPL_INFO hst_reg_info = { + hidh_l2cif_connect_ind, + hidh_l2cif_connect_cfm, + NULL, + hidh_l2cif_config_ind, + hidh_l2cif_config_cfm, + hidh_l2cif_disconnect_ind, + hidh_l2cif_disconnect_cfm, + NULL, + hidh_l2cif_data_ind, + hidh_l2cif_cong_ind, + NULL /* tL2CA_TX_COMPLETE_CB */ +}; + +/******************************************************************************* +** +** Function hidh_l2cif_reg +** +** Description This function initializes the SDP unit. +** +** Returns void +** +*******************************************************************************/ +tHID_STATUS hidh_conn_reg (void) +{ + int xx; + + /* Initialize the L2CAP configuration. We only care about MTU and flush */ + memset(&hh_cb.l2cap_cfg, 0, sizeof(tL2CAP_CFG_INFO)); + + hh_cb.l2cap_cfg.mtu_present = TRUE; + hh_cb.l2cap_cfg.mtu = HID_HOST_MTU; + hh_cb.l2cap_cfg.flush_to_present = TRUE; + hh_cb.l2cap_cfg.flush_to = HID_HOST_FLUSH_TO; + + /* Now, register with L2CAP */ + if (!L2CA_Register (HID_PSM_CONTROL, (tL2CAP_APPL_INFO *) &hst_reg_info)) { + HIDH_TRACE_ERROR ("HID-Host Control Registration failed"); + return (HID_ERR_L2CAP_FAILED) ; + } + if (!L2CA_Register (HID_PSM_INTERRUPT, (tL2CAP_APPL_INFO *) &hst_reg_info)) { + L2CA_Deregister( HID_PSM_CONTROL ) ; + HIDH_TRACE_ERROR ("HID-Host Interrupt Registration failed"); + return (HID_ERR_L2CAP_FAILED) ; + } + + for (xx = 0; xx < HID_HOST_MAX_DEVICES; xx++) { + hh_cb.devices[xx].in_use = FALSE ; + hh_cb.devices[xx].delay_remove = FALSE; + hh_cb.devices[xx].conn.conn_state = HID_CONN_STATE_UNUSED; + } + + return (HID_SUCCESS); +} + +/******************************************************************************* +** +** Function hidh_conn_disconnect +** +** Description This function disconnects a connection. +** +** Returns TRUE if disconnect started, FALSE if already disconnected +** +*******************************************************************************/ +tHID_STATUS hidh_conn_disconnect (UINT8 dhandle) +{ + tHID_CONN *p_hcon = &hh_cb.devices[dhandle].conn; + + HIDH_TRACE_EVENT ("HID-Host disconnect"); + + if ((p_hcon->ctrl_cid != 0) || (p_hcon->intr_cid != 0)) { + + /* Set l2cap idle timeout to 0 (so ACL link is disconnected + * immediately after last channel is closed) */ + L2CA_SetIdleTimeoutByBdAddr(hh_cb.devices[dhandle].addr, 0, BT_TRANSPORT_BR_EDR); + /* Disconnect both interrupt and control channels */ + if (p_hcon->intr_cid) { + p_hcon->conn_state = HID_CONN_STATE_DISCONNECTING_INTR; + L2CA_DisconnectReq (p_hcon->intr_cid); + } else if (p_hcon->ctrl_cid) { + p_hcon->conn_state = HID_CONN_STATE_DISCONNECTING_CTRL; + L2CA_DisconnectReq (p_hcon->ctrl_cid); + } + } else { + p_hcon->conn_state = HID_CONN_STATE_UNUSED; + } + + return (HID_SUCCESS); +} + +/******************************************************************************* +** +** Function hidh_sec_check_complete_term +** +** Description HID security check complete callback function. +** +** Returns Send L2CA_ConnectRsp OK if secutiry check succeed; otherwise +** send security block L2C connection response. +** +*******************************************************************************/ +void hidh_sec_check_complete_term (BD_ADDR bd_addr, tBT_TRANSPORT transport, void *p_ref_data, UINT8 res) +{ + tHID_HOST_DEV_CTB *p_dev = (tHID_HOST_DEV_CTB *) p_ref_data; + UNUSED(bd_addr); + UNUSED (transport); + + if ( res == BTM_SUCCESS && p_dev->conn.conn_state == HID_CONN_STATE_SECURITY ) { + p_dev->conn.disc_reason = HID_SUCCESS; /* Authentication passed. Reset disc_reason (from HID_ERR_AUTH_FAILED) */ + + p_dev->conn.conn_state = HID_CONN_STATE_CONNECTING_INTR; + + /* Send response to the L2CAP layer. */ + L2CA_ConnectRsp (p_dev->addr, p_dev->conn.ctrl_id, p_dev->conn.ctrl_cid, L2CAP_CONN_OK, L2CAP_CONN_OK); + + /* Send a Configuration Request. */ + L2CA_ConfigReq (p_dev->conn.ctrl_cid, &hh_cb.l2cap_cfg); + + } + /* security check fail */ + else if (res != BTM_SUCCESS) { + p_dev->conn.disc_reason = HID_ERR_AUTH_FAILED; /* Save reason for disconnecting */ + p_dev->conn.conn_state = HID_CONN_STATE_UNUSED; + L2CA_ConnectRsp (p_dev->addr, p_dev->conn.ctrl_id, p_dev->conn.ctrl_cid, L2CAP_CONN_SECURITY_BLOCK, L2CAP_CONN_OK); + } +} + +/******************************************************************************* +** +** Function hidh_l2cif_connect_ind +** +** Description This function handles an inbound connection indication +** from L2CAP. This is the case where we are acting as a +** server. +** +** Returns void +** +*******************************************************************************/ +static void hidh_l2cif_connect_ind (BD_ADDR bd_addr, UINT16 l2cap_cid, UINT16 psm, UINT8 l2cap_id) +{ + tHID_CONN *p_hcon; + BOOLEAN bAccept = TRUE; + UINT8 i = HID_HOST_MAX_DEVICES; + tHID_HOST_DEV_CTB *p_dev; + + HIDH_TRACE_EVENT ("HID-Host Rcvd L2CAP conn ind, PSM: 0x%04x CID 0x%x", psm, l2cap_cid); + + /* always add incoming connection device into HID database by default */ + if (HID_HostAddDev(bd_addr, HID_SEC_REQUIRED, &i) != HID_SUCCESS) { + L2CA_ConnectRsp (bd_addr, l2cap_id, l2cap_cid, L2CAP_CONN_SECURITY_BLOCK, 0); + return; + } + + p_hcon = &hh_cb.devices[i].conn; + p_dev = &hh_cb.devices[i]; + + /* Check we are in the correct state for this */ + if (psm == HID_PSM_INTERRUPT) { + if (p_hcon->ctrl_cid == 0) { + HIDH_TRACE_WARNING ("HID-Host Rcvd INTR L2CAP conn ind, but no CTL channel"); + bAccept = FALSE; + } + if (p_hcon->conn_state != HID_CONN_STATE_CONNECTING_INTR) { + HIDH_TRACE_WARNING ("HID-Host Rcvd INTR L2CAP conn ind, wrong state: %d", + p_hcon->conn_state); + bAccept = FALSE; + } + } else { /* CTRL channel */ +#if defined(HID_HOST_ACPT_NEW_CONN) && (HID_HOST_ACPT_NEW_CONN == TRUE) + p_hcon->ctrl_cid = p_hcon->intr_cid = 0; + p_hcon->conn_state = HID_CONN_STATE_UNUSED; +#else + if (p_hcon->conn_state != HID_CONN_STATE_UNUSED) { + HIDH_TRACE_WARNING ("HID-Host - Rcvd CTL L2CAP conn ind, wrong state: %d", + p_hcon->conn_state); + bAccept = FALSE; + } +#endif + } + + if (!bAccept) { + L2CA_ConnectRsp (bd_addr, l2cap_id, l2cap_cid, L2CAP_CONN_NO_RESOURCES, 0); + return; + } + + if (psm == HID_PSM_CONTROL) { + p_hcon->conn_flags = 0; + p_hcon->ctrl_cid = l2cap_cid; + p_hcon->ctrl_id = l2cap_id; + p_hcon->disc_reason = HID_L2CAP_CONN_FAIL; /* In case disconnection occurs before security is completed, then set CLOSE_EVT reason code to 'connection failure' */ + + p_hcon->conn_state = HID_CONN_STATE_SECURITY; + if (btm_sec_mx_access_request (p_dev->addr, HID_PSM_CONTROL, + FALSE, BTM_SEC_PROTO_HID, + (p_dev->attr_mask & HID_SEC_REQUIRED) ? HID_SEC_CHN : HID_NOSEC_CHN, + &hidh_sec_check_complete_term, p_dev) == BTM_CMD_STARTED) { + L2CA_ConnectRsp (bd_addr, l2cap_id, l2cap_cid, L2CAP_CONN_PENDING, L2CAP_CONN_OK); + } + + return; + } + + /* Transition to the next appropriate state, configuration */ + p_hcon->conn_state = HID_CONN_STATE_CONFIG; + p_hcon->intr_cid = l2cap_cid; + + /* Send response to the L2CAP layer. */ + L2CA_ConnectRsp (bd_addr, l2cap_id, l2cap_cid, L2CAP_CONN_OK, L2CAP_CONN_OK); + + /* Send a Configuration Request. */ + L2CA_ConfigReq (l2cap_cid, &hh_cb.l2cap_cfg); + + HIDH_TRACE_EVENT ("HID-Host Rcvd L2CAP conn ind, sent config req, PSM: 0x%04x CID 0x%x", + psm, l2cap_cid); +} + +/******************************************************************************* +** +** Function hidh_proc_repage_timeout +** +** Description This function handles timeout (to page device). +** +** Returns void +** +*******************************************************************************/ +void hidh_proc_repage_timeout (TIMER_LIST_ENT *p_tle) +{ + hidh_conn_initiate( (UINT8) p_tle->param ) ; + hh_cb.devices[p_tle->param].conn_tries++; + hh_cb.callback( (UINT8) p_tle->param, hh_cb.devices[p_tle->param].addr, + HID_HDEV_EVT_RETRYING, hh_cb.devices[p_tle->param].conn_tries, NULL ) ; +} + +/******************************************************************************* +** +** Function hidh_sec_check_complete_orig +** +** Description This function checks to see if security procedures are being +** carried out or not.. +** +** Returns void +** +*******************************************************************************/ +void hidh_sec_check_complete_orig (BD_ADDR bd_addr, tBT_TRANSPORT transport, void *p_ref_data, UINT8 res) +{ + tHID_HOST_DEV_CTB *p_dev = (tHID_HOST_DEV_CTB *) p_ref_data; + UINT8 dhandle; + UNUSED(bd_addr); + UNUSED (transport); + + dhandle = ((UINT32)p_dev - (UINT32) & (hh_cb.devices[0])) / sizeof(tHID_HOST_DEV_CTB); + if ( res == BTM_SUCCESS && p_dev->conn.conn_state == HID_CONN_STATE_SECURITY ) { + HIDH_TRACE_EVENT ("HID-Host Originator security pass."); + p_dev->conn.disc_reason = HID_SUCCESS; /* Authentication passed. Reset disc_reason (from HID_ERR_AUTH_FAILED) */ + + /* Transition to the next appropriate state, configuration */ + p_dev->conn.conn_state = HID_CONN_STATE_CONFIG; + L2CA_ConfigReq (p_dev->conn.ctrl_cid, &hh_cb.l2cap_cfg); + HIDH_TRACE_EVENT ("HID-Host Got Control conn cnf, sent cfg req, CID: 0x%x", p_dev->conn.ctrl_cid); + + } + + if ( res != BTM_SUCCESS && p_dev->conn.conn_state == HID_CONN_STATE_SECURITY ) { +#if (HID_HOST_MAX_CONN_RETRY > 0) + if ( res == BTM_DEVICE_TIMEOUT ) { + if ( p_dev->conn_tries <= HID_HOST_MAX_CONN_RETRY ) { + hidh_conn_retry (dhandle); + return; + } + } +#endif + p_dev->conn.disc_reason = HID_ERR_AUTH_FAILED; /* Save reason for disconnecting */ + hidh_conn_disconnect(dhandle); + } + +} + +/******************************************************************************* +** +** Function hidh_l2cif_connect_cfm +** +** Description This function handles the connect confirm events +** from L2CAP. This is the case when we are acting as a +** client and have sent a connect request. +** +** Returns void +** +*******************************************************************************/ +static void hidh_l2cif_connect_cfm (UINT16 l2cap_cid, UINT16 result) +{ + UINT8 dhandle; + tHID_CONN *p_hcon = NULL; + UINT32 reason; + tHID_HOST_DEV_CTB *p_dev = NULL; + + /* Find CCB based on CID, and verify we are in a state to accept this message */ + if ( (dhandle = find_conn_by_cid(l2cap_cid)) < HID_HOST_MAX_DEVICES ) { + p_dev = &hh_cb.devices[dhandle]; + p_hcon = &hh_cb.devices[dhandle].conn; + } + + if ((p_hcon == NULL) || (!(p_hcon->conn_flags & HID_CONN_FLAGS_IS_ORIG)) || + ((l2cap_cid == p_hcon->ctrl_cid) && (p_hcon->conn_state != HID_CONN_STATE_CONNECTING_CTRL) && + (p_hcon->conn_state != HID_CONN_STATE_DISCONNECTING_INTR)) || + ((l2cap_cid == p_hcon->intr_cid) && (p_hcon->conn_state != HID_CONN_STATE_CONNECTING_INTR) && + (p_hcon->conn_state != HID_CONN_STATE_DISCONNECTING_CTRL))) { + HIDH_TRACE_WARNING("HID-Host Rcvd unexpected conn cnf, CID 0x%x ", l2cap_cid); + return; + } + + if (result != L2CAP_CONN_OK) { + if (l2cap_cid == p_hcon->ctrl_cid) { + p_hcon->ctrl_cid = 0; + } else { + p_hcon->intr_cid = 0; + } + + hidh_conn_disconnect(dhandle); + +#if (HID_HOST_MAX_CONN_RETRY > 0) + if ( (hh_cb.devices[dhandle].conn_tries <= HID_HOST_MAX_CONN_RETRY) && + (result == HCI_ERR_CONNECTION_TOUT || result == HCI_ERR_UNSPECIFIED || + result == HCI_ERR_PAGE_TIMEOUT) ) { + hidh_conn_retry(dhandle); + } else +#endif + { + reason = HID_L2CAP_CONN_FAIL | (UINT32) result ; + hh_cb.callback( dhandle, hh_cb.devices[dhandle].addr, HID_HDEV_EVT_CLOSE, reason, NULL ) ; + } + return; + } + /* receive Control Channel connect confirmation */ + if (l2cap_cid == p_hcon->ctrl_cid) { + /* check security requirement */ + p_hcon->conn_state = HID_CONN_STATE_SECURITY; + p_hcon->disc_reason = HID_L2CAP_CONN_FAIL; /* In case disconnection occurs before security is completed, then set CLOSE_EVT reason code to "connection failure" */ + + btm_sec_mx_access_request (p_dev->addr, HID_PSM_CONTROL, + TRUE, BTM_SEC_PROTO_HID, + (p_dev->attr_mask & HID_SEC_REQUIRED) ? HID_SEC_CHN : HID_NOSEC_CHN, + &hidh_sec_check_complete_orig, p_dev); + } else { + p_hcon->conn_state = HID_CONN_STATE_CONFIG; + /* Send a Configuration Request. */ + L2CA_ConfigReq (l2cap_cid, &hh_cb.l2cap_cfg); + HIDH_TRACE_EVENT ("HID-Host got Interrupt conn cnf, sent cfg req, CID: 0x%x", l2cap_cid); + } + + return; +} + +/******************************************************************************* +** +** Function hidh_l2cif_config_ind +** +** Description This function processes the L2CAP configuration indication +** event. +** +** Returns void +** +*******************************************************************************/ +static void hidh_l2cif_config_ind (UINT16 l2cap_cid, tL2CAP_CFG_INFO *p_cfg) +{ + UINT8 dhandle; + tHID_CONN *p_hcon = NULL; + UINT32 reason; + + /* Find CCB based on CID */ + if ( (dhandle = find_conn_by_cid(l2cap_cid)) < HID_HOST_MAX_DEVICES ) { + p_hcon = &hh_cb.devices[dhandle].conn; + } + + if (p_hcon == NULL) { + HIDH_TRACE_WARNING ("HID-Host Rcvd L2CAP cfg ind, unknown CID: 0x%x", l2cap_cid); + return; + } + + HIDH_TRACE_EVENT ("HID-Host Rcvd cfg ind, sent cfg cfm, CID: 0x%x", l2cap_cid); + + /* Remember the remote MTU size */ + if ((!p_cfg->mtu_present) || (p_cfg->mtu > HID_HOST_MTU)) { + p_hcon->rem_mtu_size = HID_HOST_MTU; + } else { + p_hcon->rem_mtu_size = p_cfg->mtu; + } + + /* For now, always accept configuration from the other side */ + p_cfg->flush_to_present = FALSE; + p_cfg->mtu_present = FALSE; + p_cfg->result = L2CAP_CFG_OK; + + L2CA_ConfigRsp (l2cap_cid, p_cfg); + + if (l2cap_cid == p_hcon->ctrl_cid) { + p_hcon->conn_flags |= HID_CONN_FLAGS_HIS_CTRL_CFG_DONE; + if ((p_hcon->conn_flags & HID_CONN_FLAGS_IS_ORIG) && + (p_hcon->conn_flags & HID_CONN_FLAGS_MY_CTRL_CFG_DONE)) { + /* Connect interrupt channel */ + p_hcon->disc_reason = HID_L2CAP_CONN_FAIL; /* Reset initial reason for CLOSE_EVT: Connection Attempt was made but failed */ + if ((p_hcon->intr_cid = L2CA_ConnectReq (HID_PSM_INTERRUPT, hh_cb.devices[dhandle].addr)) == 0) { + HIDH_TRACE_WARNING ("HID-Host INTR Originate failed"); + reason = HID_L2CAP_REQ_FAIL ; + p_hcon->conn_state = HID_CONN_STATE_UNUSED; + hidh_conn_disconnect (dhandle); + hh_cb.callback( dhandle, hh_cb.devices[dhandle].addr, HID_HDEV_EVT_CLOSE, reason, NULL ) ; + return; + } else { + /* Transition to the next appropriate state, waiting for connection confirm on interrupt channel. */ + p_hcon->conn_state = HID_CONN_STATE_CONNECTING_INTR; + } + } + } else { + p_hcon->conn_flags |= HID_CONN_FLAGS_HIS_INTR_CFG_DONE; + } + + /* If all configuration is complete, change state and tell management we are up */ + if (((p_hcon->conn_flags & HID_CONN_FLAGS_ALL_CONFIGURED) == HID_CONN_FLAGS_ALL_CONFIGURED) + && (p_hcon->conn_state == HID_CONN_STATE_CONFIG)) { + p_hcon->conn_state = HID_CONN_STATE_CONNECTED; + /* Reset disconnect reason to success, as connection successful */ + p_hcon->disc_reason = HID_SUCCESS; + + hh_cb.devices[dhandle].state = HID_DEV_CONNECTED; + hh_cb.callback( dhandle, hh_cb.devices[dhandle].addr, HID_HDEV_EVT_OPEN, 0, NULL ) ; + } +} + + +/******************************************************************************* +** +** Function hidh_l2cif_config_cfm +** +** Description This function processes the L2CAP configuration confirmation +** event. +** +** Returns void +** +*******************************************************************************/ +static void hidh_l2cif_config_cfm (UINT16 l2cap_cid, tL2CAP_CFG_INFO *p_cfg) +{ + UINT8 dhandle; + tHID_CONN *p_hcon = NULL; + UINT32 reason; + + HIDH_TRACE_EVENT ("HID-Host Rcvd cfg cfm, CID: 0x%x Result: %d", l2cap_cid, p_cfg->result); + + /* Find CCB based on CID */ + if ( (dhandle = find_conn_by_cid(l2cap_cid)) < HID_HOST_MAX_DEVICES ) { + p_hcon = &hh_cb.devices[dhandle].conn; + } + + if (p_hcon == NULL) { + HIDH_TRACE_WARNING ("HID-Host Rcvd L2CAP cfg ind, unknown CID: 0x%x", l2cap_cid); + return; + } + + /* If configuration failed, disconnect the channel(s) */ + if (p_cfg->result != L2CAP_CFG_OK) { + hidh_conn_disconnect (dhandle); + reason = HID_L2CAP_CFG_FAIL | (UINT32) p_cfg->result ; + hh_cb.callback( dhandle, hh_cb.devices[dhandle].addr, HID_HDEV_EVT_CLOSE, reason, NULL ) ; + return; + } + + if (l2cap_cid == p_hcon->ctrl_cid) { + p_hcon->conn_flags |= HID_CONN_FLAGS_MY_CTRL_CFG_DONE; + if ((p_hcon->conn_flags & HID_CONN_FLAGS_IS_ORIG) && + (p_hcon->conn_flags & HID_CONN_FLAGS_HIS_CTRL_CFG_DONE)) { + /* Connect interrupt channel */ + p_hcon->disc_reason = HID_L2CAP_CONN_FAIL; /* Reset initial reason for CLOSE_EVT: Connection Attempt was made but failed */ + if ((p_hcon->intr_cid = L2CA_ConnectReq (HID_PSM_INTERRUPT, hh_cb.devices[dhandle].addr)) == 0) { + HIDH_TRACE_WARNING ("HID-Host INTR Originate failed"); + reason = HID_L2CAP_REQ_FAIL ; + p_hcon->conn_state = HID_CONN_STATE_UNUSED; + hidh_conn_disconnect (dhandle); + hh_cb.callback( dhandle, hh_cb.devices[dhandle].addr, HID_HDEV_EVT_CLOSE, reason, NULL ) ; + return; + } else { + /* Transition to the next appropriate state, waiting for connection confirm on interrupt channel. */ + p_hcon->conn_state = HID_CONN_STATE_CONNECTING_INTR; + } + } + } else { + p_hcon->conn_flags |= HID_CONN_FLAGS_MY_INTR_CFG_DONE; + } + + /* If all configuration is complete, change state and tell management we are up */ + if (((p_hcon->conn_flags & HID_CONN_FLAGS_ALL_CONFIGURED) == HID_CONN_FLAGS_ALL_CONFIGURED) + && (p_hcon->conn_state == HID_CONN_STATE_CONFIG)) { + p_hcon->conn_state = HID_CONN_STATE_CONNECTED; + /* Reset disconnect reason to success, as connection successful */ + p_hcon->disc_reason = HID_SUCCESS; + + hh_cb.devices[dhandle].state = HID_DEV_CONNECTED; + hh_cb.callback( dhandle, hh_cb.devices[dhandle].addr, HID_HDEV_EVT_OPEN, 0, NULL ) ; + } +} + + +/******************************************************************************* +** +** Function hidh_l2cif_disconnect_ind +** +** Description This function handles a disconnect event from L2CAP. If +** requested to, we ack the disconnect before dropping the CCB +** +** Returns void +** +*******************************************************************************/ +static void hidh_l2cif_disconnect_ind (UINT16 l2cap_cid, BOOLEAN ack_needed) +{ + UINT8 dhandle; + tHID_CONN *p_hcon = NULL; + UINT16 disc_res = HCI_SUCCESS; + UINT16 hid_close_evt_reason; + + /* Find CCB based on CID */ + if ( (dhandle = find_conn_by_cid(l2cap_cid)) < HID_HOST_MAX_DEVICES ) { + p_hcon = &hh_cb.devices[dhandle].conn; + } + + if (p_hcon == NULL) { + HIDH_TRACE_WARNING ("HID-Host Rcvd L2CAP disc, unknown CID: 0x%x", l2cap_cid); + return; + } + + if (ack_needed) { + L2CA_DisconnectRsp (l2cap_cid); + } + + HIDH_TRACE_EVENT ("HID-Host Rcvd L2CAP disc, CID: 0x%x", l2cap_cid); + + if (l2cap_cid == p_hcon->ctrl_cid) { + p_hcon->ctrl_cid = 0; + p_hcon->conn_state = HID_CONN_STATE_DISCONNECTING_CTRL; + } else { + p_hcon->intr_cid = 0; + p_hcon->conn_state = HID_CONN_STATE_DISCONNECTING_INTR; + } + + if ((p_hcon->ctrl_cid == 0) && (p_hcon->intr_cid == 0)) { + hh_cb.devices[dhandle].state = HID_DEV_NO_CONN; + p_hcon->conn_state = HID_CONN_STATE_UNUSED; + + if ( !ack_needed ) { + disc_res = btm_get_acl_disc_reason_code(); + } + +#if (HID_HOST_MAX_CONN_RETRY > 0) + if ( (disc_res == HCI_ERR_CONNECTION_TOUT || disc_res == HCI_ERR_UNSPECIFIED) && + (!(hh_cb.devices[dhandle].attr_mask & HID_RECONN_INIT)) && + (hh_cb.devices[dhandle].attr_mask & HID_NORMALLY_CONNECTABLE)) { + hh_cb.devices[dhandle].conn_tries = 0; + hh_cb.devices[dhandle].conn.timer_entry.param = (UINT32) dhandle; + btu_start_timer (&(hh_cb.devices[dhandle].conn.timer_entry), BTU_TTYPE_HID_HOST_REPAGE_TO, HID_HOST_REPAGE_WIN); + hh_cb.callback( dhandle, hh_cb.devices[dhandle].addr, HID_HDEV_EVT_CLOSE, disc_res, NULL); + } else +#endif + { + /* Set reason code for HID_HDEV_EVT_CLOSE */ + hid_close_evt_reason = p_hcon->disc_reason; + + /* If we got baseband sent HCI_DISCONNECT_COMPLETE_EVT due to security failure, then set reason to HID_ERR_AUTH_FAILED */ + if ((disc_res == HCI_ERR_AUTH_FAILURE) || + (disc_res == HCI_ERR_KEY_MISSING) || + (disc_res == HCI_ERR_HOST_REJECT_SECURITY) || + (disc_res == HCI_ERR_PAIRING_NOT_ALLOWED) || + (disc_res == HCI_ERR_UNIT_KEY_USED) || + (disc_res == HCI_ERR_PAIRING_WITH_UNIT_KEY_NOT_SUPPORTED) || + (disc_res == HCI_ERR_ENCRY_MODE_NOT_ACCEPTABLE) || + (disc_res == HCI_ERR_REPEATED_ATTEMPTS)) { + hid_close_evt_reason = HID_ERR_AUTH_FAILED; + } + + hh_cb.callback( dhandle, hh_cb.devices[dhandle].addr, HID_HDEV_EVT_CLOSE, hid_close_evt_reason, NULL ) ; + } + } +} + + +/******************************************************************************* +** +** Function hidh_l2cif_disconnect_cfm +** +** Description This function handles a disconnect confirm event from L2CAP. +** +** Returns void +** +*******************************************************************************/ +static void hidh_l2cif_disconnect_cfm (UINT16 l2cap_cid, UINT16 result) +{ + UINT8 dhandle; + tHID_CONN *p_hcon = NULL; + UNUSED(result); + + /* Find CCB based on CID */ + if ( (dhandle = find_conn_by_cid(l2cap_cid)) < HID_HOST_MAX_DEVICES ) { + p_hcon = &hh_cb.devices[dhandle].conn; + } + + if (p_hcon == NULL) { + HIDH_TRACE_WARNING ("HID-Host Rcvd L2CAP disc cfm, unknown CID: 0x%x", l2cap_cid); + return; + } + + HIDH_TRACE_EVENT ("HID-Host Rcvd L2CAP disc cfm, CID: 0x%x", l2cap_cid); + + if (l2cap_cid == p_hcon->ctrl_cid) { + p_hcon->ctrl_cid = 0; + } else { + p_hcon->intr_cid = 0; + if (p_hcon->ctrl_cid) { + HIDH_TRACE_EVENT ("HID-Host Initiating L2CAP Ctrl disconnection"); + L2CA_DisconnectReq (p_hcon->ctrl_cid); + } + } + + if ((p_hcon->ctrl_cid == 0) && (p_hcon->intr_cid == 0)) { + hh_cb.devices[dhandle].state = HID_DEV_NO_CONN; + p_hcon->conn_state = HID_CONN_STATE_UNUSED; + // removes the device from list devices that host has to manage + if (hh_cb.devices[dhandle].delay_remove) { + hh_cb.devices[dhandle].in_use = FALSE; + hh_cb.devices[dhandle].delay_remove = FALSE; + hh_cb.devices[dhandle].attr_mask = 0; + } + hh_cb.callback( dhandle, hh_cb.devices[dhandle].addr, HID_HDEV_EVT_CLOSE, p_hcon->disc_reason, NULL ) ; + } +} + + +/******************************************************************************* +** +** Function hidh_l2cif_cong_ind +** +** Description This function handles a congestion status event from L2CAP. +** +** Returns void +** +*******************************************************************************/ +static void hidh_l2cif_cong_ind (UINT16 l2cap_cid, BOOLEAN congested) +{ + UINT8 dhandle; + tHID_CONN *p_hcon = NULL; + + /* Find CCB based on CID */ + if ( (dhandle = find_conn_by_cid(l2cap_cid)) < HID_HOST_MAX_DEVICES ) { + p_hcon = &hh_cb.devices[dhandle].conn; + } + + if (p_hcon == NULL) { + HIDH_TRACE_WARNING ("HID-Host Rcvd L2CAP congestion status, unknown CID: 0x%x", l2cap_cid); + return; + } + + HIDH_TRACE_EVENT ("HID-Host Rcvd L2CAP congestion status, CID: 0x%x Cong: %d", l2cap_cid, congested); + + if (congested) { + p_hcon->conn_flags |= HID_CONN_FLAGS_CONGESTED; + } else { + p_hcon->conn_flags &= ~HID_CONN_FLAGS_CONGESTED; + + } +} + + +/******************************************************************************* +** +** Function hidh_l2cif_data_ind +** +** Description This function is called when data is received from L2CAP. +** if we are the originator of the connection, we are the SDP +** client, and the received message is queued up for the client. +** +** If we are the destination of the connection, we are the SDP +** server, so the message is passed to the server processing +** function. +** +** Returns void +** +*******************************************************************************/ +static void hidh_l2cif_data_ind (UINT16 l2cap_cid, BT_HDR *p_msg) +{ + UINT8 *p_data = (UINT8 *)(p_msg + 1) + p_msg->offset; + UINT8 ttype, param, rep_type, evt; + UINT8 dhandle; + tHID_CONN *p_hcon = NULL; + + HIDH_TRACE_DEBUG ("HID-Host hidh_l2cif_data_ind [l2cap_cid=0x%04x]", l2cap_cid); + + /* Find CCB based on CID */ + if ((dhandle = find_conn_by_cid(l2cap_cid)) < HID_HOST_MAX_DEVICES) { + p_hcon = &hh_cb.devices[dhandle].conn; + } + + if (p_hcon == NULL) { + HIDH_TRACE_WARNING ("HID-Host Rcvd L2CAP data, unknown CID: 0x%x", l2cap_cid); + osi_free (p_msg); + return; + } + + + ttype = HID_GET_TRANS_FROM_HDR(*p_data); + param = HID_GET_PARAM_FROM_HDR(*p_data); + rep_type = param & HID_PAR_REP_TYPE_MASK; + p_data++; + + /* Get rid of the data type */ + p_msg->len--; + p_msg->offset++; + + switch (ttype) { + case HID_TRANS_HANDSHAKE: + hh_cb.callback(dhandle, hh_cb.devices[dhandle].addr, HID_HDEV_EVT_HANDSHAKE, param, NULL); + osi_free (p_msg); + break; + + case HID_TRANS_CONTROL: + switch (param) { + case HID_PAR_CONTROL_VIRTUAL_CABLE_UNPLUG: + hidh_conn_disconnect( dhandle ) ; + /* Device is unplugging from us. Tell USB */ + hh_cb.callback(dhandle, hh_cb.devices[dhandle].addr, HID_HDEV_EVT_VC_UNPLUG, 0, NULL); + break; + + default: + break; + } + osi_free (p_msg); + break; + + + case HID_TRANS_DATA: + evt = (hh_cb.devices[dhandle].conn.intr_cid == l2cap_cid) ? + HID_HDEV_EVT_INTR_DATA : HID_HDEV_EVT_CTRL_DATA; + hh_cb.callback(dhandle, hh_cb.devices[dhandle].addr, evt, rep_type, p_msg); + break; + + case HID_TRANS_DATAC: + evt = (hh_cb.devices[dhandle].conn.intr_cid == l2cap_cid) ? + HID_HDEV_EVT_INTR_DATC : HID_HDEV_EVT_CTRL_DATC; + hh_cb.callback(dhandle, hh_cb.devices[dhandle].addr, evt, rep_type, p_msg); + break; + + default: + osi_free (p_msg); + break; + } + +} + +/******************************************************************************* +** +** Function hidh_conn_snd_data +** +** Description This function is sends out data. +** +** Returns tHID_STATUS +** +*******************************************************************************/ +tHID_STATUS hidh_conn_snd_data (UINT8 dhandle, UINT8 trans_type, UINT8 param, + UINT16 data, UINT8 report_id, BT_HDR *buf) +{ + tHID_CONN *p_hcon = &hh_cb.devices[dhandle].conn; + BT_HDR *p_buf; + UINT8 *p_out; + UINT16 bytes_copied; + BOOLEAN seg_req = FALSE; + UINT16 data_size; + UINT16 cid; + UINT16 buf_size; + UINT8 use_data = 0 ; + BOOLEAN blank_datc = FALSE; + + if (!BTM_IsAclConnectionUp(hh_cb.devices[dhandle].addr, BT_TRANSPORT_BR_EDR)) { + if (buf) { + osi_free ((void *)buf); + } + return ( HID_ERR_NO_CONNECTION ); + } + + if (p_hcon->conn_flags & HID_CONN_FLAGS_CONGESTED) { + if (buf) { + osi_free ((void *)buf); + } + return ( HID_ERR_CONGESTED ); + } + + switch ( trans_type ) { + case HID_TRANS_CONTROL: + case HID_TRANS_GET_REPORT: + case HID_TRANS_SET_REPORT: + case HID_TRANS_GET_PROTOCOL: + case HID_TRANS_SET_PROTOCOL: + case HID_TRANS_GET_IDLE: + case HID_TRANS_SET_IDLE: + cid = p_hcon->ctrl_cid; + buf_size = HID_CONTROL_BUF_SIZE; + break; + case HID_TRANS_DATA: + cid = p_hcon->intr_cid; + buf_size = HID_INTERRUPT_BUF_SIZE; + break; + default: + return (HID_ERR_INVALID_PARAM) ; + } + + if ( trans_type == HID_TRANS_SET_IDLE ) { + use_data = 1; + } else if ( (trans_type == HID_TRANS_GET_REPORT) && (param & 0x08) ) { + use_data = 2; + } + + do { + if ( buf == NULL || blank_datc ) { + if ((p_buf = (BT_HDR *)osi_malloc(buf_size)) == NULL) { + return (HID_ERR_NO_RESOURCES); + } + + p_buf->offset = L2CAP_MIN_OFFSET; + seg_req = FALSE; + data_size = 0; + bytes_copied = 0; + blank_datc = FALSE; + } else if ( (buf->len > (p_hcon->rem_mtu_size - 1))) { + if ((p_buf = (BT_HDR *)osi_malloc(buf_size)) == NULL) { + return (HID_ERR_NO_RESOURCES); + } + + p_buf->offset = L2CAP_MIN_OFFSET; + seg_req = TRUE; + data_size = buf->len; + bytes_copied = p_hcon->rem_mtu_size - 1; + } else { + p_buf = buf ; + p_buf->offset -= 1; + seg_req = FALSE; + data_size = buf->len; + bytes_copied = buf->len; + } + + p_out = (UINT8 *)(p_buf + 1) + p_buf->offset; + *p_out++ = HID_BUILD_HDR(trans_type, param); + + /* If report ID required for this device */ + if ( (trans_type == HID_TRANS_GET_REPORT) && (report_id != 0) ) { + *p_out = report_id; + data_size = bytes_copied = 1; + } + + + if (seg_req) { + memcpy (p_out, (((UINT8 *)(buf + 1)) + buf->offset), bytes_copied); + buf->offset += bytes_copied; + buf->len -= bytes_copied; + } else if ( use_data == 1) { + *(p_out + bytes_copied) = data & 0xff; + } else if ( use_data == 2 ) { + *(p_out + bytes_copied) = data & 0xff; + *(p_out + bytes_copied + 1) = (data >> 8) & 0xff ; + } + + p_buf->len = bytes_copied + 1 + use_data; + data_size -= bytes_copied; + + /* Send the buffer through L2CAP */ + if (L2CA_DataWrite(cid, p_buf) == L2CAP_DW_FAILED) { + return (HID_ERR_CONGESTED); + } + + if (data_size) { + trans_type = HID_TRANS_DATAC; + } else if ( bytes_copied == (p_hcon->rem_mtu_size - 1) ) { + trans_type = HID_TRANS_DATAC; + blank_datc = TRUE; + } + + } while ((data_size != 0) || blank_datc ) ; + + return (HID_SUCCESS); +} +/******************************************************************************* +** +** Function hidh_conn_initiate +** +** Description This function is called by the management to create a connection. +** +** Returns void +** +*******************************************************************************/ +tHID_STATUS hidh_conn_initiate (UINT8 dhandle) +{ + UINT8 service_id = BTM_SEC_SERVICE_HIDH_NOSEC_CTRL; + UINT32 mx_chan_id = HID_NOSEC_CHN; + + tHID_HOST_DEV_CTB *p_dev = &hh_cb.devices[dhandle]; + + if ( p_dev->conn.conn_state != HID_CONN_STATE_UNUSED ) { + return ( HID_ERR_CONN_IN_PROCESS ); + } + + p_dev->conn.ctrl_cid = 0; + p_dev->conn.intr_cid = 0; + p_dev->conn.disc_reason = HID_L2CAP_CONN_FAIL; /* Reset initial reason for CLOSE_EVT: Connection Attempt was made but failed */ + + /* We are the originator of this connection */ + p_dev->conn.conn_flags = HID_CONN_FLAGS_IS_ORIG; + + if (p_dev->attr_mask & HID_SEC_REQUIRED) { + service_id = BTM_SEC_SERVICE_HIDH_SEC_CTRL; + mx_chan_id = HID_SEC_CHN; + } + BTM_SetOutService (p_dev->addr, service_id, mx_chan_id); + + /* Check if L2CAP started the connection process */ + if ((p_dev->conn.ctrl_cid = L2CA_ConnectReq (HID_PSM_CONTROL, p_dev->addr)) == 0) { + HIDH_TRACE_WARNING ("HID-Host Originate failed"); + hh_cb.callback( dhandle, hh_cb.devices[dhandle].addr, HID_HDEV_EVT_CLOSE, + HID_ERR_L2CAP_FAILED, NULL ) ; + } else { + /* Transition to the next appropriate state, waiting for connection confirm on control channel. */ + p_dev->conn.conn_state = HID_CONN_STATE_CONNECTING_CTRL; + } + + return ( HID_SUCCESS ); +} + + +/******************************************************************************* +** +** Function find_conn_by_cid +** +** Description This function finds a connection control block based on CID +** +** Returns address of control block, or NULL if not found +** +*******************************************************************************/ +static UINT8 find_conn_by_cid (UINT16 cid) +{ + UINT8 xx; + + for (xx = 0; xx < HID_HOST_MAX_DEVICES; xx++) { + if ((hh_cb.devices[xx].in_use) && (hh_cb.devices[xx].conn.conn_state != HID_CONN_STATE_UNUSED) + && ((hh_cb.devices[xx].conn.ctrl_cid == cid) || (hh_cb.devices[xx].conn.intr_cid == cid))) { + break; + } + } + + return (xx); +} + +void hidh_conn_dereg( void ) +{ + L2CA_Deregister (HID_PSM_CONTROL); + L2CA_Deregister (HID_PSM_INTERRUPT); +} + +/******************************************************************************* +** +** Function hidh_conn_retry +** +** Description This function is called to retry a failed connection. +** +** Returns void +** +*******************************************************************************/ +static void hidh_conn_retry( UINT8 dhandle ) +{ + tHID_HOST_DEV_CTB *p_dev = &hh_cb.devices[dhandle]; + + p_dev->conn.conn_state = HID_CONN_STATE_UNUSED; + p_dev->conn.timer_entry.param = (UINT32) dhandle; +#if (HID_HOST_REPAGE_WIN > 0) + btu_start_timer (&(p_dev->conn.timer_entry), BTU_TTYPE_HID_HOST_REPAGE_TO, HID_HOST_REPAGE_WIN); +#else + hidh_proc_repage_timeout( &(p_dev->conn.timer_entry) ); +#endif +} + +#endif // HID_HOST_INCLUDED diff --git a/lib/bt/host/bluedroid/stack/hid/include/hid_conn.h b/lib/bt/host/bluedroid/stack/hid/include/hid_conn.h new file mode 100644 index 00000000..38bb5978 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/hid/include/hid_conn.h @@ -0,0 +1,72 @@ +/****************************************************************************** + * + * Copyright (C) 2002-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains HID connection internal definitions + * + ******************************************************************************/ + +#ifndef HID_CONN_H +#define HID_CONN_H + +#include "common/bt_defs.h" + +#if (BT_HID_INCLUDED == TRUE) + +/* Define the HID Connection Block +*/ +typedef struct hid_conn { +#define HID_CONN_STATE_UNUSED (0) +#define HID_CONN_STATE_CONNECTING_CTRL (1) +#define HID_CONN_STATE_CONNECTING_INTR (2) +#define HID_CONN_STATE_CONFIG (3) +#define HID_CONN_STATE_CONNECTED (4) +#define HID_CONN_STATE_DISCONNECTING (5) +#define HID_CONN_STATE_SECURITY (6) +#define HID_CONN_STATE_DISCONNECTING_CTRL (7) +#define HID_CONN_STATE_DISCONNECTING_INTR (8) + + UINT8 conn_state; + +#define HID_CONN_FLAGS_IS_ORIG (0x01) +#define HID_CONN_FLAGS_HIS_CTRL_CFG_DONE (0x02) +#define HID_CONN_FLAGS_MY_CTRL_CFG_DONE (0x04) +#define HID_CONN_FLAGS_HIS_INTR_CFG_DONE (0x08) +#define HID_CONN_FLAGS_MY_INTR_CFG_DONE (0x10) +#define HID_CONN_FLAGS_ALL_CONFIGURED (0x1E) /* All the config done */ +#define HID_CONN_FLAGS_CONGESTED (0x20) +#define HID_CONN_FLAGS_INACTIVE (0x40) + + UINT8 conn_flags; + + UINT8 ctrl_id; + UINT16 ctrl_cid; + UINT16 intr_cid; + UINT16 rem_mtu_size; + UINT16 disc_reason; /* Reason for disconnecting (for HID_HDEV_EVT_CLOSE) */ + TIMER_LIST_ENT timer_entry; +} tHID_CONN; + +#define HID_SEC_CHN 1 +#define HID_NOSEC_CHN 2 +#define HIDD_SEC_CHN 3 +#define HIDD_NOSEC_CHN 4 + +#endif ///BT_HID_INCLUDED == TRUE +#endif diff --git a/lib/bt/host/bluedroid/stack/hid/include/hid_int.h b/lib/bt/host/bluedroid/stack/hid/include/hid_int.h new file mode 100644 index 00000000..b4208966 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/hid/include/hid_int.h @@ -0,0 +1,144 @@ +/****************************************************************************** + * + * Copyright (C) 2016 The Android Open Source Project + * Copyright (C) 2002-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ +/****************************************************************************** + * + * This file contains HID DEVICE internal definitions + * + ******************************************************************************/ +#ifndef HID_INT_H +#define HID_INT_H + +#include "hid_conn.h" +#include "stack/l2c_api.h" +#if (BT_HID_INCLUDED == TRUE) + +#if (HID_HOST_INCLUDED == TRUE) +#include "stack/hidh_api.h" +enum { HID_DEV_NO_CONN, HID_DEV_CONNECTED }; + +typedef struct per_device_ctb { + BOOLEAN in_use; + BOOLEAN delay_remove; + BD_ADDR addr; /* BD-Addr of the host device */ + UINT16 attr_mask; /* 0x01- virtual_cable; 0x02- normally_connectable; 0x03- reconn_initiate; + 0x04- sdp_disable; */ + UINT8 state; /* Device state if in HOST-KNOWN mode */ + UINT8 conn_substate; + UINT8 conn_tries; /* Remembers to the number of connection attempts while CONNECTING */ + + tHID_CONN conn; /* L2CAP channel info */ +} tHID_HOST_DEV_CTB; + +typedef struct host_ctb { + tHID_HOST_DEV_CTB devices[HID_HOST_MAX_DEVICES]; + tHID_HOST_DEV_CALLBACK *callback; /* Application callbacks */ + tL2CAP_CFG_INFO l2cap_cfg; + +#define MAX_SERVICE_DB_SIZE 4000 + + BOOLEAN sdp_busy; + tHID_HOST_SDP_CALLBACK *sdp_cback; + tSDP_DISCOVERY_DB *p_sdp_db; + tHID_DEV_SDP_INFO sdp_rec; + BOOLEAN reg_flag; + UINT8 trace_level; +} tHID_HOST_CTB; + +extern tHID_STATUS hidh_conn_snd_data(UINT8 dhandle, UINT8 trans_type, UINT8 param, \ + UINT16 data, UINT8 rpt_id, BT_HDR *buf); +extern tHID_STATUS hidh_conn_reg (void); +extern void hidh_conn_dereg( void ); +extern tHID_STATUS hidh_conn_disconnect (UINT8 dhandle); +extern tHID_STATUS hidh_conn_initiate (UINT8 dhandle); +extern void hidh_proc_repage_timeout (TIMER_LIST_ENT *p_tle); +#ifdef __cplusplus +extern "C" { +#endif + +/****************************************************************************** + * Main Control Block + ******************************************************************************/ + +#if HID_DYNAMIC_MEMORY == FALSE +extern tHID_HOST_CTB hh_cb; +#else +extern tHID_HOST_CTB *hidh_cb_ptr; +#define hh_cb (*hidh_cb_ptr) +#endif + +#ifdef __cplusplus +} +#endif +#endif /* HID_HOST_INCLUDED == TRUE */ + +#if (HID_DEV_INCLUDED == TRUE) +#include "stack/hidd_api.h" +enum { HIDD_DEV_NO_CONN, HIDD_DEV_CONNECTED }; + +typedef struct device_ctb { + bool in_use; + BD_ADDR addr; + uint8_t state; + tHID_CONN conn; + bool boot_mode; + uint8_t idle_time; +} tHID_DEV_DEV_CTB; + +typedef struct dev_ctb { + tHID_DEV_DEV_CTB device; + tHID_DEV_HOST_CALLBACK *callback; + tL2CAP_CFG_INFO l2cap_cfg; + tL2CAP_CFG_INFO l2cap_intr_cfg; + bool use_in_qos; + FLOW_SPEC in_qos; + bool reg_flag; + uint8_t trace_level; + bool allow_incoming; + BT_HDR *pending_data; + bool pending_vc_unplug; +} tHID_DEV_CTB; + +extern tHID_STATUS hidd_conn_reg(void); +extern void hidd_conn_dereg(void); +extern tHID_STATUS hidd_conn_initiate(void); +extern tHID_STATUS hidd_conn_disconnect(void); +extern tHID_STATUS hidd_conn_send_data(uint8_t channel, uint8_t msg_type, uint8_t param, uint8_t data, uint16_t len, + uint8_t *p_data); +#ifdef __cplusplus +extern "C" { +#endif + +/****************************************************************************** + * Main Control Block + ******************************************************************************/ + +#if HID_DYNAMIC_MEMORY == FALSE +extern tHID_DEV_CTB hd_cb; +#else +extern tHID_DEV_CTB *hidd_cb_ptr; +#define hd_cb (*hidd_cb_ptr) +#endif + +#ifdef __cplusplus +} +#endif +#endif /* HID_DEV_INCLUDED == TRUE */ + +#endif /* BT_HID_INCLUDED == TRUE */ +#endif /* HID_INT_H */ diff --git a/lib/bt/host/bluedroid/stack/include/stack/a2d_api.h b/lib/bt/host/bluedroid/stack/include/stack/a2d_api.h new file mode 100644 index 00000000..980b4fe4 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/include/stack/a2d_api.h @@ -0,0 +1,295 @@ +/****************************************************************************** + * + * Copyright (C) 2000-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * nterface to A2DP Application Programming Interface + * + ******************************************************************************/ +#ifndef A2D_API_H +#define A2D_API_H +#include "common/bt_defs.h" +#include "stack/sdp_api.h" +#if (A2D_INCLUDED == TRUE) +/***************************************************************************** +** constants +*****************************************************************************/ +#define A2D_VERSION 0x0102 +#define A2D_VERSION_1_3 0x0103 +#define A2D_VERSION_1_4 0x0104 + +/* Profile supported features */ +#define A2D_SUPF_PLAYER 0x0001 +#define A2D_SUPF_MIC 0x0002 +#define A2D_SUPF_TUNER 0x0004 +#define A2D_SUPF_MIXER 0x0008 + +#define A2D_SUPF_HEADPHONE 0x0001 +#define A2D_SUPF_SPEAKER 0x0002 +#define A2D_SUPF_RECORDER 0x0004 +#define A2D_SUPF_AMP 0x0008 + +/* AV Media Types */ +#define A2D_MEDIA_TYPE_AUDIO 0x00 /* audio media type + RFA */ +#define A2D_MEDIA_TYPE_VIDEO 0x10 /* video media type + RFA */ +#define A2D_MEDIA_TYPE_MULTI 0x20 /* multimedia media type + RFA */ + +/* AV Media Codec Type (Audio Codec ID) */ +#define A2D_MEDIA_CT_SBC 0x00 /* SBC media codec type */ +#define A2D_MEDIA_CT_M12 0x01 /* MPEG-1, 2 Audio media codec type */ +#define A2D_MEDIA_CT_M24 0x02 /* MPEG-2, 4 AAC media codec type */ +#define A2D_MEDIA_CT_ATRAC 0x04 /* ATRAC family media codec type */ + +#define A2D_SUCCESS 0 /* Success */ +#define A2D_FAIL 0x0A /* Failed */ +#define A2D_BUSY 0x0B /* A2D_FindService is already in progress */ +#define A2D_INVALID_PARAMS 0x0C /* bad parameters */ +#define A2D_WRONG_CODEC 0x0D /* wrong codec info */ +#define A2D_BAD_CODEC_TYPE 0xC1 /* Media Codec Type is not valid */ +#define A2D_NS_CODEC_TYPE 0xC2 /* Media Codec Type is not supported */ +#define A2D_BAD_SAMP_FREQ 0xC3 /* Sampling Frequency is not valid or multiple values have been selected */ +#define A2D_NS_SAMP_FREQ 0xC4 /* Sampling Frequency is not supported */ +#define A2D_BAD_CH_MODE 0xC5 /* Channel Mode is not valid or multiple values have been selected */ +#define A2D_NS_CH_MODE 0xC6 /* Channel Mode is not supported */ +#define A2D_BAD_SUBBANDS 0xC7 /* None or multiple values have been selected for Number of Subbands */ +#define A2D_NS_SUBBANDS 0xC8 /* Number of Subbands is not supported */ +#define A2D_BAD_ALLOC_MTHD 0xC9 /* None or multiple values have been selected for Allocation Method */ +#define A2D_NS_ALLOC_MTHD 0xCA /* Allocation Method is not supported */ +#define A2D_BAD_MIN_BITPOOL 0xCB /* Minimum Bitpool Value is not valid */ +#define A2D_NS_MIN_BITPOOL 0xCC /* Minimum Bitpool Value is not supported */ +#define A2D_BAD_MAX_BITPOOL 0xCD /* Maximum Bitpool Value is not valid */ +#define A2D_NS_MAX_BITPOOL 0xCE /* Maximum Bitpool Value is not supported */ +#define A2D_BAD_LAYER 0xCF /* None or multiple values have been selected for Layer */ +#define A2D_NS_LAYER 0xD0 /* Layer is not supported */ +#define A2D_NS_CRC 0xD1 /* CRC is not supported */ +#define A2D_NS_MPF 0xD2 /* MPF-2 is not supported */ +#define A2D_NS_VBR 0xD3 /* VBR is not supported */ +#define A2D_BAD_BIT_RATE 0xD4 /* None or multiple values have been selected for Bit Rate */ +#define A2D_NS_BIT_RATE 0xD5 /* Bit Rate is not supported */ +#define A2D_BAD_OBJ_TYPE 0xD6 /* Either 1) Object type is not valid (b3-b0) or 2) None or multiple values have been selected for Object Type */ +#define A2D_NS_OBJ_TYPE 0xD7 /* Object type is not supported */ +#define A2D_BAD_CHANNEL 0xD8 /* None or multiple values have been selected for Channels */ +#define A2D_NS_CHANNEL 0xD9 /* Channels is not supported */ +#define A2D_BAD_BLOCK_LEN 0xDD /* None or multiple values have been selected for Block Length */ +#define A2D_BAD_CP_TYPE 0xE0 /* The requested CP Type is not supported. */ +#define A2D_BAD_CP_FORMAT 0xE1 /* The format of Content Protection Service Capability/Content Protection Scheme Dependent Data is not correct. */ + +typedef UINT8 tA2D_STATUS; + +/* the return values from A2D_BitsSet() */ +#define A2D_SET_ONE_BIT 1 /* one and only one bit is set */ +#define A2D_SET_ZERO_BIT 0 /* all bits clear */ +#define A2D_SET_MULTL_BIT 2 /* multiple bits are set */ + +/***************************************************************************** +** type definitions +*****************************************************************************/ + +/* This data type is used in A2D_FindService() to initialize the SDP database + * to hold the result service search. */ +typedef struct { + UINT32 db_len; /* Length, in bytes, of the discovery database */ + UINT16 num_attr;/* The number of attributes in p_attrs */ + tSDP_DISCOVERY_DB *p_db; /* Pointer to the discovery database */ + UINT16 *p_attrs; /* The attributes filter. If NULL, A2DP API sets the attribute filter + * to be ATTR_ID_SERVICE_CLASS_ID_LIST, ATTR_ID_BT_PROFILE_DESC_LIST, + * ATTR_ID_SUPPORTED_FEATURES, ATTR_ID_SERVICE_NAME and ATTR_ID_PROVIDER_NAME. + * If not NULL, the input is taken as the filter. */ +} tA2D_SDP_DB_PARAMS; + +/* This data type is used in tA2D_FIND_CBACK to report the result of the SDP discovery process. */ +typedef struct { + UINT16 service_len; /* Length, in bytes, of the service name */ + UINT16 provider_len; /* Length, in bytes, of the provider name */ + char *p_service_name; /* Pointer the service name. This character string may not be null terminated. + * Use the service_len parameter to safely copy this string */ + char *p_provider_name;/* Pointer the provider name. This character string may not be null terminated. + * Use the provider_len parameter to safely copy this string */ + UINT16 features; /* Profile supported features */ + UINT16 avdt_version; /* AVDTP protocol version */ +} tA2D_Service; + +/* This is the callback to notify the result of the SDP discovery process. */ +typedef void (tA2D_FIND_CBACK)(BOOLEAN found, tA2D_Service *p_service); + + +/***************************************************************************** +** external function declarations +*****************************************************************************/ +#ifdef __cplusplus +extern "C" +{ +#endif +/****************************************************************************** +** +** Function A2D_AddRecord +** +** Description This function is called by a server application to add +** SRC or SNK information to an SDP record. Prior to +** calling this function the application must call +** SDP_CreateRecord() to create an SDP record. +** +** Input Parameters: +** service_uuid: Indicates SRC or SNK. +** +** p_service_name: Pointer to a null-terminated character +** string containing the service name. +** +** p_provider_name: Pointer to a null-terminated character +** string containing the provider name. +** +** features: Profile supported features. +** +** sdp_handle: SDP handle returned by SDP_CreateRecord(). +** +** Output Parameters: +** None. +** +** Returns A2D_SUCCESS if function execution succeeded, +** A2D_INVALID_PARAMS if bad parameters are given. +** A2D_FAIL if function execution failed. +** +******************************************************************************/ +extern tA2D_STATUS A2D_AddRecord(UINT16 service_uuid, char *p_service_name, char *p_provider_name, + UINT16 features, UINT32 sdp_handle); + +/****************************************************************************** +** +** Function A2D_FindService +** +** Description This function is called by a client application to +** perform service discovery and retrieve SRC or SNK SDP +** record information from a server. Information is +** returned for the first service record found on the +** server that matches the service UUID. The callback +** function will be executed when service discovery is +** complete. There can only be one outstanding call to +** A2D_FindService() at a time; the application must wait +** for the callback before it makes another call to +** the function. +** +** Input Parameters: +** service_uuid: Indicates SRC or SNK. +** +** bd_addr: BD address of the peer device. +** +** p_db: Pointer to the information to initialize +** the discovery database. +** +** p_cback: Pointer to the A2D_FindService() +** callback function. +** +** Output Parameters: +** None. +** +** Returns A2D_SUCCESS if function execution succeeded, +** A2D_INVALID_PARAMS if bad parameters are given. +** A2D_BUSY if discovery is already in progress. +** A2D_FAIL if function execution failed. +** +******************************************************************************/ +extern tA2D_STATUS A2D_FindService(UINT16 service_uuid, BD_ADDR bd_addr, + tA2D_SDP_DB_PARAMS *p_db, tA2D_FIND_CBACK *p_cback); + +/****************************************************************************** +** +** Function A2D_SetTraceLevel +** +** Description Sets the trace level for A2D. If 0xff is passed, the +** current trace level is returned. +** +** Input Parameters: +** new_level: The level to set the A2D tracing to: +** 0xff-returns the current setting. +** 0-turns off tracing. +** >= 1-Errors. +** >= 2-Warnings. +** >= 3-APIs. +** >= 4-Events. +** >= 5-Debug. +** +** Returns The new trace level or current trace level if +** the input parameter is 0xff. +** +******************************************************************************/ +extern UINT8 A2D_SetTraceLevel (UINT8 new_level); + +/****************************************************************************** +** Function A2D_BitsSet +** +** Description Check the given num for the number of bits set +** Returns A2D_SET_ONE_BIT, if one and only one bit is set +** A2D_SET_ZERO_BIT, if all bits clear +** A2D_SET_MULTL_BIT, if multiple bits are set +******************************************************************************/ +extern UINT8 A2D_BitsSet(UINT8 num); + +/******************************************************************************* +** +** Function A2D_Init +** +** Description This function is called at stack startup to allocate the +** control block (if using dynamic memory), and initializes the +** control block and tracing level. +** +** Returns void +** +*******************************************************************************/ +extern bt_status_t A2D_Init(void); + +/******************************************************************************* +** +** Function A2D_Deinit +** +** Description This function is called at stack startup to free the +** control block (if using dynamic memory), and free the +** control block and tracing level. +** +** Returns void +** +*******************************************************************************/ +extern void A2D_Deinit(void); + +/******************************************************************************* +** +** Function a2d_set_avdt_sdp_ver +** +** Description Used for change version of avdtp +** +** Returns void +** +*******************************************************************************/ +extern void a2d_set_avdt_sdp_ver(UINT16 avdt_sdp_ver); + +/******************************************************************************* +** +** Function a2d_set_a2dp_sdp_ver +** +** Description Used for change version of a2dp +** +** Returns void +** +*******************************************************************************/ +extern void a2d_set_a2dp_sdp_ver(UINT16 a2dp_sdp_ver); + +#ifdef __cplusplus +} +#endif + +#endif ///A2D_INCLUDED +#endif /* A2D_API_H */ diff --git a/lib/bt/host/bluedroid/stack/include/stack/a2d_sbc.h b/lib/bt/host/bluedroid/stack/include/stack/a2d_sbc.h new file mode 100644 index 00000000..065f264f --- /dev/null +++ b/lib/bt/host/bluedroid/stack/include/stack/a2d_sbc.h @@ -0,0 +1,193 @@ +/****************************************************************************** + * + * Copyright (C) 2000-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * nterface to low complexity subband codec (SBC) + * + ******************************************************************************/ +#ifndef A2D_SBC_H +#define A2D_SBC_H +#if (A2D_INCLUDED == TRUE) +/***************************************************************************** +** Constants +*****************************************************************************/ +/* the length of the SBC Media Payload header. */ +#define A2D_SBC_MPL_HDR_LEN 1 + +/* the LOSC of SBC media codec capabilitiy */ +#define A2D_SBC_INFO_LEN 6 + +/* for Codec Specific Information Element */ +#define A2D_SBC_IE_SAMP_FREQ_MSK 0xF0 /* b7-b4 sampling frequency */ +#define A2D_SBC_IE_SAMP_FREQ_16 0x80 /* b7:16 kHz */ +#define A2D_SBC_IE_SAMP_FREQ_32 0x40 /* b6:32 kHz */ +#define A2D_SBC_IE_SAMP_FREQ_44 0x20 /* b5:44.1kHz */ +#define A2D_SBC_IE_SAMP_FREQ_48 0x10 /* b4:48 kHz */ + +#define A2D_SBC_IE_CH_MD_MSK 0x0F /* b3-b0 channel mode */ +#define A2D_SBC_IE_CH_MD_MONO 0x08 /* b3: mono */ +#define A2D_SBC_IE_CH_MD_DUAL 0x04 /* b2: dual */ +#define A2D_SBC_IE_CH_MD_STEREO 0x02 /* b1: stereo */ +#define A2D_SBC_IE_CH_MD_JOINT 0x01 /* b0: joint stereo */ + +#define A2D_SBC_IE_BLOCKS_MSK 0xF0 /* b7-b4 number of blocks */ +#define A2D_SBC_IE_BLOCKS_4 0x80 /* 4 blocks */ +#define A2D_SBC_IE_BLOCKS_8 0x40 /* 8 blocks */ +#define A2D_SBC_IE_BLOCKS_12 0x20 /* 12blocks */ +#define A2D_SBC_IE_BLOCKS_16 0x10 /* 16blocks */ + +#define A2D_SBC_IE_SUBBAND_MSK 0x0C /* b3-b2 number of subbands */ +#define A2D_SBC_IE_SUBBAND_4 0x08 /* b3: 4 */ +#define A2D_SBC_IE_SUBBAND_8 0x04 /* b2: 8 */ + +#define A2D_SBC_IE_ALLOC_MD_MSK 0x03 /* b1-b0 allocation mode */ +#define A2D_SBC_IE_ALLOC_MD_S 0x02 /* b1: SNR */ +#define A2D_SBC_IE_ALLOC_MD_L 0x01 /* b0: loundess */ + +#define A2D_SBC_IE_MIN_BITPOOL 2 +#define A2D_SBC_IE_MAX_BITPOOL 250 + +/* for media payload header */ +#define A2D_SBC_HDR_F_MSK 0x80 +#define A2D_SBC_HDR_S_MSK 0x40 +#define A2D_SBC_HDR_L_MSK 0x20 +#define A2D_SBC_HDR_NUM_MSK 0x0F + +/***************************************************************************** +** Type Definitions +*****************************************************************************/ + +/* data type for the SBC Codec Information Element*/ +typedef struct { + UINT8 samp_freq; /* Sampling frequency */ + UINT8 ch_mode; /* Channel mode */ + UINT8 block_len; /* Block length */ + UINT8 num_subbands; /* Number of subbands */ + UINT8 alloc_mthd; /* Allocation method */ + UINT8 max_bitpool; /* Maximum bitpool */ + UINT8 min_bitpool; /* Minimum bitpool */ +} tA2D_SBC_CIE; + + +/***************************************************************************** +** External Function Declarations +*****************************************************************************/ +#ifdef __cplusplus +extern "C" +{ +#endif +/****************************************************************************** +** +** Function A2D_BldSbcInfo +** +** Description This function is called by an application to build +** the SBC Media Codec Capabilities byte sequence +** beginning from the LOSC octet. +** Input Parameters: +** media_type: Indicates Audio, or Multimedia. +** +** p_ie: The SBC Codec Information Element information. +** +** Output Parameters: +** p_result: the resulting codec info byte sequence. +** +** Returns A2D_SUCCESS if function execution succeeded. +** Error status code, otherwise. +******************************************************************************/ +extern tA2D_STATUS A2D_BldSbcInfo(UINT8 media_type, tA2D_SBC_CIE *p_ie, + UINT8 *p_result); + +/****************************************************************************** +** +** Function A2D_ParsSbcInfo +** +** Description This function is called by an application to parse +** the SBC Media Codec Capabilities byte sequence +** beginning from the LOSC octet. +** Input Parameters: +** p_info: the byte sequence to parse. +** +** for_caps: TRUE, if the byte sequence is for get capabilities response. +** +** Output Parameters: +** p_ie: The SBC Codec Information Element information. +** +** Returns A2D_SUCCESS if function execution succeeded. +** Error status code, otherwise. +******************************************************************************/ +extern tA2D_STATUS A2D_ParsSbcInfo(tA2D_SBC_CIE *p_ie, UINT8 *p_info, + BOOLEAN for_caps); + +/****************************************************************************** +** +** Function A2D_BldSbcMplHdr +** +** Description This function is called by an application to parse +** the SBC Media Payload header. +** Input Parameters: +** frag: 1, if fragmented. 0, otherwise. +** +** start: 1, if the starting packet of a fragmented frame. +** +** last: 1, if the last packet of a fragmented frame. +** +** num: If frag is 1, this is the number of remaining fragments +** (including this fragment) of this frame. +** If frag is 0, this is the number of frames in this packet. +** +** Output Parameters: +** p_dst: the resulting media payload header byte sequence. +** +** Returns void. +******************************************************************************/ +extern void A2D_BldSbcMplHdr(UINT8 *p_dst, BOOLEAN frag, BOOLEAN start, + BOOLEAN last, UINT8 num); + +/****************************************************************************** +** +** Function A2D_ParsSbcMplHdr +** +** Description This function is called by an application to parse +** the SBC Media Payload header. +** Input Parameters: +** p_src: the byte sequence to parse.. +** +** Output Parameters: +** frag: 1, if fragmented. 0, otherwise. +** +** start: 1, if the starting packet of a fragmented frame. +** +** last: 1, if the last packet of a fragmented frame. +** +** num: If frag is 1, this is the number of remaining fragments +** (including this fragment) of this frame. +** If frag is 0, this is the number of frames in this packet. +** +** Returns void. +******************************************************************************/ +extern void A2D_ParsSbcMplHdr(UINT8 *p_src, BOOLEAN *p_frag, + BOOLEAN *p_start, BOOLEAN *p_last, + UINT8 *p_num); +#ifdef __cplusplus +} +#endif + +#endif ///A2D_INCLUDED == TRUE + +#endif /* A2D_SBC_H */ diff --git a/lib/bt/host/bluedroid/stack/include/stack/acl_hci_link_interface.h b/lib/bt/host/bluedroid/stack/include/stack/acl_hci_link_interface.h new file mode 100644 index 00000000..f62c654f --- /dev/null +++ b/lib/bt/host/bluedroid/stack/include/stack/acl_hci_link_interface.h @@ -0,0 +1,15 @@ +/* + * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ACL_HCI_LINK_INTERFACE_H +#define ACL_HCI_LINK_INTERFACE_H + +#include "bt_common.h" + +void btm_acl_connected(BD_ADDR bda, UINT16 handle, UINT8 link_type, UINT8 enc_mode, UINT8 status); +void btm_acl_disconnected(UINT16 handle, UINT8 reason); + +#endif /* ACL_HCI_LINK_INTERFACE_H */ diff --git a/lib/bt/host/bluedroid/stack/include/stack/avct_api.h b/lib/bt/host/bluedroid/stack/include/stack/avct_api.h new file mode 100644 index 00000000..3d59df66 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/include/stack/avct_api.h @@ -0,0 +1,279 @@ +/****************************************************************************** + * + * Copyright (C) 2003-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This interface file contains the interface to the Audio Video Control + * Transport Protocol (AVCTP). + * + ******************************************************************************/ +#ifndef AVCT_API_H +#define AVCT_API_H + +#include "stack/bt_types.h" +#include "common/bt_target.h" + +/***************************************************************************** +** Constants +*****************************************************************************/ + +/* API function return value result codes. */ +#define AVCT_SUCCESS 0 /* Function successful */ +#define AVCT_NO_RESOURCES 1 /* Not enough resources */ +#define AVCT_BAD_HANDLE 2 /* Bad handle */ +#define AVCT_PID_IN_USE 3 /* PID already in use */ +#define AVCT_NOT_OPEN 4 /* Connection not open */ + +/* PSM for AVCT. */ +#define AVCT_PSM 0x0017 +#define AVCT_BR_PSM 0x001B + +/* Protocol revision numbers */ +#define AVCT_REV_1_0 0x0100 +#define AVCT_REV_1_2 0x0102 +#define AVCT_REV_1_3 0x0103 +#define AVCT_REV_1_4 0x0104 + +/* the layer_specific settings */ +#define AVCT_DATA_CTRL 0x0001 /* for the control channel */ +#define AVCT_DATA_BROWSE 0x0002 /* for the browsing channel */ +#define AVCT_DATA_PARTIAL 0x0100 /* Only have room for a partial message */ + +#define AVCT_MIN_CONTROL_MTU 48 /* Per the AVRC spec, minimum MTU for the control channel */ +#define AVCT_MIN_BROWSE_MTU 335 /* Per the AVRC spec, minimum MTU for the browsing channel */ + +/* Message offset. The number of bytes needed by the protocol stack for the +** protocol headers of an AVCTP message packet. +*/ +#define AVCT_MSG_OFFSET 15 +#define AVCT_BROWSE_OFFSET 17 /* the default offset for browsing channel */ + +/* Connection role. */ +#define AVCT_INT 0 /* Initiator connection */ +#define AVCT_ACP 1 /* Acceptor connection */ + +/* Control role. */ +#define AVCT_TARGET 1 /* target */ +#define AVCT_CONTROL 2 /* controller */ +#define AVCT_PASSIVE 4 /* If conflict, allow the other side to succeed */ + +/* Command/Response indicator. */ +#define AVCT_CMD 0 /* Command message */ +#define AVCT_RSP 2 /* Response message */ +#define AVCT_REJ 3 /* Message rejected */ + +/* Control callback events. */ +#define AVCT_CONNECT_CFM_EVT 0 /* Connection confirm */ +#define AVCT_CONNECT_IND_EVT 1 /* Connection indication */ +#define AVCT_DISCONNECT_CFM_EVT 2 /* Disconnect confirm */ +#define AVCT_DISCONNECT_IND_EVT 3 /* Disconnect indication */ +#define AVCT_CONG_IND_EVT 4 /* Congestion indication */ +#define AVCT_UNCONG_IND_EVT 5 /* Uncongestion indication */ +#define AVCT_BROWSE_CONN_CFM_EVT 6 /* Browse Connection confirm */ +#define AVCT_BROWSE_CONN_IND_EVT 7 /* Browse Connection indication */ +#define AVCT_BROWSE_DISCONN_CFM_EVT 8 /* Browse Disconnect confirm */ +#define AVCT_BROWSE_DISCONN_IND_EVT 9 /* Browse Disconnect indication */ +#define AVCT_BROWSE_CONG_IND_EVT 10 /* Congestion indication */ +#define AVCT_BROWSE_UNCONG_IND_EVT 11 /* Uncongestion indication */ + + +/* General purpose failure result code for callback events. */ +#define AVCT_RESULT_FAIL 5 + +/***************************************************************************** +** Type Definitions +*****************************************************************************/ + +/* Control callback function. */ +typedef void (tAVCT_CTRL_CBACK)(UINT8 handle, UINT8 event, UINT16 result, + BD_ADDR peer_addr); + +/* Message callback function */ +/* p_pkt->layer_specific is AVCT_DATA_CTRL or AVCT_DATA_BROWSE */ +typedef void (tAVCT_MSG_CBACK)(UINT8 handle, UINT8 label, UINT8 cr, + BT_HDR *p_pkt); + +/* Structure used by AVCT_CreateConn. */ +typedef struct { + tAVCT_CTRL_CBACK *p_ctrl_cback; /* Control callback */ + tAVCT_MSG_CBACK *p_msg_cback; /* Message callback */ + UINT16 pid; /* Profile ID */ + UINT8 role; /* Initiator/acceptor role */ + UINT8 control; /* Control role (Control/Target) */ +} tAVCT_CC; + +/***************************************************************************** +** External Function Declarations +*****************************************************************************/ +#ifdef __cplusplus +extern "C" +{ +#endif + +/******************************************************************************* +** +** Function AVCT_Register +** +** Description This is the system level registration function for the +** AVCTP protocol. This function initializes AVCTP and +** prepares the protocol stack for its use. This function +** must be called once by the system or platform using AVCTP +** before the other functions of the API an be used. +** +** +** Returns void +** +*******************************************************************************/ +extern void AVCT_Register(UINT16 mtu, UINT16 mtu_br, UINT8 sec_mask); + +/******************************************************************************* +** +** Function AVCT_Deregister +** +** Description This function is called to deregister use AVCTP protocol. +** It is called when AVCTP is no longer being used by any +** application in the system. Before this function can be +** called, all connections must be removed with +** AVCT_RemoveConn(). +** +** +** Returns void +** +*******************************************************************************/ +extern void AVCT_Deregister(void); + +/******************************************************************************* +** +** Function AVCT_CreateConn +** +** Description Create an AVCTP connection. There are two types of +** connections, initiator and acceptor, as determined by +** the p_cc->role parameter. When this function is called to +** create an initiator connection, an AVCTP connection to +** the peer device is initiated if one does not already exist. +** If an acceptor connection is created, the connection waits +** passively for an incoming AVCTP connection from a peer device. +** +** +** Returns AVCT_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +extern UINT16 AVCT_CreateConn(UINT8 *p_handle, tAVCT_CC *p_cc, + BD_ADDR peer_addr); + +/******************************************************************************* +** +** Function AVCT_RemoveConn +** +** Description Remove an AVCTP connection. This function is called when +** the application is no longer using a connection. If this +** is the last connection to a peer the L2CAP channel for AVCTP +** will be closed. +** +** +** Returns AVCT_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +extern UINT16 AVCT_RemoveConn(UINT8 handle); + +/******************************************************************************* +** +** Function AVCT_CreateBrowse +** +** Description Create an AVCTP connection. There are two types of +** connections, initiator and acceptor, as determined by +** the p_cc->role parameter. When this function is called to +** create an initiator connection, an AVCTP connection to +** the peer device is initiated if one does not already exist. +** If an acceptor connection is created, the connection waits +** passively for an incoming AVCTP connection from a peer device. +** +** +** Returns AVCT_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +extern UINT16 AVCT_CreateBrowse(UINT8 handle, UINT8 role); + +/******************************************************************************* +** +** Function AVCT_RemoveBrowse +** +** Description Remove an AVCTP connection. This function is called when +** the application is no longer using a connection. If this +** is the last connection to a peer the L2CAP channel for AVCTP +** will be closed. +** +** +** Returns AVCT_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +extern UINT16 AVCT_RemoveBrowse(UINT8 handle); + +/******************************************************************************* +** +** Function AVCT_GetBrowseMtu +** +** Description Get the peer_mtu for the AVCTP Browse channel of the given +** connection. +** +** Returns the peer browsing channel MTU. +** +*******************************************************************************/ +extern UINT16 AVCT_GetBrowseMtu (UINT8 handle); + +/******************************************************************************* +** +** Function AVCT_GetPeerMtu +** +** Description Get the peer_mtu for the AVCTP channel of the given +** connection. +** +** Returns the peer MTU size. +** +*******************************************************************************/ +extern UINT16 AVCT_GetPeerMtu (UINT8 handle); + +/******************************************************************************* +** +** Function AVCT_MsgReq +** +** Description Send an AVCTP message to a peer device. In calling +** AVCT_MsgReq(), the application should keep track of the +** congestion state of AVCTP as communicated with events +** AVCT_CONG_IND_EVT and AVCT_UNCONG_IND_EVT. If the +** application calls AVCT_MsgReq() when AVCTP is congested +** the message may be discarded. The application may make its +** first call to AVCT_MsgReq() after it receives an +** AVCT_CONNECT_CFM_EVT or AVCT_CONNECT_IND_EVT on control channel or +** AVCT_BROWSE_CONN_CFM_EVT or AVCT_BROWSE_CONN_IND_EVT on browsing channel. +** +** p_msg->layer_specific must be set to +** AVCT_DATA_CTRL for control channel traffic; +** AVCT_DATA_BROWSE for for browse channel traffic. +** +** Returns AVCT_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +extern UINT16 AVCT_MsgReq(UINT8 handle, UINT8 label, UINT8 cr, BT_HDR *p_msg); + +#ifdef __cplusplus +} +#endif + + +#endif /* AVCT_API_H */ diff --git a/lib/bt/host/bluedroid/stack/include/stack/avdt_api.h b/lib/bt/host/bluedroid/stack/include/stack/avdt_api.h new file mode 100644 index 00000000..42a1f855 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/include/stack/avdt_api.h @@ -0,0 +1,1016 @@ +/****************************************************************************** + * + * Copyright (C) 2002-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This interface file contains the interface to the Audio Video + * Distribution Transport Protocol (AVDTP). + * + ******************************************************************************/ +#ifndef AVDT_API_H +#define AVDT_API_H + +#include "stack/bt_types.h" +#include "common/bt_target.h" + +/***************************************************************************** +** Constants +*****************************************************************************/ +#ifndef AVDT_VERSION +#define AVDT_VERSION 0x0102 +#endif +#define AVDT_VERSION_SYNC 0x0103 + +/* API function return value result codes. */ +#define AVDT_SUCCESS 0 /* Function successful */ +#define AVDT_BAD_PARAMS 1 /* Invalid parameters */ +#define AVDT_NO_RESOURCES 2 /* Not enough resources */ +#define AVDT_BAD_HANDLE 3 /* Bad handle */ +#define AVDT_BUSY 4 /* A procedure is already in progress */ +#define AVDT_WRITE_FAIL 5 /* Write failed */ + +/* The index to access the codec type in codec_info[]. */ +#define AVDT_CODEC_TYPE_INDEX 2 + +/* The size in bytes of a Adaptation Layer header. */ +#define AVDT_AL_HDR_SIZE 3 + +/* The size in bytes of a media packet header. */ +#define AVDT_MEDIA_HDR_SIZE 12 + +/* AVDTP 7.5.3 Adaptation Layer Fragmentation + * original length of the un-fragmented transport packet should be specified by + * two bytes length field of Adaptation Layer Header */ +#define AVDT_MAX_MEDIA_SIZE (0xFFFF - AVDT_MEDIA_HDR_SIZE) + +/* The handle is used when reporting MULTI_AV specific events */ +#define AVDT_MULTI_AV_HANDLE 0xFF + +/* The number of bytes needed by the protocol stack for the protocol headers +** of a media packet. This is the size of the media packet header, the +** L2CAP packet header and HCI header. +*/ +#define AVDT_MEDIA_OFFSET 23 + +/* The marker bit is used by the application to mark significant events such +** as frame boundaries in the data stream. This constant is used to check or +** set the marker bit in the m_pt parameter of an AVDT_WriteReq() +** or AVDT_DATA_IND_EVT. +*/ +#define AVDT_MARKER_SET 0x80 + +/* SEP Type. This indicates the stream endpoint type. */ +#define AVDT_TSEP_SRC 0 /* Source SEP */ +#define AVDT_TSEP_SNK 1 /* Sink SEP */ + +/* initiator/acceptor role for adaption */ +#define AVDT_INT 0 /* initiator */ +#define AVDT_ACP 1 /* acceptor */ + +/* Media Type. This indicates the media type of the stream endpoint. */ +#define AVDT_MEDIA_AUDIO 0 /* Audio SEP */ +#define AVDT_MEDIA_VIDEO 1 /* Video SEP */ +#define AVDT_MEDIA_MULTI 2 /* Multimedia SEP */ + +/* for reporting packets */ +#define AVDT_RTCP_PT_SR 200 /* the packet type - SR (Sender Report) */ +#define AVDT_RTCP_PT_RR 201 /* the packet type - RR (Receiver Report) */ +#define AVDT_RTCP_PT_SDES 202 /* the packet type - SDES (Source Description) */ +typedef UINT8 AVDT_REPORT_TYPE; + +#define AVDT_RTCP_SDES_CNAME 1 /* SDES item CNAME */ +#ifndef AVDT_MAX_CNAME_SIZE +#define AVDT_MAX_CNAME_SIZE 28 +#endif + +/* Protocol service capabilities. This indicates the protocol service +** capabilities of a stream endpoint. This value is a mask. +** Multiple values can be combined with a bitwise OR. +*/ +#define AVDT_PSC_TRANS (1<<1) /* Media transport */ +#define AVDT_PSC_REPORT (1<<2) /* Reporting */ +#define AVDT_PSC_RECOV (1<<3) /* Recovery */ +#define AVDT_PSC_HDRCMP (1<<5) /* Header compression */ +#define AVDT_PSC_MUX (1<<6) /* Multiplexing */ +#define AVDT_PSC_DELAY_RPT (1<<8) /* Delay Report */ + +/* Max audio per 3-DH5 EDR packet: 23.2ms +** jitter buffer: 5(JITTER_BUFFER_WATER_LEVEL) +*/ +#define AVDT_DELAY_RPT_DFT_VALUE 1200 /* 120 ms */ +#define AVDT_DELAY_RPT_TIMER_TICK_MS 2000 /* 2000 ms */ + +/* Recovery type. This indicates the recovery type. */ +#define AVDT_RECOV_RFC2733 1 /* RFC2733 recovery */ + +/* Header compression capabilities. This indicates the header compression +** capabilities. This value is a mask. Multiple values can be combined +** with a bitwise OR. +*/ +#define AVDT_HDRCMP_MEDIA (1<<5) /* Available for media packets */ +#define AVDT_HDRCMP_RECOV (1<<6) /* Available for recovery packets */ +#define AVDT_HDRCMP_BACKCH (1<<7) /* Back channel supported */ + +/* Multiplexing capabilities mask. */ +#define AVDT_MUX_FRAG (1<<7) /* Allow Adaptation Layer Fragmentation */ + +/* Application service category. This indicates the application +** service category. +*/ +#define AVDT_ASC_PROTECT 4 /* Content protection */ +#define AVDT_ASC_CODEC 7 /* Codec */ + +/* Error codes. The following are error codes defined in the AVDTP and GAVDP +** specifications. These error codes communicate protocol errors between +** AVDTP and the application. More detailed descriptions of the error codes +** and their appropriate use can be found in the AVDTP and GAVDP specifications. +** These error codes are unrelated to the result values returned by the +** AVDTP API functions. +*/ +#define AVDT_ERR_HEADER 0x01 /* Bad packet header format */ +#define AVDT_ERR_LENGTH 0x11 /* Bad packet length */ +#define AVDT_ERR_SEID 0x12 /* Invalid SEID */ +#define AVDT_ERR_IN_USE 0x13 /* The SEP is in use */ +#define AVDT_ERR_NOT_IN_USE 0x14 /* The SEP is not in use */ +#define AVDT_ERR_CATEGORY 0x17 /* Bad service category */ +#define AVDT_ERR_PAYLOAD 0x18 /* Bad payload format */ +#define AVDT_ERR_NSC 0x19 /* Requested command not supported */ +#define AVDT_ERR_INVALID_CAP 0x1A /* Reconfigure attempted invalid capabilities */ +#define AVDT_ERR_RECOV_TYPE 0x22 /* Requested recovery type not defined */ +#define AVDT_ERR_MEDIA_TRANS 0x23 /* Media transport capability not correct */ +#define AVDT_ERR_RECOV_FMT 0x25 /* Recovery service capability not correct */ +#define AVDT_ERR_ROHC_FMT 0x26 /* Header compression service capability not correct */ +#define AVDT_ERR_CP_FMT 0x27 /* Content protection service capability not correct */ +#define AVDT_ERR_MUX_FMT 0x28 /* Multiplexing service capability not correct */ +#define AVDT_ERR_UNSUP_CFG 0x29 /* Configuration not supported */ +#define AVDT_ERR_BAD_STATE 0x31 /* Message cannot be processed in this state */ +#define AVDT_ERR_REPORT_FMT 0x65 /* Report service capability not correct */ +#define AVDT_ERR_SERVICE 0x80 /* Invalid service category */ +#define AVDT_ERR_RESOURCE 0x81 /* Insufficient resources */ +#define AVDT_ERR_INVALID_MCT 0xC1 /* Invalid Media Codec Type */ +#define AVDT_ERR_UNSUP_MCT 0xC2 /* Unsupported Media Codec Type */ +#define AVDT_ERR_INVALID_LEVEL 0xC3 /* Invalid Level */ +#define AVDT_ERR_UNSUP_LEVEL 0xC4 /* Unsupported Level */ +#define AVDT_ERR_INVALID_CP 0xE0 /* Invalid Content Protection Type */ +#define AVDT_ERR_INVALID_FORMAT 0xE1 /* Invalid Content Protection format */ + +/* Additional error codes. This indicates error codes used by AVDTP +** in addition to the ones defined in the specifications. +*/ +#define AVDT_ERR_CONNECT 0x07 /* Connection failed. */ +#define AVDT_ERR_TIMEOUT 0x08 /* Response timeout. */ + +/* Control callback events. */ +#define AVDT_DISCOVER_CFM_EVT 0 /* Discover confirm */ +#define AVDT_GETCAP_CFM_EVT 1 /* Get capabilities confirm */ +#define AVDT_OPEN_CFM_EVT 2 /* Open confirm */ +#define AVDT_OPEN_IND_EVT 3 /* Open indication */ +#define AVDT_CONFIG_IND_EVT 4 /* Configuration indication */ +#define AVDT_START_CFM_EVT 5 /* Start confirm */ +#define AVDT_START_IND_EVT 6 /* Start indication */ +#define AVDT_SUSPEND_CFM_EVT 7 /* Suspend confirm */ +#define AVDT_SUSPEND_IND_EVT 8 /* Suspend indication */ +#define AVDT_CLOSE_CFM_EVT 9 /* Close confirm */ +#define AVDT_CLOSE_IND_EVT 10 /* Close indication */ +#define AVDT_RECONFIG_CFM_EVT 11 /* Reconfiguration confirm */ +#define AVDT_RECONFIG_IND_EVT 12 /* Reconfiguration indication */ +#define AVDT_SECURITY_CFM_EVT 13 /* Security confirm */ +#define AVDT_SECURITY_IND_EVT 14 /* Security indication */ +#define AVDT_WRITE_CFM_EVT 15 /* Write confirm */ +#define AVDT_CONNECT_IND_EVT 16 /* Signaling channel connected */ +#define AVDT_DISCONNECT_IND_EVT 17 /* Signaling channel disconnected */ +#define AVDT_REPORT_CONN_EVT 18 /* Reporting channel connected */ +#define AVDT_REPORT_DISCONN_EVT 19 /* Reporting channel disconnected */ +#define AVDT_DELAY_REPORT_EVT 20 /* Delay report received */ +#define AVDT_DELAY_REPORT_CFM_EVT 21 /* Delay report response received */ + +#define AVDT_MAX_EVT (AVDT_DELAY_REPORT_CFM_EVT) + +/* PSM for AVDT */ +#define AVDT_PSM 0x0019 + +/* Nonsupported protocol command messages. This value is used in tAVDT_CS */ +#define AVDT_NSC_SUSPEND 0x01 /* Suspend command not supported */ +#define AVDT_NSC_RECONFIG 0x02 /* Reconfigure command not supported */ +#define AVDT_NSC_SECURITY 0x04 /* Security command not supported */ + +/* AVDT disconnection reason */ +#define AVDT_DISC_RSN_NORMAL 0 +#define AVDT_DISC_RSN_ABNORMAL (0xce) /* unintentional disconnection */ +/***************************************************************************** +** Type Definitions +*****************************************************************************/ + +typedef struct { + UINT32 ntp_sec; /* NTP time: seconds relative to 0h UTC on 1 January 1900 */ + UINT32 ntp_frac; /* NTP time: the fractional part */ + UINT32 rtp_time; /* timestamp in RTP header */ + UINT32 pkt_count; /* sender's packet count: since starting transmission + * up until the time this SR packet was generated. */ + UINT32 octet_count; /* sender's octet count: same comment */ +} tAVDT_SENDER_INFO; + +typedef struct { + UINT8 frag_lost; /* fraction lost since last RR */ + UINT32 packet_lost; /* cumulative number of packets lost since the beginning */ + UINT32 seq_num_rcvd; /* extended highest sequence number received */ + UINT32 jitter; /* interarrival jitter */ + UINT32 lsr; /* last SR timestamp */ + UINT32 dlsr; /* delay since last SR */ +} tAVDT_REPORT_BLK; + +typedef union { + tAVDT_SENDER_INFO sr; + tAVDT_REPORT_BLK rr; + UINT8 cname[AVDT_MAX_CNAME_SIZE + 1]; +} tAVDT_REPORT_DATA; + +/* This structure contains parameters which are set at registration. */ +typedef struct { + UINT16 ctrl_mtu; /* L2CAP MTU of the AVDTP signaling channel */ + UINT8 ret_tout; /* AVDTP signaling retransmission timeout */ + UINT8 sig_tout; /* AVDTP signaling message timeout */ + UINT8 idle_tout; /* AVDTP idle signaling channel timeout */ + UINT8 sec_mask; /* Security mask for BTM_SetSecurityLevel() */ +} tAVDT_REG; + +/* This structure contains the SEP information. This information is +** transferred during the discovery procedure. +*/ +typedef struct { + BOOLEAN in_use; /* TRUE if stream is currently in use */ + UINT8 seid; /* Stream endpoint identifier */ + UINT8 media_type; /* Media type */ + UINT8 tsep; /* SEP type */ +} tAVDT_SEP_INFO; + +/* This structure contains the SEP configuration. */ +typedef struct { + UINT8 codec_info[AVDT_CODEC_SIZE]; /* Codec capabilities array */ + UINT8 protect_info[AVDT_PROTECT_SIZE]; /* Content protection capabilities */ + UINT8 num_codec; /* Number of media codec information elements */ + UINT8 num_protect; /* Number of content protection information elements */ + UINT16 psc_mask; /* Protocol service capabilities mask */ + UINT8 recov_type; /* Recovery type */ + UINT8 recov_mrws; /* Maximum recovery window size */ + UINT8 recov_mnmp; /* Recovery maximum number of media packets */ + UINT8 hdrcmp_mask; /* Header compression capabilities */ +#if AVDT_MULTIPLEXING == TRUE + UINT8 mux_mask; /* Multiplexing capabilities. AVDT_MUX_XXX bits can be combined with a bitwise OR */ + UINT8 mux_tsid_media; /* TSID for media transport session */ + UINT8 mux_tcid_media; /* TCID for media transport session */ + UINT8 mux_tsid_report; /* TSID for reporting transport session */ + UINT8 mux_tcid_report; /* TCID for reporting transport session */ + UINT8 mux_tsid_recov; /* TSID for recovery transport session */ + UINT8 mux_tcid_recov; /* TCID for recovery transport session */ +#endif +} tAVDT_CFG; + +/* Header structure for callback event parameters. */ +typedef struct { + UINT8 err_code; /* Zero if operation succeeded; nonzero if operation failed */ + UINT8 err_param; /* Error parameter included for some events */ + UINT8 label; /* Transaction label */ + UINT8 seid; /* For internal use only */ + UINT8 sig_id; /* For internal use only */ + UINT8 ccb_idx; /* For internal use only */ +} tAVDT_EVT_HDR; + +/* This data structure is associated with the AVDT_GETCAP_CFM_EVT, +** AVDT_RECONFIG_IND_EVT, and AVDT_RECONFIG_CFM_EVT. +*/ +typedef struct { + tAVDT_EVT_HDR hdr; /* Event header */ + tAVDT_CFG *p_cfg; /* Pointer to configuration for this SEP */ +} tAVDT_CONFIG; + +/* This data structure is associated with the AVDT_CONFIG_IND_EVT. */ +typedef struct { + tAVDT_EVT_HDR hdr; /* Event header */ + tAVDT_CFG *p_cfg; /* Pointer to configuration for this SEP */ + UINT8 int_seid; /* Stream endpoint ID of stream initiating the operation */ +} tAVDT_SETCONFIG; + +/* This data structure is associated with the AVDT_OPEN_IND_EVT and AVDT_OPEN_CFM_EVT. */ +typedef struct { + tAVDT_EVT_HDR hdr; /* Event header */ + UINT16 peer_mtu; /* Transport channel L2CAP MTU of the peer */ + UINT16 lcid; /* L2CAP LCID for media channel */ +} tAVDT_OPEN; + +/* This data structure is associated with the AVDT_SECURITY_IND_EVT +** and AVDT_SECURITY_CFM_EVT. +*/ +typedef struct { + tAVDT_EVT_HDR hdr; /* Event header */ + UINT8 *p_data; /* Pointer to security data */ + UINT16 len; /* Length in bytes of the security data */ +} tAVDT_SECURITY; + +/* This data structure is associated with the AVDT_DISCOVER_CFM_EVT. */ +typedef struct { + tAVDT_EVT_HDR hdr; /* Event header */ + tAVDT_SEP_INFO *p_sep_info; /* Pointer to SEP information */ + UINT8 num_seps; /* Number of stream endpoints */ +} tAVDT_DISCOVER; + +/* This data structure is associated with the AVDT_DELAY_REPORT_EVT. */ +typedef struct { + tAVDT_EVT_HDR hdr; /* Event header */ + UINT16 delay; /* Delay value */ +} tAVDT_DELAY_RPT; + +/* Union of all control callback event data structures */ +typedef union { + tAVDT_EVT_HDR hdr; + tAVDT_DISCOVER discover_cfm; + tAVDT_CONFIG getcap_cfm; + tAVDT_OPEN open_cfm; + tAVDT_OPEN open_ind; + tAVDT_SETCONFIG config_ind; + tAVDT_EVT_HDR start_cfm; + tAVDT_EVT_HDR suspend_cfm; + tAVDT_EVT_HDR close_cfm; + tAVDT_CONFIG reconfig_cfm; + tAVDT_CONFIG reconfig_ind; + tAVDT_SECURITY security_cfm; + tAVDT_SECURITY security_ind; + tAVDT_EVT_HDR connect_ind; + tAVDT_EVT_HDR disconnect_ind; + tAVDT_EVT_HDR report_conn; + tAVDT_DELAY_RPT delay_rpt_cmd; +} tAVDT_CTRL; + +/* This is the control callback function. This function passes control events +** to the application. This function is required for all registered stream +** endpoints and for the AVDT_DiscoverReq() and AVDT_GetCapReq() functions. +** +*/ +typedef void (tAVDT_CTRL_CBACK)(UINT8 handle, BD_ADDR bd_addr, UINT8 event, + tAVDT_CTRL *p_data); + +/* This is the data callback function. It is executed when AVDTP has a media +** packet ready for the application. This function is required for SNK +** endpoints and not applicable for SRC endpoints. +*/ +typedef void (tAVDT_DATA_CBACK)(UINT8 handle, BT_HDR *p_pkt, UINT32 time_stamp, + UINT8 m_pt); + +#if AVDT_MULTIPLEXING == TRUE +/* This is the second version of the data callback function. This version uses +** application buffer assigned by AVDT_SetMediaBuf. Caller can assign different +** buffer during callback or can leave the current buffer for further using. +** This callback is called when AVDTP has a media packet ready for the application. +** This function is required for SNK endpoints and not applicable for SRC endpoints. +*/ +typedef void (tAVDT_MEDIA_CBACK)(UINT8 handle, UINT8 *p_payload, UINT32 payload_len, + UINT32 time_stamp, UINT16 seq_num, UINT8 m_pt, UINT8 marker); +#endif + +#if AVDT_REPORTING == TRUE +/* This is the report callback function. It is executed when AVDTP has a reporting +** packet ready for the application. This function is required for streams +** created with AVDT_PSC_REPORT. +*/ +typedef void (tAVDT_REPORT_CBACK)(UINT8 handle, AVDT_REPORT_TYPE type, + tAVDT_REPORT_DATA *p_data); +#endif + +typedef UINT16 (tAVDT_GETCAP_REQ) (BD_ADDR bd_addr, UINT8 seid, tAVDT_CFG *p_cfg, tAVDT_CTRL_CBACK *p_cback); + +/* This structure contains information required when a stream is created. +** It is passed to the AVDT_CreateStream() function. +*/ +typedef struct { + tAVDT_CFG cfg; /* SEP configuration */ + tAVDT_CTRL_CBACK *p_ctrl_cback; /* Control callback function */ + tAVDT_DATA_CBACK *p_data_cback; /* Data callback function */ +#if AVDT_MULTIPLEXING == TRUE + tAVDT_MEDIA_CBACK *p_media_cback; /* Media callback function. It will be called only if p_data_cback is NULL */ +#endif +#if AVDT_REPORTING == TRUE + tAVDT_REPORT_CBACK *p_report_cback;/* Report callback function. */ +#endif + UINT16 mtu; /* The L2CAP MTU of the transport channel */ + UINT16 flush_to; /* The L2CAP flush timeout of the transport channel */ + UINT8 tsep; /* SEP type */ + UINT8 media_type; /* Media type */ + UINT16 nsc_mask; /* Nonsupported protocol command messages */ +} tAVDT_CS; + +/* AVDT data option mask is used in the write request */ +#define AVDT_DATA_OPT_NONE 0x00 /* No option still add RTP header */ +#define AVDT_DATA_OPT_NO_RTP (0x01 << 0) /* Skip adding RTP header */ + +typedef UINT8 tAVDT_DATA_OPT_MASK; + + + +/***************************************************************************** +** External Function Declarations +*****************************************************************************/ +#ifdef __cplusplus +extern "C" +{ +#endif + +/******************************************************************************* +** +** Function AVDT_Register +** +** Description This is the system level registration function for the +** AVDTP protocol. This function initializes AVDTP and +** prepares the protocol stack for its use. This function +** must be called once by the system or platform using AVDTP +** before the other functions of the API an be used. +** +** +** Returns void +** +*******************************************************************************/ +extern void AVDT_Register(tAVDT_REG *p_reg, tAVDT_CTRL_CBACK *p_cback); + +/******************************************************************************* +** +** Function AVDT_Deregister +** +** Description This function is called to deregister use AVDTP protocol. +** It is called when AVDTP is no longer being used by any +** application in the system. Before this function can be +** called, all streams must be removed with AVDT_RemoveStream(). +** +** +** Returns void +** +*******************************************************************************/ +extern void AVDT_Deregister(void); + + +/******************************************************************************* +** +** Function AVDT_SINK_Activate +** +** Description Activate SEP of A2DP Sink. In Use parameter is adjusted. +** In Use will be made false in case of activation. A2DP SRC +** will receive in_use as false and can open A2DP Sink +** connection +** +** Returns void +** +*******************************************************************************/ +extern void AVDT_SINK_Activate(void); + +/******************************************************************************* +** +** Function AVDT_SINK_Deactivate +** +** Description Deactivate SEP of A2DP Sink. In Use parameter is adjusted. +** In Use will be made TRUE in case of activation. A2DP SRC +** will receive in_use as true and will not open A2DP Sink +** connection +** +** Returns void. +** +*******************************************************************************/ +extern void AVDT_SINK_Deactivate(void); + +/******************************************************************************* +** +** Function AVDT_AbortReq +** +** Description Trigger Abort request to pass AVDTP Abort related mandatory +** PTS Test case. +** +** Returns void. +** +*******************************************************************************/ +extern void AVDT_AbortReq(UINT8 handle); + +/******************************************************************************* +** +** Function AVDT_CreateStream +** +** Description Create a stream endpoint. After a stream endpoint is +** created an application can initiate a connection between +** this endpoint and an endpoint on a peer device. In +** addition, a peer device can discover, get the capabilities, +** and connect to this endpoint. +** +** +** Returns AVDT_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +extern UINT16 AVDT_CreateStream(UINT8 *p_handle, tAVDT_CS *p_cs); + +/******************************************************************************* +** +** Function AVDT_RemoveStream +** +** Description Remove a stream endpoint. This function is called when +** the application is no longer using a stream endpoint. +** If this function is called when the endpoint is connected +** the connection is closed and then the stream endpoint +** is removed. +** +** +** Returns AVDT_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +extern UINT16 AVDT_RemoveStream(UINT8 handle); + +/******************************************************************************* +** +** Function AVDT_DiscoverReq +** +** Description This function initiates a connection to the AVDTP service +** on the peer device, if not already present, and discovers +** the stream endpoints on the peer device. (Please note +** that AVDTP discovery is unrelated to SDP discovery). +** This function can be called at any time regardless of whether +** there is an AVDTP connection to the peer device. +** +** When discovery is complete, an AVDT_DISCOVER_CFM_EVT +** is sent to the application via its callback function. +** The application must not call AVDT_GetCapReq() or +** AVDT_DiscoverReq() again to the same device until +** discovery is complete. +** +** The memory addressed by sep_info is allocated by the +** application. This memory is written to by AVDTP as part +** of the discovery procedure. This memory must remain +** accessible until the application receives the +** AVDT_DISCOVER_CFM_EVT. +** +** Returns AVDT_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +extern UINT16 AVDT_DiscoverReq(BD_ADDR bd_addr, tAVDT_SEP_INFO *p_sep_info, + UINT8 max_seps, tAVDT_CTRL_CBACK *p_cback); + + +/******************************************************************************* +** +** Function AVDT_GetCapReq +** +** Description This function initiates a connection to the AVDTP service +** on the peer device, if not already present, and gets the +** capabilities of a stream endpoint on the peer device. +** This function can be called at any time regardless of +** whether there is an AVDTP connection to the peer device. +** +** When the procedure is complete, an AVDT_GETCAP_CFM_EVT is +** sent to the application via its callback function. The +** application must not call AVDT_GetCapReq() or +** AVDT_DiscoverReq() again until the procedure is complete. +** +** The memory pointed to by p_cfg is allocated by the +** application. This memory is written to by AVDTP as part +** of the get capabilities procedure. This memory must +** remain accessible until the application receives +** the AVDT_GETCAP_CFM_EVT. +** +** Returns AVDT_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +extern UINT16 AVDT_GetCapReq(BD_ADDR bd_addr, UINT8 seid, tAVDT_CFG *p_cfg, + tAVDT_CTRL_CBACK *p_cback); + +/******************************************************************************* +** +** Function AVDT_GetAllCapReq +** +** Description This function initiates a connection to the AVDTP service +** on the peer device, if not already present, and gets the +** capabilities of a stream endpoint on the peer device. +** This function can be called at any time regardless of +** whether there is an AVDTP connection to the peer device. +** +** When the procedure is complete, an AVDT_GETCAP_CFM_EVT is +** sent to the application via its callback function. The +** application must not call AVDT_GetCapReq() or +** AVDT_DiscoverReq() again until the procedure is complete. +** +** The memory pointed to by p_cfg is allocated by the +** application. This memory is written to by AVDTP as part +** of the get capabilities procedure. This memory must +** remain accessible until the application receives +** the AVDT_GETCAP_CFM_EVT. +** +** Returns AVDT_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +extern UINT16 AVDT_GetAllCapReq(BD_ADDR bd_addr, UINT8 seid, tAVDT_CFG *p_cfg, + tAVDT_CTRL_CBACK *p_cback); + +/******************************************************************************* +** +** Function AVDT_DelayReport +** +** Description This functions sends a Delay Report to the peer device +** that is associated with a particular SEID. +** This function is called by SNK device. +** +** Returns AVDT_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +extern UINT16 AVDT_DelayReport(UINT8 handle, UINT8 seid, UINT16 delay); + +/******************************************************************************* +** +** Function AVDT_OpenReq +** +** Description This function initiates a connection to the AVDTP service +** on the peer device, if not already present, and connects +** to a stream endpoint on a peer device. When the connection +** is completed, an AVDT_OPEN_CFM_EVT is sent to the +** application via the control callback function for this handle. +** +** Returns AVDT_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +extern UINT16 AVDT_OpenReq(UINT8 handle, BD_ADDR bd_addr, UINT8 seid, + tAVDT_CFG *p_cfg); + + +/******************************************************************************* +** +** Function AVDT_ConfigRsp +** +** Description Respond to a configure request from the peer device. This +** function must be called if the application receives an +** AVDT_CONFIG_IND_EVT through its control callback. +** +** +** Returns AVDT_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +extern UINT16 AVDT_ConfigRsp(UINT8 handle, UINT8 label, UINT8 error_code, + UINT8 category); + +/******************************************************************************* +** +** Function AVDT_StartReq +** +** Description Start one or more stream endpoints. This initiates the +** transfer of media packets for the streams. All stream +** endpoints must previously be opened. When the streams +** are started, an AVDT_START_CFM_EVT is sent to the +** application via the control callback function for each stream. +** +** +** Returns AVDT_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +extern UINT16 AVDT_StartReq(UINT8 *p_handles, UINT8 num_handles); + +/******************************************************************************* +** +** Function AVDT_SuspendReq +** +** Description Suspend one or more stream endpoints. This suspends the +** transfer of media packets for the streams. All stream +** endpoints must previously be open and started. When the +** streams are suspended, an AVDT_SUSPEND_CFM_EVT is sent to +** the application via the control callback function for +** each stream. +** +** +** Returns AVDT_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +extern UINT16 AVDT_SuspendReq(UINT8 *p_handles, UINT8 num_handles); + +/******************************************************************************* +** +** Function AVDT_CloseReq +** +** Description Close a stream endpoint. This stops the transfer of media +** packets and closes the transport channel associated with +** this stream endpoint. When the stream is closed, an +** AVDT_CLOSE_CFM_EVT is sent to the application via the +** control callback function for this handle. +** +** +** Returns AVDT_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +extern UINT16 AVDT_CloseReq(UINT8 handle); + +/******************************************************************************* +** +** Function AVDT_ReconfigReq +** +** Description Reconfigure a stream endpoint. This allows the application +** to change the codec or content protection capabilities of +** a stream endpoint after it has been opened. This function +** can only be called if the stream is opened but not started +** or if the stream has been suspended. When the procedure +** is completed, an AVDT_RECONFIG_CFM_EVT is sent to the +** application via the control callback function for this handle. +** +** +** Returns AVDT_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +extern UINT16 AVDT_ReconfigReq(UINT8 handle, tAVDT_CFG *p_cfg); + +/******************************************************************************* +** +** Function AVDT_ReconfigRsp +** +** Description Respond to a reconfigure request from the peer device. +** This function must be called if the application receives +** an AVDT_RECONFIG_IND_EVT through its control callback. +** +** +** Returns AVDT_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +extern UINT16 AVDT_ReconfigRsp(UINT8 handle, UINT8 label, UINT8 error_code, + UINT8 category); + +/******************************************************************************* +** +** Function AVDT_SecurityReq +** +** Description Send a security request to the peer device. When the +** security procedure is completed, an AVDT_SECURITY_CFM_EVT +** is sent to the application via the control callback function +** for this handle. (Please note that AVDTP security procedures +** are unrelated to Bluetooth link level security.) +** +** +** Returns AVDT_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +extern UINT16 AVDT_SecurityReq(UINT8 handle, UINT8 *p_data, UINT16 len); + +/******************************************************************************* +** +** Function AVDT_SecurityRsp +** +** Description Respond to a security request from the peer device. +** This function must be called if the application receives +** an AVDT_SECURITY_IND_EVT through its control callback. +** (Please note that AVDTP security procedures are unrelated +** to Bluetooth link level security.) +** +** +** Returns AVDT_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +extern UINT16 AVDT_SecurityRsp(UINT8 handle, UINT8 label, UINT8 error_code, + UINT8 *p_data, UINT16 len); + +/******************************************************************************* +** +** Function AVDT_WriteReq +** +** Description Send a media packet to the peer device. The stream must +** be started before this function is called. Also, this +** function can only be called if the stream is a SRC. +** +** When AVDTP has sent the media packet and is ready for the +** next packet, an AVDT_WRITE_CFM_EVT is sent to the +** application via the control callback. The application must +** wait for the AVDT_WRITE_CFM_EVT before it makes the next +** call to AVDT_WriteReq(). If the applications calls +** AVDT_WriteReq() before it receives the event the packet +** will not be sent. The application may make its first call +** to AVDT_WriteReq() after it receives an AVDT_START_CFM_EVT +** or AVDT_START_IND_EVT. +** +** The application passes the packet using the BT_HDR structure. +** This structure is described in section 2.1. The offset +** field must be equal to or greater than AVDT_MEDIA_OFFSET. +** This allows enough space in the buffer for the L2CAP and +** AVDTP headers. +** +** The memory pointed to by p_pkt must be a GKI buffer +** allocated by the application. This buffer will be freed +** by the protocol stack; the application must not free +** this buffer. +** +** +** Returns AVDT_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +extern UINT16 AVDT_WriteReq(UINT8 handle, BT_HDR *p_pkt, UINT32 time_stamp, + UINT8 m_pt); +/******************************************************************************* +** +** Function AVDT_WriteReqOpt +** +** Description Send a media packet to the peer device. The stream must +** be started before this function is called. Also, this +** function can only be called if the stream is a SRC +** +** When AVDTP has sent the media packet and is ready for the +** next packet, an AVDT_WRITE_CFM_EVT is sent to the +** application via the control callback. The application must +** wait for the AVDT_WRITE_CFM_EVT before it makes the next +** call to AVDT_WriteReq(). If the applications calls +** AVDT_WriteReq() before it receives the event the packet +** will not be sent. The application may make its first call +** to AVDT_WriteReq() after it receives an AVDT_START_CFM_EVT +** or AVDT_START_IND_EVT. +** +** The application passes the packet using the BT_HDR structure +** This structure is described in section 2.1. The offset +** field must be equal to or greater than AVDT_MEDIA_OFFSET +** (if NO_RTP is specified, L2CAP_MIN_OFFSET can be used) +** This allows enough space in the buffer for the L2CAP and +** AVDTP headers. +** +** The memory pointed to by p_pkt must be a GKI buffer +** allocated by the application. This buffer will be freed +** by the protocol stack; the application must not free +** this buffer. +** +** The opt parameter allows passing specific options like: +** - NO_RTP : do not add the RTP header to buffer +** +** Returns AVDT_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +extern UINT16 AVDT_WriteReqOpt(UINT8 handle, BT_HDR *p_pkt, UINT32 time_stamp, + UINT8 m_pt, tAVDT_DATA_OPT_MASK opt); + +/******************************************************************************* +** +** Function AVDT_ConnectReq +** +** Description This function initiates an AVDTP signaling connection +** to the peer device. When the connection is completed, an +** AVDT_CONNECT_IND_EVT is sent to the application via its +** control callback function. If the connection attempt fails +** an AVDT_DISCONNECT_IND_EVT is sent. The security mask +** parameter overrides the outgoing security mask set in +** AVDT_Register(). +** +** Returns AVDT_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +extern UINT16 AVDT_ConnectReq(BD_ADDR bd_addr, UINT8 sec_mask, + tAVDT_CTRL_CBACK *p_cback); + +/******************************************************************************* +** +** Function AVDT_DisconnectReq +** +** Description This function disconnect an AVDTP signaling connection +** to the peer device. When disconnected an +** AVDT_DISCONNECT_IND_EVT is sent to the application via its +** control callback function. +** +** Returns AVDT_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +extern UINT16 AVDT_DisconnectReq(BD_ADDR bd_addr, tAVDT_CTRL_CBACK *p_cback); + +/******************************************************************************* +** +** Function AVDT_GetL2CapChannel +** +** Description Get the L2CAP CID used by the handle. +** +** Returns CID if successful, otherwise 0. +** +*******************************************************************************/ +extern UINT16 AVDT_GetL2CapChannel(UINT8 handle); + +/******************************************************************************* +** +** Function AVDT_GetSignalChannel +** +** Description Get the L2CAP CID used by the signal channel of the given handle. +** +** Returns CID if successful, otherwise 0. +** +*******************************************************************************/ +extern UINT16 AVDT_GetSignalChannel(UINT8 handle, BD_ADDR bd_addr); + +/******************************************************************************* +** +** Function AVDT_WriteDataReq +** +** Description Send a media packet to the peer device. The stream must +** be started before this function is called. Also, this +** function can only be called if the stream is a SRC. +** +** When AVDTP has sent the media packet and is ready for the +** next packet, an AVDT_WRITE_CFM_EVT is sent to the +** application via the control callback. The application must +** wait for the AVDT_WRITE_CFM_EVT before it makes the next +** call to AVDT_WriteDataReq(). If the applications calls +** AVDT_WriteDataReq() before it receives the event the packet +** will not be sent. The application may make its first call +** to AVDT_WriteDataReq() after it receives an +** AVDT_START_CFM_EVT or AVDT_START_IND_EVT. +** +** Returns AVDT_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +extern UINT16 AVDT_WriteDataReq(UINT8 handle, UINT8 *p_data, UINT32 data_len, + UINT32 time_stamp, UINT8 m_pt, UINT8 marker); + +/******************************************************************************* +** +** Function AVDT_SetMediaBuf +** +** Description Assigns buffer for media packets or forbids using of assigned +** buffer if argument p_buf is NULL. This function can only +** be called if the stream is a SNK. +** +** AVDTP uses this buffer to reassemble fragmented media packets. +** When AVDTP receives a complete media packet, it calls the +** p_media_cback assigned by AVDT_CreateStream(). +** This function can be called during callback to assign a +** different buffer for next media packet or can leave the current +** buffer for next packet. +** +** Returns AVDT_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +extern UINT16 AVDT_SetMediaBuf(UINT8 handle, UINT8 *p_buf, UINT32 buf_len); + +/******************************************************************************* +** +** Function AVDT_SendReport +** +** Description +** +** +** +** Returns +** +*******************************************************************************/ +extern UINT16 AVDT_SendReport(UINT8 handle, AVDT_REPORT_TYPE type, + tAVDT_REPORT_DATA *p_data); + +/****************************************************************************** +** +** Function AVDT_SetTraceLevel +** +** Description Sets the trace level for AVDT. If 0xff is passed, the +** current trace level is returned. +** +** Input Parameters: +** new_level: The level to set the AVDT tracing to: +** 0xff-returns the current setting. +** 0-turns off tracing. +** >= 1-Errors. +** >= 2-Warnings. +** >= 3-APIs. +** >= 4-Events. +** >= 5-Debug. +** +** Returns The new trace level or current trace level if +** the input parameter is 0xff. +** +******************************************************************************/ +extern UINT8 AVDT_SetTraceLevel (UINT8 new_level); + +/******************************************************************************* +** +** Function AVDT_SetDelayValue +** +** Description Set delay reporting value. +** +** Returns void +** +*******************************************************************************/ +extern void AVDT_SetDelayValue(UINT16 delay_value); + +/******************************************************************************* +** +** Function AVDT_GetDelayValue +** +** Description Get delay reporting value. +** +** Returns delay value +** +*******************************************************************************/ +extern UINT16 AVDT_GetDelayValue(void); + +#ifdef __cplusplus +} +#endif + + +#endif /* AVDT_API_H */ diff --git a/lib/bt/host/bluedroid/stack/include/stack/avdtc_api.h b/lib/bt/host/bluedroid/stack/include/stack/avdtc_api.h new file mode 100644 index 00000000..083b0b1c --- /dev/null +++ b/lib/bt/host/bluedroid/stack/include/stack/avdtc_api.h @@ -0,0 +1,230 @@ +/****************************************************************************** + * + * Copyright (C) 2002-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This interface file contains the interface AVDTP conformance API. These + * additional API functions and callback events are provided for + * conformance testing purposes only. They are not intended to be used by + * an application. + * + ******************************************************************************/ +#ifndef AVDT_CAPI_H +#define AVDT_CAPI_H + +#include "stack/avdt_api.h" + +/* start AVDTC events here to distinguish from AVDT events */ +#define AVDTC_EVT_BEGIN 0x80 + +#define AVDTC_DISCOVER_IND_EVT (0 + AVDTC_EVT_BEGIN) /* Discover indication */ +#define AVDTC_GETCAP_IND_EVT (1 + AVDTC_EVT_BEGIN) /* Get capabilities indication */ +#define AVDTC_SETCONFIG_CFM_EVT (2 + AVDTC_EVT_BEGIN) /* Set configuration confirm */ +#define AVDTC_GETCONFIG_IND_EVT (3 + AVDTC_EVT_BEGIN) /* Get configuration indication */ +#define AVDTC_GETCONFIG_CFM_EVT (4 + AVDTC_EVT_BEGIN) /* Get configuration confirm */ +#define AVDTC_OPEN_IND_EVT (5 + AVDTC_EVT_BEGIN) /* Open indication */ +#define AVDTC_START_IND_EVT (6 + AVDTC_EVT_BEGIN) /* Start indication */ +#define AVDTC_CLOSE_IND_EVT (7 + AVDTC_EVT_BEGIN) /* Close indication */ +#define AVDTC_SUSPEND_IND_EVT (8 + AVDTC_EVT_BEGIN) /* Suspend indication */ +#define AVDTC_ABORT_IND_EVT (9 + AVDTC_EVT_BEGIN) /* Abort indication */ +#define AVDTC_ABORT_CFM_EVT (10 + AVDTC_EVT_BEGIN) /* Abort confirm */ + +typedef struct { + tAVDT_EVT_HDR hdr; /* Event header */ + UINT8 seid_list[AVDT_NUM_SEPS]; /* Array of SEID values */ + UINT8 num_seps; /* Number of values in array */ +} tAVDT_MULTI; + +/* Union of all control callback event data structures */ +typedef union { + tAVDT_EVT_HDR hdr; + tAVDT_CONFIG getconfig_cfm; + tAVDT_MULTI start_ind; + tAVDT_MULTI suspend_ind; +} tAVDTC_CTRL; + +typedef void tAVDTC_CTRL_CBACK(UINT8 handle, BD_ADDR bd_addr, UINT8 event, tAVDTC_CTRL *p_data); + +#ifdef __cplusplus +extern "C" +{ +#endif + +/******************************************************************************* +** +** Function AVDTC_Init +** +** Description This function is called to begin using the conformance API. +** It must be called after AVDT_Register() and before any +** other API or conformance API functions are called. +** +** Returns void +** +*******************************************************************************/ +extern void AVDTC_Init(tAVDTC_CTRL_CBACK *p_cback); + +/******************************************************************************* +** +** Function AVDTC_DiscoverRsp +** +** Description Send a discover response. +** +** Returns void +** +*******************************************************************************/ +extern void AVDTC_DiscoverRsp(BD_ADDR bd_addr, UINT8 label, + tAVDT_SEP_INFO sep_info[], UINT8 num_seps); + +/******************************************************************************* +** +** Function AVDTC_GetCapRsp +** +** Description Send a get capabilities response. +** +** Returns void +** +*******************************************************************************/ +extern void AVDTC_GetCapRsp(BD_ADDR bd_addr, UINT8 label, tAVDT_CFG *p_cap); + +/******************************************************************************* +** +** Function AVDTC_GetAllCapRsp +** +** Description Send a get all capabilities response. +** +** Returns void +** +*******************************************************************************/ +extern void AVDTC_GetAllCapRsp(BD_ADDR bd_addr, UINT8 label, tAVDT_CFG *p_cap); + +/******************************************************************************* +** +** Function AVDTC_GetConfigReq +** +** Description Send a get configuration request. +** +** Returns void +** +*******************************************************************************/ +extern void AVDTC_GetConfigReq(UINT8 handle); + +/******************************************************************************* +** +** Function AVDTC_GetConfigRsp +** +** Description Send a get configuration response. +** +** Returns void +** +*******************************************************************************/ +extern void AVDTC_GetConfigRsp(UINT8 handle, UINT8 label, tAVDT_CFG *p_cfg); + +/******************************************************************************* +** +** Function AVDTC_OpenReq +** +** Description Send an open request. +** +** Returns void +** +*******************************************************************************/ +extern void AVDTC_OpenReq(UINT8 handle); + +/******************************************************************************* +** +** Function AVDTC_OpenRsp +** +** Description Send an open response. +** +** Returns void +** +*******************************************************************************/ +extern void AVDTC_OpenRsp(UINT8 handle, UINT8 label); + +/******************************************************************************* +** +** Function AVDTC_StartRsp +** +** Description Send a start response. +** +** Returns void +** +*******************************************************************************/ +extern void AVDTC_StartRsp(UINT8 *p_handles, UINT8 num_handles, UINT8 label); + +/******************************************************************************* +** +** Function AVDTC_CloseRsp +** +** Description Send a close response. +** +** Returns void +** +*******************************************************************************/ +extern void AVDTC_CloseRsp(UINT8 handle, UINT8 label); + +/******************************************************************************* +** +** Function AVDTC_SuspendRsp +** +** Description Send a suspend response. +** +** Returns void +** +*******************************************************************************/ +extern void AVDTC_SuspendRsp(UINT8 *p_handles, UINT8 num_handles, UINT8 label); + +/******************************************************************************* +** +** Function AVDTC_AbortReq +** +** Description Send an abort request. +** +** Returns void +** +*******************************************************************************/ +extern void AVDTC_AbortReq(UINT8 handle); + +/******************************************************************************* +** +** Function AVDTC_AbortRsp +** +** Description Send an abort response. +** +** Returns void +** +*******************************************************************************/ +extern void AVDTC_AbortRsp(UINT8 handle, UINT8 label); + +/******************************************************************************* +** +** Function AVDTC_Rej +** +** Description Send a reject message. +** +** Returns void +** +*******************************************************************************/ +extern void AVDTC_Rej(UINT8 handle, BD_ADDR bd_addr, UINT8 cmd, UINT8 label, + UINT8 err_code, UINT8 err_param); + +#ifdef __cplusplus +} +#endif + +#endif /* AVDT_CAPI_H */ diff --git a/lib/bt/host/bluedroid/stack/include/stack/avrc_api.h b/lib/bt/host/bluedroid/stack/include/stack/avrc_api.h new file mode 100644 index 00000000..21f4bee5 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/include/stack/avrc_api.h @@ -0,0 +1,655 @@ +/****************************************************************************** + * + * Copyright (C) 2006-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * nterface to AVRCP Application Programming Interface + * + ******************************************************************************/ +#ifndef AVRC_API_H +#define AVRC_API_H +#include "common/bt_target.h" +#include "common/bt_defs.h" +#include "stack/avct_api.h" +#include "stack/sdp_api.h" +#include "stack/avrc_defs.h" +#if (AVRC_INCLUDED == TRUE) +/***************************************************************************** +** constants +*****************************************************************************/ + +/* API function return value result codes. */ +#define AVRC_SUCCESS AVCT_SUCCESS /* 0 Function successful */ +#define AVRC_NO_RESOURCES AVCT_NO_RESOURCES /* 1 Not enough resources */ +#define AVRC_BAD_HANDLE AVCT_BAD_HANDLE /* 2 Bad handle */ +#define AVRC_PID_IN_USE AVCT_PID_IN_USE /* 3 PID already in use */ +#define AVRC_NOT_OPEN AVCT_NOT_OPEN /* 4 Connection not open */ +#define AVRC_MSG_TOO_BIG 5 /* 5 the message length exceed the MTU of the browsing channel */ +#define AVRC_FAIL 0x10 /* 0x10 generic failure */ +#define AVRC_BAD_PARAM 0x11 /* 0x11 bad parameter */ + +/* Control role - same as AVCT_TARGET/AVCT_CONTROL */ +#define AVRC_CT_TARGET 1 /* target */ +#define AVRC_CT_CONTROL 2 /* controller */ +#define AVRC_CT_PASSIVE 4 /* If conflict, allow the other side to succeed */ + +/* Connection role */ +#define AVRC_CONN_INT AVCT_INT /* initiator */ +#define AVRC_CONN_ACP AVCT_ACP /* Acceptor */ + + +/* AVRC CTRL events */ +/* AVRC_OPEN_IND_EVT event is sent when the connection is successfully opened. + * This eventis sent in response to an AVRC_Open(). */ +#define AVRC_OPEN_IND_EVT 0 + +/* AVRC_CLOSE_IND_EVT event is sent when a connection is closed. + * This event can result from a call to AVRC_Close() or when the peer closes + * the connection. It is also sent when a connection attempted through + * AVRC_Open() fails. */ +#define AVRC_CLOSE_IND_EVT 1 + +/* AVRC_CONG_IND_EVT event indicates that AVCTP is congested and cannot send + * any more messages. */ +#define AVRC_CONG_IND_EVT 2 + +/* AVRC_UNCONG_IND_EVT event indicates that AVCTP is uncongested and ready to + * send messages. */ +#define AVRC_UNCONG_IND_EVT 3 + +/* AVRC_BROWSE_OPEN_IND_EVT event is sent when the browse channel is successfully opened. +* This eventis sent in response to an AVRC_Open() or AVRC_OpenBrowse() . */ +#define AVRC_BROWSE_OPEN_IND_EVT 4 + +/* AVRC_BROWSE_CLOSE_IND_EVT event is sent when a browse channel is closed. + * This event can result from a call to AVRC_Close(), AVRC_CloseBrowse() or when the peer closes + * the connection. It is also sent when a connection attempted through + * AVRC_OpenBrowse() fails. */ +#define AVRC_BROWSE_CLOSE_IND_EVT 5 + +/* AVRC_BROWSE_CONG_IND_EVT event indicates that AVCTP browse channel is congested and cannot send + * any more messages. */ +#define AVRC_BROWSE_CONG_IND_EVT 6 + +/* AVRC_BROWSE_UNCONG_IND_EVT event indicates that AVCTP browse channel is uncongested and ready to + * send messages. */ +#define AVRC_BROWSE_UNCONG_IND_EVT 7 + +/* AVRC_CMD_TIMEOUT_EVT event indicates timeout waiting for AVRC command response from the peer */ +#define AVRC_CMD_TIMEOUT_EVT 8 + +/* Supported categories */ +#define AVRC_SUPF_CT_CAT1 0x0001 /* Category 1 */ +#define AVRC_SUPF_CT_CAT2 0x0002 /* Category 2 */ +#define AVRC_SUPF_CT_CAT3 0x0004 /* Category 3 */ +#define AVRC_SUPF_CT_CAT4 0x0008 /* Category 4 */ +#define AVRC_SUPF_CT_BROWSE 0x0040 /* Browsing */ + +#define AVRC_SUPF_TG_CAT1 0x0001 /* Category 1 */ +#define AVRC_SUPF_TG_CAT2 0x0002 /* Category 2 */ +#define AVRC_SUPF_TG_CAT3 0x0004 /* Category 3 */ +#define AVRC_SUPF_TG_CAT4 0x0008 /* Category 4 */ +#define AVRC_SUPF_TG_APP_SETTINGS 0x0010 /* Player Application Settings */ +#define AVRC_SUPF_TG_GROUP_NAVI 0x0020 /* Group Navigation */ +#define AVRC_SUPF_TG_BROWSE 0x0040 /* Browsing */ +#define AVRC_SUPF_TG_MULTI_PLAYER 0x0080 /* Muliple Media Player */ + +#define AVRC_META_SUCCESS AVRC_SUCCESS +#define AVRC_META_FAIL AVRC_FAIL +#define AVRC_METADATA_CMD 0x0000 +#define AVRC_METADATA_RESP 0x0001 + + + +/***************************************************************************** +** data type definitions +*****************************************************************************/ + +/* This data type is used in AVRC_FindService() to initialize the SDP database + * to hold the result service search. */ +typedef struct { + UINT32 db_len; /* Length, in bytes, of the discovery database */ + tSDP_DISCOVERY_DB *p_db; /* Pointer to the discovery database */ + UINT16 num_attr;/* The number of attributes in p_attrs */ + UINT16 *p_attrs; /* The attributes filter. If NULL, AVRCP API sets the attribute filter + * to be ATTR_ID_SERVICE_CLASS_ID_LIST, ATTR_ID_BT_PROFILE_DESC_LIST, + * ATTR_ID_SUPPORTED_FEATURES, ATTR_ID_SERVICE_NAME and ATTR_ID_PROVIDER_NAME. + * If not NULL, the input is taken as the filter. */ +} tAVRC_SDP_DB_PARAMS; + +/* This callback function returns service discovery information to the + * application after the AVRC_FindService() API function is called. The + * implementation of this callback function must copy the p_service_name + * and p_provider_name parameters passed to it as they are not guaranteed + * to remain after the callback function exits. */ +typedef void (tAVRC_FIND_CBACK) (UINT16 status); + + +/* This is the control callback function. This function passes events + * listed in Table 20 to the application. */ +typedef void (tAVRC_CTRL_CBACK) (UINT8 handle, UINT8 event, UINT16 result, + BD_ADDR peer_addr); + + +/* This is the message callback function. It is executed when AVCTP has + * a message packet ready for the application. The implementation of this + * callback function must copy the tAVRC_MSG structure passed to it as it + * is not guaranteed to remain after the callback function exits. */ +typedef void (tAVRC_MSG_CBACK) (UINT8 handle, UINT8 label, UINT8 opcode, + tAVRC_MSG *p_msg); + +typedef struct { + tAVRC_CTRL_CBACK *p_ctrl_cback; /* pointer to application control callback */ + tAVRC_MSG_CBACK *p_msg_cback; /* pointer to application message callback */ + UINT32 company_id; /* the company ID */ + UINT8 conn; /* Connection role (Initiator/acceptor) */ + UINT8 control; /* Control role (Control/Target) */ +} tAVRC_CONN_CB; + + + +/***************************************************************************** +** external function declarations +*****************************************************************************/ +#ifdef __cplusplus +extern "C" +{ +#endif + +/****************************************************************************** +** +** Function AVRC_AddRecord +** +** Description This function is called to build an AVRCP SDP record. +** Prior to calling this function the application must +** call SDP_CreateRecord() to create an SDP record. +** +** Input Parameters: +** service_uuid: Indicates TG(UUID_SERVCLASS_AV_REM_CTRL_TARGET) +** or CT(UUID_SERVCLASS_AV_REMOTE_CONTROL) +** +** p_service_name: Pointer to a null-terminated character +** string containing the service name. +** If service name is not used set this to NULL. +** +** p_provider_name: Pointer to a null-terminated character +** string containing the provider name. +** If provider name is not used set this to NULL. +** +** categories: Supported categories. +** +** sdp_handle: SDP handle returned by SDP_CreateRecord(). +** +** browsing_en: Supported browsing +** +** Output Parameters: +** None. +** +** Returns AVRC_SUCCESS if successful. +** AVRC_NO_RESOURCES if not enough resources to build the SDP record. +** +******************************************************************************/ +extern UINT16 AVRC_AddRecord(UINT16 service_uuid, char *p_service_name, char *p_provider_name, + UINT16 categories, UINT32 sdp_handle, BOOLEAN browsing_en); + +/****************************************************************************** +** +** Function AVRC_FindService +** +** Description This function is called by the application to perform service +** discovery and retrieve AVRCP SDP record information from a +** peer device. Information is returned for the first service +** record found on the server that matches the service UUID. +** The callback function will be executed when service discovery +** is complete. There can only be one outstanding call to +** AVRC_FindService() at a time; the application must wait for +** the callback before it makes another call to the function. +** The application is responsible for allocating memory for the +** discovery database. It is recommended that the size of the +** discovery database be at least 300 bytes. The application +** can deallocate the memory after the callback function has +** executed. +** +** Input Parameters: +** service_uuid: Indicates TG(UUID_SERVCLASS_AV_REM_CTRL_TARGET) +** or CT(UUID_SERVCLASS_AV_REMOTE_CONTROL) +** +** bd_addr: BD address of the peer device. +** +** p_db: SDP discovery database parameters. +** +** p_cback: Pointer to the callback function. +** +** Output Parameters: +** None. +** +** Returns AVRC_SUCCESS if successful. +** AVRC_BAD_PARAMS if discovery database parameters are invalid. +** AVRC_NO_RESOURCES if there are not enough resources to +** perform the service search. +** +******************************************************************************/ +extern UINT16 AVRC_FindService(UINT16 service_uuid, BD_ADDR bd_addr, + tAVRC_SDP_DB_PARAMS *p_db, tAVRC_FIND_CBACK *p_cback); + +/****************************************************************************** +** +** Function AVRC_Open +** +** Description This function is called to open a connection to AVCTP. +** The connection can be either an initiator or acceptor, as +** determined by the p_ccb->stream parameter. +** The connection can be a target, a controller or for both role, +** as determined by the p_ccb->control parameter. +** By definition, a target connection is an acceptor connection +** that waits for an incoming AVCTP connection from the peer. +** The connection remains available to the application until +** the application closes it by calling AVRC_Close(). The +** application does not need to reopen the connection after an +** AVRC_CLOSE_IND_EVT is received. +** +** Input Parameters: +** p_ccb->company_id: Company Identifier. +** +** p_ccb->p_ctrl_cback: Pointer to control callback function. +** +** p_ccb->p_msg_cback: Pointer to message callback function. +** +** p_ccb->conn: AVCTP connection role. This is set to +** AVCTP_INT for initiator connections and AVCTP_ACP +** for acceptor connections. +** +** p_ccb->control: Control role. This is set to +** AVRC_CT_TARGET for target connections, AVRC_CT_CONTROL +** for control connections or (AVRC_CT_TARGET|AVRC_CT_CONTROL) +** for connections that support both roles. +** +** peer_addr: BD address of peer device. This value is +** only used for initiator connections; for acceptor +** connections it can be set to NULL. +** +** Output Parameters: +** p_handle: Pointer to handle. This parameter is only +** valid if AVRC_SUCCESS is returned. +** +** Returns AVRC_SUCCESS if successful. +** AVRC_NO_RESOURCES if there are not enough resources to open +** the connection. +** +******************************************************************************/ +extern UINT16 AVRC_Open(UINT8 *p_handle, tAVRC_CONN_CB *p_ccb, + BD_ADDR_PTR peer_addr); + +/****************************************************************************** +** +** Function AVRC_Close +** +** Description Close a connection opened with AVRC_Open(). +** This function is called when the +** application is no longer using a connection. +** +** Input Parameters: +** handle: Handle of this connection. +** +** Output Parameters: +** None. +** +** Returns AVRC_SUCCESS if successful. +** AVRC_BAD_HANDLE if handle is invalid. +** +******************************************************************************/ +extern UINT16 AVRC_Close(UINT8 handle); + +/****************************************************************************** +** +** Function AVRC_OpenBrowse +** +** Description This function is called to open a browsing connection to AVCTP. +** The connection can be either an initiator or acceptor, as +** determined by the conn_role. +** The handle is returned by a previous call to AVRC_Open. +** +** Returns AVRC_SUCCESS if successful. +** AVRC_NO_RESOURCES if there are not enough resources to open +** the connection. +** +******************************************************************************/ +extern UINT16 AVRC_OpenBrowse(UINT8 handle, UINT8 conn_role); + +/****************************************************************************** +** +** Function AVRC_CloseBrowse +** +** Description Close a connection opened with AVRC_OpenBrowse(). +** This function is called when the +** application is no longer using a connection. +** +** Returns AVRC_SUCCESS if successful. +** AVRC_BAD_HANDLE if handle is invalid. +** +******************************************************************************/ +extern UINT16 AVRC_CloseBrowse(UINT8 handle); + +/****************************************************************************** +** +** Function AVRC_MsgReq +** +** Description This function is used to send the AVRCP byte stream in p_pkt +** down to AVCTP. +** +** It is expected that p_pkt->offset is at least AVCT_MSG_OFFSET +** p_pkt->layer_specific is AVCT_DATA_CTRL or AVCT_DATA_BROWSE +** p_pkt->event is AVRC_OP_VENDOR, AVRC_OP_PASS_THRU or AVRC_OP_BROWSING +** The above BT_HDR settings are set by the AVRC_Bld* functions. +** +** Returns AVRC_SUCCESS if successful. +** AVRC_BAD_HANDLE if handle is invalid. +** +******************************************************************************/ +extern UINT16 AVRC_MsgReq (UINT8 handle, UINT8 label, UINT8 ctype, BT_HDR *p_pkt); + +/****************************************************************************** +** +** Function AVRC_UnitCmd +** +** Description Send a UNIT INFO command to the peer device. This +** function can only be called for controller role connections. +** Any response message from the peer is passed back through +** the tAVRC_MSG_CBACK callback function. +** +** Input Parameters: +** handle: Handle of this connection. +** +** label: Transaction label. +** +** Output Parameters: +** None. +** +** Returns AVRC_SUCCESS if successful. +** AVRC_BAD_HANDLE if handle is invalid. +** +******************************************************************************/ +extern UINT16 AVRC_UnitCmd(UINT8 handle, UINT8 label); + +/****************************************************************************** +** +** Function AVRC_SubCmd +** +** Description Send a SUBUNIT INFO command to the peer device. This +** function can only be called for controller role connections. +** Any response message from the peer is passed back through +** the tAVRC_MSG_CBACK callback function. +** +** Input Parameters: +** handle: Handle of this connection. +** +** label: Transaction label. +** +** page: Specifies which part of the subunit type table +** is requested. For AVRCP it is typically zero. +** Value range is 0-7. +** +** Output Parameters: +** None. +** +** Returns AVRC_SUCCESS if successful. +** AVRC_BAD_HANDLE if handle is invalid. +** +******************************************************************************/ +extern UINT16 AVRC_SubCmd(UINT8 handle, UINT8 label, UINT8 page); + + +/****************************************************************************** +** +** Function AVRC_PassCmd +** +** Description Send a PASS THROUGH command to the peer device. This +** function can only be called for controller role connections. +** Any response message from the peer is passed back through +** the tAVRC_MSG_CBACK callback function. +** +** Input Parameters: +** handle: Handle of this connection. +** +** label: Transaction label. +** +** p_msg: Pointer to PASS THROUGH message structure. +** +** Output Parameters: +** None. +** +** Returns AVRC_SUCCESS if successful. +** AVRC_BAD_HANDLE if handle is invalid. +** +******************************************************************************/ +extern UINT16 AVRC_PassCmd(UINT8 handle, UINT8 label, tAVRC_MSG_PASS *p_msg); + +/****************************************************************************** +** +** Function AVRC_PassRsp +** +** Description Send a PASS THROUGH response to the peer device. This +** function can only be called for target role connections. +** This function must be called when a PASS THROUGH command +** message is received from the peer through the +** tAVRC_MSG_CBACK callback function. +** +** Input Parameters: +** handle: Handle of this connection. +** +** label: Transaction label. Must be the same value as +** passed with the command message in the callback function. +** +** p_msg: Pointer to PASS THROUGH message structure. +** +** Output Parameters: +** None. +** +** Returns AVRC_SUCCESS if successful. +** AVRC_BAD_HANDLE if handle is invalid. +** +******************************************************************************/ +extern UINT16 AVRC_PassRsp(UINT8 handle, UINT8 label, tAVRC_MSG_PASS *p_msg); + + +/****************************************************************************** +** +** Function AVRC_VendorCmd +** +** Description Send a VENDOR DEPENDENT command to the peer device. This +** function can only be called for controller role connections. +** Any response message from the peer is passed back through +** the tAVRC_MSG_CBACK callback function. +** +** Input Parameters: +** handle: Handle of this connection. +** +** label: Transaction label. +** +** p_msg: Pointer to VENDOR DEPENDENT message structure. +** +** Output Parameters: +** None. +** +** Returns AVRC_SUCCESS if successful. +** AVRC_BAD_HANDLE if handle is invalid. +** +******************************************************************************/ +extern UINT16 AVRC_VendorCmd(UINT8 handle, UINT8 label, tAVRC_MSG_VENDOR *p_msg); + + +/****************************************************************************** +** +** Function AVRC_VendorRsp +** +** Description Send a VENDOR DEPENDENT response to the peer device. This +** function can only be called for target role connections. +** This function must be called when a VENDOR DEPENDENT +** command message is received from the peer through the +** tAVRC_MSG_CBACK callback function. +** +** Input Parameters: +** handle: Handle of this connection. +** +** label: Transaction label. Must be the same value as +** passed with the command message in the callback function. +** +** p_msg: Pointer to VENDOR DEPENDENT message structure. +** +** Output Parameters: +** None. +** +** Returns AVRC_SUCCESS if successful. +** AVRC_BAD_HANDLE if handle is invalid. +** +******************************************************************************/ +extern UINT16 AVRC_VendorRsp(UINT8 handle, UINT8 label, tAVRC_MSG_VENDOR *p_msg); + + +/****************************************************************************** +** +** Function AVRC_SetTraceLevel +** +** Description Sets the trace level for AVRC. If 0xff is passed, the +** current trace level is returned. +** +** Input Parameters: +** new_level: The level to set the AVRC tracing to: +** 0xff-returns the current setting. +** 0-turns off tracing. +** >= 1-Errors. +** >= 2-Warnings. +** >= 3-APIs. +** >= 4-Events. +** >= 5-Debug. +** +** Returns The new trace level or current trace level if +** the input parameter is 0xff. +** +******************************************************************************/ +extern UINT8 AVRC_SetTraceLevel (UINT8 new_level); + +/******************************************************************************* +** +** Function AVRC_Init +** +** Description This function is called at stack startup to allocate the +** control block (if using dynamic memory), and initializes the +** control block and tracing level. +** +** Returns status +** +*******************************************************************************/ +extern bt_status_t AVRC_Init(void); + +/******************************************************************************* +** +** Function AVRC_Deinit +** +** Description This function is called at stack shotdown to free the +** control block (if using dynamic memory), and deinitializes the +** control block and tracing level. +** +** Returns void +** +*******************************************************************************/ +extern void AVRC_Deinit(void); + +/******************************************************************************* +** +** Function AVRC_ParsCommand +** +** Description This function is used to parse the received command. +** +** Returns AVRC_STS_NO_ERROR, if the message in p_data is parsed successfully. +** Otherwise, the error code defined by AVRCP 1.4 +** +*******************************************************************************/ +extern tAVRC_STS AVRC_ParsCommand (tAVRC_MSG *p_msg, tAVRC_COMMAND *p_result, + UINT8 *p_buf, UINT16 buf_len); + +/******************************************************************************* +** +** Function AVRC_ParsResponse +** +** Description This function is used to parse the received response. +** +** Returns AVRC_STS_NO_ERROR, if the message in p_data is parsed successfully. +** Otherwise, the error code defined by AVRCP 1.4 +** +*******************************************************************************/ +extern tAVRC_STS AVRC_ParsResponse (tAVRC_MSG *p_msg, tAVRC_RESPONSE *p_result); + +/******************************************************************************* +** +** Function AVRC_BldCommand +** +** Description This function builds the given AVRCP command to the given +** GKI buffer +** +** Returns AVRC_STS_NO_ERROR, if the command is built successfully +** Otherwise, the error code. +** +*******************************************************************************/ +extern tAVRC_STS AVRC_BldCommand( tAVRC_COMMAND *p_cmd, BT_HDR **pp_pkt); + +/******************************************************************************* +** +** Function AVRC_BldResponse +** +** Description This function builds the given AVRCP response to the given +** GKI buffer +** +** Returns AVRC_STS_NO_ERROR, if the response is built successfully +** Otherwise, the error code. +** +*******************************************************************************/ +extern tAVRC_STS AVRC_BldResponse( UINT8 handle, tAVRC_RESPONSE *p_rsp, BT_HDR **pp_pkt); + +/************************************************************************** +** +** Function AVRC_IsValidAvcType +** +** Description Check if correct AVC type is specified +** +** Returns returns TRUE if it is valid +** +** +*******************************************************************************/ +extern BOOLEAN AVRC_IsValidAvcType(UINT8 pdu_id, UINT8 avc_type); + +/******************************************************************************* +** +** Function AVRC_IsValidPlayerAttr +** +** Description Check if the given attrib value is a valid one +** +** +** Returns returns TRUE if it is valid +** +*******************************************************************************/ +extern BOOLEAN AVRC_IsValidPlayerAttr(UINT8 attr); + +#ifdef __cplusplus +} +#endif + +#endif ///AVRC_INCLUDED == TRUE + + +#endif /* AVRC_API_H */ diff --git a/lib/bt/host/bluedroid/stack/include/stack/avrc_defs.h b/lib/bt/host/bluedroid/stack/include/stack/avrc_defs.h new file mode 100644 index 00000000..c90fa6cf --- /dev/null +++ b/lib/bt/host/bluedroid/stack/include/stack/avrc_defs.h @@ -0,0 +1,1360 @@ +/****************************************************************************** + * + * Copyright (C) 2006-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * AVRCP definition and data types + * + ******************************************************************************/ +#ifndef _AVRC_DEFS_H +#define _AVRC_DEFS_H +#include "common/bt_target.h" + +#if (AVRC_INCLUDED == TRUE) +/***************************************************************************** +** constants +*****************************************************************************/ + +/* Profile revision numbers */ +#define AVRC_REV_1_0 0x0100 +#define AVRC_REV_1_3 0x0103 +#define AVRC_REV_1_4 0x0104 +#define AVRC_REV_1_5 0x0105 + +#define AVRC_PACKET_LEN 512 /* Per the spec, you must support 512 byte RC packets */ + +#define AVRC_MIN_CONTROL_MTU 48 /* Per the spec, minimum MTU for the control channel */ +#define AVRC_MIN_BROWSE_MTU 335 /* Per the spec, minimum MTU for the browsing channel */ + +#define AVRC_META_PDU_OFFSET 4 +#define AVRC_SUB_TYPE_LEN 4 +#define AVRC_UID_SIZE 8 +#define AVRC_FEATURE_MASK_SIZE 16 + +/* command type codes */ +#define AVRC_CMD_CTRL 0 /* Instruct a target to perform an operation */ +#define AVRC_CMD_STATUS 1 /* Check a device's current status */ +#define AVRC_CMD_SPEC_INQ 2 /* Check whether a target supports a particular control command; all operands are included */ +#define AVRC_CMD_NOTIF 3 /* Used for receiving notification of a change in a device's state */ +#define AVRC_CMD_GEN_INQ 4 /* Check whether a target supports a particular control command; operands are not included */ + +/* response type codes */ +#define AVRC_RSP_NOT_IMPL 8 /* The target does not implement the command specified by the opcode and operand, + or doesn't implement the specified subunit */ +#define AVRC_RSP_ACCEPT 9 /* The target executed or is executing the command */ +#define AVRC_RSP_REJ 10 /* The target implements the command specified by the + opcode but cannot respond because the current state + of the target doesn't allow it */ +#define AVRC_RSP_IN_TRANS 11 /* The target implements the status command but it is + in a state of transition; the status command may + be retried at a future time */ +#define AVRC_RSP_IMPL_STBL 12 /* For specific inquiry or general inquiy commands, + the target implements the command; for status + commands, the target returns stable and includes + the status results */ +#define AVRC_RSP_CHANGED 13 /* The response frame contains a notification that the + target device's state has changed */ +#define AVRC_RSP_INTERIM 15 /* For control commands, the target has accepted the + request but cannot return information within 100 + milliseconds; for notify commands, the target accepted + the command, and will notify the controller of a change + of target state at a future time */ + +/* subunit type */ +#define AVRC_SUB_MONITOR 0x00 /* Monitor */ +#define AVRC_SUB_AUDIO 0x01 /* Audio */ +#define AVRC_SUB_PRINTER 0x02 /* Printer */ +#define AVRC_SUB_DISC 0x03 /* Disc */ +#define AVRC_SUB_TAPE 0x04 /* Tape recorder/player */ +#define AVRC_SUB_TUNER 0x05 /* Tuner */ +#define AVRC_SUB_CA 0x06 /* CA */ +#define AVRC_SUB_CAMERA 0x07 /* Camera */ +#define AVRC_SUB_PANEL 0x09 /* Panel */ +#define AVRC_SUB_BB 0x0A /* Bulletin Board */ +#define AVRC_SUB_CAM_STOR 0x0B /* Camera Storage */ +#define AVRC_SUB_VENDOR 0x1C /* Vendor unique */ +#define AVRC_SUB_EXT 0x1E /* Subunit type extended to next byte */ +#define AVRC_SUB_UNIT 0x1F /* Unit */ + +/* opcodes - defined by 1394ta */ +#define AVRC_OP_UNIT_INFO 0x30 /* Report unit information */ +#define AVRC_OP_SUB_INFO 0x31 /* Report subunit information */ +#define AVRC_OP_VENDOR 0x00 /* Vendor-dependent commands */ +#define AVRC_OP_PASS_THRU 0x7C /* panel subunit opcode */ +/* opcodes 80-9F and E0-FF are not used by 1394ta.Sneak one for the browsing channel */ +#define AVRC_OP_BROWSE 0xFF /* Browsing */ +#define AVRC_OP_INVALID 0xFE /* invalid one */ + +/* Company ID's +*/ +#define AVRC_CO_BLUETOOTH_SIG 0x00FFFFFF +#define AVRC_CO_WIDCOMM 0x00000361 +#define AVRC_CO_BROADCOM 0x00001018 +#define AVRC_CO_METADATA 0x00001958 /* Unique COMPANY ID for Metadata messages */ + +/* State flag for Passthrough commands +*/ +#define AVRC_STATE_PRESS 0 +#define AVRC_STATE_RELEASE 1 + +/* Operation ID list for Passthrough commands +*/ +#define AVRC_ID_SELECT 0x00 /* select */ +#define AVRC_ID_UP 0x01 /* up */ +#define AVRC_ID_DOWN 0x02 /* down */ +#define AVRC_ID_LEFT 0x03 /* left */ +#define AVRC_ID_RIGHT 0x04 /* right */ +#define AVRC_ID_RIGHT_UP 0x05 /* right-up */ +#define AVRC_ID_RIGHT_DOWN 0x06 /* right-down */ +#define AVRC_ID_LEFT_UP 0x07 /* left-up */ +#define AVRC_ID_LEFT_DOWN 0x08 /* left-down */ +#define AVRC_ID_ROOT_MENU 0x09 /* root menu */ +#define AVRC_ID_SETUP_MENU 0x0A /* setup menu */ +#define AVRC_ID_CONT_MENU 0x0B /* contents menu */ +#define AVRC_ID_FAV_MENU 0x0C /* favorite menu */ +#define AVRC_ID_EXIT 0x0D /* exit */ +#define AVRC_ID_0 0x20 /* 0 */ +#define AVRC_ID_1 0x21 /* 1 */ +#define AVRC_ID_2 0x22 /* 2 */ +#define AVRC_ID_3 0x23 /* 3 */ +#define AVRC_ID_4 0x24 /* 4 */ +#define AVRC_ID_5 0x25 /* 5 */ +#define AVRC_ID_6 0x26 /* 6 */ +#define AVRC_ID_7 0x27 /* 7 */ +#define AVRC_ID_8 0x28 /* 8 */ +#define AVRC_ID_9 0x29 /* 9 */ +#define AVRC_ID_DOT 0x2A /* dot */ +#define AVRC_ID_ENTER 0x2B /* enter */ +#define AVRC_ID_CLEAR 0x2C /* clear */ +#define AVRC_ID_CHAN_UP 0x30 /* channel up */ +#define AVRC_ID_CHAN_DOWN 0x31 /* channel down */ +#define AVRC_ID_PREV_CHAN 0x32 /* previous channel */ +#define AVRC_ID_SOUND_SEL 0x33 /* sound select */ +#define AVRC_ID_INPUT_SEL 0x34 /* input select */ +#define AVRC_ID_DISP_INFO 0x35 /* display information */ +#define AVRC_ID_HELP 0x36 /* help */ +#define AVRC_ID_PAGE_UP 0x37 /* page up */ +#define AVRC_ID_PAGE_DOWN 0x38 /* page down */ +#define AVRC_ID_POWER 0x40 /* power */ +#define AVRC_ID_VOL_UP 0x41 /* volume up */ +#define AVRC_ID_VOL_DOWN 0x42 /* volume down */ +#define AVRC_ID_MUTE 0x43 /* mute */ +#define AVRC_ID_PLAY 0x44 /* play */ +#define AVRC_ID_STOP 0x45 /* stop */ +#define AVRC_ID_PAUSE 0x46 /* pause */ +#define AVRC_ID_RECORD 0x47 /* record */ +#define AVRC_ID_REWIND 0x48 /* rewind */ +#define AVRC_ID_FAST_FOR 0x49 /* fast forward */ +#define AVRC_ID_EJECT 0x4A /* eject */ +#define AVRC_ID_FORWARD 0x4B /* forward */ +#define AVRC_ID_BACKWARD 0x4C /* backward */ +#define AVRC_ID_ANGLE 0x50 /* angle */ +#define AVRC_ID_SUBPICT 0x51 /* subpicture */ +#define AVRC_ID_F1 0x71 /* F1 */ +#define AVRC_ID_F2 0x72 /* F2 */ +#define AVRC_ID_F3 0x73 /* F3 */ +#define AVRC_ID_F4 0x74 /* F4 */ +#define AVRC_ID_F5 0x75 /* F5 */ +#define AVRC_ID_VENDOR 0x7E /* vendor unique */ +#define AVRC_KEYPRESSED_RELEASE 0x80 + +/***************************************************************************** +** Metadata transfer definitions +*****************************************************************************/ + +/* Define the Metadata Packet types +*/ +#define AVRC_PKT_SINGLE 0 +#define AVRC_PKT_START 1 +#define AVRC_PKT_CONTINUE 2 +#define AVRC_PKT_END 3 +#define AVRC_PKT_TYPE_MASK 3 + +/* Define the PDUs carried in the vendor dependant data +*/ +#define AVRC_PDU_GET_CAPABILITIES 0x10 +#define AVRC_PDU_LIST_PLAYER_APP_ATTR 0x11 +#define AVRC_PDU_LIST_PLAYER_APP_VALUES 0x12 +#define AVRC_PDU_GET_CUR_PLAYER_APP_VALUE 0x13 +#define AVRC_PDU_SET_PLAYER_APP_VALUE 0x14 +#define AVRC_PDU_GET_PLAYER_APP_ATTR_TEXT 0x15 +#define AVRC_PDU_GET_PLAYER_APP_VALUE_TEXT 0x16 +#define AVRC_PDU_INFORM_DISPLAY_CHARSET 0x17 +#define AVRC_PDU_INFORM_BATTERY_STAT_OF_CT 0x18 +#define AVRC_PDU_GET_ELEMENT_ATTR 0x20 +#define AVRC_PDU_GET_PLAY_STATUS 0x30 +#define AVRC_PDU_REGISTER_NOTIFICATION 0x31 +#define AVRC_PDU_REQUEST_CONTINUATION_RSP 0x40 +#define AVRC_PDU_ABORT_CONTINUATION_RSP 0x41 +/* added in 1.4 */ +#define AVRC_PDU_SET_ABSOLUTE_VOLUME 0x50 +#define AVRC_PDU_SET_ADDRESSED_PLAYER 0x60 +#define AVRC_PDU_SET_BROWSED_PLAYER 0x70 +#define AVRC_PDU_GET_FOLDER_ITEMS 0x71 +#define AVRC_PDU_CHANGE_PATH 0x72 +#define AVRC_PDU_GET_ITEM_ATTRIBUTES 0x73 +#define AVRC_PDU_PLAY_ITEM 0x74 +#define AVRC_PDU_SEARCH 0x80 +#define AVRC_PDU_ADD_TO_NOW_PLAYING 0x90 +#define AVRC_PDU_GENERAL_REJECT 0xA0 + +/* Define the vendor unique id carried in the pass through data +*/ +#define AVRC_PDU_NEXT_GROUP 0x00 +#define AVRC_PDU_PREV_GROUP 0x01 +/* the only pass through vendor unique commands defined by AVRC is the group navigation commands + * The len for vendor unique data is 5 */ +#define AVRC_PASS_THRU_GROUP_LEN 5 + +#define AVRC_PDU_INVALID 0xff +/* 6.15.3 error status code for general reject */ +#define AVRC_STS_BAD_CMD 0x00 /* Invalid command, sent if TG received a PDU that it did not understand. */ +#define AVRC_STS_BAD_PARAM 0x01 /* Invalid parameter, sent if the TG received a PDU with a parameter ID that it did not understand. Sent if there is only one parameter ID in the PDU. */ +#define AVRC_STS_NOT_FOUND 0x02 /* Specified parameter not found., sent if the parameter ID is understood, but content is wrong or corrupted. */ +#define AVRC_STS_INTERNAL_ERR 0x03 /* Internal Error, sent if there are error conditions not covered by a more specific error code. */ +#define AVRC_STS_NO_ERROR 0x04 /* Operation completed without error. This is the status that should be returned if the operation was successful. */ +#define AVRC_STS_UID_CHANGED 0x05 /* UID Changed - The UIDs on the device have changed */ +/* #define AVRC_STS_GEN_ERROR 0x06 Unknown Error - this is changed to "reserved" */ +#define AVRC_STS_BAD_DIR 0x07 /* Invalid Direction - The Direction parameter is invalid - Change Path*/ +#define AVRC_STS_NOT_DIR 0x08 /* Not a Directory - The UID provided does not refer to a folder item Change Path*/ +#define AVRC_STS_NOT_EXIST 0x09 /* Does Not Exist - The UID provided does not refer to any item Change Path, PlayItem, AddToNowPlaying, GetItemAttributes*/ +#define AVRC_STS_BAD_SCOPE 0x0a /* Invalid Scope - The scope parameter is invalid GetFolderItems, PlayItem, AddToNowPlayer, GetItemAttributes, */ +#define AVRC_STS_BAD_RANGE 0x0b /* Range Out of Bounds - The start of range provided is not valid GetFolderItems*/ +#define AVRC_STS_UID_IS_DIR 0x0c /* UID is a Directory - The UID provided refers to a directory, which cannot be handled by this media player PlayItem, AddToNowPlaying */ +#define AVRC_STS_IN_USE 0x0d /* Media in Use - The media is not able to be used for this operation at this time PlayItem, AddToNowPlaying */ +#define AVRC_STS_NOW_LIST_FULL 0x0e /* Now Playing List Full - No more items can be added to the Now Playing List AddToNowPlaying*/ +#define AVRC_STS_SEARCH_NOT_SUP 0x0f /* Search Not Supported - The Browsed Media Player does not support search Search */ +#define AVRC_STS_SEARCH_BUSY 0x10 /* Search in Progress - A search operation is already in progress Search*/ +#define AVRC_STS_BAD_PLAYER_ID 0x11 /* Invalid Player Id - The specified Player Id does not refer to a valid player SetAddressedPlayer, SetBrowsedPlayer*/ +#define AVRC_STS_PLAYER_N_BR 0x12 /* Player Not Browsable - The Player Id supplied refers to a Media Player which does not support browsing. SetBrowsedPlayer */ +#define AVRC_STS_PLAYER_N_ADDR 0x13 /* Player Not Addressed. The Player Id supplied refers to a player which is not currently addressed, and the command is not able to be performed if the player is not set as addressed. Search, SetBrowsedPlayer*/ +#define AVRC_STS_BAD_SEARCH_RES 0x14 /* No valid Search Results - The Search result list does not contain valid entries, e.g. after being invalidated due to change of browsed player GetFolderItems */ +#define AVRC_STS_NO_AVAL_PLAYER 0x15 /* No available players ALL */ +#define AVRC_STS_ADDR_PLAYER_CHG 0x16 /* Addressed Player Changed - Register Notification */ +typedef UINT8 tAVRC_STS; + + +/* Define the Capability IDs +*/ +#define AVRC_CAP_COMPANY_ID 0x02 +#define AVRC_CAP_EVENTS_SUPPORTED 0x03 +#define AVRC_COMPANY_ID_LEN 3 +#define AVRC_CAPABILITY_OFFSET 2 + +/* Define the Player Application Settings IDs +*/ +#define AVRC_PLAYER_SETTING_EQUALIZER 0x01 +#define AVRC_PLAYER_SETTING_REPEAT 0x02 +#define AVRC_PLAYER_SETTING_SHUFFLE 0x03 +#define AVRC_PLAYER_SETTING_SCAN 0x04 +#define AVRC_PLAYER_SETTING_LOW_MENU_EXT 0x80 +#define AVRC_PLAYER_SETTING_HIGH_MENU_EXT 0xff + +/* Define the possible values of the Player Application Settings +*/ +#define AVRC_PLAYER_VAL_OFF 0x01 +#define AVRC_PLAYER_VAL_ON 0x02 +#define AVRC_PLAYER_VAL_SINGLE_REPEAT 0x02 +#define AVRC_PLAYER_VAL_ALL_REPEAT 0x03 +#define AVRC_PLAYER_VAL_GROUP_REPEAT 0x04 +#define AVRC_PLAYER_VAL_ALL_SHUFFLE 0x02 +#define AVRC_PLAYER_VAL_GROUP_SHUFFLE 0x03 +#define AVRC_PLAYER_VAL_ALL_SCAN 0x02 +#define AVRC_PLAYER_VAL_GROUP_SCAN 0x03 + +/* Define the possible values of Battery Status PDU +*/ +#define AVRC_BATTERY_STATUS_NORMAL 0x00 +#define AVRC_BATTERY_STATUS_WARNING 0x01 +#define AVRC_BATTERY_STATUS_CRITICAL 0x02 +#define AVRC_BATTERY_STATUS_EXTERNAL 0x03 +#define AVRC_BATTERY_STATUS_FULL_CHARGE 0x04 +typedef UINT8 tAVRC_BATTERY_STATUS; + +/* Define character set */ +#define AVRC_CHAR_SET_SIZE 2 + +/* Define the Media Attribute IDs +*/ +#define AVRC_MEDIA_ATTR_ID_TITLE 0x00000001 +#define AVRC_MEDIA_ATTR_ID_ARTIST 0x00000002 +#define AVRC_MEDIA_ATTR_ID_ALBUM 0x00000003 +#define AVRC_MEDIA_ATTR_ID_TRACK_NUM 0x00000004 +#define AVRC_MEDIA_ATTR_ID_NUM_TRACKS 0x00000005 +#define AVRC_MEDIA_ATTR_ID_GENRE 0x00000006 +#define AVRC_MEDIA_ATTR_ID_PLAYING_TIME 0x00000007 /* in miliseconds */ +#define AVRC_MAX_NUM_MEDIA_ATTR_ID 7 + +/* Define the possible values of play state +*/ +#define AVRC_PLAYSTATE_RESP_MSG_SIZE 9 +#define AVRC_PLAYSTATE_STOPPED 0x00 /* Stopped */ +#define AVRC_PLAYSTATE_PLAYING 0x01 /* Playing */ +#define AVRC_PLAYSTATE_PAUSED 0x02 /* Paused */ +#define AVRC_PLAYSTATE_FWD_SEEK 0x03 /* Fwd Seek*/ +#define AVRC_PLAYSTATE_REV_SEEK 0x04 /* Rev Seek*/ +#define AVRC_PLAYSTATE_ERROR 0xFF /* Error */ +typedef UINT8 tAVRC_PLAYSTATE; + +/* Define the events that can be registered for notifications +*/ +#define AVRC_EVT_PLAY_STATUS_CHANGE 0x01 +#define AVRC_EVT_TRACK_CHANGE 0x02 +#define AVRC_EVT_TRACK_REACHED_END 0x03 +#define AVRC_EVT_TRACK_REACHED_START 0x04 +#define AVRC_EVT_PLAY_POS_CHANGED 0x05 +#define AVRC_EVT_BATTERY_STATUS_CHANGE 0x06 +#define AVRC_EVT_SYSTEM_STATUS_CHANGE 0x07 +#define AVRC_EVT_APP_SETTING_CHANGE 0x08 +/* added in AVRCP 1.4 */ +#define AVRC_EVT_NOW_PLAYING_CHANGE 0x09 +#define AVRC_EVT_AVAL_PLAYERS_CHANGE 0x0a +#define AVRC_EVT_ADDR_PLAYER_CHANGE 0x0b +#define AVRC_EVT_UIDS_CHANGE 0x0c +#define AVRC_EVT_VOLUME_CHANGE 0x0d + +/* the number of events that can be registered for notifications */ +#define AVRC_NUM_NOTIF_EVENTS 0x0d + +#define AVRC_EVT_MSG_LEN_1 0x01 +#define AVRC_EVT_MSG_LEN_2 0x02 +#define AVRC_EVT_MSG_LEN_5 0x05 +#define AVRC_EVT_MSG_LEN_9 0x09 + +#define AVRC_MAX_VOLUME 0x7F + +/* Define the possible values of system status +*/ +#define AVRC_SYSTEMSTATE_PWR_ON 0x00 +#define AVRC_SYSTEMSTATE_PWR_OFF 0x01 +#define AVRC_SYSTEMSTATE_PWR_UNPLUGGED 0x02 +typedef UINT8 tAVRC_SYSTEMSTATE; + +/* the frequently used character set ids */ +#define AVRC_CHARSET_ID_ASCII ((UINT16) 0x0003) /* ASCII */ +#define AVRC_CHARSET_ID_UTF8 ((UINT16) 0x006a) /* UTF-8 */ +#define AVRC_CHARSET_ID_UTF16 ((UINT16) 0x03f7) /* 1015 */ +#define AVRC_CHARSET_ID_UTF32 ((UINT16) 0x03f9) /* 1017 */ + +/***************************************************************************** +** Advanced Control +*****************************************************************************/ +#define AVRC_ITEM_PLAYER 0x01 +#define AVRC_ITEM_FOLDER 0x02 +#define AVRC_ITEM_MEDIA 0x03 + +#define AVRC_SCOPE_PLAYER_LIST 0x00 /* Media Player Item - Contains all available media players */ +#define AVRC_SCOPE_FILE_SYSTEM 0x01 /* Folder Item, Media Element Item + - The virtual filesystem containing the media content of the browsed player */ +#define AVRC_SCOPE_SEARCH 0x02 /* Media Element Item The results of a search operation on the browsed player */ +#define AVRC_SCOPE_NOW_PLAYING 0x03 /* Media Element Item The Now Playing list (or queue) of the addressed player */ + +#define AVRC_FOLDER_ITEM_COUNT_NONE 0xFF + +/* folder type */ +#define AVRC_FOLDER_TYPE_MIXED 0x00 +#define AVRC_FOLDER_TYPE_TITLES 0x01 +#define AVRC_FOLDER_TYPE_ALNUMS 0x02 +#define AVRC_FOLDER_TYPE_ARTISTS 0x03 +#define AVRC_FOLDER_TYPE_GENRES 0x04 +#define AVRC_FOLDER_TYPE_PLAYLISTS 0x05 +#define AVRC_FOLDER_TYPE_YEARS 0x06 + +/* major player type */ +#define AVRC_MJ_TYPE_AUDIO 0x01 /* Audio */ +#define AVRC_MJ_TYPE_VIDEO 0x02 /* Video */ +#define AVRC_MJ_TYPE_BC_AUDIO 0x04 /* Broadcasting Audio */ +#define AVRC_MJ_TYPE_BC_VIDEO 0x08 /* Broadcasting Video */ +#define AVRC_MJ_TYPE_INVALID 0xF0 + +/* player sub type */ +#define AVRC_SUB_TYPE_NONE 0x00 +#define AVRC_SUB_TYPE_AUDIO_BOOK 0x01 /* Audio Book */ +#define AVRC_SUB_TYPE_PODCAST 0x02 /* Podcast */ +#define AVRC_SUB_TYPE_INVALID 0xFC + +/* media item - media type */ +#define AVRC_MEDIA_TYPE_AUDIO 0x00 +#define AVRC_MEDIA_TYPE_VIDEO 0x01 + +#define AVRC_DIR_UP 0x00 /* Folder Up */ +#define AVRC_DIR_DOWN 0x01 /* Folder Down */ + +#define AVRC_UID_SIZE 8 +typedef UINT8 tAVRC_UID[AVRC_UID_SIZE]; + +/***************************************************************************** +** player attribute - supported features +*****************************************************************************/ +#define AVRC_PF_SELECT_BIT_NO 0 +#define AVRC_PF_SELECT_MASK 0x01 +#define AVRC_PF_SELECT_OFF 0 +#define AVRC_PF_SELECT_SUPPORTED(x) ((x)[AVRC_PF_SELECT_OFF] & AVRC_PF_SELECT_MASK) + +#define AVRC_PF_UP_BIT_NO 1 +#define AVRC_PF_UP_MASK 0x02 +#define AVRC_PF_UP_OFF 0 +#define AVRC_PF_UP_SUPPORTED(x) ((x)[AVRC_PF_UP_OFF] & AVRC_PF_UP_MASK) + +#define AVRC_PF_DOWN_BIT_NO 2 +#define AVRC_PF_DOWN_MASK 0x04 +#define AVRC_PF_DOWN_OFF 0 +#define AVRC_PF_DOWN_SUPPORTED(x) ((x)[AVRC_PF_DOWN_OFF] & AVRC_PF_DOWN_MASK) + +#define AVRC_PF_LEFT_BIT_NO 3 +#define AVRC_PF_LEFT_MASK 0x08 +#define AVRC_PF_LEFT_OFF 0 +#define AVRC_PF_LEFT_SUPPORTED(x) ((x)[AVRC_PF_LEFT_OFF] & AVRC_PF_LEFT_MASK) + +#define AVRC_PF_RIGHT_BIT_NO 4 +#define AVRC_PF_RIGHT_MASK 0x10 +#define AVRC_PF_RIGHT_OFF 0 +#define AVRC_PF_RIGHT_SUPPORTED(x) ((x)[AVRC_PF_RIGHT_OFF] & AVRC_PF_RIGHT_MASK) + +#define AVRC_PF_RIGHTUP_BIT_NO 5 +#define AVRC_PF_RIGHTUP_MASK 0x20 +#define AVRC_PF_RIGHTUP_OFF 0 +#define AVRC_PF_RIGHTUP_SUPPORTED(x) ((x)[AVRC_PF_RIGHTUP_OFF] & AVRC_PF_RIGHTUP_MASK) + +#define AVRC_PF_RIGHTDOWN_BIT_NO 6 +#define AVRC_PF_RIGHTDOWN_MASK 0x40 +#define AVRC_PF_RIGHTDOWN_OFF 0 +#define AVRC_PF_RIGHTDOWN_SUPPORTED(x) ((x)[AVRC_PF_RIGHTDOWN_OFF] & AVRC_PF_RIGHTDOWN_MASK) + +#define AVRC_PF_LEFTUP_BIT_NO 7 +#define AVRC_PF_LEFTUP_MASK 0x80 +#define AVRC_PF_LEFTUP_OFF 0 +#define AVRC_PF_LEFTUP_SUPPORTED(x) ((x)[AVRC_PF_LEFTUP_OFF] & AVRC_PF_LEFTUP_MASK) + +#define AVRC_PF_LEFTDOWN_BIT_NO 8 +#define AVRC_PF_LEFTDOWN_MASK 0x01 +#define AVRC_PF_LEFTDOWN_OFF 1 +#define AVRC_PF_LEFTDOWN_SUPPORTED(x) ((x)[AVRC_PF_LEFTDOWN_OFF] & AVRC_PF_LEFTDOWN_MASK) + +#define AVRC_PF_ROOT_MENU_BIT_NO 9 +#define AVRC_PF_ROOT_MENU_MASK 0x02 +#define AVRC_PF_ROOT_MENU_OFF 1 +#define AVRC_PF_ROOT_MENU_SUPPORTED(x) ((x)[AVRC_PF_ROOT_MENU_OFF] & AVRC_PF_ROOT_MENU_MASK) + +#define AVRC_PF_SETUP_MENU_BIT_NO 10 +#define AVRC_PF_SETUP_MENU_MASK 0x04 +#define AVRC_PF_SETUP_MENU_OFF 1 +#define AVRC_PF_SETUP_MENU_SUPPORTED(x) ((x)[AVRC_PF_SETUP_MENU_OFF] & AVRC_PF_SETUP_MENU_MASK) + +#define AVRC_PF_CONTENTS_MENU_BIT_NO 11 +#define AVRC_PF_CONTENTS_MENU_MASK 0x08 +#define AVRC_PF_CONTENTS_MENU_OFF 1 +#define AVRC_PF_CONTENTS_MENU_SUPPORTED(x) ((x)[AVRC_PF_CONTENTS_MENU_OFF] & AVRC_PF_CONTENTS_MENU_MASK) + +#define AVRC_PF_FAVORITE_MENU_BIT_NO 12 +#define AVRC_PF_FAVORITE_MENU_MASK 0x10 +#define AVRC_PF_FAVORITE_MENU_OFF 1 +#define AVRC_PF_FAVORITE_MENU_SUPPORTED(x) ((x)[AVRC_PF_FAVORITE_MENU_OFF] & AVRC_PF_FAVORITE_MENU_MASK) + +#define AVRC_PF_EXIT_BIT_NO 13 +#define AVRC_PF_EXIT_MASK 0x20 +#define AVRC_PF_EXIT_OFF 1 +#define AVRC_PF_EXIT_SUPPORTED(x) ((x)[AVRC_PF_EXIT_OFF] & AVRC_PF_EXIT_MASK) + +#define AVRC_PF_0_BIT_NO 14 +#define AVRC_PF_0_MASK 0x40 +#define AVRC_PF_0_OFF 1 +#define AVRC_PF_0_SUPPORTED(x) ((x)[AVRC_PF_0_OFF] & AVRC_PF_0_MASK) + +#define AVRC_PF_1_BIT_NO 15 +#define AVRC_PF_1_MASK 0x80 +#define AVRC_PF_1_OFF 1 +#define AVRC_PF_1_SUPPORTED(x) ((x)[AVRC_PF_1_OFF] & AVRC_PF_1_MASK) + +#define AVRC_PF_2_BIT_NO 16 +#define AVRC_PF_2_MASK 0x01 +#define AVRC_PF_2_OFF 2 +#define AVRC_PF_2_SUPPORTED(x) ((x)[AVRC_PF_2_OFF] & AVRC_PF_2_MASK) + +#define AVRC_PF_3_BIT_NO 17 +#define AVRC_PF_3_MASK 0x02 +#define AVRC_PF_3_OFF 2 +#define AVRC_PF_3_SUPPORTED(x) ((x)[AVRC_PF_3_OFF] & AVRC_PF_3_MASK) + +#define AVRC_PF_4_BIT_NO 18 +#define AVRC_PF_4_MASK 0x04 +#define AVRC_PF_4_OFF 2 +#define AVRC_PF_4_SUPPORTED(x) ((x)[AVRC_PF_4_OFF] & AVRC_PF_4_MASK) + +#define AVRC_PF_5_BIT_NO 19 +#define AVRC_PF_5_MASK 0x08 +#define AVRC_PF_5_OFF 2 +#define AVRC_PF_5_SUPPORTED(x) ((x)[AVRC_PF_5_OFF] & AVRC_PF_5_MASK) + +#define AVRC_PF_6_BIT_NO 20 +#define AVRC_PF_6_MASK 0x10 +#define AVRC_PF_6_OFF 2 +#define AVRC_PF_6_SUPPORTED(x) ((x)[AVRC_PF_6_OFF] & AVRC_PF_6_MASK) + +#define AVRC_PF_7_BIT_NO 21 +#define AVRC_PF_7_MASK 0x20 +#define AVRC_PF_7_OFF 2 +#define AVRC_PF_7_SUPPORTED(x) ((x)[AVRC_PF_7_OFF] & AVRC_PF_7_MASK) + +#define AVRC_PF_8_BIT_NO 22 +#define AVRC_PF_8_MASK 0x40 +#define AVRC_PF_8_OFF 2 +#define AVRC_PF_8_SUPPORTED(x) ((x)[AVRC_PF_8_OFF] & AVRC_PF_8_MASK) + +#define AVRC_PF_9_BIT_NO 23 +#define AVRC_PF_9_MASK 0x80 +#define AVRC_PF_9_OFF 2 +#define AVRC_PF_9_SUPPORTED(x) ((x)[AVRC_PF_9_OFF] & AVRC_PF_9_MASK) + +#define AVRC_PF_DOT_BIT_NO 24 +#define AVRC_PF_DOT_MASK 0x01 +#define AVRC_PF_DOT_OFF 3 +#define AVRC_PF_DOT_SUPPORTED(x) ((x)[AVRC_PF_DOT_OFF] & AVRC_PF_DOT_MASK) + +#define AVRC_PF_ENTER_BIT_NO 25 +#define AVRC_PF_ENTER_MASK 0x02 +#define AVRC_PF_ENTER_OFF 3 +#define AVRC_PF_ENTER_SUPPORTED(x) ((x)[AVRC_PF_ENTER_OFF] & AVRC_PF_ENTER_MASK) + +#define AVRC_PF_CLEAR_BIT_NO 26 +#define AVRC_PF_CLEAR_MASK 0x04 +#define AVRC_PF_CLEAR_OFF 3 +#define AVRC_PF_CLEAR_SUPPORTED(x) ((x)[AVRC_PF_CLEAR_OFF] & AVRC_PF_CLEAR_MASK) + +#define AVRC_PF_CHNL_UP_BIT_NO 27 +#define AVRC_PF_CHNL_UP_MASK 0x08 +#define AVRC_PF_CHNL_UP_OFF 3 +#define AVRC_PF_CHNL_UP_SUPPORTED(x) ((x)[AVRC_PF_CHNL_UP_OFF] & AVRC_PF_CHNL_UP_MASK) + +#define AVRC_PF_CHNL_DOWN_BIT_NO 28 +#define AVRC_PF_CHNL_DOWN_MASK 0x10 +#define AVRC_PF_CHNL_DOWN_OFF 3 +#define AVRC_PF_CHNL_DOWN_SUPPORTED(x) ((x)[AVRC_PF_CHNL_DOWN_OFF] & AVRC_PF_CHNL_DOWN_MASK) + +#define AVRC_PF_PREV_CHNL_BIT_NO 29 +#define AVRC_PF_PREV_CHNL_MASK 0x20 +#define AVRC_PF_PREV_CHNL_OFF 3 +#define AVRC_PF_PREV_CHNL_SUPPORTED(x) ((x)[AVRC_PF_PREV_CHNL_OFF] & AVRC_PF_PREV_CHNL_MASK) + +#define AVRC_PF_SOUND_SEL_BIT_NO 30 +#define AVRC_PF_SOUND_SEL_MASK 0x40 +#define AVRC_PF_SOUND_SEL_OFF 3 +#define AVRC_PF_SOUND_SEL_SUPPORTED(x) ((x)[AVRC_PF_SOUND_SEL_OFF] & AVRC_PF_SOUND_SEL_MASK) + +#define AVRC_PF_INPUT_SEL_BIT_NO 31 +#define AVRC_PF_INPUT_SEL_MASK 0x80 +#define AVRC_PF_INPUT_SEL_OFF 3 +#define AVRC_PF_INPUT_SEL_SUPPORTED(x) ((x)[AVRC_PF_INPUT_SEL_OFF] & AVRC_PF_INPUT_SEL_MASK) + +#define AVRC_PF_DISP_INFO_BIT_NO 32 +#define AVRC_PF_DISP_INFO_MASK 0x01 +#define AVRC_PF_DISP_INFO_OFF 4 +#define AVRC_PF_DISP_INFO_SUPPORTED(x) ((x)[AVRC_PF_DISP_INFO_OFF] & AVRC_PF_DISP_INFO_MASK) + +#define AVRC_PF_HELP_BIT_NO 33 +#define AVRC_PF_HELP_MASK 0x02 +#define AVRC_PF_HELP_OFF 4 +#define AVRC_PF_HELP_SUPPORTED(x) ((x)[AVRC_PF_HELP_OFF] & AVRC_PF_HELP_MASK) + +#define AVRC_PF_PAGE_UP_BIT_NO 34 +#define AVRC_PF_PAGE_UP_MASK 0x04 +#define AVRC_PF_PAGE_UP_OFF 4 +#define AVRC_PF_PAGE_UP_SUPPORTED(x) ((x)[AVRC_PF_PAGE_UP_OFF] & AVRC_PF_PAGE_UP_MASK) + +#define AVRC_PF_PAGE_DOWN_BIT_NO 35 +#define AVRC_PF_PAGE_DOWN_MASK 0x08 +#define AVRC_PF_PAGE_DOWN_OFF 4 +#define AVRC_PF_PAGE_DOWN_SUPPORTED(x) ((x)[AVRC_PF_PAGE_DOWN_OFF] & AVRC_PF_PAGE_DOWN_MASK) + +#define AVRC_PF_POWER_BIT_NO 36 +#define AVRC_PF_POWER_MASK 0x10 +#define AVRC_PF_POWER_OFF 4 +#define AVRC_PF_POWER_SUPPORTED(x) ((x)[AVRC_PF_POWER_OFF] & AVRC_PF_POWER_MASK) + +#define AVRC_PF_VOL_UP_BIT_NO 37 +#define AVRC_PF_VOL_UP_MASK 0x20 +#define AVRC_PF_VOL_UP_OFF 4 +#define AVRC_PF_VOL_UP_SUPPORTED(x) ((x)[AVRC_PF_VOL_UP_OFF] & AVRC_PF_VOL_UP_MASK) + +#define AVRC_PF_VOL_DOWN_BIT_NO 38 +#define AVRC_PF_VOL_DOWN_MASK 0x40 +#define AVRC_PF_VOL_DOWN_OFF 4 +#define AVRC_PF_VOL_DOWN_SUPPORTED(x) ((x)[AVRC_PF_VOL_DOWN_OFF] & AVRC_PF_VOL_DOWN_MASK) + +#define AVRC_PF_MUTE_BIT_NO 39 +#define AVRC_PF_MUTE_MASK 0x80 +#define AVRC_PF_MUTE_OFF 4 +#define AVRC_PF_MUTE_SUPPORTED(x) ((x)[AVRC_PF_MUTE_OFF] & AVRC_PF_MUTE_MASK) + +#define AVRC_PF_PLAY_BIT_NO 40 +#define AVRC_PF_PLAY_MASK 0x01 +#define AVRC_PF_PLAY_OFF 5 +#define AVRC_PF_PLAY_SUPPORTED(x) ((x)[AVRC_PF_PLAY_OFF] & AVRC_PF_PLAY_MASK) + +#define AVRC_PF_STOP_BIT_NO 41 +#define AVRC_PF_STOP_MASK 0x02 +#define AVRC_PF_STOP_OFF 5 +#define AVRC_PF_STOP_SUPPORTED(x) ((x)[AVRC_PF_STOP_OFF] & AVRC_PF_STOP_MASK) + +#define AVRC_PF_PAUSE_BIT_NO 42 +#define AVRC_PF_PAUSE_MASK 0x04 +#define AVRC_PF_PAUSE_OFF 5 +#define AVRC_PF_PAUSE_SUPPORTED(x) ((x)[AVRC_PF_PAUSE_OFF] & AVRC_PF_PAUSE_MASK) + +#define AVRC_PF_RECORD_BIT_NO 43 +#define AVRC_PF_RECORD_MASK 0x08 +#define AVRC_PF_RECORD_OFF 5 +#define AVRC_PF_RECORD_SUPPORTED(x) ((x)[AVRC_PF_RECORD_OFF] & AVRC_PF_RECORD_MASK) + +#define AVRC_PF_REWIND_BIT_NO 44 +#define AVRC_PF_REWIND_MASK 0x10 +#define AVRC_PF_REWIND_OFF 5 +#define AVRC_PF_REWIND_SUPPORTED(x) ((x)[AVRC_PF_REWIND_OFF] & AVRC_PF_REWIND_MASK) + +#define AVRC_PF_FAST_FWD_BIT_NO 45 +#define AVRC_PF_FAST_FWD_MASK 0x20 +#define AVRC_PF_FAST_FWD_OFF 5 +#define AVRC_PF_FAST_FWD_SUPPORTED(x) ((x)[AVRC_PF_FAST_FWD_OFF] & AVRC_PF_FAST_FWD_MASK) + +#define AVRC_PF_EJECT_BIT_NO 46 +#define AVRC_PF_EJECT_MASK 0x40 +#define AVRC_PF_EJECT_OFF 5 +#define AVRC_PF_EJECT_SUPPORTED(x) ((x)[AVRC_PF_EJECT_OFF] & AVRC_PF_EJECT_MASK) + +#define AVRC_PF_FORWARD_BIT_NO 47 +#define AVRC_PF_FORWARD_MASK 0x80 +#define AVRC_PF_FORWARD_OFF 5 +#define AVRC_PF_FORWARD_SUPPORTED(x) ((x)[AVRC_PF_FORWARD_OFF] & AVRC_PF_FORWARD_MASK) + +#define AVRC_PF_BACKWARD_BIT_NO 48 +#define AVRC_PF_BACKWARD_MASK 0x01 +#define AVRC_PF_BACKWARD_OFF 6 +#define AVRC_PF_BACKWARD_SUPPORTED(x) ((x)[AVRC_PF_BACKWARD_OFF] & AVRC_PF_BACKWARD_MASK) + +#define AVRC_PF_ANGLE_BIT_NO 49 +#define AVRC_PF_ANGLE_MASK 0x02 +#define AVRC_PF_ANGLE_OFF 6 +#define AVRC_PF_ANGLE_SUPPORTED(x) ((x)[AVRC_PF_ANGLE_OFF] & AVRC_PF_ANGLE_MASK) + +#define AVRC_PF_SUBPICTURE_BIT_NO 50 +#define AVRC_PF_SUBPICTURE_MASK 0x04 +#define AVRC_PF_SUBPICTURE_OFF 6 +#define AVRC_PF_SUBPICTURE_SUPPORTED(x) ((x)[AVRC_PF_SUBPICTURE_OFF] & AVRC_PF_SUBPICTURE_MASK) + +#define AVRC_PF_F1_BIT_NO 51 +#define AVRC_PF_F1_MASK 0x08 +#define AVRC_PF_F1_OFF 6 +#define AVRC_PF_F1_SUPPORTED(x) ((x)[AVRC_PF_F1_OFF] & AVRC_PF_F1_MASK) + +#define AVRC_PF_F2_BIT_NO 52 +#define AVRC_PF_F2_MASK 0x10 +#define AVRC_PF_F2_OFF 6 +#define AVRC_PF_F2_SUPPORTED(x) ((x)[AVRC_PF_F2_OFF] & AVRC_PF_F2_MASK) + +#define AVRC_PF_F3_BIT_NO 53 +#define AVRC_PF_F3_MASK 0x20 +#define AVRC_PF_F3_OFF 6 +#define AVRC_PF_F3_SUPPORTED(x) ((x)[AVRC_PF_F3_OFF] & AVRC_PF_F3_MASK) + +#define AVRC_PF_F4_BIT_NO 54 +#define AVRC_PF_F4_MASK 0x40 +#define AVRC_PF_F4_OFF 6 +#define AVRC_PF_F4_SUPPORTED(x) ((x)[AVRC_PF_F4_OFF] & AVRC_PF_F4_MASK) + +#define AVRC_PF_F5_BIT_NO 55 +#define AVRC_PF_F5_MASK 0x80 +#define AVRC_PF_F5_OFF 6 +#define AVRC_PF_F5_SUPPORTED(x) ((x)[AVRC_PF_F5_OFF] & AVRC_PF_F5_MASK) + +/* Vendor unique. This PASSTHROUGH command is supported. */ +#define AVRC_PF_VENDOR_BIT_NO 56 +#define AVRC_PF_VENDOR_MASK 0x01 +#define AVRC_PF_VENDOR_OFF 7 +#define AVRC_PF_VENDOR_SUPPORTED(x) ((x)[AVRC_PF_VENDOR_OFF] & AVRC_PF_VENDOR_MASK) + +/* Basic Group Navigation. This overrules the SDP entry as it is set per player.7 */ +#define AVRC_PF_GROUP_NAVI_BIT_NO 57 +#define AVRC_PF_GROUP_NAVI_MASK 0x02 +#define AVRC_PF_GROUP_NAVI_OFF 7 +#define AVRC_PF_GROUP_NAVI_SUPPORTED(x) ((x)[AVRC_PF_GROUP_NAVI_OFF] & AVRC_PF_GROUP_NAVI_MASK) + +/* Advanced Control Player. This bit is set if the player supports at least AVRCP 1.4. */ +#define AVRC_PF_ADV_CTRL_BIT_NO 58 +#define AVRC_PF_ADV_CTRL_MASK 0x04 +#define AVRC_PF_ADV_CTRL_OFF 7 +#define AVRC_PF_ADV_CTRL_SUPPORTED(x) ((x)[AVRC_PF_ADV_CTRL_OFF] & AVRC_PF_ADV_CTRL_MASK) + +/* Browsing. This bit is set if the player supports browsing. */ +#define AVRC_PF_BROWSE_BIT_NO 59 +#define AVRC_PF_BROWSE_MASK 0x08 +#define AVRC_PF_BROWSE_OFF 7 +#define AVRC_PF_BROWSE_SUPPORTED(x) ((x)[AVRC_PF_BROWSE_OFF] & AVRC_PF_BROWSE_MASK) + +/* Searching. This bit is set if the player supports searching. */ +#define AVRC_PF_SEARCH_BIT_NO 60 +#define AVRC_PF_SEARCH_MASK 0x10 +#define AVRC_PF_SEARCH_OFF 7 +#define AVRC_PF_SEARCH_SUPPORTED(x) ((x)[AVRC_PF_SEARCH_OFF] & AVRC_PF_SEARCH_MASK) + +/* AddToNowPlaying. This bit is set if the player supports the AddToNowPlaying command. */ +#define AVRC_PF_ADD2NOWPLAY_BIT_NO 61 +#define AVRC_PF_ADD2NOWPLAY_MASK 0x20 +#define AVRC_PF_ADD2NOWPLAY_OFF 7 +#define AVRC_PF_ADD2NOWPLAY_SUPPORTED(x) ((x)[AVRC_PF_ADD2NOWPLAY_OFF] & AVRC_PF_ADD2NOWPLAY_MASK) + +/* UIDs unique in player browse tree. This bit is set if the player is able to maintain unique UIDs across the player browse tree. */ +#define AVRC_PF_UID_UNIQUE_BIT_NO 62 +#define AVRC_PF_UID_UNIQUE_MASK 0x40 +#define AVRC_PF_UID_UNIQUE_OFF 7 +#define AVRC_PF_UID_UNIQUE_SUPPORTED(x) ((x)[AVRC_PF_UID_UNIQUE_OFF] & AVRC_PF_UID_UNIQUE_MASK) + +/* OnlyBrowsableWhenAddressed. This bit is set if the player is only able to be browsed when it is set as the Addressed Player. */ +#define AVRC_PF_BR_WH_ADDR_BIT_NO 63 +#define AVRC_PF_BR_WH_ADDR_MASK 0x80 +#define AVRC_PF_BR_WH_ADDR_OFF 7 +#define AVRC_PF_BR_WH_ADDR_SUPPORTED(x) ((x)[AVRC_PF_BR_WH_ADDR_OFF] & AVRC_PF_BR_WH_ADDR_MASK) + +/* OnlySearchableWhenAddressed. This bit is set if the player is only able to be searched when it is set as the Addressed player. */ +#define AVRC_PF_SEARCH_WH_ADDR_BIT_NO 64 +#define AVRC_PF_SEARCH_WH_ADDR_MASK 0x01 +#define AVRC_PF_SEARCH_WH_ADDR_OFF 8 +#define AVRC_PF_SEARCH_WH_ADDR_SUPPORTED(x) ((x)[AVRC_PF_SEARCH_WH_ADDR_OFF] & AVRC_PF_SEARCH_WH_ADDR_MASK) + +/* NowPlaying. This bit is set if the player supports the NowPlaying folder. Note that for all players that support browsing this bit shall be set */ +#define AVRC_PF_NOW_PLAY_BIT_NO 65 +#define AVRC_PF_NOW_PLAY_MASK 0x02 +#define AVRC_PF_NOW_PLAY_OFF 8 +#define AVRC_PF_NOW_PLAY_SUPPORTED(x) ((x)[AVRC_PF_NOW_PLAY_OFF] & AVRC_PF_NOW_PLAY_MASK) + +/* UIDPersistency. This bit is set if the Player is able to persist UID values between AVRCP Browse Reconnect */ +#define AVRC_PF_UID_PERSIST_BIT_NO 66 +#define AVRC_PF_UID_PERSIST_MASK 0x04 +#define AVRC_PF_UID_PERSIST_OFF 8 +#define AVRC_PF_UID_PERSIST_SUPPORTED(x) ((x)[AVRC_PF_UID_PERSIST_OFF] & AVRC_PF_UID_PERSIST_MASK) + +/***************************************************************************** +** data type definitions +*****************************************************************************/ + +/* +This structure contains the header parameters of an AV/C message. +*/ +typedef struct { + UINT8 ctype; /* Command type. */ + UINT8 subunit_type; /* Subunit type. */ + UINT8 subunit_id; /* Subunit ID. This value is typically ignored in AVRCP, + * except for VENDOR DEPENDENT messages when the value is + * vendor-dependent. Value range is 0-7. */ + UINT8 opcode; /* Op Code (passthrough, vendor, etc) */ +} tAVRC_HDR; + +/* This structure contains a UNIT INFO message. */ +typedef struct { + tAVRC_HDR hdr; /* Message header. */ + UINT32 company_id; /* Company identifier. */ + UINT8 unit_type; /* Unit type. Uses the same values as subunit type. */ + UINT8 unit; /* This value is vendor dependent and typically zero. */ +} tAVRC_MSG_UNIT; + +/* This structure contains a SUBUNIT INFO message. */ +typedef struct { + tAVRC_HDR hdr; /* Message header. */ + UINT8 subunit_type[AVRC_SUB_TYPE_LEN]; + /* Array containing subunit type values. */ + BOOLEAN panel; /* TRUE if the panel subunit type is in the + * subunit_type array, FALSE otherwise. */ + UINT8 page; /* Specifies which part of the subunit type table is + * returned. For AVRCP it is typically zero. + * Value range is 0-7. */ +} tAVRC_MSG_SUB; + +/* This structure contains a VENDOR DEPENDENT message. */ +typedef struct { + tAVRC_HDR hdr; /* Message header. */ + UINT32 company_id; /* Company identifier. */ + UINT8 *p_vendor_data;/* Pointer to vendor dependent data. */ + UINT16 vendor_len; /* Length in bytes of vendor dependent data. */ +} tAVRC_MSG_VENDOR; + +/* PASS THROUGH message structure */ +typedef struct { + tAVRC_HDR hdr; /* hdr.ctype Unused. + * hdr.subunit_type Unused. + * hdr.subunit_id Unused. */ + UINT8 op_id; /* Operation ID. */ + UINT8 state; /* Keypress state. */ + UINT8 *p_pass_data;/* Pointer to data. This parameter is only valid + * when the op_id is AVRC_ID_VENDOR.*/ + UINT8 pass_len; /* Length in bytes of data. This parameter is only + * valid when the op_id is AVRC_ID_VENDOR.*/ +} tAVRC_MSG_PASS; + +/* Command/Response indicator. */ +#define AVRC_CMD AVCT_CMD /* Command message */ +#define AVRC_RSP AVCT_RSP /* Response message */ + +/* Browsing channel message structure */ +typedef struct { + tAVRC_HDR hdr; /* hdr.ctype AVRC_CMD or AVRC_RSP. + * hdr.subunit_type Unused. + * hdr.subunit_id Unused. */ + UINT8 *p_browse_data; /* Pointer to data. */ + UINT16 browse_len; /* Length in bytes of data. */ + BT_HDR *p_browse_pkt; /* The GKI buffer received. Set to NULL, if the callback function wants to keep the buffer */ +} tAVRC_MSG_BROWSE; + +/* This is a union of all message type structures. */ +typedef union { + tAVRC_HDR hdr; /* Message header. */ + tAVRC_MSG_UNIT unit; /* UNIT INFO message. */ + tAVRC_MSG_SUB sub; /* SUBUNIT INFO message. */ + tAVRC_MSG_VENDOR vendor; /* VENDOR DEPENDENT message. */ + tAVRC_MSG_PASS pass; /* PASS THROUGH message. */ + tAVRC_MSG_BROWSE browse; /* messages thru browsing channel */ +} tAVRC_MSG; + +/* macros */ +#define AVRC_IS_VALID_CAP_ID(a) (((a == AVRC_CAP_COMPANY_ID) || (a == AVRC_CAP_EVENTS_SUPPORTED)) ? TRUE : FALSE) + +#define AVRC_IS_VALID_EVENT_ID(a) (((a >= AVRC_EVT_PLAY_STATUS_CHANGE) && \ + (a <= AVRC_EVT_VOLUME_CHANGE)) ? TRUE : FALSE) + +#define AVRC_IS_VALID_ATTRIBUTE(a) (((((a > 0) && a <= AVRC_PLAYER_SETTING_SCAN)) || \ + (a >= AVRC_PLAYER_SETTING_LOW_MENU_EXT)) ? TRUE : FALSE) + +#define AVRC_IS_VALID_MEDIA_ATTRIBUTE(a) ((a >= AVRC_MEDIA_ATTR_ID_TITLE) && \ + (a <= AVRC_MEDIA_ATTR_ID_PLAYING_TIME) ? TRUE : FALSE) + +#define AVRC_IS_VALID_BATTERY_STATUS(a) ((a <= AVRC_BATTERY_STATUS_FULL_CHARGE) ? TRUE : FALSE) + +#define AVRC_IS_VALID_SYSTEM_STATUS(a) ((a <= AVRC_SYSTEMSTATE_PWR_UNPLUGGED) ? TRUE : FALSE) + +#define AVRC_IS_VALID_GROUP(a) ((a <= AVRC_PDU_PREV_GROUP) ? TRUE : FALSE) + +/* Company ID is 24-bit integer We can not use the macros in stack/bt_types.h */ +#define AVRC_CO_ID_TO_BE_STREAM(p, u32) {*(p)++ = (UINT8)((u32) >> 16); *(p)++ = (UINT8)((u32) >> 8); *(p)++ = (UINT8)(u32); } +#define AVRC_BE_STREAM_TO_CO_ID(u32, p) {u32 = (((UINT32)(*((p) + 2))) + (((UINT32)(*((p) + 1))) << 8) + (((UINT32)(*(p))) << 16)); (p) += 3;} + +/***************************************************************************** +** data type definitions +*****************************************************************************/ +#define AVRC_MAX_APP_ATTR_SIZE 16 +#define AVRC_MAX_CHARSET_SIZE 16 +#define AVRC_MAX_ELEM_ATTR_SIZE 8 + + +/***************************************************************************** +** Metadata transfer Building/Parsing definitions +*****************************************************************************/ + +typedef struct { + UINT16 charset_id; + UINT16 str_len; + UINT8 *p_str; +} tAVRC_FULL_NAME; + +typedef struct { + UINT16 str_len; + UINT8 *p_str; +} tAVRC_NAME; + + +#ifndef AVRC_CAP_MAX_NUM_COMP_ID +#define AVRC_CAP_MAX_NUM_COMP_ID 4 +#endif + +#ifndef AVRC_CAP_MAX_NUM_EVT_ID +#define AVRC_CAP_MAX_NUM_EVT_ID 16 +#endif + +typedef union { + UINT32 company_id[AVRC_CAP_MAX_NUM_COMP_ID]; + UINT8 event_id[AVRC_CAP_MAX_NUM_EVT_ID]; +} tAVRC_CAPS_PARAM; + +typedef struct { + UINT8 attr_id; + UINT8 attr_val; +} tAVRC_APP_SETTING; + +typedef struct { + UINT8 attr_id; + UINT16 charset_id; + UINT8 str_len; + UINT8 *p_str; +} tAVRC_APP_SETTING_TEXT; + +typedef UINT8 tAVRC_FEATURE_MASK[AVRC_FEATURE_MASK_SIZE]; + +typedef struct { + UINT16 player_id; /* A unique identifier for this media player.*/ + UINT8 major_type; /* Use AVRC_MJ_TYPE_AUDIO, AVRC_MJ_TYPE_VIDEO, AVRC_MJ_TYPE_BC_AUDIO, or AVRC_MJ_TYPE_BC_VIDEO.*/ + UINT32 sub_type; /* Use AVRC_SUB_TYPE_NONE, AVRC_SUB_TYPE_AUDIO_BOOK, or AVRC_SUB_TYPE_PODCAST*/ + UINT8 play_status; /* Use AVRC_PLAYSTATE_STOPPED, AVRC_PLAYSTATE_PLAYING, AVRC_PLAYSTATE_PAUSED, AVRC_PLAYSTATE_FWD_SEEK, + AVRC_PLAYSTATE_REV_SEEK, or AVRC_PLAYSTATE_ERROR*/ + tAVRC_FEATURE_MASK features; /* Supported feature bit mask*/ + tAVRC_FULL_NAME name; /* The player name, name length and character set id.*/ +} tAVRC_ITEM_PLAYER; + +typedef struct { + tAVRC_UID uid; /* The uid of this folder */ + UINT8 type; /* Use AVRC_FOLDER_TYPE_MIXED, AVRC_FOLDER_TYPE_TITLES, + AVRC_FOLDER_TYPE_ALNUMS, AVRC_FOLDER_TYPE_ARTISTS, AVRC_FOLDER_TYPE_GENRES, + AVRC_FOLDER_TYPE_PLAYLISTS, or AVRC_FOLDER_TYPE_YEARS.*/ + BOOLEAN playable; /* TRUE, if the folder can be played. */ + tAVRC_FULL_NAME name; /* The folder name, name length and character set id. */ +} tAVRC_ITEM_FOLDER; + +typedef struct { + UINT32 attr_id; /* Use AVRC_MEDIA_ATTR_ID_TITLE, AVRC_MEDIA_ATTR_ID_ARTIST, AVRC_MEDIA_ATTR_ID_ALBUM, + AVRC_MEDIA_ATTR_ID_TRACK_NUM, AVRC_MEDIA_ATTR_ID_NUM_TRACKS, + AVRC_MEDIA_ATTR_ID_GENRE, AVRC_MEDIA_ATTR_ID_PLAYING_TIME */ + tAVRC_FULL_NAME name; /* The attribute value, value length and character set id. */ +} tAVRC_ATTR_ENTRY; + +typedef struct { + tAVRC_UID uid; /* The uid of this media element item */ + UINT8 type; /* Use AVRC_MEDIA_TYPE_AUDIO or AVRC_MEDIA_TYPE_VIDEO. */ + tAVRC_FULL_NAME name; /* The media name, name length and character set id. */ + UINT8 attr_count; /* The number of attributes in p_attr_list */ + tAVRC_ATTR_ENTRY *p_attr_list; /* Attribute entry list. */ +} tAVRC_ITEM_MEDIA; + +typedef struct { + UINT8 item_type; /* AVRC_ITEM_PLAYER, AVRC_ITEM_FOLDER, or AVRC_ITEM_MEDIA */ + union { + tAVRC_ITEM_PLAYER player; /* The properties of a media player item.*/ + tAVRC_ITEM_FOLDER folder; /* The properties of a folder item.*/ + tAVRC_ITEM_MEDIA media; /* The properties of a media item.*/ + } u; +} tAVRC_ITEM; + +/* GetCapability */ +typedef struct { + UINT8 pdu; + tAVRC_STS status; + UINT8 opcode; /* Op Code (assigned by AVRC_BldCommand according to pdu) */ + UINT8 capability_id; +} tAVRC_GET_CAPS_CMD; + +/* ListPlayerAppValues */ +typedef struct { + UINT8 pdu; + tAVRC_STS status; + UINT8 opcode; /* Op Code (assigned by AVRC_BldCommand according to pdu) */ + UINT8 attr_id; +} tAVRC_LIST_APP_VALUES_CMD; + +/* GetCurAppValue */ +typedef struct { + UINT8 pdu; + tAVRC_STS status; + UINT8 opcode; /* Op Code (assigned by AVRC_BldCommand according to pdu) */ + UINT8 num_attr; + UINT8 attrs[AVRC_MAX_APP_ATTR_SIZE]; +} tAVRC_GET_CUR_APP_VALUE_CMD; + +/* SetAppValue */ +typedef struct { + UINT8 pdu; + tAVRC_STS status; + UINT8 opcode; /* Op Code (assigned by AVRC_BldCommand according to pdu) */ + UINT8 num_val; + tAVRC_APP_SETTING *p_vals; +} tAVRC_SET_APP_VALUE_CMD; + +/* GetAppAttrTxt */ +typedef struct { + UINT8 pdu; + tAVRC_STS status; + UINT8 opcode; /* Op Code (assigned by AVRC_BldCommand according to pdu) */ + UINT8 num_attr; + UINT8 attrs[AVRC_MAX_APP_ATTR_SIZE]; +} tAVRC_GET_APP_ATTR_TXT_CMD; + +/* GetAppValueTxt */ +typedef struct { + UINT8 pdu; + tAVRC_STS status; + UINT8 opcode; /* Op Code (assigned by AVRC_BldCommand according to pdu) */ + UINT8 attr_id; + UINT8 num_val; + UINT8 vals[AVRC_MAX_APP_ATTR_SIZE]; +} tAVRC_GET_APP_VAL_TXT_CMD; + +/* InformCharset */ +typedef struct { + UINT8 pdu; + tAVRC_STS status; + UINT8 opcode; /* Op Code (assigned by AVRC_BldCommand according to pdu) */ + UINT8 num_id; + UINT16 charsets[AVRC_MAX_CHARSET_SIZE]; +} tAVRC_INFORM_CHARSET_CMD; + +/* InformBatteryStatus */ +typedef struct { + UINT8 pdu; + tAVRC_STS status; + UINT8 opcode; /* Op Code (assigned by AVRC_BldCommand according to pdu) */ + UINT8 battery_status; +} tAVRC_BATTERY_STATUS_CMD; + +/* GetElemAttrs */ +typedef struct { + UINT8 pdu; + tAVRC_STS status; + UINT8 opcode; /* Op Code (assigned by AVRC_BldCommand according to pdu) */ + UINT8 num_attr; + UINT32 attrs[AVRC_MAX_ELEM_ATTR_SIZE]; +} tAVRC_GET_ELEM_ATTRS_CMD; + +/* RegNotify */ +typedef struct { + UINT8 pdu; + tAVRC_STS status; + UINT8 opcode; /* Op Code (assigned by AVRC_BldCommand according to pdu) */ + UINT8 event_id; + UINT32 param; +} tAVRC_REG_NOTIF_CMD; + +/* SetAddrPlayer */ +typedef struct { + UINT8 pdu; + tAVRC_STS status; + UINT8 opcode; /* Op Code (assigned by AVRC_BldCommand according to pdu) */ + UINT16 player_id; +} tAVRC_SET_ADDR_PLAYER_CMD; + +/* SetBrowsedPlayer */ +typedef struct { + UINT8 pdu; + tAVRC_STS status; + UINT8 opcode; /* Op Code (assigned by AVRC_BldCommand according to pdu) */ + UINT16 player_id; +} tAVRC_SET_BR_PLAYER_CMD; + +/* SetAbsVolume */ +typedef struct { + UINT8 pdu; + tAVRC_STS status; + UINT8 opcode; /* Op Code (assigned by AVRC_BldCommand according to pdu) */ + UINT8 volume; +} tAVRC_SET_VOLUME_CMD; + +/* GetFolderItems */ +typedef struct { + UINT8 pdu; + tAVRC_STS status; + UINT8 opcode; /* Op Code (assigned by AVRC_BldCommand according to pdu) */ + UINT8 scope; + UINT32 start_item; + UINT32 end_item; + UINT8 attr_count; + UINT32 *p_attr_list; +} tAVRC_GET_ITEMS_CMD; + +/* ChangePath */ +typedef struct { + UINT8 pdu; + tAVRC_STS status; + UINT8 opcode; /* Op Code (assigned by AVRC_BldCommand according to pdu) */ + UINT16 uid_counter; + UINT8 direction; + tAVRC_UID folder_uid; +} tAVRC_CHG_PATH_CMD; + +/* GetItemAttrs */ +typedef struct { + UINT8 pdu; + tAVRC_STS status; + UINT8 opcode; /* Op Code (assigned by AVRC_BldCommand according to pdu) */ + UINT8 scope; + tAVRC_UID uid; + UINT16 uid_counter; + UINT8 attr_count; + UINT32 *p_attr_list; +} tAVRC_GET_ATTRS_CMD; + +/* Search */ +typedef struct { + UINT8 pdu; + tAVRC_STS status; + UINT8 opcode; /* Op Code (assigned by AVRC_BldCommand according to pdu) */ + tAVRC_FULL_NAME string; +} tAVRC_SEARCH_CMD; + +/* PlayItem */ +typedef struct { + UINT8 pdu; + tAVRC_STS status; + UINT8 opcode; /* Op Code (assigned by AVRC_BldCommand according to pdu) */ + UINT8 scope; + tAVRC_UID uid; + UINT16 uid_counter; +} tAVRC_PLAY_ITEM_CMD; + +/* AddToNowPlaying */ +typedef struct { + UINT8 pdu; + tAVRC_STS status; + UINT8 opcode; /* Op Code (assigned by AVRC_BldCommand according to pdu) */ + UINT8 scope; + tAVRC_UID uid; + UINT16 uid_counter; +} tAVRC_ADD_TO_PLAY_CMD; + +typedef struct { + UINT8 pdu; + tAVRC_STS status; + UINT8 opcode; /* Op Code (assigned by AVRC_BldCommand according to pdu) */ +} tAVRC_CMD; + +/* Continue and Abort */ +typedef struct { + UINT8 pdu; + tAVRC_STS status; + UINT8 opcode; /* Op Code (assigned by AVRC_BldCommand according to pdu) */ + UINT8 target_pdu; +} tAVRC_NEXT_CMD; + +typedef union { + UINT8 pdu; + tAVRC_CMD cmd; + tAVRC_GET_CAPS_CMD get_caps; /* GetCapability */ + tAVRC_CMD list_app_attr; /* ListPlayerAppAttr */ + tAVRC_LIST_APP_VALUES_CMD list_app_values; /* ListPlayerAppValues */ + tAVRC_GET_CUR_APP_VALUE_CMD get_cur_app_val; /* GetCurAppValue */ + tAVRC_SET_APP_VALUE_CMD set_app_val; /* SetAppValue */ + tAVRC_GET_APP_ATTR_TXT_CMD get_app_attr_txt; /* GetAppAttrTxt */ + tAVRC_GET_APP_VAL_TXT_CMD get_app_val_txt; /* GetAppValueTxt */ + tAVRC_INFORM_CHARSET_CMD inform_charset; /* InformCharset */ + tAVRC_BATTERY_STATUS_CMD inform_battery_status; /* InformBatteryStatus */ + tAVRC_GET_ELEM_ATTRS_CMD get_elem_attrs; /* GetElemAttrs */ + tAVRC_CMD get_play_status; /* GetPlayStatus */ + tAVRC_REG_NOTIF_CMD reg_notif; /* RegNotify */ + tAVRC_NEXT_CMD continu; /* Continue */ + tAVRC_NEXT_CMD abort; /* Abort */ + + tAVRC_SET_ADDR_PLAYER_CMD addr_player; /* SetAddrPlayer */ + tAVRC_SET_VOLUME_CMD volume; /* SetAbsVolume */ + tAVRC_SET_BR_PLAYER_CMD br_player; /* SetBrowsedPlayer */ + tAVRC_GET_ITEMS_CMD get_items; /* GetFolderItems */ + tAVRC_CHG_PATH_CMD chg_path; /* ChangePath */ + tAVRC_GET_ATTRS_CMD get_attrs; /* GetItemAttrs */ + tAVRC_SEARCH_CMD search; /* Search */ + tAVRC_PLAY_ITEM_CMD play_item; /* PlayItem */ + tAVRC_ADD_TO_PLAY_CMD add_to_play; /* AddToNowPlaying */ +} tAVRC_COMMAND; + +/* GetCapability */ +typedef struct { + UINT8 pdu; + tAVRC_STS status; + UINT8 opcode; /* Op Code (copied from avrc_cmd.opcode by AVRC_BldResponse user. invalid one to generate according to pdu) */ + UINT8 capability_id; + UINT8 count; + tAVRC_CAPS_PARAM param; +} tAVRC_GET_CAPS_RSP; + +/* ListPlayerAppAttr */ +typedef struct { + UINT8 pdu; + tAVRC_STS status; + UINT8 opcode; /* Op Code (copied from avrc_cmd.opcode by AVRC_BldResponse user. invalid one to generate according to pdu) */ + UINT8 num_attr; + UINT8 attrs[AVRC_MAX_APP_ATTR_SIZE]; +} tAVRC_LIST_APP_ATTR_RSP; + +/* ListPlayerAppValues */ +typedef struct { + UINT8 pdu; + tAVRC_STS status; + UINT8 opcode; /* Op Code (copied from avrc_cmd.opcode by AVRC_BldResponse user. invalid one to generate according to pdu) */ + UINT8 num_val; + UINT8 vals[AVRC_MAX_APP_ATTR_SIZE]; +} tAVRC_LIST_APP_VALUES_RSP; + +/* GetCurAppValue */ +typedef struct { + UINT8 pdu; + tAVRC_STS status; + UINT8 opcode; /* Op Code (copied from avrc_cmd.opcode by AVRC_BldResponse user. invalid one to generate according to pdu) */ + UINT8 num_val; + tAVRC_APP_SETTING *p_vals; +} tAVRC_GET_CUR_APP_VALUE_RSP; + +/* GetAppAttrTxt */ +typedef struct { + UINT8 pdu; + tAVRC_STS status; + UINT8 opcode; /* Op Code (copied from avrc_cmd.opcode by AVRC_BldResponse user. invalid one to generate according to pdu) */ + UINT8 num_attr; + tAVRC_APP_SETTING_TEXT *p_attrs; +} tAVRC_GET_APP_ATTR_TXT_RSP; + +/* GetElemAttrs */ +typedef struct { + UINT8 pdu; + tAVRC_STS status; + UINT8 opcode; /* Op Code (copied from avrc_cmd.opcode by AVRC_BldResponse user. invalid one to generate according to pdu) */ + UINT8 num_attr; + tAVRC_ATTR_ENTRY *p_attrs; +} tAVRC_GET_ELEM_ATTRS_RSP; + +/* GetPlayStatus */ +typedef struct { + UINT8 pdu; + tAVRC_STS status; + UINT8 opcode; /* Op Code (copied from avrc_cmd.opcode by AVRC_BldResponse user. invalid one to generate according to pdu) */ + UINT32 song_len; + UINT32 song_pos; + UINT8 play_status; +} tAVRC_GET_PLAY_STATUS_RSP; + +/* notification event parameter for AddressedPlayer change */ +typedef struct { + UINT16 player_id; + UINT16 uid_counter; +} tAVRC_ADDR_PLAYER_PARAM; + +#ifndef AVRC_MAX_APP_SETTINGS +#define AVRC_MAX_APP_SETTINGS 8 +#endif + +/* notification event parameter for Player Application setting change */ +typedef struct { + UINT8 num_attr; + UINT8 attr_id[AVRC_MAX_APP_SETTINGS]; + UINT8 attr_value[AVRC_MAX_APP_SETTINGS]; +} tAVRC_PLAYER_APP_PARAM; + +typedef union { + tAVRC_PLAYSTATE play_status; + tAVRC_UID track; + UINT32 play_pos; + tAVRC_BATTERY_STATUS battery_status; + tAVRC_SYSTEMSTATE system_status; + tAVRC_PLAYER_APP_PARAM player_setting; + tAVRC_ADDR_PLAYER_PARAM addr_player; + UINT16 uid_counter; + UINT8 volume; +} tAVRC_NOTIF_RSP_PARAM; + +/* RegNotify */ +typedef struct { + UINT8 pdu; + tAVRC_STS status; + UINT8 opcode; /* Op Code (copied from avrc_cmd.opcode by AVRC_BldResponse user. invalid one to generate according to pdu) */ + UINT8 event_id; + tAVRC_NOTIF_RSP_PARAM param; +} tAVRC_REG_NOTIF_RSP; + +/* SetAbsVolume */ +typedef struct { + UINT8 pdu; + tAVRC_STS status; + UINT8 opcode; /* Op Code (copied from avrc_cmd.opcode by AVRC_BldResponse user. invalid one to generate according to pdu) */ + UINT8 volume; +} tAVRC_SET_VOLUME_RSP; + +/* SetBrowsedPlayer */ +typedef struct { + UINT8 pdu; + tAVRC_STS status; + UINT8 opcode; /* Op Code (copied from avrc_cmd.opcode by AVRC_BldResponse user. invalid one to generate according to pdu) */ + UINT16 uid_counter; + UINT32 num_items; + UINT16 charset_id; + UINT8 folder_depth; + tAVRC_NAME *p_folders; +} tAVRC_SET_BR_PLAYER_RSP; + +/* GetFolderItems */ +typedef struct { + UINT8 pdu; + tAVRC_STS status; + UINT8 opcode; /* Op Code (copied from avrc_cmd.opcode by AVRC_BldResponse user. invalid one to generate according to pdu) */ + UINT16 uid_counter; + UINT16 item_count; + tAVRC_ITEM *p_item_list; +} tAVRC_GET_ITEMS_RSP; + +/* ChangePath */ +typedef struct { + UINT8 pdu; + tAVRC_STS status; + UINT8 opcode; /* Op Code (copied from avrc_cmd.opcode by AVRC_BldResponse user. invalid one to generate according to pdu) */ + UINT32 num_items; +} tAVRC_CHG_PATH_RSP; + +/* GetItemAttrs */ +typedef struct { + UINT8 pdu; + tAVRC_STS status; + UINT8 opcode; /* Op Code (copied from avrc_cmd.opcode by AVRC_BldResponse user. invalid one to generate according to pdu) */ + UINT8 attr_count; + tAVRC_ATTR_ENTRY *p_attr_list; +} tAVRC_GET_ATTRS_RSP; + +/* Search */ +typedef struct { + UINT8 pdu; + tAVRC_STS status; + UINT8 opcode; /* Op Code (copied from avrc_cmd.opcode by AVRC_BldResponse user. invalid one to generate according to pdu) */ + UINT16 uid_counter; + UINT32 num_items; +} tAVRC_SEARCH_RSP; + + +typedef struct { + UINT8 pdu; + tAVRC_STS status; + UINT8 opcode; /* Op Code (copied from avrc_cmd.opcode by AVRC_BldResponse user. invalid one to generate according to pdu) */ +} tAVRC_RSP; + +typedef union { + UINT8 pdu; + tAVRC_RSP rsp; + tAVRC_GET_CAPS_RSP get_caps; /* GetCapability */ + tAVRC_LIST_APP_ATTR_RSP list_app_attr; /* ListPlayerAppAttr */ + tAVRC_LIST_APP_VALUES_RSP list_app_values; /* ListPlayerAppValues */ + tAVRC_GET_CUR_APP_VALUE_RSP get_cur_app_val; /* GetCurAppValue */ + tAVRC_RSP set_app_val; /* SetAppValue */ + tAVRC_GET_APP_ATTR_TXT_RSP get_app_attr_txt; /* GetAppAttrTxt */ + tAVRC_GET_APP_ATTR_TXT_RSP get_app_val_txt; /* GetAppValueTxt */ + tAVRC_RSP inform_charset; /* InformCharset */ + tAVRC_RSP inform_battery_status; /* InformBatteryStatus */ + tAVRC_GET_ELEM_ATTRS_RSP get_elem_attrs; /* GetElemAttrs */ + tAVRC_GET_PLAY_STATUS_RSP get_play_status; /* GetPlayStatus */ + tAVRC_REG_NOTIF_RSP reg_notif; /* RegNotify */ + tAVRC_RSP continu; /* Continue */ + tAVRC_RSP abort; /* Abort */ + + tAVRC_RSP addr_player; /* SetAddrPlayer */ + tAVRC_SET_VOLUME_RSP volume; /* SetAbsVolume */ + tAVRC_SET_BR_PLAYER_RSP br_player; /* SetBrowsedPlayer */ + tAVRC_GET_ITEMS_RSP get_items; /* GetFolderItems */ + tAVRC_CHG_PATH_RSP chg_path; /* ChangePath */ + tAVRC_GET_ATTRS_RSP get_attrs; /* GetItemAttrs */ + tAVRC_SEARCH_RSP search; /* Search */ + tAVRC_RSP play_item; /* PlayItem */ + tAVRC_RSP add_to_play; /* AddToNowPlaying */ +} tAVRC_RESPONSE; + +#endif ///AVRC_INCLUDED == TRUE +#endif diff --git a/lib/bt/host/bluedroid/stack/include/stack/bt_types.h b/lib/bt/host/bluedroid/stack/include/stack/bt_types.h new file mode 100644 index 00000000..be803d50 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/include/stack/bt_types.h @@ -0,0 +1,781 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +#ifndef BT_TYPES_H +#define BT_TYPES_H + +#include +#include +#include "bt_common.h" +#include "osi/semaphore.h" + +typedef int8_t INT8; +typedef int16_t INT16; +typedef int32_t INT32; + +#define PACKED __packed +// #define INLINE __inline + +#define BCM_STRCPY_S(x1,x2) strcpy((x1),(x2)) +#define BCM_STRNCPY_S(x1,x2,x3) strncpy((x1),(x2),(x3)) + +/* READ WELL !! +** +** This section defines global events. These are events that cross layers. +** Any event that passes between layers MUST be one of these events. Tasks +** can use their own events internally, but a FUNDAMENTAL design issue is +** that global events MUST be one of these events defined below. +** +** The convention used is the the event name contains the layer that the +** event is going to. +*/ +#define BT_EVT_MASK 0xFF00 +#define BT_SUB_EVT_MASK 0x00FF +#define BT_STATIC_RAND_ADDR_MASK 0xC0 +#define BT_NON_RPA_MASK 0x3F +/* To Bluetooth Upper Layers */ +/************************************/ +#define BT_EVT_TO_BTU_L2C_EVT 0x0900 /* L2CAP event */ +#define BT_EVT_TO_BTU_HCI_EVT 0x1000 /* HCI Event */ +#define BT_EVT_TO_BTU_HCI_BR_EDR_EVT (0x0000 | BT_EVT_TO_BTU_HCI_EVT) /* event from BR/EDR controller */ +#define BT_EVT_TO_BTU_HCI_AMP1_EVT (0x0001 | BT_EVT_TO_BTU_HCI_EVT) /* event from local AMP 1 controller */ +#define BT_EVT_TO_BTU_HCI_AMP2_EVT (0x0002 | BT_EVT_TO_BTU_HCI_EVT) /* event from local AMP 2 controller */ +#define BT_EVT_TO_BTU_HCI_AMP3_EVT (0x0003 | BT_EVT_TO_BTU_HCI_EVT) /* event from local AMP 3 controller */ + +#define BT_EVT_TO_BTU_HCI_ACL 0x1100 /* ACL Data from HCI */ +#define BT_EVT_TO_BTU_HCI_SCO 0x1200 /* SCO Data from HCI */ +#define BT_EVT_TO_BTU_HCIT_ERR 0x1300 /* HCI Transport Error */ + +#define BT_EVT_TO_BTU_SP_EVT 0x1400 /* Serial Port Event */ +#define BT_EVT_TO_BTU_SP_DATA 0x1500 /* Serial Port Data */ + +#define BT_EVT_TO_BTU_HCI_CMD 0x1600 /* HCI command from upper layer */ + + +#define BT_EVT_TO_BTU_L2C_SEG_XMIT 0x1900 /* L2CAP segment(s) transmitted */ + +#define BT_EVT_PROXY_INCOMING_MSG 0x1A00 /* BlueStackTester event: incoming message from target */ + +#define BT_EVT_BTSIM 0x1B00 /* Insight BTSIM event */ +#define BT_EVT_BTISE 0x1C00 /* Insight Script Engine event */ + +/* To LM */ +/************************************/ +#define BT_EVT_TO_LM_HCI_CMD 0x2000 /* HCI Command */ +#define BT_EVT_TO_LM_HCI_ACL 0x2100 /* HCI ACL Data */ +#define BT_EVT_TO_LM_HCI_SCO 0x2200 /* HCI SCO Data */ +#define BT_EVT_TO_LM_HCIT_ERR 0x2300 /* HCI Transport Error */ +#define BT_EVT_TO_LM_LC_EVT 0x2400 /* LC event */ +#define BT_EVT_TO_LM_LC_LMP 0x2500 /* LC Received LMP command frame */ +#define BT_EVT_TO_LM_LC_ACL 0x2600 /* LC Received ACL data */ +#define BT_EVT_TO_LM_LC_SCO 0x2700 /* LC Received SCO data (not used) */ +#define BT_EVT_TO_LM_LC_ACL_TX 0x2800 /* LMP data transmit complete */ +#define BT_EVT_TO_LM_LC_LMPC_TX 0x2900 /* LMP Command transmit complete */ +#define BT_EVT_TO_LM_LOCAL_ACL_LB 0x2a00 /* Data to be locally loopbacked */ +#define BT_EVT_TO_LM_HCI_ACL_ACK 0x2b00 /* HCI ACL Data ack (not used) */ +#define BT_EVT_TO_LM_DIAG 0x2c00 /* LM Diagnostics commands */ + + +#define BT_EVT_TO_BTM_CMDS 0x2f00 +#define BT_EVT_TO_BTM_PM_MDCHG_EVT (0x0001 | BT_EVT_TO_BTM_CMDS) + +#define BT_EVT_TO_TCS_CMDS 0x3000 + +#define BT_EVT_TO_CTP_CMDS 0x3300 + +/* ftp events */ +#define BT_EVT_TO_FTP_SRVR_CMDS 0x3600 +#define BT_EVT_TO_FTP_CLNT_CMDS 0x3700 + +#define BT_EVT_TO_BTU_SAP 0x3800 /* SIM Access Profile events */ + +/* opp events */ +#define BT_EVT_TO_OPP_SRVR_CMDS 0x3900 +#define BT_EVT_TO_OPP_CLNT_CMDS 0x3a00 + +/* gap events */ +#define BT_EVT_TO_GAP_MSG 0x3b00 + +/* for NFC */ +/************************************/ +#define BT_EVT_TO_NFC_NCI 0x4000 /* NCI Command, Notification or Data*/ +#define BT_EVT_TO_NFC_INIT 0x4100 /* Initialization message */ +#define BT_EVT_TO_NCI_LP 0x4200 /* Low power */ +#define BT_EVT_TO_NFC_ERR 0x4300 /* Error notification to NFC Task */ + +#define BT_EVT_TO_NFCCSIM_NCI 0x4a00 /* events to NFCC simulation (NCI packets) */ + +/* HCISU Events */ + +#define BT_EVT_HCISU 0x5000 + +// btla-specific ++ +#define BT_EVT_TO_HCISU_RECONFIG_EVT (0x0001 | BT_EVT_HCISU) +#define BT_EVT_TO_HCISU_UPDATE_BAUDRATE_EVT (0x0002 | BT_EVT_HCISU) +#define BT_EVT_TO_HCISU_LP_ENABLE_EVT (0x0003 | BT_EVT_HCISU) +#define BT_EVT_TO_HCISU_LP_DISABLE_EVT (0x0004 | BT_EVT_HCISU) +// btla-specific -- +#define BT_EVT_TO_HCISU_LP_APP_SLEEPING_EVT (0x0005 | BT_EVT_HCISU) +#define BT_EVT_TO_HCISU_LP_ALLOW_BT_SLEEP_EVT (0x0006 | BT_EVT_HCISU) +#define BT_EVT_TO_HCISU_LP_WAKEUP_HOST_EVT (0x0007 | BT_EVT_HCISU) +#define BT_EVT_TO_HCISU_LP_RCV_H4IBSS_EVT (0x0008 | BT_EVT_HCISU) +#define BT_EVT_TO_HCISU_H5_RESET_EVT (0x0009 | BT_EVT_HCISU) +#define BT_EVT_HCISU_START_QUICK_TIMER (0x000a | BT_EVT_HCISU) + +#define BT_EVT_DATA_TO_AMP_1 0x5100 +#define BT_EVT_DATA_TO_AMP_15 0x5f00 + +/* HSP Events */ + +#define BT_EVT_BTU_HSP2 0x6000 + +#define BT_EVT_TO_BTU_HSP2_EVT (0x0001 | BT_EVT_BTU_HSP2) + +/* BPP Events */ +#define BT_EVT_TO_BPP_PR_CMDS 0x6100 /* Printer Events */ +#define BT_EVT_TO_BPP_SND_CMDS 0x6200 /* BPP Sender Events */ + +/* BIP Events */ +#define BT_EVT_TO_BIP_CMDS 0x6300 + +/* HCRP Events */ + +#define BT_EVT_BTU_HCRP 0x7000 + +#define BT_EVT_TO_BTU_HCRP_EVT (0x0001 | BT_EVT_BTU_HCRP) +#define BT_EVT_TO_BTU_HCRPM_EVT (0x0002 | BT_EVT_BTU_HCRP) + + +#define BT_EVT_BTU_HFP 0x8000 +#define BT_EVT_TO_BTU_HFP_EVT (0x0001 | BT_EVT_BTU_HFP) + +#define BT_EVT_BTU_IPC_EVT 0x9000 +#define BT_EVT_BTU_IPC_LOGMSG_EVT (0x0000 | BT_EVT_BTU_IPC_EVT) +#define BT_EVT_BTU_IPC_ACL_EVT (0x0001 | BT_EVT_BTU_IPC_EVT) +#define BT_EVT_BTU_IPC_BTU_EVT (0x0002 | BT_EVT_BTU_IPC_EVT) +#define BT_EVT_BTU_IPC_L2C_EVT (0x0003 | BT_EVT_BTU_IPC_EVT) +#define BT_EVT_BTU_IPC_L2C_MSG_EVT (0x0004 | BT_EVT_BTU_IPC_EVT) +#define BT_EVT_BTU_IPC_BTM_EVT (0x0005 | BT_EVT_BTU_IPC_EVT) +#define BT_EVT_BTU_IPC_AVDT_EVT (0x0006 | BT_EVT_BTU_IPC_EVT) +#define BT_EVT_BTU_IPC_SLIP_EVT (0x0007 | BT_EVT_BTU_IPC_EVT) +#define BT_EVT_BTU_IPC_MGMT_EVT (0x0008 | BT_EVT_BTU_IPC_EVT) +#define BT_EVT_BTU_IPC_BTTRC_EVT (0x0009 | BT_EVT_BTU_IPC_EVT) +#define BT_EVT_BTU_IPC_BURST_EVT (0x000A | BT_EVT_BTU_IPC_EVT) + + +/* BTIF Events */ +#define BT_EVT_BTIF 0xA000 +#define BT_EVT_CONTEXT_SWITCH_EVT (0x0001 | BT_EVT_BTIF) + +/* Define the header of each buffer used in the Bluetooth stack. +*/ +typedef struct { + uint16_t event; + uint16_t len; + uint16_t offset; + uint16_t layer_specific; + uint8_t data[]; +} BT_HDR; + +typedef struct { + uint8_t status; + uint16_t opcode; + osi_sem_t sync_sem; +} BlE_SYNC; + + +#define BT_HDR_SIZE (sizeof (BT_HDR)) + +#define BT_PSM_SDP 0x0001 +#define BT_PSM_RFCOMM 0x0003 +#define BT_PSM_TCS 0x0005 +#define BT_PSM_CTP 0x0007 +#define BT_PSM_BNEP 0x000F +#define BT_PSM_HIDC 0x0011 +#define BT_PSM_HIDI 0x0013 +#define BT_PSM_UPNP 0x0015 +#define BT_PSM_AVCTP 0x0017 +#define BT_PSM_AVDTP 0x0019 +#define BT_PSM_AVCTP_13 0x001B /* Advanced Control - Browsing */ +#define BT_PSM_UDI_CP 0x001D /* Unrestricted Digital Information Profile C-Plane */ +#define BT_PSM_ATT 0x001F /* Attribute Protocol */ + + +/* These macros extract the HCI opcodes from a buffer +*/ +#define HCI_GET_CMD_HDR_OPCODE(p) (UINT16)((*((UINT8 *)((p) + 1) + p->offset) + \ + (*((UINT8 *)((p) + 1) + p->offset + 1) << 8))) +#define HCI_GET_CMD_HDR_PARAM_LEN(p) (UINT8) (*((UINT8 *)((p) + 1) + p->offset + 2)) + +#define HCI_GET_EVT_HDR_OPCODE(p) (UINT8)(*((UINT8 *)((p) + 1) + p->offset)) +#define HCI_GET_EVT_HDR_PARAM_LEN(p) (UINT8) (*((UINT8 *)((p) + 1) + p->offset + 1)) + + +/******************************************************************************** +** Macros to get and put bytes to and from a stream (Little Endian format). +*/ +#define UINT32_TO_STREAM(p, u32) {*(p)++ = (UINT8)(u32); *(p)++ = (UINT8)((u32) >> 8); *(p)++ = (UINT8)((u32) >> 16); *(p)++ = (UINT8)((u32) >> 24);} +#define UINT24_TO_STREAM(p, u24) {*(p)++ = (UINT8)(u24); *(p)++ = (UINT8)((u24) >> 8); *(p)++ = (UINT8)((u24) >> 16);} +#define UINT16_TO_STREAM(p, u16) {*(p)++ = (UINT8)(u16); *(p)++ = (UINT8)((u16) >> 8);} +#define UINT8_TO_STREAM(p, u8) {*(p)++ = (UINT8)(u8);} +#define INT8_TO_STREAM(p, u8) {*(p)++ = (INT8)(u8);} +#define ARRAY32_TO_STREAM(p, a) {register int ijk; for (ijk = 0; ijk < 32; ijk++) *(p)++ = (UINT8) a[31 - ijk];} +#define ARRAY16_TO_STREAM(p, a) {register int ijk; for (ijk = 0; ijk < 16; ijk++) *(p)++ = (UINT8) a[15 - ijk];} +#define ARRAY8_TO_STREAM(p, a) {register int ijk; for (ijk = 0; ijk < 8; ijk++) *(p)++ = (UINT8) a[7 - ijk];} +#define BDADDR_TO_STREAM(p, a) {register int ijk; for (ijk = 0; ijk < BD_ADDR_LEN; ijk++) *(p)++ = (UINT8) a[BD_ADDR_LEN - 1 - ijk];} +#define LAP_TO_STREAM(p, a) {register int ijk; for (ijk = 0; ijk < LAP_LEN; ijk++) *(p)++ = (UINT8) a[LAP_LEN - 1 - ijk];} +#define DEVCLASS_TO_STREAM(p, a) {register int ijk; for (ijk = 0; ijk < DEV_CLASS_LEN;ijk++) *(p)++ = (UINT8) a[DEV_CLASS_LEN - 1 - ijk];} +#define ARRAY_TO_STREAM(p, a, len) {register int ijk; for (ijk = 0; ijk < len; ijk++) *(p)++ = (UINT8) a[ijk];} +#define REVERSE_ARRAY_TO_STREAM(p, a, len) {register int ijk; for (ijk = 0; ijk < len; ijk++) *(p)++ = (UINT8) a[len - 1 - ijk];} + +#define STREAM_TO_UINT8(u8, p) {u8 = (UINT8)(*(p)); (p) += 1;} +#define STREAM_TO_UINT16(u16, p) {u16 = ((UINT16)(*(p)) + (((UINT16)(*((p) + 1))) << 8)); (p) += 2;} +#define STREAM_TO_UINT24(u32, p) {u32 = (((UINT32)(*(p))) + ((((UINT32)(*((p) + 1)))) << 8) + ((((UINT32)(*((p) + 2)))) << 16) ); (p) += 3;} +#define STREAM_TO_UINT32(u32, p) {u32 = (((UINT32)(*(p))) + ((((UINT32)(*((p) + 1)))) << 8) + ((((UINT32)(*((p) + 2)))) << 16) + ((((UINT32)(*((p) + 3)))) << 24)); (p) += 4;} +#define STREAM_TO_BDADDR(a, p) {register int ijk; register UINT8 *pbda = (UINT8 *)a + BD_ADDR_LEN - 1; for (ijk = 0; ijk < BD_ADDR_LEN; ijk++) *pbda-- = *p++;} +#define STREAM_TO_ARRAY32(a, p) {register int ijk; register UINT8 *_pa = (UINT8 *)a + 31; for (ijk = 0; ijk < 32; ijk++) *_pa-- = *p++;} +#define STREAM_TO_ARRAY16(a, p) {register int ijk; register UINT8 *_pa = (UINT8 *)a + 15; for (ijk = 0; ijk < 16; ijk++) *_pa-- = *p++;} +#define STREAM_TO_ARRAY8(a, p) {register int ijk; register UINT8 *_pa = (UINT8 *)a + 7; for (ijk = 0; ijk < 8; ijk++) *_pa-- = *p++;} +#define STREAM_TO_DEVCLASS(a, p) {register int ijk; register UINT8 *_pa = (UINT8 *)a + DEV_CLASS_LEN - 1; for (ijk = 0; ijk < DEV_CLASS_LEN; ijk++) *_pa-- = *p++;} +#define STREAM_TO_LAP(a, p) {register int ijk; register UINT8 *plap = (UINT8 *)a + LAP_LEN - 1; for (ijk = 0; ijk < LAP_LEN; ijk++) *plap-- = *p++;} +#define STREAM_TO_ARRAY(a, p, len) {register int ijk; for (ijk = 0; ijk < len; ijk++) ((UINT8 *) a)[ijk] = *p++;} +#define REVERSE_STREAM_TO_ARRAY(a, p, len) {register int ijk; register UINT8 *_pa = (UINT8 *)a + len - 1; for (ijk = 0; ijk < len; ijk++) *_pa-- = *p++;} + +#define STREAM_SKIP_UINT8(p) do { (p) += 1; } while (0) +#define STREAM_SKIP_UINT16(p) do { (p) += 2; } while (0) + +/******************************************************************************** +** Macros to get and put bytes to and from a field (Little Endian format). +** These are the same as to stream, except the pointer is not incremented. +*/ +#define UINT32_TO_FIELD(p, u32) {*(UINT8 *)(p) = (UINT8)(u32); *((UINT8 *)(p)+1) = (UINT8)((u32) >> 8); *((UINT8 *)(p)+2) = (UINT8)((u32) >> 16); *((UINT8 *)(p)+3) = (UINT8)((u32) >> 24);} +#define UINT24_TO_FIELD(p, u24) {*(UINT8 *)(p) = (UINT8)(u24); *((UINT8 *)(p)+1) = (UINT8)((u24) >> 8); *((UINT8 *)(p)+2) = (UINT8)((u24) >> 16);} +#define UINT16_TO_FIELD(p, u16) {*(UINT8 *)(p) = (UINT8)(u16); *((UINT8 *)(p)+1) = (UINT8)((u16) >> 8);} +#define UINT8_TO_FIELD(p, u8) {*(UINT8 *)(p) = (UINT8)(u8);} + + +/******************************************************************************** +** Macros to get and put bytes to and from a stream (Big Endian format) +*/ +#define UINT64_TO_BE_STREAM(p, u64) {*(p)++ = (UINT8)((u64) >> 56); *(p)++ = (UINT8)((u64) >> 48); *(p)++ = (UINT8)((u64) >> 40); *(p)++ = (UINT8)((u64) >> 32); *(p)++ = (UINT8)((u64) >> 24); *(p)++ = (UINT8)((u64) >> 16); *(p)++ = (UINT8)((u64) >> 8); *(p)++ = (UINT8)(u64); } +#define UINT32_TO_BE_STREAM(p, u32) {*(p)++ = (UINT8)((u32) >> 24); *(p)++ = (UINT8)((u32) >> 16); *(p)++ = (UINT8)((u32) >> 8); *(p)++ = (UINT8)(u32); } +#define UINT24_TO_BE_STREAM(p, u24) {*(p)++ = (UINT8)((u24) >> 16); *(p)++ = (UINT8)((u24) >> 8); *(p)++ = (UINT8)(u24);} +#define UINT16_TO_BE_STREAM(p, u16) {*(p)++ = (UINT8)((u16) >> 8); *(p)++ = (UINT8)(u16);} +#define UINT8_TO_BE_STREAM(p, u8) {*(p)++ = (UINT8)(u8);} +#define ARRAY_TO_BE_STREAM(p, a, len) {register int ijk; for (ijk = 0; ijk < len; ijk++) *(p)++ = (UINT8) a[ijk];} +#define ARRAY_TO_BE_STREAM_REVERSE(p, a, len) {register int ijk; for (ijk = 0; ijk < len; ijk++) *(p)++ = (UINT8) a[len - ijk - 1];} + +#define BE_STREAM_TO_UINT8(u8, p) {u8 = (UINT8)(*(p)); (p) += 1;} +#define BE_STREAM_TO_UINT16(u16, p) {u16 = (UINT16)(((UINT16)(*(p)) << 8) + (UINT16)(*((p) + 1))); (p) += 2;} +#define BE_STREAM_TO_UINT24(u32, p) {u32 = (((UINT32)(*((p) + 2))) + ((UINT32)(*((p) + 1)) << 8) + ((UINT32)(*(p)) << 16)); (p) += 3;} +#define BE_STREAM_TO_UINT32(u32, p) {u32 = ((UINT32)(*((p) + 3)) + ((UINT32)(*((p) + 2)) << 8) + ((UINT32)(*((p) + 1)) << 16) + ((UINT32)(*(p)) << 24)); (p) += 4;} +#define BE_STREAM_TO_ARRAY(p, a, len) {register int ijk; for (ijk = 0; ijk < len; ijk++) ((UINT8 *) a)[ijk] = *p++;} + + +/******************************************************************************** +** Macros to get and put bytes to and from a field (Big Endian format). +** These are the same as to stream, except the pointer is not incremented. +*/ +#define UINT32_TO_BE_FIELD(p, u32) {*(UINT8 *)(p) = (UINT8)((u32) >> 24); *((UINT8 *)(p)+1) = (UINT8)((u32) >> 16); *((UINT8 *)(p)+2) = (UINT8)((u32) >> 8); *((UINT8 *)(p)+3) = (UINT8)(u32); } +#define UINT24_TO_BE_FIELD(p, u24) {*(UINT8 *)(p) = (UINT8)((u24) >> 16); *((UINT8 *)(p)+1) = (UINT8)((u24) >> 8); *((UINT8 *)(p)+2) = (UINT8)(u24);} +#define UINT16_TO_BE_FIELD(p, u16) {*(UINT8 *)(p) = (UINT8)((u16) >> 8); *((UINT8 *)(p)+1) = (UINT8)(u16);} +#define UINT8_TO_BE_FIELD(p, u8) {*(UINT8 *)(p) = (UINT8)(u8);} + + +/* Common Bluetooth field definitions */ +typedef UINT8 *BD_ADDR_PTR; /* Pointer to Device Address */ + +#define AMP_KEY_TYPE_GAMP 0 +#define AMP_KEY_TYPE_WIFI 1 +#define AMP_KEY_TYPE_UWB 2 +typedef UINT8 tAMP_KEY_TYPE; + +#define BT_OCTET8_LEN 8 +typedef UINT8 BT_OCTET8[BT_OCTET8_LEN]; /* octet array: size 16 */ + +#define LINK_KEY_LEN 16 +typedef UINT8 LINK_KEY[LINK_KEY_LEN]; /* Link Key */ + +#define AMP_LINK_KEY_LEN 32 +typedef UINT8 AMP_LINK_KEY[AMP_LINK_KEY_LEN]; /* Dedicated AMP and GAMP Link Keys */ + +#define BT_OCTET16_LEN 16 +typedef UINT8 BT_OCTET16[BT_OCTET16_LEN]; /* octet array: size 16 */ + +#define PIN_CODE_LEN 16 +typedef UINT8 PIN_CODE[PIN_CODE_LEN]; /* Pin Code (upto 128 bits) MSB is 0 */ +typedef UINT8 *PIN_CODE_PTR; /* Pointer to Pin Code */ + +#define BT_OCTET32_LEN 32 +typedef UINT8 BT_OCTET32[BT_OCTET32_LEN]; /* octet array: size 32 */ + +#define DEV_CLASS_LEN 3 +typedef UINT8 DEV_CLASS[DEV_CLASS_LEN]; /* Device class */ +typedef UINT8 *DEV_CLASS_PTR; /* Pointer to Device class */ + +#define EXT_INQ_RESP_LEN 3 +typedef UINT8 EXT_INQ_RESP[EXT_INQ_RESP_LEN];/* Extended Inquiry Response */ +typedef UINT8 *EXT_INQ_RESP_PTR; /* Pointer to Extended Inquiry Response */ + +#define BD_NAME_LEN 248 +typedef UINT8 BD_NAME[BD_NAME_LEN + 1]; /* Device name */ +typedef UINT8 *BD_NAME_PTR; /* Pointer to Device name */ + +#define BD_FEATURES_LEN 8 +typedef UINT8 BD_FEATURES[BD_FEATURES_LEN]; /* LMP features supported by device */ + +#define BT_EVENT_MASK_LEN 8 +typedef UINT8 BT_EVENT_MASK[BT_EVENT_MASK_LEN]; /* Event Mask */ + +#define LAP_LEN 3 +typedef UINT8 LAP[LAP_LEN]; /* IAC as passed to Inquiry (LAP) */ +typedef UINT8 INQ_LAP[LAP_LEN]; /* IAC as passed to Inquiry (LAP) */ + +#define RAND_NUM_LEN 16 +typedef UINT8 RAND_NUM[RAND_NUM_LEN]; + +#define ACO_LEN 12 +typedef UINT8 ACO[ACO_LEN]; /* Authenticated ciphering offset */ + +#define COF_LEN 12 +typedef UINT8 COF[COF_LEN]; /* ciphering offset number */ + +#define AFH_CHANNELS_LEN 10 +typedef UINT8 AFH_CHANNELS[AFH_CHANNELS_LEN]; +#define BLE_CHANNELS_LEN 5 +typedef UINT8 BLE_CHANNELS[BLE_CHANNELS_LEN]; + +typedef struct { + UINT8 qos_flags; /* TBD */ + UINT8 service_type; /* see below */ + UINT32 token_rate; /* bytes/second */ + UINT32 token_bucket_size; /* bytes */ + UINT32 peak_bandwidth; /* bytes/second */ + UINT32 latency; /* microseconds */ + UINT32 delay_variation; /* microseconds */ +} FLOW_SPEC; + +/* Values for service_type */ +#define NO_TRAFFIC 0 +#define BEST_EFFORT 1 +#define GUARANTEED 2 + +/* Service class of the CoD */ +#define SERV_CLASS_NETWORKING (1 << 1) +#define SERV_CLASS_RENDERING (1 << 2) +#define SERV_CLASS_CAPTURING (1 << 3) +#define SERV_CLASS_OBJECT_TRANSFER (1 << 4) +#define SERV_CLASS_OBJECT_AUDIO (1 << 5) +#define SERV_CLASS_OBJECT_TELEPHONY (1 << 6) +#define SERV_CLASS_OBJECT_INFORMATION (1 << 7) + +/* Second byte */ +#define SERV_CLASS_LIMITED_DISC_MODE (0x20) + +/* Field size definitions. Note that byte lengths are rounded up. */ +#define ACCESS_CODE_BIT_LEN 72 +#define ACCESS_CODE_BYTE_LEN 9 +#define SHORTENED_ACCESS_CODE_BIT_LEN 68 + +typedef UINT8 ACCESS_CODE[ACCESS_CODE_BYTE_LEN]; + +#define SYNTH_TX 1 /* want synth code to TRANSMIT at this freq */ +#define SYNTH_RX 2 /* want synth code to RECEIVE at this freq */ + +#define SYNC_REPS 1 /* repeats of sync word transmitted to start of burst */ + +/* Bluetooth CLK27 */ +#define BT_CLK27 (2 << 26) + +/* Bluetooth CLK12 is 1.28 sec */ +#define BT_CLK12_TO_MS(x) ((x) * 1280) +#define BT_MS_TO_CLK12(x) ((x) / 1280) +#define BT_CLK12_TO_SLOTS(x) ((x) << 11) + +/* Bluetooth CLK is 0.625 msec */ +#define BT_CLK_TO_MS(x) (((x) * 5 + 3) / 8) +#define BT_MS_TO_CLK(x) (((x) * 8 + 2) / 5) + +#define BT_CLK_TO_MICROSECS(x) (((x) * 5000 + 3) / 8) +#define BT_MICROSECS_TO_CLK(x) (((x) * 8 + 2499) / 5000) + +#define BT_EIR_FLAGS_TYPE 0x01 +#define BT_EIR_MORE_16BITS_UUID_TYPE 0x02 +#define BT_EIR_COMPLETE_16BITS_UUID_TYPE 0x03 +#define BT_EIR_MORE_32BITS_UUID_TYPE 0x04 +#define BT_EIR_COMPLETE_32BITS_UUID_TYPE 0x05 +#define BT_EIR_MORE_128BITS_UUID_TYPE 0x06 +#define BT_EIR_COMPLETE_128BITS_UUID_TYPE 0x07 +#define BT_EIR_SHORTENED_LOCAL_NAME_TYPE 0x08 +#define BT_EIR_COMPLETE_LOCAL_NAME_TYPE 0x09 +#define BT_EIR_TX_POWER_LEVEL_TYPE 0x0A +#define BT_EIR_OOB_BD_ADDR_TYPE 0x0C +#define BT_EIR_OOB_COD_TYPE 0x0D +#define BT_EIR_OOB_SSP_HASH_C_TYPE 0x0E +#define BT_EIR_OOB_SSP_RAND_R_TYPE 0x0F +#define BT_EIR_URL_TYPE 0x24 +#define BT_EIR_MANUFACTURER_SPECIFIC_TYPE 0xFF + +#define BT_OOB_COD_SIZE 3 +#define BT_OOB_HASH_C_SIZE 16 +#define BT_OOB_RAND_R_SIZE 16 + +/* Broadcom proprietary UUIDs and reserved PSMs +** +** The lowest 4 bytes byte of the UUID or GUID depends on the feature. Typically, +** the value of those bytes will be the PSM or SCN, but it is up to the features. +*/ +#define BRCM_PROPRIETARY_UUID_BASE 0xDA, 0x23, 0x41, 0x02, 0xA3, 0xBB, 0xC1, 0x71, 0xBA, 0x09, 0x6f, 0x21 +#define BRCM_PROPRIETARY_GUID_BASE 0xda23, 0x4102, 0xa3, 0xbb, 0xc1, 0x71, 0xba, 0x09, 0x6f, 0x21 + +/* We will not allocate a PSM in the reserved range to 3rd party apps +*/ +#define BRCM_RESERVED_PSM_START 0x5AE1 +#define BRCM_RESERVED_PSM_END 0x5AFF + +#define BRCM_UTILITY_SERVICE_PSM 0x5AE1 +#define BRCM_MATCHER_PSM 0x5AE3 + +/* Connection statistics +*/ + +/* Structure to hold connection stats */ +#ifndef BT_CONN_STATS_DEFINED +#define BT_CONN_STATS_DEFINED + +/* These bits are used in the bIsConnected field */ +#define BT_CONNECTED_USING_BREDR 1 +#define BT_CONNECTED_USING_AMP 2 + +typedef struct { + UINT32 is_connected; + INT32 rssi; + UINT32 bytes_sent; + UINT32 bytes_rcvd; + UINT32 duration; +} tBT_CONN_STATS; + +#endif + + +/***************************************************************************** +** Low Energy definitions +** +** Address types +*/ +#define BLE_ADDR_PUBLIC 0x00 +#define BLE_ADDR_RANDOM 0x01 +#define BLE_ADDR_PUBLIC_ID 0x02 +#define BLE_ADDR_RANDOM_ID 0x03 +#define BLE_ADDR_TYPE_MAX BLE_ADDR_RANDOM_ID +#define BLE_ADDR_UNKNOWN_TYPE 0XFF +typedef UINT8 tBLE_ADDR_TYPE; +#define BLE_ADDR_TYPE_MASK (BLE_ADDR_RANDOM | BLE_ADDR_PUBLIC) + +#define BT_TRANSPORT_INVALID 0 +#define BT_TRANSPORT_BR_EDR 1 +#define BT_TRANSPORT_LE 2 +typedef UINT8 tBT_TRANSPORT; + +#define BLE_ADDR_IS_STATIC(x) ((x[0] & 0xC0) == 0xC0) + +typedef struct { + tBLE_ADDR_TYPE type; + BD_ADDR bda; +} tBLE_BD_ADDR; + +/* Device Types +*/ +#define BT_DEVICE_TYPE_BREDR 0x01 +#define BT_DEVICE_TYPE_BLE 0x02 +#define BT_DEVICE_TYPE_DUMO 0x03 +typedef UINT8 tBT_DEVICE_TYPE; +/*****************************************************************************/ + +/* Define New Trace Type Definition */ +/* TRACE_CTRL_TYPE 0x^^000000*/ +#define TRACE_CTRL_MASK 0xff000000 +#define TRACE_GET_CTRL(x) ((((UINT32)(x)) & TRACE_CTRL_MASK) >> 24) + +#define TRACE_CTRL_GENERAL 0x00000000 +#define TRACE_CTRL_STR_RESOURCE 0x01000000 +#define TRACE_CTRL_SEQ_FLOW 0x02000000 +#define TRACE_CTRL_MAX_NUM 3 + +/* LAYER SPECIFIC 0x00^^0000*/ +#define TRACE_LAYER_MASK 0x00ff0000 +#define TRACE_GET_LAYER(x) ((((UINT32)(x)) & TRACE_LAYER_MASK) >> 16) + +#define TRACE_LAYER_NONE 0x00000000 +#define TRACE_LAYER_USB 0x00010000 +#define TRACE_LAYER_SERIAL 0x00020000 +#define TRACE_LAYER_SOCKET 0x00030000 +#define TRACE_LAYER_RS232 0x00040000 +#define TRACE_LAYER_TRANS_MAX_NUM 5 +#define TRACE_LAYER_TRANS_ALL 0x007f0000 +#define TRACE_LAYER_LC 0x00050000 +#define TRACE_LAYER_LM 0x00060000 +#define TRACE_LAYER_HCI 0x00070000 +#define TRACE_LAYER_L2CAP 0x00080000 +#define TRACE_LAYER_RFCOMM 0x00090000 +#define TRACE_LAYER_SDP 0x000a0000 +#define TRACE_LAYER_TCS 0x000b0000 +#define TRACE_LAYER_OBEX 0x000c0000 +#define TRACE_LAYER_BTM 0x000d0000 +#define TRACE_LAYER_GAP 0x000e0000 +#define TRACE_LAYER_ICP 0x00110000 +#define TRACE_LAYER_HSP2 0x00120000 +#define TRACE_LAYER_SPP 0x00130000 +#define TRACE_LAYER_CTP 0x00140000 +#define TRACE_LAYER_BPP 0x00150000 +#define TRACE_LAYER_HCRP 0x00160000 +#define TRACE_LAYER_FTP 0x00170000 +#define TRACE_LAYER_OPP 0x00180000 +#define TRACE_LAYER_BTU 0x00190000 +#define TRACE_LAYER_GKI 0x001a0000 +#define TRACE_LAYER_BNEP 0x001b0000 +#define TRACE_LAYER_PAN 0x001c0000 +#define TRACE_LAYER_HFP 0x001d0000 +#define TRACE_LAYER_HID 0x001e0000 +#define TRACE_LAYER_BIP 0x001f0000 +#define TRACE_LAYER_AVP 0x00200000 +#define TRACE_LAYER_A2D 0x00210000 +#define TRACE_LAYER_SAP 0x00220000 +#define TRACE_LAYER_AMP 0x00230000 +#define TRACE_LAYER_MCA 0x00240000 +#define TRACE_LAYER_ATT 0x00250000 +#define TRACE_LAYER_SMP 0x00260000 +#define TRACE_LAYER_NFC 0x00270000 +#define TRACE_LAYER_NCI 0x00280000 +#define TRACE_LAYER_LLCP 0x00290000 +#define TRACE_LAYER_NDEF 0x002a0000 +#define TRACE_LAYER_RW 0x002b0000 +#define TRACE_LAYER_CE 0x002c0000 +#define TRACE_LAYER_P2P 0x002d0000 +#define TRACE_LAYER_SNEP 0x002e0000 +#define TRACE_LAYER_CHO 0x002f0000 +#define TRACE_LAYER_NFA 0x00300000 + +#define TRACE_LAYER_MAX_NUM 0x0031 + + +/* TRACE_ORIGINATOR 0x0000^^00*/ +#define TRACE_ORG_MASK 0x0000ff00 +#define TRACE_GET_ORG(x) ((((UINT32)(x)) & TRACE_ORG_MASK) >> 8) + +#define TRACE_ORG_STACK 0x00000000 +#define TRACE_ORG_HCI_TRANS 0x00000100 +#define TRACE_ORG_PROTO_DISP 0x00000200 +#define TRACE_ORG_RPC 0x00000300 +#define TRACE_ORG_GKI 0x00000400 +#define TRACE_ORG_APPL 0x00000500 +#define TRACE_ORG_SCR_WRAPPER 0x00000600 +#define TRACE_ORG_SCR_ENGINE 0x00000700 +#define TRACE_ORG_USER_SCR 0x00000800 +#define TRACE_ORG_TESTER 0x00000900 +#define TRACE_ORG_MAX_NUM 10 /* 32-bit mask; must be < 32 */ +#define TRACE_LITE_ORG_MAX_NUM 6 +#define TRACE_ORG_ALL 0x03ff +#define TRACE_ORG_RPC_TRANS 0x04 + +#define TRACE_ORG_REG 0x00000909 +#define TRACE_ORG_REG_SUCCESS 0x0000090a + +/* TRACE_TYPE 0x000000^^*/ +#define TRACE_TYPE_MASK 0x000000ff +#define TRACE_GET_TYPE(x) (((UINT32)(x)) & TRACE_TYPE_MASK) + +#define TRACE_TYPE_ERROR 0x00000000 +#define TRACE_TYPE_WARNING 0x00000001 +#define TRACE_TYPE_API 0x00000002 +#define TRACE_TYPE_EVENT 0x00000003 +#define TRACE_TYPE_DEBUG 0x00000004 +#define TRACE_TYPE_STACK_ONLY_MAX TRACE_TYPE_DEBUG +#define TRACE_TYPE_TX 0x00000005 +#define TRACE_TYPE_RX 0x00000006 +#define TRACE_TYPE_DEBUG_ASSERT 0x00000007 +#define TRACE_TYPE_GENERIC 0x00000008 +#define TRACE_TYPE_REG 0x00000009 +#define TRACE_TYPE_REG_SUCCESS 0x0000000a +#define TRACE_TYPE_CMD_TX 0x0000000b +#define TRACE_TYPE_EVT_TX 0x0000000c +#define TRACE_TYPE_ACL_TX 0x0000000d +#define TRACE_TYPE_CMD_RX 0x0000000e +#define TRACE_TYPE_EVT_RX 0x0000000f +#define TRACE_TYPE_ACL_RX 0x00000010 +#define TRACE_TYPE_TARGET_TRACE 0x00000011 +#define TRACE_TYPE_SCO_TX 0x00000012 +#define TRACE_TYPE_SCO_RX 0x00000013 + + +#define TRACE_TYPE_MAX_NUM 20 +#define TRACE_TYPE_ALL 0xffff + +/* Define color for script type */ +#define SCR_COLOR_DEFAULT 0 +#define SCR_COLOR_TYPE_COMMENT 1 +#define SCR_COLOR_TYPE_COMMAND 2 +#define SCR_COLOR_TYPE_EVENT 3 +#define SCR_COLOR_TYPE_SELECT 4 + +/* Define protocol trace flag values */ +#define SCR_PROTO_TRACE_HCI_SUMMARY 0x00000001 +#define SCR_PROTO_TRACE_HCI_DATA 0x00000002 +#define SCR_PROTO_TRACE_L2CAP 0x00000004 +#define SCR_PROTO_TRACE_RFCOMM 0x00000008 +#define SCR_PROTO_TRACE_SDP 0x00000010 +#define SCR_PROTO_TRACE_TCS 0x00000020 +#define SCR_PROTO_TRACE_OBEX 0x00000040 +#define SCR_PROTO_TRACE_OAPP 0x00000080 /* OBEX Application Profile */ +#define SCR_PROTO_TRACE_AMP 0x00000100 +#define SCR_PROTO_TRACE_BNEP 0x00000200 +#define SCR_PROTO_TRACE_AVP 0x00000400 +#define SCR_PROTO_TRACE_MCA 0x00000800 +#define SCR_PROTO_TRACE_ATT 0x00001000 +#define SCR_PROTO_TRACE_SMP 0x00002000 +#define SCR_PROTO_TRACE_NCI 0x00004000 +#define SCR_PROTO_TRACE_LLCP 0x00008000 +#define SCR_PROTO_TRACE_NDEF 0x00010000 +#define SCR_PROTO_TRACE_RW 0x00020000 +#define SCR_PROTO_TRACE_CE 0x00040000 +#define SCR_PROTO_TRACE_SNEP 0x00080000 +#define SCR_PROTO_TRACE_CHO 0x00100000 +#define SCR_PROTO_TRACE_ALL 0x001fffff +#define SCR_PROTO_TRACE_HCI_LOGGING_VSE 0x0800 /* Brcm vs event for logmsg and protocol traces */ + +#define MAX_SCRIPT_TYPE 5 + +#define TCS_PSM_INTERCOM 5 +#define TCS_PSM_CORDLESS 7 +#define BT_PSM_BNEP 0x000F +/* Define PSMs HID uses */ +#define HID_PSM_CONTROL 0x0011 +#define HID_PSM_INTERRUPT 0x0013 + +/* Define a function for logging */ +typedef void (BT_LOG_FUNC) (int trace_type, const char *fmt_str, ...); + +/* bd addr length and type */ +#ifndef BD_ADDR_LEN +#define BD_ADDR_LEN 6 +typedef uint8_t BD_ADDR[BD_ADDR_LEN]; +#endif + +// From bd.c + +/***************************************************************************** +** Constants +*****************************************************************************/ + +/* global constant for "any" bd addr */ +static const BD_ADDR bd_addr_any = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; +static const BD_ADDR bd_addr_null = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +/***************************************************************************** +** Functions +*****************************************************************************/ + +/******************************************************************************* +** +** Function bdcpy +** +** Description Copy bd addr b to a. +** +** +** Returns void +** +*******************************************************************************/ +static inline void bdcpy(BD_ADDR a, const BD_ADDR b) +{ + int i; + + for (i = BD_ADDR_LEN; i != 0; i--) { + *a++ = *b++; + } +} + +/******************************************************************************* +** +** Function bdcmp +** +** Description Compare bd addr b to a. +** +** +** Returns Zero if b==a, nonzero otherwise (like memcmp). +** +*******************************************************************************/ +static inline int bdcmp(const BD_ADDR a, const BD_ADDR b) +{ + int i; + + for (i = BD_ADDR_LEN; i != 0; i--) { + if (*a++ != *b++) { + return -1; + } + } + return 0; +} + +/******************************************************************************* +** +** Function bdcmpany +** +** Description Compare bd addr to "any" bd addr. +** +** +** Returns Zero if a equals bd_addr_any. +** +*******************************************************************************/ +static inline int bdcmpany(const BD_ADDR a) +{ + return bdcmp(a, bd_addr_any); +} + +/******************************************************************************* +** +** Function bdsetany +** +** Description Set bd addr to "any" bd addr. +** +** +** Returns void +** +*******************************************************************************/ +static inline void bdsetany(BD_ADDR a) +{ + bdcpy(a, bd_addr_any); +} + +/******************************************************************************* +** +** Function bt_rcopy +** +** Description memory reverse and copy. +** +** +** Returns void +** +*******************************************************************************/ +static inline void bt_rcopy(UINT8 *dst, UINT8 const *src, UINT16 len) +{ + src += len; + while (len --) { + *dst++ = *--src; + } +} +#endif diff --git a/lib/bt/host/bluedroid/stack/include/stack/btm_api.h b/lib/bt/host/bluedroid/stack/include/stack/btm_api.h new file mode 100644 index 00000000..24024505 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/include/stack/btm_api.h @@ -0,0 +1,4363 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains the Bluetooth Manager (BTM) API function external + * definitions. + * + ******************************************************************************/ +#ifndef BTM_API_H +#define BTM_API_H + +#include "common/bt_defs.h" +#include "common/bt_target.h" +#include "stack/hcidefs.h" + +#if SDP_INCLUDED == TRUE +#include "stack/sdp_api.h" +#endif + +#if SMP_INCLUDED == TRUE +#include "stack/smp_api.h" +#endif +/***************************************************************************** +** DEVICE CONTROL and COMMON +*****************************************************************************/ +/***************************** +** Device Control Constants +******************************/ +/* Maximum number of bytes allowed for vendor specific command parameters */ +#define BTM_MAX_VENDOR_SPECIFIC_LEN HCI_COMMAND_SIZE + +/* BTM application return status codes */ +enum { + BTM_SUCCESS = 0, /* 0 Command succeeded */ + BTM_CMD_STARTED, /* 1 Command started OK. */ + BTM_BUSY, /* 2 Device busy with another command */ + BTM_NO_RESOURCES, /* 3 No resources to issue command */ + BTM_MODE_UNSUPPORTED, /* 4 Request for 1 or more unsupported modes */ + BTM_ILLEGAL_VALUE, /* 5 Illegal parameter value */ + BTM_WRONG_MODE, /* 6 Device in wrong mode for request */ + BTM_UNKNOWN_ADDR, /* 7 Unknown remote BD address */ + BTM_DEVICE_TIMEOUT, /* 8 Device timeout */ + BTM_BAD_VALUE_RET, /* 9 A bad value was received from HCI */ + BTM_ERR_PROCESSING, /* 10 Generic error */ + BTM_NOT_AUTHORIZED, /* 11 Authorization failed */ + BTM_DEV_RESET, /* 12 Device has been reset */ + BTM_CMD_STORED, /* 13 request is stored in control block */ + BTM_ILLEGAL_ACTION, /* 14 state machine gets illegal command */ + BTM_DELAY_CHECK, /* 15 delay the check on encryption */ + BTM_SCO_BAD_LENGTH, /* 16 Bad SCO over HCI data length */ + BTM_SUCCESS_NO_SECURITY, /* 17 security passed, no security set */ + BTM_FAILED_ON_SECURITY, /* 18 security failed */ + BTM_REPEATED_ATTEMPTS, /* 19 repeated attempts for LE security requests */ + BTM_MODE4_LEVEL4_NOT_SUPPORTED, /* 20 Secure Connections Only Mode can't be supported */ + BTM_PEER_LE_DATA_LEN_UNSUPPORTED, /* 21 peer setting data length is unsupported*/ + BTM_CONTROL_LE_DATA_LEN_UNSUPPORTED,/* 22 controller setting data length is unsupported*/ + BTM_SET_PRIVACY_SUCCESS, /* 23 enable/disable local privacy success */ + BTM_SET_PRIVACY_FAIL, /* 24 enable/disable local privacy failed*/ + BTM_SET_STATIC_RAND_ADDR_FAIL, /* 25 Command failed */ + BTM_INVALID_STATIC_RAND_ADDR, /* 26 invalid static rand addr */ + BTM_SEC_DEV_REC_REMOVED, /* 27 Device record relate to the bd_addr is removed */ +}; + +typedef uint8_t tBTM_STATUS; + +#if (defined(BTA_HOST_INTERLEAVE_SEARCH) && BTA_HOST_INTERLEAVE_SEARCH == TRUE) +typedef enum { + BTM_BR_ONE, /*0 First state or BR/EDR scan 1*/ + BTM_BLE_ONE, /*1BLE scan 1*/ + BTM_BR_TWO, /*2 BR/EDR scan 2*/ + BTM_BLE_TWO, /*3 BLE scan 2*/ + BTM_FINISH, /*4 End of Interleave Scan, or normal scan*/ + BTM_NO_INTERLEAVING /*5 No Interleaving*/ +} btm_inq_state; +#endif + + + +/************************* +** Device Control Types +**************************/ +#define BTM_DEVICE_ROLE_BR 0x01 +#define BTM_DEVICE_ROLE_DUAL 0x02 +#define BTM_MAX_DEVICE_ROLE BTM_DEVICE_ROLE_DUAL +typedef UINT8 tBTM_DEVICE_ROLE; + +/* Device name of peer (may be truncated to save space in BTM database) */ +typedef UINT8 tBTM_BD_NAME[BTM_MAX_REM_BD_NAME_LEN + 1]; + +/* Structure returned with local version information */ +typedef struct { + UINT8 hci_version; + UINT16 hci_revision; + UINT8 lmp_version; + UINT16 manufacturer; + UINT16 lmp_subversion; +} tBTM_VERSION_INFO; + +/* Structure returned with Vendor Specific Command complete callback */ +typedef struct { + UINT16 opcode; + UINT16 param_len; + UINT8 *p_param_buf; +} tBTM_VSC_CMPL; + +#define BTM_VSC_CMPL_DATA_SIZE (BTM_MAX_VENDOR_SPECIFIC_LEN + sizeof(tBTM_VSC_CMPL)) +/************************************************** +** Device Control and General Callback Functions +***************************************************/ +/* Callback function for when device status changes. Appl must poll for +** what the new state is (BTM_IsDeviceUp). The event occurs whenever the stack +** has detected that the controller status has changed. This asynchronous event +** is enabled/disabled by calling BTM_RegisterForDeviceStatusNotif(). +*/ +enum { + BTM_DEV_STATUS_UP, + BTM_DEV_STATUS_DOWN, + BTM_DEV_STATUS_CMD_TOUT +}; + +typedef UINT8 tBTM_DEV_STATUS; + +typedef struct { + UINT16 rx_len; + UINT16 tx_len; +}tBTM_LE_SET_PKT_DATA_LENGTH_PARAMS; + +typedef struct { + UINT16 min_conn_int; + UINT16 max_conn_int; + UINT16 conn_int; + UINT16 slave_latency; + UINT16 supervision_tout; +}tBTM_LE_UPDATE_CONN_PRAMS; + +typedef enum{ + BTM_WHITELIST_REMOVE = 0X00, + BTM_WHITELIST_ADD = 0X01, + BTM_WHITELIST_CLEAR = 0x02, +}tBTM_WL_OPERATION; + + +typedef void (tBTM_DEV_STATUS_CB) (tBTM_DEV_STATUS status); + +typedef void (tBTM_GET_DEV_NAME_CBACK) (UINT8 status, char *name); + +/* Callback function for when a vendor specific event occurs. The length and +** array of returned parameter bytes are included. This asynchronous event +** is enabled/disabled by calling BTM_RegisterForVSEvents(). +*/ +typedef void (tBTM_VS_EVT_CB) (UINT8 len, UINT8 *p); + + +/* General callback function for notifying an application that a synchronous +** BTM function is complete. The pointer contains the address of any returned data. +*/ +typedef void (tBTM_CMPL_CB) (void *p1); + +typedef void (tBTM_INQ_DIS_CB) (uint32_t num_dis); + +/* VSC callback function for notifying an application that a synchronous +** BTM function is complete. The pointer contains the address of any returned data. +*/ +typedef void (tBTM_VSC_CMPL_CB) (tBTM_VSC_CMPL *p1); + +/* Callback for apps to check connection and inquiry filters. +** Parameters are the BD Address of remote and the Dev Class of remote. +** If the app returns none zero, the connection or inquiry result will be dropped. +*/ +typedef UINT8 (tBTM_FILTER_CB) (BD_ADDR bd_addr, DEV_CLASS dc); + +typedef void (tBTM_UPDATE_CONN_PARAM_CBACK) (UINT8 status, BD_ADDR bd_addr, tBTM_LE_UPDATE_CONN_PRAMS *update_conn_params); + +typedef void (tBTM_SET_PKT_DATA_LENGTH_CBACK) (UINT8 status, tBTM_LE_SET_PKT_DATA_LENGTH_PARAMS *data_length_params); + +typedef void (tBTM_DTM_CMD_CMPL_CBACK) (void *p1); + +typedef void (tBTM_SET_RAND_ADDR_CBACK) (UINT8 status); + +typedef void (tBTM_UPDATE_WHITELIST_CBACK) (UINT8 status, tBTM_WL_OPERATION wl_opration); + +typedef void (tBTM_SET_LOCAL_PRIVACY_CBACK) (UINT8 status); + +/******************************* +** Device Coexist status +********************************/ +#if (ESP_COEX_VSC_INCLUDED == TRUE) +// coexist status for MESH +#define BTM_COEX_BLE_ST_MESH_CONFIG 0x08 +#define BTM_COEX_BLE_ST_MESH_TRAFFIC 0x10 +#define BTM_COEX_BLE_ST_MESH_STANDBY 0x20 +// coexist status for A2DP +#define BTM_COEX_BT_ST_A2DP_STREAMING 0x10 +#define BTM_COEX_BT_ST_A2DP_PAUSED 0x20 + +// coexist operation +#define BTM_COEX_OP_CLEAR 0x00 +#define BTM_COEX_OP_SET 0x01 +typedef UINT8 tBTM_COEX_OPERATION; + +typedef enum { + BTM_COEX_TYPE_BLE = 1, + BTM_COEX_TYPE_BT, +} tBTM_COEX_TYPE; +#endif + +/***************************************************************************** +** DEVICE DISCOVERY - Inquiry, Remote Name, Discovery, Class of Device +*****************************************************************************/ +/******************************* +** Device Discovery Constants +********************************/ +/* Discoverable modes */ +#define BTM_NON_DISCOVERABLE 0 +#define BTM_LIMITED_DISCOVERABLE 1 +#define BTM_GENERAL_DISCOVERABLE 2 +#define BTM_DISCOVERABLE_MASK (BTM_LIMITED_DISCOVERABLE|BTM_GENERAL_DISCOVERABLE) +#define BTM_MAX_DISCOVERABLE BTM_GENERAL_DISCOVERABLE +/* high byte for BLE Discoverable modes */ +#define BTM_BLE_NON_DISCOVERABLE 0x0000 +#define BTM_BLE_LIMITED_DISCOVERABLE 0x0100 +#define BTM_BLE_GENERAL_DISCOVERABLE 0x0200 +#define BTM_BLE_MAX_DISCOVERABLE BTM_BLE_GENERAL_DISCOVERABLE +#define BTM_BLE_DISCOVERABLE_MASK (BTM_BLE_NON_DISCOVERABLE|BTM_BLE_LIMITED_DISCOVERABLE|BTM_BLE_GENERAL_DISCOVERABLE) + +/* Connectable modes */ +#define BTM_NON_CONNECTABLE 0 +#define BTM_CONNECTABLE 1 +#define BTM_CONNECTABLE_MASK (BTM_NON_CONNECTABLE | BTM_CONNECTABLE) +/* high byte for BLE Connectable modes */ +#define BTM_BLE_NON_CONNECTABLE 0x0000 +#define BTM_BLE_CONNECTABLE 0x0100 +#define BTM_BLE_MAX_CONNECTABLE BTM_BLE_CONNECTABLE +#define BTM_BLE_CONNECTABLE_MASK (BTM_BLE_NON_CONNECTABLE | BTM_BLE_CONNECTABLE) + +/* Inquiry modes + * Note: These modes are associated with the inquiry active values (BTM_*ACTIVE) */ +#define BTM_INQUIRY_NONE 0 +#define BTM_GENERAL_INQUIRY 0x01 +#define BTM_LIMITED_INQUIRY 0x02 +#define BTM_BR_INQUIRY_MASK (BTM_GENERAL_INQUIRY | BTM_LIMITED_INQUIRY) + +/* high byte of inquiry mode for BLE inquiry mode */ +#define BTM_BLE_INQUIRY_NONE 0x00 +#define BTM_BLE_GENERAL_INQUIRY 0x10 +#define BTM_BLE_LIMITED_INQUIRY 0x20 +#define BTM_BLE_INQUIRY_MASK (BTM_BLE_GENERAL_INQUIRY|BTM_BLE_LIMITED_INQUIRY) + +/* BTM_IsInquiryActive return values (Bit Mask) + * Note: These bit masks are associated with the inquiry modes (BTM_*_INQUIRY) */ +#define BTM_INQUIRY_INACTIVE 0x0 /* no inquiry in progress */ +#define BTM_GENERAL_INQUIRY_ACTIVE BTM_GENERAL_INQUIRY /* a general inquiry is in progress */ +#define BTM_LIMITED_INQUIRY_ACTIVE BTM_LIMITED_INQUIRY /* a limited inquiry is in progress */ +#define BTM_PERIODIC_INQUIRY_ACTIVE 0x8 /* a periodic inquiry is active */ +#define BTM_SSP_INQUIRY_ACTIVE 0x4 /* SSP is active, so inquiry is disallowed (work around for FW bug) */ +#define BTM_LE_GENERAL_INQUIRY_ACTIVE BTM_BLE_GENERAL_INQUIRY /* a general inquiry is in progress */ +#define BTM_LE_LIMITED_INQUIRY_ACTIVE BTM_BLE_LIMITED_INQUIRY /* a limited inquiry is in progress */ + +/* inquiry activity mask */ +#define BTM_BR_INQ_ACTIVE_MASK (BTM_GENERAL_INQUIRY_ACTIVE|BTM_LIMITED_INQUIRY_ACTIVE|BTM_PERIODIC_INQUIRY_ACTIVE) /* BR/EDR inquiry activity mask */ +#define BTM_BLE_SCAN_ACTIVE_MASK 0x01F0 /* LE scan activity mask */ +#define BTM_BLE_INQ_ACTIVE_MASK (BTM_LE_GENERAL_INQUIRY_ACTIVE|BTM_LE_LIMITED_INQUIRY_ACTIVE) /* LE inquiry activity mask*/ +#define BTM_INQUIRY_ACTIVE_MASK (BTM_BR_INQ_ACTIVE_MASK | BTM_BLE_INQ_ACTIVE_MASK) /* inquiry activity mask */ + +/* Define scan types */ +#define BTM_SCAN_TYPE_STANDARD 0 +#define BTM_SCAN_TYPE_INTERLACED 1 /* 1.2 devices only */ + +/* Define inquiry results mode */ +#define BTM_INQ_RESULT_STANDARD 0 +#define BTM_INQ_RESULT_WITH_RSSI 1 +#define BTM_INQ_RESULT_EXTENDED 2 + +#define BTM_INQ_RES_IGNORE_RSSI 0x7f /* RSSI value not supplied (ignore it) */ + +/* Inquiry Filter Condition types (see tBTM_INQ_PARMS) */ +#define BTM_CLR_INQUIRY_FILTER 0 /* Inquiry Filtering is turned off */ +#define BTM_FILTER_COND_DEVICE_CLASS HCI_FILTER_COND_DEVICE_CLASS /* Filter on device class */ +#define BTM_FILTER_COND_BD_ADDR HCI_FILTER_COND_BD_ADDR /* Filter on device addr */ + +/* State of the remote name retrieval during inquiry operations. +** Used in the tBTM_INQ_INFO structure, and returned in the +** BTM_InqDbRead, BTM_InqDbFirst, and BTM_InqDbNext functions. +** The name field is valid when the state returned is +** BTM_INQ_RMT_NAME_DONE */ +#define BTM_INQ_RMT_NAME_EMPTY 0 +#define BTM_INQ_RMT_NAME_PENDING 1 +#define BTM_INQ_RMT_NAME_DONE 2 +#define BTM_INQ_RMT_NAME_FAILED 3 + +/********************************* + *** Class of Device constants *** + *********************************/ +#define BTM_FORMAT_TYPE_1 0x00 + +/**************************** +** minor device class field +*****************************/ + +/* 0x00 is used as unclassified for all minor device classes */ +#define BTM_COD_MINOR_UNCLASSIFIED 0x00 + +/* minor device class field for Computer Major Class */ +/* #define BTM_COD_MINOR_UNCLASSIFIED 0x00 */ +#define BTM_COD_MINOR_DESKTOP_WORKSTATION 0x04 +#define BTM_COD_MINOR_SERVER_COMPUTER 0x08 +#define BTM_COD_MINOR_LAPTOP 0x0C +#define BTM_COD_MINOR_HANDHELD_PC_PDA 0x10 /* clam shell */ +#define BTM_COD_MINOR_PALM_SIZE_PC_PDA 0x14 +#define BTM_COD_MINOR_WEARABLE_COMPUTER 0x18 /* watch sized */ + +/* minor device class field for Phone Major Class */ +/* #define BTM_COD_MINOR_UNCLASSIFIED 0x00 */ +#define BTM_COD_MINOR_CELLULAR 0x04 +#define BTM_COD_MINOR_CORDLESS 0x08 +#define BTM_COD_MINOR_SMART_PHONE 0x0C +#define BTM_COD_MINOR_WIRED_MDM_V_GTWY 0x10 /* wired modem or voice gatway */ +#define BTM_COD_MINOR_ISDN_ACCESS 0x14 + +/* minor device class field for LAN Access Point Major Class */ +/* Load Factor Field bit 5-7 */ +#define BTM_COD_MINOR_FULLY_AVAILABLE 0x00 +#define BTM_COD_MINOR_1_17_UTILIZED 0x20 +#define BTM_COD_MINOR_17_33_UTILIZED 0x40 +#define BTM_COD_MINOR_33_50_UTILIZED 0x60 +#define BTM_COD_MINOR_50_67_UTILIZED 0x80 +#define BTM_COD_MINOR_67_83_UTILIZED 0xA0 +#define BTM_COD_MINOR_83_99_UTILIZED 0xC0 +#define BTM_COD_MINOR_NO_SERVICE_AVAILABLE 0xE0 +/* sub-Field bit 2-4 */ +/* #define BTM_COD_MINOR_UNCLASSIFIED 0x00 */ + +/* minor device class field for Audio/Video Major Class */ +/* #define BTM_COD_MINOR_UNCLASSIFIED 0x00 */ +#define BTM_COD_MINOR_CONFM_HEADSET 0x04 +#define BTM_COD_MINOR_CONFM_HANDSFREE 0x08 +#define BTM_COD_MINOR_MICROPHONE 0x10 +#define BTM_COD_MINOR_LOUDSPEAKER 0x14 +#define BTM_COD_MINOR_HEADPHONES 0x18 +#define BTM_COD_MINOR_PORTABLE_AUDIO 0x1C +#define BTM_COD_MINOR_CAR_AUDIO 0x20 +#define BTM_COD_MINOR_SET_TOP_BOX 0x24 +#define BTM_COD_MINOR_HIFI_AUDIO 0x28 +#define BTM_COD_MINOR_VCR 0x2C +#define BTM_COD_MINOR_VIDEO_CAMERA 0x30 +#define BTM_COD_MINOR_CAMCORDER 0x34 +#define BTM_COD_MINOR_VIDEO_MONITOR 0x38 +#define BTM_COD_MINOR_VIDDISP_LDSPKR 0x3C +#define BTM_COD_MINOR_VIDEO_CONFERENCING 0x40 +#define BTM_COD_MINOR_GAMING_TOY 0x48 + +/* minor device class field for Peripheral Major Class */ +/* Bits 6-7 independently specify mouse, keyboard, or combo mouse/keyboard */ +#define BTM_COD_MINOR_KEYBOARD 0x40 +#define BTM_COD_MINOR_POINTING 0x80 +#define BTM_COD_MINOR_COMBO 0xC0 +/* Bits 2-5 OR'd with selection from bits 6-7 */ +/* #define BTM_COD_MINOR_UNCLASSIFIED 0x00 */ +#define BTM_COD_MINOR_JOYSTICK 0x04 +#define BTM_COD_MINOR_GAMEPAD 0x08 +#define BTM_COD_MINOR_REMOTE_CONTROL 0x0C +#define BTM_COD_MINOR_SENSING_DEVICE 0x10 +#define BTM_COD_MINOR_DIGITIZING_TABLET 0x14 +#define BTM_COD_MINOR_CARD_READER 0x18 /* e.g. SIM card reader */ +#define BTM_COD_MINOR_DIGITAL_PAN 0x1C +#define BTM_COD_MINOR_HAND_SCANNER 0x20 +#define BTM_COD_MINOR_HAND_GESTURAL_INPUT 0x24 + +/* minor device class field for Imaging Major Class */ +/* Bits 5-7 independently specify display, camera, scanner, or printer */ +#define BTM_COD_MINOR_DISPLAY 0x10 +#define BTM_COD_MINOR_CAMERA 0x20 +#define BTM_COD_MINOR_SCANNER 0x40 +#define BTM_COD_MINOR_PRINTER 0x80 +/* Bits 2-3 Reserved */ +/* #define BTM_COD_MINOR_UNCLASSIFIED 0x00 */ + +/* minor device class field for Wearable Major Class */ +/* Bits 2-7 meaningful */ +#define BTM_COD_MINOR_WRIST_WATCH 0x04 +#define BTM_COD_MINOR_PAGER 0x08 +#define BTM_COD_MINOR_JACKET 0x0C +#define BTM_COD_MINOR_HELMET 0x10 +#define BTM_COD_MINOR_GLASSES 0x14 + +/* minor device class field for Toy Major Class */ +/* Bits 2-7 meaningful */ +#define BTM_COD_MINOR_ROBOT 0x04 +#define BTM_COD_MINOR_VEHICLE 0x08 +#define BTM_COD_MINOR_DOLL_ACTION_FIGURE 0x0C +#define BTM_COD_MINOR_CONTROLLER 0x10 +#define BTM_COD_MINOR_GAME 0x14 + +/* minor device class field for Health Major Class */ +/* Bits 2-7 meaningful */ +#define BTM_COD_MINOR_BLOOD_MONITOR 0x04 +#define BTM_COD_MINOR_THERMOMETER 0x08 +#define BTM_COD_MINOR_WEIGHING_SCALE 0x0C +#define BTM_COD_MINOR_GLUCOSE_METER 0x10 +#define BTM_COD_MINOR_PULSE_OXIMETER 0x14 +#define BTM_COD_MINOR_HEART_PULSE_MONITOR 0x18 +#define BTM_COD_MINOR_HEALTH_DATA_DISPLAY 0x1C +#define BTM_COD_MINOR_STEP_COUNTER 0x20 +#define BTM_COD_MINOR_BODY_COM_ANALYZER 0x24 +#define BTM_COD_MINOR_PEAK_FLOW_MONITOR 0x28 +#define BTM_COD_MINOR_MEDICATION_MONITOR 0x2C +#define BTM_COD_MINOR_KNEE_PROSTHESIS 0x30 +#define BTM_COD_MINOR_ANKLE_PROSTHESIS 0x34 + + +/*************************** +** major device class field +****************************/ +#define BTM_COD_MAJOR_MISCELLANEOUS 0x00 +#define BTM_COD_MAJOR_COMPUTER 0x01 +#define BTM_COD_MAJOR_PHONE 0x02 +#define BTM_COD_MAJOR_LAN_ACCESS_PT 0x03 +#define BTM_COD_MAJOR_AUDIO 0x04 +#define BTM_COD_MAJOR_PERIPHERAL 0x05 +#define BTM_COD_MAJOR_IMAGING 0x06 +#define BTM_COD_MAJOR_WEARABLE 0x07 +#define BTM_COD_MAJOR_TOY 0x08 +#define BTM_COD_MAJOR_HEALTH 0x09 +#define BTM_COD_MAJOR_UNCLASSIFIED 0x1F + +/*************************** +** service class fields +****************************/ +#define BTM_COD_SERVICE_LMTD_DISCOVER 0x0020 +#define BTM_COD_SERVICE_POSITIONING 0x0100 +#define BTM_COD_SERVICE_NETWORKING 0x0200 +#define BTM_COD_SERVICE_RENDERING 0x0400 +#define BTM_COD_SERVICE_CAPTURING 0x0800 +#define BTM_COD_SERVICE_OBJ_TRANSFER 0x1000 +#define BTM_COD_SERVICE_AUDIO 0x2000 +#define BTM_COD_SERVICE_TELEPHONY 0x4000 +#define BTM_COD_SERVICE_INFORMATION 0x8000 + +/* class of device field macros */ +#define BTM_COD_FORMAT_TYPE(u8, pd) {u8 = pd[2]&0x03;} +#define BTM_COD_MINOR_CLASS(u8, pd) {u8 = pd[2]&0xFC;} +#define BTM_COD_MAJOR_CLASS(u8, pd) {u8 = pd[1]&0x1F;} +#define BTM_COD_SERVICE_CLASS(u16, pd) {u16 = pd[0]; u16<<=8; u16 += pd[1]&0xE0;} + +/* to set the fields (assumes that format type is always 0) */ +#define FIELDS_TO_COD(pd, mn, mj, sv) {pd[2] = mn; pd[1] = \ + mj+ ((sv)&BTM_COD_SERVICE_CLASS_LO_B); \ + pd[0] = (sv) >> 8;} + +/* the COD masks */ +#define BTM_COD_FORMAT_TYPE_MASK 0x03 +#define BTM_COD_MINOR_CLASS_MASK 0xFC +#define BTM_COD_MAJOR_CLASS_MASK 0x1F +#define BTM_COD_SERVICE_CLASS_LO_B 0x00E0 +#define BTM_COD_SERVICE_CLASS_MASK 0xFFE0 + + +/* BTM service definitions +** Used for storing EIR data to bit mask +*/ +enum { + BTM_EIR_UUID_SERVCLASS_SERVICE_DISCOVERY_SERVER, + /* BTM_EIR_UUID_SERVCLASS_BROWSE_GROUP_DESCRIPTOR, */ + /* BTM_EIR_UUID_SERVCLASS_PUBLIC_BROWSE_GROUP, */ + BTM_EIR_UUID_SERVCLASS_SERIAL_PORT, + BTM_EIR_UUID_SERVCLASS_LAN_ACCESS_USING_PPP, + BTM_EIR_UUID_SERVCLASS_DIALUP_NETWORKING, + BTM_EIR_UUID_SERVCLASS_IRMC_SYNC, + BTM_EIR_UUID_SERVCLASS_OBEX_OBJECT_PUSH, + BTM_EIR_UUID_SERVCLASS_OBEX_FILE_TRANSFER, + BTM_EIR_UUID_SERVCLASS_IRMC_SYNC_COMMAND, + BTM_EIR_UUID_SERVCLASS_HEADSET, + BTM_EIR_UUID_SERVCLASS_CORDLESS_TELEPHONY, + BTM_EIR_UUID_SERVCLASS_AUDIO_SOURCE, + BTM_EIR_UUID_SERVCLASS_AUDIO_SINK, + BTM_EIR_UUID_SERVCLASS_AV_REM_CTRL_TARGET, + /* BTM_EIR_UUID_SERVCLASS_ADV_AUDIO_DISTRIBUTION, */ + BTM_EIR_UUID_SERVCLASS_AV_REMOTE_CONTROL, + /* BTM_EIR_UUID_SERVCLASS_VIDEO_CONFERENCING, */ + BTM_EIR_UUID_SERVCLASS_INTERCOM, + BTM_EIR_UUID_SERVCLASS_FAX, + BTM_EIR_UUID_SERVCLASS_HEADSET_AUDIO_GATEWAY, + /* BTM_EIR_UUID_SERVCLASS_WAP, */ + /* BTM_EIR_UUID_SERVCLASS_WAP_CLIENT, */ + BTM_EIR_UUID_SERVCLASS_PANU, + BTM_EIR_UUID_SERVCLASS_NAP, + BTM_EIR_UUID_SERVCLASS_GN, + BTM_EIR_UUID_SERVCLASS_DIRECT_PRINTING, + /* BTM_EIR_UUID_SERVCLASS_REFERENCE_PRINTING, */ + BTM_EIR_UUID_SERVCLASS_IMAGING, + BTM_EIR_UUID_SERVCLASS_IMAGING_RESPONDER, + BTM_EIR_UUID_SERVCLASS_IMAGING_AUTO_ARCHIVE, + BTM_EIR_UUID_SERVCLASS_IMAGING_REF_OBJECTS, + BTM_EIR_UUID_SERVCLASS_HF_HANDSFREE, + BTM_EIR_UUID_SERVCLASS_AG_HANDSFREE, + BTM_EIR_UUID_SERVCLASS_DIR_PRT_REF_OBJ_SERVICE, + /* BTM_EIR_UUID_SERVCLASS_REFLECTED_UI, */ + BTM_EIR_UUID_SERVCLASS_BASIC_PRINTING, + BTM_EIR_UUID_SERVCLASS_PRINTING_STATUS, + BTM_EIR_UUID_SERVCLASS_HUMAN_INTERFACE, + BTM_EIR_UUID_SERVCLASS_CABLE_REPLACEMENT, + BTM_EIR_UUID_SERVCLASS_HCRP_PRINT, + BTM_EIR_UUID_SERVCLASS_HCRP_SCAN, + /* BTM_EIR_UUID_SERVCLASS_COMMON_ISDN_ACCESS, */ + /* BTM_EIR_UUID_SERVCLASS_VIDEO_CONFERENCING_GW, */ + /* BTM_EIR_UUID_SERVCLASS_UDI_MT, */ + /* BTM_EIR_UUID_SERVCLASS_UDI_TA, */ + /* BTM_EIR_UUID_SERVCLASS_VCP, */ + BTM_EIR_UUID_SERVCLASS_SAP, + BTM_EIR_UUID_SERVCLASS_PBAP_PCE, + BTM_EIR_UUID_SERVCLASS_PBAP_PSE, + /* BTM_EIR_UUID_SERVCLASS_TE_PHONE_ACCESS, */ + /* BTM_EIR_UUID_SERVCLASS_ME_PHONE_ACCESS, */ + BTM_EIR_UUID_SERVCLASS_PHONE_ACCESS, + BTM_EIR_UUID_SERVCLASS_HEADSET_HS, + BTM_EIR_UUID_SERVCLASS_PNP_INFORMATION, + /* BTM_EIR_UUID_SERVCLASS_GENERIC_NETWORKING, */ + /* BTM_EIR_UUID_SERVCLASS_GENERIC_FILETRANSFER, */ + /* BTM_EIR_UUID_SERVCLASS_GENERIC_AUDIO, */ + /* BTM_EIR_UUID_SERVCLASS_GENERIC_TELEPHONY, */ + /* BTM_EIR_UUID_SERVCLASS_UPNP_SERVICE, */ + /* BTM_EIR_UUID_SERVCLASS_UPNP_IP_SERVICE, */ + /* BTM_EIR_UUID_SERVCLASS_ESDP_UPNP_IP_PAN, */ + /* BTM_EIR_UUID_SERVCLASS_ESDP_UPNP_IP_LAP, */ + /* BTM_EIR_UUID_SERVCLASS_ESDP_UPNP_IP_L2CAP, */ + BTM_EIR_UUID_SERVCLASS_VIDEO_SOURCE, + BTM_EIR_UUID_SERVCLASS_VIDEO_SINK, + /* BTM_EIR_UUID_SERVCLASS_VIDEO_DISTRIBUTION */ + /* BTM_EIR_UUID_SERVCLASS_HDP_PROFILE */ + BTM_EIR_UUID_SERVCLASS_MESSAGE_ACCESS, + BTM_EIR_UUID_SERVCLASS_MESSAGE_NOTIFICATION, + BTM_EIR_UUID_SERVCLASS_HDP_SOURCE, + BTM_EIR_UUID_SERVCLASS_HDP_SINK, + BTM_EIR_MAX_SERVICES +}; + +/* search result in EIR of inquiry database */ +#define BTM_EIR_FOUND 0 +#define BTM_EIR_NOT_FOUND 1 +#define BTM_EIR_UNKNOWN 2 + +typedef UINT8 tBTM_EIR_SEARCH_RESULT; + +#define BTM_EIR_FLAGS_TYPE HCI_EIR_FLAGS_TYPE /* 0x01 */ +#define BTM_EIR_MORE_16BITS_UUID_TYPE HCI_EIR_MORE_16BITS_UUID_TYPE /* 0x02 */ +#define BTM_EIR_COMPLETE_16BITS_UUID_TYPE HCI_EIR_COMPLETE_16BITS_UUID_TYPE /* 0x03 */ +#define BTM_EIR_MORE_32BITS_UUID_TYPE HCI_EIR_MORE_32BITS_UUID_TYPE /* 0x04 */ +#define BTM_EIR_COMPLETE_32BITS_UUID_TYPE HCI_EIR_COMPLETE_32BITS_UUID_TYPE /* 0x05 */ +#define BTM_EIR_MORE_128BITS_UUID_TYPE HCI_EIR_MORE_128BITS_UUID_TYPE /* 0x06 */ +#define BTM_EIR_COMPLETE_128BITS_UUID_TYPE HCI_EIR_COMPLETE_128BITS_UUID_TYPE /* 0x07 */ +#define BTM_EIR_SHORTENED_LOCAL_NAME_TYPE HCI_EIR_SHORTENED_LOCAL_NAME_TYPE /* 0x08 */ +#define BTM_EIR_COMPLETE_LOCAL_NAME_TYPE HCI_EIR_COMPLETE_LOCAL_NAME_TYPE /* 0x09 */ +#define BTM_EIR_TX_POWER_LEVEL_TYPE HCI_EIR_TX_POWER_LEVEL_TYPE /* 0x0A */ +#define BTM_EIR_URL_TYPE HCI_EIR_URL_TYPE /* 0x24 */ +#define BTM_EIR_MANUFACTURER_SPECIFIC_TYPE HCI_EIR_MANUFACTURER_SPECIFIC_TYPE /* 0xFF */ + +#define BTM_EIR_TYPE_MAX_NUM 12 /* Max EIR types */ + +/* the following EIR tags are defined to OOB, not regular EIR data */ +#define BTM_EIR_OOB_BD_ADDR_TYPE HCI_EIR_OOB_BD_ADDR_TYPE /* 6 bytes */ +#define BTM_EIR_OOB_COD_TYPE HCI_EIR_OOB_COD_TYPE /* 3 bytes */ +#define BTM_EIR_OOB_SSP_HASH_C_TYPE HCI_EIR_OOB_SSP_HASH_C_TYPE /* 16 bytes */ +#define BTM_EIR_OOB_SSP_RAND_R_TYPE HCI_EIR_OOB_SSP_RAND_R_TYPE /* 16 bytes */ + +#define BTM_OOB_MANDATORY_SIZE 8 /* include 2 bytes length & 6 bytes bd_addr */ +#define BTM_OOB_DATA_LEN_SIZE 2 +#define BTM_OOB_BD_ADDR_SIZE 6 +#define BTM_OOB_COD_SIZE BT_OOB_COD_SIZE +#define BTM_OOB_HASH_C_SIZE BT_OOB_HASH_C_SIZE +#define BTM_OOB_RAND_R_SIZE BT_OOB_RAND_R_SIZE + + +#if BLE_INCLUDED == TRUE +#define BTM_BLE_SEC_NONE 0 +#define BTM_BLE_SEC_ENCRYPT 1 /* encrypt the link using current key */ +#define BTM_BLE_SEC_ENCRYPT_NO_MITM 2 +#define BTM_BLE_SEC_ENCRYPT_MITM 3 +typedef UINT8 tBTM_BLE_SEC_ACT; +#endif +/************************************************************************************************ +** BTM Services MACROS handle array of UINT32 bits for more than 32 services +*************************************************************************************************/ +/* Determine the number of UINT32's necessary for services */ +#define BTM_EIR_ARRAY_BITS 32 /* Number of bits in each array element */ +#define BTM_EIR_SERVICE_ARRAY_SIZE (((UINT32)BTM_EIR_MAX_SERVICES / BTM_EIR_ARRAY_BITS) + \ + (((UINT32)BTM_EIR_MAX_SERVICES % BTM_EIR_ARRAY_BITS) ? 1 : 0)) + +/* MACRO to set the service bit mask in a bit stream */ +#define BTM_EIR_SET_SERVICE(p, service) (((UINT32 *)(p))[(((UINT32)(service)) / BTM_EIR_ARRAY_BITS)] |= \ + ((UINT32)1 << (((UINT32)(service)) % BTM_EIR_ARRAY_BITS))) + + +/* MACRO to clear the service bit mask in a bit stream */ +#define BTM_EIR_CLR_SERVICE(p, service) (((UINT32 *)(p))[(((UINT32)(service)) / BTM_EIR_ARRAY_BITS)] &= \ + ~((UINT32)1 << (((UINT32)(service)) % BTM_EIR_ARRAY_BITS))) + +/* MACRO to check the service bit mask in a bit stream */ +#define BTM_EIR_HAS_SERVICE(p, service) ((((UINT32 *)(p))[(((UINT32)(service)) / BTM_EIR_ARRAY_BITS)] & \ + ((UINT32)1 << (((UINT32)(service)) % BTM_EIR_ARRAY_BITS))) >> (((UINT32)(service)) % BTM_EIR_ARRAY_BITS)) + +/* start of EIR in HCI buffer, 4 bytes = HCI Command(2) + Length(1) + FEC_Req(1) */ +#define BTM_HCI_EIR_OFFSET (BT_HDR_SIZE + 4) + +/*************************** +** Device Discovery Types +****************************/ +/* Definitions of the parameters passed to BTM_StartInquiry and +** BTM_SetPeriodicInquiryMode. +*/ +typedef struct { /* contains the two device class condition fields */ + DEV_CLASS dev_class; + DEV_CLASS dev_class_mask; +} tBTM_COD_COND; + + +typedef union { /* contains the inquiry filter condition */ + BD_ADDR bdaddr_cond; + tBTM_COD_COND cod_cond; +} tBTM_INQ_FILT_COND; + + +typedef struct { /* contains the parameters passed to the inquiry functions */ + UINT8 mode; /* general or limited */ + UINT8 duration; /* duration of the inquiry (1.28 sec increments) */ + UINT8 max_resps; /* maximum number of responses to return */ + BOOLEAN report_dup; /* report duplicated inquiry response with higher RSSI value */ + UINT8 filter_cond_type; /* new devices, BD ADDR, COD, or No filtering */ + tBTM_INQ_FILT_COND filter_cond; /* filter value based on filter cond type */ +#if (defined(BTA_HOST_INTERLEAVE_SEARCH) && BTA_HOST_INTERLEAVE_SEARCH == TRUE) + UINT8 intl_duration[4]; /*duration array storing the interleave scan's time portions*/ +#endif +} tBTM_INQ_PARMS; + +#define BTM_INQ_RESULT_BR 0x01 +#define BTM_INQ_RESULT_BLE 0x02 + +#if (BLE_INCLUDED == TRUE) +#define BTM_BLE_EVT_CONN_ADV 0x00 +#define BTM_BLE_EVT_CONN_DIR_ADV 0x01 +#define BTM_BLE_EVT_DISC_ADV 0x02 +#define BTM_BLE_EVT_NON_CONN_ADV 0x03 +#define BTM_BLE_EVT_SCAN_RSP 0x04 +typedef UINT8 tBTM_BLE_EVT_TYPE; +#endif + +/* These are the fields returned in each device's response to the inquiry. It +** is returned in the results callback if registered. +*/ +typedef struct { + UINT16 clock_offset; + BD_ADDR remote_bd_addr; + DEV_CLASS dev_class; + UINT8 page_scan_rep_mode; + UINT8 page_scan_per_mode; + UINT8 page_scan_mode; + INT8 rssi; /* Set to BTM_INQ_RES_IGNORE_RSSI if not valid */ + UINT32 eir_uuid[BTM_EIR_SERVICE_ARRAY_SIZE]; + BOOLEAN eir_complete_list; + tBT_DEVICE_TYPE device_type; +#if (BLE_INCLUDED == TRUE) + UINT8 inq_result_type; + UINT8 ble_addr_type; + tBTM_BLE_EVT_TYPE ble_evt_type; + UINT8 flag; + UINT8 adv_data_len; + UINT8 scan_rsp_len; +#endif +} tBTM_INQ_RESULTS; + + +/* This is the inquiry response information held in its database by BTM, and available +** to applications via BTM_InqDbRead, BTM_InqDbFirst, and BTM_InqDbNext. +*/ +typedef struct { + tBTM_INQ_RESULTS results; + + BOOLEAN appl_knows_rem_name; /* set by application if it knows the remote name of the peer device. + This is later used by application to determine if remote name request is + required to be done. Having the flag here avoid duplicate store of inquiry results */ +#if ( BLE_INCLUDED == TRUE) + UINT16 remote_name_len; + tBTM_BD_NAME remote_name; + UINT8 remote_name_state; + UINT8 remote_name_type; +#endif + +} tBTM_INQ_INFO; + + +/* Structure returned with inquiry complete callback */ +typedef struct { + tBTM_STATUS status; + UINT8 num_resp; /* Number of results from the current inquiry */ +} tBTM_INQUIRY_CMPL; + +/* Structure returned with remote name request */ +typedef struct { + UINT16 status; + BD_ADDR bd_addr; + UINT16 length; + BD_NAME remote_bd_name; +} tBTM_REMOTE_DEV_NAME; + +typedef struct { + UINT8 pcm_intf_rate; /* PCM interface rate: 0: 128kbps, 1: 256 kbps; + 2:512 bps; 3: 1024kbps; 4: 2048kbps */ + UINT8 frame_type; /* frame type: 0: short; 1: long */ + UINT8 sync_mode; /* sync mode: 0: slave; 1: master */ + UINT8 clock_mode; /* clock mode: 0: slave; 1: master */ + +} tBTM_SCO_PCM_PARAM; + +/**************************************** +** Device Discovery Callback Functions +*****************************************/ +/* Callback function for asynchronous notifications when the BTM inquiry DB +** changes. First param is inquiry database, second is if added to or removed +** from the inquiry database. +*/ +typedef void (tBTM_INQ_DB_CHANGE_CB) (void *p1, BOOLEAN is_new); + +/* Callback function for notifications when the BTM gets inquiry response. +** First param is inquiry results database, second is pointer of EIR. +*/ +typedef void (tBTM_INQ_RESULTS_CB) (tBTM_INQ_RESULTS *p_inq_results, UINT8 *p_eir); + +/***************************************************************************** +** ACL CHANNEL MANAGEMENT +*****************************************************************************/ +/****************** +** ACL Constants +*******************/ + +/* ACL modes */ +#define BTM_ACL_MODE_NORMAL HCI_MODE_ACTIVE +#define BTM_ACL_MODE_HOLD HCI_MODE_HOLD +#define BTM_ACL_MODE_SNIFF HCI_MODE_SNIFF +#define BTM_ACL_MODE_PARK HCI_MODE_PARK + +/* Returned with structure in role switch callback (tBTM_ROLE_SWITCH_CMPL) */ +#define BTM_ROLE_MASTER HCI_ROLE_MASTER +#define BTM_ROLE_SLAVE HCI_ROLE_SLAVE +#define BTM_ROLE_UNDEFINED 0xff /* undefined value (error status) */ + +/* ACL Packet Types */ +#define BTM_ACL_PKT_TYPES_MASK_DM1 HCI_PKT_TYPES_MASK_DM1 +#define BTM_ACL_PKT_TYPES_MASK_DH1 HCI_PKT_TYPES_MASK_DH1 +#define BTM_ACL_PKT_TYPES_MASK_DM3 HCI_PKT_TYPES_MASK_DM3 +#define BTM_ACL_PKT_TYPES_MASK_DH3 HCI_PKT_TYPES_MASK_DH3 +#define BTM_ACL_PKT_TYPES_MASK_DM5 HCI_PKT_TYPES_MASK_DM5 +#define BTM_ACL_PKT_TYPES_MASK_DH5 HCI_PKT_TYPES_MASK_DH5 +#define BTM_ACL_PKT_TYPES_MASK_NO_2_DH1 HCI_PKT_TYPES_MASK_NO_2_DH1 +#define BTM_ACL_PKT_TYPES_MASK_NO_3_DH1 HCI_PKT_TYPES_MASK_NO_3_DH1 +#define BTM_ACL_PKT_TYPES_MASK_NO_2_DH3 HCI_PKT_TYPES_MASK_NO_2_DH3 +#define BTM_ACL_PKT_TYPES_MASK_NO_3_DH3 HCI_PKT_TYPES_MASK_NO_3_DH3 +#define BTM_ACL_PKT_TYPES_MASK_NO_2_DH5 HCI_PKT_TYPES_MASK_NO_2_DH5 +#define BTM_ACL_PKT_TYPES_MASK_NO_3_DH5 HCI_PKT_TYPES_MASK_NO_3_DH5 + +/*************** +** ACL Types +****************/ + +/* Structure returned with Role Switch information (in tBTM_CMPL_CB callback function) +** in response to BTM_SwitchRole call. +*/ +typedef struct { + UINT8 hci_status; /* HCI status returned with the event */ + UINT8 role; /* BTM_ROLE_MASTER or BTM_ROLE_SLAVE */ + BD_ADDR remote_bd_addr; /* Remote BD addr involved with the switch */ +} tBTM_ROLE_SWITCH_CMPL; + +/* Structure returned with QoS information (in tBTM_CMPL_CB callback function) +** in response to BTM_SetQoS call. +*/ +typedef struct { + FLOW_SPEC flow; + UINT16 handle; + UINT8 status; + BD_ADDR rem_bda; +} tBTM_QOS_SETUP_CMPL; + + +/* Structure returned with read RSSI event (in tBTM_CMPL_CB callback function) +** in response to BTM_ReadRSSI call. +*/ +typedef struct { + tBTM_STATUS status; + UINT8 hci_status; + INT8 rssi; + BD_ADDR rem_bda; +} tBTM_RSSI_RESULTS; + +/* Structure returned with read current TX power event (in tBTM_CMPL_CB callback function) +** in response to BTM_ReadTxPower call. +*/ +typedef struct { + tBTM_STATUS status; + UINT8 hci_status; + INT8 tx_power; + BD_ADDR rem_bda; +} tBTM_TX_POWER_RESULTS; + +/* Structure returned with read link quality event (in tBTM_CMPL_CB callback function) +** in response to BTM_ReadLinkQuality call. +*/ +typedef struct { + tBTM_STATUS status; + UINT8 hci_status; + UINT8 link_quality; + BD_ADDR rem_bda; +} tBTM_LINK_QUALITY_RESULTS; + +/* Structure returned with set AFH channels event (in tBTM_CMPL_CB callback function) +** in response to BTM_SetAfhChannels call. +*/ +typedef struct { + tBTM_STATUS status; + UINT8 hci_status; +} tBTM_SET_AFH_CHANNELS_RESULTS; + +/* Structure returned with set page timeout event (in tBTM_CMPL_CB callback function) +** in response to BTM_WritePageTimeout call. +*/ +typedef struct { + tBTM_STATUS status; + UINT8 hci_status; +} tBTM_SET_PAGE_TIMEOUT_RESULTS; + +/* Structure returned with get page timeout event (in tBTM_CMPL_CB callback function) +** in response to BTM_ReadPageTimeout call. +*/ +typedef struct { + tBTM_STATUS status; + UINT8 hci_status; + UINT16 page_to; +} tBTM_GET_PAGE_TIMEOUT_RESULTS; + +/* Structure returned with set ACL packet types event (in tBTM_CMPL_CB callback function) +** in response to BTM_SetAclPktTypes call. +*/ +typedef struct { + tBTM_STATUS status; + BD_ADDR rem_bda; + UINT16 pkt_types; +} tBTM_SET_ACL_PKT_TYPES_RESULTS; + +/* Structure returned with set BLE channels event (in tBTM_CMPL_CB callback function) +** in response to BTM_BleSetChannels call. +*/ +typedef struct { + tBTM_STATUS status; + UINT8 hci_status; +} tBTM_BLE_SET_CHANNELS_RESULTS; + +/* Structure returned with read inq tx power quality event (in tBTM_CMPL_CB callback function) +** in response to BTM_ReadInquiryRspTxPower call. +*/ +typedef struct { + tBTM_STATUS status; + UINT8 hci_status; + INT8 tx_power; +} tBTM_INQ_TXPWR_RESULTS; + + +enum { + BTM_ACL_CONN_CMPL_EVT, + BTM_ACL_DISCONN_CMPL_EVT +}; +typedef UINT8 tBTM_ACL_LINK_STAT_EVENT; + +typedef struct { + UINT8 status; /* The status of ACL connection complete */ + UINT16 handle; /* The ACL connection handle */ + BD_ADDR bd_addr; /* Peer Bluetooth device address */ +} tBTM_ACL_CONN_CMPL_DATA; + +typedef struct { + UINT8 reason; /* The reason for ACL disconnection complete */ + UINT16 handle; /* The ACL connection handle */ + BD_ADDR bd_addr; /* Peer Bluetooth device address */ +} tBTM_ACL_DISCONN_CMPL_DATA; + +typedef struct { + tBTM_ACL_LINK_STAT_EVENT event; /* The event reported */ + union { + tBTM_ACL_CONN_CMPL_DATA conn_cmpl; /* The data associated with BTM_ACL_CONN_CMPL_EVT */ + tBTM_ACL_DISCONN_CMPL_DATA disconn_cmpl; /* The data associated with BTM_ACL_DISCONN_CMPL_EVT */ + } link_act; +} tBTM_ACL_LINK_STAT_EVENT_DATA; + +/* Callback function for reporting ACL link related events to upper +*/ +typedef void (tBTM_ACL_LINK_STAT_CB) (tBTM_ACL_LINK_STAT_EVENT_DATA *p_data); + +enum { + BTM_BL_CONN_EVT, + BTM_BL_DISCN_EVT, + BTM_BL_UPDATE_EVT, + BTM_BL_ROLE_CHG_EVT, + BTM_BL_COLLISION_EVT +}; +typedef UINT8 tBTM_BL_EVENT; +typedef UINT16 tBTM_BL_EVENT_MASK; + +#define BTM_BL_CONN_MASK 0x0001 +#define BTM_BL_DISCN_MASK 0x0002 +#define BTM_BL_UPDATE_MASK 0x0004 +#define BTM_BL_ROLE_CHG_MASK 0x0008 + +/* Device features mask definitions */ +#define BTM_FEATURE_BYTES_PER_PAGE HCI_FEATURE_BYTES_PER_PAGE +#define BTM_EXT_FEATURES_PAGE_MAX HCI_EXT_FEATURES_PAGE_MAX + +/* the data type associated with BTM_BL_CONN_EVT */ +typedef struct { + tBTM_BL_EVENT event; /* The event reported. */ + BD_ADDR_PTR p_bda; /* The address of the newly connected device */ + DEV_CLASS_PTR p_dc; /* The device class */ + BD_NAME_PTR p_bdn; /* The device name */ + UINT8 *p_features; /* pointer to the remote device's features page[0] (supported features page) */ + BOOLEAN sc_downgrade; /* Secure connection downgrade state. */ +#if BLE_INCLUDED == TRUE + UINT16 handle; /* connection handle */ + tBT_TRANSPORT transport; /* link is LE or not */ +#endif +} tBTM_BL_CONN_DATA; + +/* the data type associated with BTM_BL_DISCN_EVT */ +typedef struct { + tBTM_BL_EVENT event; /* The event reported. */ + BD_ADDR_PTR p_bda; /* The address of the disconnected device */ +#if BLE_INCLUDED == TRUE + UINT16 handle; /* disconnected connection handle */ + tBT_TRANSPORT transport; /* link is LE link or not */ +#endif +} tBTM_BL_DISCN_DATA; + +/* Busy-Level shall have the inquiry_paging mask set when + * inquiry/paging is in progress, Else the number of ACL links */ +#define BTM_BL_INQUIRY_PAGING_MASK 0x10 +#define BTM_BL_INQUIRY_STARTED (BTM_BL_INQUIRY_PAGING_MASK | 0x1) +#define BTM_BL_INQUIRY_CANCELLED (BTM_BL_INQUIRY_PAGING_MASK | 0x2) +#define BTM_BL_INQUIRY_COMPLETE (BTM_BL_INQUIRY_PAGING_MASK | 0x3) +#define BTM_BL_PAGING_STARTED (BTM_BL_INQUIRY_PAGING_MASK | 0x4) +#define BTM_BL_PAGING_COMPLETE (BTM_BL_INQUIRY_PAGING_MASK | 0x5) +/* the data type associated with BTM_BL_UPDATE_EVT */ +typedef struct { + tBTM_BL_EVENT event; /* The event reported. */ + UINT8 busy_level;/* when paging or inquiring, level is 10. + * Otherwise, the number of ACL links. */ + UINT8 busy_level_flags; /* Notifies actual inquiry/page activities */ +} tBTM_BL_UPDATE_DATA; + +/* the data type associated with BTM_BL_ROLE_CHG_EVT */ +typedef struct { + tBTM_BL_EVENT event; /* The event reported. */ + BD_ADDR_PTR p_bda; /* The address of the peer connected device */ + UINT8 new_role; + UINT8 hci_status; /* HCI status returned with the event */ +} tBTM_BL_ROLE_CHG_DATA; + +typedef union { + tBTM_BL_EVENT event; /* The event reported. */ + tBTM_BL_CONN_DATA conn; /* The data associated with BTM_BL_CONN_EVT */ + tBTM_BL_DISCN_DATA discn; /* The data associated with BTM_BL_DISCN_EVT */ + tBTM_BL_UPDATE_DATA update; /* The data associated with BTM_BL_UPDATE_EVT */ + tBTM_BL_ROLE_CHG_DATA role_chg;/*The data associated with BTM_BL_ROLE_CHG_EVT */ +} tBTM_BL_EVENT_DATA; + +/* Callback function for notifications when the BTM busy level +** changes. +*/ +typedef void (tBTM_BL_CHANGE_CB) (tBTM_BL_EVENT_DATA *p_data); + +/*************************** +** ACL Callback Functions +****************************/ +/* Callback function for notifications when the BTM ACL connection DB +** changes. First param is BD address, second is if added or removed. +** Registered through BTM_AclRegisterForChanges call. +*/ +#if BLE_INCLUDED == TRUE +typedef void (tBTM_ACL_DB_CHANGE_CB) (BD_ADDR p_bda, DEV_CLASS p_dc, + BD_NAME p_bdn, UINT8 *features, + BOOLEAN is_new, UINT16 handle, + tBT_TRANSPORT transport); +#else +typedef void (tBTM_ACL_DB_CHANGE_CB) (BD_ADDR p_bda, DEV_CLASS p_dc, + BD_NAME p_bdn, UINT8 *features, + BOOLEAN is_new); +#endif +/***************************************************************************** +** SCO CHANNEL MANAGEMENT +*****************************************************************************/ +/****************** +** SCO Constants +*******************/ + +/* Define an invalid SCO index and an invalid HCI handle */ +#define BTM_INVALID_SCO_INDEX 0xFFFF +#define BTM_INVALID_HCI_HANDLE 0xFFFF + +/* Define an invalid SCO disconnect reason */ +#define BTM_INVALID_SCO_DISC_REASON 0xFFFF + +/* Define first active SCO index */ +#define BTM_FIRST_ACTIVE_SCO_INDEX BTM_MAX_SCO_LINKS + +/* Define SCO packet types used in APIs */ +#define BTM_SCO_PKT_TYPES_MASK_HV1 HCI_ESCO_PKT_TYPES_MASK_HV1 +#define BTM_SCO_PKT_TYPES_MASK_HV2 HCI_ESCO_PKT_TYPES_MASK_HV2 +#define BTM_SCO_PKT_TYPES_MASK_HV3 HCI_ESCO_PKT_TYPES_MASK_HV3 +#define BTM_SCO_PKT_TYPES_MASK_EV3 HCI_ESCO_PKT_TYPES_MASK_EV3 +#define BTM_SCO_PKT_TYPES_MASK_EV4 HCI_ESCO_PKT_TYPES_MASK_EV4 +#define BTM_SCO_PKT_TYPES_MASK_EV5 HCI_ESCO_PKT_TYPES_MASK_EV5 +#define BTM_SCO_PKT_TYPES_MASK_NO_2_EV3 HCI_ESCO_PKT_TYPES_MASK_NO_2_EV3 +#define BTM_SCO_PKT_TYPES_MASK_NO_3_EV3 HCI_ESCO_PKT_TYPES_MASK_NO_3_EV3 +#define BTM_SCO_PKT_TYPES_MASK_NO_2_EV5 HCI_ESCO_PKT_TYPES_MASK_NO_2_EV5 +#define BTM_SCO_PKT_TYPES_MASK_NO_3_EV5 HCI_ESCO_PKT_TYPES_MASK_NO_3_EV5 + +#define BTM_SCO_LINK_ONLY_MASK (BTM_SCO_PKT_TYPES_MASK_HV1 | \ + BTM_SCO_PKT_TYPES_MASK_HV2 | \ + BTM_SCO_PKT_TYPES_MASK_HV3) + +#define BTM_ESCO_LINK_ONLY_MASK (BTM_SCO_PKT_TYPES_MASK_EV3 | \ + BTM_SCO_PKT_TYPES_MASK_EV4 | \ + BTM_SCO_PKT_TYPES_MASK_EV5) + +#define BTM_SCO_LINK_ALL_PKT_MASK (BTM_SCO_LINK_ONLY_MASK | \ + BTM_ESCO_LINK_ONLY_MASK) + +#define BTM_VALID_SCO_ALL_PKT_TYPE HCI_VALID_SCO_ALL_PKT_TYPE + +/* Passed in BTM_CreateSco if the packet type parameter should be ignored */ +#define BTM_IGNORE_SCO_PKT_TYPE 0 + +/*************** +** SCO Types +****************/ +#define BTM_LINK_TYPE_SCO HCI_LINK_TYPE_SCO +#define BTM_LINK_TYPE_ESCO HCI_LINK_TYPE_ESCO +typedef UINT8 tBTM_SCO_TYPE; + + +/******************* +** SCO Routing Path +********************/ +#define BTM_SCO_ROUTE_PCM (0) // HCI_BRCM_SCO_ROUTE_PCM +#define BTM_SCO_ROUTE_HCI (1) // HCI_BRCM_SCO_ROUTE_HCI +typedef UINT8 tBTM_SCO_ROUTE_TYPE; + + +/******************* +** SCO Codec Types +********************/ +// TODO(google) This should use common definitions +// in hci/include/hci_audio.h +#define BTM_SCO_CODEC_NONE 0x0000 +#define BTM_SCO_CODEC_CVSD 0x0001 +#define BTM_SCO_CODEC_MSBC 0x0002 +typedef UINT16 tBTM_SCO_CODEC_TYPE; + + + +/******************* +** SCO Air Mode Types +********************/ +#define BTM_SCO_AIR_MODE_U_LAW 0 +#define BTM_SCO_AIR_MODE_A_LAW 1 +#define BTM_SCO_AIR_MODE_CVSD 2 +#define BTM_SCO_AIR_MODE_TRANSPNT 3 +#define BTM_SCO_AIR_MODE_UNKNOWN 0xFF +typedef UINT8 tBTM_SCO_AIR_MODE_TYPE; + +/******************* +** SCO Voice Settings +********************/ +#define BTM_VOICE_SETTING_CVSD ((UINT16) (HCI_INP_CODING_LINEAR | \ + HCI_INP_DATA_FMT_2S_COMPLEMENT | \ + HCI_INP_SAMPLE_SIZE_16BIT | \ + HCI_AIR_CODING_FORMAT_CVSD)) + +#define BTM_VOICE_SETTING_TRANS ((UINT16) (HCI_INP_CODING_LINEAR | \ + HCI_INP_DATA_FMT_2S_COMPLEMENT | \ + HCI_INP_SAMPLE_SIZE_8BIT | \ + HCI_AIR_CODING_FORMAT_TRANSPNT)) + +/******************* +** SCO Data Status +********************/ +enum { + BTM_SCO_DATA_CORRECT, + BTM_SCO_DATA_PAR_ERR, + BTM_SCO_DATA_NONE, + BTM_SCO_DATA_PAR_LOST +}; +typedef UINT8 tBTM_SCO_DATA_FLAG; + +/* Count the number of SCO Data Packet Status */ +typedef struct { + UINT32 rx_total; + UINT32 rx_correct; + UINT32 rx_err; + UINT32 rx_none; + UINT32 rx_lost; + UINT32 tx_total; + UINT32 tx_discarded; +} tBTM_SCO_PKT_STAT_NUMS; + +/*************************** +** SCO Callback Functions +****************************/ +typedef void (tBTM_SCO_CB) (UINT16 sco_inx); +typedef void (tBTM_SCO_DATA_CB) (UINT16 sco_inx, BT_HDR *p_data, tBTM_SCO_DATA_FLAG status); + +/****************** +** eSCO Constants +*******************/ +#define BTM_64KBITS_RATE 0x00001f40 /* 64 kbits/sec data rate */ + +/* Retransmission effort */ +#define BTM_ESCO_RETRANS_OFF 0 +#define BTM_ESCO_RETRANS_POWER 1 +#define BTM_ESCO_RETRANS_QUALITY 2 +#define BTM_ESCO_RETRANS_DONTCARE 0xff + +/* Max Latency Don't Care */ +#define BTM_ESCO_MAX_LAT_DONTCARE 0xffff + +/*************** +** eSCO Types +****************/ +/* tBTM_ESCO_CBACK event types */ +#define BTM_ESCO_CHG_EVT 1 +#define BTM_ESCO_CONN_REQ_EVT 2 +typedef UINT8 tBTM_ESCO_EVT; + +/* Passed into BTM_SetEScoMode() */ +typedef struct { + UINT32 tx_bw; + UINT32 rx_bw; + UINT16 max_latency; + UINT16 voice_contfmt; /* Voice Settings or Content Format */ + UINT16 packet_types; + UINT8 retrans_effort; +} tBTM_ESCO_PARAMS; + +typedef struct { + UINT16 max_latency; + UINT16 packet_types; + UINT8 retrans_effort; +} tBTM_CHG_ESCO_PARAMS; + +/* Returned by BTM_ReadEScoLinkParms() */ +typedef struct { + UINT16 rx_pkt_len; + UINT16 tx_pkt_len; + BD_ADDR bd_addr; + UINT8 link_type; /* BTM_LINK_TYPE_SCO or BTM_LINK_TYPE_ESCO */ + UINT8 tx_interval; + UINT8 retrans_window; + UINT8 air_mode; +} tBTM_ESCO_DATA; + +typedef struct { + UINT16 sco_inx; + UINT16 rx_pkt_len; + UINT16 tx_pkt_len; + BD_ADDR bd_addr; + UINT8 hci_status; + UINT8 tx_interval; + UINT8 retrans_window; +} tBTM_CHG_ESCO_EVT_DATA; + +typedef struct { + UINT16 sco_inx; + BD_ADDR bd_addr; + DEV_CLASS dev_class; + tBTM_SCO_TYPE link_type; +} tBTM_ESCO_CONN_REQ_EVT_DATA; + +typedef union { + tBTM_CHG_ESCO_EVT_DATA chg_evt; + tBTM_ESCO_CONN_REQ_EVT_DATA conn_evt; +} tBTM_ESCO_EVT_DATA; + +/*************************** +** eSCO Callback Functions +****************************/ +typedef void (tBTM_ESCO_CBACK) (tBTM_ESCO_EVT event, tBTM_ESCO_EVT_DATA *p_data); + + +/***************************************************************************** +** SECURITY MANAGEMENT +*****************************************************************************/ +/******************************* +** Security Manager Constants +********************************/ + +/* Security Mode (BTM_SetSecurityMode) */ +#define BTM_SEC_MODE_UNDEFINED 0 +#define BTM_SEC_MODE_NONE 1 +#define BTM_SEC_MODE_SERVICE 2 +#define BTM_SEC_MODE_LINK 3 +#define BTM_SEC_MODE_SP 4 +#define BTM_SEC_MODE_SP_DEBUG 5 +#define BTM_SEC_MODE_SC 6 + +/* Maximum Number of BTM Security Modes */ +#define BTM_SEC_MODES_MAX 7 + +/* Security Service Levels [bit mask] (BTM_SetSecurityLevel) +** Encryption should not be used without authentication +*/ +#define BTM_SEC_NONE 0x0000 /* Nothing required */ +#define BTM_SEC_IN_AUTHORIZE 0x0001 /* Inbound call requires authorization */ +#define BTM_SEC_IN_AUTHENTICATE 0x0002 /* Inbound call requires authentication */ +#define BTM_SEC_IN_ENCRYPT 0x0004 /* Inbound call requires encryption */ +#define BTM_SEC_OUT_AUTHORIZE 0x0008 /* Outbound call requires authorization */ +#define BTM_SEC_OUT_AUTHENTICATE 0x0010 /* Outbound call requires authentication */ +#define BTM_SEC_OUT_ENCRYPT 0x0020 /* Outbound call requires encryption */ +#define BTM_SEC_MODE4_LEVEL4 0x0040 /* Secure Connections Only Mode */ +#define BTM_SEC_FORCE_MASTER 0x0100 /* Need to switch connection to be master */ +#define BTM_SEC_ATTEMPT_MASTER 0x0200 /* Try to switch connection to be master */ +#define BTM_SEC_FORCE_SLAVE 0x0400 /* Need to switch connection to be master */ +#define BTM_SEC_ATTEMPT_SLAVE 0x0800 /* Try to switch connection to be slave */ +#define BTM_SEC_IN_MITM 0x1000 /* inbound Do man in the middle protection */ +#define BTM_SEC_OUT_MITM 0x2000 /* outbound Do man in the middle protection */ +#define BTM_SEC_IN_MIN_16_DIGIT_PIN 0x4000 /* enforce a minimum of 16 digit for sec mode 2 */ + +/* Security Flags [bit mask] (BTM_GetSecurityFlags) +*/ +#define BTM_SEC_FLAG_AUTHORIZED 0x01 +#define BTM_SEC_FLAG_AUTHENTICATED 0x02 +#define BTM_SEC_FLAG_ENCRYPTED 0x04 +#define BTM_SEC_FLAG_LKEY_KNOWN 0x10 +#define BTM_SEC_FLAG_LKEY_AUTHED 0x20 + +/* PIN types */ +#define BTM_PIN_TYPE_VARIABLE HCI_PIN_TYPE_VARIABLE +#define BTM_PIN_TYPE_FIXED HCI_PIN_TYPE_FIXED + +/* Link Key types used to generate the new link key. +** returned in link key notification callback function +*/ +#define BTM_LKEY_TYPE_COMBINATION HCI_LKEY_TYPE_COMBINATION +#define BTM_LKEY_TYPE_LOCAL_UNIT HCI_LKEY_TYPE_LOCAL_UNIT +#define BTM_LKEY_TYPE_REMOTE_UNIT HCI_LKEY_TYPE_REMOTE_UNIT +#define BTM_LKEY_TYPE_DEBUG_COMB HCI_LKEY_TYPE_DEBUG_COMB +#define BTM_LKEY_TYPE_UNAUTH_COMB HCI_LKEY_TYPE_UNAUTH_COMB +#define BTM_LKEY_TYPE_AUTH_COMB HCI_LKEY_TYPE_AUTH_COMB +#define BTM_LKEY_TYPE_CHANGED_COMB HCI_LKEY_TYPE_CHANGED_COMB + +#define BTM_LKEY_TYPE_UNAUTH_COMB_P_256 HCI_LKEY_TYPE_UNAUTH_COMB_P_256 +#define BTM_LKEY_TYPE_AUTH_COMB_P_256 HCI_LKEY_TYPE_AUTH_COMB_P_256 + +#define BTM_LTK_DERIVED_LKEY_OFFSET 0x20 /* "easy" requirements for Link Key (LK) derived from Long Term Key */ +#define BTM_LKEY_TYPE_IGNORE 0xff /* used when event is response from + hci return link keys request */ + +typedef UINT8 tBTM_LINK_KEY_TYPE; + +/* Protocol level security (BTM_SetSecurityLevel) */ +#define BTM_SEC_PROTO_L2CAP 0 +#define BTM_SEC_PROTO_SDP 1 +#define BTM_SEC_PROTO_TCS 2 +#define BTM_SEC_PROTO_RFCOMM 3 +#define BTM_SEC_PROTO_OBEX 4 +#define BTM_SEC_PROTO_BNEP 5 +#define BTM_SEC_PROTO_HID 6 /* HID */ +#define BTM_SEC_PROTO_AVDT 7 +#define BTM_SEC_PROTO_MCA 8 + +/* Determine the number of UINT32's necessary for security services */ +#define BTM_SEC_ARRAY_BITS 32 /* Number of bits in each array element */ +#define BTM_SEC_SERVICE_ARRAY_SIZE (((UINT32)BTM_SEC_MAX_SERVICES / BTM_SEC_ARRAY_BITS) + \ + (((UINT32)BTM_SEC_MAX_SERVICES % BTM_SEC_ARRAY_BITS) ? 1 : 0)) + +/* Security service definitions (BTM_SetSecurityLevel) +** Used for Authorization APIs +*/ +#define BTM_SEC_SERVICE_SDP_SERVER 0 +#define BTM_SEC_SERVICE_SERIAL_PORT 1 +#define BTM_SEC_SERVICE_LAN_ACCESS 2 +#define BTM_SEC_SERVICE_DUN 3 +#define BTM_SEC_SERVICE_IRMC_SYNC 4 +#define BTM_SEC_SERVICE_IRMC_SYNC_CMD 5 +#define BTM_SEC_SERVICE_OBEX 6 +#define BTM_SEC_SERVICE_OBEX_FTP 7 +#define BTM_SEC_SERVICE_HEADSET 8 +#define BTM_SEC_SERVICE_CORDLESS 9 +#define BTM_SEC_SERVICE_INTERCOM 10 +#define BTM_SEC_SERVICE_FAX 11 +#define BTM_SEC_SERVICE_HEADSET_AG 12 +#define BTM_SEC_SERVICE_PNP_INFO 13 +#define BTM_SEC_SERVICE_GEN_NET 14 +#define BTM_SEC_SERVICE_GEN_FILE 15 +#define BTM_SEC_SERVICE_GEN_AUDIO 16 +#define BTM_SEC_SERVICE_GEN_TEL 17 +#define BTM_SEC_SERVICE_CTP_DATA 18 +#define BTM_SEC_SERVICE_HCRP_CTRL 19 +#define BTM_SEC_SERVICE_HCRP_DATA 20 +#define BTM_SEC_SERVICE_HCRP_NOTIF 21 +#define BTM_SEC_SERVICE_BPP_JOB 22 +#define BTM_SEC_SERVICE_BPP_STATUS 23 +#define BTM_SEC_SERVICE_BPP_REF 24 +#define BTM_SEC_SERVICE_BNEP_PANU 25 +#define BTM_SEC_SERVICE_BNEP_GN 26 +#define BTM_SEC_SERVICE_BNEP_NAP 27 +#define BTM_SEC_SERVICE_HF_HANDSFREE 28 +#define BTM_SEC_SERVICE_AG_HANDSFREE 29 +#define BTM_SEC_SERVICE_TE_PHONE_ACCESS 30 +#define BTM_SEC_SERVICE_ME_PHONE_ACCESS 31 + +#define BTM_SEC_SERVICE_HIDH_SEC_CTRL 32 +#define BTM_SEC_SERVICE_HIDH_NOSEC_CTRL 33 +#define BTM_SEC_SERVICE_HIDH_INTR 34 +#define BTM_SEC_SERVICE_BIP 35 +#define BTM_SEC_SERVICE_BIP_REF 36 +#define BTM_SEC_SERVICE_AVDTP 37 +#define BTM_SEC_SERVICE_AVDTP_NOSEC 38 +#define BTM_SEC_SERVICE_AVCTP 39 +#define BTM_SEC_SERVICE_SAP 40 +#define BTM_SEC_SERVICE_PBAP 41 +#define BTM_SEC_SERVICE_RFC_MUX 42 +#define BTM_SEC_SERVICE_AVCTP_BROWSE 43 +#define BTM_SEC_SERVICE_MAP 44 +#define BTM_SEC_SERVICE_MAP_NOTIF 45 +#define BTM_SEC_SERVICE_MCAP_CTRL 46 +#define BTM_SEC_SERVICE_MCAP_DATA 47 +#define BTM_SEC_SERVICE_HDP_SNK 48 +#define BTM_SEC_SERVICE_HDP_SRC 49 +#define BTM_SEC_SERVICE_ATT 50 +#define BTM_SEC_SERVICE_HIDD_SEC_CTRL 51 +#define BTM_SEC_SERVICE_HIDD_NOSEC_CTRL 52 +#define BTM_SEC_SERVICE_HIDD_INTR 53 + +/* Update these as services are added */ +#define BTM_SEC_SERVICE_FIRST_EMPTY 54 + +#ifndef BTM_SEC_MAX_SERVICES +#define BTM_SEC_MAX_SERVICES 65 +#endif + +/************************************************************************************************ +** Security Services MACROS handle array of UINT32 bits for more than 32 trusted services +*************************************************************************************************/ +/* MACRO to set the security service bit mask in a bit stream */ +#define BTM_SEC_SET_SERVICE(p, service) (((UINT32 *)(p))[(((UINT32)(service)) / BTM_SEC_ARRAY_BITS)] |= \ + ((UINT32)1 << (((UINT32)(service)) % BTM_SEC_ARRAY_BITS))) + + +/* MACRO to clear the security service bit mask in a bit stream */ +#define BTM_SEC_CLR_SERVICE(p, service) (((UINT32 *)(p))[(((UINT32)(service)) / BTM_SEC_ARRAY_BITS)] &= \ + ~((UINT32)1 << (((UINT32)(service)) % BTM_SEC_ARRAY_BITS))) + +/* MACRO to check the security service bit mask in a bit stream (Returns TRUE or FALSE) */ +#define BTM_SEC_IS_SERVICE_TRUSTED(p, service) (((((UINT32 *)(p))[(((UINT32)(service)) / BTM_SEC_ARRAY_BITS)]) & \ + (UINT32)(((UINT32)1 << (((UINT32)(service)) % BTM_SEC_ARRAY_BITS)))) ? TRUE : FALSE) + +/* MACRO to copy two trusted device bitmask */ +#define BTM_SEC_COPY_TRUSTED_DEVICE(p_src, p_dst) {UINT32 trst; for (trst = 0; trst < BTM_SEC_SERVICE_ARRAY_SIZE; trst++) \ + ((UINT32 *)(p_dst))[trst] = ((UINT32 *)(p_src))[trst];} + +/* MACRO to clear two trusted device bitmask */ +#define BTM_SEC_CLR_TRUSTED_DEVICE(p_dst) {UINT32 trst; for (trst = 0; trst < BTM_SEC_SERVICE_ARRAY_SIZE; trst++) \ + ((UINT32 *)(p_dst))[trst] = 0;} + +/* Following bits can be provided by host in the trusted_mask array */ +/* 0..31 bits of mask[0] (Least Significant Word) */ +#define BTM_SEC_TRUST_SDP_SERVER (1 << BTM_SEC_SERVICE_SDP_SERVER) +#define BTM_SEC_TRUST_SERIAL_PORT (1 << BTM_SEC_SERVICE_SERIAL_PORT) +#define BTM_SEC_TRUST_LAN_ACCESS (1 << BTM_SEC_SERVICE_LAN_ACCESS) +#define BTM_SEC_TRUST_DUN (1 << BTM_SEC_SERVICE_DUN) +#define BTM_SEC_TRUST_IRMC_SYNC (1 << BTM_SEC_SERVICE_IRMC_SYNC) +#define BTM_SEC_TRUST_IRMC_SYNC_CMD (1 << BTM_SEC_SERVICE_IRMC_SYNC_CMD) +#define BTM_SEC_TRUST_OBEX (1 << BTM_SEC_SERVICE_OBEX) +#define BTM_SEC_TRUST_OBEX_FTP (1 << BTM_SEC_SERVICE_OBEX_FTP) +#define BTM_SEC_TRUST_HEADSET (1 << BTM_SEC_SERVICE_HEADSET) +#define BTM_SEC_TRUST_CORDLESS (1 << BTM_SEC_SERVICE_CORDLESS) +#define BTM_SEC_TRUST_INTERCOM (1 << BTM_SEC_SERVICE_INTERCOM) +#define BTM_SEC_TRUST_FAX (1 << BTM_SEC_SERVICE_FAX) +#define BTM_SEC_TRUST_HEADSET_AG (1 << BTM_SEC_SERVICE_HEADSET_AG) +#define BTM_SEC_TRUST_PNP_INFO (1 << BTM_SEC_SERVICE_PNP_INFO) +#define BTM_SEC_TRUST_GEN_NET (1 << BTM_SEC_SERVICE_GEN_NET) +#define BTM_SEC_TRUST_GEN_FILE (1 << BTM_SEC_SERVICE_GEN_FILE) +#define BTM_SEC_TRUST_GEN_AUDIO (1 << BTM_SEC_SERVICE_GEN_AUDIO) +#define BTM_SEC_TRUST_GEN_TEL (1 << BTM_SEC_SERVICE_GEN_TEL) +#define BTM_SEC_TRUST_CTP_DATA (1 << BTM_SEC_SERVICE_CTP_DATA) +#define BTM_SEC_TRUST_HCRP_CTRL (1 << BTM_SEC_SERVICE_HCRP_CTRL) +#define BTM_SEC_TRUST_HCRP_DATA (1 << BTM_SEC_SERVICE_HCRP_DATA) +#define BTM_SEC_TRUST_HCRP_NOTIF (1 << BTM_SEC_SERVICE_HCRP_NOTIF) +#define BTM_SEC_TRUST_BPP_JOB (1 << BTM_SEC_SERVICE_JOB) +#define BTM_SEC_TRUST_BPP_STATUS (1 << BTM_SEC_SERVICE_STATUS) +#define BTM_SEC_TRUST_BPP_REF (1 << BTM_SEC_SERVICE_REF) +#define BTM_SEC_TRUST_BNEP_PANU (1 << BTM_SEC_SERVICE_BNEP_PANU) +#define BTM_SEC_TRUST_BNEP_GN (1 << BTM_SEC_SERVICE_BNEP_GN) +#define BTM_SEC_TRUST_BNEP_NAP (1 << BTM_SEC_SERVICE_BNEP_NAP) +#define BTM_SEC_TRUST_HFP_HF (1 << BTM_SEC_SERVICE_HF_HANDSFREE) +#define BTM_SEC_TRUST_HFP_AG (1 << BTM_SEC_SERVICE_AG_HANDSFREE) +#define BTM_SEC_TRUST_TE_PHONE_ACCESS (1 << BTM_SEC_SERVICE_TE_PHONE_ACCESS) +#define BTM_SEC_TRUST_ME_PHONE_ACCESS (1 << BTM_SEC_SERVICE_ME_PHONE_ACCESS) + +/* 0..31 bits of mask[1] (Most Significant Word) */ +#define BTM_SEC_TRUST_HIDH_CTRL (1 << (BTM_SEC_SERVICE_HIDH_SEC_CTRL - 32)) +#define BTM_SEC_TRUST_HIDH_NOSEC_CTRL (1 << (BTM_SEC_SERVICE_HIDH_NOSEC_CTRL - 32)) +#define BTM_SEC_TRUST_HIDH_INTR (1 << (BTM_SEC_SERVICE_HIDH_INTR - 32)) +#define BTM_SEC_TRUST_BIP (1 << (BTM_SEC_SERVICE_BIP - 32)) +#define BTM_SEC_TRUST_BIP_REF (1 << (BTM_SEC_SERVICE_BIP_REF - 32)) +#define BTM_SEC_TRUST_AVDTP (1 << (BTM_SEC_SERVICE_AVDTP - 32)) +#define BTM_SEC_TRUST_AVDTP_NOSEC (1 << (BTM_SEC_SERVICE_AVDTP_NOSEC - 32)) +#define BTM_SEC_TRUST_AVCTP (1 << (BTM_SEC_SERVICE_AVCTP - 32)) +#define BTM_SEC_TRUST_SAP (1 << (BTM_SEC_SERVICE_SAP - 32)) +#define BTM_SEC_TRUST_PBAP (1 << (BTM_SEC_SERVICE_PBAP - 32)) +#define BTM_SEC_TRUST_RFC_MUX (1 << (BTM_SEC_SERVICE_RFC_MUX - 32)) +#define BTM_SEC_TRUST_AVCTP_BROWSE (1 << (BTM_SEC_SERVICE_AVCTP_BROWSE - 32)) +#define BTM_SEC_TRUST_MAP (1 << (BTM_SEC_SERVICE_MAP - 32)) +#define BTM_SEC_TRUST_MAP_NOTIF (1 << (BTM_SEC_SERVICE_MAP_NOTIF - 32)) +#define BTM_SEC_TRUST_MCAP_CTRL (1 << (BTM_SEC_SERVICE_MCAP_CTRL - 32)) +#define BTM_SEC_TRUST_MCAP_DATA (1 << (BTM_SEC_SERVICE_MCAP_DATA - 32)) +#define BTM_SEC_TRUST_HDP_SNK (1 << (BTM_SEC_SERVICE_HDP_SNK - 32)) +#define BTM_SEC_TRUST_HDP_SRC (1 << (BTM_SEC_SERVICE_HDP_SRC - 32)) + +#define BTM_SEC_TRUST_ALL 0xFFFFFFFF /* for each array element */ + +/**************************************** +** Security Manager Callback Functions +*****************************************/ +/* Authorize device for service. Parameters are +** BD Address of remote +** Device Class of remote +** BD Name of remote +** Service name +** Service Id (NULL - unknown service or unused +** [BTM_SEC_SERVICE_NAME_LEN set to 0]) +** Is originator of the connection +** Result of the operation +*/ +typedef UINT8 (tBTM_AUTHORIZE_CALLBACK) (BD_ADDR bd_addr, DEV_CLASS dev_class, + tBTM_BD_NAME bd_name, UINT8 *service_name, + UINT8 service_id, BOOLEAN is_originator); + +/* Get PIN for the connection. Parameters are +** BD Address of remote +** Device Class of remote +** BD Name of remote +** Flag indicating the minimum pin code length to be 16 digits +*/ +typedef UINT8 (tBTM_PIN_CALLBACK) (BD_ADDR bd_addr, DEV_CLASS dev_class, + tBTM_BD_NAME bd_name, BOOLEAN min_16_digit); + +/* New Link Key for the connection. Parameters are +** BD Address of remote +** Link Key +** Key Type: Combination, Local Unit, or Remote Unit +*/ +typedef UINT8 (tBTM_LINK_KEY_CALLBACK) (BD_ADDR bd_addr, DEV_CLASS dev_class, + tBTM_BD_NAME bd_name, UINT8 *key, + UINT8 key_type, BOOLEAN sc_support); + + +/* Remote Name Resolved. Parameters are +** BD Address of remote +** BD Name of remote +*/ +typedef void (tBTM_RMT_NAME_CALLBACK) (BD_ADDR bd_addr, DEV_CLASS dc, + tBTM_BD_NAME bd_name); + + +/* Authentication complete for the connection. Parameters are +** BD Address of remote +** Device Class of remote +** BD Name of remote +** +*/ +typedef UINT8 (tBTM_AUTH_COMPLETE_CALLBACK) (BD_ADDR bd_addr, DEV_CLASS dev_class, + tBTM_BD_NAME bd_name, int result); + +/* Encryption changed for the connection. Parameters are +** BD Address of remote +** Encryption mode +*/ +typedef void (tBTM_ENC_CHANGE_CALLBACK) (BD_ADDR bd_addr, UINT8 enc_mode); + +enum { + BTM_SP_IO_REQ_EVT, /* received IO_CAPABILITY_REQUEST event */ + BTM_SP_IO_RSP_EVT, /* received IO_CAPABILITY_RESPONSE event */ + BTM_SP_CFM_REQ_EVT, /* received USER_CONFIRMATION_REQUEST event */ + BTM_SP_KEY_NOTIF_EVT, /* received USER_PASSKEY_NOTIFY event */ + BTM_SP_KEY_REQ_EVT, /* received USER_PASSKEY_REQUEST event */ + BTM_SP_KEYPRESS_EVT, /* received KEYPRESS_NOTIFY event */ + BTM_SP_LOC_OOB_EVT, /* received result for READ_LOCAL_OOB_DATA command */ + BTM_SP_RMT_OOB_EVT, /* received REMOTE_OOB_DATA_REQUEST event */ + BTM_SP_COMPLT_EVT, /* received SIMPLE_PAIRING_COMPLETE event */ + BTM_SP_UPGRADE_EVT /* check if the application wants to upgrade the link key */ +}; +typedef UINT8 tBTM_SP_EVT; + +/* relate to ESP_IO_CAP_xxx in esp_gap_ble_api.h */ +#define BTM_IO_CAP_OUT 0 /* DisplayOnly */ +#define BTM_IO_CAP_IO 1 /* DisplayYesNo */ +#define BTM_IO_CAP_IN 2 /* KeyboardOnly */ +#define BTM_IO_CAP_NONE 3 /* NoInputNoOutput */ +// #if BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE +#define BTM_IO_CAP_KBDISP 4 /* Keyboard display */ +#define BTM_IO_CAP_MAX 5 +// #else +// #define BTM_IO_CAP_MAX 4 +// #endif + +typedef UINT8 tBTM_IO_CAP; + +#define BTM_MAX_PASSKEY_VAL (999999) +#define BTM_MIN_PASSKEY_VAL (0) + +#define BTM_AUTH_SP_NO 0 /* MITM Protection Not Required - Single Profile/non-bonding + Numeric comparison with automatic accept allowed */ +#define BTM_AUTH_SP_YES 1 /* MITM Protection Required - Single Profile/non-bonding + Use IO Capabilities to determine authentication procedure */ +#define BTM_AUTH_AP_NO 2 /* MITM Protection Not Required - All Profiles/dedicated bonding + Numeric comparison with automatic accept allowed */ +#define BTM_AUTH_AP_YES 3 /* MITM Protection Required - All Profiles/dedicated bonding + Use IO Capabilities to determine authentication procedure */ +#define BTM_AUTH_SPGB_NO 4 /* MITM Protection Not Required - Single Profiles/general bonding + Numeric comparison with automatic accept allowed */ +#define BTM_AUTH_SPGB_YES 5 /* MITM Protection Required - Single Profiles/general bonding + Use IO Capabilities to determine authentication procedure */ +#define BTM_AUTH_DD_BOND 2 /* this bit is ORed to the BTM_AUTH_SP_* when IO exchange for dedicated bonding */ +#define BTM_AUTH_GB_BIT 4 /* the genernal bonding bit */ +#define BTM_AUTH_BONDS 6 /* the general/dedicated bonding bits */ +#define BTM_AUTH_YN_BIT 1 /* this is the Yes or No bit */ + +#define BTM_BLE_ENC_KEY_MASK (1 << 0) +#define BTM_BLE_ID_KEY_MASK (1 << 1) +#define BTM_BLE_CSR_KEY_MASK (1 << 2) +#define BTM_BLE_LINK_KEY_MASK (1 << 3) + +#define BTM_BLE_INITIATOR_KEY_SIZE 15 +#define BTM_BLE_RESPONDER_KEY_SIZE 15 +#define BTM_BLE_MAX_KEY_SIZE 16 +#define BTM_BLE_MIN_KEY_SIZE 7 + +typedef UINT8 tBTM_AUTH_REQ; + +enum { + BTM_OOB_NONE, + BTM_OOB_PRESENT +#if BTM_OOB_INCLUDED == TRUE + , BTM_OOB_UNKNOWN +#endif +}; +typedef UINT8 tBTM_OOB_DATA; + +/* data type for BTM_SP_IO_REQ_EVT */ +typedef struct { + BD_ADDR bd_addr; /* peer address */ + tBTM_IO_CAP io_cap; /* local IO capabilities */ + tBTM_OOB_DATA oob_data; /* OOB data present (locally) for the peer device */ + tBTM_AUTH_REQ auth_req; /* Authentication required (for local device) */ + BOOLEAN is_orig; /* TRUE, if local device initiated the SP process */ +} tBTM_SP_IO_REQ; + +/* data type for BTM_SP_IO_RSP_EVT */ +typedef struct { + BD_ADDR bd_addr; /* peer address */ + tBTM_IO_CAP io_cap; /* peer IO capabilities */ + tBTM_OOB_DATA oob_data; /* OOB data present at peer device for the local device */ + tBTM_AUTH_REQ auth_req; /* Authentication required for peer device */ +} tBTM_SP_IO_RSP; + +/* data type for BTM_SP_CFM_REQ_EVT */ +typedef struct { + BD_ADDR bd_addr; /* peer address */ + DEV_CLASS dev_class; /* peer CoD */ + tBTM_BD_NAME bd_name; /* peer device name */ + UINT32 num_val; /* the numeric value for comparison. If just_works, do not show this number to UI */ + BOOLEAN just_works; /* TRUE, if "Just Works" association model */ + tBTM_AUTH_REQ loc_auth_req; /* Authentication required for local device */ + tBTM_AUTH_REQ rmt_auth_req; /* Authentication required for peer device */ + tBTM_IO_CAP loc_io_caps; /* IO Capabilities of the local device */ + tBTM_IO_CAP rmt_io_caps; /* IO Capabilities of the remot device */ +} tBTM_SP_CFM_REQ; + +/* data type for BTM_SP_KEY_REQ_EVT */ +typedef struct { + BD_ADDR bd_addr; /* peer address */ + DEV_CLASS dev_class; /* peer CoD */ + tBTM_BD_NAME bd_name; /* peer device name */ +} tBTM_SP_KEY_REQ; + +/* data type for BTM_SP_KEY_NOTIF_EVT */ +typedef struct { + BD_ADDR bd_addr; /* peer address */ + DEV_CLASS dev_class; /* peer CoD */ + tBTM_BD_NAME bd_name; /* peer device name */ + UINT32 passkey; /* passkey */ +} tBTM_SP_KEY_NOTIF; + +enum { + BTM_SP_KEY_STARTED, /* 0 - passkey entry started */ + BTM_SP_KEY_ENTERED, /* 1 - passkey digit entered */ + BTM_SP_KEY_ERASED, /* 2 - passkey digit erased */ + BTM_SP_KEY_CLEARED, /* 3 - passkey cleared */ + BTM_SP_KEY_COMPLT, /* 4 - passkey entry completed */ + BTM_SP_KEY_OUT_OF_RANGE /* 5 - out of range */ +}; +typedef UINT8 tBTM_SP_KEY_TYPE; + +/* data type for BTM_SP_KEYPRESS_EVT */ +typedef struct { + BD_ADDR bd_addr; /* peer address */ + tBTM_SP_KEY_TYPE notif_type; +} tBTM_SP_KEYPRESS; + +/* data type for BTM_SP_LOC_OOB_EVT */ +typedef struct { + tBTM_STATUS status; /* */ + BT_OCTET16 c; /* Simple Pairing Hash C */ + BT_OCTET16 r; /* Simple Pairing Randomnizer R */ +} tBTM_SP_LOC_OOB; + +/* data type for BTM_SP_RMT_OOB_EVT */ +typedef struct { + BD_ADDR bd_addr; /* peer address */ + DEV_CLASS dev_class; /* peer CoD */ + tBTM_BD_NAME bd_name; /* peer device name */ +} tBTM_SP_RMT_OOB; + + +/* data type for BTM_SP_COMPLT_EVT */ +typedef struct { + BD_ADDR bd_addr; /* peer address */ + DEV_CLASS dev_class; /* peer CoD */ + tBTM_BD_NAME bd_name; /* peer device name */ + tBTM_STATUS status; /* status of the simple pairing process */ +} tBTM_SP_COMPLT; + +/* data type for BTM_SP_UPGRADE_EVT */ +typedef struct { + BD_ADDR bd_addr; /* peer address */ + BOOLEAN upgrade; /* TRUE, to upgrade the link key */ +} tBTM_SP_UPGRADE; + +typedef union { + tBTM_SP_IO_REQ io_req; /* BTM_SP_IO_REQ_EVT */ + tBTM_SP_IO_RSP io_rsp; /* BTM_SP_IO_RSP_EVT */ + tBTM_SP_CFM_REQ cfm_req; /* BTM_SP_CFM_REQ_EVT */ + tBTM_SP_KEY_NOTIF key_notif; /* BTM_SP_KEY_NOTIF_EVT */ + tBTM_SP_KEY_REQ key_req; /* BTM_SP_KEY_REQ_EVT */ + tBTM_SP_KEYPRESS key_press; /* BTM_SP_KEYPRESS_EVT */ + tBTM_SP_LOC_OOB loc_oob; /* BTM_SP_LOC_OOB_EVT */ + tBTM_SP_RMT_OOB rmt_oob; /* BTM_SP_RMT_OOB_EVT */ + tBTM_SP_COMPLT complt; /* BTM_SP_COMPLT_EVT */ + tBTM_SP_UPGRADE upgrade; /* BTM_SP_UPGRADE_EVT */ +} tBTM_SP_EVT_DATA; + +/* Simple Pairing Events. Called by the stack when Simple Pairing related +** events occur. +*/ +typedef UINT8 (tBTM_SP_CALLBACK) (tBTM_SP_EVT event, tBTM_SP_EVT_DATA *p_data); + + +typedef void (tBTM_MKEY_CALLBACK) (BD_ADDR bd_addr, UINT8 status, UINT8 key_flag) ; + +/* Encryption enabled/disabled complete: Optionally passed with BTM_SetEncryption. +** Parameters are +** BD Address of remote +** optional data passed in by BTM_SetEncryption +** tBTM_STATUS - result of the operation +*/ +typedef void (tBTM_SEC_CBACK) (BD_ADDR bd_addr, tBT_TRANSPORT transport, + void *p_ref_data, tBTM_STATUS result); + +/* Bond Cancel complete. Parameters are +** Result of the cancel operation +** +*/ +typedef void (tBTM_BOND_CANCEL_CMPL_CALLBACK) (tBTM_STATUS result); + +/* LE related event and data structure +*/ +/* relate to ESP_LE_KEY_xxx in esp_gap_ble_api.h */ +#if (SMP_INCLUDED == TRUE) +#define BTM_LE_IO_REQ_EVT SMP_IO_CAP_REQ_EVT /* received IO_CAPABILITY_REQUEST event */ +#define BTM_LE_SEC_REQUEST_EVT SMP_SEC_REQUEST_EVT /* security request event */ +#define BTM_LE_KEY_NOTIF_EVT SMP_PASSKEY_NOTIF_EVT /* received USER_PASSKEY_NOTIFY event */ +#define BTM_LE_KEY_REQ_EVT SMP_PASSKEY_REQ_EVT /* received USER_PASSKEY_REQUEST event */ +#define BTM_LE_OOB_REQ_EVT SMP_OOB_REQ_EVT /* OOB data request event */ +#define BTM_LE_NC_REQ_EVT SMP_NC_REQ_EVT /* Numeric Comparison request event */ +#define BTM_LE_PR_KEYPR_NOT_EVT SMP_PEER_KEYPR_NOT_EVT /* Peer keypress notification recd event */ +/* SC OOB request event (both local and peer OOB data) can be expected in response */ +#define BTM_LE_SC_OOB_REQ_EVT SMP_SC_OOB_REQ_EVT +/* SC OOB local data set is created (as result of SMP_CrLocScOobData(...)) */ +#define BTM_LE_SC_LOC_OOB_EVT SMP_SC_LOC_OOB_DATA_UP_EVT +#define BTM_LE_BR_KEYS_REQ_EVT SMP_BR_KEYS_REQ_EVT /* SMP over BR keys request event */ +#define BTM_LE_COMPLT_EVT SMP_COMPLT_EVT /* SMP complete event */ +#define BTM_LE_LAST_FROM_SMP BTM_LE_BR_KEYS_REQ_EVT +#define BTM_LE_KEY_EVT BTM_LE_LAST_FROM_SMP + 1 /* KEY update event */ +#endif ///SMP_INCLUDED == TRUE +typedef UINT8 tBTM_LE_EVT; + +#if (BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE) +#define BTM_LE_KEY_NONE 0 +#define BTM_LE_KEY_PENC SMP_SEC_KEY_TYPE_ENC /* encryption information of peer device */ +#define BTM_LE_KEY_PID SMP_SEC_KEY_TYPE_ID /* identity key of the peer device */ +#define BTM_LE_KEY_PCSRK SMP_SEC_KEY_TYPE_CSRK /* peer SRK */ +#define BTM_LE_KEY_PLK SMP_SEC_KEY_TYPE_LK +#define BTM_LE_KEY_LLK (SMP_SEC_KEY_TYPE_LK << 4) +#define BTM_LE_KEY_LENC (SMP_SEC_KEY_TYPE_ENC << 4) /* master role security information:div */ +#define BTM_LE_KEY_LID (SMP_SEC_KEY_TYPE_ID << 4) /* master device ID key */ +#define BTM_LE_KEY_LCSRK (SMP_SEC_KEY_TYPE_CSRK << 4) /* local CSRK has been deliver to peer */ +#endif ///BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE +typedef UINT8 tBTM_LE_KEY_TYPE; + +/* relate to ESP_LE_AUTH_xxx in esp_gap_ble_api.h */ +#if (SMP_INCLUDED == TRUE) +#define BTM_LE_AUTH_REQ_NO_BOND SMP_AUTH_NO_BOND /* 0 */ +#define BTM_LE_AUTH_REQ_BOND SMP_AUTH_GEN_BOND /* 1 << 0 */ +#define BTM_LE_AUTH_REQ_MITM SMP_AUTH_YN_BIT /* 1 << 2 */ +#endif ///SMP_INCLUDED == TRUE +typedef UINT8 tBTM_LE_AUTH_REQ; +#if (SMP_INCLUDED == TRUE) +#define BTM_LE_SC_SUPPORT_BIT SMP_SC_SUPPORT_BIT /* (1 << 3) */ +#define BTM_LE_KP_SUPPORT_BIT SMP_KP_SUPPORT_BIT /* (1 << 4) */ + +#define BTM_LE_AUTH_REQ_SC_ONLY SMP_AUTH_SC_ENC_ONLY /* 1 << 3 */ +#define BTM_LE_AUTH_REQ_SC_BOND SMP_AUTH_SC_GB /* 1001 */ +#define BTM_LE_AUTH_REQ_SC_MITM SMP_AUTH_SC_MITM_NB /* 1100 */ +#define BTM_LE_AUTH_REQ_SC_MITM_BOND SMP_AUTH_SC_MITM_GB /* 1101 */ +#define BTM_LE_AUTH_REQ_MASK SMP_AUTH_MASK /* 0x1D */ + +/* LE security level */ +#define BTM_LE_SEC_NONE SMP_SEC_NONE +#define BTM_LE_SEC_UNAUTHENTICATE SMP_SEC_UNAUTHENTICATE /* 1 */ +#define BTM_LE_SEC_AUTHENTICATED SMP_SEC_AUTHENTICATED /* 4 */ +#endif ///SMP_INCLUDED == TRUE +typedef UINT8 tBTM_LE_SEC; + + +typedef struct { + tBTM_IO_CAP io_cap; /* local IO capabilities */ + UINT8 oob_data; /* OOB data present (locally) for the peer device */ + tBTM_LE_AUTH_REQ auth_req; /* Authentication request (for local device) contain bonding and MITM info */ + UINT8 max_key_size; /* max encryption key size */ + tBTM_LE_KEY_TYPE init_keys; /* keys to be distributed, bit mask */ + tBTM_LE_KEY_TYPE resp_keys; /* keys to be distributed, bit mask */ +} tBTM_LE_IO_REQ; + +#if BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE +/* data type for tBTM_LE_COMPLT */ +typedef struct { + UINT8 reason; + UINT8 sec_level; + BOOLEAN is_pair_cancel; + BOOLEAN smp_over_br; + tSMP_AUTH_REQ auth_mode; +} tBTM_LE_COMPLT; +#endif + +/* BLE encryption keys */ +typedef struct { + BT_OCTET16 ltk; + BT_OCTET8 rand; + UINT16 ediv; + UINT8 sec_level; + UINT8 key_size; +} tBTM_LE_PENC_KEYS; + +/* BLE CSRK keys */ +typedef struct { + UINT32 counter; + BT_OCTET16 csrk; + UINT8 sec_level; +} tBTM_LE_PCSRK_KEYS; + +/* BLE Encryption reproduction keys */ +typedef struct { + BT_OCTET16 ltk; + UINT16 div; + UINT8 key_size; + UINT8 sec_level; +} tBTM_LE_LENC_KEYS; + +/* BLE SRK keys */ +typedef struct { + UINT32 counter; + UINT16 div; + UINT8 sec_level; + BT_OCTET16 csrk; +} tBTM_LE_LCSRK_KEYS; + +typedef struct { + BT_OCTET16 irk; + tBLE_ADDR_TYPE addr_type; + BD_ADDR static_addr; +} tBTM_LE_PID_KEYS; + +typedef union { + tBTM_LE_PENC_KEYS penc_key; /* received peer encryption key */ + tBTM_LE_PCSRK_KEYS pcsrk_key; /* received peer device SRK */ + tBTM_LE_PID_KEYS pid_key; /* peer device ID key */ + tBTM_LE_LENC_KEYS lenc_key; /* local encryption reproduction keys LTK = = d1(ER,DIV,0)*/ + tBTM_LE_LCSRK_KEYS lcsrk_key; /* local device CSRK = d1(ER,DIV,1)*/ +} tBTM_LE_KEY_VALUE; + +typedef struct { + tBTM_LE_KEY_TYPE key_type; + tBTM_LE_KEY_VALUE *p_key_value; +} tBTM_LE_KEY; + +typedef union { + tBTM_LE_IO_REQ io_req; /* BTM_LE_IO_REQ_EVT */ + UINT32 key_notif; /* BTM_LE_KEY_NOTIF_EVT */ + /* BTM_LE_NC_REQ_EVT */ + /* no callback data for BTM_LE_KEY_REQ_EVT */ + /* and BTM_LE_OOB_REQ_EVT */ +#if BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE + tBTM_LE_COMPLT complt; /* BTM_LE_COMPLT_EVT */ + tSMP_OOB_DATA_TYPE req_oob_type; + tSMP_LOC_OOB_DATA local_oob_data; +#endif + tBTM_LE_KEY key; +} tBTM_LE_EVT_DATA; + +/* Simple Pairing Events. Called by the stack when Simple Pairing related +** events occur. +*/ +typedef UINT8 (tBTM_LE_CALLBACK) (tBTM_LE_EVT event, BD_ADDR bda, tBTM_LE_EVT_DATA *p_data); + +#define BTM_BLE_KEY_TYPE_ID 1 +#define BTM_BLE_KEY_TYPE_ER 2 +#define BTM_BLE_KEY_TYPE_COUNTER 3 //tobe obsolete + +typedef struct { + BT_OCTET16 ir; + BT_OCTET16 irk; + BT_OCTET16 dhk; + +} tBTM_BLE_LOCAL_ID_KEYS; + +typedef union { + tBTM_BLE_LOCAL_ID_KEYS id_keys; + BT_OCTET16 er; +} tBTM_BLE_LOCAL_KEYS; + + +/* New LE identity key for local device. +*/ +typedef void (tBTM_LE_KEY_CALLBACK) (UINT8 key_type, tBTM_BLE_LOCAL_KEYS *p_key); + + +/*************************** +** Security Manager Types +****************************/ +/* Structure that applications use to register with BTM_SecRegister */ +typedef struct { + tBTM_AUTHORIZE_CALLBACK *p_authorize_callback; + tBTM_PIN_CALLBACK *p_pin_callback; + tBTM_LINK_KEY_CALLBACK *p_link_key_callback; + tBTM_AUTH_COMPLETE_CALLBACK *p_auth_complete_callback; + tBTM_BOND_CANCEL_CMPL_CALLBACK *p_bond_cancel_cmpl_callback; + tBTM_ENC_CHANGE_CALLBACK *p_enc_change_callback; + tBTM_SP_CALLBACK *p_sp_callback; +#if BLE_INCLUDED == TRUE +#if SMP_INCLUDED == TRUE + tBTM_LE_CALLBACK *p_le_callback; +#endif + tBTM_LE_KEY_CALLBACK *p_le_key_callback; +#endif +} tBTM_APPL_INFO; + +/* Callback function for when a link supervision timeout event occurs. +** This asynchronous event is enabled/disabled by calling BTM_RegForLstoEvt(). +*/ +typedef void (tBTM_LSTO_CBACK) (BD_ADDR remote_bda, UINT16 timeout); + +/***************************************************************************** +** POWER MANAGEMENT +*****************************************************************************/ +/**************************** +** Power Manager Constants +*****************************/ +/* BTM Power manager status codes */ +enum { + BTM_PM_STS_ACTIVE = HCI_MODE_ACTIVE, + BTM_PM_STS_HOLD = HCI_MODE_HOLD, + BTM_PM_STS_SNIFF = HCI_MODE_SNIFF, + BTM_PM_STS_PARK = HCI_MODE_PARK, + BTM_PM_STS_SSR, /* report the SSR parameters in HCI_SNIFF_SUB_RATE_EVT */ + BTM_PM_STS_PENDING, /* when waiting for status from controller */ + BTM_PM_STS_ERROR /* when HCI command status returns error */ +}; +typedef UINT8 tBTM_PM_STATUS; + +/* BTM Power manager modes */ +enum { + BTM_PM_MD_ACTIVE = BTM_PM_STS_ACTIVE, + BTM_PM_MD_HOLD = BTM_PM_STS_HOLD, + BTM_PM_MD_SNIFF = BTM_PM_STS_SNIFF, + BTM_PM_MD_PARK = BTM_PM_STS_PARK, + BTM_PM_MD_FORCE = 0x10 /* OR this to force ACL link to a certain mode */ +}; +typedef UINT8 tBTM_PM_MODE; + +#define BTM_PM_SET_ONLY_ID 0x80 + +/* Operation codes */ +#define BTM_PM_REG_SET 1 /* The module wants to set the desired power mode */ +#define BTM_PM_REG_NOTIF 2 /* The module wants to receive mode change event */ +#define BTM_PM_DEREG 4 /* The module does not want to involve with PM anymore */ + +/************************ +** Power Manager Types +*************************/ +typedef struct { + UINT16 max; + UINT16 min; + UINT16 attempt; + UINT16 timeout; + tBTM_PM_MODE mode; +} tBTM_PM_PWR_MD; + +/************************************* +** Power Manager Callback Functions +**************************************/ +typedef void (tBTM_PM_STATUS_CBACK) (BD_ADDR p_bda, tBTM_PM_STATUS status, + UINT16 value, UINT8 hci_status); + + +/************************ +** Stored Linkkey Types +*************************/ +#define BTM_CB_EVT_DELETE_STORED_LINK_KEYS 4 + +typedef struct { + UINT8 event; + UINT8 status; + UINT16 num_keys; + +} tBTM_DELETE_STORED_LINK_KEY_COMPLETE; + +/* MIP evnets, callbacks */ +enum { + BTM_MIP_MODE_CHG_EVT, + BTM_MIP_DISCONNECT_EVT, + BTM_MIP_PKTS_COMPL_EVT, + BTM_MIP_RXDATA_EVT +}; +typedef UINT8 tBTM_MIP_EVT; + +typedef struct { + tBTM_MIP_EVT event; + BD_ADDR bd_addr; + UINT16 mip_id; +} tBTM_MIP_MODE_CHANGE; + +typedef struct { + tBTM_MIP_EVT event; + UINT16 mip_id; + UINT8 disc_reason; +} tBTM_MIP_CONN_TIMEOUT; + +#define BTM_MIP_MAX_RX_LEN 17 + +typedef struct { + tBTM_MIP_EVT event; + UINT16 mip_id; + UINT8 rx_len; + UINT8 rx_data[BTM_MIP_MAX_RX_LEN]; +} tBTM_MIP_RXDATA; + +typedef struct { + tBTM_MIP_EVT event; + BD_ADDR bd_addr; + UINT8 data[11]; /* data[0] shows Vender-specific device type */ +} tBTM_MIP_EIR_HANDSHAKE; + +typedef struct { + tBTM_MIP_EVT event; + UINT16 num_sent; /* Number of packets completed at the controller */ +} tBTM_MIP_PKTS_COMPL; + +typedef union { + tBTM_MIP_EVT event; + tBTM_MIP_MODE_CHANGE mod_chg; + tBTM_MIP_CONN_TIMEOUT conn_tmo; + tBTM_MIP_EIR_HANDSHAKE eir; + tBTM_MIP_PKTS_COMPL completed; + tBTM_MIP_RXDATA rxdata; +} tBTM_MIP_EVENT_DATA; + +/* MIP event callback function */ +typedef void (tBTM_MIP_EVENTS_CB) (tBTM_MIP_EVT event, tBTM_MIP_EVENT_DATA data); + +/* MIP Device query callback function */ +typedef BOOLEAN (tBTM_MIP_QUERY_CB) (BD_ADDR dev_addr, UINT8 *p_mode, LINK_KEY link_key); + +#define BTM_CONTRL_ACTIVE 1 /* ACL link on, SCO link ongoing, sniff mode */ +#define BTM_CONTRL_SCAN 2 /* Scan state - paging/inquiry/trying to connect*/ +#define BTM_CONTRL_IDLE 3 /* Idle state - page scan, LE advt, inquiry scan */ + +typedef UINT8 tBTM_CONTRL_STATE; + +/***************************************************************************** +** EXTERNAL FUNCTION DECLARATIONS +*****************************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +/***************************************************************************** +** DEVICE CONTROL and COMMON FUNCTIONS +*****************************************************************************/ + +/******************************************************************************* +** +** Function BTM_DeviceReset +** +** Description This function is called to reset the controller.The Callback function +** if provided is called when startup of the device has +** completed. +** +** Returns void +** +*******************************************************************************/ +//extern +void BTM_DeviceReset (tBTM_CMPL_CB *p_cb); + + +/******************************************************************************* +** +** Function BTM_IsDeviceUp +** +** Description This function is called to check if the device is up. +** +** Returns TRUE if device is up, else FALSE +** +*******************************************************************************/ +//extern +BOOLEAN BTM_IsDeviceUp (void); + + +/******************************************************************************* +** +** Function BTM_SetLocalDeviceName +** +** Description This function is called to set the local device name. +** +** Returns BTM_CMD_STARTED if successful, otherwise an error +** +*******************************************************************************/ +//extern +tBTM_STATUS BTM_SetLocalDeviceName (char *p_name); + +/******************************************************************************* +** +** Function BTM_SetDeviceClass +** +** Description This function is called to set the local device class +** +** Returns BTM_SUCCESS if successful, otherwise an error +** +*******************************************************************************/ +//extern +tBTM_STATUS BTM_SetDeviceClass (DEV_CLASS dev_class); + + +/******************************************************************************* +** +** Function BTM_ReadLocalDeviceName +** +** Description This function is called to read the local device name. +** +** Returns status of the operation +** If success, BTM_SUCCESS is returned and p_name points stored +** local device name +** If BTM doesn't store local device name, BTM_NO_RESOURCES is +** is returned and p_name is set to NULL +** +*******************************************************************************/ +//extern +tBTM_STATUS BTM_ReadLocalDeviceName (char **p_name); + +/******************************************************************************* +** +** Function BTM_ReadLocalDeviceNameFromController +** +** Description Get local device name from controller. Do not use cached +** name (used to get chip-id prior to btm reset complete). +** +** Returns BTM_CMD_STARTED if successful, otherwise an error +** +*******************************************************************************/ +//extern +tBTM_STATUS BTM_ReadLocalDeviceNameFromController (tBTM_CMPL_CB *p_rln_cmpl_cback); + +/******************************************************************************* +** +** Function BTM_ReadDeviceClass +** +** Description This function is called to read the local device class +** +** Returns pointer to the device class +** +*******************************************************************************/ +//extern +UINT8 *BTM_ReadDeviceClass (void); + + +/******************************************************************************* +** +** Function BTM_ReadLocalFeatures +** +** Description This function is called to read the local features +** +** Returns pointer to the local features string +** +*******************************************************************************/ +//extern +UINT8 *BTM_ReadLocalFeatures (void); + +/******************************************************************************* +** +** Function BTM_RegisterForDeviceStatusNotif +** +** Description This function is called to register for device status +** change notifications. +** +** Returns pointer to previous caller's callback function or NULL if first +** registration. +** +*******************************************************************************/ +//extern +tBTM_DEV_STATUS_CB *BTM_RegisterForDeviceStatusNotif (tBTM_DEV_STATUS_CB *p_cb); + + +/******************************************************************************* +** +** Function BTM_RegisterForVSEvents +** +** Description This function is called to register/deregister for vendor +** specific HCI events. +** +** If is_register=TRUE, then the function will be registered; +** if is_register=FALSE, then the function will be deregistered. +** +** Returns BTM_SUCCESS if successful, +** BTM_BUSY if maximum number of callbacks have already been +** registered. +** +*******************************************************************************/ +//extern +tBTM_STATUS BTM_RegisterForVSEvents (tBTM_VS_EVT_CB *p_cb, BOOLEAN is_register); + + +/******************************************************************************* +** +** Function BTM_VendorSpecificCommand +** +** Description Send a vendor specific HCI command to the controller. +** +** Returns +** BTM_SUCCESS Command sent. Does not expect command complete +** event. (command cmpl callback param is NULL) +** BTM_CMD_STARTED Command sent. Waiting for command cmpl event. +** BTM_BUSY Command not sent. Waiting for cmd cmpl event for +** prior command. +** +*******************************************************************************/ +//extern +tBTM_STATUS BTM_VendorSpecificCommand(UINT16 opcode, + UINT8 param_len, + UINT8 *p_param_buf, + tBTM_VSC_CMPL_CB *p_cb); + +/******************************************************************************* +** +** Function BTM_ConfigCoexStatus +** +** Description Config coexist status through vendor specific HCI command. +** +** Returns +** BTM_SUCCESS Command sent. Does not expect command complete +** event. (command cmpl callback param is NULL) +** BTM_NO_RESOURCES Command not sent. No resources. +** +*******************************************************************************/ +#if (ESP_COEX_VSC_INCLUDED == TRUE) +tBTM_STATUS BTM_ConfigCoexStatus(tBTM_COEX_OPERATION op, tBTM_COEX_TYPE type, UINT8 status); +#endif + +/******************************************************************************* +** +** Function BTM_AllocateSCN +** +** Description Look through the Server Channel Numbers for a free one to be +** used with an RFCOMM connection. +** +** Returns Allocated SCN number or 0 if none. +** +*******************************************************************************/ +//extern +#if (CLASSIC_BT_INCLUDED == TRUE) +UINT8 BTM_AllocateSCN(void); + +// btla-specific ++ +/******************************************************************************* +** +** Function BTM_TryAllocateSCN +** +** Description Try to allocate a fixed server channel +** +** Returns Returns TRUE if server channel was available +** +*******************************************************************************/ +//extern +BOOLEAN BTM_TryAllocateSCN(UINT8 scn); +// btla-specific -- + + +/******************************************************************************* +** +** Function BTM_FreeSCN +** +** Description Free the specified SCN. +** +** Returns TRUE if successful, FALSE if SCN is not in use or invalid +** +*******************************************************************************/ +//extern +BOOLEAN BTM_FreeSCN(UINT8 scn); +#endif ///CLASSIC_BT_INCLUDED == TRUE + + +/******************************************************************************* +** +** Function BTM_SetTraceLevel +** +** Description This function sets the trace level for BTM. If called with +** a value of 0xFF, it simply returns the current trace level. +** +** Returns The new or current trace level +** +*******************************************************************************/ +//extern +UINT8 BTM_SetTraceLevel (UINT8 new_level); + + +/******************************************************************************* +** +** Function BTM_WritePageTimeout +** +** Description Send HCI Wite Page Timeout. +** +** Returns +** BTM_SUCCESS Command sent. +** BTM_NO_RESOURCES If out of resources to send the command. +** +*******************************************************************************/ +//extern +tBTM_STATUS BTM_WritePageTimeout(UINT16 timeout, tBTM_CMPL_CB *p_cb); + +/******************************************************************************* +** +** Function BTM_ReadPageTimeout +** +** Description Send HCI Read Page Timeout. +** +** Returns +** BTM_SUCCESS Command sent. +** BTM_NO_RESOURCES If out of resources to send the command. +** +*******************************************************************************/ +//extern +tBTM_STATUS BTM_ReadPageTimeout(tBTM_CMPL_CB *p_cb); + +/******************************************************************************* +** +** Function BTM_SetAclPktTypes +** +** Description Send HCI Change Connection Packet Type +** +** Returns +** BTM_SUCCESS Command sent. +** BTM_NO_RESOURCES If out of resources to send the command. +** +*******************************************************************************/ +//extern +tBTM_STATUS BTM_SetAclPktTypes(BD_ADDR remote_bda, UINT16 pkt_types, tBTM_CMPL_CB *p_cb); + +/******************************************************************************* +** +** Function BTM_WriteVoiceSettings +** +** Description Send HCI Write Voice Settings command. +** See stack/hcidefs.h for settings bitmask values. +** +** Returns +** BTM_SUCCESS Command sent. +** BTM_NO_RESOURCES If out of resources to send the command. +** +** +*******************************************************************************/ +//extern +tBTM_STATUS BTM_WriteVoiceSettings(UINT16 settings); + +/******************************************************************************* +** +** Function BTM_EnableTestMode +** +** Description Send HCI the enable device under test command. +** +** Note: Controller can only be taken out of this mode by +** resetting the controller. +** +** Returns +** BTM_SUCCESS Command sent. +** BTM_NO_RESOURCES If out of resources to send the command. +** +** +*******************************************************************************/ +//extern +tBTM_STATUS BTM_EnableTestMode(void); + + +/***************************************************************************** +** DEVICE DISCOVERY FUNCTIONS - Inquiry, Remote Name, Discovery, Class of Device +*****************************************************************************/ + +/******************************************************************************* +** +** Function BTM_SetDiscoverability +** +** Description This function is called to set the device into or out of +** discoverable mode. Discoverable mode means inquiry +** scans are enabled. If a value of '0' is entered for window or +** interval, the default values are used. +** +** Returns BTM_SUCCESS if successful +** BTM_BUSY if a setting of the filter is already in progress +** BTM_NO_RESOURCES if couldn't get a memory pool buffer +** BTM_ILLEGAL_VALUE if a bad parameter was detected +** BTM_WRONG_MODE if the device is not up. +** +*******************************************************************************/ +//extern +tBTM_STATUS BTM_SetDiscoverability (UINT16 inq_mode, UINT16 window, + UINT16 interval); + + +/******************************************************************************* +** +** Function BTM_ReadDiscoverability +** +** Description This function is called to read the current discoverability +** mode of the device. +** +** Output Params: p_window - current inquiry scan duration +** p_interval - current inquiry scan interval +** +** Returns BTM_NON_DISCOVERABLE, BTM_LIMITED_DISCOVERABLE, or +** BTM_GENERAL_DISCOVERABLE +** +*******************************************************************************/ +//extern +UINT16 BTM_ReadDiscoverability (UINT16 *p_window, + UINT16 *p_interval); + + +/******************************************************************************* +** +** Function BTM_SetPeriodicInquiryMode +** +** Description This function is called to set the device periodic inquiry mode. +** If the duration is zero, the periodic inquiry mode is cancelled. +** +** Parameters: p_inqparms - pointer to the inquiry information +** mode - GENERAL or LIMITED inquiry +** duration - length in 1.28 sec intervals (If '0', the inquiry is CANCELLED) +** max_resps - maximum amount of devices to search for before ending the inquiry +** filter_cond_type - BTM_CLR_INQUIRY_FILTER, BTM_FILTER_COND_DEVICE_CLASS, or +** BTM_FILTER_COND_BD_ADDR +** filter_cond - value for the filter (based on filter_cond_type) +** +** max_delay - maximum amount of time between successive inquiries +** min_delay - minimum amount of time between successive inquiries +** p_results_cb - callback returning pointer to results (tBTM_INQ_RESULTS) +** +** Returns BTM_CMD_STARTED if successfully started +** BTM_ILLEGAL_VALUE if a bad parameter is detected +** BTM_NO_RESOURCES if could not allocate a message buffer +** BTM_SUCCESS - if cancelling the periodic inquiry +** BTM_BUSY - if an inquiry is already active +** BTM_WRONG_MODE if the device is not up. +** +*******************************************************************************/ +//extern +tBTM_STATUS BTM_SetPeriodicInquiryMode (tBTM_INQ_PARMS *p_inqparms, + UINT16 max_delay, UINT16 min_delay, + tBTM_INQ_RESULTS_CB *p_results_cb); + + +/******************************************************************************* +** +** Function BTM_StartInquiry +** +** Description This function is called to start an inquiry. +** +** Parameters: p_inqparms - pointer to the inquiry information +** mode - GENERAL or LIMITED inquiry +** duration - length in 1.28 sec intervals (If '0', the inquiry is CANCELLED) +** max_resps - maximum amount of devices to search for before ending the inquiry +** filter_cond_type - BTM_CLR_INQUIRY_FILTER, BTM_FILTER_COND_DEVICE_CLASS, or +** BTM_FILTER_COND_BD_ADDR +** filter_cond - value for the filter (based on filter_cond_type) +** +** p_results_cb - Pointer to the callback routine which gets called +** upon receipt of an inquiry result. If this field is +** NULL, the application is not notified. +** +** p_cmpl_cb - Pointer to the callback routine which gets called +** upon completion. If this field is NULL, the +** application is not notified when completed. +** Returns tBTM_STATUS +** BTM_CMD_STARTED if successfully initiated +** BTM_BUSY if already in progress +** BTM_ILLEGAL_VALUE if parameter(s) are out of range +** BTM_NO_RESOURCES if could not allocate resources to start the command +** BTM_WRONG_MODE if the device is not up. +** +*******************************************************************************/ +//extern +tBTM_STATUS BTM_StartInquiry (tBTM_INQ_PARMS *p_inqparms, + tBTM_INQ_RESULTS_CB *p_results_cb, + tBTM_CMPL_CB *p_cmpl_cb); + + +/******************************************************************************* +** +** Function BTM_IsInquiryActive +** +** Description This function returns a bit mask of the current inquiry state +** +** Returns BTM_INQUIRY_INACTIVE if inactive (0) +** BTM_LIMITED_INQUIRY_ACTIVE if a limted inquiry is active +** BTM_GENERAL_INQUIRY_ACTIVE if a general inquiry is active +** BTM_PERIODIC_INQUIRY_ACTIVE if a periodic inquiry is active +** +*******************************************************************************/ +//extern +UINT16 BTM_IsInquiryActive (void); + + +/******************************************************************************* +** +** Function BTM_CancelInquiry +** +** Description This function cancels an inquiry if active +** +** Returns BTM_SUCCESS if successful +** BTM_NO_RESOURCES if could not allocate a message buffer +** BTM_WRONG_MODE if the device is not up. +** +*******************************************************************************/ +//extern +tBTM_STATUS BTM_CancelInquiry(void); + + +/******************************************************************************* +** +** Function BTM_CancelPeriodicInquiry +** +** Description This function cancels a periodic inquiry +** +** Returns +** BTM_NO_RESOURCES if could not allocate a message buffer +** BTM_SUCCESS - if cancelling the periodic inquiry +** BTM_WRONG_MODE if the device is not up. +** +*******************************************************************************/ +//extern +tBTM_STATUS BTM_CancelPeriodicInquiry(void); + + +/******************************************************************************* +** +** Function BTM_SetConnectability +** +** Description This function is called to set the device into or out of +** connectable mode. Discoverable mode means page scans enabled. +** +** Returns BTM_SUCCESS if successful +** BTM_ILLEGAL_VALUE if a bad parameter is detected +** BTM_NO_RESOURCES if could not allocate a message buffer +** BTM_WRONG_MODE if the device is not up. +** +*******************************************************************************/ +//extern +tBTM_STATUS BTM_SetConnectability (UINT16 page_mode, UINT16 window, + UINT16 interval); + + +/******************************************************************************* +** +** Function BTM_ReadConnectability +** +** Description This function is called to read the current discoverability +** mode of the device. +** Output Params p_window - current page scan duration +** p_interval - current time between page scans +** +** Returns BTM_NON_CONNECTABLE or BTM_CONNECTABLE +** +*******************************************************************************/ +//extern +UINT16 BTM_ReadConnectability (UINT16 *p_window, UINT16 *p_interval); + + +/******************************************************************************* +** +** Function BTM_SetInquiryMode +** +** Description This function is called to set standard, with RSSI +** mode or extended of the inquiry for local device. +** +** Input Params: BTM_INQ_RESULT_STANDARD, BTM_INQ_RESULT_WITH_RSSI or +** BTM_INQ_RESULT_EXTENDED +** +** Returns BTM_SUCCESS if successful +** BTM_NO_RESOURCES if couldn't get a memory pool buffer +** BTM_ILLEGAL_VALUE if a bad parameter was detected +** BTM_WRONG_MODE if the device is not up. +** +*******************************************************************************/ +//extern +tBTM_STATUS BTM_SetInquiryMode (UINT8 mode); + +/******************************************************************************* +** +** Function BTM_SetInquiryScanType +** +** Description This function is called to set the iquiry scan-type to +** standard or interlaced. +** +** Input Params: BTM_SCAN_TYPE_STANDARD or BTM_SCAN_TYPE_INTERLACED +** +** Returns BTM_SUCCESS if successful +** BTM_MODE_UNSUPPORTED if not a 1.2 device +** BTM_WRONG_MODE if the device is not up. +** +*******************************************************************************/ +//extern +tBTM_STATUS BTM_SetInquiryScanType (UINT16 scan_type); + +/******************************************************************************* +** +** Function BTM_SetPageScanType +** +** Description This function is called to set the page scan-type to +** standard or interlaced. +** +** Input Params: BTM_SCAN_TYPE_STANDARD or BTM_SCAN_TYPE_INTERLACED +** +** Returns BTM_SUCCESS if successful +** BTM_MODE_UNSUPPORTED if not a 1.2 device +** BTM_WRONG_MODE if the device is not up. +** +*******************************************************************************/ + +//extern +tBTM_STATUS BTM_SetPageScanType (UINT16 scan_type); + +/******************************************************************************* +** +** Function BTM_ReadRemoteDeviceName +** +** Description This function initiates a remote device HCI command to the +** controller and calls the callback when the process has completed. +** +** Input Params: remote_bda - device address of name to retrieve +** p_cb - callback function called when BTM_CMD_STARTED +** is returned. +** A pointer to tBTM_REMOTE_DEV_NAME is passed to the +** callback. +** +** Returns +** BTM_CMD_STARTED is returned if the request was successfully sent +** to HCI. +** BTM_BUSY if already in progress +** BTM_UNKNOWN_ADDR if device address is bad +** BTM_NO_RESOURCES if could not allocate resources to start the command +** BTM_WRONG_MODE if the device is not up. +** +*******************************************************************************/ +//extern +tBTM_STATUS BTM_ReadRemoteDeviceName (BD_ADDR remote_bda, + tBTM_CMPL_CB *p_cb, + tBT_TRANSPORT transport); + + +/******************************************************************************* +** +** Function BTM_CancelRemoteDeviceName +** +** Description This function initiates the cancel request for the specified +** remote device. +** +** Input Params: None +** +** Returns +** BTM_CMD_STARTED is returned if the request was successfully sent +** to HCI. +** BTM_NO_RESOURCES if could not allocate resources to start the command +** BTM_WRONG_MODE if there is not an active remote name request. +** +*******************************************************************************/ +//extern +tBTM_STATUS BTM_CancelRemoteDeviceName (void); + +/******************************************************************************* +** +** Function BTM_ReadRemoteVersion +** +** Description This function is called to read a remote device's version +** +** Returns BTM_SUCCESS if successful, otherwise an error +** +*******************************************************************************/ +//extern +tBTM_STATUS BTM_ReadRemoteVersion (BD_ADDR addr, + UINT8 *lmp_version, + UINT16 *manufacturer, + UINT16 *lmp_sub_version); + +/******************************************************************************* +** +** Function BTM_ReadRemoteFeatures +** +** Description This function is called to read a remote device's +** supported features mask (features mask located at page 0) +** +** Note: The size of device features mask page is +** BTM_FEATURE_BYTES_PER_PAGE bytes. +** +** Returns pointer to the remote supported features mask +** +*******************************************************************************/ +//extern +UINT8 *BTM_ReadRemoteFeatures (BD_ADDR addr); + +/******************************************************************************* +** +** Function BTM_ReadRemoteExtendedFeatures +** +** Description This function is called to read a specific extended features +** page of the remote device +** +** Note1: The size of device features mask page is +** BTM_FEATURE_BYTES_PER_PAGE bytes. +** Note2: The valid device features mask page number depends on +** the remote device capabilities. It is expected to be in the +** range [0 - BTM_EXT_FEATURES_PAGE_MAX]. + +** Returns pointer to the remote extended features mask +** or NULL if page_number is not valid +** +*******************************************************************************/ +//extern +UINT8 *BTM_ReadRemoteExtendedFeatures (BD_ADDR addr, UINT8 page_number); + +/******************************************************************************* +** +** Function BTM_ReadNumberRemoteFeaturesPages +** +** Description This function is called to retrieve the number of feature pages +** read from the remote device +** +** Returns number of features pages read from the remote device +** +*******************************************************************************/ +//extern +UINT8 BTM_ReadNumberRemoteFeaturesPages (BD_ADDR addr); + +/******************************************************************************* +** +** Function BTM_ReadAllRemoteFeatures +** +** Description This function is called to read all features of the remote device +** +** Returns pointer to the byte[0] of the page[0] of the remote device +** feature mask. +** +** Note: the function returns the pointer to the array of the size +** BTM_FEATURE_BYTES_PER_PAGE * (BTM_EXT_FEATURES_PAGE_MAX + 1). +** +*******************************************************************************/ +//extern +UINT8 *BTM_ReadAllRemoteFeatures (BD_ADDR addr); + +/******************************************************************************* +** +** Function BTM_InqDbRead +** +** Description This function looks through the inquiry database for a match +** based on Bluetooth Device Address. This is the application's +** interface to get the inquiry details of a specific BD address. +** +** Returns pointer to entry, or NULL if not found +** +*******************************************************************************/ +//extern +tBTM_INQ_INFO *BTM_InqDbRead (BD_ADDR p_bda); + + +/******************************************************************************* +** +** Function BTM_InqDbFirst +** +** Description This function looks through the inquiry database for the first +** used entry, and returns that. This is used in conjunction with +** BTM_InqDbNext by applications as a way to walk through the +** inquiry database. +** +** Returns pointer to first in-use entry, or NULL if DB is empty +** +*******************************************************************************/ +//extern +tBTM_INQ_INFO *BTM_InqDbFirst (void); + + +/******************************************************************************* +** +** Function BTM_InqDbNext +** +** Description This function looks through the inquiry database for the next +** used entry, and returns that. If the input parameter is NULL, +** the first entry is returned. +** +** Returns pointer to next in-use entry, or NULL if no more found. +** +*******************************************************************************/ +//extern +tBTM_INQ_INFO *BTM_InqDbNext (tBTM_INQ_INFO *p_cur); + + +/******************************************************************************* +** +** Function BTM_ClearInqDb +** +** Description This function is called to clear out a device or all devices +** from the inquiry database. +** +** Parameter p_bda - (input) BD_ADDR -> Address of device to clear +** (NULL clears all entries) +** +** Returns BTM_BUSY if an inquiry, get remote name, or event filter +** is active, otherwise BTM_SUCCESS +** +*******************************************************************************/ +//extern +tBTM_STATUS BTM_ClearInqDb (BD_ADDR p_bda); + +/******************************************************************************* +** +** Function BTM_ReadInquiryRspTxPower +** +** Description This command will read the inquiry Transmit Power level used +** to transmit the FHS and EIR data packets. +** This can be used directly in the Tx Power Level EIR data type. +** +** Returns BTM_SUCCESS if successful +** +*******************************************************************************/ +//extern +tBTM_STATUS BTM_ReadInquiryRspTxPower (tBTM_CMPL_CB *p_cb); + +#if SDP_INCLUDED == TRUE +/******************************************************************************* +** +** Function BTM_StartDiscovery +** +** Description This function is called by an application (or profile) +** when it wants to trigger an service discovery using the +** BTM's discovery database. +** +** Returns tBTM_STATUS +** BTM_CMD_STARTED if the discovery was initiated +** BTM_BUSY if one is already in progress +** BTM_UNKNOWN_ADDR if no addresses are in the INQ DB +** BTM_ERR_PROCESSING if err initiating the command +** +*******************************************************************************/ +//extern +tBTM_STATUS BTM_StartDiscovery (tBTM_CMPL_CB *p_cmpl_cb, + BD_ADDR_PTR p_rem_addr); + + +/******************************************************************************* +** +** Function BTM_FindAttribute +** +** Description This function is called by an application (or profile) +** when it wants to see if an attribute exists in the BTM +** discovery database. +** +** Returns Pointer to matching record, or NULL +** +*******************************************************************************/ +//extern +tSDP_DISC_REC *BTM_FindAttribute (UINT16 attr_id, + tSDP_DISC_REC *p_start_rec); + + +/******************************************************************************* +** +** Function BTM_FindService +** +** Description This function is called by an application (or profile) +** when it wants to see if a service exists in the BTM +** discovery database. +** +** Returns Pointer to matching record, or NULL +** +*******************************************************************************/ +//extern +tSDP_DISC_REC *BTM_FindService (UINT16 service_uuid, + tSDP_DISC_REC *p_start_rec); + + +/******************************************************************************* +** +** Function BTM_SetDiscoveryParams +** +** Description This function is called to set the BTM default discovery parameters. +** These UUID and attribute filters are used during the call to +** BTM_StartDiscovery. +** +** Returns void +** +*******************************************************************************/ +//extern +void BTM_SetDiscoveryParams (UINT16 num_uuid, tSDP_UUID *p_uuid_list, + UINT16 num_attr, UINT16 *p_attr_list); +#endif /*SDP_INCLUDED*/ + +/***************************************************************************** +** ACL CHANNEL MANAGEMENT FUNCTIONS +*****************************************************************************/ +/******************************************************************************* +** +** Function BTM_SetLinkPolicy +** +** Description Create and send HCI "Write Policy Set" command +** +** Returns BTM_CMD_STARTED if successfully initiated, otherwise error +** +*******************************************************************************/ +//extern +tBTM_STATUS BTM_SetLinkPolicy (BD_ADDR remote_bda, + UINT16 *settings); + +/******************************************************************************* +** +** Function BTM_SetDefaultLinkPolicy +** +** Description Set the default value for HCI "Write Policy Set" command +** to use when an ACL link is created. +** +** Returns void +** +*******************************************************************************/ +//extern +void BTM_SetDefaultLinkPolicy (UINT16 settings); + + +/******************************************************************************* +** +** Function BTM_SetDefaultLinkSuperTout +** +** Description Set the default value for HCI "Write Link Supervision Timeout" +** command to use when an ACL link is created. +** +** Returns void +** +*******************************************************************************/ +//extern +void BTM_SetDefaultLinkSuperTout (UINT16 timeout); + + +/******************************************************************************* +** +** Function BTM_SetLinkSuperTout +** +** Description Create and send HCI "Write Link Supervision Timeout" command +** +** Returns BTM_CMD_STARTED if successfully initiated, otherwise error +** +*******************************************************************************/ +//extern +tBTM_STATUS BTM_SetLinkSuperTout (BD_ADDR remote_bda, + UINT16 timeout); +/******************************************************************************* +** +** Function BTM_GetLinkSuperTout +** +** Description Read the link supervision timeout value of the connection +** +** Returns status of the operation +** +*******************************************************************************/ +//extern +tBTM_STATUS BTM_GetLinkSuperTout (BD_ADDR remote_bda, + UINT16 *p_timeout); + +/******************************************************************************* +** +** Function BTM_IsAclConnectionUp +** +** Description This function is called to check if an ACL connection exists +** to a specific remote BD Address. +** +** Returns TRUE if connection is up, else FALSE. +** +*******************************************************************************/ +//extern +BOOLEAN BTM_IsAclConnectionUp (BD_ADDR remote_bda, tBT_TRANSPORT transport); + + +/******************************************************************************* +** +** Function BTM_GetRole +** +** Description This function is called to get the role of the local device +** for the ACL connection with the specified remote device +** +** Returns BTM_SUCCESS if connection exists. +** BTM_UNKNOWN_ADDR if no active link with bd addr specified +** +*******************************************************************************/ +//extern +tBTM_STATUS BTM_GetRole (BD_ADDR remote_bd_addr, UINT8 *p_role); + + + +/******************************************************************************* +** +** Function BTM_SwitchRole +** +** Description This function is called to switch role between master and +** slave. If role is already set it will do nothing. If the +** command was initiated, the callback function is called upon +** completion. +** +** Returns BTM_SUCCESS if already in specified role. +** BTM_CMD_STARTED if command issued to controller. +** BTM_NO_RESOURCES if couldn't allocate memory to issue command +** BTM_UNKNOWN_ADDR if no active link with bd addr specified +** BTM_MODE_UNSUPPORTED if local device does not support role switching +** +*******************************************************************************/ +//extern +tBTM_STATUS BTM_SwitchRole (BD_ADDR remote_bd_addr, + UINT8 new_role, + tBTM_CMPL_CB *p_cb); + +/******************************************************************************* +** +** Function BTM_ReadRSSI +** +** Description This function is called to read the RSSI for a particular transport. +** The RSSI of results are returned in the callback. +** (tBTM_RSSI_RESULTS) +** +** Returns BTM_CMD_STARTED if command issued to controller. +** BTM_NO_RESOURCES if couldn't allocate memory to issue command +** BTM_UNKNOWN_ADDR if no active link with bd addr specified +** BTM_BUSY if command is already in progress +** +*******************************************************************************/ +//extern +tBTM_STATUS BTM_ReadRSSI (BD_ADDR remote_bda, tBT_TRANSPORT transport, tBTM_CMPL_CB *p_cb); + + +/******************************************************************************* +** +** Function BTM_ReadTxPower +** +** Description This function is called to read the current connection +** TX power of the connection. The TX power level results +** are returned in the callback. +** (tBTM_RSSI_RESULTS) +** +** Returns BTM_CMD_STARTED if command issued to controller. +** BTM_NO_RESOURCES if couldn't allocate memory to issue command +** BTM_UNKNOWN_ADDR if no active link with bd addr specified +** BTM_BUSY if command is already in progress +** +*******************************************************************************/ +//extern +tBTM_STATUS BTM_ReadTxPower (BD_ADDR remote_bda, + tBT_TRANSPORT transport, tBTM_CMPL_CB *p_cb); + +tBTM_STATUS BTM_BleReadAdvTxPower(tBTM_CMPL_CB *p_cb); + +void BTM_BleGetWhiteListSize(uint16_t *length); + + +/******************************************************************************* +** +** Function BTM_ReadLinkQuality +** +** Description This function is called to read the link quality. +** The value of the link quality is returned in the callback. +** (tBTM_LINK_QUALITY_RESULTS) +** +** Returns BTM_CMD_STARTED if command issued to controller. +** BTM_NO_RESOURCES if couldn't allocate memory to issue command +** BTM_UNKNOWN_ADDR if no active link with bd addr specified +** BTM_BUSY if command is already in progress +** +*******************************************************************************/ +//extern +tBTM_STATUS BTM_ReadLinkQuality (BD_ADDR remote_bda, tBTM_CMPL_CB *p_cb); + +/******************************************************************************* +** +** Function BTM_RegBusyLevelNotif +** +** Description This function is called to register a callback to receive +** busy level change events. +** +** Returns BTM_SUCCESS if successfully registered, otherwise error +** +*******************************************************************************/ +//extern +tBTM_STATUS BTM_RegBusyLevelNotif (tBTM_BL_CHANGE_CB *p_cb, UINT8 *p_level, + tBTM_BL_EVENT_MASK evt_mask); + +/******************************************************************************* +** +** Function BTM_RegAclLinkStatNotif +** +** Description This function is called to register a callback to receive +** ACL link related events. +** +** Returns BTM_SUCCESS if successfully registered, otherwise error +** +*******************************************************************************/ +tBTM_STATUS BTM_RegAclLinkStatNotif(tBTM_ACL_LINK_STAT_CB *p_cb); + +/******************************************************************************* +** +** Function BTM_AclRegisterForChanges +** +** Description This function is called to register a callback to receive +** ACL database change events, i.e. new connection or removed. +** +** Returns BTM_SUCCESS if successfully initiated, otherwise error +** +*******************************************************************************/ +//extern +tBTM_STATUS BTM_AclRegisterForChanges (tBTM_ACL_DB_CHANGE_CB *p_cb); + +/******************************************************************************* +** +** Function BTM_GetNumAclLinks +** +** Description This function is called to count the number of +** ACL links that are active. +** +** Returns UINT16 Number of active ACL links +** +*******************************************************************************/ +//extern +UINT16 BTM_GetNumAclLinks (void); + +/******************************************************************************* +** +** Function BTM_SetQoS +** +** Description This function is called to setup QoS +** +** Returns BTM_CMD_STARTED if successfully initiated, otherwise error +** +*******************************************************************************/ +//extern +tBTM_STATUS BTM_SetQoS(BD_ADDR bd, FLOW_SPEC *p_flow, + tBTM_CMPL_CB *p_cb); + + +/***************************************************************************** +** (e)SCO CHANNEL MANAGEMENT FUNCTIONS +*****************************************************************************/ +/******************************************************************************* +** +** Function BTM_CreateSco +** +** Description This function is called to create an SCO connection. If the +** "is_orig" flag is TRUE, the connection will be originated, +** otherwise BTM will wait for the other side to connect. +** +** Returns BTM_UNKNOWN_ADDR if the ACL connection is not up +** BTM_BUSY if another SCO being set up to +** the same BD address +** BTM_NO_RESOURCES if the max SCO limit has been reached +** BTM_CMD_STARTED if the connection establishment is started. +** In this case, "*p_sco_inx" is filled in +** with the sco index used for the connection. +** +*******************************************************************************/ +//extern +tBTM_STATUS BTM_CreateSco (BD_ADDR remote_bda, BOOLEAN is_orig, + UINT16 pkt_types, UINT16 *p_sco_inx, + tBTM_SCO_CB *p_conn_cb, + tBTM_SCO_CB *p_disc_cb); + + +/******************************************************************************* +** +** Function BTM_RemoveSco +** +** Description This function is called to remove a specific SCO connection. +** +** Returns BTM_CMD_STARTED if successfully initiated, otherwise error +** +*******************************************************************************/ +//extern +tBTM_STATUS BTM_RemoveSco (UINT16 sco_inx); + + +/******************************************************************************* +** +** Function BTM_SetScoPacketTypes +** +** Description This function is called to set the packet types used for +** a specific SCO connection, +** +** Parameters pkt_types - One or more of the following +** BTM_SCO_PKT_TYPES_MASK_HV1 +** BTM_SCO_PKT_TYPES_MASK_HV2 +** BTM_SCO_PKT_TYPES_MASK_HV3 +** BTM_SCO_PKT_TYPES_MASK_EV3 +** BTM_SCO_PKT_TYPES_MASK_EV4 +** BTM_SCO_PKT_TYPES_MASK_EV5 +** +** BTM_SCO_LINK_ALL_MASK - enables all supported types +** +** Returns BTM_CMD_STARTED if successfully initiated, otherwise error +** +*******************************************************************************/ +//extern +tBTM_STATUS BTM_SetScoPacketTypes (UINT16 sco_inx, UINT16 pkt_types); + + +/******************************************************************************* +** +** Function BTM_ReadScoPacketTypes +** +** Description This function is read the packet types used for a specific +** SCO connection. +** +** Returns One or more of the following (bitmask) +** BTM_SCO_PKT_TYPES_MASK_HV1 +** BTM_SCO_PKT_TYPES_MASK_HV2 +** BTM_SCO_PKT_TYPES_MASK_HV3 +** BTM_SCO_PKT_TYPES_MASK_EV3 +** BTM_SCO_PKT_TYPES_MASK_EV4 +** BTM_SCO_PKT_TYPES_MASK_EV5 +** +** Returns packet types supported for the connection +** +*******************************************************************************/ +//extern +UINT16 BTM_ReadScoPacketTypes (UINT16 sco_inx); + + +/******************************************************************************* +** +** Function BTM_ReadDeviceScoPacketTypes +** +** Description This function is read the SCO packet types that +** the device supports. +** +** Returns packet types supported by the device. +** +*******************************************************************************/ +//extern +UINT16 BTM_ReadDeviceScoPacketTypes (void); + + +/******************************************************************************* +** +** Function BTM_ReadScoHandle +** +** Description This function is used to read the HCI handle used for a specific +** SCO connection, +** +** Returns handle for the connection, or 0xFFFF if invalid SCO index. +** +*******************************************************************************/ +//extern +UINT16 BTM_ReadScoHandle (UINT16 sco_inx); + + +/******************************************************************************* +** +** Function BTM_ReadScoBdAddr +** +** Description This function is read the remote BD Address for a specific +** SCO connection, +** +** Returns pointer to BD address or NULL if not known +** +*******************************************************************************/ +//extern +UINT8 *BTM_ReadScoBdAddr (UINT16 sco_inx); + + +/******************************************************************************* +** +** Function BTM_ReadScoDiscReason +** +** Description This function is returns the reason why an (e)SCO connection +** has been removed. It contains the value until read, or until +** another (e)SCO connection has disconnected. +** +** Returns HCI reason or BTM_INVALID_SCO_DISC_REASON if not set. +** +*******************************************************************************/ +//extern +UINT16 BTM_ReadScoDiscReason (void); + + +/******************************************************************************* +** +** Function BTM_SetEScoMode +** +** Description This function sets up the negotiated parameters for SCO or +** eSCO, and sets as the default mode used for calls to +** BTM_CreateSco. It can be called only when there are no +** active (e)SCO links. +** +** Returns BTM_SUCCESS if the successful. +** BTM_BUSY if there are one or more active (e)SCO links. +** +*******************************************************************************/ +//extern +tBTM_STATUS BTM_SetEScoMode (tBTM_SCO_TYPE sco_mode, + tBTM_ESCO_PARAMS *p_parms); + +/******************************************************************************* +** +** Function BTM_SetWBSCodec +** +** Description This function sends command to the controller to setup +** WBS codec for the upcoming eSCO connection. +** +** Returns BTM_SUCCESS. +** +** +*******************************************************************************/ +//extern +tBTM_STATUS BTM_SetWBSCodec (tBTM_SCO_CODEC_TYPE codec_type); + +/******************************************************************************* +** +** Function BTM_RegForEScoEvts +** +** Description This function registers a SCO event callback with the +** specified instance. It should be used to received +** connection indication events and change of link parameter +** events. +** +** Returns BTM_SUCCESS if the successful. +** BTM_ILLEGAL_VALUE if there is an illegal sco_inx +** +*******************************************************************************/ +//extern +tBTM_STATUS BTM_RegForEScoEvts (UINT16 sco_inx, + tBTM_ESCO_CBACK *p_esco_cback); + +/******************************************************************************* +** +** Function BTM_ReadEScoLinkParms +** +** Description This function returns the current eSCO link parameters for +** the specified handle. This can be called anytime a connection +** is active, but is typically called after receiving the SCO +** opened callback. +** +** Note: If called over a 1.1 controller, only the packet types +** field has meaning. +** Note: If the upper layer doesn't know the current sco index, +** BTM_FIRST_ACTIVE_SCO_INDEX can be used as the first parameter to +** find the first active SCO index +** +** Returns BTM_SUCCESS if returned data is valid connection. +** BTM_ILLEGAL_VALUE if no connection for specified sco_inx. +** BTM_MODE_UNSUPPORTED if local controller does not support +** 1.2 specification. +** +*******************************************************************************/ +//extern +tBTM_STATUS BTM_ReadEScoLinkParms (UINT16 sco_inx, + tBTM_ESCO_DATA *p_parms); + +/******************************************************************************* +** +** Function BTM_ChangeEScoLinkParms +** +** Description This function requests renegotiation of the parameters on +** the current eSCO Link. If any of the changes are accepted +** by the controllers, the BTM_ESCO_CHG_EVT event is sent in +** the tBTM_ESCO_CBACK function with the current settings of +** the link. The callback is registered through the call to +** BTM_SetEScoMode. +** +** +** Returns BTM_CMD_STARTED if command is successfully initiated. +** BTM_ILLEGAL_VALUE if no connection for specified sco_inx. +** BTM_NO_RESOURCES - not enough resources to initiate command. +** BTM_MODE_UNSUPPORTED if local controller does not support +** 1.2 specification. +** +*******************************************************************************/ +//extern +tBTM_STATUS BTM_ChangeEScoLinkParms (UINT16 sco_inx, + tBTM_CHG_ESCO_PARAMS *p_parms); + +/******************************************************************************* +** +** Function BTM_EScoConnRsp +** +** Description This function is called upon receipt of an (e)SCO connection +** request event (BTM_ESCO_CONN_REQ_EVT) to accept or reject +** the request. Parameters used to negotiate eSCO links. +** If p_parms is NULL, then values set through BTM_SetEScoMode +** are used. +** If the link type of the incoming request is SCO, then only +** the tx_bw, max_latency, content format, and packet_types are +** valid. The hci_status parameter should be +** ([0x0] to accept, [0x0d..0x0f] to reject) +** +** +** Returns void +** +*******************************************************************************/ +//extern +void BTM_EScoConnRsp (UINT16 sco_inx, UINT8 hci_status, + tBTM_ESCO_PARAMS *p_parms); + +/******************************************************************************* +** +** Function BTM_GetNumScoLinks +** +** Description This function returns the number of active SCO links. +** +** Returns UINT8 +** +*******************************************************************************/ +//extern +UINT8 BTM_GetNumScoLinks (void); + +/***************************************************************************** +** SECURITY MANAGEMENT FUNCTIONS +*****************************************************************************/ +/******************************************************************************* +** +** Function BTM_SecRegister +** +** Description Application manager calls this function to register for +** security services. There can be one and only one application +** saving link keys. BTM allows only first registration. +** +** Returns TRUE if registered OK, else FALSE +** +*******************************************************************************/ +//extern +BOOLEAN BTM_SecRegister (tBTM_APPL_INFO *p_cb_info); + +/******************************************************************************* +** +** Function BTM_SecRegisterLinkKeyNotificationCallback +** +** Description Profiles can register to be notified when a new Link Key +** is generated per connection. +** +** Returns TRUE if registered OK, else FALSE +** +*******************************************************************************/ +//extern +BOOLEAN BTM_SecRegisterLinkKeyNotificationCallback( + tBTM_LINK_KEY_CALLBACK *p_callback); + +/******************************************************************************* +** +** Function BTM_SecAddRmtNameNotifyCallback +** +** Description Profiles can register to be notified when name of the +** remote device is resolved (up to BTM_SEC_MAX_RMT_NAME_CALLBACKS). +** +** Returns TRUE if registered OK, else FALSE +** +*******************************************************************************/ +//extern +BOOLEAN BTM_SecAddRmtNameNotifyCallback (tBTM_RMT_NAME_CALLBACK *p_callback); + + +/******************************************************************************* +** +** Function BTM_SecDeleteRmtNameNotifyCallback +** +** Description A profile can deregister notification when a new Link Key +** is generated per connection. +** +** Returns TRUE if OK, else FALSE +** +*******************************************************************************/ +//extern +BOOLEAN BTM_SecDeleteRmtNameNotifyCallback (tBTM_RMT_NAME_CALLBACK *p_callback); + +/******************************************************************************* +** +** Function BTM_GetSecurityFlags +** +** Description Get security flags for the device +** +** Returns BOOLEAN TRUE or FALSE is device found +** +*******************************************************************************/ +//extern +BOOLEAN BTM_GetSecurityFlags (BD_ADDR bd_addr, UINT8 *p_sec_flags); + +/******************************************************************************* +** +** Function BTM_GetSecurityFlagsByTransport +** +** Description Get security flags for the device on a particular transport +** +** Parameters bd_addr: BD address of remote device +** p_sec_flags : Out parameter to be filled with security flags for the connection +** transport : Physical transport of the connection (BR/EDR or LE) +** +** Returns BOOLEAN TRUE or FALSE is device found +** +*******************************************************************************/ +//extern +BOOLEAN BTM_GetSecurityFlagsByTransport (BD_ADDR bd_addr, + UINT8 *p_sec_flags, tBT_TRANSPORT transport); + +/******************************************************************************* +** +** Function BTM_ReadTrustedMask +** +** Description Get trusted mask for the device +** +** Returns NULL, if the device record is not found. +** otherwise, the trusted mask +** +*******************************************************************************/ +//extern +UINT32 *BTM_ReadTrustedMask (BD_ADDR bd_addr); + +/******************************************************************************* +** +** Function BTM_SetPinType +** +** Description Set PIN type for the device. +** +** Returns void +** +*******************************************************************************/ +//extern +void BTM_SetPinType (UINT8 pin_type, PIN_CODE pin_code, UINT8 pin_code_len); + + +/******************************************************************************* +** +** Function BTM_SetPairableMode +** +** Description Enable or disable pairing +** +** Parameters allow_pairing - (TRUE or FALSE) whether or not the device +** allows pairing. +** connect_only_paired - (TRUE or FALSE) whether or not to +** only allow paired devices to connect. +** +** Returns void +** +*******************************************************************************/ +//extern +void BTM_SetPairableMode (BOOLEAN allow_pairing, BOOLEAN connect_only_paired); + +/******************************************************************************* +** +** Function BTM_SetSecureConnectionsOnly +** +** Description Enable or disable default treatment for Mode 4 Level 0 services +** +** Parameter secure_connections_only_mode - (TRUE or FALSE) +** TRUE means that the device should treat Mode 4 Level 0 services as +** services of other levels. +** FALSE means that the device should provide default treatment for +** Mode 4 Level 0 services. +** +** Returns void +** +*******************************************************************************/ +//extern +void BTM_SetSecureConnectionsOnly (BOOLEAN secure_connections_only_mode); + +/******************************************************************************* +** +** Function BTM_SetSecurityLevel +** +** Description Register service security level with Security Manager. Each +** service must register its requirements regardless of the +** security level that is used. This API is called once for originators +** nad again for acceptors of connections. +** +** Returns TRUE if registered OK, else FALSE +** +*******************************************************************************/ +//extern +BOOLEAN BTM_SetSecurityLevel (BOOLEAN is_originator, const char *p_name, + UINT8 service_id, UINT16 sec_level, + UINT16 psm, UINT32 mx_proto_id, + UINT32 mx_chan_id); + +/******************************************************************************* +** +** Function BTM_SetOutService +** +** Description This function is called to set the service for +** outgoing connection. +** +** Returns void +** +*******************************************************************************/ +//extern +void BTM_SetOutService(BD_ADDR bd_addr, UINT8 service_id, UINT32 mx_chan_id); + +/******************************************************************************* +** +** Function BTM_SecClrService +** +** Description Removes specified service record(s) from the security database. +** All service records with the specified name are removed. +** Typically used only by devices with limited RAM so that it can +** reuse an old security service record. +** records (except SDP). +** +** Returns Number of records that were freed. +** +*******************************************************************************/ +//extern +UINT8 BTM_SecClrService (UINT8 service_id); + +/******************************************************************************* +** +** Function BTM_SecAddDevice +** +** Description Add/modify device. This function will be normally called +** during host startup to restore all required information +** stored in the NVRAM. +** dev_class, bd_name, link_key, and features are NULL if unknown +** +** Returns TRUE if added OK, else FALSE +** +*******************************************************************************/ +//extern +BOOLEAN BTM_SecAddDevice (BD_ADDR bd_addr, DEV_CLASS dev_class, + BD_NAME bd_name, UINT8 *features, + UINT32 trusted_mask[], LINK_KEY link_key, + UINT8 key_type, tBTM_IO_CAP io_cap, UINT8 pin_length, + UINT8 sc_support); + + +/******************************************************************************* +** +** Function BTM_SecDeleteDevice +** +** Description Free resources associated with the device. +** +** Returns TRUE if rmoved OK, FALSE if not found +** +*******************************************************************************/ +//extern +BOOLEAN BTM_SecDeleteDevice (BD_ADDR bd_addr, tBT_TRANSPORT transport); + +/******************************************************************************* +** +** Function BTM_SecGetDeviceLinkKey +** +** Description This function is called to obtain link key for the device +** it returns BTM_SUCCESS if link key is available, or +** BTM_UNKNOWN_ADDR if Security Manager does not know about +** the device or device record does not contain link key info +** +** Returns BTM_SUCCESS if successful, otherwise error code +** +*******************************************************************************/ +//extern +tBTM_STATUS BTM_SecGetDeviceLinkKey (BD_ADDR bd_addr, + LINK_KEY link_key); + + +/******************************************************************************* +** +** Function BTM_SecGetDeviceLinkKeyType +** +** Description This function is called to obtain link key type for the +** device. +** it returns BTM_SUCCESS if link key is available, or +** BTM_UNKNOWN_ADDR if Security Manager does not know about +** the device or device record does not contain link key info +** +** Returns BTM_LKEY_TYPE_IGNORE if link key is unknown, link type +** otherwise. +** +*******************************************************************************/ +//extern +tBTM_LINK_KEY_TYPE BTM_SecGetDeviceLinkKeyType (BD_ADDR bd_addr); + + +/******************************************************************************* +** +** Function BTM_PINCodeReply +** +** Description This function is called after Security Manager submitted +** PIN code request to the UI. +** +** Parameters: bd_addr - Address of the device for which PIN was requested +** res - result of the operation BTM_SUCCESS if success +** pin_len - length in bytes of the PIN Code +** p_pin - pointer to array with the PIN Code +** trusted_mask - bitwise OR of trusted services (array of UINT32) +** +** Returns void +** +*******************************************************************************/ +//extern +void BTM_PINCodeReply (BD_ADDR bd_addr, UINT8 res, UINT8 pin_len, + UINT8 *p_pin, UINT32 trusted_mask[]); + + +/******************************************************************************* +** +** Function BTM_SecBond +** +** Description This function is called to perform bonding with peer device. +** +** Parameters: bd_addr - Address of the device to bond +** pin_len - length in bytes of the PIN Code +** p_pin - pointer to array with the PIN Code +** trusted_mask - bitwise OR of trusted services (array of UINT32) + +** Returns BTM_CMD_STARTED if successfully initiated, otherwise error +** +*******************************************************************************/ +//extern +tBTM_STATUS BTM_SecBond (BD_ADDR bd_addr, + UINT8 pin_len, UINT8 *p_pin, + UINT32 trusted_mask[]); + +/******************************************************************************* +** +** Function BTM_SecBondByTransport +** +** Description This function is called to perform bonding by designated transport +** +** Parameters: bd_addr - Address of the device to bond +** pin_len - length in bytes of the PIN Code +** p_pin - pointer to array with the PIN Code +** trusted_mask - bitwise OR of trusted services (array of UINT32) +** transport : Physical transport to use for bonding (BR/EDR or LE) +** +** Returns BTM_CMD_STARTED if successfully initiated, otherwise error +** +*******************************************************************************/ +//extern +tBTM_STATUS BTM_SecBondByTransport (BD_ADDR bd_addr, + tBT_TRANSPORT transport, + UINT8 pin_len, UINT8 *p_pin, + UINT32 trusted_mask[]); + +/******************************************************************************* +** +** Function BTM_SecBondCancel +** +** Description This function is called to cancel ongoing bonding process +** with peer device. +** +** Returns BTM_CMD_STARTED if successfully initiated, otherwise error +** +*******************************************************************************/ +//extern +tBTM_STATUS BTM_SecBondCancel (BD_ADDR bd_addr); + +/******************************************************************************* +** +** Function BTM_SetEncryption +** +** Description This function is called to ensure that connection is +** encrypted. Should be called only on an open connection. +** Typically only needed for connections that first want to +** bring up unencrypted links, then later encrypt them. +** +** Parameters: bd_addr - Address of the peer device +** p_callback - Pointer to callback function called if +** this function returns PENDING after required +** procedures are completed. Can be set to NULL +** if status is not desired. +** p_ref_data - pointer to any data the caller wishes to receive +** in the callback function upon completion. +* can be set to NULL if not used. +** +** Returns BTM_SUCCESS - already encrypted +** BTM_PENDING - command will be returned in the callback +** BTM_WRONG_MODE- connection not up. +** BTM_BUSY - security procedures are currently active +** BTM_MODE_UNSUPPORTED - if security manager not linked in. +** +*******************************************************************************/ +//extern +tBTM_STATUS BTM_SetEncryption (BD_ADDR bd_addr, tBT_TRANSPORT transport, + tBTM_SEC_CBACK *p_callback, void *p_ref_data); + +/******************************************************************************* +** +** Function BTM_ConfirmReqReply +** +** Description This function is called to confirm the numeric value for +** Simple Pairing in response to BTM_SP_CFM_REQ_EVT +** +** Parameters: res - result of the operation BTM_SUCCESS if success +** bd_addr - Address of the peer device +** +*******************************************************************************/ +//extern +void BTM_ConfirmReqReply(tBTM_STATUS res, BD_ADDR bd_addr); + +/******************************************************************************* +** +** Function BTM_PasskeyReqReply +** +** Description This function is called to provide the passkey for +** Simple Pairing in response to BTM_SP_KEY_REQ_EVT +** +** Parameters: res - result of the operation BTM_SUCCESS if success +** bd_addr - Address of the peer device +** passkey - numeric value in the range of 0 - 999999(0xF423F). +** +*******************************************************************************/ +//extern +void BTM_PasskeyReqReply(tBTM_STATUS res, BD_ADDR bd_addr, UINT32 passkey); + +/******************************************************************************* +** +** Function BTM_SendKeypressNotif +** +** Description This function is used during the passkey entry model +** by a device with KeyboardOnly IO capabilities +** (very likely to be a HID Device). +** It is called by a HID Device to inform the remote device when +** a key has been entered or erased. +** +** Parameters: bd_addr - Address of the peer device +** type - notification type +** +*******************************************************************************/ +//extern +void BTM_SendKeypressNotif(BD_ADDR bd_addr, tBTM_SP_KEY_TYPE type); + +/******************************************************************************* +** +** Function BTM_IoCapRsp +** +** Description This function is called in response to BTM_SP_IO_REQ_EVT +** When the event data io_req.oob_data is set to BTM_OOB_UNKNOWN +** by the tBTM_SP_CALLBACK implementation, this function is +** called to provide the actual response +** +** Parameters: bd_addr - Address of the peer device +** io_cap - The IO capability of local device. +** oob - BTM_OOB_NONE or BTM_OOB_PRESENT. +** auth_req- MITM protection required or not. +** +*******************************************************************************/ +//extern +void BTM_IoCapRsp(BD_ADDR bd_addr, tBTM_IO_CAP io_cap, + tBTM_OOB_DATA oob, tBTM_AUTH_REQ auth_req); + +/******************************************************************************* +** +** Function BTM_ReadLocalOobData +** +** Description This function is called to read the local OOB data from +** LM +** +*******************************************************************************/ +//extern +tBTM_STATUS BTM_ReadLocalOobData(void); + +/******************************************************************************* +** +** Function BTM_RemoteOobDataReply +** +** Description This function is called to provide the remote OOB data for +** Simple Pairing in response to BTM_SP_RMT_OOB_EVT +** +** Parameters: bd_addr - Address of the peer device +** c - simple pairing Hash C. +** r - simple pairing Randomizer C. +** +*******************************************************************************/ +//extern +void BTM_RemoteOobDataReply(tBTM_STATUS res, BD_ADDR bd_addr, + BT_OCTET16 c, BT_OCTET16 r); + +/******************************************************************************* +** +** Function BTM_BuildOobData +** +** Description This function is called to build the OOB data payload to +** be sent over OOB (non-Bluetooth) link +** +** Parameters: p_data - the location for OOB data +** max_len - p_data size. +** c - simple pairing Hash C. +** r - simple pairing Randomizer C. +** name_len- 0, local device name would not be included. +** otherwise, the local device name is included for +** up to this specified length +** +** Returns Number of bytes in p_data. +** +*******************************************************************************/ +//extern +UINT16 BTM_BuildOobData(UINT8 *p_data, UINT16 max_len, BT_OCTET16 c, + BT_OCTET16 r, UINT8 name_len); + +/******************************************************************************* +** +** Function BTM_BothEndsSupportSecureConnections +** +** Description This function is called to check if both the local device and the peer device +** specified by bd_addr support BR/EDR Secure Connections. +** +** Parameters: bd_addr - address of the peer +** +** Returns TRUE if BR/EDR Secure Connections are supported by both local +** and the remote device. +** else FALSE. +** +*******************************************************************************/ +//extern +BOOLEAN BTM_BothEndsSupportSecureConnections(BD_ADDR bd_addr); + +/******************************************************************************* +** +** Function BTM_PeerSupportsSecureConnections +** +** Description This function is called to check if the peer supports +** BR/EDR Secure Connections. +** +** Parameters: bd_addr - address of the peer +** +** Returns TRUE if BR/EDR Secure Connections are supported by the peer, +** else FALSE. +** +*******************************************************************************/ +//extern +BOOLEAN BTM_PeerSupportsSecureConnections(BD_ADDR bd_addr); + +/******************************************************************************* +** +** Function BTM_ReadOobData +** +** Description This function is called to parse the OOB data payload +** received over OOB (non-Bluetooth) link +** +** Parameters: p_data - the location for OOB data +** eir_tag - The associated EIR tag to read the data. +** *p_len(output) - the length of the data with the given tag. +** +** Returns the beginning of the data with the given tag. +** NULL, if the tag is not found. +** +*******************************************************************************/ +//extern +UINT8 *BTM_ReadOobData(UINT8 *p_data, UINT8 eir_tag, UINT8 *p_len); + +/******************************************************************************* +** +** Function BTM_SecReadDevName +** +** Description Looks for the device name in the security database for the +** specified BD address. +** +** Returns Pointer to the name or NULL +** +*******************************************************************************/ +//extern +char *BTM_SecReadDevName (BD_ADDR bd_addr); + +/******************************************************************************* +** +** Function BTM_SecClearSecurityFlags +** +** Description Reset the security flags (mark as not-paired) for a given +** remove device. +** +*******************************************************************************/ +extern void BTM_SecClearSecurityFlags (BD_ADDR bd_addr); + + + +/***************************************************************************** +** POWER MANAGEMENT FUNCTIONS +*****************************************************************************/ +/******************************************************************************* +** +** Function BTM_PmRegister +** +** Description register or deregister with power manager +** +** Returns BTM_SUCCESS if successful, +** BTM_NO_RESOURCES if no room to hold registration +** BTM_ILLEGAL_VALUE +** +*******************************************************************************/ +//extern +tBTM_STATUS BTM_PmRegister (UINT8 mask, UINT8 *p_pm_id, + tBTM_PM_STATUS_CBACK *p_cb); + + +/******************************************************************************* +** +** Function BTM_SetPowerMode +** +** Description store the mode in control block or +** alter ACL connection behavior. +** +** Returns BTM_SUCCESS if successful, +** BTM_UNKNOWN_ADDR if bd addr is not active or bad +** +*******************************************************************************/ +//extern +tBTM_STATUS BTM_SetPowerMode (UINT8 pm_id, BD_ADDR remote_bda, + tBTM_PM_PWR_MD *p_mode); + + +/******************************************************************************* +** +** Function BTM_ReadPowerMode +** +** Description This returns the current mode for a specific +** ACL connection. +** +** Input Param remote_bda - device address of desired ACL connection +** +** Output Param p_mode - address where the current mode is copied into. +** BTM_ACL_MODE_NORMAL +** BTM_ACL_MODE_HOLD +** BTM_ACL_MODE_SNIFF +** BTM_ACL_MODE_PARK +** (valid only if return code is BTM_SUCCESS) +** +** Returns BTM_SUCCESS if successful, +** BTM_UNKNOWN_ADDR if bd addr is not active or bad +** +*******************************************************************************/ +//extern +tBTM_STATUS BTM_ReadPowerMode (BD_ADDR remote_bda, + tBTM_PM_MODE *p_mode); + +/******************************************************************************* +** +** Function BTM_SetSsrParams +** +** Description This sends the given SSR parameters for the given ACL +** connection if it is in ACTIVE mode. +** +** Input Param remote_bda - device address of desired ACL connection +** max_lat - maximum latency (in 0.625ms)(0-0xFFFE) +** min_rmt_to - minimum remote timeout +** min_loc_to - minimum local timeout +** +** +** Returns BTM_SUCCESS if the HCI command is issued successful, +** BTM_UNKNOWN_ADDR if bd addr is not active or bad +** BTM_CMD_STORED if the command is stored +** +*******************************************************************************/ +//extern +tBTM_STATUS BTM_SetSsrParams (BD_ADDR remote_bda, UINT16 max_lat, + UINT16 min_rmt_to, UINT16 min_loc_to); + +/******************************************************************************* +** +** Function BTM_GetHCIConnHandle +** +** Description This function is called to get the handle for an ACL connection +** to a specific remote BD Address. +** +** Returns the handle of the connection, or 0xFFFF if none. +** +*******************************************************************************/ +//extern +UINT16 BTM_GetHCIConnHandle (BD_ADDR remote_bda, tBT_TRANSPORT transport); + +/******************************************************************************* +** +** Function BTM_DeleteStoredLinkKey +** +** Description This function is called to delete link key for the specified +** device addresses from the NVRAM storage attached to the Bluetooth +** controller. +** +** Parameters: bd_addr - Addresses of the devices +** p_cb - Call back function to be called to return +** the results +** +*******************************************************************************/ +//extern +tBTM_STATUS BTM_DeleteStoredLinkKey(BD_ADDR bd_addr, tBTM_CMPL_CB *p_cb); + +/******************************************************************************* +** +** Function BTM_WriteEIR +** +** Description This function is called to write EIR data to controller. +** +** Parameters p_buff - allocated HCI command buffer including extended +** inquriry response +** fec_required - FEC is required or not +** +** Returns BTM_SUCCESS - if successful +** BTM_MODE_UNSUPPORTED - if local device cannot support it +** +*******************************************************************************/ +//extern +tBTM_STATUS BTM_WriteEIR( BT_HDR *p_buff, BOOLEAN fec_required); + +/******************************************************************************* +** +** Function BTM_CheckEirData +** +** Description This function is called to get EIR data from significant part. +** +** Parameters p_eir - pointer of EIR significant part +** type - finding EIR data type +** p_length - return the length of EIR data +** +** Returns pointer of EIR data +** +*******************************************************************************/ +//extern +UINT8 *BTM_CheckEirData( UINT8 *p_eir, UINT8 type, UINT8 *p_length ); + +/******************************************************************************* +** +** Function BTM_HasEirService +** +** Description This function is called to know if UUID in bit map of UUID. +** +** Parameters p_eir_uuid - bit map of UUID list +** uuid16 - UUID 16-bit +** +** Returns TRUE - if found +** FALSE - if not found +** +*******************************************************************************/ +//extern +BOOLEAN BTM_HasEirService( UINT32 *p_eir_uuid, UINT16 uuid16 ); + +/******************************************************************************* +** +** Function BTM_HasInquiryEirService +** +** Description This function is called to know if UUID in bit map of UUID list. +** +** Parameters p_results - inquiry results +** uuid16 - UUID 16-bit +** +** Returns BTM_EIR_FOUND - if found +** BTM_EIR_NOT_FOUND - if not found and it is complete list +** BTM_EIR_UNKNOWN - if not found and it is not complete list +** +*******************************************************************************/ +//extern +tBTM_EIR_SEARCH_RESULT BTM_HasInquiryEirService( tBTM_INQ_RESULTS *p_results, + UINT16 uuid16 ); + +/******************************************************************************* +** +** Function BTM_HasCustomEirService +** +** Description This function is called to know if UUID is already in custom +** UUID list. +** +** Parameters custom_uuid - pointer to custom_uuid array in tBTA_DM_CB +** uuid - UUID struct +** +** Returns TRUE - if found +** FALSE - if not found +** +*******************************************************************************/ +BOOLEAN BTM_HasCustomEirService( tBT_UUID *custom_uuid, tBT_UUID uuid ); + +/******************************************************************************* +** +** Function BTM_AddEirService +** +** Description This function is called to add a service in bit map of UUID list. +** +** Parameters p_eir_uuid - bit mask of UUID list for EIR +** uuid16 - UUID 16-bit +** +** Returns None +** +*******************************************************************************/ +//extern +void BTM_AddEirService( UINT32 *p_eir_uuid, UINT16 uuid16 ); + +/******************************************************************************* +** +** Function BTM_AddCustomEirService +** +** Description This function is called to add a custom UUID. +** +** Parameters custom_uuid - pointer to custom_uuid array in tBTA_DM_CB +** uuid - UUID struct +** +** Returns None +** +*******************************************************************************/ +void BTM_AddCustomEirService(tBT_UUID *custom_uuid, tBT_UUID uuid); + +/******************************************************************************* +** +** Function BTM_RemoveEirService +** +** Description This function is called to remove a service in bit map of UUID list. +** +** Parameters p_eir_uuid - bit mask of UUID list for EIR +** uuid16 - UUID 16-bit +** +** Returns None +** +*******************************************************************************/ +//extern +void BTM_RemoveEirService( UINT32 *p_eir_uuid, UINT16 uuid16 ); + +/******************************************************************************* +** +** Function BTM_RemoveCustomEirService +** +** Description This function is called to remove a a custom UUID. +** +** Parameters custom_uuid - pointer to custom_uuid array in tBTA_DM_CB + uuid - UUID struct +** +** Returns None +** +*******************************************************************************/ +void BTM_RemoveCustomEirService(tBT_UUID *custom_uuid, tBT_UUID uuid); + +/******************************************************************************* +** +** Function BTM_GetEirSupportedServices +** +** Description This function is called to get UUID list from bit map of UUID list. +** +** Parameters p_eir_uuid - bit mask of UUID list for EIR +** p - reference of current pointer of EIR +** max_num_uuid16 - max number of UUID can be written in EIR +** num_uuid16 - number of UUID have been written in EIR +** +** Returns BTM_EIR_MORE_16BITS_UUID_TYPE, if it has more than max +** BTM_EIR_COMPLETE_16BITS_UUID_TYPE, otherwise +** +*******************************************************************************/ +//extern +UINT8 BTM_GetEirSupportedServices( UINT32 *p_eir_uuid, UINT8 **p, + UINT8 max_num_uuid16, UINT8 *p_num_uuid16); + +/******************************************************************************* +** +** Function BTM_GetEirUuidList +** +** Description This function parses EIR and returns UUID list. +** +** Parameters p_eir - EIR +** uuid_size - LEN_UUID_16, LEN_UUID_32, LEN_UUID_128 +** p_num_uuid - return number of UUID in found list +** p_uuid_list - return UUID 16-bit list +** max_num_uuid - maximum number of UUID to be returned +** +** Returns 0 - if not found +** BTM_EIR_COMPLETE_16BITS_UUID_TYPE +** BTM_EIR_MORE_16BITS_UUID_TYPE +** BTM_EIR_COMPLETE_32BITS_UUID_TYPE +** BTM_EIR_MORE_32BITS_UUID_TYPE +** BTM_EIR_COMPLETE_128BITS_UUID_TYPE +** BTM_EIR_MORE_128BITS_UUID_TYPE +** +*******************************************************************************/ +//extern +UINT8 BTM_GetEirUuidList( UINT8 *p_eir, UINT8 uuid_size, UINT8 *p_num_uuid, + UINT8 *p_uuid_list, UINT8 max_num_uuid); + +/***************************************************************************** +** SCO OVER HCI +*****************************************************************************/ +/******************************************************************************* +** +** Function BTM_ConfigScoPath +** +** Description This function enable/disable SCO over HCI and registers SCO +** data callback if SCO over HCI is enabled. +** +** Parameter path: SCO or HCI +** p_sco_data_cb: callback function or SCO data if path is set +** to transport. +** p_pcm_param: pointer to the PCM interface parameter. If a NULL +** pointer is used, PCM parameter maintained in +** the control block will be used; otherwise update +** control block value. +** err_data_rpt: Lisbon feature to enable the erronous data report +** or not. +** +** Returns BTM_SUCCESS if the successful. +** BTM_NO_RESOURCES: no rsource to start the command. +** BTM_ILLEGAL_VALUE: invalid callback function pointer. +** BTM_CMD_STARTED :Command sent. Waiting for command cmpl event. +** +** +*******************************************************************************/ +//extern +tBTM_STATUS BTM_ConfigScoPath (tBTM_SCO_ROUTE_TYPE path, + tBTM_SCO_DATA_CB *p_sco_data_cb, + tBTM_SCO_PCM_PARAM *p_pcm_param, + BOOLEAN err_data_rpt); + +/******************************************************************************* +** +** Function BTM_WriteScoData +** +** Description This function write SCO data to a specified instance. The data +** to be written p_buf needs to carry an offset of +** HCI_SCO_PREAMBLE_SIZE bytes, and the data length can not +** exceed BTM_SCO_DATA_SIZE_MAX bytes, whose default value is set +** to 60 and is configurable. Data longer than the maximum bytes +** will be truncated. +** +** Returns BTM_SUCCESS: data write is successful +** BTM_ILLEGAL_VALUE: SCO data contains illegal offset value. +** BTM_SCO_BAD_LENGTH: SCO data length exceeds the max SCO packet +** size. +** BTM_NO_RESOURCES: no resources. +** BTM_UNKNOWN_ADDR: unknown SCO connection handle, or SCO is not +** routed via HCI. +** +** +*******************************************************************************/ +//extern +tBTM_STATUS BTM_WriteScoData (UINT16 sco_inx, BT_HDR *p_buf); + +/******************************************************************************* +** +** Function BTM_SetARCMode +** +** Description Send Audio Routing Control command. +** +** Returns void +** +*******************************************************************************/ +//extern +void BTM_SetARCMode (UINT8 iface, UINT8 arc_mode, tBTM_VSC_CMPL_CB *p_arc_cb); + + +/******************************************************************************* +** +** Function BTM_PCM2Setup_Write +** +** Description Send PCM2_Setup write command. +** +** Returns void +** +*******************************************************************************/ +//extern +void BTM_PCM2Setup_Write (BOOLEAN clk_master, tBTM_VSC_CMPL_CB *p_arc_cb); + + +/******************************************************************************* +** +** Function BTM_PM_ReadControllerState +** +** Description This function is called to obtain the controller state +** +** Returns Controller state (BTM_CONTRL_ACTIVE, BTM_CONTRL_SCAN, and BTM_CONTRL_IDLE) +** +*******************************************************************************/ +//extern +tBTM_CONTRL_STATE BTM_PM_ReadControllerState(void); + +/******************************************************************************* +** +** Function BTM_SetAfhChannels +** +** Description This function is called to set AFH channels +** +** Returns status of the operation +** +*******************************************************************************/ +tBTM_STATUS BTM_SetAfhChannels (AFH_CHANNELS channels, tBTM_CMPL_CB *p_afh_channels_cmpl_cback); + +/******************************************************************************* +** +** Function BTM_BleSetChannels +** +** Description This function is called to set BLE channels +** +** Returns status of the operation +** +*******************************************************************************/ +tBTM_STATUS BTM_BleSetChannels (BLE_CHANNELS channels, tBTM_CMPL_CB *p_ble_channels_cmpl_cback); + +/******************************************************************************* +** +** Function BTM_PktStatNumsGet +** +** Description This function is called to get the number of packet status struct +** +** Returns void +** +*******************************************************************************/ +void BTM_PktStatNumsGet(UINT16 sync_conn_handle, tBTM_SCO_PKT_STAT_NUMS *pkt_nums); + +#ifdef __cplusplus +} +#endif + +#endif /* BTM_API_H */ diff --git a/lib/bt/host/bluedroid/stack/include/stack/btm_ble_api.h b/lib/bt/host/bluedroid/stack/include/stack/btm_ble_api.h new file mode 100644 index 00000000..a3085361 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/include/stack/btm_ble_api.h @@ -0,0 +1,2724 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains the Bluetooth Manager (BTM) API function external + * definitions. + * + ******************************************************************************/ +#ifndef BTM_BLE_API_H +#define BTM_BLE_API_H + +#include "common/bt_defs.h" +#include "stack/btm_api.h" +#include "common/bt_common_types.h" + +#define CHANNEL_MAP_LEN 5 +typedef UINT8 tBTM_BLE_CHNL_MAP[CHANNEL_MAP_LEN]; + +/* 0x00-0x04 only used for set advertising parameter command */ +#define BTM_BLE_CONNECT_EVT 0x00 /* 0x00-0x04 only used for set advertising + parameter command */ +#define BTM_BLE_CONNECT_DIR_EVT 0x01 /* Connectable directed advertising */ +#define BTM_BLE_DISCOVER_EVT 0x02 /* Scannable undirected advertising */ +#define BTM_BLE_NON_CONNECT_EVT 0x03 /* Non connectable undirected advertising */ +#define BTM_BLE_CONNECT_LO_DUTY_DIR_EVT 0x04 /* Connectable low duty + cycle directed advertising */ +/* 0x00 - 0x05 can be received on adv event type */ +#define BTM_BLE_SCAN_RSP_EVT 0x04 +#define BTM_BLE_SCAN_REQ_EVT 0x05 +#define BTM_BLE_UNKNOWN_EVT 0xff + +#define BTM_BLE_UNKNOWN_EVT 0xff + +typedef UINT8 tBTM_BLE_EVT; +typedef UINT8 tBTM_BLE_CONN_MODE; + +typedef UINT32 tBTM_BLE_REF_VALUE; + +#define BTM_BLE_SCAN_MODE_PASS 0 +#define BTM_BLE_SCAN_MODE_ACTI 1 +#define BTM_BLE_SCAN_MODE_NONE 0xff +typedef UINT8 tBLE_SCAN_MODE; + +#define BTM_BLE_BATCH_SCAN_MODE_DISABLE 0 +#define BTM_BLE_BATCH_SCAN_MODE_PASS 1 +#define BTM_BLE_BATCH_SCAN_MODE_ACTI 2 +#define BTM_BLE_BATCH_SCAN_MODE_PASS_ACTI 3 + +typedef UINT8 tBTM_BLE_BATCH_SCAN_MODE; + +/* advertising channel map */ +#define BTM_BLE_ADV_CHNL_37 (0x01 << 0) +#define BTM_BLE_ADV_CHNL_38 (0x01 << 1) +#define BTM_BLE_ADV_CHNL_39 (0x01 << 2) +typedef UINT8 tBTM_BLE_ADV_CHNL_MAP; + +/*d efault advertising channel map */ +#ifndef BTM_BLE_DEFAULT_ADV_CHNL_MAP +#define BTM_BLE_DEFAULT_ADV_CHNL_MAP (BTM_BLE_ADV_CHNL_37| BTM_BLE_ADV_CHNL_38| BTM_BLE_ADV_CHNL_39) +#endif + +/* advertising filter policy */ +#define AP_SCAN_CONN_ALL 0x00 /* default */ +#define AP_SCAN_WL_CONN_ALL 0x01 +#define AP_SCAN_ALL_CONN_WL 0x02 +#define AP_SCAN_CONN_WL 0x03 +#define AP_SCAN_CONN_POLICY_MAX 0x04 +typedef UINT8 tBTM_BLE_AFP; + +/* default advertising filter policy */ +#ifndef BTM_BLE_DEFAULT_AFP +#define BTM_BLE_DEFAULT_AFP AP_SCAN_CONN_ALL +#endif + +/* scanning filter policy */ +#define SP_ADV_ALL 0x00 /* 0: accept adv packet from all, directed adv pkt not directed */ +/* to local device is ignored */ +#define SP_ADV_WL 0x01 /* 1: accept adv packet from device in white list, directed adv */ +/* packet not directed to local device is ignored */ +#define SP_ADV_ALL_RPA_DIR_ADV 0x02 /* 2: accept adv packet from all, directed adv pkt */ +/* not directed to me is ignored except direct adv with RPA */ +#define SP_ADV_WL_RPA_DIR_ADV 0x03 /* 3: accept adv packet from device in white list, directed */ +/* adv pkt not directed to me is ignored except direct adv */ +/* with RPA */ +typedef UINT8 tBTM_BLE_SFP; + +#ifndef BTM_BLE_DEFAULT_SFP +#define BTM_BLE_DEFAULT_SFP SP_ADV_ALL +#endif + +/* adv parameter boundary values */ +#if BLE_HIGH_DUTY_ADV_INTERVAL +#define BTM_BLE_ADV_INT_MIN 0x0008 /* 5ms */ +#else +#define BTM_BLE_ADV_INT_MIN 0x0020 /* 20ms */ +#endif +#define BTM_BLE_ADV_INT_MAX 0x4000 /* 10240ms */ + +/* Full scan boundary values */ +#define BTM_BLE_ADV_SCAN_FULL_MIN 0x00 +#define BTM_BLE_ADV_SCAN_FULL_MAX 0x64 + +/* Partial scan boundary values */ +#define BTM_BLE_ADV_SCAN_TRUNC_MIN BTM_BLE_ADV_SCAN_FULL_MIN +#define BTM_BLE_ADV_SCAN_TRUNC_MAX BTM_BLE_ADV_SCAN_FULL_MAX + +/* Threshold values */ +#define BTM_BLE_ADV_SCAN_THR_MIN BTM_BLE_ADV_SCAN_FULL_MIN +#define BTM_BLE_ADV_SCAN_THR_MAX BTM_BLE_ADV_SCAN_FULL_MAX + +/* connection parameter boundary values */ +#define BTM_BLE_SCAN_INT_MIN 0x0004 +#define BTM_BLE_SCAN_INT_MAX 0x4000 +#define BTM_BLE_SCAN_WIN_MIN 0x0004 +#define BTM_BLE_SCAN_WIN_MAX 0x4000 +#define BTM_BLE_EXT_SCAN_INT_MAX 0x00FFFFFF +#define BTM_BLE_EXT_SCAN_WIN_MAX 0xFFFF +#define BTM_BLE_CONN_INT_MIN 0x0006 +#define BTM_BLE_CONN_INT_MAX 0x0C80 +#define BTM_BLE_CONN_LATENCY_MAX 499 +#define BTM_BLE_CONN_SUP_TOUT_MIN 0x000A +#define BTM_BLE_CONN_SUP_TOUT_MAX 0x0C80 +#define BTM_BLE_CONN_PARAM_UNDEF 0xffff /* use this value when a specific value not to be overwritten */ +#define BTM_BLE_SCAN_PARAM_UNDEF 0xffffffff + +/* default connection parameters if not configured, use GAP recommend value for auto/selective connection */ +/* default scan interval */ +#ifndef BTM_BLE_SCAN_FAST_INT +#define BTM_BLE_SCAN_FAST_INT 96 /* 30 ~ 60 ms (use 60) = 96 *0.625 */ +#endif +/* default scan window for background connection, applicable for auto connection or selective conenction */ +#ifndef BTM_BLE_SCAN_FAST_WIN +#define BTM_BLE_SCAN_FAST_WIN 48 /* 30 ms = 48 *0.625 */ +#endif + +/* default scan paramter used in reduced power cycle (background scanning) */ +#ifndef BTM_BLE_SCAN_SLOW_INT_1 +#define BTM_BLE_SCAN_SLOW_INT_1 2048 /* 1.28 s = 2048 *0.625 */ +#endif +#ifndef BTM_BLE_SCAN_SLOW_WIN_1 +#define BTM_BLE_SCAN_SLOW_WIN_1 48 /* 30 ms = 48 *0.625 */ +#endif + +/* default scan paramter used in reduced power cycle (background scanning) */ +#ifndef BTM_BLE_SCAN_SLOW_INT_2 +#define BTM_BLE_SCAN_SLOW_INT_2 4096 /* 2.56 s = 4096 *0.625 */ +#endif +#ifndef BTM_BLE_SCAN_SLOW_WIN_2 +#define BTM_BLE_SCAN_SLOW_WIN_2 36 /* 22.5 ms = 36 *0.625 */ +#endif + +/* default connection interval min */ +#ifndef BTM_BLE_CONN_INT_MIN_DEF +#define BTM_BLE_CONN_INT_MIN_DEF 10 /* recommended min: 12.5 ms = 10 * 1.25 */ +#endif + +/* default connection interval max */ +#ifndef BTM_BLE_CONN_INT_MAX_DEF +#if CONFIG_IDF_TARGET_ESP32 +#define BTM_BLE_CONN_INT_MAX_DEF 12 /* recommended max: 15 ms = 12 * 1.25 */ +#else +#define BTM_BLE_CONN_INT_MAX_DEF (((MAX_ACL_CONNECTIONS + 1) * 4) > 12 ? ((MAX_ACL_CONNECTIONS + 1) * 4) : 12) /* recommended max: BTM_BLE_CONN_INT_MAX_DEF * 1.25 ms*/ +#endif +#endif + +/* default slave latency */ +#ifndef BTM_BLE_CONN_SLAVE_LATENCY_DEF +#define BTM_BLE_CONN_SLAVE_LATENCY_DEF 0 /* 0 */ +#endif + +/* default supervision timeout */ +#ifndef BTM_BLE_CONN_TIMEOUT_DEF +#define BTM_BLE_CONN_TIMEOUT_DEF 600 +#endif + +/* minimum acceptable connection interval */ +#ifndef BTM_BLE_CONN_INT_MIN_LIMIT +#define BTM_BLE_CONN_INT_MIN_LIMIT 0x0009 +#endif + +#define BTM_BLE_DIR_CONN_FALLBACK_UNDIR 1 +#define BTM_BLE_DIR_CONN_FALLBACK_NO_ADV 2 + +#ifndef BTM_BLE_DIR_CONN_FALLBACK +#define BTM_BLE_DIR_CONN_FALLBACK BTM_BLE_DIR_CONN_FALLBACK_UNDIR +#endif + +#define BTM_CMAC_TLEN_SIZE 8 /* 64 bits */ +#define BTM_BLE_AUTH_SIGN_LEN 12 /* BLE data signature length 8 Bytes + 4 bytes counter*/ +typedef UINT8 BLE_SIGNATURE[BTM_BLE_AUTH_SIGN_LEN]; /* Device address */ + +#ifndef BTM_BLE_HOST_SUPPORT +#define BTM_BLE_HOST_SUPPORT 0x01 +#endif + +#ifndef BTM_BLE_SIMULTANEOUS_HOST +#define BTM_BLE_SIMULTANEOUS_HOST 0x01 +#endif + +/* Appearance Values Reported with BTM_BLE_AD_TYPE_APPEARANCE */ +#define BTM_BLE_APPEARANCE_UNKNOWN 0x0000 +#define BTM_BLE_APPEARANCE_GENERIC_PHONE 0x0040 +#define BTM_BLE_APPEARANCE_GENERIC_COMPUTER 0x0080 +#define BTM_BLE_APPEARANCE_GENERIC_WATCH 0x00C0 +#define BTM_BLE_APPEARANCE_SPORTS_WATCH 0x00C1 +#define BTM_BLE_APPEARANCE_GENERIC_CLOCK 0x0100 +#define BTM_BLE_APPEARANCE_GENERIC_DISPLAY 0x0140 +#define BTM_BLE_APPEARANCE_GENERIC_REMOTE 0x0180 +#define BTM_BLE_APPEARANCE_GENERIC_EYEGLASSES 0x01C0 +#define BTM_BLE_APPEARANCE_GENERIC_TAG 0x0200 +#define BTM_BLE_APPEARANCE_GENERIC_KEYRING 0x0240 +#define BTM_BLE_APPEARANCE_GENERIC_MEDIA_PLAYER 0x0280 +#define BTM_BLE_APPEARANCE_GENERIC_BARCODE_SCANNER 0x02C0 +#define BTM_BLE_APPEARANCE_GENERIC_THERMOMETER 0x0300 +#define BTM_BLE_APPEARANCE_THERMOMETER_EAR 0x0301 +#define BTM_BLE_APPEARANCE_GENERIC_HEART_RATE 0x0340 +#define BTM_BLE_APPEARANCE_HEART_RATE_BELT 0x0341 +#define BTM_BLE_APPEARANCE_GENERIC_BLOOD_PRESSURE 0x0380 +#define BTM_BLE_APPEARANCE_BLOOD_PRESSURE_ARM 0x0381 +#define BTM_BLE_APPEARANCE_BLOOD_PRESSURE_WRIST 0x0382 +#define BTM_BLE_APPEARANCE_GENERIC_HID 0x03C0 +#define BTM_BLE_APPEARANCE_HID_KEYBOARD 0x03C1 +#define BTM_BLE_APPEARANCE_HID_MOUSE 0x03C2 +#define BTM_BLE_APPEARANCE_HID_JOYSTICK 0x03C3 +#define BTM_BLE_APPEARANCE_HID_GAMEPAD 0x03C4 +#define BTM_BLE_APPEARANCE_HID_DIGITIZER_TABLET 0x03C5 +#define BTM_BLE_APPEARANCE_HID_CARD_READER 0x03C6 +#define BTM_BLE_APPEARANCE_HID_DIGITAL_PEN 0x03C7 +#define BTM_BLE_APPEARANCE_HID_BARCODE_SCANNER 0x03C8 +#define BTM_BLE_APPEARANCE_GENERIC_GLUCOSE 0x0400 +#define BTM_BLE_APPEARANCE_GENERIC_WALKING 0x0440 +#define BTM_BLE_APPEARANCE_WALKING_IN_SHOE 0x0441 +#define BTM_BLE_APPEARANCE_WALKING_ON_SHOE 0x0442 +#define BTM_BLE_APPEARANCE_WALKING_ON_HIP 0x0443 +#define BTM_BLE_APPEARANCE_GENERIC_CYCLING 0x0480 +#define BTM_BLE_APPEARANCE_CYCLING_COMPUTER 0x0481 +#define BTM_BLE_APPEARANCE_CYCLING_SPEED 0x0482 +#define BTM_BLE_APPEARANCE_CYCLING_CADENCE 0x0483 +#define BTM_BLE_APPEARANCE_CYCLING_POWER 0x0484 +#define BTM_BLE_APPEARANCE_CYCLING_SPEED_CADENCE 0x0485 +#define BTM_BLE_APPEARANCE_GENERIC_PULSE_OXIMETER 0x0C40 +#define BTM_BLE_APPEARANCE_PULSE_OXIMETER_FINGERTIP 0x0C41 +#define BTM_BLE_APPEARANCE_PULSE_OXIMETER_WRIST 0x0C42 +#define BTM_BLE_APPEARANCE_GENERIC_WEIGHT 0x0C80 +#define BTM_BLE_APPEARANCE_GENERIC_PERSONAL_MOBILITY_DEVICE 0x0CC0 +#define BTM_BLE_APPEARANCE_POWERED_WHEELCHAIR 0x0CC1 +#define BTM_BLE_APPEARANCE_MOBILITY_SCOOTER 0x0CC2 +#define BTM_BLE_APPEARANCE_GENERIC_CONTINUOUS_GLUCOSE_MONITOR 0x0D00 +#define BTM_BLE_APPEARANCE_GENERIC_INSULIN_PUMP 0x0D40 +#define BTM_BLE_APPEARANCE_INSULIN_PUMP_DURABLE_PUMP 0x0D41 +#define BTM_BLE_APPEARANCE_INSULIN_PUMP_PATCH_PUMP 0x0D44 +#define BTM_BLE_APPEARANCE_INSULIN_PEN 0x0D48 +#define BTM_BLE_APPEARANCE_GENERIC_MEDICATION_DELIVERY 0x0D80 +#define BTM_BLE_APPEARANCE_GENERIC_OUTDOOR_SPORTS 0x1440 +#define BTM_BLE_APPEARANCE_OUTDOOR_SPORTS_LOCATION 0x1441 +#define BTM_BLE_APPEARANCE_OUTDOOR_SPORTS_LOCATION_AND_NAV 0x1442 +#define BTM_BLE_APPEARANCE_OUTDOOR_SPORTS_LOCATION_POD 0x1443 +#define BTM_BLE_APPEARANCE_OUTDOOR_SPORTS_LOCATION_POD_AND_NAV 0x1444 + + +/* Structure returned with Rand/Encrypt complete callback */ +typedef struct { + UINT8 status; + UINT8 param_len; + UINT16 opcode; + UINT8 param_buf[BT_OCTET16_LEN]; +} tBTM_RAND_ENC; + +/* General callback function for notifying an application that a synchronous +** BTM function is complete. The pointer contains the address of any returned data. +*/ +typedef void (tBTM_RAND_ENC_CB) (tBTM_RAND_ENC *p1); + +#define BTM_BLE_FILTER_TARGET_SCANNER 0x01 +#define BTM_BLE_FILTER_TARGET_ADVR 0x00 + +#define BTM_BLE_POLICY_BLACK_ALL 0x00 /* relevant to both */ +#define BTM_BLE_POLICY_ALLOW_SCAN 0x01 /* relevant to advertiser */ +#define BTM_BLE_POLICY_ALLOW_CONN 0x02 /* relevant to advertiser */ +#define BTM_BLE_POLICY_WHITE_ALL 0x03 /* relevant to both */ + +/* ADV data flag bit definition used for BTM_BLE_AD_TYPE_FLAG */ +#define BTM_BLE_LIMIT_DISC_FLAG (0x01 << 0) +#define BTM_BLE_GEN_DISC_FLAG (0x01 << 1) +#define BTM_BLE_BREDR_NOT_SPT (0x01 << 2) +/* 4.1 spec adv flag for simultaneous BR/EDR+LE connection support */ +#define BTM_BLE_DMT_CONTROLLER_SPT (0x01 << 3) +#define BTM_BLE_DMT_HOST_SPT (0x01 << 4) +#define BTM_BLE_NON_LIMIT_DISC_FLAG (0x00 ) /* lowest bit unset */ +#define BTM_BLE_ADV_FLAG_MASK (BTM_BLE_LIMIT_DISC_FLAG | BTM_BLE_BREDR_NOT_SPT | BTM_BLE_GEN_DISC_FLAG) +#define BTM_BLE_LIMIT_DISC_MASK (BTM_BLE_LIMIT_DISC_FLAG ) + +#define BTM_BLE_AD_BIT_DEV_NAME (0x00000001 << 0) +#define BTM_BLE_AD_BIT_FLAGS (0x00000001 << 1) +#define BTM_BLE_AD_BIT_MANU (0x00000001 << 2) +#define BTM_BLE_AD_BIT_TX_PWR (0x00000001 << 3) +#define BTM_BLE_AD_BIT_INT_RANGE (0x00000001 << 5) +#define BTM_BLE_AD_BIT_SERVICE (0x00000001 << 6) +#define BTM_BLE_AD_BIT_SERVICE_SOL (0x00000001 << 7) +#define BTM_BLE_AD_BIT_SERVICE_DATA (0x00000001 << 8) +#define BTM_BLE_AD_BIT_SIGN_DATA (0x00000001 << 9) +#define BTM_BLE_AD_BIT_SERVICE_128SOL (0x00000001 << 10) +#define BTM_BLE_AD_BIT_APPEARANCE (0x00000001 << 11) +#define BTM_BLE_AD_BIT_PUBLIC_ADDR (0x00000001 << 12) +#define BTM_BLE_AD_BIT_RANDOM_ADDR (0x00000001 << 13) +#define BTM_BLE_AD_BIT_SERVICE_32 (0x00000001 << 4) +#define BTM_BLE_AD_BIT_SERVICE_32SOL (0x00000001 << 14) +#define BTM_BLE_AD_BIT_PROPRIETARY (0x00000001 << 15) +#define BTM_BLE_AD_BIT_SERVICE_128 (0x00000001 << 16) /*128-bit Service UUIDs*/ + +typedef UINT32 tBTM_BLE_AD_MASK; + +/* relate to ESP_BLE_AD_TYPE_xxx in esp_gap_ble_api.h */ +#define BTM_BLE_AD_TYPE_FLAG HCI_EIR_FLAGS_TYPE /* 0x01 */ +#define BTM_BLE_AD_TYPE_16SRV_PART HCI_EIR_MORE_16BITS_UUID_TYPE /* 0x02 */ +#define BTM_BLE_AD_TYPE_16SRV_CMPL HCI_EIR_COMPLETE_16BITS_UUID_TYPE /* 0x03 */ +#define BTM_BLE_AD_TYPE_32SRV_PART HCI_EIR_MORE_32BITS_UUID_TYPE /* 0x04 */ +#define BTM_BLE_AD_TYPE_32SRV_CMPL HCI_EIR_COMPLETE_32BITS_UUID_TYPE /* 0x05 */ +#define BTM_BLE_AD_TYPE_128SRV_PART HCI_EIR_MORE_128BITS_UUID_TYPE /* 0x06 */ +#define BTM_BLE_AD_TYPE_128SRV_CMPL HCI_EIR_COMPLETE_128BITS_UUID_TYPE /* 0x07 */ +#define BTM_BLE_AD_TYPE_NAME_SHORT HCI_EIR_SHORTENED_LOCAL_NAME_TYPE /* 0x08 */ +#define BTM_BLE_AD_TYPE_NAME_CMPL HCI_EIR_COMPLETE_LOCAL_NAME_TYPE /* 0x09 */ +#define BTM_BLE_AD_TYPE_TX_PWR HCI_EIR_TX_POWER_LEVEL_TYPE /* 0x0A */ +#define BTM_BLE_AD_TYPE_DEV_CLASS 0x0D +#define BTM_BLE_AD_TYPE_SM_TK 0x10 +#define BTM_BLE_AD_TYPE_SM_OOB_FLAG 0x11 +#define BTM_BLE_AD_TYPE_INT_RANGE 0x12 +#define BTM_BLE_AD_TYPE_SOL_SRV_UUID 0x14 +#define BTM_BLE_AD_TYPE_128SOL_SRV_UUID 0x15 +#define BTM_BLE_AD_TYPE_SERVICE_DATA 0x16 +#define BTM_BLE_AD_TYPE_PUBLIC_TARGET 0x17 +#define BTM_BLE_AD_TYPE_RANDOM_TARGET 0x18 +#define BTM_BLE_AD_TYPE_APPEARANCE 0x19 +#define BTM_BLE_AD_TYPE_ADV_INT 0x1a +#define BTM_BLE_AD_TYPE_LE_DEV_ADDR 0x1b +#define BTM_BLE_AD_TYPE_LE_ROLE 0x1c +#define BTM_BLE_AD_TYPE_SPAIR_C256 0x1d +#define BTM_BLE_AD_TYPE_SPAIR_R256 0x1e +#define BTM_BLE_AD_TYPE_32SOL_SRV_UUID 0x1f +#define BTM_BLE_AD_TYPE_32SERVICE_DATA 0x20 +#define BTM_BLE_AD_TYPE_128SERVICE_DATA 0x21 +#define BTM_BLE_AD_TYPE_LE_SECURE_CONFIRM 0x22 +#define BTM_BLE_AD_TYPE_LE_SECURE_RANDOM 0x23 +#define BTM_BLE_AD_TYPE_URI 0x24 +#define BTM_BLE_AD_TYPE_INDOOR_POSITION 0x25 +#define BTM_BLE_AD_TYPE_TRANS_DISC_DATA 0x26 +#define BTM_BLE_AD_TYPE_LE_SUPPORT_FEATURE 0x27 +#define BTM_BLE_AD_TYPE_CHAN_MAP_UPDATE 0x28 + +#define BTM_BLE_AD_TYPE_MANU HCI_EIR_MANUFACTURER_SPECIFIC_TYPE /* 0xff */ +typedef UINT8 tBTM_BLE_AD_TYPE; + +#define BTM_BLE_LONG_ADV_MAX_LEN 249 + +/* Security settings used with L2CAP LE COC */ +#define BTM_SEC_LE_LINK_ENCRYPTED 0x01 +#define BTM_SEC_LE_LINK_PAIRED_WITHOUT_MITM 0x02 +#define BTM_SEC_LE_LINK_PAIRED_WITH_MITM 0x04 + +/* Min/max Preferred number of payload octets that the local Controller + should include in a single Link Layer Data Channel PDU. */ +#define BTM_BLE_DATA_SIZE_MAX 0x00fb +#define BTM_BLE_DATA_SIZE_MIN 0x001b + +/* Preferred maximum number of microseconds that the local Controller + should use to transmit a single Link Layer Data Channel PDU. */ +#define BTM_BLE_DATA_TX_TIME_MIN 0x0148 +#if (BLE_50_FEATURE_SUPPORT == TRUE) +#define BTM_BLE_DATA_TX_TIME_MAX 0x04290 // define by spec v5.0 +#else +#define BTM_BLE_DATA_TX_TIME_MAX 0x0848 +#endif + +/* adv tx power level */ +#define BTM_BLE_ADV_TX_POWER_MIN 0 /* minimum tx power */ +#define BTM_BLE_ADV_TX_POWER_MAX BTM_TX_POWER_LEVEL_MAX /* maximum tx power */ +typedef UINT8 tBTM_BLE_ADV_TX_POWER; + +/* adv tx power in dBm */ +typedef struct { + UINT8 adv_inst_max; /* max adv instance supported in controller */ + UINT8 rpa_offloading; + UINT16 tot_scan_results_strg; + UINT8 max_irk_list_sz; + UINT8 filter_support; + UINT8 max_filter; + UINT8 energy_support; + BOOLEAN values_read; + UINT16 version_supported; + UINT16 total_trackable_advertisers; + UINT8 extended_scan_support; + UINT8 debug_logging_supported; +} tBTM_BLE_VSC_CB; + +/* slave preferred connection interval range */ +typedef struct { + UINT16 low; + UINT16 hi; + +} tBTM_BLE_INT_RANGE; + +/* Service tag supported in the device */ +typedef struct { + UINT8 num_service; + BOOLEAN list_cmpl; + UINT16 *p_uuid; +} tBTM_BLE_SERVICE; + +/* 32 bits Service supported in the device */ +typedef struct { + UINT8 num_service; + BOOLEAN list_cmpl; + UINT32 *p_uuid; +} tBTM_BLE_32SERVICE; + +/* 128 bits Service supported in the device */ +typedef struct { + BOOLEAN list_cmpl; + UINT8 uuid128[MAX_UUID_SIZE]; +} tBTM_BLE_128SERVICE; + +typedef struct { + UINT8 len; + UINT8 *p_val; +} tBTM_BLE_MANU; + + +typedef struct { + tBT_UUID service_uuid; + UINT8 len; + UINT8 *p_val; +} tBTM_BLE_SERVICE_DATA; + +typedef struct { + UINT8 adv_type; + UINT8 len; + UINT8 *p_val; /* number of len byte */ +} tBTM_BLE_PROP_ELEM; + +typedef struct { + UINT8 num_elem; + tBTM_BLE_PROP_ELEM *p_elem; +} tBTM_BLE_PROPRIETARY; + +typedef struct { + tBTM_BLE_INT_RANGE int_range; /* slave prefered conn interval range */ + tBTM_BLE_MANU *p_manu; /* manufacturer data */ + tBTM_BLE_SERVICE *p_services; /* services */ + tBTM_BLE_128SERVICE *p_services_128b; /* 128 bits service */ + tBTM_BLE_32SERVICE *p_service_32b; /* 32 bits Service UUID */ + tBTM_BLE_SERVICE *p_sol_services; /* 16 bits services Solicitation UUIDs */ + tBTM_BLE_32SERVICE *p_sol_service_32b; /* List of 32 bit Service Solicitation UUIDs */ + tBTM_BLE_128SERVICE *p_sol_service_128b; /* List of 128 bit Service Solicitation UUIDs */ + tBTM_BLE_PROPRIETARY *p_proprietary; + tBTM_BLE_SERVICE_DATA *p_service_data; /* service data */ + UINT16 appearance; + UINT8 flag; + UINT8 tx_power; +} tBTM_BLE_ADV_DATA; + +#ifndef BTM_BLE_MULTI_ADV_MAX +#define BTM_BLE_MULTI_ADV_MAX 16 /* controller returned adv_inst_max should be less + than this number */ +#endif + +#define BTM_BLE_MULTI_ADV_INVALID 0 + +#define BTM_BLE_MULTI_ADV_ENB_EVT 1 +#define BTM_BLE_MULTI_ADV_DISABLE_EVT 2 +#define BTM_BLE_MULTI_ADV_PARAM_EVT 3 +#define BTM_BLE_MULTI_ADV_DATA_EVT 4 +typedef UINT8 tBTM_BLE_MULTI_ADV_EVT; + +#define BTM_BLE_MULTI_ADV_DEFAULT_STD 0 + +typedef struct { + UINT16 adv_int_min; + UINT16 adv_int_max; + UINT8 adv_type; + tBTM_BLE_ADV_CHNL_MAP channel_map; + tBTM_BLE_AFP adv_filter_policy; + tBTM_BLE_ADV_TX_POWER tx_power; +} tBTM_BLE_ADV_PARAMS; + +typedef struct { + UINT8 *p_sub_code; /* dynamic array to store sub code */ + UINT8 *p_inst_id; /* dynamic array to store instance id */ + UINT8 pending_idx; + UINT8 next_idx; +} tBTM_BLE_MULTI_ADV_OPQ; + +typedef void (tBTM_BLE_MULTI_ADV_CBACK)(tBTM_BLE_MULTI_ADV_EVT evt, UINT8 inst_id, + void *p_ref, tBTM_STATUS status); + +typedef struct { + UINT8 inst_id; + BOOLEAN in_use; + UINT8 adv_evt; + BD_ADDR rpa; + TIMER_LIST_ENT raddr_timer_ent; + tBTM_BLE_MULTI_ADV_CBACK *p_cback; + void *p_ref; + UINT8 index; +} tBTM_BLE_MULTI_ADV_INST; + +typedef struct { + UINT8 inst_index_queue[BTM_BLE_MULTI_ADV_MAX]; + int front; + int rear; +} tBTM_BLE_MULTI_ADV_INST_IDX_Q; + +typedef struct { + tBTM_BLE_MULTI_ADV_INST *p_adv_inst; /* dynamic array to store adv instance */ + tBTM_BLE_MULTI_ADV_OPQ op_q; +} tBTM_BLE_MULTI_ADV_CB; + +typedef UINT8 tGATT_IF; + +typedef void (tBTM_BLE_SCAN_THRESHOLD_CBACK)(tBTM_BLE_REF_VALUE ref_value); +typedef void (tBTM_BLE_SCAN_REP_CBACK)(tBTM_BLE_REF_VALUE ref_value, UINT8 report_format, + UINT8 num_records, UINT16 total_len, + UINT8 *p_rep_data, UINT8 status); +typedef void (tBTM_BLE_SCAN_SETUP_CBACK)(UINT8 evt, tBTM_BLE_REF_VALUE ref_value, UINT8 status); + +#ifndef BTM_BLE_BATCH_SCAN_MAX +#define BTM_BLE_BATCH_SCAN_MAX 5 +#endif + +#ifndef BTM_BLE_BATCH_REP_MAIN_Q_SIZE +#define BTM_BLE_BATCH_REP_MAIN_Q_SIZE 2 +#endif + +typedef enum { + BTM_BLE_SCAN_INVALID_STATE = 0, + BTM_BLE_SCAN_ENABLE_CALLED = 1, + BTM_BLE_SCAN_ENABLED_STATE = 2, + BTM_BLE_SCAN_DISABLE_CALLED = 3, + BTM_BLE_SCAN_DISABLED_STATE = 4 +} tBTM_BLE_BATCH_SCAN_STATE; + +enum { + BTM_BLE_DISCARD_OLD_ITEMS, + BTM_BLE_DISCARD_LOWER_RSSI_ITEMS +}; +typedef UINT8 tBTM_BLE_DISCARD_RULE; + +typedef struct { + UINT8 sub_code[BTM_BLE_BATCH_SCAN_MAX]; + tBTM_BLE_BATCH_SCAN_STATE cur_state[BTM_BLE_BATCH_SCAN_MAX]; + tBTM_BLE_REF_VALUE ref_value[BTM_BLE_BATCH_SCAN_MAX]; + UINT8 pending_idx; + UINT8 next_idx; +} tBTM_BLE_BATCH_SCAN_OPQ; + +typedef struct { + UINT8 rep_mode[BTM_BLE_BATCH_REP_MAIN_Q_SIZE]; + tBTM_BLE_REF_VALUE ref_value[BTM_BLE_BATCH_REP_MAIN_Q_SIZE]; + UINT8 num_records[BTM_BLE_BATCH_REP_MAIN_Q_SIZE]; + UINT16 data_len[BTM_BLE_BATCH_REP_MAIN_Q_SIZE]; + UINT8 *p_data[BTM_BLE_BATCH_REP_MAIN_Q_SIZE]; + UINT8 pending_idx; + UINT8 next_idx; +} tBTM_BLE_BATCH_SCAN_REP_Q; + +typedef struct { + tBTM_BLE_BATCH_SCAN_STATE cur_state; + tBTM_BLE_BATCH_SCAN_MODE scan_mode; + UINT32 scan_interval; + UINT32 scan_window; + tBLE_ADDR_TYPE addr_type; + tBTM_BLE_DISCARD_RULE discard_rule; + tBTM_BLE_BATCH_SCAN_OPQ op_q; + tBTM_BLE_BATCH_SCAN_REP_Q main_rep_q; + tBTM_BLE_SCAN_SETUP_CBACK *p_setup_cback; + tBTM_BLE_SCAN_THRESHOLD_CBACK *p_thres_cback; + tBTM_BLE_SCAN_REP_CBACK *p_scan_rep_cback; + tBTM_BLE_REF_VALUE ref_value; +} tBTM_BLE_BATCH_SCAN_CB; + +/// Ble scan duplicate type +enum { + BTM_BLE_SCAN_DUPLICATE_DISABLE = 0x0, /*!< the Link Layer should generate advertising reports to the host for each packet received */ + BTM_BLE_SCAN_DUPLICATE_ENABLE = 0x1, /*!< the Link Layer should filter out duplicate advertising reports to the Host */ + BTM_BLE_SCAN_DUPLICATE_MAX = 0x2, /*!< 0x02 – 0xFF, Reserved for future use */ +}; +/* filter selection bit index */ +#define BTM_BLE_PF_ADDR_FILTER 0 +#define BTM_BLE_PF_SRVC_DATA 1 +#define BTM_BLE_PF_SRVC_UUID 2 +#define BTM_BLE_PF_SRVC_SOL_UUID 3 +#define BTM_BLE_PF_LOCAL_NAME 4 +#define BTM_BLE_PF_MANU_DATA 5 +#define BTM_BLE_PF_SRVC_DATA_PATTERN 6 +#define BTM_BLE_PF_TYPE_ALL 7 /* when passed in payload filter type all, only clear action is applicable */ +#define BTM_BLE_PF_TYPE_MAX 8 + +/* max number of filter spot for different filter type */ +#ifndef BTM_BLE_MAX_UUID_FILTER +#define BTM_BLE_MAX_UUID_FILTER 8 +#endif +#ifndef BTM_BLE_MAX_ADDR_FILTER +#define BTM_BLE_MAX_ADDR_FILTER 8 +#endif +#ifndef BTM_BLE_PF_STR_COND_MAX +#define BTM_BLE_PF_STR_COND_MAX 4 /* apply to manu data , or local name */ +#endif +#ifndef BTM_BLE_PF_STR_LEN_MAX +#define BTM_BLE_PF_STR_LEN_MAX 29 /* match for first 29 bytes */ +#endif + +typedef UINT8 tBTM_BLE_PF_COND_TYPE; + +#define BTM_BLE_PF_LOGIC_OR 0 +#define BTM_BLE_PF_LOGIC_AND 1 +typedef UINT8 tBTM_BLE_PF_LOGIC_TYPE; + +#define BTM_BLE_PF_ENABLE 1 +#define BTM_BLE_PF_CONFIG 2 +typedef UINT8 tBTM_BLE_PF_ACTION; + +typedef UINT8 tBTM_BLE_PF_FILT_INDEX; + +typedef UINT8 tBTM_BLE_PF_AVBL_SPACE; + +#define BTM_BLE_PF_BRDCAST_ADDR_FILT 1 +#define BTM_BLE_PF_SERV_DATA_CHG_FILT 2 +#define BTM_BLE_PF_SERV_UUID 4 +#define BTM_BLE_PF_SERV_SOLC_UUID 8 +#define BTM_BLE_PF_LOC_NAME_CHECK 16 +#define BTM_BLE_PF_MANUF_NAME_CHECK 32 +#define BTM_BLE_PF_SERV_DATA_CHECK 64 +typedef UINT16 tBTM_BLE_PF_FEAT_SEL; + +#define BTM_BLE_PF_LIST_LOGIC_OR 1 +#define BTM_BLE_PF_LIST_LOGIC_AND 2 +typedef UINT16 tBTM_BLE_PF_LIST_LOGIC_TYPE; + +#define BTM_BLE_PF_FILT_LOGIC_OR 0 +#define BTM_BLE_PF_FILT_LOGIC_AND 1 +typedef UINT16 tBTM_BLE_PF_FILT_LOGIC_TYPE; + +typedef UINT8 tBTM_BLE_PF_RSSI_THRESHOLD; +typedef UINT8 tBTM_BLE_PF_DELIVERY_MODE; +typedef UINT16 tBTM_BLE_PF_TIMEOUT; +typedef UINT8 tBTM_BLE_PF_TIMEOUT_CNT; +typedef UINT16 tBTM_BLE_PF_ADV_TRACK_ENTRIES; + +typedef struct { + tBTM_BLE_PF_FEAT_SEL feat_seln; + tBTM_BLE_PF_LIST_LOGIC_TYPE logic_type; + tBTM_BLE_PF_FILT_LOGIC_TYPE filt_logic_type; + tBTM_BLE_PF_RSSI_THRESHOLD rssi_high_thres; + tBTM_BLE_PF_RSSI_THRESHOLD rssi_low_thres; + tBTM_BLE_PF_DELIVERY_MODE dely_mode; + tBTM_BLE_PF_TIMEOUT found_timeout; + tBTM_BLE_PF_TIMEOUT lost_timeout; + tBTM_BLE_PF_TIMEOUT_CNT found_timeout_cnt; + tBTM_BLE_PF_ADV_TRACK_ENTRIES num_of_tracking_entries; +} tBTM_BLE_PF_FILT_PARAMS; + +enum { + BTM_BLE_SCAN_COND_ADD, + BTM_BLE_SCAN_COND_DELETE, + BTM_BLE_SCAN_COND_CLEAR = 2 +}; +typedef UINT8 tBTM_BLE_SCAN_COND_OP; + +enum { + BTM_BLE_FILT_ENABLE_DISABLE = 1, + BTM_BLE_FILT_CFG = 2, + BTM_BLE_FILT_ADV_PARAM = 3 +}; + +typedef UINT8 tBTM_BLE_FILT_CB_EVT; + +/* BLE adv payload filtering config complete callback */ +typedef void (tBTM_BLE_PF_CFG_CBACK)(tBTM_BLE_PF_ACTION action, tBTM_BLE_SCAN_COND_OP cfg_op, + tBTM_BLE_PF_AVBL_SPACE avbl_space, tBTM_STATUS status, + tBTM_BLE_REF_VALUE ref_value); + +typedef void (tBTM_BLE_PF_CMPL_CBACK) (tBTM_BLE_PF_CFG_CBACK); + +/* BLE adv payload filtering status setup complete callback */ +typedef void (tBTM_BLE_PF_STATUS_CBACK) (UINT8 action, tBTM_STATUS status, + tBTM_BLE_REF_VALUE ref_value); + +/* BLE adv payload filtering param setup complete callback */ +typedef void (tBTM_BLE_PF_PARAM_CBACK) (tBTM_BLE_PF_ACTION action_type, + tBTM_BLE_PF_AVBL_SPACE avbl_space, + tBTM_BLE_REF_VALUE ref_value, tBTM_STATUS status); +#if (BLE_50_FEATURE_SUPPORT == TRUE) +#define MAX_BLE_ADV_INSTANCE 10 +#define MIN_BLE_PERIODIC_ADV_REPORT_LEN 7 +typedef struct { + UINT8 inst_id; + BOOLEAN configured; + BOOLEAN legacy_pdu; + + + BOOLEAN directed; + BOOLEAN scannable; + BOOLEAN connetable; +} tBTM_BLE_EXTENDED_INST; + +typedef struct { + tBTM_BLE_EXTENDED_INST inst[MAX_BLE_ADV_INSTANCE]; /* dynamic array to store adv instance */ + UINT8 scan_duplicate; +} tBTM_BLE_EXTENDED_CB; + +#define BTM_BLE_GAP_SET_EXT_ADV_PROP_CONNECTABLE (1 << 0) +#define BTM_BLE_GAP_SET_EXT_ADV_PROP_SCANNABLE (1 << 1) +#define BTM_BLE_GAP_SET_EXT_ADV_PROP_DIRECTED (1 << 2) +#define BTM_BLE_GAP_SET_EXT_ADV_PROP_HD_DIRECTED (1 << 3) +#define BTM_BLE_GAP_SET_EXT_ADV_PROP_LEGACY (1 << 4) +#define BTM_BLE_GAP_SET_EXT_ADV_PROP_ANON_ADV (1 << 5) +#define BTM_BLE_GAP_SET_EXT_ADV_PROP_INCLUDE_TX_PWR (1 << 6) +#define BTM_BLE_GAP_SET_EXT_ADV_PROP_MASK (0x7F) + +#define BTM_BLE_GAP_SET_EXT_ADV_PROP_LEGACY_IND (ESP_BLE_GAP_SET_EXT_ADV_PROP_LEGACY |\ + ESP_BLE_GAP_SET_EXT_ADV_PROP_CONNECTABLE |\ + ESP_BLE_GAP_SET_EXT_ADV_PROP_SCANNABLE) +#define BTM_BLE_GAP_SET_EXT_ADV_PROP_LEGACY_LD_DIR (ESP_BLE_GAP_SET_EXT_ADV_PROP_LEGACY |\ + ESP_BLE_GAP_SET_EXT_ADV_PROP_CONNECTABLE |\ + ESP_BLE_GAP_SET_EXT_ADV_PROP_DIRECTED) +#define BTM_BLE_GAP_SET_EXT_ADV_PROP_LEGACY_HD_DIR (ESP_BLE_GAP_SET_EXT_ADV_PROP_LEGACY |\ + ESP_BLE_GAP_SET_EXT_ADV_PROP_CONNECTABLE |\ + ESP_BLE_GAP_SET_EXT_ADV_PROP_HD_DIRECTED) +#define BTM_BLE_GAP_SET_EXT_ADV_PROP_LEGACY_SCAN (ESP_BLE_GAP_SET_EXT_ADV_PROP_LEGACY |\ + ESP_BLE_GAP_SET_EXT_ADV_PROP_SCANNABLE) +#define BTM_BLE_GAP_SET_EXT_ADV_PROP_LEGACY_NONCONN (ESP_BLE_GAP_SET_EXT_ADV_PROP_LEGACY) +typedef UINT16 tBTM_BLE_EXT_ADV_TYPE_MASK; + +#define BTM_BLE_GAP_PHY_1M 1 +#define BTM_BLE_GAP_PHY_2M 2 +#define BTM_BLE_GAP_PHY_CODED 3 +typedef UINT8 tBTM_BLE_GAP_PHY; + +#define BTM_BLE_GAP_PHY_NO_TX_PREF_MASK (0x01) +#define BTM_BLE_GAP_PHY_NO_RX_PREF_MASK (0x02) +#define BTM_BLE_GAP_PHY_1M_PREF_MASK (0x03) +#define BTM_BLE_GAP_PHY_2M_PREF_MASK (0x04) +#define BTM_BLE_GAP_PHY_CODED_PREF_MASK (0x05) +typedef UINT8 tBTM_BLE_GAP_PHY_MASK; + +#define BTM_BLE_GAP_EXT_SCAN_UNCODE_MASK 0x01 +#define BTM_BLE_GAP_EXT_SCAN_CODE_MASK 0x02 +typedef UINT8 tBTM_BLE_EXT_SCAN_CFG_MASK; + +typedef struct { + tBTM_BLE_EXT_ADV_TYPE_MASK type; + UINT32 interval_min; + UINT32 interval_max; + tBTM_BLE_ADV_CHNL_MAP channel_map; + tBLE_ADDR_TYPE own_addr_type; + tBLE_ADDR_TYPE peer_addr_type; + BD_ADDR peer_addr; + tBTM_BLE_AFP filter_policy; + INT8 tx_power; + tBTM_BLE_GAP_PHY primary_phy; + UINT8 max_skip; + tBTM_BLE_GAP_PHY secondary_phy; + UINT8 sid; + BOOLEAN scan_req_notif; +} tBTM_BLE_GAP_EXT_ADV_PARAMS; + +typedef struct { + UINT8 instance; + int duration; + int max_events; +} tBTM_BLE_EXT_ADV; + + +typedef struct { + tBLE_SCAN_MODE scan_type; + UINT16 scan_interval; + UINT16 scan_window; +} tBTM_BLE_EXT_SCAN_CFG; + +typedef struct { + tBLE_ADDR_TYPE own_addr_type; + UINT8 filter_policy; + UINT8 scan_duplicate; + tBTM_BLE_EXT_SCAN_CFG_MASK cfg_mask; + tBTM_BLE_EXT_SCAN_CFG uncoded_cfg; + tBTM_BLE_EXT_SCAN_CFG coded_cfg; +} tBTM_BLE_EXT_SCAN_PARAMS; + +typedef struct { + UINT16 interval_min; + UINT16 interval_max; + UINT8 properties; +} tBTM_BLE_Periodic_Adv_Params; + +typedef struct { + UINT8 filter_policy; + #if (CONFIG_BT_BLE_FEAT_CREATE_SYNC_ENH) + UINT8 reports_disabled; + UINT8 filter_duplicates; + #endif + UINT8 sid; + tBLE_ADDR_TYPE addr_type; + BD_ADDR addr; + UINT16 skip; + UINT16 sync_timeout; +} tBTM_BLE_Periodic_Sync_Params; + +typedef struct { + uint8_t status; + uint16_t conn_idx; + uint8_t tx_phy; + uint8_t rx_phy; +} tBTM_BLE_UPDATE_PHY; +#endif // #if (BLE_50_FEATURE_SUPPORT == TRUE) +typedef union { + UINT16 uuid16_mask; + UINT32 uuid32_mask; + UINT8 uuid128_mask[LEN_UUID_128]; +} tBTM_BLE_PF_COND_MASK; + +typedef struct { + tBLE_BD_ADDR *p_target_addr; /* target address, if NULL, generic UUID filter */ + tBT_UUID uuid; /* UUID condition */ + tBTM_BLE_PF_LOGIC_TYPE cond_logic; /* AND/OR */ + tBTM_BLE_PF_COND_MASK *p_uuid_mask; /* UUID mask */ +} tBTM_BLE_PF_UUID_COND; + +typedef struct { + UINT8 data_len; /* <= 20 bytes */ + UINT8 *p_data; +} tBTM_BLE_PF_LOCAL_NAME_COND; + +typedef struct { + UINT16 company_id; /* company ID */ + UINT8 data_len; /* <= 20 bytes */ + UINT8 *p_pattern; + UINT16 company_id_mask; /* UUID value mask */ + UINT8 *p_pattern_mask; /* Manufacturer data matching mask, + same length as data pattern, + set to all 0xff, match exact data */ +} tBTM_BLE_PF_MANU_COND; + +typedef struct { + UINT16 uuid; /* service ID */ + UINT8 data_len; /* <= 20 bytes */ + UINT8 *p_pattern; + UINT8 *p_pattern_mask; /* Service data matching mask, same length as data pattern, + set to all 0xff, match exact data */ +} tBTM_BLE_PF_SRVC_PATTERN_COND; + + +typedef union { + tBLE_BD_ADDR target_addr; + tBTM_BLE_PF_LOCAL_NAME_COND local_name; /* local name filtering */ + tBTM_BLE_PF_MANU_COND manu_data; /* manufacturer data filtering */ + tBTM_BLE_PF_UUID_COND srvc_uuid; /* service UUID filtering */ + tBTM_BLE_PF_UUID_COND solicitate_uuid; /* solicited service UUID filtering */ + tBTM_BLE_PF_SRVC_PATTERN_COND srvc_data; /* service data pattern */ +} tBTM_BLE_PF_COND_PARAM; + +typedef struct { + UINT8 action_ocf[BTM_BLE_PF_TYPE_MAX]; + tBTM_BLE_REF_VALUE ref_value[BTM_BLE_PF_TYPE_MAX]; + tBTM_BLE_PF_PARAM_CBACK *p_filt_param_cback[BTM_BLE_PF_TYPE_MAX]; + tBTM_BLE_PF_CFG_CBACK *p_scan_cfg_cback[BTM_BLE_PF_TYPE_MAX]; + UINT8 cb_evt[BTM_BLE_PF_TYPE_MAX]; + UINT8 pending_idx; + UINT8 next_idx; +} tBTM_BLE_ADV_FILTER_ADV_OPQ; + +#define BTM_BLE_MAX_FILTER_COUNTER (BTM_BLE_MAX_ADDR_FILTER + 1) /* per device filter + one generic filter indexed by 0 */ + +#ifndef BTM_CS_IRK_LIST_MAX +#define BTM_CS_IRK_LIST_MAX 0x20 +#endif + +typedef struct { + BOOLEAN in_use; + BD_ADDR bd_addr; + UINT8 pf_counter[BTM_BLE_PF_TYPE_MAX]; /* number of filter indexed by tBTM_BLE_PF_COND_TYPE */ +} tBTM_BLE_PF_COUNT; + +typedef struct { + BOOLEAN enable; + UINT8 op_type; + tBTM_BLE_PF_COUNT *p_addr_filter_count; /* per BDA filter array */ + tBLE_BD_ADDR cur_filter_target; + tBTM_BLE_PF_STATUS_CBACK *p_filt_stat_cback; + tBTM_BLE_ADV_FILTER_ADV_OPQ op_q; +} tBTM_BLE_ADV_FILTER_CB; + +/* Sub codes */ +#define BTM_BLE_META_PF_ENABLE 0x00 +#define BTM_BLE_META_PF_FEAT_SEL 0x01 +#define BTM_BLE_META_PF_ADDR 0x02 +#define BTM_BLE_META_PF_UUID 0x03 +#define BTM_BLE_META_PF_SOL_UUID 0x04 +#define BTM_BLE_META_PF_LOCAL_NAME 0x05 +#define BTM_BLE_META_PF_MANU_DATA 0x06 +#define BTM_BLE_META_PF_SRVC_DATA 0x07 +#define BTM_BLE_META_PF_ALL 0x08 + +typedef UINT8 BTM_BLE_ADV_STATE; +typedef UINT8 BTM_BLE_ADV_INFO_PRESENT; +typedef UINT8 BTM_BLE_RSSI_VALUE; +typedef UINT16 BTM_BLE_ADV_INFO_TIMESTAMP; + +/* These are the fields returned in each device adv packet. It +** is returned in the results callback if registered. +*/ +typedef struct { + UINT8 conn_mode; + tBTM_BLE_AD_MASK ad_mask; /* mask of the valid adv data field */ + UINT8 flag; + UINT8 tx_power_level; + UINT8 remote_name_len; + UINT8 *p_remote_name; + tBTM_BLE_SERVICE service; +} tBTM_BLE_INQ_DATA; + +enum { + BTM_BLE_CONN_NONE, + BTM_BLE_CONN_AUTO, + BTM_BLE_CONN_SELECTIVE +}; +typedef UINT8 tBTM_BLE_CONN_TYPE; + +#define ADV_INFO_PRESENT 0x00 +#define NO_ADV_INFO_PRESENT 0x01 + +typedef btgatt_track_adv_info_t tBTM_BLE_TRACK_ADV_DATA; + +typedef void (tBTM_BLE_TRACK_ADV_CBACK)(tBTM_BLE_TRACK_ADV_DATA *p_track_adv_data); + +typedef UINT8 tBTM_BLE_TRACK_ADV_EVT; + +typedef struct { + tBTM_BLE_REF_VALUE ref_value; + tBTM_BLE_TRACK_ADV_CBACK *p_track_cback; +} tBTM_BLE_ADV_TRACK_CB; + +enum { + BTM_BLE_TRACK_ADV_ADD, + BTM_BLE_TRACK_ADV_REMOVE +}; + +typedef UINT8 tBTM_BLE_TRACK_ADV_ACTION; + +#define BTM_BLE_MULTI_ADV_INVALID 0 + +#define BTM_BLE_BATCH_SCAN_ENABLE_EVT 1 +#define BTM_BLE_BATCH_SCAN_CFG_STRG_EVT 2 +#define BTM_BLE_BATCH_SCAN_READ_REPTS_EVT 3 +#define BTM_BLE_BATCH_SCAN_THR_EVT 4 +#define BTM_BLE_BATCH_SCAN_PARAM_EVT 5 +#define BTM_BLE_BATCH_SCAN_DISABLE_EVT 6 + +typedef UINT8 tBTM_BLE_BATCH_SCAN_EVT; + +typedef UINT32 tBTM_BLE_TX_TIME_MS; +typedef UINT32 tBTM_BLE_RX_TIME_MS; +typedef UINT32 tBTM_BLE_IDLE_TIME_MS; +typedef UINT32 tBTM_BLE_ENERGY_USED; + +typedef void (tBTM_BLE_ENERGY_INFO_CBACK)(tBTM_BLE_TX_TIME_MS tx_time, tBTM_BLE_RX_TIME_MS rx_time, + tBTM_BLE_IDLE_TIME_MS idle_time, + tBTM_BLE_ENERGY_USED energy_used, + tBTM_STATUS status); + +typedef struct { + tBTM_BLE_ENERGY_INFO_CBACK *p_ener_cback; +} tBTM_BLE_ENERGY_INFO_CB; + +typedef BOOLEAN (tBTM_BLE_SEL_CBACK)(BD_ADDR random_bda, UINT8 *p_remote_name); +typedef void (tBTM_BLE_CTRL_FEATURES_CBACK)(tBTM_STATUS status); + +/* callback function for SMP signing algorithm, signed data in little endian order with tlen bits long */ +typedef void (tBTM_BLE_SIGN_CBACK)(void *p_ref_data, UINT8 *p_signing_data); +typedef void (tBTM_BLE_VERIFY_CBACK)(void *p_ref_data, BOOLEAN match); +/* random address set complete callback */ +typedef void (tBTM_BLE_RANDOM_SET_CBACK) (BD_ADDR random_bda); + +typedef void (tBTM_BLE_SCAN_REQ_CBACK)(BD_ADDR remote_bda, tBLE_ADDR_TYPE addr_type, UINT8 adv_evt); +typedef void (*tBLE_SCAN_PARAM_SETUP_CBACK)(tGATT_IF client_if, tBTM_STATUS status); + +tBTM_BLE_SCAN_SETUP_CBACK bta_ble_scan_setup_cb; + +typedef void (tBTM_START_ADV_CMPL_CBACK) (UINT8 status); +typedef void (tBTM_START_STOP_ADV_CMPL_CBACK) (UINT8 status); + +typedef void (tBTM_UPDATE_DUPLICATE_EXCEPTIONAL_LIST_CMPL_CBACK) (tBTM_STATUS status, uint8_t subcode, uint32_t length, uint8_t *device_info); +typedef void (tBTM_CLEAR_ADV_CMPL_CBACK) (UINT8 status); +#if (BLE_50_FEATURE_SUPPORT == TRUE) +#define BTM_BLE_5_GAP_READ_PHY_COMPLETE_EVT 1 +#define BTM_BLE_5_GAP_SET_PREFERED_DEFAULT_PHY_COMPLETE_EVT 2 +#define BTM_BLE_5_GAP_SET_PREFERED_PHY_COMPLETE_EVT 3 +#define BTM_BLE_5_GAP_EXT_ADV_SET_RAND_ADDR_COMPLETE_EVT 4 +#define BTM_BLE_5_GAP_EXT_ADV_SET_PARAMS_COMPLETE_EVT 5 +#define BTM_BLE_5_GAP_EXT_ADV_DATA_SET_COMPLETE_EVT 6 +#define BTM_BLE_5_GAP_EXT_SCAN_RSP_DATA_SET_COMPLETE_EVT 7 +#define BTM_BLE_5_GAP_EXT_ADV_START_COMPLETE_EVT 8 +#define BTM_BLE_5_GAP_EXT_ADV_STOP_COMPLETE_EVT 9 +#define BTM_BLE_5_GAP_EXT_ADV_SET_REMOVE_COMPLETE_EVT 10 +#define BTM_BLE_5_GAP_EXT_ADV_SET_CLEAR_COMPLETE_EVT 11 +#define BTM_BLE_5_GAP_PERIODIC_ADV_SET_PARAMS_COMPLETE_EVT 12 +#define BTM_BLE_5_GAP_PERIODIC_ADV_DATA_SET_COMPLETE_EVT 13 +#define BTM_BLE_5_GAP_PERIODIC_ADV_START_COMPLETE_EVT 14 +#define BTM_BLE_5_GAP_PERIODIC_ADV_STOP_COMPLETE_EVT 15 +#define BTM_BLE_5_GAP_PERIODIC_ADV_CREATE_SYNC_COMPLETE_EVT 16 +#define BTM_BLE_5_GAP_PERIODIC_ADV_SYNC_CANCEL_COMPLETE_EVT 17 +#define BTM_BLE_5_GAP_PERIODIC_ADV_SYNC_TERMINATE_COMPLETE_EVT 18 +#define BTM_BLE_5_GAP_PERIODIC_ADV_ADD_DEV_COMPLETE_EVT 19 +#define BTM_BLE_5_GAP_PERIODIC_ADV_REMOVE_DEV_COMPLETE_EVT 20 +#define BTM_BLE_5_GAP_PERIODIC_ADV_CLEAR_DEV_COMPLETE_EVT 21 +#define BTM_BLE_5_GAP_SET_EXT_SCAN_PARAMS_COMPLETE_EVT 22 +#define BTM_BLE_5_GAP_EXT_SCAN_START_COMPLETE_EVT 23 +#define BTM_BLE_5_GAP_EXT_SCAN_STOP_COMPLETE_EVT 24 +#define BTM_BLE_5_GAP_PREFER_EXT_CONN_PARAMS_SET_COMPLETE_EVT 25 +#define BTM_BLE_5_GAP_PHY_UPDATE_COMPLETE_EVT 26 +#define BTM_BLE_5_GAP_EXT_ADV_REPORT_EVT 27 +#define BTM_BLE_5_GAP_SCAN_TIMEOUT_EVT 28 +#define BTM_BLE_5_GAP_ADV_TERMINATED_EVT 29 +#define BTM_BLE_5_GAP_SCAN_REQ_RECEIVED_EVT 30 +#define BTM_BLE_5_GAP_CHANNEL_SELETE_ALGORITHM_EVT 31 +#define BTM_BLE_5_GAP_PERIODIC_ADV_REPORT_EVT 32 +#define BTM_BLE_5_GAP_PERIODIC_ADV_SYNC_LOST_EVT 33 +#define BTM_BLE_5_GAP_PERIODIC_ADV_SYNC_ESTAB_EVT 34 +#if (BLE_FEAT_PERIODIC_ADV_SYNC_TRANSFER == TRUE) +#define BTM_BLE_GAP_PERIODIC_ADV_RECV_ENABLE_COMPLETE_EVT 35 +#define BTM_BLE_GAP_PERIODIC_ADV_SYNC_TRANS_COMPLETE_EVT 36 +#define BTM_BLE_GAP_PERIODIC_ADV_SET_INFO_TRANS_COMPLETE_EVT 37 +#define BTM_BLE_GAP_SET_PAST_PARAMS_COMPLETE_EVT 38 +#define BTM_BLE_GAP_PERIODIC_ADV_SYNC_TRANS_RECV_EVT 39 +#endif // #if (BLE_FEAT_PERIODIC_ADV_SYNC_TRANSFER == TRUE) +#define BTM_BLE_5_GAP_UNKNOWN_EVT 40 +typedef UINT8 tBTM_BLE_5_GAP_EVENT; + +#define BTM_BLE_EXT_ADV_DATA_COMPLETE 0x00 +#define BTM_BLE_EXT_ADV_DATA_INCOMPLETE 0x01 +#define BTM_BLE_EXT_ADV_DATA_TRUNCATED 0x02 +typedef UINT8 tBTM_BLE_EXT_ADV_DATA_STATUS; + +#define BTM_BLE_ADV_DATA_COMPLETE_MASK (0x0000) +#define BTM_BLE_ADV_DATA_INCOMPLETE_MASK (0x0020) +#define BTM_BLE_ADV_DATA_TRUNCATED_MASK (0x0040) +#define BTM_BLE_ADV_DATA_STATUS_MASK (0x0060) + + +//#define BTM_BLE_ADV_CONN_MASK (0x0001) +//#define BTM_BLE_ADV_SCAN_MASK (0x0002) +//#define BTM_BLE_ADV_DIRECT_MASK (0x0004) +//#define BTM_BLE_SCAN_RSP_MASK (0x0008) +#define BTM_BLE_ADV_LEGACY_MASK (0x0010) +typedef UINT8 tBTM_BLE_ADV_MASK; + +// /* Advertising report */ +// #define BTM_BLE_ADV_REPORT_ADV_IND (0) +// #define BTM_BLE_ADV_REPORT_DIR_IND (1) +// #define BTM_BLE_ADV_REPORT_SCAN_IND (2) +// #define BTM_BLE_ADV_REPORT_NONCONN_IND (3) +// #define BTM_BLE_ADV_REPORT_SCAN_RSP (4) + +// /* Bluetooth 5.0, Vol 2, Part E, 7.7.65.13 */ +// #define BTM_BLE_LEGACY_ADV_TYPE_IND (0x13) +// #define BTM_BLE_LEGACY_ADV_TYPE_DIRECT_IND (0x15) +// #define BTM_BLE_LEGACY_ADV_TYPE_SCAN_IND (0x12) +// #define BTM_BLE_LEGACY_ADV_TYPE_NONCON_IND (0x10) +// #define BTM_BLE_LEGACY_ADV_TYPE_SCAN_RSP_ADV_IND (0x1b) +// #define BTM_BLE_LEGACY_ADV_TYPE_SCAN_RSP_ADV_SCAN_IND (0x1a) + +typedef struct { + UINT8 status; + BD_ADDR addr; + UINT8 tx_phy; + UINT8 rx_phy; +} tBTM_BLE_READ_PHY_CMPL; + +typedef struct { + UINT8 status; +} tBTM_BLE_SET_PREF_DEF_PHY_CMPL; + +typedef struct { + UINT8 status; +} tBTM_BLE_SET_PERF_PHY_CMPL; + +typedef struct { + UINT8 status; +} tBTM_BLE_EXT_ADV_SET_RAND_ADDR_CMPL; + +typedef struct { + UINT8 status; +} tBTM_BLE_EXT_ADV_SET_PARAMS_CMPL; + +typedef struct { + UINT8 status; +} tBTM_BLE_EXT_ADV_DATA_SET_CMPL; + +typedef struct { + UINT8 status; +} tBTM_BLE_EXT_ADV_SCAN_RSP_DATA_SET_CMPL; + +typedef struct { + UINT8 status; +} tBTM_BLE_EXT_ADV_START_CMPL; + +typedef struct { + UINT8 status; +} tBTM_BLE_EXT_ADV_STOP_CMPL; + +typedef struct { + UINT8 status; +} tBTM_BLE_PERIOD_ADV_SET_PARAMS_CMPL; + +typedef struct { + UINT8 status; +} tBTM_BLE_PERIOD_ADV_DATA_SET_CMPL; + +typedef struct { + UINT8 status; +} tBTM_BLE_PERIOD_ADV_START_CMPL; + +typedef struct { + UINT8 status; +} tBTM_BLE_PERIOD_ADV_STOP_CMPL; + +typedef struct { + UINT8 status; +} tBTM_BLE_PERIOD_ADV_SYNC_CREATE_CMPL; + +typedef struct { + UINT8 status; +} tBTM_BLE_PERIOD_ADV_SYNC_CANCEL_CMPL; + +typedef struct { + UINT8 status; +} tBTM_BLE_PERIOD_ADV_SYNC_TEMINAT_CMPL; + +typedef struct { + UINT8 status; +} tBTM_BLE_PERIOD_ADV_ADD_DEV_CMPL; + +typedef struct { + UINT8 status; +} tBTM_BLE_PERIOD_ADV_REMOVE_DEV_CMPL; + +typedef struct { + UINT8 status; +} tBTM_BLE_PEROID_ADV_CLEAR_DEV_CMPL; + +typedef struct { + UINT8 status; +} tBTM_BLE_SET_EXT_SCAN_PARAMS_CMPL; + +typedef struct { + UINT8 status; +} tBTM_BLE_EXT_SCAN_START_CMPL; + +typedef struct { + UINT8 status; +} tBTM_BLE_EXT_SCAN_STOP_CMPL; + +typedef struct { + UINT8 status; +} tBTM_BLE_PREF_EXT_CONN_SET_PARAMS_CMPL; + +typedef struct { + uint8_t status; + BD_ADDR addr; + uint8_t tx_phy; + uint8_t rx_phy; +} tBTM_BLE_PHY_UPDATE_CMPL; + +typedef struct { + // UINT8 props; + // UINT8 legacy_event_type; + UINT8 event_type; + tBLE_ADDR_TYPE addr_type; + BD_ADDR addr; + UINT8 primary_phy; + UINT8 secondry_phy; + UINT8 sid; + UINT8 tx_power; + UINT8 rssi; + UINT16 per_adv_interval; + tBLE_ADDR_TYPE dir_addr_type; + BD_ADDR dir_addr; + tBTM_BLE_EXT_ADV_DATA_STATUS data_status; + UINT8 adv_data_len; + UINT8 *adv_data; +} tBTM_BLE_EXT_ADV_REPORT; + +typedef struct { + UINT8 status; + UINT8 adv_handle; + UINT16 conn_handle; + UINT8 completed_event; +} tBTM_BLE_ADV_TERMINAT; + +typedef struct { + UINT8 adv_handle; + tBLE_ADDR_TYPE scan_addr_type; + BD_ADDR scan_addr; +} tBTM_BLE_SCAN_REQ_RECEIVED; + +typedef struct { + UINT16 conn_handle; + UINT8 channel_sel_alg; +} tBTM_BLE_CHANNEL_SEL_ALG; + +typedef struct { + UINT16 sync_handle; + UINT8 tx_power; + INT8 rssi; + tBTM_BLE_EXT_ADV_DATA_STATUS data_status; + UINT8 data_length; + UINT8 *data; +} tBTM_PERIOD_ADV_REPORT; + +typedef struct { + UINT16 sync_handle; +} tBTM_BLE_PERIOD_ADV_SYNC_LOST; + +typedef struct { + UINT8 status; + UINT16 sync_handle; + UINT8 sid; + tBLE_ADDR_TYPE adv_addr_type; + BD_ADDR adv_addr; + UINT8 adv_phy; + UINT16 period_adv_interval; + UINT8 adv_clk_accuracy; +} tBTM_BLE_PERIOD_ADV_SYNC_ESTAB; + +typedef struct { + UINT16 scan_interval; + UINT16 scan_window; + UINT16 interval_min; + UINT16 interval_max; + UINT16 latency; + UINT16 supervision_timeout; + UINT16 min_ce_len; + UINT16 max_ce_len; +} tBTM_BLE_CONN_PARAMS; + +#if (BLE_FEAT_PERIODIC_ADV_SYNC_TRANSFER == TRUE) +typedef struct { + UINT8 status; +} tBTM_BLE_PERIOD_ADV_RECV_ENABLE_CMPL; + +typedef struct { + UINT8 status; + BD_ADDR addr; +} tBTM_BLE_PERIOD_ADV_SYNC_TRANS_CMPL; + +typedef struct { + UINT8 status; + BD_ADDR addr; +} tBTM_BLE_PERIOD_ADV_SET_INFO_TRANS_CMPL; + +typedef struct { + UINT8 status; + BD_ADDR addr; +} tBTM_BLE_SET_PERIOD_ADV_SYNC_TRANS_PARAMS_CMPL; + +typedef struct { + UINT8 status; + BD_ADDR addr; + UINT16 service_data; + UINT16 sync_handle; + UINT8 adv_sid; + UINT8 adv_addr_type; + BD_ADDR adv_addr; + UINT8 adv_phy; + UINT16 adv_interval; + UINT8 adv_clk_accuracy; +} tBTM_BLE_PERIOD_ADV_SYNC_TRANS_RECV; +#endif //#if (BLE_FEAT_PERIODIC_ADV_SYNC_TRANSFER == TRUE) + +typedef union { + UINT8 status; + tBTM_BLE_READ_PHY_CMPL read_phy; + tBTM_BLE_SET_PREF_DEF_PHY_CMPL set_perf_def_phy; + tBTM_BLE_SET_PERF_PHY_CMPL set_perf_phy; + tBTM_BLE_EXT_ADV_SET_RAND_ADDR_CMPL set_ext_rand_addr; + tBTM_BLE_EXT_ADV_SET_PARAMS_CMPL set_params; + tBTM_BLE_EXT_ADV_DATA_SET_CMPL adv_data_set; + tBTM_BLE_EXT_ADV_SCAN_RSP_DATA_SET_CMPL scan_rsp_data_set; + tBTM_BLE_EXT_ADV_START_CMPL adv_start; + tBTM_BLE_EXT_ADV_STOP_CMPL adv_stop; + tBTM_BLE_PERIOD_ADV_SET_PARAMS_CMPL per_adv_set_params; + tBTM_BLE_PERIOD_ADV_DATA_SET_CMPL per_adv_data_set; + tBTM_BLE_PERIOD_ADV_START_CMPL per_adv_start; + tBTM_BLE_PERIOD_ADV_STOP_CMPL per_adv_stop; + tBTM_BLE_PERIOD_ADV_SYNC_CREATE_CMPL per_adv_sync_create; + tBTM_BLE_PERIOD_ADV_SYNC_CANCEL_CMPL per_adv_sync_cancel; + tBTM_BLE_PERIOD_ADV_SYNC_TEMINAT_CMPL per_adv_sync_term; + tBTM_BLE_PERIOD_ADV_ADD_DEV_CMPL per_adv_add_dev; + tBTM_BLE_PERIOD_ADV_REMOVE_DEV_CMPL per_adv_remove_dev; + tBTM_BLE_PEROID_ADV_CLEAR_DEV_CMPL per_adv_clear_dev; + tBTM_BLE_SET_EXT_SCAN_PARAMS_CMPL ext_scan; + tBTM_BLE_EXT_SCAN_START_CMPL scan_start; + tBTM_BLE_EXT_SCAN_STOP_CMPL scan_stop; + tBTM_BLE_PREF_EXT_CONN_SET_PARAMS_CMPL ext_conn_set_params; + tBTM_BLE_PHY_UPDATE_CMPL phy_update; + tBTM_BLE_EXT_ADV_REPORT ext_adv_report; + tBTM_BLE_ADV_TERMINAT adv_term; + tBTM_BLE_SCAN_REQ_RECEIVED scan_req; + tBTM_BLE_CHANNEL_SEL_ALG channel_sel; + tBTM_PERIOD_ADV_REPORT period_adv_report; + tBTM_BLE_PERIOD_ADV_SYNC_LOST sync_lost; + tBTM_BLE_PERIOD_ADV_SYNC_ESTAB sync_estab; +#if (BLE_FEAT_PERIODIC_ADV_SYNC_TRANSFER == TRUE) + tBTM_BLE_PERIOD_ADV_RECV_ENABLE_CMPL per_adv_recv_enable; + tBTM_BLE_PERIOD_ADV_SYNC_TRANS_CMPL per_adv_sync_trans; + tBTM_BLE_PERIOD_ADV_SET_INFO_TRANS_CMPL per_adv_set_info_trans; + tBTM_BLE_SET_PERIOD_ADV_SYNC_TRANS_PARAMS_CMPL set_past_params; + tBTM_BLE_PERIOD_ADV_SYNC_TRANS_RECV past_recv; +#endif //#if (BLE_FEAT_PERIODIC_ADV_SYNC_TRANSFER == TRUE) +} tBTM_BLE_5_GAP_CB_PARAMS; + +typedef struct { + UINT8 phy_mask; + tBTM_BLE_CONN_PARAMS phy_1m_conn_params; + tBTM_BLE_CONN_PARAMS phy_2m_conn_params; + tBTM_BLE_CONN_PARAMS phy_coded_conn_params; +} tBTM_EXT_CONN_PARAMS; + +typedef void (*tBTM_BLE_5_HCI_CBACK)(tBTM_BLE_5_GAP_EVENT event, tBTM_BLE_5_GAP_CB_PARAMS *params); + +#endif //#if (BLE_50_FEATURE_SUPPORT == TRUE) + +/***************************************************************************** +** EXTERNAL FUNCTION DECLARATIONS +*****************************************************************************/ +/* +#ifdef __cplusplus +extern "C" { +#endif +*/ + +/******************************************************************************* +** +** Function BTM_BleRegiseterConnParamCallback +** +** Description register connection parameters update callback func +** +** Parameters: update_conn_param_cb +** +** Returns void +** +*******************************************************************************/ +void BTM_BleRegiseterConnParamCallback(tBTM_UPDATE_CONN_PARAM_CBACK *update_conn_param_cb); + +/******************************************************************************* +** +** Function BTM_SecAddBleDevice +** +** Description Add/modify device. This function will be normally called +** during host startup to restore all required information +** for a LE device stored in the NVRAM. +** +** Parameters: bd_addr - BD address of the peer +** bd_name - Name of the peer device. NULL if unknown. +** dev_type - Remote device's device type. +** addr_type - LE device address type. +** auth_mode - auth mode +** +** Returns TRUE if added OK, else FALSE +** +*******************************************************************************/ +//extern +BOOLEAN BTM_SecAddBleDevice (BD_ADDR bd_addr, BD_NAME bd_name, + tBT_DEVICE_TYPE dev_type, tBLE_ADDR_TYPE addr_type, UINT32 auth_mode); + +/******************************************************************************* +** +** Function BTM_SecAddBleKey +** +** Description Add/modify LE device information. This function will be +** normally called during host startup to restore all required +** information stored in the NVRAM. +** +** Parameters: bd_addr - BD address of the peer +** p_le_key - LE key values. +** key_type - LE SMP key type. +* +** Returns TRUE if added OK, else FALSE +** +*******************************************************************************/ +//extern +BOOLEAN BTM_SecAddBleKey (BD_ADDR bd_addr, tBTM_LE_KEY_VALUE *p_le_key, + tBTM_LE_KEY_TYPE key_type); + +/******************************************************************************* +** +** Function BTM_BleSetAdvParams +** +** Description This function is called to set advertising parameters. +** +** Parameters: None. +** +** Returns void +** +*******************************************************************************/ +//extern +tBTM_STATUS BTM_BleSetAdvParams(UINT16 adv_int_min, UINT16 adv_int_max, + tBLE_BD_ADDR *p_dir_bda, tBTM_BLE_ADV_CHNL_MAP chnl_map); + + + +/******************************************************************************* +** +** Function BTM_BleSetAdvParamsAll +** +** Description This function is called to set all of the advertising parameters. +** +** Parameters: None. +** +** Returns void +** +*******************************************************************************/ +tBTM_STATUS BTM_BleSetAdvParamsAll(UINT16 adv_int_min, UINT16 adv_int_max, UINT8 adv_type, + tBLE_ADDR_TYPE own_bda_type, tBLE_BD_ADDR *p_dir_bda, + tBTM_BLE_ADV_CHNL_MAP chnl_map, tBTM_BLE_AFP afp, tBTM_START_ADV_CMPL_CBACK *adv_cb); + +/******************************************************************************* +** +** Function BTM_BleStartAdv +** +** Description This function is called to start adv. +** +** Parameters: None. +** +** Returns status +** +*******************************************************************************/ +tBTM_STATUS BTM_BleStartAdv(void); + + +/******************************************************************************* +** +** Function BTM_BleWriteAdvData +** +** Description This function is called to write advertising data. +** +** Parameters: None. +** +** Returns void +** +*******************************************************************************/ +//extern +tBTM_STATUS BTM_BleWriteAdvData(tBTM_BLE_AD_MASK data_mask, + tBTM_BLE_ADV_DATA *p_data); + +/******************************************************************************* +** +** Function BTM_BleWriteLongAdvData +** +** Description This function is called to write long advertising data. +** +** Parameters: adv_data: long advertising data +** adv_data_len: the length of long advertising data +** +** Returns void +** +*******************************************************************************/ +tBTM_STATUS BTM_BleWriteLongAdvData(uint8_t *adv_data, uint8_t adv_data_len); + +/******************************************************************************* +** +** Function BTM_BleWriteAdvDataRaw +** +** Description This function is called to write raw advertising data. +** +** Parameters: p_raw_adv : point to raw advertising data +** raw_adv_len : raw advertising data +** +** Returns BTM_SUCCESS means success. +** +*******************************************************************************/ +//extern +tBTM_STATUS BTM_BleWriteAdvDataRaw(UINT8 *p_raw_adv, UINT32 raw_adv_len); + + +tBTM_STATUS BTM_BleSetRandAddress(BD_ADDR rand_addr); + +void BTM_BleClearRandAddress(void); + + +/******************************************************************************* +** +** Function BTM_BleSetAdvParams +** +** Description This function is called to set advertising parameters. +** +** Parameters adv_int_min: minimum advertising interval +** adv_int_max: maximum advertising interval +** p_dir_bda: connectable direct initiator's LE device address +** chnl_map: advertising channel map. +** +** Returns void +** +*******************************************************************************/ +//extern +void BTM_BleReadAdvParams (UINT16 *adv_int_min, UINT16 *adv_int_max, + tBLE_BD_ADDR *p_dir_bda, tBTM_BLE_ADV_CHNL_MAP *p_chnl_map); + +/******************************************************************************* +** +** Function BTM_BleObtainVendorCapabilities +** +** Description This function is called to obtain vendor capabilities +** +** Parameters p_cmn_vsc_cb - Returns the vendor capabilities +** +** Returns void +** +*******************************************************************************/ +//extern +void BTM_BleObtainVendorCapabilities(tBTM_BLE_VSC_CB *p_cmn_vsc_cb); + +/******************************************************************************* +** +** Function BTM_BleSetScanParams +** +** Description This function is called to set Scan parameters. +** +** Parameters client_if - Client IF value +** scan_interval - Scan interval +** scan_window - Scan window +** scan_type - Scan type +** scan_setup_status_cback - Scan setup status callback +** +** Returns void +** +*******************************************************************************/ +//extern +void BTM_BleSetScanParams(tGATT_IF client_if, UINT32 scan_interval, + UINT32 scan_window, tBLE_SCAN_MODE scan_type, + tBLE_SCAN_PARAM_SETUP_CBACK scan_setup_status_cback); + + + +/******************************************************************************* +** +** Function BTM_BleSetScanFilterParams +** +** Description This function is called to set Scan Filter & parameters. +** +** Parameters client_if - Client IF value +** scan_interval - Scan interval +** scan_window - Scan window +** scan_type - Scan type +** addr_type_own - owner address type +** scan_duplicate_filter - scan duplicate filter +** scan_filter_policy - scan filter policy +** scan_setup_status_cback - Scan setup status callback +** +** Returns void +** +*******************************************************************************/ +tBTM_STATUS BTM_BleSetScanFilterParams(tGATT_IF client_if, UINT32 scan_interval, UINT32 scan_window, + tBLE_SCAN_MODE scan_mode, UINT8 addr_type_own, UINT8 scan_duplicate_filter, tBTM_BLE_SFP scan_filter_policy, + tBLE_SCAN_PARAM_SETUP_CBACK scan_setup_status_cback); + + +/******************************************************************************* +** +** Function BTM_BleGetVendorCapabilities +** +** Description This function reads local LE features +** +** Parameters p_cmn_vsc_cb : Locala LE capability structure +** +** Returns void +** +*******************************************************************************/ +//extern +void BTM_BleGetVendorCapabilities(tBTM_BLE_VSC_CB *p_cmn_vsc_cb); +/******************************************************************************* +** +** Function BTM_BleSetStorageConfig +** +** Description This function is called to setup storage configuration and setup callbacks. +** +** Parameters UINT8 batch_scan_full_max -Batch scan full maximum + UINT8 batch_scan_trunc_max - Batch scan truncated value maximum + UINT8 batch_scan_notify_threshold - Threshold value + tBTM_BLE_SCAN_SETUP_CBACK *p_setup_cback - Setup callback + tBTM_BLE_SCAN_THRESHOLD_CBACK *p_thres_cback -Threshold callback + void *p_ref - Reference value +** +** Returns tBTM_STATUS +** +*******************************************************************************/ +//extern +tBTM_STATUS BTM_BleSetStorageConfig(UINT8 batch_scan_full_max, + UINT8 batch_scan_trunc_max, + UINT8 batch_scan_notify_threshold, + tBTM_BLE_SCAN_SETUP_CBACK *p_setup_cback, + tBTM_BLE_SCAN_THRESHOLD_CBACK *p_thres_cback, + tBTM_BLE_SCAN_REP_CBACK *p_cback, + tBTM_BLE_REF_VALUE ref_value); + +/******************************************************************************* +** +** Function BTM_BleEnableBatchScan +** +** Description This function is called to enable batch scan +** +** Parameters tBTM_BLE_BATCH_SCAN_MODE scan_mode - Batch scan mode + UINT32 scan_interval -Scan interval + UINT32 scan_window - Scan window value + tBLE_ADDR_TYPE addr_type - Address type + tBTM_BLE_DISCARD_RULE discard_rule - Data discard rules +** +** Returns tBTM_STATUS +** +*******************************************************************************/ +//extern +tBTM_STATUS BTM_BleEnableBatchScan(tBTM_BLE_BATCH_SCAN_MODE scan_mode, + UINT32 scan_interval, UINT32 scan_window, + tBTM_BLE_DISCARD_RULE discard_rule, + tBLE_ADDR_TYPE addr_type, + tBTM_BLE_REF_VALUE ref_value); + +/******************************************************************************* +** +** Function BTM_BleDisableBatchScan +** +** Description This function is called to disable batch scanning +** +** Parameters void +** +** Returns void +** +*******************************************************************************/ +//extern +tBTM_STATUS BTM_BleDisableBatchScan(tBTM_BLE_REF_VALUE ref_value); + +/******************************************************************************* +** +** Function BTM_BleReadScanReports +** +** Description This function is called to read batch scan reports +** +** Parameters tBLE_SCAN_MODE scan_mode - Scan mode report to be read out + tBTM_BLE_SCAN_REP_CBACK* p_cback - Reports callback +** +** Returns tBTM_STATUS +** +*******************************************************************************/ +//extern +tBTM_STATUS BTM_BleReadScanReports(tBLE_SCAN_MODE scan_mode, + tBTM_BLE_REF_VALUE ref_value); + +/******************************************************************************* +** +** Function BTM_BleTrackAdvertiser +** +** Description This function is called to read batch scan reports +** +** Parameters p_track_cback - Tracking callback +** ref_value - Reference value +** +** Returns tBTM_STATUS +** +*******************************************************************************/ +//extern +tBTM_STATUS BTM_BleTrackAdvertiser(tBTM_BLE_TRACK_ADV_CBACK *p_track_cback, + tBTM_BLE_REF_VALUE ref_value); + +/******************************************************************************* +** +** Function BTM_BleWriteScanRsp +** +** Description This function is called to write LE scan response. +** +** Parameters: p_scan_rsp: scan response. +** +** Returns status +** +*******************************************************************************/ +//extern +tBTM_STATUS BTM_BleWriteScanRsp(tBTM_BLE_AD_MASK data_mask, + tBTM_BLE_ADV_DATA *p_data); + +/******************************************************************************* +** +** Function BTM_BleWriteScanRspRaw +** +** Description This function is called to write raw scan response data +** +** Parameters: None. +** +** Returns void +** +*******************************************************************************/ +//extern +tBTM_STATUS BTM_BleWriteScanRspRaw(UINT8 *p_raw_scan_rsp, UINT32 raw_scan_rsp_len); + +/******************************************************************************* +** +** Function BTM_BleObserve +** +** Description This procedure keep the device listening for advertising +** events from a broadcast device. +** +** Parameters start: start or stop observe. +** +** Returns void +** +*******************************************************************************/ +//extern +tBTM_STATUS BTM_BleObserve(BOOLEAN start, UINT32 duration, + tBTM_INQ_RESULTS_CB *p_results_cb, tBTM_CMPL_CB *p_cmpl_cb); + +/******************************************************************************* +** +** Function BTM_BleScan +** +** Description This procedure keep the device listening for advertising +** events from a broadcast device. +** +** Parameters start: start or stop scan. +** +** Returns void +** +*******************************************************************************/ +//extern +tBTM_STATUS BTM_BleScan(BOOLEAN start, UINT32 duration, + tBTM_INQ_RESULTS_CB *p_results_cb, tBTM_CMPL_CB *p_cmpl_cb, tBTM_INQ_DIS_CB *p_discard_cb); + + +/******************************************************************************* +** +** Function BTM_GetDeviceIDRoot +** +** Description This function is called to read the local device identity +** root. +** +** Returns void +** the local device ER is copied into er +** +*******************************************************************************/ +//extern +void BTM_GetDeviceIDRoot (BT_OCTET16 ir); + +/******************************************************************************* +** +** Function BTM_GetDeviceEncRoot +** +** Description This function is called to read the local device encryption +** root. +** +** Returns void +** the local device ER is copied into er +** +*******************************************************************************/ +//extern +void BTM_GetDeviceEncRoot (BT_OCTET16 er); + +/******************************************************************************* +** +** Function BTM_GetDeviceDHK +** +** Description This function is called to read the local device DHK. +** +** Returns void +** the local device DHK is copied into dhk +** +*******************************************************************************/ +//extern +void BTM_GetDeviceDHK (BT_OCTET16 dhk); + +/******************************************************************************* +** +** Function BTM_SecurityGrant +** +** Description This function is called to grant security process. +** +** Parameters bd_addr - peer device bd address. +** res - result of the operation BTM_SUCCESS if success. +** Otherwise, BTM_REPEATED_ATTEMPTS is too many attempts. +** +** Returns None +** +*******************************************************************************/ +//extern +void BTM_SecurityGrant(BD_ADDR bd_addr, UINT8 res); + +/******************************************************************************* +** +** Function BTM_BlePasskeyReply +** +** Description This function is called after Security Manager submitted +** passkey request to the application. +** +** Parameters: bd_addr - Address of the device for which passkey was requested +** res - result of the operation SMP_SUCCESS if success +** passkey - numeric value in the range of +** BTM_MIN_PASSKEY_VAL(0) - BTM_MAX_PASSKEY_VAL(999999(0xF423F)). +** +*******************************************************************************/ +//extern +void BTM_BlePasskeyReply (BD_ADDR bd_addr, UINT8 res, UINT32 passkey); + +/******************************************************************************* +** +** Function BTM_BleSetStaticPasskey +** +** Description This function is called to set static passkey +** +** +** Parameters: add - set static passkey when add is TRUE +** clear static passkey when add is FALSE +** passkey - static passkey +** +** +*******************************************************************************/ +void BTM_BleSetStaticPasskey(BOOLEAN add, UINT32 passkey); + +/******************************************************************************* +** +** Function BTM_BleConfirmReply +** +** Description This function is called after Security Manager submitted +** numeric comparison request to the application. +** +** Parameters: bd_addr - Address of the device with which numeric +** comparison was requested +** res - comparison result BTM_SUCCESS if success +** +*******************************************************************************/ +//extern +void BTM_BleConfirmReply (BD_ADDR bd_addr, UINT8 res); + +/******************************************************************************* +** +** Function BTM_LeOobDataReply +** +** Description This function is called to provide the OOB data for +** SMP in response to BTM_LE_OOB_REQ_EVT +** +** Parameters: bd_addr - Address of the peer device +** res - result of the operation SMP_SUCCESS if success +** p_data - simple pairing Randomizer C. +** +*******************************************************************************/ +//extern +void BTM_BleOobDataReply(BD_ADDR bd_addr, UINT8 res, UINT8 len, UINT8 *p_data); + +/******************************************************************************* +** +** Function BTM_BleSecureConnectionOobDataReply +** +** Description This function is called to provide the OOB data for +** SMP in response to BTM_LE_SC_OOB_REQ_EVT when secure connection +** +** Parameters: bd_addr - Address of the peer device +** p_c - pointer to Confirmation +** p_r - pointer to Randomizer +** +*******************************************************************************/ +void BTM_BleSecureConnectionOobDataReply(BD_ADDR bd_addr, UINT8 *p_c, UINT8 *p_r); + +/******************************************************************************* +** +** Function BTM_BleSecureConnectionCreateOobData +** +** Description This function is called to create the OOB data for +** SMP when secure connection +** +*******************************************************************************/ +void BTM_BleSecureConnectionCreateOobData(void); + +/******************************************************************************* +** +** Function BTM_BleDataSignature +** +** Description This function is called to sign the data using AES128 CMAC +** algorith. +** +** Parameter bd_addr: target device the data to be signed for. +** p_text: singing data +** len: length of the signing data +** signature: output parameter where data signature is going to +** be stored. +** +** Returns TRUE if signing sucessul, otherwise FALSE. +** +*******************************************************************************/ +//extern +BOOLEAN BTM_BleDataSignature (BD_ADDR bd_addr, UINT8 *p_text, UINT16 len, + BLE_SIGNATURE signature); + +/******************************************************************************* +** +** Function BTM_BleVerifySignature +** +** Description This function is called to verify the data signature +** +** Parameter bd_addr: target device the data to be signed for. +** p_orig: original data before signature. +** len: length of the signing data +** counter: counter used when doing data signing +** p_comp: signature to be compared against. + +** Returns TRUE if signature verified correctly; otherwise FALSE. +** +*******************************************************************************/ +//extern +BOOLEAN BTM_BleVerifySignature (BD_ADDR bd_addr, UINT8 *p_orig, + UINT16 len, UINT32 counter, + UINT8 *p_comp); + +/******************************************************************************* +** +** Function BTM_ReadConnectionAddr +** +** Description This function is called to set the local device random address +** . +** +** Returns void +** +*******************************************************************************/ +//extern +void BTM_ReadConnectionAddr (BD_ADDR remote_bda, BD_ADDR local_conn_addr, + tBLE_ADDR_TYPE *p_addr_type); + + + +/******************************************************************************* +** +** Function BTM_ReadRemoteConnectionAddr +** +** Description This function is read the remote device address currently used +** . +** +** Returns void +** +*******************************************************************************/ +//extern +BOOLEAN BTM_ReadRemoteConnectionAddr(BD_ADDR pseudo_addr, + BD_ADDR conn_addr, + tBLE_ADDR_TYPE *p_addr_type); + +/******************************************************************************* +** +** Function BTM_BleLoadLocalKeys +** +** Description Local local identity key, encryption root or sign counter. +** +** Parameters: key_type: type of key, can be BTM_BLE_KEY_TYPE_ID, BTM_BLE_KEY_TYPE_ER +** or BTM_BLE_KEY_TYPE_COUNTER. +** p_key: pointer to the key. +* +** Returns non2. +** +*******************************************************************************/ +//extern +void BTM_BleLoadLocalKeys(UINT8 key_type, tBTM_BLE_LOCAL_KEYS *p_key); + + +/******************************************************************************* +** +** Function BTM_BleSetBgConnType +** +** Description This function is called to set BLE background connection +** procedure type. It can be auto connection, or selective connection. +** +** Parameters conn_type: it can be auto connection, or selective connection. +** p_select_cback: callback function when selective connection procedure +** is being used. +** +** Returns void +** +*******************************************************************************/ +//extern +BOOLEAN BTM_BleSetBgConnType(tBTM_BLE_CONN_TYPE conn_type, + tBTM_BLE_SEL_CBACK *p_select_cback); + +/******************************************************************************* +** +** Function BTM_BleUpdateBgConnDev +** +** Description This function is called to add or remove a device into/from +** background connection procedure. The background connection +* procedure is decided by the background connection type, it can be +* auto connection, or selective connection. +** +** Parameters add_remove: TRUE to add; FALSE to remove. +** remote_bda: device address to add/remove. +** +** Returns void +** +*******************************************************************************/ +//extern +BOOLEAN BTM_BleUpdateBgConnDev(BOOLEAN add_remove, BD_ADDR remote_bda); + +/******************************************************************************* +** +** Function BTM_BleClearBgConnDev +** +** Description This function is called to clear the whitelist, +** end any pending whitelist connections, +* and reset the local bg device list. +** +** Parameters void +** +** Returns void +** +*******************************************************************************/ +//extern +void BTM_BleClearBgConnDev(void); + +/******************************************************** +** +** Function BTM_BleSetPrefConnParams +** +** Description Set a peripheral's preferred connection parameters. When +** any of the value does not want to be updated while others +** do, use BTM_BLE_CONN_PARAM_UNDEF for the ones want to +** leave untouched. +** +** Parameters: bd_addr - BD address of the peripheral +** min_conn_int - minimum preferred connection interval +** max_conn_int - maximum preferred connection interval +** slave_latency - preferred slave latency +** supervision_tout - preferred supervision timeout +** +** Returns void +** +*******************************************************************************/ +//extern +void BTM_BleSetPrefConnParams (BD_ADDR bd_addr, + UINT16 min_conn_int, UINT16 max_conn_int, + UINT16 slave_latency, UINT16 supervision_tout); + +/****************************************************************************** +** +** Function BTM_BleSetConnScanParams +** +** Description Set scan parameters used in BLE connection request +** +** Parameters: scan_interval - scan interval +** scan_window - scan window +** +** Returns void +** +*******************************************************************************/ +//extern +void BTM_BleSetConnScanParams (UINT32 scan_interval, UINT32 scan_window); + +/****************************************************************************** +** +** Function BTM_BleReadControllerFeatures +** +** Description Reads BLE specific controller features +** +** Parameters: tBTM_BLE_CTRL_FEATURES_CBACK : Callback to notify when features are read +** +** Returns void +** +*******************************************************************************/ +//extern +void BTM_BleReadControllerFeatures(tBTM_BLE_CTRL_FEATURES_CBACK *p_vsc_cback); + +/******************************************************************************* +** +** Function BTM_CheckAdvData +** +** Description This function is called to get ADV data for a specific type. +** +** Parameters p_adv - pointer of ADV data +** type - finding ADV data type +** p_length - return the length of ADV data not including type +** +** Returns pointer of ADV data +** +*******************************************************************************/ +//extern +UINT8 *BTM_CheckAdvData( UINT8 *p_adv, UINT8 type, UINT8 *p_length); + +/******************************************************************************* +** +** Function BTM_BleGetCurrentAddress +** +** Description This function is called to get local used BLE address. +** +** Parameters: None. +** +** Returns success or fail +** +*******************************************************************************/ +BOOLEAN BTM_BleGetCurrentAddress(BD_ADDR addr, uint8_t *addr_type); + +/******************************************************************************* +** +** Function BTM__BLEReadDiscoverability +** +** Description This function is called to read the current LE discoverability +** mode of the device. +** +** Returns BTM_BLE_NON_DISCOVERABLE ,BTM_BLE_LIMITED_DISCOVERABLE or +** BTM_BLE_GENRAL_DISCOVERABLE +** +*******************************************************************************/ +UINT16 BTM_BleReadDiscoverability(void); + +/******************************************************************************* +** +** Function BTM__BLEReadConnectability +** +** Description This function is called to read the current LE connectibility +** mode of the device. +** +** Returns BTM_BLE_NON_CONNECTABLE or BTM_BLE_CONNECTABLE +** +*******************************************************************************/ +//extern +UINT16 BTM_BleReadConnectability (void); + +void BTM_Recovery_Pre_State(void); + +/******************************************************************************* +** +** Function BTM_ReadDevInfo +** +** Description This function is called to read the device/address type +** of BD address. +** +** Parameter remote_bda: remote device address +** p_dev_type: output parameter to read the device type. +** p_addr_type: output parameter to read the address type. +** +*******************************************************************************/ +//extern +void BTM_ReadDevInfo (BD_ADDR remote_bda, tBT_DEVICE_TYPE *p_dev_type, + tBLE_ADDR_TYPE *p_addr_type); + + +/******************************************************************************* +** +** Function BTM_ReadConnectedTransportAddress +** +** Description This function is called to read the paired device/address type of other device paired +** corresponding to the BD_address +** +** Parameter remote_bda: remote device address, carry out the transport address +** transport: active transport +** +** Return TRUE if an active link is identified; FALSE otherwise +** +*******************************************************************************/ +//extern +BOOLEAN BTM_ReadConnectedTransportAddress(BD_ADDR remote_bda, + tBT_TRANSPORT transport); + +/******************************************************************************* +** +** Function BTM_BleBroadcast +** +** Description This function is to start or stop broadcasting. +** +** Parameters start: start or stop broadcasting. +** +** Returns status. +** +*******************************************************************************/ +//extern +tBTM_STATUS BTM_BleBroadcast(BOOLEAN start, tBTM_START_STOP_ADV_CMPL_CBACK *p_stop_adv_cback); + +/******************************************************************************* +** +** Function BTM_BleConfigPrivacy +** +** Description This function is called to enable or disable the privacy in +** the local device. +** +** Parameters enable: TRUE to enable it; FALSE to disable it. +** +** Returns BOOLEAN privacy mode set success; otherwise failed. +** +*******************************************************************************/ +//extern +BOOLEAN BTM_BleConfigPrivacy(BOOLEAN enable, tBTM_SET_LOCAL_PRIVACY_CBACK *set_local_privacy_cabck); + +/******************************************************************************* +** +** Function BTM_BleConfigLocalIcon +** +** Description This function is called to set local icon +** +** Parameters icon: appearance value. +** +** +*******************************************************************************/ +void BTM_BleConfigLocalIcon(uint16_t icon); + +/******************************************************************************* +** +** Function BTM_BleConfigConnParams +** +** Description This function is called to set the connection parameters +** +** Parameters int_min: minimum connection interval +** int_max: maximum connection interval +** latency: slave latency +** timeout: supervision timeout +** +*******************************************************************************/ +void BTM_BleConfigConnParams(uint16_t int_min, uint16_t int_max, uint16_t latency, uint16_t timeout); + +/******************************************************************************* +** +** Function BTM_BleLocalPrivacyEnabled +** +** Description Checks if local device supports private address +** +** Returns Return TRUE if local privacy is enabled else FALSE +** +*******************************************************************************/ +//extern +BOOLEAN BTM_BleLocalPrivacyEnabled(void); + +/******************************************************************************* +** +** Function BTM_BleEnableMixedPrivacyMode +** +** Description This function is called to enabled Mixed mode if privacy 1.2 +** is applicable in controller. +** +** Parameters mixed_on: mixed mode to be used or not. +** +** Returns void +** +*******************************************************************************/ +//extern +void BTM_BleEnableMixedPrivacyMode(BOOLEAN mixed_on); + +/******************************************************************************* +** +** Function BTM_BleMaxMultiAdvInstanceCount +** +** Description Returns max number of multi adv instances supported by controller +** +** Returns Max multi adv instance count +** +*******************************************************************************/ +//extern +UINT8 BTM_BleMaxMultiAdvInstanceCount(void); + +/******************************************************************************* +** +** Function BTM_BleSetConnectableMode +** +** Description This function is called to set BLE connectable mode for a +** peripheral device. +** +** Parameters connectable_mode: directed connectable mode, or non-directed.It can +** be BTM_BLE_CONNECT_EVT, BTM_BLE_CONNECT_DIR_EVT or +** BTM_BLE_CONNECT_LO_DUTY_DIR_EVT +** +** Returns BTM_ILLEGAL_VALUE if controller does not support BLE. +** BTM_SUCCESS is status set successfully; otherwise failure. +** +*******************************************************************************/ +//extern +tBTM_STATUS BTM_BleSetConnectableMode(tBTM_BLE_CONN_MODE connectable_mode); + +/******************************************************************************* +** +** Function BTM_BleTurnOnPrivacyOnRemote +** +** Description This function is called to enable or disable the privacy on the +** remote device. +** +** Parameters bd_addr: remote device address. +** privacy_on: TRUE to enable it; FALSE to disable it. +** +** Returns void +** +*******************************************************************************/ +//extern +void BTM_BleTurnOnPrivacyOnRemote(BD_ADDR bd_addr, + BOOLEAN privacy_on); + +/******************************************************************************* +** +** Function BTM_BleUpdateAdvWhitelist +** +** Description Add or remove device from advertising white list +** +** Returns void +** +*******************************************************************************/ +//extern +BOOLEAN BTM_BleUpdateAdvWhitelist(BOOLEAN add_remove, BD_ADDR emote_bda, tBLE_ADDR_TYPE addr_type, tBTM_UPDATE_WHITELIST_CBACK *update_wl_cb); + +/******************************************************************************* +** +** Function BTM_BleClearWhitelist +** +** Description Clear all white list +** +** Returns void +** +*******************************************************************************/ +void BTM_BleClearWhitelist(tBTM_UPDATE_WHITELIST_CBACK *update_wl_cb); + +/******************************************************************************* +** +** Function BTM_BleUpdateAdvFilterPolicy +** +** Description This function update the filter policy of advertiser. +** +** Parameter adv_policy: advertising filter policy +** +** Return void +*******************************************************************************/ +//extern +void BTM_BleUpdateAdvFilterPolicy(tBTM_BLE_AFP adv_policy); + +/******************************************************************************* +** +** Function BTM_BleReceiverTest +** +** Description This function is called to start the LE Receiver test +** +** Parameter rx_freq - Frequency Range +** p_cmd_cmpl_cback - Command Complete callback +** +*******************************************************************************/ +void BTM_BleReceiverTest(UINT8 rx_freq, tBTM_CMPL_CB *p_cmd_cmpl_cback); + + +/******************************************************************************* +** +** Function BTM_BleTransmitterTest +** +** Description This function is called to start the LE Transmitter test +** +** Parameter tx_freq - Frequency Range +** test_data_len - Length in bytes of payload data in each packet +** packet_payload - Pattern to use in the payload +** p_cmd_cmpl_cback - Command Complete callback +** +*******************************************************************************/ +void BTM_BleTransmitterTest(UINT8 tx_freq, UINT8 test_data_len, + UINT8 packet_payload, tBTM_CMPL_CB *p_cmd_cmpl_cback); + +/******************************************************************************* +** +** Function BTM_BleTestEnd +** +** Description This function is called to stop the in-progress TX or RX test +** +** Parameter p_cmd_cmpl_cback - Command complete callback +** +*******************************************************************************/ +void BTM_BleTestEnd(tBTM_CMPL_CB *p_cmd_cmpl_cback); + +/******************************************************************************* +** +** Function BTM_UseLeLink +** +** Description This function is to select the underneath physical link to use. +** +** Returns TRUE to use LE, FALSE use BR/EDR. +** +*******************************************************************************/ +//extern +BOOLEAN BTM_UseLeLink (BD_ADDR bd_addr); + +/******************************************************************************* +** +** Function BTM_BleStackEnable +** +** Description Enable/Disable BLE functionality on stack regarless controller +** capability. +** +** Parameters: enable: TRUE to enable, FALSE to disable. +** +** Returns TRUE if added OK, else FALSE +** +*******************************************************************************/ +//extern +tBTM_STATUS BTM_BleStackEnable (BOOLEAN enable); + +/******************************************************************************* +** +** Function BTM_GetLeSecurityState +** +** Description This function is called to get security mode 1 flags and +** encryption key size for LE peer. +** +** Returns BOOLEAN TRUE if LE device is found, FALSE otherwise. +** +*******************************************************************************/ +//extern +BOOLEAN BTM_GetLeSecurityState (BD_ADDR bd_addr, + UINT8 *p_le_dev_sec_flags, + UINT8 *p_le_key_size); + +/******************************************************************************* +** +** Function BTM_BleSecurityProcedureIsRunning +** +** Description This function indicates if LE security procedure is +** currently running with the peer. +** +** Returns BOOLEAN TRUE if security procedure is running, FALSE otherwise. +** +*******************************************************************************/ +//extern +BOOLEAN BTM_BleSecurityProcedureIsRunning (BD_ADDR bd_addr); + +/******************************************************************************* +** +** Function BTM_BleGetSupportedKeySize +** +** Description This function gets the maximum encryption key size in bytes +** the local device can suport. +** record. +** +** Returns the key size or 0 if the size can't be retrieved. +** +*******************************************************************************/ +//extern +UINT8 BTM_BleGetSupportedKeySize (BD_ADDR bd_addr); + +/*******************************************************************************/ +/* Multi ADV API */ +/******************************************************************************* +** +** Function BTM_BleEnableAdvInstance +** +** Description This function enable a Multi-ADV instance with the specified +** adv parameters +** +** Parameters p_params: pointer to the adv parameter structure, set as default +** adv parameter when the instance is enabled. +** p_cback: callback function for the adv instance. +** p_ref: reference data attach to the adv instance to be enabled. +** +** Returns status +** +*******************************************************************************/ +//extern +tBTM_STATUS BTM_BleEnableAdvInstance (tBTM_BLE_ADV_PARAMS *p_params, + tBTM_BLE_MULTI_ADV_CBACK *p_cback, + void *p_ref); + +/******************************************************************************* +** +** Function BTM_BleUpdateAdvInstParam +** +** Description This function update a Multi-ADV instance with the specififed +** adv parameters. +** +** Parameters inst_id: adv instance ID +** p_params: pointer to the adv parameter structure. +** +** Returns status +** +*******************************************************************************/ +//extern +tBTM_STATUS BTM_BleUpdateAdvInstParam (UINT8 inst_id, tBTM_BLE_ADV_PARAMS *p_params); + +/******************************************************************************* +** +** Function BTM_BleCfgAdvInstData +** +** Description This function configure a Multi-ADV instance with the specified +** adv data or scan response data. +** +** Parameters inst_id: adv instance ID +** is_scan_rsp: is this scacn response, if no set as adv data. +** data_mask: adv data mask. +** p_data: pointer to the adv data structure. +** +** Returns status +** +*******************************************************************************/ +//extern +tBTM_STATUS BTM_BleCfgAdvInstData (UINT8 inst_id, BOOLEAN is_scan_rsp, + tBTM_BLE_AD_MASK data_mask, + tBTM_BLE_ADV_DATA *p_data); + +/******************************************************************************* +** +** Function BTM_BleDisableAdvInstance +** +** Description This function disable a Multi-ADV instance. +** +** Parameters inst_id: adv instance ID +** +** Returns status +** +*******************************************************************************/ +//extern +tBTM_STATUS BTM_BleDisableAdvInstance (UINT8 inst_id); + +/******************************************************************************* +** +** Function BTM_BleAdvFilterParamSetup +** +** Description This function is called to setup the adv data payload filter +** condition. +** +** Parameters p_target: enabble the filter condition on a target device; if NULL +** enable the generic scan condition. +** enable: enable or disable the filter condition +** +** Returns void +** +*******************************************************************************/ +//extern +tBTM_STATUS BTM_BleAdvFilterParamSetup(int action, + tBTM_BLE_PF_FILT_INDEX filt_index, + tBTM_BLE_PF_FILT_PARAMS *p_filt_params, + tBLE_BD_ADDR *p_target, tBTM_BLE_PF_PARAM_CBACK *p_cmpl_cback, + tBTM_BLE_REF_VALUE ref_value); + +/******************************************************************************* +** +** Function BTM_BleCfgFilterCondition +** +** Description This function is called to configure the adv data payload filter +** condition. +** +** Parameters action: to read/write/clear +** cond_type: filter condition type. +** p_cond: filter condition paramter +** +** Returns tBTM_STATUS +** +*******************************************************************************/ +//extern +tBTM_STATUS BTM_BleCfgFilterCondition(tBTM_BLE_SCAN_COND_OP action, + tBTM_BLE_PF_COND_TYPE cond_type, + tBTM_BLE_PF_FILT_INDEX filt_index, + tBTM_BLE_PF_COND_PARAM *p_cond, + tBTM_BLE_PF_CFG_CBACK *p_cmpl_cback, + tBTM_BLE_REF_VALUE ref_value); + +/******************************************************************************* +** +** Function BTM_BleEnableDisableFilterFeature +** +** Description This function is called to enable or disable the APCF feature +** +** Parameters enable - TRUE - enables the APCF, FALSE - disables the APCF +** ref_value - Ref value +** +** Returns tBTM_STATUS +** +*******************************************************************************/ +//extern +tBTM_STATUS BTM_BleEnableDisableFilterFeature(UINT8 enable, + tBTM_BLE_PF_STATUS_CBACK *p_stat_cback, + tBTM_BLE_REF_VALUE ref_value); + +/******************************************************************************* +** +** Function BTM_BleGetEnergyInfo +** +** Description This function obtains the energy info +** +** Parameters p_ener_cback - Callback pointer +** +** Returns status +** +*******************************************************************************/ +//extern +tBTM_STATUS BTM_BleGetEnergyInfo(tBTM_BLE_ENERGY_INFO_CBACK *p_ener_cback); + +/******************************************************************************* +** +** Function BTM_SetBleDataLength +** +** Description This function is called to set maximum BLE transmission packet size +** +** Returns BTM_SUCCESS if success; otherwise failed. +** +*******************************************************************************/ +//extern +tBTM_STATUS BTM_SetBleDataLength(BD_ADDR bd_addr, UINT16 tx_pdu_length); + +/******************************************************************************* +** +** Function BTM_UpdateBleDuplicateExceptionalList +** +** Description This function is called to update duplicate scan exceptional list. +** +** Parameters: subcode: add, remove or clean duplicate scan exceptional list. +** type: device info type +** device_info: device information +** update_exceptional_list_cmp_cb: complete callback +** +** Returns status +** +*******************************************************************************/ + +tBTM_STATUS BTM_UpdateBleDuplicateExceptionalList(uint8_t subcode, uint32_t type, BD_ADDR device_info, tBTM_UPDATE_DUPLICATE_EXCEPTIONAL_LIST_CMPL_CBACK update_exceptional_list_cmp_cb); + +/******************************************************************************* +** +** Function BTM_GetCurrentConnParams +** +** Description This function is called to read the current connection parameters +** of the device +** +** Returns TRUE or FALSE +** +*******************************************************************************/ + +BOOLEAN BTM_GetCurrentConnParams(BD_ADDR bda, uint16_t *interval, uint16_t *latency, uint16_t *timeout); + +/******************************************************************************* +** +** Function BTM_Ble_Authorization +** +** Description This function is used to authorize a specified device +** +** Returns TRUE or FALSE +** +*******************************************************************************/ +BOOLEAN BTM_Ble_Authorization(BD_ADDR bd_addr, BOOLEAN authorize); + +/******************************************************************************* +** +** Function BTM_BleClearAdv +** +** Description This function is called to clear legacy advertising +** +** Parameter p_clear_adv_cback - Command complete callback +** +*******************************************************************************/ +BOOLEAN BTM_BleClearAdv(tBTM_CLEAR_ADV_CMPL_CBACK *p_clear_adv_cback); +/* +#ifdef __cplusplus +} +#endif +*/ +#if (BLE_50_FEATURE_SUPPORT == TRUE) +void BTM_BleGapRegisterCallback(tBTM_BLE_5_HCI_CBACK cb); + +tBTM_STATUS BTM_BleReadPhy(BD_ADDR bd_addr, UINT8 *tx_phy, UINT8 *rx_phy); + +tBTM_STATUS BTM_BleSetPreferDefaultPhy(UINT8 tx_phy_mask, UINT8 rx_phy_mask); + +tBTM_STATUS BTM_BleSetPreferPhy(BD_ADDR bd_addr, UINT8 all_phys, UINT8 tx_phy_mask, + UINT8 rx_phy_mask, UINT16 phy_options); + +tBTM_STATUS BTM_BleSetExtendedAdvRandaddr(UINT8 instance, BD_ADDR rand_addr); + +tBTM_STATUS BTM_BleSetExtendedAdvParams(UINT8 instance, tBTM_BLE_GAP_EXT_ADV_PARAMS *params); + +tBTM_STATUS BTM_BleConfigExtendedAdvDataRaw(BOOLEAN is_scan_rsp, UINT8 instance, UINT16 len, UINT8 *data); + +tBTM_STATUS BTM_BleStartExtAdv(BOOLEAN enable, UINT8 num, tBTM_BLE_EXT_ADV *ext_adv); + +tBTM_STATUS BTM_BleExtAdvSetRemove(UINT8 instance); + +tBTM_STATUS BTM_BleExtAdvSetClear(void); + +tBTM_STATUS BTM_BlePeriodicAdvSetParams(UINT8 instance, tBTM_BLE_Periodic_Adv_Params *params); + +tBTM_STATUS BTM_BlePeriodicAdvCfgDataRaw(UINT8 instance, UINT16 len, UINT8 *data, BOOLEAN only_update_did); + +tBTM_STATUS BTM_BlePeriodicAdvEnable(UINT8 instance, UINT8 enable); + +tBTM_STATUS BTM_BlePeriodicAdvCreateSync(tBTM_BLE_Periodic_Sync_Params *params); + +tBTM_STATUS BTM_BlePeriodicAdvSyncCancel(void); + +tBTM_STATUS BTM_BlePeriodicAdvSyncTerm(UINT16 sync_handle); + +tBTM_STATUS BTM_BlePeriodicAdvAddDevToList(tBLE_ADDR_TYPE addr_type, BD_ADDR addr, UINT16 sid); + +tBTM_STATUS BTM_BlePeriodicAdvRemoveDevFromList(tBLE_ADDR_TYPE addr_type, BD_ADDR addr, UINT16 sid); + +tBTM_STATUS BTM_BlePeriodicAdvClearDev(void); + +tBTM_STATUS BTM_BleSetExtendedScanParams(tBTM_BLE_EXT_SCAN_PARAMS *params); + +tBTM_STATUS BTM_BleExtendedScan(BOOLEAN enable, UINT16 duration, UINT16 period); + +void BTM_BleSetPreferExtenedConnParams(BD_ADDR bd_addr, tBTM_EXT_CONN_PARAMS *params); + +void BTM_BleEnhancedReceiverTest(UINT8 rx_freq, UINT8 phy, UINT8 modulation_index, tBTM_CMPL_CB *p_cmd_cmpl_cback); + +void BTM_BleEnhancedTransmitterTest(UINT8 tx_freq, UINT8 test_data_len, UINT8 packet_payload, UINT8 phy, tBTM_CMPL_CB *p_cmd_cmpl_cback); + +#endif // #if (BLE_50_FEATURE_SUPPORT == TRUE) + +#if (BLE_FEAT_PERIODIC_ADV_SYNC_TRANSFER == TRUE) +void BTM_BlePeriodicAdvRecvEnable(UINT16 sync_handle, UINT8 enable); + +void BTM_BlePeriodicAdvSyncTrans(BD_ADDR bd_addr, UINT16 service_data, UINT16 sync_handle); + +void BTM_BlePeriodicAdvSetInfoTrans(BD_ADDR bd_addr, UINT16 service_data, UINT8 adv_handle); + +void BTM_BleSetPeriodicAdvSyncTransParams(BD_ADDR bd_addr, UINT8 mode, UINT16 skip, UINT16 sync_timeout, UINT8 cte_type); +#endif // #if (BLE_FEAT_PERIODIC_ADV_SYNC_TRANSFER == TRUE) + +#endif diff --git a/lib/bt/host/bluedroid/stack/include/stack/btu.h b/lib/bt/host/bluedroid/stack/include/stack/btu.h new file mode 100644 index 00000000..8df767b4 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/include/stack/btu.h @@ -0,0 +1,311 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * this file contains the main Bluetooth Upper Layer definitions. The Broadcom + * implementations of L2CAP RFCOMM, SDP and the BTIf run as one GKI task. The + * btu_task switches between them. + * + ******************************************************************************/ + +#ifndef BTU_H +#define BTU_H + +#include "common/bt_target.h" +#include "common/bt_defs.h" +#include "osi/thread.h" + +// HACK(zachoverflow): temporary dark magic +#define BTU_POST_TO_TASK_NO_GOOD_HORRIBLE_HACK 0x1700 // didn't look used in bt_types...here goes nothing +typedef struct { + void (*callback)(BT_HDR *); +} post_to_task_hack_t; + +typedef struct { + void (*callback)(BT_HDR *); + BT_HDR *response; + void *context; +} command_complete_hack_t; + +typedef struct { + void (*callback)(BT_HDR *); + uint8_t status; + BT_HDR *command; + void *context; +} command_status_hack_t; + +/* callbacks +*/ +typedef void (*tBTU_TIMER_CALLBACK)(TIMER_LIST_ENT *p_tle); +typedef void (*tBTU_EVENT_CALLBACK)(BT_HDR *p_hdr); + + +/* Define the timer types maintained by BTU +*/ +#define BTU_TTYPE_BTM_DEV_CTL 1 +#define BTU_TTYPE_L2CAP_LINK 2 +#define BTU_TTYPE_L2CAP_CHNL 3 +#define BTU_TTYPE_L2CAP_HOLD 4 +#define BTU_TTYPE_SDP 5 +#define BTU_TTYPE_BTM_SCO 6 +#define BTU_TTYPE_BTM_ACL 9 +#define BTU_TTYPE_BTM_RMT_NAME 10 +#define BTU_TTYPE_RFCOMM_MFC 11 +#define BTU_TTYPE_RFCOMM_PORT 12 +#define BTU_TTYPE_TCS_L2CAP 13 +#define BTU_TTYPE_TCS_CALL 14 +#define BTU_TTYPE_TCS_WUG 15 +#define BTU_TTYPE_AUTO_SYNC 16 +#define BTU_TTYPE_CTP_RECON 17 +#define BTU_TTYPE_CTP_T100 18 +#define BTU_TTYPE_CTP_GUARD 19 +#define BTU_TTYPE_CTP_DETACH 20 + +#define BTU_TTYPE_SPP_CONN_RETRY 21 +#define BTU_TTYPE_USER_FUNC 22 + +#define BTU_TTYPE_FTP_DISC 25 +#define BTU_TTYPE_OPP_DISC 26 + +#define BTU_TTYPE_CTP_TL_DISCVY 28 +#define BTU_TTYPE_IPFRAG_TIMER 29 +#define BTU_TTYPE_HSP2_AT_CMD_TO 30 +#define BTU_TTYPE_HSP2_REPEAT_RING 31 + +#define BTU_TTYPE_CTP_GW_INIT 32 +#define BTU_TTYPE_CTP_GW_CONN 33 +#define BTU_TTYPE_CTP_GW_IDLE 35 + +#define BTU_TTYPE_ICP_L2CAP 36 +#define BTU_TTYPE_ICP_T100 37 + +#define BTU_TTYPE_HSP2_WAIT_OK 38 + +/* HCRP Timers */ +#define BTU_TTYPE_HCRP_NOTIF_REG 39 +#define BTU_TTYPE_HCRP_PROTO_RSP 40 +#define BTU_TTYPE_HCRP_CR_GRANT 41 +#define BTU_TTYPE_HCRP_CR_CHECK 42 +#define BTU_TTYPE_HCRP_W4_CLOSE 43 + +/* HCRPM Timers */ +#define BTU_TTYPE_HCRPM_NOTIF_REG 44 +#define BTU_TTYPE_HCRPM_NOTIF_KEEP 45 +#define BTU_TTYPE_HCRPM_API_RSP 46 +#define BTU_TTYPE_HCRPM_W4_OPEN 47 +#define BTU_TTYPE_HCRPM_W4_CLOSE 48 + +/* BNEP Timers */ +#define BTU_TTYPE_BNEP 50 + +#define BTU_TTYPE_HSP2_SDP_FAIL_TO 55 +#define BTU_TTYPE_HSP2_SDP_RTRY_TO 56 + +/* BTU internal */ +#define BTU_TTYPE_AVDT_SCB_DELAY_RPT 60 +#define BTU_TTYPE_AVDT_CCB_RET 61 +#define BTU_TTYPE_AVDT_CCB_RSP 62 +#define BTU_TTYPE_AVDT_CCB_IDLE 63 +#define BTU_TTYPE_AVDT_SCB_TC 64 + +#define BTU_TTYPE_HID_DEV_REPAGE_TO 65 +#define BTU_TTYPE_HID_HOST_REPAGE_TO 66 + +#define BTU_TTYPE_HSP2_DELAY_CKPD_RCV 67 + +#define BTU_TTYPE_SAP_TO 68 + +/* BPP Timer */ +#define BTU_TTYPE_BPP_REF_CHNL 72 + +/* LP HC idle Timer */ +#define BTU_TTYPE_LP_HC_IDLE_TO 74 + +/* Patch RAM Timer */ +#define BTU_TTYPE_PATCHRAM_TO 75 + +/* eL2CAP Info Request and other proto cmds timer */ +#define BTU_TTYPE_L2CAP_FCR_ACK 78 +#define BTU_TTYPE_L2CAP_INFO 79 +/* L2CAP update connection parameters timer */ +#define BTU_TTYPE_L2CAP_UPDA_CONN_PARAMS 80 + +#define BTU_TTYPE_MCA_CCB_RSP 98 + +/* BTU internal timer for BLE activity */ +#define BTU_TTYPE_BLE_INQUIRY 99 +#define BTU_TTYPE_BLE_GAP_LIM_DISC 100 +#define BTU_TTYPE_ATT_WAIT_FOR_RSP 101 +#define BTU_TTYPE_SMP_PAIRING_CMD 102 +#define BTU_TTYPE_BLE_RANDOM_ADDR 103 +#define BTU_TTYPE_ATT_WAIT_FOR_APP_RSP 104 +#define BTU_TTYPE_ATT_WAIT_FOR_IND_ACK 105 + +#define BTU_TTYPE_BLE_GAP_FAST_ADV 106 +#define BTU_TTYPE_BLE_OBSERVE 107 + +#define BTU_TTYPE_UCD_TO 108 +#define BTU_TTYPE_BLE_SCAN 109 + +/* BTU internal timer for QOS */ +#define BTU_TTYPE_BTM_QOS 110 + +/* BTU internal timer for set page timeout*/ +#define BTU_TTYPE_BTM_SET_PAGE_TO 111 + +/* BTU Task Signal */ +typedef enum { + SIG_BTU_START_UP = 0, + SIG_BTU_HCI_MSG, + SIG_BTU_BTA_MSG, + SIG_BTU_BTA_ALARM, + SIG_BTU_GENERAL_ALARM, + SIG_BTU_ONESHOT_ALARM, + SIG_BTU_L2CAP_ALARM, + SIG_BTU_HCI_ADV_RPT_MSG, + SIG_BTU_NUM, +} SIG_BTU_t; + +/* This is the inquiry response information held by BTU, and available +** to applications. +*/ +typedef struct { + BD_ADDR remote_bd_addr; + UINT8 page_scan_rep_mode; + UINT8 page_scan_per_mode; + UINT8 page_scan_mode; + DEV_CLASS dev_class; + UINT16 clock_offset; +} tBTU_INQ_INFO; + + + +#define BTU_MAX_REG_TIMER (2) /* max # timer callbacks which may register */ +#define BTU_MAX_REG_EVENT (6) /* max # event callbacks which may register */ +#define BTU_DEFAULT_DATA_SIZE (0x2a0) + +#if (BLE_INCLUDED == TRUE) +#define BTU_DEFAULT_BLE_DATA_SIZE (27) +#endif + +/* structure to hold registered timers */ +typedef struct { + TIMER_LIST_ENT *p_tle; /* timer entry */ + tBTU_TIMER_CALLBACK timer_cb; /* callback triggered when timer expires */ +} tBTU_TIMER_REG; + +/* structure to hold registered event callbacks */ +typedef struct { + UINT16 event_range; /* start of event range */ + tBTU_EVENT_CALLBACK event_cb; /* callback triggered when event is in range */ +} tBTU_EVENT_REG; + +#define NFC_MAX_LOCAL_CTRLS 0 + +/* the index to BTU command queue array */ +#define NFC_CONTROLLER_ID (1) +#define BTU_MAX_LOCAL_CTRLS (1 + NFC_MAX_LOCAL_CTRLS) /* only BR/EDR */ + +/* Define structure holding BTU variables +*/ +typedef struct { + tBTU_TIMER_REG timer_reg[BTU_MAX_REG_TIMER]; + tBTU_EVENT_REG event_reg[BTU_MAX_REG_EVENT]; + + BOOLEAN reset_complete; /* TRUE after first ack from device received */ + UINT8 trace_level; /* Trace level for HCI layer */ +} tBTU_CB; + +/* +#ifdef __cplusplus +extern "C" { +#endif +*/ +/* Global BTU data */ +#if BTU_DYNAMIC_MEMORY == FALSE +extern tBTU_CB btu_cb; +#else +extern tBTU_CB *btu_cb_ptr; +#define btu_cb (*btu_cb_ptr) +#endif + +extern const BD_ADDR BT_BD_ANY; + +/* Functions provided by btu_task.c +************************************ +*/ +void btu_start_timer (TIMER_LIST_ENT *p_tle, UINT16 type, UINT32 timeout); +void btu_stop_timer (TIMER_LIST_ENT *p_tle); +void btu_free_timer (TIMER_LIST_ENT *p_tle); +void btu_start_timer_oneshot(TIMER_LIST_ENT *p_tle, UINT16 type, UINT32 timeout); +void btu_stop_timer_oneshot(TIMER_LIST_ENT *p_tle); + +void btu_uipc_rx_cback(BT_HDR *p_msg); + +/* +** Quick Timer +*/ +#if defined(QUICK_TIMER_TICKS_PER_SEC) && (QUICK_TIMER_TICKS_PER_SEC > 0) +void btu_start_quick_timer (TIMER_LIST_ENT *p_tle, UINT16 type, UINT32 timeout); +void btu_stop_quick_timer (TIMER_LIST_ENT *p_tle); +void btu_free_quick_timer (TIMER_LIST_ENT *p_tle); +void btu_process_quick_timer_evt (void); +#endif + +#if (defined(HCILP_INCLUDED) && HCILP_INCLUDED == TRUE) +void btu_check_bt_sleep (void); +#endif + +/* Functions provided by btu_hcif.c +************************************ +*/ +void btu_hcif_process_event (UINT8 controller_id, BT_HDR *p_buf); +void btu_hcif_send_cmd (UINT8 controller_id, BT_HDR *p_msg); +#if (BLE_50_FEATURE_SUPPORT == TRUE) +UINT8 btu_hcif_send_cmd_sync (UINT8 controller_id, BT_HDR *p_buf); +#endif // #if (BLE_50_FEATURE_SUPPORT == TRUE) +void btu_hcif_send_host_rdy_for_data(void); +void btu_hcif_cmd_timeout (UINT8 controller_id); + +/* Functions provided by btu_core.c +************************************ +*/ +void btu_init_core(void); +void btu_free_core(void); + +void BTU_StartUp(void); +void BTU_ShutDown(void); + +void btu_task_start_up(void *param); +void btu_task_shut_down(void); + +UINT16 BTU_BleAclPktSize(void); + +bool btu_task_post(uint32_t sig, void *param, uint32_t timeout); + +int get_btu_work_queue_size(void); + +osi_thread_t *btu_get_current_thread(void); +/* +#ifdef __cplusplus +} +#endif +*/ + +#endif diff --git a/lib/bt/host/bluedroid/stack/include/stack/dyn_mem.h b/lib/bt/host/bluedroid/stack/include/stack/dyn_mem.h new file mode 100644 index 00000000..fa1ed498 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/include/stack/dyn_mem.h @@ -0,0 +1,234 @@ +/****************************************************************************** + * + * Copyright (C) 2002-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ +#ifndef DYN_MEM_H +#define DYN_MEM_H + +#include "common/bluedroid_user_config.h" + +#if UC_BT_BLE_DYNAMIC_ENV_MEMORY +#define BTU_DYNAMIC_MEMORY TRUE +#define BTM_DYNAMIC_MEMORY TRUE +#define L2C_DYNAMIC_MEMORY TRUE +#define GATT_DYNAMIC_MEMORY TRUE +#define SMP_DYNAMIC_MEMORY TRUE +#define BTA_DYNAMIC_MEMORY TRUE +#define SDP_DYNAMIC_MEMORY TRUE +#define GAP_DYNAMIC_MEMORY TRUE +#define RFC_DYNAMIC_MEMORY TRUE +#define TCS_DYNAMIC_MEMORY TRUE +#define BNEP_DYNAMIC_MEMORY TRUE +#define AVDT_DYNAMIC_MEMORY TRUE +#define AVCT_DYNAMIC_MEMORY TRUE +#define MCA_DYNAMIC_MEMORY TRUE +#define A2D_DYNAMIC_MEMORY TRUE +#define VDP_DYNAMIC_MEMORY TRUE +#define AVRC_DYNAMIC_MEMORY TRUE +#define BIP_DYNAMIC_MEMORY TRUE +#define BPP_DYNAMIC_MEMORY TRUE +#define CTP_DYNAMIC_MEMORY TRUE +#define FTP_DYNAMIC_MEMORY TRUE +#define HCRP_DYNAMIC_MEMORY TRUE +#define HFP_DYNAMIC_MEMORY TRUE +#define HID_DYNAMIC_MEMORY TRUE +#define HSP2_DYNAMIC_MEMORY TRUE +#define ICP_DYNAMIC_MEMORY TRUE +#define OPP_DYNAMIC_MEMORY TRUE +#define PAN_DYNAMIC_MEMORY TRUE +#define SPP_DYNAMIC_MEMORY TRUE +#define SLIP_DYNAMIC_MEMORY TRUE +#define LLCP_DYNAMIC_MEMORY TRUE +#define BTC_SBC_DEC_DYNAMIC_MEMORY TRUE + +#else /* #if UC_BT_BLE_DYNAMIC_ENV_MEMORY */ +#define BTU_DYNAMIC_MEMORY FALSE +#define BTM_DYNAMIC_MEMORY FALSE +#define L2C_DYNAMIC_MEMORY FALSE +#define GATT_DYNAMIC_MEMORY FALSE +#define SMP_DYNAMIC_MEMORY FALSE +#define BTA_DYNAMIC_MEMORY FALSE +#define SDP_DYNAMIC_MEMORY FALSE +#define GAP_DYNAMIC_MEMORY FALSE +#define RFC_DYNAMIC_MEMORY FALSE +#define TCS_DYNAMIC_MEMORY FALSE +#define BNEP_DYNAMIC_MEMORY FALSE +#define AVDT_DYNAMIC_MEMORY FALSE +#define AVCT_DYNAMIC_MEMORY FALSE +#define MCA_DYNAMIC_MEMORY FALSE +#define A2D_DYNAMIC_MEMORY FALSE +#define VDP_DYNAMIC_MEMORY FALSE +#define AVRC_DYNAMIC_MEMORY FALSE +#define BIP_DYNAMIC_MEMORY FALSE +#define BPP_DYNAMIC_MEMORY FALSE +#define CTP_DYNAMIC_MEMORY FALSE +#define FTP_DYNAMIC_MEMORY FALSE +#define HCRP_DYNAMIC_MEMORY FALSE +#define HFP_DYNAMIC_MEMORY FALSE +#define HID_DYNAMIC_MEMORY FALSE +#define HSP2_DYNAMIC_MEMORY FALSE +#define ICP_DYNAMIC_MEMORY FALSE +#define OPP_DYNAMIC_MEMORY FALSE +#define PAN_DYNAMIC_MEMORY FALSE +#define SPP_DYNAMIC_MEMORY FALSE +#define SLIP_DYNAMIC_MEMORY FALSE +#define LLCP_DYNAMIC_MEMORY FALSE +#define BTC_SBC_DEC_DYNAMIC_MEMORY FALSE + +#endif /* #if UC_BT_BLE_DYNAMIC_ENV_MEMORY */ + + +/**************************************************************************** +** Define memory usage for each CORE component (if not defined in bdroid_buildcfg.h) +** The default for each component is to use static memory allocations. +*/ +#ifndef BTU_DYNAMIC_MEMORY +#define BTU_DYNAMIC_MEMORY FALSE +#endif + +#ifndef BTM_DYNAMIC_MEMORY +#define BTM_DYNAMIC_MEMORY FALSE +#endif + +#ifndef SDP_DYNAMIC_MEMORY +#define SDP_DYNAMIC_MEMORY FALSE +#endif + +#ifndef GAP_DYNAMIC_MEMORY +#define GAP_DYNAMIC_MEMORY FALSE +#endif + +#ifndef L2C_DYNAMIC_MEMORY +#define L2C_DYNAMIC_MEMORY FALSE +#endif + +#ifndef RFC_DYNAMIC_MEMORY +#define RFC_DYNAMIC_MEMORY FALSE +#endif + +#ifndef TCS_DYNAMIC_MEMORY +#define TCS_DYNAMIC_MEMORY FALSE +#endif + +#ifndef BNEP_DYNAMIC_MEMORY +#define BNEP_DYNAMIC_MEMORY FALSE +#endif + +#ifndef AVDT_DYNAMIC_MEMORY +#define AVDT_DYNAMIC_MEMORY FALSE +#endif + +#ifndef AVCT_DYNAMIC_MEMORY +#define AVCT_DYNAMIC_MEMORY FALSE +#endif + +#ifndef MCA_DYNAMIC_MEMORY +#define MCA_DYNAMIC_MEMORY FALSE +#endif + +#ifndef GATT_DYNAMIC_MEMORY +#define GATT_DYNAMIC_MEMORY FALSE +#endif + +#ifndef SMP_DYNAMIC_MEMORY +#define SMP_DYNAMIC_MEMORY FALSE +#endif + +/**************************************************************************** +** Define memory usage for each PROFILE component (if not defined in bdroid_buildcfg.h) +** The default for each component is to use static memory allocations. +*/ +#ifndef A2D_DYNAMIC_MEMORY +#define A2D_DYNAMIC_MEMORY FALSE +#endif + +#ifndef VDP_DYNAMIC_MEMORY +#define VDP_DYNAMIC_MEMORY FALSE +#endif + +#ifndef AVRC_DYNAMIC_MEMORY +#define AVRC_DYNAMIC_MEMORY FALSE +#endif + +#ifndef BIP_DYNAMIC_MEMORY +#define BIP_DYNAMIC_MEMORY FALSE +#endif + +#ifndef BPP_DYNAMIC_MEMORY +#define BPP_DYNAMIC_MEMORY FALSE +#endif + +#ifndef CTP_DYNAMIC_MEMORY +#define CTP_DYNAMIC_MEMORY FALSE +#endif + +#ifndef FTP_DYNAMIC_MEMORY +#define FTP_DYNAMIC_MEMORY FALSE +#endif + +#ifndef HCRP_DYNAMIC_MEMORY +#define HCRP_DYNAMIC_MEMORY FALSE +#endif + +#ifndef HFP_DYNAMIC_MEMORY +#define HFP_DYNAMIC_MEMORY FALSE +#endif + +#ifndef HID_DYNAMIC_MEMORY +#define HID_DYNAMIC_MEMORY FALSE +#endif + +#ifndef HSP2_DYNAMIC_MEMORY +#define HSP2_DYNAMIC_MEMORY FALSE +#endif + +#ifndef ICP_DYNAMIC_MEMORY +#define ICP_DYNAMIC_MEMORY FALSE +#endif + +#ifndef OPP_DYNAMIC_MEMORY +#define OPP_DYNAMIC_MEMORY FALSE +#endif + +#ifndef PAN_DYNAMIC_MEMORY +#define PAN_DYNAMIC_MEMORY FALSE +#endif + +#ifndef SPP_DYNAMIC_MEMORY +#define SPP_DYNAMIC_MEMORY FALSE +#endif + +#ifndef SLIP_DYNAMIC_MEMORY +#define SLIP_DYNAMIC_MEMORY FALSE +#endif + +#ifndef LLCP_DYNAMIC_MEMORY +#define LLCP_DYNAMIC_MEMORY FALSE +#endif + +/**************************************************************************** +** Define memory usage for BTA and BTC (if not defined in bdroid_buildcfg.h) +** The default for each component is to use static memory allocations. +*/ +#ifndef BTA_DYNAMIC_MEMORY +#define BTA_DYNAMIC_MEMORY FALSE +#endif + +#ifndef BTC_DYNAMIC_MEMORY +#define BTC_DYNAMIC_MEMORY FALSE +#endif + +#endif /* #ifdef DYN_MEM_H */ diff --git a/lib/bt/host/bluedroid/stack/include/stack/gap_api.h b/lib/bt/host/bluedroid/stack/include/stack/gap_api.h new file mode 100644 index 00000000..62062d2f --- /dev/null +++ b/lib/bt/host/bluedroid/stack/include/stack/gap_api.h @@ -0,0 +1,403 @@ +/****************************************************************************** + * + * Copyright (C) 2009-2013 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +#ifndef GAP_API_H +#define GAP_API_H + +#include "stack/sdpdefs.h" +#include "stack/profiles_api.h" +#include "stack/btm_api.h" +#include "stack/l2c_api.h" + +/***************************************************************************** +** Constants +*****************************************************************************/ +/*** GAP Error and Status Codes ***/ +#define GAP_UNSUPPORTED (GAP_ERR_GRP + 0x01) /* Unsupported call */ +#define GAP_EOINQDB (GAP_ERR_GRP + 0x02) /* End of inquiry database marker */ +#define GAP_ERR_BUSY (GAP_ERR_GRP + 0x03) /* The requested function was busy */ +#define GAP_ERR_NO_CTRL_BLK (GAP_ERR_GRP + 0x04) /* No control blocks available */ +#define GAP_ERR_STARTING_CMD (GAP_ERR_GRP + 0x05) /* Error occurred while initiating the command */ +#define GAP_NO_BDADDR_REC (GAP_ERR_GRP + 0x06) /* No Inquiry DB record for BD_ADDR */ +#define GAP_ERR_ILL_MODE (GAP_ERR_GRP + 0x07) /* An illegal mode parameter was detected */ +#define GAP_ERR_ILL_INQ_TIME (GAP_ERR_GRP + 0x08) /* An illegal time parameter was detected */ +#define GAP_ERR_ILL_PARM (GAP_ERR_GRP + 0x09) /* An illegal parameter was detected */ +#define GAP_ERR_REM_NAME (GAP_ERR_GRP + 0x0a) /* Error starting the remote device name request */ +#define GAP_CMD_INITIATED (GAP_ERR_GRP + 0x0b) /* The GAP command was started (result pending) */ +#define GAP_DEVICE_NOT_UP (GAP_ERR_GRP + 0x0c) /* The device was not up; the request was not executed */ +#define GAP_BAD_BD_ADDR (GAP_ERR_GRP + 0x0d) /* The bd addr passed in was not found or invalid */ + +#define GAP_ERR_BAD_HANDLE (GAP_ERR_GRP + 0x0e) /* Bad GAP handle */ +#define GAP_ERR_BUF_OFFSET (GAP_ERR_GRP + 0x0f) /* Buffer offset invalid */ +#define GAP_ERR_BAD_STATE (GAP_ERR_GRP + 0x10) /* Connection is in invalid state */ +#define GAP_NO_DATA_AVAIL (GAP_ERR_GRP + 0x11) /* No data available */ +#define GAP_ERR_CONGESTED (GAP_ERR_GRP + 0x12) /* BT stack is congested */ +#define GAP_ERR_SECURITY (GAP_ERR_GRP + 0x13) /* Security failed */ + +#define GAP_ERR_PROCESSING (GAP_ERR_GRP + 0x14) /* General error processing BTM request */ +#define GAP_ERR_TIMEOUT (GAP_ERR_GRP + 0x15) /* Timeout occurred while processing cmd */ +#define GAP_EVT_CONN_OPENED 0x0100 +#define GAP_EVT_CONN_CLOSED 0x0101 +#define GAP_EVT_CONN_DATA_AVAIL 0x0102 +#define GAP_EVT_CONN_CONGESTED 0x0103 +#define GAP_EVT_CONN_UNCONGESTED 0x0104 +/* Values for 'chan_mode_mask' field */ +/* GAP_ConnOpen() - optional channels to negotiate */ +#define GAP_FCR_CHAN_OPT_BASIC L2CAP_FCR_CHAN_OPT_BASIC +#define GAP_FCR_CHAN_OPT_ERTM L2CAP_FCR_CHAN_OPT_ERTM +#define GAP_FCR_CHAN_OPT_STREAM L2CAP_FCR_CHAN_OPT_STREAM +/*** used in connection variables and functions ***/ +#define GAP_INVALID_HANDLE 0xFFFF + +/* This is used to change the criteria for AMP */ +#define GAP_PROTOCOL_ID (UUID_PROTOCOL_UDP) + + +#ifndef GAP_PREFER_CONN_INT_MAX +#define GAP_PREFER_CONN_INT_MAX BTM_BLE_CONN_INT_MIN +#endif + +#ifndef GAP_PREFER_CONN_INT_MIN +#define GAP_PREFER_CONN_INT_MIN BTM_BLE_CONN_INT_MIN +#endif + +#ifndef GAP_PREFER_CONN_LATENCY +#define GAP_PREFER_CONN_LATENCY 0 +#endif + +#ifndef GAP_PREFER_CONN_SP_TOUT +#define GAP_PREFER_CONN_SP_TOUT 2000 +#endif + +/***************************************************************************** +** Type Definitions +*****************************************************************************/ +/* +** Callback function for connection services +*/ +typedef void (tGAP_CONN_CALLBACK) (UINT16 gap_handle, UINT16 event); + +/* +** Define the callback function prototypes. Parameters are specific +** to each event and are described below +*/ +typedef void (tGAP_CALLBACK) (UINT16 event, void *p_data); + + +/* Definition of the GAP_FindAddrByName results structure */ +typedef struct { + UINT16 status; + BD_ADDR bd_addr; + tBTM_BD_NAME devname; +} tGAP_FINDADDR_RESULTS; + +typedef struct { + UINT16 int_min; + UINT16 int_max; + UINT16 latency; + UINT16 sp_tout; +} tGAP_BLE_PREF_PARAM; + +typedef union { + tGAP_BLE_PREF_PARAM conn_param; + BD_ADDR reconn_bda; + UINT16 icon; + UINT8 *p_dev_name; + UINT8 addr_resolution; + +} tGAP_BLE_ATTR_VALUE; + +typedef void (tGAP_BLE_CMPL_CBACK)(BOOLEAN status, BD_ADDR addr, UINT16 length, char *p_name); + + +/***************************************************************************** +** External Function Declarations +*****************************************************************************/ + +/*** Functions for L2CAP connection interface ***/ + +/******************************************************************************* +** +** Function GAP_ConnOpen +** +** Description This function is called to open a generic L2CAP connection. +** +** Returns handle of the connection if successful, else GAP_INVALID_HANDLE +** +*******************************************************************************/ +extern UINT16 GAP_ConnOpen (const char *p_serv_name, UINT8 service_id, BOOLEAN is_server, + BD_ADDR p_rem_bda, UINT16 psm, tL2CAP_CFG_INFO *p_cfg, + tL2CAP_ERTM_INFO *ertm_info, + UINT16 security, UINT8 chan_mode_mask, tGAP_CONN_CALLBACK *p_cb); + +/******************************************************************************* +** +** Function GAP_ConnClose +** +** Description This function is called to close a connection. +** +** Returns BT_PASS - closed OK +** GAP_ERR_BAD_HANDLE - invalid handle +** +*******************************************************************************/ +extern UINT16 GAP_ConnClose (UINT16 gap_handle); + +/******************************************************************************* +** +** Function GAP_ConnReadData +** +** Description GKI buffer unaware application will call this function +** after receiving GAP_EVT_RXDATA event. A data copy is made +** into the receive buffer parameter. +** +** Returns BT_PASS - data read +** GAP_ERR_BAD_HANDLE - invalid handle +** GAP_NO_DATA_AVAIL - no data available +** +*******************************************************************************/ +extern UINT16 GAP_ConnReadData (UINT16 gap_handle, UINT8 *p_data, + UINT16 max_len, UINT16 *p_len); + +/******************************************************************************* +** +** Function GAP_GetRxQueueCnt +** +** Description This function return number of bytes on the rx queue. +** +** Parameters: handle - Handle returned in the GAP_ConnOpen +** p_rx_queue_count - Pointer to return queue count in. +** +** +*******************************************************************************/ +extern int GAP_GetRxQueueCnt (UINT16 handle, UINT32 *p_rx_queue_count); + +/******************************************************************************* +** +** Function GAP_ConnBTRead +** +** Description GKI buffer aware applications will call this function after +** receiving an GAP_EVT_RXDATA event to process the incoming +** data buffer. +** +** Returns BT_PASS - data read +** GAP_ERR_BAD_HANDLE - invalid handle +** GAP_NO_DATA_AVAIL - no data available +** +*******************************************************************************/ +extern UINT16 GAP_ConnBTRead (UINT16 gap_handle, BT_HDR **pp_buf); + +/******************************************************************************* +** +** Function GAP_ConnBTWrite +** +** Description GKI buffer aware applications can call this function to write data +** by passing a pointer to the GKI buffer of data. +** +** Returns BT_PASS - data read +** GAP_ERR_BAD_HANDLE - invalid handle +** GAP_ERR_BAD_STATE - connection not established +** GAP_INVALID_BUF_OFFSET - buffer offset is invalid +*******************************************************************************/ +extern UINT16 GAP_ConnBTWrite (UINT16 gap_handle, BT_HDR *p_buf); + +/******************************************************************************* +** +** Function GAP_ConnWriteData +** +** Description GKI buffer unaware application will call this function +** to send data to the connection. A data copy is made into a GKI +** buffer. +** +** Returns BT_PASS - data read +** GAP_ERR_BAD_HANDLE - invalid handle +** GAP_ERR_BAD_STATE - connection not established +** GAP_CONGESTION - system is congested +** +*******************************************************************************/ +extern UINT16 GAP_ConnWriteData (UINT16 gap_handle, UINT8 *p_data, + UINT16 max_len, UINT16 *p_len); + +/******************************************************************************* +** +** Function GAP_ConnReconfig +** +** Description Applications can call this function to reconfigure the connection. +** +** Returns BT_PASS - config process started +** GAP_ERR_BAD_HANDLE - invalid handle +** +*******************************************************************************/ +extern UINT16 GAP_ConnReconfig (UINT16 gap_handle, tL2CAP_CFG_INFO *p_cfg); + +/******************************************************************************* +** +** Function GAP_ConnSetIdleTimeout +** +** Description Higher layers call this function to set the idle timeout for +** a connection, or for all future connections. The "idle timeout" +** is the amount of time that a connection can remain up with +** no L2CAP channels on it. A timeout of zero means that the +** connection will be torn down immediately when the last channel +** is removed. A timeout of 0xFFFF means no timeout. Values are +** in seconds. +** +** Returns BT_PASS - config process started +** GAP_ERR_BAD_HANDLE - invalid handle +** +*******************************************************************************/ +extern UINT16 GAP_ConnSetIdleTimeout (UINT16 gap_handle, UINT16 timeout); + +/******************************************************************************* +** +** Function GAP_ConnGetRemoteAddr +** +** Description This function is called to get the remote BD address +** of a connection. +** +** Returns BT_PASS - closed OK +** GAP_ERR_BAD_HANDLE - invalid handle +** +*******************************************************************************/ +extern UINT8 *GAP_ConnGetRemoteAddr (UINT16 gap_handle); + +/******************************************************************************* +** +** Function GAP_ConnGetRemMtuSize +** +** Description Returns the remote device's MTU size. +** +** Returns UINT16 - maximum size buffer that can be transmitted to the peer +** +*******************************************************************************/ +extern UINT16 GAP_ConnGetRemMtuSize (UINT16 gap_handle); + +/******************************************************************************* +** +** Function GAP_ConnGetL2CAPCid +** +** Description Returns the L2CAP channel id +** +** Parameters: handle - Handle of the connection +** +** Returns UINT16 - The L2CAP channel id +** 0, if error +** +*******************************************************************************/ +extern UINT16 GAP_ConnGetL2CAPCid (UINT16 gap_handle); + +/******************************************************************************* +** +** Function GAP_SetTraceLevel +** +** Description This function sets the trace level for GAP. If called with +** a value of 0xFF, it simply returns the current trace level. +** +** Returns The new or current trace level +** +*******************************************************************************/ +extern UINT8 GAP_SetTraceLevel (UINT8 new_level); + +/******************************************************************************* +** +** Function GAP_Init +** +** Description Initializes the control blocks used by GAP. +** This routine should not be called except once per +** stack invocation. +** +** Returns status +** +*******************************************************************************/ +extern bt_status_t GAP_Init(void); + +/******************************************************************************* +** +** Function GAP_Deinit +** +** Description This function is called to deinitialize the control block +** for this layer. +** +** Returns void +** +*******************************************************************************/ +extern void GAP_Deinit(void); + +#if (BLE_INCLUDED == TRUE) +/******************************************************************************* +** +** Function GAP_BleAttrDBUpdate +** +** Description update GAP local BLE attribute database. +** +** Returns Nothing +** +*******************************************************************************/ +extern void GAP_BleAttrDBUpdate(UINT16 attr_uuid, tGAP_BLE_ATTR_VALUE *p_value); + + +/******************************************************************************* +** +** Function GAP_BleReadPeerPrefConnParams +** +** Description Start a process to read a connected peripheral's preferred +** connection parameters +** +** Returns TRUE if read started, else FALSE if GAP is busy +** +*******************************************************************************/ +extern BOOLEAN GAP_BleReadPeerPrefConnParams (BD_ADDR peer_bda); + +/******************************************************************************* +** +** Function GAP_BleReadPeerDevName +** +** Description Start a process to read a connected peripheral's device name. +** +** Returns TRUE if request accepted +** +*******************************************************************************/ +extern BOOLEAN GAP_BleReadPeerDevName (BD_ADDR peer_bda, tGAP_BLE_CMPL_CBACK *p_cback); + + +/******************************************************************************* +** +** Function GAP_BleReadPeerAddressResolutionCap +** +** Description Start a process to read peer address resolution capability +** +** Returns TRUE if request accepted +** +*******************************************************************************/ +extern BOOLEAN GAP_BleReadPeerAddressResolutionCap (BD_ADDR peer_bda, + tGAP_BLE_CMPL_CBACK *p_cback); + +/******************************************************************************* +** +** Function GAP_BleCancelReadPeerDevName +** +** Description Cancel reading a peripheral's device name. +** +** Returns TRUE if request accepted +** +*******************************************************************************/ +extern BOOLEAN GAP_BleCancelReadPeerDevName (BD_ADDR peer_bda); + + +#endif + +#endif /* GAP_API_H */ diff --git a/lib/bt/host/bluedroid/stack/include/stack/gatt_api.h b/lib/bt/host/bluedroid/stack/include/stack/gatt_api.h new file mode 100644 index 00000000..7872e139 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/include/stack/gatt_api.h @@ -0,0 +1,1298 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ +#ifndef GATT_API_H +#define GATT_API_H + +#include "common/bt_target.h" +#include "stack/gattdefs.h" +#include "stack/btm_ble_api.h" + +/***************************************************************************** +** Constants +*****************************************************************************/ +/* Success code and error codes */ +#define GATT_SUCCESS 0x00 +#define GATT_INVALID_HANDLE 0x01 +#define GATT_READ_NOT_PERMIT 0x02 +#define GATT_WRITE_NOT_PERMIT 0x03 +#define GATT_INVALID_PDU 0x04 +#define GATT_INSUF_AUTHENTICATION 0x05 +#define GATT_REQ_NOT_SUPPORTED 0x06 +#define GATT_INVALID_OFFSET 0x07 +#define GATT_INSUF_AUTHORIZATION 0x08 +#define GATT_PREPARE_Q_FULL 0x09 +#define GATT_NOT_FOUND 0x0a +#define GATT_NOT_LONG 0x0b +#define GATT_INSUF_KEY_SIZE 0x0c +#define GATT_INVALID_ATTR_LEN 0x0d +#define GATT_ERR_UNLIKELY 0x0e +#define GATT_INSUF_ENCRYPTION 0x0f +#define GATT_UNSUPPORT_GRP_TYPE 0x10 +#define GATT_INSUF_RESOURCE 0x11 +#define GATT_DATABASE_OUT_OF_SYNC 0x12 +#define GATT_VALUE_NOT_ALLOWED 0x13 + + +#define GATT_NO_RESOURCES 0x80 +#define GATT_INTERNAL_ERROR 0x81 +#define GATT_WRONG_STATE 0x82 +#define GATT_DB_FULL 0x83 +#define GATT_BUSY 0x84 +#define GATT_ERROR 0x85 +#define GATT_CMD_STARTED 0x86 +#define GATT_ILLEGAL_PARAMETER 0x87 +#define GATT_PENDING 0x88 +#define GATT_AUTH_FAIL 0x89 +#define GATT_MORE 0x8a +#define GATT_INVALID_CFG 0x8b +#define GATT_SERVICE_STARTED 0x8c +#define GATT_ENCRYPED_MITM GATT_SUCCESS +#define GATT_ENCRYPED_NO_MITM 0x8d +#define GATT_NOT_ENCRYPTED 0x8e +#define GATT_CONGESTED 0x8f + +#define GATT_DUP_REG 0x90 +#define GATT_ALREADY_OPEN 0x91 +#define GATT_CANCEL 0x92 + +/* 0xE0 ~ 0xFC reserved for future use */ +#define GATT_STACK_RSP 0xE0 +#define GATT_APP_RSP 0xE1 +//Error caused by customer application or stack bug +#define GATT_UNKNOWN_ERROR 0XEF + +#define GATT_CCC_CFG_ERR 0xFD /* Client Characteristic Configuration Descriptor Improperly Configured */ +#define GATT_PRC_IN_PROGRESS 0xFE /* Procedure Already in progress */ +#define GATT_OUT_OF_RANGE 0xFF /* Attribute value out of range */ + +typedef UINT8 tGATT_STATUS; + + +#define GATT_RSP_ERROR 0x01 +#define GATT_REQ_MTU 0x02 +#define GATT_RSP_MTU 0x03 +#define GATT_REQ_FIND_INFO 0x04 +#define GATT_RSP_FIND_INFO 0x05 +#define GATT_REQ_FIND_TYPE_VALUE 0x06 +#define GATT_RSP_FIND_TYPE_VALUE 0x07 +#define GATT_REQ_READ_BY_TYPE 0x08 +#define GATT_RSP_READ_BY_TYPE 0x09 +#define GATT_REQ_READ 0x0A +#define GATT_RSP_READ 0x0B +#define GATT_REQ_READ_BLOB 0x0C +#define GATT_RSP_READ_BLOB 0x0D +#define GATT_REQ_READ_MULTI 0x0E +#define GATT_RSP_READ_MULTI 0x0F +#define GATT_REQ_READ_BY_GRP_TYPE 0x10 +#define GATT_RSP_READ_BY_GRP_TYPE 0x11 +#define GATT_REQ_WRITE 0x12 /* 0001-0010 (write)*/ +#define GATT_RSP_WRITE 0x13 +#define GATT_CMD_WRITE 0x52 /* changed in V4.0 01001-0010(write cmd)*/ +#define GATT_REQ_PREPARE_WRITE 0x16 +#define GATT_RSP_PREPARE_WRITE 0x17 +#define GATT_REQ_EXEC_WRITE 0x18 +#define GATT_RSP_EXEC_WRITE 0x19 +#define GATT_HANDLE_VALUE_NOTIF 0x1B +#define GATT_HANDLE_VALUE_IND 0x1D +#define GATT_HANDLE_VALUE_CONF 0x1E +#define GATT_REQ_READ_MULTI_VAR 0x20 +#define GATT_RSP_READ_MULTI_VAR 0x21 +#define GATT_HANDLE_MULTI_VALUE_NOTIF 0x23 +#define GATT_SIGN_CMD_WRITE 0xD2 /* changed in V4.0 1101-0010 (signed write) see write cmd above*/ +#define GATT_OP_CODE_MAX GATT_HANDLE_MULTI_VALUE_NOTIF + 1 /* 0x1E = 30 + 1 = 31*/ + +#define GATT_COMMAND_FLAG 0x40 /* Command Flag: set to one means command */ + +#define GATT_HANDLE_IS_VALID(x) ((x) != 0) + +#define GATT_CONN_UNKNOWN 0 +#define GATT_CONN_L2C_FAILURE 1 /* general L2cap failure */ +#define GATT_CONN_TIMEOUT HCI_ERR_CONNECTION_TOUT /* 0x08 connection timeout */ +#define GATT_CONN_TERMINATE_PEER_USER HCI_ERR_PEER_USER /* 0x13 connection terminate by peer user */ +#define GATT_CONN_TERMINATE_LOCAL_HOST HCI_ERR_CONN_CAUSE_LOCAL_HOST /* 0x16 connectionterminated by local host */ +#define GATT_CONN_FAIL_ESTABLISH HCI_ERR_CONN_FAILED_ESTABLISHMENT/* 0x03E connection fail to establish */ +#define GATT_CONN_LMP_TIMEOUT HCI_ERR_LMP_RESPONSE_TIMEOUT /* 0x22 connection fail for LMP response tout */ +#define GATT_CONN_CANCEL L2CAP_CONN_CANCEL /* 0x0100 L2CAP connection cancelled */ +typedef UINT16 tGATT_DISCONN_REASON; + +/* MAX GATT MTU size +*/ +#ifndef GATT_MAX_MTU_SIZE +#define GATT_MAX_MTU_SIZE 517 +#endif + +/* max length of an attribute value +*/ +#ifndef GATT_MAX_ATTR_LEN +#define GATT_MAX_ATTR_LEN 600 +#endif + +/* default GATT MTU size over LE link +*/ +#define GATT_DEF_BLE_MTU_SIZE 23 + +/* invalid connection ID +*/ +#define GATT_INVALID_CONN_ID 0xFFFF + +#ifndef GATT_CL_MAX_LCB +#define GATT_CL_MAX_LCB 12 // 22 +#endif + +#ifndef GATT_MAX_SCCB +#define GATT_MAX_SCCB 10 +#endif + + +/* GATT notification caching timer, default to be three seconds +*/ +#ifndef GATTC_NOTIF_TIMEOUT +#define GATTC_NOTIF_TIMEOUT 3 +#endif + +/***************************************************************************** +** GATT Structure Definition +*****************************************************************************/ + +/* Attribute permissions +*/ +#define GATT_PERM_READ (1 << 0) /* bit 0 */ +#define GATT_PERM_READ_ENCRYPTED (1 << 1) /* bit 1 */ +#define GATT_PERM_READ_ENC_MITM (1 << 2) /* bit 2 */ +#define GATT_PERM_WRITE (1 << 4) /* bit 4 */ +#define GATT_PERM_WRITE_ENCRYPTED (1 << 5) /* bit 5 */ +#define GATT_PERM_WRITE_ENC_MITM (1 << 6) /* bit 6 */ +#define GATT_PERM_WRITE_SIGNED (1 << 7) /* bit 7 */ +#define GATT_PERM_WRITE_SIGNED_MITM (1 << 8) /* bit 8 */ +#define GATT_PERM_READ_AUTHORIZATION (1 << 9) /* bit 9 */ +#define GATT_PERM_WRITE_AUTHORIZATION (1 << 10)/* bit 10 */ +typedef UINT16 tGATT_PERM; + +#define GATT_ENCRYPT_KEY_SIZE_MASK (0xF000) /* the MS nibble of tGATT_PERM; key size 7=0; size 16=9 */ + +#define GATT_READ_ALLOWED (GATT_PERM_READ | GATT_PERM_READ_ENCRYPTED | GATT_PERM_READ_ENC_MITM | GATT_PERM_READ_AUTHORIZATION) +#define GATT_READ_AUTH_REQUIRED (GATT_PERM_READ_ENCRYPTED) +#define GATT_READ_MITM_REQUIRED (GATT_PERM_READ_ENC_MITM) +#define GATT_READ_ENCRYPTED_REQUIRED (GATT_PERM_READ_ENCRYPTED | GATT_PERM_READ_ENC_MITM) +#define GATT_READ_AUTHORIZATION (GATT_PERM_READ_AUTHORIZATION) + + +#define GATT_WRITE_ALLOWED (GATT_PERM_WRITE | GATT_PERM_WRITE_ENCRYPTED | GATT_PERM_WRITE_ENC_MITM | \ + GATT_PERM_WRITE_SIGNED | GATT_PERM_WRITE_SIGNED_MITM | GATT_PERM_WRITE_AUTHORIZATION) + +#define GATT_WRITE_AUTH_REQUIRED (GATT_PERM_WRITE_ENCRYPTED | GATT_PERM_WRITE_SIGNED) + +#define GATT_WRITE_MITM_REQUIRED (GATT_PERM_WRITE_ENC_MITM | GATT_PERM_WRITE_SIGNED_MITM) + +#define GATT_WRITE_ENCRYPTED_PERM (GATT_PERM_WRITE_ENCRYPTED | GATT_PERM_WRITE_ENC_MITM) + +#define GATT_WRITE_SIGNED_PERM (GATT_PERM_WRITE_SIGNED | GATT_PERM_WRITE_SIGNED_MITM) + +#define GATT_WRITE_AUTHORIZATION (GATT_PERM_WRITE_AUTHORIZATION) + + +/* Characteristic properties +*/ +#define GATT_CHAR_PROP_BIT_BROADCAST (1 << 0) +#define GATT_CHAR_PROP_BIT_READ (1 << 1) +#define GATT_CHAR_PROP_BIT_WRITE_NR (1 << 2) +#define GATT_CHAR_PROP_BIT_WRITE (1 << 3) +#define GATT_CHAR_PROP_BIT_NOTIFY (1 << 4) +#define GATT_CHAR_PROP_BIT_INDICATE (1 << 5) +#define GATT_CHAR_PROP_BIT_AUTH (1 << 6) +#define GATT_CHAR_PROP_BIT_EXT_PROP (1 << 7) +typedef UINT8 tGATT_CHAR_PROP; + + +/* Format of the value of a characteristic. enumeration type +*/ +enum { + GATT_FORMAT_RES, /* rfu */ + GATT_FORMAT_BOOL, /* 0x01 boolean */ + GATT_FORMAT_2BITS, /* 0x02 2 bit */ + GATT_FORMAT_NIBBLE, /* 0x03 nibble */ + GATT_FORMAT_UINT8, /* 0x04 uint8 */ + GATT_FORMAT_UINT12, /* 0x05 uint12 */ + GATT_FORMAT_UINT16, /* 0x06 uint16 */ + GATT_FORMAT_UINT24, /* 0x07 uint24 */ + GATT_FORMAT_UINT32, /* 0x08 uint32 */ + GATT_FORMAT_UINT48, /* 0x09 uint48 */ + GATT_FORMAT_UINT64, /* 0x0a uint64 */ + GATT_FORMAT_UINT128, /* 0x0B uint128 */ + GATT_FORMAT_SINT8, /* 0x0C signed 8 bit integer */ + GATT_FORMAT_SINT12, /* 0x0D signed 12 bit integer */ + GATT_FORMAT_SINT16, /* 0x0E signed 16 bit integer */ + GATT_FORMAT_SINT24, /* 0x0F signed 24 bit integer */ + GATT_FORMAT_SINT32, /* 0x10 signed 32 bit integer */ + GATT_FORMAT_SINT48, /* 0x11 signed 48 bit integer */ + GATT_FORMAT_SINT64, /* 0x12 signed 64 bit integer */ + GATT_FORMAT_SINT128, /* 0x13 signed 128 bit integer */ + GATT_FORMAT_FLOAT32, /* 0x14 float 32 */ + GATT_FORMAT_FLOAT64, /* 0x15 float 64*/ + GATT_FORMAT_SFLOAT, /* 0x16 IEEE-11073 16 bit SFLOAT */ + GATT_FORMAT_FLOAT, /* 0x17 IEEE-11073 32 bit SFLOAT */ + GATT_FORMAT_DUINT16, /* 0x18 IEEE-20601 format */ + GATT_FORMAT_UTF8S, /* 0x19 UTF-8 string */ + GATT_FORMAT_UTF16S, /* 0x1a UTF-16 string */ + GATT_FORMAT_STRUCT, /* 0x1b Opaque structure*/ + GATT_FORMAT_MAX /* 0x1c or above reserved */ +}; +typedef UINT8 tGATT_FORMAT; + +/* Characteristic Presentation Format Descriptor value +*/ +typedef struct { + UINT16 unit; /* as UUID defined by SIG */ + UINT16 descr; /* as UUID as defined by SIG */ + tGATT_FORMAT format; + INT8 exp; + UINT8 name_spc; /* The name space of the description */ +} tGATT_CHAR_PRES; + +/* Characteristic Report reference Descriptor format +*/ +typedef struct { + UINT8 rpt_id; /* report ID */ + UINT8 rpt_type; /* report type */ +} tGATT_CHAR_RPT_REF; + + +#define GATT_VALID_RANGE_MAX_SIZE 16 +typedef struct { + UINT8 format; + UINT16 len; + UINT8 lower_range[GATT_VALID_RANGE_MAX_SIZE]; /* in little endian format */ + UINT8 upper_range[GATT_VALID_RANGE_MAX_SIZE]; +} tGATT_VALID_RANGE; + +/* Characteristic Aggregate Format attribute value +*/ +#define GATT_AGGR_HANDLE_NUM_MAX 10 +typedef struct { + UINT8 num_handle; + UINT16 handle_list[GATT_AGGR_HANDLE_NUM_MAX]; +} tGATT_CHAR_AGGRE; + +/* Characteristic descriptor: Extended Properties value +*/ +#define GATT_CHAR_BIT_REL_WRITE 0x0001 /* permits reliable writes of the Characteristic Value */ +#define GATT_CHAR_BIT_WRITE_AUX 0x0002 /* permits writes to the characteristic descriptor */ + + +/* characteristic descriptor: client configuration value +*/ +#define GATT_CLT_CONFIG_NONE 0x0000 +#define GATT_CLT_CONFIG_NOTIFICATION 0x0001 +#define GATT_CLT_CONFIG_INDICATION 0x0002 +typedef UINT16 tGATT_CLT_CHAR_CONFIG; + + +/* characteristic descriptor: server configuration value +*/ +#define GATT_SVR_CONFIG_NONE 0x0000 +#define GATT_SVR_CONFIG_BROADCAST 0x0001 +typedef UINT16 tGATT_SVR_CHAR_CONFIG; + +/* Characteristic descriptor: Extended Properties value +*/ +#define GATT_CHAR_BIT_REL_WRITE 0x0001 /* permits reliable writes of the Characteristic Value */ +#define GATT_CHAR_BIT_WRITE_AUX 0x0002 /* permits writes to the characteristic descriptor */ + +/* authentication requirement +*/ +#define GATT_AUTH_REQ_NONE 0 +#define GATT_AUTH_REQ_NO_MITM 1 /* unauthenticated encryption */ +#define GATT_AUTH_REQ_MITM 2 /* authenticated encryption */ +#define GATT_AUTH_REQ_SIGNED_NO_MITM 3 +#define GATT_AUTH_REQ_SIGNED_MITM 4 +typedef UINT8 tGATT_AUTH_REQ; + +/* Attribute Value structure +*/ +typedef struct { + UINT16 conn_id; + UINT16 handle; /* attribute handle */ + UINT16 offset; /* attribute value offset, if no offset is needed for the command, ignore it */ + UINT16 len; /* length of attribute value */ + tGATT_AUTH_REQ auth_req; /* authentication request */ + UINT8 value[GATT_MAX_ATTR_LEN]; /* the actual attribute value */ +} tGATT_VALUE; + +typedef struct{ + UINT16 attr_max_len; + UINT16 attr_len; + UINT8 *attr_val; +}tGATT_ATTR_VAL; + +typedef struct{ + uint8_t auto_rsp; +}tGATTS_ATTR_CONTROL; + +/* Mask for gatt server attribute */ +#define GATT_ATTR_VALUE_ALLOCATED 0x01 +typedef UINT8 tGATT_ATTR_MASK; + +/* Union of the event data which is used in the server respond API to carry the server response information +*/ +typedef union { + /* data type member event */ + tGATT_VALUE attr_value; /* READ, HANDLE_VALUE_IND, PREPARE_WRITE */ + /* READ_BLOB, READ_BY_TYPE */ + UINT16 handle; /* WRITE, WRITE_BLOB */ + +} tGATTS_RSP; + +/* Transports for the primary service */ +#define GATT_TRANSPORT_LE BT_TRANSPORT_LE +#define GATT_TRANSPORT_BR_EDR BT_TRANSPORT_BR_EDR +#define GATT_TRANSPORT_LE_BR_EDR (BT_TRANSPORT_LE|BT_TRANSPORT_BR_EDR) +typedef UINT8 tGATT_TRANSPORT; + +#define GATT_PREP_WRITE_CANCEL 0x00 +#define GATT_PREP_WRITE_EXEC 0x01 +typedef UINT8 tGATT_EXEC_FLAG; + +/* read request always based on UUID */ +typedef struct { + UINT16 handle; + UINT16 offset; + BOOLEAN is_long; + BOOLEAN need_rsp; +} tGATT_READ_REQ; + +/* write request data */ +typedef struct { + UINT16 handle; /* attribute handle */ + UINT16 offset; /* attribute value offset, if no offset is needed for the command, ignore it */ + UINT16 len; /* length of attribute value */ + UINT8 value[GATT_MAX_ATTR_LEN]; /* the actual attribute value */ + BOOLEAN need_rsp; /* need write response */ + BOOLEAN is_prep; /* is prepare write */ +} tGATT_WRITE_REQ; + +/* callback data for server access request from client */ +typedef union { + tGATT_READ_REQ read_req; /* read request, read by Type, read blob */ + + tGATT_WRITE_REQ write_req; /* write */ + /* prepare write */ + /* write blob */ + UINT16 handle; /* handle value confirmation */ + UINT16 mtu; /* MTU exchange request */ + tGATT_EXEC_FLAG exec_write; /* execute write */ +} tGATTS_DATA; + +typedef UINT8 tGATT_SERV_IF; /* GATT Service Interface */ + +enum { + GATTS_REQ_TYPE_READ = 1, /* Attribute read request */ + GATTS_REQ_TYPE_WRITE, /* Attribute write request */ + GATTS_REQ_TYPE_WRITE_EXEC, /* Execute write */ + GATTS_REQ_TYPE_MTU, /* MTU exchange information */ + GATTS_REQ_TYPE_CONF /* handle value confirmation */ +}; +typedef UINT8 tGATTS_REQ_TYPE; + + + +/* Client Used Data Structure +*/ +/* definition of different discovery types */ +enum { + GATT_DISC_SRVC_ALL = 1, /* discover all services */ + GATT_DISC_SRVC_BY_UUID, /* discover service of a special type */ + GATT_DISC_INC_SRVC, /* discover the included service within a service */ + GATT_DISC_CHAR, /* discover characteristics of a service with/without type requirement */ + GATT_DISC_CHAR_BY_UUID, /* discover characteristic with type requirement */ + GATT_DISC_CHAR_DSCPT, /* discover characteristic descriptors of a character */ + GATT_DISC_MAX /* maximnun discover type */ +}; +typedef UINT8 tGATT_DISC_TYPE; + +/* Discover parameters of different discovery types +*/ +typedef struct { + tBT_UUID service; + UINT16 s_handle; + UINT16 e_handle; +} tGATT_DISC_PARAM; + +/* GATT read type enumeration +*/ +enum { + GATT_READ_BY_TYPE = 1, + GATT_READ_BY_HANDLE, + GATT_READ_MULTIPLE, + GATT_READ_MULTIPLE_VAR, + GATT_READ_CHAR_VALUE, + GATT_READ_PARTIAL, + GATT_READ_MAX +}; +typedef UINT8 tGATT_READ_TYPE; + +/* Read By Type Request (GATT_READ_BY_TYPE) Data +*/ +typedef struct { + tGATT_AUTH_REQ auth_req; + UINT16 s_handle; + UINT16 e_handle; + tBT_UUID uuid; +} tGATT_READ_BY_TYPE; + +/* GATT_READ_MULTIPLE request data +*/ +#define GATT_MAX_READ_MULTI_HANDLES 10 /* Max attributes to read in one request */ +typedef struct { + tGATT_AUTH_REQ auth_req; + UINT16 num_handles; /* number of handles to read */ + UINT16 handles[GATT_MAX_READ_MULTI_HANDLES]; /* handles list to be read */ +} tGATT_READ_MULTI; + +/* Read By Handle Request (GATT_READ_BY_HANDLE) data */ +typedef struct { + tGATT_AUTH_REQ auth_req; + UINT16 handle; +} tGATT_READ_BY_HANDLE; + +/* READ_BT_HANDLE_Request data */ +typedef struct { + tGATT_AUTH_REQ auth_req; + UINT16 handle; + UINT16 offset; +} tGATT_READ_PARTIAL; + +/* Read Request Data +*/ +typedef union { + tGATT_READ_BY_TYPE service; + tGATT_READ_BY_TYPE char_type; /* characteristic type */ + tGATT_READ_MULTI read_multiple; + tGATT_READ_BY_HANDLE by_handle; + tGATT_READ_PARTIAL partial; +} tGATT_READ_PARAM; + +/* GATT write type enumeration */ +enum { + GATT_WRITE_NO_RSP = 1, + GATT_WRITE , + GATT_WRITE_PREPARE +}; +typedef UINT8 tGATT_WRITE_TYPE; + +/* Client Operation Complete Callback Data +*/ +typedef union { + tGATT_VALUE att_value; + UINT16 mtu; + UINT16 handle; +} tGATT_CL_COMPLETE; + +/* GATT client operation type, used in client callback function +*/ +#define GATTC_OPTYPE_NONE 0 +#define GATTC_OPTYPE_DISCOVERY 1 +#define GATTC_OPTYPE_READ 2 +#define GATTC_OPTYPE_WRITE 3 +#define GATTC_OPTYPE_EXE_WRITE 4 +#define GATTC_OPTYPE_CONFIG 5 +#define GATTC_OPTYPE_NOTIFICATION 6 +#define GATTC_OPTYPE_INDICATION 7 +typedef UINT8 tGATTC_OPTYPE; + +/* characteristic declaration +*/ +typedef struct { + tGATT_CHAR_PROP char_prop; /* characteristic properties */ + UINT16 val_handle; /* characteristic value attribute handle */ + tBT_UUID char_uuid; /* characteristic UUID type */ +} tGATT_CHAR_DCLR_VAL; + +/* primary service group data +*/ +typedef struct { + UINT16 e_handle; /* ending handle of the group */ + tBT_UUID service_type; /* group type */ +} tGATT_GROUP_VALUE; + + +/* included service attribute value +*/ +typedef struct { + tBT_UUID service_type; /* included service UUID */ + UINT16 s_handle; /* starting handle */ + UINT16 e_handle; /* ending handle */ +} tGATT_INCL_SRVC; + +typedef union { + tGATT_INCL_SRVC incl_service; /* include service value */ + tGATT_GROUP_VALUE group_value; /* Service UUID type. + This field is used with GATT_DISC_SRVC_ALL + or GATT_DISC_SRVC_BY_UUID + type of discovery result callback. */ + + UINT16 handle; /* When used with GATT_DISC_INC_SRVC type discovery result, + it is the included service starting handle.*/ + + tGATT_CHAR_DCLR_VAL dclr_value; /* Characteristic declaration value. + This field is used with GATT_DISC_CHAR type discovery.*/ +} tGATT_DISC_VALUE; + +/* discover result record +*/ +typedef struct { + tBT_UUID type; + UINT16 handle; + tGATT_DISC_VALUE value; +} tGATT_DISC_RES; + + +#define GATT_LINK_IDLE_TIMEOUT_WHEN_NO_APP 0 /* start a idle timer for this duration + when no application need to use the link */ + +#define GATT_LINK_NO_IDLE_TIMEOUT 0xFFFF + +#define GATT_INVALID_ACL_HANDLE 0xFFFF +/* discover result callback function */ +typedef void (tGATT_DISC_RES_CB) (UINT16 conn_id, tGATT_DISC_TYPE disc_type, + tGATT_DISC_RES *p_data); + +/* discover complete callback function */ +typedef void (tGATT_DISC_CMPL_CB) (UINT16 conn_id, tGATT_DISC_TYPE disc_type, tGATT_STATUS status); + +/* Define a callback function for when read/write/disc/config operation is completed. */ +typedef void (tGATT_CMPL_CBACK) (UINT16 conn_id, tGATTC_OPTYPE op, tGATT_STATUS status, + tGATT_CL_COMPLETE *p_data); + +/* Define a callback function when an initialized connection is established. */ +typedef void (tGATT_CONN_CBACK) (tGATT_IF gatt_if, BD_ADDR bda, UINT16 conn_id, BOOLEAN connected, + tGATT_DISCONN_REASON reason, tBT_TRANSPORT transport); + +/* attribute request callback for ATT server */ +typedef void (tGATT_REQ_CBACK )(UINT16 conn_id, UINT32 trans_id, tGATTS_REQ_TYPE type, + tGATTS_DATA *p_data); + +/* channel congestion/uncongestion callback */ +typedef void (tGATT_CONGESTION_CBACK )(UINT16 conn_id, BOOLEAN congested); + +/* Define a callback function when encryption is established. */ +typedef void (tGATT_ENC_CMPL_CB)(tGATT_IF gatt_if, BD_ADDR bda); + + +/* Define the structure that applications use to register with +** GATT. This structure includes callback functions. All functions +** MUST be provided. +*/ +typedef struct { + tGATT_CONN_CBACK *p_conn_cb; + tGATT_CMPL_CBACK *p_cmpl_cb; + tGATT_DISC_RES_CB *p_disc_res_cb; + tGATT_DISC_CMPL_CB *p_disc_cmpl_cb; + tGATT_REQ_CBACK *p_req_cb; + tGATT_ENC_CMPL_CB *p_enc_cmpl_cb; + tGATT_CONGESTION_CBACK *p_congestion_cb; +} tGATT_CBACK; + +/*********************** Start Handle Management Definitions ********************** +*/ + + +typedef struct { + tBT_UUID app_uuid128; + tBT_UUID svc_uuid; + UINT16 svc_inst; + UINT16 s_handle; + UINT16 e_handle; + BOOLEAN is_primary; /* primary service or secondary */ +} tGATTS_HNDL_RANGE; + + + +#define GATTS_SRV_CHG_CMD_ADD_CLIENT 1 +#define GATTS_SRV_CHG_CMD_UPDATE_CLIENT 2 +#define GATTS_SRV_CHG_CMD_REMOVE_CLIENT 3 +#define GATTS_SRV_CHG_CMD_READ_NUM_CLENTS 4 +#define GATTS_SRV_CHG_CMD_READ_CLENT 5 +typedef UINT8 tGATTS_SRV_CHG_CMD; + +typedef struct { + BD_ADDR bda; + BOOLEAN srv_changed; +} tGATTS_SRV_CHG; + + +typedef union { + tGATTS_SRV_CHG srv_chg; + UINT8 client_read_index; /* only used for sequential reading client srv chg info */ +} tGATTS_SRV_CHG_REQ; + +typedef union { + tGATTS_SRV_CHG srv_chg; + UINT8 num_clients; +} tGATTS_SRV_CHG_RSP; + + + +typedef struct { + tGATTS_HNDL_RANGE *p_new_srv_start; +} tGATTS_PENDING_NEW_SRV_START; + +/* Attribute server handle ranges NV storage callback functions +*/ +typedef void (tGATTS_NV_SAVE_CBACK)(BOOLEAN is_saved, tGATTS_HNDL_RANGE *p_hndl_range); +typedef BOOLEAN (tGATTS_NV_SRV_CHG_CBACK)(tGATTS_SRV_CHG_CMD cmd, tGATTS_SRV_CHG_REQ *p_req, + tGATTS_SRV_CHG_RSP *p_rsp); + +typedef struct { + tGATTS_NV_SAVE_CBACK *p_nv_save_callback; + tGATTS_NV_SRV_CHG_CBACK *p_srv_chg_callback; +} tGATT_APPL_INFO; + +typedef struct { + UINT16 handle; + UINT16 length; + UINT8 *value; +} tGATT_HLV; + +/* +*********************** End Handle Management Definitions **********************/ + +/***************************************************************************** +** External Function Declarations +*****************************************************************************/ +#ifdef __cplusplus +extern "C" +{ +#endif + +/******************************************************************************* +** +** Function GATT_SetTraceLevel +** +** Description This function sets the trace level. If called with +** a value of 0xFF, it simply returns the current trace level. +** +** Returns The new or current trace level +** +*******************************************************************************/ +extern UINT8 GATT_SetTraceLevel (UINT8 new_level); + + +/*******************************************************************************/ +/* GATT Profile API Functions */ +/*******************************************************************************/ +/* GATT Profile Server Functions */ +/*******************************************************************************/ +/******************************************************************************* +** +** Function GATTS_AddHandleRange +** +** Description This function add the allocated handles range for the specifed +** application UUID, service UUID and service instance +** +** Parameter p_hndl_range: pointer to allocated handles information +** +** Returns TRUE if handle range is added successfully; otherwise FALSE. +** +*******************************************************************************/ + +extern BOOLEAN GATTS_AddHandleRange(tGATTS_HNDL_RANGE *p_hndl_range); + +/******************************************************************************* +** +** Function GATTS_NVRegister +** +** Description Application manager calls this function to register for +** NV save callback function. There can be one and only one +** NV save callback function. +** +** Parameter p_cb_info : callback informaiton +** +** Returns TRUE if registered OK, else FALSE +** +*******************************************************************************/ +extern BOOLEAN GATTS_NVRegister (const tGATT_APPL_INFO *p_cb_info); + + +/******************************************************************************* +** +** Function GATTS_CreateService +** +** Description This function is called to reserve a block of handles for a service. +** +** *** It should be called only once per service instance *** +** +** Parameter gatt_if : application if +** p_svc_uuid : service UUID +** svc_inst : instance of the service inside the application +** num_handles : number of handles needed by the service. +** is_pri : is a primary service or not. +** +** Returns service handle if successful, otherwise 0. +** +*******************************************************************************/ +extern UINT16 GATTS_CreateService (tGATT_IF gatt_if, tBT_UUID *p_svc_uuid, + UINT16 svc_inst, UINT16 num_handles, BOOLEAN is_pri); + + +/******************************************************************************* +** +** Function GATTS_AddIncludeService +** +** Description This function is called to add an included service. +** +** Parameter service_handle : To which service this included service is added to. +** include_svc_handle : included service handle. +** +** Returns included service attribute handle. If 0, add included service +** fail. +** +*******************************************************************************/ +extern UINT16 GATTS_AddIncludeService (UINT16 service_handle, + UINT16 include_svc_handle); + + +/******************************************************************************* +** +** Function GATTS_AddCharacteristic +** +** Description This function is called to add a characteristic into a service. +** It will add a characteristic declaration and characteristic +** value declaration into the service database identified by the +** service handle. +** +** Parameter service_handle : To which service this included service is added to. +** char_uuid : Characteristic UUID. +** perm : Characteristic value declaration attribute permission. +** property : Characteristic Properties +** +** Returns Characteristic value declaration attribute handle. 0 if add +** characteristic failed. +** +*******************************************************************************/ +extern UINT16 GATTS_AddCharacteristic (UINT16 service_handle, tBT_UUID *p_char_uuid, + tGATT_PERM perm, tGATT_CHAR_PROP property, + tGATT_ATTR_VAL *attr_val, tGATTS_ATTR_CONTROL *control); + +/******************************************************************************* +** +** Function GATTS_AddCharDescriptor +** +** Description This function is called to add a characteristic descriptor +** into a service database. Add descriptor should follow add char +** to which it belongs, and next add char should be done only +** after all add descriptors for the previous char. +** +** Parameter service_handle : To which service this characteristic descriptor +** is added to. +** perm : Characteristic value declaration attribute +** permission. +** p_descr_uuid : Characteristic descriptor UUID. +** +** Returns Characteristic descriptor attribute handle. 0 if add +** characteristic descriptor failed. +** +*******************************************************************************/ +extern UINT16 GATTS_AddCharDescriptor (UINT16 service_handle, tGATT_PERM perm, + tBT_UUID *p_descr_uuid, tGATT_ATTR_VAL *attr_val, + tGATTS_ATTR_CONTROL *control); + +/******************************************************************************* +** +** Function GATTS_DeleteService +** +** Description This function is called to delete a service. +** +** Parameter gatt_if : application interface +** p_svc_uuid : service UUID +** svc_inst : instance of the service inside the application +** +** Returns TRUE if operation succeed, FALSE if handle block was not found. +** +*******************************************************************************/ +extern BOOLEAN GATTS_DeleteService (tGATT_IF gatt_if, tBT_UUID *p_svc_uuid, + UINT16 svc_inst); + +/******************************************************************************* +** +** Function GATTS_StartService +** +** Description This function is called to start a service with GATT +** +** Parameter gatt_if : service handle. +** p_cback : application service callback functions. +** sup_transport : supported transport(s) for this primary service +** +** return GATT_SUCCESS if successfully started; otherwise error code. +** +*******************************************************************************/ +extern tGATT_STATUS GATTS_StartService (tGATT_IF gatt_if, UINT16 service_handle, + tGATT_TRANSPORT sup_transport); + + +/******************************************************************************* +** +** Function GATTS_StopService +** +** Description This function is called to stop a service +** +** Parameter service_handle : this is the start handle of a service +** +** Returns None. +** +*******************************************************************************/ +extern void GATTS_StopService (UINT16 service_handle); + + +/******************************************************************************* +** +** Function GATTs_HandleValueIndication +** +** Description This function sends a handle value indication to a client. +** +** Parameter conn_id: connection identifier. +** attr_handle: Attribute handle of this handle value indication. +** val_len: Length of the indicated attribute value. +** p_val: Pointer to the indicated attribute value data. +** +** Returns GATT_SUCCESS if successfully sent or queued; otherwise error code. +** +*******************************************************************************/ +extern tGATT_STATUS GATTS_HandleValueIndication (UINT16 conn_id, + UINT16 attr_handle, + UINT16 val_len, UINT8 *p_val); + +/******************************************************************************* +** +** Function GATTS_HandleValueNotification +** +** Description This function sends a handle value notification to a client. +** +** Parameter conn_id: connection identifier. +** attr_handle: Attribute handle of this handle value indication. +** val_len: Length of the indicated attribute value. +** p_val: Pointer to the indicated attribute value data. +** +** Returns GATT_SUCCESS if successfully sent; otherwise error code. +** +*******************************************************************************/ +extern tGATT_STATUS GATTS_HandleValueNotification (UINT16 conn_id, UINT16 attr_handle, + UINT16 val_len, UINT8 *p_val); + + +/******************************************************************************* +** +** Function GATTS_SendRsp +** +** Description This function sends the server response to client. +** +** Parameter conn_id: connection identifier. +** trans_id: transaction id +** status: response status +** p_msg: pointer to message parameters structure. +** +** Returns GATT_SUCCESS if successfully sent; otherwise error code. +** +*******************************************************************************/ +extern tGATT_STATUS GATTS_SendRsp (UINT16 conn_id, UINT32 trans_id, + tGATT_STATUS status, tGATTS_RSP *p_msg); + + +/******************************************************************************* +** +** Function GATTS_SetAttributeValue +** +** Description This function sends to set the attribute value . +** +** Parameter attr_handle:the attribute handle +** length: the attribute length +** value: the value to be set to the attribute in the database +** +** Returns GATT_SUCCESS if successfully sent; otherwise error code. +** +*******************************************************************************/ +tGATT_STATUS GATTS_SetAttributeValue(UINT16 attr_handle, UINT16 length, UINT8 *value); + + +/******************************************************************************* +** +** Function GATTS_GetAttributeValue +** +** Description This function sends to set the attribute value . +** +** Parameter attr_handle: the attribute handle +** length:the attribute value length in the database +** value: the attribute value out put +** +** Returns GATT_SUCCESS if successfully sent; otherwise error code. +** +*******************************************************************************/ +tGATT_STATUS GATTS_GetAttributeValue(UINT16 attr_handle, UINT16 *length, UINT8 **value); + +tGATT_STATUS GATTS_GetAttributeValueInternal(UINT16 attr_handle, UINT16 *length, UINT8 **value); + +/*******************************************************************************/ +/* GATT Profile Client Functions */ +/*******************************************************************************/ + +/******************************************************************************* +** +** Function GATTC_ConfigureMTU +** +** Description This function is called to configure the ATT MTU size for +** a connection on an LE transport. +** +** Parameters conn_id: connection identifier. +** mtu - attribute MTU size.. +** +** Returns GATT_SUCCESS if command started successfully. +** +*******************************************************************************/ +extern tGATT_STATUS GATTC_ConfigureMTU (UINT16 conn_id); + +/******************************************************************************* +** +** Function GATTC_Discover +** +** Description This function is called to do a discovery procedure on ATT server. +** +** Parameters conn_id: connection identifier. +** disc_type:discovery type. +** p_param: parameters of discovery requirement. +** +** Returns GATT_SUCCESS if command received/sent successfully. +** +*******************************************************************************/ +extern tGATT_STATUS GATTC_Discover (UINT16 conn_id, + tGATT_DISC_TYPE disc_type, + tGATT_DISC_PARAM *p_param ); +/******************************************************************************* +** +** Function GATTC_Read +** +** Description This function is called to read the value of an attribute from +** the server. +** +** Parameters conn_id: connection identifier. +** type - attribute read type. +** p_read - read operation parameters. +** +** Returns GATT_SUCCESS if command started successfully. +** +*******************************************************************************/ +extern tGATT_STATUS GATTC_Read (UINT16 conn_id, tGATT_READ_TYPE type, + tGATT_READ_PARAM *p_read); + +/******************************************************************************* +** +** Function GATTC_Write +** +** Description This function is called to read the value of an attribute from +** the server. +** +** Parameters conn_id: connection identifier. +** type - attribute write type. +** p_write - write operation parameters. +** +** Returns GATT_SUCCESS if command started successfully. +** +*******************************************************************************/ +extern tGATT_STATUS GATTC_Write (UINT16 conn_id, tGATT_WRITE_TYPE type, + tGATT_VALUE *p_write); + + +/******************************************************************************* +** +** Function GATTC_ExecuteWrite +** +** Description This function is called to send an Execute write request to +** the server. +** +** Parameters conn_id: connection identifier. +** is_execute - to execute or cancel the prepare write request(s) +** +** Returns GATT_SUCCESS if command started successfully. +** +*******************************************************************************/ +extern tGATT_STATUS GATTC_ExecuteWrite (UINT16 conn_id, BOOLEAN is_execute); + +/******************************************************************************* +** +** Function GATTC_SendHandleValueConfirm +** +** Description This function is called to send a handle value confirmation +** as response to a handle value notification from server. +** +** Parameters conn_id: connection identifier. +** handle: the handle of the attribute confirmation. +** +** Returns GATT_SUCCESS if command started successfully. +** +*******************************************************************************/ +extern tGATT_STATUS GATTC_SendHandleValueConfirm (UINT16 conn_id, UINT16 handle); + +/******************************************************************************* +** +** Function GATTC_AutoDiscoverEnable +** +** Description This function is called to enable/disable auto discover. +** +** Parameters enable: 0 for disable, otherwise enable. +** +** Returns GATT_SUCCESS if command started successfully. +** +*******************************************************************************/ +extern tGATT_STATUS GATTC_AutoDiscoverEnable(UINT8 enable); + +/******************************************************************************* +** +** Function GATT_SetIdleTimeout +** +** Description This function (common to both client and server) sets the idle +** timeout for a tansport connection +** +** Parameter bd_addr: target device bd address. +** idle_tout: timeout value in seconds. +** transport: transport option. +** +** Returns void +** +*******************************************************************************/ +extern void GATT_SetIdleTimeout (BD_ADDR bd_addr, UINT16 idle_tout, + tGATT_TRANSPORT transport); + + +/******************************************************************************* +** +** Function GATT_Register +** +** Description This function is called to register an application +** with GATT +** +** Parameter p_app_uuid128: Application UUID +** p_cb_info: callback functions. +** +** Returns 0 for error, otherwise the index of the client registered with GATT +** +*******************************************************************************/ +extern tGATT_IF GATT_Register (tBT_UUID *p_app_uuid128, const tGATT_CBACK *p_cb_info); + +/******************************************************************************* +** +** Function GATT_Deregister +** +** Description This function deregistered the application from GATT. +** +** Parameters gatt_if: application interface. +** +** Returns None. +** +*******************************************************************************/ +extern void GATT_Deregister (tGATT_IF gatt_if); + +/******************************************************************************* +** +** Function GATT_StartIf +** +** Description This function is called after registration to start receiving +** callbacks for registered interface. Function may call back +** with connection status and queued notifications +** +** Parameter gatt_if: application interface. +** +** Returns None +** +*******************************************************************************/ +extern void GATT_StartIf (tGATT_IF gatt_if); + +/******************************************************************************* +** +** Function GATT_Connect +** +** Description This function initiate a connection to a remote device on GATT +** channel. +** +** Parameters gatt_if: application interface +** bd_addr: peer device address. +** bd_addr_type: peer device address type. +** is_direct: is a direct connection or a background auto connection +** transport : Physical transport for GATT connection (BR/EDR or LE) +** +** Returns TRUE if connection started; FALSE if connection start failure. +** +*******************************************************************************/ +extern BOOLEAN GATT_Connect (tGATT_IF gatt_if, BD_ADDR bd_addr, tBLE_ADDR_TYPE bd_addr_type, + BOOLEAN is_direct, tBT_TRANSPORT transport, BOOLEAN is_aux); + + +/******************************************************************************* +** +** Function GATT_CancelConnect +** +** Description This function terminate the connection initaition to a remote +** device on GATT channel. +** +** Parameters gatt_if: client interface. If 0 used as unconditionally disconnect, +** typically used for direct connection cancellation. +** bd_addr: peer device address. +** is_direct: is a direct connection or a background auto connection +** +** Returns TRUE if connection started; FALSE if connection start failure. +** +*******************************************************************************/ +extern BOOLEAN GATT_CancelConnect (tGATT_IF gatt_if, BD_ADDR bd_addr, + BOOLEAN is_direct); + +/******************************************************************************* +** +** Function GATT_Disconnect +** +** Description This function disconnect the GATT channel for this registered +** application. +** +** Parameters conn_id: connection identifier. +** +** Returns GATT_SUCCESS if disconnected. +** +*******************************************************************************/ +extern tGATT_STATUS GATT_Disconnect (UINT16 conn_id); + +/******************************************************************************* +** +** Function GATT_SendServiceChangeIndication +** +** Description This function is to send a service change indication +** +** Parameters bd_addr: peer device address. +** +** Returns status. +** +*******************************************************************************/ +extern tGATT_STATUS GATT_SendServiceChangeIndication (BD_ADDR bd_addr); + +/******************************************************************************* +** +** Function GATT_GetConnectionInfor +** +** Description This function use conn_id to find its associated BD address and application +** interface +** +** Parameters conn_id: connection id (input) +** p_gatt_if: application interface (output) +** bd_addr: peer device address. (output) +** transport : physical transport of the GATT connection (BR/EDR or LE) +** +** Returns TRUE the logical link information is found for conn_id +** +*******************************************************************************/ +extern BOOLEAN GATT_GetConnectionInfor(UINT16 conn_id, tGATT_IF *p_gatt_if, + BD_ADDR bd_addr, tBT_TRANSPORT *p_transport); + + +/******************************************************************************* +** +** Function GATT_GetConnIdIfConnected +** +** Description This function find the conn_id if the logical link for BD address +** and application interface is connected +** +** Parameters gatt_if: application interface (input) +** bd_addr: peer device address. (input) +** p_conn_id: connection id (output) +** transport : physical transport of the GATT connection (BR/EDR or LE) +** +** Returns TRUE the logical link is connected +** +*******************************************************************************/ +extern BOOLEAN GATT_GetConnIdIfConnected(tGATT_IF gatt_if, BD_ADDR bd_addr, + UINT16 *p_conn_id, tBT_TRANSPORT transport); + + +/******************************************************************************* +** +** Function GATT_Listen +** +** Description This function start or stop LE advertisement and listen for +** connection. +** +** Parameters gatt_if: application interface +** p_bd_addr: listen for specific address connection, or NULL for +** listen to all device connection. +** start: is a direct connection or a background auto connection +** +** Returns TRUE if advertisement is started; FALSE if adv start failure. +** +*******************************************************************************/ +extern BOOLEAN GATT_Listen (tGATT_IF gatt_if, BOOLEAN start, BD_ADDR_PTR bd_addr); + +/******************************************************************************* +** +** Function GATT_ConfigServiceChangeCCC +** +** Description Configure service change indication on remote device +** +** Returns None. +** +*******************************************************************************/ +extern void GATT_ConfigServiceChangeCCC (BD_ADDR remote_bda, BOOLEAN enable, + tBT_TRANSPORT transport); + +/******************************************************************************* +** +** Function GATTS_SetServiceChangeMode +** +** Description Configure service change indication mode +** +** Parameters mode: service change mode +** +** Returns Status. +** +*******************************************************************************/ +extern tGATT_STATUS GATTS_SetServiceChangeMode(UINT8 mode); + +/******************************************************************************* +** +** Function GATTS_HandleMultiValueNotification +** +** Description This function sends multiple handle value notification to a client. +** +** Parameter conn_id: connection identifier. +** tuples: Pointer to handle-length-value tuple list. +** num_tuples: Number of tuples. +** +** Returns GATT_SUCCESS if successfully sent; otherwise error code. +** +*******************************************************************************/ +extern tGATT_STATUS GATTS_HandleMultiValueNotification (UINT16 conn_id, tGATT_HLV *tuples, UINT16 num_tuples); + +/******************************************************************************* +** +** Function GATTS_ShowLocalDatabase +** +** Description This function print local service database. +** +** Returns GATT_SUCCESS if successfully sent; otherwise error code. +** +*******************************************************************************/ +extern tGATT_STATUS GATTS_ShowLocalDatabase(void); + +#ifdef __cplusplus + +} +#endif + +#endif /* GATT_API_H */ diff --git a/lib/bt/host/bluedroid/stack/include/stack/gattdefs.h b/lib/bt/host/bluedroid/stack/include/stack/gattdefs.h new file mode 100644 index 00000000..d8e4cb59 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/include/stack/gattdefs.h @@ -0,0 +1,128 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains internally used ATT definitions + * + ******************************************************************************/ + +#ifndef _GATTDEFS_H +#define _GATTDEFS_H + +#define GATT_ILLEGAL_UUID 0 + +/* GATT attribute types +*/ +#define GATT_UUID_PRI_SERVICE 0x2800 +#define GATT_UUID_SEC_SERVICE 0x2801 +#define GATT_UUID_INCLUDE_SERVICE 0x2802 +#define GATT_UUID_CHAR_DECLARE 0x2803 /* Characteristic Declaration*/ + +#define GATT_UUID_CHAR_EXT_PROP 0x2900 /* Characteristic Extended Properties */ +#define GATT_UUID_CHAR_DESCRIPTION 0x2901 /* Characteristic User Description*/ +#define GATT_UUID_CHAR_CLIENT_CONFIG 0x2902 /* Client Characteristic Configuration */ +#define GATT_UUID_CHAR_SRVR_CONFIG 0x2903 /* Server Characteristic Configuration */ +#define GATT_UUID_CHAR_PRESENT_FORMAT 0x2904 /* Characteristic Presentation Format*/ +#define GATT_UUID_CHAR_AGG_FORMAT 0x2905 /* Characteristic Aggregate Format*/ +#define GATT_UUID_CHAR_VALID_RANGE 0x2906 /* Characteristic Valid Range */ +#define GATT_UUID_EXT_RPT_REF_DESCR 0x2907 +#define GATT_UUID_RPT_REF_DESCR 0x2908 + + +/* GAP Profile Attributes +*/ +#define GATT_UUID_GAP_DEVICE_NAME 0x2A00 +#define GATT_UUID_GAP_ICON 0x2A01 +#define GATT_UUID_GAP_PREF_CONN_PARAM 0x2A04 +#define GATT_UUID_GAP_CENTRAL_ADDR_RESOL 0x2AA6 + +/* Attribute Profile Attribute UUID */ +#define GATT_UUID_GATT_SRV_CHGD 0x2A05 +/* Attribute Protocol Test */ + +/* Link Loss Service */ +#define GATT_UUID_ALERT_LEVEL 0x2A06 /* Alert Level */ +#define GATT_UUID_TX_POWER_LEVEL 0x2A07 /* TX power level */ + +/* Time Profile */ +/* Current Time Service */ +#define GATT_UUID_CURRENT_TIME 0x2A2B /* Current Time */ +#define GATT_UUID_LOCAL_TIME_INFO 0x2A0F /* Local time info */ +#define GATT_UUID_REF_TIME_INFO 0x2A14 /* reference time information */ + +/* NwA Profile */ +#define GATT_UUID_NW_STATUS 0x2A18 /* network availability status */ +#define GATT_UUID_NW_TRIGGER 0x2A1A /* Network availability trigger */ + +/* phone alert */ +#define GATT_UUID_ALERT_STATUS 0x2A3F /* alert status */ +#define GATT_UUID_RINGER_CP 0x2A40 /* ringer control point */ +#define GATT_UUID_RINGER_SETTING 0x2A41 /* ringer setting */ + +/* Glucose Service */ +#define GATT_UUID_GM_MEASUREMENT 0x2A18 +#define GATT_UUID_GM_CONTEXT 0x2A34 +#define GATT_UUID_GM_CONTROL_POINT 0x2A52 +#define GATT_UUID_GM_FEATURE 0x2A51 + +/* device infor characteristic */ +#define GATT_UUID_SYSTEM_ID 0x2A23 +#define GATT_UUID_MODEL_NUMBER_STR 0x2A24 +#define GATT_UUID_SERIAL_NUMBER_STR 0x2A25 +#define GATT_UUID_FW_VERSION_STR 0x2A26 +#define GATT_UUID_HW_VERSION_STR 0x2A27 +#define GATT_UUID_SW_VERSION_STR 0x2A28 +#define GATT_UUID_MANU_NAME 0x2A29 +#define GATT_UUID_IEEE_DATA 0x2A2A +#define GATT_UUID_PNP_ID 0x2A50 + +/* HID characteristics */ +#define GATT_UUID_HID_INFORMATION 0x2A4A +#define GATT_UUID_HID_REPORT_MAP 0x2A4B +#define GATT_UUID_HID_CONTROL_POINT 0x2A4C +#define GATT_UUID_HID_REPORT 0x2A4D +#define GATT_UUID_HID_PROTO_MODE 0x2A4E +#define GATT_UUID_HID_BT_KB_INPUT 0x2A22 +#define GATT_UUID_HID_BT_KB_OUTPUT 0x2A32 +#define GATT_UUID_HID_BT_MOUSE_INPUT 0x2A33 + +/* Battery Service char */ +#define GATT_UUID_BATTERY_LEVEL 0x2A19 + +#define GATT_UUID_SC_CONTROL_POINT 0x2A55 +#define GATT_UUID_SENSOR_LOCATION 0x2A5D + +/* RUNNERS SPEED AND CADENCE SERVICE */ +#define GATT_UUID_RSC_MEASUREMENT 0x2A53 +#define GATT_UUID_RSC_FEATURE 0x2A54 + +/* CYCLING SPEED AND CADENCE SERVICE */ +#define GATT_UUID_CSC_MEASUREMENT 0x2A5B +#define GATT_UUID_CSC_FEATURE 0x2A5C + + +/* Scan Parameter charatceristics */ +#define GATT_UUID_SCAN_INT_WINDOW 0x2A4F +#define GATT_UUID_SCAN_REFRESH 0x2A31 + +#define GATT_UUID_CLIENT_SUP_FEAT 0x2B29 +#define GATT_UUID_GATT_DATABASE_HASH 0x2B2A +#define GATT_UUID_SERVER_SUP_FEAT 0x2B3A + +#endif diff --git a/lib/bt/host/bluedroid/stack/include/stack/hcidefs.h b/lib/bt/host/bluedroid/stack/include/stack/hcidefs.h new file mode 100644 index 00000000..8c5ba3f2 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/include/stack/hcidefs.h @@ -0,0 +1,2730 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2014 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +#ifndef HCIDEFS_H +#define HCIDEFS_H + +#include "common/bt_target.h" + +#include "stack/bt_types.h" + +#define HCI_PROTO_VERSION 0x01 /* Version for BT spec 1.1 */ +#define HCI_PROTO_VERSION_1_2 0x02 /* Version for BT spec 1.2 */ +#define HCI_PROTO_VERSION_2_0 0x03 /* Version for BT spec 2.0 */ +#define HCI_PROTO_VERSION_2_1 0x04 /* Version for BT spec 2.1 [Lisbon] */ +#define HCI_PROTO_VERSION_3_0 0x05 /* Version for BT spec 3.0 */ +#define HCI_PROTO_VERSION_4_0 0x06 /* Version for BT spec 4.0 */ +#define HCI_PROTO_VERSION_4_1 0x07 /* Version for BT spec 4.1 */ +#define HCI_PROTO_VERSION_4_2 0x08 /* Version for BT spec 4.2 */ +#define HCI_PROTO_REVISION 0x000C /* Current implementation version */ +/* +** Definitions for HCI groups +*/ +#define HCI_GRP_LINK_CONTROL_CMDS (0x01 << 10) /* 0x0400 */ +#define HCI_GRP_LINK_POLICY_CMDS (0x02 << 10) /* 0x0800 */ +#define HCI_GRP_HOST_CONT_BASEBAND_CMDS (0x03 << 10) /* 0x0C00 */ +#define HCI_GRP_INFORMATIONAL_PARAMS (0x04 << 10) /* 0x1000 */ +#define HCI_GRP_STATUS_PARAMS (0x05 << 10) /* 0x1400 */ +#define HCI_GRP_TESTING_CMDS (0x06 << 10) /* 0x1800 */ + +#define HCI_GRP_VENDOR_SPECIFIC (0x3F << 10) /* 0xFC00 */ + +/* Group occupies high 6 bits of the HCI command rest is opcode itself */ +#define HCI_OGF(p) (UINT8)(0x003F & (p >> 10)) +#define HCI_OCF(p) ( 0x3FF & (p)) + +/* +** Definitions for Link Control Commands +*/ +/* Following opcode is used only in command complete event for flow control */ +#define HCI_COMMAND_NONE 0x0000 + +/* Commands of HCI_GRP_LINK_CONTROL_CMDS group */ +#define HCI_INQUIRY (0x0001 | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_INQUIRY_CANCEL (0x0002 | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_PERIODIC_INQUIRY_MODE (0x0003 | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_EXIT_PERIODIC_INQUIRY_MODE (0x0004 | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_CREATE_CONNECTION (0x0005 | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_DISCONNECT (0x0006 | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_ADD_SCO_CONNECTION (0x0007 | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_CREATE_CONNECTION_CANCEL (0x0008 | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_ACCEPT_CONNECTION_REQUEST (0x0009 | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_REJECT_CONNECTION_REQUEST (0x000A | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_LINK_KEY_REQUEST_REPLY (0x000B | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_LINK_KEY_REQUEST_NEG_REPLY (0x000C | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_PIN_CODE_REQUEST_REPLY (0x000D | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_PIN_CODE_REQUEST_NEG_REPLY (0x000E | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_CHANGE_CONN_PACKET_TYPE (0x000F | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_AUTHENTICATION_REQUESTED (0x0011 | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_SET_CONN_ENCRYPTION (0x0013 | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_CHANGE_CONN_LINK_KEY (0x0015 | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_MASTER_LINK_KEY (0x0017 | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_RMT_NAME_REQUEST (0x0019 | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_RMT_NAME_REQUEST_CANCEL (0x001A | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_READ_RMT_FEATURES (0x001B | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_READ_RMT_EXT_FEATURES (0x001C | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_READ_RMT_VERSION_INFO (0x001D | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_READ_RMT_CLOCK_OFFSET (0x001F | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_READ_LMP_HANDLE (0x0020 | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_SETUP_ESCO_CONNECTION (0x0028 | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_ACCEPT_ESCO_CONNECTION (0x0029 | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_REJECT_ESCO_CONNECTION (0x002A | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_IO_CAPABILITY_REQUEST_REPLY (0x002B | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_USER_CONF_REQUEST_REPLY (0x002C | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_USER_CONF_VALUE_NEG_REPLY (0x002D | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_USER_PASSKEY_REQ_REPLY (0x002E | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_USER_PASSKEY_REQ_NEG_REPLY (0x002F | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_REM_OOB_DATA_REQ_REPLY (0x0030 | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_REM_OOB_DATA_REQ_NEG_REPLY (0x0033 | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_IO_CAP_REQ_NEG_REPLY (0x0034 | HCI_GRP_LINK_CONTROL_CMDS) + +/* AMP HCI */ +#define HCI_CREATE_PHYSICAL_LINK (0x0035 | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_ACCEPT_PHYSICAL_LINK (0x0036 | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_DISCONNECT_PHYSICAL_LINK (0x0037 | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_CREATE_LOGICAL_LINK (0x0038 | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_ACCEPT_LOGICAL_LINK (0x0039 | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_DISCONNECT_LOGICAL_LINK (0x003A | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_LOGICAL_LINK_CANCEL (0x003B | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_FLOW_SPEC_MODIFY (0x003C | HCI_GRP_LINK_CONTROL_CMDS) + +#define HCI_ENH_SETUP_ESCO_CONNECTION (0x003D | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_ENH_ACCEPT_ESCO_CONNECTION (0x003E | HCI_GRP_LINK_CONTROL_CMDS) + +/* ConnectionLess Broadcast */ +#define HCI_TRUNCATED_PAGE (0x003F | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_TRUNCATED_PAGE_CANCEL (0x0040 | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_SET_CLB (0x0041 | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_RECEIVE_CLB (0x0042 | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_START_SYNC_TRAIN (0x0043 | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_RECEIVE_SYNC_TRAIN (0x0044 | HCI_GRP_LINK_CONTROL_CMDS) + +#define HCI_LINK_CTRL_CMDS_FIRST HCI_INQUIRY +#define HCI_LINK_CTRL_CMDS_LAST HCI_RECEIVE_SYNC_TRAIN + +/* Commands of HCI_GRP_LINK_POLICY_CMDS */ +#define HCI_HOLD_MODE (0x0001 | HCI_GRP_LINK_POLICY_CMDS) +#define HCI_SNIFF_MODE (0x0003 | HCI_GRP_LINK_POLICY_CMDS) +#define HCI_EXIT_SNIFF_MODE (0x0004 | HCI_GRP_LINK_POLICY_CMDS) +#define HCI_PARK_MODE (0x0005 | HCI_GRP_LINK_POLICY_CMDS) +#define HCI_EXIT_PARK_MODE (0x0006 | HCI_GRP_LINK_POLICY_CMDS) +#define HCI_QOS_SETUP (0x0007 | HCI_GRP_LINK_POLICY_CMDS) +#define HCI_ROLE_DISCOVERY (0x0009 | HCI_GRP_LINK_POLICY_CMDS) +#define HCI_SWITCH_ROLE (0x000B | HCI_GRP_LINK_POLICY_CMDS) +#define HCI_READ_POLICY_SETTINGS (0x000C | HCI_GRP_LINK_POLICY_CMDS) +#define HCI_WRITE_POLICY_SETTINGS (0x000D | HCI_GRP_LINK_POLICY_CMDS) +#define HCI_READ_DEF_POLICY_SETTINGS (0x000E | HCI_GRP_LINK_POLICY_CMDS) +#define HCI_WRITE_DEF_POLICY_SETTINGS (0x000F | HCI_GRP_LINK_POLICY_CMDS) +#define HCI_FLOW_SPECIFICATION (0x0010 | HCI_GRP_LINK_POLICY_CMDS) +#define HCI_SNIFF_SUB_RATE (0x0011 | HCI_GRP_LINK_POLICY_CMDS) + +#define HCI_LINK_POLICY_CMDS_FIRST HCI_HOLD_MODE +#define HCI_LINK_POLICY_CMDS_LAST HCI_SNIFF_SUB_RATE + + +/* Commands of HCI_GRP_HOST_CONT_BASEBAND_CMDS */ +#define HCI_SET_EVENT_MASK (0x0001 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_RESET (0x0003 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_SET_EVENT_FILTER (0x0005 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_FLUSH (0x0008 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_READ_PIN_TYPE (0x0009 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_WRITE_PIN_TYPE (0x000A | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_CREATE_NEW_UNIT_KEY (0x000B | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_GET_MWS_TRANS_LAYER_CFG (0x000C | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_READ_STORED_LINK_KEY (0x000D | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_WRITE_STORED_LINK_KEY (0x0011 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_DELETE_STORED_LINK_KEY (0x0012 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_CHANGE_LOCAL_NAME (0x0013 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_READ_LOCAL_NAME (0x0014 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_READ_CONN_ACCEPT_TOUT (0x0015 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_WRITE_CONN_ACCEPT_TOUT (0x0016 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_READ_PAGE_TOUT (0x0017 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_WRITE_PAGE_TOUT (0x0018 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_READ_SCAN_ENABLE (0x0019 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_WRITE_SCAN_ENABLE (0x001A | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_READ_PAGESCAN_CFG (0x001B | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_WRITE_PAGESCAN_CFG (0x001C | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_READ_INQUIRYSCAN_CFG (0x001D | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_WRITE_INQUIRYSCAN_CFG (0x001E | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_READ_AUTHENTICATION_ENABLE (0x001F | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_WRITE_AUTHENTICATION_ENABLE (0x0020 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_READ_ENCRYPTION_MODE (0x0021 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_WRITE_ENCRYPTION_MODE (0x0022 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_READ_CLASS_OF_DEVICE (0x0023 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_WRITE_CLASS_OF_DEVICE (0x0024 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_READ_VOICE_SETTINGS (0x0025 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_WRITE_VOICE_SETTINGS (0x0026 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_READ_AUTO_FLUSH_TOUT (0x0027 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_WRITE_AUTO_FLUSH_TOUT (0x0028 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_READ_NUM_BCAST_REXMITS (0x0029 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_WRITE_NUM_BCAST_REXMITS (0x002A | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_READ_HOLD_MODE_ACTIVITY (0x002B | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_WRITE_HOLD_MODE_ACTIVITY (0x002C | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_READ_TRANSMIT_POWER_LEVEL (0x002D | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_READ_SCO_FLOW_CTRL_ENABLE (0x002E | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_WRITE_SCO_FLOW_CTRL_ENABLE (0x002F | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_SET_HC_TO_HOST_FLOW_CTRL (0x0031 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_HOST_BUFFER_SIZE (0x0033 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_HOST_NUM_PACKETS_DONE (0x0035 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_READ_LINK_SUPER_TOUT (0x0036 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_WRITE_LINK_SUPER_TOUT (0x0037 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_READ_NUM_SUPPORTED_IAC (0x0038 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_READ_CURRENT_IAC_LAP (0x0039 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_WRITE_CURRENT_IAC_LAP (0x003A | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_READ_PAGESCAN_PERIOD_MODE (0x003B | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_WRITE_PAGESCAN_PERIOD_MODE (0x003C | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_READ_PAGESCAN_MODE (0x003D | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_WRITE_PAGESCAN_MODE (0x003E | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_SET_AFH_CHANNELS (0x003F | HCI_GRP_HOST_CONT_BASEBAND_CMDS) + +#define HCI_READ_INQSCAN_TYPE (0x0042 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_WRITE_INQSCAN_TYPE (0x0043 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_READ_INQUIRY_MODE (0x0044 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_WRITE_INQUIRY_MODE (0x0045 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_READ_PAGESCAN_TYPE (0x0046 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_WRITE_PAGESCAN_TYPE (0x0047 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_READ_AFH_ASSESSMENT_MODE (0x0048 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_WRITE_AFH_ASSESSMENT_MODE (0x0049 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_READ_EXT_INQ_RESPONSE (0x0051 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_WRITE_EXT_INQ_RESPONSE (0x0052 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_REFRESH_ENCRYPTION_KEY (0x0053 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_READ_SIMPLE_PAIRING_MODE (0x0055 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_WRITE_SIMPLE_PAIRING_MODE (0x0056 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_READ_LOCAL_OOB_DATA (0x0057 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_READ_INQ_TX_POWER_LEVEL (0x0058 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_WRITE_INQ_TX_POWER_LEVEL (0x0059 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_READ_ERRONEOUS_DATA_RPT (0x005A | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_WRITE_ERRONEOUS_DATA_RPT (0x005B | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_ENHANCED_FLUSH (0x005F | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_SEND_KEYPRESS_NOTIF (0x0060 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) + + +/* AMP HCI */ +#define HCI_READ_LOGICAL_LINK_ACCEPT_TIMEOUT (0x0061 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_WRITE_LOGICAL_LINK_ACCEPT_TIMEOUT (0x0062 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_SET_EVENT_MASK_PAGE_2 (0x0063 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_READ_LOCATION_DATA (0x0064 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_WRITE_LOCATION_DATA (0x0065 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_READ_FLOW_CONTROL_MODE (0x0066 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_WRITE_FLOW_CONTROL_MODE (0x0067 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_READ_BE_FLUSH_TOUT (0x0069 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_WRITE_BE_FLUSH_TOUT (0x006A | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_SHORT_RANGE_MODE (0x006B | HCI_GRP_HOST_CONT_BASEBAND_CMDS) /* 802.11 only */ +#define HCI_READ_LE_HOST_SUPPORT (0x006C | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_WRITE_LE_HOST_SUPPORT (0x006D | HCI_GRP_HOST_CONT_BASEBAND_CMDS) + + +/* MWS coexistence */ +#define HCI_SET_MWS_CHANNEL_PARAMETERS (0x006E | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_SET_EXTERNAL_FRAME_CONFIGURATION (0x006F | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_SET_MWS_SIGNALING (0x0070 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_SET_MWS_TRANSPORT_LAYER (0x0071 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_SET_MWS_SCAN_FREQUENCY_TABLE (0x0072 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_SET_MWS_PATTERN_CONFIGURATION (0x0073 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) + +/* Connectionless Broadcast */ +#define HCI_SET_RESERVED_LT_ADDR (0x0074 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_DELETE_RESERVED_LT_ADDR (0x0075 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_WRITE_CLB_DATA (0x0076 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_READ_SYNC_TRAIN_PARAM (0x0077 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_WRITE_SYNC_TRAIN_PARAM (0x0078 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) + +#define HCI_READ_SECURE_CONNS_SUPPORT (0x0079 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_WRITE_SECURE_CONNS_SUPPORT (0x007A | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_CONT_BASEBAND_CMDS_FIRST HCI_SET_EVENT_MASK +#define HCI_CONT_BASEBAND_CMDS_LAST HCI_READ_SYNC_TRAIN_PARAM + + +/* Commands of HCI_GRP_INFORMATIONAL_PARAMS group */ +#define HCI_READ_LOCAL_VERSION_INFO (0x0001 | HCI_GRP_INFORMATIONAL_PARAMS) +#define HCI_READ_LOCAL_SUPPORTED_CMDS (0x0002 | HCI_GRP_INFORMATIONAL_PARAMS) +#define HCI_READ_LOCAL_FEATURES (0x0003 | HCI_GRP_INFORMATIONAL_PARAMS) +#define HCI_READ_LOCAL_EXT_FEATURES (0x0004 | HCI_GRP_INFORMATIONAL_PARAMS) +#define HCI_READ_BUFFER_SIZE (0x0005 | HCI_GRP_INFORMATIONAL_PARAMS) +#define HCI_READ_COUNTRY_CODE (0x0007 | HCI_GRP_INFORMATIONAL_PARAMS) +#define HCI_READ_BD_ADDR (0x0009 | HCI_GRP_INFORMATIONAL_PARAMS) +#define HCI_READ_DATA_BLOCK_SIZE (0x000A | HCI_GRP_INFORMATIONAL_PARAMS) +#define HCI_READ_LOCAL_SUPPORTED_CODECS (0x000B | HCI_GRP_INFORMATIONAL_PARAMS) + +#define HCI_INFORMATIONAL_CMDS_FIRST HCI_READ_LOCAL_VERSION_INFO +#define HCI_INFORMATIONAL_CMDS_LAST HCI_READ_LOCAL_SUPPORTED_CODECS + + +/* Commands of HCI_GRP_STATUS_PARAMS group */ +#define HCI_READ_FAILED_CONTACT_COUNT (0x0001 | HCI_GRP_STATUS_PARAMS) +#define HCI_RESET_FAILED_CONTACT_COUNT (0x0002 | HCI_GRP_STATUS_PARAMS) +#define HCI_GET_LINK_QUALITY (0x0003 | HCI_GRP_STATUS_PARAMS) +#define HCI_READ_RSSI (0x0005 | HCI_GRP_STATUS_PARAMS) +#define HCI_READ_AFH_CH_MAP (0x0006 | HCI_GRP_STATUS_PARAMS) +#define HCI_READ_CLOCK (0x0007 | HCI_GRP_STATUS_PARAMS) +#define HCI_READ_ENCR_KEY_SIZE (0x0008 | HCI_GRP_STATUS_PARAMS) + +/* AMP HCI */ +#define HCI_READ_LOCAL_AMP_INFO (0x0009 | HCI_GRP_STATUS_PARAMS) +#define HCI_READ_LOCAL_AMP_ASSOC (0x000A | HCI_GRP_STATUS_PARAMS) +#define HCI_WRITE_REMOTE_AMP_ASSOC (0x000B | HCI_GRP_STATUS_PARAMS) + +#define HCI_STATUS_PARAMS_CMDS_FIRST HCI_READ_FAILED_CONTACT_COUNT +#define HCI_STATUS_PARAMS_CMDS_LAST HCI_WRITE_REMOTE_AMP_ASSOC + +/* Commands of HCI_GRP_TESTING_CMDS group */ +#define HCI_READ_LOOPBACK_MODE (0x0001 | HCI_GRP_TESTING_CMDS) +#define HCI_WRITE_LOOPBACK_MODE (0x0002 | HCI_GRP_TESTING_CMDS) +#define HCI_ENABLE_DEV_UNDER_TEST_MODE (0x0003 | HCI_GRP_TESTING_CMDS) +#define HCI_WRITE_SIMP_PAIR_DEBUG_MODE (0x0004 | HCI_GRP_TESTING_CMDS) + +/* AMP HCI */ +#define HCI_ENABLE_AMP_RCVR_REPORTS (0x0007 | HCI_GRP_TESTING_CMDS) +#define HCI_AMP_TEST_END (0x0008 | HCI_GRP_TESTING_CMDS) +#define HCI_AMP_TEST (0x0009 | HCI_GRP_TESTING_CMDS) + +#define HCI_TESTING_CMDS_FIRST HCI_READ_LOOPBACK_MODE +#define HCI_TESTING_CMDS_LAST HCI_AMP_TEST + +#define HCI_VENDOR_CMDS_FIRST 0x0001 +#define HCI_VENDOR_CMDS_LAST 0xFFFF +#define HCI_VSC_MULTI_AV_HANDLE 0x0AAA +#define HCI_VSC_BURST_MODE_HANDLE 0x0BBB + +/* BLE HCI */ +#define HCI_GRP_BLE_CMDS (0x08 << 10) +/* Commands of BLE Controller setup and configuration */ +#define HCI_BLE_SET_EVENT_MASK (0x0001 | HCI_GRP_BLE_CMDS) +#define HCI_BLE_READ_BUFFER_SIZE (0x0002 | HCI_GRP_BLE_CMDS) +#define HCI_BLE_READ_LOCAL_SPT_FEAT (0x0003 | HCI_GRP_BLE_CMDS) +#define HCI_BLE_WRITE_LOCAL_SPT_FEAT (0x0004 | HCI_GRP_BLE_CMDS) +#define HCI_BLE_WRITE_RANDOM_ADDR (0x0005 | HCI_GRP_BLE_CMDS) +#define HCI_BLE_WRITE_ADV_PARAMS (0x0006 | HCI_GRP_BLE_CMDS) +#define HCI_BLE_READ_ADV_CHNL_TX_POWER (0x0007 | HCI_GRP_BLE_CMDS) +#define HCI_BLE_WRITE_ADV_DATA (0x0008 | HCI_GRP_BLE_CMDS) +#define HCI_BLE_WRITE_SCAN_RSP_DATA (0x0009 | HCI_GRP_BLE_CMDS) +#define HCI_BLE_WRITE_ADV_ENABLE (0x000A | HCI_GRP_BLE_CMDS) +#define HCI_BLE_WRITE_SCAN_PARAMS (0x000B | HCI_GRP_BLE_CMDS) +#define HCI_BLE_WRITE_SCAN_ENABLE (0x000C | HCI_GRP_BLE_CMDS) +#define HCI_BLE_CREATE_LL_CONN (0x000D | HCI_GRP_BLE_CMDS) +#define HCI_BLE_CREATE_CONN_CANCEL (0x000E | HCI_GRP_BLE_CMDS) +#define HCI_BLE_READ_WHITE_LIST_SIZE (0x000F | HCI_GRP_BLE_CMDS) +#define HCI_BLE_CLEAR_WHITE_LIST (0x0010 | HCI_GRP_BLE_CMDS) +#define HCI_BLE_ADD_WHITE_LIST (0x0011 | HCI_GRP_BLE_CMDS) +#define HCI_BLE_REMOVE_WHITE_LIST (0x0012 | HCI_GRP_BLE_CMDS) +#define HCI_BLE_UPD_LL_CONN_PARAMS (0x0013 | HCI_GRP_BLE_CMDS) +#define HCI_BLE_SET_HOST_CHNL_CLASS (0x0014 | HCI_GRP_BLE_CMDS) +#define HCI_BLE_READ_CHNL_MAP (0x0015 | HCI_GRP_BLE_CMDS) +#define HCI_BLE_READ_REMOTE_FEAT (0x0016 | HCI_GRP_BLE_CMDS) +#define HCI_BLE_ENCRYPT (0x0017 | HCI_GRP_BLE_CMDS) +#define HCI_BLE_RAND (0x0018 | HCI_GRP_BLE_CMDS) +#define HCI_BLE_START_ENC (0x0019 | HCI_GRP_BLE_CMDS) +#define HCI_BLE_LTK_REQ_REPLY (0x001A | HCI_GRP_BLE_CMDS) +#define HCI_BLE_LTK_REQ_NEG_REPLY (0x001B | HCI_GRP_BLE_CMDS) +#define HCI_BLE_READ_SUPPORTED_STATES (0x001C | HCI_GRP_BLE_CMDS) +/*0x001D, 0x001E and 0x001F are reserved*/ +#define HCI_BLE_RECEIVER_TEST (0x001D | HCI_GRP_BLE_CMDS) +#define HCI_BLE_TRANSMITTER_TEST (0x001E | HCI_GRP_BLE_CMDS) +/* BLE TEST COMMANDS */ +#define HCI_BLE_TEST_END (0x001F | HCI_GRP_BLE_CMDS) +#define HCI_BLE_RC_PARAM_REQ_REPLY (0x0020 | HCI_GRP_BLE_CMDS) +#define HCI_BLE_RC_PARAM_REQ_NEG_REPLY (0x0021 | HCI_GRP_BLE_CMDS) + +#define HCI_BLE_SET_DATA_LENGTH (0x0022 | HCI_GRP_BLE_CMDS) +#define HCI_BLE_READ_DEFAULT_DATA_LENGTH (0x0023 | HCI_GRP_BLE_CMDS) +#define HCI_BLE_WRITE_DEFAULT_DATA_LENGTH (0x0024 | HCI_GRP_BLE_CMDS) + +#define HCI_BLE_ADD_DEV_RESOLVING_LIST (0x0027 | HCI_GRP_BLE_CMDS) +#define HCI_BLE_RM_DEV_RESOLVING_LIST (0x0028 | HCI_GRP_BLE_CMDS) +#define HCI_BLE_CLEAR_RESOLVING_LIST (0x0029 | HCI_GRP_BLE_CMDS) +#define HCI_BLE_READ_RESOLVING_LIST_SIZE (0x002A | HCI_GRP_BLE_CMDS) +#define HCI_BLE_READ_RESOLVABLE_ADDR_PEER (0x002B | HCI_GRP_BLE_CMDS) +#define HCI_BLE_READ_RESOLVABLE_ADDR_LOCAL (0x002C | HCI_GRP_BLE_CMDS) +#define HCI_BLE_SET_ADDR_RESOLUTION_ENABLE (0x002D | HCI_GRP_BLE_CMDS) +#define HCI_BLE_SET_RAND_PRIV_ADDR_TIMOUT (0x002E | HCI_GRP_BLE_CMDS) +#if (BLE_50_FEATURE_SUPPORT == TRUE) +#define HCI_BLE_READ_PHY (0x0030 | HCI_GRP_BLE_CMDS) +#define HCI_BLE_SET_DEFAULT_PHY (0x0031 | HCI_GRP_BLE_CMDS) +#define HCI_BLE_SET_PHY (0x0032 | HCI_GRP_BLE_CMDS) +#define HCI_BLE_ENH_RX_TEST (0x0033 | HCI_GRP_BLE_CMDS) +#define HCI_BLE_ENH_TX_TEST (0x0034 | HCI_GRP_BLE_CMDS) +#define HCI_BLE_SET_ADV_RAND_ADDR (0x0035 | HCI_GRP_BLE_CMDS) +#define HCI_BLE_SET_EXT_ADV_PARAM (0x0036 | HCI_GRP_BLE_CMDS) +#define HCI_BLE_SET_EXT_ADV_DATA (0x0037 | HCI_GRP_BLE_CMDS) +#define HCI_BLE_SET_EXT_SCAN_RSP_DATA (0x0038 | HCI_GRP_BLE_CMDS) +#define HCI_BLE_SET_EXT_ADV_ENABLE (0x0039 | HCI_GRP_BLE_CMDS) +#define HCI_BLE_RD_MAX_ADV_DATA_LEN (0x003A | HCI_GRP_BLE_CMDS) +#define HCI_BLE_RD_NUM_OF_ADV_SETS (0x003B | HCI_GRP_BLE_CMDS) +#define HCI_BLE_REMOVE_ADV_SET (0x003C | HCI_GRP_BLE_CMDS) +#define HCI_BLE_CLEAR_ADV_SETS (0x003D | HCI_GRP_BLE_CMDS) +#define HCI_BLE_SET_PERIOD_ADV_PARAMS (0x003E | HCI_GRP_BLE_CMDS) +#define HCI_BLE_SET_PERIOD_ADV_DATA (0x003F | HCI_GRP_BLE_CMDS) +#define HCI_BLE_SET_PERIOD_ADV_ENABLE (0x0040 | HCI_GRP_BLE_CMDS) +#define HCI_BLE_SET_EXT_SCAN_PARAMS (0x0041 | HCI_GRP_BLE_CMDS) +#define HCI_BLE_SET_EXT_SCAN_ENABLE (0x0042 | HCI_GRP_BLE_CMDS) +#define HCI_BLE_EXT_CREATE_CONN (0x0043 | HCI_GRP_BLE_CMDS) +#define HCI_BLE_PERIOD_ADV_CREATE_SYNC (0x0044 | HCI_GRP_BLE_CMDS) +#define HCI_BLE_PERIOD_ADV_CREATE_SYNC_CANCEL (0x0045 | HCI_GRP_BLE_CMDS) +#define HCI_BLE_PERIOD_ADV_TERM_SYNC (0x0046 | HCI_GRP_BLE_CMDS) +#define HCI_BLE_ADV_DEV_TO_PERIOD_ADV_LIST (0x0047 | HCI_GRP_BLE_CMDS) +#define HCI_BLE_REMOVE_DEV_FROM_PERIOD_ADV_LIST (0x0048 | HCI_GRP_BLE_CMDS) +#define HCI_BLE_CLEAR_PERIOD_ADV_LIST (0x0049 | HCI_GRP_BLE_CMDS) +#define HCI_BLE_RD_PERIOD_ADV_LIST_SIZE (0x004A | HCI_GRP_BLE_CMDS) +#define HCI_BLE_RD_TRANSMIT_POWER (0x004B | HCI_GRP_BLE_CMDS) +#define HCI_BLE_RD_RF_PATH_COMPENSATION (0x004C | HCI_GRP_BLE_CMDS) +#define HCI_BLE_WR_RF_PATH_COMPENSATION (0x004D | HCI_GRP_BLE_CMDS) +#define HCI_BLE_SET_PRIVACY_MODE (0x004E | HCI_GRP_BLE_CMDS) +#endif // #if (BLE_50_FEATURE_SUPPORT == TRUE) +#if (BLE_FEAT_PERIODIC_ADV_SYNC_TRANSFER == TRUE) +#define HCI_BLE_SET_PERIOD_ADV_RECV_ENABLE (0x0059 | HCI_GRP_BLE_CMDS) +#define HCI_BLE_PERIOD_ADV_SYNC_TRANS (0x005A | HCI_GRP_BLE_CMDS) +#define HCI_BLE_PERIOD_ADV_SET_INFO_TRANS (0x005B | HCI_GRP_BLE_CMDS) +#define HCI_BLE_SET_PAST_PARAMS (0x005C | HCI_GRP_BLE_CMDS) +#define HCI_BLE_SET_DEFAULT_PAST_PARAMS (0x005D | HCI_GRP_BLE_CMDS) +#endif // #if (BLE_FEAT_PERIODIC_ADV_SYNC_TRANSFER == TRUE) +// Vendor OGF define +#define HCI_VENDOR_OGF 0x3F + +// ESP vendor group define +#define HCI_ESP_GROUP_COMMON 0x01 +#define HCI_ESP_GROUP_BLE 0x02 +#define HCI_ESP_GROUP_BT 0x03 +#define HCI_ESP_GROUP_END 0x07 + +//ESP common subcode define +#define HCI_SUBCODE_COMMON_INIT 0x00 +#define HCI_SUBCODE_COMMON_ECHO 0x01 +#define HCI_SUBCODE_COMMON_COEX_STATUS 0x02 +#define HCI_SUBCODE_COMMON_MAX 0x7F + +//ESP BLE subcode define +#define HCI_SUBCODE_BLE_INIT 0x00 +#define HCI_SUBCODE_BLE_MULTI_ADV 0x01 +#define HCI_SUBCODE_BLE_BATCH_SCAN 0x02 +#define HCI_SUBCODE_BLE_ADV_FILTER 0x03 +#define HCI_SUBCODE_BLE_TRACK_ADV 0x04 +#define HCI_SUBCODE_BLE_ENERGY_INFO 0x05 +#define HCI_SUBCODE_BLE_EXTENDED_SCAN_PARAMS 0x06 +#define HCI_SUBCODE_BLE_LONG_ADV 0x07 +#define HCI_SUBCODE_BLE_DUPLICATE_EXCEPTIONAL_LIST 0x08 +#define HCI_SUBCODE_BLE_SET_ADV_FLOW_CONTROL 0x09 +#define HCI_SUBCODE_BLE_ADV_REPORT_FLOW_CONTROL 0x0A +#define HCI_SUBCODE_BLE_RD_STATIC_ADDR 0x0B +#define HCI_SUBCODE_BLE_CLEAR_ADV 0x0C +#define HCI_SUBCODE_BLE_MAX 0x7F + +//ESP BT subcode define +#define HCI_SUBCODE_BT_INIT 0x00 +#define HCI_SUBCODE_BT_MAX 0x7F + +#define HCI_ESP_VENDOR_OPCODE_BUILD(ogf, group, subcode) ((ogf << 10) | (group <<7) | (subcode << 0)) +/* +* | OGF | VENDIOR GROUP | GROUP SUBCODE | +* | 1 1 1 1 1 1 | 0 0 0 | X X X X X X X | Already Exist +* | 1 1 1 1 1 1 | 0 0 1 | X X X X X X X | ESP VENDOR COMMON HCI CMD +* | 1 1 1 1 1 1 | 0 1 0 | X X X X X X X | ESP VENDOR BLE HCI CMD +* | 1 1 1 1 1 1 | 0 1 1 | X X X X X X X | ESP VENDOR BT HCI CMD +* | 1 1 1 1 1 1 | 1 0 0 | X X X X X X X | RESERVED FOR FUTURE USE +* | 1 1 1 1 1 1 | 1 0 1 | X X X X X X X | RESERVED FOR FUTURE USE +* | 1 1 1 1 1 1 | 1 1 0 | X X X X X X X | RESERVED FOR FUTURE USE +* | 1 1 1 1 1 1 | 1 1 1 | X X X X X X X | RESERVED FOR FUTURE USE +*/ + +// ESP COMMON HCI CMD +#define HCI_VENDOR_COMMON_ECHO_CMD_OPCODE HCI_ESP_VENDOR_OPCODE_BUILD(HCI_VENDOR_OGF, HCI_ESP_GROUP_COMMON, HCI_SUBCODE_COMMON_ECHO) +// Set/clear coex schm status +#define HCI_VENDOR_COMMON_COEX_STATUS_CMD_OPCODE HCI_ESP_VENDOR_OPCODE_BUILD(HCI_VENDOR_OGF, HCI_ESP_GROUP_COMMON, HCI_SUBCODE_COMMON_COEX_STATUS) + +//ESP BLE HCI CMD +/* Multi adv OCF */ +#define HCI_BLE_MULTI_ADV_OCF HCI_ESP_VENDOR_OPCODE_BUILD(HCI_VENDOR_OGF, HCI_ESP_GROUP_BLE, HCI_SUBCODE_BLE_MULTI_ADV) +/* Batch scan OCF */ +#define HCI_BLE_BATCH_SCAN_OCF HCI_ESP_VENDOR_OPCODE_BUILD(HCI_VENDOR_OGF, HCI_ESP_GROUP_BLE, HCI_SUBCODE_BLE_BATCH_SCAN) +/* ADV filter OCF */ +#define HCI_BLE_ADV_FILTER_OCF HCI_ESP_VENDOR_OPCODE_BUILD(HCI_VENDOR_OGF, HCI_ESP_GROUP_BLE, HCI_SUBCODE_BLE_ADV_FILTER) +/* Tracking OCF */ +#define HCI_BLE_TRACK_ADV_OCF HCI_ESP_VENDOR_OPCODE_BUILD(HCI_VENDOR_OGF, HCI_ESP_GROUP_BLE, HCI_SUBCODE_BLE_TRACK_ADV) +/* Energy info OCF */ +#define HCI_BLE_ENERGY_INFO_OCF HCI_ESP_VENDOR_OPCODE_BUILD(HCI_VENDOR_OGF, HCI_ESP_GROUP_BLE, HCI_SUBCODE_BLE_ENERGY_INFO) +/* Extended BLE Scan parameters OCF */ +#define HCI_BLE_EXTENDED_SCAN_PARAMS_OCF HCI_ESP_VENDOR_OPCODE_BUILD(HCI_VENDOR_OGF, HCI_ESP_GROUP_BLE, HCI_SUBCODE_BLE_EXTENDED_SCAN_PARAMS) +/* Long BLE Adv data OCF */ +#define HCI_VENDOR_BLE_LONG_ADV_DATA HCI_ESP_VENDOR_OPCODE_BUILD(HCI_VENDOR_OGF, HCI_ESP_GROUP_BLE, HCI_SUBCODE_BLE_LONG_ADV) +/* BLE update duplicate scan exceptional list */ +#define HCI_VENDOR_BLE_UPDATE_DUPLICATE_EXCEPTIONAL_LIST HCI_ESP_VENDOR_OPCODE_BUILD(HCI_VENDOR_OGF, HCI_ESP_GROUP_BLE, HCI_SUBCODE_BLE_DUPLICATE_EXCEPTIONAL_LIST) +#define HCI_VENDOR_BLE_SET_ADV_FLOW_CONTROL HCI_ESP_VENDOR_OPCODE_BUILD(HCI_VENDOR_OGF, HCI_ESP_GROUP_BLE, HCI_SUBCODE_BLE_SET_ADV_FLOW_CONTROL) +#define HCI_VENDOR_BLE_ADV_REPORT_FLOW_CONTROL HCI_ESP_VENDOR_OPCODE_BUILD(HCI_VENDOR_OGF, HCI_ESP_GROUP_BLE, HCI_SUBCODE_BLE_ADV_REPORT_FLOW_CONTROL) +/* BLE clear legacy advertising */ +#define HCI_VENDOR_BLE_CLEAR_ADV HCI_ESP_VENDOR_OPCODE_BUILD(HCI_VENDOR_OGF, HCI_ESP_GROUP_BLE, HCI_SUBCODE_BLE_CLEAR_ADV) +//ESP BT HCI CMD + +/* subcode for multi adv feature */ +#define BTM_BLE_MULTI_ADV_SET_PARAM 0x01 +#define BTM_BLE_MULTI_ADV_WRITE_ADV_DATA 0x02 +#define BTM_BLE_MULTI_ADV_WRITE_SCAN_RSP_DATA 0x03 +#define BTM_BLE_MULTI_ADV_SET_RANDOM_ADDR 0x04 +#define BTM_BLE_MULTI_ADV_ENB 0x05 + +/* multi adv VSE subcode */ +#define HCI_VSE_SUBCODE_BLE_MULTI_ADV_ST_CHG 0x55 /* multi adv instance state change */ + +/* subcode for batch scan feature */ +#define BTM_BLE_BATCH_SCAN_ENB_DISAB_CUST_FEATURE 0x01 +#define BTM_BLE_BATCH_SCAN_SET_STORAGE_PARAM 0x02 +#define BTM_BLE_BATCH_SCAN_SET_PARAMS 0x03 +#define BTM_BLE_BATCH_SCAN_READ_RESULTS 0x04 + +/* batch scan VSE subcode */ +#define HCI_VSE_SUBCODE_BLE_THRESHOLD_SUB_EVT 0x54 /* Threshold event */ + +/* tracking sub event */ +#define HCI_VSE_SUBCODE_BLE_TRACKING_SUB_EVT 0x56 /* Tracking event */ + +/* LE supported states definition */ +#define HCI_LE_ADV_STATE 0x00000001 +#define HCI_LE_SCAN_STATE 0x00000002 +#define HCI_LE_INIT_STATE 0x00000004 +#define HCI_LE_CONN_SL_STATE 0x00000008 +#define HCI_LE_ADV_SCAN_STATE 0x00000010 +#define HCI_LE_ADV_INIT_STATE 0x00000020 +#define HCI_LE_ADV_MA_STATE 0x00000040 +#define HCI_LE_ADV_SL_STATE 0x00000080 +#define HCI_LE_SCAN_INIT_STATE 0x00000100 +#define HCI_LE_SCAN_MA_STATE 0x00000200 +#define HCI_LE_SCAN_SL_STATE 0x00000400 +#define HCI_LE_INIT_MA_STATE 0x00000800 + +/* LE Supported States */ +/* Non Connectable Adv state is supported. 0x0000000000000001 */ +#define HCI_SUPP_LE_STATES_NON_CONN_ADV_MASK 0x01 +#define HCI_SUPP_LE_STATES_NON_CONN_ADV_OFF 0 +#define HCI_LE_STATES_NON_CONN_ADV_SUPPORTED(x) ((x)[HCI_SUPP_LE_STATES_NON_CONN_ADV_OFF] & HCI_SUPP_LE_STATES_NON_CONN_ADV_MASK) + +/*Scanneable Connectable Adv state is supported. 0x0000000000000002 */ +#define HCI_SUPP_LE_STATES_SCAN_ADV_MASK 0x02 +#define HCI_SUPP_LE_STATESSCAN_ADV_OFF 0 +#define HCI_LE_STATES_SCAN_ADV_SUPPORTED(x) ((x)[HCI_SUPP_LE_STATESSCAN_ADV_OFF] & HCI_SUPP_LE_STATES_SCAN_ADV_MASK) + +/* Connectable Adv state is supported. 0x0000000000000004 */ +#define HCI_SUPP_LE_STATES_CONN_ADV_MASK 0x04 +#define HCI_SUPP_LE_STATES_CONN_ADV_OFF 0 +#define HCI_LE_STATES_CONN_ADV_SUPPORTED(x) ((x)[HCI_SUPP_LE_STATES_CONN_ADV_OFF] & HCI_SUPP_LE_STATES_CONN_ADV_MASK) + +/* Hi duty Cycle Directed Adv state is supported. 0x0000000000000008 */ +#define HCI_SUPP_LE_STATES_HI_DUTY_DIR_ADV_MASK 0x08 +#define HCI_SUPP_LE_STATES_HI_DUTY_DIR_ADV_OFF 0 +#define HCI_LE_STATES_HI_DUTY_DIR_ADV_SUPPORTED(x) ((x)[HCI_SUPP_LE_STATES_HI_DUTY_DIR_ADV_OFF] & HCI_SUPP_LE_STATES_HI_DUTY_DIR_ADV_MASK) + +/* Passive Scan state is supported. 0x0000000000000010 */ +#define HCI_SUPP_LE_STATES_PASS_SCAN_MASK 0x10 +#define HCI_SUPP_LE_STATES_PASS_SCAN_OFF 0 +#define HCI_LE_STATES_PASS_SCAN_SUPPORTED(x) ((x)[HCI_SUPP_LE_STATES_PASS_SCAN_OFF] & HCI_SUPP_LE_STATES_PASS_SCAN_MASK) + +/* Active Scan state is supported. 0x0000000000000020 */ +#define HCI_SUPP_LE_STATES_ACTIVE_SCAN_MASK 0x20 +#define HCI_SUPP_LE_STATES_ACTIVE_SCAN_OFF 0 +#define HCI_LE_STATES_ACTIVE_SCAN_SUPPORTED(x) ((x)[HCI_SUPP_LE_STATES_ACTIVE_SCAN_OFF] & HCI_SUPP_LE_STATES_ACTIVE_SCAN_MASK) + +/* Initiating state is supported. 0x0000000000000040 (or connection state in master role is also supported) */ +#define HCI_SUPP_LE_STATES_INIT_MASK 0x40 +#define HCI_SUPP_LE_STATES_INIT_OFF 0 +#define HCI_LE_STATES_INIT_SUPPORTED(x) ((x)[HCI_SUPP_LE_STATES_INIT_OFF] & HCI_SUPP_LE_STATES_INIT_MASK) + +/*connection state in slave role is also supported. 0x0000000000000080 */ +#define HCI_SUPP_LE_STATES_SLAVE_MASK 0x80 +#define HCI_SUPP_LE_STATES_SLAVE_OFF 0 +#define HCI_LE_STATES_SLAVE_SUPPORTED(x) ((x)[HCI_SUPP_LE_STATES_SLAVE_OFF] & HCI_SUPP_LE_STATES_SLAVE_MASK) + +/* Non Connectable Adv state and Passive Scanning State combination is supported. 0x0000000000000100 */ +#define HCI_SUPP_LE_STATES_NON_CONN_ADV_PASS_SCAN_MASK 0x01 +#define HCI_SUPP_LE_STATES_NON_CONN_ADV_PASS_SCAN_OFF 1 +#define HCI_LE_STATES_NON_CONN_ADV_PASS_SCAN_SUPPORTED(x) ((x)[HCI_SUPP_LE_STATES_NON_CONN_ADV_PASS_SCAN_OFF] & HCI_SUPP_LE_STATES_NON_CONN_ADV_PASS_SCAN_MASK) + +/*Scannable Adv state and Passive Scanning State combination is supported. 0x0000000000000200 */ +#define HCI_SUPP_LE_STATES_SCAN_ADV_PASS_SCAN_MASK 0x02 +#define HCI_SUPP_LE_STATES_SCAN_ADV_PASS_SCAN_OFF 1 +#define HCI_LE_STATES_SCAN_ADV_PASS_SCAN_SUPPORTED(x) ((x)[HCI_SUPP_LE_STATES_SCAN_ADV_PASS_SCAN_OFF] & HCI_SUPP_LE_STATES_SCAN_ADV_PASS_SCAN_MASK) + +/*Connectable Adv state and Passive Scanning State combination is supported. 0x0000000000000400 */ +#define HCI_SUPP_LE_STATES_CONN_ADV_PASS_SCAN_MASK 0x04 +#define HCI_SUPP_LE_STATES_CONN_ADV_PASS_SCAN_OFF 1 +#define HCI_LE_STATES_CONN_ADV_PASS_SCAN_SUPPORTED(x) ((x)[HCI_SUPP_LE_STATES_CONN_ADV_PASS_SCAN_OFF] & HCI_SUPP_LE_STATES_CONN_ADV_PASS_SCAN_MASK) + +/*High Duty Cycl Directed ADv and Passive Scanning State combination is supported. 0x0000000000000800 */ +#define HCI_SUPP_LE_STATES_HI_DUTY_DIR_ADV_PASS_SCAN_MASK 0x08 +#define HCI_SUPP_LE_STATES_HI_DUTY_DIR_ADV_PASS_SCAN_OFF 1 +#define HCI_LE_STATES_HI_DUTY_DIR_ADV_PASS_SCAN_SUPPORTED(x) ((x)[HCI_SUPP_LE_STATES_HI_DUTY_DIR_ADV_PASS_SCAN_MASK] & HCI_SUPP_LE_STATES_HI_DUTY_DIR_ADV_PASS_SCAN_OFF) + +/*Non Connectable Adv state and Passive Scanning State combination is supported. 0x0000000000001000 */ +#define HCI_SUPP_LE_STATES_NON_CONN_ADV_ACTIVE_SCAN_MASK 0x10 +#define HCI_SUPP_LE_STATES_NON_CONN_ADV_ACTIVE_SCAN_OFF 1 +#define HCI_LE_STATES_NON_CONN_ADV_ACTIVE_SCAN_SUPPORTED(x) ((x)[HCI_SUPP_LE_STATES_NON_CONN_ADV_ACTIVE_SCAN_OFF] & HCI_SUPP_LE_STATES_NON_CONN_ADV_ACTIVE_SCAN_MASK) + +/*Scannable Adv state and Active Scanning State combination is supported. 0x0000000000002000 */ +#define HCI_SUPP_LE_STATES_SCAN_ADV_ACTIVE_SCAN_MASK 0x20 +#define HCI_SUPP_LE_STATES_SCAN_ADV_ACTIVE_SCAN_OFF 1 +#define HCI_LE_STATES_SCAN_ADV_ACTIVE_SCAN_SUPPORTED(x) ((x)[HCI_SUPP_LE_STATES_SCAN_ADV_ACTIVE_SCAN_OFF] & HCI_SUPP_LE_STATES_SCAN_ADV_ACTIVE_SCAN_MASK) + +/*Connectable Adv state and Active Scanning State combination is supported. 0x0000000000004000 */ +#define HCI_SUPP_LE_STATES_CONN_ADV_ACTIVE_SCAN_MASK 0x40 +#define HCI_SUPP_LE_STATES_CONN_ADV_ACTIVE_SCAN_OFF 1 +#define HCI_LE_STATES_CONN_ADV_ACTIVE_SCAN_SUPPORTED(x) ((x)[HCI_SUPP_LE_STATES_CONN_ADV_ACTIVE_SCAN_OFF] & HCI_SUPP_LE_STATES_CONN_ADV_ACTIVE_SCAN_MASK) + +/*High Duty Cycl Directed ADv and ACtive Scanning State combination is supported. 0x0000000000008000 */ +#define HCI_SUPP_LE_STATES_HI_DUTY_DIR_ADV_ACTIVE_SCAN_MASK 0x80 +#define HCI_SUPP_LE_STATES_HI_DUTY_DIR_ADV_ACTIVE_SCAN_OFF 1 +#define HCI_LE_STATES_HI_DUTY_DIR_ADV_ACTIVE_SCAN_SUPPORTED(x) ((x)[HCI_SUPP_LE_STATES_HI_DUTY_DIR_ADV_ACTIVE_SCAN_MASK] & HCI_SUPP_LE_STATES_HI_DUTY_DIR_ADV_ACTIVE_SCAN_OFF) + +/*Non-Connectable Adv state and Initiating State combination is supported. 0x0000000000010000 */ +#define HCI_SUPP_LE_STATES_NON_CONN_INIT_MASK 0x01 +#define HCI_SUPP_LE_STATES_NON_CONN_INIT_OFF 2 +#define HCI_LE_STATES_NON_CONN_INIT_SUPPORTED(x) ((x)[HCI_SUPP_LE_STATES_NON_CONN_INIT_OFF] & HCI_SUPP_LE_STATES_NON_CONN_INIT_MASK) + +/* Scannable Adv state and Initiating State combination is supported. 0x0000000000020000 */ +#define HCI_SUPP_LE_STATES_SCAN_ADV_INIT_MASK 0x02 +#define HCI_SUPP_LE_STATES_SCAN_ADV_INIT_OFF 2 +#define HCI_LE_STATES_SCAN_ADV_INIT_SUPPORTED(x) ((x)[HCI_SUPP_LE_STATES_SCAN_ADV_INIT_OFF] & HCI_SUPP_LE_STATES_SCAN_ADV_INIT_MASK) + +/* Non-Connectable Adv state and Master Role combination is supported. 0x0000000000040000 */ +#define HCI_SUPP_LE_STATES_NON_CONN_ADV_MASTER_MASK 0x04 +#define HCI_SUPP_LE_STATES_NON_CONN_ADV_MASTER_OFF 2 +#define HCI_LE_STATES_NON_CONN_ADV_MASTER_SUPPORTED(x) ((x)[HCI_SUPP_LE_STATES_NON_CONN_ADV_MASTER_OFF] & HCI_SUPP_LE_STATES_NON_CONN_ADV_MASTER_MASK) + +/*Scannable Adv state and Master Role combination is supported. 0x0000000000040000 */ +#define HCI_SUPP_LE_STATES_SCAN_ADV_MASTER_MASK 0x08 +#define HCI_SUPP_LE_STATES_SCAN_ADV_MASTER_OFF 2 +#define HCI_LE_STATES_SCAN_ADV_MASTER_SUPPORTED(x) ((x)[HCI_SUPP_LE_STATES_SCAN_ADV_MASTER_OFF] & HCI_SUPP_LE_STATES_SCAN_ADV_MASTER_MASK) + +/* Non-Connectable Adv and Slave Role combination is supported. 0x000000000100000 */ +#define HCI_SUPP_LE_STATES_NON_CONN_ADV_SLAVE_MASK 0x10 +#define HCI_SUPP_LE_STATES_NON_CONN_ADV_SLAVE_OFF 2 +#define HCI_LE_STATES_NON_CONN_ADV_SLAVE_SUPPORTED(x) ((x)[HCI_SUPP_LE_STATES_NON_CONN_ADV_SLAVE_OFF] & HCI_SUPP_LE_STATES_NON_CONN_ADV_SLAVE_MASK) + +/*Scannable Adv and Slave Role combination is supported. 0x000000000200000 */ +#define HCI_SUPP_LE_STATES_SCAN_ADV_SLAVE_MASK 0x20 +#define HCI_SUPP_LE_STATES_SCAN_ADV_SLAVE_OFF 2 +#define HCI_LE_STATES_SCAN_ADV_SLAVE_SUPPORTED(x) ((x)[HCI_SUPP_LE_STATES_SCAN_ADV_SLAVE_OFF] & HCI_SUPP_LE_STATES_SCAN_ADV_SLAVE_MASK) + +/*Passive Scan and Initiating State combination is supported. 0x000000000400000 */ +#define HCI_SUPP_LE_STATES_PASS_SCAN_INIT_MASK 0x40 +#define HCI_SUPP_LE_STATES_PASS_SCAN_INIT_OFF 2 +#define HCI_LE_STATES_PASS_SCAN_INIT_SUPPORTED(x) ((x)[HCI_SUPP_LE_STATES_PASS_SCAN_INIT_OFF] & HCI_SUPP_LE_STATES_PASS_SCAN_INIT_MASK) + +/*Active Scan and Initiating State combination is supported. 0x000000000800000 */ +#define HCI_SUPP_LE_STATES_ACTIVE_SCAN_INIT_MASK 0x80 +#define HCI_SUPP_LE_STATES_ACTIVE_SCAN_INIT_OFF 2 +#define HCI_LE_STATES_ACTIVE_SCAN_INIT_SUPPORTED(x) ((x)[HCI_SUPP_LE_STATES_ACTIVE_SCAN_INIT_OFF] & HCI_SUPP_LE_STATES_ACTIVE_SCAN_INIT_MASK) + +/*Passive Scan and Master Role combination is supported. 0x000000001000000 */ +#define HCI_SUPP_LE_STATES_PASS_SCAN_MASTER_MASK 0x01 +#define HCI_SUPP_LE_STATES_PASS_SCAN_MASTER_OFF 3 +#define HCI_LE_STATES_PASS_SCAN_MASTER_SUPPORTED(x) ((x)[HCI_SUPP_LE_STATES_PASS_SCAN_MASTER_OFF] & HCI_SUPP_LE_STATES_PASS_SCAN_MASTER_MASK) + +/*Active Scan and Master Role combination is supported. 0x000000002000000 */ +#define HCI_SUPP_LE_STATES_ACTIVE_SCAN_MASTER_MASK 0x02 +#define HCI_SUPP_LE_STATES_ACTIVE_SCAN_MASTER_OFF 3 +#define HCI_LE_STATES_ACTIVE_SCAN_MASTER_SUPPORTED(x) ((x)[HCI_SUPP_LE_STATES_ACTIVE_SCAN_MASTER_OFF] & HCI_SUPP_LE_STATES_ACTIVE_SCAN_MASTER_MASK) + +/*Passive Scan and Slave Role combination is supported. 0x000000004000000 */ +#define HCI_SUPP_LE_STATES_PASS_SCAN_SLAVE_MASK 0x04 +#define HCI_SUPP_LE_STATES_PASS_SCAN_SLAVE_OFF 3 +#define HCI_LE_STATES_PASS_SCAN_SLAVE_SUPPORTED(x) ((x)[HCI_SUPP_LE_STATES_PASS_SCAN_SLAVE_OFF] & HCI_SUPP_LE_STATES_PASS_SCAN_SLAVE_MASK) + +/*Active Scan and Slave Role combination is supported. 0x000000008000000 */ +#define HCI_SUPP_LE_STATES_ACTIVE_SCAN_SLAVE_MASK 0x08 +#define HCI_SUPP_LE_STATES_ACTIVE_SCAN_SLAVE_OFF 3 +#define HCI_LE_STATES_ACTIVE_SCAN_SLAVE_SUPPORTED(x) ((x)[HCI_SUPP_LE_STATES_ACTIVE_SCAN_SLAVE_OFF] & HCI_SUPP_LE_STATES_ACTIVE_SCAN_SLAVE_MASK) + +/*Link Layer Topology Added States Combo */ +/*Initiating State and Master Role combination supported. + Master Role and Master Role combination is also supported. 0x0000000010000000 */ +#define HCI_SUPP_LE_STATES_INIT_MASTER_MASK 0x10 +#define HCI_SUPP_LE_STATES_INIT_MASTER_OFF 3 +#define HCI_LE_STATES_INIT_MASTER_SUPPORTED(x) ((x)[HCI_SUPP_LE_STATES_INIT_MASTER_OFF] & HCI_SUPP_LE_STATES_INIT_MASTER_MASK) + +/*Low Duty Cycle Directed Advertising State . 0x0000000020000000 */ +#define HCI_SUPP_LE_STATES_LO_DUTY_DIR_ADV_MASK 0x20 +#define HCI_SUPP_LE_STATES_LO_DUTY_DIR_ADV_OFF 3 +#define HCI_LE_STATES_LOW_DUTY_DIR_ADV_SUPPORTED(x) ((x)[HCI_SUPP_LE_STATES_LOW_DUTY_DIR_ADV_OFF] & HCI_SUPP_LE_STATES_LOW_DUTY_DIR_ADV_MASK) + +/*Low Duty Cycle Directed Advertising State and Passive scan combination. 0x0000000040000000 */ +#define HCI_SUPP_LE_STATES_LO_DUTY_DIR_ADV_PASS_SCAN_MASK 0x40 +#define HCI_SUPP_LE_STATES_LO_DUTY_DIR_ADV_PASS_SCAN_OFF 3 +#define HCI_LE_STATES_LO_DUTY_DIR_ADV_PASS_SCAN_SUPPORTED(x) ((x)[HCI_SUPP_LE_STATES_LO_DUTY_DIR_ADV_PASS_SCAN_OFF] & HCI_SUPP_LE_STATES_LO_DUTY_DIR_ADV_PASS_SCAN_MASK) + +/*Low Duty Cycle Directed Advertising State and Active scan combination . 0x0000000080000000 */ +#define HCI_SUPP_LE_STATES_LO_DUTY_DIR_ADV_ACTIVE_SCAN_MASK 0x80 +#define HCI_SUPP_LE_STATES_LO_DUTY_DIR_ADV_ACTIVE_SCAN_OFF 3 +#define HCI_LE_STATES_LO_DUTY_DIR_ADV_ACTIVE_SCAN_SUPPORTED(x) ((x)[HCI_SUPP_LE_STATES_LO_DUTY_DIR_ADV_ACTIVE_SCAN_OFF] & HCI_SUPP_LE_STATES_LO_DUTY_DIR_ADV_ACTIVE_SCAN_MASK) + +/* Connectable Advertising State and Initiating State combination supported. 0x0000000100000000 */ +#define HCI_SUPP_LE_STATES_CONN_ADV_INIT_MASK 0x01 +#define HCI_SUPP_LE_STATES_CONN_ADV_INIT_OFF 4 +#define HCI_LE_STATES_CONN_ADV_INIT_SUPPORTED(x) ((x)[HCI_SUPP_LE_STATES_CONN_ADV_INIT_OFF] & HCI_SUPP_LE_STATES_CONN_ADV_INIT_MASK) + +/* High Duty Cycle Directed Advertising State and Initiating State combination supported. */ +#define HCI_SUPP_LE_STATES_HI_DUTY_DIR_ADV_INIT_MASK 0x02 +#define HCI_SUPP_LE_STATES_HI_DUTY_DIR_ADV_INIT_OFF 4 +#define HCI_LE_STATES_HI_DUTY_DIR_ADV_INIT_SUPPORTED(x) ((x)[HCI_SUPP_LE_STATES_HI_DUTY_DIR_ADV_INIT_OFF] & HCI_SUPP_LE_STATES_HI_DUTY_DIR_ADV_INIT_MASK) + +/* Low Duty Cycle Directed Advertising State and Initiating State combination supported.*/ +#define HCI_SUPP_LE_STATES_LO_DUTY_DIR_ADV_INIT_MASK 0x04 +#define HCI_SUPP_LE_STATES_LO_DUTY_DIR_ADV_INIT_OFF 4 +#define HCI_LE_STATES_LO_DUTY_DIR_ADV_INIT_SUPPORTED(x) ((x)[HCI_SUPP_LE_STATES_LO_DUTY_DIR_ADV_INIT_OFF] & HCI_SUPP_LE_STATES_LO_DUTY_DIR_ADV_INIT_MASK) + +/* Connectable Advertising State and Master Role combination supported.*/ +#define HCI_SUPP_LE_STATES_CONN_ADV_MASTER_MASK 0x08 +#define HCI_SUPP_LE_STATES_CONN_ADV_MASTER_OFF 4 +#define HCI_LE_STATES_CONN_ADV_MASTER_SUPPORTED(x) ((x)[HCI_SUPP_LE_STATES_CONN_ADV_MASTER_OFF] & HCI_SUPP_LE_STATES_CONN_ADV_MASTER_MASK) + +/* High Duty Cycle Directed Advertising State and Master Role combination supported.*/ +#define HCI_SUPP_LE_STATES_HI_DUTY_DIR_ADV_MASTER_MASK 0x10 +#define HCI_SUPP_LE_STATES_HI_DUTY_DIR_ADV_MASTER_OFF 4 +#define HCI_LE_STATES_HI_DUTY_DIR_ADV_MASTER_SUPPORTED(x) ((x)[HCI_SUPP_LE_STATES_HI_DUTY_DIR_ADV_MASTER_OFF] & HCI_SUPP_LE_STATES_HI_DUTY_DIR_ADV_MASTER_MASK) + +/* Low Duty Cycle Directed Advertising State and Master Role combination supported.*/ +#define HCI_SUPP_LE_STATES_LO_DUTY_DIR_ADV_MASTER_MASK 0x20 +#define HCI_SUPP_LE_STATES_LO_DUTY_DIR_ADV_MASTER_OFF 4 +#define HCI_LE_STATES_LO_DUTY_DIR_ADV_MASTER_SUPPORTED(x) ((x)[HCI_SUPP_LE_STATES_LO_DUTY_DIR_ADV_MASTER_OFF] & HCI_SUPP_LE_STATES_LO_DUTY_DIR_ADV_MASTER_MASK) + +/* Connectable Advertising State and Slave Role combination supported. */ +#define HCI_SUPP_LE_STATES_CONN_ADV_SLAVE_MASK 0x40 +#define HCI_SUPP_LE_STATES_CONN_ADV_SLAVE_OFF 4 +#define HCI_LE_STATES_CONN_ADV_SLAVE_SUPPORTED(x) ((x)[HCI_SUPP_LE_STATES_CONN_ADV_SLAVE_OFF] & HCI_SUPP_LE_STATES_CONN_ADV_SLAVE_MASK) + +/* High Duty Cycle Directed Advertising State and slave Role combination supported.*/ +#define HCI_SUPP_LE_STATES_HI_DUTY_DIR_ADV_SLAVE_MASK 0x80 +#define HCI_SUPP_LE_STATES_HI_DUTY_DIR_ADV_SLAVE_OFF 4 +#define HCI_LE_STATES_HI_DUTY_DIR_ADV_SLAVE_SUPPORTED(x) ((x)[HCI_SUPP_LE_STATES_HI_DUTY_DIR_ADV_SLAVE_OFF] & HCI_SUPP_LE_STATES_HI_DUTY_DIR_ADV_SLAVE_MASK) + +/* Low Duty Cycle Directed Advertising State and slave Role combination supported.*/ +#define HCI_SUPP_LE_STATES_LO_DUTY_DIR_ADV_SLAVE_MASK 0x01 +#define HCI_SUPP_LE_STATES_LO_DUTY_DIR_ADV_SLAVE_OFF 5 +#define HCI_LE_STATES_LO_DUTY_DIR_ADV_SLAVE_SUPPORTED(x) ((x)[HCI_SUPP_LE_STATES_LO_DUTY_DIR_ADV_SLAVE_OFF] & HCI_SUPP_LE_STATES_LO_DUTY_DIR_ADV_SLAVE_MASK) + +/* Initiating State and Slave Role combination supported. + Master Role and Slave Role combination also supported. + */ +#define HCI_SUPP_LE_STATES_INIT_MASTER_SLAVE_MASK 0x02 +#define HCI_SUPP_LE_STATES_INIT_MASTER_SLAVE_OFF 5 +#define HCI_LE_STATES_INIT_MASTER_SLAVE_SUPPORTED(x) ((x)[HCI_SUPP_LE_STATES_INIT_MASTER_SLAVE_OFF] & HCI_SUPP_LE_STATES_INIT_MASTER_SLAVE_MASK) + +/* +** Definitions for HCI Events +*/ +#define HCI_INQUIRY_COMP_EVT 0x01 +#define HCI_INQUIRY_RESULT_EVT 0x02 +#define HCI_CONNECTION_COMP_EVT 0x03 +#define HCI_CONNECTION_REQUEST_EVT 0x04 +#define HCI_DISCONNECTION_COMP_EVT 0x05 +#define HCI_AUTHENTICATION_COMP_EVT 0x06 +#define HCI_RMT_NAME_REQUEST_COMP_EVT 0x07 +#define HCI_ENCRYPTION_CHANGE_EVT 0x08 +#define HCI_CHANGE_CONN_LINK_KEY_EVT 0x09 +#define HCI_MASTER_LINK_KEY_COMP_EVT 0x0A +#define HCI_READ_RMT_FEATURES_COMP_EVT 0x0B +#define HCI_READ_RMT_VERSION_COMP_EVT 0x0C +#define HCI_QOS_SETUP_COMP_EVT 0x0D +#define HCI_COMMAND_COMPLETE_EVT 0x0E +#define HCI_COMMAND_STATUS_EVT 0x0F +#define HCI_HARDWARE_ERROR_EVT 0x10 +#define HCI_FLUSH_OCCURED_EVT 0x11 +#define HCI_ROLE_CHANGE_EVT 0x12 +#define HCI_NUM_COMPL_DATA_PKTS_EVT 0x13 +#define HCI_MODE_CHANGE_EVT 0x14 +#define HCI_RETURN_LINK_KEYS_EVT 0x15 +#define HCI_PIN_CODE_REQUEST_EVT 0x16 +#define HCI_LINK_KEY_REQUEST_EVT 0x17 +#define HCI_LINK_KEY_NOTIFICATION_EVT 0x18 +#define HCI_LOOPBACK_COMMAND_EVT 0x19 +#define HCI_DATA_BUF_OVERFLOW_EVT 0x1A +#define HCI_MAX_SLOTS_CHANGED_EVT 0x1B +#define HCI_READ_CLOCK_OFF_COMP_EVT 0x1C +#define HCI_CONN_PKT_TYPE_CHANGE_EVT 0x1D +#define HCI_QOS_VIOLATION_EVT 0x1E +#define HCI_PAGE_SCAN_MODE_CHANGE_EVT 0x1F +#define HCI_PAGE_SCAN_REP_MODE_CHNG_EVT 0x20 +#define HCI_FLOW_SPECIFICATION_COMP_EVT 0x21 +#define HCI_INQUIRY_RSSI_RESULT_EVT 0x22 +#define HCI_READ_RMT_EXT_FEATURES_COMP_EVT 0x23 +#define HCI_ESCO_CONNECTION_COMP_EVT 0x2C +#define HCI_ESCO_CONNECTION_CHANGED_EVT 0x2D +#define HCI_SNIFF_SUB_RATE_EVT 0x2E +#define HCI_EXTENDED_INQUIRY_RESULT_EVT 0x2F +#define HCI_ENCRYPTION_KEY_REFRESH_COMP_EVT 0x30 +#define HCI_IO_CAPABILITY_REQUEST_EVT 0x31 +#define HCI_IO_CAPABILITY_RESPONSE_EVT 0x32 +#define HCI_USER_CONFIRMATION_REQUEST_EVT 0x33 +#define HCI_USER_PASSKEY_REQUEST_EVT 0x34 +#define HCI_REMOTE_OOB_DATA_REQUEST_EVT 0x35 +#define HCI_SIMPLE_PAIRING_COMPLETE_EVT 0x36 +#define HCI_LINK_SUPER_TOUT_CHANGED_EVT 0x38 +#define HCI_ENHANCED_FLUSH_COMPLETE_EVT 0x39 +#define HCI_USER_PASSKEY_NOTIFY_EVT 0x3B +#define HCI_KEYPRESS_NOTIFY_EVT 0x3C +#define HCI_RMT_HOST_SUP_FEAT_NOTIFY_EVT 0x3D + +/*#define HCI_GENERIC_AMP_LINK_KEY_NOTIF_EVT 0x3E Removed from spec */ +#define HCI_PHYSICAL_LINK_COMP_EVT 0x40 +#define HCI_CHANNEL_SELECTED_EVT 0x41 +#define HCI_DISC_PHYSICAL_LINK_COMP_EVT 0x42 +#define HCI_PHY_LINK_LOSS_EARLY_WARNING_EVT 0x43 +#define HCI_PHY_LINK_RECOVERY_EVT 0x44 +#define HCI_LOGICAL_LINK_COMP_EVT 0x45 +#define HCI_DISC_LOGICAL_LINK_COMP_EVT 0x46 +#define HCI_FLOW_SPEC_MODIFY_COMP_EVT 0x47 +#define HCI_NUM_COMPL_DATA_BLOCKS_EVT 0x48 +#define HCI_SHORT_RANGE_MODE_COMPLETE_EVT 0x4C +#define HCI_AMP_STATUS_CHANGE_EVT 0x4D +#define HCI_SET_TRIGGERED_CLOCK_CAPTURE_EVT 0x4E + +/* ULP HCI Event */ +#define HCI_BLE_EVENT 0x3e +/* ULP Event sub code */ +#define HCI_BLE_CONN_COMPLETE_EVT 0x01 +#define HCI_BLE_ADV_PKT_RPT_EVT 0x02 +#define HCI_BLE_LL_CONN_PARAM_UPD_EVT 0x03 +#define HCI_BLE_READ_REMOTE_FEAT_CMPL_EVT 0x04 +#define HCI_BLE_LTK_REQ_EVT 0x05 +#define HCI_BLE_RC_PARAM_REQ_EVT 0x06 +#define HCI_BLE_DATA_LENGTH_CHANGE_EVT 0x07 +#define HCI_BLE_ENHANCED_CONN_COMPLETE_EVT 0x0a +#define HCI_BLE_DIRECT_ADV_EVT 0x0b +/* ESP vendor BLE Event sub code */ +#define HCI_BLE_ADV_DISCARD_REPORT_EVT 0XF0 +#if (BLE_50_FEATURE_SUPPORT == TRUE) +#define HCI_BLE_PHY_UPDATE_COMPLETE_EVT 0x0c +#define HCI_BLE_EXT_ADV_REPORT_EVT 0x0d +#define HCI_BLE_PERIOD_ADV_SYNC_ESTAB_EVT 0x0e +#define HCI_BLE_PERIOD_ADV_REPORT_EVT 0x0f +#define HCI_BLE_PERIOD_ADV_SYNC_LOST_EVT 0x10 +#define HCI_BLE_SCAN_TIMEOUT_EVT 0x11 +#define HCI_BLE_ADV_SET_TERMINATED_EVT 0x12 +#define HCI_BLE_SCAN_REQ_RECEIVED_EVT 0x13 +#define HCI_BLE_CHANNEL_SELECT_ALG 0x14 +#endif // #if (BLE_50_FEATURE_SUPPORT == TRUE) +#if (BLE_FEAT_PERIODIC_ADV_SYNC_TRANSFER == TRUE) +#define HCI_BLE_PERIOD_ADV_SYNC_TRANS_RECV_EVT 0x18 +#endif // #if (BLE_FEAT_PERIODIC_ADV_SYNC_TRANSFER == TRUE) + +/* Definitions for LE Channel Map */ +#define HCI_BLE_CHNL_MAP_SIZE 5 + +#define HCI_VENDOR_SPECIFIC_EVT 0xFF /* Vendor specific events */ +#define HCI_NAP_TRACE_EVT 0xFF /* was define 0xFE, 0xFD, change to 0xFF + because conflict w/ TCI_EVT and per + specification compliant */ + +/* +** Defentions for HCI Error Codes that are past in the events +*/ +#define HCI_SUCCESS 0x00 +#define HCI_ERR_ILLEGAL_COMMAND 0x01 +#define HCI_ERR_NO_CONNECTION 0x02 +#define HCI_ERR_HW_FAILURE 0x03 +#define HCI_ERR_PAGE_TIMEOUT 0x04 +#define HCI_ERR_AUTH_FAILURE 0x05 +#define HCI_ERR_KEY_MISSING 0x06 +#define HCI_ERR_MEMORY_FULL 0x07 +#define HCI_ERR_CONNECTION_TOUT 0x08 +#define HCI_ERR_MAX_NUM_OF_CONNECTIONS 0x09 +#define HCI_ERR_MAX_NUM_OF_SCOS 0x0A +#define HCI_ERR_CONNECTION_EXISTS 0x0B +#define HCI_ERR_COMMAND_DISALLOWED 0x0C +#define HCI_ERR_HOST_REJECT_RESOURCES 0x0D +#define HCI_ERR_HOST_REJECT_SECURITY 0x0E +#define HCI_ERR_HOST_REJECT_DEVICE 0x0F +#define HCI_ERR_HOST_TIMEOUT 0x10 +#define HCI_ERR_UNSUPPORTED_VALUE 0x11 +#define HCI_ERR_ILLEGAL_PARAMETER_FMT 0x12 +#define HCI_ERR_PEER_USER 0x13 +#define HCI_ERR_PEER_LOW_RESOURCES 0x14 +#define HCI_ERR_PEER_POWER_OFF 0x15 +#define HCI_ERR_CONN_CAUSE_LOCAL_HOST 0x16 +#define HCI_ERR_REPEATED_ATTEMPTS 0x17 +#define HCI_ERR_PAIRING_NOT_ALLOWED 0x18 +#define HCI_ERR_UNKNOWN_LMP_PDU 0x19 +#define HCI_ERR_UNSUPPORTED_REM_FEATURE 0x1A +#define HCI_ERR_SCO_OFFSET_REJECTED 0x1B +#define HCI_ERR_SCO_INTERVAL_REJECTED 0x1C +#define HCI_ERR_SCO_AIR_MODE 0x1D +#define HCI_ERR_INVALID_LMP_PARAM 0x1E +#define HCI_ERR_UNSPECIFIED 0x1F +#define HCI_ERR_UNSUPPORTED_LMP_PARAMETERS 0x20 +#define HCI_ERR_ROLE_CHANGE_NOT_ALLOWED 0x21 +#define HCI_ERR_LMP_RESPONSE_TIMEOUT 0x22 +#define HCI_ERR_LMP_ERR_TRANS_COLLISION 0x23 +#define HCI_ERR_LMP_PDU_NOT_ALLOWED 0x24 +#define HCI_ERR_ENCRY_MODE_NOT_ACCEPTABLE 0x25 +#define HCI_ERR_UNIT_KEY_USED 0x26 +#define HCI_ERR_QOS_NOT_SUPPORTED 0x27 +#define HCI_ERR_INSTANT_PASSED 0x28 +#define HCI_ERR_PAIRING_WITH_UNIT_KEY_NOT_SUPPORTED 0x29 +#define HCI_ERR_DIFF_TRANSACTION_COLLISION 0x2A +#define HCI_ERR_UNDEFINED_0x2B 0x2B +#define HCI_ERR_QOS_UNACCEPTABLE_PARAM 0x2C +#define HCI_ERR_QOS_REJECTED 0x2D +#define HCI_ERR_CHAN_CLASSIF_NOT_SUPPORTED 0x2E +#define HCI_ERR_INSUFFCIENT_SECURITY 0x2F +#define HCI_ERR_PARAM_OUT_OF_RANGE 0x30 +#define HCI_ERR_UNDEFINED_0x31 0x31 +#define HCI_ERR_ROLE_SWITCH_PENDING 0x32 +#define HCI_ERR_UNDEFINED_0x33 0x33 +#define HCI_ERR_RESERVED_SLOT_VIOLATION 0x34 +#define HCI_ERR_ROLE_SWITCH_FAILED 0x35 +#define HCI_ERR_INQ_RSP_DATA_TOO_LARGE 0x36 +#define HCI_ERR_SIMPLE_PAIRING_NOT_SUPPORTED 0x37 +#define HCI_ERR_HOST_BUSY_PAIRING 0x38 +#define HCI_ERR_REJ_NO_SUITABLE_CHANNEL 0x39 +#define HCI_ERR_CONTROLLER_BUSY 0x3A +#define HCI_ERR_UNACCEPT_CONN_INTERVAL 0x3B +#define HCI_ERR_DIRECTED_ADVERTISING_TIMEOUT 0x3C +#define HCI_ERR_CONN_TOUT_DUE_TO_MIC_FAILURE 0x3D +#define HCI_ERR_CONN_FAILED_ESTABLISHMENT 0x3E +#define HCI_ERR_MAC_CONNECTION_FAILED 0x3F + +/* ConnectionLess Broadcast errors */ +#define HCI_ERR_LT_ADDR_ALREADY_IN_USE 0x40 +#define HCI_ERR_LT_ADDR_NOT_ALLOCATED 0x41 +#define HCI_ERR_CLB_NOT_ENABLED 0x42 +#define HCI_ERR_CLB_DATA_TOO_BIG 0x43 + +#define HCI_ERR_MAX_ERR 0x43 + +//ESP vendor error code +#define HCI_ERR_ESP_VENDOR_FAIL 0xE0 + +#define HCI_HINT_TO_RECREATE_AMP_PHYS_LINK 0xFF +#if (BLE_50_FEATURE_SUPPORT == TRUE) +typedef UINT8 tHCI_STATUS; +#endif // #if (BLE_50_FEATURE_SUPPORT == TRUE) + +/* +** Definitions for HCI enable event +*/ +#define HCI_INQUIRY_COMPLETE_EV(p) (*((UINT32 *)(p)) & 0x00000001) +#define HCI_INQUIRY_RESULT_EV(p) (*((UINT32 *)(p)) & 0x00000002) +#define HCI_CONNECTION_COMPLETE_EV(p) (*((UINT32 *)(p)) & 0x00000004) +#define HCI_CONNECTION_REQUEST_EV(p) (*((UINT32 *)(p)) & 0x00000008) +#define HCI_DISCONNECTION_COMPLETE_EV(p) (*((UINT32 *)(p)) & 0x00000010) +#define HCI_AUTHENTICATION_COMPLETE_EV(p) (*((UINT32 *)(p)) & 0x00000020) +#define HCI_RMT_NAME_REQUEST_COMPL_EV(p) (*((UINT32 *)(p)) & 0x00000040) +#define HCI_CHANGE_CONN_ENCRPT_ENABLE_EV(p) (*((UINT32 *)(p)) & 0x00000080) +#define HCI_CHANGE_CONN_LINK_KEY_EV(p) (*((UINT32 *)(p)) & 0x00000100) +#define HCI_MASTER_LINK_KEY_COMPLETE_EV(p) (*((UINT32 *)(p)) & 0x00000200) +#define HCI_READ_RMT_FEATURES_COMPL_EV(p) (*((UINT32 *)(p)) & 0x00000400) +#define HCI_READ_RMT_VERSION_COMPL_EV(p) (*((UINT32 *)(p)) & 0x00000800) +#define HCI_QOS_SETUP_COMPLETE_EV(p) (*((UINT32 *)(p)) & 0x00001000) +#define HCI_COMMAND_COMPLETE_EV(p) (*((UINT32 *)(p)) & 0x00002000) +#define HCI_COMMAND_STATUS_EV(p) (*((UINT32 *)(p)) & 0x00004000) +#define HCI_HARDWARE_ERROR_EV(p) (*((UINT32 *)(p)) & 0x00008000) +#define HCI_FLASH_OCCURED_EV(p) (*((UINT32 *)(p)) & 0x00010000) +#define HCI_ROLE_CHANGE_EV(p) (*((UINT32 *)(p)) & 0x00020000) +#define HCI_NUM_COMPLETED_PKTS_EV(p) (*((UINT32 *)(p)) & 0x00040000) +#define HCI_MODE_CHANGE_EV(p) (*((UINT32 *)(p)) & 0x00080000) +#define HCI_RETURN_LINK_KEYS_EV(p) (*((UINT32 *)(p)) & 0x00100000) +#define HCI_PIN_CODE_REQUEST_EV(p) (*((UINT32 *)(p)) & 0x00200000) +#define HCI_LINK_KEY_REQUEST_EV(p) (*((UINT32 *)(p)) & 0x00400000) +#define HCI_LINK_KEY_NOTIFICATION_EV(p) (*((UINT32 *)(p)) & 0x00800000) +#define HCI_LOOPBACK_COMMAND_EV(p) (*((UINT32 *)(p)) & 0x01000000) +#define HCI_DATA_BUF_OVERFLOW_EV(p) (*((UINT32 *)(p)) & 0x02000000) +#define HCI_MAX_SLOTS_CHANGE_EV(p) (*((UINT32 *)(p)) & 0x04000000) +#define HCI_READ_CLOCK_OFFSET_COMP_EV(p) (*((UINT32 *)(p)) & 0x08000000) +#define HCI_CONN_PKT_TYPE_CHANGED_EV(p) (*((UINT32 *)(p)) & 0x10000000) +#define HCI_QOS_VIOLATION_EV(p) (*((UINT32 *)(p)) & 0x20000000) +#define HCI_PAGE_SCAN_MODE_CHANGED_EV(p) (*((UINT32 *)(p)) & 0x40000000) +#define HCI_PAGE_SCAN_REP_MODE_CHNG_EV(p) (*((UINT32 *)(p)) & 0x80000000) + +/* the default event mask for 2.1+EDR (Lisbon) does not include Lisbon events */ +#define HCI_DEFAULT_EVENT_MASK_0 0xFFFFFFFF +#define HCI_DEFAULT_EVENT_MASK_1 0x00001FFF + +/* the event mask for 2.0 + EDR and later (includes Lisbon events) */ +#define HCI_LISBON_EVENT_MASK_0 0xFFFFFFFF +#define HCI_LISBON_EVENT_MASK_1 0x1DBFFFFF +#define HCI_LISBON_EVENT_MASK "\x0D\xBF\xFF\xFF\xFF\xFF\xFF\xFF" +#define HCI_LISBON_EVENT_MASK_EXT "\x1D\xBF\xFF\xFF\xFF\xFF\xFF\xFF" +#define HCI_DUMO_EVENT_MASK_EXT "\x3D\xBF\xFF\xFF\xFF\xFF\xFF\xFF" +/* 0x00001FFF FFFFFFFF Default - no Lisbon events + 0x00000800 00000000 Synchronous Connection Complete Event + 0x00001000 00000000 Synchronous Connection Changed Event + 0x00002000 00000000 Sniff Subrate Event + 0x00004000 00000000 Extended Inquiry Result Event + 0x00008000 00000000 Encryption Key Refresh Complete Event + 0x00010000 00000000 IO Capability Request Event + 0x00020000 00000000 IO Capability Response Event + 0x00040000 00000000 User Confirmation Request Event + 0x00080000 00000000 User Passkey Request Event + 0x00100000 00000000 Remote OOB Data Request Event + 0x00200000 00000000 Simple Pairing Complete Event + 0x00400000 00000000 Generic AMP Link Key Notification Event + 0x00800000 00000000 Link Supervision Timeout Changed Event + 0x01000000 00000000 Enhanced Flush Complete Event + 0x04000000 00000000 User Passkey Notification Event + 0x08000000 00000000 Keypress Notification Event + 0x10000000 00000000 Remote Host Supported Features Notification Event + 0x20000000 00000000 LE Meta Event + */ + +#define HCI_LE_ONLY_EVENT_MASK "\x20\x00\x80\x00\x02\x00\x88\x90" +/* 0x00000000 00000010 Disconnection complete event + 0x00000000 00000080 Encryption change event + 0x00000000 00000800 Read remote version information complete event + 0x00000000 00008000 Hardware error event + 0x00000000 02000000 Data buffer overflow event + 0x00008000 00000000 Encryption key refresh complete event + 0x20000000 00000000 LE Meta Event +*/ + +/* the event mask for AMP controllers */ +#define HCI_AMP_EVENT_MASK_3_0 "\x00\x00\x00\x00\x00\x00\x3F\xFF" + +/* 0x0000000000000000 No events specified (default) + 0x0000000000000001 Physical Link Complete Event + 0x0000000000000002 Channel Selected Event + 0x0000000000000004 Disconnection Physical Link Event + 0x0000000000000008 Physical Link Loss Early Warning Event + 0x0000000000000010 Physical Link Recovery Event + 0x0000000000000020 Logical Link Complete Event + 0x0000000000000040 Disconnection Logical Link Complete Event + 0x0000000000000080 Flow Spec Modify Complete Event + 0x0000000000000100 Number of Completed Data Blocks Event + 0x0000000000000200 AMP Start Test Event + 0x0000000000000400 AMP Test End Event + 0x0000000000000800 AMP Receiver Report Event + 0x0000000000001000 Short Range Mode Change Complete Event + 0x0000000000002000 AMP Status Change Event +*/ + +/* the event mask page 2 (CLB + CSA4) for BR/EDR controller */ +#define HCI_PAGE_2_EVENT_MASK "\x00\x00\x00\x00\x00\x7F\xC0\x00" +/* 0x0000000000004000 Triggered Clock Capture Event + 0x0000000000008000 Sync Train Complete Event + 0x0000000000010000 Sync Train Received Event + 0x0000000000020000 Connectionless Broadcast Receive Event + 0x0000000000040000 Connectionless Broadcast Timeout Event + 0x0000000000080000 Truncated Page Complete Event + 0x0000000000100000 Salve Page Response Timeout Event + 0x0000000000200000 Connectionless Broadcast Channel Map Change Event + 0x0000000000400000 Inquiry Response Notification Event +*/ +#if BLE_PRIVACY_SPT == TRUE +/* BLE event mask */ +#define HCI_BLE_EVENT_MASK_DEF "\x00\x00\x00\x00\x00\x00\x07\xff" +#else +#define HCI_BLE_EVENT_MASK_DEF "\x00\x00\x00\x00\x00\x00\x00\x7f" +#endif +/* +** Definitions for packet type masks (BT1.2 and BT2.0 definitions) +*/ +#define HCI_PKT_TYPES_MASK_NO_2_DH1 0x0002 +#define HCI_PKT_TYPES_MASK_NO_3_DH1 0x0004 +#define HCI_PKT_TYPES_MASK_DM1 0x0008 +#define HCI_PKT_TYPES_MASK_DH1 0x0010 +#define HCI_PKT_TYPES_MASK_HV1 0x0020 +#define HCI_PKT_TYPES_MASK_HV2 0x0040 +#define HCI_PKT_TYPES_MASK_HV3 0x0080 +#define HCI_PKT_TYPES_MASK_NO_2_DH3 0x0100 +#define HCI_PKT_TYPES_MASK_NO_3_DH3 0x0200 +#define HCI_PKT_TYPES_MASK_DM3 0x0400 +#define HCI_PKT_TYPES_MASK_DH3 0x0800 +#define HCI_PKT_TYPES_MASK_NO_2_DH5 0x1000 +#define HCI_PKT_TYPES_MASK_NO_3_DH5 0x2000 +#define HCI_PKT_TYPES_MASK_DM5 0x4000 +#define HCI_PKT_TYPES_MASK_DH5 0x8000 + +/* Packet type should be one of valid but at least one should be specified */ +#define HCI_VALID_SCO_PKT_TYPE(t) (((((t) & ~(HCI_PKT_TYPES_MASK_HV1 \ + | HCI_PKT_TYPES_MASK_HV2 \ + | HCI_PKT_TYPES_MASK_HV3)) == 0)) \ + && ((t) != 0)) + + + + + +/* Packet type should not be invalid and at least one should be specified */ +#define HCI_VALID_ACL_PKT_TYPE(t) (((((t) & ~(HCI_PKT_TYPES_MASK_DM1 \ + | HCI_PKT_TYPES_MASK_DH1 \ + | HCI_PKT_TYPES_MASK_DM3 \ + | HCI_PKT_TYPES_MASK_DH3 \ + | HCI_PKT_TYPES_MASK_DM5 \ + | HCI_PKT_TYPES_MASK_DH5 \ + | HCI_PKT_TYPES_MASK_NO_2_DH1 \ + | HCI_PKT_TYPES_MASK_NO_3_DH1 \ + | HCI_PKT_TYPES_MASK_NO_2_DH3 \ + | HCI_PKT_TYPES_MASK_NO_3_DH3 \ + | HCI_PKT_TYPES_MASK_NO_2_DH5 \ + | HCI_PKT_TYPES_MASK_NO_3_DH5 )) == 0)) \ + && (((t) & (HCI_PKT_TYPES_MASK_DM1 \ + | HCI_PKT_TYPES_MASK_DH1 \ + | HCI_PKT_TYPES_MASK_DM3 \ + | HCI_PKT_TYPES_MASK_DH3 \ + | HCI_PKT_TYPES_MASK_DM5 \ + | HCI_PKT_TYPES_MASK_DH5)) != 0)) + +/* +** Definitions for eSCO packet type masks (BT1.2 and BT2.0 definitions) +*/ +#define HCI_ESCO_PKT_TYPES_MASK_HV1 0x0001 +#define HCI_ESCO_PKT_TYPES_MASK_HV2 0x0002 +#define HCI_ESCO_PKT_TYPES_MASK_HV3 0x0004 +#define HCI_ESCO_PKT_TYPES_MASK_EV3 0x0008 +#define HCI_ESCO_PKT_TYPES_MASK_EV4 0x0010 +#define HCI_ESCO_PKT_TYPES_MASK_EV5 0x0020 +#define HCI_ESCO_PKT_TYPES_MASK_NO_2_EV3 0x0040 +#define HCI_ESCO_PKT_TYPES_MASK_NO_3_EV3 0x0080 +#define HCI_ESCO_PKT_TYPES_MASK_NO_2_EV5 0x0100 +#define HCI_ESCO_PKT_TYPES_MASK_NO_3_EV5 0x0200 + +/* Packet type should be one of valid but at least one should be specified for 1.2 */ +#define HCI_VALID_ESCO_PKT_TYPE(t) (((((t) & ~(HCI_ESCO_PKT_TYPES_MASK_EV3 \ + | HCI_ESCO_PKT_TYPES_MASK_EV4 \ + | HCI_ESCO_PKT_TYPES_MASK_EV5)) == 0)) \ + && ((t) != 0))/* Packet type should be one of valid but at least one should be specified */ + +#define HCI_VALID_ESCO_SCOPKT_TYPE(t) (((((t) & ~(HCI_ESCO_PKT_TYPES_MASK_HV1 \ + | HCI_ESCO_PKT_TYPES_MASK_HV2 \ + | HCI_ESCO_PKT_TYPES_MASK_HV3)) == 0)) \ + && ((t) != 0)) + +#define HCI_VALID_SCO_ALL_PKT_TYPE(t) (((((t) & ~(HCI_ESCO_PKT_TYPES_MASK_HV1 \ + | HCI_ESCO_PKT_TYPES_MASK_HV2 \ + | HCI_ESCO_PKT_TYPES_MASK_HV3 \ + | HCI_ESCO_PKT_TYPES_MASK_EV3 \ + | HCI_ESCO_PKT_TYPES_MASK_EV4 \ + | HCI_ESCO_PKT_TYPES_MASK_EV5)) == 0)) \ + && ((t) != 0)) + +/* +** Define parameters to allow role switch during create connection +*/ +#define HCI_CR_CONN_NOT_ALLOW_SWITCH 0x00 +#define HCI_CR_CONN_ALLOW_SWITCH 0x01 + +/* +** Hold Mode command destination +*/ +#define HOLD_MODE_DEST_LOCAL_DEVICE 0x00 +#define HOLD_MODE_DEST_RMT_DEVICE 0x01 + +/* +** Definitions for different HCI parameters +*/ +#define HCI_PER_INQ_MIN_MAX_PERIOD 0x0003 +#define HCI_PER_INQ_MAX_MAX_PERIOD 0xFFFF +#define HCI_PER_INQ_MIN_MIN_PERIOD 0x0002 +#define HCI_PER_INQ_MAX_MIN_PERIOD 0xFFFE + +#define HCI_MAX_INQUIRY_LENGTH 0x30 + +#define HCI_MIN_INQ_LAP 0x9E8B00 +#define HCI_MAX_INQ_LAP 0x9E8B3F + +/* HCI role defenitions */ +#define HCI_ROLE_MASTER 0x00 +#define HCI_ROLE_SLAVE 0x01 +#define HCI_ROLE_UNKNOWN 0xff + +/* HCI mode defenitions */ +#define HCI_MODE_ACTIVE 0x00 +#define HCI_MODE_HOLD 0x01 +#define HCI_MODE_SNIFF 0x02 +#define HCI_MODE_PARK 0x03 + +/* HCI Flow Control Mode defenitions */ +#define HCI_PACKET_BASED_FC_MODE 0x00 +#define HCI_BLOCK_BASED_FC_MODE 0x01 + +/* Define Packet types as requested by the Host */ +#define HCI_ACL_PKT_TYPE_NONE 0x0000 +#define HCI_ACL_PKT_TYPE_DM1 0x0008 +#define HCI_ACL_PKT_TYPE_DH1 0x0010 +#define HCI_ACL_PKT_TYPE_AUX1 0x0200 +#define HCI_ACL_PKT_TYPE_DM3 0x0400 +#define HCI_ACL_PKT_TYPE_DH3 0x0800 +#define HCI_ACL_PKT_TYPE_DM5 0x4000 +#define HCI_ACL_PKT_TYPE_DH5 0x8000 + +/* Define key type in the Master Link Key command */ +#define HCI_USE_SEMI_PERMANENT_KEY 0x00 +#define HCI_USE_TEMPORARY_KEY 0x01 + +/* Page scan period modes */ +#define HCI_PAGE_SCAN_REP_MODE_R0 0x00 +#define HCI_PAGE_SCAN_REP_MODE_R1 0x01 +#define HCI_PAGE_SCAN_REP_MODE_R2 0x02 + +/* Define limits for page scan repetition modes */ +#define HCI_PAGE_SCAN_R1_LIMIT 0x0800 +#define HCI_PAGE_SCAN_R2_LIMIT 0x1000 + +/* Page scan period modes */ +#define HCI_PAGE_SCAN_PER_MODE_P0 0x00 +#define HCI_PAGE_SCAN_PER_MODE_P1 0x01 +#define HCI_PAGE_SCAN_PER_MODE_P2 0x02 + +/* Page scan modes */ +#define HCI_MANDATARY_PAGE_SCAN_MODE 0x00 +#define HCI_OPTIONAL_PAGE_SCAN_MODE1 0x01 +#define HCI_OPTIONAL_PAGE_SCAN_MODE2 0x02 +#define HCI_OPTIONAL_PAGE_SCAN_MODE3 0x03 + +/* Page and inquiry scan types */ +#define HCI_SCAN_TYPE_STANDARD 0x00 +#define HCI_SCAN_TYPE_INTERLACED 0x01 /* 1.2 devices or later */ +#define HCI_DEF_SCAN_TYPE HCI_SCAN_TYPE_STANDARD + +/* Definitions for quality of service service types */ +#define HCI_SERVICE_NO_TRAFFIC 0x00 +#define HCI_SERVICE_BEST_EFFORT 0x01 +#define HCI_SERVICE_GUARANTEED 0x02 + +#define HCI_QOS_LATENCY_DO_NOT_CARE 0xFFFFFFFF +#define HCI_QOS_DELAY_DO_NOT_CARE 0xFFFFFFFF + +/* Definitions for Flow Specification */ +#define HCI_FLOW_SPEC_LATENCY_DO_NOT_CARE 0xFFFFFFFF + +/* Definitions for AFH Channel Map */ +#define HCI_AFH_CHANNEL_MAP_LEN 10 + +/* Definitions for Extended Inquiry Response */ +#define HCI_EXT_INQ_RESPONSE_LEN 240 +#define HCI_EIR_FLAGS_TYPE BT_EIR_FLAGS_TYPE +#define HCI_EIR_MORE_16BITS_UUID_TYPE BT_EIR_MORE_16BITS_UUID_TYPE +#define HCI_EIR_COMPLETE_16BITS_UUID_TYPE BT_EIR_COMPLETE_16BITS_UUID_TYPE +#define HCI_EIR_MORE_32BITS_UUID_TYPE BT_EIR_MORE_32BITS_UUID_TYPE +#define HCI_EIR_COMPLETE_32BITS_UUID_TYPE BT_EIR_COMPLETE_32BITS_UUID_TYPE +#define HCI_EIR_MORE_128BITS_UUID_TYPE BT_EIR_MORE_128BITS_UUID_TYPE +#define HCI_EIR_COMPLETE_128BITS_UUID_TYPE BT_EIR_COMPLETE_128BITS_UUID_TYPE +#define HCI_EIR_SHORTENED_LOCAL_NAME_TYPE BT_EIR_SHORTENED_LOCAL_NAME_TYPE +#define HCI_EIR_COMPLETE_LOCAL_NAME_TYPE BT_EIR_COMPLETE_LOCAL_NAME_TYPE +#define HCI_EIR_TX_POWER_LEVEL_TYPE BT_EIR_TX_POWER_LEVEL_TYPE +#define HCI_EIR_URL_TYPE BT_EIR_URL_TYPE +#define HCI_EIR_MANUFACTURER_SPECIFIC_TYPE BT_EIR_MANUFACTURER_SPECIFIC_TYPE +#define HCI_EIR_OOB_BD_ADDR_TYPE BT_EIR_OOB_BD_ADDR_TYPE +#define HCI_EIR_OOB_COD_TYPE BT_EIR_OOB_COD_TYPE +#define HCI_EIR_OOB_SSP_HASH_C_TYPE BT_EIR_OOB_SSP_HASH_C_TYPE +#define HCI_EIR_OOB_SSP_RAND_R_TYPE BT_EIR_OOB_SSP_RAND_R_TYPE + +/* Definitions for Write Simple Pairing Mode */ +#define HCI_SP_MODE_UNDEFINED 0x00 +#define HCI_SP_MODE_ENABLED 0x01 + +/* Definitions for Write Simple Pairing Debug Mode */ +#define HCI_SPD_MODE_DISABLED 0x00 +#define HCI_SPD_MODE_ENABLED 0x01 + +/* Definitions for Write Secure Connections Host Support */ +#define HCI_SC_MODE_DISABLED 0x00 +#define HCI_SC_MODE_ENABLED 0x01 + +/* Definitions for IO Capability Response/Command */ +#define HCI_IO_CAP_DISPLAY_ONLY 0x00 +#define HCI_IO_CAP_DISPLAY_YESNO 0x01 +#define HCI_IO_CAP_KEYBOARD_ONLY 0x02 +#define HCI_IO_CAP_NO_IO 0x03 + +#define HCI_OOB_AUTH_DATA_NOT_PRESENT 0x00 +#define HCI_OOB_REM_AUTH_DATA_PRESENT 0x01 + +#define HCI_MITM_PROTECT_NOT_REQUIRED 0x00 +#define HCI_MITM_PROTECT_REQUIRED 0x01 + + +/* Policy settings status */ +#define HCI_DISABLE_ALL_LM_MODES 0x0000 +#define HCI_ENABLE_MASTER_SLAVE_SWITCH 0x0001 +#define HCI_ENABLE_HOLD_MODE 0x0002 +#define HCI_ENABLE_SNIFF_MODE 0x0004 +#define HCI_ENABLE_PARK_MODE 0x0008 + +/* By default allow switch, because host can not allow that */ +/* that until he created the connection */ +#define HCI_DEFAULT_POLICY_SETTINGS HCI_DISABLE_ALL_LM_MODES + +/* Filters that are sent in set filter command */ +#define HCI_FILTER_TYPE_CLEAR_ALL 0x00 +#define HCI_FILTER_INQUIRY_RESULT 0x01 +#define HCI_FILTER_CONNECTION_SETUP 0x02 + +#define HCI_FILTER_COND_NEW_DEVICE 0x00 +#define HCI_FILTER_COND_DEVICE_CLASS 0x01 +#define HCI_FILTER_COND_BD_ADDR 0x02 + +#define HCI_DO_NOT_AUTO_ACCEPT_CONNECT 1 +#define HCI_DO_AUTO_ACCEPT_CONNECT 2 /* role switch disabled */ +#define HCI_DO_AUTO_ACCEPT_CONNECT_RS 3 /* role switch enabled (1.1 errata 1115) */ + +/* Auto accept flags */ +#define HCI_AUTO_ACCEPT_OFF 0x00 +#define HCI_AUTO_ACCEPT_ACL_CONNECTIONS 0x01 +#define HCI_AUTO_ACCEPT_SCO_CONNECTIONS 0x02 + +/* PIN type */ +#define HCI_PIN_TYPE_VARIABLE 0 +#define HCI_PIN_TYPE_FIXED 1 + +/* Loopback Modes */ +#define HCI_LOOPBACK_MODE_DISABLED 0 +#define HCI_LOOPBACK_MODE_LOCAL 1 +#define HCI_LOOPBACK_MODE_REMOTE 2 + +#define SLOTS_PER_10MS 16 /* 0.625 ms slots in a 10 ms tick */ + +/* Maximum connection accept timeout in 0.625msec */ +#define HCI_MAX_CONN_ACCEPT_TOUT 0xB540 /* 29 sec */ +#define HCI_DEF_CONN_ACCEPT_TOUT 0x1F40 /* 5 sec */ + +/* Page timeout is used in LC only and LC is counting down slots not using OS */ +#define HCI_DEFAULT_PAGE_TOUT 0x2000 /* 5.12 sec (in slots) */ +#define HCI_MIN_PAGE_TOUT 0x0016 /* 13.75 ms (in slots) */ + +/* Scan enable flags */ +#define HCI_NO_SCAN_ENABLED 0x00 +#define HCI_INQUIRY_SCAN_ENABLED 0x01 +#define HCI_PAGE_SCAN_ENABLED 0x02 + +/* Pagescan timer definitions in 0.625 ms */ +#define HCI_MIN_PAGESCAN_INTERVAL 0x12 /* 11.25 ms */ +#define HCI_MAX_PAGESCAN_INTERVAL 0x1000 /* 2.56 sec */ +#define HCI_DEF_PAGESCAN_INTERVAL 0x0800 /* 1.28 sec */ + +/* Parameter for pagescan window is passed to LC and is kept in slots */ +#define HCI_MIN_PAGESCAN_WINDOW 0x11 /* 10.625 ms */ +#define HCI_MAX_PAGESCAN_WINDOW 0x1000 /* 2.56 sec */ +#define HCI_DEF_PAGESCAN_WINDOW 0x12 /* 11.25 ms */ + +/* Inquiryscan timer definitions in 0.625 ms */ +#define HCI_MIN_INQUIRYSCAN_INTERVAL 0x12 /* 11.25 ms */ +#define HCI_MAX_INQUIRYSCAN_INTERVAL 0x1000 /* 2.56 sec */ +#define HCI_DEF_INQUIRYSCAN_INTERVAL 0x1000 /* 2.56 sec */ + +/* Parameter for inquiryscan window is passed to LC and is kept in slots */ +#define HCI_MIN_INQUIRYSCAN_WINDOW 0x11 /* 10.625 ms */ +#define HCI_MAX_INQUIRYSCAN_WINDOW 0x1000 /* 2.56 sec */ +#define HCI_DEF_INQUIRYSCAN_WINDOW 0x12 /* 11.25 ms */ + +/* Encryption modes */ +#define HCI_ENCRYPT_MODE_DISABLED 0x00 +#define HCI_ENCRYPT_MODE_POINT_TO_POINT 0x01 +#define HCI_ENCRYPT_MODE_ALL 0x02 + +/* Voice settings */ +#define HCI_INP_CODING_LINEAR 0x0000 /* 0000000000 */ +#define HCI_INP_CODING_U_LAW 0x0100 /* 0100000000 */ +#define HCI_INP_CODING_A_LAW 0x0200 /* 1000000000 */ +#define HCI_INP_CODING_MASK 0x0300 /* 1100000000 */ + +#define HCI_INP_DATA_FMT_1S_COMPLEMENT 0x0000 /* 0000000000 */ +#define HCI_INP_DATA_FMT_2S_COMPLEMENT 0x0040 /* 0001000000 */ +#define HCI_INP_DATA_FMT_SIGN_MAGNITUDE 0x0080 /* 0010000000 */ +#define HCI_INP_DATA_FMT_UNSIGNED 0x00c0 /* 0011000000 */ +#define HCI_INP_DATA_FMT_MASK 0x00c0 /* 0011000000 */ + +#define HCI_INP_SAMPLE_SIZE_8BIT 0x0000 /* 0000000000 */ +#define HCI_INP_SAMPLE_SIZE_16BIT 0x0020 /* 0000100000 */ +#define HCI_INP_SAMPLE_SIZE_MASK 0x0020 /* 0000100000 */ + +#define HCI_INP_LINEAR_PCM_BIT_POS_MASK 0x001c /* 0000011100 */ +#define HCI_INP_LINEAR_PCM_BIT_POS_OFFS 2 + +#define HCI_AIR_CODING_FORMAT_CVSD 0x0000 /* 0000000000 */ +#define HCI_AIR_CODING_FORMAT_U_LAW 0x0001 /* 0000000001 */ +#define HCI_AIR_CODING_FORMAT_A_LAW 0x0002 /* 0000000010 */ +#define HCI_AIR_CODING_FORMAT_TRANSPNT 0x0003 /* 0000000011 */ +#define HCI_AIR_CODING_FORMAT_MASK 0x0003 /* 0000000011 */ + +/* default 0001100000 */ +#define HCI_DEFAULT_VOICE_SETTINGS (HCI_INP_CODING_LINEAR \ + | HCI_INP_DATA_FMT_2S_COMPLEMENT \ + | HCI_INP_SAMPLE_SIZE_16BIT \ + | HCI_AIR_CODING_FORMAT_CVSD) + +#define HCI_CVSD_SUPPORTED(x) (((x) & HCI_AIR_CODING_FORMAT_MASK) == HCI_AIR_CODING_FORMAT_CVSD) +#define HCI_U_LAW_SUPPORTED(x) (((x) & HCI_AIR_CODING_FORMAT_MASK) == HCI_AIR_CODING_FORMAT_U_LAW) +#define HCI_A_LAW_SUPPORTED(x) (((x) & HCI_AIR_CODING_FORMAT_MASK) == HCI_AIR_CODING_FORMAT_A_LAW) +#define HCI_TRANSPNT_SUPPORTED(x) (((x) & HCI_AIR_CODING_FORMAT_MASK) == HCI_AIR_CODING_FORMAT_TRANSPNT) + +/* Retransmit timer definitions in 0.625 */ +#define HCI_MAX_AUTO_FLUSH_TOUT 0x07FF +#define HCI_DEFAULT_AUTO_FLUSH_TOUT 0 /* No auto flush */ + +/* Broadcast retransmitions */ +#define HCI_DEFAULT_NUM_BCAST_RETRAN 1 + +/* Define broadcast data types as passed in the hci data packet */ +#define HCI_DATA_POINT_TO_POINT 0x00 +#define HCI_DATA_ACTIVE_BCAST 0x01 +#define HCI_DATA_PICONET_BCAST 0x02 + +/* Hold mode activity */ +#define HCI_MAINTAIN_CUR_POWER_STATE 0x00 +#define HCI_SUSPEND_PAGE_SCAN 0x01 +#define HCI_SUSPEND_INQUIRY_SCAN 0x02 +#define HCI_SUSPEND_PERIODIC_INQUIRIES 0x04 + +/* Default Link Supervision timeoout */ +#define HCI_DEFAULT_INACT_TOUT 0x7D00 /* BR/EDR (20 seconds) */ +#define HCI_DEFAULT_AMP_INACT_TOUT 0x3E80 /* AMP (10 seconds) */ + +/* Read transmit power level parameter */ +#define HCI_READ_CURRENT 0x00 +#define HCI_READ_MAXIMUM 0x01 + +/* Link types for connection complete event */ +#define HCI_LINK_TYPE_SCO 0x00 +#define HCI_LINK_TYPE_ACL 0x01 +#define HCI_LINK_TYPE_ESCO 0x02 + +/* Link Key Notification Event (Key Type) definitions */ +#define HCI_LKEY_TYPE_COMBINATION 0x00 +#define HCI_LKEY_TYPE_LOCAL_UNIT 0x01 +#define HCI_LKEY_TYPE_REMOTE_UNIT 0x02 +#define HCI_LKEY_TYPE_DEBUG_COMB 0x03 +#define HCI_LKEY_TYPE_UNAUTH_COMB 0x04 +#define HCI_LKEY_TYPE_AUTH_COMB 0x05 +#define HCI_LKEY_TYPE_CHANGED_COMB 0x06 +#define HCI_LKEY_TYPE_UNAUTH_COMB_P_256 0x07 +#define HCI_LKEY_TYPE_AUTH_COMB_P_256 0x08 + +/* Internal definitions - not used over HCI */ +#define HCI_LKEY_TYPE_AMP_WIFI 0x80 +#define HCI_LKEY_TYPE_AMP_UWB 0x81 +#define HCI_LKEY_TYPE_UNKNOWN 0xff + +/* Read Local Version HCI Version return values (Command Complete Event) */ +#define HCI_VERSION_1_0B 0x00 +#define HCI_VERSION_1_1 0x01 + +/* Define an invalid value for a handle */ +#define HCI_INVALID_HANDLE 0xFFFF + +/* Define max ammount of data in the HCI command */ +#define HCI_COMMAND_SIZE 255 + +/* Define the preamble length for all HCI Commands. +** This is 2-bytes for opcode and 1 byte for length +*/ +#define HCIC_PREAMBLE_SIZE 3 + +/* Define the preamble length for all HCI Events +** This is 1-byte for opcode and 1 byte for length +*/ +#define HCIE_PREAMBLE_SIZE 2 +#define HCI_SCO_PREAMBLE_SIZE 3 +#define HCI_DATA_PREAMBLE_SIZE 4 + +/* local Bluetooth controller id for AMP HCI */ +#define LOCAL_BR_EDR_CONTROLLER_ID 0 + +/* controller id types for AMP HCI */ +#define HCI_CONTROLLER_TYPE_BR_EDR 0 +#define HCI_CONTROLLER_TYPE_802_11 1 +#define HCI_CONTROLLER_TYPE_ECMA 2 +#define HCI_MAX_CONTROLLER_TYPES 3 + +/* ConnectionLess Broadcast */ +#define HCI_CLB_DISABLE 0x00 +#define HCI_CLB_ENABLE 0x01 + +/* ConnectionLess Broadcast Data fragment */ +#define HCI_CLB_FRAGMENT_CONT 0x00 +#define HCI_CLB_FRAGMENT_START 0x01 +#define HCI_CLB_FRAGMENT_END 0x02 +#define HCI_CLB_FRAGMENT_SINGLE 0x03 + +/* AMP Controller Status codes +*/ +#define HCI_AMP_CTRLR_PHYSICALLY_DOWN 0 +#define HCI_AMP_CTRLR_USABLE_BY_BT 1 +#define HCI_AMP_CTRLR_UNUSABLE_FOR_BT 2 +#define HCI_AMP_CTRLR_LOW_CAP_FOR_BT 3 +#define HCI_AMP_CTRLR_MED_CAP_FOR_BT 4 +#define HCI_AMP_CTRLR_HIGH_CAP_FOR_BT 5 +#define HCI_AMP_CTRLR_FULL_CAP_FOR_BT 6 + +#define HCI_MAX_AMP_STATUS_TYPES 7 + + +/* Define the extended flow specification fields used by AMP */ +typedef struct { + UINT8 id; + UINT8 stype; + UINT16 max_sdu_size; + UINT32 sdu_inter_time; + UINT32 access_latency; + UINT32 flush_timeout; +} tHCI_EXT_FLOW_SPEC; + + +/* HCI message type definitions (for H4 messages) */ +#define HCIT_TYPE_COMMAND 1 +#define HCIT_TYPE_ACL_DATA 2 +#define HCIT_TYPE_SCO_DATA 3 +#define HCIT_TYPE_EVENT 4 +#define HCIT_TYPE_LM_DIAG 7 +#define HCIT_TYPE_NFC 16 + +#define HCIT_LM_DIAG_LENGTH 63 + +/* Parameter information for HCI_BRCM_SET_ACL_PRIORITY */ +#define HCI_BRCM_ACL_PRIORITY_PARAM_SIZE 3 +#define HCI_BRCM_ACL_PRIORITY_LOW 0x00 +#define HCI_BRCM_ACL_PRIORITY_HIGH 0xFF +#define HCI_BRCM_SET_ACL_PRIORITY (0x0057 | HCI_GRP_VENDOR_SPECIFIC) + +/* Define values for LMP Test Control parameters +** Test Scenario, Hopping Mode, Power Control Mode +*/ +#define LMP_TESTCTL_TESTSC_PAUSE 0 +#define LMP_TESTCTL_TESTSC_TXTEST_0 1 +#define LMP_TESTCTL_TESTSC_TXTEST_1 2 +#define LMP_TESTCTL_TESTSC_TXTEST_1010 3 +#define LMP_TESTCTL_TESTSC_PSRND_BITSEQ 4 +#define LMP_TESTCTL_TESTSC_CLOSEDLB_ACL 5 +#define LMP_TESTCTL_TESTSC_CLOSEDLB_SCO 6 +#define LMP_TESTCTL_TESTSC_ACL_NOWHIT 7 +#define LMP_TESTCTL_TESTSC_SCO_NOWHIT 8 +#define LMP_TESTCTL_TESTSC_TXTEST_11110000 9 +#define LMP_TESTCTL_TESTSC_EXITTESTMODE 255 + +#define LMP_TESTCTL_HOPMOD_RXTX1FREQ 0 +#define LMP_TESTCTL_HOPMOD_HOP_EURUSA 1 +#define LMP_TESTCTL_HOPMOD_HOP_JAPAN 2 +#define LMP_TESTCTL_HOPMOD_HOP_FRANCE 3 +#define LMP_TESTCTL_HOPMOD_HOP_SPAIN 4 +#define LMP_TESTCTL_HOPMOD_REDUCED_HOP 5 + +#define LMP_TESTCTL_POWCTL_FIXEDTX_OP 0 +#define LMP_TESTCTL_POWCTL_ADAPTIVE 1 + +// TODO(zachoverflow): remove this once broadcom specific hacks are removed +#define LMP_COMPID_BROADCOM 15 + +/* +** Define the packet types in the packet header, and a couple extra +*/ +#define PKT_TYPE_NULL 0x00 +#define PKT_TYPE_POLL 0x01 +#define PKT_TYPE_FHS 0x02 +#define PKT_TYPE_DM1 0x03 + +#define PKT_TYPE_DH1 0x04 +#define PKT_TYPE_HV1 0x05 +#define PKT_TYPE_HV2 0x06 +#define PKT_TYPE_HV3 0x07 +#define PKT_TYPE_DV 0x08 +#define PKT_TYPE_AUX1 0x09 + +#define PKT_TYPE_DM3 0x0a +#define PKT_TYPE_DH3 0x0b + +#define PKT_TYPE_DM5 0x0e +#define PKT_TYPE_DH5 0x0f + + +#define PKT_TYPE_ID 0x10 /* Internally used packet types */ +#define PKT_TYPE_BAD 0x11 +#define PKT_TYPE_NONE 0x12 + +/* +** Define packet size +*/ +#define HCI_DM1_PACKET_SIZE 17 +#define HCI_DH1_PACKET_SIZE 27 +#define HCI_DM3_PACKET_SIZE 121 +#define HCI_DH3_PACKET_SIZE 183 +#define HCI_DM5_PACKET_SIZE 224 +#define HCI_DH5_PACKET_SIZE 339 +#define HCI_AUX1_PACKET_SIZE 29 +#define HCI_HV1_PACKET_SIZE 10 +#define HCI_HV2_PACKET_SIZE 20 +#define HCI_HV3_PACKET_SIZE 30 +#define HCI_DV_PACKET_SIZE 9 +#define HCI_EDR2_DH1_PACKET_SIZE 54 +#define HCI_EDR2_DH3_PACKET_SIZE 367 +#define HCI_EDR2_DH5_PACKET_SIZE 679 +#define HCI_EDR3_DH1_PACKET_SIZE 83 +#define HCI_EDR3_DH3_PACKET_SIZE 552 +#define HCI_EDR3_DH5_PACKET_SIZE 1021 + +/* Feature Pages */ +#define HCI_EXT_FEATURES_PAGE_0 0 /* Extended Feature Page 0 (regular features) */ +#define HCI_EXT_FEATURES_PAGE_1 1 /* Extended Feature Page 1 */ +#define HCI_EXT_FEATURES_PAGE_2 2 /* Extended Feature Page 2 */ +#define HCI_EXT_FEATURES_PAGE_MAX HCI_EXT_FEATURES_PAGE_2 + +#define HCI_FEATURE_BYTES_PER_PAGE 8 + +#define HCI_FEATURES_KNOWN(x) ((x[0] | x[1] | x[2] | x[3] | x[4] | x[5] | x[6] | x[7]) != 0) + +/* +** LMP features encoding - page 0 +*/ +#define HCI_FEATURE_3_SLOT_PACKETS_MASK 0x01 +#define HCI_FEATURE_3_SLOT_PACKETS_OFF 0 +#define HCI_3_SLOT_PACKETS_SUPPORTED(x) ((x)[HCI_FEATURE_3_SLOT_PACKETS_OFF] & HCI_FEATURE_3_SLOT_PACKETS_MASK) + +#define HCI_FEATURE_5_SLOT_PACKETS_MASK 0x02 +#define HCI_FEATURE_5_SLOT_PACKETS_OFF 0 +#define HCI_5_SLOT_PACKETS_SUPPORTED(x) ((x)[HCI_FEATURE_5_SLOT_PACKETS_OFF] & HCI_FEATURE_5_SLOT_PACKETS_MASK) + +#define HCI_FEATURE_ENCRYPTION_MASK 0x04 +#define HCI_FEATURE_ENCRYPTION_OFF 0 +#define HCI_ENCRYPTION_SUPPORTED(x) ((x)[HCI_FEATURE_ENCRYPTION_OFF] & HCI_FEATURE_ENCRYPTION_MASK) + +#define HCI_FEATURE_SLOT_OFFSET_MASK 0x08 +#define HCI_FEATURE_SLOT_OFFSET_OFF 0 +#define HCI_SLOT_OFFSET_SUPPORTED(x) ((x)[HCI_FEATURE_SLOT_OFFSET_OFF] & HCI_FEATURE_SLOT_OFFSET_MASK) + +#define HCI_FEATURE_TIMING_ACC_MASK 0x10 +#define HCI_FEATURE_TIMING_ACC_OFF 0 +#define HCI_TIMING_ACC_SUPPORTED(x) ((x)[HCI_FEATURE_TIMING_ACC_OFF] & HCI_FEATURE_TIMING_ACC_MASK) + +#define HCI_FEATURE_SWITCH_MASK 0x20 +#define HCI_FEATURE_SWITCH_OFF 0 +#define HCI_SWITCH_SUPPORTED(x) ((x)[HCI_FEATURE_SWITCH_OFF] & HCI_FEATURE_SWITCH_MASK) + +#define HCI_FEATURE_HOLD_MODE_MASK 0x40 +#define HCI_FEATURE_HOLD_MODE_OFF 0 +#define HCI_HOLD_MODE_SUPPORTED(x) ((x)[HCI_FEATURE_HOLD_MODE_OFF] & HCI_FEATURE_HOLD_MODE_MASK) + +#define HCI_FEATURE_SNIFF_MODE_MASK 0x80 +#define HCI_FEATURE_SNIFF_MODE_OFF 0 +#define HCI_SNIFF_MODE_SUPPORTED(x) ((x)[HCI_FEATURE_SNIFF_MODE_OFF] & HCI_FEATURE_SNIFF_MODE_MASK) + +#define HCI_FEATURE_PARK_MODE_MASK 0x01 +#define HCI_FEATURE_PARK_MODE_OFF 1 +#define HCI_PARK_MODE_SUPPORTED(x) ((x)[HCI_FEATURE_PARK_MODE_OFF] & HCI_FEATURE_PARK_MODE_MASK) + +#define HCI_FEATURE_RSSI_MASK 0x02 +#define HCI_FEATURE_RSSI_OFF 1 +#define HCI_RSSI_SUPPORTED(x) ((x)[HCI_FEATURE_RSSI_OFF] & HCI_FEATURE_RSSI_MASK) + +#define HCI_FEATURE_CQM_DATA_RATE_MASK 0x04 +#define HCI_FEATURE_CQM_DATA_RATE_OFF 1 +#define HCI_CQM_DATA_RATE_SUPPORTED(x) ((x)[HCI_FEATURE_CQM_DATA_RATE_OFF] & HCI_FEATURE_CQM_DATA_RATE_MASK) + +#define HCI_FEATURE_SCO_LINK_MASK 0x08 +#define HCI_FEATURE_SCO_LINK_OFF 1 +#define HCI_SCO_LINK_SUPPORTED(x) ((x)[HCI_FEATURE_SCO_LINK_OFF] & HCI_FEATURE_SCO_LINK_MASK) + +#define HCI_FEATURE_HV2_PACKETS_MASK 0x10 +#define HCI_FEATURE_HV2_PACKETS_OFF 1 +#define HCI_HV2_PACKETS_SUPPORTED(x) ((x)[HCI_FEATURE_HV2_PACKETS_OFF] & HCI_FEATURE_HV2_PACKETS_MASK) + +#define HCI_FEATURE_HV3_PACKETS_MASK 0x20 +#define HCI_FEATURE_HV3_PACKETS_OFF 1 +#define HCI_HV3_PACKETS_SUPPORTED(x) ((x)[HCI_FEATURE_HV3_PACKETS_OFF] & HCI_FEATURE_HV3_PACKETS_MASK) + +#define HCI_FEATURE_U_LAW_MASK 0x40 +#define HCI_FEATURE_U_LAW_OFF 1 +#define HCI_LMP_U_LAW_SUPPORTED(x) ((x)[HCI_FEATURE_U_LAW_OFF] & HCI_FEATURE_U_LAW_MASK) + +#define HCI_FEATURE_A_LAW_MASK 0x80 +#define HCI_FEATURE_A_LAW_OFF 1 +#define HCI_LMP_A_LAW_SUPPORTED(x) ((x)[HCI_FEATURE_A_LAW_OFF] & HCI_FEATURE_A_LAW_MASK) + +#define HCI_FEATURE_CVSD_MASK 0x01 +#define HCI_FEATURE_CVSD_OFF 2 +#define HCI_LMP_CVSD_SUPPORTED(x) ((x)[HCI_FEATURE_CVSD_OFF] & HCI_FEATURE_CVSD_MASK) + +#define HCI_FEATURE_PAGING_SCHEME_MASK 0x02 +#define HCI_FEATURE_PAGING_SCHEME_OFF 2 +#define HCI_PAGING_SCHEME_SUPPORTED(x) ((x)[HCI_FEATURE_PAGING_SCHEME_OFF] & HCI_FEATURE_PAGING_SCHEME_MASK) + +#define HCI_FEATURE_POWER_CTRL_MASK 0x04 +#define HCI_FEATURE_POWER_CTRL_OFF 2 +#define HCI_POWER_CTRL_SUPPORTED(x) ((x)[HCI_FEATURE_POWER_CTRL_OFF] & HCI_FEATURE_POWER_CTRL_MASK) + +#define HCI_FEATURE_TRANSPNT_MASK 0x08 +#define HCI_FEATURE_TRANSPNT_OFF 2 +#define HCI_LMP_TRANSPNT_SUPPORTED(x) ((x)[HCI_FEATURE_TRANSPNT_OFF] & HCI_FEATURE_TRANSPNT_MASK) + +#define HCI_FEATURE_FLOW_CTRL_LAG_MASK 0x70 +#define HCI_FEATURE_FLOW_CTRL_LAG_OFF 2 +#define HCI_FLOW_CTRL_LAG_VALUE(x) (((x)[HCI_FEATURE_FLOW_CTRL_LAG_OFF] & HCI_FEATURE_FLOW_CTRL_LAG_MASK) >> 4) + +#define HCI_FEATURE_BROADCAST_ENC_MASK 0x80 +#define HCI_FEATURE_BROADCAST_ENC_OFF 2 +#define HCI_LMP_BCAST_ENC_SUPPORTED(x) ((x)[HCI_FEATURE_BROADCAST_ENC_OFF] & HCI_FEATURE_BROADCAST_ENC_MASK) + +#define HCI_FEATURE_SCATTER_MODE_MASK 0x01 +#define HCI_FEATURE_SCATTER_MODE_OFF 3 +#define HCI_LMP_SCATTER_MODE_SUPPORTED(x) ((x)[HCI_FEATURE_SCATTER_MODE_OFF] & HCI_FEATURE_SCATTER_MODE_MASK) + +#define HCI_FEATURE_EDR_ACL_2MPS_MASK 0x02 +#define HCI_FEATURE_EDR_ACL_2MPS_OFF 3 +#define HCI_EDR_ACL_2MPS_SUPPORTED(x) ((x)[HCI_FEATURE_EDR_ACL_2MPS_OFF] & HCI_FEATURE_EDR_ACL_2MPS_MASK) + +#define HCI_FEATURE_EDR_ACL_3MPS_MASK 0x04 +#define HCI_FEATURE_EDR_ACL_3MPS_OFF 3 +#define HCI_EDR_ACL_3MPS_SUPPORTED(x) ((x)[HCI_FEATURE_EDR_ACL_3MPS_OFF] & HCI_FEATURE_EDR_ACL_3MPS_MASK) + +#define HCI_FEATURE_ENHANCED_INQ_MASK 0x08 +#define HCI_FEATURE_ENHANCED_INQ_OFF 3 +#define HCI_ENHANCED_INQ_SUPPORTED(x) ((x)[HCI_FEATURE_ENHANCED_INQ_OFF] & HCI_FEATURE_ENHANCED_INQ_MASK) + +#define HCI_FEATURE_INTERLACED_INQ_SCAN_MASK 0x10 +#define HCI_FEATURE_INTERLACED_INQ_SCAN_OFF 3 +#define HCI_LMP_INTERLACED_INQ_SCAN_SUPPORTED(x) ((x)[HCI_FEATURE_INTERLACED_INQ_SCAN_OFF] & HCI_FEATURE_INTERLACED_INQ_SCAN_MASK) + +#define HCI_FEATURE_INTERLACED_PAGE_SCAN_MASK 0x20 +#define HCI_FEATURE_INTERLACED_PAGE_SCAN_OFF 3 +#define HCI_LMP_INTERLACED_PAGE_SCAN_SUPPORTED(x) ((x)[HCI_FEATURE_INTERLACED_PAGE_SCAN_OFF] & HCI_FEATURE_INTERLACED_PAGE_SCAN_MASK) + +#define HCI_FEATURE_INQ_RSSI_MASK 0x40 +#define HCI_FEATURE_INQ_RSSI_OFF 3 +#define HCI_LMP_INQ_RSSI_SUPPORTED(x) ((x)[HCI_FEATURE_INQ_RSSI_OFF] & HCI_FEATURE_INQ_RSSI_MASK) + +#define HCI_FEATURE_ESCO_EV3_MASK 0x80 +#define HCI_FEATURE_ESCO_EV3_OFF 3 +#define HCI_ESCO_EV3_SUPPORTED(x) ((x)[HCI_FEATURE_ESCO_EV3_OFF] & HCI_FEATURE_ESCO_EV3_MASK) + +#define HCI_FEATURE_ESCO_EV4_MASK 0x01 +#define HCI_FEATURE_ESCO_EV4_OFF 4 +#define HCI_ESCO_EV4_SUPPORTED(x) ((x)[HCI_FEATURE_ESCO_EV4_OFF] & HCI_FEATURE_ESCO_EV4_MASK) + +#define HCI_FEATURE_ESCO_EV5_MASK 0x02 +#define HCI_FEATURE_ESCO_EV5_OFF 4 +#define HCI_ESCO_EV5_SUPPORTED(x) ((x)[HCI_FEATURE_ESCO_EV5_OFF] & HCI_FEATURE_ESCO_EV5_MASK) + +#define HCI_FEATURE_ABSENCE_MASKS_MASK 0x04 +#define HCI_FEATURE_ABSENCE_MASKS_OFF 4 +#define HCI_LMP_ABSENCE_MASKS_SUPPORTED(x) ((x)[HCI_FEATURE_ABSENCE_MASKS_OFF] & HCI_FEATURE_ABSENCE_MASKS_MASK) + +#define HCI_FEATURE_AFH_CAP_SLAVE_MASK 0x08 +#define HCI_FEATURE_AFH_CAP_SLAVE_OFF 4 +#define HCI_LMP_AFH_CAP_SLAVE_SUPPORTED(x) ((x)[HCI_FEATURE_AFH_CAP_SLAVE_OFF] & HCI_FEATURE_AFH_CAP_SLAVE_MASK) + +#define HCI_FEATURE_AFH_CLASS_SLAVE_MASK 0x10 +#define HCI_FEATURE_AFH_CLASS_SLAVE_OFF 4 +#define HCI_LMP_AFH_CLASS_SLAVE_SUPPORTED(x) ((x)[HCI_FEATURE_AFH_CLASS_SLAVE_OFF] & HCI_FEATURE_AFH_CLASS_SLAVE_MASK) + +#if 1 +#define HCI_FEATURE_BREDR_NOT_SPT_MASK 0x20 +#define HCI_FEATURE_BREDR_NOT_SPT_OFF 4 +#define HCI_BREDR_NOT_SPT_SUPPORTED(x) ((x)[HCI_FEATURE_BREDR_NOT_SPT_OFF] & HCI_FEATURE_BREDR_NOT_SPT_MASK) + +#define HCI_FEATURE_LE_SPT_MASK 0x40 +#define HCI_FEATURE_LE_SPT_OFF 4 +#define HCI_LE_SPT_SUPPORTED(x) ((x)[HCI_FEATURE_LE_SPT_OFF] & HCI_FEATURE_LE_SPT_MASK) +#else + +#define HCI_FEATURE_ALIAS_AUTH_MASK 0x20 +#define HCI_FEATURE_ALIAS_AUTH_OFF 4 +#define HCI_LMP_ALIAS_AUTH_SUPPORTED(x) ((x)[HCI_FEATURE_ALIAS_AUTH_OFF] & HCI_FEATURE_ALIAS_AUTH_MASK) + +#define HCI_FEATURE_ANON_MODE_MASK 0x40 +#define HCI_FEATURE_ANON_MODE_OFF 4 +#define HCI_LMP_ANON_MODE_SUPPORTED(x) ((x)[HCI_FEATURE_ANON_MODE_OFF] & HCI_FEATURE_ANON_MODE_MASK) +#endif + +#define HCI_FEATURE_3_SLOT_EDR_ACL_MASK 0x80 +#define HCI_FEATURE_3_SLOT_EDR_ACL_OFF 4 +#define HCI_3_SLOT_EDR_ACL_SUPPORTED(x) ((x)[HCI_FEATURE_3_SLOT_EDR_ACL_OFF] & HCI_FEATURE_3_SLOT_EDR_ACL_MASK) + +#define HCI_FEATURE_5_SLOT_EDR_ACL_MASK 0x01 +#define HCI_FEATURE_5_SLOT_EDR_ACL_OFF 5 +#define HCI_5_SLOT_EDR_ACL_SUPPORTED(x) ((x)[HCI_FEATURE_5_SLOT_EDR_ACL_OFF] & HCI_FEATURE_5_SLOT_EDR_ACL_MASK) + +#define HCI_FEATURE_SNIFF_SUB_RATE_MASK 0x02 +#define HCI_FEATURE_SNIFF_SUB_RATE_OFF 5 +#define HCI_SNIFF_SUB_RATE_SUPPORTED(x) ((x)[HCI_FEATURE_SNIFF_SUB_RATE_OFF] & HCI_FEATURE_SNIFF_SUB_RATE_MASK) + +#define HCI_FEATURE_ATOMIC_ENCRYPT_MASK 0x04 +#define HCI_FEATURE_ATOMIC_ENCRYPT_OFF 5 +#define HCI_ATOMIC_ENCRYPT_SUPPORTED(x) ((x)[HCI_FEATURE_ATOMIC_ENCRYPT_OFF] & HCI_FEATURE_ATOMIC_ENCRYPT_MASK) + +#define HCI_FEATURE_AFH_CAP_MASTR_MASK 0x08 +#define HCI_FEATURE_AFH_CAP_MASTR_OFF 5 +#define HCI_LMP_AFH_CAP_MASTR_SUPPORTED(x) ((x)[HCI_FEATURE_AFH_CAP_MASTR_OFF] & HCI_FEATURE_AFH_CAP_MASTR_MASK) + +#define HCI_FEATURE_AFH_CLASS_MASTR_MASK 0x10 +#define HCI_FEATURE_AFH_CLASS_MASTR_OFF 5 +#define HCI_LMP_AFH_CLASS_MASTR_SUPPORTED(x) ((x)[HCI_FEATURE_AFH_CLASS_MASTR_OFF] & HCI_FEATURE_AFH_CLASS_MASTR_MASK) + +#define HCI_FEATURE_EDR_ESCO_2MPS_MASK 0x20 +#define HCI_FEATURE_EDR_ESCO_2MPS_OFF 5 +#define HCI_EDR_ESCO_2MPS_SUPPORTED(x) ((x)[HCI_FEATURE_EDR_ESCO_2MPS_OFF] & HCI_FEATURE_EDR_ESCO_2MPS_MASK) + +#define HCI_FEATURE_EDR_ESCO_3MPS_MASK 0x40 +#define HCI_FEATURE_EDR_ESCO_3MPS_OFF 5 +#define HCI_EDR_ESCO_3MPS_SUPPORTED(x) ((x)[HCI_FEATURE_EDR_ESCO_3MPS_OFF] & HCI_FEATURE_EDR_ESCO_3MPS_MASK) + +#define HCI_FEATURE_3_SLOT_EDR_ESCO_MASK 0x80 +#define HCI_FEATURE_3_SLOT_EDR_ESCO_OFF 5 +#define HCI_3_SLOT_EDR_ESCO_SUPPORTED(x) ((x)[HCI_FEATURE_3_SLOT_EDR_ESCO_OFF] & HCI_FEATURE_3_SLOT_EDR_ESCO_MASK) + +#define HCI_FEATURE_EXT_INQ_RSP_MASK 0x01 +#define HCI_FEATURE_EXT_INQ_RSP_OFF 6 +#define HCI_EXT_INQ_RSP_SUPPORTED(x) ((x)[HCI_FEATURE_EXT_INQ_RSP_OFF] & HCI_FEATURE_EXT_INQ_RSP_MASK) + +#if 1 /* TOKYO spec definition */ +#define HCI_FEATURE_SIMUL_LE_BREDR_MASK 0x02 +#define HCI_FEATURE_SIMUL_LE_BREDR_OFF 6 +#define HCI_SIMUL_LE_BREDR_SUPPORTED(x) ((x)[HCI_FEATURE_SIMUL_LE_BREDR_OFF] & HCI_FEATURE_SIMUL_LE_BREDR_MASK) + +#else +#define HCI_FEATURE_ANUM_PIN_AWARE_MASK 0x02 +#define HCI_FEATURE_ANUM_PIN_AWARE_OFF 6 +#define HCI_ANUM_PIN_AWARE_SUPPORTED(x) ((x)[HCI_FEATURE_ANUM_PIN_AWARE_OFF] & HCI_FEATURE_ANUM_PIN_AWARE_MASK) +#endif + +#define HCI_FEATURE_ANUM_PIN_CAP_MASK 0x04 +#define HCI_FEATURE_ANUM_PIN_CAP_OFF 6 +#define HCI_ANUM_PIN_CAP_SUPPORTED(x) ((x)[HCI_FEATURE_ANUM_PIN_CAP_OFF] & HCI_FEATURE_ANUM_PIN_CAP_MASK) + +#define HCI_FEATURE_SIMPLE_PAIRING_MASK 0x08 +#define HCI_FEATURE_SIMPLE_PAIRING_OFF 6 +#define HCI_SIMPLE_PAIRING_SUPPORTED(x) ((x)[HCI_FEATURE_SIMPLE_PAIRING_OFF] & HCI_FEATURE_SIMPLE_PAIRING_MASK) + +#define HCI_FEATURE_ENCAP_PDU_MASK 0x10 +#define HCI_FEATURE_ENCAP_PDU_OFF 6 +#define HCI_ENCAP_PDU_SUPPORTED(x) ((x)[HCI_FEATURE_ENCAP_PDU_OFF] & HCI_FEATURE_ENCAP_PDU_MASK) + +#define HCI_FEATURE_ERROR_DATA_MASK 0x20 +#define HCI_FEATURE_ERROR_DATA_OFF 6 +#define HCI_ERROR_DATA_SUPPORTED(x) ((x)[HCI_FEATURE_ERROR_DATA_OFF] & HCI_FEATURE_ERROR_DATA_MASK) + +#define HCI_FEATURE_NON_FLUSHABLE_PB_MASK 0x40 +#define HCI_FEATURE_NON_FLUSHABLE_PB_OFF 6 + +/* This feature is causing frequent link drops when doing call switch with certain av/hfp headsets */ +#define HCI_NON_FLUSHABLE_PB_SUPPORTED(x) (0)//((x)[HCI_FEATURE_NON_FLUSHABLE_PB_OFF] & HCI_FEATURE_NON_FLUSHABLE_PB_MASK) + +#define HCI_FEATURE_LINK_SUP_TO_EVT_MASK 0x01 +#define HCI_FEATURE_LINK_SUP_TO_EVT_OFF 7 +#define HCI_LINK_SUP_TO_EVT_SUPPORTED(x) ((x)[HCI_FEATURE_LINK_SUP_TO_EVT_OFF] & HCI_FEATURE_LINK_SUP_TO_EVT_MASK) + +#define HCI_FEATURE_INQ_RESP_TX_MASK 0x02 +#define HCI_FEATURE_INQ_RESP_TX_OFF 7 +#define HCI_INQ_RESP_TX_SUPPORTED(x) ((x)[HCI_FEATURE_INQ_RESP_TX_OFF] & HCI_FEATURE_INQ_RESP_TX_MASK) + +#define HCI_FEATURE_EXTENDED_MASK 0x80 +#define HCI_FEATURE_EXTENDED_OFF 7 +#define HCI_LMP_EXTENDED_SUPPORTED(x) ((x)[HCI_FEATURE_EXTENDED_OFF] & HCI_FEATURE_EXTENDED_MASK) + +/* +** LMP features encoding - page 1 +*/ +#define HCI_EXT_FEATURE_SSP_HOST_MASK 0x01 +#define HCI_EXT_FEATURE_SSP_HOST_OFF 0 +#define HCI_SSP_HOST_SUPPORTED(x) ((x)[HCI_EXT_FEATURE_SSP_HOST_OFF] & HCI_EXT_FEATURE_SSP_HOST_MASK) + +#define HCI_EXT_FEATURE_LE_HOST_MASK 0x02 +#define HCI_EXT_FEATURE_LE_HOST_OFF 0 +#define HCI_LE_HOST_SUPPORTED(x) ((x)[HCI_EXT_FEATURE_LE_HOST_OFF] & HCI_EXT_FEATURE_LE_HOST_MASK) + +#define HCI_EXT_FEATURE_SIMUL_DUMO_HOST_MASK 0x04 +#define HCI_EXT_FEATURE_SIMUL_DUMO_HOST_OFF 0 +#define HCI_SIMUL_DUMO_HOST_SUPPORTED(x) ((x)[HCI_EXT_FEATURE_SIMUL_DUMO_HOST_OFF] & HCI_EXT_FEATURE_SIMUL_DUMO_HOST_MASK) + +#define HCI_EXT_FEATURE_SC_HOST_MASK 0x08 +#define HCI_EXT_FEATURE_SC_HOST_OFF 0 +#define HCI_SC_HOST_SUPPORTED(x) ((x)[HCI_EXT_FEATURE_SC_HOST_OFF] & HCI_EXT_FEATURE_SC_HOST_MASK) + +/* +** LMP features encoding - page 2 +*/ +#define HCI_EXT_FEATURE_CSB_MASTER_MASK 0x01 +#define HCI_EXT_FEATURE_CSB_MASTER_OFF 0 +#define HCI_CSB_MASTER_SUPPORTED(x) ((x)[HCI_EXT_FEATURE_CSB_MASTER_OFF] & HCI_EXT_FEATURE_CSB_MASTER_MASK) + +#define HCI_EXT_FEATURE_CSB_SLAVE_MASK 0x02 +#define HCI_EXT_FEATURE_CSB_SLAVE_OFF 0 +#define HCI_CSB_SLAVE_SUPPORTED(x) ((x)[HCI_EXT_FEATURE_CSB_SLAVE_OFF] & HCI_EXT_FEATURE_CSB_SLAVE_MASK) + +#define HCI_EXT_FEATURE_SYNC_TRAIN_MASTER_MASK 0x04 +#define HCI_EXT_FEATURE_SYNC_TRAIN_MASTER_OFF 0 +#define HCI_SYNC_TRAIN_MASTER_SUPPORTED(x) ((x)[HCI_EXT_FEATURE_SYNC_TRAIN_MASTER_OFF] & HCI_EXT_FEATURE_SYNC_TRAIN_MASTER_MASK) + +#define HCI_EXT_FEATURE_SYNC_SCAN_SLAVE_MASK 0x08 +#define HCI_EXT_FEATURE_SYNC_SCAN_SLAVE_OFF 0 +#define HCI_SYNC_SCAN_SLAVE_SUPPORTED(x) ((x)[HCI_EXT_FEATURE_SYNC_SCAN_SLAVE_OFF] & HCI_EXT_FEATURE_SYNC_SCAN_SLAVE_MASK) + +#define HCI_EXT_FEATURE_INQ_RESP_NOTIF_MASK 0x10 +#define HCI_EXT_FEATURE_INQ_RESP_NOTIF_OFF 0 +#define HCI_INQ_RESP_NOTIF_SUPPORTED(x) ((x)[HCI_EXT_FEATURE_INQ_RESP_NOTIF_OFF] & HCI_EXT_FEATURE_INQ_RESP_NOTIF_MASK) + +#define HCI_EXT_FEATURE_SC_CTRLR_MASK 0x01 +#define HCI_EXT_FEATURE_SC_CTRLR_OFF 1 +#define HCI_SC_CTRLR_SUPPORTED(x) ((x)[HCI_EXT_FEATURE_SC_CTRLR_OFF] & HCI_EXT_FEATURE_SC_CTRLR_MASK) + +#define HCI_EXT_FEATURE_PING_MASK 0x02 +#define HCI_EXT_FEATURE_PING_OFF 1 +#define HCI_PING_SUPPORTED(x) ((x)[HCI_EXT_FEATURE_PING_OFF] & HCI_EXT_FEATURE_PING_MASK) + +/* +** LE features encoding - page 0 (the only page for now) +*/ +/* LE Encryption */ +#define HCI_LE_FEATURE_LE_ENCRYPTION_MASK 0x01 +#define HCI_LE_FEATURE_LE_ENCRYPTION_OFF 0 +#define HCI_LE_ENCRYPTION_SUPPORTED(x) ((x)[HCI_LE_FEATURE_LE_ENCRYPTION_OFF] & HCI_LE_FEATURE_LE_ENCRYPTION_MASK) + +/* Connection Parameters Request Procedure */ +#define HCI_LE_FEATURE_CONN_PARAM_REQ_MASK 0x02 +#define HCI_LE_FEATURE_CONN_PARAM_REQ_OFF 0 +#define HCI_LE_CONN_PARAM_REQ_SUPPORTED(x) ((x)[HCI_LE_FEATURE_CONN_PARAM_REQ_OFF] & HCI_LE_FEATURE_CONN_PARAM_REQ_MASK) + +/* Extended Reject Indication */ +#define HCI_LE_FEATURE_EXT_REJ_IND_MASK 0x04 +#define HCI_LE_FEATURE_EXT_REJ_IND_OFF 0 +#define HCI_LE_EXT_REJ_IND_SUPPORTED(x) ((x)[HCI_LE_FEATURE_EXT_REJ_IND_OFF] & HCI_LE_FEATURE_EXT_REJ_IND_MASK) + +/* Slave-initiated Features Exchange */ +#define HCI_LE_FEATURE_SLAVE_INIT_FEAT_EXC_MASK 0x08 +#define HCI_LE_FEATURE_SLAVE_INIT_FEAT_EXC_OFF 0 +#define HCI_LE_SLAVE_INIT_FEAT_EXC_SUPPORTED(x) ((x)[HCI_LE_FEATURE_SLAVE_INIT_FEAT_EXC_OFF] & HCI_LE_FEATURE_SLAVE_INIT_FEAT_EXC_MASK) + +/* Enhanced privacy Feature: bit 6 */ +#define HCI_LE_FEATURE_ENHANCED_PRIVACY_MASK 0x40 +#define HCI_LE_FEATURE_ENHANCED_PRIVACY_OFF 0 +#define HCI_LE_ENHANCED_PRIVACY_SUPPORTED(x) ((x)[HCI_LE_FEATURE_ENHANCED_PRIVACY_OFF] & HCI_LE_FEATURE_ENHANCED_PRIVACY_MASK) + +/* Extended scanner filter policy : 7 */ +#define HCI_LE_FEATURE_EXT_SCAN_FILTER_POLICY_MASK 0x80 +#define HCI_LE_FEATURE_EXT_SCAN_FILTER_POLICY_OFF 0 +#define HCI_LE_EXT_SCAN_FILTER_POLICY_SUPPORTED(x) ((x)[HCI_LE_FEATURE_EXT_SCAN_FILTER_POLICY_OFF] & HCI_LE_FEATURE_EXT_SCAN_FILTER_POLICY_MASK) + +/* Slave-initiated Features Exchange */ +#define HCI_LE_FEATURE_DATA_LEN_EXT_MASK 0x20 +#define HCI_LE_FEATURE_DATA_LEN_EXT_OFF 0 +#define HCI_LE_DATA_LEN_EXT_SUPPORTED(x) ((x)[HCI_LE_FEATURE_DATA_LEN_EXT_OFF] & HCI_LE_FEATURE_DATA_LEN_EXT_MASK) + +/* +** Local Supported Commands encoding +*/ +#define HCI_NUM_SUPP_COMMANDS_BYTES 64 + +/* Supported Commands Byte 0 */ +#define HCI_SUPP_COMMANDS_INQUIRY_MASK 0x01 +#define HCI_SUPP_COMMANDS_INQUIRY_OFF 0 +#define HCI_INQUIRY_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_INQUIRY_OFF] & HCI_SUPP_COMMANDS_INQUIRY_MASK) + +#define HCI_SUPP_COMMANDS_INQUIRY_CANCEL_MASK 0x02 +#define HCI_SUPP_COMMANDS_INQUIRY_CANCEL_OFF 0 +#define HCI_INQUIRY_CANCEL_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_INQUIRY_CANCEL_OFF] & HCI_SUPP_COMMANDS_INQUIRY_CANCEL_MASK) + +#define HCI_SUPP_COMMANDS_PERIODIC_INQUIRY_MASK 0x04 +#define HCI_SUPP_COMMANDS_PERIODIC_INQUIRY_OFF 0 +#define HCI_PERIODIC_INQUIRY_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_PERIODIC_INQUIRY_OFF] & HCI_SUPP_COMMANDS_PERIODIC_INQUIRY_MASK) + +#define HCI_SUPP_COMMANDS_EXIT_PERIODIC_INQUIRY_MASK 0x08 +#define HCI_SUPP_COMMANDS_EXIT_PERIODIC_INQUIRY_OFF 0 +#define HCI_EXIT_PERIODIC_INQUIRY_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_EXIT_PERIODIC_INQUIRY_OFF] & HCI_SUPP_COMMANDS_EXIT_PERIODIC_INQUIRY_MASK) + +#define HCI_SUPP_COMMANDS_CREATE_CONN_MASK 0x10 +#define HCI_SUPP_COMMANDS_CREATE_CONN_OFF 0 +#define HCI_CREATE_CONN_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_CREATE_CONN_OFF] & HCI_SUPP_COMMANDS_CREATE_CONN_MASK) + +#define HCI_SUPP_COMMANDS_DISCONNECT_MASK 0x20 +#define HCI_SUPP_COMMANDS_DISCONNECT_OFF 0 +#define HCI_DISCONNECT_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_DISCONNECT_OFF] & HCI_SUPP_COMMANDS_DISCONNECT_MASK) + +#define HCI_SUPP_COMMANDS_ADD_SCO_CONN_MASK 0x40 +#define HCI_SUPP_COMMANDS_ADD_SCO_CONN_OFF 0 +#define HCI_ADD_SCO_CONN_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_ADD_SCO_CONN_OFF] & HCI_SUPP_COMMANDS_ADD_SCO_CONN_MASK) + +#define HCI_SUPP_COMMANDS_CANCEL_CREATE_CONN_MASK 0x80 +#define HCI_SUPP_COMMANDS_CANCEL_CREATE_CONN_OFF 0 +#define HCI_CANCEL_CREATE_CONN_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_CANCEL_CREATE_CONN_OFF] & HCI_SUPP_COMMANDS_CANCEL_CREATE_CONN_MASK) + +#define HCI_SUPP_COMMANDS_ACCEPT_CONN_REQUEST_MASK 0x01 +#define HCI_SUPP_COMMANDS_ACCEPT_CONN_REQUEST_OFF 1 +#define HCI_ACCEPT_CONN_REQUEST_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_ACCEPT_CONN_REQUEST_OFF] & HCI_SUPP_COMMANDS_ACCEPT_CONN_REQUEST_MASK) + +#define HCI_SUPP_COMMANDS_REJECT_CONN_REQUEST_MASK 0x02 +#define HCI_SUPP_COMMANDS_REJECT_CONN_REQUEST_OFF 1 +#define HCI_REJECT_CONN_REQUEST_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_REJECT_CONN_REQUEST_OFF] & HCI_SUPP_COMMANDS_REJECT_CONN_REQUEST_MASK) + +#define HCI_SUPP_COMMANDS_LINK_KEY_REQUEST_REPLY_MASK 0x04 +#define HCI_SUPP_COMMANDS_LINK_KEY_REQUEST_REPLY_OFF 1 +#define HCI_LINK_KEY_REQUEST_REPLY_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_LINK_KEY_REQUEST_REPLY_OFF] & HCI_SUPP_COMMANDS_LINK_KEY_REQUEST_REPLY_MASK) + +#define HCI_SUPP_COMMANDS_LINK_KEY_REQUEST_NEG_REPLY_MASK 0x08 +#define HCI_SUPP_COMMANDS_LINK_KEY_REQUEST_NEG_REPLY_OFF 1 +#define HCI_LINK_KEY_REQUEST_NEG_REPLY_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_LINK_KEY_REQUEST_NEG_REPLY_OFF] & HCI_SUPP_COMMANDS_LINK_KEY_REQUEST_NEG_REPLY_MASK) + +#define HCI_SUPP_COMMANDS_PIN_CODE_REQUEST_REPLY_MASK 0x10 +#define HCI_SUPP_COMMANDS_PIN_CODE_REQUEST_REPLY_OFF 1 +#define HCI_PIN_CODE_REQUEST_REPLY_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_PIN_CODE_REQUEST_REPLY_OFF] & HCI_SUPP_COMMANDS_PIN_CODE_REQUEST_REPLY_MASK) + +#define HCI_SUPP_COMMANDS_PIN_CODE_REQUEST_NEG_REPLY_MASK 0x20 +#define HCI_SUPP_COMMANDS_PIN_CODE_REQUEST_NEG_REPLY_OFF 1 +#define HCI_PIN_CODE_REQUEST_NEG_REPLY_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_PIN_CODE_REQUEST_NEG_REPLY_OFF] & HCI_SUPP_COMMANDS_PIN_CODE_REQUEST_NEG_REPLY_MASK) + +#define HCI_SUPP_COMMANDS_CHANGE_CONN_PKT_TYPE_MASK 0x40 +#define HCI_SUPP_COMMANDS_CHANGE_CONN_PKT_TYPE_OFF 1 +#define HCI_CHANGE_CONN_PKT_TYPE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_CHANGE_CONN_PKT_TYPE_OFF] & HCI_SUPP_COMMANDS_CHANGE_CONN_PKT_TYPE_MASK) + +#define HCI_SUPP_COMMANDS_AUTH_REQUEST_MASK 0x80 +#define HCI_SUPP_COMMANDS_AUTH_REQUEST_OFF 1 +#define HCI_AUTH_REQUEST_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_AUTH_REQUEST_OFF] & HCI_SUPP_COMMANDS_AUTH_REQUEST_MASK) + +#define HCI_SUPP_COMMANDS_SET_CONN_ENCRYPTION_MASK 0x01 +#define HCI_SUPP_COMMANDS_SET_CONN_ENCRYPTION_OFF 2 +#define HCI_SET_CONN_ENCRYPTION_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_SET_CONN_ENCRYPTION_OFF] & HCI_SUPP_COMMANDS_SET_CONN_ENCRYPTION_MASK) + +#define HCI_SUPP_COMMANDS_CHANGE_CONN_LINK_KEY_MASK 0x02 +#define HCI_SUPP_COMMANDS_CHANGE_CONN_LINK_KEY_OFF 2 +#define HCI_CHANGE_CONN_LINK_KEY_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_CHANGE_CONN_LINK_KEY_OFF] & HCI_SUPP_COMMANDS_CHANGE_CONN_LINK_KEY_MASK) + +#define HCI_SUPP_COMMANDS_MASTER_LINK_KEY_MASK 0x04 +#define HCI_SUPP_COMMANDS_MASTER_LINK_KEY_OFF 2 +#define HCI_MASTER_LINK_KEY_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_MASTER_LINK_KEY_OFF] & HCI_SUPP_COMMANDS_MASTER_LINK_KEY_MASK) + +#define HCI_SUPP_COMMANDS_REMOTE_NAME_REQUEST_MASK 0x08 +#define HCI_SUPP_COMMANDS_REMOTE_NAME_REQUEST_OFF 2 +#define HCI_REMOTE_NAME_REQUEST_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_REMOTE_NAME_REQUEST_OFF] & HCI_SUPP_COMMANDS_REMOTE_NAME_REQUEST_MASK) + +#define HCI_SUPP_COMMANDS_CANCEL_REMOTE_NAME_REQUEST_MASK 0x10 +#define HCI_SUPP_COMMANDS_CANCEL_REMOTE_NAME_REQUEST_OFF 2 +#define HCI_CANCEL_REMOTE_NAME_REQUEST_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_CANCEL_REMOTE_NAME_REQUEST_OFF] & HCI_SUPP_COMMANDS_CANCEL_REMOTE_NAME_REQUEST_MASK) + +#define HCI_SUPP_COMMANDS_READ_REMOTE_SUPP_FEATURES_MASK 0x20 +#define HCI_SUPP_COMMANDS_READ_REMOTE_SUPP_FEATURES_OFF 2 +#define HCI_READ_REMOTE_SUPP_FEATURES_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_REMOTE_SUPP_FEATURES_OFF] & HCI_SUPP_COMMANDS_READ_REMOTE_SUPP_FEATURES_MASK) + +#define HCI_SUPP_COMMANDS_READ_REMOTE_EXT_FEATURES_MASK 0x40 +#define HCI_SUPP_COMMANDS_READ_REMOTE_EXT_FEATURES_OFF 2 +#define HCI_READ_REMOTE_EXT_FEATURES_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_REMOTE_EXT_FEATURES_OFF] & HCI_SUPP_COMMANDS_READ_REMOTE_EXT_FEATURES_MASK) + +#define HCI_SUPP_COMMANDS_READ_REMOTE_VER_INFO_MASK 0x80 +#define HCI_SUPP_COMMANDS_READ_REMOTE_VER_INFO_OFF 2 +#define HCI_READ_REMOTE_VER_INFO_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_REMOTE_VER_INFO_OFF] & HCI_SUPP_COMMANDS_READ_REMOTE_VER_INFO_MASK) + +#define HCI_SUPP_COMMANDS_READ_CLOCK_OFFSET_MASK 0x01 +#define HCI_SUPP_COMMANDS_READ_CLOCK_OFFSET_OFF 3 +#define HCI_READ_CLOCK_OFFSET_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_CLOCK_OFFSET_OFF] & HCI_SUPP_COMMANDS_READ_CLOCK_OFFSET_MASK) + +#define HCI_SUPP_COMMANDS_READ_LMP_HANDLE_MASK 0x02 +#define HCI_SUPP_COMMANDS_READ_LMP_HANDLE_OFF 3 +#define HCI_READ_LMP_HANDLE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_LMP_HANDLE_OFF] & HCI_SUPP_COMMANDS_READ_LMP_HANDLE_MASK) + +#define HCI_SUPP_COMMANDS_HOLD_MODE_CMD_MASK 0x02 +#define HCI_SUPP_COMMANDS_HOLD_MODE_CMD_OFF 4 +#define HCI_HOLD_MODE_CMD_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_HOLD_MODE_CMD_OFF] & HCI_SUPP_COMMANDS_HOLD_MODE_CMD_MASK) + +#define HCI_SUPP_COMMANDS_SNIFF_MODE_CMD_MASK 0x04 +#define HCI_SUPP_COMMANDS_SNIFF_MODE_CMD_OFF 4 +#define HCI_SNIFF_MODE_CMD_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_SNIFF_MODE_CMD_OFF] & HCI_SUPP_COMMANDS_SNIFF_MODE_CMD_MASK) + +#define HCI_SUPP_COMMANDS_EXIT_SNIFF_MODE_MASK 0x08 +#define HCI_SUPP_COMMANDS_EXIT_SNIFF_MODE_OFF 4 +#define HCI_EXIT_SNIFF_MODE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_EXIT_SNIFF_MODE_OFF] & HCI_SUPP_COMMANDS_EXIT_SNIFF_MODE_MASK) + +#define HCI_SUPP_COMMANDS_PARK_STATE_MASK 0x10 +#define HCI_SUPP_COMMANDS_PARK_STATE_OFF 4 +#define HCI_PARK_STATE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_PARK_STATE_OFF] & HCI_SUPP_COMMANDS_PARK_STATE_MASK) + +#define HCI_SUPP_COMMANDS_EXIT_PARK_STATE_MASK 0x20 +#define HCI_SUPP_COMMANDS_EXIT_PARK_STATE_OFF 4 +#define HCI_EXIT_PARK_STATE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_EXIT_PARK_STATE_OFF] & HCI_SUPP_COMMANDS_EXIT_PARK_STATE_MASK) + +#define HCI_SUPP_COMMANDS_QOS_SETUP_MASK 0x40 +#define HCI_SUPP_COMMANDS_QOS_SETUP_OFF 4 +#define HCI_QOS_SETUP_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_QOS_SETUP_OFF] & HCI_SUPP_COMMANDS_QOS_SETUP_MASK) + +#define HCI_SUPP_COMMANDS_ROLE_DISCOVERY_MASK 0x80 +#define HCI_SUPP_COMMANDS_ROLE_DISCOVERY_OFF 4 +#define HCI_ROLE_DISCOVERY_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_ROLE_DISCOVERY_OFF] & HCI_SUPP_COMMANDS_ROLE_DISCOVERY_MASK) + +#define HCI_SUPP_COMMANDS_SWITCH_ROLE_MASK 0x01 +#define HCI_SUPP_COMMANDS_SWITCH_ROLE_OFF 5 +#define HCI_SWITCH_ROLE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_SWITCH_ROLE_OFF] & HCI_SUPP_COMMANDS_SWITCH_ROLE_MASK) + +#define HCI_SUPP_COMMANDS_READ_LINK_POLICY_SET_MASK 0x02 +#define HCI_SUPP_COMMANDS_READ_LINK_POLICY_SET_OFF 5 +#define HCI_READ_LINK_POLICY_SET_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_LINK_POLICY_SET_OFF] & HCI_SUPP_COMMANDS_READ_LINK_POLICY_SET_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_LINK_POLICY_SET_MASK 0x04 +#define HCI_SUPP_COMMANDS_WRITE_LINK_POLICY_SET_OFF 5 +#define HCI_WRITE_LINK_POLICY_SET_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_LINK_POLICY_SET_OFF] & HCI_SUPP_COMMANDS_WRITE_LINK_POLICY_SET_MASK) + +#define HCI_SUPP_COMMANDS_READ_DEF_LINK_POLICY_SET_MASK 0x08 +#define HCI_SUPP_COMMANDS_READ_DEF_LINK_POLICY_SET_OFF 5 +#define HCI_READ_DEF_LINK_POLICY_SET_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_DEF_LINK_POLICY_SET_OFF] & HCI_SUPP_COMMANDS_READ_DEF_LINK_POLICY_SET_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_DEF_LINK_POLICY_SET_MASK 0x10 +#define HCI_SUPP_COMMANDS_WRITE_DEF_LINK_POLICY_SET_OFF 5 +#define HCI_WRITE_DEF_LINK_POLICY_SET_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_DEF_LINK_POLICY_SET_OFF] & HCI_SUPP_COMMANDS_WRITE_DEF_LINK_POLICY_SET_MASK) + +#define HCI_SUPP_COMMANDS_FLOW_SPECIFICATION_MASK 0x20 +#define HCI_SUPP_COMMANDS_FLOW_SPECIFICATION_OFF 5 +#define HCI_FLOW_SPECIFICATION_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_FLOW_SPECIFICATION_OFF] & HCI_SUPP_COMMANDS_FLOW_SPECIFICATION_MASK) + +#define HCI_SUPP_COMMANDS_SET_EVENT_MASK_MASK 0x40 +#define HCI_SUPP_COMMANDS_SET_EVENT_MASK_OFF 5 +#define HCI_SET_EVENT_MASK_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_SET_EVENT_MASK_OFF] & HCI_SUPP_COMMANDS_SET_EVENT_MASK_MASK) + +#define HCI_SUPP_COMMANDS_RESET_MASK 0x80 +#define HCI_SUPP_COMMANDS_RESET_OFF 5 +#define HCI_RESET_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_RESET_OFF] & HCI_SUPP_COMMANDS_RESET_MASK) + +#define HCI_SUPP_COMMANDS_SET_EVENT_FILTER_MASK 0x01 +#define HCI_SUPP_COMMANDS_SET_EVENT_FILTER_OFF 6 +#define HCI_SET_EVENT_FILTER_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_SET_EVENT_FILTER_OFF] & HCI_SUPP_COMMANDS_SET_EVENT_FILTER_MASK) + +#define HCI_SUPP_COMMANDS_FLUSH_MASK 0x02 +#define HCI_SUPP_COMMANDS_FLUSH_OFF 6 +#define HCI_FLUSH_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_FLUSH_OFF] & HCI_SUPP_COMMANDS_FLUSH_MASK) + +#define HCI_SUPP_COMMANDS_READ_PIN_TYPE_MASK 0x04 +#define HCI_SUPP_COMMANDS_READ_PIN_TYPE_OFF 6 +#define HCI_READ_PIN_TYPE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_PIN_TYPE_OFF] & HCI_SUPP_COMMANDS_READ_PIN_TYPE_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_PIN_TYPE_MASK 0x08 +#define HCI_SUPP_COMMANDS_WRITE_PIN_TYPE_OFF 6 +#define HCI_WRITE_PIN_TYPE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_PIN_TYPE_OFF] & HCI_SUPP_COMMANDS_WRITE_PIN_TYPE_MASK) + +#define HCI_SUPP_COMMANDS_CREATE_NEW_UNIT_KEY_MASK 0x10 +#define HCI_SUPP_COMMANDS_CREATE_NEW_UNIT_KEY_OFF 6 +#define HCI_CREATE_NEW_UNIT_KEY_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_CREATE_NEW_UNIT_KEY_OFF] & HCI_SUPP_COMMANDS_CREATE_NEW_UNIT_KEY_MASK) + +#define HCI_SUPP_COMMANDS_READ_STORED_LINK_KEY_MASK 0x20 +#define HCI_SUPP_COMMANDS_READ_STORED_LINK_KEY_OFF 6 +#define HCI_READ_STORED_LINK_KEY_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_STORED_LINK_KEY_OFF] & HCI_SUPP_COMMANDS_READ_STORED_LINK_KEY_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_STORED_LINK_KEY_MASK 0x40 +#define HCI_SUPP_COMMANDS_WRITE_STORED_LINK_KEY_OFF 6 +#define HCI_WRITE_STORED_LINK_KEY_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_STORED_LINK_KEY_OFF] & HCI_SUPP_COMMANDS_WRITE_STORED_LINK_KEY_MASK) + +#define HCI_SUPP_COMMANDS_DELETE_STORED_LINK_KEY_MASK 0x80 +#define HCI_SUPP_COMMANDS_DELETE_STORED_LINK_KEY_OFF 6 +#define HCI_DELETE_STORED_LINK_KEY_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_DELETE_STORED_LINK_KEY_OFF] & HCI_SUPP_COMMANDS_DELETE_STORED_LINK_KEY_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_LOCAL_NAME_MASK 0x01 +#define HCI_SUPP_COMMANDS_WRITE_LOCAL_NAME_OFF 7 +#define HCI_WRITE_LOCAL_NAME_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_LOCAL_NAME_OFF] & HCI_SUPP_COMMANDS_WRITE_LOCAL_NAME_MASK) + +#define HCI_SUPP_COMMANDS_READ_LOCAL_NAME_MASK 0x02 +#define HCI_SUPP_COMMANDS_READ_LOCAL_NAME_OFF 7 +#define HCI_READ_LOCAL_NAME_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_LOCAL_NAME_OFF] & HCI_SUPP_COMMANDS_READ_LOCAL_NAME_MASK) + +#define HCI_SUPP_COMMANDS_READ_CONN_ACCEPT_TOUT_MASK 0x04 +#define HCI_SUPP_COMMANDS_READ_CONN_ACCEPT_TOUT_OFF 7 +#define HCI_READ_CONN_ACCEPT_TOUT_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_CONN_ACCEPT_TOUT_OFF] & HCI_SUPP_COMMANDS_READ_CONN_ACCEPT_TOUT_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_CONN_ACCEPT_TOUT_MASK 0x08 +#define HCI_SUPP_COMMANDS_WRITE_CONN_ACCEPT_TOUT_OFF 7 +#define HCI_WRITE_CONN_ACCEPT_TOUT_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_CONN_ACCEPT_TOUT_OFF] & HCI_SUPP_COMMANDS_WRITE_CONN_ACCEPT_TOUT_MASK) + +#define HCI_SUPP_COMMANDS_READ_PAGE_TOUT_MASK 0x10 +#define HCI_SUPP_COMMANDS_READ_PAGE_TOUT_OFF 7 +#define HCI_READ_PAGE_TOUT_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_PAGE_TOUT_OFF] & HCI_SUPP_COMMANDS_READ_PAGE_TOUT_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_PAGE_TOUT_MASK 0x20 +#define HCI_SUPP_COMMANDS_WRITE_PAGE_TOUT_OFF 7 +#define HCI_WRITE_PAGE_TOUT_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_PAGE_TOUT_OFF] & HCI_SUPP_COMMANDS_WRITE_PAGE_TOUT_MASK) + +#define HCI_SUPP_COMMANDS_READ_SCAN_ENABLE_MASK 0x40 +#define HCI_SUPP_COMMANDS_READ_SCAN_ENABLE_OFF 7 +#define HCI_READ_SCAN_ENABLE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_SCAN_ENABLE_OFF] & HCI_SUPP_COMMANDS_READ_SCAN_ENABLE_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_SCAN_ENABLE_MASK 0x80 +#define HCI_SUPP_COMMANDS_WRITE_SCAN_ENABLE_OFF 7 +#define HCI_WRITE_SCAN_ENABLE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_SCAN_ENABLE_OFF] & HCI_SUPP_COMMANDS_WRITE_SCAN_ENABLE_MASK) + +#define HCI_SUPP_COMMANDS_READ_PAGE_SCAN_ACTIVITY_MASK 0x01 +#define HCI_SUPP_COMMANDS_READ_PAGE_SCAN_ACTIVITY_OFF 8 +#define HCI_READ_PAGE_SCAN_ACTIVITY_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_PAGE_SCAN_ACTIVITY_OFF] & HCI_SUPP_COMMANDS_READ_PAGE_SCAN_ACTIVITY_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_PAGE_SCAN_ACTIVITY_MASK 0x02 +#define HCI_SUPP_COMMANDS_WRITE_PAGE_SCAN_ACTIVITY_OFF 8 +#define HCI_WRITE_PAGE_SCAN_ACTIVITY_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_PAGE_SCAN_ACTIVITY_OFF] & HCI_SUPP_COMMANDS_WRITE_PAGE_SCAN_ACTIVITY_MASK) + +#define HCI_SUPP_COMMANDS_READ_INQURIY_SCAN_ACTIVITY_MASK 0x04 +#define HCI_SUPP_COMMANDS_READ_INQURIY_SCAN_ACTIVITY_OFF 8 +#define HCI_READ_INQURIY_SCAN_ACTIVITY_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_INQURIY_SCAN_ACTIVITY_OFF] & HCI_SUPP_COMMANDS_READ_INQURIY_SCAN_ACTIVITY_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_INQURIY_SCAN_ACTIVITY_MASK 0x08 +#define HCI_SUPP_COMMANDS_WRITE_INQURIY_SCAN_ACTIVITY_OFF 8 +#define HCI_WRITE_INQURIY_SCAN_ACTIVITY_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_INQURIY_SCAN_ACTIVITY_OFF] & HCI_SUPP_COMMANDS_WRITE_INQURIY_SCAN_ACTIVITY_MASK) + +#define HCI_SUPP_COMMANDS_READ_AUTH_ENABLE_MASK 0x10 +#define HCI_SUPP_COMMANDS_READ_AUTH_ENABLE_OFF 8 +#define HCI_READ_AUTH_ENABLE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_AUTH_ENABLE_OFF] & HCI_SUPP_COMMANDS_READ_AUTH_ENABLE_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_AUTH_ENABLE_MASK 0x20 +#define HCI_SUPP_COMMANDS_WRITE_AUTH_ENABLE_OFF 8 +#define HCI_WRITE_AUTH_ENABLE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_AUTH_ENABLE_OFF] & HCI_SUPP_COMMANDS_WRITE_AUTH_ENABLE_MASK) + +#define HCI_SUPP_COMMANDS_READ_ENCRYPT_ENABLE_MASK 0x40 +#define HCI_SUPP_COMMANDS_READ_ENCRYPT_ENABLE_OFF 8 +#define HCI_READ_ENCRYPT_ENABLE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_ENCRYPT_ENABLE_OFF] & HCI_SUPP_COMMANDS_READ_ENCRYPT_ENABLE_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_ENCRYPT_ENABLE_MASK 0x80 +#define HCI_SUPP_COMMANDS_WRITE_ENCRYPT_ENABLE_OFF 8 +#define HCI_WRITE_ENCRYPT_ENABLE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_ENCRYPT_ENABLE_OFF] & HCI_SUPP_COMMANDS_WRITE_ENCRYPT_ENABLE_MASK) + +#define HCI_SUPP_COMMANDS_READ_CLASS_DEVICE_MASK 0x01 +#define HCI_SUPP_COMMANDS_READ_CLASS_DEVICE_OFF 9 +#define HCI_READ_CLASS_DEVICE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_CLASS_DEVICE_OFF] & HCI_SUPP_COMMANDS_READ_CLASS_DEVICE_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_CLASS_DEVICE_MASK 0x02 +#define HCI_SUPP_COMMANDS_WRITE_CLASS_DEVICE_OFF 9 +#define HCI_WRITE_CLASS_DEVICE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_CLASS_DEVICE_OFF] & HCI_SUPP_COMMANDS_WRITE_CLASS_DEVICE_MASK) + +#define HCI_SUPP_COMMANDS_READ_VOICE_SETTING_MASK 0x04 +#define HCI_SUPP_COMMANDS_READ_VOICE_SETTING_OFF 9 +#define HCI_READ_VOICE_SETTING_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_VOICE_SETTING_OFF] & HCI_SUPP_COMMANDS_READ_VOICE_SETTING_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_VOICE_SETTING_MASK 0x08 +#define HCI_SUPP_COMMANDS_WRITE_VOICE_SETTING_OFF 9 +#define HCI_WRITE_VOICE_SETTING_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_VOICE_SETTING_OFF] & HCI_SUPP_COMMANDS_WRITE_VOICE_SETTING_MASK) + +#define HCI_SUPP_COMMANDS_READ_AUTO_FLUSH_TOUT_MASK 0x10 +#define HCI_SUPP_COMMANDS_READ_AUTO_FLUSH_TOUT_OFF 9 +#define HCI_READ_AUTO_FLUSH_TOUT_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_AUTO_FLUSH_TOUT_OFF] & HCI_SUPP_COMMANDS_READ_AUTO_FLUSH_TOUT_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_AUTO_FLUSH_TOUT_MASK 0x20 +#define HCI_SUPP_COMMANDS_WRITE_AUTO_FLUSH_TOUT_OFF 9 +#define HCI_WRITE_AUTO_FLUSH_TOUT_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_AUTO_FLUSH_TOUT_OFF] & HCI_SUPP_COMMANDS_WRITE_AUTO_FLUSH_TOUT_MASK) + +#define HCI_SUPP_COMMANDS_READ_NUM_BROAD_RETRANS_MASK 0x40 +#define HCI_SUPP_COMMANDS_READ_NUM_BROAD_RETRANS_OFF 9 +#define HCI_READ_NUM_BROAD_RETRANS_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_NUM_BROAD_RETRANS_OFF] & HCI_SUPP_COMMANDS_READ_NUM_BROAD_RETRANS_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_NUM_BROAD_RETRANS_MASK 0x80 +#define HCI_SUPP_COMMANDS_WRITE_NUM_BROAD_RETRANS_OFF 9 +#define HCI_WRITE_NUM_BROAD_RETRANS_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_NUM_BROAD_RETRANS_OFF] & HCI_SUPP_COMMANDS_WRITE_NUM_BROAD_RETRANS_MASK) + +#define HCI_SUPP_COMMANDS_READ_HOLD_MODE_ACTIVITY_MASK 0x01 +#define HCI_SUPP_COMMANDS_READ_HOLD_MODE_ACTIVITY_OFF 10 +#define HCI_READ_HOLD_MODE_ACTIVITY_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_HOLD_MODE_ACTIVITY_OFF] & HCI_SUPP_COMMANDS_READ_HOLD_MODE_ACTIVITY_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_HOLD_MODE_ACTIVITY_MASK 0x02 +#define HCI_SUPP_COMMANDS_WRITE_HOLD_MODE_ACTIVITY_OFF 10 +#define HCI_WRITE_HOLD_MODE_ACTIVITY_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_HOLD_MODE_ACTIVITY_OFF] & HCI_SUPP_COMMANDS_WRITE_HOLD_MODE_ACTIVITY_MASK) + +#define HCI_SUPP_COMMANDS_READ_TRANS_PWR_LEVEL_MASK 0x04 +#define HCI_SUPP_COMMANDS_READ_TRANS_PWR_LEVEL_OFF 10 +#define HCI_READ_TRANS_PWR_LEVEL_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_TRANS_PWR_LEVEL_OFF] & HCI_SUPP_COMMANDS_READ_TRANS_PWR_LEVEL_MASK) + +#define HCI_SUPP_COMMANDS_READ_SYNCH_FLOW_CTRL_ENABLE_MASK 0x08 +#define HCI_SUPP_COMMANDS_READ_SYNCH_FLOW_CTRL_ENABLE_OFF 10 +#define HCI_READ_SYNCH_FLOW_CTRL_ENABLE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_SYNCH_FLOW_CTRL_ENABLE_OFF] & HCI_SUPP_COMMANDS_READ_SYNCH_FLOW_CTRL_ENABLE_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_SYNCH_FLOW_CTRL_ENABLE_MASK 0x10 +#define HCI_SUPP_COMMANDS_WRITE_SYNCH_FLOW_CTRL_ENABLE_OFF 10 +#define HCI_WRITE_SYNCH_FLOW_CTRL_ENABLE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_SYNCH_FLOW_CTRL_ENABLE_OFF] & HCI_SUPP_COMMANDS_WRITE_SYNCH_FLOW_CTRL_ENABLE_MASK) + +#define HCI_SUPP_COMMANDS_SET_HOST_CTRLR_TO_HOST_FC_MASK 0x20 +#define HCI_SUPP_COMMANDS_SET_HOST_CTRLR_TO_HOST_FC_OFF 10 +#define HCI_SET_HOST_CTRLR_TO_HOST_FC_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_SET_HOST_CTRLR_TO_HOST_FC_OFF] & HCI_SUPP_COMMANDS_SET_HOST_CTRLR_TO_HOST_FC_MASK) + +#define HCI_SUPP_COMMANDS_HOST_BUFFER_SIZE_MASK 0x40 +#define HCI_SUPP_COMMANDS_HOST_BUFFER_SIZE_OFF 10 +#define HCI_HOST_BUFFER_SIZE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_HOST_BUFFER_SIZE_OFF] & HCI_SUPP_COMMANDS_HOST_BUFFER_SIZE_MASK) + +#define HCI_SUPP_COMMANDS_HOST_NUM_COMPLETED_PKTS_MASK 0x80 +#define HCI_SUPP_COMMANDS_HOST_NUM_COMPLETED_PKTS_OFF 10 +#define HCI_HOST_NUM_COMPLETED_PKTS_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_HOST_NUM_COMPLETED_PKTS_OFF] & HCI_SUPP_COMMANDS_HOST_NUM_COMPLETED_PKTS_MASK) + +#define HCI_SUPP_COMMANDS_READ_LINK_SUP_TOUT_MASK 0x01 +#define HCI_SUPP_COMMANDS_READ_LINK_SUP_TOUT_OFF 11 +#define HCI_READ_LINK_SUP_TOUT_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_LINK_SUP_TOUT_OFF] & HCI_SUPP_COMMANDS_READ_LINK_SUP_TOUT_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_LINK_SUP_TOUT_MASK 0x02 +#define HCI_SUPP_COMMANDS_WRITE_LINK_SUP_TOUT_OFF 11 +#define HCI_WRITE_LINK_SUP_TOUT_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_LINK_SUP_TOUT_OFF] & HCI_SUPP_COMMANDS_WRITE_LINK_SUP_TOUT_MASK) + +#define HCI_SUPP_COMMANDS_READ_NUM_SUPP_IAC_MASK 0x04 +#define HCI_SUPP_COMMANDS_READ_NUM_SUPP_IAC_OFF 11 +#define HCI_READ_NUM_SUPP_IAC_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_NUM_SUPP_IAC_OFF] & HCI_SUPP_COMMANDS_READ_NUM_SUPP_IAC_MASK) + +#define HCI_SUPP_COMMANDS_READ_CURRENT_IAC_LAP_MASK 0x08 +#define HCI_SUPP_COMMANDS_READ_CURRENT_IAC_LAP_OFF 11 +#define HCI_READ_CURRENT_IAC_LAP_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_CURRENT_IAC_LAP_OFF] & HCI_SUPP_COMMANDS_READ_CURRENT_IAC_LAP_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_CURRENT_IAC_LAP_MASK 0x10 +#define HCI_SUPP_COMMANDS_WRITE_CURRENT_IAC_LAP_OFF 11 +#define HCI_WRITE_CURRENT_IAC_LAP_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_CURRENT_IAC_LAP_OFF] & HCI_SUPP_COMMANDS_WRITE_CURRENT_IAC_LAP_MASK) + +#define HCI_SUPP_COMMANDS_READ_PAGE_SCAN_PER_MODE_MASK 0x20 +#define HCI_SUPP_COMMANDS_READ_PAGE_SCAN_PER_MODE_OFF 11 +#define HCI_READ_PAGE_SCAN_PER_MODE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_PAGE_SCAN_PER_MODE_OFF] & HCI_SUPP_COMMANDS_READ_PAGE_SCAN_PER_MODE_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_PAGE_SCAN_PER_MODE_MASK 0x40 +#define HCI_SUPP_COMMANDS_WRITE_PAGE_SCAN_PER_MODE_OFF 11 +#define HCI_WRITE_PAGE_SCAN_PER_MODE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_PAGE_SCAN_PER_MODE_OFF] & HCI_SUPP_COMMANDS_WRITE_PAGE_SCAN_PER_MODE_MASK) + +#define HCI_SUPP_COMMANDS_READ_PAGE_SCAN_MODE_MASK 0x80 +#define HCI_SUPP_COMMANDS_READ_PAGE_SCAN_MODE_OFF 11 +#define HCI_READ_PAGE_SCAN_MODE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_PAGE_SCAN_MODE_OFF] & HCI_SUPP_COMMANDS_READ_PAGE_SCAN_MODE_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_PAGE_SCAN_MODE_MASK 0x01 +#define HCI_SUPP_COMMANDS_WRITE_PAGE_SCAN_MODE_OFF 12 +#define HCI_WRITE_PAGE_SCAN_MODE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_PAGE_SCAN_MODE_OFF] & HCI_SUPP_COMMANDS_WRITE_PAGE_SCAN_MODE_MASK) + +#define HCI_SUPP_COMMANDS_SET_AFH_CHNL_CLASS_MASK 0x02 +#define HCI_SUPP_COMMANDS_SET_AFH_CHNL_CLASS_OFF 12 +#define HCI_SET_AFH_CHNL_CLASS_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_SET_AFH_CHNL_CLASS_OFF] & HCI_SUPP_COMMANDS_SET_AFH_CHNL_CLASS_MASK) + +#define HCI_SUPP_COMMANDS_READ_INQUIRY_SCAN_TYPE_MASK 0x10 +#define HCI_SUPP_COMMANDS_READ_INQUIRY_SCAN_TYPE_OFF 12 +#define HCI_READ_INQUIRY_SCAN_TYPE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_INQUIRY_SCAN_TYPE_OFF] & HCI_SUPP_COMMANDS_READ_INQUIRY_SCAN_TYPE_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_INQUIRY_SCAN_TYPE_MASK 0x20 +#define HCI_SUPP_COMMANDS_WRITE_INQUIRY_SCAN_TYPE_OFF 12 +#define HCI_WRITE_INQUIRY_SCAN_TYPE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_INQUIRY_SCAN_TYPE_OFF] & HCI_SUPP_COMMANDS_WRITE_INQUIRY_SCAN_TYPE_MASK) + +#define HCI_SUPP_COMMANDS_READ_INQUIRY_MODE_MASK 0x40 +#define HCI_SUPP_COMMANDS_READ_INQUIRY_MODE_OFF 12 +#define HCI_READ_INQUIRY_MODE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_INQUIRY_MODE_OFF] & HCI_SUPP_COMMANDS_READ_INQUIRY_MODE_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_INQUIRY_MODE_MASK 0x80 +#define HCI_SUPP_COMMANDS_WRITE_INQUIRY_MODE_OFF 12 +#define HCI_WRITE_INQUIRY_MODE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_INQUIRY_MODE_OFF] & HCI_SUPP_COMMANDS_WRITE_INQUIRY_MODE_MASK) + +#define HCI_SUPP_COMMANDS_READ_PAGE_SCAN_TYPE_MASK 0x01 +#define HCI_SUPP_COMMANDS_READ_PAGE_SCAN_TYPE_OFF 13 +#define HCI_READ_PAGE_SCAN_TYPE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_PAGE_SCAN_TYPE_OFF] & HCI_SUPP_COMMANDS_READ_PAGE_SCAN_TYPE_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_PAGE_SCAN_TYPE_MASK 0x02 +#define HCI_SUPP_COMMANDS_WRITE_PAGE_SCAN_TYPE_OFF 13 +#define HCI_WRITE_PAGE_SCAN_TYPE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_PAGE_SCAN_TYPE_OFF] & HCI_SUPP_COMMANDS_WRITE_PAGE_SCAN_TYPE_MASK) + +#define HCI_SUPP_COMMANDS_READ_AFH_CHNL_ASSESS_MODE_MASK 0x04 +#define HCI_SUPP_COMMANDS_READ_AFH_CHNL_ASSESS_MODE_OFF 13 +#define HCI_READ_AFH_CHNL_ASSESS_MODE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_AFH_CHNL_ASSESS_MODE_OFF] & HCI_SUPP_COMMANDS_READ_AFH_CHNL_ASSESS_MODE_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_AFH_CHNL_ASSESS_MODE_MASK 0x08 +#define HCI_SUPP_COMMANDS_WRITE_AFH_CHNL_ASSESS_MODE_OFF 13 +#define HCI_WRITE_AFH_CHNL_ASSESS_MODE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_AFH_CHNL_ASSESS_MODE_OFF] & HCI_SUPP_COMMANDS_WRITE_AFH_CHNL_ASSESS_MODE_MASK) + +#define HCI_SUPP_COMMANDS_READ_LOCAL_VER_INFO_MASK 0x08 +#define HCI_SUPP_COMMANDS_READ_LOCAL_VER_INFO_OFF 14 +#define HCI_READ_LOCAL_VER_INFO_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_LOCAL_VER_INFO_OFF] & HCI_SUPP_COMMANDS_READ_LOCAL_VER_INFO_MASK) + +#define HCI_SUPP_COMMANDS_READ_LOCAL_SUP_CMDS_MASK 0x10 +#define HCI_SUPP_COMMANDS_READ_LOCAL_SUP_CMDS_OFF 14 +#define HCI_READ_LOCAL_SUP_CMDS_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_LOCAL_SUP_CMDS_OFF] & HCI_SUPP_COMMANDS_READ_LOCAL_SUP_CMDS_MASK) + +#define HCI_SUPP_COMMANDS_READ_LOCAL_SUPP_FEATURES_MASK 0x20 +#define HCI_SUPP_COMMANDS_READ_LOCAL_SUPP_FEATURES_OFF 14 +#define HCI_READ_LOCAL_SUPP_FEATURES_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_LOCAL_SUPP_FEATURES_OFF] & HCI_SUPP_COMMANDS_READ_LOCAL_SUPP_FEATURES_MASK) + +#define HCI_SUPP_COMMANDS_READ_LOCAL_EXT_FEATURES_MASK 0x40 +#define HCI_SUPP_COMMANDS_READ_LOCAL_EXT_FEATURES_OFF 14 +#define HCI_READ_LOCAL_EXT_FEATURES_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_LOCAL_EXT_FEATURES_OFF] & HCI_SUPP_COMMANDS_READ_LOCAL_EXT_FEATURES_MASK) + +#define HCI_SUPP_COMMANDS_READ_BUFFER_SIZE_MASK 0x80 +#define HCI_SUPP_COMMANDS_READ_BUFFER_SIZE_OFF 14 +#define HCI_READ_BUFFER_SIZE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_BUFFER_SIZE_OFF] & HCI_SUPP_COMMANDS_READ_BUFFER_SIZE_MASK) + +#define HCI_SUPP_COMMANDS_READ_COUNTRY_CODE_MASK 0x01 +#define HCI_SUPP_COMMANDS_READ_COUNTRY_CODE_OFF 15 +#define HCI_READ_COUNTRY_CODE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_COUNTRY_CODE_OFF] & HCI_SUPP_COMMANDS_READ_COUNTRY_CODE_MASK) + +#define HCI_SUPP_COMMANDS_READ_BD_ADDR_MASK 0x02 +#define HCI_SUPP_COMMANDS_READ_BD_ADDR_OFF 15 +#define HCI_READ_BD_ADDR_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_BD_ADDR_OFF] & HCI_SUPP_COMMANDS_READ_BD_ADDR_MASK) + +#define HCI_SUPP_COMMANDS_READ_FAIL_CONTACT_CNTR_MASK 0x04 +#define HCI_SUPP_COMMANDS_READ_FAIL_CONTACT_CNTR_OFF 15 +#define HCI_READ_FAIL_CONTACT_CNTR_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_FAIL_CONTACT_CNTR_OFF] & HCI_SUPP_COMMANDS_READ_FAIL_CONTACT_CNTR_MASK) + +#define HCI_SUPP_COMMANDS_RESET_FAIL_CONTACT_CNTR_MASK 0x08 +#define HCI_SUPP_COMMANDS_RESET_FAIL_CONTACT_CNTR_OFF 15 +#define HCI_RESET_FAIL_CONTACT_CNTR_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_RESET_FAIL_CONTACT_CNTR_OFF] & HCI_SUPP_COMMANDS_RESET_FAIL_CONTACT_CNTR_MASK) + +#define HCI_SUPP_COMMANDS_GET_LINK_QUALITY_MASK 0x10 +#define HCI_SUPP_COMMANDS_GET_LINK_QUALITY_OFF 15 +#define HCI_GET_LINK_QUALITY_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_GET_LINK_QUALITY_OFF] & HCI_SUPP_COMMANDS_GET_LINK_QUALITY_MASK) + +#define HCI_SUPP_COMMANDS_READ_RSSI_MASK 0x20 +#define HCI_SUPP_COMMANDS_READ_RSSI_OFF 15 +#define HCI_READ_RSSI_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_RSSI_OFF] & HCI_SUPP_COMMANDS_READ_RSSI_MASK) + +#define HCI_SUPP_COMMANDS_READ_AFH_CH_MAP_MASK 0x40 +#define HCI_SUPP_COMMANDS_READ_AFH_CH_MAP_OFF 15 +#define HCI_READ_AFH_CH_MAP_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_AFH_CH_MAP_OFF] & HCI_SUPP_COMMANDS_READ_AFH_CH_MAP_MASK) + +#define HCI_SUPP_COMMANDS_READ_BD_CLOCK_MASK 0x80 +#define HCI_SUPP_COMMANDS_READ_BD_CLOCK_OFF 15 +#define HCI_READ_BD_CLOCK_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_BD_CLOCK_OFF] & HCI_SUPP_COMMANDS_READ_BD_CLOCK_MASK) + +#define HCI_SUPP_COMMANDS_READ_LOOPBACK_MODE_MASK 0x01 +#define HCI_SUPP_COMMANDS_READ_LOOPBACK_MODE_OFF 16 +#define HCI_READ_LOOPBACK_MODE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_LOOPBACK_MODE_OFF] & HCI_SUPP_COMMANDS_READ_LOOPBACK_MODE_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_LOOPBACK_MODE_MASK 0x02 +#define HCI_SUPP_COMMANDS_WRITE_LOOPBACK_MODE_OFF 16 +#define HCI_WRITE_LOOPBACK_MODE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_LOOPBACK_MODE_OFF] & HCI_SUPP_COMMANDS_WRITE_LOOPBACK_MODE_MASK) + +#define HCI_SUPP_COMMANDS_ENABLE_DEV_UNDER_TEST_MASK 0x04 +#define HCI_SUPP_COMMANDS_ENABLE_DEV_UNDER_TEST_OFF 16 +#define HCI_ENABLE_DEV_UNDER_TEST_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_ENABLE_DEV_UNDER_TEST_OFF] & HCI_SUPP_COMMANDS_ENABLE_DEV_UNDER_TEST_MASK) + +#define HCI_SUPP_COMMANDS_SETUP_SYNCH_CONN_MASK 0x08 +#define HCI_SUPP_COMMANDS_SETUP_SYNCH_CONN_OFF 16 +#define HCI_SETUP_SYNCH_CONN_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_SETUP_SYNCH_CONN_OFF] & HCI_SUPP_COMMANDS_SETUP_SYNCH_CONN_MASK) + +#define HCI_SUPP_COMMANDS_ACCEPT_SYNCH_CONN_MASK 0x10 +#define HCI_SUPP_COMMANDS_ACCEPT_SYNCH_CONN_OFF 16 +#define HCI_ACCEPT_SYNCH_CONN_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_ACCEPT_SYNCH_CONN_OFF] & HCI_SUPP_COMMANDS_ACCEPT_SYNCH_CONN_MASK) + +#define HCI_SUPP_COMMANDS_REJECT_SYNCH_CONN_MASK 0x20 +#define HCI_SUPP_COMMANDS_REJECT_SYNCH_CONN_OFF 16 +#define HCI_REJECT_SYNCH_CONN_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_REJECT_SYNCH_CONN_OFF] & HCI_SUPP_COMMANDS_REJECT_SYNCH_CONN_MASK) + +#define HCI_SUPP_COMMANDS_READ_EXT_INQUIRY_RESP_MASK 0x01 +#define HCI_SUPP_COMMANDS_READ_EXT_INQUIRY_RESP_OFF 17 +#define HCI_READ_EXT_INQUIRY_RESP_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_EXT_INQUIRY_RESP_OFF] & HCI_SUPP_COMMANDS_READ_EXT_INQUIRY_RESP_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_EXT_INQUIRY_RESP_MASK 0x02 +#define HCI_SUPP_COMMANDS_WRITE_EXT_INQUIRY_RESP_OFF 17 +#define HCI_WRITE_EXT_INQUIRY_RESP_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_EXT_INQUIRY_RESP_OFF] & HCI_SUPP_COMMANDS_WRITE_EXT_INQUIRY_RESP_MASK) + +#define HCI_SUPP_COMMANDS_REFRESH_ENCRYPTION_KEY_MASK 0x04 +#define HCI_SUPP_COMMANDS_REFRESH_ENCRYPTION_KEY_OFF 17 +#define HCI_REFRESH_ENCRYPTION_KEY_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_REFRESH_ENCRYPTION_KEY_OFF] & HCI_SUPP_COMMANDS_REFRESH_ENCRYPTION_KEY_MASK) + +/* Octet 17, bit 3 is reserved */ + +#define HCI_SUPP_COMMANDS_SNIFF_SUB_RATE_MASK 0x10 +#define HCI_SUPP_COMMANDS_SNIFF_SUB_RATE_OFF 17 +#define HCI_SNIFF_SUB_RATE_CMD_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_SNIFF_SUB_RATE_OFF] & HCI_SUPP_COMMANDS_SNIFF_SUB_RATE_MASK) + +#define HCI_SUPP_COMMANDS_READ_SIMPLE_PAIRING_MODE_MASK 0x20 +#define HCI_SUPP_COMMANDS_READ_SIMPLE_PAIRING_MODE_OFF 17 +#define HCI_READ_SIMPLE_PAIRING_MODE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_SIMPLE_PAIRING_MODE_OFF] & HCI_SUPP_COMMANDS_READ_SIMPLE_PAIRING_MODE_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_SIMPLE_PAIRING_MODE_MASK 0x40 +#define HCI_SUPP_COMMANDS_WRITE_SIMPLE_PAIRING_MODE_OFF 17 +#define HCI_WRITE_SIMPLE_PAIRING_MODE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_SIMPLE_PAIRING_MODE_OFF] & HCI_SUPP_COMMANDS_WRITE_SIMPLE_PAIRING_MODE_MASK) + +#define HCI_SUPP_COMMANDS_READ_LOCAL_OOB_DATA_MASK 0x80 +#define HCI_SUPP_COMMANDS_READ_LOCAL_OOB_DATA_OFF 17 +#define HCI_READ_LOCAL_OOB_DATA_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_LOCAL_OOB_DATA_OFF] & HCI_SUPP_COMMANDS_READ_LOCAL_OOB_DATA_MASK) + +#define HCI_SUPP_COMMANDS_READ_INQUIRY_RESPONSE_TX_POWER_MASK 0x01 +#define HCI_SUPP_COMMANDS_READ_INQUIRY_RESPONSE_TX_POWER_OFF 18 +#define HCI_READ_INQUIRY_RESPONSE_TX_POWER_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_INQUIRY_RESPONSE_TX_POWER_OFF] & HCI_SUPP_COMMANDS_READ_INQUIRY_RESPONSE_TX_POWER_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_INQUIRY_RESPONSE_TX_POWER_MASK 0x02 +#define HCI_SUPP_COMMANDS_WRITE_INQUIRY_RESPONSE_TX_POWER_OFF 18 +#define HCI_WRITE_INQUIRY_RESPONSE_TX_POWER_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_INQUIRY_RESPONSE_TX_POWER_OFF] & HCI_SUPP_COMMANDS_WRITE_INQUIRY_RESPONSE_TX_POWER_MASK) + +#define HCI_SUPP_COMMANDS_READ_DEFAULT_ERRONEOUS_DATA_REPORTING_MASK 0x04 +#define HCI_SUPP_COMMANDS_READ_DEFAULT_ERRONEOUS_DATA_REPORTING_OFF 18 +#define HCI_READ_DEFAULT_ERRONEOUS_DATA_REPORTING_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_DEFAULT_ERRONEOUS_DATA_REPORTING_OFF] & HCI_SUPP_COMMANDS_READ_DEFAULT_ERRONEOUS_DATA_REPORTING_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_DEFAULT_ERRONEOUS_DATA_REPORTING_MASK 0x08 +#define HCI_SUPP_COMMANDS_WRITE_DEFAULT_ERRONEOUS_DATA_REPORTING_OFF 18 +#define HCI_WRITE_DEFAULT_ERRONEOUS_DATA_REPORTING_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_DEFAULT_ERRONEOUS_DATA_REPORTING_OFF] & HCI_SUPP_COMMANDS_WRITE_DEFAULT_ERRONEOUS_DATA_REPORTING_MASK) + +#define HCI_SUPP_COMMANDS_IO_CAPABILITY_REQUEST_REPLY_MASK 0x80 +#define HCI_SUPP_COMMANDS_IO_CAPABILITY_REQUEST_REPLY_OFF 18 +#define HCI_IO_CAPABILITY_REQUEST_REPLY_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_IO_CAPABILITY_REQUEST_REPLY_OFF] & HCI_SUPP_COMMANDS_IO_CAPABILITY_REQUEST_REPLY_MASK) + +#define HCI_SUPP_COMMANDS_USER_CONFIRMATION_REQUEST_REPLY_MASK 0x01 +#define HCI_SUPP_COMMANDS_USER_CONFIRMATION_REQUEST_REPLY_OFF 19 +#define HCI_USER_CONFIRMATION_REQUEST_REPLY_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_USER_CONFIRMATION_REQUEST_REPLY_OFF] & HCI_SUPP_COMMANDS_USER_CONFIRMATION_REQUEST_REPLY_MASK) + +#define HCI_SUPP_COMMANDS_USER_CONFIRMATION_REQUEST_NEG_REPLY_MASK 0x02 +#define HCI_SUPP_COMMANDS_USER_CONFIRMATION_REQUEST_NEG_REPLY_OFF 19 +#define HCI_USER_CONFIRMATION_REQUEST_NEG_REPLY_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_USER_CONFIRMATION_REQUEST_NEG_REPLY_OFF] & HCI_SUPP_COMMANDS_USER_CONFIRMATION_REQUEST_NEG_REPLY_MASK) + +#define HCI_SUPP_COMMANDS_USER_PASSKEY_REQUEST_REPLY_MASK 0x04 +#define HCI_SUPP_COMMANDS_USER_PASSKEY_REQUEST_REPLY_OFF 19 +#define HCI_USER_PASSKEY_REQUEST_REPLY_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_USER_PASSKEY_REQUEST_REPLY_OFF] & HCI_SUPP_COMMANDS_USER_PASSKEY_REQUEST_REPLY_MASK) + +#define HCI_SUPP_COMMANDS_USER_PASSKEY_REQUEST_NEG_REPLY_MASK 0x08 +#define HCI_SUPP_COMMANDS_USER_PASSKEY_REQUEST_NEG_REPLY_OFF 19 +#define HCI_USER_PASSKEY_REQUEST_NEG_REPLY_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_USER_PASSKEY_REQUEST_NEG_REPLY_OFF] & HCI_SUPP_COMMANDS_USER_PASSKEY_REQUEST_NEG_REPLY_MASK) + +#define HCI_SUPP_COMMANDS_REMOTE_OOB_DATA_REQUEST_REPLY_MASK 0x10 +#define HCI_SUPP_COMMANDS_REMOTE_OOB_DATA_REQUEST_REPLY_OFF 19 +#define HCI_REMOTE_OOB_DATA_REQUEST_REPLY_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_REMOTE_OOB_DATA_REQUEST_REPLY_OFF] & HCI_SUPP_COMMANDS_REMOTE_OOB_DATA_REQUEST_REPLY_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_SIMPLE_PAIRING_DBG_MODE_MASK 0x20 +#define HCI_SUPP_COMMANDS_WRITE_SIMPLE_PAIRING_DBG_MODE_OFF 19 +#define HCI_WRITE_SIMPLE_PAIRING_DBG_MODE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_SIMPLE_PAIRING_DBG_MODE_OFF] & HCI_SUPP_COMMANDS_WRITE_SIMPLE_PAIRING_DBG_MODE_MASK) + +#define HCI_SUPP_COMMANDS_ENHANCED_FLUSH_MASK 0x40 +#define HCI_SUPP_COMMANDS_ENHANCED_FLUSH_OFF 19 +#define HCI_ENHANCED_FLUSH_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_ENHANCED_FLUSH_OFF] & HCI_SUPP_COMMANDS_ENHANCED_FLUSH_MASK) + +#define HCI_SUPP_COMMANDS_REMOTE_OOB_DATA_REQUEST_NEG_REPLY_MASK 0x80 +#define HCI_SUPP_COMMANDS_REMOTE_OOB_DATA_REQUEST_NEG_REPLY_OFF 19 +#define HCI_REMOTE_OOB_DATA_REQUEST_NEG_REPLY_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_REMOTE_OOB_DATA_REQUEST_NEG_REPLY_OFF] & HCI_SUPP_COMMANDS_REMOTE_OOB_DATA_REQUEST_NEG_REPLY_MASK) + +/* Supported Commands (Byte 20) */ +#define HCI_SUPP_COMMANDS_SEND_KEYPRESS_NOTIF_MASK 0x04 +#define HCI_SUPP_COMMANDS_SEND_KEYPRESS_NOTIF_OFF 20 +#define HCI_SEND_NOTIF_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_SEND_KEYPRESS_NOTIF_OFF] & HCI_SUPP_COMMANDS_SEND_KEYPRESS_NOTIF_MASK) + +#define HCI_SUPP_COMMANDS_IO_CAP_REQ_NEG_REPLY_MASK 0x08 +#define HCI_SUPP_COMMANDS_IO_CAP_REQ_NEG_REPLY_OFF 20 +#define HCI_IO_CAP_REQ_NEG_REPLY_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_IO_CAP_REQ_NEG_REPLY_OFF] & HCI_SUPP_COMMANDS_IO_CAP_REQ_NEG_REPLY_MASK) + +#define HCI_SUPP_COMMANDS_READ_ENCR_KEY_SIZE_MASK 0x10 +#define HCI_SUPP_COMMANDS_READ_ENCR_KEY_SIZE_OFF 20 +#define HCI_READ_ENCR_KEY_SIZE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_ENCR_KEY_SIZE_OFF] & HCI_SUPP_COMMANDS_READ_ENCR_KEY_SIZE_MASK) + +/* Supported Commands (Byte 21) */ +#define HCI_SUPP_COMMANDS_CREATE_PHYSICAL_LINK_MASK 0x01 +#define HCI_SUPP_COMMANDS_CREATE_PHYSICAL_LINK_OFF 21 +#define HCI_CREATE_PHYSICAL_LINK_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_CREATE_PHYSICAL_LINK_OFF] & HCI_SUPP_COMMANDS_CREATE_PHYSICAL_LINK_MASK) + +#define HCI_SUPP_COMMANDS_ACCEPT_PHYSICAL_LINK_MASK 0x02 +#define HCI_SUPP_COMMANDS_ACCEPT_PHYSICAL_LINK_OFF 21 +#define HCI_ACCEPT_PHYSICAL_LINK_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_ACCEPT_PHYSICAL_LINK_OFF] & HCI_SUPP_COMMANDS_ACCEPT_PHYSICAL_LINK_MASK) + +#define HCI_SUPP_COMMANDS_DISCONNECT_PHYSICAL_LINK_MASK 0x04 +#define HCI_SUPP_COMMANDS_DISCONNECT_PHYSICAL_LINK_OFF 21 +#define HCI_DISCONNECT_PHYSICAL_LINK_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_DISCONNECT_PHYSICAL_LINK_OFF] & HCI_SUPP_COMMANDS_DISCONNECT_PHYSICAL_LINK_MASK) + +#define HCI_SUPP_COMMANDS_CREATE_LOGICAL_LINK_MASK 0x08 +#define HCI_SUPP_COMMANDS_CREATE_LOGICAL_LINK_OFF 21 +#define HCI_CREATE_LOGICAL_LINK_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_CREATE_LOGICAL_LINK_OFF] & HCI_SUPP_COMMANDS_CREATE_LOGICAL_LINK_MASK) + +#define HCI_SUPP_COMMANDS_ACCEPT_LOGICAL_LINK_MASK 0x10 +#define HCI_SUPP_COMMANDS_ACCEPT_LOGICAL_LINK_OFF 21 +#define HCI_ACCEPT_LOGICAL_LINK_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_ACCEPT_LOGICAL_LINK_OFF] & HCI_SUPP_COMMANDS_ACCEPT_LOGICAL_LINK_MASK) + +#define HCI_SUPP_COMMANDS_DISCONNECT_LOGICAL_LINK_MASK 0x20 +#define HCI_SUPP_COMMANDS_DISCONNECT_LOGICAL_LINK_OFF 21 +#define HCI_DISCONNECT_LOGICAL_LINK_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_DISCONNECT_LOGICAL_LINK_OFF] & HCI_SUPP_COMMANDS_DISCONNECT_LOGICAL_LINK_MASK) + +#define HCI_SUPP_COMMANDS_LOGICAL_LINK_CANCEL_MASK 0x40 +#define HCI_SUPP_COMMANDS_LOGICAL_LINK_CANCEL_OFF 21 +#define HCI_LOGICAL_LINK_CANCEL_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_LOGICAL_LINK_CANCEL_OFF] & HCI_SUPP_COMMANDS_LOGICAL_LINK_CANCEL_MASK) + +#define HCI_SUPP_COMMANDS_FLOW_SPEC_MODIFY_MASK 0x80 +#define HCI_SUPP_COMMANDS_FLOW_SPEC_MODIFY_OFF 21 +#define HCI_FLOW_SPEC_MODIFY_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_FLOW_SPEC_MODIFY_OFF] & HCI_SUPP_COMMANDS_FLOW_SPEC_MODIFY_MASK) + +/* Supported Commands (Byte 22) */ +#define HCI_SUPP_COMMANDS_READ_LOGICAL_LINK_ACCEPT_TIMEOUT_MASK 0x01 +#define HCI_SUPP_COMMANDS_READ_LOGICAL_LINK_ACCEPT_TIMEOUT_OFF 22 +#define HCI_READ_LOGICAL_LINK_ACCEPT_TIMEOUT_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_LOGICAL_LINK_ACCEPT_TIMEOUT_OFF] & HCI_SUPP_COMMANDS_READ_LOGICAL_LINK_ACCEPT_TIMEOUT_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_LOGICAL_LINK_ACCEPT_TIMEOUT_MASK 0x02 +#define HCI_SUPP_COMMANDS_WRITE_LOGICAL_LINK_ACCEPT_TIMEOUT_OFF 22 +#define HCI_WRITE_LOGICAL_LINK_ACCEPT_TIMEOUT_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_LOGICAL_LINK_ACCEPT_TIMEOUT_OFF] & HCI_SUPP_COMMANDS_WRITE_LOGICAL_LINK_ACCEPT_TIMEOUT_MASK) + +#define HCI_SUPP_COMMANDS_SET_EVENT_MASK_PAGE_2_MASK 0x04 +#define HCI_SUPP_COMMANDS_SET_EVENT_MASK_PAGE_2_OFF 22 +#define HCI_SET_EVENT_MASK_PAGE_2_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_SET_EVENT_MASK_PAGE_2_OFF] & HCI_SUPP_COMMANDS_SET_EVENT_MASK_PAGE_2_MASK) + +#define HCI_SUPP_COMMANDS_READ_LOCATION_DATA_MASK 0x08 +#define HCI_SUPP_COMMANDS_READ_LOCATION_DATA_OFF 22 +#define HCI_READ_LOCATION_DATA_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_LOCATION_DATA_OFF] & HCI_SUPP_COMMANDS_READ_LOCATION_DATA_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_LOCATION_DATA_MASK 0x10 +#define HCI_SUPP_COMMANDS_WRITE_LOCATION_DATA_OFF 22 +#define HCI_WRITE_LOCATION_DATA_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_LOCATION_DATA_OFF] & HCI_SUPP_COMMANDS_WRITE_LOCATION_DATA_MASK) + +#define HCI_SUPP_COMMANDS_READ_LOCAL_AMP_INFO_MASK 0x20 +#define HCI_SUPP_COMMANDS_READ_LOCAL_AMP_INFO_OFF 22 +#define HCI_READ_LOCAL_AMP_INFO_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_LOCAL_AMP_INFO_OFF] & HCI_SUPP_COMMANDS_READ_LOCAL_AMP_INFO_MASK) + +#define HCI_SUPP_COMMANDS_READ_LOCAL_AMP_ASSOC_MASK 0x40 +#define HCI_SUPP_COMMANDS_READ_LOCAL_AMP_ASSOC_OFF 22 +#define HCI_READ_LOCAL_AMP_ASSOC_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_LOCAL_AMP_ASSOC_OFF] & HCI_SUPP_COMMANDS_READ_LOCAL_AMP_ASSOC_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_REMOTE_AMP_ASSOC_MASK 0x80 +#define HCI_SUPP_COMMANDS_WRITE_REMOTE_AMP_ASSOC_OFF 22 +#define HCI_WRITE_REMOTE_AMP_ASSOC_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_REMOTE_AMP_ASSOC_OFF] & HCI_SUPP_COMMANDS_WRITE_REMOTE_AMP_ASSOC_MASK) + +/* Supported Commands (Byte 23) */ +#define HCI_SUPP_COMMANDS_READ_FLOW_CONTROL_MODE_MASK 0x01 +#define HCI_SUPP_COMMANDS_READ_FLOW_CONTROL_MODE_OFF 23 +#define HCI_READ_FLOW_CONTROL_MODE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_FLOW_CONTROL_MODE_OFF] & HCI_SUPP_COMMANDS_READ_FLOW_CONTROL_MODE_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_FLOW_CONTROL_MODE_MASK 0x02 +#define HCI_SUPP_COMMANDS_WRITE_FLOW_CONTROL_MODE_OFF 23 +#define HCI_WRITE_FLOW_CONTROL_MODE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_FLOW_CONTROL_MODE_OFF] & HCI_SUPP_COMMANDS_WRITE_FLOW_CONTROL_MODE_MASK) + +#define HCI_SUPP_COMMANDS_READ_DATA_BLOCK_SIZE_MASK 0x04 +#define HCI_SUPP_COMMANDS_READ_DATA_BLOCK_SIZE_OFF 23 +#define HCI_READ_DATA_BLOCK_SIZE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_DATA_BLOCK_SIZE_OFF] & HCI_SUPP_COMMANDS_READ_DATA_BLOCK_SIZE_MASK) + +#define HCI_SUPP_COMMANDS_ENABLE_AMP_RCVR_REPORTS_MASK 0x20 +#define HCI_SUPP_COMMANDS_ENABLE_AMP_RCVR_REPORTS_OFF 23 +#define HCI_ENABLE_AMP_RCVR_REPORTS_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_ENABLE_AMP_RCVR_REPORTS_OFF] & HCI_SUPP_COMMANDS_ENABLE_AMP_RCVR_REPORTS_MASK) + +#define HCI_SUPP_COMMANDS_AMP_TEST_END_MASK 0x40 +#define HCI_SUPP_COMMANDS_AMP_TEST_END_OFF 23 +#define HCI_AMP_TEST_END_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_AMP_TEST_END_OFF] & HCI_SUPP_COMMANDS_AMP_TEST_END_MASK) + +#define HCI_SUPP_COMMANDS_AMP_TEST_MASK 0x80 +#define HCI_SUPP_COMMANDS_AMP_TEST_OFF 23 +#define HCI_AMP_TEST_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_AMP_TEST_OFF] & HCI_SUPP_COMMANDS_AMP_TEST_MASK) + +/* Supported Commands (Byte 24) */ +#define HCI_SUPP_COMMANDS_READ_TRANSMIT_POWER_LEVEL_MASK 0x01 +#define HCI_SUPP_COMMANDS_READ_TRANSMIT_POWER_LEVEL_OFF 24 +#define HCI_READ_TRANSMIT_POWER_LEVEL_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_TRANSMIT_POWER_LEVEL_OFF] & HCI_SUPP_COMMANDS_READ_TRANSMIT_POWER_LEVEL_MASK) + +#define HCI_SUPP_COMMANDS_READ_BE_FLUSH_TOUT_MASK 0x04 +#define HCI_SUPP_COMMANDS_READ_BE_FLUSH_TOUT_OFF 24 +#define HCI_READ_BE_FLUSH_TOUT_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_BE_FLUSH_TOUT_OFF] & HCI_SUPP_COMMANDS_READ_BE_FLUSH_TOUT_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_BE_FLUSH_TOUT_MASK 0x08 +#define HCI_SUPP_COMMANDS_WRITE_BE_FLUSH_TOUT_OFF 24 +#define HCI_WRITE_BE_FLUSH_TOUT_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_BE_FLUSH_TOUT_OFF] & HCI_SUPP_COMMANDS_WRITE_BE_FLUSH_TOUT_MASK) + +#define HCI_SUPP_COMMANDS_SHORT_RANGE_MODE_MASK 0x10 +#define HCI_SUPP_COMMANDS_SHORT_RANGE_MODE_OFF 24 +#define HCI_SHORT_RANGE_MODE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_SHORT_RANGE_MODE_OFF] & HCI_SUPP_COMMANDS_SHORT_RANGE_MODE_MASK) + +/* LE commands TBD +** Supported Commands (Byte 24 continued) +** Supported Commands (Byte 25) +** Supported Commands (Byte 26) +** Supported Commands (Byte 27) +** Supported Commands (Byte 28) +*/ + +/* Supported Commands (Byte 29) */ +#define HCI_SUPP_COMMANDS_ENH_SETUP_SYNCH_CONN_MASK 0x08 +#define HCI_SUPP_COMMANDS_ENH_SETUP_SYNCH_CONN_OFF 29 +#define HCI_READ_ENH_SETUP_SYNCH_CONN_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_ENH_SETUP_SYNCH_CONN_OFF] & HCI_SUPP_COMMANDS_ENH_SETUP_SYNCH_CONN_MASK) + +#define HCI_SUPP_COMMANDS_ENH_ACCEPT_SYNCH_CONN_MASK 0x10 +#define HCI_SUPP_COMMANDS_ENH_ACCEPT_SYNCH_CONN_OFF 29 +#define HCI_READ_ENH_ACCEPT_SYNCH_CONN_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_ENH_ACCEPT_SYNCH_CONN_OFF] & HCI_SUPP_COMMANDS_ENH_ACCEPT_SYNCH_CONN_MASK) + +#define HCI_SUPP_COMMANDS_READ_LOCAL_CODECS_MASK 0x20 +#define HCI_SUPP_COMMANDS_READ_LOCAL_CODECS_OFF 29 +#define HCI_READ_LOCAL_CODECS_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_LOCAL_CODECS_OFF] & HCI_SUPP_COMMANDS_READ_LOCAL_CODECS_MASK) + +#define HCI_SUPP_COMMANDS_SET_MWS_CHANN_PARAM_MASK 0x40 +#define HCI_SUPP_COMMANDS_SET_MWS_CHANN_PARAM_OFF 29 +#define HCI_SET_MWS_CHANNEL_PARAMETERS_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_SET_MWS_CHANN_PARAM_OFF] & HCI_SUPP_COMMANDS_SET_MWS_CHANN_PARAM_MASK) + +#define HCI_SUPP_COMMANDS_SET_EXT_FRAME_CONF_MASK 0x80 +#define HCI_SUPP_COMMANDS_SET_EXT_FRAME_CONF_OFF 29 +#define HCI_SET_EXTERNAL_FRAME_CONFIGURATION_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_SET_EXT_FRAME_CONF_OFF] & HCI_SUPP_COMMANDS_SET_EXT_FRAME_CONF_MASK) + + +/* Supported Commands (Byte 30) */ +#define HCI_SUPP_COMMANDS_SET_MWS_SIGNALING_MASK 0x01 +#define HCI_SUPP_COMMANDS_SET_MWS_SIGNALING_OFF 30 +#define HCI_SET_MWS_SIGNALING_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_SET_MWS_SIGNALING_OFF] & HCI_SUPP_COMMANDS_SET_MWS_SIGNALING_MASK) + +#define HCI_SUPP_COMMANDS_SET_MWS_TRANS_LAYER_MASK 0x02 +#define HCI_SUPP_COMMANDS_SET_MWS_TRANS_LAYER_OFF 30 +#define HCI_SET_MWS_TRANSPORT_LAYER_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_SET_MWS_TRANS_LAYER_OFF] & HCI_SUPP_COMMANDS_SET_MWS_TRANS_LAYER_MASK) + +#define HCI_SUPP_COMMANDS_SET_MWS_SCAN_FREQ_TABLE_MASK 0x04 +#define HCI_SUPP_COMMANDS_SET_MWS_SCAN_FREQ_TABLE_OFF 30 +#define HCI_SET_MWS_SCAN_FREQUENCY_TABLE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_SET_MWS_SCAN_FREQ_TABLE_OFF] & HCI_SUPP_COMMANDS_SET_MWS_SCAN_FREQ_TABLE_MASK) + +#define HCI_SUPP_COMMANDS_GET_TRANS_LAYER_CONF_MASK 0x08 +#define HCI_SUPP_COMMANDS_GET_TRANS_LAYER_CONF_OFF 30 +#define HCI_GET_MWS_TRANS_LAYER_CFG_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_GET_TRANS_LAYER_CONF_OFF] & HCI_SUPP_COMMANDS_GET_TRANS_LAYER_CONF_MASK) + +#define HCI_SUPP_COMMANDS_SET_MWS_PATTERN_CONF_MASK 0x10 +#define HCI_SUPP_COMMANDS_SET_MWS_PATTERN_CONF_OFF 30 +#define HCI_SET_MWS_PATTERN_CONFIGURATION_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_SET_MWS_PATTERN_CONF_OFF] & HCI_SUPP_COMMANDS_SET_MWS_PATTERN_CONF_MASK) + +/* Supported Commands (Byte 30 bit 5) */ +#define HCI_SUPP_COMMANDS_SET_TRIG_CLK_CAP_MASK 0x20 +#define HCI_SUPP_COMMANDS_SET_TRIG_CLK_CAP_OFF 30 +#define HCI_SET_TRIG_CLK_CAP_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_SET_TRIG_CLK_CAP_OFF] & HCI_SUPP_COMMANDS_SET_TRIG_CLK_CAP_MASK) + + +/* Supported Commands (Byte 30 bit 6-7) */ +#define HCI_SUPP_COMMANDS_TRUNCATED_PAGE 0x06 +#define HCI_SUPP_COMMANDS_TRUNCATED_PAGE_OFF 30 +#define HCI_TRUNCATED_PAGE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_TRUNCATED_PAGE_OFF] & HCI_SUPP_COMMANDS_TRUNCATED_PAGE) + +#define HCI_SUPP_COMMANDS_TRUNCATED_PAGE_CANCEL 0x07 +#define HCI_SUPP_COMMANDS_TRUNCATED_PAGE_CANCEL_OFF 30 +#define HCI_TRUNCATED_PAGE_CANCEL_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_TRUNCATED_PAGE_CANCEL_OFF] & HCI_SUPP_COMMANDS_TRUNCATED_PAGE_CANCEL) + +/* Supported Commands (Byte 31 bit 6-7) */ +#define HCI_SUPP_COMMANDS_SET_CONLESS_SLAVE_BRCST 0x00 +#define HCI_SUPP_COMMANDS_SET_CONLESS_SLAVE_BRCST_OFF 31 +#define HCI_SET_CONLESS_SLAVE_BRCST_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_SET_CONLESS_SLAVE_BRCST_OFF] & HCI_SUPP_COMMANDS_SET_CONLESS_SLAVE_BRCST) + +#define HCI_SUPP_COMMANDS_SET_CONLESS_SLAVE_BRCST_RECEIVE 0x01 +#define HCI_SUPP_COMMANDS_SET_CONLESS_SLAVE_BRCST_RECEIVE_OFF 31 +#define HCI_SET_CONLESS_SLAVE_BRCST_RECEIVE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_SET_CONLESS_SLAVE_BRCST_RECEIVE_OFF] & HCI_SUPP_COMMANDS_SET_CONLESS_SLAVE_BRCST_RECEIVE) + +#define HCI_SUPP_COMMANDS_START_SYNC_TRAIN 0x02 +#define HCI_SUPP_COMMANDS_START_SYNC_TRAIN_OFF 31 +#define HCI_START_SYNC_TRAIN_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_START_SYNC_TRAIN_OFF] & HCI_SUPP_COMMANDS_START_SYNC_TRAIN) + +#define HCI_SUPP_COMMANDS_RECEIVE_SYNC_TRAIN 0x03 +#define HCI_SUPP_COMMANDS_RECEIVE_SYNC_TRAIN_OFF 31 +#define HCI_RECEIVE_SYNC_TRAIN_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_RECEIVE_SYNC_TRAIN_OFF] & HCI_SUPP_COMMANDS_RECEIVE_SYNC_TRAIN) + +#define HCI_SUPP_COMMANDS_SET_RESERVED_LT_ADDR 0x04 +#define HCI_SUPP_COMMANDS_SET_RESERVED_LT_ADDR_OFF 31 +#define HCI_SET_RESERVED_LT_ADDR_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_SET_RESERVED_LT_ADDR_OFF] & HCI_SUPP_COMMANDS_SET_RESERVED_LT_ADDR) + +#define HCI_SUPP_COMMANDS_DELETE_RESERVED_LT_ADDR 0x05 +#define HCI_SUPP_COMMANDS_DELETE_RESERVED_LT_ADDR_OFF 31 +#define HCI_DELETE_RESERVED_LT_ADDR_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_DELETE_RESERVED_LT_ADDR_OFF] & HCI_SUPP_COMMANDS_DELETE_RESERVED_LT_ADDR) + +#define HCI_SUPP_COMMANDS_SET_CONLESS_SLAVE_BRCST_DATA 0x06 +#define HCI_SUPP_COMMANDS_SET_CONLESS_SLAVE_BRCST_DATA_OFF 31 +#define HCI_SET_CONLESS_SLAVE_BRCST_DATA_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_SET_CONLESS_SLAVE_BRCST_DATA_OFF] & HCI_SUPP_COMMANDS_SET_CONLESS_SLAVE_BRCST_DATA) + +#define HCI_SUPP_COMMANDS_READ_SYNC_TRAIN_PARAM 0x07 +#define HCI_SUPP_COMMANDS_READ_SYNC_TRAIN_PARAM_OFF 31 +#define HCI_READ_SYNC_TRAIN_PARAM_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_SYNC_TRAIN_PARAM_OFF] & HCI_SUPP_COMMANDS_READ_SYNC_TRAIN_PARAM) + +/* Supported Commands (Byte 32 bit 0) */ +#define HCI_SUPP_COMMANDS_WRITE_SYNC_TRAIN_PARAM 0x00 +#define HCI_SUPP_COMMANDS_WRITE_SYNC_TRAIN_PARAM_OFF 32 +#define HCI_WRITE_SYNC_TRAIN_PARAM_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_SYNC_TRAIN_PARAM_OFF] & HCI_SUPP_COMMANDS_WRITE_SYNC_TRAIN_PARAM) + +#define HCI_SUPP_COMMANDS_REMOTE_OOB_EXTENDED_DATA_REQUEST_REPLY_MASK 0x02 +#define HCI_SUPP_COMMANDS_REMOTE_OOB_EXTENDED_DATA_REQUEST_REPLY_OFF 32 +#define HCI_REMOTE_OOB_EXTENDED_DATA_REQUEST_REPLY_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_REMOTE_OOB_EXTENDED_DATA_REQUEST_REPLY_OFF] & HCI_SUPP_COMMANDS_REMOTE_OOB_EXTENDED_DATA_REQUEST_REPLY_MASK) + +#define HCI_SUPP_COMMANDS_READ_SECURE_CONNS_SUPPORT_MASK 0x04 +#define HCI_SUPP_COMMANDS_READ_SECURE_CONNS_SUPPORT_OFF 32 +#define HCI_READ_SECURE_CONNS_SUPPORT_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_SECURE_CONNS_SUPPORT_OFF] & HCI_SUPP_COMMANDS_READ_SECURE_CONNS_SUPPORT_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_SECURE_CONNS_SUPPORT_MASK 0x08 +#define HCI_SUPP_COMMANDS_WRITE_SECURE_CONNS_SUPPORT_OFF 32 +#define HCI_WRITE_SECURE_CONNS_SUPPORT_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_SECURE_CONNS_SUPPORT_OFF] & HCI_SUPP_COMMANDS_WRITE_SECURE_CONNS_SUPPORT_MASK) + +#define HCI_SUPP_COMMANDS_READ_AUTHENT_PAYLOAD_TOUT_MASK 0x10 +#define HCI_SUPP_COMMANDS_READ_AUTHENT_PAYLOAD_TOUT_OFF 32 +#define HCI_READ_AUTHENT_PAYLOAD_TOUT_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_AUTHENT_PAYLOAD_TOUT_OFF] & HCI_SUPP_COMMANDS_READ_AUTHENT_PAYLOAD_TOUT_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_AUTHENT_PAYLOAD_TOUT_MASK 0x20 +#define HCI_SUPP_COMMANDS_WRITE_AUTHENT_PAYLOAD_TOUT_OFF 32 +#define HCI_WRITE_AUTHENT_PAYLOAD_TOUT_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_AUTHENT_PAYLOAD_TOUT_OFF] & HCI_SUPP_COMMANDS_WRITE_AUTHENT_PAYLOAD_TOUT_MASK) + +#define HCI_SUPP_COMMANDS_READ_LOCAL_OOB_EXTENDED_DATA_MASK 0x40 +#define HCI_SUPP_COMMANDS_READ_LOCAL_OOB_EXTENDED_DATA_OFF 32 +#define HCI_READ_LOCAL_OOB_EXTENDED_DATA_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_LOCAL_OOB_EXTENDED_DATA_OFF] & HCI_SUPP_COMMANDS_READ_LOCAL_OOB_EXTENDED_DATA_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_SECURE_CONNECTIONS_TEST_MODE_MASK 0x80 +#define HCI_SUPP_COMMANDS_WRITE_SECURE_CONNECTIONS_TEST_MODE_OFF 32 +#define HCI_WRITE_SECURE_CONNECTIONS_TEST_MODE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_SECURE_CONNECTIONS_TEST_MODE_OFF] & HCI_SUPP_COMMANDS_WRITE_SECURE_CONNECTIONS_TEST_MODE_MASK) + +/* supported LE remote control connection parameter request reply */ +#define HCI_SUPP_COMMANDS_LE_RC_CONN_PARAM_UPD_RPY_MASK 0x10 +#define HCI_SUPP_COMMANDS_LE_RC_CONN_PARAM_UPD_RPY_OFF 33 +#define HCI_LE_RC_CONN_PARAM_UPD_RPY_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_LE_RC_CONN_PARAM_UPD_RPY_OFF] & HCI_SUPP_COMMANDS_LE_RC_CONN_PARAM_UPD_RPY_MASK) + +#define HCI_SUPP_COMMANDS_RLE_RC_CONN_PARAM_UPD_NEG_RPY_MASK 0x20 +#define HCI_SUPP_COMMANDS_LE_RC_CONN_PARAM_UPD_NEG_RPY_OFF 33 +#define HCI_LE_RC_CONN_PARAM_UPD_NEG_RPY_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_LE_RC_CONN_PARAM_UPD_NEG_RPY_OFF] & HCI_SUPP_COMMANDS_RLE_RC_CONN_PARAM_UPD_NEG_RPY_MASK) + +#endif diff --git a/lib/bt/host/bluedroid/stack/include/stack/hcimsgs.h b/lib/bt/host/bluedroid/stack/include/stack/hcimsgs.h new file mode 100644 index 00000000..bd328fab --- /dev/null +++ b/lib/bt/host/bluedroid/stack/include/stack/hcimsgs.h @@ -0,0 +1,1065 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +#ifndef HCIMSGS_H +#define HCIMSGS_H + +#include +#include "common/bt_target.h" +#include "stack/hcidefs.h" +#include "stack/bt_types.h" +#include "osi/pkt_queue.h" +#include "osi/allocator.h" + +#define HCI_CMD_BUF_TYPE_METADATA (0xa56e) + +#define HCI_CMD_MSG_F_SRC_NOACK (0x01) + +typedef void (*hci_cmd_cmpl_cb)(BT_HDR *response, void *context); +typedef void (*hci_cmd_stat_cb)(uint8_t status, BT_HDR *command, void *context); +typedef void (*hci_cmd_free_cb)(pkt_linked_item_t *linked_pkt); + +typedef struct { + uint8_t flags_src; + uint8_t flags_vnd; // used for downstream layer + uint16_t opcode; + hci_cmd_cmpl_cb command_complete_cb; + hci_cmd_stat_cb command_status_cb; + void *context; + void *complete_future; + hci_cmd_free_cb command_free_cb; + BT_HDR command; +} hci_cmd_metadata_t; + +#define HCI_CMD_METADATA_HDR_SIZE (sizeof(hci_cmd_metadata_t)) + +#define HCI_CMD_LINKED_BUF_SIZE(paramlen) (BT_PKT_LINKED_HDR_SIZE + HCI_CMD_METADATA_HDR_SIZE + HCIC_PREAMBLE_SIZE + (paramlen)) + +#define HCI_GET_CMD_METAMSG(cmd_ptr) (hci_cmd_metadata_t *)((void *)(cmd_ptr) - offsetof(hci_cmd_metadata_t, command)) +#define HCI_GET_CMD_LINKED_STRUCT(metadata_ptr) (pkt_linked_item_t *)((void *)(metadata_ptr) - offsetof(pkt_linked_item_t, data)) + +static inline BT_HDR *hci_get_cmd_buf(size_t param_len) +{ + pkt_linked_item_t *linked_pkt = osi_calloc(HCI_CMD_LINKED_BUF_SIZE(param_len)); + if (linked_pkt == NULL) { + return NULL; + } + hci_cmd_metadata_t *metadata = (hci_cmd_metadata_t *)linked_pkt->data; + BT_HDR *command = &metadata->command; + + command->layer_specific = HCI_CMD_BUF_TYPE_METADATA; + command->len = HCIC_PREAMBLE_SIZE + param_len; + command->offset = 0; + + return command; +} + +static inline void hci_free_cmd_buf(BT_HDR *buf) +{ + assert(buf->layer_specific == HCI_CMD_BUF_TYPE_METADATA); + hci_cmd_metadata_t *metadata = HCI_GET_CMD_METAMSG(buf); + pkt_linked_item_t *linked_pkt = HCI_GET_CMD_LINKED_STRUCT(metadata); + osi_free(linked_pkt); +} + +#define HCI_GET_CMD_BUF(param_len) hci_get_cmd_buf(param_len) +#define HCI_FREE_CMD_BUF(buf) hci_free_cmd_buf(buf) + +void bte_main_hci_send(BT_HDR *p_msg, UINT16 event); +void bte_main_lpm_allow_bt_device_sleep(void); + +/* Message by message.... */ + +BOOLEAN btsnd_hcic_inquiry(const LAP inq_lap, UINT8 duration, + UINT8 response_cnt); + +#define HCIC_PARAM_SIZE_INQUIRY 5 + + +#define HCIC_INQ_INQ_LAP_OFF 0 +#define HCIC_INQ_DUR_OFF 3 +#define HCIC_INQ_RSP_CNT_OFF 4 +/* Inquiry */ + +/* Inquiry Cancel */ +BOOLEAN btsnd_hcic_inq_cancel(void); + +#define HCIC_PARAM_SIZE_INQ_CANCEL 0 + +/* Periodic Inquiry Mode */ +BOOLEAN btsnd_hcic_per_inq_mode(UINT16 max_period, UINT16 min_period, + const LAP inq_lap, UINT8 duration, + UINT8 response_cnt); + +#define HCIC_PARAM_SIZE_PER_INQ_MODE 9 + +#define HCI_PER_INQ_MAX_INTRVL_OFF 0 +#define HCI_PER_INQ_MIN_INTRVL_OFF 2 +#define HCI_PER_INQ_INQ_LAP_OFF 4 +#define HCI_PER_INQ_DURATION_OFF 7 +#define HCI_PER_INQ_RSP_CNT_OFF 8 +/* Periodic Inquiry Mode */ + +/* Exit Periodic Inquiry Mode */ +BOOLEAN btsnd_hcic_exit_per_inq(void); + +#define HCIC_PARAM_SIZE_EXIT_PER_INQ 0 +/* Create Connection */ +BOOLEAN btsnd_hcic_create_conn(BD_ADDR dest, UINT16 packet_types, + UINT8 page_scan_rep_mode, + UINT8 page_scan_mode, + UINT16 clock_offset, + UINT8 allow_switch); + +#define HCIC_PARAM_SIZE_CREATE_CONN 13 + +#define HCIC_CR_CONN_BD_ADDR_OFF 0 +#define HCIC_CR_CONN_PKT_TYPES_OFF 6 +#define HCIC_CR_CONN_REP_MODE_OFF 8 +#define HCIC_CR_CONN_PAGE_SCAN_MODE_OFF 9 +#define HCIC_CR_CONN_CLK_OFF_OFF 10 +#define HCIC_CR_CONN_ALLOW_SWITCH_OFF 12 +/* Create Connection */ + +/* Disconnect */ +BOOLEAN btsnd_hcic_disconnect(UINT16 handle, UINT8 reason); + +#define HCIC_PARAM_SIZE_DISCONNECT 3 + +#define HCI_DISC_HANDLE_OFF 0 +#define HCI_DISC_REASON_OFF 2 +/* Disconnect */ + +#if BTM_SCO_INCLUDED == TRUE +/* Add SCO Connection */ +BOOLEAN btsnd_hcic_add_SCO_conn (UINT16 handle, UINT16 packet_types); +#endif /* BTM_SCO_INCLUDED */ + +#define HCIC_PARAM_SIZE_ADD_SCO_CONN 4 + +#define HCI_ADD_SCO_HANDLE_OFF 0 +#define HCI_ADD_SCO_PACKET_TYPES_OFF 2 +/* Add SCO Connection */ + +/* Create Connection Cancel */ +BOOLEAN btsnd_hcic_create_conn_cancel(BD_ADDR dest); + +#define HCIC_PARAM_SIZE_CREATE_CONN_CANCEL 6 + +#define HCIC_CR_CONN_CANCEL_BD_ADDR_OFF 0 +/* Create Connection Cancel */ + +/* Accept Connection Request */ +BOOLEAN btsnd_hcic_accept_conn (BD_ADDR bd_addr, UINT8 role); + +#define HCIC_PARAM_SIZE_ACCEPT_CONN 7 + +#define HCI_ACC_CONN_BD_ADDR_OFF 0 +#define HCI_ACC_CONN_ROLE_OFF 6 +/* Accept Connection Request */ + +/* Reject Connection Request */ +BOOLEAN btsnd_hcic_reject_conn (BD_ADDR bd_addr, UINT8 reason); + +#define HCIC_PARAM_SIZE_REJECT_CONN 7 + +#define HCI_REJ_CONN_BD_ADDR_OFF 0 +#define HCI_REJ_CONN_REASON_OFF 6 +/* Reject Connection Request */ + +/* Link Key Request Reply */ +BOOLEAN btsnd_hcic_link_key_req_reply (BD_ADDR bd_addr, + LINK_KEY link_key); + +#define HCIC_PARAM_SIZE_LINK_KEY_REQ_REPLY 22 + +#define HCI_LINK_KEY_REPLY_BD_ADDR_OFF 0 +#define HCI_LINK_KEY_REPLY_LINK_KEY_OFF 6 +/* Link Key Request Reply */ + +/* Link Key Request Neg Reply */ +BOOLEAN btsnd_hcic_link_key_neg_reply (BD_ADDR bd_addr); + +#define HCIC_PARAM_SIZE_LINK_KEY_NEG_REPLY 6 + +#define HCI_LINK_KEY_NEG_REP_BD_ADR_OFF 0 +/* Link Key Request Neg Reply */ + +/* PIN Code Request Reply */ +BOOLEAN btsnd_hcic_pin_code_req_reply (BD_ADDR bd_addr, + UINT8 pin_code_len, + PIN_CODE pin_code); + +#define HCIC_PARAM_SIZE_PIN_CODE_REQ_REPLY 23 + +#define HCI_PIN_CODE_REPLY_BD_ADDR_OFF 0 +#define HCI_PIN_CODE_REPLY_PIN_LEN_OFF 6 +#define HCI_PIN_CODE_REPLY_PIN_CODE_OFF 7 +/* PIN Code Request Reply */ + +/* Link Key Request Neg Reply */ +BOOLEAN btsnd_hcic_pin_code_neg_reply (BD_ADDR bd_addr); + +#define HCIC_PARAM_SIZE_PIN_CODE_NEG_REPLY 6 + +#define HCI_PIN_CODE_NEG_REP_BD_ADR_OFF 0 +/* Link Key Request Neg Reply */ + +/* Change Connection Type */ +BOOLEAN btsnd_hcic_change_conn_type (UINT16 handle, UINT16 packet_types); + +#define HCIC_PARAM_SIZE_CHANGE_CONN_TYPE 4 + +#define HCI_CHNG_PKT_TYPE_HANDLE_OFF 0 +#define HCI_CHNG_PKT_TYPE_PKT_TYPE_OFF 2 +/* Change Connection Type */ + +#define HCIC_PARAM_SIZE_CMD_HANDLE 2 + +#define HCI_CMD_HANDLE_HANDLE_OFF 0 + +BOOLEAN btsnd_hcic_auth_request (UINT16 handle); /* Authentication Request */ + +/* Set Connection Encryption */ +BOOLEAN btsnd_hcic_set_conn_encrypt (UINT16 handle, BOOLEAN enable); +#define HCIC_PARAM_SIZE_SET_CONN_ENCRYPT 3 + + +#define HCI_SET_ENCRYPT_HANDLE_OFF 0 +#define HCI_SET_ENCRYPT_ENABLE_OFF 2 +/* Set Connection Encryption */ + +/* Remote Name Request */ +BOOLEAN btsnd_hcic_rmt_name_req (BD_ADDR bd_addr, + UINT8 page_scan_rep_mode, + UINT8 page_scan_mode, + UINT16 clock_offset); + +#define HCIC_PARAM_SIZE_RMT_NAME_REQ 10 + +#define HCI_RMT_NAME_BD_ADDR_OFF 0 +#define HCI_RMT_NAME_REP_MODE_OFF 6 +#define HCI_RMT_NAME_PAGE_SCAN_MODE_OFF 7 +#define HCI_RMT_NAME_CLK_OFF_OFF 8 +/* Remote Name Request */ + +/* Remote Name Request Cancel */ +BOOLEAN btsnd_hcic_rmt_name_req_cancel(BD_ADDR bd_addr); + +#define HCIC_PARAM_SIZE_RMT_NAME_REQ_CANCEL 6 + +#define HCI_RMT_NAME_CANCEL_BD_ADDR_OFF 0 +/* Remote Name Request Cancel */ + +BOOLEAN btsnd_hcic_rmt_features_req(UINT16 handle); /* Remote Features Request */ + +/* Remote Extended Features */ +BOOLEAN btsnd_hcic_rmt_ext_features(UINT16 handle, UINT8 page_num); + +#define HCIC_PARAM_SIZE_RMT_EXT_FEATURES 3 + +#define HCI_RMT_EXT_FEATURES_HANDLE_OFF 0 +#define HCI_RMT_EXT_FEATURES_PAGE_NUM_OFF 2 +/* Remote Extended Features */ + + +BOOLEAN btsnd_hcic_rmt_ver_req(UINT16 handle); /* Remote Version Info Request */ +BOOLEAN btsnd_hcic_read_rmt_clk_offset(UINT16 handle); /* Remote Clock Offset */ +BOOLEAN btsnd_hcic_read_lmp_handle(UINT16 handle); /* Remote LMP Handle */ + +BOOLEAN btsnd_hcic_setup_esco_conn (UINT16 handle, + UINT32 tx_bw, UINT32 rx_bw, + UINT16 max_latency, UINT16 voice, + UINT8 retrans_effort, + UINT16 packet_types); +#define HCIC_PARAM_SIZE_SETUP_ESCO 17 + +#define HCI_SETUP_ESCO_HANDLE_OFF 0 +#define HCI_SETUP_ESCO_TX_BW_OFF 2 +#define HCI_SETUP_ESCO_RX_BW_OFF 6 +#define HCI_SETUP_ESCO_MAX_LAT_OFF 10 +#define HCI_SETUP_ESCO_VOICE_OFF 12 +#define HCI_SETUP_ESCO_RETRAN_EFF_OFF 14 +#define HCI_SETUP_ESCO_PKT_TYPES_OFF 15 + + +BOOLEAN btsnd_hcic_accept_esco_conn (BD_ADDR bd_addr, + UINT32 tx_bw, UINT32 rx_bw, + UINT16 max_latency, + UINT16 content_fmt, + UINT8 retrans_effort, + UINT16 packet_types); +#define HCIC_PARAM_SIZE_ACCEPT_ESCO 21 + +#define HCI_ACCEPT_ESCO_BDADDR_OFF 0 +#define HCI_ACCEPT_ESCO_TX_BW_OFF 6 +#define HCI_ACCEPT_ESCO_RX_BW_OFF 10 +#define HCI_ACCEPT_ESCO_MAX_LAT_OFF 14 +#define HCI_ACCEPT_ESCO_VOICE_OFF 16 +#define HCI_ACCEPT_ESCO_RETRAN_EFF_OFF 18 +#define HCI_ACCEPT_ESCO_PKT_TYPES_OFF 19 + + +BOOLEAN btsnd_hcic_reject_esco_conn (BD_ADDR bd_addr, UINT8 reason); +#define HCIC_PARAM_SIZE_REJECT_ESCO 7 + +#define HCI_REJECT_ESCO_BDADDR_OFF 0 +#define HCI_REJECT_ESCO_REASON_OFF 6 + +/* Hold Mode */ +BOOLEAN btsnd_hcic_hold_mode(UINT16 handle, UINT16 max_hold_period, + UINT16 min_hold_period); + +#define HCIC_PARAM_SIZE_HOLD_MODE 6 + +#define HCI_HOLD_MODE_HANDLE_OFF 0 +#define HCI_HOLD_MODE_MAX_PER_OFF 2 +#define HCI_HOLD_MODE_MIN_PER_OFF 4 +/* Hold Mode */ + +/** + * Sniff Mode + * sniff_attempt should no more than 0xFF + */ +BOOLEAN btsnd_hcic_sniff_mode(UINT16 handle, + UINT16 max_sniff_period, + UINT16 min_sniff_period, + UINT16 sniff_attempt, + UINT16 sniff_timeout); + +#define HCIC_PARAM_SIZE_SNIFF_MODE 10 + + +#define HCI_SNIFF_MODE_HANDLE_OFF 0 +#define HCI_SNIFF_MODE_MAX_PER_OFF 2 +#define HCI_SNIFF_MODE_MIN_PER_OFF 4 +#define HCI_SNIFF_MODE_ATTEMPT_OFF 6 +#define HCI_SNIFF_MODE_TIMEOUT_OFF 8 +/* Sniff Mode */ + +BOOLEAN btsnd_hcic_exit_sniff_mode(UINT16 handle); /* Exit Sniff Mode */ + +/* Park Mode */ +BOOLEAN btsnd_hcic_park_mode (UINT16 handle, + UINT16 beacon_max_interval, + UINT16 beacon_min_interval); + +#define HCIC_PARAM_SIZE_PARK_MODE 6 + +#define HCI_PARK_MODE_HANDLE_OFF 0 +#define HCI_PARK_MODE_MAX_PER_OFF 2 +#define HCI_PARK_MODE_MIN_PER_OFF 4 +/* Park Mode */ + +BOOLEAN btsnd_hcic_exit_park_mode(UINT16 handle); /* Exit Park Mode */ + +/* QoS Setup */ +BOOLEAN btsnd_hcic_qos_setup (UINT16 handle, UINT8 flags, + UINT8 service_type, + UINT32 token_rate, UINT32 peak, + UINT32 latency, UINT32 delay_var); + +#define HCIC_PARAM_SIZE_QOS_SETUP 20 + +#define HCI_QOS_HANDLE_OFF 0 +#define HCI_QOS_FLAGS_OFF 2 +#define HCI_QOS_SERVICE_TYPE_OFF 3 +#define HCI_QOS_TOKEN_RATE_OFF 4 +#define HCI_QOS_PEAK_BANDWIDTH_OFF 8 +#define HCI_QOS_LATENCY_OFF 12 +#define HCI_QOS_DELAY_VAR_OFF 16 +/* QoS Setup */ + +/* Switch Role Request */ +BOOLEAN btsnd_hcic_switch_role (BD_ADDR bd_addr, UINT8 role); + +#define HCIC_PARAM_SIZE_SWITCH_ROLE 7 + +#define HCI_SWITCH_BD_ADDR_OFF 0 +#define HCI_SWITCH_ROLE_OFF 6 +/* Switch Role Request */ + +/* Write Policy Settings */ +BOOLEAN btsnd_hcic_write_policy_set(UINT16 handle, UINT16 settings); + +#define HCIC_PARAM_SIZE_WRITE_POLICY_SET 4 + +#define HCI_WRITE_POLICY_HANDLE_OFF 0 +#define HCI_WRITE_POLICY_SETTINGS_OFF 2 +/* Write Policy Settings */ + +/* Write Default Policy Settings */ +BOOLEAN btsnd_hcic_write_def_policy_set(UINT16 settings); + +#define HCIC_PARAM_SIZE_WRITE_DEF_POLICY_SET 2 + +#define HCI_WRITE_DEF_POLICY_SETTINGS_OFF 0 +/* Write Default Policy Settings */ + +/****************************************** +** Lisbon Features +*******************************************/ +#if BTM_SSR_INCLUDED == TRUE +/* Sniff Subrating */ +BOOLEAN btsnd_hcic_sniff_sub_rate(UINT16 handle, UINT16 max_lat, + UINT16 min_remote_lat, + UINT16 min_local_lat); + +#define HCIC_PARAM_SIZE_SNIFF_SUB_RATE 8 + +#define HCI_SNIFF_SUB_RATE_HANDLE_OFF 0 +#define HCI_SNIFF_SUB_RATE_MAX_LAT_OFF 2 +#define HCI_SNIFF_SUB_RATE_MIN_REM_LAT_OFF 4 +#define HCI_SNIFF_SUB_RATE_MIN_LOC_LAT_OFF 6 +/* Sniff Subrating */ + +#else /* BTM_SSR_INCLUDED == FALSE */ + +#define btsnd_hcic_sniff_sub_rate(handle, max_lat, min_remote_lat, min_local_lat) FALSE + +#endif /* BTM_SSR_INCLUDED */ + +/* Extended Inquiry Response */ +void btsnd_hcic_write_ext_inquiry_response(BT_HDR *buffer, UINT8 fec_req); + +#define HCIC_PARAM_SIZE_EXT_INQ_RESP 241 + +#define HCIC_EXT_INQ_RESP_FEC_OFF 0 +#define HCIC_EXT_INQ_RESP_RESPONSE 1 +/* IO Capabilities Response */ +BOOLEAN btsnd_hcic_io_cap_req_reply (BD_ADDR bd_addr, UINT8 capability, + UINT8 oob_present, UINT8 auth_req); + +#define HCIC_PARAM_SIZE_IO_CAP_RESP 9 + +#define HCI_IO_CAP_BD_ADDR_OFF 0 +#define HCI_IO_CAPABILITY_OFF 6 +#define HCI_IO_CAP_OOB_DATA_OFF 7 +#define HCI_IO_CAP_AUTH_REQ_OFF 8 + +/* IO Capabilities Req Neg Reply */ +BOOLEAN btsnd_hcic_io_cap_req_neg_reply (BD_ADDR bd_addr, UINT8 err_code); + +#define HCIC_PARAM_SIZE_IO_CAP_NEG_REPLY 7 + +#define HCI_IO_CAP_NR_BD_ADDR_OFF 0 +#define HCI_IO_CAP_NR_ERR_CODE 6 + +/* Read Local OOB Data */ +BOOLEAN btsnd_hcic_read_local_oob_data (void); + +#define HCIC_PARAM_SIZE_R_LOCAL_OOB 0 + + +BOOLEAN btsnd_hcic_user_conf_reply (BD_ADDR bd_addr, BOOLEAN is_yes); + +#define HCIC_PARAM_SIZE_UCONF_REPLY 6 + +#define HCI_USER_CONF_BD_ADDR_OFF 0 + + +BOOLEAN btsnd_hcic_user_passkey_reply (BD_ADDR bd_addr, UINT32 value); + +#define HCIC_PARAM_SIZE_U_PKEY_REPLY 10 + +#define HCI_USER_PASSKEY_BD_ADDR_OFF 0 +#define HCI_USER_PASSKEY_VALUE_OFF 6 + + +BOOLEAN btsnd_hcic_user_passkey_neg_reply (BD_ADDR bd_addr); + +#define HCIC_PARAM_SIZE_U_PKEY_NEG_REPLY 6 + +#define HCI_USER_PASSKEY_NEG_BD_ADDR_OFF 0 + +/* Remote OOB Data Request Reply */ +BOOLEAN btsnd_hcic_rem_oob_reply (BD_ADDR bd_addr, UINT8 *p_c, + UINT8 *p_r); + +#define HCIC_PARAM_SIZE_REM_OOB_REPLY 38 + +#define HCI_REM_OOB_DATA_BD_ADDR_OFF 0 +#define HCI_REM_OOB_DATA_C_OFF 6 +#define HCI_REM_OOB_DATA_R_OFF 22 + +/* Remote OOB Data Request Negative Reply */ +BOOLEAN btsnd_hcic_rem_oob_neg_reply (BD_ADDR bd_addr); + +#define HCIC_PARAM_SIZE_REM_OOB_NEG_REPLY 6 + +#define HCI_REM_OOB_DATA_NEG_BD_ADDR_OFF 0 + +/* Read Tx Power Level */ +BOOLEAN btsnd_hcic_read_inq_tx_power (void); + +#define HCIC_PARAM_SIZE_R_TX_POWER 0 + +/* Read Default Erroneous Data Reporting */ +BOOLEAN btsnd_hcic_read_default_erroneous_data_rpt (void); + +#define HCIC_PARAM_SIZE_R_ERR_DATA_RPT 0 + +#if L2CAP_NON_FLUSHABLE_PB_INCLUDED == TRUE +BOOLEAN btsnd_hcic_enhanced_flush (UINT16 handle, UINT8 packet_type); + +#define HCIC_PARAM_SIZE_ENHANCED_FLUSH 3 +#endif + + +BOOLEAN btsnd_hcic_send_keypress_notif (BD_ADDR bd_addr, UINT8 notif); + +#define HCIC_PARAM_SIZE_SEND_KEYPRESS_NOTIF 7 + +#define HCI_SEND_KEYPRESS_NOTIF_BD_ADDR_OFF 0 +#define HCI_SEND_KEYPRESS_NOTIF_NOTIF_OFF 6 + +/**** end of Simple Pairing Commands ****/ + +/* Store Current Settings */ +#define MAX_FILT_COND (sizeof (BD_ADDR) + 1) + +BOOLEAN btsnd_hcic_set_event_filter(UINT8 filt_type, + UINT8 filt_cond_type, + UINT8 *filt_cond, + UINT8 filt_cond_len); + +#define HCIC_PARAM_SIZE_SET_EVT_FILTER 9 + +#define HCI_FILT_COND_FILT_TYPE_OFF 0 +#define HCI_FILT_COND_COND_TYPE_OFF 1 +#define HCI_FILT_COND_FILT_OFF 2 +/* Set Event Filter */ + +/* Delete Stored Key */ +BOOLEAN btsnd_hcic_delete_stored_key (BD_ADDR bd_addr, BOOLEAN delete_all_flag); + +#define HCIC_PARAM_SIZE_DELETE_STORED_KEY 7 + +#define HCI_DELETE_KEY_BD_ADDR_OFF 0 +#define HCI_DELETE_KEY_ALL_FLAG_OFF 6 +/* Delete Stored Key */ + +/* Change Local Name */ +BOOLEAN btsnd_hcic_change_name (const UINT8 *name); + +#define HCIC_PARAM_SIZE_CHANGE_NAME BD_NAME_LEN + +#define HCI_CHANGE_NAME_NAME_OFF 0 +/* Change Local Name */ + + +#define HCIC_PARAM_SIZE_READ_CMD 0 + +#define HCIC_PARAM_SIZE_WRITE_PARAM1 1 + +#define HCIC_WRITE_PARAM1_PARAM_OFF 0 + +#define HCIC_PARAM_SIZE_WRITE_PARAM2 2 + +#define HCIC_WRITE_PARAM2_PARAM_OFF 0 + +#define HCIC_PARAM_SIZE_WRITE_PARAM3 3 + +#define HCIC_WRITE_PARAM3_PARAM_OFF 0 + +/* set AFH channels */ +BOOLEAN btsnd_hcic_set_afh_channels (AFH_CHANNELS channels); +#define HCIC_PARAM_SIZE_SET_AFH_CHANNELS 10 +BOOLEAN btsnd_hcic_ble_set_channels (BLE_CHANNELS channels); +#define HCIC_PARAM_SIZE_BLE_SET_CHANNELS 5 + +BOOLEAN btsnd_hcic_write_pin_type(UINT8 type); /* Write PIN Type */ +BOOLEAN btsnd_hcic_write_auto_accept(UINT8 flag); /* Write Auto Accept */ +BOOLEAN btsnd_hcic_read_name (void); /* Read Local Name */ +BOOLEAN btsnd_hcic_write_page_tout(UINT16 timeout); /* Write Page Timeout */ +BOOLEAN btsnd_hcic_write_scan_enable(UINT8 flag); /* Write Scan Enable */ +BOOLEAN btsnd_hcic_write_pagescan_cfg(UINT16 interval, + UINT16 window); /* Write Page Scan Activity */ + +#define HCIC_PARAM_SIZE_WRITE_PAGESCAN_CFG 4 + +#define HCI_SCAN_CFG_INTERVAL_OFF 0 +#define HCI_SCAN_CFG_WINDOW_OFF 2 +/* Write Page Scan Activity */ + +/* Write Inquiry Scan Activity */ +BOOLEAN btsnd_hcic_write_inqscan_cfg(UINT16 interval, UINT16 window); + +#define HCIC_PARAM_SIZE_WRITE_INQSCAN_CFG 4 + +#define HCI_SCAN_CFG_INTERVAL_OFF 0 +#define HCI_SCAN_CFG_WINDOW_OFF 2 +/* Write Inquiry Scan Activity */ + +BOOLEAN btsnd_hcic_write_auth_enable(UINT8 flag); /* Write Authentication Enable */ +BOOLEAN btsnd_hcic_write_dev_class(DEV_CLASS dev); /* Write Class of Device */ +BOOLEAN btsnd_hcic_write_voice_settings(UINT16 flags); /* Write Voice Settings */ + +/* Host Controller to Host flow control */ +#define HCI_HOST_FLOW_CTRL_OFF 0 +#define HCI_HOST_FLOW_CTRL_ACL_ON 1 +#define HCI_HOST_FLOW_CTRL_SCO_ON 2 +#define HCI_HOST_FLOW_CTRL_BOTH_ON 3 + +#define HCI_HOST_FLOW_CTRL_ADV_REPORT_OFF 0 +#define HCI_HOST_FLOW_CTRL_ADV_REPORT_ON 1 + + +BOOLEAN btsnd_hcic_write_auto_flush_tout(UINT16 handle, + UINT16 timeout); /* Write Retransmit Timout */ + +#define HCIC_PARAM_SIZE_WRITE_AUTO_FLUSH_TOUT 4 + +#define HCI_FLUSH_TOUT_HANDLE_OFF 0 +#define HCI_FLUSH_TOUT_TOUT_OFF 2 + +BOOLEAN btsnd_hcic_read_tx_power(UINT16 handle, UINT8 type); /* Read Tx Power */ + +#define HCIC_PARAM_SIZE_READ_TX_POWER 3 + +#define HCI_READ_TX_POWER_HANDLE_OFF 0 +#define HCI_READ_TX_POWER_TYPE_OFF 2 + +/* Read transmit power level parameter */ +#define HCI_READ_CURRENT 0x00 +#define HCI_READ_MAXIMUM 0x01 + +BOOLEAN btsnd_hcic_host_num_xmitted_pkts (UINT8 num_handles, + UINT16 *handle, + UINT16 *num_pkts); /* Set Host Buffer Size */ + +#define HCIC_PARAM_SIZE_NUM_PKTS_DONE_SIZE sizeof(btmsg_hcic_num_pkts_done_t) + +#define MAX_DATA_HANDLES 10 + +#define HCI_PKTS_DONE_NUM_HANDLES_OFF 0 +#define HCI_PKTS_DONE_HANDLE_OFF 1 +#define HCI_PKTS_DONE_NUM_PKTS_OFF 3 + +/* Write Link Supervision Timeout */ +BOOLEAN btsnd_hcic_write_link_super_tout(UINT8 local_controller_id, UINT16 handle, UINT16 timeout); + +#define HCIC_PARAM_SIZE_WRITE_LINK_SUPER_TOUT 4 + +#define HCI_LINK_SUPER_TOUT_HANDLE_OFF 0 +#define HCI_LINK_SUPER_TOUT_TOUT_OFF 2 +/* Write Link Supervision Timeout */ + +BOOLEAN btsnd_hcic_write_cur_iac_lap (UINT8 num_cur_iac, + LAP *const iac_lap); /* Write Current IAC LAP */ + +#define MAX_IAC_LAPS 0x40 + +#define HCI_WRITE_IAC_LAP_NUM_OFF 0 +#define HCI_WRITE_IAC_LAP_LAP_OFF 1 +/* Write Current IAC LAP */ + +BOOLEAN btsnd_hcic_get_link_quality (UINT16 handle); /* Get Link Quality */ +BOOLEAN btsnd_hcic_read_rssi (UINT16 handle); /* Read RSSI */ +BOOLEAN btsnd_hcic_enable_test_mode (void); /* Enable Device Under Test Mode */ +BOOLEAN btsnd_hcic_write_pagescan_type(UINT8 type); /* Write Page Scan Type */ +BOOLEAN btsnd_hcic_write_inqscan_type(UINT8 type); /* Write Inquiry Scan Type */ +BOOLEAN btsnd_hcic_write_inquiry_mode(UINT8 type); /* Write Inquiry Mode */ + +#define HCI_DATA_HANDLE_MASK 0x0FFF + +#define HCID_GET_HANDLE_EVENT(p) (UINT16)((*((UINT8 *)((p) + 1) + p->offset) + \ + (*((UINT8 *)((p) + 1) + p->offset + 1) << 8))) + +#define HCID_GET_HANDLE(u16) (UINT16)((u16) & HCI_DATA_HANDLE_MASK) + +#define HCI_DATA_EVENT_MASK 3 +#define HCI_DATA_EVENT_OFFSET 12 +#define HCID_GET_EVENT(u16) (UINT8)(((u16) >> HCI_DATA_EVENT_OFFSET) & HCI_DATA_EVENT_MASK) + +#define HCI_DATA_BCAST_MASK 3 +#define HCI_DATA_BCAST_OFFSET 10 +#define HCID_GET_BCAST(u16) (UINT8)(((u16) >> HCI_DATA_BCAST_OFFSET) & HCI_DATA_BCAST_MASK) + +#define HCID_GET_ACL_LEN(p) (UINT16)((*((UINT8 *)((p) + 1) + p->offset + 2) + \ + (*((UINT8 *)((p) + 1) + p->offset + 3) << 8))) + +#define HCID_HEADER_SIZE 4 + +#define HCID_GET_SCO_LEN(p) (*((UINT8 *)((p) + 1) + p->offset + 2)) + +void btsnd_hcic_vendor_spec_cmd (BT_HDR *buffer, UINT16 opcode, + UINT8 len, UINT8 *p_data, + void *p_cmd_cplt_cback); + +#if (BLE_INCLUDED == TRUE) +/******************************************************************************** +** BLE Commands +** Note: "local_controller_id" is for transport, not counted in HCI message size +*********************************************************************************/ +#define HCIC_BLE_RAND_DI_SIZE 8 +#define HCIC_BLE_ENCRYT_KEY_SIZE 16 +#define HCIC_BLE_IRK_SIZE 16 + +#define HCIC_PARAM_SIZE_SET_USED_FEAT_CMD 8 +#define HCIC_PARAM_SIZE_WRITE_RANDOM_ADDR_CMD 6 +#define HCIC_PARAM_SIZE_BLE_WRITE_ADV_PARAMS 15 +#if (BLE_50_FEATURE_SUPPORT == TRUE) +#define HCIC_PARAM_SIZE_BLE_WRITE_EXT_ADV_PARAMS 25 +#endif // #if (BLE_50_FEATURE_SUPPORT == TRUE) +#define HCIC_PARAM_SIZE_BLE_WRITE_SCAN_RSP 31 +#define HCIC_PARAM_SIZE_WRITE_ADV_ENABLE 1 +#define HCIC_PARAM_SIZE_BLE_WRITE_SCAN_PARAM 7 +#define HCIC_PARAM_SIZE_BLE_WRITE_SCAN_ENABLE 2 +#define HCIC_PARAM_SIZE_BLE_CREATE_LL_CONN 25 +#define HCIC_PARAM_SIZE_BLE_CREATE_CONN_CANCEL 0 +#define HCIC_PARAM_SIZE_CLEAR_WHITE_LIST 0 +#define HCIC_PARAM_SIZE_ADD_WHITE_LIST 7 +#define HCIC_PARAM_SIZE_REMOVE_WHITE_LIST 7 +#define HCIC_PARAM_SIZE_BLE_UPD_LL_CONN_PARAMS 14 +#define HCIC_PARAM_SIZE_SET_HOST_CHNL_CLASS 5 +#define HCIC_PARAM_SIZE_READ_CHNL_MAP 2 +#define HCIC_PARAM_SIZE_BLE_READ_REMOTE_FEAT 2 +#define HCIC_PARAM_SIZE_BLE_ENCRYPT 32 +#define HCIC_PARAM_SIZE_BLE_RAND 0 +#define HCIC_PARAM_SIZE_WRITE_LE_HOST_SUPPORTED 2 + +#define HCIC_BLE_RAND_DI_SIZE 8 +#define HCIC_BLE_ENCRYT_KEY_SIZE 16 +#define HCIC_PARAM_SIZE_BLE_START_ENC (4 + HCIC_BLE_RAND_DI_SIZE + HCIC_BLE_ENCRYT_KEY_SIZE) +#define HCIC_PARAM_SIZE_LTK_REQ_REPLY (2 + HCIC_BLE_ENCRYT_KEY_SIZE) +#define HCIC_PARAM_SIZE_LTK_REQ_NEG_REPLY 2 +#define HCIC_BLE_CHNL_MAP_SIZE 5 +#define HCIC_PARAM_SIZE_BLE_WRITE_ADV_DATA 31 + +#define HCIC_PARAM_SIZE_BLE_ADD_DEV_RESOLVING_LIST (7 + HCIC_BLE_IRK_SIZE * 2) +#define HCIC_PARAM_SIZE_BLE_RM_DEV_RESOLVING_LIST 7 +#define HCIC_PARAM_SIZE_BLE_CLEAR_RESOLVING_LIST 0 +#define HCIC_PARAM_SIZE_BLE_READ_RESOLVING_LIST_SIZE 0 +#define HCIC_PARAM_SIZE_BLE_READ_RESOLVABLE_ADDR_PEER 7 +#define HCIC_PARAM_SIZE_BLE_READ_RESOLVABLE_ADDR_LOCAL 7 +#define HCIC_PARAM_SIZE_BLE_SET_ADDR_RESOLUTION_ENABLE 1 +#define HCIC_PARAM_SIZE_BLE_SET_RAND_PRIV_ADDR_TIMOUT 2 +#define HCIC_PARAM_SIZE_BLE_SET_DATA_LENGTH 6 +#define HCIC_PARAM_SIZE_BLE_WRITE_EXTENDED_SCAN_PARAM 11 +#define HCIC_PARAM_SIZE_BLE_UPDATE_ADV_FLOW_CONTROL 2 +#define HCIC_PARAM_SIZE_BLE_CLEAR_ADV 0 +#if (BLE_50_FEATURE_SUPPORT == TRUE) +#define HCIC_PARAM_SIZE_BLE_READ_PHY 2 +#define HCIC_PARAM_SIZE_BLE_SET_DEF_PHY 3 +#define HCIC_PARAM_SIZE_BLE_SET_PHY 7 +#define HCIC_PARAM_SIZE_ENH_RX_TEST 3 +#define HCIC_PARAM_SIZE_ENH_TX_TEST 4 +#define HCIC_PARAM_SIZE_EXT_RAND_ADDR 7 +#define HCIC_PARAM_SIZE_EXT_ADV_SET_PARAMS 25 +#define HCIC_PARAM_SIZE_EXT_ADV_WRITE_DATA 251 +#define HCIC_PARAM_SIZE_READ_MAX_ADV_SIZE 0 +#define HCIC_PARAM_SIZE_NUM_SUPPORT_ADV_SET 0 +#define HCIC_PARAM_SIZE_REMOVE_ADV_SET 1 +#define HCIC_PARAM_SIZE_CLEAR_ADV_SET 0 +#define HCIC_PARAM_SIZE_SET_PERIODIC_ADV_PARAMS 7 +#define HCIC_PARAM_SIZE_WRITE_PERIODIC_ADV_DATA 252 +#define HCIC_PARAM_SIZE_PERIODIC_ADV_ENABLE 2 +#define HCIC_PARAM_SIZE_SET_EXT_SCAN_PARAMS 3 +#define HCIC_PARAM_SIZE_EXT_SCAN_ENABLE 6 +#define HCIC_PARAM_SIZE_EXT_CONN_CREATE_BASE 10 +#define HCIC_PARAM_SIZE_PERIODIC_ADV_CREATE_SYNC 12 +#define HCIC_PARAM_SIZE_PERIODIC_ADV_CREATE_SYNC_CANCEL 0 +#define HCIC_PARAM_SIZE_PERIODIC_ADV_TERM_SYNC 2 +#define HCIC_PARAM_SIZE_ADD_DEV_TO_PERIODIC_ADV_LIST 8 +#define HCIC_PARAM_SIZE_RM_DEV_FROM_PERIODIC_ADV_LIST 8 +#define HCIC_PARAM_SIZE_CLEAR_PERIODIC_ADV_LIST 0 +#define HCIC_PARAM_SIZE_READ_PERIODIC_ADV_LIST 0 +#define HCIC_PARAM_SIZE_READ_TRANS_POWER 0 +#define HCIC_PARAM_SIZE_READ_RF_PATH_COMPENSATION 0 +#define HCIC_PARAM_SIZE_WRITE_RF_PATH_COMPENSATION 4 + +BlE_SYNC *btsnd_hcic_ble_get_sync_info(void); +void btsnd_hcic_ble_sync_sem_init(void); +void btsnd_hcic_ble_sync_sem_deinit(void); + +uint8_t btsnd_hcic_ble_get_status(void); + +void btsnd_hci_ble_set_status(UINT8 hci_status); +#endif // #if (BLE_50_FEATURE_SUPPORT == TRUE) + +/* ULP HCI command */ +BOOLEAN btsnd_hcic_ble_set_evt_mask (BT_EVENT_MASK event_mask); + +BOOLEAN btsnd_hcic_ble_read_buffer_size (void); + +BOOLEAN btsnd_hcic_ble_read_local_spt_feat (void); + +BOOLEAN btsnd_hcic_ble_set_local_used_feat (UINT8 feat_set[8]); + +BOOLEAN btsnd_hcic_ble_set_random_addr (BD_ADDR random_addr); + +BOOLEAN btsnd_hcic_ble_write_adv_params (UINT16 adv_int_min, UINT16 adv_int_max, + UINT8 adv_type, UINT8 addr_type_own, + UINT8 addr_type_dir, BD_ADDR direct_bda, + UINT8 channel_map, UINT8 adv_filter_policy); + +BOOLEAN btsnd_hcic_ble_read_adv_chnl_tx_power (void); + +BOOLEAN btsnd_hcic_ble_set_adv_data (UINT8 data_len, UINT8 *p_data); + +BOOLEAN btsnd_hcic_ble_set_scan_rsp_data (UINT8 data_len, UINT8 *p_scan_rsp); + +BOOLEAN btsnd_hcic_ble_set_adv_enable (UINT8 adv_enable); + +BOOLEAN btsnd_hcic_ble_set_scan_params (UINT8 scan_type, + UINT16 scan_int, UINT16 scan_win, + UINT8 addr_type, UINT8 scan_filter_policy); + +BOOLEAN btsnd_hcic_ble_set_scan_enable (UINT8 scan_enable, UINT8 duplicate); + +BOOLEAN btsnd_hcic_ble_create_ll_conn (UINT16 scan_int, UINT16 scan_win, + UINT8 init_filter_policy, UINT8 addr_type_peer, BD_ADDR bda_peer, UINT8 addr_type_own, + UINT16 conn_int_min, UINT16 conn_int_max, UINT16 conn_latency, UINT16 conn_timeout, + UINT16 min_ce_len, UINT16 max_ce_len); + +BOOLEAN btsnd_hcic_ble_create_conn_cancel (void); + +BOOLEAN btsnd_hcic_ble_read_white_list_size (void); + +BOOLEAN btsnd_hcic_ble_clear_white_list (void); + +BOOLEAN btsnd_hcic_ble_add_white_list (UINT8 addr_type, BD_ADDR bda); + +BOOLEAN btsnd_hcic_ble_remove_from_white_list (UINT8 addr_type, BD_ADDR bda); + +BOOLEAN btsnd_hcic_ble_upd_ll_conn_params (UINT16 handle, UINT16 conn_int_min, UINT16 conn_int_max, + UINT16 conn_latency, UINT16 conn_timeout, UINT16 min_len, UINT16 max_len); + +BOOLEAN btsnd_hcic_ble_set_host_chnl_class (UINT8 chnl_map[HCIC_BLE_CHNL_MAP_SIZE]); + +BOOLEAN btsnd_hcic_ble_read_chnl_map (UINT16 handle); + +BOOLEAN btsnd_hcic_ble_read_remote_feat ( UINT16 handle); + +BOOLEAN btsnd_hcic_ble_encrypt (UINT8 *key, UINT8 key_len, UINT8 *plain_text, UINT8 pt_len, void *p_cmd_cplt_cback); + +BOOLEAN btsnd_hcic_ble_rand (void *p_cmd_cplt_cback); + +BOOLEAN btsnd_hcic_ble_start_enc ( UINT16 handle, + UINT8 rand[HCIC_BLE_RAND_DI_SIZE], + UINT16 ediv, UINT8 ltk[HCIC_BLE_ENCRYT_KEY_SIZE]); + +BOOLEAN btsnd_hcic_ble_ltk_req_reply (UINT16 handle, UINT8 ltk[HCIC_BLE_ENCRYT_KEY_SIZE]); + +BOOLEAN btsnd_hcic_ble_ltk_req_neg_reply (UINT16 handle); + +BOOLEAN btsnd_hcic_ble_read_supported_states (void); + +BOOLEAN btsnd_hcic_ble_write_host_supported (UINT8 le_host_spt, UINT8 simul_le_host_spt); + +BOOLEAN btsnd_hcic_ble_read_host_supported (void); + +BOOLEAN btsnd_hcic_ble_receiver_test(UINT8 rx_freq); + +BOOLEAN btsnd_hcic_ble_transmitter_test(UINT8 tx_freq, UINT8 test_data_len, + UINT8 payload); +BOOLEAN btsnd_hcic_ble_test_end(void); + +#if (defined BLE_LLT_INCLUDED) && (BLE_LLT_INCLUDED == TRUE) + +#define HCIC_PARAM_SIZE_BLE_RC_PARAM_REQ_REPLY 14 +BOOLEAN btsnd_hcic_ble_rc_param_req_reply(UINT16 handle, + UINT16 conn_int_min, UINT16 conn_int_max, + UINT16 conn_latency, UINT16 conn_timeout, + UINT16 min_ce_len, UINT16 max_ce_len); + +#define HCIC_PARAM_SIZE_BLE_RC_PARAM_REQ_NEG_REPLY 3 +BOOLEAN btsnd_hcic_ble_rc_param_req_neg_reply(UINT16 handle, UINT8 reason); + +#endif /* BLE_LLT_INCLUDED */ + +BOOLEAN btsnd_hcic_ble_set_data_length(UINT16 conn_handle, UINT16 tx_octets, + UINT16 tx_time); + +BOOLEAN btsnd_hcic_ble_add_device_resolving_list (UINT8 addr_type_peer, + BD_ADDR bda_peer, + UINT8 irk_peer[HCIC_BLE_IRK_SIZE], + UINT8 irk_local[HCIC_BLE_IRK_SIZE]); + +BOOLEAN btsnd_hcic_ble_rm_device_resolving_list (UINT8 addr_type_peer, + BD_ADDR bda_peer); + +BOOLEAN btsnd_hcic_ble_clear_resolving_list (void); + +BOOLEAN btsnd_hcic_ble_read_resolvable_addr_peer (UINT8 addr_type_peer, + BD_ADDR bda_peer); + +BOOLEAN btsnd_hcic_ble_read_resolvable_addr_local (UINT8 addr_type_peer, + BD_ADDR bda_peer); + +BOOLEAN btsnd_hcic_ble_set_addr_resolution_enable (UINT8 addr_resolution_enable); + +BOOLEAN btsnd_hcic_ble_set_rand_priv_addr_timeout (UINT16 rpa_timout); + +BOOLEAN btsnd_hcic_ble_clear_adv(void); + +#endif /* BLE_INCLUDED */ +#if (BLE_50_FEATURE_SUPPORT == TRUE) +typedef struct { + UINT8 scan_type; + UINT16 scan_interval; + UINT16 scan_window; +} tHCI_EXT_SCAN_PARAMS; + +typedef struct { + UINT16 scan_interval; + UINT16 scan_window; + UINT16 conn_interval_min; + UINT16 conn_interval_max; + UINT16 conn_latency; + UINT16 sup_timeout; + UINT16 min_ce_len; + UINT16 max_ce_len; +} tHCI_ExtConnParams; + +typedef struct { + UINT8 filter_policy; + UINT8 own_addr_type; + UINT8 peer_addr_type; + BD_ADDR peer_addr; + UINT8 init_phy_mask; + tHCI_ExtConnParams params[3]; +} tHCI_CreatExtConn; +#endif // #if (BLE_50_FEATURE_SUPPORT == TRUE) +BOOLEAN btsnd_hcic_read_authenticated_payload_tout(UINT16 handle); + +BOOLEAN btsnd_hcic_write_authenticated_payload_tout(UINT16 handle, + UINT16 timeout); + +BOOLEAN btsnd_hcic_ble_update_adv_report_flow_control (UINT16 num, BT_HDR *static_buf); +#if (BLE_50_FEATURE_SUPPORT == TRUE) +BOOLEAN btsnd_hcic_ble_read_phy(UINT16 conn_handle); + +UINT8 btsnd_hcic_ble_set_prefered_default_phy(UINT8 all_phys, + UINT8 tx_phys, + UINT8 rx_phys); +BOOLEAN btsnd_hcic_ble_set_phy(UINT16 conn_handle, + UINT8 all_phys, UINT8 tx_phys, + UINT8 rx_phys, UINT16 phy_options); + +UINT8 btsnd_hcic_ble_enhand_rx_test(UINT8 rx_channel, UINT8 phy, + UINT8 modulation_idx); + +UINT8 btsnd_hcic_ble_enhand_tx_test(UINT8 tx_channel, UINT8 len, + UINT8 packect, + UINT8 phy); + +UINT8 btsnd_hcic_ble_set_extend_rand_address(UINT8 adv_handle, BD_ADDR rand_addr); + +UINT8 btsnd_hcic_ble_set_ext_adv_params(UINT8 adv_handle, UINT16 properties, UINT32 interval_min, + UINT32 interval_max, UINT8 channel_map, UINT8 own_addr_type, + UINT8 peer_addr_type, BD_ADDR peer_addr, + UINT8 adv_filter_policy, INT8 adv_tx_power, + UINT8 primary_adv_phy, UINT8 secondary_adv_max_skip, + UINT8 secondary_adv_phy, + UINT8 adv_sid, UINT8 scan_req_ntf_enable); + +UINT8 btsnd_hcic_ble_set_ext_adv_data(UINT8 adv_handle, + UINT8 operation, UINT8 fragment_prefrence, + UINT8 data_len, UINT8 *p_data); + +UINT8 btsnd_hcic_ble_set_ext_adv_scan_rsp_data(UINT8 adv_handle, + UINT8 operation, UINT8 fragment_prefrence, + UINT8 data_len, UINT8 *p_data); + +UINT8 btsnd_hcic_ble_ext_adv_enable(UINT8 enable, UINT8 num_of_sets, UINT8 *adv_handle, + UINT16 *duration, UINT8 *max_adv_evt); + +UINT8 btsnd_hcic_ble_read_max_adv_len(void); + +UINT8 btsnd_hcic_ble_read_num_support_adv_set(void); + +UINT8 btsnd_hcic_ble_remove_adv_set(UINT8 adv_handle); + +UINT8 btsnd_hcic_ble_clear_adv_set(void); + +UINT8 btsnd_hcic_ble_set_periodic_adv_params(UINT8 adv_handle, + UINT16 interval_min, + UINT16 interval_max, + UINT8 propertics); + +UINT8 btsnd_hcic_ble_set_periodic_adv_data(UINT8 adv_handle, + UINT8 operation, + UINT8 len, + UINT8 *p_data); + +UINT8 btsnd_hcic_ble_periodic_adv_enable(UINT8 enable, UINT8 adv_handle); + +UINT8 btsnd_hcic_ble_set_ext_scan_params(UINT8 own_addr_type, UINT8 filter_policy, + UINT8 phy_mask, UINT8 phy_count, + tHCI_EXT_SCAN_PARAMS *params); + +UINT8 btsnd_hcic_ble_ext_scan_enable(UINT8 enable, UINT8 filter_dups, + UINT16 duration, UINT16 period); + + +BOOLEAN btsnd_hcic_ble_create_ext_conn(tHCI_CreatExtConn *p_conn); + +BOOLEAN btsnd_hcic_ble_periodic_adv_create_sync(UINT8 filter_policy, UINT8 adv_sid, + UINT8 adv_addr_type, BD_ADDR adv_addr, + UINT16 sync_timeout, UINT8 unused); + +UINT8 btsnd_hcic_ble_periodic_adv_create_sync_cancel(void); + + +UINT8 btsnd_hcic_ble_periodic_adv_term_sync(UINT16 sync_handle); + +UINT8 btsnd_hcic_ble_add_dev_to_periodic_adv_list(UINT8 adv_addr_type, BD_ADDR adv_addr, + UINT8 adv_sid); +UINT8 btsnd_hcic_ble_rm_dev_from_periodic_adv_list(UINT8 adv_addr_type, BD_ADDR adv_addr, + UINT8 adv_sid); + +UINT8 btsnd_hcic_ble_clear_periodic_adv_list(void); + +UINT8 btsnd_hcic_ble_read_periodic_adv_list_size(void); + +UINT8 btsnd_hcic_ble_read_trans_power(void); + +UINT8 btsnd_hcic_ble_read_rf_path_compensation(void); + +UINT8 btsnd_hcic_ble_write_rf_path_compensation(UINT16 rf_tx_path, UINT16 rf_rx_path); + +#endif // #if (BLE_50_FEATURE_SUPPORT == TRUE) + +#define HCIC_PARAM_SIZE_WRITE_AUTHENT_PAYLOAD_TOUT 4 + +#define HCI__WRITE_AUTHENT_PAYLOAD_TOUT_HANDLE_OFF 0 +#define HCI__WRITE_AUTHENT_PAYLOAD_TOUT_TOUT_OFF 2 + +#if (BLE_FEAT_PERIODIC_ADV_SYNC_TRANSFER == TRUE) +#define HCIC_PARAM_SIZE_PERIODIC_ADV_RECV_ENABLE 3 +#define HCI_PERIODIC_ADV_RECV_REPORT_EN 1 +#define HCI_PERIODIC_ADV_RECV_DUP_FILTER_EN 2 +#define HCIC_PARAM_SIZE_PERIODIC_ADV_SYNC_TRANS 6 +#define HCIC_PARAM_SIZE_PERIODIC_ADV_SET_INFO_TRANS 5 +#define HCIC_PARAM_SIZE_SET_PAST_PARAMS 8 +#define HCIC_PARAM_SIZE_SET_DEFAULT_PAST_PARAMS 6 + +UINT8 btsnd_hcic_ble_set_periodic_adv_recv_enable(UINT16 sync_handle, UINT8 enable); + +BOOLEAN btsnd_hcic_ble_periodic_adv_sync_trans(UINT16 conn_handle, UINT16 service_data, UINT16 sync_handle); + +BOOLEAN btsnd_hcic_ble_periodic_adv_set_info_trans(UINT16 conn_handle, UINT16 service_data, UINT8 adv_handle); + +BOOLEAN btsnd_hcic_ble_set_periodic_adv_sync_trans_params(UINT16 conn_handle, UINT8 mode, UINT16 skip, UINT16 sync_timeout, UINT8 cte_type); + +UINT8 btsnd_hcic_ble_set_default_periodic_adv_sync_trans_params(UINT8 mode, UINT16 skip, UINT16 sync_timeout, UINT8 cte_type); +#endif // #if (BLE_FEAT_PERIODIC_ADV_SYNC_TRANSFER == TRUE) + +#endif diff --git a/lib/bt/host/bluedroid/stack/include/stack/hidd_api.h b/lib/bt/host/bluedroid/stack/include/stack/hidd_api.h new file mode 100644 index 00000000..3e99db9d --- /dev/null +++ b/lib/bt/host/bluedroid/stack/include/stack/hidd_api.h @@ -0,0 +1,273 @@ +/****************************************************************************** + * + * Copyright (C) 2016 The Android Open Source Project + * Copyright (C) 2002-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ +#ifndef HIDD_API_H +#define HIDD_API_H +#include "hiddefs.h" +#include "sdp_api.h" + +#if (HID_DEV_INCLUDED == TRUE) +/***************************************************************************** + * Type Definitions + ****************************************************************************/ +enum { + HID_CHANNEL_INTR, + HID_CHANNEL_CTRL +}; +/* + HID_DHOST_EVT_OPEN - connected to host device (CTRL and INTR), data = n/a + HID_DHOST_EVT_CLOSE - disconnected from host device, data=reason + HID_DHOST_EVT_GET_REPORT - got GET_REPORT from host + HID_DHOST_EVT_SET_REPORT - got SET_REPORT from host + HID_DHOST_EVT_SET_PROTOCOL - got SET_PROTOCOL from host +*/ + +enum { + HID_DHOST_EVT_OPEN, + HID_DHOST_EVT_CLOSE, + HID_DHOST_EVT_GET_REPORT, + HID_DHOST_EVT_SET_REPORT, + HID_DHOST_EVT_SET_PROTOCOL, + HID_DHOST_EVT_INTR_DATA, + HID_DHOST_EVT_VC_UNPLUG, + HID_DHOST_EVT_SUSPEND, + HID_DHOST_EVT_EXIT_SUSPEND, +}; + +typedef void (tHID_DEV_HOST_CALLBACK)(BD_ADDR bd_addr, uint8_t event, uint32_t data, BT_HDR* p_buf); + +#ifdef __cplusplus +extern "C" { +#endif +/***************************************************************************** + * External Function Declarations + ****************************************************************************/ + +/******************************************************************************* + * + * Function HID_DevInit + * + * Description Initializes control block + * + * Returns tHID_STATUS + * + ******************************************************************************/ +extern tHID_STATUS HID_DevInit(void); + +/******************************************************************************* + * + * Function HID_DevInit + * + * Description Deinitializes control block + * + * Returns void + * + ******************************************************************************/ +extern void HID_DevDeinit(void); + +/******************************************************************************* + * + * Function HID_DevRegister + * + * Description Registers HID device with lower layers + * + * Returns tHID_STATUS + * + ******************************************************************************/ +extern tHID_STATUS HID_DevRegister(tHID_DEV_HOST_CALLBACK* host_cback); + +/******************************************************************************* + * + * Function HID_DevDeregister + * + * Description Deregisters HID device with lower layers + * + * Returns tHID_STATUS + * + ******************************************************************************/ +extern tHID_STATUS HID_DevDeregister(void); + +/******************************************************************************* + * + * Function HID_DevSetSecurityLevel + * + * Description Sets security level for HID device connections + * + * Returns tHID_STATUS + * + ******************************************************************************/ +extern tHID_STATUS HID_DevSetSecurityLevel(uint8_t sec_lvl); + +/******************************************************************************* + * + * Function HID_DevAddRecord + * + * Description Creates SDP record for HID device + * + * Returns tHID_STATUS + * + ******************************************************************************/ +extern tHID_STATUS HID_DevAddRecord(uint32_t handle, char* p_name, + char* p_description, char* p_provider, + uint16_t subclass, uint16_t desc_len, + uint8_t* p_desc_data); + +/******************************************************************************* + * + * Function HID_DevSendReport + * + * Description Sends report + * + * Returns tHID_STATUS + * + ******************************************************************************/ +extern tHID_STATUS HID_DevSendReport(uint8_t channel, uint8_t type, uint8_t id, + uint16_t len, uint8_t* p_data); + +/******************************************************************************* + * + * Function HID_DevVirtualCableUnplug + * + * Description Sends Virtual Cable Unplug + * + * Returns tHID_STATUS + * + ******************************************************************************/ +extern tHID_STATUS HID_DevVirtualCableUnplug(void); + +/******************************************************************************* + * + * Function HID_DevPlugDevice + * + * Description Establishes virtual cable to given host + * + * Returns tHID_STATUS + * + ******************************************************************************/ +extern tHID_STATUS HID_DevPlugDevice(BD_ADDR addr); + +/******************************************************************************* + * + * Function HID_DevUnplugDevice + * + * Description Unplugs virtual cable from given host + * + * Returns tHID_STATUS + * + ******************************************************************************/ +extern tHID_STATUS HID_DevUnplugDevice(BD_ADDR addr); + +/******************************************************************************* + * + * Function HID_DevConnect + * + * Description Connects to device + * + * Returns tHID_STATUS + * + ******************************************************************************/ +extern tHID_STATUS HID_DevConnect(void); + +/******************************************************************************* + * + * Function HID_DevDisconnect + * + * Description Disconnects from device + * + * Returns tHID_STATUS + * + ******************************************************************************/ +extern tHID_STATUS HID_DevDisconnect(void); + +/******************************************************************************* + * + * Function HID_DevSetIncomingPolicy + * + * Description Sets policy for incoming connections (allowed/disallowed) + * + * Returns tHID_STATUS + * + ******************************************************************************/ +extern tHID_STATUS HID_DevSetIncomingPolicy(bool allow); + +/******************************************************************************* + * + * Function HID_DevReportError + * + * Description Reports error for Set Report via HANDSHAKE + * + * Returns tHID_STATUS + * + ******************************************************************************/ +extern tHID_STATUS HID_DevReportError(uint8_t error); + +/******************************************************************************* + * + * Function HID_DevGetDevice + * + * Description Returns the BD Address of virtually cabled device + * + * Returns tHID_STATUS + * + ******************************************************************************/ +extern tHID_STATUS HID_DevGetDevice(BD_ADDR* addr); + +/******************************************************************************* + * + * Function HID_DevSetIncomingQos + * + * Description Sets Incoming QoS values for Interrupt L2CAP Channel + * + * Returns tHID_STATUS + * + ******************************************************************************/ +extern tHID_STATUS HID_DevSetIncomingQos( + uint8_t service_type, uint32_t token_rate, uint32_t token_bucket_size, + uint32_t peak_bandwidth, uint32_t latency, uint32_t delay_variation); + +/******************************************************************************* + * + * Function HID_DevSetOutgoingQos + * + * Description Sets Outgoing QoS values for Interrupt L2CAP Channel + * + * Returns tHID_STATUS + * + ******************************************************************************/ +extern tHID_STATUS HID_DevSetOutgoingQos( + uint8_t service_type, uint32_t token_rate, uint32_t token_bucket_size, + uint32_t peak_bandwidth, uint32_t latency, uint32_t delay_variation); + +/******************************************************************************* + * + * Function HID_DevSetTraceLevel + * + * Description This function sets the trace level for HID Dev. If called + * with a value of 0xFF, it simply reads the current trace level. + * + * Returns the new (current) trace level + * + ******************************************************************************/ +extern uint8_t HID_DevSetTraceLevel(uint8_t new_level); + +#ifdef __cplusplus +} +#endif + +#endif +#endif /* HIDD_API_H */ diff --git a/lib/bt/host/bluedroid/stack/include/stack/hiddefs.h b/lib/bt/host/bluedroid/stack/include/stack/hiddefs.h new file mode 100644 index 00000000..71dd73e0 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/include/stack/hiddefs.h @@ -0,0 +1,163 @@ +/****************************************************************************** + * + * Copyright (C) 2002-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains HID protocol definitions + * + ******************************************************************************/ + +#ifndef HIDDEFS_H +#define HIDDEFS_H +#include "common/bt_target.h" + +#if (HID_HOST_INCLUDED == TRUE || HID_DEV_INCLUDED == TRUE) + +#if (SDP_INCLUDED == TRUE) +#include "stack/sdp_api.h" +#endif ///SDP_INCLUDED == TRUE +/* +** tHID_STATUS: HID result codes, returned by HID and device and host functions. +*/ +enum { + HID_SUCCESS, + HID_ERR_NOT_REGISTERED, + HID_ERR_ALREADY_REGISTERED, + HID_ERR_NO_RESOURCES, + HID_ERR_NO_CONNECTION, + HID_ERR_INVALID_PARAM, + HID_ERR_UNSUPPORTED, + HID_ERR_UNKNOWN_COMMAND, + HID_ERR_CONGESTED, + HID_ERR_CONN_IN_PROCESS, + HID_ERR_ALREADY_CONN, + HID_ERR_DISCONNECTING, + HID_ERR_SET_CONNABLE_FAIL, + /* Device specific error codes */ + HID_ERR_HOST_UNKNOWN, + HID_ERR_L2CAP_FAILED, + HID_ERR_AUTH_FAILED, + HID_ERR_SDP_BUSY, + HID_ERR_GATT, + + HID_ERR_INVALID = 0xFF +}; + +typedef UINT8 tHID_STATUS; + +#define HID_L2CAP_CONN_FAIL (0x0100) /* Connection Attempt was made but failed */ +#define HID_L2CAP_REQ_FAIL (0x0200) /* L2CAP_ConnectReq API failed */ +#define HID_L2CAP_CFG_FAIL (0x0400) /* L2CAP Configuration was rejected by peer */ + + + +/* Define the HID transaction types +*/ +#define HID_TRANS_HANDSHAKE (0) +#define HID_TRANS_CONTROL (1) +#define HID_TRANS_GET_REPORT (4) +#define HID_TRANS_SET_REPORT (5) +#define HID_TRANS_GET_PROTOCOL (6) +#define HID_TRANS_SET_PROTOCOL (7) +#define HID_TRANS_GET_IDLE (8) +#define HID_TRANS_SET_IDLE (9) +#define HID_TRANS_DATA (10) +#define HID_TRANS_DATAC (11) + +#define HID_GET_TRANS_FROM_HDR(x) ((x >> 4) & 0x0f) +#define HID_GET_PARAM_FROM_HDR(x) (x & 0x0f) +#define HID_BUILD_HDR(t,p) (UINT8)((t << 4) | (p & 0x0f)) + + +/* Parameters for Handshake +*/ +#define HID_PAR_HANDSHAKE_RSP_SUCCESS (0) +#define HID_PAR_HANDSHAKE_RSP_NOT_READY (1) +#define HID_PAR_HANDSHAKE_RSP_ERR_INVALID_REP_ID (2) +#define HID_PAR_HANDSHAKE_RSP_ERR_UNSUPPORTED_REQ (3) +#define HID_PAR_HANDSHAKE_RSP_ERR_INVALID_PARAM (4) +#define HID_PAR_HANDSHAKE_RSP_ERR_UNKNOWN (14) +#define HID_PAR_HANDSHAKE_RSP_ERR_FATAL (15) + + +/* Parameters for Control +*/ +#define HID_PAR_CONTROL_NOP (0) +#define HID_PAR_CONTROL_HARD_RESET (1) +#define HID_PAR_CONTROL_SOFT_RESET (2) +#define HID_PAR_CONTROL_SUSPEND (3) +#define HID_PAR_CONTROL_EXIT_SUSPEND (4) +#define HID_PAR_CONTROL_VIRTUAL_CABLE_UNPLUG (5) + + +/* Different report types in get, set, data +*/ +#define HID_PAR_REP_TYPE_MASK (0x03) +#define HID_PAR_REP_TYPE_OTHER (0x00) +#define HID_PAR_REP_TYPE_INPUT (0x01) +#define HID_PAR_REP_TYPE_OUTPUT (0x02) +#define HID_PAR_REP_TYPE_FEATURE (0x03) + +/* Parameters for Get Report +*/ + +/* Buffer size in two bytes after Report ID */ +#define HID_PAR_GET_REP_BUFSIZE_FOLLOWS (0x08) + + +/* Parameters for Protocol Type +*/ +#define HID_PAR_PROTOCOL_MASK (0x01) +#define HID_PAR_PROTOCOL_REPORT (0x01) +#define HID_PAR_PROTOCOL_BOOT_MODE (0x00) + +#define HID_PAR_REP_TYPE_MASK (0x03) + +/* Descriptor types in the SDP record +*/ +#define HID_SDP_DESCRIPTOR_REPORT (0x22) +#define HID_SDP_DESCRIPTOR_PHYSICAL (0x23) + +typedef struct desc_info { + UINT16 dl_len; + UINT8 *dsc_list; +} tHID_DEV_DSCP_INFO; + +#define HID_SSR_PARAM_INVALID 0xffff + +typedef struct sdp_info { + char svc_name[HID_MAX_SVC_NAME_LEN]; /*Service Name */ + char svc_descr[HID_MAX_SVC_DESCR_LEN]; /*Service Description*/ + char prov_name[HID_MAX_PROV_NAME_LEN]; /*Provider Name.*/ + UINT16 rel_num; /*Release Number */ + UINT16 hpars_ver; /*HID Parser Version.*/ + UINT16 ssr_max_latency; /* HIDSSRHostMaxLatency value, if HID_SSR_PARAM_INVALID not used*/ + UINT16 ssr_min_tout; /* HIDSSRHostMinTimeout value, if HID_SSR_PARAM_INVALID not used* */ + UINT8 sub_class; /*Device Subclass.*/ + UINT8 ctry_code; /*Country Code.*/ + UINT16 sup_timeout;/* Supervisory Timeout */ + + tHID_DEV_DSCP_INFO dscp_info; /* Descriptor list and Report list to be set in the SDP record. + This parameter is used if HID_DEV_USE_GLB_SDP_REC is set to FALSE.*/ +#if(SDP_INCLUDED == TRUE) + tSDP_DISC_REC *p_sdp_layer_rec; +#endif ///SDP_INCLUDED == TRUE +} tHID_DEV_SDP_INFO; + +#endif ///HID_HOST_INCLUDED == TRUE +#endif diff --git a/lib/bt/host/bluedroid/stack/include/stack/hidh_api.h b/lib/bt/host/bluedroid/stack/include/stack/hidh_api.h new file mode 100644 index 00000000..2ddaf1c5 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/include/stack/hidh_api.h @@ -0,0 +1,258 @@ +/****************************************************************************** + * + * Copyright (C) 2002-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ +#ifndef HIDH_API_H +#define HIDH_API_H + +#include "stack/hiddefs.h" +#include "stack/sdp_api.h" + +#if (HID_HOST_INCLUDED == TRUE) +/***************************************************************************** +** Constants +*****************************************************************************/ + +enum { + HID_SDP_NO_SERV_UUID = (SDP_ILLEGAL_PARAMETER + 1), + HID_SDP_MANDATORY_MISSING +}; + +/* Attributes mask values to be used in HID_HostAddDev API */ +#define HID_VIRTUAL_CABLE 0x0001 +#define HID_NORMALLY_CONNECTABLE 0x0002 +#define HID_RECONN_INIT 0x0004 +#define HID_SDP_DISABLE 0x0008 +#define HID_BATTERY_POWER 0x0010 +#define HID_REMOTE_WAKE 0x0020 +#define HID_SUP_TOUT_AVLBL 0x0040 +#define HID_SSR_MAX_LATENCY 0x0080 +#define HID_SSR_MIN_TOUT 0x0100 + +#define HID_SEC_REQUIRED 0x8000 +#define HID_ATTR_MASK_IGNORE 0 + + +/***************************************************************************** +** Type Definitions +*****************************************************************************/ + +typedef void (tHID_HOST_SDP_CALLBACK) (UINT16 result, UINT16 attr_mask, + tHID_DEV_SDP_INFO *sdp_rec ); + +/* HID-HOST returns the events in the following table to the application via tHID_HOST_DEV_CALLBACK +HID_HDEV_EVT_OPEN Connected to device with Interrupt and Control Channels in OPEN state. + Data = NA +HID_HDEV_EVT_CLOSE Connection with device is closed. Data=reason code. +HID_HDEV_EVT_RETRYING Lost connection is being re-connected. + Data=Retrial number +HID_HDEV_EVT_IN_REPORT Device sent an input report Data=Report Type pdata= pointer to BT_HDR + (GKI buffer having report data.) +HID_HDEV_EVT_HANDSHAKE Device sent SET_REPORT Data=Result-code pdata=NA. +HID_HDEV_EVT_VC_UNPLUG Device sent Virtual Unplug Data=NA. pdata=NA. +*/ + +enum { + HID_HDEV_EVT_OPEN, + HID_HDEV_EVT_CLOSE, + HID_HDEV_EVT_RETRYING, + HID_HDEV_EVT_INTR_DATA, + HID_HDEV_EVT_INTR_DATC, + HID_HDEV_EVT_CTRL_DATA, + HID_HDEV_EVT_CTRL_DATC, + HID_HDEV_EVT_HANDSHAKE, + HID_HDEV_EVT_VC_UNPLUG +}; +typedef void (tHID_HOST_DEV_CALLBACK) (UINT8 dev_handle, + BD_ADDR addr, + UINT8 event, /* Event from HID-DEVICE. */ + UINT32 data, /* Integer data corresponding to the event.*/ + BT_HDR *p_buf ); /* Pointer data corresponding to the event. */ + + +/***************************************************************************** +** External Function Declarations +*****************************************************************************/ +#ifdef __cplusplus +extern "C" +{ +#endif + +/******************************************************************************* +** +** Function HID_HostGetSDPRecord +** +** Description This function reads the device SDP record. +** +** Returns tHID_STATUS +** +*******************************************************************************/ +extern tHID_STATUS HID_HostGetSDPRecord (BD_ADDR addr, + tSDP_DISCOVERY_DB *p_db, + UINT32 db_len, + tHID_HOST_SDP_CALLBACK *sdp_cback ); + +/******************************************************************************* +** +** Function HID_HostRegister +** +** Description This function registers HID-Host with lower layers. +** +** Returns tHID_STATUS +** +*******************************************************************************/ +extern tHID_STATUS HID_HostRegister (tHID_HOST_DEV_CALLBACK *dev_cback); + +/******************************************************************************* +** +** Function HID_HostDeregister +** +** Description This function is called when the host is about power down. +** +** Returns tHID_STATUS +** +*******************************************************************************/ +extern tHID_STATUS HID_HostDeregister(void); + +/******************************************************************************* +** +** Function HID_HostAddDev +** +** Description This is called so HID-host may manage this device. +** +** Returns tHID_STATUS +** +*******************************************************************************/ +extern tHID_STATUS HID_HostAddDev (BD_ADDR addr, UINT16 attr_mask, + UINT8 *handle ); + +/******************************************************************************* +** +** Function HID_HostGetDev +** +** Description This is called so HID-host can find this device. +** +** Returns tHID_STATUS +** +*******************************************************************************/ +extern tHID_STATUS HID_HostGetDev(BD_ADDR addr, UINT8 *handle); + +/******************************************************************************* +** +** Function HID_HostRemoveDev +** +** Description This removes the device from list devices that host has to manage. +** +** Returns tHID_STATUS +** +*******************************************************************************/ +extern tHID_STATUS HID_HostRemoveDev (UINT8 dev_handle ); + +/******************************************************************************* +** +** Function HID_HostOpenDev +** +** Description This function is called when the user wants to initiate a +** connection attempt to a device. +** +** Returns void +** +*******************************************************************************/ +extern tHID_STATUS HID_HostOpenDev (UINT8 dev_handle ); + +/******************************************************************************* +** +** Function HID_HostWriteDev +** +** Description This function is called when the host has a report to send. +** +** Returns void +** +*******************************************************************************/ +extern tHID_STATUS HID_HostWriteDev(UINT8 dev_handle, UINT8 t_type, + UINT8 param, UINT16 data, + UINT8 report_id, BT_HDR *pbuf); + +/******************************************************************************* +** +** Function HID_HostCloseDev +** +** Description This function disconnects the device. +** +** Returns void +** +*******************************************************************************/ +extern tHID_STATUS HID_HostCloseDev(UINT8 dev_handle ); + +/******************************************************************************* +** Function HID_HostInit +** +** Description This function initializes the control block and trace variable +** +** Returns tHID_STATUS +*******************************************************************************/ +extern tHID_STATUS HID_HostInit(void); + +/******************************************************************************* +** Function HID_HostDeinit +** +** Description This function deinitializes the control block +** +** Returns void +*******************************************************************************/ +extern void HID_HostDeinit(void); + +/******************************************************************************* +** Function HID_HostSetSecurityLevel +** +** Description This function sets the security level for the devices which +** are marked by application as requiring security +** +** Returns tHID_STATUS +*******************************************************************************/ +extern tHID_STATUS HID_HostSetSecurityLevel( char serv_name[], UINT8 sec_lvl ); + +/******************************************************************************* +** +** Function hid_known_hid_device +** +** Description This function checks if this device is of type HID Device +** +** Returns TRUE if device exists else FALSE +** +*******************************************************************************/ +BOOLEAN hid_known_hid_device (BD_ADDR bd_addr); + + +/******************************************************************************* +** +** Function HID_HostSetTraceLevel +** +** Description This function sets the trace level for HID Host. If called with +** a value of 0xFF, it simply reads the current trace level. +** +** Returns the new (current) trace level +** +*******************************************************************************/ +extern UINT8 HID_HostSetTraceLevel (UINT8 new_level); + +#ifdef __cplusplus +} +#endif + +#endif ///HID_HOST_INCLUDED == TRUE + +#endif /* HIDH_API_H */ diff --git a/lib/bt/host/bluedroid/stack/include/stack/l2c_api.h b/lib/bt/host/bluedroid/stack/include/stack/l2c_api.h new file mode 100644 index 00000000..b985b641 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/include/stack/l2c_api.h @@ -0,0 +1,1270 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * this file contains the L2CAP API definitions + * + ******************************************************************************/ +#ifndef L2C_API_H +#define L2C_API_H + +#include + +#include "common/bt_target.h" +#include "stack/l2cdefs.h" +#include "stack/hcidefs.h" +#include "osi/fixed_queue.h" + +/***************************************************************************** +** Constants +*****************************************************************************/ + +/* Define the minimum offset that L2CAP needs in a buffer. This is made up of +** HCI type(1), len(2), handle(2), L2CAP len(2) and CID(2) => 9 +*/ +#define L2CAP_MIN_OFFSET 13 /* plus control(2), SDU length(2) */ +#define L2CAP_FCS_LEN 2 /* FCS 0 or 2 bytes */ + +/* Minimum offset for broadcast needs another two bytes for the PSM */ +#define L2CAP_BCST_MIN_OFFSET 11 + +/* ping result codes */ +#define L2CAP_PING_RESULT_OK 0 /* Ping reply received OK */ +#define L2CAP_PING_RESULT_NO_LINK 1 /* Link could not be setup */ +#define L2CAP_PING_RESULT_NO_RESP 2 /* Remote L2CAP did not reply */ + +/* result code for L2CA_DataWrite() */ +#define L2CAP_DW_FAILED FALSE +#define L2CAP_DW_SUCCESS TRUE +#define L2CAP_DW_CONGESTED 2 + +/* Values for priority parameter to L2CA_SetAclPriority */ +#define L2CAP_PRIORITY_NORMAL 0 +#define L2CAP_PRIORITY_HIGH 1 + +/* Values for priority parameter to L2CA_SetTxPriority */ +#define L2CAP_CHNL_PRIORITY_HIGH 0 +#define L2CAP_CHNL_PRIORITY_MEDIUM 1 +#define L2CAP_CHNL_PRIORITY_LOW 2 + +typedef UINT8 tL2CAP_CHNL_PRIORITY; + +/* Values for Tx/Rx data rate parameter to L2CA_SetChnlDataRate */ +#define L2CAP_CHNL_DATA_RATE_HIGH 3 +#define L2CAP_CHNL_DATA_RATE_MEDIUM 2 +#define L2CAP_CHNL_DATA_RATE_LOW 1 +#define L2CAP_CHNL_DATA_RATE_NO_TRAFFIC 0 + +typedef UINT8 tL2CAP_CHNL_DATA_RATE; + +/* Data Packet Flags (bits 2-15 are reserved) */ +/* layer specific 14-15 bits are used for FCR SAR */ +#define L2CAP_FLUSHABLE_MASK 0x0003 +#define L2CAP_FLUSHABLE_CH_BASED 0x0000 +#define L2CAP_FLUSHABLE_PKT 0x0001 +#define L2CAP_NON_FLUSHABLE_PKT 0x0002 + + +/* L2CA_FlushChannel num_to_flush definitions */ +#define L2CAP_FLUSH_CHANS_ALL 0xffff +#define L2CAP_FLUSH_CHANS_GET 0x0000 + + +/* special CID for Multi-AV for reporting congestion */ +#define L2CAP_MULTI_AV_CID 0 + +/* length of the HCI header block */ +/* HCI header(4) + SNK count(1) + FCR bits(1) + AV data length(2) */ +#define L2CAP_MULTI_AV_HCI_HDR_LEN 8 + +/* length of padding for 4 bytes align */ +#define L2CAP_MULTI_AV_PADDING_LEN 2 + +/* length of the HCI header block with padding for FCR */ +/* HCI header(4) + SNK count(1) + FCR bits(1) + AV data length(2) + padding(2) */ +#define L2CAP_MULTI_AV_HCI_HDR_LEN_WITH_PADDING 10 + +/* length of the L2CAP header block */ +/* HCI header(4) + L2CAP header(4) + padding(4) or control word(2) + FCS(2) */ +#define L2CAP_MULTI_AV_L2C_HDR_LEN 12 + +/* definition used for L2CA_SetDesireRole */ +#define L2CAP_ROLE_SLAVE HCI_ROLE_SLAVE +#define L2CAP_ROLE_MASTER HCI_ROLE_MASTER +#define L2CAP_ROLE_ALLOW_SWITCH 0x80 /* set this bit to allow switch at create conn */ +#define L2CAP_ROLE_DISALLOW_SWITCH 0x40 /* set this bit to disallow switch at create conn */ +#define L2CAP_ROLE_CHECK_SWITCH 0xC0 + + +/* Values for 'allowed_modes' field passed in structure tL2CAP_ERTM_INFO +*/ +#define L2CAP_FCR_CHAN_OPT_BASIC (1 << L2CAP_FCR_BASIC_MODE) +#define L2CAP_FCR_CHAN_OPT_ERTM (1 << L2CAP_FCR_ERTM_MODE) +#define L2CAP_FCR_CHAN_OPT_STREAM (1 << L2CAP_FCR_STREAM_MODE) + +#define L2CAP_FCR_CHAN_OPT_ALL_MASK (L2CAP_FCR_CHAN_OPT_BASIC | L2CAP_FCR_CHAN_OPT_ERTM | L2CAP_FCR_CHAN_OPT_STREAM) + +/* Validity check for PSM. PSM values must be odd. Also, all PSM values must +** be assigned such that the least significant bit of the most sigificant +** octet equals zero. +*/ +#define L2C_INVALID_PSM(psm) (((psm) & 0x0101) != 0x0001) +#define L2C_IS_VALID_PSM(psm) (((psm) & 0x0101) == 0x0001) +#define L2C_IS_VALID_LE_PSM(psm) (((psm) > 0x0000) && ((psm) < 0x0100)) + + +/***************************************************************************** +** Type Definitions +*****************************************************************************/ + +typedef struct { +#define L2CAP_FCR_BASIC_MODE 0x00 +#define L2CAP_FCR_ERTM_MODE 0x03 +#define L2CAP_FCR_STREAM_MODE 0x04 + + UINT8 mode; + + UINT8 tx_win_sz; + UINT8 max_transmit; + UINT16 rtrans_tout; + UINT16 mon_tout; + UINT16 mps; +} tL2CAP_FCR_OPTS; + +/* Define a structure to hold the configuration parameters. Since the +** parameters are optional, for each parameter there is a boolean to +** use to signify its presence or absence. +*/ +typedef struct { + UINT16 result; /* Only used in confirm messages */ + BOOLEAN mtu_present; + UINT16 mtu; + BOOLEAN qos_present; + FLOW_SPEC qos; + BOOLEAN flush_to_present; + UINT16 flush_to; + BOOLEAN fcr_present; + tL2CAP_FCR_OPTS fcr; + BOOLEAN fcs_present; /* Optionally bypasses FCS checks */ + UINT8 fcs; /* '0' if desire is to bypass FCS, otherwise '1' */ + BOOLEAN ext_flow_spec_present; + tHCI_EXT_FLOW_SPEC ext_flow_spec; + UINT16 flags; /* bit 0: 0-no continuation, 1-continuation */ +} tL2CAP_CFG_INFO; + +/* Define a structure to hold the configuration parameter for LE L2CAP connection +** oriented channels. +*/ +typedef struct +{ + UINT16 mtu; + UINT16 mps; + UINT16 credits; +} tL2CAP_LE_CFG_INFO; + + +/* L2CAP channel configured field bitmap */ +#define L2CAP_CH_CFG_MASK_MTU 0x0001 +#define L2CAP_CH_CFG_MASK_QOS 0x0002 +#define L2CAP_CH_CFG_MASK_FLUSH_TO 0x0004 +#define L2CAP_CH_CFG_MASK_FCR 0x0008 +#define L2CAP_CH_CFG_MASK_FCS 0x0010 +#define L2CAP_CH_CFG_MASK_EXT_FLOW_SPEC 0x0020 + +typedef UINT16 tL2CAP_CH_CFG_BITS; + +/********************************* +** Callback Functions Prototypes +**********************************/ + +/* Connection indication callback prototype. Parameters are +** BD Address of remote +** Local CID assigned to the connection +** PSM that the remote wants to connect to +** Identifier that the remote sent +*/ +typedef void (tL2CA_CONNECT_IND_CB) (BD_ADDR, UINT16, UINT16, UINT8); + + +/* Connection confirmation callback prototype. Parameters are +** Local CID +** Result - 0 = connected, non-zero means failure reason +*/ +typedef void (tL2CA_CONNECT_CFM_CB) (UINT16, UINT16); + + +/* Connection pending callback prototype. Parameters are +** Local CID +*/ +typedef void (tL2CA_CONNECT_PND_CB) (UINT16); + + +/* Configuration indication callback prototype. Parameters are +** Local CID assigned to the connection +** Pointer to configuration info +*/ +typedef void (tL2CA_CONFIG_IND_CB) (UINT16, tL2CAP_CFG_INFO *); + + +/* Configuration confirm callback prototype. Parameters are +** Local CID assigned to the connection +** Pointer to configuration info +*/ +typedef void (tL2CA_CONFIG_CFM_CB) (UINT16, tL2CAP_CFG_INFO *); + + +/* Disconnect indication callback prototype. Parameters are +** Local CID +** Boolean whether upper layer should ack this +*/ +typedef void (tL2CA_DISCONNECT_IND_CB) (UINT16, BOOLEAN); + + +/* Disconnect confirm callback prototype. Parameters are +** Local CID +** Result +*/ +typedef void (tL2CA_DISCONNECT_CFM_CB) (UINT16, UINT16); + + +/* QOS Violation indication callback prototype. Parameters are +** BD Address of violating device +*/ +typedef void (tL2CA_QOS_VIOLATION_IND_CB) (BD_ADDR); + + +/* Data received indication callback prototype. Parameters are +** Local CID +** Address of buffer +*/ +typedef void (tL2CA_DATA_IND_CB) (UINT16, BT_HDR *); + + +/* Echo response callback prototype. Note that this is not included in the +** registration information, but is passed to L2CAP as part of the API to +** actually send an echo request. Parameters are +** Result +*/ +typedef void (tL2CA_ECHO_RSP_CB) (UINT16); + + +/* Callback function prototype to pass broadcom specific echo response */ +/* to the upper layer */ +typedef void (tL2CA_ECHO_DATA_CB) (BD_ADDR, UINT16, UINT8 *); + + +/* Congestion status callback protype. This callback is optional. If +** an application tries to send data when the transmit queue is full, +** the data will anyways be dropped. The parameter is: +** Local CID +** TRUE if congested, FALSE if uncongested +*/ +typedef void (tL2CA_CONGESTION_STATUS_CB) (UINT16, BOOLEAN); + +/* Callback prototype for number of packets completed events. +** This callback notifies the application when Number of Completed Packets +** event has been received. +** This callback is originally designed for 3DG devices. +** The parameter is: +** peer BD_ADDR +*/ +typedef void (tL2CA_NOCP_CB) (BD_ADDR); + +/* Transmit complete callback protype. This callback is optional. If +** set, L2CAP will call it when packets are sent or flushed. If the +** count is 0xFFFF, it means all packets are sent for that CID (eRTM +** mode only). The parameters are: +** Local CID +** Number of SDUs sent or dropped +*/ +typedef void (tL2CA_TX_COMPLETE_CB) (UINT16, UINT16); + +/* Define the structure that applications use to register with +** L2CAP. This structure includes callback functions. All functions +** MUST be provided, with the exception of the "connect pending" +** callback and "congestion status" callback. +*/ +typedef struct { + tL2CA_CONNECT_IND_CB *pL2CA_ConnectInd_Cb; + tL2CA_CONNECT_CFM_CB *pL2CA_ConnectCfm_Cb; + tL2CA_CONNECT_PND_CB *pL2CA_ConnectPnd_Cb; + tL2CA_CONFIG_IND_CB *pL2CA_ConfigInd_Cb; + tL2CA_CONFIG_CFM_CB *pL2CA_ConfigCfm_Cb; + tL2CA_DISCONNECT_IND_CB *pL2CA_DisconnectInd_Cb; + tL2CA_DISCONNECT_CFM_CB *pL2CA_DisconnectCfm_Cb; + tL2CA_QOS_VIOLATION_IND_CB *pL2CA_QoSViolationInd_Cb; + tL2CA_DATA_IND_CB *pL2CA_DataInd_Cb; + tL2CA_CONGESTION_STATUS_CB *pL2CA_CongestionStatus_Cb; + tL2CA_TX_COMPLETE_CB *pL2CA_TxComplete_Cb; + +} tL2CAP_APPL_INFO; + +/* Define the structure that applications use to create or accept +** connections with enhanced retransmission mode. +*/ +typedef struct { + UINT8 preferred_mode; + UINT8 allowed_modes; + UINT16 user_rx_buf_size; + UINT16 user_tx_buf_size; + UINT16 fcr_rx_buf_size; + UINT16 fcr_tx_buf_size; + +} tL2CAP_ERTM_INFO; + +#define L2CA_REGISTER(a,b,c) L2CA_Register(a,(tL2CAP_APPL_INFO *)b) +#define L2CA_DEREGISTER(a) L2CA_Deregister(a) +#define L2CA_CONNECT_REQ(a,b,c,d) L2CA_ErtmConnectReq(a,b,c) +#define L2CA_CONNECT_RSP(a,b,c,d,e,f,g) L2CA_ErtmConnectRsp(a,b,c,d,e,f) +#define L2CA_CONFIG_REQ(a,b) L2CA_ConfigReq(a,b) +#define L2CA_CONFIG_RSP(a,b) L2CA_ConfigRsp(a,b) +#define L2CA_DISCONNECT_REQ(a) L2CA_DisconnectReq(a) +#define L2CA_DISCONNECT_RSP(a) L2CA_DisconnectRsp(a) +#define L2CA_DATA_WRITE(a, b) L2CA_DataWrite(a, b) + +/***************************************************************************** +** External Function Declarations +*****************************************************************************/ +#ifdef __cplusplus +extern "C" +{ +#endif + +#if (CLASSIC_BT_INCLUDED == TRUE) +/******************************************************************************* +** +** Function L2CA_Register +** +** Description Other layers call this function to register for L2CAP +** services. +** +** Returns PSM to use or zero if error. Typically, the PSM returned +** is the same as was passed in, but for an outgoing-only +** connection to a dynamic PSM, a "virtual" PSM is returned +** and should be used in the calls to L2CA_ConnectReq() and +** BTM_SetSecurityLevel(). +** +*******************************************************************************/ +extern UINT16 L2CA_Register (UINT16 psm, tL2CAP_APPL_INFO *p_cb_info); + +/******************************************************************************* +** +** Function L2CA_Deregister +** +** Description Other layers call this function to deregister for L2CAP +** services. +** +** Returns void +** +*******************************************************************************/ +extern void L2CA_Deregister (UINT16 psm); + +/******************************************************************************* +** +** Function L2CA_AllocatePSM +** +** Description Other layers call this function to find an unused PSM for L2CAP +** services. +** +** Returns PSM to use. +** +*******************************************************************************/ +extern UINT16 L2CA_AllocatePSM(void); + +/******************************************************************************* +** +** Function L2CA_ConnectReq +** +** Description Higher layers call this function to create an L2CAP connection. +** Note that the connection is not established at this time, but +** connection establishment gets started. The callback function +** will be invoked when connection establishes or fails. +** +** Returns the CID of the connection, or 0 if it failed to start +** +*******************************************************************************/ +extern UINT16 L2CA_ConnectReq (UINT16 psm, BD_ADDR p_bd_addr); + +/******************************************************************************* +** +** Function L2CA_ConnectRsp +** +** Description Higher layers call this function to accept an incoming +** L2CAP connection, for which they had gotten an connect +** indication callback. +** +** Returns TRUE for success, FALSE for failure +** +*******************************************************************************/ +extern BOOLEAN L2CA_ConnectRsp (BD_ADDR p_bd_addr, UINT8 id, UINT16 lcid, + UINT16 result, UINT16 status); + +/******************************************************************************* +** +** Function L2CA_ErtmConnectReq +** +** Description Higher layers call this function to create an L2CAP connection +** that needs to use Enhanced Retransmission Mode. +** Note that the connection is not established at this time, but +** connection establishment gets started. The callback function +** will be invoked when connection establishes or fails. +** +** Returns the CID of the connection, or 0 if it failed to start +** +*******************************************************************************/ +extern UINT16 L2CA_ErtmConnectReq (UINT16 psm, BD_ADDR p_bd_addr, + tL2CAP_ERTM_INFO *p_ertm_info); + +// This function sets the callback routines for the L2CAP connection referred to by +// |local_cid|. The callback routines can only be modified for outgoing connections +// established by |L2CA_ConnectReq| or accepted incoming connections. |callbacks| +// must not be NULL. This function returns true if the callbacks could be updated, +// false if not (e.g. |local_cid| was not found). +bool L2CA_SetConnectionCallbacks(uint16_t local_cid, const tL2CAP_APPL_INFO *callbacks); + +/******************************************************************************* +** +** Function L2CA_ErtmConnectRsp +** +** Description Higher layers call this function to accept an incoming +** L2CAP connection, for which they had gotten an connect +** indication callback, and for which the higher layer wants +** to use Enhanced Retransmission Mode. +** +** Returns TRUE for success, FALSE for failure +** +*******************************************************************************/ +extern BOOLEAN L2CA_ErtmConnectRsp (BD_ADDR p_bd_addr, UINT8 id, UINT16 lcid, + UINT16 result, UINT16 status, + tL2CAP_ERTM_INFO *p_ertm_info); + +/******************************************************************************* +** +** Function L2CA_ConfigReq +** +** Description Higher layers call this function to send configuration. +** +** Returns TRUE if configuration sent, else FALSE +** +*******************************************************************************/ +extern BOOLEAN L2CA_ConfigReq (UINT16 cid, tL2CAP_CFG_INFO *p_cfg); + +/******************************************************************************* +** +** Function L2CA_ConfigRsp +** +** Description Higher layers call this function to send a configuration +** response. +** +** Returns TRUE if configuration response sent, else FALSE +** +*******************************************************************************/ +extern BOOLEAN L2CA_ConfigRsp (UINT16 cid, tL2CAP_CFG_INFO *p_cfg); + +/******************************************************************************* +** +** Function L2CA_DisconnectReq +** +** Description Higher layers call this function to disconnect a channel. +** +** Returns TRUE if disconnect sent, else FALSE +** +*******************************************************************************/ +extern BOOLEAN L2CA_DisconnectReq (UINT16 cid); + +/******************************************************************************* +** +** Function L2CA_DisconnectRsp +** +** Description Higher layers call this function to acknowledge the +** disconnection of a channel. +** +** Returns void +** +*******************************************************************************/ +extern BOOLEAN L2CA_DisconnectRsp (UINT16 cid); +#endif ///CLASSIC_BT_INCLUDED == TRUE + +#if (BLE_L2CAP_COC_INCLUDED == TRUE) +/******************************************************************************* +** +** Function L2CA_RegisterLECoc +** +** Description Other layers call this function to register for L2CAP +** Connection Oriented Channel. +** +** Returns PSM to use or zero if error. Typically, the PSM returned +** is the same as was passed in, but for an outgoing-only +** connection to a dynamic PSM, a "virtual" PSM is returned +** and should be used in the calls to L2CA_ConnectLECocReq() +** and BTM_SetSecurityLevel(). +** +*******************************************************************************/ +extern UINT16 L2CA_RegisterLECoc (UINT16 psm, tL2CAP_APPL_INFO *p_cb_info); + +/******************************************************************************* +** +** Function L2CA_DeregisterLECoc +** +** Description Other layers call this function to deregister for L2CAP +** Connection Oriented Channel. +** +** Returns void +** +*******************************************************************************/ +extern void L2CA_DeregisterLECoc (UINT16 psm); + +/******************************************************************************* +** +** Function L2CA_ConnectLECocReq +** +** Description Higher layers call this function to create an L2CAP LE COC. +** Note that the connection is not established at this time, but +** connection establishment gets started. The callback function +** will be invoked when connection establishes or fails. +** +** Returns the CID of the connection, or 0 if it failed to start +** +*******************************************************************************/ +extern UINT16 L2CA_ConnectLECocReq (UINT16 psm, BD_ADDR p_bd_addr, tL2CAP_LE_CFG_INFO *p_cfg); + +/******************************************************************************* +** +** Function L2CA_ConnectLECocRsp +** +** Description Higher layers call this function to accept an incoming +** L2CAP LE COC connection, for which they had gotten an connect +** indication callback. +** +** Returns TRUE for success, FALSE for failure +** +*******************************************************************************/ +extern BOOLEAN L2CA_ConnectLECocRsp (BD_ADDR p_bd_addr, UINT8 id, UINT16 lcid, UINT16 result, + UINT16 status, tL2CAP_LE_CFG_INFO *p_cfg); + +/******************************************************************************* +** +** Function L2CA_GetPeerLECocConfig +** +** Description Get peers configuration for LE Connection Oriented Channel. +** +** Return value: TRUE if peer is connected +** +*******************************************************************************/ +extern BOOLEAN L2CA_GetPeerLECocConfig (UINT16 lcid, tL2CAP_LE_CFG_INFO* peer_cfg); + +#endif // (BLE_L2CAP_COC_INCLUDED == TRUE) + +/******************************************************************************* +** +** Function L2CA_DataWrite +** +** Description Higher layers call this function to write data. +** +** Returns L2CAP_DW_SUCCESS, if data accepted, else FALSE +** L2CAP_DW_CONGESTED, if data accepted and the channel is congested +** L2CAP_DW_FAILED, if error +** +*******************************************************************************/ +extern UINT8 L2CA_DataWrite (UINT16 cid, BT_HDR *p_data); + +#if (CLASSIC_BT_INCLUDED == TRUE) + +/******************************************************************************* +** +** Function L2CA_Ping +** +** Description Higher layers call this function to send an echo request. +** +** Returns TRUE if echo request sent, else FALSE. +** +*******************************************************************************/ +extern BOOLEAN L2CA_Ping (BD_ADDR p_bd_addr, tL2CA_ECHO_RSP_CB *p_cb); + +/******************************************************************************* +** +** Function L2CA_Echo +** +** Description Higher layers call this function to send an echo request +** with application-specific data. +** +** Returns TRUE if echo request sent, else FALSE. +** +*******************************************************************************/ +extern BOOLEAN L2CA_Echo (BD_ADDR p_bd_addr, BT_HDR *p_data, tL2CA_ECHO_DATA_CB *p_callback); +#endif ///CLASSIC_BT_INCLUDED == TRUE + + +// Given a local channel identifier, |lcid|, this function returns the bound remote +// channel identifier, |rcid|, and the ACL link handle, |handle|. If |lcid| is not +// known or is invalid, this function returns false and does not modify the values +// pointed at by |rcid| and |handle|. |rcid| and |handle| may be NULL. +bool L2CA_GetIdentifiers(uint16_t lcid, uint16_t *rcid, uint16_t *handle); + +/******************************************************************************* +** +** Function L2CA_SetIdleTimeout +** +** Description Higher layers call this function to set the idle timeout for +** a connection, or for all future connections. The "idle timeout" +** is the amount of time that a connection can remain up with +** no L2CAP channels on it. A timeout of zero means that the +** connection will be torn down immediately when the last channel +** is removed. A timeout of 0xFFFF means no timeout. Values are +** in seconds. +** +** Returns TRUE if command succeeded, FALSE if failed +** +*******************************************************************************/ +extern BOOLEAN L2CA_SetIdleTimeout (UINT16 cid, UINT16 timeout, + BOOLEAN is_global); + + +/******************************************************************************* +** +** Function L2CA_SetIdleTimeoutByBdAddr +** +** Description Higher layers call this function to set the idle timeout for +** a connection. The "idle timeout" is the amount of time that +** a connection can remain up with no L2CAP channels on it. +** A timeout of zero means that the connection will be torn +** down immediately when the last channel is removed. +** A timeout of 0xFFFF means no timeout. Values are in seconds. +** A bd_addr is the remote BD address. If bd_addr = BT_BD_ANY, +** then the idle timeouts for all active l2cap links will be +** changed. +** +** Returns TRUE if command succeeded, FALSE if failed +** +** NOTE This timeout applies to all logical channels active on the +** ACL link. +*******************************************************************************/ +extern BOOLEAN L2CA_SetIdleTimeoutByBdAddr(BD_ADDR bd_addr, UINT16 timeout, + tBT_TRANSPORT transport); + + +/******************************************************************************* +** +** Function L2CA_SetTraceLevel +** +** Description This function sets the trace level for L2CAP. If called with +** a value of 0xFF, it simply reads the current trace level. +** +** Returns the new (current) trace level +** +*******************************************************************************/ +extern UINT8 L2CA_SetTraceLevel (UINT8 trace_level); + + +/******************************************************************************* +** +** Function L2CA_SetDesireRole +** +** Description This function sets the desire role for L2CAP. +** If the new role is L2CAP_ROLE_ALLOW_SWITCH, allow switch on +** HciCreateConnection. +** If the new role is L2CAP_ROLE_DISALLOW_SWITCH, do not allow switch on +** HciCreateConnection. +** +** If the new role is a valid role (HCI_ROLE_MASTER or HCI_ROLE_SLAVE), +** the desire role is set to the new value. Otherwise, it is not changed. +** +** Returns the new (current) role +** +*******************************************************************************/ +extern UINT8 L2CA_SetDesireRole (UINT8 new_role); +#if (CLASSIC_BT_INCLUDED == TRUE) +/******************************************************************************* +** +** Function L2CA_LocalLoopbackReq +** +** Description This function sets up a CID for local loopback +** +** Returns CID of 0 if none. +** +*******************************************************************************/ +extern UINT16 L2CA_LocalLoopbackReq (UINT16 psm, UINT16 handle, BD_ADDR p_bd_addr); + +/******************************************************************************* +** +** Function L2CA_FlushChannel +** +** Description This function flushes none, some or all buffers queued up +** for xmission for a particular CID. If called with +** L2CAP_FLUSH_CHANS_GET (0), it simply returns the number +** of buffers queued for that CID L2CAP_FLUSH_CHANS_ALL (0xffff) +** flushes all buffers. All other values specifies the maximum +** buffers to flush. +** +** Returns Number of buffers left queued for that CID +** +*******************************************************************************/ +extern UINT16 L2CA_FlushChannel (UINT16 lcid, UINT16 num_to_flush); + + +/******************************************************************************* +** +** Function L2CA_SetAclPriority +** +** Description Sets the transmission priority for an ACL channel. +** (For initial implementation only two values are valid. +** L2CAP_PRIORITY_NORMAL and L2CAP_PRIORITY_HIGH). +** +** Returns TRUE if a valid channel, else FALSE +** +*******************************************************************************/ +extern BOOLEAN L2CA_SetAclPriority (BD_ADDR bd_addr, UINT8 priority); + +/******************************************************************************* +** +** Function L2CA_FlowControl +** +** Description Higher layers call this function to flow control a channel. +** +** data_enabled - TRUE data flows, FALSE data is stopped +** +** Returns TRUE if valid channel, else FALSE +** +*******************************************************************************/ +extern BOOLEAN L2CA_FlowControl (UINT16 cid, BOOLEAN data_enabled); + +/******************************************************************************* +** +** Function L2CA_SendTestSFrame +** +** Description Higher layers call this function to send a test S-frame. +** +** Returns TRUE if valid Channel, else FALSE +** +*******************************************************************************/ +extern BOOLEAN L2CA_SendTestSFrame (UINT16 cid, UINT8 sup_type, + UINT8 back_track); + +/******************************************************************************* +** +** Function L2CA_SetTxPriority +** +** Description Sets the transmission priority for a channel. (FCR Mode) +** +** Returns TRUE if a valid channel, else FALSE +** +*******************************************************************************/ +extern BOOLEAN L2CA_SetTxPriority (UINT16 cid, tL2CAP_CHNL_PRIORITY priority); + +/******************************************************************************* +** +** Function L2CA_RegForNoCPEvt +** +** Description Register callback for Number of Completed Packets event. +** +** Input Param p_cb - callback for Number of completed packets event +** p_bda - BT address of remote device +** +** Returns +** +*******************************************************************************/ +extern BOOLEAN L2CA_RegForNoCPEvt(tL2CA_NOCP_CB *p_cb, BD_ADDR p_bda); + +/******************************************************************************* +** +** Function L2CA_SetChnlDataRate +** +** Description Sets the tx/rx data rate for a channel. +** +** Returns TRUE if a valid channel, else FALSE +** +*******************************************************************************/ +extern BOOLEAN L2CA_SetChnlDataRate (UINT16 cid, tL2CAP_CHNL_DATA_RATE tx, tL2CAP_CHNL_DATA_RATE rx); + +typedef void (tL2CA_RESERVE_CMPL_CBACK) (void); + +/******************************************************************************* +** +** Function L2CA_SetFlushTimeout +** +** Description This function set the automatic flush time out in Baseband +** for ACL-U packets. +** BdAddr : the remote BD address of ACL link. If it is BT_DB_ANY +** then the flush time out will be applied to all ACL link. +** FlushTimeout: flush time out in ms +** 0x0000 : No automatic flush +** L2CAP_NO_RETRANSMISSION : No retransmission +** 0x0002 - 0xFFFE : flush time out, if (flush_tout*8)+3/5) +** <= HCI_MAX_AUTO_FLUSH_TOUT (in 625us slot). +** Otherwise, return FALSE. +** L2CAP_NO_AUTOMATIC_FLUSH : No automatic flush +** +** Returns TRUE if command succeeded, FALSE if failed +** +** NOTE This flush timeout applies to all logical channels active on the +** ACL link. +*******************************************************************************/ +extern BOOLEAN L2CA_SetFlushTimeout (BD_ADDR bd_addr, UINT16 flush_tout); +#endif ///CLASSIC_BT_INCLUDED == TRUE + +/******************************************************************************* +** +** Function L2CA_DataWriteEx +** +** Description Higher layers call this function to write data with extended +** flags. +** flags : L2CAP_FLUSHABLE_CH_BASED +** L2CAP_FLUSHABLE_PKT +** L2CAP_NON_FLUSHABLE_PKT +** +** Returns L2CAP_DW_SUCCESS, if data accepted, else FALSE +** L2CAP_DW_CONGESTED, if data accepted and the channel is congested +** L2CAP_DW_FAILED, if error +** +*******************************************************************************/ +extern UINT8 L2CA_DataWriteEx (UINT16 cid, BT_HDR *p_data, UINT16 flags); + +/******************************************************************************* +** +** Function L2CA_SetChnlFlushability +** +** Description Higher layers call this function to set a channels +** flushability flags +** +** Returns TRUE if CID found, else FALSE +** +*******************************************************************************/ +extern BOOLEAN L2CA_SetChnlFlushability (UINT16 cid, BOOLEAN is_flushable); + +/******************************************************************************* +** +** Function L2CA_GetPeerFeatures +** +** Description Get a peers features and fixed channel map +** +** Parameters: BD address of the peer +** Pointers to features and channel mask storage area +** +** Return value: TRUE if peer is connected +** +*******************************************************************************/ +extern BOOLEAN L2CA_GetPeerFeatures (BD_ADDR bd_addr, UINT32 *p_ext_feat, UINT8 *p_chnl_mask); + +/******************************************************************************* +** +** Function L2CA_GetBDAddrbyHandle +** +** Description Get BD address for the given HCI handle +** +** Parameters: HCI handle +** BD address of the peer +** +** Return value: TRUE if found lcb for the given handle, FALSE otherwise +** +*******************************************************************************/ +extern BOOLEAN L2CA_GetBDAddrbyHandle (UINT16 handle, BD_ADDR bd_addr); + +#if (CLASSIC_BT_INCLUDED == TRUE) + +/******************************************************************************* +** +** Function L2CA_GetChnlFcrMode +** +** Description Get the channel FCR mode +** +** Parameters: Local CID +** +** Return value: Channel mode +** +*******************************************************************************/ +extern UINT8 L2CA_GetChnlFcrMode (UINT16 lcid); +#endif ///CLASSIC_BT_INCLUDED == TRUE + + +/******************************************************************************* +** +** UCD callback prototypes +** +*******************************************************************************/ + +/* UCD discovery. Parameters are +** BD Address of remote +** Data Type +** Data +*/ +#define L2CAP_UCD_INFO_TYPE_RECEPTION 0x01 +#define L2CAP_UCD_INFO_TYPE_MTU 0x02 + +typedef void (tL2CA_UCD_DISCOVER_CB) (BD_ADDR, UINT8, UINT32); + +/* UCD data received. Parameters are +** BD Address of remote +** Pointer to buffer with data +*/ +typedef void (tL2CA_UCD_DATA_CB) (BD_ADDR, BT_HDR *); + +/* Congestion status callback protype. This callback is optional. If +** an application tries to send data when the transmit queue is full, +** the data will anyways be dropped. The parameter is: +** remote BD_ADDR +** TRUE if congested, FALSE if uncongested +*/ +typedef void (tL2CA_UCD_CONGESTION_STATUS_CB) (BD_ADDR, BOOLEAN); + +/* UCD registration info (the callback addresses and PSM) +*/ +typedef struct { + tL2CA_UCD_DISCOVER_CB *pL2CA_UCD_Discover_Cb; + tL2CA_UCD_DATA_CB *pL2CA_UCD_Data_Cb; + tL2CA_UCD_CONGESTION_STATUS_CB *pL2CA_UCD_Congestion_Status_Cb; +} tL2CAP_UCD_CB_INFO; + +/******************************************************************************* +** +** Function L2CA_UcdRegister +** +** Description Register PSM on UCD. +** +** Parameters: tL2CAP_UCD_CB_INFO +** +** Return value: TRUE if successs +** +*******************************************************************************/ +extern BOOLEAN L2CA_UcdRegister ( UINT16 psm, tL2CAP_UCD_CB_INFO *p_cb_info ); + +/******************************************************************************* +** +** Function L2CA_UcdDeregister +** +** Description Deregister PSM on UCD. +** +** Parameters: PSM +** +** Return value: TRUE if successs +** +*******************************************************************************/ +extern BOOLEAN L2CA_UcdDeregister ( UINT16 psm ); + +/******************************************************************************* +** +** Function L2CA_UcdDiscover +** +** Description Discover UCD of remote device. +** +** Parameters: PSM +** BD_ADDR of remote device +** info_type : L2CAP_UCD_INFO_TYPE_RECEPTION +** L2CAP_UCD_INFO_TYPE_MTU +** +** +** Return value: TRUE if successs +** +*******************************************************************************/ +extern BOOLEAN L2CA_UcdDiscover ( UINT16 psm, BD_ADDR rem_bda, UINT8 info_type ); + +/******************************************************************************* +** +** Function L2CA_UcdDataWrite +** +** Description Send UCD to remote device +** +** Parameters: PSM +** BD Address of remote +** Pointer to buffer of type BT_HDR +** flags : L2CAP_FLUSHABLE_CH_BASED +** L2CAP_FLUSHABLE_PKT +** L2CAP_NON_FLUSHABLE_PKT +** +** Return value L2CAP_DW_SUCCESS, if data accepted +** L2CAP_DW_FAILED, if error +** +*******************************************************************************/ +extern UINT16 L2CA_UcdDataWrite (UINT16 psm, BD_ADDR rem_bda, BT_HDR *p_buf, UINT16 flags); + +/******************************************************************************* +** +** Function L2CA_UcdSetIdleTimeout +** +** Description Set UCD Idle timeout. +** +** Parameters: BD Addr +** Timeout in second +** +** Return value: TRUE if successs +** +*******************************************************************************/ +extern BOOLEAN L2CA_UcdSetIdleTimeout ( BD_ADDR rem_bda, UINT16 timeout ); + +/******************************************************************************* +** +** Function L2CA_UCDSetTxPriority +** +** Description Sets the transmission priority for a connectionless channel. +** +** Returns TRUE if a valid channel, else FALSE +** +*******************************************************************************/ +extern BOOLEAN L2CA_UCDSetTxPriority ( BD_ADDR rem_bda, tL2CAP_CHNL_PRIORITY priority ); + + +/******************************************************************************* +** +** Fixed Channel callback prototypes +** +*******************************************************************************/ + +/* Fixed channel connected and disconnected. Parameters are +** channel +** BD Address of remote +** TRUE if channel is connected, FALSE if disconnected +** Reason for connection failure +** transport : physical transport, BR/EDR or LE +*/ +typedef void (tL2CA_FIXED_CHNL_CB) (UINT16, BD_ADDR, BOOLEAN, UINT16, tBT_TRANSPORT); + +/* Signalling data received. Parameters are +** channel +** BD Address of remote +** Pointer to buffer with data +*/ +typedef void (tL2CA_FIXED_DATA_CB) (UINT16, BD_ADDR, BT_HDR *); + +/* Congestion status callback protype. This callback is optional. If +** an application tries to send data when the transmit queue is full, +** the data will anyways be dropped. The parameter is: +** remote BD_ADDR +** TRUE if congested, FALSE if uncongested +*/ +typedef void (tL2CA_FIXED_CONGESTION_STATUS_CB) (BD_ADDR, BOOLEAN); + +/* Fixed channel registration info (the callback addresses and channel config) +*/ +typedef struct { + tL2CA_FIXED_CHNL_CB *pL2CA_FixedConn_Cb; + tL2CA_FIXED_DATA_CB *pL2CA_FixedData_Cb; + tL2CA_FIXED_CONGESTION_STATUS_CB *pL2CA_FixedCong_Cb; + tL2CAP_FCR_OPTS fixed_chnl_opts; + + UINT16 default_idle_tout; + tL2CA_TX_COMPLETE_CB *pL2CA_FixedTxComplete_Cb; /* fixed channel tx complete callback */ +} tL2CAP_FIXED_CHNL_REG; + + +#if (L2CAP_NUM_FIXED_CHNLS > 0) +/******************************************************************************* +** +** Function L2CA_RegisterFixedChannel +** +** Description Register a fixed channel. +** +** Parameters: Fixed Channel # +** Channel Callbacks and config +** +** Return value: TRUE if registered OK +** +*******************************************************************************/ +extern BOOLEAN L2CA_RegisterFixedChannel (UINT16 fixed_cid, tL2CAP_FIXED_CHNL_REG *p_freg); + +/******************************************************************************* +** +** Function L2CA_ConnectFixedChnl +** +** Description Connect an fixed signalling channel to a remote device. +** +** Parameters: Fixed CID +** BD Address of remote +** BD Address type +** +** Return value: TRUE if connection started +** +*******************************************************************************/ +extern BOOLEAN L2CA_ConnectFixedChnl (UINT16 fixed_cid, BD_ADDR bd_addr, tBLE_ADDR_TYPE bd_addr_type, BOOLEAN is_aux); + +/******************************************************************************* +** +** Function L2CA_SendFixedChnlData +** +** Description Write data on a fixed signalling channel. +** +** Parameters: Fixed CID +** BD Address of remote +** Pointer to buffer of type BT_HDR +** +** Return value L2CAP_DW_SUCCESS, if data accepted +** L2CAP_DW_FAILED, if error +** +*******************************************************************************/ +extern UINT16 L2CA_SendFixedChnlData (UINT16 fixed_cid, BD_ADDR rem_bda, BT_HDR *p_buf); + +/******************************************************************************* +** +** Function L2CA_RemoveFixedChnl +** +** Description Remove a fixed channel to a remote device. +** +** Parameters: Fixed CID +** BD Address of remote +** Idle timeout to use (or 0xFFFF if don't care) +** +** Return value: TRUE if channel removed +** +*******************************************************************************/ +extern BOOLEAN L2CA_RemoveFixedChnl (UINT16 fixed_cid, BD_ADDR rem_bda); + +/******************************************************************************* +** +** Function L2CA_SetFixedChannelTout +** +** Description Higher layers call this function to set the idle timeout for +** a fixed channel. The "idle timeout" is the amount of time that +** a connection can remain up with no L2CAP channels on it. +** A timeout of zero means that the connection will be torn +** down immediately when the last channel is removed. +** A timeout of 0xFFFF means no timeout. Values are in seconds. +** A bd_addr is the remote BD address. If bd_addr = BT_BD_ANY, +** then the idle timeouts for all active l2cap links will be +** changed. +** +** Returns TRUE if command succeeded, FALSE if failed +** +*******************************************************************************/ +extern BOOLEAN L2CA_SetFixedChannelTout (BD_ADDR rem_bda, UINT16 fixed_cid, UINT16 idle_tout); + +#endif /* (L2CAP_NUM_FIXED_CHNLS > 0) */ + +#if (CLASSIC_BT_INCLUDED == TRUE) +/******************************************************************************* +** +** Function L2CA_GetCurrentConfig +** +** Description This function returns configurations of L2CAP channel +** pp_our_cfg : pointer of our saved configuration options +** p_our_cfg_bits : valid config in bitmap +** pp_peer_cfg: pointer of peer's saved configuration options +** p_peer_cfg_bits : valid config in bitmap +** +** Returns TRUE if successful +** +*******************************************************************************/ +extern BOOLEAN L2CA_GetCurrentConfig (UINT16 lcid, + tL2CAP_CFG_INFO **pp_our_cfg, tL2CAP_CH_CFG_BITS *p_our_cfg_bits, + tL2CAP_CFG_INFO **pp_peer_cfg, tL2CAP_CH_CFG_BITS *p_peer_cfg_bits); +#endif ///CLASSIC_BT_INCLUDED == TRUE + + +#if (BLE_INCLUDED == TRUE) +/******************************************************************************* +** +** Function L2CA_CancelBleConnectReq +** +** Description Cancel a pending connection attempt to a BLE device. +** +** Parameters: BD Address of remote +** +** Return value: TRUE if connection was cancelled +** +*******************************************************************************/ +extern BOOLEAN L2CA_CancelBleConnectReq (BD_ADDR rem_bda); + +/******************************************************************************* +** +** Function L2CA_UpdateBleConnParams +** +** Description Update BLE connection parameters. +** +** Parameters: BD Address of remote +** +** Return value: TRUE if update started +** +*******************************************************************************/ +extern BOOLEAN L2CA_UpdateBleConnParams (BD_ADDR rem_bdRa, UINT16 min_int, + UINT16 max_int, UINT16 latency, UINT16 timeout); + +/******************************************************************************* +** +** Function L2CA_EnableUpdateBleConnParams +** +** Description Update BLE connection parameters. +** +** Parameters: BD Address of remote +** enable flag +** +** Return value: TRUE if update started +** +*******************************************************************************/ +extern BOOLEAN L2CA_EnableUpdateBleConnParams (BD_ADDR rem_bda, BOOLEAN enable); + +/******************************************************************************* +** +** Function L2CA_GetBleConnRole +** +** Description This function returns the connection role. +** +** Returns link role. +** +*******************************************************************************/ +extern UINT8 L2CA_GetBleConnRole (BD_ADDR bd_addr); + +/******************************************************************************* +** +** Function L2CA_BleDisconnect +** +** Description This function use to disconnect LE connection. +** +** Parameters BD Address of remote +** +** Returns TRUE if disconnect successfully. +** +*******************************************************************************/ +extern BOOLEAN L2CA_BleDisconnect (BD_ADDR rem_bda); +#endif /* (BLE_INCLUDED == TRUE) */ + +/******************************************************************************* +** +** Function L2CA_GetDisconnectReason +** +** Description This function returns the disconnect reason code. +** +** Parameters: BD Address of remote +** Physical transport for the L2CAP connection (BR/EDR or LE) +** +** Returns disconnect reason +** +*******************************************************************************/ +extern UINT16 L2CA_GetDisconnectReason (BD_ADDR remote_bda, tBT_TRANSPORT transport); + +extern BOOLEAN L2CA_CheckIsCongest(UINT16 fixed_cid, BD_ADDR addr); + +#define L2CA_GET_ATT_NUM 0 +#define L2CA_ADD_BTC_NUM 1 +#define L2CA_DECREASE_BTC_NUM 2 +#define L2CA_ADD_BTU_NUM 3 +#define L2CA_DECREASE_BTU_NUM 4 +#define L2CA_BUFF_INI 5 +#define L2CA_BUFF_DEINIT 6 +#define L2CA_BUFF_FREE 7 + +typedef struct { + UINT16 conn_id; + UINT16 * get_num; +} tl2c_buff_param_t; + + +extern void l2ble_update_att_acl_pkt_num(UINT8 type, tl2c_buff_param_t *param); + +#ifdef __cplusplus +} +#endif + +#endif /* L2C_API_H */ diff --git a/lib/bt/host/bluedroid/stack/include/stack/l2cap_client.h b/lib/bt/host/bluedroid/stack/include/stack/l2cap_client.h new file mode 100644 index 00000000..d18be32c --- /dev/null +++ b/lib/bt/host/bluedroid/stack/include/stack/l2cap_client.h @@ -0,0 +1,80 @@ +/****************************************************************************** + * + * Copyright (C) 2014 Google, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +#ifndef _L2CAP_CLIENT_H_ +#define _L2CAP_CLIENT_H_ +#if (defined(L2CAP_CLIENT_INCLUDED) && L2CAP_CLIENT_INCLUDED == TRUE) + +//#include +#include +#include + +typedef struct buffer_t buffer_t; +typedef struct l2cap_client_t l2cap_client_t; + +typedef struct { + void (*connected)(l2cap_client_t *client, void *context); + void (*disconnected)(l2cap_client_t *client, void *context); + void (*read_ready)(l2cap_client_t *client, buffer_t *packet, void *context); + void (*write_ready)(l2cap_client_t *client, void *context); +} l2cap_client_callbacks_t; + +// Returns a new buffer with enough space for |size| bytes of L2CAP payload. +// |size| must be greater than zero. This function returns NULL if the buffer +// could not be allocated. The returned buffer must be freed with |buffer_free| +// when it is no longer needed. +buffer_t *l2cap_buffer_new(size_t size); + +// Creates and returns a new L2CAP client object. |callbacks| must not be NULL and +// must specify a set of functions that should be called back when events occur +// on the L2CAP connection. |context| may be NULL and will be passed as the argument +// to all callbacks in |l2cap_client_callbacks_t|. The returned object must be freed +// with |l2cap_client_free|. +l2cap_client_t *l2cap_client_new(const l2cap_client_callbacks_t *callbacks, void *context); + +// Frees the L2CAP client object allocated with |l2cap_client_new|. |client| may be NULL. +void l2cap_client_free(l2cap_client_t *client); + +// Attempts to connect the |client| to a peer device specified by |remote_bdaddr| +// using the |psm| protocol specifier. This function returns true if the connect +// operation could be started and will indicate completion with either a 'connected' +// callback (success) or a 'disconnected' callback (failure). +// +// This function must not be called while a connect operation is in progress or +// while |l2cap_client_is_connected|. |client| and |remote_bdaddr| must not be NULL. +// |psm| must be greater than zero. +bool l2cap_client_connect(l2cap_client_t *client, const bt_bdaddr_t *remote_bdaddr, uint16_t psm); + +// Disconnects a connected |client|. This function is asynchronous and idempotent. It +// will indicate completion with a 'disconnected' callback. |client| must not be NULL. +void l2cap_client_disconnect(l2cap_client_t *client); + +// Returns true if |client| is connected and is ready to accept data written to it. +// |client| must not be NULL. +bool l2cap_client_is_connected(const l2cap_client_t *client); + +// Writes data contained in |packet| to a connected |client|. This function returns +// true if the packet was successfully queued for delivery, false if the client cannot +// accept more data at this time. If this function returns false, the caller must wait +// for the 'write_ready' callback to write additional data to the client. Neither +// |client| nor |packet| may be NULL. +bool l2cap_client_write(l2cap_client_t *client, buffer_t *packet); + +#endif ///(defined(L2CAP_CLIENT_INCLUDED) && L2CAP_CLIENT_INCLUDED == TRUE) + +#endif /*_L2CAP_CLIENT_H_*/ diff --git a/lib/bt/host/bluedroid/stack/include/stack/l2cap_hci_link_interface.h b/lib/bt/host/bluedroid/stack/include/stack/l2cap_hci_link_interface.h new file mode 100644 index 00000000..61e1daac --- /dev/null +++ b/lib/bt/host/bluedroid/stack/include/stack/l2cap_hci_link_interface.h @@ -0,0 +1,15 @@ +/* + * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef L2CAP_HCI_LINK_INTERFACE_H +#define L2CAP_HCI_LINK_INTERFACE_H + +#include "bt_common.h" + +extern BOOLEAN l2c_link_hci_conn_comp (UINT8 status, UINT16 handle, BD_ADDR p_bda); +extern BOOLEAN l2c_link_hci_disc_comp (UINT16 handle, UINT8 reason); + +#endif /* L2CAP_HCI_LINK_INTERFACE_H */ diff --git a/lib/bt/host/bluedroid/stack/include/stack/l2cdefs.h b/lib/bt/host/bluedroid/stack/include/stack/l2cdefs.h new file mode 100644 index 00000000..1572a37d --- /dev/null +++ b/lib/bt/host/bluedroid/stack/include/stack/l2cdefs.h @@ -0,0 +1,336 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +#ifndef L2CDEFS_H +#define L2CDEFS_H + +/* L2CAP command codes +*/ +#define L2CAP_CMD_REJECT 0x01 +#define L2CAP_CMD_CONN_REQ 0x02 +#define L2CAP_CMD_CONN_RSP 0x03 +#define L2CAP_CMD_CONFIG_REQ 0x04 +#define L2CAP_CMD_CONFIG_RSP 0x05 +#define L2CAP_CMD_DISC_REQ 0x06 +#define L2CAP_CMD_DISC_RSP 0x07 +#define L2CAP_CMD_ECHO_REQ 0x08 +#define L2CAP_CMD_ECHO_RSP 0x09 +#define L2CAP_CMD_INFO_REQ 0x0A +#define L2CAP_CMD_INFO_RSP 0x0B +#define L2CAP_CMD_AMP_CONN_REQ 0x0C +#define L2CAP_CMD_AMP_CONN_RSP 0x0D +#define L2CAP_CMD_AMP_MOVE_REQ 0x0E +#define L2CAP_CMD_AMP_MOVE_RSP 0x0F +#define L2CAP_CMD_AMP_MOVE_CFM 0x10 +#define L2CAP_CMD_AMP_MOVE_CFM_RSP 0x11 + +#define L2CAP_CMD_BLE_UPDATE_REQ 0x12 +#define L2CAP_CMD_BLE_UPDATE_RSP 0x13 +#define L2CAP_CMD_BLE_CREDIT_BASED_CONN_REQ 0x14 +#define L2CAP_CMD_BLE_CREDIT_BASED_CONN_RES 0x15 +#define L2CAP_CMD_BLE_FLOW_CTRL_CREDIT 0x16 + + + +/* Define some packet and header lengths +*/ +#define L2CAP_PKT_OVERHEAD 4 /* Length and CID */ +#define L2CAP_CMD_OVERHEAD 4 /* Cmd code, Id and length */ +#define L2CAP_CMD_REJECT_LEN 2 /* Reason (data is optional) */ +#define L2CAP_CONN_REQ_LEN 4 /* PSM and source CID */ +#define L2CAP_CONN_RSP_LEN 8 /* Dest CID, source CID, reason, status */ +#define L2CAP_CONFIG_REQ_LEN 4 /* Dest CID, flags (data is optional) */ +#define L2CAP_CONFIG_RSP_LEN 6 /* Dest CID, flags, result,data optional*/ +#define L2CAP_DISC_REQ_LEN 4 /* Dest CID, source CID */ +#define L2CAP_DISC_RSP_LEN 4 /* Dest CID, source CID */ +#define L2CAP_ECHO_REQ_LEN 0 /* Data is optional */ +#define L2CAP_ECHO_RSP_LEN 0 /* Data is optional */ +#define L2CAP_INFO_REQ_LEN 2 /* Info type */ +#define L2CAP_INFO_RSP_LEN 4 /* Info type, result (data is optional) */ +#define L2CAP_BCST_OVERHEAD 2 /* Additional broadcast packet overhead */ +#define L2CAP_UCD_OVERHEAD 2 /* Additional connectionless packet overhead */ + +#define L2CAP_AMP_CONN_REQ_LEN 5 /* PSM, CID, and remote controller ID */ +#define L2CAP_AMP_MOVE_REQ_LEN 3 /* CID and remote controller ID */ +#define L2CAP_AMP_MOVE_RSP_LEN 4 /* CID and result */ +#define L2CAP_AMP_MOVE_CFM_LEN 4 /* CID and result */ +#define L2CAP_AMP_MOVE_CFM_RSP_LEN 2 /* CID */ + +#define L2CAP_CMD_BLE_UPD_REQ_LEN 8 /* Min and max interval, latency, tout */ +#define L2CAP_CMD_BLE_UPD_RSP_LEN 2 /* Result */ + +#define L2CAP_CMD_BLE_CREDIT_BASED_CONN_REQ_LEN 10 /* LE_PSM, SCID, MTU, MPS, Init Credit */ +#define L2CAP_CMD_BLE_CREDIT_BASED_CONN_RES_LEN 10 /* DCID, MTU, MPS, Init credit, Result */ +#define L2CAP_CMD_BLE_FLOW_CTRL_CREDIT_LEN 4 /* CID, Credit */ + + + +/* Define the packet boundary flags +*/ +#if (L2CAP_NON_FLUSHABLE_PB_INCLUDED == TRUE) +#define L2CAP_PKT_START_FLUSHABLE 2 +#define L2CAP_PKT_START_NON_FLUSHABLE 0 +#endif +#define L2CAP_COMPLETE_AMP_PKT 3 /* complete L2CAP packet on AMP HCI */ +#define L2CAP_PKT_START 2 +#define L2CAP_PKT_CONTINUE 1 +#define L2CAP_MASK_FLAG 0x0FFF +#define L2CAP_PKT_TYPE_SHIFT 12 +#define L2CAP_PKT_TYPE_MASK 3 + + +/* Define the L2CAP connection result codes +*/ +#define L2CAP_CONN_OK 0 +#define L2CAP_CONN_PENDING 1 +#define L2CAP_CONN_NO_PSM 2 +#define L2CAP_CONN_SECURITY_BLOCK 3 +#define L2CAP_CONN_NO_RESOURCES 4 +#define L2CAP_CONN_BAD_CTLR_ID 5 /* AMP related */ +#define L2CAP_CONN_TIMEOUT 0xEEEE +#define L2CAP_CONN_AMP_FAILED 254 +#define L2CAP_CONN_NO_LINK 255 /* Add a couple of our own for internal use */ +#define L2CAP_CONN_CANCEL 256 /* L2CAP connection cancelled */ + + +/* Define L2CAP Move Channel Response result codes +*/ +#define L2CAP_MOVE_OK 0 +#define L2CAP_MOVE_PENDING 1 +#define L2CAP_MOVE_CTRL_ID_NOT_SUPPORT 2 +#define L2CAP_MOVE_SAME_CTRLR_ID 3 +#define L2CAP_MOVE_CONFIG_NOT_SUPPORTED 4 +#define L2CAP_MOVE_CHAN_COLLISION 5 +#define L2CAP_MOVE_NOT_ALLOWED 6 + + +/* Define L2CAP Move Channel Confirmation result codes +*/ +#define L2CAP_MOVE_CFM_OK 0 +#define L2CAP_MOVE_CFM_REFUSED 1 + + +/* Define the L2CAP command reject reason codes +*/ +#define L2CAP_CMD_REJ_NOT_UNDERSTOOD 0 +#define L2CAP_CMD_REJ_MTU_EXCEEDED 1 +#define L2CAP_CMD_REJ_INVALID_CID 2 + + +/* L2CAP Predefined CIDs +*/ +#define L2CAP_SIGNALLING_CID 1 +#define L2CAP_CONNECTIONLESS_CID 2 +#define L2CAP_AMP_CID 3 +#define L2CAP_ATT_CID 4 +#define L2CAP_BLE_SIGNALLING_CID 5 +#define L2CAP_SMP_CID 6 +#define L2CAP_SMP_BR_CID 7 +#define L2CAP_AMP_TEST_CID 0x003F +#define L2CAP_BASE_APPL_CID 0x0040 +#define L2CAP_BLE_CONN_MAX_CID 0x007F + +/* Fixed Channels mask bits */ + +/* Signal channel supported (Mandatory) */ +#define L2CAP_FIXED_CHNL_SIG_BIT (1 << L2CAP_SIGNALLING_CID) + +/* Connectionless reception */ +#define L2CAP_FIXED_CHNL_CNCTLESS_BIT (1 << L2CAP_CONNECTIONLESS_CID) + +/* AMP Manager supported */ +#define L2CAP_FIXED_CHNL_AMP_BIT (1 << L2CAP_AMP_CID) + +/* Attribute protocol supported */ +#define L2CAP_FIXED_CHNL_ATT_BIT (1 << L2CAP_ATT_CID) + +/* BLE Signalling supported */ +#define L2CAP_FIXED_CHNL_BLE_SIG_BIT (1 << L2CAP_BLE_SIGNALLING_CID) + +/* BLE Security Mgr supported */ +#define L2CAP_FIXED_CHNL_SMP_BIT (1 << L2CAP_SMP_CID) + +/* Security Mgr over BR supported */ +#define L2CAP_FIXED_CHNL_SMP_BR_BIT (1 << L2CAP_SMP_BR_CID) + + + +/* Define the L2CAP configuration result codes +*/ +#define L2CAP_CFG_OK 0 +#define L2CAP_CFG_UNACCEPTABLE_PARAMS 1 +#define L2CAP_CFG_FAILED_NO_REASON 2 +#define L2CAP_CFG_UNKNOWN_OPTIONS 3 +#define L2CAP_CFG_PENDING 4 +#define L2CAP_CFG_FLOW_SPEC_REJECTED 5 + + +/* Define the L2CAP configuration option types +*/ +#define L2CAP_CFG_TYPE_MTU 0x01 +#define L2CAP_CFG_TYPE_FLUSH_TOUT 0x02 +#define L2CAP_CFG_TYPE_QOS 0x03 +#define L2CAP_CFG_TYPE_FCR 0x04 +#define L2CAP_CFG_TYPE_FCS 0x05 +#define L2CAP_CFG_TYPE_EXT_FLOW 0x06 +#define L2CAP_CFG_TYPE_EXT_WIN_SIZE 0x07 + +#define L2CAP_CFG_MTU_OPTION_LEN 2 /* MTU option length */ +#define L2CAP_CFG_FLUSH_OPTION_LEN 2 /* Flush option len */ +#define L2CAP_CFG_QOS_OPTION_LEN 22 /* QOS option length */ +#define L2CAP_CFG_FCR_OPTION_LEN 9 /* FCR option length */ +#define L2CAP_CFG_FCS_OPTION_LEN 1 /* FCR option length */ +#define L2CAP_CFG_EXT_FLOW_OPTION_LEN 16 /* Extended Flow Spec */ +#define L2CAP_CFG_EXT_WIN_SIZE_LEN 2 /* Ext window size length */ +#define L2CAP_CFG_OPTION_OVERHEAD 2 /* Type and length */ + +/* Configuration Cmd/Rsp Flags mask +*/ +#define L2CAP_CFG_FLAGS_MASK_CONT 0x0001 /* Flags mask: Continuation */ + +/* FCS Check Option values +*/ +#define L2CAP_CFG_FCS_BYPASS 0 /* Bypass the FCS in streaming or ERTM modes */ +#define L2CAP_CFG_FCS_USE 1 /* Use the FCS in streaming or ERTM modes [default] */ + +/* Default values for configuration +*/ +#define L2CAP_NO_AUTOMATIC_FLUSH 0xFFFF +#define L2CAP_NO_RETRANSMISSION 0x0001 + +#define L2CAP_DEFAULT_MTU (672) +#define L2CAP_DEFAULT_FLUSH_TO L2CAP_NO_AUTOMATIC_FLUSH +#define L2CAP_DEFAULT_SERV_TYPE 1 +#define L2CAP_DEFAULT_TOKEN_RATE 0 +#define L2CAP_DEFAULT_BUCKET_SIZE 0 +#define L2CAP_DEFAULT_PEAK_BANDWIDTH 0 +#define L2CAP_DEFAULT_LATENCY 0xFFFFFFFF +#define L2CAP_DEFAULT_DELAY 0xFFFFFFFF +#define L2CAP_DEFAULT_FCS L2CAP_CFG_FCS_USE + + +/* Define the L2CAP disconnect result codes +*/ +#define L2CAP_DISC_OK 0 +#define L2CAP_DISC_TIMEOUT 0xEEEE + +/* Define the L2CAP info resp result codes +*/ +#define L2CAP_INFO_RESP_RESULT_SUCCESS 0 +#define L2CAP_INFO_RESP_RESULT_NOT_SUPPORTED 1 + +/* Define the info-type fields of information request & response +*/ +#define L2CAP_CONNLESS_MTU_INFO_TYPE 0x0001 +#define L2CAP_EXTENDED_FEATURES_INFO_TYPE 0x0002 /* Used in Information Req/Response */ +#define L2CAP_FIXED_CHANNELS_INFO_TYPE 0x0003 /* Used in AMP */ + +#define L2CAP_CONNLESS_MTU_INFO_SIZE 2 /* Connectionless MTU size */ +#define L2CAP_EXTENDED_FEATURES_ARRAY_SIZE 4 /* Extended features array size */ +#define L2CAP_FIXED_CHNL_ARRAY_SIZE 8 /* Fixed channel array size */ + +/* Extended features mask bits +*/ +#define L2CAP_EXTFEA_RTRANS 0x00000001 /* Retransmission Mode (Not Supported) */ +#define L2CAP_EXTFEA_FC 0x00000002 /* Flow Control Mode (Not Supported) */ +#define L2CAP_EXTFEA_QOS 0x00000004 +#define L2CAP_EXTFEA_ENH_RETRANS 0x00000008 /* Enhanced retransmission mode */ +#define L2CAP_EXTFEA_STREAM_MODE 0x00000010 /* Streaming Mode */ +#define L2CAP_EXTFEA_NO_CRC 0x00000020 /* Optional FCS (if set No FCS desired) */ +#define L2CAP_EXTFEA_EXT_FLOW_SPEC 0x00000040 /* Extended flow spec */ +#define L2CAP_EXTFEA_FIXED_CHNLS 0x00000080 /* Fixed channels */ +#define L2CAP_EXTFEA_EXT_WINDOW 0x00000100 /* Extended Window Size */ +#define L2CAP_EXTFEA_UCD_RECEPTION 0x00000200 /* Unicast Connectionless Data Reception */ + +/* Mask for LE supported features used in Information Response (default to none) */ +#ifndef L2CAP_BLE_EXTFEA_MASK +#define L2CAP_BLE_EXTFEA_MASK 0 +#endif + +/* Define a value that tells L2CAP to use the default HCI ACL buffer size */ +#define L2CAP_INVALID_ERM_BUF_SIZE 0 + +/* Define a value that tells L2CAP to use the default MPS */ +#define L2CAP_DEFAULT_ERM_MPS 0x0000 + +#define L2CAP_FCR_OVERHEAD 2 /* Control word */ +#define L2CAP_FCS_LEN 2 /* FCS takes 2 bytes */ +#define L2CAP_SDU_LEN_OVERHEAD 2 /* SDU length field is 2 bytes */ +#define L2CAP_SDU_LEN_OFFSET 2 /* SDU length offset is 2 bytes */ +#define L2CAP_EXT_CONTROL_OVERHEAD 4 /* Extended Control Field */ +#define L2CAP_MAX_HEADER_FCS (L2CAP_PKT_OVERHEAD + L2CAP_EXT_CONTROL_OVERHEAD + L2CAP_SDU_LEN_OVERHEAD + L2CAP_FCS_LEN) +/* length(2), channel(2), control(4), SDU length(2) FCS(2) */ +/* To optimize this, it must be a multiplum of the L2CAP PDU length AND match the 3DH5 air + * including the l2cap headers in each packet - to match the latter - the -5 is added + */ +#define L2CAP_MAX_SDU_LENGTH (8080 + 26 - (L2CAP_MIN_OFFSET + 6)) +#define L2CAP_MAX_BUF_SIZE (10240 + 24) + +/* Part of L2CAP_MIN_OFFSET that is not part of L2CAP +*/ +#define L2CAP_OFFSET_WO_L2HDR (L2CAP_MIN_OFFSET-(L2CAP_PKT_OVERHEAD+L2CAP_FCR_OVERHEAD)) + +/* SAR bits in the control word +*/ +#define L2CAP_FCR_UNSEG_SDU 0x0000 /* Control word to begin with for unsegmented PDU*/ +#define L2CAP_FCR_START_SDU 0x4000 /* ...for Starting PDU of a semented SDU */ +#define L2CAP_FCR_END_SDU 0x8000 /* ...for ending PDU of a segmented SDU */ +#define L2CAP_FCR_CONT_SDU 0xc000 /* ...for continuation PDU of a segmented SDU */ + +/* Supervisory frame types +*/ +#define L2CAP_FCR_SUP_RR 0x0000 /* Supervisory frame - RR */ +#define L2CAP_FCR_SUP_REJ 0x0001 /* Supervisory frame - REJ */ +#define L2CAP_FCR_SUP_RNR 0x0002 /* Supervisory frame - RNR */ +#define L2CAP_FCR_SUP_SREJ 0x0003 /* Supervisory frame - SREJ */ + +#define L2CAP_FCR_SAR_BITS 0xC000 /* Mask to get the SAR bits from control word */ +#define L2CAP_FCR_SAR_BITS_SHIFT 14 /* Bits to shift right to get the SAR bits from ctrl-word */ + +#define L2CAP_FCR_S_FRAME_BIT 0x0001 /* Mask to check if a PDU is S-frame */ +#define L2CAP_FCR_REQ_SEQ_BITS 0x3F00 /* Mask to get the req-seq from control word */ +#define L2CAP_FCR_REQ_SEQ_BITS_SHIFT 8 /* Bits to shift right to get the req-seq from ctrl-word */ +#define L2CAP_FCR_TX_SEQ_BITS 0x007E /* Mask on get the tx-seq from control word */ +#define L2CAP_FCR_TX_SEQ_BITS_SHIFT 1 /* Bits to shift right to get the tx-seq from ctrl-word */ + +#define L2CAP_FCR_F_BIT 0x0080 /* F-bit in the control word (Sup and I frames) */ +#define L2CAP_FCR_P_BIT 0x0010 /* P-bit in the control word (Sup frames only) */ + +#define L2CAP_FCR_F_BIT_SHIFT 7 +#define L2CAP_FCR_P_BIT_SHIFT 4 + +#define L2CAP_FCR_SEG_BITS 0xC000 /* Mask to get the segmentation bits from ctrl-word */ +#define L2CAP_FCR_SUP_SHIFT 2 /* Bits to shift right to get the S-bits from ctrl-word */ +#define L2CAP_FCR_SUP_BITS 0x000C /* Mask to get the supervisory bits from ctrl-word */ + +#define L2CAP_FCR_INIT_CRC 0 /* Initial state of the CRC register */ +#define L2CAP_FCR_SEQ_MODULO 0x3F /* Mask for sequence numbers (range 0 - 63) */ + +#define L2CAP_LE_RESULT_CONN_OK 0 +#define L2CAP_LE_RESULT_NO_PSM 2 +#define L2CAP_LE_RESULT_NO_RESOURCES 4 +#define L2CAP_LE_RESULT_INSUFFICIENT_AUTHENTICATION 5 +#define L2CAP_LE_RESULT_INSUFFICIENT_AUTHORIZATION 6 +#define L2CAP_LE_RESULT_INSUFFICIENT_ENCRY_KEY_SIZE 7 +#define L2CAP_LE_RESULT_INSUFFICIENT_ENCRY 8 +#define L2CAP_LE_RESULT_INVALID_SOURCE_CID 9 +#define L2CAP_LE_RESULT_SOURCE_CID_ALREADY_ALLOCATED 0x0A +#define L2CAP_LE_RESULT_UNACCEPTABLE_PARAMETERS 0x0B +#define L2CAP_LE_RESULT_INVALID_PARAMETERS 0x0C + +#endif diff --git a/lib/bt/host/bluedroid/stack/include/stack/port_api.h b/lib/bt/host/bluedroid/stack/include/stack/port_api.h new file mode 100644 index 00000000..599cfaa3 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/include/stack/port_api.h @@ -0,0 +1,710 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * this file contains the PORT API definitions + * + ******************************************************************************/ +#ifndef PORT_API_H +#define PORT_API_H + +#include "common/bt_target.h" +#include "common/bt_defs.h" + +/***************************************************************************** +** Constants and Types +*****************************************************************************/ + +/* +** Define port settings structure send from the application in the +** set settings request, or to the application in the set settings indication. +*/ +typedef struct { + +#define PORT_BAUD_RATE_2400 0x00 +#define PORT_BAUD_RATE_4800 0x01 +#define PORT_BAUD_RATE_7200 0x02 +#define PORT_BAUD_RATE_9600 0x03 +#define PORT_BAUD_RATE_19200 0x04 +#define PORT_BAUD_RATE_38400 0x05 +#define PORT_BAUD_RATE_57600 0x06 +#define PORT_BAUD_RATE_115200 0x07 +#define PORT_BAUD_RATE_230400 0x08 + + UINT8 baud_rate; + +#define PORT_5_BITS 0x00 +#define PORT_6_BITS 0x01 +#define PORT_7_BITS 0x02 +#define PORT_8_BITS 0x03 + + UINT8 byte_size; + +#define PORT_ONESTOPBIT 0x00 +#define PORT_ONE5STOPBITS 0x01 + UINT8 stop_bits; + +#define PORT_PARITY_NO 0x00 +#define PORT_PARITY_YES 0x01 + UINT8 parity; + +#define PORT_ODD_PARITY 0x00 +#define PORT_EVEN_PARITY 0x01 +#define PORT_MARK_PARITY 0x02 +#define PORT_SPACE_PARITY 0x03 + + UINT8 parity_type; + +#define PORT_FC_OFF 0x00 +#define PORT_FC_XONXOFF_ON_INPUT 0x01 +#define PORT_FC_XONXOFF_ON_OUTPUT 0x02 +#define PORT_FC_CTS_ON_INPUT 0x04 +#define PORT_FC_CTS_ON_OUTPUT 0x08 +#define PORT_FC_DSR_ON_INPUT 0x10 +#define PORT_FC_DSR_ON_OUTPUT 0x20 + + UINT8 fc_type; + + UINT8 rx_char1; + +#define PORT_XON_DC1 0x11 + UINT8 xon_char; + +#define PORT_XOFF_DC3 0x13 + UINT8 xoff_char; + +} tPORT_STATE; + + +/* +** Define the callback function prototypes. Parameters are specific +** to each event and are described bellow +*/ +typedef int (tPORT_DATA_CALLBACK) (UINT16 port_handle, void *p_data, UINT16 len); + +#define DATA_CO_CALLBACK_TYPE_INCOMING 1 +#define DATA_CO_CALLBACK_TYPE_OUTGOING_SIZE 2 +#define DATA_CO_CALLBACK_TYPE_OUTGOING 3 +typedef int (tPORT_DATA_CO_CALLBACK) (UINT16 port_handle, UINT8 *p_buf, UINT16 len, int type); + +typedef void (tPORT_CALLBACK) (UINT32 code, UINT16 port_handle); + +typedef void (tPORT_MGMT_CALLBACK) (UINT32 code, UINT16 port_handle, void* data); + +/** + * Define the server port manage callback function argument + */ +typedef struct { + BOOLEAN accept; /* If upper layer accepts the incoming connection */ + BOOLEAN ignore_rfc_state; /* If need to ignore rfc state for PORT_CheckConnection */ + UINT16 peer_mtu; /* Max MTU that port can send */ +} tPORT_MGMT_SR_CALLBACK_ARG; + +/** + * Define the client port manage callback function argument + */ +typedef struct { + UINT16 peer_mtu; /* Max MTU that port can send */ +} tPORT_MGMT_CL_CALLBACK_ARG; + +/* +** Define events that registered application can receive in the callback +*/ + +#define PORT_EV_RXCHAR 0x00000001 /* Any Character received */ +#define PORT_EV_RXFLAG 0x00000002 /* Received certain character */ +#define PORT_EV_TXEMPTY 0x00000004 /* Transmitt Queue Empty */ +#define PORT_EV_CTS 0x00000008 /* CTS changed state */ +#define PORT_EV_DSR 0x00000010 /* DSR changed state */ +#define PORT_EV_RLSD 0x00000020 /* RLSD changed state */ +#define PORT_EV_BREAK 0x00000040 /* BREAK received */ +#define PORT_EV_ERR 0x00000080 /* Line status error occurred */ +#define PORT_EV_RING 0x00000100 /* Ring signal detected */ +#define PORT_EV_CTSS 0x00000400 /* CTS state */ +#define PORT_EV_DSRS 0x00000800 /* DSR state */ +#define PORT_EV_RLSDS 0x00001000 /* RLSD state */ +#define PORT_EV_OVERRUN 0x00002000 /* receiver buffer overrun */ +#define PORT_EV_TXCHAR 0x00004000 /* Any character transmitted */ + +#define PORT_EV_CONNECTED 0x00000200 /* RFCOMM connection established */ +#define PORT_EV_CONNECT_ERR 0x00008000 /* Was not able to establish connection */ +/* or disconnected */ +#define PORT_EV_FC 0x00010000 /* data flow enabled flag changed by remote */ +#define PORT_EV_FCS 0x00020000 /* data flow enable status true = enabled */ + +/* +** To register for events application should provide bitmask with +** corresponding bit set +*/ + +#define PORT_MASK_ALL (PORT_EV_RXCHAR | PORT_EV_TXEMPTY | PORT_EV_CTS | \ + PORT_EV_DSR | PORT_EV_RLSD | PORT_EV_BREAK | \ + PORT_EV_ERR | PORT_EV_RING | PORT_EV_CONNECT_ERR | \ + PORT_EV_DSRS | PORT_EV_CTSS | PORT_EV_RLSDS | \ + PORT_EV_RXFLAG | PORT_EV_TXCHAR | PORT_EV_OVERRUN | \ + PORT_EV_FC | PORT_EV_FCS | PORT_EV_CONNECTED) + + +/* +** Define port result codes +*/ +#define PORT_SUCCESS 0 + +#define PORT_ERR_BASE 0 + +#define PORT_UNKNOWN_ERROR (PORT_ERR_BASE + 1) +#define PORT_ALREADY_OPENED (PORT_ERR_BASE + 2) +#define PORT_CMD_PENDING (PORT_ERR_BASE + 3) +#define PORT_APP_NOT_REGISTERED (PORT_ERR_BASE + 4) +#define PORT_NO_MEM (PORT_ERR_BASE + 5) +#define PORT_NO_RESOURCES (PORT_ERR_BASE + 6) +#define PORT_BAD_BD_ADDR (PORT_ERR_BASE + 7) +#define PORT_BAD_HANDLE (PORT_ERR_BASE + 9) +#define PORT_NOT_OPENED (PORT_ERR_BASE + 10) +#define PORT_LINE_ERR (PORT_ERR_BASE + 11) +#define PORT_START_FAILED (PORT_ERR_BASE + 12) +#define PORT_PAR_NEG_FAILED (PORT_ERR_BASE + 13) +#define PORT_PORT_NEG_FAILED (PORT_ERR_BASE + 14) +#define PORT_SEC_FAILED (PORT_ERR_BASE + 15) +#define PORT_PEER_CONNECTION_FAILED (PORT_ERR_BASE + 16) +#define PORT_PEER_FAILED (PORT_ERR_BASE + 17) +#define PORT_PEER_TIMEOUT (PORT_ERR_BASE + 18) +#define PORT_CLOSED (PORT_ERR_BASE + 19) +#define PORT_TX_FULL (PORT_ERR_BASE + 20) +#define PORT_LOCAL_CLOSED (PORT_ERR_BASE + 21) +#define PORT_LOCAL_TIMEOUT (PORT_ERR_BASE + 22) +#define PORT_TX_QUEUE_DISABLED (PORT_ERR_BASE + 23) +#define PORT_PAGE_TIMEOUT (PORT_ERR_BASE + 24) +#define PORT_INVALID_SCN (PORT_ERR_BASE + 25) + +#define PORT_ERR_MAX (PORT_ERR_BASE + 26) + +/***************************************************************************** +** External Function Declarations +*****************************************************************************/ +#ifdef __cplusplus +extern "C" +{ +#endif + +/******************************************************************************* +** +** Function RFCOMM_CreateConnection +** +** Description RFCOMM_CreateConnection function is used from the application +** to establish serial port connection to the peer device, +** or allow RFCOMM to accept a connection from the peer +** application. +** +** Parameters: scn - Service Channel Number as registered with +** the SDP (server) or obtained using SDP from +** the peer device (client). +** is_server - TRUE if requesting application is a server +** mtu - Maximum frame size the application can accept +** bd_addr - BD_ADDR of the peer (client) +** mask - specifies events to be enabled. A value +** of zero disables all events. +** p_handle - OUT pointer to the handle. +** p_mgmt_cb - pointer to callback function to receive +** connection up/down events. +** Notes: +** +** Server can call this function with the same scn parameter multiple times if +** it is ready to accept multiple simulteneous connections. +** +** DLCI for the connection is (scn * 2 + 1) if client originates connection on +** existing none initiator multiplexer channel. Otherwise it is (scn * 2). +** For the server DLCI can be changed later if client will be calling it using +** (scn * 2 + 1) dlci. +** +*******************************************************************************/ +extern int RFCOMM_CreateConnection (UINT16 uuid, UINT8 scn, + BOOLEAN is_server, UINT16 mtu, + BD_ADDR bd_addr, UINT16 *p_handle, + tPORT_MGMT_CALLBACK *p_mgmt_cb); + + +/******************************************************************************* +** +** Function RFCOMM_RemoveConnection +** +** Description This function is called to close the specified connection. +** +** Parameters: handle - Handle of the port returned in the Open +** +*******************************************************************************/ +extern int RFCOMM_RemoveConnection (UINT16 handle); + + +/******************************************************************************* +** +** Function RFCOMM_RemoveServer +** +** Description This function is called to close the server port. +** +** Parameters: handle - Handle returned in the RFCOMM_CreateConnection +** +*******************************************************************************/ +extern int RFCOMM_RemoveServer (UINT16 handle); + + +/******************************************************************************* +** +** Function PORT_SetEventCallback +** +** Description Set event callback the specified connection. +** +** Parameters: handle - Handle of the port returned in the Open +** p_callback - address of the callback function which should +** be called from the RFCOMM when an event +** specified in the mask occurs. +** +*******************************************************************************/ +extern int PORT_SetEventCallback (UINT16 port_handle, + tPORT_CALLBACK *p_port_cb); + +/******************************************************************************* +** +** Function PORT_ClearKeepHandleFlag +** +** Description This function is called to clear the keep handle flag +** which will cause not to keep the port handle open when closed +** Parameters: handle - Handle returned in the RFCOMM_CreateConnection +** +*******************************************************************************/ +int PORT_ClearKeepHandleFlag (UINT16 port_handle); + +/******************************************************************************* +** +** Function PORT_SetEventCallback +** +** Description Set event data callback the specified connection. +** +** Parameters: handle - Handle of the port returned in the Open +** p_callback - address of the callback function which should +** be called from the RFCOMM when a data +** packet is received. +** +*******************************************************************************/ +extern int PORT_SetDataCallback (UINT16 port_handle, + tPORT_DATA_CALLBACK *p_cb); + +extern int PORT_SetDataCOCallback (UINT16 port_handle, tPORT_DATA_CO_CALLBACK *p_port_cb); +/******************************************************************************* +** +** Function PORT_SetEventMask +** +** Description This function is called to close the specified connection. +** +** Parameters: handle - Handle of the port returned in the Open +** mask - specifies events to be enabled. A value +** of zero disables all events. +** +*******************************************************************************/ +extern int PORT_SetEventMask (UINT16 port_handle, UINT32 mask); + + +/******************************************************************************* +** +** Function PORT_CheckConnection +** +** Description This function returns PORT_SUCCESS if connection referenced +** by handle is up and running +** +** Parameters: handle - Handle of the port returned in the Open +** ignore_rfc_state - If need to ignore rfc state +** bd_addr - OUT bd_addr of the peer +** p_lcid - OUT L2CAP's LCID +** +*******************************************************************************/ +extern int PORT_CheckConnection (UINT16 handle, BOOLEAN ignore_rfc_state, BD_ADDR bd_addr, + UINT16 *p_lcid); + +/******************************************************************************* +** +** Function PORT_IsOpening +** +** Description This function returns TRUE if there is any RFCOMM connection +** opening in process. +** +** Parameters: TRUE if any connection opening is found +** bd_addr - bd_addr of the peer +** +*******************************************************************************/ +extern BOOLEAN PORT_IsOpening (BD_ADDR bd_addr); + +/******************************************************************************* +** +** Function PORT_SetState +** +** Description This function configures connection according to the +** specifications in the tPORT_STATE structure. +** +** Parameters: handle - Handle returned in the RFCOMM_CreateConnection +** p_settings - Pointer to a tPORT_STATE structure containing +** configuration information for the connection. +** +*******************************************************************************/ +extern int PORT_SetState (UINT16 handle, tPORT_STATE *p_settings); + +/******************************************************************************* +** +** Function PORT_GetRxQueueCnt +** +** Description This function return number of buffers on the rx queue. +** +** Parameters: handle - Handle returned in the RFCOMM_CreateConnection +** p_rx_queue_count - Pointer to return queue count in. +** +*******************************************************************************/ +extern int PORT_GetRxQueueCnt (UINT16 handle, UINT16 *p_rx_queue_count); + +/******************************************************************************* +** +** Function PORT_GetState +** +** Description This function is called to fill tPORT_STATE structure +** with the current control settings for the port +** +** Parameters: handle - Handle returned in the RFCOMM_CreateConnection +** p_settings - Pointer to a tPORT_STATE structure in which +** configuration information is returned. +** +*******************************************************************************/ +extern int PORT_GetState (UINT16 handle, tPORT_STATE *p_settings); + + +/******************************************************************************* +** +** Function PORT_Control +** +** Description This function directs a specified connection to pass control +** control information to the peer device. +** +** Parameters: handle - Handle returned in the RFCOMM_CreateConnection +** signal - specify the function to be passed +** +*******************************************************************************/ +#define PORT_SET_DTRDSR 0x01 +#define PORT_CLR_DTRDSR 0x02 +#define PORT_SET_CTSRTS 0x03 +#define PORT_CLR_CTSRTS 0x04 +#define PORT_SET_RI 0x05 /* DCE only */ +#define PORT_CLR_RI 0x06 /* DCE only */ +#define PORT_SET_DCD 0x07 /* DCE only */ +#define PORT_CLR_DCD 0x08 /* DCE only */ +#define PORT_BREAK 0x09 /* Break event */ + +extern int PORT_Control (UINT16 handle, UINT8 signal); + + +/******************************************************************************* +** +** Function PORT_FlowControl +** +** Description This function directs a specified connection to pass +** flow control message to the peer device. Enable flag passed +** shows if port can accept more data. +** +** Parameters: handle - Handle returned in the RFCOMM_CreateConnection +** enable - enables data flow +** +*******************************************************************************/ +extern int PORT_FlowControl (UINT16 handle, BOOLEAN enable); + +/******************************************************************************* +** +** Function PORT_FlowControl_GiveCredit +** +** Description This function gives specified credits to the peer +** +** Parameters: handle - Handle returned in the RFCOMM_CreateConnection +** enable - enables data flow +** credits_given - credits to give +** +*******************************************************************************/ +extern int PORT_FlowControl_GiveCredit (UINT16 handle, BOOLEAN enable, UINT16 credits_given); +/******************************************************************************* +** +** Function PORT_GetModemStatus +** +** Description This function retrieves modem control signals. Normally +** application will call this function after a callback +** function is called with notification that one of signals +** has been changed. +** +** Parameters: handle - Handle returned in the RFCOMM_CreateConnection +** callback. +** p_signal - specify the pointer to control signals info +** +*******************************************************************************/ +#define PORT_DTRDSR_ON 0x01 +#define PORT_CTSRTS_ON 0x02 +#define PORT_RING_ON 0x04 +#define PORT_DCD_ON 0x08 + +/* +** Define default initial local modem signals state set after connection established +*/ +#define PORT_OBEX_DEFAULT_SIGNAL_STATE (PORT_DTRDSR_ON | PORT_CTSRTS_ON | PORT_DCD_ON) +#define PORT_SPP_DEFAULT_SIGNAL_STATE (PORT_DTRDSR_ON | PORT_CTSRTS_ON | PORT_DCD_ON) +#define PORT_PPP_DEFAULT_SIGNAL_STATE (PORT_DTRDSR_ON | PORT_CTSRTS_ON | PORT_DCD_ON) +#define PORT_DUN_DEFAULT_SIGNAL_STATE (PORT_DTRDSR_ON | PORT_CTSRTS_ON) + +extern int PORT_GetModemStatus (UINT16 handle, UINT8 *p_control_signal); + + +/******************************************************************************* +** +** Function PORT_ClearError +** +** Description This function retreives information about a communications +** error and reports current status of a connection. The +** function should be called when an error occures to clear +** the connection error flag and to enable additional read +** and write operations. +** +** Parameters: handle - Handle returned in the RFCOMM_CreateConnection +** p_errors - pointer of the variable to receive error codes +** p_status - pointer to the tPORT_STATUS structur to receive +** connection status +** +*******************************************************************************/ + +#define PORT_ERR_BREAK 0x01 /* Break condition occured on the peer device */ +#define PORT_ERR_OVERRUN 0x02 /* Overrun is reported by peer device */ +#define PORT_ERR_FRAME 0x04 /* Framing error reported by peer device */ +#define PORT_ERR_RXOVER 0x08 /* Input queue overflow occured */ +#define PORT_ERR_TXFULL 0x10 /* Output queue overflow occured */ + +typedef struct { +#define PORT_FLAG_CTS_HOLD 0x01 /* Tx is waiting for CTS signal */ +#define PORT_FLAG_DSR_HOLD 0x02 /* Tx is waiting for DSR signal */ +#define PORT_FLAG_RLSD_HOLD 0x04 /* Tx is waiting for RLSD signal */ + + UINT16 flags; + UINT16 in_queue_size; /* Number of bytes in the input queue */ + UINT16 out_queue_size; /* Number of bytes in the output queue */ + UINT16 mtu_size; /* peer MTU size */ +} tPORT_STATUS; + + +extern int PORT_ClearError (UINT16 handle, UINT16 *p_errors, + tPORT_STATUS *p_status); + + +/******************************************************************************* +** +** Function PORT_SendError +** +** Description This function send a communications error to the peer device +** +** Parameters: handle - Handle returned in the RFCOMM_CreateConnection +** errors - receive error codes +** +*******************************************************************************/ +extern int PORT_SendError (UINT16 handle, UINT8 errors); + + +/******************************************************************************* +** +** Function PORT_GetQueueStatus +** +** Description This function reports current status of a connection. +** +** Parameters: handle - Handle returned in the RFCOMM_CreateConnection +** p_status - pointer to the tPORT_STATUS structur to receive +** connection status +** +*******************************************************************************/ +extern int PORT_GetQueueStatus (UINT16 handle, tPORT_STATUS *p_status); + + +/******************************************************************************* +** +** Function PORT_Purge +** +** Description This function discards all the data from the output or +** input queues of the specified connection. +** +** Parameters: handle - Handle returned in the RFCOMM_CreateConnection +** purge_flags - specify the action to take. +** +*******************************************************************************/ +#define PORT_PURGE_TXCLEAR 0x01 +#define PORT_PURGE_RXCLEAR 0x02 + +extern int PORT_Purge (UINT16 handle, UINT8 purge_flags); + + +/******************************************************************************* +** +** Function PORT_Read +** +** Description This function returns the pointer to the buffer received +** from the peer device. Normally application will call this +** function after receiving PORT_EVT_RXCHAR event. +** Application calling this function is responsible to free +** buffer returned. +** +** Parameters: handle - Handle returned in the RFCOMM_CreateConnection +** callback. +** pp_buf - pointer to address of buffer with data, +** +*******************************************************************************/ +extern int PORT_Read (UINT16 handle, BT_HDR **pp_buf); + + +/******************************************************************************* +** +** Function PORT_ReadData +** +** Description Normally application will call this function after receiving +** PORT_EVT_RXCHAR event. +** +** Parameters: handle - Handle returned in the RFCOMM_CreateConnection +** callback. +** p_data - Data area +** max_len - Byte count requested +** p_len - Byte count received +** +*******************************************************************************/ +extern int PORT_ReadData (UINT16 handle, char *p_data, UINT16 max_len, + UINT16 *p_len); + + +/******************************************************************************* +** +** Function PORT_Write +** +** Description This function to send BT buffer to the peer device. +** Application should not free the buffer. +** +** Parameters: handle - Handle returned in the RFCOMM_CreateConnection +** p_buf - pointer to the buffer with data, +** +*******************************************************************************/ +extern int PORT_Write (UINT16 handle, BT_HDR *p_buf); + + +/******************************************************************************* +** +** Function PORT_WriteData +** +** Description This function is called from the legacy application to +** send data. +** +** Parameters: handle - Handle returned in the RFCOMM_CreateConnection +** p_data - Data area +** max_len - Byte count to write +** p_len - Bytes written +** +*******************************************************************************/ +extern int PORT_WriteData (UINT16 handle, char *p_data, UINT16 max_len, + UINT16 *p_len); + +/******************************************************************************* +** +** Function PORT_WriteDataCO +** +** Description Normally not GKI aware application will call this function +** to send data to the port by callout functions. +** +** Parameters: handle - Handle returned in the RFCOMM_CreateConnection +** +*******************************************************************************/ +extern int PORT_WriteDataCO (UINT16 handle, int *p_len, int len, UINT8 *p_data); + +/******************************************************************************* +** +** Function PORT_Test +** +** Description Application can call this function to send RFCOMM Test frame +** +** Parameters: handle - Handle returned in the RFCOMM_CreateConnection +** p_data - Data area +** max_len - Byte count requested +** +*******************************************************************************/ +extern int PORT_Test (UINT16 handle, UINT8 *p_data, UINT16 len); + + +/******************************************************************************* +** +** Function RFCOMM_Init +** +** Description This function is called to initialize RFCOMM layer +** +*******************************************************************************/ +extern bt_status_t RFCOMM_Init (void); + +/******************************************************************************* +** +** Function RFCOMM_Deinit +** +** Description This function is called to deinitialize the control block +** for this layer. +** +** Returns void +** +*******************************************************************************/ +extern void RFCOMM_Deinit(void); + +/******************************************************************************* +** +** Function PORT_SetTraceLevel +** +** Description This function sets the trace level for RFCOMM. If called with +** a value of 0xFF, it simply reads the current trace level. +** +** Returns the new (current) trace level +** +*******************************************************************************/ +extern UINT8 PORT_SetTraceLevel (UINT8 new_level); + + +/******************************************************************************* +** +** Function PORT_GetResultString +** +** Description This function returns the human-readable string for a given +** result code. +** +** Returns a pointer to the human-readable string for the given +** result. Note that the string returned must not be freed. +** +*******************************************************************************/ +extern const char *PORT_GetResultString (const uint8_t result_code); + +/******************************************************************************* +** +** Function PORT_SetL2capErtm +** +** Description This function sets whether RFCOMM uses L2CAP ERTM. +** +** Returns void +** +*******************************************************************************/ +extern void PORT_SetL2capErtm (BOOLEAN enable_l2cap_ertm); + +#ifdef __cplusplus +} +#endif + +#endif /* PORT_API_H */ diff --git a/lib/bt/host/bluedroid/stack/include/stack/port_ext.h b/lib/bt/host/bluedroid/stack/include/stack/port_ext.h new file mode 100644 index 00000000..40cdbfce --- /dev/null +++ b/lib/bt/host/bluedroid/stack/include/stack/port_ext.h @@ -0,0 +1,31 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains external definitions of Port Emulation entity unit + * + ******************************************************************************/ + +#ifndef PORTEXT_H +#define PORTEXT_H + + +/* Port emulation entity Entry Points */ +extern void rfcomm_process_timeout (TIMER_LIST_ENT *p_tle); +#endif diff --git a/lib/bt/host/bluedroid/stack/include/stack/profiles_api.h b/lib/bt/host/bluedroid/stack/include/stack/profiles_api.h new file mode 100644 index 00000000..4bbf5a22 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/include/stack/profiles_api.h @@ -0,0 +1,69 @@ +/****************************************************************************** + * + * Copyright (C) 2009-2013 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +#ifndef PROFILES_API_H +#define PROFILES_API_H + +#include "common/bt_target.h" +#include "stack/btm_api.h" + +/***************************************************************************** +** Constants +*****************************************************************************/ +#define BT_PASS 0 /* Used for general successful function returns */ + +/*** Port entity passes back 8 bit errors; will use upper byte offset ***/ +#define PORT_ERR_GRP 0x0000 /* base offset for port entity */ +#define GAP_ERR_GRP 0x0100 /* base offset for GAP profile */ +#define SPP_ERR_GRP 0x0200 /* base offset for serial port profile */ +#define HCRP_ERR_GRP 0x0300 /* base offset for HCRP */ +#define HCRPM_ERR_GRP 0x0400 /* base offset for HCRPM */ + +/* #define HSP2_ERR_GRP 0x0F00 */ + +/* security level definitions (tBT_SECURITY) */ +#define BT_USE_DEF_SECURITY 0 +#define BT_SEC_MODE_NONE BTM_SEC_MODE_NONE +#define BT_SEC_MODE_SERVICE BTM_SEC_MODE_SERVICE +#define BT_SEC_MODE_LINK BTM_SEC_MODE_LINK + +/* security mask definitions (tBT_SECURITY) */ +/* The following definitions are OR'd together to form the security requirements */ +#define BT_SEC_IN_AUTHORIZE BTM_SEC_IN_AUTHORIZE /* Inbound call requires authorization */ +#define BT_SEC_IN_AUTHENTICATE BTM_SEC_IN_AUTHENTICATE /* Inbound call requires authentication */ +#define BT_SEC_IN_ENCRYPT BTM_SEC_IN_ENCRYPT /* Inbound call requires encryption */ +#define BT_SEC_OUT_AUTHORIZE BTM_SEC_OUT_AUTHORIZE /* Outbound call requires authorization */ +#define BT_SEC_OUT_AUTHENTICATE BTM_SEC_OUT_AUTHENTICATE /* Outbound call requires authentication */ +#define BT_SEC_OUT_ENCRYPT BTM_SEC_OUT_ENCRYPT /* Outbound call requires encryption */ + + +/***************************************************************************** +** Type Definitions +*****************************************************************************/ + +/* +** Security Definitions +** This following definitions are used to indicate the security +** requirements for a service. +*/ +typedef struct { + UINT8 level; + UINT8 mask; +} tBT_SECURITY; + +#endif /* PROFILES_API_H */ diff --git a/lib/bt/host/bluedroid/stack/include/stack/rfcdefs.h b/lib/bt/host/bluedroid/stack/include/stack/rfcdefs.h new file mode 100644 index 00000000..dcc37bc7 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/include/stack/rfcdefs.h @@ -0,0 +1,248 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/**************************************************************************** + * + * This file contains definitions for the RFCOMM protocol + * + ****************************************************************************/ + +#ifndef RFCDEFS_H +#define RFCDEFS_H + +#define PORT_MAX_RFC_PORTS 31 + +/* +** If nothing is negotiated MTU should be 127 +*/ +#define RFCOMM_DEFAULT_MTU 127 + +/* +** Define used by RFCOMM TS frame types +*/ +#define RFCOMM_SABME 0x2F +#define RFCOMM_UA 0x63 +#define RFCOMM_DM 0x0F +#define RFCOMM_DISC 0x43 +#define RFCOMM_UIH 0xEF + +/* +** Defenitions for the TS control frames +*/ +#define RFCOMM_CTRL_FRAME_LEN 3 +#define RFCOMM_MIN_OFFSET 5 /* ctrl 2 , len 1 or 2 bytes, credit 1 byte */ +#define RFCOMM_DATA_OVERHEAD (RFCOMM_MIN_OFFSET + 1) /* add 1 for checksum */ + +#define RFCOMM_EA 1 +#define RFCOMM_EA_MASK 0x01 +#define RFCOMM_CR_MASK 0x02 +#define RFCOMM_SHIFT_CR 1 +#define RFCOMM_SHIFT_DLCI 2 +#define RFCOMM_SHIFT_DLCI2 6 +#define RFCOMM_PF 0x10 +#define RFCOMM_PF_MASK 0x10 +#define RFCOMM_PF_OFFSET 4 +#define RFCOMM_SHIFT_LENGTH1 1 +#define RFCOMM_SHIFT_LENGTH2 7 +#define RFCOMM_SHIFT_MX_CTRL_TYPE 2 + +#define RFCOMM_INITIATOR_CMD 1 +#define RFCOMM_INITIATOR_RSP 0 +#define RFCOMM_RESPONDER_CMD 0 +#define RFCOMM_RESPONDER_RSP 1 + +#define RFCOMM_PARSE_CTRL_FIELD(ea, cr, dlci, p_data) \ +{ \ + ea = *p_data & RFCOMM_EA; \ + cr = (*p_data & RFCOMM_CR_MASK) >> RFCOMM_SHIFT_CR; \ + dlci = *p_data++ >> RFCOMM_SHIFT_DLCI; \ + if (!ea) dlci += *p_data++ << RFCOMM_SHIFT_DLCI2; \ +} + +#define RFCOMM_FORMAT_CTRL_FIELD(p_data, ea, cr, dlci) \ + *p_data++ = ea | cr | (dlci << RFCOMM_SHIFT_DLCI) + +#define RFCOMM_PARSE_TYPE_FIELD(type, pf, p_data) \ +{ \ + type = *p_data & ~RFCOMM_PF_MASK; \ + pf = (*p_data++ & RFCOMM_PF_MASK) >> RFCOMM_PF_OFFSET;\ +} + +#define RFCOMM_FORMAT_TYPE_FIELD(p_data, type, pf) \ + *p_data++ = (type | (pf << RFCOMM_PF_OFFSET)) \ +{ \ + type = *p_data & ~RFCOMM_PF_MASK; \ + pf = (*p_data++ & RFCOMM_PF_MASK) >> RFCOMM_PF_OFFSET;\ +} + +#define RFCOMM_PARSE_LEN_FIELD(ea, length, p_data) \ +{ \ + ea = (*p_data & RFCOMM_EA); \ + length = (*p_data++ >> RFCOMM_SHIFT_LENGTH1); \ + if (!ea) length += (*p_data++ << RFCOMM_SHIFT_LENGTH2); \ +} + +#define RFCOMM_FRAME_IS_CMD(initiator, cr) \ + (( (initiator) && !(cr)) || (!(initiator) && (cr))) + +#define RFCOMM_FRAME_IS_RSP(initiator, cr) \ + (( (initiator) && (cr)) || (!(initiator) && !(cr))) + +#define RFCOMM_CR(initiator, is_command) \ + (( ( (initiator) && (is_command)) \ + || (!(initiator) && !(is_command))) << 1) + +#define RFCOMM_I_CR(is_command) ((is_command) ? 0x02 : 0x00) + +#define RFCOMM_MAX_DLCI 61 + +#define RFCOMM_VALID_DLCI(dlci) \ + (((dlci) == 0) || (((dlci) >= 2) && ((dlci) <= RFCOMM_MAX_DLCI))) + + +/* Port Negotiation (PN) */ +#define RFCOMM_PN_DLCI_MASK 0x3F + +#define RFCOMM_PN_FRAM_TYPE_UIH 0x00 +#define RFCOMM_PN_FRAME_TYPE_MASK 0x0F + +#define RFCOMM_PN_CONV_LAYER_MASK 0xF0 +#define RFCOMM_PN_CONV_LAYER_TYPE_1 0 +#define RFCOMM_PN_CONV_LAYER_CBFC_I 0xF0 +#define RFCOMM_PN_CONV_LAYER_CBFC_R 0xE0 + +#define RFCOMM_PN_PRIORITY_MASK 0x3F +#define RFCOMM_PN_PRIORITY_0 0 + +#define RFCOMM_PN_K_MASK 0x07 + +#define RFCOMM_T1_DSEC 0 /* None negotiable in RFCOMM */ +#define RFCOMM_N2 0 /* Number of retransmissions */ +#define RFCOMM_K 0 /* Window size */ +#define RFCOMM_K_MAX 7 /* Max value of K for credit based flow control */ + +#define RFCOMM_MSC_FC 0x02 /* Flow control*/ +#define RFCOMM_MSC_RTC 0x04 /* Ready to communicate*/ +#define RFCOMM_MSC_RTR 0x08 /* Ready to receive*/ +#define RFCOMM_MSC_IC 0x40 /* Incomming call indicator*/ +#define RFCOMM_MSC_DV 0x80 /* Data Valid*/ + +#define RFCOMM_MSC_SHIFT_BREAK 4 +#define RFCOMM_MSC_BREAK_MASK 0xF0 +#define RFCOMM_MSC_BREAK_PRESENT_MASK 0x02 + +#define RFCOMM_BAUD_RATE_2400 0x00 +#define RFCOMM_BAUD_RATE_4800 0x01 +#define RFCOMM_BAUD_RATE_7200 0x02 +#define RFCOMM_BAUD_RATE_9600 0x03 +#define RFCOMM_BAUD_RATE_19200 0x04 +#define RFCOMM_BAUD_RATE_38400 0x05 +#define RFCOMM_BAUD_RATE_57600 0x06 +#define RFCOMM_BAUD_RATE_115200 0x07 +#define RFCOMM_BAUD_RATE_230400 0x08 + +#define RFCOMM_5_BITS 0x00 +#define RFCOMM_6_BITS 0x01 +#define RFCOMM_7_BITS 0x02 +#define RFCOMM_8_BITS 0x03 + +#define RFCOMM_RPN_BITS_MASK 0x03 +#define RFCOMM_RPN_BITS_SHIFT 0 + +#define RFCOMM_ONESTOPBIT 0x00 +#define RFCOMM_ONE5STOPBITS 0x01 + +#define RFCOMM_RPN_STOP_BITS_MASK 0x01 +#define RFCOMM_RPN_STOP_BITS_SHIFT 2 + +#define RFCOMM_PARITY_NO 0x00 +#define RFCOMM_PARITY_YES 0x01 +#define RFCOMM_RPN_PARITY_MASK 0x01 +#define RFCOMM_RPN_PARITY_SHIFT 3 + +#define RFCOMM_ODD_PARITY 0x00 +#define RFCOMM_EVEN_PARITY 0x01 +#define RFCOMM_MARK_PARITY 0x02 +#define RFCOMM_SPACE_PARITY 0x03 + +#define RFCOMM_RPN_PARITY_TYPE_MASK 0x03 +#define RFCOMM_RPN_PARITY_TYPE_SHIFT 4 + +#define RFCOMM_FC_OFF 0x00 +#define RFCOMM_FC_XONXOFF_ON_INPUT 0x01 +#define RFCOMM_FC_XONXOFF_ON_OUTPUT 0x02 +#define RFCOMM_FC_RTR_ON_INPUT 0x04 +#define RFCOMM_FC_RTR_ON_OUTPUT 0x08 +#define RFCOMM_FC_RTC_ON_INPUT 0x10 +#define RFCOMM_FC_RTC_ON_OUTPUT 0x20 +#define RFCOMM_FC_MASK 0x3F + +#define RFCOMM_RPN_PM_BIT_RATE 0x0001 +#define RFCOMM_RPN_PM_DATA_BITS 0x0002 +#define RFCOMM_RPN_PM_STOP_BITS 0x0004 +#define RFCOMM_RPN_PM_PARITY 0x0008 +#define RFCOMM_RPN_PM_PARITY_TYPE 0x0010 +#define RFCOMM_RPN_PM_XON_CHAR 0x0020 +#define RFCOMM_RPN_PM_XOFF_CHAR 0x0040 +#define RFCOMM_RPN_PM_XONXOFF_ON_INPUT 0x0100 +#define RFCOMM_RPN_PM_XONXOFF_ON_OUTPUT 0x0200 +#define RFCOMM_RPN_PM_RTR_ON_INPUT 0x0400 +#define RFCOMM_RPN_PM_RTR_ON_OUTPUT 0x0800 +#define RFCOMM_RPN_PM_RTC_ON_INPUT 0x1000 +#define RFCOMM_RPN_PM_RTC_ON_OUTPUT 0x2000 +#define RFCOMM_RPN_PM_MASK 0x3F7F + +#define RFCOMM_RLS_ERROR 0x01 +#define RFCOMM_RLS_OVERRUN 0x02 +#define RFCOMM_RLS_PARITY 0x04 +#define RFCOMM_RLS_FRAMING 0x08 + +/* Multiplexor channel uses DLCI 0 */ +#define RFCOMM_MX_DLCI 0 + +/* +** Define RFCOMM Multiplexer message types +*/ +#define RFCOMM_MX_PN 0x80 +#define RFCOMM_MX_PN_LEN 8 + +#define RFCOMM_MX_CLD 0xC0 +#define RFCOMM_MX_CLD_LEN 0 + +#define RFCOMM_MX_TEST 0x20 + +#define RFCOMM_MX_FCON 0xA0 +#define RFCOMM_MX_FCON_LEN 0 + +#define RFCOMM_MX_FCOFF 0x60 +#define RFCOMM_MX_FCOFF_LEN 0 + +#define RFCOMM_MX_MSC 0xE0 +#define RFCOMM_MX_MSC_LEN_NO_BREAK 2 +#define RFCOMM_MX_MSC_LEN_WITH_BREAK 3 + +#define RFCOMM_MX_NSC 0x10 +#define RFCOMM_MX_NSC_LEN 1 + +#define RFCOMM_MX_RPN 0x90 +#define RFCOMM_MX_RPN_REQ_LEN 1 +#define RFCOMM_MX_RPN_LEN 8 + +#define RFCOMM_MX_RLS 0x50 +#define RFCOMM_MX_RLS_LEN 2 +#endif diff --git a/lib/bt/host/bluedroid/stack/include/stack/sdp_api.h b/lib/bt/host/bluedroid/stack/include/stack/sdp_api.h new file mode 100644 index 00000000..70da3182 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/include/stack/sdp_api.h @@ -0,0 +1,726 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ +#ifndef SDP_API_H +#define SDP_API_H + +#include "common/bt_target.h" +#include "stack/sdpdefs.h" +#if (SDP_INCLUDED == TRUE) +/***************************************************************************** +** Constants +*****************************************************************************/ + +/* Success code and error codes */ +#define SDP_SUCCESS 0x0000 +#define SDP_INVALID_VERSION 0x0001 +#define SDP_INVALID_SERV_REC_HDL 0x0002 +#define SDP_INVALID_REQ_SYNTAX 0x0003 +#define SDP_INVALID_PDU_SIZE 0x0004 +#define SDP_INVALID_CONT_STATE 0x0005 +#define SDP_NO_RESOURCES 0x0006 +#define SDP_DI_REG_FAILED 0x0007 +#define SDP_DI_DISC_FAILED 0x0008 +#define SDP_NO_DI_RECORD_FOUND 0x0009 +#define SDP_ERR_ATTR_NOT_PRESENT 0x000A +#define SDP_ILLEGAL_PARAMETER 0x000B + +#define SDP_NO_RECS_MATCH 0xFFF0 +#define SDP_CONN_FAILED 0xFFF1 +#define SDP_CFG_FAILED 0xFFF2 +#define SDP_GENERIC_ERROR 0xFFF3 +#define SDP_DB_FULL 0xFFF4 +#define SDP_INVALID_PDU 0xFFF5 +#define SDP_SECURITY_ERR 0xFFF6 +#define SDP_CONN_REJECTED 0xFFF7 +#define SDP_CANCEL 0xFFF8 + +/* Define the PSM that SDP uses */ +#define SDP_PSM 0x0001 + +/* Legacy #define to avoid code changes - SDP UUID is same as BT UUID */ +#define tSDP_UUID tBT_UUID + +/* Masks for attr_value field of tSDP_DISC_ATTR */ +#define SDP_DISC_ATTR_LEN_MASK 0x0FFF +#define SDP_DISC_ATTR_TYPE(len_type) (len_type >> 12) +#define SDP_DISC_ATTR_LEN(len_type) (len_type & SDP_DISC_ATTR_LEN_MASK) + +/* Maximum number of protocol list items (list_elem in tSDP_PROTOCOL_ELEM) */ +#define SDP_MAX_LIST_ELEMS 3 + + +/***************************************************************************** +** Type Definitions +*****************************************************************************/ + +/* Define a callback function for when discovery is complete. */ +typedef void (tSDP_DISC_CMPL_CB) (UINT16 result); +typedef void (tSDP_DISC_CMPL_CB2) (UINT16 result, void *user_data); + +typedef struct { + BD_ADDR peer_addr; + UINT16 peer_mtu; +} tSDP_DR_OPEN; + +typedef struct { + UINT8 *p_data; + UINT16 data_len; +} tSDP_DR_DATA; + +typedef union { + tSDP_DR_OPEN open; + tSDP_DR_DATA data; +} tSDP_DATA; + +/* Define a callback function for when discovery result is received. */ +typedef void (tSDP_DISC_RES_CB) (UINT16 event, tSDP_DATA *p_data); + +/* Define a structure to hold the discovered service information. */ +typedef struct { + union { + UINT8 u8; /* 8-bit integer */ + UINT16 u16; /* 16-bit integer */ + UINT32 u32; /* 32-bit integer */ + UINT8 array[4]; /* Variable length field */ + struct t_sdp_disc_attr *p_sub_attr; /* Addr of first sub-attr (list)*/ + } v; + +} tSDP_DISC_ATVAL; + +typedef struct t_sdp_disc_attr { + struct t_sdp_disc_attr *p_next_attr; /* Addr of next linked attr */ + UINT16 attr_id; /* Attribute ID */ + UINT16 attr_len_type; /* Length and type fields */ + tSDP_DISC_ATVAL attr_value; /* Variable length entry data */ +} tSDP_DISC_ATTR; + +typedef struct t_sdp_disc_rec { + tSDP_DISC_ATTR *p_first_attr; /* First attribute of record */ + struct t_sdp_disc_rec *p_next_rec; /* Addr of next linked record */ + UINT32 time_read; /* The time the record was read */ + BD_ADDR remote_bd_addr; /* Remote BD address */ +} tSDP_DISC_REC; + +typedef struct { + UINT32 mem_size; /* Memory size of the DB */ + UINT32 mem_free; /* Memory still available */ + tSDP_DISC_REC *p_first_rec; /* Addr of first record in DB */ + UINT16 num_uuid_filters; /* Number of UUIds to filter */ + tSDP_UUID uuid_filters[SDP_MAX_UUID_FILTERS]; /* UUIDs to filter */ + UINT16 num_attr_filters; /* Number of attribute filters */ + UINT16 attr_filters[SDP_MAX_ATTR_FILTERS]; /* Attributes to filter */ + UINT8 *p_free_mem; /* Pointer to free memory */ +#if (SDP_RAW_DATA_INCLUDED == TRUE) + UINT8 *raw_data; /* Received record from server. allocated/released by client */ + UINT32 raw_size; /* size of raw_data */ + UINT32 raw_used; /* length of raw_data used */ +#endif +} tSDP_DISCOVERY_DB; + +/* This structure is used to add protocol lists and find protocol elements */ +typedef struct { + UINT16 protocol_uuid; + UINT16 num_params; + UINT16 params[SDP_MAX_PROTOCOL_PARAMS]; +} tSDP_PROTOCOL_ELEM; + +typedef struct { + UINT16 num_elems; + tSDP_PROTOCOL_ELEM list_elem[SDP_MAX_LIST_ELEMS]; +} tSDP_PROTO_LIST_ELEM; + +/* Device Identification (DI) data structure +*/ +/* Used to set the DI record */ +typedef struct t_sdp_di_record { + UINT16 vendor; + UINT16 vendor_id_source; + UINT16 product; + UINT16 version; + BOOLEAN primary_record; + char client_executable_url[SDP_MAX_ATTR_LEN]; /* optional */ + char service_description[SDP_MAX_ATTR_LEN]; /* optional */ + char documentation_url[SDP_MAX_ATTR_LEN]; /* optional */ +} tSDP_DI_RECORD; + +/* Used to get the DI record */ +typedef struct t_sdp_di_get_record { + UINT16 spec_id; + tSDP_DI_RECORD rec; +} tSDP_DI_GET_RECORD; + + +/***************************************************************************** +** External Function Declarations +*****************************************************************************/ +#ifdef __cplusplus +extern "C" +{ +#endif + +/* API into the SDP layer for service discovery. */ + +/******************************************************************************* +** +** Function SDP_InitDiscoveryDb +** +** Description This function is called to initialize a discovery database. +** +** Returns TRUE if successful, FALSE if one or more parameters are bad +** +*******************************************************************************/ +extern BOOLEAN SDP_InitDiscoveryDb (tSDP_DISCOVERY_DB *p_db, UINT32 len, + UINT16 num_uuid, + tSDP_UUID *p_uuid_list, + UINT16 num_attr, + UINT16 *p_attr_list); + +/******************************************************************************* +** +** Function SDP_CancelServiceSearch +** +** Description This function cancels an active query to an SDP server. +** +** Returns TRUE if discovery cancelled, FALSE if a matching activity is not found. +** +*******************************************************************************/ +extern BOOLEAN SDP_CancelServiceSearch (tSDP_DISCOVERY_DB *p_db); + +/******************************************************************************* +** +** Function SDP_ServiceSearchRequest +** +** Description This function queries an SDP server for information. +** +** Returns TRUE if discovery started, FALSE if failed. +** +*******************************************************************************/ +extern BOOLEAN SDP_ServiceSearchRequest (UINT8 *p_bd_addr, + tSDP_DISCOVERY_DB *p_db, + tSDP_DISC_CMPL_CB *p_cb); + + +/******************************************************************************* +** +** Function SDP_ServiceSearchAttributeRequest +** +** Description This function queries an SDP server for information. +** +** The difference between this API function and the function +** SDP_ServiceSearchRequest2 is that this one does a +** combined ServiceSearchAttributeRequest SDP function. +** +** Returns TRUE if discovery started, FALSE if failed. +** +*******************************************************************************/ +extern BOOLEAN SDP_ServiceSearchAttributeRequest (UINT8 *p_bd_addr, + tSDP_DISCOVERY_DB *p_db, + tSDP_DISC_CMPL_CB *p_cb); + +/******************************************************************************* +** +** Function SDP_ServiceSearchAttributeRequest2 +** +** Description This function queries an SDP server for information. +** +** The difference between this API function and the function +** SDP_ServiceSearchRequest is that this one does a +** combined ServiceSearchAttributeRequest SDP function with the +** user data piggyback +** +** Returns TRUE if discovery started, FALSE if failed. +** +*******************************************************************************/ +extern BOOLEAN SDP_ServiceSearchAttributeRequest2 (UINT8 *p_bd_addr, + tSDP_DISCOVERY_DB *p_db, + tSDP_DISC_CMPL_CB2 *p_cb, void *user_data); + +/* API of utilities to find data in the local discovery database */ + +/******************************************************************************* +** +** Function SDP_FindAttributeInDb +** +** Description This function queries an SDP database for a specific attribute. +** If the p_start_rec pointer is NULL, it looks from the beginning +** of the database, else it continues from the next record after +** p_start_rec. +** +** Returns Pointer to matching record, or NULL +** +*******************************************************************************/ +extern tSDP_DISC_REC *SDP_FindAttributeInDb (tSDP_DISCOVERY_DB *p_db, + UINT16 attr_id, + tSDP_DISC_REC *p_start_rec); + + +/******************************************************************************* +** +** Function SDP_FindAttributeInRec +** +** Description This function searches an SDP discovery record for a +** specific attribute. +** +** Returns Pointer to matching attribute entry, or NULL +** +*******************************************************************************/ +extern tSDP_DISC_ATTR *SDP_FindAttributeInRec (tSDP_DISC_REC *p_rec, + UINT16 attr_id); + + +/******************************************************************************* +** +** Function SDP_FindServiceInDb +** +** Description This function queries an SDP database for a specific service. +** If the p_start_rec pointer is NULL, it looks from the beginning +** of the database, else it continues from the next record after +** p_start_rec. +** +** Returns Pointer to record containing service class, or NULL +** +*******************************************************************************/ +extern tSDP_DISC_REC *SDP_FindServiceInDb (tSDP_DISCOVERY_DB *p_db, + UINT16 service_uuid, + tSDP_DISC_REC *p_start_rec); + + +/******************************************************************************* +** +** Function SDP_FindServiceUUIDInDb +** +** Description This function queries an SDP database for a specific service. +** If the p_start_rec pointer is NULL, it looks from the beginning +** of the database, else it continues from the next record after +** p_start_rec. +** +** NOTE the only difference between this function and the previous +** function "SDP_FindServiceInDb()" is that this function takes +** a tBT_UUID input. +** +** Returns Pointer to record containing service class, or NULL +** +*******************************************************************************/ +extern tSDP_DISC_REC *SDP_FindServiceUUIDInDb (tSDP_DISCOVERY_DB *p_db, + tBT_UUID *p_uuid, + tSDP_DISC_REC *p_start_rec); + +/******************************************************************************* +** +** Function SDP_FindServiceUUIDInRec_128bit +** +** Description This function is called to read the 128-bit service UUID within a record +** if there is any. +** +** Parameters: p_rec - pointer to a SDP record. +** p_uuid - output parameter to save the UUID found. +** +** Returns TRUE if found, otherwise FALSE. +** +*******************************************************************************/ +extern BOOLEAN SDP_FindServiceUUIDInRec_128bit(tSDP_DISC_REC *p_rec, tBT_UUID *p_uuid); + +/******************************************************************************* +** +** Function SDP_FindServiceInDb_128bit +** +** Description This function queries an SDP database for a specific service. +** If the p_start_rec pointer is NULL, it looks from the beginning +** of the database, else it continues from the next record after +** p_start_rec. +** +** Returns Pointer to record containing service class, or NULL +** +*******************************************************************************/ +extern tSDP_DISC_REC *SDP_FindServiceInDb_128bit(tSDP_DISCOVERY_DB *p_db, + tSDP_DISC_REC *p_start_rec); + +/******************************************************************************* +** +** Function SDP_FindProtocolListElemInRec +** +** Description This function looks at a specific discovery record for a +** protocol list element. +** +** Returns TRUE if found, FALSE if not +** If found, the passed protocol list element is filled in. +** +*******************************************************************************/ +extern BOOLEAN SDP_FindProtocolListElemInRec (tSDP_DISC_REC *p_rec, + UINT16 layer_uuid, + tSDP_PROTOCOL_ELEM *p_elem); + + +/******************************************************************************* +** +** Function SDP_FindAddProtoListsElemInRec +** +** Description This function looks at a specific discovery record for a +** protocol list element. +** +** Returns TRUE if found, FALSE if not +** If found, the passed protocol list element is filled in. +** +*******************************************************************************/ +extern BOOLEAN SDP_FindAddProtoListsElemInRec (tSDP_DISC_REC *p_rec, + UINT16 layer_uuid, + tSDP_PROTOCOL_ELEM *p_elem); + + +/******************************************************************************* +** +** Function SDP_FindProfileVersionInRec +** +** Description This function looks at a specific discovery record for the +** Profile list descriptor, and pulls out the version number. +** The version number consists of an 8-bit major version and +** an 8-bit minor version. +** +** Returns TRUE if found, FALSE if not +** If found, the major and minor version numbers that were passed +** in are filled in. +** +*******************************************************************************/ +extern BOOLEAN SDP_FindProfileVersionInRec (tSDP_DISC_REC *p_rec, + UINT16 profile_uuid, + UINT16 *p_version); + + +/* API into SDP for local service database updates */ + +/******************************************************************************* +** +** Function SDP_CreateRecord +** +** Description This function is called to create a record in the database. +** This would be through the SDP database maintenance API. The +** record is created empty, teh application should then call +** "add_attribute" to add the record's attributes. +** +** Returns Record handle if OK, else 0. +** +*******************************************************************************/ +extern UINT32 SDP_CreateRecord (void); + + +/******************************************************************************* +** +** Function SDP_DeleteRecord +** +** Description This function is called to add a record (or all records) +** from the database. This would be through the SDP database +** maintenance API. +** +** If a record handle of 0 is passed, all records are deleted. +** +** Returns TRUE if succeeded, else FALSE +** +*******************************************************************************/ +extern BOOLEAN SDP_DeleteRecord (UINT32 handle); + + +/******************************************************************************* +** +** Function SDP_ReadRecord +** +** Description This function is called to get the raw data of the record +** with the given handle from the database. +** +** Returns -1, if the record is not found. +** Otherwise, the offset (0 or 1) to start of data in p_data. +** +** The size of data copied into p_data is in *p_data_len. +** +*******************************************************************************/ +extern INT32 SDP_ReadRecord(UINT32 handle, UINT8 *p_data, INT32 *p_data_len); + +/******************************************************************************* +** +** Function SDP_AddAttribute +** +** Description This function is called to add an attribute to a record. +** This would be through the SDP database maintenance API. +** If the attribute already exists in the record, it is replaced +** with the new value. +** +** NOTE Attribute values must be passed as a Big Endian stream. +** +** Returns TRUE if added OK, else FALSE +** +*******************************************************************************/ +extern BOOLEAN SDP_AddAttribute (UINT32 handle, UINT16 attr_id, + UINT8 attr_type, UINT32 attr_len, + UINT8 *p_val); + + +/******************************************************************************* +** +** Function SDP_AddSequence +** +** Description This function is called to add a sequence to a record. +** This would be through the SDP database maintenance API. +** If the sequence already exists in the record, it is replaced +** with the new sequence. +** +** NOTE Element values must be passed as a Big Endian stream. +** +** Returns TRUE if added OK, else FALSE +** +*******************************************************************************/ +extern BOOLEAN SDP_AddSequence (UINT32 handle, UINT16 attr_id, + UINT16 num_elem, UINT8 type[], + UINT8 len[], UINT8 *p_val[]); + + +/******************************************************************************* +** +** Function SDP_AddUuidSequence +** +** Description This function is called to add a UUID sequence to a record. +** This would be through the SDP database maintenance API. +** If the sequence already exists in the record, it is replaced +** with the new sequence. +** +** Returns TRUE if added OK, else FALSE +** +*******************************************************************************/ +extern BOOLEAN SDP_AddUuidSequence (UINT32 handle, UINT16 attr_id, + UINT16 num_uuids, UINT16 *p_uuids); + + +/******************************************************************************* +** +** Function SDP_AddProtocolList +** +** Description This function is called to add a protocol descriptor list to +** a record. This would be through the SDP database maintenance API. +** If the protocol list already exists in the record, it is replaced +** with the new list. +** +** Returns TRUE if added OK, else FALSE +** +*******************************************************************************/ +extern BOOLEAN SDP_AddProtocolList (UINT32 handle, UINT16 num_elem, + tSDP_PROTOCOL_ELEM *p_elem_list); + + +/******************************************************************************* +** +** Function SDP_AddAdditionProtoLists +** +** Description This function is called to add a protocol descriptor list to +** a record. This would be through the SDP database maintenance API. +** If the protocol list already exists in the record, it is replaced +** with the new list. +** +** Returns TRUE if added OK, else FALSE +** +*******************************************************************************/ +extern BOOLEAN SDP_AddAdditionProtoLists (UINT32 handle, UINT16 num_elem, + tSDP_PROTO_LIST_ELEM *p_proto_list); + + +/******************************************************************************* +** +** Function SDP_AddProfileDescriptorList +** +** Description This function is called to add a profile descriptor list to +** a record. This would be through the SDP database maintenance API. +** If the version already exists in the record, it is replaced +** with the new one. +** +** Returns TRUE if added OK, else FALSE +** +*******************************************************************************/ +extern BOOLEAN SDP_AddProfileDescriptorList (UINT32 handle, + UINT16 profile_uuid, + UINT16 version); + + +/******************************************************************************* +** +** Function SDP_AddLanguageBaseAttrIDList +** +** Description This function is called to add a language base attr list to +** a record. This would be through the SDP database maintenance API. +** If the version already exists in the record, it is replaced +** with the new one. +** +** Returns TRUE if added OK, else FALSE +** +*******************************************************************************/ +extern BOOLEAN SDP_AddLanguageBaseAttrIDList (UINT32 handle, + UINT16 lang, UINT16 char_enc, + UINT16 base_id); + + +/******************************************************************************* +** +** Function SDP_AddServiceClassIdList +** +** Description This function is called to add a service list to a record. +** This would be through the SDP database maintenance API. +** If the service list already exists in the record, it is replaced +** with the new list. +** +** Returns TRUE if added OK, else FALSE +** +*******************************************************************************/ +extern BOOLEAN SDP_AddServiceClassIdList (UINT32 handle, + UINT16 num_services, + UINT16 *p_service_uuids); + + +/******************************************************************************* +** +** Function SDP_DeleteAttribute +** +** Description This function is called to delete an attribute from a record. +** This would be through the SDP database maintenance API. +** +** Returns TRUE if deleted OK, else FALSE if not found +** +*******************************************************************************/ +extern BOOLEAN SDP_DeleteAttribute (UINT32 handle, UINT16 attr_id); + + +/* Device Identification APIs */ + +/******************************************************************************* +** +** Function SDP_SetLocalDiRecord +** +** Description This function adds a DI record to the local SDP database. +** +** Returns Returns SDP_SUCCESS if record added successfully, else error +** +*******************************************************************************/ +extern UINT16 SDP_SetLocalDiRecord (tSDP_DI_RECORD *device_info, + UINT32 *p_handle); + +/******************************************************************************* +** +** Function SDP_DiDiscover +** +** Description This function queries a remote device for DI information. +** +** Returns SDP_SUCCESS if query started successfully, else error +** +*******************************************************************************/ +extern UINT16 SDP_DiDiscover (BD_ADDR remote_device, + tSDP_DISCOVERY_DB *p_db, UINT32 len, + tSDP_DISC_CMPL_CB *p_cb); + + +/******************************************************************************* +** +** Function SDP_GetNumDiRecords +** +** Description Searches specified database for DI records +** +** Returns number of DI records found +** +*******************************************************************************/ +extern UINT8 SDP_GetNumDiRecords (tSDP_DISCOVERY_DB *p_db); + + +/******************************************************************************* +** +** Function SDP_GetDiRecord +** +** Description This function retrieves a remote device's DI record from +** the specified database. +** +** Returns SDP_SUCCESS if record retrieved, else error +** +*******************************************************************************/ +extern UINT16 SDP_GetDiRecord (UINT8 getRecordIndex, + tSDP_DI_GET_RECORD *device_info, + tSDP_DISCOVERY_DB *p_db); + + +/******************************************************************************* +** +** Function SDP_SetTraceLevel +** +** Description This function sets the trace level for SDP. If called with +** a value of 0xFF, it simply reads the current trace level. +** +** Returns the new (current) trace level +** +*******************************************************************************/ +extern UINT8 SDP_SetTraceLevel (UINT8 new_level); + +/******************************************************************************* +** +** Function SDP_ConnOpen +** +** Description This function creates a connection to the SDP server on the +** given device. +** +** Returns 0, if failed to initiate connection. Otherwise, the handle. +** +*******************************************************************************/ +UINT32 SDP_ConnOpen (UINT8 *p_bd_addr, tSDP_DISC_RES_CB *p_rcb, + tSDP_DISC_CMPL_CB *p_cb); + +/******************************************************************************* +** +** Function SDP_WriteData +** +** Description This function sends data to the connected SDP server. +** +** Returns TRUE if data is sent, FALSE if failed. +** +*******************************************************************************/ +BOOLEAN SDP_WriteData (UINT32 handle, BT_HDR *p_msg); + +/******************************************************************************* +** +** Function SDP_ConnClose +** +** Description This function is called to close a SDP connection. +** +** Parameters: handle - Handle of the connection returned by SDP_ConnOpen +** +** Returns TRUE if connection is closed, FALSE if failed to find the handle. +** +*******************************************************************************/ +BOOLEAN SDP_ConnClose (UINT32 handle); + +/******************************************************************************* +** +** Function SDP_FindServiceUUIDInRec +** +** Description This function is called to read the service UUID within a record +** if there is any. +** +** Parameters: p_rec - pointer to a SDP record. +** +** Returns TRUE if found, otherwise FALSE. +** +*******************************************************************************/ +BOOLEAN SDP_FindServiceUUIDInRec(tSDP_DISC_REC *p_rec, tBT_UUID *p_uuid); + +#ifdef __cplusplus +} +#endif + +#endif ///SDP_INCLUDED == TRUE + +#endif /* SDP_API_H */ diff --git a/lib/bt/host/bluedroid/stack/include/stack/sdpdefs.h b/lib/bt/host/bluedroid/stack/include/stack/sdpdefs.h new file mode 100644 index 00000000..d7eab356 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/include/stack/sdpdefs.h @@ -0,0 +1,325 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains the definitions for the SDP API + * + ******************************************************************************/ + +#ifndef SDP_DEFS_H +#define SDP_DEFS_H + +/* Define the service attribute IDs. +*/ +#define ATTR_ID_SERVICE_RECORD_HDL 0x0000 +#define ATTR_ID_SERVICE_CLASS_ID_LIST 0x0001 +#define ATTR_ID_SERVICE_RECORD_STATE 0x0002 +#define ATTR_ID_SERVICE_ID 0x0003 +#define ATTR_ID_PROTOCOL_DESC_LIST 0x0004 +#define ATTR_ID_BROWSE_GROUP_LIST 0x0005 +#define ATTR_ID_LANGUAGE_BASE_ATTR_ID_LIST 0x0006 +#define ATTR_ID_SERVICE_INFO_TIME_TO_LIVE 0x0007 +#define ATTR_ID_SERVICE_AVAILABILITY 0x0008 +#define ATTR_ID_BT_PROFILE_DESC_LIST 0x0009 +#define ATTR_ID_DOCUMENTATION_URL 0x000A +#define ATTR_ID_CLIENT_EXE_URL 0x000B +#define ATTR_ID_ICON_URL 0x000C +#define ATTR_ID_ADDITION_PROTO_DESC_LISTS 0x000D + +#define LANGUAGE_BASE_ID 0x0100 +#define ATTR_ID_SERVICE_NAME LANGUAGE_BASE_ID + 0x0000 +#define ATTR_ID_SERVICE_DESCRIPTION LANGUAGE_BASE_ID + 0x0001 +#define ATTR_ID_PROVIDER_NAME LANGUAGE_BASE_ID + 0x0002 + +/* Device Identification (DI) +*/ +#define ATTR_ID_SPECIFICATION_ID 0x0200 +#define ATTR_ID_VENDOR_ID 0x0201 +#define ATTR_ID_PRODUCT_ID 0x0202 +#define ATTR_ID_PRODUCT_VERSION 0x0203 +#define ATTR_ID_PRIMARY_RECORD 0x0204 +#define ATTR_ID_VENDOR_ID_SOURCE 0x0205 + +#define BLUETOOTH_DI_SPECIFICATION 0x0103 /* 1.3 */ +#define DI_VENDOR_ID_DEFAULT 0xFFFF +#define DI_VENDOR_ID_SOURCE_BTSIG 0x0001 +#define DI_VENDOR_ID_SOURCE_USBIF 0x0002 + + +#define ATTR_ID_IP_SUBNET 0x0200 /* PAN Profile (***) */ +#define ATTR_ID_VERSION_NUMBER_LIST 0x0200 +#define ATTR_ID_GOEP_L2CAP_PSM 0x0200 +#define ATTR_ID_GROUP_ID 0x0200 +#define ATTR_ID_SERVICE_DATABASE_STATE 0x0201 +#define ATTR_ID_SERVICE_VERSION 0x0300 +#define ATTR_ID_HCRP_1284ID 0x0300 + +#define ATTR_ID_SUPPORTED_DATA_STORES 0x0301 +#define ATTR_ID_NETWORK 0x0301 +#define ATTR_ID_EXTERNAL_NETWORK 0x0301 +#define ATTR_ID_FAX_CLASS_1_SUPPORT 0x0302 +#define ATTR_ID_REMOTE_AUDIO_VOLUME_CONTROL 0x0302 +#define ATTR_ID_DEVICE_NAME 0x0302 +#define ATTR_ID_SUPPORTED_FORMATS_LIST 0x0303 +#define ATTR_ID_FAX_CLASS_2_0_SUPPORT 0x0303 +#define ATTR_ID_FAX_CLASS_2_SUPPORT 0x0304 +#define ATTR_ID_FRIENDLY_NAME 0x0304 +#define ATTR_ID_AUDIO_FEEDBACK_SUPPORT 0x0305 +#define ATTR_ID_NETWORK_ADDRESS 0x0306 +#define ATTR_ID_DEVICE_LOCATION 0x0306 +#define ATTR_ID_WAP_GATEWAY 0x0307 +#define ATTR_ID_HOME_PAGE_URL 0x0308 +#define ATTR_ID_WAP_STACK_TYPE 0x0309 +#define ATTR_ID_IMG_SUPPORTED_CAPABILITIES 0x0310 /* Imaging Profile */ +#define ATTR_ID_SUPPORTED_FEATURES 0x0311 /* HFP, BIP */ +#define ATTR_ID_IMG_SUPPORTED_FUNCTIONS 0x0312 /* Imaging Profile */ +#define ATTR_ID_IMG_TOT_DATA_CAPABILITY 0x0313 /* Imaging Profile */ +#define ATTR_ID_SUPPORTED_REPOSITORIES 0x0314 /* Phone book access Profile */ +#define ATTR_ID_MAS_INSTANCE_ID 0x0315 /* MAP profile */ +#define ATTR_ID_SUPPORTED_MSG_TYPE 0x0316 /* MAP profile */ +#define ATTR_ID_MAP_SUPPORTED_FEATURES 0x0317 /* MAP profile */ +#define ATTR_ID_PBAP_SUPPORTED_FEATURES 0x0317 /* PBAP profile */ + + +/* These values are for the BPP profile */ +#define ATTR_ID_DOCUMENT_FORMATS_SUPPORTED 0x0350 +#define ATTR_ID_CHARACTER_REPERTOIRES_SUPPORTED 0x0352 +#define ATTR_ID_XHTML_IMAGE_FORMATS_SUPPORTED 0x0354 +#define ATTR_ID_COLOR_SUPPORTED 0x0356 +#define ATTR_ID_1284ID 0x0358 +#define ATTR_ID_PRINTER_NAME 0x035A +#define ATTR_ID_PRINTER_LOCATION 0x035C +#define ATTR_ID_DUPLEX_SUPPORTED 0x035E +#define ATTR_ID_MEDIA_TYPES_SUPPORTED 0x0360 +#define ATTR_ID_MAX_MEDIA_WIDTH 0x0362 +#define ATTR_ID_MAX_MEDIA_LENGTH 0x0364 +#define ATTR_ID_ENHANCED_LAYOUT_SUPPORTED 0x0366 +#define ATTR_ID_RUI_FORMATS_SUPPORTED 0x0368 +#define ATTR_ID_RUI_REF_PRINTING_SUPPORTED 0x0370 /* Boolean */ +#define ATTR_ID_RUI_DIRECT_PRINTING_SUPPORTED 0x0372 /* Boolean */ +#define ATTR_ID_REF_PRINTING_TOP_URL 0x0374 +#define ATTR_ID_DIRECT_PRINTING_TOP_URL 0x0376 +#define ATTR_ID_PRINTER_ADMIN_RUI_TOP_URL 0x0378 +#define ATTR_ID_BPP_DEVICE_NAME 0x037A + +/* These values are for the PAN profile */ +#define ATTR_ID_SECURITY_DESCRIPTION 0x030A +#define ATTR_ID_NET_ACCESS_TYPE 0x030B +#define ATTR_ID_MAX_NET_ACCESS_RATE 0x030C +#define ATTR_ID_IPV4_SUBNET 0x030D +#define ATTR_ID_IPV6_SUBNET 0x030E +#define ATTR_ID_PAN_SECURITY 0x0400 + +/* These values are for HID profile */ +#define ATTR_ID_HID_DEVICE_RELNUM 0x0200 +#define ATTR_ID_HID_PARSER_VERSION 0x0201 +#define ATTR_ID_HID_DEVICE_SUBCLASS 0x0202 +#define ATTR_ID_HID_COUNTRY_CODE 0x0203 +#define ATTR_ID_HID_VIRTUAL_CABLE 0x0204 +#define ATTR_ID_HID_RECONNECT_INITIATE 0x0205 +#define ATTR_ID_HID_DESCRIPTOR_LIST 0x0206 +#define ATTR_ID_HID_LANGUAGE_ID_BASE 0x0207 +#define ATTR_ID_HID_SDP_DISABLE 0x0208 +#define ATTR_ID_HID_BATTERY_POWER 0x0209 +#define ATTR_ID_HID_REMOTE_WAKE 0x020A +#define ATTR_ID_HID_PROFILE_VERSION 0x020B +#define ATTR_ID_HID_LINK_SUPERVISION_TO 0x020C +#define ATTR_ID_HID_NORMALLY_CONNECTABLE 0x020D +#define ATTR_ID_HID_BOOT_DEVICE 0x020E +#define ATTR_ID_HID_SSR_HOST_MAX_LAT 0x020F +#define ATTR_ID_HID_SSR_HOST_MIN_TOUT 0x0210 + +/* These values are for the HDP profile */ +#define ATTR_ID_HDP_SUP_FEAT_LIST 0x0200 /* Supported features list */ +#define ATTR_ID_HDP_DATA_EXCH_SPEC 0x0301 /* Data exchange specification */ +#define ATTR_ID_HDP_MCAP_SUP_PROC 0x0302 /* MCAP supported procedures */ + +/* Define common 16-bit protocol UUIDs +*/ +#define UUID_PROTOCOL_SDP 0x0001 +#define UUID_PROTOCOL_UDP 0x0002 +#define UUID_PROTOCOL_RFCOMM 0x0003 +#define UUID_PROTOCOL_TCP 0x0004 +#define UUID_PROTOCOL_TCS_BIN 0x0005 +#define UUID_PROTOCOL_TCS_AT 0x0006 +#define UUID_PROTOCOL_OBEX 0x0008 +#define UUID_PROTOCOL_IP 0x0009 +#define UUID_PROTOCOL_FTP 0x000A +#define UUID_PROTOCOL_HTTP 0x000C +#define UUID_PROTOCOL_WSP 0x000E +#define UUID_PROTOCOL_BNEP 0x000F +#define UUID_PROTOCOL_UPNP 0x0010 +#define UUID_PROTOCOL_HIDP 0x0011 +#define UUID_PROTOCOL_HCRP_CTRL 0x0012 +#define UUID_PROTOCOL_HCRP_DATA 0x0014 +#define UUID_PROTOCOL_HCRP_NOTIF 0x0016 +#define UUID_PROTOCOL_AVCTP 0x0017 +#define UUID_PROTOCOL_AVDTP 0x0019 +#define UUID_PROTOCOL_CMTP 0x001B +#define UUID_PROTOCOL_UDI 0x001D +#define UUID_PROTOCOL_MCAP_CTRL 0x001E +#define UUID_PROTOCOL_MCAP_DATA 0x001F +#define UUID_PROTOCOL_L2CAP 0x0100 +#define UUID_PROTOCOL_ATT 0x0007 + +/* Define common 16-bit service class UUIDs +*/ +#define UUID_SERVCLASS_SERVICE_DISCOVERY_SERVER 0X1000 +#define UUID_SERVCLASS_BROWSE_GROUP_DESCRIPTOR 0X1001 +#define UUID_SERVCLASS_PUBLIC_BROWSE_GROUP 0X1002 +#define UUID_SERVCLASS_SERIAL_PORT 0X1101 +#define UUID_SERVCLASS_LAN_ACCESS_USING_PPP 0X1102 +#define UUID_SERVCLASS_DIALUP_NETWORKING 0X1103 +#define UUID_SERVCLASS_IRMC_SYNC 0X1104 +#define UUID_SERVCLASS_OBEX_OBJECT_PUSH 0X1105 +#define UUID_SERVCLASS_OBEX_FILE_TRANSFER 0X1106 +#define UUID_SERVCLASS_IRMC_SYNC_COMMAND 0X1107 +#define UUID_SERVCLASS_HEADSET 0X1108 +#define UUID_SERVCLASS_CORDLESS_TELEPHONY 0X1109 +#define UUID_SERVCLASS_AUDIO_SOURCE 0X110A +#define UUID_SERVCLASS_AUDIO_SINK 0X110B +#define UUID_SERVCLASS_AV_REM_CTRL_TARGET 0X110C /* Audio/Video Control profile */ +#define UUID_SERVCLASS_ADV_AUDIO_DISTRIBUTION 0X110D /* Advanced Audio Distribution profile */ +#define UUID_SERVCLASS_AV_REMOTE_CONTROL 0X110E /* Audio/Video Control profile */ +#define UUID_SERVCLASS_AV_REM_CTRL_CONTROL 0X110F /* Audio/Video Control profile */ +#define UUID_SERVCLASS_INTERCOM 0X1110 +#define UUID_SERVCLASS_FAX 0X1111 +#define UUID_SERVCLASS_HEADSET_AUDIO_GATEWAY 0X1112 +#define UUID_SERVCLASS_WAP 0X1113 +#define UUID_SERVCLASS_WAP_CLIENT 0X1114 +#define UUID_SERVCLASS_PANU 0X1115 /* PAN profile */ +#define UUID_SERVCLASS_NAP 0X1116 /* PAN profile */ +#define UUID_SERVCLASS_GN 0X1117 /* PAN profile */ +#define UUID_SERVCLASS_DIRECT_PRINTING 0X1118 /* BPP profile */ +#define UUID_SERVCLASS_REFERENCE_PRINTING 0X1119 /* BPP profile */ +#define UUID_SERVCLASS_IMAGING 0X111A /* Imaging profile */ +#define UUID_SERVCLASS_IMAGING_RESPONDER 0X111B /* Imaging profile */ +#define UUID_SERVCLASS_IMAGING_AUTO_ARCHIVE 0X111C /* Imaging profile */ +#define UUID_SERVCLASS_IMAGING_REF_OBJECTS 0X111D /* Imaging profile */ +#define UUID_SERVCLASS_HF_HANDSFREE 0X111E /* Handsfree profile */ +#define UUID_SERVCLASS_AG_HANDSFREE 0X111F /* Handsfree profile */ +#define UUID_SERVCLASS_DIR_PRT_REF_OBJ_SERVICE 0X1120 /* BPP profile */ +#define UUID_SERVCLASS_REFLECTED_UI 0X1121 /* BPP profile */ +#define UUID_SERVCLASS_BASIC_PRINTING 0X1122 /* BPP profile */ +#define UUID_SERVCLASS_PRINTING_STATUS 0X1123 /* BPP profile */ +#define UUID_SERVCLASS_HUMAN_INTERFACE 0X1124 /* HID profile */ +#define UUID_SERVCLASS_CABLE_REPLACEMENT 0X1125 /* HCRP profile */ +#define UUID_SERVCLASS_HCRP_PRINT 0X1126 /* HCRP profile */ +#define UUID_SERVCLASS_HCRP_SCAN 0X1127 /* HCRP profile */ +#define UUID_SERVCLASS_COMMON_ISDN_ACCESS 0X1128 /* CAPI Message Transport Protocol*/ +#define UUID_SERVCLASS_VIDEO_CONFERENCING_GW 0X1129 /* Video Conferencing profile */ +#define UUID_SERVCLASS_UDI_MT 0X112A /* Unrestricted Digital Information profile */ +#define UUID_SERVCLASS_UDI_TA 0X112B /* Unrestricted Digital Information profile */ +#define UUID_SERVCLASS_VCP 0X112C /* Video Conferencing profile */ +#define UUID_SERVCLASS_SAP 0X112D /* SIM Access profile */ +#define UUID_SERVCLASS_PBAP_PCE 0X112E /* Phonebook Access - PCE */ +#define UUID_SERVCLASS_PBAP_PSE 0X112F /* Phonebook Access - PSE */ +#define UUID_SERVCLASS_PHONE_ACCESS 0x1130 +#define UUID_SERVCLASS_HEADSET_HS 0x1131 /* Headset - HS, from HSP v1.2 */ +#define UUID_SERVCLASS_PNP_INFORMATION 0X1200 /* Device Identification */ +#define UUID_SERVCLASS_GENERIC_NETWORKING 0X1201 +#define UUID_SERVCLASS_GENERIC_FILETRANSFER 0X1202 +#define UUID_SERVCLASS_GENERIC_AUDIO 0X1203 +#define UUID_SERVCLASS_GENERIC_TELEPHONY 0X1204 +#define UUID_SERVCLASS_UPNP_SERVICE 0X1205 /* UPNP_Service [ESDP] */ +#define UUID_SERVCLASS_UPNP_IP_SERVICE 0X1206 /* UPNP_IP_Service [ESDP] */ +#define UUID_SERVCLASS_ESDP_UPNP_IP_PAN 0X1300 /* UPNP_IP_PAN [ESDP] */ +#define UUID_SERVCLASS_ESDP_UPNP_IP_LAP 0X1301 /* UPNP_IP_LAP [ESDP] */ +#define UUID_SERVCLASS_ESDP_UPNP_IP_L2CAP 0X1302 /* UPNP_L2CAP [ESDP] */ +#define UUID_SERVCLASS_VIDEO_SOURCE 0X1303 /* Video Distribution Profile (VDP) */ +#define UUID_SERVCLASS_VIDEO_SINK 0X1304 /* Video Distribution Profile (VDP) */ +#define UUID_SERVCLASS_VIDEO_DISTRIBUTION 0X1305 /* Video Distribution Profile (VDP) */ +#define UUID_SERVCLASS_HDP_PROFILE 0X1400 /* Health Device profile (HDP) */ +#define UUID_SERVCLASS_HDP_SOURCE 0X1401 /* Health Device profile (HDP) */ +#define UUID_SERVCLASS_HDP_SINK 0X1402 /* Health Device profile (HDP) */ +#define UUID_SERVCLASS_MAP_PROFILE 0X1134 /* MAP profile UUID */ +#define UUID_SERVCLASS_MESSAGE_ACCESS 0X1132 /* Message Access Service UUID */ +#define UUID_SERVCLASS_MESSAGE_NOTIFICATION 0X1133 /* Message Notification Service UUID */ + +#define UUID_SERVCLASS_GAP_SERVER 0x1800 +#define UUID_SERVCLASS_GATT_SERVER 0x1801 +#define UUID_SERVCLASS_IMMEDIATE_ALERT 0x1802 /* immediate alert */ +#define UUID_SERVCLASS_LINKLOSS 0x1803 /* Link Loss Alert */ +#define UUID_SERVCLASS_TX_POWER 0x1804 /* TX power */ +#define UUID_SERVCLASS_CURRENT_TIME 0x1805 /* Link Loss Alert */ +#define UUID_SERVCLASS_DST_CHG 0x1806 /* DST Time change */ +#define UUID_SERVCLASS_REF_TIME_UPD 0x1807 /* reference time update */ +#define UUID_SERVCLASS_THERMOMETER 0x1809 /* Thermometer UUID */ +#define UUID_SERVCLASS_DEVICE_INFO 0x180A /* device info service */ +#define UUID_SERVCLASS_NWA 0x180B /* Network availability */ +#define UUID_SERVCLASS_HEART_RATE 0x180D /* Heart Rate service */ +#define UUID_SERVCLASS_PHALERT 0x180E /* phone alert service */ +#define UUID_SERVCLASS_BATTERY 0x180F /* battery service */ +#define UUID_SERVCLASS_BPM 0x1810 /* blood pressure service */ +#define UUID_SERVCLASS_ALERT_NOTIFICATION 0x1811 /* alert notification service */ +#define UUID_SERVCLASS_LE_HID 0x1812 /* HID over LE */ +#define UUID_SERVCLASS_SCAN_PARAM 0x1813 /* Scan Parameter service */ +#define UUID_SERVCLASS_GLUCOSE 0x1808 /* Glucose Meter Service */ +#define UUID_SERVCLASS_RSC 0x1814 /* RUNNERS SPEED AND CADENCE SERVICE */ +#define UUID_SERVCLASS_CSC 0x1816 /* Cycling SPEED AND CADENCE SERVICE */ + +#define UUID_SERVCLASS_TEST_SERVER 0x9000 /* Test Group UUID */ + +#if (BTM_WBS_INCLUDED == TRUE) +#define UUID_CODEC_CVSD 0x0001 /* CVSD */ +#define UUID_CODEC_MSBC 0x0002 /* mSBC */ +#endif + +/* Define all the 'Descriptor Type' values. +*/ +#define NULL_DESC_TYPE 0 +#define UINT_DESC_TYPE 1 +#define TWO_COMP_INT_DESC_TYPE 2 +#define UUID_DESC_TYPE 3 +#define TEXT_STR_DESC_TYPE 4 +#define BOOLEAN_DESC_TYPE 5 +#define DATA_ELE_SEQ_DESC_TYPE 6 +#define DATA_ELE_ALT_DESC_TYPE 7 +#define URL_DESC_TYPE 8 + +/* Define all the "Descriptor Size" values. +*/ +#define SIZE_ONE_BYTE 0 +#define SIZE_TWO_BYTES 1 +#define SIZE_FOUR_BYTES 2 +#define SIZE_EIGHT_BYTES 3 +#define SIZE_SIXTEEN_BYTES 4 +#define SIZE_IN_NEXT_BYTE 5 +#define SIZE_IN_NEXT_WORD 6 +#define SIZE_IN_NEXT_LONG 7 + +/* Language Encoding Constants */ +#define LANG_ID_CODE_ENGLISH ((UINT16) 0x656e) /* "en" */ +#define LANG_ID_CHAR_ENCODE_UTF8 ((UINT16) 0x006a) /* UTF-8 */ + +/* Constants used for display purposes only. These define ovelapping attribute values */ +#define ATTR_ID_VERS_OR_GRP_OR_DRELNUM_OR_IPSUB_OR_SPECID 0x0200 +#define ATTR_ID_VEND_ID_OR_SERVICE_DB_STATE_OR_PARSE_VER 0x0201 +#define ATTR_ID_PROD_ID_OR_HID_DEV_SUBCLASS 0x0202 +#define ATTR_ID_PROD_VER_OR_HID_COUNTRY_CODE 0x0203 +#define ATTR_ID_PRIMARY_REC_OR_HID_VIRTUAL_CABLE 0x0204 +#define ATTR_ID_DI_VENDOR_ID_SOURCE_OR_HID_INIT_RECONNECT 0x0205 +#define ATTR_ID_SERV_VERS_OR_1284ID 0x0300 +#define ATTR_ID_DATA_STORES_OR_NETWORK 0x0301 +#define ATTR_ID_FAX_1_OR_AUD_VOL_OR_DEV_NAME 0x0302 +#define ATTR_ID_FORMATS_OR_FAX_2_0 0x0303 +#define ATTR_ID_FAX_CLASS_2_OR_FRIENDLY_NAME 0x0304 +#define ATTR_ID_NETADDRESS_OR_DEVLOCATION 0x0306 + +#endif diff --git a/lib/bt/host/bluedroid/stack/include/stack/smp_api.h b/lib/bt/host/bluedroid/stack/include/stack/smp_api.h new file mode 100644 index 00000000..11d432e7 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/include/stack/smp_api.h @@ -0,0 +1,508 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains the SMP API function external definitions. + * + ******************************************************************************/ +#ifndef SMP_API_H +#define SMP_API_H + +#include "common/bt_target.h" + +#define SMP_PIN_CODE_LEN_MAX PIN_CODE_LEN +#define SMP_PIN_CODE_LEN_MIN 6 + +// #if SMP_INCLUDED == TRUE +/* SMP command code */ +#define SMP_OPCODE_PAIRING_REQ 0x01 +#define SMP_OPCODE_PAIRING_RSP 0x02 +#define SMP_OPCODE_CONFIRM 0x03 +#define SMP_OPCODE_RAND 0x04 +#define SMP_OPCODE_PAIRING_FAILED 0x05 +#define SMP_OPCODE_ENCRYPT_INFO 0x06 +#define SMP_OPCODE_MASTER_ID 0x07 +#define SMP_OPCODE_IDENTITY_INFO 0x08 +#define SMP_OPCODE_ID_ADDR 0x09 +#define SMP_OPCODE_SIGN_INFO 0x0A +#define SMP_OPCODE_SEC_REQ 0x0B +#define SMP_OPCODE_PAIR_PUBLIC_KEY 0x0C +#define SMP_OPCODE_PAIR_DHKEY_CHECK 0x0D +#define SMP_OPCODE_PAIR_KEYPR_NOTIF 0x0E +#define SMP_OPCODE_MAX SMP_OPCODE_PAIR_KEYPR_NOTIF +#define SMP_OPCODE_MIN SMP_OPCODE_PAIRING_REQ +#define SMP_OPCODE_PAIR_COMMITM 0x0F +// #endif + +/* SMP event type */ +#define SMP_IO_CAP_REQ_EVT 1 /* IO capability request event */ +#define SMP_SEC_REQUEST_EVT 2 /* SMP pairing request */ +#define SMP_PASSKEY_NOTIF_EVT 3 /* passkey notification event */ +#define SMP_PASSKEY_REQ_EVT 4 /* passkey request event */ +#define SMP_OOB_REQ_EVT 5 /* OOB request event */ +#define SMP_NC_REQ_EVT 6 /* Numeric Comparison request event */ +#define SMP_COMPLT_EVT 7 /* SMP complete event */ +#define SMP_PEER_KEYPR_NOT_EVT 8 /* Peer keypress notification received event */ +#define SMP_SC_OOB_REQ_EVT 9 /* SC OOB request event (both local and peer OOB data */ +/* can be expected in response) */ +#define SMP_SC_LOC_OOB_DATA_UP_EVT 10 /* SC OOB local data set is created */ +/* (as result of SMP_CrLocScOobData(...)) */ +#define SMP_BR_KEYS_REQ_EVT 12 /* SMP over BR keys request event */ +typedef UINT8 tSMP_EVT; + + +/* pairing failure reason code */ +#define SMP_PASSKEY_ENTRY_FAIL 0x01 +#define SMP_OOB_FAIL 0x02 +#define SMP_PAIR_AUTH_FAIL 0x03 +#define SMP_CONFIRM_VALUE_ERR 0x04 +#define SMP_PAIR_NOT_SUPPORT 0x05 +#define SMP_ENC_KEY_SIZE 0x06 +#define SMP_INVALID_CMD 0x07 +#define SMP_PAIR_FAIL_UNKNOWN 0x08 +#define SMP_REPEATED_ATTEMPTS 0x09 +#define SMP_INVALID_PARAMETERS 0x0A +#define SMP_DHKEY_CHK_FAIL 0x0B +#define SMP_NUMERIC_COMPAR_FAIL 0x0C +#define SMP_BR_PARING_IN_PROGR 0x0D +#define SMP_XTRANS_DERIVE_NOT_ALLOW 0x0E +#define SMP_MAX_FAIL_RSN_PER_SPEC SMP_XTRANS_DERIVE_NOT_ALLOW + +/* self defined error code */ +#define SMP_PAIR_INTERNAL_ERR (SMP_MAX_FAIL_RSN_PER_SPEC + 0x01) /* 0x0F */ + +/* 0x0F unknown IO capability, unable to decide association model */ +#define SMP_UNKNOWN_IO_CAP (SMP_MAX_FAIL_RSN_PER_SPEC + 0x02) /* 0x10 */ + +#define SMP_INIT_FAIL (SMP_MAX_FAIL_RSN_PER_SPEC + 0x03) /* 0x11 */ +#define SMP_CONFIRM_FAIL (SMP_MAX_FAIL_RSN_PER_SPEC + 0x04) /* 0x12 */ +#define SMP_BUSY (SMP_MAX_FAIL_RSN_PER_SPEC + 0x05) /* 0x13 */ +#define SMP_ENC_FAIL (SMP_MAX_FAIL_RSN_PER_SPEC + 0x06) /* 0x14 */ +#define SMP_STARTED (SMP_MAX_FAIL_RSN_PER_SPEC + 0x07) /* 0x15 */ +#define SMP_RSP_TIMEOUT (SMP_MAX_FAIL_RSN_PER_SPEC + 0x08) /* 0x16 */ +#define SMP_DIV_NOT_AVAIL (SMP_MAX_FAIL_RSN_PER_SPEC + 0x09) /* 0x17 */ + +/* 0x17 unspecified failed reason */ +#define SMP_FAIL (SMP_MAX_FAIL_RSN_PER_SPEC + 0x0A) /* 0x18 */ + +#define SMP_CONN_TOUT (SMP_MAX_FAIL_RSN_PER_SPEC + 0x0B) /* 0x19 */ +#define SMP_SUCCESS 0 + +typedef UINT8 tSMP_STATUS; + + +/* Device IO capability */ +#define SMP_IO_CAP_OUT BTM_IO_CAP_OUT /* DisplayOnly */ +#define SMP_IO_CAP_IO BTM_IO_CAP_IO /* DisplayYesNo */ +#define SMP_IO_CAP_IN BTM_IO_CAP_IN /* KeyboardOnly */ +#define SMP_IO_CAP_NONE BTM_IO_CAP_NONE /* NoInputNoOutput */ +#define SMP_IO_CAP_KBDISP BTM_IO_CAP_KBDISP /* Keyboard Display */ +#define SMP_IO_CAP_MAX BTM_IO_CAP_MAX +typedef UINT8 tSMP_IO_CAP; + +#ifndef SMP_DEFAULT_IO_CAPS +#define SMP_DEFAULT_IO_CAPS SMP_IO_CAP_KBDISP +#endif + +/* OOB data present or not */ +enum { + SMP_OOB_NONE, + SMP_OOB_PRESENT, + SMP_OOB_UNKNOWN +}; +typedef UINT8 tSMP_OOB_FLAG; + +/* type of OOB data required from application */ +enum { + SMP_OOB_INVALID_TYPE, + SMP_OOB_PEER, + SMP_OOB_LOCAL, + SMP_OOB_BOTH +}; +typedef UINT8 tSMP_OOB_DATA_TYPE; + +#define SMP_AUTH_NO_BOND 0x00 +#define SMP_AUTH_GEN_BOND 0x01 //todo sdh change GEN_BOND to BOND + +/* SMP Authentication requirement */ +#define SMP_AUTH_YN_BIT (1 << 2) +#define SMP_SC_SUPPORT_BIT (1 << 3) +#define SMP_KP_SUPPORT_BIT (1 << 4) + +#define SMP_AUTH_MASK (SMP_AUTH_GEN_BOND|SMP_AUTH_YN_BIT|SMP_SC_SUPPORT_BIT|SMP_KP_SUPPORT_BIT) + +#define SMP_AUTH_BOND SMP_AUTH_GEN_BOND + +/* no MITM, No Bonding, encryption only */ +#define SMP_AUTH_NB_ENC_ONLY 0x00 //(SMP_AUTH_MASK | BTM_AUTH_SP_NO) + +/* MITM, No Bonding, Use IO Capability to determine authentication procedure */ +#define SMP_AUTH_NB_IOCAP (SMP_AUTH_NO_BOND | SMP_AUTH_YN_BIT) + +/* No MITM, General Bonding, Encryption only */ +#define SMP_AUTH_GB_ENC_ONLY (SMP_AUTH_GEN_BOND ) + +/* MITM, General Bonding, Use IO Capability to determine authentication procedure */ +#define SMP_AUTH_GB_IOCAP (SMP_AUTH_GEN_BOND | SMP_AUTH_YN_BIT) + +/* Secure Connections, no MITM, no Bonding */ +#define SMP_AUTH_SC_ENC_ONLY (SMP_SC_SUPPORT_BIT) + +/* Secure Connections, no MITM, Bonding */ +#define SMP_AUTH_SC_GB (SMP_SC_SUPPORT_BIT | SMP_AUTH_GEN_BOND) + +/* Secure Connections, MITM, no Bonding */ +#define SMP_AUTH_SC_MITM_NB (SMP_SC_SUPPORT_BIT | SMP_AUTH_YN_BIT | SMP_AUTH_NO_BOND) + +/* Secure Connections, MITM, Bonding */ +#define SMP_AUTH_SC_MITM_GB (SMP_SC_SUPPORT_BIT | SMP_AUTH_YN_BIT | SMP_AUTH_GEN_BOND) + +/* All AuthReq RFU bits are set to 1 - NOTE: reserved bit in Bonding_Flags is not set */ +#define SMP_AUTH_ALL_RFU_SET 0xF8 + +typedef UINT8 tSMP_AUTH_REQ; + +#define SMP_SEC_NONE 0 +#define SMP_SEC_UNAUTHENTICATE (1 << 0) +#define SMP_SEC_AUTHENTICATED (1 << 2) +typedef UINT8 tSMP_SEC_LEVEL; + +/* Maximum Encryption Key Size range */ +#define SMP_ENCR_KEY_SIZE_MIN 7 +#define SMP_ENCR_KEY_SIZE_MAX 16 + +/* SMP key types */ +#define SMP_SEC_KEY_TYPE_ENC (1 << 0) /* encryption key */ +#define SMP_SEC_KEY_TYPE_ID (1 << 1) /* identity key */ +#define SMP_SEC_KEY_TYPE_CSRK (1 << 2) /* slave CSRK */ +#define SMP_SEC_KEY_TYPE_LK (1 << 3) /* BR/EDR link key */ +typedef UINT8 tSMP_KEYS; + +#define SMP_BR_SEC_DEFAULT_KEY (SMP_SEC_KEY_TYPE_ENC | SMP_SEC_KEY_TYPE_ID | \ + SMP_SEC_KEY_TYPE_CSRK) + +/* default security key distribution value */ +#define SMP_SEC_DEFAULT_KEY (SMP_SEC_KEY_TYPE_ENC | SMP_SEC_KEY_TYPE_ID | \ + SMP_SEC_KEY_TYPE_CSRK | SMP_SEC_KEY_TYPE_LK) + +#define SMP_SC_KEY_STARTED 0 /* passkey entry started */ +#define SMP_SC_KEY_ENTERED 1 /* passkey digit entered */ +#define SMP_SC_KEY_ERASED 2 /* passkey digit erased */ +#define SMP_SC_KEY_CLEARED 3 /* passkey cleared */ +#define SMP_SC_KEY_COMPLT 4 /* passkey entry completed */ +#define SMP_SC_KEY_OUT_OF_RANGE 5 /* out of range */ +typedef UINT8 tSMP_SC_KEY_TYPE; + +/* data type for BTM_SP_IO_REQ_EVT */ +typedef struct { + tSMP_IO_CAP io_cap; /* local IO capabilities */ + tSMP_OOB_FLAG oob_data; /* OOB data present (locally) for the peer device */ + tSMP_AUTH_REQ auth_req; /* Authentication required (for local device) */ + UINT8 max_key_size; /* max encryption key size */ + tSMP_KEYS init_keys; /* initiator keys to be distributed */ + tSMP_KEYS resp_keys; /* responder keys */ +} tSMP_IO_REQ; + +typedef struct { + tSMP_STATUS reason; + tSMP_SEC_LEVEL sec_level; + BOOLEAN is_pair_cancel; + BOOLEAN smp_over_br; + tSMP_AUTH_REQ auth_mode; +} tSMP_CMPL; + +typedef struct { + BT_OCTET32 x; + BT_OCTET32 y; +} tSMP_PUBLIC_KEY; + +/* the data associated with the info sent to the peer via OOB interface */ +typedef struct { + BOOLEAN present; + BT_OCTET16 randomizer; + BT_OCTET16 commitment; + + tBLE_BD_ADDR addr_sent_to; + BT_OCTET32 private_key_used; /* is used to calculate: */ + /* publ_key_used = P-256(private_key_used, curve_p256.G) - send it to the */ + /* other side */ + /* dhkey = P-256(private_key_used, publ key rcvd from the other side) */ + tSMP_PUBLIC_KEY publ_key_used; /* P-256(private_key_used, curve_p256.G) */ +} tSMP_LOC_OOB_DATA; + +/* the data associated with the info received from the peer via OOB interface */ +typedef struct { + BOOLEAN present; + BT_OCTET16 randomizer; + BT_OCTET16 commitment; + tBLE_BD_ADDR addr_rcvd_from; +} tSMP_PEER_OOB_DATA; + +typedef struct { + tSMP_LOC_OOB_DATA loc_oob_data; + tSMP_PEER_OOB_DATA peer_oob_data; +} tSMP_SC_OOB_DATA; + + +typedef union { + UINT32 passkey; + tSMP_IO_REQ io_req; /* IO request */ + tSMP_CMPL cmplt; + tSMP_OOB_DATA_TYPE req_oob_type; + tSMP_LOC_OOB_DATA loc_oob_data; +} tSMP_EVT_DATA; + + +/* AES Encryption output */ +typedef struct { + UINT8 status; + UINT8 param_len; + UINT16 opcode; + UINT8 param_buf[BT_OCTET16_LEN]; +} tSMP_ENC; + +/* Security Manager events - Called by the stack when Security Manager related events occur.*/ +typedef UINT8 (tSMP_CALLBACK) (tSMP_EVT event, BD_ADDR bd_addr, tSMP_EVT_DATA *p_data); + +/* callback function for CMAC algorithm +*/ +typedef void (tCMAC_CMPL_CBACK)(UINT8 *p_mac, UINT16 tlen, UINT32 sign_counter); + +/***************************************************************************** +** External Function Declarations +*****************************************************************************/ +#ifdef __cplusplus +extern "C" +{ +#endif +/* API of SMP */ + +/******************************************************************************* +** +** Function SMP_Init +** +** Description This function initializes the SMP unit. +** +** Returns void +** +*******************************************************************************/ +extern void SMP_Init(void); + +/******************************************************************************* +** +** Function SMP_Free +** +** Description This function de initializes the SMP unit. +** +** Returns void +** +*******************************************************************************/ +extern void SMP_Free(void); + + +/******************************************************************************* +** +** Function SMP_SetTraceLevel +** +** Description This function sets the trace level for SMP. If called with +** a value of 0xFF, it simply returns the current trace level. +** +** Returns The new or current trace level +** +*******************************************************************************/ +extern UINT8 SMP_SetTraceLevel (UINT8 new_level); + +/******************************************************************************* +** +** Function SMP_Register +** +** Description This function register for the SMP service callback. +** +** Returns void +** +*******************************************************************************/ +extern BOOLEAN SMP_Register (tSMP_CALLBACK *p_cback); + +/******************************************************************************* +** +** Function SMP_Pair +** +** Description This function is called to start a SMP pairing. +** +** Returns SMP_STARTED if bond started, else otherwise exception. +** +*******************************************************************************/ +extern tSMP_STATUS SMP_Pair (BD_ADDR bd_addr); + +/******************************************************************************* +** +** Function SMP_BR_PairWith +** +** Description This function is called to start a SMP pairing over BR/EDR. +** +** Returns SMP_STARTED if pairing started, otherwise reason for failure. +** +*******************************************************************************/ +extern tSMP_STATUS SMP_BR_PairWith (BD_ADDR bd_addr); + +/******************************************************************************* +** +** Function SMP_PairCancel +** +** Description This function is called to cancel a SMP pairing. +** +** Returns TRUE - pairing cancelled +** +*******************************************************************************/ +extern BOOLEAN SMP_PairCancel (BD_ADDR bd_addr); + +/******************************************************************************* +** +** Function SMP_SecurityGrant +** +** Description This function is called to grant security process. +** +** Parameters bd_addr - peer device bd address. +** res - result of the operation SMP_SUCCESS if success. +** Otherwise, SMP_REPEATED_ATTEMPTS is too many attempts. +** +** Returns None +** +*******************************************************************************/ +extern void SMP_SecurityGrant(BD_ADDR bd_addr, UINT8 res); + +/******************************************************************************* +** +** Function SMP_PasskeyReply +** +** Description This function is called after Security Manager submitted +** Passkey request to the application. +** +** Parameters: bd_addr - Address of the device for which PIN was requested +** res - result of the operation SMP_SUCCESS if success +** passkey - numeric value in the range of +** BTM_MIN_PASSKEY_VAL(0) - BTM_MAX_PASSKEY_VAL(999999(0xF423F)). +** +*******************************************************************************/ +extern void SMP_PasskeyReply (BD_ADDR bd_addr, UINT8 res, UINT32 passkey); + +/******************************************************************************* +** +** Function SMP_SetStaticPasskey +** +** Description This function is called to set static passkey +** +** +** Parameters: add - set static passkey when add is TRUE +** clear static passkey when add is FALSE +** passkey - static passkey +** +** +*******************************************************************************/ +extern void SMP_SetStaticPasskey (BOOLEAN add, UINT32 passkey); + +/******************************************************************************* +** +** Function SMP_ConfirmReply +** +** Description This function is called after Security Manager submitted +** numeric comparison request to the application. +** +** Parameters: bd_addr - Address of the device with which numeric +** comparison was requested +** res - comparison result SMP_SUCCESS if success +** +*******************************************************************************/ +extern void SMP_ConfirmReply (BD_ADDR bd_addr, UINT8 res); + +/******************************************************************************* +** +** Function SMP_OobDataReply +** +** Description This function is called to provide the OOB data for +** SMP in response to SMP_OOB_REQ_EVT +** +** Parameters: bd_addr - Address of the peer device +** res - result of the operation SMP_SUCCESS if success +** p_data - SM Randomizer C. +** +*******************************************************************************/ +extern void SMP_OobDataReply(BD_ADDR bd_addr, tSMP_STATUS res, UINT8 len, + UINT8 *p_data); + +/******************************************************************************* +** +** Function SMP_SecureConnectionOobDataReply +** +** Description This function is called to provide the SC OOB data for +** SMP in response to SMP_SC_OOB_REQ_EVT +** +** Parameters: p_data - pointer to the data +** +*******************************************************************************/ +extern void SMP_SecureConnectionOobDataReply(UINT8 *p_data); + +/******************************************************************************* +** +** Function SMP_Encrypt +** +** Description This function is called to encrypt the data with the specified +** key +** +** Parameters: key - Pointer to key key[0] conatins the MSB +** key_len - key length +** plain_text - Pointer to data to be encrypted +** plain_text[0] conatins the MSB +** pt_len - plain text length +** p_out - pointer to the encrypted outputs +** +** Returns Boolean - TRUE: encryption is successful +*******************************************************************************/ +extern BOOLEAN SMP_Encrypt (UINT8 *key, UINT8 key_len, + UINT8 *plain_text, UINT8 pt_len, + tSMP_ENC *p_out); + +/******************************************************************************* +** +** Function SMP_KeypressNotification +** +** Description This function is called to notify SM about Keypress Notification. +** +** Parameters: bd_addr - Address of the device to send keypress +** notification to +** value - keypress notification parameter value +** +*******************************************************************************/ +extern void SMP_KeypressNotification (BD_ADDR bd_addr, UINT8 value); + +/******************************************************************************* +** +** Function SMP_CreateLocalSecureConnectionsOobData +** +** Description This function is called to start creation of local SC OOB +** data set (tSMP_LOC_OOB_DATA). +** +** Returns Boolean - TRUE: creation of local SC OOB data set started. +*******************************************************************************/ +extern BOOLEAN SMP_CreateLocalSecureConnectionsOobData (void); + +#ifdef __cplusplus +} +#endif +#endif /* SMP_API_H */ diff --git a/lib/bt/host/bluedroid/stack/l2cap/include/l2c_int.h b/lib/bt/host/bluedroid/stack/l2cap/include/l2c_int.h new file mode 100644 index 00000000..045fb10d --- /dev/null +++ b/lib/bt/host/bluedroid/stack/l2cap/include/l2c_int.h @@ -0,0 +1,824 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains L2CAP internal definitions + * + ******************************************************************************/ +#ifndef L2C_INT_H +#define L2C_INT_H + +#include +#include "stack/btm_api.h" +#include "stack/l2c_api.h" +#include "stack/l2cdefs.h" +#include "osi/list.h" +#include "osi/fixed_queue.h" + +#define L2CAP_MIN_MTU 48 /* Minimum acceptable MTU is 48 bytes */ + +/* LE credit based L2CAP connection parameters */ +#define L2CAP_LE_MIN_MTU 23 +#define L2CAP_LE_MIN_MPS 23 +#define L2CAP_LE_MAX_MPS 65533 +#define L2CAP_LE_MIN_CREDIT 0 +#define L2CAP_LE_MAX_CREDIT 65535 +#define L2CAP_LE_DEFAULT_MTU 512 +#define L2CAP_LE_DEFAULT_MPS 23 +#define L2CAP_LE_DEFAULT_CREDIT 1 + + +/* Timeouts. Since L2CAP works off a 1-second list, all are in seconds. +*/ +#define L2CAP_LINK_ROLE_SWITCH_TOUT 10 /* 10 seconds */ +#define L2CAP_LINK_CONNECT_TOUT 60 /* 30 seconds */ +#define L2CAP_LINK_CONNECT_TOUT_EXT 120 /* 120 seconds */ +#define L2CAP_ECHO_RSP_TOUT 30 /* 30 seconds */ +#define L2CAP_LINK_FLOW_CONTROL_TOUT 2 /* 2 seconds */ +#define L2CAP_LINK_DISCONNECT_TOUT 45 /* 45 seconds */ + +#ifndef L2CAP_CHNL_CONNECT_TOUT /* BTIF needs to override for internal project needs */ +#define L2CAP_CHNL_CONNECT_TOUT 60 /* 60 seconds */ +#endif + +#define L2CAP_CHNL_CONNECT_TOUT_EXT 120 /* 120 seconds */ +#define L2CAP_CHNL_CFG_TIMEOUT 30 /* 30 seconds */ +#define L2CAP_CHNL_DISCONNECT_TOUT 10 /* 10 seconds */ +#define L2CAP_DELAY_CHECK_SM4 2 /* 2 seconds */ +#define L2CAP_WAIT_INFO_RSP_TOUT 3 /* 3 seconds */ +#define L2CAP_WAIT_UNPARK_TOUT 2 /* 2 seconds */ +#define L2CAP_LINK_INFO_RESP_TOUT 2 /* 2 seconds */ +#define L2CAP_UPDATE_CONN_PARAM_TOUT 6 /* 6 seconds */ +#define L2CAP_BLE_LINK_CONNECT_TOUT BLE_ESTABLISH_LINK_CONNECTION_TIMEOUT // configed in menuconfig +#define L2CAP_BLE_CONN_PARAM_UPD_TOUT 30 /* 30 seconds */ + +/* quick timer uses millisecond unit */ +#define L2CAP_DEFAULT_RETRANS_TOUT 2000 /* 2000 milliseconds */ +#define L2CAP_DEFAULT_MONITOR_TOUT 12000 /* 12000 milliseconds */ +#define L2CAP_FCR_ACK_TOUT 200 /* 200 milliseconds */ + +#define L2CAP_CACHE_ATT_ACL_NUM 10 + +/* Define the possible L2CAP channel states. The names of +** the states may seem a bit strange, but they are taken from +** the Bluetooth specification. +*/ +typedef enum { + CST_CLOSED, /* Channel is in clodes state */ + CST_ORIG_W4_SEC_COMP, /* Originator waits security clearence */ + CST_TERM_W4_SEC_COMP, /* Acceptor waits security clearence */ + CST_W4_L2CAP_CONNECT_RSP, /* Waiting for peer conenct response */ + CST_W4_L2CA_CONNECT_RSP, /* Waiting for upper layer connect rsp */ + CST_CONFIG, /* Negotiating configuration */ + CST_OPEN, /* Data transfer state */ + CST_W4_L2CAP_DISCONNECT_RSP, /* Waiting for peer disconnect rsp */ + CST_W4_L2CA_DISCONNECT_RSP /* Waiting for upper layer disc rsp */ +} tL2C_CHNL_STATE; + +/* Define the possible L2CAP link states +*/ +typedef enum { + LST_DISCONNECTED, + LST_CONNECT_HOLDING, + LST_CONNECTING_WAIT_SWITCH, + LST_CONNECTING, + LST_CONNECTED, + LST_DISCONNECTING +} tL2C_LINK_STATE; + + + +/* Define input events to the L2CAP link and channel state machines. The names +** of the events may seem a bit strange, but they are taken from +** the Bluetooth specification. +*/ +#define L2CEVT_LP_CONNECT_CFM 0 /* Lower layer connect confirm */ +#define L2CEVT_LP_CONNECT_CFM_NEG 1 /* Lower layer connect confirm (failed) */ +#define L2CEVT_LP_CONNECT_IND 2 /* Lower layer connect indication */ +#define L2CEVT_LP_DISCONNECT_IND 3 /* Lower layer disconnect indication */ +#define L2CEVT_LP_QOS_CFM 4 /* Lower layer QOS confirmation */ +#define L2CEVT_LP_QOS_CFM_NEG 5 /* Lower layer QOS confirmation (failed)*/ +#define L2CEVT_LP_QOS_VIOLATION_IND 6 /* Lower layer QOS violation indication */ + +#define L2CEVT_SEC_COMP 7 /* Security cleared successfully */ +#define L2CEVT_SEC_COMP_NEG 8 /* Security procedure failed */ + +#define L2CEVT_L2CAP_CONNECT_REQ 10 /* Peer connection request */ +#define L2CEVT_L2CAP_CONNECT_RSP 11 /* Peer connection response */ +#define L2CEVT_L2CAP_CONNECT_RSP_PND 12 /* Peer connection response pending */ +#define L2CEVT_L2CAP_CONNECT_RSP_NEG 13 /* Peer connection response (failed) */ +#define L2CEVT_L2CAP_CONFIG_REQ 14 /* Peer configuration request */ +#define L2CEVT_L2CAP_CONFIG_RSP 15 /* Peer configuration response */ +#define L2CEVT_L2CAP_CONFIG_RSP_NEG 16 /* Peer configuration response (failed) */ +#define L2CEVT_L2CAP_DISCONNECT_REQ 17 /* Peer disconnect request */ +#define L2CEVT_L2CAP_DISCONNECT_RSP 18 /* Peer disconnect response */ +#define L2CEVT_L2CAP_INFO_RSP 19 /* Peer information response */ +#define L2CEVT_L2CAP_DATA 20 /* Peer data */ + +#define L2CEVT_L2CA_CONNECT_REQ 21 /* Upper layer connect request */ +#define L2CEVT_L2CA_CONNECT_RSP 22 /* Upper layer connect response */ +#define L2CEVT_L2CA_CONNECT_RSP_NEG 23 /* Upper layer connect response (failed)*/ +#define L2CEVT_L2CA_CONFIG_REQ 24 /* Upper layer config request */ +#define L2CEVT_L2CA_CONFIG_RSP 25 /* Upper layer config response */ +#define L2CEVT_L2CA_CONFIG_RSP_NEG 26 /* Upper layer config response (failed) */ +#define L2CEVT_L2CA_DISCONNECT_REQ 27 /* Upper layer disconnect request */ +#define L2CEVT_L2CA_DISCONNECT_RSP 28 /* Upper layer disconnect response */ +#define L2CEVT_L2CA_DATA_READ 29 /* Upper layer data read */ +#define L2CEVT_L2CA_DATA_WRITE 30 /* Upper layer data write */ +#define L2CEVT_L2CA_FLUSH_REQ 31 /* Upper layer flush */ + +#define L2CEVT_TIMEOUT 32 /* Timeout */ +#define L2CEVT_SEC_RE_SEND_CMD 33 /* btm_sec has enough info to proceed */ + +#define L2CEVT_ACK_TIMEOUT 34 /* RR delay timeout */ + + +/* Bitmask to skip over Broadcom feature reserved (ID) to avoid sending two + successive ID values, '0' id only or both */ +#define L2CAP_ADJ_BRCM_ID 0x1 +#define L2CAP_ADJ_ZERO_ID 0x2 +#define L2CAP_ADJ_ID 0x3 + +/* Return values for l2cu_process_peer_cfg_req() */ +#define L2CAP_PEER_CFG_UNACCEPTABLE 0 +#define L2CAP_PEER_CFG_OK 1 +#define L2CAP_PEER_CFG_DISCONNECT 2 + +/* eL2CAP option constants */ +#define L2CAP_MIN_RETRANS_TOUT 2000 /* Min retransmission timeout if no flush timeout or PBF */ +#define L2CAP_MIN_MONITOR_TOUT 12000 /* Min monitor timeout if no flush timeout or PBF */ + +#define L2CAP_MAX_FCR_CFG_TRIES 2 /* Config attempts before disconnecting */ + +#ifndef MIN +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) +#endif +typedef uint8_t tL2C_BLE_FIXED_CHNLS_MASK; + +typedef struct { + UINT8 next_tx_seq; /* Next sequence number to be Tx'ed */ + UINT8 last_rx_ack; /* Last sequence number ack'ed by the peer */ + UINT8 next_seq_expected; /* Next peer sequence number expected */ + UINT8 last_ack_sent; /* Last peer sequence number ack'ed */ + UINT8 num_tries; /* Number of retries to send a packet */ + UINT8 max_held_acks; /* Max acks we can hold before sending */ + + BOOLEAN remote_busy; /* TRUE if peer has flowed us off */ + BOOLEAN local_busy; /* TRUE if we have flowed off the peer */ + + BOOLEAN rej_sent; /* Reject was sent */ + BOOLEAN srej_sent; /* Selective Reject was sent */ + BOOLEAN wait_ack; /* Transmitter is waiting ack (poll sent) */ + BOOLEAN rej_after_srej; /* Send a REJ when SREJ clears */ + + BOOLEAN send_f_rsp; /* We need to send an F-bit response */ + + UINT16 rx_sdu_len; /* Length of the SDU being received */ + BT_HDR *p_rx_sdu; /* Buffer holding the SDU being received */ + fixed_queue_t *waiting_for_ack_q; /* Buffers sent and waiting for peer to ack */ + fixed_queue_t *srej_rcv_hold_q; /* Buffers rcvd but held pending SREJ rsp */ + fixed_queue_t *retrans_q; /* Buffers being retransmitted */ + + TIMER_LIST_ENT ack_timer; /* Timer delaying RR */ + TIMER_LIST_ENT mon_retrans_timer; /* Timer Monitor or Retransmission */ + +#if (L2CAP_ERTM_STATS == TRUE) + UINT32 connect_tick_count; /* Time channel was established */ + UINT32 ertm_pkt_counts[2]; /* Packets sent and received */ + UINT32 ertm_byte_counts[2]; /* Bytes sent and received */ + UINT32 s_frames_sent[4]; /* S-frames sent (RR, REJ, RNR, SREJ) */ + UINT32 s_frames_rcvd[4]; /* S-frames rcvd (RR, REJ, RNR, SREJ) */ + UINT32 xmit_window_closed; /* # of times the xmit window was closed */ + UINT32 controller_idle; /* # of times less than 2 packets in controller */ + /* when the xmit window was closed */ + UINT32 pkts_retransmitted; /* # of packets that were retransmitted */ + UINT32 retrans_touts; /* # of retransmission timouts */ + UINT32 xmit_ack_touts; /* # of xmit ack timouts */ + +#define L2CAP_ERTM_STATS_NUM_AVG 10 +#define L2CAP_ERTM_STATS_AVG_NUM_SAMPLES 100 + UINT32 ack_delay_avg_count; + UINT32 ack_delay_avg_index; + UINT32 throughput_start; + UINT32 throughput[L2CAP_ERTM_STATS_NUM_AVG]; + UINT32 ack_delay_avg[L2CAP_ERTM_STATS_NUM_AVG]; + UINT32 ack_delay_min[L2CAP_ERTM_STATS_NUM_AVG]; + UINT32 ack_delay_max[L2CAP_ERTM_STATS_NUM_AVG]; + UINT32 ack_q_count_avg[L2CAP_ERTM_STATS_NUM_AVG]; + UINT32 ack_q_count_min[L2CAP_ERTM_STATS_NUM_AVG]; + UINT32 ack_q_count_max[L2CAP_ERTM_STATS_NUM_AVG]; +#endif +} tL2C_FCRB; + + +/* Define a registration control block. Every application (e.g. RFCOMM, SDP, +** TCS etc) that registers with L2CAP is assigned one of these. +*/ +#if (L2CAP_UCD_INCLUDED == TRUE) +#define L2C_UCD_RCB_ID 0x00 +#define L2C_UCD_STATE_UNUSED 0x00 +#define L2C_UCD_STATE_W4_DATA 0x01 +#define L2C_UCD_STATE_W4_RECEPTION 0x02 +#define L2C_UCD_STATE_W4_MTU 0x04 + +typedef struct { + UINT8 state; + tL2CAP_UCD_CB_INFO cb_info; +} tL2C_UCD_REG; +#endif + +typedef struct { + BOOLEAN in_use; + UINT16 psm; + UINT16 real_psm; /* This may be a dummy RCB for an o/b connection but */ + /* this is the real PSM that we need to connect to */ +#if (L2CAP_UCD_INCLUDED == TRUE) + tL2C_UCD_REG ucd; +#endif + + tL2CAP_APPL_INFO api; +} tL2C_RCB; + +typedef void (tL2CAP_SEC_CBACK) (BD_ADDR bd_addr, tBT_TRANSPORT transport, + void *p_ref_data, tBTM_STATUS result); + +typedef struct +{ + UINT16 psm; + tBT_TRANSPORT transport; + BOOLEAN is_originator; + tL2CAP_SEC_CBACK *p_callback; + void *p_ref_data; +}tL2CAP_SEC_DATA; + +#ifndef L2CAP_CBB_DEFAULT_DATA_RATE_BUFF_QUOTA +#define L2CAP_CBB_DEFAULT_DATA_RATE_BUFF_QUOTA 10 +#endif +/* Define a channel control block (CCB). There may be many channel control blocks +** between the same two Bluetooth devices (i.e. on the same link). +** Each CCB has unique local and remote CIDs. All channel control blocks on +** the same physical link and are chained together. +*/ +typedef struct t_l2c_ccb { + BOOLEAN in_use; /* TRUE when in use, FALSE when not */ + tL2C_CHNL_STATE chnl_state; /* Channel state */ + tL2CAP_LE_CFG_INFO local_conn_cfg; /* Our config for ble conn oriented channel */ + tL2CAP_LE_CFG_INFO peer_conn_cfg; /* Peer device config ble conn oriented channel */ + + struct t_l2c_ccb *p_next_ccb; /* Next CCB in the chain */ + struct t_l2c_ccb *p_prev_ccb; /* Previous CCB in the chain */ + struct t_l2c_linkcb *p_lcb; /* Link this CCB is assigned to */ + + UINT16 local_cid; /* Local CID */ + UINT16 remote_cid; /* Remote CID */ + + TIMER_LIST_ENT timer_entry; /* CCB Timer List Entry */ + + tL2C_RCB *p_rcb; /* Registration CB for this Channel */ + bool should_free_rcb; /* True if RCB was allocated on the heap */ + +#define IB_CFG_DONE 0x01 +#define OB_CFG_DONE 0x02 +#define RECONFIG_FLAG 0x04 /* True after initial configuration */ +#define CFG_DONE_MASK (IB_CFG_DONE | OB_CFG_DONE) + + UINT8 config_done; /* Configuration flag word */ + UINT8 local_id; /* Transaction ID for local trans */ + UINT8 remote_id; /* Transaction ID for local */ + +#define CCB_FLAG_NO_RETRY 0x01 /* no more retry */ +#define CCB_FLAG_SENT_PENDING 0x02 /* already sent pending response */ + UINT8 flags; + + tL2CAP_CFG_INFO our_cfg; /* Our saved configuration options */ + tL2CAP_CH_CFG_BITS peer_cfg_bits; /* Store what peer wants to configure */ + tL2CAP_CFG_INFO peer_cfg; /* Peer's saved configuration options */ + + fixed_queue_t *xmit_hold_q; /* Transmit data hold queue */ + BOOLEAN cong_sent; /* Set when congested status sent */ + UINT16 buff_quota; /* Buffer quota before sending congestion */ + + tL2CAP_CHNL_PRIORITY ccb_priority; /* Channel priority */ + tL2CAP_CHNL_DATA_RATE tx_data_rate; /* Channel Tx data rate */ + tL2CAP_CHNL_DATA_RATE rx_data_rate; /* Channel Rx data rate */ + + /* Fields used for eL2CAP */ + tL2CAP_ERTM_INFO ertm_info; + tL2C_FCRB fcrb; + UINT16 tx_mps; /* TX MPS adjusted based on current controller */ + UINT16 max_rx_mtu; + UINT8 fcr_cfg_tries; /* Max number of negotiation attempts */ + BOOLEAN peer_cfg_already_rejected; /* If mode rejected once, set to TRUE */ + BOOLEAN out_cfg_fcr_present; /* TRUE if cfg response shoulkd include fcr options */ + +#define L2CAP_CFG_FCS_OUR 0x01 /* Our desired config FCS option */ +#define L2CAP_CFG_FCS_PEER 0x02 /* Peer's desired config FCS option */ +#define L2CAP_BYPASS_FCS (L2CAP_CFG_FCS_OUR | L2CAP_CFG_FCS_PEER) + UINT8 bypass_fcs; + +#if (L2CAP_NON_FLUSHABLE_PB_INCLUDED == TRUE) + BOOLEAN is_flushable; /* TRUE if channel is flushable */ +#endif + +#if (L2CAP_NUM_FIXED_CHNLS > 0) || (L2CAP_UCD_INCLUDED == TRUE) + UINT16 fixed_chnl_idle_tout; /* Idle timeout to use for the fixed channel */ +#endif + UINT16 tx_data_len; +} tL2C_CCB; + +/*********************************************************************** +** Define a queue of linked CCBs. +*/ +typedef struct { + tL2C_CCB *p_first_ccb; /* The first channel in this queue */ + tL2C_CCB *p_last_ccb; /* The last channel in this queue */ +} tL2C_CCB_Q; + +#if (L2CAP_ROUND_ROBIN_CHANNEL_SERVICE == TRUE) + +/* Round-Robin service for the same priority channels */ +#define L2CAP_NUM_CHNL_PRIORITY 3 /* Total number of priority group (high, medium, low)*/ +#define L2CAP_CHNL_PRIORITY_WEIGHT 5 /* weight per priority for burst transmission quota */ +#define L2CAP_GET_PRIORITY_QUOTA(pri) ((L2CAP_NUM_CHNL_PRIORITY - (pri)) * L2CAP_CHNL_PRIORITY_WEIGHT) + +/* CCBs within the same LCB are served in round robin with priority */ +/* It will make sure that low priority channel (for example, HF signaling on RFCOMM) */ +/* can be sent to headset even if higher priority channel (for example, AV media channel) */ +/* is congested. */ + +typedef struct { + tL2C_CCB *p_serve_ccb; /* current serving ccb within priority group */ + tL2C_CCB *p_first_ccb; /* first ccb of priority group */ + UINT8 num_ccb; /* number of channels in priority group */ + UINT8 quota; /* burst transmission quota */ +} tL2C_RR_SERV; + +#endif /* (L2CAP_ROUND_ROBIN_CHANNEL_SERVICE == TRUE) */ + + +/* Define a link control block. There is one link control block between +** this device and any other device (i.e. BD ADDR). +*/ +typedef struct t_l2c_linkcb { + BOOLEAN in_use; /* TRUE when in use, FALSE when not */ + tL2C_LINK_STATE link_state; + BOOLEAN is_aux; /* This variable used for BLE 5.0 or higher version when do auxiliary connection */ + TIMER_LIST_ENT timer_entry; /* Timer list entry for timeout evt */ + UINT16 handle; /* The handle used with LM */ + + tL2C_CCB_Q ccb_queue; /* Queue of CCBs on this LCB */ + + tL2C_CCB *p_pending_ccb; /* ccb of waiting channel during link disconnect */ + TIMER_LIST_ENT info_timer_entry; /* Timer entry for info resp timeout evt */ + TIMER_LIST_ENT upda_con_timer; /* Timer entry for update connection parametr */ + BD_ADDR remote_bd_addr; /* The BD address of the remote */ + + UINT8 link_role; /* Master or slave */ + UINT8 id; + UINT8 cur_echo_id; /* Current id value for echo request */ + tL2CA_ECHO_RSP_CB *p_echo_rsp_cb; /* Echo response callback */ + UINT16 idle_timeout; /* Idle timeout */ + BOOLEAN is_bonding; /* True - link active only for bonding */ + + UINT16 link_flush_tout; /* Flush timeout used */ + + UINT16 link_xmit_quota; /* Num outstanding pkts allowed */ + UINT16 sent_not_acked; /* Num packets sent but not acked */ + + BOOLEAN partial_segment_being_sent; /* Set TRUE when a partial segment */ + /* is being sent. */ + BOOLEAN w4_info_rsp; /* TRUE when info request is active */ + UINT8 info_rx_bits; /* set 1 if received info type */ + UINT32 peer_ext_fea; /* Peer's extended features mask */ + list_t *link_xmit_data_q; /* Link transmit data buffer queue */ + + UINT8 peer_chnl_mask[L2CAP_FIXED_CHNL_ARRAY_SIZE]; +#if (L2CAP_UCD_INCLUDED == TRUE) + UINT16 ucd_mtu; /* peer MTU on UCD */ + fixed_queue_t *ucd_out_sec_pending_q; /* Security pending outgoing UCD packet */ + fixed_queue_t *ucd_in_sec_pending_q; /* Security pending incoming UCD packet */ +#endif + + BT_HDR *p_hcit_rcv_acl; /* Current HCIT ACL buf being rcvd */ + UINT16 idle_timeout_sv; /* Save current Idle timeout */ + UINT8 acl_priority; /* L2C_PRIORITY_NORMAL or L2C_PRIORITY_HIGH */ + tL2CA_NOCP_CB *p_nocp_cb; /* Num Cmpl pkts callback */ + +#if (L2CAP_NUM_FIXED_CHNLS > 0) + tL2C_CCB *p_fixed_ccbs[L2CAP_NUM_FIXED_CHNLS]; + UINT16 disc_reason; +#endif + + tBT_TRANSPORT transport; +#if (BLE_INCLUDED == TRUE) + tBLE_ADDR_TYPE open_addr_type; /* be set by open API */ + tBLE_ADDR_TYPE ble_addr_type; + UINT16 tx_data_len; /* tx data length used in data length extension */ + fixed_queue_t *le_sec_pending_q; /* LE coc channels waiting for security check completion */ + UINT8 sec_act; +#define L2C_BLE_CONN_UPDATE_DISABLE 0x1 /* disable update connection parameters */ +#define L2C_BLE_NEW_CONN_PARAM 0x2 /* new connection parameter to be set */ +#define L2C_BLE_UPDATE_PENDING 0x4 /* waiting for connection update finished */ +#define L2C_BLE_NOT_DEFAULT_PARAM 0x8 /* not using default connection parameters */ +#define L2C_BLE_UPDATE_PARAM_FULL 0x10 /* update connection parameters full, can not update */ + UINT8 conn_update_mask; + /* cache connection parameters that wait to update */ + UINT16 waiting_update_conn_min_interval; + UINT16 waiting_update_conn_max_interval; + UINT16 waiting_update_conn_latency; + UINT16 waiting_update_conn_timeout; + /* cache parameters that is being updated */ + UINT16 updating_conn_min_interval; + UINT16 updating_conn_max_interval; + bool updating_param_flag; + /* current connection parameters that current connection is using */ + UINT16 current_used_conn_interval; + UINT16 current_used_conn_latency; + UINT16 current_used_conn_timeout; + /* connection parameters update order: + waiting_update_conn_xx -> updating_conn_xx -> current_used_conn_xx + */ + /* create connection retry count*/ + UINT8 retry_create_con; + UINT32 start_time_s; +#endif + +#if (L2CAP_ROUND_ROBIN_CHANNEL_SERVICE == TRUE) + /* each priority group is limited burst transmission */ + /* round robin service for the same priority channels */ + tL2C_RR_SERV rr_serv[L2CAP_NUM_CHNL_PRIORITY]; + UINT8 rr_pri; /* current serving priority group */ +#endif + +} tL2C_LCB; + + +/* Define the L2CAP control structure +*/ +typedef struct { + UINT8 l2cap_trace_level; + UINT16 controller_xmit_window; /* Total ACL window for all links */ + + UINT16 round_robin_quota; /* Round-robin link quota */ + UINT16 round_robin_unacked; /* Round-robin unacked */ + BOOLEAN check_round_robin; /* Do a round robin check */ + + BOOLEAN is_cong_cback_context; + + list_t *p_lcb_pool; /* Link Control Block pool */ + list_t *p_ccb_pool; /* Channel Control Block pool */ + tL2C_RCB rcb_pool[MAX_L2CAP_CLIENTS]; /* Registration info pool */ + + + UINT8 desire_role; /* desire to be master/slave when accepting a connection */ + BOOLEAN disallow_switch; /* FALSE, to allow switch at create conn */ + UINT16 num_lm_acl_bufs; /* # of ACL buffers on controller */ + UINT16 idle_timeout; /* Idle timeout */ + + list_t *rcv_pending_q; /* Recv pending queue */ + TIMER_LIST_ENT rcv_hold_tle; /* Timer list entry for rcv hold */ + + tL2C_LCB *p_cur_hcit_lcb; /* Current HCI Transport buffer */ + UINT16 num_links_active; /* Number of links active */ + +#if (L2CAP_NON_FLUSHABLE_PB_INCLUDED == TRUE) + UINT16 non_flushable_pbf; /* L2CAP_PKT_START_NON_FLUSHABLE if controller supports */ + /* Otherwise, L2CAP_PKT_START */ + BOOLEAN is_flush_active; /* TRUE if an HCI_Enhanced_Flush has been sent */ +#endif + +#if L2CAP_CONFORMANCE_TESTING == TRUE + UINT32 test_info_resp; /* Conformance testing needs a dynamic response */ +#endif + +#if (L2CAP_NUM_FIXED_CHNLS > 0) + tL2CAP_FIXED_CHNL_REG fixed_reg[L2CAP_NUM_FIXED_CHNLS]; /* Reg info for fixed channels */ +#endif + +#if (BLE_INCLUDED == TRUE) + UINT16 num_ble_links_active; /* Number of LE links active */ + BOOLEAN is_ble_connecting; + BD_ADDR ble_connecting_bda; + UINT16 controller_le_xmit_window; /* Total ACL window for all links */ + tL2C_BLE_FIXED_CHNLS_MASK l2c_ble_fixed_chnls_mask; // LE fixed channels mask + UINT16 num_lm_ble_bufs; /* # of ACL buffers on controller */ + UINT16 ble_round_robin_quota; /* Round-robin link quota */ + UINT16 ble_round_robin_unacked; /* Round-robin unacked */ + BOOLEAN ble_check_round_robin; /* Do a round robin check */ + tL2C_RCB ble_rcb_pool[BLE_MAX_L2CAP_CLIENTS]; /* Registration info pool */ +#endif + + tL2CA_ECHO_DATA_CB *p_echo_data_cb; /* Echo data callback */ + +#if (defined(L2CAP_HIGH_PRI_CHAN_QUOTA_IS_CONFIGURABLE) && (L2CAP_HIGH_PRI_CHAN_QUOTA_IS_CONFIGURABLE == TRUE)) + UINT16 high_pri_min_xmit_quota; /* Minimum number of ACL credit for high priority link */ +#endif /* (L2CAP_HIGH_PRI_CHAN_QUOTA_IS_CONFIGURABLE == TRUE) */ + + UINT16 dyn_psm; +} tL2C_CB; + + + +/* Define a structure that contains the information about a connection. +** This structure is used to pass between functions, and not all the +** fields will always be filled in. +*/ +typedef struct { + BD_ADDR bd_addr; /* Remote BD address */ + UINT8 status; /* Connection status */ + UINT16 psm; /* PSM of the connection */ + UINT16 l2cap_result; /* L2CAP result */ + UINT16 l2cap_status; /* L2CAP status */ + UINT16 remote_cid; /* Remote CID */ +} tL2C_CONN_INFO; + + +typedef void (tL2C_FCR_MGMT_EVT_HDLR) (UINT8, tL2C_CCB *); + +/* The offset in a buffer that L2CAP will use when building commands. +*/ +#define L2CAP_SEND_CMD_OFFSET 0 + + +/* Number of ACL buffers to use for high priority channel +*/ +#if (!defined(L2CAP_HIGH_PRI_CHAN_QUOTA_IS_CONFIGURABLE) || (L2CAP_HIGH_PRI_CHAN_QUOTA_IS_CONFIGURABLE == FALSE)) +#define L2CAP_HIGH_PRI_MIN_XMIT_QUOTA_A (L2CAP_HIGH_PRI_MIN_XMIT_QUOTA) +#else +#define L2CAP_HIGH_PRI_MIN_XMIT_QUOTA_A (l2cb.high_pri_min_xmit_quota) +#endif + +#ifdef __cplusplus +extern "C" { +#endif + + +/* L2CAP global data +************************************ +*/ +#if (!defined L2C_DYNAMIC_MEMORY) || (L2C_DYNAMIC_MEMORY == FALSE) +extern tL2C_CB l2cb; +#else +extern tL2C_CB *l2c_cb_ptr; +#define l2cb (*l2c_cb_ptr) +#endif + + +/* Functions provided by l2c_main.c +************************************ +*/ +void l2c_init(void); +void l2c_free(void); + +extern void l2c_process_timeout (TIMER_LIST_ENT *p_tle); +extern UINT8 l2c_data_write (UINT16 cid, BT_HDR *p_data, UINT16 flag); +extern void l2c_rcv_acl_data (BT_HDR *p_msg); +extern void l2c_process_held_packets (BOOLEAN timed_out); + +/* Functions provided by l2c_utils.c +************************************ +*/ +extern tL2C_LCB *l2cu_allocate_lcb (BD_ADDR p_bd_addr, BOOLEAN is_bonding, tBT_TRANSPORT transport); +extern BOOLEAN l2cu_start_post_bond_timer (UINT16 handle); +extern void l2cu_release_lcb (tL2C_LCB *p_lcb); +extern tL2C_LCB *l2cu_find_lcb_by_bd_addr (BD_ADDR p_bd_addr, tBT_TRANSPORT transport); +extern tL2C_LCB *l2cu_find_lcb_by_handle (UINT16 handle); +extern uint8_t l2cu_plcb_active_count(void); +extern void l2cu_update_lcb_4_bonding (BD_ADDR p_bd_addr, BOOLEAN is_bonding); + +extern UINT8 l2cu_get_conn_role (tL2C_LCB *p_this_lcb); +extern BOOLEAN l2cu_set_acl_priority (BD_ADDR bd_addr, UINT8 priority, BOOLEAN reset_after_rs); + +extern void l2cu_enqueue_ccb (tL2C_CCB *p_ccb); +extern void l2cu_dequeue_ccb (tL2C_CCB *p_ccb); +extern void l2cu_change_pri_ccb (tL2C_CCB *p_ccb, tL2CAP_CHNL_PRIORITY priority); + +extern tL2C_CCB *l2cu_allocate_ccb (tL2C_LCB *p_lcb, UINT16 cid); +extern void l2cu_release_ccb (tL2C_CCB *p_ccb); +extern tL2C_CCB *l2cu_find_ccb_by_cid (tL2C_LCB *p_lcb, UINT16 local_cid); +extern tL2C_CCB *l2cu_find_free_ccb(void); +extern tL2C_CCB *l2cu_find_ccb_by_remote_cid (tL2C_LCB *p_lcb, UINT16 remote_cid); +extern void l2cu_adj_id (tL2C_LCB *p_lcb, UINT8 adj_mask); +extern BOOLEAN l2c_is_cmd_rejected (UINT8 cmd_code, UINT8 id, tL2C_LCB *p_lcb); + +extern void l2cu_send_peer_cmd_reject (tL2C_LCB *p_lcb, UINT16 reason, + UINT8 rem_id, UINT16 p1, UINT16 p2); +extern void l2cu_send_peer_connect_req (tL2C_CCB *p_ccb); +extern void l2cu_send_peer_connect_rsp (tL2C_CCB *p_ccb, UINT16 result, UINT16 status); +extern void l2cu_send_peer_config_req (tL2C_CCB *p_ccb, tL2CAP_CFG_INFO *p_cfg); +extern void l2cu_send_peer_config_rsp (tL2C_CCB *p_ccb, tL2CAP_CFG_INFO *p_cfg); +extern void l2cu_send_peer_config_rej (tL2C_CCB *p_ccb, UINT8 *p_data, UINT16 data_len, UINT16 rej_len); +extern void l2cu_send_peer_disc_req (tL2C_CCB *p_ccb); +extern void l2cu_send_peer_disc_rsp (tL2C_LCB *p_lcb, UINT8 remote_id, UINT16 local_cid, UINT16 remote_cid); +extern void l2cu_send_peer_echo_req (tL2C_LCB *p_lcb, UINT8 *p_data, UINT16 data_len); +extern void l2cu_send_peer_echo_rsp (tL2C_LCB *p_lcb, UINT8 id, UINT8 *p_data, UINT16 data_len); +extern void l2cu_send_peer_info_rsp (tL2C_LCB *p_lcb, UINT8 id, UINT16 info_type); +extern void l2cu_reject_connection (tL2C_LCB *p_lcb, UINT16 remote_cid, UINT8 rem_id, UINT16 result); +extern void l2cu_send_peer_info_req (tL2C_LCB *p_lcb, UINT16 info_type); +extern void l2cu_set_acl_hci_header (BT_HDR *p_buf, tL2C_CCB *p_ccb); +extern void l2cu_check_channel_congestion (tL2C_CCB *p_ccb); +extern void l2cu_disconnect_chnl (tL2C_CCB *p_ccb); + +#if (L2CAP_NON_FLUSHABLE_PB_INCLUDED == TRUE) +extern void l2cu_set_non_flushable_pbf(BOOLEAN); +#endif + +#if (BLE_INCLUDED == TRUE) +extern void l2cu_send_peer_ble_par_req (tL2C_LCB *p_lcb, UINT16 min_int, UINT16 max_int, UINT16 latency, UINT16 timeout); +extern void l2cu_send_peer_ble_par_rsp (tL2C_LCB *p_lcb, UINT16 reason, UINT8 rem_id); +#endif + +extern BOOLEAN l2cu_initialize_fixed_ccb (tL2C_LCB *p_lcb, UINT16 fixed_cid, tL2CAP_FCR_OPTS *p_fcr); +extern void l2cu_no_dynamic_ccbs (tL2C_LCB *p_lcb); +extern void l2cu_process_fixed_chnl_resp (tL2C_LCB *p_lcb); + +/* Functions provided by l2c_ucd.c +************************************ +*/ +#if (L2CAP_UCD_INCLUDED == TRUE) +void l2c_ucd_delete_sec_pending_q(tL2C_LCB *p_lcb); +void l2c_ucd_enqueue_pending_out_sec_q(tL2C_CCB *p_ccb, void *p_data); +BOOLEAN l2c_ucd_check_pending_info_req(tL2C_CCB *p_ccb); +BOOLEAN l2c_ucd_check_pending_out_sec_q(tL2C_CCB *p_ccb); +void l2c_ucd_send_pending_out_sec_q(tL2C_CCB *p_ccb); +void l2c_ucd_discard_pending_out_sec_q(tL2C_CCB *p_ccb); +BOOLEAN l2c_ucd_check_pending_in_sec_q(tL2C_CCB *p_ccb); +void l2c_ucd_send_pending_in_sec_q(tL2C_CCB *p_ccb); +void l2c_ucd_discard_pending_in_sec_q(tL2C_CCB *p_ccb); +BOOLEAN l2c_ucd_check_rx_pkts(tL2C_LCB *p_lcb, BT_HDR *p_msg); +BOOLEAN l2c_ucd_process_event(tL2C_CCB *p_ccb, UINT16 event, void *p_data); +#endif + +#if (BLE_INCLUDED == TRUE) +extern void l2cu_send_peer_ble_par_req (tL2C_LCB *p_lcb, UINT16 min_int, UINT16 max_int, UINT16 latency, UINT16 timeout); +extern void l2cu_send_peer_ble_par_rsp (tL2C_LCB *p_lcb, UINT16 reason, UINT8 rem_id); +extern void l2cu_reject_ble_connection (tL2C_LCB *p_lcb, UINT8 rem_id, UINT16 result); +extern void l2cu_send_peer_ble_credit_based_conn_res (tL2C_CCB *p_ccb, UINT16 result); +extern void l2cu_send_peer_ble_credit_based_conn_req (tL2C_CCB *p_ccb); +extern void l2cu_send_peer_ble_flow_control_credit(tL2C_CCB *p_ccb, UINT16 credit_value); +extern void l2cu_send_peer_ble_credit_based_disconn_req(tL2C_CCB *p_ccb); + +#endif + +extern BOOLEAN l2cu_initialize_fixed_ccb (tL2C_LCB *p_lcb, UINT16 fixed_cid, tL2CAP_FCR_OPTS *p_fcr); +extern void l2cu_no_dynamic_ccbs (tL2C_LCB *p_lcb); +extern void l2cu_process_fixed_chnl_resp (tL2C_LCB *p_lcb); + + +/* Functions provided for Broadcom Aware +**************************************** +*/ +extern BOOLEAN l2cu_check_feature_req (tL2C_LCB *p_lcb, UINT8 id, UINT8 *p_data, UINT16 data_len); +extern void l2cu_check_feature_rsp (tL2C_LCB *p_lcb, UINT8 id, UINT8 *p_data, UINT16 data_len); +extern void l2cu_send_feature_req (tL2C_CCB *p_ccb); + +extern tL2C_RCB *l2cu_allocate_rcb (UINT16 psm); +extern tL2C_RCB *l2cu_find_rcb_by_psm (UINT16 psm); +extern void l2cu_release_rcb (tL2C_RCB *p_rcb); +extern tL2C_RCB *l2cu_allocate_ble_rcb (UINT16 psm); +extern tL2C_RCB *l2cu_find_ble_rcb_by_psm (UINT16 psm); + +#if (L2CAP_COC_INCLUDED == TRUE) +extern UINT8 l2cu_process_peer_cfg_req (tL2C_CCB *p_ccb, tL2CAP_CFG_INFO *p_cfg); +extern void l2cu_process_peer_cfg_rsp (tL2C_CCB *p_ccb, tL2CAP_CFG_INFO *p_cfg); +extern void l2cu_process_our_cfg_req (tL2C_CCB *p_ccb, tL2CAP_CFG_INFO *p_cfg); +extern void l2cu_process_our_cfg_rsp (tL2C_CCB *p_ccb, tL2CAP_CFG_INFO *p_cfg); +#endif // (L2CAP_COC_INCLUDED == TRUE) + +extern void l2cu_device_reset (void); +extern tL2C_LCB *l2cu_find_lcb_by_state (tL2C_LINK_STATE state); +extern BOOLEAN l2cu_lcb_disconnecting (void); + +extern BOOLEAN l2cu_create_conn (tL2C_LCB *p_lcb, tBT_TRANSPORT transport); +extern BOOLEAN l2cu_create_conn_after_switch (tL2C_LCB *p_lcb); +extern BT_HDR *l2cu_get_next_buffer_to_send (tL2C_LCB *p_lcb); +extern void l2cu_resubmit_pending_sec_req (BD_ADDR p_bda); +extern void l2cu_initialize_amp_ccb (tL2C_LCB *p_lcb); +extern void l2cu_adjust_out_mps (tL2C_CCB *p_ccb); + +/* Functions provided by l2c_link.c +************************************ +*/ +extern BOOLEAN l2c_link_hci_conn_req (BD_ADDR bd_addr); +extern BOOLEAN l2c_link_hci_conn_comp (UINT8 status, UINT16 handle, BD_ADDR p_bda); +extern BOOLEAN l2c_link_hci_disc_comp (UINT16 handle, UINT8 reason); +extern BOOLEAN l2c_link_hci_qos_violation (UINT16 handle); +extern void l2c_link_timeout (tL2C_LCB *p_lcb); +extern void l2c_info_timeout (tL2C_LCB *p_lcb); +extern void l2c_link_check_send_pkts (tL2C_LCB *p_lcb, tL2C_CCB *p_ccb, BT_HDR *p_buf); +extern void l2c_link_adjust_allocation (void); +extern void l2c_link_process_num_completed_pkts (UINT8 *p); +extern void l2c_link_process_num_completed_blocks (UINT8 controller_id, UINT8 *p, UINT16 evt_len); +extern void l2c_link_processs_num_bufs (UINT16 num_lm_acl_bufs); +extern UINT8 l2c_link_pkts_rcvd (UINT16 *num_pkts, UINT16 *handles); +extern void l2c_link_role_changed (BD_ADDR bd_addr, UINT8 new_role, UINT8 hci_status); +extern void l2c_link_sec_comp (BD_ADDR p_bda, tBT_TRANSPORT transport, void *p_ref_data, UINT8 status); +extern void l2c_link_segments_xmitted (BT_HDR *p_msg); +extern void l2c_pin_code_request (BD_ADDR bd_addr); +extern void l2c_link_adjust_chnl_allocation (void); + +#if (BLE_INCLUDED == TRUE) +extern void l2c_link_processs_ble_num_bufs (UINT16 num_lm_acl_bufs); +#endif + +#if L2CAP_WAKE_PARKED_LINK == TRUE +extern BOOLEAN l2c_link_check_power_mode ( tL2C_LCB *p_lcb ); +#define L2C_LINK_CHECK_POWER_MODE(x) l2c_link_check_power_mode ((x)) +#else // L2CAP_WAKE_PARKED_LINK +#define L2C_LINK_CHECK_POWER_MODE(x) (FALSE) +#endif // L2CAP_WAKE_PARKED_LINK + +#if L2CAP_CONFORMANCE_TESTING == TRUE +/* Used only for conformance testing */ +extern void l2cu_set_info_rsp_mask (UINT32 mask); +#endif + +/* Functions provided by l2c_csm.c +************************************ +*/ +#if (L2CAP_COC_INCLUDED == TRUE) +extern void l2c_csm_execute (tL2C_CCB *p_ccb, UINT16 event, void *p_data); +#endif +extern void l2c_enqueue_peer_data (tL2C_CCB *p_ccb, BT_HDR *p_buf); + +/* Functions provided by l2c_fcr.c +************************************ +*/ +extern void l2c_fcr_cleanup (tL2C_CCB *p_ccb); +extern void l2c_fcr_proc_pdu (tL2C_CCB *p_ccb, BT_HDR *p_buf); +extern void l2c_fcr_proc_tout (tL2C_CCB *p_ccb); +extern void l2c_fcr_proc_ack_tout (tL2C_CCB *p_ccb); +extern void l2c_fcr_send_S_frame (tL2C_CCB *p_ccb, UINT16 function_code, UINT16 pf_bit); +extern BT_HDR *l2c_fcr_clone_buf (BT_HDR *p_buf, UINT16 new_offset, UINT16 no_of_bytes); +extern BOOLEAN l2c_fcr_is_flow_controlled (tL2C_CCB *p_ccb); +extern BT_HDR *l2c_fcr_get_next_xmit_sdu_seg (tL2C_CCB *p_ccb, UINT16 max_packet_length); +extern void l2c_fcr_start_timer (tL2C_CCB *p_ccb); + +/* Configuration negotiation */ +extern UINT8 l2c_fcr_chk_chan_modes (tL2C_CCB *p_ccb); +extern BOOLEAN l2c_fcr_adj_our_req_options (tL2C_CCB *p_ccb, tL2CAP_CFG_INFO *p_cfg); +extern void l2c_fcr_adj_our_rsp_options (tL2C_CCB *p_ccb, tL2CAP_CFG_INFO *p_peer_cfg); +extern BOOLEAN l2c_fcr_renegotiate_chan(tL2C_CCB *p_ccb, tL2CAP_CFG_INFO *p_cfg); +extern UINT8 l2c_fcr_process_peer_cfg_req(tL2C_CCB *p_ccb, tL2CAP_CFG_INFO *p_cfg); +extern void l2c_fcr_adj_monitor_retran_timeout (tL2C_CCB *p_ccb); +extern void l2c_fcr_stop_timer (tL2C_CCB *p_ccb); +extern void l2c_fcr_free_timer (tL2C_CCB *p_ccb); +/* Functions provided by l2c_ble.c +************************************ +*/ +#if (BLE_INCLUDED == TRUE) +extern BOOLEAN l2cble_create_conn (tL2C_LCB *p_lcb); +extern void l2cble_process_sig_cmd (tL2C_LCB *p_lcb, UINT8 *p, UINT16 pkt_len); +extern void l2cble_conn_comp (UINT16 handle, UINT8 role, BD_ADDR bda, tBLE_ADDR_TYPE type, + UINT16 conn_interval, UINT16 conn_latency, UINT16 conn_timeout); +extern BOOLEAN l2cble_init_direct_conn (tL2C_LCB *p_lcb); +extern void l2cble_notify_le_connection (BD_ADDR bda); +extern void l2c_ble_link_adjust_allocation (void); +extern void l2cble_process_conn_update_evt (UINT16 handle, UINT8 status, UINT16 conn_interval, + UINT16 conn_latency, UINT16 conn_timeout); +extern void l2cble_get_conn_param_format_err_from_contoller(UINT8 status, UINT16 handle); + +extern void l2cble_credit_based_conn_req (tL2C_CCB *p_ccb); +extern void l2cble_credit_based_conn_res (tL2C_CCB *p_ccb, UINT16 result); +extern void l2cble_send_peer_disc_req(tL2C_CCB *p_ccb); +extern void l2cble_send_flow_control_credit(tL2C_CCB *p_ccb, UINT16 credit_value); +extern BOOLEAN l2ble_sec_access_req(BD_ADDR bd_addr, UINT16 psm, BOOLEAN is_originator, tL2CAP_SEC_CBACK *p_callback, void *p_ref_data); + + +#if (defined BLE_LLT_INCLUDED) && (BLE_LLT_INCLUDED == TRUE) +extern void l2cble_process_rc_param_request_evt(UINT16 handle, UINT16 int_min, UINT16 int_max, + UINT16 latency, UINT16 timeout); +#endif + +extern void l2cble_update_data_length(tL2C_LCB *p_lcb); +extern void l2cble_set_fixed_channel_tx_data_length(BD_ADDR remote_bda, UINT16 fix_cid, + UINT16 tx_mtu); +extern void l2c_send_update_conn_params_cb(tL2C_LCB *p_lcb, UINT8 status); +extern void l2cble_process_data_length_change_event(UINT16 handle, UINT16 tx_data_len, + UINT16 rx_data_len); +extern UINT32 CalConnectParamTimeout(tL2C_LCB *p_lcb); + +#endif +extern void l2cu_process_fixed_disc_cback (tL2C_LCB *p_lcb); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/bt/host/bluedroid/stack/l2cap/l2c_api.c b/lib/bt/host/bluedroid/stack/l2cap/l2c_api.c new file mode 100644 index 00000000..fa01790d --- /dev/null +++ b/lib/bt/host/bluedroid/stack/l2cap/l2c_api.c @@ -0,0 +1,2456 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains the L2CAP API code + * + ******************************************************************************/ + +//#define LOG_TAG "bt_l2cap" + +//#include +#include +#include +#include "common/bt_trace.h" +#include "stack/bt_types.h" +#include "stack/hcidefs.h" +#include "stack/hcimsgs.h" +#include "stack/l2cdefs.h" +#include "l2c_int.h" +#include "stack/btu.h" +#include "stack/btm_api.h" +#include "osi/allocator.h" +#include "gatt_int.h" +#if (CLASSIC_BT_INCLUDED == TRUE) +/******************************************************************************* +** +** Function L2CA_Register +** +** Description Other layers call this function to register for L2CAP +** services. +** +** Returns PSM to use or zero if error. Typically, the PSM returned +** is the same as was passed in, but for an outgoing-only +** connection to a dynamic PSM, a "virtual" PSM is returned +** and should be used in the calls to L2CA_ConnectReq(), +** L2CA_ErtmConnectReq() and L2CA_Deregister() +** +*******************************************************************************/ +UINT16 L2CA_Register (UINT16 psm, tL2CAP_APPL_INFO *p_cb_info) +{ + tL2C_RCB *p_rcb; + UINT16 vpsm = psm; + + + /* Verify that the required callback info has been filled in + ** Note: Connection callbacks are required but not checked + ** for here because it is possible to be only a client + ** or only a server. + */ + if ((!p_cb_info->pL2CA_ConfigCfm_Cb) + || (!p_cb_info->pL2CA_ConfigInd_Cb) + || (!p_cb_info->pL2CA_DataInd_Cb) + || (!p_cb_info->pL2CA_DisconnectInd_Cb)) { + L2CAP_TRACE_ERROR ("L2CAP - no cb registering PSM: 0x%04x", psm); + return (0); + } + + /* Verify PSM is valid */ + if (L2C_INVALID_PSM(psm)) { + L2CAP_TRACE_ERROR ("L2CAP - invalid PSM value, PSM: 0x%04x", psm); + return (0); + } + + /* Check if this is a registration for an outgoing-only connection to */ + /* a dynamic PSM. If so, allocate a "virtual" PSM for the app to use. */ + if ( (psm >= 0x1001) && (p_cb_info->pL2CA_ConnectInd_Cb == NULL) ) { + for (vpsm = 0x1002; vpsm < 0x8000; vpsm += 2) { + if ((p_rcb = l2cu_find_rcb_by_psm (vpsm)) == NULL) { + break; + } + } + + //L2CAP_TRACE_API ("L2CA_Register - Real PSM: 0x%04x Virtual PSM: 0x%04x", psm, vpsm); + } + + /* If registration block already there, just overwrite it */ + if ((p_rcb = l2cu_find_rcb_by_psm (vpsm)) == NULL) { + if ((p_rcb = l2cu_allocate_rcb (vpsm)) == NULL) { + L2CAP_TRACE_WARNING ("L2CAP - no RCB available, PSM: 0x%04x vPSM: 0x%04x", psm, vpsm); + return (0); + } + } + + p_rcb->api = *p_cb_info; + p_rcb->real_psm = psm; + + return (vpsm); +} + + + +/******************************************************************************* +** +** Function L2CA_Deregister +** +** Description Other layers call this function to de-register for L2CAP +** services. +** +** Returns void +** +*******************************************************************************/ +void L2CA_Deregister (UINT16 psm) +{ + tL2C_RCB *p_rcb; + tL2C_CCB *p_ccb; + tL2C_LCB *p_lcb; + list_node_t *p_node = NULL; + + if ((p_rcb = l2cu_find_rcb_by_psm (psm)) != NULL) { + for (p_node = list_begin(l2cb.p_lcb_pool); p_node; p_node = list_next(p_node)) { + p_lcb = list_node(p_node); + if (p_lcb->in_use) { + if (((p_ccb = p_lcb->ccb_queue.p_first_ccb) == NULL) + || (p_lcb->link_state == LST_DISCONNECTING)) { + continue; + } + + if ((p_ccb->in_use) && + ((p_ccb->chnl_state == CST_W4_L2CAP_DISCONNECT_RSP) || + (p_ccb->chnl_state == CST_W4_L2CA_DISCONNECT_RSP))) { + continue; + } + + if (p_ccb->p_rcb == p_rcb) { + l2c_csm_execute (p_ccb, L2CEVT_L2CA_DISCONNECT_REQ, NULL); + } + } + } + l2cu_release_rcb (p_rcb); + } else { + L2CAP_TRACE_WARNING ("L2CAP - PSM: 0x%04x not found for deregistration", psm); + } +} + +/******************************************************************************* +** +** Function L2CA_AllocatePSM +** +** Description Other layers call this function to find an unused PSM for L2CAP +** services. +** +** Returns PSM to use. +** +*******************************************************************************/ +UINT16 L2CA_AllocatePSM(void) +{ + BOOLEAN done = FALSE; + UINT16 psm = l2cb.dyn_psm; + + while (!done) { + psm += 2; + if (psm > 0xfeff) { + psm = 0x1001; + } else if (psm & 0x0100) { + /* the upper byte must be even */ + psm += 0x0100; + } + + /* if psm is in range of reserved BRCM Aware features */ + if ((BRCM_RESERVED_PSM_START <= psm) && (psm <= BRCM_RESERVED_PSM_END)) { + continue; + } + + /* make sure the newlly allocated psm is not used right now */ + if ((l2cu_find_rcb_by_psm (psm)) == NULL) { + done = TRUE; + } + } + l2cb.dyn_psm = psm; + + return (psm); +} + +/******************************************************************************* +** +** Function L2CA_ConnectReq +** +** Description Higher layers call this function to create an L2CAP connection. +** Note that the connection is not established at this time, but +** connection establishment gets started. The callback function +** will be invoked when connection establishes or fails. +** +** Returns the CID of the connection, or 0 if it failed to start +** +*******************************************************************************/ +UINT16 L2CA_ConnectReq (UINT16 psm, BD_ADDR p_bd_addr) +{ + return L2CA_ErtmConnectReq (psm, p_bd_addr, NULL); +} + +/******************************************************************************* +** +** Function L2CA_ErtmConnectReq +** +** Description Higher layers call this function to create an L2CAP connection. +** Note that the connection is not established at this time, but +** connection establishment gets started. The callback function +** will be invoked when connection establishes or fails. +** +** Parameters: PSM: L2CAP PSM for the connection +** BD address of the peer +** Enhaced retransmission mode configurations + +** Returns the CID of the connection, or 0 if it failed to start +** +*******************************************************************************/ +UINT16 L2CA_ErtmConnectReq (UINT16 psm, BD_ADDR p_bd_addr, tL2CAP_ERTM_INFO *p_ertm_info) +{ + tL2C_LCB *p_lcb; + tL2C_CCB *p_ccb; + tL2C_RCB *p_rcb; + + //counter_add("l2cap.conn.req", 1); + L2CAP_TRACE_API ("L2CA_ErtmConnectReq() PSM: 0x%04x BDA: %08x%04x p_ertm_info: %p allowed:0x%x preferred:%d", psm, + (p_bd_addr[0] << 24) + (p_bd_addr[1] << 16) + (p_bd_addr[2] << 8) + p_bd_addr[3], + (p_bd_addr[4] << 8) + p_bd_addr[5], p_ertm_info, + (p_ertm_info) ? p_ertm_info->allowed_modes : 0, + (p_ertm_info) ? p_ertm_info->preferred_mode : 0); + + /* Fail if we have not established communications with the controller */ + if (!BTM_IsDeviceUp()) { + L2CAP_TRACE_WARNING ("L2CAP connect req - BTU not ready"); + return (0); + } + /* Fail if the PSM is not registered */ + if ((p_rcb = l2cu_find_rcb_by_psm (psm)) == NULL) { + L2CAP_TRACE_WARNING ("L2CAP - no RCB for L2CA_conn_req, PSM: 0x%04x", psm); + return (0); + } + + /* First, see if we already have a link to the remote */ + /* assume all ERTM l2cap connection is going over BR/EDR for now */ + if ((p_lcb = l2cu_find_lcb_by_bd_addr (p_bd_addr, BT_TRANSPORT_BR_EDR)) == NULL) { + /* No link. Get an LCB and start link establishment */ + if ( ((p_lcb = l2cu_allocate_lcb (p_bd_addr, FALSE, BT_TRANSPORT_BR_EDR)) == NULL) + /* currently use BR/EDR for ERTM mode l2cap connection */ + || (l2cu_create_conn(p_lcb, BT_TRANSPORT_BR_EDR) == FALSE) ) { + L2CAP_TRACE_WARNING ("L2CAP - conn not started for PSM: 0x%04x p_lcb: %p", psm, p_lcb); + return (0); + } + } + + /* Allocate a channel control block */ + if ((p_ccb = l2cu_allocate_ccb (p_lcb, 0)) == NULL) { + L2CAP_TRACE_WARNING ("L2CAP - no CCB for L2CA_conn_req, PSM: 0x%04x", psm); + return (0); + } + + /* Save registration info */ + p_ccb->p_rcb = p_rcb; + + if (p_ertm_info) { + p_ccb->ertm_info = *p_ertm_info; + + /* Replace default indicators with the actual default pool */ + if (p_ccb->ertm_info.fcr_rx_buf_size == L2CAP_INVALID_ERM_BUF_SIZE) { + p_ccb->ertm_info.fcr_rx_buf_size = L2CAP_FCR_RX_BUF_SIZE; + } + + if (p_ccb->ertm_info.fcr_tx_buf_size == L2CAP_INVALID_ERM_BUF_SIZE) { + p_ccb->ertm_info.fcr_tx_buf_size = L2CAP_FCR_TX_BUF_SIZE; + } + + if (p_ccb->ertm_info.user_rx_buf_size == L2CAP_INVALID_ERM_BUF_SIZE) { + p_ccb->ertm_info.user_rx_buf_size = L2CAP_USER_RX_BUF_SIZE; + } + + if (p_ccb->ertm_info.user_tx_buf_size == L2CAP_INVALID_ERM_BUF_SIZE) { + p_ccb->ertm_info.user_tx_buf_size = L2CAP_USER_TX_BUF_SIZE; + } + + p_ccb->max_rx_mtu = p_ertm_info->user_rx_buf_size - + (L2CAP_MIN_OFFSET + L2CAP_SDU_LEN_OFFSET + L2CAP_FCS_LEN); + } + + + + /* If link is up, start the L2CAP connection */ + if (p_lcb->link_state == LST_CONNECTED) { + l2c_csm_execute (p_ccb, L2CEVT_L2CA_CONNECT_REQ, NULL); + } + + /* If link is disconnecting, save link info to retry after disconnect + * Possible Race condition when a reconnect occurs + * on the channel during a disconnect of link. This + * ccb will be automatically retried after link disconnect + * arrives + */ + else if (p_lcb->link_state == LST_DISCONNECTING) { + L2CAP_TRACE_DEBUG ("L2CAP API - link disconnecting: RETRY LATER"); + + /* Save ccb so it can be started after disconnect is finished */ + p_lcb->p_pending_ccb = p_ccb; + } + + L2CAP_TRACE_API ("L2CAP - L2CA_conn_req(psm: 0x%04x) returned CID: 0x%04x", psm, p_ccb->local_cid); + + /* Return the local CID as our handle */ + return (p_ccb->local_cid); +} + +bool L2CA_SetConnectionCallbacks(uint16_t local_cid, const tL2CAP_APPL_INFO *callbacks) +{ + assert(callbacks != NULL); + assert(callbacks->pL2CA_ConnectInd_Cb == NULL); + assert(callbacks->pL2CA_ConnectCfm_Cb != NULL); + assert(callbacks->pL2CA_ConfigInd_Cb != NULL); + assert(callbacks->pL2CA_ConfigCfm_Cb != NULL); + assert(callbacks->pL2CA_DisconnectInd_Cb != NULL); + assert(callbacks->pL2CA_DisconnectCfm_Cb != NULL); + assert(callbacks->pL2CA_CongestionStatus_Cb != NULL); + assert(callbacks->pL2CA_DataInd_Cb != NULL); + assert(callbacks->pL2CA_TxComplete_Cb != NULL); + + tL2C_CCB *channel_control_block = l2cu_find_ccb_by_cid(NULL, local_cid); + if (!channel_control_block) { + L2CAP_TRACE_ERROR("%s no channel control block found for L2CAP LCID=0x%04x.", __func__, local_cid); + return false; + } + + // We're making a connection-specific registration control block so we check if + // we already have a private one allocated to us on the heap. If not, we make a + // new allocation, mark it as heap-allocated, and inherit the fields from the old + // control block. + tL2C_RCB *registration_control_block = channel_control_block->p_rcb; + if (!channel_control_block->should_free_rcb) { + registration_control_block = (tL2C_RCB *)osi_calloc(sizeof(tL2C_RCB)); + if (!registration_control_block) { + L2CAP_TRACE_ERROR("%s unable to allocate registration control block.", __func__); + return false; + } + + *registration_control_block = *channel_control_block->p_rcb; + channel_control_block->p_rcb = registration_control_block; + channel_control_block->should_free_rcb = true; + } + + registration_control_block->api = *callbacks; + return true; +} + +/******************************************************************************* +** +** Function L2CA_ConnectRsp +** +** Description Higher layers call this function to accept an incoming +** L2CAP connection, for which they had gotten an connect +** indication callback. +** +** Returns TRUE for success, FALSE for failure +** +*******************************************************************************/ +BOOLEAN L2CA_ConnectRsp (BD_ADDR p_bd_addr, UINT8 id, UINT16 lcid, + UINT16 result, UINT16 status) +{ + return L2CA_ErtmConnectRsp (p_bd_addr, id, lcid, result, status, NULL); +} + + +/******************************************************************************* +** +** Function L2CA_ErtmConnectRsp +** +** Description Higher layers call this function to accept an incoming +** L2CAP connection, for which they had gotten an connect +** indication callback. +** +** Returns TRUE for success, FALSE for failure +** +*******************************************************************************/ +BOOLEAN L2CA_ErtmConnectRsp (BD_ADDR p_bd_addr, UINT8 id, UINT16 lcid, UINT16 result, + UINT16 status, tL2CAP_ERTM_INFO *p_ertm_info) +{ + tL2C_LCB *p_lcb; + tL2C_CCB *p_ccb; + + //counter_add("l2cap.conn.rsp", 1); + L2CAP_TRACE_API ("L2CA_ErtmConnectRsp() CID: 0x%04x Result: %d Status: %d BDA: %08x%04x p_ertm_info:%p", + lcid, result, status, + (p_bd_addr[0] << 24) + (p_bd_addr[1] << 16) + (p_bd_addr[2] << 8) + p_bd_addr[3], + (p_bd_addr[4] << 8) + p_bd_addr[5], p_ertm_info); + + /* First, find the link control block */ + if ((p_lcb = l2cu_find_lcb_by_bd_addr (p_bd_addr, BT_TRANSPORT_BR_EDR)) == NULL) { + /* No link. Get an LCB and start link establishment */ + L2CAP_TRACE_WARNING ("L2CAP - no LCB for L2CA_conn_rsp"); + return (FALSE); + } + /* Now, find the channel control block */ + if ((p_ccb = l2cu_find_ccb_by_cid (p_lcb, lcid)) == NULL) { + L2CAP_TRACE_WARNING ("L2CAP - no CCB for L2CA_conn_rsp"); + return (FALSE); + } + + /* The IDs must match */ + if (p_ccb->remote_id != id) { + L2CAP_TRACE_WARNING ("L2CAP - bad id in L2CA_conn_rsp. Exp: %d Got: %d", p_ccb->remote_id, id); + return (FALSE); + } + + if (p_ertm_info) { + p_ccb->ertm_info = *p_ertm_info; + + /* Replace default indicators with the actual default pool */ + if (p_ccb->ertm_info.fcr_rx_buf_size == L2CAP_INVALID_ERM_BUF_SIZE) { + p_ccb->ertm_info.fcr_rx_buf_size = L2CAP_FCR_RX_BUF_SIZE; + } + + if (p_ccb->ertm_info.fcr_tx_buf_size == L2CAP_INVALID_ERM_BUF_SIZE) { + p_ccb->ertm_info.fcr_tx_buf_size = L2CAP_FCR_TX_BUF_SIZE; + } + + if (p_ccb->ertm_info.user_rx_buf_size == L2CAP_INVALID_ERM_BUF_SIZE) { + p_ccb->ertm_info.user_rx_buf_size = L2CAP_USER_RX_BUF_SIZE; + } + + if (p_ccb->ertm_info.user_tx_buf_size == L2CAP_INVALID_ERM_BUF_SIZE) { + p_ccb->ertm_info.user_tx_buf_size = L2CAP_USER_TX_BUF_SIZE; + } + + p_ccb->max_rx_mtu = p_ertm_info->user_rx_buf_size - + (L2CAP_MIN_OFFSET + L2CAP_SDU_LEN_OFFSET + L2CAP_FCS_LEN); + } + + if (result == L2CAP_CONN_OK) { + l2c_csm_execute (p_ccb, L2CEVT_L2CA_CONNECT_RSP, NULL); + } else { + tL2C_CONN_INFO conn_info; + + conn_info.l2cap_result = result; + conn_info.l2cap_status = status; + + if (result == L2CAP_CONN_PENDING) { + l2c_csm_execute (p_ccb, L2CEVT_L2CA_CONNECT_RSP, &conn_info); + } else { + l2c_csm_execute (p_ccb, L2CEVT_L2CA_CONNECT_RSP_NEG, &conn_info); + } + } + + return (TRUE); +} + + +/******************************************************************************* +** +** Function L2CA_ConfigReq +** +** Description Higher layers call this function to send configuration. +** +** Note: The FCR options of p_cfg are not used. +** +** Returns TRUE if configuration sent, else FALSE +** +*******************************************************************************/ +BOOLEAN L2CA_ConfigReq (UINT16 cid, tL2CAP_CFG_INFO *p_cfg) +{ + tL2C_CCB *p_ccb; + + //counter_add("l2cap.cfg.req", 1); + L2CAP_TRACE_API ("L2CA_ConfigReq() CID 0x%04x: fcr_present:%d (mode %d) mtu_present:%d (%d)", + cid, p_cfg->fcr_present, p_cfg->fcr.mode, p_cfg->mtu_present, p_cfg->mtu); + + /* Find the channel control block. We don't know the link it is on. */ + if ((p_ccb = l2cu_find_ccb_by_cid (NULL, cid)) == NULL) { + L2CAP_TRACE_WARNING ("L2CAP - no CCB for L2CA_cfg_req, CID: %d", cid); + return (FALSE); + } + + /* We need to have at least one mode type common with the peer */ + if (!l2c_fcr_adj_our_req_options(p_ccb, p_cfg)) { + return (FALSE); + } + + /* Don't adjust FCR options if not used */ + if ((!p_cfg->fcr_present) || (p_cfg->fcr.mode == L2CAP_FCR_BASIC_MODE)) { + /* FCR and FCS options are not used in basic mode */ + p_cfg->fcs_present = FALSE; + p_cfg->ext_flow_spec_present = FALSE; + + if ( (p_cfg->mtu_present) && (p_cfg->mtu > L2CAP_MTU_SIZE) ) { + L2CAP_TRACE_WARNING ("L2CAP - adjust MTU: %u too large", p_cfg->mtu); + p_cfg->mtu = L2CAP_MTU_SIZE; + } + } + + /* Save the adjusted configuration in case it needs to be used for renegotiation */ + p_ccb->our_cfg = *p_cfg; + + l2c_csm_execute (p_ccb, L2CEVT_L2CA_CONFIG_REQ, p_cfg); + + return (TRUE); +} + + +/******************************************************************************* +** +** Function L2CA_ConfigRsp +** +** Description Higher layers call this function to send a configuration +** response. +** +** Returns TRUE if configuration response sent, else FALSE +** +*******************************************************************************/ +BOOLEAN L2CA_ConfigRsp (UINT16 cid, tL2CAP_CFG_INFO *p_cfg) +{ + tL2C_CCB *p_ccb; + + //counter_add("l2cap.cfg.rsp", 1); + L2CAP_TRACE_API ("L2CA_ConfigRsp() CID: 0x%04x Result: %d MTU present:%d Flush TO:%d FCR:%d FCS:%d", + cid, p_cfg->result, p_cfg->mtu_present, p_cfg->flush_to_present, p_cfg->fcr_present, p_cfg->fcs_present); + + /* Find the channel control block. We don't know the link it is on. */ + if ((p_ccb = l2cu_find_ccb_by_cid (NULL, cid)) == NULL) { + L2CAP_TRACE_WARNING ("L2CAP - no CCB for L2CA_cfg_rsp, CID: %d", cid); + return (FALSE); + } + + if ( (p_cfg->result == L2CAP_CFG_OK) || (p_cfg->result == L2CAP_CFG_PENDING) ) { + l2c_csm_execute (p_ccb, L2CEVT_L2CA_CONFIG_RSP, p_cfg); + } else { + p_cfg->fcr_present = FALSE; /* FCR options already negotiated before this point */ + + /* Clear out any cached options that are being returned as an error (excluding FCR) */ + if (p_cfg->mtu_present) { + p_ccb->peer_cfg.mtu_present = FALSE; + } + if (p_cfg->flush_to_present) { + p_ccb->peer_cfg.flush_to_present = FALSE; + } + if (p_cfg->qos_present) { + p_ccb->peer_cfg.qos_present = FALSE; + } + + l2c_csm_execute (p_ccb, L2CEVT_L2CA_CONFIG_RSP_NEG, p_cfg); + } + + return (TRUE); +} + + +/******************************************************************************* +** +** Function L2CA_DisconnectReq +** +** Description Higher layers call this function to disconnect a channel. +** +** Returns TRUE if disconnect sent, else FALSE +** +*******************************************************************************/ +BOOLEAN L2CA_DisconnectReq (UINT16 cid) +{ + tL2C_CCB *p_ccb; + + //counter_add("l2cap.disconn.req", 1); + L2CAP_TRACE_API ("L2CA_DisconnectReq() CID: 0x%04x", cid); + + /* Find the channel control block. We don't know the link it is on. */ + if ((p_ccb = l2cu_find_ccb_by_cid (NULL, cid)) == NULL) { + L2CAP_TRACE_WARNING ("L2CAP - no CCB for L2CA_disc_req, CID: %d", cid); + return (FALSE); + } + + l2c_csm_execute (p_ccb, L2CEVT_L2CA_DISCONNECT_REQ, NULL); + + return (TRUE); +} + +/******************************************************************************* +** +** Function L2CA_DisconnectRsp +** +** Description Higher layers call this function to acknowledge the +** disconnection of a channel. +** +** Returns void +** +*******************************************************************************/ +BOOLEAN L2CA_DisconnectRsp (UINT16 cid) +{ + tL2C_CCB *p_ccb; + + //counter_add("l2cap.disconn.rsp", 1); + L2CAP_TRACE_API ("L2CA_DisconnectRsp() CID: 0x%04x", cid); + + /* Find the channel control block. We don't know the link it is on. */ + if ((p_ccb = l2cu_find_ccb_by_cid (NULL, cid)) == NULL) { + L2CAP_TRACE_WARNING ("L2CAP - no CCB for L2CA_disc_rsp, CID: %d", cid); + return (FALSE); + } + + l2c_csm_execute (p_ccb, L2CEVT_L2CA_DISCONNECT_RSP, NULL); + + return (TRUE); +} + +/******************************************************************************* +** +** Function L2CA_Ping +** +** Description Higher layers call this function to send an echo request. +** +** Returns TRUE if echo request sent, else FALSE. +** +*******************************************************************************/ +BOOLEAN L2CA_Ping (BD_ADDR p_bd_addr, tL2CA_ECHO_RSP_CB *p_callback) +{ + tL2C_LCB *p_lcb; + + L2CAP_TRACE_API ("L2CA_Ping() BDA: %02x-%02x-%02x-%02x-%02x-%02x", + p_bd_addr[0], p_bd_addr[1], p_bd_addr[2], p_bd_addr[3], p_bd_addr[4], p_bd_addr[5]); + + /* Fail if we have not established communications with the controller */ + if (!BTM_IsDeviceUp()) { + return (FALSE); + } + + /* First, see if we already have a link to the remote */ + if ((p_lcb = l2cu_find_lcb_by_bd_addr (p_bd_addr, BT_TRANSPORT_BR_EDR)) == NULL) { + /* No link. Get an LCB and start link establishment */ + if ((p_lcb = l2cu_allocate_lcb (p_bd_addr, FALSE, BT_TRANSPORT_BR_EDR)) == NULL) { + L2CAP_TRACE_WARNING ("L2CAP - no LCB for L2CA_ping"); + return (FALSE); + } + if (l2cu_create_conn(p_lcb, BT_TRANSPORT_BR_EDR) == FALSE) { + return (FALSE); + } + + p_lcb->p_echo_rsp_cb = p_callback; + + return (TRUE); + } + + /* We only allow 1 ping outstanding at a time */ + if (p_lcb->p_echo_rsp_cb != NULL) { + L2CAP_TRACE_WARNING ("L2CAP - rejected second L2CA_ping"); + return (FALSE); + } + + /* Have a link control block. If link is disconnecting, tell user to retry later */ + if (p_lcb->link_state == LST_DISCONNECTING) { + L2CAP_TRACE_WARNING ("L2CAP - L2CA_ping rejected - link disconnecting"); + return (FALSE); + } + + /* Save address of callback */ + p_lcb->p_echo_rsp_cb = p_callback; + + if (p_lcb->link_state == LST_CONNECTED) { + l2cu_adj_id(p_lcb, L2CAP_ADJ_BRCM_ID); /* Make sure not using Broadcom ID */ + l2cu_send_peer_echo_req (p_lcb, NULL, 0); + btu_start_timer (&p_lcb->timer_entry, BTU_TTYPE_L2CAP_LINK, L2CAP_ECHO_RSP_TOUT); + } + + return (TRUE); +} + +/******************************************************************************* +** +** Function L2CA_Echo +** +** Description Higher layers call this function to send an echo request +** with application-specific data. +** +** Returns TRUE if echo request sent, else FALSE. +** +*******************************************************************************/ +BOOLEAN L2CA_Echo (BD_ADDR p_bd_addr, BT_HDR *p_data, tL2CA_ECHO_DATA_CB *p_callback) +{ + tL2C_LCB *p_lcb; + UINT8 *pp; + + L2CAP_TRACE_API ("L2CA_Echo() BDA: %08X%04X", + ((p_bd_addr[0] << 24) + (p_bd_addr[1] << 16) + (p_bd_addr[2] << 8) + (p_bd_addr[3])), + ((p_bd_addr[4] << 8) + (p_bd_addr[5]))); + + /* Fail if we have not established communications with the controller */ + if (!BTM_IsDeviceUp()) { + return (FALSE); + } + + if ((memcmp(BT_BD_ANY, p_bd_addr, BD_ADDR_LEN) == 0) && (p_data == NULL)) { + /* Only register callback without sending message. */ + l2cb.p_echo_data_cb = p_callback; + return TRUE; + } + + /* We assume the upper layer will call this function only when the link is established. */ + if ((p_lcb = l2cu_find_lcb_by_bd_addr (p_bd_addr, BT_TRANSPORT_BR_EDR)) == NULL) { + L2CAP_TRACE_ERROR ("L2CA_Echo ERROR : link not established"); + return FALSE; + } + + if (p_lcb->link_state != LST_CONNECTED) { + L2CAP_TRACE_ERROR ("L2CA_Echo ERROR : link is not connected"); + return FALSE; + } + + /* Save address of callback */ + l2cb.p_echo_data_cb = p_callback; + + /* Set the pointer to the beginning of the data */ + pp = (UINT8 *)(p_data + 1) + p_data->offset; + l2cu_adj_id(p_lcb, L2CAP_ADJ_BRCM_ID); /* Make sure not using Broadcom ID */ + l2cu_send_peer_echo_req (p_lcb, pp, p_data->len); + + return (TRUE); + +} + +#endif ///CLASSIC_BT_INCLUDED == TRUE + + +bool L2CA_GetIdentifiers(uint16_t lcid, uint16_t *rcid, uint16_t *handle) +{ + tL2C_CCB *control_block = l2cu_find_ccb_by_cid(NULL, lcid); + if (!control_block) { + return false; + } + + if (rcid) { + *rcid = control_block->remote_cid; + } + if (handle) { + *handle = control_block->p_lcb->handle; + } + + return true; +} + +/******************************************************************************* +** +** Function L2CA_SetIdleTimeout +** +** Description Higher layers call this function to set the idle timeout for +** a connection, or for all future connections. The "idle timeout" +** is the amount of time that a connection can remain up with +** no L2CAP channels on it. A timeout of zero means that the +** connection will be torn down immediately when the last channel +** is removed. A timeout of 0xFFFF means no timeout. Values are +** in seconds. +** +** Returns TRUE if command succeeded, FALSE if failed +** +** NOTE This timeout takes effect after at least 1 channel has been +** established and removed. L2CAP maintains its own timer from +** whan a connection is established till the first channel is +** set up. +*******************************************************************************/ +BOOLEAN L2CA_SetIdleTimeout (UINT16 cid, UINT16 timeout, BOOLEAN is_global) +{ + tL2C_CCB *p_ccb; + tL2C_LCB *p_lcb; + + if (is_global) { + l2cb.idle_timeout = timeout; + } else { + /* Find the channel control block. We don't know the link it is on. */ + if ((p_ccb = l2cu_find_ccb_by_cid (NULL, cid)) == NULL) { + L2CAP_TRACE_WARNING ("L2CAP - no CCB for L2CA_SetIdleTimeout, CID: %d", cid); + return (FALSE); + } + + p_lcb = p_ccb->p_lcb; + + if ((p_lcb) && (p_lcb->in_use) && (p_lcb->link_state == LST_CONNECTED)) { + p_lcb->idle_timeout = timeout; + } else { + return (FALSE); + } + } + + return (TRUE); +} + + + +/******************************************************************************* +** +** Function L2CA_SetIdleTimeoutByBdAddr +** +** Description Higher layers call this function to set the idle timeout for +** a connection. The "idle timeout" is the amount of time that +** a connection can remain up with no L2CAP channels on it. +** A timeout of zero means that the connection will be torn +** down immediately when the last channel is removed. +** A timeout of 0xFFFF means no timeout. Values are in seconds. +** A bd_addr is the remote BD address. If bd_addr = BT_BD_ANY, +** then the idle timeouts for all active l2cap links will be +** changed. +** +** Returns TRUE if command succeeded, FALSE if failed +** +** NOTE This timeout applies to all logical channels active on the +** ACL link. +*******************************************************************************/ +BOOLEAN L2CA_SetIdleTimeoutByBdAddr(BD_ADDR bd_addr, UINT16 timeout, tBT_TRANSPORT transport) +{ + tL2C_LCB *p_lcb; + list_node_t *p_node = NULL; + + if (memcmp (BT_BD_ANY, bd_addr, BD_ADDR_LEN)) { + p_lcb = l2cu_find_lcb_by_bd_addr( bd_addr, transport); + if ((p_lcb) && (p_lcb->in_use) && (p_lcb->link_state == LST_CONNECTED)) { + p_lcb->idle_timeout = timeout; + + if (!p_lcb->ccb_queue.p_first_ccb) { + l2cu_no_dynamic_ccbs (p_lcb); + } + } else { + return FALSE; + } + } else { + for (p_node = list_begin(l2cb.p_lcb_pool); p_node; p_node = list_next(p_node)) { + p_lcb = list_node(p_node); + if ((p_lcb->in_use) && (p_lcb->link_state == LST_CONNECTED)) { + p_lcb->idle_timeout = timeout; + + if (!p_lcb->ccb_queue.p_first_ccb) { + l2cu_no_dynamic_ccbs (p_lcb); + } + } + } + } + + return TRUE; +} + + + +/******************************************************************************* +** +** Function L2CA_SetTraceLevel +** +** Description This function sets the trace level for L2CAP. If called with +** a value of 0xFF, it simply reads the current trace level. +** +** Returns the new (current) trace level +** +*******************************************************************************/ +UINT8 L2CA_SetTraceLevel (UINT8 new_level) +{ + if (new_level != 0xFF) { + l2cb.l2cap_trace_level = new_level; + } + + return (l2cb.l2cap_trace_level); +} + + +/******************************************************************************* +** +** Function L2CA_SetDesireRole +** +** Description This function sets the desire role for L2CAP. +** If the new role is L2CAP_ROLE_ALLOW_SWITCH, allow switch on +** HciCreateConnection. +** If the new role is L2CAP_ROLE_DISALLOW_SWITCH, do not allow switch on +** HciCreateConnection. +** +** If the new role is a valid role (HCI_ROLE_MASTER or HCI_ROLE_SLAVE), +** the desire role is set to the new value. Otherwise, it is not changed. +** +** Returns the new (current) role +** +*******************************************************************************/ +UINT8 L2CA_SetDesireRole (UINT8 new_role) +{ + L2CAP_TRACE_API ("L2CA_SetDesireRole() new:x%x, disallow_switch:%d", + new_role, l2cb.disallow_switch); + + if (L2CAP_ROLE_CHECK_SWITCH != (L2CAP_ROLE_CHECK_SWITCH & new_role)) { + /* do not process the allow_switch when both bits are set */ + if (new_role & L2CAP_ROLE_ALLOW_SWITCH) { + l2cb.disallow_switch = FALSE; + } + if (new_role & L2CAP_ROLE_DISALLOW_SWITCH) { + l2cb.disallow_switch = TRUE; + } + } + + if (new_role == HCI_ROLE_MASTER || new_role == HCI_ROLE_SLAVE) { + l2cb.desire_role = new_role; + } + + return (l2cb.desire_role); +} + +#if (CLASSIC_BT_INCLUDED == TRUE) + +/******************************************************************************* +** +** Function L2CA_LocalLoopbackReq +** +** Description This function sets up a CID for local loopback +** +** Returns CID of 0 if none. +** +*******************************************************************************/ +UINT16 L2CA_LocalLoopbackReq (UINT16 psm, UINT16 handle, BD_ADDR p_bd_addr) +{ + tL2C_LCB *p_lcb; + tL2C_CCB *p_ccb; + tL2C_RCB *p_rcb; + + L2CAP_TRACE_API ("L2CA_LocalLoopbackReq() PSM: %d Handle: 0x%04x", psm, handle); + + /* Fail if we have not established communications with the controller */ + if (!BTM_IsDeviceUp()) { + L2CAP_TRACE_WARNING ("L2CAP loop req - BTU not ready"); + return (0); + } + + /* Fail if the PSM is not registered */ + if ((p_rcb = l2cu_find_rcb_by_psm (psm)) == NULL) { + L2CAP_TRACE_WARNING ("L2CAP - no RCB for L2CA_conn_req, PSM: %d", psm); + return (0); + } + + if ((p_lcb = l2cu_allocate_lcb (p_bd_addr, FALSE, BT_TRANSPORT_BR_EDR)) == NULL) { + L2CAP_TRACE_WARNING ("L2CAP - no LCB for L2CA_conn_req"); + return (0); + } + + p_lcb->link_state = LST_CONNECTED; + p_lcb->handle = handle; + + /* Allocate a channel control block */ + if ((p_ccb = l2cu_allocate_ccb (p_lcb, 0)) == NULL) { + L2CAP_TRACE_WARNING ("L2CAP - no CCB for L2CA_conn_req"); + return (0); + } + + /* Save registration info */ + p_ccb->p_rcb = p_rcb; + p_ccb->chnl_state = CST_OPEN; + p_ccb->remote_cid = p_ccb->local_cid; + p_ccb->config_done = CFG_DONE_MASK; + + /* Return the local CID as our handle */ + return (p_ccb->local_cid); +} + +/******************************************************************************* +** +** Function L2CA_SetAclPriority +** +** Description Sets the transmission priority for a channel. +** (For initial implementation only two values are valid. +** L2CAP_PRIORITY_NORMAL and L2CAP_PRIORITY_HIGH). +** +** Returns TRUE if a valid channel, else FALSE +** +*******************************************************************************/ +BOOLEAN L2CA_SetAclPriority (BD_ADDR bd_addr, UINT8 priority) +{ + L2CAP_TRACE_API ("L2CA_SetAclPriority() bdaddr: %02x%02x%02x%02x%04x, priority:%d", + bd_addr[0], bd_addr[1], bd_addr[2], + bd_addr[3], (bd_addr[4] << 8) + bd_addr[5], priority); + + return (l2cu_set_acl_priority(bd_addr, priority, FALSE)); +} + +/******************************************************************************* +** +** Function L2CA_FlowControl +** +** Description Higher layers call this function to flow control a channel. +** +** data_enabled - TRUE data flows, FALSE data is stopped +** +** Returns TRUE if valid channel, else FALSE +** +*******************************************************************************/ +BOOLEAN L2CA_FlowControl (UINT16 cid, BOOLEAN data_enabled) +{ + tL2C_CCB *p_ccb; + BOOLEAN on_off = !data_enabled; + + L2CAP_TRACE_API ("L2CA_FlowControl(%d) CID: 0x%04x", on_off, cid); + + /* Find the channel control block. We don't know the link it is on. */ + if ((p_ccb = l2cu_find_ccb_by_cid (NULL, cid)) == NULL) { + L2CAP_TRACE_WARNING ("L2CAP - no CCB for L2CA_FlowControl, CID: 0x%04x data_enabled: %d", cid, data_enabled); + return (FALSE); + } + + if (p_ccb->peer_cfg.fcr.mode != L2CAP_FCR_ERTM_MODE) { + L2CAP_TRACE_EVENT ("L2CA_FlowControl() invalid mode:%d", p_ccb->peer_cfg.fcr.mode); + return (FALSE); + } + if (p_ccb->fcrb.local_busy != on_off) { + p_ccb->fcrb.local_busy = on_off; + + if ( (p_ccb->chnl_state == CST_OPEN) && (!p_ccb->fcrb.wait_ack) ) { + if (on_off) { + l2c_fcr_send_S_frame (p_ccb, L2CAP_FCR_SUP_RNR, 0); + } else { + l2c_fcr_send_S_frame (p_ccb, L2CAP_FCR_SUP_RR, L2CAP_FCR_P_BIT); + } + } + } + + return (TRUE); +} + +/******************************************************************************* +** +** Function L2CA_SendTestSFrame +** +** Description Higher layers call this function to send a test S-frame. +** +** Returns TRUE if valid Channel, else FALSE +** +*******************************************************************************/ +BOOLEAN L2CA_SendTestSFrame (UINT16 cid, UINT8 sup_type, UINT8 back_track) +{ + tL2C_CCB *p_ccb; + + L2CAP_TRACE_API ("L2CA_SendTestSFrame() CID: 0x%04x Type: 0x%02x back_track: %u", cid, sup_type, back_track); + + /* Find the channel control block. We don't know the link it is on. */ + if ((p_ccb = l2cu_find_ccb_by_cid (NULL, cid)) == NULL) { + L2CAP_TRACE_WARNING ("L2CAP - no CCB for L2CA_SendTestSFrame, CID: %d", cid); + return (FALSE); + } + + if ( (p_ccb->chnl_state != CST_OPEN) || (p_ccb->peer_cfg.fcr.mode != L2CAP_FCR_ERTM_MODE) ) { + return (FALSE); + } + + p_ccb->fcrb.next_seq_expected -= back_track; + + l2c_fcr_send_S_frame (p_ccb, (UINT16)(sup_type & 3), (UINT16)(sup_type & (L2CAP_FCR_P_BIT | L2CAP_FCR_F_BIT))); + + return (TRUE); +} + + +/******************************************************************************* +** +** Function L2CA_SetTxPriority +** +** Description Sets the transmission priority for a channel. +** +** Returns TRUE if a valid channel, else FALSE +** +*******************************************************************************/ +BOOLEAN L2CA_SetTxPriority (UINT16 cid, tL2CAP_CHNL_PRIORITY priority) +{ + tL2C_CCB *p_ccb; + + L2CAP_TRACE_API ("L2CA_SetTxPriority() CID: 0x%04x, priority:%d", cid, priority); + + /* Find the channel control block. We don't know the link it is on. */ + if ((p_ccb = l2cu_find_ccb_by_cid (NULL, cid)) == NULL) { + L2CAP_TRACE_WARNING ("L2CAP - no CCB for L2CA_SetTxPriority, CID: %d", cid); + return (FALSE); + } + + /* it will update the order of CCB in LCB by priority and update round robin service variables */ + l2cu_change_pri_ccb (p_ccb, priority); + + return (TRUE); +} + +/******************************************************************************* +** +** Function L2CA_SetChnlDataRate +** +** Description Sets the tx/rx data rate for a channel. +** +** Returns TRUE if a valid channel, else FALSE +** +*******************************************************************************/ +BOOLEAN L2CA_SetChnlDataRate (UINT16 cid, tL2CAP_CHNL_DATA_RATE tx, tL2CAP_CHNL_DATA_RATE rx) +{ + tL2C_CCB *p_ccb; + + L2CAP_TRACE_API ("L2CA_SetChnlDataRate() CID: 0x%04x, tx:%d, rx:%d", cid, tx, rx); + + /* Find the channel control block. We don't know the link it is on. */ + if ((p_ccb = l2cu_find_ccb_by_cid (NULL, cid)) == NULL) { + L2CAP_TRACE_WARNING ("L2CAP - no CCB for L2CA_SetChnlDataRate, CID: %d", cid); + return (FALSE); + } + + p_ccb->tx_data_rate = tx; + p_ccb->rx_data_rate = rx; + + /* Adjust channel buffer allocation */ + l2c_link_adjust_chnl_allocation (); + + return (TRUE); +} + +/******************************************************************************* +** +** Function L2CA_SetFlushTimeout +** +** Description This function set the automatic flush time out in Baseband +** for ACL-U packets. +** BdAddr : the remote BD address of ACL link. If it is BT_DB_ANY +** then the flush time out will be applied to all ACL link. +** FlushTimeout: flush time out in ms +** 0x0000 : No automatic flush +** L2CAP_NO_RETRANSMISSION : No retransmission +** 0x0002 - 0xFFFE : flush time out, if (flush_tout*8)+3/5) +** <= HCI_MAX_AUTO_FLUSH_TOUT (in 625us slot). +** Otherwise, return FALSE. +** L2CAP_NO_AUTOMATIC_FLUSH : No automatic flush +** +** Returns TRUE if command succeeded, FALSE if failed +** +** NOTE This flush timeout applies to all logical channels active on the +** ACL link. +*******************************************************************************/ +BOOLEAN L2CA_SetFlushTimeout (BD_ADDR bd_addr, UINT16 flush_tout) +{ + tL2C_LCB *p_lcb; + UINT16 hci_flush_to; + UINT32 temp; + + /* no automatic flush (infinite timeout) */ + if (flush_tout == 0x0000) { + hci_flush_to = flush_tout; + flush_tout = L2CAP_NO_AUTOMATIC_FLUSH; + } + /* no retransmission */ + else if (flush_tout == L2CAP_NO_RETRANSMISSION) { + /* not mandatory range for controller */ + /* Packet is flushed before getting any ACK/NACK */ + /* To do this, flush timeout should be 1 baseband slot */ + hci_flush_to = flush_tout; + } + /* no automatic flush (infinite timeout) */ + else if (flush_tout == L2CAP_NO_AUTOMATIC_FLUSH) { + hci_flush_to = 0x0000; + } else { + /* convert L2CAP flush_to to 0.625 ms units, with round */ + temp = (((UINT32)flush_tout * 8) + 3) / 5; + + /* if L2CAP flush_to within range of HCI, set HCI flush timeout */ + if (temp > HCI_MAX_AUTO_FLUSH_TOUT) { + L2CAP_TRACE_WARNING("WARNING L2CA_SetFlushTimeout timeout(0x%x) is out of range", flush_tout); + return FALSE; + } else { + hci_flush_to = (UINT16)temp; + } + } + + if (memcmp (BT_BD_ANY, bd_addr, BD_ADDR_LEN)) { + p_lcb = l2cu_find_lcb_by_bd_addr (bd_addr, BT_TRANSPORT_BR_EDR); + + if ((p_lcb) && (p_lcb->in_use) && (p_lcb->link_state == LST_CONNECTED)) { + if (p_lcb->link_flush_tout != flush_tout) { + p_lcb->link_flush_tout = flush_tout; + + L2CAP_TRACE_API ("L2CA_SetFlushTimeout 0x%04x ms for bd_addr [...;%02x%02x%02x]", + flush_tout, bd_addr[3], bd_addr[4], bd_addr[5]); + + if (!btsnd_hcic_write_auto_flush_tout (p_lcb->handle, hci_flush_to)) { + return (FALSE); + } + } + } else { + L2CAP_TRACE_WARNING ("WARNING L2CA_SetFlushTimeout No lcb for bd_addr [...;%02x%02x%02x]", + bd_addr[3], bd_addr[4], bd_addr[5]); + return (FALSE); + } + } else { + list_node_t *p_node = NULL; + for (p_node = list_begin(l2cb.p_lcb_pool); p_node; p_node = list_next(p_node)) { + p_lcb = list_node(p_node); + if ((p_lcb->in_use) && (p_lcb->link_state == LST_CONNECTED)) { + if (p_lcb->link_flush_tout != flush_tout) { + p_lcb->link_flush_tout = flush_tout; + + L2CAP_TRACE_API ("L2CA_SetFlushTimeout 0x%04x ms for bd_addr [...;%02x%02x%02x]", + flush_tout, p_lcb->remote_bd_addr[3], + p_lcb->remote_bd_addr[4], p_lcb->remote_bd_addr[5]); + + if (!btsnd_hcic_write_auto_flush_tout(p_lcb->handle, hci_flush_to)) { + return (FALSE); + } + } + } + } + } + + return (TRUE); +} +#endif ///CLASSIC_BT_INCLUDED == TRUE + + +/******************************************************************************* +** +** Function L2CA_GetPeerFeatures +** +** Description Get a peers features and fixed channel map +** +** Parameters: BD address of the peer +** Pointers to features and channel mask storage area +** +** Return value: TRUE if peer is connected +** +*******************************************************************************/ +BOOLEAN L2CA_GetPeerFeatures (BD_ADDR bd_addr, UINT32 *p_ext_feat, UINT8 *p_chnl_mask) +{ + tL2C_LCB *p_lcb; + + /* We must already have a link to the remote */ + if ((p_lcb = l2cu_find_lcb_by_bd_addr (bd_addr, BT_TRANSPORT_BR_EDR)) == NULL) { + L2CAP_TRACE_WARNING ("L2CA_GetPeerFeatures() No BDA: %08x%04x", + (bd_addr[0] << 24) + (bd_addr[1] << 16) + (bd_addr[2] << 8) + bd_addr[3], + (bd_addr[4] << 8) + bd_addr[5]); + return (FALSE); + } + + L2CAP_TRACE_API ("L2CA_GetPeerFeatures() BDA: %08x%04x ExtFea: 0x%08x Chnl_Mask[0]: 0x%02x", + (bd_addr[0] << 24) + (bd_addr[1] << 16) + (bd_addr[2] << 8) + bd_addr[3], + (bd_addr[4] << 8) + bd_addr[5], p_lcb->peer_ext_fea, p_lcb->peer_chnl_mask[0]); + + *p_ext_feat = p_lcb->peer_ext_fea; + + memcpy (p_chnl_mask, p_lcb->peer_chnl_mask, L2CAP_FIXED_CHNL_ARRAY_SIZE); + + return (TRUE); +} + +/******************************************************************************* +** +** Function L2CA_GetBDAddrbyHandle +** +** Description Get BD address for the given HCI handle +** +** Parameters: HCI handle +** BD address of the peer +** +** Return value: TRUE if found lcb for the given handle, FALSE otherwise +** +*******************************************************************************/ +BOOLEAN L2CA_GetBDAddrbyHandle (UINT16 handle, BD_ADDR bd_addr) +{ + tL2C_LCB *p_lcb = NULL; + BOOLEAN found_dev = FALSE; + + p_lcb = l2cu_find_lcb_by_handle (handle); + if (p_lcb) { + found_dev = TRUE; + memcpy (bd_addr, p_lcb->remote_bd_addr, BD_ADDR_LEN); + } + + return found_dev; +} + +#if (CLASSIC_BT_INCLUDED == TRUE) +/******************************************************************************* +** +** Function L2CA_GetChnlFcrMode +** +** Description Get the channel FCR mode +** +** Parameters: Local CID +** +** Return value: Channel mode +** +*******************************************************************************/ +UINT8 L2CA_GetChnlFcrMode (UINT16 lcid) +{ + tL2C_CCB *p_ccb = l2cu_find_ccb_by_cid (NULL, lcid); + + if (p_ccb) { + L2CAP_TRACE_API ("L2CA_GetChnlFcrMode() returns mode %d", p_ccb->peer_cfg.fcr.mode); + return (p_ccb->peer_cfg.fcr.mode); + } + + L2CAP_TRACE_API ("L2CA_GetChnlFcrMode() returns mode L2CAP_FCR_BASIC_MODE"); + return (L2CAP_FCR_BASIC_MODE); +} + +#endif ///CLASSIC_BT_INCLUDED == TRUE + +#if (BLE_L2CAP_COC_INCLUDED == TRUE) +/******************************************************************************* +** +** Function L2CA_RegisterLECoc +** +** Description Other layers call this function to register for L2CAP +** Connection Oriented Channel. +** +** Returns PSM to use or zero if error. Typically, the PSM returned +** is the same as was passed in, but for an outgoing-only +** connection to a dynamic PSM, a "virtual" PSM is returned +** and should be used in the calls to L2CA_ConnectLECocReq() +** and L2CA_DeregisterLECoc() +** +*******************************************************************************/ +UINT16 L2CA_RegisterLECoc(UINT16 psm, tL2CAP_APPL_INFO *p_cb_info) +{ + L2CAP_TRACE_API("%s called for LE PSM: 0x%04x", __func__, psm); + + /* Verify that the required callback info has been filled in + ** Note: Connection callbacks are required but not checked + ** for here because it is possible to be only a client + ** or only a server. + */ + if ((!p_cb_info->pL2CA_DataInd_Cb) + || (!p_cb_info->pL2CA_DisconnectInd_Cb)) + { + L2CAP_TRACE_ERROR("%s No cb registering BLE PSM: 0x%04x", __func__, psm); + return 0; + } + + /* Verify PSM is valid */ + if (!L2C_IS_VALID_LE_PSM(psm)) + { + L2CAP_TRACE_ERROR("%s Invalid BLE PSM value, PSM: 0x%04x", __func__, psm); + return 0; + } + + tL2C_RCB *p_rcb; + UINT16 vpsm = psm; + + /* Check if this is a registration for an outgoing-only connection to */ + /* a dynamic PSM. If so, allocate a "virtual" PSM for the app to use. */ + if ((psm >= 0x0080) && (p_cb_info->pL2CA_ConnectInd_Cb == NULL)) + { + for (vpsm = 0x0080; vpsm < 0x0100; vpsm++) + { + p_rcb = l2cu_find_ble_rcb_by_psm(vpsm); + if (p_rcb == NULL) { + break; + } + } + + L2CAP_TRACE_API("%s Real PSM: 0x%04x Virtual PSM: 0x%04x", __func__, psm, vpsm); + } + + /* If registration block already there, just overwrite it */ + p_rcb = l2cu_find_ble_rcb_by_psm(vpsm); + if (p_rcb == NULL) + { + p_rcb = l2cu_allocate_ble_rcb(vpsm); + if (p_rcb == NULL) + { + L2CAP_TRACE_WARNING("%s No BLE RCB available, PSM: 0x%04x vPSM: 0x%04x", + __func__, psm, vpsm); + return 0; + } + } + + p_rcb->api = *p_cb_info; + p_rcb->real_psm = psm; + + return vpsm; +} + +/******************************************************************************* +** +** Function L2CA_DeregisterLECoc +** +** Description Other layers call this function to de-register for L2CAP +** Connection Oriented Channel. +** +** Returns void +** +*******************************************************************************/ +void L2CA_DeregisterLECoc(UINT16 psm) +{ + L2CAP_TRACE_API("%s called for PSM: 0x%04x", __func__, psm); + + tL2C_RCB *p_rcb = l2cu_find_ble_rcb_by_psm(psm); + if (p_rcb == NULL) + { + L2CAP_TRACE_WARNING("%s PSM: 0x%04x not found for deregistration", __func__, psm); + return; + } + + tL2C_LCB *p_lcb = NULL; + list_node_t *p_node = NULL; + for (p_node = list_begin(l2cb.p_lcb_pool); p_node; p_node = list_next(p_node)) { + p_lcb = list_node(p_node); + if (!p_lcb->in_use || p_lcb->transport != BT_TRANSPORT_LE) { + continue; + } + + tL2C_CCB *p_ccb = p_lcb->ccb_queue.p_first_ccb; + if ((p_ccb == NULL) || (p_lcb->link_state == LST_DISCONNECTING)) { + continue; + } + + if (p_ccb->in_use && + (p_ccb->chnl_state == CST_W4_L2CAP_DISCONNECT_RSP || + p_ccb->chnl_state == CST_W4_L2CA_DISCONNECT_RSP)) { + continue; + } + + if (p_ccb->p_rcb == p_rcb) { + l2c_csm_execute(p_ccb, L2CEVT_L2CA_DISCONNECT_REQ, NULL); + } + } + + l2cu_release_rcb (p_rcb); +} + +/******************************************************************************* +** +** Function L2CA_ConnectLECocReq +** +** Description Higher layers call this function to create an L2CAP connection. +** Note that the connection is not established at this time, but +** connection establishment gets started. The callback function +** will be invoked when connection establishes or fails. +** +** Parameters: PSM: L2CAP PSM for the connection +** BD address of the peer +** Local Coc configurations + +** Returns the CID of the connection, or 0 if it failed to start +** +*******************************************************************************/ +UINT16 L2CA_ConnectLECocReq(UINT16 psm, BD_ADDR p_bd_addr, tL2CAP_LE_CFG_INFO *p_cfg) +{ + L2CAP_TRACE_API("%s PSM: 0x%04x BDA: %02x:%02x:%02x:%02x:%02x:%02x", __func__, psm, + p_bd_addr[0], p_bd_addr[1], p_bd_addr[2], p_bd_addr[3], p_bd_addr[4], p_bd_addr[5]); + + /* Fail if we have not established communications with the controller */ + if (!BTM_IsDeviceUp()) + { + L2CAP_TRACE_WARNING("%s BTU not ready", __func__); + return 0; + } + + /* Fail if the PSM is not registered */ + tL2C_RCB *p_rcb = l2cu_find_ble_rcb_by_psm(psm); + if (p_rcb == NULL) + { + L2CAP_TRACE_WARNING("%s No BLE RCB, PSM: 0x%04x", __func__, psm); + return 0; + } + + /* First, see if we already have a le link to the remote */ + tL2C_LCB *p_lcb = l2cu_find_lcb_by_bd_addr(p_bd_addr, BT_TRANSPORT_LE); + if (p_lcb == NULL) + { + /* No link. Get an LCB and start link establishment */ + p_lcb = l2cu_allocate_lcb(p_bd_addr, FALSE, BT_TRANSPORT_LE); + if ((p_lcb == NULL) + /* currently use BR/EDR for ERTM mode l2cap connection */ + || (l2cu_create_conn(p_lcb, BT_TRANSPORT_LE) == FALSE) ) + { + L2CAP_TRACE_WARNING("%s conn not started for PSM: 0x%04x p_lcb: 0x%p", + __func__, psm, p_lcb); + return 0; + } + } + + /* Allocate a channel control block */ + tL2C_CCB *p_ccb = l2cu_allocate_ccb(p_lcb, 0); + if (p_ccb == NULL) + { + L2CAP_TRACE_WARNING("%s no CCB, PSM: 0x%04x", __func__, psm); + return 0; + } + + /* Save registration info */ + p_ccb->p_rcb = p_rcb; + + /* Save the configuration */ + if (p_cfg) { + memcpy(&p_ccb->local_conn_cfg, p_cfg, sizeof(tL2CAP_LE_CFG_INFO)); + } + + /* If link is up, start the L2CAP connection */ + if (p_lcb->link_state == LST_CONNECTED) + { + if (p_ccb->p_lcb->transport == BT_TRANSPORT_LE) + { + L2CAP_TRACE_DEBUG("%s LE Link is up", __func__); + l2c_csm_execute(p_ccb, L2CEVT_L2CA_CONNECT_REQ, NULL); + } + } + + /* If link is disconnecting, save link info to retry after disconnect + * Possible Race condition when a reconnect occurs + * on the channel during a disconnect of link. This + * ccb will be automatically retried after link disconnect + * arrives + */ + else if (p_lcb->link_state == LST_DISCONNECTING) + { + L2CAP_TRACE_DEBUG("%s link disconnecting: RETRY LATER", __func__); + + /* Save ccb so it can be started after disconnect is finished */ + p_lcb->p_pending_ccb = p_ccb; + } + + L2CAP_TRACE_API("%s(psm: 0x%04x) returned CID: 0x%04x", __func__, psm, p_ccb->local_cid); + + /* Return the local CID as our handle */ + return p_ccb->local_cid; +} + +/******************************************************************************* +** +** Function L2CA_ConnectLECocRsp +** +** Description Higher layers call this function to accept an incoming +** L2CAP COC connection, for which they had gotten an connect +** indication callback. +** +** Returns TRUE for success, FALSE for failure +** +*******************************************************************************/ +BOOLEAN L2CA_ConnectLECocRsp (BD_ADDR p_bd_addr, UINT8 id, UINT16 lcid, UINT16 result, + UINT16 status, tL2CAP_LE_CFG_INFO *p_cfg) +{ + L2CAP_TRACE_API("%s CID: 0x%04x Result: %d Status: %d BDA: %02x:%02x:%02x:%02x:%02x:%02x", + __func__, lcid, result, status, + p_bd_addr[0], p_bd_addr[1], p_bd_addr[2], p_bd_addr[3], p_bd_addr[4], p_bd_addr[5]); + + + /* First, find the link control block */ + tL2C_LCB *p_lcb = l2cu_find_lcb_by_bd_addr(p_bd_addr, BT_TRANSPORT_LE); + if (p_lcb == NULL) + { + /* No link. Get an LCB and start link establishment */ + L2CAP_TRACE_WARNING("%s no LCB", __func__); + return FALSE; + } + + /* Now, find the channel control block */ + tL2C_CCB *p_ccb = l2cu_find_ccb_by_cid(p_lcb, lcid); + if (p_ccb == NULL) + { + L2CAP_TRACE_WARNING("%s no CCB", __func__); + return FALSE; + } + + /* The IDs must match */ + if (p_ccb->remote_id != id) + { + L2CAP_TRACE_WARNING("%s bad id. Expected: %d Got: %d", __func__, p_ccb->remote_id, id); + return FALSE; + } + + if (p_cfg) { + memcpy(&p_ccb->local_conn_cfg, p_cfg, sizeof(tL2CAP_LE_CFG_INFO)); + } + + if (result == L2CAP_CONN_OK) + l2c_csm_execute (p_ccb, L2CEVT_L2CA_CONNECT_RSP, NULL); + else + { + tL2C_CONN_INFO conn_info; + memcpy(conn_info.bd_addr, p_bd_addr, BD_ADDR_LEN); + conn_info.l2cap_result = result; + conn_info.l2cap_status = status; + l2c_csm_execute(p_ccb, L2CEVT_L2CA_CONNECT_RSP_NEG, &conn_info); + } + + return TRUE; +} + +/******************************************************************************* +** +** Function L2CA_GetPeerLECocConfig +** +** Description Get a peers configuration for LE Connection Oriented Channel. +** +** Parameters: local channel id +** Pointers to peers configuration storage area +** +** Return value: TRUE if peer is connected +** +*******************************************************************************/ +BOOLEAN L2CA_GetPeerLECocConfig (UINT16 lcid, tL2CAP_LE_CFG_INFO* peer_cfg) +{ + L2CAP_TRACE_API ("%s CID: 0x%04x", __func__, lcid); + + tL2C_CCB *p_ccb = l2cu_find_ccb_by_cid(NULL, lcid); + if (p_ccb == NULL) + { + L2CAP_TRACE_ERROR("%s No CCB for CID:0x%04x", __func__, lcid); + return FALSE; + } + + if (peer_cfg != NULL) { + memcpy(peer_cfg, &p_ccb->peer_conn_cfg, sizeof(tL2CAP_LE_CFG_INFO)); + } + + return TRUE; +} +#endif // (BLE_L2CAP_COC_INCLUDED == TRUE) + +#if (L2CAP_NUM_FIXED_CHNLS > 0) +/******************************************************************************* +** +** Function L2CA_RegisterFixedChannel +** +** Description Register a fixed channel. +** +** Parameters: Fixed Channel # +** Channel Callbacks and config +** +** Return value: - +** +*******************************************************************************/ +BOOLEAN L2CA_RegisterFixedChannel (UINT16 fixed_cid, tL2CAP_FIXED_CHNL_REG *p_freg) +{ + L2CAP_TRACE_DEBUG ("L2CA_RegisterFixedChannel() CID: 0x%04x, %p", fixed_cid,p_freg); + if ( (fixed_cid < L2CAP_FIRST_FIXED_CHNL) || (fixed_cid > L2CAP_LAST_FIXED_CHNL) ) { + L2CAP_TRACE_ERROR ("L2CA_RegisterFixedChannel() Invalid CID: 0x%04x", fixed_cid); + + return (FALSE); + } + + l2cb.fixed_reg[fixed_cid - L2CAP_FIRST_FIXED_CHNL] = *p_freg; + return (TRUE); +} + +/******************************************************************************* +** +** Function L2CA_ConnectFixedChnl +** +** Description Connect an fixed signalling channel to a remote device. +** +** Parameters: Fixed CID +** BD Address of remote +** BD Address type +** +** Return value: TRUE if connection started +** +*******************************************************************************/ +BOOLEAN L2CA_ConnectFixedChnl (UINT16 fixed_cid, BD_ADDR rem_bda, tBLE_ADDR_TYPE bd_addr_type, BOOLEAN is_aux) +{ + tL2C_LCB *p_lcb; + tBT_TRANSPORT transport = BT_TRANSPORT_BR_EDR; + + L2CAP_TRACE_API ("%s() CID: 0x%04x BDA: %08x%04x", __func__, fixed_cid, + (rem_bda[0] << 24) + (rem_bda[1] << 16) + (rem_bda[2] << 8) + rem_bda[3], (rem_bda[4] << 8) + rem_bda[5]); + + // Check CID is valid and registered + if ( (fixed_cid < L2CAP_FIRST_FIXED_CHNL) || (fixed_cid > L2CAP_LAST_FIXED_CHNL) + || (l2cb.fixed_reg[fixed_cid - L2CAP_FIRST_FIXED_CHNL].pL2CA_FixedData_Cb == NULL) ) { + L2CAP_TRACE_ERROR ("%s() Invalid CID: 0x%04x", __func__, fixed_cid); + return (FALSE); + } + + // Fail if BT is not yet up + if (!BTM_IsDeviceUp()) { + L2CAP_TRACE_WARNING ("%s(0x%04x) - BTU not ready", __func__, fixed_cid); + return (FALSE); + } + +#if BLE_INCLUDED == TRUE + if (fixed_cid >= L2CAP_ATT_CID && fixed_cid <= L2CAP_SMP_CID) { + transport = BT_TRANSPORT_LE; + } +#endif + + tL2C_BLE_FIXED_CHNLS_MASK peer_channel_mask; + + // If we already have a link to the remote, check if it supports that CID + if ((p_lcb = l2cu_find_lcb_by_bd_addr (rem_bda, transport)) != NULL) { + // Fixed channels are mandatory on LE transports so ignore the received + // channel mask and use the locally cached LE channel mask. + +#if BLE_INCLUDED == TRUE + if (transport == BT_TRANSPORT_LE) { + peer_channel_mask = l2cb.l2c_ble_fixed_chnls_mask; + } else +#endif + { + peer_channel_mask = p_lcb->peer_chnl_mask[0]; + } + + // Check for supported channel + if (!(peer_channel_mask & (1 << fixed_cid))) { + L2CAP_TRACE_EVENT ("%s() CID:0x%04x BDA: %08x%04x not supported", __func__, + fixed_cid, (rem_bda[0] << 24) + (rem_bda[1] << 16) + (rem_bda[2] << 8) + rem_bda[3], + (rem_bda[4] << 8) + rem_bda[5]); + return FALSE; + } + + // Get a CCB and link the lcb to it + if (!l2cu_initialize_fixed_ccb (p_lcb, fixed_cid, + &l2cb.fixed_reg[fixed_cid - L2CAP_FIRST_FIXED_CHNL].fixed_chnl_opts)) { + L2CAP_TRACE_WARNING ("%s(0x%04x) - LCB but no CCB", __func__, fixed_cid); + return FALSE; + } + + // racing with disconnecting, queue the connection request + if (p_lcb->link_state == LST_DISCONNECTING) { + L2CAP_TRACE_DEBUG ("%s() - link disconnecting: RETRY LATER", __func__); + /* Save ccb so it can be started after disconnect is finished */ + p_lcb->p_pending_ccb = p_lcb->p_fixed_ccbs[fixed_cid - L2CAP_FIRST_FIXED_CHNL]; + return TRUE; + } + + (*l2cb.fixed_reg[fixed_cid - L2CAP_FIRST_FIXED_CHNL].pL2CA_FixedConn_Cb) + (fixed_cid, p_lcb->remote_bd_addr, TRUE, 0, transport); + + return TRUE; + } + + // No link. Get an LCB and start link establishment + if ((p_lcb = l2cu_allocate_lcb (rem_bda, FALSE, transport)) == NULL) { + L2CAP_TRACE_WARNING ("%s(0x%04x) - no LCB", __func__, fixed_cid); + return FALSE; + } + + // Get a CCB and link the lcb to it + if (!l2cu_initialize_fixed_ccb (p_lcb, fixed_cid, + &l2cb.fixed_reg[fixed_cid - L2CAP_FIRST_FIXED_CHNL].fixed_chnl_opts)) { + p_lcb->disc_reason = L2CAP_CONN_NO_RESOURCES; + L2CAP_TRACE_WARNING ("%s(0x%04x) - no CCB", __func__, fixed_cid); + l2cu_release_lcb (p_lcb); + return FALSE; + } +#if (BLE_INCLUDED == TRUE) + p_lcb->is_aux = is_aux; + p_lcb->open_addr_type = bd_addr_type; +#endif + if (!l2cu_create_conn(p_lcb, transport)) { + L2CAP_TRACE_WARNING ("%s() - create_conn failed", __func__); + l2cu_release_lcb (p_lcb); + return FALSE; + } + return TRUE; +} + +/******************************************************************************* +** +** Function L2CA_SendFixedChnlData +** +** Description Write data on a fixed channel. +** +** Parameters: Fixed CID +** BD Address of remote +** Pointer to buffer of type BT_HDR +** +** Return value L2CAP_DW_SUCCESS, if data accepted +** L2CAP_DW_FAILED, if error +** +*******************************************************************************/ +UINT16 L2CA_SendFixedChnlData (UINT16 fixed_cid, BD_ADDR rem_bda, BT_HDR *p_buf) +{ + tL2C_LCB *p_lcb; + tBT_TRANSPORT transport = BT_TRANSPORT_BR_EDR; + + L2CAP_TRACE_API ("L2CA_SendFixedChnlData() CID: 0x%04x BDA: %08x%04x", fixed_cid, + (rem_bda[0] << 24) + (rem_bda[1] << 16) + (rem_bda[2] << 8) + rem_bda[3], (rem_bda[4] << 8) + rem_bda[5]); + +#if BLE_INCLUDED == TRUE + if (fixed_cid >= L2CAP_ATT_CID && fixed_cid <= L2CAP_SMP_CID) { + transport = BT_TRANSPORT_LE; + } +#endif + + // Check CID is valid and registered + if ( (fixed_cid < L2CAP_FIRST_FIXED_CHNL) || (fixed_cid > L2CAP_LAST_FIXED_CHNL) + || (l2cb.fixed_reg[fixed_cid - L2CAP_FIRST_FIXED_CHNL].pL2CA_FixedData_Cb == NULL) ) { + L2CAP_TRACE_ERROR ("L2CA_SendFixedChnlData() Invalid CID: 0x%04x", fixed_cid); + osi_free (p_buf); + return (L2CAP_DW_FAILED); + } + + // Fail if BT is not yet up + if (!BTM_IsDeviceUp()) { + L2CAP_TRACE_WARNING ("L2CA_SendFixedChnlData(0x%04x) - BTU not ready", fixed_cid); + osi_free (p_buf); + return (L2CAP_DW_FAILED); + } + + // We need to have a link up + if ((p_lcb = l2cu_find_lcb_by_bd_addr (rem_bda, transport)) == NULL || + /* if link is disconnecting, also report data sending failure */ + p_lcb->link_state == LST_DISCONNECTING) { + L2CAP_TRACE_WARNING ("L2CA_SendFixedChnlData(0x%04x) - no LCB", fixed_cid); + osi_free (p_buf); + return (L2CAP_DW_FAILED); + } + + tL2C_BLE_FIXED_CHNLS_MASK peer_channel_mask; + + // Select peer channels mask to use depending on transport +#if BLE_INCLUDED == TRUE + if (transport == BT_TRANSPORT_LE) { + peer_channel_mask = l2cb.l2c_ble_fixed_chnls_mask; + } else +#endif + { + peer_channel_mask = p_lcb->peer_chnl_mask[0]; + } + + if ((peer_channel_mask & (1 << fixed_cid)) == 0) { + L2CAP_TRACE_WARNING ("L2CA_SendFixedChnlData() - peer does not support fixed chnl: 0x%04x", fixed_cid); + osi_free (p_buf); + return (L2CAP_DW_FAILED); + } + + p_buf->event = 0; + p_buf->layer_specific = L2CAP_FLUSHABLE_CH_BASED; + + if (!p_lcb->p_fixed_ccbs[fixed_cid - L2CAP_FIRST_FIXED_CHNL]) { + if (!l2cu_initialize_fixed_ccb (p_lcb, fixed_cid, &l2cb.fixed_reg[fixed_cid - L2CAP_FIRST_FIXED_CHNL].fixed_chnl_opts)) { + L2CAP_TRACE_WARNING ("L2CA_SendFixedChnlData() - no CCB for chnl: 0x%4x", fixed_cid); + osi_free (p_buf); + return (L2CAP_DW_FAILED); + } + } + + // If already congested, do not accept any more packets + if (p_lcb->p_fixed_ccbs[fixed_cid - L2CAP_FIRST_FIXED_CHNL]->cong_sent && fixed_cid != L2CAP_SMP_CID) { + L2CAP_TRACE_DEBUG ("L2CAP - CID: 0x%04x cannot send, already congested\ + xmit_hold_q.count: %u buff_quota: %u", fixed_cid, + fixed_queue_length(p_lcb->p_fixed_ccbs[fixed_cid - L2CAP_FIRST_FIXED_CHNL]->xmit_hold_q), + p_lcb->p_fixed_ccbs[fixed_cid - L2CAP_FIRST_FIXED_CHNL]->buff_quota); + osi_free(p_buf); + return (L2CAP_DW_CONGESTED); + } + + l2c_enqueue_peer_data (p_lcb->p_fixed_ccbs[fixed_cid - L2CAP_FIRST_FIXED_CHNL], p_buf); + + l2c_link_check_send_pkts (p_lcb, NULL, NULL); + + // If there is no dynamic CCB on the link, restart the idle timer each time something is sent + if (p_lcb->in_use && p_lcb->link_state == LST_CONNECTED && !p_lcb->ccb_queue.p_first_ccb) { + l2cu_no_dynamic_ccbs (p_lcb); + } + + if (p_lcb->p_fixed_ccbs[fixed_cid - L2CAP_FIRST_FIXED_CHNL]->cong_sent) { + return (L2CAP_DW_CONGESTED); + } + + return (L2CAP_DW_SUCCESS); +} + +BOOLEAN L2CA_CheckIsCongest(UINT16 fixed_cid, BD_ADDR addr) +{ + tL2C_LCB *p_lcb; + p_lcb = l2cu_find_lcb_by_bd_addr(addr, BT_TRANSPORT_LE); + + if (p_lcb != NULL && p_lcb->p_fixed_ccbs[fixed_cid - L2CAP_FIRST_FIXED_CHNL] != NULL) { + return p_lcb->p_fixed_ccbs[fixed_cid - L2CAP_FIRST_FIXED_CHNL]->cong_sent; + } + + return TRUE; +} + +#if (BLE_INCLUDED == TRUE) +UINT16 L2CA_GetFreePktBufferNum_LE(void) +{ + return l2cb.controller_le_xmit_window; +} +UINT16 L2CA_GetCurFreePktBufferNum_LE(UINT16 conn_id) +{ + uint16_t num = 0; + tl2c_buff_param_t param; + param.conn_id = conn_id; + param.get_num = # + l2ble_update_att_acl_pkt_num(L2CA_GET_ATT_NUM, ¶m); + return num; +} +#endif + +/******************************************************************************* +** +** Function L2CA_RemoveFixedChnl +** +** Description Remove a fixed channel to a remote device. +** +** Parameters: Fixed CID +** BD Address of remote +** Idle timeout to use (or 0xFFFF if don't care) +** +** Return value: TRUE if channel removed +** +*******************************************************************************/ +BOOLEAN L2CA_RemoveFixedChnl (UINT16 fixed_cid, BD_ADDR rem_bda) +{ + tL2C_LCB *p_lcb; + tL2C_CCB *p_ccb; + tBT_TRANSPORT transport = BT_TRANSPORT_BR_EDR; + + /* Check CID is valid and registered */ + if ( (fixed_cid < L2CAP_FIRST_FIXED_CHNL) || (fixed_cid > L2CAP_LAST_FIXED_CHNL) + || (l2cb.fixed_reg[fixed_cid - L2CAP_FIRST_FIXED_CHNL].pL2CA_FixedData_Cb == NULL) ) { + L2CAP_TRACE_ERROR ("L2CA_RemoveFixedChnl() Invalid CID: 0x%04x", fixed_cid); + return (FALSE); + } + +#if BLE_INCLUDED == TRUE + if (fixed_cid >= L2CAP_ATT_CID && fixed_cid <= L2CAP_SMP_CID) { + transport = BT_TRANSPORT_LE; + } +#endif + + /* Is a fixed channel connected to the remote BDA ?*/ + p_lcb = l2cu_find_lcb_by_bd_addr (rem_bda, transport); + + if ( ((p_lcb) == NULL) || (!p_lcb->p_fixed_ccbs[fixed_cid - L2CAP_FIRST_FIXED_CHNL]) ) { + L2CAP_TRACE_DEBUG ("L2CA_RemoveFixedChnl() CID: 0x%04x BDA: %08x%04x not connected", fixed_cid, + (rem_bda[0] << 24) + (rem_bda[1] << 16) + (rem_bda[2] << 8) + rem_bda[3], (rem_bda[4] << 8) + rem_bda[5]); + return (FALSE); + } + + L2CAP_TRACE_API ("L2CA_RemoveFixedChnl() CID: 0x%04x BDA: %08x%04x", fixed_cid, + (rem_bda[0] << 24) + (rem_bda[1] << 16) + (rem_bda[2] << 8) + rem_bda[3], (rem_bda[4] << 8) + rem_bda[5]); + + /* Release the CCB, starting an inactivity timeout on the LCB if no other CCBs exist */ + p_ccb = p_lcb->p_fixed_ccbs[fixed_cid - L2CAP_FIRST_FIXED_CHNL]; + + p_lcb->p_fixed_ccbs[fixed_cid - L2CAP_FIRST_FIXED_CHNL] = NULL; + p_lcb->disc_reason = HCI_ERR_CONN_CAUSE_LOCAL_HOST; + +#if BLE_INCLUDED == TRUE + // Retain the link for a few more seconds after SMP pairing is done, since the Android + // platform always does service discovery after pairing is complete. This will avoid + // the link down (pairing is complete) and an immediate re-connection for service + // discovery. + // Some devices do not do auto advertising when link is dropped, thus fail the second + // connection and service discovery. + if ((fixed_cid == L2CAP_ATT_CID ) && !p_lcb->ccb_queue.p_first_ccb) { + p_lcb->idle_timeout = 0; + } +#endif + + l2cu_release_ccb (p_ccb); + + return (TRUE); +} + +#if BLE_INCLUDED == TRUE +BOOLEAN L2CA_BleDisconnect (BD_ADDR rem_bda) +{ + tL2C_LCB *p_lcb; + tGATT_TCB *p_tcb; + + p_lcb = l2cu_find_lcb_by_bd_addr (rem_bda, BT_TRANSPORT_LE); + if (p_lcb == NULL) { + return FALSE; + } + + if (p_lcb->link_state != LST_CONNECTED) { + return FALSE; + } + + p_lcb->disc_reason = HCI_ERR_CONN_CAUSE_LOCAL_HOST; + p_lcb->link_state = LST_DISCONNECTING; + btsnd_hcic_disconnect (p_lcb->handle, HCI_ERR_PEER_USER); + + p_tcb = gatt_find_tcb_by_addr(rem_bda, BT_TRANSPORT_LE); + if (p_tcb == NULL) { + return FALSE; + } + + gatt_set_ch_state(p_tcb, GATT_CH_CLOSING); + + return TRUE; +} +#endif + +/******************************************************************************* +** +** Function L2CA_SetFixedChannelTout +** +** Description Higher layers call this function to set the idle timeout for +** a fixed channel. The "idle timeout" is the amount of time that +** a connection can remain up with no L2CAP channels on it. +** A timeout of zero means that the connection will be torn +** down immediately when the last channel is removed. +** A timeout of 0xFFFF means no timeout. Values are in seconds. +** A bd_addr is the remote BD address. If bd_addr = BT_BD_ANY, +** then the idle timeouts for all active l2cap links will be +** changed. +** +** Returns TRUE if command succeeded, FALSE if failed +** +*******************************************************************************/ +BOOLEAN L2CA_SetFixedChannelTout (BD_ADDR rem_bda, UINT16 fixed_cid, UINT16 idle_tout) +{ + tL2C_LCB *p_lcb; + tBT_TRANSPORT transport = BT_TRANSPORT_BR_EDR; + +#if BLE_INCLUDED == TRUE + if (fixed_cid >= L2CAP_ATT_CID && fixed_cid <= L2CAP_SMP_CID) { + transport = BT_TRANSPORT_LE; + } +#endif + if (fixed_cidp_fixed_ccbs[fixed_cid - L2CAP_FIRST_FIXED_CHNL]) ) { + L2CAP_TRACE_WARNING ("L2CA_SetFixedChannelTout() CID: 0x%04x BDA: %08x%04x not connected", fixed_cid, + (rem_bda[0] << 24) + (rem_bda[1] << 16) + (rem_bda[2] << 8) + rem_bda[3], (rem_bda[4] << 8) + rem_bda[5]); + return (FALSE); + } + + p_lcb->p_fixed_ccbs[fixed_cid - L2CAP_FIRST_FIXED_CHNL]->fixed_chnl_idle_tout = idle_tout; + + if (p_lcb->in_use && p_lcb->link_state == LST_CONNECTED && !p_lcb->ccb_queue.p_first_ccb) { + /* If there are no dynamic CCBs, (re)start the idle timer in case we changed it */ + l2cu_no_dynamic_ccbs (p_lcb); + } + + return TRUE; +} + +#endif /* #if (L2CAP_NUM_FIXED_CHNLS > 0) */ + +#if (CLASSIC_BT_INCLUDED == TRUE) +/******************************************************************************* +** +** Function L2CA_GetCurrentConfig +** +** Description This function returns configurations of L2CAP channel +** pp_our_cfg : pointer of our saved configuration options +** p_our_cfg_bits : valid config in bitmap +** pp_peer_cfg: pointer of peer's saved configuration options +** p_peer_cfg_bits : valid config in bitmap +** +** Returns TRUE if successful +** +*******************************************************************************/ +BOOLEAN L2CA_GetCurrentConfig (UINT16 lcid, + tL2CAP_CFG_INFO **pp_our_cfg, tL2CAP_CH_CFG_BITS *p_our_cfg_bits, + tL2CAP_CFG_INFO **pp_peer_cfg, tL2CAP_CH_CFG_BITS *p_peer_cfg_bits) +{ + tL2C_CCB *p_ccb; + + L2CAP_TRACE_API ("L2CA_GetCurrentConfig() CID: 0x%04x", lcid); + + p_ccb = l2cu_find_ccb_by_cid(NULL, lcid); + + if (p_ccb) { + *pp_our_cfg = &(p_ccb->our_cfg); + + /* convert valid config items into bitmap */ + *p_our_cfg_bits = 0; + if (p_ccb->our_cfg.mtu_present) { + *p_our_cfg_bits |= L2CAP_CH_CFG_MASK_MTU; + } + if (p_ccb->our_cfg.qos_present) { + *p_our_cfg_bits |= L2CAP_CH_CFG_MASK_QOS; + } + if (p_ccb->our_cfg.flush_to_present) { + *p_our_cfg_bits |= L2CAP_CH_CFG_MASK_FLUSH_TO; + } + if (p_ccb->our_cfg.fcr_present) { + *p_our_cfg_bits |= L2CAP_CH_CFG_MASK_FCR; + } + if (p_ccb->our_cfg.fcs_present) { + *p_our_cfg_bits |= L2CAP_CH_CFG_MASK_FCS; + } + if (p_ccb->our_cfg.ext_flow_spec_present) { + *p_our_cfg_bits |= L2CAP_CH_CFG_MASK_EXT_FLOW_SPEC; + } + + *pp_peer_cfg = &(p_ccb->peer_cfg); + *p_peer_cfg_bits = p_ccb->peer_cfg_bits; + + return TRUE; + } else { + L2CAP_TRACE_ERROR ("No CCB for CID:0x%04x", lcid); + return FALSE; + } +} + +/******************************************************************************* +** +** Function L2CA_RegForNoCPEvt +** +** Description Register callback for Number of Completed Packets event. +** +** Input Param p_cb - callback for Number of completed packets event +** p_bda - BT address of remote device +** +** Returns TRUE if registered OK, else FALSE +** +*******************************************************************************/ +BOOLEAN L2CA_RegForNoCPEvt(tL2CA_NOCP_CB *p_cb, BD_ADDR p_bda) +{ + tL2C_LCB *p_lcb; + + /* Find the link that is associated with this remote bdaddr */ + p_lcb = l2cu_find_lcb_by_bd_addr (p_bda, BT_TRANSPORT_BR_EDR); + + /* If no link for this handle, nothing to do. */ + if (!p_lcb) { + return FALSE; + } + + p_lcb->p_nocp_cb = p_cb; + + return TRUE; +} +#endif ///CLASSIC_BT_INCLUDED == TRUE + +/******************************************************************************* +** +** Function L2CA_DataWrite +** +** Description Higher layers call this function to write data. +** +** Returns L2CAP_DW_SUCCESS, if data accepted, else FALSE +** L2CAP_DW_CONGESTED, if data accepted and the channel is congested +** L2CAP_DW_FAILED, if error +** +*******************************************************************************/ +#if (CLASSIC_BT_INCLUDED == TRUE) +UINT8 L2CA_DataWrite (UINT16 cid, BT_HDR *p_data) +{ + L2CAP_TRACE_API ("L2CA_DataWrite() CID: 0x%04x Len: %d", cid, p_data->len); + return l2c_data_write (cid, p_data, L2CAP_FLUSHABLE_CH_BASED); +} +#endif ///CLASSIC_BT_INCLUDED == TRUE + +/******************************************************************************* +** +** Function l2cap_bqb_write_data +** +** Description Call L2CA_DataWrite and write I-Frame data for BQB test. +** +** Returns None +** +*******************************************************************************/ +#if (BT_CLASSIC_BQB_INCLUDED == TRUE) +void l2cap_bqb_write_data(UINT16 cid) +{ + BT_HDR *p_buf; + uint8_t *p; + + if ((p_buf = (BT_HDR *)osi_malloc(SDP_DATA_BUF_SIZE)) != NULL) { + p_buf->len = 30; + p_buf->offset = L2CAP_MIN_OFFSET; + p = (UINT8 *)(p_buf + 1) + p_buf->offset; + for(int i = 0 ; i < 10; i++) { + UINT8_TO_BE_STREAM(p, 0) + } + L2CA_DataWrite(cid, p_buf); + } +} +#endif /* BT_CLASSIC_BQB_INCLUDED */ + +/******************************************************************************* +** +** Function L2CA_SetChnlFlushability +** +** Description Higher layers call this function to set a channels +** flushability flags +** +** Returns TRUE if CID found, else FALSE +** +*******************************************************************************/ +BOOLEAN L2CA_SetChnlFlushability (UINT16 cid, BOOLEAN is_flushable) +{ +#if (L2CAP_NON_FLUSHABLE_PB_INCLUDED == TRUE) + + tL2C_CCB *p_ccb; + + /* Find the channel control block. We don't know the link it is on. */ + if ((p_ccb = l2cu_find_ccb_by_cid (NULL, cid)) == NULL) { + L2CAP_TRACE_WARNING ("L2CAP - no CCB for L2CA_SetChnlFlushability, CID: %d", cid); + return (FALSE); + } + + p_ccb->is_flushable = is_flushable; + + L2CAP_TRACE_API ("L2CA_SetChnlFlushability() CID: 0x%04x is_flushable: %d", cid, is_flushable); + +#endif + + return (TRUE); +} + +/******************************************************************************* +** +** Function L2CA_DataWriteEx +** +** Description Higher layers call this function to write data with extended +** flags. +** flags : L2CAP_FLUSHABLE_CH_BASED +** L2CAP_FLUSHABLE_PKT +** L2CAP_NON_FLUSHABLE_PKT +** +** Returns L2CAP_DW_SUCCESS, if data accepted, else FALSE +** L2CAP_DW_CONGESTED, if data accepted and the channel is congested +** L2CAP_DW_FAILED, if error +** +*******************************************************************************/ +#if (CLASSIC_BT_INCLUDED == TRUE) +UINT8 L2CA_DataWriteEx (UINT16 cid, BT_HDR *p_data, UINT16 flags) +{ + L2CAP_TRACE_API ("L2CA_DataWriteEx() CID: 0x%04x Len: %d Flags:0x%04X", + cid, p_data->len, flags); + return l2c_data_write (cid, p_data, flags); +} +#endif ///CLASSIC_BT_INCLUDED == TRUE + +/******************************************************************************* +** +** Function L2CA_FlushChannel +** +** Description This function flushes none, some or all buffers queued up +** for xmission for a particular CID. If called with +** L2CAP_FLUSH_CHANS_GET (0), it simply returns the number +** of buffers queued for that CID L2CAP_FLUSH_CHANS_ALL (0xffff) +** flushes all buffers. All other values specifies the maximum +** buffers to flush. +** +** Returns Number of buffers left queued for that CID +** +*******************************************************************************/ +UINT16 L2CA_FlushChannel (UINT16 lcid, UINT16 num_to_flush) +{ + tL2C_CCB *p_ccb; + tL2C_LCB *p_lcb; + UINT16 num_left = 0, + num_flushed1 = 0, + num_flushed2 = 0; + + p_ccb = l2cu_find_ccb_by_cid(NULL, lcid); + + if ( !p_ccb || ((p_lcb = p_ccb->p_lcb) == NULL) ) { + L2CAP_TRACE_WARNING ("L2CA_FlushChannel() abnormally returning 0 CID: 0x%04x", lcid); + return (0); + } + + if (num_to_flush != L2CAP_FLUSH_CHANS_GET) { + L2CAP_TRACE_API ("L2CA_FlushChannel (FLUSH) CID: 0x%04x NumToFlush: %d QC: %u pFirst: %p", + lcid, num_to_flush, + fixed_queue_length(p_ccb->xmit_hold_q), + fixed_queue_try_peek_first(p_ccb->xmit_hold_q)); + } else { + L2CAP_TRACE_API ("L2CA_FlushChannel (QUERY) CID: 0x%04x", lcid); + } + + /* Cannot flush eRTM buffers once they have a sequence number */ + if (p_ccb->peer_cfg.fcr.mode != L2CAP_FCR_ERTM_MODE) { +#if L2CAP_NON_FLUSHABLE_PB_INCLUDED == TRUE + if (num_to_flush != L2CAP_FLUSH_CHANS_GET) { + /* If the controller supports enhanced flush, flush the data queued at the controller */ + if ( (HCI_NON_FLUSHABLE_PB_SUPPORTED(BTM_ReadLocalFeatures ())) + && (BTM_GetNumScoLinks() == 0) ) { + if ( l2cb.is_flush_active == FALSE ) { + l2cb.is_flush_active = TRUE; + + /* The only packet type defined - 0 - Automatically-Flushable Only */ + btsnd_hcic_enhanced_flush (p_lcb->handle, 0); + } + } + } +#endif + + // Iterate though list and flush the amount requested from + // the transmit data queue that satisfy the layer and event conditions. + for (const list_node_t *node = list_begin(p_lcb->link_xmit_data_q); + (num_to_flush > 0) && node != list_end(p_lcb->link_xmit_data_q);) { + BT_HDR *p_buf = (BT_HDR *)list_node(node); + node = list_next(node); + if ((p_buf->layer_specific == 0) && (p_buf->event == lcid)) { + num_to_flush--; + num_flushed1++; + + list_remove(p_lcb->link_xmit_data_q, p_buf); + osi_free(p_buf); + } + } + } + + /* If needed, flush buffers in the CCB xmit hold queue */ + while ( (num_to_flush != 0) && (!fixed_queue_is_empty(p_ccb->xmit_hold_q))) { + BT_HDR *p_buf = (BT_HDR *)fixed_queue_dequeue(p_ccb->xmit_hold_q, 0); + if (p_buf) { + osi_free (p_buf); + } + num_to_flush--; + num_flushed2++; + } + + /* If app needs to track all packets, call him */ + if ( (p_ccb->p_rcb) && (p_ccb->p_rcb->api.pL2CA_TxComplete_Cb) && (num_flushed2) ) { + (*p_ccb->p_rcb->api.pL2CA_TxComplete_Cb)(p_ccb->local_cid, num_flushed2); + } + + /* Now count how many are left */ + for (const list_node_t *node = list_begin(p_lcb->link_xmit_data_q); + node != list_end(p_lcb->link_xmit_data_q); + node = list_next(node)) { + + BT_HDR *p_buf = (BT_HDR *)list_node(node); + if (p_buf->event == lcid) { + num_left++; + } + } + + /* Add in the number in the CCB xmit queue */ + num_left += fixed_queue_length(p_ccb->xmit_hold_q); + + /* Return the local number of buffers left for the CID */ + L2CAP_TRACE_DEBUG ("L2CA_FlushChannel() flushed: %u + %u, num_left: %u", num_flushed1, num_flushed2, num_left); + + /* If we were congested, and now we are not, tell the app */ + l2cu_check_channel_congestion (p_ccb); + + return (num_left); +} + +/****************************************************************************** +** +** Function update_acl_pkt_num +** +** Description Update the number of att acl packets to be sent in xmit_hold_q. +** +** Returns None +** +*******************************************************************************/ +#if BLE_INCLUDED == TRUE +void l2ble_update_att_acl_pkt_num(UINT8 type, tl2c_buff_param_t *param) +{ + static SemaphoreHandle_t buff_semaphore = NULL ; + static INT16 btc_buf; + static INT16 btu_buf; + + if(buff_semaphore == NULL && type != L2CA_BUFF_INI){ + L2CAP_TRACE_ERROR("%s buff_semaphore not init", __func__); + return; + } + switch (type) + { + case L2CA_ADD_BTC_NUM:{ + xSemaphoreTake(buff_semaphore, portMAX_DELAY); + btc_buf ++; + xSemaphoreGive(buff_semaphore); + break; + } + case L2CA_DECREASE_BTC_NUM:{ + xSemaphoreTake(buff_semaphore, portMAX_DELAY); + btc_buf --; + xSemaphoreGive(buff_semaphore); + break; + } + case L2CA_ADD_BTU_NUM:{ + xSemaphoreTake(buff_semaphore, portMAX_DELAY); + btu_buf ++; + xSemaphoreGive(buff_semaphore); + break; + } + case L2CA_DECREASE_BTU_NUM:{ + xSemaphoreTake(buff_semaphore, portMAX_DELAY); + btu_buf --; + xSemaphoreGive(buff_semaphore); + break; + } + case L2CA_GET_ATT_NUM:{ + xSemaphoreTake(buff_semaphore, portMAX_DELAY); + INT16 att_acl_pkt_num = 0; + INT16 att_max_num = 0; + *(param->get_num) = 0; + UINT8 tcb_idx = param->conn_id; + tGATT_TCB * p_tcb = gatt_get_tcb_by_idx(tcb_idx); + if (p_tcb == NULL){ + L2CAP_TRACE_ERROR("%s not found p_tcb", __func__); + xSemaphoreGive(buff_semaphore); + break; + } + + if (!gatt_check_connection_state_by_tcb(p_tcb)) { + L2CAP_TRACE_ERROR("connection not established\n"); + xSemaphoreGive(buff_semaphore); + break; + } + + tL2C_LCB * p_lcb = l2cu_find_lcb_by_bd_addr (p_tcb->peer_bda, BT_TRANSPORT_LE); + if (p_lcb == NULL){ + L2CAP_TRACE_ERROR("%s not found p_lcb", __func__); + xSemaphoreGive(buff_semaphore); + break; + } + + tL2C_CCB *p_ccb = p_lcb->p_fixed_ccbs[L2CAP_ATT_CID - L2CAP_FIRST_FIXED_CHNL]; + if(p_ccb == NULL) { + L2CAP_TRACE_ERROR("%s not found p_ccb", __func__); + xSemaphoreGive(buff_semaphore); + break; + } + + fixed_queue_t * queue = p_ccb->xmit_hold_q; + att_max_num = MIN(p_lcb->link_xmit_quota, L2CAP_CACHE_ATT_ACL_NUM); + if (queue == NULL){ + L2CAP_TRACE_ERROR("%s not found queue", __func__); + xSemaphoreGive(buff_semaphore); + break; + } + att_acl_pkt_num = fixed_queue_length(queue); + if(att_acl_pkt_num < att_max_num){ + if(btc_buf + btu_buf < att_max_num - att_acl_pkt_num){ + *(param->get_num) = att_max_num - att_acl_pkt_num - (btc_buf + btu_buf); + } + } + xSemaphoreGive(buff_semaphore); + break; + } + case L2CA_BUFF_INI:{ + btc_buf = 0; + btu_buf = 0; + buff_semaphore = xSemaphoreCreateBinary(); + if (buff_semaphore == NULL) { + L2CAP_TRACE_ERROR("%s NO MEMORY", __func__); + break; + } + xSemaphoreGive(buff_semaphore); + break; + } + case L2CA_BUFF_DEINIT:{ + xSemaphoreTake(buff_semaphore, portMAX_DELAY); + btc_buf = 0; + btu_buf = 0; + xSemaphoreGive(buff_semaphore); + vSemaphoreDelete(buff_semaphore); + buff_semaphore = NULL; + break; + } + case L2CA_BUFF_FREE:{ + xSemaphoreTake(buff_semaphore, portMAX_DELAY); + // Do nothing + xSemaphoreGive(buff_semaphore); + break; + } + default: + break; + } +} +#endif diff --git a/lib/bt/host/bluedroid/stack/l2cap/l2c_ble.c b/lib/bt/host/bluedroid/stack/l2cap/l2c_ble.c new file mode 100644 index 00000000..4e6c8534 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/l2cap/l2c_ble.c @@ -0,0 +1,1707 @@ +/****************************************************************************** + * + * Copyright (C) 2009-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * this file contains functions relating to BLE management. + * + ******************************************************************************/ + +#include +#include "common/bt_target.h" +//#include "bt_utils.h" +#include "stack/l2cdefs.h" +#include "l2c_int.h" +#include "stack/btu.h" +#include "btm_int.h" +#include "stack/hcimsgs.h" +#include "device/controller.h" + +#if (BLE_INCLUDED == TRUE) + +#if (BLE_50_FEATURE_SUPPORT == TRUE) +#define EXT_CONN_INT_DEF_1M MAX(((MAX_ACL_CONNECTIONS + 1) * 4), 12) +#define EXT_CONN_INT_DEF_2M MAX(((MAX_ACL_CONNECTIONS + 1) * 2), 12) +#define EXT_CONN_INT_DEF_CODED (320) // 306-> 362Kbps + +const static tHCI_ExtConnParams ext_conn_params_1m_phy = { + .scan_interval = 0x40, + .scan_window = 0x40, + .conn_interval_min = EXT_CONN_INT_DEF_1M, + .conn_interval_max = EXT_CONN_INT_DEF_1M, + .conn_latency = 0, + .sup_timeout = 600, + .min_ce_len = 0, + .max_ce_len = 0, +}; +const static tHCI_ExtConnParams ext_conn_params_2m_phy = { + .scan_interval = 0x40, + .scan_window = 0x40, + .conn_interval_min = EXT_CONN_INT_DEF_2M, + .conn_interval_max = EXT_CONN_INT_DEF_2M, + .conn_latency = 0, + .sup_timeout = 600, + .min_ce_len = 0, + .max_ce_len = 0, +}; +const static tHCI_ExtConnParams ext_conn_params_coded_phy = { + .scan_interval = 0x40, + .scan_window = 0x40, + .conn_interval_min = EXT_CONN_INT_DEF_CODED, + .conn_interval_max = EXT_CONN_INT_DEF_CODED, + .conn_latency = 0, + .sup_timeout = 600, + .min_ce_len = 0, + .max_ce_len = 0, +}; +#define BLE_PHY_NO_PREF 0 +#define BLE_PHY_PREF_MASK ((1 << 2) | (1 << 1) | (1 << 0)) + +#endif // #if (BLE_50_FEATURE_SUPPORT == TRUE) + +static BOOLEAN l2cble_start_conn_update (tL2C_LCB *p_lcb); +extern int64_t esp_system_get_time(void); + +/******************************************************************************* +** +** Function L2CA_CancelBleConnectReq +** +** Description Cancel a pending connection attempt to a BLE device. +** +** Parameters: BD Address of remote +** +** Return value: TRUE if connection was cancelled +** +*******************************************************************************/ +BOOLEAN L2CA_CancelBleConnectReq (BD_ADDR rem_bda) +{ + tL2C_LCB *p_lcb; + + /* There can be only one BLE connection request outstanding at a time */ + if (btm_ble_get_conn_st() == BLE_CONN_IDLE) { + L2CAP_TRACE_WARNING ("L2CA_CancelBleConnectReq - no connection pending"); + return (FALSE); + } + + if (memcmp (rem_bda, l2cb.ble_connecting_bda, BD_ADDR_LEN)) { + L2CAP_TRACE_WARNING ("L2CA_CancelBleConnectReq - different BDA Connecting: %08x%04x Cancel: %08x%04x", + (l2cb.ble_connecting_bda[0] << 24) + (l2cb.ble_connecting_bda[1] << 16) + (l2cb.ble_connecting_bda[2] << 8) + l2cb.ble_connecting_bda[3], + (l2cb.ble_connecting_bda[4] << 8) + l2cb.ble_connecting_bda[5], + (rem_bda[0] << 24) + (rem_bda[1] << 16) + (rem_bda[2] << 8) + rem_bda[3], (rem_bda[4] << 8) + rem_bda[5]); + + return (FALSE); + } + + if (btsnd_hcic_ble_create_conn_cancel()) { + p_lcb = l2cu_find_lcb_by_bd_addr(rem_bda, BT_TRANSPORT_LE); + /* Do not remove lcb if an LE link is already up as a peripheral */ + if (p_lcb != NULL && + !(p_lcb->link_role == HCI_ROLE_SLAVE && BTM_ACL_IS_CONNECTED(rem_bda))) { + p_lcb->disc_reason = L2CAP_CONN_CANCEL; + l2cu_release_lcb (p_lcb); + } + /* update state to be cancel, wait for connection cancel complete */ + btm_ble_set_conn_st (BLE_CONN_CANCEL); + + return (TRUE); + } else { + return (FALSE); + } +} + +/******************************************************************************* +** +** Function L2CA_UpdateBleConnParams +** +** Description Update BLE connection parameters. +** +** Parameters: BD Address of remote +** +** Return value: TRUE if update started +** +*******************************************************************************/ +BOOLEAN L2CA_UpdateBleConnParams (BD_ADDR rem_bda, UINT16 min_int, UINT16 max_int, + UINT16 latency, UINT16 timeout) +{ + tL2C_LCB *p_lcb; + tACL_CONN *p_acl_cb = btm_bda_to_acl(rem_bda, BT_TRANSPORT_LE); + UINT8 status = HCI_SUCCESS; + BOOLEAN need_cb = false; + + /* See if we have a link control block for the remote device */ + p_lcb = l2cu_find_lcb_by_bd_addr (rem_bda, BT_TRANSPORT_LE); + + /* If we don't have one, create one and accept the connection. */ + if (!p_lcb || !p_acl_cb) { + L2CAP_TRACE_WARNING ("L2CA_UpdateBleConnParams - unknown BD_ADDR %08x%04x", + (rem_bda[0] << 24) + (rem_bda[1] << 16) + (rem_bda[2] << 8) + rem_bda[3], + (rem_bda[4] << 8) + rem_bda[5]); + return (FALSE); + } + + if (p_lcb->transport != BT_TRANSPORT_LE) { + L2CAP_TRACE_WARNING ("L2CA_UpdateBleConnParams - BD_ADDR %08x%04x not LE", + (rem_bda[0] << 24) + (rem_bda[1] << 16) + (rem_bda[2] << 8) + rem_bda[3], + (rem_bda[4] << 8) + rem_bda[5]); + return (FALSE); + } + + /* Check whether the request conn params is already set */ + if ((max_int == p_lcb->current_used_conn_interval) && (latency == p_lcb->current_used_conn_latency) && + (timeout == p_lcb->current_used_conn_timeout)) { + status = HCI_SUCCESS; + need_cb = true; + L2CAP_TRACE_WARNING("%s connection parameter already set", __func__); + } + + if (p_lcb->conn_update_mask & L2C_BLE_UPDATE_PARAM_FULL){ + status = HCI_ERR_ILLEGAL_COMMAND; + need_cb = true; + L2CAP_TRACE_ERROR("There are two connection parameter requests that are being updated, please try later "); + } + + if ((need_cb == TRUE) && (conn_param_update_cb.update_conn_param_cb != NULL)) { + tBTM_LE_UPDATE_CONN_PRAMS update_param; + update_param.max_conn_int = max_int; + update_param.min_conn_int = min_int; + update_param.conn_int = p_lcb->current_used_conn_interval; + update_param.slave_latency = p_lcb->current_used_conn_latency; + update_param.supervision_tout = p_lcb->current_used_conn_timeout; + (conn_param_update_cb.update_conn_param_cb)(status, p_lcb->remote_bd_addr, &update_param); + return (status == HCI_SUCCESS); + } + + p_lcb->waiting_update_conn_min_interval = min_int; + p_lcb->waiting_update_conn_max_interval = max_int; + p_lcb->waiting_update_conn_latency = latency; + p_lcb->waiting_update_conn_timeout = timeout; + + p_lcb->conn_update_mask |= L2C_BLE_NEW_CONN_PARAM; + + if(l2cble_start_conn_update(p_lcb) == TRUE) { + UINT32 time = CalConnectParamTimeout(p_lcb); + btu_start_timer(&p_lcb->upda_con_timer, BTU_TTYPE_L2CAP_UPDA_CONN_PARAMS, time); + } + + return (TRUE); +} + + +/******************************************************************************* +** +** Function L2CA_EnableUpdateBleConnParams +** +** Description Enable or disable update based on the request from the peer +** +** Parameters: BD Address of remote +** +** Return value: TRUE if update started +** +*******************************************************************************/ +BOOLEAN L2CA_EnableUpdateBleConnParams (BD_ADDR rem_bda, BOOLEAN enable) +{ + tL2C_LCB *p_lcb; + + /* See if we have a link control block for the remote device */ + p_lcb = l2cu_find_lcb_by_bd_addr (rem_bda, BT_TRANSPORT_LE); + + if (!p_lcb) { + L2CAP_TRACE_WARNING ("L2CA_EnableUpdateBleConnParams - unknown BD_ADDR %08x%04x", + (rem_bda[0] << 24) + (rem_bda[1] << 16) + (rem_bda[2] << 8) + rem_bda[3], + (rem_bda[4] << 8) + rem_bda[5]); + return (FALSE); + } + + L2CAP_TRACE_API ("%s - BD_ADDR %08x%04x enable %d current upd state 0x%02x", __FUNCTION__, + (rem_bda[0] << 24) + (rem_bda[1] << 16) + (rem_bda[2] << 8) + rem_bda[3], + (rem_bda[4] << 8) + rem_bda[5], enable, p_lcb->conn_update_mask); + + if (p_lcb->transport != BT_TRANSPORT_LE) { + L2CAP_TRACE_WARNING ("%s - BD_ADDR %08x%04x not LE (link role %d)", __FUNCTION__, + (rem_bda[0] << 24) + (rem_bda[1] << 16) + (rem_bda[2] << 8) + rem_bda[3], + (rem_bda[4] << 8) + rem_bda[5], p_lcb->link_role); + return (FALSE); + } + + if (p_lcb->current_used_conn_interval <= BTM_BLE_CONN_INT_MAX_DEF && (p_lcb->conn_update_mask & L2C_BLE_CONN_UPDATE_DISABLE) == 0){ + return (FALSE); + } + bool is_disable = (p_lcb->conn_update_mask & L2C_BLE_CONN_UPDATE_DISABLE); + if(l2cu_plcb_active_count() >1 && !(enable && is_disable)) { + return FALSE; + } + + if (enable) { + p_lcb->conn_update_mask &= ~L2C_BLE_CONN_UPDATE_DISABLE; + } else { + p_lcb->conn_update_mask |= L2C_BLE_CONN_UPDATE_DISABLE; + } + + if (l2cble_start_conn_update(p_lcb) == TRUE) { + UINT32 time = CalConnectParamTimeout(p_lcb); + btu_start_timer(&p_lcb->upda_con_timer, BTU_TTYPE_L2CAP_UPDA_CONN_PARAMS, time); + } + + return (TRUE); +} + + +/******************************************************************************* +** +** Function L2CA_GetBleConnRole +** +** Description This function returns the connection role. +** +** Returns link role. +** +*******************************************************************************/ +UINT8 L2CA_GetBleConnRole (BD_ADDR bd_addr) +{ + UINT8 role = HCI_ROLE_UNKNOWN; + + tL2C_LCB *p_lcb; + + if ((p_lcb = l2cu_find_lcb_by_bd_addr (bd_addr, BT_TRANSPORT_LE)) != NULL) { + role = p_lcb->link_role; + } + + return role; +} + +/******************************************************************************* +** +** Function l2cble_notify_le_connection +** +** Description This function notifiy the l2cap connection to the app layer +** +** Returns none +** +*******************************************************************************/ +void l2cble_notify_le_connection (BD_ADDR bda) +{ + tL2C_LCB *p_lcb = l2cu_find_lcb_by_bd_addr (bda, BT_TRANSPORT_LE); + tACL_CONN *p_acl = btm_bda_to_acl(bda, BT_TRANSPORT_LE) ; + + if (p_lcb != NULL && p_acl != NULL && p_lcb->link_state != LST_CONNECTED) { + + if(p_acl->link_role == HCI_ROLE_SLAVE) { + //clear p_cb->state, controller will stop adv when ble connected. + tBTM_BLE_INQ_CB *p_cb = &btm_cb.ble_ctr_cb.inq_var; + if(p_cb) { + p_cb->adv_mode = BTM_BLE_ADV_DISABLE; + p_cb->state &= ~BTM_BLE_ADVERTISING; + } + } + /* update link status */ + btm_establish_continue(p_acl); + /* update l2cap link status and send callback */ + p_lcb->link_state = LST_CONNECTED; + l2cu_process_fixed_chnl_resp (p_lcb); + } +} + +/******************************************************************************* +** +** Function l2cble_scanner_conn_comp +** +** Description This function is called when an HCI Connection Complete +** event is received while we are a scanner (so we are master). +** +** Returns void +** +*******************************************************************************/ +void l2cble_scanner_conn_comp (UINT16 handle, BD_ADDR bda, tBLE_ADDR_TYPE type, + UINT16 conn_interval, UINT16 conn_latency, UINT16 conn_timeout) +{ + tL2C_LCB *p_lcb; + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_or_alloc_dev (bda); + + L2CAP_TRACE_DEBUG ("l2cble_scanner_conn_comp: HANDLE=%d addr_type=%d conn_interval=%d slave_latency=%d supervision_tout=%d", + handle, type, conn_interval, conn_latency, conn_timeout); + + l2cb.is_ble_connecting = FALSE; + + /* See if we have a link control block for the remote device */ + p_lcb = l2cu_find_lcb_by_bd_addr (bda, BT_TRANSPORT_LE); + + /* If we don't have one, create one. this is auto connection complete. */ + if (!p_lcb) { + p_lcb = l2cu_allocate_lcb (bda, FALSE, BT_TRANSPORT_LE); + if (!p_lcb) { +#if (SMP_INCLUDED == TRUE) + btm_sec_disconnect (handle, HCI_ERR_NO_CONNECTION); + L2CAP_TRACE_ERROR ("l2cble_scanner_conn_comp - failed to allocate LCB"); +#endif ///SMP_INCLUDED == TRUE + return; + } else { + if (!l2cu_initialize_fixed_ccb (p_lcb, L2CAP_ATT_CID, &l2cb.fixed_reg[L2CAP_ATT_CID - L2CAP_FIRST_FIXED_CHNL].fixed_chnl_opts)) { +#if (SMP_INCLUDED == TRUE) + btm_sec_disconnect (handle, HCI_ERR_NO_CONNECTION); + L2CAP_TRACE_WARNING ("l2cble_scanner_conn_comp - LCB but no CCB"); +#endif ///SMP_INCLUDED == TRUE + return ; + } + } + } else if (p_lcb->link_state != LST_CONNECTING) { + L2CAP_TRACE_ERROR ("L2CAP got BLE scanner conn_comp in bad state: %d", p_lcb->link_state); + return; + } + btu_stop_timer(&p_lcb->timer_entry); + + /* Save the handle */ + p_lcb->handle = handle; + + /* Connected OK. Change state to connected, we were scanning so we are master */ + p_lcb->link_role = HCI_ROLE_MASTER; + p_lcb->transport = BT_TRANSPORT_LE; + + /* update link parameter, set slave link as non-spec default upon link up */ + p_lcb->waiting_update_conn_min_interval = p_lcb->waiting_update_conn_max_interval = p_lcb->current_used_conn_interval = conn_interval; + p_lcb->waiting_update_conn_timeout = p_lcb->current_used_conn_timeout = conn_timeout; + p_lcb->waiting_update_conn_latency = p_lcb->current_used_conn_latency = conn_latency; + p_lcb->conn_update_mask = L2C_BLE_NOT_DEFAULT_PARAM; + p_lcb->updating_param_flag = false; + + /* If there are any preferred connection parameters, set them now */ + if ( (p_dev_rec->conn_params.min_conn_int >= BTM_BLE_CONN_INT_MIN ) && + (p_dev_rec->conn_params.min_conn_int <= BTM_BLE_CONN_INT_MAX ) && + (p_dev_rec->conn_params.max_conn_int >= BTM_BLE_CONN_INT_MIN ) && + (p_dev_rec->conn_params.max_conn_int <= BTM_BLE_CONN_INT_MAX ) && + (p_dev_rec->conn_params.slave_latency <= BTM_BLE_CONN_LATENCY_MAX ) && + (p_dev_rec->conn_params.supervision_tout >= BTM_BLE_CONN_SUP_TOUT_MIN) && + (p_dev_rec->conn_params.supervision_tout <= BTM_BLE_CONN_SUP_TOUT_MAX) && + ((conn_interval < p_dev_rec->conn_params.min_conn_int && + p_dev_rec->conn_params.min_conn_int != BTM_BLE_CONN_PARAM_UNDEF) || + (conn_interval > p_dev_rec->conn_params.max_conn_int) || + (conn_latency > p_dev_rec->conn_params.slave_latency) || + (conn_timeout > p_dev_rec->conn_params.supervision_tout))) { + L2CAP_TRACE_ERROR ("upd_ll_conn_params: HANDLE=%d min_conn_int=%d max_conn_int=%d slave_latency=%d supervision_tout=%d", + handle, p_dev_rec->conn_params.min_conn_int, p_dev_rec->conn_params.max_conn_int, + p_dev_rec->conn_params.slave_latency, p_dev_rec->conn_params.supervision_tout); + + p_lcb->waiting_update_conn_min_interval = p_dev_rec->conn_params.min_conn_int; + p_lcb->waiting_update_conn_max_interval = p_dev_rec->conn_params.max_conn_int; + p_lcb->waiting_update_conn_timeout = p_dev_rec->conn_params.supervision_tout; + p_lcb->waiting_update_conn_latency = p_dev_rec->conn_params.slave_latency; + + btsnd_hcic_ble_upd_ll_conn_params (handle, + p_dev_rec->conn_params.min_conn_int, + p_dev_rec->conn_params.max_conn_int, + p_dev_rec->conn_params.slave_latency, + p_dev_rec->conn_params.supervision_tout, + BLE_CE_LEN_MIN, BLE_CE_LEN_MIN); + } + + /* Tell BTM Acl management about the link */ + btm_acl_created (bda, NULL, p_dev_rec->sec_bd_name, handle, p_lcb->link_role, BT_TRANSPORT_LE); + + p_lcb->peer_chnl_mask[0] = L2CAP_FIXED_CHNL_ATT_BIT | L2CAP_FIXED_CHNL_BLE_SIG_BIT | L2CAP_FIXED_CHNL_SMP_BIT; + + btm_ble_set_conn_st(BLE_CONN_IDLE); + +#if BLE_PRIVACY_SPT == TRUE + btm_ble_disable_resolving_list(BTM_BLE_RL_INIT, TRUE); +#endif +} + + +/******************************************************************************* +** +** Function l2cble_advertiser_conn_comp +** +** Description This function is called when an HCI Connection Complete +** event is received while we are an advertiser (so we are slave). +** +** Returns void +** +*******************************************************************************/ +void l2cble_advertiser_conn_comp (UINT16 handle, BD_ADDR bda, tBLE_ADDR_TYPE type, + UINT16 conn_interval, UINT16 conn_latency, UINT16 conn_timeout) +{ + tL2C_LCB *p_lcb; + tBTM_SEC_DEV_REC *p_dev_rec; + UNUSED(type); + UNUSED(conn_interval); + UNUSED(conn_latency); + UNUSED(conn_timeout); + + /* See if we have a link control block for the remote device */ + p_lcb = l2cu_find_lcb_by_bd_addr (bda, BT_TRANSPORT_LE); + + /* If we don't have one, create one and accept the connection. */ + if (!p_lcb) { + p_lcb = l2cu_allocate_lcb (bda, FALSE, BT_TRANSPORT_LE); + if (!p_lcb) { +#if (SMP_INCLUDED == TRUE) + btm_sec_disconnect (handle, HCI_ERR_NO_CONNECTION); +#endif ///SMP_INCLUDED == TRUE + L2CAP_TRACE_ERROR ("l2cble_advertiser_conn_comp - failed to allocate LCB"); + return; + } else { + if (!l2cu_initialize_fixed_ccb (p_lcb, L2CAP_ATT_CID, &l2cb.fixed_reg[L2CAP_ATT_CID - L2CAP_FIRST_FIXED_CHNL].fixed_chnl_opts)) { +#if (SMP_INCLUDED == TRUE) + btm_sec_disconnect (handle, HCI_ERR_NO_CONNECTION); +#endif ///SMP_INCLUDED == TRUE + L2CAP_TRACE_WARNING ("l2cble_scanner_conn_comp - LCB but no CCB"); + return ; + } + } + } + + /* Save the handle */ + p_lcb->handle = handle; + + /* Connected OK. Change state to connected, we were advertising, so we are slave */ + p_lcb->link_role = HCI_ROLE_SLAVE; + p_lcb->transport = BT_TRANSPORT_LE; + + /* update link parameter, set slave link as non-spec default upon link up */ + p_lcb->waiting_update_conn_min_interval = p_lcb->waiting_update_conn_max_interval = p_lcb->current_used_conn_interval = conn_interval; + p_lcb->waiting_update_conn_timeout = p_lcb->current_used_conn_timeout = conn_timeout; + p_lcb->waiting_update_conn_latency = p_lcb->current_used_conn_latency = conn_latency; + p_lcb->conn_update_mask = L2C_BLE_NOT_DEFAULT_PARAM; + p_lcb->updating_param_flag = false; + + /* Tell BTM Acl management about the link */ + p_dev_rec = btm_find_or_alloc_dev (bda); + + btm_acl_created (bda, NULL, p_dev_rec->sec_bd_name, handle, p_lcb->link_role, BT_TRANSPORT_LE); + +#if BLE_PRIVACY_SPT == TRUE + btm_ble_disable_resolving_list(BTM_BLE_RL_ADV, TRUE); +#endif + + p_lcb->peer_chnl_mask[0] = L2CAP_FIXED_CHNL_ATT_BIT | L2CAP_FIXED_CHNL_BLE_SIG_BIT | L2CAP_FIXED_CHNL_SMP_BIT; + + if (!HCI_LE_SLAVE_INIT_FEAT_EXC_SUPPORTED(controller_get_interface()->get_features_ble()->as_array)) { + p_lcb->link_state = LST_CONNECTED; + l2cu_process_fixed_chnl_resp (p_lcb); + } + + /* when adv and initiating are both active, cancel the direct connection */ + if (l2cb.is_ble_connecting && memcmp(bda, l2cb.ble_connecting_bda, BD_ADDR_LEN) == 0) { + L2CA_CancelBleConnectReq(bda); + } +} + +/******************************************************************************* +** +** Function l2cble_conn_comp +** +** Description This function is called when an HCI Connection Complete +** event is received. +** +** Returns void +** +*******************************************************************************/ +void l2cble_conn_comp(UINT16 handle, UINT8 role, BD_ADDR bda, tBLE_ADDR_TYPE type, + UINT16 conn_interval, UINT16 conn_latency, UINT16 conn_timeout) +{ + btm_ble_update_link_topology_mask(role, TRUE); + + if (role == HCI_ROLE_MASTER) { + l2cble_scanner_conn_comp(handle, bda, type, conn_interval, conn_latency, conn_timeout); + } else { + l2cble_advertiser_conn_comp(handle, bda, type, conn_interval, conn_latency, conn_timeout); + } +} + +/******************************************************************************* +** +** Function l2cble_start_conn_update +** +** Description start BLE connection parameter update process based on status +** +** Parameters: lcb : l2cap link control block +** +** Return value: true if successfully sending the request to peer device, else false. +** +*******************************************************************************/ +static BOOLEAN l2cble_start_conn_update (tL2C_LCB *p_lcb) +{ + UINT16 min_conn_int, max_conn_int, slave_latency, supervision_tout; +#if (defined BLE_LLT_INCLUDED) && (BLE_LLT_INCLUDED == TRUE) && (BLE_SLAVE_UPD_CONN_PARAMS == TRUE) + tACL_CONN *p_acl_cb = btm_bda_to_acl(p_lcb->remote_bd_addr, BT_TRANSPORT_LE); +#endif /* defined BLE_LLT_INCLUDED) && (BLE_LLT_INCLUDED == TRUE) && (BLE_SLAVE_UPD_CONN_PARAMS == TRUE */ + + if (p_lcb->conn_update_mask & L2C_BLE_UPDATE_PENDING) { + L2CAP_TRACE_WARNING("%s, the last connection update command still pending.", __func__); + p_lcb->conn_update_mask |= L2C_BLE_UPDATE_PARAM_FULL; + return FALSE; + } + + if (p_lcb->conn_update_mask & L2C_BLE_CONN_UPDATE_DISABLE) { + /* application requests to disable parameters update. + If parameters are already updated, lets set them + up to what has been requested during connection establishement */ + if (p_lcb->conn_update_mask & L2C_BLE_NOT_DEFAULT_PARAM && + /* current connection interval is greater than default min */ + p_lcb->current_used_conn_interval > BTM_BLE_CONN_INT_MAX_DEF) { + /* use 6 * 1.25 = 7.5 ms as fast connection parameter, 0 slave latency */ + min_conn_int = max_conn_int = BTM_BLE_CONN_INT_MIN; + slave_latency = BTM_BLE_CONN_SLAVE_LATENCY_DEF; + supervision_tout = BTM_BLE_CONN_TIMEOUT_DEF; + + /* if both side 4.1, or we are master device, send HCI command */ + if (p_lcb->link_role == HCI_ROLE_MASTER +#if (defined BLE_LLT_INCLUDED) && (BLE_LLT_INCLUDED == TRUE) && (BLE_SLAVE_UPD_CONN_PARAMS == TRUE) + || (HCI_LE_CONN_PARAM_REQ_SUPPORTED(controller_get_interface()->get_features_ble()->as_array) && + HCI_LE_CONN_PARAM_REQ_SUPPORTED(p_acl_cb->peer_le_features)) +#endif + ) { + btsnd_hcic_ble_upd_ll_conn_params(p_lcb->handle, min_conn_int, max_conn_int, + slave_latency, supervision_tout, BLE_CE_LEN_MIN, BLE_CE_LEN_MIN); + } else { + l2cu_send_peer_ble_par_req (p_lcb, min_conn_int, max_conn_int, slave_latency, supervision_tout); + } + + //cache save + p_lcb->updating_conn_min_interval = min_conn_int; + p_lcb->updating_conn_max_interval = max_conn_int; + p_lcb->updating_param_flag = true; + + p_lcb->conn_update_mask |= L2C_BLE_UPDATE_PENDING; + p_lcb->conn_update_mask &= ~L2C_BLE_NOT_DEFAULT_PARAM; + p_lcb->conn_update_mask |= L2C_BLE_NEW_CONN_PARAM; + return TRUE; + }else { + return FALSE; + } + } else { + /* application allows to do update, if we were delaying one do it now */ + if (p_lcb->conn_update_mask & L2C_BLE_NEW_CONN_PARAM) { + /* if both side 4.1, or we are master device, send HCI command */ + if (p_lcb->link_role == HCI_ROLE_MASTER +#if (defined BLE_LLT_INCLUDED) && (BLE_LLT_INCLUDED == TRUE) && (BLE_SLAVE_UPD_CONN_PARAMS == TRUE) + || (HCI_LE_CONN_PARAM_REQ_SUPPORTED(controller_get_interface()->get_features_ble()->as_array) && + HCI_LE_CONN_PARAM_REQ_SUPPORTED(p_acl_cb->peer_le_features)) +#endif + ) { + btsnd_hcic_ble_upd_ll_conn_params(p_lcb->handle, p_lcb->waiting_update_conn_min_interval, + p_lcb->waiting_update_conn_max_interval, p_lcb->waiting_update_conn_latency, p_lcb->waiting_update_conn_timeout, BLE_CE_LEN_MIN, BLE_CE_LEN_MIN); + } else { + l2cu_send_peer_ble_par_req (p_lcb, p_lcb->waiting_update_conn_min_interval, p_lcb->waiting_update_conn_max_interval, + p_lcb->waiting_update_conn_latency, p_lcb->waiting_update_conn_timeout); + } + + //cache save + p_lcb->updating_conn_min_interval = p_lcb->waiting_update_conn_min_interval; + p_lcb->updating_conn_max_interval = p_lcb->waiting_update_conn_max_interval; + p_lcb->updating_param_flag = true; + + p_lcb->conn_update_mask |= L2C_BLE_UPDATE_PENDING; + p_lcb->conn_update_mask &= ~L2C_BLE_NEW_CONN_PARAM; + p_lcb->conn_update_mask |= L2C_BLE_NOT_DEFAULT_PARAM; + return TRUE; + } else { + return FALSE; + } + } +} + +/******************************************************************************* +** +** Function l2cble_process_conn_update_evt +** +** Description This function enables the connection update request from remote +** after a successful connection update response is received. +** +** Returns void +** +*******************************************************************************/ +void l2cble_process_conn_update_evt (UINT16 handle, UINT8 status, UINT16 conn_interval, + UINT16 conn_latency, UINT16 conn_timeout) +{ + tL2C_LCB *p_lcb; + + /* See if we have a link control block for the remote device */ + p_lcb = l2cu_find_lcb_by_handle(handle); + if (!p_lcb) { + L2CAP_TRACE_WARNING("le con upd: inv hdl=%d", handle); + return; + } + if (status == HCI_SUCCESS){ + p_lcb->current_used_conn_interval = conn_interval; + p_lcb->current_used_conn_latency = conn_latency; + p_lcb->current_used_conn_timeout = conn_timeout; + }else{ + L2CAP_TRACE_WARNING("le con upd: err_stat=0x%x", status); + } + + p_lcb->conn_update_mask &= ~L2C_BLE_UPDATE_PENDING; + p_lcb->conn_update_mask &= ~L2C_BLE_UPDATE_PARAM_FULL; + btu_stop_timer(&p_lcb->upda_con_timer); + + if (conn_param_update_cb.update_conn_param_cb != NULL) { + l2c_send_update_conn_params_cb(p_lcb, status); + } + + if (l2cble_start_conn_update(p_lcb) == TRUE) { + UINT32 time = CalConnectParamTimeout(p_lcb); + btu_start_timer(&p_lcb->upda_con_timer, BTU_TTYPE_L2CAP_UPDA_CONN_PARAMS, time); + } + + btu_stop_timer (&p_lcb->timer_entry); + + L2CAP_TRACE_DEBUG("le con upd: conn_update_mask=%d", p_lcb->conn_update_mask); +} + +/******************************************************************************* +** +** Function l2cble_get_conn_param_format_err_from_contoller +** +** Description This function is called when host get illegal connection paramrters +** format status from controller +** +** Returns void +** +*******************************************************************************/ +void l2cble_get_conn_param_format_err_from_contoller (UINT8 status, UINT16 handle) +{ + tL2C_LCB *p_lcb; + + /* See if we have a link control block for the remote device */ + p_lcb = l2cu_find_lcb_by_handle(handle); + if (!p_lcb) { + L2CAP_TRACE_ERROR("%s: Invalid handle: %d", __FUNCTION__, handle); + return; + } + + p_lcb->conn_update_mask &= ~L2C_BLE_UPDATE_PENDING; + + btu_stop_timer (&p_lcb->upda_con_timer); + + if (conn_param_update_cb.update_conn_param_cb != NULL) { + l2c_send_update_conn_params_cb(p_lcb, status); + } + if ((p_lcb->conn_update_mask & L2C_BLE_UPDATE_PARAM_FULL) != 0){ + p_lcb->conn_update_mask &= ~L2C_BLE_UPDATE_PARAM_FULL; + if (l2cble_start_conn_update(p_lcb) == TRUE) { + UINT32 time = CalConnectParamTimeout(p_lcb); + btu_start_timer(&p_lcb->upda_con_timer, BTU_TTYPE_L2CAP_UPDA_CONN_PARAMS, time); + } + } + +} + +/******************************************************************************* +** +** Function l2cble_process_sig_cmd +** +** Description This function is called when a signalling packet is received +** on the BLE signalling CID +** +** Returns void +** +*******************************************************************************/ +void l2cble_process_sig_cmd (tL2C_LCB *p_lcb, UINT8 *p, UINT16 pkt_len) +{ + UINT8 *p_pkt_end; + UINT8 cmd_code, id; + UINT16 cmd_len; + UINT16 min_interval, max_interval, latency, timeout; + + p_pkt_end = p + pkt_len; + + STREAM_TO_UINT8 (cmd_code, p); + STREAM_TO_UINT8 (id, p); + STREAM_TO_UINT16 (cmd_len, p); + + /* Check command length does not exceed packet length */ + if ((p + cmd_len) > p_pkt_end) { + L2CAP_TRACE_WARNING ("L2CAP - LE - format error, pkt_len: %d cmd_len: %d code: %d", pkt_len, cmd_len, cmd_code); + return; + } + + switch (cmd_code) { + case L2CAP_CMD_REJECT: + case L2CAP_CMD_ECHO_RSP: + case L2CAP_CMD_INFO_RSP: + p += 2; + break; + case L2CAP_CMD_ECHO_REQ: + case L2CAP_CMD_INFO_REQ: + l2cu_send_peer_cmd_reject (p_lcb, L2CAP_CMD_REJ_NOT_UNDERSTOOD, id, 0, 0); + break; + + case L2CAP_CMD_BLE_UPDATE_REQ: + STREAM_TO_UINT16 (min_interval, p); /* 0x0006 - 0x0C80 */ + STREAM_TO_UINT16 (max_interval, p); /* 0x0006 - 0x0C80 */ + STREAM_TO_UINT16 (latency, p); /* 0x0000 - 0x03E8 */ + STREAM_TO_UINT16 (timeout, p); /* 0x000A - 0x0C80 */ + /* If we are a master, the slave wants to update the parameters */ + if (p_lcb->link_role == HCI_ROLE_MASTER) { + if (min_interval < BTM_BLE_CONN_INT_MIN || min_interval > BTM_BLE_CONN_INT_MAX || + max_interval < BTM_BLE_CONN_INT_MIN || max_interval > BTM_BLE_CONN_INT_MAX || + latency > BTM_BLE_CONN_LATENCY_MAX || + timeout < BTM_BLE_CONN_SUP_TOUT_MIN || timeout > BTM_BLE_CONN_SUP_TOUT_MAX || + /* The supervision_timeout parameter defines the link supervision timeout for the connection. + The supervision_timeout in milliseconds shall be large than (1 + latency) * max_interval * 2, + where max_interval is given in milliseconds. (See [Vol 6] Part B, Section 4.5.2). + supervision_timeout (mult of 10ms); conn_interval (mult of 1.25ms) + (max_interval * 1.25 * 2) replaced by ((max_interval * 5) >> 1). + */ + ((timeout * 10) < ((1 + latency) *((max_interval * 5) >> 1))) || + max_interval < min_interval) { + l2cu_send_peer_ble_par_rsp (p_lcb, L2CAP_CFG_UNACCEPTABLE_PARAMS, id); + + L2CAP_TRACE_ERROR("slave connection parameters update failed, the parameters are out of range"); + + } else { + + l2cu_send_peer_ble_par_rsp (p_lcb, L2CAP_CFG_OK, id); + p_lcb->waiting_update_conn_min_interval = min_interval; + p_lcb->waiting_update_conn_max_interval = max_interval; + p_lcb->waiting_update_conn_latency = latency; + p_lcb->waiting_update_conn_timeout = timeout; + p_lcb->conn_update_mask |= L2C_BLE_NEW_CONN_PARAM; + + if (l2cble_start_conn_update(p_lcb) == TRUE) { + UINT32 time = CalConnectParamTimeout(p_lcb); + btu_start_timer(&p_lcb->upda_con_timer, BTU_TTYPE_L2CAP_UPDA_CONN_PARAMS, time); + } + } + } else { + l2cu_send_peer_cmd_reject (p_lcb, L2CAP_CMD_REJ_NOT_UNDERSTOOD, id, 0, 0); + } + break; + + case L2CAP_CMD_BLE_UPDATE_RSP: { + UINT16 result = 0; + STREAM_TO_UINT16(result, p); //result = 0 connection param accepted, result = 1 connection param rejected. + UINT8 status = (result == 0) ? HCI_SUCCESS : HCI_ERR_PARAM_OUT_OF_RANGE; + if (status != HCI_SUCCESS) { + btu_stop_timer(&p_lcb->upda_con_timer); + p_lcb->conn_update_mask &= ~L2C_BLE_UPDATE_PENDING; + p_lcb->conn_update_mask &= ~L2C_BLE_UPDATE_PARAM_FULL; + l2c_send_update_conn_params_cb(p_lcb, status); + } + break; + } + case L2CAP_CMD_BLE_CREDIT_BASED_CONN_REQ: { + tL2C_CCB *p_ccb = NULL; + tL2C_RCB *p_rcb = NULL; + UINT16 spsm; + UINT16 scid; + UINT16 mtu; + UINT16 mps; + UINT16 credits; + STREAM_TO_UINT16(spsm, p); + STREAM_TO_UINT16(scid, p); + STREAM_TO_UINT16(mtu, p); + STREAM_TO_UINT16(mps, p); + STREAM_TO_UINT16(credits, p); + L2CAP_TRACE_DEBUG("%s spsm %x, scid %x", __func__, spsm, scid); + UNUSED(spsm); + + p_ccb = l2cu_find_ccb_by_remote_cid(p_lcb, scid); + if (p_ccb) { + l2cu_reject_ble_connection(p_lcb, id, L2CAP_LE_RESULT_SOURCE_CID_ALREADY_ALLOCATED); + break; + } + + #if 0 + p_rcb = l2cu_find_ble_rcb_by_psm(spsm); + if (p_rcb == NULL) { + break; + } + #endif + + p_ccb = l2cu_allocate_ccb(p_lcb, 0); + if (p_ccb == NULL) { + l2cu_reject_ble_connection(p_lcb, id, L2CAP_LE_RESULT_NO_RESOURCES); + break; + } + + p_ccb->remote_id = id; + p_ccb->p_rcb = p_rcb; + p_ccb->remote_cid = scid; + p_ccb->local_conn_cfg.mtu = mtu; + p_ccb->local_conn_cfg.mps = controller_get_interface()->get_acl_data_size_ble(); + p_ccb->local_conn_cfg.credits = credits; + p_ccb->peer_conn_cfg.mtu = mtu; + p_ccb->peer_conn_cfg.mps = mps; + p_ccb->peer_conn_cfg.credits = credits; + + l2cu_send_peer_ble_credit_based_conn_res(p_ccb, L2CAP_LE_RESULT_CONN_OK); + break; + } + case L2CAP_CMD_DISC_REQ: { + tL2C_CCB *p_ccb = NULL; + UINT16 lcid; + UINT16 rcid; + STREAM_TO_UINT16(lcid, p); + STREAM_TO_UINT16(rcid, p); + + p_ccb = l2cu_find_ccb_by_cid(p_lcb, lcid); + if (p_ccb) { + p_ccb->remote_id = id; + // TODO + } + + l2cu_send_peer_disc_rsp(p_lcb, id, lcid, rcid); + break; + } + default: + L2CAP_TRACE_WARNING ("L2CAP - LE - unknown cmd code: %d", cmd_code); + l2cu_send_peer_cmd_reject (p_lcb, L2CAP_CMD_REJ_NOT_UNDERSTOOD, id, 0, 0); + return; + } +} + +/******************************************************************************* +** +** Function l2cble_init_direct_conn +** +** Description This function is to initate a direct connection +** +** Returns TRUE connection initiated, FALSE otherwise. +** +*******************************************************************************/ +BOOLEAN l2cble_init_direct_conn (tL2C_LCB *p_lcb) +{ +#if ( (defined BLE_PRIVACY_SPT) && (BLE_PRIVACY_SPT == TRUE) && (!CONTROLLER_RPA_LIST_ENABLE)) + //check for security device information in the cache + bool dev_rec_exist = true; + tBTM_SEC_DEV_REC *find_dev_rec = btm_find_dev (p_lcb->remote_bd_addr); + if(find_dev_rec == NULL) { + dev_rec_exist = false; + } + +#endif // ( (defined BLE_PRIVACY_SPT) && (BLE_PRIVACY_SPT == TRUE) && (!CONTROLLER_RPA_LIST_ENABLE)) + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_or_alloc_dev (p_lcb->remote_bd_addr); + tBTM_BLE_CB *p_cb = &btm_cb.ble_ctr_cb; + UINT16 scan_int; + UINT16 scan_win; + BD_ADDR peer_addr; + UINT8 peer_addr_type = BLE_ADDR_PUBLIC; + UINT8 own_addr_type = BLE_ADDR_PUBLIC; + + /* There can be only one BLE connection request outstanding at a time */ + if (p_dev_rec == NULL) { + L2CAP_TRACE_WARNING ("unknown device, can not initate connection"); + return (FALSE); + } + + scan_int = (p_cb->scan_int == BTM_BLE_SCAN_PARAM_UNDEF) ? BTM_BLE_SCAN_FAST_INT : p_cb->scan_int; + scan_win = (p_cb->scan_win == BTM_BLE_SCAN_PARAM_UNDEF) ? BTM_BLE_SCAN_FAST_WIN : p_cb->scan_win; + + peer_addr_type = p_lcb->ble_addr_type; + memcpy(peer_addr, p_lcb->remote_bd_addr, BD_ADDR_LEN); + +#if ( (defined BLE_PRIVACY_SPT) && (BLE_PRIVACY_SPT == TRUE)) + own_addr_type = btm_cb.ble_ctr_cb.addr_mgnt_cb.own_addr_type; +#if (!CONTROLLER_RPA_LIST_ENABLE) + if(dev_rec_exist) { + // if the current address information is valid, get the real address information + if(p_dev_rec->ble.current_addr_valid) { + peer_addr_type = p_dev_rec->ble.current_addr_type; + memcpy(peer_addr, p_dev_rec->ble.current_addr, 6); + } else { + /* find security device information but not find the real address information + * This state may be directly open without scanning. In this case, you must + * use the current adv address of the device to open*/ + } + } else { + //not find security device information, We think this is a new device, connect directly + } + + /* It will cause that scanner doesn't send scan request to advertiser + * which has sent IRK to us and we have stored the IRK in controller. + * It is a hardware limitation. The preliminary solution is not to + * send key to the controller, but to resolve the random address in host. + * so we need send the real address information to controller. */ + +#endif // (!CONTROLLER_RPA_LIST_ENABLE) + +#if (CONTROLLER_RPA_LIST_ENABLE) + + if (p_dev_rec->ble.in_controller_list & BTM_RESOLVING_LIST_BIT) { + if (btm_cb.ble_ctr_cb.privacy_mode >= BTM_PRIVACY_1_2) { + own_addr_type |= BLE_ADDR_TYPE_ID_BIT; + } + + //btm_ble_enable_resolving_list(BTM_BLE_RL_INIT); + btm_random_pseudo_to_identity_addr(peer_addr, &peer_addr_type); + } else { + btm_ble_disable_resolving_list(BTM_BLE_RL_INIT, TRUE); + } + +#endif // CONTROLLER_RPA_LIST_ENABLE +#endif // (defined BLE_PRIVACY_SPT) && (BLE_PRIVACY_SPT == TRUE) + + if (!btm_ble_topology_check(BTM_BLE_STATE_INIT)) { + l2cu_release_lcb (p_lcb); + L2CAP_TRACE_ERROR("initate direct connection fail, topology limitation"); + return FALSE; + } + uint32_t link_timeout = L2CAP_BLE_LINK_CONNECT_TOUT; + if(GATTC_CONNECT_RETRY_COUNT) { + if(!p_lcb->retry_create_con) { + p_lcb->start_time_s = (esp_system_get_time()/1000); + } + uint32_t current_time = (esp_system_get_time()/1000); + link_timeout = (L2CAP_BLE_LINK_CONNECT_TOUT*1000 - (current_time - p_lcb->start_time_s))/1000; + + if(link_timeout == 0 || link_timeout > L2CAP_BLE_LINK_CONNECT_TOUT) { + link_timeout = L2CAP_BLE_LINK_CONNECT_TOUT; + } + } + + if (!p_lcb->is_aux) { + if (!btsnd_hcic_ble_create_ll_conn (scan_int,/* UINT16 scan_int */ + scan_win, /* UINT16 scan_win */ + FALSE, /* UINT8 white_list */ + peer_addr_type, /* UINT8 addr_type_peer */ + peer_addr, /* BD_ADDR bda_peer */ + own_addr_type, /* UINT8 addr_type_own */ + (UINT16) ((p_dev_rec->conn_params.min_conn_int != BTM_BLE_CONN_PARAM_UNDEF) ? + p_dev_rec->conn_params.min_conn_int : BTM_BLE_CONN_INT_MIN_DEF), /* UINT16 conn_int_min */ + (UINT16) ((p_dev_rec->conn_params.max_conn_int != BTM_BLE_CONN_PARAM_UNDEF) ? + p_dev_rec->conn_params.max_conn_int : BTM_BLE_CONN_INT_MAX_DEF), /* UINT16 conn_int_max */ + (UINT16) ((p_dev_rec->conn_params.slave_latency != BTM_BLE_CONN_PARAM_UNDEF) ? + p_dev_rec->conn_params.slave_latency : BTM_BLE_CONN_SLAVE_LATENCY_DEF), /* UINT16 conn_latency */ + (UINT16) ((p_dev_rec->conn_params.supervision_tout != BTM_BLE_CONN_PARAM_UNDEF) ? + p_dev_rec->conn_params.supervision_tout : BTM_BLE_CONN_TIMEOUT_DEF), /* conn_timeout */ + BLE_CE_LEN_MIN, /* UINT16 min_len */ + BLE_CE_LEN_MIN)) { /* UINT16 max_len */ + l2cu_release_lcb (p_lcb); + L2CAP_TRACE_ERROR("initate direct connection fail, no resources"); + return (FALSE); + } else { + p_lcb->link_state = LST_CONNECTING; + l2cb.is_ble_connecting = TRUE; + memcpy (l2cb.ble_connecting_bda, p_lcb->remote_bd_addr, BD_ADDR_LEN); + btu_start_timer (&p_lcb->timer_entry, BTU_TTYPE_L2CAP_LINK, link_timeout); + btm_ble_set_conn_st (BLE_DIR_CONN); + + return (TRUE); + } + } else { +#if (BLE_50_FEATURE_SUPPORT == TRUE) + + /* + * 0x00 Public Device Address + * 0x01 Random Device Address + * 0x02 Public Identity Address (corresponds to Resolved Private Address) + * 0x03 Random (static) Identity Address (corresponds to Resolved Private Address) + * 0xFF No address provided (anonymous advertisement) + */ + + if ((peer_addr_type & BLE_ADDR_RANDOM) == BLE_ADDR_RANDOM) { + peer_addr_type = BLE_ADDR_RANDOM; + } else { + peer_addr_type = BLE_ADDR_PUBLIC; + } + + tHCI_CreatExtConn aux_conn = {0}; + aux_conn.filter_policy = FALSE; + aux_conn.own_addr_type = own_addr_type; + aux_conn.peer_addr_type = peer_addr_type; + memcpy(aux_conn.peer_addr, peer_addr, sizeof(BD_ADDR)); + if (p_dev_rec->ext_conn_params.phy_mask == BLE_PHY_NO_PREF) { + L2CAP_TRACE_WARNING("No extend connection parameters set, use default parameters"); + aux_conn.init_phy_mask = BLE_PHY_PREF_MASK; + memcpy(&aux_conn.params[0], &ext_conn_params_1m_phy, sizeof(tHCI_ExtConnParams)); + memcpy(&aux_conn.params[1], &ext_conn_params_2m_phy, sizeof(tHCI_ExtConnParams)); + memcpy(&aux_conn.params[2], &ext_conn_params_coded_phy, sizeof(tHCI_ExtConnParams)); + } else { + aux_conn.init_phy_mask = p_dev_rec->ext_conn_params.phy_mask; + memcpy(&aux_conn.params[0], &p_dev_rec->ext_conn_params.phy_1m_conn_params, sizeof(tHCI_ExtConnParams)); + memcpy(&aux_conn.params[1], &p_dev_rec->ext_conn_params.phy_2m_conn_params, sizeof(tHCI_ExtConnParams)); + memcpy(&aux_conn.params[2], &p_dev_rec->ext_conn_params.phy_coded_conn_params, sizeof(tHCI_ExtConnParams)); + } + p_lcb->link_state = LST_CONNECTING; + l2cb.is_ble_connecting = TRUE; + memcpy (l2cb.ble_connecting_bda, p_lcb->remote_bd_addr, BD_ADDR_LEN); + btu_start_timer (&p_lcb->timer_entry, BTU_TTYPE_L2CAP_LINK, link_timeout); + btm_ble_set_conn_st (BLE_DIR_CONN); + if(!btsnd_hcic_ble_create_ext_conn(&aux_conn)) { + l2cu_release_lcb (p_lcb); + L2CAP_TRACE_ERROR("initate Aux connection failed, no resources"); + } +#else + L2CAP_TRACE_ERROR("BLE 5.0 not support!\n"); +#endif // #if (BLE_50_FEATURE_SUPPORT == TRUE) + return (TRUE); + } +} + +/******************************************************************************* +** +** Function l2cble_create_conn +** +** Description This function initiates an acl connection via HCI +** +** Returns TRUE if successful, FALSE if connection not started. +** +*******************************************************************************/ +BOOLEAN l2cble_create_conn (tL2C_LCB *p_lcb) +{ + tBTM_BLE_CONN_ST conn_st = btm_ble_get_conn_st(); + BOOLEAN rt = FALSE; + + /* There can be only one BLE connection request outstanding at a time */ + if (conn_st == BLE_CONN_IDLE) { + rt = l2cble_init_direct_conn(p_lcb); + } else { + L2CAP_TRACE_WARNING ("L2CAP - LE - cannot start new connection at conn st: %d", conn_st); + + btm_ble_enqueue_direct_conn_req(p_lcb); + + if (conn_st == BLE_BG_CONN) { + btm_ble_suspend_bg_conn(); + } + + rt = TRUE; + } + return rt; +} + +/******************************************************************************* +** +** Function l2c_link_processs_ble_num_bufs +** +** Description This function is called when a "controller buffer size" +** event is first received from the controller. It updates +** the L2CAP values. +** +** Returns void +** +*******************************************************************************/ +void l2c_link_processs_ble_num_bufs (UINT16 num_lm_ble_bufs) +{ + if (num_lm_ble_bufs == 0) { + num_lm_ble_bufs = L2C_DEF_NUM_BLE_BUF_SHARED; + l2cb.num_lm_acl_bufs -= L2C_DEF_NUM_BLE_BUF_SHARED; + } + L2CAP_TRACE_DEBUG("num_lm_ble_bufs = %d",num_lm_ble_bufs); + l2cb.num_lm_ble_bufs = l2cb.controller_le_xmit_window = num_lm_ble_bufs; +} + +/******************************************************************************* +** +** Function l2c_ble_link_adjust_allocation +** +** Description This function is called when a link is created or removed +** to calculate the amount of packets each link may send to +** the HCI without an ack coming back. +** +** Currently, this is a simple allocation, dividing the +** number of Controller Packets by the number of links. In +** the future, QOS configuration should be examined. +** +** Returns void +** +*******************************************************************************/ +void l2c_ble_link_adjust_allocation (void) +{ + UINT16 qq, qq_remainder; + tL2C_LCB *p_lcb; + UINT16 hi_quota, low_quota; + UINT16 num_lowpri_links = 0; + UINT16 num_hipri_links = 0; + UINT16 controller_xmit_quota = l2cb.num_lm_ble_bufs; + UINT16 high_pri_link_quota = L2CAP_HIGH_PRI_MIN_XMIT_QUOTA_A; + list_node_t *p_node = NULL; + + /* If no links active, reset buffer quotas and controller buffers */ + if (l2cb.num_ble_links_active == 0) { + l2cb.controller_le_xmit_window = l2cb.num_lm_ble_bufs; + l2cb.ble_round_robin_quota = l2cb.ble_round_robin_unacked = 0; + return; + } + + /* First, count the links */ + for (p_node = list_begin(l2cb.p_lcb_pool); p_node; p_node = list_next(p_node)) { + p_lcb = list_node(p_node); + if (p_lcb->in_use && p_lcb->transport == BT_TRANSPORT_LE) { + if (p_lcb->acl_priority == L2CAP_PRIORITY_HIGH) { + num_hipri_links++; + } else { + num_lowpri_links++; + } + } + } + + /* now adjust high priority link quota */ + low_quota = num_lowpri_links ? 1 : 0; + while ( (num_hipri_links * high_pri_link_quota + low_quota) > controller_xmit_quota ) { + high_pri_link_quota--; + } + + + /* Work out the xmit quota and buffer quota high and low priorities */ + hi_quota = num_hipri_links * high_pri_link_quota; + low_quota = (hi_quota < controller_xmit_quota) ? controller_xmit_quota - hi_quota : 1; + + /* Work out and save the HCI xmit quota for each low priority link */ + + /* If each low priority link cannot have at least one buffer */ + if (num_lowpri_links > low_quota) { + l2cb.ble_round_robin_quota = low_quota; + qq = qq_remainder = 0; + } + /* If each low priority link can have at least one buffer */ + else if (num_lowpri_links > 0) { + l2cb.ble_round_robin_quota = 0; + l2cb.ble_round_robin_unacked = 0; + qq = low_quota / num_lowpri_links; + qq_remainder = low_quota % num_lowpri_links; + } + /* If no low priority link */ + else { + l2cb.ble_round_robin_quota = 0; + l2cb.ble_round_robin_unacked = 0; + qq = qq_remainder = 0; + } + L2CAP_TRACE_EVENT ("l2c_ble_link_adjust_allocation num_hipri: %u num_lowpri: %u low_quota: %u round_robin_quota: %u qq: %u", + num_hipri_links, num_lowpri_links, low_quota, + l2cb.ble_round_robin_quota, qq); + + /* Now, assign the quotas to each link */ + p_node = NULL; + for (p_node = list_begin(l2cb.p_lcb_pool); p_node; p_node = list_next(p_node)) { + p_lcb = list_node(p_node); + if (p_lcb->in_use && p_lcb->transport == BT_TRANSPORT_LE) { + if (p_lcb->acl_priority == L2CAP_PRIORITY_HIGH) { + p_lcb->link_xmit_quota = high_pri_link_quota; + } else { + /* Safety check in case we switched to round-robin with something outstanding */ + /* if sent_not_acked is added into round_robin_unacked then don't add it again */ + /* l2cap keeps updating sent_not_acked for exiting from round robin */ + if (( p_lcb->link_xmit_quota > 0 ) && ( qq == 0 )) { + l2cb.ble_round_robin_unacked += p_lcb->sent_not_acked; + } + + p_lcb->link_xmit_quota = qq; + if (qq_remainder > 0) { + p_lcb->link_xmit_quota++; + qq_remainder--; + } + } + + L2CAP_TRACE_EVENT("l2c_ble_link_adjust_allocation Priority: %d XmitQuota: %d", + p_lcb->acl_priority, p_lcb->link_xmit_quota); + + L2CAP_TRACE_EVENT(" SentNotAcked: %d RRUnacked: %d", + p_lcb->sent_not_acked, l2cb.round_robin_unacked); + + /* There is a special case where we have readjusted the link quotas and */ + /* this link may have sent anything but some other link sent packets so */ + /* so we may need a timer to kick off this link's transmissions. */ + if ( (p_lcb->link_state == LST_CONNECTED) + && (!list_is_empty(p_lcb->link_xmit_data_q)) + && (p_lcb->sent_not_acked < p_lcb->link_xmit_quota) ) { + btu_start_timer (&p_lcb->timer_entry, BTU_TTYPE_L2CAP_LINK, L2CAP_LINK_FLOW_CONTROL_TOUT); + } + } + } +} + +#if (defined BLE_LLT_INCLUDED) && (BLE_LLT_INCLUDED == TRUE) +/******************************************************************************* +** +** Function l2cble_process_rc_param_request_evt +** +** Description process LE Remote Connection Parameter Request Event. +** +** Returns void +** +*******************************************************************************/ +void l2cble_process_rc_param_request_evt(UINT16 handle, UINT16 int_min, UINT16 int_max, + UINT16 latency, UINT16 timeout) +{ + tL2C_LCB *p_lcb = l2cu_find_lcb_by_handle (handle); + + if (p_lcb != NULL) { + + /* if update is enabled, always accept connection parameter update */ + if ((p_lcb->conn_update_mask & L2C_BLE_CONN_UPDATE_DISABLE) == 0) { + p_lcb->conn_update_mask |= L2C_BLE_UPDATE_PENDING; + btsnd_hcic_ble_rc_param_req_reply(handle, int_min, int_max, latency, timeout, BLE_CE_LEN_MIN, BLE_CE_LEN_MIN); + }else { + /* always accept connection parameters request which is sent by itself */ + if (int_max == BTM_BLE_CONN_INT_MIN) { + p_lcb->conn_update_mask |= L2C_BLE_UPDATE_PENDING; + btsnd_hcic_ble_rc_param_req_reply(handle, int_min, int_max, latency, timeout, BLE_CE_LEN_MIN, BLE_CE_LEN_MIN); + }else { + L2CAP_TRACE_EVENT ("L2CAP - LE - update currently disabled"); + p_lcb->conn_update_mask |= L2C_BLE_NEW_CONN_PARAM; + btsnd_hcic_ble_rc_param_req_neg_reply (handle, HCI_ERR_UNACCEPT_CONN_INTERVAL); + } + } + + } else { + L2CAP_TRACE_WARNING("No link to update connection parameter") + } +} +#endif + +/******************************************************************************* +** +** Function l2cble_update_data_length +** +** Description This function update link tx data length if applicable +** +** Returns void +** +*******************************************************************************/ +void l2cble_update_data_length(tL2C_LCB *p_lcb) +{ + UINT16 tx_mtu = 0; + UINT16 i = 0; + + L2CAP_TRACE_DEBUG("%s", __FUNCTION__); + + /* See if we have a link control block for the connection */ + if (p_lcb == NULL) { + return; + } + + for (i = 0; i < L2CAP_NUM_FIXED_CHNLS; i++) { + if (i + L2CAP_FIRST_FIXED_CHNL != L2CAP_BLE_SIGNALLING_CID) { + if ((p_lcb->p_fixed_ccbs[i] != NULL) && + (tx_mtu < (p_lcb->p_fixed_ccbs[i]->tx_data_len + L2CAP_PKT_OVERHEAD))) { + tx_mtu = p_lcb->p_fixed_ccbs[i]->tx_data_len + L2CAP_PKT_OVERHEAD; + } + } + } + + if (tx_mtu > BTM_BLE_DATA_SIZE_MAX) { + tx_mtu = BTM_BLE_DATA_SIZE_MAX; + } + + /* update TX data length if changed */ + if (p_lcb->tx_data_len != tx_mtu) { + BTM_SetBleDataLength(p_lcb->remote_bd_addr, tx_mtu); + } + +} + +/******************************************************************************* +** +** Function l2cble_process_data_length_change_evt +** +** Description This function process the data length change event +** +** Returns void +** +*******************************************************************************/ +void l2cble_process_data_length_change_event(UINT16 handle, UINT16 tx_data_len, UINT16 rx_data_len) +{ + tL2C_LCB *p_lcb = l2cu_find_lcb_by_handle(handle); + tACL_CONN *p_acl = btm_handle_to_acl(handle); + tBTM_LE_SET_PKT_DATA_LENGTH_PARAMS data_length_params; + + L2CAP_TRACE_DEBUG("%s TX data len = %d", __FUNCTION__, tx_data_len); + if (p_lcb == NULL) { + return; + } + + if (tx_data_len > 0) { + p_lcb->tx_data_len = tx_data_len; + } + + data_length_params.rx_len = rx_data_len; + data_length_params.tx_len = tx_data_len; + + if(p_acl) { + p_acl->data_length_params = data_length_params; + if (p_acl->p_set_pkt_data_cback) { + (*p_acl->p_set_pkt_data_cback)(BTM_SUCCESS, &data_length_params); + } + + p_acl->data_len_updating = false; + if(p_acl->data_len_waiting) { + p_acl->data_len_waiting = false; + p_acl->p_set_pkt_data_cback = p_acl->p_set_data_len_cback_waiting; + p_acl->p_set_data_len_cback_waiting = NULL; + // if value is same, triger callback directly + if(p_acl->tx_len_waiting == p_acl->data_length_params.tx_len) { + if(p_acl->p_set_pkt_data_cback) { + (*p_acl->p_set_pkt_data_cback)(BTM_SUCCESS, &p_acl->data_length_params); + } + return; + } + p_acl->data_len_updating = true; + /* always set the TxTime to be max, as controller does not care for now */ + btsnd_hcic_ble_set_data_length(handle, p_acl->tx_len_waiting, + BTM_BLE_DATA_TX_TIME_MAX); + } + } +} + +/******************************************************************************* +** +** Function l2cble_set_fixed_channel_tx_data_length +** +** Description This function update max fixed channel tx data length if applicable +** +** Returns void +** +*******************************************************************************/ +void l2cble_set_fixed_channel_tx_data_length(BD_ADDR remote_bda, UINT16 fix_cid, UINT16 tx_mtu) +{ + tL2C_LCB *p_lcb = l2cu_find_lcb_by_bd_addr(remote_bda, BT_TRANSPORT_LE); + UINT16 cid = fix_cid - L2CAP_FIRST_FIXED_CHNL; + + L2CAP_TRACE_DEBUG("%s TX MTU = %d", __FUNCTION__, tx_mtu); + + if (!controller_get_interface()->supports_ble_packet_extension()) { + L2CAP_TRACE_WARNING("%s, request not supported", __FUNCTION__); + return; + } + + /* See if we have a link control block for the connection */ + if (p_lcb == NULL) { + return; + } + + if (p_lcb->p_fixed_ccbs[cid] != NULL) { + if (tx_mtu > BTM_BLE_DATA_SIZE_MAX) { + tx_mtu = BTM_BLE_DATA_SIZE_MAX; + } + + p_lcb->p_fixed_ccbs[cid]->tx_data_len = tx_mtu; + } + + l2cble_update_data_length(p_lcb); +} + + +/******************************************************************************* +** +** Function l2c_send_update_conn_params_cb +** +** Description This function send the update connection parameter callback to the uplayer. +** +** Returns void +** +*******************************************************************************/ +void l2c_send_update_conn_params_cb(tL2C_LCB *p_lcb, UINT8 status) +{ + if(conn_param_update_cb.update_conn_param_cb != NULL){ + tBTM_LE_UPDATE_CONN_PRAMS update_param; + //if myself update the connection parameters + if (p_lcb->updating_param_flag){ + update_param.max_conn_int = p_lcb->updating_conn_max_interval; + update_param.min_conn_int = p_lcb->updating_conn_min_interval; + p_lcb->updating_param_flag = false; + }else{ + // remote device update the connection parameters + update_param.max_conn_int = update_param.min_conn_int = 0; + } + // current connection parameters + update_param.conn_int = p_lcb->current_used_conn_interval; + update_param.slave_latency = p_lcb->current_used_conn_latency; + update_param.supervision_tout = p_lcb->current_used_conn_timeout; + + (conn_param_update_cb.update_conn_param_cb)(status, p_lcb->remote_bd_addr, &update_param); + } +} + +/******************************************************************************* +** +** Function CalConnectParamTimeout +** +** Description This function is called to calculate the connection parameter timeout. +** +** Returns timeout +** +*******************************************************************************/ +UINT32 CalConnectParamTimeout(tL2C_LCB *p_lcb) +{ + UINT32 timeout = 6; + if (p_lcb != NULL){ + //1.25 * conn_int *(1+ latency) *32 + timeout = (40 * ( 1 + p_lcb->current_used_conn_latency) * p_lcb->current_used_conn_interval + 1.25 * p_lcb->waiting_update_conn_max_interval + 1000) / 1000; + if (timeout < 1){ + timeout = 1; + }else if (timeout > 120){ + timeout = 120; + } + } + return timeout; +} + +/******************************************************************************* +** +** Function l2cble_credit_based_conn_req +** +** Description This function sends LE Credit Based Connection Request for +** LE connection oriented channels. +** +** Returns void +** +*******************************************************************************/ +void l2cble_credit_based_conn_req (tL2C_CCB *p_ccb) +{ + if (!p_ccb) { + return; + } + + if (p_ccb->p_lcb && p_ccb->p_lcb->transport != BT_TRANSPORT_LE) + { + L2CAP_TRACE_WARNING ("LE link doesn't exist"); + return; + } + + l2cu_send_peer_ble_credit_based_conn_req (p_ccb); + return; +} + +/******************************************************************************* +** +** Function l2cble_credit_based_conn_res +** +** Description This function sends LE Credit Based Connection Response for +** LE connection oriented channels. +** +** Returns void +** +*******************************************************************************/ +void l2cble_credit_based_conn_res (tL2C_CCB *p_ccb, UINT16 result) +{ + if (!p_ccb) { + return; + } + + if (p_ccb->p_lcb && p_ccb->p_lcb->transport != BT_TRANSPORT_LE) + { + L2CAP_TRACE_WARNING ("LE link doesn't exist"); + return; + } + + l2cu_send_peer_ble_credit_based_conn_res (p_ccb, result); + return; +} + +/******************************************************************************* +** +** Function l2cble_send_flow_control_credit +** +** Description This function sends flow control credits for +** LE connection oriented channels. +** +** Returns void +** +*******************************************************************************/ +void l2cble_send_flow_control_credit(tL2C_CCB *p_ccb, UINT16 credit_value) +{ + if (!p_ccb) { + return; + } + + if (p_ccb->p_lcb && p_ccb->p_lcb->transport != BT_TRANSPORT_LE) + { + L2CAP_TRACE_WARNING ("LE link doesn't exist"); + return; + } + + l2cu_send_peer_ble_flow_control_credit(p_ccb, credit_value); + return; + +} + +/******************************************************************************* +** +** Function l2cble_send_peer_disc_req +** +** Description This function sends disconnect request +** to the peer LE device +** +** Returns void +** +*******************************************************************************/ +void l2cble_send_peer_disc_req(tL2C_CCB *p_ccb) +{ + L2CAP_TRACE_DEBUG ("%s",__func__); + if (!p_ccb) { + return; + } + + if (p_ccb->p_lcb && p_ccb->p_lcb->transport != BT_TRANSPORT_LE) + { + L2CAP_TRACE_WARNING ("LE link doesn't exist"); + return; + } + + l2cu_send_peer_ble_credit_based_disconn_req(p_ccb); + return; +} + +#if (SMP_INCLUDED == TRUE) +/******************************************************************************* +** +** Function l2cble_sec_comp +** +** Description This function is called when security procedure for an LE COC +** link is done +** +** Returns void +** +*******************************************************************************/ +void l2cble_sec_comp(BD_ADDR p_bda, tBT_TRANSPORT transport, void *p_ref_data, UINT8 status) +{ + tL2C_LCB *p_lcb = l2cu_find_lcb_by_bd_addr(p_bda, BT_TRANSPORT_LE); + tL2CAP_SEC_DATA *p_buf = NULL; + UINT8 sec_flag; + UINT8 sec_act; + + if (!p_lcb) + { + L2CAP_TRACE_WARNING ("%s security complete for unknown device", __func__); + return; + } + + sec_act = p_lcb->sec_act; + p_lcb->sec_act = 0; + + if (!fixed_queue_is_empty(p_lcb->le_sec_pending_q)) + { + p_buf = (tL2CAP_SEC_DATA*) fixed_queue_dequeue(p_lcb->le_sec_pending_q, FIXED_QUEUE_MAX_TIMEOUT); + if (!p_buf) + { + L2CAP_TRACE_WARNING ("%s Security complete for request not initiated from L2CAP", + __func__); + return; + } + + if (status != BTM_SUCCESS) + { + (*(p_buf->p_callback))(p_bda, BT_TRANSPORT_LE, p_buf->p_ref_data, status); + } + else + { + if (sec_act == BTM_SEC_ENCRYPT_MITM) + { + BTM_GetSecurityFlagsByTransport(p_bda, &sec_flag, transport); + if (sec_flag & BTM_SEC_FLAG_LKEY_AUTHED) { + (*(p_buf->p_callback))(p_bda, BT_TRANSPORT_LE, p_buf->p_ref_data, status); + } + else + { + L2CAP_TRACE_DEBUG ("%s MITM Protection Not present", __func__); + (*(p_buf->p_callback))(p_bda, BT_TRANSPORT_LE, p_buf->p_ref_data, + BTM_FAILED_ON_SECURITY); + } + } + else + { + L2CAP_TRACE_DEBUG ("%s MITM Protection not required sec_act = %d", + __func__, p_lcb->sec_act); + + (*(p_buf->p_callback))(p_bda, BT_TRANSPORT_LE, p_buf->p_ref_data, status); + } + } + } + else + { + L2CAP_TRACE_WARNING ("%s Security complete for request not initiated from L2CAP", __func__); + return; + } + osi_free(p_buf); + + while (!fixed_queue_is_empty(p_lcb->le_sec_pending_q)) + { + p_buf = (tL2CAP_SEC_DATA*) fixed_queue_dequeue(p_lcb->le_sec_pending_q, FIXED_QUEUE_MAX_TIMEOUT); + + if (status != BTM_SUCCESS) { + (*(p_buf->p_callback))(p_bda, BT_TRANSPORT_LE, p_buf->p_ref_data, status); + } else { + l2ble_sec_access_req(p_bda, p_buf->psm, p_buf->is_originator, + p_buf->p_callback, p_buf->p_ref_data); + } + + osi_free(p_buf); + } +} + +/******************************************************************************* +** +** Function l2ble_sec_access_req +** +** Description This function is called by LE COC link to meet the +** security requirement for the link +** +** Returns TRUE - security procedures are started +** FALSE - failure +** +*******************************************************************************/ +BOOLEAN l2ble_sec_access_req(BD_ADDR bd_addr, UINT16 psm, BOOLEAN is_originator, tL2CAP_SEC_CBACK *p_callback, void *p_ref_data) +{ + L2CAP_TRACE_DEBUG ("%s", __func__); + BOOLEAN status; + tL2C_LCB *p_lcb = NULL; + + if (!p_callback) + { + L2CAP_TRACE_ERROR("%s No callback function", __func__); + return FALSE; + } + + p_lcb = l2cu_find_lcb_by_bd_addr(bd_addr, BT_TRANSPORT_LE); + + if (!p_lcb) + { + L2CAP_TRACE_ERROR ("%s Security check for unknown device", __func__); + p_callback(bd_addr, BT_TRANSPORT_LE, p_ref_data, BTM_UNKNOWN_ADDR); + return FALSE; + } + + tL2CAP_SEC_DATA *p_buf = (tL2CAP_SEC_DATA*) osi_malloc((UINT16)sizeof(tL2CAP_SEC_DATA)); + if (!p_buf) + { + p_callback(bd_addr, BT_TRANSPORT_LE, p_ref_data, BTM_NO_RESOURCES); + return FALSE; + } + + p_buf->psm = psm; + p_buf->is_originator = is_originator; + p_buf->p_callback = p_callback; + p_buf->p_ref_data = p_ref_data; + fixed_queue_enqueue(p_lcb->le_sec_pending_q, p_buf, FIXED_QUEUE_MAX_TIMEOUT); + status = btm_ble_start_sec_check(bd_addr, psm, is_originator, &l2cble_sec_comp, p_ref_data); + + return status; +} +#endif /* #if (SMP_INCLUDED == TRUE) */ +#endif /* (BLE_INCLUDED == TRUE) */ +/******************************************************************************* +** +** Function L2CA_GetDisconnectReason +** +** Description This function returns the disconnect reason code. +** +** Returns disconnect reason +** +*******************************************************************************/ +UINT16 L2CA_GetDisconnectReason (BD_ADDR remote_bda, tBT_TRANSPORT transport) +{ + tL2C_LCB *p_lcb; + UINT16 reason = 0; + + if ((p_lcb = l2cu_find_lcb_by_bd_addr (remote_bda, transport)) != NULL) { + reason = p_lcb->disc_reason; + } + + L2CAP_TRACE_DEBUG ("L2CA_GetDisconnectReason=%d ", reason); + + return reason; +} diff --git a/lib/bt/host/bluedroid/stack/l2cap/l2c_csm.c b/lib/bt/host/bluedroid/stack/l2cap/l2c_csm.c new file mode 100644 index 00000000..9abe72e2 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/l2cap/l2c_csm.c @@ -0,0 +1,1263 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains the L2CAP channel state machine + * + ******************************************************************************/ + +#include +#include +#include + +#include "common/bt_target.h" +#include "stack/hcidefs.h" +#include "stack/hcimsgs.h" +#include "stack/l2cdefs.h" +#include "l2c_int.h" +#include "btm_int.h" +#include "stack/btu.h" +#include "stack/hcimsgs.h" +#include "osi/allocator.h" + +#if (L2CAP_COC_INCLUDED == TRUE) +/********************************************************************************/ +/* L O C A L F U N C T I O N P R O T O T Y P E S */ +/********************************************************************************/ +static void l2c_csm_closed (tL2C_CCB *p_ccb, UINT16 event, void *p_data); +static void l2c_csm_orig_w4_sec_comp (tL2C_CCB *p_ccb, UINT16 event, void *p_data); +static void l2c_csm_term_w4_sec_comp (tL2C_CCB *p_ccb, UINT16 event, void *p_data); +static void l2c_csm_w4_l2cap_connect_rsp (tL2C_CCB *p_ccb, UINT16 event, void *p_data); +static void l2c_csm_w4_l2ca_connect_rsp (tL2C_CCB *p_ccb, UINT16 event, void *p_data); +static void l2c_csm_config (tL2C_CCB *p_ccb, UINT16 event, void *p_data); +static void l2c_csm_open (tL2C_CCB *p_ccb, UINT16 event, void *p_data); +static void l2c_csm_w4_l2cap_disconnect_rsp (tL2C_CCB *p_ccb, UINT16 event, void *p_data); +static void l2c_csm_w4_l2ca_disconnect_rsp (tL2C_CCB *p_ccb, UINT16 event, void *p_data); + +#if (BT_TRACE_VERBOSE == TRUE) +static char *l2c_csm_get_event_name (UINT16 event); +#endif + +/******************************************************************************* +** +** Function l2c_csm_execute +** +** Description This function executes the state machine. +** +** Returns void +** +*******************************************************************************/ +void l2c_csm_execute (tL2C_CCB *p_ccb, UINT16 event, void *p_data) +{ + switch (p_ccb->chnl_state) { + case CST_CLOSED: + l2c_csm_closed (p_ccb, event, p_data); + break; + + case CST_ORIG_W4_SEC_COMP: + l2c_csm_orig_w4_sec_comp (p_ccb, event, p_data); + break; + + case CST_TERM_W4_SEC_COMP: + l2c_csm_term_w4_sec_comp (p_ccb, event, p_data); + break; + + case CST_W4_L2CAP_CONNECT_RSP: + l2c_csm_w4_l2cap_connect_rsp (p_ccb, event, p_data); + break; + + case CST_W4_L2CA_CONNECT_RSP: + l2c_csm_w4_l2ca_connect_rsp (p_ccb, event, p_data); + break; + + case CST_CONFIG: + l2c_csm_config (p_ccb, event, p_data); + break; + + case CST_OPEN: + l2c_csm_open (p_ccb, event, p_data); + break; + + case CST_W4_L2CAP_DISCONNECT_RSP: + l2c_csm_w4_l2cap_disconnect_rsp (p_ccb, event, p_data); + break; + + case CST_W4_L2CA_DISCONNECT_RSP: + l2c_csm_w4_l2ca_disconnect_rsp (p_ccb, event, p_data); + break; + + default: + L2CAP_TRACE_DEBUG("Unhandled event! event = %d", event); + break; + } +} + +/******************************************************************************* +** +** Function l2c_csm_closed +** +** Description This function handles events when the channel is in +** CLOSED state. This state exists only when the link is +** being initially established. +** +** Returns void +** +*******************************************************************************/ +static void l2c_csm_closed (tL2C_CCB *p_ccb, UINT16 event, void *p_data) +{ + tL2C_CONN_INFO *p_ci = (tL2C_CONN_INFO *)p_data; + UINT16 local_cid = p_ccb->local_cid; + tL2CA_DISCONNECT_IND_CB *disconnect_ind; + tL2CA_CONNECT_CFM_CB *connect_cfm; + + if (p_ccb->p_rcb == NULL) { +#if (BT_TRACE_VERBOSE == TRUE) + L2CAP_TRACE_ERROR ("L2CAP - LCID: 0x%04x st: CLOSED evt: %s p_rcb == NULL", p_ccb->local_cid, l2c_csm_get_event_name (event)); +#else + L2CAP_TRACE_ERROR ("L2CAP - LCID: 0x%04x st: CLOSED evt: 0x%04x p_rcb == NULL", p_ccb->local_cid, event); +#endif + return; + } + +#if (L2CAP_UCD_INCLUDED == TRUE) + if ( local_cid == L2CAP_CONNECTIONLESS_CID ) { + /* check if this event can be processed by UCD */ + if ( l2c_ucd_process_event (p_ccb, event, p_data) ) { + /* The event is processed by UCD state machine */ + return; + } + } +#endif + + disconnect_ind = p_ccb->p_rcb->api.pL2CA_DisconnectInd_Cb; + connect_cfm = p_ccb->p_rcb->api.pL2CA_ConnectCfm_Cb; + +#if (BT_TRACE_VERBOSE == TRUE) + L2CAP_TRACE_EVENT ("L2CAP - LCID: 0x%04x st: CLOSED evt: %s", p_ccb->local_cid, l2c_csm_get_event_name (event)); +#else + L2CAP_TRACE_EVENT ("L2CAP - st: CLOSED evt: %d", event); +#endif + + switch (event) { + case L2CEVT_LP_DISCONNECT_IND: /* Link was disconnected */ + L2CAP_TRACE_API ("L2CAP - Calling Disconnect_Ind_Cb(), CID: 0x%04x No Conf Needed", p_ccb->local_cid); + l2cu_release_ccb (p_ccb); + (*disconnect_ind)(local_cid, FALSE); + break; + + case L2CEVT_LP_CONNECT_CFM: /* Link came up */ + p_ccb->chnl_state = CST_ORIG_W4_SEC_COMP; + btm_sec_l2cap_access_req (p_ccb->p_lcb->remote_bd_addr, p_ccb->p_rcb->psm, + p_ccb->p_lcb->handle, TRUE, &l2c_link_sec_comp, p_ccb); + break; + + case L2CEVT_LP_CONNECT_CFM_NEG: /* Link failed */ + /* Disconnect unless ACL collision and upper layer wants to handle it */ + if (p_ci->status != HCI_ERR_CONNECTION_EXISTS + || !btm_acl_notif_conn_collision(p_ccb->p_lcb->remote_bd_addr)) { + L2CAP_TRACE_API ("L2CAP - Calling ConnectCfm_Cb(), CID: 0x%04x Status: %d", p_ccb->local_cid, p_ci->status); + l2cu_release_ccb (p_ccb); + (*connect_cfm)(local_cid, p_ci->status); + } + break; + + case L2CEVT_L2CA_CONNECT_REQ: /* API connect request */ + /* Cancel sniff mode if needed */ + { + tBTM_PM_PWR_MD settings; +// btla-specific ++ + memset((void *)&settings, 0, sizeof(settings)); +// btla-specific -- + settings.mode = BTM_PM_MD_ACTIVE; + /* COVERITY + Event uninit_use_in_call: Using uninitialized value "settings" (field "settings".timeout uninitialized) in call to function "BTM_SetPowerMode" [details] + Event uninit_use_in_call: Using uninitialized value "settings.max" in call to function "BTM_SetPowerMode" [details] + Event uninit_use_in_call: Using uninitialized value "settings.min" in call to function "BTM_SetPowerMode" + // FALSE-POSITIVE error from Coverity test-tool. Please do NOT remove following comment. + // coverity[uninit_use_in_call] False-positive: setting the mode to BTM_PM_MD_ACTIVE only uses settings.mode the other data members of tBTM_PM_PWR_MD are ignored + */ + BTM_SetPowerMode (BTM_PM_SET_ONLY_ID, p_ccb->p_lcb->remote_bd_addr, &settings); + } + + /* If sec access does not result in started SEC_COM or COMP_NEG are already processed */ + if (btm_sec_l2cap_access_req (p_ccb->p_lcb->remote_bd_addr, p_ccb->p_rcb->psm, + p_ccb->p_lcb->handle, TRUE, &l2c_link_sec_comp, p_ccb) == BTM_CMD_STARTED) { + p_ccb->chnl_state = CST_ORIG_W4_SEC_COMP; + } + break; + + case L2CEVT_SEC_COMP: + p_ccb->chnl_state = CST_W4_L2CAP_CONNECT_RSP; + + /* Wait for the info resp in this state before sending connect req (if needed) */ + if (!p_ccb->p_lcb->w4_info_rsp) { + /* Need to have at least one compatible channel to continue */ + if (!l2c_fcr_chk_chan_modes(p_ccb)) { + l2cu_release_ccb (p_ccb); + (*p_ccb->p_rcb->api.pL2CA_ConnectCfm_Cb)(local_cid, L2CAP_CONN_NO_LINK); + } else { + l2cu_send_peer_connect_req (p_ccb); + btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, L2CAP_CHNL_CONNECT_TOUT); + } + } + break; + + case L2CEVT_SEC_COMP_NEG: /* something is really bad with security */ + L2CAP_TRACE_API ("L2CAP - Calling ConnectCfm_Cb(), CID: 0x%04x Status: %d", p_ccb->local_cid, L2CAP_CONN_TIMEOUT); + l2cu_release_ccb (p_ccb); + (*connect_cfm)(local_cid, L2CAP_CONN_SECURITY_BLOCK); + break; + + case L2CEVT_L2CAP_CONNECT_REQ: /* Peer connect request */ + /* stop link timer to avoid race condition between A2MP, Security, and L2CAP */ + btu_stop_timer (&p_ccb->p_lcb->timer_entry); + + /* Cancel sniff mode if needed */ + { + tBTM_PM_PWR_MD settings; +// btla-specific ++ + memset((void *)&settings, 0, sizeof(settings)); +// btla-specific -- + settings.mode = BTM_PM_MD_ACTIVE; + /* COVERITY + Event uninit_use_in_call: Using uninitialized value "settings" (field "settings".timeout uninitialized) in call to function "BTM_SetPowerMode" [details] + Event uninit_use_in_call: Using uninitialized value "settings.max" in call to function "BTM_SetPowerMode" [details] + Event uninit_use_in_call: Using uninitialized value "settings.min" in call to function "BTM_SetPowerMode" + // FALSE-POSITIVE error from Coverity test-tool. Please do NOT remove following comment. + // coverity[uninit_use_in_call] False-positive: setting the mode to BTM_PM_MD_ACTIVE only uses settings.mode the other data members of tBTM_PM_PWR_MD are ignored + */ + BTM_SetPowerMode (BTM_PM_SET_ONLY_ID, p_ccb->p_lcb->remote_bd_addr, &settings); + } + + p_ccb->chnl_state = CST_TERM_W4_SEC_COMP; + if (btm_sec_l2cap_access_req (p_ccb->p_lcb->remote_bd_addr, p_ccb->p_rcb->psm, + p_ccb->p_lcb->handle, FALSE, &l2c_link_sec_comp, p_ccb) == BTM_CMD_STARTED) { + /* started the security process, tell the peer to set a longer timer */ + l2cu_send_peer_connect_rsp(p_ccb, L2CAP_CONN_PENDING, 0); + } + break; + + case L2CEVT_TIMEOUT: + L2CAP_TRACE_API ("L2CAP - Calling ConnectCfm_Cb(), CID: 0x%04x Status: %d", p_ccb->local_cid, L2CAP_CONN_TIMEOUT); + l2cu_release_ccb (p_ccb); + (*connect_cfm)(local_cid, L2CAP_CONN_TIMEOUT); + break; + + case L2CEVT_L2CAP_DATA: /* Peer data packet rcvd */ + case L2CEVT_L2CA_DATA_WRITE: /* Upper layer data to send */ + osi_free (p_data); + break; + + case L2CEVT_L2CA_DISCONNECT_REQ: /* Upper wants to disconnect */ + l2cu_release_ccb (p_ccb); + break; + } +} + + +/******************************************************************************* +** +** Function l2c_csm_orig_w4_sec_comp +** +** Description This function handles events when the channel is in +** CST_ORIG_W4_SEC_COMP state. +** +** Returns void +** +*******************************************************************************/ +static void l2c_csm_orig_w4_sec_comp (tL2C_CCB *p_ccb, UINT16 event, void *p_data) +{ + tL2CA_DISCONNECT_IND_CB *disconnect_ind = p_ccb->p_rcb->api.pL2CA_DisconnectInd_Cb; + tL2CA_CONNECT_CFM_CB *connect_cfm = p_ccb->p_rcb->api.pL2CA_ConnectCfm_Cb; + UINT16 local_cid = p_ccb->local_cid; + +#if (BT_TRACE_VERBOSE == TRUE) + L2CAP_TRACE_EVENT ("L2CAP - LCID: 0x%04x st: ORIG_W4_SEC_COMP evt: %s", p_ccb->local_cid, l2c_csm_get_event_name (event)); +#else + L2CAP_TRACE_EVENT ("L2CAP - st: ORIG_W4_SEC_COMP evt: %d", event); +#endif + +#if (L2CAP_UCD_INCLUDED == TRUE) + if ( local_cid == L2CAP_CONNECTIONLESS_CID ) { + /* check if this event can be processed by UCD */ + if ( l2c_ucd_process_event (p_ccb, event, p_data) ) { + /* The event is processed by UCD state machine */ + return; + } + } +#endif + + switch (event) { + case L2CEVT_LP_DISCONNECT_IND: /* Link was disconnected */ + L2CAP_TRACE_API ("L2CAP - Calling Disconnect_Ind_Cb(), CID: 0x%04x No Conf Needed", p_ccb->local_cid); + l2cu_release_ccb (p_ccb); + (*disconnect_ind)(local_cid, FALSE); + break; + + case L2CEVT_SEC_RE_SEND_CMD: /* BTM has enough info to proceed */ + case L2CEVT_LP_CONNECT_CFM: /* Link came up */ + btm_sec_l2cap_access_req (p_ccb->p_lcb->remote_bd_addr, p_ccb->p_rcb->psm, + p_ccb->p_lcb->handle, TRUE, &l2c_link_sec_comp, p_ccb); + break; + + case L2CEVT_SEC_COMP: /* Security completed success */ + /* Wait for the info resp in this state before sending connect req (if needed) */ + p_ccb->chnl_state = CST_W4_L2CAP_CONNECT_RSP; + if (!p_ccb->p_lcb->w4_info_rsp) { + /* Need to have at least one compatible channel to continue */ + if (!l2c_fcr_chk_chan_modes(p_ccb)) { + l2cu_release_ccb (p_ccb); + (*connect_cfm)(local_cid, L2CAP_CONN_NO_LINK); + } else { + btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, L2CAP_CHNL_CONNECT_TOUT); + l2cu_send_peer_connect_req (p_ccb); /* Start Connection */ + } + } + break; + + case L2CEVT_SEC_COMP_NEG: + L2CAP_TRACE_API ("L2CAP - Calling ConnectCfm_Cb(), CID: 0x%04x Status: %d", p_ccb->local_cid, HCI_ERR_AUTH_FAILURE); + + /* If last channel immediately disconnect the ACL for better security. + Also prevents a race condition between BTM and L2CAP */ + if ( (p_ccb == p_ccb->p_lcb->ccb_queue.p_first_ccb) && (p_ccb == p_ccb->p_lcb->ccb_queue.p_last_ccb) ) { + p_ccb->p_lcb->idle_timeout = 0; + } + + l2cu_release_ccb (p_ccb); + (*connect_cfm)(local_cid, HCI_ERR_AUTH_FAILURE); + break; + + case L2CEVT_L2CA_DATA_WRITE: /* Upper layer data to send */ + case L2CEVT_L2CAP_DATA: /* Peer data packet rcvd */ + osi_free (p_data); + break; + + case L2CEVT_L2CA_DISCONNECT_REQ: /* Upper wants to disconnect */ + /* Tell security manager to abort */ + btm_sec_abort_access_req (p_ccb->p_lcb->remote_bd_addr); + + l2cu_release_ccb (p_ccb); + break; + } +} + + +/******************************************************************************* +** +** Function l2c_csm_term_w4_sec_comp +** +** Description This function handles events when the channel is in +** CST_TERM_W4_SEC_COMP state. +** +** Returns void +** +*******************************************************************************/ +static void l2c_csm_term_w4_sec_comp (tL2C_CCB *p_ccb, UINT16 event, void *p_data) +{ +#if (BT_TRACE_VERBOSE == TRUE) + L2CAP_TRACE_EVENT ("L2CAP - LCID: 0x%04x st: TERM_W4_SEC_COMP evt: %s", p_ccb->local_cid, l2c_csm_get_event_name (event)); +#else + L2CAP_TRACE_EVENT ("L2CAP - st: TERM_W4_SEC_COMP evt: %d", event); +#endif + +#if (L2CAP_UCD_INCLUDED == TRUE) + if ( p_ccb->local_cid == L2CAP_CONNECTIONLESS_CID ) { + /* check if this event can be processed by UCD */ + if ( l2c_ucd_process_event (p_ccb, event, p_data) ) { + /* The event is processed by UCD state machine */ + return; + } + } +#endif + + switch (event) { + case L2CEVT_LP_DISCONNECT_IND: /* Link was disconnected */ + /* Tell security manager to abort */ + btm_sec_abort_access_req (p_ccb->p_lcb->remote_bd_addr); + + l2cu_release_ccb (p_ccb); + break; + + case L2CEVT_SEC_COMP: + p_ccb->chnl_state = CST_W4_L2CA_CONNECT_RSP; + + /* Wait for the info resp in next state before sending connect ind (if needed) */ + if (!p_ccb->p_lcb->w4_info_rsp) { + /* Don't need to get info from peer or already retrieved so continue */ + btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, L2CAP_CHNL_CONNECT_TOUT); + L2CAP_TRACE_API ("L2CAP - Calling Connect_Ind_Cb(), CID: 0x%04x", p_ccb->local_cid); + + (*p_ccb->p_rcb->api.pL2CA_ConnectInd_Cb) (p_ccb->p_lcb->remote_bd_addr, p_ccb->local_cid, + p_ccb->p_rcb->psm, p_ccb->remote_id); + } else { + /* + ** L2CAP Connect Response will be sent out by 3 sec timer expiration + ** because Bluesoleil doesn't respond to L2CAP Information Request. + ** Bluesoleil seems to disconnect ACL link as failure case, because + ** it takes too long (4~7secs) to get response. + ** product version : Bluesoleil 2.1.1.0 EDR Release 060123 + ** stack version : 05.04.11.20060119 + */ + + /* Waiting for the info resp, tell the peer to set a longer timer */ + l2cu_send_peer_connect_rsp(p_ccb, L2CAP_CONN_PENDING, 0); + } + break; + + case L2CEVT_SEC_COMP_NEG: + if (((tL2C_CONN_INFO *)p_data)->status == BTM_DELAY_CHECK) { + /* start a timer - encryption change not received before L2CAP connect req */ + btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, L2CAP_DELAY_CHECK_SM4); + } else { + l2cu_send_peer_connect_rsp (p_ccb, L2CAP_CONN_SECURITY_BLOCK, 0); + l2cu_release_ccb (p_ccb); + } + break; + + case L2CEVT_L2CA_DATA_WRITE: /* Upper layer data to send */ + case L2CEVT_L2CAP_DATA: /* Peer data packet rcvd */ + osi_free (p_data); + break; + + case L2CEVT_L2CA_DISCONNECT_REQ: /* Upper wants to disconnect */ + l2cu_release_ccb (p_ccb); + break; + + case L2CEVT_L2CAP_DISCONNECT_REQ: /* Peer disconnected request */ + l2cu_send_peer_disc_rsp (p_ccb->p_lcb, p_ccb->remote_id, p_ccb->local_cid, p_ccb->remote_cid); + + /* Tell security manager to abort */ + btm_sec_abort_access_req (p_ccb->p_lcb->remote_bd_addr); + + l2cu_release_ccb (p_ccb); + break; + + case L2CEVT_TIMEOUT: + /* SM4 related. */ + if (!btsnd_hcic_disconnect (p_ccb->p_lcb->handle, HCI_ERR_AUTH_FAILURE)) { + L2CAP_TRACE_API ("L2CAP - Calling btsnd_hcic_disconnect for handle %i failed", p_ccb->p_lcb->handle); + btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, 1); + } + break; + + case L2CEVT_SEC_RE_SEND_CMD: /* BTM has enough info to proceed */ + btm_sec_l2cap_access_req (p_ccb->p_lcb->remote_bd_addr, p_ccb->p_rcb->psm, + p_ccb->p_lcb->handle, FALSE, &l2c_link_sec_comp, p_ccb); + break; + } +} + + +/******************************************************************************* +** +** Function l2c_csm_w4_l2cap_connect_rsp +** +** Description This function handles events when the channel is in +** CST_W4_L2CAP_CONNECT_RSP state. +** +** Returns void +** +*******************************************************************************/ +static void l2c_csm_w4_l2cap_connect_rsp (tL2C_CCB *p_ccb, UINT16 event, void *p_data) +{ + tL2C_CONN_INFO *p_ci = (tL2C_CONN_INFO *)p_data; + tL2CA_DISCONNECT_IND_CB *disconnect_ind = p_ccb->p_rcb->api.pL2CA_DisconnectInd_Cb; + tL2CA_CONNECT_CFM_CB *connect_cfm = p_ccb->p_rcb->api.pL2CA_ConnectCfm_Cb; + UINT16 local_cid = p_ccb->local_cid; + +#if (BT_TRACE_VERBOSE == TRUE) + L2CAP_TRACE_EVENT ("L2CAP - LCID: 0x%04x st: W4_L2CAP_CON_RSP evt: %s", p_ccb->local_cid, l2c_csm_get_event_name (event)); +#else + L2CAP_TRACE_EVENT ("L2CAP - st: W4_L2CAP_CON_RSP evt: %d", event); +#endif + + switch (event) { + case L2CEVT_LP_DISCONNECT_IND: /* Link was disconnected */ + /* Send disc indication unless peer to peer race condition AND normal disconnect */ + /* *((UINT8 *)p_data) != HCI_ERR_PEER_USER happens when peer device try to disconnect for normal reason */ + p_ccb->chnl_state = CST_CLOSED; + if ((p_ccb->flags & CCB_FLAG_NO_RETRY) || !p_data || (*((UINT8 *)p_data) != HCI_ERR_PEER_USER)) { + L2CAP_TRACE_API ("L2CAP - Calling Disconnect_Ind_Cb(), CID: 0x%04x No Conf Needed", + p_ccb->local_cid); + l2cu_release_ccb (p_ccb); + (*disconnect_ind)(local_cid, FALSE); + } + p_ccb->flags |= CCB_FLAG_NO_RETRY; + break; + + case L2CEVT_L2CAP_CONNECT_RSP: /* Got peer connect confirm */ + p_ccb->remote_cid = p_ci->remote_cid; + p_ccb->chnl_state = CST_CONFIG; + btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, L2CAP_CHNL_CFG_TIMEOUT); + L2CAP_TRACE_API ("L2CAP - Calling Connect_Cfm_Cb(), CID: 0x%04x, Success", p_ccb->local_cid); + + (*p_ccb->p_rcb->api.pL2CA_ConnectCfm_Cb)(local_cid, L2CAP_CONN_OK); + break; + + case L2CEVT_L2CAP_CONNECT_RSP_PND: /* Got peer connect pending */ + p_ccb->remote_cid = p_ci->remote_cid; + btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, L2CAP_CHNL_CONNECT_TOUT_EXT); + if (p_ccb->p_rcb->api.pL2CA_ConnectPnd_Cb) { + L2CAP_TRACE_API ("L2CAP - Calling Connect_Pnd_Cb(), CID: 0x%04x", p_ccb->local_cid); + (*p_ccb->p_rcb->api.pL2CA_ConnectPnd_Cb)(p_ccb->local_cid); + } + break; + + case L2CEVT_L2CAP_CONNECT_RSP_NEG: /* Peer rejected connection */ + L2CAP_TRACE_API ("L2CAP - Calling Connect_Cfm_Cb(), CID: 0x%04x, Failure Code: %d", p_ccb->local_cid, p_ci->l2cap_result); + l2cu_release_ccb (p_ccb); + (*connect_cfm)(local_cid, p_ci->l2cap_result); + break; + + case L2CEVT_TIMEOUT: + L2CAP_TRACE_API ("L2CAP - Calling Connect_Cfm_Cb(), CID: 0x%04x, Timeout", p_ccb->local_cid); + l2cu_release_ccb (p_ccb); + (*connect_cfm)(local_cid, L2CAP_CONN_TIMEOUT); + break; + + case L2CEVT_L2CA_DISCONNECT_REQ: /* Upper wants to disconnect */ + /* If we know peer CID from connect pending, we can send disconnect */ + if (p_ccb->remote_cid != 0) { + l2cu_send_peer_disc_req (p_ccb); + p_ccb->chnl_state = CST_W4_L2CAP_DISCONNECT_RSP; + btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, L2CAP_CHNL_DISCONNECT_TOUT); + } else { + l2cu_release_ccb (p_ccb); + } + break; + + case L2CEVT_L2CA_DATA_WRITE: /* Upper layer data to send */ + case L2CEVT_L2CAP_DATA: /* Peer data packet rcvd */ + osi_free (p_data); + break; + + case L2CEVT_L2CAP_INFO_RSP: + /* Need to have at least one compatible channel to continue */ + if (!l2c_fcr_chk_chan_modes(p_ccb)) { + l2cu_release_ccb (p_ccb); + (*connect_cfm)(local_cid, L2CAP_CONN_NO_LINK); + } else { + /* We have feature info, so now send peer connect request */ + btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, L2CAP_CHNL_CONNECT_TOUT); + l2cu_send_peer_connect_req (p_ccb); /* Start Connection */ + } + break; + } +} + + +/******************************************************************************* +** +** Function l2c_csm_w4_l2ca_connect_rsp +** +** Description This function handles events when the channel is in +** CST_W4_L2CA_CONNECT_RSP state. +** +** Returns void +** +*******************************************************************************/ +static void l2c_csm_w4_l2ca_connect_rsp (tL2C_CCB *p_ccb, UINT16 event, void *p_data) +{ + tL2C_CONN_INFO *p_ci; + tL2CA_DISCONNECT_IND_CB *disconnect_ind = p_ccb->p_rcb->api.pL2CA_DisconnectInd_Cb; + UINT16 local_cid = p_ccb->local_cid; + +#if (BT_TRACE_VERBOSE == TRUE) + L2CAP_TRACE_EVENT ("L2CAP - LCID: 0x%04x st: W4_L2CA_CON_RSP evt: %s", p_ccb->local_cid, l2c_csm_get_event_name (event)); +#else + L2CAP_TRACE_EVENT ("L2CAP - st: W4_L2CA_CON_RSP evt: %d", event); +#endif + + switch (event) { + case L2CEVT_LP_DISCONNECT_IND: /* Link was disconnected */ + L2CAP_TRACE_API ("L2CAP - Calling Disconnect_Ind_Cb(), CID: 0x%04x No Conf Needed", p_ccb->local_cid); + l2cu_release_ccb (p_ccb); + (*disconnect_ind)(local_cid, FALSE); + break; + + case L2CEVT_L2CA_CONNECT_RSP: + p_ci = (tL2C_CONN_INFO *)p_data; + + /* Result should be OK or PENDING */ + if ((!p_ci) || (p_ci->l2cap_result == L2CAP_CONN_OK)) { + l2cu_send_peer_connect_rsp (p_ccb, L2CAP_CONN_OK, 0); + p_ccb->chnl_state = CST_CONFIG; + btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, L2CAP_CHNL_CFG_TIMEOUT); + } else { + /* If pending, stay in same state and start extended timer */ + l2cu_send_peer_connect_rsp (p_ccb, p_ci->l2cap_result, p_ci->l2cap_status); + btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, L2CAP_CHNL_CONNECT_TOUT_EXT); + } + break; + + case L2CEVT_L2CA_CONNECT_RSP_NEG: + p_ci = (tL2C_CONN_INFO *)p_data; + l2cu_send_peer_connect_rsp (p_ccb, p_ci->l2cap_result, p_ci->l2cap_status); + l2cu_release_ccb (p_ccb); + break; + + case L2CEVT_TIMEOUT: + l2cu_send_peer_connect_rsp (p_ccb, L2CAP_CONN_NO_PSM, 0); + L2CAP_TRACE_API ("L2CAP - Calling Disconnect_Ind_Cb(), CID: 0x%04x No Conf Needed", p_ccb->local_cid); + l2cu_release_ccb (p_ccb); + (*disconnect_ind)(local_cid, FALSE); + break; + + case L2CEVT_L2CA_DATA_WRITE: /* Upper layer data to send */ + case L2CEVT_L2CAP_DATA: /* Peer data packet rcvd */ + osi_free (p_data); + break; + + case L2CEVT_L2CA_DISCONNECT_REQ: /* Upper wants to disconnect */ + l2cu_send_peer_disc_req (p_ccb); + p_ccb->chnl_state = CST_W4_L2CAP_DISCONNECT_RSP; + btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, L2CAP_CHNL_DISCONNECT_TOUT); + break; + + case L2CEVT_L2CAP_INFO_RSP: + /* We have feature info, so now give the upper layer connect IND */ + btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, L2CAP_CHNL_CONNECT_TOUT); + L2CAP_TRACE_API ("L2CAP - Calling Connect_Ind_Cb(), CID: 0x%04x", p_ccb->local_cid); + + (*p_ccb->p_rcb->api.pL2CA_ConnectInd_Cb) (p_ccb->p_lcb->remote_bd_addr, + p_ccb->local_cid, + p_ccb->p_rcb->psm, + p_ccb->remote_id); + break; + } +} + + +/******************************************************************************* +** +** Function l2c_csm_config +** +** Description This function handles events when the channel is in +** CONFIG state. +** +** Returns void +** +*******************************************************************************/ +static void l2c_csm_config (tL2C_CCB *p_ccb, UINT16 event, void *p_data) +{ + tL2CAP_CFG_INFO *p_cfg = (tL2CAP_CFG_INFO *)p_data; + tL2CA_DISCONNECT_IND_CB *disconnect_ind = p_ccb->p_rcb->api.pL2CA_DisconnectInd_Cb; + UINT16 local_cid = p_ccb->local_cid; + UINT8 cfg_result; + +#if (BT_TRACE_VERBOSE == TRUE) + L2CAP_TRACE_EVENT ("L2CAP - LCID: 0x%04x st: CONFIG evt: %s", p_ccb->local_cid, l2c_csm_get_event_name (event)); +#else + L2CAP_TRACE_EVENT ("L2CAP - st: CONFIG evt: %d", event); +#endif + + switch (event) { + case L2CEVT_LP_DISCONNECT_IND: /* Link was disconnected */ + L2CAP_TRACE_API ("L2CAP - Calling Disconnect_Ind_Cb(), CID: 0x%04x No Conf Needed", p_ccb->local_cid); + l2cu_release_ccb (p_ccb); + (*disconnect_ind)(local_cid, FALSE); + break; + + case L2CEVT_L2CAP_CONFIG_REQ: /* Peer config request */ + + if ((cfg_result = l2cu_process_peer_cfg_req (p_ccb, p_cfg)) == L2CAP_PEER_CFG_OK) { + L2CAP_TRACE_EVENT ("L2CAP - Calling Config_Req_Cb(), CID: 0x%04x, C-bit %d", + p_ccb->local_cid, (p_cfg->flags & L2CAP_CFG_FLAGS_MASK_CONT)); + (*p_ccb->p_rcb->api.pL2CA_ConfigInd_Cb)(p_ccb->local_cid, p_cfg); + } else if (cfg_result == L2CAP_PEER_CFG_DISCONNECT) { + /* Disconnect if channels are incompatible */ + L2CAP_TRACE_EVENT ("L2CAP - incompatible configurations disconnect"); + l2cu_disconnect_chnl (p_ccb); + } else { /* Return error to peer so he can renegotiate if possible */ + L2CAP_TRACE_EVENT ("L2CAP - incompatible configurations trying reconfig"); + l2cu_send_peer_config_rsp (p_ccb, p_cfg); + } + break; + + case L2CEVT_L2CAP_CONFIG_RSP: /* Peer config response */ + l2cu_process_peer_cfg_rsp (p_ccb, p_cfg); + + if (p_cfg->result != L2CAP_CFG_PENDING) { + /* TBD: When config options grow beyong minimum MTU (48 bytes) + * logic needs to be added to handle responses with + * continuation bit set in flags field. + * 1. Send additional config request out until C-bit is cleared in response + */ + p_ccb->config_done |= OB_CFG_DONE; + + if (p_ccb->config_done & IB_CFG_DONE) { + /* Verify two sides are in compatible modes before continuing */ + if (p_ccb->our_cfg.fcr.mode != p_ccb->peer_cfg.fcr.mode) { + l2cu_send_peer_disc_req (p_ccb); + L2CAP_TRACE_WARNING ("L2CAP - Calling Disconnect_Ind_Cb(Incompatible CFG), CID: 0x%04x No Conf Needed", p_ccb->local_cid); + l2cu_release_ccb (p_ccb); + (*disconnect_ind)(local_cid, FALSE); + break; + } + + p_ccb->config_done |= RECONFIG_FLAG; + p_ccb->chnl_state = CST_OPEN; + l2c_link_adjust_chnl_allocation (); + btu_stop_timer (&p_ccb->timer_entry); + + /* If using eRTM and waiting for an ACK, restart the ACK timer */ + if (p_ccb->fcrb.wait_ack) { + l2c_fcr_start_timer(p_ccb); + } + + /* + ** check p_ccb->our_cfg.fcr.mon_tout and p_ccb->our_cfg.fcr.rtrans_tout + ** we may set them to zero when sending config request during renegotiation + */ + if ((p_ccb->our_cfg.fcr.mode == L2CAP_FCR_ERTM_MODE) + && ((p_ccb->our_cfg.fcr.mon_tout == 0) || (p_ccb->our_cfg.fcr.rtrans_tout))) { + l2c_fcr_adj_monitor_retran_timeout (p_ccb); + } + +#if (L2CAP_ERTM_STATS == TRUE) + p_ccb->fcrb.connect_tick_count = osi_time_get_os_boottime_ms(); +#endif + /* See if we can forward anything on the hold queue */ + if (!fixed_queue_is_empty(p_ccb->xmit_hold_q)) { + l2c_link_check_send_pkts (p_ccb->p_lcb, NULL, NULL); + } + } + } + + L2CAP_TRACE_API ("L2CAP - Calling Config_Rsp_Cb(), CID: 0x%04x", p_ccb->local_cid); + (*p_ccb->p_rcb->api.pL2CA_ConfigCfm_Cb)(p_ccb->local_cid, p_cfg); + break; + + case L2CEVT_L2CAP_CONFIG_RSP_NEG: /* Peer config error rsp */ + /* Disable the Timer */ + btu_stop_timer (&p_ccb->timer_entry); + + /* If failure was channel mode try to renegotiate */ + if (l2c_fcr_renegotiate_chan(p_ccb, p_cfg) == FALSE) { + L2CAP_TRACE_API ("L2CAP - Calling Config_Rsp_Cb(), CID: 0x%04x, Failure: %d", p_ccb->local_cid, p_cfg->result); + (*p_ccb->p_rcb->api.pL2CA_ConfigCfm_Cb)(p_ccb->local_cid, p_cfg); + } + break; + + case L2CEVT_L2CAP_DISCONNECT_REQ: /* Peer disconnected request */ + btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, L2CAP_CHNL_DISCONNECT_TOUT); + p_ccb->chnl_state = CST_W4_L2CA_DISCONNECT_RSP; + L2CAP_TRACE_API ("L2CAP - Calling Disconnect_Ind_Cb(), CID: 0x%04x Conf Needed", p_ccb->local_cid); + (*p_ccb->p_rcb->api.pL2CA_DisconnectInd_Cb)(p_ccb->local_cid, TRUE); + break; + + case L2CEVT_L2CA_CONFIG_REQ: /* Upper layer config req */ + l2cu_process_our_cfg_req (p_ccb, p_cfg); + l2cu_send_peer_config_req (p_ccb, p_cfg); + btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, L2CAP_CHNL_CFG_TIMEOUT); + break; + + case L2CEVT_L2CA_CONFIG_RSP: /* Upper layer config rsp */ + l2cu_process_our_cfg_rsp (p_ccb, p_cfg); + + /* Not finished if continuation flag is set */ + if ( (p_cfg->flags & L2CAP_CFG_FLAGS_MASK_CONT) || (p_cfg->result == L2CAP_CFG_PENDING) ) { + /* Send intermediate response; remain in cfg state */ + l2cu_send_peer_config_rsp (p_ccb, p_cfg); + break; + } + + /* Local config done; clear cached configuration in case reconfig takes place later */ + p_ccb->peer_cfg.mtu_present = FALSE; + p_ccb->peer_cfg.flush_to_present = FALSE; + p_ccb->peer_cfg.qos_present = FALSE; + + p_ccb->config_done |= IB_CFG_DONE; + + if (p_ccb->config_done & OB_CFG_DONE) { + /* Verify two sides are in compatible modes before continuing */ + if (p_ccb->our_cfg.fcr.mode != p_ccb->peer_cfg.fcr.mode) { + l2cu_send_peer_disc_req (p_ccb); + L2CAP_TRACE_WARNING ("L2CAP - Calling Disconnect_Ind_Cb(Incompatible CFG), CID: 0x%04x No Conf Needed", p_ccb->local_cid); + l2cu_release_ccb (p_ccb); + (*disconnect_ind)(local_cid, FALSE); + break; + } + + p_ccb->config_done |= RECONFIG_FLAG; + p_ccb->chnl_state = CST_OPEN; + l2c_link_adjust_chnl_allocation (); + btu_stop_timer (&p_ccb->timer_entry); + } + + l2cu_send_peer_config_rsp (p_ccb, p_cfg); + + /* If using eRTM and waiting for an ACK, restart the ACK timer */ + if (p_ccb->fcrb.wait_ack) { + l2c_fcr_start_timer(p_ccb); + } + +#if (L2CAP_ERTM_STATS == TRUE) + p_ccb->fcrb.connect_tick_count = osi_time_get_os_boottime_ms(); +#endif + + /* See if we can forward anything on the hold queue */ + if ( (p_ccb->chnl_state == CST_OPEN) && + (!fixed_queue_is_empty(p_ccb->xmit_hold_q))) { + l2c_link_check_send_pkts (p_ccb->p_lcb, NULL, NULL); + } + break; + + case L2CEVT_L2CA_CONFIG_RSP_NEG: /* Upper layer config reject */ + l2cu_send_peer_config_rsp (p_ccb, p_cfg); + btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, L2CAP_CHNL_CFG_TIMEOUT); + break; + + case L2CEVT_L2CA_DISCONNECT_REQ: /* Upper wants to disconnect */ + l2cu_send_peer_disc_req (p_ccb); + p_ccb->chnl_state = CST_W4_L2CAP_DISCONNECT_RSP; + btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, L2CAP_CHNL_DISCONNECT_TOUT); + break; + + case L2CEVT_L2CAP_DATA: /* Peer data packet rcvd */ + L2CAP_TRACE_API ("L2CAP - Calling DataInd_Cb(), CID: 0x%04x", p_ccb->local_cid); +#if (L2CAP_NUM_FIXED_CHNLS > 0) + if (p_ccb->local_cid >= L2CAP_FIRST_FIXED_CHNL && + p_ccb->local_cid <= L2CAP_LAST_FIXED_CHNL) { + if (p_ccb->local_cid < L2CAP_BASE_APPL_CID) { + if (l2cb.fixed_reg[p_ccb->local_cid - L2CAP_FIRST_FIXED_CHNL].pL2CA_FixedData_Cb) { + (*l2cb.fixed_reg[p_ccb->local_cid - L2CAP_FIRST_FIXED_CHNL].pL2CA_FixedData_Cb) + (p_ccb->local_cid, p_ccb->p_lcb->remote_bd_addr, (BT_HDR *)p_data); + } else { + osi_free (p_data); + } + break; + } + } +#endif + (*p_ccb->p_rcb->api.pL2CA_DataInd_Cb)(p_ccb->local_cid, (BT_HDR *)p_data); + break; + + case L2CEVT_L2CA_DATA_WRITE: /* Upper layer data to send */ + if (p_ccb->config_done & OB_CFG_DONE) { + l2c_enqueue_peer_data (p_ccb, (BT_HDR *)p_data); + } else { + osi_free (p_data); + } + break; + + case L2CEVT_TIMEOUT: + l2cu_send_peer_disc_req (p_ccb); + L2CAP_TRACE_API ("L2CAP - Calling Disconnect_Ind_Cb(), CID: 0x%04x No Conf Needed", + p_ccb->local_cid); + l2cu_release_ccb (p_ccb); + (*disconnect_ind)(local_cid, FALSE); + break; + } +} + + +/******************************************************************************* +** +** Function l2c_csm_open +** +** Description This function handles events when the channel is in +** OPEN state. +** +** Returns void +** +*******************************************************************************/ +static void l2c_csm_open (tL2C_CCB *p_ccb, UINT16 event, void *p_data) +{ + UINT16 local_cid = p_ccb->local_cid; + tL2CAP_CFG_INFO *p_cfg; + tL2C_CHNL_STATE tempstate; + UINT8 tempcfgdone; + UINT8 cfg_result; + +#if (BT_TRACE_VERBOSE == TRUE) + L2CAP_TRACE_EVENT ("L2CAP - LCID: 0x%04x st: OPEN evt: %s", + p_ccb->local_cid, l2c_csm_get_event_name (event)); +#else + L2CAP_TRACE_EVENT ("L2CAP - st: OPEN evt: %d", event); +#endif + +#if (L2CAP_UCD_INCLUDED == TRUE) + if ( local_cid == L2CAP_CONNECTIONLESS_CID ) { + /* check if this event can be processed by UCD */ + if ( l2c_ucd_process_event (p_ccb, event, p_data) ) { + /* The event is processed by UCD state machine */ + return; + } + } +#endif + + switch (event) { + case L2CEVT_LP_DISCONNECT_IND: /* Link was disconnected */ + L2CAP_TRACE_API ("L2CAP - Calling Disconnect_Ind_Cb(), CID: 0x%04x No Conf Needed", + p_ccb->local_cid); + l2cu_release_ccb (p_ccb); + if (p_ccb->p_rcb) { + (*p_ccb->p_rcb->api.pL2CA_DisconnectInd_Cb)(local_cid, FALSE); + } + break; + + case L2CEVT_LP_QOS_VIOLATION_IND: /* QOS violation */ + /* Tell upper layer. If service guaranteed, then clear the channel */ + if (p_ccb->p_rcb->api.pL2CA_QoSViolationInd_Cb) { + (*p_ccb->p_rcb->api.pL2CA_QoSViolationInd_Cb)(p_ccb->p_lcb->remote_bd_addr); + } + break; + + case L2CEVT_L2CAP_CONFIG_REQ: /* Peer config request */ + p_cfg = (tL2CAP_CFG_INFO *)p_data; + + tempstate = p_ccb->chnl_state; + tempcfgdone = p_ccb->config_done; + p_ccb->chnl_state = CST_CONFIG; + p_ccb->config_done &= ~CFG_DONE_MASK; + + btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, L2CAP_CHNL_CFG_TIMEOUT); + + if ((cfg_result = l2cu_process_peer_cfg_req (p_ccb, p_cfg)) == L2CAP_PEER_CFG_OK) { + (*p_ccb->p_rcb->api.pL2CA_ConfigInd_Cb)(p_ccb->local_cid, p_cfg); + } + + /* Error in config parameters: reset state and config flag */ + else if (cfg_result == L2CAP_PEER_CFG_UNACCEPTABLE) { + btu_stop_timer(&p_ccb->timer_entry); + p_ccb->chnl_state = tempstate; + p_ccb->config_done = tempcfgdone; + l2cu_send_peer_config_rsp (p_ccb, p_cfg); + } else { /* L2CAP_PEER_CFG_DISCONNECT */ + /* Disconnect if channels are incompatible + * Note this should not occur if reconfigure + * since this should have never passed original config. + */ + l2cu_disconnect_chnl (p_ccb); + } + break; + + case L2CEVT_L2CAP_DISCONNECT_REQ: /* Peer disconnected request */ +// btla-specific ++ + /* Make sure we are not in sniff mode */ + { + tBTM_PM_PWR_MD settings; + memset((void *)&settings, 0, sizeof(settings)); + settings.mode = BTM_PM_MD_ACTIVE; + BTM_SetPowerMode (BTM_PM_SET_ONLY_ID, p_ccb->p_lcb->remote_bd_addr, &settings); + } +// btla-specific -- + + p_ccb->chnl_state = CST_W4_L2CA_DISCONNECT_RSP; + btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, L2CAP_CHNL_DISCONNECT_TOUT); + L2CAP_TRACE_API ("L2CAP - Calling Disconnect_Ind_Cb(), CID: 0x%04x Conf Needed", p_ccb->local_cid); + (*p_ccb->p_rcb->api.pL2CA_DisconnectInd_Cb)(p_ccb->local_cid, TRUE); + break; + + case L2CEVT_L2CAP_DATA: /* Peer data packet rcvd */ + if ((p_ccb->p_rcb) && (p_ccb->p_rcb->api.pL2CA_DataInd_Cb)) { + (*p_ccb->p_rcb->api.pL2CA_DataInd_Cb)(p_ccb->local_cid, (BT_HDR *)p_data); + } + break; + + case L2CEVT_L2CA_DISCONNECT_REQ: /* Upper wants to disconnect */ + /* Make sure we are not in sniff mode */ + { + tBTM_PM_PWR_MD settings; + memset((void *)&settings, 0, sizeof(settings)); + settings.mode = BTM_PM_MD_ACTIVE; + BTM_SetPowerMode (BTM_PM_SET_ONLY_ID, p_ccb->p_lcb->remote_bd_addr, &settings); + } + + l2cu_send_peer_disc_req (p_ccb); + p_ccb->chnl_state = CST_W4_L2CAP_DISCONNECT_RSP; + btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, L2CAP_CHNL_DISCONNECT_TOUT); + break; + + case L2CEVT_L2CA_DATA_WRITE: /* Upper layer data to send */ + l2c_enqueue_peer_data (p_ccb, (BT_HDR *)p_data); + l2c_link_check_send_pkts (p_ccb->p_lcb, NULL, NULL); + break; + + case L2CEVT_L2CA_CONFIG_REQ: /* Upper layer config req */ + p_ccb->chnl_state = CST_CONFIG; + p_ccb->config_done &= ~CFG_DONE_MASK; + l2cu_process_our_cfg_req (p_ccb, (tL2CAP_CFG_INFO *)p_data); + l2cu_send_peer_config_req (p_ccb, (tL2CAP_CFG_INFO *)p_data); + btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, L2CAP_CHNL_CFG_TIMEOUT); + break; + + case L2CEVT_TIMEOUT: + /* Process the monitor/retransmission time-outs in flow control/retrans mode */ + if (p_ccb->peer_cfg.fcr.mode == L2CAP_FCR_ERTM_MODE) { + l2c_fcr_proc_tout (p_ccb); + } + break; + + case L2CEVT_ACK_TIMEOUT: + l2c_fcr_proc_ack_tout (p_ccb); + break; + } +} + + +/******************************************************************************* +** +** Function l2c_csm_w4_l2cap_disconnect_rsp +** +** Description This function handles events when the channel is in +** CST_W4_L2CAP_DISCONNECT_RSP state. +** +** Returns void +** +*******************************************************************************/ +static void l2c_csm_w4_l2cap_disconnect_rsp (tL2C_CCB *p_ccb, UINT16 event, void *p_data) +{ + tL2CA_DISCONNECT_CFM_CB *disconnect_cfm = p_ccb->p_rcb->api.pL2CA_DisconnectCfm_Cb; + UINT16 local_cid = p_ccb->local_cid; + +#if (BT_TRACE_VERBOSE == TRUE) + L2CAP_TRACE_EVENT ("L2CAP - LCID: 0x%04x st: W4_L2CAP_DISC_RSP evt: %s", p_ccb->local_cid, l2c_csm_get_event_name (event)); +#else + L2CAP_TRACE_EVENT ("L2CAP - st: W4_L2CAP_DISC_RSP evt: %d", event); +#endif + + switch (event) { + case L2CEVT_L2CAP_DISCONNECT_RSP: /* Peer disconnect response */ + l2cu_release_ccb (p_ccb); + if (disconnect_cfm) { + L2CAP_TRACE_API ("L2CAP - Calling DisconnectCfm_Cb(), CID: 0x%04x", local_cid); + (*disconnect_cfm)(local_cid, L2CAP_DISC_OK); + } + break; + + case L2CEVT_L2CAP_DISCONNECT_REQ: /* Peer disconnect request */ + l2cu_send_peer_disc_rsp (p_ccb->p_lcb, p_ccb->remote_id, p_ccb->local_cid, p_ccb->remote_cid); + l2cu_release_ccb (p_ccb); + if (disconnect_cfm) { + L2CAP_TRACE_API ("L2CAP - Calling DisconnectCfm_Cb(), CID: 0x%04x", local_cid); + (*disconnect_cfm)(local_cid, L2CAP_DISC_OK); + } + break; + + case L2CEVT_LP_DISCONNECT_IND: /* Link was disconnected */ + case L2CEVT_TIMEOUT: /* Timeout */ + l2cu_release_ccb (p_ccb); + if (disconnect_cfm) { + L2CAP_TRACE_API ("L2CAP - Calling DisconnectCfm_Cb(), CID: 0x%04x", local_cid); + (*disconnect_cfm)(local_cid, L2CAP_DISC_TIMEOUT); + } + break; + + case L2CEVT_L2CAP_DATA: /* Peer data packet rcvd */ + case L2CEVT_L2CA_DATA_WRITE: /* Upper layer data to send */ + osi_free (p_data); + break; + } +} + + +/******************************************************************************* +** +** Function l2c_csm_w4_l2ca_disconnect_rsp +** +** Description This function handles events when the channel is in +** CST_W4_L2CA_DISCONNECT_RSP state. +** +** Returns void +** +*******************************************************************************/ +static void l2c_csm_w4_l2ca_disconnect_rsp (tL2C_CCB *p_ccb, UINT16 event, void *p_data) +{ + tL2CA_DISCONNECT_IND_CB *disconnect_ind = p_ccb->p_rcb->api.pL2CA_DisconnectInd_Cb; + UINT16 local_cid = p_ccb->local_cid; + +#if (BT_TRACE_VERBOSE == TRUE) + L2CAP_TRACE_EVENT ("L2CAP - LCID: 0x%04x st: W4_L2CA_DISC_RSP evt: %s", p_ccb->local_cid, l2c_csm_get_event_name (event)); +#else + L2CAP_TRACE_EVENT ("L2CAP - st: W4_L2CA_DISC_RSP evt: %d", event); +#endif + + switch (event) { + case L2CEVT_LP_DISCONNECT_IND: /* Link was disconnected */ + L2CAP_TRACE_API ("L2CAP - Calling Disconnect_Ind_Cb(), CID: 0x%04x No Conf Needed", p_ccb->local_cid); + l2cu_release_ccb (p_ccb); + (*disconnect_ind)(local_cid, FALSE); + break; + + case L2CEVT_TIMEOUT: + l2cu_send_peer_disc_rsp (p_ccb->p_lcb, p_ccb->remote_id, p_ccb->local_cid, p_ccb->remote_cid); + L2CAP_TRACE_API ("L2CAP - Calling Disconnect_Ind_Cb(), CID: 0x%04x No Conf Needed", p_ccb->local_cid); + l2cu_release_ccb (p_ccb); + (*disconnect_ind)(local_cid, FALSE); + break; + + case L2CEVT_L2CA_DISCONNECT_REQ: /* Upper disconnect request */ + case L2CEVT_L2CA_DISCONNECT_RSP: /* Upper disconnect response */ + l2cu_send_peer_disc_rsp (p_ccb->p_lcb, p_ccb->remote_id, p_ccb->local_cid, p_ccb->remote_cid); + l2cu_release_ccb (p_ccb); + break; + + case L2CEVT_L2CAP_DATA: /* Peer data packet rcvd */ + case L2CEVT_L2CA_DATA_WRITE: /* Upper layer data to send */ + osi_free (p_data); + break; + } +} +#endif /// (L2CAP_COC_INCLUDED == TRUE) + +#if (BT_TRACE_VERBOSE == TRUE) +/******************************************************************************* +** +** Function l2c_csm_get_event_name +** +** Description This function returns the event name. +** +** NOTE conditionally compiled to save memory. +** +** Returns pointer to the name +** +*******************************************************************************/ +static char *l2c_csm_get_event_name (UINT16 event) +{ + switch (event) { + case L2CEVT_LP_CONNECT_CFM: /* Lower layer connect confirm */ + return ("LOWER_LAYER_CONNECT_CFM"); + case L2CEVT_LP_CONNECT_CFM_NEG: /* Lower layer connect confirm (failed) */ + return ("LOWER_LAYER_CONNECT_CFM_NEG"); + case L2CEVT_LP_CONNECT_IND: /* Lower layer connect indication */ + return ("LOWER_LAYER_CONNECT_IND"); + case L2CEVT_LP_DISCONNECT_IND: /* Lower layer disconnect indication */ + return ("LOWER_LAYER_DISCONNECT_IND"); + case L2CEVT_LP_QOS_CFM: /* Lower layer QOS confirmation */ + return ("LOWER_LAYER_QOS_CFM"); + case L2CEVT_LP_QOS_CFM_NEG: /* Lower layer QOS confirmation (failed)*/ + return ("LOWER_LAYER_QOS_CFM_NEG"); + case L2CEVT_LP_QOS_VIOLATION_IND: /* Lower layer QOS violation indication */ + return ("LOWER_LAYER_QOS_VIOLATION_IND"); + + case L2CEVT_SEC_COMP: /* Security cleared successfully */ + return ("SECURITY_COMPLETE"); + case L2CEVT_SEC_COMP_NEG: /* Security procedure failed */ + return ("SECURITY_COMPLETE_NEG"); + + case L2CEVT_L2CAP_CONNECT_REQ: /* Peer connection request */ + return ("PEER_CONNECT_REQ"); + case L2CEVT_L2CAP_CONNECT_RSP: /* Peer connection response */ + return ("PEER_CONNECT_RSP"); + case L2CEVT_L2CAP_CONNECT_RSP_PND: /* Peer connection response pending */ + return ("PEER_CONNECT_RSP_PND"); + case L2CEVT_L2CAP_CONNECT_RSP_NEG: /* Peer connection response (failed) */ + return ("PEER_CONNECT_RSP_NEG"); + case L2CEVT_L2CAP_CONFIG_REQ: /* Peer configuration request */ + return ("PEER_CONFIG_REQ"); + case L2CEVT_L2CAP_CONFIG_RSP: /* Peer configuration response */ + return ("PEER_CONFIG_RSP"); + case L2CEVT_L2CAP_CONFIG_RSP_NEG: /* Peer configuration response (failed) */ + return ("PEER_CONFIG_RSP_NEG"); + case L2CEVT_L2CAP_DISCONNECT_REQ: /* Peer disconnect request */ + return ("PEER_DISCONNECT_REQ"); + case L2CEVT_L2CAP_DISCONNECT_RSP: /* Peer disconnect response */ + return ("PEER_DISCONNECT_RSP"); + case L2CEVT_L2CAP_DATA: /* Peer data */ + return ("PEER_DATA"); + + case L2CEVT_L2CA_CONNECT_REQ: /* Upper layer connect request */ + return ("UPPER_LAYER_CONNECT_REQ"); + case L2CEVT_L2CA_CONNECT_RSP: /* Upper layer connect response */ + return ("UPPER_LAYER_CONNECT_RSP"); + case L2CEVT_L2CA_CONNECT_RSP_NEG: /* Upper layer connect response (failed)*/ + return ("UPPER_LAYER_CONNECT_RSP_NEG"); + case L2CEVT_L2CA_CONFIG_REQ: /* Upper layer config request */ + return ("UPPER_LAYER_CONFIG_REQ"); + case L2CEVT_L2CA_CONFIG_RSP: /* Upper layer config response */ + return ("UPPER_LAYER_CONFIG_RSP"); + case L2CEVT_L2CA_CONFIG_RSP_NEG: /* Upper layer config response (failed) */ + return ("UPPER_LAYER_CONFIG_RSP_NEG"); + case L2CEVT_L2CA_DISCONNECT_REQ: /* Upper layer disconnect request */ + return ("UPPER_LAYER_DISCONNECT_REQ"); + case L2CEVT_L2CA_DISCONNECT_RSP: /* Upper layer disconnect response */ + return ("UPPER_LAYER_DISCONNECT_RSP"); + case L2CEVT_L2CA_DATA_READ: /* Upper layer data read */ + return ("UPPER_LAYER_DATA_READ"); + case L2CEVT_L2CA_DATA_WRITE: /* Upper layer data write */ + return ("UPPER_LAYER_DATA_WRITE"); + case L2CEVT_TIMEOUT: /* Timeout */ + return ("TIMEOUT"); + case L2CEVT_SEC_RE_SEND_CMD: + return ("SEC_RE_SEND_CMD"); + case L2CEVT_L2CAP_INFO_RSP: /* Peer information response */ + return ("L2CEVT_L2CAP_INFO_RSP"); + case L2CEVT_ACK_TIMEOUT: + return ("L2CEVT_ACK_TIMEOUT"); + + default: + return ("???? UNKNOWN EVENT"); + } +} +#endif /* (BT_TRACE_VERBOSE == TRUE) */ + + +/******************************************************************************* +** +** Function l2c_enqueue_peer_data +** +** Description Enqueues data destined for the peer in the ccb. Handles +** FCR segmentation and checks for congestion. +** +** Returns void +** +*******************************************************************************/ +void l2c_enqueue_peer_data (tL2C_CCB *p_ccb, BT_HDR *p_buf) +{ + UINT8 *p; + + if (p_ccb->peer_cfg.fcr.mode != L2CAP_FCR_BASIC_MODE) { + p_buf->event = 0; + } else { + /* Save the channel ID for faster counting */ + p_buf->event = p_ccb->local_cid; + + /* Step back to add the L2CAP header */ + p_buf->offset -= L2CAP_PKT_OVERHEAD; + p_buf->len += L2CAP_PKT_OVERHEAD; + + /* Set the pointer to the beginning of the data */ + p = (UINT8 *)(p_buf + 1) + p_buf->offset; + + /* Now the L2CAP header */ + UINT16_TO_STREAM (p, p_buf->len - L2CAP_PKT_OVERHEAD); + UINT16_TO_STREAM (p, p_ccb->remote_cid); + } + + fixed_queue_enqueue(p_ccb->xmit_hold_q, p_buf, FIXED_QUEUE_MAX_TIMEOUT); + + l2cu_check_channel_congestion (p_ccb); + +#if (L2CAP_ROUND_ROBIN_CHANNEL_SERVICE == TRUE) + /* if new packet is higher priority than serving ccb and it is not overrun */ + if (( p_ccb->p_lcb->rr_pri > p_ccb->ccb_priority ) + && ( p_ccb->p_lcb->rr_serv[p_ccb->ccb_priority].quota > 0)) { + /* send out higher priority packet */ + p_ccb->p_lcb->rr_pri = p_ccb->ccb_priority; + } +#endif + + /* if we are doing a round robin scheduling, set the flag */ + if (p_ccb->p_lcb->link_xmit_quota == 0) { + l2cb.check_round_robin = TRUE; + } +} diff --git a/lib/bt/host/bluedroid/stack/l2cap/l2c_fcr.c b/lib/bt/host/bluedroid/stack/l2cap/l2c_fcr.c new file mode 100644 index 00000000..1da2459d --- /dev/null +++ b/lib/bt/host/bluedroid/stack/l2cap/l2c_fcr.c @@ -0,0 +1,2229 @@ +/****************************************************************************** + * + * Copyright (C) 2004-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains the L2CAP 1.2 Flow Control and retransmissions + * functions + * + ******************************************************************************/ + +#include +#include +#include +#include "common/bt_trace.h" +#include "stack/bt_types.h" +#include "stack/hcimsgs.h" +#include "stack/l2c_api.h" +#include "l2c_int.h" +#include "stack/l2cdefs.h" +#include "stack/btm_api.h" +#include "btm_int.h" +#include "stack/btu.h" +#include "osi/allocator.h" + +#if (L2CAP_COC_INCLUDED == TRUE) + +/* Flag passed to retransmit_i_frames() when all packets should be retransmitted */ +#define L2C_FCR_RETX_ALL_PKTS 0xFF + +#if BT_TRACE_VERBOSE == TRUE +static char *SAR_types[] = { "Unsegmented", "Start", "End", "Continuation" }; +static char *SUP_types[] = { "RR", "REJ", "RNR", "SREJ" }; +#endif + +/* Look-up table for the CRC calculation */ +static const unsigned short crctab[256] = { + 0x0000, 0xc0c1, 0xc181, 0x0140, 0xc301, 0x03c0, 0x0280, 0xc241, + 0xc601, 0x06c0, 0x0780, 0xc741, 0x0500, 0xc5c1, 0xc481, 0x0440, + 0xcc01, 0x0cc0, 0x0d80, 0xcd41, 0x0f00, 0xcfc1, 0xce81, 0x0e40, + 0x0a00, 0xcac1, 0xcb81, 0x0b40, 0xc901, 0x09c0, 0x0880, 0xc841, + 0xd801, 0x18c0, 0x1980, 0xd941, 0x1b00, 0xdbc1, 0xda81, 0x1a40, + 0x1e00, 0xdec1, 0xdf81, 0x1f40, 0xdd01, 0x1dc0, 0x1c80, 0xdc41, + 0x1400, 0xd4c1, 0xd581, 0x1540, 0xd701, 0x17c0, 0x1680, 0xd641, + 0xd201, 0x12c0, 0x1380, 0xd341, 0x1100, 0xd1c1, 0xd081, 0x1040, + 0xf001, 0x30c0, 0x3180, 0xf141, 0x3300, 0xf3c1, 0xf281, 0x3240, + 0x3600, 0xf6c1, 0xf781, 0x3740, 0xf501, 0x35c0, 0x3480, 0xf441, + 0x3c00, 0xfcc1, 0xfd81, 0x3d40, 0xff01, 0x3fc0, 0x3e80, 0xfe41, + 0xfa01, 0x3ac0, 0x3b80, 0xfb41, 0x3900, 0xf9c1, 0xf881, 0x3840, + 0x2800, 0xe8c1, 0xe981, 0x2940, 0xeb01, 0x2bc0, 0x2a80, 0xea41, + 0xee01, 0x2ec0, 0x2f80, 0xef41, 0x2d00, 0xedc1, 0xec81, 0x2c40, + 0xe401, 0x24c0, 0x2580, 0xe541, 0x2700, 0xe7c1, 0xe681, 0x2640, + 0x2200, 0xe2c1, 0xe381, 0x2340, 0xe101, 0x21c0, 0x2080, 0xe041, + 0xa001, 0x60c0, 0x6180, 0xa141, 0x6300, 0xa3c1, 0xa281, 0x6240, + 0x6600, 0xa6c1, 0xa781, 0x6740, 0xa501, 0x65c0, 0x6480, 0xa441, + 0x6c00, 0xacc1, 0xad81, 0x6d40, 0xaf01, 0x6fc0, 0x6e80, 0xae41, + 0xaa01, 0x6ac0, 0x6b80, 0xab41, 0x6900, 0xa9c1, 0xa881, 0x6840, + 0x7800, 0xb8c1, 0xb981, 0x7940, 0xbb01, 0x7bc0, 0x7a80, 0xba41, + 0xbe01, 0x7ec0, 0x7f80, 0xbf41, 0x7d00, 0xbdc1, 0xbc81, 0x7c40, + 0xb401, 0x74c0, 0x7580, 0xb541, 0x7700, 0xb7c1, 0xb681, 0x7640, + 0x7200, 0xb2c1, 0xb381, 0x7340, 0xb101, 0x71c0, 0x7080, 0xb041, + 0x5000, 0x90c1, 0x9181, 0x5140, 0x9301, 0x53c0, 0x5280, 0x9241, + 0x9601, 0x56c0, 0x5780, 0x9741, 0x5500, 0x95c1, 0x9481, 0x5440, + 0x9c01, 0x5cc0, 0x5d80, 0x9d41, 0x5f00, 0x9fc1, 0x9e81, 0x5e40, + 0x5a00, 0x9ac1, 0x9b81, 0x5b40, 0x9901, 0x59c0, 0x5880, 0x9841, + 0x8801, 0x48c0, 0x4980, 0x8941, 0x4b00, 0x8bc1, 0x8a81, 0x4a40, + 0x4e00, 0x8ec1, 0x8f81, 0x4f40, 0x8d01, 0x4dc0, 0x4c80, 0x8c41, + 0x4400, 0x84c1, 0x8581, 0x4540, 0x8701, 0x47c0, 0x4680, 0x8641, + 0x8201, 0x42c0, 0x4380, 0x8341, 0x4100, 0x81c1, 0x8081, 0x4040, +}; + + +/******************************************************************************* +** Static local functions +*/ +static BOOLEAN process_reqseq (tL2C_CCB *p_ccb, UINT16 ctrl_word); +static void process_s_frame (tL2C_CCB *p_ccb, BT_HDR *p_buf, UINT16 ctrl_word); +static void process_i_frame (tL2C_CCB *p_ccb, BT_HDR *p_buf, UINT16 ctrl_word, BOOLEAN delay_ack); +static BOOLEAN retransmit_i_frames (tL2C_CCB *p_ccb, UINT8 tx_seq); +static void prepare_I_frame (tL2C_CCB *p_ccb, BT_HDR *p_buf, BOOLEAN is_retransmission); +static void process_stream_frame (tL2C_CCB *p_ccb, BT_HDR *p_buf); +static BOOLEAN do_sar_reassembly (tL2C_CCB *p_ccb, BT_HDR *p_buf, UINT16 ctrl_word); + +#if (L2CAP_ERTM_STATS == TRUE) +static void l2c_fcr_collect_ack_delay (tL2C_CCB *p_ccb, UINT8 num_bufs_acked); +#endif + +/******************************************************************************* +** +** Function l2c_fcr_updcrc +** +** Description This function computes the CRC using the look-up table. +** +** Returns CRC +** +*******************************************************************************/ +static unsigned short l2c_fcr_updcrc(unsigned short icrc, unsigned char *icp, int icnt) +{ + register unsigned short crc = icrc; + register unsigned char *cp = icp; + register int cnt = icnt; + + while (cnt--) { + crc = ((crc >> 8) & 0xff) ^ crctab[(crc & 0xff) ^ *cp++]; + } + + return (crc); +} + + +/******************************************************************************* +** +** Function l2c_fcr_tx_get_fcs +** +** Description This function computes the CRC for a frame to be TXed. +** +** Returns CRC +** +*******************************************************************************/ +static UINT16 l2c_fcr_tx_get_fcs (BT_HDR *p_buf) +{ + UINT8 *p = ((UINT8 *) (p_buf + 1)) + p_buf->offset; + + return (l2c_fcr_updcrc (L2CAP_FCR_INIT_CRC, p, p_buf->len)); +} + +/******************************************************************************* +** +** Function l2c_fcr_rx_get_fcs +** +** Description This function computes the CRC for a received frame. +** +** Returns CRC +** +*******************************************************************************/ +static UINT16 l2c_fcr_rx_get_fcs (BT_HDR *p_buf) +{ + UINT8 *p = ((UINT8 *) (p_buf + 1)) + p_buf->offset; + + /* offset points past the L2CAP header, but the CRC check includes it */ + p -= L2CAP_PKT_OVERHEAD; + + return (l2c_fcr_updcrc (L2CAP_FCR_INIT_CRC, p, p_buf->len + L2CAP_PKT_OVERHEAD)); +} + +/******************************************************************************* +** +** Function l2c_fcr_start_timer +** +** Description This function starts the (monitor or retransmission) timer. +** +** Returns - +** +*******************************************************************************/ +void l2c_fcr_start_timer (tL2C_CCB *p_ccb) +{ + assert(p_ccb != NULL); + UINT32 tout; + + /* The timers which are in milliseconds */ + if (p_ccb->fcrb.wait_ack) { + tout = (UINT32)p_ccb->our_cfg.fcr.mon_tout; + } else { + tout = (UINT32)p_ccb->our_cfg.fcr.rtrans_tout; + } + + /* Only start a timer that was not started */ + if (p_ccb->fcrb.mon_retrans_timer.in_use == 0) { + btu_start_quick_timer (&p_ccb->fcrb.mon_retrans_timer, BTU_TTYPE_L2CAP_CHNL, tout * QUICK_TIMER_TICKS_PER_SEC / 1000); + } +} + +/******************************************************************************* +** +** Function l2c_fcr_stop_timer +** +** Description This function stops the (monitor or transmission) timer. +** +** Returns - +** +*******************************************************************************/ +void l2c_fcr_stop_timer (tL2C_CCB *p_ccb) +{ + assert(p_ccb != NULL); + if (p_ccb->fcrb.mon_retrans_timer.in_use) { + btu_stop_quick_timer (&p_ccb->fcrb.mon_retrans_timer); + } +} + +/******************************************************************************* +** +** Function l2c_fcr_free_timer +** +** Description This function releases the (monitor or transmission) timer. +** +** Returns - +** +*******************************************************************************/ +void l2c_fcr_free_timer (tL2C_CCB *p_ccb) +{ + assert(p_ccb != NULL); + btu_free_quick_timer (&p_ccb->fcrb.mon_retrans_timer); +} + +/******************************************************************************* +** +** Function l2c_fcr_cleanup +** +** Description This function cleans up the variable used for flow-control/retrans. +** +** Returns - +** +*******************************************************************************/ +void l2c_fcr_cleanup (tL2C_CCB *p_ccb) +{ + assert(p_ccb != NULL); + tL2C_FCRB *p_fcrb = &p_ccb->fcrb; + + l2c_fcr_stop_timer (p_ccb); + + if (p_fcrb->p_rx_sdu) { + osi_free(p_fcrb->p_rx_sdu); + p_fcrb->p_rx_sdu = NULL; + } + + + fixed_queue_free(p_fcrb->waiting_for_ack_q, osi_free_func); + p_fcrb->waiting_for_ack_q = NULL; + + fixed_queue_free(p_fcrb->srej_rcv_hold_q, osi_free_func); + p_fcrb->srej_rcv_hold_q = NULL; + + fixed_queue_free(p_fcrb->retrans_q, osi_free_func); + p_fcrb->retrans_q = NULL; + + btu_free_quick_timer (&p_fcrb->ack_timer); + memset(&p_fcrb->ack_timer, 0, sizeof(TIMER_LIST_ENT)); + + btu_free_quick_timer (&p_ccb->fcrb.mon_retrans_timer); + memset(&p_fcrb->mon_retrans_timer, 0, sizeof(TIMER_LIST_ENT)); + +#if (L2CAP_ERTM_STATS == TRUE) + if ( (p_ccb->local_cid >= L2CAP_BASE_APPL_CID) && (p_ccb->peer_cfg.fcr.mode == L2CAP_FCR_ERTM_MODE) ) { + UINT32 dur = osi_time_get_os_boottime_ms() - p_ccb->fcrb.connect_tick_count; + char *p_str = (char *)osi_malloc(120); + UINT16 i; + UINT32 throughput_avg, ack_delay_avg, ack_q_count_avg; + + BT_TRACE(TRACE_CTRL_GENERAL | TRACE_LAYER_GKI | TRACE_ORG_GKI , TRACE_TYPE_GENERIC, + "--- L2CAP ERTM Stats for CID: 0x%04x Duration: %08ums", p_ccb->local_cid, dur); + BT_TRACE(TRACE_CTRL_GENERAL | TRACE_LAYER_GKI | TRACE_ORG_GKI , TRACE_TYPE_GENERIC, + "Retransmissions:%08u Times Flow Controlled:%08u Retrans Touts:%08u Ack Touts:%08u", + p_ccb->fcrb.pkts_retransmitted, p_ccb->fcrb.xmit_window_closed, p_ccb->fcrb.retrans_touts, p_ccb->fcrb.xmit_ack_touts); + BT_TRACE(TRACE_CTRL_GENERAL | TRACE_LAYER_GKI | TRACE_ORG_GKI , TRACE_TYPE_GENERIC, + "Times there is less than 2 packets in controller when flow controlled:%08u", p_ccb->fcrb.controller_idle); + BT_TRACE(TRACE_CTRL_GENERAL | TRACE_LAYER_GKI | TRACE_ORG_GKI , TRACE_TYPE_GENERIC, + "max_held_acks:%08u, in_cfg.fcr.tx_win_sz:%08u", p_ccb->fcrb.max_held_acks, p_ccb->peer_cfg.fcr.tx_win_sz ); + if (p_str) { + sprintf(p_str, "Sent Pkts:%08u Bytes:%10u(%06u/sec) RR:%08u REJ:%08u RNR:%08u SREJ:%08u", + p_ccb->fcrb.ertm_pkt_counts[0], p_ccb->fcrb.ertm_byte_counts[0], + (dur >= 10 ? (p_ccb->fcrb.ertm_byte_counts[0] * 100) / (dur / 10) : 0), + p_ccb->fcrb.s_frames_sent[0], p_ccb->fcrb.s_frames_sent[1], p_ccb->fcrb.s_frames_sent[2], p_ccb->fcrb.s_frames_sent[3]); + + BT_TRACE(TRACE_CTRL_GENERAL | TRACE_LAYER_GKI | TRACE_ORG_GKI , TRACE_TYPE_GENERIC, "%s", p_str); + + sprintf(p_str, "Rcvd Pkts:%08u Bytes:%10u(%06u/sec) RR:%08u REJ:%08u RNR:%08u SREJ:%08u", + p_ccb->fcrb.ertm_pkt_counts[1], p_ccb->fcrb.ertm_byte_counts[1], + (dur >= 10 ? (p_ccb->fcrb.ertm_byte_counts[1] * 100) / (dur / 10) : 0), + p_ccb->fcrb.s_frames_rcvd[0], p_ccb->fcrb.s_frames_rcvd[1], p_ccb->fcrb.s_frames_rcvd[2], p_ccb->fcrb.s_frames_rcvd[3]); + + BT_TRACE(TRACE_CTRL_GENERAL | TRACE_LAYER_GKI | TRACE_ORG_GKI , TRACE_TYPE_GENERIC, "%s", p_str); + + throughput_avg = 0; + ack_delay_avg = 0; + ack_q_count_avg = 0; + + for (i = 0; i < L2CAP_ERTM_STATS_NUM_AVG; i++ ) { + if (i == p_ccb->fcrb.ack_delay_avg_index ) { + BT_TRACE(TRACE_CTRL_GENERAL | TRACE_LAYER_GKI | TRACE_ORG_GKI , TRACE_TYPE_GENERIC, + "[%02u] collecting data ...", i ); + continue; + } + + sprintf(p_str, "[%02u] throughput: %5u, ack_delay avg:%3u, min:%3u, max:%3u, ack_q_count avg:%3u, min:%3u, max:%3u", + i, p_ccb->fcrb.throughput[i], + p_ccb->fcrb.ack_delay_avg[i], p_ccb->fcrb.ack_delay_min[i], p_ccb->fcrb.ack_delay_max[i], + p_ccb->fcrb.ack_q_count_avg[i], p_ccb->fcrb.ack_q_count_min[i], p_ccb->fcrb.ack_q_count_max[i] ); + + BT_TRACE(TRACE_CTRL_GENERAL | TRACE_LAYER_GKI | TRACE_ORG_GKI , TRACE_TYPE_GENERIC, "%s", p_str); + + throughput_avg += p_ccb->fcrb.throughput[i]; + ack_delay_avg += p_ccb->fcrb.ack_delay_avg[i]; + ack_q_count_avg += p_ccb->fcrb.ack_q_count_avg[i]; + } + + throughput_avg /= (L2CAP_ERTM_STATS_NUM_AVG - 1); + ack_delay_avg /= (L2CAP_ERTM_STATS_NUM_AVG - 1); + ack_q_count_avg /= (L2CAP_ERTM_STATS_NUM_AVG - 1); + + BT_TRACE(TRACE_CTRL_GENERAL | TRACE_LAYER_GKI | TRACE_ORG_GKI , TRACE_TYPE_GENERIC, + "throughput_avg: %8u (kbytes/sec), ack_delay_avg: %8u ms, ack_q_count_avg: %8u", + throughput_avg, ack_delay_avg, ack_q_count_avg ); + + osi_free(p_str); + } + + BT_TRACE(TRACE_CTRL_GENERAL | TRACE_LAYER_GKI | TRACE_ORG_GKI , TRACE_TYPE_GENERIC, + "---"); + } +#endif + + memset (p_fcrb, 0, sizeof (tL2C_FCRB)); +} + +/******************************************************************************* +** +** Function l2c_fcr_clone_buf +** +** Description This function allocates and copies requested part of a buffer +** at a new-offset. +** +** Returns pointer to new buffer +** +*******************************************************************************/ +BT_HDR *l2c_fcr_clone_buf (BT_HDR *p_buf, UINT16 new_offset, UINT16 no_of_bytes) +{ + assert(p_buf != NULL); + /* + * NOTE: We allocate extra L2CAP_FCS_LEN octets, in case we need to put + * the FCS (Frame Check Sequence) at the end of the buffer. + */ + uint16_t buf_size = no_of_bytes + sizeof(BT_HDR) + new_offset + L2CAP_FCS_LEN; +#if (L2CAP_ERTM_STATS == TRUE) + /* + * NOTE: If L2CAP_ERTM_STATS is enabled, we need 4 extra octets at the + * end for a timestamp at the end of an I-frame. + */ + buf_size += sizeof(uint32_t); +#endif + BT_HDR *p_buf2 = (BT_HDR *)osi_malloc(buf_size); + + p_buf2->offset = new_offset; + p_buf2->len = no_of_bytes; + memcpy(((UINT8 *)(p_buf2 + 1)) + p_buf2->offset, + ((UINT8 *)(p_buf + 1)) + p_buf->offset, + no_of_bytes); + + return (p_buf2); +} + +/******************************************************************************* +** +** Function l2c_fcr_is_flow_controlled +** +** Description This function checks if the CCB is flow controlled by peer. +** +** Returns The control word +** +*******************************************************************************/ +BOOLEAN l2c_fcr_is_flow_controlled (tL2C_CCB *p_ccb) +{ + assert(p_ccb != NULL); + if (p_ccb->peer_cfg.fcr.mode == L2CAP_FCR_ERTM_MODE) { + /* Check if remote side flowed us off or the transmit window is full */ + if ( (p_ccb->fcrb.remote_busy == TRUE) + || (fixed_queue_length(p_ccb->fcrb.waiting_for_ack_q) >= p_ccb->peer_cfg.fcr.tx_win_sz) ) { +#if (L2CAP_ERTM_STATS == TRUE) + if (!fixed_queue_is_empty(p_ccb->xmit_hold_q)) { + p_ccb->fcrb.xmit_window_closed++; + + if ((p_ccb->p_lcb->sent_not_acked < 2) && (l2cb.controller_xmit_window > 0)) { + p_ccb->fcrb.controller_idle++; + } + } +#endif + return (TRUE); + } + } + return (FALSE); +} + +/******************************************************************************* +** +** Function prepare_I_frame +** +** Description This function sets the FCR variables in an I-frame that is +** about to be sent to HCI for transmission. This may be the +** first time the I-frame is sent, or a retransmission +** +** Returns - +** +*******************************************************************************/ +static void prepare_I_frame (tL2C_CCB *p_ccb, BT_HDR *p_buf, BOOLEAN is_retransmission) +{ + assert(p_ccb != NULL); + assert(p_buf != NULL); + tL2C_FCRB *p_fcrb = &p_ccb->fcrb; + UINT8 *p; + UINT16 fcs; + UINT16 ctrl_word; + BOOLEAN set_f_bit = p_fcrb->send_f_rsp; + + p_fcrb->send_f_rsp = FALSE; + + if (is_retransmission) { + /* Get the old control word and clear out the old req_seq and F bits */ + p = ((UINT8 *) (p_buf + 1)) + p_buf->offset + L2CAP_PKT_OVERHEAD; + + STREAM_TO_UINT16 (ctrl_word, p); + + ctrl_word &= ~(L2CAP_FCR_REQ_SEQ_BITS + L2CAP_FCR_F_BIT); + } else { + ctrl_word = p_buf->layer_specific & L2CAP_FCR_SEG_BITS; /* SAR bits */ + ctrl_word |= (p_fcrb->next_tx_seq << L2CAP_FCR_TX_SEQ_BITS_SHIFT); /* Tx Seq */ + + p_fcrb->next_tx_seq = (p_fcrb->next_tx_seq + 1) & L2CAP_FCR_SEQ_MODULO; + } + + /* Set the F-bit and reqseq only if using re-transmission mode */ + if (p_ccb->peer_cfg.fcr.mode == L2CAP_FCR_ERTM_MODE) { + if (set_f_bit) { + ctrl_word |= L2CAP_FCR_F_BIT; + } + + ctrl_word |= (p_fcrb->next_seq_expected) << L2CAP_FCR_REQ_SEQ_BITS_SHIFT; + + p_fcrb->last_ack_sent = p_ccb->fcrb.next_seq_expected; + + if (p_ccb->fcrb.ack_timer.in_use) { + btu_stop_quick_timer (&p_ccb->fcrb.ack_timer); + } + } + + /* Set the control word */ + p = ((UINT8 *) (p_buf + 1)) + p_buf->offset + L2CAP_PKT_OVERHEAD; + + UINT16_TO_STREAM (p, ctrl_word); + + /* Compute the FCS and add to the end of the buffer if not bypassed */ + if (p_ccb->bypass_fcs != L2CAP_BYPASS_FCS) { + /* length field in l2cap header has to include FCS length */ + p = ((UINT8 *) (p_buf + 1)) + p_buf->offset; + UINT16_TO_STREAM (p, p_buf->len + L2CAP_FCS_LEN - L2CAP_PKT_OVERHEAD); + + /* Calculate the FCS */ + fcs = l2c_fcr_tx_get_fcs(p_buf); + + /* Point to the end of the buffer and put the FCS there */ + p = ((UINT8 *) (p_buf + 1)) + p_buf->offset + p_buf->len; + + UINT16_TO_STREAM (p, fcs); + + p_buf->len += L2CAP_FCS_LEN; + } + +#if BT_TRACE_VERBOSE == TRUE + if (is_retransmission) { + L2CAP_TRACE_EVENT ("L2CAP eRTM ReTx I-frame CID: 0x%04x Len: %u SAR: %s TxSeq: %u ReqSeq: %u F: %u", + p_ccb->local_cid, p_buf->len, + SAR_types[(ctrl_word & L2CAP_FCR_SAR_BITS) >> L2CAP_FCR_SAR_BITS_SHIFT], + (ctrl_word & L2CAP_FCR_TX_SEQ_BITS) >> L2CAP_FCR_TX_SEQ_BITS_SHIFT, + (ctrl_word & L2CAP_FCR_REQ_SEQ_BITS) >> L2CAP_FCR_REQ_SEQ_BITS_SHIFT, + (ctrl_word & L2CAP_FCR_F_BIT) >> L2CAP_FCR_F_BIT_SHIFT); + } else { + L2CAP_TRACE_EVENT ("L2CAP eRTM Tx I-frame CID: 0x%04x Len: %u SAR: %-12s TxSeq: %u ReqSeq: %u F: %u", + p_ccb->local_cid, p_buf->len, + SAR_types[(ctrl_word & L2CAP_FCR_SAR_BITS) >> L2CAP_FCR_SAR_BITS_SHIFT], + (ctrl_word & L2CAP_FCR_TX_SEQ_BITS) >> L2CAP_FCR_TX_SEQ_BITS_SHIFT, + (ctrl_word & L2CAP_FCR_REQ_SEQ_BITS) >> L2CAP_FCR_REQ_SEQ_BITS_SHIFT, + (ctrl_word & L2CAP_FCR_F_BIT) >> L2CAP_FCR_F_BIT_SHIFT); + } +#endif + + /* Start the retransmission timer if not already running */ + if (p_ccb->peer_cfg.fcr.mode == L2CAP_FCR_ERTM_MODE) { + l2c_fcr_start_timer (p_ccb); + } +} + +/******************************************************************************* +** +** Function l2c_fcr_send_S_frame +** +** Description This function formats and sends an S-frame for transmission. +** +** Returns - +** +*******************************************************************************/ +void l2c_fcr_send_S_frame (tL2C_CCB *p_ccb, UINT16 function_code, UINT16 pf_bit) +{ + assert(p_ccb != NULL); + BT_HDR *p_buf; + UINT8 *p; + UINT16 ctrl_word; + UINT16 fcs; + + if ((!p_ccb->in_use) || (p_ccb->chnl_state != CST_OPEN)) { + return; + } + +#if (L2CAP_ERTM_STATS == TRUE) + p_ccb->fcrb.s_frames_sent[function_code]++; +#endif + + if (pf_bit == L2CAP_FCR_P_BIT) { + p_ccb->fcrb.wait_ack = TRUE; + + l2c_fcr_stop_timer (p_ccb); /* Restart the monitor timer */ + l2c_fcr_start_timer (p_ccb); + } + + /* Create the control word to use */ + ctrl_word = (function_code << L2CAP_FCR_SUP_SHIFT) | L2CAP_FCR_S_FRAME_BIT; + ctrl_word |= (p_ccb->fcrb.next_seq_expected << L2CAP_FCR_REQ_SEQ_BITS_SHIFT); + ctrl_word |= pf_bit; + + if ((p_buf = (BT_HDR *)osi_malloc(L2CAP_CMD_BUF_SIZE)) != NULL) { + p_buf->offset = HCI_DATA_PREAMBLE_SIZE; + p_buf->len = L2CAP_PKT_OVERHEAD + L2CAP_FCR_OVERHEAD; + + /* Set the pointer to the beginning of the data */ + p = (UINT8 *)(p_buf + 1) + p_buf->offset; + + /* Put in the L2CAP header */ + UINT16_TO_STREAM (p, L2CAP_FCR_OVERHEAD + L2CAP_FCS_LEN); + UINT16_TO_STREAM (p, p_ccb->remote_cid); + UINT16_TO_STREAM (p, ctrl_word); + + /* Compute the FCS and add to the end of the buffer if not bypassed */ + if (p_ccb->bypass_fcs != L2CAP_BYPASS_FCS) { + fcs = l2c_fcr_tx_get_fcs (p_buf); + + UINT16_TO_STREAM (p, fcs); + p_buf->len += L2CAP_FCS_LEN; + } else { /* rewrite the length without FCS length */ + p -= 6; + UINT16_TO_STREAM (p, L2CAP_FCR_OVERHEAD); + } + + /* Now, the HCI transport header */ + p_buf->layer_specific = L2CAP_NON_FLUSHABLE_PKT; + l2cu_set_acl_hci_header (p_buf, p_ccb); + +#if BT_TRACE_VERBOSE == TRUE + if ((((ctrl_word & L2CAP_FCR_SUP_BITS) >> L2CAP_FCR_SUP_SHIFT) == 1) + || (((ctrl_word & L2CAP_FCR_SUP_BITS) >> L2CAP_FCR_SUP_SHIFT) == 3)) { + L2CAP_TRACE_WARNING ("L2CAP eRTM Tx S-frame CID: 0x%04x ctrlword: 0x%04x Type: %s ReqSeq: %u P: %u F: %u", + p_ccb->local_cid, ctrl_word, + SUP_types[(ctrl_word & L2CAP_FCR_SUP_BITS) >> L2CAP_FCR_SUP_SHIFT], + (ctrl_word & L2CAP_FCR_REQ_SEQ_BITS) >> L2CAP_FCR_REQ_SEQ_BITS_SHIFT, + (ctrl_word & L2CAP_FCR_P_BIT) >> L2CAP_FCR_P_BIT_SHIFT, + (ctrl_word & L2CAP_FCR_F_BIT) >> L2CAP_FCR_F_BIT_SHIFT); + L2CAP_TRACE_WARNING (" Buf Len: %u", p_buf->len); + } else { + L2CAP_TRACE_EVENT ("L2CAP eRTM Tx S-frame CID: 0x%04x ctrlword: 0x%04x Type: %s ReqSeq: %u P: %u F: %u", + p_ccb->local_cid, ctrl_word, + SUP_types[(ctrl_word & L2CAP_FCR_SUP_BITS) >> L2CAP_FCR_SUP_SHIFT], + (ctrl_word & L2CAP_FCR_REQ_SEQ_BITS) >> L2CAP_FCR_REQ_SEQ_BITS_SHIFT, + (ctrl_word & L2CAP_FCR_P_BIT) >> L2CAP_FCR_P_BIT_SHIFT, + (ctrl_word & L2CAP_FCR_F_BIT) >> L2CAP_FCR_F_BIT_SHIFT); + L2CAP_TRACE_EVENT (" Buf Len: %u", p_buf->len); + } +#endif /* BT_TRACE_VERBOSE */ + + l2c_link_check_send_pkts (p_ccb->p_lcb, NULL, p_buf); + + p_ccb->fcrb.last_ack_sent = p_ccb->fcrb.next_seq_expected; + + if (p_ccb->fcrb.ack_timer.in_use) { + btu_stop_quick_timer (&p_ccb->fcrb.ack_timer); + } + } else { + L2CAP_TRACE_ERROR ("l2c_fcr_send_S_frame(No Resources) cid 0x%04x, Type: 0x%4x", + p_ccb->local_cid, function_code); + } +} + + +/******************************************************************************* +** +** Function l2c_fcr_proc_pdu +** +** Description This function is the entry point for processing of a +** received PDU when in flow control and/or retransmission modes. +** +** Returns - +** +*******************************************************************************/ +void l2c_fcr_proc_pdu (tL2C_CCB *p_ccb, BT_HDR *p_buf) +{ + assert(p_ccb != NULL); + assert(p_buf != NULL); + UINT8 *p; + UINT16 fcs; + UINT16 min_pdu_len; + UINT16 ctrl_word; + + /* Check the length */ + min_pdu_len = (p_ccb->bypass_fcs == L2CAP_BYPASS_FCS) ? + (UINT16)L2CAP_FCR_OVERHEAD : (UINT16)(L2CAP_FCS_LEN + L2CAP_FCR_OVERHEAD); + + if (p_buf->len < min_pdu_len) { + L2CAP_TRACE_WARNING ("Rx L2CAP PDU: CID: 0x%04x Len too short: %u", p_ccb->local_cid, p_buf->len); + osi_free (p_buf); + return; + } + + if (p_ccb->peer_cfg.fcr.mode == L2CAP_FCR_STREAM_MODE) { + process_stream_frame (p_ccb, p_buf); + return; + } + +#if BT_TRACE_VERBOSE == TRUE + /* Get the control word */ + p = ((UINT8 *)(p_buf + 1)) + p_buf->offset; + STREAM_TO_UINT16 (ctrl_word, p); + + if (ctrl_word & L2CAP_FCR_S_FRAME_BIT) { + if ((((ctrl_word & L2CAP_FCR_SUP_BITS) >> L2CAP_FCR_SUP_SHIFT) == 1) + || (((ctrl_word & L2CAP_FCR_SUP_BITS) >> L2CAP_FCR_SUP_SHIFT) == 3)) { + /* REJ or SREJ */ + L2CAP_TRACE_WARNING ("L2CAP eRTM Rx S-frame: cid: 0x%04x Len: %u Type: %s ReqSeq: %u P: %u F: %u", + p_ccb->local_cid, p_buf->len, + SUP_types[(ctrl_word & L2CAP_FCR_SUP_BITS) >> L2CAP_FCR_SUP_SHIFT], + (ctrl_word & L2CAP_FCR_REQ_SEQ_BITS) >> L2CAP_FCR_REQ_SEQ_BITS_SHIFT, + (ctrl_word & L2CAP_FCR_P_BIT) >> L2CAP_FCR_P_BIT_SHIFT, + (ctrl_word & L2CAP_FCR_F_BIT) >> L2CAP_FCR_F_BIT_SHIFT); + } else { + L2CAP_TRACE_EVENT ("L2CAP eRTM Rx S-frame: cid: 0x%04x Len: %u Type: %s ReqSeq: %u P: %u F: %u", + p_ccb->local_cid, p_buf->len, + SUP_types[(ctrl_word & L2CAP_FCR_SUP_BITS) >> L2CAP_FCR_SUP_SHIFT], + (ctrl_word & L2CAP_FCR_REQ_SEQ_BITS) >> L2CAP_FCR_REQ_SEQ_BITS_SHIFT, + (ctrl_word & L2CAP_FCR_P_BIT) >> L2CAP_FCR_P_BIT_SHIFT, + (ctrl_word & L2CAP_FCR_F_BIT) >> L2CAP_FCR_F_BIT_SHIFT); + } + } else { + L2CAP_TRACE_EVENT ("L2CAP eRTM Rx I-frame: cid: 0x%04x Len: %u SAR: %-12s TxSeq: %u ReqSeq: %u F: %u", + p_ccb->local_cid, p_buf->len, + SAR_types[(ctrl_word & L2CAP_FCR_SAR_BITS) >> L2CAP_FCR_SAR_BITS_SHIFT], + (ctrl_word & L2CAP_FCR_TX_SEQ_BITS) >> L2CAP_FCR_TX_SEQ_BITS_SHIFT, + (ctrl_word & L2CAP_FCR_REQ_SEQ_BITS) >> L2CAP_FCR_REQ_SEQ_BITS_SHIFT, + (ctrl_word & L2CAP_FCR_F_BIT) >> L2CAP_FCR_F_BIT_SHIFT); + } + + L2CAP_TRACE_EVENT (" eRTM Rx Nxt_tx_seq %u, Lst_rx_ack %u, Nxt_seq_exp %u, Lst_ack_snt %u, wt_q.cnt %u, tries %u", + p_ccb->fcrb.next_tx_seq, p_ccb->fcrb.last_rx_ack, + p_ccb->fcrb.next_seq_expected, + p_ccb->fcrb.last_ack_sent, + fixed_queue_length(p_ccb->fcrb.waiting_for_ack_q), + p_ccb->fcrb.num_tries); + +#endif /* BT_TRACE_VERBOSE */ + + /* Verify FCS if using */ + if (p_ccb->bypass_fcs != L2CAP_BYPASS_FCS) { + p = ((UINT8 *)(p_buf + 1)) + p_buf->offset + p_buf->len - L2CAP_FCS_LEN; + + /* Extract and drop the FCS from the packet */ + STREAM_TO_UINT16 (fcs, p); + p_buf->len -= L2CAP_FCS_LEN; + + if (l2c_fcr_rx_get_fcs(p_buf) != fcs) { + L2CAP_TRACE_WARNING ("Rx L2CAP PDU: CID: 0x%04x BAD FCS", p_ccb->local_cid); + osi_free(p_buf); + return; + } + } + + /* Get the control word */ + p = ((UINT8 *)(p_buf + 1)) + p_buf->offset; + + STREAM_TO_UINT16 (ctrl_word, p); + + p_buf->len -= L2CAP_FCR_OVERHEAD; + p_buf->offset += L2CAP_FCR_OVERHEAD; + + /* If we had a poll bit outstanding, check if we got a final response */ + if (p_ccb->fcrb.wait_ack) { + /* If final bit not set, ignore the frame unless it is a polled S-frame */ + if ( !(ctrl_word & L2CAP_FCR_F_BIT) ) { + if ( (ctrl_word & L2CAP_FCR_P_BIT) && (ctrl_word & L2CAP_FCR_S_FRAME_BIT) ) { + if (p_ccb->fcrb.srej_sent) { + l2c_fcr_send_S_frame (p_ccb, L2CAP_FCR_SUP_SREJ, L2CAP_FCR_F_BIT); + } else if (p_ccb->fcrb.local_busy) { + l2c_fcr_send_S_frame (p_ccb, L2CAP_FCR_SUP_RNR, L2CAP_FCR_F_BIT); + } else { + l2c_fcr_send_S_frame (p_ccb, L2CAP_FCR_SUP_RR, L2CAP_FCR_F_BIT); + } + + /* Got a poll while in wait_ack state, so re-start our timer with 1-second */ + /* This is a small optimization... the monitor timer is 12 secs, but we saw */ + /* that if the other side sends us a poll when we are waiting for a final, */ + /* then it speeds up recovery significantly if we poll him back soon after his poll. */ + btu_start_quick_timer (&p_ccb->fcrb.mon_retrans_timer, BTU_TTYPE_L2CAP_CHNL, QUICK_TIMER_TICKS_PER_SEC); + } + osi_free (p_buf); + return; + } + + p_ccb->fcrb.wait_ack = FALSE; + + /* P and F are mutually exclusive */ + if (ctrl_word & L2CAP_FCR_S_FRAME_BIT) { + ctrl_word &= ~L2CAP_FCR_P_BIT; + } + + if (fixed_queue_is_empty(p_ccb->fcrb.waiting_for_ack_q)) { + p_ccb->fcrb.num_tries = 0; + } + + l2c_fcr_stop_timer (p_ccb); + } else { + /* Otherwise, ensure the final bit is ignored */ + ctrl_word &= ~L2CAP_FCR_F_BIT; + } + + /* Process receive sequence number */ + if (!process_reqseq (p_ccb, ctrl_word)) { + osi_free (p_buf); + return; + } + + /* Process based on whether it is an S-frame or an I-frame */ + if (ctrl_word & L2CAP_FCR_S_FRAME_BIT) { + process_s_frame (p_ccb, p_buf, ctrl_word); + } else { + process_i_frame (p_ccb, p_buf, ctrl_word, FALSE); + } + + /* Return if the channel got disconnected by a bad packet or max retransmissions */ + if ( (!p_ccb->in_use) || (p_ccb->chnl_state != CST_OPEN) ) { + return; + } + + /* If we have some buffers held while doing SREJ, and SREJ has cleared, process them now */ + if ( (!p_ccb->fcrb.local_busy) && (!p_ccb->fcrb.srej_sent) && + (!fixed_queue_is_empty(p_ccb->fcrb.srej_rcv_hold_q))) { + fixed_queue_t *temp_q = p_ccb->fcrb.srej_rcv_hold_q; + p_ccb->fcrb.srej_rcv_hold_q = fixed_queue_new(QUEUE_SIZE_MAX); + + while ((p_buf = (BT_HDR *)fixed_queue_dequeue(temp_q, 0)) != NULL) { + if (p_ccb->in_use && (p_ccb->chnl_state == CST_OPEN)) { + /* Get the control word */ + p = ((UINT8 *)(p_buf + 1)) + p_buf->offset - L2CAP_FCR_OVERHEAD; + + STREAM_TO_UINT16 (ctrl_word, p); + + L2CAP_TRACE_DEBUG ("l2c_fcr_proc_pdu() CID: 0x%04x Process Buffer from SREJ_Hold_Q TxSeq: %u Expected_Seq: %u", + p_ccb->local_cid, (ctrl_word & L2CAP_FCR_TX_SEQ_BITS) >> L2CAP_FCR_TX_SEQ_BITS_SHIFT, + p_ccb->fcrb.next_seq_expected); + + /* Process the SREJ held I-frame, but do not send an RR for each individual frame */ + process_i_frame (p_ccb, p_buf, ctrl_word, TRUE); + } else { + osi_free (p_buf); + } + + /* If more frames were lost during SREJ, send a REJ */ + if (p_ccb->fcrb.rej_after_srej) { + p_ccb->fcrb.rej_after_srej = FALSE; + p_ccb->fcrb.rej_sent = TRUE; + + l2c_fcr_send_S_frame (p_ccb, L2CAP_FCR_SUP_REJ, 0); + } + } + fixed_queue_free(temp_q, NULL); + + /* Now, if needed, send one RR for the whole held queue */ + if ( (!p_ccb->fcrb.local_busy) && (!p_ccb->fcrb.rej_sent) && (!p_ccb->fcrb.srej_sent) + && (p_ccb->fcrb.next_seq_expected != p_ccb->fcrb.last_ack_sent) ) { + l2c_fcr_send_S_frame (p_ccb, L2CAP_FCR_SUP_RR, 0); + } else { + L2CAP_TRACE_DEBUG ("l2c_fcr_proc_pdu() not sending RR CID: 0x%04x local_busy:%d rej_sent:%d srej_sent:%d Expected_Seq:%u Last_Ack:%u", + p_ccb->local_cid, p_ccb->fcrb.local_busy, p_ccb->fcrb.rej_sent, p_ccb->fcrb.srej_sent, p_ccb->fcrb.next_seq_expected, + p_ccb->fcrb.last_ack_sent); + } + } + + /* If a window has opened, check if we can send any more packets */ + if ( (!fixed_queue_is_empty(p_ccb->fcrb.retrans_q) || + !fixed_queue_is_empty(p_ccb->xmit_hold_q)) + && (p_ccb->fcrb.wait_ack == FALSE) + && (l2c_fcr_is_flow_controlled (p_ccb) == FALSE) ) { + l2c_link_check_send_pkts (p_ccb->p_lcb, NULL, NULL); + } +} + +/******************************************************************************* +** +** Function l2c_fcr_proc_tout +** +** Description Handle a timeout. We should be in error recovery state. +** +** Returns - +** +*******************************************************************************/ +void l2c_fcr_proc_tout (tL2C_CCB *p_ccb) +{ + assert(p_ccb != NULL); + L2CAP_TRACE_DEBUG ("l2c_fcr_proc_tout: CID: 0x%04x num_tries: %u (max: %u) wait_ack: %u ack_q_count: %u", + p_ccb->local_cid, p_ccb->fcrb.num_tries, + p_ccb->peer_cfg.fcr.max_transmit, + p_ccb->fcrb.wait_ack, + fixed_queue_length(p_ccb->fcrb.waiting_for_ack_q)); + +#if (L2CAP_ERTM_STATS == TRUE) + p_ccb->fcrb.retrans_touts++; +#endif + + if ( (p_ccb->peer_cfg.fcr.max_transmit != 0) && (++p_ccb->fcrb.num_tries > p_ccb->peer_cfg.fcr.max_transmit) ) { + l2cu_disconnect_chnl (p_ccb); + } else { + if (!p_ccb->fcrb.srej_sent && !p_ccb->fcrb.rej_sent) { + if (p_ccb->fcrb.local_busy) { + l2c_fcr_send_S_frame (p_ccb, L2CAP_FCR_SUP_RNR, L2CAP_FCR_P_BIT); + } else { + l2c_fcr_send_S_frame (p_ccb, L2CAP_FCR_SUP_RR, L2CAP_FCR_P_BIT); + } + } + } +} + + +/******************************************************************************* +** +** Function l2c_fcr_proc_ack_tout +** +** Description Send RR/RNR if we have not acked I frame +** +** Returns - +** +*******************************************************************************/ +void l2c_fcr_proc_ack_tout (tL2C_CCB *p_ccb) +{ + assert(p_ccb != NULL); + L2CAP_TRACE_DEBUG ("l2c_fcr_proc_ack_tout: CID: 0x%04x State: %u Wack:%u Rq:%d Acked:%d", p_ccb->local_cid, + p_ccb->chnl_state, p_ccb->fcrb.wait_ack, p_ccb->fcrb.next_seq_expected, p_ccb->fcrb.last_ack_sent); + + if ( (p_ccb->chnl_state == CST_OPEN) && (!p_ccb->fcrb.wait_ack) + && (p_ccb->fcrb.last_ack_sent != p_ccb->fcrb.next_seq_expected) ) { +#if (L2CAP_ERTM_STATS == TRUE) + p_ccb->fcrb.xmit_ack_touts++; +#endif + if (p_ccb->fcrb.local_busy) { + l2c_fcr_send_S_frame (p_ccb, L2CAP_FCR_SUP_RNR, 0); + } else { + l2c_fcr_send_S_frame (p_ccb, L2CAP_FCR_SUP_RR, 0); + } + } +} + + +/******************************************************************************* +** +** Function process_reqseq +** +** Description Handle receive sequence number +** +** Returns - +** +*******************************************************************************/ +static BOOLEAN process_reqseq (tL2C_CCB *p_ccb, UINT16 ctrl_word) +{ + assert(p_ccb != NULL); + tL2C_FCRB *p_fcrb = &p_ccb->fcrb; + UINT8 req_seq, num_bufs_acked, xx; + UINT16 ls; + UINT16 full_sdus_xmitted; + + /* Receive sequence number does not ack anything for SREJ with P-bit set to zero */ + if ( (ctrl_word & L2CAP_FCR_S_FRAME_BIT) + && ((ctrl_word & L2CAP_FCR_SUP_BITS) == (L2CAP_FCR_SUP_SREJ << L2CAP_FCR_SUP_SHIFT)) + && ((ctrl_word & L2CAP_FCR_P_BIT) == 0) ) { + /* If anything still waiting for ack, restart the timer if it was stopped */ + if (!fixed_queue_is_empty(p_fcrb->waiting_for_ack_q)) { + l2c_fcr_start_timer(p_ccb); + } + + return (TRUE); + } + + /* Extract the receive sequence number from the control word */ + req_seq = (ctrl_word & L2CAP_FCR_REQ_SEQ_BITS) >> L2CAP_FCR_REQ_SEQ_BITS_SHIFT; + + num_bufs_acked = (req_seq - p_fcrb->last_rx_ack) & L2CAP_FCR_SEQ_MODULO; + + /* Verify the request sequence is in range before proceeding */ + if (num_bufs_acked > fixed_queue_length(p_fcrb->waiting_for_ack_q)) { + /* The channel is closed if ReqSeq is not in range */ + L2CAP_TRACE_WARNING ("L2CAP eRTM Frame BAD Req_Seq - ctrl_word: 0x%04x req_seq 0x%02x last_rx_ack: 0x%02x QCount: %u", + ctrl_word, req_seq, p_fcrb->last_rx_ack, + fixed_queue_length(p_fcrb->waiting_for_ack_q)); + + l2cu_disconnect_chnl (p_ccb); + return (FALSE); + } + + p_fcrb->last_rx_ack = req_seq; + + /* Now we can release all acknowledged frames, and restart the retransmission timer if needed */ + if (num_bufs_acked != 0) { + p_fcrb->num_tries = 0; + full_sdus_xmitted = 0; + +#if (L2CAP_ERTM_STATS == TRUE) + l2c_fcr_collect_ack_delay (p_ccb, num_bufs_acked); +#endif + + for (xx = 0; xx < num_bufs_acked; xx++) { + BT_HDR *p_tmp = (BT_HDR *)fixed_queue_dequeue(p_fcrb->waiting_for_ack_q, 0); + ls = p_tmp->layer_specific & L2CAP_FCR_SAR_BITS; + + if ( (ls == L2CAP_FCR_UNSEG_SDU) || (ls == L2CAP_FCR_END_SDU) ) { + full_sdus_xmitted++; + } + osi_free(p_tmp); + if (p_ccb->cong_sent) { + l2cu_check_channel_congestion(p_ccb); + } + } + + /* If we are still in a wait_ack state, do not mess with the timer */ + if (!p_ccb->fcrb.wait_ack) { + l2c_fcr_stop_timer (p_ccb); + } + + /* Check if we need to call the "packet_sent" callback */ + if ( (p_ccb->p_rcb) && (p_ccb->p_rcb->api.pL2CA_TxComplete_Cb) && (full_sdus_xmitted) ) { + /* Special case for eRTM, if all packets sent, send 0xFFFF */ + if (fixed_queue_is_empty(p_fcrb->waiting_for_ack_q) && + fixed_queue_is_empty(p_ccb->xmit_hold_q)) { + full_sdus_xmitted = 0xFFFF; + } + + (*p_ccb->p_rcb->api.pL2CA_TxComplete_Cb)(p_ccb->local_cid, full_sdus_xmitted); + } + } + + /* If anything still waiting for ack, restart the timer if it was stopped */ + if (!fixed_queue_is_empty(p_fcrb->waiting_for_ack_q)) { + l2c_fcr_start_timer (p_ccb); + } + + return (TRUE); +} + + +/******************************************************************************* +** +** Function process_s_frame +** +** Description Process an S frame +** +** Returns - +** +*******************************************************************************/ +static void process_s_frame (tL2C_CCB *p_ccb, BT_HDR *p_buf, UINT16 ctrl_word) +{ + assert(p_ccb != NULL); + assert(p_buf != NULL); + + tL2C_FCRB *p_fcrb = &p_ccb->fcrb; + UINT16 s_frame_type = (ctrl_word & L2CAP_FCR_SUP_BITS) >> L2CAP_FCR_SUP_SHIFT; + BOOLEAN remote_was_busy; + BOOLEAN all_ok = TRUE; + + if (p_buf->len != 0) { + L2CAP_TRACE_WARNING ("Incorrect S-frame Length (%d)", p_buf->len); + } + + L2CAP_TRACE_DEBUG ("process_s_frame ctrl_word 0x%04x fcrb_remote_busy:%d", ctrl_word, p_fcrb->remote_busy); + +#if (L2CAP_ERTM_STATS == TRUE) + p_ccb->fcrb.s_frames_rcvd[s_frame_type]++; +#endif + + if (ctrl_word & L2CAP_FCR_P_BIT) { + p_fcrb->rej_sent = FALSE; /* After checkpoint, we can send anoher REJ */ + p_fcrb->send_f_rsp = TRUE; /* Set a flag in case an I-frame is pending */ + } + + switch (s_frame_type) { + case L2CAP_FCR_SUP_RR: + remote_was_busy = p_fcrb->remote_busy; + p_fcrb->remote_busy = FALSE; + + if ( (ctrl_word & L2CAP_FCR_F_BIT) || (remote_was_busy) ) { + all_ok = retransmit_i_frames (p_ccb, L2C_FCR_RETX_ALL_PKTS); + } + break; + + case L2CAP_FCR_SUP_REJ: + p_fcrb->remote_busy = FALSE; + all_ok = retransmit_i_frames (p_ccb, L2C_FCR_RETX_ALL_PKTS); + break; + + case L2CAP_FCR_SUP_RNR: + p_fcrb->remote_busy = TRUE; + l2c_fcr_stop_timer (p_ccb); + break; + + case L2CAP_FCR_SUP_SREJ: + p_fcrb->remote_busy = FALSE; + all_ok = retransmit_i_frames (p_ccb, (UINT8)((ctrl_word & L2CAP_FCR_REQ_SEQ_BITS) >> L2CAP_FCR_REQ_SEQ_BITS_SHIFT)); + break; + } + + if (all_ok) { + /* If polled, we need to respond with F-bit. Note, we may have sent a I-frame with the F-bit */ + if (p_fcrb->send_f_rsp) { + if (p_fcrb->srej_sent) { + l2c_fcr_send_S_frame (p_ccb, L2CAP_FCR_SUP_SREJ, L2CAP_FCR_F_BIT); + } else if (p_fcrb->local_busy) { + l2c_fcr_send_S_frame (p_ccb, L2CAP_FCR_SUP_RNR, L2CAP_FCR_F_BIT); + } else { + l2c_fcr_send_S_frame (p_ccb, L2CAP_FCR_SUP_RR, L2CAP_FCR_F_BIT); + } + + p_fcrb->send_f_rsp = FALSE; + } + } else { + L2CAP_TRACE_DEBUG ("process_s_frame hit_max_retries"); + } + + osi_free (p_buf); +} + + +/******************************************************************************* +** +** Function process_i_frame +** +** Description Process an I frame +** +** Returns - +** +*******************************************************************************/ +static void process_i_frame (tL2C_CCB *p_ccb, BT_HDR *p_buf, UINT16 ctrl_word, BOOLEAN delay_ack) +{ + assert(p_ccb != NULL); + assert(p_buf != NULL); + + tL2C_FCRB *p_fcrb = &p_ccb->fcrb; + UINT8 tx_seq, num_lost, num_to_ack, next_srej; + + /* If we were doing checkpoint recovery, first retransmit all unacked I-frames */ + if (ctrl_word & L2CAP_FCR_F_BIT) { + if (!retransmit_i_frames (p_ccb, L2C_FCR_RETX_ALL_PKTS)) { + osi_free(p_buf); + return; + } + } + +#if (L2CAP_ERTM_STATS == TRUE) + p_ccb->fcrb.ertm_pkt_counts[1]++; + p_ccb->fcrb.ertm_byte_counts[1] += p_buf->len; +#endif + + /* Extract the sequence number */ + tx_seq = (ctrl_word & L2CAP_FCR_TX_SEQ_BITS) >> L2CAP_FCR_TX_SEQ_BITS_SHIFT; + + /* If we have flow controlled the peer, ignore any bad I-frames from him */ + if ( (tx_seq != p_fcrb->next_seq_expected) && (p_fcrb->local_busy) ) { + L2CAP_TRACE_WARNING ("Dropping bad I-Frame since we flowed off, tx_seq:%u", tx_seq); + l2c_fcr_send_S_frame (p_ccb, L2CAP_FCR_SUP_RNR, 0); + osi_free(p_buf); + return; + } + + /* Check if tx-sequence is the expected one */ + if (tx_seq != p_fcrb->next_seq_expected) { + num_lost = (tx_seq - p_fcrb->next_seq_expected) & L2CAP_FCR_SEQ_MODULO; + + /* Is the frame a duplicate ? If so, just drop it */ + if (num_lost >= p_ccb->our_cfg.fcr.tx_win_sz) { + /* Duplicate - simply drop it */ + L2CAP_TRACE_WARNING ("process_i_frame() Dropping Duplicate Frame tx_seq:%u ExpectedTxSeq %u", tx_seq, p_fcrb->next_seq_expected); + osi_free(p_buf); + } else { + L2CAP_TRACE_WARNING ("process_i_frame() CID: 0x%04x Lost: %u tx_seq:%u ExpTxSeq %u Rej: %u SRej: %u", + p_ccb->local_cid, num_lost, tx_seq, p_fcrb->next_seq_expected, p_fcrb->rej_sent, p_fcrb->srej_sent); + + if (p_fcrb->srej_sent) { + /* If SREJ sent, save the frame for later processing as long as it is in sequence */ + next_srej = (((BT_HDR *)fixed_queue_try_peek_last(p_fcrb->srej_rcv_hold_q))->layer_specific + 1) & L2CAP_FCR_SEQ_MODULO; + + if ( (tx_seq == next_srej) && (fixed_queue_length(p_fcrb->srej_rcv_hold_q) < p_ccb->our_cfg.fcr.tx_win_sz) ) { + /* If user gave us a pool for held rx buffers, use that */ + /* TODO: Could that happen? Get rid of this code. */ + if (p_ccb->ertm_info.fcr_rx_buf_size != L2CAP_FCR_RX_BUF_SIZE) { + BT_HDR *p_buf2; + + /* Adjust offset and len so that control word is copied */ + p_buf->offset -= L2CAP_FCR_OVERHEAD; + p_buf->len += L2CAP_FCR_OVERHEAD; + + p_buf2 = l2c_fcr_clone_buf (p_buf, p_buf->offset, p_buf->len); + + if (p_buf2) { + osi_free (p_buf); + p_buf = p_buf2; + } + p_buf->offset += L2CAP_FCR_OVERHEAD; + p_buf->len -= L2CAP_FCR_OVERHEAD; + } + L2CAP_TRACE_DEBUG ("process_i_frame() Lost: %u tx_seq:%u ExpTxSeq %u Rej: %u SRej1", + num_lost, tx_seq, p_fcrb->next_seq_expected, p_fcrb->rej_sent); + + p_buf->layer_specific = tx_seq; + fixed_queue_enqueue(p_fcrb->srej_rcv_hold_q, p_buf, FIXED_QUEUE_MAX_TIMEOUT); + } else { + L2CAP_TRACE_WARNING ("process_i_frame() CID: 0x%04x frame dropped in Srej Sent next_srej:%u hold_q.count:%u win_sz:%u", + p_ccb->local_cid, next_srej, fixed_queue_length(p_fcrb->srej_rcv_hold_q), p_ccb->our_cfg.fcr.tx_win_sz); + + p_fcrb->rej_after_srej = TRUE; + osi_free (p_buf); + } + } else if (p_fcrb->rej_sent) { + L2CAP_TRACE_WARNING ("process_i_frame() CID: 0x%04x Lost: %u tx_seq:%u ExpTxSeq %u Rej: 1 SRej: %u", + p_ccb->local_cid, num_lost, tx_seq, p_fcrb->next_seq_expected, p_fcrb->srej_sent); + + /* If REJ sent, just drop the frame */ + osi_free (p_buf); + } else { + L2CAP_TRACE_DEBUG ("process_i_frame() CID: 0x%04x tx_seq:%u ExpTxSeq %u Rej: %u", + p_ccb->local_cid, tx_seq, p_fcrb->next_seq_expected, p_fcrb->rej_sent); + + /* If only one lost, we will send SREJ, otherwise we will send REJ */ + if (num_lost > 1) { + osi_free (p_buf); + p_fcrb->rej_sent = TRUE; + l2c_fcr_send_S_frame (p_ccb, L2CAP_FCR_SUP_REJ, 0); + } else { + if (!fixed_queue_is_empty(p_fcrb->srej_rcv_hold_q)) { + L2CAP_TRACE_ERROR ("process_i_frame() CID: 0x%04x sending SREJ tx_seq:%d hold_q.count:%u", + p_ccb->local_cid, tx_seq, fixed_queue_length(p_fcrb->srej_rcv_hold_q)); + } + p_buf->layer_specific = tx_seq; + fixed_queue_enqueue(p_fcrb->srej_rcv_hold_q, p_buf, FIXED_QUEUE_MAX_TIMEOUT); + p_fcrb->srej_sent = TRUE; + l2c_fcr_send_S_frame (p_ccb, L2CAP_FCR_SUP_SREJ, 0); + } + btu_stop_quick_timer (&p_ccb->fcrb.ack_timer); + } + } + return; + } + + /* Seq number is the next expected. Clear possible reject exception in case it occured */ + p_fcrb->rej_sent = p_fcrb->srej_sent = FALSE; + + /* Adjust the next_seq, so that if the upper layer sends more data in the callback + context, the received frame is acked by an I-frame. */ + p_fcrb->next_seq_expected = (tx_seq + 1) & L2CAP_FCR_SEQ_MODULO; + + /* If any SAR problem in eRTM mode, spec says disconnect. */ + if (!do_sar_reassembly (p_ccb, p_buf, ctrl_word)) { + L2CAP_TRACE_WARNING ("process_i_frame() CID: 0x%04x reassembly failed", p_ccb->local_cid); + l2cu_disconnect_chnl (p_ccb); + return; + } + + /* RR optimization - if peer can still send us more, then start an ACK timer */ + num_to_ack = (p_fcrb->next_seq_expected - p_fcrb->last_ack_sent) & L2CAP_FCR_SEQ_MODULO; + + if ( (num_to_ack < p_ccb->fcrb.max_held_acks) && (!p_fcrb->local_busy) ) { + delay_ack = TRUE; + } + + /* We should neve never ack frame if we are not in OPEN state */ + if ((num_to_ack != 0) && p_ccb->in_use && (p_ccb->chnl_state == CST_OPEN)) { + /* If no frames are awaiting transmission or are held, send an RR or RNR S-frame for ack */ + if (delay_ack) { + /* If it is the first I frame we did not ack, start ack timer */ + if (!p_ccb->fcrb.ack_timer.in_use) { + btu_start_quick_timer (&p_ccb->fcrb.ack_timer, BTU_TTYPE_L2CAP_FCR_ACK, + (L2CAP_FCR_ACK_TOUT * QUICK_TIMER_TICKS_PER_SEC) / 1000); + } + } else if ((fixed_queue_is_empty(p_ccb->xmit_hold_q) || + l2c_fcr_is_flow_controlled(p_ccb)) + && fixed_queue_is_empty(p_ccb->fcrb.srej_rcv_hold_q)) { + if (p_fcrb->local_busy) { + l2c_fcr_send_S_frame (p_ccb, L2CAP_FCR_SUP_RNR, 0); + } else { + l2c_fcr_send_S_frame (p_ccb, L2CAP_FCR_SUP_RR, 0); + } + } + } +} + + +/******************************************************************************* +** +** Function process_stream_frame +** +** Description This function processes frames in streaming mode +** +** Returns - +** +*******************************************************************************/ +static void process_stream_frame (tL2C_CCB *p_ccb, BT_HDR *p_buf) +{ + assert(p_ccb != NULL); + assert(p_buf != NULL); + + UINT16 ctrl_word; + UINT16 fcs; + UINT8 *p; + UINT8 tx_seq; + + /* Verify FCS if using */ + if (p_ccb->bypass_fcs != L2CAP_BYPASS_FCS) { + p = ((UINT8 *)(p_buf + 1)) + p_buf->offset + p_buf->len - L2CAP_FCS_LEN; + + /* Extract and drop the FCS from the packet */ + STREAM_TO_UINT16 (fcs, p); + p_buf->len -= L2CAP_FCS_LEN; + + if (l2c_fcr_rx_get_fcs(p_buf) != fcs) { + L2CAP_TRACE_WARNING ("Rx L2CAP PDU: CID: 0x%04x BAD FCS", p_ccb->local_cid); + osi_free(p_buf); + return; + } + } + + /* Get the control word */ + p = ((UINT8 *)(p_buf + 1)) + p_buf->offset; + + STREAM_TO_UINT16 (ctrl_word, p); + + p_buf->len -= L2CAP_FCR_OVERHEAD; + p_buf->offset += L2CAP_FCR_OVERHEAD; + + /* Make sure it is an I-frame */ + if (ctrl_word & L2CAP_FCR_S_FRAME_BIT) { + L2CAP_TRACE_WARNING ("Rx L2CAP PDU: CID: 0x%04x BAD S-frame in streaming mode ctrl_word: 0x%04x", p_ccb->local_cid, ctrl_word); + osi_free (p_buf); + return; + } + +#if BT_TRACE_VERBOSE == TRUE + L2CAP_TRACE_EVENT ("L2CAP eRTM Rx I-frame: cid: 0x%04x Len: %u SAR: %-12s TxSeq: %u ReqSeq: %u F: %u", + p_ccb->local_cid, p_buf->len, + SAR_types[(ctrl_word & L2CAP_FCR_SAR_BITS) >> L2CAP_FCR_SAR_BITS_SHIFT], + (ctrl_word & L2CAP_FCR_TX_SEQ_BITS) >> L2CAP_FCR_TX_SEQ_BITS_SHIFT, + (ctrl_word & L2CAP_FCR_REQ_SEQ_BITS) >> L2CAP_FCR_REQ_SEQ_BITS_SHIFT, + (ctrl_word & L2CAP_FCR_F_BIT) >> L2CAP_FCR_F_BIT_SHIFT); +#endif + + /* Extract the sequence number */ + tx_seq = (ctrl_word & L2CAP_FCR_TX_SEQ_BITS) >> L2CAP_FCR_TX_SEQ_BITS_SHIFT; + + /* Check if tx-sequence is the expected one */ + if (tx_seq != p_ccb->fcrb.next_seq_expected) { + L2CAP_TRACE_WARNING ("Rx L2CAP PDU: CID: 0x%04x Lost frames Exp: %u Got: %u p_rx_sdu: %p", + p_ccb->local_cid, p_ccb->fcrb.next_seq_expected, tx_seq, p_ccb->fcrb.p_rx_sdu); + + /* Lost one or more packets, so flush the SAR queue */ + if (p_ccb->fcrb.p_rx_sdu != NULL) { + osi_free(p_ccb->fcrb.p_rx_sdu); + p_ccb->fcrb.p_rx_sdu = NULL; + } + } + + p_ccb->fcrb.next_seq_expected = (tx_seq + 1) & L2CAP_FCR_SEQ_MODULO; + + if (!do_sar_reassembly (p_ccb, p_buf, ctrl_word)) { + /* Some sort of SAR error, so flush the SAR queue */ + if (p_ccb->fcrb.p_rx_sdu != NULL) { + osi_free(p_ccb->fcrb.p_rx_sdu); + p_ccb->fcrb.p_rx_sdu = NULL; + } + } +} + + +/******************************************************************************* +** +** Function do_sar_reassembly +** +** Description Process SAR bits and re-assemble frame +** +** Returns TRUE if all OK, else FALSE +** +*******************************************************************************/ +static BOOLEAN do_sar_reassembly (tL2C_CCB *p_ccb, BT_HDR *p_buf, UINT16 ctrl_word) +{ + assert(p_ccb != NULL); + assert(p_buf != NULL); + + tL2C_FCRB *p_fcrb = &p_ccb->fcrb; + UINT16 sar_type = ctrl_word & L2CAP_FCR_SEG_BITS; + BOOLEAN packet_ok = TRUE; + UINT8 *p; + + /* Check if the SAR state is correct */ + if ((sar_type == L2CAP_FCR_UNSEG_SDU) || (sar_type == L2CAP_FCR_START_SDU)) { + if (p_fcrb->p_rx_sdu != NULL) { + L2CAP_TRACE_WARNING ("SAR - got unexpected unsegmented or start SDU Expected len: %u Got so far: %u", + p_fcrb->rx_sdu_len, p_fcrb->p_rx_sdu->len); + + packet_ok = FALSE; + } + /* Check the length of the packet */ + if ( (sar_type == L2CAP_FCR_START_SDU) && (p_buf->len < L2CAP_SDU_LEN_OVERHEAD) ) { + L2CAP_TRACE_WARNING ("SAR start packet too short: %u", p_buf->len); + packet_ok = FALSE; + } + } else { + if (p_fcrb->p_rx_sdu == NULL) { + L2CAP_TRACE_WARNING ("SAR - got unexpected cont or end SDU"); + packet_ok = FALSE; + } + } + + if ( (packet_ok) && (sar_type != L2CAP_FCR_UNSEG_SDU) ) { + p = ((UINT8 *)(p_buf + 1)) + p_buf->offset; + + /* For start SDU packet, extract the SDU length */ + if (sar_type == L2CAP_FCR_START_SDU) { + /* Get the SDU length */ + STREAM_TO_UINT16 (p_fcrb->rx_sdu_len, p); + p_buf->offset += 2; + p_buf->len -= 2; + + if (p_fcrb->rx_sdu_len > p_ccb->max_rx_mtu) { + L2CAP_TRACE_WARNING ("SAR - SDU len: %u larger than MTU: %u", p_fcrb->rx_sdu_len, p_fcrb->rx_sdu_len); + packet_ok = FALSE; + } else if ((p_fcrb->p_rx_sdu = (BT_HDR *)osi_malloc(L2CAP_MAX_BUF_SIZE)) == NULL) { + L2CAP_TRACE_ERROR ("SAR - no buffer for SDU start user_rx_buf_size:%d", p_ccb->ertm_info.user_rx_buf_size); + packet_ok = FALSE; + } else { + p_fcrb->p_rx_sdu->offset = 4; /* this is the minimal offset required by OBX to process incoming packets */ + p_fcrb->p_rx_sdu->len = 0; + } + } + + if (packet_ok) { + if ((p_fcrb->p_rx_sdu->len + p_buf->len) > p_fcrb->rx_sdu_len) { + L2CAP_TRACE_ERROR ("SAR - SDU len exceeded Type: %u Lengths: %u %u %u", + sar_type, p_fcrb->p_rx_sdu->len, p_buf->len, p_fcrb->rx_sdu_len); + packet_ok = FALSE; + } else if ( (sar_type == L2CAP_FCR_END_SDU) && ((p_fcrb->p_rx_sdu->len + p_buf->len) != p_fcrb->rx_sdu_len) ) { + L2CAP_TRACE_WARNING ("SAR - SDU end rcvd but SDU incomplete: %u %u %u", + p_fcrb->p_rx_sdu->len, p_buf->len, p_fcrb->rx_sdu_len); + packet_ok = FALSE; + } else { + memcpy (((UINT8 *) (p_fcrb->p_rx_sdu + 1)) + p_fcrb->p_rx_sdu->offset + p_fcrb->p_rx_sdu->len, p, p_buf->len); + + p_fcrb->p_rx_sdu->len += p_buf->len; + + osi_free (p_buf); + p_buf = NULL; + + if (sar_type == L2CAP_FCR_END_SDU) { + p_buf = p_fcrb->p_rx_sdu; + p_fcrb->p_rx_sdu = NULL; + } + } + } + } + + if (packet_ok == FALSE) { + osi_free (p_buf); + } else if (p_buf != NULL) { +#if (L2CAP_NUM_FIXED_CHNLS > 0) + if (p_ccb->local_cid < L2CAP_BASE_APPL_CID && + (p_ccb->local_cid >= L2CAP_FIRST_FIXED_CHNL && p_ccb->local_cid <= L2CAP_LAST_FIXED_CHNL)) { + if (l2cb.fixed_reg[p_ccb->local_cid - L2CAP_FIRST_FIXED_CHNL].pL2CA_FixedData_Cb) + (*l2cb.fixed_reg[p_ccb->local_cid - L2CAP_FIRST_FIXED_CHNL].pL2CA_FixedData_Cb) + (p_ccb->local_cid, p_ccb->p_lcb->remote_bd_addr, p_buf); + } else +#endif + { + l2c_csm_execute (p_ccb, L2CEVT_L2CAP_DATA, p_buf); + } + } + + return (packet_ok); +} + + +/******************************************************************************* +** +** Function retransmit_i_frames +** +** Description This function retransmits i-frames awaiting acks. +** +** Returns BOOLEAN - TRUE if retransmitted +** +*******************************************************************************/ +static BOOLEAN retransmit_i_frames (tL2C_CCB *p_ccb, UINT8 tx_seq) +{ + assert(p_ccb != NULL); + + BT_HDR *p_buf = NULL; + UINT8 *p; + UINT8 buf_seq; + UINT16 ctrl_word; + + if ( (!fixed_queue_is_empty(p_ccb->fcrb.waiting_for_ack_q)) + && (p_ccb->peer_cfg.fcr.max_transmit != 0) + && (p_ccb->fcrb.num_tries >= p_ccb->peer_cfg.fcr.max_transmit) ) { + L2CAP_TRACE_EVENT ("Max Tries Exceeded: (last_acq: %d CID: 0x%04x num_tries: %u (max: %u) ack_q_count: %u", + p_ccb->fcrb.last_rx_ack, p_ccb->local_cid, p_ccb->fcrb.num_tries, p_ccb->peer_cfg.fcr.max_transmit, + fixed_queue_length(p_ccb->fcrb.waiting_for_ack_q)); + + l2cu_disconnect_chnl (p_ccb); + return (FALSE); + } + + /* tx_seq indicates whether to retransmit a specific sequence or all (if == L2C_FCR_RETX_ALL_PKTS) */ + list_t *list_ack = NULL; + const list_node_t *node_ack = NULL; + if (! fixed_queue_is_empty(p_ccb->fcrb.waiting_for_ack_q)) { + list_ack = fixed_queue_get_list(p_ccb->fcrb.waiting_for_ack_q); + node_ack = list_begin(list_ack); + } + if (tx_seq != L2C_FCR_RETX_ALL_PKTS) { + /* If sending only one, the sequence number tells us which one. Look for it. + */ + if (list_ack != NULL) { + for ( ; node_ack != list_end(list_ack); node_ack = list_next(node_ack)) { + p_buf = (BT_HDR *)list_node(node_ack); + /* Get the old control word */ + p = ((UINT8 *) (p_buf+1)) + p_buf->offset + L2CAP_PKT_OVERHEAD; + + STREAM_TO_UINT16 (ctrl_word, p); + + buf_seq = (ctrl_word & L2CAP_FCR_TX_SEQ_BITS) >> L2CAP_FCR_TX_SEQ_BITS_SHIFT; + + L2CAP_TRACE_DEBUG ("retransmit_i_frames() cur seq: %u looking for: %u", buf_seq, tx_seq); + + if (tx_seq == buf_seq) { + break; + } + } + } + + if (!p_buf) { + L2CAP_TRACE_ERROR ("retransmit_i_frames() UNKNOWN seq: %u q_count: %u", + tx_seq, + fixed_queue_length(p_ccb->fcrb.waiting_for_ack_q)); + return (TRUE); + } + } else { + // Iterate though list and flush the amount requested from + // the transmit data queue that satisfy the layer and event conditions. + for (const list_node_t *node = list_begin(p_ccb->p_lcb->link_xmit_data_q); + node != list_end(p_ccb->p_lcb->link_xmit_data_q);) { + BT_HDR *p_buf = (BT_HDR *)list_node(node); + node = list_next(node); + + /* Do not flush other CIDs or partial segments */ + if ((p_buf->layer_specific == 0) && (p_buf->event == p_ccb->local_cid)) { + list_remove(p_ccb->p_lcb->link_xmit_data_q, p_buf); + osi_free(p_buf); + } + } + + /* Also flush our retransmission queue */ + while (!fixed_queue_is_empty(p_ccb->fcrb.retrans_q)) { + osi_free(fixed_queue_dequeue(p_ccb->fcrb.retrans_q, 0)); + } + + if (list_ack != NULL) { + node_ack = list_begin(list_ack); + } + } + + if (list_ack != NULL) { + while (node_ack != list_end(list_ack)) + { + p_buf = (BT_HDR *)list_node(node_ack); + node_ack = list_next(node_ack); + + BT_HDR *p_buf2 = l2c_fcr_clone_buf(p_buf, p_buf->offset, p_buf->len); + if (p_buf2) + { + p_buf2->layer_specific = p_buf->layer_specific; + + fixed_queue_enqueue(p_ccb->fcrb.retrans_q, p_buf2, FIXED_QUEUE_MAX_TIMEOUT); + } + + if ( (tx_seq != L2C_FCR_RETX_ALL_PKTS) || (p_buf2 == NULL) ) { + break; + } + } + } + + l2c_link_check_send_pkts (p_ccb->p_lcb, NULL, NULL); + + if (fixed_queue_length(p_ccb->fcrb.waiting_for_ack_q)) + { + p_ccb->fcrb.num_tries++; + l2c_fcr_start_timer (p_ccb); + } + + return (TRUE); +} + + +/******************************************************************************* +** +** Function l2c_fcr_get_next_xmit_sdu_seg +** +** Description Get the next SDU segment to transmit. +** +** Returns pointer to buffer with segment or NULL +** +*******************************************************************************/ +BT_HDR *l2c_fcr_get_next_xmit_sdu_seg (tL2C_CCB *p_ccb, UINT16 max_packet_length) +{ + assert(p_ccb != NULL); + + BOOLEAN first_seg = FALSE, /* The segment is the first part of data */ + mid_seg = FALSE, /* The segment is the middle part of data */ + last_seg = FALSE; /* The segment is the last part of data */ + UINT16 sdu_len = 0; + BT_HDR *p_buf, *p_xmit; + UINT8 *p; + UINT16 max_pdu = p_ccb->tx_mps /* Needed? - L2CAP_MAX_HEADER_FCS*/; + + /* If there is anything in the retransmit queue, that goes first + */ + p_buf = (BT_HDR *)fixed_queue_dequeue(p_ccb->fcrb.retrans_q, 0); + if (p_buf != NULL) { + /* Update Rx Seq and FCS if we acked some packets while this one was queued */ + prepare_I_frame (p_ccb, p_buf, TRUE); + + p_buf->event = p_ccb->local_cid; + +#if (L2CAP_ERTM_STATS == TRUE) + p_ccb->fcrb.pkts_retransmitted++; + p_ccb->fcrb.ertm_pkt_counts[0]++; + p_ccb->fcrb.ertm_byte_counts[0] += (p_buf->len - 8); +#endif + return (p_buf); + } + + /* For BD/EDR controller, max_packet_length is set to 0 */ + /* For AMP controller, max_packet_length is set by available blocks */ + if ( (max_packet_length > L2CAP_MAX_HEADER_FCS) + && (max_pdu + L2CAP_MAX_HEADER_FCS > max_packet_length) ) { + max_pdu = max_packet_length - L2CAP_MAX_HEADER_FCS; + } + + p_buf = (BT_HDR *)fixed_queue_try_peek_first(p_ccb->xmit_hold_q); + + /* If there is more data than the MPS, it requires segmentation */ + if (p_buf->len > max_pdu) { + /* We are using the "event" field to tell is if we already started segmentation */ + if (p_buf->event == 0) { + first_seg = TRUE; + sdu_len = p_buf->len; + } else { + mid_seg = TRUE; + } + + /* Get a new buffer and copy the data that can be sent in a PDU */ + p_xmit = l2c_fcr_clone_buf (p_buf, L2CAP_MIN_OFFSET + L2CAP_SDU_LEN_OFFSET, + max_pdu); + + if (p_xmit != NULL) { + p_buf->event = p_ccb->local_cid; + p_xmit->event = p_ccb->local_cid; + + p_buf->len -= max_pdu; + p_buf->offset += max_pdu; + + /* copy PBF setting */ + p_xmit->layer_specific = p_buf->layer_specific; + } else { /* Should never happen if the application has configured buffers correctly */ + L2CAP_TRACE_ERROR ("L2CAP - cannot get buffer for segmentation, max_pdu: %u", max_pdu); + return (NULL); + } + } else { /* Use the original buffer if no segmentation, or the last segment */ + p_xmit = (BT_HDR *)fixed_queue_dequeue(p_ccb->xmit_hold_q, 0); + + if (p_xmit->event != 0) { + last_seg = TRUE; + } + + p_xmit->event = p_ccb->local_cid; + } + + /* Step back to add the L2CAP headers */ + p_xmit->offset -= (L2CAP_PKT_OVERHEAD + L2CAP_FCR_OVERHEAD); + p_xmit->len += L2CAP_PKT_OVERHEAD + L2CAP_FCR_OVERHEAD; + + if (first_seg) { + p_xmit->offset -= L2CAP_SDU_LEN_OVERHEAD; + p_xmit->len += L2CAP_SDU_LEN_OVERHEAD; + } + + /* Set the pointer to the beginning of the data */ + p = (UINT8 *)(p_xmit + 1) + p_xmit->offset; + + /* Now the L2CAP header */ + + /* Note: if FCS has to be included then the length is recalculated later */ + UINT16_TO_STREAM (p, p_xmit->len - L2CAP_PKT_OVERHEAD); + + UINT16_TO_STREAM (p, p_ccb->remote_cid); + + if (first_seg) { + /* Skip control word and add SDU length */ + p += 2; + UINT16_TO_STREAM (p, sdu_len); + + /* We will store the SAR type in layer-specific */ + /* layer_specific is shared with flushable flag(bits 0-1), don't clear it */ + p_xmit->layer_specific |= L2CAP_FCR_START_SDU; + + first_seg = FALSE; + } else if (mid_seg) { + p_xmit->layer_specific |= L2CAP_FCR_CONT_SDU; + } else if (last_seg) { + p_xmit->layer_specific |= L2CAP_FCR_END_SDU; + } else { + p_xmit->layer_specific |= L2CAP_FCR_UNSEG_SDU; + } + + prepare_I_frame (p_ccb, p_xmit, FALSE); + + if (p_ccb->peer_cfg.fcr.mode == L2CAP_FCR_ERTM_MODE) { + BT_HDR *p_wack = l2c_fcr_clone_buf (p_xmit, HCI_DATA_PREAMBLE_SIZE, p_xmit->len); + + if (!p_wack) { + L2CAP_TRACE_ERROR("L2CAP - no buffer for xmit cloning, CID: 0x%04x Length: %u", + p_ccb->local_cid, p_xmit->len); + + /* We will not save the FCS in case we reconfigure and change options */ + if (p_ccb->bypass_fcs != L2CAP_BYPASS_FCS) { + p_xmit->len -= L2CAP_FCS_LEN; + } + + /* Pretend we sent it and it got lost */ + fixed_queue_enqueue(p_ccb->fcrb.waiting_for_ack_q, p_xmit, FIXED_QUEUE_MAX_TIMEOUT); + return (NULL); + } else { +#if (L2CAP_ERTM_STATS == TRUE) + /* set timestamp at the end of tx I-frame to get acking delay */ + p = ((UINT8 *) (p_wack + 1)) + p_wack->offset + p_wack->len; + UINT32_TO_STREAM (p, osi_time_get_os_boottime_ms()); +#endif + /* We will not save the FCS in case we reconfigure and change options */ + if (p_ccb->bypass_fcs != L2CAP_BYPASS_FCS) { + p_wack->len -= L2CAP_FCS_LEN; + } + + p_wack->layer_specific = p_xmit->layer_specific; + fixed_queue_enqueue(p_ccb->fcrb.waiting_for_ack_q, p_wack, FIXED_QUEUE_MAX_TIMEOUT); + } + +#if (L2CAP_ERTM_STATS == TRUE) + p_ccb->fcrb.ertm_pkt_counts[0]++; + p_ccb->fcrb.ertm_byte_counts[0] += (p_xmit->len - 8); +#endif + + } + + return (p_xmit); +} + + +/******************************************************************************* +** Configuration negotiation functions +** +** The following functions are used in negotiating channel modes during +** configuration +********************************************************************************/ + +/******************************************************************************* +** +** Function l2c_fcr_chk_chan_modes +** +** Description Validates and adjusts if necessary, the FCR options +** based on remote EXT features. +** +** Note: This assumes peer EXT Features have been received. +** Basic mode is used if FCR Options have not been received +** +** Returns UINT8 - nonzero if can continue, '0' if no compatible channels +** +*******************************************************************************/ +UINT8 l2c_fcr_chk_chan_modes (tL2C_CCB *p_ccb) +{ + assert(p_ccb != NULL); + + /* Remove nonbasic options that the peer does not support */ + if (!(p_ccb->p_lcb->peer_ext_fea & L2CAP_EXTFEA_ENH_RETRANS)) { + p_ccb->ertm_info.allowed_modes &= ~L2CAP_FCR_CHAN_OPT_ERTM; + } + + if (!(p_ccb->p_lcb->peer_ext_fea & L2CAP_EXTFEA_STREAM_MODE)) { + p_ccb->ertm_info.allowed_modes &= ~L2CAP_FCR_CHAN_OPT_STREAM; + } + + /* At least one type needs to be set (Basic, ERTM, STM) to continue */ + if (!p_ccb->ertm_info.allowed_modes) { + L2CAP_TRACE_WARNING ("L2CAP - Peer does not support our desired channel types"); + } + + return (p_ccb->ertm_info.allowed_modes); +} + +/******************************************************************************* +** +** Function l2c_fcr_adj_our_req_options +** +** Description Validates and sets up the FCR options passed in from +** L2CA_ConfigReq based on remote device's features. +** +** Returns TRUE if no errors, Otherwise FALSE +** +*******************************************************************************/ +BOOLEAN l2c_fcr_adj_our_req_options (tL2C_CCB *p_ccb, tL2CAP_CFG_INFO *p_cfg) +{ + assert(p_ccb != NULL); + assert(p_cfg != NULL); + + tL2CAP_FCR_OPTS *p_fcr = &p_cfg->fcr; + + if (p_fcr->mode != p_ccb->ertm_info.preferred_mode) { + L2CAP_TRACE_WARNING ("l2c_fcr_adj_our_req_options - preferred_mode (%d), does not match mode (%d)", + p_ccb->ertm_info.preferred_mode, p_fcr->mode); + + /* The preferred mode is passed in through tL2CAP_ERTM_INFO, so override this one */ + p_fcr->mode = p_ccb->ertm_info.preferred_mode; + } + + /* If upper layer did not request eRTM mode, BASIC must be used */ + if (p_ccb->ertm_info.allowed_modes == L2CAP_FCR_CHAN_OPT_BASIC) { + if (p_cfg->fcr_present && p_fcr->mode != L2CAP_FCR_BASIC_MODE) { + L2CAP_TRACE_WARNING ("l2c_fcr_adj_our_req_options (mode %d): ERROR: No FCR options set using BASIC mode", p_fcr->mode); + } + p_fcr->mode = L2CAP_FCR_BASIC_MODE; + } + + /* Process the FCR options if initial channel bring-up (not a reconfig request) + ** Determine initial channel mode to try based on our options and remote's features + */ + if (p_cfg->fcr_present && !(p_ccb->config_done & RECONFIG_FLAG)) { + /* We need to have at least one mode type common with the peer */ + if (!l2c_fcr_chk_chan_modes(p_ccb)) { + /* Two channels have incompatible supported types */ + l2cu_disconnect_chnl (p_ccb); + return (FALSE); + } + + /* Basic is the only common channel mode between the two devices */ + else if (p_ccb->ertm_info.allowed_modes == L2CAP_FCR_CHAN_OPT_BASIC) { + /* We only want to try Basic, so bypass sending the FCR options entirely */ + p_cfg->fcr_present = FALSE; + p_cfg->fcs_present = FALSE; /* Illegal to use FCS option in basic mode */ + p_cfg->ext_flow_spec_present = FALSE; /* Illegal to use extended flow spec in basic mode */ + } + + /* We have at least one non-basic mode available + * Override mode from available mode options based on preference, if needed + */ + else { + /* If peer does not support STREAMING, try ERTM */ + if (p_fcr->mode == L2CAP_FCR_STREAM_MODE && !(p_ccb->ertm_info.allowed_modes & L2CAP_FCR_CHAN_OPT_STREAM)) { + L2CAP_TRACE_DEBUG ("L2C CFG: mode is STREAM, but peer does not support; Try ERTM"); + p_fcr->mode = L2CAP_FCR_ERTM_MODE; + } + + /* If peer does not support ERTM, try BASIC (will support this if made it here in the code) */ + if (p_fcr->mode == L2CAP_FCR_ERTM_MODE && !(p_ccb->ertm_info.allowed_modes & L2CAP_FCR_CHAN_OPT_ERTM)) { + L2CAP_TRACE_DEBUG ("L2C CFG: mode is ERTM, but peer does not support; Try BASIC"); + p_fcr->mode = L2CAP_FCR_BASIC_MODE; + } + } + + if (p_fcr->mode != L2CAP_FCR_BASIC_MODE) { + /* MTU must be smaller than buffer size */ + if ( (p_cfg->mtu_present) && (p_cfg->mtu > p_ccb->max_rx_mtu) ) { + L2CAP_TRACE_WARNING ("L2CAP - MTU: %u larger than buf size: %u", p_cfg->mtu, p_ccb->max_rx_mtu); + return (FALSE); + } + + /* application want to use the default MPS */ + if (p_fcr->mps == L2CAP_DEFAULT_ERM_MPS) { + p_fcr->mps = L2CAP_MPS_OVER_BR_EDR; + } + /* MPS must be less than MTU */ + else if (p_fcr->mps > p_ccb->max_rx_mtu) { + L2CAP_TRACE_WARNING ("L2CAP - MPS %u invalid MTU: %u", p_fcr->mps, p_ccb->max_rx_mtu); + return (FALSE); + } + + /* We always initially read into the HCI buffer pool, so make sure it fits */ + if (p_fcr->mps > (L2CAP_MTU_SIZE - L2CAP_MAX_HEADER_FCS)) { + p_fcr->mps = L2CAP_MTU_SIZE - L2CAP_MAX_HEADER_FCS; + } + } else { + p_cfg->fcs_present = FALSE; /* Illegal to use FCS option in basic mode */ + p_cfg->ext_flow_spec_present = FALSE; /* Illegal to use extended flow spec in basic mode */ + } + + p_ccb->our_cfg.fcr = *p_fcr; + } else { /* Not sure how to send a reconfiguration(??) should fcr be included? */ + p_ccb->our_cfg.fcr_present = FALSE; + } + + return (TRUE); +} + + +/******************************************************************************* +** +** Function l2c_fcr_adj_monitor_retran_timeout +** +** Description Overrides monitor/retrans timer value based on controller +** +** Returns None +** +*******************************************************************************/ +void l2c_fcr_adj_monitor_retran_timeout (tL2C_CCB *p_ccb) +{ + assert(p_ccb != NULL); + + /* adjust our monitor/retran timeout */ + if (p_ccb->out_cfg_fcr_present) { + /* + ** if we requestd ERTM or accepted ERTM + ** We may accept ERTM even if we didn't request ERTM, in case of requesting STREAM + */ + if ((p_ccb->our_cfg.fcr.mode == L2CAP_FCR_ERTM_MODE) + || (p_ccb->peer_cfg.fcr.mode == L2CAP_FCR_ERTM_MODE)) { + /* upper layer setting is ignored */ + p_ccb->our_cfg.fcr.mon_tout = L2CAP_MIN_MONITOR_TOUT; + p_ccb->our_cfg.fcr.rtrans_tout = L2CAP_MIN_RETRANS_TOUT; + } else { + p_ccb->our_cfg.fcr.mon_tout = 0; + p_ccb->our_cfg.fcr.rtrans_tout = 0; + } + + L2CAP_TRACE_DEBUG ("l2c_fcr_adj_monitor_retran_timeout: mon_tout:%d, rtrans_tout:%d", + p_ccb->our_cfg.fcr.mon_tout, p_ccb->our_cfg.fcr.rtrans_tout); + } +} +/******************************************************************************* +** +** Function l2c_fcr_adj_our_rsp_options +** +** Description Overrides any neccesary FCR options passed in from +** L2CA_ConfigRsp based on our FCR options. +** Only makes adjustments if channel is in ERTM mode. +** +** Returns None +** +*******************************************************************************/ +void l2c_fcr_adj_our_rsp_options (tL2C_CCB *p_ccb, tL2CAP_CFG_INFO *p_cfg) +{ + assert(p_ccb != NULL); + assert(p_cfg != NULL); + + /* adjust our monitor/retran timeout */ + l2c_fcr_adj_monitor_retran_timeout(p_ccb); + + p_cfg->fcr_present = p_ccb->out_cfg_fcr_present; + + if (p_cfg->fcr_present) { +// btla-specific ++ + /* Temporary - until a better algorithm is implemented */ + /* If peer's tx_wnd_sz requires too many buffers for us to support, then adjust it. For now, respond with our own tx_wnd_sz. */ + /* Note: peer is not guaranteed to obey our adjustment */ + if (p_ccb->peer_cfg.fcr.tx_win_sz > p_ccb->our_cfg.fcr.tx_win_sz) { + L2CAP_TRACE_DEBUG ("%s: adjusting requested tx_win_sz from %i to %i", __FUNCTION__, p_ccb->peer_cfg.fcr.tx_win_sz, p_ccb->our_cfg.fcr.tx_win_sz); + p_ccb->peer_cfg.fcr.tx_win_sz = p_ccb->our_cfg.fcr.tx_win_sz; + } +// btla-specific -- + + p_cfg->fcr.mode = p_ccb->peer_cfg.fcr.mode; + p_cfg->fcr.tx_win_sz = p_ccb->peer_cfg.fcr.tx_win_sz; + p_cfg->fcr.max_transmit = p_ccb->peer_cfg.fcr.max_transmit; + p_cfg->fcr.mps = p_ccb->peer_cfg.fcr.mps; + p_cfg->fcr.rtrans_tout = p_ccb->our_cfg.fcr.rtrans_tout; + p_cfg->fcr.mon_tout = p_ccb->our_cfg.fcr.mon_tout; + } +} + +/******************************************************************************* +** +** Function l2c_fcr_renegotiate_chan +** +** Description Called upon unsuccessful peer response to config request. +** If the error is because of the channel mode, it will try +** to resend using another supported optional channel. +** +** Returns TRUE if resent configuration, False if channel matches or +** cannot match. +** +*******************************************************************************/ +BOOLEAN l2c_fcr_renegotiate_chan(tL2C_CCB *p_ccb, tL2CAP_CFG_INFO *p_cfg) +{ + assert(p_ccb != NULL); + assert(p_cfg != NULL); + + UINT8 peer_mode = p_ccb->our_cfg.fcr.mode; + BOOLEAN can_renegotiate; + + /* Skip if this is a reconfiguration from OPEN STATE or if FCR is not returned */ + if (!p_cfg->fcr_present || (p_ccb->config_done & RECONFIG_FLAG)) { + return (FALSE); + } + + /* Only retry if there are more channel options to try */ + if (p_cfg->result == L2CAP_CFG_UNACCEPTABLE_PARAMS) { + peer_mode = (p_cfg->fcr_present) ? p_cfg->fcr.mode : L2CAP_FCR_BASIC_MODE; + + if (p_ccb->our_cfg.fcr.mode != peer_mode) { + + if ((--p_ccb->fcr_cfg_tries) == 0) { + p_cfg->result = L2CAP_CFG_FAILED_NO_REASON; + L2CAP_TRACE_WARNING ("l2c_fcr_renegotiate_chan (Max retries exceeded)"); + } + + can_renegotiate = FALSE; + + /* Try another supported mode if available based on our last attempted channel */ + switch (p_ccb->our_cfg.fcr.mode) { + /* Our Streaming mode request was unnacceptable; try ERTM or Basic */ + case L2CAP_FCR_STREAM_MODE: + /* Peer wants ERTM and we support it */ + if ( (peer_mode == L2CAP_FCR_ERTM_MODE) && (p_ccb->ertm_info.allowed_modes & L2CAP_FCR_CHAN_OPT_ERTM) ) { + L2CAP_TRACE_DEBUG ("l2c_fcr_renegotiate_chan(Trying ERTM)"); + p_ccb->our_cfg.fcr.mode = L2CAP_FCR_ERTM_MODE; + can_renegotiate = TRUE; + } else /* Falls through */ + + case L2CAP_FCR_ERTM_MODE: { + /* We can try basic for any other peer mode if we support it */ + if (p_ccb->ertm_info.allowed_modes & L2CAP_FCR_CHAN_OPT_BASIC) { + L2CAP_TRACE_DEBUG ("l2c_fcr_renegotiate_chan(Trying Basic)"); + can_renegotiate = TRUE; + p_ccb->our_cfg.fcr.mode = L2CAP_FCR_BASIC_MODE; + } + } + break; + + default: + /* All other scenarios cannot be renegotiated */ + break; + } + + if (can_renegotiate) { + p_ccb->our_cfg.fcr_present = TRUE; + + if (p_ccb->our_cfg.fcr.mode == L2CAP_FCR_BASIC_MODE) { + p_ccb->our_cfg.fcs_present = FALSE; + p_ccb->our_cfg.ext_flow_spec_present = FALSE; + + /* Basic Mode uses ACL Data Pool, make sure the MTU fits */ + if ( (p_cfg->mtu_present) && (p_cfg->mtu > L2CAP_MTU_SIZE) ) { + L2CAP_TRACE_WARNING ("L2CAP - adjust MTU: %u too large", p_cfg->mtu); + p_cfg->mtu = L2CAP_MTU_SIZE; + } + } + + l2cu_process_our_cfg_req (p_ccb, &p_ccb->our_cfg); + l2cu_send_peer_config_req (p_ccb, &p_ccb->our_cfg); + btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, L2CAP_CHNL_CFG_TIMEOUT); + return (TRUE); + } + } + } + + /* Disconnect if the channels do not match */ + if (p_ccb->our_cfg.fcr.mode != peer_mode) { + L2CAP_TRACE_WARNING ("L2C CFG: Channels incompatible (local %d, peer %d)", + p_ccb->our_cfg.fcr.mode, peer_mode); + l2cu_disconnect_chnl (p_ccb); + } + + return (FALSE); +} + + +/******************************************************************************* +** +** Function l2c_fcr_process_peer_cfg_req +** +** Description This function is called to process the FCR options passed +** in the peer's configuration request. +** +** Returns UINT8 - L2CAP_PEER_CFG_OK, L2CAP_PEER_CFG_UNACCEPTABLE, +** or L2CAP_PEER_CFG_DISCONNECT. +** +*******************************************************************************/ +UINT8 l2c_fcr_process_peer_cfg_req(tL2C_CCB *p_ccb, tL2CAP_CFG_INFO *p_cfg) +{ + assert(p_ccb != NULL); + assert(p_cfg != NULL); + + UINT16 max_retrans_size; + UINT8 fcr_ok = L2CAP_PEER_CFG_OK; + + p_ccb->p_lcb->w4_info_rsp = FALSE; /* Handles T61x SonyEricsson Bug in Info Request */ + + L2CAP_TRACE_EVENT ("l2c_fcr_process_peer_cfg_req() CFG fcr_present:%d fcr.mode:%d CCB FCR mode:%d preferred: %u allowed:%u", + p_cfg->fcr_present, p_cfg->fcr.mode, p_ccb->our_cfg.fcr.mode, p_ccb->ertm_info.preferred_mode, + p_ccb->ertm_info.allowed_modes); + + /* If Peer wants basic, we are done (accept it or disconnect) */ + if (p_cfg->fcr.mode == L2CAP_FCR_BASIC_MODE) { + /* If we do not allow basic, disconnect */ + if ( !(p_ccb->ertm_info.allowed_modes & L2CAP_FCR_CHAN_OPT_BASIC) ) { + fcr_ok = L2CAP_PEER_CFG_DISCONNECT; + } + } + + /* Need to negotiate if our modes are not the same */ + else if (p_cfg->fcr.mode != p_ccb->ertm_info.preferred_mode) { + /* If peer wants a mode that we don't support then retry our mode (ex. rtx/flc), OR + ** If we want ERTM and they wanted streaming retry our mode. + ** Note: If we have already determined they support our mode previously + ** from their EXF mask. + */ + if ( (((1 << p_cfg->fcr.mode) & L2CAP_FCR_CHAN_OPT_ALL_MASK) == 0) + || (p_ccb->ertm_info.preferred_mode == L2CAP_FCR_ERTM_MODE) ) { + p_cfg->fcr.mode = p_ccb->our_cfg.fcr.mode; + p_cfg->fcr.tx_win_sz = p_ccb->our_cfg.fcr.tx_win_sz; + p_cfg->fcr.max_transmit = p_ccb->our_cfg.fcr.max_transmit; + fcr_ok = L2CAP_PEER_CFG_UNACCEPTABLE; + } + + /* If we wanted basic, then try to renegotiate it */ + else if (p_ccb->ertm_info.preferred_mode == L2CAP_FCR_BASIC_MODE) { + p_cfg->fcr.mode = L2CAP_FCR_BASIC_MODE; + p_cfg->fcr.max_transmit = p_cfg->fcr.tx_win_sz = 0; + p_cfg->fcr.rtrans_tout = p_cfg->fcr.mon_tout = p_cfg->fcr.mps = 0; + p_ccb->our_cfg.fcr.rtrans_tout = p_ccb->our_cfg.fcr.mon_tout = p_ccb->our_cfg.fcr.mps = 0; + fcr_ok = L2CAP_PEER_CFG_UNACCEPTABLE; + } + + /* Only other valid case is if they want ERTM and we wanted STM which should be + accepted if we support it; otherwise the channel should be disconnected */ + else if ( (p_cfg->fcr.mode != L2CAP_FCR_ERTM_MODE) + || !(p_ccb->ertm_info.allowed_modes & L2CAP_FCR_CHAN_OPT_ERTM) ) { + fcr_ok = L2CAP_PEER_CFG_DISCONNECT; + } + } + + /* Configuration for FCR channels so make any adjustments and fwd to upper layer */ + if (fcr_ok == L2CAP_PEER_CFG_OK) { + /* by default don't need to send params in the response */ + p_ccb->out_cfg_fcr_present = FALSE; + + /* Make any needed adjustments for the response to the peer */ + if (p_cfg->fcr_present && p_cfg->fcr.mode != L2CAP_FCR_BASIC_MODE) { + /* Peer desires to bypass FCS check, and streaming or ERTM mode */ + if (p_cfg->fcs_present) { + p_ccb->peer_cfg.fcs = p_cfg->fcs; + p_ccb->peer_cfg_bits |= L2CAP_CH_CFG_MASK_FCS; + if ( p_cfg->fcs == L2CAP_CFG_FCS_BYPASS) { + p_ccb->bypass_fcs |= L2CAP_CFG_FCS_PEER; + } + } + + max_retrans_size = p_ccb->ertm_info.fcr_tx_buf_size - sizeof(BT_HDR) + - L2CAP_MIN_OFFSET - L2CAP_SDU_LEN_OFFSET - L2CAP_FCS_LEN; + + /* Ensure the MPS is not bigger than the MTU */ + if ( (p_cfg->fcr.mps == 0) || (p_cfg->fcr.mps > p_ccb->peer_cfg.mtu) ) { + p_cfg->fcr.mps = p_ccb->peer_cfg.mtu; + p_ccb->out_cfg_fcr_present = TRUE; + } + + /* Ensure the MPS is not bigger than our retransmission buffer */ + if (p_cfg->fcr.mps > max_retrans_size) { + L2CAP_TRACE_DEBUG("CFG: Overriding MPS to %d (orig %d)", max_retrans_size, p_cfg->fcr.mps); + + p_cfg->fcr.mps = max_retrans_size; + p_ccb->out_cfg_fcr_present = TRUE; + } + + if (p_cfg->fcr.mode == L2CAP_FCR_ERTM_MODE || p_cfg->fcr.mode == L2CAP_FCR_STREAM_MODE) { + /* Always respond with FCR ERTM parameters */ + p_ccb->out_cfg_fcr_present = TRUE; + } + } + + /* Everything ok, so save the peer's adjusted fcr options */ + p_ccb->peer_cfg.fcr = p_cfg->fcr; + + if (p_cfg->fcr_present) { + p_ccb->peer_cfg_bits |= L2CAP_CH_CFG_MASK_FCR; + } + } else if (fcr_ok == L2CAP_PEER_CFG_UNACCEPTABLE) { + /* Allow peer only one retry for mode */ + if (p_ccb->peer_cfg_already_rejected) { + fcr_ok = L2CAP_PEER_CFG_DISCONNECT; + } else { + p_ccb->peer_cfg_already_rejected = TRUE; + } + } + + return (fcr_ok); +} + +#if (L2CAP_ERTM_STATS == TRUE) +/******************************************************************************* +** +** Function l2c_fcr_collect_ack_delay +** +** Description collect throughput, delay, queue size of waiting ack +** +** Parameters +** tL2C_CCB +** +** Returns void +** +*******************************************************************************/ +static void l2c_fcr_collect_ack_delay (tL2C_CCB *p_ccb, UINT8 num_bufs_acked) +{ + UINT32 index; + BT_HDR *p_buf; + UINT8 *p; + UINT32 timestamp, delay; + UINT8 xx; + UINT8 str[120]; + + index = p_ccb->fcrb.ack_delay_avg_index; + + /* update sum, max and min of waiting for ack queue size */ + p_ccb->fcrb.ack_q_count_avg[index] += + fixed_queue_length(p_ccb->fcrb.waiting_for_ack_q); + + if (fixed_queue_length(p_ccb->fcrb.waiting_for_ack_q) > p_ccb->fcrb.ack_q_count_max[index]) { + p_ccb->fcrb.ack_q_count_max[index] = fixed_queue_length(p_ccb->fcrb.waiting_for_ack_q); + } + + if (fixed_queue_length(p_ccb->fcrb.waiting_for_ack_q) < p_ccb->fcrb.ack_q_count_min[index]) { + p_ccb->fcrb.ack_q_count_min[index] = fixed_queue_length(p_ccb->fcrb.waiting_for_ack_q); + } + + /* update sum, max and min of round trip delay of acking */ + list_t *list = NULL; + if (! fixed_queue_is_empty(p_ccb->fcrb.waiting_for_ack_q)) { + list = fixed_queue_get_list(p_ccb->fcrb.waiting_for_ack_q); + } + if (list != NULL) { + for (const list_node_t *node = list_begin(list), xx = 0; + (node != list_end(list)) && (xx < num_bufs_acked); + node = list_next(node), xx++) { + p_buf = list_node(node); + /* adding up length of acked I-frames to get throughput */ + p_ccb->fcrb.throughput[index] += p_buf->len - 8; + + if ( xx == num_bufs_acked - 1 ) { + /* get timestamp from tx I-frame that receiver is acking */ + p = ((UINT8 *) (p_buf+1)) + p_buf->offset + p_buf->len; + if (p_ccb->bypass_fcs != L2CAP_BYPASS_FCS) { + p += L2CAP_FCS_LEN; + } + + STREAM_TO_UINT32(timestamp, p); + delay = osi_time_get_os_boottime_ms() - timestamp; + + p_ccb->fcrb.ack_delay_avg[index] += delay; + if ( delay > p_ccb->fcrb.ack_delay_max[index] ) { + p_ccb->fcrb.ack_delay_max[index] = delay; + } + if ( delay < p_ccb->fcrb.ack_delay_min[index] ) { + p_ccb->fcrb.ack_delay_min[index] = delay; + } + } + } + } + + p_ccb->fcrb.ack_delay_avg_count++; + + /* calculate average and initialize next avg, min and max */ + if (p_ccb->fcrb.ack_delay_avg_count > L2CAP_ERTM_STATS_AVG_NUM_SAMPLES) { + p_ccb->fcrb.ack_delay_avg_count = 0; + + p_ccb->fcrb.ack_q_count_avg[index] /= L2CAP_ERTM_STATS_AVG_NUM_SAMPLES; + p_ccb->fcrb.ack_delay_avg[index] /= L2CAP_ERTM_STATS_AVG_NUM_SAMPLES; + + /* calculate throughput */ + timestamp = osi_time_get_os_boottime_ms(); + if (timestamp - p_ccb->fcrb.throughput_start > 0 ) { + p_ccb->fcrb.throughput[index] /= (timestamp - p_ccb->fcrb.throughput_start); + } + + p_ccb->fcrb.throughput_start = timestamp; + + sprintf(str, "[%02u] throughput: %5u, ack_delay avg:%3u, min:%3u, max:%3u, ack_q_count avg:%3u, min:%3u, max:%3u", + index, p_ccb->fcrb.throughput[index], + p_ccb->fcrb.ack_delay_avg[index], p_ccb->fcrb.ack_delay_min[index], p_ccb->fcrb.ack_delay_max[index], + p_ccb->fcrb.ack_q_count_avg[index], p_ccb->fcrb.ack_q_count_min[index], p_ccb->fcrb.ack_q_count_max[index] ); + + BT_TRACE(TRACE_CTRL_GENERAL | TRACE_LAYER_GKI | TRACE_ORG_GKI , TRACE_TYPE_GENERIC, "%s", str); + + index = (index + 1) % L2CAP_ERTM_STATS_NUM_AVG; + p_ccb->fcrb.ack_delay_avg_index = index; + + p_ccb->fcrb.ack_q_count_max[index] = 0; + p_ccb->fcrb.ack_q_count_min[index] = 0xFFFFFFFF; + p_ccb->fcrb.ack_q_count_avg[index] = 0; + + + p_ccb->fcrb.ack_delay_max[index] = 0; + p_ccb->fcrb.ack_delay_min[index] = 0xFFFFFFFF; + p_ccb->fcrb.ack_delay_avg[index] = 0; + + p_ccb->fcrb.throughput[index] = 0; + } +} +#endif +#endif /// (L2CAP_COC_INCLUDED == TRUE) diff --git a/lib/bt/host/bluedroid/stack/l2cap/l2c_link.c b/lib/bt/host/bluedroid/stack/l2cap/l2c_link.c new file mode 100644 index 00000000..4b81b4b3 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/l2cap/l2c_link.c @@ -0,0 +1,1509 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * this file contains the functions relating to link management. A "link" + * is a connection between this device and another device. Only ACL links + * are managed. + * + ******************************************************************************/ + +#include +#include +//#include + +#include "device/controller.h" +//#include "btcore/include/counter.h" +#include "stack/bt_types.h" +//#include "bt_utils.h" +#include "stack/hcimsgs.h" +#include "stack/l2cdefs.h" +#include "l2c_int.h" +#include "stack/l2c_api.h" +#include "stack/btu.h" +#include "stack/btm_api.h" +#include "btm_int.h" + +static BOOLEAN l2c_link_send_to_lower (tL2C_LCB *p_lcb, BT_HDR *p_buf); + +#if (BLE_50_FEATURE_SUPPORT == TRUE) +extern tBTM_STATUS BTM_BleStartExtAdvRestart(uint8_t handle); +#endif// #if (BLE_50_FEATURE_SUPPORT == TRUE) +extern bool btm_ble_inter_get(void); + +/******************************************************************************* +** +** Function l2c_link_hci_conn_req +** +** Description This function is called when an HCI Connection Request +** event is received. +** +** Returns TRUE, if accept conn +** +*******************************************************************************/ +BOOLEAN l2c_link_hci_conn_req (BD_ADDR bd_addr) +{ + tL2C_LCB *p_lcb; + tL2C_LCB *p_lcb_cur; + BOOLEAN no_links; + + /* See if we have a link control block for the remote device */ + p_lcb = l2cu_find_lcb_by_bd_addr (bd_addr, BT_TRANSPORT_BR_EDR); + + /* If we don't have one, create one and accept the connection. */ + if (!p_lcb) { + p_lcb = l2cu_allocate_lcb (bd_addr, FALSE, BT_TRANSPORT_BR_EDR); + if (!p_lcb) { + btsnd_hcic_reject_conn (bd_addr, HCI_ERR_HOST_REJECT_RESOURCES); + L2CAP_TRACE_ERROR ("L2CAP failed to allocate LCB"); + return FALSE; + } + + no_links = TRUE; + + /* If we already have connection, accept as a master */ + list_node_t *p_node = NULL; + for (p_node = list_begin(l2cb.p_lcb_pool); p_node; p_node = list_next(p_node)) { + p_lcb_cur = list_node(p_node); + if (p_lcb_cur == p_lcb) { + continue; + } + + if (p_lcb_cur->in_use) { + no_links = FALSE; + p_lcb->link_role = HCI_ROLE_MASTER; + break; + } + } + + if (no_links) { + if (!btm_dev_support_switch (bd_addr)) { + p_lcb->link_role = HCI_ROLE_SLAVE; + } else { + p_lcb->link_role = l2cu_get_conn_role(p_lcb); + } + } + + //counter_add("l2cap.conn.accept", 1); + + /* Tell the other side we accept the connection */ + btsnd_hcic_accept_conn (bd_addr, p_lcb->link_role); + + p_lcb->link_state = LST_CONNECTING; + + /* Start a timer waiting for connect complete */ + btu_start_timer (&p_lcb->timer_entry, BTU_TTYPE_L2CAP_LINK, L2CAP_LINK_CONNECT_TOUT); + return (TRUE); + } + + /* We already had a link control block to the guy. Check what state it is in */ + if ((p_lcb->link_state == LST_CONNECTING) || (p_lcb->link_state == LST_CONNECT_HOLDING)) { + /* Connection collision. Accept the connection anyways. */ + + if (!btm_dev_support_switch (bd_addr)) { + p_lcb->link_role = HCI_ROLE_SLAVE; + } else { + p_lcb->link_role = l2cu_get_conn_role(p_lcb); + } + + //counter_add("l2cap.conn.accept", 1); + btsnd_hcic_accept_conn (bd_addr, p_lcb->link_role); + + p_lcb->link_state = LST_CONNECTING; + return (TRUE); + } else if (p_lcb->link_state == LST_DISCONNECTING) { + /* In disconnecting state, reject the connection. */ + //counter_add("l2cap.conn.reject.disconn", 1); + btsnd_hcic_reject_conn (bd_addr, HCI_ERR_HOST_REJECT_DEVICE); + } else { + L2CAP_TRACE_ERROR("L2CAP got conn_req while connected (state:%d). Reject it\n", + p_lcb->link_state); + /* Reject the connection with ACL Connection Already exist reason */ + //counter_add("l2cap.conn.reject.exists", 1); + btsnd_hcic_reject_conn (bd_addr, HCI_ERR_CONNECTION_EXISTS); + } + return (FALSE); +} + +/******************************************************************************* +** +** Function l2c_link_hci_conn_comp +** +** Description This function is called when an HCI Connection Complete +** event is received. +** +** Returns void +** +*******************************************************************************/ +BOOLEAN l2c_link_hci_conn_comp (UINT8 status, UINT16 handle, BD_ADDR p_bda) +{ + tL2C_CONN_INFO ci; + tL2C_LCB *p_lcb; +#if (CLASSIC_BT_INCLUDED == TRUE) + tL2C_CCB *p_ccb; +#endif ///CLASSIC_BT_INCLUDED == TRUE + tBTM_SEC_DEV_REC *p_dev_info = NULL; + + btm_acl_update_busy_level (BTM_BLI_PAGE_DONE_EVT); + + /* Save the parameters */ + ci.status = status; + memcpy (ci.bd_addr, p_bda, BD_ADDR_LEN); + + /* See if we have a link control block for the remote device */ + p_lcb = l2cu_find_lcb_by_bd_addr (ci.bd_addr, BT_TRANSPORT_BR_EDR); + + /* If we don't have one, this is an error */ + if (!p_lcb) { + L2CAP_TRACE_WARNING ("L2CAP got conn_comp for unknown BD_ADDR\n"); + return (FALSE); + } + + if (p_lcb->link_state != LST_CONNECTING) { + L2CAP_TRACE_ERROR ("L2CAP got conn_comp in bad state: %d status: 0x%d\n", p_lcb->link_state, status); + + if (status != HCI_SUCCESS) { + l2c_link_hci_disc_comp (p_lcb->handle, status); + } + + return (FALSE); + } + + /* Save the handle */ + p_lcb->handle = handle; + + if (ci.status == HCI_SUCCESS) { + /* Connected OK. Change state to connected */ + p_lcb->link_state = LST_CONNECTED; + //counter_add("l2cap.conn.ok", 1); + + /* Get the peer information if the l2cap flow-control/rtrans is supported */ + l2cu_send_peer_info_req (p_lcb, L2CAP_EXTENDED_FEATURES_INFO_TYPE); + + /* Tell BTM Acl management about the link */ + if ((p_dev_info = btm_find_dev (p_bda)) != NULL) { + btm_acl_created (ci.bd_addr, p_dev_info->dev_class, + p_dev_info->sec_bd_name, handle, + p_lcb->link_role, BT_TRANSPORT_BR_EDR); + } else { + btm_acl_created (ci.bd_addr, NULL, NULL, handle, p_lcb->link_role, BT_TRANSPORT_BR_EDR); + } + + BTM_SetLinkSuperTout (ci.bd_addr, btm_cb.btm_def_link_super_tout); + + /* If dedicated bonding do not process any further */ + if (p_lcb->is_bonding) { + if (l2cu_start_post_bond_timer(handle)) { + return (TRUE); + } + } + + /* Update the timeouts in the hold queue */ + l2c_process_held_packets(FALSE); + + btu_stop_timer (&p_lcb->timer_entry); +#if (CLASSIC_BT_INCLUDED == TRUE) + /* For all channels, send the event through their FSMs */ + for (p_ccb = p_lcb->ccb_queue.p_first_ccb; p_ccb; p_ccb = p_ccb->p_next_ccb) { + l2c_csm_execute (p_ccb, L2CEVT_LP_CONNECT_CFM, &ci); + } +#endif ///CLASSIC_BT_INCLUDED == TRUE + if (p_lcb->p_echo_rsp_cb) { + l2cu_send_peer_echo_req (p_lcb, NULL, 0); + btu_start_timer (&p_lcb->timer_entry, BTU_TTYPE_L2CAP_LINK, L2CAP_ECHO_RSP_TOUT); + } else if (!p_lcb->ccb_queue.p_first_ccb) { + btu_start_timer (&p_lcb->timer_entry, BTU_TTYPE_L2CAP_LINK, L2CAP_LINK_STARTUP_TOUT); + } + } + /* Max number of acl connections. */ + /* If there's an lcb disconnecting set this one to holding */ + else if ((ci.status == HCI_ERR_MAX_NUM_OF_CONNECTIONS) && l2cu_lcb_disconnecting()) { + p_lcb->link_state = LST_CONNECT_HOLDING; + p_lcb->handle = HCI_INVALID_HANDLE; + } else { + /* Just in case app decides to try again in the callback context */ + p_lcb->link_state = LST_DISCONNECTING; +#if(CLASSIC_BT_INCLUDED == TRUE) + /* Connection failed. For all channels, send the event through */ + /* their FSMs. The CCBs should remove themselves from the LCB */ + for (p_ccb = p_lcb->ccb_queue.p_first_ccb; p_ccb; ) { + tL2C_CCB *pn = p_ccb->p_next_ccb; + + l2c_csm_execute (p_ccb, L2CEVT_LP_CONNECT_CFM_NEG, &ci); + + p_ccb = pn; + } +#endif ///CLASSIC_BT_INCLUDED == TRUE + p_lcb->disc_reason = status; + /* Release the LCB */ + if (p_lcb->ccb_queue.p_first_ccb == NULL) { + l2cu_release_lcb (p_lcb); + } else { /* there are any CCBs remaining */ + if (ci.status == HCI_ERR_CONNECTION_EXISTS) { + /* we are in collision situation, wait for connection request from controller */ + p_lcb->link_state = LST_CONNECTING; + } else { + l2cu_create_conn(p_lcb, BT_TRANSPORT_BR_EDR); + } + } + } + return (TRUE); +} + + +/******************************************************************************* +** +** Function l2c_link_sec_comp +** +** Description This function is called when required security procedures +** are completed. +** +** Returns void +** +*******************************************************************************/ +void l2c_link_sec_comp (BD_ADDR p_bda, tBT_TRANSPORT transport, void *p_ref_data, UINT8 status) +{ + tL2C_CONN_INFO ci; + tL2C_LCB *p_lcb; + tL2C_CCB *p_ccb; + tL2C_CCB *p_next_ccb; +#if (CLASSIC_BT_INCLUDED == TRUE) + UINT8 event; +#endif ///CLASSIC_BT_INCLUDED == TRUE + UNUSED(transport); + + L2CAP_TRACE_DEBUG ("l2c_link_sec_comp: %d, %p", status, p_ref_data); + + if (status == BTM_SUCCESS_NO_SECURITY) { + status = BTM_SUCCESS; + } + + /* Save the parameters */ + ci.status = status; + memcpy (ci.bd_addr, p_bda, BD_ADDR_LEN); + + p_lcb = l2cu_find_lcb_by_bd_addr (p_bda, BT_TRANSPORT_BR_EDR); + + /* If we don't have one, this is an error */ + if (!p_lcb) { + L2CAP_TRACE_WARNING ("L2CAP got sec_comp for unknown BD_ADDR\n"); + return; + } + + /* Match p_ccb with p_ref_data returned by sec manager */ + for (p_ccb = p_lcb->ccb_queue.p_first_ccb; p_ccb; p_ccb = p_next_ccb) { + p_next_ccb = p_ccb->p_next_ccb; + + if (p_ccb == p_ref_data) { + switch (status) { + case BTM_SUCCESS: + L2CAP_TRACE_DEBUG ("ccb timer ticks: %u", p_ccb->timer_entry.ticks); +#if (CLASSIC_BT_INCLUDED == TRUE) + event = L2CEVT_SEC_COMP; +#endif ///CLASSIC_BT_INCLUDED == TRUE + break; + + case BTM_DELAY_CHECK: + /* start a timer - encryption change not received before L2CAP connect req */ + btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, L2CAP_DELAY_CHECK_SM4); + return; + + default: +#if (CLASSIC_BT_INCLUDED == TRUE) + event = L2CEVT_SEC_COMP_NEG; +#endif ///CLASSIC_BT_INCLUDED == TRUE + break; + } +#if (CLASSIC_BT_INCLUDED == TRUE) + l2c_csm_execute (p_ccb, event, &ci); +#endif ///CLASSIC_BT_INCLUDED == TRUE + break; + } + } +} + +/******************************************************************************* +** +** Function l2c_link_hci_disc_comp +** +** Description This function is called when an HCI Disconnect Complete +** event is received. +** +** Returns TRUE if the link is known about, else FALSE +** +*******************************************************************************/ +BOOLEAN l2c_link_hci_disc_comp (UINT16 handle, UINT8 reason) +{ + tL2C_LCB *p_lcb; +#if (CLASSIC_BT_INCLUDED == TRUE) + tL2C_CCB *p_ccb; +#endif ///CLASSIC_BT_INCLUDED == TRUE + BOOLEAN status = TRUE; + BOOLEAN lcb_is_free = TRUE; + tBT_TRANSPORT transport = BT_TRANSPORT_BR_EDR; + + /* See if we have a link control block for the connection */ + p_lcb = l2cu_find_lcb_by_handle (handle); + /* If we don't have one, maybe an SCO link. Send to MM */ + if (!p_lcb) { +#if (BLE_INCLUDED == TRUE) + /* The Directed Advertising Timeout error code indicates that directed advertising completed */ + if (reason != HCI_ERR_DIRECTED_ADVERTISING_TIMEOUT) { + BTM_Recovery_Pre_State(); + } +#endif ///BLE_INCLUDED == TRUE + status = FALSE; + } else { + /* There can be a case when we rejected PIN code authentication */ + /* otherwise save a new reason */ + if (btm_cb.acl_disc_reason != HCI_ERR_HOST_REJECT_SECURITY) { + btm_cb.acl_disc_reason = reason; + } + + p_lcb->disc_reason = btm_cb.acl_disc_reason; + + /* Just in case app decides to try again in the callback context */ + p_lcb->link_state = LST_DISCONNECTING; + +#if (BLE_INCLUDED == TRUE) + /* Check for BLE and handle that differently */ + if (p_lcb->transport == BT_TRANSPORT_LE) { + btm_ble_update_link_topology_mask(p_lcb->link_role, FALSE); + } +#endif +#if (CLASSIC_BT_INCLUDED == TRUE) + /* Link is disconnected. For all channels, send the event through */ + /* their FSMs. The CCBs should remove themselves from the LCB */ + for (p_ccb = p_lcb->ccb_queue.p_first_ccb; p_ccb; ) { + tL2C_CCB *pn = p_ccb->p_next_ccb; + + /* Keep connect pending control block (if exists) + * Possible Race condition when a reconnect occurs + * on the channel during a disconnect of link. This + * ccb will be automatically retried after link disconnect + * arrives + */ + if (p_ccb != p_lcb->p_pending_ccb) { + l2c_csm_execute (p_ccb, L2CEVT_LP_DISCONNECT_IND, &reason); + } + p_ccb = pn; + } +#endif ///CLASSIC_BT_INCLUDED == TRUE +#if (BTM_SCO_INCLUDED == TRUE) +#if (BLE_INCLUDED == TRUE) + if (p_lcb->transport == BT_TRANSPORT_BR_EDR) +#endif + { + /* Tell SCO management to drop any SCOs on this ACL */ + btm_sco_acl_removed (p_lcb->remote_bd_addr); + } +#endif + + /* If waiting for disconnect and reconnect is pending start the reconnect now + race condition where layer above issued connect request on link that was + disconnecting + */ + if (p_lcb->ccb_queue.p_first_ccb != NULL || p_lcb->p_pending_ccb) { + L2CAP_TRACE_DEBUG("l2c_link_hci_disc_comp: Restarting pending ACL request"); + transport = p_lcb->transport; +#if BLE_INCLUDED == TRUE + /* for LE link, always drop and re-open to ensure to get LE remote feature */ + if (p_lcb->transport == BT_TRANSPORT_LE) { + l2cb.is_ble_connecting = FALSE; + btm_acl_removed (p_lcb->remote_bd_addr, p_lcb->transport); + /* Release any held buffers */ + BT_HDR *p_buf; + while (!list_is_empty(p_lcb->link_xmit_data_q)) { + p_buf = list_front(p_lcb->link_xmit_data_q); + list_remove(p_lcb->link_xmit_data_q, p_buf); + osi_free(p_buf); + } + } else +#endif + { +#if (L2CAP_NUM_FIXED_CHNLS > 0) + /* If we are going to re-use the LCB without dropping it, release all fixed channels + here */ + int xx; + for (xx = 0; xx < L2CAP_NUM_FIXED_CHNLS; xx++) { + if (p_lcb->p_fixed_ccbs[xx] && p_lcb->p_fixed_ccbs[xx] != p_lcb->p_pending_ccb) { +#if BLE_INCLUDED == TRUE + (*l2cb.fixed_reg[xx].pL2CA_FixedConn_Cb)(xx + L2CAP_FIRST_FIXED_CHNL, + p_lcb->remote_bd_addr, FALSE, p_lcb->disc_reason, p_lcb->transport); +#else + (*l2cb.fixed_reg[xx].pL2CA_FixedConn_Cb)(xx + L2CAP_FIRST_FIXED_CHNL, + p_lcb->remote_bd_addr, FALSE, p_lcb->disc_reason, BT_TRANSPORT_BR_EDR); +#endif + l2cu_release_ccb (p_lcb->p_fixed_ccbs[xx]); + + p_lcb->p_fixed_ccbs[xx] = NULL; + } + } +#endif + } + if (l2cu_create_conn(p_lcb, transport)) { + lcb_is_free = FALSE; /* still using this lcb */ + } + } + + p_lcb->p_pending_ccb = NULL; +#if (BLE_INCLUDED == TRUE && GATTC_CONNECT_RETRY_EN == TRUE) + if(reason == HCI_ERR_CONN_FAILED_ESTABLISHMENT && p_lcb->transport == BT_TRANSPORT_LE) { + + if(p_lcb->link_role == HCI_ROLE_MASTER && p_lcb->retry_create_con < GATTC_CONNECT_RETRY_COUNT) { + L2CAP_TRACE_DEBUG("master retry connect, retry count %d reason 0x%x\n", p_lcb->retry_create_con, reason); + p_lcb->retry_create_con ++; + // create connection retry + if (l2cu_create_conn(p_lcb, BT_TRANSPORT_LE)) { + btm_acl_removed (p_lcb->remote_bd_addr, BT_TRANSPORT_LE); + lcb_is_free = FALSE; /* still using this lcb */ + } + } + + #if (BLE_50_FEATURE_SUPPORT == TRUE) + if(btm_ble_inter_get() && p_lcb->link_role == HCI_ROLE_SLAVE && p_lcb->retry_create_con < GATTC_CONNECT_RETRY_COUNT) { + p_lcb->retry_create_con ++; + L2CAP_TRACE_DEBUG("slave restart extend adv, retry count %d reason 0x%x\n", p_lcb->retry_create_con, reason); + BTM_BleStartExtAdvRestart(handle); + } + #endif // #if (BLE_50_FEATURE_SUPPORT == TRUE) + + #if (BLE_42_FEATURE_SUPPORT == TRUE) + if(!btm_ble_inter_get() && p_lcb->link_role == HCI_ROLE_SLAVE && p_lcb->retry_create_con < GATTC_CONNECT_RETRY_COUNT) { + p_lcb->retry_create_con ++; + L2CAP_TRACE_DEBUG("slave resatrt adv, retry count %d reason 0x%x\n", p_lcb->retry_create_con, reason); + btm_ble_start_adv(); + } + #endif // #if (BLE_42_FEATURE_SUPPORT == TRUE) + } + + +#endif // #if (BLE_INCLUDED == TRUE) + /* Release the LCB */ + if (lcb_is_free) { + l2cu_release_lcb (p_lcb); + } + } + + /* Now that we have a free acl connection, see if any lcbs are pending */ + if (lcb_is_free && ((p_lcb = l2cu_find_lcb_by_state(LST_CONNECT_HOLDING)) != NULL)) { + /* we found one-- create a connection */ + l2cu_create_conn(p_lcb, BT_TRANSPORT_BR_EDR); + } + + return status; +} + + +/******************************************************************************* +** +** Function l2c_link_hci_qos_violation +** +** Description This function is called when an HCI QOS Violation +** event is received. +** +** Returns TRUE if the link is known about, else FALSE +** +*******************************************************************************/ +BOOLEAN l2c_link_hci_qos_violation (UINT16 handle) +{ + tL2C_LCB *p_lcb; +#if (CLASSIC_BT_INCLUDED == TRUE) + tL2C_CCB *p_ccb; +#endif ///CLASSIC_BT_INCLUDED == TRUE + /* See if we have a link control block for the connection */ + p_lcb = l2cu_find_lcb_by_handle (handle); + + /* If we don't have one, maybe an SCO link. */ + if (!p_lcb) { + return (FALSE); + } +#if (CLASSIC_BT_INCLUDED == TRUE) + /* For all channels, tell the upper layer about it */ + for (p_ccb = p_lcb->ccb_queue.p_first_ccb; p_ccb; p_ccb = p_ccb->p_next_ccb) { + if (p_ccb->p_rcb->api.pL2CA_QoSViolationInd_Cb) { + l2c_csm_execute (p_ccb, L2CEVT_LP_QOS_VIOLATION_IND, NULL); + } + } +#endif ///CLASSIC_BT_INCLUDED == TRUE + return (TRUE); +} + + + +/******************************************************************************* +** +** Function l2c_link_timeout +** +** Description This function is called when a link timer expires +** +** Returns void +** +*******************************************************************************/ +void l2c_link_timeout (tL2C_LCB *p_lcb) +{ +#if (CLASSIC_BT_INCLUDED == TRUE) + tL2C_CCB *p_ccb; +#endif ///CLASSIC_BT_INCLUDED == TRUE +#if (SMP_INCLUDED == TRUE) + UINT16 timeout; + tBTM_STATUS rc; +#endif ///SMP_INCLUDED == TRUE + L2CAP_TRACE_EVENT ("L2CAP - l2c_link_timeout() link state %d first CCB %p is_bonding:%d", + p_lcb->link_state, p_lcb->ccb_queue.p_first_ccb, p_lcb->is_bonding); + + /* If link was connecting or disconnecting, clear all channels and drop the LCB */ + if ((p_lcb->link_state == LST_CONNECTING_WAIT_SWITCH) || + (p_lcb->link_state == LST_CONNECTING) || + (p_lcb->link_state == LST_CONNECT_HOLDING) || + (p_lcb->link_state == LST_DISCONNECTING)) { + p_lcb->p_pending_ccb = NULL; +#if (CLASSIC_BT_INCLUDED == TRUE) + /* For all channels, send a disconnect indication event through */ + /* their FSMs. The CCBs should remove themselves from the LCB */ + for (p_ccb = p_lcb->ccb_queue.p_first_ccb; p_ccb; ) { + tL2C_CCB *pn = p_ccb->p_next_ccb; + + l2c_csm_execute (p_ccb, L2CEVT_LP_DISCONNECT_IND, NULL); + + p_ccb = pn; + } +#endif ///CLASSIC_BT_INCLUDED == TRUE +#if (BLE_INCLUDED == TRUE) + if (p_lcb->link_state == LST_CONNECTING && + l2cb.is_ble_connecting == TRUE) { + L2CA_CancelBleConnectReq(l2cb.ble_connecting_bda); + } +#endif + /* Release the LCB */ + l2cu_release_lcb (p_lcb); + } + + /* If link is connected, check for inactivity timeout */ + if (p_lcb->link_state == LST_CONNECTED) { + /* Check for ping outstanding */ + if (p_lcb->p_echo_rsp_cb) { + tL2CA_ECHO_RSP_CB *p_cb = p_lcb->p_echo_rsp_cb; + + /* Zero out the callback in case app immediately calls us again */ + p_lcb->p_echo_rsp_cb = NULL; + + (*p_cb) (L2CAP_PING_RESULT_NO_RESP); + + L2CAP_TRACE_WARNING ("L2CAP - ping timeout"); +#if (CLASSIC_BT_INCLUDED == TRUE) + /* For all channels, send a disconnect indication event through */ + /* their FSMs. The CCBs should remove themselves from the LCB */ + for (p_ccb = p_lcb->ccb_queue.p_first_ccb; p_ccb; ) { + tL2C_CCB *pn = p_ccb->p_next_ccb; + + l2c_csm_execute (p_ccb, L2CEVT_LP_DISCONNECT_IND, NULL); + + p_ccb = pn; + } +#endif ///CLASSIC_BT_INCLUDED == TRUE + } + +#if (SMP_INCLUDED == TRUE) + /* If no channels in use, drop the link. */ + if (!p_lcb->ccb_queue.p_first_ccb) { + rc = btm_sec_disconnect (p_lcb->handle, HCI_ERR_PEER_USER); + + if (rc == BTM_CMD_STORED) { + /* Security Manager will take care of disconnecting, state will be updated at that time */ + timeout = 0xFFFF; + } else if (rc == BTM_CMD_STARTED) { + p_lcb->link_state = LST_DISCONNECTING; + timeout = L2CAP_LINK_DISCONNECT_TOUT; + } else if (rc == BTM_SUCCESS) { + l2cu_process_fixed_disc_cback(p_lcb); + /* BTM SEC will make sure that link is release (probably after pairing is done) */ + p_lcb->link_state = LST_DISCONNECTING; + timeout = 0xFFFF; + } else if (rc == BTM_BUSY) { + /* BTM is still executing security process. Let lcb stay as connected */ + timeout = 0xFFFF; + } else if ((p_lcb->is_bonding) + && (btsnd_hcic_disconnect (p_lcb->handle, HCI_ERR_PEER_USER))) { + l2cu_process_fixed_disc_cback(p_lcb); + p_lcb->link_state = LST_DISCONNECTING; + timeout = L2CAP_LINK_DISCONNECT_TOUT; + } else { + /* probably no buffer to send disconnect */ + timeout = BT_1SEC_TIMEOUT; + } + + if (timeout != 0xFFFF) { + btu_start_timer (&p_lcb->timer_entry, BTU_TTYPE_L2CAP_LINK, timeout); + } + } else { + /* Check in case we were flow controlled */ + l2c_link_check_send_pkts (p_lcb, NULL, NULL); + } +#endif ///SMP_INCLUDED == TRUE + } +} + +/******************************************************************************* +** +** Function l2c_info_timeout +** +** Description This function is called when an info request times out +** +** Returns void +** +*******************************************************************************/ +void l2c_info_timeout (tL2C_LCB *p_lcb) +{ + tL2C_CCB *p_ccb; +#if (CLASSIC_BT_INCLUDED == TRUE) + tL2C_CONN_INFO ci; +#endif ///CLASSIC_BT_INCLUDED == TRUE + /* If we timed out waiting for info response, just continue using basic if allowed */ + if (p_lcb->w4_info_rsp) { + /* If waiting for security complete, restart the info response timer */ + for (p_ccb = p_lcb->ccb_queue.p_first_ccb; p_ccb; p_ccb = p_ccb->p_next_ccb) { + if ( (p_ccb->chnl_state == CST_ORIG_W4_SEC_COMP) || (p_ccb->chnl_state == CST_TERM_W4_SEC_COMP) ) { + btu_start_timer (&p_lcb->info_timer_entry, BTU_TTYPE_L2CAP_INFO, L2CAP_WAIT_INFO_RSP_TOUT); + return; + } + } + + p_lcb->w4_info_rsp = FALSE; +#if (CLASSIC_BT_INCLUDED == TRUE) + /* If link is in process of being brought up */ + if ((p_lcb->link_state != LST_DISCONNECTED) && + (p_lcb->link_state != LST_DISCONNECTING)) { + /* Notify active channels that peer info is finished */ + if (p_lcb->ccb_queue.p_first_ccb) { + ci.status = HCI_SUCCESS; + memcpy (ci.bd_addr, p_lcb->remote_bd_addr, sizeof(BD_ADDR)); + + for (p_ccb = p_lcb->ccb_queue.p_first_ccb; p_ccb; p_ccb = p_ccb->p_next_ccb) { + l2c_csm_execute (p_ccb, L2CEVT_L2CAP_INFO_RSP, &ci); + } + } + } +#endif ///CLASSIC_BT_INCLUDED == TRUE + } +} + +/******************************************************************************* +** +** Function l2c_link_adjust_allocation +** +** Description This function is called when a link is created or removed +** to calculate the amount of packets each link may send to +** the HCI without an ack coming back. +** +** Currently, this is a simple allocation, dividing the +** number of Controller Packets by the number of links. In +** the future, QOS configuration should be examined. +** +** Returns void +** +*******************************************************************************/ +void l2c_link_adjust_allocation (void) +{ + UINT16 qq, qq_remainder; + tL2C_LCB *p_lcb; + UINT16 hi_quota, low_quota; + UINT16 num_lowpri_links = 0; + UINT16 num_hipri_links = 0; + UINT16 controller_xmit_quota = l2cb.num_lm_acl_bufs; + UINT16 high_pri_link_quota = L2CAP_HIGH_PRI_MIN_XMIT_QUOTA_A; + list_node_t *p_node = NULL; + + /* If no links active, reset buffer quotas and controller buffers */ + if (l2cb.num_links_active == 0) { + l2cb.controller_xmit_window = l2cb.num_lm_acl_bufs; + l2cb.round_robin_quota = l2cb.round_robin_unacked = 0; + return; + } + + /* First, count the links */ + for (p_node = list_begin(l2cb.p_lcb_pool); p_node; p_node = list_next(p_node)) { + p_lcb = list_node(p_node); + if (p_lcb->in_use) { + if (p_lcb->acl_priority == L2CAP_PRIORITY_HIGH) { + num_hipri_links++; + } else { + num_lowpri_links++; + } + } + } + + /* now adjust high priority link quota */ + low_quota = num_lowpri_links ? 1 : 0; + while ( (num_hipri_links * high_pri_link_quota + low_quota) > controller_xmit_quota ) { + high_pri_link_quota--; + } + + /* Work out the xmit quota and buffer quota high and low priorities */ + hi_quota = num_hipri_links * high_pri_link_quota; + low_quota = (hi_quota < controller_xmit_quota) ? controller_xmit_quota - hi_quota : 1; + + /* Work out and save the HCI xmit quota for each low priority link */ + + /* If each low priority link cannot have at least one buffer */ + if (num_lowpri_links > low_quota) { + l2cb.round_robin_quota = low_quota; + qq = qq_remainder = 1; + } + /* If each low priority link can have at least one buffer */ + else if (num_lowpri_links > 0) { + l2cb.round_robin_quota = 0; + l2cb.round_robin_unacked = 0; + qq = low_quota / num_lowpri_links; + qq_remainder = low_quota % num_lowpri_links; + } + /* If no low priority link */ + else { + l2cb.round_robin_quota = 0; + l2cb.round_robin_unacked = 0; + qq = qq_remainder = 1; + } + + L2CAP_TRACE_EVENT ("l2c_link_adjust_allocation num_hipri: %u num_lowpri: %u low_quota: %u round_robin_quota: %u qq: %u\n", + num_hipri_links, num_lowpri_links, low_quota, + l2cb.round_robin_quota, qq); + + /* Now, assign the quotas to each link */ + p_node = NULL; + for (p_node = list_begin(l2cb.p_lcb_pool); p_node; p_node = list_next(p_node)) { + p_lcb = list_node(p_node); + if (p_lcb->in_use) { + if (p_lcb->acl_priority == L2CAP_PRIORITY_HIGH) { + p_lcb->link_xmit_quota = high_pri_link_quota; + } else { + /* Safety check in case we switched to round-robin with something outstanding */ + /* if sent_not_acked is added into round_robin_unacked then don't add it again */ + /* l2cap keeps updating sent_not_acked for exiting from round robin */ + if (( p_lcb->link_xmit_quota > 0 ) && ( qq == 0 )) { + l2cb.round_robin_unacked += p_lcb->sent_not_acked; + } + + p_lcb->link_xmit_quota = qq; + if (qq_remainder > 0) { + p_lcb->link_xmit_quota++; + qq_remainder--; + } + } + + L2CAP_TRACE_EVENT ("l2c_link_adjust_allocation Priority: %d XmitQuota: %d\n", + p_lcb->acl_priority, p_lcb->link_xmit_quota); + + L2CAP_TRACE_EVENT (" SentNotAcked: %d RRUnacked: %d\n", + p_lcb->sent_not_acked, l2cb.round_robin_unacked); + + /* There is a special case where we have readjusted the link quotas and */ + /* this link may have sent anything but some other link sent packets so */ + /* so we may need a timer to kick off this link's transmissions. */ + if ( (p_lcb->link_state == LST_CONNECTED) + && (!list_is_empty(p_lcb->link_xmit_data_q)) + && (p_lcb->sent_not_acked < p_lcb->link_xmit_quota) ) { + btu_start_timer (&p_lcb->timer_entry, BTU_TTYPE_L2CAP_LINK, L2CAP_LINK_FLOW_CONTROL_TOUT); + } + } + } + +} + +/******************************************************************************* +** +** Function l2c_link_adjust_chnl_allocation +** +** Description This function is called to calculate the amount of packets each +** non-F&EC channel may have outstanding. +** +** Currently, this is a simple allocation, dividing the number +** of packets allocated to the link by the number of channels. In +** the future, QOS configuration should be examined. +** +** Returns void +** +*******************************************************************************/ + +bool l2c_chnl_allocation_in_ccb_list (void *p_ccb_node, void *context) +{ + UNUSED(context); + tL2C_CCB *p_ccb = (tL2C_CCB *)p_ccb_node; + if (p_ccb->in_use) { + tL2CAP_CHNL_DATA_RATE data_rate = p_ccb->tx_data_rate + p_ccb->rx_data_rate; + p_ccb->buff_quota = L2CAP_CBB_DEFAULT_DATA_RATE_BUFF_QUOTA * data_rate; + L2CAP_TRACE_EVENT("CID:0x%04x FCR Mode:%u Priority:%u TxDataRate:%u RxDataRate:%u Quota:%u", + p_ccb->local_cid, p_ccb->peer_cfg.fcr.mode, + p_ccb->ccb_priority, p_ccb->tx_data_rate, + p_ccb->rx_data_rate, p_ccb->buff_quota); + + /* quota may be change so check congestion */ + l2cu_check_channel_congestion (p_ccb); + } + return false; +} +void l2c_link_adjust_chnl_allocation (void) +{ + + L2CAP_TRACE_DEBUG ("l2c_link_adjust_chnl_allocation"); + + /* assign buffer quota to each channel based on its data rate requirement */ + list_foreach(l2cb.p_ccb_pool, l2c_chnl_allocation_in_ccb_list, NULL); +} + +/******************************************************************************* +** +** Function l2c_link_processs_num_bufs +** +** Description This function is called when a "controller buffer size" +** event is first received from the controller. It updates +** the L2CAP values. +** +** Returns void +** +*******************************************************************************/ +void l2c_link_processs_num_bufs (UINT16 num_lm_acl_bufs) +{ + l2cb.num_lm_acl_bufs = l2cb.controller_xmit_window = num_lm_acl_bufs; + +} + +/******************************************************************************* +** +** Function l2c_link_pkts_rcvd +** +** Description This function is called from the HCI transport when it is time +** tto send a "Host ready for packets" command. This is only when +** host to controller flow control is used. If fills in the arrays +** of numbers of packets and handles. +** +** Returns count of number of entries filled in +** +*******************************************************************************/ +UINT8 l2c_link_pkts_rcvd (UINT16 *num_pkts, UINT16 *handles) +{ + UINT8 num_found = 0; + + UNUSED(num_pkts); + UNUSED(handles); + + return (num_found); +} + +/******************************************************************************* +** +** Function l2c_link_role_changed +** +** Description This function is called whan a link's master/slave role change +** event is received. It simply updates the link control block. +** +** Returns void +** +*******************************************************************************/ +void l2c_link_role_changed (BD_ADDR bd_addr, UINT8 new_role, UINT8 hci_status) +{ + tL2C_LCB *p_lcb; + + /* Make sure not called from HCI Command Status (bd_addr and new_role are invalid) */ + if (bd_addr) { + /* If here came form hci role change event */ + p_lcb = l2cu_find_lcb_by_bd_addr (bd_addr, BT_TRANSPORT_BR_EDR); + if (p_lcb) { + p_lcb->link_role = new_role; + + /* Reset high priority link if needed */ + if (hci_status == HCI_SUCCESS) { + l2cu_set_acl_priority(bd_addr, p_lcb->acl_priority, TRUE); + } + } + } + + /* Check if any LCB was waiting for switch to be completed */ + list_node_t *p_node = NULL; + for (p_node = list_begin(l2cb.p_lcb_pool); p_node; p_node = list_next(p_node)) { + p_lcb = list_node(p_node); + if ((p_lcb->in_use) && (p_lcb->link_state == LST_CONNECTING_WAIT_SWITCH)) { + l2cu_create_conn_after_switch (p_lcb); + } + } +} + +/******************************************************************************* +** +** Function l2c_pin_code_request +** +** Description This function is called whan a pin-code request is received +** on a connection. If there are no channels active yet on the +** link, it extends the link first connection timer. Make sure +** that inactivity timer is not extended if PIN code happens +** to be after last ccb released. +** +** Returns void +** +*******************************************************************************/ +void l2c_pin_code_request (BD_ADDR bd_addr) +{ + tL2C_LCB *p_lcb = l2cu_find_lcb_by_bd_addr (bd_addr, BT_TRANSPORT_BR_EDR); + + if ( (p_lcb) && (!p_lcb->ccb_queue.p_first_ccb) ) { + btu_start_timer (&p_lcb->timer_entry, BTU_TTYPE_L2CAP_LINK, L2CAP_LINK_CONNECT_TOUT_EXT); + } +} + +#if L2CAP_WAKE_PARKED_LINK == TRUE +/******************************************************************************* +** +** Function l2c_link_check_power_mode +** +** Description This function is called to check power mode. +** +** Returns TRUE if link is going to be active from park +** FALSE if nothing to send or not in park mode +** +*******************************************************************************/ +BOOLEAN l2c_link_check_power_mode (tL2C_LCB *p_lcb) +{ + tBTM_PM_MODE mode; + tL2C_CCB *p_ccb; + BOOLEAN need_to_active = FALSE; + + /* + * We only switch park to active only if we have unsent packets + */ + if (list_is_empty(p_lcb->link_xmit_data_q)) { + for (p_ccb = p_lcb->ccb_queue.p_first_ccb; p_ccb; p_ccb = p_ccb->p_next_ccb) { + if (!fixed_queue_is_empty(p_ccb->xmit_hold_q)) { + need_to_active = TRUE; + break; + } + } + } else { + need_to_active = TRUE; + } + + /* if we have packets to send */ + if ( need_to_active ) { + /* check power mode */ + if (BTM_ReadPowerMode(p_lcb->remote_bd_addr, &mode) == BTM_SUCCESS) { + if ( mode == BTM_PM_STS_PENDING ) { + L2CAP_TRACE_DEBUG ("LCB(0x%x) is in PM pending state\n", p_lcb->handle); + + return TRUE; + } + } + } + return FALSE; +} +#endif /* L2CAP_WAKE_PARKED_LINK == TRUE) */ + +/******************************************************************************* +** +** Function l2c_link_check_send_pkts +** +** Description This function is called to check if it can send packets +** to the Host Controller. It may be passed the address of +** a packet to send. +** +** Returns void +** +*******************************************************************************/ +void l2c_link_check_send_pkts (tL2C_LCB *p_lcb, tL2C_CCB *p_ccb, BT_HDR *p_buf) +{ + BOOLEAN single_write = FALSE; + L2CAP_TRACE_DEBUG("%s",__func__); + /* Save the channel ID for faster counting */ + if (p_buf) { + if (p_ccb != NULL) { + p_buf->event = p_ccb->local_cid; + single_write = TRUE; + } else { + p_buf->event = 0; + } + + p_buf->layer_specific = 0; + list_append(p_lcb->link_xmit_data_q, p_buf); + + if (p_lcb->link_xmit_quota == 0) { +#if BLE_INCLUDED == TRUE + if (p_lcb->transport == BT_TRANSPORT_LE) { + l2cb.ble_check_round_robin = TRUE; + } else +#endif + { + l2cb.check_round_robin = TRUE; + } + } + } + + /* If this is called from uncongested callback context break recursive calling. + ** This LCB will be served when receiving number of completed packet event. + */ + if (l2cb.is_cong_cback_context) { + L2CAP_TRACE_ERROR("l2cab is_cong_cback_context"); + return; + } + + /* If we are in a scenario where there are not enough buffers for each link to + ** have at least 1, then do a round-robin for all the LCBs + */ + if ( (p_lcb == NULL) || (p_lcb->link_xmit_quota == 0) ) { + list_node_t *p_node = NULL; + tL2C_LCB *p_lcb_cur = NULL; + if (p_lcb == NULL) { + p_node = list_begin(l2cb.p_lcb_pool); + p_lcb = list_node(p_node); + } else if (!single_write) { + for (p_node = list_begin(l2cb.p_lcb_pool); p_node; p_node = list_next(p_node)) { + p_lcb_cur = list_node(p_node); + if (p_lcb_cur == p_lcb) { + p_node = list_next(p_node); + p_lcb = list_node(p_node); + break; + } + } + } + + /* Loop through, starting at the next */ + for ( ; p_node; p_node = list_next(p_node)) { + p_lcb = list_node(p_node); +#if (BLE_INCLUDED == TRUE) + L2CAP_TRACE_DEBUG("window = %d,robin_unacked = %d,robin_quota=%d",l2cb.controller_le_xmit_window,l2cb.ble_round_robin_unacked,l2cb.ble_round_robin_quota); +#endif ///BLE_INCLUDED == TRUE + /* If controller window is full, nothing to do */ + if (((l2cb.controller_xmit_window == 0 || + (l2cb.round_robin_unacked >= l2cb.round_robin_quota)) +#if (BLE_INCLUDED == TRUE) + && (p_lcb->transport == BT_TRANSPORT_BR_EDR) + ) + || (p_lcb->transport == BT_TRANSPORT_LE && + (l2cb.ble_round_robin_unacked >= l2cb.ble_round_robin_quota || + l2cb.controller_le_xmit_window == 0 ))) +#else + )) +#endif ///BLE_INCLUDED == TRUE + break; + + + /* Check for wraparound */ + if (p_node == list_end(l2cb.p_lcb_pool)) { + p_node = list_begin(l2cb.p_lcb_pool); + p_lcb = list_node(p_node); + } + L2CAP_TRACE_DEBUG("in_use=%d,segment_being_sent=%d,link_state=%d,link_xmit_quota=%d",p_lcb->in_use,p_lcb->partial_segment_being_sent,p_lcb->link_state,p_lcb->link_xmit_quota); + if ( (!p_lcb->in_use) + || (p_lcb->partial_segment_being_sent) + || (p_lcb->link_state != LST_CONNECTED) + || (p_lcb->link_xmit_quota != 0) + || (L2C_LINK_CHECK_POWER_MODE (p_lcb)) ) { + continue; + } + + /* See if we can send anything from the Link Queue */ + if (!list_is_empty(p_lcb->link_xmit_data_q)) { + p_buf = (BT_HDR *)list_front(p_lcb->link_xmit_data_q); + list_remove(p_lcb->link_xmit_data_q, p_buf); + l2c_link_send_to_lower (p_lcb, p_buf); + } else if (single_write) { + /* If only doing one write, break out */ + break; + } + /* If nothing on the link queue, check the channel queue */ + else if ((p_buf = l2cu_get_next_buffer_to_send (p_lcb)) != NULL) { + l2c_link_send_to_lower (p_lcb, p_buf); + } + } + + /* If we finished without using up our quota, no need for a safety check */ + if ( (l2cb.controller_xmit_window > 0) + && (l2cb.round_robin_unacked < l2cb.round_robin_quota) +#if (BLE_INCLUDED == TRUE) + && (p_lcb->transport == BT_TRANSPORT_BR_EDR) +#endif + ) { + l2cb.check_round_robin = FALSE; + } + +#if (BLE_INCLUDED == TRUE) + if ( (l2cb.controller_le_xmit_window > 0) + && (l2cb.ble_round_robin_unacked < l2cb.ble_round_robin_quota) + && (p_lcb->transport == BT_TRANSPORT_LE)) { + l2cb.ble_check_round_robin = FALSE; + } +#endif + } else { /* if this is not round-robin service */ + /* If a partial segment is being sent, can't send anything else */ + L2CAP_TRACE_DEBUG("partial_segment_being_sent=%d,link_state=%d,power_mode=%d",p_lcb->partial_segment_being_sent,p_lcb->link_state,L2C_LINK_CHECK_POWER_MODE (p_lcb)); + if ( (p_lcb->partial_segment_being_sent) + || (p_lcb->link_state != LST_CONNECTED) + || (L2C_LINK_CHECK_POWER_MODE (p_lcb)) ) { + return; + } + + /* See if we can send anything from the link queue */ +#if (BLE_INCLUDED == TRUE) + while ( ((l2cb.controller_xmit_window != 0 && (p_lcb->transport == BT_TRANSPORT_BR_EDR)) || + (l2cb.controller_le_xmit_window != 0 && (p_lcb->transport == BT_TRANSPORT_LE))) + && (p_lcb->sent_not_acked < p_lcb->link_xmit_quota)) +#else + while ( (l2cb.controller_xmit_window != 0) + && (p_lcb->sent_not_acked < p_lcb->link_xmit_quota)) +#endif + { + if (list_is_empty(p_lcb->link_xmit_data_q)) { + break; + } + + p_buf = (BT_HDR *)list_front(p_lcb->link_xmit_data_q); + list_remove(p_lcb->link_xmit_data_q, p_buf); + if (!l2c_link_send_to_lower (p_lcb, p_buf)) { + break; + } + } + + if (!single_write) { + /* See if we can send anything for any channel */ +#if (BLE_INCLUDED == TRUE) + while ( ((l2cb.controller_xmit_window != 0 && (p_lcb->transport == BT_TRANSPORT_BR_EDR)) || + (l2cb.controller_le_xmit_window != 0 && (p_lcb->transport == BT_TRANSPORT_LE))) + && (p_lcb->sent_not_acked < p_lcb->link_xmit_quota)) +#else + while ((l2cb.controller_xmit_window != 0) && (p_lcb->sent_not_acked < p_lcb->link_xmit_quota)) +#endif + { + //need check flag: partial_segment_being_sent + if ( (p_lcb->partial_segment_being_sent) + || (p_lcb->link_state != LST_CONNECTED) + || (L2C_LINK_CHECK_POWER_MODE (p_lcb)) ) { + break; + } + //L2CAP_TRACE_DEBUG("l2cu_get_next_buffer_to_send = %p",l2cu_get_next_buffer_to_send(p_lcb)); + if ((p_buf = l2cu_get_next_buffer_to_send (p_lcb)) == NULL) { + break; + } + + if (!l2c_link_send_to_lower (p_lcb, p_buf)) { + break; + } + } + } + + /* There is a special case where we have readjusted the link quotas and */ + /* this link may have sent anything but some other link sent packets so */ + /* so we may need a timer to kick off this link's transmissions. */ + if ( (!list_is_empty(p_lcb->link_xmit_data_q)) && (p_lcb->sent_not_acked < p_lcb->link_xmit_quota) ) { + btu_start_timer (&p_lcb->timer_entry, BTU_TTYPE_L2CAP_LINK, L2CAP_LINK_FLOW_CONTROL_TOUT); + } + } + +} + +/******************************************************************************* +** +** Function l2c_link_send_to_lower +** +** Description This function queues the buffer for HCI transmission +** +** Returns TRUE for success, FALSE for fail +** +*******************************************************************************/ +static BOOLEAN l2c_link_send_to_lower (tL2C_LCB *p_lcb, BT_HDR *p_buf) +{ + UINT16 num_segs; + UINT16 xmit_window, acl_data_size; + const controller_t *controller = controller_get_interface(); + L2CAP_TRACE_DEBUG("%s",__func__); + if ((p_buf->len <= controller->get_acl_packet_size_classic() +#if (BLE_INCLUDED == TRUE) + && (p_lcb->transport == BT_TRANSPORT_BR_EDR)) || + ((p_lcb->transport == BT_TRANSPORT_LE) && (p_buf->len <= controller->get_acl_packet_size_ble())) +#else + ) +#endif + ) { + if (p_lcb->link_xmit_quota == 0) { +#if (BLE_INCLUDED == TRUE) + if (p_lcb->transport == BT_TRANSPORT_LE) { + l2cb.ble_round_robin_unacked++; + } else +#endif + l2cb.round_robin_unacked++; + } + p_lcb->sent_not_acked++; + p_buf->layer_specific = 0; + +#if (BLE_INCLUDED == TRUE) + if (p_lcb->transport == BT_TRANSPORT_LE) { + l2cb.controller_le_xmit_window--; + bte_main_hci_send(p_buf, (UINT16)(BT_EVT_TO_LM_HCI_ACL | LOCAL_BLE_CONTROLLER_ID)); + } else +#endif + { + l2cb.controller_xmit_window--; + bte_main_hci_send(p_buf, BT_EVT_TO_LM_HCI_ACL); + } + } else { +#if BLE_INCLUDED == TRUE + if (p_lcb->transport == BT_TRANSPORT_LE) { + acl_data_size = controller->get_acl_data_size_ble(); + xmit_window = l2cb.controller_le_xmit_window; + + } else +#endif + { + acl_data_size = controller->get_acl_data_size_classic(); + xmit_window = l2cb.controller_xmit_window; + } + num_segs = (p_buf->len - HCI_DATA_PREAMBLE_SIZE + acl_data_size - 1) / acl_data_size; + + + /* If doing round-robin, then only 1 segment each time */ + if (p_lcb->link_xmit_quota == 0) { + num_segs = 1; + p_lcb->partial_segment_being_sent = TRUE; + } else { + /* Multi-segment packet. Make sure it can fit */ + if (num_segs > xmit_window) { + num_segs = xmit_window; + p_lcb->partial_segment_being_sent = TRUE; + } + + if (num_segs > (p_lcb->link_xmit_quota - p_lcb->sent_not_acked)) { + num_segs = (p_lcb->link_xmit_quota - p_lcb->sent_not_acked); + p_lcb->partial_segment_being_sent = TRUE; + } + } + + p_buf->layer_specific = num_segs; +#if BLE_INCLUDED == TRUE + if (p_lcb->transport == BT_TRANSPORT_LE) { + l2cb.controller_le_xmit_window -= num_segs; + if (p_lcb->link_xmit_quota == 0) { + l2cb.ble_round_robin_unacked += num_segs; + } + } else +#endif + { + l2cb.controller_xmit_window -= num_segs; + + if (p_lcb->link_xmit_quota == 0) { + l2cb.round_robin_unacked += num_segs; + } + } + + p_lcb->sent_not_acked += num_segs; +#if BLE_INCLUDED == TRUE + if (p_lcb->transport == BT_TRANSPORT_LE) { + bte_main_hci_send(p_buf, (UINT16)(BT_EVT_TO_LM_HCI_ACL | LOCAL_BLE_CONTROLLER_ID)); + } else +#endif + { + bte_main_hci_send(p_buf, BT_EVT_TO_LM_HCI_ACL); + } + } + +#if (L2CAP_HCI_FLOW_CONTROL_DEBUG == TRUE) +#if (BLE_INCLUDED == TRUE) + if (p_lcb->transport == BT_TRANSPORT_LE) { + L2CAP_TRACE_DEBUG ("TotalWin=%d,Hndl=0x%x,Quota=%d,Unack=%d,RRQuota=%d,RRUnack=%d", + l2cb.controller_le_xmit_window, + p_lcb->handle, + p_lcb->link_xmit_quota, p_lcb->sent_not_acked, + l2cb.ble_round_robin_quota, l2cb.ble_round_robin_unacked); + } else +#endif + { + L2CAP_TRACE_DEBUG ("TotalWin=%d,Hndl=0x%x,Quota=%d,Unack=%d,RRQuota=%d,RRUnack=%d", + l2cb.controller_xmit_window, + p_lcb->handle, + p_lcb->link_xmit_quota, p_lcb->sent_not_acked, + l2cb.round_robin_quota, l2cb.round_robin_unacked); + } +#endif + + return TRUE; +} + +/******************************************************************************* +** +** Function l2c_link_process_num_completed_pkts +** +** Description This function is called when a "number-of-completed-packets" +** event is received from the controller. It updates all the +** LCB transmit counts. +** +** Returns void +** +*******************************************************************************/ +void l2c_link_process_num_completed_pkts (UINT8 *p) +{ + UINT8 num_handles, xx; + UINT16 handle; + UINT16 num_sent; + tL2C_LCB *p_lcb; + + STREAM_TO_UINT8 (num_handles, p); + + for (xx = 0; xx < num_handles; xx++) { + STREAM_TO_UINT16 (handle, p); + STREAM_TO_UINT16 (num_sent, p); + + p_lcb = l2cu_find_lcb_by_handle (handle); + + /* Callback for number of completed packet event */ + /* Originally designed for [3DSG] */ + if ((p_lcb != NULL) && (p_lcb->p_nocp_cb)) { + L2CAP_TRACE_DEBUG ("L2CAP - calling NoCP callback"); + (*p_lcb->p_nocp_cb)(p_lcb->remote_bd_addr); + } + + if (p_lcb) { +#if (BLE_INCLUDED == TRUE) + if (p_lcb && (p_lcb->transport == BT_TRANSPORT_LE)) { + l2cb.controller_le_xmit_window += num_sent; + } else +#endif + { + /* Maintain the total window to the controller */ + l2cb.controller_xmit_window += num_sent; + } + /* If doing round-robin, adjust communal counts */ + if (p_lcb->link_xmit_quota == 0) { +#if BLE_INCLUDED == TRUE + if (p_lcb->transport == BT_TRANSPORT_LE) { + /* Don't go negative */ + if (l2cb.ble_round_robin_unacked > num_sent) { + l2cb.ble_round_robin_unacked -= num_sent; + } else { + l2cb.ble_round_robin_unacked = 0; + } + } else +#endif + { + /* Don't go negative */ + if (l2cb.round_robin_unacked > num_sent) { + l2cb.round_robin_unacked -= num_sent; + } else { + l2cb.round_robin_unacked = 0; + } + } + } + + /* Don't go negative */ + if (p_lcb->sent_not_acked > num_sent) { + p_lcb->sent_not_acked -= num_sent; + } else { + p_lcb->sent_not_acked = 0; + } + + l2c_link_check_send_pkts (p_lcb, NULL, NULL); + + /* If we were doing round-robin for low priority links, check 'em */ + if ( (p_lcb->acl_priority == L2CAP_PRIORITY_HIGH) + && (l2cb.check_round_robin) + && (l2cb.round_robin_unacked < l2cb.round_robin_quota) ) { + l2c_link_check_send_pkts (NULL, NULL, NULL); + } +#if BLE_INCLUDED == TRUE + if ((p_lcb->transport == BT_TRANSPORT_LE) + && (p_lcb->acl_priority == L2CAP_PRIORITY_HIGH) + && ((l2cb.ble_check_round_robin) + && (l2cb.ble_round_robin_unacked < l2cb.ble_round_robin_quota))) { + l2c_link_check_send_pkts (NULL, NULL, NULL); + } +#endif + } + +#if (L2CAP_HCI_FLOW_CONTROL_DEBUG == TRUE) + if (p_lcb) { +#if (BLE_INCLUDED == TRUE) + if (p_lcb->transport == BT_TRANSPORT_LE) { + L2CAP_TRACE_DEBUG ("TotalWin=%d,LinkUnack(0x%x)=%d,RRCheck=%d,RRUnack=%d\n", + l2cb.controller_le_xmit_window, + p_lcb->handle, p_lcb->sent_not_acked, + l2cb.ble_check_round_robin, l2cb.ble_round_robin_unacked); + } else +#endif + { + L2CAP_TRACE_DEBUG ("TotalWin=%d,LinkUnack(0x%x)=%d,RRCheck=%d,RRUnack=%d\n", + l2cb.controller_xmit_window, + p_lcb->handle, p_lcb->sent_not_acked, + l2cb.check_round_robin, l2cb.round_robin_unacked); + + } + } else { +#if (BLE_INCLUDED == TRUE) + L2CAP_TRACE_DEBUG ("TotalWin=%d LE_Win: %d, Handle=0x%x, RRCheck=%d, RRUnack=%d\n", + l2cb.controller_xmit_window, + l2cb.controller_le_xmit_window, + handle, + l2cb.ble_check_round_robin, l2cb.ble_round_robin_unacked); +#else + L2CAP_TRACE_DEBUG ("TotalWin=%d Handle=0x%x RRCheck=%d RRUnack=%d\n", + l2cb.controller_xmit_window, + handle, + l2cb.check_round_robin, l2cb.round_robin_unacked); +#endif + } +#endif + } + +#if (defined(HCILP_INCLUDED) && HCILP_INCLUDED == TRUE) + /* only full stack can enable sleep mode */ + btu_check_bt_sleep (); +#endif +} + +/******************************************************************************* +** +** Function l2c_link_segments_xmitted +** +** Description This function is called from the HCI Interface when an ACL +** data packet segment is transmitted. +** +** Returns void +** +*******************************************************************************/ +void l2c_link_segments_xmitted (BT_HDR *p_msg) +{ + UINT8 *p = (UINT8 *)(p_msg + 1) + p_msg->offset; + UINT16 handle; + tL2C_LCB *p_lcb; + + /* Extract the handle */ + STREAM_TO_UINT16 (handle, p); + handle = HCID_GET_HANDLE (handle); + + /* Find the LCB based on the handle */ + if ((p_lcb = l2cu_find_lcb_by_handle (handle)) == NULL) { + L2CAP_TRACE_WARNING ("L2CAP - rcvd segment complete, unknown handle: %d\n", handle); + osi_free (p_msg); + return; + } + + if (p_lcb->link_state == LST_CONNECTED) { + /* Enqueue the buffer to the head of the transmit queue, and see */ + /* if we can transmit anything more. */ + list_prepend(p_lcb->link_xmit_data_q, p_msg); + + p_lcb->partial_segment_being_sent = FALSE; + + l2c_link_check_send_pkts (p_lcb, NULL, NULL); + } else { + osi_free (p_msg); + } +} diff --git a/lib/bt/host/bluedroid/stack/l2cap/l2c_main.c b/lib/bt/host/bluedroid/stack/l2cap/l2c_main.c new file mode 100644 index 00000000..dfdef99b --- /dev/null +++ b/lib/bt/host/bluedroid/stack/l2cap/l2c_main.c @@ -0,0 +1,1065 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains the main L2CAP entry points + * + ******************************************************************************/ + +#include +#include +//#include + +#include "device/controller.h" +//#include "btcore/include/counter.h" +#include "common/bt_target.h" +#include "btm_int.h" +#include "stack/btu.h" +#include "stack/hcimsgs.h" +#include "stack/l2c_api.h" +#include "l2c_int.h" +#include "stack/l2cdefs.h" +//#include "osi/include/log.h" + +/********************************************************************************/ +/* L O C A L F U N C T I O N P R O T O T Y P E S */ +/********************************************************************************/ +#if (CLASSIC_BT_INCLUDED == TRUE) +static void process_l2cap_cmd (tL2C_LCB *p_lcb, UINT8 *p, UINT16 pkt_len); +#endif ///CLASSIC_BT_INCLUDED == TRUE +/********************************************************************************/ +/* G L O B A L L 2 C A P D A T A */ +/********************************************************************************/ +#if L2C_DYNAMIC_MEMORY == FALSE +tL2C_CB l2cb; +#else +tL2C_CB *l2c_cb_ptr; +#endif + +#if BT_CLASSIC_BQB_INCLUDED +static BOOLEAN s_l2cap_bqb_bad_cmd_len_rej_flag = FALSE; +#endif /* BT_CLASSIC_BQB_INCLUDED */ + +#if 0 //Unused +/******************************************************************************* +** +** Function l2c_bcst_msg +** +** Description +** +** Returns void +** +*******************************************************************************/ +void l2c_bcst_msg( BT_HDR *p_buf, UINT16 psm ) +{ + UINT8 *p; + + /* Ensure we have enough space in the buffer for the L2CAP and HCI headers */ + if (p_buf->offset < L2CAP_BCST_MIN_OFFSET) { + L2CAP_TRACE_ERROR ("L2CAP - cannot send buffer, offset: %d", p_buf->offset); + osi_free (p_buf); + return; + } + + /* Step back some bytes to add the headers */ + p_buf->offset -= (HCI_DATA_PREAMBLE_SIZE + L2CAP_PKT_OVERHEAD + L2CAP_BCST_OVERHEAD); + p_buf->len += L2CAP_PKT_OVERHEAD + L2CAP_BCST_OVERHEAD; + + /* Set the pointer to the beginning of the data */ + p = (UINT8 *)(p_buf + 1) + p_buf->offset; + + /* First, the HCI transport header */ + UINT16_TO_STREAM (p, 0x0050 | (L2CAP_PKT_START << 12) | (2 << 14)); + + uint16_t acl_data_size = controller_get_interface()->get_acl_data_size_classic(); + /* The HCI transport will segment the buffers. */ + if (p_buf->len > acl_data_size) { + UINT16_TO_STREAM (p, acl_data_size); + } else { + UINT16_TO_STREAM (p, p_buf->len); + } + + /* Now the L2CAP header */ + UINT16_TO_STREAM (p, p_buf->len - L2CAP_PKT_OVERHEAD); + UINT16_TO_STREAM (p, L2CAP_CONNECTIONLESS_CID); + UINT16_TO_STREAM (p, psm); + + p_buf->len += HCI_DATA_PREAMBLE_SIZE; + + if (p_buf->len <= controller_get_interface()->get_acl_packet_size_classic()) { + //counter_add("l2cap.ch2.tx.bytes", p_buf->len); + //counter_add("l2cap.ch2.tx.pkts", 1); + + bte_main_hci_send(p_buf, BT_EVT_TO_LM_HCI_ACL); + } +} +#endif + +/******************************************************************************* +** +** Function l2cap_bqb_bad_cmd_len_rej_ctrl +** +** Description Control rejecting L2CAP signaling PDUs with incorrect length +** for BQB test. +** +** Returns void +** +*******************************************************************************/ +#if BT_CLASSIC_BQB_INCLUDED +void l2cap_bqb_bad_cmd_len_rej_ctrl(BOOLEAN enable) +{ + s_l2cap_bqb_bad_cmd_len_rej_flag = enable; +} +#endif /* BT_CLASSIC_BQB_INCLUDED */ + + +/******************************************************************************* +** +** Function l2c_rcv_acl_data +** +** Description This function is called from the HCI Interface when an ACL +** data packet is received. +** +** Returns void +** +*******************************************************************************/ +void l2c_rcv_acl_data (BT_HDR *p_msg) +{ + UINT8 *p = (UINT8 *)(p_msg + 1) + p_msg->offset; + UINT16 handle, hci_len; + UINT8 pkt_type; + tL2C_LCB *p_lcb; + tL2C_CCB *p_ccb = NULL; + UINT16 l2cap_len, rcv_cid; +#if (!CONFIG_BT_STACK_NO_LOG) + UINT16 psm; +#endif + UINT16 credit; + + /* Extract the handle */ + STREAM_TO_UINT16 (handle, p); + pkt_type = HCID_GET_EVENT (handle); + handle = HCID_GET_HANDLE (handle); + + /* Since the HCI Transport is putting segmented packets back together, we */ + /* should never get a valid packet with the type set to "continuation" */ + if (pkt_type != L2CAP_PKT_CONTINUE) { + /* Find the LCB based on the handle */ + if ((p_lcb = l2cu_find_lcb_by_handle (handle)) == NULL) { + UINT8 cmd_code; + + /* There is a slight possibility (specifically with USB) that we get an */ + /* L2CAP connection request before we get the HCI connection complete. */ + /* So for these types of messages, hold them for up to 2 seconds. */ + STREAM_TO_UINT16 (hci_len, p); + STREAM_TO_UINT16 (l2cap_len, p); + STREAM_TO_UINT16 (rcv_cid, p); + STREAM_TO_UINT8 (cmd_code, p); + + if ((p_msg->layer_specific == 0) && (rcv_cid == L2CAP_SIGNALLING_CID) + && (cmd_code == L2CAP_CMD_INFO_REQ || cmd_code == L2CAP_CMD_CONN_REQ)) { + L2CAP_TRACE_WARNING ("L2CAP - holding ACL for unknown handle:%d ls:%d" + " cid:%d opcode:%d cur count:%d", handle, p_msg->layer_specific, + rcv_cid, cmd_code, list_length(l2cb.rcv_pending_q)); + p_msg->layer_specific = 2; + list_append(l2cb.rcv_pending_q, p_msg); + + if (list_length(l2cb.rcv_pending_q) == 1) { + btu_start_timer (&l2cb.rcv_hold_tle, BTU_TTYPE_L2CAP_HOLD, BT_1SEC_TIMEOUT); + } + + return; + } else { + L2CAP_TRACE_ERROR ("L2CAP - rcvd ACL for unknown handle:%d ls:%d cid:%d" + " opcode:%d cur count:%d", handle, p_msg->layer_specific, rcv_cid, + cmd_code, list_length(l2cb.rcv_pending_q)); + } + osi_free (p_msg); + return; + } + } else { + L2CAP_TRACE_WARNING ("L2CAP - expected pkt start or complete, got: %d", pkt_type); + osi_free (p_msg); + return; + } + + /* Extract the length and update the buffer header */ + STREAM_TO_UINT16 (hci_len, p); + p_msg->offset += 4; + + /* Extract the length and CID */ + STREAM_TO_UINT16 (l2cap_len, p); + STREAM_TO_UINT16 (rcv_cid, p); + +#if BLE_INCLUDED == TRUE + /* for BLE channel, always notify connection when ACL data received on the link */ + if (p_lcb && p_lcb->transport == BT_TRANSPORT_LE && p_lcb->link_state != LST_DISCONNECTING) + /* only process fixed channel data as channel open indication when link is not in disconnecting mode */ + { + l2cble_notify_le_connection(p_lcb->remote_bd_addr); + } +#endif + L2CAP_TRACE_DEBUG ("L2CAP - rcv_cid CID: 0x%04x\n", rcv_cid); + /* Find the CCB for this CID */ + if (rcv_cid >= L2CAP_BASE_APPL_CID) { + if ((p_ccb = l2cu_find_ccb_by_cid (p_lcb, rcv_cid)) == NULL) { + L2CAP_TRACE_WARNING ("L2CAP - unknown CID: 0x%04x", rcv_cid); + osi_free (p_msg); + return; + } + } + + if (hci_len >= L2CAP_PKT_OVERHEAD) { /* Must receive at least the L2CAP length and CID.*/ + p_msg->len = hci_len - L2CAP_PKT_OVERHEAD; + p_msg->offset += L2CAP_PKT_OVERHEAD; + } else { + L2CAP_TRACE_WARNING ("L2CAP - got incorrect hci header" ); + osi_free (p_msg); + return; + } + + if (l2cap_len != p_msg->len) { + L2CAP_TRACE_WARNING ("L2CAP - bad length in pkt. Exp: %d Act: %d", + l2cap_len, p_msg->len); + + osi_free (p_msg); + return; + } + + /* Send the data through the channel state machine */ + if (rcv_cid == L2CAP_SIGNALLING_CID) { + //counter_add("l2cap.sig.rx.bytes", l2cap_len); + //counter_add("l2cap.sig.rx.pkts", 1); +#if (CLASSIC_BT_INCLUDED == TRUE) + process_l2cap_cmd (p_lcb, p, l2cap_len); +#endif ///CLASSIC_BT_INCLUDED == TRUE + osi_free (p_msg); + } else if (rcv_cid == L2CAP_CONNECTIONLESS_CID) { + //counter_add("l2cap.ch2.rx.bytes", l2cap_len); + //counter_add("l2cap.ch2.rx.pkts", 1); + /* process_connectionless_data (p_lcb); */ +#if !CONFIG_BT_STACK_NO_LOG + STREAM_TO_UINT16 (psm, p); +#endif + L2CAP_TRACE_DEBUG( "GOT CONNECTIONLESS DATA PSM:%d", psm ) ; + +#if (L2CAP_UCD_INCLUDED == TRUE) + /* if it is not broadcast, check UCD registration */ + if ( l2c_ucd_check_rx_pkts( p_lcb, p_msg ) ) { + /* nothing to do */ + } else +#endif + { + osi_free (p_msg); + } + } +#if (BLE_INCLUDED == TRUE) + else if (rcv_cid == L2CAP_BLE_SIGNALLING_CID) { + //counter_add("l2cap.ble.rx.bytes", l2cap_len); + //counter_add("l2cap.ble.rx.pkts", 1); + l2cble_process_sig_cmd (p_lcb, p, l2cap_len); + osi_free (p_msg); + } +#endif +#if (L2CAP_NUM_FIXED_CHNLS > 0) + else if ((rcv_cid >= L2CAP_FIRST_FIXED_CHNL) && (rcv_cid <= L2CAP_LAST_FIXED_CHNL) && + (l2cb.fixed_reg[rcv_cid - L2CAP_FIRST_FIXED_CHNL].pL2CA_FixedData_Cb != NULL) ) { + //counter_add("l2cap.fix.rx.bytes", l2cap_len); + //counter_add("l2cap.fix.rx.pkts", 1); + /* If no CCB for this channel, allocate one */ + if (p_lcb && + /* only process fixed channel data when link is open or wait for data indication */ + (p_lcb->link_state != LST_DISCONNECTING) && + l2cu_initialize_fixed_ccb (p_lcb, rcv_cid, &l2cb.fixed_reg[rcv_cid - L2CAP_FIRST_FIXED_CHNL].fixed_chnl_opts)) { + p_ccb = p_lcb->p_fixed_ccbs[rcv_cid - L2CAP_FIRST_FIXED_CHNL]; + + if (p_ccb->peer_cfg.fcr.mode != L2CAP_FCR_BASIC_MODE) { +#if (CLASSIC_BT_INCLUDED == TRUE) + l2c_fcr_proc_pdu (p_ccb, p_msg); +#endif ///CLASSIC_BT_INCLUDED == TRUE + } else { + (*l2cb.fixed_reg[rcv_cid - L2CAP_FIRST_FIXED_CHNL].pL2CA_FixedData_Cb) + (rcv_cid, p_lcb->remote_bd_addr, p_msg); + } + } else { + osi_free (p_msg); + } + } +#endif + + else { + //counter_add("l2cap.dyn.rx.bytes", l2cap_len); + //counter_add("l2cap.dyn.rx.pkts", 1); + if (p_ccb == NULL) { + osi_free (p_msg); + } else { + if (p_lcb->transport == BT_TRANSPORT_LE) { + // Got a pkt, valid send out credits to the peer device + credit = L2CAP_LE_DEFAULT_CREDIT; + L2CAP_TRACE_DEBUG("%s Credits received %d",__func__, credit); + if((p_ccb->peer_conn_cfg.credits + credit) > L2CAP_LE_MAX_CREDIT) { + /* we have received credits more than max coc credits, + * so disconnecting the Le Coc Channel + */ +#if (BLE_INCLUDED == TRUE) + l2cble_send_peer_disc_req (p_ccb); +#endif ///BLE_INCLUDED == TRUE + } else { + p_ccb->peer_conn_cfg.credits += credit; + l2c_link_check_send_pkts (p_ccb->p_lcb, NULL, NULL); + } + } + /* Basic mode packets go straight to the state machine */ + if (p_ccb->peer_cfg.fcr.mode == L2CAP_FCR_BASIC_MODE) { +#if (CLASSIC_BT_INCLUDED == TRUE) + l2c_csm_execute (p_ccb, L2CEVT_L2CAP_DATA, p_msg); +#endif ///CLASSIC_BT_INCLUDED == TRUE + } else { + /* eRTM or streaming mode, so we need to validate states first */ + if ((p_ccb->chnl_state == CST_OPEN) || (p_ccb->chnl_state == CST_CONFIG)) { +#if (CLASSIC_BT_INCLUDED == TRUE) + l2c_fcr_proc_pdu (p_ccb, p_msg); +#endif ///CLASSIC_BT_INCLUDED == TRUE + } else { + osi_free (p_msg); + } + } + } + } +} + +/******************************************************************************* +** +** Function process_l2cap_cmd +** +** Description This function is called when a packet is received on the +** L2CAP signalling CID +** +** Returns void +** +*******************************************************************************/ +#if (CLASSIC_BT_INCLUDED == TRUE) +static void process_l2cap_cmd (tL2C_LCB *p_lcb, UINT8 *p, UINT16 pkt_len) +{ + UINT8 *p_pkt_end, *p_next_cmd, *p_cfg_end, *p_cfg_start; + UINT8 cmd_code, cfg_code, cfg_len, id; + tL2C_CONN_INFO con_info; + tL2CAP_CFG_INFO cfg_info; + UINT16 rej_reason, rej_mtu, lcid, rcid, info_type; + tL2C_CCB *p_ccb; + tL2C_RCB *p_rcb; + BOOLEAN cfg_rej, pkt_size_rej = FALSE; + UINT16 cfg_rej_len, cmd_len; + UINT16 result; + tL2C_CONN_INFO ci; + +#if (defined BLE_INCLUDED && BLE_INCLUDED == TRUE) + /* if l2cap command received in CID 1 on top of an LE link, ignore this command */ + if (p_lcb->transport == BT_TRANSPORT_LE) { + return; + } +#endif + + /* Reject the packet if it exceeds the default Signalling Channel MTU */ + if (pkt_len > L2CAP_DEFAULT_MTU) { + /* Core Spec requires a single response to the first command found in a multi-command + ** L2cap packet. If only responses in the packet, then it will be ignored. + ** Here we simply mark the bad packet and decide which cmd ID to reject later + */ + pkt_size_rej = TRUE; + L2CAP_TRACE_ERROR ("L2CAP SIG MTU Pkt Len Exceeded (672) -> pkt_len: %d", pkt_len); + } + + p_next_cmd = p; + p_pkt_end = p + pkt_len; + + memset (&cfg_info, 0, sizeof(cfg_info)); + + /* An L2CAP packet may contain multiple commands */ + while (TRUE) { + /* Smallest command is 4 bytes */ + if ((p = p_next_cmd) > (p_pkt_end - 4)) { + break; + } + + STREAM_TO_UINT8 (cmd_code, p); + STREAM_TO_UINT8 (id, p); + STREAM_TO_UINT16 (cmd_len, p); + + /* Check command length does not exceed packet length */ + if ((p_next_cmd = p + cmd_len) > p_pkt_end) { + L2CAP_TRACE_WARNING ("Command len bad pkt_len: %d cmd_len: %d code: %d", + pkt_len, cmd_len, cmd_code); + break; + } + + L2CAP_TRACE_DEBUG ("cmd_code: %d, id:%d, cmd_len:%d", cmd_code, id, cmd_len); + + /* Bad L2CAP packet length, look or cmd to reject */ + if (pkt_size_rej) { + /* If command found rejected it and we're done, otherwise keep looking */ + if (l2c_is_cmd_rejected(cmd_code, id, p_lcb)) { + return; + } else { + continue; /* Look for next cmd/response in current packet */ + } + } + + switch (cmd_code) { + case L2CAP_CMD_REJECT: + STREAM_TO_UINT16 (rej_reason, p); + if (rej_reason == L2CAP_CMD_REJ_MTU_EXCEEDED) { + STREAM_TO_UINT16 (rej_mtu, p); + /* What to do with the MTU reject ? We have negotiated an MTU. For now */ + /* we will ignore it and let a higher protocol timeout take care of it */ + L2CAP_TRACE_WARNING ("L2CAP - MTU rej Handle: %d MTU: %d", p_lcb->handle, rej_mtu); + } + if (rej_reason == L2CAP_CMD_REJ_INVALID_CID) { + STREAM_TO_UINT16 (rcid, p); + STREAM_TO_UINT16 (lcid, p); + + L2CAP_TRACE_WARNING ("L2CAP - rej with CID invalid, LCID: 0x%04x RCID: 0x%04x", lcid, rcid); + + /* Remote CID invalid. Treat as a disconnect */ + if (((p_ccb = l2cu_find_ccb_by_cid (p_lcb, lcid)) != NULL) + && (p_ccb->remote_cid == rcid)) { + /* Fake link disconnect - no reply is generated */ + l2c_csm_execute (p_ccb, L2CEVT_LP_DISCONNECT_IND, NULL); + } + } + + /* SonyEricsson Info request Bug workaround (Continue connection) */ + else if (rej_reason == L2CAP_CMD_REJ_NOT_UNDERSTOOD && p_lcb->w4_info_rsp) { + btu_stop_timer (&p_lcb->info_timer_entry); + + p_lcb->w4_info_rsp = FALSE; + ci.status = HCI_SUCCESS; + memcpy (ci.bd_addr, p_lcb->remote_bd_addr, sizeof(BD_ADDR)); + + /* For all channels, send the event through their FSMs */ + for (p_ccb = p_lcb->ccb_queue.p_first_ccb; p_ccb; p_ccb = p_ccb->p_next_ccb) { + l2c_csm_execute (p_ccb, L2CEVT_L2CAP_INFO_RSP, &ci); + } + } + break; + + case L2CAP_CMD_CONN_REQ: + STREAM_TO_UINT16 (con_info.psm, p); + STREAM_TO_UINT16 (rcid, p); + if ((p_rcb = l2cu_find_rcb_by_psm (con_info.psm)) == NULL) { + L2CAP_TRACE_WARNING ("L2CAP - rcvd conn req for unknown PSM: %d", con_info.psm); + l2cu_reject_connection (p_lcb, rcid, id, L2CAP_CONN_NO_PSM); + break; + } else { + if (!p_rcb->api.pL2CA_ConnectInd_Cb) { + L2CAP_TRACE_WARNING ("L2CAP - rcvd conn req for outgoing-only connection PSM: %d", con_info.psm); + l2cu_reject_connection (p_lcb, rcid, id, L2CAP_CONN_NO_PSM); + break; + } + } + if ((p_ccb = l2cu_allocate_ccb (p_lcb, 0)) == NULL) { + L2CAP_TRACE_ERROR ("L2CAP - unable to allocate CCB"); + l2cu_reject_connection (p_lcb, rcid, id, L2CAP_CONN_NO_RESOURCES); + break; + } + p_ccb->remote_id = id; + p_ccb->p_rcb = p_rcb; + p_ccb->remote_cid = rcid; + + l2c_csm_execute(p_ccb, L2CEVT_L2CAP_CONNECT_REQ, &con_info); +#if BT_CLASSIC_BQB_INCLUDED + // L2CAP/COS/CED/BI-02-C + if (s_l2cap_bqb_bad_cmd_len_rej_flag) { + l2cu_send_peer_cmd_reject (p_lcb, L2CAP_CMD_REJ_NOT_UNDERSTOOD, id, 0, 0); + } +#endif /* BT_CLASSIC_BQB_INCLUDED */ + break; + + case L2CAP_CMD_CONN_RSP: + STREAM_TO_UINT16 (con_info.remote_cid, p); + STREAM_TO_UINT16 (lcid, p); + STREAM_TO_UINT16 (con_info.l2cap_result, p); + STREAM_TO_UINT16 (con_info.l2cap_status, p); + + if ((p_ccb = l2cu_find_ccb_by_cid (p_lcb, lcid)) == NULL) { + L2CAP_TRACE_WARNING ("L2CAP - no CCB for conn rsp, LCID: %d RCID: %d", + lcid, con_info.remote_cid); + break; + } + if (p_ccb->local_id != id) { + L2CAP_TRACE_WARNING ("L2CAP - con rsp - bad ID. Exp: %d Got: %d", + p_ccb->local_id, id); + break; + } + + if (con_info.l2cap_result == L2CAP_CONN_OK) { + l2c_csm_execute(p_ccb, L2CEVT_L2CAP_CONNECT_RSP, &con_info); + } else if (con_info.l2cap_result == L2CAP_CONN_PENDING) { + l2c_csm_execute(p_ccb, L2CEVT_L2CAP_CONNECT_RSP_PND, &con_info); + } else { + l2c_csm_execute(p_ccb, L2CEVT_L2CAP_CONNECT_RSP_NEG, &con_info); + } + + break; + + case L2CAP_CMD_CONFIG_REQ: + p_cfg_end = p + cmd_len; + cfg_rej = FALSE; + cfg_rej_len = 0; + + STREAM_TO_UINT16 (lcid, p); + STREAM_TO_UINT16 (cfg_info.flags, p); + + p_cfg_start = p; + + cfg_info.flush_to_present = cfg_info.mtu_present = cfg_info.qos_present = + cfg_info.fcr_present = cfg_info.fcs_present = FALSE; + + while (p < p_cfg_end) { + STREAM_TO_UINT8 (cfg_code, p); + STREAM_TO_UINT8 (cfg_len, p); + + switch (cfg_code & 0x7F) { + case L2CAP_CFG_TYPE_MTU: + cfg_info.mtu_present = TRUE; + STREAM_TO_UINT16 (cfg_info.mtu, p); + break; + + case L2CAP_CFG_TYPE_FLUSH_TOUT: + cfg_info.flush_to_present = TRUE; + STREAM_TO_UINT16 (cfg_info.flush_to, p); + break; + + case L2CAP_CFG_TYPE_QOS: + cfg_info.qos_present = TRUE; + STREAM_TO_UINT8 (cfg_info.qos.qos_flags, p); + STREAM_TO_UINT8 (cfg_info.qos.service_type, p); + STREAM_TO_UINT32 (cfg_info.qos.token_rate, p); + STREAM_TO_UINT32 (cfg_info.qos.token_bucket_size, p); + STREAM_TO_UINT32 (cfg_info.qos.peak_bandwidth, p); + STREAM_TO_UINT32 (cfg_info.qos.latency, p); + STREAM_TO_UINT32 (cfg_info.qos.delay_variation, p); + break; + + case L2CAP_CFG_TYPE_FCR: + cfg_info.fcr_present = TRUE; + STREAM_TO_UINT8 (cfg_info.fcr.mode, p); + STREAM_TO_UINT8 (cfg_info.fcr.tx_win_sz, p); + STREAM_TO_UINT8 (cfg_info.fcr.max_transmit, p); + STREAM_TO_UINT16 (cfg_info.fcr.rtrans_tout, p); + STREAM_TO_UINT16 (cfg_info.fcr.mon_tout, p); + STREAM_TO_UINT16 (cfg_info.fcr.mps, p); + break; + + case L2CAP_CFG_TYPE_FCS: + cfg_info.fcs_present = TRUE; + STREAM_TO_UINT8 (cfg_info.fcs, p); + break; + + case L2CAP_CFG_TYPE_EXT_FLOW: + cfg_info.ext_flow_spec_present = TRUE; + STREAM_TO_UINT8 (cfg_info.ext_flow_spec.id, p); + STREAM_TO_UINT8 (cfg_info.ext_flow_spec.stype, p); + STREAM_TO_UINT16 (cfg_info.ext_flow_spec.max_sdu_size, p); + STREAM_TO_UINT32 (cfg_info.ext_flow_spec.sdu_inter_time, p); + STREAM_TO_UINT32 (cfg_info.ext_flow_spec.access_latency, p); + STREAM_TO_UINT32 (cfg_info.ext_flow_spec.flush_timeout, p); + break; + + default: + /* sanity check option length */ + if ((cfg_len + L2CAP_CFG_OPTION_OVERHEAD) <= cmd_len) { + p += cfg_len; + if ((cfg_code & 0x80) == 0) { + cfg_rej_len += cfg_len + L2CAP_CFG_OPTION_OVERHEAD; + cfg_rej = TRUE; + } + } + /* bad length; force loop exit */ + else { + p = p_cfg_end; + cfg_rej = TRUE; + } + break; + } + } + + if ((p_ccb = l2cu_find_ccb_by_cid (p_lcb, lcid)) != NULL) { + p_ccb->remote_id = id; + if (cfg_rej) { + l2cu_send_peer_config_rej (p_ccb, p_cfg_start, (UINT16) (cmd_len - L2CAP_CONFIG_REQ_LEN), cfg_rej_len); + } else { + l2c_csm_execute (p_ccb, L2CEVT_L2CAP_CONFIG_REQ, &cfg_info); + } + } else { + /* updated spec says send command reject on invalid cid */ + l2cu_send_peer_cmd_reject (p_lcb, L2CAP_CMD_REJ_INVALID_CID, id, 0, 0); + } + break; + + case L2CAP_CMD_CONFIG_RSP: + p_cfg_end = p + cmd_len; + STREAM_TO_UINT16 (lcid, p); + STREAM_TO_UINT16 (cfg_info.flags, p); + STREAM_TO_UINT16 (cfg_info.result, p); + + cfg_info.flush_to_present = cfg_info.mtu_present = cfg_info.qos_present = + cfg_info.fcr_present = cfg_info.fcs_present = FALSE; + + while (p < p_cfg_end) { + STREAM_TO_UINT8 (cfg_code, p); + STREAM_TO_UINT8 (cfg_len, p); + + switch (cfg_code & 0x7F) { + case L2CAP_CFG_TYPE_MTU: + cfg_info.mtu_present = TRUE; + STREAM_TO_UINT16 (cfg_info.mtu, p); + break; + + case L2CAP_CFG_TYPE_FLUSH_TOUT: + cfg_info.flush_to_present = TRUE; + STREAM_TO_UINT16 (cfg_info.flush_to, p); + break; + + case L2CAP_CFG_TYPE_QOS: + cfg_info.qos_present = TRUE; + STREAM_TO_UINT8 (cfg_info.qos.qos_flags, p); + STREAM_TO_UINT8 (cfg_info.qos.service_type, p); + STREAM_TO_UINT32 (cfg_info.qos.token_rate, p); + STREAM_TO_UINT32 (cfg_info.qos.token_bucket_size, p); + STREAM_TO_UINT32 (cfg_info.qos.peak_bandwidth, p); + STREAM_TO_UINT32 (cfg_info.qos.latency, p); + STREAM_TO_UINT32 (cfg_info.qos.delay_variation, p); + break; + + case L2CAP_CFG_TYPE_FCR: + cfg_info.fcr_present = TRUE; + STREAM_TO_UINT8 (cfg_info.fcr.mode, p); + STREAM_TO_UINT8 (cfg_info.fcr.tx_win_sz, p); + STREAM_TO_UINT8 (cfg_info.fcr.max_transmit, p); + STREAM_TO_UINT16 (cfg_info.fcr.rtrans_tout, p); + STREAM_TO_UINT16 (cfg_info.fcr.mon_tout, p); + STREAM_TO_UINT16 (cfg_info.fcr.mps, p); + break; + + case L2CAP_CFG_TYPE_FCS: + cfg_info.fcs_present = TRUE; + STREAM_TO_UINT8 (cfg_info.fcs, p); + break; + + case L2CAP_CFG_TYPE_EXT_FLOW: + cfg_info.ext_flow_spec_present = TRUE; + STREAM_TO_UINT8 (cfg_info.ext_flow_spec.id, p); + STREAM_TO_UINT8 (cfg_info.ext_flow_spec.stype, p); + STREAM_TO_UINT16 (cfg_info.ext_flow_spec.max_sdu_size, p); + STREAM_TO_UINT32 (cfg_info.ext_flow_spec.sdu_inter_time, p); + STREAM_TO_UINT32 (cfg_info.ext_flow_spec.access_latency, p); + STREAM_TO_UINT32 (cfg_info.ext_flow_spec.flush_timeout, p); + break; + } + } + + if ((p_ccb = l2cu_find_ccb_by_cid (p_lcb, lcid)) != NULL) { + if (p_ccb->local_id != id) { + L2CAP_TRACE_WARNING ("L2CAP - cfg rsp - bad ID. Exp: %d Got: %d", + p_ccb->local_id, id); + break; + } + if ( (cfg_info.result == L2CAP_CFG_OK) || (cfg_info.result == L2CAP_CFG_PENDING) ) { + l2c_csm_execute (p_ccb, L2CEVT_L2CAP_CONFIG_RSP, &cfg_info); + } else { + l2c_csm_execute (p_ccb, L2CEVT_L2CAP_CONFIG_RSP_NEG, &cfg_info); + } + } else { + L2CAP_TRACE_WARNING ("L2CAP - rcvd cfg rsp for unknown CID: 0x%04x", lcid); + } + break; + + + case L2CAP_CMD_DISC_REQ: + STREAM_TO_UINT16 (lcid, p); + STREAM_TO_UINT16 (rcid, p); + + if ((p_ccb = l2cu_find_ccb_by_cid (p_lcb, lcid)) != NULL) { + if (p_ccb->remote_cid == rcid) { + p_ccb->remote_id = id; + l2c_csm_execute (p_ccb, L2CEVT_L2CAP_DISCONNECT_REQ, &con_info); + } + } else { + l2cu_send_peer_disc_rsp (p_lcb, id, lcid, rcid); + } + + break; + + case L2CAP_CMD_DISC_RSP: + STREAM_TO_UINT16 (rcid, p); + STREAM_TO_UINT16 (lcid, p); + + if ((p_ccb = l2cu_find_ccb_by_cid (p_lcb, lcid)) != NULL) { + if ((p_ccb->remote_cid == rcid) && (p_ccb->local_id == id)) { + l2c_csm_execute (p_ccb, L2CEVT_L2CAP_DISCONNECT_RSP, &con_info); + } + } + break; + + case L2CAP_CMD_ECHO_REQ: + l2cu_send_peer_echo_rsp (p_lcb, id, NULL, 0); + break; + + case L2CAP_CMD_ECHO_RSP: + if (p_lcb->p_echo_rsp_cb) { + tL2CA_ECHO_RSP_CB *p_cb = p_lcb->p_echo_rsp_cb; + + /* Zero out the callback in case app immediately calls us again */ + p_lcb->p_echo_rsp_cb = NULL; + + (*p_cb) (L2CAP_PING_RESULT_OK); + } + break; + + case L2CAP_CMD_INFO_REQ: + STREAM_TO_UINT16 (info_type, p); + l2cu_send_peer_info_rsp (p_lcb, id, info_type); + break; + + case L2CAP_CMD_INFO_RSP: + /* Stop the link connect timer if sent before L2CAP connection is up */ + if (p_lcb->w4_info_rsp) { + btu_stop_timer (&p_lcb->info_timer_entry); + p_lcb->w4_info_rsp = FALSE; + } + + STREAM_TO_UINT16 (info_type, p); + STREAM_TO_UINT16 (result, p); + + p_lcb->info_rx_bits |= (1 << info_type); + + if ( (info_type == L2CAP_EXTENDED_FEATURES_INFO_TYPE) + && (result == L2CAP_INFO_RESP_RESULT_SUCCESS) ) { + STREAM_TO_UINT32( p_lcb->peer_ext_fea, p ); + +#if (L2CAP_NUM_FIXED_CHNLS > 0) + if (p_lcb->peer_ext_fea & L2CAP_EXTFEA_FIXED_CHNLS) { + l2cu_send_peer_info_req (p_lcb, L2CAP_FIXED_CHANNELS_INFO_TYPE); + break; + } else { + l2cu_process_fixed_chnl_resp (p_lcb); + } +#endif + } + + +#if (L2CAP_NUM_FIXED_CHNLS > 0) + if (info_type == L2CAP_FIXED_CHANNELS_INFO_TYPE) { + if (result == L2CAP_INFO_RESP_RESULT_SUCCESS) { + memcpy (p_lcb->peer_chnl_mask, p, L2CAP_FIXED_CHNL_ARRAY_SIZE); + } + + l2cu_process_fixed_chnl_resp (p_lcb); + } +#endif +#if (L2CAP_UCD_INCLUDED == TRUE) + else if (info_type == L2CAP_CONNLESS_MTU_INFO_TYPE) { + if (result == L2CAP_INFO_RESP_RESULT_SUCCESS) { + STREAM_TO_UINT16 (p_lcb->ucd_mtu, p); + } + } +#endif + + ci.status = HCI_SUCCESS; + memcpy (ci.bd_addr, p_lcb->remote_bd_addr, sizeof(BD_ADDR)); + for (p_ccb = p_lcb->ccb_queue.p_first_ccb; p_ccb; p_ccb = p_ccb->p_next_ccb) { + l2c_csm_execute (p_ccb, L2CEVT_L2CAP_INFO_RSP, &ci); + } + break; + + default: + L2CAP_TRACE_WARNING ("L2CAP - bad cmd code: %d", cmd_code); + l2cu_send_peer_cmd_reject (p_lcb, L2CAP_CMD_REJ_NOT_UNDERSTOOD, id, 0, 0); + return; + } + } + + UNUSED(rej_mtu); +} +#endif ///CLASSIC_BT_INCLUDED == TRUE + + +/******************************************************************************* +** +** Function l2c_process_held_packets +** +** Description This function processes any L2CAP packets that arrived before +** the HCI connection complete arrived. It is a work around for +** badly behaved controllers. +** +** Returns void +** +*******************************************************************************/ +void l2c_process_held_packets(BOOLEAN timed_out) +{ + if (list_is_empty(l2cb.rcv_pending_q)) { + return; + } + + if (!timed_out) { + btu_stop_timer(&l2cb.rcv_hold_tle); + L2CAP_TRACE_WARNING("L2CAP HOLD CONTINUE"); + } else { + L2CAP_TRACE_WARNING("L2CAP HOLD TIMEOUT"); + } + + for (const list_node_t *node = list_begin(l2cb.rcv_pending_q); + node != list_end(l2cb.rcv_pending_q);) { + BT_HDR *p_buf = list_node(node); + node = list_next(node); + if (!timed_out || (!p_buf->layer_specific) || (--p_buf->layer_specific == 0)) { + list_remove(l2cb.rcv_pending_q, p_buf); + p_buf->layer_specific = 0xFFFF; + l2c_rcv_acl_data(p_buf); + } + } + + /* If anyone still in the queue, restart the timeout */ + if (!list_is_empty(l2cb.rcv_pending_q)) { + btu_start_timer (&l2cb.rcv_hold_tle, BTU_TTYPE_L2CAP_HOLD, BT_1SEC_TIMEOUT); + } +} + + +/******************************************************************************* +** +** Function l2c_init +** +** Description This function is called once at startup to initialize +** all the L2CAP structures +** +** Returns void +** +*******************************************************************************/ +void l2c_init (void) +{ +#if L2C_DYNAMIC_MEMORY + l2c_cb_ptr = (tL2C_CB *)osi_malloc(sizeof(tL2C_CB)); +#endif /* #if L2C_DYNAMIC_MEMORY */ + memset (&l2cb, 0, sizeof (tL2C_CB)); + /* the psm is increased by 2 before being used */ + l2cb.dyn_psm = 0xFFF; + + l2cb.p_ccb_pool = list_new(osi_free_func); + if (l2cb.p_ccb_pool == NULL) { + L2CAP_TRACE_ERROR("%s unable to allocate memory for L2CAP channel control block", __func__); + } + l2cb.p_lcb_pool = list_new(osi_free_func); + if (l2cb.p_lcb_pool == NULL) { + L2CAP_TRACE_ERROR("%s unable to allocate memory for L2CAP Link control block", __func__); + } + +#if (L2CAP_NON_FLUSHABLE_PB_INCLUDED == TRUE) + /* it will be set to L2CAP_PKT_START_NON_FLUSHABLE if controller supports */ + l2cb.non_flushable_pbf = L2CAP_PKT_START << L2CAP_PKT_TYPE_SHIFT; +#endif + + + +#ifdef L2CAP_DESIRED_LINK_ROLE + l2cb.desire_role = L2CAP_DESIRED_LINK_ROLE; +#else + l2cb.desire_role = HCI_ROLE_SLAVE; +#endif + + /* Set the default idle timeout */ + l2cb.idle_timeout = L2CAP_LINK_INACTIVITY_TOUT; + +#if defined(L2CAP_INITIAL_TRACE_LEVEL) + l2cb.l2cap_trace_level = L2CAP_INITIAL_TRACE_LEVEL; +#else + l2cb.l2cap_trace_level = BT_TRACE_LEVEL_NONE; /* No traces */ +#endif + +#if L2CAP_CONFORMANCE_TESTING == TRUE + /* Conformance testing needs a dynamic response */ + l2cb.test_info_resp = L2CAP_EXTFEA_SUPPORTED_MASK; +#endif + + /* Number of ACL buffers to use for high priority channel */ +#if (defined(L2CAP_HIGH_PRI_CHAN_QUOTA_IS_CONFIGURABLE) && (L2CAP_HIGH_PRI_CHAN_QUOTA_IS_CONFIGURABLE == TRUE)) + l2cb.high_pri_min_xmit_quota = L2CAP_HIGH_PRI_MIN_XMIT_QUOTA; +#endif + +#if BLE_INCLUDED == TRUE + l2cb.l2c_ble_fixed_chnls_mask = + L2CAP_FIXED_CHNL_ATT_BIT | L2CAP_FIXED_CHNL_BLE_SIG_BIT | L2CAP_FIXED_CHNL_SMP_BIT; +#endif + + l2cb.rcv_pending_q = list_new(NULL); + if (l2cb.rcv_pending_q == NULL) { + L2CAP_TRACE_ERROR("%s unable to allocate memory for link layer control block", __func__); + } +#if BLE_INCLUDED == TRUE + l2ble_update_att_acl_pkt_num(L2CA_BUFF_INI, NULL); +#endif +} +void l2c_free_p_lcb_pool(void) +{ + list_node_t *p_node = NULL; + tL2C_LCB *p_lcb = NULL; + for (p_node = list_begin(l2cb.p_lcb_pool); p_node; p_node = list_next(p_node)) { + p_lcb = list_node(p_node); + if (p_lcb) { + l2cu_release_lcb (p_lcb); + } + } + + list_free(l2cb.p_lcb_pool); +} + +void l2c_free_p_ccb_pool(void) +{ + list_node_t *p_node = NULL; + tL2C_CCB *p_ccb = NULL; + for (p_node = list_begin(l2cb.p_ccb_pool); p_node; p_node = list_next(p_node)) { + p_ccb = list_node(p_node); + if (p_ccb) { + l2cu_release_ccb (p_ccb); + } + } + + list_free(l2cb.p_ccb_pool); +} + +void l2c_free(void) +{ + list_free(l2cb.rcv_pending_q); + l2cb.rcv_pending_q = NULL; + l2c_free_p_lcb_pool(); + l2c_free_p_ccb_pool(); +#if L2C_DYNAMIC_MEMORY + FREE_AND_RESET(l2c_cb_ptr); +#endif +#if BLE_INCLUDED == TRUE + l2ble_update_att_acl_pkt_num(L2CA_BUFF_DEINIT, NULL); +#endif +} + +/******************************************************************************* +** +** Function l2c_process_timeout +** +** Description This function is called when an L2CAP-related timeout occurs +** +** Returns void +** +*******************************************************************************/ +void l2c_process_timeout (TIMER_LIST_ENT *p_tle) +{ + /* What type of timeout ? */ + switch (p_tle->event) { + case BTU_TTYPE_L2CAP_LINK: + l2c_link_timeout ((tL2C_LCB *)p_tle->param); + break; +#if (CLASSIC_BT_INCLUDED == TRUE) + case BTU_TTYPE_L2CAP_CHNL: + l2c_csm_execute (((tL2C_CCB *)p_tle->param), L2CEVT_TIMEOUT, NULL); + break; + + case BTU_TTYPE_L2CAP_FCR_ACK: + l2c_csm_execute (((tL2C_CCB *)p_tle->param), L2CEVT_ACK_TIMEOUT, NULL); + break; +#endif ///CLASSIC_BT_INCLUDED == TRUE + case BTU_TTYPE_L2CAP_HOLD: + /* Update the timeouts in the hold queue */ + l2c_process_held_packets(TRUE); + break; + + case BTU_TTYPE_L2CAP_INFO: + l2c_info_timeout((tL2C_LCB *)p_tle->param); + break; + case BTU_TTYPE_L2CAP_UPDA_CONN_PARAMS: { +#if (BLE_INCLUDED == TRUE) + UINT8 status = HCI_ERR_HOST_TIMEOUT; + tL2C_LCB *p_lcb = (tL2C_LCB *)p_tle->param; + if (p_lcb){ + p_lcb->conn_update_mask &= ~L2C_BLE_UPDATE_PENDING; + p_lcb->conn_update_mask &= ~L2C_BLE_UPDATE_PARAM_FULL; + l2c_send_update_conn_params_cb(p_lcb, status); + } +#endif ///BLE_INCLUDED == TRUE + break; + } + } +} + +/******************************************************************************* +** +** Function l2c_data_write +** +** Description API functions call this function to write data. +** +** Returns L2CAP_DW_SUCCESS, if data accepted, else FALSE +** L2CAP_DW_CONGESTED, if data accepted and the channel is congested +** L2CAP_DW_FAILED, if error +** +*******************************************************************************/ +#if (CLASSIC_BT_INCLUDED == TRUE) +UINT8 l2c_data_write (UINT16 cid, BT_HDR *p_data, UINT16 flags) +{ + tL2C_CCB *p_ccb; + + /* Find the channel control block. We don't know the link it is on. */ + if ((p_ccb = l2cu_find_ccb_by_cid (NULL, cid)) == NULL) { + L2CAP_TRACE_WARNING ("L2CAP - no CCB for L2CA_DataWrite, CID: %d", cid); + osi_free (p_data); + return (L2CAP_DW_FAILED); + } + +#ifndef TESTER /* Tester may send any amount of data. otherwise sending message + bigger than mtu size of peer is a violation of protocol */ + if (p_data->len > p_ccb->peer_cfg.mtu) { + L2CAP_TRACE_WARNING ("L2CAP - CID: 0x%04x cannot send message bigger than peer's mtu size", cid); + osi_free (p_data); + return (L2CAP_DW_FAILED); + } +#endif + + /* channel based, packet based flushable or non-flushable */ + p_data->layer_specific = flags; + + /* If already congested, do not accept any more packets */ + if (p_ccb->cong_sent) { + L2CAP_TRACE_DEBUG ("L2CAP - CID: 0x%04x cannot send, already congested xmit_hold_q.count: %u buff_quota: %u", + p_ccb->local_cid, + fixed_queue_length(p_ccb->xmit_hold_q), + p_ccb->buff_quota); + + osi_free (p_data); + return (L2CAP_DW_FAILED); + } + + //counter_add("l2cap.dyn.tx.bytes", p_data->len); + //counter_add("l2cap.dyn.tx.pkts", 1); + + l2c_csm_execute (p_ccb, L2CEVT_L2CA_DATA_WRITE, p_data); + + if (p_ccb->cong_sent) { + return (L2CAP_DW_CONGESTED); + } + return (L2CAP_DW_SUCCESS); +} +#endif ///CLASSIC_BT_INCLUDED == TRUE diff --git a/lib/bt/host/bluedroid/stack/l2cap/l2c_ucd.c b/lib/bt/host/bluedroid/stack/l2cap/l2c_ucd.c new file mode 100644 index 00000000..d2de138b --- /dev/null +++ b/lib/bt/host/bluedroid/stack/l2cap/l2c_ucd.c @@ -0,0 +1,1079 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains the L2CAP UCD code + * + ******************************************************************************/ + +#include +#include +//#include + +#include "stack/bt_types.h" +#include "stack/hcidefs.h" +#include "stack/hcimsgs.h" +#include "stack/l2cdefs.h" +#include "l2c_int.h" +#include "stack/btu.h" +#include "stack/btm_api.h" +#include "btm_int.h" + +#if (L2CAP_UCD_INCLUDED == TRUE) +static BOOLEAN l2c_ucd_connect ( BD_ADDR rem_bda ); + +/******************************************************************************* +** +** Function l2c_ucd_discover_cback +** +** Description UCD Discover callback +** +** Returns void +** +*******************************************************************************/ +static void l2c_ucd_discover_cback (BD_ADDR rem_bda, UINT8 info_type, UINT32 data) +{ + tL2C_RCB *p_rcb = &l2cb.rcb_pool[0]; + UINT16 xx; + + L2CAP_TRACE_DEBUG ("L2CAP - l2c_ucd_discover_cback"); + + for (xx = 0; xx < MAX_L2CAP_CLIENTS; xx++, p_rcb++) { + if (p_rcb->in_use) { + /* if this application is waiting UCD reception info */ + if (( info_type == L2CAP_UCD_INFO_TYPE_RECEPTION ) + && ( p_rcb->ucd.state & L2C_UCD_STATE_W4_RECEPTION )) { + p_rcb->ucd.cb_info.pL2CA_UCD_Discover_Cb (rem_bda, info_type, data); + p_rcb->ucd.state &= ~(L2C_UCD_STATE_W4_RECEPTION); + } + + /* if this application is waiting UCD MTU info */ + if (( info_type == L2CAP_UCD_INFO_TYPE_MTU ) + && ( p_rcb->ucd.state & L2C_UCD_STATE_W4_MTU )) { + p_rcb->ucd.cb_info.pL2CA_UCD_Discover_Cb (rem_bda, info_type, data); + p_rcb->ucd.state &= ~(L2C_UCD_STATE_W4_MTU); + } + } + } +} + +/******************************************************************************* +** +** Function l2c_ucd_data_ind_cback +** +** Description UCD Data callback +** +** Returns void +** +*******************************************************************************/ +static void l2c_ucd_data_ind_cback (BD_ADDR rem_bda, BT_HDR *p_buf) +{ + UINT8 *p; + UINT16 psm; + tL2C_RCB *p_rcb; + + L2CAP_TRACE_DEBUG ("L2CAP - l2c_ucd_data_ind_cback"); + + p = (UINT8 *)(p_buf + 1) + p_buf->offset; + STREAM_TO_UINT16(psm, p) + + p_buf->offset += L2CAP_UCD_OVERHEAD; + p_buf->len -= L2CAP_UCD_OVERHEAD; + + if ((p_rcb = l2cu_find_rcb_by_psm (psm)) == NULL) { + L2CAP_TRACE_ERROR ("L2CAP - no RCB for l2c_ucd_data_ind_cback, PSM: 0x%04x", psm); + osi_free (p_buf); + } else { + p_rcb->ucd.cb_info.pL2CA_UCD_Data_Cb(rem_bda, p_buf); + } +} + +/******************************************************************************* +** +** Function l2c_ucd_congestion_status_cback +** +** Description UCD Congestion Status callback +** +** Returns void +** +*******************************************************************************/ +static void l2c_ucd_congestion_status_cback (BD_ADDR rem_bda, BOOLEAN is_congested) +{ + tL2C_RCB *p_rcb = &l2cb.rcb_pool[0]; + UINT16 xx; + + L2CAP_TRACE_DEBUG ("L2CAP - l2c_ucd_congestion_status_cback"); + + for (xx = 0; xx < MAX_L2CAP_CLIENTS; xx++, p_rcb++) { + if (( p_rcb->in_use ) + && ( p_rcb->ucd.state != L2C_UCD_STATE_UNUSED )) { + if ( p_rcb->ucd.cb_info.pL2CA_UCD_Congestion_Status_Cb ) { + L2CAP_TRACE_DEBUG ("L2CAP - Calling UCDCongestionStatus_Cb (%d), PSM=0x%04x, BDA: %08x%04x,", + is_congested, p_rcb->psm, + (rem_bda[0] << 24) + (rem_bda[1] << 16) + (rem_bda[2] << 8) + rem_bda[3], + (rem_bda[4] << 8) + rem_bda[5]); + + p_rcb->ucd.cb_info.pL2CA_UCD_Congestion_Status_Cb ( rem_bda, is_congested ); + } + } + } +} + +/******************************************************************************* +** +** Function l2c_ucd_disconnect_ind_cback +** +** Description UCD disconnect callback (This prevent to access null pointer) +** +** Returns void +** +*******************************************************************************/ +static void l2c_ucd_disconnect_ind_cback (UINT16 cid, BOOLEAN result) +{ + /* do nothing */ +} + +/******************************************************************************* +** +** Function l2c_ucd_config_ind_cback +** +** Description UCD config callback (This prevent to access null pointer) +** +** Returns void +** +*******************************************************************************/ +static void l2c_ucd_config_ind_cback (UINT16 cid, tL2CAP_CFG_INFO *p_cfg) +{ + /* do nothing */ +} + +/******************************************************************************* +** +** Function l2c_ucd_config_cfm_cback +** +** Description UCD config callback (This prevent to access null pointer) +** +** Returns void +** +*******************************************************************************/ +static void l2c_ucd_config_cfm_cback (UINT16 cid, tL2CAP_CFG_INFO *p_cfg) +{ + /* do nothing */ +} + +/******************************************************************************* +** +** Function L2CA_UcdRegister +** +** Description Register PSM on UCD. +** +** Parameters: tL2CAP_UCD_CB_INFO +** +** Return value: TRUE if successs +** +*******************************************************************************/ +BOOLEAN L2CA_UcdRegister ( UINT16 psm, tL2CAP_UCD_CB_INFO *p_cb_info ) +{ + tL2C_RCB *p_rcb; + + L2CAP_TRACE_API ("L2CA_UcdRegister() PSM: 0x%04x", psm); + + if ((!p_cb_info->pL2CA_UCD_Discover_Cb) + || (!p_cb_info->pL2CA_UCD_Data_Cb)) { + L2CAP_TRACE_ERROR ("L2CAP - no callback registering PSM(0x%04x) on UCD", psm); + return (FALSE); + } + + if ((p_rcb = l2cu_find_rcb_by_psm (psm)) == NULL) { + L2CAP_TRACE_ERROR ("L2CAP - no RCB for L2CA_UcdRegister, PSM: 0x%04x", psm); + return (FALSE); + } + + p_rcb->ucd.state = L2C_UCD_STATE_W4_DATA; + p_rcb->ucd.cb_info = *p_cb_info; + + /* check if master rcb is created for UCD */ + if ((p_rcb = l2cu_find_rcb_by_psm (L2C_UCD_RCB_ID)) == NULL) { + if ((p_rcb = l2cu_allocate_rcb (L2C_UCD_RCB_ID)) == NULL) { + L2CAP_TRACE_ERROR ("L2CAP - no RCB available for L2CA_UcdRegister"); + return (FALSE); + } else { + /* these callback functions will forward data to each UCD application */ + p_rcb->ucd.cb_info.pL2CA_UCD_Discover_Cb = l2c_ucd_discover_cback; + p_rcb->ucd.cb_info.pL2CA_UCD_Data_Cb = l2c_ucd_data_ind_cback; + p_rcb->ucd.cb_info.pL2CA_UCD_Congestion_Status_Cb = l2c_ucd_congestion_status_cback; + + memset (&p_rcb->api, 0, sizeof(tL2CAP_APPL_INFO)); + p_rcb->api.pL2CA_DisconnectInd_Cb = l2c_ucd_disconnect_ind_cback; + + /* This will make L2CAP check UCD congestion callback */ + p_rcb->api.pL2CA_CongestionStatus_Cb = NULL; + + /* do nothing but prevent crash */ + p_rcb->api.pL2CA_ConfigInd_Cb = l2c_ucd_config_ind_cback; + p_rcb->api.pL2CA_ConfigCfm_Cb = l2c_ucd_config_cfm_cback; + } + } + + return (TRUE); +} + +/******************************************************************************* +** +** Function L2CA_UcdDeregister +** +** Description Deregister PSM on UCD. +** +** Parameters: PSM +** +** Return value: TRUE if successs +** +*******************************************************************************/ +BOOLEAN L2CA_UcdDeregister_In_CCB_List (void *p_ccb_node, void * context) +{ + p_ccb = (tL2C_CCB *)p_ccb_node; + if (( p_ccb->in_use ) + && ( p_ccb->local_cid == L2CAP_CONNECTIONLESS_CID )) { + l2cu_release_ccb (p_ccb); + } + return false; +} + +BOOLEAN L2CA_UcdDeregister ( UINT16 psm ) +{ + tL2C_CCB *p_ccb; + tL2C_RCB *p_rcb; + UINT16 xx; + + L2CAP_TRACE_API ("L2CA_UcdDeregister() PSM: 0x%04x", psm); + + if ((p_rcb = l2cu_find_rcb_by_psm (psm)) == NULL) { + L2CAP_TRACE_ERROR ("L2CAP - no RCB for L2CA_UcdDeregister, PSM: 0x%04x", psm); + return (FALSE); + } + + p_rcb->ucd.state = L2C_UCD_STATE_UNUSED; + + /* check this was the last UCD registration */ + p_rcb = &l2cb.rcb_pool[0]; + + for (xx = 0; xx < MAX_L2CAP_CLIENTS; xx++, p_rcb++) { + if ((p_rcb->in_use) && (p_rcb->ucd.state != L2C_UCD_STATE_UNUSED)) { + return (TRUE); + } + } + + /* delete master rcb for UCD */ + if ((p_rcb = l2cu_find_rcb_by_psm (L2C_UCD_RCB_ID)) != NULL) { + l2cu_release_rcb (p_rcb); + } + + /* delete CCB for UCD */ + list_foreach(l2cb.p_ccb_pool, L2CA_UcdDeregister_In_CCB_List, NULL); + return (TRUE); +} + +/******************************************************************************* +** +** Function L2CA_UcdDiscover +** +** Description Discover UCD of remote device. +** +** Parameters: PSM +** BD_ADDR of remote device +** info_type : L2CAP_UCD_INFO_TYPE_RECEPTION +** L2CAP_UCD_INFO_TYPE_MTU +** +** +** Return value: TRUE if successs +** +*******************************************************************************/ +BOOLEAN L2CA_UcdDiscover ( UINT16 psm, BD_ADDR rem_bda, UINT8 info_type ) +{ + tL2C_LCB *p_lcb; + tL2C_CCB *p_ccb; + tL2C_RCB *p_rcb; + + L2CAP_TRACE_API ("L2CA_UcdDiscover() PSM: 0x%04x BDA: %08x%04x, InfoType=0x%02x", psm, + (rem_bda[0] << 24) + (rem_bda[1] << 16) + (rem_bda[2] << 8) + rem_bda[3], + (rem_bda[4] << 8) + rem_bda[5], info_type); + + /* Fail if the PSM is not registered */ + if (((p_rcb = l2cu_find_rcb_by_psm (psm)) == NULL) + || ( p_rcb->ucd.state == L2C_UCD_STATE_UNUSED )) { + L2CAP_TRACE_WARNING ("L2CAP - no RCB for L2CA_UcdDiscover, PSM: 0x%04x", psm); + return (FALSE); + } + + /* First, see if we already have a link to the remote */ + /* then find the channel control block for UCD. */ + if (((p_lcb = l2cu_find_lcb_by_bd_addr (rem_bda, BT_TRANSPORT_BR_EDR)) == NULL) + || ((p_ccb = l2cu_find_ccb_by_cid (p_lcb, L2CAP_CONNECTIONLESS_CID)) == NULL)) { + if ( l2c_ucd_connect (rem_bda) == FALSE ) { + return (FALSE); + } + } + + /* set waiting flags in rcb */ + + if ( info_type & L2CAP_UCD_INFO_TYPE_RECEPTION ) { + p_rcb->ucd.state |= L2C_UCD_STATE_W4_RECEPTION; + } + + if ( info_type & L2CAP_UCD_INFO_TYPE_MTU ) { + p_rcb->ucd.state |= L2C_UCD_STATE_W4_MTU; + } + + /* if link is already established */ + if ((p_lcb) && (p_lcb->link_state == LST_CONNECTED)) { + if (!p_ccb) { + p_ccb = l2cu_find_ccb_by_cid (p_lcb, L2CAP_CONNECTIONLESS_CID); + } + l2c_ucd_check_pending_info_req(p_ccb); + } + return (TRUE); +} + +/******************************************************************************* +** +** Function L2CA_UcdDataWrite +** +** Description Send UCD to remote device +** +** Parameters: PSM +** BD Address of remote +** Pointer to buffer of type BT_HDR +** flags : L2CAP_FLUSHABLE_CH_BASED +** L2CAP_FLUSHABLE_PKT +** L2CAP_NON_FLUSHABLE_PKT +** +** Return value L2CAP_DW_SUCCESS, if data accepted +** L2CAP_DW_FAILED, if error +** +*******************************************************************************/ +UINT16 L2CA_UcdDataWrite (UINT16 psm, BD_ADDR rem_bda, BT_HDR *p_buf, UINT16 flags) +{ + tL2C_LCB *p_lcb; + tL2C_CCB *p_ccb; + tL2C_RCB *p_rcb; + UINT8 *p; + + L2CAP_TRACE_API ("L2CA_UcdDataWrite() PSM: 0x%04x BDA: %08x%04x", psm, + (rem_bda[0] << 24) + (rem_bda[1] << 16) + (rem_bda[2] << 8) + rem_bda[3], + (rem_bda[4] << 8) + rem_bda[5]); + + /* Fail if the PSM is not registered */ + if (((p_rcb = l2cu_find_rcb_by_psm (psm)) == NULL) + || ( p_rcb->ucd.state == L2C_UCD_STATE_UNUSED )) { + L2CAP_TRACE_WARNING ("L2CAP - no RCB for L2CA_UcdDataWrite, PSM: 0x%04x", psm); + osi_free (p_buf); + return (L2CAP_DW_FAILED); + } + + /* First, see if we already have a link to the remote */ + /* then find the channel control block for UCD */ + if (((p_lcb = l2cu_find_lcb_by_bd_addr (rem_bda, BT_TRANSPORT_BR_EDR)) == NULL) + || ((p_ccb = l2cu_find_ccb_by_cid (p_lcb, L2CAP_CONNECTIONLESS_CID)) == NULL)) { + if ( l2c_ucd_connect (rem_bda) == FALSE ) { + osi_free (p_buf); + return (L2CAP_DW_FAILED); + } + + /* If we still don't have lcb and ccb after connect attempt, then can't proceed */ + if (((p_lcb = l2cu_find_lcb_by_bd_addr (rem_bda, BT_TRANSPORT_BR_EDR)) == NULL) + || ((p_ccb = l2cu_find_ccb_by_cid (p_lcb, L2CAP_CONNECTIONLESS_CID)) == NULL)) { + osi_free (p_buf); + return (L2CAP_DW_FAILED); + } + } + + /* write PSM */ + p_buf->offset -= L2CAP_UCD_OVERHEAD; + p_buf->len += L2CAP_UCD_OVERHEAD; + p = (UINT8 *)(p_buf + 1) + p_buf->offset; + + UINT16_TO_STREAM (p, psm); + + /* UCD MTU check */ + if ((p_lcb->ucd_mtu) && (p_buf->len > p_lcb->ucd_mtu)) { + L2CAP_TRACE_WARNING ("L2CAP - Handle: 0x%04x UCD bigger than peer's UCD mtu size cannot be sent", p_lcb->handle); + osi_free (p_buf); + return (L2CAP_DW_FAILED); + } + + /* If already congested, do not accept any more packets */ + if (p_ccb->cong_sent) { + L2CAP_TRACE_ERROR ("L2CAP - Handle: 0x%04x UCD cannot be sent, already congested count: %u buff_quota: %u", + p_lcb->handle, + (fixed_queue_length(p_ccb->xmit_hold_q) + + fixed_queue_length(p_lcb->ucd_out_sec_pending_q)), + p_ccb->buff_quota); + + osi_free (p_buf); + return (L2CAP_DW_FAILED); + } + + /* channel based, packet based flushable or non-flushable */ + p_buf->layer_specific = flags; + + l2c_csm_execute (p_ccb, L2CEVT_L2CA_DATA_WRITE, p_buf); + + if (p_ccb->cong_sent) { + return (L2CAP_DW_CONGESTED); + } else { + return (L2CAP_DW_SUCCESS); + } +} + +/******************************************************************************* +** +** Function L2CA_UcdSetIdleTimeout +** +** Description Set UCD Idle timeout. +** +** Parameters: BD Addr +** Timeout in second +** +** Return value: TRUE if successs +** +*******************************************************************************/ +BOOLEAN L2CA_UcdSetIdleTimeout ( BD_ADDR rem_bda, UINT16 timeout ) +{ + tL2C_LCB *p_lcb; + tL2C_CCB *p_ccb; + + L2CAP_TRACE_API ("L2CA_UcdSetIdleTimeout() Timeout: 0x%04x BDA: %08x%04x", timeout, + (rem_bda[0] << 24) + (rem_bda[1] << 16) + (rem_bda[2] << 8) + rem_bda[3], + (rem_bda[4] << 8) + rem_bda[5]); + + /* First, see if we already have a link to the remote */ + /* then find the channel control block. */ + if (((p_lcb = l2cu_find_lcb_by_bd_addr (rem_bda, BT_TRANSPORT_BR_EDR)) == NULL) + || ((p_ccb = l2cu_find_ccb_by_cid (p_lcb, L2CAP_CONNECTIONLESS_CID)) == NULL)) { + L2CAP_TRACE_WARNING ("L2CAP - no UCD channel"); + return (FALSE); + } else { + p_ccb->fixed_chnl_idle_tout = timeout; + return (TRUE); + } +} + +/******************************************************************************* +** +** Function L2CA_UCDSetTxPriority +** +** Description Sets the transmission priority for a connectionless channel. +** +** Returns TRUE if a valid channel, else FALSE +** +*******************************************************************************/ +BOOLEAN L2CA_UCDSetTxPriority ( BD_ADDR rem_bda, tL2CAP_CHNL_PRIORITY priority ) +{ + tL2C_LCB *p_lcb; + tL2C_CCB *p_ccb; + + L2CAP_TRACE_API ("L2CA_UCDSetTxPriority() priority: 0x%02x BDA: %08x%04x", priority, + (rem_bda[0] << 24) + (rem_bda[1] << 16) + (rem_bda[2] << 8) + rem_bda[3], + (rem_bda[4] << 8) + rem_bda[5]); + + if ((p_lcb = l2cu_find_lcb_by_bd_addr (rem_bda, BT_TRANSPORT_BR_EDR)) == NULL) { + L2CAP_TRACE_WARNING ("L2CAP - no LCB for L2CA_UCDSetTxPriority"); + return (FALSE); + } + + /* Find the channel control block */ + if ((p_ccb = l2cu_find_ccb_by_cid (p_lcb, L2CAP_CONNECTIONLESS_CID)) == NULL) { + L2CAP_TRACE_WARNING ("L2CAP - no CCB for L2CA_UCDSetTxPriority"); + return (FALSE); + } + + /* it will update the order of CCB in LCB by priority and update round robin service variables */ + l2cu_change_pri_ccb (p_ccb, priority); + + return (TRUE); +} + +/******************************************************************************* +** +** Function l2c_ucd_connect +** +** Description Connect UCD to remote device. +** +** Parameters: BD_ADDR of remote device +** +** Return value: TRUE if successs +** +*******************************************************************************/ +static BOOLEAN l2c_ucd_connect ( BD_ADDR rem_bda ) +{ + tL2C_LCB *p_lcb; + tL2C_CCB *p_ccb; + tL2C_RCB *p_rcb; + + L2CAP_TRACE_DEBUG ("l2c_ucd_connect() BDA: %08x%04x", + (rem_bda[0] << 24) + (rem_bda[1] << 16) + (rem_bda[2] << 8) + rem_bda[3], + (rem_bda[4] << 8) + rem_bda[5]); + + /* Fail if we have not established communications with the controller */ + if (!BTM_IsDeviceUp()) { + L2CAP_TRACE_WARNING ("l2c_ucd_connect - BTU not ready"); + return (FALSE); + } + + /* First, see if we already have a link to the remote */ + if ((p_lcb = l2cu_find_lcb_by_bd_addr (rem_bda, BT_TRANSPORT_BR_EDR)) == NULL) { + /* No link. Get an LCB and start link establishment */ + if ( ((p_lcb = l2cu_allocate_lcb (rem_bda, FALSE, BT_TRANSPORT_BR_EDR)) == NULL) + || (l2cu_create_conn(p_lcb, BT_TRANSPORT_BR_EDR) == FALSE) ) { + L2CAP_TRACE_WARNING ("L2CAP - conn not started l2c_ucd_connect"); + return (FALSE); + } + } else if ( p_lcb->info_rx_bits & (1 << L2CAP_EXTENDED_FEATURES_INFO_TYPE) ) { + if (!(p_lcb->peer_ext_fea & L2CAP_EXTFEA_UCD_RECEPTION)) { + L2CAP_TRACE_WARNING ("L2CAP - UCD is not supported by peer, l2c_ucd_connect"); + return (FALSE); + } + } + + /* Find the channel control block. */ + if ((p_ccb = l2cu_find_ccb_by_cid (p_lcb, L2CAP_CONNECTIONLESS_CID)) == NULL) { + /* Allocate a channel control block */ + if ((p_ccb = l2cu_allocate_ccb (p_lcb, 0)) == NULL) { + L2CAP_TRACE_WARNING ("L2CAP - no CCB for l2c_ucd_connect"); + return (FALSE); + } else { + /* Set CID for the connection */ + p_ccb->local_cid = L2CAP_CONNECTIONLESS_CID; + p_ccb->remote_cid = L2CAP_CONNECTIONLESS_CID; + + /* Set the default idle timeout value to use */ + p_ccb->fixed_chnl_idle_tout = L2CAP_UCD_IDLE_TIMEOUT; + + /* Set the default channel priority value to use */ + l2cu_change_pri_ccb (p_ccb, L2CAP_UCD_CH_PRIORITY); + + if ((p_rcb = l2cu_find_rcb_by_psm (L2C_UCD_RCB_ID)) == NULL) { + L2CAP_TRACE_WARNING ("L2CAP - no UCD registered, l2c_ucd_connect"); + return (FALSE); + } + /* Save UCD registration info */ + p_ccb->p_rcb = p_rcb; + + /* There is no configuration, so if the link is up, the channel is up */ + if (p_lcb->link_state == LST_CONNECTED) { + p_ccb->chnl_state = CST_OPEN; + } + } + } + + return (TRUE); +} + +/******************************************************************************* +** +** Function l2c_ucd_delete_sec_pending_q +** +** Description discard all of UCD packets in security pending queue +** +** Returns None +** +*******************************************************************************/ +void l2c_ucd_delete_sec_pending_q(tL2C_LCB *p_lcb) +{ + /* clean up any security pending UCD */ + while (p_lcb->ucd_out_sec_pending_q.p_first) { + osi_free(fixed_queue_dequeue(p_lcb->ucd_out_sec_pending_q, 0)); + } + fixed_queue_free(p_lcb->ucd_out_sec_pending_q, NULL); + p_lcb->ucd_out_sec_pending_q = NULL; + + while (! fixed_queue_is_empty(p_lcb->ucd_in_sec_pending_q)) { + osi_free(fixed_queue_dequeue(p_lcb->ucd_in_sec_pending_q, 0)); + } + fixed_queue_free(p_lcb->ucd_in_sec_pending_q); + p_lcb->ucd_in_sec_pending_q = NULL; +} + +/******************************************************************************* +** +** Function l2c_ucd_check_pending_info_req +** +** Description check if any application is waiting for UCD information +** +** Return TRUE if any pending UCD info request +** +*******************************************************************************/ +BOOLEAN l2c_ucd_check_pending_info_req(tL2C_CCB *p_ccb) +{ + tL2C_RCB *p_rcb = &l2cb.rcb_pool[0]; + UINT16 xx; + BOOLEAN pending = FALSE; + + if (p_ccb == NULL) { + L2CAP_TRACE_ERROR ("L2CAP - NULL p_ccb in l2c_ucd_check_pending_info_req"); + return (FALSE); + } + + for (xx = 0; xx < MAX_L2CAP_CLIENTS; xx++, p_rcb++) { + if (p_rcb->in_use) { + /* if application is waiting UCD reception info */ + if (p_rcb->ucd.state & L2C_UCD_STATE_W4_RECEPTION) { + /* if this information is available */ + if ( p_ccb->p_lcb->info_rx_bits & (1 << L2CAP_EXTENDED_FEATURES_INFO_TYPE) ) { + if (!(p_ccb->p_lcb->peer_ext_fea & L2CAP_EXTFEA_UCD_RECEPTION)) { + L2CAP_TRACE_WARNING ("L2CAP - UCD is not supported by peer, l2c_ucd_check_pending_info_req"); + + l2c_ucd_delete_sec_pending_q(p_ccb->p_lcb); + l2cu_release_ccb (p_ccb); + } + + p_ccb->p_rcb->ucd.cb_info.pL2CA_UCD_Discover_Cb (p_ccb->p_lcb->remote_bd_addr, + L2CAP_UCD_INFO_TYPE_RECEPTION, + p_ccb->p_lcb->peer_ext_fea & L2CAP_EXTFEA_UCD_RECEPTION); + } else { + pending = TRUE; + if (p_ccb->p_lcb->w4_info_rsp == FALSE) { + l2cu_send_peer_info_req (p_ccb->p_lcb, L2CAP_EXTENDED_FEATURES_INFO_TYPE); + } + } + } + + /* if application is waiting for UCD MTU */ + if (p_rcb->ucd.state & L2C_UCD_STATE_W4_MTU) { + /* if this information is available */ + if ( p_ccb->p_lcb->info_rx_bits & (1 << L2CAP_CONNLESS_MTU_INFO_TYPE)) { + p_ccb->p_rcb->ucd.cb_info.pL2CA_UCD_Discover_Cb (p_ccb->p_lcb->remote_bd_addr, + L2CAP_UCD_INFO_TYPE_MTU, + p_ccb->p_lcb->ucd_mtu); + } else { + pending = TRUE; + if (p_ccb->p_lcb->w4_info_rsp == FALSE) { + l2cu_send_peer_info_req (p_ccb->p_lcb, L2CAP_CONNLESS_MTU_INFO_TYPE); + } + } + } + } + } + return (pending); +} + +/******************************************************************************* +** +** Function l2c_ucd_enqueue_pending_out_sec_q +** +** Description enqueue outgoing UCD packet into security pending queue +** and check congestion +** +** Return None +** +*******************************************************************************/ +void l2c_ucd_enqueue_pending_out_sec_q(tL2C_CCB *p_ccb, void *p_data) +{ + fixed_queue_enqueue(p_ccb->p_lcb->ucd_out_sec_pending_q, p_data, FIXED_QUEUE_MAX_TIMEOUT); + l2cu_check_channel_congestion (p_ccb); +} + +/******************************************************************************* +** +** Function l2c_ucd_check_pending_out_sec_q +** +** Description check outgoing security +** +** Return TRUE if any UCD packet for security +** +*******************************************************************************/ +BOOLEAN l2c_ucd_check_pending_out_sec_q(tL2C_CCB *p_ccb) +{ + BT_HDR *p_buf = (BT_HDR*)fixed_queue_try_peek_first(p_ccb->p_lcb->ucd_out_sec_pending_q); + + if (p_buf != NULL) { + UINT16 psm; + UINT8 *p = (UINT8 *)(p_buf + 1) + p_buf->offset; + + STREAM_TO_UINT16(psm, p) + + p_ccb->chnl_state = CST_ORIG_W4_SEC_COMP; + btm_sec_l2cap_access_req (p_ccb->p_lcb->remote_bd_addr, psm, + p_ccb->p_lcb->handle, CONNLESS_ORIG, &l2c_link_sec_comp, p_ccb); + + return (TRUE); + } + return (FALSE); +} + +/******************************************************************************* +** +** Function l2c_ucd_send_pending_out_sec_q +** +** Description dequeue UCD packet from security pending queue and +** enqueue it into CCB +** +** Return None +** +*******************************************************************************/ +void l2c_ucd_send_pending_out_sec_q(tL2C_CCB *p_ccb) +{ + BT_HDR *p_buf = (BT_HDR*)fixed_queue_dequeue(p_ccb->p_lcb->ucd_out_sec_pending_q, 0); + + if (p_buf != NULL) { + l2c_enqueue_peer_data (p_ccb, (BT_HDR *)p_buf); + l2c_link_check_send_pkts (p_ccb->p_lcb, NULL, NULL); + } +} + +/******************************************************************************* +** +** Function l2c_ucd_discard_pending_out_sec_q +** +** Description dequeue UCD packet from security pending queue and +** discard it. +** +** Return None +** +*******************************************************************************/ +void l2c_ucd_discard_pending_out_sec_q(tL2C_CCB *p_ccb) +{ + BT_HDR *p_buf = (BT_HDR*)fixed_queue_dequeue(p_ccb->p_lcb->ucd_out_sec_pending_q, 0); + + /* we may need to report to application */ + + if (p_buf) { + osi_free (p_buf); + } +} + +/******************************************************************************* +** +** Function l2c_ucd_check_pending_in_sec_q +** +** Description check incoming security +** +** Return TRUE if any UCD packet for security +** +*******************************************************************************/ +BOOLEAN l2c_ucd_check_pending_in_sec_q(tL2C_CCB *p_ccb) +{ + BT_HDR *p_buf = (BT_HDR*)fixed_queue_dequeue(p_ccb->p_lcb->ucd_in_sec_pending_q, 0); + + if (p_buf != NULL) { + UINT16 psm; + UINT8 *p = (UINT8 *)(p_buf + 1) + p_buf->offset; + STREAM_TO_UINT16(psm, p) + + p_ccb->chnl_state = CST_TERM_W4_SEC_COMP; + btm_sec_l2cap_access_req (p_ccb->p_lcb->remote_bd_addr, psm, + p_ccb->p_lcb->handle, CONNLESS_TERM, &l2c_link_sec_comp, p_ccb); + + return (TRUE); + } + return (FALSE); +} + +/******************************************************************************* +** +** Function l2c_ucd_send_pending_in_sec_q +** +** Description dequeue UCD packet from security pending queue and +** send it to application +** +** Return None +** +*******************************************************************************/ +void l2c_ucd_send_pending_in_sec_q(tL2C_CCB *p_ccb) +{ + BT_HDR *p_buf = (BT_HDR*)fixed_queue_dequeue(p_ccb->p_lcb->ucd_in_sec_pending_q, 0) + + if (p_buf != NULL) { + p_ccb->p_rcb->ucd.cb_info.pL2CA_UCD_Data_Cb(p_ccb->p_lcb->remote_bd_addr, (BT_HDR *)p_buf); + } +} + +/******************************************************************************* +** +** Function l2c_ucd_discard_pending_in_sec_q +** +** Description dequeue UCD packet from security pending queue and +** discard it. +** +** Return None +** +*******************************************************************************/ +void l2c_ucd_discard_pending_in_sec_q(tL2C_CCB *p_ccb) +{ + BT_HDR *p_buf = (BT_HDR*)fixed_queue_dequeue(p_ccb->p_lcb->ucd_in_sec_pending_q, 0); + + if (p_buf) { + osi_free (p_buf); + } +} + +/******************************************************************************* +** +** Function l2c_ucd_check_rx_pkts +** +** Description Check if UCD reception is registered. +** Process received UCD packet if application is expecting. +** +** Return TRUE if UCD reception is registered +** +*******************************************************************************/ +BOOLEAN l2c_ucd_check_rx_pkts(tL2C_LCB *p_lcb, BT_HDR *p_msg) +{ + tL2C_CCB *p_ccb; + tL2C_RCB *p_rcb; + + if (((p_ccb = l2cu_find_ccb_by_cid (p_lcb, L2CAP_CONNECTIONLESS_CID)) != NULL) + || ((p_rcb = l2cu_find_rcb_by_psm (L2C_UCD_RCB_ID)) != NULL)) { + if (p_ccb == NULL) { + /* Allocate a channel control block */ + if ((p_ccb = l2cu_allocate_ccb (p_lcb, 0)) == NULL) { + L2CAP_TRACE_WARNING ("L2CAP - no CCB for UCD reception"); + osi_free (p_msg); + return TRUE; + } else { + /* Set CID for the connection */ + p_ccb->local_cid = L2CAP_CONNECTIONLESS_CID; + p_ccb->remote_cid = L2CAP_CONNECTIONLESS_CID; + + /* Set the default idle timeout value to use */ + p_ccb->fixed_chnl_idle_tout = L2CAP_UCD_IDLE_TIMEOUT; + + /* Set the default channel priority value to use */ + l2cu_change_pri_ccb (p_ccb, L2CAP_UCD_CH_PRIORITY); + + /* Save registration info */ + p_ccb->p_rcb = p_rcb; + + p_ccb->chnl_state = CST_OPEN; + } + } + l2c_csm_execute(p_ccb, L2CEVT_L2CAP_DATA, p_msg); + return TRUE; + } else { + return FALSE; + } +} + +/******************************************************************************* +** +** Function l2c_ucd_process_event +** +** Description This is called from main state machine when LCID is connectionless +** Process the event if it is for UCD. +** +** Return TRUE if the event is consumed by UCD +** FALSE if the event needs to be processed by main state machine +** +*******************************************************************************/ +BOOLEAN l2c_ucd_process_event(tL2C_CCB *p_ccb, UINT16 event, void *p_data) +{ + /* if the event is not processed by this function, this variable will be set to FALSE */ + BOOLEAN done = TRUE; + + switch (p_ccb->chnl_state) { + case CST_CLOSED: + switch (event) { + case L2CEVT_LP_CONNECT_CFM: /* Link came up */ + /* check if waiting for UCD info */ + if (!l2c_ucd_check_pending_info_req (p_ccb)) { + /* check if any outgoing UCD packet is waiting security check */ + if (!l2c_ucd_check_pending_out_sec_q(p_ccb)) { + p_ccb->chnl_state = CST_OPEN; + } + } + break; + + case L2CEVT_L2CAP_DATA: /* Peer data packet rcvd */ + fixed_queue_enqueue(p_ccb->p_lcb->ucd_in_sec_pending_q, p_data, FIXED_QUEUE_MAX_TIMEOUT); + break; + + case L2CEVT_L2CA_DATA_WRITE: /* Upper layer data to send */ + l2c_ucd_enqueue_pending_out_sec_q(p_ccb, p_data); + break; + + case L2CEVT_L2CAP_INFO_RSP: + /* check if waiting for UCD info */ + if (!l2c_ucd_check_pending_info_req (p_ccb)) { + /* check if any outgoing UCD packet is waiting security check */ + if (!l2c_ucd_check_pending_out_sec_q(p_ccb)) { + p_ccb->chnl_state = CST_OPEN; + } + } + break; + + default: + done = FALSE; /* main state machine continues to process event */ + break; + } + break; + + case CST_ORIG_W4_SEC_COMP: + switch (event) { + case L2CEVT_SEC_RE_SEND_CMD: /* BTM has enough info to proceed */ + /* check if any outgoing UCD packet is waiting security check */ + if (!l2c_ucd_check_pending_out_sec_q(p_ccb)) { + p_ccb->chnl_state = CST_OPEN; + } + break; + + case L2CEVT_SEC_COMP: /* Security completed success */ + p_ccb->chnl_state = CST_OPEN; + l2c_ucd_send_pending_out_sec_q(p_ccb); + + if (! fixed_queue_is_empty(p_ccb->p_lcb->ucd_out_sec_pending_q)) + { + /* start a timer to send next UCD packet in OPEN state */ + /* it will prevent stack overflow */ + btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, 0); + } else { + /* start a timer for idle timeout of UCD */ + btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, p_ccb->fixed_chnl_idle_tout); + } + break; + + case L2CEVT_SEC_COMP_NEG: + p_ccb->chnl_state = CST_OPEN; + l2c_ucd_discard_pending_out_sec_q(p_ccb); + + /* start a timer for idle timeout of UCD */ + btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, p_ccb->fixed_chnl_idle_tout); + break; + + case L2CEVT_L2CA_DATA_WRITE: /* Upper layer data to send */ + l2c_ucd_enqueue_pending_out_sec_q(p_ccb, p_data); + break; + + case L2CEVT_L2CAP_DATA: /* Peer data packet rcvd */ + fixed_queue_enqueue(p_ccb->p_lcb->ucd_in_sec_pending_q, p_data, FIXED_QUEUE_MAX_TIMEOUT); + break; + + case L2CEVT_L2CAP_INFO_RSP: + /* check if waiting for UCD info */ + l2c_ucd_check_pending_info_req (p_ccb); + break; + + default: + done = FALSE; /* main state machine continues to process event */ + break; + } + break; + + + case CST_TERM_W4_SEC_COMP: + switch (event) { + case L2CEVT_SEC_COMP: + p_ccb->chnl_state = CST_OPEN; + l2c_ucd_send_pending_in_sec_q (p_ccb); + + if (! fixed_queue_is_empty(p_ccb->p_lcb->ucd_in_sec_pending_q)) { + /* start a timer to check next UCD packet in OPEN state */ + /* it will prevent stack overflow */ + btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, 0); + } else { + /* start a timer for idle timeout of UCD */ + btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, p_ccb->fixed_chnl_idle_tout); + } + break; + + case L2CEVT_SEC_COMP_NEG: + if (((tL2C_CONN_INFO *)p_data)->status == BTM_DELAY_CHECK) { + done = FALSE; + break; + } + p_ccb->chnl_state = CST_OPEN; + l2c_ucd_discard_pending_in_sec_q (p_ccb); + + /* start a timer for idle timeout of UCD */ + btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, p_ccb->fixed_chnl_idle_tout); + break; + + case L2CEVT_L2CA_DATA_WRITE: /* Upper layer data to send */ + l2c_ucd_enqueue_pending_out_sec_q(p_ccb, p_data); + break; + + case L2CEVT_L2CAP_DATA: /* Peer data packet rcvd */ + fixed_queue_enqueue(p_ccb->p_lcb->ucd_in_sec_pending_q, p_data, FIXED_QUEUE_MAX_TIMEOUT); + break; + + case L2CEVT_SEC_RE_SEND_CMD: /* BTM has enough info to proceed */ + /* check if any incoming UCD packet is waiting security check */ + if (!l2c_ucd_check_pending_in_sec_q(p_ccb)) { + p_ccb->chnl_state = CST_OPEN; + } + break; + + case L2CEVT_L2CAP_INFO_RSP: + /* check if waiting for UCD info */ + l2c_ucd_check_pending_info_req (p_ccb); + break; + + default: + done = FALSE; /* main state machine continues to process event */ + break; + } + break; + + case CST_OPEN: + switch (event) { + case L2CEVT_L2CAP_DATA: /* Peer data packet rcvd */ + /* stop idle timer of UCD */ + btu_stop_timer (&p_ccb->timer_entry); + + fixed_queue_enqueue(p_ccb->p_lcb->ucd_in_sec_pending_q, p_data, FIXED_QUEUE_MAX_TIMEOUT); + l2c_ucd_check_pending_in_sec_q (p_ccb); + break; + + case L2CEVT_L2CA_DATA_WRITE: /* Upper layer data to send */ + /* stop idle timer of UCD */ + btu_stop_timer (&p_ccb->timer_entry); + + l2c_ucd_enqueue_pending_out_sec_q(p_ccb, p_data); + + /* coverity[check_return] */ /* coverity[unchecked_value] */ + /* success changes state, failure stays in current state */ + l2c_ucd_check_pending_out_sec_q (p_ccb); + break; + + case L2CEVT_TIMEOUT: + /* check if any UCD packet is waiting security check */ + if ((!l2c_ucd_check_pending_in_sec_q(p_ccb)) + && (!l2c_ucd_check_pending_out_sec_q(p_ccb))) { + l2cu_release_ccb (p_ccb); + } + break; + + case L2CEVT_L2CAP_INFO_RSP: + /* check if waiting for UCD info */ + l2c_ucd_check_pending_info_req (p_ccb); + break; + + default: + done = FALSE; /* main state machine continues to process event */ + break; + } + break; + + default: + done = FALSE; /* main state machine continues to process event */ + break; + } + + return done; +} +#endif /* (L2CAP_UCD_INCLUDED == TRUE) */ diff --git a/lib/bt/host/bluedroid/stack/l2cap/l2c_utils.c b/lib/bt/host/bluedroid/stack/l2cap/l2c_utils.c new file mode 100644 index 00000000..992d4b68 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/l2cap/l2c_utils.c @@ -0,0 +1,3731 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains L2CAP utility functions + * + ******************************************************************************/ + +#include +#include + +#include "osi/allocator.h" +#include "device/controller.h" +#include "stack/bt_types.h" +#include "stack/hcimsgs.h" +#include "stack/l2cdefs.h" +#include "l2c_int.h" +#include "stack/hcidefs.h" +#include "stack/btu.h" +#include "stack/btm_api.h" +#include "btm_int.h" +#include "stack/hcidefs.h" +#include "osi/allocator.h" +#include "osi/list.h" + +#if BT_SDP_BQB_INCLUDED +extern BOOLEAN l2cap_bqb_ertm_mode_included_flag; +#endif /* BT_SDP_BQB_INCLUDED */ + +/******************************************************************************* +** +** Function l2cu_allocate_lcb +** +** Description Look for an unused LCB +** +** Returns LCB address or NULL if none found +** +*******************************************************************************/ +tL2C_LCB *l2cu_allocate_lcb (BD_ADDR p_bd_addr, BOOLEAN is_bonding, tBT_TRANSPORT transport) +{ + tL2C_LCB *p_lcb = NULL; + bool list_ret = false; + extern tL2C_LCB *l2cu_find_free_lcb (void); + // temp solution + p_lcb = l2cu_find_free_lcb(); + if(p_lcb != NULL) { + list_ret = true; + } + +#if (CLASSIC_BT_INCLUDED == TRUE) + /* Check if peer device's and our BD_ADDR is same or not. It + should be different to avoid 'Impersonation in the Pin Pairing + Protocol' (CVE-2020-26555) vulnerability. */ + if (memcmp((uint8_t *)p_bd_addr, (uint8_t *)&controller_get_interface()->get_address()->address, sizeof (BD_ADDR)) == 0) { + L2CAP_TRACE_ERROR ("%s connection rejected due to same BD ADDR", __func__); + return (NULL); + } +#endif + + if(p_lcb == NULL && list_length(l2cb.p_lcb_pool) < MAX_L2CAP_LINKS) { + p_lcb = (tL2C_LCB *)osi_malloc(sizeof(tL2C_LCB)); + if (p_lcb) { + memset (p_lcb, 0, sizeof(tL2C_LCB)); + list_ret = list_append(l2cb.p_lcb_pool, p_lcb); + }else { + L2CAP_TRACE_ERROR("Error in allocating L2CAP Link Control Block"); + } + } + if (list_ret) { + if (p_lcb) { + btu_free_timer(&p_lcb->timer_entry); + btu_free_timer(&p_lcb->info_timer_entry); + btu_free_timer(&p_lcb->upda_con_timer); + + memset (p_lcb, 0, sizeof (tL2C_LCB)); + memcpy (p_lcb->remote_bd_addr, p_bd_addr, BD_ADDR_LEN); + + p_lcb->in_use = TRUE; + p_lcb->link_state = LST_DISCONNECTED; + p_lcb->handle = HCI_INVALID_HANDLE; + p_lcb->link_flush_tout = 0xFFFF; + p_lcb->timer_entry.param = (TIMER_PARAM_TYPE)p_lcb; + p_lcb->info_timer_entry.param = (TIMER_PARAM_TYPE)p_lcb; + p_lcb->upda_con_timer.param = (TIMER_PARAM_TYPE)p_lcb; + p_lcb->idle_timeout = l2cb.idle_timeout; + p_lcb->id = 1; /* spec does not allow '0' */ + p_lcb->is_bonding = is_bonding; +#if (BLE_INCLUDED == TRUE) + p_lcb->transport = transport; + p_lcb->tx_data_len = controller_get_interface()->get_ble_default_data_packet_length(); + p_lcb->le_sec_pending_q = fixed_queue_new(QUEUE_SIZE_MAX); + + if (transport == BT_TRANSPORT_LE) { + l2cb.num_ble_links_active++; + l2c_ble_link_adjust_allocation(); + } else +#endif + { + l2cb.num_links_active++; + l2c_link_adjust_allocation(); + } + p_lcb->link_xmit_data_q = list_new(NULL); + return (p_lcb); + } + } + + /* If here, no free LCB found */ + return (NULL); +} + +/******************************************************************************* +** +** Function l2cu_update_lcb_4_bonding +** +** Description Mark the lcb for bonding. Used when bonding takes place on +** an existing ACL connection. (Pre-Lisbon devices) +** +** Returns Nothing +** +*******************************************************************************/ +void l2cu_update_lcb_4_bonding (BD_ADDR p_bd_addr, BOOLEAN is_bonding) +{ + tL2C_LCB *p_lcb = l2cu_find_lcb_by_bd_addr (p_bd_addr, BT_TRANSPORT_BR_EDR); + + if (p_lcb) { + p_lcb->is_bonding = is_bonding; + } +} + +/******************************************************************************* +** +** Function l2cu_release_lcb +** +** Description Release an LCB. All timers will be stopped, channels +** dropped, buffers returned etc. +** +** Returns void +** +*******************************************************************************/ +void l2cu_release_lcb (tL2C_LCB *p_lcb) +{ + tL2C_CCB *p_ccb; + + p_lcb->in_use = FALSE; + p_lcb->is_bonding = FALSE; +#if (BLE_INCLUDED == TRUE) + p_lcb->retry_create_con = 0; + p_lcb->start_time_s = 0; +#endif // #if (BLE_INCLUDED == TRUE) + + /* Stop and release timers */ + btu_free_timer (&p_lcb->timer_entry); + memset(&p_lcb->timer_entry, 0, sizeof(TIMER_LIST_ENT)); + btu_free_timer (&p_lcb->info_timer_entry); + memset(&p_lcb->info_timer_entry, 0, sizeof(TIMER_LIST_ENT)); + btu_free_timer(&p_lcb->upda_con_timer); + memset(&p_lcb->upda_con_timer, 0, sizeof(TIMER_LIST_ENT)); + + /* Release any unfinished L2CAP packet on this link */ + if (p_lcb->p_hcit_rcv_acl) { + osi_free(p_lcb->p_hcit_rcv_acl); + p_lcb->p_hcit_rcv_acl = NULL; + } + +#if BTM_SCO_INCLUDED == TRUE +#if (BLE_INCLUDED == TRUE) + if (p_lcb->transport == BT_TRANSPORT_BR_EDR) +#endif + { + /* Release all SCO links */ + btm_remove_sco_links(p_lcb->remote_bd_addr); + } +#endif + + if (p_lcb->sent_not_acked > 0) { +#if (BLE_INCLUDED == TRUE) + if (p_lcb->transport == BT_TRANSPORT_LE) { + l2cb.controller_le_xmit_window += p_lcb->sent_not_acked; + if (l2cb.controller_le_xmit_window > l2cb.num_lm_ble_bufs) { + l2cb.controller_le_xmit_window = l2cb.num_lm_ble_bufs; + } + } else +#endif + { + l2cb.controller_xmit_window += p_lcb->sent_not_acked; + if (l2cb.controller_xmit_window > l2cb.num_lm_acl_bufs) { + l2cb.controller_xmit_window = l2cb.num_lm_acl_bufs; + } + } + } + +#if (BLE_INCLUDED == TRUE) + // Reset BLE connecting flag only if the address matches + if (!memcmp(l2cb.ble_connecting_bda, p_lcb->remote_bd_addr, BD_ADDR_LEN)) { + l2cb.is_ble_connecting = FALSE; + } +#endif + +#if (L2CAP_NUM_FIXED_CHNLS > 0) + l2cu_process_fixed_disc_cback(p_lcb); +#endif + + /* Ensure no CCBs left on this LCB */ + for (p_ccb = p_lcb->ccb_queue.p_first_ccb; p_ccb; p_ccb = p_lcb->ccb_queue.p_first_ccb) { + l2cu_release_ccb (p_ccb); + } + + /* Tell BTM Acl management the link was removed */ + if ((p_lcb->link_state == LST_CONNECTED) || (p_lcb->link_state == LST_DISCONNECTING)) { +#if (BLE_INCLUDED == TRUE) + btm_acl_removed (p_lcb->remote_bd_addr, p_lcb->transport); +#else + btm_acl_removed (p_lcb->remote_bd_addr, BT_TRANSPORT_BR_EDR); +#endif + } + + /* Release any held buffers */ + if (p_lcb->link_xmit_data_q) { + while (!list_is_empty(p_lcb->link_xmit_data_q)) { + BT_HDR *p_buf = list_front(p_lcb->link_xmit_data_q); + list_remove(p_lcb->link_xmit_data_q, p_buf); + osi_free(p_buf); + } + list_free(p_lcb->link_xmit_data_q); + p_lcb->link_xmit_data_q = NULL; + } + +#if (L2CAP_UCD_INCLUDED == TRUE) + /* clean up any security pending UCD */ + l2c_ucd_delete_sec_pending_q(p_lcb); +#endif + +#if BLE_INCLUDED == TRUE + /* Re-adjust flow control windows make sure it does not go negative */ + if (p_lcb->transport == BT_TRANSPORT_LE) { + if (l2cb.num_ble_links_active >= 1) { + l2cb.num_ble_links_active--; + } + + l2c_ble_link_adjust_allocation(); + } else +#endif + { + if (l2cb.num_links_active >= 1) { + l2cb.num_links_active--; + } + + l2c_link_adjust_allocation(); + } + + /* Check for ping outstanding */ + if (p_lcb->p_echo_rsp_cb) { + tL2CA_ECHO_RSP_CB *p_cb = p_lcb->p_echo_rsp_cb; + + /* Zero out the callback in case app immediately calls us again */ + p_lcb->p_echo_rsp_cb = NULL; + + (*p_cb) (L2CAP_PING_RESULT_NO_LINK); + } + +#if (BLE_INCLUDED == TRUE) + /* Check and release all the LE COC connections waiting for security */ + if (p_lcb->le_sec_pending_q) + { + while (!fixed_queue_is_empty(p_lcb->le_sec_pending_q)) + { + tL2CAP_SEC_DATA *p_buf = (tL2CAP_SEC_DATA*) fixed_queue_dequeue(p_lcb->le_sec_pending_q, FIXED_QUEUE_MAX_TIMEOUT); + if (p_buf->p_callback) { + p_buf->p_callback(p_lcb->remote_bd_addr, p_lcb->transport, p_buf->p_ref_data, BTM_DEV_RESET); + } + osi_free(p_buf); + } + fixed_queue_free(p_lcb->le_sec_pending_q, NULL); + p_lcb->le_sec_pending_q = NULL; + } +#endif ///BLE_INCLUDED == TRUE +} + + +/******************************************************************************* +** +** Function l2cu_find_lcb_by_bd_addr +** +** Description Look through all active LCBs for a match based on the +** remote BD address. +** +** Returns pointer to matched LCB, or NULL if no match +** +*******************************************************************************/ +tL2C_LCB *l2cu_find_lcb_by_bd_addr (BD_ADDR p_bd_addr, tBT_TRANSPORT transport) +{ + list_node_t *p_node = NULL; + tL2C_LCB *p_lcb = NULL; + for (p_node = list_begin(l2cb.p_lcb_pool); p_node; p_node = list_next(p_node)) { + p_lcb = list_node(p_node); + if ((p_lcb->in_use) && +#if BLE_INCLUDED == TRUE + p_lcb->transport == transport && +#endif + (!memcmp (p_lcb->remote_bd_addr, p_bd_addr, BD_ADDR_LEN))) { + return (p_lcb); + } + } + + /* If here, no match found */ + return (NULL); +} + +tL2C_LCB *l2cu_find_free_lcb (void) +{ + list_node_t *p_node = NULL; + tL2C_LCB *p_lcb = NULL; + for (p_node = list_begin(l2cb.p_lcb_pool); p_node; p_node = list_next(p_node)) { + p_lcb = list_node(p_node); + if (!p_lcb->in_use) { + return (p_lcb); + } + } + /* If here, no match found */ + return (NULL); +} + +uint8_t l2cu_plcb_active_count(void) +{ + list_node_t *p_node = NULL; + tL2C_LCB *p_lcb = NULL; + uint8_t active_count = 0; + for (p_node = list_begin(l2cb.p_lcb_pool); p_node; p_node = list_next(p_node)) { + p_lcb = list_node(p_node); + if (p_lcb && p_lcb->in_use) { + active_count ++; + } + } + if (active_count >= MAX_L2CAP_CHANNELS) { + L2CAP_TRACE_ERROR("error active count"); + active_count = 0; + } + L2CAP_TRACE_DEBUG("plcb active count %d", active_count); + return active_count; + +} + +/******************************************************************************* +** +** Function l2cu_get_conn_role +** +** Description Determine the desired role (master or slave) of a link. +** If already got a slave link, this one must be a master. If +** already got at least 1 link where we are the master, make this +** also a master. +** +** Returns HCI_ROLE_MASTER or HCI_ROLE_SLAVE +** +*******************************************************************************/ +UINT8 l2cu_get_conn_role (tL2C_LCB *p_this_lcb) +{ + return l2cb.desire_role; +} + +/******************************************************************************* +** +** Function l2c_is_cmd_rejected +** +** Description Checks if cmd_code is command or response +** If a command it will be rejected per spec. +** This function is used when a illegal packet length is detected +** +** Returns BOOLEAN - TRUE if cmd_code is a command and it is rejected, +** FALSE if response code. (command not rejected) +** +*******************************************************************************/ +BOOLEAN l2c_is_cmd_rejected (UINT8 cmd_code, UINT8 id, tL2C_LCB *p_lcb) +{ + switch (cmd_code) { + case L2CAP_CMD_CONN_REQ: + case L2CAP_CMD_CONFIG_REQ: + case L2CAP_CMD_DISC_REQ: + case L2CAP_CMD_ECHO_REQ: + case L2CAP_CMD_INFO_REQ: + case L2CAP_CMD_AMP_CONN_REQ: + case L2CAP_CMD_AMP_MOVE_REQ: + case L2CAP_CMD_BLE_UPDATE_REQ: + l2cu_send_peer_cmd_reject (p_lcb, L2CAP_CMD_REJ_MTU_EXCEEDED, id, L2CAP_DEFAULT_MTU, 0); + L2CAP_TRACE_WARNING ("Dumping first Command (%d)", cmd_code); + return TRUE; + + default: /* Otherwise a response */ + return FALSE; + } +} + +/******************************************************************************* +** +** Function l2cu_build_header +** +** Description Builds the L2CAP command packet header +** +** Returns Pointer to allocated packet or NULL if no resources +** +*******************************************************************************/ +BT_HDR *l2cu_build_header (tL2C_LCB *p_lcb, UINT16 len, UINT8 cmd, UINT8 id) +{ + BT_HDR *p_buf = (BT_HDR *)osi_malloc(L2CAP_CMD_BUF_SIZE); + UINT8 *p; + + if (!p_buf) { + return (NULL); + } + + p_buf->offset = L2CAP_SEND_CMD_OFFSET; + p_buf->len = len + HCI_DATA_PREAMBLE_SIZE + L2CAP_PKT_OVERHEAD + L2CAP_CMD_OVERHEAD; + p = (UINT8 *)(p_buf + 1) + L2CAP_SEND_CMD_OFFSET; + + /* Put in HCI header - handle + pkt boundary */ +#if (BLE_INCLUDED == TRUE) + if (p_lcb->transport == BT_TRANSPORT_LE) { + UINT16_TO_STREAM (p, (p_lcb->handle | (L2CAP_PKT_START_NON_FLUSHABLE << L2CAP_PKT_TYPE_SHIFT))); + } else +#endif + { +#if (L2CAP_NON_FLUSHABLE_PB_INCLUDED == TRUE) + UINT16_TO_STREAM (p, p_lcb->handle | l2cb.non_flushable_pbf); +#else + UINT16_TO_STREAM (p, (p_lcb->handle | (L2CAP_PKT_START << L2CAP_PKT_TYPE_SHIFT))); +#endif + } + + UINT16_TO_STREAM (p, len + L2CAP_PKT_OVERHEAD + L2CAP_CMD_OVERHEAD); + UINT16_TO_STREAM (p, len + L2CAP_CMD_OVERHEAD); + +#if (BLE_INCLUDED == TRUE) + if (p_lcb->transport == BT_TRANSPORT_LE) { + //counter_add("l2cap.ble.tx.bytes", p_buf->len); + //counter_add("l2cap.ble.tx.pkts", 1); + + UINT16_TO_STREAM (p, L2CAP_BLE_SIGNALLING_CID); + } else +#endif + { + //counter_add("l2cap.sig.tx.bytes", p_buf->len); + //counter_add("l2cap.sig.tx.pkts", 1); + UINT16_TO_STREAM (p, L2CAP_SIGNALLING_CID); + } + + /* Put in L2CAP command header */ + UINT8_TO_STREAM (p, cmd); + UINT8_TO_STREAM (p, id); + UINT16_TO_STREAM (p, len); + + return (p_buf); +} + +/******************************************************************************* +** +** Function l2cu_adj_id +** +** Description Checks for valid ID based on specified mask +** and adjusts the id if invalid. +** +** Returns void +** +*******************************************************************************/ +void l2cu_adj_id (tL2C_LCB *p_lcb, UINT8 adj_mask) +{ + if ((adj_mask & L2CAP_ADJ_ZERO_ID) && !p_lcb->id) { + p_lcb->id++; + } +} + +/******************************************************************************* +** +** Function l2cu_send_peer_cmd_reject +** +** Description Build and send an L2CAP "command reject" message +** to the peer. +** +** Returns void +** +*******************************************************************************/ +void l2cu_send_peer_cmd_reject (tL2C_LCB *p_lcb, UINT16 reason, UINT8 rem_id, + UINT16 p1, UINT16 p2) +{ + UINT16 param_len; + BT_HDR *p_buf; + UINT8 *p; + + /* Put in L2CAP packet header */ + if (reason == L2CAP_CMD_REJ_MTU_EXCEEDED) { + param_len = 2; + } else if (reason == L2CAP_CMD_REJ_INVALID_CID) { + param_len = 4; + } else { + param_len = 0; + } + + if ((p_buf = l2cu_build_header (p_lcb, (UINT16) (L2CAP_CMD_REJECT_LEN + param_len), L2CAP_CMD_REJECT, rem_id)) == NULL ) { + L2CAP_TRACE_WARNING ("L2CAP - no buffer cmd_rej"); + return; + } + + p = (UINT8 *)(p_buf + 1) + L2CAP_SEND_CMD_OFFSET + HCI_DATA_PREAMBLE_SIZE + + L2CAP_PKT_OVERHEAD + L2CAP_CMD_OVERHEAD; + + UINT16_TO_STREAM (p, reason); + + if (param_len >= 2) { + UINT16_TO_STREAM (p, p1); + } + + if (param_len >= 4) { + UINT16_TO_STREAM (p, p2); + } + + l2c_link_check_send_pkts (p_lcb, NULL, p_buf); +} + + +/******************************************************************************* +** +** Function l2cu_send_peer_connect_req +** +** Description Build and send an L2CAP "connection request" message +** to the peer. +** +** Returns void +** +*******************************************************************************/ +void l2cu_send_peer_connect_req (tL2C_CCB *p_ccb) +{ + BT_HDR *p_buf; + UINT8 *p; + + /* Create an identifier for this packet */ + p_ccb->p_lcb->id++; + l2cu_adj_id(p_ccb->p_lcb, L2CAP_ADJ_ID); + + p_ccb->local_id = p_ccb->p_lcb->id; + + if ((p_buf = l2cu_build_header (p_ccb->p_lcb, L2CAP_CONN_REQ_LEN, L2CAP_CMD_CONN_REQ, + p_ccb->local_id)) == NULL) { + L2CAP_TRACE_WARNING ("L2CAP - no buffer for conn_req"); + return; + } + + p = (UINT8 *)(p_buf + 1) + L2CAP_SEND_CMD_OFFSET + HCI_DATA_PREAMBLE_SIZE + + L2CAP_PKT_OVERHEAD + L2CAP_CMD_OVERHEAD; + + UINT16_TO_STREAM (p, p_ccb->p_rcb->real_psm); + UINT16_TO_STREAM (p, p_ccb->local_cid); + + l2c_link_check_send_pkts (p_ccb->p_lcb, NULL, p_buf); +} + + +/******************************************************************************* +** +** Function l2cu_send_peer_connect_rsp +** +** Description Build and send an L2CAP "connection response" message +** to the peer. +** +** Returns void +** +*******************************************************************************/ +void l2cu_send_peer_connect_rsp (tL2C_CCB *p_ccb, UINT16 result, UINT16 status) +{ + BT_HDR *p_buf; + UINT8 *p; + + if (result == L2CAP_CONN_PENDING) { + /* if we already sent pending response */ + if (p_ccb->flags & CCB_FLAG_SENT_PENDING) { + return; + } else { + p_ccb->flags |= CCB_FLAG_SENT_PENDING; + } + } + + if ((p_buf = l2cu_build_header(p_ccb->p_lcb, L2CAP_CONN_RSP_LEN, L2CAP_CMD_CONN_RSP, p_ccb->remote_id)) == NULL) { + L2CAP_TRACE_WARNING ("L2CAP - no buffer for conn_rsp"); + return; + } + + p = (UINT8 *)(p_buf + 1) + L2CAP_SEND_CMD_OFFSET + HCI_DATA_PREAMBLE_SIZE + + L2CAP_PKT_OVERHEAD + L2CAP_CMD_OVERHEAD; + + UINT16_TO_STREAM (p, p_ccb->local_cid); + UINT16_TO_STREAM (p, p_ccb->remote_cid); + UINT16_TO_STREAM (p, result); + UINT16_TO_STREAM (p, status); + + l2c_link_check_send_pkts (p_ccb->p_lcb, NULL, p_buf); +} + + +/******************************************************************************* +** +** Function l2cu_reject_connection +** +** Description Build and send an L2CAP "connection response neg" message +** to the peer. This function is called when there is no peer +** CCB (non-existant PSM or no resources). +** +** Returns void +** +*******************************************************************************/ +void l2cu_reject_connection (tL2C_LCB *p_lcb, UINT16 remote_cid, UINT8 rem_id, UINT16 result) +{ + BT_HDR *p_buf; + UINT8 *p; + + if ((p_buf = l2cu_build_header(p_lcb, L2CAP_CONN_RSP_LEN, L2CAP_CMD_CONN_RSP, rem_id)) == NULL ) { + L2CAP_TRACE_WARNING ("L2CAP - no buffer for conn_req"); + return; + } + + p = (UINT8 *)(p_buf + 1) + L2CAP_SEND_CMD_OFFSET + HCI_DATA_PREAMBLE_SIZE + L2CAP_PKT_OVERHEAD + L2CAP_CMD_OVERHEAD; + + UINT16_TO_STREAM (p, 0); /* Local CID of 0 */ + UINT16_TO_STREAM (p, remote_cid); + UINT16_TO_STREAM (p, result); + UINT16_TO_STREAM (p, 0); /* Status of 0 */ + + l2c_link_check_send_pkts (p_lcb, NULL, p_buf); +} + +/******************************************************************************* +** +** Function l2cu_send_peer_config_req +** +** Description Build and send an L2CAP "configuration request" message +** to the peer. +** +** Returns void +** +*******************************************************************************/ +void l2cu_send_peer_config_req (tL2C_CCB *p_ccb, tL2CAP_CFG_INFO *p_cfg) +{ + BT_HDR *p_buf; + UINT16 cfg_len = 0; + UINT8 *p; + + /* Create an identifier for this packet */ + p_ccb->p_lcb->id++; + l2cu_adj_id(p_ccb->p_lcb, L2CAP_ADJ_ID); + + p_ccb->local_id = p_ccb->p_lcb->id; + + if (p_cfg->mtu_present) { + cfg_len += L2CAP_CFG_MTU_OPTION_LEN + L2CAP_CFG_OPTION_OVERHEAD; + } + if (p_cfg->flush_to_present) { + cfg_len += L2CAP_CFG_FLUSH_OPTION_LEN + L2CAP_CFG_OPTION_OVERHEAD; + } + if (p_cfg->qos_present) { + cfg_len += L2CAP_CFG_QOS_OPTION_LEN + L2CAP_CFG_OPTION_OVERHEAD; + } + if (p_cfg->fcr_present) { + cfg_len += L2CAP_CFG_FCR_OPTION_LEN + L2CAP_CFG_OPTION_OVERHEAD; + } + if (p_cfg->fcs_present) { + cfg_len += L2CAP_CFG_FCS_OPTION_LEN + L2CAP_CFG_OPTION_OVERHEAD; + } + if (p_cfg->ext_flow_spec_present) { + cfg_len += L2CAP_CFG_EXT_FLOW_OPTION_LEN + L2CAP_CFG_OPTION_OVERHEAD; + } + + if ((p_buf = l2cu_build_header (p_ccb->p_lcb, (UINT16) (L2CAP_CONFIG_REQ_LEN + cfg_len), + L2CAP_CMD_CONFIG_REQ, p_ccb->local_id)) == NULL ) { + L2CAP_TRACE_WARNING ("L2CAP - no buffer for conn_req"); + return; + } + + p = (UINT8 *)(p_buf + 1) + L2CAP_SEND_CMD_OFFSET + HCI_DATA_PREAMBLE_SIZE + + L2CAP_PKT_OVERHEAD + L2CAP_CMD_OVERHEAD; + + UINT16_TO_STREAM (p, p_ccb->remote_cid); + UINT16_TO_STREAM (p, p_cfg->flags); /* Flags (continuation) */ + + /* Now, put the options */ + if (p_cfg->mtu_present) { + UINT8_TO_STREAM (p, L2CAP_CFG_TYPE_MTU); + UINT8_TO_STREAM (p, L2CAP_CFG_MTU_OPTION_LEN); + UINT16_TO_STREAM (p, p_cfg->mtu); + } + if (p_cfg->flush_to_present) { + UINT8_TO_STREAM (p, L2CAP_CFG_TYPE_FLUSH_TOUT); + UINT8_TO_STREAM (p, L2CAP_CFG_FLUSH_OPTION_LEN); + UINT16_TO_STREAM (p, p_cfg->flush_to); + } + if (p_cfg->qos_present) { + UINT8_TO_STREAM (p, L2CAP_CFG_TYPE_QOS); + UINT8_TO_STREAM (p, L2CAP_CFG_QOS_OPTION_LEN); + UINT8_TO_STREAM (p, p_cfg->qos.qos_flags); + UINT8_TO_STREAM (p, p_cfg->qos.service_type); + UINT32_TO_STREAM (p, p_cfg->qos.token_rate); + UINT32_TO_STREAM (p, p_cfg->qos.token_bucket_size); + UINT32_TO_STREAM (p, p_cfg->qos.peak_bandwidth); + UINT32_TO_STREAM (p, p_cfg->qos.latency); + UINT32_TO_STREAM (p, p_cfg->qos.delay_variation); + } + if (p_cfg->fcr_present) { + UINT8_TO_STREAM (p, L2CAP_CFG_TYPE_FCR); + UINT8_TO_STREAM (p, L2CAP_CFG_FCR_OPTION_LEN); + UINT8_TO_STREAM (p, p_cfg->fcr.mode); + UINT8_TO_STREAM (p, p_cfg->fcr.tx_win_sz); + UINT8_TO_STREAM (p, p_cfg->fcr.max_transmit); + UINT16_TO_STREAM (p, p_cfg->fcr.rtrans_tout); + UINT16_TO_STREAM (p, p_cfg->fcr.mon_tout); + UINT16_TO_STREAM (p, p_cfg->fcr.mps); + } + + if (p_cfg->fcs_present) { + UINT8_TO_STREAM (p, L2CAP_CFG_TYPE_FCS); + UINT8_TO_STREAM (p, L2CAP_CFG_FCS_OPTION_LEN); + UINT8_TO_STREAM (p, p_cfg->fcs); + } + + if (p_cfg->ext_flow_spec_present) { + UINT8_TO_STREAM (p, L2CAP_CFG_TYPE_EXT_FLOW); + UINT8_TO_STREAM (p, L2CAP_CFG_EXT_FLOW_OPTION_LEN); + UINT8_TO_STREAM (p, p_cfg->ext_flow_spec.id); + UINT8_TO_STREAM (p, p_cfg->ext_flow_spec.stype); + UINT16_TO_STREAM (p, p_cfg->ext_flow_spec.max_sdu_size); + UINT32_TO_STREAM (p, p_cfg->ext_flow_spec.sdu_inter_time); + UINT32_TO_STREAM (p, p_cfg->ext_flow_spec.access_latency); + UINT32_TO_STREAM (p, p_cfg->ext_flow_spec.flush_timeout); + } + + l2c_link_check_send_pkts (p_ccb->p_lcb, NULL, p_buf); +} + +/******************************************************************************* +** +** Function l2cu_send_peer_config_rsp +** +** Description Build and send an L2CAP "configuration response" message +** to the peer. +** +** Returns void +** +*******************************************************************************/ +void l2cu_send_peer_config_rsp (tL2C_CCB *p_ccb, tL2CAP_CFG_INFO *p_cfg) +{ + BT_HDR *p_buf; + UINT16 cfg_len = 0; + UINT8 *p; + + /* Create an identifier for this packet */ + if (p_cfg->mtu_present) { + cfg_len += L2CAP_CFG_MTU_OPTION_LEN + L2CAP_CFG_OPTION_OVERHEAD; + } + if (p_cfg->flush_to_present) { + cfg_len += L2CAP_CFG_FLUSH_OPTION_LEN + L2CAP_CFG_OPTION_OVERHEAD; + } + if (p_cfg->qos_present) { + cfg_len += L2CAP_CFG_QOS_OPTION_LEN + L2CAP_CFG_OPTION_OVERHEAD; + } + if (p_cfg->fcr_present) { + cfg_len += L2CAP_CFG_FCR_OPTION_LEN + L2CAP_CFG_OPTION_OVERHEAD; + } + if (p_cfg->ext_flow_spec_present) { + cfg_len += L2CAP_CFG_EXT_FLOW_OPTION_LEN + L2CAP_CFG_OPTION_OVERHEAD; + } + + if ((p_buf = l2cu_build_header (p_ccb->p_lcb, (UINT16)(L2CAP_CONFIG_RSP_LEN + cfg_len), + L2CAP_CMD_CONFIG_RSP, p_ccb->remote_id)) == NULL ) { + L2CAP_TRACE_WARNING ("L2CAP - no buffer for conn_req"); + return; + } + + p = (UINT8 *)(p_buf + 1) + L2CAP_SEND_CMD_OFFSET + HCI_DATA_PREAMBLE_SIZE + L2CAP_PKT_OVERHEAD + L2CAP_CMD_OVERHEAD; + + UINT16_TO_STREAM (p, p_ccb->remote_cid); + UINT16_TO_STREAM (p, p_cfg->flags); /* Flags (continuation) Must match request */ + UINT16_TO_STREAM (p, p_cfg->result); + + /* Now, put the options */ + if (p_cfg->mtu_present) { + UINT8_TO_STREAM (p, L2CAP_CFG_TYPE_MTU); + UINT8_TO_STREAM (p, L2CAP_CFG_MTU_OPTION_LEN); + UINT16_TO_STREAM (p, p_cfg->mtu); + } + if (p_cfg->flush_to_present) { + UINT8_TO_STREAM (p, L2CAP_CFG_TYPE_FLUSH_TOUT); + UINT8_TO_STREAM (p, L2CAP_CFG_FLUSH_OPTION_LEN); + UINT16_TO_STREAM (p, p_cfg->flush_to); + } + if (p_cfg->qos_present) { + UINT8_TO_STREAM (p, L2CAP_CFG_TYPE_QOS); + UINT8_TO_STREAM (p, L2CAP_CFG_QOS_OPTION_LEN); + UINT8_TO_STREAM (p, p_cfg->qos.qos_flags); + UINT8_TO_STREAM (p, p_cfg->qos.service_type); + UINT32_TO_STREAM (p, p_cfg->qos.token_rate); + UINT32_TO_STREAM (p, p_cfg->qos.token_bucket_size); + UINT32_TO_STREAM (p, p_cfg->qos.peak_bandwidth); + UINT32_TO_STREAM (p, p_cfg->qos.latency); + UINT32_TO_STREAM (p, p_cfg->qos.delay_variation); + } + if (p_cfg->fcr_present) { + UINT8_TO_STREAM (p, L2CAP_CFG_TYPE_FCR); + UINT8_TO_STREAM (p, L2CAP_CFG_FCR_OPTION_LEN); + UINT8_TO_STREAM (p, p_cfg->fcr.mode); + UINT8_TO_STREAM (p, p_cfg->fcr.tx_win_sz); + UINT8_TO_STREAM (p, p_cfg->fcr.max_transmit); + UINT16_TO_STREAM (p, p_ccb->our_cfg.fcr.rtrans_tout); + UINT16_TO_STREAM (p, p_ccb->our_cfg.fcr.mon_tout); + UINT16_TO_STREAM (p, p_cfg->fcr.mps); + } + + if (p_cfg->ext_flow_spec_present) { + UINT8_TO_STREAM (p, L2CAP_CFG_TYPE_EXT_FLOW); + UINT8_TO_STREAM (p, L2CAP_CFG_EXT_FLOW_OPTION_LEN); + UINT8_TO_STREAM (p, p_cfg->ext_flow_spec.id); + UINT8_TO_STREAM (p, p_cfg->ext_flow_spec.stype); + UINT16_TO_STREAM (p, p_cfg->ext_flow_spec.max_sdu_size); + UINT32_TO_STREAM (p, p_cfg->ext_flow_spec.sdu_inter_time); + UINT32_TO_STREAM (p, p_cfg->ext_flow_spec.access_latency); + UINT32_TO_STREAM (p, p_cfg->ext_flow_spec.flush_timeout); + } + + l2c_link_check_send_pkts (p_ccb->p_lcb, NULL, p_buf); +} + +/******************************************************************************* +** +** Function l2cu_send_peer_config_rej +** +** Description Build and send an L2CAP "configuration reject" message +** to the peer. +** +** Returns void +** +*******************************************************************************/ +void l2cu_send_peer_config_rej (tL2C_CCB *p_ccb, UINT8 *p_data, UINT16 data_len, UINT16 rej_len) +{ + BT_HDR *p_buf; + UINT16 len, cfg_len, buf_space, len1; + UINT8 *p, *p_hci_len, *p_data_end; + UINT8 cfg_code; + + L2CAP_TRACE_DEBUG("l2cu_send_peer_config_rej: data_len=%d, rej_len=%d", data_len, rej_len); + + + len = BT_HDR_SIZE + HCI_DATA_PREAMBLE_SIZE + L2CAP_PKT_OVERHEAD + L2CAP_CMD_OVERHEAD + L2CAP_CONFIG_RSP_LEN; + len1 = 0xFFFF - len; + if (rej_len > len1) { + L2CAP_TRACE_ERROR ("L2CAP - cfg_rej pkt size exceeds buffer design max limit."); + return; + } + + p_buf = (BT_HDR *)osi_malloc (len + rej_len); + + if (!p_buf) { + L2CAP_TRACE_ERROR ("L2CAP - no buffer for cfg_rej"); + return; + } + + p_buf->offset = L2CAP_SEND_CMD_OFFSET; + p = (UINT8 *)(p_buf + 1) + L2CAP_SEND_CMD_OFFSET; + + /* Put in HCI header - handle + pkt boundary */ +#if (L2CAP_NON_FLUSHABLE_PB_INCLUDED == TRUE) + if (HCI_NON_FLUSHABLE_PB_SUPPORTED(BTM_ReadLocalFeatures ())) { + UINT16_TO_STREAM (p, (p_ccb->p_lcb->handle | (L2CAP_PKT_START_NON_FLUSHABLE << L2CAP_PKT_TYPE_SHIFT))); + } else +#endif + { + UINT16_TO_STREAM (p, (p_ccb->p_lcb->handle | (L2CAP_PKT_START << L2CAP_PKT_TYPE_SHIFT))); + } + + /* Remember the HCI header length position, and save space for it */ + p_hci_len = p; + p += 2; + + /* Put in L2CAP packet header */ + UINT16_TO_STREAM (p, L2CAP_CMD_OVERHEAD + L2CAP_CONFIG_RSP_LEN + rej_len); + UINT16_TO_STREAM (p, L2CAP_SIGNALLING_CID); + + /* Put in L2CAP command header */ + UINT8_TO_STREAM (p, L2CAP_CMD_CONFIG_RSP); + UINT8_TO_STREAM (p, p_ccb->remote_id); + + UINT16_TO_STREAM (p, L2CAP_CONFIG_RSP_LEN + rej_len); + + UINT16_TO_STREAM (p, p_ccb->remote_cid); + UINT16_TO_STREAM (p, 0); /* Flags = 0 (no continuation) */ + UINT16_TO_STREAM (p, L2CAP_CFG_UNKNOWN_OPTIONS); + + buf_space = rej_len; + + /* Now, put the rejected options */ + p_data_end = p_data + data_len; + while (p_data < p_data_end) { + cfg_code = *p_data; + cfg_len = *(p_data + 1); + + switch (cfg_code & 0x7F) { + /* skip known options */ + case L2CAP_CFG_TYPE_MTU: + case L2CAP_CFG_TYPE_FLUSH_TOUT: + case L2CAP_CFG_TYPE_QOS: + p_data += cfg_len + L2CAP_CFG_OPTION_OVERHEAD; + break; + + /* unknown options; copy into rsp if not hints */ + default: + /* sanity check option length */ + if ((cfg_len + L2CAP_CFG_OPTION_OVERHEAD) <= data_len) { + if ((cfg_code & 0x80) == 0) { + if (buf_space >= (cfg_len + L2CAP_CFG_OPTION_OVERHEAD)) { + memcpy(p, p_data, cfg_len + L2CAP_CFG_OPTION_OVERHEAD); + p += cfg_len + L2CAP_CFG_OPTION_OVERHEAD; + buf_space -= (cfg_len + L2CAP_CFG_OPTION_OVERHEAD); + } else { + L2CAP_TRACE_WARNING("L2CAP - cfg_rej exceeds allocated buffer"); + p_data = p_data_end; /* force loop exit */ + break; + } + } + p_data += cfg_len + L2CAP_CFG_OPTION_OVERHEAD; + } + /* bad length; force loop exit */ + else { + p_data = p_data_end; + } + break; + } + } + + len = (UINT16) (p - p_hci_len - 2); + UINT16_TO_STREAM (p_hci_len, len); + + p_buf->len = len + 4; + + L2CAP_TRACE_DEBUG ("L2CAP - cfg_rej pkt hci_len=%d, l2cap_len=%d", + len, (L2CAP_CMD_OVERHEAD + L2CAP_CONFIG_RSP_LEN + rej_len)); + + l2c_link_check_send_pkts (p_ccb->p_lcb, NULL, p_buf); +} + +/******************************************************************************* +** +** Function l2cu_send_peer_disc_req +** +** Description Build and send an L2CAP "disconnect request" message +** to the peer. +** +** Returns void +** +*******************************************************************************/ +void l2cu_send_peer_disc_req (tL2C_CCB *p_ccb) +{ + BT_HDR *p_buf, *p_buf2; + UINT8 *p; + + /* Create an identifier for this packet */ + p_ccb->p_lcb->id++; + l2cu_adj_id(p_ccb->p_lcb, L2CAP_ADJ_ID); + + p_ccb->local_id = p_ccb->p_lcb->id; + + if ((p_buf = l2cu_build_header(p_ccb->p_lcb, L2CAP_DISC_REQ_LEN, L2CAP_CMD_DISC_REQ, p_ccb->local_id)) == NULL) { + L2CAP_TRACE_WARNING ("L2CAP - no buffer for disc_req"); + return; + } + + p = (UINT8 *)(p_buf + 1) + L2CAP_SEND_CMD_OFFSET + HCI_DATA_PREAMBLE_SIZE + L2CAP_PKT_OVERHEAD + L2CAP_CMD_OVERHEAD; + + UINT16_TO_STREAM (p, p_ccb->remote_cid); + UINT16_TO_STREAM (p, p_ccb->local_cid); + + /* Move all queued data packets to the LCB. In FCR mode, assume the higher + layer checks that all buffers are sent before disconnecting. + */ + if (p_ccb->peer_cfg.fcr.mode == L2CAP_FCR_BASIC_MODE) { + while ((p_buf2 = (BT_HDR *)fixed_queue_dequeue(p_ccb->xmit_hold_q, 0)) != NULL) { + l2cu_set_acl_hci_header (p_buf2, p_ccb); + l2c_link_check_send_pkts (p_ccb->p_lcb, p_ccb, p_buf2); + } + } + + l2c_link_check_send_pkts (p_ccb->p_lcb, NULL, p_buf); +} + + +/******************************************************************************* +** +** Function l2cu_send_peer_disc_rsp +** +** Description Build and send an L2CAP "disconnect response" message +** to the peer. +** +** This function is passed the parameters for the disconnect +** response instead of the CCB address, as it may be called +** to send a disconnect response when there is no CCB. +** +** Returns void +** +*******************************************************************************/ +void l2cu_send_peer_disc_rsp (tL2C_LCB *p_lcb, UINT8 remote_id, UINT16 local_cid, + UINT16 remote_cid) +{ + BT_HDR *p_buf; + UINT8 *p; + + if (!p_lcb) { + L2CAP_TRACE_WARNING("lcb already released\n"); + return; + } + + if ((p_buf = l2cu_build_header(p_lcb, L2CAP_DISC_RSP_LEN, L2CAP_CMD_DISC_RSP, remote_id)) == NULL) { + L2CAP_TRACE_WARNING ("L2CAP - no buffer for disc_rsp"); + return; + } + + p = (UINT8 *)(p_buf + 1) + L2CAP_SEND_CMD_OFFSET + HCI_DATA_PREAMBLE_SIZE + L2CAP_PKT_OVERHEAD + L2CAP_CMD_OVERHEAD; + + UINT16_TO_STREAM (p, local_cid); + UINT16_TO_STREAM (p, remote_cid); + + l2c_link_check_send_pkts (p_lcb, NULL, p_buf); +} + + +/******************************************************************************* +** +** Function l2cu_send_peer_echo_req +** +** Description Build and send an L2CAP "echo request" message +** to the peer. Note that we do not currently allow +** data in the echo request. +** +** Returns void +** +*******************************************************************************/ +void l2cu_send_peer_echo_req (tL2C_LCB *p_lcb, UINT8 *p_data, UINT16 data_len) +{ + BT_HDR *p_buf; + UINT8 *p; + + p_lcb->id++; + l2cu_adj_id(p_lcb, L2CAP_ADJ_ZERO_ID); /* check for wrap to '0' */ + + if ((p_buf = l2cu_build_header(p_lcb, (UINT16) (L2CAP_ECHO_REQ_LEN + data_len), L2CAP_CMD_ECHO_REQ, p_lcb->id)) == NULL) { + L2CAP_TRACE_WARNING ("L2CAP - no buffer for echo_req"); + return; + } + + p = (UINT8 *)(p_buf + 1) + L2CAP_SEND_CMD_OFFSET + HCI_DATA_PREAMBLE_SIZE + L2CAP_PKT_OVERHEAD + L2CAP_CMD_OVERHEAD; + + if (data_len) { + ARRAY_TO_STREAM (p, p_data, data_len); + } + + l2c_link_check_send_pkts (p_lcb, NULL, p_buf); +} + + +/******************************************************************************* +** +** Function l2cu_send_peer_echo_rsp +** +** Description Build and send an L2CAP "echo response" message +** to the peer. +** +** Returns void +** +*******************************************************************************/ +void l2cu_send_peer_echo_rsp (tL2C_LCB *p_lcb, UINT8 id, UINT8 *p_data, UINT16 data_len) +{ + BT_HDR *p_buf; + UINT8 *p; + UINT16 maxlen; + /* Filter out duplicate IDs or if available buffers are low (intruder checking) */ + if (!id || id == p_lcb->cur_echo_id) { + /* Dump this request since it is illegal */ + L2CAP_TRACE_WARNING ("L2CAP ignoring duplicate echo request (%d)", id); + return; + } else { + p_lcb->cur_echo_id = id; + } + + uint16_t acl_data_size = controller_get_interface()->get_acl_data_size_classic(); + uint16_t acl_packet_size = controller_get_interface()->get_acl_packet_size_classic(); + /* Don't return data if it does not fit in ACL and L2CAP MTU */ + maxlen = (L2CAP_CMD_BUF_SIZE > acl_packet_size) ? + acl_data_size : (UINT16)L2CAP_CMD_BUF_SIZE; + maxlen -= (UINT16)(BT_HDR_SIZE + HCI_DATA_PREAMBLE_SIZE + L2CAP_PKT_OVERHEAD + + L2CAP_CMD_OVERHEAD + L2CAP_ECHO_RSP_LEN); + + if (data_len > maxlen) { + data_len = 0; + } + + if ((p_buf = l2cu_build_header (p_lcb, (UINT16)(L2CAP_ECHO_RSP_LEN + data_len), L2CAP_CMD_ECHO_RSP, id)) == NULL) { + L2CAP_TRACE_WARNING ("L2CAP - no buffer for echo_rsp"); + return; + } + + p = (UINT8 *)(p_buf + 1) + L2CAP_SEND_CMD_OFFSET + HCI_DATA_PREAMBLE_SIZE + + L2CAP_PKT_OVERHEAD + L2CAP_CMD_OVERHEAD; + + if (data_len) { + ARRAY_TO_STREAM (p, p_data, data_len); + } + + l2c_link_check_send_pkts (p_lcb, NULL, p_buf); +} + +/******************************************************************************* +** +** Function l2cu_send_peer_info_req +** +** Description Build and send an L2CAP "info request" message +** to the peer. +** Returns void +** +*******************************************************************************/ +void l2cu_send_peer_info_req (tL2C_LCB *p_lcb, UINT16 info_type) +{ + BT_HDR *p_buf; + UINT8 *p; + + /* check for wrap and/or BRCM ID */ + p_lcb->id++; + l2cu_adj_id(p_lcb, L2CAP_ADJ_ID); + + if ((p_buf = l2cu_build_header(p_lcb, 2, L2CAP_CMD_INFO_REQ, p_lcb->id)) == NULL) { + L2CAP_TRACE_WARNING ("L2CAP - no buffer for info_req"); + return; + } + + L2CAP_TRACE_EVENT ("l2cu_send_peer_info_req: type 0x%04x", info_type); + + p = (UINT8 *)(p_buf + 1) + L2CAP_SEND_CMD_OFFSET + HCI_DATA_PREAMBLE_SIZE + + L2CAP_PKT_OVERHEAD + L2CAP_CMD_OVERHEAD; + + UINT16_TO_STREAM (p, info_type); + + p_lcb->w4_info_rsp = TRUE; + btu_start_timer (&p_lcb->info_timer_entry, BTU_TTYPE_L2CAP_INFO, L2CAP_WAIT_INFO_RSP_TOUT); + + l2c_link_check_send_pkts (p_lcb, NULL, p_buf); +} + + +/******************************************************************************* +** +** Function l2cu_send_peer_info_rsp +** +** Description Build and send an L2CAP "info response" message +** to the peer. +** +** Returns void +** +*******************************************************************************/ +void l2cu_send_peer_info_rsp (tL2C_LCB *p_lcb, UINT8 remote_id, UINT16 info_type) +{ + BT_HDR *p_buf; + UINT8 *p; + UINT16 len = L2CAP_INFO_RSP_LEN; + +#if (L2CAP_CONFORMANCE_TESTING == TRUE) + if ((info_type == L2CAP_EXTENDED_FEATURES_INFO_TYPE) + && (l2cb.test_info_resp & (L2CAP_EXTFEA_ENH_RETRANS | L2CAP_EXTFEA_STREAM_MODE | + L2CAP_EXTFEA_NO_CRC | L2CAP_EXTFEA_EXT_FLOW_SPEC | + L2CAP_EXTFEA_FIXED_CHNLS | L2CAP_EXTFEA_EXT_WINDOW | + L2CAP_EXTFEA_UCD_RECEPTION )) ) +#else + if ((info_type == L2CAP_EXTENDED_FEATURES_INFO_TYPE) + && (L2CAP_EXTFEA_SUPPORTED_MASK & (L2CAP_EXTFEA_ENH_RETRANS | L2CAP_EXTFEA_STREAM_MODE | + L2CAP_EXTFEA_NO_CRC | L2CAP_EXTFEA_FIXED_CHNLS | + L2CAP_EXTFEA_UCD_RECEPTION )) ) +#endif + { + len += L2CAP_EXTENDED_FEATURES_ARRAY_SIZE; + } else if (info_type == L2CAP_FIXED_CHANNELS_INFO_TYPE) { + len += L2CAP_FIXED_CHNL_ARRAY_SIZE; + } else if (info_type == L2CAP_CONNLESS_MTU_INFO_TYPE) { + len += L2CAP_CONNLESS_MTU_INFO_SIZE; + } + + if ((p_buf = l2cu_build_header(p_lcb, len, L2CAP_CMD_INFO_RSP, remote_id)) == NULL) { + L2CAP_TRACE_WARNING ("L2CAP - no buffer for info_rsp"); + return; + } + + p = (UINT8 *)(p_buf + 1) + L2CAP_SEND_CMD_OFFSET + HCI_DATA_PREAMBLE_SIZE + + L2CAP_PKT_OVERHEAD + L2CAP_CMD_OVERHEAD; + + UINT16_TO_STREAM (p, info_type); + +#if (L2CAP_CONFORMANCE_TESTING == TRUE) + if ((info_type == L2CAP_EXTENDED_FEATURES_INFO_TYPE) + && (l2cb.test_info_resp & ( L2CAP_EXTFEA_ENH_RETRANS | L2CAP_EXTFEA_STREAM_MODE + | L2CAP_EXTFEA_UCD_RECEPTION )) ) +#else + if ((info_type == L2CAP_EXTENDED_FEATURES_INFO_TYPE) + && (L2CAP_EXTFEA_SUPPORTED_MASK & ( L2CAP_EXTFEA_ENH_RETRANS | L2CAP_EXTFEA_STREAM_MODE + | L2CAP_EXTFEA_UCD_RECEPTION )) ) +#endif + { + UINT16_TO_STREAM (p, L2CAP_INFO_RESP_RESULT_SUCCESS); +#if (BLE_INCLUDED == TRUE) + if (p_lcb->transport == BT_TRANSPORT_LE) { + /* optional data are not added for now */ + UINT32_TO_STREAM (p, L2CAP_BLE_EXTFEA_MASK); + } else +#endif + { +#if L2CAP_CONFORMANCE_TESTING == TRUE + UINT32_TO_STREAM (p, l2cb.test_info_resp); +#else +#if (L2CAP_NUM_FIXED_CHNLS > 0) + UINT32_TO_STREAM (p, L2CAP_EXTFEA_SUPPORTED_MASK | L2CAP_EXTFEA_FIXED_CHNLS); +#else + UINT32_TO_STREAM (p, L2CAP_EXTFEA_SUPPORTED_MASK); +#endif +#endif + } + } else if (info_type == L2CAP_FIXED_CHANNELS_INFO_TYPE) { + UINT16_TO_STREAM (p, L2CAP_INFO_RESP_RESULT_SUCCESS); + memset (p, 0, L2CAP_FIXED_CHNL_ARRAY_SIZE); + + p[0] = L2CAP_FIXED_CHNL_SIG_BIT; + + if ( L2CAP_EXTFEA_SUPPORTED_MASK & L2CAP_EXTFEA_UCD_RECEPTION ) { + p[0] |= L2CAP_FIXED_CHNL_CNCTLESS_BIT; + } + +#if (L2CAP_NUM_FIXED_CHNLS > 0) + { + int xx; + + for (xx = 0; xx < L2CAP_NUM_FIXED_CHNLS; xx++) + if (l2cb.fixed_reg[xx].pL2CA_FixedConn_Cb != NULL) { + p[(xx + L2CAP_FIRST_FIXED_CHNL) / 8] |= 1 << ((xx + L2CAP_FIRST_FIXED_CHNL) % 8); + } + } +#endif + } else if (info_type == L2CAP_CONNLESS_MTU_INFO_TYPE) { + UINT16_TO_STREAM (p, L2CAP_INFO_RESP_RESULT_SUCCESS); + UINT16_TO_STREAM (p, L2CAP_UCD_MTU); + } else { + UINT16_TO_STREAM (p, L2CAP_INFO_RESP_RESULT_NOT_SUPPORTED); /* 'not supported' */ + } + + l2c_link_check_send_pkts (p_lcb, NULL, p_buf); +} + +/****************************************************************************** +** +** Function l2cu_enqueue_ccb +** +** Description queue CCB by priority. The first CCB is highest priority and +** is served at first. The CCB is queued to an LLCB or an LCB. +** +** Returns None +** +*******************************************************************************/ +void l2cu_enqueue_ccb (tL2C_CCB *p_ccb) +{ + tL2C_CCB *p_ccb1; + tL2C_CCB_Q *p_q = NULL; + + /* Find out which queue the channel is on + */ + if (p_ccb->p_lcb != NULL) { + p_q = &p_ccb->p_lcb->ccb_queue; + } + + if ( (!p_ccb->in_use) || (p_q == NULL) ) { + L2CAP_TRACE_ERROR ("l2cu_enqueue_ccb CID: 0x%04x ERROR in_use: %u p_lcb: %p", + p_ccb->local_cid, p_ccb->in_use, p_ccb->p_lcb); + return; + } + + L2CAP_TRACE_DEBUG ("l2cu_enqueue_ccb CID: 0x%04x priority: %d", + p_ccb->local_cid, p_ccb->ccb_priority); + + /* If the queue is empty, we go at the front */ + if (!p_q->p_first_ccb) { + p_q->p_first_ccb = p_q->p_last_ccb = p_ccb; + p_ccb->p_next_ccb = p_ccb->p_prev_ccb = NULL; + } else { + p_ccb1 = p_q->p_first_ccb; + + while (p_ccb1 != NULL) { + /* Insert new ccb at the end of the same priority. Lower number, higher priority */ + if (p_ccb->ccb_priority < p_ccb1->ccb_priority) { + /* Are we at the head of the queue ? */ + if (p_ccb1 == p_q->p_first_ccb) { + p_q->p_first_ccb = p_ccb; + } else { + p_ccb1->p_prev_ccb->p_next_ccb = p_ccb; + } + + p_ccb->p_next_ccb = p_ccb1; + p_ccb->p_prev_ccb = p_ccb1->p_prev_ccb; + p_ccb1->p_prev_ccb = p_ccb; + break; + } + + p_ccb1 = p_ccb1->p_next_ccb; + } + + /* If we are lower then anyone in the list, we go at the end */ + if (!p_ccb1) { + /* add new ccb at the end of the list */ + p_q->p_last_ccb->p_next_ccb = p_ccb; + + p_ccb->p_next_ccb = NULL; + p_ccb->p_prev_ccb = p_q->p_last_ccb; + p_q->p_last_ccb = p_ccb; + } + } + +#if (L2CAP_ROUND_ROBIN_CHANNEL_SERVICE == TRUE) + /* Adding CCB into round robin service table of its LCB */ + if (p_ccb->p_lcb != NULL) { + /* if this is the first channel in this priority group */ + if (p_ccb->p_lcb->rr_serv[p_ccb->ccb_priority].num_ccb == 0 ) { + /* Set the first channel to this CCB */ + p_ccb->p_lcb->rr_serv[p_ccb->ccb_priority].p_first_ccb = p_ccb; + /* Set the next serving channel in this group to this CCB */ + p_ccb->p_lcb->rr_serv[p_ccb->ccb_priority].p_serve_ccb = p_ccb; + /* Initialize quota of this priority group based on its priority */ + p_ccb->p_lcb->rr_serv[p_ccb->ccb_priority].quota = L2CAP_GET_PRIORITY_QUOTA(p_ccb->ccb_priority); + } + /* increase number of channels in this group */ + p_ccb->p_lcb->rr_serv[p_ccb->ccb_priority].num_ccb++; + } +#endif + +} + +/****************************************************************************** +** +** Function l2cu_dequeue_ccb +** +** Description dequeue CCB from a queue +** +** Returns - +** +*******************************************************************************/ +void l2cu_dequeue_ccb (tL2C_CCB *p_ccb) +{ + tL2C_CCB_Q *p_q = NULL; + + L2CAP_TRACE_DEBUG ("l2cu_dequeue_ccb CID: 0x%04x", p_ccb->local_cid); + + /* Find out which queue the channel is on + */ + if (p_ccb->p_lcb != NULL) { + p_q = &p_ccb->p_lcb->ccb_queue; + } + + if ( (!p_ccb->in_use) || (p_q == NULL) || (p_q->p_first_ccb == NULL) ) { + L2CAP_TRACE_ERROR ("l2cu_dequeue_ccb CID: 0x%04x ERROR in_use: %u p_lcb: %p p_q: %p p_q->p_first_ccb: %p", + p_ccb->local_cid, p_ccb->in_use, p_ccb->p_lcb, p_q, p_q ? p_q->p_first_ccb : 0); + return; + } + +#if (L2CAP_ROUND_ROBIN_CHANNEL_SERVICE == TRUE) + /* Removing CCB from round robin service table of its LCB */ + if (p_ccb->p_lcb != NULL) { + /* decrease number of channels in this priority group */ + p_ccb->p_lcb->rr_serv[p_ccb->ccb_priority].num_ccb--; + + /* if it was the last channel in the priority group */ + if (p_ccb->p_lcb->rr_serv[p_ccb->ccb_priority].num_ccb == 0 ) { + p_ccb->p_lcb->rr_serv[p_ccb->ccb_priority].p_first_ccb = NULL; + p_ccb->p_lcb->rr_serv[p_ccb->ccb_priority].p_serve_ccb = NULL; + } else { + /* if it is the first channel of this group */ + if ( p_ccb->p_lcb->rr_serv[p_ccb->ccb_priority].p_first_ccb == p_ccb ) { + p_ccb->p_lcb->rr_serv[p_ccb->ccb_priority].p_first_ccb = p_ccb->p_next_ccb; + } + /* if it is the next serving channel of this group */ + if ( p_ccb->p_lcb->rr_serv[p_ccb->ccb_priority].p_serve_ccb == p_ccb ) { + /* simply, start serving from the first channel */ + p_ccb->p_lcb->rr_serv[p_ccb->ccb_priority].p_serve_ccb + = p_ccb->p_lcb->rr_serv[p_ccb->ccb_priority].p_first_ccb; + } + } + } +#endif + + if (p_ccb == p_q->p_first_ccb) { + /* We are removing the first in a queue */ + p_q->p_first_ccb = p_ccb->p_next_ccb; + + if (p_q->p_first_ccb) { + p_q->p_first_ccb->p_prev_ccb = NULL; + } else { + p_q->p_last_ccb = NULL; + } + } else if (p_ccb == p_q->p_last_ccb) { + /* We are removing the last in a queue */ + p_q->p_last_ccb = p_ccb->p_prev_ccb; + p_q->p_last_ccb->p_next_ccb = NULL; + } else { + /* In the middle of a chain. */ + p_ccb->p_prev_ccb->p_next_ccb = p_ccb->p_next_ccb; + p_ccb->p_next_ccb->p_prev_ccb = p_ccb->p_prev_ccb; + } + + p_ccb->p_next_ccb = p_ccb->p_prev_ccb = NULL; +} + +/****************************************************************************** +** +** Function l2cu_change_pri_ccb +** +** Description +** +** Returns - +** +*******************************************************************************/ +void l2cu_change_pri_ccb (tL2C_CCB *p_ccb, tL2CAP_CHNL_PRIORITY priority) +{ + if (p_ccb->ccb_priority != priority) { + /* If CCB is not the only guy on the queue */ + if ( (p_ccb->p_next_ccb != NULL) || (p_ccb->p_prev_ccb != NULL) ) { + L2CAP_TRACE_DEBUG ("Update CCB list in logical link"); + + /* Remove CCB from queue and re-queue it at new priority */ + l2cu_dequeue_ccb (p_ccb); + + p_ccb->ccb_priority = priority; + l2cu_enqueue_ccb (p_ccb); + } +#if (L2CAP_ROUND_ROBIN_CHANNEL_SERVICE == TRUE) + else { + /* If CCB is the only guy on the queue, no need to re-enqueue */ + /* update only round robin service data */ + p_ccb->p_lcb->rr_serv[p_ccb->ccb_priority].num_ccb = 0; + p_ccb->p_lcb->rr_serv[p_ccb->ccb_priority].p_first_ccb = NULL; + p_ccb->p_lcb->rr_serv[p_ccb->ccb_priority].p_serve_ccb = NULL; + + p_ccb->ccb_priority = priority; + + p_ccb->p_lcb->rr_serv[p_ccb->ccb_priority].p_first_ccb = p_ccb; + p_ccb->p_lcb->rr_serv[p_ccb->ccb_priority].p_serve_ccb = p_ccb; + p_ccb->p_lcb->rr_serv[p_ccb->ccb_priority].quota = L2CAP_GET_PRIORITY_QUOTA(p_ccb->ccb_priority); + p_ccb->p_lcb->rr_serv[p_ccb->ccb_priority].num_ccb = 1; + } +#endif + } +} + +/******************************************************************************* +** +** Function l2cu_allocate_ccb +** +** Description This function allocates a Channel Control Block and +** attaches it to a link control block. The local CID +** is also assigned. +** +** Returns pointer to CCB, or NULL if none +** +*******************************************************************************/ +bool l2cu_find_ccb_in_list(void *p_ccb_node, void *p_local_cid); +tL2C_CCB *l2cu_allocate_ccb (tL2C_LCB *p_lcb, UINT16 cid) +{ + tL2C_CCB *p_ccb = NULL; + uint16_t tmp_cid = L2CAP_BASE_APPL_CID; + L2CAP_TRACE_DEBUG ("l2cu_allocate_ccb: cid 0x%04x", cid); + + p_ccb = l2cu_find_free_ccb (); + if(p_ccb == NULL) { + if (list_length(l2cb.p_ccb_pool) < MAX_L2CAP_CHANNELS) { + p_ccb = (tL2C_CCB *)osi_malloc(sizeof(tL2C_CCB)); + + if (p_ccb) { + memset (p_ccb, 0, sizeof(tL2C_CCB)); + list_append(l2cb.p_ccb_pool, p_ccb); + } + } + } + if (p_ccb == NULL) { + return (NULL); + } + + p_ccb->p_next_ccb = p_ccb->p_prev_ccb = NULL; + + p_ccb->in_use = TRUE; + + /* Get a CID for the connection */ + for (tmp_cid = L2CAP_BASE_APPL_CID; tmp_cid < MAX_L2CAP_CHANNELS + L2CAP_BASE_APPL_CID; tmp_cid++) { + if (list_foreach(l2cb.p_ccb_pool, l2cu_find_ccb_in_list, &tmp_cid) == NULL) { + break; + } + } + assert(tmp_cid != MAX_L2CAP_CHANNELS + L2CAP_BASE_APPL_CID); + p_ccb->local_cid = tmp_cid; + p_ccb->p_lcb = p_lcb; + p_ccb->p_rcb = NULL; + p_ccb->should_free_rcb = false; + + /* Set priority then insert ccb into LCB queue (if we have an LCB) */ + p_ccb->ccb_priority = L2CAP_CHNL_PRIORITY_LOW; + + if (p_lcb) { + l2cu_enqueue_ccb (p_ccb); + } + + /* clear what peer wants to configure */ + p_ccb->peer_cfg_bits = 0; + + /* Put in default values for configuration */ + memset (&p_ccb->our_cfg, 0, sizeof(tL2CAP_CFG_INFO)); + memset (&p_ccb->peer_cfg, 0, sizeof(tL2CAP_CFG_INFO)); + + /* Put in default values for local/peer configurations */ + p_ccb->our_cfg.flush_to = p_ccb->peer_cfg.flush_to = L2CAP_DEFAULT_FLUSH_TO; + p_ccb->our_cfg.mtu = p_ccb->peer_cfg.mtu = L2CAP_DEFAULT_MTU; + p_ccb->our_cfg.qos.service_type = p_ccb->peer_cfg.qos.service_type = L2CAP_DEFAULT_SERV_TYPE; + p_ccb->our_cfg.qos.token_rate = p_ccb->peer_cfg.qos.token_rate = L2CAP_DEFAULT_TOKEN_RATE; + p_ccb->our_cfg.qos.token_bucket_size = p_ccb->peer_cfg.qos.token_bucket_size = L2CAP_DEFAULT_BUCKET_SIZE; + p_ccb->our_cfg.qos.peak_bandwidth = p_ccb->peer_cfg.qos.peak_bandwidth = L2CAP_DEFAULT_PEAK_BANDWIDTH; + p_ccb->our_cfg.qos.latency = p_ccb->peer_cfg.qos.latency = L2CAP_DEFAULT_LATENCY; + p_ccb->our_cfg.qos.delay_variation = p_ccb->peer_cfg.qos.delay_variation = L2CAP_DEFAULT_DELAY; + + p_ccb->bypass_fcs = 0; + memset (&p_ccb->ertm_info, 0, sizeof(tL2CAP_ERTM_INFO)); + p_ccb->peer_cfg_already_rejected = FALSE; + p_ccb->fcr_cfg_tries = L2CAP_MAX_FCR_CFG_TRIES; + + /* stop and release timers */ + btu_free_quick_timer(&p_ccb->fcrb.ack_timer); + memset(&p_ccb->fcrb.ack_timer, 0, sizeof(TIMER_LIST_ENT)); + p_ccb->fcrb.ack_timer.param = (TIMER_PARAM_TYPE)p_ccb; + + btu_free_quick_timer(&p_ccb->fcrb.mon_retrans_timer); + memset(&p_ccb->fcrb.mon_retrans_timer, 0, sizeof(TIMER_LIST_ENT)); + p_ccb->fcrb.mon_retrans_timer.param = (TIMER_PARAM_TYPE)p_ccb; + +// btla-specific ++ + /* CSP408639 Fix: When L2CAP send amp move channel request or receive + * L2CEVT_AMP_MOVE_REQ do following sequence. Send channel move + * request -> Stop retrans/monitor timer -> Change channel state to CST_AMP_MOVING. */ +// btla-specific -- + +#if (CLASSIC_BT_INCLUDED == TRUE) + l2c_fcr_free_timer (p_ccb); +#endif ///CLASSIC_BT_INCLUDED == TRUE + +#if BT_SDP_BQB_INCLUDED + if (l2cap_bqb_ertm_mode_included_flag) { + p_ccb->ertm_info.preferred_mode = L2CAP_FCR_ERTM_MODE; + p_ccb->ertm_info.allowed_modes = L2CAP_FCR_CHAN_OPT_ERTM; + } else +#endif /* BT_SDP_BQB_INCLUDED */ + { + p_ccb->ertm_info.preferred_mode = L2CAP_FCR_BASIC_MODE; /* Default mode for channel is basic mode */ + p_ccb->ertm_info.allowed_modes = L2CAP_FCR_CHAN_OPT_BASIC|L2CAP_FCR_CHAN_OPT_ERTM; + } + p_ccb->ertm_info.fcr_rx_buf_size = L2CAP_FCR_RX_BUF_SIZE; + p_ccb->ertm_info.fcr_tx_buf_size = L2CAP_FCR_TX_BUF_SIZE; + p_ccb->ertm_info.user_rx_buf_size = L2CAP_USER_RX_BUF_SIZE; + p_ccb->ertm_info.user_tx_buf_size = L2CAP_USER_TX_BUF_SIZE; + p_ccb->max_rx_mtu = L2CAP_MTU_SIZE; + p_ccb->tx_mps = L2CAP_FCR_TX_BUF_SIZE - 32; + + p_ccb->xmit_hold_q = fixed_queue_new(QUEUE_SIZE_MAX); +#if (CLASSIC_BT_INCLUDED == TRUE) + p_ccb->fcrb.srej_rcv_hold_q = fixed_queue_new(QUEUE_SIZE_MAX); + p_ccb->fcrb.retrans_q = fixed_queue_new(QUEUE_SIZE_MAX); + p_ccb->fcrb.waiting_for_ack_q = fixed_queue_new(QUEUE_SIZE_MAX); +#endif ///CLASSIC_BT_INCLUDED == TRUE + + p_ccb->cong_sent = FALSE; + p_ccb->buff_quota = 2; /* This gets set after config */ + + /* If CCB was reserved Config_Done can already have some value */ + if (cid == 0) { + p_ccb->config_done = 0; + } else { + L2CAP_TRACE_DEBUG ("l2cu_allocate_ccb: cid 0x%04x config_done:0x%x", cid, p_ccb->config_done); + } + + p_ccb->chnl_state = CST_CLOSED; + p_ccb->flags = 0; + p_ccb->tx_data_rate = L2CAP_CHNL_DATA_RATE_LOW; + p_ccb->rx_data_rate = L2CAP_CHNL_DATA_RATE_LOW; + +#if (L2CAP_NON_FLUSHABLE_PB_INCLUDED == TRUE) + p_ccb->is_flushable = FALSE; +#endif + + btu_free_timer(&p_ccb->timer_entry); + memset(&p_ccb->timer_entry, 0, sizeof(TIMER_LIST_ENT)); + p_ccb->timer_entry.param = (TIMER_PARAM_TYPE)p_ccb; + p_ccb->timer_entry.in_use = 0; + + l2c_link_adjust_chnl_allocation (); + + return (p_ccb); +} + +/******************************************************************************* +** +** Function l2cu_start_post_bond_timer +** +** Description This function starts the ACL Link inactivity timer after +** dedicated bonding +** This timer can be longer than the normal link inactivity +** timer for some platforms. +** +** Returns BOOLEAN - TRUE if idle timer started or disconnect initiated +** FALSE if there's one or more pending CCB's exist +** +*******************************************************************************/ +BOOLEAN l2cu_start_post_bond_timer (UINT16 handle) +{ + UINT16 timeout; + tL2C_LCB *p_lcb = l2cu_find_lcb_by_handle(handle); + + if (!p_lcb) { + return (TRUE); + } + + p_lcb->is_bonding = FALSE; + + /* Only start timer if no control blocks allocated */ + if (p_lcb->ccb_queue.p_first_ccb != NULL) { + return (FALSE); + } + + /* If no channels on the connection, start idle timeout */ + if ( (p_lcb->link_state == LST_CONNECTED) || (p_lcb->link_state == LST_CONNECTING) || (p_lcb->link_state == LST_DISCONNECTING) ) { + if (p_lcb->idle_timeout == 0) { + if (btsnd_hcic_disconnect (p_lcb->handle, HCI_ERR_PEER_USER)) { + p_lcb->link_state = LST_DISCONNECTING; + timeout = L2CAP_LINK_DISCONNECT_TOUT; + } else { + timeout = BT_1SEC_TIMEOUT; + } + } else { + timeout = L2CAP_BONDING_TIMEOUT; + } + + if (timeout != 0xFFFF) { + btu_start_timer (&p_lcb->timer_entry, BTU_TTYPE_L2CAP_LINK, timeout); + } + + return (TRUE); + } + + return (FALSE); +} + +/******************************************************************************* +** +** Function l2cu_release_ccb +** +** Description This function releases a Channel Control Block. The timer +** is stopped, any attached buffers freed, and the CCB is removed +** from the link control block. +** +** Returns void +** +*******************************************************************************/ +void l2cu_release_ccb (tL2C_CCB *p_ccb) +{ + tL2C_LCB *p_lcb = p_ccb->p_lcb; + tL2C_RCB *p_rcb = p_ccb->p_rcb; + L2CAP_TRACE_DEBUG ("l2cu_release_ccb: cid 0x%04x in_use: %u", p_ccb->local_cid, p_ccb->in_use); + + /* If already released, could be race condition */ + if (!p_ccb->in_use) { + return; + } +#if BLE_INCLUDED == TRUE + if (p_lcb->transport == BT_TRANSPORT_LE) { + /* Take samephore to avoid race condition */ + l2ble_update_att_acl_pkt_num(L2CA_BUFF_FREE, NULL); + } +#endif +#if (SDP_INCLUDED == TRUE) + if (p_rcb && (p_rcb->psm != p_rcb->real_psm)) { + btm_sec_clr_service_by_psm(p_rcb->psm); + } +#endif ///SMP_INCLUDED == TRUE + if (p_ccb->should_free_rcb) { + osi_free(p_rcb); + p_ccb->p_rcb = NULL; + p_ccb->should_free_rcb = false; + } + if (p_lcb) { + btm_sec_clr_temp_auth_service (p_lcb->remote_bd_addr); + } + + /* Stop and free the timer */ + btu_free_timer (&p_ccb->timer_entry); + + fixed_queue_free(p_ccb->xmit_hold_q, osi_free_func); + p_ccb->xmit_hold_q = NULL; +#if (CLASSIC_BT_INCLUDED == TRUE) + fixed_queue_free(p_ccb->fcrb.srej_rcv_hold_q, osi_free_func); + fixed_queue_free(p_ccb->fcrb.retrans_q, osi_free_func); + fixed_queue_free(p_ccb->fcrb.waiting_for_ack_q, osi_free_func); + p_ccb->fcrb.srej_rcv_hold_q = NULL; + p_ccb->fcrb.retrans_q = NULL; + p_ccb->fcrb.waiting_for_ack_q = NULL; +#endif ///CLASSIC_BT_INCLUDED == TRUE + + +#if (CLASSIC_BT_INCLUDED == TRUE) + l2c_fcr_cleanup (p_ccb); +#endif ///CLASSIC_BT_INCLUDED == TRUE + /* Channel may not be assigned to any LCB if it was just pre-reserved */ + if ( (p_lcb) && + ( (p_ccb->local_cid >= L2CAP_BASE_APPL_CID) +#if (L2CAP_UCD_INCLUDED == TRUE) + || (p_ccb->local_cid == L2CAP_CONNECTIONLESS_CID) +#endif + ) + ) { + l2cu_dequeue_ccb (p_ccb); + + /* Delink the CCB from the LCB */ + p_ccb->p_lcb = NULL; + } + + + /* Flag as not in use */ + p_ccb->in_use = FALSE; + + /* If no channels on the connection, start idle timeout */ + if ((p_lcb) && p_lcb->in_use && (p_lcb->link_state == LST_CONNECTED)) { + if (!p_lcb->ccb_queue.p_first_ccb) { + l2cu_no_dynamic_ccbs (p_lcb); + } else { + /* Link is still active, adjust channel quotas. */ + l2c_link_adjust_chnl_allocation (); + } + } +} + +/******************************************************************************* +** +** Function l2cu_find_ccb_by_remote_cid +** +** Description Look through all active CCBs on a link for a match based +** on the remote CID. +** +** Returns pointer to matched CCB, or NULL if no match +** +*******************************************************************************/ +tL2C_CCB *l2cu_find_ccb_by_remote_cid (tL2C_LCB *p_lcb, UINT16 remote_cid) +{ + tL2C_CCB *p_ccb; + + /* If LCB is NULL, look through all active links */ + if (!p_lcb) { + return NULL; + } else { + for (p_ccb = p_lcb->ccb_queue.p_first_ccb; p_ccb; p_ccb = p_ccb->p_next_ccb) + if ((p_ccb->in_use) && (p_ccb->remote_cid == remote_cid)) { + return (p_ccb); + } + } + + /* If here, no match found */ + return (NULL); +} + +/******************************************************************************* +** +** Function l2cu_allocate_rcb +** +** Description Look through the Registration Control Blocks for a free +** one. +** +** Returns Pointer to the RCB or NULL if not found +** +*******************************************************************************/ +tL2C_RCB *l2cu_allocate_rcb (UINT16 psm) +{ + tL2C_RCB *p_rcb = &l2cb.rcb_pool[0]; + UINT16 xx; + + for (xx = 0; xx < MAX_L2CAP_CLIENTS; xx++, p_rcb++) { + if (!p_rcb->in_use) { + p_rcb->in_use = TRUE; + p_rcb->psm = psm; +#if (L2CAP_UCD_INCLUDED == TRUE) + p_rcb->ucd.state = L2C_UCD_STATE_UNUSED; +#endif + return (p_rcb); + } + } + + /* If here, no free RCB found */ + return (NULL); +} + +#if (BLE_INCLUDED == TRUE) +/******************************************************************************* +** +** Function l2cu_allocate_ble_rcb +** +** Description Look through the BLE Registration Control Blocks for a free +** one. +** +** Returns Pointer to the BLE RCB or NULL if not found +** +*******************************************************************************/ +tL2C_RCB *l2cu_allocate_ble_rcb (UINT16 psm) +{ + tL2C_RCB *p_rcb = &l2cb.ble_rcb_pool[0]; + UINT16 xx; + + for (xx = 0; xx < BLE_MAX_L2CAP_CLIENTS; xx++, p_rcb++) + { + if (!p_rcb->in_use) + { + p_rcb->in_use = TRUE; + p_rcb->psm = psm; +#if (L2CAP_UCD_INCLUDED == TRUE) + p_rcb->ucd.state = L2C_UCD_STATE_UNUSED; +#endif + return (p_rcb); + } + } + + /* If here, no free RCB found */ + return (NULL); +} +#endif ///BLE_INCLUDED == TRUE + +/******************************************************************************* +** +** Function l2cu_release_rcb +** +** Description Mark an RCB as no longet in use +** +** Returns void +** +*******************************************************************************/ +void l2cu_release_rcb (tL2C_RCB *p_rcb) +{ + p_rcb->in_use = FALSE; + p_rcb->psm = 0; +} + + +/******************************************************************************* +** +** Function l2cu_disconnect_chnl +** +** Description Disconnect a channel. Typically, this is due to either +** receiving a bad configuration, bad packet or max_retries expiring. +** +*******************************************************************************/ +void l2cu_disconnect_chnl (tL2C_CCB *p_ccb) +{ + UINT16 local_cid = p_ccb->local_cid; + + if (local_cid >= L2CAP_BASE_APPL_CID) { + tL2CA_DISCONNECT_IND_CB *p_disc_cb = p_ccb->p_rcb->api.pL2CA_DisconnectInd_Cb; + + L2CAP_TRACE_WARNING ("L2CAP - disconnect_chnl CID: 0x%04x", local_cid); + + l2cu_send_peer_disc_req (p_ccb); + + l2cu_release_ccb (p_ccb); + + (*p_disc_cb)(local_cid, FALSE); + } else { + /* failure on the AMP channel, probably need to disconnect ACL */ + L2CAP_TRACE_ERROR ("L2CAP - disconnect_chnl CID: 0x%04x Ignored", local_cid); + } +} + + +/******************************************************************************* +** +** Function l2cu_find_rcb_by_psm +** +** Description Look through the Registration Control Blocks to see if +** anyone registered to handle the PSM in question +** +** Returns Pointer to the RCB or NULL if not found +** +*******************************************************************************/ +tL2C_RCB *l2cu_find_rcb_by_psm (UINT16 psm) +{ + tL2C_RCB *p_rcb = &l2cb.rcb_pool[0]; + UINT16 xx; + + for (xx = 0; xx < MAX_L2CAP_CLIENTS; xx++, p_rcb++) { + if ((p_rcb->in_use) && (p_rcb->psm == psm)) { + return (p_rcb); + } + } + + /* If here, no match found */ + return (NULL); +} + +#if (BLE_INCLUDED == TRUE) +/******************************************************************************* +** +** Function l2cu_find_ble_rcb_by_psm +** +** Description Look through the BLE Registration Control Blocks to see if +** anyone registered to handle the PSM in question +** +** Returns Pointer to the BLE RCB or NULL if not found +** +*******************************************************************************/ +tL2C_RCB *l2cu_find_ble_rcb_by_psm (UINT16 psm) +{ + tL2C_RCB *p_rcb = &l2cb.ble_rcb_pool[0]; + UINT16 xx; + + for (xx = 0; xx < BLE_MAX_L2CAP_CLIENTS; xx++, p_rcb++) + { + if ((p_rcb->in_use) && (p_rcb->psm == psm)) { + return (p_rcb); + } + } + + /* If here, no match found */ + return (NULL); +} +#endif ///BLE_INCLUDED == TRUE + +#if (L2CAP_COC_INCLUDED == TRUE) +/******************************************************************************* +** +** Function l2cu_process_peer_cfg_req +** +** Description This function is called when the peer sends us a "config request" +** message. It extracts the configuration of interest and saves +** it in the CCB. +** +** Note: Negotiation of the FCR channel type is handled internally, +** all others are passed to the upper layer. +** +** Returns UINT8 - L2CAP_PEER_CFG_OK if passed to upper layer, +** L2CAP_PEER_CFG_UNACCEPTABLE if automatically responded to +** because parameters are unnacceptable from a specification +** point of view. +** L2CAP_PEER_CFG_DISCONNECT if no compatible channel modes +** between the two devices, and shall be closed. +** +*******************************************************************************/ +UINT8 l2cu_process_peer_cfg_req (tL2C_CCB *p_ccb, tL2CAP_CFG_INFO *p_cfg) +{ + BOOLEAN mtu_ok = TRUE; + BOOLEAN qos_type_ok = TRUE; + BOOLEAN flush_to_ok = TRUE; + BOOLEAN fcr_ok = TRUE; +#if (CLASSIC_BT_INCLUDED == TRUE) + UINT8 fcr_status; +#endif ///CLASSIC_BT_INCLUDED == TRUE + /* Ignore FCR parameters for basic mode */ + if (!p_cfg->fcr_present) { + p_cfg->fcr.mode = L2CAP_FCR_BASIC_MODE; + } + + /* Save the MTU that our peer can receive */ + if (p_cfg->mtu_present) { + /* Make sure MTU is at least the minimum */ + if (p_cfg->mtu >= L2CAP_MIN_MTU) { + /* In basic mode, limit the MTU to our buffer size */ + if ( (p_cfg->fcr_present == FALSE) && (p_cfg->mtu > L2CAP_MTU_SIZE) ) { + p_cfg->mtu = L2CAP_MTU_SIZE; + } + + /* Save the accepted value in case of renegotiation */ + p_ccb->peer_cfg.mtu = p_cfg->mtu; + p_ccb->peer_cfg.mtu_present = TRUE; + p_ccb->peer_cfg_bits |= L2CAP_CH_CFG_MASK_MTU; + } else { /* Illegal MTU value */ + p_cfg->mtu = L2CAP_MIN_MTU; + mtu_ok = FALSE; + } + } + /* Reload mtu from a previously accepted config request */ + else if (p_ccb->peer_cfg.mtu_present) { + p_cfg->mtu_present = TRUE; + p_cfg->mtu = p_ccb->peer_cfg.mtu; + } + + /* Verify that the flush timeout is a valid value (0 is illegal) */ + if (p_cfg->flush_to_present) { + if (!p_cfg->flush_to) { + p_cfg->flush_to = 0xFFFF; /* Infinite retransmissions (spec default) */ + flush_to_ok = FALSE; + } else { /* Save the accepted value in case of renegotiation */ + p_ccb->peer_cfg.flush_to_present = TRUE; + p_ccb->peer_cfg.flush_to = p_cfg->flush_to; + p_ccb->peer_cfg_bits |= L2CAP_CH_CFG_MASK_FLUSH_TO; + } + } + /* Reload flush_to from a previously accepted config request */ + else if (p_ccb->peer_cfg.flush_to_present) { + p_cfg->flush_to_present = TRUE; + p_cfg->flush_to = p_ccb->peer_cfg.flush_to; + } + + /* Save the QOS settings the the peer is using */ + if (p_cfg->qos_present) { + /* Make sure service type is not a reserved value; otherwise let upper + layer decide if acceptable + */ + if (p_cfg->qos.service_type <= GUARANTEED) { + p_ccb->peer_cfg.qos = p_cfg->qos; + p_ccb->peer_cfg.qos_present = TRUE; + p_ccb->peer_cfg_bits |= L2CAP_CH_CFG_MASK_QOS; + } else { /* Illegal service type value */ + p_cfg->qos.service_type = BEST_EFFORT; + qos_type_ok = FALSE; + } + } + /* Reload QOS from a previously accepted config request */ + else if (p_ccb->peer_cfg.qos_present) { + p_cfg->qos_present = TRUE; + p_cfg->qos = p_ccb->peer_cfg.qos; + } +#if (CLASSIC_BT_INCLUDED == TRUE) + if ((fcr_status = l2c_fcr_process_peer_cfg_req (p_ccb, p_cfg)) == L2CAP_PEER_CFG_DISCONNECT) { + /* Notify caller to disconnect the channel (incompatible modes) */ + p_cfg->result = L2CAP_CFG_FAILED_NO_REASON; + p_cfg->mtu_present = p_cfg->qos_present = p_cfg->flush_to_present = 0; + + return (L2CAP_PEER_CFG_DISCONNECT); + } + + fcr_ok = (fcr_status == L2CAP_PEER_CFG_OK); +#endif ///CLASSIC_BT_INCLUDED == TRUE + + /* Return any unacceptable parameters */ + if (mtu_ok && flush_to_ok && qos_type_ok && fcr_ok) { + l2cu_adjust_out_mps (p_ccb); + return (L2CAP_PEER_CFG_OK); + } else { + p_cfg->result = L2CAP_CFG_UNACCEPTABLE_PARAMS; + + if (mtu_ok) { + p_cfg->mtu_present = FALSE; + } + if (flush_to_ok) { + p_cfg->flush_to_present = FALSE; + } + if (qos_type_ok) { + p_cfg->qos_present = FALSE; + } + if (fcr_ok) { + p_cfg->fcr_present = FALSE; + } + + return (L2CAP_PEER_CFG_UNACCEPTABLE); + } +} + + +/******************************************************************************* +** +** Function l2cu_process_peer_cfg_rsp +** +** Description This function is called when the peer sends us a "config response" +** message. It extracts the configuration of interest and saves +** it in the CCB. +** +** Returns void +** +*******************************************************************************/ +void l2cu_process_peer_cfg_rsp (tL2C_CCB *p_ccb, tL2CAP_CFG_INFO *p_cfg) +{ + /* If we wanted QoS and the peer sends us a positive response with QoS, use his values */ + if ( (p_cfg->qos_present) && (p_ccb->our_cfg.qos_present) ) { + p_ccb->our_cfg.qos = p_cfg->qos; + } + + if (p_cfg->fcr_present) { + /* Save the retransmission and monitor timeout values */ + if (p_cfg->fcr.mode == L2CAP_FCR_ERTM_MODE) { + p_ccb->peer_cfg.fcr.rtrans_tout = p_cfg->fcr.rtrans_tout; + p_ccb->peer_cfg.fcr.mon_tout = p_cfg->fcr.mon_tout; + } + + /* Calculate the max number of packets for which we can delay sending an ack */ + if (p_cfg->fcr.tx_win_sz < p_ccb->our_cfg.fcr.tx_win_sz) { + p_ccb->fcrb.max_held_acks = p_cfg->fcr.tx_win_sz / 3; + } else { + p_ccb->fcrb.max_held_acks = p_ccb->our_cfg.fcr.tx_win_sz / 3; + } + + L2CAP_TRACE_DEBUG ("l2cu_process_peer_cfg_rsp(): peer tx_win_sz: %d, our tx_win_sz: %d, max_held_acks: %d", + p_cfg->fcr.tx_win_sz, p_ccb->our_cfg.fcr.tx_win_sz, p_ccb->fcrb.max_held_acks); + } +} + +/******************************************************************************* +** +** Function l2cu_process_our_cfg_req +** +** Description This function is called when we send a "config request" +** message. It extracts the configuration of interest and saves +** it in the CCB. +** +** Returns void +** +*******************************************************************************/ +void l2cu_process_our_cfg_req (tL2C_CCB *p_ccb, tL2CAP_CFG_INFO *p_cfg) +{ + tL2C_LCB *p_lcb; + UINT16 hci_flush_to; + + /* Save the QOS settings we are using for transmit */ + if (p_cfg->qos_present) { + p_ccb->our_cfg.qos_present = TRUE; + p_ccb->our_cfg.qos = p_cfg->qos; + } + + if (p_cfg->fcr_present) { + /* Override FCR options if attempting streaming or basic */ + if (p_cfg->fcr.mode == L2CAP_FCR_BASIC_MODE) { + memset(&p_cfg->fcr, 0, sizeof(tL2CAP_FCR_OPTS)); + } else { + /* On BR/EDR, timer values are zero in config request */ + /* On class 2 AMP, timer value in config request shall be non-0 processing time */ + /* timer value in config response shall be greater than received processing time */ + p_cfg->fcr.mon_tout = p_cfg->fcr.rtrans_tout = 0; + + if (p_cfg->fcr.mode == L2CAP_FCR_STREAM_MODE) { + p_cfg->fcr.max_transmit = p_cfg->fcr.tx_win_sz = 0; + } + } + + /* Set the threshold to send acks (may be updated in the cfg response) */ + p_ccb->fcrb.max_held_acks = p_cfg->fcr.tx_win_sz / 3; + + /* Include FCS option only if peer can handle it */ + if (p_ccb->p_lcb->peer_ext_fea & L2CAP_EXTFEA_NO_CRC) { + /* FCS check can be bypassed if peer also desires to bypass */ + if (p_cfg->fcs_present && p_cfg->fcs == L2CAP_CFG_FCS_BYPASS) { + p_ccb->bypass_fcs |= L2CAP_CFG_FCS_OUR; + } + } else { + p_cfg->fcs_present = FALSE; + } + } else { + p_cfg->fcr.mode = L2CAP_FCR_BASIC_MODE; + } + + p_ccb->our_cfg.fcr.mode = p_cfg->fcr.mode; + p_ccb->our_cfg.fcr_present = p_cfg->fcr_present; + + /* Check the flush timeout. If it is lower than the current one used */ + /* then we need to adjust the flush timeout sent to the controller */ + if (p_cfg->flush_to_present) { + if ((p_cfg->flush_to == 0) || (p_cfg->flush_to == L2CAP_NO_AUTOMATIC_FLUSH)) { + /* don't send invalid flush timeout */ + /* SPEC: The sender of the Request shall specify its flush timeout value */ + /* if it differs from the default value of 0xFFFF */ + p_cfg->flush_to_present = FALSE; + } else { + p_ccb->our_cfg.flush_to = p_cfg->flush_to; + p_lcb = p_ccb->p_lcb; + + if (p_cfg->flush_to < p_lcb->link_flush_tout) { + p_lcb->link_flush_tout = p_cfg->flush_to; + + /* If the timeout is within range of HCI, set the flush timeout */ + if (p_cfg->flush_to <= ((HCI_MAX_AUTO_FLUSH_TOUT * 5) / 8)) { + /* Convert flush timeout to 0.625 ms units, with round */ + hci_flush_to = ((p_cfg->flush_to * 8) + 3) / 5; + btsnd_hcic_write_auto_flush_tout (p_lcb->handle, hci_flush_to); + } + } + } + } +} + + +/******************************************************************************* +** +** Function l2cu_process_our_cfg_rsp +** +** Description This function is called when we send the peer a "config response" +** message. It extracts the configuration of interest and saves +** it in the CCB. +** +** Returns void +** +*******************************************************************************/ +void l2cu_process_our_cfg_rsp (tL2C_CCB *p_ccb, tL2CAP_CFG_INFO *p_cfg) +{ + /* If peer wants QoS, we are allowed to change the values in a positive response */ + if ( (p_cfg->qos_present) && (p_ccb->peer_cfg.qos_present) ) { + p_ccb->peer_cfg.qos = p_cfg->qos; + } else { + p_cfg->qos_present = FALSE; + } + + l2c_fcr_adj_our_rsp_options (p_ccb, p_cfg); +} +#endif // (L2CAP_COC_INCLUDED == TRUE) + + +/******************************************************************************* +** +** Function l2cu_device_reset +** +** Description This function is called when reset of the device is +** completed. For all active connection simulate HCI_DISC +** +** Returns void +** +*******************************************************************************/ +void l2cu_device_reset (void) +{ + list_node_t *p_node = NULL; + tL2C_LCB *p_lcb = NULL; + for (p_node = list_begin(l2cb.p_lcb_pool); p_node; p_node = list_next(p_node)) { + p_lcb = list_node(p_node); + if ((p_lcb->in_use) && (p_lcb->handle != HCI_INVALID_HANDLE)) { + l2c_link_hci_disc_comp (p_lcb->handle, (UINT8) - 1); + } + } +#if (BLE_INCLUDED == TRUE) + l2cb.is_ble_connecting = FALSE; +#endif +} + +/******************************************************************************* +** +** Function l2cu_create_conn +** +** Description This function initiates an acl connection via HCI +** +** Returns TRUE if successful, FALSE if gki get buffer fails. +** +*******************************************************************************/ +BOOLEAN l2cu_create_conn (tL2C_LCB *p_lcb, tBT_TRANSPORT transport) +{ +#if BTM_SCO_INCLUDED == TRUE + BOOLEAN is_sco_active; +#endif + list_node_t *p_node = NULL; + tL2C_LCB *p_lcb_cur = NULL; + +#if (BLE_INCLUDED == TRUE) + tBT_DEVICE_TYPE dev_type; + tBLE_ADDR_TYPE addr_type = p_lcb->open_addr_type; + if(addr_type == BLE_ADDR_UNKNOWN_TYPE) { + BTM_ReadDevInfo(p_lcb->remote_bd_addr, &dev_type, &addr_type); + } + + if (transport == BT_TRANSPORT_LE) { + if (!controller_get_interface()->supports_ble()) { + return FALSE; + } + if(addr_type > BLE_ADDR_TYPE_MAX) { + addr_type = BLE_ADDR_PUBLIC; + } + p_lcb->ble_addr_type = addr_type; + p_lcb->transport = BT_TRANSPORT_LE; + + return (l2cble_create_conn(p_lcb)); + } +#endif + + /* If there is a connection where we perform as a slave, try to switch roles + for this connection */ + for (p_node = list_begin(l2cb.p_lcb_pool); p_node; p_node = list_next(p_node)) { + p_lcb_cur = list_node(p_node); + if (p_lcb_cur == p_lcb) { + continue; + } + + if ((p_lcb_cur->in_use) && (p_lcb_cur->link_role == HCI_ROLE_SLAVE)) { + +#if BTM_SCO_INCLUDED == TRUE + /* The LMP_switch_req shall be sent only if the ACL logical transport + is in active mode, when encryption is disabled, and all synchronous + logical transports on the same physical link are disabled." */ + + /* Check if there is any SCO Active on this BD Address */ + is_sco_active = btm_is_sco_active_by_bdaddr(p_lcb_cur->remote_bd_addr); + + L2CAP_TRACE_API ("l2cu_create_conn - btm_is_sco_active_by_bdaddr() is_sco_active = %s", \ + (is_sco_active == TRUE) ? "TRUE" : "FALSE"); + + if (is_sco_active == TRUE) { + continue; /* No Master Slave switch not allowed when SCO Active */ + } +#endif + /*4_1_TODO check if btm_cb.devcb.local_features to be used instead */ + if (HCI_SWITCH_SUPPORTED(BTM_ReadLocalFeatures())) { + /* mark this lcb waiting for switch to be completed and + start switch on the other one */ + p_lcb->link_state = LST_CONNECTING_WAIT_SWITCH; + p_lcb->link_role = HCI_ROLE_MASTER; + + if (BTM_SwitchRole (p_lcb_cur->remote_bd_addr, HCI_ROLE_MASTER, NULL) == BTM_CMD_STARTED) { + btu_start_timer (&p_lcb->timer_entry, BTU_TTYPE_L2CAP_LINK, L2CAP_LINK_ROLE_SWITCH_TOUT); + return (TRUE); + } + } + } + } + + p_lcb->link_state = LST_CONNECTING; + + return (l2cu_create_conn_after_switch (p_lcb)); +} + +/******************************************************************************* +** +** Function l2cu_get_num_hi_priority +** +** Description Gets the number of high priority channels. +** +** Returns +** +*******************************************************************************/ +UINT8 l2cu_get_num_hi_priority (void) +{ + UINT8 no_hi = 0; + list_node_t *p_node = NULL; + tL2C_LCB *p_lcb = NULL; + + for (p_node = list_begin(l2cb.p_lcb_pool); p_node; p_node = list_next(p_node)) { + p_lcb = list_node(p_node); + if ((p_lcb->in_use) && (p_lcb->acl_priority == L2CAP_PRIORITY_HIGH)) { + no_hi++; + } + } + return no_hi; +} + + +/******************************************************************************* +** +** Function l2cu_create_conn_after_switch +** +** Description This function initiates an acl connection via HCI +** If switch required to create connection it is already done. +** +** Returns TRUE if successful, FALSE if osi get buffer fails. +** +*******************************************************************************/ + +BOOLEAN l2cu_create_conn_after_switch (tL2C_LCB *p_lcb) +{ + UINT8 allow_switch = HCI_CR_CONN_ALLOW_SWITCH; + tBTM_INQ_INFO *p_inq_info; + UINT8 page_scan_rep_mode; + UINT8 page_scan_mode; + UINT16 clock_offset; + UINT8 *p_features; + UINT16 num_acl = BTM_GetNumAclLinks(); + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev (p_lcb->remote_bd_addr); + UINT8 no_hi_prio_chs = l2cu_get_num_hi_priority(); + + p_features = BTM_ReadLocalFeatures(); + + L2CAP_TRACE_DEBUG ("l2cu_create_conn_after_switch :%d num_acl:%d no_hi: %d is_bonding:%d", + l2cb.disallow_switch, num_acl, no_hi_prio_chs, p_lcb->is_bonding); + /* FW team says that we can participant in 4 piconets + * typically 3 piconet + 1 for scanning. + * We can enhance the code to count the number of piconets later. */ + if ( ((!l2cb.disallow_switch && (num_acl < 3)) || (p_lcb->is_bonding && (no_hi_prio_chs == 0))) + && HCI_SWITCH_SUPPORTED(p_features)) { + allow_switch = HCI_CR_CONN_ALLOW_SWITCH; + } else { + allow_switch = HCI_CR_CONN_NOT_ALLOW_SWITCH; + } + + p_lcb->link_state = LST_CONNECTING; + + /* Check with the BT manager if details about remote device are known */ + if ((p_inq_info = BTM_InqDbRead(p_lcb->remote_bd_addr)) != NULL) { + page_scan_rep_mode = p_inq_info->results.page_scan_rep_mode; + page_scan_mode = p_inq_info->results.page_scan_mode; + clock_offset = (UINT16)(p_inq_info->results.clock_offset); + } else { + /* No info known. Use default settings */ + page_scan_rep_mode = HCI_PAGE_SCAN_REP_MODE_R2; + page_scan_mode = HCI_MANDATARY_PAGE_SCAN_MODE; + + clock_offset = (p_dev_rec) ? p_dev_rec->clock_offset : 0; + } + + if (!btsnd_hcic_create_conn (p_lcb->remote_bd_addr, + ( HCI_PKT_TYPES_MASK_DM1 | HCI_PKT_TYPES_MASK_DH1 + | HCI_PKT_TYPES_MASK_DM3 | HCI_PKT_TYPES_MASK_DH3 + | HCI_PKT_TYPES_MASK_DM5 | HCI_PKT_TYPES_MASK_DH5 ), + page_scan_rep_mode, + page_scan_mode, + clock_offset, + allow_switch)) + + { + L2CAP_TRACE_ERROR ("L2CAP - no buffer for l2cu_create_conn"); + l2cu_release_lcb (p_lcb); + return (FALSE); + } + + btm_acl_update_busy_level (BTM_BLI_PAGE_EVT); + + btu_start_timer (&p_lcb->timer_entry, BTU_TTYPE_L2CAP_LINK, + L2CAP_LINK_CONNECT_TOUT); + + return (TRUE); +} + + +/******************************************************************************* +** +** Function l2cu_find_lcb_by_state +** +** Description Look through all active LCBs for a match based on the +** LCB state. +** +** Returns pointer to first matched LCB, or NULL if no match +** +*******************************************************************************/ +tL2C_LCB *l2cu_find_lcb_by_state (tL2C_LINK_STATE state) +{ + list_node_t *p_node = NULL; + tL2C_LCB *p_lcb = NULL; + + for (p_node = list_begin(l2cb.p_lcb_pool); p_node; p_node = list_next(p_node)) { + p_lcb = list_node(p_node); + if ((p_lcb->in_use) && (p_lcb->link_state == state)) { + return (p_lcb); + } + } + + /* If here, no match found */ + return (NULL); +} + + +/******************************************************************************* +** +** Function l2cu_lcb_disconnecting +** +** Description On each active lcb, check if the lcb is in disconnecting +** state, or if there are no ccb's on the lcb (implying + idle timeout is running), or if last ccb on the link + is in disconnecting state. +** +** Returns TRUE if any of above conditions met, FALSE otherwise +** +*******************************************************************************/ +BOOLEAN l2cu_lcb_disconnecting (void) +{ + tL2C_LCB *p_lcb; + tL2C_CCB *p_ccb; + BOOLEAN status = FALSE; + list_node_t *p_node = NULL; + + for (p_node = list_begin(l2cb.p_lcb_pool); p_node; p_node = list_next(p_node)) { + p_lcb = list_node(p_node); + if (p_lcb->in_use) { + /* no ccbs on lcb, or lcb is in disconnecting state */ + if ((!p_lcb->ccb_queue.p_first_ccb) || (p_lcb->link_state == LST_DISCONNECTING)) { + status = TRUE; + break; + } + /* only one ccb left on lcb */ + else if (p_lcb->ccb_queue.p_first_ccb == p_lcb->ccb_queue.p_last_ccb) { + p_ccb = p_lcb->ccb_queue.p_first_ccb; + + if ((p_ccb->in_use) && + ((p_ccb->chnl_state == CST_W4_L2CAP_DISCONNECT_RSP) || + (p_ccb->chnl_state == CST_W4_L2CA_DISCONNECT_RSP))) { + status = TRUE; + break; + } + } + } + } + return status; +} + + +/******************************************************************************* +** +** Function l2cu_set_acl_priority +** +** Description Sets the transmission priority for a channel. +** (For initial implementation only two values are valid. +** L2CAP_PRIORITY_NORMAL and L2CAP_PRIORITY_HIGH). +** +** Returns TRUE if a valid channel, else FALSE +** +*******************************************************************************/ + +BOOLEAN l2cu_set_acl_priority (BD_ADDR bd_addr, UINT8 priority, BOOLEAN reset_after_rs) +{ + tL2C_LCB *p_lcb; + UINT8 *pp; + UINT8 command[HCI_BRCM_ACL_PRIORITY_PARAM_SIZE]; + UINT8 vs_param; + + //APPL_TRACE_EVENT("SET ACL PRIORITY %d", priority); + + /* Find the link control block for the acl channel */ + if ((p_lcb = l2cu_find_lcb_by_bd_addr(bd_addr, BT_TRANSPORT_BR_EDR)) == NULL) { + L2CAP_TRACE_WARNING ("L2CAP - no LCB for L2CA_SetAclPriority"); + return (FALSE); + } + + if (BTM_IS_BRCM_CONTROLLER()) { + /* Called from above L2CAP through API; send VSC if changed */ + if ((!reset_after_rs && (priority != p_lcb->acl_priority)) || + /* Called because of a master/slave role switch; if high resend VSC */ + ( reset_after_rs && p_lcb->acl_priority == L2CAP_PRIORITY_HIGH)) { + pp = command; + + vs_param = (priority == L2CAP_PRIORITY_HIGH) ? HCI_BRCM_ACL_PRIORITY_HIGH : HCI_BRCM_ACL_PRIORITY_LOW; + + UINT16_TO_STREAM (pp, p_lcb->handle); + UINT8_TO_STREAM (pp, vs_param); + + BTM_VendorSpecificCommand (HCI_BRCM_SET_ACL_PRIORITY, HCI_BRCM_ACL_PRIORITY_PARAM_SIZE, command, NULL); + + /* Adjust lmp buffer allocation for this channel if priority changed */ + if (p_lcb->acl_priority != priority) { + p_lcb->acl_priority = priority; + l2c_link_adjust_allocation(); + } + } + } + return (TRUE); +} + +#if (L2CAP_NON_FLUSHABLE_PB_INCLUDED == TRUE) +/****************************************************************************** +** +** Function l2cu_set_non_flushable_pbf +** +** Description set L2CAP_PKT_START_NON_FLUSHABLE if controller supoorts +** +** Returns void +** +*******************************************************************************/ +void l2cu_set_non_flushable_pbf (BOOLEAN is_supported) +{ + if (is_supported) { + l2cb.non_flushable_pbf = (L2CAP_PKT_START_NON_FLUSHABLE << L2CAP_PKT_TYPE_SHIFT); + } else { + l2cb.non_flushable_pbf = (L2CAP_PKT_START << L2CAP_PKT_TYPE_SHIFT); + } +} +#endif + +/******************************************************************************* +** +** Function l2cu_resubmit_pending_sec_req +** +** Description This function is called when required security procedures +** are completed and any pending requests can be re-submitted. +** +** Returns void +** +*******************************************************************************/ +#if (CLASSIC_BT_INCLUDED == TRUE) +void l2cu_resubmit_pending_sec_req (BD_ADDR p_bda) +{ + tL2C_LCB *p_lcb; + tL2C_CCB *p_ccb; + tL2C_CCB *p_next_ccb; + L2CAP_TRACE_DEBUG ("l2cu_resubmit_pending_sec_req p_bda: %p", p_bda); + list_node_t *p_node = NULL; + + /* If we are called with a BDA, only resubmit for that BDA */ + if (p_bda) { + p_lcb = l2cu_find_lcb_by_bd_addr (p_bda, BT_TRANSPORT_BR_EDR); + /* If we don't have one, this is an error */ + if (p_lcb) { + /* For all channels, send the event through their FSMs */ + for (p_ccb = p_lcb->ccb_queue.p_first_ccb; p_ccb; p_ccb = p_next_ccb) { + p_next_ccb = p_ccb->p_next_ccb; + l2c_csm_execute (p_ccb, L2CEVT_SEC_RE_SEND_CMD, NULL); + } + } else { + L2CAP_TRACE_WARNING ("l2cu_resubmit_pending_sec_req - unknown BD_ADDR"); + } + } else { + /* No BDA pasesed in, so check all links */ + for (p_node = list_begin(l2cb.p_lcb_pool); p_node; p_node = list_next(p_node)) { + p_lcb = list_node(p_node); + if (p_lcb->in_use) { + /* For all channels, send the event through their FSMs */ + for (p_ccb = p_lcb->ccb_queue.p_first_ccb; p_ccb; p_ccb = p_next_ccb) { + p_next_ccb = p_ccb->p_next_ccb; + l2c_csm_execute (p_ccb, L2CEVT_SEC_RE_SEND_CMD, NULL); + } + } + } + } +} +#endif ///CLASSIC_BT_INCLUDED == TRUE + +#if L2CAP_CONFORMANCE_TESTING == TRUE +/******************************************************************************* +** +** Function l2cu_set_info_rsp_mask +** +** Description This function allows the script wrapper to change the +** info resp mask for conformance testing. +** +** Returns pointer to CCB, or NULL if none +** +*******************************************************************************/ +void l2cu_set_info_rsp_mask (UINT32 mask) +{ + l2cb.test_info_resp = mask; +} +#endif /* L2CAP_CONFORMANCE_TESTING */ + +/******************************************************************************* +** +** Function l2cu_adjust_out_mps +** +** Description Sets our MPS based on current controller capabilities +** +** Returns void +** +*******************************************************************************/ +void l2cu_adjust_out_mps (tL2C_CCB *p_ccb) +{ + UINT16 packet_size; + + /* on the tx side MTU is selected based on packet size of the controller */ + packet_size = btm_get_max_packet_size (p_ccb->p_lcb->remote_bd_addr); + + if (packet_size <= (L2CAP_PKT_OVERHEAD + L2CAP_FCR_OVERHEAD + L2CAP_SDU_LEN_OVERHEAD + L2CAP_FCS_LEN)) { + /* something is very wrong */ + L2CAP_TRACE_DEBUG ("l2cu_adjust_out_mps bad packet size: %u will use MPS: %u", packet_size, p_ccb->peer_cfg.fcr.mps); + p_ccb->tx_mps = p_ccb->peer_cfg.fcr.mps; + } else { + packet_size -= (L2CAP_PKT_OVERHEAD + L2CAP_FCR_OVERHEAD + L2CAP_SDU_LEN_OVERHEAD + L2CAP_FCS_LEN); + + /* We try to negotiate MTU that each packet can be split into whole + number of max packets. For example if link is 1.2 max packet size is 339 bytes. + At first calculate how many whole packets it is. MAX L2CAP is 1691 + 4 overhead. + 1695, that will be 5 Dh5 packets. Now maximum L2CAP packet is + 5 * 339 = 1695. Minus 4 bytes L2CAP header 1691. + + For EDR 2.0 packet size is 1027. So we better send RFCOMM packet as 1 3DH5 packet + 1 * 1027 = 1027. Minus 4 bytes L2CAP header 1023. */ + if (p_ccb->peer_cfg.fcr.mps >= packet_size) { + p_ccb->tx_mps = p_ccb->peer_cfg.fcr.mps / packet_size * packet_size; + } else { + p_ccb->tx_mps = p_ccb->peer_cfg.fcr.mps; + } + + L2CAP_TRACE_DEBUG ("l2cu_adjust_out_mps use %d Based on peer_cfg.fcr.mps: %u packet_size: %u", + p_ccb->tx_mps, p_ccb->peer_cfg.fcr.mps, packet_size); + } +} + + +/******************************************************************************* +** +** Function l2cu_initialize_fixed_ccb +** +** Description Initialize a fixed channel's CCB +** +** Returns TRUE or FALSE +** +*******************************************************************************/ +BOOLEAN l2cu_initialize_fixed_ccb (tL2C_LCB *p_lcb, UINT16 fixed_cid, tL2CAP_FCR_OPTS *p_fcr) +{ +#if (L2CAP_NUM_FIXED_CHNLS > 0) + tL2C_CCB *p_ccb; + /* If we already have a CCB, then simply return */ + if (p_lcb->p_fixed_ccbs[fixed_cid - L2CAP_FIRST_FIXED_CHNL] != NULL) { + return (TRUE); + } + + if ((p_ccb = l2cu_allocate_ccb (NULL, 0)) == NULL) { + return (FALSE); + } + + btu_stop_timer(&p_lcb->timer_entry); + + /* Set CID for the connection */ + p_ccb->local_cid = fixed_cid; + p_ccb->remote_cid = fixed_cid; + + p_ccb->is_flushable = FALSE; + + p_ccb->timer_entry.param = (TIMER_PARAM_TYPE)p_ccb; + + + if (p_fcr) { + /* Set the FCR parameters. For now, we will use default pools */ + p_ccb->our_cfg.fcr = p_ccb->peer_cfg.fcr = *p_fcr; + + p_ccb->ertm_info.fcr_rx_buf_size = L2CAP_FCR_RX_BUF_SIZE; + p_ccb->ertm_info.fcr_tx_buf_size = L2CAP_FCR_TX_BUF_SIZE; + p_ccb->ertm_info.user_rx_buf_size = L2CAP_USER_RX_BUF_SIZE; + p_ccb->ertm_info.user_tx_buf_size = L2CAP_USER_TX_BUF_SIZE; + + p_ccb->fcrb.max_held_acks = p_fcr->tx_win_sz / 3; + } + + /* Link ccb to lcb and lcb to ccb */ + p_lcb->p_fixed_ccbs[fixed_cid - L2CAP_FIRST_FIXED_CHNL] = p_ccb; + p_ccb->p_lcb = p_lcb; + + /* There is no configuration, so if the link is up, the channel is up */ + if (p_lcb->link_state == LST_CONNECTED) { + p_ccb->chnl_state = CST_OPEN; + } + + /* Set the default idle timeout value to use */ + p_ccb->fixed_chnl_idle_tout = l2cb.fixed_reg[fixed_cid - L2CAP_FIRST_FIXED_CHNL].default_idle_tout; +#endif + return (TRUE); +} + +/******************************************************************************* +** +** Function l2cu_no_dynamic_ccbs +** +** Description Handles the case when there are no more dynamic CCBs. If there +** are any fixed CCBs, start the longest of the fixed CCB timeouts, +** otherwise start the default link idle timeout or disconnect. +** +** Returns void +** +*******************************************************************************/ +void l2cu_no_dynamic_ccbs (tL2C_LCB *p_lcb) +{ +#if (SMP_INCLUDED == TRUE) + tBTM_STATUS rc; +#endif ///SMP_INCLUDED == TRUE + UINT16 timeout = p_lcb->idle_timeout; + +#if (L2CAP_NUM_FIXED_CHNLS > 0) + int xx; + + for (xx = 0; xx < L2CAP_NUM_FIXED_CHNLS; xx++) { + if ( (p_lcb->p_fixed_ccbs[xx] != NULL) && (p_lcb->p_fixed_ccbs[xx]->fixed_chnl_idle_tout > timeout) ) { + timeout = p_lcb->p_fixed_ccbs[xx]->fixed_chnl_idle_tout; + } + } +#endif + + /* If the link is pairing, do not mess with the timeouts */ + if (p_lcb->is_bonding) { + return; + } + + if (timeout == 0) { + L2CAP_TRACE_DEBUG ("l2cu_no_dynamic_ccbs() IDLE timer 0, disconnecting link"); +#if (SMP_INCLUDED == TRUE) + rc = btm_sec_disconnect (p_lcb->handle, HCI_ERR_PEER_USER); + if (rc == BTM_CMD_STARTED) { + l2cu_process_fixed_disc_cback(p_lcb); + p_lcb->link_state = LST_DISCONNECTING; + timeout = L2CAP_LINK_DISCONNECT_TOUT; + } else if (rc == BTM_SUCCESS) { + l2cu_process_fixed_disc_cback(p_lcb); + /* BTM SEC will make sure that link is release (probably after pairing is done) */ + p_lcb->link_state = LST_DISCONNECTING; + timeout = 0xFFFF; + } else if ( (p_lcb->is_bonding) + && (btsnd_hcic_disconnect (p_lcb->handle, HCI_ERR_PEER_USER)) ) { + l2cu_process_fixed_disc_cback(p_lcb); + p_lcb->link_state = LST_DISCONNECTING; + timeout = L2CAP_LINK_DISCONNECT_TOUT; + } else { + /* probably no buffer to send disconnect */ + timeout = BT_1SEC_TIMEOUT; + } +#else + if (btsnd_hcic_disconnect (p_lcb->handle, HCI_ERR_PEER_USER)) { + l2cu_process_fixed_disc_cback(p_lcb); + p_lcb->link_state = LST_DISCONNECTING; + timeout = L2CAP_LINK_DISCONNECT_TOUT; + } else { + timeout = BT_1SEC_TIMEOUT; + } +#endif ///SMP_INCLUDED == TRUE + + } + + if (timeout != 0xFFFF) { + L2CAP_TRACE_DEBUG ("l2cu_no_dynamic_ccbs() starting IDLE timeout: %d", timeout); + btu_start_timer (&p_lcb->timer_entry, BTU_TTYPE_L2CAP_LINK, timeout); + } else { + btu_stop_timer(&p_lcb->timer_entry); + } +} + +#if (L2CAP_NUM_FIXED_CHNLS > 0) +/******************************************************************************* +** +** Function l2cu_process_fixed_chnl_resp +** +** Description handle a fixed channel response (or lack thereof) +** if the link failed, or a fixed channel response was +** not received, the bitfield is all zeros. +** +*******************************************************************************/ +void l2cu_process_fixed_chnl_resp (tL2C_LCB *p_lcb) +{ + L2CAP_TRACE_DEBUG("%s",__func__); +#if (BLE_INCLUDED == TRUE) + if (p_lcb->transport == BT_TRANSPORT_BR_EDR) { + /* ignore all not assigned BR/EDR channels */ + p_lcb->peer_chnl_mask[0] &= (L2CAP_FIXED_CHNL_SIG_BIT | \ + L2CAP_FIXED_CHNL_CNCTLESS_BIT | \ + L2CAP_FIXED_CHNL_SMP_BR_BIT); + } else { + p_lcb->peer_chnl_mask[0] = l2cb.l2c_ble_fixed_chnls_mask; + } +#endif + + /* Tell all registered fixed channels about the connection */ + for (int xx = 0; xx < L2CAP_NUM_FIXED_CHNLS; xx++) { +#if BLE_INCLUDED == TRUE + /* skip sending LE fix channel callbacks on BR/EDR links */ + if (p_lcb->transport == BT_TRANSPORT_BR_EDR && + xx + L2CAP_FIRST_FIXED_CHNL >= L2CAP_ATT_CID && + xx + L2CAP_FIRST_FIXED_CHNL <= L2CAP_SMP_CID) { + continue; + } +#endif + if (l2cb.fixed_reg[xx].pL2CA_FixedConn_Cb != NULL) { + if (p_lcb->peer_chnl_mask[(xx + L2CAP_FIRST_FIXED_CHNL) / 8] + & (1 << ((xx + L2CAP_FIRST_FIXED_CHNL) % 8))) { + if (p_lcb->p_fixed_ccbs[xx]) { + p_lcb->p_fixed_ccbs[xx]->chnl_state = CST_OPEN; + } +#if BLE_INCLUDED == TRUE + (*l2cb.fixed_reg[xx].pL2CA_FixedConn_Cb)(xx + L2CAP_FIRST_FIXED_CHNL, + p_lcb->remote_bd_addr, TRUE, 0, p_lcb->transport); +#else + (*l2cb.fixed_reg[xx].pL2CA_FixedConn_Cb)(xx + L2CAP_FIRST_FIXED_CHNL, + p_lcb->remote_bd_addr, TRUE, 0, BT_TRANSPORT_BR_EDR); +#endif + } else { +#if BLE_INCLUDED == TRUE + (*l2cb.fixed_reg[xx].pL2CA_FixedConn_Cb)(xx + L2CAP_FIRST_FIXED_CHNL, + p_lcb->remote_bd_addr, FALSE, p_lcb->disc_reason, p_lcb->transport); +#else + (*l2cb.fixed_reg[xx].pL2CA_FixedConn_Cb)(xx + L2CAP_FIRST_FIXED_CHNL, + p_lcb->remote_bd_addr, FALSE, p_lcb->disc_reason, BT_TRANSPORT_BR_EDR); +#endif + + if (p_lcb->p_fixed_ccbs[xx]) { + l2cu_release_ccb (p_lcb->p_fixed_ccbs[xx]); + p_lcb->p_fixed_ccbs[xx] = NULL; + } + } + } + } +} +#endif + + +/******************************************************************************* +** +** Function l2cu_process_fixed_disc_cback +** +** Description send l2cap fixed channel disconnection callback to application +** +** +** Returns void +** +*******************************************************************************/ +void l2cu_process_fixed_disc_cback (tL2C_LCB *p_lcb) +{ +#if (L2CAP_NUM_FIXED_CHNLS > 0) + + /* Select peer channels mask to use depending on transport */ + UINT8 peer_channel_mask = p_lcb->peer_chnl_mask[0]; + + // For LE, reset the stored peer channel mask + if (p_lcb->transport == BT_TRANSPORT_LE) { + p_lcb->peer_chnl_mask[0] = 0; + } + + for (int xx = 0; xx < L2CAP_NUM_FIXED_CHNLS; xx++) { + if (p_lcb->p_fixed_ccbs[xx]) { + if (p_lcb->p_fixed_ccbs[xx] != p_lcb->p_pending_ccb) { + tL2C_CCB *p_l2c_chnl_ctrl_block; + p_l2c_chnl_ctrl_block = p_lcb->p_fixed_ccbs[xx]; + p_lcb->p_fixed_ccbs[xx] = NULL; + l2cu_release_ccb(p_l2c_chnl_ctrl_block); +#if BLE_INCLUDED == TRUE + (*l2cb.fixed_reg[xx].pL2CA_FixedConn_Cb)(xx + L2CAP_FIRST_FIXED_CHNL, + p_lcb->remote_bd_addr, FALSE, p_lcb->disc_reason, p_lcb->transport); +#else + (*l2cb.fixed_reg[xx].pL2CA_FixedConn_Cb)(xx + L2CAP_FIRST_FIXED_CHNL, + p_lcb->remote_bd_addr, FALSE, p_lcb->disc_reason, BT_TRANSPORT_BR_EDR); +#endif + } + } else if ( (peer_channel_mask & (1 << (xx + L2CAP_FIRST_FIXED_CHNL))) + && (l2cb.fixed_reg[xx].pL2CA_FixedConn_Cb != NULL) ) { +#if BLE_INCLUDED == TRUE + (*l2cb.fixed_reg[xx].pL2CA_FixedConn_Cb)(xx + L2CAP_FIRST_FIXED_CHNL, + p_lcb->remote_bd_addr, FALSE, p_lcb->disc_reason, p_lcb->transport); +#else + (*l2cb.fixed_reg[xx].pL2CA_FixedConn_Cb)(xx + L2CAP_FIRST_FIXED_CHNL, + p_lcb->remote_bd_addr, FALSE, p_lcb->disc_reason, BT_TRANSPORT_BR_EDR); +#endif + } + } +#endif +} + +#if (BLE_INCLUDED == TRUE) +/******************************************************************************* +** +** Function l2cu_send_peer_ble_par_req +** +** Description Build and send a BLE parameter update request message +** to the peer. +** +** Returns void +** +*******************************************************************************/ +void l2cu_send_peer_ble_par_req (tL2C_LCB *p_lcb, UINT16 min_int, UINT16 max_int, + UINT16 latency, UINT16 timeout) +{ + BT_HDR *p_buf; + UINT8 *p; + + /* Create an identifier for this packet */ + p_lcb->id++; + l2cu_adj_id (p_lcb, L2CAP_ADJ_ID); + + if ((p_buf = l2cu_build_header (p_lcb, L2CAP_CMD_BLE_UPD_REQ_LEN, + L2CAP_CMD_BLE_UPDATE_REQ, p_lcb->id)) == NULL ) { + L2CAP_TRACE_WARNING ("l2cu_send_peer_ble_par_req - no buffer"); + return; + } + + p = (UINT8 *)(p_buf + 1) + L2CAP_SEND_CMD_OFFSET + HCI_DATA_PREAMBLE_SIZE + + L2CAP_PKT_OVERHEAD + L2CAP_CMD_OVERHEAD; + + UINT16_TO_STREAM (p, min_int); + UINT16_TO_STREAM (p, max_int); + UINT16_TO_STREAM (p, latency); + UINT16_TO_STREAM (p, timeout); + + l2c_link_check_send_pkts (p_lcb, NULL, p_buf); +} + +/******************************************************************************* +** +** Function l2cu_send_peer_ble_par_rsp +** +** Description Build and send a BLE parameter update response message +** to the peer. +** +** Returns void +** +*******************************************************************************/ +void l2cu_send_peer_ble_par_rsp (tL2C_LCB *p_lcb, UINT16 reason, UINT8 rem_id) +{ + BT_HDR *p_buf; + UINT8 *p; + + if ((p_buf = l2cu_build_header (p_lcb, L2CAP_CMD_BLE_UPD_RSP_LEN, + L2CAP_CMD_BLE_UPDATE_RSP, rem_id)) == NULL ) { + L2CAP_TRACE_WARNING ("l2cu_send_peer_ble_par_rsp - no buffer"); + return; + } + + p = (UINT8 *)(p_buf + 1) + L2CAP_SEND_CMD_OFFSET + HCI_DATA_PREAMBLE_SIZE + + L2CAP_PKT_OVERHEAD + L2CAP_CMD_OVERHEAD; + + UINT16_TO_STREAM (p, reason); + + l2c_link_check_send_pkts (p_lcb, NULL, p_buf); +} + +/******************************************************************************* +** +** Function l2cu_send_peer_ble_credit_based_conn_req +** +** Description Build and send a BLE packet to establish LE connection oriented +** L2CAP channel. +** +** Returns void +** +*******************************************************************************/ +void l2cu_send_peer_ble_credit_based_conn_req (tL2C_CCB *p_ccb) +{ + BT_HDR *p_buf; + UINT8 *p; + tL2C_LCB *p_lcb = NULL; + UINT16 mtu; + UINT16 mps; + UINT16 initial_credit; + + if (!p_ccb) { + return; + } + p_lcb = p_ccb->p_lcb; + + /* Create an identifier for this packet */ + p_ccb->p_lcb->id++; + l2cu_adj_id(p_ccb->p_lcb, L2CAP_ADJ_ID); + + p_ccb->local_id = p_ccb->p_lcb->id; + + if ((p_buf = l2cu_build_header (p_lcb, L2CAP_CMD_BLE_CREDIT_BASED_CONN_REQ_LEN, + L2CAP_CMD_BLE_CREDIT_BASED_CONN_REQ, p_lcb->id)) == NULL ) + { + L2CAP_TRACE_WARNING ("l2cu_send_peer_ble_credit_based_conn_req - no buffer"); + return; + } + + p = (UINT8 *)(p_buf + 1) + L2CAP_SEND_CMD_OFFSET + HCI_DATA_PREAMBLE_SIZE + + L2CAP_PKT_OVERHEAD + L2CAP_CMD_OVERHEAD; + + mtu = p_ccb->local_conn_cfg.mtu; + mps = p_ccb->local_conn_cfg.mps; + initial_credit = p_ccb->local_conn_cfg.credits; + + L2CAP_TRACE_DEBUG ("l2cu_send_peer_ble_credit_based_conn_req PSM:0x%04x local_cid:%d\ + mtu:%d mps:%d initial_credit:%d", p_ccb->p_rcb->real_psm,\ + p_ccb->local_cid, mtu, mps, initial_credit); + + UINT16_TO_STREAM (p, p_ccb->p_rcb->real_psm); + UINT16_TO_STREAM (p, p_ccb->local_cid); + UINT16_TO_STREAM (p, mtu); + UINT16_TO_STREAM (p, mps); + UINT16_TO_STREAM (p, initial_credit); + + l2c_link_check_send_pkts (p_lcb, NULL, p_buf); +} + +/******************************************************************************* +** +** Function l2cu_reject_ble_connection +** +** Description Build and send an L2CAP "Credit based connection res" message +** to the peer. This function is called for non-success cases. +** +** Returns void +** +*******************************************************************************/ +void l2cu_reject_ble_connection (tL2C_LCB *p_lcb, UINT8 rem_id, UINT16 result) +{ + BT_HDR *p_buf; + UINT8 *p; + + if ((p_buf = l2cu_build_header(p_lcb, L2CAP_CMD_BLE_CREDIT_BASED_CONN_RES_LEN, + L2CAP_CMD_BLE_CREDIT_BASED_CONN_RES, rem_id)) == NULL ) + { + L2CAP_TRACE_WARNING ("l2cu_reject_ble_connection - no buffer"); + return; + } + + p = (UINT8 *)(p_buf + 1) + L2CAP_SEND_CMD_OFFSET + HCI_DATA_PREAMBLE_SIZE + + L2CAP_PKT_OVERHEAD + L2CAP_CMD_OVERHEAD; + + UINT16_TO_STREAM (p, 0); /* Local CID of 0 */ + UINT16_TO_STREAM (p, 0); /* MTU */ + UINT16_TO_STREAM (p, 0); /* MPS */ + UINT16_TO_STREAM (p, 0); /* initial credit */ + UINT16_TO_STREAM (p, result); + + l2c_link_check_send_pkts (p_lcb, NULL, p_buf); +} + +/******************************************************************************* +** +** Function l2cu_send_peer_ble_credit_based_conn_res +** +** Description Build and send an L2CAP "Credit based connection res" message +** to the peer. This function is called in case of success. +** +** Returns void +** +*******************************************************************************/ +void l2cu_send_peer_ble_credit_based_conn_res (tL2C_CCB *p_ccb, UINT16 result) +{ + BT_HDR *p_buf; + UINT8 *p; + + L2CAP_TRACE_DEBUG ("l2cu_send_peer_ble_credit_based_conn_res"); + if ((p_buf = l2cu_build_header(p_ccb->p_lcb, L2CAP_CMD_BLE_CREDIT_BASED_CONN_RES_LEN, + L2CAP_CMD_BLE_CREDIT_BASED_CONN_RES, p_ccb->remote_id)) == NULL ) + { + L2CAP_TRACE_WARNING ("l2cu_send_peer_ble_credit_based_conn_res - no buffer"); + return; + } + + p = (UINT8 *)(p_buf + 1) + L2CAP_SEND_CMD_OFFSET + HCI_DATA_PREAMBLE_SIZE + + L2CAP_PKT_OVERHEAD + L2CAP_CMD_OVERHEAD; + + UINT16_TO_STREAM (p, p_ccb->local_cid); /* Local CID */ + UINT16_TO_STREAM (p, p_ccb->local_conn_cfg.mtu); /* MTU */ + UINT16_TO_STREAM (p, p_ccb->local_conn_cfg.mps); /* MPS */ + UINT16_TO_STREAM (p, p_ccb->local_conn_cfg.credits); /* initial credit */ + UINT16_TO_STREAM (p, result); + + l2c_link_check_send_pkts (p_ccb->p_lcb, NULL, p_buf); +} + +/******************************************************************************* +** +** Function l2cu_send_peer_ble_flow_control_credit +** +** Description Build and send a BLE packet to give credits to peer device +** for LE connection oriented L2CAP channel. +** +** Returns void +** +*******************************************************************************/ +void l2cu_send_peer_ble_flow_control_credit(tL2C_CCB *p_ccb, UINT16 credit_value) +{ + BT_HDR *p_buf; + UINT8 *p; + tL2C_LCB *p_lcb = NULL; + + if (!p_ccb) { + return; + } + p_lcb = p_ccb->p_lcb; + + /* Create an identifier for this packet */ + p_ccb->p_lcb->id++; + l2cu_adj_id(p_ccb->p_lcb, L2CAP_ADJ_ID); + + p_ccb->local_id = p_ccb->p_lcb->id; + + if ((p_buf = l2cu_build_header (p_lcb, L2CAP_CMD_BLE_FLOW_CTRL_CREDIT_LEN, + L2CAP_CMD_BLE_FLOW_CTRL_CREDIT, p_lcb->id)) == NULL ) + { + L2CAP_TRACE_WARNING ("l2cu_send_peer_ble_credit_based_conn_req - no buffer"); + return; + } + + p = (UINT8 *)(p_buf + 1) + L2CAP_SEND_CMD_OFFSET + HCI_DATA_PREAMBLE_SIZE + + L2CAP_PKT_OVERHEAD + L2CAP_CMD_OVERHEAD; + + UINT16_TO_STREAM (p, p_ccb->local_cid); + UINT16_TO_STREAM (p, credit_value); + + l2c_link_check_send_pkts (p_lcb, NULL, p_buf); +} + +/******************************************************************************* +** +** Function l2cu_send_peer_ble_credit_based_conn_req +** +** Description Build and send a BLE packet to disconnect LE connection oriented +** L2CAP channel. +** +** Returns void +** +*******************************************************************************/ +void l2cu_send_peer_ble_credit_based_disconn_req(tL2C_CCB *p_ccb) +{ + BT_HDR *p_buf; + UINT8 *p; + tL2C_LCB *p_lcb = NULL; + L2CAP_TRACE_DEBUG ("%s",__func__); + + if (!p_ccb) { + return; + } + p_lcb = p_ccb->p_lcb; + + /* Create an identifier for this packet */ + p_ccb->p_lcb->id++; + l2cu_adj_id(p_ccb->p_lcb, L2CAP_ADJ_ID); + + p_ccb->local_id = p_ccb->p_lcb->id; + if ((p_buf = l2cu_build_header (p_lcb, L2CAP_DISC_REQ_LEN, + L2CAP_CMD_DISC_REQ, p_lcb->id)) == NULL ) + { + L2CAP_TRACE_WARNING ("l2cu_send_peer_ble_credit_based_disconn_req - no buffer"); + return; + } + + p = (UINT8 *)(p_buf + 1) + L2CAP_SEND_CMD_OFFSET + HCI_DATA_PREAMBLE_SIZE + + L2CAP_PKT_OVERHEAD + L2CAP_CMD_OVERHEAD; + + UINT16_TO_STREAM (p, p_ccb->remote_cid); + UINT16_TO_STREAM (p,p_ccb->local_cid); + + l2c_link_check_send_pkts (p_lcb, NULL, p_buf); +} + +#endif /* BLE_INCLUDED == TRUE */ + +/******************************************************************************* +** Functions used by both Full and Light Stack +********************************************************************************/ + +/******************************************************************************* +** +** Function l2cu_find_lcb_by_handle +** +** Description Look through all active LCBs for a match based on the +** HCI handle. +** +** Returns pointer to matched LCB, or NULL if no match +** +*******************************************************************************/ +tL2C_LCB *l2cu_find_lcb_by_handle (UINT16 handle) +{ + list_node_t *p_node = NULL; + tL2C_LCB *p_lcb = NULL; + for (p_node = list_begin(l2cb.p_lcb_pool); p_node; p_node = list_next(p_node)) { + p_lcb = list_node(p_node); + if ((p_lcb->in_use) && (p_lcb->handle == handle)) { + return (p_lcb); + } + } + + /* If here, no match found */ + return (NULL); +} + +/******************************************************************************* +** +** Function l2cu_find_ccb_by_cid +** +** Description Look through all active CCBs on a link for a match based +** on the local CID. If passed the link pointer is NULL, all +** active links are searched. +** +** Returns pointer to matched CCB, or NULL if no match +** +*******************************************************************************/ +bool l2cu_find_ccb_in_list(void *p_ccb_node, void *p_local_cid) +{ + tL2C_CCB *p_ccb = (tL2C_CCB *)p_ccb_node; + uint8_t local_cid = *((uint8_t *)p_local_cid); + + if (p_ccb->local_cid == local_cid && p_ccb->in_use) { + return FALSE; + } + return TRUE; +} + +tL2C_CCB *l2cu_find_ccb_by_cid (tL2C_LCB *p_lcb, UINT16 local_cid) +{ + tL2C_CCB *p_ccb = NULL; +#if (L2CAP_UCD_INCLUDED == FALSE) + if (local_cid < L2CAP_BASE_APPL_CID) { + return NULL; + } +#endif //(L2CAP_UCD_INCLUDED == FALSE) + list_node_t *p_node = NULL; + + p_node = (list_foreach(l2cb.p_ccb_pool, l2cu_find_ccb_in_list, &local_cid)); + if (p_node) { + p_ccb = (tL2C_CCB *)list_node(p_node); + + if (p_lcb && p_lcb != p_ccb->p_lcb) { + p_ccb = NULL; + } + } + + return (p_ccb); +} + +tL2C_CCB *l2cu_find_free_ccb (void) +{ + tL2C_CCB *p_ccb = NULL; + + list_node_t *p_node = NULL; + + for (p_node = list_begin(l2cb.p_ccb_pool); p_node; p_node = list_next(p_node)) + { + p_ccb = list_node(p_node); + if(p_ccb && !p_ccb->in_use ) { + return p_ccb; + } + } + + return (NULL); +} + +#if (L2CAP_ROUND_ROBIN_CHANNEL_SERVICE == TRUE && CLASSIC_BT_INCLUDED == TRUE) + +/****************************************************************************** +** +** Function l2cu_get_next_channel_in_rr +** +** Description get the next channel to send on a link. It also adjusts the +** CCB queue to do a basic priority and round-robin scheduling. +** +** Returns pointer to CCB or NULL +** +*******************************************************************************/ +static tL2C_CCB *l2cu_get_next_channel_in_rr(tL2C_LCB *p_lcb) +{ + tL2C_CCB *p_serve_ccb = NULL; + tL2C_CCB *p_ccb; + + int i, j; + + /* scan all of priority until finding a channel to serve */ + for ( i = 0; (i < L2CAP_NUM_CHNL_PRIORITY) && (!p_serve_ccb); i++ ) { + /* scan all channel within serving priority group until finding a channel to serve */ + for ( j = 0; (j < p_lcb->rr_serv[p_lcb->rr_pri].num_ccb) && (!p_serve_ccb); j++) { + /* scaning from next serving channel */ + p_ccb = p_lcb->rr_serv[p_lcb->rr_pri].p_serve_ccb; + + if (!p_ccb) { + L2CAP_TRACE_ERROR("p_serve_ccb is NULL, rr_pri=%d", p_lcb->rr_pri); + return NULL; + } + + L2CAP_TRACE_DEBUG("RR scan pri=%d, lcid=0x%04x, q_cout=%d", + p_ccb->ccb_priority, p_ccb->local_cid, + fixed_queue_length(p_ccb->xmit_hold_q)); + + /* store the next serving channel */ + /* this channel is the last channel of its priority group */ + if (( p_ccb->p_next_ccb == NULL ) + || ( p_ccb->p_next_ccb->ccb_priority != p_ccb->ccb_priority )) { + /* next serving channel is set to the first channel in the group */ + p_lcb->rr_serv[p_lcb->rr_pri].p_serve_ccb = p_lcb->rr_serv[p_lcb->rr_pri].p_first_ccb; + } else { + /* next serving channel is set to the next channel in the group */ + p_lcb->rr_serv[p_lcb->rr_pri].p_serve_ccb = p_ccb->p_next_ccb; + } + + if (p_ccb->chnl_state != CST_OPEN) { + continue; + } + + /* eL2CAP option in use */ + if (p_ccb->peer_cfg.fcr.mode != L2CAP_FCR_BASIC_MODE) { + if (p_ccb->fcrb.wait_ack || p_ccb->fcrb.remote_busy) { + continue; + } + + if (fixed_queue_is_empty(p_ccb->fcrb.retrans_q)) { + if (fixed_queue_is_empty(p_ccb->xmit_hold_q)) { + continue; + } + + +#if (CLASSIC_BT_INCLUDED == TRUE) + /* If in eRTM mode, check for window closure */ + if ( (p_ccb->peer_cfg.fcr.mode == L2CAP_FCR_ERTM_MODE) && (l2c_fcr_is_flow_controlled (p_ccb)) ) { + continue; + } +#endif ///CLASSIC_BT_INCLUDED == TRUE + } + } else { + if (fixed_queue_is_empty(p_ccb->xmit_hold_q)) { + continue; + } + } + + /* found a channel to serve */ + p_serve_ccb = p_ccb; + /* decrease quota of its priority group */ + p_lcb->rr_serv[p_lcb->rr_pri].quota--; + } + + /* if there is no more quota of the priority group or no channel to have data to send */ + if ((p_lcb->rr_serv[p_lcb->rr_pri].quota == 0) || (!p_serve_ccb)) { + /* serve next priority group */ + p_lcb->rr_pri = (p_lcb->rr_pri + 1) % L2CAP_NUM_CHNL_PRIORITY; + /* initialize its quota */ + p_lcb->rr_serv[p_lcb->rr_pri].quota = L2CAP_GET_PRIORITY_QUOTA(p_lcb->rr_pri); + } + } + + if (p_serve_ccb) { + L2CAP_TRACE_DEBUG("RR service pri=%d, quota=%d, lcid=0x%04x", + p_serve_ccb->ccb_priority, + p_lcb->rr_serv[p_serve_ccb->ccb_priority].quota, + p_serve_ccb->local_cid ); + } + + return p_serve_ccb; +} + +#else /* (L2CAP_ROUND_ROBIN_CHANNEL_SERVICE == TRUE) */ + +/****************************************************************************** +** +** Function l2cu_get_next_channel +** +** Description get the next channel to send on a link bassed on priority +** scheduling. +** +** Returns pointer to CCB or NULL +** +*******************************************************************************/ +#if (CLASSIC_BT_INCLUDED == TRUE) +static tL2C_CCB *l2cu_get_next_channel(tL2C_LCB *p_lcb) +{ + tL2C_CCB *p_ccb; + + /* Get the first CCB with data to send. + */ + for (p_ccb = p_lcb->ccb_queue.p_first_ccb; p_ccb; p_ccb = p_ccb->p_next_ccb) { + if (p_ccb->chnl_state != CST_OPEN) { + continue; + } + + if (p_ccb->fcrb.wait_ack || p_ccb->fcrb.remote_busy) { + continue; + } + + if (!fixed_queue_is_empty(p_ccb->fcrb.retrans_q)) + return p_ccb; + } + + if (fixed_queue_is_empty(p_ccb->xmit_hold_q)) + continue; + } + + /* If in eRTM mode, check for window closure */ + if ( (p_ccb->peer_cfg.fcr.mode == L2CAP_FCR_ERTM_MODE) && (l2c_fcr_is_flow_controlled (p_ccb)) ) { + continue; + } + + /* If here, we found someone */ + return p_ccb; + } + + return NULL; +} +#endif ///CLASSIC_BT_INCLUDED == TRUE + +#endif /* (L2CAP_ROUND_ROBIN_CHANNEL_SERVICE == TRUE) */ + +/****************************************************************************** +** +** Function l2cu_get_next_buffer_to_send +** +** Description get the next buffer to send on a link. It also adjusts the +** CCB queue to do a basic priority and round-robin scheduling. +** +** Returns pointer to buffer or NULL +** +*******************************************************************************/ +BT_HDR *l2cu_get_next_buffer_to_send (tL2C_LCB *p_lcb) +{ + tL2C_CCB *p_ccb; + BT_HDR *p_buf = NULL; + + /* Highest priority are fixed channels */ +#if (L2CAP_NUM_FIXED_CHNLS > 0) + int xx; + + for (xx = 0; xx < L2CAP_NUM_FIXED_CHNLS; xx++) { + if ((p_ccb = p_lcb->p_fixed_ccbs[xx]) == NULL) { + continue; + } + + /* eL2CAP option in use */ + if (p_ccb->peer_cfg.fcr.mode != L2CAP_FCR_BASIC_MODE) { +#if (CLASSIC_BT_INCLUDED == TRUE) + if (p_ccb->fcrb.wait_ack || p_ccb->fcrb.remote_busy) { + continue; + } + + /* No more checks needed if sending from the reatransmit queue */ + if (fixed_queue_is_empty(p_ccb->fcrb.retrans_q)) + { + if (fixed_queue_is_empty(p_ccb->xmit_hold_q)) { + continue; + } + /* If in eRTM mode, check for window closure */ + if ( (p_ccb->peer_cfg.fcr.mode == L2CAP_FCR_ERTM_MODE) && (l2c_fcr_is_flow_controlled (p_ccb)) ) { + continue; + } + } + if ((p_buf = l2c_fcr_get_next_xmit_sdu_seg(p_ccb, 0)) != NULL) { + l2cu_check_channel_congestion (p_ccb); + l2cu_set_acl_hci_header (p_buf, p_ccb); + return (p_buf); + } +#else + continue; +#endif ///CLASSIC_BT_INCLUDED == TRUE + + } else { + if (!fixed_queue_is_empty(p_ccb->xmit_hold_q)) { + p_buf = (BT_HDR *)fixed_queue_dequeue(p_ccb->xmit_hold_q, 0); + if (NULL == p_buf) { + L2CAP_TRACE_ERROR("l2cu_get_buffer_to_send: No data to be sent"); + return (NULL); + } + l2cu_check_channel_congestion (p_ccb); + l2cu_set_acl_hci_header (p_buf, p_ccb); + /* send tx complete */ + if (l2cb.fixed_reg[xx].pL2CA_FixedTxComplete_Cb) { + (*l2cb.fixed_reg[xx].pL2CA_FixedTxComplete_Cb)(p_ccb->local_cid, 1); + } + return (p_buf); + } + } + } +#endif +#if (CLASSIC_BT_INCLUDED == TRUE) +#if (L2CAP_ROUND_ROBIN_CHANNEL_SERVICE == TRUE) + /* get next serving channel in round-robin */ + p_ccb = l2cu_get_next_channel_in_rr( p_lcb ); +#else + p_ccb = l2cu_get_next_channel( p_lcb ); +#endif + + /* Return if no buffer */ + if (p_ccb == NULL) { + return (NULL); + } + + if (p_ccb->peer_cfg.fcr.mode != L2CAP_FCR_BASIC_MODE) { + + if ((p_buf = l2c_fcr_get_next_xmit_sdu_seg(p_ccb, 0)) == NULL) { + return (NULL); + } + + } else { + p_buf = (BT_HDR *)fixed_queue_dequeue(p_ccb->xmit_hold_q, 0); + if (NULL == p_buf) { + L2CAP_TRACE_ERROR("l2cu_get_buffer_to_send() #2: No data to be sent"); + return (NULL); + } + } + + if ( p_ccb->p_rcb && p_ccb->p_rcb->api.pL2CA_TxComplete_Cb && (p_ccb->peer_cfg.fcr.mode != L2CAP_FCR_ERTM_MODE) ) { + (*p_ccb->p_rcb->api.pL2CA_TxComplete_Cb)(p_ccb->local_cid, 1); + } + + + l2cu_check_channel_congestion (p_ccb); + + l2cu_set_acl_hci_header (p_buf, p_ccb); +#endif ///CLASSIC_BT_INCLUDED == TRUE + + return (p_buf); +} + +/****************************************************************************** +** +** Function l2cu_set_acl_hci_header +** +** Description Set HCI handle for ACL packet +** +** Returns None +** +*******************************************************************************/ +void l2cu_set_acl_hci_header (BT_HDR *p_buf, tL2C_CCB *p_ccb) +{ + UINT8 *p; + + /* Set the pointer to the beginning of the data minus 4 bytes for the packet header */ + p = (UINT8 *)(p_buf + 1) + p_buf->offset - HCI_DATA_PREAMBLE_SIZE; + +#if (BLE_INCLUDED == TRUE) + if (p_ccb->p_lcb->transport == BT_TRANSPORT_LE) { + UINT16_TO_STREAM (p, p_ccb->p_lcb->handle | (L2CAP_PKT_START_NON_FLUSHABLE << L2CAP_PKT_TYPE_SHIFT)); + + uint16_t acl_data_size = controller_get_interface()->get_acl_data_size_ble(); + /* The HCI transport will segment the buffers. */ + if (p_buf->len > acl_data_size) { + UINT16_TO_STREAM (p, acl_data_size); + } else { + UINT16_TO_STREAM (p, p_buf->len); + } + } /* (BLE_INCLUDED == TRUE) */ + else +#endif + { +#if (L2CAP_NON_FLUSHABLE_PB_INCLUDED == TRUE) + if ( (((p_buf->layer_specific & L2CAP_FLUSHABLE_MASK) == L2CAP_FLUSHABLE_CH_BASED) && (p_ccb->is_flushable)) + || ((p_buf->layer_specific & L2CAP_FLUSHABLE_MASK) == L2CAP_FLUSHABLE_PKT) ) { + UINT16_TO_STREAM (p, p_ccb->p_lcb->handle | (L2CAP_PKT_START << L2CAP_PKT_TYPE_SHIFT)); + } else { + UINT16_TO_STREAM (p, p_ccb->p_lcb->handle | l2cb.non_flushable_pbf); + } +#else + UINT16_TO_STREAM (p, p_ccb->p_lcb->handle | (L2CAP_PKT_START << L2CAP_PKT_TYPE_SHIFT)); +#endif + + uint16_t acl_data_size = controller_get_interface()->get_acl_data_size_classic(); + /* The HCI transport will segment the buffers. */ + if (p_buf->len > acl_data_size) { + UINT16_TO_STREAM (p, acl_data_size); + } else { + UINT16_TO_STREAM (p, p_buf->len); + } + } + p_buf->offset -= HCI_DATA_PREAMBLE_SIZE; + p_buf->len += HCI_DATA_PREAMBLE_SIZE; +} + +/****************************************************************************** +** +** Function l2cu_check_channel_congestion +** +** Description check if any change in congestion status +** +** Returns None +** +*******************************************************************************/ +void l2cu_check_channel_congestion (tL2C_CCB *p_ccb) +{ + size_t q_count = fixed_queue_length(p_ccb->xmit_hold_q); +#if (CLASSIC_BT_INCLUDED == TRUE) + size_t q_waiting_ack_count = fixed_queue_length(p_ccb->fcrb.waiting_for_ack_q); +#endif + +#if (L2CAP_UCD_INCLUDED == TRUE) + if ( p_ccb->local_cid == L2CAP_CONNECTIONLESS_CID ) { + q_count += fixed_queue_length(p_ccb->p_lcb->ucd_out_sec_pending_q); + } +#endif + /* If the CCB queue limit is subject to a quota, check for congestion */ + /* if this channel has outgoing traffic */ + if (p_ccb->buff_quota != 0) { + /* If this channel was congested */ + if ( p_ccb->cong_sent ) { + /* If the channel is not congested now, tell the app */ + if (q_count <= (p_ccb->buff_quota / 2) +#if (CLASSIC_BT_INCLUDED == TRUE) + && (p_ccb->peer_cfg.fcr.mode == L2CAP_FCR_BASIC_MODE || q_waiting_ack_count < p_ccb->our_cfg.fcr.tx_win_sz) +#endif + ) { + p_ccb->cong_sent = FALSE; + if (p_ccb->p_rcb && p_ccb->p_rcb->api.pL2CA_CongestionStatus_Cb) { + L2CAP_TRACE_DEBUG ("L2CAP - Calling CongestionStatus_Cb (FALSE), CID: 0x%04x xmit_hold_q.count: %u buff_quota: %u", + p_ccb->local_cid, q_count, p_ccb->buff_quota); + + /* Prevent recursive calling */ + l2cb.is_cong_cback_context = TRUE; + (*p_ccb->p_rcb->api.pL2CA_CongestionStatus_Cb)(p_ccb->local_cid, FALSE); + l2cb.is_cong_cback_context = FALSE; + } +#if (L2CAP_UCD_INCLUDED == TRUE) + else if ( p_ccb->p_rcb && p_ccb->local_cid == L2CAP_CONNECTIONLESS_CID ) { + if ( p_ccb->p_rcb->ucd.cb_info.pL2CA_UCD_Congestion_Status_Cb ) { + L2CAP_TRACE_DEBUG ("L2CAP - Calling UCD CongestionStatus_Cb (FALSE), SecPendingQ:%u,XmitQ:%u,Quota:%u", + fixed_queue_length(p_ccb->p_lcb->ucd_out_sec_pending_q), + fixed_queue_length(p_ccb->xmit_hold_q), + p_ccb->buff_quota); + p_ccb->p_rcb->ucd.cb_info.pL2CA_UCD_Congestion_Status_Cb( p_ccb->p_lcb->remote_bd_addr, FALSE ); + } + } +#endif +#if (L2CAP_NUM_FIXED_CHNLS > 0) + else { + UINT8 xx; + for (xx = 0; xx < L2CAP_NUM_FIXED_CHNLS; xx ++) { + if (p_ccb->p_lcb->p_fixed_ccbs[xx] == p_ccb) { + if (l2cb.fixed_reg[xx].pL2CA_FixedCong_Cb != NULL) { + (* l2cb.fixed_reg[xx].pL2CA_FixedCong_Cb)(p_ccb->p_lcb->remote_bd_addr, FALSE); + } + break; + } + } + } +#endif + } + } else { + tL2C_LCB *p_lcb = p_ccb->p_lcb; + /* If this channel was not congested but it is congested now, tell the app */ + if (q_count > p_ccb->buff_quota || (p_lcb && (p_lcb->link_xmit_data_q) && (list_length(p_lcb->link_xmit_data_q) + q_count) > p_ccb->buff_quota) +#if (CLASSIC_BT_INCLUDED == TRUE) + || (p_ccb->peer_cfg.fcr.mode != L2CAP_FCR_BASIC_MODE && q_waiting_ack_count >= p_ccb->our_cfg.fcr.tx_win_sz) +#endif + ) { + p_ccb->cong_sent = TRUE; + if (p_ccb->p_rcb && p_ccb->p_rcb->api.pL2CA_CongestionStatus_Cb) { + L2CAP_TRACE_DEBUG ("L2CAP - Calling CongestionStatus_Cb (TRUE),CID:0x%04x,XmitQ:%u,Quota:%u", + p_ccb->local_cid, q_count, p_ccb->buff_quota); + + (*p_ccb->p_rcb->api.pL2CA_CongestionStatus_Cb)(p_ccb->local_cid, TRUE); + } +#if (L2CAP_UCD_INCLUDED == TRUE) + else if ( p_ccb->p_rcb && p_ccb->local_cid == L2CAP_CONNECTIONLESS_CID ) { + if ( p_ccb->p_rcb->ucd.cb_info.pL2CA_UCD_Congestion_Status_Cb ) { + L2CAP_TRACE_DEBUG ("L2CAP - Calling UCD CongestionStatus_Cb (TRUE), SecPendingQ:%u,XmitQ:%u,Quota:%u", + fixed_queue_length(p_ccb->p_lcb->ucd_out_sec_pending_q), + fixed_queue_length(p_ccb->xmit_hold_q), + p_ccb->buff_quota); + p_ccb->p_rcb->ucd.cb_info.pL2CA_UCD_Congestion_Status_Cb( p_ccb->p_lcb->remote_bd_addr, TRUE ); + } + } +#endif +#if (L2CAP_NUM_FIXED_CHNLS > 0) + else { + UINT8 xx; + for (xx = 0; xx < L2CAP_NUM_FIXED_CHNLS; xx ++) { + if (p_ccb->p_lcb->p_fixed_ccbs[xx] == p_ccb) { + if (l2cb.fixed_reg[xx].pL2CA_FixedCong_Cb != NULL) { + (* l2cb.fixed_reg[xx].pL2CA_FixedCong_Cb)(p_ccb->p_lcb->remote_bd_addr, TRUE); + } + break; + } + } + } +#endif + } + } + } +} diff --git a/lib/bt/host/bluedroid/stack/l2cap/l2cap_client.c b/lib/bt/host/bluedroid/stack/l2cap/l2cap_client.c new file mode 100644 index 00000000..c7314b62 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/l2cap/l2cap_client.c @@ -0,0 +1,462 @@ +/****************************************************************************** + * + * Copyright (C) 2014 Google, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ +#if (defined(L2CAP_CLIENT_INCLUDED) && L2CAP_CLIENT_INCLUDED == TRUE) +#include +#include "common/bt_trace.h" +#include "common/bt_defs.h" +#include "device/bdaddr.h" +#include "osi/allocator.h" +#include "osi/buffer.h" +#include "osi/list.h" +#include "osi/osi.h" +#include "stack/l2cap_client.h" +#include "stack/l2c_api.h" + +struct l2cap_client_t { + l2cap_client_callbacks_t callbacks; + void *context; + + uint16_t local_channel_id; + uint16_t remote_mtu; + bool configured_self; + bool configured_peer; + bool is_congested; + list_t *outbound_fragments; +}; + +static void connect_completed_cb(uint16_t local_channel_id, uint16_t error_code); +static void config_request_cb(uint16_t local_channel_id, tL2CAP_CFG_INFO *requested_parameters); +static void config_completed_cb(uint16_t local_channel_id, tL2CAP_CFG_INFO *negotiated_parameters); +static void disconnect_request_cb(uint16_t local_channel_id, bool ack_required); +static void disconnect_completed_cb(uint16_t local_channel_id, uint16_t error_code); +static void congestion_cb(uint16_t local_channel_id, bool is_congested); +static void read_ready_cb(uint16_t local_channel_id, BT_HDR *packet); +static void write_completed_cb(uint16_t local_channel_id, uint16_t packets_completed); + +static void fragment_packet(l2cap_client_t *client, buffer_t *packet); +static void dispatch_fragments(l2cap_client_t *client); +static l2cap_client_t *find(uint16_t local_channel_id); + +// From the Bluetooth Core specification. +static const uint16_t L2CAP_MTU_DEFAULT = 672; +static const uint16_t L2CAP_MTU_MINIMUM = 48; + +static const tL2CAP_APPL_INFO l2cap_callbacks = { + .pL2CA_ConnectCfm_Cb = connect_completed_cb, + .pL2CA_ConfigInd_Cb = config_request_cb, + .pL2CA_ConfigCfm_Cb = config_completed_cb, + .pL2CA_DisconnectInd_Cb = disconnect_request_cb, + .pL2CA_DisconnectCfm_Cb = disconnect_completed_cb, + .pL2CA_CongestionStatus_Cb = congestion_cb, + .pL2CA_DataInd_Cb = read_ready_cb, + .pL2CA_TxComplete_Cb = write_completed_cb, +}; + +static list_t *l2cap_clients; // A list of l2cap_client_t. Container does not own objects. + +buffer_t *l2cap_buffer_new(size_t size) +{ + buffer_t *buf = buffer_new(size + L2CAP_MIN_OFFSET); + buffer_t *slice = NULL; + if (buf) { + slice = buffer_new_slice(buf, size); + } + buffer_free(buf); + return slice; +} + +l2cap_client_t *l2cap_client_new(const l2cap_client_callbacks_t *callbacks, void *context) +{ + assert(callbacks != NULL); + assert(callbacks->connected != NULL); + assert(callbacks->disconnected != NULL); + assert(callbacks->read_ready != NULL); + assert(callbacks->write_ready != NULL); + + if (!l2cap_clients) { + l2cap_clients = list_new(NULL); + if (!l2cap_clients) { + L2CAP_TRACE_ERROR("%s unable to allocate space for L2CAP client list.", __func__); + return NULL; + } + } + + l2cap_client_t *ret = (l2cap_client_t *)osi_calloc(sizeof(l2cap_client_t)); + if (!ret) { + L2CAP_TRACE_ERROR("%s unable to allocate L2CAP client.", __func__); + goto error; + } + + ret->callbacks = *callbacks; + ret->context = context; + + ret->remote_mtu = L2CAP_MTU_DEFAULT; + ret->outbound_fragments = list_new(NULL); + if (!ret) { + L2CAP_TRACE_ERROR("%s unable to allocate outbound L2CAP fragment list.", __func__); + goto error; + } + + list_append(l2cap_clients, ret); + + return ret; + +error:; + osi_free(ret); + return NULL; +} + +void l2cap_client_free(l2cap_client_t *client) +{ + if (!client) { + return; + } + + list_remove(l2cap_clients, client); + l2cap_client_disconnect(client); + list_free(client->outbound_fragments); + osi_free(client); +} + +bool l2cap_client_connect(l2cap_client_t *client, const bt_bdaddr_t *remote_bdaddr, uint16_t psm) +{ + assert(client != NULL); + assert(remote_bdaddr != NULL); + assert(psm != 0); + assert(!bdaddr_is_empty(remote_bdaddr)); + assert(client->local_channel_id == 0); + assert(!client->configured_self); + assert(!client->configured_peer); + assert(!L2C_INVALID_PSM(psm)); + + client->local_channel_id = L2CA_ConnectReq(psm, (uint8_t *)remote_bdaddr); + if (!client->local_channel_id) { + L2CAP_TRACE_ERROR("%s unable to create L2CAP connection.", __func__); + return false; + } + + L2CA_SetConnectionCallbacks(client->local_channel_id, &l2cap_callbacks); + return true; +} + +void l2cap_client_disconnect(l2cap_client_t *client) +{ + assert(client != NULL); + + if (client->local_channel_id && !L2CA_DisconnectReq(client->local_channel_id)) { + L2CAP_TRACE_ERROR("%s unable to send disconnect message for LCID 0x%04x.", __func__, client->local_channel_id); + } + + client->local_channel_id = 0; + client->remote_mtu = L2CAP_MTU_DEFAULT; + client->configured_self = false; + client->configured_peer = false; + client->is_congested = false; + + for (const list_node_t *node = list_begin(client->outbound_fragments); node != list_end(client->outbound_fragments); node = list_next(node)) { + osi_free(list_node(node)); + } + + list_clear(client->outbound_fragments); +} + +bool l2cap_client_is_connected(const l2cap_client_t *client) +{ + assert(client != NULL); + + return client->local_channel_id != 0 && client->configured_self && client->configured_peer; +} + +bool l2cap_client_write(l2cap_client_t *client, buffer_t *packet) +{ + assert(client != NULL); + assert(packet != NULL); + assert(l2cap_client_is_connected(client)); + + if (client->is_congested) { + return false; + } + + fragment_packet(client, packet); + dispatch_fragments(client); + return true; +} + +static void connect_completed_cb(uint16_t local_channel_id, uint16_t error_code) +{ + assert(local_channel_id != 0); + + l2cap_client_t *client = find(local_channel_id); + if (!client) { + L2CAP_TRACE_ERROR("%s unable to find L2CAP client for LCID 0x%04x.", __func__, local_channel_id); + return; + } + + if (error_code != L2CAP_CONN_OK) { + L2CAP_TRACE_ERROR("%s error connecting L2CAP channel: %d.", __func__, error_code); + client->callbacks.disconnected(client, client->context); + return; + } + + // Use default L2CAP parameters. + tL2CAP_CFG_INFO desired_parameters = { 0 }; + if (!L2CA_ConfigReq(local_channel_id, &desired_parameters)) { + L2CAP_TRACE_ERROR("%s error sending L2CAP config parameters.", __func__); + client->callbacks.disconnected(client, client->context); + } +} + +static void config_request_cb(uint16_t local_channel_id, tL2CAP_CFG_INFO *requested_parameters) +{ + tL2CAP_CFG_INFO response = { 0 }; + l2cap_client_t *client = find(local_channel_id); + + if (!client) { + L2CAP_TRACE_ERROR("%s unable to find L2CAP client matching LCID 0x%04x.", __func__, local_channel_id); + return; + } + + response.result = L2CAP_CFG_OK; + + if (requested_parameters->mtu_present) { + // Make sure the peer chose an MTU at least as large as the minimum L2CAP MTU defined + // by the Bluetooth Core spec. + if (requested_parameters->mtu < L2CAP_MTU_MINIMUM) { + response.mtu = L2CAP_MTU_MINIMUM; + response.mtu_present = true; + response.result = L2CAP_CFG_UNACCEPTABLE_PARAMS; + } else { + client->remote_mtu = requested_parameters->mtu; + } + } + + if (requested_parameters->fcr_present) { + if (requested_parameters->fcr.mode != L2CAP_FCR_BASIC_MODE) { + response.fcr_present = true; + response.fcr = requested_parameters->fcr; + response.fcr.mode = L2CAP_FCR_BASIC_MODE; + response.result = L2CAP_CFG_UNACCEPTABLE_PARAMS; + } + } + + if (!L2CA_ConfigRsp(local_channel_id, &response)) { + L2CAP_TRACE_ERROR("%s unable to send config response for LCID 0x%04x.", __func__, local_channel_id); + l2cap_client_disconnect(client); + return; + } + + // If we've configured both endpoints, let the listener know we've connected. + client->configured_peer = true; + if (l2cap_client_is_connected(client)) { + client->callbacks.connected(client, client->context); + } +} + +static void config_completed_cb(uint16_t local_channel_id, tL2CAP_CFG_INFO *negotiated_parameters) +{ + l2cap_client_t *client = find(local_channel_id); + + if (!client) { + L2CAP_TRACE_ERROR("%s unable to find L2CAP client matching LCID 0x%04x.", __func__, local_channel_id); + return; + } + + switch (negotiated_parameters->result) { + // We'll get another configuration response later. + case L2CAP_CFG_PENDING: + break; + + case L2CAP_CFG_UNACCEPTABLE_PARAMS: + // TODO: see if we can renegotiate parameters instead of dropping the connection. + L2CAP_TRACE_WARNING("%s dropping L2CAP connection due to unacceptable config parameters.\n", __func__); + l2cap_client_disconnect(client); + break; + + case L2CAP_CFG_OK: + // If we've configured both endpoints, let the listener know we've connected. + client->configured_self = true; + if (l2cap_client_is_connected(client)) { + client->callbacks.connected(client, client->context); + } + break; + + // Failure, no further parameter negotiation possible. + default: + L2CAP_TRACE_WARNING("%s L2CAP parameter negotiation failed with error code %d.\n", __func__, negotiated_parameters->result); + l2cap_client_disconnect(client); + break; + } +} + +static void disconnect_request_cb(uint16_t local_channel_id, bool ack_required) +{ + l2cap_client_t *client = find(local_channel_id); + if (!client) { + L2CAP_TRACE_ERROR("%s unable to find L2CAP client with LCID 0x%04x.\n", __func__, local_channel_id); + return; + } + + if (ack_required) { + L2CA_DisconnectRsp(local_channel_id); + } + + // We already sent a disconnect response so this LCID is now invalid. + client->local_channel_id = 0; + l2cap_client_disconnect(client); + + client->callbacks.disconnected(client, client->context); +} + +static void disconnect_completed_cb(uint16_t local_channel_id, UNUSED_ATTR uint16_t error_code) +{ + assert(local_channel_id != 0); + + l2cap_client_t *client = find(local_channel_id); + if (!client) { + L2CAP_TRACE_ERROR("%s unable to find L2CAP client with LCID 0x%04x.\n", __func__, local_channel_id); + return; + } + + client->local_channel_id = 0; + l2cap_client_disconnect(client); + + client->callbacks.disconnected(client, client->context); +} + +static void congestion_cb(uint16_t local_channel_id, bool is_congested) +{ + assert(local_channel_id != 0); + + l2cap_client_t *client = find(local_channel_id); + if (!client) { + L2CAP_TRACE_ERROR("%s unable to find L2CAP client matching LCID 0x%04x.\n", __func__, local_channel_id); + return; + } + + client->is_congested = is_congested; + + if (!is_congested) { + // If we just decongested, dispatch whatever we have left over in our queue. + // Once that's done, if we're still decongested, notify the listener so it + // can start writing again. + dispatch_fragments(client); + if (!client->is_congested) { + client->callbacks.write_ready(client, client->context); + } + } +} + +static void read_ready_cb(uint16_t local_channel_id, BT_HDR *packet) +{ + assert(local_channel_id != 0); + + l2cap_client_t *client = find(local_channel_id); + if (!client) { + L2CAP_TRACE_ERROR("%s unable to find L2CAP client matching LCID 0x%04x.\n", __func__, local_channel_id); + return; + } + + // TODO(sharvil): eliminate copy from BT_HDR. + buffer_t *buffer = buffer_new(packet->len); + memcpy(buffer_ptr(buffer), packet->data + packet->offset, packet->len); + osi_free(packet); + + client->callbacks.read_ready(client, buffer, client->context); + buffer_free(buffer); +} + +static void write_completed_cb(UNUSED_ATTR uint16_t local_channel_id, UNUSED_ATTR uint16_t packets_completed) +{ + // Do nothing. We update congestion state based on the congestion callback + // and we've already removed items from outbound_fragments list so we don't + // really care how many packets were successfully dispatched. +} + +static void fragment_packet(l2cap_client_t *client, buffer_t *packet) +{ + assert(client != NULL); + assert(packet != NULL); + + // TODO(sharvil): eliminate copy into BT_HDR. + BT_HDR *bt_packet = osi_malloc(buffer_length(packet) + L2CAP_MIN_OFFSET); + bt_packet->offset = L2CAP_MIN_OFFSET; + bt_packet->len = buffer_length(packet); + memcpy(bt_packet->data + bt_packet->offset, buffer_ptr(packet), buffer_length(packet)); + + for (;;) { + if (bt_packet->len <= client->remote_mtu) { + if (bt_packet->len > 0) { + list_append(client->outbound_fragments, bt_packet); + } else { + osi_free(bt_packet); + } + break; + } + + BT_HDR *fragment = osi_malloc(client->remote_mtu + L2CAP_MIN_OFFSET); + fragment->offset = L2CAP_MIN_OFFSET; + fragment->len = client->remote_mtu; + memcpy(fragment->data + fragment->offset, bt_packet->data + bt_packet->offset, client->remote_mtu); + + list_append(client->outbound_fragments, fragment); + + bt_packet->offset += client->remote_mtu; + bt_packet->len -= client->remote_mtu; + } +} + +static void dispatch_fragments(l2cap_client_t *client) +{ + assert(client != NULL); + assert(!client->is_congested); + + while (!list_is_empty(client->outbound_fragments)) { + BT_HDR *packet = (BT_HDR *)list_front(client->outbound_fragments); + list_remove(client->outbound_fragments, packet); + + switch (L2CA_DataWrite(client->local_channel_id, packet)) { + case L2CAP_DW_CONGESTED: + client->is_congested = true; + return; + + case L2CAP_DW_FAILED: + L2CAP_TRACE_ERROR("%s error writing data to L2CAP connection LCID 0x%04x; disconnecting.", __func__, client->local_channel_id); + l2cap_client_disconnect(client); + return; + + case L2CAP_DW_SUCCESS: + break; + } + } +} + +static l2cap_client_t *find(uint16_t local_channel_id) +{ + assert(local_channel_id != 0); + + for (const list_node_t *node = list_begin(l2cap_clients); node != list_end(l2cap_clients); node = list_next(node)) { + l2cap_client_t *client = (l2cap_client_t *)list_node(node); + if (client->local_channel_id == local_channel_id) { + return client; + } + } + + return NULL; +} + +#endif /*L2CAP_CLIENT_INCLUDED*/ diff --git a/lib/bt/host/bluedroid/stack/rfcomm/include/port_int.h b/lib/bt/host/bluedroid/stack/rfcomm/include/port_int.h new file mode 100644 index 00000000..8ceb1442 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/rfcomm/include/port_int.h @@ -0,0 +1,248 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/***************************************************************************** + * + * This file contains definitions internal to the PORT unit + * + *****************************************************************************/ + +#ifndef PORT_INT_H +#define PORT_INT_H + +#include "common/bt_target.h" +#include "stack/rfcdefs.h" +#include "stack/port_api.h" +#include "osi/fixed_queue.h" +#include "common/bt_defs.h" + +/* Local events passed when application event is sent from the api to PORT */ +/* ???*/ +#define PORT_EVENT_OPEN (1 | BT_EVT_TO_BTU_SP_EVT) +#define PORT_EVENT_CONTROL (2 | BT_EVT_TO_BTU_SP_EVT) +#define PORT_EVENT_SET_STATE (3 | BT_EVT_TO_BTU_SP_EVT) +#define PORT_EVENT_SET_CALLBACK (5 | BT_EVT_TO_BTU_SP_EVT) +#define PORT_EVENT_WRITE (6 | BT_EVT_TO_BTU_SP_EVT) +#define PORT_EVENT_PURGE (7 | BT_EVT_TO_BTU_SP_EVT) +#define PORT_EVENT_SEND_ERROR (8 | BT_EVT_TO_BTU_SP_EVT) +#define PORT_EVENT_FLOW_CONTROL (9 | BT_EVT_TO_BTU_SP_EVT) + +/* +** Flow control configuration values for the mux +*/ +#define PORT_FC_UNDEFINED 0 /* mux flow control mechanism not defined yet */ +#define PORT_FC_TS710 1 /* use TS 07.10 flow control */ +#define PORT_FC_CREDIT 2 /* use RFCOMM credit based flow control */ + +/* +** Define Port Data Transfere control block +*/ +typedef struct { + fixed_queue_t *queue; /* Queue of buffers waiting to be sent */ + BOOLEAN peer_fc; /* TRUE if flow control is set based on peer's request */ + BOOLEAN user_fc; /* TRUE if flow control is set based on user's request */ + UINT32 queue_size; /* Number of data bytes in the queue */ + tPORT_CALLBACK *p_callback; /* Address of the callback function */ +} tPORT_DATA; + +/* +** Port control structure used to pass modem info +*/ +typedef struct { +#define MODEM_SIGNAL_DTRDSR 0x01 +#define MODEM_SIGNAL_RTSCTS 0x02 +#define MODEM_SIGNAL_RI 0x04 +#define MODEM_SIGNAL_DCD 0x08 + + UINT8 modem_signal; /* [DTR/DSR | RTS/CTS | RI | DCD ] */ + + UINT8 break_signal; /* 0-3 s in steps of 200 ms */ + + UINT8 discard_buffers; /* 0 - do not discard, 1 - discard */ + +#define RFCOMM_CTRL_BREAK_ASAP 0 +#define RFCOMM_CTRL_BREAK_IN_SEQ 1 + + UINT8 break_signal_seq; /* as soon as possible | in sequence (default) */ + + BOOLEAN fc; /* TRUE when the device is unable to accept frames */ +} tPORT_CTRL; + + +/* +** RFCOMM multiplexer Control Block +*/ +typedef struct { + TIMER_LIST_ENT tle; /* Timer list entry */ + fixed_queue_t *cmd_q; /* Queue for command messages on this mux */ + UINT8 port_inx[RFCOMM_MAX_DLCI + 1]; /* Array for quick access to */ + /* tPORT based on dlci */ + BD_ADDR bd_addr; /* BD ADDR of the peer if initiator */ + UINT16 lcid; /* Local cid used for this channel */ + UINT16 peer_l2cap_mtu; /* Max frame that can be sent to peer L2CAP */ + UINT8 state; /* Current multiplexer channel state */ + UINT8 is_initiator; /* TRUE if this side sends SABME (dlci=0) */ + BOOLEAN local_cfg_sent; + BOOLEAN peer_cfg_rcvd; + BOOLEAN restart_required; /* TRUE if has to restart channel after disc */ + BOOLEAN peer_ready; /* True if other side can accept frames */ + UINT8 flow; /* flow control mechanism for this mux */ + BOOLEAN l2cap_congested; /* TRUE if L2CAP is congested */ + BOOLEAN is_disc_initiator; /* TRUE if initiated disc of port */ + UINT16 pending_lcid; /* store LCID for incoming connection while connecting */ + UINT8 pending_id; /* store l2cap ID for incoming connection while connecting */ +} tRFC_MCB; + + +/* +** RFCOMM Port Connection Control Block +*/ +struct t_rfc_port { +#define RFC_PORT_STATE_IDLE 0 +#define RFC_PORT_STATE_WAIT_START 1 +#define RFC_PORT_STATE_OPENING 2 +#define RFC_PORT_STATE_OPENED 3 +#define RFC_PORT_STATE_CLOSING 4 + + UINT8 state; /* Current state of the connection */ + +#define RFC_RSP_PN 0x01 +#define RFC_RSP_RPN_REPLY 0x02 +#define RFC_RSP_RPN 0x04 +#define RFC_RSP_MSC 0x08 +#define RFC_RSP_RLS 0x10 + + UINT8 expected_rsp; + + tRFC_MCB *p_mcb; + + TIMER_LIST_ENT tle; /* Timer list entry */ +}; +typedef struct t_rfc_port tRFC_PORT; + + +/* +** Define control block containing information about PORT connection +*/ +struct t_port_info { + UINT8 inx; /* Index of this control block in the port_info array */ + BOOLEAN in_use; /* True when structure is allocated */ + +#define PORT_STATE_CLOSED 0 +#define PORT_STATE_OPENING 1 +#define PORT_STATE_OPENED 2 +#define PORT_STATE_CLOSING 3 + + UINT8 state; /* State of the application */ + + UINT8 scn; /* Service channel number */ + UINT16 uuid; /* Service UUID */ + + BD_ADDR bd_addr; /* BD ADDR of the device for the multiplexer channel */ + BOOLEAN is_server; /* TRUE if the server application */ + UINT8 dlci; /* DLCI of the connection */ + + UINT8 error; /* Last error detected */ + + UINT8 line_status; /* Line status as reported by peer */ + + UINT8 default_signal_state; /* Initial signal state depending on uuid */ + + UINT16 mtu; /* Max MTU that port can receive */ + UINT16 peer_mtu; /* Max MTU that port can send */ + + tPORT_DATA tx; /* Control block for data from app to peer */ + tPORT_DATA rx; /* Control block for data from peer to app */ + + tPORT_STATE user_port_pars; /* Port parameters for user connection */ + tPORT_STATE peer_port_pars; /* Port parameters for user connection */ + + tPORT_CTRL local_ctrl; + tPORT_CTRL peer_ctrl; + +#define PORT_CTRL_REQ_SENT 0x01 +#define PORT_CTRL_REQ_CONFIRMED 0x02 +#define PORT_CTRL_IND_RECEIVED 0x04 +#define PORT_CTRL_IND_RESPONDED 0x08 + + UINT8 port_ctrl; /* Modem Status Command */ + + BOOLEAN rx_flag_ev_pending; /* RXFLAG Character is received */ + + tRFC_PORT rfc; /* RFCOMM port control block */ + + UINT32 ev_mask; /* Event mask for the callback */ + tPORT_CALLBACK *p_callback; /* Pointer to users callback function */ + tPORT_MGMT_CALLBACK *p_mgmt_callback; /* Callback function to receive connection up/down */ + tPORT_DATA_CALLBACK *p_data_callback; /* Callback function to receive data indications */ + tPORT_DATA_CO_CALLBACK *p_data_co_callback; /* Callback function with callouts and flowctrl */ + UINT16 credit_tx; /* Flow control credits for tx path */ + UINT16 credit_rx; /* Flow control credits for rx path, this is */ + /* number of buffers peer is allowed to sent */ + UINT16 credit_rx_max; /* Max number of credits we will allow this guy to sent */ + UINT16 credit_rx_low; /* Number of credits when we send credit update */ + UINT16 rx_buf_critical; /* port receive queue critical watermark level */ + BOOLEAN keep_port_handle; /* TRUE if port is not deallocated when closing */ + /* it is set to TRUE for server when allocating port */ + UINT16 keep_mtu; /* Max MTU that port can receive by server */ +}; +typedef struct t_port_info tPORT; + + +/* Define the PORT/RFCOMM control structure +*/ +typedef struct { + tPORT port[MAX_RFC_PORTS]; /* Port info pool */ + tRFC_MCB rfc_mcb[MAX_BD_CONNECTIONS]; /* RFCOMM bd_connections pool */ + BOOLEAN enable_l2cap_ertm; /* enable/disable l2cap ertm */ +} tPORT_CB; + +#ifdef __cplusplus +extern "C" { +#endif + +/* +** Functions provided by the port_utils.c +*/ +extern tPORT *port_allocate_port (UINT8 dlci, BD_ADDR bd_addr); +extern void port_set_defaults (tPORT *p_port); +extern void port_select_mtu (tPORT *p_port); +extern void port_release_port (tPORT *p_port); +extern tPORT *port_find_mcb_dlci_port (tRFC_MCB *p_mcb, UINT8 dlci); +extern tRFC_MCB *port_find_mcb (BD_ADDR bd_addr); +extern tPORT *port_find_dlci_port (UINT8 dlci); +extern tPORT *port_find_port (UINT8 dlci, BD_ADDR bd_addr); +extern UINT32 port_get_signal_changes (tPORT *p_port, UINT8 old_signals, UINT8 signal); +extern UINT32 port_flow_control_user (tPORT *p_port); +extern void port_flow_control_peer(tPORT *p_port, BOOLEAN enable, UINT16 count); + +/* +** Functions provided by the port_rfc.c +*/ +extern int port_open_continue (tPORT *p_port); +extern void port_start_port_open (tPORT *p_port); +extern void port_start_par_neg (tPORT *p_port); +extern void port_start_control (tPORT *p_port); +extern void port_start_close (tPORT *p_port); +extern void port_rfc_closed (tPORT *p_port, UINT8 res); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/bt/host/bluedroid/stack/rfcomm/include/rfc_int.h b/lib/bt/host/bluedroid/stack/rfcomm/include/rfc_int.h new file mode 100644 index 00000000..b5f8e088 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/rfcomm/include/rfc_int.h @@ -0,0 +1,378 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/***************************************************************************** + * + * This file contains definitions internal to the RFC unit + * + *****************************************************************************/ + +#ifndef RFC_INT_H +#define RFC_INT_H + +#include "stack/l2c_api.h" +#include "port_int.h" + +/* +** Define RFCOMM result codes +*/ +#define RFCOMM_SUCCESS 0 +#define RFCOMM_ERROR 1 +#define RFCOMM_LOW_RESOURCES 2 +#define RFCOMM_TRY_LATER 3 + +#define RFCOMM_USER_ERR 111 +#define RFCOMM_SECURITY_ERR 112 + +/* +** Define max and min RFCOMM MTU (N1) +*/ +#define RFCOMM_MIN_MTU 23 +#define RFCOMM_MAX_MTU 32767 + +extern void RFCOMM_StartReq (tRFC_MCB *p_mcb); +extern void RFCOMM_StartRsp (tRFC_MCB *p_mcb, UINT16 result); + +extern void RFCOMM_DlcEstablishReq (tRFC_MCB *p_mcb, UINT8 dlci, UINT16 mtu); +extern void RFCOMM_DlcEstablishRsp (tRFC_MCB *p_mcb, UINT8 dlci, UINT16 mtu, UINT16 result); + +extern void RFCOMM_DataReq (tRFC_MCB *p_mcb, UINT8 dlci, BT_HDR *p_buf); + +extern void RFCOMM_DlcReleaseReq (tRFC_MCB *p_mcb, UINT8 dlci); + +extern void RFCOMM_ParNegReq (tRFC_MCB *p_mcb, UINT8 dlci, UINT16 mtu); +extern void RFCOMM_ParNegRsp (tRFC_MCB *p_mcb, UINT8 dlci, UINT16 mtu, UINT8 cl, UINT8 k); + +extern void RFCOMM_TestReq (UINT8 *p_data, UINT16 len); + +#define RFCOMM_FLOW_STATE_DISABLE 0 +#define RFCOMM_FLOW_STATE_ENABLE 1 + +extern void RFCOMM_FlowReq (tRFC_MCB *p_mcb, UINT8 dlci, UINT8 state); + +extern void RFCOMM_PortNegReq (tRFC_MCB *p_mcb, UINT8 dlci, tPORT_STATE *p_pars); +extern void RFCOMM_PortNegRsp (tRFC_MCB *p_mcb, UINT8 dlci, tPORT_STATE *p_pars, UINT16 param_mask); + +extern void RFCOMM_ControlReq (tRFC_MCB *p_mcb, UINT8 dlci, tPORT_CTRL *p_pars); +extern void RFCOMM_ControlRsp (tRFC_MCB *p_mcb, UINT8 dlci, tPORT_CTRL *p_pars); + +extern void RFCOMM_LineStatusReq (tRFC_MCB *p_mcb, UINT8 dlci, UINT8 line_status); +/* +** Define logical struct used for sending and decoding MX frames +*/ +typedef struct { + UINT8 dlci; + UINT8 type; + UINT8 cr; + UINT8 ea; + UINT8 pf; + UINT8 credit; + + union { + struct { + UINT8 dlci; + UINT8 frame_type; + UINT8 conv_layer; + UINT8 priority; + UINT8 t1; + UINT16 mtu; + UINT8 n2; + UINT8 k; + } pn; + struct { + UINT8 *p_data; + UINT16 data_len; + } test; + struct { + UINT8 dlci; + UINT8 signals; + UINT8 break_present; + UINT8 break_duration; + } msc; + struct { + UINT8 ea; + UINT8 cr; + UINT8 type; + } nsc; + struct { + UINT8 dlci; + UINT8 is_request; + UINT8 baud_rate; + UINT8 byte_size; + UINT8 stop_bits; + UINT8 parity; + UINT8 parity_type; + UINT8 fc_type; + UINT8 xon_char; + UINT8 xoff_char; + UINT16 param_mask; + } rpn; + struct { + UINT8 dlci; + UINT8 line_status; + } rls; + } u; +} MX_FRAME; + +#define LINE_STATUS_NO_ERROR 0x00 +#define LINE_STATUS_OVERRUN 0x02 /* Receive Overrun Error */ +#define LINE_STATUS_RXPARITY 0x04 /* Receive Parity Error */ +#define LINE_STATUS_FRAME 0x08 /* Receive Framing error */ +#define LINE_STATUS_FAILED 0x10 /* Connection Failed */ + +/* +** Define states and events for the RFC multiplexer state machine +*/ +#define RFC_MX_STATE_IDLE 0 +#define RFC_MX_STATE_WAIT_CONN_CNF 1 +#define RFC_MX_STATE_CONFIGURE 2 +#define RFC_MX_STATE_SABME_WAIT_UA 3 +#define RFC_MX_STATE_WAIT_SABME 4 +#define RFC_MX_STATE_CONNECTED 5 +#define RFC_MX_STATE_DISC_WAIT_UA 6 + +/* +** Define port states +*/ +#define RFC_STATE_CLOSED 0 +#define RFC_STATE_SABME_WAIT_UA 1 +#define RFC_STATE_ORIG_WAIT_SEC_CHECK 2 +#define RFC_STATE_TERM_WAIT_SEC_CHECK 3 +#define RFC_STATE_OPENED 4 +#define RFC_STATE_DISC_WAIT_UA 5 + +/* +** Events that can be received by multiplexer as well as port state machines +*/ +#define RFC_EVENT_SABME 0 +#define RFC_EVENT_UA 1 +#define RFC_EVENT_DM 2 +#define RFC_EVENT_DISC 3 +#define RFC_EVENT_UIH 4 +#define RFC_EVENT_TIMEOUT 5 +#define RFC_EVENT_BAD_FRAME 50 +/* +** Multiplexer events +*/ +#define RFC_MX_EVENT_START_REQ 6 +#define RFC_MX_EVENT_START_RSP 7 +#define RFC_MX_EVENT_CLOSE_REQ 8 +#define RFC_MX_EVENT_CONN_CNF 9 +#define RFC_MX_EVENT_CONN_IND 10 +#define RFC_MX_EVENT_CONF_CNF 11 +#define RFC_MX_EVENT_CONF_IND 12 +#define RFC_MX_EVENT_QOS_VIOLATION_IND 13 +#define RFC_MX_EVENT_DISC_IND 14 +#define RFC_MX_EVENT_TEST_CMD 15 +#define RFC_MX_EVENT_TEST_RSP 16 +#define RFC_MX_EVENT_FCON_CMD 17 +#define RFC_MX_EVENT_FCOFF_CMD 18 +#define RFC_MX_EVENT_NSC 19 +#define RFC_MX_EVENT_NSC_RSP 20 + +/* +** Port events +*/ +#define RFC_EVENT_OPEN 9 +#define RFC_EVENT_ESTABLISH_RSP 11 +#define RFC_EVENT_CLOSE 12 +#define RFC_EVENT_CLEAR 13 +#define RFC_EVENT_DATA 14 +#define RFC_EVENT_SEC_COMPLETE 15 + +// btla-specific ++ +#define RFC_T1_TIMEOUT 20 /* seconds to wait for reply with Poll bit */ +#define RFC_PORT_T1_TIMEOUT 60 /* seconds to wait for reply with Poll bit other than MX */ +#define RFC_T2_TIMEOUT 20 /* timeout to wait for Mx UIH */ +// btla-specific -- +#define RFC_DISC_TIMEOUT 3 /* If something goes wrong and we send DISC we should not wait for min */ +#define RFC_CLOSE_TIMEOUT 10 +#define RFCOMM_CONN_TIMEOUT 120 /* first connection to be established on Mx */ + + +/* Define RFComm control block +*/ +typedef struct { + MX_FRAME rx_frame; + tL2CAP_APPL_INFO reg_info; /* L2CAP Registration info */ + tRFC_MCB *p_rfc_lcid_mcb[MAX_L2CAP_CHANNELS]; /* MCB based on the L2CAP's lcid */ + BOOLEAN peer_rx_disabled; /* If TRUE peer sent FCOFF */ + UINT8 last_mux; /* Last mux allocated */ + UINT8 last_port; /* Last port allocated */ +} tRFCOMM_CB; + +/* Main Control Block for the RFCOMM Layer (PORT and RFC) */ +typedef struct { + tRFCOMM_CB rfc; + tPORT_CB port; + UINT8 trace_level; +} tRFC_CB; + + +#if RFC_DYNAMIC_MEMORY == FALSE +extern tRFC_CB rfc_cb; +#else +extern tRFC_CB *rfc_cb_ptr; +#define rfc_cb (*rfc_cb_ptr) +#endif + +/* Timer running on the multiplexor channel while no DLCI connection is opened */ +#define RFC_MCB_INIT_INACT_TIMER 60 /* in seconds */ + +/* Timer running on the multiplexor channel after last DLCI is released */ +#define RFC_MCB_RELEASE_INACT_TIMER 2 /* in seconds */ + +/* +** Define RFCOMM frame processing errors +*/ +#define RFCOMM_ERR_BAD_SABME 1 +#define RFCOMM_ERR_BAD_UA 2 +#define RFCOMM_ERR_BAD_DM 3 +#define RFCOMM_ERR_BAD_DISC 4 +#define RFCOMM_ERR_BAD_UIH 5 + +#ifdef RFCOMM_PRECALC_FCS + +#define RFCOMM_SABME_FCS(p_data, cr, dlci) rfc_sabme_fcs[cr][dlci] +#define RFCOMM_UA_FCS(p_data, cr, dlci) rfc_ua_fcs[cr][dlci] +#define RFCOMM_DM_FCS(p_data, cr, dlci) rfc_dm_fcs[cr][dlci] +#define RFCOMM_DISC_FCS(p_data, cr, dlci) rfc_disc_fcs[cr][dlci] +#define RFCOMM_UIH_FCS(p_data, dlci) rfc_uih_fcs[dlci] + +#else + +extern UINT8 rfc_calc_fcs (UINT16 len, UINT8 *p); + +#define RFCOMM_SABME_FCS(p_data, cr, dlci) rfc_calc_fcs(3, p_data) +#define RFCOMM_UA_FCS(p_data, cr, dlci) rfc_calc_fcs(3, p_data) +#define RFCOMM_DM_FCS(p_data, cr, dlci) rfc_calc_fcs(3, p_data) +#define RFCOMM_DISC_FCS(p_data, cr, dlci) rfc_calc_fcs(3, p_data) +#define RFCOMM_UIH_FCS(p_data, dlci) rfc_calc_fcs(2, p_data) + +#endif + + +#ifdef __cplusplus +extern "C" { +#endif + +extern void rfc_mx_sm_execute (tRFC_MCB *p_mcb, UINT16 event, void *p_data); + +/* +** Functions provided by the rfc_port_fsm.c +*/ +extern void rfc_port_sm_execute (tPORT *p_port, UINT16 event, void *p_data); + + +extern void rfc_process_pn (tRFC_MCB *p_rfc_mcb, BOOLEAN is_command, MX_FRAME *p_frame); +extern void rfc_process_msc (tRFC_MCB *p_rfc_mcb, BOOLEAN is_command, MX_FRAME *p_frame); +extern void rfc_process_rpn (tRFC_MCB *p_rfc_mcb, BOOLEAN is_command, BOOLEAN is_request, MX_FRAME *p_frame); +extern void rfc_process_rls (tRFC_MCB *p_rfc_mcb, BOOLEAN is_command, MX_FRAME *p_frame); +extern void rfc_process_nsc (tRFC_MCB *p_rfc_mcb, MX_FRAME *p_frame); +extern void rfc_process_test_rsp (tRFC_MCB *p_rfc_mcb, BT_HDR *p_buf); +extern void rfc_process_fcon (tRFC_MCB *p_rfc_mcb, BOOLEAN is_command); +extern void rfc_process_fcoff (tRFC_MCB *p_rfc_mcb, BOOLEAN is_command); +extern void rfc_process_l2cap_congestion (tRFC_MCB *p_mcb, BOOLEAN is_congested); + +/* +** Functions provided by the rfc_utils.c +*/ +tRFC_MCB *rfc_alloc_multiplexer_channel (BD_ADDR bd_addr, BOOLEAN is_initiator); +extern void rfc_release_multiplexer_channel (tRFC_MCB *p_rfc_mcb); +extern void rfc_timer_start (tRFC_MCB *p_rfc_mcb, UINT16 timeout); +extern void rfc_timer_stop (tRFC_MCB *p_rfc_mcb); +extern void rfc_timer_free (tRFC_MCB *p_rfc_mcb); +extern void rfc_port_timer_start (tPORT *p_port, UINT16 tout); +extern void rfc_port_timer_stop (tPORT *p_port); +extern void rfc_port_timer_free (tPORT *p_port); + +BOOLEAN rfc_check_uih_fcs (UINT8 dlci, UINT8 received_fcs); +BOOLEAN rfc_check_fcs (UINT16 len, UINT8 *p, UINT8 received_fcs); +tRFC_MCB *rfc_find_lcid_mcb (UINT16 lcid); +extern void rfc_save_lcid_mcb (tRFC_MCB *p_rfc_mcb, UINT16 lcid); +extern void rfc_check_mcb_active (tRFC_MCB *p_mcb); +extern void rfc_port_closed (tPORT *p_port); +extern void rfc_sec_check_complete (BD_ADDR bd_addr, tBT_TRANSPORT transport, void *p_ref_data, UINT8 res); +extern void rfc_inc_credit (tPORT *p_port, UINT8 credit); +extern void rfc_dec_credit (tPORT *p_port); +extern void rfc_check_send_cmd(tRFC_MCB *p_mcb, BT_HDR *p_buf); + +/* +** Functions provided by the rfc_ts_frames.c +*/ +extern void rfc_send_sabme (tRFC_MCB *p_rfc_mcb, UINT8 dlci); +extern void rfc_send_ua (tRFC_MCB *p_rfc_mcb, UINT8 dlci); +extern void rfc_send_dm (tRFC_MCB *p_rfc_mcb, UINT8 dlci, BOOLEAN pf); +extern void rfc_send_disc (tRFC_MCB *p_rfc_mcb, UINT8 dlci); +extern void rfc_send_pn (tRFC_MCB *p_mcb, UINT8 dlci, BOOLEAN is_command, UINT16 mtu, + UINT8 cl, UINT8 k); +extern void rfc_send_test (tRFC_MCB *p_rfc_mcb, BOOLEAN is_command, BT_HDR *p_buf); +extern void rfc_send_msc (tRFC_MCB *p_mcb, UINT8 dlci, BOOLEAN is_command, + tPORT_CTRL *p_pars); +extern void rfc_send_rls (tRFC_MCB *p_mcb, UINT8 dlci, BOOLEAN is_command, UINT8 status); +extern void rfc_send_rpn (tRFC_MCB *p_mcb, UINT8 dlci, BOOLEAN is_command, + tPORT_STATE *p_pars, UINT16 mask); +extern void rfc_send_fcon (tRFC_MCB *p_mcb, BOOLEAN is_command); +extern void rfc_send_fcoff (tRFC_MCB *p_mcb, BOOLEAN is_command); +extern void rfc_send_buf_uih (tRFC_MCB *p_rfc_mcb, UINT8 dlci, BT_HDR *p_buf); +extern void rfc_send_credit(tRFC_MCB *p_mcb, UINT8 dlci, UINT8 credit); +extern void rfc_process_mx_message (tRFC_MCB *p_rfc_mcb, BT_HDR *p_buf); +extern UINT8 rfc_parse_data (tRFC_MCB *p_rfc_mcb, MX_FRAME *p_frame, BT_HDR *p_buf); + +/* +** Functions provided by the rfc_disp.c +*/ + + + +/* Call back functions from RFCOMM */ +extern void rfcomm_l2cap_if_init (void); + +extern void PORT_StartInd (tRFC_MCB *p_mcb); +extern void PORT_StartCnf (tRFC_MCB *p_mcb, UINT16 result); + +extern void PORT_CloseInd (tRFC_MCB *p_mcb); +extern void Port_TimeOutCloseMux (tRFC_MCB *p_mcb); + +extern void PORT_DlcEstablishInd (tRFC_MCB *p_mcb, UINT8 dlci, UINT16 mtu); +extern void PORT_DlcEstablishCnf (tRFC_MCB *p_mcb, UINT8 dlci, UINT16 mtu, UINT16 result); + +extern void PORT_DataInd (tRFC_MCB *p_mcb, UINT8 dlci, BT_HDR *p_buf); + +extern void PORT_DlcReleaseInd (tRFC_MCB *p_mcb, UINT8 dlci); + +extern void PORT_ParNegInd (tRFC_MCB *p_mcb, UINT8 dlci, UINT16 mtu, UINT8 cl, UINT8 k); +extern void PORT_ParNegCnf (tRFC_MCB *p_mcb, UINT8 dlci, UINT16 mtu, UINT8 cl, UINT8 k); + +extern void PORT_TestCnf (tRFC_MCB *p_mcb, UINT8 *p_data, UINT16 len); + +extern void PORT_FlowInd (tRFC_MCB *p_mcb, UINT8 dlci, BOOLEAN fc); + +extern void PORT_PortNegInd (tRFC_MCB *p_mcb, UINT8 dlci, tPORT_STATE *p_pars, UINT16 param_mask); +extern void PORT_PortNegCnf (tRFC_MCB *p_mcb, UINT8 dlci, tPORT_STATE *p_pars, UINT16 result); + +extern void PORT_ControlInd (tRFC_MCB *p_mcb, UINT8 dlci, tPORT_CTRL *p_pars); +extern void PORT_ControlCnf (tRFC_MCB *p_mcb, UINT8 dlci, tPORT_CTRL *p_pars); + +extern void PORT_LineStatusInd (tRFC_MCB *p_mcb, UINT8 dlci, UINT8 line_status); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/bt/host/bluedroid/stack/rfcomm/port_api.c b/lib/bt/host/bluedroid/stack/rfcomm/port_api.c new file mode 100644 index 00000000..1aa38ff7 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/rfcomm/port_api.c @@ -0,0 +1,1883 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * this file contains the Serial Port API code + * + ******************************************************************************/ + +#include +#include "common/bt_target.h" +#include "stack/rfcdefs.h" +#include "stack/port_api.h" +#include "port_int.h" +#include "btm_int.h" +#include "stack/btm_api.h" +#include "rfc_int.h" +#include "stack/l2c_api.h" +#include "stack/sdp_api.h" +#include "osi/allocator.h" +#include "osi/mutex.h" + +#if (defined RFCOMM_INCLUDED && RFCOMM_INCLUDED == TRUE) +/* duration of break in 200ms units */ +#define PORT_BREAK_DURATION 1 + +/* Mapping from PORT_* result codes to human readable strings. */ +static const char *result_code_strings[] = { + "Success", + "Unknown error", + "Already opened", + "Command pending", + "App not registered", + "No memory", + "No resources", + "Bad BD address", + "Unspecified error", + "Bad handle", + "Not opened", + "Line error", + "Start failed", + "Parameter negotiation failed", + "Port negotiation failed", + "Sec failed", + "Peer connection failed", + "Peer failed", + "Peer timeout", + "Closed", + "TX full", + "Local closed", + "Local timeout", + "TX queue disabled", + "Page timeout", + "Invalid SCN", + "Unknown result code" +}; + +/******************************************************************************* +** +** Function RFCOMM_CreateConnection +** +** Description RFCOMM_CreateConnection function is used from the application +** to establish serial port connection to the peer device, +** or allow RFCOMM to accept a connection from the peer +** application. +** +** Parameters: scn - Service Channel Number as registered with +** the SDP (server) or obtained using SDP from +** the peer device (client). +** is_server - TRUE if requesting application is a server +** mtu - Maximum frame size the application can accept +** bd_addr - BD_ADDR of the peer (client) +** mask - specifies events to be enabled. A value +** of zero disables all events. +** p_handle - OUT pointer to the handle. +** p_mgmt_cb - pointer to callback function to receive +** connection up/down events. +** Notes: +** +** Server can call this function with the same scn parameter multiple times if +** it is ready to accept multiple simulteneous connections. +** +** DLCI for the connection is (scn * 2 + 1) if client originates connection on +** existing none initiator multiplexer channel. Otherwise it is (scn * 2). +** For the server DLCI can be changed later if client will be calling it using +** (scn * 2 + 1) dlci. +** +*******************************************************************************/ +int RFCOMM_CreateConnection (UINT16 uuid, UINT8 scn, BOOLEAN is_server, + UINT16 mtu, BD_ADDR bd_addr, UINT16 *p_handle, + tPORT_MGMT_CALLBACK *p_mgmt_cb) +{ + tPORT *p_port; + int i; + UINT8 dlci; + tRFC_MCB *p_mcb = port_find_mcb (bd_addr); + UINT16 rfcomm_mtu; + + RFCOMM_TRACE_API ("RFCOMM_CreateConnection() BDA: %02x-%02x-%02x-%02x-%02x-%02x", + bd_addr[0], bd_addr[1], bd_addr[2], bd_addr[3], bd_addr[4], bd_addr[5]); + + *p_handle = 0; + + if (( scn == 0 ) || (scn >= PORT_MAX_RFC_PORTS )) { + /* Server Channel Number(SCN) should be in range 1...30 */ + RFCOMM_TRACE_ERROR ("RFCOMM_CreateConnection - invalid SCN"); + return (PORT_INVALID_SCN); + } + + /* For client that originate connection on the existing none initiator */ + /* multiplexer channel DLCI should be odd */ + if (p_mcb && !p_mcb->is_initiator && !is_server) { + dlci = (scn << 1) + 1; + } else { + dlci = (scn << 1); + } + RFCOMM_TRACE_API("RFCOMM_CreateConnection(): scn:%d, dlci:%d, is_server:%d mtu:%d, p_mcb:%p", + scn, dlci, is_server, mtu, p_mcb); + + /* For the server side always allocate a new port. On the client side */ + /* do not allow the same (dlci, bd_addr) to be opened twice by application */ + if (!is_server && ((p_port = port_find_port (dlci, bd_addr)) != NULL)) { + /* if existing port is also a client port */ + if (p_port->is_server == FALSE) { + RFCOMM_TRACE_ERROR ("RFCOMM_CreateConnection - already opened state:%d, RFC state:%d, MCB state:%d", + p_port->state, p_port->rfc.state, p_port->rfc.p_mcb ? p_port->rfc.p_mcb->state : 0); + return (PORT_ALREADY_OPENED); + } + } + + if ((p_port = port_allocate_port (dlci, bd_addr)) == NULL) { + RFCOMM_TRACE_WARNING ("RFCOMM_CreateConnection - no resources"); + return (PORT_NO_RESOURCES); + } + RFCOMM_TRACE_API("RFCOMM_CreateConnection(): scn:%d, dlci:%d, is_server:%d mtu:%d, p_mcb:%p, p_port:%p", + scn, dlci, is_server, mtu, p_mcb, p_port); + + p_port->default_signal_state = (PORT_DTRDSR_ON | PORT_CTSRTS_ON | PORT_DCD_ON); + + switch (uuid) { + case UUID_PROTOCOL_OBEX: + p_port->default_signal_state = PORT_OBEX_DEFAULT_SIGNAL_STATE; + break; + case UUID_SERVCLASS_SERIAL_PORT: + p_port->default_signal_state = PORT_SPP_DEFAULT_SIGNAL_STATE; + break; + case UUID_SERVCLASS_LAN_ACCESS_USING_PPP: + p_port->default_signal_state = PORT_PPP_DEFAULT_SIGNAL_STATE; + break; + case UUID_SERVCLASS_DIALUP_NETWORKING: + case UUID_SERVCLASS_FAX: + p_port->default_signal_state = PORT_DUN_DEFAULT_SIGNAL_STATE; + break; + } + + RFCOMM_TRACE_EVENT ("RFCOMM_CreateConnection dlci:%d signal state:0x%x", dlci, p_port->default_signal_state); + + *p_handle = p_port->inx; + + p_port->state = PORT_STATE_OPENING; + p_port->uuid = uuid; + p_port->is_server = is_server; + p_port->scn = scn; + p_port->ev_mask = 0; + + /* If the MTU is not specified (0), keep MTU decision until the + * PN frame has to be send + * at that time connection should be established and we + * will know for sure our prefered MTU + */ + + rfcomm_mtu = L2CAP_MTU_SIZE - RFCOMM_DATA_OVERHEAD; + + if (mtu) { + p_port->mtu = (mtu < rfcomm_mtu) ? mtu : rfcomm_mtu; + } else { + p_port->mtu = rfcomm_mtu; + } + + /* server doesn't need to release port when closing */ + if ( is_server ) { + p_port->keep_port_handle = TRUE; + + /* keep mtu that user asked, p_port->mtu could be updated during param negotiation */ + p_port->keep_mtu = p_port->mtu; + } + + p_port->local_ctrl.modem_signal = p_port->default_signal_state; + p_port->local_ctrl.fc = FALSE; + + p_port->p_mgmt_callback = p_mgmt_cb; + + for (i = 0; i < BD_ADDR_LEN; i++) { + p_port->bd_addr[i] = bd_addr[i]; + } + + /* If this is not initiator of the connection need to just wait */ + if (p_port->is_server) { + return (PORT_SUCCESS); + } + + /* Open will be continued after security checks are passed */ + return port_open_continue (p_port); +} + + +/******************************************************************************* +** +** Function RFCOMM_RemoveConnection +** +** Description This function is called to close the specified connection. +** +** Parameters: handle - Handle returned in the RFCOMM_CreateConnection +** +*******************************************************************************/ +int RFCOMM_RemoveConnection (UINT16 handle) +{ + tPORT *p_port; + + RFCOMM_TRACE_API ("RFCOMM_RemoveConnection() handle:%d", handle); + + /* Check if handle is valid to avoid crashing */ + if ((handle == 0) || (handle > MAX_RFC_PORTS)) { + RFCOMM_TRACE_ERROR ("RFCOMM_RemoveConnection() BAD handle:%d", handle); + return (PORT_BAD_HANDLE); + } + p_port = &rfc_cb.port.port[handle - 1]; + + if (!p_port->in_use || (p_port->state == PORT_STATE_CLOSED)) { + RFCOMM_TRACE_EVENT ("RFCOMM_RemoveConnection() Not opened:%d", handle); + return (PORT_SUCCESS); + } + + p_port->state = PORT_STATE_CLOSING; + + port_start_close (p_port); + + return (PORT_SUCCESS); +} + +/******************************************************************************* +** +** Function RFCOMM_RemoveServer +** +** Description This function is called to close the server port. +** +** Parameters: handle - Handle returned in the RFCOMM_CreateConnection +** +*******************************************************************************/ +int RFCOMM_RemoveServer (UINT16 handle) +{ + tPORT *p_port; + + RFCOMM_TRACE_API ("RFCOMM_RemoveServer() handle:%d", handle); + + /* Check if handle is valid to avoid crashing */ + if ((handle == 0) || (handle > MAX_RFC_PORTS)) { + RFCOMM_TRACE_ERROR ("RFCOMM_RemoveServer() BAD handle:%d", handle); + return (PORT_BAD_HANDLE); + } + p_port = &rfc_cb.port.port[handle - 1]; + + /* Do not report any events to the client any more. */ + p_port->p_mgmt_callback = NULL; + + if (!p_port->in_use || (p_port->state == PORT_STATE_CLOSED)) { + RFCOMM_TRACE_EVENT ("RFCOMM_RemoveServer() Not opened:%d", handle); + return (PORT_SUCCESS); + } + + /* this port will be deallocated after closing */ + p_port->keep_port_handle = FALSE; + p_port->state = PORT_STATE_CLOSING; + + port_start_close (p_port); + + return (PORT_SUCCESS); +} + +/******************************************************************************* +** +** Function PORT_SetEventCallback +** +** Description This function is called to provide an address of the +** function which will be called when one of the events +** specified in the mask occures. +** +** Parameters: handle - Handle returned in the RFCOMM_CreateConnection +** p_callback - address of the callback function which should +** be called from the RFCOMM when an event +** specified in the mask occures. +** +** +*******************************************************************************/ +int PORT_SetEventCallback (UINT16 port_handle, tPORT_CALLBACK *p_port_cb) +{ + tPORT *p_port; + + /* Check if handle is valid to avoid crashing */ + if ((port_handle == 0) || (port_handle > MAX_RFC_PORTS)) { + return (PORT_BAD_HANDLE); + } + + p_port = &rfc_cb.port.port[port_handle - 1]; + + if (!p_port->in_use || (p_port->state == PORT_STATE_CLOSED)) { + return (PORT_NOT_OPENED); + } + + RFCOMM_TRACE_API ("PORT_SetEventCallback() handle:%d", port_handle); + + p_port->p_callback = p_port_cb; + + return (PORT_SUCCESS); +} +/******************************************************************************* +** +** Function PORT_ClearKeepHandleFlag +** +** Description This function is called to clear the keep handle flag +** which will cause not to keep the port handle open when closed +** Parameters: handle - Handle returned in the RFCOMM_CreateConnection +** +*******************************************************************************/ + +int PORT_ClearKeepHandleFlag (UINT16 port_handle) +{ + tPORT *p_port; + + /* Check if handle is valid to avoid crashing */ + if ((port_handle == 0) || (port_handle > MAX_RFC_PORTS)) { + return (PORT_BAD_HANDLE); + } + + p_port = &rfc_cb.port.port[port_handle - 1]; + p_port->keep_port_handle = 0; + return (PORT_SUCCESS); +} + +/******************************************************************************* +** +** Function PORT_SetDataCallback +** +** Description This function is when a data packet is received +** +** Parameters: handle - Handle returned in the RFCOMM_CreateConnection +** p_callback - address of the callback function which should +** be called from the RFCOMM when data packet +** is received. +** +** +*******************************************************************************/ +int PORT_SetDataCallback (UINT16 port_handle, tPORT_DATA_CALLBACK *p_port_cb) +{ + tPORT *p_port; + + // RFCOMM_TRACE_API ("PORT_SetDataCallback() handle:%d cb 0x%x", port_handle, p_port_cb); + + /* Check if handle is valid to avoid crashing */ + if ((port_handle == 0) || (port_handle > MAX_RFC_PORTS)) { + return (PORT_BAD_HANDLE); + } + + p_port = &rfc_cb.port.port[port_handle - 1]; + + if (!p_port->in_use || (p_port->state == PORT_STATE_CLOSED)) { + return (PORT_NOT_OPENED); + } + + p_port->p_data_callback = p_port_cb; + + return (PORT_SUCCESS); +} +/******************************************************************************* +** +** Function PORT_SetCODataCallback +** +** Description This function is when a data packet is received +** +** Parameters: handle - Handle returned in the RFCOMM_CreateConnection +** p_callback - address of the callback function which should +** be called from the RFCOMM when data packet +** is received. +** +** +*******************************************************************************/ +int PORT_SetDataCOCallback (UINT16 port_handle, tPORT_DATA_CO_CALLBACK *p_port_cb) +{ + tPORT *p_port; + + // RFCOMM_TRACE_API ("PORT_SetDataCOCallback() handle:%d cb 0x%x", port_handle, p_port_cb); + + /* Check if handle is valid to avoid crashing */ + if ((port_handle == 0) || (port_handle > MAX_RFC_PORTS)) { + return (PORT_BAD_HANDLE); + } + + p_port = &rfc_cb.port.port[port_handle - 1]; + + if (!p_port->in_use || (p_port->state == PORT_STATE_CLOSED)) { + return (PORT_NOT_OPENED); + } + + p_port->p_data_co_callback = p_port_cb; + + return (PORT_SUCCESS); +} + + + +/******************************************************************************* +** +** Function PORT_SetEventMask +** +** Description This function is called to close the specified connection. +** +** Parameters: handle - Handle returned in the RFCOMM_CreateConnection +** mask - Bitmask of the events the host is interested in +** +*******************************************************************************/ +int PORT_SetEventMask (UINT16 port_handle, UINT32 mask) +{ + tPORT *p_port; + + RFCOMM_TRACE_API ("PORT_SetEventMask() handle:%d mask:0x%x", port_handle, mask); + + /* Check if handle is valid to avoid crashing */ + if ((port_handle == 0) || (port_handle > MAX_RFC_PORTS)) { + return (PORT_BAD_HANDLE); + } + + p_port = &rfc_cb.port.port[port_handle - 1]; + + if (!p_port->in_use || (p_port->state == PORT_STATE_CLOSED)) { + return (PORT_NOT_OPENED); + } + + p_port->ev_mask = mask; + + return (PORT_SUCCESS); +} + + +/******************************************************************************* +** +** Function PORT_CheckConnection +** +** Description This function returns PORT_SUCCESS if connection referenced +** by handle is up and running +** +** Parameters: handle - Handle returned in the RFCOMM_CreateConnection +** ignore_rfc_state - If need to ignore rfc state +** bd_addr - OUT bd_addr of the peer +** p_lcid - OUT L2CAP's LCID +** +*******************************************************************************/ +int PORT_CheckConnection(UINT16 handle, BOOLEAN ignore_rfc_state, BD_ADDR bd_addr, UINT16 *p_lcid) +{ + tPORT *p_port; + + RFCOMM_TRACE_API ("PORT_CheckConnection() handle:%d", handle); + + /* Check if handle is valid to avoid crashing */ + if ((handle == 0) || (handle > MAX_RFC_PORTS)) { + return (PORT_BAD_HANDLE); + } + + p_port = &rfc_cb.port.port[handle - 1]; + + if (!p_port->in_use || (p_port->state == PORT_STATE_CLOSED)) { + return (PORT_NOT_OPENED); + } + + if (!p_port->rfc.p_mcb || !p_port->rfc.p_mcb->peer_ready || + (!ignore_rfc_state ? (p_port->rfc.state != RFC_STATE_OPENED) : false)) { + return (PORT_LINE_ERR); + } + + memcpy (bd_addr, p_port->rfc.p_mcb->bd_addr, BD_ADDR_LEN); + if (p_lcid) { + *p_lcid = p_port->rfc.p_mcb->lcid; + } + + return (PORT_SUCCESS); +} + +/******************************************************************************* +** +** Function PORT_IsOpening +** +** Description This function returns TRUE if there is any RFCOMM connection +** opening in process. +** +** Parameters: TRUE if any connection opening is found +** bd_addr - bd_addr of the peer +** +*******************************************************************************/ +BOOLEAN PORT_IsOpening (BD_ADDR bd_addr) +{ + UINT8 xx, yy; + tRFC_MCB *p_mcb = NULL; + tPORT *p_port; + BOOLEAN found_port; + + /* Check for any rfc_mcb which is in the middle of opening. */ + for (xx = 0; xx < MAX_BD_CONNECTIONS; xx++) { + if ((rfc_cb.port.rfc_mcb[xx].state > RFC_MX_STATE_IDLE) && + (rfc_cb.port.rfc_mcb[xx].state < RFC_MX_STATE_CONNECTED)) { + memcpy (bd_addr, rfc_cb.port.rfc_mcb[xx].bd_addr, BD_ADDR_LEN); + return TRUE; + } + + if (rfc_cb.port.rfc_mcb[xx].state == RFC_MX_STATE_CONNECTED) { + found_port = FALSE; + p_mcb = &rfc_cb.port.rfc_mcb[xx]; + p_port = &rfc_cb.port.port[0]; + + for (yy = 0; yy < MAX_RFC_PORTS; yy++, p_port++) { + if (p_port->rfc.p_mcb == p_mcb) { + found_port = TRUE; + break; + } + } + + if ((!found_port) || + (found_port && (p_port->rfc.state < RFC_STATE_OPENED))) { + /* Port is not established yet. */ + memcpy (bd_addr, rfc_cb.port.rfc_mcb[xx].bd_addr, BD_ADDR_LEN); + return TRUE; + } + } + } + + return FALSE; +} + +/******************************************************************************* +** +** Function PORT_SetState +** +** Description This function configures connection according to the +** specifications in the tPORT_STATE structure. +** +** Parameters: handle - Handle returned in the RFCOMM_CreateConnection +** p_settings - Pointer to a tPORT_STATE structure containing +** configuration information for the connection. +** +** +*******************************************************************************/ +int PORT_SetState (UINT16 handle, tPORT_STATE *p_settings) +{ + tPORT *p_port; + UINT8 baud_rate; + + RFCOMM_TRACE_API ("PORT_SetState() handle:%d", handle); + + /* Check if handle is valid to avoid crashing */ + if ((handle == 0) || (handle > MAX_RFC_PORTS)) { + return (PORT_BAD_HANDLE); + } + + p_port = &rfc_cb.port.port[handle - 1]; + + if (!p_port->in_use || (p_port->state == PORT_STATE_CLOSED)) { + return (PORT_NOT_OPENED); + } + + if (p_port->line_status) { + return (PORT_LINE_ERR); + } + + RFCOMM_TRACE_API ("PORT_SetState() handle:%d FC_TYPE:0x%x", handle, + p_settings->fc_type); + + baud_rate = p_port->user_port_pars.baud_rate; + p_port->user_port_pars = *p_settings; + + /* for now we've been asked to pass only baud rate */ + if (baud_rate != p_settings->baud_rate) { + port_start_par_neg (p_port); + } + return (PORT_SUCCESS); +} + +/******************************************************************************* +** +** Function PORT_GetRxQueueCnt +** +** Description This function return number of buffers on the rx queue. +** +** Parameters: handle - Handle returned in the RFCOMM_CreateConnection +** p_rx_queue_count - Pointer to return queue count in. +** +*******************************************************************************/ +int PORT_GetRxQueueCnt (UINT16 handle, UINT16 *p_rx_queue_count) +{ + tPORT *p_port; + + RFCOMM_TRACE_API ("PORT_GetRxQueueCnt() handle:%d", handle); + + /* Check if handle is valid to avoid crashing */ + if ((handle == 0) || (handle > MAX_RFC_PORTS)) { + return (PORT_BAD_HANDLE); + } + + p_port = &rfc_cb.port.port[handle - 1]; + + if (!p_port->in_use || (p_port->state == PORT_STATE_CLOSED)) { + return (PORT_NOT_OPENED); + } + + if (p_port->line_status) { + return (PORT_LINE_ERR); + } + + *p_rx_queue_count = p_port->rx.queue_size; + + RFCOMM_TRACE_API ("PORT_GetRxQueueCnt() p_rx_queue_count:%d, p_port->rx.queue.count = %d", + *p_rx_queue_count, p_port->rx.queue_size); + + return (PORT_SUCCESS); +} + +/******************************************************************************* +** +** Function PORT_GetState +** +** Description This function is called to fill tPORT_STATE structure +** with the curremt control settings for the port +** +** Parameters: handle - Handle returned in the RFCOMM_CreateConnection +** p_settings - Pointer to a tPORT_STATE structure in which +** configuration information is returned. +** +*******************************************************************************/ +int PORT_GetState (UINT16 handle, tPORT_STATE *p_settings) +{ + tPORT *p_port; + + RFCOMM_TRACE_API ("PORT_GetState() handle:%d", handle); + + /* Check if handle is valid to avoid crashing */ + if ((handle == 0) || (handle > MAX_RFC_PORTS)) { + return (PORT_BAD_HANDLE); + } + + p_port = &rfc_cb.port.port[handle - 1]; + + if (!p_port->in_use || (p_port->state == PORT_STATE_CLOSED)) { + return (PORT_NOT_OPENED); + } + + if (p_port->line_status) { + return (PORT_LINE_ERR); + } + + *p_settings = p_port->user_port_pars; + return (PORT_SUCCESS); +} + + +/******************************************************************************* +** +** Function PORT_Control +** +** Description This function directs a specified connection to pass control +** control information to the peer device. +** +** Parameters: handle - Handle returned in the RFCOMM_CreateConnection +** signal = specify the function to be passed +** +*******************************************************************************/ +int PORT_Control (UINT16 handle, UINT8 signal) +{ + tPORT *p_port; + UINT8 old_modem_signal; + + RFCOMM_TRACE_API ("PORT_Control() handle:%d signal:0x%x", handle, signal); + + /* Check if handle is valid to avoid crashing */ + if ((handle == 0) || (handle > MAX_RFC_PORTS)) { + return (PORT_BAD_HANDLE); + } + + p_port = &rfc_cb.port.port[handle - 1]; + + if (!p_port->in_use || (p_port->state == PORT_STATE_CLOSED)) { + return (PORT_NOT_OPENED); + } + + old_modem_signal = p_port->local_ctrl.modem_signal; + p_port->local_ctrl.break_signal = 0; + + switch (signal) { + case PORT_SET_CTSRTS: + p_port->local_ctrl.modem_signal |= PORT_CTSRTS_ON; + break; + + case PORT_CLR_CTSRTS: + p_port->local_ctrl.modem_signal &= ~PORT_CTSRTS_ON; + break; + + case PORT_SET_DTRDSR: + p_port->local_ctrl.modem_signal |= PORT_DTRDSR_ON; + break; + + case PORT_CLR_DTRDSR: + p_port->local_ctrl.modem_signal &= ~PORT_DTRDSR_ON; + break; + + case PORT_SET_RI: + p_port->local_ctrl.modem_signal |= PORT_RING_ON; + break; + + case PORT_CLR_RI: + p_port->local_ctrl.modem_signal &= ~PORT_RING_ON; + break; + + case PORT_SET_DCD: + p_port->local_ctrl.modem_signal |= PORT_DCD_ON; + break; + + case PORT_CLR_DCD: + p_port->local_ctrl.modem_signal &= ~PORT_DCD_ON; + break; + } + + if (signal == PORT_BREAK) { + p_port->local_ctrl.break_signal = PORT_BREAK_DURATION; + } else if (p_port->local_ctrl.modem_signal == old_modem_signal) { + return (PORT_SUCCESS); + } + + port_start_control (p_port); + + RFCOMM_TRACE_EVENT ("PORT_Control DTR_DSR : %d, RTS_CTS : %d, RI : %d, DCD : %d", + ((p_port->local_ctrl.modem_signal & MODEM_SIGNAL_DTRDSR) ? 1 : 0), + ((p_port->local_ctrl.modem_signal & MODEM_SIGNAL_RTSCTS) ? 1 : 0), + ((p_port->local_ctrl.modem_signal & MODEM_SIGNAL_RI) ? 1 : 0), + ((p_port->local_ctrl.modem_signal & MODEM_SIGNAL_DCD) ? 1 : 0)); + + return (PORT_SUCCESS); +} + + +/******************************************************************************* +** +** Function PORT_FlowControl +** +** Description This function directs a specified connection to pass +** flow control message to the peer device. Enable flag passed +** shows if port can accept more data. +** +** Parameters: handle - Handle returned in the RFCOMM_CreateConnection +** enable - enables data flow +** +*******************************************************************************/ +int PORT_FlowControl (UINT16 handle, BOOLEAN enable) +{ + tPORT *p_port; + BOOLEAN old_fc; + UINT32 events; + + RFCOMM_TRACE_API ("PORT_FlowControl() handle:%d enable: %d", handle, enable); + + /* Check if handle is valid to avoid crashing */ + if ((handle == 0) || (handle > MAX_RFC_PORTS)) { + return (PORT_BAD_HANDLE); + } + + p_port = &rfc_cb.port.port[handle - 1]; + + if (!p_port->in_use || (p_port->state == PORT_STATE_CLOSED)) { + return (PORT_NOT_OPENED); + } + + if (!p_port->rfc.p_mcb) { + return (PORT_NOT_OPENED); + } + + p_port->rx.user_fc = !enable; + + if (p_port->rfc.p_mcb->flow == PORT_FC_CREDIT) { + if (!p_port->rx.user_fc) { + port_flow_control_peer(p_port, TRUE, 0); + } + } else { + old_fc = p_port->local_ctrl.fc; + + /* FC is set if user is set or peer is set */ + p_port->local_ctrl.fc = (p_port->rx.user_fc | p_port->rx.peer_fc); + + if (p_port->local_ctrl.fc != old_fc) { + port_start_control (p_port); + } + } + + /* Need to take care of the case when we could not deliver events */ + /* to the application because we were flow controlled */ + if (enable && (p_port->rx.queue_size != 0)) { + events = PORT_EV_RXCHAR; + if (p_port->rx_flag_ev_pending) { + p_port->rx_flag_ev_pending = FALSE; + events |= PORT_EV_RXFLAG; + } + + events &= p_port->ev_mask; + if (p_port->p_callback && events) { + p_port->p_callback (events, p_port->inx); + } + } + return (PORT_SUCCESS); +} + +#if 0 //Unused +/******************************************************************************* +** +** Function PORT_FlowControl_MaxCredit +** +** Description This function directs a specified connection to pass +** flow control message to the peer device. Enable flag passed +** shows if port can accept more data. It also sends max credit +** when data flow enabled +** +** Parameters: handle - Handle returned in the RFCOMM_CreateConnection +** enable - enables data flow +** +*******************************************************************************/ +int PORT_FlowControl_MaxCredit (UINT16 handle, BOOLEAN enable) +{ + tPORT *p_port; + BOOLEAN old_fc; + UINT32 events; + + RFCOMM_TRACE_API ("PORT_FlowControl() handle:%d enable: %d", handle, enable); + + /* Check if handle is valid to avoid crashing */ + if ((handle == 0) || (handle > MAX_RFC_PORTS)) { + return (PORT_BAD_HANDLE); + } + + p_port = &rfc_cb.port.port[handle - 1]; + + if (!p_port->in_use || (p_port->state == PORT_STATE_CLOSED)) { + return (PORT_NOT_OPENED); + } + + if (!p_port->rfc.p_mcb) { + return (PORT_NOT_OPENED); + } + + p_port->rx.user_fc = !enable; + + if (p_port->rfc.p_mcb->flow == PORT_FC_CREDIT) { + if (!p_port->rx.user_fc) { + port_flow_control_peer(p_port, TRUE, p_port->credit_rx); + } + } else { + old_fc = p_port->local_ctrl.fc; + + /* FC is set if user is set or peer is set */ + p_port->local_ctrl.fc = (p_port->rx.user_fc | p_port->rx.peer_fc); + + if (p_port->local_ctrl.fc != old_fc) { + port_start_control (p_port); + } + } + + /* Need to take care of the case when we could not deliver events */ + /* to the application because we were flow controlled */ + if (enable && (p_port->rx.queue_size != 0)) { + events = PORT_EV_RXCHAR; + if (p_port->rx_flag_ev_pending) { + p_port->rx_flag_ev_pending = FALSE; + events |= PORT_EV_RXFLAG; + } + + events &= p_port->ev_mask; + if (p_port->p_callback && events) { + p_port->p_callback (events, p_port->inx); + } + } + return (PORT_SUCCESS); +} +#endif + +/******************************************************************************* +** +** Function PORT_FlowControl_GiveCredit +** +** Description This function gives specified credits to the peer +** +** Parameters: handle - Handle returned in the RFCOMM_CreateConnection +** enable - enables data flow +** credits_given - credits to give +** +*******************************************************************************/ +int PORT_FlowControl_GiveCredit (UINT16 handle, BOOLEAN enable, UINT16 credits_given) +{ + tPORT *p_port; + BOOLEAN old_fc; + UINT32 events; + + RFCOMM_TRACE_DEBUG("%s handle:%d enable: %d, cred %d", __func__, handle, enable, credits_given); + + /* Check if handle is valid to avoid crashing */ + if ((handle == 0) || (handle > MAX_RFC_PORTS)) { + return (PORT_BAD_HANDLE); + } + + p_port = &rfc_cb.port.port[handle - 1]; + + if (!p_port->in_use || (p_port->state == PORT_STATE_CLOSED)) { + return (PORT_NOT_OPENED); + } + + if (!p_port->rfc.p_mcb) { + return (PORT_NOT_OPENED); + } + + p_port->rx.user_fc = !enable; + + if (p_port->rfc.p_mcb->flow == PORT_FC_CREDIT) { + if (!p_port->rx.user_fc) { + port_flow_control_peer(p_port, TRUE, credits_given); + } + } else { + assert(0); // karl: temporarily not allowed + old_fc = p_port->local_ctrl.fc; + + /* FC is set if user is set or peer is set */ + p_port->local_ctrl.fc = (p_port->rx.user_fc | p_port->rx.peer_fc); + + if (p_port->local_ctrl.fc != old_fc) { + port_start_control (p_port); + } + } + + /* Need to take care of the case when we could not deliver events */ + /* to the application because we were flow controlled */ + if (enable && (p_port->rx.queue_size != 0)) { + assert(0); // karl: temporarily not allowed + events = PORT_EV_RXCHAR; + if (p_port->rx_flag_ev_pending) { + p_port->rx_flag_ev_pending = FALSE; + events |= PORT_EV_RXFLAG; + } + + events &= p_port->ev_mask; + if (p_port->p_callback && events) { + p_port->p_callback (events, p_port->inx); + } + } + return (PORT_SUCCESS); +} + + +/******************************************************************************* +** +** Function PORT_GetModemStatus +** +** Description This function retrieves modem control signals. Normally +** application will call this function after a callback +** function is called with notification that one of signals +** has been changed. +** +** Parameters: handle - Handle returned in the RFCOMM_CreateConnection +** p_signal - specify the pointer to control signals info +** +*******************************************************************************/ +int PORT_GetModemStatus (UINT16 handle, UINT8 *p_signal) +{ + tPORT *p_port; + + if ((handle == 0) || (handle > MAX_RFC_PORTS)) { + return (PORT_BAD_HANDLE); + } + + p_port = &rfc_cb.port.port[handle - 1]; + + if (!p_port->in_use || (p_port->state == PORT_STATE_CLOSED)) { + return (PORT_NOT_OPENED); + } + + *p_signal = p_port->peer_ctrl.modem_signal; + + RFCOMM_TRACE_API ("PORT_GetModemStatus() handle:%d signal:%x", handle, *p_signal); + + return (PORT_SUCCESS); +} + + +/******************************************************************************* +** +** Function PORT_ClearError +** +** Description This function retreives information about a communications +** error and reports current status of a connection. The +** function should be called when an error occures to clear +** the connection error flag and to enable additional read +** and write operations. +** +** Parameters: handle - Handle returned in the RFCOMM_CreateConnection +** p_errors - pointer of the variable to receive error codes +** p_status - pointer to the tPORT_STATUS structur to receive +** connection status +** +*******************************************************************************/ +int PORT_ClearError (UINT16 handle, UINT16 *p_errors, tPORT_STATUS *p_status) +{ + tPORT *p_port; + + RFCOMM_TRACE_API ("PORT_ClearError() handle:%d", handle); + + if ((handle == 0) || (handle > MAX_RFC_PORTS)) { + return (PORT_BAD_HANDLE); + } + + p_port = &rfc_cb.port.port[handle - 1]; + + if (!p_port->in_use || (p_port->state == PORT_STATE_CLOSED)) { + return (PORT_NOT_OPENED); + } + + *p_errors = p_port->line_status; + + /* This is the only call to clear error status. We can not clear */ + /* connection failed status. To clean it port should be closed and reopened */ + p_port->line_status = (p_port->line_status & LINE_STATUS_FAILED); + + PORT_GetQueueStatus (handle, p_status); + return (PORT_SUCCESS); +} + + +/******************************************************************************* +** +** Function PORT_SendError +** +** Description This function send a communications error to the peer device +** +** Parameters: handle - Handle returned in the RFCOMM_CreateConnection +** errors - receive error codes +** +*******************************************************************************/ +int PORT_SendError (UINT16 handle, UINT8 errors) +{ + tPORT *p_port; + + RFCOMM_TRACE_API ("PORT_SendError() handle:%d errors:0x%x", handle, errors); + + if ((handle == 0) || (handle > MAX_RFC_PORTS)) { + return (PORT_BAD_HANDLE); + } + + p_port = &rfc_cb.port.port[handle - 1]; + + if (!p_port->in_use || (p_port->state == PORT_STATE_CLOSED)) { + return (PORT_NOT_OPENED); + } + + if (!p_port->rfc.p_mcb) { + return (PORT_NOT_OPENED); + } + + RFCOMM_LineStatusReq (p_port->rfc.p_mcb, p_port->dlci, errors); + return (PORT_SUCCESS); +} + + +/******************************************************************************* +** +** Function PORT_GetQueueStatus +** +** Description This function reports current status of a connection. +** +** Parameters: handle - Handle returned in the RFCOMM_CreateConnection +** p_status - pointer to the tPORT_STATUS structur to receive +** connection status +** +*******************************************************************************/ +int PORT_GetQueueStatus (UINT16 handle, tPORT_STATUS *p_status) +{ + tPORT *p_port; + + /* RFCOMM_TRACE_API ("PORT_GetQueueStatus() handle:%d", handle); */ + + if ((handle == 0) || (handle > MAX_RFC_PORTS)) { + return (PORT_BAD_HANDLE); + } + + p_port = &rfc_cb.port.port[handle - 1]; + + if (!p_port->in_use || (p_port->state == PORT_STATE_CLOSED)) { + return (PORT_NOT_OPENED); + } + + p_status->in_queue_size = (UINT16) p_port->rx.queue_size; + p_status->out_queue_size = (UINT16) p_port->tx.queue_size; + + p_status->mtu_size = (UINT16) p_port->peer_mtu; + + p_status->flags = 0; + + if (!(p_port->peer_ctrl.modem_signal & PORT_CTSRTS_ON)) { + p_status->flags |= PORT_FLAG_CTS_HOLD; + } + + if (!(p_port->peer_ctrl.modem_signal & PORT_DTRDSR_ON)) { + p_status->flags |= PORT_FLAG_DSR_HOLD; + } + + if (!(p_port->peer_ctrl.modem_signal & PORT_DCD_ON)) { + p_status->flags |= PORT_FLAG_RLSD_HOLD; + } + + return (PORT_SUCCESS); +} + + +/******************************************************************************* +** +** Function PORT_Purge +** +** Description This function discards all the data from the output or +** input queues of the specified connection. +** +** Parameters: handle - Handle returned in the RFCOMM_CreateConnection +** purge_flags - specify the action to take. +** +*******************************************************************************/ +int PORT_Purge (UINT16 handle, UINT8 purge_flags) +{ + tPORT *p_port; + BT_HDR *p_buf; + UINT16 count; + UINT32 events; + + RFCOMM_TRACE_API ("PORT_Purge() handle:%d flags:0x%x", handle, purge_flags); + + /* Check if handle is valid to avoid crashing */ + if ((handle == 0) || (handle > MAX_RFC_PORTS)) { + return (PORT_BAD_HANDLE); + } + + p_port = &rfc_cb.port.port[handle - 1]; + + if (!p_port->in_use || (p_port->state == PORT_STATE_CLOSED)) { + return (PORT_NOT_OPENED); + } + + if (purge_flags & PORT_PURGE_RXCLEAR) { + osi_mutex_global_lock(); /* to prevent missing credit */ + + count = fixed_queue_length(p_port->rx.queue); + + while ((p_buf = (BT_HDR *)fixed_queue_dequeue(p_port->rx.queue, 0)) != NULL) { + osi_free (p_buf); + } + + p_port->rx.queue_size = 0; + + osi_mutex_global_unlock(); + + /* If we flowed controlled peer based on rx_queue size enable data again */ + if (count) { + port_flow_control_peer (p_port, TRUE, count); + } + } + + if (purge_flags & PORT_PURGE_TXCLEAR) { + osi_mutex_global_lock(); /* to prevent tx.queue_size from being negative */ + + while ((p_buf = (BT_HDR *)fixed_queue_dequeue(p_port->tx.queue, 0)) != NULL) { + osi_free (p_buf); + } + + p_port->tx.queue_size = 0; + + osi_mutex_global_unlock(); + + events = PORT_EV_TXEMPTY; + + events |= port_flow_control_user (p_port); + + events &= p_port->ev_mask; + + if ((p_port->p_callback != NULL) && events) { + (p_port->p_callback)(events, p_port->inx); + } + } + + return (PORT_SUCCESS); +} + + +/******************************************************************************* +** +** Function PORT_ReadData +** +** Description Normally not GKI aware application will call this function +** after receiving PORT_EV_RXCHAR event. +** +** Parameters: handle - Handle returned in the RFCOMM_CreateConnection +** p_data - Data area +** max_len - Byte count requested +** p_len - Byte count received +** +*******************************************************************************/ +int PORT_ReadData (UINT16 handle, char *p_data, UINT16 max_len, UINT16 *p_len) +{ + tPORT *p_port; + BT_HDR *p_buf; + UINT16 count; + + RFCOMM_TRACE_API ("PORT_ReadData() handle:%d max_len:%d", handle, max_len); + + /* Initialize this in case of an error */ + *p_len = 0; + + /* Check if handle is valid to avoid crashing */ + if ((handle == 0) || (handle > MAX_RFC_PORTS)) { + return (PORT_BAD_HANDLE); + } + + p_port = &rfc_cb.port.port[handle - 1]; + + if (!p_port->in_use || (p_port->state == PORT_STATE_CLOSED)) { + return (PORT_NOT_OPENED); + } + + if (p_port->line_status) { + return (PORT_LINE_ERR); + } + + if (fixed_queue_is_empty(p_port->rx.queue)){ + return (PORT_SUCCESS); + } + + count = 0; + + while (max_len) + { + p_buf = (BT_HDR *)fixed_queue_try_peek_first(p_port->rx.queue); + if (p_buf == NULL){ + break; + } + + if (p_buf->len > max_len) { + memcpy (p_data, (UINT8 *)(p_buf + 1) + p_buf->offset, max_len); + p_buf->offset += max_len; + p_buf->len -= max_len; + + *p_len += max_len; + + osi_mutex_global_lock(); + + p_port->rx.queue_size -= max_len; + + osi_mutex_global_unlock(); + + break; + } else { + memcpy (p_data, (UINT8 *)(p_buf + 1) + p_buf->offset, p_buf->len); + + *p_len += p_buf->len; + max_len -= p_buf->len; + + osi_mutex_global_lock(); + + p_port->rx.queue_size -= p_buf->len; + + if (max_len) { + p_data += p_buf->len; + } + + osi_free(fixed_queue_dequeue(p_port->rx.queue, 0)); + + osi_mutex_global_unlock(); + + count++; + } + } + + if (*p_len == 1) { + RFCOMM_TRACE_EVENT ("PORT_ReadData queue:%d returned:%d %x", p_port->rx.queue_size, *p_len, (p_data[0])); + } else { + RFCOMM_TRACE_EVENT ("PORT_ReadData queue:%d returned:%d", p_port->rx.queue_size, *p_len); + } + + /* If rfcomm suspended traffic from the peer based on the rx_queue_size */ + /* check if it can be resumed now */ + port_flow_control_peer (p_port, TRUE, count); + + return (PORT_SUCCESS); +} + + +/******************************************************************************* +** +** Function PORT_Read +** +** Description Normally application will call this function after receiving +** PORT_EV_RXCHAR event. +** +** Parameters: handle - Handle returned in the RFCOMM_CreateConnection +** pp_buf - pointer to address of buffer with data, +** +*******************************************************************************/ +int PORT_Read (UINT16 handle, BT_HDR **pp_buf) +{ + tPORT *p_port; + BT_HDR *p_buf; + + RFCOMM_TRACE_API ("PORT_Read() handle:%d", handle); + + /* Check if handle is valid to avoid crashing */ + if ((handle == 0) || (handle > MAX_RFC_PORTS)) { + return (PORT_BAD_HANDLE); + } + p_port = &rfc_cb.port.port[handle - 1]; + + if (!p_port->in_use || (p_port->state == PORT_STATE_CLOSED)) { + return (PORT_NOT_OPENED); + } + + if (p_port->line_status) { + return (PORT_LINE_ERR); + } + + osi_mutex_global_lock(); + + p_buf = (BT_HDR *)fixed_queue_dequeue(p_port->rx.queue, 0); + if (p_buf) { + p_port->rx.queue_size -= p_buf->len; + + osi_mutex_global_unlock(); + + /* If rfcomm suspended traffic from the peer based on the rx_queue_size */ + /* check if it can be resumed now */ + port_flow_control_peer (p_port, TRUE, 1); + } else { + osi_mutex_global_unlock(); + } + + *pp_buf = p_buf; + return (PORT_SUCCESS); +} + + +/******************************************************************************* +** +** Function port_write +** +** Description This function when a data packet is received from the apper +** layer task. +** +** Parameters: p_port - pointer to address of port control block +** p_buf - pointer to address of buffer with data, +** +*******************************************************************************/ +static int port_write (tPORT *p_port, BT_HDR *p_buf) +{ + /* We should not allow to write data in to server port when connection is not opened */ + if (p_port->is_server && (p_port->rfc.state != RFC_STATE_OPENED)) { + osi_free (p_buf); + return (PORT_CLOSED); + } + + /* Keep the data in pending queue if peer does not allow data, or */ + /* Peer is not ready or Port is not yet opened or initial port control */ + /* command has not been sent */ + if (p_port->tx.peer_fc + || !p_port->rfc.p_mcb + || !p_port->rfc.p_mcb->peer_ready + || (p_port->rfc.state != RFC_STATE_OPENED) + || ((p_port->port_ctrl & (PORT_CTRL_REQ_SENT | PORT_CTRL_IND_RECEIVED)) != + (PORT_CTRL_REQ_SENT | PORT_CTRL_IND_RECEIVED))) { + if ((p_port->tx.queue_size > PORT_TX_CRITICAL_WM) + || (fixed_queue_length(p_port->tx.queue) > PORT_TX_BUF_CRITICAL_WM)){ + RFCOMM_TRACE_WARNING ("PORT_Write: Queue size: %d", + p_port->tx.queue_size); + + osi_free (p_buf); + + if ((p_port->p_callback != NULL) && (p_port->ev_mask & PORT_EV_ERR)) { + p_port->p_callback (PORT_EV_ERR, p_port->inx); + } + + return (PORT_TX_FULL); + } + + RFCOMM_TRACE_EVENT ("PORT_Write : Data is enqued. flow disabled %d peer_ready %d state %d ctrl_state %x", + p_port->tx.peer_fc, + (p_port->rfc.p_mcb && p_port->rfc.p_mcb->peer_ready), + p_port->rfc.state, + p_port->port_ctrl); + + fixed_queue_enqueue(p_port->tx.queue, p_buf, FIXED_QUEUE_MAX_TIMEOUT); + p_port->tx.queue_size += p_buf->len; + + return (PORT_CMD_PENDING); + } else { + RFCOMM_TRACE_EVENT ("PORT_Write : Data is being sent"); + + RFCOMM_DataReq (p_port->rfc.p_mcb, p_port->dlci, p_buf); + return (PORT_SUCCESS); + } +} + +/******************************************************************************* +** +** Function PORT_Write +** +** Description This function when a data packet is received from the apper +** layer task. +** +** Parameters: handle - Handle returned in the RFCOMM_CreateConnection +** pp_buf - pointer to address of buffer with data, +** +*******************************************************************************/ +int PORT_Write (UINT16 handle, BT_HDR *p_buf) +{ + tPORT *p_port; + UINT32 event = 0; + int rc; + + RFCOMM_TRACE_API ("PORT_Write() handle:%d", handle); + + /* Check if handle is valid to avoid crashing */ + if ((handle == 0) || (handle > MAX_RFC_PORTS)) { + osi_free (p_buf); + return (PORT_BAD_HANDLE); + } + + p_port = &rfc_cb.port.port[handle - 1]; + + if (!p_port->in_use || (p_port->state == PORT_STATE_CLOSED)) { + osi_free (p_buf); + return (PORT_NOT_OPENED); + } + + if (p_port->line_status) { + RFCOMM_TRACE_WARNING ("PORT_Write: Data dropped line_status:0x%x", + p_port->line_status); + osi_free (p_buf); + return (PORT_LINE_ERR); + } + + rc = port_write (p_port, p_buf); + event |= port_flow_control_user (p_port); + + switch (rc) { + case PORT_TX_FULL: + event |= PORT_EV_ERR; + break; + + case PORT_SUCCESS: + event |= (PORT_EV_TXCHAR | PORT_EV_TXEMPTY); + break; + } + /* Mask out all events that are not of interest to user */ + event &= p_port->ev_mask; + + /* Send event to the application */ + if (p_port->p_callback && event) { + (p_port->p_callback)(event, p_port->inx); + } + + return (PORT_SUCCESS); +} +/******************************************************************************* +** +** Function PORT_WriteDataCO +** +** Description Normally not GKI aware application will call this function +** to send data to the port by callout functions +** +** Parameters: handle - Handle returned in the RFCOMM_CreateConnection +** fd - socket fd +** p_len - Byte count returned +** +*******************************************************************************/ +int PORT_WriteDataCO (UINT16 handle, int *p_len, int len, UINT8 *p_data) +{ + + tPORT *p_port; + BT_HDR *p_buf; + UINT32 event = 0; + int rc = 0; + UINT16 length; + + RFCOMM_TRACE_API ("PORT_WriteDataCO() handle:%d", handle); + *p_len = 0; + + /* Check if handle is valid to avoid crashing */ + if ((handle == 0) || (handle > MAX_RFC_PORTS)) { + return (PORT_BAD_HANDLE); + } + p_port = &rfc_cb.port.port[handle - 1]; + + if (!p_port->in_use || (p_port->state == PORT_STATE_CLOSED)) { + RFCOMM_TRACE_WARNING ("PORT_WriteDataByFd() no port state:%d", p_port->state); + return (PORT_NOT_OPENED); + } + + if (!p_port->peer_mtu) { + RFCOMM_TRACE_ERROR ("PORT_WriteDataByFd() peer_mtu:%d", p_port->peer_mtu); + return (PORT_UNKNOWN_ERROR); + } + int available = 0; + available = len; + if (available == 0) { + return PORT_SUCCESS; + } + /* Length for each buffer is the smaller of GKI buffer, peer MTU, or max_len */ + length = RFCOMM_DATA_BUF_SIZE - + (UINT16)(sizeof(BT_HDR) + L2CAP_MIN_OFFSET + RFCOMM_DATA_OVERHEAD); + + while (available) { + /* if we're over buffer high water mark, we're done */ + if ((p_port->tx.queue_size > PORT_TX_HIGH_WM) + || (fixed_queue_length(p_port->tx.queue) > PORT_TX_BUF_HIGH_WM)) { + port_flow_control_user(p_port); + event |= PORT_EV_FC; + RFCOMM_TRACE_EVENT ("tx queue is full,tx.queue_size:%d,tx.queue.count:%d,available:%d", + p_port->tx.queue_size, fixed_queue_length(p_port->tx.queue), available); + break; + } + + /* continue with rfcomm data write */ + if (p_port->peer_mtu < length) { + length = p_port->peer_mtu; + } + + if (available < (int)length) { + length = (UINT16)available; + } + + UINT16 alloc_size = (UINT16)(sizeof(BT_HDR) + L2CAP_MIN_OFFSET + RFCOMM_DATA_OVERHEAD + + length + L2CAP_FCS_LEN); + p_buf = (BT_HDR *)osi_malloc(alloc_size); + if (!p_buf) { + RFCOMM_TRACE_EVENT ("PORT_WriteDataCO: out of heap."); + break; + } + + p_buf->offset = L2CAP_MIN_OFFSET + RFCOMM_MIN_OFFSET; + p_buf->layer_specific = handle; + + p_buf->len = length; + p_buf->event = BT_EVT_TO_BTU_SP_DATA; + + memcpy ((UINT8 *)(p_buf + 1) + p_buf->offset, p_data, length); + + RFCOMM_TRACE_EVENT ("PORT_WriteData %d bytes", length); + + rc = port_write (p_port, p_buf); + + /* If queue went below the threshold need to send flow control */ + event |= port_flow_control_user (p_port); + + if (rc == PORT_SUCCESS) { + event |= PORT_EV_TXCHAR; + } + + if ((rc != PORT_SUCCESS) && (rc != PORT_CMD_PENDING)) { + break; + } + + *p_len += length; + available -= (int)length; + p_data += length; + } + if (!available && (rc != PORT_CMD_PENDING) && (rc != PORT_TX_QUEUE_DISABLED)) { + event |= PORT_EV_TXEMPTY; + } + + /* Mask out all events that are not of interest to user */ + event &= p_port->ev_mask; + + /* Send event to the application */ + if (p_port->p_callback && event) { + (p_port->p_callback)(event, p_port->inx); + } + + return (PORT_SUCCESS); +} + + + +/******************************************************************************* +** +** Function PORT_WriteData +** +** Description Normally not GKI aware application will call this function +** to send data to the port. +** +** Parameters: handle - Handle returned in the RFCOMM_CreateConnection +** p_data - Data area +** max_len - Byte count requested +** p_len - Byte count received +** +*******************************************************************************/ +int PORT_WriteData (UINT16 handle, char *p_data, UINT16 max_len, UINT16 *p_len) +{ + tPORT *p_port; + BT_HDR *p_buf; + UINT32 event = 0; + int rc = 0; + UINT16 length; + + RFCOMM_TRACE_API ("PORT_WriteData() max_len:%d", max_len); + + *p_len = 0; + + /* Check if handle is valid to avoid crashing */ + if ((handle == 0) || (handle > MAX_RFC_PORTS)) { + return (PORT_BAD_HANDLE); + } + p_port = &rfc_cb.port.port[handle - 1]; + + if (!p_port->in_use || (p_port->state == PORT_STATE_CLOSED)) { + RFCOMM_TRACE_WARNING ("PORT_WriteData() no port state:%d", p_port->state); + return (PORT_NOT_OPENED); + } + + if (!max_len || !p_port->peer_mtu) { + RFCOMM_TRACE_ERROR ("PORT_WriteData() peer_mtu:%d", p_port->peer_mtu); + return (PORT_UNKNOWN_ERROR); + } + + /* Length for each buffer is the smaller of GKI buffer, peer MTU, or max_len */ + length = RFCOMM_DATA_BUF_SIZE - + (UINT16)(sizeof(BT_HDR) + L2CAP_MIN_OFFSET + RFCOMM_DATA_OVERHEAD); + + /* If there are buffers scheduled for transmission check if requested */ + /* data fits into the end of the queue */ + osi_mutex_global_lock(); + + if (((p_buf = (BT_HDR *)fixed_queue_try_peek_last(p_port->tx.queue)) != NULL) + && ((p_buf->len + max_len) <= p_port->peer_mtu) + && ((p_buf->len + max_len) <= length)) { + memcpy ((UINT8 *)(p_buf + 1) + p_buf->offset + p_buf->len, p_data, max_len); + p_port->tx.queue_size += max_len; + + *p_len = max_len; + p_buf->len += max_len; + + osi_mutex_global_unlock(); + + return (PORT_SUCCESS); + } + + osi_mutex_global_unlock(); + + while (max_len) { + /* if we're over buffer high water mark, we're done */ + if ((p_port->tx.queue_size > PORT_TX_HIGH_WM) + || (fixed_queue_length(p_port->tx.queue) > PORT_TX_BUF_HIGH_WM)) { + break; + } + + /* continue with rfcomm data write */ + p_buf = (BT_HDR *)osi_malloc(RFCOMM_DATA_BUF_SIZE); + if (!p_buf) { + break; + } + + p_buf->offset = L2CAP_MIN_OFFSET + RFCOMM_MIN_OFFSET; + p_buf->layer_specific = handle; + + if (p_port->peer_mtu < length) { + length = p_port->peer_mtu; + } + if (max_len < length) { + length = max_len; + } + p_buf->len = length; + p_buf->event = BT_EVT_TO_BTU_SP_DATA; + + memcpy ((UINT8 *)(p_buf + 1) + p_buf->offset, p_data, length); + + RFCOMM_TRACE_EVENT ("PORT_WriteData %d bytes", length); + + rc = port_write (p_port, p_buf); + + /* If queue went below the threashold need to send flow control */ + event |= port_flow_control_user (p_port); + + if (rc == PORT_SUCCESS) { + event |= PORT_EV_TXCHAR; + } + + if ((rc != PORT_SUCCESS) && (rc != PORT_CMD_PENDING)) { + break; + } + + *p_len += length; + max_len -= length; + p_data += length; + + } + if (!max_len && (rc != PORT_CMD_PENDING) && (rc != PORT_TX_QUEUE_DISABLED)) { + event |= PORT_EV_TXEMPTY; + } + + /* Mask out all events that are not of interest to user */ + event &= p_port->ev_mask; + + /* Send event to the application */ + if (p_port->p_callback && event) { + (p_port->p_callback)(event, p_port->inx); + } + + return (PORT_SUCCESS); +} + + +/******************************************************************************* +** +** Function PORT_Test +** +** Description Application can call this function to send RFCOMM Test frame +** +** Parameters: handle - Handle returned in the RFCOMM_CreateConnection +** p_data - Data area +** max_len - Byte count requested +** +*******************************************************************************/ +int PORT_Test (UINT16 handle, UINT8 *p_data, UINT16 len) +{ + BT_HDR *p_buf; + tPORT *p_port; + + RFCOMM_TRACE_API ("PORT_Test() len:%d", len); + + if ((handle == 0) || (handle > MAX_RFC_PORTS)) { + return (PORT_BAD_HANDLE); + } + p_port = &rfc_cb.port.port[handle - 1]; + + if (!p_port->in_use || (p_port->state == PORT_STATE_CLOSED)) { + return (PORT_NOT_OPENED); + } + + if (len > ((p_port->mtu == 0) ? RFCOMM_DEFAULT_MTU : p_port->mtu)) { + return (PORT_UNKNOWN_ERROR); + } + + if ((p_buf = (BT_HDR *)osi_malloc(RFCOMM_CMD_BUF_SIZE)) != NULL) { + + p_buf->offset = L2CAP_MIN_OFFSET + RFCOMM_MIN_OFFSET + 2; + p_buf->len = len; + + memcpy ((UINT8 *)(p_buf + 1) + p_buf->offset, p_data, p_buf->len); + + rfc_send_test (p_port->rfc.p_mcb, TRUE, p_buf); + return (PORT_SUCCESS); + } else { + return (PORT_NO_MEM); + } +} + +/******************************************************************************* +** +** Function RFCOMM_Init +** +** Description This function is called to initialize RFCOMM layer +** +** Returns status +** +*******************************************************************************/ +bt_status_t RFCOMM_Init (void) +{ +#if RFC_DYNAMIC_MEMORY == TRUE + rfc_cb_ptr = (tRFC_CB *)osi_malloc(sizeof(tRFC_CB)); + if (rfc_cb_ptr == NULL) { + return BT_STATUS_NOMEM; + } +#endif /* #if (RFC_DYNAMIC_MEMORY) */ + memset (&rfc_cb, 0, sizeof (tRFC_CB)); /* Init RFCOMM control block */ + + rfc_cb.rfc.last_mux = MAX_BD_CONNECTIONS; + +#if defined(RFCOMM_INITIAL_TRACE_LEVEL) + rfc_cb.trace_level = RFCOMM_INITIAL_TRACE_LEVEL; +#else + rfc_cb.trace_level = BT_TRACE_LEVEL_NONE; /* No traces */ +#endif + + rfcomm_l2cap_if_init (); + return BT_STATUS_SUCCESS; +} + +/******************************************************************************* +** +** Function RFCOMM_Deinit +** +** Description This function is called to deinitialize the control block +** for this layer. +** +** Returns void +** +*******************************************************************************/ +void RFCOMM_Deinit(void) +{ +#if RFC_DYNAMIC_MEMORY == TRUE + if (rfc_cb_ptr){ + osi_free(rfc_cb_ptr); + rfc_cb_ptr = NULL; + } +#endif +} + +/******************************************************************************* +** +** Function PORT_SetTraceLevel +** +** Description This function sets the trace level for RFCOMM. If called with +** a value of 0xFF, it simply reads the current trace level. +** +** Returns the new (current) trace level +** +*******************************************************************************/ +UINT8 PORT_SetTraceLevel (UINT8 new_level) +{ + if (new_level != 0xFF) { + rfc_cb.trace_level = new_level; + } + + return (rfc_cb.trace_level); +} + +/******************************************************************************* +** +** Function PORT_GetResultString +** +** Description This function returns the human-readable string for a given +** result code. +** +** Returns a pointer to the human-readable string for the given result. +** +*******************************************************************************/ +const char *PORT_GetResultString (const uint8_t result_code) +{ + if (result_code > PORT_ERR_MAX) { + return result_code_strings[PORT_ERR_MAX]; + } + + return result_code_strings[result_code]; +} + +/******************************************************************************* +** +** Function PORT_SetL2capErtm +** +** Description This function sets whether RFCOMM uses L2CAP ERTM. +** +** Returns void +** +*******************************************************************************/ +void PORT_SetL2capErtm (BOOLEAN enable_l2cap_ertm) +{ + rfc_cb.port.enable_l2cap_ertm = enable_l2cap_ertm; +} + +#endif ///(defined RFCOMM_INCLUDED && RFCOMM_INCLUDED == TRUE) diff --git a/lib/bt/host/bluedroid/stack/rfcomm/port_rfc.c b/lib/bt/host/bluedroid/stack/rfcomm/port_rfc.c new file mode 100644 index 00000000..c925ed9c --- /dev/null +++ b/lib/bt/host/bluedroid/stack/rfcomm/port_rfc.c @@ -0,0 +1,1132 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This module contains functions for port emulation entity and RFCOMM + * communications + * + ******************************************************************************/ +#include + +#include "common/bt_target.h" +#include "stack/rfcdefs.h" +#include "stack/port_api.h" +#include "btm_int.h" +#include "stack/btm_api.h" +#include "port_int.h" +#include "rfc_int.h" +#include "common/bt_defs.h" +#include "osi/mutex.h" +#include "osi/allocator.h" +#if (defined RFCOMM_INCLUDED && RFCOMM_INCLUDED == TRUE) +/* +** Local function definitions +*/ +UINT32 port_rfc_send_tx_data (tPORT *p_port); +void port_rfc_closed (tPORT *p_port, UINT8 res); +void port_get_credits (tPORT *p_port, UINT8 k); + + +/******************************************************************************* +** +** Function port_open_continue +** +** Description This function is called after security manager completes +** required security checks. +** +** Returns void +** +*******************************************************************************/ +int port_open_continue (tPORT *p_port) +{ + tRFC_MCB *p_mcb; + + RFCOMM_TRACE_EVENT ("port_open_continue, p_port:%p", p_port); + + /* Check if multiplexer channel has already been established */ + if ((p_mcb = rfc_alloc_multiplexer_channel (p_port->bd_addr, TRUE)) == NULL) { + RFCOMM_TRACE_WARNING ("port_open_continue no mx channel"); + port_release_port (p_port); + return (PORT_NO_RESOURCES); + } + + p_port->rfc.p_mcb = p_mcb; + + p_mcb->port_inx[p_port->dlci] = p_port->inx; + + /* Connection is up and we know local and remote features, select MTU */ + port_select_mtu (p_port); + + if (p_mcb->state == RFC_MX_STATE_CONNECTED) { + RFCOMM_ParNegReq (p_mcb, p_port->dlci, p_port->mtu); + } else if ((p_mcb->state == RFC_MX_STATE_IDLE) + || (p_mcb->state == RFC_MX_STATE_DISC_WAIT_UA)) { + /* In RFC_MX_STATE_IDLE state, MX state machine will create connection */ + /* In RFC_MX_STATE_DISC_WAIT_UA state, MX state machine will recreate connection */ + /* after disconnecting is completed */ + RFCOMM_StartReq (p_mcb); + } else { + /* MX state machine ignores RFC_MX_EVENT_START_REQ in these states */ + /* When it enters RFC_MX_STATE_CONNECTED, it will check any openning ports */ + RFCOMM_TRACE_DEBUG ("port_open_continue: mx state(%d) mx channel is openning", p_mcb->state); + } + return (PORT_SUCCESS); +} + + +/******************************************************************************* +** +** Function port_start_control +** +** Description This function is called in the BTU_TASK context to +** send control information +** +** Returns void +** +*******************************************************************************/ +void port_start_control (tPORT *p_port) +{ + tRFC_MCB *p_mcb = p_port->rfc.p_mcb; + + if (p_mcb == NULL) { + return; + } + + RFCOMM_ControlReq (p_mcb, p_port->dlci, &p_port->local_ctrl); +} + + +/******************************************************************************* +** +** Function port_start_par_neg +** +** Description This function is called in the BTU_TASK context to +** send configuration information +** +** Returns void +** +*******************************************************************************/ +void port_start_par_neg (tPORT *p_port) +{ + tRFC_MCB *p_mcb = p_port->rfc.p_mcb; + + if (p_mcb == NULL) { + return; + } + + RFCOMM_PortNegReq (p_mcb, p_port->dlci, &p_port->user_port_pars); +} + + +/******************************************************************************* +** +** Function port_start_close +** +** Description This function is called in the BTU_TASK context to +** release DLC +** +** Returns void +** +*******************************************************************************/ +void port_start_close (tPORT *p_port) +{ + tRFC_MCB *p_mcb = p_port->rfc.p_mcb; + UINT8 old_signals; + UINT32 events = 0; + + /* At first indicate to the user that signals on the connection were dropped */ + p_port->line_status |= LINE_STATUS_FAILED; + old_signals = p_port->peer_ctrl.modem_signal; + + p_port->peer_ctrl.modem_signal &= ~(PORT_DTRDSR_ON | PORT_CTSRTS_ON | PORT_DCD_ON); + + events |= port_get_signal_changes (p_port, old_signals, p_port->peer_ctrl.modem_signal); + + if (p_port->ev_mask & PORT_EV_CONNECT_ERR) { + events |= PORT_EV_CONNECT_ERR; + } + + if (p_port->ev_mask & PORT_EV_ERR) { + events |= PORT_EV_ERR; + } + + if ((p_port->p_callback != NULL) && events) { + p_port->p_callback (events, p_port->inx); + } + + + /* Check if RFCOMM side has been closed while the message was queued */ + if ((p_mcb == NULL) || (p_port->rfc.state == RFC_STATE_CLOSED)) { + /* Call management callback function before calling port_release_port() to clear tPort */ + if (p_port->p_mgmt_callback) { + p_port->p_mgmt_callback (PORT_CLOSED, p_port->inx, NULL); + } + + port_release_port (p_port); + } else { + RFCOMM_DlcReleaseReq (p_mcb, p_port->dlci); + } +} + + +/******************************************************************************* +** +** Function PORT_StartCnf +** +** Description This function is called from the RFCOMM layer when +** establishing of the multiplexer channel is completed. +** Continue establishing of the connection for all ports that +** are in the OPENING state +** +*******************************************************************************/ +void PORT_StartCnf (tRFC_MCB *p_mcb, UINT16 result) +{ + tPORT *p_port; + int i; + BOOLEAN no_ports_up = TRUE; + + RFCOMM_TRACE_EVENT ("PORT_StartCnf result:%d", result); + + p_port = &rfc_cb.port.port[0]; + for (i = 0; i < MAX_RFC_PORTS; i++, p_port++) { + if (p_port->rfc.p_mcb == p_mcb) { + no_ports_up = FALSE; + + if (result == RFCOMM_SUCCESS) { + RFCOMM_ParNegReq (p_mcb, p_port->dlci, p_port->mtu); + } else { + RFCOMM_TRACE_WARNING ("PORT_StartCnf failed result:%d", result); + + /* Warning: result is also set to 4 when l2cap connection + fails due to l2cap connect cnf (no_resources) */ + if ( result == HCI_ERR_PAGE_TIMEOUT ) { + p_port->error = PORT_PAGE_TIMEOUT; + } else { + p_port->error = PORT_START_FAILED; + } + + rfc_release_multiplexer_channel (p_mcb); + p_port->rfc.p_mcb = NULL; + + /* Send event to the application */ + if (p_port->p_callback && (p_port->ev_mask & PORT_EV_CONNECT_ERR)) { + (p_port->p_callback)(PORT_EV_CONNECT_ERR, p_port->inx); + } + + if (p_port->p_mgmt_callback) { + p_port->p_mgmt_callback (PORT_START_FAILED, p_port->inx, NULL); + } + + port_release_port (p_port); + } + } + } + + /* There can be a situation when after starting connection, user closes the */ + /* port, we can catch it here to close multiplexor channel */ + if (no_ports_up) { + rfc_check_mcb_active (p_mcb); + } +} + + +/******************************************************************************* +** +** Function PORT_StartInd +** +** Description This function is called from the RFCOMM layer when +** some peer device wants to establish a multiplexer +** connection. Check if there are any ports open with this +** or not assigned multiplexer. +** +*******************************************************************************/ +void PORT_StartInd (tRFC_MCB *p_mcb) +{ + tPORT *p_port; + int i; + + RFCOMM_TRACE_EVENT ("PORT_StartInd"); + + p_port = &rfc_cb.port.port[0]; + for (i = 0; i < MAX_RFC_PORTS; i++, p_port++) { + if ((p_port->rfc.p_mcb == NULL) + || (p_port->rfc.p_mcb == p_mcb)) { + RFCOMM_TRACE_DEBUG("PORT_StartInd, RFCOMM_StartRsp RFCOMM_SUCCESS: p_mcb:%p", p_mcb); + RFCOMM_StartRsp (p_mcb, RFCOMM_SUCCESS); + return; + } + } + RFCOMM_StartRsp (p_mcb, RFCOMM_ERROR); +} + + +/******************************************************************************* +** +** Function PORT_ParNegInd +** +** Description This function is called from the RFCOMM layer to change +** DLCI parameters (currently only MTU is negotiated). +** If can not find the port do not accept the request. +** Otherwise save the MTU size supported by the peer. +** +*******************************************************************************/ +void PORT_ParNegInd (tRFC_MCB *p_mcb, UINT8 dlci, UINT16 mtu, UINT8 cl, UINT8 k) +{ + tPORT *p_port = port_find_mcb_dlci_port (p_mcb, dlci); + UINT8 our_cl; + UINT8 our_k; + + RFCOMM_TRACE_EVENT ("PORT_ParNegInd dlci:%d mtu:%d", dlci, mtu); + + if (!p_port) { + /* This can be a first request for this port */ + p_port = port_find_dlci_port (dlci); + if (!p_port) { + /* If the port cannot be opened, send a DM. Per Errata 1205 */ + rfc_send_dm(p_mcb, dlci, FALSE); + /* check if this is the last port open, some headsets have + problem, they don't disconnect if we send DM */ + rfc_check_mcb_active( p_mcb ); + RFCOMM_TRACE_EVENT( "PORT_ParNegInd: port not found" ); + return; + } + p_mcb->port_inx[dlci] = p_port->inx; + } + + memcpy (p_port->bd_addr, p_mcb->bd_addr, BD_ADDR_LEN); + + /* Connection is up and we know local and remote features, select MTU */ + port_select_mtu (p_port); + + p_port->rfc.p_mcb = p_mcb; + p_port->mtu = (p_port->mtu < mtu) ? p_port->mtu : mtu; + p_port->peer_mtu = p_port->mtu; + + /* Negotiate the flow control mechanism. If flow control mechanism for */ + /* mux has not been set yet, set it now. If either we or peer wants TS 07.10, */ + /* use that. Otherwise both must want credit based, so use that. If flow is */ + /* already defined for this mux, we respond with that value. */ + if (p_mcb->flow == PORT_FC_UNDEFINED) { + if ((PORT_FC_DEFAULT == PORT_FC_TS710) || (cl == RFCOMM_PN_CONV_LAYER_TYPE_1)) { + p_mcb->flow = PORT_FC_TS710; + } else { + p_mcb->flow = PORT_FC_CREDIT; + } + } + + /* Regardless of our flow control mechanism, if the PN cl is zero, we must */ + /* respond with zero. "A responding implementation must set this field to 14 */ + /* if (and only if) the PN request was 15." This could happen if a PN is sent */ + /* after the DLCI is already established-- the PN in that case must have cl = 0. */ + /* See RFCOMM spec 5.5.3 */ + if (cl == RFCOMM_PN_CONV_LAYER_TYPE_1) { + our_cl = RFCOMM_PN_CONV_LAYER_TYPE_1; + our_k = 0; + } else if (p_mcb->flow == PORT_FC_CREDIT) { + /* get credits */ + port_get_credits (p_port, k); + + /* Set convergence layer and number of credits (k) */ + our_cl = RFCOMM_PN_CONV_LAYER_CBFC_R; + our_k = (p_port->credit_rx_max < RFCOMM_K_MAX) ? p_port->credit_rx_max : RFCOMM_K_MAX; + p_port->credit_rx = our_k; + } else { + /* must not be using credit based flow control; use TS 7.10 */ + our_cl = RFCOMM_PN_CONV_LAYER_TYPE_1; + our_k = 0; + } + RFCOMM_ParNegRsp (p_mcb, dlci, p_port->mtu, our_cl, our_k); +} + + +/******************************************************************************* +** +** Function PORT_ParNegCnf +** +** Description This function is called from the RFCOMM layer to change +** DLCI parameters (currently only MTU is negotiated). +** Save the MTU size supported by the peer. +** If the confirmation is received during the port opening +** procedure send EstablishRequest to continue. +** +*******************************************************************************/ +void PORT_ParNegCnf (tRFC_MCB *p_mcb, UINT8 dlci, UINT16 mtu, UINT8 cl, UINT8 k) +{ + tPORT *p_port = port_find_mcb_dlci_port (p_mcb, dlci); + + RFCOMM_TRACE_EVENT ("PORT_ParNegCnf dlci:%d mtu:%d cl: %d k: %d", dlci, mtu, cl, k); + + if (!p_port) { + return; + } + + /* Flow control mechanism not set yet. Negotiate flow control mechanism. */ + if (p_mcb->flow == PORT_FC_UNDEFINED) { + /* Our stack is configured for TS07.10 and they responded with credit-based. */ + /* This is illegal-- negotiation fails. */ + if ((PORT_FC_DEFAULT == PORT_FC_TS710) && (cl == RFCOMM_PN_CONV_LAYER_CBFC_R)) { + rfc_send_disc (p_mcb, p_port->dlci); + rfc_port_closed (p_port); + return; + } + /* Our stack is configured for credit-based and they responded with credit-based. */ + else if (cl == RFCOMM_PN_CONV_LAYER_CBFC_R) { + p_mcb->flow = PORT_FC_CREDIT; + } + /* They responded with any other value. Treat this as negotiation to TS07.10. */ + else { + p_mcb->flow = PORT_FC_TS710; + } + } + /* If mux flow control mechanism set, we honor that setting regardless of */ + /* the CL value in their response. This allows us to gracefully accept any */ + /* illegal PN negotiation scenarios. */ + + p_port->mtu = (p_port->mtu < mtu) ? p_port->mtu : mtu; + p_port->peer_mtu = p_port->mtu; + + if (p_mcb->flow == PORT_FC_CREDIT) { + port_get_credits (p_port, k); + } + + if (p_port->state == PORT_STATE_OPENING) { + RFCOMM_DlcEstablishReq (p_mcb, p_port->dlci, p_port->mtu); + } +} + + +/******************************************************************************* +** +** Function PORT_DlcEstablishInd +** +** Description This function is called from the RFCOMM layer when peer +** device wants to establish a new DLC. If this is not the +** first message in the establishment procedure port_handle +** has a handle to the port control block otherwise the control +** block should be found based on the muliplexer channel and +** dlci. The block should be allocated allocated before +** meaning that application already made open. +** +*******************************************************************************/ +void PORT_DlcEstablishInd (tRFC_MCB *p_mcb, UINT8 dlci, UINT16 mtu) +{ + tPORT *p_port = port_find_mcb_dlci_port (p_mcb, dlci); + tPORT_MGMT_CL_CALLBACK_ARG cl_mgmt_cb_arg = {0}; + tPORT_MGMT_SR_CALLBACK_ARG sr_mgmt_cb_arg = { + .accept = TRUE, + .ignore_rfc_state = FALSE, + .peer_mtu = 0, + }; + + RFCOMM_TRACE_DEBUG ("PORT_DlcEstablishInd p_mcb:%p, dlci:%d mtu:%di, p_port:%p", p_mcb, dlci, mtu, p_port); + RFCOMM_TRACE_DEBUG ("PORT_DlcEstablishInd p_mcb addr:%02x:%02x:%02x:%02x:%02x:%02x", + p_mcb->bd_addr[0], p_mcb->bd_addr[1], p_mcb->bd_addr[2], + p_mcb->bd_addr[3], p_mcb->bd_addr[4], p_mcb->bd_addr[5]); + + if (!p_port) { + /* This can be a first request for this port */ + p_port = port_find_dlci_port (dlci); + if (!p_port) { + RFCOMM_DlcEstablishRsp (p_mcb, dlci, 0, RFCOMM_ERROR); + return; + } + p_mcb->port_inx[dlci] = p_port->inx; + } + + /* If L2CAP's mtu less then RFCOMM's take it */ + if (mtu && (mtu < p_port->peer_mtu)) { + p_port->peer_mtu = mtu; + } + + /* If there was an inactivity timer running for MCB stop it */ + rfc_timer_stop (p_mcb); + + // RFCOMM_DlcEstablishRsp (p_mcb, dlci, p_port->mtu, RFCOMM_SUCCESS); + + /* This is the server side. If application wants to know when connection */ + /* is established, thats the place */ + if (p_port->p_callback && (p_port->ev_mask & PORT_EV_CONNECTED)) { + (p_port->p_callback)(PORT_EV_CONNECTED, p_port->inx); + } + + if (p_port->p_mgmt_callback) { + if (p_port->is_server) { + sr_mgmt_cb_arg.peer_mtu = p_port->peer_mtu; + /** + * @note + * 1. The manage callback function may change the value of accept in mgmt_cb_arg. + * 2. Use mgmt_cb_arg.ignore_rfc_state to work around the issue caused by sending + * RFCOMM establish response after the manage callback function. + */ + sr_mgmt_cb_arg.ignore_rfc_state = TRUE; + p_port->p_mgmt_callback (PORT_SUCCESS, p_port->inx, &sr_mgmt_cb_arg); + + if (!sr_mgmt_cb_arg.accept) { + RFCOMM_DlcEstablishRsp(p_mcb, dlci, 0, RFCOMM_LOW_RESOURCES); + return; + } + } else { + cl_mgmt_cb_arg.peer_mtu = p_port->peer_mtu; + p_port->p_mgmt_callback (PORT_SUCCESS, p_port->inx, &cl_mgmt_cb_arg); + } + } + + RFCOMM_DlcEstablishRsp(p_mcb, dlci, p_port->mtu, RFCOMM_SUCCESS); + p_port->state = PORT_STATE_OPENED; +} + + +/******************************************************************************* +** +** Function PORT_DlcEstablishCnf +** +** Description This function is called from the RFCOMM layer when peer +** acknowledges establish procedure (SABME/UA). Send reply +** to the user and set state to OPENED if result was +** successfull. +** +*******************************************************************************/ +void PORT_DlcEstablishCnf (tRFC_MCB *p_mcb, UINT8 dlci, UINT16 mtu, UINT16 result) +{ + tPORT *p_port = port_find_mcb_dlci_port (p_mcb, dlci); + tPORT_MGMT_SR_CALLBACK_ARG sr_mgmt_cb_arg = {0}; + tPORT_MGMT_CL_CALLBACK_ARG cl_mgmt_cb_arg = {0}; + + RFCOMM_TRACE_EVENT ("PORT_DlcEstablishCnf dlci:%d mtu:%d result:%d", dlci, mtu, result); + + if (!p_port) { + return; + } + + if (result != RFCOMM_SUCCESS) { + p_port->error = PORT_START_FAILED; + port_rfc_closed (p_port, PORT_START_FAILED); + return; + } + + /* If L2CAP's mtu less then RFCOMM's take it */ + if (mtu && (mtu < p_port->peer_mtu)) { + p_port->peer_mtu = mtu; + } + + /* If there was an inactivity timer running for MCB stop it */ + rfc_timer_stop (p_mcb); + + if (p_port->p_callback && (p_port->ev_mask & PORT_EV_CONNECTED)) { + (p_port->p_callback)(PORT_EV_CONNECTED, p_port->inx); + } + + if (p_port->p_mgmt_callback) { + if (p_port->is_server) { + sr_mgmt_cb_arg.peer_mtu = p_port->peer_mtu; + p_port->p_mgmt_callback (PORT_SUCCESS, p_port->inx, &sr_mgmt_cb_arg); + } else { + cl_mgmt_cb_arg.peer_mtu = p_port->peer_mtu; + p_port->p_mgmt_callback (PORT_SUCCESS, p_port->inx, &cl_mgmt_cb_arg); + } + } + + p_port->state = PORT_STATE_OPENED; + + /* RPN is required only if we want to tell DTE how the port should be opened */ + if ((p_port->uuid == UUID_SERVCLASS_DIALUP_NETWORKING) + || (p_port->uuid == UUID_SERVCLASS_FAX)) { + RFCOMM_PortNegReq (p_port->rfc.p_mcb, p_port->dlci, NULL); + } else { + RFCOMM_ControlReq (p_port->rfc.p_mcb, p_port->dlci, &p_port->local_ctrl); + } +} + + +/******************************************************************************* +** +** Function PORT_PortNegInd +** +** Description This function is called from the RFCOMM layer when peer +** device wants to set parameters of the port. As per the spec +** this message has to be sent before the first data packet +** and can be sent before establish. The block should be +** allocated before meaning that application already made open. +** +*******************************************************************************/ +void PORT_PortNegInd (tRFC_MCB *p_mcb, UINT8 dlci, tPORT_STATE *p_pars, + UINT16 param_mask) +{ + tPORT *p_port = port_find_mcb_dlci_port (p_mcb, dlci); + + RFCOMM_TRACE_EVENT ("PORT_PortNegInd"); + + if (!p_port) { + /* This can be a first request for this port */ + p_port = port_find_dlci_port (dlci); + if (!p_port) { + RFCOMM_PortNegRsp (p_mcb, dlci, p_pars, 0); + return; + } + p_mcb->port_inx[dlci] = p_port->inx; + } + + /* Check if the flow control is acceptable on local side */ + p_port->peer_port_pars = *p_pars; + RFCOMM_PortNegRsp (p_mcb, dlci, p_pars, param_mask); +} + + +/******************************************************************************* +** +** Function PORT_PortNegCnf +** +** Description This function is called from the RFCOMM layer to change +** state for the port. Propagate change to the user. +** +*******************************************************************************/ +void PORT_PortNegCnf (tRFC_MCB *p_mcb, UINT8 dlci, tPORT_STATE *p_pars, UINT16 result) +{ + tPORT *p_port = port_find_mcb_dlci_port (p_mcb, dlci); + UNUSED(p_pars); + + RFCOMM_TRACE_EVENT ("PORT_PortNegCnf"); + + if (!p_port) { + RFCOMM_TRACE_WARNING ("PORT_PortNegCnf no port"); + return; + } + /* Port negotiation failed. Drop the connection */ + if (result != RFCOMM_SUCCESS) { + p_port->error = PORT_PORT_NEG_FAILED; + + RFCOMM_DlcReleaseReq (p_mcb, p_port->dlci); + + port_rfc_closed (p_port, PORT_PORT_NEG_FAILED); + return; + } + + if (!(p_port->port_ctrl & PORT_CTRL_REQ_SENT)) { + RFCOMM_ControlReq (p_port->rfc.p_mcb, p_port->dlci, &p_port->local_ctrl); + } else { + RFCOMM_TRACE_WARNING ("PORT_PortNegCnf Control Already sent"); + } +} + + +/******************************************************************************* +** +** Function PORT_ControlInd +** +** Description This function is called from the RFCOMM layer on the modem +** signal change. Propagate change to the user. +** +*******************************************************************************/ +void PORT_ControlInd (tRFC_MCB *p_mcb, UINT8 dlci, tPORT_CTRL *p_pars) +{ + tPORT *p_port = port_find_mcb_dlci_port (p_mcb, dlci); + UINT32 event; + UINT8 old_signals; + + RFCOMM_TRACE_EVENT ("PORT_ControlInd"); + + if (!p_port) { + return; + } + + old_signals = p_port->peer_ctrl.modem_signal; + + event = port_get_signal_changes (p_port, old_signals, p_pars->modem_signal); + + p_port->peer_ctrl = *p_pars; + + if (!(p_port->port_ctrl & PORT_CTRL_REQ_SENT)) { + RFCOMM_ControlReq (p_port->rfc.p_mcb, p_port->dlci, &p_port->local_ctrl); + } else { + /* If this is the first time we received control RFCOMM is connected */ + if (!(p_port->port_ctrl & PORT_CTRL_IND_RECEIVED)) { + event |= (PORT_EV_CONNECTED & p_port->ev_mask); + } + + if (p_port->port_ctrl & PORT_CTRL_REQ_CONFIRMED) { + event |= port_rfc_send_tx_data(p_port); + } + } + + p_port->port_ctrl |= (PORT_CTRL_IND_RECEIVED | PORT_CTRL_IND_RESPONDED); + + if (p_pars->break_signal) { + event |= (PORT_EV_BREAK & p_port->ev_mask); + } + + /* execute call back function only if the application is registered for events */ + if (event && p_port->p_callback) { + (p_port->p_callback)(event, p_port->inx); + } + + RFCOMM_TRACE_EVENT ("PORT_ControlInd DTR_DSR : %d, RTS_CTS : %d, RI : %d, DCD : %d", + ((p_port->peer_ctrl.modem_signal & MODEM_SIGNAL_DTRDSR) ? 1 : 0), + ((p_port->peer_ctrl.modem_signal & MODEM_SIGNAL_RTSCTS) ? 1 : 0), + ((p_port->peer_ctrl.modem_signal & MODEM_SIGNAL_RI) ? 1 : 0), + ((p_port->peer_ctrl.modem_signal & MODEM_SIGNAL_DCD) ? 1 : 0)); + +} + + +/******************************************************************************* +** +** Function PORT_ControlCnf +** +** Description This function is called from the RFCOMM layer when +** peer acknowleges change of the modem signals. +** +*******************************************************************************/ +void PORT_ControlCnf (tRFC_MCB *p_mcb, UINT8 dlci, tPORT_CTRL *p_pars) +{ + tPORT *p_port = port_find_mcb_dlci_port (p_mcb, dlci); + UINT32 event = 0; + UNUSED(p_pars); + + RFCOMM_TRACE_EVENT ("PORT_ControlCnf"); + + if (!p_port) { + return; + } + + if (!(p_port->port_ctrl & PORT_CTRL_REQ_CONFIRMED)) { + p_port->port_ctrl |= PORT_CTRL_REQ_CONFIRMED; + + if (p_port->port_ctrl & PORT_CTRL_IND_RECEIVED) { + event = (p_port->ev_mask & PORT_EV_CONNECTED); + } + } + + if (p_port->port_ctrl & PORT_CTRL_IND_RECEIVED) { + event |= port_rfc_send_tx_data(p_port); + } + + /* execute call back function only if the application is registered for events */ + if (event && p_port->p_callback) { + (p_port->p_callback)(event, p_port->inx); + } +} + + +/******************************************************************************* +** +** Function PORT_LineStatusInd +** +** Description This function is called from the RFCOMM layer when +** peer indicates change in the line status +** +*******************************************************************************/ +void PORT_LineStatusInd (tRFC_MCB *p_mcb, UINT8 dlci, UINT8 line_status) +{ + tPORT *p_port = port_find_mcb_dlci_port (p_mcb, dlci); + UINT32 event = 0; + + RFCOMM_TRACE_EVENT ("PORT_LineStatusInd"); + + if (!p_port) { + return; + } + + p_port->line_status |= line_status; + + if (line_status & PORT_ERR_OVERRUN) { + event |= PORT_EV_OVERRUN; + } + + if (line_status & PORT_ERR_BREAK) { + event |= PORT_EV_BREAK; + } + + if (line_status & ~(PORT_ERR_OVERRUN | PORT_ERR_BREAK)) { + event |= PORT_EV_ERR; + } + + if ((p_port->p_callback != NULL) && (p_port->ev_mask & event)) { + p_port->p_callback ((p_port->ev_mask & event), p_port->inx); + } +} + + +/******************************************************************************* +** +** Function PORT_DlcReleaseInd +** +** Description This function is called from the RFCOMM layer when +** DLC connection is released. +** +*******************************************************************************/ +void PORT_DlcReleaseInd (tRFC_MCB *p_mcb, UINT8 dlci) +{ + tPORT *p_port = port_find_mcb_dlci_port (p_mcb, dlci); + + RFCOMM_TRACE_EVENT ("PORT_DlcReleaseInd"); + + if (!p_port) { + return; + } + + port_rfc_closed (p_port, PORT_CLOSED); +} + + +/******************************************************************************* +** +** Function PORT_CloseInd +** +** Description This function is called from the RFCOMM layer when +** multiplexer connection is released. +** +*******************************************************************************/ +void PORT_CloseInd (tRFC_MCB *p_mcb) +{ + tPORT *p_port; + int i; + + RFCOMM_TRACE_EVENT ("PORT_CloseInd"); + + p_port = &rfc_cb.port.port[0]; + for (i = 0; i < MAX_RFC_PORTS; i++, p_port++) { + if (p_port->rfc.p_mcb == p_mcb) { + port_rfc_closed (p_port, PORT_PEER_CONNECTION_FAILED); + } + } + rfc_release_multiplexer_channel (p_mcb); +} + +/******************************************************************************* +** +** Function Port_TimeOutCloseMux +** +** Description This function is called when RFCOMM timesout on a command +** as a result multiplexer connection is closed. +** +*******************************************************************************/ +void Port_TimeOutCloseMux (tRFC_MCB *p_mcb) +{ + tPORT *p_port; + int i; + + RFCOMM_TRACE_EVENT ("Port_TimeOutCloseMux"); + + p_port = &rfc_cb.port.port[0]; + for (i = 0; i < MAX_RFC_PORTS; i++, p_port++) { + if (p_port->rfc.p_mcb == p_mcb) { + port_rfc_closed (p_port, PORT_PEER_TIMEOUT); + } + } +} + + +/******************************************************************************* +** +** Function PORT_DataInd +** +** Description This function is called from the RFCOMM layer when data +** buffer is received from the peer. +** +*******************************************************************************/ +void PORT_DataInd (tRFC_MCB *p_mcb, UINT8 dlci, BT_HDR *p_buf) +{ + tPORT *p_port = port_find_mcb_dlci_port (p_mcb, dlci); + UINT8 rx_char1; + UINT32 events = 0; + UINT8 *p; + int i; + + RFCOMM_TRACE_EVENT("PORT_DataInd with data length %d, p_mcb:%p,p_port:%p,dlci:%d", + p_buf->len, p_mcb, p_port, dlci); + if (!p_port) { + osi_free (p_buf); + return; + } + /* If client registered callout callback with flow control we can just deliver receive data */ + if (p_port->p_data_co_callback) { + /* Another packet is delivered to user. Send credits to peer if required */ + + if (p_port->p_data_co_callback(p_port->inx, (UINT8 *)p_buf, -1, DATA_CO_CALLBACK_TYPE_INCOMING)) { + // do nothing, flow control credits will be given upon upper-layer request; + // port_flow_control_peer(p_port, TRUE, 1); + } else { + port_flow_control_peer(p_port, FALSE, 0); + } + //osi_free (p_buf); + return; + } else { + RFCOMM_TRACE_DEBUG("PORT_DataInd, p_port:%p, p_data_co_callback is null", p_port); + } + /* If client registered callback we can just deliver receive data */ + if (p_port->p_data_callback) { + /* Another packet is delivered to user. Send credits to peer if required */ + port_flow_control_peer(p_port, TRUE, 1); + + p_port->p_data_callback (p_port->inx, (UINT8 *)(p_buf + 1) + p_buf->offset, p_buf->len); + osi_free (p_buf); + return; + } + + /* Check if rx queue exceeds the limit */ + if ((p_port->rx.queue_size + p_buf->len > PORT_RX_CRITICAL_WM) + || (fixed_queue_length(p_port->rx.queue) + 1 > p_port->rx_buf_critical)) { + RFCOMM_TRACE_EVENT ("PORT_DataInd. Buffer over run. Dropping the buffer"); + osi_free (p_buf); + + RFCOMM_LineStatusReq (p_mcb, dlci, LINE_STATUS_OVERRUN); + return; + } + + /* If user registered to receive notification when a particular byte is */ + /* received we mast check all received bytes */ + if (((rx_char1 = p_port->user_port_pars.rx_char1) != 0) + && (p_port->ev_mask & PORT_EV_RXFLAG)) { + for (i = 0, p = (UINT8 *)(p_buf + 1) + p_buf->offset; i < p_buf->len; i++) { + if (*p++ == rx_char1) { + events |= PORT_EV_RXFLAG; + break; + } + } + } + + osi_mutex_global_lock(); + + fixed_queue_enqueue(p_port->rx.queue, p_buf, FIXED_QUEUE_MAX_TIMEOUT); + p_port->rx.queue_size += p_buf->len; + + osi_mutex_global_unlock(); + + /* perform flow control procedures if necessary */ + port_flow_control_peer(p_port, FALSE, 0); + + /* If user indicated flow control can not deliver any notifications to him */ + if (p_port->rx.user_fc) { + if (events & PORT_EV_RXFLAG) { + p_port->rx_flag_ev_pending = TRUE; + } + + return; + } + + events |= PORT_EV_RXCHAR; + + /* Mask out all events that are not of interest to user */ + events &= p_port->ev_mask; + + if (p_port->p_callback && events) { + p_port->p_callback (events, p_port->inx); + } +} + + +/******************************************************************************* +** +** Function PORT_FlowInd +** +** Description This function is called from the RFCOMM layer on the flow +** control signal change. Propagate change to the user. +** +*******************************************************************************/ +void PORT_FlowInd (tRFC_MCB *p_mcb, UINT8 dlci, BOOLEAN enable_data) +{ + tPORT *p_port = (tPORT *)NULL; + UINT32 events = 0; + int i; + + RFCOMM_TRACE_EVENT ("PORT_FlowInd fc:%d", enable_data); + + if (dlci == 0) { + p_mcb->peer_ready = enable_data; + } else { + if ((p_port = port_find_mcb_dlci_port (p_mcb, dlci)) == NULL) { + return; + } + + p_port->tx.peer_fc = !enable_data; + } + + for (i = 0; i < MAX_RFC_PORTS; i++) { + /* If DLCI is 0 event applies to all ports */ + if (dlci == 0) { + p_port = &rfc_cb.port.port[i]; + if (!p_port->in_use + || (p_port->rfc.p_mcb != p_mcb) + || (p_port->rfc.state != RFC_STATE_OPENED)) { + continue; + } + } + events = 0; + + /* Check if flow of data is still enabled */ + events |= port_flow_control_user (p_port); + + /* Check if data can be sent and send it */ + events |= port_rfc_send_tx_data (p_port); + + /* Mask out all events that are not of interest to user */ + events &= p_port->ev_mask; + + /* Send event to the application */ + if (p_port->p_callback && events) { + (p_port->p_callback)(events, p_port->inx); + } + + /* If DLCI is not 0 event applies to one port only */ + if (dlci != 0) { + break; + } + } +} + + +/******************************************************************************* +** +** Function port_rfc_send_tx_data +** +** Description This function is when forward data can be sent to the peer +** +*******************************************************************************/ +UINT32 port_rfc_send_tx_data (tPORT *p_port) +{ + UINT32 events = 0; + BT_HDR *p_buf; + + /* if there is data to be sent */ + if (p_port->tx.queue_size > 0) { + /* while the rfcomm peer is not flow controlling us, and peer is ready */ + while (!p_port->tx.peer_fc && p_port->rfc.p_mcb && p_port->rfc.p_mcb->peer_ready) { + /* get data from tx queue and send it */ + osi_mutex_global_lock(); + + if ((p_buf = (BT_HDR *)fixed_queue_dequeue(p_port->tx.queue, 0)) != NULL) { + p_port->tx.queue_size -= p_buf->len; + + osi_mutex_global_unlock(); + + RFCOMM_TRACE_DEBUG ("Sending RFCOMM_DataReq tx.queue_size=%d", p_port->tx.queue_size); + + RFCOMM_DataReq (p_port->rfc.p_mcb, p_port->dlci, p_buf); + + events |= PORT_EV_TXCHAR; + + if (p_port->tx.queue_size == 0) { + events |= PORT_EV_TXEMPTY; + break; + } + } + /* queue is empty-- all data sent */ + else { + osi_mutex_global_unlock(); + + events |= PORT_EV_TXEMPTY; + break; + } + } + /* If we flow controlled user based on the queue size enable data again */ + events |= port_flow_control_user (p_port); + } + return (events & p_port->ev_mask); +} + + +/******************************************************************************* +** +** Function port_rfc_closed +** +** Description This function when RFCOMM side of port is closed +** +*******************************************************************************/ +void port_rfc_closed (tPORT *p_port, UINT8 res) +{ + UINT8 old_signals; + UINT32 events = 0; + tRFC_MCB *p_mcb = p_port->rfc.p_mcb; + + if ((p_port->state == PORT_STATE_OPENING) && (p_port->is_server)) { + /* The servr side has not been informed that connection is up, ignore */ + RFCOMM_TRACE_EVENT ("port_rfc_closed in OPENING state ignored"); + + rfc_port_timer_stop (p_port); + p_port->rfc.state = RFC_STATE_CLOSED; + + if (p_mcb) { + p_mcb->port_inx[p_port->dlci] = 0; + + /* If there are no more ports opened on this MCB release it */ + rfc_check_mcb_active (p_mcb); + p_port->rfc.p_mcb = NULL; + } + + /* Need to restore DLCI to listening state + * if the server was on the initiating RFC + */ + p_port->dlci &= 0xfe; + + return; + } + + if ((p_port->state != PORT_STATE_CLOSING) && (p_port->state != PORT_STATE_CLOSED)) { + p_port->line_status |= LINE_STATUS_FAILED; + + old_signals = p_port->peer_ctrl.modem_signal; + + p_port->peer_ctrl.modem_signal &= ~(PORT_DTRDSR_ON | PORT_CTSRTS_ON | PORT_DCD_ON); + + events |= port_get_signal_changes (p_port, old_signals, p_port->peer_ctrl.modem_signal); + + if (p_port->ev_mask & PORT_EV_CONNECT_ERR) { + events |= PORT_EV_CONNECT_ERR; + } + } + RFCOMM_TRACE_EVENT ("port_rfc_closed state:%d sending events:%x", p_port->state, events); + + if ((p_port->p_callback != NULL) && events) { + p_port->p_callback (events, p_port->inx); + } + + if (p_port->p_mgmt_callback && !(p_port->state == PORT_STATE_CLOSED && p_port->is_server)) { + p_port->p_mgmt_callback(res, p_port->inx, NULL); + } + + p_port->rfc.state = RFC_STATE_CLOSED; + + RFCOMM_TRACE_WARNING("%s RFCOMM connection in server:%d state %d closed: %s (res: %d)", + __func__, p_port->is_server, p_port->state, PORT_GetResultString(res), + res); + + port_release_port (p_port); +} + + +/******************************************************************************* +** +** Function port_get_credits +** +** Description Set initial values for credits. +** Adjust max number of rx credits based on negotiated MTU. +** Check max allowed num of bytes, max allowed num buffers, +** should be less then 255 +** +*******************************************************************************/ +void port_get_credits (tPORT *p_port, UINT8 k) +{ + p_port->credit_tx = k; + if (p_port->credit_tx == 0) { + p_port->tx.peer_fc = TRUE; + } +} + + +#endif ///(defined RFCOMM_INCLUDED && RFCOMM_INCLUDED == TRUE) diff --git a/lib/bt/host/bluedroid/stack/rfcomm/port_utils.c b/lib/bt/host/bluedroid/stack/rfcomm/port_utils.c new file mode 100644 index 00000000..391c5312 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/rfcomm/port_utils.c @@ -0,0 +1,576 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * Port Emulation entity utilities + * + ******************************************************************************/ +#include + +#include "common/bt_target.h" +#include "stack/rfcdefs.h" +#include "stack/port_api.h" +#include "port_int.h" +#include "rfc_int.h" +#include "stack/l2cdefs.h" +#include "btm_int.h" +#include "stack/btu.h" +#include "osi/mutex.h" +#include "osi/allocator.h" +#if (defined RFCOMM_INCLUDED && RFCOMM_INCLUDED == TRUE) + +static const tPORT_STATE default_port_pars = { + PORT_BAUD_RATE_9600, + PORT_8_BITS, + PORT_ONESTOPBIT, + PORT_PARITY_NO, + PORT_ODD_PARITY, + PORT_FC_OFF, + 0, /* No rx_char */ + PORT_XON_DC1, + PORT_XOFF_DC3, +}; + + + +/******************************************************************************* +** +** Function port_allocate_port +** +** Description Look through the Port Control Blocks for a free one. Note +** that one server can open several ports with the same SCN +** if it can support simulteneous requests from different +** clients. +** +** Returns Pointer to the PORT or NULL if not found +** +*******************************************************************************/ +tPORT *port_allocate_port (UINT8 dlci, BD_ADDR bd_addr) +{ + tPORT *p_port = &rfc_cb.port.port[0]; + UINT8 xx, yy; + + for (xx = 0, yy = rfc_cb.rfc.last_port + 1; xx < MAX_RFC_PORTS; xx++, yy++) { + if (yy >= MAX_RFC_PORTS) { + yy = 0; + } + + p_port = &rfc_cb.port.port[yy]; + if (!p_port->in_use) { + memset (p_port, 0, sizeof (tPORT)); + + p_port->in_use = TRUE; + p_port->inx = yy + 1; + + p_port->dlci = dlci; + memcpy (p_port->bd_addr, bd_addr, BD_ADDR_LEN); + + /* During the open set default state for the port connection */ + port_set_defaults (p_port); + + rfc_cb.rfc.last_port = yy; + RFCOMM_TRACE_DEBUG("rfc_cb.port.port[%d]:%p allocated, last_port:%d", yy, p_port, rfc_cb.rfc.last_port); + RFCOMM_TRACE_DEBUG("port_allocate_port:bd_addr:%02x:%02x:%02x:%02x:%02x:%02x", + bd_addr[0], bd_addr[1], bd_addr[2], bd_addr[3], bd_addr[4], bd_addr[5]); + return (p_port); + } + } + + /* If here, no free PORT found */ + return (NULL); +} + + +/******************************************************************************* +** +** Function port_set_defaults +** +** Description Set defualt port parameters +** +** +*******************************************************************************/ +void port_set_defaults (tPORT *p_port) +{ + p_port->ev_mask = 0; + p_port->p_callback = NULL; + p_port->port_ctrl = 0; + p_port->error = 0; + p_port->line_status = 0; + p_port->rx_flag_ev_pending = FALSE; + p_port->peer_mtu = RFCOMM_DEFAULT_MTU; + + p_port->user_port_pars = default_port_pars; + p_port->peer_port_pars = default_port_pars; + + p_port->credit_tx = 0; + p_port->credit_rx = 0; + /* p_port->credit_rx_max = PORT_CREDIT_RX_MAX; Determined later */ + /* p_port->credit_rx_low = PORT_CREDIT_RX_LOW; Determined later */ + + memset (&p_port->local_ctrl, 0, sizeof (p_port->local_ctrl)); + memset (&p_port->peer_ctrl, 0, sizeof (p_port->peer_ctrl)); + memset (&p_port->rx, 0, sizeof (p_port->rx)); + memset (&p_port->tx, 0, sizeof (p_port->tx)); + + p_port->tx.queue = fixed_queue_new(QUEUE_SIZE_MAX); + p_port->rx.queue = fixed_queue_new(QUEUE_SIZE_MAX); +} + +/******************************************************************************* +** +** Function port_select_mtu +** +** Description Select MTU which will best serve connection from our +** point of view. +** If our device is 1.2 or lower we calculate how many DH5s +** fit into 1 RFCOMM buffer. +** +** +*******************************************************************************/ +void port_select_mtu (tPORT *p_port) +{ + UINT16 packet_size; + + /* Will select MTU only if application did not setup something */ + if (p_port->mtu == 0) { + /* find packet size which connection supports */ + packet_size = btm_get_max_packet_size (p_port->bd_addr); + if (packet_size == 0) { + /* something is very wrong */ + RFCOMM_TRACE_WARNING ("port_select_mtu bad packet size"); + p_port->mtu = RFCOMM_DEFAULT_MTU; + } else { + /* We try to negotiate MTU that each packet can be split into whole + number of max packets. For example if link is 1.2 max packet size is 339 bytes. + At first calculate how many whole packets it is. MAX L2CAP is 1691 + 4 overhead. + 1695, that will be 5 Dh5 packets. Now maximum RFCOMM packet is + 5 * 339 = 1695. Minus 4 bytes L2CAP header 1691. Minus RFCOMM 6 bytes header overhead 1685 + + For EDR 2.0 packet size is 1027. So we better send RFCOMM packet as 1 3DH5 packet + 1 * 1027 = 1027. Minus 4 bytes L2CAP header 1023. Minus RFCOMM 6 bytes header overhead 1017 */ + if ((L2CAP_MTU_SIZE + L2CAP_PKT_OVERHEAD) >= packet_size) { + p_port->mtu = ((L2CAP_MTU_SIZE + L2CAP_PKT_OVERHEAD) / packet_size * packet_size) - RFCOMM_DATA_OVERHEAD - L2CAP_PKT_OVERHEAD; + RFCOMM_TRACE_DEBUG ("port_select_mtu selected %d based on connection speed", p_port->mtu); + } else { + p_port->mtu = L2CAP_MTU_SIZE - RFCOMM_DATA_OVERHEAD; + RFCOMM_TRACE_DEBUG ("port_select_mtu selected %d based on l2cap PDU size", p_port->mtu); + } + } + } else { + RFCOMM_TRACE_DEBUG ("port_select_mtu application selected %d", p_port->mtu); + } + p_port->credit_rx_max = (PORT_RX_HIGH_WM / p_port->mtu); + if ( p_port->credit_rx_max > PORT_RX_BUF_HIGH_WM ) { + p_port->credit_rx_max = PORT_RX_BUF_HIGH_WM; + } + p_port->credit_rx_low = (PORT_RX_LOW_WM / p_port->mtu); + if ( p_port->credit_rx_low > PORT_RX_BUF_LOW_WM ) { + p_port->credit_rx_low = PORT_RX_BUF_LOW_WM; + } + p_port->rx_buf_critical = (PORT_RX_CRITICAL_WM / p_port->mtu); + if ( p_port->rx_buf_critical > PORT_RX_BUF_CRITICAL_WM ) { + p_port->rx_buf_critical = PORT_RX_BUF_CRITICAL_WM; + } + RFCOMM_TRACE_DEBUG ("port_select_mtu credit_rx_max %d, credit_rx_low %d, rx_buf_critical %d", + p_port->credit_rx_max, p_port->credit_rx_low, p_port->rx_buf_critical); +} + + +/******************************************************************************* +** +** Function port_release_port +** +** Description Release port infor control block. +** +** Returns Pointer to the PORT or NULL if not found +** +*******************************************************************************/ +void port_release_port (tPORT *p_port) +{ + BT_HDR *p_buf; + UINT32 mask; + tPORT_CALLBACK *p_port_cb; + tPORT_STATE user_port_pars; + + osi_mutex_global_lock(); + RFCOMM_TRACE_DEBUG("port_release_port, p_port:%p", p_port); + if (p_port->rx.queue != NULL) { + while ((p_buf = (BT_HDR *)fixed_queue_dequeue(p_port->rx.queue, 0)) != NULL) { + osi_free(p_buf); + } + } + p_port->rx.queue_size = 0; + + if (p_port->tx.queue != NULL) { + while ((p_buf = (BT_HDR *)fixed_queue_dequeue(p_port->tx.queue, 0)) != NULL) { + osi_free(p_buf); + } + } + p_port->tx.queue_size = 0; + + osi_mutex_global_unlock(); + + p_port->state = PORT_STATE_CLOSED; + + if (p_port->rfc.state == RFC_STATE_CLOSED) { + RFCOMM_TRACE_DEBUG ("rfc_port_closed DONE"); + if (p_port->rfc.p_mcb) { + p_port->rfc.p_mcb->port_inx[p_port->dlci] = 0; + + /* If there are no more ports opened on this MCB release it */ + rfc_check_mcb_active (p_port->rfc.p_mcb); + } + rfc_port_timer_stop (p_port); + fixed_queue_free(p_port->tx.queue, NULL); + p_port->tx.queue = NULL; + fixed_queue_free(p_port->rx.queue, NULL); + p_port->rx.queue = NULL; + + RFCOMM_TRACE_DEBUG ("port_release_port:p_port->keep_port_handle:%d", p_port->keep_port_handle); + if ( p_port->keep_port_handle ) { + RFCOMM_TRACE_DEBUG ("port_release_port:Initialize handle:%d", p_port->inx); + /* save event mask and callback */ + mask = p_port->ev_mask; + p_port_cb = p_port->p_callback; + user_port_pars = p_port->user_port_pars; + + port_set_defaults(p_port); + /* restore */ + p_port->ev_mask = mask; + p_port->p_callback = p_port_cb; + p_port->user_port_pars = user_port_pars; + p_port->mtu = p_port->keep_mtu; + + p_port->state = PORT_STATE_OPENING; + p_port->rfc.p_mcb = NULL; + if (p_port->is_server) { + p_port->dlci &= 0xfe; + } + + p_port->local_ctrl.modem_signal = p_port->default_signal_state; + memcpy (p_port->bd_addr, BT_BD_ANY, BD_ADDR_LEN); + } else { + RFCOMM_TRACE_DEBUG ("port_release_port:Clean-up handle:%d", p_port->inx); + rfc_port_timer_free (p_port); + memset (p_port, 0, sizeof (tPORT)); + } + } +} + + +/******************************************************************************* +** +** Function port_find_mcb +** +** Description This function checks if connection exists to device with +** the BD_ADDR. +** +*******************************************************************************/ +tRFC_MCB *port_find_mcb (BD_ADDR bd_addr) +{ + int i; + + for (i = 0; i < MAX_BD_CONNECTIONS; i++) { + if ((rfc_cb.port.rfc_mcb[i].state != RFC_MX_STATE_IDLE) + && !memcmp (rfc_cb.port.rfc_mcb[i].bd_addr, bd_addr, BD_ADDR_LEN)) { + /* Multiplexer channel found do not change anything */ + RFCOMM_TRACE_DEBUG("port_find_mcb: found bd_addr:%02x:%02x:%02x:%02x:%02x:%02x", + bd_addr[0], bd_addr[1], bd_addr[2], bd_addr[3], bd_addr[4], bd_addr[5]); + RFCOMM_TRACE_DEBUG("port_find_mcb: rfc_cb.port.rfc_mcb:index:%d, %p, lcid:%d", + i, &rfc_cb.port.rfc_mcb[i], rfc_cb.port.rfc_mcb[i].lcid); + return (&rfc_cb.port.rfc_mcb[i]); + } + } + RFCOMM_TRACE_DEBUG("port_find_mcb: not found, bd_addr:%02x:%02x:%02x:%02x:%02x:%02x", + bd_addr[0], bd_addr[1], bd_addr[2], bd_addr[3], bd_addr[4], bd_addr[5]); + return (NULL); +} + + +/******************************************************************************* +** +** Function port_find_mcb_dlci_port +** +** Description Find port on the multiplexer channel based on DLCI. If +** this port with DLCI not found try to use even DLCI. This +** is for the case when client is establishing connection on +** none-initiator MCB. +** +** Returns Pointer to the PORT or NULL if not found +** +*******************************************************************************/ +tPORT *port_find_mcb_dlci_port (tRFC_MCB *p_mcb, UINT8 dlci) +{ + UINT8 inx; + + if (!p_mcb) { + return (NULL); + } + + if (dlci > RFCOMM_MAX_DLCI) { + return (NULL); + } + + inx = p_mcb->port_inx[dlci]; + if (inx == 0) { + RFCOMM_TRACE_DEBUG("port_find_mcb_dlci_port: p_mcb:%p, port_inx[dlci:%d] is 0", p_mcb, dlci); + return (NULL); + } else { + return (&rfc_cb.port.port[inx - 1]); + } +} + + +/******************************************************************************* +** +** Function port_find_dlci_port +** +** Description Find port with DLCI not assigned to multiplexer channel +** +** Returns Pointer to the PORT or NULL if not found +** +*******************************************************************************/ +tPORT *port_find_dlci_port (UINT8 dlci) +{ + UINT16 i; + tPORT *p_port; + + for (i = 0; i < MAX_RFC_PORTS; i++) { + p_port = &rfc_cb.port.port[i]; + + if (p_port->in_use && (p_port->rfc.p_mcb == NULL)) { + if (p_port->dlci == dlci) { + return (p_port); + } else if ((dlci & 0x01) && (p_port->dlci == (dlci - 1))) { + p_port->dlci++; + return (p_port); + } + } + } + return (NULL); +} + + +/******************************************************************************* +** +** Function port_find_port +** +** Description Find port with DLCI, BD_ADDR +** +** Returns Pointer to the PORT or NULL if not found +** +*******************************************************************************/ +tPORT *port_find_port (UINT8 dlci, BD_ADDR bd_addr) +{ + UINT16 i; + tPORT *p_port; + + for (i = 0; i < MAX_RFC_PORTS; i++) { + p_port = &rfc_cb.port.port[i]; + if (p_port->in_use + && (p_port->dlci == dlci) + && !memcmp (p_port->bd_addr, bd_addr, BD_ADDR_LEN)) { + return (p_port); + } + } + return (NULL); +} + + +/******************************************************************************* +** +** Function port_flow_control_user +** +** Description Check the current user flow control and if necessary return +** events to be send to the user based on the user's specified +** flow control type. +** +** Returns event mask to be returned to the application +** +*******************************************************************************/ +UINT32 port_flow_control_user (tPORT *p_port) +{ + UINT32 event = 0; + + /* Flow control to the user can be caused by flow controlling by the peer */ + /* (FlowInd, or flow control by the peer RFCOMM (Fcon) or internally if */ + /* tx_queue is full */ + BOOLEAN fc = p_port->tx.peer_fc + || !p_port->rfc.p_mcb + || !p_port->rfc.p_mcb->peer_ready + || (p_port->tx.queue_size > PORT_TX_HIGH_WM) + || (fixed_queue_length(p_port->tx.queue) > PORT_TX_BUF_HIGH_WM); + + if (p_port->tx.user_fc == fc) { + return (0); + } + + p_port->tx.user_fc = fc; + + if (fc) { + event = PORT_EV_FC; + } else { + event = PORT_EV_FC | PORT_EV_FCS; + } + + return (event); +} + + +/******************************************************************************* +** +** Function port_get_signal_changes +** +** Description Check modem signals that has been changed +** +** Returns event mask to be returned to the application +** +*******************************************************************************/ +UINT32 port_get_signal_changes (tPORT *p_port, UINT8 old_signals, UINT8 signal) +{ + UINT8 changed_signals = (signal ^ old_signals); + UINT32 events = 0; + + if (changed_signals & PORT_DTRDSR_ON) { + events |= PORT_EV_DSR; + + if (signal & PORT_DTRDSR_ON) { + events |= PORT_EV_DSRS; + } + } + + if (changed_signals & PORT_CTSRTS_ON) { + events |= PORT_EV_CTS; + + if (signal & PORT_CTSRTS_ON) { + events |= PORT_EV_CTSS; + } + } + + if (changed_signals & PORT_RING_ON) { + events |= PORT_EV_RING; + } + + if (changed_signals & PORT_DCD_ON) { + events |= PORT_EV_RLSD; + + if (signal & PORT_DCD_ON) { + events |= PORT_EV_RLSDS; + } + } + + return (p_port->ev_mask & events); +} + +/******************************************************************************* +** +** Function port_flow_control_peer +** +** Description Send flow control messages to the peer for both enabling +** and disabling flow control, for both credit-based and +** TS 07.10 flow control mechanisms. +** +** Returns nothing +** +*******************************************************************************/ +void port_flow_control_peer(tPORT *p_port, BOOLEAN enable, UINT16 count) +{ + if (!p_port->rfc.p_mcb) { + return; + } + + /* If using credit based flow control */ + if (p_port->rfc.p_mcb->flow == PORT_FC_CREDIT) { + /* if want to enable flow from peer */ + if (enable) { + /* update rx credits */ + if (count > p_port->credit_rx) { + p_port->credit_rx = 0; + } else { + p_port->credit_rx -= count; + } + + /* If credit count is less than low credit watermark, and user */ + /* did not force flow control, send a credit update */ + /* There might be a special case when we just adjusted rx_max */ + if ((p_port->credit_rx <= p_port->credit_rx_low) + && !p_port->rx.user_fc + && (p_port->credit_rx_max > p_port->credit_rx)) { + rfc_send_credit(p_port->rfc.p_mcb, p_port->dlci, + (UINT8) (p_port->credit_rx_max - p_port->credit_rx)); + RFCOMM_TRACE_DEBUG("send credit: max %d, rx %d, count %d", p_port->credit_rx_max, p_port->credit_rx, count); + p_port->credit_rx = p_port->credit_rx_max; + + p_port->rx.peer_fc = FALSE; + } else { + RFCOMM_TRACE_DEBUG("credit: max %d, rx %d, low %d", p_port->credit_rx_max, p_port->credit_rx, p_port->credit_rx_low); + } + } + /* else want to disable flow from peer */ + else { + /* if client registered data callback, just do what they want */ + if (p_port->p_data_callback || p_port->p_data_co_callback) { + p_port->rx.peer_fc = TRUE; + } + /* if queue count reached credit rx max, set peer fc */ + else if (fixed_queue_length(p_port->rx.queue) >= p_port->credit_rx_max) { + p_port->rx.peer_fc = TRUE; + } + } + } + /* else using TS 07.10 flow control */ + else { + /* if want to enable flow from peer */ + if (enable) { + /* If rfcomm suspended traffic from the peer based on the rx_queue_size */ + /* check if it can be resumed now */ + if (p_port->rx.peer_fc + && (p_port->rx.queue_size < PORT_RX_LOW_WM) + && (fixed_queue_length(p_port->rx.queue) < PORT_RX_BUF_LOW_WM)) { + p_port->rx.peer_fc = FALSE; + + /* If user did not force flow control allow traffic now */ + if (!p_port->rx.user_fc) { + RFCOMM_FlowReq (p_port->rfc.p_mcb, p_port->dlci, TRUE); + } + } + } + /* else want to disable flow from peer */ + else { + /* if client registered data callback, just do what they want */ + if (p_port->p_data_callback || p_port->p_data_co_callback) { + p_port->rx.peer_fc = TRUE; + RFCOMM_FlowReq (p_port->rfc.p_mcb, p_port->dlci, FALSE); + } + /* Check the size of the rx queue. If it exceeds certain */ + /* level and flow control has not been sent to the peer do it now */ + else if ( ((p_port->rx.queue_size > PORT_RX_HIGH_WM) + || (fixed_queue_length(p_port->rx.queue) > PORT_RX_BUF_HIGH_WM)) + && !p_port->rx.peer_fc) { + RFCOMM_TRACE_EVENT ("PORT_DataInd Data reached HW. Sending FC set."); + + p_port->rx.peer_fc = TRUE; + RFCOMM_FlowReq (p_port->rfc.p_mcb, p_port->dlci, FALSE); + } + } + } +} + + +#endif ///(defined RFCOMM_INCLUDED && RFCOMM_INCLUDED == TRUE) diff --git a/lib/bt/host/bluedroid/stack/rfcomm/rfc_l2cap_if.c b/lib/bt/host/bluedroid/stack/rfcomm/rfc_l2cap_if.c new file mode 100644 index 00000000..aa28da9d --- /dev/null +++ b/lib/bt/host/bluedroid/stack/rfcomm/rfc_l2cap_if.c @@ -0,0 +1,432 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains L2CAP interface functions + * + ******************************************************************************/ + +#include +#include "common/bt_target.h" + +#include "stack/rfcdefs.h" +#include "stack/port_api.h" +#include "port_int.h" +#include "stack/l2c_api.h" +#include "stack/l2cdefs.h" +#include "rfc_int.h" +#include "common/bt_defs.h" +#include "osi/allocator.h" +#include "osi/mutex.h" +#include "osi/alarm.h" +#if (defined RFCOMM_INCLUDED && RFCOMM_INCLUDED == TRUE) + +static tL2CAP_ERTM_INFO rfc_l2c_etm_opt = +{ + L2CAP_FCR_ERTM_MODE, + L2CAP_FCR_CHAN_OPT_ERTM|L2CAP_FCR_CHAN_OPT_BASIC, /* Some devices do not support ERTM */ + L2CAP_USER_RX_BUF_SIZE, + L2CAP_USER_TX_BUF_SIZE, + L2CAP_FCR_RX_BUF_SIZE, + L2CAP_FCR_TX_BUF_SIZE +}; + +/* +** Define Callback functions to be called by L2CAP +*/ +static void RFCOMM_ConnectInd (BD_ADDR bd_addr, UINT16 lcid, UINT16 psm, UINT8 id); +static void RFCOMM_ConnectCnf (UINT16 lcid, UINT16 err); +static void RFCOMM_ConfigInd (UINT16 lcid, tL2CAP_CFG_INFO *p_cfg); +static void RFCOMM_ConfigCnf (UINT16 lcid, tL2CAP_CFG_INFO *p_cfg); +static void RFCOMM_DisconnectInd (UINT16 lcid, BOOLEAN is_clear); +static void RFCOMM_QoSViolationInd (BD_ADDR bd_addr); +static void RFCOMM_BufDataInd (UINT16 lcid, BT_HDR *p_buf); +static void RFCOMM_CongestionStatusInd (UINT16 lcid, BOOLEAN is_congested); + + +/******************************************************************************* +** +** Function rfcomm_l2cap_if_init +** +** Description This function is called during the RFCOMM task startup +** to register interface functions with L2CAP. +** +*******************************************************************************/ +void rfcomm_l2cap_if_init (void) +{ + tL2CAP_APPL_INFO *p_l2c = &rfc_cb.rfc.reg_info; + + p_l2c->pL2CA_ConnectInd_Cb = RFCOMM_ConnectInd; + p_l2c->pL2CA_ConnectCfm_Cb = RFCOMM_ConnectCnf; + p_l2c->pL2CA_ConnectPnd_Cb = NULL; + p_l2c->pL2CA_ConfigInd_Cb = RFCOMM_ConfigInd; + p_l2c->pL2CA_ConfigCfm_Cb = RFCOMM_ConfigCnf; + p_l2c->pL2CA_DisconnectInd_Cb = RFCOMM_DisconnectInd; + p_l2c->pL2CA_DisconnectCfm_Cb = NULL; + p_l2c->pL2CA_QoSViolationInd_Cb = RFCOMM_QoSViolationInd; + p_l2c->pL2CA_DataInd_Cb = RFCOMM_BufDataInd; + p_l2c->pL2CA_CongestionStatus_Cb = RFCOMM_CongestionStatusInd; + p_l2c->pL2CA_TxComplete_Cb = NULL; + + + L2CA_Register (BT_PSM_RFCOMM, p_l2c); +} + + +/******************************************************************************* +** +** Function RFCOMM_ConnectInd +** +** Description This is a callback function called by L2CAP when +** L2CA_ConnectInd received. Allocate multiplexer control block +** and dispatch the event to it. +** +*******************************************************************************/ +void RFCOMM_ConnectInd (BD_ADDR bd_addr, UINT16 lcid, UINT16 psm, UINT8 id) +{ + tRFC_MCB *p_mcb = rfc_alloc_multiplexer_channel(bd_addr, FALSE); + UNUSED(psm); + + if ((p_mcb) && (p_mcb->state != RFC_MX_STATE_IDLE)) { + /* if this is collision case */ + if ((p_mcb->is_initiator) && (p_mcb->state == RFC_MX_STATE_WAIT_CONN_CNF)) { + p_mcb->pending_lcid = lcid; + p_mcb->pending_id = id; + + /* wait random timeout (2 - 12) to resolve collision */ + /* if peer gives up then local device rejects incoming connection and continues as initiator */ + /* if timeout, local device disconnects outgoing connection and continues as acceptor */ + RFCOMM_TRACE_DEBUG ("RFCOMM_ConnectInd start timer for collision, initiator's LCID(0x%x), acceptor's LCID(0x%x)", + p_mcb->lcid, p_mcb->pending_lcid); + + rfc_timer_start(p_mcb, (UINT16)(osi_time_get_os_boottime_ms() % 10 + 2)); + return; + } else { + /* we cannot accept connection request from peer at this state */ + /* don't update lcid */ + p_mcb = NULL; + } + } else { + /* store mcb even if null */ + rfc_save_lcid_mcb (p_mcb, lcid); + } + + if (p_mcb == NULL) { + tL2CAP_ERTM_INFO *ertm_opt = rfc_cb.port.enable_l2cap_ertm ? &rfc_l2c_etm_opt : NULL; + L2CA_ErtmConnectRsp (bd_addr, id, lcid, L2CAP_CONN_NO_RESOURCES, 0, ertm_opt); + return; + } + p_mcb->lcid = lcid; + + rfc_mx_sm_execute (p_mcb, RFC_MX_EVENT_CONN_IND, &id); +} + + +/******************************************************************************* +** +** Function RFCOMM_ConnectCnf +** +** Description This is a callback function called by L2CAP when +** L2CA_ConnectCnf received. Save L2CAP handle and dispatch +** event to the FSM. +** +*******************************************************************************/ +void RFCOMM_ConnectCnf (UINT16 lcid, UINT16 result) +{ + tRFC_MCB *p_mcb = rfc_find_lcid_mcb (lcid); + + if (!p_mcb) { + RFCOMM_TRACE_ERROR ("RFCOMM_ConnectCnf LCID:0x%x", lcid); + return; + } + + if (p_mcb->pending_lcid) { + /* if peer rejects our connect request but peer's connect request is pending */ + if (result != L2CAP_CONN_OK ) { + UINT16 i; + UINT8 idx; + + RFCOMM_TRACE_DEBUG ("RFCOMM_ConnectCnf retry as acceptor on pending LCID(0x%x)", p_mcb->pending_lcid); + + /* remove mcb from mapping table */ + rfc_save_lcid_mcb (NULL, p_mcb->lcid); + + p_mcb->lcid = p_mcb->pending_lcid; + p_mcb->is_initiator = FALSE; + p_mcb->state = RFC_MX_STATE_IDLE; + + /* store mcb into mapping table */ + rfc_save_lcid_mcb (p_mcb, p_mcb->lcid); + + /* update direction bit */ + for (i = 0; i < RFCOMM_MAX_DLCI; i += 2) { + if ((idx = p_mcb->port_inx[i]) != 0) { + p_mcb->port_inx[i] = 0; + p_mcb->port_inx[i + 1] = idx; + rfc_cb.port.port[idx - 1].dlci += 1; + RFCOMM_TRACE_DEBUG ("RFCOMM MX - DLCI:%d -> %d", i, rfc_cb.port.port[idx - 1].dlci); + } + } + + rfc_mx_sm_execute (p_mcb, RFC_MX_EVENT_CONN_IND, &(p_mcb->pending_id)); + return; + } else { + RFCOMM_TRACE_DEBUG ("RFCOMM_ConnectCnf peer gave up pending LCID(0x%x)", p_mcb->pending_lcid); + + tL2CAP_ERTM_INFO *ertm_opt = rfc_cb.port.enable_l2cap_ertm ? &rfc_l2c_etm_opt : NULL; + /* Peer gave up his connection request, make sure cleaning up L2CAP channel */ + L2CA_ErtmConnectRsp (p_mcb->bd_addr, p_mcb->pending_id, p_mcb->pending_lcid, L2CAP_CONN_NO_RESOURCES, 0, ertm_opt); + + p_mcb->pending_lcid = 0; + } + } + + /* Save LCID to be used in all consecutive calls to L2CAP */ + p_mcb->lcid = lcid; + + rfc_mx_sm_execute (p_mcb, RFC_MX_EVENT_CONN_CNF, &result); +} + + +/******************************************************************************* +** +** Function RFCOMM_ConfigInd +** +** Description This is a callback function called by L2CAP when +** L2CA_ConfigInd received. Save parameters in the control +** block and dispatch event to the FSM. +** +*******************************************************************************/ +void RFCOMM_ConfigInd (UINT16 lcid, tL2CAP_CFG_INFO *p_cfg) +{ + tRFC_MCB *p_mcb = rfc_find_lcid_mcb (lcid); + + if (!p_mcb) { + RFCOMM_TRACE_ERROR ("RFCOMM_ConfigInd LCID:0x%x", lcid); + return; + } + + rfc_mx_sm_execute (p_mcb, RFC_MX_EVENT_CONF_IND, (void *)p_cfg); +} + + +/******************************************************************************* +** +** Function RFCOMM_ConfigCnf +** +** Description This is a callback function called by L2CAP when +** L2CA_ConfigCnf received. Save L2CAP handle and dispatch +** event to the FSM. +** +*******************************************************************************/ +void RFCOMM_ConfigCnf (UINT16 lcid, tL2CAP_CFG_INFO *p_cfg) +{ + tRFC_MCB *p_mcb = rfc_find_lcid_mcb (lcid); + + if (!p_mcb) { + RFCOMM_TRACE_ERROR ("RFCOMM_ConfigCnf no MCB LCID:0x%x", lcid); + return; + } + + rfc_mx_sm_execute (p_mcb, RFC_MX_EVENT_CONF_CNF, (void *)p_cfg); +} + + +/******************************************************************************* +** +** Function RFCOMM_QoSViolationInd +** +** Description This is a callback function called by L2CAP when +** L2CA_QoSViolationIndInd received. Dispatch event to the FSM. +** +*******************************************************************************/ +void RFCOMM_QoSViolationInd (BD_ADDR bd_addr) +{ + UNUSED(bd_addr); +} + + +/******************************************************************************* +** +** Function RFCOMM_DisconnectInd +** +** Description This is a callback function called by L2CAP when +** L2CA_DisconnectInd received. Dispatch event to the FSM. +** +*******************************************************************************/ +void RFCOMM_DisconnectInd (UINT16 lcid, BOOLEAN is_conf_needed) +{ + tRFC_MCB *p_mcb = rfc_find_lcid_mcb (lcid); + + if (is_conf_needed) { + L2CA_DisconnectRsp (lcid); + } + + if (!p_mcb) { + RFCOMM_TRACE_WARNING ("RFCOMM_DisconnectInd LCID:0x%x", lcid); + return; + } + + rfc_mx_sm_execute (p_mcb, RFC_MX_EVENT_DISC_IND, NULL); +} + + +/******************************************************************************* +** +** Function RFCOMM_BufDataInd +** +** Description This is a callback function called by L2CAP when +** data RFCOMM frame is received. Parse the frames, check +** the checksum and dispatch event to multiplexer or port +** state machine depending on the frame destination. +** +*******************************************************************************/ +void RFCOMM_BufDataInd (UINT16 lcid, BT_HDR *p_buf) +{ + tRFC_MCB *p_mcb = rfc_find_lcid_mcb (lcid); + tPORT *p_port; + UINT8 event; + + + if (!p_mcb) { + RFCOMM_TRACE_WARNING ("RFCOMM_BufDataInd LCID:0x%x", lcid); + osi_free (p_buf); + return; + } + + event = rfc_parse_data (p_mcb, &rfc_cb.rfc.rx_frame, p_buf); + + /* If the frame did not pass validation just ignore it */ + if (event == RFC_EVENT_BAD_FRAME) { + osi_free (p_buf); + return; + } + + if (rfc_cb.rfc.rx_frame.dlci == RFCOMM_MX_DLCI) { + /* Take special care of the Multiplexer Control Messages */ + if (event == RFC_EVENT_UIH) { + rfc_process_mx_message (p_mcb, p_buf); + return; + } + + /* Other multiplexer events go to state machine */ + rfc_mx_sm_execute (p_mcb, event, NULL); + osi_free (p_buf); + return; + } + + /* The frame was received on the data channel DLCI, verify that DLC exists */ + if (((p_port = port_find_mcb_dlci_port (p_mcb, rfc_cb.rfc.rx_frame.dlci)) == NULL) + || (!p_port->rfc.p_mcb)) { + /* If this is a SABME on the new port, check if any appl is waiting for it */ + if (event != RFC_EVENT_SABME) { + if (( p_mcb->is_initiator && !rfc_cb.rfc.rx_frame.cr) + || (!p_mcb->is_initiator && rfc_cb.rfc.rx_frame.cr)) { + rfc_send_dm (p_mcb, rfc_cb.rfc.rx_frame.dlci, rfc_cb.rfc.rx_frame.pf); + } + osi_free (p_buf); + return; + } + + if ((p_port = port_find_dlci_port (rfc_cb.rfc.rx_frame.dlci)) == NULL) { + rfc_send_dm (p_mcb, rfc_cb.rfc.rx_frame.dlci, TRUE); + osi_free (p_buf); + return; + } + p_mcb->port_inx[rfc_cb.rfc.rx_frame.dlci] = p_port->inx; + p_port->rfc.p_mcb = p_mcb; + } + + if (event == RFC_EVENT_UIH) { + if (p_buf->len > 0) { + rfc_port_sm_execute (p_port, event, p_buf); + } else { + osi_free (p_buf); + } + + if (rfc_cb.rfc.rx_frame.credit != 0) { + rfc_inc_credit (p_port, rfc_cb.rfc.rx_frame.credit); + } + + return; + } + rfc_port_sm_execute (p_port, event, NULL); + osi_free (p_buf); +} + +/******************************************************************************* +** +** Function RFCOMM_CongestionStatusInd +** +** Description This is a callback function called by L2CAP when +** data RFCOMM L2CAP congestion status changes +** +*******************************************************************************/ +void RFCOMM_CongestionStatusInd (UINT16 lcid, BOOLEAN is_congested) +{ + tRFC_MCB *p_mcb = rfc_find_lcid_mcb (lcid); + + if (!p_mcb) { + RFCOMM_TRACE_ERROR ("RFCOMM_CongestionStatusInd dropped LCID:0x%x", lcid); + return; + } else { + RFCOMM_TRACE_EVENT ("RFCOMM_CongestionStatusInd LCID:0x%x", lcid); + } + rfc_process_l2cap_congestion (p_mcb, is_congested); +} + +/******************************************************************************* +** +** Function rfc_find_lcid_mcb +** +** Description This function returns MCB block supporting local cid +** +*******************************************************************************/ +tRFC_MCB *rfc_find_lcid_mcb (UINT16 lcid) +{ + tRFC_MCB *p_mcb; + + if (lcid - L2CAP_BASE_APPL_CID >= MAX_L2CAP_CHANNELS) { + RFCOMM_TRACE_ERROR ("rfc_find_lcid_mcb LCID:0x%x", lcid); + return (NULL); + } else { + if ((p_mcb = rfc_cb.rfc.p_rfc_lcid_mcb[lcid - L2CAP_BASE_APPL_CID]) != NULL) { + if (p_mcb->lcid != lcid) { + RFCOMM_TRACE_WARNING ("rfc_find_lcid_mcb LCID reused LCID:0x%x current:0x%x", lcid, p_mcb->lcid); + return (NULL); + } + } + } + return (p_mcb); +} + + +/******************************************************************************* +** +** Function rfc_save_lcid_mcb +** +** Description This function returns MCB block supporting local cid +** +*******************************************************************************/ +void rfc_save_lcid_mcb (tRFC_MCB *p_mcb, UINT16 lcid) +{ + rfc_cb.rfc.p_rfc_lcid_mcb[lcid - L2CAP_BASE_APPL_CID] = p_mcb; +} + +#endif ///(defined RFCOMM_INCLUDED && RFCOMM_INCLUDED == TRUE) diff --git a/lib/bt/host/bluedroid/stack/rfcomm/rfc_mx_fsm.c b/lib/bt/host/bluedroid/stack/rfcomm/rfc_mx_fsm.c new file mode 100644 index 00000000..d3986813 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/rfcomm/rfc_mx_fsm.c @@ -0,0 +1,683 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains state machine and action routines for multiplexer + * channel of the RFCOMM unit + * + ******************************************************************************/ +#include +#include "stack/bt_types.h" +#include "stack/rfcdefs.h" +#include "stack/l2cdefs.h" +#include "stack/port_api.h" +#include "port_int.h" +#include "stack/l2c_api.h" +#include "rfc_int.h" +#include "common/bt_defs.h" +#include "osi/allocator.h" +#include "osi/mutex.h" +#include "common/bt_target.h" +#if (defined RFCOMM_INCLUDED && RFCOMM_INCLUDED == TRUE) + +#define L2CAP_SUCCESS 0 +#define L2CAP_ERROR 1 + +static tL2CAP_ERTM_INFO rfc_l2c_etm_opt = +{ + L2CAP_FCR_ERTM_MODE, + L2CAP_FCR_CHAN_OPT_ERTM|L2CAP_FCR_CHAN_OPT_BASIC, /* Some devices do not support ERTM */ + L2CAP_USER_RX_BUF_SIZE, + L2CAP_USER_TX_BUF_SIZE, + L2CAP_FCR_RX_BUF_SIZE, + L2CAP_FCR_TX_BUF_SIZE +}; + +static tL2CAP_FCR_OPTS rfc_l2c_fcr_opts_def = +{ + L2CAP_FCR_ERTM_MODE, + RFC_FCR_OPT_TX_WINDOW_SIZE, /* Tx window size */ + RFC_FCR_OPT_MAX_TX_B4_DISCNT, /* Maximum transmissions before disconnecting */ + RFC_FCR_OPT_RETX_TOUT, /* Retransmission timeout (2 secs) */ + RFC_FCR_OPT_MONITOR_TOUT, /* Monitor timeout (12 secs) */ + RFC_FCR_OPT_MAX_PDU_SIZE /* MPS segment size */ +}; + +/********************************************************************************/ +/* L O C A L F U N C T I O N P R O T O T Y P E S */ +/********************************************************************************/ +static void rfc_mx_sm_state_idle (tRFC_MCB *p_mcb, UINT16 event, void *p_data); +static void rfc_mx_sm_state_wait_conn_cnf (tRFC_MCB *p_mcb, UINT16 event, void *p_data); +static void rfc_mx_sm_state_configure (tRFC_MCB *p_mcb, UINT16 event, void *p_data); +static void rfc_mx_sm_sabme_wait_ua (tRFC_MCB *p_mcb, UINT16 event, void *p_data); +static void rfc_mx_sm_state_wait_sabme (tRFC_MCB *p_mcb, UINT16 event, void *p_data); +static void rfc_mx_sm_state_connected (tRFC_MCB *p_mcb, UINT16 event, void *p_data); +static void rfc_mx_sm_state_disc_wait_ua (tRFC_MCB *p_mcb, UINT16 event, void *p_data); + +static void rfc_mx_send_config_req (tRFC_MCB *p_mcb); +static void rfc_mx_conf_ind (tRFC_MCB *p_mcb, tL2CAP_CFG_INFO *p_cfg); +static void rfc_mx_conf_cnf (tRFC_MCB *p_mcb, tL2CAP_CFG_INFO *p_cfg); + + + +/******************************************************************************* +** +** Function rfc_mx_sm_execute +** +** Description This function sends multiplexor events through the state +** machine. +** +** Returns void +** +*******************************************************************************/ +void rfc_mx_sm_execute (tRFC_MCB *p_mcb, UINT16 event, void *p_data) +{ + RFCOMM_TRACE_DEBUG("%s st:%d, evt:%d\n", __func__, p_mcb->state, event); + + switch (p_mcb->state) { + case RFC_MX_STATE_IDLE: + rfc_mx_sm_state_idle (p_mcb, event, p_data); + break; + + case RFC_MX_STATE_WAIT_CONN_CNF: + rfc_mx_sm_state_wait_conn_cnf (p_mcb, event, p_data); + break; + + case RFC_MX_STATE_CONFIGURE: + rfc_mx_sm_state_configure (p_mcb, event, p_data); + break; + + case RFC_MX_STATE_SABME_WAIT_UA: + rfc_mx_sm_sabme_wait_ua (p_mcb, event, p_data); + break; + + case RFC_MX_STATE_WAIT_SABME: + rfc_mx_sm_state_wait_sabme (p_mcb, event, p_data); + break; + + case RFC_MX_STATE_CONNECTED: + rfc_mx_sm_state_connected (p_mcb, event, p_data); + break; + + case RFC_MX_STATE_DISC_WAIT_UA: + rfc_mx_sm_state_disc_wait_ua (p_mcb, event, p_data); + break; + + } +} + + +/******************************************************************************* +** +** Function rfc_mx_sm_state_idle +** +** Description This function handles events when the multiplexer is in +** IDLE state. This state exists when connection is being +** initially established. +** +** Returns void +** +*******************************************************************************/ +void rfc_mx_sm_state_idle (tRFC_MCB *p_mcb, UINT16 event, void *p_data) +{ + tL2CAP_ERTM_INFO *ertm_opt = NULL; + + RFCOMM_TRACE_EVENT ("rfc_mx_sm_state_idle - evt:%d", event); + + switch (event) { + case RFC_MX_EVENT_START_REQ: + + /* Initialize L2CAP MTU */ + p_mcb->peer_l2cap_mtu = L2CAP_DEFAULT_MTU - RFCOMM_MIN_OFFSET - 1; + + ertm_opt = rfc_cb.port.enable_l2cap_ertm ? &rfc_l2c_etm_opt : NULL; + p_mcb->lcid = L2CA_ErtmConnectReq (BT_PSM_RFCOMM, p_mcb->bd_addr, ertm_opt); + + if (p_mcb->lcid == 0) { + PORT_StartCnf (p_mcb, RFCOMM_ERROR); + return; + } + /* Save entry for quicker access to mcb based on the LCID */ + rfc_save_lcid_mcb (p_mcb, p_mcb->lcid); + + p_mcb->state = RFC_MX_STATE_WAIT_CONN_CNF; + return; + + case RFC_MX_EVENT_START_RSP: + case RFC_MX_EVENT_CONN_CNF: + case RFC_MX_EVENT_CONF_IND: + case RFC_MX_EVENT_CONF_CNF: + RFCOMM_TRACE_ERROR ("Mx error state %d event %d", p_mcb->state, event); + return; + + case RFC_MX_EVENT_CONN_IND: + + rfc_timer_start (p_mcb, RFCOMM_CONN_TIMEOUT); + ertm_opt = rfc_cb.port.enable_l2cap_ertm ? &rfc_l2c_etm_opt : NULL; + L2CA_ErtmConnectRsp (p_mcb->bd_addr, *((UINT8 *)p_data), p_mcb->lcid, L2CAP_CONN_OK, 0, ertm_opt); + + rfc_mx_send_config_req (p_mcb); + + p_mcb->state = RFC_MX_STATE_CONFIGURE; + return; + + case RFC_EVENT_SABME: + break; + + case RFC_EVENT_UA: + case RFC_EVENT_DM: + return; + + case RFC_EVENT_DISC: + rfc_send_dm (p_mcb, RFCOMM_MX_DLCI, TRUE); + return; + + case RFC_EVENT_UIH: + rfc_send_dm (p_mcb, RFCOMM_MX_DLCI, FALSE); + return; + } + RFCOMM_TRACE_EVENT ("RFCOMM MX ignored - evt:%d in state:%d", event, p_mcb->state); +} + + +/******************************************************************************* +** +** Function rfc_mx_sm_state_wait_conn_cnf +** +** Description This function handles events when the multiplexer is +** waiting for Connection Confirm from L2CAP. +** +** Returns void +** +*******************************************************************************/ +void rfc_mx_sm_state_wait_conn_cnf (tRFC_MCB *p_mcb, UINT16 event, void *p_data) +{ + RFCOMM_TRACE_EVENT ("rfc_mx_sm_state_wait_conn_cnf - evt:%d", event); + switch (event) { + case RFC_MX_EVENT_START_REQ: + RFCOMM_TRACE_ERROR ("Mx error state %d event %d", p_mcb->state, event); + return; + + /* There is some new timing so that Config Ind comes before security is completed + so we are still waiting fo the confirmation. */ + case RFC_MX_EVENT_CONF_IND: + rfc_mx_conf_ind (p_mcb, (tL2CAP_CFG_INFO *)p_data); + return; + + case RFC_MX_EVENT_CONN_CNF: + if (*((UINT16 *)p_data) != L2CAP_SUCCESS) { + p_mcb->state = RFC_MX_STATE_IDLE; + + PORT_StartCnf (p_mcb, *((UINT16 *)p_data)); + return; + } + p_mcb->state = RFC_MX_STATE_CONFIGURE; + rfc_mx_send_config_req (p_mcb); + return; + + case RFC_MX_EVENT_DISC_IND: + p_mcb->state = RFC_MX_STATE_IDLE; + PORT_CloseInd (p_mcb); + return; + + case RFC_EVENT_TIMEOUT: + p_mcb->state = RFC_MX_STATE_IDLE; + L2CA_DisconnectReq (p_mcb->lcid); + + /* we gave up outgoing connection request then try peer's request */ + if (p_mcb->pending_lcid) { + UINT16 i; + UINT8 idx; + + RFCOMM_TRACE_DEBUG ("RFCOMM MX retry as acceptor in collision case - evt:%d in state:%d", event, p_mcb->state); + + rfc_save_lcid_mcb (NULL, p_mcb->lcid); + p_mcb->lcid = p_mcb->pending_lcid; + rfc_save_lcid_mcb (p_mcb, p_mcb->lcid); + + p_mcb->is_initiator = FALSE; + + /* update direction bit */ + for (i = 0; i < RFCOMM_MAX_DLCI; i += 2) { + if ((idx = p_mcb->port_inx[i]) != 0) { + p_mcb->port_inx[i] = 0; + p_mcb->port_inx[i + 1] = idx; + rfc_cb.port.port[idx - 1].dlci += 1; + RFCOMM_TRACE_DEBUG ("RFCOMM MX - DLCI:%d -> %d", i, rfc_cb.port.port[idx - 1].dlci); + } + } + + rfc_mx_sm_execute (p_mcb, RFC_MX_EVENT_CONN_IND, &(p_mcb->pending_id)); + } else { + PORT_CloseInd (p_mcb); + } + return; + } + RFCOMM_TRACE_EVENT ("RFCOMM MX ignored - evt:%d in state:%d", event, p_mcb->state); +} + + +/******************************************************************************* +** +** Function rfc_mx_sm_state_configure +** +** Description This function handles events when the multiplexer in the +** configuration state. +** +** Returns void +** +*******************************************************************************/ +void rfc_mx_sm_state_configure (tRFC_MCB *p_mcb, UINT16 event, void *p_data) +{ + RFCOMM_TRACE_EVENT ("rfc_mx_sm_state_configure - evt:%d", event); + switch (event) { + case RFC_MX_EVENT_START_REQ: + case RFC_MX_EVENT_CONN_CNF: + + RFCOMM_TRACE_ERROR ("Mx error state %d event %d", p_mcb->state, event); + return; + + case RFC_MX_EVENT_CONF_IND: + rfc_mx_conf_ind (p_mcb, (tL2CAP_CFG_INFO *)p_data); + return; + + case RFC_MX_EVENT_CONF_CNF: + rfc_mx_conf_cnf (p_mcb, (tL2CAP_CFG_INFO *)p_data); + return; + + case RFC_MX_EVENT_DISC_IND: + p_mcb->state = RFC_MX_STATE_IDLE; + PORT_CloseInd (p_mcb); + return; + + case RFC_EVENT_TIMEOUT: + p_mcb->state = RFC_MX_STATE_IDLE; + L2CA_DisconnectReq (p_mcb->lcid); + + PORT_StartCnf (p_mcb, RFCOMM_ERROR); + return; + } + RFCOMM_TRACE_EVENT ("RFCOMM MX ignored - evt:%d in state:%d", event, p_mcb->state); +} + + +/******************************************************************************* +** +** Function rfc_mx_sm_sabme_wait_ua +** +** Description This function handles events when the multiplexer sent +** SABME and is waiting for UA reply. +** +** Returns void +** +*******************************************************************************/ +void rfc_mx_sm_sabme_wait_ua (tRFC_MCB *p_mcb, UINT16 event, void *p_data) +{ + UNUSED(p_data); + + RFCOMM_TRACE_EVENT ("rfc_mx_sm_sabme_wait_ua - evt:%d", event); + switch (event) { + case RFC_MX_EVENT_START_REQ: + case RFC_MX_EVENT_CONN_CNF: + RFCOMM_TRACE_ERROR ("Mx error state %d event %d", p_mcb->state, event); + return; + + /* workaround: we don't support reconfig */ + /* commented out until we support reconfig + case RFC_MX_EVENT_CONF_IND: + rfc_mx_conf_ind (p_mcb, (tL2CAP_CFG_INFO *)p_data); + return; + + case RFC_MX_EVENT_CONF_CNF: + rfc_mx_conf_cnf (p_mcb, (tL2CAP_CFG_INFO *)p_data); + return; + */ + + case RFC_MX_EVENT_DISC_IND: + p_mcb->state = RFC_MX_STATE_IDLE; + PORT_CloseInd (p_mcb); + return; + + case RFC_EVENT_UA: + rfc_timer_stop (p_mcb); + + p_mcb->state = RFC_MX_STATE_CONNECTED; + p_mcb->peer_ready = TRUE; + + PORT_StartCnf (p_mcb, RFCOMM_SUCCESS); + return; + + case RFC_EVENT_DM: + rfc_timer_stop (p_mcb); + /* Case falls through */ + + case RFC_MX_EVENT_CONF_IND: /* workaround: we don't support reconfig */ + case RFC_MX_EVENT_CONF_CNF: /* workaround: we don't support reconfig */ + case RFC_EVENT_TIMEOUT: + p_mcb->state = RFC_MX_STATE_IDLE; + L2CA_DisconnectReq (p_mcb->lcid); + + PORT_StartCnf (p_mcb, RFCOMM_ERROR); + return; + } + RFCOMM_TRACE_EVENT ("RFCOMM MX ignored - evt:%d in state:%d", event, p_mcb->state); +} + +/******************************************************************************* +** +** Function rfc_mx_sm_state_wait_sabme +** +** Description This function handles events when the multiplexer is +** waiting for SABME on the acceptor side after configuration +** +** Returns void +** +*******************************************************************************/ +void rfc_mx_sm_state_wait_sabme (tRFC_MCB *p_mcb, UINT16 event, void *p_data) +{ + RFCOMM_TRACE_EVENT ("rfc_mx_sm_state_wait_sabme - evt:%d", event); + switch (event) { + case RFC_MX_EVENT_DISC_IND: + p_mcb->state = RFC_MX_STATE_IDLE; + PORT_CloseInd (p_mcb); + return; + + case RFC_EVENT_SABME: + /* if we gave up outgoing connection request */ + if (p_mcb->pending_lcid) { + p_mcb->pending_lcid = 0; + + rfc_send_ua (p_mcb, RFCOMM_MX_DLCI); + + rfc_timer_stop (p_mcb); + p_mcb->state = RFC_MX_STATE_CONNECTED; + p_mcb->peer_ready = TRUE; + + /* MX channel collision has been resolved, continue to open ports */ + PORT_StartCnf (p_mcb, RFCOMM_SUCCESS); + } else { + rfc_timer_stop (p_mcb); + PORT_StartInd (p_mcb); + } + return; + + case RFC_MX_EVENT_START_RSP: + if (*((UINT16 *)p_data) != RFCOMM_SUCCESS) { + rfc_send_dm (p_mcb, RFCOMM_MX_DLCI, TRUE); + } else { + rfc_send_ua (p_mcb, RFCOMM_MX_DLCI); + + p_mcb->state = RFC_MX_STATE_CONNECTED; + p_mcb->peer_ready = TRUE; + } + return; + + case RFC_MX_EVENT_CONF_IND: /* workaround: we don't support reconfig */ + case RFC_MX_EVENT_CONF_CNF: /* workaround: we don't support reconfig */ + case RFC_EVENT_TIMEOUT: + p_mcb->state = RFC_MX_STATE_IDLE; + L2CA_DisconnectReq (p_mcb->lcid); + + PORT_CloseInd (p_mcb); + return; + } + RFCOMM_TRACE_EVENT ("RFCOMM MX ignored - evt:%d in state:%d", event, p_mcb->state); +} + + +/******************************************************************************* +** +** Function rfc_mx_sm_state_connected +** +** Description This function handles events when the multiplexer is +** in the CONNECTED state +** +** Returns void +** +*******************************************************************************/ +void rfc_mx_sm_state_connected (tRFC_MCB *p_mcb, UINT16 event, void *p_data) +{ + UNUSED(p_data); + + RFCOMM_TRACE_EVENT ("rfc_mx_sm_state_connected - evt:%d", event); + + switch (event) { + case RFC_EVENT_TIMEOUT: + case RFC_MX_EVENT_CLOSE_REQ: + rfc_timer_start (p_mcb, RFC_DISC_TIMEOUT); + p_mcb->state = RFC_MX_STATE_DISC_WAIT_UA; + rfc_send_disc (p_mcb, RFCOMM_MX_DLCI); + return; + + case RFC_MX_EVENT_DISC_IND: + p_mcb->state = RFC_MX_STATE_IDLE; + PORT_CloseInd (p_mcb); + return; + + case RFC_EVENT_DISC: + /* Reply with UA. If initiator bring down L2CAP connection */ + /* If server wait for some time if client decide to reinitiate channel */ + rfc_send_ua (p_mcb, RFCOMM_MX_DLCI); + if (p_mcb->is_initiator) { + L2CA_DisconnectReq (p_mcb->lcid); + } + /* notify all ports that connection is gone */ + PORT_CloseInd (p_mcb); + return; + } + RFCOMM_TRACE_EVENT ("RFCOMM MX ignored - evt:%d in state:%d", event, p_mcb->state); +} + + +/******************************************************************************* +** +** Function rfc_mx_sm_state_disc_wait_ua +** +** Description This function handles events when the multiplexer sent +** DISC and is waiting for UA reply. +** +** Returns void +** +*******************************************************************************/ +void rfc_mx_sm_state_disc_wait_ua (tRFC_MCB *p_mcb, UINT16 event, void *p_data) +{ + BT_HDR *p_buf; + + RFCOMM_TRACE_EVENT ("rfc_mx_sm_state_disc_wait_ua - evt:%d", event); + switch (event) { + case RFC_EVENT_UA: + case RFC_EVENT_DM: + case RFC_EVENT_TIMEOUT: + L2CA_DisconnectReq (p_mcb->lcid); + + if (p_mcb->restart_required) { + tL2CAP_ERTM_INFO *ertm_opt = rfc_cb.port.enable_l2cap_ertm ? &rfc_l2c_etm_opt : NULL; + /* Start Request was received while disconnecting. Execute it again */ + p_mcb->lcid = L2CA_ErtmConnectReq(BT_PSM_RFCOMM, p_mcb->bd_addr, ertm_opt); + + if (p_mcb->lcid == 0) { + PORT_StartCnf (p_mcb, RFCOMM_ERROR); + return; + } + /* Save entry for quicker access to mcb based on the LCID */ + rfc_save_lcid_mcb (p_mcb, p_mcb->lcid); + + /* clean up before reuse it */ + while ((p_buf = (BT_HDR *)fixed_queue_dequeue(p_mcb->cmd_q, 0)) != NULL) { + osi_free(p_buf); + } + + rfc_timer_start (p_mcb, RFC_MCB_INIT_INACT_TIMER); + + p_mcb->is_initiator = TRUE; + p_mcb->restart_required = FALSE; + p_mcb->local_cfg_sent = FALSE; + p_mcb->peer_cfg_rcvd = FALSE; + + p_mcb->state = RFC_MX_STATE_WAIT_CONN_CNF; + return; + } + rfc_release_multiplexer_channel (p_mcb); + return; + + case RFC_EVENT_DISC: + rfc_send_ua (p_mcb, RFCOMM_MX_DLCI); + return; + + case RFC_EVENT_UIH: + osi_free (p_data); + rfc_send_dm (p_mcb, RFCOMM_MX_DLCI, FALSE); + return; + + case RFC_MX_EVENT_START_REQ: + p_mcb->restart_required = TRUE; + return; + + case RFC_MX_EVENT_DISC_IND: + p_mcb->state = RFC_MX_STATE_IDLE; + PORT_CloseInd (p_mcb); + return; + + case RFC_MX_EVENT_CLOSE_REQ: + return; + + case RFC_MX_EVENT_QOS_VIOLATION_IND: + break; + } + RFCOMM_TRACE_EVENT ("RFCOMM MX ignored - evt:%d in state:%d", event, p_mcb->state); +} + + +/******************************************************************************* +** +** Function rfc_mx_send_config_req +** +** Description This function handles L2CA_ConnectInd message from the +** L2CAP. Accept connection. +** +*******************************************************************************/ +static void rfc_mx_send_config_req (tRFC_MCB *p_mcb) +{ + tL2CAP_CFG_INFO cfg; + + RFCOMM_TRACE_EVENT ("rfc_mx_send_config_req"); + + memset (&cfg, 0, sizeof (tL2CAP_CFG_INFO)); + + cfg.mtu_present = TRUE; + cfg.mtu = L2CAP_MTU_SIZE; + + if (rfc_cb.port.enable_l2cap_ertm) { + cfg.fcr_present = TRUE; + cfg.fcr = rfc_l2c_fcr_opts_def; + } + + /* Defaults set by memset + cfg.flush_to_present = FALSE; + cfg.qos_present = FALSE; + cfg.fcr_present = FALSE; + cfg.fcr.mode = L2CAP_FCR_BASIC_MODE; + cfg.fcs_present = FALSE; + cfg.fcs = N/A when fcs_present is FALSE; + */ + L2CA_ConfigReq (p_mcb->lcid, &cfg); +} + + +/******************************************************************************* +** +** Function rfc_mx_conf_cnf +** +** Description This function handles L2CA_ConfigCnf message from the +** L2CAP. If result is not success tell upper layer that +** start has not been accepted. If initiator send SABME +** on DLCI 0. T1 is still running. +** +*******************************************************************************/ +static void rfc_mx_conf_cnf (tRFC_MCB *p_mcb, tL2CAP_CFG_INFO *p_cfg) +{ + // RFCOMM_TRACE_EVENT ("rfc_mx_conf_cnf p_cfg:%08x res:%d ", p_cfg, (p_cfg) ? p_cfg->result : 0); + + if (p_cfg->result != L2CAP_CFG_OK) { + if (p_mcb->is_initiator) { + PORT_StartCnf (p_mcb, p_cfg->result); + L2CA_DisconnectReq (p_mcb->lcid); + } + rfc_release_multiplexer_channel (p_mcb); + return; + } + + p_mcb->local_cfg_sent = TRUE; + if ((p_mcb->state == RFC_MX_STATE_CONFIGURE) && p_mcb->peer_cfg_rcvd) { + if (p_mcb->is_initiator) { + p_mcb->state = RFC_MX_STATE_SABME_WAIT_UA; + rfc_send_sabme (p_mcb, RFCOMM_MX_DLCI); + rfc_timer_start (p_mcb, RFC_T1_TIMEOUT); + } else { + p_mcb->state = RFC_MX_STATE_WAIT_SABME; + rfc_timer_start (p_mcb, RFCOMM_CONN_TIMEOUT); /* - increased from T2=20 to CONN=120 + to allow the user more than 10 sec to type in the + pin which can be e.g. 16 digits */ + } + } +} + + +/******************************************************************************* +** +** Function rfc_mx_conf_ind +** +** Description This function handles L2CA_ConfigInd message from the +** L2CAP. Send the L2CA_ConfigRsp message. +** +*******************************************************************************/ +static void rfc_mx_conf_ind (tRFC_MCB *p_mcb, tL2CAP_CFG_INFO *p_cfg) +{ + /* Save peer L2CAP MTU if present */ + /* RFCOMM adds 3-4 bytes in the beginning and 1 bytes FCS */ + if (p_cfg->mtu_present) { + p_mcb->peer_l2cap_mtu = p_cfg->mtu - RFCOMM_MIN_OFFSET - 1; + } else { + p_mcb->peer_l2cap_mtu = L2CAP_DEFAULT_MTU - RFCOMM_MIN_OFFSET - 1; + } + + p_cfg->mtu_present = FALSE; + p_cfg->flush_to_present = FALSE; + p_cfg->qos_present = FALSE; + + p_cfg->result = L2CAP_CFG_OK; + + L2CA_ConfigRsp (p_mcb->lcid, p_cfg); + + p_mcb->peer_cfg_rcvd = TRUE; + if ((p_mcb->state == RFC_MX_STATE_CONFIGURE) && p_mcb->local_cfg_sent) { + if (p_mcb->is_initiator) { + p_mcb->state = RFC_MX_STATE_SABME_WAIT_UA; + rfc_send_sabme (p_mcb, RFCOMM_MX_DLCI); + rfc_timer_start (p_mcb, RFC_T1_TIMEOUT); + } else { + p_mcb->state = RFC_MX_STATE_WAIT_SABME; + rfc_timer_start (p_mcb, RFCOMM_CONN_TIMEOUT); /* - increased from T2=20 to CONN=120 + to allow the user more than 10 sec to type in the + pin which can be e.g. 16 digits */ + } + } +} + +#endif ///(defined RFCOMM_INCLUDED && RFCOMM_INCLUDED == TRUE) diff --git a/lib/bt/host/bluedroid/stack/rfcomm/rfc_port_fsm.c b/lib/bt/host/bluedroid/stack/rfcomm/rfc_port_fsm.c new file mode 100644 index 00000000..8d8fe3ca --- /dev/null +++ b/lib/bt/host/bluedroid/stack/rfcomm/rfc_port_fsm.c @@ -0,0 +1,908 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains state machine and action routines for a port of the + * RFCOMM unit + * + ******************************************************************************/ +#include +#include "common/bt_target.h" +#include "stack/rfcdefs.h" +#include "stack/btm_api.h" +#include "btm_int.h" +#include "stack/port_api.h" +#include "port_int.h" +#include "rfc_int.h" +#include "common/bt_defs.h" +#include "osi/allocator.h" +#include "osi/mutex.h" +#if (defined RFCOMM_INCLUDED && RFCOMM_INCLUDED == TRUE) + +/********************************************************************************/ +/* L O C A L F U N C T I O N P R O T O T Y P E S */ +/********************************************************************************/ +static void rfc_port_sm_state_closed (tPORT *p_port, UINT16 event, void *p_data); +static void rfc_port_sm_sabme_wait_ua (tPORT *p_port, UINT16 event, void *p_data); +static void rfc_port_sm_opened (tPORT *p_port, UINT16 event, void *p_data); +static void rfc_port_sm_orig_wait_sec_check (tPORT *p_port, UINT16 event, void *p_data); +static void rfc_port_sm_term_wait_sec_check (tPORT *p_port, UINT16 event, void *p_data); +static void rfc_port_sm_disc_wait_ua (tPORT *p_port, UINT16 event, void *p_data); + +static void rfc_port_uplink_data (tPORT *p_port, BT_HDR *p_buf); + +static void rfc_set_port_state(tPORT_STATE *port_pars, MX_FRAME *p_frame); + + +/******************************************************************************* +** +** Function rfc_port_sm_execute +** +** Description This function sends port events through the state +** machine. +** +** Returns void +** +*******************************************************************************/ +void rfc_port_sm_execute (tPORT *p_port, UINT16 event, void *p_data) +{ + RFCOMM_TRACE_DEBUG("%s st:%d, evt:%d\n", __func__, p_port->rfc.state, event); + + if (!p_port) { + RFCOMM_TRACE_WARNING ("NULL port event %d", event); + return; + } + + switch (p_port->rfc.state) { + case RFC_STATE_CLOSED: + rfc_port_sm_state_closed (p_port, event, p_data); + break; + + case RFC_STATE_SABME_WAIT_UA: + rfc_port_sm_sabme_wait_ua (p_port, event, p_data); + break; + + case RFC_STATE_ORIG_WAIT_SEC_CHECK: + rfc_port_sm_orig_wait_sec_check (p_port, event, p_data); + break; + + case RFC_STATE_TERM_WAIT_SEC_CHECK: + rfc_port_sm_term_wait_sec_check (p_port, event, p_data); + break; + + case RFC_STATE_OPENED: + rfc_port_sm_opened (p_port, event, p_data); + break; + + case RFC_STATE_DISC_WAIT_UA: + rfc_port_sm_disc_wait_ua (p_port, event, p_data); + break; + } +} + + +/******************************************************************************* +** +** Function rfc_port_sm_state_closed +** +** Description This function handles events when the port is in +** CLOSED state. This state exists when port is +** being initially established. +** +** Returns void +** +*******************************************************************************/ +void rfc_port_sm_state_closed (tPORT *p_port, UINT16 event, void *p_data) +{ + switch (event) { + case RFC_EVENT_OPEN: + p_port->rfc.state = RFC_STATE_ORIG_WAIT_SEC_CHECK; + btm_sec_mx_access_request (p_port->rfc.p_mcb->bd_addr, BT_PSM_RFCOMM, TRUE, + BTM_SEC_PROTO_RFCOMM, (UINT32)(p_port->dlci / 2), + &rfc_sec_check_complete, p_port); + return; + + case RFC_EVENT_CLOSE: + break; + + case RFC_EVENT_CLEAR: + return; + + case RFC_EVENT_DATA: + osi_free (p_data); + break; + + case RFC_EVENT_SABME: + /* make sure the multiplexer disconnect timer is not running (reconnect case) */ + rfc_timer_stop(p_port->rfc.p_mcb ); + + /* Open will be continued after security checks are passed */ + p_port->rfc.state = RFC_STATE_TERM_WAIT_SEC_CHECK; + btm_sec_mx_access_request (p_port->rfc.p_mcb->bd_addr, BT_PSM_RFCOMM, FALSE, + BTM_SEC_PROTO_RFCOMM, (UINT32)(p_port->dlci / 2), + &rfc_sec_check_complete, p_port); + return; + + case RFC_EVENT_UA: + return; + + case RFC_EVENT_DM: + rfc_port_closed (p_port); + return; + + case RFC_EVENT_UIH: + osi_free (p_data); + rfc_send_dm (p_port->rfc.p_mcb, p_port->dlci, FALSE); + return; + + case RFC_EVENT_DISC: + rfc_send_dm (p_port->rfc.p_mcb, p_port->dlci, FALSE); + return; + + case RFC_EVENT_TIMEOUT: + Port_TimeOutCloseMux( p_port->rfc.p_mcb ) ; + RFCOMM_TRACE_ERROR ("Port error state %d event %d", p_port->rfc.state, event); + return; + } + + RFCOMM_TRACE_WARNING ("Port state closed Event ignored %d", event); + return; +} + +/******************************************************************************* +** +** Function rfc_port_sm_sabme_wait_ua +** +** Description This function handles events when SABME on the DLC was +** sent and SM is waiting for UA or DM. +** +** Returns void +** +*******************************************************************************/ +void rfc_port_sm_sabme_wait_ua (tPORT *p_port, UINT16 event, void *p_data) +{ + switch (event) { + case RFC_EVENT_OPEN: + case RFC_EVENT_ESTABLISH_RSP: + RFCOMM_TRACE_ERROR ("Port error state %d event %d", p_port->rfc.state, event); + return; + + case RFC_EVENT_CLOSE: + rfc_port_timer_start (p_port, RFC_DISC_TIMEOUT); + rfc_send_disc (p_port->rfc.p_mcb, p_port->dlci); + p_port->rfc.expected_rsp = 0; + p_port->rfc.state = RFC_STATE_DISC_WAIT_UA; + return; + + case RFC_EVENT_CLEAR: + rfc_port_closed (p_port); + return; + + case RFC_EVENT_DATA: + osi_free (p_data); + break; + + case RFC_EVENT_UA: + rfc_port_timer_stop (p_port); + p_port->rfc.state = RFC_STATE_OPENED; + PORT_DlcEstablishCnf (p_port->rfc.p_mcb, p_port->dlci, p_port->rfc.p_mcb->peer_l2cap_mtu, RFCOMM_SUCCESS); + return; + + case RFC_EVENT_DM: + p_port->rfc.p_mcb->is_disc_initiator = TRUE; + PORT_DlcEstablishCnf (p_port->rfc.p_mcb, p_port->dlci, p_port->rfc.p_mcb->peer_l2cap_mtu, RFCOMM_ERROR); + rfc_port_closed (p_port); + return; + + case RFC_EVENT_DISC: + rfc_send_ua (p_port->rfc.p_mcb, p_port->dlci); + PORT_DlcEstablishCnf (p_port->rfc.p_mcb, p_port->dlci, p_port->rfc.p_mcb->peer_l2cap_mtu, RFCOMM_ERROR); + rfc_port_closed (p_port); + return; + + case RFC_EVENT_SABME: + /* Continue to wait for the UA the SABME this side sent */ + rfc_send_ua (p_port->rfc.p_mcb, p_port->dlci); + return; + + case RFC_EVENT_UIH: + osi_free (p_data); + return; + + case RFC_EVENT_TIMEOUT: + p_port->rfc.state = RFC_STATE_CLOSED; + PORT_DlcEstablishCnf (p_port->rfc.p_mcb, p_port->dlci, p_port->rfc.p_mcb->peer_l2cap_mtu, RFCOMM_ERROR); + return; + } + RFCOMM_TRACE_WARNING ("Port state sabme_wait_ua Event ignored %d", event); +} + + +/******************************************************************************* +** +** Function rfc_port_sm_term_wait_sec_check +** +** Description This function handles events for the port in the +** WAIT_SEC_CHECK state. SABME has been received from the +** peer and Security Manager verifes BD_ADDR, before we can +** send ESTABLISH_IND to the Port entity +** +** Returns void +** +*******************************************************************************/ +void rfc_port_sm_term_wait_sec_check (tPORT *p_port, UINT16 event, void *p_data) +{ + switch (event) { + case RFC_EVENT_SEC_COMPLETE: + if (*((UINT8 *)p_data) != BTM_SUCCESS) { + /* Authentication/authorization failed. If link is still */ + /* up send DM and check if we need to start inactive timer */ + if (p_port->rfc.p_mcb) { + rfc_send_dm (p_port->rfc.p_mcb, p_port->dlci, TRUE); + p_port->rfc.p_mcb->is_disc_initiator = TRUE; + port_rfc_closed (p_port, PORT_SEC_FAILED); + } + } else { + PORT_DlcEstablishInd (p_port->rfc.p_mcb, p_port->dlci, p_port->rfc.p_mcb->peer_l2cap_mtu); + } + return; + + case RFC_EVENT_OPEN: + case RFC_EVENT_CLOSE: + RFCOMM_TRACE_ERROR ("Port error state %d event %d", p_port->rfc.state, event); + return; + + case RFC_EVENT_CLEAR: + btm_sec_abort_access_req (p_port->rfc.p_mcb->bd_addr); + rfc_port_closed (p_port); + return; + + case RFC_EVENT_DATA: + RFCOMM_TRACE_ERROR ("Port error state Term Wait Sec event Data"); + osi_free (p_data); + return; + + case RFC_EVENT_SABME: + /* Ignore SABME retransmission if client dares to do so */ + return; + + case RFC_EVENT_DISC: + btm_sec_abort_access_req (p_port->rfc.p_mcb->bd_addr); + p_port->rfc.state = RFC_STATE_CLOSED; + rfc_send_ua (p_port->rfc.p_mcb, p_port->dlci); + + PORT_DlcReleaseInd (p_port->rfc.p_mcb, p_port->dlci); + return; + + case RFC_EVENT_UIH: + osi_free (p_data); + return; + + case RFC_EVENT_ESTABLISH_RSP: + if (*((UINT8 *)p_data) != RFCOMM_SUCCESS) { + if (p_port->rfc.p_mcb) { + rfc_send_dm (p_port->rfc.p_mcb, p_port->dlci, TRUE); + if (*((UINT8 *)p_data) == RFCOMM_LOW_RESOURCES) { + port_rfc_closed(p_port, PORT_NO_RESOURCES); + } else { + port_rfc_closed(p_port, PORT_UNKNOWN_ERROR); + } + } + } else { + rfc_send_ua (p_port->rfc.p_mcb, p_port->dlci); + p_port->rfc.state = RFC_STATE_OPENED; + } + return; + } + RFCOMM_TRACE_WARNING ("Port state term_wait_sec_check Event ignored %d", event); +} + + +/******************************************************************************* +** +** Function rfc_port_sm_orig_wait_sec_check +** +** Description This function handles events for the port in the +** ORIG_WAIT_SEC_CHECK state. RFCOMM is waiting for Security +** manager to finish before sending SABME to the peer +** +** Returns void +** +*******************************************************************************/ +void rfc_port_sm_orig_wait_sec_check (tPORT *p_port, UINT16 event, void *p_data) +{ + switch (event) { + case RFC_EVENT_SEC_COMPLETE: + if (*((UINT8 *)p_data) != BTM_SUCCESS) { + p_port->rfc.p_mcb->is_disc_initiator = TRUE; + PORT_DlcEstablishCnf (p_port->rfc.p_mcb, p_port->dlci, 0, RFCOMM_SECURITY_ERR); + rfc_port_closed (p_port); + return; + } + rfc_send_sabme (p_port->rfc.p_mcb, p_port->dlci); + rfc_port_timer_start (p_port, RFC_PORT_T1_TIMEOUT); + p_port->rfc.state = RFC_STATE_SABME_WAIT_UA; + return; + + case RFC_EVENT_OPEN: + case RFC_EVENT_SABME: /* Peer should not use the same dlci */ + RFCOMM_TRACE_ERROR ("Port error state %d event %d", p_port->rfc.state, event); + return; + + case RFC_EVENT_CLOSE: + btm_sec_abort_access_req (p_port->rfc.p_mcb->bd_addr); + rfc_port_closed (p_port); + return; + + case RFC_EVENT_DATA: + RFCOMM_TRACE_ERROR ("Port error state Orig Wait Sec event Data"); + osi_free (p_data); + return; + + case RFC_EVENT_UIH: + osi_free (p_data); + return; + } + RFCOMM_TRACE_WARNING ("Port state orig_wait_sec_check Event ignored %d", event); +} + + +/******************************************************************************* +** +** Function rfc_port_sm_opened +** +** Description This function handles events for the port in the OPENED +** state +** +** Returns void +** +*******************************************************************************/ +void rfc_port_sm_opened (tPORT *p_port, UINT16 event, void *p_data) +{ + switch (event) { + case RFC_EVENT_OPEN: + RFCOMM_TRACE_ERROR ("Port error state %d event %d", p_port->rfc.state, event); + return; + + case RFC_EVENT_CLOSE: + rfc_port_timer_start (p_port, RFC_DISC_TIMEOUT); + rfc_send_disc (p_port->rfc.p_mcb, p_port->dlci); + p_port->rfc.expected_rsp = 0; + p_port->rfc.state = RFC_STATE_DISC_WAIT_UA; + return; + + case RFC_EVENT_CLEAR: + rfc_port_closed (p_port); + return; + + case RFC_EVENT_DATA: + /* Send credits in the frame. Pass them in the layer specific member of the hdr. */ + /* There might be an initial case when we reduced rx_max and credit_rx is still */ + /* bigger. Make sure that we do not send 255 */ + if ((p_port->rfc.p_mcb->flow == PORT_FC_CREDIT) + && (((BT_HDR *)p_data)->len < p_port->peer_mtu) + && (!p_port->rx.user_fc) + && (p_port->credit_rx_max > p_port->credit_rx)) { + ((BT_HDR *)p_data)->layer_specific = (UINT8) (p_port->credit_rx_max - p_port->credit_rx); + p_port->credit_rx = p_port->credit_rx_max; + } else { + ((BT_HDR *)p_data)->layer_specific = 0; + } + rfc_send_buf_uih (p_port->rfc.p_mcb, p_port->dlci, (BT_HDR *)p_data); + rfc_dec_credit (p_port); + return; + + case RFC_EVENT_UA: + return; + + case RFC_EVENT_SABME: + rfc_send_ua (p_port->rfc.p_mcb, p_port->dlci); + return; + + case RFC_EVENT_DM: + PORT_DlcReleaseInd (p_port->rfc.p_mcb, p_port->dlci); + rfc_port_closed (p_port); + return; + + case RFC_EVENT_DISC: + p_port->rfc.state = RFC_STATE_CLOSED; + rfc_send_ua (p_port->rfc.p_mcb, p_port->dlci); + if (! fixed_queue_is_empty(p_port->rx.queue)) { + /* give a chance to upper stack to close port properly */ + RFCOMM_TRACE_DEBUG("port queue is not empty"); + rfc_port_timer_start (p_port, RFC_DISC_TIMEOUT); + } else { + PORT_DlcReleaseInd (p_port->rfc.p_mcb, p_port->dlci); + } + return; + + case RFC_EVENT_UIH: + rfc_port_uplink_data (p_port, (BT_HDR *)p_data); + return; + + case RFC_EVENT_TIMEOUT: + Port_TimeOutCloseMux( p_port->rfc.p_mcb ) ; + RFCOMM_TRACE_ERROR ("Port error state %d event %d", p_port->rfc.state, event); + return; + } + RFCOMM_TRACE_WARNING ("Port state opened Event ignored %d", event); +} + + +/******************************************************************************* +** +** Function rfc_port_sm_disc_wait_ua +** +** Description This function handles events when DISC on the DLC was +** sent and SM is waiting for UA or DM. +** +** Returns void +** +*******************************************************************************/ +void rfc_port_sm_disc_wait_ua (tPORT *p_port, UINT16 event, void *p_data) +{ + switch (event) { + case RFC_EVENT_OPEN: + case RFC_EVENT_ESTABLISH_RSP: + RFCOMM_TRACE_ERROR ("Port error state %d event %d", p_port->rfc.state, event); + return; + + case RFC_EVENT_CLEAR: + rfc_port_closed (p_port); + return; + + case RFC_EVENT_DATA: + osi_free (p_data); + return; + + case RFC_EVENT_UA: + p_port->rfc.p_mcb->is_disc_initiator = TRUE; + /* Case falls through */ + + case RFC_EVENT_DM: + rfc_port_closed (p_port); + return; + + case RFC_EVENT_SABME: + rfc_send_dm (p_port->rfc.p_mcb, p_port->dlci, TRUE); + return; + + case RFC_EVENT_DISC: + rfc_send_dm (p_port->rfc.p_mcb, p_port->dlci, TRUE); + return; + + case RFC_EVENT_UIH: + osi_free (p_data); + rfc_send_dm (p_port->rfc.p_mcb, p_port->dlci, FALSE); + return; + + case RFC_EVENT_TIMEOUT: + rfc_port_closed (p_port); + return; + } + + RFCOMM_TRACE_WARNING ("Port state disc_wait_ua Event ignored %d", event); +} + + +/******************************************************************************* +** +** Function rfc_port_uplink_data +** +** Description This function handles uplink information data frame. +** +*******************************************************************************/ +void rfc_port_uplink_data (tPORT *p_port, BT_HDR *p_buf) +{ + PORT_DataInd (p_port->rfc.p_mcb, p_port->dlci, p_buf); +} + + +/******************************************************************************* +** +** Function rfc_process_pn +** +** Description This function handles DLC parameter negotiation frame. +** Record MTU and pass indication to the upper layer. +** +*******************************************************************************/ +void rfc_process_pn (tRFC_MCB *p_mcb, BOOLEAN is_command, MX_FRAME *p_frame) +{ + tPORT *p_port; + UINT8 dlci = p_frame->dlci; + + if (is_command) { + /* Ignore if Multiplexer is being shut down */ + if (p_mcb->state != RFC_MX_STATE_DISC_WAIT_UA) { + PORT_ParNegInd (p_mcb, dlci, p_frame->u.pn.mtu, + p_frame->u.pn.conv_layer, p_frame->u.pn.k); + } else { + rfc_send_dm(p_mcb, dlci, FALSE); + RFCOMM_TRACE_WARNING("***** MX PN while disconnecting *****"); + } + + return; + } + /* If we are not awaiting response just ignore it */ + p_port = port_find_mcb_dlci_port (p_mcb, dlci); + if ((p_port == NULL) || !(p_port->rfc.expected_rsp & RFC_RSP_PN)) { + return; + } + + p_port->rfc.expected_rsp &= ~RFC_RSP_PN; + + rfc_port_timer_stop (p_port); + + PORT_ParNegCnf (p_mcb, dlci, p_frame->u.pn.mtu, + p_frame->u.pn.conv_layer, p_frame->u.pn.k); +} + + +/******************************************************************************* +** +** Function rfc_process_rpn +** +** Description This function handles Remote DLC parameter negotiation +** command/response. Pass command to the user. +** +*******************************************************************************/ +void rfc_process_rpn (tRFC_MCB *p_mcb, BOOLEAN is_command, + BOOLEAN is_request, MX_FRAME *p_frame) +{ + tPORT_STATE port_pars; + tPORT *p_port; + + if ((p_port = port_find_mcb_dlci_port (p_mcb, p_frame->dlci)) == NULL) { + /* This is the first command on the port */ + if (is_command) { + + memset(&port_pars, 0, sizeof(tPORT_STATE)); + rfc_set_port_state(&port_pars, p_frame); + + PORT_PortNegInd(p_mcb, p_frame->dlci, &port_pars, p_frame->u.rpn.param_mask); + } + return; + } + + if (is_command && is_request) { + /* This is the special situation when peer just request local pars */ + port_pars = p_port->peer_port_pars; + rfc_send_rpn (p_mcb, p_frame->dlci, FALSE, &p_port->peer_port_pars, 0); + return; + } + + port_pars = p_port->peer_port_pars; + + rfc_set_port_state(&port_pars, p_frame); + + if (is_command) { + PORT_PortNegInd (p_mcb, p_frame->dlci, &port_pars, p_frame->u.rpn.param_mask); + return; + } + + /* If we are not awaiting response just ignore it */ + p_port = port_find_mcb_dlci_port (p_mcb, p_frame->dlci); + if ((p_port == NULL) || !(p_port->rfc.expected_rsp & (RFC_RSP_RPN | RFC_RSP_RPN_REPLY))) { + return; + } + + /* If we sent a request for port parameters to the peer he is replying with */ + /* mask 0. */ + rfc_port_timer_stop (p_port); + + if (p_port->rfc.expected_rsp & RFC_RSP_RPN_REPLY) { + p_port->rfc.expected_rsp &= ~RFC_RSP_RPN_REPLY; + + p_port->peer_port_pars = port_pars; + + if ((port_pars.fc_type == (RFCOMM_FC_RTR_ON_INPUT | RFCOMM_FC_RTR_ON_OUTPUT)) + || (port_pars.fc_type == (RFCOMM_FC_RTC_ON_INPUT | RFCOMM_FC_RTC_ON_OUTPUT))) { + /* This is satisfactory port parameters. Set mask as it was Ok */ + p_frame->u.rpn.param_mask = RFCOMM_RPN_PM_MASK; + } else { + /* Current peer parameters are not good, try to fix them */ + p_port->peer_port_pars.fc_type = (RFCOMM_FC_RTR_ON_INPUT | RFCOMM_FC_RTR_ON_OUTPUT); + + p_port->rfc.expected_rsp |= RFC_RSP_RPN; + rfc_send_rpn (p_mcb, p_frame->dlci, TRUE, &p_port->peer_port_pars, + RFCOMM_RPN_PM_RTR_ON_INPUT | RFCOMM_RPN_PM_RTR_ON_OUTPUT); + rfc_port_timer_start (p_port, RFC_T2_TIMEOUT) ; + return; + } + } else { + p_port->rfc.expected_rsp &= ~RFC_RSP_RPN; + } + + /* Check if all suggested parameters were accepted */ + if (((p_frame->u.rpn.param_mask & (RFCOMM_RPN_PM_RTR_ON_INPUT | RFCOMM_RPN_PM_RTR_ON_OUTPUT)) == + (RFCOMM_RPN_PM_RTR_ON_INPUT | RFCOMM_RPN_PM_RTR_ON_OUTPUT)) + || ((p_frame->u.rpn.param_mask & (RFCOMM_RPN_PM_RTC_ON_INPUT | RFCOMM_RPN_PM_RTC_ON_OUTPUT)) == + (RFCOMM_RPN_PM_RTC_ON_INPUT | RFCOMM_RPN_PM_RTC_ON_OUTPUT))) { + PORT_PortNegCnf (p_mcb, p_port->dlci, &port_pars, RFCOMM_SUCCESS); + return; + } + + /* If we were proposing RTR flow control try RTC flow control */ + /* If we were proposing RTC flow control try no flow control */ + /* otherwise drop the connection */ + if (p_port->peer_port_pars.fc_type == (RFCOMM_FC_RTR_ON_INPUT | RFCOMM_FC_RTR_ON_OUTPUT)) { + /* Current peer parameters are not good, try to fix them */ + p_port->peer_port_pars.fc_type = (RFCOMM_FC_RTC_ON_INPUT | RFCOMM_FC_RTC_ON_OUTPUT); + + p_port->rfc.expected_rsp |= RFC_RSP_RPN; + + rfc_send_rpn (p_mcb, p_frame->dlci, TRUE, &p_port->peer_port_pars, + RFCOMM_RPN_PM_RTC_ON_INPUT | RFCOMM_RPN_PM_RTC_ON_OUTPUT); + rfc_port_timer_start (p_port, RFC_T2_TIMEOUT) ; + return; + } + + /* Other side does not support flow control */ + if (p_port->peer_port_pars.fc_type == (RFCOMM_FC_RTC_ON_INPUT | RFCOMM_FC_RTC_ON_OUTPUT)) { + p_port->peer_port_pars.fc_type = RFCOMM_FC_OFF; + PORT_PortNegCnf (p_mcb, p_port->dlci, &port_pars, RFCOMM_SUCCESS); + } +} + + +/******************************************************************************* +** +** Function rfc_process_msc +** +** Description This function handles Modem Status Command. +** Pass command to the user. +** +*******************************************************************************/ +void rfc_process_msc (tRFC_MCB *p_mcb, BOOLEAN is_command, MX_FRAME *p_frame) +{ + tPORT_CTRL pars; + tPORT *p_port; + UINT8 modem_signals = p_frame->u.msc.signals; + BOOLEAN new_peer_fc = FALSE; + + p_port = port_find_mcb_dlci_port (p_mcb, p_frame->dlci); + if (p_port == NULL) { + return; + } + + pars.modem_signal = 0; + + if (modem_signals & RFCOMM_MSC_RTC) { + pars.modem_signal |= MODEM_SIGNAL_DTRDSR; + } + + if (modem_signals & RFCOMM_MSC_RTR) { + pars.modem_signal |= MODEM_SIGNAL_RTSCTS; + } + + if (modem_signals & RFCOMM_MSC_IC) { + pars.modem_signal |= MODEM_SIGNAL_RI; + } + + if (modem_signals & RFCOMM_MSC_DV) { + pars.modem_signal |= MODEM_SIGNAL_DCD; + } + + pars.fc = ((modem_signals & RFCOMM_MSC_FC) == RFCOMM_MSC_FC); + + pars.break_signal = (p_frame->u.msc.break_present) ? + p_frame->u.msc.break_duration : 0; + pars.discard_buffers = 0; + pars.break_signal_seq = RFCOMM_CTRL_BREAK_IN_SEQ; /* this is default */ + + /* Check if this command is passed only to indicate flow control */ + if (is_command) { + rfc_send_msc (p_mcb, p_frame->dlci, FALSE, &pars); + + if (p_port->rfc.p_mcb->flow != PORT_FC_CREDIT) { + /* Spec 1.1 indicates that only FC bit is used for flow control */ + p_port->peer_ctrl.fc = new_peer_fc = pars.fc; + + if (new_peer_fc != p_port->tx.peer_fc) { + PORT_FlowInd (p_mcb, p_frame->dlci, (BOOLEAN)!new_peer_fc); + } + } + + PORT_ControlInd (p_mcb, p_frame->dlci, &pars); + + return; + } + + /* If we are not awaiting response just ignore it */ + if (!(p_port->rfc.expected_rsp & RFC_RSP_MSC)) { + return; + } + + p_port->rfc.expected_rsp &= ~RFC_RSP_MSC; + + rfc_port_timer_stop (p_port); + + PORT_ControlCnf (p_port->rfc.p_mcb, p_port->dlci, &pars); +} + + +/******************************************************************************* +** +** Function rfc_process_rls +** +** Description This function handles Remote Line Status command. +** Pass command to the user. +** +*******************************************************************************/ +void rfc_process_rls (tRFC_MCB *p_mcb, BOOLEAN is_command, MX_FRAME *p_frame) +{ + tPORT *p_port; + + if (is_command) { + PORT_LineStatusInd (p_mcb, p_frame->dlci, p_frame->u.rls.line_status); + rfc_send_rls (p_mcb, p_frame->dlci, FALSE, p_frame->u.rls.line_status); + } else { + p_port = port_find_mcb_dlci_port (p_mcb, p_frame->dlci); + + /* If we are not awaiting response just ignore it */ + if (!p_port || !(p_port->rfc.expected_rsp & RFC_RSP_RLS)) { + return; + } + + p_port->rfc.expected_rsp &= ~RFC_RSP_RLS; + + rfc_port_timer_stop (p_port); + } +} + + +/******************************************************************************* +** +** Function rfc_process_nsc +** +** Description This function handles None Supported Command frame. +** +*******************************************************************************/ +void rfc_process_nsc (tRFC_MCB *p_mcb, MX_FRAME *p_frame) +{ + UNUSED(p_mcb); + UNUSED(p_frame); +} + + +/******************************************************************************* +** +** Function rfc_process_test +** +** Description This function handles Test frame. If this is a command +** reply to it. Otherwise pass response to the user. +** +*******************************************************************************/ +void rfc_process_test_rsp (tRFC_MCB *p_mcb, BT_HDR *p_buf) +{ + UNUSED(p_mcb); + + osi_free (p_buf); +} + + +/******************************************************************************* +** +** Function rfc_process_fcon +** +** Description This function handles FCON frame. The peer entity is able +** to receive new information +** +*******************************************************************************/ +void rfc_process_fcon (tRFC_MCB *p_mcb, BOOLEAN is_command) +{ + if (is_command) { + rfc_cb.rfc.peer_rx_disabled = FALSE; + + rfc_send_fcon (p_mcb, FALSE); + + if (!p_mcb->l2cap_congested) { + PORT_FlowInd (p_mcb, 0, TRUE); + } + } +} + +/******************************************************************************* +** +** Function rfc_process_fcoff +** +** Description This function handles FCOFF frame. The peer entity is unable +** to receive new information +** +*******************************************************************************/ +void rfc_process_fcoff (tRFC_MCB *p_mcb, BOOLEAN is_command) +{ + if (is_command) { + rfc_cb.rfc.peer_rx_disabled = TRUE; + + if (!p_mcb->l2cap_congested) { + PORT_FlowInd (p_mcb, 0, FALSE); + } + + rfc_send_fcoff (p_mcb, FALSE); + } +} + + +/******************************************************************************* +** +** Function rfc_process_l2cap_congestion +** +** Description This function handles L2CAP congestion messages +** +*******************************************************************************/ +void rfc_process_l2cap_congestion (tRFC_MCB *p_mcb, BOOLEAN is_congested) +{ + p_mcb->l2cap_congested = is_congested; + + if (!is_congested) { + rfc_check_send_cmd(p_mcb, NULL); + } + + if (!rfc_cb.rfc.peer_rx_disabled) { + if (!is_congested) { + PORT_FlowInd (p_mcb, 0, TRUE); + } else { + PORT_FlowInd (p_mcb, 0, FALSE); + } + } +} + +/******************************************************************************* +** +** Function rfc_set_port_pars +** +** Description This function sets the tPORT_STATE structure given a p_frame. +** +*******************************************************************************/ + +void rfc_set_port_state(tPORT_STATE *port_pars, MX_FRAME *p_frame) +{ + if (p_frame->u.rpn.param_mask & RFCOMM_RPN_PM_BIT_RATE) { + port_pars->baud_rate = p_frame->u.rpn.baud_rate; + } + if (p_frame->u.rpn.param_mask & RFCOMM_RPN_PM_DATA_BITS) { + port_pars->byte_size = p_frame->u.rpn.byte_size; + } + if (p_frame->u.rpn.param_mask & RFCOMM_RPN_PM_STOP_BITS) { + port_pars->stop_bits = p_frame->u.rpn.stop_bits; + } + if (p_frame->u.rpn.param_mask & RFCOMM_RPN_PM_PARITY) { + port_pars->parity = p_frame->u.rpn.parity; + } + if (p_frame->u.rpn.param_mask & RFCOMM_RPN_PM_PARITY_TYPE) { + port_pars->parity_type = p_frame->u.rpn.parity_type; + } + if (p_frame->u.rpn.param_mask & (RFCOMM_RPN_PM_XONXOFF_ON_INPUT | + RFCOMM_RPN_PM_XONXOFF_ON_OUTPUT | + RFCOMM_RPN_PM_RTR_ON_INPUT | + RFCOMM_RPN_PM_RTR_ON_OUTPUT | + RFCOMM_RPN_PM_RTC_ON_INPUT | + RFCOMM_RPN_PM_RTC_ON_OUTPUT)) { + port_pars->fc_type = p_frame->u.rpn.fc_type; + } + if (p_frame->u.rpn.param_mask & RFCOMM_RPN_PM_XON_CHAR) { + port_pars->xon_char = p_frame->u.rpn.xon_char; + } + if (p_frame->u.rpn.param_mask & RFCOMM_RPN_PM_XOFF_CHAR) { + port_pars->xoff_char = p_frame->u.rpn.xoff_char; + } +} + +#endif ///(defined RFCOMM_INCLUDED && RFCOMM_INCLUDED == TRUE) diff --git a/lib/bt/host/bluedroid/stack/rfcomm/rfc_port_if.c b/lib/bt/host/bluedroid/stack/rfcomm/rfc_port_if.c new file mode 100644 index 00000000..6968de81 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/rfcomm/rfc_port_if.c @@ -0,0 +1,379 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/***************************************************************************** + * + * This file contains functions callable by an application + * running on top of RFCOMM + * + *****************************************************************************/ + +#include +#include "common/bt_target.h" +#include "stack/rfcdefs.h" +#include "stack/port_api.h" +#include "stack/l2c_api.h" +#include "port_int.h" +#include "rfc_int.h" +#include "common/bt_defs.h" + +#if (defined RFCOMM_INCLUDED && RFCOMM_INCLUDED == TRUE) + +#if RFC_DYNAMIC_MEMORY == FALSE +tRFC_CB rfc_cb; +#else +tRFC_CB *rfc_cb_ptr; +#endif + +/******************************************************************************* +** +** Function RFCOMM_StartReq +** +** Description This function handles Start Request from the upper layer. +** If RFCOMM multiplexer channel can not be allocated +** send start not accepted confirmation. Otherwise dispatch +** start event to the state machine. +** +*******************************************************************************/ +void RFCOMM_StartReq (tRFC_MCB *p_mcb) +{ + rfc_mx_sm_execute (p_mcb, RFC_MX_EVENT_START_REQ, NULL); +} + + +/******************************************************************************* +** +** Function RFCOMM_StartRsp +** +** Description This function handles Start Response from the upper layer. +** Save upper layer handle and result of the Start Indication +** in the control block and dispatch event to the FSM. +** +*******************************************************************************/ +void RFCOMM_StartRsp (tRFC_MCB *p_mcb, UINT16 result) +{ + rfc_mx_sm_execute (p_mcb, RFC_MX_EVENT_START_RSP, &result); +} + + +/******************************************************************************* +** +** Function RFCOMM_DlcEstablishReq +** +** Description This function is called by the user app to establish +** connection with the specific dlci on a specific bd device. +** It will allocate RFCOMM connection control block if not +** allocated before and dispatch open event to the state +** machine. +** +*******************************************************************************/ +void RFCOMM_DlcEstablishReq (tRFC_MCB *p_mcb, UINT8 dlci, UINT16 mtu) +{ + UNUSED(mtu); + if (p_mcb->state != RFC_MX_STATE_CONNECTED) { + PORT_DlcEstablishCnf (p_mcb, dlci, 0, RFCOMM_ERROR); + return; + } + + tPORT *p_port = port_find_mcb_dlci_port(p_mcb, dlci); + if (p_port == NULL) { + RFCOMM_TRACE_WARNING("%s Unable to find DLCI port dlci:%d", __func__, + dlci); + return; + } + + rfc_port_sm_execute(p_port, RFC_EVENT_OPEN, NULL); +} + + +/******************************************************************************* +** +** Function RFCOMM_DlcEstablishRsp +** +** Description This function is called by the port emulation entity +** acks Establish Indication. +** +*******************************************************************************/ +void RFCOMM_DlcEstablishRsp (tRFC_MCB *p_mcb, UINT8 dlci, UINT16 mtu, UINT16 result) +{ + UNUSED(mtu); + if ((p_mcb->state != RFC_MX_STATE_CONNECTED) && (result == RFCOMM_SUCCESS)) { + PORT_DlcReleaseInd (p_mcb, dlci); + return; + } + + tPORT *p_port = port_find_mcb_dlci_port (p_mcb, dlci); + if (p_port == NULL) { + RFCOMM_TRACE_WARNING("%s Unable to find DLCI port dlci:%d", __func__, + dlci); + return; + } + rfc_port_sm_execute(p_port, RFC_EVENT_ESTABLISH_RSP, &result); +} + + +/******************************************************************************* +** +** Function RFCOMM_ParNegReq +** +** Description This function is called by the user app to start +** DLC parameter negotiation. Port emulation can send this +** request before actually establishing the DLC. In this +** case the function will allocate RFCOMM connection control +** block. +** +*******************************************************************************/ +void RFCOMM_ParNegReq (tRFC_MCB *p_mcb, UINT8 dlci, UINT16 mtu) +{ + UINT8 flow; + UINT8 cl; + UINT8 k; + + tPORT *p_port = port_find_mcb_dlci_port(p_mcb, dlci); + if (p_port == NULL) { + RFCOMM_TRACE_WARNING("%s Unable to find DLCI port dlci:%d", __func__, + dlci); + return; + } + + if (p_mcb->state != RFC_MX_STATE_CONNECTED) { + p_port->error = PORT_PAR_NEG_FAILED; + return; + } + + /* Negotiate the flow control mechanism. If flow control mechanism for */ + /* mux has not been set yet, use our default value. If it has been set, */ + /* use that value. */ + flow = (p_mcb->flow == PORT_FC_UNDEFINED) ? PORT_FC_DEFAULT : p_mcb->flow; + + /* Set convergence layer and number of credits (k) */ + if (flow == PORT_FC_CREDIT) { + cl = RFCOMM_PN_CONV_LAYER_CBFC_I; + k = (p_port->credit_rx_max < RFCOMM_K_MAX) ? p_port->credit_rx_max : RFCOMM_K_MAX; + p_port->credit_rx = k; + } else { + cl = RFCOMM_PN_CONV_LAYER_TYPE_1; + k = 0; + } + + /* Send Parameter Negotiation Command UIH frame */ + p_port->rfc.expected_rsp |= RFC_RSP_PN; + + rfc_send_pn (p_mcb, dlci, TRUE, mtu, cl, k); + + rfc_port_timer_start (p_port, RFC_T2_TIMEOUT) ; +} + + +/******************************************************************************* +** +** Function RFCOMM_ParNegRsp +** +** Description This function is called by the user app to acknowledge +** DLC parameter negotiation. +** +*******************************************************************************/ +void RFCOMM_ParNegRsp (tRFC_MCB *p_mcb, UINT8 dlci, UINT16 mtu, UINT8 cl, UINT8 k) +{ + if (p_mcb->state != RFC_MX_STATE_CONNECTED) { + return; + } + + /* Send Parameter Negotiation Response UIH frame */ + rfc_send_pn (p_mcb, dlci, FALSE, mtu, cl, k); +} + + +/******************************************************************************* +** +** Function RFCOMM_PortNegReq +** +** Description This function is called by the user app to start +** Remote Port parameter negotiation. Port emulation can +** send this request before actually establishing the DLC. +** In this case the function will allocate RFCOMM connection +** control block. +** +*******************************************************************************/ +void RFCOMM_PortNegReq (tRFC_MCB *p_mcb, UINT8 dlci, tPORT_STATE *p_pars) +{ + if (p_mcb->state != RFC_MX_STATE_CONNECTED) { + PORT_PortNegCnf (p_mcb, dlci, NULL, RFCOMM_ERROR); + return; + } + + tPORT *p_port = port_find_mcb_dlci_port(p_mcb, dlci); + if (p_port == NULL) { + RFCOMM_TRACE_WARNING("%s Unable to find DLCI port dlci:%d", __func__, + dlci); + return; + } + + /* Send Parameter Negotiation Command UIH frame */ + if (!p_pars) { + p_port->rfc.expected_rsp |= RFC_RSP_RPN_REPLY; + } else { + p_port->rfc.expected_rsp |= RFC_RSP_RPN; + } + + rfc_send_rpn (p_mcb, dlci, TRUE, p_pars, RFCOMM_RPN_PM_MASK); + rfc_port_timer_start (p_port, RFC_T2_TIMEOUT) ; + +} + + +/******************************************************************************* +** +** Function RFCOMM_PortNegRsp +** +** Description This function is called by the user app to acknowledge +** Port parameters negotiation. +** +*******************************************************************************/ +void RFCOMM_PortNegRsp (tRFC_MCB *p_mcb, UINT8 dlci, tPORT_STATE *p_pars, + UINT16 param_mask) +{ + if (p_mcb->state != RFC_MX_STATE_CONNECTED) { + return; + } + + rfc_send_rpn (p_mcb, dlci, FALSE, p_pars, param_mask); +} + + +/******************************************************************************* +** +** Function RFCOMM_ControlReq +** +** Description This function is called by the port entity to send control +** parameters to remote port emulation entity. +** +*******************************************************************************/ +void RFCOMM_ControlReq (tRFC_MCB *p_mcb, UINT8 dlci, tPORT_CTRL *p_pars) +{ + tPORT *p_port = port_find_mcb_dlci_port(p_mcb, dlci); + if (p_port == NULL) { + RFCOMM_TRACE_WARNING("%s Unable to find DLCI port dlci:%d", __func__, + dlci); + return; + } + + if ((p_port->state != PORT_STATE_OPENED) + || (p_port->rfc.state != RFC_STATE_OPENED)) { + return; + } + + p_port->port_ctrl |= PORT_CTRL_REQ_SENT; + + p_port->rfc.expected_rsp |= RFC_RSP_MSC; + + rfc_send_msc (p_mcb, dlci, TRUE, p_pars); + rfc_port_timer_start (p_port, RFC_T2_TIMEOUT) ; + +} + + +/******************************************************************************* +** +** Function RFCOMM_FlowReq +** +** Description This function is called by the port entity when flow +** control state has changed. Enable flag passed shows if +** port can accept more data. +** +*******************************************************************************/ +void RFCOMM_FlowReq (tRFC_MCB *p_mcb, UINT8 dlci, UINT8 enable) +{ + tPORT *p_port = port_find_mcb_dlci_port(p_mcb, dlci); + if (p_port == NULL) { + RFCOMM_TRACE_WARNING("%s Unable to find DLCI port dlci:%d", __func__, + dlci); + return; + } + + if ((p_port->state != PORT_STATE_OPENED) + || (p_port->rfc.state != RFC_STATE_OPENED)) { + return; + } + + p_port->local_ctrl.fc = !enable; + + p_port->rfc.expected_rsp |= RFC_RSP_MSC; + + rfc_send_msc (p_mcb, dlci, TRUE, &p_port->local_ctrl); + rfc_port_timer_start (p_port, RFC_T2_TIMEOUT) ; + +} + + +/******************************************************************************* +** +** Function RFCOMM_LineStatusReq +** +** Description This function is called by the port entity when line +** status should be delivered to the peer. +** +*******************************************************************************/ +void RFCOMM_LineStatusReq (tRFC_MCB *p_mcb, UINT8 dlci, UINT8 status) +{ + tPORT *p_port = port_find_mcb_dlci_port(p_mcb, dlci); + if (p_port == NULL) { + RFCOMM_TRACE_WARNING("%s Unable to find DLCI port dlci:%d", __func__, + dlci); + return; + } + + if ((p_port->state != PORT_STATE_OPENED) + || (p_port->rfc.state != RFC_STATE_OPENED)) { + return; + } + + p_port->rfc.expected_rsp |= RFC_RSP_RLS; + + rfc_send_rls (p_mcb, dlci, TRUE, status); + rfc_port_timer_start (p_port, RFC_T2_TIMEOUT); +} + + +/******************************************************************************* +** +** Function RFCOMM_DlcReleaseReq +** +** Description This function is called by the PORT unit to close DLC +** +*******************************************************************************/ +void RFCOMM_DlcReleaseReq (tRFC_MCB *p_mcb, UINT8 dlci) +{ + rfc_port_sm_execute(port_find_mcb_dlci_port (p_mcb, dlci), RFC_EVENT_CLOSE, 0); +} + + +/******************************************************************************* +** +** Function RFCOMM_DataReq +** +** Description This function is called by the user app to send data buffer +** +*******************************************************************************/ +void RFCOMM_DataReq (tRFC_MCB *p_mcb, UINT8 dlci, BT_HDR *p_buf) +{ + rfc_port_sm_execute(port_find_mcb_dlci_port (p_mcb, dlci), RFC_EVENT_DATA, p_buf); +} + + +#endif ///(defined RFCOMM_INCLUDED && RFCOMM_INCLUDED == TRUE) diff --git a/lib/bt/host/bluedroid/stack/rfcomm/rfc_ts_frames.c b/lib/bt/host/bluedroid/stack/rfcomm/rfc_ts_frames.c new file mode 100644 index 00000000..86ba2550 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/rfcomm/rfc_ts_frames.c @@ -0,0 +1,953 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains functions to send TS 07.10 frames + * + ******************************************************************************/ + +#include +#include +#include "common/bt_target.h" +#include "stack/rfcdefs.h" +#include "stack/port_api.h" +#include "stack/l2c_api.h" +#include "port_int.h" +#include "rfc_int.h" +#include "osi/mutex.h" +#include "osi/allocator.h" +#if (defined RFCOMM_INCLUDED && RFCOMM_INCLUDED == TRUE) + +/******************************************************************************* +** +** Function rfc_send_sabme +** +** Description This function sends SABME frame. +** +*******************************************************************************/ +void rfc_send_sabme (tRFC_MCB *p_mcb, UINT8 dlci) +{ + BT_HDR *p_buf; + UINT8 *p_data; + UINT8 cr = RFCOMM_CR(p_mcb->is_initiator, TRUE); + + if ((p_buf = (BT_HDR *)osi_malloc(RFCOMM_CMD_BUF_SIZE)) == NULL) { + return; + } + + p_buf->offset = L2CAP_MIN_OFFSET; + p_data = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET; + + /* SABME frame, command, PF = 1, dlci */ + *p_data++ = RFCOMM_EA | cr | (dlci << RFCOMM_SHIFT_DLCI); + *p_data++ = RFCOMM_SABME | RFCOMM_PF; + *p_data++ = RFCOMM_EA | 0; + + *p_data = RFCOMM_SABME_FCS ((UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET, cr, dlci); + + p_buf->len = 4; + + rfc_check_send_cmd(p_mcb, p_buf); +} + + +/******************************************************************************* +** +** Function rfc_send_ua +** +** Description This function sends UA frame. +** +*******************************************************************************/ +void rfc_send_ua (tRFC_MCB *p_mcb, UINT8 dlci) +{ + BT_HDR *p_buf; + UINT8 *p_data; + UINT8 cr = RFCOMM_CR(p_mcb->is_initiator, FALSE); + + if ((p_buf = (BT_HDR *)osi_malloc(RFCOMM_CMD_BUF_SIZE)) == NULL) { + return; + } + + p_buf->offset = L2CAP_MIN_OFFSET; + p_data = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET; + + /* ua frame, response, PF = 1, dlci */ + *p_data++ = RFCOMM_EA | cr | (dlci << RFCOMM_SHIFT_DLCI); + *p_data++ = RFCOMM_UA | RFCOMM_PF; + *p_data++ = RFCOMM_EA | 0; + + *p_data = RFCOMM_UA_FCS ((UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET, cr, dlci); + + p_buf->len = 4; + + rfc_check_send_cmd(p_mcb, p_buf); +} + + +/******************************************************************************* +** +** Function rfc_send_dm +** +** Description This function sends DM frame. +** +*******************************************************************************/ +void rfc_send_dm (tRFC_MCB *p_mcb, UINT8 dlci, BOOLEAN pf) +{ + BT_HDR *p_buf; + UINT8 *p_data; + UINT8 cr = RFCOMM_CR(p_mcb->is_initiator, FALSE); + + if ((p_buf = (BT_HDR *)osi_malloc(RFCOMM_CMD_BUF_SIZE)) == NULL) { + return; + } + + p_buf->offset = L2CAP_MIN_OFFSET; + p_data = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET; + + /* DM frame, response, PF = 1, dlci */ + *p_data++ = RFCOMM_EA | cr | (dlci << RFCOMM_SHIFT_DLCI); + *p_data++ = RFCOMM_DM | ((pf) ? RFCOMM_PF : 0); + *p_data++ = RFCOMM_EA | 0; + + *p_data = RFCOMM_DM_FCS ((UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET, cr, dlci); + + p_buf->len = 4; + + rfc_check_send_cmd(p_mcb, p_buf); +} + + +/******************************************************************************* +** +** Function rfc_send_disc +** +** Description This function sends DISC frame. +** +*******************************************************************************/ +void rfc_send_disc (tRFC_MCB *p_mcb, UINT8 dlci) +{ + BT_HDR *p_buf; + UINT8 *p_data; + UINT8 cr = RFCOMM_CR(p_mcb->is_initiator, TRUE); + + if ((p_buf = (BT_HDR *)osi_malloc(RFCOMM_CMD_BUF_SIZE)) == NULL) { + return; + } + + p_buf->offset = L2CAP_MIN_OFFSET; + p_data = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET; + + /* DISC frame, command, PF = 1, dlci */ + *p_data++ = RFCOMM_EA | cr | (dlci << RFCOMM_SHIFT_DLCI); + *p_data++ = RFCOMM_DISC | RFCOMM_PF; + *p_data++ = RFCOMM_EA | 0; + + *p_data = RFCOMM_DISC_FCS ((UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET, cr, dlci); + + p_buf->len = 4; + + rfc_check_send_cmd(p_mcb, p_buf); +} + + +/******************************************************************************* +** +** Function rfc_send_buf_uih +** +** Description This function sends UIH frame. +** +*******************************************************************************/ +void rfc_send_buf_uih (tRFC_MCB *p_mcb, UINT8 dlci, BT_HDR *p_buf) +{ + UINT8 *p_data; + UINT8 cr = RFCOMM_CR(p_mcb->is_initiator, TRUE); + UINT8 credits; + + p_buf->offset -= RFCOMM_CTRL_FRAME_LEN; + if (p_buf->len > 127) { + p_buf->offset--; + } + + if (dlci) { + credits = (UINT8)p_buf->layer_specific; + } else { + credits = 0; + } + + if (credits) { + p_buf->offset--; + } + + p_data = (UINT8 *)(p_buf + 1) + p_buf->offset; + + /* UIH frame, command, PF = 0, dlci */ + *p_data++ = RFCOMM_EA | cr | (dlci << RFCOMM_SHIFT_DLCI); + *p_data++ = RFCOMM_UIH | ((credits) ? RFCOMM_PF : 0); + if (p_buf->len <= 127) { + *p_data++ = RFCOMM_EA | (p_buf->len << 1); + p_buf->len += 3; + } else { + *p_data++ = (p_buf->len & 0x7f) << 1; + *p_data++ = p_buf->len >> RFCOMM_SHIFT_LENGTH2; + p_buf->len += 4; + } + + if (credits) { + *p_data++ = credits; + p_buf->len++; + } + + p_data = (UINT8 *)(p_buf + 1) + p_buf->offset + p_buf->len++; + + *p_data = RFCOMM_UIH_FCS ((UINT8 *)(p_buf + 1) + p_buf->offset, dlci); + + if (dlci == RFCOMM_MX_DLCI) { + rfc_check_send_cmd(p_mcb, p_buf); + } else { + L2CA_DataWrite (p_mcb->lcid, p_buf); + } +} + + +/******************************************************************************* +** +** Function rfc_send_pn +** +** Description This function sends DLC Parameters Negotiation Frame. +** +*******************************************************************************/ +void rfc_send_pn (tRFC_MCB *p_mcb, UINT8 dlci, BOOLEAN is_command, UINT16 mtu, UINT8 cl, UINT8 k) +{ + BT_HDR *p_buf; + UINT8 *p_data; + + if ((p_buf = (BT_HDR *)osi_malloc(RFCOMM_CMD_BUF_SIZE)) == NULL) { + return; + } + + p_buf->offset = L2CAP_MIN_OFFSET + RFCOMM_CTRL_FRAME_LEN; + p_data = (UINT8 *)(p_buf + 1) + p_buf->offset; + + *p_data++ = RFCOMM_EA | RFCOMM_I_CR(is_command) | RFCOMM_MX_PN; + *p_data++ = RFCOMM_EA | (RFCOMM_MX_PN_LEN << 1); + + *p_data++ = dlci; + *p_data++ = RFCOMM_PN_FRAM_TYPE_UIH | cl; + + /* It appeared that we need to reply with the same priority bits as we received. + ** We will use the fact that we reply in the same context so rx_frame can still be used. + */ + if (is_command) { + *p_data++ = RFCOMM_PN_PRIORITY_0; + } else { + *p_data++ = rfc_cb.rfc.rx_frame.u.pn.priority; + } + + *p_data++ = RFCOMM_T1_DSEC; + *p_data++ = mtu & 0xFF; + *p_data++ = mtu >> 8; + *p_data++ = RFCOMM_N2; + *p_data = k; + + /* Total length is sizeof PN data + mx header 2 */ + p_buf->len = RFCOMM_MX_PN_LEN + 2; + + rfc_send_buf_uih (p_mcb, RFCOMM_MX_DLCI, p_buf); +} + + +/******************************************************************************* +** +** Function rfc_send_fcon +** +** Description This function sends Flow Control On Command. +** +*******************************************************************************/ +void rfc_send_fcon (tRFC_MCB *p_mcb, BOOLEAN is_command) +{ + BT_HDR *p_buf; + UINT8 *p_data; + + if ((p_buf = (BT_HDR *)osi_malloc(RFCOMM_CMD_BUF_SIZE)) == NULL) { + return; + } + + p_buf->offset = L2CAP_MIN_OFFSET + RFCOMM_CTRL_FRAME_LEN; + p_data = (UINT8 *)(p_buf + 1) + p_buf->offset; + + *p_data++ = RFCOMM_EA | RFCOMM_I_CR(is_command) | RFCOMM_MX_FCON; + *p_data++ = RFCOMM_EA | (RFCOMM_MX_FCON_LEN << 1); + + /* Total length is sizeof FCON data + mx header 2 */ + p_buf->len = RFCOMM_MX_FCON_LEN + 2; + + rfc_send_buf_uih (p_mcb, RFCOMM_MX_DLCI, p_buf); +} + + +/******************************************************************************* +** +** Function rfc_send_fcoff +** +** Description This function sends Flow Control Off Command. +** +*******************************************************************************/ +void rfc_send_fcoff (tRFC_MCB *p_mcb, BOOLEAN is_command) +{ + BT_HDR *p_buf; + UINT8 *p_data; + + if ((p_buf = (BT_HDR *)osi_malloc(RFCOMM_CMD_BUF_SIZE)) == NULL) { + return; + } + + p_buf->offset = L2CAP_MIN_OFFSET + RFCOMM_CTRL_FRAME_LEN; + p_data = (UINT8 *)(p_buf + 1) + p_buf->offset; + + *p_data++ = RFCOMM_EA | RFCOMM_I_CR(is_command) | RFCOMM_MX_FCOFF; + *p_data++ = RFCOMM_EA | (RFCOMM_MX_FCOFF_LEN << 1); + + /* Total length is sizeof FCOFF data + mx header 2 */ + p_buf->len = RFCOMM_MX_FCOFF_LEN + 2; + + rfc_send_buf_uih (p_mcb, RFCOMM_MX_DLCI, p_buf); +} + + +/******************************************************************************* +** +** Function rfc_send_msc +** +** Description This function sends Modem Status Command Frame. +** +*******************************************************************************/ +void rfc_send_msc (tRFC_MCB *p_mcb, UINT8 dlci, BOOLEAN is_command, + tPORT_CTRL *p_pars) +{ + BT_HDR *p_buf; + UINT8 *p_data; + UINT8 signals; + UINT8 break_duration; + UINT8 len; + + signals = p_pars->modem_signal; + break_duration = p_pars->break_signal; + + if ((p_buf = (BT_HDR *)osi_malloc(RFCOMM_CMD_BUF_SIZE)) == NULL) { + return; + } + + p_buf->offset = L2CAP_MIN_OFFSET + RFCOMM_CTRL_FRAME_LEN; + p_data = (UINT8 *)(p_buf + 1) + p_buf->offset; + + if (break_duration) { + len = RFCOMM_MX_MSC_LEN_WITH_BREAK; + } else { + len = RFCOMM_MX_MSC_LEN_NO_BREAK; + } + + *p_data++ = RFCOMM_EA | RFCOMM_I_CR(is_command) | RFCOMM_MX_MSC; + *p_data++ = RFCOMM_EA | (len << 1); + + *p_data++ = RFCOMM_EA | RFCOMM_CR_MASK | (dlci << RFCOMM_SHIFT_DLCI); + *p_data++ = RFCOMM_EA | + ((p_pars->fc) ? RFCOMM_MSC_FC : 0) | + ((signals & MODEM_SIGNAL_DTRDSR) ? RFCOMM_MSC_RTC : 0) | + ((signals & MODEM_SIGNAL_RTSCTS) ? RFCOMM_MSC_RTR : 0) | + ((signals & MODEM_SIGNAL_RI) ? RFCOMM_MSC_IC : 0) | + ((signals & MODEM_SIGNAL_DCD) ? RFCOMM_MSC_DV : 0); + + if (break_duration) { + *p_data++ = RFCOMM_EA | RFCOMM_MSC_BREAK_PRESENT_MASK | + (break_duration << RFCOMM_MSC_SHIFT_BREAK); + } + + /* Total length is sizeof MSC data + mx header 2 */ + p_buf->len = len + 2; + + rfc_send_buf_uih (p_mcb, RFCOMM_MX_DLCI, p_buf); +} + + +/******************************************************************************* +** +** Function rfc_send_rls +** +** Description This function sends Remote Line Status Command Frame. +** +*******************************************************************************/ +void rfc_send_rls (tRFC_MCB *p_mcb, UINT8 dlci, BOOLEAN is_command, UINT8 status) +{ + BT_HDR *p_buf; + UINT8 *p_data; + + if ((p_buf = (BT_HDR *)osi_malloc(RFCOMM_CMD_BUF_SIZE)) == NULL) { + return; + } + + p_buf->offset = L2CAP_MIN_OFFSET + RFCOMM_CTRL_FRAME_LEN; + p_data = (UINT8 *)(p_buf + 1) + p_buf->offset; + + *p_data++ = RFCOMM_EA | RFCOMM_I_CR(is_command) | RFCOMM_MX_RLS; + *p_data++ = RFCOMM_EA | (RFCOMM_MX_RLS_LEN << 1); + + *p_data++ = RFCOMM_EA | RFCOMM_CR_MASK | (dlci << RFCOMM_SHIFT_DLCI); + *p_data++ = RFCOMM_RLS_ERROR | status; + + /* Total length is sizeof RLS data + mx header 2 */ + p_buf->len = RFCOMM_MX_RLS_LEN + 2; + + rfc_send_buf_uih (p_mcb, RFCOMM_MX_DLCI, p_buf); +} + + +/******************************************************************************* +** +** Function rfc_send_nsc +** +** Description This function sends Non Supported Command Response. +** +*******************************************************************************/ +void rfc_send_nsc (tRFC_MCB *p_mcb) +{ + BT_HDR *p_buf; + UINT8 *p_data; + + if ((p_buf = (BT_HDR *)osi_malloc(RFCOMM_CMD_BUF_SIZE)) == NULL) { + return; + } + + p_buf->offset = L2CAP_MIN_OFFSET + RFCOMM_CTRL_FRAME_LEN; + p_data = (UINT8 *)(p_buf + 1) + p_buf->offset; + + *p_data++ = RFCOMM_EA | RFCOMM_I_CR(FALSE) | RFCOMM_MX_NSC; + *p_data++ = RFCOMM_EA | (RFCOMM_MX_NSC_LEN << 1); + + *p_data++ = rfc_cb.rfc.rx_frame.ea | + (rfc_cb.rfc.rx_frame.cr << RFCOMM_SHIFT_CR) | + rfc_cb.rfc.rx_frame.type; + + /* Total length is sizeof NSC data + mx header 2 */ + p_buf->len = RFCOMM_MX_NSC_LEN + 2; + + rfc_send_buf_uih (p_mcb, RFCOMM_MX_DLCI, p_buf); +} + + +/******************************************************************************* +** +** Function rfc_send_rpn +** +** Description This function sends Remote Port Negotiation Command +** +*******************************************************************************/ +void rfc_send_rpn (tRFC_MCB *p_mcb, UINT8 dlci, BOOLEAN is_command, + tPORT_STATE *p_pars, UINT16 mask) +{ + BT_HDR *p_buf; + UINT8 *p_data; + + if ((p_buf = (BT_HDR *)osi_malloc(RFCOMM_CMD_BUF_SIZE)) == NULL) { + return; + } + + p_buf->offset = L2CAP_MIN_OFFSET + RFCOMM_CTRL_FRAME_LEN; + p_data = (UINT8 *)(p_buf + 1) + p_buf->offset; + + *p_data++ = RFCOMM_EA | RFCOMM_I_CR(is_command) | RFCOMM_MX_RPN; + + if (!p_pars) { + *p_data++ = RFCOMM_EA | (RFCOMM_MX_RPN_REQ_LEN << 1); + + *p_data++ = RFCOMM_EA | RFCOMM_CR_MASK | (dlci << RFCOMM_SHIFT_DLCI); + + p_buf->len = RFCOMM_MX_RPN_REQ_LEN + 2; + } else { + *p_data++ = RFCOMM_EA | (RFCOMM_MX_RPN_LEN << 1); + + *p_data++ = RFCOMM_EA | RFCOMM_CR_MASK | (dlci << RFCOMM_SHIFT_DLCI); + *p_data++ = p_pars->baud_rate; + *p_data++ = (p_pars->byte_size << RFCOMM_RPN_BITS_SHIFT) + | (p_pars->stop_bits << RFCOMM_RPN_STOP_BITS_SHIFT) + | (p_pars->parity << RFCOMM_RPN_PARITY_SHIFT) + | (p_pars->parity_type << RFCOMM_RPN_PARITY_TYPE_SHIFT); + *p_data++ = p_pars->fc_type; + *p_data++ = p_pars->xon_char; + *p_data++ = p_pars->xoff_char; + *p_data++ = (mask & 0xFF); + *p_data++ = (mask >> 8); + + /* Total length is sizeof RPN data + mx header 2 */ + p_buf->len = RFCOMM_MX_RPN_LEN + 2; + } + + rfc_send_buf_uih (p_mcb, RFCOMM_MX_DLCI, p_buf); +} + +#if BT_RFCOMM_BQB_INCLUDED +/******************************************************************************* +** +** Function rfc_bqb_send_msc_cmd +** +** Description This function sends msc command for BQB test. +** +*******************************************************************************/ +void rfc_bqb_send_msc_cmd(BD_ADDR cert_pts_addr) +{ + UINT8 i; + UINT8 dlci; + BOOLEAN get_dlci = FALSE; + tPORT *p_port; + tPORT_CTRL *p_pars; + tRFC_MCB *p_mcb; + + if ((p_pars = (tPORT_CTRL *)osi_malloc(sizeof(tPORT_CTRL))) == NULL) { + return; + } + + p_pars->modem_signal = 0; + p_pars->break_signal = 0; + p_pars->fc = TRUE; + + p_mcb = port_find_mcb (cert_pts_addr); + + for (i = 0; i < MAX_RFC_PORTS; i++) { + p_port = &rfc_cb.port.port[i]; + if (p_port->in_use && !memcmp (p_port->bd_addr, cert_pts_addr, BD_ADDR_LEN)) { + dlci = p_port->dlci; + get_dlci = TRUE; + break; + } + } + + if (get_dlci) { + rfc_send_msc(p_mcb, dlci, TRUE, p_pars); + } else { + RFCOMM_TRACE_ERROR ("Get dlci fail"); + } + osi_free(p_pars); +} +#endif /* BT_RFCOMM_BQB_INCLUDED */ + +/******************************************************************************* +** +** Function rfc_send_test +** +** Description This function sends Test frame. +** +*******************************************************************************/ +void rfc_send_test (tRFC_MCB *p_mcb, BOOLEAN is_command, BT_HDR *p_buf) +{ + UINT8 *p_data; + UINT16 xx; + UINT8 *p_src, *p_dest; + + BT_HDR *p_buf_new; + if ((p_buf_new = (BT_HDR *)osi_malloc(RFCOMM_CMD_BUF_SIZE)) == NULL) { + return; + } + memcpy(p_buf_new, p_buf, sizeof(BT_HDR) + p_buf->offset + p_buf->len); + osi_free(p_buf); + p_buf = p_buf_new; + /* Shift buffer to give space for header */ + if (p_buf->offset < (L2CAP_MIN_OFFSET + RFCOMM_MIN_OFFSET + 2)) { + p_src = (UINT8 *) (p_buf + 1) + p_buf->offset + p_buf->len - 1; + p_dest = (UINT8 *) (p_buf + 1) + L2CAP_MIN_OFFSET + RFCOMM_MIN_OFFSET + 2 + p_buf->len - 1; + + for (xx = 0; xx < p_buf->len; xx++) { + *p_dest-- = *p_src--; + } + + p_buf->offset = L2CAP_MIN_OFFSET + RFCOMM_MIN_OFFSET + 2; + } + + /* Adjust offset by number of bytes we are going to fill */ + p_buf->offset -= 2; + p_data = (UINT8 *)(p_buf + 1) + p_buf->offset; + + *p_data++ = RFCOMM_EA | RFCOMM_I_CR(is_command) | RFCOMM_MX_TEST; + *p_data++ = RFCOMM_EA | (p_buf->len << 1); + + p_buf->len += 2; + + rfc_send_buf_uih (p_mcb, RFCOMM_MX_DLCI, p_buf); +} + +/******************************************************************************* +** +** Function rfc_send_credit +** +** Description This function sends a flow control credit in UIH frame. +** +*******************************************************************************/ +void rfc_send_credit(tRFC_MCB *p_mcb, UINT8 dlci, UINT8 credit) +{ + BT_HDR *p_buf; + UINT8 *p_data; + UINT8 cr = RFCOMM_CR(p_mcb->is_initiator, TRUE); + + if ((p_buf = (BT_HDR *)osi_malloc(RFCOMM_CMD_BUF_SIZE)) == NULL) { + return; + } + + p_buf->offset = L2CAP_MIN_OFFSET; + p_data = (UINT8 *)(p_buf + 1) + p_buf->offset; + + *p_data++ = RFCOMM_EA | cr | (dlci << RFCOMM_SHIFT_DLCI); + *p_data++ = RFCOMM_UIH | RFCOMM_PF; + *p_data++ = RFCOMM_EA | 0; + *p_data++ = credit; + *p_data = RFCOMM_UIH_FCS ((UINT8 *)(p_buf + 1) + p_buf->offset, dlci); + + p_buf->len = 5; + + rfc_check_send_cmd(p_mcb, p_buf); +} + + +/******************************************************************************* +** +** Function rfc_parse_data +** +** Description This function processes data packet received from L2CAP +** +*******************************************************************************/ +UINT8 rfc_parse_data (tRFC_MCB *p_mcb, MX_FRAME *p_frame, BT_HDR *p_buf) +{ + UINT8 ead, eal, fcs; + UINT8 *p_data = (UINT8 *)(p_buf + 1) + p_buf->offset; + UINT8 *p_start = p_data; + UINT16 len; + + if (p_buf->len < RFCOMM_CTRL_FRAME_LEN) { + RFCOMM_TRACE_ERROR ("Bad Length1: %d", p_buf->len); + return (RFC_EVENT_BAD_FRAME); + } + + RFCOMM_PARSE_CTRL_FIELD (ead, p_frame->cr, p_frame->dlci, p_data); + if ( !ead ) { + RFCOMM_TRACE_ERROR ("Bad Address(EA must be 1)"); + return (RFC_EVENT_BAD_FRAME); + } + RFCOMM_PARSE_TYPE_FIELD (p_frame->type, p_frame->pf, p_data); + RFCOMM_PARSE_LEN_FIELD (eal, len, p_data); + + p_buf->len -= (3 + !ead + !eal + 1); /* Additional 1 for FCS */ + p_buf->offset += (3 + !ead + !eal); + + /* handle credit if credit based flow control */ + if ((p_mcb->flow == PORT_FC_CREDIT) && (p_frame->type == RFCOMM_UIH) && + (p_frame->dlci != RFCOMM_MX_DLCI) && (p_frame->pf == 1)) { + p_frame->credit = *p_data++; + p_buf->len--; + p_buf->offset++; + } else { + p_frame->credit = 0; + } + + if (p_buf->len != len) { + RFCOMM_TRACE_ERROR ("Bad Length2 %d %d", p_buf->len, len); + return (RFC_EVENT_BAD_FRAME); + } + + fcs = *(p_data + len); + + /* All control frames that we are sending are sent with P=1, expect */ + /* reply with F=1 */ + /* According to TS 07.10 spec ivalid frames are discarded without */ + /* notification to the sender */ + switch (p_frame->type) { + case RFCOMM_SABME: + if (RFCOMM_FRAME_IS_RSP(p_mcb->is_initiator, p_frame->cr) + || !p_frame->pf || len || !RFCOMM_VALID_DLCI (p_frame->dlci) + || !rfc_check_fcs (RFCOMM_CTRL_FRAME_LEN, p_start, fcs)) { + RFCOMM_TRACE_ERROR ("Bad SABME"); + return (RFC_EVENT_BAD_FRAME); + } else { + return (RFC_EVENT_SABME); + } + + case RFCOMM_UA: + if (RFCOMM_FRAME_IS_CMD(p_mcb->is_initiator, p_frame->cr) + || !p_frame->pf || len || !RFCOMM_VALID_DLCI (p_frame->dlci) + || !rfc_check_fcs (RFCOMM_CTRL_FRAME_LEN, p_start, fcs)) { + RFCOMM_TRACE_ERROR ("Bad UA"); + return (RFC_EVENT_BAD_FRAME); + } else { + return (RFC_EVENT_UA); + } + + case RFCOMM_DM: + if (RFCOMM_FRAME_IS_CMD(p_mcb->is_initiator, p_frame->cr) + || len || !RFCOMM_VALID_DLCI(p_frame->dlci) + || !rfc_check_fcs (RFCOMM_CTRL_FRAME_LEN, p_start, fcs)) { + RFCOMM_TRACE_ERROR ("Bad DM"); + return (RFC_EVENT_BAD_FRAME); + } else { + return (RFC_EVENT_DM); + } + + case RFCOMM_DISC: + if (RFCOMM_FRAME_IS_RSP(p_mcb->is_initiator, p_frame->cr) + || !p_frame->pf || len || !RFCOMM_VALID_DLCI(p_frame->dlci) + || !rfc_check_fcs (RFCOMM_CTRL_FRAME_LEN, p_start, fcs)) { + RFCOMM_TRACE_ERROR ("Bad DISC"); + return (RFC_EVENT_BAD_FRAME); + } else { + return (RFC_EVENT_DISC); + } + + case RFCOMM_UIH: + if (!RFCOMM_VALID_DLCI(p_frame->dlci)) { + RFCOMM_TRACE_ERROR ("Bad UIH - invalid DLCI"); + return (RFC_EVENT_BAD_FRAME); + } else if (!rfc_check_fcs (2, p_start, fcs)) { + RFCOMM_TRACE_ERROR ("Bad UIH - FCS"); + return (RFC_EVENT_BAD_FRAME); + } else if (RFCOMM_FRAME_IS_RSP(p_mcb->is_initiator, p_frame->cr)) { + /* we assume that this is ok to allow bad implementations to work */ + RFCOMM_TRACE_ERROR ("Bad UIH - response"); + return (RFC_EVENT_UIH); + } else { + return (RFC_EVENT_UIH); + } + } + + return (RFC_EVENT_BAD_FRAME); +} + + +/******************************************************************************* +** +** Function rfc_process_mx_message +** +** Description This function processes UIH frames received on the +** multiplexer control channel. +** +*******************************************************************************/ +void rfc_process_mx_message (tRFC_MCB *p_mcb, BT_HDR *p_buf) +{ + UINT8 *p_data = (UINT8 *)(p_buf + 1) + p_buf->offset; + MX_FRAME *p_rx_frame = &rfc_cb.rfc.rx_frame; + UINT16 length = p_buf->len; + UINT8 ea, cr, mx_len; + BOOLEAN is_command; + + p_rx_frame->ea = *p_data & RFCOMM_EA; + p_rx_frame->cr = (*p_data & RFCOMM_CR_MASK) >> RFCOMM_SHIFT_CR; + p_rx_frame->type = *p_data++ & ~(RFCOMM_CR_MASK | RFCOMM_EA_MASK); + + if (!p_rx_frame->ea || !length) { + RFCOMM_TRACE_ERROR ("Illegal MX Frame ea:%d len:%d", p_rx_frame->ea, length); + osi_free (p_buf); + return; + } + + length--; + + is_command = p_rx_frame->cr; + + ea = *p_data & RFCOMM_EA; + + mx_len = *p_data++ >> RFCOMM_SHIFT_LENGTH1; + length--; + + if (!ea) { + mx_len += *p_data++ << RFCOMM_SHIFT_LENGTH2; + length --; + } + + if (mx_len != length) { + RFCOMM_TRACE_ERROR ("Bad MX frame"); + osi_free (p_buf); + return; + } + + switch (p_rx_frame->type) { + case RFCOMM_MX_PN: + if (length != RFCOMM_MX_PN_LEN) { + break; + } + + p_rx_frame->dlci = *p_data++ & RFCOMM_PN_DLCI_MASK; + p_rx_frame->u.pn.frame_type = *p_data & RFCOMM_PN_FRAME_TYPE_MASK; + p_rx_frame->u.pn.conv_layer = *p_data++ & RFCOMM_PN_CONV_LAYER_MASK; + p_rx_frame->u.pn.priority = *p_data++ & RFCOMM_PN_PRIORITY_MASK; + p_rx_frame->u.pn.t1 = *p_data++; + p_rx_frame->u.pn.mtu = *p_data + (*(p_data + 1) << 8); + p_data += 2; + p_rx_frame->u.pn.n2 = *p_data++; + p_rx_frame->u.pn.k = *p_data++ & RFCOMM_PN_K_MASK; + + if (!p_rx_frame->dlci + || !RFCOMM_VALID_DLCI (p_rx_frame->dlci) + || (p_rx_frame->u.pn.mtu < RFCOMM_MIN_MTU) + || (p_rx_frame->u.pn.mtu > RFCOMM_MAX_MTU)) { + RFCOMM_TRACE_ERROR ("Bad PN frame"); + break; + } + + osi_free (p_buf); + + rfc_process_pn (p_mcb, is_command, p_rx_frame); + return; + + case RFCOMM_MX_TEST: + if (!length) { + break; + } + + p_rx_frame->u.test.p_data = p_data; + p_rx_frame->u.test.data_len = length; + + p_buf->offset += 2; + p_buf->len -= 2; + + if (is_command) { + rfc_send_test (p_mcb, FALSE, p_buf); + } else { + rfc_process_test_rsp (p_mcb, p_buf); + } + return; + + case RFCOMM_MX_FCON: + if (length != RFCOMM_MX_FCON_LEN) { + break; + } + + osi_free (p_buf); + + rfc_process_fcon (p_mcb, is_command); + return; + + case RFCOMM_MX_FCOFF: + if (length != RFCOMM_MX_FCOFF_LEN) { + break; + } + + osi_free (p_buf); + + rfc_process_fcoff (p_mcb, is_command); + return; + + case RFCOMM_MX_MSC: + + ea = *p_data & RFCOMM_EA; + cr = (*p_data & RFCOMM_CR_MASK) >> RFCOMM_SHIFT_CR; + p_rx_frame->dlci = *p_data++ >> RFCOMM_SHIFT_DLCI; + + if (!ea || !cr || !p_rx_frame->dlci + || !RFCOMM_VALID_DLCI (p_rx_frame->dlci)) { + RFCOMM_TRACE_ERROR ("Bad MSC frame"); + break; + } + + p_rx_frame->u.msc.signals = *p_data++; + + if (mx_len == RFCOMM_MX_MSC_LEN_WITH_BREAK) { + p_rx_frame->u.msc.break_present = *p_data & RFCOMM_MSC_BREAK_PRESENT_MASK; + p_rx_frame->u.msc.break_duration = (*p_data & RFCOMM_MSC_BREAK_MASK) >> RFCOMM_MSC_SHIFT_BREAK; + } else { + p_rx_frame->u.msc.break_present = FALSE; + p_rx_frame->u.msc.break_duration = 0; + } + osi_free (p_buf); + + rfc_process_msc (p_mcb, is_command, p_rx_frame); + return; + + case RFCOMM_MX_NSC: + if ((length != RFCOMM_MX_NSC_LEN) || !is_command) { + break; + } + + p_rx_frame->u.nsc.ea = *p_data & RFCOMM_EA; + p_rx_frame->u.nsc.cr = (*p_data & RFCOMM_CR_MASK) >> RFCOMM_SHIFT_CR; + p_rx_frame->u.nsc.type = *p_data++ >> RFCOMM_SHIFT_DLCI; + + osi_free (p_buf); + + rfc_process_nsc (p_mcb, p_rx_frame); + return; + + case RFCOMM_MX_RPN: + if ((length != RFCOMM_MX_RPN_REQ_LEN) && (length != RFCOMM_MX_RPN_LEN)) { + break; + } + + ea = *p_data & RFCOMM_EA; + cr = (*p_data & RFCOMM_CR_MASK) >> RFCOMM_SHIFT_CR; + p_rx_frame->dlci = *p_data++ >> RFCOMM_SHIFT_DLCI; + + if (!ea || !cr || !p_rx_frame->dlci + || !RFCOMM_VALID_DLCI (p_rx_frame->dlci)) { + RFCOMM_TRACE_ERROR ("Bad RPN frame"); + break; + } + + p_rx_frame->u.rpn.is_request = (length == RFCOMM_MX_RPN_REQ_LEN); + + if (!p_rx_frame->u.rpn.is_request) { + p_rx_frame->u.rpn.baud_rate = *p_data++; + p_rx_frame->u.rpn.byte_size = (*p_data >> RFCOMM_RPN_BITS_SHIFT) & RFCOMM_RPN_BITS_MASK; + p_rx_frame->u.rpn.stop_bits = (*p_data >> RFCOMM_RPN_STOP_BITS_SHIFT) & RFCOMM_RPN_STOP_BITS_MASK; + p_rx_frame->u.rpn.parity = (*p_data >> RFCOMM_RPN_PARITY_SHIFT) & RFCOMM_RPN_PARITY_MASK; + p_rx_frame->u.rpn.parity_type = (*p_data++ >> RFCOMM_RPN_PARITY_TYPE_SHIFT) & RFCOMM_RPN_PARITY_TYPE_MASK; + + p_rx_frame->u.rpn.fc_type = *p_data++ & RFCOMM_FC_MASK; + p_rx_frame->u.rpn.xon_char = *p_data++; + p_rx_frame->u.rpn.xoff_char = *p_data++; + p_rx_frame->u.rpn.param_mask = (*p_data + (*(p_data + 1) << 8)) & RFCOMM_RPN_PM_MASK; + } + osi_free (p_buf); + + rfc_process_rpn (p_mcb, is_command, p_rx_frame->u.rpn.is_request, p_rx_frame); + return; + + case RFCOMM_MX_RLS: + if (length != RFCOMM_MX_RLS_LEN) { + break; + } + + ea = *p_data & RFCOMM_EA; + cr = (*p_data & RFCOMM_CR_MASK) >> RFCOMM_SHIFT_CR; + + p_rx_frame->dlci = *p_data++ >> RFCOMM_SHIFT_DLCI; + p_rx_frame->u.rls.line_status = (*p_data & ~0x01); + + if (!ea || !cr || !p_rx_frame->dlci + || !RFCOMM_VALID_DLCI (p_rx_frame->dlci)) { + RFCOMM_TRACE_ERROR ("Bad RPN frame"); + break; + } + + osi_free (p_buf); + + rfc_process_rls (p_mcb, is_command, p_rx_frame); + return; + } + + osi_free (p_buf); + + if (is_command) { + rfc_send_nsc (p_mcb); + } +} + +#endif ///(defined RFCOMM_INCLUDED && RFCOMM_INCLUDED == TRUE) diff --git a/lib/bt/host/bluedroid/stack/rfcomm/rfc_utils.c b/lib/bt/host/bluedroid/stack/rfcomm/rfc_utils.c new file mode 100644 index 00000000..b766918d --- /dev/null +++ b/lib/bt/host/bluedroid/stack/rfcomm/rfc_utils.c @@ -0,0 +1,510 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/***************************************************************************** + * + * This file contains collection of utility functions used the RFCOMM unit + * + *****************************************************************************/ + +#include "common/bt_target.h" + +#include "stack/btm_api.h" +#include "btm_int.h" +#include "stack/rfcdefs.h" +#include "stack/port_api.h" +#include "stack/port_ext.h" +#include "port_int.h" +#include "rfc_int.h" +#include "stack/btu.h" +#include "common/bt_defs.h" + +#include "osi/allocator.h" +#include "osi/mutex.h" + +#include + +#if (defined RFCOMM_INCLUDED && RFCOMM_INCLUDED == TRUE) +/******************************************************************************* +** +** Function rfc_calc_fcs +** +** Description Reversed CRC Table , 8-bit, poly=0x07 +** (GSM 07.10 TS 101 369 V6.3.0) +*******************************************************************************/ +static const UINT8 rfc_crctable[] = { + 0x00, 0x91, 0xE3, 0x72, 0x07, 0x96, 0xE4, 0x75, 0x0E, 0x9F, 0xED, 0x7C, 0x09, 0x98, 0xEA, 0x7B, + 0x1C, 0x8D, 0xFF, 0x6E, 0x1B, 0x8A, 0xF8, 0x69, 0x12, 0x83, 0xF1, 0x60, 0x15, 0x84, 0xF6, 0x67, + 0x38, 0xA9, 0xDB, 0x4A, 0x3F, 0xAE, 0xDC, 0x4D, 0x36, 0xA7, 0xD5, 0x44, 0x31, 0xA0, 0xD2, 0x43, + 0x24, 0xB5, 0xC7, 0x56, 0x23, 0xB2, 0xC0, 0x51, 0x2A, 0xBB, 0xC9, 0x58, 0x2D, 0xBC, 0xCE, 0x5F, + + 0x70, 0xE1, 0x93, 0x02, 0x77, 0xE6, 0x94, 0x05, 0x7E, 0xEF, 0x9D, 0x0C, 0x79, 0xE8, 0x9A, 0x0B, + 0x6C, 0xFD, 0x8F, 0x1E, 0x6B, 0xFA, 0x88, 0x19, 0x62, 0xF3, 0x81, 0x10, 0x65, 0xF4, 0x86, 0x17, + 0x48, 0xD9, 0xAB, 0x3A, 0x4F, 0xDE, 0xAC, 0x3D, 0x46, 0xD7, 0xA5, 0x34, 0x41, 0xD0, 0xA2, 0x33, + 0x54, 0xC5, 0xB7, 0x26, 0x53, 0xC2, 0xB0, 0x21, 0x5A, 0xCB, 0xB9, 0x28, 0x5D, 0xCC, 0xBE, 0x2F, + + 0xE0, 0x71, 0x03, 0x92, 0xE7, 0x76, 0x04, 0x95, 0xEE, 0x7F, 0x0D, 0x9C, 0xE9, 0x78, 0x0A, 0x9B, + 0xFC, 0x6D, 0x1F, 0x8E, 0xFB, 0x6A, 0x18, 0x89, 0xF2, 0x63, 0x11, 0x80, 0xF5, 0x64, 0x16, 0x87, + 0xD8, 0x49, 0x3B, 0xAA, 0xDF, 0x4E, 0x3C, 0xAD, 0xD6, 0x47, 0x35, 0xA4, 0xD1, 0x40, 0x32, 0xA3, + 0xC4, 0x55, 0x27, 0xB6, 0xC3, 0x52, 0x20, 0xB1, 0xCA, 0x5B, 0x29, 0xB8, 0xCD, 0x5C, 0x2E, 0xBF, + + 0x90, 0x01, 0x73, 0xE2, 0x97, 0x06, 0x74, 0xE5, 0x9E, 0x0F, 0x7D, 0xEC, 0x99, 0x08, 0x7A, 0xEB, + 0x8C, 0x1D, 0x6F, 0xFE, 0x8B, 0x1A, 0x68, 0xF9, 0x82, 0x13, 0x61, 0xF0, 0x85, 0x14, 0x66, 0xF7, + 0xA8, 0x39, 0x4B, 0xDA, 0xAF, 0x3E, 0x4C, 0xDD, 0xA6, 0x37, 0x45, 0xD4, 0xA1, 0x30, 0x42, 0xD3, + 0xB4, 0x25, 0x57, 0xC6, 0xB3, 0x22, 0x50, 0xC1, 0xBA, 0x2B, 0x59, 0xC8, 0xBD, 0x2C, 0x5E, 0xCF +}; + + +/******************************************************************************* +** +** Function rfc_calc_fcs +** +** Description This function calculate FCS for the RFCOMM frame +** (GSM 07.10 TS 101 369 V6.3.0) +** +** Input len - number of bytes in the message +** p - points to message +** +*******************************************************************************/ +UINT8 rfc_calc_fcs (UINT16 len, UINT8 *p) +{ + UINT8 fcs = 0xFF; + + while (len--) { + fcs = rfc_crctable[fcs ^ *p++]; + } + + /* Ones compliment */ + return (0xFF - fcs); +} + + +/******************************************************************************* +** +** Function rfc_check_fcs +** +** Description This function checks FCS for the RFCOMM frame +** (GSM 07.10 TS 101 369 V6.3.0) +** +** Input len - number of bytes in the message +** p - points to message +** received_fcs - received FCS +** +*******************************************************************************/ +BOOLEAN rfc_check_fcs (UINT16 len, UINT8 *p, UINT8 received_fcs) +{ + UINT8 fcs = 0xFF; + + while (len--) { + fcs = rfc_crctable[fcs ^ *p++]; + } + + /* Ones compliment */ + fcs = rfc_crctable[fcs ^ received_fcs]; + + /*0xCF is the reversed order of 11110011.*/ + return (fcs == 0xCF); +} + + +/******************************************************************************* +** +** Function rfc_alloc_multiplexer_channel +** +** Description This function returns existing or new control block for +** the BD_ADDR. +** +*******************************************************************************/ +tRFC_MCB *rfc_alloc_multiplexer_channel (BD_ADDR bd_addr, BOOLEAN is_initiator) +{ + int i, j; + tRFC_MCB *p_mcb = NULL; + RFCOMM_TRACE_DEBUG("rfc_alloc_multiplexer_channel: bd_addr:%02x:%02x:%02x:%02x:%02x:%02x", + bd_addr[0], bd_addr[1], bd_addr[2], bd_addr[3], bd_addr[4], bd_addr[5]); + RFCOMM_TRACE_DEBUG("rfc_alloc_multiplexer_channel:is_initiator:%d", is_initiator); + + for (i = 0; i < MAX_BD_CONNECTIONS; i++) { + RFCOMM_TRACE_DEBUG("rfc_alloc_multiplexer_channel rfc_cb.port.rfc_mcb[%d].state:%d", + i, rfc_cb.port.rfc_mcb[i].state); + RFCOMM_TRACE_DEBUG("(rfc_cb.port.rfc_mcb[i].bd_addr:%02x:%02x:%02x:%02x:%02x:%02x", + rfc_cb.port.rfc_mcb[i].bd_addr[0], rfc_cb.port.rfc_mcb[i].bd_addr[1], + rfc_cb.port.rfc_mcb[i].bd_addr[2], rfc_cb.port.rfc_mcb[i].bd_addr[3], + rfc_cb.port.rfc_mcb[i].bd_addr[4], rfc_cb.port.rfc_mcb[i].bd_addr[5]); + + if ((rfc_cb.port.rfc_mcb[i].state != RFC_MX_STATE_IDLE) + && (!memcmp (rfc_cb.port.rfc_mcb[i].bd_addr, bd_addr, BD_ADDR_LEN))) { + /* Multiplexer channel found do not change anything */ + /* If there was an inactivity timer running stop it now */ + if (rfc_cb.port.rfc_mcb[i].state == RFC_MX_STATE_CONNECTED) { + rfc_timer_stop (&rfc_cb.port.rfc_mcb[i]); + } + RFCOMM_TRACE_DEBUG("rfc_alloc_multiplexer_channel:is_initiator:%d, found, state:%d, p_mcb:%p", + is_initiator, rfc_cb.port.rfc_mcb[i].state, &rfc_cb.port.rfc_mcb[i]); + return (&rfc_cb.port.rfc_mcb[i]); + } + } + + /* connection with bd_addr does not exist */ + for (i = 0, j = rfc_cb.rfc.last_mux + 1; i < MAX_BD_CONNECTIONS; i++, j++) { + if (j >= MAX_BD_CONNECTIONS) { + j = 0; + } + + p_mcb = &rfc_cb.port.rfc_mcb[j]; + if (rfc_cb.port.rfc_mcb[j].state == RFC_MX_STATE_IDLE) { + /* New multiplexer control block */ + fixed_queue_free(p_mcb->cmd_q, NULL); + rfc_timer_free(p_mcb); + memset (p_mcb, 0, sizeof (tRFC_MCB)); + memcpy (p_mcb->bd_addr, bd_addr, BD_ADDR_LEN); + RFCOMM_TRACE_DEBUG("rfc_alloc_multiplexer_channel:is_initiator:%d, create new p_mcb:%p, index:%d", + is_initiator, &rfc_cb.port.rfc_mcb[j], j); + + p_mcb->cmd_q = fixed_queue_new(QUEUE_SIZE_MAX); + + p_mcb->is_initiator = is_initiator; + + rfc_timer_start (p_mcb, RFC_MCB_INIT_INACT_TIMER); + + rfc_cb.rfc.last_mux = (UINT8) j; + return (p_mcb); + } + } + return (NULL); +} + +void osi_free_fun(void *p) +{ + osi_free(p); +} +/******************************************************************************* +** +** Function rfc_release_multiplexer_channel +** +** Description This function returns existing or new control block for +** the BD_ADDR. +** +*******************************************************************************/ +void rfc_release_multiplexer_channel (tRFC_MCB *p_mcb) +{ + rfc_timer_free (p_mcb); + + fixed_queue_free(p_mcb->cmd_q, osi_free_fun); + + memset (p_mcb, 0, sizeof (tRFC_MCB)); + p_mcb->state = RFC_MX_STATE_IDLE; +} + + +/******************************************************************************* +** +** Function rfc_timer_start +** +** Description Start RFC Timer +** +*******************************************************************************/ +void rfc_timer_start (tRFC_MCB *p_mcb, UINT16 timeout) +{ + TIMER_LIST_ENT *p_tle = &p_mcb->tle; + + RFCOMM_TRACE_EVENT ("rfc_timer_start - timeout:%d", timeout); + + p_tle->param = (UINT32)p_mcb; + + btu_start_timer (p_tle, BTU_TTYPE_RFCOMM_MFC, timeout); +} + +/******************************************************************************* +** +** Function rfc_timer_stop +** +** Description Stop RFC Timer +** +*******************************************************************************/ +void rfc_timer_stop (tRFC_MCB *p_mcb) +{ + RFCOMM_TRACE_EVENT ("rfc_timer_stop"); + + btu_stop_timer (&p_mcb->tle); +} + +/******************************************************************************* +** +** Function rfc_timer_free +** +** Description Stop and free RFC Timer +** +*******************************************************************************/ +void rfc_timer_free (tRFC_MCB *p_mcb) +{ + RFCOMM_TRACE_EVENT ("rfc_timer_free"); + + btu_free_timer (&p_mcb->tle); + memset(&p_mcb->tle, 0, sizeof(TIMER_LIST_ENT)); +} + +/******************************************************************************* +** +** Function rfc_port_timer_start +** +** Description Start RFC Timer +** +*******************************************************************************/ +void rfc_port_timer_start (tPORT *p_port, UINT16 timeout) +{ + TIMER_LIST_ENT *p_tle = &p_port->rfc.tle; + + RFCOMM_TRACE_EVENT ("rfc_port_timer_start - timeout:%d", timeout); + + p_tle->param = (UINT32)p_port; + + btu_start_timer (p_tle, BTU_TTYPE_RFCOMM_PORT, timeout); +} + +/******************************************************************************* +** +** Function rfc_port_timer_stop +** +** Description Stop RFC Timer +** +*******************************************************************************/ +void rfc_port_timer_stop (tPORT *p_port) +{ + RFCOMM_TRACE_EVENT ("rfc_port_timer_stop"); + + btu_stop_timer (&p_port->rfc.tle); +} + +/******************************************************************************* +** +** Function rfc_port_timer_free +** +** Description Stop and free RFC Timer +** +*******************************************************************************/ +void rfc_port_timer_free (tPORT *p_port) +{ + RFCOMM_TRACE_EVENT ("rfc_port_timer_stop"); + + btu_free_timer (&p_port->rfc.tle); + memset(&p_port->rfc.tle, 0, sizeof(TIMER_LIST_ENT)); +} + +/******************************************************************************* +** +** Function rfc_check_mcb_active +** +** Description Check if there are any opened ports on the MCB if not +** start MCB Inact timer. +** +** Returns void +** +*******************************************************************************/ +void rfc_check_mcb_active (tRFC_MCB *p_mcb) +{ + UINT16 i; + + for (i = 0; i < RFCOMM_MAX_DLCI; i++) { + if (p_mcb->port_inx[i] != 0) { + p_mcb->is_disc_initiator = FALSE; + return; + } + } + /* The last port was DISCed. On the client side start disconnecting Mx */ + /* On the server side start inactivity timer */ + if (p_mcb->is_disc_initiator) { + p_mcb->is_disc_initiator = FALSE; + rfc_mx_sm_execute (p_mcb, RFC_MX_EVENT_CLOSE_REQ, NULL); + } else { + rfc_timer_start (p_mcb, RFC_MCB_RELEASE_INACT_TIMER); + } +} + + +/******************************************************************************* +** +** Function rfcomm_process_timeout +** +** Description The function called every second to check RFCOMM timers +** +** Returns void +** +*******************************************************************************/ +void rfcomm_process_timeout (TIMER_LIST_ENT *p_tle) +{ + switch (p_tle->event) { + case BTU_TTYPE_RFCOMM_MFC: + rfc_mx_sm_execute ((tRFC_MCB *)p_tle->param, RFC_EVENT_TIMEOUT, NULL); + break; + + case BTU_TTYPE_RFCOMM_PORT: + rfc_port_sm_execute ((tPORT *)p_tle->param, RFC_EVENT_TIMEOUT, NULL); + break; + + default: + break; + } +} + + +/******************************************************************************* +** +** Function rfc_sec_check_complete +** +** Description The function called when Security Manager finishes +** verification of the service side connection +** +** Returns void +** +*******************************************************************************/ +void rfc_sec_check_complete (BD_ADDR bd_addr, tBT_TRANSPORT transport, void *p_ref_data, UINT8 res) +{ + tPORT *p_port = (tPORT *)p_ref_data; + UNUSED(bd_addr); + UNUSED(transport); + + /* Verify that PORT is still waiting for Security to complete */ + if (!p_port->in_use + || ((p_port->rfc.state != RFC_STATE_ORIG_WAIT_SEC_CHECK) + && (p_port->rfc.state != RFC_STATE_TERM_WAIT_SEC_CHECK))) { + return; + } + + rfc_port_sm_execute ((tPORT *)p_ref_data, RFC_EVENT_SEC_COMPLETE, &res); +} + + +/******************************************************************************* +** +** Function rfc_port_closed +** +** Description The function is called when port is released based on the +** event received from the lower layer, typically L2CAP +** connection down, DISC, or DM frame. +** +** Returns void +** +*******************************************************************************/ +void rfc_port_closed (tPORT *p_port) +{ + tRFC_MCB *p_mcb = p_port->rfc.p_mcb; + + RFCOMM_TRACE_DEBUG ("rfc_port_closed"); + + rfc_port_timer_stop (p_port); + + p_port->rfc.state = RFC_STATE_CLOSED; + + /* If multiplexer channel was up mark it as down */ + if (p_mcb) { + p_mcb->port_inx[p_port->dlci] = 0; + + /* If there are no more ports opened on this MCB release it */ + rfc_check_mcb_active (p_mcb); + } + + /* Notify port that RFC connection is gone */ + port_rfc_closed (p_port, PORT_CLOSED); +} + +/******************************************************************************* +** +** Function rfc_inc_credit +** +** Description The function is called when a credit is received in a UIH +** frame. It increments the TX credit count, and if data +** flow had halted, it restarts it. +** +** Returns void +** +*******************************************************************************/ +void rfc_inc_credit (tPORT *p_port, UINT8 credit) +{ + if (p_port->rfc.p_mcb->flow == PORT_FC_CREDIT) { + p_port->credit_tx += credit; + + RFCOMM_TRACE_EVENT ("rfc_inc_credit:%d", p_port->credit_tx); + + if (p_port->tx.peer_fc == TRUE) { + PORT_FlowInd(p_port->rfc.p_mcb, p_port->dlci, TRUE); + } + } +} + +/******************************************************************************* +** +** Function rfc_dec_credit +** +** Description The function is called when a UIH frame of user data is +** sent. It decrements the credit count. If credit count +** Reaches zero, peer_fc is set. +** +** Returns void +** +*******************************************************************************/ +void rfc_dec_credit (tPORT *p_port) +{ + if (p_port->rfc.p_mcb->flow == PORT_FC_CREDIT) { + if (p_port->credit_tx > 0) { + p_port->credit_tx--; + } + + if (p_port->credit_tx == 0) { + p_port->tx.peer_fc = TRUE; + } + } +} + + +/******************************************************************************* +** +** Function rfc_check_send_cmd +** +** Description This function is called to send an RFCOMM command message +** or to handle the RFCOMM command message queue. +** +** Returns void +** +*******************************************************************************/ +void rfc_check_send_cmd(tRFC_MCB *p_mcb, BT_HDR *p_buf) +{ + BT_HDR *p; + + /* if passed a buffer queue it */ + if (p_buf != NULL) { + if (p_mcb->cmd_q == NULL) { + RFCOMM_TRACE_ERROR("%s: empty queue: p_mcb = %p p_mcb->lcid = %u cached p_mcb = %p", + __func__, p_mcb, p_mcb->lcid, + rfc_find_lcid_mcb(p_mcb->lcid)); + } + fixed_queue_enqueue(p_mcb->cmd_q, p_buf, FIXED_QUEUE_MAX_TIMEOUT); + } + + /* handle queue if L2CAP not congested */ + while (p_mcb->l2cap_congested == FALSE) { + if ((p = (BT_HDR *)fixed_queue_dequeue(p_mcb->cmd_q, 0)) == NULL) { + break; + } + + + L2CA_DataWrite (p_mcb->lcid, p); + } +} + + +#endif ///(defined RFCOMM_INCLUDED && RFCOMM_INCLUDED == TRUE) diff --git a/lib/bt/host/bluedroid/stack/sdp/include/sdpint.h b/lib/bt/host/bluedroid/stack/sdp/include/sdpint.h new file mode 100644 index 00000000..ab97b702 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/sdp/include/sdpint.h @@ -0,0 +1,319 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains internally used SDP definitions + * + ******************************************************************************/ + +#ifndef SDP_INT_H +#define SDP_INT_H + +#include "common/bt_target.h" +#include "common/bt_defs.h" +#include "stack/sdp_api.h" +#include "stack/l2c_api.h" +#include "osi/list.h" + +#if (SDP_INCLUDED == TRUE) +/* Continuation length - we use a 2-byte offset */ +#define SDP_CONTINUATION_LEN 2 +#define SDP_MAX_CONTINUATION_LEN 16 /* As per the spec */ + +/* Timeout definitions. */ +#define SDP_INACT_TIMEOUT 30 /* Inactivity timeout */ +#if BT_SDP_BQB_INCLUDED +#define SDP_BQB_INACT_TIMEOUT 90 /* Inactivity timeout for BQB test */ +#endif /* BT_SDP_BQB_INCLUDED */ + +/* Define the Out-Flow default values. */ +#define SDP_OFLOW_QOS_FLAG 0 +#define SDP_OFLOW_SERV_TYPE 0 +#define SDP_OFLOW_TOKEN_RATE 0 +#define SDP_OFLOW_TOKEN_BUCKET_SIZE 0 +#define SDP_OFLOW_PEAK_BANDWIDTH 0 +#define SDP_OFLOW_LATENCY 0 +#define SDP_OFLOW_DELAY_VARIATION 0 + +/* Define the In-Flow default values. */ +#define SDP_IFLOW_QOS_FLAG 0 +#define SDP_IFLOW_SERV_TYPE 0 +#define SDP_IFLOW_TOKEN_RATE 0 +#define SDP_IFLOW_TOKEN_BUCKET_SIZE 0 +#define SDP_IFLOW_PEAK_BANDWIDTH 0 +#define SDP_IFLOW_LATENCY 0 +#define SDP_IFLOW_DELAY_VARIATION 0 + +#define SDP_LINK_TO 0 + +/* Define the type of device notification. */ +/* (Inquiry Scan and Page Scan) */ +#define SDP_DEVICE_NOTI_LEN sizeof (BT_HDR) + \ + HCIC_PREAMBLE_SIZE + \ + HCIC_PARAM_SIZE_WRITE_PARAM1 + +#define SDP_DEVICE_NOTI_FLAG 0x03 + +/* Define the Protocol Data Unit (PDU) types. +*/ +#define SDP_PDU_ERROR_RESPONSE 0x01 +#define SDP_PDU_SERVICE_SEARCH_REQ 0x02 +#define SDP_PDU_SERVICE_SEARCH_RSP 0x03 +#define SDP_PDU_SERVICE_ATTR_REQ 0x04 +#define SDP_PDU_SERVICE_ATTR_RSP 0x05 +#define SDP_PDU_SERVICE_SEARCH_ATTR_REQ 0x06 +#define SDP_PDU_SERVICE_SEARCH_ATTR_RSP 0x07 + +/* Max UUIDs and attributes we support per sequence */ +#define MAX_UUIDS_PER_SEQ 8 +#define MAX_ATTR_PER_SEQ 8 + +/* Max length we support for any attribute */ +// btla-specific ++ +#ifdef SDP_MAX_ATTR_LEN +#define MAX_ATTR_LEN SDP_MAX_ATTR_LEN +#else +#define MAX_ATTR_LEN 256 +#endif +// btla-specific -- + +/* Internal UUID sequence representation */ +typedef struct { + UINT16 len; + UINT8 value[MAX_UUID_SIZE]; +} tUID_ENT; + +typedef struct { + UINT16 num_uids; + tUID_ENT uuid_entry[MAX_UUIDS_PER_SEQ]; +} tSDP_UUID_SEQ; + + +/* Internal attribute sequence definitions */ +typedef struct { + UINT16 start; + UINT16 end; +} tATT_ENT; + +typedef struct { + UINT16 num_attr; + tATT_ENT attr_entry[MAX_ATTR_PER_SEQ]; +} tSDP_ATTR_SEQ; + + +/* Define the attribute element of the SDP database record */ +typedef struct { + UINT32 len; /* Number of bytes in the entry */ + UINT8 *value_ptr; /* Points to attr_pad */ + UINT16 id; + UINT8 type; +} tSDP_ATTRIBUTE; + +/* An SDP record consists of a handle, and 1 or more attributes */ +typedef struct { + UINT32 record_handle; + UINT32 free_pad_ptr; + UINT16 num_attributes; + tSDP_ATTRIBUTE attribute[SDP_MAX_REC_ATTR]; + UINT8 attr_pad[SDP_MAX_PAD_LEN]; +} tSDP_RECORD; + + +/* Define the SDP database */ +typedef struct { + UINT32 di_primary_handle; /* Device ID Primary record or NULL if nonexistent */ + UINT16 num_records; + list_t *p_record_list; +} tSDP_DB; + +enum { + SDP_IS_SEARCH, + SDP_IS_ATTR_SEARCH, +}; + +#if SDP_SERVER_ENABLED == TRUE +/* Continuation information for the SDP server response */ +typedef struct { + UINT16 next_attr_index; /* attr index for next continuation response */ + UINT16 next_attr_start_id; /* attr id to start with for the attr index in next cont. response */ + tSDP_RECORD *prev_sdp_rec; /* last sdp record that was completely sent in the response */ + BOOLEAN last_attr_seq_desc_sent; /* whether attr seq length has been sent previously */ + UINT16 attr_offset; /* offset within the attr to keep trak of partial attributes in the responses */ +} tSDP_CONT_INFO; +#endif /* SDP_SERVER_ENABLED == TRUE */ + +/* Define the SDP Connection Control Block */ +typedef struct { +#define SDP_STATE_IDLE 0 +#define SDP_STATE_CONN_SETUP 1 +#define SDP_STATE_CFG_SETUP 2 +#define SDP_STATE_CONNECTED 3 + UINT8 con_state; + +#define SDP_FLAGS_IS_ORIG 0x01 +#define SDP_FLAGS_HIS_CFG_DONE 0x02 +#define SDP_FLAGS_MY_CFG_DONE 0x04 + UINT8 con_flags; + + BD_ADDR device_address; + TIMER_LIST_ENT timer_entry; + UINT16 rem_mtu_size; + UINT16 connection_id; + UINT16 list_len; /* length of the response in the GKI buffer */ + UINT8 *rsp_list; /* pointer to GKI buffer holding response */ + +#if SDP_CLIENT_ENABLED == TRUE + tSDP_DISCOVERY_DB *p_db; /* Database to save info into */ + tSDP_DISC_CMPL_CB *p_cb; /* Callback for discovery done */ + tSDP_DISC_CMPL_CB2 *p_cb2; /* Callback for discovery done piggy back with the user data */ + void *user_data; /* piggy back user data */ + UINT32 handles[SDP_MAX_DISC_SERVER_RECS]; /* Discovered server record handles */ + UINT16 num_handles; /* Number of server handles */ + UINT16 cur_handle; /* Current handle being processed */ + UINT16 transaction_id; + UINT16 disconnect_reason; /* Disconnect reason */ +#if (defined(SDP_BROWSE_PLUS) && SDP_BROWSE_PLUS == TRUE) + UINT16 cur_uuid_idx; +#endif + +#define SDP_DISC_WAIT_CONN 0 +#define SDP_DISC_WAIT_HANDLES 1 +#define SDP_DISC_WAIT_ATTR 2 +#define SDP_DISC_WAIT_SEARCH_ATTR 3 +#define SDP_DISC_WAIT_CANCEL 5 + + UINT8 disc_state; + UINT8 is_attr_search; +#endif /* SDP_CLIENT_ENABLED == TRUE */ + +#if SDP_SERVER_ENABLED == TRUE + UINT16 cont_offset; /* Continuation state data in the server response */ + tSDP_CONT_INFO cont_info; /* structure to hold continuation information for the server response */ +#endif /* SDP_SERVER_ENABLED == TRUE */ + +} tCONN_CB; + + +/* The main SDP control block */ +typedef struct { + tL2CAP_CFG_INFO l2cap_my_cfg; /* My L2CAP config */ + tCONN_CB ccb[SDP_MAX_CONNECTIONS]; +#if SDP_SERVER_ENABLED == TRUE + tSDP_DB server_db; +#endif + tL2CAP_APPL_INFO reg_info; /* L2CAP Registration info */ + UINT16 max_attr_list_size; /* Max attribute list size to use */ + UINT16 max_recs_per_search; /* Max records we want per seaarch */ + UINT8 trace_level; +} tSDP_CB; + +#ifdef __cplusplus +extern "C" { +#endif +/* Global SDP data */ +#if SDP_DYNAMIC_MEMORY == FALSE +extern tSDP_CB sdp_cb; +#else +extern tSDP_CB *sdp_cb_ptr; +#define sdp_cb (*sdp_cb_ptr) +#endif + +#ifdef __cplusplus +} +#endif + +/* Functions provided by sdp_main.c */ +extern void sdp_init (void); +extern void sdp_deinit (void); +extern void sdp_disconnect (tCONN_CB *p_ccb, UINT16 reason); + +#if (defined(SDP_DEBUG) && SDP_DEBUG == TRUE) +extern UINT16 sdp_set_max_attr_list_size (UINT16 max_size); +#endif + +/* Functions provided by sdp_conn.c +*/ +extern void sdp_conn_rcv_l2e_conn_ind (BT_HDR *p_msg); +extern void sdp_conn_rcv_l2e_conn_cfm (BT_HDR *p_msg); +extern void sdp_conn_rcv_l2e_disc (BT_HDR *p_msg); +extern void sdp_conn_rcv_l2e_config_ind (BT_HDR *p_msg); +extern void sdp_conn_rcv_l2e_config_cfm (BT_HDR *p_msg); +extern void sdp_conn_rcv_l2e_conn_failed (BT_HDR *p_msg); +extern void sdp_conn_rcv_l2e_connected (BT_HDR *p_msg); +extern void sdp_conn_rcv_l2e_conn_failed (BT_HDR *p_msg); +extern void sdp_conn_rcv_l2e_data (BT_HDR *p_msg); +extern void sdp_conn_timeout (tCONN_CB *p_ccb); + +extern tCONN_CB *sdp_conn_originate (UINT8 *p_bd_addr); + +/* Functions provided by sdp_utils.c +*/ +extern tCONN_CB *sdpu_find_ccb_by_cid (UINT16 cid); +extern tCONN_CB *sdpu_find_ccb_by_db (tSDP_DISCOVERY_DB *p_db); +extern tCONN_CB *sdpu_allocate_ccb (void); +extern void sdpu_release_ccb (tCONN_CB *p_ccb); + +extern UINT8 *sdpu_build_attrib_seq (UINT8 *p_out, UINT16 *p_attr, UINT16 num_attrs); +extern UINT8 *sdpu_build_attrib_entry (UINT8 *p_out, tSDP_ATTRIBUTE *p_attr); +extern void sdpu_build_n_send_error (tCONN_CB *p_ccb, UINT16 trans_num, UINT16 error_code, char *p_error_text); + +extern UINT8 *sdpu_extract_attr_seq (UINT8 *p, UINT16 param_len, tSDP_ATTR_SEQ *p_seq); +extern UINT8 *sdpu_extract_uid_seq (UINT8 *p, UINT16 param_len, tSDP_UUID_SEQ *p_seq); + +extern UINT8 *sdpu_get_len_from_type (UINT8 *p, UINT8 type, UINT32 *p_len); +extern BOOLEAN sdpu_is_base_uuid (UINT8 *p_uuid); +extern BOOLEAN sdpu_compare_uuid_arrays (UINT8 *p_uuid1, UINT32 len1, UINT8 *p_uuid2, UINT16 len2); +extern BOOLEAN sdpu_compare_bt_uuids (tBT_UUID *p_uuid1, tBT_UUID *p_uuid2); +extern BOOLEAN sdpu_compare_uuid_with_attr (tBT_UUID *p_btuuid, tSDP_DISC_ATTR *p_attr); + +extern void sdpu_sort_attr_list( UINT16 num_attr, tSDP_DISCOVERY_DB *p_db ); +extern UINT16 sdpu_get_list_len( tSDP_UUID_SEQ *uid_seq, tSDP_ATTR_SEQ *attr_seq ); +extern UINT16 sdpu_get_attrib_seq_len(tSDP_RECORD *p_rec, tSDP_ATTR_SEQ *attr_seq); +extern UINT16 sdpu_get_attrib_entry_len(tSDP_ATTRIBUTE *p_attr); +extern UINT8 *sdpu_build_partial_attrib_entry (UINT8 *p_out, tSDP_ATTRIBUTE *p_attr, UINT16 len, UINT16 *offset); +extern void sdpu_uuid16_to_uuid128(UINT16 uuid16, UINT8 *p_uuid128); + +/* Functions provided by sdp_db.c +*/ +extern tSDP_RECORD *sdp_db_service_search (tSDP_RECORD *p_rec, tSDP_UUID_SEQ *p_seq); +extern tSDP_RECORD *sdp_db_find_record (UINT32 handle); +extern tSDP_ATTRIBUTE *sdp_db_find_attr_in_rec (tSDP_RECORD *p_rec, UINT16 start_attr, UINT16 end_attr); + + +/* Functions provided by sdp_server.c +*/ +#if SDP_SERVER_ENABLED == TRUE +extern void sdp_server_handle_client_req (tCONN_CB *p_ccb, BT_HDR *p_msg); +#else +#define sdp_server_handle_client_req(p_ccb, p_msg) +#endif + +/* Functions provided by sdp_discovery.c +*/ +#if SDP_CLIENT_ENABLED == TRUE +extern void sdp_disc_connected (tCONN_CB *p_ccb); +extern void sdp_disc_server_rsp (tCONN_CB *p_ccb, BT_HDR *p_msg); +#else +#define sdp_disc_connected(p_ccb) +#define sdp_disc_server_rsp(p_ccb, p_msg) +#endif + +#endif ///SDP_INCLUDED == TRUE + +#endif diff --git a/lib/bt/host/bluedroid/stack/sdp/sdp_api.c b/lib/bt/host/bluedroid/stack/sdp/sdp_api.c new file mode 100644 index 00000000..0103e4de --- /dev/null +++ b/lib/bt/host/bluedroid/stack/sdp/sdp_api.c @@ -0,0 +1,1242 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * this file contains SDP interface functions + * + ******************************************************************************/ + +//#include +#include +//#include + +#include "common/bt_target.h" +//#include "bt_utils.h" +#include "stack/l2cdefs.h" +#include "stack/hcidefs.h" +#include "stack/hcimsgs.h" +#include "common/bt_defs.h" +#include "stack/sdp_api.h" +#include "sdpint.h" +#include "stack/btu.h" + +#if (SDP_INCLUDED == TRUE) +/********************************************************************** +** C L I E N T F U N C T I O N P R O T O T Y P E S * +***********************************************************************/ + +/******************************************************************************* +** +** Function SDP_InitDiscoveryDb +** +** Description This function is called to initialize a discovery database. +** +** Parameters: p_db - (input) address of an area of memory where the +** discovery database is managed. +** len - (input) size (in bytes) of the memory +** NOTE: This must be larger than sizeof(tSDP_DISCOVERY_DB) +** num_uuid - (input) number of UUID filters applied +** p_uuid_list - (input) list of UUID filters +** num_attr - (input) number of attribute filters applied +** p_attr_list - (input) list of attribute filters +** +** +** Returns BOOLEAN +** TRUE if successful +** FALSE if one or more parameters are bad +** +*******************************************************************************/ +BOOLEAN SDP_InitDiscoveryDb (tSDP_DISCOVERY_DB *p_db, UINT32 len, UINT16 num_uuid, + tSDP_UUID *p_uuid_list, UINT16 num_attr, UINT16 *p_attr_list) +{ +#if SDP_CLIENT_ENABLED == TRUE + UINT16 xx; + + /* verify the parameters */ + if (p_db == NULL || (sizeof (tSDP_DISCOVERY_DB) > len) || + num_attr > SDP_MAX_ATTR_FILTERS || num_uuid > SDP_MAX_UUID_FILTERS) { + SDP_TRACE_ERROR("SDP_InitDiscoveryDb Illegal param: p_db 0x%x, len %d, num_uuid %d, num_attr %d", + (UINT32)p_db, len, num_uuid, num_attr); + + return (FALSE); + } + + memset (p_db, 0, (size_t)len); + + p_db->mem_size = len - sizeof (tSDP_DISCOVERY_DB); + p_db->mem_free = p_db->mem_size; + p_db->p_first_rec = NULL; + p_db->p_free_mem = (UINT8 *)(p_db + 1); + + for (xx = 0; xx < num_uuid; xx++) { + p_db->uuid_filters[xx] = *p_uuid_list++; + } + + p_db->num_uuid_filters = num_uuid; + + for (xx = 0; xx < num_attr; xx++) { + p_db->attr_filters[xx] = *p_attr_list++; + } + + /* sort attributes */ + sdpu_sort_attr_list( num_attr, p_db ); + + p_db->num_attr_filters = num_attr; +#endif + return (TRUE); +} + + + +/******************************************************************************* +** +** Function SDP_CancelServiceSearch +** +** Description This function cancels an active query to an SDP server. +** +** Returns TRUE if discovery cancelled, FALSE if a matching activity is not found. +** +*******************************************************************************/ +BOOLEAN SDP_CancelServiceSearch (tSDP_DISCOVERY_DB *p_db) +{ +#if SDP_CLIENT_ENABLED == TRUE + tCONN_CB *p_ccb = sdpu_find_ccb_by_db (p_db); + if (!p_ccb) { + return (FALSE); + } + + sdp_disconnect (p_ccb, SDP_CANCEL); + p_ccb->disc_state = SDP_DISC_WAIT_CANCEL; +#endif + return (TRUE); +} + + + +/******************************************************************************* +** +** Function SDP_ServiceSearchRequest +** +** Description This function queries an SDP server for information. +** +** Returns TRUE if discovery started, FALSE if failed. +** +*******************************************************************************/ +BOOLEAN SDP_ServiceSearchRequest (UINT8 *p_bd_addr, tSDP_DISCOVERY_DB *p_db, + tSDP_DISC_CMPL_CB *p_cb) +{ +#if SDP_CLIENT_ENABLED == TRUE + tCONN_CB *p_ccb; + + /* Specific BD address */ + p_ccb = sdp_conn_originate (p_bd_addr); + + if (!p_ccb) { + return (FALSE); + } + + p_ccb->disc_state = SDP_DISC_WAIT_CONN; + p_ccb->p_db = p_db; + p_ccb->p_cb = p_cb; + + return (TRUE); +#else + return (FALSE); +#endif +} + + +/******************************************************************************* +** +** Function SDP_ServiceSearchAttributeRequest +** +** Description This function queries an SDP server for information. +** +** The difference between this API function and the function +** SDP_ServiceSearchRequest2 is that this one does a +** combined ServiceSearchAttributeRequest SDP function. +** +** Returns TRUE if discovery started, FALSE if failed. +** +*******************************************************************************/ +BOOLEAN SDP_ServiceSearchAttributeRequest (UINT8 *p_bd_addr, tSDP_DISCOVERY_DB *p_db, + tSDP_DISC_CMPL_CB *p_cb) +{ +#if SDP_CLIENT_ENABLED == TRUE + tCONN_CB *p_ccb; + + /* Specific BD address */ + p_ccb = sdp_conn_originate (p_bd_addr); + + if (!p_ccb) { + return (FALSE); + } + + p_ccb->disc_state = SDP_DISC_WAIT_CONN; + p_ccb->p_db = p_db; + p_ccb->p_cb = p_cb; + + p_ccb->is_attr_search = TRUE; + + return (TRUE); +#else + return (FALSE); +#endif +} +/******************************************************************************* +** +** Function SDP_ServiceSearchAttributeRequest2 +** +** Description This function queries an SDP server for information. +** +** The difference between this API function and the function +** SDP_ServiceSearchRequest is that this one does a +** combined ServiceSearchAttributeRequest SDP function with the +** user data piggyback +** +** Returns TRUE if discovery started, FALSE if failed. +** +*******************************************************************************/ +BOOLEAN SDP_ServiceSearchAttributeRequest2 (UINT8 *p_bd_addr, tSDP_DISCOVERY_DB *p_db, + tSDP_DISC_CMPL_CB2 *p_cb2, void *user_data) +{ +#if SDP_CLIENT_ENABLED == TRUE + tCONN_CB *p_ccb; + + /* Specific BD address */ + p_ccb = sdp_conn_originate (p_bd_addr); + + if (!p_ccb) { + return (FALSE); + } + + p_ccb->disc_state = SDP_DISC_WAIT_CONN; + p_ccb->p_db = p_db; + p_ccb->p_cb2 = p_cb2; + + p_ccb->is_attr_search = TRUE; + p_ccb->user_data = user_data; + + return (TRUE); +#else + return (FALSE); +#endif +} + +#if SDP_CLIENT_ENABLED == TRUE +void SDP_SetIdleTimeout (BD_ADDR addr, UINT16 timeout) +{ + UNUSED(addr); + UNUSED(timeout); +} +#endif + +/******************************************************************************* +** +** Function SDP_FindAttributeInDb +** +** Description This function queries an SDP database for a specific attribute. +** If the p_start_rec pointer is NULL, it looks from the beginning +** of the database, else it continues from the next record after +** p_start_rec. +** +** Returns Pointer to matching record, or NULL +** +*******************************************************************************/ +tSDP_DISC_REC *SDP_FindAttributeInDb (tSDP_DISCOVERY_DB *p_db, UINT16 attr_id, + tSDP_DISC_REC *p_start_rec) +{ +#if SDP_CLIENT_ENABLED == TRUE + tSDP_DISC_REC *p_rec; + tSDP_DISC_ATTR *p_attr; + + /* Must have a valid database */ + if (p_db == NULL) { + return (NULL); + } + + if (!p_start_rec) { + p_rec = p_db->p_first_rec; + } else { + p_rec = p_start_rec->p_next_rec; + } + + while (p_rec) { + p_attr = p_rec->p_first_attr; + while (p_attr) { + if (p_attr->attr_id == attr_id) { + return (p_rec); + } + + p_attr = p_attr->p_next_attr; + } + + p_rec = p_rec->p_next_rec; + } +#endif + /* If here, no matching attribute found */ + return (NULL); +} + + +/******************************************************************************* +** +** Function SDP_FindAttributeInRec +** +** Description This function searches an SDP discovery record for a specific +** attribute. +** +** Returns Pointer to matching attribute entry, or NULL +** +*******************************************************************************/ +tSDP_DISC_ATTR *SDP_FindAttributeInRec (tSDP_DISC_REC *p_rec, UINT16 attr_id) +{ +#if SDP_CLIENT_ENABLED == TRUE + tSDP_DISC_ATTR *p_attr; + + p_attr = p_rec->p_first_attr; + while (p_attr) { + if (p_attr->attr_id == attr_id) { + return (p_attr); + } + + p_attr = p_attr->p_next_attr; + } +#endif + /* If here, no matching attribute found */ + return (NULL); +} + +/******************************************************************************* +** +** Function SDP_FindServiceUUIDInRec +** +** Description This function is called to read the service UUID within a record +** if there is any. +** +** Parameters: p_rec - pointer to a SDP record. +** p_uuid - output parameter to save the UUID found. +** +** Returns TRUE if found, otherwise FALSE. +** +*******************************************************************************/ +BOOLEAN SDP_FindServiceUUIDInRec(tSDP_DISC_REC *p_rec, tBT_UUID *p_uuid) +{ +#if SDP_CLIENT_ENABLED == TRUE + tSDP_DISC_ATTR *p_attr, *p_sattr, *p_extra_sattr; + + p_attr = p_rec->p_first_attr; + + while (p_attr) { + if ((p_attr->attr_id == ATTR_ID_SERVICE_CLASS_ID_LIST) + && (SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == DATA_ELE_SEQ_DESC_TYPE)) { + for (p_sattr = p_attr->attr_value.v.p_sub_attr; p_sattr; p_sattr = p_sattr->p_next_attr) { + if (SDP_DISC_ATTR_TYPE(p_sattr->attr_len_type) == UUID_DESC_TYPE) { + if (SDP_DISC_ATTR_LEN(p_sattr->attr_len_type) == LEN_UUID_16) { + p_uuid->len = LEN_UUID_16; + p_uuid->uu.uuid16 = p_sattr->attr_value.v.u16; + } else if (SDP_DISC_ATTR_LEN(p_sattr->attr_len_type) == LEN_UUID_128) { + p_uuid->len = LEN_UUID_128; + for (uint8_t i = 0; i != LEN_UUID_128; ++i) { + p_uuid->uu.uuid128[i] = p_sattr->attr_value.v.array[LEN_UUID_128 - i - 1]; + } + } else if (SDP_DISC_ATTR_LEN(p_sattr->attr_len_type) == LEN_UUID_32) { + p_uuid->len = LEN_UUID_32; + p_uuid->uu.uuid32 = p_sattr->attr_value.v.u32; + } + + return (TRUE); + } + + /* Checking for Toyota G Block Car Kit: + ** This car kit puts an extra data element sequence + ** where the UUID is suppose to be!!! + */ + else { + if (SDP_DISC_ATTR_TYPE(p_sattr->attr_len_type) == DATA_ELE_SEQ_DESC_TYPE) { + /* Look through data element sequence until no more UUIDs */ + for (p_extra_sattr = p_sattr->attr_value.v.p_sub_attr; p_extra_sattr; p_extra_sattr = p_extra_sattr->p_next_attr) { + /* Increment past this to see if the next attribut is UUID */ + if ((SDP_DISC_ATTR_TYPE(p_extra_sattr->attr_len_type) == UUID_DESC_TYPE) + /* only support 16 bits UUID for now */ + && (SDP_DISC_ATTR_LEN(p_extra_sattr->attr_len_type) == 2)) { + p_uuid->len = 2; + p_uuid->uu.uuid16 = p_extra_sattr->attr_value.v.u16; + return (TRUE); + } + } + } + } + } + break; + } else if (p_attr->attr_id == ATTR_ID_SERVICE_ID) { + if ((SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == UUID_DESC_TYPE) + /* only support 16 bits UUID for now */ + && (SDP_DISC_ATTR_LEN(p_attr->attr_len_type) == 2)) { + p_uuid->len = 2; + p_uuid->uu.uuid16 = p_attr->attr_value.v.u16; + return (TRUE); + } + } + p_attr = p_attr->p_next_attr; + } + return FALSE; +#else + return FALSE; +#endif +} + +/******************************************************************************* +** +** Function SDP_FindServiceUUIDInRec_128bit +** +** Description This function is called to read the 128-bit service UUID within a record +** if there is any. +** +** Parameters: p_rec - pointer to a SDP record. +** p_uuid - output parameter to save the UUID found. +** +** Returns TRUE if found, otherwise FALSE. +** +*******************************************************************************/ +BOOLEAN SDP_FindServiceUUIDInRec_128bit(tSDP_DISC_REC *p_rec, tBT_UUID *p_uuid) +{ +#if SDP_CLIENT_ENABLED == TRUE + tSDP_DISC_ATTR *p_attr, *p_sattr; + + p_attr = p_rec->p_first_attr; + + while (p_attr) { + if ((p_attr->attr_id == ATTR_ID_SERVICE_CLASS_ID_LIST) + && (SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == DATA_ELE_SEQ_DESC_TYPE)) { + for (p_sattr = p_attr->attr_value.v.p_sub_attr; p_sattr; p_sattr = p_sattr->p_next_attr) { + if (SDP_DISC_ATTR_TYPE(p_sattr->attr_len_type) == UUID_DESC_TYPE) { + /* only support 128 bits UUID for now */ + if (SDP_DISC_ATTR_LEN(p_sattr->attr_len_type) == 16) { + p_uuid->len = LEN_UUID_128; + for (uint8_t i = 0; i != LEN_UUID_128; ++i) { + p_uuid->uu.uuid128[i] = p_sattr->attr_value.v.array[LEN_UUID_128 - i - 1]; + } + } + return (TRUE); + } + } + break; + } else if (p_attr->attr_id == ATTR_ID_SERVICE_ID) { + if ((SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == UUID_DESC_TYPE) + /* only support 128 bits UUID for now */ + && (SDP_DISC_ATTR_LEN(p_attr->attr_len_type) == 16)) { + p_uuid->len = LEN_UUID_128; + for (uint8_t i = 0; i != LEN_UUID_128; ++i) { + p_uuid->uu.uuid128[i] = p_attr->attr_value.v.array[LEN_UUID_128 - i - 1]; + } + return (TRUE); + } + } + p_attr = p_attr->p_next_attr; + } + return FALSE; +#else + return FALSE; +#endif +} + +/******************************************************************************* +** +** Function SDP_FindServiceInDb +** +** Description This function queries an SDP database for a specific service. +** If the p_start_rec pointer is NULL, it looks from the beginning +** of the database, else it continues from the next record after +** p_start_rec. +** +** Returns Pointer to record containing service class, or NULL +** +*******************************************************************************/ +tSDP_DISC_REC *SDP_FindServiceInDb (tSDP_DISCOVERY_DB *p_db, UINT16 service_uuid, tSDP_DISC_REC *p_start_rec) +{ +#if SDP_CLIENT_ENABLED == TRUE + tSDP_DISC_REC *p_rec; + tSDP_DISC_ATTR *p_attr, *p_sattr, *p_extra_sattr; + + /* Must have a valid database */ + if (p_db == NULL) { + return (NULL); + } + + if (!p_start_rec) { + p_rec = p_db->p_first_rec; + } else { + p_rec = p_start_rec->p_next_rec; + } + + while (p_rec) { + p_attr = p_rec->p_first_attr; + while (p_attr) { + if ((p_attr->attr_id == ATTR_ID_SERVICE_CLASS_ID_LIST) + && (SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == DATA_ELE_SEQ_DESC_TYPE)) { + for (p_sattr = p_attr->attr_value.v.p_sub_attr; p_sattr; p_sattr = p_sattr->p_next_attr) { + + if ((SDP_DISC_ATTR_TYPE(p_sattr->attr_len_type) == UUID_DESC_TYPE) + && (SDP_DISC_ATTR_LEN(p_sattr->attr_len_type) == 2) ) { + SDP_TRACE_DEBUG("SDP_FindServiceInDb - p_sattr value = 0x%x serviceuuid = 0x%x\r\n", + p_sattr->attr_value.v.u16, service_uuid); + if (service_uuid == UUID_SERVCLASS_HDP_PROFILE) { + if ( (p_sattr->attr_value.v.u16 == UUID_SERVCLASS_HDP_SOURCE) || ( p_sattr->attr_value.v.u16 == UUID_SERVCLASS_HDP_SINK)) { + SDP_TRACE_DEBUG("SDP_FindServiceInDb found HDP source or sink\n" ); + return (p_rec); + } + } + + } + + if (SDP_DISC_ATTR_TYPE(p_sattr->attr_len_type) == UUID_DESC_TYPE && (service_uuid == 0 + || (SDP_DISC_ATTR_LEN(p_sattr->attr_len_type) == 2 + && p_sattr->attr_value.v.u16 == service_uuid))) + /* for a specific uuid, or any one */ + { + return (p_rec); + } + + /* Checking for Toyota G Block Car Kit: + ** This car kit puts an extra data element sequence + ** where the UUID is suppose to be!!! + */ + else { + if (SDP_DISC_ATTR_TYPE(p_sattr->attr_len_type) == DATA_ELE_SEQ_DESC_TYPE) { + /* Look through data element sequence until no more UUIDs */ + for (p_extra_sattr = p_sattr->attr_value.v.p_sub_attr; p_extra_sattr; p_extra_sattr = p_extra_sattr->p_next_attr) { + /* Increment past this to see if the next attribut is UUID */ + if ((SDP_DISC_ATTR_TYPE(p_extra_sattr->attr_len_type) == UUID_DESC_TYPE) + && (SDP_DISC_ATTR_LEN(p_extra_sattr->attr_len_type) == 2) + /* for a specific uuid, or any one */ + && ((p_extra_sattr->attr_value.v.u16 == service_uuid) || (service_uuid == 0))) { + return (p_rec); + } + } + } + } + } + break; + } else if (p_attr->attr_id == ATTR_ID_SERVICE_ID) { + if ((SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == UUID_DESC_TYPE) + && (SDP_DISC_ATTR_LEN(p_attr->attr_len_type) == 2) + /* find a specific UUID or anyone */ + && ((p_attr->attr_value.v.u16 == service_uuid) || service_uuid == 0)) { + return (p_rec); + } + } + + p_attr = p_attr->p_next_attr; + } + + p_rec = p_rec->p_next_rec; + } +#endif + /* If here, no matching UUID found */ + return (NULL); +} + +/******************************************************************************* +** +** Function SDP_FindServiceInDb_128bit +** +** Description This function queries an SDP database for a specific service. +** If the p_start_rec pointer is NULL, it looks from the beginning +** of the database, else it continues from the next record after +** p_start_rec. +** +** This function is kept separate from SDP_FindServiceInDb since +** that API is expected to return only 16-bit UUIDs +** +** Returns Pointer to record containing service class, or NULL +** +*******************************************************************************/ +tSDP_DISC_REC *SDP_FindServiceInDb_128bit(tSDP_DISCOVERY_DB *p_db, tSDP_DISC_REC *p_start_rec) +{ +#if SDP_CLIENT_ENABLED == TRUE + tSDP_DISC_REC *p_rec; + tSDP_DISC_ATTR *p_attr, *p_sattr; + + /* Must have a valid database */ + if (p_db == NULL) { + return (NULL); + } + + if (!p_start_rec) { + p_rec = p_db->p_first_rec; + } else { + p_rec = p_start_rec->p_next_rec; + } + + while (p_rec) { + p_attr = p_rec->p_first_attr; + while (p_attr) { + if ((p_attr->attr_id == ATTR_ID_SERVICE_CLASS_ID_LIST) + && (SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == DATA_ELE_SEQ_DESC_TYPE)) { + for (p_sattr = p_attr->attr_value.v.p_sub_attr; p_sattr; p_sattr = p_sattr->p_next_attr) { + if ((SDP_DISC_ATTR_TYPE(p_sattr->attr_len_type) == UUID_DESC_TYPE) + && (SDP_DISC_ATTR_LEN(p_sattr->attr_len_type) == 16)) { + return (p_rec); + } + } + break; + } else if (p_attr->attr_id == ATTR_ID_SERVICE_ID) { + if ((SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == UUID_DESC_TYPE) + && (SDP_DISC_ATTR_LEN(p_attr->attr_len_type) == 16)) { + return (p_rec); + } + } + + p_attr = p_attr->p_next_attr; + } + + p_rec = p_rec->p_next_rec; + } +#endif + /* If here, no matching UUID found */ + return (NULL); +} + + +/******************************************************************************* +** +** Function SDP_FindServiceUUIDInDb +** +** Description This function queries an SDP database for a specific service. +** If the p_start_rec pointer is NULL, it looks from the beginning +** of the database, else it continues from the next record after +** p_start_rec. +** +** NOTE the only difference between this function and the previous function +** "SDP_FindServiceInDb()" is that this function takes a tBT_UUID input +** +** Returns Pointer to record containing service class, or NULL +** +*******************************************************************************/ +tSDP_DISC_REC *SDP_FindServiceUUIDInDb (tSDP_DISCOVERY_DB *p_db, tBT_UUID *p_uuid, tSDP_DISC_REC *p_start_rec) +{ +#if SDP_CLIENT_ENABLED == TRUE + tSDP_DISC_REC *p_rec; + tSDP_DISC_ATTR *p_attr, *p_sattr; + + /* Must have a valid database */ + if (p_db == NULL) { + return (NULL); + } + + if (!p_start_rec) { + p_rec = p_db->p_first_rec; + } else { + p_rec = p_start_rec->p_next_rec; + } + + while (p_rec) { + p_attr = p_rec->p_first_attr; + while (p_attr) { + if ((p_attr->attr_id == ATTR_ID_SERVICE_CLASS_ID_LIST) + && (SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == DATA_ELE_SEQ_DESC_TYPE)) { + for (p_sattr = p_attr->attr_value.v.p_sub_attr; p_sattr; p_sattr = p_sattr->p_next_attr) { + if (SDP_DISC_ATTR_TYPE(p_sattr->attr_len_type) == UUID_DESC_TYPE) { + if (sdpu_compare_uuid_with_attr (p_uuid, p_sattr)) { + return (p_rec); + } + } + } + break; + } else if (p_attr->attr_id == ATTR_ID_SERVICE_ID) { + if (SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == UUID_DESC_TYPE ) { + if (sdpu_compare_uuid_with_attr (p_uuid, p_attr)) { + return (p_rec); + } + } + } + + p_attr = p_attr->p_next_attr; + } + + p_rec = p_rec->p_next_rec; + } +#endif /* CLIENT_ENABLED == TRUE */ + /* If here, no matching UUID found */ + return (NULL); +} + +#if SDP_CLIENT_ENABLED == TRUE +/******************************************************************************* +** +** Function sdp_fill_proto_elem +** +** Description This function retrieves the protocol element. +** +** Returns TRUE if found, FALSE if not +** If found, the passed protocol list element is filled in. +** +*******************************************************************************/ +static BOOLEAN sdp_fill_proto_elem( tSDP_DISC_ATTR *p_attr, UINT16 layer_uuid, + tSDP_PROTOCOL_ELEM *p_elem) +{ + tSDP_DISC_ATTR *p_sattr; + + /* Walk through the protocol descriptor list */ + for (p_attr = p_attr->attr_value.v.p_sub_attr; p_attr; p_attr = p_attr->p_next_attr) { + /* Safety check - each entry should itself be a sequence */ + if (SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) != DATA_ELE_SEQ_DESC_TYPE) { + return (FALSE); + } + + /* Now, see if the entry contains the layer we are interested in */ + for (p_sattr = p_attr->attr_value.v.p_sub_attr; p_sattr; p_sattr = p_sattr->p_next_attr) { + /* SDP_TRACE_DEBUG ("SDP - p_sattr 0x%x, layer_uuid:0x%x, u16:0x%x####", + p_sattr, layer_uuid, p_sattr->attr_value.v.u16); */ + + if ((SDP_DISC_ATTR_TYPE(p_sattr->attr_len_type) == UUID_DESC_TYPE) + && (SDP_DISC_ATTR_LEN(p_sattr->attr_len_type) == 2) + && (p_sattr->attr_value.v.u16 == layer_uuid)) { + /* Bingo. Now fill in the passed element */ + p_elem->protocol_uuid = layer_uuid; + p_elem->num_params = 0; + + /* Store the parameters, if any */ + for (p_sattr = p_sattr->p_next_attr; p_sattr; p_sattr = p_sattr->p_next_attr) { + if (SDP_DISC_ATTR_TYPE(p_sattr->attr_len_type) != UINT_DESC_TYPE) { + break; + } + + if (SDP_DISC_ATTR_LEN(p_sattr->attr_len_type) == 2) { + p_elem->params[p_elem->num_params++] = p_sattr->attr_value.v.u16; + } else { + p_elem->params[p_elem->num_params++] = p_sattr->attr_value.v.u8; + } + + if (p_elem->num_params >= SDP_MAX_PROTOCOL_PARAMS) { + break; + } + } + return (TRUE); + } + } + } + + return (FALSE); +} +#endif /* CLIENT_ENABLED == TRUE */ + +/******************************************************************************* +** +** Function SDP_FindProtocolListElemInRec +** +** Description This function looks at a specific discovery record for a protocol +** list element. +** +** Returns TRUE if found, FALSE if not +** If found, the passed protocol list element is filled in. +** +*******************************************************************************/ +BOOLEAN SDP_FindProtocolListElemInRec (tSDP_DISC_REC *p_rec, UINT16 layer_uuid, tSDP_PROTOCOL_ELEM *p_elem) +{ +#if SDP_CLIENT_ENABLED == TRUE + tSDP_DISC_ATTR *p_attr; + + p_attr = p_rec->p_first_attr; + while (p_attr) { + /* Find the protocol descriptor list */ + if ((p_attr->attr_id == ATTR_ID_PROTOCOL_DESC_LIST) + && (SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == DATA_ELE_SEQ_DESC_TYPE)) { + return sdp_fill_proto_elem(p_attr, layer_uuid, p_elem); + } + p_attr = p_attr->p_next_attr; + } +#endif + /* If here, no match found */ + return (FALSE); +} + + +/******************************************************************************* +** +** Function SDP_FindAddProtoListsElemInRec +** +** Description This function looks at a specific discovery record for a protocol +** list element. +** +** Returns TRUE if found, FALSE if not +** If found, the passed protocol list element is filled in. +** +*******************************************************************************/ +BOOLEAN SDP_FindAddProtoListsElemInRec (tSDP_DISC_REC *p_rec, UINT16 layer_uuid, tSDP_PROTOCOL_ELEM *p_elem) +{ +#if SDP_CLIENT_ENABLED == TRUE + tSDP_DISC_ATTR *p_attr, *p_sattr; + BOOLEAN ret = FALSE; + + p_attr = p_rec->p_first_attr; + while (p_attr) { + /* Find the additional protocol descriptor list attribute */ + if ((p_attr->attr_id == ATTR_ID_ADDITION_PROTO_DESC_LISTS) + && (SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == DATA_ELE_SEQ_DESC_TYPE)) { + for (p_sattr = p_attr->attr_value.v.p_sub_attr; p_sattr; p_sattr = p_sattr->p_next_attr) { + /* Safety check - each entry should itself be a sequence */ + if (SDP_DISC_ATTR_TYPE(p_sattr->attr_len_type) == DATA_ELE_SEQ_DESC_TYPE) { + if ( (ret = sdp_fill_proto_elem(p_sattr, layer_uuid, p_elem)) == TRUE) { + break; + } + } + } + return ret; + } + p_attr = p_attr->p_next_attr; + } +#endif + /* If here, no match found */ + return (FALSE); +} + + +/******************************************************************************* +** +** Function SDP_FindProfileVersionInRec +** +** Description This function looks at a specific discovery record for the +** Profile list descriptor, and pulls out the version number. +** The version number consists of an 8-bit major version and +** an 8-bit minor version. +** +** Returns TRUE if found, FALSE if not +** If found, the major and minor version numbers that were passed +** in are filled in. +** +*******************************************************************************/ +BOOLEAN SDP_FindProfileVersionInRec (tSDP_DISC_REC *p_rec, UINT16 profile_uuid, UINT16 *p_version) +{ +#if SDP_CLIENT_ENABLED == TRUE + tSDP_DISC_ATTR *p_attr, *p_sattr; + + p_attr = p_rec->p_first_attr; + while (p_attr) { + /* Find the profile descriptor list */ + if ((p_attr->attr_id == ATTR_ID_BT_PROFILE_DESC_LIST) + && (SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == DATA_ELE_SEQ_DESC_TYPE)) { + /* Walk through the protocol descriptor list */ + for (p_attr = p_attr->attr_value.v.p_sub_attr; p_attr; p_attr = p_attr->p_next_attr) { + /* Safety check - each entry should itself be a sequence */ + if (SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) != DATA_ELE_SEQ_DESC_TYPE) { + return (FALSE); + } + + /* Now, see if the entry contains the profile UUID we are interested in */ + for (p_sattr = p_attr->attr_value.v.p_sub_attr; p_sattr; p_sattr = p_sattr->p_next_attr) { + if ((SDP_DISC_ATTR_TYPE(p_sattr->attr_len_type) == UUID_DESC_TYPE) + && (SDP_DISC_ATTR_LEN(p_sattr->attr_len_type) == 2) /* <- This is bytes, not size code! */ + && (p_sattr->attr_value.v.u16 == profile_uuid)) { + /* Now fill in the major and minor numbers */ + /* if the attribute matches the description for version (type UINT, size 2 bytes) */ + p_sattr = p_sattr->p_next_attr; + + if ((SDP_DISC_ATTR_TYPE(p_sattr->attr_len_type) == UINT_DESC_TYPE) && + (SDP_DISC_ATTR_LEN(p_sattr->attr_len_type) == 2)) { + /* The high order 8 bits is the major number, low order is the minor number (big endian) */ + *p_version = p_sattr->attr_value.v.u16; + + return (TRUE); + } else { + return (FALSE); /* The type and/or size was not valid for the profile list version */ + } + } + } + } + + return (FALSE); + } + p_attr = p_attr->p_next_attr; + } +#endif /* CLIENT_ENABLED == TRUE */ + + /* If here, no match found */ + return (FALSE); +} + +/******************************************************************************* +** Device Identification (DI) Client Functions +*******************************************************************************/ + +/******************************************************************************* +** +** Function SDP_DiDiscover +** +** Description This function queries a remote device for DI information. +** +** Returns SDP_SUCCESS if query started successfully, else error +** +*******************************************************************************/ +UINT16 SDP_DiDiscover( BD_ADDR remote_device, tSDP_DISCOVERY_DB *p_db, + UINT32 len, tSDP_DISC_CMPL_CB *p_cb ) +{ +#if SDP_CLIENT_ENABLED == TRUE + UINT16 result = SDP_DI_DISC_FAILED; + UINT16 num_uuids = 1; + UINT16 di_uuid = UUID_SERVCLASS_PNP_INFORMATION; + + /* build uuid for db init */ + tSDP_UUID init_uuid; + init_uuid.len = 2; + init_uuid.uu.uuid16 = di_uuid; + + if ( SDP_InitDiscoveryDb(p_db, len, num_uuids, &init_uuid, 0, NULL) ) { + if ( SDP_ServiceSearchRequest(remote_device, p_db, p_cb) ) { + result = SDP_SUCCESS; + } + } + return result; +#else + return SDP_DI_DISC_FAILED; +#endif +} + +/******************************************************************************* +** +** Function SDP_GetNumDiRecords +** +** Description Searches specified database for DI records +** +** Returns number of DI records found +** +*******************************************************************************/ +UINT8 SDP_GetNumDiRecords( tSDP_DISCOVERY_DB *p_db ) +{ +#if SDP_CLIENT_ENABLED == TRUE + UINT8 num_records = 0; + tSDP_DISC_REC *p_curr_record = NULL; + + do { + p_curr_record = SDP_FindServiceInDb( p_db, UUID_SERVCLASS_PNP_INFORMATION, + p_curr_record ); + if ( p_curr_record ) { + num_records++; + } + } while ( p_curr_record ); + + return num_records; +#else + return 0; +#endif +} + +#if SDP_CLIENT_ENABLED == TRUE +/******************************************************************************* +** +** Function SDP_AttrStringCopy +** +** Description This function copy given attribute to specified buffer as a string +** +** Returns none +** +*******************************************************************************/ +static void SDP_AttrStringCopy(char *dst, tSDP_DISC_ATTR *p_attr, UINT16 dst_size) +{ + if ( dst == NULL ) { + return; + } + if ( p_attr ) { + UINT16 len = SDP_DISC_ATTR_LEN(p_attr->attr_len_type); + if ( len > dst_size - 1 ) { + len = dst_size - 1; + } + memcpy(dst, (char *)p_attr->attr_value.v.array, len); + dst[len] = '\0'; + } else { + dst[0] = '\0'; + } +} +#endif + +/******************************************************************************* +** +** Function SDP_GetDiRecord +** +** Description This function retrieves a remote device's DI record from +** the specified database. +** +** Returns SDP_SUCCESS if record retrieved, else error +** +*******************************************************************************/ +UINT16 SDP_GetDiRecord( UINT8 get_record_index, tSDP_DI_GET_RECORD *p_device_info, + tSDP_DISCOVERY_DB *p_db ) +{ +#if SDP_CLIENT_ENABLED == TRUE + UINT16 result = SDP_NO_DI_RECORD_FOUND; + UINT8 curr_record_index = 1; + + tSDP_DISC_REC *p_curr_record = NULL; + + /* find the requested SDP record in the discovery database */ + do { + p_curr_record = SDP_FindServiceInDb( p_db, UUID_SERVCLASS_PNP_INFORMATION, + p_curr_record ); + if ( p_curr_record ) { + if ( curr_record_index++ == get_record_index ) { + result = SDP_SUCCESS; + break; + } + } + } while ( p_curr_record ); + + if ( result == SDP_SUCCESS ) { + /* copy the information from the SDP record to the DI record */ + tSDP_DISC_ATTR *p_curr_attr = NULL; + + /* ClientExecutableURL is optional */ + p_curr_attr = SDP_FindAttributeInRec( p_curr_record, ATTR_ID_CLIENT_EXE_URL ); + SDP_AttrStringCopy( p_device_info->rec.client_executable_url, p_curr_attr, + SDP_MAX_ATTR_LEN ); + + /* Service Description is optional */ + p_curr_attr = SDP_FindAttributeInRec( p_curr_record, ATTR_ID_SERVICE_DESCRIPTION ); + SDP_AttrStringCopy( p_device_info->rec.service_description, p_curr_attr, SDP_MAX_ATTR_LEN ); + + /* DocumentationURL is optional */ + p_curr_attr = SDP_FindAttributeInRec( p_curr_record, ATTR_ID_DOCUMENTATION_URL ); + SDP_AttrStringCopy( p_device_info->rec.documentation_url, p_curr_attr, SDP_MAX_ATTR_LEN ); + + p_curr_attr = SDP_FindAttributeInRec( p_curr_record, ATTR_ID_SPECIFICATION_ID ); + if ( p_curr_attr ) { + p_device_info->spec_id = p_curr_attr->attr_value.v.u16; + } else { + result = SDP_ERR_ATTR_NOT_PRESENT; + } + + p_curr_attr = SDP_FindAttributeInRec( p_curr_record, ATTR_ID_VENDOR_ID ); + if ( p_curr_attr ) { + p_device_info->rec.vendor = p_curr_attr->attr_value.v.u16; + } else { + result = SDP_ERR_ATTR_NOT_PRESENT; + } + + p_curr_attr = SDP_FindAttributeInRec( p_curr_record, ATTR_ID_VENDOR_ID_SOURCE ); + if ( p_curr_attr ) { + p_device_info->rec.vendor_id_source = p_curr_attr->attr_value.v.u16; + } else { + result = SDP_ERR_ATTR_NOT_PRESENT; + } + + p_curr_attr = SDP_FindAttributeInRec( p_curr_record, ATTR_ID_PRODUCT_ID ); + if ( p_curr_attr ) { + p_device_info->rec.product = p_curr_attr->attr_value.v.u16; + } else { + result = SDP_ERR_ATTR_NOT_PRESENT; + } + + p_curr_attr = SDP_FindAttributeInRec( p_curr_record, ATTR_ID_PRODUCT_VERSION ); + if ( p_curr_attr ) { + p_device_info->rec.version = p_curr_attr->attr_value.v.u16; + } else { + result = SDP_ERR_ATTR_NOT_PRESENT; + } + + p_curr_attr = SDP_FindAttributeInRec( p_curr_record, ATTR_ID_PRIMARY_RECORD ); + if ( p_curr_attr ) { + p_device_info->rec.primary_record = (BOOLEAN)p_curr_attr->attr_value.v.u8; + } else { + result = SDP_ERR_ATTR_NOT_PRESENT; + } + } + + return result; +#else /* SDP_CLIENT_ENABLED is FALSE */ + return SDP_NO_DI_RECORD_FOUND; +#endif +} + +/******************************************************************************* +** Device Identification (DI) Server Functions +*******************************************************************************/ + +/******************************************************************************* +** +** Function SDP_SetLocalDiRecord +** +** Description This function adds a DI record to the local SDP database. +** +** +** +** Returns Returns SDP_SUCCESS if record added successfully, else error +** +*******************************************************************************/ +UINT16 SDP_SetLocalDiRecord( tSDP_DI_RECORD *p_device_info, UINT32 *p_handle ) +{ +#if SDP_SERVER_ENABLED == TRUE + UINT16 result = SDP_SUCCESS; + UINT32 handle; + UINT16 di_uuid = UUID_SERVCLASS_PNP_INFORMATION; + UINT16 di_specid = BLUETOOTH_DI_SPECIFICATION; + UINT8 temp_u16[2]; + UINT8 *p_temp; + UINT8 u8; + + *p_handle = 0; + if ( p_device_info == NULL ) { + return SDP_ILLEGAL_PARAMETER; + } + + /* if record is to be primary record, get handle to replace old primary */ + if ( p_device_info->primary_record == TRUE && sdp_cb.server_db.di_primary_handle ) { + handle = sdp_cb.server_db.di_primary_handle; + } else { + if ( (handle = SDP_CreateRecord()) == 0 ) { + return SDP_NO_RESOURCES; + } + } + + *p_handle = handle; + + /* build the SDP entry */ + /* Add the UUID to the Service Class ID List */ + if ((SDP_AddServiceClassIdList(handle, 1, &di_uuid)) == FALSE) { + result = SDP_DI_REG_FAILED; + } + + /* mandatory */ + if ( result == SDP_SUCCESS) { + p_temp = temp_u16; + UINT16_TO_BE_STREAM(p_temp, di_specid); + if ( !(SDP_AddAttribute(handle, ATTR_ID_SPECIFICATION_ID, + UINT_DESC_TYPE, sizeof(di_specid), + temp_u16)) ) { + result = SDP_DI_REG_FAILED; + } + } + + /* optional - if string is null, do not add attribute */ + if ( result == SDP_SUCCESS ) { + if ( p_device_info->client_executable_url[0] != '\0' ) { + if ( !((strlen(p_device_info->client_executable_url) + 1 <= SDP_MAX_ATTR_LEN) && + SDP_AddAttribute(handle, ATTR_ID_CLIENT_EXE_URL, URL_DESC_TYPE, + (UINT32)(strlen(p_device_info->client_executable_url) + 1), + (UINT8 *)p_device_info->client_executable_url)) ) { + result = SDP_DI_REG_FAILED; + } + } + } + + /* optional - if string is null, do not add attribute */ + if ( result == SDP_SUCCESS ) { + if ( p_device_info->service_description[0] != '\0' ) { + if ( !((strlen(p_device_info->service_description) + 1 <= SDP_MAX_ATTR_LEN) && + SDP_AddAttribute(handle, ATTR_ID_SERVICE_DESCRIPTION, + TEXT_STR_DESC_TYPE, + (UINT32)(strlen(p_device_info->service_description) + 1), + (UINT8 *)p_device_info->service_description)) ) { + result = SDP_DI_REG_FAILED; + } + } + } + + /* optional - if string is null, do not add attribute */ + if ( result == SDP_SUCCESS ) { + if ( p_device_info->documentation_url[0] != '\0' ) { + if ( !((strlen(p_device_info->documentation_url) + 1 <= SDP_MAX_ATTR_LEN) && + SDP_AddAttribute(handle, ATTR_ID_DOCUMENTATION_URL, URL_DESC_TYPE, + (UINT32)(strlen(p_device_info->documentation_url) + 1), + (UINT8 *)p_device_info->documentation_url)) ) { + result = SDP_DI_REG_FAILED; + } + } + } + + /* mandatory */ + if ( result == SDP_SUCCESS) { + p_temp = temp_u16; + UINT16_TO_BE_STREAM(p_temp, p_device_info->vendor); + if ( !(SDP_AddAttribute(handle, ATTR_ID_VENDOR_ID, UINT_DESC_TYPE, + sizeof(p_device_info->vendor), temp_u16)) ) { + result = SDP_DI_REG_FAILED; + } + } + + /* mandatory */ + if ( result == SDP_SUCCESS) { + p_temp = temp_u16; + UINT16_TO_BE_STREAM (p_temp, p_device_info->product); + if ( !(SDP_AddAttribute(handle, ATTR_ID_PRODUCT_ID, + UINT_DESC_TYPE, sizeof(p_device_info->product), temp_u16)) ) { + result = SDP_DI_REG_FAILED; + } + } + + /* mandatory */ + if ( result == SDP_SUCCESS) { + p_temp = temp_u16; + UINT16_TO_BE_STREAM (p_temp, p_device_info->version); + if ( !(SDP_AddAttribute(handle, ATTR_ID_PRODUCT_VERSION, UINT_DESC_TYPE, + sizeof(p_device_info->version), temp_u16)) ) { + result = SDP_DI_REG_FAILED; + } + } + + /* mandatory */ + if ( result == SDP_SUCCESS) { + u8 = (UINT8)p_device_info->primary_record; + if ( !(SDP_AddAttribute(handle, ATTR_ID_PRIMARY_RECORD, + BOOLEAN_DESC_TYPE, 1, &u8)) ) { + result = SDP_DI_REG_FAILED; + } + } + + /* mandatory */ + if ( result == SDP_SUCCESS) { + p_temp = temp_u16; + UINT16_TO_BE_STREAM(p_temp, p_device_info->vendor_id_source); + if ( !(SDP_AddAttribute(handle, ATTR_ID_VENDOR_ID_SOURCE, UINT_DESC_TYPE, + sizeof(p_device_info->vendor_id_source), temp_u16)) ) { + result = SDP_DI_REG_FAILED; + } + } + + if ( result != SDP_SUCCESS ) { + SDP_DeleteRecord( handle ); + } else if (p_device_info->primary_record == TRUE) { + sdp_cb.server_db.di_primary_handle = handle; + } + + return result; +#else /* SDP_SERVER_ENABLED is FALSE */ + return SDP_DI_REG_FAILED; +#endif /* if SDP_SERVER_ENABLED */ +} + +/******************************************************************************* +** +** Function SDP_SetTraceLevel +** +** Description This function sets the trace level for SDP. If called with +** a value of 0xFF, it simply reads the current trace level. +** +** Returns the new (current) trace level +** +*******************************************************************************/ +UINT8 SDP_SetTraceLevel (UINT8 new_level) +{ + if (new_level != 0xFF) { + sdp_cb.trace_level = new_level; + } + + return (sdp_cb.trace_level); +} + +#endif ///SDP_INCLUDED == TRUE diff --git a/lib/bt/host/bluedroid/stack/sdp/sdp_db.c b/lib/bt/host/bluedroid/stack/sdp/sdp_db.c new file mode 100644 index 00000000..a9b3daa4 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/sdp/sdp_db.c @@ -0,0 +1,992 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * this file contains functions that handle the database + * + ******************************************************************************/ + +#include +#include +#include + +#include "common/bt_target.h" +#include "osi/allocator.h" + + +#include "stack/l2cdefs.h" +#include "stack/hcidefs.h" +#include "stack/hcimsgs.h" + +#include "stack/sdp_api.h" +#include "sdpint.h" + +#include "osi/list.h" + +#if (SDP_INCLUDED == TRUE) + +#if SDP_SERVER_ENABLED == TRUE +/********************************************************************************/ +/* L O C A L F U N C T I O N P R O T O T Y P E S */ +/********************************************************************************/ +static BOOLEAN find_uuid_in_seq (UINT8 *p , UINT32 seq_len, UINT8 *p_his_uuid, + UINT16 his_len, int nest_level); + + +/******************************************************************************* +** +** Function sdp_db_service_search +** +** Description This function searches for a record that contains the +** specified UIDs. It is passed either NULL to start at the +** beginning, or the previous record found. +** +** Returns Pointer to the record, or NULL if not found. +** +*******************************************************************************/ +tSDP_RECORD *sdp_db_service_search (tSDP_RECORD *p_rec, tSDP_UUID_SEQ *p_seq) +{ + UINT16 xx, yy; + tSDP_ATTRIBUTE *p_attr; + list_node_t *p_node = NULL; + + /* If NULL, start at the beginning, else start at the first specified record */ + if (!p_rec) { + p_node = list_begin(sdp_cb.server_db.p_record_list); + } else { + /* get node in the record list with given p_rec */ + p_node = list_get_node(sdp_cb.server_db.p_record_list, p_rec); + if (p_node == NULL) { + return NULL; + } + /* get next node */ + p_node = list_next(p_node); + } + + /* Look through the records. The spec says that a match occurs if */ + /* the record contains all the passed UUIDs in it. */ + for( ; p_node; p_node = list_next(p_node)) { + p_rec = list_node(p_node); + for (yy = 0; yy < p_seq->num_uids; yy++) { + p_attr = &p_rec->attribute[0]; + for (xx = 0; xx < p_rec->num_attributes; xx++, p_attr++) { + if (p_attr->type == UUID_DESC_TYPE) { + if (sdpu_compare_uuid_arrays (p_attr->value_ptr, p_attr->len, + &p_seq->uuid_entry[yy].value[0], + p_seq->uuid_entry[yy].len)) { + break; + } + } else if (p_attr->type == DATA_ELE_SEQ_DESC_TYPE) { + if (find_uuid_in_seq (p_attr->value_ptr, p_attr->len, + &p_seq->uuid_entry[yy].value[0], + p_seq->uuid_entry[yy].len, 0)) { + break; + } + } + } + /* If any UUID was not found, on to the next record */ + if (xx == p_rec->num_attributes) { + break; + } + } + + /* If every UUID was found in the record, return the record */ + if (yy == p_seq->num_uids) { + return (p_rec); + } + } + + /* If here, no more records found */ + return (NULL); +} + +/******************************************************************************* +** +** Function find_uuid_in_seq +** +** Description This function searches a data element sequenct for a UUID. +** +** Returns TRUE if found, else FALSE +** +*******************************************************************************/ +static BOOLEAN find_uuid_in_seq (UINT8 *p , UINT32 seq_len, UINT8 *p_uuid, + UINT16 uuid_len, int nest_level) +{ + UINT8 *p_end = p + seq_len; + UINT8 type; + UINT32 len; + + /* A little safety check to avoid excessive recursion */ + if (nest_level > 3) { + return (FALSE); + } + + while (p < p_end) { + type = *p++; + p = sdpu_get_len_from_type (p, type, &len); + type = type >> 3; + if (type == UUID_DESC_TYPE) { + if (sdpu_compare_uuid_arrays (p, len, p_uuid, uuid_len)) { + return (TRUE); + } + } else if (type == DATA_ELE_SEQ_DESC_TYPE) { + if (find_uuid_in_seq (p, len, p_uuid, uuid_len, nest_level + 1)) { + return (TRUE); + } + } + p = p + len; + } + + /* If here, failed to match */ + return (FALSE); +} + +/******************************************************************************* +** +** Function sdp_db_find_record +** +** Description This function searches for a record with a specific handle +** It is passed the handle of the record. +** +** Returns Pointer to the record, or NULL if not found. +** +*******************************************************************************/ +tSDP_RECORD *sdp_db_find_record (UINT32 handle) +{ + tSDP_RECORD *p_rec; + list_node_t *p_node = NULL; + + /* Look through the records for the caller's handle */ + for(p_node = list_begin(sdp_cb.server_db.p_record_list); p_node; p_node = list_next(p_node)) { + p_rec = list_node(p_node); + if (p_rec->record_handle == handle) { + return (p_rec); + } + } + + /* Record with that handle not found. */ + return (NULL); +} + +/******************************************************************************* +** +** Function sdp_db_find_attr_in_rec +** +** Description This function searches a record for specific attributes. +** It is passed a pointer to the record. If the record contains +** the specified attribute, (the caller may specify be a range +** of attributes), the attribute is returned. +** +** Returns Pointer to the attribute, or NULL if not found. +** +*******************************************************************************/ +tSDP_ATTRIBUTE *sdp_db_find_attr_in_rec (tSDP_RECORD *p_rec, UINT16 start_attr, + UINT16 end_attr) +{ + tSDP_ATTRIBUTE *p_at; + UINT16 xx; + + /* Note that the attributes in a record are assumed to be in sorted order */ + for (xx = 0, p_at = &p_rec->attribute[0]; xx < p_rec->num_attributes; + xx++, p_at++) { + if ((p_at->id >= start_attr) && (p_at->id <= end_attr)) { + return (p_at); + } + } + + /* No matching attribute found */ + return (NULL); +} + + +/******************************************************************************* +** +** Function sdp_compose_proto_list +** +** Description This function is called to compose a data sequence from +** protocol element list struct pointer +** +** Returns the length of the data sequence +** +*******************************************************************************/ +static int sdp_compose_proto_list( UINT8 *p, UINT16 num_elem, + tSDP_PROTOCOL_ELEM *p_elem_list) +{ + UINT16 xx, yy, len; + BOOLEAN is_rfcomm_scn; + UINT8 *p_head = p; + UINT8 *p_len; + + /* First, build the protocol list. This consists of a set of data element + ** sequences, one for each layer. Each layer sequence consists of layer's + ** UUID and optional parameters + */ + for (xx = 0; xx < num_elem; xx++, p_elem_list++) { + len = 3 + (p_elem_list->num_params * 3); + UINT8_TO_BE_STREAM (p, (DATA_ELE_SEQ_DESC_TYPE << 3) | SIZE_IN_NEXT_BYTE); + + p_len = p; + *p++ = (UINT8) len; + + UINT8_TO_BE_STREAM (p, (UUID_DESC_TYPE << 3) | SIZE_TWO_BYTES); + UINT16_TO_BE_STREAM (p, p_elem_list->protocol_uuid); + + if (p_elem_list->protocol_uuid == UUID_PROTOCOL_RFCOMM) { + is_rfcomm_scn = TRUE; + } else { + is_rfcomm_scn = FALSE; + } + + for (yy = 0; yy < p_elem_list->num_params; yy++) { + if (is_rfcomm_scn) { + UINT8_TO_BE_STREAM (p, (UINT_DESC_TYPE << 3) | SIZE_ONE_BYTE); + UINT8_TO_BE_STREAM (p, p_elem_list->params[yy]); + + *p_len -= 1; + } else { + UINT8_TO_BE_STREAM (p, (UINT_DESC_TYPE << 3) | SIZE_TWO_BYTES); + UINT16_TO_BE_STREAM (p, p_elem_list->params[yy]); + } + } + } + return (p - p_head); +} + +#endif /* SDP_SERVER_ENABLED == TRUE */ + +/******************************************************************************* +** +** Function SDP_CreateRecord +** +** Description This function is called to create a record in the database. +** This would be through the SDP database maintenance API. The +** record is created empty, teh application should then call +** "add_attribute" to add the record's attributes. +** +** Returns Record handle if OK, else 0. +** +*******************************************************************************/ +UINT32 SDP_CreateRecord (void) +{ +#if SDP_SERVER_ENABLED == TRUE + UINT32 handle; + UINT8 buf[4]; + tSDP_DB *p_db = &sdp_cb.server_db; + tSDP_RECORD *p_rec = NULL; + tSDP_RECORD *p_rec_prev = NULL; + + /* First, check if there is a free record */ + if (p_db->num_records < SDP_MAX_RECORDS) { + p_rec =(tSDP_RECORD *)osi_malloc(sizeof(tSDP_RECORD)); + if (p_rec) { + memset(p_rec, 0, sizeof(tSDP_RECORD)); + /* Save previous rec */ + if (p_db->num_records) { + p_rec_prev = list_back(p_db->p_record_list); + } + /* Append new record */ + list_append(p_db->p_record_list, p_rec); + + /* We will use a handle of the first unreserved handle plus last record + ** number + 1 */ + if (p_db->num_records) { + handle = p_rec_prev->record_handle + 1; + } else { + handle = 0x10000; + } + + p_rec->record_handle = handle; + + p_db->num_records++; + SDP_TRACE_DEBUG("SDP_CreateRecord ok, num_records:%d\n", p_db->num_records); + /* Add the first attribute (the handle) automatically */ + UINT32_TO_BE_FIELD (buf, handle); + SDP_AddAttribute (handle, ATTR_ID_SERVICE_RECORD_HDL, UINT_DESC_TYPE, + 4, buf); + + return (p_rec->record_handle); + } else { + SDP_TRACE_ERROR("SDP_CreateRecord fail, memory allocation failed\n"); + } + } else { + SDP_TRACE_ERROR("SDP_CreateRecord fail, exceed maximum records:%d\n", SDP_MAX_RECORDS); + } +#endif + return (0); +} + + +/******************************************************************************* +** +** Function SDP_DeleteRecord +** +** Description This function is called to add a record (or all records) +** from the database. This would be through the SDP database +** maintenance API. +** +** If a record handle of 0 is passed, all records are deleted. +** +** Returns TRUE if succeeded, else FALSE +** +*******************************************************************************/ +BOOLEAN SDP_DeleteRecord (UINT32 handle) +{ +#if SDP_SERVER_ENABLED == TRUE + tSDP_RECORD *p_rec = NULL; + list_node_t *p_node = NULL; + + if (handle == 0 || sdp_cb.server_db.num_records == 0) { + /* Delete all records in the database */ + sdp_cb.server_db.num_records = 0; + for(p_node = list_begin(sdp_cb.server_db.p_record_list); p_node; p_node = list_next(p_node)) { + list_remove(sdp_cb.server_db.p_record_list, p_node); + } + /* require new DI record to be created in SDP_SetLocalDiRecord */ + sdp_cb.server_db.di_primary_handle = 0; + + return (TRUE); + } else { + /* Find the record in the database */ + for(p_node = list_begin(sdp_cb.server_db.p_record_list); p_node; p_node = list_next(p_node)) { + p_rec = list_node(p_node); + if (p_rec->record_handle == handle) { + /* Found it. Shift everything up one */ + list_remove(sdp_cb.server_db.p_record_list, p_rec); + + sdp_cb.server_db.num_records--; + + SDP_TRACE_DEBUG("SDP_DeleteRecord ok, num_records:%d\n", sdp_cb.server_db.num_records); + /* if we're deleting the primary DI record, clear the */ + /* value in the control block */ + if ( sdp_cb.server_db.di_primary_handle == handle ) { + sdp_cb.server_db.di_primary_handle = 0; + } + + return (TRUE); + } + } + } +#endif + return (FALSE); +} + + +/******************************************************************************* +** +** Function SDP_AddAttribute +** +** Description This function is called to add an attribute to a record. +** This would be through the SDP database maintenance API. +** If the attribute already exists in the record, it is replaced +** with the new value. +** +** NOTE Attribute values must be passed as a Big Endian stream. +** +** Returns TRUE if added OK, else FALSE +** +*******************************************************************************/ +BOOLEAN SDP_AddAttribute (UINT32 handle, UINT16 attr_id, UINT8 attr_type, + UINT32 attr_len, UINT8 *p_val) +{ +#if SDP_SERVER_ENABLED == TRUE + UINT16 xx, yy; + tSDP_RECORD *p_rec = NULL; + list_node_t *p_node= NULL; + +#if (BT_TRACE_VERBOSE == TRUE) + if (sdp_cb.trace_level >= BT_TRACE_LEVEL_DEBUG) { + if ((attr_type == UINT_DESC_TYPE) || + (attr_type == TWO_COMP_INT_DESC_TYPE) || + (attr_type == UUID_DESC_TYPE) || + (attr_type == DATA_ELE_SEQ_DESC_TYPE) || + (attr_type == DATA_ELE_ALT_DESC_TYPE)) { + UINT8 num_array[400]; + UINT32 i; + UINT32 len = (attr_len > 200) ? 200 : attr_len; + + num_array[0] = '\0'; + for (i = 0; i < len; i++) { + sprintf((char *)&num_array[i * 2], "%02X", (UINT8)(p_val[i])); + } + SDP_TRACE_DEBUG("SDP_AddAttribute: handle:%X, id:%04X, type:%d, len:%d, p_val:%p, *p_val:%s\n", + handle, attr_id, attr_type, attr_len, p_val, num_array); + } else if (attr_type == BOOLEAN_DESC_TYPE) { + SDP_TRACE_DEBUG("SDP_AddAttribute: handle:%X, id:%04X, type:%d, len:%d, p_val:%p, *p_val:%d\n", + handle, attr_id, attr_type, attr_len, p_val, *p_val); + } else { + SDP_TRACE_DEBUG("SDP_AddAttribute: handle:%X, id:%04X, type:%d, len:%d, p_val:%p, *p_val:%s\n", + handle, attr_id, attr_type, attr_len, p_val, p_val); + } + } +#endif + + /* Find the record in the database */ + for(p_node = list_begin(sdp_cb.server_db.p_record_list); p_node; p_node = list_next(p_node)) { + p_rec= list_node(p_node); + if (p_rec->record_handle == handle) { + tSDP_ATTRIBUTE *p_attr = &p_rec->attribute[0]; + + /* Found the record. Now, see if the attribute already exists */ + for (xx = 0; xx < p_rec->num_attributes; xx++, p_attr++) { + /* The attribute exists. replace it */ + if (p_attr->id == attr_id) { + SDP_DeleteAttribute (handle, attr_id); + break; + } + if (p_attr->id > attr_id) { + break; + } + } + + if (p_rec->num_attributes == SDP_MAX_REC_ATTR) { + return (FALSE); + } + + /* If not found, see if we can allocate a new entry */ + if (xx == p_rec->num_attributes) { + p_attr = &p_rec->attribute[p_rec->num_attributes]; + } else { + /* Since the attributes are kept in sorted order, insert ours here */ + for (yy = p_rec->num_attributes; yy > xx; yy--) { + p_rec->attribute[yy] = p_rec->attribute[yy - 1]; + } + } + + p_attr->id = attr_id; + p_attr->type = attr_type; + p_attr->len = attr_len; + + if (p_rec->free_pad_ptr + attr_len >= SDP_MAX_PAD_LEN) { + /* do truncate only for text string type descriptor */ + if (attr_type == TEXT_STR_DESC_TYPE) { + SDP_TRACE_WARNING("SDP_AddAttribute: attr_len:%d too long. truncate to (%d)\n", + attr_len, SDP_MAX_PAD_LEN - p_rec->free_pad_ptr ); + + attr_len = SDP_MAX_PAD_LEN - p_rec->free_pad_ptr; + p_val[SDP_MAX_PAD_LEN - p_rec->free_pad_ptr] = '\0'; + p_val[SDP_MAX_PAD_LEN - p_rec->free_pad_ptr + 1] = '\0'; + } else { + attr_len = 0; + } + } + + if ((attr_len > 0) && (p_val != 0)) { + p_attr->len = attr_len; + memcpy (&p_rec->attr_pad[p_rec->free_pad_ptr], p_val, (size_t)attr_len); + p_attr->value_ptr = &p_rec->attr_pad[p_rec->free_pad_ptr]; + p_rec->free_pad_ptr += attr_len; + } else if ((attr_len == 0 && p_attr->len != 0) || /* if truncate to 0 length, simply don't add */ + p_val == 0) { + SDP_TRACE_ERROR("SDP_AddAttribute fail, length exceed maximum: ID %d: attr_len:%d \n", + attr_id, attr_len ); + p_attr->id = p_attr->type = p_attr->len = 0; + return (FALSE); + } + p_rec->num_attributes++; + return (TRUE); + } + } +#endif + return (FALSE); +} + + +/******************************************************************************* +** +** Function SDP_AddSequence +** +** Description This function is called to add a sequence to a record. +** This would be through the SDP database maintenance API. +** If the sequence already exists in the record, it is replaced +** with the new sequence. +** +** NOTE Element values must be passed as a Big Endian stream. +** +** Returns TRUE if added OK, else FALSE +** +*******************************************************************************/ +BOOLEAN SDP_AddSequence (UINT32 handle, UINT16 attr_id, UINT16 num_elem, + UINT8 type[], UINT8 len[], UINT8 *p_val[]) +{ +#if SDP_SERVER_ENABLED == TRUE + UINT16 xx; + UINT8 *p_buff; + UINT8 *p; + UINT8 *p_head; + BOOLEAN result; + + if ((p_buff = (UINT8 *) osi_malloc(sizeof(UINT8) * SDP_MAX_ATTR_LEN * 2)) == NULL) { + SDP_TRACE_ERROR("SDP_AddSequence cannot get a buffer!\n"); + return (FALSE); + } + p = p_buff; + + /* First, build the sequence */ + for (xx = 0; xx < num_elem; xx++) { + p_head = p; + switch (len[xx]) { + case 1: + UINT8_TO_BE_STREAM (p, (type[xx] << 3) | SIZE_ONE_BYTE); + break; + case 2: + UINT8_TO_BE_STREAM (p, (type[xx] << 3) | SIZE_TWO_BYTES); + break; + case 4: + UINT8_TO_BE_STREAM (p, (type[xx] << 3) | SIZE_FOUR_BYTES); + break; + case 8: + UINT8_TO_BE_STREAM (p, (type[xx] << 3) | SIZE_EIGHT_BYTES); + break; + case 16: + UINT8_TO_BE_STREAM (p, (type[xx] << 3) | SIZE_SIXTEEN_BYTES); + break; + default: + UINT8_TO_BE_STREAM (p, (type[xx] << 3) | SIZE_IN_NEXT_BYTE); + UINT8_TO_BE_STREAM (p, len[xx]); + break; + } + + ARRAY_TO_BE_STREAM (p, p_val[xx], len[xx]); + + if (p - p_buff > SDP_MAX_ATTR_LEN) { + /* go back to before we add this element */ + p = p_head; + if (p_head == p_buff) { + /* the first element exceed the max length */ + SDP_TRACE_ERROR ("SDP_AddSequence - too long(attribute is not added)!!\n"); + osi_free(p_buff); + return FALSE; + } else { + SDP_TRACE_ERROR ("SDP_AddSequence - too long, add %d elements of %d\n", xx, num_elem); + } + break; + } + } + result = SDP_AddAttribute (handle, attr_id, DATA_ELE_SEQ_DESC_TYPE, (UINT32) (p - p_buff), p_buff); + osi_free(p_buff); + return result; +#else /* SDP_SERVER_ENABLED == FALSE */ + return (FALSE); +#endif +} + + +/******************************************************************************* +** +** Function SDP_AddUuidSequence +** +** Description This function is called to add a UUID sequence to a record. +** This would be through the SDP database maintenance API. +** If the sequence already exists in the record, it is replaced +** with the new sequence. +** +** Returns TRUE if added OK, else FALSE +** +*******************************************************************************/ +BOOLEAN SDP_AddUuidSequence (UINT32 handle, UINT16 attr_id, UINT16 num_uuids, + UINT16 *p_uuids) +{ +#if SDP_SERVER_ENABLED == TRUE + UINT16 xx; + UINT8 *p_buff; + UINT8 *p; + INT32 max_len = SDP_MAX_ATTR_LEN - 3; + BOOLEAN result; + + if ((p_buff = (UINT8 *) osi_malloc(sizeof(UINT8) * SDP_MAX_ATTR_LEN * 2)) == NULL) { + SDP_TRACE_ERROR("SDP_AddUuidSequence cannot get a buffer!\n"); + return (FALSE); + } + p = p_buff; + + /* First, build the sequence */ + for (xx = 0; xx < num_uuids ; xx++, p_uuids++) { + UINT8_TO_BE_STREAM (p, (UUID_DESC_TYPE << 3) | SIZE_TWO_BYTES); + UINT16_TO_BE_STREAM (p, *p_uuids); + + if ((p - p_buff) > max_len) { + SDP_TRACE_WARNING ("SDP_AddUuidSequence - too long, add %d uuids of %d\n", xx, num_uuids); + break; + } + } + + result = SDP_AddAttribute (handle, attr_id, DATA_ELE_SEQ_DESC_TYPE, (UINT32) (p - p_buff), p_buff); + osi_free(p_buff); + return result; +#else /* SDP_SERVER_ENABLED == FALSE */ + return (FALSE); +#endif +} + +/******************************************************************************* +** +** Function SDP_AddProtocolList +** +** Description This function is called to add a protocol descriptor list to +** a record. This would be through the SDP database maintenance API. +** If the protocol list already exists in the record, it is replaced +** with the new list. +** +** Returns TRUE if added OK, else FALSE +** +*******************************************************************************/ +BOOLEAN SDP_AddProtocolList (UINT32 handle, UINT16 num_elem, + tSDP_PROTOCOL_ELEM *p_elem_list) +{ +#if SDP_SERVER_ENABLED == TRUE + UINT8 *p_buff; + int offset; + BOOLEAN result; + + if ((p_buff = (UINT8 *) osi_malloc(sizeof(UINT8) * SDP_MAX_ATTR_LEN * 2)) == NULL) { + SDP_TRACE_ERROR("SDP_AddProtocolList cannot get a buffer!\n"); + return (FALSE); + } + + offset = sdp_compose_proto_list(p_buff, num_elem, p_elem_list); + result = SDP_AddAttribute (handle, ATTR_ID_PROTOCOL_DESC_LIST, DATA_ELE_SEQ_DESC_TYPE, (UINT32) offset, p_buff); + osi_free(p_buff); + return result; +#else /* SDP_SERVER_ENABLED == FALSE */ + return (FALSE); +#endif +} + + +/******************************************************************************* +** +** Function SDP_AddAdditionProtoLists +** +** Description This function is called to add a protocol descriptor list to +** a record. This would be through the SDP database maintenance API. +** If the protocol list already exists in the record, it is replaced +** with the new list. +** +** Returns TRUE if added OK, else FALSE +** +*******************************************************************************/ +BOOLEAN SDP_AddAdditionProtoLists (UINT32 handle, UINT16 num_elem, + tSDP_PROTO_LIST_ELEM *p_proto_list) +{ +#if SDP_SERVER_ENABLED == TRUE + UINT16 xx; + UINT8 *p_buff; + UINT8 *p; + UINT8 *p_len; + int offset; + BOOLEAN result; + + if ((p_buff = (UINT8 *) osi_malloc(sizeof(UINT8) * SDP_MAX_ATTR_LEN * 2)) == NULL) { + SDP_TRACE_ERROR("SDP_AddAdditionProtoLists cannot get a buffer!\n"); + return (FALSE); + } + p = p_buff; + + /* for each ProtocolDescriptorList */ + for (xx = 0; xx < num_elem; xx++, p_proto_list++) { + UINT8_TO_BE_STREAM (p, (DATA_ELE_SEQ_DESC_TYPE << 3) | SIZE_IN_NEXT_BYTE); + p_len = p++; + + offset = sdp_compose_proto_list(p, p_proto_list->num_elems, + p_proto_list->list_elem); + p += offset; + + *p_len = (UINT8)(p - p_len - 1); + } + result = SDP_AddAttribute (handle, ATTR_ID_ADDITION_PROTO_DESC_LISTS, DATA_ELE_SEQ_DESC_TYPE, + (UINT32) (p - p_buff), p_buff); + osi_free(p_buff); + return result; + +#else /* SDP_SERVER_ENABLED == FALSE */ + return (FALSE); +#endif +} + +/******************************************************************************* +** +** Function SDP_AddProfileDescriptorList +** +** Description This function is called to add a profile descriptor list to +** a record. This would be through the SDP database maintenance API. +** If the version already exists in the record, it is replaced +** with the new one. +** +** Returns TRUE if added OK, else FALSE +** +*******************************************************************************/ +BOOLEAN SDP_AddProfileDescriptorList (UINT32 handle, UINT16 profile_uuid, + UINT16 version) +{ +#if SDP_SERVER_ENABLED == TRUE + UINT8 *p_buff; + UINT8 *p; + BOOLEAN result; + + if ((p_buff = (UINT8 *) osi_malloc(sizeof(UINT8) * SDP_MAX_ATTR_LEN)) == NULL) { + SDP_TRACE_ERROR("SDP_AddProfileDescriptorList cannot get a buffer!\n"); + return (FALSE); + } + p = p_buff + 2; + + /* First, build the profile descriptor list. This consists of a data element sequence. */ + /* The sequence consists of profile's UUID and version number */ + UINT8_TO_BE_STREAM (p, (UUID_DESC_TYPE << 3) | SIZE_TWO_BYTES); + UINT16_TO_BE_STREAM (p, profile_uuid); + + UINT8_TO_BE_STREAM (p, (UINT_DESC_TYPE << 3) | SIZE_TWO_BYTES); + UINT16_TO_BE_STREAM (p, version); + + /* Add in type and length fields */ + *p_buff = (UINT8) ((DATA_ELE_SEQ_DESC_TYPE << 3) | SIZE_IN_NEXT_BYTE); + *(p_buff + 1) = (UINT8) (p - (p_buff + 2)); + + result = SDP_AddAttribute (handle, ATTR_ID_BT_PROFILE_DESC_LIST, DATA_ELE_SEQ_DESC_TYPE, (UINT32) (p - p_buff), p_buff); + osi_free(p_buff); + return result; + +#else /* SDP_SERVER_ENABLED == FALSE */ + return (FALSE); +#endif +} + + +/******************************************************************************* +** +** Function SDP_AddLanguageBaseAttrIDList +** +** Description This function is called to add a language base attr list to +** a record. This would be through the SDP database maintenance API. +** If the version already exists in the record, it is replaced +** with the new one. +** +** Returns TRUE if added OK, else FALSE +** +*******************************************************************************/ +BOOLEAN SDP_AddLanguageBaseAttrIDList (UINT32 handle, UINT16 lang, + UINT16 char_enc, UINT16 base_id) +{ +#if SDP_SERVER_ENABLED == TRUE + UINT8 *p_buff; + UINT8 *p; + BOOLEAN result; + + if ((p_buff = (UINT8 *) osi_malloc(sizeof(UINT8) * SDP_MAX_ATTR_LEN)) == NULL) { + SDP_TRACE_ERROR("SDP_AddLanguageBaseAttrIDList cannot get a buffer!\n"); + return (FALSE); + } + p = p_buff; + + /* First, build the language base descriptor list. This consists of a data */ + /* element sequence. The sequence consists of 9 bytes (3 UINt16 fields) */ + UINT8_TO_BE_STREAM (p, (UINT_DESC_TYPE << 3) | SIZE_TWO_BYTES); + UINT16_TO_BE_STREAM (p, lang); + + UINT8_TO_BE_STREAM (p, (UINT_DESC_TYPE << 3) | SIZE_TWO_BYTES); + UINT16_TO_BE_STREAM (p, char_enc); + + UINT8_TO_BE_STREAM (p, (UINT_DESC_TYPE << 3) | SIZE_TWO_BYTES); + UINT16_TO_BE_STREAM (p, base_id); + + result = SDP_AddAttribute (handle, ATTR_ID_LANGUAGE_BASE_ATTR_ID_LIST, DATA_ELE_SEQ_DESC_TYPE, + (UINT32) (p - p_buff), p_buff); + osi_free(p_buff); + return result; +#else /* SDP_SERVER_ENABLED == FALSE */ + return (FALSE); +#endif +} + + +/******************************************************************************* +** +** Function SDP_AddServiceClassIdList +** +** Description This function is called to add a service list to a record. +** This would be through the SDP database maintenance API. +** If the service list already exists in the record, it is replaced +** with the new list. +** +** Returns TRUE if added OK, else FALSE +** +*******************************************************************************/ +BOOLEAN SDP_AddServiceClassIdList (UINT32 handle, UINT16 num_services, + UINT16 *p_service_uuids) +{ +#if SDP_SERVER_ENABLED == TRUE + UINT16 xx; + UINT8 *p_buff; + UINT8 *p; + BOOLEAN result; + + if ((p_buff = (UINT8 *) osi_malloc(sizeof(UINT8) * SDP_MAX_ATTR_LEN * 2)) == NULL) { + SDP_TRACE_ERROR("SDP_AddServiceClassIdList cannot get a buffer!\n"); + return (FALSE); + } + p = p_buff; + + for (xx = 0; xx < num_services; xx++, p_service_uuids++) { + UINT8_TO_BE_STREAM (p, (UUID_DESC_TYPE << 3) | SIZE_TWO_BYTES); + UINT16_TO_BE_STREAM (p, *p_service_uuids); + } + + result = SDP_AddAttribute (handle, ATTR_ID_SERVICE_CLASS_ID_LIST, DATA_ELE_SEQ_DESC_TYPE, + (UINT32) (p - p_buff), p_buff); + osi_free(p_buff); + return result; +#else /* SDP_SERVER_ENABLED == FALSE */ + return (FALSE); +#endif +} + + +/******************************************************************************* +** +** Function SDP_DeleteAttribute +** +** Description This function is called to delete an attribute from a record. +** This would be through the SDP database maintenance API. +** +** Returns TRUE if deleted OK, else FALSE if not found +** +*******************************************************************************/ +BOOLEAN SDP_DeleteAttribute (UINT32 handle, UINT16 attr_id) +{ +#if SDP_SERVER_ENABLED == TRUE + UINT16 xx, yy; + tSDP_RECORD *p_rec = NULL; + list_node_t *p_node= NULL; + UINT8 *pad_ptr; + UINT32 len; /* Number of bytes in the entry */ + + /* Find the record in the database */ + for(p_node = list_begin(sdp_cb.server_db.p_record_list); p_node; p_node = list_next(p_node)) { + p_rec= list_node(p_node); + if (p_rec->record_handle == handle) { + tSDP_ATTRIBUTE *p_attr = &p_rec->attribute[0]; + + SDP_TRACE_API("Deleting attr_id 0x%04x for handle 0x%x\n", attr_id, handle); + /* Found it. Now, find the attribute */ + for (xx = 0; xx < p_rec->num_attributes; xx++, p_attr++) { + if (p_attr->id == attr_id) { + pad_ptr = p_attr->value_ptr; + len = p_attr->len; + + if (len) { + for (yy = 0; yy < p_rec->num_attributes; yy++) { + if ( p_rec->attribute[yy].value_ptr > pad_ptr ) { + p_rec->attribute[yy].value_ptr -= len; + } + } + } + + /* Found it. Shift everything up one */ + p_rec->num_attributes--; + + for (yy = xx; yy < p_rec->num_attributes; yy++, p_attr++) { + *p_attr = *(p_attr + 1); + } + + /* adjust attribute values if needed */ + if (len) { + xx = (p_rec->free_pad_ptr - ((pad_ptr + len) - + &p_rec->attr_pad[0])); + for ( yy = 0; yy < xx; yy++, pad_ptr++) { + *pad_ptr = *(pad_ptr + len); + } + p_rec->free_pad_ptr -= len; + } + return (TRUE); + } + } + } + } +#endif + /* If here, not found */ + return (FALSE); +} + +/******************************************************************************* +** +** Function SDP_ReadRecord +** +** Description This function is called to get the raw data of the record +** with the given handle from the database. +** +** Returns -1, if the record is not found. +** Otherwise, the offset (0 or 1) to start of data in p_data. +** +** The size of data copied into p_data is in *p_data_len. +** +*******************************************************************************/ +#if (SDP_RAW_DATA_INCLUDED == TRUE) +INT32 SDP_ReadRecord(UINT32 handle, UINT8 *p_data, INT32 *p_data_len) +{ + INT32 offset = -1; /* default to not found */ +#if SDP_SERVER_ENABLED == TRUE + INT32 len = 0; /* Number of bytes in the entry */ + tSDP_RECORD *p_rec; + UINT16 start = 0; + UINT16 end = 0xffff; + tSDP_ATTRIBUTE *p_attr; + UINT16 rem_len; + UINT8 *p_rsp; + + /* Find the record in the database */ + p_rec = sdp_db_find_record(handle); + if (p_rec && p_data && p_data_len) { + p_rsp = &p_data[3]; + while ( (p_attr = sdp_db_find_attr_in_rec (p_rec, start, end)) != NULL) { + /* Check if attribute fits. Assume 3-byte value type/length */ + rem_len = *p_data_len - (UINT16) (p_rsp - p_data); + + if (p_attr->len > (UINT32)(rem_len - 6)) { + break; + } + + p_rsp = sdpu_build_attrib_entry (p_rsp, p_attr); + + /* next attr id */ + start = p_attr->id + 1; + } + len = (INT32) (p_rsp - p_data); + + /* Put in the sequence header (2 or 3 bytes) */ + if (len > 255) { + offset = 0; + p_data[0] = (UINT8) ((DATA_ELE_SEQ_DESC_TYPE << 3) | SIZE_IN_NEXT_WORD); + p_data[1] = (UINT8) ((len - 3) >> 8); + p_data[2] = (UINT8) (len - 3); + } else { + offset = 1; + + p_data[1] = (UINT8) ((DATA_ELE_SEQ_DESC_TYPE << 3) | SIZE_IN_NEXT_BYTE); + p_data[2] = (UINT8) (len - 3); + + len--; + } + *p_data_len = len; + } +#endif + /* If here, not found */ + return (offset); +} +#endif + +#endif ///SDP_INCLUDED == TRUE diff --git a/lib/bt/host/bluedroid/stack/sdp/sdp_discovery.c b/lib/bt/host/bluedroid/stack/sdp/sdp_discovery.c new file mode 100644 index 00000000..d63b164d --- /dev/null +++ b/lib/bt/host/bluedroid/stack/sdp/sdp_discovery.c @@ -0,0 +1,1026 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * this file contains SDP discovery functions + * + ******************************************************************************/ + +#include +#include +#include + +#include "common/bt_target.h" +#include "osi/allocator.h" +#include "stack/l2cdefs.h" +#include "stack/hcidefs.h" +#include "stack/hcimsgs.h" +#include "stack/sdp_api.h" +#include "sdpint.h" +#include "stack/btu.h" +#include "stack/btm_api.h" + + +#ifndef SDP_DEBUG_RAW +#define SDP_DEBUG_RAW FALSE +#endif + +/********************************************************************************/ +/* L O C A L F U N C T I O N P R O T O T Y P E S */ +/********************************************************************************/ +#if SDP_CLIENT_ENABLED == TRUE +static void process_service_search_rsp (tCONN_CB *p_ccb, UINT8 *p_reply); +static void process_service_attr_rsp (tCONN_CB *p_ccb, UINT8 *p_reply); +static void process_service_search_attr_rsp (tCONN_CB *p_ccb, UINT8 *p_reply); +static UINT8 *save_attr_seq (tCONN_CB *p_ccb, UINT8 *p, UINT8 *p_msg_end); +static tSDP_DISC_REC *add_record (tSDP_DISCOVERY_DB *p_db, BD_ADDR p_bda); +static UINT8 *add_attr (UINT8 *p, tSDP_DISCOVERY_DB *p_db, tSDP_DISC_REC *p_rec, + UINT16 attr_id, tSDP_DISC_ATTR *p_parent_attr, UINT8 nest_level); + +/* Safety check in case we go crazy */ +#define MAX_NEST_LEVELS 5 + + +/******************************************************************************* +** +** Function sdpu_build_uuid_seq +** +** Description This function builds a UUID sequence from the list of +** passed UUIDs. It is also passed the address of the output +** buffer. +** +** Returns Pointer to next byte in the output buffer. +** +*******************************************************************************/ +static UINT8 *sdpu_build_uuid_seq (UINT8 *p_out, UINT16 num_uuids, tSDP_UUID *p_uuid_list) +{ + UINT16 xx; + UINT8 *p_len; + + /* First thing is the data element header */ + UINT8_TO_BE_STREAM (p_out, (DATA_ELE_SEQ_DESC_TYPE << 3) | SIZE_IN_NEXT_BYTE); + + /* Remember where the length goes. Leave space for it. */ + p_len = p_out; + p_out += 1; + + /* Now, loop through and put in all the UUID(s) */ + for (xx = 0; xx < num_uuids; xx++, p_uuid_list++) { + if (p_uuid_list->len == 2) { + UINT8_TO_BE_STREAM (p_out, (UUID_DESC_TYPE << 3) | SIZE_TWO_BYTES); + UINT16_TO_BE_STREAM (p_out, p_uuid_list->uu.uuid16); + } else if (p_uuid_list->len == 4) { + UINT8_TO_BE_STREAM (p_out, (UUID_DESC_TYPE << 3) | SIZE_FOUR_BYTES); + UINT32_TO_BE_STREAM (p_out, p_uuid_list->uu.uuid32); + } else { + UINT8_TO_BE_STREAM (p_out, (UUID_DESC_TYPE << 3) | SIZE_SIXTEEN_BYTES); + ARRAY_TO_BE_STREAM (p_out, p_uuid_list->uu.uuid128, p_uuid_list->len); + } + } + + /* Now, put in the length */ + xx = (UINT16)(p_out - p_len - 1); + UINT8_TO_BE_STREAM (p_len, xx); + + return (p_out); +} + +/******************************************************************************* +** +** Function sdp_snd_service_search_req +** +** Description Send a service search request to the SDP server. +** +** Returns void +** +*******************************************************************************/ +static void sdp_snd_service_search_req(tCONN_CB *p_ccb, UINT8 cont_len, UINT8 *p_cont) +{ + UINT8 *p, *p_start, *p_param_len; + BT_HDR *p_cmd; + UINT16 param_len; + + /* Get a buffer to send the packet to L2CAP */ + if ((p_cmd = (BT_HDR *) osi_malloc(SDP_DATA_BUF_SIZE)) == NULL) { + sdp_disconnect (p_ccb, SDP_NO_RESOURCES); + return; + } + + p_cmd->offset = L2CAP_MIN_OFFSET; + p = p_start = (UINT8 *)(p_cmd + 1) + L2CAP_MIN_OFFSET; + + /* Build a service search request packet */ + UINT8_TO_BE_STREAM (p, SDP_PDU_SERVICE_SEARCH_REQ); + UINT16_TO_BE_STREAM (p, p_ccb->transaction_id); + p_ccb->transaction_id++; + + /* Skip the length, we need to add it at the end */ + p_param_len = p; + p += 2; + + /* Build the UID sequence. */ +#if (defined(SDP_BROWSE_PLUS) && SDP_BROWSE_PLUS == TRUE) + p = sdpu_build_uuid_seq (p, 1, &p_ccb->p_db->uuid_filters[p_ccb->cur_uuid_idx]); +#else + p = sdpu_build_uuid_seq (p, p_ccb->p_db->num_uuid_filters, p_ccb->p_db->uuid_filters); +#endif + + /* Set max service record count */ + UINT16_TO_BE_STREAM (p, sdp_cb.max_recs_per_search); + + /* Set continuation state */ + UINT8_TO_BE_STREAM (p, cont_len); + + /* if this is not the first request */ + if (cont_len && p_cont) { + memcpy(p, p_cont, cont_len); + p += cont_len; + } + + /* Go back and put the parameter length into the buffer */ + param_len = (UINT16)(p - p_param_len - 2); + UINT16_TO_BE_STREAM (p_param_len, param_len); + + p_ccb->disc_state = SDP_DISC_WAIT_HANDLES; + + /* Set the length of the SDP data in the buffer */ + p_cmd->len = (UINT16)(p - p_start); + +#if (SDP_DEBUG_RAW == TRUE) + SDP_TRACE_WARNING("sdp_snd_service_search_req cont_len :%d disc_state:%d\n", cont_len, p_ccb->disc_state); +#endif + + + L2CA_DataWrite (p_ccb->connection_id, p_cmd); + + /* Start inactivity timer */ + btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_SDP, SDP_INACT_TIMEOUT); + +} + +/******************************************************************************* +** +** Function sdp_disc_connected +** +** Description This function is called when an SDP discovery attempt is +** connected. +** +** Returns void +** +*******************************************************************************/ +void sdp_disc_connected (tCONN_CB *p_ccb) +{ + if (p_ccb->is_attr_search) { + p_ccb->disc_state = SDP_DISC_WAIT_SEARCH_ATTR; + + process_service_search_attr_rsp (p_ccb, NULL); + } else { + /* First step is to get a list of the handles from the server. */ + /* We are not searching for a specific attribute, so we will */ + /* first search for the service, then get all attributes of it */ + + p_ccb->num_handles = 0; + sdp_snd_service_search_req(p_ccb, 0, NULL); + } + +} + +/******************************************************************************* +** +** Function sdp_disc_server_rsp +** +** Description This function is called when there is a response from +** the server. +** +** Returns void +** +*******************************************************************************/ +void sdp_disc_server_rsp (tCONN_CB *p_ccb, BT_HDR *p_msg) +{ + UINT8 *p, rsp_pdu; + BOOLEAN invalid_pdu = TRUE; + +#if (SDP_DEBUG_RAW == TRUE) + SDP_TRACE_WARNING("sdp_disc_server_rsp disc_state:%d\n", p_ccb->disc_state); +#endif + + /* stop inactivity timer when we receive a response */ + btu_stop_timer (&p_ccb->timer_entry); + + /* Got a reply!! Check what we got back */ + p = (UINT8 *)(p_msg + 1) + p_msg->offset; + + BE_STREAM_TO_UINT8 (rsp_pdu, p); + + p_msg->len--; + + switch (rsp_pdu) { + case SDP_PDU_SERVICE_SEARCH_RSP: + if (p_ccb->disc_state == SDP_DISC_WAIT_HANDLES) { + process_service_search_rsp (p_ccb, p); + invalid_pdu = FALSE; + } + break; + + case SDP_PDU_SERVICE_ATTR_RSP: + if (p_ccb->disc_state == SDP_DISC_WAIT_ATTR) { + process_service_attr_rsp (p_ccb, p); + invalid_pdu = FALSE; + } + break; + + case SDP_PDU_SERVICE_SEARCH_ATTR_RSP: + if (p_ccb->disc_state == SDP_DISC_WAIT_SEARCH_ATTR) { + process_service_search_attr_rsp (p_ccb, p); + invalid_pdu = FALSE; + } + break; + } + + if (invalid_pdu) { + SDP_TRACE_WARNING ("SDP - Unexp. PDU: %d in state: %d\n", rsp_pdu, p_ccb->disc_state); + sdp_disconnect (p_ccb, SDP_GENERIC_ERROR); + } +} + +/****************************************************************************** +** +** Function process_service_search_rsp +** +** Description This function is called when there is a search response from +** the server. +** +** Returns void +** +*******************************************************************************/ +static void process_service_search_rsp (tCONN_CB *p_ccb, UINT8 *p_reply) +{ + UINT16 xx; + UINT16 total, cur_handles, orig; + UINT8 cont_len; + + /* Skip transaction, and param len */ + p_reply += 4; + BE_STREAM_TO_UINT16 (total, p_reply); + BE_STREAM_TO_UINT16 (cur_handles, p_reply); + + orig = p_ccb->num_handles; + p_ccb->num_handles += cur_handles; + if (p_ccb->num_handles == 0) { + SDP_TRACE_WARNING ("SDP - Rcvd ServiceSearchRsp, no matches\n"); + sdp_disconnect (p_ccb, SDP_NO_RECS_MATCH); + return; + } + + /* Save the handles that match. We will can only process a certain number. */ + if (total > sdp_cb.max_recs_per_search) { + total = sdp_cb.max_recs_per_search; + } + if (p_ccb->num_handles > sdp_cb.max_recs_per_search) { + p_ccb->num_handles = sdp_cb.max_recs_per_search; + } + + for (xx = orig; xx < p_ccb->num_handles; xx++) { + BE_STREAM_TO_UINT32 (p_ccb->handles[xx], p_reply); + } + + BE_STREAM_TO_UINT8 (cont_len, p_reply); + if (cont_len != 0) { + if (cont_len > SDP_MAX_CONTINUATION_LEN) { + sdp_disconnect (p_ccb, SDP_INVALID_CONT_STATE); + return; + } + /* stay in the same state */ + sdp_snd_service_search_req(p_ccb, cont_len, p_reply); + } else { + /* change state */ + p_ccb->disc_state = SDP_DISC_WAIT_ATTR; + + /* Kick off the first attribute request */ + process_service_attr_rsp (p_ccb, NULL); + } +} + +/******************************************************************************* +** +** Function sdp_copy_raw_data +** +** Description copy the raw data +** +** +** Returns void +** +*******************************************************************************/ +#if (SDP_RAW_DATA_INCLUDED == TRUE) +static void sdp_copy_raw_data (tCONN_CB *p_ccb, BOOLEAN offset) +{ + unsigned int cpy_len; + UINT32 list_len; + UINT8 *p; + UINT8 type; + +#if (SDP_DEBUG_RAW == TRUE) + UINT8 num_array[SDP_MAX_LIST_BYTE_COUNT]; + UINT32 i; + + for (i = 0; i < p_ccb->list_len; i++) { + sprintf((char *)&num_array[i * 2], "%02X\n", (UINT8)(p_ccb->rsp_list[i])); + } + SDP_TRACE_WARNING("result :%s\n", num_array); +#endif + + if (p_ccb->p_db->raw_data) { + cpy_len = p_ccb->p_db->raw_size - p_ccb->p_db->raw_used; + list_len = p_ccb->list_len; + p = &p_ccb->rsp_list[0]; + + if (offset) { + type = *p++; + p = sdpu_get_len_from_type (p, type, &list_len); + } + if (list_len < cpy_len ) { + cpy_len = list_len; + } +#if (SDP_DEBUG_RAW == TRUE) + SDP_TRACE_DEBUG("list_len :%d cpy_len:%d raw_size:%d raw_used:%d\n", + list_len, cpy_len, p_ccb->p_db->raw_size, p_ccb->p_db->raw_used); +#endif + if (cpy_len != 0){ + memcpy (&p_ccb->p_db->raw_data[p_ccb->p_db->raw_used], p, cpy_len); + p_ccb->p_db->raw_used += cpy_len; + } + } +} +#endif + +/******************************************************************************* +** +** Function process_service_attr_rsp +** +** Description This function is called when there is a attribute response from +** the server. +** +** Returns void +** +*******************************************************************************/ +static void process_service_attr_rsp (tCONN_CB *p_ccb, UINT8 *p_reply) +{ + UINT8 *p_start, *p_param_len; + UINT16 param_len, list_byte_count; + BOOLEAN cont_request_needed = FALSE; + +#if (SDP_DEBUG_RAW == TRUE) + SDP_TRACE_WARNING("process_service_attr_rsp raw inc:%d\n", + SDP_RAW_DATA_INCLUDED); +#endif + /* If p_reply is NULL, we were called after the records handles were read */ + if (p_reply) { +#if (SDP_DEBUG_RAW == TRUE) + SDP_TRACE_WARNING("ID & len: 0x%02x-%02x-%02x-%02x\n", + p_reply[0], p_reply[1], p_reply[2], p_reply[3]); +#endif + /* Skip transaction ID and length */ + p_reply += 4; + + BE_STREAM_TO_UINT16 (list_byte_count, p_reply); +#if (SDP_DEBUG_RAW == TRUE) + SDP_TRACE_WARNING("list_byte_count:%d\n", list_byte_count); +#endif + + /* Copy the response to the scratchpad. First, a safety check on the length */ + if ((p_ccb->list_len + list_byte_count) > SDP_MAX_LIST_BYTE_COUNT) { + sdp_disconnect (p_ccb, SDP_INVALID_PDU_SIZE); + return; + } + +#if (SDP_DEBUG_RAW == TRUE) + SDP_TRACE_WARNING("list_len: %d, list_byte_count: %d\n", + p_ccb->list_len, list_byte_count); +#endif + if (p_ccb->rsp_list == NULL) { + p_ccb->rsp_list = (UINT8 *)osi_malloc (SDP_MAX_LIST_BYTE_COUNT); + if (p_ccb->rsp_list == NULL) { + SDP_TRACE_ERROR ("SDP - no gki buf to save rsp\n"); + sdp_disconnect (p_ccb, SDP_NO_RESOURCES); + return; + } + } + memcpy (&p_ccb->rsp_list[p_ccb->list_len], p_reply, list_byte_count); + p_ccb->list_len += list_byte_count; + p_reply += list_byte_count; +#if (SDP_DEBUG_RAW == TRUE) + SDP_TRACE_WARNING("list_len: %d(attr_rsp)\n", p_ccb->list_len); + + /* Check if we need to request a continuation */ + SDP_TRACE_WARNING("*p_reply:%d(%d)\n", *p_reply, SDP_MAX_CONTINUATION_LEN); +#endif + if (*p_reply) { + if (*p_reply > SDP_MAX_CONTINUATION_LEN) { + sdp_disconnect (p_ccb, SDP_INVALID_CONT_STATE); + return; + } + cont_request_needed = TRUE; + } else { + +#if (SDP_RAW_DATA_INCLUDED == TRUE) + SDP_TRACE_WARNING("process_service_attr_rsp\n"); + sdp_copy_raw_data (p_ccb, FALSE); +#endif + + /* Save the response in the database. Stop on any error */ + if (!save_attr_seq (p_ccb, &p_ccb->rsp_list[0], &p_ccb->rsp_list[p_ccb->list_len])) { + sdp_disconnect (p_ccb, SDP_DB_FULL); + return; + } + p_ccb->list_len = 0; + p_ccb->cur_handle++; + } + } + + /* Now, ask for the next handle. Re-use the buffer we just got. */ + if (p_ccb->cur_handle < p_ccb->num_handles) { + BT_HDR *p_msg = (BT_HDR *) osi_malloc(SDP_DATA_BUF_SIZE); + UINT8 *p; + + if (!p_msg) { + sdp_disconnect (p_ccb, SDP_NO_RESOURCES); + return; + } + + p_msg->offset = L2CAP_MIN_OFFSET; + p = p_start = (UINT8 *)(p_msg + 1) + L2CAP_MIN_OFFSET; + + /* Get all the attributes from the server */ + UINT8_TO_BE_STREAM (p, SDP_PDU_SERVICE_ATTR_REQ); + UINT16_TO_BE_STREAM (p, p_ccb->transaction_id); + p_ccb->transaction_id++; + + /* Skip the length, we need to add it at the end */ + p_param_len = p; + p += 2; + + UINT32_TO_BE_STREAM (p, p_ccb->handles[p_ccb->cur_handle]); + + /* Max attribute byte count */ + UINT16_TO_BE_STREAM (p, sdp_cb.max_attr_list_size); + + /* If no attribute filters, build a wildcard attribute sequence */ + if (p_ccb->p_db->num_attr_filters) { + p = sdpu_build_attrib_seq (p, p_ccb->p_db->attr_filters, p_ccb->p_db->num_attr_filters); + } else { + p = sdpu_build_attrib_seq (p, NULL, 0); + } + + /* Was this a continuation request ? */ + if (cont_request_needed) { + memcpy (p, p_reply, *p_reply + 1); + p += *p_reply + 1; + } else { + UINT8_TO_BE_STREAM (p, 0); + } + + /* Go back and put the parameter length into the buffer */ + param_len = (UINT16)(p - p_param_len - 2); + UINT16_TO_BE_STREAM (p_param_len, param_len); + + /* Set the length of the SDP data in the buffer */ + p_msg->len = (UINT16)(p - p_start); + + + L2CA_DataWrite (p_ccb->connection_id, p_msg); + + /* Start inactivity timer */ + btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_SDP, SDP_INACT_TIMEOUT); + } else { + sdp_disconnect (p_ccb, SDP_SUCCESS); + return; + } +} + + +/******************************************************************************* +** +** Function process_service_search_attr_rsp +** +** Description This function is called when there is a search attribute +** response from the server. +** +** Returns void +** +*******************************************************************************/ +static void process_service_search_attr_rsp (tCONN_CB *p_ccb, UINT8 *p_reply) +{ + UINT8 *p, *p_start, *p_end, *p_param_len; + UINT8 type; + UINT32 seq_len; + UINT16 param_len, lists_byte_count = 0; + BOOLEAN cont_request_needed = FALSE; + +#if (SDP_DEBUG_RAW == TRUE) + SDP_TRACE_WARNING("process_service_search_attr_rsp\n"); +#endif + /* If p_reply is NULL, we were called for the initial read */ + if (p_reply) { +#if (SDP_DEBUG_RAW == TRUE) + SDP_TRACE_WARNING("ID & len: 0x%02x-%02x-%02x-%02x\n", + p_reply[0], p_reply[1], p_reply[2], p_reply[3]); +#endif + /* Skip transaction ID and length */ + p_reply += 4; + + BE_STREAM_TO_UINT16 (lists_byte_count, p_reply); +#if (SDP_DEBUG_RAW == TRUE) + SDP_TRACE_WARNING("lists_byte_count:%d\n", lists_byte_count); +#endif + + /* Copy the response to the scratchpad. First, a safety check on the length */ + if ((p_ccb->list_len + lists_byte_count) > SDP_MAX_LIST_BYTE_COUNT) { + sdp_disconnect (p_ccb, SDP_INVALID_PDU_SIZE); + return; + } + +#if (SDP_DEBUG_RAW == TRUE) + SDP_TRACE_WARNING("list_len: %d, list_byte_count: %d\n", + p_ccb->list_len, lists_byte_count); +#endif + if (p_ccb->rsp_list == NULL) { + p_ccb->rsp_list = (UINT8 *)osi_malloc (SDP_MAX_LIST_BYTE_COUNT); + if (p_ccb->rsp_list == NULL) { + SDP_TRACE_ERROR ("SDP - no gki buf to save rsp\n"); + sdp_disconnect (p_ccb, SDP_NO_RESOURCES); + return; + } + } + memcpy (&p_ccb->rsp_list[p_ccb->list_len], p_reply, lists_byte_count); + p_ccb->list_len += lists_byte_count; + p_reply += lists_byte_count; +#if (SDP_DEBUG_RAW == TRUE) + SDP_TRACE_WARNING("list_len: %d(search_attr_rsp)\n", p_ccb->list_len); + + /* Check if we need to request a continuation */ + SDP_TRACE_WARNING("*p_reply:%d(%d)\n", *p_reply, SDP_MAX_CONTINUATION_LEN); +#endif + if (*p_reply) { + if (*p_reply > SDP_MAX_CONTINUATION_LEN) { + sdp_disconnect (p_ccb, SDP_INVALID_CONT_STATE); + return; + } + + cont_request_needed = TRUE; + } + } + +#if (SDP_DEBUG_RAW == TRUE) + SDP_TRACE_WARNING("cont_request_needed:%d\n", cont_request_needed); +#endif + /* If continuation request (or first time request) */ + if ((cont_request_needed) || (!p_reply)) { + BT_HDR *p_msg = (BT_HDR *) osi_malloc(SDP_DATA_BUF_SIZE); + UINT8 *p; + + if (!p_msg) { + sdp_disconnect (p_ccb, SDP_NO_RESOURCES); + return; + } + + p_msg->offset = L2CAP_MIN_OFFSET; + p = p_start = (UINT8 *)(p_msg + 1) + L2CAP_MIN_OFFSET; + + /* Build a service search request packet */ + UINT8_TO_BE_STREAM (p, SDP_PDU_SERVICE_SEARCH_ATTR_REQ); + UINT16_TO_BE_STREAM (p, p_ccb->transaction_id); + p_ccb->transaction_id++; + + /* Skip the length, we need to add it at the end */ + p_param_len = p; + p += 2; + + /* Build the UID sequence. */ +#if (defined(SDP_BROWSE_PLUS) && SDP_BROWSE_PLUS == TRUE) + p = sdpu_build_uuid_seq (p, 1, &p_ccb->p_db->uuid_filters[p_ccb->cur_uuid_idx]); +#else + p = sdpu_build_uuid_seq (p, p_ccb->p_db->num_uuid_filters, p_ccb->p_db->uuid_filters); +#endif + + /* Max attribute byte count */ + UINT16_TO_BE_STREAM (p, sdp_cb.max_attr_list_size); + + /* If no attribute filters, build a wildcard attribute sequence */ + if (p_ccb->p_db->num_attr_filters) { + p = sdpu_build_attrib_seq (p, p_ccb->p_db->attr_filters, p_ccb->p_db->num_attr_filters); + } else { + p = sdpu_build_attrib_seq (p, NULL, 0); + } + + /* No continuation for first request */ + if (p_reply) { + memcpy (p, p_reply, *p_reply + 1); + p += *p_reply + 1; + } else { + UINT8_TO_BE_STREAM (p, 0); + } + + /* Go back and put the parameter length into the buffer */ + param_len = p - p_param_len - 2; + UINT16_TO_BE_STREAM (p_param_len, param_len); + + /* Set the length of the SDP data in the buffer */ + p_msg->len = p - p_start; + + + L2CA_DataWrite (p_ccb->connection_id, p_msg); + + /* Start inactivity timer */ + btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_SDP, SDP_INACT_TIMEOUT); + + return; + } + + + /*******************************************************************/ + /* We now have the full response, which is a sequence of sequences */ + /*******************************************************************/ + +#if (SDP_RAW_DATA_INCLUDED == TRUE) + SDP_TRACE_DEBUG("process_service_search_attr_rsp\n"); + sdp_copy_raw_data (p_ccb, TRUE); +#endif + + p = &p_ccb->rsp_list[0]; + + /* The contents is a sequence of attribute sequences */ + type = *p++; + + if ((type >> 3) != DATA_ELE_SEQ_DESC_TYPE) { + SDP_TRACE_WARNING ("SDP - Wrong type: 0x%02x in attr_rsp\n", type); + return; + } + p = sdpu_get_len_from_type (p, type, &seq_len); + + p_end = &p_ccb->rsp_list[p_ccb->list_len]; + + if ((p + seq_len) != p_end) { + sdp_disconnect (p_ccb, SDP_INVALID_CONT_STATE); + return; + } + + while (p < p_end) { + p = save_attr_seq (p_ccb, p, &p_ccb->rsp_list[p_ccb->list_len]); + if (!p) { + sdp_disconnect (p_ccb, SDP_DB_FULL); + return; + } + } + + /* Since we got everything we need, disconnect the call */ + sdp_disconnect (p_ccb, SDP_SUCCESS); +} + +/******************************************************************************* +** +** Function save_attr_seq +** +** Description This function is called when there is a response from +** the server. +** +** Returns pointer to next byte or NULL if error +** +*******************************************************************************/ +static UINT8 *save_attr_seq (tCONN_CB *p_ccb, UINT8 *p, UINT8 *p_msg_end) +{ + UINT32 seq_len, attr_len; + UINT16 attr_id; + UINT8 type, *p_seq_end; + tSDP_DISC_REC *p_rec; + + type = *p++; + + if ((type >> 3) != DATA_ELE_SEQ_DESC_TYPE) { + SDP_TRACE_WARNING ("SDP - Wrong type: 0x%02x in attr_rsp\n", type); + return (NULL); + } + + p = sdpu_get_len_from_type (p, type, &seq_len); + if ((p + seq_len) > p_msg_end) { + SDP_TRACE_WARNING ("SDP - Bad len in attr_rsp %d\n", seq_len); + return (NULL); + } + + /* Create a record */ + p_rec = add_record (p_ccb->p_db, p_ccb->device_address); + if (!p_rec) { + SDP_TRACE_WARNING ("SDP - DB full add_record\n"); + return (NULL); + } + + p_seq_end = p + seq_len; + + while (p < p_seq_end) { + /* First get the attribute ID */ + type = *p++; + p = sdpu_get_len_from_type (p, type, &attr_len); + if (((type >> 3) != UINT_DESC_TYPE) || (attr_len != 2)) { + SDP_TRACE_WARNING ("SDP - Bad type: 0x%02x or len: %d in attr_rsp\n", type, attr_len); + return (NULL); + } + BE_STREAM_TO_UINT16 (attr_id, p); + + /* Now, add the attribute value */ + p = add_attr (p, p_ccb->p_db, p_rec, attr_id, NULL, 0); + + if (!p) { + SDP_TRACE_WARNING ("SDP - DB full add_attr\n"); + return (NULL); + } + } + + return (p); +} + + +/******************************************************************************* +** +** Function add_record +** +** Description This function allocates space for a record from the DB. +** +** Returns pointer to next byte in data stream +** +*******************************************************************************/ +tSDP_DISC_REC *add_record (tSDP_DISCOVERY_DB *p_db, BD_ADDR p_bda) +{ + tSDP_DISC_REC *p_rec; + + /* See if there is enough space in the database */ + if (p_db->mem_free < sizeof (tSDP_DISC_REC)) { + return (NULL); + } + + p_rec = (tSDP_DISC_REC *) p_db->p_free_mem; + p_db->p_free_mem += sizeof (tSDP_DISC_REC); + p_db->mem_free -= sizeof (tSDP_DISC_REC); + + p_rec->p_first_attr = NULL; + p_rec->p_next_rec = NULL; + + memcpy (p_rec->remote_bd_addr, p_bda, BD_ADDR_LEN); + + /* Add the record to the end of chain */ + if (!p_db->p_first_rec) { + p_db->p_first_rec = p_rec; + } else { + tSDP_DISC_REC *p_rec1 = p_db->p_first_rec; + + while (p_rec1->p_next_rec) { + p_rec1 = p_rec1->p_next_rec; + } + + p_rec1->p_next_rec = p_rec; + } + + return (p_rec); +} + +#define SDP_ADDITIONAL_LIST_MASK 0x80 +/******************************************************************************* +** +** Function add_attr +** +** Description This function allocates space for an attribute from the DB +** and copies the data into it. +** +** Returns pointer to next byte in data stream +** +*******************************************************************************/ +static UINT8 *add_attr (UINT8 *p, tSDP_DISCOVERY_DB *p_db, tSDP_DISC_REC *p_rec, + UINT16 attr_id, tSDP_DISC_ATTR *p_parent_attr, UINT8 nest_level) +{ + tSDP_DISC_ATTR *p_attr; + UINT32 attr_len; + UINT32 total_len; + UINT16 attr_type; + UINT16 id; + UINT8 type; + UINT8 *p_end; + UINT8 is_additional_list = nest_level & SDP_ADDITIONAL_LIST_MASK; + + nest_level &= ~(SDP_ADDITIONAL_LIST_MASK); + + type = *p++; + p = sdpu_get_len_from_type (p, type, &attr_len); + + attr_len &= SDP_DISC_ATTR_LEN_MASK; + attr_type = (type >> 3) & 0x0f; + + /* See if there is enough space in the database */ + if (attr_len > 4) { + total_len = attr_len - 4 + (UINT16)sizeof (tSDP_DISC_ATTR); + } else { + total_len = sizeof (tSDP_DISC_ATTR); + } + + /* Ensure it is a multiple of 4 */ + total_len = (total_len + 3) & ~3; + + /* See if there is enough space in the database */ + if (p_db->mem_free < total_len) { + return (NULL); + } + + p_attr = (tSDP_DISC_ATTR *) p_db->p_free_mem; + p_attr->attr_id = attr_id; + p_attr->attr_len_type = (UINT16)attr_len | (attr_type << 12); + p_attr->p_next_attr = NULL; + + /* Store the attribute value */ + switch (attr_type) { + case UINT_DESC_TYPE: + if ( (is_additional_list != 0) && (attr_len == 2) ) { + BE_STREAM_TO_UINT16 (id, p); + if (id != ATTR_ID_PROTOCOL_DESC_LIST) { + p -= 2; + } else { + /* Reserve the memory for the attribute now, as we need to add sub-attributes */ + p_db->p_free_mem += sizeof (tSDP_DISC_ATTR); + p_db->mem_free -= sizeof (tSDP_DISC_ATTR); + p_end = p + attr_len; + total_len = 0; + + /* SDP_TRACE_DEBUG ("SDP - attr nest level:%d(list)", nest_level); */ + if (nest_level >= MAX_NEST_LEVELS) { + SDP_TRACE_ERROR ("SDP - attr nesting too deep\n"); + return (p_end); + } + + /* Now, add the list entry */ + p = add_attr (p, p_db, p_rec, ATTR_ID_PROTOCOL_DESC_LIST, p_attr, (UINT8)(nest_level + 1)); + + break; + } + } + /* Case falls through */ + + case TWO_COMP_INT_DESC_TYPE: + switch (attr_len) { + case 1: + p_attr->attr_value.v.u8 = *p++; + break; + case 2: + BE_STREAM_TO_UINT16 (p_attr->attr_value.v.u16, p); + break; + case 4: + BE_STREAM_TO_UINT32 (p_attr->attr_value.v.u32, p); + break; + default: + BE_STREAM_TO_ARRAY (p, p_attr->attr_value.v.array, (INT32)attr_len); + break; + } + break; + + case UUID_DESC_TYPE: + switch (attr_len) { + case 2: + BE_STREAM_TO_UINT16 (p_attr->attr_value.v.u16, p); + break; + case 4: + BE_STREAM_TO_UINT32 (p_attr->attr_value.v.u32, p); + if (p_attr->attr_value.v.u32 < 0x10000) { + attr_len = 2; + p_attr->attr_len_type = (UINT16)attr_len | (attr_type << 12); + p_attr->attr_value.v.u16 = (UINT16) p_attr->attr_value.v.u32; + + } + break; + case 16: + /* See if we can compress his UUID down to 16 or 32bit UUIDs */ + if (sdpu_is_base_uuid (p)) { + if ((p[0] == 0) && (p[1] == 0)) { + p_attr->attr_len_type = (p_attr->attr_len_type & ~SDP_DISC_ATTR_LEN_MASK) | 2; + p += 2; + BE_STREAM_TO_UINT16 (p_attr->attr_value.v.u16, p); + p += MAX_UUID_SIZE - 4; + } else { + p_attr->attr_len_type = (p_attr->attr_len_type & ~SDP_DISC_ATTR_LEN_MASK) | 4; + BE_STREAM_TO_UINT32 (p_attr->attr_value.v.u32, p); + p += MAX_UUID_SIZE - 4; + } + } else { + /* coverity[overrun-local] */ + /* + Event overrun-local: Overrun of static array "p_attr->attr_value.v.array" of size 4 at position 15 with index variable "ijk" + False-positive: SDP uses scratch buffer to hold the attribute value. + The actual size of tSDP_DISC_ATVAL does not matter. + If the array size in tSDP_DISC_ATVAL is increase, we would increase the system RAM usage unnecessarily + */ + BE_STREAM_TO_ARRAY (p, p_attr->attr_value.v.array, (INT32)attr_len); + } + break; + default: + SDP_TRACE_WARNING ("SDP - bad len in UUID attr: %d\n", attr_len); + return (p + attr_len); + } + break; + + case DATA_ELE_SEQ_DESC_TYPE: + case DATA_ELE_ALT_DESC_TYPE: + /* Reserve the memory for the attribute now, as we need to add sub-attributes */ + p_db->p_free_mem += sizeof (tSDP_DISC_ATTR); + p_db->mem_free -= sizeof (tSDP_DISC_ATTR); + p_end = p + attr_len; + total_len = 0; + + /* SDP_TRACE_DEBUG ("SDP - attr nest level:%d", nest_level); */ + if (nest_level >= MAX_NEST_LEVELS) { + SDP_TRACE_ERROR ("SDP - attr nesting too deep\n"); + return (p_end); + } + if (is_additional_list != 0 || attr_id == ATTR_ID_ADDITION_PROTO_DESC_LISTS) { + nest_level |= SDP_ADDITIONAL_LIST_MASK; + } + /* SDP_TRACE_DEBUG ("SDP - attr nest level:0x%x(finish)", nest_level); */ + + while (p < p_end) { + /* Now, add the list entry */ + p = add_attr (p, p_db, p_rec, 0, p_attr, (UINT8)(nest_level + 1)); + + if (!p) { + return (NULL); + } + } + break; + + case TEXT_STR_DESC_TYPE: + case URL_DESC_TYPE: + BE_STREAM_TO_ARRAY (p, p_attr->attr_value.v.array, (INT32)attr_len); + break; + + case BOOLEAN_DESC_TYPE: + switch (attr_len) { + case 1: + p_attr->attr_value.v.u8 = *p++; + break; + default: + SDP_TRACE_WARNING ("SDP - bad len in boolean attr: %d\n", attr_len); + return (p + attr_len); + } + break; + + default: /* switch (attr_type) */ + break; + } + + p_db->p_free_mem += total_len; + p_db->mem_free -= total_len; + + /* Add the attribute to the end of the chain */ + if (!p_parent_attr) { + if (!p_rec->p_first_attr) { + p_rec->p_first_attr = p_attr; + } else { + tSDP_DISC_ATTR *p_attr1 = p_rec->p_first_attr; + + while (p_attr1->p_next_attr) { + p_attr1 = p_attr1->p_next_attr; + } + + p_attr1->p_next_attr = p_attr; + } + } else { + if (!p_parent_attr->attr_value.v.p_sub_attr) { + p_parent_attr->attr_value.v.p_sub_attr = p_attr; + /* SDP_TRACE_DEBUG ("parent:0x%x(id:%d), ch:0x%x(id:%d)", + p_parent_attr, p_parent_attr->attr_id, p_attr, p_attr->attr_id); */ + } else { + tSDP_DISC_ATTR *p_attr1 = p_parent_attr->attr_value.v.p_sub_attr; + /* SDP_TRACE_DEBUG ("parent:0x%x(id:%d), ch1:0x%x(id:%d)", + p_parent_attr, p_parent_attr->attr_id, p_attr1, p_attr1->attr_id); */ + + while (p_attr1->p_next_attr) { + p_attr1 = p_attr1->p_next_attr; + } + + p_attr1->p_next_attr = p_attr; + /* SDP_TRACE_DEBUG ("new ch:0x%x(id:%d)", p_attr, p_attr->attr_id); */ + } + } + + return (p); +} + +#endif /* CLIENT_ENABLED == TRUE */ diff --git a/lib/bt/host/bluedroid/stack/sdp/sdp_main.c b/lib/bt/host/bluedroid/stack/sdp/sdp_main.c new file mode 100644 index 00000000..e945314b --- /dev/null +++ b/lib/bt/host/bluedroid/stack/sdp/sdp_main.c @@ -0,0 +1,786 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains the main SDP functions + * + ******************************************************************************/ + +#include +#include +//#include + +#include "common/bt_target.h" +#include "osi/allocator.h" +#include "stack/l2cdefs.h" +#include "stack/hcidefs.h" +#include "stack/hcimsgs.h" + +#include "stack/l2c_api.h" +#include "stack/l2cdefs.h" + +#include "stack/btu.h" +#include "stack/btm_api.h" + +#include "stack/sdp_api.h" +#include "sdpint.h" + +#include "osi/list.h" + +#if (SDP_INCLUDED == TRUE) +/********************************************************************************/ +/* G L O B A L S D P D A T A */ +/********************************************************************************/ +#if SDP_DYNAMIC_MEMORY == FALSE +tSDP_CB sdp_cb; +#else +tSDP_CB *sdp_cb_ptr; +#endif + +/********************************************************************************/ +/* L O C A L F U N C T I O N P R O T O T Y P E S */ +/********************************************************************************/ +static void sdp_connect_ind (BD_ADDR bd_addr, UINT16 l2cap_cid, UINT16 psm, + UINT8 l2cap_id); +static void sdp_config_ind (UINT16 l2cap_cid, tL2CAP_CFG_INFO *p_cfg); +static void sdp_config_cfm (UINT16 l2cap_cid, tL2CAP_CFG_INFO *p_cfg); +static void sdp_disconnect_ind (UINT16 l2cap_cid, BOOLEAN ack_needed); +static void sdp_data_ind (UINT16 l2cap_cid, BT_HDR *p_msg); + +#if SDP_CLIENT_ENABLED == TRUE +static void sdp_connect_cfm (UINT16 l2cap_cid, UINT16 result); +static void sdp_disconnect_cfm (UINT16 l2cap_cid, UINT16 result); +#else +#define sdp_connect_cfm NULL +#define sdp_disconnect_cfm NULL +#endif + +#if BT_SDP_BQB_INCLUDED +static BOOLEAN s_sdp_bqb_disable_flag = FALSE; +static BOOLEAN s_sdp_bqb_inact_timeout_flag = FALSE; +BOOLEAN l2cap_bqb_ertm_mode_included_flag = FALSE; +#endif /* BT_SDP_BQB_INCLUDED */ + +/******************************************************************************* +** +** Function sdp_bqb_disable_ctrl +** +** Description Control the disable of bqb for SDP BQB test +** +** Returns void +** +*******************************************************************************/ +#if BT_SDP_BQB_INCLUDED +void sdp_bqb_disable_ctrl(BOOLEAN enable) +{ + s_sdp_bqb_disable_flag = enable; +} +#endif /* BT_SDP_BQB_INCLUDED */ + +/******************************************************************************* +** +** Function sdp_bqb_inact_timeout_ctrl +** +** Description Control the inactivity timeout for SDP BQB test +** +** Returns void +** +*******************************************************************************/ +#if BT_SDP_BQB_INCLUDED +void sdp_bqb_inact_timeout_ctrl(BOOLEAN enable) +{ + s_sdp_bqb_inact_timeout_flag = enable; +} +#endif /* BT_SDP_BQB_INCLUDED */ + +/******************************************************************************* +** +** Function l2cap_bqb_ertm_mode_included_ctrl +** +** Description Control the L2CAP flow control and retransmissions mode for SDP BQB test +** +** Returns void +** +*******************************************************************************/ +#if BT_SDP_BQB_INCLUDED +void l2cap_bqb_ertm_mode_included_ctrl(BOOLEAN enable) +{ + l2cap_bqb_ertm_mode_included_flag = enable; +} +#endif /* BT_SDP_BQB_INCLUDED */ + +/******************************************************************************* +** +** Function sdp_init +** +** Description This function initializes the SDP unit. +** +** Returns void +** +*******************************************************************************/ +void sdp_init (void) +{ +#if SDP_DYNAMIC_MEMORY + sdp_cb_ptr = (tSDP_CB *)osi_malloc(sizeof(tSDP_CB)); +#endif /* #if SDP_DYNAMIC_MEMORY */ + /* Clears all structures and local SDP database (if Server is enabled) */ + memset (&sdp_cb, 0, sizeof (tSDP_CB)); + + sdp_cb.server_db.p_record_list = list_new(osi_free_func); + /* Initialize the L2CAP configuration. We only care about MTU and flush */ + sdp_cb.l2cap_my_cfg.mtu_present = TRUE; + sdp_cb.l2cap_my_cfg.mtu = SDP_MTU_SIZE; + sdp_cb.l2cap_my_cfg.flush_to_present = TRUE; + sdp_cb.l2cap_my_cfg.flush_to = SDP_FLUSH_TO; +#if BT_SDP_BQB_INCLUDED + if (l2cap_bqb_ertm_mode_included_flag) { + sdp_cb.l2cap_my_cfg.fcr_present = TRUE; + sdp_cb.l2cap_my_cfg.fcr.mode = L2CAP_FCR_ERTM_MODE; + sdp_cb.l2cap_my_cfg.fcr.tx_win_sz = 8; + sdp_cb.l2cap_my_cfg.fcr.max_transmit = 0xff; + sdp_cb.l2cap_my_cfg.fcr.rtrans_tout = 2000; + sdp_cb.l2cap_my_cfg.fcr.mon_tout = 12000; + sdp_cb.l2cap_my_cfg.fcr.mps = 672; + } +#endif /* BT_SDP_BQB_INCLUDED */ + sdp_cb.max_attr_list_size = SDP_MTU_SIZE - 16; + sdp_cb.max_recs_per_search = SDP_MAX_DISC_SERVER_RECS; + +#if SDP_SERVER_ENABLED == TRUE + /* Register with Security Manager for the specific security level */ + if (!BTM_SetSecurityLevel (FALSE, SDP_SERVICE_NAME, BTM_SEC_SERVICE_SDP_SERVER, + SDP_SECURITY_LEVEL, SDP_PSM, 0, 0)) { + SDP_TRACE_ERROR ("Security Registration Server failed\n"); + return; + } +#endif + +#if SDP_CLIENT_ENABLED == TRUE + /* Register with Security Manager for the specific security level */ + if (!BTM_SetSecurityLevel (TRUE, SDP_SERVICE_NAME, BTM_SEC_SERVICE_SDP_SERVER, + SDP_SECURITY_LEVEL, SDP_PSM, 0, 0)) { + SDP_TRACE_ERROR ("Security Registration for Client failed\n"); + return; + } +#endif + +#if defined(SDP_INITIAL_TRACE_LEVEL) + sdp_cb.trace_level = SDP_INITIAL_TRACE_LEVEL; +#else + sdp_cb.trace_level = BT_TRACE_LEVEL_NONE; /* No traces */ +#endif + + sdp_cb.reg_info.pL2CA_ConnectInd_Cb = sdp_connect_ind; + sdp_cb.reg_info.pL2CA_ConnectCfm_Cb = sdp_connect_cfm; + sdp_cb.reg_info.pL2CA_ConnectPnd_Cb = NULL; + sdp_cb.reg_info.pL2CA_ConfigInd_Cb = sdp_config_ind; + sdp_cb.reg_info.pL2CA_ConfigCfm_Cb = sdp_config_cfm; + sdp_cb.reg_info.pL2CA_DisconnectInd_Cb = sdp_disconnect_ind; + sdp_cb.reg_info.pL2CA_DisconnectCfm_Cb = sdp_disconnect_cfm; + sdp_cb.reg_info.pL2CA_QoSViolationInd_Cb = NULL; + sdp_cb.reg_info.pL2CA_DataInd_Cb = sdp_data_ind; + sdp_cb.reg_info.pL2CA_CongestionStatus_Cb = NULL; + sdp_cb.reg_info.pL2CA_TxComplete_Cb = NULL; + + /* Now, register with L2CAP */ + if (!L2CA_Register (SDP_PSM, &sdp_cb.reg_info)) { + SDP_TRACE_ERROR ("SDP Registration failed\n"); + } +} + +void sdp_deinit (void) +{ + list_free(sdp_cb.server_db.p_record_list); +#if SDP_DYNAMIC_MEMORY + osi_free(sdp_cb_ptr); + sdp_cb_ptr = NULL; +#endif /* #if SDP_DYNAMIC_MEMORY */ +} + +#if (defined(SDP_DEBUG) && SDP_DEBUG == TRUE) +/******************************************************************************* +** +** Function sdp_set_max_attr_list_size +** +** Description This function sets the max attribute list size to use +** +** Returns void +** +*******************************************************************************/ +UINT16 sdp_set_max_attr_list_size (UINT16 max_size) +{ + if (max_size > (sdp_cb.l2cap_my_cfg.mtu - 16) ) { + max_size = sdp_cb.l2cap_my_cfg.mtu - 16; + } + + sdp_cb.max_attr_list_size = max_size; + + return sdp_cb.max_attr_list_size; +} +#endif + +/******************************************************************************* +** +** Function sdp_connect_ind +** +** Description This function handles an inbound connection indication +** from L2CAP. This is the case where we are acting as a +** server. +** +** Returns void +** +*******************************************************************************/ +static void sdp_connect_ind (BD_ADDR bd_addr, UINT16 l2cap_cid, UINT16 psm, UINT8 l2cap_id) +{ + UNUSED(psm); +#if SDP_SERVER_ENABLED == TRUE + tCONN_CB *p_ccb; + + /* Allocate a new CCB. Return if none available. */ + if ((p_ccb = sdpu_allocate_ccb()) == NULL) { + return; + } + + /* Transition to the next appropriate state, waiting for config setup. */ + p_ccb->con_state = SDP_STATE_CFG_SETUP; + + /* Save the BD Address and Channel ID. */ + memcpy (&p_ccb->device_address[0], bd_addr, sizeof (BD_ADDR)); + p_ccb->connection_id = l2cap_cid; + + /* Send response to the L2CAP layer. */ + L2CA_ConnectRsp (bd_addr, l2cap_id, l2cap_cid, L2CAP_CONN_OK, L2CAP_CONN_OK); + { + tL2CAP_CFG_INFO cfg = sdp_cb.l2cap_my_cfg; + + if (cfg.fcr_present) { + SDP_TRACE_DEBUG("sdp_connect_ind: mode %u, txwinsz %u, max_trans %u, rtrans_tout %u, mon_tout %u, mps %u\n", + cfg.fcr.mode, cfg.fcr.tx_win_sz, cfg.fcr.max_transmit, + cfg.fcr.rtrans_tout, cfg.fcr.mon_tout, cfg.fcr.mps); + } + + if ((!L2CA_ConfigReq (l2cap_cid, &cfg)) && cfg.fcr_present + && cfg.fcr.mode != L2CAP_FCR_BASIC_MODE) { + /* FCR not desired; try again in basic mode */ + cfg.fcr.mode = L2CAP_FCR_BASIC_MODE; + cfg.fcr_present = FALSE; + L2CA_ConfigReq (l2cap_cid, &cfg); + } + } + + SDP_TRACE_EVENT ("SDP - Rcvd L2CAP conn ind, sent config req, CID 0x%x\n", p_ccb->connection_id); +#else /* No server */ + /* Reject the connection */ + L2CA_ConnectRsp (bd_addr, l2cap_id, l2cap_cid, L2CAP_CONN_NO_PSM, 0); +#endif +} + +#if SDP_CLIENT_ENABLED == TRUE +/******************************************************************************* +** +** Function sdp_connect_cfm +** +** Description This function handles the connect confirm events +** from L2CAP. This is the case when we are acting as a +** client and have sent a connect request. +** +** Returns void +** +*******************************************************************************/ +static void sdp_connect_cfm (UINT16 l2cap_cid, UINT16 result) +{ + tCONN_CB *p_ccb; + tL2CAP_CFG_INFO cfg; + + /* Find CCB based on CID */ + if ((p_ccb = sdpu_find_ccb_by_cid (l2cap_cid)) == NULL) { + SDP_TRACE_WARNING ("SDP - Rcvd conn cnf for unknown CID 0x%x\n", l2cap_cid); + return; + } + + /* If the connection response contains success status, then */ + /* Transition to the next state and startup the timer. */ + if ((result == L2CAP_CONN_OK) && (p_ccb->con_state == SDP_STATE_CONN_SETUP)) { + p_ccb->con_state = SDP_STATE_CFG_SETUP; + + cfg = sdp_cb.l2cap_my_cfg; + + if (cfg.fcr_present) { + SDP_TRACE_DEBUG("sdp_connect_cfm: mode %u, txwinsz %u, max_trans %u, rtrans_tout %u, mon_tout %u, mps %u\n", + cfg.fcr.mode, cfg.fcr.tx_win_sz, cfg.fcr.max_transmit, + cfg.fcr.rtrans_tout, cfg.fcr.mon_tout, cfg.fcr.mps); + } + + if ((!L2CA_ConfigReq (l2cap_cid, &cfg)) && cfg.fcr_present + && cfg.fcr.mode != L2CAP_FCR_BASIC_MODE) { + /* FCR not desired; try again in basic mode */ + cfg.fcr_present = FALSE; + cfg.fcr.mode = L2CAP_FCR_BASIC_MODE; + L2CA_ConfigReq (l2cap_cid, &cfg); + } + + SDP_TRACE_EVENT ("SDP - got conn cnf, sent cfg req, CID: 0x%x\n", p_ccb->connection_id); + } else { + SDP_TRACE_WARNING ("SDP - Rcvd conn cnf with error: 0x%x CID 0x%x\n", result, p_ccb->connection_id); + + /* Tell the user if he has a callback */ + if (p_ccb->p_cb || p_ccb->p_cb2) { + UINT16 err = -1; + if ((result == HCI_ERR_HOST_REJECT_SECURITY) + || (result == HCI_ERR_AUTH_FAILURE) + || (result == HCI_ERR_PAIRING_NOT_ALLOWED) + || (result == HCI_ERR_PAIRING_WITH_UNIT_KEY_NOT_SUPPORTED) + || (result == HCI_ERR_KEY_MISSING)) { + err = SDP_SECURITY_ERR; + } else if (result == HCI_ERR_HOST_REJECT_DEVICE) { + err = SDP_CONN_REJECTED; + } else { + err = SDP_CONN_FAILED; + } + if (p_ccb->p_cb) { + (*p_ccb->p_cb)(err); + } else if (p_ccb->p_cb2) { + (*p_ccb->p_cb2)(err, p_ccb->user_data); + } + + } + sdpu_release_ccb (p_ccb); + } +} +#endif /* SDP_CLIENT_ENABLED == TRUE */ + + +/******************************************************************************* +** +** Function sdp_config_ind +** +** Description This function processes the L2CAP configuration indication +** event. +** +** Returns void +** +*******************************************************************************/ +static void sdp_config_ind (UINT16 l2cap_cid, tL2CAP_CFG_INFO *p_cfg) +{ + tCONN_CB *p_ccb; + + /* Find CCB based on CID */ + if ((p_ccb = sdpu_find_ccb_by_cid (l2cap_cid)) == NULL) { + SDP_TRACE_WARNING ("SDP - Rcvd L2CAP cfg ind, unknown CID: 0x%x\n", l2cap_cid); + return; + } + + /* Remember the remote MTU size */ + if (!p_cfg->mtu_present) { + /* use min(L2CAP_DEFAULT_MTU,SDP_MTU_SIZE) for GKI buffer size reasons */ + p_ccb->rem_mtu_size = (L2CAP_DEFAULT_MTU > SDP_MTU_SIZE) ? SDP_MTU_SIZE : L2CAP_DEFAULT_MTU; + } else { + if (p_cfg->mtu > SDP_MTU_SIZE) { + p_ccb->rem_mtu_size = SDP_MTU_SIZE; + } else { + p_ccb->rem_mtu_size = p_cfg->mtu; + } + } + + /* For now, always accept configuration from the other side */ + p_cfg->flush_to_present = FALSE; + p_cfg->mtu_present = FALSE; + p_cfg->result = L2CAP_CFG_OK; + + /* Check peer config request against our rfcomm configuration */ + if (p_cfg->fcr_present) { + /* Reject the window size if it is bigger than we want it to be */ + if (p_cfg->fcr.mode != L2CAP_FCR_BASIC_MODE) { + if (sdp_cb.l2cap_my_cfg.fcr.mode != L2CAP_FCR_BASIC_MODE + && p_cfg->fcr.tx_win_sz > sdp_cb.l2cap_my_cfg.fcr.tx_win_sz) { + p_cfg->fcr.tx_win_sz = sdp_cb.l2cap_my_cfg.fcr.tx_win_sz; + p_cfg->result = L2CAP_CFG_UNACCEPTABLE_PARAMS; + SDP_TRACE_DEBUG("sdp_config_ind(CONFIG) -> Please try again with SMALLER TX WINDOW\n"); + } + + /* Reject if locally we want basic and they don't */ + if (sdp_cb.l2cap_my_cfg.fcr.mode == L2CAP_FCR_BASIC_MODE) { + /* Ask for a new setup */ + p_cfg->fcr.mode = L2CAP_FCR_BASIC_MODE; + p_cfg->result = L2CAP_CFG_UNACCEPTABLE_PARAMS; + SDP_TRACE_DEBUG("sdp_config_ind(CONFIG) -> Please try again with BASIC mode\n"); + } + /* Remain in configure state and give the peer our desired configuration */ + if (p_cfg->result != L2CAP_CFG_OK) { + SDP_TRACE_WARNING ("SDP - Rcvd cfg ind, Unacceptable Parameters sent cfg cfm, CID: 0x%x\n", l2cap_cid); + L2CA_ConfigRsp (l2cap_cid, p_cfg); + return; + } + } else { /* We agree with peer's request */ + p_cfg->fcr_present = FALSE; + } + } + + L2CA_ConfigRsp (l2cap_cid, p_cfg); + + SDP_TRACE_EVENT ("SDP - Rcvd cfg ind, sent cfg cfm, CID: 0x%x\n", l2cap_cid); + + p_ccb->con_flags |= SDP_FLAGS_HIS_CFG_DONE; + + if (p_ccb->con_flags & SDP_FLAGS_MY_CFG_DONE) { + p_ccb->con_state = SDP_STATE_CONNECTED; + + if (p_ccb->con_flags & SDP_FLAGS_IS_ORIG) { + sdp_disc_connected (p_ccb); + } else + /* Start inactivity timer */ + { + btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_SDP, SDP_INACT_TIMEOUT); + } + } + +} + + +/******************************************************************************* +** +** Function sdp_config_cfm +** +** Description This function processes the L2CAP configuration confirmation +** event. +** +** Returns void +** +*******************************************************************************/ +static void sdp_config_cfm (UINT16 l2cap_cid, tL2CAP_CFG_INFO *p_cfg) +{ + tCONN_CB *p_ccb; + + SDP_TRACE_EVENT ("SDP - Rcvd cfg cfm, CID: 0x%x Result: %d\n", l2cap_cid, p_cfg->result); + + /* Find CCB based on CID */ + if ((p_ccb = sdpu_find_ccb_by_cid (l2cap_cid)) == NULL) { + SDP_TRACE_WARNING ("SDP - Rcvd L2CAP cfg ind, unknown CID: 0x%x\n", l2cap_cid); + return; + } + + /* For now, always accept configuration from the other side */ + if (p_cfg->result == L2CAP_CFG_OK) { + p_ccb->con_flags |= SDP_FLAGS_MY_CFG_DONE; + + if (p_ccb->con_flags & SDP_FLAGS_HIS_CFG_DONE) { + p_ccb->con_state = SDP_STATE_CONNECTED; + + if (p_ccb->con_flags & SDP_FLAGS_IS_ORIG) { + sdp_disc_connected (p_ccb); + } else + /* Start inactivity timer */ + { +#if BT_SDP_BQB_INCLUDED + /* Change the timeout from 30s to 90s for BQB test */ + if (s_sdp_bqb_inact_timeout_flag) { + btu_start_timer(&p_ccb->timer_entry, BTU_TTYPE_SDP, SDP_BQB_INACT_TIMEOUT); + } else +#endif /* BT_SDP_BQB_INCLUDED */ + { + btu_start_timer(&p_ccb->timer_entry, BTU_TTYPE_SDP, SDP_INACT_TIMEOUT); + } + } + } + } else { + /* If peer has rejected FCR and suggested basic then try basic */ + if (p_cfg->fcr_present) { + tL2CAP_CFG_INFO cfg = sdp_cb.l2cap_my_cfg; + cfg.fcr_present = FALSE; + L2CA_ConfigReq (l2cap_cid, &cfg); + + /* Remain in configure state */ + return; + } + +#if SDP_CLIENT_ENABLED == TRUE + sdp_disconnect(p_ccb, SDP_CFG_FAILED); +#endif + } +} + +/******************************************************************************* +** +** Function sdp_disconnect_ind +** +** Description This function handles a disconnect event from L2CAP. If +** requested to, we ack the disconnect before dropping the CCB +** +** Returns void +** +*******************************************************************************/ +static void sdp_disconnect_ind (UINT16 l2cap_cid, BOOLEAN ack_needed) +{ + tCONN_CB *p_ccb; + + /* Find CCB based on CID */ + if ((p_ccb = sdpu_find_ccb_by_cid (l2cap_cid)) == NULL) { + SDP_TRACE_WARNING ("SDP - Rcvd L2CAP disc, unknown CID: 0x%x\n", l2cap_cid); + return; + } + + if (ack_needed) { + L2CA_DisconnectRsp (l2cap_cid); + } + + SDP_TRACE_EVENT ("SDP - Rcvd L2CAP disc, CID: 0x%x\n", l2cap_cid); +#if SDP_CLIENT_ENABLED == TRUE + /* Tell the user if he has a callback */ + if (p_ccb->p_cb) { + (*p_ccb->p_cb) ((UINT16) ((p_ccb->con_state == SDP_STATE_CONNECTED) ? + SDP_SUCCESS : SDP_CONN_FAILED)); + } else if (p_ccb->p_cb2) { + (*p_ccb->p_cb2) ((UINT16) ((p_ccb->con_state == SDP_STATE_CONNECTED) ? + SDP_SUCCESS : SDP_CONN_FAILED), p_ccb->user_data); + } + +#endif + sdpu_release_ccb (p_ccb); +} + +/******************************************************************************* +** +** Function sdp_data_ind +** +** Description This function is called when data is received from L2CAP. +** if we are the originator of the connection, we are the SDP +** client, and the received message is queued up for the client. +** +** If we are the destination of the connection, we are the SDP +** server, so the message is passed to the server processing +** function. +** +** Returns void +** +*******************************************************************************/ +static void sdp_data_ind (UINT16 l2cap_cid, BT_HDR *p_msg) +{ + tCONN_CB *p_ccb; + + /* Find CCB based on CID */ + if ((p_ccb = sdpu_find_ccb_by_cid (l2cap_cid)) != NULL) { + if (p_ccb->con_state == SDP_STATE_CONNECTED) { +#if BT_SDP_BQB_INCLUDED + /* Skip the following code in BQB test when the flag is true, since the PDU is reserved and + function sdp_server_handle_client_req will return error (sdpu_build_n_send_error) */ + if (!s_sdp_bqb_disable_flag) +#endif /* BT_SDP_BQB_INCLUDED */ + { + if (p_ccb->con_flags & SDP_FLAGS_IS_ORIG) { + sdp_disc_server_rsp(p_ccb, p_msg); + } else { + sdp_server_handle_client_req(p_ccb, p_msg); + } + } + } else { + SDP_TRACE_WARNING ("SDP - Ignored L2CAP data while in state: %d, CID: 0x%x\n", + p_ccb->con_state, l2cap_cid); + } + } else { + SDP_TRACE_WARNING ("SDP - Rcvd L2CAP data, unknown CID: 0x%x\n", l2cap_cid); + } + + osi_free (p_msg); +} + + +#if SDP_CLIENT_ENABLED == TRUE +/******************************************************************************* +** +** Function sdp_conn_originate +** +** Description This function is called from the API to originate a +** connection. +** +** Returns void +** +*******************************************************************************/ +tCONN_CB *sdp_conn_originate (UINT8 *p_bd_addr) +{ + tCONN_CB *p_ccb; + UINT16 cid; + + /* Allocate a new CCB. Return if none available. */ + if ((p_ccb = sdpu_allocate_ccb()) == NULL) { + SDP_TRACE_WARNING ("SDP - no spare CCB for orig\n"); + return (NULL); + } + + SDP_TRACE_EVENT ("SDP - Originate started\n"); + + /* We are the originator of this connection */ + p_ccb->con_flags |= SDP_FLAGS_IS_ORIG; + + /* Save the BD Address and Channel ID. */ + memcpy (&p_ccb->device_address[0], p_bd_addr, sizeof (BD_ADDR)); + + /* Transition to the next appropriate state, waiting for connection confirm. */ + p_ccb->con_state = SDP_STATE_CONN_SETUP; + + cid = L2CA_ConnectReq (SDP_PSM, p_bd_addr); + + /* Check if L2CAP started the connection process */ + if (cid != 0) { + p_ccb->connection_id = cid; + + return (p_ccb); + } else { + SDP_TRACE_WARNING ("SDP - Originate failed\n"); + sdpu_release_ccb (p_ccb); + return (NULL); + } +} + +/******************************************************************************* +** +** Function sdp_disconnect +** +** Description This function disconnects a connection. +** +** Returns void +** +*******************************************************************************/ +void sdp_disconnect (tCONN_CB *p_ccb, UINT16 reason) +{ +#if (defined(SDP_BROWSE_PLUS) && SDP_BROWSE_PLUS == TRUE) + /* If we are browsing for multiple UUIDs ... */ + if ((p_ccb->con_state == SDP_STATE_CONNECTED) + && (p_ccb->con_flags & SDP_FLAGS_IS_ORIG) + && ((reason == SDP_SUCCESS) || (reason == SDP_NO_RECS_MATCH))) { + /* If the browse found something, do no more searching */ + if ((p_ccb->cur_uuid_idx == 0) && (p_ccb->p_db->p_first_rec)) { + p_ccb->cur_uuid_idx = p_ccb->p_db->num_uuid_filters; + } + + while (++p_ccb->cur_uuid_idx < p_ccb->p_db->num_uuid_filters) { + /* Check we have not already found the UUID (maybe through browse) */ + if ((p_ccb->p_db->uuid_filters[p_ccb->cur_uuid_idx].len == 2) + && (SDP_FindServiceInDb (p_ccb->p_db, + p_ccb->p_db->uuid_filters[p_ccb->cur_uuid_idx].uu.uuid16, + NULL))) { + continue; + } + + if ((p_ccb->p_db->uuid_filters[p_ccb->cur_uuid_idx].len > 2) + && (SDP_FindServiceUUIDInDb (p_ccb->p_db, + &p_ccb->p_db->uuid_filters[p_ccb->cur_uuid_idx], NULL))) { + continue; + } + + p_ccb->cur_handle = 0; + + SDP_TRACE_EVENT ("SDP - looking for for more, CID: 0x%x\n", + p_ccb->connection_id); + + sdp_disc_connected (p_ccb); + return; + } + } + + if ((reason == SDP_NO_RECS_MATCH) && (p_ccb->p_db->p_first_rec)) { + reason = SDP_SUCCESS; + } + +#endif + + SDP_TRACE_EVENT ("SDP - disconnect CID: 0x%x\n", p_ccb->connection_id); + + /* Check if we have a connection ID */ + if (p_ccb->connection_id != 0) { + L2CA_DisconnectReq (p_ccb->connection_id); + p_ccb->disconnect_reason = reason; + } + + /* If at setup state, we may not get callback ind from L2CAP */ + /* Call user callback immediately */ + if (p_ccb->con_state == SDP_STATE_CONN_SETUP) { + /* Tell the user if he has a callback */ + if (p_ccb->p_cb) { + (*p_ccb->p_cb) (reason); + } else if (p_ccb->p_cb2) { + (*p_ccb->p_cb2) (reason, p_ccb->user_data); + } + + sdpu_release_ccb (p_ccb); + } + +} + +/******************************************************************************* +** +** Function sdp_disconnect_cfm +** +** Description This function handles a disconnect confirm event from L2CAP. +** +** Returns void +** +*******************************************************************************/ +static void sdp_disconnect_cfm (UINT16 l2cap_cid, UINT16 result) +{ + tCONN_CB *p_ccb; + UNUSED(result); + + /* Find CCB based on CID */ + if ((p_ccb = sdpu_find_ccb_by_cid (l2cap_cid)) == NULL) { + SDP_TRACE_WARNING ("SDP - Rcvd L2CAP disc cfm, unknown CID: 0x%x\n", l2cap_cid); + return; + } + + SDP_TRACE_EVENT ("SDP - Rcvd L2CAP disc cfm, CID: 0x%x, rsn %d\n", l2cap_cid, p_ccb->disconnect_reason); + /* Tell the user if he has a callback */ + if (p_ccb->p_cb) { + (*p_ccb->p_cb) (p_ccb->disconnect_reason); + } else if (p_ccb->p_cb2) { + (*p_ccb->p_cb2) (p_ccb->disconnect_reason, p_ccb->user_data); + } + + + sdpu_release_ccb (p_ccb); +} + +#endif /* SDP_CLIENT_ENABLED == TRUE */ + +/******************************************************************************* +** +** Function sdp_conn_timeout +** +** Description This function processes a timeout. Currently, we simply send +** a disconnect request to L2CAP. +** +** Returns void +** +*******************************************************************************/ +void sdp_conn_timeout (tCONN_CB *p_ccb) +{ + SDP_TRACE_EVENT ("SDP - CCB timeout in state: %d CID: 0x%x\n", + p_ccb->con_state, p_ccb->connection_id); + + L2CA_DisconnectReq (p_ccb->connection_id); +#if SDP_CLIENT_ENABLED == TRUE + /* Tell the user if he has a callback */ + if (p_ccb->p_cb) { + (*p_ccb->p_cb) (SDP_CONN_FAILED); + } else if (p_ccb->p_cb2) { + (*p_ccb->p_cb2) (SDP_CONN_FAILED, p_ccb->user_data); + } +#endif + sdpu_release_ccb (p_ccb); +} + +#endif ///SDP_INCLUDED == TRUE diff --git a/lib/bt/host/bluedroid/stack/sdp/sdp_server.c b/lib/bt/host/bluedroid/stack/sdp/sdp_server.c new file mode 100644 index 00000000..856aba71 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/sdp/sdp_server.c @@ -0,0 +1,838 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains functions that handle the SDP server functions. + * This is mainly dealing with client requests + * + ******************************************************************************/ + +//#include +#include +//#include + +#include "stack/bt_types.h" +#include "osi/allocator.h" +#include "stack/btu.h" +#include "common/bt_defs.h" +#include "stack/l2cdefs.h" +#include "stack/hcidefs.h" +#include "stack/hcimsgs.h" + +#include "stack/sdp_api.h" +#include "sdpint.h" + + +#if SDP_SERVER_ENABLED == TRUE + +/* Maximum number of bytes to reserve out of SDP MTU for response data */ +#define SDP_MAX_SERVICE_RSPHDR_LEN 12 +#define SDP_MAX_SERVATTR_RSPHDR_LEN 10 +#define SDP_MAX_ATTR_RSPHDR_LEN 10 + +/********************************************************************************/ +/* L O C A L F U N C T I O N P R O T O T Y P E S */ +/********************************************************************************/ +static void process_service_search (tCONN_CB *p_ccb, UINT16 trans_num, + UINT16 param_len, UINT8 *p_req, + UINT8 *p_req_end); + +static void process_service_attr_req (tCONN_CB *p_ccb, UINT16 trans_num, + UINT16 param_len, UINT8 *p_req, + UINT8 *p_req_end); + +static void process_service_search_attr_req (tCONN_CB *p_ccb, UINT16 trans_num, + UINT16 param_len, UINT8 *p_req, + UINT8 *p_req_end); + + +/********************************************************************************/ +/* E R R O R T E X T S T R I N G S */ +/* */ +/* The default is to have no text string, but we allow the strings to be */ +/* configured in target.h if people want them. */ +/********************************************************************************/ +#ifndef SDP_TEXT_BAD_HEADER +#define SDP_TEXT_BAD_HEADER NULL +#endif + +#ifndef SDP_TEXT_BAD_PDU +#define SDP_TEXT_BAD_PDU NULL +#endif + +#ifndef SDP_TEXT_BAD_UUID_LIST +#define SDP_TEXT_BAD_UUID_LIST NULL +#endif + +#ifndef SDP_TEXT_BAD_HANDLE +#define SDP_TEXT_BAD_HANDLE NULL +#endif + +#ifndef SDP_TEXT_BAD_ATTR_LIST +#define SDP_TEXT_BAD_ATTR_LIST NULL +#endif + +#ifndef SDP_TEXT_BAD_CONT_LEN +#define SDP_TEXT_BAD_CONT_LEN NULL +#endif + +#ifndef SDP_TEXT_BAD_CONT_INX +#define SDP_TEXT_BAD_CONT_INX NULL +#endif + +#ifndef SDP_TEXT_BAD_MAX_RECORDS_LIST +#define SDP_TEXT_BAD_MAX_RECORDS_LIST NULL +#endif + +/******************************************************************************* +** +** Function sdp_server_handle_client_req +** +** Description This is the main dispatcher of the SDP server. It is called +** when any data is received from L2CAP, and dispatches the +** request to the appropriate handler. +** +** Returns void +** +*******************************************************************************/ +void sdp_server_handle_client_req (tCONN_CB *p_ccb, BT_HDR *p_msg) +{ + UINT8 *p_req = (UINT8 *) (p_msg + 1) + p_msg->offset; + UINT8 *p_req_end = p_req + p_msg->len; + UINT8 pdu_id; + UINT16 trans_num, param_len; + + + /* Start inactivity timer */ + btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_SDP, SDP_INACT_TIMEOUT); + + /* The first byte in the message is the pdu type */ + pdu_id = *p_req++; + + /* Extract the transaction number and parameter length */ + BE_STREAM_TO_UINT16 (trans_num, p_req); + BE_STREAM_TO_UINT16 (param_len, p_req); + + if ((p_req + param_len) != p_req_end) { + sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_PDU_SIZE, SDP_TEXT_BAD_HEADER); + return; + } + + switch (pdu_id) { + case SDP_PDU_SERVICE_SEARCH_REQ: + process_service_search (p_ccb, trans_num, param_len, p_req, p_req_end); + break; + + case SDP_PDU_SERVICE_ATTR_REQ: + process_service_attr_req (p_ccb, trans_num, param_len, p_req, p_req_end); + break; + + case SDP_PDU_SERVICE_SEARCH_ATTR_REQ: + process_service_search_attr_req (p_ccb, trans_num, param_len, p_req, p_req_end); + break; + + default: + sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_REQ_SYNTAX, SDP_TEXT_BAD_PDU); + SDP_TRACE_WARNING ("SDP - server got unknown PDU: 0x%x\n", pdu_id); + break; + } +} + + + +/******************************************************************************* +** +** Function process_service_search +** +** Description This function handles a service search request from the +** client. It builds a reply message with info from the database, +** and sends the reply back to the client. +** +** Returns void +** +*******************************************************************************/ +static void process_service_search (tCONN_CB *p_ccb, UINT16 trans_num, + UINT16 param_len, UINT8 *p_req, + UINT8 *p_req_end) +{ + UINT16 max_replies, cur_handles, rem_handles, cont_offset; + tSDP_UUID_SEQ uid_seq; + UINT8 *p_rsp, *p_rsp_start, *p_rsp_param_len; + UINT16 rsp_param_len, num_rsp_handles, xx; + UINT32 rsp_handles[SDP_MAX_RECORDS] = {0}; + tSDP_RECORD *p_rec = NULL; + BT_HDR *p_buf; + BOOLEAN is_cont = FALSE; + UNUSED(p_req_end); + + p_req = sdpu_extract_uid_seq (p_req, param_len, &uid_seq); + + if ((!p_req) || (!uid_seq.num_uids)) { + sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_REQ_SYNTAX, SDP_TEXT_BAD_UUID_LIST); + return; + } + + /* Get the max replies we can send. Cap it at our max anyways. */ + BE_STREAM_TO_UINT16 (max_replies, p_req); + + if (max_replies > SDP_MAX_RECORDS) { + max_replies = SDP_MAX_RECORDS; + } + + + if ((!p_req) || (p_req > p_req_end)) { + sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_REQ_SYNTAX, SDP_TEXT_BAD_MAX_RECORDS_LIST); + return; + } + + + /* Get a list of handles that match the UUIDs given to us */ + for (num_rsp_handles = 0; num_rsp_handles < max_replies; ) { + p_rec = sdp_db_service_search (p_rec, &uid_seq); + + if (p_rec) { + rsp_handles[num_rsp_handles++] = p_rec->record_handle; + } else { + break; + } + } + + /* Check if this is a continuation request */ + if (*p_req) { + if (*p_req++ != SDP_CONTINUATION_LEN || (p_req >= p_req_end)) { + sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_CONT_STATE, + SDP_TEXT_BAD_CONT_LEN); + return; + } + BE_STREAM_TO_UINT16 (cont_offset, p_req); + + if (cont_offset != p_ccb->cont_offset || num_rsp_handles < cont_offset) { + sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_CONT_STATE, + SDP_TEXT_BAD_CONT_INX); + return; + } + + rem_handles = num_rsp_handles - cont_offset; /* extract the remaining handles */ + } else { + rem_handles = num_rsp_handles; + cont_offset = 0; + p_ccb->cont_offset = 0; + } + + /* Calculate how many handles will fit in one PDU */ + cur_handles = (UINT16)((p_ccb->rem_mtu_size - SDP_MAX_SERVICE_RSPHDR_LEN) / 4); + + if (rem_handles <= cur_handles) { + cur_handles = rem_handles; + } else { /* Continuation is set */ + p_ccb->cont_offset += cur_handles; + is_cont = TRUE; + } + + /* Get a buffer to use to build the response */ + if ((p_buf = (BT_HDR *)osi_malloc(SDP_DATA_BUF_SIZE)) == NULL) { + SDP_TRACE_ERROR ("SDP - no buf for search rsp\n"); + return; + } + p_buf->offset = L2CAP_MIN_OFFSET; + p_rsp = p_rsp_start = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET; + + /* Start building a rsponse */ + UINT8_TO_BE_STREAM (p_rsp, SDP_PDU_SERVICE_SEARCH_RSP); + UINT16_TO_BE_STREAM (p_rsp, trans_num); + + /* Skip the length, we need to add it at the end */ + p_rsp_param_len = p_rsp; + p_rsp += 2; + + /* Put in total and current number of handles, and handles themselves */ + UINT16_TO_BE_STREAM (p_rsp, num_rsp_handles); + UINT16_TO_BE_STREAM (p_rsp, cur_handles); + + /* SDP_TRACE_DEBUG("SDP Service Rsp: tothdl %d, curhdlr %d, start %d, end %d, cont %d", + num_rsp_handles, cur_handles, cont_offset, + cont_offset + cur_handles-1, is_cont); */ + for (xx = cont_offset; xx < cont_offset + cur_handles; xx++) { + UINT32_TO_BE_STREAM (p_rsp, rsp_handles[xx]); + } + + if (is_cont) { + UINT8_TO_BE_STREAM (p_rsp, SDP_CONTINUATION_LEN); + UINT16_TO_BE_STREAM (p_rsp, p_ccb->cont_offset); + } else { + UINT8_TO_BE_STREAM (p_rsp, 0); + } + + /* Go back and put the parameter length into the buffer */ + rsp_param_len = p_rsp - p_rsp_param_len - 2; + UINT16_TO_BE_STREAM (p_rsp_param_len, rsp_param_len); + + /* Set the length of the SDP data in the buffer */ + p_buf->len = p_rsp - p_rsp_start; + + + /* Send the buffer through L2CAP */ + L2CA_DataWrite (p_ccb->connection_id, p_buf); +} + + +/******************************************************************************* +** +** Function process_service_attr_req +** +** Description This function handles an attribute request from the client. +** It builds a reply message with info from the database, +** and sends the reply back to the client. +** +** Returns void +** +*******************************************************************************/ +static void process_service_attr_req (tCONN_CB *p_ccb, UINT16 trans_num, + UINT16 param_len, UINT8 *p_req, + UINT8 *p_req_end) +{ + UINT16 max_list_len, len_to_send, cont_offset; + INT16 rem_len; + tSDP_ATTR_SEQ attr_seq, attr_seq_sav; + UINT8 *p_rsp, *p_rsp_start, *p_rsp_param_len; + UINT16 rsp_param_len, xx; + UINT32 rec_handle; + tSDP_RECORD *p_rec; + tSDP_ATTRIBUTE *p_attr; + BT_HDR *p_buf; + BOOLEAN is_cont = FALSE; + UINT16 attr_len; + + /* Extract the record handle */ + BE_STREAM_TO_UINT32 (rec_handle, p_req); + + if (p_req > p_req_end) { + sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_SERV_REC_HDL, SDP_TEXT_BAD_HANDLE); + return; + } + + /* Get the max list length we can send. Cap it at MTU size minus overhead */ + BE_STREAM_TO_UINT16 (max_list_len, p_req); + + if (max_list_len > (p_ccb->rem_mtu_size - SDP_MAX_ATTR_RSPHDR_LEN)) { + max_list_len = p_ccb->rem_mtu_size - SDP_MAX_ATTR_RSPHDR_LEN; + } + + p_req = sdpu_extract_attr_seq (p_req, param_len, &attr_seq); + + if ((!p_req) || (!attr_seq.num_attr) || (p_req > p_req_end)) { + sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_REQ_SYNTAX, SDP_TEXT_BAD_ATTR_LIST); + return; + } + + memcpy(&attr_seq_sav, &attr_seq, sizeof(tSDP_ATTR_SEQ)) ; + + /* Find a record with the record handle */ + p_rec = sdp_db_find_record (rec_handle); + if (!p_rec) { + sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_SERV_REC_HDL, SDP_TEXT_BAD_HANDLE); + return; + } + + /* Check if this is a continuation request */ + if (*p_req) { + /* Free and reallocate buffer */ + if (p_ccb->rsp_list) { + osi_free(p_ccb->rsp_list); + } + + p_ccb->rsp_list = (UINT8 *)osi_malloc(max_list_len); + if (p_ccb->rsp_list == NULL) { + SDP_TRACE_ERROR("%s No scratch buf for attr rsp\n", __func__); + return; + } + + if (*p_req++ != SDP_CONTINUATION_LEN) { + sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_CONT_STATE, SDP_TEXT_BAD_CONT_LEN); + return; + } + BE_STREAM_TO_UINT16 (cont_offset, p_req); + + if (cont_offset != p_ccb->cont_offset) { + sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_CONT_STATE, SDP_TEXT_BAD_CONT_INX); + return; + } + + if (!p_ccb->rsp_list) { + sdpu_build_n_send_error (p_ccb, trans_num, SDP_NO_RESOURCES, NULL); + return; + } + is_cont = TRUE; + + /* Initialise for continuation response */ + p_rsp = &p_ccb->rsp_list[0]; + attr_seq.attr_entry[p_ccb->cont_info.next_attr_index].start = p_ccb->cont_info.next_attr_start_id; + } else { + if (p_ccb->rsp_list) { + osi_free (p_ccb->rsp_list); + } + + p_ccb->rsp_list = (UINT8 *)osi_malloc (max_list_len); + if (p_ccb->rsp_list == NULL) { + SDP_TRACE_ERROR ("SDP - no scratch buf for search rsp\n"); + return; + } + + p_ccb->cont_offset = 0; + p_rsp = &p_ccb->rsp_list[3]; /* Leave space for data elem descr */ + + /* Reset continuation parameters in p_ccb */ + p_ccb->cont_info.prev_sdp_rec = NULL; + p_ccb->cont_info.next_attr_index = 0; + p_ccb->cont_info.attr_offset = 0; + } + + /* Search for attributes that match the list given to us */ + for (xx = p_ccb->cont_info.next_attr_index; xx < attr_seq.num_attr; xx++) { + p_attr = sdp_db_find_attr_in_rec (p_rec, attr_seq.attr_entry[xx].start, attr_seq.attr_entry[xx].end); + + if (p_attr) { + /* Check if attribute fits. Assume 3-byte value type/length */ + rem_len = max_list_len - (INT16) (p_rsp - &p_ccb->rsp_list[0]); + + /* just in case */ + if (rem_len <= 0) { + p_ccb->cont_info.next_attr_index = xx; + p_ccb->cont_info.next_attr_start_id = p_attr->id; + break; + } + + attr_len = sdpu_get_attrib_entry_len(p_attr); + /* if there is a partial attribute pending to be sent */ + if (p_ccb->cont_info.attr_offset) { + p_rsp = sdpu_build_partial_attrib_entry (p_rsp, p_attr, rem_len, + &p_ccb->cont_info.attr_offset); + + /* If the partial attrib could not been fully added yet */ + if (p_ccb->cont_info.attr_offset != attr_len) { + break; + } else { /* If the partial attrib has been added in full by now */ + p_ccb->cont_info.attr_offset = 0; /* reset attr_offset */ + } + } else if (rem_len < attr_len) { /* Not enough space for attr... so add partially */ + if (attr_len >= SDP_MAX_ATTR_LEN) { + SDP_TRACE_ERROR("SDP attr too big: max_list_len=%d,attr_len=%d\n", max_list_len, attr_len); + sdpu_build_n_send_error (p_ccb, trans_num, SDP_NO_RESOURCES, NULL); + return; + } + + /* add the partial attribute if possible */ + p_rsp = sdpu_build_partial_attrib_entry (p_rsp, p_attr, (UINT16)rem_len, + &p_ccb->cont_info.attr_offset); + + p_ccb->cont_info.next_attr_index = xx; + p_ccb->cont_info.next_attr_start_id = p_attr->id; + break; + } else { /* build the whole attribute */ + p_rsp = sdpu_build_attrib_entry (p_rsp, p_attr); + } + + /* If doing a range, stick with this one till no more attributes found */ + if (attr_seq.attr_entry[xx].start != attr_seq.attr_entry[xx].end) { + /* Update for next time through */ + attr_seq.attr_entry[xx].start = p_attr->id + 1; + + xx--; + } + } + } + /* If all the attributes have been accomodated in p_rsp, + reset next_attr_index */ + if (xx == attr_seq.num_attr) { + p_ccb->cont_info.next_attr_index = 0; + } + + len_to_send = (UINT16) (p_rsp - &p_ccb->rsp_list[0]); + cont_offset = 0; + + if (!is_cont) { + p_ccb->list_len = sdpu_get_attrib_seq_len(p_rec, &attr_seq_sav) + 3; + /* Put in the sequence header (2 or 3 bytes) */ + if (p_ccb->list_len > 255) { + p_ccb->rsp_list[0] = (UINT8) ((DATA_ELE_SEQ_DESC_TYPE << 3) | SIZE_IN_NEXT_WORD); + p_ccb->rsp_list[1] = (UINT8) ((p_ccb->list_len - 3) >> 8); + p_ccb->rsp_list[2] = (UINT8) (p_ccb->list_len - 3); + } else { + cont_offset = 1; + + p_ccb->rsp_list[1] = (UINT8) ((DATA_ELE_SEQ_DESC_TYPE << 3) | SIZE_IN_NEXT_BYTE); + p_ccb->rsp_list[2] = (UINT8) (p_ccb->list_len - 3); + + p_ccb->list_len--; + len_to_send--; + } + } + + /* Get a buffer to use to build the response */ + if ((p_buf = (BT_HDR *)osi_malloc(SDP_DATA_BUF_SIZE)) == NULL) { + SDP_TRACE_ERROR ("SDP - no buf for search rsp\n"); + return; + } + p_buf->offset = L2CAP_MIN_OFFSET; + p_rsp = p_rsp_start = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET; + + /* Start building a rsponse */ + UINT8_TO_BE_STREAM (p_rsp, SDP_PDU_SERVICE_ATTR_RSP); + UINT16_TO_BE_STREAM (p_rsp, trans_num); + + /* Skip the parameter length, add it when we know the length */ + p_rsp_param_len = p_rsp; + p_rsp += 2; + + UINT16_TO_BE_STREAM (p_rsp, len_to_send); + + memcpy (p_rsp, &p_ccb->rsp_list[cont_offset], len_to_send); + p_rsp += len_to_send; + + p_ccb->cont_offset += len_to_send; + + /* If anything left to send, continuation needed */ + if (p_ccb->cont_offset < p_ccb->list_len) { + is_cont = TRUE; + + UINT8_TO_BE_STREAM (p_rsp, SDP_CONTINUATION_LEN); + UINT16_TO_BE_STREAM (p_rsp, p_ccb->cont_offset); + } else { + UINT8_TO_BE_STREAM (p_rsp, 0); + } + + /* Go back and put the parameter length into the buffer */ + rsp_param_len = p_rsp - p_rsp_param_len - 2; + UINT16_TO_BE_STREAM (p_rsp_param_len, rsp_param_len); + + /* Set the length of the SDP data in the buffer */ + p_buf->len = p_rsp - p_rsp_start; + + + /* Send the buffer through L2CAP */ + L2CA_DataWrite (p_ccb->connection_id, p_buf); +} + + + +/******************************************************************************* +** +** Function process_service_search_attr_req +** +** Description This function handles a combined service search and attribute +** read request from the client. It builds a reply message with +** info from the database, and sends the reply back to the client. +** +** Returns void +** +*******************************************************************************/ +static void process_service_search_attr_req (tCONN_CB *p_ccb, UINT16 trans_num, + UINT16 param_len, UINT8 *p_req, + UINT8 *p_req_end) +{ + UINT16 max_list_len; + INT16 rem_len; + UINT16 len_to_send, cont_offset; + tSDP_UUID_SEQ uid_seq; + UINT8 *p_rsp, *p_rsp_start, *p_rsp_param_len; + UINT16 rsp_param_len, xx; + tSDP_RECORD *p_rec; + tSDP_ATTR_SEQ attr_seq, attr_seq_sav; + tSDP_ATTRIBUTE *p_attr; + BT_HDR *p_buf; + BOOLEAN maxxed_out = FALSE, is_cont = FALSE; + UINT8 *p_seq_start; + UINT16 seq_len, attr_len; + UNUSED(p_req_end); + + /* Extract the UUID sequence to search for */ + p_req = sdpu_extract_uid_seq (p_req, param_len, &uid_seq); + + if ((!p_req) || (!uid_seq.num_uids)) { + sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_REQ_SYNTAX, SDP_TEXT_BAD_UUID_LIST); + return; + } + + /* Get the max list length we can send. Cap it at our max list length. */ + BE_STREAM_TO_UINT16 (max_list_len, p_req); + + if (max_list_len > (p_ccb->rem_mtu_size - SDP_MAX_SERVATTR_RSPHDR_LEN)) { + max_list_len = p_ccb->rem_mtu_size - SDP_MAX_SERVATTR_RSPHDR_LEN; + } + + p_req = sdpu_extract_attr_seq (p_req, param_len, &attr_seq); + + if ((!p_req) || (!attr_seq.num_attr)) { + sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_REQ_SYNTAX, SDP_TEXT_BAD_ATTR_LIST); + return; + } + + memcpy(&attr_seq_sav, &attr_seq, sizeof(tSDP_ATTR_SEQ)) ; + + /* Check if this is a continuation request */ + if (*p_req) { + /* Free and reallocate buffer */ + if (p_ccb->rsp_list) { + osi_free (p_ccb->rsp_list); + } + + p_ccb->rsp_list = (UINT8 *)osi_malloc (max_list_len); + if (p_ccb->rsp_list == NULL) { + SDP_TRACE_ERROR ("SDP - no scratch buf for search rsp\n"); + return; + } + + if (*p_req++ != SDP_CONTINUATION_LEN) { + sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_CONT_STATE, SDP_TEXT_BAD_CONT_LEN); + return; + } + BE_STREAM_TO_UINT16 (cont_offset, p_req); + + if (cont_offset != p_ccb->cont_offset) { + sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_CONT_STATE, SDP_TEXT_BAD_CONT_INX); + return; + } + + if (!p_ccb->rsp_list) { + sdpu_build_n_send_error (p_ccb, trans_num, SDP_NO_RESOURCES, NULL); + return; + } + is_cont = TRUE; + + /* Initialise for continuation response */ + p_rsp = &p_ccb->rsp_list[0]; + attr_seq.attr_entry[p_ccb->cont_info.next_attr_index].start = p_ccb->cont_info.next_attr_start_id; + } else { + /* Get a scratch buffer to store response */ + /* Free and reallocate if the earlier allocated buffer is small */ + if (p_ccb->rsp_list) { + osi_free (p_ccb->rsp_list); + } + + p_ccb->rsp_list = (UINT8 *)osi_malloc (max_list_len); + if (p_ccb->rsp_list == NULL) { + SDP_TRACE_ERROR ("SDP - no scratch buf for search rsp\n"); + return; + } + + p_ccb->cont_offset = 0; + p_rsp = &p_ccb->rsp_list[3]; /* Leave space for data elem descr */ + + /* Reset continuation parameters in p_ccb */ + p_ccb->cont_info.prev_sdp_rec = NULL; + p_ccb->cont_info.next_attr_index = 0; + p_ccb->cont_info.last_attr_seq_desc_sent = FALSE; + p_ccb->cont_info.attr_offset = 0; + } + + /* Get a list of handles that match the UUIDs given to us */ + for (p_rec = sdp_db_service_search (p_ccb->cont_info.prev_sdp_rec, &uid_seq); p_rec; p_rec = sdp_db_service_search (p_rec, &uid_seq)) { + /* Allow space for attribute sequence type and length */ + p_seq_start = p_rsp; + if (p_ccb->cont_info.last_attr_seq_desc_sent == FALSE) { + /* See if there is enough room to include a new service in the current response */ + rem_len = max_list_len - (INT16) (p_rsp - &p_ccb->rsp_list[0]); + if (rem_len < 3) { + /* Not enough room. Update continuation info for next response */ + p_ccb->cont_info.next_attr_index = 0; + p_ccb->cont_info.next_attr_start_id = attr_seq.attr_entry[0].start; + break; + } + p_rsp += 3; + } + + /* Get a list of handles that match the UUIDs given to us */ + for (xx = p_ccb->cont_info.next_attr_index; xx < attr_seq.num_attr; xx++) { + p_attr = sdp_db_find_attr_in_rec (p_rec, attr_seq.attr_entry[xx].start, attr_seq.attr_entry[xx].end); + + if (p_attr) { + /* Check if attribute fits. Assume 3-byte value type/length */ + rem_len = max_list_len - (INT16) (p_rsp - &p_ccb->rsp_list[0]); + + /* just in case */ + if (rem_len <= 0) { + p_ccb->cont_info.next_attr_index = xx; + p_ccb->cont_info.next_attr_start_id = p_attr->id; + maxxed_out = TRUE; + break; + } + + attr_len = sdpu_get_attrib_entry_len(p_attr); + /* if there is a partial attribute pending to be sent */ + if (p_ccb->cont_info.attr_offset) { + p_rsp = sdpu_build_partial_attrib_entry (p_rsp, p_attr, rem_len, + &p_ccb->cont_info.attr_offset); + + /* If the partial attrib could not been fully added yet */ + if (p_ccb->cont_info.attr_offset != attr_len) { + maxxed_out = TRUE; + break; + } else { /* If the partial attrib has been added in full by now */ + p_ccb->cont_info.attr_offset = 0; /* reset attr_offset */ + } + } else if (rem_len < attr_len) { /* Not enough space for attr... so add partially */ + if (attr_len >= SDP_MAX_ATTR_LEN) { + SDP_TRACE_ERROR("SDP attr too big: max_list_len=%d,attr_len=%d\n", max_list_len, attr_len); + sdpu_build_n_send_error (p_ccb, trans_num, SDP_NO_RESOURCES, NULL); + return; + } + + /* add the partial attribute if possible */ + p_rsp = sdpu_build_partial_attrib_entry (p_rsp, p_attr, (UINT16)rem_len, + &p_ccb->cont_info.attr_offset); + + p_ccb->cont_info.next_attr_index = xx; + p_ccb->cont_info.next_attr_start_id = p_attr->id; + maxxed_out = TRUE; + break; + } else { /* build the whole attribute */ + p_rsp = sdpu_build_attrib_entry (p_rsp, p_attr); + } + + /* If doing a range, stick with this one till no more attributes found */ + if (attr_seq.attr_entry[xx].start != attr_seq.attr_entry[xx].end) { + /* Update for next time through */ + attr_seq.attr_entry[xx].start = p_attr->id + 1; + + xx--; + } + } + } + + /* Go back and put the type and length into the buffer */ + if (p_ccb->cont_info.last_attr_seq_desc_sent == FALSE) { + seq_len = sdpu_get_attrib_seq_len(p_rec, &attr_seq_sav); + if (seq_len != 0) { + UINT8_TO_BE_STREAM (p_seq_start, (DATA_ELE_SEQ_DESC_TYPE << 3) | SIZE_IN_NEXT_WORD); + UINT16_TO_BE_STREAM (p_seq_start, seq_len); + + if (maxxed_out) { + p_ccb->cont_info.last_attr_seq_desc_sent = TRUE; + } + } else { + p_rsp = p_seq_start; + } + } + + if (maxxed_out) { + break; + } + + /* Restore the attr_seq to look for in the next sdp record */ + memcpy(&attr_seq, &attr_seq_sav, sizeof(tSDP_ATTR_SEQ)) ; + + /* Reset the next attr index */ + p_ccb->cont_info.next_attr_index = 0; + p_ccb->cont_info.prev_sdp_rec = p_rec; + p_ccb->cont_info.last_attr_seq_desc_sent = FALSE; + } + + /* response length */ + len_to_send = (UINT16) (p_rsp - &p_ccb->rsp_list[0]); + cont_offset = 0; + + // The current SDP server design has a critical flaw where it can run into an infinite + // request/response loop with the client. Here's the scenario: + // - client makes SDP request + // - server returns the first fragment of the response with a continuation token + // - an SDP record is deleted from the server + // - client issues another request with previous continuation token + // - server has nothing to send back because the record is unavailable but in the + // first fragment, it had specified more response bytes than are now available + // - server sends back no additional response bytes and returns the same continuation token + // - client issues another request with the continuation token, and the process repeats + // + // We work around this design flaw here by checking if we will make forward progress + // (i.e. we will send > 0 response bytes) on a continued request. If not, we must have + // run into the above situation and we tell the peer an error occurred. + // + // TODO(sharvil): rewrite SDP server. + if (is_cont && len_to_send == 0) { + sdpu_build_n_send_error(p_ccb, trans_num, SDP_INVALID_CONT_STATE, NULL); + return; + } + + /* If first response, insert sequence header */ + if (!is_cont) { + /* Get the total list length for requested uid and attribute sequence */ + p_ccb->list_len = sdpu_get_list_len(&uid_seq, &attr_seq_sav) + 3; + /* Put in the sequence header (2 or 3 bytes) */ + if (p_ccb->list_len > 255) { + p_ccb->rsp_list[0] = (UINT8) ((DATA_ELE_SEQ_DESC_TYPE << 3) | SIZE_IN_NEXT_WORD); + p_ccb->rsp_list[1] = (UINT8) ((p_ccb->list_len - 3) >> 8); + p_ccb->rsp_list[2] = (UINT8) (p_ccb->list_len - 3); + } else { + cont_offset = 1; + + p_ccb->rsp_list[1] = (UINT8) ((DATA_ELE_SEQ_DESC_TYPE << 3) | SIZE_IN_NEXT_BYTE); + p_ccb->rsp_list[2] = (UINT8) (p_ccb->list_len - 3); + + p_ccb->list_len--; + len_to_send--; + } + } + + /* Get a buffer to use to build the response */ + if ((p_buf = (BT_HDR *)osi_malloc(SDP_DATA_BUF_SIZE)) == NULL) { + SDP_TRACE_ERROR ("SDP - no buf for search rsp\n"); + return; + } + p_buf->offset = L2CAP_MIN_OFFSET; + p_rsp = p_rsp_start = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET; + + /* Start building a rsponse */ + UINT8_TO_BE_STREAM (p_rsp, SDP_PDU_SERVICE_SEARCH_ATTR_RSP); + UINT16_TO_BE_STREAM (p_rsp, trans_num); + + /* Skip the parameter length, add it when we know the length */ + p_rsp_param_len = p_rsp; + p_rsp += 2; + + /* Stream the list length to send */ + UINT16_TO_BE_STREAM (p_rsp, len_to_send); + + /* copy from rsp_list to the actual buffer to be sent */ + memcpy (p_rsp, &p_ccb->rsp_list[cont_offset], len_to_send); + p_rsp += len_to_send; + + p_ccb->cont_offset += len_to_send; + + /* If anything left to send, continuation needed */ + if (p_ccb->cont_offset < p_ccb->list_len) { + is_cont = TRUE; + + UINT8_TO_BE_STREAM (p_rsp, SDP_CONTINUATION_LEN); + UINT16_TO_BE_STREAM (p_rsp, p_ccb->cont_offset); + } else { + UINT8_TO_BE_STREAM (p_rsp, 0); + } + + /* Go back and put the parameter length into the buffer */ + rsp_param_len = p_rsp - p_rsp_param_len - 2; + UINT16_TO_BE_STREAM (p_rsp_param_len, rsp_param_len); + + /* Set the length of the SDP data in the buffer */ + p_buf->len = p_rsp - p_rsp_start; + + + /* Send the buffer through L2CAP */ + L2CA_DataWrite (p_ccb->connection_id, p_buf); +} + +#endif /* SDP_SERVER_ENABLED == TRUE */ diff --git a/lib/bt/host/bluedroid/stack/sdp/sdp_utils.c b/lib/bt/host/bluedroid/stack/sdp/sdp_utils.c new file mode 100644 index 00000000..cd14158d --- /dev/null +++ b/lib/bt/host/bluedroid/stack/sdp/sdp_utils.c @@ -0,0 +1,1030 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains SDP utility functions + * + ******************************************************************************/ + +#include +#include + +#include "osi/allocator.h" + +#include "common/bt_defs.h" + +#include "stack/bt_types.h" + +#include "stack/l2cdefs.h" +#include "stack/hcidefs.h" +#include "stack/hcimsgs.h" + +#include "stack/sdp_api.h" +#include "sdpint.h" + +#include "stack/btu.h" + +#if (SDP_INCLUDED == TRUE) +static const UINT8 sdp_base_uuid[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, + 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB + }; + +/******************************************************************************* +** +** Function sdpu_find_ccb_by_cid +** +** Description This function searches the CCB table for an entry with the +** passed CID. +** +** Returns the CCB address, or NULL if not found. +** +*******************************************************************************/ +tCONN_CB *sdpu_find_ccb_by_cid (UINT16 cid) +{ + UINT16 xx; + tCONN_CB *p_ccb; + + /* Look through each connection control block */ + for (xx = 0, p_ccb = sdp_cb.ccb; xx < SDP_MAX_CONNECTIONS; xx++, p_ccb++) { + if ((p_ccb->con_state != SDP_STATE_IDLE) && (p_ccb->connection_id == cid)) { + return (p_ccb); + } + } + + /* If here, not found */ + return (NULL); +} + + +/******************************************************************************* +** +** Function sdpu_find_ccb_by_db +** +** Description This function searches the CCB table for an entry with the +** passed discovery db. +** +** Returns the CCB address, or NULL if not found. +** +*******************************************************************************/ +tCONN_CB *sdpu_find_ccb_by_db (tSDP_DISCOVERY_DB *p_db) +{ +#if SDP_CLIENT_ENABLED == TRUE + UINT16 xx; + tCONN_CB *p_ccb; + + if (p_db) { + /* Look through each connection control block */ + for (xx = 0, p_ccb = sdp_cb.ccb; xx < SDP_MAX_CONNECTIONS; xx++, p_ccb++) { + if ((p_ccb->con_state != SDP_STATE_IDLE) && (p_ccb->p_db == p_db)) { + return (p_ccb); + } + } + } +#endif + /* If here, not found */ + return (NULL); +} + + +/******************************************************************************* +** +** Function sdpu_allocate_ccb +** +** Description This function allocates a new CCB. +** +** Returns CCB address, or NULL if none available. +** +*******************************************************************************/ +tCONN_CB *sdpu_allocate_ccb (void) +{ + UINT16 xx; + tCONN_CB *p_ccb; + + /* Look through each connection control block for a free one */ + for (xx = 0, p_ccb = sdp_cb.ccb; xx < SDP_MAX_CONNECTIONS; xx++, p_ccb++) { + if (p_ccb->con_state == SDP_STATE_IDLE) { + btu_free_timer(&p_ccb->timer_entry); + memset (p_ccb, 0, sizeof (tCONN_CB)); + + p_ccb->timer_entry.param = (UINT32) p_ccb; + + return (p_ccb); + } + } + + /* If here, no free CCB found */ + return (NULL); +} + + +/******************************************************************************* +** +** Function sdpu_release_ccb +** +** Description This function releases a CCB. +** +** Returns void +** +*******************************************************************************/ +void sdpu_release_ccb (tCONN_CB *p_ccb) +{ + /* Ensure timer is stopped and released */ + btu_free_timer(&p_ccb->timer_entry); + + /* Drop any response pointer we may be holding */ + p_ccb->con_state = SDP_STATE_IDLE; +#if SDP_CLIENT_ENABLED == TRUE + p_ccb->is_attr_search = FALSE; +#endif + + /* Free the response buffer */ + if (p_ccb->rsp_list) { + SDP_TRACE_DEBUG("releasing SDP rsp_list\n"); + + osi_free(p_ccb->rsp_list); + p_ccb->rsp_list = NULL; + } +} + + +/******************************************************************************* +** +** Function sdpu_build_attrib_seq +** +** Description This function builds an attribute sequence from the list of +** passed attributes. It is also passed the address of the output +** buffer. +** +** Returns Pointer to next byte in the output buffer. +** +*******************************************************************************/ +UINT8 *sdpu_build_attrib_seq (UINT8 *p_out, UINT16 *p_attr, UINT16 num_attrs) +{ + UINT16 xx; + + /* First thing is the data element header. See if the length fits 1 byte */ + /* If no attributes, assume a 4-byte wildcard */ + if (!p_attr) { + xx = 5; + } else { + xx = num_attrs * 3; + } + + if (xx > 255) { + UINT8_TO_BE_STREAM (p_out, (DATA_ELE_SEQ_DESC_TYPE << 3) | SIZE_IN_NEXT_WORD); + UINT16_TO_BE_STREAM (p_out, xx); + } else { + UINT8_TO_BE_STREAM (p_out, (DATA_ELE_SEQ_DESC_TYPE << 3) | SIZE_IN_NEXT_BYTE); + UINT8_TO_BE_STREAM (p_out, xx); + } + + /* If there are no attributes specified, assume caller wants wildcard */ + if (!p_attr) { + UINT8_TO_BE_STREAM (p_out, (UINT_DESC_TYPE << 3) | SIZE_FOUR_BYTES); + UINT16_TO_BE_STREAM (p_out, 0); + UINT16_TO_BE_STREAM (p_out, 0xFFFF); + } else { + /* Loop through and put in all the attributes(s) */ + for (xx = 0; xx < num_attrs; xx++, p_attr++) { + UINT8_TO_BE_STREAM (p_out, (UINT_DESC_TYPE << 3) | SIZE_TWO_BYTES); + UINT16_TO_BE_STREAM (p_out, *p_attr); + } + } + + return (p_out); +} + + +/******************************************************************************* +** +** Function sdpu_build_attrib_entry +** +** Description This function builds an attribute entry from the passed +** attribute record. It is also passed the address of the output +** buffer. +** +** Returns Pointer to next byte in the output buffer. +** +*******************************************************************************/ +UINT8 *sdpu_build_attrib_entry (UINT8 *p_out, tSDP_ATTRIBUTE *p_attr) +{ + /* First, store the attribute ID. Goes as a UINT */ + UINT8_TO_BE_STREAM (p_out, (UINT_DESC_TYPE << 3) | SIZE_TWO_BYTES); + UINT16_TO_BE_STREAM (p_out, p_attr->id); + + /* the attribute is in the db record. + * assuming the attribute len is less than SDP_MAX_ATTR_LEN */ + switch (p_attr->type) { + case TEXT_STR_DESC_TYPE: /* 4 */ + case DATA_ELE_SEQ_DESC_TYPE:/* 6 */ + case DATA_ELE_ALT_DESC_TYPE:/* 7 */ + case URL_DESC_TYPE: /* 8 */ +#if (SDP_MAX_ATTR_LEN > 0xFFFF) + if (p_attr->len > 0xFFFF) { + UINT8_TO_BE_STREAM (p_out, (p_attr->type << 3) | SIZE_IN_NEXT_LONG); + UINT32_TO_BE_STREAM (p_out, p_attr->len); + } else + +#endif /* 0xFFFF - 0xFF */ + { +#if (SDP_MAX_ATTR_LEN > 0xFF) + if (p_attr->len > 0xFF) { + UINT8_TO_BE_STREAM (p_out, (p_attr->type << 3) | SIZE_IN_NEXT_WORD); + UINT16_TO_BE_STREAM (p_out, p_attr->len); + } else + +#endif /* 0xFF and less*/ + { + UINT8_TO_BE_STREAM (p_out, (p_attr->type << 3) | SIZE_IN_NEXT_BYTE); + UINT8_TO_BE_STREAM (p_out, p_attr->len); + } + } + if (p_attr->value_ptr != NULL) { + ARRAY_TO_BE_STREAM (p_out, p_attr->value_ptr, (int)p_attr->len); + } + + return (p_out); + } + + /* Now, store the attribute value */ + switch (p_attr->len) { + case 1: + UINT8_TO_BE_STREAM (p_out, (p_attr->type << 3) | SIZE_ONE_BYTE); + break; + case 2: + UINT8_TO_BE_STREAM (p_out, (p_attr->type << 3) | SIZE_TWO_BYTES); + break; + case 4: + UINT8_TO_BE_STREAM (p_out, (p_attr->type << 3) | SIZE_FOUR_BYTES); + break; + case 8: + UINT8_TO_BE_STREAM (p_out, (p_attr->type << 3) | SIZE_EIGHT_BYTES); + break; + case 16: + UINT8_TO_BE_STREAM (p_out, (p_attr->type << 3) | SIZE_SIXTEEN_BYTES); + break; + default: + UINT8_TO_BE_STREAM (p_out, (p_attr->type << 3) | SIZE_IN_NEXT_BYTE); + UINT8_TO_BE_STREAM (p_out, p_attr->len); + break; + } + + if (p_attr->value_ptr != NULL) { + ARRAY_TO_BE_STREAM (p_out, p_attr->value_ptr, (int)p_attr->len); + } + + return (p_out); +} + + +/******************************************************************************* +** +** Function sdpu_build_n_send_error +** +** Description This function builds and sends an error packet. +** +** Returns void +** +*******************************************************************************/ +void sdpu_build_n_send_error (tCONN_CB *p_ccb, UINT16 trans_num, UINT16 error_code, char *p_error_text) +{ + UINT8 *p_rsp, *p_rsp_start, *p_rsp_param_len; + UINT16 rsp_param_len; + BT_HDR *p_buf; + + + SDP_TRACE_WARNING ("SDP - sdpu_build_n_send_error code: 0x%x CID: 0x%x\n", + error_code, p_ccb->connection_id); + + /* Get a buffer to use to build and send the packet to L2CAP */ + if ((p_buf = (BT_HDR *)osi_malloc(SDP_DATA_BUF_SIZE)) == NULL) { + SDP_TRACE_ERROR ("SDP - no buf for err msg\n"); + return; + } + p_buf->offset = L2CAP_MIN_OFFSET; + p_rsp = p_rsp_start = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET; + + UINT8_TO_BE_STREAM (p_rsp, SDP_PDU_ERROR_RESPONSE); + UINT16_TO_BE_STREAM (p_rsp, trans_num); + + /* Skip the parameter length, we need to add it at the end */ + p_rsp_param_len = p_rsp; + p_rsp += 2; + + UINT16_TO_BE_STREAM (p_rsp, error_code); + + /* Unplugfest example traces do not have any error text */ + if (p_error_text) { + ARRAY_TO_BE_STREAM (p_rsp, p_error_text, (int) strlen (p_error_text)); + } + + /* Go back and put the parameter length into the buffer */ + rsp_param_len = p_rsp - p_rsp_param_len - 2; + UINT16_TO_BE_STREAM (p_rsp_param_len, rsp_param_len); + + /* Set the length of the SDP data in the buffer */ + p_buf->len = p_rsp - p_rsp_start; + + + /* Send the buffer through L2CAP */ + L2CA_DataWrite (p_ccb->connection_id, p_buf); +} + + + +/******************************************************************************* +** +** Function sdpu_extract_uid_seq +** +** Description This function extracts a UUID sequence from the passed input +** buffer, and puts it into the passed output list. +** +** Returns Pointer to next byte in the input buffer after the sequence. +** +*******************************************************************************/ +UINT8 *sdpu_extract_uid_seq (UINT8 *p, UINT16 param_len, tSDP_UUID_SEQ *p_seq) +{ + UINT8 *p_seq_end; + UINT8 descr, type, size; + UINT32 seq_len, uuid_len; + + /* Assume none found */ + p_seq->num_uids = 0; + + /* A UID sequence is composed of a bunch of UIDs. */ + + BE_STREAM_TO_UINT8 (descr, p); + type = descr >> 3; + size = descr & 7; + + if (type != DATA_ELE_SEQ_DESC_TYPE) { + return (NULL); + } + + switch (size) { + case SIZE_TWO_BYTES: + seq_len = 2; + break; + case SIZE_FOUR_BYTES: + seq_len = 4; + break; + case SIZE_SIXTEEN_BYTES: + seq_len = 16; + break; + case SIZE_IN_NEXT_BYTE: + BE_STREAM_TO_UINT8 (seq_len, p); + break; + case SIZE_IN_NEXT_WORD: + BE_STREAM_TO_UINT16 (seq_len, p); + break; + case SIZE_IN_NEXT_LONG: + BE_STREAM_TO_UINT32 (seq_len, p); + break; + default: + return (NULL); + } + + if (seq_len >= param_len) { + return (NULL); + } + + p_seq_end = p + seq_len; + + /* Loop through, extracting the UIDs */ + for ( ; p < p_seq_end ; ) { + BE_STREAM_TO_UINT8 (descr, p); + type = descr >> 3; + size = descr & 7; + + if (type != UUID_DESC_TYPE) { + return (NULL); + } + + switch (size) { + case SIZE_TWO_BYTES: + uuid_len = 2; + break; + case SIZE_FOUR_BYTES: + uuid_len = 4; + break; + case SIZE_SIXTEEN_BYTES: + uuid_len = 16; + break; + case SIZE_IN_NEXT_BYTE: + BE_STREAM_TO_UINT8 (uuid_len, p); + break; + case SIZE_IN_NEXT_WORD: + BE_STREAM_TO_UINT16 (uuid_len, p); + break; + case SIZE_IN_NEXT_LONG: + BE_STREAM_TO_UINT32 (uuid_len, p); + break; + default: + return (NULL); + } + + /* If UUID length is valid, copy it across */ + if ((uuid_len == 2) || (uuid_len == 4) || (uuid_len == 16)) { + p_seq->uuid_entry[p_seq->num_uids].len = (UINT16) uuid_len; + BE_STREAM_TO_ARRAY (p, p_seq->uuid_entry[p_seq->num_uids].value, (int)uuid_len); + p_seq->num_uids++; + } else { + return (NULL); + } + + /* We can only do so many */ + if (p_seq->num_uids >= MAX_UUIDS_PER_SEQ) { + return (NULL); + } + } + + if (p != p_seq_end) { + return (NULL); + } + + return (p); +} + + + +/******************************************************************************* +** +** Function sdpu_extract_attr_seq +** +** Description This function extracts an attribute sequence from the passed +** input buffer, and puts it into the passed output list. +** +** Returns Pointer to next byte in the input buffer after the sequence. +** +*******************************************************************************/ +UINT8 *sdpu_extract_attr_seq (UINT8 *p, UINT16 param_len, tSDP_ATTR_SEQ *p_seq) +{ + UINT8 *p_end_list; + UINT8 descr, type, size; + UINT32 list_len, attr_len; + + /* Assume none found */ + p_seq->num_attr = 0; + + /* Get attribute sequence info */ + BE_STREAM_TO_UINT8 (descr, p); + type = descr >> 3; + size = descr & 7; + + if (type != DATA_ELE_SEQ_DESC_TYPE) { + return (p); + } + + switch (size) { + case SIZE_IN_NEXT_BYTE: + BE_STREAM_TO_UINT8 (list_len, p); + break; + + case SIZE_IN_NEXT_WORD: + BE_STREAM_TO_UINT16 (list_len, p); + break; + + case SIZE_IN_NEXT_LONG: + BE_STREAM_TO_UINT32 (list_len, p); + break; + + default: + return (p); + } + + if (list_len > param_len) { + return (p); + } + + p_end_list = p + list_len; + + /* Loop through, extracting the attribute IDs */ + for ( ; p < p_end_list ; ) { + BE_STREAM_TO_UINT8 (descr, p); + type = descr >> 3; + size = descr & 7; + + if (type != UINT_DESC_TYPE) { + return (p); + } + + switch (size) { + case SIZE_TWO_BYTES: + attr_len = 2; + break; + case SIZE_FOUR_BYTES: + attr_len = 4; + break; + case SIZE_IN_NEXT_BYTE: + BE_STREAM_TO_UINT8 (attr_len, p); + break; + case SIZE_IN_NEXT_WORD: + BE_STREAM_TO_UINT16 (attr_len, p); + break; + case SIZE_IN_NEXT_LONG: + BE_STREAM_TO_UINT32 (attr_len, p); + break; + default: + return (NULL); + break; + } + + /* Attribute length must be 2-bytes or 4-bytes for a paired entry. */ + if (attr_len == 2) { + BE_STREAM_TO_UINT16 (p_seq->attr_entry[p_seq->num_attr].start, p); + p_seq->attr_entry[p_seq->num_attr].end = p_seq->attr_entry[p_seq->num_attr].start; + } else if (attr_len == 4) { + BE_STREAM_TO_UINT16 (p_seq->attr_entry[p_seq->num_attr].start, p); + BE_STREAM_TO_UINT16 (p_seq->attr_entry[p_seq->num_attr].end, p); + } else { + return (NULL); + } + + /* We can only do so many */ + if (++p_seq->num_attr >= MAX_ATTR_PER_SEQ) { + return (NULL); + } + } + + return (p); +} + + +/******************************************************************************* +** +** Function sdpu_get_len_from_type +** +** Description This function gets the length +** +** Returns void +** +*******************************************************************************/ +UINT8 *sdpu_get_len_from_type (UINT8 *p, UINT8 type, UINT32 *p_len) +{ + UINT8 u8; + UINT16 u16; + UINT32 u32; + + switch (type & 7) { + case SIZE_ONE_BYTE: + *p_len = 1; + break; + case SIZE_TWO_BYTES: + *p_len = 2; + break; + case SIZE_FOUR_BYTES: + *p_len = 4; + break; + case SIZE_EIGHT_BYTES: + *p_len = 8; + break; + case SIZE_SIXTEEN_BYTES: + *p_len = 16; + break; + case SIZE_IN_NEXT_BYTE: + BE_STREAM_TO_UINT8 (u8, p); + *p_len = u8; + break; + case SIZE_IN_NEXT_WORD: + BE_STREAM_TO_UINT16 (u16, p); + *p_len = u16; + break; + case SIZE_IN_NEXT_LONG: + BE_STREAM_TO_UINT32 (u32, p); + *p_len = (UINT16) u32; + break; + } + + return (p); +} + + +/******************************************************************************* +** +** Function sdpu_is_base_uuid +** +** Description This function checks a 128-bit UUID with the base to see if +** it matches. Only the last 12 bytes are compared. +** +** Returns TRUE if matched, else FALSE +** +*******************************************************************************/ +BOOLEAN sdpu_is_base_uuid (UINT8 *p_uuid) +{ + UINT16 xx; + + for (xx = 4; xx < MAX_UUID_SIZE; xx++) + if (p_uuid[xx] != sdp_base_uuid[xx]) { + return (FALSE); + } + + /* If here, matched */ + return (TRUE); +} + + +/******************************************************************************* +** +** Function sdpu_compare_uuid_arrays +** +** Description This function compares 2 BE UUIDs. If needed, they are expanded +** to 128-bit UUIDs, then compared. +** +** NOTE it is assumed that the arrays are in Big Endian format +** +** Returns TRUE if matched, else FALSE +** +*******************************************************************************/ +BOOLEAN sdpu_compare_uuid_arrays (UINT8 *p_uuid1, UINT32 len1, UINT8 *p_uuid2, UINT16 len2) +{ + UINT8 nu1[MAX_UUID_SIZE]; + UINT8 nu2[MAX_UUID_SIZE]; + + if ( ((len1 != 2) && (len1 != 4) && (len1 != 16)) || + ((len2 != 2) && (len2 != 4) && (len2 != 16)) ) { + SDP_TRACE_ERROR("%s: invalid length\n", __func__); + return FALSE; + } + + /* If lengths match, do a straight compare */ + if (len1 == len2) { + if (len1 == 2) { + return ((p_uuid1[0] == p_uuid2[0]) && (p_uuid1[1] == p_uuid2[1])); + } + if (len1 == 4) { + return ( (p_uuid1[0] == p_uuid2[0]) && (p_uuid1[1] == p_uuid2[1]) + && (p_uuid1[2] == p_uuid2[2]) && (p_uuid1[3] == p_uuid2[3]) ); + } else { + return (memcmp (p_uuid1, p_uuid2, (size_t)len1) == 0); + } + } else if (len1 > len2) { + /* If the len1 was 4-byte, (so len2 is 2-byte), compare on the fly */ + if (len1 == 4) { + return ( (p_uuid1[0] == 0) && (p_uuid1[1] == 0) + && (p_uuid1[2] == p_uuid2[0]) && (p_uuid1[3] == p_uuid2[1]) ); + } else { + /* Normalize UUIDs to 16-byte form, then compare. Len1 must be 16 */ + memcpy (nu1, p_uuid1, MAX_UUID_SIZE); + memcpy (nu2, sdp_base_uuid, MAX_UUID_SIZE); + + if (len2 == 4) { + memcpy (nu2, p_uuid2, len2); + } else if (len2 == 2) { + memcpy (nu2 + 2, p_uuid2, len2); + } + + return (memcmp (nu1, nu2, MAX_UUID_SIZE) == 0); + } + } else { + /* len2 is greater than len1 */ + /* If the len2 was 4-byte, (so len1 is 2-byte), compare on the fly */ + if (len2 == 4) { + return ( (p_uuid2[0] == 0) && (p_uuid2[1] == 0) + && (p_uuid2[2] == p_uuid1[0]) && (p_uuid2[3] == p_uuid1[1]) ); + } else { + /* Normalize UUIDs to 16-byte form, then compare. Len1 must be 16 */ + memcpy (nu2, p_uuid2, MAX_UUID_SIZE); + memcpy (nu1, sdp_base_uuid, MAX_UUID_SIZE); + + if (len1 == 4) { + memcpy (nu1, p_uuid1, (size_t)len1); + } else if (len1 == 2) { + memcpy (nu1 + 2, p_uuid1, (size_t)len1); + } + + return (memcmp (nu1, nu2, MAX_UUID_SIZE) == 0); + } + } +} + + +/******************************************************************************* +** +** Function sdpu_compare_bt_uuids +** +** Description This function compares 2 BT UUID structures. +** +** NOTE it is assumed that BT UUID structures are compressed to the +** smallest possible UUIDs (by removing the base SDP UUID) +** +** Returns TRUE if matched, else FALSE +** +*******************************************************************************/ +BOOLEAN sdpu_compare_bt_uuids (tBT_UUID *p_uuid1, tBT_UUID *p_uuid2) +{ + /* Lengths must match for BT UUIDs to match */ + if (p_uuid1->len == p_uuid2->len) { + if (p_uuid1->len == 2) { + return (p_uuid1->uu.uuid16 == p_uuid2->uu.uuid16); + } else if (p_uuid1->len == 4) { + return (p_uuid1->uu.uuid32 == p_uuid2->uu.uuid32); + } else if (!memcmp (p_uuid1->uu.uuid128, p_uuid2->uu.uuid128, 16)) { + return (TRUE); + } + } + + return (FALSE); +} + + +/******************************************************************************* +** +** Function sdpu_compare_uuid_with_attr +** +** Description This function compares a BT UUID structure with the UUID in an +** SDP attribute record. If needed, they are expanded to 128-bit +** UUIDs, then compared. +** +** NOTE - it is assumed that BT UUID structures are compressed to the +** smallest possible UUIDs (by removing the base SDP UUID). +** - it is also assumed that the discovery atribute is compressed +** to the smallest possible +** +** Returns TRUE if matched, else FALSE +** +*******************************************************************************/ +BOOLEAN sdpu_compare_uuid_with_attr (tBT_UUID *p_btuuid, tSDP_DISC_ATTR *p_attr) +{ + UINT16 attr_len = SDP_DISC_ATTR_LEN (p_attr->attr_len_type); + + /* Since both UUIDs are compressed, lengths must match */ + if (p_btuuid->len != attr_len) { + return (FALSE); + } + + if (p_btuuid->len == 2) { + return (BOOLEAN)(p_btuuid->uu.uuid16 == p_attr->attr_value.v.u16); + } else if (p_btuuid->len == 4) { + return (BOOLEAN)(p_btuuid->uu.uuid32 == p_attr->attr_value.v.u32); + } + /* coverity[overrun-buffer-arg] */ + /* + Event overrun-buffer-arg: Overrun of static array "&p_attr->attr_value.v.array" of size 4 bytes by passing it to a function which indexes it with argument "16U" at byte position 15 + FALSE-POSITIVE error from Coverity test tool. Please do NOT remove following comment. + False-positive: SDP uses scratch buffer to hold the attribute value. + The actual size of tSDP_DISC_ATVAL does not matter. + If the array size in tSDP_DISC_ATVAL is increase, we would increase the system RAM usage unnecessarily + */ + else if (!memcmp (p_btuuid->uu.uuid128, (void *) p_attr->attr_value.v.array, MAX_UUID_SIZE)) { + return (TRUE); + } + + return (FALSE); +} + +/******************************************************************************* +** +** Function sdpu_sort_attr_list +** +** Description sorts a list of attributes in numeric order from lowest to +** highest to conform to SDP specification +** +** Returns void +** +*******************************************************************************/ +void sdpu_sort_attr_list( UINT16 num_attr, tSDP_DISCOVERY_DB *p_db ) +{ + UINT16 i; + UINT16 x; + + /* Done if no attributes to sort */ + if (num_attr <= 1) { + return; + } else if (num_attr > SDP_MAX_ATTR_FILTERS) { + num_attr = SDP_MAX_ATTR_FILTERS; + } + + num_attr--; /* for the for-loop */ + for ( i = 0; i < num_attr; ) { + if ( p_db->attr_filters[i] > p_db->attr_filters[i + 1] ) { + /* swap the attribute IDs and start from the beginning */ + x = p_db->attr_filters[i]; + p_db->attr_filters[i] = p_db->attr_filters[i + 1]; + p_db->attr_filters[i + 1] = x; + + i = 0; + } else { + i++; + } + } +} + + +/******************************************************************************* +** +** Function sdpu_get_list_len +** +** Description gets the total list length in the sdp database for a given +** uid sequence and attr sequence +** +** Returns void +** +*******************************************************************************/ +UINT16 sdpu_get_list_len(tSDP_UUID_SEQ *uid_seq, tSDP_ATTR_SEQ *attr_seq) +{ + tSDP_RECORD *p_rec; + UINT16 len = 0; + UINT16 len1; + + for (p_rec = sdp_db_service_search (NULL, uid_seq); p_rec; p_rec = sdp_db_service_search (p_rec, uid_seq)) { + len += 3; + + len1 = sdpu_get_attrib_seq_len(p_rec, attr_seq ); + + if (len1 != 0) { + len += len1; + } else { + len -= 3; + } + } + return len; +} + +/******************************************************************************* +** +** Function sdpu_get_attrib_seq_len +** +** Description gets the length of the specific attributes in a given +** sdp record +** +** Returns void +** +*******************************************************************************/ +UINT16 sdpu_get_attrib_seq_len(tSDP_RECORD *p_rec, tSDP_ATTR_SEQ *attr_seq) +{ + tSDP_ATTRIBUTE *p_attr; + UINT16 len1 = 0; + UINT16 xx; + BOOLEAN is_range = FALSE; + UINT16 start_id = 0, end_id = 0; + + for (xx = 0; xx < attr_seq->num_attr; xx++) { + if (is_range == FALSE) { + start_id = attr_seq->attr_entry[xx].start; + end_id = attr_seq->attr_entry[xx].end; + } + p_attr = sdp_db_find_attr_in_rec (p_rec, + start_id, + end_id); + if (p_attr) { + len1 += sdpu_get_attrib_entry_len (p_attr); + + /* If doing a range, stick with this one till no more attributes found */ + if (start_id != end_id) { + /* Update for next time through */ + start_id = p_attr->id + 1; + xx--; + is_range = TRUE; + } else { + is_range = FALSE; + } + } else { + is_range = FALSE; + } + } + return len1; +} + +/******************************************************************************* +** +** Function sdpu_get_attrib_entry_len +** +** Description gets the length of a specific attribute +** +** Returns void +** +*******************************************************************************/ +UINT16 sdpu_get_attrib_entry_len(tSDP_ATTRIBUTE *p_attr) +{ + UINT16 len = 3; + + /* the attribute is in the db record. + * assuming the attribute len is less than SDP_MAX_ATTR_LEN */ + switch (p_attr->type) { + case TEXT_STR_DESC_TYPE: /* 4 */ + case DATA_ELE_SEQ_DESC_TYPE:/* 6 */ + case DATA_ELE_ALT_DESC_TYPE:/* 7 */ + case URL_DESC_TYPE: /* 8 */ +#if (SDP_MAX_ATTR_LEN > 0xFFFF) + if (p_attr->len > 0xFFFF) { + len += 5; + } else + +#endif/* 0xFFFF - 0xFF */ + { +#if (SDP_MAX_ATTR_LEN > 0xFF) + if (p_attr->len > 0xFF) { + len += 3; + } else + +#endif /* 0xFF and less*/ + { + len += 2; + } + } + len += p_attr->len; + return len; + } + + /* Now, the attribute value */ + switch (p_attr->len) { + case 1: + case 2: + case 4: + case 8: + case 16: + len += 1; + break; + default: + len += 2; + break; + } + + len += p_attr->len; + return len; +} + + +/******************************************************************************* +** +** Function sdpu_build_partial_attrib_entry +** +** Description This function fills a buffer with partial attribute. It is +** assumed that the maximum size of any attribute is 256 bytes. +** +** p_out: output buffer +** p_attr: attribute to be copied partially into p_out +** rem_len: num bytes to copy into p_out +** offset: current start offset within the attr that needs to be copied +** +** Returns Pointer to next byte in the output buffer. +** offset is also updated +** +*******************************************************************************/ +UINT8 *sdpu_build_partial_attrib_entry (UINT8 *p_out, tSDP_ATTRIBUTE *p_attr, UINT16 len, UINT16 *offset) +{ + UINT8 *p_attr_buff; + UINT8 *p_tmp_attr; + size_t len_to_copy; + UINT16 attr_len; + + if ((p_attr_buff = (UINT8 *) osi_malloc(sizeof(UINT8) * SDP_MAX_ATTR_LEN )) == NULL) { + SDP_TRACE_ERROR("sdpu_build_partial_attrib_entry cannot get a buffer!\n"); + return NULL; + } + p_tmp_attr = p_attr_buff; + + sdpu_build_attrib_entry(p_tmp_attr, p_attr); + attr_len = sdpu_get_attrib_entry_len(p_attr); + + len_to_copy = ((attr_len - *offset) < len) ? (attr_len - *offset) : len; + + memcpy(p_out, &p_attr_buff[*offset], len_to_copy); + + p_out = &p_out[len_to_copy]; + *offset += len_to_copy; + + osi_free(p_attr_buff); + return p_out; +} + +/******************************************************************************* +** +** Function sdpu_uuid16_to_uuid128 +** +** Description This function converts UUID-16 to UUID-128 by including the base UUID +** +** uuid16: 2-byte UUID +** p_uuid128: Expanded 128-bit UUID +** +** Returns None +** +*******************************************************************************/ + +void sdpu_uuid16_to_uuid128(UINT16 uuid16, UINT8 *p_uuid128) +{ + UINT16 uuid16_bo; + memset(p_uuid128, 0, 16); + + memcpy(p_uuid128, sdp_base_uuid, MAX_UUID_SIZE); + uuid16_bo = ntohs(uuid16); + memcpy(p_uuid128 + 2, &uuid16_bo, sizeof(uint16_t)); +} + +#endif ///SDP_INCLUDED == TRUE diff --git a/lib/bt/host/bluedroid/stack/smp/aes.c b/lib/bt/host/bluedroid/stack/smp/aes.c new file mode 100644 index 00000000..18b56b87 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/smp/aes.c @@ -0,0 +1,938 @@ +/* + --------------------------------------------------------------------------- + Copyright (c) 1998-2008, Brian Gladman, Worcester, UK. All rights reserved. + + LICENSE TERMS + + The redistribution and use of this software (with or without changes) + is allowed without the payment of fees or royalties provided that: + + 1. source code distributions include the above copyright notice, this + list of conditions and the following disclaimer; + + 2. binary distributions include the above copyright notice, this list + of conditions and the following disclaimer in their documentation; + + 3. the name of the copyright holder is not used to endorse products + built using this software without specific written permission. + + DISCLAIMER + + This software is provided 'as is' with no explicit or implied warranties + in respect of its properties, including, but not limited to, correctness + and/or fitness for purpose. + --------------------------------------------------------------------------- + Issue 09/09/2006 + + This is an AES implementation that uses only 8-bit byte operations on the + cipher state (there are options to use 32-bit types if available). + + The combination of mix columns and byte substitution used here is based on + that developed by Karl Malbrain. His contribution is acknowledged. + */ + +/* define if you have a fast memcpy function on your system */ +#if 1 +# define HAVE_MEMCPY +# include +#if 0 +# if defined( _MSC_VER ) +# include +# pragma intrinsic( memcpy ) +# endif +#endif +#endif + +#include + +/* add the target configuration to allow using internal data types and compilation options */ +#include "common/bt_target.h" + +/* define if you have fast 32-bit types on your system */ +#if 1 +# define HAVE_UINT_32T +#endif + +/* define if you don't want any tables */ +#if 1 +# define USE_TABLES +#endif + +/* On Intel Core 2 duo VERSION_1 is faster */ + +/* alternative versions (test for performance on your system) */ +#if 1 +# define VERSION_1 +#endif + +#include "aes.h" + +#if defined( HAVE_UINT_32T ) +typedef UINT32 uint_32t; +#endif + +/* functions for finite field multiplication in the AES Galois field */ + +#define WPOLY 0x011b +#define BPOLY 0x1b +#define DPOLY 0x008d + +#define f1(x) (x) +#define f2(x) ((x << 1) ^ (((x >> 7) & 1) * WPOLY)) +#define f4(x) ((x << 2) ^ (((x >> 6) & 1) * WPOLY) ^ (((x >> 6) & 2) * WPOLY)) +#define f8(x) ((x << 3) ^ (((x >> 5) & 1) * WPOLY) ^ (((x >> 5) & 2) * WPOLY) \ + ^ (((x >> 5) & 4) * WPOLY)) +#define d2(x) (((x) >> 1) ^ ((x) & 1 ? DPOLY : 0)) + +#define f3(x) (f2(x) ^ x) +#define f9(x) (f8(x) ^ x) +#define fb(x) (f8(x) ^ f2(x) ^ x) +#define fd(x) (f8(x) ^ f4(x) ^ x) +#define fe(x) (f8(x) ^ f4(x) ^ f2(x)) + +#if defined( USE_TABLES ) + +#define sb_data(w) { /* S Box data values */ \ + w(0x63), w(0x7c), w(0x77), w(0x7b), w(0xf2), w(0x6b), w(0x6f), w(0xc5),\ + w(0x30), w(0x01), w(0x67), w(0x2b), w(0xfe), w(0xd7), w(0xab), w(0x76),\ + w(0xca), w(0x82), w(0xc9), w(0x7d), w(0xfa), w(0x59), w(0x47), w(0xf0),\ + w(0xad), w(0xd4), w(0xa2), w(0xaf), w(0x9c), w(0xa4), w(0x72), w(0xc0),\ + w(0xb7), w(0xfd), w(0x93), w(0x26), w(0x36), w(0x3f), w(0xf7), w(0xcc),\ + w(0x34), w(0xa5), w(0xe5), w(0xf1), w(0x71), w(0xd8), w(0x31), w(0x15),\ + w(0x04), w(0xc7), w(0x23), w(0xc3), w(0x18), w(0x96), w(0x05), w(0x9a),\ + w(0x07), w(0x12), w(0x80), w(0xe2), w(0xeb), w(0x27), w(0xb2), w(0x75),\ + w(0x09), w(0x83), w(0x2c), w(0x1a), w(0x1b), w(0x6e), w(0x5a), w(0xa0),\ + w(0x52), w(0x3b), w(0xd6), w(0xb3), w(0x29), w(0xe3), w(0x2f), w(0x84),\ + w(0x53), w(0xd1), w(0x00), w(0xed), w(0x20), w(0xfc), w(0xb1), w(0x5b),\ + w(0x6a), w(0xcb), w(0xbe), w(0x39), w(0x4a), w(0x4c), w(0x58), w(0xcf),\ + w(0xd0), w(0xef), w(0xaa), w(0xfb), w(0x43), w(0x4d), w(0x33), w(0x85),\ + w(0x45), w(0xf9), w(0x02), w(0x7f), w(0x50), w(0x3c), w(0x9f), w(0xa8),\ + w(0x51), w(0xa3), w(0x40), w(0x8f), w(0x92), w(0x9d), w(0x38), w(0xf5),\ + w(0xbc), w(0xb6), w(0xda), w(0x21), w(0x10), w(0xff), w(0xf3), w(0xd2),\ + w(0xcd), w(0x0c), w(0x13), w(0xec), w(0x5f), w(0x97), w(0x44), w(0x17),\ + w(0xc4), w(0xa7), w(0x7e), w(0x3d), w(0x64), w(0x5d), w(0x19), w(0x73),\ + w(0x60), w(0x81), w(0x4f), w(0xdc), w(0x22), w(0x2a), w(0x90), w(0x88),\ + w(0x46), w(0xee), w(0xb8), w(0x14), w(0xde), w(0x5e), w(0x0b), w(0xdb),\ + w(0xe0), w(0x32), w(0x3a), w(0x0a), w(0x49), w(0x06), w(0x24), w(0x5c),\ + w(0xc2), w(0xd3), w(0xac), w(0x62), w(0x91), w(0x95), w(0xe4), w(0x79),\ + w(0xe7), w(0xc8), w(0x37), w(0x6d), w(0x8d), w(0xd5), w(0x4e), w(0xa9),\ + w(0x6c), w(0x56), w(0xf4), w(0xea), w(0x65), w(0x7a), w(0xae), w(0x08),\ + w(0xba), w(0x78), w(0x25), w(0x2e), w(0x1c), w(0xa6), w(0xb4), w(0xc6),\ + w(0xe8), w(0xdd), w(0x74), w(0x1f), w(0x4b), w(0xbd), w(0x8b), w(0x8a),\ + w(0x70), w(0x3e), w(0xb5), w(0x66), w(0x48), w(0x03), w(0xf6), w(0x0e),\ + w(0x61), w(0x35), w(0x57), w(0xb9), w(0x86), w(0xc1), w(0x1d), w(0x9e),\ + w(0xe1), w(0xf8), w(0x98), w(0x11), w(0x69), w(0xd9), w(0x8e), w(0x94),\ + w(0x9b), w(0x1e), w(0x87), w(0xe9), w(0xce), w(0x55), w(0x28), w(0xdf),\ + w(0x8c), w(0xa1), w(0x89), w(0x0d), w(0xbf), w(0xe6), w(0x42), w(0x68),\ + w(0x41), w(0x99), w(0x2d), w(0x0f), w(0xb0), w(0x54), w(0xbb), w(0x16) } + +#define isb_data(w) { /* inverse S Box data values */ \ + w(0x52), w(0x09), w(0x6a), w(0xd5), w(0x30), w(0x36), w(0xa5), w(0x38),\ + w(0xbf), w(0x40), w(0xa3), w(0x9e), w(0x81), w(0xf3), w(0xd7), w(0xfb),\ + w(0x7c), w(0xe3), w(0x39), w(0x82), w(0x9b), w(0x2f), w(0xff), w(0x87),\ + w(0x34), w(0x8e), w(0x43), w(0x44), w(0xc4), w(0xde), w(0xe9), w(0xcb),\ + w(0x54), w(0x7b), w(0x94), w(0x32), w(0xa6), w(0xc2), w(0x23), w(0x3d),\ + w(0xee), w(0x4c), w(0x95), w(0x0b), w(0x42), w(0xfa), w(0xc3), w(0x4e),\ + w(0x08), w(0x2e), w(0xa1), w(0x66), w(0x28), w(0xd9), w(0x24), w(0xb2),\ + w(0x76), w(0x5b), w(0xa2), w(0x49), w(0x6d), w(0x8b), w(0xd1), w(0x25),\ + w(0x72), w(0xf8), w(0xf6), w(0x64), w(0x86), w(0x68), w(0x98), w(0x16),\ + w(0xd4), w(0xa4), w(0x5c), w(0xcc), w(0x5d), w(0x65), w(0xb6), w(0x92),\ + w(0x6c), w(0x70), w(0x48), w(0x50), w(0xfd), w(0xed), w(0xb9), w(0xda),\ + w(0x5e), w(0x15), w(0x46), w(0x57), w(0xa7), w(0x8d), w(0x9d), w(0x84),\ + w(0x90), w(0xd8), w(0xab), w(0x00), w(0x8c), w(0xbc), w(0xd3), w(0x0a),\ + w(0xf7), w(0xe4), w(0x58), w(0x05), w(0xb8), w(0xb3), w(0x45), w(0x06),\ + w(0xd0), w(0x2c), w(0x1e), w(0x8f), w(0xca), w(0x3f), w(0x0f), w(0x02),\ + w(0xc1), w(0xaf), w(0xbd), w(0x03), w(0x01), w(0x13), w(0x8a), w(0x6b),\ + w(0x3a), w(0x91), w(0x11), w(0x41), w(0x4f), w(0x67), w(0xdc), w(0xea),\ + w(0x97), w(0xf2), w(0xcf), w(0xce), w(0xf0), w(0xb4), w(0xe6), w(0x73),\ + w(0x96), w(0xac), w(0x74), w(0x22), w(0xe7), w(0xad), w(0x35), w(0x85),\ + w(0xe2), w(0xf9), w(0x37), w(0xe8), w(0x1c), w(0x75), w(0xdf), w(0x6e),\ + w(0x47), w(0xf1), w(0x1a), w(0x71), w(0x1d), w(0x29), w(0xc5), w(0x89),\ + w(0x6f), w(0xb7), w(0x62), w(0x0e), w(0xaa), w(0x18), w(0xbe), w(0x1b),\ + w(0xfc), w(0x56), w(0x3e), w(0x4b), w(0xc6), w(0xd2), w(0x79), w(0x20),\ + w(0x9a), w(0xdb), w(0xc0), w(0xfe), w(0x78), w(0xcd), w(0x5a), w(0xf4),\ + w(0x1f), w(0xdd), w(0xa8), w(0x33), w(0x88), w(0x07), w(0xc7), w(0x31),\ + w(0xb1), w(0x12), w(0x10), w(0x59), w(0x27), w(0x80), w(0xec), w(0x5f),\ + w(0x60), w(0x51), w(0x7f), w(0xa9), w(0x19), w(0xb5), w(0x4a), w(0x0d),\ + w(0x2d), w(0xe5), w(0x7a), w(0x9f), w(0x93), w(0xc9), w(0x9c), w(0xef),\ + w(0xa0), w(0xe0), w(0x3b), w(0x4d), w(0xae), w(0x2a), w(0xf5), w(0xb0),\ + w(0xc8), w(0xeb), w(0xbb), w(0x3c), w(0x83), w(0x53), w(0x99), w(0x61),\ + w(0x17), w(0x2b), w(0x04), w(0x7e), w(0xba), w(0x77), w(0xd6), w(0x26),\ + w(0xe1), w(0x69), w(0x14), w(0x63), w(0x55), w(0x21), w(0x0c), w(0x7d) } + +#define mm_data(w) { /* basic data for forming finite field tables */ \ + w(0x00), w(0x01), w(0x02), w(0x03), w(0x04), w(0x05), w(0x06), w(0x07),\ + w(0x08), w(0x09), w(0x0a), w(0x0b), w(0x0c), w(0x0d), w(0x0e), w(0x0f),\ + w(0x10), w(0x11), w(0x12), w(0x13), w(0x14), w(0x15), w(0x16), w(0x17),\ + w(0x18), w(0x19), w(0x1a), w(0x1b), w(0x1c), w(0x1d), w(0x1e), w(0x1f),\ + w(0x20), w(0x21), w(0x22), w(0x23), w(0x24), w(0x25), w(0x26), w(0x27),\ + w(0x28), w(0x29), w(0x2a), w(0x2b), w(0x2c), w(0x2d), w(0x2e), w(0x2f),\ + w(0x30), w(0x31), w(0x32), w(0x33), w(0x34), w(0x35), w(0x36), w(0x37),\ + w(0x38), w(0x39), w(0x3a), w(0x3b), w(0x3c), w(0x3d), w(0x3e), w(0x3f),\ + w(0x40), w(0x41), w(0x42), w(0x43), w(0x44), w(0x45), w(0x46), w(0x47),\ + w(0x48), w(0x49), w(0x4a), w(0x4b), w(0x4c), w(0x4d), w(0x4e), w(0x4f),\ + w(0x50), w(0x51), w(0x52), w(0x53), w(0x54), w(0x55), w(0x56), w(0x57),\ + w(0x58), w(0x59), w(0x5a), w(0x5b), w(0x5c), w(0x5d), w(0x5e), w(0x5f),\ + w(0x60), w(0x61), w(0x62), w(0x63), w(0x64), w(0x65), w(0x66), w(0x67),\ + w(0x68), w(0x69), w(0x6a), w(0x6b), w(0x6c), w(0x6d), w(0x6e), w(0x6f),\ + w(0x70), w(0x71), w(0x72), w(0x73), w(0x74), w(0x75), w(0x76), w(0x77),\ + w(0x78), w(0x79), w(0x7a), w(0x7b), w(0x7c), w(0x7d), w(0x7e), w(0x7f),\ + w(0x80), w(0x81), w(0x82), w(0x83), w(0x84), w(0x85), w(0x86), w(0x87),\ + w(0x88), w(0x89), w(0x8a), w(0x8b), w(0x8c), w(0x8d), w(0x8e), w(0x8f),\ + w(0x90), w(0x91), w(0x92), w(0x93), w(0x94), w(0x95), w(0x96), w(0x97),\ + w(0x98), w(0x99), w(0x9a), w(0x9b), w(0x9c), w(0x9d), w(0x9e), w(0x9f),\ + w(0xa0), w(0xa1), w(0xa2), w(0xa3), w(0xa4), w(0xa5), w(0xa6), w(0xa7),\ + w(0xa8), w(0xa9), w(0xaa), w(0xab), w(0xac), w(0xad), w(0xae), w(0xaf),\ + w(0xb0), w(0xb1), w(0xb2), w(0xb3), w(0xb4), w(0xb5), w(0xb6), w(0xb7),\ + w(0xb8), w(0xb9), w(0xba), w(0xbb), w(0xbc), w(0xbd), w(0xbe), w(0xbf),\ + w(0xc0), w(0xc1), w(0xc2), w(0xc3), w(0xc4), w(0xc5), w(0xc6), w(0xc7),\ + w(0xc8), w(0xc9), w(0xca), w(0xcb), w(0xcc), w(0xcd), w(0xce), w(0xcf),\ + w(0xd0), w(0xd1), w(0xd2), w(0xd3), w(0xd4), w(0xd5), w(0xd6), w(0xd7),\ + w(0xd8), w(0xd9), w(0xda), w(0xdb), w(0xdc), w(0xdd), w(0xde), w(0xdf),\ + w(0xe0), w(0xe1), w(0xe2), w(0xe3), w(0xe4), w(0xe5), w(0xe6), w(0xe7),\ + w(0xe8), w(0xe9), w(0xea), w(0xeb), w(0xec), w(0xed), w(0xee), w(0xef),\ + w(0xf0), w(0xf1), w(0xf2), w(0xf3), w(0xf4), w(0xf5), w(0xf6), w(0xf7),\ + w(0xf8), w(0xf9), w(0xfa), w(0xfb), w(0xfc), w(0xfd), w(0xfe), w(0xff) } + +static const uint_8t sbox[256] = sb_data(f1); +static const uint_8t isbox[256] = isb_data(f1); + +static const uint_8t gfm2_sbox[256] = sb_data(f2); +static const uint_8t gfm3_sbox[256] = sb_data(f3); + +static const uint_8t gfmul_9[256] = mm_data(f9); +static const uint_8t gfmul_b[256] = mm_data(fb); +static const uint_8t gfmul_d[256] = mm_data(fd); +static const uint_8t gfmul_e[256] = mm_data(fe); + +#define s_box(x) sbox[(x)] +#define is_box(x) isbox[(x)] +#define gfm2_sb(x) gfm2_sbox[(x)] +#define gfm3_sb(x) gfm3_sbox[(x)] +#define gfm_9(x) gfmul_9[(x)] +#define gfm_b(x) gfmul_b[(x)] +#define gfm_d(x) gfmul_d[(x)] +#define gfm_e(x) gfmul_e[(x)] + +#else + +/* this is the high bit of x right shifted by 1 */ +/* position. Since the starting polynomial has */ +/* 9 bits (0x11b), this right shift keeps the */ +/* values of all top bits within a byte */ + +static uint_8t hibit(const uint_8t x) +{ + uint_8t r = (uint_8t)((x >> 1) | (x >> 2)); + + r |= (r >> 2); + r |= (r >> 4); + return (r + 1) >> 1; +} + +/* return the inverse of the finite field element x */ + +static uint_8t gf_inv(const uint_8t x) +{ + uint_8t p1 = x, p2 = BPOLY, n1 = hibit(x), n2 = 0x80, v1 = 1, v2 = 0; + + if (x < 2) { + return x; + } + + for ( ; ; ) { + if (n1) { + while (n2 >= n1) { /* divide polynomial p2 by p1 */ + n2 /= n1; /* shift smaller polynomial left */ + p2 ^= (p1 * n2) & 0xff; /* and remove from larger one */ + v2 ^= (v1 * n2); /* shift accumulated value and */ + n2 = hibit(p2); /* add into result */ + } + } else { + return v1; + } + + if (n2) { /* repeat with values swapped */ + while (n1 >= n2) { + n1 /= n2; + p1 ^= p2 * n1; + v1 ^= v2 * n1; + n1 = hibit(p1); + } + } else { + return v2; + } + } +} + +/* The forward and inverse affine transformations used in the S-box */ +uint_8t fwd_affine(const uint_8t x) +{ +#if defined( HAVE_UINT_32T ) + uint_32t w = x; + w ^= (w << 1) ^ (w << 2) ^ (w << 3) ^ (w << 4); + return 0x63 ^ ((w ^ (w >> 8)) & 0xff); +#else + return 0x63 ^ x ^ (x << 1) ^ (x << 2) ^ (x << 3) ^ (x << 4) + ^ (x >> 7) ^ (x >> 6) ^ (x >> 5) ^ (x >> 4); +#endif +} + +uint_8t inv_affine(const uint_8t x) +{ +#if defined( HAVE_UINT_32T ) + uint_32t w = x; + w = (w << 1) ^ (w << 3) ^ (w << 6); + return 0x05 ^ ((w ^ (w >> 8)) & 0xff); +#else + return 0x05 ^ (x << 1) ^ (x << 3) ^ (x << 6) + ^ (x >> 7) ^ (x >> 5) ^ (x >> 2); +#endif +} + +#define s_box(x) fwd_affine(gf_inv(x)) +#define is_box(x) gf_inv(inv_affine(x)) +#define gfm2_sb(x) f2(s_box(x)) +#define gfm3_sb(x) f3(s_box(x)) +#define gfm_9(x) f9(x) +#define gfm_b(x) fb(x) +#define gfm_d(x) fd(x) +#define gfm_e(x) fe(x) + +#endif + +#if defined( HAVE_MEMCPY ) +# define block_copy_nn(d, s, l) memcpy(d, s, l) +# define block_copy(d, s) memcpy(d, s, N_BLOCK) +#else +# define block_copy_nn(d, s, l) copy_block_nn(d, s, l) +# define block_copy(d, s) copy_block(d, s) +#endif + +#if !defined( HAVE_MEMCPY ) +static void copy_block( void *d, const void *s ) +{ +#if defined( HAVE_UINT_32T ) + ((uint_32t *)d)[ 0] = ((uint_32t *)s)[ 0]; + ((uint_32t *)d)[ 1] = ((uint_32t *)s)[ 1]; + ((uint_32t *)d)[ 2] = ((uint_32t *)s)[ 2]; + ((uint_32t *)d)[ 3] = ((uint_32t *)s)[ 3]; +#else + ((uint_8t *)d)[ 0] = ((uint_8t *)s)[ 0]; + ((uint_8t *)d)[ 1] = ((uint_8t *)s)[ 1]; + ((uint_8t *)d)[ 2] = ((uint_8t *)s)[ 2]; + ((uint_8t *)d)[ 3] = ((uint_8t *)s)[ 3]; + ((uint_8t *)d)[ 4] = ((uint_8t *)s)[ 4]; + ((uint_8t *)d)[ 5] = ((uint_8t *)s)[ 5]; + ((uint_8t *)d)[ 6] = ((uint_8t *)s)[ 6]; + ((uint_8t *)d)[ 7] = ((uint_8t *)s)[ 7]; + ((uint_8t *)d)[ 8] = ((uint_8t *)s)[ 8]; + ((uint_8t *)d)[ 9] = ((uint_8t *)s)[ 9]; + ((uint_8t *)d)[10] = ((uint_8t *)s)[10]; + ((uint_8t *)d)[11] = ((uint_8t *)s)[11]; + ((uint_8t *)d)[12] = ((uint_8t *)s)[12]; + ((uint_8t *)d)[13] = ((uint_8t *)s)[13]; + ((uint_8t *)d)[14] = ((uint_8t *)s)[14]; + ((uint_8t *)d)[15] = ((uint_8t *)s)[15]; +#endif +} + +static void copy_block_nn( void *d, const void *s, uint_8t nn ) +{ + while ( nn-- ) { + *((uint_8t *)d)++ = *((uint_8t *)s)++; + } +} +#endif + +static void xor_block( void *d, const void *s ) +{ +#if defined( HAVE_UINT_32T ) + ((uint_32t *)d)[ 0] ^= ((uint_32t *)s)[ 0]; + ((uint_32t *)d)[ 1] ^= ((uint_32t *)s)[ 1]; + ((uint_32t *)d)[ 2] ^= ((uint_32t *)s)[ 2]; + ((uint_32t *)d)[ 3] ^= ((uint_32t *)s)[ 3]; +#else + ((uint_8t *)d)[ 0] ^= ((uint_8t *)s)[ 0]; + ((uint_8t *)d)[ 1] ^= ((uint_8t *)s)[ 1]; + ((uint_8t *)d)[ 2] ^= ((uint_8t *)s)[ 2]; + ((uint_8t *)d)[ 3] ^= ((uint_8t *)s)[ 3]; + ((uint_8t *)d)[ 4] ^= ((uint_8t *)s)[ 4]; + ((uint_8t *)d)[ 5] ^= ((uint_8t *)s)[ 5]; + ((uint_8t *)d)[ 6] ^= ((uint_8t *)s)[ 6]; + ((uint_8t *)d)[ 7] ^= ((uint_8t *)s)[ 7]; + ((uint_8t *)d)[ 8] ^= ((uint_8t *)s)[ 8]; + ((uint_8t *)d)[ 9] ^= ((uint_8t *)s)[ 9]; + ((uint_8t *)d)[10] ^= ((uint_8t *)s)[10]; + ((uint_8t *)d)[11] ^= ((uint_8t *)s)[11]; + ((uint_8t *)d)[12] ^= ((uint_8t *)s)[12]; + ((uint_8t *)d)[13] ^= ((uint_8t *)s)[13]; + ((uint_8t *)d)[14] ^= ((uint_8t *)s)[14]; + ((uint_8t *)d)[15] ^= ((uint_8t *)s)[15]; +#endif +} + +static void copy_and_key( void *d, const void *s, const void *k ) +{ +#if defined( HAVE_UINT_32T ) + ((uint_32t *)d)[ 0] = ((uint_32t *)s)[ 0] ^ ((uint_32t *)k)[ 0]; + ((uint_32t *)d)[ 1] = ((uint_32t *)s)[ 1] ^ ((uint_32t *)k)[ 1]; + ((uint_32t *)d)[ 2] = ((uint_32t *)s)[ 2] ^ ((uint_32t *)k)[ 2]; + ((uint_32t *)d)[ 3] = ((uint_32t *)s)[ 3] ^ ((uint_32t *)k)[ 3]; +#elif 1 + ((uint_8t *)d)[ 0] = ((uint_8t *)s)[ 0] ^ ((uint_8t *)k)[ 0]; + ((uint_8t *)d)[ 1] = ((uint_8t *)s)[ 1] ^ ((uint_8t *)k)[ 1]; + ((uint_8t *)d)[ 2] = ((uint_8t *)s)[ 2] ^ ((uint_8t *)k)[ 2]; + ((uint_8t *)d)[ 3] = ((uint_8t *)s)[ 3] ^ ((uint_8t *)k)[ 3]; + ((uint_8t *)d)[ 4] = ((uint_8t *)s)[ 4] ^ ((uint_8t *)k)[ 4]; + ((uint_8t *)d)[ 5] = ((uint_8t *)s)[ 5] ^ ((uint_8t *)k)[ 5]; + ((uint_8t *)d)[ 6] = ((uint_8t *)s)[ 6] ^ ((uint_8t *)k)[ 6]; + ((uint_8t *)d)[ 7] = ((uint_8t *)s)[ 7] ^ ((uint_8t *)k)[ 7]; + ((uint_8t *)d)[ 8] = ((uint_8t *)s)[ 8] ^ ((uint_8t *)k)[ 8]; + ((uint_8t *)d)[ 9] = ((uint_8t *)s)[ 9] ^ ((uint_8t *)k)[ 9]; + ((uint_8t *)d)[10] = ((uint_8t *)s)[10] ^ ((uint_8t *)k)[10]; + ((uint_8t *)d)[11] = ((uint_8t *)s)[11] ^ ((uint_8t *)k)[11]; + ((uint_8t *)d)[12] = ((uint_8t *)s)[12] ^ ((uint_8t *)k)[12]; + ((uint_8t *)d)[13] = ((uint_8t *)s)[13] ^ ((uint_8t *)k)[13]; + ((uint_8t *)d)[14] = ((uint_8t *)s)[14] ^ ((uint_8t *)k)[14]; + ((uint_8t *)d)[15] = ((uint_8t *)s)[15] ^ ((uint_8t *)k)[15]; +#else + block_copy(d, s); + xor_block(d, k); +#endif +} + +static void add_round_key( uint_8t d[N_BLOCK], const uint_8t k[N_BLOCK] ) +{ + xor_block(d, k); +} + +static void shift_sub_rows( uint_8t st[N_BLOCK] ) +{ + uint_8t tt; + + st[ 0] = s_box(st[ 0]); st[ 4] = s_box(st[ 4]); + st[ 8] = s_box(st[ 8]); st[12] = s_box(st[12]); + + tt = st[1]; st[ 1] = s_box(st[ 5]); st[ 5] = s_box(st[ 9]); + st[ 9] = s_box(st[13]); st[13] = s_box( tt ); + + tt = st[2]; st[ 2] = s_box(st[10]); st[10] = s_box( tt ); + tt = st[6]; st[ 6] = s_box(st[14]); st[14] = s_box( tt ); + + tt = st[15]; st[15] = s_box(st[11]); st[11] = s_box(st[ 7]); + st[ 7] = s_box(st[ 3]); st[ 3] = s_box( tt ); +} + +static void inv_shift_sub_rows( uint_8t st[N_BLOCK] ) +{ + uint_8t tt; + + st[ 0] = is_box(st[ 0]); st[ 4] = is_box(st[ 4]); + st[ 8] = is_box(st[ 8]); st[12] = is_box(st[12]); + + tt = st[13]; st[13] = is_box(st[9]); st[ 9] = is_box(st[5]); + st[ 5] = is_box(st[1]); st[ 1] = is_box( tt ); + + tt = st[2]; st[ 2] = is_box(st[10]); st[10] = is_box( tt ); + tt = st[6]; st[ 6] = is_box(st[14]); st[14] = is_box( tt ); + + tt = st[3]; st[ 3] = is_box(st[ 7]); st[ 7] = is_box(st[11]); + st[11] = is_box(st[15]); st[15] = is_box( tt ); +} + +#if defined( VERSION_1 ) +static void mix_sub_columns( uint_8t dt[N_BLOCK] ) +{ + uint_8t st[N_BLOCK]; + block_copy(st, dt); +#else +static void mix_sub_columns( uint_8t dt[N_BLOCK], uint_8t st[N_BLOCK] ) +{ +#endif + dt[ 0] = gfm2_sb(st[0]) ^ gfm3_sb(st[5]) ^ s_box(st[10]) ^ s_box(st[15]); + dt[ 1] = s_box(st[0]) ^ gfm2_sb(st[5]) ^ gfm3_sb(st[10]) ^ s_box(st[15]); + dt[ 2] = s_box(st[0]) ^ s_box(st[5]) ^ gfm2_sb(st[10]) ^ gfm3_sb(st[15]); + dt[ 3] = gfm3_sb(st[0]) ^ s_box(st[5]) ^ s_box(st[10]) ^ gfm2_sb(st[15]); + + dt[ 4] = gfm2_sb(st[4]) ^ gfm3_sb(st[9]) ^ s_box(st[14]) ^ s_box(st[3]); + dt[ 5] = s_box(st[4]) ^ gfm2_sb(st[9]) ^ gfm3_sb(st[14]) ^ s_box(st[3]); + dt[ 6] = s_box(st[4]) ^ s_box(st[9]) ^ gfm2_sb(st[14]) ^ gfm3_sb(st[3]); + dt[ 7] = gfm3_sb(st[4]) ^ s_box(st[9]) ^ s_box(st[14]) ^ gfm2_sb(st[3]); + + dt[ 8] = gfm2_sb(st[8]) ^ gfm3_sb(st[13]) ^ s_box(st[2]) ^ s_box(st[7]); + dt[ 9] = s_box(st[8]) ^ gfm2_sb(st[13]) ^ gfm3_sb(st[2]) ^ s_box(st[7]); + dt[10] = s_box(st[8]) ^ s_box(st[13]) ^ gfm2_sb(st[2]) ^ gfm3_sb(st[7]); + dt[11] = gfm3_sb(st[8]) ^ s_box(st[13]) ^ s_box(st[2]) ^ gfm2_sb(st[7]); + + dt[12] = gfm2_sb(st[12]) ^ gfm3_sb(st[1]) ^ s_box(st[6]) ^ s_box(st[11]); + dt[13] = s_box(st[12]) ^ gfm2_sb(st[1]) ^ gfm3_sb(st[6]) ^ s_box(st[11]); + dt[14] = s_box(st[12]) ^ s_box(st[1]) ^ gfm2_sb(st[6]) ^ gfm3_sb(st[11]); + dt[15] = gfm3_sb(st[12]) ^ s_box(st[1]) ^ s_box(st[6]) ^ gfm2_sb(st[11]); +} + +#if defined( VERSION_1 ) +static void inv_mix_sub_columns( uint_8t dt[N_BLOCK] ) +{ + uint_8t st[N_BLOCK]; + block_copy(st, dt); +#else +static void inv_mix_sub_columns( uint_8t dt[N_BLOCK], uint_8t st[N_BLOCK] ) +{ +#endif + dt[ 0] = is_box(gfm_e(st[ 0]) ^ gfm_b(st[ 1]) ^ gfm_d(st[ 2]) ^ gfm_9(st[ 3])); + dt[ 5] = is_box(gfm_9(st[ 0]) ^ gfm_e(st[ 1]) ^ gfm_b(st[ 2]) ^ gfm_d(st[ 3])); + dt[10] = is_box(gfm_d(st[ 0]) ^ gfm_9(st[ 1]) ^ gfm_e(st[ 2]) ^ gfm_b(st[ 3])); + dt[15] = is_box(gfm_b(st[ 0]) ^ gfm_d(st[ 1]) ^ gfm_9(st[ 2]) ^ gfm_e(st[ 3])); + + dt[ 4] = is_box(gfm_e(st[ 4]) ^ gfm_b(st[ 5]) ^ gfm_d(st[ 6]) ^ gfm_9(st[ 7])); + dt[ 9] = is_box(gfm_9(st[ 4]) ^ gfm_e(st[ 5]) ^ gfm_b(st[ 6]) ^ gfm_d(st[ 7])); + dt[14] = is_box(gfm_d(st[ 4]) ^ gfm_9(st[ 5]) ^ gfm_e(st[ 6]) ^ gfm_b(st[ 7])); + dt[ 3] = is_box(gfm_b(st[ 4]) ^ gfm_d(st[ 5]) ^ gfm_9(st[ 6]) ^ gfm_e(st[ 7])); + + dt[ 8] = is_box(gfm_e(st[ 8]) ^ gfm_b(st[ 9]) ^ gfm_d(st[10]) ^ gfm_9(st[11])); + dt[13] = is_box(gfm_9(st[ 8]) ^ gfm_e(st[ 9]) ^ gfm_b(st[10]) ^ gfm_d(st[11])); + dt[ 2] = is_box(gfm_d(st[ 8]) ^ gfm_9(st[ 9]) ^ gfm_e(st[10]) ^ gfm_b(st[11])); + dt[ 7] = is_box(gfm_b(st[ 8]) ^ gfm_d(st[ 9]) ^ gfm_9(st[10]) ^ gfm_e(st[11])); + + dt[12] = is_box(gfm_e(st[12]) ^ gfm_b(st[13]) ^ gfm_d(st[14]) ^ gfm_9(st[15])); + dt[ 1] = is_box(gfm_9(st[12]) ^ gfm_e(st[13]) ^ gfm_b(st[14]) ^ gfm_d(st[15])); + dt[ 6] = is_box(gfm_d(st[12]) ^ gfm_9(st[13]) ^ gfm_e(st[14]) ^ gfm_b(st[15])); + dt[11] = is_box(gfm_b(st[12]) ^ gfm_d(st[13]) ^ gfm_9(st[14]) ^ gfm_e(st[15])); +} + +#if defined( AES_ENC_PREKEYED ) || defined( AES_DEC_PREKEYED ) + +/* Set the cipher key for the pre-keyed version */ +/* NOTE: If the length_type used for the key length is an + unsigned 8-bit character, a key length of 256 bits must + be entered as a length in bytes (valid inputs are hence + 128, 192, 16, 24 and 32). +*/ + +return_type aes_set_key( const unsigned char key[], length_type keylen, aes_context ctx[1] ) +{ + uint_8t cc, rc, hi; + + switch ( keylen ) { + case 16: + case 128: /* length in bits (128 = 8*16) */ + keylen = 16; + break; + case 24: + case 192: /* length in bits (192 = 8*24) */ + keylen = 24; + break; + case 32: + /* case 256: length in bits (256 = 8*32) */ + keylen = 32; + break; + default: + ctx->rnd = 0; + return (return_type) - 1; + } + block_copy_nn(ctx->ksch, key, keylen); + hi = (keylen + 28) << 2; + ctx->rnd = (hi >> 4) - 1; + for ( cc = keylen, rc = 1; cc < hi; cc += 4 ) { + uint_8t tt, t0, t1, t2, t3; + + t0 = ctx->ksch[cc - 4]; + t1 = ctx->ksch[cc - 3]; + t2 = ctx->ksch[cc - 2]; + t3 = ctx->ksch[cc - 1]; + if ( cc % keylen == 0 ) { + tt = t0; + t0 = s_box(t1) ^ rc; + t1 = s_box(t2); + t2 = s_box(t3); + t3 = s_box(tt); + rc = f2(rc); + } else if ( keylen > 24 && cc % keylen == 16 ) { + t0 = s_box(t0); + t1 = s_box(t1); + t2 = s_box(t2); + t3 = s_box(t3); + } + tt = cc - keylen; + ctx->ksch[cc + 0] = ctx->ksch[tt + 0] ^ t0; + ctx->ksch[cc + 1] = ctx->ksch[tt + 1] ^ t1; + ctx->ksch[cc + 2] = ctx->ksch[tt + 2] ^ t2; + ctx->ksch[cc + 3] = ctx->ksch[tt + 3] ^ t3; + } + return 0; +} + +#endif + +#if defined( AES_ENC_PREKEYED ) + +/* Encrypt a single block of 16 bytes */ + +/* @breif change the name by snake for avoid the conflict with libcrypto */ +return_type bluedroid_aes_encrypt( const unsigned char in[N_BLOCK], unsigned char out[N_BLOCK], const aes_context ctx[1] ) +{ + if ( ctx->rnd ) { + uint_8t s1[N_BLOCK], r; + copy_and_key( s1, in, ctx->ksch ); + + for ( r = 1 ; r < ctx->rnd ; ++r ) +#if defined( VERSION_1 ) + { + mix_sub_columns( s1 ); + add_round_key( s1, ctx->ksch + r * N_BLOCK); + } +#else + { + uint_8t s2[N_BLOCK]; + mix_sub_columns( s2, s1 ); + copy_and_key( s1, s2, ctx->ksch + r * N_BLOCK); + } +#endif + shift_sub_rows( s1 ); + copy_and_key( out, s1, ctx->ksch + r * N_BLOCK ); + } else { + return (return_type) - 1; + } + return 0; +} + +/* CBC encrypt a number of blocks (input and return an IV) */ + +return_type aes_cbc_encrypt( const unsigned char *in, unsigned char *out, + int n_block, unsigned char iv[N_BLOCK], const aes_context ctx[1] ) +{ + + while (n_block--) { + xor_block(iv, in); + if (bluedroid_aes_encrypt(iv, iv, ctx) != EXIT_SUCCESS) { + return EXIT_FAILURE; + } + memcpy(out, iv, N_BLOCK); + in += N_BLOCK; + out += N_BLOCK; + } + return EXIT_SUCCESS; +} + +#endif + +#if defined( AES_DEC_PREKEYED ) + +/* Decrypt a single block of 16 bytes */ + +return_type bluedroid_aes_decrypt( const unsigned char in[N_BLOCK], unsigned char out[N_BLOCK], const aes_context ctx[1] ) +{ + if ( ctx->rnd ) { + uint_8t s1[N_BLOCK], r; + copy_and_key( s1, in, ctx->ksch + ctx->rnd * N_BLOCK ); + inv_shift_sub_rows( s1 ); + + for ( r = ctx->rnd ; --r ; ) +#if defined( VERSION_1 ) + { + add_round_key( s1, ctx->ksch + r * N_BLOCK ); + inv_mix_sub_columns( s1 ); + } +#else + { + uint_8t s2[N_BLOCK]; + copy_and_key( s2, s1, ctx->ksch + r * N_BLOCK ); + inv_mix_sub_columns( s1, s2 ); + } +#endif + copy_and_key( out, s1, ctx->ksch ); + } else { + return (return_type) - 1; + } + return 0; +} + +/* CBC decrypt a number of blocks (input and return an IV) */ + +return_type aes_cbc_decrypt( const unsigned char *in, unsigned char *out, + int n_block, unsigned char iv[N_BLOCK], const aes_context ctx[1] ) +{ + while (n_block--) { + uint_8t tmp[N_BLOCK]; + + memcpy(tmp, in, N_BLOCK); + if (bluedroid_aes_decrypt(in, out, ctx) != EXIT_SUCCESS) { + return EXIT_FAILURE; + } + xor_block(out, iv); + memcpy(iv, tmp, N_BLOCK); + in += N_BLOCK; + out += N_BLOCK; + } + return EXIT_SUCCESS; +} + +#endif + +#if defined( AES_ENC_128_OTFK ) + +/* The 'on the fly' encryption key update for for 128 bit keys */ + +static void update_encrypt_key_128( uint_8t k[N_BLOCK], uint_8t *rc ) +{ + uint_8t cc; + + k[0] ^= s_box(k[13]) ^ *rc; + k[1] ^= s_box(k[14]); + k[2] ^= s_box(k[15]); + k[3] ^= s_box(k[12]); + *rc = f2( *rc ); + + for (cc = 4; cc < 16; cc += 4 ) { + k[cc + 0] ^= k[cc - 4]; + k[cc + 1] ^= k[cc - 3]; + k[cc + 2] ^= k[cc - 2]; + k[cc + 3] ^= k[cc - 1]; + } +} + +/* Encrypt a single block of 16 bytes with 'on the fly' 128 bit keying */ + +void bluedroid_aes_encrypt_128( const unsigned char in[N_BLOCK], unsigned char out[N_BLOCK], + const unsigned char key[N_BLOCK], unsigned char o_key[N_BLOCK] ) +{ + uint_8t s1[N_BLOCK], r, rc = 1; + + if (o_key != key) { + block_copy( o_key, key ); + } + copy_and_key( s1, in, o_key ); + + for ( r = 1 ; r < 10 ; ++r ) +#if defined( VERSION_1 ) + { + mix_sub_columns( s1 ); + update_encrypt_key_128( o_key, &rc ); + add_round_key( s1, o_key ); + } +#else + { + uint_8t s2[N_BLOCK]; + mix_sub_columns( s2, s1 ); + update_encrypt_key_128( o_key, &rc ); + copy_and_key( s1, s2, o_key ); + } +#endif + + shift_sub_rows( s1 ); + update_encrypt_key_128( o_key, &rc ); + copy_and_key( out, s1, o_key ); +} + +#endif + +#if defined( AES_DEC_128_OTFK ) + +/* The 'on the fly' decryption key update for for 128 bit keys */ + +static void update_decrypt_key_128( uint_8t k[N_BLOCK], uint_8t *rc ) +{ + uint_8t cc; + + for ( cc = 12; cc > 0; cc -= 4 ) { + k[cc + 0] ^= k[cc - 4]; + k[cc + 1] ^= k[cc - 3]; + k[cc + 2] ^= k[cc - 2]; + k[cc + 3] ^= k[cc - 1]; + } + *rc = d2(*rc); + k[0] ^= s_box(k[13]) ^ *rc; + k[1] ^= s_box(k[14]); + k[2] ^= s_box(k[15]); + k[3] ^= s_box(k[12]); +} + +/* Decrypt a single block of 16 bytes with 'on the fly' 128 bit keying */ + +void bluedroid_aes_decrypt_128( const unsigned char in[N_BLOCK], unsigned char out[N_BLOCK], + const unsigned char key[N_BLOCK], unsigned char o_key[N_BLOCK] ) +{ + uint_8t s1[N_BLOCK], r, rc = 0x6c; + if (o_key != key) { + block_copy( o_key, key ); + } + + copy_and_key( s1, in, o_key ); + inv_shift_sub_rows( s1 ); + + for ( r = 10 ; --r ; ) +#if defined( VERSION_1 ) + { + update_decrypt_key_128( o_key, &rc ); + add_round_key( s1, o_key ); + inv_mix_sub_columns( s1 ); + } +#else + { + uint_8t s2[N_BLOCK]; + update_decrypt_key_128( o_key, &rc ); + copy_and_key( s2, s1, o_key ); + inv_mix_sub_columns( s1, s2 ); + } +#endif + update_decrypt_key_128( o_key, &rc ); + copy_and_key( out, s1, o_key ); +} + +#endif + +#if defined( AES_ENC_256_OTFK ) + +/* The 'on the fly' encryption key update for for 256 bit keys */ + +static void update_encrypt_key_256( uint_8t k[2 * N_BLOCK], uint_8t *rc ) +{ + uint_8t cc; + + k[0] ^= s_box(k[29]) ^ *rc; + k[1] ^= s_box(k[30]); + k[2] ^= s_box(k[31]); + k[3] ^= s_box(k[28]); + *rc = f2( *rc ); + + for (cc = 4; cc < 16; cc += 4) { + k[cc + 0] ^= k[cc - 4]; + k[cc + 1] ^= k[cc - 3]; + k[cc + 2] ^= k[cc - 2]; + k[cc + 3] ^= k[cc - 1]; + } + + k[16] ^= s_box(k[12]); + k[17] ^= s_box(k[13]); + k[18] ^= s_box(k[14]); + k[19] ^= s_box(k[15]); + + for ( cc = 20; cc < 32; cc += 4 ) { + k[cc + 0] ^= k[cc - 4]; + k[cc + 1] ^= k[cc - 3]; + k[cc + 2] ^= k[cc - 2]; + k[cc + 3] ^= k[cc - 1]; + } +} + +/* Encrypt a single block of 16 bytes with 'on the fly' 256 bit keying */ + +void bluedroid_aes_encrypt_256( const unsigned char in[N_BLOCK], unsigned char out[N_BLOCK], + const unsigned char key[2 * N_BLOCK], unsigned char o_key[2 * N_BLOCK] ) +{ + uint_8t s1[N_BLOCK], r, rc = 1; + if (o_key != key) { + block_copy( o_key, key ); + block_copy( o_key + 16, key + 16 ); + } + copy_and_key( s1, in, o_key ); + + for ( r = 1 ; r < 14 ; ++r ) +#if defined( VERSION_1 ) + { + mix_sub_columns(s1); + if ( r & 1 ) { + add_round_key( s1, o_key + 16 ); + } else { + update_encrypt_key_256( o_key, &rc ); + add_round_key( s1, o_key ); + } + } +#else + { + uint_8t s2[N_BLOCK]; + mix_sub_columns( s2, s1 ); + if ( r & 1 ) { + copy_and_key( s1, s2, o_key + 16 ); + } else { + update_encrypt_key_256( o_key, &rc ); + copy_and_key( s1, s2, o_key ); + } + } +#endif + + shift_sub_rows( s1 ); + update_encrypt_key_256( o_key, &rc ); + copy_and_key( out, s1, o_key ); +} + +#endif + +#if defined( AES_DEC_256_OTFK ) + +/* The 'on the fly' encryption key update for for 256 bit keys */ + +static void update_decrypt_key_256( uint_8t k[2 * N_BLOCK], uint_8t *rc ) +{ + uint_8t cc; + + for (cc = 28; cc > 16; cc -= 4) { + k[cc + 0] ^= k[cc - 4]; + k[cc + 1] ^= k[cc - 3]; + k[cc + 2] ^= k[cc - 2]; + k[cc + 3] ^= k[cc - 1]; + } + + k[16] ^= s_box(k[12]); + k[17] ^= s_box(k[13]); + k[18] ^= s_box(k[14]); + k[19] ^= s_box(k[15]); + + for (cc = 12; cc > 0; cc -= 4) { + k[cc + 0] ^= k[cc - 4]; + k[cc + 1] ^= k[cc - 3]; + k[cc + 2] ^= k[cc - 2]; + k[cc + 3] ^= k[cc - 1]; + } + + *rc = d2(*rc); + k[0] ^= s_box(k[29]) ^ *rc; + k[1] ^= s_box(k[30]); + k[2] ^= s_box(k[31]); + k[3] ^= s_box(k[28]); +} + +/* Decrypt a single block of 16 bytes with 'on the fly' + 256 bit keying +*/ +void bluedroid_aes_decrypt_256( const unsigned char in[N_BLOCK], unsigned char out[N_BLOCK], + const unsigned char key[2 * N_BLOCK], unsigned char o_key[2 * N_BLOCK] ) +{ + uint_8t s1[N_BLOCK], r, rc = 0x80; + + if (o_key != key) { + block_copy( o_key, key ); + block_copy( o_key + 16, key + 16 ); + } + + copy_and_key( s1, in, o_key ); + inv_shift_sub_rows( s1 ); + + for ( r = 14 ; --r ; ) +#if defined( VERSION_1 ) + { + if ( ( r & 1 ) ) { + update_decrypt_key_256( o_key, &rc ); + add_round_key( s1, o_key + 16 ); + } else { + add_round_key( s1, o_key ); + } + inv_mix_sub_columns( s1 ); + } +#else + { + uint_8t s2[N_BLOCK]; + if ( ( r & 1 ) ) { + update_decrypt_key_256( o_key, &rc ); + copy_and_key( s2, s1, o_key + 16 ); + } else { + copy_and_key( s2, s1, o_key ); + } + inv_mix_sub_columns( s1, s2 ); + } +#endif + copy_and_key( out, s1, o_key ); +} + +#endif diff --git a/lib/bt/host/bluedroid/stack/smp/include/aes.h b/lib/bt/host/bluedroid/stack/smp/include/aes.h new file mode 100644 index 00000000..48495bb1 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/smp/include/aes.h @@ -0,0 +1,162 @@ +/* + --------------------------------------------------------------------------- + Copyright (c) 1998-2008, Brian Gladman, Worcester, UK. All rights reserved. + + LICENSE TERMS + + The redistribution and use of this software (with or without changes) + is allowed without the payment of fees or royalties provided that: + + 1. source code distributions include the above copyright notice, this + list of conditions and the following disclaimer; + + 2. binary distributions include the above copyright notice, this list + of conditions and the following disclaimer in their documentation; + + 3. the name of the copyright holder is not used to endorse products + built using this software without specific written permission. + + DISCLAIMER + + This software is provided 'as is' with no explicit or implied warranties + in respect of its properties, including, but not limited to, correctness + and/or fitness for purpose. + --------------------------------------------------------------------------- + Issue 09/09/2006 + + This is an AES implementation that uses only 8-bit byte operations on the + cipher state. + */ + +#ifndef AES_H +#define AES_H + +#if 1 +# define AES_ENC_PREKEYED /* AES encryption with a precomputed key schedule */ +#endif +#if 1 +# define AES_DEC_PREKEYED /* AES decryption with a precomputed key schedule */ +#endif +#if 1 +# define AES_ENC_128_OTFK /* AES encryption with 'on the fly' 128 bit keying */ +#endif +#if 1 +# define AES_DEC_128_OTFK /* AES decryption with 'on the fly' 128 bit keying */ +#endif +#if 1 +# define AES_ENC_256_OTFK /* AES encryption with 'on the fly' 256 bit keying */ +#endif +#if 1 +# define AES_DEC_256_OTFK /* AES decryption with 'on the fly' 256 bit keying */ +#endif + +#define N_ROW 4 +#define N_COL 4 +#define N_BLOCK (N_ROW * N_COL) +#define N_MAX_ROUNDS 14 + +typedef unsigned char uint_8t; + +typedef uint_8t return_type; + +/* Warning: The key length for 256 bit keys overflows a byte + (see comment below) +*/ + +typedef uint_8t length_type; + +typedef struct { + uint_8t ksch[(N_MAX_ROUNDS + 1) * N_BLOCK]; + uint_8t rnd; +} aes_context; + +/* The following calls are for a precomputed key schedule + + NOTE: If the length_type used for the key length is an + unsigned 8-bit character, a key length of 256 bits must + be entered as a length in bytes (valid inputs are hence + 128, 192, 16, 24 and 32). +*/ + +#if defined( AES_ENC_PREKEYED ) || defined( AES_DEC_PREKEYED ) + +return_type aes_set_key( const unsigned char key[], + length_type keylen, + aes_context ctx[1] ); +#endif + +#if defined( AES_ENC_PREKEYED ) + +return_type bluedroid_aes_encrypt( const unsigned char in[N_BLOCK], + unsigned char out[N_BLOCK], + const aes_context ctx[1] ); + +return_type aes_cbc_encrypt( const unsigned char *in, + unsigned char *out, + int n_block, + unsigned char iv[N_BLOCK], + const aes_context ctx[1] ); +#endif + +#if defined( AES_DEC_PREKEYED ) + +return_type bluedroid_aes_decrypt( const unsigned char in[N_BLOCK], + unsigned char out[N_BLOCK], + const aes_context ctx[1] ); + +return_type aes_cbc_decrypt( const unsigned char *in, + unsigned char *out, + int n_block, + unsigned char iv[N_BLOCK], + const aes_context ctx[1] ); +#endif + +/* The following calls are for 'on the fly' keying. In this case the + encryption and decryption keys are different. + + The encryption subroutines take a key in an array of bytes in + key[L] where L is 16, 24 or 32 bytes for key lengths of 128, + 192, and 256 bits respectively. They then encrypts the input + data, in[] with this key and put the reult in the output array + out[]. In addition, the second key array, o_key[L], is used + to output the key that is needed by the decryption subroutine + to reverse the encryption operation. The two key arrays can + be the same array but in this case the original key will be + overwritten. + + In the same way, the decryption subroutines output keys that + can be used to reverse their effect when used for encryption. + + Only 128 and 256 bit keys are supported in these 'on the fly' + modes. +*/ + +#if defined( AES_ENC_128_OTFK ) +void bluedroid_aes_encrypt_128( const unsigned char in[N_BLOCK], + unsigned char out[N_BLOCK], + const unsigned char key[N_BLOCK], + uint_8t o_key[N_BLOCK] ); +#endif + +#if defined( AES_DEC_128_OTFK ) +void bluedroid_aes_decrypt_128( const unsigned char in[N_BLOCK], + unsigned char out[N_BLOCK], + const unsigned char key[N_BLOCK], + unsigned char o_key[N_BLOCK] ); +#endif + +#if defined( AES_ENC_256_OTFK ) +void bluedroid_aes_encrypt_256( const unsigned char in[N_BLOCK], + unsigned char out[N_BLOCK], + const unsigned char key[2 * N_BLOCK], + unsigned char o_key[2 * N_BLOCK] ); +#endif + +#if defined( AES_DEC_256_OTFK ) +void bluedroid_aes_decrypt_256( const unsigned char in[N_BLOCK], + unsigned char out[N_BLOCK], + const unsigned char key[2 * N_BLOCK], + unsigned char o_key[2 * N_BLOCK] ); +#endif + +#endif diff --git a/lib/bt/host/bluedroid/stack/smp/include/p_256_ecc_pp.h b/lib/bt/host/bluedroid/stack/smp/include/p_256_ecc_pp.h new file mode 100644 index 00000000..3b28e0c9 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/smp/include/p_256_ecc_pp.h @@ -0,0 +1,74 @@ +/****************************************************************************** + * + * Copyright (C) 2006-2015 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains simple pairing algorithms using Elliptic Curve Cryptography for private public key + * + ******************************************************************************/ + +#pragma once + +#include "p_256_multprecision.h" +#include "common/bt_target.h" + +typedef unsigned long DWORD; + +typedef struct { + DWORD x[KEY_LENGTH_DWORDS_P256]; + DWORD y[KEY_LENGTH_DWORDS_P256]; + DWORD z[KEY_LENGTH_DWORDS_P256]; +} Point; + +typedef struct { + // curve's coefficients + DWORD a[KEY_LENGTH_DWORDS_P256]; + DWORD b[KEY_LENGTH_DWORDS_P256]; + + //whether a is -3 + int a_minus3; + + // prime modulus + DWORD p[KEY_LENGTH_DWORDS_P256]; + + // Omega, p = 2^m -omega + DWORD omega[KEY_LENGTH_DWORDS_P256]; + + // base point, a point on E of order r + Point G; + +} elliptic_curve_t; + +#if SMP_DYNAMIC_MEMORY == FALSE +extern elliptic_curve_t curve; +extern elliptic_curve_t curve_p256; +#else +extern elliptic_curve_t *curve_ptr; +extern elliptic_curve_t *curve_p256_ptr; +#define curve (*curve_ptr) +#define curve_p256 (*curve_p256_ptr) +#endif + + +void ECC_PointMult_Bin_NAF(Point *q, Point *p, DWORD *n, uint32_t keyLength); + +bool ECC_CheckPointIsInElliCur_P256(Point *p); + +#define ECC_PointMult(q, p, n, keyLength) ECC_PointMult_Bin_NAF(q, p, n, keyLength) + +void p_256_init_curve(UINT32 keyLength); diff --git a/lib/bt/host/bluedroid/stack/smp/include/p_256_multprecision.h b/lib/bt/host/bluedroid/stack/smp/include/p_256_multprecision.h new file mode 100644 index 00000000..0a33b4e2 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/smp/include/p_256_multprecision.h @@ -0,0 +1,60 @@ +/****************************************************************************** + * + * Copyright (C) 2006-2015 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains simple pairing algorithms + * + ******************************************************************************/ +#pragma once + +#include "stack/bt_types.h" + +/* Type definitions */ +typedef unsigned long DWORD; + +#define DWORD_BITS 32 +#define DWORD_BYTES 4 +#define DWORD_BITS_SHIFT 5 + +#define KEY_LENGTH_DWORDS_P192 6 +#define KEY_LENGTH_DWORDS_P256 8 +/* Arithmetic Operations */ + +int multiprecision_compare(DWORD *a, DWORD *b, uint32_t keyLength); +int multiprecision_iszero(DWORD *a, uint32_t keyLength); +void multiprecision_init(DWORD *c, uint32_t keyLength); +void multiprecision_copy(DWORD *c, DWORD *a, uint32_t keyLength); +UINT32 multiprecision_dword_bits (DWORD a); +UINT32 multiprecision_most_signdwords(DWORD *a, uint32_t keyLength); +UINT32 multiprecision_most_signbits(DWORD *a, uint32_t keyLength); +void multiprecision_inv_mod(DWORD *aminus, DWORD *a, uint32_t keyLength); +DWORD multiprecision_add(DWORD *c, DWORD *a, DWORD *b, uint32_t keyLength); // c=a+b +void multiprecision_add_mod(DWORD *c, DWORD *a, DWORD *b, uint32_t keyLength); +DWORD multiprecision_sub(DWORD *c, DWORD *a, DWORD *b, uint32_t keyLength); // c=a-b +void multiprecision_sub_mod(DWORD *c, DWORD *a, DWORD *b, uint32_t keyLength); +void multiprecision_rshift(DWORD *c, DWORD *a, uint32_t keyLength); // c=a>>1, return carrier +void multiprecision_lshift_mod(DWORD *c, DWORD *a, uint32_t keyLength); // c=a<rand */ + +#define SMP_SC_PHASE1_CMPLT_EVT (SMP_SELF_DEF_EVT + 16) /* time to start SC phase2 */ + +#define SMP_SC_CALC_NC_EVT (SMP_SELF_DEF_EVT + 17) /* request to calculate number */ +/* for user check. Used only in the */ +/* numeric compare protocol */ + +/* Request to display the number for user check to the user.*/ +/* Used only in the numeric compare protocol */ +#define SMP_SC_DSPL_NC_EVT (SMP_SELF_DEF_EVT + 18) + +#define SMP_SC_NC_OK_EVT (SMP_SELF_DEF_EVT + 19) /* user confirms 'OK' numeric */ +/*comparison request */ + +/* both local and peer DHKey Checks are already present - it is used on slave to prevent race condition */ +#define SMP_SC_2_DHCK_CHKS_PRES_EVT (SMP_SELF_DEF_EVT + 20) + +/* same meaning as SMP_KEY_READY_EVT to separate between SC and legacy actions */ +#define SMP_SC_KEY_READY_EVT (SMP_SELF_DEF_EVT + 21) +#define SMP_KEYPRESS_NOTIFICATION_EVENT (SMP_SELF_DEF_EVT + 22) + +#define SMP_SC_OOB_DATA_EVT (SMP_SELF_DEF_EVT + 23) /* SC OOB data from some */ +/* repository is provided */ + +#define SMP_CR_LOC_SC_OOB_DATA_EVT (SMP_SELF_DEF_EVT + 24) +#define SMP_MAX_EVT SMP_CR_LOC_SC_OOB_DATA_EVT + +typedef UINT8 tSMP_EVENT; + +/* Assumption it's only using the low 8 bits, if bigger than that, need to expand it to 16 bits */ +#define SMP_SEC_KEY_MASK 0x00ff + +#define SMP_PASSKEY_MASK 0xfff00000 + +/* SMP pairing state */ +enum { + SMP_STATE_IDLE, + SMP_STATE_WAIT_APP_RSP, + SMP_STATE_SEC_REQ_PENDING, + SMP_STATE_PAIR_REQ_RSP, + SMP_STATE_WAIT_CONFIRM, + SMP_STATE_CONFIRM, + SMP_STATE_RAND, + SMP_STATE_PUBLIC_KEY_EXCH, + SMP_STATE_SEC_CONN_PHS1_START, + SMP_STATE_WAIT_COMMITMENT, + SMP_STATE_WAIT_NONCE, + SMP_STATE_SEC_CONN_PHS2_START, + SMP_STATE_WAIT_DHK_CHECK, + SMP_STATE_DHK_CHECK, + SMP_STATE_ENCRYPTION_PENDING, + SMP_STATE_BOND_PENDING, + SMP_STATE_CREATE_LOCAL_SEC_CONN_OOB_DATA, + SMP_STATE_MAX +}; +typedef UINT8 tSMP_STATE; + +/* SMP over BR/EDR events */ +#define SMP_BR_PAIRING_REQ_EVT SMP_OPCODE_PAIRING_REQ +#define SMP_BR_PAIRING_RSP_EVT SMP_OPCODE_PAIRING_RSP +#define SMP_BR_CONFIRM_EVT SMP_OPCODE_CONFIRM /* not expected over BR/EDR */ +#define SMP_BR_RAND_EVT SMP_OPCODE_RAND /* not expected over BR/EDR */ +#define SMP_BR_PAIRING_FAILED_EVT SMP_OPCODE_PAIRING_FAILED +#define SMP_BR_ENCRPTION_INFO_EVT SMP_OPCODE_ENCRYPT_INFO /* not expected over BR/EDR */ +#define SMP_BR_MASTER_ID_EVT SMP_OPCODE_MASTER_ID /* not expected over BR/EDR */ +#define SMP_BR_ID_INFO_EVT SMP_OPCODE_IDENTITY_INFO +#define SMP_BR_ID_ADDR_EVT SMP_OPCODE_ID_ADDR +#define SMP_BR_SIGN_INFO_EVT SMP_OPCODE_SIGN_INFO +#define SMP_BR_SECURITY_REQ_EVT SMP_OPCODE_SEC_REQ /* not expected over BR/EDR */ +#define SMP_BR_PAIR_PUBLIC_KEY_EVT SMP_OPCODE_PAIR_PUBLIC_KEY /* not expected over BR/EDR */ +#define SMP_BR_PAIR_DHKEY_CHCK_EVT SMP_OPCODE_PAIR_DHKEY_CHECK /* not expected over BR/EDR */ +#define SMP_BR_PAIR_KEYPR_NOTIF_EVT SMP_OPCODE_PAIR_KEYPR_NOTIF /* not expected over BR/EDR */ +#define SMP_BR_SELF_DEF_EVT SMP_BR_PAIR_KEYPR_NOTIF_EVT +#define SMP_BR_KEY_READY_EVT (SMP_BR_SELF_DEF_EVT + 1) +#define SMP_BR_ENCRYPTED_EVT (SMP_BR_SELF_DEF_EVT + 2) +#define SMP_BR_L2CAP_CONN_EVT (SMP_BR_SELF_DEF_EVT + 3) +#define SMP_BR_L2CAP_DISCONN_EVT (SMP_BR_SELF_DEF_EVT + 4) +#define SMP_BR_KEYS_RSP_EVT (SMP_BR_SELF_DEF_EVT + 5) +#define SMP_BR_API_SEC_GRANT_EVT (SMP_BR_SELF_DEF_EVT + 6) +#define SMP_BR_TK_REQ_EVT (SMP_BR_SELF_DEF_EVT + 7) +#define SMP_BR_AUTH_CMPL_EVT (SMP_BR_SELF_DEF_EVT + 8) +#define SMP_BR_ENC_REQ_EVT (SMP_BR_SELF_DEF_EVT + 9) +#define SMP_BR_BOND_REQ_EVT (SMP_BR_SELF_DEF_EVT + 10) +#define SMP_BR_DISCARD_SEC_REQ_EVT (SMP_BR_SELF_DEF_EVT + 11) +#define SMP_BR_MAX_EVT (SMP_BR_SELF_DEF_EVT + 12) +typedef UINT8 tSMP_BR_EVENT; + +/* SMP over BR/EDR pairing states */ +enum { + SMP_BR_STATE_IDLE = SMP_STATE_IDLE, + SMP_BR_STATE_WAIT_APP_RSP, + SMP_BR_STATE_PAIR_REQ_RSP, + SMP_BR_STATE_BOND_PENDING, + SMP_BR_STATE_MAX +}; +typedef UINT8 tSMP_BR_STATE; + +/* random and encrption activity state */ +enum { + SMP_GEN_COMPARE = 1, + SMP_GEN_CONFIRM, + + SMP_GEN_DIV_LTK, + SMP_GEN_DIV_CSRK, + SMP_GEN_RAND_V, + SMP_GEN_TK, + SMP_GEN_SRAND_MRAND, + SMP_GEN_SRAND_MRAND_CONT, + SMP_GENERATE_PRIVATE_KEY_0_7, + SMP_GENERATE_PRIVATE_KEY_8_15, + SMP_GENERATE_PRIVATE_KEY_16_23, + SMP_GENERATE_PRIVATE_KEY_24_31, + SMP_GEN_NONCE_0_7, + SMP_GEN_NONCE_8_15 +}; + +enum { + SMP_KEY_TYPE_TK, + SMP_KEY_TYPE_CFM, + SMP_KEY_TYPE_CMP, + SMP_KEY_TYPE_PEER_DHK_CHCK, + SMP_KEY_TYPE_STK, + SMP_KEY_TYPE_LTK +}; +typedef struct { + UINT8 key_type; + UINT8 *p_data; +} tSMP_KEY; + +typedef union { + UINT8 *p_data; /* UINT8 type data pointer */ + tSMP_KEY key; + UINT16 reason; + UINT32 passkey; + tSMP_OOB_DATA_TYPE req_oob_type; +} tSMP_INT_DATA; + +/* internal status mask */ +#define SMP_PAIR_FLAGS_WE_STARTED_DD (1) +#define SMP_PAIR_FLAGS_PEER_STARTED_DD (1 << 1) +#define SMP_PAIR_FLAGS_CMD_CONFIRM (1 << SMP_OPCODE_CONFIRM) /* 1 << 3 */ +#define SMP_PAIR_FLAG_ENC_AFTER_PAIR (1 << 4) +#define SMP_PAIR_FLAG_HAVE_PEER_DHK_CHK (1 << 5) /* used on slave to resolve race condition */ +#define SMP_PAIR_FLAG_HAVE_PEER_PUBL_KEY (1 << 6) /* used on slave to resolve race condition */ +#define SMP_PAIR_FLAG_HAVE_PEER_COMM (1 << 7) /* used to resolve race condition */ +#define SMP_PAIR_FLAG_HAVE_LOCAL_PUBL_KEY (1 << 8) /* used on slave to resolve race condition */ + +/* check if authentication requirement need MITM protection */ +#define SMP_NO_MITM_REQUIRED(x) (((x) & SMP_AUTH_YN_BIT) == 0) + +#define SMP_ENCRYT_KEY_SIZE 16 +#define SMP_ENCRYT_DATA_SIZE 16 +#define SMP_ECNCRPYT_STATUS HCI_SUCCESS + +typedef struct { + BD_ADDR bd_addr; + BT_HDR *p_copy; +} tSMP_REQ_Q_ENTRY; + +/* SMP control block */ +typedef struct { + tSMP_CALLBACK *p_callback; + TIMER_LIST_ENT rsp_timer_ent; + UINT8 trace_level; + BD_ADDR pairing_bda; + tSMP_STATE state; + BOOLEAN derive_lk; + BOOLEAN id_addr_rcvd; + tBLE_ADDR_TYPE id_addr_type; + BD_ADDR id_addr; + BOOLEAN smp_over_br; + tSMP_BR_STATE br_state; /* if SMP over BR/ERD has priority over SMP */ + UINT8 failure; + UINT8 status; + UINT8 role; + UINT16 flags; + UINT8 cb_evt; + tSMP_SEC_LEVEL sec_level; + BOOLEAN connect_initialized; + BT_OCTET16 confirm; + BT_OCTET16 rconfirm; + BT_OCTET16 rrand; /* for SC this is peer nonce */ + BT_OCTET16 rand; /* for SC this is local nonce */ + BT_OCTET32 private_key; + BT_OCTET32 dhkey; + BT_OCTET16 commitment; + BT_OCTET16 remote_commitment; + BT_OCTET16 local_random; /* local randomizer - passkey or OOB randomizer */ + BT_OCTET16 peer_random; /* peer randomizer - passkey or OOB randomizer */ + BT_OCTET16 dhkey_check; + BT_OCTET16 remote_dhkey_check; + tSMP_PUBLIC_KEY loc_publ_key; + tSMP_PUBLIC_KEY peer_publ_key; + tSMP_OOB_DATA_TYPE req_oob_type; + tSMP_SC_OOB_DATA sc_oob_data; + tSMP_IO_CAP peer_io_caps; + tSMP_IO_CAP local_io_capability; + tSMP_OOB_FLAG peer_oob_flag; + tSMP_OOB_FLAG loc_oob_flag; + tSMP_AUTH_REQ peer_auth_req; + tSMP_AUTH_REQ loc_auth_req; + tSMP_AUTH_REQ auth_mode; + BOOLEAN secure_connections_only_mode_required;/* TRUE if locally SM is required to operate */ + /* either in Secure Connections mode or not at all */ + tSMP_ASSO_MODEL selected_association_model; + BOOLEAN le_secure_connections_mode_is_used; + BOOLEAN le_sc_kp_notif_is_used; + tSMP_SC_KEY_TYPE local_keypress_notification; + tSMP_SC_KEY_TYPE peer_keypress_notification; + UINT8 round; /* authentication stage 1 round for passkey association model */ + UINT32 number_to_display; + BT_OCTET16 mac_key; + UINT8 peer_enc_size; + UINT8 loc_enc_size; + UINT8 peer_i_key; + UINT8 peer_r_key; + UINT8 local_i_key; + UINT8 local_r_key; + + BT_OCTET16 tk; + BT_OCTET16 ltk; + UINT16 div; + BT_OCTET16 csrk; /* storage for local CSRK */ + UINT16 ediv; + BT_OCTET8 enc_rand; + UINT8 rand_enc_proc_state; + UINT8 addr_type; + BD_ADDR local_bda; + BOOLEAN is_pair_cancel; + BOOLEAN discard_sec_req; + UINT8 rcvd_cmd_code; + UINT8 rcvd_cmd_len; + UINT16 total_tx_unacked; + BOOLEAN wait_for_authorization_complete; + BOOLEAN use_static_passkey; + UINT32 static_passkey; + BOOLEAN accept_specified_sec_auth; + tSMP_AUTH_REQ origin_loc_auth_req; +} tSMP_CB; + +/* Server Action functions are of this type */ +typedef void (*tSMP_ACT)(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); + + +#ifdef __cplusplus +extern "C" +{ +#endif + +#if SMP_DYNAMIC_MEMORY == FALSE +extern tSMP_CB smp_cb; +#else +extern tSMP_CB *smp_cb_ptr; +#define smp_cb (*smp_cb_ptr) +#endif + +#ifdef __cplusplus +} +#endif + +/* Functions provided by att_main.c */ +extern void smp_init (void); + +/* smp main */ +extern void smp_sm_event(tSMP_CB *p_cb, tSMP_EVENT event, void *p_data); + +extern void smp_proc_sec_request(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_set_fail_nc (BOOLEAN enable); +extern void smp_set_fail_conf (BOOLEAN enable); +extern void smp_set_passk_entry_fail(BOOLEAN enable); +extern void smp_set_oob_fail(BOOLEAN enable); +extern void smp_set_peer_sc_notif(BOOLEAN enable); +extern void smp_aes_cmac_rfc4493_chk (UINT8 *key, UINT8 *msg, UINT8 msg_len, + UINT8 mac_len, UINT8 *mac); +extern void smp_f4_calc_chk (UINT8 *U, UINT8 *V, UINT8 *X, UINT8 *Z, UINT8 *mac); +extern void smp_g2_calc_chk (UINT8 *U, UINT8 *V, UINT8 *X, UINT8 *Y); +extern void smp_h6_calc_chk (UINT8 *key, UINT8 *key_id, UINT8 *mac); +extern void smp_f5_key_calc_chk (UINT8 *w, UINT8 *mac); +extern void smp_f5_mackey_or_ltk_calc_chk(UINT8 *t, UINT8 *counter, + UINT8 *key_id, UINT8 *n1, + UINT8 *n2, UINT8 *a1, UINT8 *a2, + UINT8 *length, UINT8 *mac); +extern void smp_f5_calc_chk (UINT8 *w, UINT8 *n1, UINT8 *n2, UINT8 *a1, UINT8 *a2, + UINT8 *mac_key, UINT8 *ltk); +extern void smp_f6_calc_chk (UINT8 *w, UINT8 *n1, UINT8 *n2, UINT8 *r, + UINT8 *iocap, UINT8 *a1, UINT8 *a2, UINT8 *mac); +/* smp_main */ +extern void smp_sm_event(tSMP_CB *p_cb, tSMP_EVENT event, void *p_data); +extern tSMP_STATE smp_get_state(void); +extern void smp_set_state(tSMP_STATE state); + +/* smp_br_main */ +extern void smp_br_state_machine_event(tSMP_CB *p_cb, tSMP_BR_EVENT event, void *p_data); +extern tSMP_BR_STATE smp_get_br_state(void); +extern void smp_set_br_state(tSMP_BR_STATE state); + + +/* smp_act.c */ +extern void smp_send_pair_req(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_send_confirm(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_send_pair_fail(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_send_rand(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_send_pair_public_key(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_send_commitment(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_send_dhkey_check(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_send_keypress_notification(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_proc_pair_fail(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_proc_confirm(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_proc_rand(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_process_pairing_public_key(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_proc_enc_info(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_proc_master_id(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_proc_id_info(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_proc_id_addr(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_proc_sec_grant(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_proc_sec_req(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_proc_sl_key(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_start_enc(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_enc_cmpl(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_proc_discard(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_pairing_cmpl(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_decide_association_model(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_send_app_cback(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_proc_compare(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_check_auth_req(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_process_io_response(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_send_id_info(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_send_enc_info(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_send_csrk_info(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_send_ltk_reply(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_proc_pair_cmd(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_pair_terminate(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_idle_terminate(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_send_pair_rsp(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_key_distribution(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_proc_srk_info(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_generate_csrk(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_fast_conn_param(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_key_pick_key(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_both_have_public_keys(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_start_secure_connection_phase1(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_process_local_nonce(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_process_pairing_commitment(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_process_peer_nonce(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_process_dhkey_check(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_match_dhkey_checks(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_process_keypress_notification(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_move_to_secure_connections_phase2(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_phase_2_dhkey_checks_are_present(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_wait_for_both_public_keys(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_start_passkey_verification(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_process_secure_connection_oob_data(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_process_secure_connection_long_term_key(void); +extern void smp_set_local_oob_keys(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_set_local_oob_random_commitment(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_set_derive_link_key(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_derive_link_key_from_long_term_key(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_br_process_pairing_command(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_br_process_security_grant(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_br_process_slave_keys_response(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_br_send_pair_response(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_br_check_authorization_request(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_br_select_next_key(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_br_process_link_key(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_key_distribution_by_transport(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_br_pairing_complete(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); + +/* smp_l2c */ +extern void smp_l2cap_if_init (void); +extern void smp_data_ind (BD_ADDR bd_addr, BT_HDR *p_buf); + +/* smp_util.c */ +extern BOOLEAN smp_send_cmd(UINT8 cmd_code, tSMP_CB *p_cb); +extern void smp_cb_cleanup(tSMP_CB *p_cb); +extern void smp_reset_control_value(tSMP_CB *p_cb); +extern void smp_proc_pairing_cmpl(tSMP_CB *p_cb); +extern void smp_convert_string_to_tk(BT_OCTET16 tk, UINT32 passkey); +extern void smp_mask_enc_key(UINT8 loc_enc_size, UINT8 *p_data); +extern void smp_rsp_timeout(TIMER_LIST_ENT *p_tle); +extern void smp_xor_128(BT_OCTET16 a, const BT_OCTET16 b); +extern BOOLEAN smp_encrypt_data (UINT8 *key, UINT8 key_len, + UINT8 *plain_text, UINT8 pt_len, + tSMP_ENC *p_out); +extern BOOLEAN smp_command_has_invalid_parameters(tSMP_CB *p_cb); +extern void smp_reject_unexpected_pairing_command(BD_ADDR bd_addr); +extern tSMP_ASSO_MODEL smp_select_association_model(tSMP_CB *p_cb); +extern void smp_reverse_array(UINT8 *arr, UINT8 len); +extern UINT8 smp_calculate_random_input(UINT8 *random, UINT8 round); +extern void smp_collect_local_io_capabilities(UINT8 *iocap, tSMP_CB *p_cb); +extern void smp_collect_peer_io_capabilities(UINT8 *iocap, tSMP_CB *p_cb); +extern void smp_collect_local_ble_address(UINT8 *le_addr, tSMP_CB *p_cb); +extern void smp_collect_peer_ble_address(UINT8 *le_addr, tSMP_CB *p_cb); +extern BOOLEAN smp_check_commitment(tSMP_CB *p_cb); +extern void smp_save_secure_connections_long_term_key(tSMP_CB *p_cb); +extern BOOLEAN smp_calculate_f5_mackey_and_long_term_key(tSMP_CB *p_cb); +extern void smp_remove_fixed_channel(tSMP_CB *p_cb); +extern BOOLEAN smp_request_oob_data(tSMP_CB *p_cb); + +/* smp_keys.c */ +extern void smp_generate_srand_mrand_confirm (tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_generate_compare (tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_generate_stk (tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_generate_ltk(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_generate_passkey (tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_generate_rand_cont(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_create_private_key(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_use_oob_private_key(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_compute_dhkey(tSMP_CB *p_cb); +extern void smp_calculate_local_commitment(tSMP_CB *p_cb); +extern void smp_calculate_peer_commitment(tSMP_CB *p_cb, BT_OCTET16 output_buf); +extern void smp_calculate_numeric_comparison_display_number(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_calculate_local_dhkey_check(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_calculate_peer_dhkey_check(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_start_nonce_generation(tSMP_CB *p_cb); +extern BOOLEAN smp_calculate_link_key_from_long_term_key(tSMP_CB *p_cb); +extern BOOLEAN smp_calculate_long_term_key_from_link_key(tSMP_CB *p_cb); +extern void smp_calculate_f4(UINT8 *u, UINT8 *v, UINT8 *x, UINT8 z, UINT8 *c); +extern UINT32 smp_calculate_g2(UINT8 *u, UINT8 *v, UINT8 *x, UINT8 *y); +extern BOOLEAN smp_calculate_f5(UINT8 *w, UINT8 *n1, UINT8 *n2, UINT8 *a1, UINT8 *a2, + UINT8 *mac_key, UINT8 *ltk); +extern BOOLEAN smp_calculate_f5_mackey_or_long_term_key(UINT8 *t, UINT8 *counter, + UINT8 *key_id, UINT8 *n1, UINT8 *n2, UINT8 *a1, + UINT8 *a2, UINT8 *length, UINT8 *mac); +extern BOOLEAN smp_calculate_f5_key(UINT8 *w, UINT8 *t); +extern BOOLEAN smp_calculate_f6(UINT8 *w, UINT8 *n1, UINT8 *n2, UINT8 *r, UINT8 *iocap, + UINT8 *a1, UINT8 *a2, UINT8 *f3); +extern BOOLEAN smp_calculate_h6(UINT8 *w, UINT8 *keyid, UINT8 *h2); +extern void smp_save_local_oob_data(tSMP_CB *p_cb); +extern void smp_clear_local_oob_data(void); +extern tSMP_LOC_OOB_DATA *smp_get_local_oob_data(void); +#if SMP_DEBUG == TRUE +extern void smp_debug_print_nbyte_little_endian (UINT8 *p, const UINT8 *key_name, + UINT8 len); +#endif + +/* smp_cmac.c */ +extern BOOLEAN aes_cipher_msg_auth_code(BT_OCTET16 key, UINT8 *input, UINT16 length, + UINT16 tlen, UINT8 *p_signature); +extern void print128(BT_OCTET16 x, const UINT8 *key_name); + +// #endif ///BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE + +#endif /* SMP_INT_H */ diff --git a/lib/bt/host/bluedroid/stack/smp/p_256_curvepara.c b/lib/bt/host/bluedroid/stack/smp/p_256_curvepara.c new file mode 100644 index 00000000..abf9a8ee --- /dev/null +++ b/lib/bt/host/bluedroid/stack/smp/p_256_curvepara.c @@ -0,0 +1,78 @@ +/****************************************************************************** + * + * Copyright (C) 2006-2015 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains simple pairing algorithms + * + ******************************************************************************/ + +#include +#include "p_256_ecc_pp.h" + +void p_256_init_curve(UINT32 keyLength) +{ + elliptic_curve_t *ec; + + if (keyLength == KEY_LENGTH_DWORDS_P256) { + ec = &curve_p256; + + ec->p[7] = 0xFFFFFFFF; + ec->p[6] = 0x00000001; + ec->p[5] = 0x0; + ec->p[4] = 0x0; + ec->p[3] = 0x0; + ec->p[2] = 0xFFFFFFFF; + ec->p[1] = 0xFFFFFFFF; + ec->p[0] = 0xFFFFFFFF; + + memset(ec->omega, 0, KEY_LENGTH_DWORDS_P256 * sizeof(ec->omega[0])); + memset(ec->a, 0, KEY_LENGTH_DWORDS_P256 * sizeof(ec->a[0])); + + ec->a_minus3 = TRUE; + + //b + ec->b[7] = 0x5ac635d8; + ec->b[6] = 0xaa3a93e7; + ec->b[5] = 0xb3ebbd55; + ec->b[4] = 0x769886bc; + ec->b[3] = 0x651d06b0; + ec->b[2] = 0xcc53b0f6; + ec->b[1] = 0x3bce3c3e; + ec->b[0] = 0x27d2604b; + + //base point + ec->G.x[7] = 0x6b17d1f2; + ec->G.x[6] = 0xe12c4247; + ec->G.x[5] = 0xf8bce6e5; + ec->G.x[4] = 0x63a440f2; + ec->G.x[3] = 0x77037d81; + ec->G.x[2] = 0x2deb33a0; + ec->G.x[1] = 0xf4a13945; + ec->G.x[0] = 0xd898c296; + + ec->G.y[7] = 0x4fe342e2; + ec->G.y[6] = 0xfe1a7f9b; + ec->G.y[5] = 0x8ee7eb4a; + ec->G.y[4] = 0x7c0f9e16; + ec->G.y[3] = 0x2bce3357; + ec->G.y[2] = 0x6b315ece; + ec->G.y[1] = 0xcbb64068; + ec->G.y[0] = 0x37bf51f5; + } +} diff --git a/lib/bt/host/bluedroid/stack/smp/p_256_ecc_pp.c b/lib/bt/host/bluedroid/stack/smp/p_256_ecc_pp.c new file mode 100644 index 00000000..1f49c652 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/smp/p_256_ecc_pp.c @@ -0,0 +1,283 @@ +/****************************************************************************** + * + * Copyright (C) 2006-2015 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains simple pairing algorithms using Elliptic Curve Cryptography for private public key + * + ******************************************************************************/ +//#include +//#include +#include +#include "p_256_ecc_pp.h" +#include "p_256_multprecision.h" +#include "common/bt_target.h" + +#if SMP_DYNAMIC_MEMORY == FALSE +elliptic_curve_t curve; +elliptic_curve_t curve_p256; +#else +elliptic_curve_t *curve_ptr; +elliptic_curve_t *curve_p256_ptr; +#endif + +static void p_256_init_point(Point *q) +{ + memset(q, 0, sizeof(Point)); +} + +static void p_256_copy_point(Point *q, Point *p) +{ + memcpy(q, p, sizeof(Point)); +} + +// q=2q +static void ECC_Double(Point *q, Point *p, uint32_t keyLength) +{ + DWORD t1[KEY_LENGTH_DWORDS_P256]; + DWORD t2[KEY_LENGTH_DWORDS_P256]; + DWORD t3[KEY_LENGTH_DWORDS_P256]; + DWORD *x1; + DWORD *x3; + DWORD *y1; + DWORD *y3; + DWORD *z1; + DWORD *z3; + + if (multiprecision_iszero(p->z, keyLength)) { + multiprecision_init(q->z, keyLength); + return; // return infinity + } + + x1 = p->x; y1 = p->y; z1 = p->z; + x3 = q->x; y3 = q->y; z3 = q->z; + + multiprecision_mersenns_squa_mod(t1, z1, keyLength); // t1=z1^2 + multiprecision_sub_mod(t2, x1, t1, keyLength); // t2=x1-t1 + multiprecision_add_mod(t1, x1, t1, keyLength); // t1=x1+t1 + multiprecision_mersenns_mult_mod(t2, t1, t2, keyLength); // t2=t2*t1 + multiprecision_lshift_mod(t3, t2, keyLength); + multiprecision_add_mod(t2, t3, t2, keyLength); // t2=3t2 + + multiprecision_mersenns_mult_mod(z3, y1, z1, keyLength); // z3=y1*z1 + multiprecision_lshift_mod(z3, z3, keyLength); + + multiprecision_mersenns_squa_mod(y3, y1, keyLength); // y3=y1^2 + multiprecision_lshift_mod(y3, y3, keyLength); + multiprecision_mersenns_mult_mod(t3, y3, x1, keyLength); // t3=y3*x1=x1*y1^2 + multiprecision_lshift_mod(t3, t3, keyLength); + multiprecision_mersenns_squa_mod(y3, y3, keyLength); // y3=y3^2=y1^4 + multiprecision_lshift_mod(y3, y3, keyLength); + + multiprecision_mersenns_squa_mod(x3, t2, keyLength); // x3=t2^2 + multiprecision_lshift_mod(t1, t3, keyLength); // t1=2t3 + multiprecision_sub_mod(x3, x3, t1, keyLength); // x3=x3-t1 + multiprecision_sub_mod(t1, t3, x3, keyLength); // t1=t3-x3 + multiprecision_mersenns_mult_mod(t1, t1, t2, keyLength); // t1=t1*t2 + multiprecision_sub_mod(y3, t1, y3, keyLength); // y3=t1-y3 +} + +// q=q+p, zp must be 1 +static void ECC_Add(Point *r, Point *p, Point *q, uint32_t keyLength) +{ + DWORD t1[KEY_LENGTH_DWORDS_P256]; + DWORD t2[KEY_LENGTH_DWORDS_P256]; + DWORD *x1; + DWORD *x2; + DWORD *x3; + DWORD *y1; + DWORD *y2; + DWORD *y3; + DWORD *z1; + DWORD *z2; + DWORD *z3; + + x1 = p->x; y1 = p->y; z1 = p->z; + x2 = q->x; y2 = q->y; z2 = q->z; + x3 = r->x; y3 = r->y; z3 = r->z; + + // if Q=infinity, return p + if (multiprecision_iszero(z2, keyLength)) { + p_256_copy_point(r, p); + return; + } + + // if P=infinity, return q + if (multiprecision_iszero(z1, keyLength)) { + p_256_copy_point(r, q); + return; + } + + multiprecision_mersenns_squa_mod(t1, z1, keyLength); // t1=z1^2 + multiprecision_mersenns_mult_mod(t2, z1, t1, keyLength); // t2=t1*z1 + multiprecision_mersenns_mult_mod(t1, x2, t1, keyLength); // t1=t1*x2 + multiprecision_mersenns_mult_mod(t2, y2, t2, keyLength); // t2=t2*y2 + + multiprecision_sub_mod(t1, t1, x1, keyLength); // t1=t1-x1 + multiprecision_sub_mod(t2, t2, y1, keyLength); // t2=t2-y1 + + if (multiprecision_iszero(t1, keyLength)) { + if (multiprecision_iszero(t2, keyLength)) { + ECC_Double(r, q, keyLength) ; + return; + } else { + multiprecision_init(z3, keyLength); + return; // return infinity + } + } + + multiprecision_mersenns_mult_mod(z3, z1, t1, keyLength); // z3=z1*t1 + multiprecision_mersenns_squa_mod(y3, t1, keyLength); // t3=t1^2 + multiprecision_mersenns_mult_mod(z1, y3, t1, keyLength); // t4=t3*t1 + multiprecision_mersenns_mult_mod(y3, y3, x1, keyLength); // t3=t3*x1 + multiprecision_lshift_mod(t1, y3, keyLength); // t1=2*t3 + multiprecision_mersenns_squa_mod(x3, t2, keyLength); // x3=t2^2 + multiprecision_sub_mod(x3, x3, t1, keyLength); // x3=x3-t1 + multiprecision_sub_mod(x3, x3, z1, keyLength); // x3=x3-t4 + multiprecision_sub_mod(y3, y3, x3, keyLength); // t3=t3-x3 + multiprecision_mersenns_mult_mod(y3, y3, t2, keyLength); // t3=t3*t2 + multiprecision_mersenns_mult_mod(z1, z1, y1, keyLength); // t4=t4*t1 + multiprecision_sub_mod(y3, y3, z1, keyLength); +} + +// Computing the Non-Adjacent Form of a positive integer +static void ECC_NAF(uint8_t *naf, uint32_t *NumNAF, DWORD *k, uint32_t keyLength) +{ + uint32_t sign; + int i = 0; + int j; + uint32_t var; + + while ((var = multiprecision_most_signbits(k, keyLength)) >= 1) { + if (k[0] & 0x01) { // k is odd + sign = (k[0] & 0x03); // 1 or 3 + + // k = k-naf[i] + if (sign == 1) { + k[0] = k[0] & 0xFFFFFFFE; + } else { + k[0] = k[0] + 1; + if (k[0] == 0) { //overflow + j = 1; + do { + k[j]++; + } while (k[j++] == 0); //overflow + } + } + } else { + sign = 0; + } + + multiprecision_rshift(k, k, keyLength); + naf[i / 4] |= (sign) << ((i % 4) * 2); + i++; + } + + *NumNAF = i; +} + +// Binary Non-Adjacent Form for point multiplication +void ECC_PointMult_Bin_NAF(Point *q, Point *p, DWORD *n, uint32_t keyLength) +{ + uint32_t sign; + UINT8 naf[256 / 4 + 1]; + uint32_t NumNaf; + Point minus_p; + Point r; + DWORD *modp; + + if (keyLength == KEY_LENGTH_DWORDS_P256) { + modp = curve_p256.p; + } else { + modp = curve.p; + } + + p_256_init_point(&r); + multiprecision_init(p->z, keyLength); + p->z[0] = 1; + + // initialization + p_256_init_point(q); + + // -p + multiprecision_copy(minus_p.x, p->x, keyLength); + multiprecision_sub(minus_p.y, modp, p->y, keyLength); + + multiprecision_init(minus_p.z, keyLength); + minus_p.z[0] = 1; + + // NAF + memset(naf, 0, sizeof(naf)); + ECC_NAF(naf, &NumNaf, n, keyLength); + + for (int i = NumNaf - 1; i >= 0; i--) { + p_256_copy_point(&r, q); + ECC_Double(q, &r, keyLength); + sign = (naf[i / 4] >> ((i % 4) * 2)) & 0x03; + + if (sign == 1) { + p_256_copy_point(&r, q); + ECC_Add(q, &r, p, keyLength); + } else if (sign == 3) { + p_256_copy_point(&r, q); + ECC_Add(q, &r, &minus_p, keyLength); + } + } + + multiprecision_inv_mod(minus_p.x, q->z, keyLength); + multiprecision_mersenns_squa_mod(q->z, minus_p.x, keyLength); + multiprecision_mersenns_mult_mod(q->x, q->x, q->z, keyLength); + multiprecision_mersenns_mult_mod(q->z, q->z, minus_p.x, keyLength); + multiprecision_mersenns_mult_mod(q->y, q->y, q->z, keyLength); +} + +bool ECC_CheckPointIsInElliCur_P256(Point *p) +{ + /* y^2 % q */ + DWORD y_y_q[KEY_LENGTH_DWORDS_P256] = {0x0}; + /* x^2 % q */ + DWORD x_x_q[KEY_LENGTH_DWORDS_P256] = {0x0}; + /* x % q */ + DWORD x_q[KEY_LENGTH_DWORDS_P256] = {0x0}; + /* x^2, To prevent overflow, the length of the x square here needs to + be expanded to two times the original one. */ + DWORD x_x[2*KEY_LENGTH_DWORDS_P256] = {0x0}; + /* y_y_q =(p->y)^2(mod q) */ + multiprecision_mersenns_squa_mod(y_y_q, p->y, KEY_LENGTH_DWORDS_P256); + /* Calculate the value of p->x square, x_x = (p->x)^2 */ + multiprecision_mult(x_x, p->x, p->x, KEY_LENGTH_DWORDS_P256); + /* The function of the elliptic curve is y^2 = x^3 - 3x + b (mod q) ==> + y^2 = (x^2 - 3)*x + b (mod q), + so we calculate the x^2 - 3 value here */ + x_x[0] -= 3; + /* Using math relations. (a*b) % q = ((a%q)*(b%q)) % q ==> + (x^2 - 3)*x = (((x^2 - 3) % q) * x % q) % q */ + multiprecision_fast_mod_P256(x_x_q, x_x); + /* x_x = x_x_q * x_q */ + multiprecision_mult(x_x, x_x_q, p->x, KEY_LENGTH_DWORDS_P256); + /* x_q = x_x % q */ + multiprecision_fast_mod_P256(x_q, x_x); + /* Save the result in x_x_q */ + multiprecision_add_mod(x_x_q, x_q, curve_p256.b, KEY_LENGTH_DWORDS_P256); + /* compare the y_y_q and x_x_q, see if they are on a given elliptic curve. */ + if (multiprecision_compare(y_y_q, x_x_q, KEY_LENGTH_DWORDS_P256)) { + return false; + } else { + return true; + } +} diff --git a/lib/bt/host/bluedroid/stack/smp/p_256_multprecision.c b/lib/bt/host/bluedroid/stack/smp/p_256_multprecision.c new file mode 100644 index 00000000..1dbca137 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/smp/p_256_multprecision.c @@ -0,0 +1,647 @@ +/****************************************************************************** + * + * Copyright (C) 2006-2015 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains simple pairing algorithms + * + ******************************************************************************/ + +#include +#include "common/bt_target.h" +#include "p_256_ecc_pp.h" +#include "p_256_multprecision.h" + +void multiprecision_init(DWORD *c, uint32_t keyLength) +{ + for (uint32_t i = 0; i < keyLength; i++) { + c[i] = 0; + } +} + +void multiprecision_copy(DWORD *c, DWORD *a, uint32_t keyLength) +{ + for (uint32_t i = 0; i < keyLength; i++) { + c[i] = a[i]; + } +} + +int multiprecision_compare(DWORD *a, DWORD *b, uint32_t keyLength) +{ + for (int i = keyLength - 1; i >= 0; i--) { + if (a[i] > b[i]) { + return 1; + } + if (a[i] < b[i]) { + return -1; + } + } + return 0; +} + +int multiprecision_iszero(DWORD *a, uint32_t keyLength) +{ + for (uint32_t i = 0; i < keyLength; i++) + if (a[i]) { + return 0; + } + + return 1; +} + +UINT32 multiprecision_dword_bits(DWORD a) +{ + uint32_t i; + for (i = 0; i < DWORD_BITS; i++, a >>= 1) + if (a == 0) { + break; + } + + return i; +} + +UINT32 multiprecision_most_signdwords(DWORD *a, uint32_t keyLength) +{ + int i; + for (i = keyLength - 1; i >= 0; i--) + if (a[i]) { + break; + } + return (i + 1); +} + +UINT32 multiprecision_most_signbits(DWORD *a, uint32_t keyLength) +{ + int aMostSignDWORDs; + + aMostSignDWORDs = multiprecision_most_signdwords(a, keyLength); + if (aMostSignDWORDs == 0) { + return 0; + } + + return (((aMostSignDWORDs - 1) << DWORD_BITS_SHIFT) + + multiprecision_dword_bits(a[aMostSignDWORDs - 1]) ); +} + +DWORD multiprecision_add(DWORD *c, DWORD *a, DWORD *b, uint32_t keyLength) +{ + DWORD carrier; + DWORD temp; + + carrier = 0; + for (uint32_t i = 0; i < keyLength; i++) { + temp = a[i] + carrier; + carrier = (temp < carrier); + temp += b[i]; + carrier |= (temp < b[i]); + c[i] = temp; + } + + return carrier; +} + +//c=a-b +DWORD multiprecision_sub(DWORD *c, DWORD *a, DWORD *b, uint32_t keyLength) +{ + DWORD borrow; + DWORD temp; + + borrow = 0; + for (uint32_t i = 0; i < keyLength; i++) { + temp = a[i] - borrow; + borrow = (temp > a[i]); + c[i] = temp - b[i]; + borrow |= (c[i] > temp); + } + + return borrow; +} + +// c = a << 1 +void multiprecision_lshift_mod(DWORD *c, DWORD *a, uint32_t keyLength) +{ + DWORD carrier; + DWORD *modp; + + if (keyLength == KEY_LENGTH_DWORDS_P192) { + modp = curve.p; + } else if (keyLength == KEY_LENGTH_DWORDS_P256) { + modp = curve_p256.p; + } else { + return; + } + + carrier = multiprecision_lshift(c, a, keyLength); + if (carrier) { + multiprecision_sub(c, c, modp, keyLength); + } else if (multiprecision_compare(c, modp, keyLength) >= 0) { + multiprecision_sub(c, c, modp, keyLength); + } +} + +// c=a>>1 +void multiprecision_rshift(DWORD *c, DWORD *a, uint32_t keyLength) +{ + int j; + DWORD b = 1; + + j = DWORD_BITS - b; + + DWORD carrier = 0; + DWORD temp; + for (int i = keyLength - 1; i >= 0; i--) { + temp = a[i]; // in case of c==a + c[i] = (temp >> b) | carrier; + carrier = temp << j; + } +} + +// Curve specific optimization when p is a pseudo-Mersenns prime, p=2^(KEY_LENGTH_BITS)-omega +void multiprecision_mersenns_mult_mod(DWORD *c, DWORD *a, DWORD *b, uint32_t keyLength) +{ + DWORD cc[2 * KEY_LENGTH_DWORDS_P256]; + + multiprecision_mult(cc, a, b, keyLength); + if (keyLength == 6) { + multiprecision_fast_mod(c, cc); + } else if (keyLength == 8) { + multiprecision_fast_mod_P256(c, cc); + } +} + +// Curve specific optimization when p is a pseudo-Mersenns prime +void multiprecision_mersenns_squa_mod(DWORD *c, DWORD *a, uint32_t keyLength) +{ + multiprecision_mersenns_mult_mod(c, a, a, keyLength); +} + +// c=(a+b) mod p, b= 0) { + multiprecision_sub(c, c, modp, keyLength); + } +} + +// c=(a-b) mod p, a> j; + } + + return carrier; +} + +// c=a*b; c must have a buffer of 2*Key_LENGTH_DWORDS, c != a != b +void multiprecision_mult(DWORD *c, DWORD *a, DWORD *b, uint32_t keyLength) +{ + DWORD W; + DWORD U; + DWORD V; + + U = V = W = 0; + multiprecision_init(c, keyLength); + + //assume little endian right now + for (uint32_t i = 0; i < keyLength; i++) { + U = 0; + for (uint32_t j = 0; j < keyLength; j++) { + uint64_t result; + result = ((UINT64)a[i]) * ((uint64_t) b[j]); + W = result >> 32; + V = a[i] * b[j]; + V = V + U; + U = (V < U); + U += W; + V = V + c[i + j]; + U += (V < c[i + j]); + c[i + j] = V; + } + c[i + keyLength] = U; + } +} + +void multiprecision_fast_mod(DWORD *c, DWORD *a) +{ + DWORD U; + DWORD V; + DWORD *modp = curve.p; + + c[0] = a[0] + a[6]; + U = c[0] < a[0]; + c[0] += a[10]; + U += c[0] < a[10]; + + c[1] = a[1] + U; + U = c[1] < a[1]; + c[1] += a[7]; + U += c[1] < a[7]; + c[1] += a[11]; + U += c[1] < a[11]; + + c[2] = a[2] + U; + U = c[2] < a[2]; + c[2] += a[6]; + U += c[2] < a[6]; + c[2] += a[8]; + U += c[2] < a[8]; + c[2] += a[10]; + U += c[2] < a[10]; + + c[3] = a[3] + U; + U = c[3] < a[3]; + c[3] += a[7]; + U += c[3] < a[7]; + c[3] += a[9]; + U += c[3] < a[9]; + c[3] += a[11]; + U += c[3] < a[11]; + + c[4] = a[4] + U; + U = c[4] < a[4]; + c[4] += a[8]; + U += c[4] < a[8]; + c[4] += a[10]; + U += c[4] < a[10]; + + c[5] = a[5] + U; + U = c[5] < a[5]; + c[5] += a[9]; + U += c[5] < a[9]; + c[5] += a[11]; + U += c[5] < a[11]; + + c[0] += U; + V = c[0] < U; + c[1] += V; + V = c[1] < V; + c[2] += V; + V = c[2] < V; + c[2] += U; + V = c[2] < U; + c[3] += V; + V = c[3] < V; + c[4] += V; + V = c[4] < V; + c[5] += V; + V = c[5] < V; + + if (V) { + multiprecision_sub(c, c, modp, KEY_LENGTH_DWORDS_P192); + } else if (multiprecision_compare(c, modp, KEY_LENGTH_DWORDS_P192) >= 0) { + multiprecision_sub(c, c, modp, KEY_LENGTH_DWORDS_P192); + } +} + +void multiprecision_fast_mod_P256(DWORD *c, DWORD *a) +{ + DWORD A; + DWORD B; + DWORD C; + DWORD D; + DWORD E; + DWORD F; + DWORD G; + uint8_t UA; + uint8_t UB; + uint8_t UC; + uint8_t UD; + uint8_t UE; + uint8_t UF; + uint8_t UG; + DWORD U; + DWORD *modp = curve_p256.p; + + // C = a[13] + a[14] + a[15]; + C = a[13]; + C += a[14]; + UC = (C < a[14]); + C += a[15]; + UC += (C < a[15]); + + // E = a[8] + a[9]; + E = a[8]; + E += a[9]; + UE = (E < a[9]); + + // F = a[9] + a[10]; + F = a[9]; + F += a[10]; + UF = (F < a[10]); + + // G = a[10] + a[11] + G = a[10]; + G += a[11]; + UG = (G < a[11]); + + // B = a[12] + a[13] + a[14] + a[15] == C + a[12] + B = C; + UB = UC; + B += a[12]; + UB += (B < a[12]); + + // A = a[11] + a[12] + a[13] + a[14] == B + a[11] - a[15] + A = B; + UA = UB; + A += a[11]; + UA += (A < a[11]); + UA -= (A < a[15]); + A -= a[15]; + + // D = a[10] + a[11] + a[12] + a[13] == A + a[10] - a[14] + D = A; + UD = UA; + D += a[10]; + UD += (D < a[10]); + UD -= (D < a[14]); + D -= a[14]; + + c[0] = a[0]; + c[0] += E; + U = (c[0] < E); + U += UE; + U -= (c[0] < A); + U -= UA; + c[0] -= A; + + if (U & 0x80000000) { + DWORD UU; + UU = 0 - U; + U = (a[1] < UU); + c[1] = a[1] - UU; + } else { + c[1] = a[1] + U; + U = (c[1] < a[1]); + } + + c[1] += F; + U += (c[1] < F); + U += UF; + U -= (c[1] < B); + U -= UB; + c[1] -= B; + + if (U & 0x80000000) { + DWORD UU; + UU = 0 - U; + U = (a[2] < UU); + c[2] = a[2] - UU; + } else { + c[2] = a[2] + U; + U = (c[2] < a[2]); + } + + c[2] += G; + U += (c[2] < G); + U += UG; + U -= (c[2] < C); + U -= UC; + c[2] -= C; + + if (U & 0x80000000) { + DWORD UU; + UU = 0 - U; + U = (a[3] < UU); + c[3] = a[3] - UU; + } else { + c[3] = a[3] + U; + U = (c[3] < a[3]); + } + + c[3] += A; + U += (c[3] < A); + U += UA; + c[3] += a[11]; + U += (c[3] < a[11]); + c[3] += a[12]; + U += (c[3] < a[12]); + U -= (c[3] < a[14]); + c[3] -= a[14]; + U -= (c[3] < a[15]); + c[3] -= a[15]; + U -= (c[3] < E); + U -= UE; + c[3] -= E; + + if (U & 0x80000000) { + DWORD UU; + UU = 0 - U; + U = (a[4] < UU); + c[4] = a[4] - UU; + } else { + c[4] = a[4] + U; + U = (c[4] < a[4]); + } + + c[4] += B; + U += (c[4] < B); + U += UB; + U -= (c[4] < a[15]); + c[4] -= a[15]; + c[4] += a[12]; + U += (c[4] < a[12]); + c[4] += a[13]; + U += (c[4] < a[13]); + U -= (c[4] < F); + U -= UF; + c[4] -= F; + + if (U & 0x80000000) { + DWORD UU; + UU = 0 - U; + U = (a[5] < UU); + c[5] = a[5] - UU; + } else { + c[5] = a[5] + U; + U = (c[5] < a[5]); + } + + c[5] += C; + U += (c[5] < C); + U += UC; + c[5] += a[13]; + U += (c[5] < a[13]); + c[5] += a[14]; + U += (c[5] < a[14]); + U -= (c[5] < G); + U -= UG; + c[5] -= G; + + if (U & 0x80000000) { + DWORD UU; + UU = 0 - U; + U = (a[6] < UU); + c[6] = a[6] - UU; + } else { + c[6] = a[6] + U; + U = (c[6] < a[6]); + } + + c[6] += C; + U += (c[6] < C); + U += UC; + c[6] += a[14]; + U += (c[6] < a[14]); + c[6] += a[14]; + U += (c[6] < a[14]); + c[6] += a[15]; + U += (c[6] < a[15]); + U -= (c[6] < E); + U -= UE; + c[6] -= E; + + if (U & 0x80000000) { + DWORD UU; + UU = 0 - U; + U = (a[7] < UU); + c[7] = a[7] - UU; + } else { + c[7] = a[7] + U; + U = (c[7] < a[7]); + } + + c[7] += a[15]; + U += (c[7] < a[15]); + c[7] += a[15]; + U += (c[7] < a[15]); + c[7] += a[15]; + U += (c[7] < a[15]); + c[7] += a[8]; + U += (c[7] < a[8]); + U -= (c[7] < D); + U -= UD; + c[7] -= D; + + if (U & 0x80000000) { + while (U) { + multiprecision_add(c, c, modp, KEY_LENGTH_DWORDS_P256); + U++; + } + } else if (U) { + while (U) { + multiprecision_sub(c, c, modp, KEY_LENGTH_DWORDS_P256); + U--; + } + } + + if (multiprecision_compare(c, modp, KEY_LENGTH_DWORDS_P256) >= 0) { + multiprecision_sub(c, c, modp, KEY_LENGTH_DWORDS_P256); + } + +} + +void multiprecision_inv_mod(DWORD *aminus, DWORD *u, uint32_t keyLength) +{ + DWORD v[KEY_LENGTH_DWORDS_P256]; + DWORD A[KEY_LENGTH_DWORDS_P256 + 1]; + DWORD C[KEY_LENGTH_DWORDS_P256 + 1]; + DWORD *modp; + + if (keyLength == KEY_LENGTH_DWORDS_P256) { + modp = curve_p256.p; + } else { + modp = curve.p; + } + + multiprecision_copy(v, modp, keyLength); + multiprecision_init(A, keyLength); + multiprecision_init(C, keyLength); + A[0] = 1; + + while (!multiprecision_iszero(u, keyLength)) { + while (!(u[0] & 0x01)) { // u is even + multiprecision_rshift(u, u, keyLength); + if (!(A[0] & 0x01)) { // A is even + multiprecision_rshift(A, A, keyLength); + } else { + A[keyLength] = multiprecision_add(A, A, modp, keyLength); // A =A+p + multiprecision_rshift(A, A, keyLength); + A[keyLength - 1] |= (A[keyLength] << 31); + } + } + + while (!(v[0] & 0x01)) { // v is even + multiprecision_rshift(v, v, keyLength); + if (!(C[0] & 0x01)) { // C is even + multiprecision_rshift(C, C, keyLength); + } else { + C[keyLength] = multiprecision_add(C, C, modp, keyLength); // C =C+p + multiprecision_rshift(C, C, keyLength); + C[keyLength - 1] |= (C[keyLength] << 31); + } + } + + if (multiprecision_compare(u, v, keyLength) >= 0) { + multiprecision_sub(u, u, v, keyLength); + multiprecision_sub_mod(A, A, C, keyLength); + } else { + multiprecision_sub(v, v, u, keyLength); + multiprecision_sub_mod(C, C, A, keyLength); + } + } + + if (multiprecision_compare(C, modp, keyLength) >= 0) { + multiprecision_sub(aminus, C, modp, keyLength); + } else { + multiprecision_copy(aminus, C, keyLength); + } +} diff --git a/lib/bt/host/bluedroid/stack/smp/smp_act.c b/lib/bt/host/bluedroid/stack/smp/smp_act.c new file mode 100644 index 00000000..0e7dcd5b --- /dev/null +++ b/lib/bt/host/bluedroid/stack/smp/smp_act.c @@ -0,0 +1,2194 @@ +/****************************************************************************** + * + * Copyright (C) 2003-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +#include +#include "device/interop.h" +#include "common/bt_target.h" +#include "btm_int.h" +#include "stack/l2c_api.h" +#include "smp_int.h" +#include "p_256_ecc_pp.h" +//#include "utils/include/bt_utils.h" + +#if SMP_INCLUDED == TRUE +const UINT8 smp_association_table[2][SMP_IO_CAP_MAX][SMP_IO_CAP_MAX] = { + /* initiator */ + { {SMP_MODEL_ENCRYPTION_ONLY, SMP_MODEL_ENCRYPTION_ONLY, SMP_MODEL_PASSKEY, SMP_MODEL_ENCRYPTION_ONLY, SMP_MODEL_PASSKEY}, /* Display Only */ + {SMP_MODEL_ENCRYPTION_ONLY, SMP_MODEL_ENCRYPTION_ONLY, SMP_MODEL_PASSKEY, SMP_MODEL_ENCRYPTION_ONLY, SMP_MODEL_PASSKEY}, /* SMP_CAP_IO = 1 */ + {SMP_MODEL_KEY_NOTIF, SMP_MODEL_KEY_NOTIF, SMP_MODEL_PASSKEY, SMP_MODEL_ENCRYPTION_ONLY, SMP_MODEL_KEY_NOTIF}, /* keyboard only */ + {SMP_MODEL_ENCRYPTION_ONLY, SMP_MODEL_ENCRYPTION_ONLY, SMP_MODEL_ENCRYPTION_ONLY, SMP_MODEL_ENCRYPTION_ONLY, SMP_MODEL_ENCRYPTION_ONLY},/* No Input No Output */ + {SMP_MODEL_KEY_NOTIF, SMP_MODEL_KEY_NOTIF, SMP_MODEL_PASSKEY, SMP_MODEL_ENCRYPTION_ONLY, SMP_MODEL_KEY_NOTIF} + }, /* keyboard display */ + /* responder */ + { {SMP_MODEL_ENCRYPTION_ONLY, SMP_MODEL_ENCRYPTION_ONLY, SMP_MODEL_KEY_NOTIF, SMP_MODEL_ENCRYPTION_ONLY, SMP_MODEL_KEY_NOTIF}, /* Display Only */ + {SMP_MODEL_ENCRYPTION_ONLY, SMP_MODEL_ENCRYPTION_ONLY, SMP_MODEL_KEY_NOTIF, SMP_MODEL_ENCRYPTION_ONLY, SMP_MODEL_KEY_NOTIF}, /* SMP_CAP_IO = 1 */ + {SMP_MODEL_PASSKEY, SMP_MODEL_PASSKEY, SMP_MODEL_PASSKEY, SMP_MODEL_ENCRYPTION_ONLY, SMP_MODEL_PASSKEY}, /* keyboard only */ + {SMP_MODEL_ENCRYPTION_ONLY, SMP_MODEL_ENCRYPTION_ONLY, SMP_MODEL_ENCRYPTION_ONLY, SMP_MODEL_ENCRYPTION_ONLY, SMP_MODEL_ENCRYPTION_ONLY},/* No Input No Output */ + {SMP_MODEL_PASSKEY, SMP_MODEL_PASSKEY, SMP_MODEL_KEY_NOTIF, SMP_MODEL_ENCRYPTION_ONLY, SMP_MODEL_PASSKEY} + } /* keyboard display */ + /* display only */ /*SMP_CAP_IO = 1 */ /* keyboard only */ /* No InputOutput */ /* keyboard display */ +}; + +#define SMP_KEY_DIST_TYPE_MAX 4 +const tSMP_ACT smp_distribute_act [] = { +#if (BLE_INCLUDED == TRUE) + smp_generate_ltk, + smp_send_id_info, + smp_generate_csrk, + smp_set_derive_link_key +#else + NULL, + NULL, + NULL, + NULL +#endif ///BLE_INCLUDED == TRUE +}; + +extern UINT8 bta_dm_co_ble_get_accept_auth_enable(void); +extern UINT8 bta_dm_co_ble_get_auth_req(void); + +static bool lmp_version_below(BD_ADDR bda, uint8_t version) +{ + tACL_CONN *acl = btm_bda_to_acl(bda, BT_TRANSPORT_LE); + if (acl == NULL || acl->lmp_version == 0) { + SMP_TRACE_WARNING("%s cannot retrieve LMP version...", __func__); + return false; + } + SMP_TRACE_DEBUG("%s LMP version %d < %d", __func__, acl->lmp_version, version); + return acl->lmp_version < version; +} + +/******************************************************************************* +** Function smp_update_key_mask +** Description This function updates the key mask for sending or receiving. +*******************************************************************************/ +static void smp_update_key_mask (tSMP_CB *p_cb, UINT8 key_type, BOOLEAN recv) +{ + SMP_TRACE_DEBUG("%s before update role=%d recv=%d local_i_key = %02x, local_r_key = %02x\n", + __func__, p_cb->role, recv, p_cb->local_i_key, p_cb->local_r_key); + + if (((p_cb->le_secure_connections_mode_is_used) || + (p_cb->smp_over_br)) && + ((key_type == SMP_SEC_KEY_TYPE_ENC) || (key_type == SMP_SEC_KEY_TYPE_LK))) { + /* in LE SC mode LTK, CSRK and BR/EDR LK are derived locally instead of + ** being exchanged with the peer */ + p_cb->local_i_key &= ~key_type; + p_cb->local_r_key &= ~key_type; + } else if (p_cb->role == HCI_ROLE_SLAVE) { + if (recv) { + p_cb->local_i_key &= ~key_type; + } else { + p_cb->local_r_key &= ~key_type; + } + } else { + if (recv) { + p_cb->local_r_key &= ~key_type; + } else { + p_cb->local_i_key &= ~key_type; + } + } + + SMP_TRACE_DEBUG("updated local_i_key = %02x, local_r_key = %02x\n", p_cb->local_i_key, + p_cb->local_r_key); +} + +/******************************************************************************* +** Function smp_send_app_cback +** Description notifies application about the events the application is interested in +*******************************************************************************/ +void smp_send_app_cback(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + tSMP_EVT_DATA cb_data; + tSMP_STATUS callback_rc; + SMP_TRACE_DEBUG("%s p_cb->cb_evt=%d\n", __func__, p_cb->cb_evt); + if (p_cb->p_callback && p_cb->cb_evt != 0) { + switch (p_cb->cb_evt) { + case SMP_IO_CAP_REQ_EVT: + cb_data.io_req.auth_req = p_cb->peer_auth_req; + cb_data.io_req.oob_data = SMP_OOB_NONE; + cb_data.io_req.io_cap = SMP_DEFAULT_IO_CAPS; + cb_data.io_req.max_key_size = SMP_MAX_ENC_KEY_SIZE; + cb_data.io_req.init_keys = p_cb->local_i_key ; + cb_data.io_req.resp_keys = p_cb->local_r_key ; + SMP_TRACE_DEBUG ( "io_cap = %d", cb_data.io_req.io_cap); + break; + + case SMP_NC_REQ_EVT: + cb_data.passkey = p_data->passkey; + break; + case SMP_SC_OOB_REQ_EVT: + cb_data.req_oob_type = p_data->req_oob_type; + break; + case SMP_SC_LOC_OOB_DATA_UP_EVT: + cb_data.loc_oob_data = p_cb->sc_oob_data.loc_oob_data; + break; + + case SMP_BR_KEYS_REQ_EVT: + cb_data.io_req.auth_req = 0; + cb_data.io_req.oob_data = SMP_OOB_NONE; + cb_data.io_req.io_cap = 0; + cb_data.io_req.max_key_size = SMP_MAX_ENC_KEY_SIZE; + cb_data.io_req.init_keys = SMP_BR_SEC_DEFAULT_KEY; + cb_data.io_req.resp_keys = SMP_BR_SEC_DEFAULT_KEY; + break; + + default: + break; + } + + callback_rc = (*p_cb->p_callback)(p_cb->cb_evt, p_cb->pairing_bda, &cb_data); + + SMP_TRACE_DEBUG("callback_rc=%d p_cb->cb_evt=%d\n", callback_rc, p_cb->cb_evt ); + + if (callback_rc == SMP_SUCCESS) { + switch (p_cb->cb_evt) { + case SMP_IO_CAP_REQ_EVT: + p_cb->loc_auth_req = cb_data.io_req.auth_req; + p_cb->local_io_capability = cb_data.io_req.io_cap; + p_cb->loc_oob_flag = cb_data.io_req.oob_data; + p_cb->loc_enc_size = cb_data.io_req.max_key_size; + p_cb->local_i_key = cb_data.io_req.init_keys; + p_cb->local_r_key = cb_data.io_req.resp_keys; + + if (!(p_cb->loc_auth_req & SMP_AUTH_BOND)) { + SMP_TRACE_WARNING ("Non bonding: No keys will be exchanged"); + p_cb->local_i_key = 0; + p_cb->local_r_key = 0; + } + + SMP_TRACE_DEBUG ("rcvd auth_req: 0x%02x, io_cap: %d \ + loc_oob_flag: %d loc_enc_size: %d," + "local_i_key: 0x%02x, local_r_key: 0x%02x\n", + p_cb->loc_auth_req, p_cb->local_io_capability, p_cb->loc_oob_flag, + p_cb->loc_enc_size, p_cb->local_i_key, p_cb->local_r_key); + + p_cb->secure_connections_only_mode_required = + (btm_cb.security_mode == BTM_SEC_MODE_SC) ? TRUE : FALSE; + + if (p_cb->secure_connections_only_mode_required) { + p_cb->loc_auth_req |= SMP_SC_SUPPORT_BIT; + } + + if (!(p_cb->loc_auth_req & SMP_SC_SUPPORT_BIT) + || lmp_version_below(p_cb->pairing_bda, HCI_PROTO_VERSION_4_2) + || interop_match(INTEROP_DISABLE_LE_SECURE_CONNECTIONS, + (const bt_bdaddr_t *)&p_cb->pairing_bda)) { + p_cb->loc_auth_req &= ~SMP_KP_SUPPORT_BIT; + p_cb->local_i_key &= ~SMP_SEC_KEY_TYPE_LK; + p_cb->local_r_key &= ~SMP_SEC_KEY_TYPE_LK; + } + + SMP_TRACE_DEBUG("set auth_req: 0x%02x, local_i_key: 0x%02x, local_r_key: 0x%02x\n", + p_cb->loc_auth_req, p_cb->local_i_key, p_cb->local_r_key); + + smp_sm_event(p_cb, SMP_IO_RSP_EVT, NULL); + break; +#if (CLASSIC_BT_INCLUDED == TRUE) + case SMP_BR_KEYS_REQ_EVT: + p_cb->loc_enc_size = cb_data.io_req.max_key_size; + p_cb->local_i_key = cb_data.io_req.init_keys; + p_cb->local_r_key = cb_data.io_req.resp_keys; + + p_cb->local_i_key &= ~SMP_SEC_KEY_TYPE_LK; + p_cb->local_r_key &= ~SMP_SEC_KEY_TYPE_LK; + + SMP_TRACE_WARNING ( "for SMP over BR max_key_size: 0x%02x,\ + local_i_key: 0x%02x, local_r_key: 0x%02x\n", + p_cb->loc_enc_size, p_cb->local_i_key, p_cb->local_r_key); + + smp_br_state_machine_event(p_cb, SMP_BR_KEYS_RSP_EVT, NULL); + break; +#endif ///CLASSIC_BT_INCLUDED == TRUE + } + } + } + + if (!p_cb->cb_evt && p_cb->discard_sec_req) { + p_cb->discard_sec_req = FALSE; + smp_sm_event(p_cb, SMP_DISCARD_SEC_REQ_EVT, NULL); + } + + SMP_TRACE_DEBUG("%s return\n", __func__); +} + +/******************************************************************************* +** Function smp_send_pair_fail +** Description pairing failure to peer device if needed. +*******************************************************************************/ +void smp_send_pair_fail(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + p_cb->status = *(UINT8 *)p_data; + p_cb->failure = *(UINT8 *)p_data; + + SMP_TRACE_DEBUG("%s status=%d failure=%d ", __func__, p_cb->status, p_cb->failure); + + if (p_cb->status <= SMP_MAX_FAIL_RSN_PER_SPEC && p_cb->status != SMP_SUCCESS) { + smp_send_cmd(SMP_OPCODE_PAIRING_FAILED, p_cb); + p_cb->wait_for_authorization_complete = TRUE; + } +} + +/******************************************************************************* +** Function smp_send_pair_req +** Description actions related to sending pairing request +*******************************************************************************/ +void smp_send_pair_req(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + SMP_TRACE_DEBUG("%s\n", __func__); + +#if (BLE_INCLUDED == TRUE) + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev (p_cb->pairing_bda); + /* erase all keys when master sends pairing req*/ + if (p_dev_rec) { + btm_sec_clear_ble_keys(p_dev_rec); + } +#endif ///BLE_INCLUDED == TRUE + /* do not manipulate the key, let app decide, + leave out to BTM to mandate key distribution for bonding case */ + smp_send_cmd(SMP_OPCODE_PAIRING_REQ, p_cb); +} + +/******************************************************************************* +** Function smp_send_pair_rsp +** Description actions related to sending pairing response +*******************************************************************************/ +void smp_send_pair_rsp(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + SMP_TRACE_DEBUG("%s\n", __func__); + +#if (BLE_INCLUDED == TRUE) + p_cb->local_i_key &= p_cb->peer_i_key; + p_cb->local_r_key &= p_cb->peer_r_key; + + if (smp_send_cmd (SMP_OPCODE_PAIRING_RSP, p_cb)) { + if (p_cb->selected_association_model == SMP_MODEL_SEC_CONN_OOB) { + smp_use_oob_private_key(p_cb, NULL); + } else { + smp_decide_association_model(p_cb, NULL); + } + } +#endif ///BLE_INCLUDED == TRUE +} + +/******************************************************************************* +** Function smp_send_confirm +** Description send confirmation to the peer +*******************************************************************************/ +void smp_send_confirm(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + SMP_TRACE_DEBUG("%s\n", __func__); + smp_send_cmd(SMP_OPCODE_CONFIRM, p_cb); +} + +#if 0 //Unused +/******************************************************************************* +** Function smp_send_init +** Description process pairing initializer to slave device +*******************************************************************************/ +void smp_send_init(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + SMP_TRACE_DEBUG("%s\n", __func__); + smp_send_cmd(SMP_OPCODE_INIT, p_cb); +} +#endif + +/******************************************************************************* +** Function smp_send_rand +** Description send pairing random to the peer +*******************************************************************************/ +void smp_send_rand(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + SMP_TRACE_DEBUG("%s\n", __func__); + smp_send_cmd(SMP_OPCODE_RAND, p_cb); +} + +/******************************************************************************* +** Function smp_send_pair_public_key +** Description send pairing public key command to the peer +*******************************************************************************/ +void smp_send_pair_public_key(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + SMP_TRACE_DEBUG("%s\n", __func__); + smp_send_cmd(SMP_OPCODE_PAIR_PUBLIC_KEY, p_cb); +} + +/******************************************************************************* +** Function SMP_SEND_COMMITMENT +** Description send commitment command to the peer +*******************************************************************************/ +void smp_send_commitment(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + SMP_TRACE_DEBUG("%s", __func__); + smp_send_cmd(SMP_OPCODE_PAIR_COMMITM, p_cb); +} + +/******************************************************************************* +** Function smp_send_dhkey_check +** Description send DHKey Check command to the peer +*******************************************************************************/ +void smp_send_dhkey_check(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + SMP_TRACE_DEBUG("%s", __func__); + smp_send_cmd(SMP_OPCODE_PAIR_DHKEY_CHECK, p_cb); +} + +/******************************************************************************* +** Function smp_send_keypress_notification +** Description send Keypress Notification command to the peer +*******************************************************************************/ +void smp_send_keypress_notification(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + p_cb->local_keypress_notification = *(UINT8 *) p_data; + smp_send_cmd(SMP_OPCODE_PAIR_KEYPR_NOTIF, p_cb); +} + +/******************************************************************************* +** Function smp_send_enc_info +** Description send encryption information command. +*******************************************************************************/ +void smp_send_enc_info(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + tBTM_LE_LENC_KEYS le_key; + + SMP_TRACE_DEBUG("%s p_cb->loc_enc_size = %d\n", __func__, p_cb->loc_enc_size); + smp_update_key_mask (p_cb, SMP_SEC_KEY_TYPE_ENC, FALSE); + + smp_send_cmd(SMP_OPCODE_ENCRYPT_INFO, p_cb); + smp_send_cmd(SMP_OPCODE_MASTER_ID, p_cb); + + /* save the DIV and key size information when acting as slave device */ + memcpy(le_key.ltk, p_cb->ltk, BT_OCTET16_LEN); + le_key.div = p_cb->div; + le_key.key_size = p_cb->loc_enc_size; + le_key.sec_level = p_cb->sec_level; + +#if (BLE_INCLUDED == TRUE) + if ((p_cb->peer_auth_req & SMP_AUTH_BOND) && (p_cb->loc_auth_req & SMP_AUTH_BOND)) { + btm_sec_save_le_key(p_cb->pairing_bda, BTM_LE_KEY_LENC, + (tBTM_LE_KEY_VALUE *)&le_key, TRUE); + } + + SMP_TRACE_DEBUG ("%s\n", __func__); + + smp_key_distribution(p_cb, NULL); +#endif ///BLE_INCLUDED == TRUE +} + +/******************************************************************************* +** Function smp_send_id_info +** Description send ID information command. +*******************************************************************************/ +void smp_send_id_info(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + SMP_TRACE_DEBUG("%s\n", __func__); + smp_update_key_mask (p_cb, SMP_SEC_KEY_TYPE_ID, FALSE); + + smp_send_cmd(SMP_OPCODE_IDENTITY_INFO, p_cb); + smp_send_cmd(SMP_OPCODE_ID_ADDR, p_cb); + +#if (BLE_INCLUDED == TRUE) + tBTM_LE_KEY_VALUE le_key; + if ((p_cb->peer_auth_req & SMP_AUTH_BOND) && (p_cb->loc_auth_req & SMP_AUTH_BOND)) { + btm_sec_save_le_key(p_cb->pairing_bda, BTM_LE_KEY_LID, + &le_key, TRUE); + } +#endif ///BLE_INCLUDED == TRUE + + smp_key_distribution_by_transport(p_cb, NULL); +} + +#if (BLE_INCLUDED == TRUE) +/******************************************************************************* +** Function smp_send_csrk_info +** Description send CSRK command. +*******************************************************************************/ +void smp_send_csrk_info(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + tBTM_LE_LCSRK_KEYS key; + SMP_TRACE_DEBUG("%s\n", __func__); + smp_update_key_mask (p_cb, SMP_SEC_KEY_TYPE_CSRK, FALSE); + + if (smp_send_cmd(SMP_OPCODE_SIGN_INFO, p_cb)) { + key.div = p_cb->div; + key.sec_level = p_cb->sec_level; + key.counter = 0; /* initialize the local counter */ + memcpy (key.csrk, p_cb->csrk, BT_OCTET16_LEN); + btm_sec_save_le_key(p_cb->pairing_bda, BTM_LE_KEY_LCSRK, (tBTM_LE_KEY_VALUE *)&key, TRUE); + } + + smp_key_distribution_by_transport(p_cb, NULL); +} + +/******************************************************************************* +** Function smp_send_ltk_reply +** Description send LTK reply +*******************************************************************************/ +void smp_send_ltk_reply(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + SMP_TRACE_DEBUG("%s", __func__); + /* send stk as LTK response */ + btm_ble_ltk_request_reply(p_cb->pairing_bda, TRUE, p_data->key.p_data); +} + +/******************************************************************************* +** Function smp_proc_sec_req +** Description process security request. +*******************************************************************************/ +void smp_proc_sec_req(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + tBTM_LE_AUTH_REQ auth_req = *(tBTM_LE_AUTH_REQ *)p_data; + tBTM_BLE_SEC_REQ_ACT sec_req_act; + UINT8 reason; + + SMP_TRACE_DEBUG("%s auth_req=0x%x", __func__, auth_req); + + p_cb->cb_evt = 0; + btm_ble_link_sec_check(p_cb->pairing_bda, auth_req, &sec_req_act); + + SMP_TRACE_DEBUG("%s sec_req_act=0x%x", __func__, sec_req_act); + + switch (sec_req_act) { + case BTM_BLE_SEC_REQ_ACT_ENCRYPT: + SMP_TRACE_DEBUG("%s BTM_BLE_SEC_REQ_ACT_ENCRYPT", __func__); + smp_sm_event(p_cb, SMP_ENC_REQ_EVT, NULL); + break; + + case BTM_BLE_SEC_REQ_ACT_PAIR: + p_cb->secure_connections_only_mode_required = + (btm_cb.security_mode == BTM_SEC_MODE_SC) ? TRUE : FALSE; + + /* respond to non SC pairing request as failure in SC only mode */ + if (p_cb->secure_connections_only_mode_required && + (auth_req & SMP_SC_SUPPORT_BIT) == 0) { + reason = SMP_PAIR_AUTH_FAIL; + smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &reason); + } else { + /* initialize local i/r key to be default keys */ + p_cb->peer_auth_req = auth_req; + p_cb->local_r_key = p_cb->local_i_key = SMP_SEC_DEFAULT_KEY ; + p_cb->cb_evt = SMP_SEC_REQUEST_EVT; + } + break; + + case BTM_BLE_SEC_REQ_ACT_DISCARD: + p_cb->discard_sec_req = TRUE; + break; + + default: + /* do nothing */ + break; + } +} +#endif ///BLE_INCLUDED == TRUE + +/******************************************************************************* +** Function smp_proc_sec_grant +** Description process security grant. +*******************************************************************************/ +void smp_proc_sec_grant(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + UINT8 res = *(UINT8 *)p_data; + SMP_TRACE_DEBUG("%s", __func__); + if (res != SMP_SUCCESS) { + smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, p_data); + } else { /*otherwise, start pairing */ + /* send IO request callback */ + p_cb->cb_evt = SMP_IO_CAP_REQ_EVT; + } +} + +/******************************************************************************* +** Function smp_proc_pair_fail +** Description process pairing failure from peer device +*******************************************************************************/ +void smp_proc_pair_fail(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + SMP_TRACE_DEBUG("%s", __func__); + p_cb->status = *(UINT8 *)p_data; +} + +/******************************************************************************* +** Function smp_get_auth_mode +** Description Get the SMP pairing auth mode +*******************************************************************************/ +uint16_t smp_get_auth_mode (tSMP_ASSO_MODEL model) +{ + SMP_TRACE_DEBUG("%s model %d", __func__, model); + uint16_t auth = 0; + if (model == SMP_MODEL_ENCRYPTION_ONLY || model == SMP_MODEL_SEC_CONN_JUSTWORKS) { + //No MITM + if(model == SMP_MODEL_SEC_CONN_JUSTWORKS) { + //SC SMP_SC_SUPPORT_BIT + auth |= SMP_SC_SUPPORT_BIT; + } + } else if (model <= SMP_MODEL_KEY_NOTIF) { + //NO SC, MITM + auth |= SMP_AUTH_YN_BIT; + } else if (model <= SMP_MODEL_SEC_CONN_OOB) { + //SC, MITM + auth |= SMP_SC_SUPPORT_BIT; + auth |= SMP_AUTH_YN_BIT; + } else { + auth = 0; + } + return auth; +} + +#if (BLE_INCLUDED == TRUE) +/******************************************************************************* +** Function smp_proc_pair_cmd +** Description Process the SMP pairing request/response from peer device +*******************************************************************************/ +void smp_proc_pair_cmd(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + UINT8 *p = (UINT8 *)p_data; + UINT8 reason = SMP_ENC_KEY_SIZE; + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev (p_cb->pairing_bda); + + SMP_TRACE_DEBUG("%s\n", __func__); + /* erase all keys if it is slave proc pairing req*/ + if (p_dev_rec && (p_cb->role == HCI_ROLE_SLAVE)) { + btm_sec_clear_ble_keys(p_dev_rec); + } + + p_cb->flags |= SMP_PAIR_FLAG_ENC_AFTER_PAIR; + + STREAM_TO_UINT8(p_cb->peer_io_caps, p); + STREAM_TO_UINT8(p_cb->peer_oob_flag, p); + STREAM_TO_UINT8(p_cb->peer_auth_req, p); + STREAM_TO_UINT8(p_cb->peer_enc_size, p); + STREAM_TO_UINT8(p_cb->peer_i_key, p); + STREAM_TO_UINT8(p_cb->peer_r_key, p); + + if (smp_command_has_invalid_parameters(p_cb)) { + reason = SMP_INVALID_PARAMETERS; + smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &reason); + return; + } + p_cb->accept_specified_sec_auth = bta_dm_co_ble_get_accept_auth_enable(); + p_cb->origin_loc_auth_req = bta_dm_co_ble_get_auth_req(); + if (p_cb->role == HCI_ROLE_SLAVE) { + if (!(p_cb->flags & SMP_PAIR_FLAGS_WE_STARTED_DD)) { + /* peer (master) started pairing sending Pairing Request */ + p_cb->local_i_key = p_cb->peer_i_key; + p_cb->local_r_key = p_cb->peer_r_key; + + p_cb->cb_evt = SMP_SEC_REQUEST_EVT; + } else { /* update local i/r key according to pairing request */ + /* pairing started with this side (slave) sending Security Request */ + p_cb->local_i_key &= p_cb->peer_i_key; + p_cb->local_r_key &= p_cb->peer_r_key; + p_cb->selected_association_model = smp_select_association_model(p_cb); + + if (p_cb->secure_connections_only_mode_required && + (!(p_cb->le_secure_connections_mode_is_used) || + (p_cb->selected_association_model == SMP_MODEL_SEC_CONN_JUSTWORKS))) { + SMP_TRACE_ERROR("%s pairing failed - slave requires secure connection only mode", + __func__); + reason = SMP_PAIR_AUTH_FAIL; + smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &reason); + return; + } + uint16_t auth = smp_get_auth_mode(p_cb->selected_association_model); + if(p_cb->peer_auth_req & p_cb->loc_auth_req & SMP_AUTH_GEN_BOND) { + auth |= SMP_AUTH_GEN_BOND; + } + p_cb->auth_mode = auth; + if (p_cb->accept_specified_sec_auth) { + if ((auth & p_cb->origin_loc_auth_req) != p_cb->origin_loc_auth_req ) { + SMP_TRACE_ERROR("%s pairing failed - slave requires auth is 0x%x but peer auth is 0x%x local auth is 0x%x", + __func__, p_cb->origin_loc_auth_req, p_cb->peer_auth_req, p_cb->loc_auth_req); + if (BTM_IsAclConnectionUp(p_cb->pairing_bda, BT_TRANSPORT_LE)) { + btm_remove_acl (p_cb->pairing_bda, BT_TRANSPORT_LE); + } + reason = SMP_PAIR_AUTH_FAIL; + smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &reason); + return; + } + } + + if (p_cb->selected_association_model == SMP_MODEL_SEC_CONN_OOB && p_cb->loc_oob_flag == SMP_OOB_PRESENT) { + if (smp_request_oob_data(p_cb)) { + return; + } + } else { + smp_send_pair_rsp(p_cb, NULL); + } + } + } else { /* Master receives pairing response */ + p_cb->selected_association_model = smp_select_association_model(p_cb); + + if (p_cb->secure_connections_only_mode_required && + (!(p_cb->le_secure_connections_mode_is_used) || + (p_cb->selected_association_model == SMP_MODEL_SEC_CONN_JUSTWORKS))) { + SMP_TRACE_ERROR ("Master requires secure connection only mode \ + but it can't be provided -> Master fails pairing"); + reason = SMP_PAIR_AUTH_FAIL; + smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &reason); + return; + } + + uint16_t auth = smp_get_auth_mode(p_cb->selected_association_model); + if(p_cb->peer_auth_req & p_cb->loc_auth_req & SMP_AUTH_GEN_BOND) { + auth |= SMP_AUTH_GEN_BOND; + } + p_cb->auth_mode = auth; + if (p_cb->accept_specified_sec_auth) { + if ((auth & p_cb->origin_loc_auth_req) != p_cb->origin_loc_auth_req ) { + SMP_TRACE_ERROR("%s pairing failed - master requires auth is 0x%x but peer auth is 0x%x local auth is 0x%x", + __func__, p_cb->origin_loc_auth_req, p_cb->peer_auth_req, p_cb->loc_auth_req); + if (BTM_IsAclConnectionUp(p_cb->pairing_bda, BT_TRANSPORT_LE)) { + btm_remove_acl (p_cb->pairing_bda, BT_TRANSPORT_LE); + } + reason = SMP_PAIR_AUTH_FAIL; + smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &reason); + return; + } + } + + /* Only if peer oob data present, then should request peer oob data */ + if (p_cb->selected_association_model == SMP_MODEL_SEC_CONN_OOB && p_cb->loc_oob_flag == SMP_OOB_PRESENT) { + if (smp_request_oob_data(p_cb)) { + return; + } + } else { + smp_decide_association_model(p_cb, NULL); + } + } +} +#endif ///BLE_INCLUDED == TRUE + +/******************************************************************************* +** Function smp_proc_confirm +** Description process pairing confirm from peer device +*******************************************************************************/ +void smp_proc_confirm(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + UINT8 *p = (UINT8 *)p_data; + UINT8 reason = SMP_INVALID_PARAMETERS; + + SMP_TRACE_DEBUG("%s\n", __func__); + + if (smp_command_has_invalid_parameters(p_cb)) { + smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &reason); + return; + } + + if (p != NULL) { + /* save the SConfirm for comparison later */ + STREAM_TO_ARRAY(p_cb->rconfirm, p, BT_OCTET16_LEN); + } + + p_cb->flags |= SMP_PAIR_FLAGS_CMD_CONFIRM; +} + +#if 0 //Unused +/******************************************************************************* +** Function smp_proc_init +** Description process pairing initializer from peer device +*******************************************************************************/ +void smp_proc_init(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + UINT8 *p = (UINT8 *)p_data; + UINT8 reason = SMP_INVALID_PARAMETERS; + + SMP_TRACE_DEBUG("%s", __func__); + + if (smp_command_has_invalid_parameters(p_cb)) { + smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &reason); + return; + } + + /* save the SRand for comparison */ + STREAM_TO_ARRAY(p_cb->rrand, p, BT_OCTET16_LEN); +} +#endif + +/******************************************************************************* +** Function smp_proc_rand +** Description process pairing random (nonce) from peer device +*******************************************************************************/ +void smp_proc_rand(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + UINT8 *p = (UINT8 *)p_data; + UINT8 reason = SMP_INVALID_PARAMETERS; + + SMP_TRACE_DEBUG("%s\n", __func__); + + if (smp_command_has_invalid_parameters(p_cb)) { + smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &reason); + return; + } + + /* save the SRand for comparison */ + STREAM_TO_ARRAY(p_cb->rrand, p, BT_OCTET16_LEN); +} + +/******************************************************************************* +** Function smp_process_pairing_public_key +** Description process pairing public key command from the peer device +** - saves the peer public key; +** - sets the flag indicating that the peer public key is received; +** - calls smp_wait_for_both_public_keys(...). +** +*******************************************************************************/ +void smp_process_pairing_public_key(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + UINT8 *p = (UINT8 *)p_data; + UINT8 reason = SMP_INVALID_PARAMETERS; + + SMP_TRACE_DEBUG("%s", __func__); + + if (smp_command_has_invalid_parameters(p_cb)) { + smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &reason); + return; + } + + STREAM_TO_ARRAY(p_cb->peer_publ_key.x, p, BT_OCTET32_LEN); + STREAM_TO_ARRAY(p_cb->peer_publ_key.y, p, BT_OCTET32_LEN); + + /* Check if the peer device's and own public key are not same. If they are same then + * return pairing fail. This check is needed to avoid 'Impersonation in Passkey entry + * protocol' vulnerability (CVE-2020-26558).*/ + if ((memcmp(p_cb->loc_publ_key.x, p_cb->peer_publ_key.x, sizeof(BT_OCTET32)) == 0)) { + p_cb->status = SMP_PAIR_AUTH_FAIL; + p_cb->failure = SMP_PAIR_AUTH_FAIL; + reason = SMP_PAIR_AUTH_FAIL; + SMP_TRACE_ERROR("%s, Peer and own device cannot have same public key.", __func__); + smp_sm_event(p_cb, SMP_PAIRING_FAILED_EVT, &reason); + return ; + } + /* In order to prevent the x and y coordinates of the public key from being modified, + we need to check whether the x and y coordinates are on the given elliptic curve. */ + if (!ECC_CheckPointIsInElliCur_P256((Point *)&p_cb->peer_publ_key)) { + SMP_TRACE_ERROR("%s, Invalid Public key.", __func__); + smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &reason); + } + p_cb->flags |= SMP_PAIR_FLAG_HAVE_PEER_PUBL_KEY; + + smp_wait_for_both_public_keys(p_cb, NULL); +} + +/******************************************************************************* +** Function smp_process_pairing_commitment +** Description process pairing commitment from peer device +*******************************************************************************/ +void smp_process_pairing_commitment(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + UINT8 *p = (UINT8 *)p_data; + UINT8 reason = SMP_INVALID_PARAMETERS; + + SMP_TRACE_DEBUG("%s", __func__); + + if (smp_command_has_invalid_parameters(p_cb)) { + smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &reason); + return; + } + + p_cb->flags |= SMP_PAIR_FLAG_HAVE_PEER_COMM; + + if (p != NULL) { + STREAM_TO_ARRAY(p_cb->remote_commitment, p, BT_OCTET16_LEN); + } +} + +/******************************************************************************* +** Function smp_process_dhkey_check +** Description process DHKey Check from peer device +*******************************************************************************/ +void smp_process_dhkey_check(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + UINT8 *p = (UINT8 *)p_data; + UINT8 reason = SMP_INVALID_PARAMETERS; + + SMP_TRACE_DEBUG("%s", __func__); + + if (smp_command_has_invalid_parameters(p_cb)) { + smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &reason); + return; + } + + if (p != NULL) { + STREAM_TO_ARRAY(p_cb->remote_dhkey_check, p, BT_OCTET16_LEN); + } + + p_cb->flags |= SMP_PAIR_FLAG_HAVE_PEER_DHK_CHK; +} + +/******************************************************************************* +** Function smp_process_keypress_notification +** Description process pairing keypress notification from peer device +*******************************************************************************/ +void smp_process_keypress_notification(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + UINT8 *p = (UINT8 *)p_data; + UINT8 reason = SMP_INVALID_PARAMETERS; + + SMP_TRACE_DEBUG("%s", __func__); + p_cb->status = *(UINT8 *)p_data; + + if (smp_command_has_invalid_parameters(p_cb)) { + smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &reason); + return; + } + + if (p != NULL) { + STREAM_TO_UINT8(p_cb->peer_keypress_notification, p); + } else { + p_cb->peer_keypress_notification = BTM_SP_KEY_OUT_OF_RANGE; + } + p_cb->cb_evt = SMP_PEER_KEYPR_NOT_EVT; +} + +#if (CLASSIC_BT_INCLUDED == TRUE) +/******************************************************************************* +** Function smp_br_process_pairing_command +** Description Process the SMP pairing request/response from peer device via +** BR/EDR transport. +*******************************************************************************/ +void smp_br_process_pairing_command(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + UINT8 *p = (UINT8 *)p_data; + UINT8 reason = SMP_ENC_KEY_SIZE; + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev (p_cb->pairing_bda); + + SMP_TRACE_DEBUG("%s", __func__); + /* rejecting BR pairing request over non-SC BR link */ + if (!p_dev_rec->new_encryption_key_is_p256 && p_cb->role == HCI_ROLE_SLAVE) { + reason = SMP_XTRANS_DERIVE_NOT_ALLOW; + smp_br_state_machine_event(p_cb, SMP_BR_AUTH_CMPL_EVT, &reason); + return; + } + +#if (BLE_INCLUDED == TRUE) + /* erase all keys if it is slave proc pairing req*/ + if (p_dev_rec && (p_cb->role == HCI_ROLE_SLAVE)) { + btm_sec_clear_ble_keys(p_dev_rec); + } +#endif ///BLE_INCLUDED == TRUE + + p_cb->flags |= SMP_PAIR_FLAG_ENC_AFTER_PAIR; + + STREAM_TO_UINT8(p_cb->peer_io_caps, p); + STREAM_TO_UINT8(p_cb->peer_oob_flag, p); + STREAM_TO_UINT8(p_cb->peer_auth_req, p); + STREAM_TO_UINT8(p_cb->peer_enc_size, p); + STREAM_TO_UINT8(p_cb->peer_i_key, p); + STREAM_TO_UINT8(p_cb->peer_r_key, p); + + if (smp_command_has_invalid_parameters(p_cb)) { + reason = SMP_INVALID_PARAMETERS; + smp_br_state_machine_event(p_cb, SMP_BR_AUTH_CMPL_EVT, &reason); + return; + } + + /* peer (master) started pairing sending Pairing Request */ + /* or being master device always use received i/r key as keys to distribute */ + p_cb->local_i_key = p_cb->peer_i_key; + p_cb->local_r_key = p_cb->peer_r_key; + + if (p_cb->role == HCI_ROLE_SLAVE) { + p_dev_rec->new_encryption_key_is_p256 = FALSE; + /* shortcut to skip Security Grant step */ + p_cb->cb_evt = SMP_BR_KEYS_REQ_EVT; + } else { /* Master receives pairing response */ + SMP_TRACE_DEBUG("%s master rcvs valid PAIRING RESPONSE." + " Supposed to move to key distribution phase. ", __func__); + } + + /* auth_req received via BR/EDR SM channel is set to 0, + but everything derived/exchanged has to be saved */ + p_cb->peer_auth_req |= SMP_AUTH_BOND; + p_cb->loc_auth_req |= SMP_AUTH_BOND; +} + +/******************************************************************************* +** Function smp_br_process_security_grant +** Description process security grant in case of pairing over BR/EDR transport. +*******************************************************************************/ +void smp_br_process_security_grant(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + UINT8 res = *(UINT8 *)p_data; + SMP_TRACE_DEBUG("%s", __func__); + if (res != SMP_SUCCESS) { + smp_br_state_machine_event(p_cb, SMP_BR_AUTH_CMPL_EVT, p_data); + } else { /*otherwise, start pairing */ + /* send IO request callback */ + p_cb->cb_evt = SMP_BR_KEYS_REQ_EVT; + } +} + +/******************************************************************************* +** Function smp_br_check_authorization_request +** Description sets the SMP kes to be derived/distribute over BR/EDR transport +** before starting the distribution/derivation +*******************************************************************************/ +void smp_br_check_authorization_request(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + UINT8 reason = SMP_SUCCESS; + + SMP_TRACE_DEBUG("%s rcvs i_keys=0x%x r_keys=0x%x " + "(i-initiator r-responder)", __FUNCTION__, p_cb->local_i_key, + p_cb->local_r_key); + + /* In LE SC mode LK field is ignored when BR/EDR transport is used */ + p_cb->local_i_key &= ~SMP_SEC_KEY_TYPE_LK; + p_cb->local_r_key &= ~SMP_SEC_KEY_TYPE_LK; + + /* In LE SC mode only IRK, IAI, CSRK are exchanged with the peer. + ** Set local_r_key on master to expect only these keys. */ + if (p_cb->role == HCI_ROLE_MASTER) { + p_cb->local_r_key &= (SMP_SEC_KEY_TYPE_ID | SMP_SEC_KEY_TYPE_CSRK); + } + + SMP_TRACE_DEBUG("%s rcvs upgrades: i_keys=0x%x r_keys=0x%x " + "(i-initiator r-responder)", __FUNCTION__, p_cb->local_i_key, + p_cb->local_r_key); + + if (/*((p_cb->peer_auth_req & SMP_AUTH_BOND) || + (p_cb->loc_auth_req & SMP_AUTH_BOND)) &&*/ + (p_cb->local_i_key || p_cb->local_r_key)) { + smp_br_state_machine_event(p_cb, SMP_BR_BOND_REQ_EVT, NULL); + + /* if no peer key is expected, start master key distribution */ + if (p_cb->role == HCI_ROLE_MASTER && p_cb->local_r_key == 0) { + smp_key_distribution_by_transport(p_cb, NULL); + } + } else { + smp_br_state_machine_event(p_cb, SMP_BR_AUTH_CMPL_EVT, &reason); + } +} + +/******************************************************************************* +** Function smp_br_select_next_key +** Description selects the next key to derive/send when BR/EDR transport is +** used. +*******************************************************************************/ +void smp_br_select_next_key(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + UINT8 reason = SMP_SUCCESS; + SMP_TRACE_DEBUG("%s role=%d (0-master) r_keys=0x%x i_keys=0x%x", + __func__, p_cb->role, p_cb->local_r_key, p_cb->local_i_key); + + if (p_cb->role == HCI_ROLE_SLAVE || + (!p_cb->local_r_key && p_cb->role == HCI_ROLE_MASTER)) { + smp_key_pick_key(p_cb, p_data); + } + + if (!p_cb->local_i_key && !p_cb->local_r_key) { + /* state check to prevent re-entrance */ + if (smp_get_br_state() == SMP_BR_STATE_BOND_PENDING) { + if (p_cb->total_tx_unacked == 0) { + smp_br_state_machine_event(p_cb, SMP_BR_AUTH_CMPL_EVT, &reason); + } else { + p_cb->wait_for_authorization_complete = TRUE; + } + } + } +} +#endif ///CLASSIC_BT_INCLUDED == TRUE + +#if (BLE_INCLUDED == TRUE) +/******************************************************************************* +** Function smp_proc_enc_info +** Description process encryption information from peer device +*******************************************************************************/ +void smp_proc_enc_info(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + UINT8 *p = (UINT8 *)p_data; + + SMP_TRACE_DEBUG("%s\n", __func__); + STREAM_TO_ARRAY(p_cb->ltk, p, BT_OCTET16_LEN); + + smp_key_distribution(p_cb, NULL); +} +#endif ///BLE_INCLUDED == TRUE + +/******************************************************************************* +** Function smp_proc_master_id +** Description process master ID from slave device +*******************************************************************************/ +void smp_proc_master_id(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + UINT8 *p = (UINT8 *)p_data; + tBTM_LE_PENC_KEYS le_key; + + SMP_TRACE_DEBUG("%s\np_cb->peer_auth_req = %d,p_cb->loc_auth_req= %d\n", __func__, + p_cb->peer_auth_req, p_cb->loc_auth_req); + smp_update_key_mask (p_cb, SMP_SEC_KEY_TYPE_ENC, TRUE); + + STREAM_TO_UINT16(le_key.ediv, p); + STREAM_TO_ARRAY(le_key.rand, p, BT_OCTET8_LEN ); + + /* store the encryption keys from peer device */ + memcpy(le_key.ltk, p_cb->ltk, BT_OCTET16_LEN); + le_key.sec_level = p_cb->sec_level; + le_key.key_size = p_cb->loc_enc_size; + +#if (BLE_INCLUDED == TRUE) + if ((p_cb->peer_auth_req & SMP_AUTH_BOND) && (p_cb->loc_auth_req & SMP_AUTH_BOND)) { + btm_sec_save_le_key(p_cb->pairing_bda, + BTM_LE_KEY_PENC, + (tBTM_LE_KEY_VALUE *)&le_key, TRUE); + } + + smp_key_distribution(p_cb, NULL); +#endif ///BLE_INCLUDED == TRUE +} + +/******************************************************************************* +** Function smp_proc_enc_info +** Description process identity information from peer device +*******************************************************************************/ +void smp_proc_id_info(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + UINT8 *p = (UINT8 *)p_data; + + SMP_TRACE_DEBUG("%s", __func__); + STREAM_TO_ARRAY (p_cb->tk, p, BT_OCTET16_LEN); /* reuse TK for IRK */ + smp_key_distribution_by_transport(p_cb, NULL); +} + +/******************************************************************************* +** Function smp_proc_id_addr +** Description process identity address from peer device +*******************************************************************************/ +void smp_proc_id_addr(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + UINT8 *p = (UINT8 *)p_data; + tBTM_LE_PID_KEYS pid_key; + + SMP_TRACE_DEBUG("%s", __func__); + smp_update_key_mask (p_cb, SMP_SEC_KEY_TYPE_ID, TRUE); + + STREAM_TO_UINT8(pid_key.addr_type, p); + STREAM_TO_BDADDR(pid_key.static_addr, p); + memcpy(pid_key.irk, p_cb->tk, BT_OCTET16_LEN); + + /* to use as BD_ADDR for lk derived from ltk */ + p_cb->id_addr_rcvd = TRUE; + p_cb->id_addr_type = pid_key.addr_type; + memcpy(p_cb->id_addr, pid_key.static_addr, BD_ADDR_LEN); + +#if (BLE_INCLUDED == TRUE) + /* store the ID key from peer device */ + if ((p_cb->peer_auth_req & SMP_AUTH_BOND) && (p_cb->loc_auth_req & SMP_AUTH_BOND)) { + btm_sec_save_le_key(p_cb->pairing_bda, BTM_LE_KEY_PID, + (tBTM_LE_KEY_VALUE *)&pid_key, TRUE); + } +#endif ///BLE_INCLUDED == TRUE + + smp_key_distribution_by_transport(p_cb, NULL); +} + +/******************************************************************************* +** Function smp_proc_srk_info +** Description process security information from peer device +*******************************************************************************/ +void smp_proc_srk_info(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ +#if (BLE_INCLUDED == TRUE) + tBTM_LE_PCSRK_KEYS le_key; + + SMP_TRACE_DEBUG("%s", __func__); + smp_update_key_mask (p_cb, SMP_SEC_KEY_TYPE_CSRK, TRUE); + + /* save CSRK to security record */ + le_key.sec_level = p_cb->sec_level; + memcpy (le_key.csrk, p_data, BT_OCTET16_LEN); /* get peer CSRK */ + le_key.counter = 0; /* initialize the peer counter */ + + if ((p_cb->peer_auth_req & SMP_AUTH_BOND) && (p_cb->loc_auth_req & SMP_AUTH_BOND)) { + btm_sec_save_le_key(p_cb->pairing_bda, + BTM_LE_KEY_PCSRK, + (tBTM_LE_KEY_VALUE *)&le_key, TRUE); + } + +#endif ///BLE_INCLUDED == TRUE + smp_key_distribution_by_transport(p_cb, NULL); +} + +/******************************************************************************* +** Function smp_proc_compare +** Description process compare value +*******************************************************************************/ +void smp_proc_compare(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + UINT8 reason; + + SMP_TRACE_DEBUG("%s\n", __func__); + if (!memcmp(p_cb->rconfirm, p_data->key.p_data, BT_OCTET16_LEN)) { + /* compare the max encryption key size, and save the smaller one for the link */ + if ( p_cb->peer_enc_size < p_cb->loc_enc_size) { + p_cb->loc_enc_size = p_cb->peer_enc_size; + } + + if (p_cb->role == HCI_ROLE_SLAVE) { + smp_sm_event(p_cb, SMP_RAND_EVT, NULL); + } else { + /* master device always use received i/r key as keys to distribute */ + p_cb->local_i_key = p_cb->peer_i_key; + p_cb->local_r_key = p_cb->peer_r_key; + + smp_sm_event(p_cb, SMP_ENC_REQ_EVT, NULL); + } + + } else { + reason = p_cb->failure = SMP_CONFIRM_VALUE_ERR; + smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &reason); + } +} + +#if (BLE_INCLUDED == TRUE) +/******************************************************************************* +** Function smp_proc_sl_key +** Description process key ready events. +*******************************************************************************/ +void smp_proc_sl_key(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + UINT8 key_type = p_data->key.key_type; + + SMP_TRACE_DEBUG("%s\n", __func__); + if (key_type == SMP_KEY_TYPE_TK) { + smp_generate_srand_mrand_confirm(p_cb, NULL); + } else if (key_type == SMP_KEY_TYPE_CFM) { + smp_set_state(SMP_STATE_WAIT_CONFIRM); + + if (p_cb->flags & SMP_PAIR_FLAGS_CMD_CONFIRM) { + smp_sm_event(p_cb, SMP_CONFIRM_EVT, NULL); + } + } +} + +/******************************************************************************* +** Function smp_start_enc +** Description start encryption +*******************************************************************************/ +void smp_start_enc(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + tBTM_STATUS cmd; + UINT8 reason = SMP_ENC_FAIL; + + SMP_TRACE_DEBUG("%s\n", __func__); + if (p_data != NULL) { + cmd = btm_ble_start_encrypt(p_cb->pairing_bda, TRUE, p_data->key.p_data); + } else { + cmd = btm_ble_start_encrypt(p_cb->pairing_bda, FALSE, NULL); + } + + if (cmd != BTM_CMD_STARTED && cmd != BTM_BUSY) { + smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &reason); + } +} +#endif ///BLE_INCLUDED == TRUE + +/******************************************************************************* +** Function smp_proc_discard +** Description processing for discard security request +*******************************************************************************/ +void smp_proc_discard(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + SMP_TRACE_DEBUG("%s\n", __func__); + if (!(p_cb->flags & SMP_PAIR_FLAGS_WE_STARTED_DD)) { + smp_reset_control_value(p_cb); + } +} + +/******************************************************************************* +** Function smp_enc_cmpl +** Description encryption success +*******************************************************************************/ +void smp_enc_cmpl(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + UINT8 enc_enable = *(UINT8 *)p_data; + UINT8 reason = enc_enable ? SMP_SUCCESS : SMP_ENC_FAIL; + + SMP_TRACE_DEBUG("%s\n", __func__); + smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &reason); +} + +/******************************************************************************* +** Function smp_check_auth_req +** Description check authentication request +*******************************************************************************/ +void smp_check_auth_req(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + UINT8 enc_enable = *(UINT8 *)p_data; + UINT8 reason = enc_enable ? SMP_SUCCESS : SMP_ENC_FAIL; + + SMP_TRACE_DEBUG("%s rcvs enc_enable=%d i_keys=0x%x r_keys=0x%x " + "(i-initiator r-responder)\n", + __func__, enc_enable, p_cb->local_i_key, p_cb->local_r_key); + if (enc_enable == 1) { + if (p_cb->le_secure_connections_mode_is_used) { + /* In LE SC mode LTK is used instead of STK and has to be always saved */ + p_cb->local_i_key |= SMP_SEC_KEY_TYPE_ENC; + p_cb->local_r_key |= SMP_SEC_KEY_TYPE_ENC; + + /* In LE SC mode LK is derived from LTK only if both sides request it */ + if (!(p_cb->local_i_key & SMP_SEC_KEY_TYPE_LK) || + !(p_cb->local_r_key & SMP_SEC_KEY_TYPE_LK)) { + p_cb->local_i_key &= ~SMP_SEC_KEY_TYPE_LK; + p_cb->local_r_key &= ~SMP_SEC_KEY_TYPE_LK; + } + + /* In LE SC mode only IRK, IAI, CSRK are exchanged with the peer. + ** Set local_r_key on master to expect only these keys. + */ + if (p_cb->role == HCI_ROLE_MASTER) { + p_cb->local_r_key &= (SMP_SEC_KEY_TYPE_ID | SMP_SEC_KEY_TYPE_CSRK); + } + } else { + /* in legacy mode derivation of BR/EDR LK is not supported */ + p_cb->local_i_key &= ~SMP_SEC_KEY_TYPE_LK; + p_cb->local_r_key &= ~SMP_SEC_KEY_TYPE_LK; + } + SMP_TRACE_DEBUG("%s rcvs upgrades: i_keys=0x%x r_keys=0x%x " + "(i-initiator r-responder)\n", + __func__, p_cb->local_i_key, p_cb->local_r_key); + + if (/*((p_cb->peer_auth_req & SMP_AUTH_BOND) || + (p_cb->loc_auth_req & SMP_AUTH_BOND)) &&*/ + (p_cb->local_i_key || p_cb->local_r_key)) { + smp_sm_event(p_cb, SMP_BOND_REQ_EVT, NULL); + } else { + smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &reason); + } + } else if (enc_enable == 0) { + /* if failed for encryption after pairing, send callback */ + if (p_cb->flags & SMP_PAIR_FLAG_ENC_AFTER_PAIR) { + smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &reason); + } + /* if enc failed for old security information */ + /* if master device, clean up and abck to idle; slave device do nothing */ + else if (p_cb->role == HCI_ROLE_MASTER) { + smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &reason); + } + } +} + +/******************************************************************************* +** Function smp_key_pick_key +** Description Pick a key distribution function based on the key mask. +*******************************************************************************/ +void smp_key_pick_key(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + UINT8 key_to_dist = (p_cb->role == HCI_ROLE_SLAVE) ? p_cb->local_r_key : p_cb->local_i_key; + UINT8 i = 0; + + SMP_TRACE_DEBUG("%s key_to_dist=0x%x\n", __func__, key_to_dist); + while (i < SMP_KEY_DIST_TYPE_MAX) { + SMP_TRACE_DEBUG("key to send = %02x, i = %d\n", key_to_dist, i); + + if (key_to_dist & (1 << i) && smp_distribute_act[i] != NULL) { + SMP_TRACE_DEBUG("smp_distribute_act[%d]\n", i); + (* smp_distribute_act[i])(p_cb, p_data); + break; + } + i ++; + } +} + +#if (BLE_INCLUDED == TRUE) +/******************************************************************************* +** Function smp_key_distribution +** Description start key distribution if required. +*******************************************************************************/ +void smp_key_distribution(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + UINT8 reason = SMP_SUCCESS; + SMP_TRACE_DEBUG("\n%s role=%d (0-master) r_keys=0x%x i_keys=0x%x\n", + __func__, p_cb->role, p_cb->local_r_key, p_cb->local_i_key); + + if (p_cb->role == HCI_ROLE_SLAVE || + (!p_cb->local_r_key && p_cb->role == HCI_ROLE_MASTER)) { + smp_key_pick_key(p_cb, p_data); + } + + if (!p_cb->local_i_key && !p_cb->local_r_key) { + /* state check to prevent re-entrant */ + if (smp_get_state() == SMP_STATE_BOND_PENDING) { + if (p_cb->derive_lk) { + smp_derive_link_key_from_long_term_key(p_cb, NULL); + p_cb->derive_lk = FALSE; + } + + if (p_cb->total_tx_unacked == 0) { + smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &reason); + } else { + p_cb->wait_for_authorization_complete = TRUE; + } + } + } +} + +/******************************************************************************* +** Function smp_decide_association_model +** Description This function is called to select assoc model to be used for +** STK generation and to start STK generation process. +** +*******************************************************************************/ +void smp_decide_association_model(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + UINT8 failure = SMP_UNKNOWN_IO_CAP; + UINT8 int_evt = 0; + tSMP_KEY key; + tSMP_INT_DATA *p = NULL; + + SMP_TRACE_DEBUG("%s Association Model = %d\n", __func__, p_cb->selected_association_model); + + switch (p_cb->selected_association_model) { + case SMP_MODEL_ENCRYPTION_ONLY: /* TK = 0, go calculate Confirm */ + p_cb->sec_level = SMP_SEC_UNAUTHENTICATE; + SMP_TRACE_EVENT ("p_cb->sec_level =%d (SMP_SEC_UNAUTHENTICATE) \n", p_cb->sec_level ); + + key.key_type = SMP_KEY_TYPE_TK; + key.p_data = p_cb->tk; + p = (tSMP_INT_DATA *)&key; + + memset(p_cb->tk, 0, BT_OCTET16_LEN); + /* TK, ready */ + int_evt = SMP_KEY_READY_EVT; + break; + + case SMP_MODEL_PASSKEY: + p_cb->sec_level = SMP_SEC_AUTHENTICATED; + SMP_TRACE_EVENT ("p_cb->sec_level =%d (SMP_SEC_AUTHENTICATED) \n", p_cb->sec_level ); + + p_cb->cb_evt = SMP_PASSKEY_REQ_EVT; + int_evt = SMP_TK_REQ_EVT; + break; + + case SMP_MODEL_OOB: + SMP_TRACE_ERROR ("Association Model = SMP_MODEL_OOB\n"); + p_cb->sec_level = SMP_SEC_AUTHENTICATED; + SMP_TRACE_EVENT ("p_cb->sec_level =%d (SMP_SEC_AUTHENTICATED) \n", p_cb->sec_level ); + + p_cb->cb_evt = SMP_OOB_REQ_EVT; + int_evt = SMP_TK_REQ_EVT; + break; + + case SMP_MODEL_KEY_NOTIF: + p_cb->sec_level = SMP_SEC_AUTHENTICATED; + SMP_TRACE_DEBUG("Need to generate Passkey\n"); + + /* generate passkey and notify application */ + smp_generate_passkey(p_cb, NULL); + break; + + case SMP_MODEL_SEC_CONN_JUSTWORKS: + case SMP_MODEL_SEC_CONN_NUM_COMP: + case SMP_MODEL_SEC_CONN_PASSKEY_ENT: + case SMP_MODEL_SEC_CONN_PASSKEY_DISP: + case SMP_MODEL_SEC_CONN_OOB: + int_evt = SMP_PUBL_KEY_EXCH_REQ_EVT; + break; + + case SMP_MODEL_OUT_OF_RANGE: + SMP_TRACE_ERROR("Association Model = SMP_MODEL_OUT_OF_RANGE (failed)\n"); + p = (tSMP_INT_DATA *)&failure; + int_evt = SMP_AUTH_CMPL_EVT; + break; + + default: + SMP_TRACE_ERROR("Association Model = %d (SOMETHING IS WRONG WITH THE CODE)\n", + p_cb->selected_association_model); + p = (tSMP_INT_DATA *)&failure; + int_evt = SMP_AUTH_CMPL_EVT; + } + + SMP_TRACE_EVENT ("sec_level=%d \n", p_cb->sec_level ); + if (int_evt) { + smp_sm_event(p_cb, int_evt, p); + } +} + +/******************************************************************************* +** Function smp_process_io_response +** Description process IO response for a slave device. +*******************************************************************************/ +void smp_process_io_response(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + uint8_t reason = SMP_PAIR_AUTH_FAIL; + SMP_TRACE_DEBUG("%s\n", __func__); + if (p_cb->flags & SMP_PAIR_FLAGS_WE_STARTED_DD) { + /* pairing started by local (slave) Security Request */ + smp_set_state(SMP_STATE_SEC_REQ_PENDING); + smp_send_cmd(SMP_OPCODE_SEC_REQ, p_cb); + } else { /* plan to send pairing respond */ + /* pairing started by peer (master) Pairing Request */ + p_cb->selected_association_model = smp_select_association_model(p_cb); + + if (p_cb->secure_connections_only_mode_required && + (!(p_cb->le_secure_connections_mode_is_used) || + (p_cb->selected_association_model == SMP_MODEL_SEC_CONN_JUSTWORKS))) { + SMP_TRACE_ERROR ("Slave requires secure connection only mode \ + but it can't be provided -> Slave fails pairing\n"); + smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &reason); + return; + } + uint16_t auth = smp_get_auth_mode(p_cb->selected_association_model); + if(p_cb->peer_auth_req & p_cb->loc_auth_req & SMP_AUTH_GEN_BOND) { + auth |= SMP_AUTH_GEN_BOND; + } + p_cb->auth_mode = auth; + if (p_cb->accept_specified_sec_auth) { + if ((auth & p_cb->origin_loc_auth_req) != p_cb->origin_loc_auth_req ) { + SMP_TRACE_ERROR("pairing failed - slave requires auth is 0x%x but peer auth is 0x%x local auth is 0x%x", + p_cb->origin_loc_auth_req, p_cb->peer_auth_req, p_cb->loc_auth_req); + if (BTM_IsAclConnectionUp(p_cb->pairing_bda, BT_TRANSPORT_LE)) { + btm_remove_acl (p_cb->pairing_bda, BT_TRANSPORT_LE); + } + reason = SMP_PAIR_AUTH_FAIL; + smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &reason); + return; + } + } + + if (p_cb->selected_association_model == SMP_MODEL_SEC_CONN_OOB && p_cb->loc_oob_flag == SMP_OOB_PRESENT) { + if (smp_request_oob_data(p_cb)) { + return; + } + } + smp_send_pair_rsp(p_cb, NULL); + } +} +#endif ///BLE_INCLUDED == TRUE + +/******************************************************************************* +** Function smp_br_process_slave_keys_response +** Description process application keys response for a slave device +** (BR/EDR transport). +*******************************************************************************/ +void smp_br_process_slave_keys_response(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + smp_br_send_pair_response(p_cb, NULL); +} + +/******************************************************************************* +** Function smp_br_send_pair_response +** Description actions related to sending pairing response over BR/EDR transport. +*******************************************************************************/ +void smp_br_send_pair_response(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + SMP_TRACE_DEBUG("%s\n", __func__); + + p_cb->local_i_key &= p_cb->peer_i_key; + p_cb->local_r_key &= p_cb->peer_r_key; + + smp_send_cmd (SMP_OPCODE_PAIRING_RSP, p_cb); +} + +/******************************************************************************* +** Function smp_pairing_cmpl +** Description This function is called to send the pairing complete callback +** and remove the connection if needed. +*******************************************************************************/ +void smp_pairing_cmpl(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + if (p_cb->total_tx_unacked == 0) { + /* process the pairing complete */ + smp_proc_pairing_cmpl(p_cb); + } +} + +/******************************************************************************* +** Function smp_pair_terminate +** Description This function is called to send the pairing complete callback +** and remove the connection if needed. +*******************************************************************************/ +void smp_pair_terminate(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + SMP_TRACE_DEBUG("%s\n", __func__); + p_cb->status = SMP_CONN_TOUT; + smp_proc_pairing_cmpl(p_cb); +} + +/******************************************************************************* +** Function smp_idle_terminate +** Description This function calledin idle state to determine to send authentication +** complete or not. +*******************************************************************************/ +void smp_idle_terminate(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + if (p_cb->flags & SMP_PAIR_FLAGS_WE_STARTED_DD) { + SMP_TRACE_DEBUG("Pairing terminated at IDLE state.\n"); + p_cb->status = SMP_FAIL; + smp_proc_pairing_cmpl(p_cb); + } +} + +#if (BLE_INCLUDED == TRUE) +/******************************************************************************* +** Function smp_fast_conn_param +** Description apply default connection parameter for pairing process +*******************************************************************************/ +void smp_fast_conn_param(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + if(p_cb->role == BTM_ROLE_MASTER) { +#if (BT_MULTI_CONNECTION_ENBALE == FALSE) + L2CA_EnableUpdateBleConnParams(p_cb->pairing_bda, FALSE); +#endif + } else { +#if (SMP_SLAVE_CON_PARAMS_UPD_ENABLE == TRUE) + tBTM_SEC_DEV_REC *p_rec = btm_find_dev (p_cb->pairing_bda); + if(p_rec && p_rec->ble.skip_update_conn_param) { + //do nothing + return; + } + /* Disable L2CAP connection parameter updates while bonding since + some peripherals are not able to revert to fast connection parameters + during the start of service discovery. Connection paramter updates + get enabled again once service discovery completes. */ + #if (BT_MULTI_CONNECTION_ENBALE == FALSE) + L2CA_EnableUpdateBleConnParams(p_cb->pairing_bda, FALSE); + #endif +#endif + } +} + +/******************************************************************************* +** Function smp_both_have_public_keys +** Description The function is called when both local and peer public keys are +** saved. +** Actions: +** - invokes DHKey computation; +** - on slave side invokes sending local public key to the peer. +** - invokes SC phase 1 process. +*******************************************************************************/ +void smp_both_have_public_keys(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + SMP_TRACE_DEBUG("%s\n", __func__); + + /* invokes DHKey computation */ + smp_compute_dhkey(p_cb); + + /* on slave side invokes sending local public key to the peer */ + if (p_cb->role == HCI_ROLE_SLAVE) { + smp_send_pair_public_key(p_cb, NULL); + } + + smp_sm_event(p_cb, SMP_SC_DHKEY_CMPLT_EVT, NULL); +} + +/******************************************************************************* +** Function smp_start_secure_connection_phase1 +** Description The function starts Secure Connection phase1 i.e. invokes initialization of Secure Connection +** phase 1 parameters and starts building/sending to the peer +** messages appropriate for the role and association model. +*******************************************************************************/ +void smp_start_secure_connection_phase1(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + SMP_TRACE_DEBUG("%s\n", __func__); + + if (p_cb->selected_association_model == SMP_MODEL_SEC_CONN_JUSTWORKS) { + p_cb->sec_level = SMP_SEC_UNAUTHENTICATE; + SMP_TRACE_EVENT ("p_cb->sec_level =%d (SMP_SEC_UNAUTHENTICATE)\n ", p_cb->sec_level ); + } else { + p_cb->sec_level = SMP_SEC_AUTHENTICATED; + SMP_TRACE_EVENT ("p_cb->sec_level =%d (SMP_SEC_AUTHENTICATED)\n ", p_cb->sec_level ); + } + + switch (p_cb->selected_association_model) { + case SMP_MODEL_SEC_CONN_JUSTWORKS: + case SMP_MODEL_SEC_CONN_NUM_COMP: + memset(p_cb->local_random, 0, BT_OCTET16_LEN); + smp_start_nonce_generation(p_cb); + break; + case SMP_MODEL_SEC_CONN_PASSKEY_ENT: + /* user has to provide passkey */ + p_cb->cb_evt = SMP_PASSKEY_REQ_EVT; + smp_sm_event(p_cb, SMP_TK_REQ_EVT, NULL); + break; + case SMP_MODEL_SEC_CONN_PASSKEY_DISP: + /* passkey has to be provided to user */ + SMP_TRACE_DEBUG("Need to generate SC Passkey\n"); + smp_generate_passkey(p_cb, NULL); + break; + case SMP_MODEL_SEC_CONN_OOB: + /* use the available OOB information */ + smp_process_secure_connection_oob_data(p_cb, NULL); + break; + default: + SMP_TRACE_ERROR ("Association Model = %d is not used in LE SC\n", + p_cb->selected_association_model); + break; + } +} + +/******************************************************************************* +** Function smp_process_local_nonce +** Description The function processes new local nonce. +** +** Note It is supposed to be called in SC phase1. +*******************************************************************************/ +void smp_process_local_nonce(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + SMP_TRACE_DEBUG("%s\n", __func__); + + switch (p_cb->selected_association_model) { + case SMP_MODEL_SEC_CONN_JUSTWORKS: + case SMP_MODEL_SEC_CONN_NUM_COMP: + if (p_cb->role == HCI_ROLE_SLAVE) { + /* slave calculates and sends local commitment */ + smp_calculate_local_commitment(p_cb); + smp_send_commitment(p_cb, NULL); + /* slave has to wait for peer nonce */ + smp_set_state(SMP_STATE_WAIT_NONCE); + } else { /* i.e. master */ + if (p_cb->flags & SMP_PAIR_FLAG_HAVE_PEER_COMM) { + /* slave commitment is already received, send local nonce, wait for remote nonce*/ + SMP_TRACE_DEBUG("master in assoc mode = %d \ + already rcvd slave commitment - race condition\n", + p_cb->selected_association_model); + p_cb->flags &= ~SMP_PAIR_FLAG_HAVE_PEER_COMM; + smp_send_rand(p_cb, NULL); + smp_set_state(SMP_STATE_WAIT_NONCE); + } + } + break; + case SMP_MODEL_SEC_CONN_PASSKEY_ENT: + case SMP_MODEL_SEC_CONN_PASSKEY_DISP: + smp_calculate_local_commitment(p_cb); + + if (p_cb->role == HCI_ROLE_MASTER) { + smp_send_commitment(p_cb, NULL); + } else { /* slave */ + if (p_cb->flags & SMP_PAIR_FLAG_HAVE_PEER_COMM) { + /* master commitment is already received */ + smp_send_commitment(p_cb, NULL); + smp_set_state(SMP_STATE_WAIT_NONCE); + } + } + break; + case SMP_MODEL_SEC_CONN_OOB: + if (p_cb->role == HCI_ROLE_MASTER) { + smp_send_rand(p_cb, NULL); + } + + smp_set_state(SMP_STATE_WAIT_NONCE); + break; + default: + SMP_TRACE_ERROR ("Association Model = %d is not used in LE SC\n", + p_cb->selected_association_model); + break; + } +} + +/******************************************************************************* +** Function smp_process_peer_nonce +** Description The function processes newly received and saved in CB peer nonce. +** The actions depend on the selected association model and the role. +** +** Note It is supposed to be called in SC phase1. +*******************************************************************************/ +void smp_process_peer_nonce(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + UINT8 reason; + + SMP_TRACE_DEBUG("%s start \n", __func__); + + switch (p_cb->selected_association_model) { + case SMP_MODEL_SEC_CONN_JUSTWORKS: + case SMP_MODEL_SEC_CONN_NUM_COMP: + /* in these models only master receives commitment */ + if (p_cb->role == HCI_ROLE_MASTER) { + if (!smp_check_commitment(p_cb)) { + reason = p_cb->failure = SMP_CONFIRM_VALUE_ERR; + smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &reason); + break; + } + } else { + /* slave sends local nonce */ + smp_send_rand(p_cb, NULL); + } + + if (p_cb->selected_association_model == SMP_MODEL_SEC_CONN_JUSTWORKS) { + /* go directly to phase 2 */ + smp_sm_event(p_cb, SMP_SC_PHASE1_CMPLT_EVT, NULL); + } else { /* numeric comparison */ + smp_set_state(SMP_STATE_WAIT_NONCE); + smp_sm_event(p_cb, SMP_SC_CALC_NC_EVT, NULL); + } + break; + case SMP_MODEL_SEC_CONN_PASSKEY_ENT: + case SMP_MODEL_SEC_CONN_PASSKEY_DISP: + if (!smp_check_commitment(p_cb)) { + reason = p_cb->failure = SMP_CONFIRM_VALUE_ERR; + smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &reason); + break; + } + + if (p_cb->role == HCI_ROLE_SLAVE) { + smp_send_rand(p_cb, NULL); + } + + if (++p_cb->round < 20) { + smp_set_state(SMP_STATE_SEC_CONN_PHS1_START); + p_cb->flags &= ~SMP_PAIR_FLAG_HAVE_PEER_COMM; + smp_start_nonce_generation(p_cb); + break; + } + + smp_sm_event(p_cb, SMP_SC_PHASE1_CMPLT_EVT, NULL); + break; + case SMP_MODEL_SEC_CONN_OOB: + if (p_cb->role == HCI_ROLE_SLAVE) { + smp_send_rand(p_cb, NULL); + } + + smp_sm_event(p_cb, SMP_SC_PHASE1_CMPLT_EVT, NULL); + break; + default: + SMP_TRACE_ERROR ("Association Model = %d is not used in LE SC\n", + p_cb->selected_association_model); + break; + } + + SMP_TRACE_DEBUG("%s end\n ", __FUNCTION__); +} +#endif ///BLE_INCLUDED == TRUE + +/******************************************************************************* +** Function smp_match_dhkey_checks +** Description checks if the calculated peer DHKey Check value is the same as +** received from the peer DHKey check value. +*******************************************************************************/ +void smp_match_dhkey_checks(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + UINT8 reason = SMP_DHKEY_CHK_FAIL; + + SMP_TRACE_DEBUG("%s\n", __func__); + + if (memcmp(p_data->key.p_data, p_cb->remote_dhkey_check, BT_OCTET16_LEN)) { + SMP_TRACE_WARNING ("dhkey checks do no match\n"); + p_cb->failure = reason; + smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &reason); + return; + } + + SMP_TRACE_EVENT ("dhkey checks match\n"); + + /* compare the max encryption key size, and save the smaller one for the link */ + if (p_cb->peer_enc_size < p_cb->loc_enc_size) { + p_cb->loc_enc_size = p_cb->peer_enc_size; + } + + if (p_cb->role == HCI_ROLE_SLAVE) { + smp_sm_event(p_cb, SMP_PAIR_DHKEY_CHCK_EVT, NULL); + } else { + /* master device always use received i/r key as keys to distribute */ + p_cb->local_i_key = p_cb->peer_i_key; + p_cb->local_r_key = p_cb->peer_r_key; + smp_sm_event(p_cb, SMP_ENC_REQ_EVT, NULL); + } +} + +/******************************************************************************* +** Function smp_move_to_secure_connections_phase2 +** Description Signal State Machine to start SC phase 2 initialization (to +** compute local DHKey Check value). +** +** Note SM is supposed to be in the state SMP_STATE_SEC_CONN_PHS2_START. +*******************************************************************************/ +void smp_move_to_secure_connections_phase2(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + SMP_TRACE_DEBUG("%s\n", __func__); + smp_sm_event(p_cb, SMP_SC_PHASE1_CMPLT_EVT, NULL); +} + +/******************************************************************************* +** Function smp_phase_2_dhkey_checks_are_present +** Description generates event if dhkey check from the peer is already received. +** +** Note It is supposed to be used on slave to prevent race condition. +** It is supposed to be called after slave dhkey check is calculated. +*******************************************************************************/ +void smp_phase_2_dhkey_checks_are_present(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + SMP_TRACE_DEBUG("%s\n", __func__); + + if (p_cb->flags & SMP_PAIR_FLAG_HAVE_PEER_DHK_CHK) { + smp_sm_event(p_cb, SMP_SC_2_DHCK_CHKS_PRES_EVT, NULL); + } +} + +/******************************************************************************* +** Function smp_wait_for_both_public_keys +** Description generates SMP_BOTH_PUBL_KEYS_RCVD_EVT event when both local and master +** public keys are available. +** +** Note on the slave it is used to prevent race condition. +** +*******************************************************************************/ +void smp_wait_for_both_public_keys(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + SMP_TRACE_DEBUG("%s\n", __func__); + + if ((p_cb->flags & SMP_PAIR_FLAG_HAVE_PEER_PUBL_KEY) && + (p_cb->flags & SMP_PAIR_FLAG_HAVE_LOCAL_PUBL_KEY)) { + if ((p_cb->role == HCI_ROLE_SLAVE) && + ((p_cb->req_oob_type == SMP_OOB_LOCAL) || (p_cb->req_oob_type == SMP_OOB_BOTH))) { + smp_set_state(SMP_STATE_PUBLIC_KEY_EXCH); + } + smp_sm_event(p_cb, SMP_BOTH_PUBL_KEYS_RCVD_EVT, NULL); + } +} + +#if (BLE_INCLUDED == TRUE) +/******************************************************************************* +** Function smp_start_passkey_verification +** Description Starts SC passkey entry verification. +*******************************************************************************/ +void smp_start_passkey_verification(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + UINT8 *p = NULL; + + SMP_TRACE_DEBUG("%s\n", __func__); + p = p_cb->local_random; + UINT32_TO_STREAM(p, p_data->passkey); + + p = p_cb->peer_random; + UINT32_TO_STREAM(p, p_data->passkey); + + p_cb->round = 0; + smp_start_nonce_generation(p_cb); +} + +/******************************************************************************* +** Function smp_process_secure_connection_oob_data +** Description Processes local/peer SC OOB data received from somewhere. +*******************************************************************************/ +void smp_process_secure_connection_oob_data(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + SMP_TRACE_DEBUG("%s\n", __func__); + + tSMP_SC_OOB_DATA *p_sc_oob_data = &p_cb->sc_oob_data; + if (p_sc_oob_data->loc_oob_data.present) { + memcpy(p_cb->local_random, p_sc_oob_data->loc_oob_data.randomizer, + sizeof(p_cb->local_random)); + } else { + SMP_TRACE_EVENT ("local OOB randomizer is absent\n"); + memset(p_cb->local_random, 0, sizeof (p_cb->local_random)); + } + + if (!p_sc_oob_data->peer_oob_data.present) { + SMP_TRACE_EVENT ("peer OOB data is absent\n"); + memset(p_cb->peer_random, 0, sizeof (p_cb->peer_random)); + } else { + memcpy(p_cb->peer_random, p_sc_oob_data->peer_oob_data.randomizer, + sizeof(p_cb->peer_random)); + memcpy(p_cb->remote_commitment, p_sc_oob_data->peer_oob_data.commitment, + sizeof(p_cb->remote_commitment)); + + UINT8 reason = SMP_CONFIRM_VALUE_ERR; + /* check commitment */ + if (!smp_check_commitment(p_cb)) { + p_cb->failure = reason; + smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &reason); + return; + } + + if (p_cb->peer_oob_flag != SMP_OOB_PRESENT) { + /* the peer doesn't have local randomiser */ + SMP_TRACE_EVENT ("peer didn't receive local OOB data, set local randomizer to 0\n"); + memset(p_cb->local_random, 0, sizeof (p_cb->local_random)); + } + } + + print128(p_cb->local_random, (const UINT8 *)"local OOB randomizer"); + print128(p_cb->peer_random, (const UINT8 *)"peer OOB randomizer"); + smp_start_nonce_generation(p_cb); +} + +/******************************************************************************* +** Function smp_set_local_oob_keys +** Description Saves calculated private/public keys in sc_oob_data.loc_oob_data, +** starts nonce generation +** (to be saved in sc_oob_data.loc_oob_data.randomizer). +*******************************************************************************/ +void smp_set_local_oob_keys(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + SMP_TRACE_DEBUG("%s\n", __func__); + + memcpy(p_cb->sc_oob_data.loc_oob_data.private_key_used, p_cb->private_key, + BT_OCTET32_LEN); + p_cb->sc_oob_data.loc_oob_data.publ_key_used = p_cb->loc_publ_key; + smp_start_nonce_generation(p_cb); +} + +/******************************************************************************* +** Function smp_set_local_oob_random_commitment +** Description Saves calculated randomizer and commitment in sc_oob_data.loc_oob_data, +** passes sc_oob_data.loc_oob_data up for safekeeping. +*******************************************************************************/ +void smp_set_local_oob_random_commitment(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + SMP_TRACE_DEBUG("%s\n", __func__); + memcpy(p_cb->sc_oob_data.loc_oob_data.randomizer, p_cb->rand, + BT_OCTET16_LEN); + + smp_calculate_f4(p_cb->sc_oob_data.loc_oob_data.publ_key_used.x, + p_cb->sc_oob_data.loc_oob_data.publ_key_used.x, + p_cb->sc_oob_data.loc_oob_data.randomizer, 0, + p_cb->sc_oob_data.loc_oob_data.commitment); + + p_cb->sc_oob_data.loc_oob_data.present = true; +#if SMP_DEBUG == TRUE + UINT8 *p_print = NULL; + SMP_TRACE_DEBUG("local SC OOB data set:\n"); + p_print = (UINT8 *) &p_cb->sc_oob_data.loc_oob_data.addr_sent_to; + smp_debug_print_nbyte_little_endian (p_print, (const UINT8 *)"addr_sent_to", + sizeof(tBLE_BD_ADDR)); + p_print = (UINT8 *) &p_cb->sc_oob_data.loc_oob_data.private_key_used; + smp_debug_print_nbyte_little_endian (p_print, (const UINT8 *)"private_key_used", + BT_OCTET32_LEN); + p_print = (UINT8 *) &p_cb->sc_oob_data.loc_oob_data.publ_key_used.x; + smp_debug_print_nbyte_little_endian (p_print, (const UINT8 *)"publ_key_used.x", + BT_OCTET32_LEN); + p_print = (UINT8 *) &p_cb->sc_oob_data.loc_oob_data.publ_key_used.y; + smp_debug_print_nbyte_little_endian (p_print, (const UINT8 *)"publ_key_used.y", + BT_OCTET32_LEN); + p_print = (UINT8 *) &p_cb->sc_oob_data.loc_oob_data.randomizer; + smp_debug_print_nbyte_little_endian (p_print, (const UINT8 *)"randomizer", + BT_OCTET16_LEN); + p_print = (UINT8 *) &p_cb->sc_oob_data.loc_oob_data.commitment; + smp_debug_print_nbyte_little_endian (p_print, (const UINT8 *) "commitment", + BT_OCTET16_LEN); + SMP_TRACE_DEBUG(""); +#endif + + /* pass created OOB data up */ + p_cb->cb_evt = SMP_SC_LOC_OOB_DATA_UP_EVT; + smp_send_app_cback(p_cb, NULL); + + // Store the data for later use when we are paired with + smp_save_local_oob_data(p_cb); + + smp_cb_cleanup(p_cb); +} + +/******************************************************************************* +** +** Function smp_link_encrypted +** +** Description This function is called when link is encrypted and notified to +** slave device. Proceed to to send LTK, DIV and ER to master if +** bonding the devices. +** +** +** Returns void +** +*******************************************************************************/ +void smp_link_encrypted(BD_ADDR bda, UINT8 encr_enable) +{ + tSMP_CB *p_cb = &smp_cb; + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev (bda); + SMP_TRACE_DEBUG("%s encr_enable=%d\n", __func__, encr_enable); + + if (memcmp(&smp_cb.pairing_bda[0], bda, BD_ADDR_LEN) == 0) { + /* encryption completed with STK, remmeber the key size now, could be overwite + * when key exchange happens */ + if (p_cb->loc_enc_size != 0 && encr_enable) { + /* update the link encryption key size if a SMP pairing just performed */ + btm_ble_update_sec_key_size(bda, p_cb->loc_enc_size); + } + + smp_sm_event(&smp_cb, SMP_ENCRYPTED_EVT, &encr_enable); + } else if (p_dev_rec && !p_dev_rec->role_master && !p_dev_rec->enc_init_by_we ){ + /* + if enc_init_by_we is false, it means that client initiates encryption before slave calls esp_ble_set_encryption() + we need initiate pairing_bda and p_cb->role then encryption, for example iPhones + */ + memcpy(&smp_cb.pairing_bda[0], bda, BD_ADDR_LEN); + p_cb->state = SMP_STATE_ENCRYPTION_PENDING; + p_cb->role = HCI_ROLE_SLAVE; + p_dev_rec->enc_init_by_we = FALSE; + smp_sm_event(&smp_cb, SMP_ENCRYPTED_EVT, &encr_enable); + } else if (p_dev_rec && p_dev_rec->role_master && p_dev_rec->enc_init_by_we){ + memcpy(&smp_cb.pairing_bda[0], bda, BD_ADDR_LEN); + p_cb->state = SMP_STATE_ENCRYPTION_PENDING; + p_cb->role = HCI_ROLE_MASTER; + p_dev_rec->enc_init_by_we = FALSE; + smp_sm_event(&smp_cb, SMP_ENCRYPTED_EVT, &encr_enable); + } +} + +/******************************************************************************* +** +** Function smp_proc_ltk_request +** +** Description This function is called when LTK request is received from +** controller. +** +** Returns void +** +*******************************************************************************/ +BOOLEAN smp_proc_ltk_request(BD_ADDR bda) +{ + SMP_TRACE_DEBUG("%s state = %d\n", __func__, smp_cb.state); + BOOLEAN match = FALSE; + + if (!memcmp(bda, smp_cb.pairing_bda, BD_ADDR_LEN)) { + match = TRUE; + } else { + BD_ADDR dummy_bda = {0}; + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev(bda); + if (p_dev_rec != NULL && + 0 == memcmp(p_dev_rec->ble.pseudo_addr, smp_cb.pairing_bda, BD_ADDR_LEN) && + 0 != memcmp(p_dev_rec->ble.pseudo_addr, dummy_bda, BD_ADDR_LEN)) { + match = TRUE; + } + } + + if (match && smp_cb.state == SMP_STATE_ENCRYPTION_PENDING) { + smp_sm_event(&smp_cb, SMP_ENC_REQ_EVT, NULL); + return TRUE; + } + + return FALSE; +} + +/******************************************************************************* +** +** Function smp_process_secure_connection_long_term_key +** +** Description This function is called to process SC LTK. +** SC LTK is calculated and used instead of STK. +** Here SC LTK is saved in BLE DB. +** +** Returns void +** +*******************************************************************************/ +void smp_process_secure_connection_long_term_key(void) +{ + tSMP_CB *p_cb = &smp_cb; + + SMP_TRACE_DEBUG("%s\n", __func__); + smp_save_secure_connections_long_term_key(p_cb); + + smp_update_key_mask (p_cb, SMP_SEC_KEY_TYPE_ENC, FALSE); + smp_key_distribution(p_cb, NULL); +} + +/******************************************************************************* +** +** Function smp_set_derive_link_key +** +** Description This function is called to set flag that indicates that +** BR/EDR LK has to be derived from LTK after all keys are +** distributed. +** +** Returns void +** +*******************************************************************************/ +void smp_set_derive_link_key(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + SMP_TRACE_DEBUG ("%s\n", __func__); + p_cb->derive_lk = TRUE; + smp_update_key_mask (p_cb, SMP_SEC_KEY_TYPE_LK, FALSE); + smp_key_distribution(p_cb, NULL); +} + +/******************************************************************************* +** +** Function smp_derive_link_key_from_long_term_key +** +** Description This function is called to derive BR/EDR LK from LTK. +** +** Returns void +** +*******************************************************************************/ +void smp_derive_link_key_from_long_term_key(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + tSMP_STATUS status = SMP_PAIR_FAIL_UNKNOWN; + + SMP_TRACE_DEBUG("%s\n", __func__); + if (!smp_calculate_link_key_from_long_term_key(p_cb)) { + SMP_TRACE_ERROR("%s failed\n", __FUNCTION__); + smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &status); + return; + } +} +#endif ///BLE_INCLUDED == TRUE + +#if (CLASSIC_BT_INCLUDED == TRUE) +/******************************************************************************* +** +** Function smp_br_process_link_key +** +** Description This function is called to process BR/EDR LK: +** - to derive SMP LTK from BR/EDR LK; +*8 - to save SMP LTK. +** +** Returns void +** +*******************************************************************************/ +void smp_br_process_link_key(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + tSMP_STATUS status = SMP_PAIR_FAIL_UNKNOWN; + + SMP_TRACE_DEBUG("%s\n", __func__); + if (!smp_calculate_long_term_key_from_link_key(p_cb)) { + SMP_TRACE_ERROR ("%s failed\n", __FUNCTION__); + smp_sm_event(p_cb, SMP_BR_AUTH_CMPL_EVT, &status); + return; + } + + SMP_TRACE_DEBUG("%s: LTK derivation from LK successfully completed\n", __FUNCTION__); + smp_save_secure_connections_long_term_key(p_cb); + smp_update_key_mask (p_cb, SMP_SEC_KEY_TYPE_ENC, FALSE); + smp_br_select_next_key(p_cb, NULL); +} +#endif ///CLASSIC_BT_INCLUDED == TRUE + +/******************************************************************************* +** Function smp_key_distribution_by_transport +** Description depending on the transport used at the moment calls either +** smp_key_distribution(...) or smp_br_key_distribution(...). +*******************************************************************************/ +void smp_key_distribution_by_transport(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + SMP_TRACE_DEBUG("%s\n", __func__); + if (p_cb->smp_over_br) { +#if (CLASSIC_BT_INCLUDED == TRUE) + smp_br_select_next_key(p_cb, NULL); +#endif ///CLASSIC_BT_INCLUDED == TRUE + } else { +#if (BLE_INCLUDED == TRUE) + smp_key_distribution(p_cb, NULL); +#endif ///BLE_INCLUDED == TRUE + } +} + +/******************************************************************************* +** Function smp_br_pairing_complete +** Description This function is called to send the pairing complete callback +** and remove the connection if needed. +*******************************************************************************/ +void smp_br_pairing_complete(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + SMP_TRACE_DEBUG("%s\n", __func__); + + if (p_cb->total_tx_unacked == 0) { + /* process the pairing complete */ + smp_proc_pairing_cmpl(p_cb); + } +} + +#endif diff --git a/lib/bt/host/bluedroid/stack/smp/smp_api.c b/lib/bt/host/bluedroid/stack/smp/smp_api.c new file mode 100644 index 00000000..78e295f4 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/smp/smp_api.c @@ -0,0 +1,615 @@ +/****************************************************************************** + * + * Copyright (C) 2008-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains the implementation of the SMP interface used by + * applications that can run over an SMP. + * + ******************************************************************************/ +#include + +#include "common/bt_target.h" +//#include "bt_utils.h" +#if SMP_INCLUDED == TRUE +#include "smp_int.h" +#include "stack/smp_api.h" +#include "stack/l2cdefs.h" +#include "l2c_int.h" +#include "btm_int.h" +#include "stack/hcimsgs.h" + +#include "stack/btu.h" +#include "p_256_ecc_pp.h" +#include "osi/allocator.h" + +/******************************************************************************* +** +** Function SMP_Init +** +** Description This function initializes the SMP unit. +** +** Returns void +** +*******************************************************************************/ +void SMP_Init(void) +{ +#if SMP_DYNAMIC_MEMORY + smp_cb_ptr = (tSMP_CB *)osi_malloc(sizeof(tSMP_CB)); + curve_ptr = (elliptic_curve_t *)osi_malloc(sizeof(elliptic_curve_t)); + curve_p256_ptr = (elliptic_curve_t *)osi_malloc(sizeof(elliptic_curve_t)); +#endif + memset(&smp_cb, 0, sizeof(tSMP_CB)); + memset(&curve, 0, sizeof(elliptic_curve_t)); + memset(&curve_p256, 0, sizeof(elliptic_curve_t)); + +#if defined(SMP_INITIAL_TRACE_LEVEL) + smp_cb.trace_level = SMP_INITIAL_TRACE_LEVEL; +#else + smp_cb.trace_level = BT_TRACE_LEVEL_NONE; /* No traces */ +#endif + SMP_TRACE_EVENT ("%s", __FUNCTION__); + + smp_l2cap_if_init(); + /* initialization of P-256 parameters */ + p_256_init_curve(KEY_LENGTH_DWORDS_P256); +} + +void SMP_Free(void) +{ + memset(&smp_cb, 0, sizeof(tSMP_CB)); +#if SMP_DYNAMIC_MEMORY + FREE_AND_RESET(smp_cb_ptr); + FREE_AND_RESET(curve_ptr); + FREE_AND_RESET(curve_p256_ptr); +#endif /* #if SMP_DYNAMIC_MEMORY */ +} + + +/******************************************************************************* +** +** Function SMP_SetTraceLevel +** +** Description This function sets the trace level for SMP. If called with +** a value of 0xFF, it simply returns the current trace level. +** +** Input Parameters: +** level: The level to set the GATT tracing to: +** 0xff-returns the current setting. +** 0-turns off tracing. +** >= 1-Errors. +** >= 2-Warnings. +** >= 3-APIs. +** >= 4-Events. +** >= 5-Debug. +** +** Returns The new or current trace level +** +*******************************************************************************/ +extern UINT8 SMP_SetTraceLevel (UINT8 new_level) +{ + if (new_level != 0xFF) { + smp_cb.trace_level = new_level; + } + + return (smp_cb.trace_level); +} + + +/******************************************************************************* +** +** Function SMP_Register +** +** Description This function register for the SMP services callback. +** +** Returns void +** +*******************************************************************************/ +BOOLEAN SMP_Register (tSMP_CALLBACK *p_cback) +{ + SMP_TRACE_EVENT ("SMP_Register state=%d", smp_cb.state); + + if (smp_cb.p_callback != NULL) { + SMP_TRACE_WARNING ("SMP_Register: duplicate registration, overwrite it"); + } + smp_cb.p_callback = p_cback; + + return (TRUE); + +} + +/******************************************************************************* +** +** Function SMP_Pair +** +** Description This function call to perform a SMP pairing with peer device. +** Device support one SMP pairing at one time. +** +** Parameters bd_addr - peer device bd address. +** +** Returns None +** +*******************************************************************************/ +tSMP_STATUS SMP_Pair (BD_ADDR bd_addr) +{ + tSMP_CB *p_cb = &smp_cb; + UINT8 status = SMP_PAIR_INTERNAL_ERR; + + SMP_TRACE_EVENT ("%s state=%d br_state=%d flag=0x%x \n", + __FUNCTION__, p_cb->state, p_cb->br_state, p_cb->flags); + if (p_cb->state != SMP_STATE_IDLE || p_cb->flags & SMP_PAIR_FLAGS_WE_STARTED_DD || + p_cb->smp_over_br) { + /* pending security on going, reject this one */ + return SMP_BUSY; + } else { + p_cb->flags = SMP_PAIR_FLAGS_WE_STARTED_DD; + + memcpy (p_cb->pairing_bda, bd_addr, BD_ADDR_LEN); + + if (!L2CA_ConnectFixedChnl (L2CAP_SMP_CID, bd_addr, BLE_ADDR_UNKNOWN_TYPE, FALSE)) { + SMP_TRACE_ERROR("%s: L2C connect fixed channel failed.\n", __FUNCTION__); + smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &status); + return status; + } + + return SMP_STARTED; + } +} + +/******************************************************************************* +** +** Function SMP_BR_PairWith +** +** Description This function is called to start a SMP pairing over BR/EDR. +** Device support one SMP pairing at one time. +** +** Parameters bd_addr - peer device bd address. +** +** Returns SMP_STARTED if pairing started, otherwise reason for failure. +** +*******************************************************************************/ +#if (CLASSIC_BT_INCLUDED == TRUE) +tSMP_STATUS SMP_BR_PairWith (BD_ADDR bd_addr) +{ + tSMP_CB *p_cb = &smp_cb; + UINT8 status = SMP_PAIR_INTERNAL_ERR; + + SMP_TRACE_EVENT ("%s state=%d br_state=%d flag=0x%x ", + __func__, p_cb->state, p_cb->br_state, p_cb->flags); + + if (p_cb->state != SMP_STATE_IDLE || + p_cb->smp_over_br || + p_cb->flags & SMP_PAIR_FLAGS_WE_STARTED_DD) { + /* pending security on going, reject this one */ + return SMP_BUSY; + } + + p_cb->role = HCI_ROLE_MASTER; + p_cb->flags = SMP_PAIR_FLAGS_WE_STARTED_DD; + p_cb->smp_over_br = TRUE; + + memcpy (p_cb->pairing_bda, bd_addr, BD_ADDR_LEN); + + if (!L2CA_ConnectFixedChnl (L2CAP_SMP_BR_CID, bd_addr, BLE_ADDR_UNKNOWN_TYPE, FALSE)) { + SMP_TRACE_ERROR("%s: L2C connect fixed channel failed.", __FUNCTION__); + smp_br_state_machine_event(p_cb, SMP_BR_AUTH_CMPL_EVT, &status); + return status; + } + + return SMP_STARTED; +} +#endif ///CLASSIC_BT_INCLUDED == TRUE + +/******************************************************************************* +** +** Function SMP_PairCancel +** +** Description This function call to cancel a SMP pairing with peer device. +** +** Parameters bd_addr - peer device bd address. +** +** Returns TRUE - Pairining is cancelled +** +*******************************************************************************/ +BOOLEAN SMP_PairCancel (BD_ADDR bd_addr) +{ + tSMP_CB *p_cb = &smp_cb; + UINT8 err_code = SMP_PAIR_FAIL_UNKNOWN; + BOOLEAN status = FALSE; + + BTM_TRACE_EVENT ("SMP_CancelPair state=%d flag=0x%x ", p_cb->state, p_cb->flags); + if ( (p_cb->state != SMP_STATE_IDLE) && + (!memcmp (p_cb->pairing_bda, bd_addr, BD_ADDR_LEN)) ) { + p_cb->is_pair_cancel = TRUE; + SMP_TRACE_DEBUG("Cancel Pairing: set fail reason Unknown"); + smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &err_code); + status = TRUE; + } + + return status; +} +/******************************************************************************* +** +** Function SMP_SecurityGrant +** +** Description This function is called to grant security process. +** +** Parameters bd_addr - peer device bd address. +** res - result of the operation SMP_SUCCESS if success. +** Otherwise, SMP_REPEATED_ATTEMPTS is too many attempts. +** +** Returns None +** +*******************************************************************************/ +void SMP_SecurityGrant(BD_ADDR bd_addr, UINT8 res) +{ + SMP_TRACE_EVENT ("SMP_SecurityGrant "); + +#if (CLASSIC_BT_INCLUDED == TRUE) + if (smp_cb.smp_over_br) { + if (smp_cb.br_state != SMP_BR_STATE_WAIT_APP_RSP || + smp_cb.cb_evt != SMP_SEC_REQUEST_EVT || + memcmp (smp_cb.pairing_bda, bd_addr, BD_ADDR_LEN)) { + return; + } + + /* clear the SMP_SEC_REQUEST_EVT event after get grant */ + /* avoid generating duplicate pair request */ + smp_cb.cb_evt = 0; + smp_br_state_machine_event(&smp_cb, SMP_BR_API_SEC_GRANT_EVT, &res); + return; + } +#endif ///CLASSIC_BT_INCLUDED == TRUE + + if (smp_cb.state != SMP_STATE_WAIT_APP_RSP || + smp_cb.cb_evt != SMP_SEC_REQUEST_EVT || + memcmp (smp_cb.pairing_bda, bd_addr, BD_ADDR_LEN)) { + return; + } + /* clear the SMP_SEC_REQUEST_EVT event after get grant */ + /* avoid generate duplicate pair request */ + smp_cb.cb_evt = 0; + smp_sm_event(&smp_cb, SMP_API_SEC_GRANT_EVT, &res); +} + +/******************************************************************************* +** +** Function SMP_PasskeyReply +** +** Description This function is called after Security Manager submitted +** passkey request to the application. +** +** Parameters: bd_addr - Address of the device for which passkey was requested +** res - result of the operation SMP_SUCCESS if success +** passkey - numeric value in the range of +** BTM_MIN_PASSKEY_VAL(0) - BTM_MAX_PASSKEY_VAL(999999(0xF423F)). +** +*******************************************************************************/ +void SMP_PasskeyReply (BD_ADDR bd_addr, UINT8 res, UINT32 passkey) +{ + tSMP_CB *p_cb = & smp_cb; + UINT8 failure = SMP_PASSKEY_ENTRY_FAIL; + + SMP_TRACE_EVENT ("SMP_PasskeyReply: Key: %d Result:%d", + passkey, res); + + /* If timeout already expired or has been canceled, ignore the reply */ + if (p_cb->cb_evt != SMP_PASSKEY_REQ_EVT) { + SMP_TRACE_WARNING ("SMP_PasskeyReply() - Wrong State: %d", p_cb->state); + return; + } + + if (memcmp (bd_addr, p_cb->pairing_bda, BD_ADDR_LEN) != 0) { + SMP_TRACE_ERROR ("SMP_PasskeyReply() - Wrong BD Addr"); + return; + } + + if (btm_find_dev (bd_addr) == NULL) { + SMP_TRACE_ERROR ("SMP_PasskeyReply() - no dev CB"); + return; + } + + if (passkey > BTM_MAX_PASSKEY_VAL || res != SMP_SUCCESS) { + SMP_TRACE_WARNING ("SMP_PasskeyReply() - Wrong key len: %d or passkey entry fail", passkey); + /* send pairing failure */ + smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &failure); + + } else if (p_cb->selected_association_model == SMP_MODEL_SEC_CONN_PASSKEY_ENT) { + smp_sm_event(&smp_cb, SMP_SC_KEY_READY_EVT, &passkey); + } else { + smp_convert_string_to_tk(p_cb->tk, passkey); + } + + return; +} + +/******************************************************************************* +** +** Function SMP_SetStaticPasskey +** +** Description This function is called to set static passkey +** +** +** Parameters: add - set static passkey when add is TRUE +** clear static passkey when add is FALSE +** passkey - static passkey +** +** +*******************************************************************************/ +void SMP_SetStaticPasskey (BOOLEAN add, UINT32 passkey) +{ + SMP_TRACE_DEBUG("static passkey %6d", passkey); + tSMP_CB *p_cb = & smp_cb; + if(add) { + p_cb->static_passkey = passkey; + p_cb->use_static_passkey = true; + } else { + p_cb->static_passkey = 0; + p_cb->use_static_passkey = false; + } +} + +/******************************************************************************* +** +** Function SMP_ConfirmReply +** +** Description This function is called after Security Manager submitted +** numeric comparison request to the application. +** +** Parameters: bd_addr - Address of the device with which numeric +** comparison was requested +** res - comparison result SMP_SUCCESS if success +** +*******************************************************************************/ +void SMP_ConfirmReply (BD_ADDR bd_addr, UINT8 res) +{ + tSMP_CB *p_cb = & smp_cb; + UINT8 failure = SMP_NUMERIC_COMPAR_FAIL; + + SMP_TRACE_EVENT ("%s: Result:%d", __FUNCTION__, res); + + /* If timeout already expired or has been canceled, ignore the reply */ + if (p_cb->cb_evt != SMP_NC_REQ_EVT) { + SMP_TRACE_WARNING ("%s() - Wrong State: %d", __FUNCTION__, p_cb->state); + return; + } + + if (memcmp (bd_addr, p_cb->pairing_bda, BD_ADDR_LEN) != 0) { + SMP_TRACE_ERROR ("%s() - Wrong BD Addr", __FUNCTION__); + return; + } + + if (btm_find_dev (bd_addr) == NULL) { + SMP_TRACE_ERROR ("%s() - no dev CB", __FUNCTION__); + return; + } + + if (res != SMP_SUCCESS) { + SMP_TRACE_WARNING ("%s() - Numeric Comparison fails", __FUNCTION__); + /* send pairing failure */ + smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &failure); + } else { + smp_sm_event(p_cb, SMP_SC_NC_OK_EVT, NULL); + } +} + +/******************************************************************************* +** +** Function SMP_OobDataReply +** +** Description This function is called to provide the OOB data for +** SMP in response to SMP_OOB_REQ_EVT +** +** Parameters: bd_addr - Address of the peer device +** res - result of the operation SMP_SUCCESS if success +** p_data - simple pairing Randomizer C. +** +*******************************************************************************/ +void SMP_OobDataReply(BD_ADDR bd_addr, tSMP_STATUS res, UINT8 len, UINT8 *p_data) +{ + tSMP_CB *p_cb = & smp_cb; + UINT8 failure = SMP_OOB_FAIL; + tSMP_KEY key; + + SMP_TRACE_EVENT ("%s State: %d res:%d", __FUNCTION__, smp_cb.state, res); + + /* If timeout already expired or has been canceled, ignore the reply */ + if (p_cb->state != SMP_STATE_WAIT_APP_RSP || p_cb->cb_evt != SMP_OOB_REQ_EVT) { + return; + } + + if (res != SMP_SUCCESS || len == 0 || !p_data) { + smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &failure); + } else { + if (len > BT_OCTET16_LEN) { + len = BT_OCTET16_LEN; + } + + memcpy(p_cb->tk, p_data, len); + + key.key_type = SMP_KEY_TYPE_TK; + key.p_data = p_cb->tk; + + smp_sm_event(&smp_cb, SMP_KEY_READY_EVT, &key); + } +} + +/******************************************************************************* +** +** Function SMP_SecureConnectionOobDataReply +** +** Description This function is called to provide the SC OOB data for +** SMP in response to SMP_SC_OOB_REQ_EVT +** +** Parameters: p_data - pointer to the data +** +*******************************************************************************/ +void SMP_SecureConnectionOobDataReply(UINT8 *p_data) +{ + tSMP_CB *p_cb = &smp_cb; + + UINT8 failure = SMP_OOB_FAIL; + tSMP_SC_OOB_DATA *p_oob = (tSMP_SC_OOB_DATA *) p_data; + if (!p_oob) { + SMP_TRACE_ERROR("%s received no data", __FUNCTION__); + smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &failure); + return; + } + + /* Set local oob data when req_oob_type = SMP_OOB_BOTH */ + memcpy(&p_oob->loc_oob_data, smp_get_local_oob_data(), sizeof(tSMP_LOC_OOB_DATA)); + + SMP_TRACE_EVENT ("%s req_oob_type: %d, loc_oob_data.present: %d, " + "peer_oob_data.present: %d", + __FUNCTION__, p_cb->req_oob_type, p_oob->loc_oob_data.present, + p_oob->peer_oob_data.present); + + if (p_cb->state != SMP_STATE_WAIT_APP_RSP || p_cb->cb_evt != SMP_SC_OOB_REQ_EVT) { + return; + } + + BOOLEAN data_missing = FALSE; + switch (p_cb->req_oob_type) { + case SMP_OOB_PEER: + if (!p_oob->peer_oob_data.present) { + data_missing = TRUE; + } + break; + case SMP_OOB_LOCAL: + if (!p_oob->loc_oob_data.present) { + data_missing = TRUE; + } + break; + case SMP_OOB_BOTH: + if (!p_oob->loc_oob_data.present || !p_oob->peer_oob_data.present) { + data_missing = TRUE; + } + break; + default: + SMP_TRACE_EVENT ("Unexpected OOB data type requested. Fail OOB"); + data_missing = TRUE; + break; + } + + if (data_missing) { + SMP_TRACE_ERROR("%s data missing", __func__); + smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &failure); + return; + } + + p_cb->sc_oob_data = *p_oob; + + smp_sm_event(&smp_cb, SMP_SC_OOB_DATA_EVT, p_data); +} + +/******************************************************************************* +** +** Function SMP_Encrypt +** +** Description This function is called to encrypt the data with the specified +** key +** +** Parameters: key - Pointer to key key[0] conatins the MSB +** key_len - key length +** plain_text - Pointer to data to be encrypted +** plain_text[0] conatins the MSB +** pt_len - plain text length +** p_out - output of the encrypted texts +** +** Returns Boolean - request is successful +*******************************************************************************/ +BOOLEAN SMP_Encrypt (UINT8 *key, UINT8 key_len, + UINT8 *plain_text, UINT8 pt_len, + tSMP_ENC *p_out) + +{ + BOOLEAN status = FALSE; + status = smp_encrypt_data(key, key_len, plain_text, pt_len, p_out); + return status; +} + +/******************************************************************************* +** +** Function SMP_KeypressNotification +** +** Description This function is called to notify Security Manager about Keypress Notification. +** +** Parameters: bd_addr Address of the device to send keypress notification to +** value Keypress notification parameter value +** +*******************************************************************************/ +void SMP_KeypressNotification (BD_ADDR bd_addr, UINT8 value) +{ + tSMP_CB *p_cb = &smp_cb; + + SMP_TRACE_EVENT ("%s: Value: %d", __FUNCTION__, value); + + if (memcmp (bd_addr, p_cb->pairing_bda, BD_ADDR_LEN) != 0) { + SMP_TRACE_ERROR ("%s() - Wrong BD Addr", __FUNCTION__); + return; + } + + if (btm_find_dev (bd_addr) == NULL) { + SMP_TRACE_ERROR ("%s() - no dev CB", __FUNCTION__); + return; + } + + /* Keypress Notification is used by a device with KeyboardOnly IO capabilities */ + /* during the passkey entry protocol */ + if (p_cb->local_io_capability != SMP_IO_CAP_IN) { + SMP_TRACE_ERROR ("%s() - wrong local IO capabilities %d", + __FUNCTION__, p_cb->local_io_capability); + return; + } + + if (p_cb->selected_association_model != SMP_MODEL_SEC_CONN_PASSKEY_ENT) { + SMP_TRACE_ERROR ("%s() - wrong protocol %d", __FUNCTION__, + p_cb->selected_association_model); + return; + } + + smp_sm_event(p_cb, SMP_KEYPRESS_NOTIFICATION_EVENT, &value); +} + +/******************************************************************************* +** +** Function SMP_CreateLocalSecureConnectionsOobData +** +** Description This function is called to start creation of local SC OOB +** data set (tSMP_LOC_OOB_DATA). +** +** Returns Boolean - TRUE: creation of local SC OOB data set started. +*******************************************************************************/ +BOOLEAN SMP_CreateLocalSecureConnectionsOobData (void) +{ + tSMP_CB *p_cb = &smp_cb; + + SMP_TRACE_EVENT ("%s state: %u, br_state: %u", __FUNCTION__, p_cb->state, p_cb->br_state); + + if ((p_cb->state != SMP_STATE_IDLE) || (p_cb->smp_over_br)) { + SMP_TRACE_WARNING ("%s creation of local OOB data set "\ + "starts only in IDLE state", __FUNCTION__); + return FALSE; + } + + smp_sm_event(p_cb, SMP_CR_LOC_SC_OOB_DATA_EVT, NULL); + + return TRUE; +} + +#endif /* SMP_INCLUDED */ diff --git a/lib/bt/host/bluedroid/stack/smp/smp_br_main.c b/lib/bt/host/bluedroid/stack/smp/smp_br_main.c new file mode 100644 index 00000000..5ef7a7c6 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/smp/smp_br_main.c @@ -0,0 +1,369 @@ +/****************************************************************************** + * + * Copyright (C) 2014-2015 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +#include "common/bt_target.h" + +#include +#include "smp_int.h" + +#if ( CLASSIC_BT_INCLUDED== TRUE && SMP_INCLUDED == TRUE) + +const char *const smp_br_state_name [SMP_BR_STATE_MAX + 1] = { + "SMP_BR_STATE_IDLE", + "SMP_BR_STATE_WAIT_APP_RSP", + "SMP_BR_STATE_PAIR_REQ_RSP", + "SMP_BR_STATE_BOND_PENDING", + "SMP_BR_STATE_OUT_OF_RANGE" +}; + +const char *const smp_br_event_name [SMP_BR_MAX_EVT] = { + "BR_PAIRING_REQ_EVT", + "BR_PAIRING_RSP_EVT", + "BR_CONFIRM_EVT", + "BR_RAND_EVT", + "BR_PAIRING_FAILED_EVT", + "BR_ENCRPTION_INFO_EVT", + "BR_MASTER_ID_EVT", + "BR_ID_INFO_EVT", + "BR_ID_ADDR_EVT", + "BR_SIGN_INFO_EVT", + "BR_SECURITY_REQ_EVT", + "BR_PAIR_PUBLIC_KEY_EVT", + "BR_PAIR_DHKEY_CHCK_EVT", + "BR_PAIR_KEYPR_NOTIF_EVT", + "BR_KEY_READY_EVT", + "BR_ENCRYPTED_EVT", + "BR_L2CAP_CONN_EVT", + "BR_L2CAP_DISCONN_EVT", + "BR_KEYS_RSP_EVT", + "BR_API_SEC_GRANT_EVT", + "BR_TK_REQ_EVT", + "BR_AUTH_CMPL_EVT", + "BR_ENC_REQ_EVT", + "BR_BOND_REQ_EVT", + "BR_DISCARD_SEC_REQ_EVT", + "BR_OUT_OF_RANGE_EVT" +}; + +const char *smp_get_br_event_name(tSMP_BR_EVENT event); +const char *smp_get_br_state_name(tSMP_BR_STATE state); + +#define SMP_BR_SM_IGNORE 0 +#define SMP_BR_NUM_ACTIONS 2 +#define SMP_BR_SME_NEXT_STATE 2 +#define SMP_BR_SM_NUM_COLS 3 +typedef const UINT8 (*tSMP_BR_SM_TBL)[SMP_BR_SM_NUM_COLS]; + +enum { + SMP_SEND_PAIR_REQ, + SMP_BR_SEND_PAIR_RSP, + SMP_SEND_PAIR_FAIL, + SMP_SEND_ID_INFO, + SMP_BR_PROC_PAIR_CMD, + SMP_PROC_PAIR_FAIL, + SMP_PROC_ID_INFO, + SMP_PROC_ID_ADDR, + SMP_PROC_SRK_INFO, + SMP_BR_PROC_SEC_GRANT, + SMP_BR_PROC_SL_KEYS_RSP, + SMP_BR_KEY_DISTRIBUTION, + SMP_BR_PAIRING_COMPLETE, + SMP_SEND_APP_CBACK, + SMP_BR_CHECK_AUTH_REQ, + SMP_PAIR_TERMINATE, + SMP_IDLE_TERMINATE, + SMP_BR_SM_NO_ACTION +}; + +static const tSMP_ACT smp_br_sm_action[] = { + smp_send_pair_req, + smp_br_send_pair_response, + smp_send_pair_fail, + smp_send_id_info, + smp_br_process_pairing_command, + smp_proc_pair_fail, + smp_proc_id_info, + smp_proc_id_addr, + smp_proc_srk_info, + smp_br_process_security_grant, + smp_br_process_slave_keys_response, + smp_br_select_next_key, + smp_br_pairing_complete, + smp_send_app_cback, + smp_br_check_authorization_request, + smp_pair_terminate, + smp_idle_terminate +}; + +static const UINT8 smp_br_all_table[][SMP_BR_SM_NUM_COLS] = { + /* Event Action Next State */ + /* BR_PAIRING_FAILED */ {SMP_PROC_PAIR_FAIL, SMP_BR_PAIRING_COMPLETE, SMP_BR_STATE_IDLE}, + /* BR_AUTH_CMPL */ {SMP_SEND_PAIR_FAIL, SMP_BR_PAIRING_COMPLETE, SMP_BR_STATE_IDLE}, + /* BR_L2CAP_DISCONN */ {SMP_PAIR_TERMINATE, SMP_BR_SM_NO_ACTION, SMP_BR_STATE_IDLE} +}; + +/************ SMP Master FSM State/Event Indirection Table **************/ +static const UINT8 smp_br_master_entry_map[][SMP_BR_STATE_MAX] = { + /* br_state name: Idle WaitApp Pair Bond + Rsp ReqRsp Pend */ + /* BR_PAIRING_REQ */ { 0, 0, 0, 0 }, + /* BR_PAIRING_RSP */ { 0, 0, 1, 0 }, + /* BR_CONFIRM */ { 0, 0, 0, 0 }, + /* BR_RAND */ { 0, 0, 0, 0 }, + /* BR_PAIRING_FAILED */ { 0, 0x81, 0x81, 0 }, + /* BR_ENCRPTION_INFO */ { 0, 0, 0, 0 }, + /* BR_MASTER_ID */ { 0, 0, 0, 0 }, + /* BR_ID_INFO */ { 0, 0, 0, 1 }, + /* BR_ID_ADDR */ { 0, 0, 0, 2 }, + /* BR_SIGN_INFO */ { 0, 0, 0, 3 }, + /* BR_SECURITY_REQ */ { 0, 0, 0, 0 }, + /* BR_PAIR_PUBLIC_KEY_EVT */ { 0, 0, 0, 0 }, + /* BR_PAIR_DHKEY_CHCK_EVT */ { 0, 0, 0, 0 }, + /* BR_PAIR_KEYPR_NOTIF_EVT */ { 0, 0, 0, 0 }, + /* BR_KEY_READY */ { 0, 0, 0, 0 }, + /* BR_ENCRYPTED */ { 0, 0, 0, 0 }, + /* BR_L2CAP_CONN */ { 1, 0, 0, 0 }, + /* BR_L2CAP_DISCONN */ { 2, 0x83, 0x83, 0x83 }, + /* BR_KEYS_RSP */ { 0, 1, 0, 0 }, + /* BR_API_SEC_GRANT */ { 0, 0, 0, 0 }, + /* BR_TK_REQ */ { 0, 0, 0, 0 }, + /* BR_AUTH_CMPL */ { 0, 0x82, 0x82, 0x82 }, + /* BR_ENC_REQ */ { 0, 0, 0, 0 }, + /* BR_BOND_REQ */ { 0, 0, 2, 0 }, + /* BR_DISCARD_SEC_REQ */ { 0, 0, 0, 0 } +}; + +static const UINT8 smp_br_master_idle_table[][SMP_BR_SM_NUM_COLS] = { + /* Event Action Next State */ + /* BR_L2CAP_CONN */ {SMP_SEND_APP_CBACK, SMP_BR_SM_NO_ACTION, SMP_BR_STATE_WAIT_APP_RSP}, + /* BR_L2CAP_DISCONN */ {SMP_IDLE_TERMINATE, SMP_BR_SM_NO_ACTION, SMP_BR_STATE_IDLE} +}; + +static const UINT8 smp_br_master_wait_appln_response_table[][SMP_BR_SM_NUM_COLS] = { + /* Event Action Next State */ + /* BR_KEYS_RSP */{SMP_SEND_PAIR_REQ, SMP_BR_SM_NO_ACTION, SMP_BR_STATE_PAIR_REQ_RSP} +}; + +static const UINT8 smp_br_master_pair_request_response_table [][SMP_BR_SM_NUM_COLS] = { + /* Event Action Next State */ + /* BR_PAIRING_RSP */ {SMP_BR_PROC_PAIR_CMD, SMP_BR_CHECK_AUTH_REQ, SMP_BR_STATE_PAIR_REQ_RSP}, + /* BR_BOND_REQ */ {SMP_BR_SM_NO_ACTION, SMP_BR_SM_NO_ACTION, SMP_BR_STATE_BOND_PENDING} +}; + +static const UINT8 smp_br_master_bond_pending_table[][SMP_BR_SM_NUM_COLS] = { + /* Event Action Next State */ + /* BR_ID_INFO */{SMP_PROC_ID_INFO, SMP_BR_SM_NO_ACTION, SMP_BR_STATE_BOND_PENDING}, + /* BR_ID_ADDR */{SMP_PROC_ID_ADDR, SMP_BR_SM_NO_ACTION, SMP_BR_STATE_BOND_PENDING}, + /* BR_SIGN_INFO */{SMP_PROC_SRK_INFO, SMP_BR_SM_NO_ACTION, SMP_BR_STATE_BOND_PENDING} +}; + +static const UINT8 smp_br_slave_entry_map[][SMP_BR_STATE_MAX] = { + /* br_state name: Idle WaitApp Pair Bond + Rsp ReqRsp Pend */ + /* BR_PAIRING_REQ */ { 1, 0, 0, 0 }, + /* BR_PAIRING_RSP */ { 0, 0, 0, 0 }, + /* BR_CONFIRM */ { 0, 0, 0, 0 }, + /* BR_RAND */ { 0, 0, 0, 0 }, + /* BR_PAIRING_FAILED */ { 0, 0x81, 0x81, 0x81 }, + /* BR_ENCRPTION_INFO */ { 0, 0, 0, 0 }, + /* BR_MASTER_ID */ { 0, 0, 0, 0 }, + /* BR_ID_INFO */ { 0, 0, 0, 1 }, + /* BR_ID_ADDR */ { 0, 0, 0, 2 }, + /* BR_SIGN_INFO */ { 0, 0, 0, 3 }, + /* BR_SECURITY_REQ */ { 0, 0, 0, 0 }, + /* BR_PAIR_PUBLIC_KEY_EVT */ { 0, 0, 0, 0 }, + /* BR_PAIR_DHKEY_CHCK_EVT */ { 0, 0, 0, 0 }, + /* BR_PAIR_KEYPR_NOTIF_EVT */ { 0, 0, 0, 0 }, + /* BR_KEY_READY */ { 0, 0, 0, 0 }, + /* BR_ENCRYPTED */ { 0, 0, 0, 0 }, + /* BR_L2CAP_CONN */ { 0, 0, 0, 0 }, + /* BR_L2CAP_DISCONN */ { 0, 0x83, 0x83, 0x83 }, + /* BR_KEYS_RSP */ { 0, 2, 0, 0 }, + /* BR_API_SEC_GRANT */ { 0, 1, 0, 0 }, + /* BR_TK_REQ */ { 0, 0, 0, 0 }, + /* BR_AUTH_CMPL */ { 0, 0x82, 0x82, 0x82 }, + /* BR_ENC_REQ */ { 0, 0, 0, 0 }, + /* BR_BOND_REQ */ { 0, 3, 0, 0 }, + /* BR_DISCARD_SEC_REQ */ { 0, 0, 0, 0 } +}; + +static const UINT8 smp_br_slave_idle_table[][SMP_BR_SM_NUM_COLS] = { + /* Event Action Next State */ + /* BR_PAIRING_REQ */ {SMP_BR_PROC_PAIR_CMD, SMP_SEND_APP_CBACK, SMP_BR_STATE_WAIT_APP_RSP} +}; + +static const UINT8 smp_br_slave_wait_appln_response_table [][SMP_BR_SM_NUM_COLS] = { + /* Event Action Next State */ + /* BR_API_SEC_GRANT */ {SMP_BR_PROC_SEC_GRANT, SMP_SEND_APP_CBACK, SMP_BR_STATE_WAIT_APP_RSP}, + /* BR_KEYS_RSP */{SMP_BR_PROC_SL_KEYS_RSP, SMP_BR_CHECK_AUTH_REQ, SMP_BR_STATE_WAIT_APP_RSP}, + /* BR_BOND_REQ */ {SMP_BR_KEY_DISTRIBUTION, SMP_BR_SM_NO_ACTION, SMP_BR_STATE_BOND_PENDING} +}; + +static const UINT8 smp_br_slave_bond_pending_table[][SMP_BR_SM_NUM_COLS] = { + /* Event Action Next State */ + /* BR_ID_INFO */ {SMP_PROC_ID_INFO, SMP_BR_SM_NO_ACTION, SMP_BR_STATE_BOND_PENDING}, + /* BR_ID_ADDR */ {SMP_PROC_ID_ADDR, SMP_BR_SM_NO_ACTION, SMP_BR_STATE_BOND_PENDING}, + /* BR_SIGN_INFO */ {SMP_PROC_SRK_INFO, SMP_BR_SM_NO_ACTION, SMP_BR_STATE_BOND_PENDING} +}; + +static const tSMP_BR_SM_TBL smp_br_state_table[][2] = { + /* SMP_BR_STATE_IDLE */ + {smp_br_master_idle_table, smp_br_slave_idle_table}, + + /* SMP_BR_STATE_WAIT_APP_RSP */ + {smp_br_master_wait_appln_response_table, smp_br_slave_wait_appln_response_table}, + + /* SMP_BR_STATE_PAIR_REQ_RSP */ + {smp_br_master_pair_request_response_table, NULL}, + + /* SMP_BR_STATE_BOND_PENDING */ + {smp_br_master_bond_pending_table, smp_br_slave_bond_pending_table}, +}; + +typedef const UINT8 (*tSMP_BR_ENTRY_TBL)[SMP_BR_STATE_MAX]; + +static const tSMP_BR_ENTRY_TBL smp_br_entry_table[] = { + smp_br_master_entry_map, + smp_br_slave_entry_map +}; + +#define SMP_BR_ALL_TABLE_MASK 0x80 + +/******************************************************************************* +** Function smp_set_br_state +** Returns None +*******************************************************************************/ +void smp_set_br_state(tSMP_BR_STATE br_state) +{ + if (br_state < SMP_BR_STATE_MAX) { + SMP_TRACE_DEBUG( "BR_State change: %s(%d) ==> %s(%d)", + smp_get_br_state_name(smp_cb.br_state), smp_cb.br_state, + smp_get_br_state_name(br_state), br_state ); + smp_cb.br_state = br_state; + } else { + SMP_TRACE_DEBUG("%s invalid br_state =%d", __FUNCTION__, br_state ); + } +} + +/******************************************************************************* +** Function smp_get_br_state +** Returns The smp_br state +*******************************************************************************/ +tSMP_BR_STATE smp_get_br_state(void) +{ + return smp_cb.br_state; +} + +/******************************************************************************* +** Function smp_get_br_state_name +** Returns The smp_br state name. +*******************************************************************************/ +const char *smp_get_br_state_name(tSMP_BR_STATE br_state) +{ + const char *p_str = smp_br_state_name[SMP_BR_STATE_MAX]; + + if (br_state < SMP_BR_STATE_MAX) { + p_str = smp_br_state_name[br_state]; + } + + return p_str; +} +/******************************************************************************* +** Function smp_get_br_event_name +** Returns The smp_br event name. +*******************************************************************************/ +const char *smp_get_br_event_name(tSMP_BR_EVENT event) +{ + const char *p_str = smp_br_event_name[SMP_BR_MAX_EVT - 1]; + + if (event < SMP_BR_MAX_EVT) { + p_str = smp_br_event_name[event - 1]; + } + return p_str; +} + +/******************************************************************************* +** +** Function smp_br_state_machine_event +** +** Description Handle events to the state machine. It looks up the entry +** in the smp_br_entry_table array. +** If it is a valid entry, it gets the state table.Set the next state, +** if not NULL state. Execute the action function according to the +** state table. If the state returned by action function is not NULL +** state, adjust the new state to the returned state. +** +** Returns void. +** +*******************************************************************************/ +void smp_br_state_machine_event(tSMP_CB *p_cb, tSMP_BR_EVENT event, void *p_data) +{ + tSMP_BR_STATE curr_state = p_cb->br_state; + tSMP_BR_SM_TBL state_table; + UINT8 action, entry; + tSMP_BR_ENTRY_TBL entry_table = smp_br_entry_table[p_cb->role]; + + SMP_TRACE_EVENT("main %s", __func__); + if (curr_state >= SMP_BR_STATE_MAX) { + SMP_TRACE_DEBUG( "Invalid br_state: %d", curr_state) ; + return; + } + + SMP_TRACE_DEBUG( "SMP Role: %s State: [%s (%d)], Event: [%s (%d)]", + (p_cb->role == HCI_ROLE_SLAVE) ? "Slave" : "Master", + smp_get_br_state_name( p_cb->br_state), + p_cb->br_state, smp_get_br_event_name(event), event) ; + + /* look up the state table for the current state */ + /* lookup entry / w event & curr_state */ + /* If entry is ignore, return. + * Otherwise, get state table (according to curr_state or all_state) */ + if ((event <= SMP_BR_MAX_EVT) && ( (entry = entry_table[event - 1][curr_state]) + != SMP_BR_SM_IGNORE )) { + if (entry & SMP_BR_ALL_TABLE_MASK) { + entry &= ~SMP_BR_ALL_TABLE_MASK; + state_table = smp_br_all_table; + } else { + state_table = smp_br_state_table[curr_state][p_cb->role]; + } + } else { + SMP_TRACE_DEBUG( "Ignore event [%s (%d)] in state [%s (%d)]", + smp_get_br_event_name(event), event, + smp_get_br_state_name(curr_state), curr_state); + return; + } + + /* Get possible next state from state table. */ + + smp_set_br_state(state_table[entry - 1][SMP_BR_SME_NEXT_STATE]); + + /* If action is not ignore, clear param, exec action and get next state. + * The action function may set the Param for cback. + * Depending on param, call cback or free buffer. */ + /* execute action functions */ + for (UINT8 i = 0; i < SMP_BR_NUM_ACTIONS; i++) { + if ((action = state_table[entry - 1][i]) != SMP_BR_SM_NO_ACTION) { + (*smp_br_sm_action[action])(p_cb, (tSMP_INT_DATA *)p_data); + } else { + break; + } + } + SMP_TRACE_DEBUG( "result state = %s", smp_get_br_state_name( p_cb->br_state ) ) ; +} + +#endif diff --git a/lib/bt/host/bluedroid/stack/smp/smp_cmac.c b/lib/bt/host/bluedroid/stack/smp/smp_cmac.c new file mode 100644 index 00000000..e47c56b7 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/smp/smp_cmac.c @@ -0,0 +1,369 @@ +/****************************************************************************** + * + * Copyright (C) 2008-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains the implementation of the AES128 CMAC algorithm. + * + ******************************************************************************/ + +#include "common/bt_target.h" +#include "osi/allocator.h" + +#if SMP_INCLUDED == TRUE +// #include +#include + +#include "stack/btm_ble_api.h" +#include "smp_int.h" +#include "stack/hcimsgs.h" + +typedef struct { + UINT8 *text; + UINT16 len; + UINT16 round; +} tCMAC_CB; + +tCMAC_CB cmac_cb; + +/* Rb for AES-128 as block cipher, LSB as [0] */ +const BT_OCTET16 const_Rb = { + 0x87, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +void print128(BT_OCTET16 x, const UINT8 *key_name) +{ +#if SMP_DEBUG == TRUE && SMP_DEBUG_VERBOSE == TRUE + UINT8 *p = (UINT8 *)x; + UINT8 i; + + SMP_TRACE_WARNING("%s(MSB ~ LSB) = ", key_name); + + for (i = 0; i < 4; i ++) { + SMP_TRACE_WARNING("%02x %02x %02x %02x", + p[BT_OCTET16_LEN - i * 4 - 1], p[BT_OCTET16_LEN - i * 4 - 2], + p[BT_OCTET16_LEN - i * 4 - 3], p[BT_OCTET16_LEN - i * 4 - 4]); + } +#endif +} + +/******************************************************************************* +** +** Function padding +** +** Description utility function to padding the given text to be a 128 bits +** data. The parameter dest is input and output parameter, it +** must point to a BT_OCTET16_LEN memory space; where include +** length bytes valid data. +** +** Returns void +** +*******************************************************************************/ +static void padding ( BT_OCTET16 dest, UINT8 length ) +{ + UINT8 i, *p = dest; + /* original last block */ + for ( i = length ; i < BT_OCTET16_LEN; i++ ) { + p[BT_OCTET16_LEN - i - 1] = ( i == length ) ? 0x80 : 0; + } +} +/******************************************************************************* +** +** Function leftshift_onebit +** +** Description utility function to left shift one bit for a 128 bits value. +** +** Returns void +** +*******************************************************************************/ +static void leftshift_onebit(UINT8 *input, UINT8 *output) +{ + UINT8 i, overflow = 0 , next_overflow = 0; + SMP_TRACE_EVENT ("leftshift_onebit "); + /* input[0] is LSB */ + for ( i = 0; i < BT_OCTET16_LEN ; i ++ ) { + next_overflow = (input[i] & 0x80) ? 1 : 0; + output[i] = (input[i] << 1) | overflow; + overflow = next_overflow; + } + return; +} +/******************************************************************************* +** +** Function cmac_aes_cleanup +** +** Description clean up function for AES_CMAC algorithm. +** +** Returns void +** +*******************************************************************************/ +static void cmac_aes_cleanup(void) +{ + if (cmac_cb.text != NULL) { + osi_free(cmac_cb.text); + } + memset(&cmac_cb, 0, sizeof(tCMAC_CB)); +} + +/******************************************************************************* +** +** Function cmac_aes_k_calculate +** +** Description This function is the calculation of block cipher using AES-128. +** +** Returns void +** +*******************************************************************************/ +static BOOLEAN cmac_aes_k_calculate(BT_OCTET16 key, UINT8 *p_signature, UINT16 tlen) +{ + tSMP_ENC output; + UINT16 i = 1, err = 0; + UINT8 x[16] = {0}; + UINT8 *p_mac; + + SMP_TRACE_EVENT ("cmac_aes_k_calculate "); + + while (i <= cmac_cb.round) { + smp_xor_128(&cmac_cb.text[(cmac_cb.round - i)*BT_OCTET16_LEN], x); /* Mi' := Mi (+) X */ + + if (!SMP_Encrypt(key, BT_OCTET16_LEN, &cmac_cb.text[(cmac_cb.round - i)*BT_OCTET16_LEN], BT_OCTET16_LEN, &output)) { + err = 1; + break; + } + + memcpy(x, output.param_buf, BT_OCTET16_LEN); + i ++; + } + + if (!err) { + p_mac = output.param_buf + (BT_OCTET16_LEN - tlen); + memcpy(p_signature, p_mac, tlen); + + SMP_TRACE_DEBUG("tlen = %d p_mac = %p", tlen, p_mac); + SMP_TRACE_DEBUG("p_mac[0] = 0x%02x p_mac[1] = 0x%02x p_mac[2] = 0x%02x p_mac[3] = 0x%02x", + *p_mac, *(p_mac + 1), *(p_mac + 2), *(p_mac + 3)); + SMP_TRACE_DEBUG("p_mac[4] = 0x%02x p_mac[5] = 0x%02x p_mac[6] = 0x%02x p_mac[7] = 0x%02x", + *(p_mac + 4), *(p_mac + 5), *(p_mac + 6), *(p_mac + 7)); + + return TRUE; + + } else { + return FALSE; + } +} +/******************************************************************************* +** +** Function cmac_prepare_last_block +** +** Description This function proceeed to prepare the last block of message +** Mn depending on the size of the message. +** +** Returns void +** +*******************************************************************************/ +static void cmac_prepare_last_block (BT_OCTET16 k1, BT_OCTET16 k2) +{ +// UINT8 x[16] = {0}; + BOOLEAN flag; + + SMP_TRACE_EVENT ("cmac_prepare_last_block "); + /* last block is a complete block set flag to 1 */ + flag = ((cmac_cb.len % BT_OCTET16_LEN) == 0 && cmac_cb.len != 0) ? TRUE : FALSE; + + SMP_TRACE_DEBUG("flag = %d round = %d", flag, cmac_cb.round); + + if ( flag ) { + /* last block is complete block */ + smp_xor_128(&cmac_cb.text[0], k1); + } else { /* padding then xor with k2 */ + padding(&cmac_cb.text[0], (UINT8)(cmac_cb.len % 16)); + + smp_xor_128(&cmac_cb.text[0], k2); + } +} +/******************************************************************************* +** +** Function cmac_subkey_cont +** +** Description This is the callback function when CIPHk(0[128]) is completed. +** +** Returns void +** +*******************************************************************************/ +static void cmac_subkey_cont(tSMP_ENC *p) +{ + UINT8 k1[BT_OCTET16_LEN], k2[BT_OCTET16_LEN]; + UINT8 *pp = p->param_buf; + SMP_TRACE_EVENT ("cmac_subkey_cont "); + print128(pp, (const UINT8 *)"K1 before shift"); + + /* If MSB(L) = 0, then K1 = L << 1 */ + if ( (pp[BT_OCTET16_LEN - 1] & 0x80) != 0 ) { + /* Else K1 = ( L << 1 ) (+) Rb */ + leftshift_onebit(pp, k1); + smp_xor_128(k1, const_Rb); + } else { + leftshift_onebit(pp, k1); + } + + if ( (k1[BT_OCTET16_LEN - 1] & 0x80) != 0 ) { + /* K2 = (K1 << 1) (+) Rb */ + leftshift_onebit(k1, k2); + smp_xor_128(k2, const_Rb); + } else { + /* If MSB(K1) = 0, then K2 = K1 << 1 */ + leftshift_onebit(k1, k2); + } + + print128(k1, (const UINT8 *)"K1"); + print128(k2, (const UINT8 *)"K2"); + + cmac_prepare_last_block (k1, k2); +} +/******************************************************************************* +** +** Function cmac_generate_subkey +** +** Description This is the function to generate the two subkeys. +** +** Parameters key - CMAC key, expect SRK when used by SMP. +** +** Returns void +** +*******************************************************************************/ +static BOOLEAN cmac_generate_subkey(BT_OCTET16 key) +{ + BT_OCTET16 z = {0}; + BOOLEAN ret = TRUE; + tSMP_ENC output; + SMP_TRACE_EVENT (" cmac_generate_subkey"); + + if (SMP_Encrypt(key, BT_OCTET16_LEN, z, BT_OCTET16_LEN, &output)) { + cmac_subkey_cont(&output);; + } else { + ret = FALSE; + } + + return ret; +} +/******************************************************************************* +** +** Function aes_cipher_msg_auth_code +** +** Description This is the AES-CMAC Generation Function with tlen implemented. +** +** Parameters key - CMAC key in little endian order, expect SRK when used by SMP. +** input - text to be signed in little endian byte order. +** length - length of the input in byte. +** tlen - lenth of mac desired +** p_signature - data pointer to where signed data to be stored, tlen long. +** +** Returns FALSE if out of resources, TRUE in other cases. +** +*******************************************************************************/ +BOOLEAN aes_cipher_msg_auth_code(BT_OCTET16 key, UINT8 *input, UINT16 length, + UINT16 tlen, UINT8 *p_signature) +{ + UINT16 len, diff; + UINT16 n = (length + BT_OCTET16_LEN - 1) / BT_OCTET16_LEN; /* n is number of rounds */ + BOOLEAN ret = FALSE; + + SMP_TRACE_EVENT ("%s", __func__); + + if (n == 0) { + n = 1; + } + len = n * BT_OCTET16_LEN; + + SMP_TRACE_DEBUG("AES128_CMAC started, allocate buffer size = %d", len); + /* allocate a memory space of multiple of 16 bytes to hold text */ + if ((cmac_cb.text = (UINT8 *)osi_malloc(len)) != NULL) { + cmac_cb.round = n; + + memset(cmac_cb.text, 0, len); + diff = len - length; + + if (input != NULL && length > 0) { + memcpy(&cmac_cb.text[diff] , input, (int)length); + cmac_cb.len = length; + } else { + cmac_cb.len = 0; + } + + /* prepare calculation for subkey s and last block of data */ + if (cmac_generate_subkey(key)) { + /* start calculation */ + ret = cmac_aes_k_calculate(key, p_signature, tlen); + } + /* clean up */ + cmac_aes_cleanup(); + } else { + ret = FALSE; + SMP_TRACE_ERROR("No resources"); + } + + return ret; +} + +#if 0 /* testing code, sample data from spec */ +void test_cmac_cback(UINT8 *p_mac, UINT16 tlen) +{ + SMP_TRACE_EVENT ("test_cmac_cback "); + SMP_TRACE_ERROR("test_cmac_cback"); +} + +void test_cmac(void) +{ + SMP_TRACE_EVENT ("test_cmac "); + UINT8 M[64] = { + 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, + 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a, + 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, + 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51, + 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, + 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef, + 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, + 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10 + }; + + UINT8 key[16] = { + 0x3c, 0x4f, 0xcf, 0x09, 0x88, 0x15, 0xf7, 0xab, + 0xa6, 0xd2, 0xae, 0x28, 0x16, 0x15, 0x7e, 0x2b + }; + UINT8 i = 0, tmp; + UINT16 len; + + len = 64; + + for (i = 0; i < len / 2; i ++) { + tmp = M[i]; + M[i] = M[len - 1 - i]; + M[len - 1 - i] = tmp; + } + + + memset(&cmac_cb, 0, sizeof(tCMAC_CB)); + + SMP_TRACE_WARNING("\n Example 1: len = %d\n", len); + + aes_cipher_msg_auth_code(key, M, len, 128, test_cmac_cback, 0); + +} +#endif +#endif diff --git a/lib/bt/host/bluedroid/stack/smp/smp_keys.c b/lib/bt/host/bluedroid/stack/smp/smp_keys.c new file mode 100644 index 00000000..aec6f709 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/smp/smp_keys.c @@ -0,0 +1,2293 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains security manager protocol utility functions + * + ******************************************************************************/ +#include "common/bt_target.h" + +#if (BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE) +#if SMP_DEBUG == TRUE +#include +#endif +#include +//#include "bt_utils.h" +#include "stack/btm_ble_api.h" +#include "smp_int.h" +#include "btm_int.h" +#include "btm_ble_int.h" +#include "stack/hcimsgs.h" +#include "aes.h" +#include "p_256_ecc_pp.h" +#include "device/controller.h" + +#ifndef SMP_MAX_ENC_REPEAT +#define SMP_MAX_ENC_REPEAT 3 +#endif + +static void smp_rand_back(tBTM_RAND_ENC *p); +static void smp_generate_confirm(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +static void smp_generate_ltk_cont(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +static void smp_generate_y(tSMP_CB *p_cb, tSMP_INT_DATA *p); +static void smp_generate_rand_vector (tSMP_CB *p_cb, tSMP_INT_DATA *p); +static void smp_process_stk(tSMP_CB *p_cb, tSMP_ENC *p); +static void smp_calculate_comfirm_cont(tSMP_CB *p_cb, tSMP_ENC *p); +static void smp_process_confirm(tSMP_CB *p_cb, tSMP_ENC *p); +static void smp_process_compare(tSMP_CB *p_cb, tSMP_ENC *p); +static void smp_process_ediv(tSMP_CB *p_cb, tSMP_ENC *p); +static BOOLEAN smp_calculate_legacy_short_term_key(tSMP_CB *p_cb, tSMP_ENC *output); +static void smp_continue_private_key_creation(tSMP_CB *p_cb, tBTM_RAND_ENC *p); +static void smp_process_private_key(tSMP_CB *p_cb); +static void smp_finish_nonce_generation(tSMP_CB *p_cb); +static void smp_process_new_nonce(tSMP_CB *p_cb); + +static const tSMP_ACT smp_encrypt_action[] = { + smp_generate_compare, /* SMP_GEN_COMPARE */ + smp_generate_confirm, /* SMP_GEN_CONFIRM*/ + smp_generate_stk, /* SMP_GEN_STK*/ + smp_generate_ltk_cont, /* SMP_GEN_LTK */ + smp_generate_ltk, /* SMP_GEN_DIV_LTK */ + smp_generate_rand_vector, /* SMP_GEN_RAND_V */ + smp_generate_y, /* SMP_GEN_EDIV */ + smp_generate_passkey, /* SMP_GEN_TK */ + smp_generate_srand_mrand_confirm, /* SMP_GEN_SRAND_MRAND */ + smp_generate_rand_cont /* SMP_GEN_SRAND_MRAND_CONT */ +}; + +/* If there is data saved here, then use its info instead + * This needs to be cleared on a successful pairing using the oob data + */ +static tSMP_LOC_OOB_DATA saved_local_oob_data = {}; + +void smp_save_local_oob_data(tSMP_CB *p_cb) +{ + memcpy(&saved_local_oob_data, &p_cb->sc_oob_data.loc_oob_data, sizeof(tSMP_LOC_OOB_DATA)); +} + +void smp_clear_local_oob_data(void) +{ + memset(&saved_local_oob_data, 0, sizeof(tSMP_LOC_OOB_DATA)); +} + +static BOOLEAN oob_data_is_empty(tSMP_LOC_OOB_DATA *data) +{ + tSMP_LOC_OOB_DATA empty_data = {0}; + return (memcmp(data, &empty_data, sizeof(tSMP_LOC_OOB_DATA)) == 0); +} + +tSMP_LOC_OOB_DATA *smp_get_local_oob_data(void) +{ + return &saved_local_oob_data; +} + +void smp_debug_print_nbyte_little_endian(UINT8 *p, const UINT8 *key_name, UINT8 len) +{ +#if SMP_DEBUG == TRUE + int ind, x; + int col_count = 32; + int row_count; + UINT8 p_buf[512]; + + SMP_TRACE_WARNING("%s(LSB ~ MSB):\n", key_name); + memset(p_buf, 0, sizeof(p_buf)); + row_count = len % col_count ? len / col_count + 1 : len / col_count; + + ind = 0; + for (int row = 0; row < row_count; row++) { + for (int column = 0, x = 0; (ind < len) && (column < col_count); column++, ind++) { + x += sprintf((char *)&p_buf[x], "%02x ", p[ind]); + } + SMP_TRACE_WARNING(" [%03d]: %s", row * col_count, p_buf); + } +#endif +} + +#if 0 //Unused +void smp_debug_print_nbyte_big_endian (UINT8 *p, const UINT8 *key_name, UINT8 len) +{ +#if SMP_DEBUG == TRUE + UINT8 p_buf[512]; + + SMP_TRACE_WARNING("%s(MSB ~ LSB):", key_name); + memset(p_buf, 0, sizeof(p_buf)); + nrows = len % ncols ? len / ncols + 1 : len / ncols; + + int ind = 0; + int ncols = 32; /* num entries in one line */ + int nrows; /* num lines */ + int x; + + for (int row = 0; row < nrows; row++) { + for (int col = 0, x = 0; (ind < len) && (col < ncols); col++, ind++) { + x += sprintf ((char *)&p_buf[len - x - 1], "%02x ", p[ind]); + } + SMP_TRACE_WARNING("[%03d]: %s", row * ncols, p_buf); + } +#endif +} +#endif + +/******************************************************************************* +** +** Function smp_encrypt_data +** +** Description This function is called to encrypt data. +** It uses AES-128 encryption algorithm. +** Plain_text is encrypted using key, the result is at p_out. +** +** Returns void +** +*******************************************************************************/ +BOOLEAN smp_encrypt_data (UINT8 *key, UINT8 key_len, + UINT8 *plain_text, UINT8 pt_len, + tSMP_ENC *p_out) +{ + aes_context ctx; + UINT8 *p_start = NULL; + UINT8 *p = NULL; + UINT8 *p_rev_data = NULL; /* input data in big endilan format */ + UINT8 *p_rev_key = NULL; /* input key in big endilan format */ + UINT8 *p_rev_output = NULL; /* encrypted output in big endilan format */ + + SMP_TRACE_DEBUG ("%s\n", __func__); + if ( (p_out == NULL ) || (key_len != SMP_ENCRYT_KEY_SIZE) ) { + SMP_TRACE_ERROR ("%s failed\n", __func__); + return FALSE; + } + + if ((p_start = (UINT8 *)osi_malloc((SMP_ENCRYT_DATA_SIZE * 4))) == NULL) { + SMP_TRACE_ERROR ("%s failed unable to allocate buffer\n", __func__); + return FALSE; + } + + if (pt_len > SMP_ENCRYT_DATA_SIZE) { + pt_len = SMP_ENCRYT_DATA_SIZE; + } + + memset(p_start, 0, SMP_ENCRYT_DATA_SIZE * 4); + p = p_start; + ARRAY_TO_STREAM (p, plain_text, pt_len); /* byte 0 to byte 15 */ + p_rev_data = p = p_start + SMP_ENCRYT_DATA_SIZE; /* start at byte 16 */ + REVERSE_ARRAY_TO_STREAM (p, p_start, SMP_ENCRYT_DATA_SIZE); /* byte 16 to byte 31 */ + p_rev_key = p; /* start at byte 32 */ + REVERSE_ARRAY_TO_STREAM (p, key, SMP_ENCRYT_KEY_SIZE); /* byte 32 to byte 47 */ + +#if SMP_DEBUG == TRUE && SMP_DEBUG_VERBOSE == TRUE + smp_debug_print_nbyte_little_endian(key, (const UINT8 *)"Key", SMP_ENCRYT_KEY_SIZE); + smp_debug_print_nbyte_little_endian(p_start, (const UINT8 *)"Plain text", SMP_ENCRYT_DATA_SIZE); +#endif + p_rev_output = p; + aes_set_key(p_rev_key, SMP_ENCRYT_KEY_SIZE, &ctx); + bluedroid_aes_encrypt(p_rev_data, p, &ctx); /* outputs in byte 48 to byte 63 */ + + p = p_out->param_buf; + REVERSE_ARRAY_TO_STREAM (p, p_rev_output, SMP_ENCRYT_DATA_SIZE); +#if SMP_DEBUG == TRUE && SMP_DEBUG_VERBOSE == TRUE + smp_debug_print_nbyte_little_endian(p_out->param_buf, (const UINT8 *)"Encrypted text", SMP_ENCRYT_KEY_SIZE); +#endif + + p_out->param_len = SMP_ENCRYT_KEY_SIZE; + p_out->status = HCI_SUCCESS; + p_out->opcode = HCI_BLE_ENCRYPT; + + osi_free(p_start); + + return TRUE; +} + +void smp_use_static_passkey(void) +{ + tSMP_CB *p_cb = &smp_cb; + UINT8 *tt = p_cb->tk; + tSMP_KEY key; + UINT32 passkey = p_cb->static_passkey; + /* save the TK */ + memset(p_cb->tk, 0, BT_OCTET16_LEN); + UINT32_TO_STREAM(tt, passkey); + + key.key_type = SMP_KEY_TYPE_TK; + key.p_data = p_cb->tk; + + if (p_cb->p_callback) { + (*p_cb->p_callback)(SMP_PASSKEY_NOTIF_EVT, p_cb->pairing_bda, (tSMP_EVT_DATA *)&passkey); + } + + if (p_cb->selected_association_model == SMP_MODEL_SEC_CONN_PASSKEY_DISP) { + smp_sm_event(&smp_cb, SMP_KEY_READY_EVT, &passkey); + } else { + smp_sm_event(p_cb, SMP_KEY_READY_EVT, (tSMP_INT_DATA *)&key); + } +} +/******************************************************************************* +** +** Function smp_generate_passkey +** +** Description This function is called to generate passkey. +** +** Returns void +** +*******************************************************************************/ +void smp_generate_passkey(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + UNUSED(p_data); + + if(p_cb->use_static_passkey) { + SMP_TRACE_DEBUG ("%s use static passkey %6d", __func__, p_cb->static_passkey); + smp_use_static_passkey(); + return; + } + SMP_TRACE_DEBUG ("%s generate rand passkey", __func__); + p_cb->rand_enc_proc_state = SMP_GEN_TK; + + /* generate MRand or SRand */ + if (!btsnd_hcic_ble_rand((void *)smp_rand_back)) { + smp_rand_back(NULL); + } +} + +/******************************************************************************* +** +** Function smp_proc_passkey +** +** Description This function is called to process a passkey. +** +** Returns void +** +*******************************************************************************/ +void smp_proc_passkey(tSMP_CB *p_cb , tBTM_RAND_ENC *p) +{ + UINT8 *tt = p_cb->tk; + tSMP_KEY key; + UINT32 passkey; /* 19655 test number; */ + UINT8 *pp = p->param_buf; + + SMP_TRACE_DEBUG ("%s", __func__); + STREAM_TO_UINT32(passkey, pp); + passkey &= ~SMP_PASSKEY_MASK; + + /* truncate by maximum value */ + while (passkey > BTM_MAX_PASSKEY_VAL) { + passkey >>= 1; + } + + /* save the TK */ + memset(p_cb->tk, 0, BT_OCTET16_LEN); + UINT32_TO_STREAM(tt, passkey); + + key.key_type = SMP_KEY_TYPE_TK; + key.p_data = p_cb->tk; + + if (p_cb->p_callback) { + (*p_cb->p_callback)(SMP_PASSKEY_NOTIF_EVT, p_cb->pairing_bda, (tSMP_EVT_DATA *)&passkey); + } + + if (p_cb->selected_association_model == SMP_MODEL_SEC_CONN_PASSKEY_DISP) { + smp_sm_event(&smp_cb, SMP_KEY_READY_EVT, &passkey); + } else { + smp_sm_event(p_cb, SMP_KEY_READY_EVT, (tSMP_INT_DATA *)&key); + } +} + +/******************************************************************************* +** +** Function smp_generate_stk +** +** Description This function is called to generate STK calculated by running +** AES with the TK value as key and a concatenation of the random +** values. +** +** Returns void +** +*******************************************************************************/ +void smp_generate_stk(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + UNUSED(p_data); + + tSMP_ENC output; + tSMP_STATUS status = SMP_PAIR_FAIL_UNKNOWN; + + SMP_TRACE_DEBUG ("%s\n", __func__); + + if (p_cb->le_secure_connections_mode_is_used) { + SMP_TRACE_WARNING ("FOR LE SC LTK IS USED INSTEAD OF STK"); + output.param_len = SMP_ENCRYT_KEY_SIZE; + output.status = HCI_SUCCESS; + output.opcode = HCI_BLE_ENCRYPT; + memcpy(output.param_buf, p_cb->ltk, SMP_ENCRYT_DATA_SIZE); + } else if (!smp_calculate_legacy_short_term_key(p_cb, &output)) { + SMP_TRACE_ERROR("%s failed", __func__); + smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &status); + return; + } + + smp_process_stk(p_cb, &output); +} + +/******************************************************************************* +** +** Function smp_generate_srand_mrand_confirm +** +** Description This function is called to start the second pairing phase by +** start generating random number. +** +** +** Returns void +** +*******************************************************************************/ +void smp_generate_srand_mrand_confirm(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + UNUSED(p_data); + + SMP_TRACE_DEBUG ("%s\n", __func__); + p_cb->rand_enc_proc_state = SMP_GEN_SRAND_MRAND; + /* generate MRand or SRand */ + if (!btsnd_hcic_ble_rand((void *)smp_rand_back)) { + smp_rand_back(NULL); + } +} + +/******************************************************************************* +** +** Function smp_generate_rand_cont +** +** Description This function is called to generate another 64 bits random for +** MRand or Srand. +** +** Returns void +** +*******************************************************************************/ +void smp_generate_rand_cont(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + UNUSED(p_data); + + SMP_TRACE_DEBUG ("%s\n", __func__); + p_cb->rand_enc_proc_state = SMP_GEN_SRAND_MRAND_CONT; + /* generate 64 MSB of MRand or SRand */ + if (!btsnd_hcic_ble_rand((void *)smp_rand_back)) { + smp_rand_back(NULL); + } +} + +/******************************************************************************* +** +** Function smp_generate_ltk +** +** Description This function is called: +** - in legacy pairing - to calculate LTK, starting with DIV +** generation; +** - in LE Secure Connections pairing over LE transport - to process LTK +** already generated to encrypt LE link; +** - in LE Secure Connections pairing over BR/EDR transport - to start +** BR/EDR Link Key processing. +** +** Returns void +** +*******************************************************************************/ +void smp_generate_ltk(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + UNUSED(p_data); + + BOOLEAN div_status; + SMP_TRACE_DEBUG ("%s\n", __FUNCTION__); +#if (CLASSIC_BT_INCLUDED == TRUE) + if (smp_get_br_state() == SMP_BR_STATE_BOND_PENDING) { + smp_br_process_link_key(p_cb, NULL); + return; + } +#endif ///CLASSIC_BT_INCLUDED == TRUE + if (p_cb->le_secure_connections_mode_is_used) { + smp_process_secure_connection_long_term_key(); + return; + } + + div_status = btm_get_local_div(p_cb->pairing_bda, &p_cb->div); + + if (div_status) { + smp_generate_ltk_cont(p_cb, NULL); + } else { + SMP_TRACE_DEBUG ("Generate DIV for LTK\n"); + p_cb->rand_enc_proc_state = SMP_GEN_DIV_LTK; + /* generate MRand or SRand */ + if (!btsnd_hcic_ble_rand((void *)smp_rand_back)) { + smp_rand_back(NULL); + } + } +} + +/******************************************************************************* +** +** Function smp_compute_csrk +** +** Description This function is called to calculate CSRK +** +** +** Returns void +** +*******************************************************************************/ +void smp_compute_csrk(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + UNUSED(p_data); + + BT_OCTET16 er; + UINT8 buffer[4]; /* for (r || DIV) r=1*/ + UINT16 r = 1; + UINT8 *p = buffer; + tSMP_ENC output; + tSMP_STATUS status = SMP_PAIR_FAIL_UNKNOWN; + + SMP_TRACE_DEBUG ("smp_compute_csrk div=%x\n", p_cb->div); + BTM_GetDeviceEncRoot(er); + /* CSRK = d1(ER, DIV, 1) */ + UINT16_TO_STREAM(p, p_cb->div); + UINT16_TO_STREAM(p, r); + + if (!SMP_Encrypt(er, BT_OCTET16_LEN, buffer, 4, &output)) { + SMP_TRACE_ERROR("smp_generate_csrk failed\n"); + if (p_cb->smp_over_br) { +#if (CLASSIC_BT_INCLUDED == TRUE) + smp_br_state_machine_event(p_cb, SMP_BR_AUTH_CMPL_EVT, &status); +#endif ///CLASSIC_BT_INCLUDED == TRUE + } else { + smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &status); + } + } else { + memcpy((void *)p_cb->csrk, output.param_buf, BT_OCTET16_LEN); + smp_send_csrk_info(p_cb, NULL); + } +} + +/******************************************************************************* +** +** Function smp_generate_csrk +** +** Description This function is called to calculate CSRK, starting with DIV +** generation. +** +** +** Returns void +** +*******************************************************************************/ +void smp_generate_csrk(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + UNUSED(p_data); + + BOOLEAN div_status; + + SMP_TRACE_DEBUG ("smp_generate_csrk"); + + div_status = btm_get_local_div(p_cb->pairing_bda, &p_cb->div); + if (div_status) { + smp_compute_csrk(p_cb, NULL); + } else { + SMP_TRACE_DEBUG ("Generate DIV for CSRK"); + p_cb->rand_enc_proc_state = SMP_GEN_DIV_CSRK; + if (!btsnd_hcic_ble_rand((void *)smp_rand_back)) { + smp_rand_back(NULL); + } + } +} + +/******************************************************************************* +** Function smp_concatenate_peer +** add pairing command sent from local device into p1. +*******************************************************************************/ +void smp_concatenate_local( tSMP_CB *p_cb, UINT8 **p_data, UINT8 op_code) +{ + UINT8 *p = *p_data; + + SMP_TRACE_DEBUG ("%s\n", __func__); + UINT8_TO_STREAM(p, op_code); + UINT8_TO_STREAM(p, p_cb->local_io_capability); + UINT8_TO_STREAM(p, p_cb->loc_oob_flag); + UINT8_TO_STREAM(p, p_cb->loc_auth_req); + UINT8_TO_STREAM(p, p_cb->loc_enc_size); + UINT8_TO_STREAM(p, p_cb->local_i_key); + UINT8_TO_STREAM(p, p_cb->local_r_key); + + *p_data = p; +} + +/******************************************************************************* +** Function smp_concatenate_peer +** add pairing command received from peer device into p1. +*******************************************************************************/ +void smp_concatenate_peer( tSMP_CB *p_cb, UINT8 **p_data, UINT8 op_code) +{ + UINT8 *p = *p_data; + + SMP_TRACE_DEBUG ("smp_concatenate_peer \n"); + UINT8_TO_STREAM(p, op_code); + UINT8_TO_STREAM(p, p_cb->peer_io_caps); + UINT8_TO_STREAM(p, p_cb->peer_oob_flag); + UINT8_TO_STREAM(p, p_cb->peer_auth_req); + UINT8_TO_STREAM(p, p_cb->peer_enc_size); + UINT8_TO_STREAM(p, p_cb->peer_i_key); + UINT8_TO_STREAM(p, p_cb->peer_r_key); + + *p_data = p; +} + +/******************************************************************************* +** +** Function smp_gen_p1_4_confirm +** +** Description Generate Confirm/Compare Step1: +** p1 = pres || preq || rat' || iat' +** +** Returns void +** +*******************************************************************************/ +void smp_gen_p1_4_confirm( tSMP_CB *p_cb, BT_OCTET16 p1) +{ + UINT8 *p = (UINT8 *)p1; + tBLE_ADDR_TYPE addr_type = 0; + BD_ADDR remote_bda; + + SMP_TRACE_DEBUG ("smp_gen_p1_4_confirm\n"); + + if (!BTM_ReadRemoteConnectionAddr(p_cb->pairing_bda, remote_bda, &addr_type)) { + SMP_TRACE_ERROR("can not generate confirm for unknown device\n"); + return; + } + + BTM_ReadConnectionAddr( p_cb->pairing_bda, p_cb->local_bda, &p_cb->addr_type); + + if (p_cb->role == HCI_ROLE_MASTER) { + /* LSB : rat': initiator's(local) address type */ + UINT8_TO_STREAM(p, p_cb->addr_type); + /* LSB : iat': responder's address type */ + UINT8_TO_STREAM(p, addr_type); + /* concatinate preq */ + smp_concatenate_local(p_cb, &p, SMP_OPCODE_PAIRING_REQ); + /* concatinate pres */ + smp_concatenate_peer(p_cb, &p, SMP_OPCODE_PAIRING_RSP); + } else { + /* LSB : iat': initiator's address type */ + UINT8_TO_STREAM(p, addr_type); + /* LSB : rat': responder's(local) address type */ + UINT8_TO_STREAM(p, p_cb->addr_type); + /* concatinate preq */ + smp_concatenate_peer(p_cb, &p, SMP_OPCODE_PAIRING_REQ); + /* concatinate pres */ + smp_concatenate_local(p_cb, &p, SMP_OPCODE_PAIRING_RSP); + } +#if SMP_DEBUG == TRUE + SMP_TRACE_DEBUG("p1 = pres || preq || rat' || iat'\n"); + smp_debug_print_nbyte_little_endian ((UINT8 *)p1, (const UINT8 *)"P1", 16); +#endif +} + +/******************************************************************************* +** +** Function smp_gen_p2_4_confirm +** +** Description Generate Confirm/Compare Step2: +** p2 = padding || ia || ra +** +** Returns void +** +*******************************************************************************/ +void smp_gen_p2_4_confirm( tSMP_CB *p_cb, BT_OCTET16 p2) +{ + UINT8 *p = (UINT8 *)p2; + BD_ADDR remote_bda; + tBLE_ADDR_TYPE addr_type = 0; + SMP_TRACE_DEBUG ("smp_gen_p2_4_confirm\n"); + if (!BTM_ReadRemoteConnectionAddr(p_cb->pairing_bda, remote_bda, &addr_type)) { + SMP_TRACE_ERROR("can not generate confirm p2 for unknown device\n"); + return; + } + + SMP_TRACE_DEBUG ("smp_gen_p2_4_confirm\n"); + + memset(p, 0, sizeof(BT_OCTET16)); + + if (p_cb->role == HCI_ROLE_MASTER) { + /* LSB ra */ + BDADDR_TO_STREAM(p, remote_bda); + /* ia */ + BDADDR_TO_STREAM(p, p_cb->local_bda); + } else { + /* LSB ra */ + BDADDR_TO_STREAM(p, p_cb->local_bda); + /* ia */ + BDADDR_TO_STREAM(p, remote_bda); + } +#if SMP_DEBUG == TRUE + SMP_TRACE_DEBUG("p2 = padding || ia || ra"); + smp_debug_print_nbyte_little_endian(p2, (const UINT8 *)"p2", 16); +#endif +} + +/******************************************************************************* +** +** Function smp_calculate_comfirm +** +** Description This function is called to calculate Confirm value. +** +** Returns void +** +*******************************************************************************/ +void smp_calculate_comfirm (tSMP_CB *p_cb, BT_OCTET16 rand, BD_ADDR bda) +{ + UNUSED(bda); + + BT_OCTET16 p1; + tSMP_ENC output; + tSMP_STATUS status = SMP_PAIR_FAIL_UNKNOWN; + + SMP_TRACE_DEBUG ("smp_calculate_comfirm \n"); + /* generate p1 = pres || preq || rat' || iat' */ + smp_gen_p1_4_confirm(p_cb, p1); + + /* p1 = rand XOR p1 */ + smp_xor_128(p1, rand); + + smp_debug_print_nbyte_little_endian ((UINT8 *)p1, (const UINT8 *)"P1' = r XOR p1", 16); + + /* calculate e(k, r XOR p1), where k = TK */ + if (!SMP_Encrypt(p_cb->tk, BT_OCTET16_LEN, p1, BT_OCTET16_LEN, &output)) { + SMP_TRACE_ERROR("smp_generate_csrk failed"); + smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &status); + } else { + smp_calculate_comfirm_cont(p_cb, &output); + } +} + +/******************************************************************************* +** +** Function smp_calculate_comfirm_cont +** +** Description This function is called when SConfirm/MConfirm is generated +** proceed to send the Confirm request/response to peer device. +** +** Returns void +** +*******************************************************************************/ +static void smp_calculate_comfirm_cont(tSMP_CB *p_cb, tSMP_ENC *p) +{ + BT_OCTET16 p2; + tSMP_ENC output; + tSMP_STATUS status = SMP_PAIR_FAIL_UNKNOWN; + + SMP_TRACE_DEBUG ("smp_calculate_comfirm_cont \n"); +#if SMP_DEBUG == TRUE + SMP_TRACE_DEBUG("Confirm step 1 p1' = e(k, r XOR p1) Generated\n"); + smp_debug_print_nbyte_little_endian (p->param_buf, (const UINT8 *)"C1", 16); +#endif + + smp_gen_p2_4_confirm(p_cb, p2); + + /* calculate p2 = (p1' XOR p2) */ + smp_xor_128(p2, p->param_buf); + smp_debug_print_nbyte_little_endian ((UINT8 *)p2, (const UINT8 *)"p2' = C1 xor p2", 16); + + /* calculate: Confirm = E(k, p1' XOR p2) */ + if (!SMP_Encrypt(p_cb->tk, BT_OCTET16_LEN, p2, BT_OCTET16_LEN, &output)) { + SMP_TRACE_ERROR("smp_calculate_comfirm_cont failed\n"); + smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &status); + } else { + SMP_TRACE_DEBUG("p_cb->rand_enc_proc_state=%d\n", p_cb->rand_enc_proc_state); + switch (p_cb->rand_enc_proc_state) { + case SMP_GEN_CONFIRM: + smp_process_confirm(p_cb, &output); + break; + + case SMP_GEN_COMPARE: + smp_process_compare(p_cb, &output); + break; + } + } +} + +/******************************************************************************* +** +** Function smp_generate_confirm +** +** Description This function is called when a 48 bits random number is generated +** as SRand or MRand, continue to calculate Sconfirm or MConfirm. +** +** Returns void +** +*******************************************************************************/ +static void smp_generate_confirm(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + UNUSED(p_data); + + SMP_TRACE_DEBUG ("%s\n", __func__); + p_cb->rand_enc_proc_state = SMP_GEN_CONFIRM; + smp_debug_print_nbyte_little_endian ((UINT8 *)p_cb->rand, (const UINT8 *)"local rand", 16); + smp_calculate_comfirm(p_cb, p_cb->rand, p_cb->pairing_bda); +} + +/******************************************************************************* +** +** Function smp_generate_compare +** +** Description This function is called to generate SConfirm for Slave device, +** or MSlave for Master device. This function can be also used for +** generating Compare number for confirm value check. +** +** Returns void +** +*******************************************************************************/ +void smp_generate_compare (tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + UNUSED(p_data); + + SMP_TRACE_DEBUG ("smp_generate_compare \n"); + p_cb->rand_enc_proc_state = SMP_GEN_COMPARE; + smp_debug_print_nbyte_little_endian ((UINT8 *)p_cb->rrand, (const UINT8 *)"peer rand", 16); + smp_calculate_comfirm(p_cb, p_cb->rrand, p_cb->local_bda); +} + +/******************************************************************************* +** +** Function smp_process_confirm +** +** Description This function is called when SConfirm/MConfirm is generated +** proceed to send the Confirm request/response to peer device. +** +** Returns void +** +*******************************************************************************/ +static void smp_process_confirm(tSMP_CB *p_cb, tSMP_ENC *p) +{ + tSMP_KEY key; + + SMP_TRACE_DEBUG ("%s\n", __FUNCTION__); + memcpy(p_cb->confirm, p->param_buf, BT_OCTET16_LEN); + +#if (SMP_DEBUG == TRUE) + SMP_TRACE_DEBUG("Confirm Generated"); + smp_debug_print_nbyte_little_endian ((UINT8 *)p_cb->confirm, (const UINT8 *)"Confirm", 16); +#endif + + key.key_type = SMP_KEY_TYPE_CFM; + key.p_data = p->param_buf; + + smp_sm_event(p_cb, SMP_KEY_READY_EVT, &key); + +} + +/******************************************************************************* +** +** Function smp_process_compare +** +** Description This function is called when Compare is generated using the +** RRand and local BDA, TK information. +** +** Returns void +** +*******************************************************************************/ +static void smp_process_compare(tSMP_CB *p_cb, tSMP_ENC *p) +{ + tSMP_KEY key; + + SMP_TRACE_DEBUG ("smp_process_compare \n"); +#if (SMP_DEBUG == TRUE) + SMP_TRACE_DEBUG("Compare Generated\n"); + smp_debug_print_nbyte_little_endian (p->param_buf, (const UINT8 *)"Compare", 16); +#endif + key.key_type = SMP_KEY_TYPE_CMP; + key.p_data = p->param_buf; + //smp_set_state(SMP_STATE_CONFIRM); + smp_sm_event(p_cb, SMP_KEY_READY_EVT, &key); +} + +/******************************************************************************* +** +** Function smp_process_stk +** +** Description This function is called when STK is generated +** proceed to send the encrypt the link using STK. +** +** Returns void +** +*******************************************************************************/ +static void smp_process_stk(tSMP_CB *p_cb, tSMP_ENC *p) +{ + tSMP_KEY key; + + SMP_TRACE_DEBUG ("smp_process_stk "); +#if (SMP_DEBUG == TRUE) + SMP_TRACE_ERROR("STK Generated"); +#endif + smp_mask_enc_key(p_cb->loc_enc_size, p->param_buf); + + key.key_type = SMP_KEY_TYPE_STK; + key.p_data = p->param_buf; + + smp_sm_event(p_cb, SMP_KEY_READY_EVT, &key); +} + +/******************************************************************************* +** +** Function smp_generate_ltk_cont +** +** Description This function is to calculate LTK = d1(ER, DIV, 0)= e(ER, DIV) +** +** Returns void +** +*******************************************************************************/ +static void smp_generate_ltk_cont(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + UNUSED(p_data); + + BT_OCTET16 er; + tSMP_ENC output; + tSMP_STATUS status = SMP_PAIR_FAIL_UNKNOWN; + + SMP_TRACE_DEBUG ("%s\n", __func__); + BTM_GetDeviceEncRoot(er); + + /* LTK = d1(ER, DIV, 0)= e(ER, DIV)*/ + if (!SMP_Encrypt(er, BT_OCTET16_LEN, (UINT8 *)&p_cb->div, + sizeof(UINT16), &output)) { + SMP_TRACE_ERROR("%s failed\n", __func__); + smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &status); + } else { + /* mask the LTK */ + smp_mask_enc_key(p_cb->loc_enc_size, output.param_buf); + memcpy((void *)p_cb->ltk, output.param_buf, BT_OCTET16_LEN); + smp_generate_rand_vector(p_cb, NULL); + } +} + +/******************************************************************************* +** +** Function smp_generate_y +** +** Description This function is to proceed generate Y = E(DHK, Rand) +** +** Returns void +** +*******************************************************************************/ +static void smp_generate_y(tSMP_CB *p_cb, tSMP_INT_DATA *p) +{ + UNUSED(p); + + BT_OCTET16 dhk; + tSMP_ENC output; + tSMP_STATUS status = SMP_PAIR_FAIL_UNKNOWN; + + + SMP_TRACE_DEBUG ("smp_generate_y \n"); + BTM_GetDeviceDHK(dhk); + + if (!SMP_Encrypt(dhk, BT_OCTET16_LEN, p_cb->enc_rand, + BT_OCTET8_LEN, &output)) { + SMP_TRACE_ERROR("smp_generate_y failed"); + smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &status); + } else { + smp_process_ediv(p_cb, &output); + } +} + +/******************************************************************************* +** +** Function smp_generate_rand_vector +** +** Description This function is called when LTK is generated, send state machine +** event to SMP. +** +** Returns void +** +*******************************************************************************/ +static void smp_generate_rand_vector (tSMP_CB *p_cb, tSMP_INT_DATA *p) +{ + UNUSED(p); + + /* generate EDIV and rand now */ + /* generate random vector */ + SMP_TRACE_DEBUG ("smp_generate_rand_vector\n"); + p_cb->rand_enc_proc_state = SMP_GEN_RAND_V; + if (!btsnd_hcic_ble_rand((void *)smp_rand_back)) { + smp_rand_back(NULL); + } +} + +/******************************************************************************* +** +** Function smp_process_ediv +** +** Description This function is to calculate EDIV = Y xor DIV +** +** Returns void +** +*******************************************************************************/ +static void smp_process_ediv(tSMP_CB *p_cb, tSMP_ENC *p) +{ + tSMP_KEY key; + UINT8 *pp = p->param_buf; + UINT16 y; + + SMP_TRACE_DEBUG ("smp_process_ediv "); + STREAM_TO_UINT16(y, pp); + + /* EDIV = Y xor DIV */ + p_cb->ediv = p_cb->div ^ y; + /* send LTK ready */ + SMP_TRACE_DEBUG("LTK ready"); + key.key_type = SMP_KEY_TYPE_LTK; + key.p_data = p->param_buf; + + smp_sm_event(p_cb, SMP_KEY_READY_EVT, &key); +} + +/******************************************************************************* +** +** Function smp_calculate_legacy_short_term_key +** +** Description The function calculates legacy STK. +** +** Returns FALSE if out of resources, TRUE in other cases. +** +*******************************************************************************/ +BOOLEAN smp_calculate_legacy_short_term_key(tSMP_CB *p_cb, tSMP_ENC *output) +{ + BT_OCTET16 ptext; + UINT8 *p = ptext; + + SMP_TRACE_DEBUG ("%s\n", __func__); + memset(p, 0, BT_OCTET16_LEN); + if (p_cb->role == HCI_ROLE_MASTER) { + memcpy(p, p_cb->rand, BT_OCTET8_LEN); + memcpy(&p[BT_OCTET8_LEN], p_cb->rrand, BT_OCTET8_LEN); + } else { + memcpy(p, p_cb->rrand, BT_OCTET8_LEN); + memcpy(&p[BT_OCTET8_LEN], p_cb->rand, BT_OCTET8_LEN); + } + + BOOLEAN encrypted; + /* generate STK = Etk(rand|rrand)*/ + encrypted = SMP_Encrypt( p_cb->tk, BT_OCTET16_LEN, ptext, BT_OCTET16_LEN, output); + if (!encrypted) { + SMP_TRACE_ERROR("%s failed\n", __func__); + } + return encrypted; +} + +/******************************************************************************* +** +** Function smp_create_private_key +** +** Description This function is called to create private key used to +** calculate public key and DHKey. +** The function starts private key creation requesting controller +** to generate [0-7] octets of private key. +** +** Returns void +** +*******************************************************************************/ +void smp_create_private_key(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + SMP_TRACE_DEBUG("%s", __func__); + + if (p_cb->selected_association_model == SMP_MODEL_SEC_CONN_OOB) { + SMP_TRACE_EVENT("OOB Association Model"); + if (!oob_data_is_empty(&saved_local_oob_data)) { + SMP_TRACE_EVENT("Found OOB data, loading keys"); + memcpy(&p_cb->sc_oob_data.loc_oob_data, &saved_local_oob_data, sizeof(tSMP_LOC_OOB_DATA)); + smp_process_private_key(p_cb); + return; + } + SMP_TRACE_EVENT("OOB Association Model with no saved data"); + } + + p_cb->rand_enc_proc_state = SMP_GENERATE_PRIVATE_KEY_0_7; + if (!btsnd_hcic_ble_rand((void *)smp_rand_back)) { + smp_rand_back(NULL); + } +} + +/******************************************************************************* +** +** Function smp_use_oob_private_key +** +** Description This function is called +** - to save the secret key used to calculate the public key used +** in calculations of commitment sent OOB to a peer +** - to use this secret key to recalculate the public key and +** start the process of sending this public key to the peer +** if secret/public keys have to be reused. +** If the keys aren't supposed to be reused, continue from the +** point from which request for OOB data was issued. +** +** Returns void +** +*******************************************************************************/ +void smp_use_oob_private_key(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + SMP_TRACE_DEBUG ("%s req_oob_type: %d, role: %d\n", + __func__, p_cb->req_oob_type, p_cb->role); + + switch (p_cb->req_oob_type) { + case SMP_OOB_BOTH: + case SMP_OOB_LOCAL: + SMP_TRACE_DEBUG("%s restore secret key\n", __func__); + // copy private key in smp_process_private_key + smp_process_private_key(p_cb); + break; + default: + SMP_TRACE_DEBUG("%s create secret key anew\n", __func__); + smp_set_state(SMP_STATE_PAIR_REQ_RSP); + smp_decide_association_model(p_cb, NULL); + break; + } +} + +/******************************************************************************* +** +** Function smp_continue_private_key_creation +** +** Description This function is used to continue private key creation. +** +** Returns void +** +*******************************************************************************/ +void smp_continue_private_key_creation (tSMP_CB *p_cb, tBTM_RAND_ENC *p) +{ + UINT8 state = p_cb->rand_enc_proc_state & ~0x80; + SMP_TRACE_DEBUG ("%s state=0x%x\n", __func__, state); + + switch (state) { + case SMP_GENERATE_PRIVATE_KEY_0_7: + memcpy((void *)p_cb->private_key, p->param_buf, p->param_len); + p_cb->rand_enc_proc_state = SMP_GENERATE_PRIVATE_KEY_8_15; + if (!btsnd_hcic_ble_rand((void *)smp_rand_back)) { + smp_rand_back(NULL); + } + break; + + case SMP_GENERATE_PRIVATE_KEY_8_15: + memcpy((void *)&p_cb->private_key[8], p->param_buf, p->param_len); + p_cb->rand_enc_proc_state = SMP_GENERATE_PRIVATE_KEY_16_23; + if (!btsnd_hcic_ble_rand((void *)smp_rand_back)) { + smp_rand_back(NULL); + } + break; + + case SMP_GENERATE_PRIVATE_KEY_16_23: + memcpy((void *)&p_cb->private_key[16], p->param_buf, p->param_len); + p_cb->rand_enc_proc_state = SMP_GENERATE_PRIVATE_KEY_24_31; + if (!btsnd_hcic_ble_rand((void *)smp_rand_back)) { + smp_rand_back(NULL); + } + break; + + case SMP_GENERATE_PRIVATE_KEY_24_31: + memcpy((void *)&p_cb->private_key[24], p->param_buf, p->param_len); + smp_process_private_key (p_cb); + break; + + default: + break; + } + + return; +} + +/******************************************************************************* +** +** Function smp_process_private_key +** +** Description This function processes private key. +** It calculates public key and notifies SM that private key / +** public key pair is created. +** +** Returns void +** +*******************************************************************************/ +void smp_process_private_key(tSMP_CB *p_cb) +{ + Point public_key; + BT_OCTET32 private_key; + tSMP_LOC_OOB_DATA *p_loc_oob = &p_cb->sc_oob_data.loc_oob_data; + + SMP_TRACE_DEBUG ("%s", __FUNCTION__); + + /* if local oob data present, then restore oob private and public key */ + if (p_loc_oob->present) { + memcpy(p_cb->private_key, p_loc_oob->private_key_used, BT_OCTET32_LEN); + memcpy(p_cb->loc_publ_key.x, p_loc_oob->publ_key_used.x, BT_OCTET32_LEN); + memcpy(p_cb->loc_publ_key.y, p_loc_oob->publ_key_used.y, BT_OCTET32_LEN); + memcpy(p_cb->local_random, p_loc_oob->randomizer, BT_OCTET16_LEN); + } else { + memcpy(private_key, p_cb->private_key, BT_OCTET32_LEN); + ECC_PointMult(&public_key, &(curve_p256.G), (DWORD *) private_key, KEY_LENGTH_DWORDS_P256); + memcpy(p_cb->loc_publ_key.x, public_key.x, BT_OCTET32_LEN); + memcpy(p_cb->loc_publ_key.y, public_key.y, BT_OCTET32_LEN); + } + + smp_debug_print_nbyte_little_endian (p_cb->private_key, (const UINT8 *)"private", + BT_OCTET32_LEN); + smp_debug_print_nbyte_little_endian (p_cb->loc_publ_key.x, (const UINT8 *)"local public(x)", + BT_OCTET32_LEN); + smp_debug_print_nbyte_little_endian (p_cb->loc_publ_key.y, (const UINT8 *)"local public(y)", + BT_OCTET32_LEN); + p_cb->flags |= SMP_PAIR_FLAG_HAVE_LOCAL_PUBL_KEY; + smp_sm_event(p_cb, SMP_LOC_PUBL_KEY_CRTD_EVT, NULL); +} + +/******************************************************************************* +** +** Function smp_compute_dhkey +** +** Description The function: +** - calculates a new public key using as input local private +** key and peer public key; +** - saves the new public key x-coordinate as DHKey. +** +** Returns void +** +*******************************************************************************/ +void smp_compute_dhkey (tSMP_CB *p_cb) +{ + Point peer_publ_key, new_publ_key; + BT_OCTET32 private_key; + + SMP_TRACE_DEBUG ("%s\n", __FUNCTION__); + + memcpy(private_key, p_cb->private_key, BT_OCTET32_LEN); + memcpy(peer_publ_key.x, p_cb->peer_publ_key.x, BT_OCTET32_LEN); + memcpy(peer_publ_key.y, p_cb->peer_publ_key.y, BT_OCTET32_LEN); + + ECC_PointMult(&new_publ_key, &peer_publ_key, (DWORD *) private_key, KEY_LENGTH_DWORDS_P256); + + memcpy(p_cb->dhkey, new_publ_key.x, BT_OCTET32_LEN); + + smp_debug_print_nbyte_little_endian (p_cb->dhkey, (const UINT8 *)"Old DHKey", + BT_OCTET32_LEN); + + smp_debug_print_nbyte_little_endian (p_cb->private_key, (const UINT8 *)"private", + BT_OCTET32_LEN); + smp_debug_print_nbyte_little_endian (p_cb->peer_publ_key.x, (const UINT8 *)"rem public(x)", + BT_OCTET32_LEN); + smp_debug_print_nbyte_little_endian (p_cb->peer_publ_key.y, (const UINT8 *)"rem public(y)", + BT_OCTET32_LEN); + smp_debug_print_nbyte_little_endian (p_cb->dhkey, (const UINT8 *)"Reverted DHKey", + BT_OCTET32_LEN); +} + +/******************************************************************************* +** +** Function smp_calculate_local_commitment +** +** Description The function calculates and saves local commmitment in CB. +** +** Returns void +** +*******************************************************************************/ +void smp_calculate_local_commitment(tSMP_CB *p_cb) +{ + UINT8 random_input; + + SMP_TRACE_DEBUG("%s\n", __FUNCTION__); + + switch (p_cb->selected_association_model) { + case SMP_MODEL_SEC_CONN_JUSTWORKS: + case SMP_MODEL_SEC_CONN_NUM_COMP: + if (p_cb->role == HCI_ROLE_MASTER) { + SMP_TRACE_WARNING ("local commitment calc on master is not expected \ + for Just Works/Numeric Comparison models\n"); + } + smp_calculate_f4(p_cb->loc_publ_key.x, p_cb->peer_publ_key.x, p_cb->rand, 0, + p_cb->commitment); + break; + case SMP_MODEL_SEC_CONN_PASSKEY_ENT: + case SMP_MODEL_SEC_CONN_PASSKEY_DISP: + random_input = smp_calculate_random_input(p_cb->local_random, p_cb->round); + smp_calculate_f4(p_cb->loc_publ_key.x, p_cb->peer_publ_key.x, p_cb->rand, + random_input, p_cb->commitment); + break; + case SMP_MODEL_SEC_CONN_OOB: + SMP_TRACE_WARNING ("local commitment calc is expected for OOB model BEFORE pairing\n"); + smp_calculate_f4(p_cb->loc_publ_key.x, p_cb->loc_publ_key.x, p_cb->local_random, 0, + p_cb->commitment); + break; + default: + SMP_TRACE_ERROR("Association Model = %d is not used in LE SC\n", + p_cb->selected_association_model); + return; + } + + SMP_TRACE_EVENT ("local commitment calculation is completed"); +} + +/******************************************************************************* +** +** Function smp_calculate_peer_commitment +** +** Description The function calculates and saves peer commmitment at the +** provided output buffer. +** +** Returns void +** +*******************************************************************************/ +void smp_calculate_peer_commitment(tSMP_CB *p_cb, BT_OCTET16 output_buf) +{ + UINT8 ri; + + SMP_TRACE_DEBUG ("%s", __FUNCTION__); + + switch (p_cb->selected_association_model) { + case SMP_MODEL_SEC_CONN_JUSTWORKS: + case SMP_MODEL_SEC_CONN_NUM_COMP: + if (p_cb->role == HCI_ROLE_SLAVE) { + SMP_TRACE_WARNING ("peer commitment calc on slave is not expected \ + for Just Works/Numeric Comparison models\n"); + } + smp_calculate_f4(p_cb->peer_publ_key.x, p_cb->loc_publ_key.x, p_cb->rrand, 0, + output_buf); + break; + case SMP_MODEL_SEC_CONN_PASSKEY_ENT: + case SMP_MODEL_SEC_CONN_PASSKEY_DISP: + ri = smp_calculate_random_input(p_cb->peer_random, p_cb->round); + smp_calculate_f4(p_cb->peer_publ_key.x, p_cb->loc_publ_key.x, p_cb->rrand, ri, + output_buf); + break; + case SMP_MODEL_SEC_CONN_OOB: + smp_calculate_f4(p_cb->peer_publ_key.x, p_cb->peer_publ_key.x, p_cb->peer_random, 0, + output_buf); + break; + default: + SMP_TRACE_ERROR("Association Model = %d is not used in LE SC\n", + p_cb->selected_association_model); + return; + } + + SMP_TRACE_EVENT ("peer commitment calculation is completed\n"); +} + +/******************************************************************************* +** +** Function smp_calculate_f4 +** +** Description The function calculates +** C = f4(U, V, X, Z) = AES-CMAC (U||V||Z) +** X +** where +** input: U is 256 bit, +** V is 256 bit, +** X is 128 bit, +** Z is 8 bit, +** output: C is 128 bit. +** +** Returns void +** +** Note The LSB is the first octet, the MSB is the last octet of +** the AES-CMAC input/output stream. +** +*******************************************************************************/ +void smp_calculate_f4(UINT8 *u, UINT8 *v, UINT8 *x, UINT8 z, UINT8 *c) +{ + UINT8 msg_len = BT_OCTET32_LEN /* U size */ + BT_OCTET32_LEN /* V size */ + 1 /* Z size */; + UINT8 msg[BT_OCTET32_LEN + BT_OCTET32_LEN + 1]; + UINT8 key[BT_OCTET16_LEN]; + UINT8 cmac[BT_OCTET16_LEN]; + UINT8 *p = NULL; +#if SMP_DEBUG == TRUE + UINT8 *p_prnt = NULL; +#endif + + SMP_TRACE_DEBUG ("%s", __FUNCTION__); + +#if SMP_DEBUG == TRUE + p_prnt = u; + smp_debug_print_nbyte_little_endian (p_prnt, (const UINT8 *)"U", BT_OCTET32_LEN); + p_prnt = v; + smp_debug_print_nbyte_little_endian (p_prnt, (const UINT8 *)"V", BT_OCTET32_LEN); + p_prnt = x; + smp_debug_print_nbyte_little_endian (p_prnt, (const UINT8 *)"X", BT_OCTET16_LEN); + p_prnt = &z; + smp_debug_print_nbyte_little_endian (p_prnt, (const UINT8 *)"Z", 1); +#endif + + p = msg; + UINT8_TO_STREAM(p, z); + ARRAY_TO_STREAM(p, v, BT_OCTET32_LEN); + ARRAY_TO_STREAM(p, u, BT_OCTET32_LEN); +#if SMP_DEBUG == TRUE + p_prnt = msg; + smp_debug_print_nbyte_little_endian (p_prnt, (const UINT8 *)"M", msg_len); +#endif + + p = key; + ARRAY_TO_STREAM(p, x, BT_OCTET16_LEN); +#if SMP_DEBUG == TRUE + p_prnt = key; + smp_debug_print_nbyte_little_endian (p_prnt, (const UINT8 *)"K", BT_OCTET16_LEN); +#endif + + aes_cipher_msg_auth_code(key, msg, msg_len, BT_OCTET16_LEN, cmac); +#if SMP_DEBUG == TRUE + p_prnt = cmac; + smp_debug_print_nbyte_little_endian (p_prnt, (const UINT8 *)"AES_CMAC", BT_OCTET16_LEN); +#endif + + p = c; + ARRAY_TO_STREAM(p, cmac, BT_OCTET16_LEN); +} + +/******************************************************************************* +** +** Function smp_calculate_numeric_comparison_display_number +** +** Description The function calculates and saves number to display in numeric +** comparison association mode. +** +** Returns void +** +*******************************************************************************/ +void smp_calculate_numeric_comparison_display_number(tSMP_CB *p_cb, + tSMP_INT_DATA *p_data) +{ + SMP_TRACE_DEBUG ("%s", __func__); + + if (p_cb->role == HCI_ROLE_MASTER) { + p_cb->number_to_display = + smp_calculate_g2(p_cb->loc_publ_key.x, p_cb->peer_publ_key.x, p_cb->rand, + p_cb->rrand); + } else { + p_cb->number_to_display = + smp_calculate_g2(p_cb->peer_publ_key.x, p_cb->loc_publ_key.x, p_cb->rrand, + p_cb->rand); + } + + if (p_cb->number_to_display >= (BTM_MAX_PASSKEY_VAL + 1)) { + UINT8 reason; + reason = p_cb->failure = SMP_PAIR_FAIL_UNKNOWN; + smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &reason); + return; + } + + SMP_TRACE_EVENT("Number to display in numeric comparison = %d", p_cb->number_to_display); + p_cb->cb_evt = SMP_NC_REQ_EVT; + smp_sm_event(p_cb, SMP_SC_DSPL_NC_EVT, &p_cb->number_to_display); + return; +} + +/******************************************************************************* +** +** Function smp_calculate_g2 +** +** Description The function calculates +** g2(U, V, X, Y) = AES-CMAC (U||V||Y) mod 2**32 mod 10**6 +** X +** and +** Vres = g2(U, V, X, Y) mod 10**6 +** where +** input: U is 256 bit, +** V is 256 bit, +** X is 128 bit, +** Y is 128 bit, +** +** Returns Vres. +** Expected value has to be in the range [0 - 999999] i.e. [0 - 0xF423F]. +** Vres = 1000000 means that the calculation fails. +** +** Note The LSB is the first octet, the MSB is the last octet of +** the AES-CMAC input/output stream. +** +*******************************************************************************/ +UINT32 smp_calculate_g2(UINT8 *u, UINT8 *v, UINT8 *x, UINT8 *y) +{ + UINT8 msg_len = BT_OCTET32_LEN /* U size */ + BT_OCTET32_LEN /* V size */ + + BT_OCTET16_LEN /* Y size */; + UINT8 msg[BT_OCTET32_LEN + BT_OCTET32_LEN + BT_OCTET16_LEN]; + UINT8 key[BT_OCTET16_LEN]; + UINT8 cmac[BT_OCTET16_LEN]; + UINT8 *p = NULL; + UINT32 vres; +#if SMP_DEBUG == TRUE + UINT8 *p_prnt = NULL; +#endif + + SMP_TRACE_DEBUG ("%s\n", __FUNCTION__); + + p = msg; + ARRAY_TO_STREAM(p, y, BT_OCTET16_LEN); + ARRAY_TO_STREAM(p, v, BT_OCTET32_LEN); + ARRAY_TO_STREAM(p, u, BT_OCTET32_LEN); +#if SMP_DEBUG == TRUE + p_prnt = u; + smp_debug_print_nbyte_little_endian (p_prnt, (const UINT8 *)"U", BT_OCTET32_LEN); + p_prnt = v; + smp_debug_print_nbyte_little_endian (p_prnt, (const UINT8 *)"V", BT_OCTET32_LEN); + p_prnt = x; + smp_debug_print_nbyte_little_endian (p_prnt, (const UINT8 *)"X", BT_OCTET16_LEN); + p_prnt = y; + smp_debug_print_nbyte_little_endian (p_prnt, (const UINT8 *)"Y", BT_OCTET16_LEN); +#endif + + p = key; + ARRAY_TO_STREAM(p, x, BT_OCTET16_LEN); +#if SMP_DEBUG == TRUE + p_prnt = key; + smp_debug_print_nbyte_little_endian (p_prnt, (const UINT8 *)"K", BT_OCTET16_LEN); +#endif + + if (!aes_cipher_msg_auth_code(key, msg, msg_len, BT_OCTET16_LEN, cmac)) { + SMP_TRACE_ERROR("%s failed", __FUNCTION__); + return (BTM_MAX_PASSKEY_VAL + 1); + } + +#if SMP_DEBUG == TRUE + p_prnt = cmac; + smp_debug_print_nbyte_little_endian (p_prnt, (const UINT8 *)"AES-CMAC", BT_OCTET16_LEN); +#endif + + /* vres = cmac mod 2**32 mod 10**6 */ + p = &cmac[0]; + STREAM_TO_UINT32(vres, p); +#if SMP_DEBUG == TRUE + p_prnt = (UINT8 *) &vres; + smp_debug_print_nbyte_little_endian (p_prnt, (const UINT8 *)"cmac mod 2**32", 4); +#endif + + while (vres > BTM_MAX_PASSKEY_VAL) { + vres -= (BTM_MAX_PASSKEY_VAL + 1); + } +#if SMP_DEBUG == TRUE + p_prnt = (UINT8 *) &vres; + smp_debug_print_nbyte_little_endian (p_prnt, (const UINT8 *)"cmac mod 2**32 mod 10**6", 4); +#endif + + SMP_TRACE_ERROR("Value for numeric comparison = %d", vres); + return vres; +} + +/******************************************************************************* +** +** Function smp_calculate_f5 +** +** Description The function provides two AES-CMAC that are supposed to be used as +** - MacKey (MacKey is used in pairing DHKey check calculation); +** - LTK (LTK is used to ecrypt the link after completion of Phase 2 +** and on reconnection, to derive BR/EDR LK). +** The function inputs are W, N1, N2, A1, A2. +** F5 rules: +** - the value used as key in MacKey/LTK (T) is calculated +** (function smp_calculate_f5_key(...)); +** The formula is: +** T = AES-CMAC (W) +** salt +** where salt is internal parameter of smp_calculate_f5_key(...). +** - MacKey and LTK are calculated as AES-MAC values received with the +** key T calculated in the previous step and the plaintext message +** built from the external parameters N1, N2, A1, A2 and the internal +** parameters counter, keyID, length. +** The function smp_calculate_f5_mackey_or_long_term_key(...) is used in the +** calculations. +** The same formula is used in calculation of MacKey and LTK and the +** same parameter values except the value of the internal parameter +** counter: +** - in MacKey calculations the value is 0; +** - in LTK calculations the value is 1. +** MacKey = AES-CMAC (Counter=0||keyID||N1||N2||A1||A2||Length=256) +** T +** LTK = AES-CMAC (Counter=1||keyID||N1||N2||A1||A2||Length=256) +** T +** The parameters are +** input: +** W is 256 bits, +** N1 is 128 bits, +** N2 is 128 bits, +** A1 is 56 bit, +** A2 is 56 bit. +** internal: +** Counter is 8 bits, its value is 0 for MacKey, +** 1 for LTK; +** KeyId is 32 bits, its value is +** 0x62746c65 (MSB~LSB); +** Length is 16 bits, its value is 0x0100 +** (MSB~LSB). +** output: +** MacKey is 128 bits; +** LTK is 128 bits +** +** Returns FALSE if out of resources, TRUE in other cases. +** +** Note The LSB is the first octet, the MSB is the last octet of +** the AES-CMAC input/output stream. +** +*******************************************************************************/ +BOOLEAN smp_calculate_f5(UINT8 *w, UINT8 *n1, UINT8 *n2, UINT8 *a1, UINT8 *a2, + UINT8 *mac_key, UINT8 *ltk) +{ + BT_OCTET16 t; /* AES-CMAC output in smp_calculate_f5_key(...), key in */ + /* smp_calculate_f5_mackey_or_long_term_key(...) */ +#if SMP_DEBUG == TRUE + UINT8 *p_prnt = NULL; +#endif + /* internal parameters: */ + + /* + counter is 0 for MacKey, + is 1 for LTK + */ + UINT8 counter_mac_key[1] = {0}; + UINT8 counter_ltk[1] = {1}; + /* + keyID 62746c65 + */ + UINT8 key_id[4] = {0x65, 0x6c, 0x74, 0x62}; + /* + length 0100 + */ + UINT8 length[2] = {0x00, 0x01}; + + SMP_TRACE_DEBUG ("%s", __FUNCTION__); +#if SMP_DEBUG == TRUE + p_prnt = w; + smp_debug_print_nbyte_little_endian (p_prnt, (const UINT8 *)"W", BT_OCTET32_LEN); + p_prnt = n1; + smp_debug_print_nbyte_little_endian (p_prnt, (const UINT8 *)"N1", BT_OCTET16_LEN); + p_prnt = n2; + smp_debug_print_nbyte_little_endian (p_prnt, (const UINT8 *)"N2", BT_OCTET16_LEN); + p_prnt = a1; + smp_debug_print_nbyte_little_endian (p_prnt, (const UINT8 *)"A1", 7); + p_prnt = a2; + smp_debug_print_nbyte_little_endian (p_prnt, (const UINT8 *) "A2", 7); +#endif + + if (!smp_calculate_f5_key(w, t)) { + SMP_TRACE_ERROR("%s failed to calc T", __FUNCTION__); + return FALSE; + } +#if SMP_DEBUG == TRUE + p_prnt = t; + smp_debug_print_nbyte_little_endian (p_prnt, (const UINT8 *)"T", BT_OCTET16_LEN); +#endif + + if (!smp_calculate_f5_mackey_or_long_term_key(t, counter_mac_key, key_id, n1, n2, a1, a2, + length, mac_key)) { + SMP_TRACE_ERROR("%s failed to calc MacKey", __FUNCTION__); + return FALSE; + } +#if SMP_DEBUG == TRUE + p_prnt = mac_key; + smp_debug_print_nbyte_little_endian (p_prnt, (const UINT8 *)"MacKey", BT_OCTET16_LEN); +#endif + + if (!smp_calculate_f5_mackey_or_long_term_key(t, counter_ltk, key_id, n1, n2, a1, a2, + length, ltk)) { + SMP_TRACE_ERROR("%s failed to calc LTK", __FUNCTION__); + return FALSE; + } +#if SMP_DEBUG == TRUE + p_prnt = ltk; + smp_debug_print_nbyte_little_endian (p_prnt, (const UINT8 *)"LTK", BT_OCTET16_LEN); +#endif + + return TRUE; +} + +/******************************************************************************* +** +** Function smp_calculate_f5_mackey_or_long_term_key +** +** Description The function calculates the value of MacKey or LTK by the rules +** defined for f5 function. +** At the moment exactly the same formula is used to calculate +** LTK and MacKey. +** The difference is the value of input parameter Counter: +** - in MacKey calculations the value is 0; +** - in LTK calculations the value is 1. +** The formula: +** mac = AES-CMAC (Counter||keyID||N1||N2||A1||A2||Length) +** T +** where +** input: T is 256 bits; +** Counter is 8 bits, its value is 0 for MacKey, +** 1 for LTK; +** keyID is 32 bits, its value is 0x62746c65; +** N1 is 128 bits; +** N2 is 128 bits; +** A1 is 56 bits; +** A2 is 56 bits; +** Length is 16 bits, its value is 0x0100 +** output: LTK is 128 bit. +** +** Returns FALSE if out of resources, TRUE in other cases. +** +** Note The LSB is the first octet, the MSB is the last octet of +** the AES-CMAC input/output stream. +** +*******************************************************************************/ +BOOLEAN smp_calculate_f5_mackey_or_long_term_key(UINT8 *t, UINT8 *counter, + UINT8 *key_id, UINT8 *n1, UINT8 *n2, UINT8 *a1, UINT8 *a2, + UINT8 *length, UINT8 *mac) +{ + UINT8 *p = NULL; + UINT8 cmac[BT_OCTET16_LEN]; + UINT8 key[BT_OCTET16_LEN]; + UINT8 msg_len = 1 /* Counter size */ + 4 /* keyID size */ + + BT_OCTET16_LEN /* N1 size */ + BT_OCTET16_LEN /* N2 size */ + + 7 /* A1 size*/ + 7 /* A2 size*/ + 2 /* Length size */; + UINT8 msg[1 + 4 + BT_OCTET16_LEN + BT_OCTET16_LEN + 7 + 7 + 2]; + BOOLEAN ret = TRUE; +#if SMP_DEBUG == TRUE + UINT8 *p_prnt = NULL; +#endif + + SMP_TRACE_DEBUG ("%s", __FUNCTION__); +#if SMP_DEBUG == TRUE + p_prnt = t; + smp_debug_print_nbyte_little_endian (p_prnt, (const UINT8 *)"T", BT_OCTET16_LEN); + p_prnt = counter; + smp_debug_print_nbyte_little_endian (p_prnt, (const UINT8 *)"Counter", 1); + p_prnt = key_id; + smp_debug_print_nbyte_little_endian (p_prnt, (const UINT8 *)"KeyID", 4); + p_prnt = n1; + smp_debug_print_nbyte_little_endian (p_prnt, (const UINT8 *)"N1", BT_OCTET16_LEN); + p_prnt = n2; + smp_debug_print_nbyte_little_endian (p_prnt, (const UINT8 *)"N2", BT_OCTET16_LEN); + p_prnt = a1; + smp_debug_print_nbyte_little_endian (p_prnt, (const UINT8 *)"A1", 7); + p_prnt = a2; + smp_debug_print_nbyte_little_endian (p_prnt, (const UINT8 *)"A2", 7); + p_prnt = length; + smp_debug_print_nbyte_little_endian (p_prnt, (const UINT8 *)"Length", 2); +#endif + + p = key; + ARRAY_TO_STREAM(p, t, BT_OCTET16_LEN); +#if SMP_DEBUG == TRUE + p_prnt = key; + smp_debug_print_nbyte_little_endian (p_prnt, (const UINT8 *)"K", BT_OCTET16_LEN); +#endif + p = msg; + ARRAY_TO_STREAM(p, length, 2); + ARRAY_TO_STREAM(p, a2, 7); + ARRAY_TO_STREAM(p, a1, 7); + ARRAY_TO_STREAM(p, n2, BT_OCTET16_LEN); + ARRAY_TO_STREAM(p, n1, BT_OCTET16_LEN); + ARRAY_TO_STREAM(p, key_id, 4); + ARRAY_TO_STREAM(p, counter, 1); +#if SMP_DEBUG == TRUE + p_prnt = msg; + smp_debug_print_nbyte_little_endian (p_prnt, (const UINT8 *)"M", msg_len); +#endif + + if (!aes_cipher_msg_auth_code(key, msg, msg_len, BT_OCTET16_LEN, cmac)) { + SMP_TRACE_ERROR("%s failed", __FUNCTION__); + ret = FALSE; + } + +#if SMP_DEBUG == TRUE + p_prnt = cmac; + smp_debug_print_nbyte_little_endian (p_prnt, (const UINT8 *)"AES-CMAC", BT_OCTET16_LEN); +#endif + + p = mac; + ARRAY_TO_STREAM(p, cmac, BT_OCTET16_LEN); + return ret; +} + +/******************************************************************************* +** +** Function smp_calculate_f5_key +** +** Description The function calculates key T used in calculation of +** MacKey and LTK (f5 output is defined as MacKey || LTK). +** T = AES-CMAC (W) +** salt +** where +** Internal: salt is 128 bit. +** input: W is 256 bit. +** Output: T is 128 bit. +** +** Returns FALSE if out of resources, TRUE in other cases. +** +** Note The LSB is the first octet, the MSB is the last octet of +** the AES-CMAC input/output stream. +** +*******************************************************************************/ +BOOLEAN smp_calculate_f5_key(UINT8 *w, UINT8 *t) +{ + UINT8 *p = NULL; + /* Please see 2.2.7 LE Secure Connections Key Generation Function f5 */ + /* + salt: 6C88 8391 AAF5 A538 6037 0BDB 5A60 83BE + */ + BT_OCTET16 salt = { + 0xBE, 0x83, 0x60, 0x5A, 0xDB, 0x0B, 0x37, 0x60, + 0x38, 0xA5, 0xF5, 0xAA, 0x91, 0x83, 0x88, 0x6C + }; +#if SMP_DEBUG == TRUE + UINT8 *p_prnt = NULL; +#endif + + SMP_TRACE_DEBUG ("%s", __FUNCTION__); +#if SMP_DEBUG == TRUE + p_prnt = salt; + smp_debug_print_nbyte_little_endian (p_prnt, (const UINT8 *)"salt", BT_OCTET16_LEN); + p_prnt = w; + smp_debug_print_nbyte_little_endian (p_prnt, (const UINT8 *)"W", BT_OCTET32_LEN); +#endif + + BT_OCTET16 key; + BT_OCTET32 msg; + + p = key; + ARRAY_TO_STREAM(p, salt, BT_OCTET16_LEN); + p = msg; + ARRAY_TO_STREAM(p, w, BT_OCTET32_LEN); +#if SMP_DEBUG == TRUE + p_prnt = key; + smp_debug_print_nbyte_little_endian (p_prnt, (const UINT8 *)"K", BT_OCTET16_LEN); + p_prnt = msg; + smp_debug_print_nbyte_little_endian (p_prnt, (const UINT8 *)"M", BT_OCTET32_LEN); +#endif + + BT_OCTET16 cmac; + BOOLEAN ret = TRUE; + if (!aes_cipher_msg_auth_code(key, msg, BT_OCTET32_LEN, BT_OCTET16_LEN, cmac)) { + SMP_TRACE_ERROR("%s failed", __FUNCTION__); + ret = FALSE; + } + +#if SMP_DEBUG == TRUE + p_prnt = cmac; + smp_debug_print_nbyte_little_endian (p_prnt, (const UINT8 *)"AES-CMAC", BT_OCTET16_LEN); +#endif + + p = t; + ARRAY_TO_STREAM(p, cmac, BT_OCTET16_LEN); + return ret; +} + +/******************************************************************************* +** +** Function smp_calculate_local_dhkey_check +** +** Description The function calculates and saves local device DHKey check +** value in CB. +** Before doing this it calls smp_calculate_f5_mackey_and_long_term_key(...). +** to calculate MacKey and LTK. +** MacKey is used in dhkey calculation. +** +** Returns void +** +*******************************************************************************/ +void smp_calculate_local_dhkey_check(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + UINT8 iocap[3], a[7], b[7]; + + SMP_TRACE_DEBUG ("%s", __FUNCTION__); + + smp_calculate_f5_mackey_and_long_term_key(p_cb); + + smp_collect_local_io_capabilities(iocap, p_cb); + + smp_collect_local_ble_address(a, p_cb); + smp_collect_peer_ble_address(b, p_cb); + smp_calculate_f6(p_cb->mac_key, p_cb->rand, p_cb->rrand, p_cb->peer_random, iocap, a, b, + p_cb->dhkey_check); + + SMP_TRACE_EVENT ("local DHKey check calculation is completed"); +} + +/******************************************************************************* +** +** Function smp_calculate_peer_dhkey_check +** +** Description The function calculates peer device DHKey check value. +** +** Returns void +** +*******************************************************************************/ +void smp_calculate_peer_dhkey_check(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + UINT8 iocap[3], a[7], b[7]; + BT_OCTET16 param_buf; + BOOLEAN ret; + tSMP_KEY key; + tSMP_STATUS status = SMP_PAIR_FAIL_UNKNOWN; + + SMP_TRACE_DEBUG ("%s", __FUNCTION__); + + smp_collect_peer_io_capabilities(iocap, p_cb); + + smp_collect_local_ble_address(a, p_cb); + smp_collect_peer_ble_address(b, p_cb); + ret = smp_calculate_f6(p_cb->mac_key, p_cb->rrand, p_cb->rand, p_cb->local_random, iocap, + b, a, param_buf); + + if (ret) { + SMP_TRACE_EVENT ("peer DHKey check calculation is completed"); +#if (SMP_DEBUG == TRUE) + smp_debug_print_nbyte_little_endian (param_buf, (const UINT8 *)"peer DHKey check", + BT_OCTET16_LEN); +#endif + key.key_type = SMP_KEY_TYPE_PEER_DHK_CHCK; + key.p_data = param_buf; + smp_sm_event(p_cb, SMP_SC_KEY_READY_EVT, &key); + } else { + SMP_TRACE_EVENT ("peer DHKey check calculation failed"); + smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &status); + } +} + +/******************************************************************************* +** +** Function smp_calculate_f6 +** +** Description The function calculates +** C = f6(W, N1, N2, R, IOcap, A1, A2) = AES-CMAC (N1||N2||R||IOcap||A1||A2) +** W +** where +** input: W is 128 bit, +** N1 is 128 bit, +** N2 is 128 bit, +** R is 128 bit, +** IOcap is 24 bit, +** A1 is 56 bit, +** A2 is 56 bit, +** output: C is 128 bit. +** +** Returns FALSE if out of resources, TRUE in other cases. +** +** Note The LSB is the first octet, the MSB is the last octet of +** the AES-CMAC input/output stream. +** +*******************************************************************************/ +BOOLEAN smp_calculate_f6(UINT8 *w, UINT8 *n1, UINT8 *n2, UINT8 *r, UINT8 *iocap, UINT8 *a1, + UINT8 *a2, UINT8 *c) +{ + UINT8 *p = NULL; + UINT8 msg_len = BT_OCTET16_LEN /* N1 size */ + BT_OCTET16_LEN /* N2 size */ + + BT_OCTET16_LEN /* R size */ + 3 /* IOcap size */ + 7 /* A1 size*/ + + 7 /* A2 size*/; + UINT8 msg[BT_OCTET16_LEN + BT_OCTET16_LEN + BT_OCTET16_LEN + 3 + 7 + 7]; +#if SMP_DEBUG == TRUE + UINT8 *p_print = NULL; +#endif + + SMP_TRACE_DEBUG ("%s", __FUNCTION__); +#if SMP_DEBUG == TRUE + p_print = w; + smp_debug_print_nbyte_little_endian (p_print, (const UINT8 *)"W", BT_OCTET16_LEN); + p_print = n1; + smp_debug_print_nbyte_little_endian (p_print, (const UINT8 *)"N1", BT_OCTET16_LEN); + p_print = n2; + smp_debug_print_nbyte_little_endian (p_print, (const UINT8 *)"N2", BT_OCTET16_LEN); + p_print = r; + smp_debug_print_nbyte_little_endian (p_print, (const UINT8 *)"R", BT_OCTET16_LEN); + p_print = iocap; + smp_debug_print_nbyte_little_endian (p_print, (const UINT8 *)"IOcap", 3); + p_print = a1; + smp_debug_print_nbyte_little_endian (p_print, (const UINT8 *)"A1", 7); + p_print = a2; + smp_debug_print_nbyte_little_endian (p_print, (const UINT8 *)"A2", 7); +#endif + + UINT8 cmac[BT_OCTET16_LEN]; + UINT8 key[BT_OCTET16_LEN]; + + p = key; + ARRAY_TO_STREAM(p, w, BT_OCTET16_LEN); +#if SMP_DEBUG == TRUE + p_print = key; + smp_debug_print_nbyte_little_endian (p_print, (const UINT8 *)"K", BT_OCTET16_LEN); +#endif + + p = msg; + ARRAY_TO_STREAM(p, a2, 7); + ARRAY_TO_STREAM(p, a1, 7); + ARRAY_TO_STREAM(p, iocap, 3); + ARRAY_TO_STREAM(p, r, BT_OCTET16_LEN); + ARRAY_TO_STREAM(p, n2, BT_OCTET16_LEN); + ARRAY_TO_STREAM(p, n1, BT_OCTET16_LEN); +#if SMP_DEBUG == TRUE + p_print = msg; + smp_debug_print_nbyte_little_endian (p_print, (const UINT8 *)"M", msg_len); +#endif + + BOOLEAN ret = TRUE; + if (!aes_cipher_msg_auth_code(key, msg, msg_len, BT_OCTET16_LEN, cmac)) { + SMP_TRACE_ERROR("%s failed", __FUNCTION__); + ret = FALSE; + } + +#if SMP_DEBUG == TRUE + p_print = cmac; + smp_debug_print_nbyte_little_endian (p_print, (const UINT8 *)"AES-CMAC", BT_OCTET16_LEN); +#endif + + p = c; + ARRAY_TO_STREAM(p, cmac, BT_OCTET16_LEN); + return ret; +} + +/******************************************************************************* +** +** Function smp_calculate_link_key_from_long_term_key +** +** Description The function calculates and saves BR/EDR link key derived from +** LE SC LTK. +** +** Returns FALSE if out of resources, TRUE in other cases. +** +*******************************************************************************/ +BOOLEAN smp_calculate_link_key_from_long_term_key(tSMP_CB *p_cb) +{ + tBTM_SEC_DEV_REC *p_dev_rec; + BD_ADDR bda_for_lk; + tBLE_ADDR_TYPE conn_addr_type; + + SMP_TRACE_DEBUG ("%s", __func__); + + if (p_cb->id_addr_rcvd && p_cb->id_addr_type == BLE_ADDR_PUBLIC) { + SMP_TRACE_DEBUG ("Use rcvd identity address as BD_ADDR of LK rcvd identity address"); + memcpy(bda_for_lk, p_cb->id_addr, BD_ADDR_LEN); + } else if ((BTM_ReadRemoteConnectionAddr(p_cb->pairing_bda, bda_for_lk, &conn_addr_type)) && + conn_addr_type == BLE_ADDR_PUBLIC) { + SMP_TRACE_DEBUG ("Use rcvd connection address as BD_ADDR of LK"); + } else { + SMP_TRACE_WARNING ("Don't have peer public address to associate with LK"); + return FALSE; + } + + if ((p_dev_rec = btm_find_dev (p_cb->pairing_bda)) == NULL) { + SMP_TRACE_ERROR("%s failed to find Security Record", __func__); + return FALSE; + } + + BT_OCTET16 intermediate_link_key; + BOOLEAN ret = TRUE; + + ret = smp_calculate_h6(p_cb->ltk, (UINT8 *)"1pmt" /* reversed "tmp1" */, intermediate_link_key); + if (!ret) { + SMP_TRACE_ERROR("%s failed to derive intermediate_link_key", __func__); + return ret; + } + + BT_OCTET16 link_key; + ret = smp_calculate_h6(intermediate_link_key, (UINT8 *) "rbel" /* reversed "lebr" */, link_key); + if (!ret) { + SMP_TRACE_ERROR("%s failed", __func__); + } else { + UINT8 link_key_type; + if (btm_cb.security_mode == BTM_SEC_MODE_SC) { + /* Secure Connections Only Mode */ + link_key_type = BTM_LKEY_TYPE_AUTH_COMB_P_256; + } else if (controller_get_interface()->supports_secure_connections()) { + /* both transports are SC capable */ + if (p_cb->sec_level == SMP_SEC_AUTHENTICATED) { + link_key_type = BTM_LKEY_TYPE_AUTH_COMB_P_256; + } else { + link_key_type = BTM_LKEY_TYPE_UNAUTH_COMB_P_256; + } + } else if (btm_cb.security_mode == BTM_SEC_MODE_SP) { + /* BR/EDR transport is SSP capable */ + if (p_cb->sec_level == SMP_SEC_AUTHENTICATED) { + link_key_type = BTM_LKEY_TYPE_AUTH_COMB; + } else { + link_key_type = BTM_LKEY_TYPE_UNAUTH_COMB; + } + } else { + SMP_TRACE_ERROR ("%s failed to update link_key. Sec Mode = %d, sm4 = 0x%02x", + __func__, btm_cb.security_mode, p_dev_rec->sm4); + return FALSE; + } + + link_key_type += BTM_LTK_DERIVED_LKEY_OFFSET; + + UINT8 *p; + BT_OCTET16 notif_link_key; + p = notif_link_key; + ARRAY16_TO_STREAM(p, link_key); + + btm_sec_link_key_notification (bda_for_lk, notif_link_key, link_key_type); + + SMP_TRACE_EVENT ("%s is completed", __func__); + } + + return ret; +} + +/******************************************************************************* +** +** Function smp_calculate_long_term_key_from_link_key +** +** Description The function calculates and saves SC LTK derived from BR/EDR +** link key. +** +** Returns FALSE if out of resources, TRUE in other cases. +** +*******************************************************************************/ +BOOLEAN smp_calculate_long_term_key_from_link_key(tSMP_CB *p_cb) +{ + BOOLEAN ret = TRUE; + tBTM_SEC_DEV_REC *p_dev_rec; + UINT8 rev_link_key[16]; + + SMP_TRACE_DEBUG ("%s", __FUNCTION__); + + if ((p_dev_rec = btm_find_dev (p_cb->pairing_bda)) == NULL) { + SMP_TRACE_ERROR("%s failed to find Security Record", __FUNCTION__); + return FALSE; + } + + UINT8 br_link_key_type; + if ((br_link_key_type = BTM_SecGetDeviceLinkKeyType (p_cb->pairing_bda)) + == BTM_LKEY_TYPE_IGNORE) { + SMP_TRACE_ERROR("%s failed to retrieve BR link type", __FUNCTION__); + return FALSE; + } + + if ((br_link_key_type != BTM_LKEY_TYPE_AUTH_COMB_P_256) && + (br_link_key_type != BTM_LKEY_TYPE_UNAUTH_COMB_P_256)) { + SMP_TRACE_ERROR("%s LE SC LTK can't be derived from LK %d", + __FUNCTION__, br_link_key_type); + return FALSE; + } + + UINT8 *p1; + UINT8 *p2; + p1 = rev_link_key; + p2 = p_dev_rec->link_key; + REVERSE_ARRAY_TO_STREAM(p1, p2, 16); + + BT_OCTET16 intermediate_long_term_key; + /* "tmp2" obtained from the spec */ + ret = smp_calculate_h6(rev_link_key, (UINT8 *) "2pmt" /* reversed "tmp2" */, + intermediate_long_term_key); + + if (!ret) { + SMP_TRACE_ERROR("%s failed to derive intermediate_long_term_key", __FUNCTION__); + return ret; + } + + /* "brle" obtained from the spec */ + ret = smp_calculate_h6(intermediate_long_term_key, (UINT8 *) "elrb" /* reversed "brle" */, + p_cb->ltk); + + if (!ret) { + SMP_TRACE_ERROR("%s failed", __FUNCTION__); + } else { + p_cb->sec_level = (br_link_key_type == BTM_LKEY_TYPE_AUTH_COMB_P_256) + ? SMP_SEC_AUTHENTICATED : SMP_SEC_UNAUTHENTICATE; + SMP_TRACE_EVENT ("%s is completed", __FUNCTION__); + } + + return ret; +} + +/******************************************************************************* +** +** Function smp_calculate_h6 +** +** Description The function calculates +** C = h6(W, KeyID) = AES-CMAC (KeyID) +** W +** where +** input: W is 128 bit, +** KeyId is 32 bit, +** output: C is 128 bit. +** +** Returns FALSE if out of resources, TRUE in other cases. +** +** Note The LSB is the first octet, the MSB is the last octet of +** the AES-CMAC input/output stream. +** +*******************************************************************************/ +BOOLEAN smp_calculate_h6(UINT8 *w, UINT8 *keyid, UINT8 *c) +{ +#if SMP_DEBUG == TRUE + UINT8 *p_print = NULL; +#endif + + SMP_TRACE_DEBUG ("%s", __FUNCTION__); +#if SMP_DEBUG == TRUE + p_print = w; + smp_debug_print_nbyte_little_endian (p_print, (const UINT8 *)"W", BT_OCTET16_LEN); + p_print = keyid; + smp_debug_print_nbyte_little_endian (p_print, (const UINT8 *)"keyID", 4); +#endif + + UINT8 *p = NULL; + UINT8 key[BT_OCTET16_LEN]; + + p = key; + ARRAY_TO_STREAM(p, w, BT_OCTET16_LEN); + +#if SMP_DEBUG == TRUE + p_print = key; + smp_debug_print_nbyte_little_endian (p_print, (const UINT8 *)"K", BT_OCTET16_LEN); +#endif + + UINT8 msg_len = 4 /* KeyID size */; + UINT8 msg[4]; + + p = msg; + ARRAY_TO_STREAM(p, keyid, 4); + +#if SMP_DEBUG == TRUE + p_print = msg; + smp_debug_print_nbyte_little_endian (p_print, (const UINT8 *) "M", msg_len); +#endif + + BOOLEAN ret = TRUE; + UINT8 cmac[BT_OCTET16_LEN]; + if (!aes_cipher_msg_auth_code(key, msg, msg_len, BT_OCTET16_LEN, cmac)) { + SMP_TRACE_ERROR("%s failed", __FUNCTION__); + ret = FALSE; + } + +#if SMP_DEBUG == TRUE + p_print = cmac; + smp_debug_print_nbyte_little_endian (p_print, (const UINT8 *)"AES-CMAC", BT_OCTET16_LEN); +#endif + + p = c; + ARRAY_TO_STREAM(p, cmac, BT_OCTET16_LEN); + return ret; +} + +/******************************************************************************* +** +** Function smp_start_nonce_generation +** +** Description This function starts nonce generation. +** +** Returns void +** +*******************************************************************************/ +void smp_start_nonce_generation(tSMP_CB *p_cb) +{ + SMP_TRACE_DEBUG("%s", __FUNCTION__); + p_cb->rand_enc_proc_state = SMP_GEN_NONCE_0_7; + if (!btsnd_hcic_ble_rand((void *)smp_rand_back)) { + smp_rand_back(NULL); + } +} + +/******************************************************************************* +** +** Function smp_finish_nonce_generation +** +** Description This function finishes nonce generation. +** +** Returns void +** +*******************************************************************************/ +void smp_finish_nonce_generation(tSMP_CB *p_cb) +{ + SMP_TRACE_DEBUG("%s", __FUNCTION__); + p_cb->rand_enc_proc_state = SMP_GEN_NONCE_8_15; + if (!btsnd_hcic_ble_rand((void *)smp_rand_back)) { + smp_rand_back(NULL); + } +} + +/******************************************************************************* +** +** Function smp_process_new_nonce +** +** Description This function notifies SM that it has new nonce. +** +** Returns void +** +*******************************************************************************/ +void smp_process_new_nonce(tSMP_CB *p_cb) +{ + SMP_TRACE_DEBUG ("%s round %d", __FUNCTION__, p_cb->round); + smp_sm_event(p_cb, SMP_HAVE_LOC_NONCE_EVT, NULL); +} + +/******************************************************************************* +** +** Function smp_rand_back +** +** Description This function is to process the rand command finished, +** process the random/encrypted number for further action. +** +** Returns void +** +*******************************************************************************/ +static void smp_rand_back(tBTM_RAND_ENC *p) +{ + tSMP_CB *p_cb = &smp_cb; + UINT8 *pp = p->param_buf; + UINT8 failure = SMP_PAIR_FAIL_UNKNOWN; + UINT8 state = p_cb->rand_enc_proc_state & ~0x80; + + SMP_TRACE_DEBUG ("%s state=0x%x", __FUNCTION__, state); + if (p && p->status == HCI_SUCCESS) { + switch (state) { + case SMP_GEN_SRAND_MRAND: + memcpy((void *)p_cb->rand, p->param_buf, p->param_len); + smp_generate_rand_cont(p_cb, NULL); + break; + + case SMP_GEN_SRAND_MRAND_CONT: + memcpy((void *)&p_cb->rand[8], p->param_buf, p->param_len); + smp_generate_confirm(p_cb, NULL); + break; + + case SMP_GEN_DIV_LTK: + STREAM_TO_UINT16(p_cb->div, pp); + smp_generate_ltk_cont(p_cb, NULL); + break; + + case SMP_GEN_DIV_CSRK: + STREAM_TO_UINT16(p_cb->div, pp); + smp_compute_csrk(p_cb, NULL); + break; + + case SMP_GEN_TK: + smp_proc_passkey(p_cb, p); + break; + + case SMP_GEN_RAND_V: + memcpy(p_cb->enc_rand, p->param_buf, BT_OCTET8_LEN); + smp_generate_y(p_cb, NULL); + break; + + case SMP_GENERATE_PRIVATE_KEY_0_7: + case SMP_GENERATE_PRIVATE_KEY_8_15: + case SMP_GENERATE_PRIVATE_KEY_16_23: + case SMP_GENERATE_PRIVATE_KEY_24_31: + smp_continue_private_key_creation(p_cb, p); + break; + + case SMP_GEN_NONCE_0_7: + memcpy((void *)p_cb->rand, p->param_buf, p->param_len); + smp_finish_nonce_generation(p_cb); + break; + + case SMP_GEN_NONCE_8_15: + memcpy((void *)&p_cb->rand[8], p->param_buf, p->param_len); + smp_process_new_nonce(p_cb); + break; + } + + return; + } + + SMP_TRACE_ERROR("%s key generation failed: (%d)", __FUNCTION__, p_cb->rand_enc_proc_state); + smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &failure); +} + +#endif diff --git a/lib/bt/host/bluedroid/stack/smp/smp_l2c.c b/lib/bt/host/bluedroid/stack/smp/smp_l2c.c new file mode 100644 index 00000000..a6f06705 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/smp/smp_l2c.c @@ -0,0 +1,344 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains functions for the SMP L2Cap interface + * + ******************************************************************************/ + +#include "common/bt_target.h" +#include "osi/allocator.h" + +#if SMP_INCLUDED == TRUE + +#include +#include "stack/btm_ble_api.h" +#include "stack/l2c_api.h" + +#include "smp_int.h" + + +static void smp_tx_complete_callback(UINT16 cid, UINT16 num_pkt); +#if (BLE_INCLUDED == TRUE) + +static void smp_connect_callback(UINT16 channel, BD_ADDR bd_addr, BOOLEAN connected, UINT16 reason, + tBT_TRANSPORT transport); +static void smp_data_received(UINT16 channel, BD_ADDR bd_addr, BT_HDR *p_buf); +#endif ///BLE_INCLUDED == TRUE +#if (CLASSIC_BT_INCLUDED == TRUE) +static void smp_br_connect_callback(UINT16 channel, BD_ADDR bd_addr, BOOLEAN connected, UINT16 reason, + tBT_TRANSPORT transport); +static void smp_br_data_received(UINT16 channel, BD_ADDR bd_addr, BT_HDR *p_buf); +#endif ///CLASSIC_BT_INCLUDED == TRUE + +/******************************************************************************* +** +** Function smp_l2cap_if_init +** +** Description This function is called during the SMP task startup +** to register interface functions with L2CAP. +** +*******************************************************************************/ +void smp_l2cap_if_init (void) +{ + tL2CAP_FIXED_CHNL_REG fixed_reg; + SMP_TRACE_EVENT ("SMDBG l2c %s", __func__); + fixed_reg.fixed_chnl_opts.mode = L2CAP_FCR_BASIC_MODE; + fixed_reg.fixed_chnl_opts.max_transmit = 0; + fixed_reg.fixed_chnl_opts.rtrans_tout = 0; + fixed_reg.fixed_chnl_opts.mon_tout = 0; + fixed_reg.fixed_chnl_opts.mps = 0; + fixed_reg.fixed_chnl_opts.tx_win_sz = 0; + + fixed_reg.pL2CA_FixedTxComplete_Cb = smp_tx_complete_callback; + + fixed_reg.pL2CA_FixedCong_Cb = NULL; /* do not handle congestion on this channel */ + fixed_reg.default_idle_tout = 0; /* set 0 seconds timeout, 0xffff default idle timeout. + This timeout is used to wait for the end of the pairing + and then make a disconnect request, setting a larger value + will cause the disconnect event to go back up for a long time. + Set to 0 will be disconnected directly, and it will come up + pairing failure, so it will not cause adverse effects. */ +#if (BLE_INCLUDED == TRUE) + fixed_reg.pL2CA_FixedConn_Cb = smp_connect_callback; + fixed_reg.pL2CA_FixedData_Cb = smp_data_received; + L2CA_RegisterFixedChannel (L2CAP_SMP_CID, &fixed_reg); +#endif ///BLE_INCLUDED == TRUE + +#if (CLASSIC_BT_INCLUDED == TRUE) + fixed_reg.pL2CA_FixedConn_Cb = smp_br_connect_callback; + fixed_reg.pL2CA_FixedData_Cb = smp_br_data_received; + + L2CA_RegisterFixedChannel (L2CAP_SMP_BR_CID, &fixed_reg); +#endif ///CLASSIC_BT_INCLUDED == TRUE +} + +#if (BLE_INCLUDED == TRUE) +/******************************************************************************* +** +** Function smp_connect_callback +** +** Description This callback function is called by L2CAP to indicate that +** SMP channel is +** connected (conn = TRUE)/disconnected (conn = FALSE). +** +*******************************************************************************/ +static void smp_connect_callback (UINT16 channel, BD_ADDR bd_addr, BOOLEAN connected, UINT16 reason, + tBT_TRANSPORT transport) +{ + tSMP_CB *p_cb = &smp_cb; + tSMP_INT_DATA int_data; + BD_ADDR dummy_bda = {0}; + + SMP_TRACE_EVENT ("SMDBG l2c %s\n", __FUNCTION__); + + if (transport == BT_TRANSPORT_BR_EDR || memcmp(bd_addr, dummy_bda, BD_ADDR_LEN) == 0) { + return; + } + if(!connected) { + //free timer + btu_free_timer(&p_cb->rsp_timer_ent); + } + if (memcmp(bd_addr, p_cb->pairing_bda, BD_ADDR_LEN) == 0) { + SMP_TRACE_EVENT ("%s() for pairing BDA: %08x%04x Event: %s\n", + __FUNCTION__, + (bd_addr[0] << 24) + (bd_addr[1] << 16) + (bd_addr[2] << 8) + bd_addr[3], + (bd_addr[4] << 8) + bd_addr[5], + (connected) ? "connected" : "disconnected"); + + if (connected) { + if (!p_cb->connect_initialized) { + p_cb->connect_initialized = TRUE; + /* initiating connection established */ + p_cb->role = L2CA_GetBleConnRole(bd_addr); + + /* initialize local i/r key to be default keys */ + p_cb->local_r_key = p_cb->local_i_key = SMP_SEC_DEFAULT_KEY; + p_cb->loc_auth_req = p_cb->peer_auth_req = SMP_DEFAULT_AUTH_REQ; + p_cb->cb_evt = SMP_IO_CAP_REQ_EVT; + smp_sm_event(p_cb, SMP_L2CAP_CONN_EVT, NULL); + } + } else { + int_data.reason = reason; + /* Disconnected while doing security */ + smp_sm_event(p_cb, SMP_L2CAP_DISCONN_EVT, &int_data); + } + } +} + +/******************************************************************************* +** +** Function smp_data_received +** +** Description This function is called when data is received from L2CAP on +** SMP channel. +** +** +** Returns void +** +*******************************************************************************/ +static void smp_data_received(UINT16 channel, BD_ADDR bd_addr, BT_HDR *p_buf) +{ + tSMP_CB *p_cb = &smp_cb; + UINT8 *p = (UINT8 *)(p_buf + 1) + p_buf->offset; + UINT8 cmd ; + SMP_TRACE_EVENT ("\nSMDBG l2c %s\n", __FUNCTION__); + + STREAM_TO_UINT8(cmd, p); + + /* sanity check */ + if ((SMP_OPCODE_MAX < cmd) || (SMP_OPCODE_MIN > cmd)) { + SMP_TRACE_WARNING( "Ignore received command with RESERVED code 0x%02x\n", cmd); + osi_free (p_buf); + return; + } + + /* reject the pairing request if there is an on-going SMP pairing */ + if (SMP_OPCODE_PAIRING_REQ == cmd || SMP_OPCODE_SEC_REQ == cmd) { + if ((p_cb->state == SMP_STATE_IDLE) && (p_cb->br_state == SMP_BR_STATE_IDLE) && + !(p_cb->flags & SMP_PAIR_FLAGS_WE_STARTED_DD)) { + p_cb->role = L2CA_GetBleConnRole(bd_addr); + memcpy(&p_cb->pairing_bda[0], bd_addr, BD_ADDR_LEN); + } else if (memcmp(&bd_addr[0], p_cb->pairing_bda, BD_ADDR_LEN)) { + osi_free (p_buf); + smp_reject_unexpected_pairing_command(bd_addr); + return; + } + /* else, out of state pairing request/security request received, passed into SM */ + } + + if (memcmp(&bd_addr[0], p_cb->pairing_bda, BD_ADDR_LEN) == 0) { + btu_stop_timer (&p_cb->rsp_timer_ent); + btu_start_timer (&p_cb->rsp_timer_ent, BTU_TTYPE_SMP_PAIRING_CMD, + SMP_WAIT_FOR_RSP_TOUT); + + if (cmd == SMP_OPCODE_CONFIRM) { + SMP_TRACE_DEBUG ("in %s cmd = 0x%02x, peer_auth_req = 0x%02x," + "loc_auth_req = 0x%02x\n", + __FUNCTION__, cmd, p_cb->peer_auth_req, p_cb->loc_auth_req); + + if ((p_cb->peer_auth_req & SMP_SC_SUPPORT_BIT) && + (p_cb->loc_auth_req & SMP_SC_SUPPORT_BIT)) { + cmd = SMP_OPCODE_PAIR_COMMITM; + } + } + + p_cb->rcvd_cmd_code = cmd; + p_cb->rcvd_cmd_len = (UINT8) p_buf->len; + smp_sm_event(p_cb, cmd, p); + } + + osi_free (p_buf); +} +#endif ///BLE_INCLUDED == TRUE + +/******************************************************************************* +** +** Function smp_tx_complete_callback +** +** Description SMP channel tx complete callback +** +*******************************************************************************/ +static void smp_tx_complete_callback (UINT16 cid, UINT16 num_pkt) +{ + tSMP_CB *p_cb = &smp_cb; + + if (p_cb->total_tx_unacked >= num_pkt) { + p_cb->total_tx_unacked -= num_pkt; + } else { + SMP_TRACE_ERROR("Unexpected %s: num_pkt = %d", __func__, num_pkt); + } + + UINT8 reason = SMP_SUCCESS; + if (p_cb->total_tx_unacked == 0 && p_cb->wait_for_authorization_complete) { + if (cid == L2CAP_SMP_CID) { + smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &reason); + } else { +#if (CLASSIC_BT_INCLUDED == TRUE) + smp_br_state_machine_event(p_cb, SMP_BR_AUTH_CMPL_EVT, &reason); +#endif ///CLASSIC_BT_INCLUDED == TRUE + } + } +} + +/******************************************************************************* +** +** Function smp_br_connect_callback +** +** Description This callback function is called by L2CAP to indicate that +** SMP BR channel is +** connected (conn = TRUE)/disconnected (conn = FALSE). +** +*******************************************************************************/ +#if (CLASSIC_BT_INCLUDED == TRUE) +static void smp_br_connect_callback(UINT16 channel, BD_ADDR bd_addr, BOOLEAN connected, + UINT16 reason, tBT_TRANSPORT transport) +{ + tSMP_CB *p_cb = &smp_cb; + tSMP_INT_DATA int_data; + + SMP_TRACE_EVENT ("%s", __func__); + + if (transport != BT_TRANSPORT_BR_EDR) { + SMP_TRACE_EVENT ("%s is called on unexpected transport %d\n", + __func__, transport); + return; + } + + if (!(memcmp(bd_addr, p_cb->pairing_bda, BD_ADDR_LEN) == 0)) { + return; + } + + SMP_TRACE_EVENT ("%s for pairing BDA: %08x%04x Event: %s\n", + __func__, + (bd_addr[0] << 24) + (bd_addr[1] << 16) + (bd_addr[2] << 8) + bd_addr[3], + (bd_addr[4] << 8) + bd_addr[5], + (connected) ? "connected" : "disconnected"); + + if (connected) { + if (!p_cb->connect_initialized) { + p_cb->connect_initialized = TRUE; + /* initialize local i/r key to be default keys */ + p_cb->local_r_key = p_cb->local_i_key = SMP_BR_SEC_DEFAULT_KEY; + p_cb->loc_auth_req = p_cb->peer_auth_req = 0; + p_cb->cb_evt = SMP_BR_KEYS_REQ_EVT; + smp_br_state_machine_event(p_cb, SMP_BR_L2CAP_CONN_EVT, NULL); + } + } else { + int_data.reason = reason; + /* Disconnected while doing security */ + smp_br_state_machine_event(p_cb, SMP_BR_L2CAP_DISCONN_EVT, &int_data); + } +} + +/******************************************************************************* +** +** Function smp_br_data_received +** +** Description This function is called when data is received from L2CAP on +** SMP BR channel. +** +** Returns void +** +*******************************************************************************/ +static void smp_br_data_received(UINT16 channel, BD_ADDR bd_addr, BT_HDR *p_buf) +{ + tSMP_CB *p_cb = &smp_cb; + UINT8 *p = (UINT8 *)(p_buf + 1) + p_buf->offset; + UINT8 cmd ; + SMP_TRACE_EVENT ("SMDBG l2c %s\n", __func__); + + STREAM_TO_UINT8(cmd, p); + + /* sanity check */ + if ((SMP_OPCODE_MAX < cmd) || (SMP_OPCODE_MIN > cmd)) { + SMP_TRACE_WARNING( "Ignore received command with RESERVED code 0x%02x", cmd); + osi_free(p_buf); + return; + } + + /* reject the pairing request if there is an on-going SMP pairing */ + if (SMP_OPCODE_PAIRING_REQ == cmd) { + if ((p_cb->state == SMP_STATE_IDLE) && (p_cb->br_state == SMP_BR_STATE_IDLE)) { + p_cb->role = HCI_ROLE_SLAVE; + p_cb->smp_over_br = TRUE; + memcpy(&p_cb->pairing_bda[0], bd_addr, BD_ADDR_LEN); + } else if (memcmp(&bd_addr[0], p_cb->pairing_bda, BD_ADDR_LEN)) { + osi_free (p_buf); + smp_reject_unexpected_pairing_command(bd_addr); + return; + } + /* else, out of state pairing request received, passed into State Machine */ + } + + if (memcmp(&bd_addr[0], p_cb->pairing_bda, BD_ADDR_LEN) == 0) { + btu_stop_timer (&p_cb->rsp_timer_ent); + btu_start_timer (&p_cb->rsp_timer_ent, BTU_TTYPE_SMP_PAIRING_CMD, + SMP_WAIT_FOR_RSP_TOUT); + + p_cb->rcvd_cmd_code = cmd; + p_cb->rcvd_cmd_len = (UINT8) p_buf->len; + smp_br_state_machine_event(p_cb, cmd, p); + } + + osi_free (p_buf); +} +#endif /* CLASSIC_BT_INCLUDED == TRUE */ + +#endif /* SMP_INCLUDED == TRUE */ diff --git a/lib/bt/host/bluedroid/stack/smp/smp_main.c b/lib/bt/host/bluedroid/stack/smp/smp_main.c new file mode 100644 index 00000000..512aca0a --- /dev/null +++ b/lib/bt/host/bluedroid/stack/smp/smp_main.c @@ -0,0 +1,810 @@ +/****************************************************************************** + * + * Copyright (C) 2003-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +#include "common/bt_target.h" + +#if SMP_INCLUDED == TRUE + +#include +#include "smp_int.h" + +const char *const smp_state_name [] = { + "SMP_STATE_IDLE", + "SMP_STATE_WAIT_APP_RSP", + "SMP_STATE_SEC_REQ_PENDING", + "SMP_STATE_PAIR_REQ_RSP", + "SMP_STATE_WAIT_CONFIRM", + "SMP_STATE_CONFIRM", + "SMP_STATE_RAND", + "SMP_STATE_PUBLIC_KEY_EXCH", + "SMP_STATE_SEC_CONN_PHS1_START", + "SMP_STATE_WAIT_COMMITMENT", + "SMP_STATE_WAIT_NONCE", + "SMP_STATE_SEC_CONN_PHS2_START", + "SMP_STATE_WAIT_DHK_CHECK", + "SMP_STATE_DHK_CHECK", + "SMP_STATE_ENCRYPTION_PENDING", + "SMP_STATE_BOND_PENDING", + "SMP_STATE_CREATE_LOCAL_SEC_CONN_OOB_DATA", + "SMP_STATE_MAX" +}; + +const char *const smp_event_name [] = { + "PAIRING_REQ_EVT", + "PAIRING_RSP_EVT", + "CONFIRM_EVT", + "RAND_EVT", + "PAIRING_FAILED_EVT", + "ENC_INFO_EVT", + "MASTER_ID_EVT", + "ID_INFO_EVT", + "ID_ADDR_EVT", + "SIGN_INFO_EVT", + "SECURITY_REQ_EVT", + "PAIR_PUBLIC_KEY_EVT", + "PAIR_DHKEY_CHECK_EVT", + "PAIR_KEYPRESS_NOTIFICATION_EVT", + "PAIR_COMMITMENT_EVT", + "KEY_READY_EVT", + "ENCRYPTED_EVT", + "L2CAP_CONN_EVT", + "L2CAP_DISCONN_EVT", + "API_IO_RSP_EVT", + "API_SEC_GRANT_EVT", + "TK_REQ_EVT", + "AUTH_CMPL_EVT", + "ENC_REQ_EVT", + "BOND_REQ_EVT", + "DISCARD_SEC_REQ_EVT", + "PUBLIC_KEY_EXCHANGE_REQ_EVT", + "LOCAL_PUBLIC_KEY_CRTD_EVT", + "BOTH_PUBLIC_KEYS_RCVD_EVT", + "SEC_CONN_DHKEY_COMPLETE_EVT", + "HAVE_LOCAL_NONCE_EVT", + "SEC_CONN_PHASE1_CMPLT_EVT", + "SEC_CONN_CALC_NC_EVT", + "SEC_CONN_DISPLAY_NC_EVT", + "SEC_CONN_OK_EVT", + "SEC_CONN_2_DHCK_CHECKS_PRESENT_EVT", + "SEC_CONN_KEY_READY_EVT", + "KEYPRESS_NOTIFICATION_EVT", + "SEC_CONN_OOB_DATA_EVT", + "CREATE_LOCAL_SEC_CONN_OOB_DATA_EVT", + "OUT_OF_RANGE_EVT" +}; + +const char *smp_get_event_name(tSMP_EVENT event); +const char *smp_get_state_name(tSMP_STATE state); + +#define SMP_SM_IGNORE 0 +#define SMP_NUM_ACTIONS 2 +#define SMP_SME_NEXT_STATE 2 +#define SMP_SM_NUM_COLS 3 + +typedef const UINT8(*tSMP_SM_TBL)[SMP_SM_NUM_COLS]; + +enum { + SMP_PROC_SEC_REQ, + SMP_SEND_PAIR_REQ, + SMP_SEND_PAIR_RSP, + SMP_SEND_CONFIRM, + SMP_SEND_PAIR_FAIL, + SMP_SEND_RAND, + SMP_SEND_ENC_INFO, + SMP_SEND_ID_INFO, + SMP_SEND_LTK_REPLY, + SMP_PROC_PAIR_CMD, + SMP_PROC_PAIR_FAIL, + SMP_PROC_CONFIRM, + SMP_PROC_RAND, + SMP_PROC_ENC_INFO, + SMP_PROC_MASTER_ID, + SMP_PROC_ID_INFO, + SMP_PROC_ID_ADDR, + SMP_PROC_SRK_INFO, + SMP_PROC_SEC_GRANT, + SMP_PROC_SL_KEY, + SMP_PROC_COMPARE, + SMP_PROC_IO_RSP, + SMP_GENERATE_COMPARE, + SMP_GENERATE_CONFIRM, + SMP_GENERATE_STK, + SMP_KEY_DISTRIBUTE, + SMP_START_ENC, + SMP_PAIRING_CMPL, + SMP_DECIDE_ASSO_MODEL, + SMP_SEND_APP_CBACK, + SMP_CHECK_AUTH_REQ, + SMP_PAIR_TERMINATE, + SMP_ENC_CMPL, + SMP_PROC_DISCARD, + SMP_CREATE_PRIVATE_KEY, + SMP_USE_OOB_PRIVATE_KEY, + SMP_SEND_PAIR_PUBLIC_KEY, + SMP_PROCESS_PAIR_PUBLIC_KEY, + SMP_HAVE_BOTH_PUBLIC_KEYS, + SMP_START_SEC_CONN_PHASE1, + SMP_PROCESS_LOCAL_NONCE, + SMP_SEND_COMMITMENT, + SMP_PROCESS_PAIRING_COMMITMENT, + SMP_PROCESS_PEER_NONCE, + SMP_CALCULATE_LOCAL_DHKEY_CHECK, + SMP_SEND_DHKEY_CHECK, + SMP_PROCESS_DHKEY_CHECK, + SMP_CALCULATE_PEER_DHKEY_CHECK, + SMP_MATCH_DHKEY_CHECKS, + SMP_CALCULATE_NUMERIC_COMPARISON_DISPLAY_NUMBER, + SMP_MOVE_TO_SEC_CONN_PHASE2, + SMP_PH2_DHKEY_CHECKS_ARE_PRESENT, + SMP_WAIT_FOR_BOTH_PUBLIC_KEYS, + SMP_START_PASSKEY_VERIFICATION, + SMP_SEND_KEYPRESS_NOTIFICATION, + SMP_PROCESS_KEYPRESS_NOTIFICATION, + SMP_PROCESS_SECURE_CONNECTION_OOB_DATA, + SMP_SET_LOCAL_OOB_KEYS, + SMP_SET_LOCAL_OOB_RAND_COMMITMENT, + SMP_IDLE_TERMINATE, + SMP_FAST_CONN_PARAM, + SMP_SM_NO_ACTION +}; + +#if (BLE_INCLUDED == TRUE) +static const tSMP_ACT smp_sm_action[SMP_SM_NO_ACTION] = { + smp_proc_sec_req, + smp_send_pair_req, + smp_send_pair_rsp, + smp_send_confirm, + smp_send_pair_fail, + smp_send_rand, + smp_send_enc_info, + smp_send_id_info, + smp_send_ltk_reply, + smp_proc_pair_cmd, + smp_proc_pair_fail, + smp_proc_confirm, + smp_proc_rand, + smp_proc_enc_info, + smp_proc_master_id, + smp_proc_id_info, + smp_proc_id_addr, + smp_proc_srk_info, + smp_proc_sec_grant, + smp_proc_sl_key, + smp_proc_compare, + smp_process_io_response, + smp_generate_compare, + smp_generate_srand_mrand_confirm, + smp_generate_stk, + smp_key_distribution, + smp_start_enc, + smp_pairing_cmpl, + smp_decide_association_model, + smp_send_app_cback, + smp_check_auth_req, + smp_pair_terminate, + smp_enc_cmpl, + smp_proc_discard, + smp_create_private_key, + smp_use_oob_private_key, + smp_send_pair_public_key, + smp_process_pairing_public_key, + smp_both_have_public_keys, + smp_start_secure_connection_phase1, + smp_process_local_nonce, + smp_send_commitment, + smp_process_pairing_commitment, + smp_process_peer_nonce, + smp_calculate_local_dhkey_check, + smp_send_dhkey_check, + smp_process_dhkey_check, + smp_calculate_peer_dhkey_check, + smp_match_dhkey_checks, + smp_calculate_numeric_comparison_display_number, + smp_move_to_secure_connections_phase2, + smp_phase_2_dhkey_checks_are_present, + smp_wait_for_both_public_keys, + smp_start_passkey_verification, + smp_send_keypress_notification, + smp_process_keypress_notification, + smp_process_secure_connection_oob_data, + smp_set_local_oob_keys, + smp_set_local_oob_random_commitment, + smp_idle_terminate, + smp_fast_conn_param +}; +#else +static const tSMP_ACT smp_sm_action[SMP_SM_NO_ACTION] = {NULL}; +#endif ///BLE_INCLUDED == TRUE + +/************ SMP Master FSM State/Event Indirection Table **************/ +static const UINT8 smp_master_entry_map[][SMP_STATE_MAX] = { + /* state name: Idle WaitApp SecReq Pair Wait Confirm Rand PublKey SCPhs1 Wait Wait SCPhs2 Wait DHKChk Enc Bond CrLocSc + Rsp Pend ReqRsp Cfm Exch Strt Cmtm Nonce Strt DHKChk Pend Pend OobData */ + /* PAIR_REQ */{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + /* PAIR_RSP */{ 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + /* CONFIRM */{ 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + /* RAND */{ 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 }, + /* PAIR_FAIL */{ 0, 0x81, 0, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0, 0x81, 0 }, + /* ENC_INFO */{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0 }, + /* MASTER_ID */{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0 }, + /* ID_INFO */{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0 }, + /* ID_ADDR */{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0 }, + /* SIGN_INFO */{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0 }, + /* SEC_REQ */{ 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + /* PAIR_PUBLIC_KEY */{ 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + /* PAIR_DHKEY_CHCK */{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0 }, + /* PAIR_KEYPR_NOTIF */{ 0, 8, 0, 0, 0, 0, 0, 0, 5, 2, 0, 0, 0, 0, 0, 0, 0 }, + /* PAIR_COMMITM */{ 0, 0, 0, 0, 0, 0, 0, 0, 6, 1, 0, 0, 0, 0, 0, 0, 0 }, + /* KEY_READY */{ 0, 3, 0, 3, 1, 0, 2, 0, 4, 0, 0, 0, 0, 0, 1, 6, 0 }, + /* ENC_CMPL */{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0 }, + /* L2C_CONN */{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + /* L2C_DISC */{ 3, 0x83, 0, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0 }, + /* IO_RSP */{ 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + /* SEC_GRANT */{ 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + /* TK_REQ */{ 0, 0, 0, 2, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0 }, + /* AUTH_CMPL */{ 4, 0x82, 0, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0 }, + /* ENC_REQ */{ 0, 4, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0 }, + /* BOND_REQ */{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0 }, + /* DISCARD_SEC_REQ */{ 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0 }, + /* PUBL_KEY_EXCH_REQ */{ 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + /* LOC_PUBL_KEY_CRTD */{ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1 }, + /* BOTH_PUBL_KEYS_RCVD */{ 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + /* SC_DHKEY_CMPLT */{ 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0 }, + /* HAVE_LOC_NONCE */{ 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 2 }, + /* SC_PHASE1_CMPLT */{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + /* SC_CALC_NC */{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0 }, + /* SC_DSPL_NC */{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0 }, + /* SC_NC_OK */{ 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + /* SC_2_DHCK_CHKS_PRES */{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + /* SC_KEY_READY */{ 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 }, + /* KEYPR_NOTIF */{ 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + /* SC_OOB_DATA */{ 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + /* CR_LOC_SC_OOB_DATA */{ 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, +}; + +static const UINT8 smp_all_table[][SMP_SM_NUM_COLS] = { + /* Event Action Next State */ + /* PAIR_FAIL */ {SMP_PROC_PAIR_FAIL, SMP_PAIRING_CMPL, SMP_STATE_IDLE}, + /* AUTH_CMPL */ {SMP_SEND_PAIR_FAIL, SMP_PAIRING_CMPL, SMP_STATE_IDLE}, + /* L2C_DISC */ {SMP_PAIR_TERMINATE, SMP_SM_NO_ACTION, SMP_STATE_IDLE} +}; + +static const UINT8 smp_master_idle_table[][SMP_SM_NUM_COLS] = { + /* Event Action Next State */ + /* L2C_CONN */ {SMP_SEND_APP_CBACK, SMP_SM_NO_ACTION, SMP_STATE_WAIT_APP_RSP}, + /* SEC_REQ */ {SMP_PROC_SEC_REQ, SMP_SEND_APP_CBACK, SMP_STATE_WAIT_APP_RSP}, + /* L2C_DISC */ {SMP_IDLE_TERMINATE, SMP_SM_NO_ACTION, SMP_STATE_IDLE}, + /* AUTH_CMPL */ {SMP_PAIRING_CMPL, SMP_SM_NO_ACTION, SMP_STATE_IDLE} + /* CR_LOC_SC_OOB_DATA */ , {SMP_CREATE_PRIVATE_KEY, SMP_SM_NO_ACTION, SMP_STATE_CREATE_LOCAL_SEC_CONN_OOB_DATA} + +}; + +static const UINT8 smp_master_wait_for_app_response_table[][SMP_SM_NUM_COLS] = { + /* Event Action Next State */ + /* SEC_GRANT */ {SMP_PROC_SEC_GRANT, SMP_SEND_APP_CBACK, SMP_STATE_WAIT_APP_RSP}, + /* IO_RSP */ {SMP_SEND_PAIR_REQ, SMP_FAST_CONN_PARAM, SMP_STATE_PAIR_REQ_RSP}, + + /* TK ready */ + /* KEY_READY */ {SMP_GENERATE_CONFIRM, SMP_SM_NO_ACTION, SMP_STATE_WAIT_CONFIRM}, + + /* start enc mode setup */ + /* ENC_REQ */ { SMP_START_ENC, SMP_FAST_CONN_PARAM, SMP_STATE_ENCRYPTION_PENDING}, + /* DISCARD_SEC_REQ */ { SMP_PROC_DISCARD, SMP_SM_NO_ACTION, SMP_STATE_IDLE} + /* user confirms NC 'OK', i.e. phase 1 is completed */ + /* SC_NC_OK */, { SMP_MOVE_TO_SEC_CONN_PHASE2, SMP_SM_NO_ACTION, SMP_STATE_SEC_CONN_PHS2_START}, + /* user-provided passkey is rcvd */ + /* SC_KEY_READY */ { SMP_START_PASSKEY_VERIFICATION, SMP_SM_NO_ACTION, SMP_STATE_SEC_CONN_PHS1_START}, + /* PAIR_KEYPR_NOTIF */ { SMP_PROCESS_KEYPRESS_NOTIFICATION, SMP_SEND_APP_CBACK, SMP_STATE_WAIT_APP_RSP}, + /* KEYPR_NOTIF */ { SMP_SEND_KEYPRESS_NOTIFICATION, SMP_SM_NO_ACTION, SMP_STATE_WAIT_APP_RSP}, + /* SC_OOB_DATA */ { SMP_USE_OOB_PRIVATE_KEY, SMP_SM_NO_ACTION, SMP_STATE_PUBLIC_KEY_EXCH} +}; + +static const UINT8 smp_master_pair_request_response_table[][SMP_SM_NUM_COLS] = { + /* Event Action Next State */ + /* PAIR_RSP */ { SMP_PROC_PAIR_CMD, SMP_SM_NO_ACTION, SMP_STATE_PAIR_REQ_RSP}, + /* TK_REQ */ { SMP_SEND_APP_CBACK, SMP_SM_NO_ACTION, SMP_STATE_WAIT_APP_RSP}, + + /* TK ready */ + /* KEY_READY */{ SMP_GENERATE_CONFIRM, SMP_SM_NO_ACTION, SMP_STATE_WAIT_CONFIRM} + /* PUBL_KEY_EXCH_REQ */, { SMP_CREATE_PRIVATE_KEY, SMP_SM_NO_ACTION, SMP_STATE_PUBLIC_KEY_EXCH} +}; + +static const UINT8 smp_master_wait_for_confirm_table[][SMP_SM_NUM_COLS] = { + /* Event Action Next State */ + /* KEY_READY*/ {SMP_SEND_CONFIRM, SMP_SM_NO_ACTION, SMP_STATE_CONFIRM}/* CONFIRM ready */ +}; + +static const UINT8 smp_master_confirm_table[][SMP_SM_NUM_COLS] = { + /* Event Action Next State */ + /* CONFIRM */ { SMP_PROC_CONFIRM, SMP_SEND_RAND, SMP_STATE_RAND} +}; + +static const UINT8 smp_master_rand_table[][SMP_SM_NUM_COLS] = { + /* Event Action Next State */ + /* RAND */ { SMP_PROC_RAND, SMP_GENERATE_COMPARE, SMP_STATE_RAND}, + /* KEY_READY*/ { SMP_PROC_COMPARE, SMP_SM_NO_ACTION, SMP_STATE_RAND}, /* Compare ready */ + /* ENC_REQ */ { SMP_GENERATE_STK, SMP_SM_NO_ACTION, SMP_STATE_ENCRYPTION_PENDING} +}; + +static const UINT8 smp_master_public_key_exchange_table[][SMP_SM_NUM_COLS] = { + /* Event Action Next State */ + /* LOC_PUBL_KEY_CRTD */{ SMP_SEND_PAIR_PUBLIC_KEY, SMP_SM_NO_ACTION, SMP_STATE_PUBLIC_KEY_EXCH}, + /* PAIR_PUBLIC_KEY */{ SMP_PROCESS_PAIR_PUBLIC_KEY, SMP_SM_NO_ACTION, SMP_STATE_PUBLIC_KEY_EXCH}, + /* BOTH_PUBL_KEYS_RCVD */{ SMP_HAVE_BOTH_PUBLIC_KEYS, SMP_SM_NO_ACTION, SMP_STATE_SEC_CONN_PHS1_START}, +}; + +static const UINT8 smp_master_sec_conn_phs1_start_table[][SMP_SM_NUM_COLS] = { + /* Event Action Next State */ + /* SC_DHKEY_CMPLT */{ SMP_START_SEC_CONN_PHASE1, SMP_SM_NO_ACTION, SMP_STATE_SEC_CONN_PHS1_START}, + /* HAVE_LOC_NONCE */{ SMP_PROCESS_LOCAL_NONCE, SMP_SM_NO_ACTION, SMP_STATE_WAIT_COMMITMENT}, + /* TK_REQ */{ SMP_SEND_APP_CBACK, SMP_SM_NO_ACTION, SMP_STATE_WAIT_APP_RSP}, + /* SMP_MODEL_SEC_CONN_PASSKEY_DISP model, passkey is sent up to display,*/ + /* It's time to start commitment calculation */ + /* KEY_READY */{ SMP_START_PASSKEY_VERIFICATION, SMP_SM_NO_ACTION, SMP_STATE_SEC_CONN_PHS1_START}, + /* PAIR_KEYPR_NOTIF */{ SMP_PROCESS_KEYPRESS_NOTIFICATION, SMP_SEND_APP_CBACK, SMP_STATE_SEC_CONN_PHS1_START}, + /* PAIR_COMMITM */{ SMP_PROCESS_PAIRING_COMMITMENT, SMP_SM_NO_ACTION, SMP_STATE_SEC_CONN_PHS1_START}, +}; + +static const UINT8 smp_master_wait_commitment_table[][SMP_SM_NUM_COLS] = { + /* Event Action Next State */ + /* PAIR_COMMITM */{ SMP_PROCESS_PAIRING_COMMITMENT, SMP_SEND_RAND, SMP_STATE_WAIT_NONCE}, + /* PAIR_KEYPR_NOTIF */{ SMP_PROCESS_KEYPRESS_NOTIFICATION, SMP_SEND_APP_CBACK, SMP_STATE_WAIT_COMMITMENT}, +}; + +static const UINT8 smp_master_wait_nonce_table[][SMP_SM_NUM_COLS] = { + /* Event Action Next State */ + /* peer nonce is received */ + /* RAND */{SMP_PROC_RAND, SMP_PROCESS_PEER_NONCE, SMP_STATE_SEC_CONN_PHS2_START}, + /* NC model, time to calculate number for NC */ + /* SC_CALC_NC */{SMP_CALCULATE_NUMERIC_COMPARISON_DISPLAY_NUMBER, SMP_SM_NO_ACTION, SMP_STATE_WAIT_NONCE}, + /* NC model, time to display calculated number for NC to the user */ + /* SC_DSPL_NC */{SMP_SEND_APP_CBACK, SMP_SM_NO_ACTION, SMP_STATE_WAIT_APP_RSP}, +}; + +static const UINT8 smp_master_sec_conn_phs2_start_table[][SMP_SM_NUM_COLS] = { + /* Event Action Next State */ + /* SC_PHASE1_CMPLT */{SMP_CALCULATE_LOCAL_DHKEY_CHECK, SMP_SEND_DHKEY_CHECK, SMP_STATE_WAIT_DHK_CHECK}, +}; + +static const UINT8 smp_master_wait_dhk_check_table[][SMP_SM_NUM_COLS] = { + /* Event Action Next State */ + /* PAIR_DHKEY_CHCK */{SMP_PROCESS_DHKEY_CHECK, SMP_CALCULATE_PEER_DHKEY_CHECK, SMP_STATE_DHK_CHECK}, +}; + +static const UINT8 smp_master_dhk_check_table[][SMP_SM_NUM_COLS] = { + /* Event Action Next State */ + /* locally calculated peer dhkey check is ready -> compare it withs DHKey Check actually received from peer */ + /* SC_KEY_READY */{SMP_MATCH_DHKEY_CHECKS, SMP_SM_NO_ACTION, SMP_STATE_DHK_CHECK}, + /* locally calculated peer dhkey check is ready -> calculate STK, go to sending */ + /* HCI LE Start Encryption command */ + /* ENC_REQ */{SMP_GENERATE_STK, SMP_SM_NO_ACTION, SMP_STATE_ENCRYPTION_PENDING}, +}; + +static const UINT8 smp_master_enc_pending_table[][SMP_SM_NUM_COLS] = { + /* Event Action Next State */ + /* STK ready */ + /* KEY_READY */ { SMP_START_ENC, SMP_SM_NO_ACTION, SMP_STATE_ENCRYPTION_PENDING}, + /* ENCRYPTED */ { SMP_CHECK_AUTH_REQ, SMP_SM_NO_ACTION, SMP_STATE_ENCRYPTION_PENDING}, + /* BOND_REQ */ { SMP_KEY_DISTRIBUTE, SMP_SM_NO_ACTION, SMP_STATE_BOND_PENDING} +}; +static const UINT8 smp_master_bond_pending_table[][SMP_SM_NUM_COLS] = { + /* Event Action Next State */ + /* ENC_INFO */ { SMP_PROC_ENC_INFO, SMP_SM_NO_ACTION, SMP_STATE_BOND_PENDING}, + /* ID_INFO */ { SMP_PROC_ID_INFO, SMP_SM_NO_ACTION, SMP_STATE_BOND_PENDING}, + /* SIGN_INFO*/ { SMP_PROC_SRK_INFO, SMP_SM_NO_ACTION, SMP_STATE_BOND_PENDING}, + /* MASTER_ID*/ { SMP_PROC_MASTER_ID, SMP_SM_NO_ACTION, SMP_STATE_BOND_PENDING}, + /* ID_ADDR */ { SMP_PROC_ID_ADDR, SMP_SM_NO_ACTION, SMP_STATE_BOND_PENDING}, + /* KEY_READY */{SMP_SEND_ENC_INFO, SMP_SM_NO_ACTION, SMP_STATE_BOND_PENDING} /* LTK ready */ +}; + +static const UINT8 smp_master_create_local_sec_conn_oob_data[][SMP_SM_NUM_COLS] = { + /* Event Action Next State */ + /* LOC_PUBL_KEY_CRTD */ {SMP_SET_LOCAL_OOB_KEYS, SMP_SM_NO_ACTION, SMP_STATE_CREATE_LOCAL_SEC_CONN_OOB_DATA}, + /* HAVE_LOC_NONCE */ {SMP_SET_LOCAL_OOB_RAND_COMMITMENT, SMP_SM_NO_ACTION, SMP_STATE_IDLE} +}; + + +/************ SMP Slave FSM State/Event Indirection Table **************/ +static const UINT8 smp_slave_entry_map[][SMP_STATE_MAX] = { + /* state name: Idle WaitApp SecReq Pair Wait Confirm Rand PublKey SCPhs1 Wait Wait SCPhs2 Wait DHKChk Enc Bond CrLocSc + Rsp Pend ReqRsp Cfm Exch Strt Cmtm Nonce Strt DHKChk Pend Pend OobData */ + /* PAIR_REQ */{ 2, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + /* PAIR_RSP */{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + /* CONFIRM */{ 0, 4, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + /* RAND */{ 0, 0, 0, 0, 0, 1, 2, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 }, + /* PAIR_FAIL */{ 0, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0, 0 }, + /* ENC_INFO */{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0 }, + /* MASTER_ID */{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0 }, + /* ID_INFO */{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0 }, + /* ID_ADDR */{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0 }, + /* SIGN_INFO */{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0 }, + /* SEC_REQ */{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + /* PAIR_PUBLIC_KEY */{ 0, 0, 0, 5, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + /* PAIR_DHKEY_CHCK */{ 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 2, 0, 0, 0 }, + /* PAIR_KEYPR_NOTIF */{ 0, 9, 0, 0, 0, 0, 0, 0, 5, 2, 0, 0, 0, 0, 0, 0, 0 }, + /* PAIR_COMMITM */{ 0, 8, 0, 0, 0, 0, 0, 0, 6, 1, 0, 0, 0, 0, 0, 0, 0 }, + /* KEY_READY */{ 0, 3, 0, 3, 2, 2, 1, 0, 4, 0, 0, 0, 0, 0, 2, 1, 0 }, + /* ENC_CMPL */{ 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0 }, + /* L2C_CONN */{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + /* L2C_DISC */{ 0, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0 }, + /* IO_RSP */{ 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + /* SEC_GRANT */{ 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + /* TK_REQ */{ 0, 0, 0, 2, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0 }, + /* AUTH_CMPL */{ 0, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0 }, + /* ENC_REQ */{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, + /* BOND_REQ */{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0 }, + /* DISCARD_SEC_REQ */{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + /* PUBL_KEY_EXCH_REQ */{ 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + /* LOC_PUBL_KEY_CRTD */{ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1 }, + /* BOTH_PUBL_KEYS_RCVD */{ 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + /* SC_DHKEY_CMPLT */{ 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0 }, + /* HAVE_LOC_NONCE */{ 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 2 }, + /* SC_PHASE1_CMPLT */{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + /* SC_CALC_NC */{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0 }, + /* SC_DSPL_NC */{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0 }, + /* SC_NC_OK */{ 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + /* SC_2_DHCK_CHKS_PRES */{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0 }, + /* SC_KEY_READY */{ 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 }, + /* KEYPR_NOTIF */{ 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + /* SC_OOB_DATA */{ 0, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + /* CR_LOC_SC_OOB_DATA */{ 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, +}; + +static const UINT8 smp_slave_idle_table[][SMP_SM_NUM_COLS] = { + /* Event Action Next State */ + /* L2C_CONN */ {SMP_SEND_APP_CBACK, SMP_SM_NO_ACTION, SMP_STATE_WAIT_APP_RSP}, + /* PAIR_REQ */ {SMP_PROC_PAIR_CMD, SMP_SEND_APP_CBACK, SMP_STATE_WAIT_APP_RSP} + /* CR_LOC_SC_OOB_DATA */ , {SMP_CREATE_PRIVATE_KEY, SMP_SM_NO_ACTION, SMP_STATE_CREATE_LOCAL_SEC_CONN_OOB_DATA} +}; + +static const UINT8 smp_slave_wait_for_app_response_table [][SMP_SM_NUM_COLS] = { + /* Event Action Next State */ + /* IO_RSP */ {SMP_PROC_IO_RSP, SMP_FAST_CONN_PARAM, SMP_STATE_PAIR_REQ_RSP}, + /* SEC_GRANT */ {SMP_PROC_SEC_GRANT, SMP_SEND_APP_CBACK, SMP_STATE_WAIT_APP_RSP}, + + /* TK ready */ + /* KEY_READY */ {SMP_PROC_SL_KEY, SMP_SM_NO_ACTION, SMP_STATE_WAIT_APP_RSP}, + /* CONFIRM */ {SMP_PROC_CONFIRM, SMP_SM_NO_ACTION, SMP_STATE_CONFIRM} + /* DHKey Check from master is received before phase 1 is completed - race */ + /* PAIR_DHKEY_CHCK */, {SMP_PROCESS_DHKEY_CHECK, SMP_SM_NO_ACTION, SMP_STATE_WAIT_APP_RSP}, + /* user confirms NC 'OK', i.e. phase 1 is completed */ + /* SC_NC_OK */ {SMP_MOVE_TO_SEC_CONN_PHASE2, SMP_SM_NO_ACTION, SMP_STATE_SEC_CONN_PHS2_START}, + /* user-provided passkey is rcvd */ + /* SC_KEY_READY */ {SMP_START_PASSKEY_VERIFICATION, SMP_SM_NO_ACTION, SMP_STATE_SEC_CONN_PHS1_START}, + /* PAIR_COMMITM */ {SMP_PROCESS_PAIRING_COMMITMENT, SMP_SM_NO_ACTION, SMP_STATE_WAIT_APP_RSP}, + /* PAIR_KEYPR_NOTIF */ {SMP_PROCESS_KEYPRESS_NOTIFICATION, SMP_SEND_APP_CBACK, SMP_STATE_WAIT_APP_RSP}, + /* KEYPR_NOTIF */ {SMP_SEND_KEYPRESS_NOTIFICATION, SMP_SM_NO_ACTION, SMP_STATE_WAIT_APP_RSP}, + /* SC_OOB_DATA */ {SMP_SEND_PAIR_RSP, SMP_SM_NO_ACTION, SMP_STATE_PAIR_REQ_RSP}, +}; + +static const UINT8 smp_slave_sec_request_table[][SMP_SM_NUM_COLS] = { + /* Event Action Next State */ + /* PAIR_REQ */{SMP_PROC_PAIR_CMD, SMP_SM_NO_ACTION, SMP_STATE_PAIR_REQ_RSP}, + /* ENCRYPTED*/{SMP_ENC_CMPL, SMP_SM_NO_ACTION, SMP_STATE_PAIR_REQ_RSP}, +}; + +static const UINT8 smp_slave_pair_request_response_table[][SMP_SM_NUM_COLS] = { + /* Event Action Next State */ + /* CONFIRM */ {SMP_PROC_CONFIRM, SMP_SM_NO_ACTION, SMP_STATE_CONFIRM}, + /* TK_REQ */ {SMP_SEND_APP_CBACK, SMP_SM_NO_ACTION, SMP_STATE_WAIT_APP_RSP}, + /* TK/Confirm ready */ + /* KEY_READY */{SMP_PROC_SL_KEY, SMP_SM_NO_ACTION, SMP_STATE_PAIR_REQ_RSP} + /* PUBL_KEY_EXCH_REQ */, { SMP_CREATE_PRIVATE_KEY, SMP_SM_NO_ACTION, SMP_STATE_PUBLIC_KEY_EXCH}, + /* PAIR_PUBLIC_KEY */ { SMP_PROCESS_PAIR_PUBLIC_KEY, SMP_SM_NO_ACTION, SMP_STATE_PAIR_REQ_RSP}, +}; + +static const UINT8 smp_slave_wait_confirm_table[][SMP_SM_NUM_COLS] = { + /* Event Action Next State */ + /* CONFIRM */ {SMP_PROC_CONFIRM, SMP_SEND_CONFIRM, SMP_STATE_CONFIRM}, + /* KEY_READY*/ {SMP_PROC_SL_KEY, SMP_SM_NO_ACTION, SMP_STATE_WAIT_CONFIRM} +}; + +static const UINT8 smp_slave_confirm_table[][SMP_SM_NUM_COLS] = { + /* Event Action Next State */ + /* RAND */ {SMP_PROC_RAND, SMP_GENERATE_COMPARE, SMP_STATE_RAND}, + + /* TK/Confirm ready */ + /* KEY_READY*/ {SMP_PROC_SL_KEY, SMP_SM_NO_ACTION, SMP_STATE_CONFIRM} +}; + +static const UINT8 smp_slave_rand_table[][SMP_SM_NUM_COLS] = { + /* Event Action Next State */ + /* KEY_READY */ {SMP_PROC_COMPARE, SMP_SM_NO_ACTION, SMP_STATE_RAND}, /* compare match */ + /* RAND */ {SMP_SEND_RAND, SMP_SM_NO_ACTION, SMP_STATE_ENCRYPTION_PENDING} +}; + +static const UINT8 smp_slave_public_key_exch_table[][SMP_SM_NUM_COLS] = { + /* Event Action Next State */ + /* LOC_PUBL_KEY_CRTD */{ SMP_WAIT_FOR_BOTH_PUBLIC_KEYS, SMP_SM_NO_ACTION, SMP_STATE_PUBLIC_KEY_EXCH}, + /* PAIR_PUBLIC_KEY */{ SMP_PROCESS_PAIR_PUBLIC_KEY, SMP_SM_NO_ACTION, SMP_STATE_PUBLIC_KEY_EXCH}, + /* BOTH_PUBL_KEYS_RCVD */{ SMP_HAVE_BOTH_PUBLIC_KEYS, SMP_SM_NO_ACTION, SMP_STATE_SEC_CONN_PHS1_START}, +}; + +static const UINT8 smp_slave_sec_conn_phs1_start_table[][SMP_SM_NUM_COLS] = { + /* Event Action Next State */ + /* SC_DHKEY_CMPLT */{ SMP_START_SEC_CONN_PHASE1, SMP_SM_NO_ACTION, SMP_STATE_SEC_CONN_PHS1_START}, + /* HAVE_LOC_NONCE */{ SMP_PROCESS_LOCAL_NONCE, SMP_SM_NO_ACTION, SMP_STATE_WAIT_COMMITMENT}, + /* TK_REQ */{ SMP_SEND_APP_CBACK, SMP_SM_NO_ACTION, SMP_STATE_WAIT_APP_RSP}, + /* SMP_MODEL_SEC_CONN_PASSKEY_DISP model, passkey is sent up to display, it's time to start */ + /* commitment calculation */ + /* KEY_READY */{ SMP_START_PASSKEY_VERIFICATION, SMP_SM_NO_ACTION, SMP_STATE_SEC_CONN_PHS1_START}, + /* PAIR_KEYPR_NOTIF */{ SMP_PROCESS_KEYPRESS_NOTIFICATION, SMP_SEND_APP_CBACK, SMP_STATE_SEC_CONN_PHS1_START}, + /*COMMIT*/{SMP_PROCESS_PAIRING_COMMITMENT, SMP_SM_NO_ACTION, SMP_STATE_SEC_CONN_PHS1_START}, +}; + +static const UINT8 smp_slave_wait_commitment_table[][SMP_SM_NUM_COLS] = { + /* Event Action Next State */ + /* PAIR_COMMITM */{SMP_PROCESS_PAIRING_COMMITMENT, SMP_SEND_COMMITMENT, SMP_STATE_WAIT_NONCE}, + /* PAIR_KEYPR_NOTIF */{SMP_PROCESS_KEYPRESS_NOTIFICATION, SMP_SEND_APP_CBACK, SMP_STATE_WAIT_COMMITMENT}, +}; + +static const UINT8 smp_slave_wait_nonce_table[][SMP_SM_NUM_COLS] = { + /* Event Action Next State */ + /* peer nonce is received */ + /* RAND */{SMP_PROC_RAND, SMP_PROCESS_PEER_NONCE, SMP_STATE_SEC_CONN_PHS2_START}, + /* NC model, time to calculate number for NC */ + /* SC_CALC_NC */{SMP_CALCULATE_NUMERIC_COMPARISON_DISPLAY_NUMBER, SMP_SM_NO_ACTION, SMP_STATE_WAIT_NONCE}, + /* NC model, time to display calculated number for NC to the user */ + /* SC_DSPL_NC */{SMP_SEND_APP_CBACK, SMP_SM_NO_ACTION, SMP_STATE_WAIT_APP_RSP}, +}; + +static const UINT8 smp_slave_sec_conn_phs2_start_table[][SMP_SM_NUM_COLS] = { + /* Event Action Next State */ + /* SC_PHASE1_CMPLT */{SMP_CALCULATE_LOCAL_DHKEY_CHECK, SMP_PH2_DHKEY_CHECKS_ARE_PRESENT, SMP_STATE_WAIT_DHK_CHECK}, + /* DHKey Check from master is received before slave DHKey calculation is completed - race */ + /* PAIR_DHKEY_CHCK */{SMP_PROCESS_DHKEY_CHECK, SMP_SM_NO_ACTION, SMP_STATE_SEC_CONN_PHS2_START}, +}; + +static const UINT8 smp_slave_wait_dhk_check_table[][SMP_SM_NUM_COLS] = { + /* Event Action Next State */ + /* PAIR_DHKEY_CHCK */{SMP_PROCESS_DHKEY_CHECK, SMP_CALCULATE_PEER_DHKEY_CHECK, SMP_STATE_DHK_CHECK}, + /* DHKey Check from master was received before slave came to this state */ + /* SC_2_DHCK_CHKS_PRES */{SMP_CALCULATE_PEER_DHKEY_CHECK, SMP_SM_NO_ACTION, SMP_STATE_DHK_CHECK}, +}; + +static const UINT8 smp_slave_dhk_check_table[][SMP_SM_NUM_COLS] = { + /* Event Action Next State */ + + /* locally calculated peer dhkey check is ready -> compare it withs DHKey Check */ + /* actually received from peer */ + /* SC_KEY_READY */{SMP_MATCH_DHKEY_CHECKS, SMP_SM_NO_ACTION, SMP_STATE_DHK_CHECK}, + + /* dhkey checks match -> send local dhkey check to master, go to wait for HCI LE */ + /* Long Term Key Request Event */ + /* PAIR_DHKEY_CHCK */{SMP_SEND_DHKEY_CHECK, SMP_SM_NO_ACTION, SMP_STATE_ENCRYPTION_PENDING}, +}; + +static const UINT8 smp_slave_enc_pending_table[][SMP_SM_NUM_COLS] = { + /* Event Action Next State */ + /* ENC_REQ */ {SMP_GENERATE_STK, SMP_SM_NO_ACTION, SMP_STATE_ENCRYPTION_PENDING}, + + /* STK ready */ + /* KEY_READY */ {SMP_SEND_LTK_REPLY, SMP_SM_NO_ACTION, SMP_STATE_ENCRYPTION_PENDING}, + /* ENCRYPTED */ {SMP_CHECK_AUTH_REQ, SMP_SM_NO_ACTION, SMP_STATE_ENCRYPTION_PENDING}, + /* BOND_REQ */ {SMP_KEY_DISTRIBUTE, SMP_SM_NO_ACTION, SMP_STATE_BOND_PENDING} +}; +static const UINT8 smp_slave_bond_pending_table[][SMP_SM_NUM_COLS] = { + /* Event Action Next State */ + + /* LTK ready */ + /* KEY_READY */{ SMP_SEND_ENC_INFO, SMP_SM_NO_ACTION, SMP_STATE_BOND_PENDING}, + + /* rev SRK */ + /* SIGN_INFO */{ SMP_PROC_SRK_INFO, SMP_SM_NO_ACTION, SMP_STATE_BOND_PENDING}, + /* ENC_INFO */ { SMP_PROC_ENC_INFO, SMP_SM_NO_ACTION, SMP_STATE_BOND_PENDING}, + /* ID_INFO */ { SMP_PROC_ID_INFO, SMP_SM_NO_ACTION, SMP_STATE_BOND_PENDING}, + /* MASTER_ID*/ { SMP_PROC_MASTER_ID, SMP_SM_NO_ACTION, SMP_STATE_BOND_PENDING}, + /* ID_ADDR */ { SMP_PROC_ID_ADDR, SMP_SM_NO_ACTION, SMP_STATE_BOND_PENDING} + +}; + +static const UINT8 smp_slave_create_local_sec_conn_oob_data[][SMP_SM_NUM_COLS] = { + /* Event Action Next State */ + /* LOC_PUBL_KEY_CRTD */ {SMP_SET_LOCAL_OOB_KEYS, SMP_SM_NO_ACTION, SMP_STATE_CREATE_LOCAL_SEC_CONN_OOB_DATA}, + /* HAVE_LOC_NONCE */ {SMP_SET_LOCAL_OOB_RAND_COMMITMENT, SMP_SM_NO_ACTION, SMP_STATE_IDLE} +}; + +static const tSMP_SM_TBL smp_state_table[][2] = { + /* SMP_STATE_IDLE */ + {smp_master_idle_table, smp_slave_idle_table}, + + /* SMP_STATE_WAIT_APP_RSP */ + {smp_master_wait_for_app_response_table, smp_slave_wait_for_app_response_table}, + + /* SMP_STATE_SEC_REQ_PENDING */ + {NULL, smp_slave_sec_request_table}, + + /* SMP_STATE_PAIR_REQ_RSP */ + {smp_master_pair_request_response_table, smp_slave_pair_request_response_table}, + + /* SMP_STATE_WAIT_CONFIRM */ + {smp_master_wait_for_confirm_table, smp_slave_wait_confirm_table}, + + /* SMP_STATE_CONFIRM */ + {smp_master_confirm_table, smp_slave_confirm_table}, + + /* SMP_STATE_RAND */ + {smp_master_rand_table, smp_slave_rand_table}, + + /* SMP_STATE_PUBLIC_KEY_EXCH */ + {smp_master_public_key_exchange_table, smp_slave_public_key_exch_table}, + + /* SMP_STATE_SEC_CONN_PHS1_START */ + {smp_master_sec_conn_phs1_start_table, smp_slave_sec_conn_phs1_start_table}, + + /* SMP_STATE_WAIT_COMMITMENT */ + {smp_master_wait_commitment_table, smp_slave_wait_commitment_table}, + + /* SMP_STATE_WAIT_NONCE */ + {smp_master_wait_nonce_table, smp_slave_wait_nonce_table}, + + /* SMP_STATE_SEC_CONN_PHS2_START */ + {smp_master_sec_conn_phs2_start_table, smp_slave_sec_conn_phs2_start_table}, + + /* SMP_STATE_WAIT_DHK_CHECK */ + {smp_master_wait_dhk_check_table, smp_slave_wait_dhk_check_table}, + + /* SMP_STATE_DHK_CHECK */ + {smp_master_dhk_check_table, smp_slave_dhk_check_table}, + + /* SMP_STATE_ENCRYPTION_PENDING */ + {smp_master_enc_pending_table, smp_slave_enc_pending_table}, + + /* SMP_STATE_BOND_PENDING */ + {smp_master_bond_pending_table, smp_slave_bond_pending_table}, + + /* SMP_STATE_CREATE_LOCAL_SEC_CONN_OOB_DATA */ + {smp_master_create_local_sec_conn_oob_data, smp_slave_create_local_sec_conn_oob_data} +}; + +typedef const UINT8 (*tSMP_ENTRY_TBL)[SMP_STATE_MAX]; +static const tSMP_ENTRY_TBL smp_entry_table[] = { + smp_master_entry_map, + smp_slave_entry_map +}; + +#if SMP_DYNAMIC_MEMORY == FALSE +tSMP_CB smp_cb; +#else +tSMP_CB *smp_cb_ptr; +#endif +#define SMP_ALL_TBL_MASK 0x80 + +/******************************************************************************* +** Function smp_set_state +** Returns None +*******************************************************************************/ +void smp_set_state(tSMP_STATE state) +{ + if (state < SMP_STATE_MAX) { + SMP_TRACE_DEBUG( "State change: %s(%d) ==> %s(%d)", + smp_get_state_name(smp_cb.state), smp_cb.state, + smp_get_state_name(state), state ); + smp_cb.state = state; + } else { + SMP_TRACE_DEBUG("smp_set_state invalid state =%d", state ); + } +} + +/******************************************************************************* +** Function smp_get_state +** Returns The smp state +*******************************************************************************/ +tSMP_STATE smp_get_state(void) +{ + return smp_cb.state; +} + +/******************************************************************************* +** +** Function smp_sm_event +** +** Description Handle events to the state machine. It looks up the entry +** in the smp_entry_table array. +** If it is a valid entry, it gets the state table.Set the next state, +** if not NULL state.Execute the action function according to the +** state table. If the state returned by action function is not NULL +** state, adjust the new state to the returned state.If (api_evt != MAX), +** call callback function. +** +** Returns void. +** +*******************************************************************************/ +void smp_sm_event(tSMP_CB *p_cb, tSMP_EVENT event, void *p_data) +{ + UINT8 curr_state = p_cb->state; + tSMP_SM_TBL state_table; + UINT8 action, entry, i; + tSMP_ENTRY_TBL entry_table = smp_entry_table[p_cb->role]; + + SMP_TRACE_EVENT("main smp_sm_event\n"); + if (curr_state >= SMP_STATE_MAX) { + SMP_TRACE_DEBUG( "Invalid state: %d\n", curr_state) ; + return; + } + + SMP_TRACE_DEBUG( "SMP Role: %s State: [%s (%d)], Event: [%s (%d)]", \ + (p_cb->role == 0x01) ? "Slave" : "Master\n", smp_get_state_name( p_cb->state), + p_cb->state, smp_get_event_name(event), event) ; + + /* look up the state table for the current state */ + /* lookup entry /w event & curr_state */ + /* If entry is ignore, return. + * Otherwise, get state table (according to curr_state or all_state) */ + if ((event <= SMP_MAX_EVT) && ( (entry = entry_table[event - 1][curr_state]) != SMP_SM_IGNORE )) { + if (entry & SMP_ALL_TBL_MASK) { + entry &= ~SMP_ALL_TBL_MASK; + state_table = smp_all_table; + } else { + state_table = smp_state_table[curr_state][p_cb->role ? 1 : 0]; + } + } else { + SMP_TRACE_DEBUG( "Ignore event [%s (%d)] in state [%s (%d)]\n", + smp_get_event_name(event), event, smp_get_state_name(curr_state), + curr_state); + return; + } + + /* Get possible next state from state table. */ + + smp_set_state(state_table[entry - 1][SMP_SME_NEXT_STATE]); + + /* If action is not ignore, clear param, exec action and get next state. + * The action function may set the Param for cback. + * Depending on param, call cback or free buffer. */ + /* execute action */ + /* execute action functions */ + for (i = 0; i < SMP_NUM_ACTIONS; i++) { + if ((action = state_table[entry - 1][i]) != SMP_SM_NO_ACTION && smp_sm_action[action] != NULL) { + (*smp_sm_action[action])(p_cb, (tSMP_INT_DATA *)p_data); + } else { + break; + } + } + SMP_TRACE_DEBUG( "result state = %s\n", smp_get_state_name( p_cb->state ) ) ; +} + +/******************************************************************************* +** Function smp_get_state_name +** Returns The smp state name. +*******************************************************************************/ +const char *smp_get_state_name(tSMP_STATE state) +{ + const char *p_str = smp_state_name[SMP_STATE_MAX]; + + if (state < SMP_STATE_MAX) { + p_str = smp_state_name[state]; + } + return p_str; +} + +/******************************************************************************* +** Function smp_get_event_name +** Returns The smp event name. +*******************************************************************************/ +const char *smp_get_event_name(tSMP_EVENT event) +{ + const char *p_str = smp_event_name[SMP_MAX_EVT]; + + if (event <= SMP_MAX_EVT) { + p_str = smp_event_name[event - 1]; + } + return p_str; +} + +#endif diff --git a/lib/bt/host/bluedroid/stack/smp/smp_utils.c b/lib/bt/host/bluedroid/stack/smp/smp_utils.c new file mode 100644 index 00000000..2e9fac77 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/smp/smp_utils.c @@ -0,0 +1,1645 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +******************************************************************************/ + +/****************************************************************************** + * + * This file contains functions for the SMP L2CAP utility functions + * + ******************************************************************************/ +#include "common/bt_target.h" + +#if SMP_INCLUDED == TRUE + +#include "stack/bt_types.h" +//#include "bt_utils.h" +#include +//#include +#include "stack/hcidefs.h" +#include "stack/btm_ble_api.h" +#include "stack/l2c_api.h" +#include "l2c_int.h" +#include "smp_int.h" +#include "device/controller.h" +#include "btm_int.h" +#include "common/bte_appl.h" + +#define SMP_PAIRING_REQ_SIZE 7 +#define SMP_CONFIRM_CMD_SIZE (BT_OCTET16_LEN + 1) +#define SMP_RAND_CMD_SIZE (BT_OCTET16_LEN + 1) +#define SMP_INIT_CMD_SIZE (BT_OCTET16_LEN + 1) +#define SMP_ENC_INFO_SIZE (BT_OCTET16_LEN + 1) +#define SMP_MASTER_ID_SIZE (BT_OCTET8_LEN + 2 + 1) +#define SMP_ID_INFO_SIZE (BT_OCTET16_LEN + 1) +#define SMP_ID_ADDR_SIZE (BD_ADDR_LEN + 1 + 1) +#define SMP_SIGN_INFO_SIZE (BT_OCTET16_LEN + 1) +#define SMP_PAIR_FAIL_SIZE 2 +#define SMP_SECURITY_REQUEST_SIZE 2 +#define SMP_PAIR_PUBL_KEY_SIZE (1 /* opcode */ + (2*BT_OCTET32_LEN)) +#define SMP_PAIR_COMMITM_SIZE (1 /* opcode */ + BT_OCTET16_LEN /*Commitment*/) +#define SMP_PAIR_DHKEY_CHECK_SIZE (1 /* opcode */ + BT_OCTET16_LEN /*DHKey Check*/) +#define SMP_PAIR_KEYPR_NOTIF_SIZE (1 /* opcode */ + 1 /*Notif Type*/) + +/* SMP command sizes per spec */ +static const UINT8 smp_cmd_size_per_spec[] = { + 0, + SMP_PAIRING_REQ_SIZE, /* 0x01: pairing request */ + SMP_PAIRING_REQ_SIZE, /* 0x02: pairing response */ + SMP_CONFIRM_CMD_SIZE, /* 0x03: pairing confirm */ + SMP_RAND_CMD_SIZE, /* 0x04: pairing random */ + SMP_PAIR_FAIL_SIZE, /* 0x05: pairing failed */ + SMP_ENC_INFO_SIZE, /* 0x06: encryption information */ + SMP_MASTER_ID_SIZE, /* 0x07: master identification */ + SMP_ID_INFO_SIZE, /* 0x08: identity information */ + SMP_ID_ADDR_SIZE, /* 0x09: identity address information */ + SMP_SIGN_INFO_SIZE, /* 0x0A: signing information */ + SMP_SECURITY_REQUEST_SIZE, /* 0x0B: security request */ + SMP_PAIR_PUBL_KEY_SIZE, /* 0x0C: pairing public key */ + SMP_PAIR_DHKEY_CHECK_SIZE, /* 0x0D: pairing dhkey check */ + SMP_PAIR_KEYPR_NOTIF_SIZE, /* 0x0E: pairing keypress notification */ + SMP_PAIR_COMMITM_SIZE /* 0x0F: pairing commitment */ +}; + +static BOOLEAN smp_parameter_unconditionally_valid(tSMP_CB *p_cb); +static BOOLEAN smp_parameter_unconditionally_invalid(tSMP_CB *p_cb); + +/* type for SMP command length validation functions */ +typedef BOOLEAN (*tSMP_CMD_LEN_VALID)(tSMP_CB *p_cb); + +static BOOLEAN smp_command_has_valid_fixed_length(tSMP_CB *p_cb); + +static const tSMP_CMD_LEN_VALID smp_cmd_len_is_valid[] = { + smp_parameter_unconditionally_invalid, + smp_command_has_valid_fixed_length, /* 0x01: pairing request */ + smp_command_has_valid_fixed_length, /* 0x02: pairing response */ + smp_command_has_valid_fixed_length, /* 0x03: pairing confirm */ + smp_command_has_valid_fixed_length, /* 0x04: pairing random */ + smp_command_has_valid_fixed_length, /* 0x05: pairing failed */ + smp_command_has_valid_fixed_length, /* 0x06: encryption information */ + smp_command_has_valid_fixed_length, /* 0x07: master identification */ + smp_command_has_valid_fixed_length, /* 0x08: identity information */ + smp_command_has_valid_fixed_length, /* 0x09: identity address information */ + smp_command_has_valid_fixed_length, /* 0x0A: signing information */ + smp_command_has_valid_fixed_length, /* 0x0B: security request */ + smp_command_has_valid_fixed_length, /* 0x0C: pairing public key */ + smp_command_has_valid_fixed_length, /* 0x0D: pairing dhkey check */ + smp_command_has_valid_fixed_length, /* 0x0E: pairing keypress notification */ + smp_command_has_valid_fixed_length /* 0x0F: pairing commitment */ +}; + +/* type for SMP command parameter ranges validation functions */ +typedef BOOLEAN (*tSMP_CMD_PARAM_RANGES_VALID)(tSMP_CB *p_cb); + +static BOOLEAN smp_pairing_request_response_parameters_are_valid(tSMP_CB *p_cb); +static BOOLEAN smp_pairing_keypress_notification_is_valid(tSMP_CB *p_cb); + +static const tSMP_CMD_PARAM_RANGES_VALID smp_cmd_param_ranges_are_valid[] = { + smp_parameter_unconditionally_invalid, + smp_pairing_request_response_parameters_are_valid, /* 0x01: pairing request */ + smp_pairing_request_response_parameters_are_valid, /* 0x02: pairing response */ + smp_parameter_unconditionally_valid, /* 0x03: pairing confirm */ + smp_parameter_unconditionally_valid, /* 0x04: pairing random */ + smp_parameter_unconditionally_valid, /* 0x05: pairing failed */ + smp_parameter_unconditionally_valid, /* 0x06: encryption information */ + smp_parameter_unconditionally_valid, /* 0x07: master identification */ + smp_parameter_unconditionally_valid, /* 0x08: identity information */ + smp_parameter_unconditionally_valid, /* 0x09: identity address information */ + smp_parameter_unconditionally_valid, /* 0x0A: signing information */ + smp_parameter_unconditionally_valid, /* 0x0B: security request */ + smp_parameter_unconditionally_valid, /* 0x0C: pairing public key */ + smp_parameter_unconditionally_valid, /* 0x0D: pairing dhkey check */ + smp_pairing_keypress_notification_is_valid, /* 0x0E: pairing keypress notification */ + smp_parameter_unconditionally_valid /* 0x0F: pairing commitment */ +}; + +/* type for action functions */ +typedef BT_HDR *(*tSMP_CMD_ACT)(UINT8 cmd_code, tSMP_CB *p_cb); + +static BT_HDR *smp_build_pairing_cmd(UINT8 cmd_code, tSMP_CB *p_cb); +static BT_HDR *smp_build_confirm_cmd(UINT8 cmd_code, tSMP_CB *p_cb); +static BT_HDR *smp_build_rand_cmd(UINT8 cmd_code, tSMP_CB *p_cb); +static BT_HDR *smp_build_pairing_fail(UINT8 cmd_code, tSMP_CB *p_cb); +static BT_HDR *smp_build_identity_info_cmd(UINT8 cmd_code, tSMP_CB *p_cb); +static BT_HDR *smp_build_encrypt_info_cmd(UINT8 cmd_code, tSMP_CB *p_cb); +static BT_HDR *smp_build_security_request(UINT8 cmd_code, tSMP_CB *p_cb); +static BT_HDR *smp_build_signing_info_cmd(UINT8 cmd_code, tSMP_CB *p_cb); +static BT_HDR *smp_build_master_id_cmd(UINT8 cmd_code, tSMP_CB *p_cb); +static BT_HDR *smp_build_id_addr_cmd(UINT8 cmd_code, tSMP_CB *p_cb); +static BT_HDR *smp_build_pair_public_key_cmd(UINT8 cmd_code, tSMP_CB *p_cb); +static BT_HDR *smp_build_pairing_commitment_cmd(UINT8 cmd_code, tSMP_CB *p_cb); +static BT_HDR *smp_build_pair_dhkey_check_cmd(UINT8 cmd_code, tSMP_CB *p_cb); +static BT_HDR *smp_build_pairing_keypress_notification_cmd(UINT8 cmd_code, tSMP_CB *p_cb); + +static const tSMP_CMD_ACT smp_cmd_build_act[] = { + NULL, + smp_build_pairing_cmd, /* 0x01: pairing request */ + smp_build_pairing_cmd, /* 0x02: pairing response */ + smp_build_confirm_cmd, /* 0x03: pairing confirm */ + smp_build_rand_cmd, /* 0x04: pairing random */ + smp_build_pairing_fail, /* 0x05: pairing failure */ + smp_build_encrypt_info_cmd, /* 0x06: encryption information */ + smp_build_master_id_cmd, /* 0x07: master identification */ + smp_build_identity_info_cmd, /* 0x08: identity information */ + smp_build_id_addr_cmd, /* 0x09: identity address information */ + smp_build_signing_info_cmd, /* 0x0A: signing information */ + smp_build_security_request, /* 0x0B: security request */ + smp_build_pair_public_key_cmd, /* 0x0C: pairing public key */ + smp_build_pair_dhkey_check_cmd, /* 0x0D: pairing DHKey check */ + smp_build_pairing_keypress_notification_cmd, /* 0x0E: pairing keypress notification */ + smp_build_pairing_commitment_cmd /* 0x0F: pairing commitment */ +}; + +static const UINT8 smp_association_table[2][SMP_IO_CAP_MAX][SMP_IO_CAP_MAX] = { + /* display only */ /* Display Yes/No */ /* keyboard only */ + /* No Input/Output */ /* keyboard display */ + + /* initiator */ + /* model = tbl[peer_io_caps][loc_io_caps] */ + /* Display Only */ + { { + SMP_MODEL_ENCRYPTION_ONLY, SMP_MODEL_ENCRYPTION_ONLY, SMP_MODEL_PASSKEY, + SMP_MODEL_ENCRYPTION_ONLY, SMP_MODEL_PASSKEY + }, + + /* Display Yes/No */ + { + SMP_MODEL_ENCRYPTION_ONLY, SMP_MODEL_ENCRYPTION_ONLY, SMP_MODEL_PASSKEY, + SMP_MODEL_ENCRYPTION_ONLY, SMP_MODEL_PASSKEY + }, + + /* Keyboard only */ + { + SMP_MODEL_KEY_NOTIF, SMP_MODEL_KEY_NOTIF, SMP_MODEL_PASSKEY, + SMP_MODEL_ENCRYPTION_ONLY, SMP_MODEL_KEY_NOTIF + }, + + /* No Input No Output */ + { + SMP_MODEL_ENCRYPTION_ONLY, SMP_MODEL_ENCRYPTION_ONLY, SMP_MODEL_ENCRYPTION_ONLY, + SMP_MODEL_ENCRYPTION_ONLY, SMP_MODEL_ENCRYPTION_ONLY + }, + + /* keyboard display */ + { + SMP_MODEL_KEY_NOTIF, SMP_MODEL_KEY_NOTIF, SMP_MODEL_PASSKEY, + SMP_MODEL_ENCRYPTION_ONLY, SMP_MODEL_KEY_NOTIF + } + }, + + /* responder */ + /* model = tbl[loc_io_caps][peer_io_caps] */ + /* Display Only */ + { { + SMP_MODEL_ENCRYPTION_ONLY, SMP_MODEL_ENCRYPTION_ONLY, SMP_MODEL_KEY_NOTIF, + SMP_MODEL_ENCRYPTION_ONLY, SMP_MODEL_KEY_NOTIF + }, + + /* Display Yes/No */ + { + SMP_MODEL_ENCRYPTION_ONLY, SMP_MODEL_ENCRYPTION_ONLY, SMP_MODEL_KEY_NOTIF, + SMP_MODEL_ENCRYPTION_ONLY, SMP_MODEL_KEY_NOTIF + }, + + /* keyboard only */ + { + SMP_MODEL_PASSKEY, SMP_MODEL_PASSKEY, SMP_MODEL_PASSKEY, + SMP_MODEL_ENCRYPTION_ONLY, SMP_MODEL_PASSKEY + }, + + /* No Input No Output */ + { + SMP_MODEL_ENCRYPTION_ONLY, SMP_MODEL_ENCRYPTION_ONLY, SMP_MODEL_ENCRYPTION_ONLY, + SMP_MODEL_ENCRYPTION_ONLY, SMP_MODEL_ENCRYPTION_ONLY + }, + + /* keyboard display */ + { + SMP_MODEL_PASSKEY, SMP_MODEL_PASSKEY, SMP_MODEL_KEY_NOTIF, + SMP_MODEL_ENCRYPTION_ONLY, SMP_MODEL_PASSKEY + } + } +}; + +static const UINT8 smp_association_table_sc[2][SMP_IO_CAP_MAX][SMP_IO_CAP_MAX] = { + /* display only */ /* Display Yes/No */ /* keyboard only */ + /* No InputOutput */ /* keyboard display */ + + /* initiator */ + /* model = tbl[peer_io_caps][loc_io_caps] */ + + /* Display Only */ + { { + SMP_MODEL_SEC_CONN_JUSTWORKS, SMP_MODEL_SEC_CONN_JUSTWORKS, SMP_MODEL_SEC_CONN_PASSKEY_ENT, + SMP_MODEL_SEC_CONN_JUSTWORKS, SMP_MODEL_SEC_CONN_PASSKEY_ENT + }, + + /* Display Yes/No */ + { + SMP_MODEL_SEC_CONN_JUSTWORKS, SMP_MODEL_SEC_CONN_NUM_COMP, SMP_MODEL_SEC_CONN_PASSKEY_ENT, + SMP_MODEL_SEC_CONN_JUSTWORKS, SMP_MODEL_SEC_CONN_NUM_COMP + }, + + /* keyboard only */ + { + SMP_MODEL_SEC_CONN_PASSKEY_DISP, SMP_MODEL_SEC_CONN_PASSKEY_DISP, SMP_MODEL_SEC_CONN_PASSKEY_ENT, + SMP_MODEL_SEC_CONN_JUSTWORKS, SMP_MODEL_SEC_CONN_PASSKEY_DISP + }, + + /* No Input No Output */ + { + SMP_MODEL_SEC_CONN_JUSTWORKS, SMP_MODEL_SEC_CONN_JUSTWORKS, SMP_MODEL_SEC_CONN_JUSTWORKS, + SMP_MODEL_SEC_CONN_JUSTWORKS, SMP_MODEL_SEC_CONN_JUSTWORKS + }, + + /* keyboard display */ + { + SMP_MODEL_SEC_CONN_PASSKEY_DISP, SMP_MODEL_SEC_CONN_NUM_COMP, SMP_MODEL_SEC_CONN_PASSKEY_ENT, + SMP_MODEL_SEC_CONN_JUSTWORKS, SMP_MODEL_SEC_CONN_NUM_COMP + } + }, + + /* responder */ + /* model = tbl[loc_io_caps][peer_io_caps] */ + + /* Display Only */ + { { + SMP_MODEL_SEC_CONN_JUSTWORKS, SMP_MODEL_SEC_CONN_JUSTWORKS, SMP_MODEL_SEC_CONN_PASSKEY_DISP, + SMP_MODEL_SEC_CONN_JUSTWORKS, SMP_MODEL_SEC_CONN_PASSKEY_DISP + }, + + /* Display Yes/No */ + { + SMP_MODEL_SEC_CONN_JUSTWORKS, SMP_MODEL_SEC_CONN_NUM_COMP, SMP_MODEL_SEC_CONN_PASSKEY_DISP, + SMP_MODEL_SEC_CONN_JUSTWORKS, SMP_MODEL_SEC_CONN_NUM_COMP + }, + + /* keyboard only */ + { + SMP_MODEL_SEC_CONN_PASSKEY_ENT, SMP_MODEL_SEC_CONN_PASSKEY_ENT, SMP_MODEL_SEC_CONN_PASSKEY_ENT, + SMP_MODEL_SEC_CONN_JUSTWORKS, SMP_MODEL_SEC_CONN_PASSKEY_ENT + }, + + /* No Input No Output */ + { + SMP_MODEL_SEC_CONN_JUSTWORKS, SMP_MODEL_SEC_CONN_JUSTWORKS, SMP_MODEL_SEC_CONN_JUSTWORKS, + SMP_MODEL_SEC_CONN_JUSTWORKS, SMP_MODEL_SEC_CONN_JUSTWORKS + }, + + /* keyboard display */ + { + SMP_MODEL_SEC_CONN_PASSKEY_ENT, SMP_MODEL_SEC_CONN_NUM_COMP, SMP_MODEL_SEC_CONN_PASSKEY_DISP, + SMP_MODEL_SEC_CONN_JUSTWORKS, SMP_MODEL_SEC_CONN_NUM_COMP + } + } +}; + +static tSMP_ASSO_MODEL smp_select_legacy_association_model(tSMP_CB *p_cb); +static tSMP_ASSO_MODEL smp_select_association_model_secure_connections(tSMP_CB *p_cb); + +/******************************************************************************* +** +** Function smp_send_msg_to_L2CAP +** +** Description Send message to L2CAP. +** +*******************************************************************************/ +BOOLEAN smp_send_msg_to_L2CAP(BD_ADDR rem_bda, BT_HDR *p_toL2CAP) +{ + UINT16 l2cap_ret; + UINT16 fixed_cid = L2CAP_SMP_CID; + + if (smp_cb.smp_over_br) { + fixed_cid = L2CAP_SMP_BR_CID; + } + + SMP_TRACE_EVENT("%s", __FUNCTION__); + smp_cb.total_tx_unacked += 1; + + if ((l2cap_ret = L2CA_SendFixedChnlData (fixed_cid, rem_bda, p_toL2CAP)) == L2CAP_DW_FAILED) { + smp_cb.total_tx_unacked -= 1; + SMP_TRACE_ERROR("SMP failed to pass msg:0x%0x to L2CAP", + *((UINT8 *)(p_toL2CAP + 1) + p_toL2CAP->offset)); + return FALSE; + } else { + return TRUE; + } +} + +/******************************************************************************* +** +** Function smp_send_cmd +** +** Description send a SMP command on L2CAP channel. +** +*******************************************************************************/ +BOOLEAN smp_send_cmd(UINT8 cmd_code, tSMP_CB *p_cb) +{ + BT_HDR *p_buf; + BOOLEAN sent = FALSE; + UINT8 failure = SMP_PAIR_INTERNAL_ERR; + SMP_TRACE_EVENT("smp_send_cmd on l2cap cmd_code=0x%x\n", cmd_code); + if ( cmd_code <= (SMP_OPCODE_MAX + 1 /* for SMP_OPCODE_PAIR_COMMITM */) && + smp_cmd_build_act[cmd_code] != NULL) { + p_buf = (*smp_cmd_build_act[cmd_code])(cmd_code, p_cb); + + if (p_buf != NULL && + smp_send_msg_to_L2CAP(p_cb->pairing_bda, p_buf)) { + sent = TRUE; + + btu_stop_timer (&p_cb->rsp_timer_ent); + btu_start_timer (&p_cb->rsp_timer_ent, BTU_TTYPE_SMP_PAIRING_CMD, + SMP_WAIT_FOR_RSP_TOUT); + } + } + + if (!sent) { + if (p_cb->smp_over_br) { +#if (CLASSIC_BT_INCLUDED == TRUE) + smp_br_state_machine_event(p_cb, SMP_BR_AUTH_CMPL_EVT, &failure); +#endif ///CLASSIC_BT_INCLUDED == TRUE + } else { + smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &failure); + } + } + return sent; +} + +/******************************************************************************* +** +** Function smp_rsp_timeout +** +** Description Called when SMP wait for SMP command response timer expires +** +** Returns void +** +*******************************************************************************/ +void smp_rsp_timeout(TIMER_LIST_ENT *p_tle) +{ + tSMP_CB *p_cb = &smp_cb; + UINT8 failure = SMP_RSP_TIMEOUT; + UNUSED(p_tle); + + SMP_TRACE_EVENT("%s state:%d br_state:%d", __FUNCTION__, p_cb->state, p_cb->br_state); + + if (p_cb->smp_over_br) { +#if (CLASSIC_BT_INCLUDED == TRUE) + smp_br_state_machine_event(p_cb, SMP_BR_AUTH_CMPL_EVT, &failure); +#endif ///CLASSIC_BT_INCLUDED == TRUE + } else { + smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &failure); + } +} + +/******************************************************************************* +** +** Function smp_build_pairing_req_cmd +** +** Description Build pairing request command. +** +*******************************************************************************/ +BT_HDR *smp_build_pairing_cmd(UINT8 cmd_code, tSMP_CB *p_cb) +{ + BT_HDR *p_buf = NULL ; + UINT8 *p; + + SMP_TRACE_EVENT("smp_build_pairing_cmd"); + if ((p_buf = (BT_HDR *)osi_malloc(sizeof(BT_HDR) + SMP_PAIRING_REQ_SIZE + L2CAP_MIN_OFFSET)) != NULL) { + p = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET; + + UINT8_TO_STREAM (p, cmd_code); + UINT8_TO_STREAM (p, p_cb->local_io_capability); + UINT8_TO_STREAM (p, p_cb->loc_oob_flag); + UINT8_TO_STREAM (p, p_cb->loc_auth_req); + UINT8_TO_STREAM (p, p_cb->loc_enc_size); + UINT8_TO_STREAM (p, p_cb->local_i_key); + UINT8_TO_STREAM (p, p_cb->local_r_key); + + p_buf->offset = L2CAP_MIN_OFFSET; + /* 1B ERR_RSP op code + 1B cmd_op_code + 2B handle + 1B status */ + p_buf->len = SMP_PAIRING_REQ_SIZE; + } + + return p_buf; +} + +/******************************************************************************* +** +** Function smp_build_confirm_cmd +** +** Description Build confirm request command. +** +*******************************************************************************/ +static BT_HDR *smp_build_confirm_cmd(UINT8 cmd_code, tSMP_CB *p_cb) +{ + BT_HDR *p_buf = NULL ; + UINT8 *p; + UNUSED(cmd_code); + + SMP_TRACE_EVENT("smp_build_confirm_cmd\n"); + if ((p_buf = (BT_HDR *)osi_malloc(sizeof(BT_HDR) + SMP_CONFIRM_CMD_SIZE + L2CAP_MIN_OFFSET)) != NULL) { + p = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET; + + UINT8_TO_STREAM (p, SMP_OPCODE_CONFIRM); + ARRAY_TO_STREAM (p, p_cb->confirm, BT_OCTET16_LEN); + + p_buf->offset = L2CAP_MIN_OFFSET; + p_buf->len = SMP_CONFIRM_CMD_SIZE; + } + + return p_buf; +} +/******************************************************************************* +** +** Function smp_build_rand_cmd +** +** Description Build Random command. +** +*******************************************************************************/ +static BT_HDR *smp_build_rand_cmd(UINT8 cmd_code, tSMP_CB *p_cb) +{ + BT_HDR *p_buf = NULL ; + UINT8 *p; + UNUSED(cmd_code); + + SMP_TRACE_EVENT("%s\n", __func__); + if ((p_buf = (BT_HDR *)osi_malloc(sizeof(BT_HDR) + SMP_RAND_CMD_SIZE + L2CAP_MIN_OFFSET)) + != NULL) { + p = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET; + + UINT8_TO_STREAM (p, SMP_OPCODE_RAND); + ARRAY_TO_STREAM (p, p_cb->rand, BT_OCTET16_LEN); + + p_buf->offset = L2CAP_MIN_OFFSET; + p_buf->len = SMP_RAND_CMD_SIZE; + } + + return p_buf; +} +/******************************************************************************* +** +** Function smp_build_encrypt_info_cmd +** +** Description Build security information command. +** +*******************************************************************************/ +static BT_HDR *smp_build_encrypt_info_cmd(UINT8 cmd_code, tSMP_CB *p_cb) +{ + BT_HDR *p_buf = NULL ; + UINT8 *p; + UNUSED(cmd_code); + + SMP_TRACE_EVENT("smp_build_encrypt_info_cmd\n"); + if ((p_buf = (BT_HDR *)osi_malloc(sizeof(BT_HDR) + SMP_ENC_INFO_SIZE + L2CAP_MIN_OFFSET)) != NULL) { + p = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET; + + UINT8_TO_STREAM (p, SMP_OPCODE_ENCRYPT_INFO); + ARRAY_TO_STREAM (p, p_cb->ltk, BT_OCTET16_LEN); + + p_buf->offset = L2CAP_MIN_OFFSET; + p_buf->len = SMP_ENC_INFO_SIZE; + } + + return p_buf; +} + +/******************************************************************************* +** +** Function smp_build_master_id_cmd +** +** Description Build security information command. +** +*******************************************************************************/ +static BT_HDR *smp_build_master_id_cmd(UINT8 cmd_code, tSMP_CB *p_cb) +{ + BT_HDR *p_buf = NULL ; + UINT8 *p; + UNUSED(cmd_code); + + SMP_TRACE_EVENT("%s\n", __func__); + + if ((p_buf = (BT_HDR *)osi_malloc(sizeof(BT_HDR) + SMP_MASTER_ID_SIZE + L2CAP_MIN_OFFSET)) != NULL) { + p = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET; + + UINT8_TO_STREAM (p, SMP_OPCODE_MASTER_ID); + UINT16_TO_STREAM (p, p_cb->ediv); + ARRAY_TO_STREAM (p, p_cb->enc_rand, BT_OCTET8_LEN); + + p_buf->offset = L2CAP_MIN_OFFSET; + p_buf->len = SMP_MASTER_ID_SIZE; + } + + return p_buf; +} + +/******************************************************************************* +** +** Function smp_build_identity_info_cmd +** +** Description Build identity information command. +** +*******************************************************************************/ +static BT_HDR *smp_build_identity_info_cmd(UINT8 cmd_code, tSMP_CB *p_cb) +{ + BT_HDR *p_buf = NULL ; +#if (BLE_INCLUDED == TRUE) + UINT8 *p; + BT_OCTET16 irk; + UNUSED(cmd_code); + UNUSED(p_cb); + + SMP_TRACE_EVENT("smp_build_identity_info_cmd\n"); + if ((p_buf = (BT_HDR *)osi_malloc(sizeof(BT_HDR) + SMP_ID_INFO_SIZE + L2CAP_MIN_OFFSET)) != NULL) { + p = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET; + + BTM_GetDeviceIDRoot(irk); + + UINT8_TO_STREAM (p, SMP_OPCODE_IDENTITY_INFO); + ARRAY_TO_STREAM (p, irk, BT_OCTET16_LEN); + + p_buf->offset = L2CAP_MIN_OFFSET; + p_buf->len = SMP_ID_INFO_SIZE; + } + +#endif ///BLE_INCLUDED == TRUE + return p_buf; +} + +/******************************************************************************* +** +** Function smp_build_id_addr_cmd +** +** Description Build identity address information command. +** +*******************************************************************************/ +static BT_HDR *smp_build_id_addr_cmd(UINT8 cmd_code, tSMP_CB *p_cb) +{ + BT_HDR *p_buf = NULL; + UINT8 *p; + + UNUSED(cmd_code); + UNUSED(p_cb); + SMP_TRACE_EVENT("smp_build_id_addr_cmd\n"); + if ((p_buf = (BT_HDR *)osi_malloc(sizeof(BT_HDR) + SMP_ID_ADDR_SIZE + L2CAP_MIN_OFFSET)) != NULL) { + p = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET; + + UINT8_TO_STREAM (p, SMP_OPCODE_ID_ADDR); + /* Identity Address Information is used in the Transport Specific Key Distribution phase to distribute + its public device address or static random address. if slave using static random address is encrypted, + it should distribute its static random address */ +#if (BLE_INCLUDED == TRUE) + if(btm_cb.ble_ctr_cb.addr_mgnt_cb.own_addr_type == BLE_ADDR_RANDOM && memcmp(btm_cb.ble_ctr_cb.addr_mgnt_cb.static_rand_addr, btm_cb.ble_ctr_cb.addr_mgnt_cb.private_addr,6) == 0) { + UINT8_TO_STREAM (p, 0x01); + BDADDR_TO_STREAM (p, btm_cb.ble_ctr_cb.addr_mgnt_cb.static_rand_addr); + } else +#endif ///BLE_INCLUDED == TRUE + { + UINT8_TO_STREAM (p, 0); + BDADDR_TO_STREAM (p, controller_get_interface()->get_address()->address); + } + + p_buf->offset = L2CAP_MIN_OFFSET; + p_buf->len = SMP_ID_ADDR_SIZE; + } + + return p_buf; +} + +/******************************************************************************* +** +** Function smp_build_signing_info_cmd +** +** Description Build signing information command. +** +*******************************************************************************/ +static BT_HDR *smp_build_signing_info_cmd(UINT8 cmd_code, tSMP_CB *p_cb) +{ + BT_HDR *p_buf = NULL ; + UINT8 *p; + UNUSED(cmd_code); + + SMP_TRACE_EVENT("smp_build_signing_info_cmd\n"); + if ((p_buf = (BT_HDR *)osi_malloc(sizeof(BT_HDR) + SMP_SIGN_INFO_SIZE + L2CAP_MIN_OFFSET)) != NULL) { + p = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET; + + UINT8_TO_STREAM (p, SMP_OPCODE_SIGN_INFO); + ARRAY_TO_STREAM (p, p_cb->csrk, BT_OCTET16_LEN); + + p_buf->offset = L2CAP_MIN_OFFSET; + p_buf->len = SMP_SIGN_INFO_SIZE; + } + + return p_buf; +} + +/******************************************************************************* +** +** Function smp_build_pairing_fail +** +** Description Build Pairing Fail command. +** +*******************************************************************************/ +static BT_HDR *smp_build_pairing_fail(UINT8 cmd_code, tSMP_CB *p_cb) +{ + BT_HDR *p_buf = NULL ; + UINT8 *p; + UNUSED(cmd_code); + + SMP_TRACE_EVENT("%s\n", __func__); + if ((p_buf = (BT_HDR *)osi_malloc(sizeof(BT_HDR) + SMP_PAIR_FAIL_SIZE + L2CAP_MIN_OFFSET)) != NULL) { + p = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET; + + UINT8_TO_STREAM (p, SMP_OPCODE_PAIRING_FAILED); + UINT8_TO_STREAM (p, p_cb->failure); + + p_buf->offset = L2CAP_MIN_OFFSET; + p_buf->len = SMP_PAIR_FAIL_SIZE; + } + + return p_buf; +} + +/******************************************************************************* +** +** Function smp_build_security_request +** +** Description Build security request command. +** +*******************************************************************************/ +static BT_HDR *smp_build_security_request(UINT8 cmd_code, tSMP_CB *p_cb) +{ + BT_HDR *p_buf = NULL ; + UINT8 *p; + UNUSED(cmd_code); + + SMP_TRACE_EVENT("%s\n", __func__); + if ((p_buf = (BT_HDR *)osi_malloc(sizeof(BT_HDR) + 2 + L2CAP_MIN_OFFSET)) != NULL) { + p = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET; + + UINT8_TO_STREAM (p, SMP_OPCODE_SEC_REQ); + UINT8_TO_STREAM (p, p_cb->loc_auth_req); + + p_buf->offset = L2CAP_MIN_OFFSET; + p_buf->len = SMP_SECURITY_REQUEST_SIZE; + + SMP_TRACE_EVENT("opcode=%d auth_req=0x%x", SMP_OPCODE_SEC_REQ, p_cb->loc_auth_req ); + } + + return p_buf; + +} + +/******************************************************************************* +** +** Function smp_build_pair_public_key_cmd +** +** Description Build pairing public key command. +** +*******************************************************************************/ +static BT_HDR *smp_build_pair_public_key_cmd(UINT8 cmd_code, tSMP_CB *p_cb) +{ + BT_HDR *p_buf = NULL ; + UINT8 *p; + UINT8 publ_key[2 * BT_OCTET32_LEN]; + UINT8 *p_publ_key = publ_key; + UNUSED(cmd_code); + + SMP_TRACE_EVENT("%s\n", __FUNCTION__); + + memcpy(p_publ_key, p_cb->loc_publ_key.x, BT_OCTET32_LEN); + memcpy(p_publ_key + BT_OCTET32_LEN, p_cb->loc_publ_key.y, BT_OCTET32_LEN); + + if ((p_buf = (BT_HDR *)osi_malloc(sizeof(BT_HDR) + + SMP_PAIR_PUBL_KEY_SIZE + L2CAP_MIN_OFFSET)) != NULL) { + p = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET; + + UINT8_TO_STREAM (p, SMP_OPCODE_PAIR_PUBLIC_KEY); + ARRAY_TO_STREAM (p, p_publ_key, 2 * BT_OCTET32_LEN); + + p_buf->offset = L2CAP_MIN_OFFSET; + p_buf->len = SMP_PAIR_PUBL_KEY_SIZE; + } + + return p_buf; +} + +/******************************************************************************* +** +** Function smp_build_pairing_commitment_cmd +** +** Description Build pairing commitment command. +** +*******************************************************************************/ +static BT_HDR *smp_build_pairing_commitment_cmd(UINT8 cmd_code, tSMP_CB *p_cb) +{ + BT_HDR *p_buf = NULL; + UINT8 *p; + UNUSED(cmd_code); + + SMP_TRACE_EVENT("%s\n", __func__); + if ((p_buf = (BT_HDR *)osi_malloc(sizeof(BT_HDR) + SMP_PAIR_COMMITM_SIZE + L2CAP_MIN_OFFSET)) + != NULL) { + p = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET; + + UINT8_TO_STREAM (p, SMP_OPCODE_CONFIRM); + ARRAY_TO_STREAM (p, p_cb->commitment, BT_OCTET16_LEN); + + p_buf->offset = L2CAP_MIN_OFFSET; + p_buf->len = SMP_PAIR_COMMITM_SIZE; + } + + return p_buf; +} + +/******************************************************************************* +** +** Function smp_build_pair_dhkey_check_cmd +** +** Description Build pairing DHKey check command. +** +*******************************************************************************/ +static BT_HDR *smp_build_pair_dhkey_check_cmd(UINT8 cmd_code, tSMP_CB *p_cb) +{ + BT_HDR *p_buf = NULL; + UINT8 *p; + UNUSED(cmd_code); + + SMP_TRACE_EVENT("%s\n", __FUNCTION__); + if ((p_buf = (BT_HDR *)osi_malloc(sizeof(BT_HDR) + + SMP_PAIR_DHKEY_CHECK_SIZE + L2CAP_MIN_OFFSET)) != NULL) { + p = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET; + + UINT8_TO_STREAM (p, SMP_OPCODE_PAIR_DHKEY_CHECK); + ARRAY_TO_STREAM (p, p_cb->dhkey_check, BT_OCTET16_LEN); + + p_buf->offset = L2CAP_MIN_OFFSET; + p_buf->len = SMP_PAIR_DHKEY_CHECK_SIZE; + } + + return p_buf; +} + +/******************************************************************************* +** +** Function smp_build_pairing_keypress_notification_cmd +** +** Description Build keypress notification command. +** +*******************************************************************************/ +static BT_HDR *smp_build_pairing_keypress_notification_cmd(UINT8 cmd_code, tSMP_CB *p_cb) +{ + BT_HDR *p_buf = NULL ; + UINT8 *p; + UNUSED(cmd_code); + + SMP_TRACE_EVENT("%s\n", __FUNCTION__); + if ((p_buf = (BT_HDR *)osi_malloc(sizeof(BT_HDR)\ + + SMP_PAIR_KEYPR_NOTIF_SIZE + L2CAP_MIN_OFFSET)) != NULL) { + p = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET; + + UINT8_TO_STREAM (p, SMP_OPCODE_PAIR_KEYPR_NOTIF); + UINT8_TO_STREAM (p, p_cb->local_keypress_notification); + + p_buf->offset = L2CAP_MIN_OFFSET; + p_buf->len = SMP_PAIR_KEYPR_NOTIF_SIZE; + } + + return p_buf; +} + +/******************************************************************************* +** +** Function smp_convert_string_to_tk +** +** Description This function is called to convert a 6 to 16 digits numeric +** character string into SMP TK. +** +** +** Returns void +** +*******************************************************************************/ +void smp_convert_string_to_tk(BT_OCTET16 tk, UINT32 passkey) +{ + UINT8 *p = tk; + tSMP_KEY key; + SMP_TRACE_EVENT("smp_convert_string_to_tk\n"); + UINT32_TO_STREAM(p, passkey); + + key.key_type = SMP_KEY_TYPE_TK; + key.p_data = tk; + + smp_sm_event(&smp_cb, SMP_KEY_READY_EVT, &key); +} + +/******************************************************************************* +** +** Function smp_mask_enc_key +** +** Description This function is called to mask off the encryption key based +** on the maximum encryption key size. +** +** +** Returns void +** +*******************************************************************************/ +void smp_mask_enc_key(UINT8 loc_enc_size, UINT8 *p_data) +{ + SMP_TRACE_EVENT("smp_mask_enc_key\n"); + if (loc_enc_size < BT_OCTET16_LEN) { + for (; loc_enc_size < BT_OCTET16_LEN; loc_enc_size ++) { + * (p_data + loc_enc_size) = 0; + } + } + return; +} + +/******************************************************************************* +** +** Function smp_xor_128 +** +** Description utility function to do an biteise exclusive-OR of two bit +** strings of the length of BT_OCTET16_LEN. +** +** Returns void +** +*******************************************************************************/ +void smp_xor_128(BT_OCTET16 a, const BT_OCTET16 b) +{ + UINT8 i, *aa = a; + const UINT8 *bb = b; + + SMP_TRACE_EVENT("smp_xor_128\n"); + for (i = 0; i < BT_OCTET16_LEN; i++) { + aa[i] = aa[i] ^ bb[i]; + } +} + +/******************************************************************************* +** +** Function smp_cb_cleanup +** +** Description Clean up SMP control block +** +** Returns void +** +*******************************************************************************/ +void smp_cb_cleanup(tSMP_CB *p_cb) +{ + tSMP_CALLBACK *p_callback = p_cb->p_callback; + UINT8 trace_level = p_cb->trace_level; + UINT32 static_passkey = p_cb->static_passkey; + BOOLEAN use_static_passkey = p_cb->use_static_passkey; + SMP_TRACE_EVENT("smp_cb_cleanup\n"); + + memset(p_cb, 0, sizeof(tSMP_CB)); + p_cb->p_callback = p_callback; + p_cb->trace_level = trace_level; + if(use_static_passkey) { + p_cb->use_static_passkey = use_static_passkey; + p_cb->static_passkey = static_passkey; + } +} + +/******************************************************************************* +** +** Function smp_remove_fixed_channel +** +** Description This function is called to remove the fixed channel +** +** Returns void +** +*******************************************************************************/ +void smp_remove_fixed_channel(tSMP_CB *p_cb) +{ + SMP_TRACE_DEBUG("%s\n", __func__); + + if (p_cb->smp_over_br) { + L2CA_RemoveFixedChnl (L2CAP_SMP_BR_CID, p_cb->pairing_bda); + } else { + L2CA_RemoveFixedChnl (L2CAP_SMP_CID, p_cb->pairing_bda); + } +} + +/******************************************************************************* +** +** Function smp_reset_control_value +** +** Description This function is called to reset the control block value when +** pairing procedure finished. +** +** +** Returns void +** +*******************************************************************************/ +void smp_reset_control_value(tSMP_CB *p_cb) +{ + SMP_TRACE_EVENT("smp_reset_control_value\n"); + btu_stop_timer (&p_cb->rsp_timer_ent); + p_cb->flags = 0; + /* set the link idle timer to drop the link when pairing is done + usually service discovery will follow authentication complete, to avoid + racing condition for a link down/up, set link idle timer to be + SMP_LINK_TOUT_MIN to guarantee SMP key exchange */ + L2CA_SetIdleTimeoutByBdAddr(p_cb->pairing_bda, SMP_LINK_TOUT_MIN, BT_TRANSPORT_LE); + + /* We can tell L2CAP to remove the fixed channel (if it has one) */ + smp_remove_fixed_channel(p_cb); + smp_cb_cleanup(p_cb); +} + +/******************************************************************************* +** +** Function smp_proc_pairing_cmpl +** +** Description This function is called to process pairing complete +** +** +** Returns void +** +*******************************************************************************/ +void smp_proc_pairing_cmpl(tSMP_CB *p_cb) +{ + tSMP_EVT_DATA evt_data = {0}; + tSMP_CALLBACK *p_callback = p_cb->p_callback; + BD_ADDR pairing_bda; + + SMP_TRACE_DEBUG ("smp_proc_pairing_cmpl \n"); + + evt_data.cmplt.reason = p_cb->status; + evt_data.cmplt.smp_over_br = p_cb->smp_over_br; + evt_data.cmplt.auth_mode = 0; +#if (BLE_INCLUDED == TRUE) + tBTM_SEC_DEV_REC *p_rec = btm_find_dev (p_cb->pairing_bda); + if (p_cb->status == SMP_SUCCESS) { + evt_data.cmplt.sec_level = p_cb->sec_level; + if (p_cb->auth_mode) { // the first encryption + evt_data.cmplt.auth_mode = p_cb->auth_mode; + if (p_rec) { + p_rec->ble.auth_mode = p_cb->auth_mode; + } + } else if (p_rec) { + evt_data.cmplt.auth_mode = p_rec->ble.auth_mode; + } + } +#else + if (p_cb->status == SMP_SUCCESS) { + evt_data.cmplt.sec_level = p_cb->sec_level; + evt_data.cmplt.auth_mode = p_cb->auth_mode; + } +#endif + + evt_data.cmplt.is_pair_cancel = FALSE; + + if (p_cb->is_pair_cancel) { + evt_data.cmplt.is_pair_cancel = TRUE; + } + + + SMP_TRACE_DEBUG ("send SMP_COMPLT_EVT reason=0x%0x sec_level=0x%0x\n", + evt_data.cmplt.reason, + evt_data.cmplt.sec_level ); + + memcpy (pairing_bda, p_cb->pairing_bda, BD_ADDR_LEN); + +#if (BLE_INCLUDED == TRUE) +#if (SMP_SLAVE_CON_PARAMS_UPD_ENABLE == TRUE) + if (p_cb->role == HCI_ROLE_SLAVE) { + if(p_rec && p_rec->ble.skip_update_conn_param) { + //clear flag + p_rec->ble.skip_update_conn_param = false; + } else { + #if (BT_MULTI_CONNECTION_ENBALE == FALSE) + L2CA_EnableUpdateBleConnParams(p_cb->pairing_bda, TRUE); + #endif + } + } + +#endif +#endif ///BLE_INCLUDED == TRUE + + smp_reset_control_value(p_cb); + // TODO: clear local oob data when start advertising + smp_clear_local_oob_data(); + + if (p_callback) { + (*p_callback) (SMP_COMPLT_EVT, pairing_bda, &evt_data); + } +} + +/******************************************************************************* +** +** Function smp_command_has_invalid_parameters +** +** Description Checks if the received SMP command has invalid parameters i.e. +** if the command length is valid and the command parameters are +** inside specified range. +** It returns TRUE if the command has invalid parameters. +** +** Returns TRUE if the command has invalid parameters, FALSE otherwise. +** +*******************************************************************************/ +BOOLEAN smp_command_has_invalid_parameters(tSMP_CB *p_cb) +{ + UINT8 cmd_code = p_cb->rcvd_cmd_code; + + SMP_TRACE_DEBUG("%s for cmd code 0x%02x\n", __func__, cmd_code); + + if ((cmd_code > (SMP_OPCODE_MAX + 1 /* for SMP_OPCODE_PAIR_COMMITM */)) || + (cmd_code < SMP_OPCODE_MIN)) { + SMP_TRACE_WARNING("Somehow received command with the RESERVED code 0x%02x\n", cmd_code); + return TRUE; + } + + if (!(*smp_cmd_len_is_valid[cmd_code])(p_cb)) { + return TRUE; + } + + if (!(*smp_cmd_param_ranges_are_valid[cmd_code])(p_cb)) { + return TRUE; + } + + return FALSE; +} + +/******************************************************************************* +** +** Function smp_command_has_valid_fixed_length +** +** Description Checks if the received command size is equal to the size +** according to specs. +** +** Returns TRUE if the command size is as expected, FALSE otherwise. +** +** Note The command is expected to have fixed length. +*******************************************************************************/ +BOOLEAN smp_command_has_valid_fixed_length(tSMP_CB *p_cb) +{ + UINT8 cmd_code = p_cb->rcvd_cmd_code; + + SMP_TRACE_DEBUG("%s for cmd code 0x%02x\n", __func__, cmd_code); + + if (p_cb->rcvd_cmd_len != smp_cmd_size_per_spec[cmd_code]) { + SMP_TRACE_WARNING("Rcvd from the peer cmd 0x%02x with invalid length\ + 0x%02x (per spec the length is 0x%02x).\n", + cmd_code, p_cb->rcvd_cmd_len, smp_cmd_size_per_spec[cmd_code]); + return FALSE; + } + + return TRUE; +} + +/******************************************************************************* +** +** Function smp_pairing_request_response_parameters_are_valid +** +** Description Validates parameter ranges in the received SMP command +** pairing request or pairing response. +** The parameters to validate: +** IO capability, +** OOB data flag, +** Bonding_flags in AuthReq +** Maximum encryption key size. +** Returns FALSE if at least one of these parameters is out of range. +** +*******************************************************************************/ +BOOLEAN smp_pairing_request_response_parameters_are_valid(tSMP_CB *p_cb) +{ + UINT8 io_caps = p_cb->peer_io_caps; + UINT8 oob_flag = p_cb->peer_oob_flag; + UINT8 bond_flag = p_cb->peer_auth_req & 0x03; //0x03 is gen bond with appropriate mask + UINT8 enc_size = p_cb->peer_enc_size; + + SMP_TRACE_DEBUG("%s for cmd code 0x%02x\n", __func__, p_cb->rcvd_cmd_code); + + if (io_caps >= BTM_IO_CAP_MAX) { + SMP_TRACE_WARNING("Rcvd from the peer cmd 0x%02x with IO Capabilty \ + value (0x%02x) out of range).\n", + p_cb->rcvd_cmd_code, io_caps); + return FALSE; + } + + if (!((oob_flag == SMP_OOB_NONE) || (oob_flag == SMP_OOB_PRESENT))) { + SMP_TRACE_WARNING("Rcvd from the peer cmd 0x%02x with OOB data flag value \ + (0x%02x) out of range).\n", + p_cb->rcvd_cmd_code, oob_flag); + return FALSE; + } + + if (!((bond_flag == SMP_AUTH_NO_BOND) || (bond_flag == SMP_AUTH_BOND))) { + SMP_TRACE_WARNING("Rcvd from the peer cmd 0x%02x with Bonding_Flags value (0x%02x)\ + out of range).\n", + p_cb->rcvd_cmd_code, bond_flag); + return FALSE; + } + + /* `bte_appl_cfg.ble_min_enc_key_size` will be `SMP_ENCR_KEY_SIZE_MIN` by + * default if not set explicitly */ +#if (BLE_INCLUDED == TRUE) + if (enc_size < bte_appl_cfg.ble_min_key_size) { + SMP_TRACE_WARNING("Rcvd from the peer cmd 0x%02x with Maximum Encryption \ + Key value (0x%02x) less than minimum required key size).\n", + p_cb->rcvd_cmd_code, enc_size); + return FALSE; + } +#else + if (enc_size < SMP_ENCR_KEY_SIZE_MIN) { + SMP_TRACE_WARNING("Rcvd from the peer cmd 0x%02x with Maximum Encryption \ + Key value (0x%02x) less than minimum required key size).\n", + p_cb->rcvd_cmd_code, enc_size); + return FALSE; + } +#endif + + if (enc_size > SMP_ENCR_KEY_SIZE_MAX) { + SMP_TRACE_WARNING("Rcvd from the peer cmd 0x%02x with Maximum Encryption \ + Key value (0x%02x) greater than supported by stack).\n", + p_cb->rcvd_cmd_code, enc_size); + return FALSE; + } + + return TRUE; +} + +/******************************************************************************* +** +** Function smp_pairing_keypress_notification_is_valid +** +** Description Validates Notification Type parameter range in the received SMP command +** pairing keypress notification. +** Returns FALSE if this parameter is out of range. +** +*******************************************************************************/ +BOOLEAN smp_pairing_keypress_notification_is_valid(tSMP_CB *p_cb) +{ + tBTM_SP_KEY_TYPE keypress_notification = p_cb->peer_keypress_notification; + + SMP_TRACE_DEBUG("%s for cmd code 0x%02x\n", __func__, p_cb->rcvd_cmd_code); + + if (keypress_notification >= BTM_SP_KEY_OUT_OF_RANGE) { + SMP_TRACE_WARNING("Rcvd from the peer cmd 0x%02x with Pairing Keypress \ + Notification value (0x%02x) out of range).\n", + p_cb->rcvd_cmd_code, keypress_notification); + return FALSE; + } + + return TRUE; +} + +/******************************************************************************* +** +** Function smp_parameter_unconditionally_valid +** +** Description Always returns TRUE. +** +*******************************************************************************/ +BOOLEAN smp_parameter_unconditionally_valid(tSMP_CB *p_cb) +{ + return TRUE; +} + +/******************************************************************************* +** +** Function smp_parameter_unconditionally_invalid +** +** Description Always returns FALSE. +** +*******************************************************************************/ +BOOLEAN smp_parameter_unconditionally_invalid(tSMP_CB *p_cb) +{ + return FALSE; +} + +/******************************************************************************* +** +** Function smp_reject_unexpected_pairing_command +** +** Description send pairing failure to an unexpected pairing command during +** an active pairing process. +** +** Returns void +** +*******************************************************************************/ +void smp_reject_unexpected_pairing_command(BD_ADDR bd_addr) +{ + BT_HDR *p_buf; + UINT8 *p; + + SMP_TRACE_DEBUG ("%s\n", __FUNCTION__); + + if ((p_buf = (BT_HDR *)osi_malloc(sizeof(BT_HDR) + \ + SMP_PAIR_FAIL_SIZE + L2CAP_MIN_OFFSET)) != NULL) { + p = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET; + + UINT8_TO_STREAM (p, SMP_OPCODE_PAIRING_FAILED); + UINT8_TO_STREAM (p, SMP_PAIR_NOT_SUPPORT); + + p_buf->offset = L2CAP_MIN_OFFSET; + p_buf->len = SMP_PAIR_FAIL_SIZE; + + smp_send_msg_to_L2CAP(bd_addr, p_buf); + } +} + +/******************************************************************************* +** Function smp_select_association_model +** +** Description This function selects association model to use for STK +** generation. Selection is based on both sides' io capability, +** oob data flag and authentication request. +** +** Note If Secure Connections Only mode is required locally then we +** come to this point only if both sides support Secure Connections +** mode, i.e. if p_cb->secure_connections_only_mode_required = TRUE then we come +** to this point only if +** (p_cb->peer_auth_req & SMP_SC_SUPPORT_BIT) == +** (p_cb->loc_auth_req & SMP_SC_SUPPORT_BIT) == +** SMP_SC_SUPPORT_BIT +** +*******************************************************************************/ +tSMP_ASSO_MODEL smp_select_association_model(tSMP_CB *p_cb) +{ + tSMP_ASSO_MODEL model = SMP_MODEL_OUT_OF_RANGE; + p_cb->le_secure_connections_mode_is_used = FALSE; + + SMP_TRACE_EVENT("%s\n", __FUNCTION__); + SMP_TRACE_DEBUG("%s p_cb->peer_io_caps = %d p_cb->local_io_capability = %d\n", + __FUNCTION__, p_cb->peer_io_caps, p_cb->local_io_capability); + SMP_TRACE_DEBUG("%s p_cb->peer_oob_flag = %d p_cb->loc_oob_flag = %d\n", + __FUNCTION__, p_cb->peer_oob_flag, p_cb->loc_oob_flag); + SMP_TRACE_DEBUG("%s p_cb->peer_auth_req = 0x%02x p_cb->loc_auth_req = 0x%02x\n", + __FUNCTION__, p_cb->peer_auth_req, p_cb->loc_auth_req); + SMP_TRACE_DEBUG("%s p_cb->secure_connections_only_mode_required = %s\n", + __FUNCTION__, p_cb->secure_connections_only_mode_required ? + "TRUE" : "FALSE"); + + if ((p_cb->peer_auth_req & SMP_SC_SUPPORT_BIT) && (p_cb->loc_auth_req & SMP_SC_SUPPORT_BIT)) { + p_cb->le_secure_connections_mode_is_used = TRUE; + } + + SMP_TRACE_DEBUG("use_sc_process = %d\n", p_cb->le_secure_connections_mode_is_used); + + if (p_cb->le_secure_connections_mode_is_used) { + model = smp_select_association_model_secure_connections(p_cb); + } else { + model = smp_select_legacy_association_model(p_cb); + } + return model; +} + +/******************************************************************************* +** Function smp_select_legacy_association_model +** +** Description This function is called to select association mode if at least +** one side doesn't support secure connections. +** +*******************************************************************************/ +tSMP_ASSO_MODEL smp_select_legacy_association_model(tSMP_CB *p_cb) +{ + tSMP_ASSO_MODEL model = SMP_MODEL_OUT_OF_RANGE; + + SMP_TRACE_DEBUG("%s\n", __func__); + /* if OOB data is present on both devices, then use OOB association model */ + if (p_cb->peer_oob_flag == SMP_OOB_PRESENT && p_cb->loc_oob_flag == SMP_OOB_PRESENT) { + return SMP_MODEL_OOB; + } + + /* else if neither device requires MITM, then use Just Works association model */ + if (SMP_NO_MITM_REQUIRED (p_cb->peer_auth_req) && SMP_NO_MITM_REQUIRED(p_cb->loc_auth_req)) { + return SMP_MODEL_ENCRYPTION_ONLY; + } + + /* otherwise use IO capability to select association model */ + if (p_cb->peer_io_caps < SMP_IO_CAP_MAX && p_cb->local_io_capability < SMP_IO_CAP_MAX) { + if (p_cb->role == HCI_ROLE_MASTER) { + model = smp_association_table[p_cb->role][p_cb->peer_io_caps] + [p_cb->local_io_capability]; + } else { + model = smp_association_table[p_cb->role][p_cb->local_io_capability] + [p_cb->peer_io_caps]; + } + } + + return model; +} + +/******************************************************************************* +** Function smp_select_association_model_secure_connections +** +** Description This function is called to select association mode if both +** sides support secure connections. +** +*******************************************************************************/ +tSMP_ASSO_MODEL smp_select_association_model_secure_connections(tSMP_CB *p_cb) +{ + tSMP_ASSO_MODEL model = SMP_MODEL_OUT_OF_RANGE; + + SMP_TRACE_DEBUG("%s\n", __func__); + /* if OOB data is present on at least one device, then use OOB association model */ + if (p_cb->peer_oob_flag == SMP_OOB_PRESENT || p_cb->loc_oob_flag == SMP_OOB_PRESENT) { + return SMP_MODEL_SEC_CONN_OOB; + } + + /* else if neither device requires MITM, then use Just Works association model */ + if (SMP_NO_MITM_REQUIRED (p_cb->peer_auth_req) && SMP_NO_MITM_REQUIRED(p_cb->loc_auth_req)) { + return SMP_MODEL_SEC_CONN_JUSTWORKS; + } + + /* otherwise use IO capability to select association model */ + if (p_cb->peer_io_caps < SMP_IO_CAP_MAX && p_cb->local_io_capability < SMP_IO_CAP_MAX) { + if (p_cb->role == HCI_ROLE_MASTER) { + model = smp_association_table_sc[p_cb->role][p_cb->peer_io_caps] + [p_cb->local_io_capability]; + } else { + model = smp_association_table_sc[p_cb->role][p_cb->local_io_capability] + [p_cb->peer_io_caps]; + } + } + + return model; +} + +/******************************************************************************* +** Function smp_reverse_array +** +** Description This function reverses array bytes +** +*******************************************************************************/ +void smp_reverse_array(UINT8 *arr, UINT8 len) +{ + UINT8 i = 0, tmp; + + SMP_TRACE_DEBUG("smp_reverse_array\n"); + + for (i = 0; i < len / 2; i ++) { + tmp = arr[i]; + arr[i] = arr[len - 1 - i]; + arr[len - 1 - i] = tmp; + } +} + +/******************************************************************************* +** Function smp_calculate_random_input +** +** Description This function returns random input value to be used in commitment +** calculation for SC passkey entry association mode +** (if bit["round"] in "random" array == 1 then returns 0x81 +** else returns 0x80). +** +** Returns ri value +** +*******************************************************************************/ +UINT8 smp_calculate_random_input(UINT8 *random, UINT8 round) +{ + UINT8 i = round / 8; + UINT8 j = round % 8; + UINT8 ri; + + SMP_TRACE_DEBUG("random: 0x%02x, round: %d, i: %d, j: %d\n", random[i], round, i, j); + ri = ((random[i] >> j) & 1) | 0x80; + SMP_TRACE_DEBUG("%s ri=0x%02x\n", __func__, ri); + return ri; +} + +/******************************************************************************* +** Function smp_collect_local_io_capabilities +** +** Description This function puts into IOcap array local device +** IOCapability, OOB data, AuthReq. +** +** Returns void +** +*******************************************************************************/ +void smp_collect_local_io_capabilities(UINT8 *iocap, tSMP_CB *p_cb) +{ + SMP_TRACE_DEBUG("%s\n", __func__); + + iocap[0] = p_cb->local_io_capability; + iocap[1] = p_cb->loc_oob_flag; + iocap[2] = p_cb->loc_auth_req; +} + +/******************************************************************************* +** Function smp_collect_peer_io_capabilities +** +** Description This function puts into IOcap array peer device +** IOCapability, OOB data, AuthReq. +** +** Returns void +** +*******************************************************************************/ +void smp_collect_peer_io_capabilities(UINT8 *iocap, tSMP_CB *p_cb) +{ + SMP_TRACE_DEBUG("%s\n", __func__); + + iocap[0] = p_cb->peer_io_caps; + iocap[1] = p_cb->peer_oob_flag; + iocap[2] = p_cb->peer_auth_req; +} +#if (BLE_INCLUDED == TRUE) +/******************************************************************************* +** Function smp_collect_local_ble_address +** +** Description This function puts into le_addr array local device le address: +** le_addr[0-5] = local BD ADDR, +** le_addr[6] = local le address type (PUBLIC/RANDOM). +** +** Returns void +** +*******************************************************************************/ +void smp_collect_local_ble_address(UINT8 *le_addr, tSMP_CB *p_cb) +{ + tBLE_ADDR_TYPE addr_type = 0; + BD_ADDR bda; + UINT8 *p = le_addr; + + SMP_TRACE_DEBUG("%s\n", __func__); + + BTM_ReadConnectionAddr( p_cb->pairing_bda, bda, &addr_type); + BDADDR_TO_STREAM(p, bda); + UINT8_TO_STREAM(p, addr_type); +} + +/******************************************************************************* +** Function smp_collect_peer_ble_address +** +** Description This function puts into le_addr array peer device le address: +** le_addr[0-5] = peer BD ADDR, +** le_addr[6] = peer le address type (PUBLIC/RANDOM). +** +** Returns void +** +*******************************************************************************/ +void smp_collect_peer_ble_address(UINT8 *le_addr, tSMP_CB *p_cb) +{ + tBLE_ADDR_TYPE addr_type = 0; + BD_ADDR bda; + UINT8 *p = le_addr; + + SMP_TRACE_DEBUG("%s\n", __func__); + + if (!BTM_ReadRemoteConnectionAddr(p_cb->pairing_bda, bda, &addr_type)) { + SMP_TRACE_ERROR("can not collect peer le addr information for unknown device\n"); + return; + } + + BDADDR_TO_STREAM(p, bda); + UINT8_TO_STREAM(p, addr_type); +} + +/******************************************************************************* +** Function smp_check_commitment +** +** Description This function compares peer commitment values: +** - expected (i.e. calculated locally), +** - received from the peer. +** +** Returns TRUE if the values are the same +** FALSE otherwise +** +*******************************************************************************/ +BOOLEAN smp_check_commitment(tSMP_CB *p_cb) +{ + BT_OCTET16 expected; + + SMP_TRACE_DEBUG("%s\n", __func__); + + smp_calculate_peer_commitment(p_cb, expected); + print128(expected, (const UINT8 *)"calculated peer commitment"); + print128(p_cb->remote_commitment, (const UINT8 *)"received peer commitment"); + + if (memcmp(p_cb->remote_commitment, expected, BT_OCTET16_LEN)) { + SMP_TRACE_WARNING("Commitment check fails\n"); + return FALSE; + } + + SMP_TRACE_DEBUG("Commitment check succeeds\n"); + return TRUE; +} + +/******************************************************************************* +** +** Function smp_save_secure_connections_long_term_key +** +** Description The function saves SC LTK as BLE key for future use as local +** and/or peer key. +** +** Returns void +** +*******************************************************************************/ +void smp_save_secure_connections_long_term_key(tSMP_CB *p_cb) +{ + tBTM_LE_LENC_KEYS lle_key; + tBTM_LE_PENC_KEYS ple_key; + + SMP_TRACE_DEBUG("%s-Save LTK as local LTK key\n", __func__); + memcpy(lle_key.ltk, p_cb->ltk, BT_OCTET16_LEN); + lle_key.div = 0; + lle_key.key_size = p_cb->loc_enc_size; + lle_key.sec_level = p_cb->sec_level; + btm_sec_save_le_key(p_cb->pairing_bda, BTM_LE_KEY_LENC, (tBTM_LE_KEY_VALUE *)&lle_key, TRUE); + + SMP_TRACE_DEBUG("%s-Save LTK as peer LTK key\n", __func__); + ple_key.ediv = 0; + memset(ple_key.rand, 0, BT_OCTET8_LEN); + memcpy(ple_key.ltk, p_cb->ltk, BT_OCTET16_LEN); + ple_key.sec_level = p_cb->sec_level; + ple_key.key_size = p_cb->loc_enc_size; + btm_sec_save_le_key(p_cb->pairing_bda, BTM_LE_KEY_PENC, (tBTM_LE_KEY_VALUE *)&ple_key, TRUE); +} + +/******************************************************************************* +** +** Function smp_calculate_f5_mackey_and_long_term_key +** +** Description The function calculates MacKey and LTK and saves them in CB. +** To calculate MacKey and LTK it calls smp_calc_f5(...). +** MacKey is used in dhkey calculation, LTK is used to encrypt +** the link. +** +** Returns FALSE if out of resources, TRUE otherwise. +** +*******************************************************************************/ +BOOLEAN smp_calculate_f5_mackey_and_long_term_key(tSMP_CB *p_cb) +{ + UINT8 a[7]; + UINT8 b[7]; + UINT8 *p_na; + UINT8 *p_nb; + + SMP_TRACE_DEBUG("%s\n", __func__); + + if (p_cb->role == HCI_ROLE_MASTER) { + smp_collect_local_ble_address(a, p_cb); + smp_collect_peer_ble_address(b, p_cb); + p_na = p_cb->rand; + p_nb = p_cb->rrand; + } else { + smp_collect_local_ble_address(b, p_cb); + smp_collect_peer_ble_address(a, p_cb); + p_na = p_cb->rrand; + p_nb = p_cb->rand; + } + + if (!smp_calculate_f5(p_cb->dhkey, p_na, p_nb, a, b, p_cb->mac_key, p_cb->ltk)) { + SMP_TRACE_ERROR("%s failed\n", __func__); + return FALSE; + } + + SMP_TRACE_EVENT ("%s is completed\n", __func__); + return TRUE; +} +#endif ///BLE_INCLUDED == TRUE +/******************************************************************************* +** +** Function smp_request_oob_data +** +** Description Requests application to provide OOB data. +** +** Returns TRUE - OOB data has to be provided by application +** FALSE - otherwise (unexpected) +** +*******************************************************************************/ +BOOLEAN smp_request_oob_data(tSMP_CB *p_cb) +{ + tSMP_OOB_DATA_TYPE req_oob_type = SMP_OOB_INVALID_TYPE; + + SMP_TRACE_DEBUG("%s\n", __func__); + + if (p_cb->peer_oob_flag == SMP_OOB_PRESENT && p_cb->loc_oob_flag == SMP_OOB_PRESENT) { + /* both local and peer rcvd data OOB */ + req_oob_type = SMP_OOB_BOTH; + } else if (p_cb->peer_oob_flag == SMP_OOB_PRESENT) { + /* peer rcvd OOB local data, local didn't receive OOB peer data */ + req_oob_type = SMP_OOB_LOCAL; + } else if (p_cb->loc_oob_flag == SMP_OOB_PRESENT) { + req_oob_type = SMP_OOB_PEER; + } + + SMP_TRACE_DEBUG("req_oob_type = %d\n", req_oob_type); + + if (req_oob_type == SMP_OOB_INVALID_TYPE) { + return FALSE; + } + + p_cb->req_oob_type = req_oob_type; + p_cb->cb_evt = SMP_SC_OOB_REQ_EVT; + smp_sm_event(p_cb, SMP_TK_REQ_EVT, &req_oob_type); + + return TRUE; +} + + +#endif diff --git a/lib/bt/host/nimble/Kconfig.in b/lib/bt/host/nimble/Kconfig.in new file mode 100644 index 00000000..eec575c3 --- /dev/null +++ b/lib/bt/host/nimble/Kconfig.in @@ -0,0 +1,1016 @@ + +choice BT_NIMBLE_MEM_ALLOC_MODE + prompt "Memory allocation strategy" + default BT_NIMBLE_MEM_ALLOC_MODE_INTERNAL + help + Allocation strategy for NimBLE host stack, essentially provides ability to + allocate all required dynamic allocations from, + + - Internal DRAM memory only + - External SPIRAM memory only + - Either internal or external memory based on default malloc() + behavior in ESP-IDF + - Internal IRAM memory wherever applicable else internal DRAM + + config BT_NIMBLE_MEM_ALLOC_MODE_INTERNAL + bool "Internal memory" + + config BT_NIMBLE_MEM_ALLOC_MODE_EXTERNAL + bool "External SPIRAM" + depends on SPIRAM_USE_CAPS_ALLOC || SPIRAM_USE_MALLOC + + config BT_NIMBLE_MEM_ALLOC_MODE_DEFAULT + bool "Default alloc mode" + + config BT_NIMBLE_MEM_ALLOC_MODE_IRAM_8BIT + bool "Internal IRAM" + depends on ESP32_IRAM_AS_8BIT_ACCESSIBLE_MEMORY + help + Allows to use IRAM memory region as 8bit accessible region. + + Every unaligned (8bit or 16bit) access will result in an exception + and incur penalty of certain clock cycles per unaligned read/write. + +endchoice #BT_NIMBLE_MEM_ALLOC_MODE + +choice BT_NIMBLE_LOG_LEVEL + prompt "NimBLE Host log verbosity" + depends on BT_NIMBLE_ENABLED + default BT_NIMBLE_LOG_LEVEL_INFO + help + Select NimBLE log level. Please make a note that the selected NimBLE log + verbosity can not exceed the level set in "Component config --> Log output + --> Default log verbosity". + + config BT_NIMBLE_LOG_LEVEL_NONE + bool "No logs" + config BT_NIMBLE_LOG_LEVEL_ERROR + bool "Error logs" + config BT_NIMBLE_LOG_LEVEL_WARNING + bool "Warning logs" + config BT_NIMBLE_LOG_LEVEL_INFO + bool "Info logs" + config BT_NIMBLE_LOG_LEVEL_DEBUG + bool "Debug logs" +endchoice #BT_NIMBLE_LOG_LEVEL + +config BT_NIMBLE_LOG_LEVEL + int + default 0 if BT_NIMBLE_LOG_LEVEL_DEBUG + default 1 if BT_NIMBLE_LOG_LEVEL_INFO + default 2 if BT_NIMBLE_LOG_LEVEL_WARNING + default 3 if BT_NIMBLE_LOG_LEVEL_ERROR + default 4 if BT_NIMBLE_LOG_LEVEL_NONE + +config BT_NIMBLE_MAX_CONNECTIONS + int "Maximum number of concurrent connections" + range 1 2 if IDF_TARGET_ESP32C2 + range 1 70 if IDF_TARGET_ESP32C6 + range 1 35 if IDF_TARGET_ESP32H2 + range 1 9 + default 2 if IDF_TARGET_ESP32C2 + default 3 + depends on BT_NIMBLE_ENABLED + help + Defines maximum number of concurrent BLE connections. For ESP32, user + is expected to configure BTDM_CTRL_BLE_MAX_CONN from controller menu + along with this option. Similarly for ESP32-C3 or ESP32-S3, user is expected to + configure BT_CTRL_BLE_MAX_ACT from controller menu. + For ESP32C2, ESP32C6 and ESP32H2, each connection will take about 1k DRAM. + +config BT_NIMBLE_MAX_BONDS + int "Maximum number of bonds to save across reboots" + default 3 + depends on BT_NIMBLE_ENABLED + help + Defines maximum number of bonds to save for peer security and our security + +config BT_NIMBLE_MAX_CCCDS + int "Maximum number of CCC descriptors to save across reboots" + default 8 + depends on BT_NIMBLE_ENABLED + help + Defines maximum number of CCC descriptors to save + +config BT_NIMBLE_L2CAP_COC_MAX_NUM + int "Maximum number of connection oriented channels" + range 0 9 + depends on BT_NIMBLE_ENABLED + default 0 + help + Defines maximum number of BLE Connection Oriented Channels. When set to (0), BLE COC is not compiled in + +choice BT_NIMBLE_PINNED_TO_CORE_CHOICE + prompt "The CPU core on which NimBLE host will run" + depends on BT_NIMBLE_ENABLED && !FREERTOS_UNICORE + help + The CPU core on which NimBLE host will run. You can choose Core 0 or Core 1. + Cannot specify no-affinity + + config BT_NIMBLE_PINNED_TO_CORE_0 + bool "Core 0 (PRO CPU)" + config BT_NIMBLE_PINNED_TO_CORE_1 + bool "Core 1 (APP CPU)" + depends on !FREERTOS_UNICORE +endchoice + +config BT_NIMBLE_PINNED_TO_CORE + int + depends on BT_NIMBLE_ENABLED + default 0 if BT_NIMBLE_PINNED_TO_CORE_0 + default 1 if BT_NIMBLE_PINNED_TO_CORE_1 + default 0 + +config BT_NIMBLE_HOST_TASK_STACK_SIZE + int "NimBLE Host task stack size" + depends on BT_NIMBLE_ENABLED + default 5120 if BLE_MESH + default 4096 + help + This configures stack size of NimBLE host task + +config BT_NIMBLE_ROLE_CENTRAL + bool "Enable BLE Central role" + depends on BT_NIMBLE_ENABLED + default y + help + Enables central role + +config BT_NIMBLE_ROLE_PERIPHERAL + bool "Enable BLE Peripheral role" + depends on BT_NIMBLE_ENABLED + default y + help + Enable peripheral role + +config BT_NIMBLE_ROLE_BROADCASTER + bool "Enable BLE Broadcaster role" + depends on BT_NIMBLE_ENABLED + default y + help + Enables broadcaster role + +config BT_NIMBLE_ROLE_OBSERVER + bool "Enable BLE Observer role" + depends on BT_NIMBLE_ENABLED + default y + help + Enables observer role + +config BT_NIMBLE_NVS_PERSIST + bool "Persist the BLE Bonding keys in NVS" + depends on BT_NIMBLE_ENABLED + default n + help + Enable this flag to make bonding persistent across device reboots + +menuconfig BT_NIMBLE_SECURITY_ENABLE + bool "Enable BLE SM feature" + depends on BT_NIMBLE_ENABLED + default y + help + Enable BLE sm feature + +config BT_NIMBLE_SM_LEGACY + bool "Security manager legacy pairing" + depends on BT_NIMBLE_SECURITY_ENABLE + default y + help + Enable security manager legacy pairing + +config BT_NIMBLE_SM_SC + bool "Security manager secure connections (4.2)" + depends on BT_NIMBLE_SECURITY_ENABLE + default y + help + Enable security manager secure connections + +config BT_NIMBLE_SM_SC_DEBUG_KEYS + bool "Use predefined public-private key pair" + default n + depends on BT_NIMBLE_SECURITY_ENABLE && BT_NIMBLE_SM_SC + help + If this option is enabled, SM uses predefined DH key pair as described + in Core Specification, Vol. 3, Part H, 2.3.5.6.1. This allows to + decrypt air traffic easily and thus should only be used for debugging. + +config BT_NIMBLE_LL_CFG_FEAT_LE_ENCRYPTION + bool "Enable LE encryption" + depends on BT_NIMBLE_SECURITY_ENABLE && BT_NIMBLE_ENABLED + default y + help + Enable encryption connection + +config BT_NIMBLE_SM_SC_LVL + int "Security level" + depends on BT_NIMBLE_SECURITY_ENABLE + default 0 + help + LE Security Mode 1 Levels: + 1. No Security + 2. Unauthenticated pairing with encryption + 3. Authenticated pairing with encryption + 4. Authenticated LE Secure Connections pairing with encryption using a 128-bit strength encryption key. + +config BT_NIMBLE_DEBUG + bool "Enable extra runtime asserts and host debugging" + default n + depends on BT_NIMBLE_ENABLED + help + This enables extra runtime asserts and host debugging + +config BT_NIMBLE_DYNAMIC_SERVICE + bool "Enable dynamic services" + depends on BT_NIMBLE_ENABLED + help + This enables user to add/remove Gatt services at runtime + + +config BT_NIMBLE_SVC_GAP_DEVICE_NAME + string "BLE GAP default device name" + depends on BT_NIMBLE_ENABLED + default "nimble" + help + The Device Name characteristic shall contain the name of the device as an UTF-8 string. + This name can be changed by using API ble_svc_gap_device_name_set() + +config BT_NIMBLE_GAP_DEVICE_NAME_MAX_LEN + int "Maximum length of BLE device name in octets" + depends on BT_NIMBLE_ENABLED + default 31 + help + Device Name characteristic value shall be 0 to 248 octets in length + +config BT_NIMBLE_ATT_PREFERRED_MTU + int "Preferred MTU size in octets" + depends on BT_NIMBLE_ENABLED + default 256 + help + This is the default value of ATT MTU indicated by the device during an ATT MTU exchange. + This value can be changed using API ble_att_set_preferred_mtu() + +config BT_NIMBLE_SVC_GAP_APPEARANCE + hex "External appearance of the device" + depends on BT_NIMBLE_ENABLED + default 0 + help + Standard BLE GAP Appearance value in HEX format e.g. 0x02C0 + + +menu "Memory Settings" + config BT_NIMBLE_MSYS_1_BLOCK_COUNT + int "MSYS_1 Block Count" + default 24 if SOC_ESP_NIMBLE_CONTROLLER + default 12 if !SOC_ESP_NIMBLE_CONTROLLER + help + MSYS is a system level mbuf registry. For prepare write & prepare + responses MBUFs are allocated out of msys_1 pool. For NIMBLE_MESH + enabled cases, this block count is increased by 8 than user defined + count. + + config BT_NIMBLE_MSYS_1_BLOCK_SIZE + int "MSYS_1 Block Size" + default 128 if SOC_ESP_NIMBLE_CONTROLLER + default 256 if !SOC_ESP_NIMBLE_CONTROLLER + help + Dynamic memory size of block 1 + + config BT_NIMBLE_MSYS_2_BLOCK_COUNT + int "MSYS_2 Block Count" + default 24 + help + Dynamic memory count + + config BT_NIMBLE_MSYS_2_BLOCK_SIZE + int "MSYS_2 Block Size" + default 320 + help + Dynamic memory size of block 2 + + config BT_NIMBLE_MSYS_BUF_FROM_HEAP + bool "Get Msys Mbuf from heap" + default y + depends on BT_LE_MSYS_INIT_IN_CONTROLLER + help + This option sets the source of the shared msys mbuf memory between + the Host and the Controller. Allocate the memory from the heap if + this option is sets, from the mempool otherwise. + + config BT_NIMBLE_TRANSPORT_ACL_FROM_LL_COUNT + int "ACL Buffer count" + depends on BT_NIMBLE_ENABLED + default 24 + help + The number of ACL data buffers allocated for host. + + config BT_NIMBLE_TRANSPORT_ACL_SIZE + int "Transport ACL Buffer size" + depends on BT_NIMBLE_ENABLED + default 255 + help + This is the maximum size of the data portion of HCI ACL data packets. + It does not include the HCI data header (of 4 bytes) + + config BT_NIMBLE_TRANSPORT_EVT_SIZE + int "Transport Event Buffer size" + depends on BT_NIMBLE_ENABLED + default 257 if BT_NIMBLE_EXT_ADV + default 70 + help + This is the size of each HCI event buffer in bytes. In case of + extended advertising, packets can be fragmented. 257 bytes is the + maximum size of a packet. + + config BT_NIMBLE_TRANSPORT_EVT_COUNT + int "Transport Event Buffer count" + depends on BT_NIMBLE_ENABLED + default 30 + help + This is the high priority HCI events' buffer size. High-priority + event buffers are for everything except advertising reports. If there + are no free high-priority event buffers then host will try to allocate a + low-priority buffer instead + + config BT_NIMBLE_TRANSPORT_EVT_DISCARD_COUNT + int "Discardable Transport Event Buffer count" + depends on BT_NIMBLE_ENABLED + default 8 + help + This is the low priority HCI events' buffer size. Low-priority event + buffers are only used for advertising reports. If there are no free + low-priority event buffers, then an incoming advertising report will + get dropped + +endmenu + +config BT_NIMBLE_GATT_MAX_PROCS + int "Maximum number of GATT client procedures" + depends on BT_NIMBLE_ENABLED + default 4 + help + Maximum number of GATT client procedures that can be executed. + +config BT_NIMBLE_HS_FLOW_CTRL + bool "Enable Host Flow control" + depends on BT_NIMBLE_ENABLED + default y if IDF_TARGET_ESP32 + default n + help + Enable Host Flow control + +config BT_NIMBLE_HS_FLOW_CTRL_ITVL + int "Host Flow control interval" + depends on BT_NIMBLE_HS_FLOW_CTRL + default 1000 + help + Host flow control interval in msecs + +config BT_NIMBLE_HS_FLOW_CTRL_THRESH + int "Host Flow control threshold" + depends on BT_NIMBLE_HS_FLOW_CTRL + default 2 + help + Host flow control threshold, if the number of free buffers are at or + below this threshold, send an immediate number-of-completed-packets + event + +config BT_NIMBLE_HS_FLOW_CTRL_TX_ON_DISCONNECT + bool "Host Flow control on disconnect" + depends on BT_NIMBLE_HS_FLOW_CTRL + default y + help + Enable this option to send number-of-completed-packets event to + controller after disconnection + +config BT_NIMBLE_RPA_TIMEOUT + int "RPA timeout in seconds" + range 1 41400 + depends on BT_NIMBLE_ENABLED + default 900 + help + Time interval between RPA address change. This is applicable in case of + Host based RPA + +menuconfig BT_NIMBLE_MESH + bool "Enable BLE mesh functionality" + select BT_NIMBLE_SM_SC + depends on BT_NIMBLE_ENABLED + default n + help + Enable BLE Mesh example present in upstream mynewt-nimble and not maintained by Espressif. + + IDF maintains ESP-BLE-MESH as the official Mesh solution. Please refer to ESP-BLE-MESH guide at: + `https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/esp-ble-mesh/ble-mesh-index.html` + +config BT_NIMBLE_MESH_PROXY + bool "Enable mesh proxy functionality" + default n + depends on BT_NIMBLE_MESH + help + Enable proxy. This is automatically set whenever NIMBLE_MESH_PB_GATT or + NIMBLE_MESH_GATT_PROXY is set + + +config BT_NIMBLE_MESH_PROV + bool "Enable BLE mesh provisioning" + default y + depends on BT_NIMBLE_MESH + help + Enable mesh provisioning + +config BT_NIMBLE_MESH_PB_ADV + bool "Enable mesh provisioning over advertising bearer" + default y + depends on BT_NIMBLE_MESH_PROV + help + Enable this option to allow the device to be provisioned over + the advertising bearer + + +config BT_NIMBLE_MESH_PB_GATT + bool "Enable mesh provisioning over GATT bearer" + default y + select BT_NIMBLE_MESH_PROXY + depends on BT_NIMBLE_MESH_PROV + help + Enable this option to allow the device to be provisioned over the GATT + bearer + +config BT_NIMBLE_MESH_GATT_PROXY + bool "Enable GATT Proxy functionality" + default y + select BT_NIMBLE_MESH_PROXY + depends on BT_NIMBLE_MESH + help + This option enables support for the Mesh GATT Proxy Service, + i.e. the ability to act as a proxy between a Mesh GATT Client + and a Mesh network + +config BT_NIMBLE_MESH_RELAY + bool "Enable mesh relay functionality" + default n + depends on BT_NIMBLE_MESH + help + Support for acting as a Mesh Relay Node + +config BT_NIMBLE_MESH_LOW_POWER + bool "Enable mesh low power mode" + default n + depends on BT_NIMBLE_MESH + help + Enable this option to be able to act as a Low Power Node + +config BT_NIMBLE_MESH_FRIEND + bool "Enable mesh friend functionality" + default n + depends on BT_NIMBLE_MESH + help + Enable this option to be able to act as a Friend Node + +config BT_NIMBLE_MESH_DEVICE_NAME + string "Set mesh device name" + default "nimble-mesh-node" + depends on BT_NIMBLE_MESH + help + This value defines Bluetooth Mesh device/node name + +config BT_NIMBLE_MESH_NODE_COUNT + int "Set mesh node count" + default 1 + depends on BT_NIMBLE_MESH + help + Defines mesh node count. + +config BT_NIMBLE_MESH_PROVISIONER + bool "Enable BLE mesh provisioner" + default 0 + depends on BT_NIMBLE_MESH + help + Enable mesh provisioner. + +config BT_NIMBLE_CRYPTO_STACK_MBEDTLS + bool "Override TinyCrypt with mbedTLS for crypto computations" + default y + depends on BT_NIMBLE_ENABLED + select MBEDTLS_ECP_RESTARTABLE + select MBEDTLS_CMAC_C + help + Enable this option to choose mbedTLS instead of TinyCrypt for crypto + computations. + +config BT_NIMBLE_HS_STOP_TIMEOUT_MS + int "BLE host stop timeout in msec" + default 2000 + depends on BT_NIMBLE_ENABLED + help + BLE Host stop procedure timeout in milliseconds. + +config BT_NIMBLE_HOST_BASED_PRIVACY + bool "Enable host based privacy for random address." + default n + depends on BT_NIMBLE_ENABLED && IDF_TARGET_ESP32 + help + Use this option to do host based Random Private Address resolution. + If this option is disabled then controller based privacy is used. + +config BT_NIMBLE_ENABLE_CONN_REATTEMPT + bool "Enable connection reattempts on connection establishment error" + default y if (IDF_TARGET_ESP32C3 || IDF_TARGET_ESP32S3 || SOC_ESP_NIMBLE_CONTROLLER) + default n if IDF_TARGET_ESP32 + help + Enable to make the NimBLE host to reattempt GAP connection on connection + establishment failure. + +config BT_NIMBLE_MAX_CONN_REATTEMPT + int "Maximum number connection reattempts" + range 1 7 + default 3 + depends on BT_NIMBLE_ENABLED && BT_NIMBLE_ENABLE_CONN_REATTEMPT + help + Defines maximum number of connection reattempts. + +menuconfig BT_NIMBLE_50_FEATURE_SUPPORT + bool "Enable BLE 5 feature" + depends on BT_NIMBLE_ENABLED && (SOC_BLE_50_SUPPORTED || !BT_CONTROLLER_ENABLED) + default y + help + Enable BLE 5 feature + +config BT_NIMBLE_LL_CFG_FEAT_LE_2M_PHY + bool "Enable 2M Phy" + depends on BT_NIMBLE_50_FEATURE_SUPPORT + default y + help + Enable 2M-PHY + +config BT_NIMBLE_LL_CFG_FEAT_LE_CODED_PHY + bool "Enable coded Phy" + depends on BT_NIMBLE_50_FEATURE_SUPPORT + default y + help + Enable coded-PHY + +config BT_NIMBLE_EXT_ADV + bool "Enable extended advertising" + depends on BT_NIMBLE_50_FEATURE_SUPPORT + default n + help + Enable this option to do extended advertising. Extended advertising + will be supported from BLE 5.0 onwards. + +if BT_NIMBLE_EXT_ADV + config BT_NIMBLE_MAX_EXT_ADV_INSTANCES + int "Maximum number of extended advertising instances." + range 0 4 + default 1 if BT_NIMBLE_EXT_ADV + default 0 + depends on BT_NIMBLE_EXT_ADV + help + Change this option to set maximum number of extended advertising + instances. Minimum there is always one instance of + advertising. Enter how many more advertising instances you + want. + For ESP32C2, ESP32C6 and ESP32H2, each extended advertising instance + will take about 0.5k DRAM. + + config BT_NIMBLE_EXT_ADV_MAX_SIZE + int "Maximum length of the advertising data." + range 0 1650 + default 1650 if BT_NIMBLE_EXT_ADV + default 0 + depends on BT_NIMBLE_EXT_ADV + help + Defines the length of the extended adv data. The value should not + exceed 1650. + + config BT_NIMBLE_ENABLE_PERIODIC_ADV + bool "Enable periodic advertisement." + default y + depends on BT_NIMBLE_EXT_ADV + help + Enable this option to start periodic advertisement. + + config BT_NIMBLE_PERIODIC_ADV_SYNC_TRANSFER + bool "Enable Transer Sync Events" + depends on BT_NIMBLE_ENABLE_PERIODIC_ADV + default y + help + This enables controller transfer periodic sync events to host +endif + +config BT_NIMBLE_MAX_PERIODIC_SYNCS + int "Maximum number of periodic advertising syncs" + depends on BT_NIMBLE_50_FEATURE_SUPPORT + range 0 3 if IDF_TARGET_ESP32C2 + range 0 8 + default 1 if BT_NIMBLE_ENABLE_PERIODIC_ADV + default 0 + help + Set this option to set the upper limit for number of periodic sync + connections. This should be less than maximum connections allowed by + controller. + +config BT_NIMBLE_MAX_PERIODIC_ADVERTISER_LIST + int "Maximum number of periodic advertiser list" + depends on BT_NIMBLE_50_FEATURE_SUPPORT && SOC_ESP_NIMBLE_CONTROLLER + range 1 5 + default 5 if BT_NIMBLE_50_FEATURE_SUPPORT + help + Set this option to set the upper limit for number of periodic advertiser list. + +config BT_NIMBLE_BLE_POWER_CONTROL + bool "Enable support for BLE Power Control" + depends on BT_NIMBLE_50_FEATURE_SUPPORT && SOC_BLE_POWER_CONTROL_SUPPORTED + default n + help + Set this option to enable the Power Control feature + +config BT_NIMBLE_PERIODIC_ADV_ENH + bool "Periodic adv enhancements(adi support)" + depends on BT_NIMBLE_ENABLE_PERIODIC_ADV && BT_NIMBLE_50_FEATURE_SUPPORT && SOC_BLE_PERIODIC_ADV_ENH_SUPPORTED + help + Enable the periodic advertising enhancements + +menuconfig BT_NIMBLE_GATT_CACHING + bool "Enable GATT caching" + depends on BT_NIMBLE_ENABLED && BT_NIMBLE_50_FEATURE_SUPPORT + help + Enable GATT caching +config BT_NIMBLE_GATT_CACHING_MAX_CONNS + int "Maximum connections to be cached" + depends on BT_NIMBLE_GATT_CACHING + default 1 + help + Set this option to set the upper limit on number of connections to be cached. +config BT_NIMBLE_GATT_CACHING_MAX_SVCS + int "Maximum number of services per connection" + depends on BT_NIMBLE_GATT_CACHING + default 64 + help + Set this option to set the upper limit on number of services per connection to be cached. +config BT_NIMBLE_GATT_CACHING_MAX_CHRS + int "Maximum number of characteristics per connection" + depends on BT_NIMBLE_GATT_CACHING + default 64 + help + Set this option to set the upper limit on number of characteristics per connection to be cached. +config BT_NIMBLE_GATT_CACHING_MAX_DSCS + int "Maximum number of descriptors per connection" + depends on BT_NIMBLE_GATT_CACHING + default 64 + help + Set this option to set the upper limit on number of discriptors per connection to be cached. + +config BT_NIMBLE_WHITELIST_SIZE + int "BLE white list size" + depends on BT_NIMBLE_ENABLED + range 1 15 + default 12 + help + BLE list size + +config BT_NIMBLE_TEST_THROUGHPUT_TEST + bool "Throughput Test Mode enable" + default n + help + Enable the throughput test mode + +config BT_NIMBLE_BLUFI_ENABLE + bool "Enable blufi functionality" + depends on BT_NIMBLE_ENABLED + default n + help + Set this option to enable blufi functionality. + +config BT_NIMBLE_USE_ESP_TIMER + bool "Enable Esp Timer for Nimble" + default y + help + Set this option to use Esp Timer which has higher priority timer instead of FreeRTOS timer + +config BT_NIMBLE_LEGACY_VHCI_ENABLE + bool + default y if (IDF_TARGET_ESP32 || IDF_TARGET_ESP32C3 || IDF_TARGET_ESP32S3) + default n + help + This option is used to distinguish whether a previous version of VHCI is being used + +config BT_NIMBLE_BLE_GATT_BLOB_TRANSFER + bool "Blob transfer" + help + This option is used when data to be sent is more than 512 bytes. For peripheral role, + BT_NIMBLE_MSYS_1_BLOCK_COUNT needs to be increased according to the need. + +menu "GAP Service" + menu "GAP Appearance write permissions" + config BT_NIMBLE_SVC_GAP_APPEAR_WRITE + bool "Write" + default n + help + Enable write permission (BLE_GATT_CHR_F_WRITE) + + config BT_NIMBLE_SVC_GAP_APPEAR_WRITE_ENC + depends on BT_NIMBLE_SVC_GAP_APPEAR_WRITE + bool "Write with encryption" + default n + help + Enable write with encryption permission (BLE_GATT_CHR_F_WRITE_ENC) + + config BT_NIMBLE_SVC_GAP_APPEAR_WRITE_AUTHEN + depends on BT_NIMBLE_SVC_GAP_APPEAR_WRITE + bool "Write with authentication" + default n + help + Enable write with authentication permission (BLE_GATT_CHR_F_WRITE_AUTHEN) + + config BT_NIMBLE_SVC_GAP_APPEAR_WRITE_AUTHOR + depends on BT_NIMBLE_SVC_GAP_APPEAR_WRITE + bool "Write with authorisation" + default n + help + Enable write with authorisation permission (BLE_GATT_CHR_F_WRITE_AUTHOR) + endmenu + + config BT_NIMBLE_SVC_GAP_APPEAR_WRITE_PERM + int + default 0 if !BT_NIMBLE_SVC_GAP_APPEAR_WRITE + default 8 if BT_NIMBLE_SVC_GAP_APPEAR_WRITE + + config BT_NIMBLE_SVC_GAP_APPEAR_WRITE_PERM_ENC + int + default 0 if !BT_NIMBLE_SVC_GAP_APPEAR_WRITE_ENC + default 4096 if BT_NIMBLE_SVC_GAP_APPEAR_WRITE_ENC + + config BT_NIMBLE_SVC_GAP_APPEAR_WRITE_PERM_ATHN + int + default 0 if !BT_NIMBLE_SVC_GAP_APPEAR_WRITE_AUTHEN + default 8192 if BT_NIMBLE_SVC_GAP_APPEAR_WRITE_AUTHEN + + config BT_NIMBLE_SVC_GAP_APPEAR_WRITE_PERM_ATHR + int + default 0 if !BT_NIMBLE_SVC_GAP_APPEAR_WRITE_AUTHOR + default 16384 if BT_NIMBLE_SVC_GAP_APPEAR_WRITE_AUTHOR + + choice BT_NIMBLE_SVC_GAP_CENT_ADDR_RESOLUTION + prompt "GAP Characteristic - Central Address Resolution" + default BT_NIMBLE_SVC_GAP_CAR_CHAR_NOT_SUPP + help + Weather or not Central Address Resolution characteristic is supported on + the device, and if supported, weather or not Central Address Resolution + is supported. + + - Central Address Resolution characteristic not supported + - Central Address Resolution not supported + - Central Address Resolution supported + + config BT_NIMBLE_SVC_GAP_CAR_CHAR_NOT_SUPP + bool "Characteristic not supported" + + config BT_NIMBLE_SVC_GAP_CAR_NOT_SUPP + bool "Central Address Resolution not supported" + + config BT_NIMBLE_SVC_GAP_CAR_SUPP + bool "Central Address Resolution supported" + endchoice + + config BT_NIMBLE_SVC_GAP_CENT_ADDR_RESOLUTION + int + default -1 if BT_NIMBLE_SVC_GAP_CAR_CHAR_NOT_SUPP + default 0 if BT_NIMBLE_SVC_GAP_CAR_NOT_SUPP + default 1 if BT_NIMBLE_SVC_GAP_CAR_SUPP + + menu "GAP device name write permissions" + config BT_NIMBLE_SVC_GAP_NAME_WRITE + bool "Write" + default n + help + Enable write permission (BLE_GATT_CHR_F_WRITE) + + config BT_NIMBLE_SVC_GAP_NAME_WRITE_ENC + depends on BT_NIMBLE_SVC_GAP_NAME_WRITE + bool "Write with encryption" + default n + help + Enable write with encryption permission (BLE_GATT_CHR_F_WRITE_ENC) + + config BT_NIMBLE_SVC_GAP_NAME_WRITE_AUTHEN + depends on BT_NIMBLE_SVC_GAP_NAME_WRITE + bool "Write with authentication" + default n + help + Enable write with authentication permission (BLE_GATT_CHR_F_WRITE_AUTHEN) + + config BT_NIMBLE_SVC_GAP_NAME_WRITE_AUTHOR + depends on BT_NIMBLE_SVC_GAP_NAME_WRITE + bool "Write with authorisation" + default n + help + Enable write with authorisation permission (BLE_GATT_CHR_F_WRITE_AUTHOR) + endmenu + + config BT_NIMBLE_SVC_GAP_NAME_WRITE_PERM + int + default 0 if !BT_NIMBLE_SVC_GAP_NAME_WRITE + default 8 if BT_NIMBLE_SVC_GAP_NAME_WRITE + + config BT_NIMBLE_SVC_GAP_NAME_WRITE_PERM_ENC + int + default 0 if !BT_NIMBLE_SVC_GAP_NAME_WRITE_ENC + default 4096 if BT_NIMBLE_SVC_GAP_NAME_WRITE_ENC + + config BT_NIMBLE_SVC_GAP_NAME_WRITE_PERM_AUTHEN + int + default 0 if !BT_NIMBLE_SVC_GAP_NAME_WRITE_AUTHEN + default 8192 if BT_NIMBLE_SVC_GAP_NAME_WRITE_AUTHEN + + config BT_NIMBLE_SVC_GAP_NAME_WRITE_PERM_AUTHOR + int + default 0 if !BT_NIMBLE_SVC_GAP_NAME_WRITE_AUTHOR + default 16384 if BT_NIMBLE_SVC_GAP_NAME_WRITE_AUTHOR + + config BT_NIMBLE_SVC_GAP_PPCP_MAX_CONN_INTERVAL + int "PPCP Connection Interval Max (Unit: 1.25 ms)" + depends on BT_NIMBLE_ROLE_PERIPHERAL + default 0 + help + Peripheral Preferred Connection Parameter: Connection Interval maximum value + Interval Max = value * 1.25 ms + + config BT_NIMBLE_SVC_GAP_PPCP_MIN_CONN_INTERVAL + int "PPCP Connection Interval Min (Unit: 1.25 ms)" + depends on BT_NIMBLE_ROLE_PERIPHERAL + default 0 + help + Peripheral Preferred Connection Parameter: Connection Interval minimum value + Interval Min = value * 1.25 ms + + config BT_NIMBLE_SVC_GAP_PPCP_SLAVE_LATENCY + int "PPCP Slave Latency" + default 0 + help + Peripheral Preferred Connection Parameter: Slave Latency + + config BT_NIMBLE_SVC_GAP_PPCP_SUPERVISION_TMO + int "PPCP Supervision Timeout (Uint: 10 ms)" + default 0 + help + Peripheral Preferred Connection Parameter: Supervision Timeout + Timeout = Value * 10 ms + +endmenu + +menu "BLE Services" + menuconfig BT_NIMBLE_HID_SERVICE + bool "HID service" + depends on BT_NIMBLE_ENABLED + default n + help + Enable HID service support + + config BT_NIMBLE_SVC_HID_MAX_INSTANCES + depends on BT_NIMBLE_HID_SERVICE + int "Maximum HID service instances" + default 2 + help + Defines maximum number of HID service instances + + config BT_NIMBLE_SVC_HID_MAX_RPTS + depends on BT_NIMBLE_HID_SERVICE + int "Maximum HID Report characteristics per service instance" + default 3 + help + Defines maximum number of report characteristics per service instance +endmenu + +config BT_NIMBLE_VS_SUPPORT + bool "Enable support for VSC and VSE" + help + This option is used to enable support for sending Vendor Specific HCI commands and handling + Vendor Specific HCI Events. + +config BT_NIMBLE_OPTIMIZE_MULTI_CONN + bool "Enable the optimization of multi-connection" + depends on SOC_BLE_MULTI_CONN_OPTIMIZATION + select BT_NIMBLE_VS_SUPPORT + default n + help + This option enables the use of vendor-specific APIs for multi-connections, which can + greatly enhance the stability of coexistence between numerous central and peripheral + devices. It will prohibit the usage of standard APIs. + +config BT_NIMBLE_ENC_ADV_DATA + bool "Encrypted Advertising Data" + depends on SOC_ESP_NIMBLE_CONTROLLER + select BT_NIMBLE_EXT_ADV + help + This option is used to enable encrypted advertising data. + +config BT_NIMBLE_MAX_EADS + int "Maximum number of EAD devices to save across reboots" + default 10 + depends on BT_NIMBLE_ENABLED && BT_NIMBLE_ENC_ADV_DATA + help + Defines maximum number of encrypted advertising data key material to save + +config BT_NIMBLE_HIGH_DUTY_ADV_ITVL + bool "Enable BLE high duty advertising interval feature" + depends on BT_NIMBLE_ENABLED + help + This enable BLE high duty advertising interval feature + +config BT_NIMBLE_HOST_QUEUE_CONG_CHECK + bool "BLE queue congestion check" + depends on BT_NIMBLE_ENABLED + default n + help + When scanning and scan duplicate is not enabled, if there are a lot of adv packets around + or application layer handling adv packets is slow, it will cause the controller memory + to run out. if enabled, adv packets will be lost when host queue is congested. + +menu "Host-controller Transport" + config BT_NIMBLE_TRANSPORT_UART + bool "Enable Uart Transport" + default y + depends on BT_CONTROLLER_DISABLED + help + Use UART transport + + config BT_NIMBLE_TRANSPORT_UART_PORT + int "Uart port" + depends on BT_CONTROLLER_DISABLED && BT_NIMBLE_TRANSPORT_UART + default 1 + help + Uart port + + choice BT_NIMBLE_USE_HCI_UART_PARITY + prompt "Uart PARITY" + default UART_PARITY_NONE + help + Uart Parity + + config UART_PARITY_NONE + bool "None" + config UART_PARITY_ODD + bool "Odd" + config UART_PARITY_EVEN + bool "Even" + endchoice + + config BT_NIMBLE_TRANSPORT_UART_PARITY_NONE + int + default 0 if !UART_PARITY_NONE + default 1 if UART_PARITY_NONE + + config BT_NIMBLE_TRANSPORT_UART_PARITY_ODD + int + default 0 if !UART_PARITY_ODD + default 1 if UART_PARITY_ODD + + config BT_NIMBLE_TRANSPORT_UART_PARITY_EVEN + int + default 0 if !UART_PARITY_EVEN + default 1 if UART_PARITY_EVEN + + config BT_NIMBLE_UART_RX_PIN + int "UART Rx pin" + depends on BT_CONTROLLER_DISABLED && BT_NIMBLE_TRANSPORT_UART + default 5 + help + Rx pin for Nimble Transport + + config BT_NIMBLE_UART_TX_PIN + int "UART Tx pin" + depends on BT_CONTROLLER_DISABLED && BT_NIMBLE_TRANSPORT_UART + default 4 + help + Tx pin for Nimble Transport + + choice BT_NIMBLE_USE_HCI_UART_FLOW_CTRL + prompt "Uart Flow Control" + default UART_HW_FLOWCTRL_DISABLE + help + Uart Flow Control + + config UART_HW_FLOWCTRL_DISABLE + bool "Disable" + config UART_HW_FLOWCTRL_CTS_RTS + bool "Enable hardware flow control" + endchoice + + config BT_NIMBLE_HCI_UART_FLOW_CTRL + int + default 0 if UART_HW_FLOWCTRL_DISABLE + default 1 if UART_HW_FLOWCTRL_CTS_RTS + + config BT_NIMBLE_HCI_UART_RTS_PIN + int "UART Rts Pin" + default 19 + help + UART HCI RTS pin + + config BT_NIMBLE_HCI_UART_CTS_PIN + int "UART Cts Pin" + default 23 + help + UART HCI CTS pin +endmenu diff --git a/lib/bt/host/nimble/esp-hci/include/esp_nimble_hci.h b/lib/bt/host/nimble/esp-hci/include/esp_nimble_hci.h new file mode 100644 index 00000000..2127426e --- /dev/null +++ b/lib/bt/host/nimble/esp-hci/include/esp_nimble_hci.h @@ -0,0 +1,52 @@ +/* + * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __ESP_NIMBLE_HCI_H__ +#define __ESP_NIMBLE_HCI_H__ + +#include "nimble/transport.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define BLE_HCI_UART_H4_NONE 0x00 +#define BLE_HCI_UART_H4_CMD 0x01 +#define BLE_HCI_UART_H4_ACL 0x02 +#define BLE_HCI_UART_H4_SCO 0x03 +#define BLE_HCI_UART_H4_EVT 0x04 + +/** + * @brief Initialize VHCI transport layer between NimBLE Host and + * ESP Bluetooth controller + * + * This function initializes the transport buffers to be exchanged + * between NimBLE host and ESP controller. It also registers required + * host callbacks with the controller. + * + * @return + * - ESP_OK if the initialization is successful + * - Appropriate error code from esp_err_t in case of an error + */ +esp_err_t esp_nimble_hci_init(void); + +/** + * @brief Deinitialize VHCI transport layer between NimBLE Host and + * ESP Bluetooth controller + * + * @note This function should be called after the NimBLE host is deinitialized. + * + * @return + * - ESP_OK if the deinitialization is successful + * - Appropriate error codes from esp_err_t in case of an error + */ +esp_err_t esp_nimble_hci_deinit(void); + +#ifdef __cplusplus +} +#endif + +#endif /* __ESP_NIMBLE_HCI_H__ */ diff --git a/lib/bt/host/nimble/esp-hci/src/esp_nimble_hci.c b/lib/bt/host/nimble/esp-hci/src/esp_nimble_hci.c new file mode 100644 index 00000000..9d997389 --- /dev/null +++ b/lib/bt/host/nimble/esp-hci/src/esp_nimble_hci.c @@ -0,0 +1,300 @@ +/* + * SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "sysinit/sysinit.h" +#include "nimble/hci_common.h" +#if CONFIG_BT_NIMBLE_ENABLED +#include "host/ble_hs.h" +#endif //CONFIG_BT_NIMBLE_ENABLED +#include "nimble/nimble_port.h" +#include "nimble/nimble_port_freertos.h" +#include "esp_nimble_hci.h" +#include "esp_nimble_mem.h" +#include "bt_osi_mem.h" +#if CONFIG_BT_CONTROLLER_ENABLED +#include "esp_bt.h" +#endif +#include "freertos/semphr.h" +#include "esp_compiler.h" +#include "soc/soc_caps.h" + +#define NIMBLE_VHCI_TIMEOUT_MS 2000 +#define BLE_HCI_EVENT_HDR_LEN (2) +#define BLE_HCI_CMD_HDR_LEN (3) + +static ble_hci_trans_rx_cmd_fn *ble_hci_rx_cmd_hs_cb; +static void *ble_hci_rx_cmd_hs_arg; + +static ble_hci_trans_rx_acl_fn *ble_hci_rx_acl_hs_cb; +static void *ble_hci_rx_acl_hs_arg; + +/* + * The MBUF payload size must accommodate the HCI data header size plus the + * maximum ACL data packet length. The ACL block size is the size of the + * mbufs we will allocate. + */ +#define ACL_BLOCK_SIZE OS_ALIGN(MYNEWT_VAL(BLE_TRANSPORT_ACL_SIZE) \ + + BLE_MBUF_MEMBLOCK_OVERHEAD \ + + BLE_HCI_DATA_HDR_SZ, OS_ALIGNMENT) + + + +static SemaphoreHandle_t vhci_send_sem; +const static char *TAG = "NimBLE"; + +int os_msys_buf_alloc(void); +void os_msys_buf_free(void); +extern uint8_t ble_hs_enabled_state; + +void ble_hci_trans_cfg_hs(ble_hci_trans_rx_cmd_fn *cmd_cb, + void *cmd_arg, + ble_hci_trans_rx_acl_fn *acl_cb, + void *acl_arg) +{ + ble_hci_rx_cmd_hs_cb = cmd_cb; + ble_hci_rx_cmd_hs_arg = cmd_arg; + ble_hci_rx_acl_hs_cb = acl_cb; + ble_hci_rx_acl_hs_arg = acl_arg; +} + + +int ble_hci_trans_hs_cmd_tx(uint8_t *cmd) +{ + uint16_t len; + uint8_t rc = 0; + + assert(cmd != NULL); + *cmd = BLE_HCI_UART_H4_CMD; + len = BLE_HCI_CMD_HDR_LEN + cmd[3] + 1; + if (!esp_vhci_host_check_send_available()) { + ESP_LOGD(TAG, "Controller not ready to receive packets"); + } + + if (xSemaphoreTake(vhci_send_sem, NIMBLE_VHCI_TIMEOUT_MS / portTICK_PERIOD_MS) == pdTRUE) { + esp_vhci_host_send_packet(cmd, len); + } else { + rc = BLE_HS_ETIMEOUT_HCI; + } + + ble_transport_free(cmd); + return rc; +} + +int ble_hci_trans_ll_evt_tx(uint8_t *hci_ev) +{ + int rc = ESP_FAIL; + + rc = ble_transport_to_hs_evt((void *)hci_ev); + return rc; +} + +int ble_hci_trans_hs_acl_tx(struct os_mbuf *om) +{ + uint16_t len = 0; + uint8_t data[MYNEWT_VAL(BLE_TRANSPORT_ACL_SIZE) + 1], rc = 0; + /* If this packet is zero length, just free it */ + if (OS_MBUF_PKTLEN(om) == 0) { + os_mbuf_free_chain(om); + return 0; + } + data[0] = BLE_HCI_UART_H4_ACL; + len++; + + if (!esp_vhci_host_check_send_available()) { + ESP_LOGD(TAG, "Controller not ready to receive packets"); + } + + os_mbuf_copydata(om, 0, OS_MBUF_PKTLEN(om), &data[1]); + len += OS_MBUF_PKTLEN(om); + + if (xSemaphoreTake(vhci_send_sem, NIMBLE_VHCI_TIMEOUT_MS / portTICK_PERIOD_MS) == pdTRUE) { + esp_vhci_host_send_packet(data, len); + } else { + rc = BLE_HS_ETIMEOUT_HCI; + } + + os_mbuf_free_chain(om); + + return rc; +} + +int ble_hci_trans_ll_acl_tx(struct os_mbuf *om) +{ + int rc = ESP_FAIL; + + rc = ble_transport_to_hs_acl((void *)om); + return rc; +} + + + +int ble_hci_trans_reset(void) +{ + /* No work to do. All allocated buffers are owned by the host or + * controller, and they will get freed by their owners. + */ + return 0; +} + + +static void ble_hci_rx_acl(uint8_t *data, uint16_t len) +{ + struct os_mbuf *m = NULL; + int rc; + int sr; + if (len < BLE_HCI_DATA_HDR_SZ || len > MYNEWT_VAL(BLE_TRANSPORT_ACL_SIZE)) { + return; + } + + do { + m = ble_transport_alloc_acl_from_ll(); + + if (!m) { + ESP_LOGD(TAG,"Failed to allocate buffer, retrying "); + /* Give some time to free buffer and try again */ + vTaskDelay(1); + } + }while(!m); + + if ((rc = os_mbuf_append(m, data, len)) != 0) { + ESP_LOGE(TAG, "%s failed to os_mbuf_append; rc = %d", __func__, rc); + os_mbuf_free_chain(m); + return; + } + OS_ENTER_CRITICAL(sr); + ble_transport_to_hs_acl(m); + OS_EXIT_CRITICAL(sr); +} + + +/* + * @brief: BT controller callback function, used to notify the upper layer that + * controller is ready to receive command + */ +static void controller_rcv_pkt_ready(void) +{ + if (vhci_send_sem) { + xSemaphoreGive(vhci_send_sem); + } +} + +/* + * @brief: BT controller callback function, to transfer data packet to the host + */ +static int host_rcv_pkt(uint8_t *data, uint16_t len) +{ + if(!ble_hs_enabled_state) { + /* If host is not enabled, drop the packet */ + ESP_LOGE(TAG, "Host not enabled. Dropping the packet!"); + return 0; + } + + if (data[0] == BLE_HCI_UART_H4_EVT) { + uint8_t *evbuf; + int totlen; + int rc; + + totlen = BLE_HCI_EVENT_HDR_LEN + data[2]; + assert(totlen <= UINT8_MAX + BLE_HCI_EVENT_HDR_LEN); + + if (totlen > MYNEWT_VAL(BLE_TRANSPORT_EVT_SIZE)) { + ESP_LOGE(TAG, "Received HCI data length at host (%d) exceeds maximum configured HCI event buffer size (%d).", + totlen, MYNEWT_VAL(BLE_TRANSPORT_EVT_SIZE)); + ble_hs_sched_reset(BLE_HS_ECONTROLLER); + return 0; + } + + if (data[1] == BLE_HCI_EVCODE_HW_ERROR) { + assert(0); + } + + /* Allocate LE Advertising Report Event from lo pool only */ + if ((data[1] == BLE_HCI_EVCODE_LE_META) && + (data[3] == BLE_HCI_LE_SUBEV_ADV_RPT || data[3] == BLE_HCI_LE_SUBEV_EXT_ADV_RPT)) { + evbuf = ble_transport_alloc_evt(1); + /* Skip advertising report if we're out of memory */ + if (!evbuf) { + return 0; + } + } else { + evbuf = ble_transport_alloc_evt(0); + assert(evbuf != NULL); + } + + memset(evbuf, 0, sizeof *evbuf); + memcpy(evbuf, &data[1], totlen); + + rc = ble_hci_trans_ll_evt_tx(evbuf); + assert(rc == 0); + } else if (data[0] == BLE_HCI_UART_H4_ACL) { + ble_hci_rx_acl(data + 1, len - 1); + } + return 0; +} + +static const esp_vhci_host_callback_t vhci_host_cb = { + .notify_host_send_available = controller_rcv_pkt_ready, + .notify_host_recv = host_rcv_pkt, +}; + + +extern void ble_transport_init(void); +extern esp_err_t ble_buf_alloc(void); +extern void ble_buf_free(void); +esp_err_t esp_nimble_hci_init(void) +{ + esp_err_t ret; + + ret = ble_buf_alloc(); + if (ret != ESP_OK) { + goto err; + } + if ((ret = esp_vhci_host_register_callback(&vhci_host_cb)) != ESP_OK) { + goto err; + } + + ble_transport_init(); + + vhci_send_sem = xSemaphoreCreateBinary(); + if (vhci_send_sem == NULL) { + ret = ESP_ERR_NO_MEM; + goto err; + } + + xSemaphoreGive(vhci_send_sem); + +#if MYNEWT_VAL(BLE_QUEUE_CONG_CHECK) + ble_adv_list_init(); +#endif + + return ret; +err: + ble_buf_free(); + return ret; + +} + +extern void ble_transport_deinit(void); +esp_err_t esp_nimble_hci_deinit(void) +{ + if (vhci_send_sem) { + /* Dummy take & give semaphore before deleting */ + xSemaphoreTake(vhci_send_sem, portMAX_DELAY); + xSemaphoreGive(vhci_send_sem); + vSemaphoreDelete(vhci_send_sem); + vhci_send_sem = NULL; + } + ble_transport_deinit(); + + ble_buf_free(); + +#if MYNEWT_VAL(BLE_QUEUE_CONG_CHECK) + ble_adv_list_deinit(); +#endif + + return ESP_OK; +} diff --git a/lib/bt/host/nimble/nimble/.gitignore b/lib/bt/host/nimble/nimble/.gitignore new file mode 100644 index 00000000..bcc3a72b --- /dev/null +++ b/lib/bt/host/nimble/nimble/.gitignore @@ -0,0 +1,5 @@ +# Dummy NPL build +*.o +/porting/examples/dummy/dummy +/porting/examples/linux/nimble-linux +/porting/examples/linux_blemesh/nimble-linux-blemesh diff --git a/lib/bt/host/nimble/nimble/.mailmap b/lib/bt/host/nimble/nimble/.mailmap new file mode 100644 index 00000000..c1458fc5 --- /dev/null +++ b/lib/bt/host/nimble/nimble/.mailmap @@ -0,0 +1,8 @@ +Christopher Collins +Marko Kiiskila +Vipul Rahane +Vipul Rahane +Vipul Rahane +Will San Filippo +Will San Filippo + diff --git a/lib/bt/host/nimble/nimble/.rat-excludes b/lib/bt/host/nimble/nimble/.rat-excludes new file mode 100644 index 00000000..a4bcb91b --- /dev/null +++ b/lib/bt/host/nimble/nimble/.rat-excludes @@ -0,0 +1,37 @@ +# Can't easily add license to rat-excludes file. +.rat-excludes + +# Ignore documentation folder +docs + +# Non-source files +RELEASE_NOTES.md +.gitignore +README.md +pts-gap.txt +pts-gatt.txt +pts-l2cap.txt +pts-sm.txt +94654-20170317-085122560.tpg +94654-20170317-085441153.pts +uncrustify.cfg +.style_ignored_dirs +.mailmap + +# tinycrypt - BSD License. +tinycrypt + +# Bluetooth Mesh - Apache 2.0 License +mesh + +# Queue implementation - BSD License +queue.h + +# mbuf implementation - BSD License +os_mbuf.c + +# Bluetooth Mesh badge sample - Apache 2.0 License +mesh_badge + +#BabbleSim and EDDT - Apache 2.0 License +babblesim diff --git a/lib/bt/host/nimble/nimble/.style_ignored_dirs b/lib/bt/host/nimble/nimble/.style_ignored_dirs new file mode 100644 index 00000000..4be8efa0 --- /dev/null +++ b/lib/bt/host/nimble/nimble/.style_ignored_dirs @@ -0,0 +1,4 @@ +# Skip those directories while doing style checks in the CI. Do not add '/' at +# the beginning! + +ext/tinycrypt diff --git a/lib/bt/host/nimble/nimble/.travis.yml b/lib/bt/host/nimble/nimble/.travis.yml new file mode 100644 index 00000000..d3dd9742 --- /dev/null +++ b/lib/bt/host/nimble/nimble/.travis.yml @@ -0,0 +1,156 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +language: go + +dist: bionic + +_addons: &addon_conf + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - gcc-multilib + - gcc-7-multilib + - g++-multilib + +go: + - "1.16" + +git: + depth: false + +matrix: + allow_failures: + - env: + - TEST=BUILD_PORTS + - VM_AMOUNT=1 + - TARGET_SET=1 + include: + # Style checking + - os: linux + language: python + python: + - "3.5" + addons: + apt: + packages: + - "python3-pip" + env: + - TEST=STYLE + - DEBUG=1 + + # newt build + - os: linux + addons: *addon_conf + env: + - TEST=BUILD_TARGETS + - VM_AMOUNT=4 + - TARGET_SET=1 + - os: linux + addons: *addon_conf + env: + - TEST=BUILD_TARGETS + - VM_AMOUNT=4 + - TARGET_SET=2 + - os: linux + addons: *addon_conf + env: + - TEST=BUILD_TARGETS + - VM_AMOUNT=4 + - TARGET_SET=3 + - os: linux + addons: *addon_conf + env: + - TEST=BUILD_TARGETS + - VM_AMOUNT=4 + - TARGET_SET=4 + + # newt test all (Linux) + - os: linux + addons: *addon_conf + env: + - TEST=TEST_ALL + - VM_AMOUNT=2 + - TARGET_SET=1 + - os: linux + addons: *addon_conf + env: + - TEST=TEST_ALL + - VM_AMOUNT=2 + - TARGET_SET=2 + + # ports + - os: linux + addons: *addon_conf + env: + - TEST=BUILD_PORTS + - VM_AMOUNT=1 + - TARGET_SET=1 + + - os: windows + env: + - TEST=BUILD_TARGETS_WINDOWS + - VM_AMOUNT=1 + - TARGET_SET=1 + +before_install: + - printenv + - export GOPATH=$HOME/gopath + - go version + +install: + - git clone https://github.com/JuulLabs-OSS/mynewt-travis-ci $HOME/ci + - chmod +x $HOME/ci/*.sh + - | + if [ "${TEST}" == "STYLE" ]; then + pip3 install requests + else + $HOME/ci/${TRAVIS_OS_NAME}_travis_install.sh + fi + +before_script: + - | + if [ "${TEST}" == "STYLE" ]; then + $HOME/ci/install_uncrustify.sh + else + newt version + gcc --version + if [ "${TEST}" != "TEST_ALL" ]; then arm-none-eabi-gcc --version; fi + cp -R $HOME/ci/mynewt-nimble-project.yml project.yml + mkdir -p targets + cp -R $HOME/ci/mynewt-nimble-targets targets + $HOME/ci/prepare_test.sh $VM_AMOUNT + mkdir -p repos && pushd repos/ + git clone --depth=1 https://github.com/apache/mynewt-core apache-mynewt-core + git clone --depth=1 https://github.com/JuulLabs-OSS/mcuboot mcuboot + git clone --depth=1 https://github.com/apache/mynewt-mcumgr apache-mynewt-mcumgr + popd + fi + +script: + - | + if [ "${TEST}" == "STYLE" ]; then + python3 $HOME/ci/check_style.py + else + $HOME/ci/run_test.sh + fi + +cache: + directories: + - $HOME/TOOLCHAIN + - $HOME/Library/Caches/Homebrew diff --git a/lib/bt/host/nimble/nimble/CODING_STANDARDS.md b/lib/bt/host/nimble/nimble/CODING_STANDARDS.md new file mode 100644 index 00000000..d14b9fdb --- /dev/null +++ b/lib/bt/host/nimble/nimble/CODING_STANDARDS.md @@ -0,0 +1,267 @@ +# Coding Style for Apache NimBLE + +Apache NimBLE project is part of Apache Mynewt projct and follows its coding +style. + +# Coding Style for Apache Mynewt Core + +This document is meant to define the coding style for Apache Mynewt, and +all subprojects of Apache Mynewt. This covers C and Assembly coding +conventions, *only*. Other languages (such as Go), have their own +coding conventions. + +## Headers + +* All files that are newly written, should have the Apache License clause +at the top of them. + +* For files that are copied from another source, but contain an Apache +compatible license, the original license header shall be maintained. + +* For more information on applying the Apache license, the definitive +source is here: http://www.apache.org/dev/apply-license.html + +* The Apache License clause for the top of files is as follows: + +```no-highlight +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +``` + +## Whitespace and Braces + +* Code must be indented to 4 spaces, tabs should not be used. + +* Do not add whitespace at the end of a line. + +* Put space after keywords (for, if, return, switch, while). + +* for, else, if, while statements must have braces around their +code blocks, i.e., do: + +``` + if (x) { + assert(0); + } else { + assert(0); + } +``` + +Not: + +``` + if (x) + assert(0); + else + assert(0); +``` + +* Braces for statements must be on the same line as the statement. Good: + +``` + for (i = 0; i < 10; i++) { + if (i == 5) { + break; + } else { + continue; + } + } +``` + +Not: + +``` + for (i = 0; i < 10; i++) + { <-- brace must be on same line as for + if (i == 5) { + break; + } <-- no new line between else + else { + continue; + } + } +``` + +* After a function declaration, the braces should be on a newline, i.e. do: + +``` + static void * + function(int var1, int var2) + { +``` + +not: + +``` + static void * + function(int var1, int var2) { +``` + +## Line Length and Wrap + +* Line length should never exceed 79 columns. + +* When you have to wrap a long statement, put the operator at the end of the + line. i.e.: + +``` + if (x && + y == 10 && + b) +``` + +Not: + +``` + if (x + && y == 10 + && b) +``` + +## Comments + +* No C++ style comments allowed. + +* When using a single line comment, put it above the line of code that you +intend to comment, i.e., do: + +``` + /* check variable */ + if (a) { +``` + +Not: + +``` + if (a) { /* check variable */ +``` + + +* All public APIs should be commented with Doxygen style comments describing +purpose, parameters and return values. Private APIs need not be documented. + + +## Header files + +* Header files must contain the following structure: + * Apache License (see above) + * ```#ifdef``` aliasing, to prevent multiple includes + * ```#include``` directives for other required header files + * ```#ifdef __cplusplus``` wrappers to maintain C++ friendly APIs + * Contents of the header file + +* ```#ifdef``` aliasing, shall be in the following format, where +the package name is "os" and the file name is "callout.h": + +```no-highlight +#ifndef _OS_CALLOUT_H +#define _OS_CALLOUT_H +``` + +* ```#include``` directives must happen prior to the cplusplus +wrapper. + +* The cplusplus wrapper must have the following format, and precedes +any contents of the header file: + +```no-highlight +#ifdef __cplusplus +#extern "C" { +##endif +``` + +## Naming + +* Names of functions, structures and variables must be in all lowercase. + +* Names should be as short as possible, but no shorter. + +* Globally visible names must be prefixed with the name of the module, +followed by the '_' character, i.e.: + +``` + os_callout_init(&c) +``` + +Not: + +``` + callout_init(c) +``` + +## Functions + +* No spaces after function names when calling a function, i.e, do: + +``` + rc = function(a) +``` + +Not: + +``` + rc = function (a) +``` + + +* Arguments to function calls should have spaces between the comma, i.e. do: + +``` + rc = function(a, b) +``` + +Not: + +``` + rc = function(a,b) +``` + +* The function type must be on a line by itself preceding the function, i.e. do: + +``` + static void * + function(int var1, int var2) + { +``` + +Not: + +``` + static void *function(int var1, int var2) + { +``` + +* In general, for functions that return values that denote success or error, 0 +shall be success, and non-zero shall be the failure code. + +## Variables and Macros + +* Do not use typedefs for structures. This makes it impossible for +applications to use pointers to those structures opaquely. + +* typedef may be used for non-structure types, where it is beneficial to +hide or alias the underlying type used (e.g. ```os_time_t```.) Indicate +typedefs by applying the ```_t``` marker to them. + +* Place all function-local variable definitions at the top of the function body, before any statements. + +## Compiler Directives + +* Code must compile cleanly with -Wall enabled. + diff --git a/lib/bt/host/nimble/nimble/LICENSE b/lib/bt/host/nimble/nimble/LICENSE new file mode 100644 index 00000000..08b9b218 --- /dev/null +++ b/lib/bt/host/nimble/nimble/LICENSE @@ -0,0 +1,217 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +This product bundles queue.h 8.5, which is available under the "3-clause BSD" +license. For details, see porting/nimble/include/os/queue.h + +This product partly derives from FreeBSD, which is available under the +"3-clause BSD" license. For details, see: + * porting/nimble/src/os_mbuf.c + +This product bundles Gary S. Brown's CRC32 implementation, which is available +under the following license: + COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or + code or tables extracted from it, as desired without restriction. + +This product bundles tinycrypt, which is available under the "3-clause BSD" +license. For details, and bundled files see: + * ext/tinycrypt/LICENSE diff --git a/lib/bt/host/nimble/nimble/NOTICE b/lib/bt/host/nimble/nimble/NOTICE new file mode 100644 index 00000000..3f9a1c8c --- /dev/null +++ b/lib/bt/host/nimble/nimble/NOTICE @@ -0,0 +1,9 @@ +Apache Mynewt NimBLE +Copyright 2015-2022 The Apache Software Foundation +Modifications Copyright 2017-2022 Espressif Systems (Shanghai) CO., LTD. + +This product includes software developed at +The Apache Software Foundation (http://www.apache.org/). + +Portions of this software were developed at +Runtime Inc, copyright 2015. diff --git a/lib/bt/host/nimble/nimble/README.md b/lib/bt/host/nimble/nimble/README.md new file mode 100644 index 00000000..07a893aa --- /dev/null +++ b/lib/bt/host/nimble/nimble/README.md @@ -0,0 +1,172 @@ + + + +Apache Mynewt + + +## Overview + +Apache NimBLE is an open-source Bluetooth 5.1 stack (both Host & Controller) +that completely replaces the proprietary SoftDevice on Nordic chipsets. It is +part of [Apache Mynewt project](https://github.com/apache/mynewt-core). + +Feature highlight: + - Support for 251 byte packet size. + - Support for all 4 roles concurrently - Broadcaster, Observer, Peripheral and Central + - Support for up to 32 simultaneous connections. + - Legacy and SC (secure connections) SMP support (pairing and bonding). + - Advertising Extensions. + - Periodic Advertising. + - Coded (a.k.a. Long Range) and 2M PHYs. + - Bluetooth Mesh. + +## Supported hardware + +Controller supports Nordic nRF51 and nRF52 chipsets. Host runs on any board +and architecture [supported](https://github.com/apache/mynewt-core#overview) +by Apache Mynewt OS. + + +## Browsing + +If you are browsing around the source tree, and want to see some of the +major functional chunks, here are a few pointers: + +- nimble/controller: Contains code for controller including Link Layer and HCI implementation +([controller](https://github.com/apache/mynewt-nimble/tree/master/nimble/controller)) + +- nimble/drivers: Contains drivers for supported radio transceivers (Nordic nRF51 and nRF52) +([drivers](https://github.com/apache/mynewt-nimble/tree/master/nimble/drivers)) + +- nimble/host: Contains code for host subsystem. This includes protocols like +L2CAP and ATT, support for HCI commands and events, Generic Access Profile (GAP), +Generic Attribute Profile (GATT) and Security Manager (SM). +([host](https://github.com/apache/mynewt-nimble/tree/master/nimble/host)) + +- nimble/host/mesh: Contains code for Bluetooth Mesh subsystem. +([mesh](https://github.com/apache/mynewt-nimble/tree/master/nimble/host/mesh)) + +- nimble/transport: Contains code for supported transport protocols between host +and controller. This includes UART, emSPI and RAM (used in combined build when +host and controller run on same CPU) +([transport](https://github.com/apache/mynewt-nimble/tree/master/nimble/transport)) + +- porting: Contains implementation of NimBLE Porting Layer (NPL) for supported +operating systems +([porting](https://github.com/apache/mynewt-nimble/tree/master/porting)) + +- ext: Contains external libraries used by NimBLE. Those are used if not +provided by OS +([ext](https://github.com/apache/mynewt-nimble/tree/master/ext)) + +- kernel: Contains the core of the RTOS ([kernel/os](https://github.com/apache/mynewt-core/tree/master/kernel/os)) + +## Sample Applications + +There are also some sample applications that show how to Apache Mynewt NimBLE +stack. These sample applications are located in the `apps/` directory of +Apache Mynewt [repo](https://github.com/apache/mynewt-core). Some examples: + +* [blecent](https://github.com/apache/mynewt-nimble/tree/master/apps/blecent): +A basic central device with no user interface. This application scans for +a peripheral that supports the alert notification service (ANS). Upon +discovering such a peripheral, blecent connects and performs a characteristic +read, characteristic write, and notification subscription. +* [blehci](https://github.com/apache/mynewt-nimble/tree/master/apps/blehci): +Implements a BLE controller-only application. A separate host-only +implementation, such as Linux's BlueZ, can interface with this application via +HCI over UART. +* [bleprph](https://github.com/apache/mynewt-nimble/tree/master/apps/bleprph): An + implementation of a minimal BLE peripheral. +* [btshell](https://github.com/apache/mynewt-nimble/tree/master/apps/btshell): A + shell-like application allowing to configure and use most of NimBLE + functionality from command line. +* [bleuart](https://github.com/apache/mynewt-core/tree/master/apps/bleuart): +Implements a simple BLE peripheral that supports the Nordic +UART / Serial Port Emulation service +(https://developer.nordicsemi.com/nRF5_SDK/nRF51_SDK_v8.x.x/doc/8.0.0/s110/html/a00072.html). + +# Getting Help + +If you are having trouble using or contributing to Apache Mynewt NimBLE, or just +want to talk to a human about what you're working on, you can contact us via the +[developers mailing list](mailto:dev@mynewt.apache.org). + +Although not a formal channel, you can also find a number of core developers +on the #mynewt channel on Freenode IRC or #general channel on [Mynewt Slack](https://mynewt.slack.com/join/shared_invite/enQtNjA1MTg0NzgyNzg3LTcyMmZiOGQzOGMxM2U4ODFmMTIwNjNmYTE5Y2UwYjQwZWIxNTE0MTUzY2JmMTEzOWFjYWZkNGM0YmM4MzAxNWQ) + +Also, be sure to checkout the [Frequently Asked Questions](https://mynewt.apache.org/faq/answers) +for some help troubleshooting first. + +# Contributing + +Anybody who works with Apache Mynewt can be a contributing member of the +community that develops and deploys it. The process of releasing an operating +system for microcontrollers is never done: and we welcome your contributions +to that effort. + +More information can be found at the Community section of the Apache Mynewt +website, located [here](https://mynewt.apache.org/community). + +## Pull Requests + +Apache Mynewt welcomes pull request via Github. Discussions are done on Github, +but depending on the topic, can also be relayed to the official Apache Mynewt +developer mailing list dev@mynewt.apache.org. + +If you are suggesting a new feature, please email the developer list directly, +with a description of the feature you are planning to work on. + +## Filing Bugs + +Bugs can be filed on the +[Apache Mynewt NimBLE Issues](https://github.com/apache/mynewt-nimble/issues). +Please label the issue as a "Bug". + +Where possible, please include a self-contained reproduction case! + +## Feature Requests + +Feature requests should also be filed on the +[Apache Mynewt NimBLE Bug Tracker](https://github.com/apache/mynewt-nimble/issues). +Please label the issue as a "Feature" or "Enhancement" depending on the scope. + +## Writing Tests + +We love getting newt tests! Apache Mynewt is a huge undertaking, and improving +code coverage is a win for every Apache Mynewt user. + + + +# License + +The code in this repository is all under either the Apache 2 license, or a +license compatible with the Apache 2 license. See the LICENSE file for more +information. diff --git a/lib/bt/host/nimble/nimble/RELEASE_NOTES.md b/lib/bt/host/nimble/nimble/RELEASE_NOTES.md new file mode 100644 index 00000000..cde2ce27 --- /dev/null +++ b/lib/bt/host/nimble/nimble/RELEASE_NOTES.md @@ -0,0 +1,25 @@ +# RELEASE NOTES + +20 April 2022 - Apache NimBLE v1.5.0 + +For full release notes, please visit the +[Apache Mynewt Wiki](https://cwiki.apache.org/confluence/display/MYNEWT/Release+Notes). + +Apache NimBLE is an open-source Bluetooth 5.3 stack (both Host & Controller) that completely +replaces the proprietary SoftDevice on Nordic chipsets. + +New features in this version of NimBLE include: + +* Fake dual-mode option for controller +* LLCP tracing via HCI events +* Code size optimization for disabled GAP roles +* Support for PA/LNA +* LE Secure Connections Only mode +* Support for Bluetooth Core Specification 5.3 +* Connection subrating +* BabbleSim support +* Various bugfixes and improvements + +If working on next-generation RTOS and Bluetooth protocol stack +sounds exciting to you, get in touch, by sending a mail to the Apache Mynewt +Developer's list, dev@mynewt.apache.org. diff --git a/lib/bt/host/nimble/nimble/apps/README.md b/lib/bt/host/nimble/nimble/apps/README.md new file mode 100644 index 00000000..f36a836a --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/README.md @@ -0,0 +1,54 @@ + + +# Sample applications + +## advertiser + +This is the simplest example of advertising. Application sets NRPA, configures +advertisement parameters: general discoverable and not connectable and fills +advertisement fields. Transmited data contains only flags, tx power level and +device name, which fits in 31B limit of single package. With this data set, +device advertises for 10 seconds, terminates advertisement and repeats process +again infinitely. + +## scanner + +This application shows how to perform simple scan. Device performs discovery +procedure, during which receives advertising reports (if any devices are +advertising nearby). These reports are being parsed and results are printed to +serial port. Applicaton starts new discovery every second. + +## peripheral + +Peripheral application is based on advertiser, but has added capability of +connecting with other devices. As peripheral, device doesn't initiate any +connection by itself; instead, advertises infinitely and accepts any connection +request it receives. Because we cannot use any 16 or 32 bit UUIDs, as these are +reserved by Bluetooth SIG, we are forced to use 128-bit one. Including such +long UUID in advertising data consumes large part of available payload, so this +data is split in advertising data and response data. + +## central + +This application works in pair with peripheral. It's based on scanner +application - the difference is, that if there was detected device with UUID +fitting to the one predefined in central application, connection is initiated. \ No newline at end of file diff --git a/lib/bt/host/nimble/nimble/apps/advertiser/pkg.yml b/lib/bt/host/nimble/nimble/apps/advertiser/pkg.yml new file mode 100644 index 00000000..ad6f133d --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/advertiser/pkg.yml @@ -0,0 +1,33 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +pkg.name: "apps/advertiser" +pkg.type: app +pkg.description: "Basic advertiser application" +pkg.author: "Krzysztof Kopyściński " + +pkg.deps: + - "@apache-mynewt-core/kernel/os" + - "@apache-mynewt-core/sys/console/full" + - "@apache-mynewt-core/sys/log/full" + - "@apache-mynewt-core/sys/stats/full" + - "@apache-mynewt-core/sys/log/modlog" + - "@apache-mynewt-nimble/nimble/host" + - "@apache-mynewt-nimble/nimble/host/util" + - "@apache-mynewt-nimble/nimble/host/services/gap" diff --git a/lib/bt/host/nimble/nimble/apps/advertiser/src/main.c b/lib/bt/host/nimble/nimble/apps/advertiser/src/main.c new file mode 100644 index 00000000..486d5c59 --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/advertiser/src/main.c @@ -0,0 +1,136 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#include +#include +#include + +#include "os/os.h" +#include "sysinit/sysinit.h" +#include "log/log.h" +#include "host/ble_hs.h" +#include "host/util/util.h" +#include "services/gap/ble_svc_gap.h" + +static const char *device_name = "Apache Mynewt"; + +/* adv_event() calls advertise(), so forward declaration is required */ +static void advertise(void); + +static void +set_ble_addr(void) +{ + int rc; + ble_addr_t addr; + + /* generate new non-resolvable private address */ + rc = ble_hs_id_gen_rnd(1, &addr); + assert(rc == 0); + + /* set generated address */ + rc = ble_hs_id_set_rnd(addr.val); + assert(rc == 0); +} + +static int +adv_event(struct ble_gap_event *event, void *arg) +{ + switch (event->type) { + case BLE_GAP_EVENT_ADV_COMPLETE: + MODLOG_DFLT(INFO, "Advertising completed, termination code: %d\n", + event->adv_complete.reason); + advertise(); + return 0; + default: + MODLOG_DFLT(ERROR, "Advertising event not handled\n"); + return 0; + } +} + +static void +advertise(void) +{ + int rc; + struct ble_gap_adv_params adv_params; + struct ble_hs_adv_fields fields; + + /* set adv parameters */ + memset(&adv_params, 0, sizeof(adv_params)); + adv_params.conn_mode = BLE_GAP_CONN_MODE_NON; + adv_params.disc_mode = BLE_GAP_DISC_MODE_GEN; + + memset(&fields, 0, sizeof(fields)); + + /* Fill the fields with advertising data - flags, tx power level, name */ + fields.flags = BLE_HS_ADV_F_DISC_GEN; + fields.tx_pwr_lvl_is_present = 1; + fields.tx_pwr_lvl = BLE_HS_ADV_TX_PWR_LVL_AUTO; + fields.name = (uint8_t *)device_name; + fields.name_len = strlen(device_name); + fields.name_is_complete = 1; + + rc = ble_gap_adv_set_fields(&fields); + assert(rc == 0); + + MODLOG_DFLT(INFO, "Starting advertising...\n"); + + /* As own address type we use hard-coded value, because we generate + NRPA and by definition it's random */ + rc = ble_gap_adv_start(BLE_OWN_ADDR_RANDOM, NULL, 10000, + &adv_params, adv_event, NULL); + assert(rc == 0); +} + +static void +on_sync(void) +{ + set_ble_addr(); + + /* begin advertising */ + advertise(); +} + +static void +on_reset(int reason) +{ + MODLOG_DFLT(INFO, "Resetting state; reason=%d\n", reason); +} + +int +main(int argc, char **argv) +{ + int rc; + + /* Initialize all packages. */ + sysinit(); + + ble_hs_cfg.sync_cb = on_sync; + ble_hs_cfg.reset_cb = on_reset; + + rc = ble_svc_gap_device_name_set(device_name); + assert(rc == 0); + + /* As the last thing, process events from default event queue. */ + while (1) { + os_eventq_run(os_eventq_dflt_get()); + } + + return 0; +} diff --git a/lib/bt/host/nimble/nimble/apps/advertiser/syscfg.yml b/lib/bt/host/nimble/nimble/apps/advertiser/syscfg.yml new file mode 100644 index 00000000..963839fc --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/advertiser/syscfg.yml @@ -0,0 +1,23 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + + +syscfg.vals: + BLE_ROLE_BROADCASTER: 1 + BLE_ROLE_CENTRAL: 0 + BLE_ROLE_OBSERVER: 0 + BLE_ROLE_PERIPHERAL: 0 diff --git a/lib/bt/host/nimble/nimble/apps/blecent/pkg.yml b/lib/bt/host/nimble/nimble/apps/blecent/pkg.yml new file mode 100644 index 00000000..86e3f016 --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/blecent/pkg.yml @@ -0,0 +1,35 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +pkg.name: apps/blecent +pkg.type: app +pkg.description: Simple BLE central application. +pkg.author: "Apache Mynewt " +pkg.homepage: "http://mynewt.apache.org/" +pkg.keywords: + +pkg.deps: + - "@apache-mynewt-core/kernel/os" + - "@apache-mynewt-core/sys/console/full" + - "@apache-mynewt-core/sys/log/full" + - "@apache-mynewt-core/sys/log/modlog" + - "@apache-mynewt-core/sys/stats/full" + - nimble/host + - nimble/host/util + - nimble/host/services/gap + - nimble/host/services/gatt + - nimble/host/store/config diff --git a/lib/bt/host/nimble/nimble/apps/blecent/src/blecent.h b/lib/bt/host/nimble/nimble/apps/blecent/src/blecent.h new file mode 100644 index 00000000..a694f402 --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/blecent/src/blecent.h @@ -0,0 +1,111 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_BLECENT_ +#define H_BLECENT_ + +#include "os/mynewt.h" +#include "modlog/modlog.h" +#ifdef __cplusplus +extern "C" { +#endif + +struct ble_hs_adv_fields; +struct ble_gap_conn_desc; +struct ble_hs_cfg; +union ble_store_value; +union ble_store_key; + +#define BLECENT_SVC_ALERT_UUID 0x1811 +#define BLECENT_CHR_SUP_NEW_ALERT_CAT_UUID 0x2A47 +#define BLECENT_CHR_NEW_ALERT 0x2A46 +#define BLECENT_CHR_SUP_UNR_ALERT_CAT_UUID 0x2A48 +#define BLECENT_CHR_UNR_ALERT_STAT_UUID 0x2A45 +#define BLECENT_CHR_ALERT_NOT_CTRL_PT 0x2A44 + +/** Misc. */ +void print_bytes(const uint8_t *bytes, int len); +void print_mbuf(const struct os_mbuf *om); +char *addr_str(const void *addr); +void print_uuid(const ble_uuid_t *uuid); +void print_conn_desc(const struct ble_gap_conn_desc *desc); +void print_adv_fields(const struct ble_hs_adv_fields *fields); + +/** Peer. */ +struct peer_dsc { + SLIST_ENTRY(peer_dsc) next; + struct ble_gatt_dsc dsc; +}; +SLIST_HEAD(peer_dsc_list, peer_dsc); + +struct peer_chr { + SLIST_ENTRY(peer_chr) next; + struct ble_gatt_chr chr; + + struct peer_dsc_list dscs; +}; +SLIST_HEAD(peer_chr_list, peer_chr); + +struct peer_svc { + SLIST_ENTRY(peer_svc) next; + struct ble_gatt_svc svc; + + struct peer_chr_list chrs; +}; +SLIST_HEAD(peer_svc_list, peer_svc); + +struct peer; +typedef void peer_disc_fn(const struct peer *peer, int status, void *arg); + +struct peer { + SLIST_ENTRY(peer) next; + + uint16_t conn_handle; + + /** List of discovered GATT services. */ + struct peer_svc_list svcs; + + /** Keeps track of where we are in the service discovery process. */ + uint16_t disc_prev_chr_val; + struct peer_svc *cur_svc; + + /** Callback that gets executed when service discovery completes. */ + peer_disc_fn *disc_cb; + void *disc_cb_arg; +}; + +int peer_disc_all(uint16_t conn_handle, peer_disc_fn *disc_cb, + void *disc_cb_arg); +const struct peer_dsc * +peer_dsc_find_uuid(const struct peer *peer, const ble_uuid_t *svc_uuid, + const ble_uuid_t *chr_uuid, const ble_uuid_t *dsc_uuid); +const struct peer_chr * +peer_chr_find_uuid(const struct peer *peer, const ble_uuid_t *svc_uuid, + const ble_uuid_t *chr_uuid); +const struct peer_svc * +peer_svc_find_uuid(const struct peer *peer, const ble_uuid_t *uuid); +int peer_delete(uint16_t conn_handle); +int peer_add(uint16_t conn_handle); +int peer_init(int max_peers, int max_svcs, int max_chrs, int max_dscs); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/bt/host/nimble/nimble/apps/blecent/src/main.c b/lib/bt/host/nimble/nimble/apps/blecent/src/main.c new file mode 100644 index 00000000..7f1c5f15 --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/blecent/src/main.c @@ -0,0 +1,538 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#include +#include "os/mynewt.h" +#include "bsp/bsp.h" + +/* BLE */ +#include "nimble/ble.h" +#include "host/ble_hs.h" +#include "host/util/util.h" + +/* Mandatory services. */ +#include "services/gap/ble_svc_gap.h" +#include "services/gatt/ble_svc_gatt.h" + +/* Application-specified header. */ +#include "blecent.h" + +static int blecent_gap_event(struct ble_gap_event *event, void *arg); + +/** + * Application callback. Called when the read of the ANS Supported New Alert + * Category characteristic has completed. + */ +static int +blecent_on_read(uint16_t conn_handle, + const struct ble_gatt_error *error, + struct ble_gatt_attr *attr, + void *arg) +{ + MODLOG_DFLT(INFO, "Read complete; status=%d conn_handle=%d", error->status, + conn_handle); + if (error->status == 0) { + MODLOG_DFLT(INFO, " attr_handle=%d value=", attr->handle); + print_mbuf(attr->om); + } + MODLOG_DFLT(INFO, "\n"); + + return 0; +} + +/** + * Application callback. Called when the write to the ANS Alert Notification + * Control Point characteristic has completed. + */ +static int +blecent_on_write(uint16_t conn_handle, + const struct ble_gatt_error *error, + struct ble_gatt_attr *attr, + void *arg) +{ + MODLOG_DFLT(INFO, + "Write complete; status=%d conn_handle=%d attr_handle=%d\n", + error->status, conn_handle, attr->handle); + + return 0; +} + +/** + * Application callback. Called when the attempt to subscribe to notifications + * for the ANS Unread Alert Status characteristic has completed. + */ +static int +blecent_on_subscribe(uint16_t conn_handle, + const struct ble_gatt_error *error, + struct ble_gatt_attr *attr, + void *arg) +{ + MODLOG_DFLT(INFO, "Subscribe complete; status=%d conn_handle=%d " + "attr_handle=%d\n", + error->status, conn_handle, attr->handle); + + return 0; +} + +/** + * Performs three concurrent GATT operations against the specified peer: + * 1. Reads the ANS Supported New Alert Category characteristic. + * 2. Writes the ANS Alert Notification Control Point characteristic. + * 3. Subscribes to notifications for the ANS Unread Alert Status + * characteristic. + * + * If the peer does not support a required service, characteristic, or + * descriptor, then the peer lied when it claimed support for the alert + * notification service! When this happens, or if a GATT procedure fails, + * this function immediately terminates the connection. + */ +static void +blecent_read_write_subscribe(const struct peer *peer) +{ + const struct peer_chr *chr; + const struct peer_dsc *dsc; + uint8_t value[2]; + int rc; + + /* Read the supported-new-alert-category characteristic. */ + chr = peer_chr_find_uuid(peer, + BLE_UUID16_DECLARE(BLECENT_SVC_ALERT_UUID), + BLE_UUID16_DECLARE(BLECENT_CHR_SUP_NEW_ALERT_CAT_UUID)); + if (chr == NULL) { + MODLOG_DFLT(ERROR, "Error: Peer doesn't support the Supported New " + "Alert Category characteristic\n"); + goto err; + } + + rc = ble_gattc_read(peer->conn_handle, chr->chr.val_handle, + blecent_on_read, NULL); + if (rc != 0) { + MODLOG_DFLT(ERROR, "Error: Failed to read characteristic; rc=%d\n", + rc); + goto err; + } + + /* Write two bytes (99, 100) to the alert-notification-control-point + * characteristic. + */ + chr = peer_chr_find_uuid(peer, + BLE_UUID16_DECLARE(BLECENT_SVC_ALERT_UUID), + BLE_UUID16_DECLARE(BLECENT_CHR_ALERT_NOT_CTRL_PT)); + if (chr == NULL) { + MODLOG_DFLT(ERROR, "Error: Peer doesn't support the Alert " + "Notification Control Point characteristic\n"); + goto err; + } + + value[0] = 99; + value[1] = 100; + rc = ble_gattc_write_flat(peer->conn_handle, chr->chr.val_handle, + value, sizeof value, blecent_on_write, NULL); + if (rc != 0) { + MODLOG_DFLT(ERROR, "Error: Failed to write characteristic; rc=%d\n", + rc); + } + + /* Subscribe to notifications for the Unread Alert Status characteristic. + * A central enables notifications by writing two bytes (1, 0) to the + * characteristic's client-characteristic-configuration-descriptor (CCCD). + */ + dsc = peer_dsc_find_uuid(peer, + BLE_UUID16_DECLARE(BLECENT_SVC_ALERT_UUID), + BLE_UUID16_DECLARE(BLECENT_CHR_UNR_ALERT_STAT_UUID), + BLE_UUID16_DECLARE(BLE_GATT_DSC_CLT_CFG_UUID16)); + if (dsc == NULL) { + MODLOG_DFLT(ERROR, "Error: Peer lacks a CCCD for the Unread Alert " + "Status characteristic\n"); + goto err; + } + + value[0] = 1; + value[1] = 0; + rc = ble_gattc_write_flat(peer->conn_handle, dsc->dsc.handle, + value, sizeof value, blecent_on_subscribe, NULL); + if (rc != 0) { + MODLOG_DFLT(ERROR, "Error: Failed to subscribe to characteristic; " + "rc=%d\n", rc); + goto err; + } + + return; + +err: + /* Terminate the connection. */ + ble_gap_terminate(peer->conn_handle, BLE_ERR_REM_USER_CONN_TERM); +} + +/** + * Called when service discovery of the specified peer has completed. + */ +static void +blecent_on_disc_complete(const struct peer *peer, int status, void *arg) +{ + + if (status != 0) { + /* Service discovery failed. Terminate the connection. */ + MODLOG_DFLT(ERROR, "Error: Service discovery failed; status=%d " + "conn_handle=%d\n", status, peer->conn_handle); + ble_gap_terminate(peer->conn_handle, BLE_ERR_REM_USER_CONN_TERM); + return; + } + + /* Service discovery has completed successfully. Now we have a complete + * list of services, characteristics, and descriptors that the peer + * supports. + */ + MODLOG_DFLT(ERROR, "Service discovery complete; status=%d " + "conn_handle=%d\n", status, peer->conn_handle); + + /* Now perform three concurrent GATT procedures against the peer: read, + * write, and subscribe to notifications. + */ + blecent_read_write_subscribe(peer); +} + +/** + * Initiates the GAP general discovery procedure. + */ +static void +blecent_scan(void) +{ + uint8_t own_addr_type; + struct ble_gap_disc_params disc_params; + int rc; + + /* Figure out address to use while advertising (no privacy for now) */ + rc = ble_hs_id_infer_auto(0, &own_addr_type); + if (rc != 0) { + MODLOG_DFLT(ERROR, "error determining address type; rc=%d\n", rc); + return; + } + + /* Tell the controller to filter duplicates; we don't want to process + * repeated advertisements from the same device. + */ + disc_params.filter_duplicates = 1; + + /** + * Perform a passive scan. I.e., don't send follow-up scan requests to + * each advertiser. + */ + disc_params.passive = 1; + + /* Use defaults for the rest of the parameters. */ + disc_params.itvl = 0; + disc_params.window = 0; + disc_params.filter_policy = 0; + disc_params.limited = 0; + + rc = ble_gap_disc(own_addr_type, BLE_HS_FOREVER, &disc_params, + blecent_gap_event, NULL); + if (rc != 0) { + MODLOG_DFLT(ERROR, "Error initiating GAP discovery procedure; rc=%d\n", + rc); + } +} + +/** + * Indicates whether we should tre to connect to the sender of the specified + * advertisement. The function returns a positive result if the device + * advertises connectability and support for the Alert Notification service. + */ +static int +blecent_should_connect(const struct ble_gap_disc_desc *disc) +{ + struct ble_hs_adv_fields fields; + int rc; + int i; + + /* The device has to be advertising connectability. */ + if (disc->event_type != BLE_HCI_ADV_RPT_EVTYPE_ADV_IND && + disc->event_type != BLE_HCI_ADV_RPT_EVTYPE_DIR_IND) { + + return 0; + } + + rc = ble_hs_adv_parse_fields(&fields, disc->data, disc->length_data); + if (rc != 0) { + return rc; + } + + /* The device has to advertise support for the Alert Notification + * service (0x1811). + */ + for (i = 0; i < fields.num_uuids16; i++) { + if (ble_uuid_u16(&fields.uuids16[i].u) == BLECENT_SVC_ALERT_UUID) { + return 1; + } + } + + return 0; +} + +/** + * Connects to the sender of the specified advertisement of it looks + * interesting. A device is "interesting" if it advertises connectability and + * support for the Alert Notification service. + */ +static void +blecent_connect_if_interesting(const struct ble_gap_disc_desc *disc) +{ + uint8_t own_addr_type; + int rc; + + /* Don't do anything if we don't care about this advertiser. */ + if (!blecent_should_connect(disc)) { + return; + } + + /* Scanning must be stopped before a connection can be initiated. */ + rc = ble_gap_disc_cancel(); + if (rc != 0) { + MODLOG_DFLT(DEBUG, "Failed to cancel scan; rc=%d\n", rc); + return; + } + + /* Figure out address to use for connect (no privacy for now) */ + rc = ble_hs_id_infer_auto(0, &own_addr_type); + if (rc != 0) { + MODLOG_DFLT(ERROR, "error determining address type; rc=%d\n", rc); + return; + } + + /* Try to connect the the advertiser. Allow 30 seconds (30000 ms) for + * timeout. + */ + rc = ble_gap_connect(own_addr_type, &disc->addr, 30000, NULL, + blecent_gap_event, NULL); + if (rc != 0) { + MODLOG_DFLT(ERROR, "Error: Failed to connect to device; addr_type=%d " + "addr=%s\n; rc=%d", + disc->addr.type, addr_str(disc->addr.val), rc); + return; + } +} + +/** + * The nimble host executes this callback when a GAP event occurs. The + * application associates a GAP event callback with each connection that is + * established. blecent uses the same callback for all connections. + * + * @param event The event being signalled. + * @param arg Application-specified argument; unused by + * blecent. + * + * @return 0 if the application successfully handled the + * event; nonzero on failure. The semantics + * of the return code is specific to the + * particular GAP event being signalled. + */ +static int +blecent_gap_event(struct ble_gap_event *event, void *arg) +{ + struct ble_gap_conn_desc desc; + struct ble_hs_adv_fields fields; + int rc; + + switch (event->type) { + case BLE_GAP_EVENT_DISC: + rc = ble_hs_adv_parse_fields(&fields, event->disc.data, + event->disc.length_data); + if (rc != 0) { + return 0; + } + + /* An advertisment report was received during GAP discovery. */ + print_adv_fields(&fields); + + /* Try to connect to the advertiser if it looks interesting. */ + blecent_connect_if_interesting(&event->disc); + return 0; + + case BLE_GAP_EVENT_CONNECT: + /* A new connection was established or a connection attempt failed. */ + if (event->connect.status == 0) { + /* Connection successfully established. */ + MODLOG_DFLT(INFO, "Connection established "); + + rc = ble_gap_conn_find(event->connect.conn_handle, &desc); + assert(rc == 0); + print_conn_desc(&desc); + MODLOG_DFLT(INFO, "\n"); + + /* Remember peer. */ + rc = peer_add(event->connect.conn_handle); + if (rc != 0) { + MODLOG_DFLT(ERROR, "Failed to add peer; rc=%d\n", rc); + return 0; + } + + /* Perform service discovery. */ + rc = peer_disc_all(event->connect.conn_handle, + blecent_on_disc_complete, NULL); + if (rc != 0) { + MODLOG_DFLT(ERROR, "Failed to discover services; rc=%d\n", rc); + return 0; + } + } else { + /* Connection attempt failed; resume scanning. */ + MODLOG_DFLT(ERROR, "Error: Connection failed; status=%d\n", + event->connect.status); + blecent_scan(); + } + + return 0; + + case BLE_GAP_EVENT_DISCONNECT: + /* Connection terminated. */ + MODLOG_DFLT(INFO, "disconnect; reason=%d ", event->disconnect.reason); + print_conn_desc(&event->disconnect.conn); + MODLOG_DFLT(INFO, "\n"); + + /* Forget about peer. */ + peer_delete(event->disconnect.conn.conn_handle); + + /* Resume scanning. */ + blecent_scan(); + return 0; + + case BLE_GAP_EVENT_DISC_COMPLETE: + MODLOG_DFLT(INFO, "discovery complete; reason=%d\n", + event->disc_complete.reason); + return 0; + + case BLE_GAP_EVENT_ENC_CHANGE: + /* Encryption has been enabled or disabled for this connection. */ + MODLOG_DFLT(INFO, "encryption change event; status=%d ", + event->enc_change.status); + rc = ble_gap_conn_find(event->enc_change.conn_handle, &desc); + assert(rc == 0); + print_conn_desc(&desc); + return 0; + + case BLE_GAP_EVENT_NOTIFY_RX: + /* Peer sent us a notification or indication. */ + MODLOG_DFLT(INFO, "received %s; conn_handle=%d attr_handle=%d " + "attr_len=%d\n", + event->notify_rx.indication ? + "indication" : + "notification", + event->notify_rx.conn_handle, + event->notify_rx.attr_handle, + OS_MBUF_PKTLEN(event->notify_rx.om)); + + /* Attribute data is contained in event->notify_rx.attr_data. */ + return 0; + + case BLE_GAP_EVENT_MTU: + MODLOG_DFLT(INFO, "mtu update event; conn_handle=%d cid=%d mtu=%d\n", + event->mtu.conn_handle, + event->mtu.channel_id, + event->mtu.value); + return 0; + + case BLE_GAP_EVENT_REPEAT_PAIRING: + /* We already have a bond with the peer, but it is attempting to + * establish a new secure link. This app sacrifices security for + * convenience: just throw away the old bond and accept the new link. + */ + + /* Delete the old bond. */ + rc = ble_gap_conn_find(event->repeat_pairing.conn_handle, &desc); + assert(rc == 0); + ble_store_util_delete_peer(&desc.peer_id_addr); + + /* Return BLE_GAP_REPEAT_PAIRING_RETRY to indicate that the host should + * continue with the pairing operation. + */ + return BLE_GAP_REPEAT_PAIRING_RETRY; + + default: + return 0; + } +} + +static void +blecent_on_reset(int reason) +{ + MODLOG_DFLT(ERROR, "Resetting state; reason=%d\n", reason); +} + +static void +blecent_on_sync(void) +{ + int rc; + + /* Make sure we have proper identity address set (public preferred) */ + rc = ble_hs_util_ensure_addr(0); + assert(rc == 0); + + /* Begin scanning for a peripheral to connect to. */ + blecent_scan(); +} + +/** + * main + * + * All application logic and NimBLE host work is performed in default task. + * + * @return int NOTE: this function should never return! + */ +static int +main_fn(int argc, char **argv) +{ + int rc; + + /* Initialize OS */ + sysinit(); + + /* Configure the host. */ + ble_hs_cfg.reset_cb = blecent_on_reset; + ble_hs_cfg.sync_cb = blecent_on_sync; + ble_hs_cfg.store_status_cb = ble_store_util_status_rr; + + /* Initialize data structures to track connected peers. */ + rc = peer_init(MYNEWT_VAL(BLE_MAX_CONNECTIONS), 64, 64, 64); + assert(rc == 0); + + /* Set the default device name. */ + rc = ble_svc_gap_device_name_set("nimble-blecent"); + assert(rc == 0); + + /* os start should never return. If it does, this should be an error */ + while (1) { + os_eventq_run(os_eventq_dflt_get()); + } + + return 0; +} + +int +main(int argc, char **argv) +{ +#if BABBLESIM + extern void bsim_init(int argc, char** argv, void *main_fn); + bsim_init(argc, argv, main_fn); +#else + main_fn(argc, argv); +#endif + + return 0; +} diff --git a/lib/bt/host/nimble/nimble/apps/blecent/src/misc.c b/lib/bt/host/nimble/nimble/apps/blecent/src/misc.c new file mode 100644 index 00000000..6813a122 --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/blecent/src/misc.c @@ -0,0 +1,209 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#include +#include +#include "host/ble_hs.h" +#include "host/ble_uuid.h" +#include "blecent.h" + +/** + * Utility function to log an array of bytes. + */ +void +print_bytes(const uint8_t *bytes, int len) +{ + int i; + + for (i = 0; i < len; i++) { + MODLOG_DFLT(DEBUG, "%s0x%02x", i != 0 ? ":" : "", bytes[i]); + } +} + +void +print_mbuf(const struct os_mbuf *om) +{ + int colon; + + colon = 0; + while (om != NULL) { + if (colon) { + MODLOG_DFLT(DEBUG, ":"); + } else { + colon = 1; + } + print_bytes(om->om_data, om->om_len); + om = SLIST_NEXT(om, om_next); + } +} + +char * +addr_str(const void *addr) +{ + static char buf[6 * 2 + 5 + 1]; + const uint8_t *u8p; + + u8p = addr; + sprintf(buf, "%02x:%02x:%02x:%02x:%02x:%02x", + u8p[5], u8p[4], u8p[3], u8p[2], u8p[1], u8p[0]); + + return buf; +} + +void +print_uuid(const ble_uuid_t *uuid) +{ + char buf[BLE_UUID_STR_LEN]; + + MODLOG_DFLT(DEBUG, "%s", ble_uuid_to_str(uuid, buf)); +} + +/** + * Logs information about a connection to the console. + */ +void +print_conn_desc(const struct ble_gap_conn_desc *desc) +{ + MODLOG_DFLT(DEBUG, "handle=%d our_ota_addr_type=%d our_ota_addr=%s ", + desc->conn_handle, desc->our_ota_addr.type, + addr_str(desc->our_ota_addr.val)); + MODLOG_DFLT(DEBUG, "our_id_addr_type=%d our_id_addr=%s ", + desc->our_id_addr.type, addr_str(desc->our_id_addr.val)); + MODLOG_DFLT(DEBUG, "peer_ota_addr_type=%d peer_ota_addr=%s ", + desc->peer_ota_addr.type, addr_str(desc->peer_ota_addr.val)); + MODLOG_DFLT(DEBUG, "peer_id_addr_type=%d peer_id_addr=%s ", + desc->peer_id_addr.type, addr_str(desc->peer_id_addr.val)); + MODLOG_DFLT(DEBUG, "conn_itvl=%d conn_latency=%d supervision_timeout=%d " + "encrypted=%d authenticated=%d bonded=%d", + desc->conn_itvl, desc->conn_latency, + desc->supervision_timeout, + desc->sec_state.encrypted, + desc->sec_state.authenticated, + desc->sec_state.bonded); +} + + +void +print_adv_fields(const struct ble_hs_adv_fields *fields) +{ + char s[BLE_HS_ADV_MAX_SZ]; + const uint8_t *u8p; + int i; + + if (fields->flags != 0) { + MODLOG_DFLT(DEBUG, " flags=0x%02x\n", fields->flags); + } + + if (fields->uuids16 != NULL) { + MODLOG_DFLT(DEBUG, " uuids16(%scomplete)=", + fields->uuids16_is_complete ? "" : "in"); + for (i = 0; i < fields->num_uuids16; i++) { + print_uuid(&fields->uuids16[i].u); + MODLOG_DFLT(DEBUG, " "); + } + MODLOG_DFLT(DEBUG, "\n"); + } + + if (fields->uuids32 != NULL) { + MODLOG_DFLT(DEBUG, " uuids32(%scomplete)=", + fields->uuids32_is_complete ? "" : "in"); + for (i = 0; i < fields->num_uuids32; i++) { + print_uuid(&fields->uuids32[i].u); + MODLOG_DFLT(DEBUG, " "); + } + MODLOG_DFLT(DEBUG, "\n"); + } + + if (fields->uuids128 != NULL) { + MODLOG_DFLT(DEBUG, " uuids128(%scomplete)=", + fields->uuids128_is_complete ? "" : "in"); + for (i = 0; i < fields->num_uuids128; i++) { + print_uuid(&fields->uuids128[i].u); + MODLOG_DFLT(DEBUG, " "); + } + MODLOG_DFLT(DEBUG, "\n"); + } + + if (fields->name != NULL) { + assert(fields->name_len < sizeof s - 1); + memcpy(s, fields->name, fields->name_len); + s[fields->name_len] = '\0'; + MODLOG_DFLT(DEBUG, " name(%scomplete)=%s\n", + fields->name_is_complete ? "" : "in", s); + } + + if (fields->tx_pwr_lvl_is_present) { + MODLOG_DFLT(DEBUG, " tx_pwr_lvl=%d\n", fields->tx_pwr_lvl); + } + + if (fields->slave_itvl_range != NULL) { + MODLOG_DFLT(DEBUG, " slave_itvl_range="); + print_bytes(fields->slave_itvl_range, BLE_HS_ADV_SLAVE_ITVL_RANGE_LEN); + MODLOG_DFLT(DEBUG, "\n"); + } + + if (fields->svc_data_uuid16 != NULL) { + MODLOG_DFLT(DEBUG, " svc_data_uuid16="); + print_bytes(fields->svc_data_uuid16, fields->svc_data_uuid16_len); + MODLOG_DFLT(DEBUG, "\n"); + } + + if (fields->public_tgt_addr != NULL) { + MODLOG_DFLT(DEBUG, " public_tgt_addr="); + u8p = fields->public_tgt_addr; + for (i = 0; i < fields->num_public_tgt_addrs; i++) { + MODLOG_DFLT(DEBUG, "public_tgt_addr=%s ", addr_str(u8p)); + u8p += BLE_HS_ADV_PUBLIC_TGT_ADDR_ENTRY_LEN; + } + MODLOG_DFLT(DEBUG, "\n"); + } + + if (fields->appearance_is_present) { + MODLOG_DFLT(DEBUG, " appearance=0x%04x\n", fields->appearance); + } + + if (fields->adv_itvl_is_present) { + MODLOG_DFLT(DEBUG, " adv_itvl=0x%04x\n", fields->adv_itvl); + } + + if (fields->svc_data_uuid32 != NULL) { + MODLOG_DFLT(DEBUG, " svc_data_uuid32="); + print_bytes(fields->svc_data_uuid32, fields->svc_data_uuid32_len); + MODLOG_DFLT(DEBUG, "\n"); + } + + if (fields->svc_data_uuid128 != NULL) { + MODLOG_DFLT(DEBUG, " svc_data_uuid128="); + print_bytes(fields->svc_data_uuid128, fields->svc_data_uuid128_len); + MODLOG_DFLT(DEBUG, "\n"); + } + + if (fields->uri != NULL) { + MODLOG_DFLT(DEBUG, " uri="); + print_bytes(fields->uri, fields->uri_len); + MODLOG_DFLT(DEBUG, "\n"); + } + + if (fields->mfg_data != NULL) { + MODLOG_DFLT(DEBUG, " mfg_data="); + print_bytes(fields->mfg_data, fields->mfg_data_len); + MODLOG_DFLT(DEBUG, "\n"); + } +} diff --git a/lib/bt/host/nimble/nimble/apps/blecent/src/peer.c b/lib/bt/host/nimble/nimble/apps/blecent/src/peer.c new file mode 100644 index 00000000..aeca7d90 --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/blecent/src/peer.c @@ -0,0 +1,807 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#include +#include "host/ble_hs.h" +#include "blecent.h" + +static void *peer_svc_mem; +static struct os_mempool peer_svc_pool; + +static void *peer_chr_mem; +static struct os_mempool peer_chr_pool; + +static void *peer_dsc_mem; +static struct os_mempool peer_dsc_pool; + +static void *peer_mem; +static struct os_mempool peer_pool; +static SLIST_HEAD(, peer) peers; + +static struct peer_svc * +peer_svc_find_range(struct peer *peer, uint16_t attr_handle); +static struct peer_svc * +peer_svc_find(struct peer *peer, uint16_t svc_start_handle, + struct peer_svc **out_prev); +int +peer_svc_is_empty(const struct peer_svc *svc); + +uint16_t +chr_end_handle(const struct peer_svc *svc, const struct peer_chr *chr); +int +chr_is_empty(const struct peer_svc *svc, const struct peer_chr *chr); +static struct peer_chr * +peer_chr_find(const struct peer_svc *svc, uint16_t chr_def_handle, + struct peer_chr **out_prev); +static void +peer_disc_chrs(struct peer *peer); + +static int +peer_dsc_disced(uint16_t conn_handle, const struct ble_gatt_error *error, + uint16_t chr_val_handle, const struct ble_gatt_dsc *dsc, + void *arg); + +static struct peer * +peer_find(uint16_t conn_handle) +{ + struct peer *peer; + + SLIST_FOREACH(peer, &peers, next) { + if (peer->conn_handle == conn_handle) { + return peer; + } + } + + return NULL; +} + +static void +peer_disc_complete(struct peer *peer, int rc) +{ + peer->disc_prev_chr_val = 0; + + /* Notify caller that discovery has completed. */ + if (peer->disc_cb != NULL) { + peer->disc_cb(peer, rc, peer->disc_cb_arg); + } +} + +static struct peer_dsc * +peer_dsc_find_prev(const struct peer_chr *chr, uint16_t dsc_handle) +{ + struct peer_dsc *prev; + struct peer_dsc *dsc; + + prev = NULL; + SLIST_FOREACH(dsc, &chr->dscs, next) { + if (dsc->dsc.handle >= dsc_handle) { + break; + } + + prev = dsc; + } + + return prev; +} + +static struct peer_dsc * +peer_dsc_find(const struct peer_chr *chr, uint16_t dsc_handle, + struct peer_dsc **out_prev) +{ + struct peer_dsc *prev; + struct peer_dsc *dsc; + + prev = peer_dsc_find_prev(chr, dsc_handle); + if (prev == NULL) { + dsc = SLIST_FIRST(&chr->dscs); + } else { + dsc = SLIST_NEXT(prev, next); + } + + if (dsc != NULL && dsc->dsc.handle != dsc_handle) { + dsc = NULL; + } + + if (out_prev != NULL) { + *out_prev = prev; + } + return dsc; +} + +static int +peer_dsc_add(struct peer *peer, uint16_t chr_val_handle, + const struct ble_gatt_dsc *gatt_dsc) +{ + struct peer_dsc *prev; + struct peer_dsc *dsc; + struct peer_svc *svc; + struct peer_chr *chr; + + svc = peer_svc_find_range(peer, chr_val_handle); + if (svc == NULL) { + /* Can't find service for discovered descriptor; this shouldn't + * happen. + */ + assert(0); + return BLE_HS_EUNKNOWN; + } + + chr = peer_chr_find(svc, chr_val_handle, NULL); + if (chr == NULL) { + /* Can't find characteristic for discovered descriptor; this shouldn't + * happen. + */ + assert(0); + return BLE_HS_EUNKNOWN; + } + + dsc = peer_dsc_find(chr, gatt_dsc->handle, &prev); + if (dsc != NULL) { + /* Descriptor already discovered. */ + return 0; + } + + dsc = os_memblock_get(&peer_dsc_pool); + if (dsc == NULL) { + /* Out of memory. */ + return BLE_HS_ENOMEM; + } + memset(dsc, 0, sizeof *dsc); + + dsc->dsc = *gatt_dsc; + + if (prev == NULL) { + SLIST_INSERT_HEAD(&chr->dscs, dsc, next); + } else { + SLIST_NEXT(prev, next) = dsc; + } + + return 0; +} + +static void +peer_disc_dscs(struct peer *peer) +{ + struct peer_chr *chr; + struct peer_svc *svc; + int rc; + + /* Search through the list of discovered characteristics for the first + * characteristic that contains undiscovered descriptors. Then, discover + * all descriptors belonging to that characteristic. + */ + SLIST_FOREACH(svc, &peer->svcs, next) { + SLIST_FOREACH(chr, &svc->chrs, next) { + if (!chr_is_empty(svc, chr) && + SLIST_EMPTY(&chr->dscs) && + peer->disc_prev_chr_val <= chr->chr.def_handle) { + + rc = ble_gattc_disc_all_dscs(peer->conn_handle, + chr->chr.val_handle, + chr_end_handle(svc, chr), + peer_dsc_disced, peer); + if (rc != 0) { + peer_disc_complete(peer, rc); + } + + peer->disc_prev_chr_val = chr->chr.val_handle; + return; + } + } + } + + /* All descriptors discovered. */ + peer_disc_complete(peer, 0); +} + +static int +peer_dsc_disced(uint16_t conn_handle, const struct ble_gatt_error *error, + uint16_t chr_val_handle, const struct ble_gatt_dsc *dsc, + void *arg) +{ + struct peer *peer; + int rc; + + peer = arg; + assert(peer->conn_handle == conn_handle); + + switch (error->status) { + case 0: + rc = peer_dsc_add(peer, chr_val_handle, dsc); + break; + + case BLE_HS_EDONE: + /* All descriptors in this characteristic discovered; start discovering + * descriptors in the next characteristic. + */ + if (peer->disc_prev_chr_val > 0) { + peer_disc_dscs(peer); + } + rc = 0; + break; + + default: + /* Error; abort discovery. */ + rc = error->status; + break; + } + + if (rc != 0) { + /* Error; abort discovery. */ + peer_disc_complete(peer, rc); + } + + return rc; +} + +uint16_t +chr_end_handle(const struct peer_svc *svc, const struct peer_chr *chr) +{ + const struct peer_chr *next_chr; + + next_chr = SLIST_NEXT(chr, next); + if (next_chr != NULL) { + return next_chr->chr.def_handle - 1; + } else { + return svc->svc.end_handle; + } +} + +int +chr_is_empty(const struct peer_svc *svc, const struct peer_chr *chr) +{ + return chr_end_handle(svc, chr) <= chr->chr.val_handle; +} + +static struct peer_chr * +peer_chr_find_prev(const struct peer_svc *svc, uint16_t chr_val_handle) +{ + struct peer_chr *prev; + struct peer_chr *chr; + + prev = NULL; + SLIST_FOREACH(chr, &svc->chrs, next) { + if (chr->chr.val_handle >= chr_val_handle) { + break; + } + + prev = chr; + } + + return prev; +} + +static struct peer_chr * +peer_chr_find(const struct peer_svc *svc, uint16_t chr_val_handle, + struct peer_chr **out_prev) +{ + struct peer_chr *prev; + struct peer_chr *chr; + + prev = peer_chr_find_prev(svc, chr_val_handle); + if (prev == NULL) { + chr = SLIST_FIRST(&svc->chrs); + } else { + chr = SLIST_NEXT(prev, next); + } + + if (chr != NULL && chr->chr.val_handle != chr_val_handle) { + chr = NULL; + } + + if (out_prev != NULL) { + *out_prev = prev; + } + return chr; +} + +static void +peer_chr_delete(struct peer_chr *chr) +{ + struct peer_dsc *dsc; + + while ((dsc = SLIST_FIRST(&chr->dscs)) != NULL) { + SLIST_REMOVE_HEAD(&chr->dscs, next); + os_memblock_put(&peer_dsc_pool, dsc); + } + + os_memblock_put(&peer_chr_pool, chr); +} + +static int +peer_chr_add(struct peer *peer, uint16_t svc_start_handle, + const struct ble_gatt_chr *gatt_chr) +{ + struct peer_chr *prev; + struct peer_chr *chr; + struct peer_svc *svc; + + svc = peer_svc_find(peer, svc_start_handle, NULL); + if (svc == NULL) { + /* Can't find service for discovered characteristic; this shouldn't + * happen. + */ + assert(0); + return BLE_HS_EUNKNOWN; + } + + chr = peer_chr_find(svc, gatt_chr->def_handle, &prev); + if (chr != NULL) { + /* Characteristic already discovered. */ + return 0; + } + + chr = os_memblock_get(&peer_chr_pool); + if (chr == NULL) { + /* Out of memory. */ + return BLE_HS_ENOMEM; + } + memset(chr, 0, sizeof *chr); + + chr->chr = *gatt_chr; + + if (prev == NULL) { + SLIST_INSERT_HEAD(&svc->chrs, chr, next); + } else { + SLIST_NEXT(prev, next) = chr; + } + + return 0; +} + +static int +peer_chr_disced(uint16_t conn_handle, const struct ble_gatt_error *error, + const struct ble_gatt_chr *chr, void *arg) +{ + struct peer *peer; + int rc; + + peer = arg; + assert(peer->conn_handle == conn_handle); + + switch (error->status) { + case 0: + rc = peer_chr_add(peer, peer->cur_svc->svc.start_handle, chr); + break; + + case BLE_HS_EDONE: + /* All characteristics in this service discovered; start discovering + * characteristics in the next service. + */ + if (peer->disc_prev_chr_val > 0) { + peer_disc_chrs(peer); + } + rc = 0; + break; + + default: + rc = error->status; + break; + } + + if (rc != 0) { + /* Error; abort discovery. */ + peer_disc_complete(peer, rc); + } + + return rc; +} + +static void +peer_disc_chrs(struct peer *peer) +{ + struct peer_svc *svc; + int rc; + + /* Search through the list of discovered service for the first service that + * contains undiscovered characteristics. Then, discover all + * characteristics belonging to that service. + */ + SLIST_FOREACH(svc, &peer->svcs, next) { + if (!peer_svc_is_empty(svc) && SLIST_EMPTY(&svc->chrs)) { + peer->cur_svc = svc; + rc = ble_gattc_disc_all_chrs(peer->conn_handle, + svc->svc.start_handle, + svc->svc.end_handle, + peer_chr_disced, peer); + if (rc != 0) { + peer_disc_complete(peer, rc); + } + return; + } + } + + /* All characteristics discovered. */ + peer_disc_dscs(peer); +} + +int +peer_svc_is_empty(const struct peer_svc *svc) +{ + return svc->svc.end_handle <= svc->svc.start_handle; +} + +static struct peer_svc * +peer_svc_find_prev(struct peer *peer, uint16_t svc_start_handle) +{ + struct peer_svc *prev; + struct peer_svc *svc; + + prev = NULL; + SLIST_FOREACH(svc, &peer->svcs, next) { + if (svc->svc.start_handle >= svc_start_handle) { + break; + } + + prev = svc; + } + + return prev; +} + +static struct peer_svc * +peer_svc_find(struct peer *peer, uint16_t svc_start_handle, + struct peer_svc **out_prev) +{ + struct peer_svc *prev; + struct peer_svc *svc; + + prev = peer_svc_find_prev(peer, svc_start_handle); + if (prev == NULL) { + svc = SLIST_FIRST(&peer->svcs); + } else { + svc = SLIST_NEXT(prev, next); + } + + if (svc != NULL && svc->svc.start_handle != svc_start_handle) { + svc = NULL; + } + + if (out_prev != NULL) { + *out_prev = prev; + } + return svc; +} + +static struct peer_svc * +peer_svc_find_range(struct peer *peer, uint16_t attr_handle) +{ + struct peer_svc *svc; + + SLIST_FOREACH(svc, &peer->svcs, next) { + if (svc->svc.start_handle <= attr_handle && + svc->svc.end_handle >= attr_handle) { + + return svc; + } + } + + return NULL; +} + +const struct peer_svc * +peer_svc_find_uuid(const struct peer *peer, const ble_uuid_t *uuid) +{ + const struct peer_svc *svc; + + SLIST_FOREACH(svc, &peer->svcs, next) { + if (ble_uuid_cmp(&svc->svc.uuid.u, uuid) == 0) { + return svc; + } + } + + return NULL; +} + +const struct peer_chr * +peer_chr_find_uuid(const struct peer *peer, const ble_uuid_t *svc_uuid, + const ble_uuid_t *chr_uuid) +{ + const struct peer_svc *svc; + const struct peer_chr *chr; + + svc = peer_svc_find_uuid(peer, svc_uuid); + if (svc == NULL) { + return NULL; + } + + SLIST_FOREACH(chr, &svc->chrs, next) { + if (ble_uuid_cmp(&chr->chr.uuid.u, chr_uuid) == 0) { + return chr; + } + } + + return NULL; +} + +const struct peer_dsc * +peer_dsc_find_uuid(const struct peer *peer, const ble_uuid_t *svc_uuid, + const ble_uuid_t *chr_uuid, const ble_uuid_t *dsc_uuid) +{ + const struct peer_chr *chr; + const struct peer_dsc *dsc; + + chr = peer_chr_find_uuid(peer, svc_uuid, chr_uuid); + if (chr == NULL) { + return NULL; + } + + SLIST_FOREACH(dsc, &chr->dscs, next) { + if (ble_uuid_cmp(&dsc->dsc.uuid.u, dsc_uuid) == 0) { + return dsc; + } + } + + return NULL; +} + +static int +peer_svc_add(struct peer *peer, const struct ble_gatt_svc *gatt_svc) +{ + struct peer_svc *prev; + struct peer_svc *svc; + + svc = peer_svc_find(peer, gatt_svc->start_handle, &prev); + if (svc != NULL) { + /* Service already discovered. */ + return 0; + } + + svc = os_memblock_get(&peer_svc_pool); + if (svc == NULL) { + /* Out of memory. */ + return BLE_HS_ENOMEM; + } + memset(svc, 0, sizeof *svc); + + svc->svc = *gatt_svc; + SLIST_INIT(&svc->chrs); + + if (prev == NULL) { + SLIST_INSERT_HEAD(&peer->svcs, svc, next); + } else { + SLIST_INSERT_AFTER(prev, svc, next); + } + + return 0; +} + +static void +peer_svc_delete(struct peer_svc *svc) +{ + struct peer_chr *chr; + + while ((chr = SLIST_FIRST(&svc->chrs)) != NULL) { + SLIST_REMOVE_HEAD(&svc->chrs, next); + peer_chr_delete(chr); + } + + os_memblock_put(&peer_svc_pool, svc); +} + +static int +peer_svc_disced(uint16_t conn_handle, const struct ble_gatt_error *error, + const struct ble_gatt_svc *service, void *arg) +{ + struct peer *peer; + int rc; + + peer = arg; + assert(peer->conn_handle == conn_handle); + + switch (error->status) { + case 0: + rc = peer_svc_add(peer, service); + break; + + case BLE_HS_EDONE: + /* All services discovered; start discovering characteristics. */ + if (peer->disc_prev_chr_val > 0) { + peer_disc_chrs(peer); + } + rc = 0; + break; + + default: + rc = error->status; + break; + } + + if (rc != 0) { + /* Error; abort discovery. */ + peer_disc_complete(peer, rc); + } + + return rc; +} + + +int +peer_disc_all(uint16_t conn_handle, peer_disc_fn *disc_cb, void *disc_cb_arg) +{ + struct peer_svc *svc; + struct peer *peer; + int rc; + + peer = peer_find(conn_handle); + if (peer == NULL) { + return BLE_HS_ENOTCONN; + } + + /* Undiscover everything first. */ + while ((svc = SLIST_FIRST(&peer->svcs)) != NULL) { + SLIST_REMOVE_HEAD(&peer->svcs, next); + peer_svc_delete(svc); + } + + peer->disc_prev_chr_val = 1; + peer->disc_cb = disc_cb; + peer->disc_cb_arg = disc_cb_arg; + + rc = ble_gattc_disc_all_svcs(conn_handle, peer_svc_disced, peer); + if (rc != 0) { + return rc; + } + + return 0; +} + +int +peer_delete(uint16_t conn_handle) +{ + struct peer_svc *svc; + struct peer *peer; + int rc; + + peer = peer_find(conn_handle); + if (peer == NULL) { + return BLE_HS_ENOTCONN; + } + + SLIST_REMOVE(&peers, peer, peer, next); + + while ((svc = SLIST_FIRST(&peer->svcs)) != NULL) { + SLIST_REMOVE_HEAD(&peer->svcs, next); + peer_svc_delete(svc); + } + + rc = os_memblock_put(&peer_pool, peer); + if (rc != 0) { + return BLE_HS_EOS; + } + + return 0; +} + +int +peer_add(uint16_t conn_handle) +{ + struct peer *peer; + + /* Make sure the connection handle is unique. */ + peer = peer_find(conn_handle); + if (peer != NULL) { + return BLE_HS_EALREADY; + } + + peer = os_memblock_get(&peer_pool); + if (peer == NULL) { + /* Out of memory. */ + return BLE_HS_ENOMEM; + } + + memset(peer, 0, sizeof *peer); + peer->conn_handle = conn_handle; + + SLIST_INSERT_HEAD(&peers, peer, next); + + return 0; +} + +static void +peer_free_mem(void) +{ + free(peer_mem); + peer_mem = NULL; + + free(peer_svc_mem); + peer_svc_mem = NULL; + + free(peer_chr_mem); + peer_chr_mem = NULL; + + free(peer_dsc_mem); + peer_dsc_mem = NULL; +} + +int +peer_init(int max_peers, int max_svcs, int max_chrs, int max_dscs) +{ + int rc; + + /* Free memory first in case this function gets called more than once. */ + peer_free_mem(); + + peer_mem = malloc( + OS_MEMPOOL_BYTES(max_peers, sizeof (struct peer))); + if (peer_mem == NULL) { + rc = BLE_HS_ENOMEM; + goto err; + } + + rc = os_mempool_init(&peer_pool, max_peers, + sizeof (struct peer), peer_mem, + "peer_pool"); + if (rc != 0) { + rc = BLE_HS_EOS; + goto err; + } + + peer_svc_mem = malloc( + OS_MEMPOOL_BYTES(max_svcs, sizeof (struct peer_svc))); + if (peer_svc_mem == NULL) { + rc = BLE_HS_ENOMEM; + goto err; + } + + rc = os_mempool_init(&peer_svc_pool, max_svcs, + sizeof (struct peer_svc), peer_svc_mem, + "peer_svc_pool"); + if (rc != 0) { + rc = BLE_HS_EOS; + goto err; + } + + peer_chr_mem = malloc( + OS_MEMPOOL_BYTES(max_chrs, sizeof (struct peer_chr))); + if (peer_chr_mem == NULL) { + rc = BLE_HS_ENOMEM; + goto err; + } + + rc = os_mempool_init(&peer_chr_pool, max_chrs, + sizeof (struct peer_chr), peer_chr_mem, + "peer_chr_pool"); + if (rc != 0) { + rc = BLE_HS_EOS; + goto err; + } + + peer_dsc_mem = malloc( + OS_MEMPOOL_BYTES(max_dscs, sizeof (struct peer_dsc))); + if (peer_dsc_mem == NULL) { + rc = BLE_HS_ENOMEM; + goto err; + } + + rc = os_mempool_init(&peer_dsc_pool, max_dscs, + sizeof (struct peer_dsc), peer_dsc_mem, + "peer_dsc_pool"); + if (rc != 0) { + rc = BLE_HS_EOS; + goto err; + } + + return 0; + +err: + peer_free_mem(); + return rc; +} diff --git a/lib/bt/host/nimble/nimble/apps/blecent/syscfg.yml b/lib/bt/host/nimble/nimble/apps/blecent/syscfg.yml new file mode 100644 index 00000000..e1863839 --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/blecent/syscfg.yml @@ -0,0 +1,33 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +syscfg.vals: + # DEBUG logging is a bit noisy; use INFO. + LOG_LEVEL: 1 + + # Default task settings + OS_MAIN_STACK_SIZE: 336 + +# Disable peripheral and broadcaster roles. + BLE_ROLE_BROADCASTER: 0 + BLE_ROLE_CENTRAL: 1 + BLE_ROLE_OBSERVER: 1 + BLE_ROLE_PERIPHERAL: 0 + + # Whether to save data to sys/config, or just keep it in RAM. + BLE_STORE_CONFIG_PERSIST: 0 diff --git a/lib/bt/host/nimble/nimble/apps/blecsc/README.md b/lib/bt/host/nimble/nimble/apps/blecsc/README.md new file mode 100644 index 00000000..bccf176a --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/blecsc/README.md @@ -0,0 +1,9 @@ +# BLE Cycling Speed and Cadence peripheral app. + +The source files are located in the src/ directory. + +pkg.yml contains the base definition of the app. + +syscfg.yml contains setting definitions and overrides. + + diff --git a/lib/bt/host/nimble/nimble/apps/blecsc/pkg.yml b/lib/bt/host/nimble/nimble/apps/blecsc/pkg.yml new file mode 100644 index 00000000..d3768fc7 --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/blecsc/pkg.yml @@ -0,0 +1,38 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +pkg.name: apps/blecsc +pkg.type: app +pkg.description: BLE peripheral cycling speed and cadence sensor. +pkg.author: "Maciej Jurczak" +pkg.email: "mjurczak@gmail.com" +pkg.homepage: "http://mynewt.apache.org/" +pkg.keywords: + +pkg.deps: + - "@apache-mynewt-core/kernel/os" + - "@apache-mynewt-core/sys/console/full" + - "@apache-mynewt-core/sys/log/full" + - "@apache-mynewt-core/sys/log/modlog" + - "@apache-mynewt-core/sys/stats/full" + - "@apache-mynewt-core/sys/sysinit" + - "@apache-mynewt-core/sys/id" + - nimble/host + - nimble/host/services/gap + - nimble/host/services/gatt + - nimble/host/store/config diff --git a/lib/bt/host/nimble/nimble/apps/blecsc/src/blecsc_sens.h b/lib/bt/host/nimble/nimble/apps/blecsc/src/blecsc_sens.h new file mode 100644 index 00000000..99addc0f --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/blecsc/src/blecsc_sens.h @@ -0,0 +1,105 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_BLECSC_SENSOR_ +#define H_BLECSC_SENSOR_ + +#include "modlog/modlog.h" +#include "nimble/ble.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Cycling Speed and Cadence configuration */ +#define GATT_CSC_UUID 0x1816 +#define GATT_CSC_MEASUREMENT_UUID 0x2A5B +#define GATT_CSC_FEATURE_UUID 0x2A5C +#define GATT_SENSOR_LOCATION_UUID 0x2A5D +#define GATT_SC_CONTROL_POINT_UUID 0x2A55 +/* Device Information configuration */ +#define GATT_DEVICE_INFO_UUID 0x180A +#define GATT_MANUFACTURER_NAME_UUID 0x2A29 +#define GATT_MODEL_NUMBER_UUID 0x2A24 + +/*CSC Measurement flags*/ +#define CSC_MEASUREMENT_WHEEL_REV_PRESENT 0x01 +#define CSC_MEASUREMENT_CRANK_REV_PRESENT 0x02 + +/* CSC feature flags */ +#define CSC_FEATURE_WHEEL_REV_DATA 0x01 +#define CSC_FEATURE_CRANK_REV_DATA 0x02 +#define CSC_FEATURE_MULTIPLE_SENSOR_LOC 0x04 + +/* Sensor location enum */ +#define SENSOR_LOCATION_OTHER 0 +#define SENSOR_LOCATION_TOP_OF_SHOE 1 +#define SENSOR_LOCATION_IN_SHOE 2 +#define SENSOR_LOCATION_HIP 3 +#define SENSOR_LOCATION_FRONT_WHEEL 4 +#define SENSOR_LOCATION_LEFT_CRANK 5 +#define SENSOR_LOCATION_RIGHT_CRANK 6 +#define SENSOR_LOCATION_LEFT_PEDAL 7 +#define SENSOR_LOCATION_RIGHT_PEDAL 8 +#define SENSOR_LOCATION_FROT_HUB 9 +#define SENSOR_LOCATION_REAR_DROPOUT 10 +#define SENSOR_LOCATION_CHAINSTAY 11 +#define SENSOR_LOCATION_REAR_WHEEL 12 +#define SENSOR_LOCATION_REAR_HUB 13 +#define SENSOR_LOCATION_CHEST 14 +#define SENSOR_LOCATION_SPIDER 15 +#define SENSOR_LOCATION_CHAIN_RING 16 + +/* SC Control Point op codes */ +#define SC_CP_OP_SET_CUMULATIVE_VALUE 1 +#define SC_CP_OP_START_SENSOR_CALIBRATION 2 +#define SC_CP_OP_UPDATE_SENSOR_LOCATION 3 +#define SC_CP_OP_REQ_SUPPORTED_SENSOR_LOCATIONS 4 +#define SC_CP_OP_RESPONSE 16 + +/*SC Control Point response values */ +#define SC_CP_RESPONSE_SUCCESS 1 +#define SC_CP_RESPONSE_OP_NOT_SUPPORTED 2 +#define SC_CP_RESPONSE_INVALID_PARAM 3 +#define SC_CP_RESPONSE_OP_FAILED 4 + +/* CSC simulation configuration */ +#define CSC_FEATURES (CSC_FEATURE_WHEEL_REV_DATA | \ + CSC_FEATURE_CRANK_REV_DATA |\ + CSC_FEATURE_MULTIPLE_SENSOR_LOC) + +struct ble_csc_measurement_state { + uint32_t cumulative_wheel_rev; + uint16_t last_wheel_evt_time; + uint16_t cumulative_crank_rev; + uint16_t last_crank_evt_time; +}; + +extern uint16_t csc_measurement_handle; +extern uint16_t csc_control_point_handle; + +int gatt_svr_init(struct ble_csc_measurement_state * csc_measurement_state); +int gatt_svr_chr_notify_csc_measurement(uint16_t conn_handle); +void gatt_svr_set_cp_indicate(uint8_t indication_status); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/bt/host/nimble/nimble/apps/blecsc/src/gatt_svr.c b/lib/bt/host/nimble/nimble/apps/blecsc/src/gatt_svr.c new file mode 100644 index 00000000..0c178c0c --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/blecsc/src/gatt_svr.c @@ -0,0 +1,385 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#include +#include +#include "os/mynewt.h" +#include "host/ble_hs.h" +#include "host/ble_uuid.h" +#include "blecsc_sens.h" + +#define CSC_ERR_CCC_DESC_IMPROPERLY_CONFIGURED 0x81 + +static const char *manuf_name = "Apache Mynewt"; +static const char *model_num = "Mynewt CSC Sensor"; + +static const uint8_t csc_supported_sensor_locations[] = { + SENSOR_LOCATION_FRONT_WHEEL, + SENSOR_LOCATION_REAR_DROPOUT, + SENSOR_LOCATION_CHAINSTAY, + SENSOR_LOCATION_REAR_WHEEL +}; + +static uint8_t sensor_location = SENSOR_LOCATION_REAR_DROPOUT; +static struct ble_csc_measurement_state * measurement_state; +uint16_t csc_measurement_handle; +uint16_t csc_control_point_handle; +uint8_t csc_cp_indication_status; + +static int +gatt_svr_chr_access_csc_measurement(uint16_t conn_handle, + uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, + void *arg); + +static int +gatt_svr_chr_access_csc_feature(uint16_t conn_handle, + uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, + void *arg); + +static int +gatt_svr_chr_access_sensor_location(uint16_t conn_handle, + uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, + void *arg); + +static int +gatt_svr_chr_access_sc_control_point(uint16_t conn_handle, + uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, + void *arg); + +static int +gatt_svr_chr_access_device_info(uint16_t conn_handle, + uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, + void *arg); + +static const struct ble_gatt_svc_def gatt_svr_svcs[] = { + { + /* Service: Cycling Speed and Cadence */ + .type = BLE_GATT_SVC_TYPE_PRIMARY, + .uuid = BLE_UUID16_DECLARE(GATT_CSC_UUID), + .characteristics = (struct ble_gatt_chr_def[]) { { + /* Characteristic: Cycling Speed and Cadence Measurement */ + .uuid = BLE_UUID16_DECLARE(GATT_CSC_MEASUREMENT_UUID), + .access_cb = gatt_svr_chr_access_csc_measurement, + .val_handle = &csc_measurement_handle, + .flags = BLE_GATT_CHR_F_NOTIFY, + }, { + /* Characteristic: Cycling Speed and Cadence features */ + .uuid = BLE_UUID16_DECLARE(GATT_CSC_FEATURE_UUID), + .access_cb = gatt_svr_chr_access_csc_feature, + .flags = BLE_GATT_CHR_F_READ, + }, { + /* Characteristic: Sensor Location */ + .uuid = BLE_UUID16_DECLARE(GATT_SENSOR_LOCATION_UUID), + .access_cb = gatt_svr_chr_access_sensor_location, + .flags = BLE_GATT_CHR_F_READ, + }, { + /* Characteristic: SC Control Point*/ + .uuid = BLE_UUID16_DECLARE(GATT_SC_CONTROL_POINT_UUID), + .access_cb = gatt_svr_chr_access_sc_control_point, + .val_handle = &csc_control_point_handle, + .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_INDICATE, + }, { + 0, /* No more characteristics in this service */ + }, } + }, + + { + /* Service: Device Information */ + .type = BLE_GATT_SVC_TYPE_PRIMARY, + .uuid = BLE_UUID16_DECLARE(GATT_DEVICE_INFO_UUID), + .characteristics = (struct ble_gatt_chr_def[]) { { + /* Characteristic: * Manufacturer name */ + .uuid = BLE_UUID16_DECLARE(GATT_MANUFACTURER_NAME_UUID), + .access_cb = gatt_svr_chr_access_device_info, + .flags = BLE_GATT_CHR_F_READ, + }, { + /* Characteristic: Model number string */ + .uuid = BLE_UUID16_DECLARE(GATT_MODEL_NUMBER_UUID), + .access_cb = gatt_svr_chr_access_device_info, + .flags = BLE_GATT_CHR_F_READ, + }, { + 0, /* No more characteristics in this service */ + }, } + }, + + { + 0, /* No more services */ + }, +}; + +static int +gatt_svr_chr_access_csc_measurement(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, void *arg) +{ + return BLE_ATT_ERR_READ_NOT_PERMITTED; +} + +static int +gatt_svr_chr_access_csc_feature(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, void *arg) +{ + static const uint16_t csc_feature = CSC_FEATURES; + int rc; + + assert(ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR); + rc = os_mbuf_append(ctxt->om, &csc_feature, sizeof(csc_feature)); + + return (rc == 0) ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; +} + +static int +gatt_svr_chr_access_sensor_location(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, void *arg) +{ + int rc; + + assert(ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR); + rc = os_mbuf_append(ctxt->om, &sensor_location, sizeof(sensor_location)); + + return (rc == 0) ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; +} + +static int +gatt_svr_chr_access_sc_control_point(uint16_t conn_handle, + uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, + void *arg) +{ + uint8_t op_code; + uint8_t new_sensor_location; + uint8_t new_cumulative_wheel_rev_arr[4]; + struct os_mbuf *om_indication; + uint8_t response = SC_CP_RESPONSE_OP_NOT_SUPPORTED; + int ii; + int rc; + + assert(ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR); + + if (!csc_cp_indication_status) { + MODLOG_DFLT(INFO, "SC Control Point; CCC descriptor " + "improperly configured"); + return CSC_ERR_CCC_DESC_IMPROPERLY_CONFIGURED; + } + + /* Read control point op code*/ + rc = os_mbuf_copydata(ctxt->om, 0, sizeof(op_code), &op_code); + if (rc != 0){ + return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; + } + MODLOG_DFLT(INFO, "SC Control Point; opcode=%d\n", op_code); + + /* Allocate response buffer */ + om_indication = ble_hs_mbuf_att_pkt(); + + switch(op_code){ +#if (CSC_FEATURES & CSC_FEATURE_WHEEL_REV_DATA) + case SC_CP_OP_SET_CUMULATIVE_VALUE: + /* Read new cumulative wheel revolutions value*/ + rc = os_mbuf_copydata(ctxt->om, 1, + sizeof(new_cumulative_wheel_rev_arr), + new_cumulative_wheel_rev_arr); + if (rc != 0){ + return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; + } + + measurement_state->cumulative_wheel_rev = + get_le32(new_cumulative_wheel_rev_arr); + + MODLOG_DFLT(INFO, "SC Control Point; Set cumulative value = %d\n", + measurement_state->cumulative_wheel_rev); + + response = SC_CP_RESPONSE_SUCCESS; + break; +#endif + +#if (CSC_FEATURES & CSC_FEATURE_MULTIPLE_SENSOR_LOC) + case SC_CP_OP_UPDATE_SENSOR_LOCATION: + /* Read new sensor location value*/ + rc = os_mbuf_copydata(ctxt->om, 1, 1, &new_sensor_location); + if (rc != 0){ + return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; + } + + MODLOG_DFLT(INFO, "SC Control Point; Sensor location update = %d\n", + new_sensor_location); + + /* Verify if requested new location is on supported locations list */ + response = SC_CP_RESPONSE_INVALID_PARAM; + for (ii = 0; ii < sizeof(csc_supported_sensor_locations); ii++){ + if (new_sensor_location == csc_supported_sensor_locations[ii]){ + sensor_location = new_sensor_location; + response = SC_CP_RESPONSE_SUCCESS; + break; + } + } + break; + + case SC_CP_OP_REQ_SUPPORTED_SENSOR_LOCATIONS: + response = SC_CP_RESPONSE_SUCCESS; + break; +#endif + + default: + break; + } + + /* Append response value */ + rc = os_mbuf_append(om_indication, &response, sizeof(response)); + + if (rc != 0){ + return BLE_ATT_ERR_INSUFFICIENT_RES; + } + +#if (CSC_FEATURES & CSC_FEATURE_MULTIPLE_SENSOR_LOC) + /* In case of supported locations request append locations list */ + if (op_code == SC_CP_OP_REQ_SUPPORTED_SENSOR_LOCATIONS){ + rc = os_mbuf_append(om_indication, &csc_supported_sensor_locations, + sizeof(csc_supported_sensor_locations)); + } + + if (rc != 0){ + return BLE_ATT_ERR_INSUFFICIENT_RES; + } +#endif + + rc = ble_gatts_indicate_custom(conn_handle, csc_control_point_handle, + om_indication); + + return rc; +} + +static int +gatt_svr_chr_access_device_info(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, void *arg) +{ + uint16_t uuid; + int rc; + + uuid = ble_uuid_u16(ctxt->chr->uuid); + + if (uuid == GATT_MODEL_NUMBER_UUID) { + rc = os_mbuf_append(ctxt->om, model_num, strlen(model_num)); + return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; + } + + if (uuid == GATT_MANUFACTURER_NAME_UUID) { + rc = os_mbuf_append(ctxt->om, manuf_name, strlen(manuf_name)); + return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; + } + + assert(0); + return BLE_ATT_ERR_UNLIKELY; +} + +int +gatt_svr_chr_notify_csc_measurement(uint16_t conn_handle) +{ + int rc; + struct os_mbuf *om; + uint8_t data_buf[11]; + uint8_t data_offset = 1; + + memset(data_buf, 0, sizeof(data_buf)); + +#if (CSC_FEATURES & CSC_FEATURE_WHEEL_REV_DATA) + data_buf[0] |= CSC_MEASUREMENT_WHEEL_REV_PRESENT; + put_le16(&(data_buf[5]), measurement_state->last_wheel_evt_time); + put_le32(&(data_buf[1]), measurement_state->cumulative_wheel_rev); + data_offset += 6; +#endif + +#if (CSC_FEATURES & CSC_FEATURE_CRANK_REV_DATA) + data_buf[0] |= CSC_MEASUREMENT_CRANK_REV_PRESENT; + put_le16(&(data_buf[data_offset]), + measurement_state->cumulative_crank_rev); + put_le16(&(data_buf[data_offset + 2]), + measurement_state->last_crank_evt_time); + data_offset += 4; +#endif + + om = ble_hs_mbuf_from_flat(data_buf, data_offset); + + rc = ble_gatts_notify_custom(conn_handle, csc_measurement_handle, om); + return rc; +} + +void +gatt_svr_set_cp_indicate(uint8_t indication_status) +{ + csc_cp_indication_status = indication_status; +} + +void +gatt_svr_register_cb(struct ble_gatt_register_ctxt *ctxt, void *arg) +{ + char buf[BLE_UUID_STR_LEN]; + + switch (ctxt->op) { + case BLE_GATT_REGISTER_OP_SVC: + MODLOG_DFLT(DEBUG, "registered service %s with handle=%d\n", + ble_uuid_to_str(ctxt->svc.svc_def->uuid, buf), + ctxt->svc.handle); + break; + + case BLE_GATT_REGISTER_OP_CHR: + MODLOG_DFLT(DEBUG, "registering characteristic %s with " + "def_handle=%d val_handle=%d\n", + ble_uuid_to_str(ctxt->chr.chr_def->uuid, buf), + ctxt->chr.def_handle, + ctxt->chr.val_handle); + break; + + case BLE_GATT_REGISTER_OP_DSC: + MODLOG_DFLT(DEBUG, "registering descriptor %s with handle=%d\n", + ble_uuid_to_str(ctxt->dsc.dsc_def->uuid, buf), + ctxt->dsc.handle); + break; + + default: + assert(0); + break; + } +} + +int +gatt_svr_init(struct ble_csc_measurement_state * csc_measurement_state) +{ + int rc; + + rc = ble_gatts_count_cfg(gatt_svr_svcs); + if (rc != 0) { + return rc; + } + + rc = ble_gatts_add_svcs(gatt_svr_svcs); + if (rc != 0) { + return rc; + } + + measurement_state = csc_measurement_state; + + return 0; +} + diff --git a/lib/bt/host/nimble/nimble/apps/blecsc/src/main.c b/lib/bt/host/nimble/nimble/apps/blecsc/src/main.c new file mode 100644 index 00000000..5cca6b77 --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/blecsc/src/main.c @@ -0,0 +1,310 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#include +#include +#include + +#include "os/mynewt.h" +#include "console/console.h" +#include "config/config.h" +#include "nimble/ble.h" +#include "host/ble_hs.h" +#include "services/gap/ble_svc_gap.h" +#include "blecsc_sens.h" + +/* Wheel size for simulation calculations */ +#define CSC_SIM_WHEEL_CIRCUMFERENCE_MM 2000 +/* Simulated cadence lower limit */ +#define CSC_SIM_CRANK_RPM_MIN 20 +/* Simulated cadence upper limit */ +#define CSC_SIM_CRANK_RPM_MAX 100 +/* Simulated speed lower limit */ +#define CSC_SIM_SPEED_KPH_MIN 0 +/* Simulated speed upper limit */ +#define CSC_SIM_SPEED_KPH_MAX 35 + +/* Noticication status */ +static bool notify_state = false; + +/* Connection handle */ +static uint16_t conn_handle; + +static uint8_t blecsc_addr_type; + +/* Advertised device name */ +static const char *device_name = "blecsc_sensor"; + +/* Measurement and notification timer */ +static struct os_callout blecsc_measure_timer; + +/* Variable holds current CSC measurement state */ +static struct ble_csc_measurement_state csc_measurement_state; + +/* Variable holds simulated speed (kilometers per hour) */ +static uint16_t csc_sim_speed_kph = CSC_SIM_SPEED_KPH_MIN; + +/* Variable holds simulated cadence (RPM) */ +static uint8_t csc_sim_crank_rpm = CSC_SIM_CRANK_RPM_MIN; + +static int blecsc_gap_event(struct ble_gap_event *event, void *arg); + + +/* + * Enables advertising with parameters: + * o General discoverable mode + * o Undirected connectable mode + */ +static void +blecsc_advertise(void) +{ + struct ble_gap_adv_params adv_params; + struct ble_hs_adv_fields fields; + int rc; + + /* + * Set the advertisement data included in our advertisements: + * o Flags (indicates advertisement type and other general info) + * o Advertising tx power + * o Device name + */ + memset(&fields, 0, sizeof(fields)); + + /* + * Advertise two flags: + * o Discoverability in forthcoming advertisement (general) + * o BLE-only (BR/EDR unsupported) + */ + fields.flags = BLE_HS_ADV_F_DISC_GEN | + BLE_HS_ADV_F_BREDR_UNSUP; + + /* + * Indicate that the TX power level field should be included; have the + * stack fill this value automatically. This is done by assigning the + * special value BLE_HS_ADV_TX_PWR_LVL_AUTO. + */ + fields.tx_pwr_lvl_is_present = 1; + fields.tx_pwr_lvl = BLE_HS_ADV_TX_PWR_LVL_AUTO; + + fields.name = (uint8_t *)device_name; + fields.name_len = strlen(device_name); + fields.name_is_complete = 1; + + /* + * Set appearance. + */ + fields.appearance = ble_svc_gap_device_appearance(); + fields.appearance_is_present = 1; + + rc = ble_gap_adv_set_fields(&fields); + if (rc != 0) { + MODLOG_DFLT(ERROR, "error setting advertisement data; rc=%d\n", rc); + return; + } + + /* Begin advertising */ + memset(&adv_params, 0, sizeof(adv_params)); + adv_params.conn_mode = BLE_GAP_CONN_MODE_UND; + adv_params.disc_mode = BLE_GAP_DISC_MODE_GEN; + rc = ble_gap_adv_start(blecsc_addr_type, NULL, BLE_HS_FOREVER, + &adv_params, blecsc_gap_event, NULL); + if (rc != 0) { + MODLOG_DFLT(ERROR, "error enabling advertisement; rc=%d\n", rc); + return; + } +} + + +/* Update simulated CSC measurements. + * Each call increments wheel and crank revolution counters by one and + * computes last event time in order to match simulated candence and speed. + * Last event time is expressedd in 1/1024th of second units. + * + * 60 * 1024 + * crank_dt = -------------- + * cadence[RPM] + * + * + * circumference[mm] * 1024 * 60 * 60 + * wheel_dt = ------------------------------------- + * 10^6 * speed [kph] + */ +static void +blecsc_simulate_speed_and_cadence(void) +{ + uint16_t wheel_rev_period; + uint16_t crank_rev_period; + + /* Update simulated crank and wheel rotation speed */ + csc_sim_speed_kph++; + if (csc_sim_speed_kph >= CSC_SIM_SPEED_KPH_MAX) { + csc_sim_speed_kph = CSC_SIM_SPEED_KPH_MIN; + } + + csc_sim_crank_rpm++; + if (csc_sim_crank_rpm >= CSC_SIM_CRANK_RPM_MAX) { + csc_sim_crank_rpm = CSC_SIM_CRANK_RPM_MIN; + } + + /* Calculate simulated measurement values */ + if (csc_sim_speed_kph > 0){ + wheel_rev_period = (36*64*CSC_SIM_WHEEL_CIRCUMFERENCE_MM) / + (625*csc_sim_speed_kph); + csc_measurement_state.cumulative_wheel_rev++; + csc_measurement_state.last_wheel_evt_time += wheel_rev_period; + } + + if (csc_sim_crank_rpm > 0){ + crank_rev_period = (60*1024) / csc_sim_crank_rpm; + csc_measurement_state.cumulative_crank_rev++; + csc_measurement_state.last_crank_evt_time += crank_rev_period; + } + + MODLOG_DFLT(INFO, "CSC simulated values: speed = %d kph, cadence = %d \n", + csc_sim_speed_kph, csc_sim_crank_rpm); +} + +/* Run CSC measurement simulation and notify it to the client */ +static void +blecsc_measurement(struct os_event *ev) +{ + int rc; + + rc = os_callout_reset(&blecsc_measure_timer, OS_TICKS_PER_SEC); + assert(rc == 0); + + blecsc_simulate_speed_and_cadence(); + + if (notify_state) { + rc = gatt_svr_chr_notify_csc_measurement(conn_handle); + assert(rc == 0); + } +} + +static int +blecsc_gap_event(struct ble_gap_event *event, void *arg) +{ + switch (event->type) { + case BLE_GAP_EVENT_CONNECT: + /* A new connection was established or a connection attempt failed */ + MODLOG_DFLT(INFO, "connection %s; status=%d\n", + event->connect.status == 0 ? "established" : "failed", + event->connect.status); + + if (event->connect.status != 0) { + /* Connection failed; resume advertising */ + blecsc_advertise(); + conn_handle = 0; + } + else { + conn_handle = event->connect.conn_handle; + } + break; + + case BLE_GAP_EVENT_DISCONNECT: + MODLOG_DFLT(INFO, "disconnect; reason=%d\n", event->disconnect.reason); + conn_handle = 0; + /* Connection terminated; resume advertising */ + blecsc_advertise(); + break; + + case BLE_GAP_EVENT_ADV_COMPLETE: + MODLOG_DFLT(INFO, "adv complete\n"); + break; + + case BLE_GAP_EVENT_SUBSCRIBE: + MODLOG_DFLT(INFO, "subscribe event attr_handle=%d\n", + event->subscribe.attr_handle); + + if (event->subscribe.attr_handle == csc_measurement_handle) { + notify_state = event->subscribe.cur_notify; + MODLOG_DFLT(INFO, "csc measurement notify state = %d\n", + notify_state); + } + else if (event->subscribe.attr_handle == csc_control_point_handle) { + gatt_svr_set_cp_indicate(event->subscribe.cur_indicate); + MODLOG_DFLT(INFO, "csc control point indicate state = %d\n", + event->subscribe.cur_indicate); + } + break; + + case BLE_GAP_EVENT_MTU: + MODLOG_DFLT(INFO, "mtu update event; conn_handle=%d mtu=%d\n", + event->mtu.conn_handle, + event->mtu.value); + break; + + } + + return 0; +} + +static void +blecsc_on_sync(void) +{ + int rc; + + /* Figure out address to use while advertising (no privacy) */ + rc = ble_hs_id_infer_auto(0, &blecsc_addr_type); + assert(rc == 0); + + /* Begin advertising */ + blecsc_advertise(); +} + +/* + * main + * + * The main task for the project. This function initializes the packages, + * then starts serving events from default event queue. + * + * @return int NOTE: this function should never return! + */ +int +main(void) +{ + int rc; + + /* Initialize OS */ + sysinit(); + + /* Initialize the NimBLE host configuration */ + ble_hs_cfg.sync_cb = blecsc_on_sync; + + /* Initialize measurement and notification timer */ + os_callout_init(&blecsc_measure_timer, os_eventq_dflt_get(), + blecsc_measurement, NULL); + rc = os_callout_reset(&blecsc_measure_timer, OS_TICKS_PER_SEC); + assert(rc == 0); + + rc = gatt_svr_init(&csc_measurement_state); + assert(rc == 0); + + /* Set the default device name */ + rc = ble_svc_gap_device_name_set(device_name); + assert(rc == 0); + + /* As the last thing, process events from default event queue */ + while (1) { + os_eventq_run(os_eventq_dflt_get()); + } + return 0; +} + diff --git a/lib/bt/host/nimble/nimble/apps/blecsc/syscfg.yml b/lib/bt/host/nimble/nimble/apps/blecsc/syscfg.yml new file mode 100644 index 00000000..abf89969 --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/blecsc/syscfg.yml @@ -0,0 +1,41 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + + +syscfg.vals: + # Disable central and observer roles. + BLE_ROLE_BROADCASTER: 1 + BLE_ROLE_CENTRAL: 0 + BLE_ROLE_OBSERVER: 0 + BLE_ROLE_PERIPHERAL: 1 + + # Disable unused eddystone feature. + BLE_EDDYSTONE: 0 + + # Log reboot messages to a flash circular buffer. + REBOOT_LOG_FCB: 1 + LOG_FCB: 1 + CONFIG_FCB: 1 + + # Set public device address. + BLE_LL_PUBLIC_DEV_ADDR: 0x1122aabb33cc + + # Set device appearance to Cycling Speed and Cadence Sensor + BLE_SVC_GAP_APPEARANCE: BLE_SVC_GAP_APPEARANCE_CYC_SPEED_AND_CADENCE_SENSOR + + # Whether to save data to sys/config, or just keep it in RAM. + BLE_STORE_CONFIG_PERSIST: 0 diff --git a/lib/bt/host/nimble/nimble/apps/blehci/pkg.yml b/lib/bt/host/nimble/nimble/apps/blehci/pkg.yml new file mode 100644 index 00000000..e385d7da --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/blehci/pkg.yml @@ -0,0 +1,33 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +pkg.name: apps/blehci +pkg.type: app +pkg.description: BLE controller application exposing HCI over external interface +pkg.author: "Johan Hedberg " +pkg.homepage: "http://mynewt.apache.org/" +pkg.keywords: + +pkg.deps: + - "@apache-mynewt-core/sys/console" + - "@apache-mynewt-core/sys/log/stub" + - "@apache-mynewt-core/sys/stats/full" + - "@apache-mynewt-core/kernel/os" + - nimble/transport + +pkg.req_apis: + - ble_transport diff --git a/lib/bt/host/nimble/nimble/apps/blehci/src/main.c b/lib/bt/host/nimble/nimble/apps/blehci/src/main.c new file mode 100644 index 00000000..e97e5427 --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/blehci/src/main.c @@ -0,0 +1,45 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "os/mynewt.h" + +static int +main_fn(int argc, char **argv) +{ + /* Initialize OS */ + sysinit(); + + while (1) { + os_eventq_run(os_eventq_dflt_get()); + } + return 0; +} + +int +main(int argc, char **argv) +{ +#if BABBLESIM + extern void bsim_init(int argc, char** argv, void *main_fn); + bsim_init(argc, argv, main_fn); +#else + main_fn(argc, argv); +#endif + + return 0; +} \ No newline at end of file diff --git a/lib/bt/host/nimble/nimble/apps/blehci/syscfg.yml b/lib/bt/host/nimble/nimble/apps/blehci/syscfg.yml new file mode 100644 index 00000000..ec20e689 --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/blehci/syscfg.yml @@ -0,0 +1,27 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +syscfg.vals: + # Default task settings + OS_MAIN_STACK_SIZE: 64 + # Stub console + CONSOLE_MODE: stub + +syscfg.vals.'!BLE_TRANSPORT_NETCORE': + # Use UART by default if not built on netcore + BLE_TRANSPORT_HS: uart diff --git a/lib/bt/host/nimble/nimble/apps/blehr/README.md b/lib/bt/host/nimble/nimble/apps/blehr/README.md new file mode 100644 index 00000000..d06d95de --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/blehr/README.md @@ -0,0 +1,9 @@ +# BLE Heart Rate peripheral app. + +The source files are located in the src/ directory. + +pkg.yml contains the base definition of the app. + +syscfg.yml contains setting definitions and overrides. + + diff --git a/lib/bt/host/nimble/nimble/apps/blehr/pkg.yml b/lib/bt/host/nimble/nimble/apps/blehr/pkg.yml new file mode 100644 index 00000000..ace633b1 --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/blehr/pkg.yml @@ -0,0 +1,38 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +pkg.name: apps/blehr +pkg.type: app +pkg.description: BLE peripheral heartrate sensor. +pkg.author: "Szymon Czapracki" +pkg.email: "szymon.czapracki@codecoup.pl" +pkg.homepage: "http://mynewt.apache.org/" +pkg.keywords: + +pkg.deps: + - "@apache-mynewt-core/kernel/os" + - "@apache-mynewt-core/sys/console/full" + - "@apache-mynewt-core/sys/log/full" + - "@apache-mynewt-core/sys/log/modlog" + - "@apache-mynewt-core/sys/stats/full" + - "@apache-mynewt-core/sys/sysinit" + - "@apache-mynewt-core/sys/id" + - nimble/host + - nimble/host/services/gap + - nimble/host/services/gatt + - nimble/host/store/config diff --git a/lib/bt/host/nimble/nimble/apps/blehr/src/blehr_sens.h b/lib/bt/host/nimble/nimble/apps/blehr/src/blehr_sens.h new file mode 100644 index 00000000..a3f59ca9 --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/blehr/src/blehr_sens.h @@ -0,0 +1,46 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_BLEHR_SENSOR_ +#define H_BLEHR_SENSOR_ + +#include "nimble/ble.h" +#include "modlog/modlog.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Heart-rate configuration */ +#define GATT_HRS_UUID 0x180D +#define GATT_HRS_MEASUREMENT_UUID 0x2A37 +#define GATT_HRS_BODY_SENSOR_LOC_UUID 0x2A38 +#define GATT_DEVICE_INFO_UUID 0x180A +#define GATT_MANUFACTURER_NAME_UUID 0x2A29 +#define GATT_MODEL_NUMBER_UUID 0x2A24 + +extern uint16_t hrs_hrm_handle; + +int gatt_svr_init(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/bt/host/nimble/nimble/apps/blehr/src/gatt_svr.c b/lib/bt/host/nimble/nimble/apps/blehr/src/gatt_svr.c new file mode 100644 index 00000000..b2e9b4e0 --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/blehr/src/gatt_svr.c @@ -0,0 +1,177 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#include +#include +#include "host/ble_hs.h" +#include "host/ble_uuid.h" +#include "blehr_sens.h" + +static const char *manuf_name = "Apache Mynewt"; +static const char *model_num = "Mynewt HR Sensor"; +uint16_t hrs_hrm_handle; + +static int +gatt_svr_chr_access_heart_rate(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, void *arg); + +static int +gatt_svr_chr_access_device_info(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, void *arg); + +static const struct ble_gatt_svc_def gatt_svr_svcs[] = { + { + /* Service: Heart-rate */ + .type = BLE_GATT_SVC_TYPE_PRIMARY, + .uuid = BLE_UUID16_DECLARE(GATT_HRS_UUID), + .characteristics = (struct ble_gatt_chr_def[]) { { + /* Characteristic: Heart-rate measurement */ + .uuid = BLE_UUID16_DECLARE(GATT_HRS_MEASUREMENT_UUID), + .access_cb = gatt_svr_chr_access_heart_rate, + .val_handle = &hrs_hrm_handle, + .flags = BLE_GATT_CHR_F_NOTIFY, + }, { + /* Characteristic: Body sensor location */ + .uuid = BLE_UUID16_DECLARE(GATT_HRS_BODY_SENSOR_LOC_UUID), + .access_cb = gatt_svr_chr_access_heart_rate, + .flags = BLE_GATT_CHR_F_READ, + }, { + 0, /* No more characteristics in this service */ + }, } + }, + + { + /* Service: Device Information */ + .type = BLE_GATT_SVC_TYPE_PRIMARY, + .uuid = BLE_UUID16_DECLARE(GATT_DEVICE_INFO_UUID), + .characteristics = (struct ble_gatt_chr_def[]) { { + /* Characteristic: * Manufacturer name */ + .uuid = BLE_UUID16_DECLARE(GATT_MANUFACTURER_NAME_UUID), + .access_cb = gatt_svr_chr_access_device_info, + .flags = BLE_GATT_CHR_F_READ, + }, { + /* Characteristic: Model number string */ + .uuid = BLE_UUID16_DECLARE(GATT_MODEL_NUMBER_UUID), + .access_cb = gatt_svr_chr_access_device_info, + .flags = BLE_GATT_CHR_F_READ, + }, { + 0, /* No more characteristics in this service */ + }, } + }, + + { + 0, /* No more services */ + }, +}; + +static int +gatt_svr_chr_access_heart_rate(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, void *arg) +{ + /* Sensor location, set to "Chest" */ + static uint8_t body_sens_loc = 0x01; + uint16_t uuid; + int rc; + + uuid = ble_uuid_u16(ctxt->chr->uuid); + + if (uuid == GATT_HRS_BODY_SENSOR_LOC_UUID) { + rc = os_mbuf_append(ctxt->om, &body_sens_loc, sizeof(body_sens_loc)); + + return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; + } + + assert(0); + return BLE_ATT_ERR_UNLIKELY; +} + +static int +gatt_svr_chr_access_device_info(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, void *arg) +{ + uint16_t uuid; + int rc; + + uuid = ble_uuid_u16(ctxt->chr->uuid); + + if (uuid == GATT_MODEL_NUMBER_UUID) { + rc = os_mbuf_append(ctxt->om, model_num, strlen(model_num)); + return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; + } + + if (uuid == GATT_MANUFACTURER_NAME_UUID) { + rc = os_mbuf_append(ctxt->om, manuf_name, strlen(manuf_name)); + return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; + } + + assert(0); + return BLE_ATT_ERR_UNLIKELY; +} + +void +gatt_svr_register_cb(struct ble_gatt_register_ctxt *ctxt, void *arg) +{ + char buf[BLE_UUID_STR_LEN]; + + switch (ctxt->op) { + case BLE_GATT_REGISTER_OP_SVC: + MODLOG_DFLT(DEBUG, "registered service %s with handle=%d\n", + ble_uuid_to_str(ctxt->svc.svc_def->uuid, buf), + ctxt->svc.handle); + break; + + case BLE_GATT_REGISTER_OP_CHR: + MODLOG_DFLT(DEBUG, "registering characteristic %s with " + "def_handle=%d val_handle=%d\n", + ble_uuid_to_str(ctxt->chr.chr_def->uuid, buf), + ctxt->chr.def_handle, + ctxt->chr.val_handle); + break; + + case BLE_GATT_REGISTER_OP_DSC: + MODLOG_DFLT(DEBUG, "registering descriptor %s with handle=%d\n", + ble_uuid_to_str(ctxt->dsc.dsc_def->uuid, buf), + ctxt->dsc.handle); + break; + + default: + assert(0); + break; + } +} + +int +gatt_svr_init(void) +{ + int rc; + + rc = ble_gatts_count_cfg(gatt_svr_svcs); + if (rc != 0) { + return rc; + } + + rc = ble_gatts_add_svcs(gatt_svr_svcs); + if (rc != 0) { + return rc; + } + + return 0; +} + diff --git a/lib/bt/host/nimble/nimble/apps/blehr/src/main.c b/lib/bt/host/nimble/nimble/apps/blehr/src/main.c new file mode 100644 index 00000000..e09349fa --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/blehr/src/main.c @@ -0,0 +1,261 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#include +#include +#include + +#include "os/mynewt.h" +#include "console/console.h" +#include "config/config.h" +#include "nimble/ble.h" +#include "host/ble_hs.h" +#include "services/gap/ble_svc_gap.h" +#include "blehr_sens.h" + +static bool notify_state; + +/* Connection handle */ +static uint16_t conn_handle; + +static const char *device_name = "blehr_sensor"; + +static int blehr_gap_event(struct ble_gap_event *event, void *arg); + +static uint8_t blehr_addr_type; + +/* Sending notify data timer */ +static struct os_callout blehr_tx_timer; + +/* Variable to simulate heart beats */ +static uint8_t heartrate = 90; + +/* + * Enables advertising with parameters: + * o General discoverable mode + * o Undirected connectable mode + */ +static void +blehr_advertise(void) +{ + struct ble_gap_adv_params adv_params; + struct ble_hs_adv_fields fields; + int rc; + + /* + * Set the advertisement data included in our advertisements: + * o Flags (indicates advertisement type and other general info) + * o Advertising tx power + * o Device name + */ + memset(&fields, 0, sizeof(fields)); + + /* + * Advertise two flags: + * o Discoverability in forthcoming advertisement (general) + * o BLE-only (BR/EDR unsupported) + */ + fields.flags = BLE_HS_ADV_F_DISC_GEN | + BLE_HS_ADV_F_BREDR_UNSUP; + + /* + * Indicate that the TX power level field should be included; have the + * stack fill this value automatically. This is done by assigning the + * special value BLE_HS_ADV_TX_PWR_LVL_AUTO. + */ + fields.tx_pwr_lvl_is_present = 1; + fields.tx_pwr_lvl = BLE_HS_ADV_TX_PWR_LVL_AUTO; + + fields.name = (uint8_t *)device_name; + fields.name_len = strlen(device_name); + fields.name_is_complete = 1; + + rc = ble_gap_adv_set_fields(&fields); + if (rc != 0) { + MODLOG_DFLT(ERROR, "error setting advertisement data; rc=%d\n", rc); + return; + } + + /* Begin advertising */ + memset(&adv_params, 0, sizeof(adv_params)); + adv_params.conn_mode = BLE_GAP_CONN_MODE_UND; + adv_params.disc_mode = BLE_GAP_DISC_MODE_GEN; + rc = ble_gap_adv_start(blehr_addr_type, NULL, BLE_HS_FOREVER, + &adv_params, blehr_gap_event, NULL); + if (rc != 0) { + MODLOG_DFLT(ERROR, "error enabling advertisement; rc=%d\n", rc); + return; + } +} + +static void +blehr_tx_hrate_stop(void) +{ + os_callout_stop(&blehr_tx_timer); +} + +/* Reset heartrate measurment */ +static void +blehr_tx_hrate_reset(void) +{ + int rc; + + rc = os_callout_reset(&blehr_tx_timer, OS_TICKS_PER_SEC); + assert(rc == 0); +} + +/* This functions simulates heart beat and notifies it to the client */ +static void +blehr_tx_hrate(struct os_event *ev) +{ + static uint8_t hrm[2]; + int rc; + struct os_mbuf *om; + + if (!notify_state) { + blehr_tx_hrate_stop(); + heartrate = 90; + return; + } + + hrm[0] = 0x06; /* contact of a sensor */ + hrm[1] = heartrate; /* storing dummy data */ + + /* Simulation of heart beats */ + heartrate++; + if (heartrate == 160) { + heartrate = 90; + } + + om = ble_hs_mbuf_from_flat(hrm, sizeof(hrm)); + + rc = ble_gatts_notify_custom(conn_handle, hrs_hrm_handle, om); + + assert(rc == 0); + blehr_tx_hrate_reset(); +} + +static int +blehr_gap_event(struct ble_gap_event *event, void *arg) +{ + switch (event->type) { + case BLE_GAP_EVENT_CONNECT: + /* A new connection was established or a connection attempt failed */ + MODLOG_DFLT(INFO, "connection %s; status=%d\n", + event->connect.status == 0 ? "established" : "failed", + event->connect.status); + + if (event->connect.status != 0) { + /* Connection failed; resume advertising */ + blehr_advertise(); + conn_handle = 0; + } + else { + conn_handle = event->connect.conn_handle; + } + + break; + + case BLE_GAP_EVENT_DISCONNECT: + MODLOG_DFLT(INFO, "disconnect; reason=%d\n", event->disconnect.reason); + conn_handle = BLE_HS_CONN_HANDLE_NONE; /* reset conn_handle */ + + /* Connection terminated; resume advertising */ + blehr_advertise(); + break; + + case BLE_GAP_EVENT_ADV_COMPLETE: + MODLOG_DFLT(INFO, "adv complete\n"); + blehr_advertise(); + break; + + case BLE_GAP_EVENT_SUBSCRIBE: + MODLOG_DFLT(INFO, "subscribe event; cur_notify=%d\n value handle; " + "val_handle=%d\n", + event->subscribe.cur_notify, hrs_hrm_handle); + if (event->subscribe.attr_handle == hrs_hrm_handle) { + notify_state = event->subscribe.cur_notify; + blehr_tx_hrate_reset(); + } else if (event->subscribe.attr_handle != hrs_hrm_handle) { + notify_state = event->subscribe.cur_notify; + blehr_tx_hrate_stop(); + } + break; + + case BLE_GAP_EVENT_MTU: + MODLOG_DFLT(INFO, "mtu update event; conn_handle=%d mtu=%d\n", + event->mtu.conn_handle, + event->mtu.value); + break; + + } + + return 0; +} + +static void +blehr_on_sync(void) +{ + int rc; + + /* Use privacy */ + rc = ble_hs_id_infer_auto(0, &blehr_addr_type); + assert(rc == 0); + + /* Begin advertising */ + blehr_advertise(); +} + +/* + * main + * + * The main task for the project. This function initializes the packages, + * then starts serving events from default event queue. + * + * @return int NOTE: this function should never return! + */ +int +main(void) +{ + int rc; + + /* Initialize OS */ + sysinit(); + + /* Initialize the NimBLE host configuration */ + ble_hs_cfg.sync_cb = blehr_on_sync; + + os_callout_init(&blehr_tx_timer, os_eventq_dflt_get(), + blehr_tx_hrate, NULL); + + rc = gatt_svr_init(); + assert(rc == 0); + + /* Set the default device name */ + rc = ble_svc_gap_device_name_set(device_name); + assert(rc == 0); + + /* As the last thing, process events from default event queue */ + while (1) { + os_eventq_run(os_eventq_dflt_get()); + } + return 0; +} + diff --git a/lib/bt/host/nimble/nimble/apps/blehr/syscfg.yml b/lib/bt/host/nimble/nimble/apps/blehr/syscfg.yml new file mode 100644 index 00000000..98cf2554 --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/blehr/syscfg.yml @@ -0,0 +1,38 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + + +syscfg.vals: + # Disable central and observer roles. + BLE_ROLE_BROADCASTER: 1 + BLE_ROLE_CENTRAL: 0 + BLE_ROLE_OBSERVER: 0 + BLE_ROLE_PERIPHERAL: 1 + + # Disable unused eddystone feature. + BLE_EDDYSTONE: 0 + + # Log reboot messages to a flash circular buffer. + REBOOT_LOG_FCB: 1 + LOG_FCB: 1 + CONFIG_FCB: 1 + + # Set public device address. + BLE_LL_PUBLIC_DEV_ADDR: 0x1122aabb33cc + + # Whether to save data to sys/config, or just keep it in RAM. + BLE_STORE_CONFIG_PERSIST: 0 diff --git a/lib/bt/host/nimble/nimble/apps/blemesh/pkg.yml b/lib/bt/host/nimble/nimble/apps/blemesh/pkg.yml new file mode 100644 index 00000000..19fb4782 --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/blemesh/pkg.yml @@ -0,0 +1,35 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +pkg.name: apps/blemesh +pkg.type: app +pkg.description: Sample application for BLE Mesh node with on/off model +pkg.author: "Łukasz Rymanowski " +pkg.homepage: "http://mynewt.apache.org/" +pkg.keywords: + +pkg.deps: + - "@apache-mynewt-core/kernel/os" + - "@apache-mynewt-core/sys/console/full" + - "@apache-mynewt-core/sys/log/full" + - "@apache-mynewt-core/sys/log/modlog" + - "@apache-mynewt-core/sys/stats/full" + - "@apache-mynewt-core/sys/shell" + - nimble/host + - nimble/host/services/gap + - nimble/host/services/gatt + - nimble/host/store/config diff --git a/lib/bt/host/nimble/nimble/apps/blemesh/src/main.c b/lib/bt/host/nimble/nimble/apps/blemesh/src/main.c new file mode 100644 index 00000000..56d0476f --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/blemesh/src/main.c @@ -0,0 +1,468 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#include "os/mynewt.h" +#include "mesh/mesh.h" +#include "console/console.h" +#include "hal/hal_system.h" +#include "hal/hal_gpio.h" +#include "bsp/bsp.h" +#include "shell/shell.h" + +/* BLE */ +#include "nimble/ble.h" +#include "host/ble_hs.h" +#include "services/gap/ble_svc_gap.h" +#include "mesh/glue.h" + +/* Company ID */ +#define CID_VENDOR 0x05C3 +#define STANDARD_TEST_ID 0x00 +#define TEST_ID 0x01 +static int recent_test_id = STANDARD_TEST_ID; + +#define FAULT_ARR_SIZE 2 + +static bool has_reg_fault = true; + +static int +fault_get_cur(struct bt_mesh_model *model, + uint8_t *test_id, + uint16_t *company_id, + uint8_t *faults, + uint8_t *fault_count) +{ + uint8_t reg_faults[FAULT_ARR_SIZE] = { [0 ... FAULT_ARR_SIZE-1] = 0xff }; + + console_printf("fault_get_cur() has_reg_fault %u\n", has_reg_fault); + + *test_id = recent_test_id; + *company_id = CID_VENDOR; + + *fault_count = min(*fault_count, sizeof(reg_faults)); + memcpy(faults, reg_faults, *fault_count); + + return 0; +} + +static int +fault_get_reg(struct bt_mesh_model *model, + uint16_t company_id, + uint8_t *test_id, + uint8_t *faults, + uint8_t *fault_count) +{ + if (company_id != CID_VENDOR) { + return -BLE_HS_EINVAL; + } + + console_printf("fault_get_reg() has_reg_fault %u\n", has_reg_fault); + + *test_id = recent_test_id; + + if (has_reg_fault) { + uint8_t reg_faults[FAULT_ARR_SIZE] = { [0 ... FAULT_ARR_SIZE-1] = 0xff }; + + *fault_count = min(*fault_count, sizeof(reg_faults)); + memcpy(faults, reg_faults, *fault_count); + } else { + *fault_count = 0; + } + + return 0; +} + +static int +fault_clear(struct bt_mesh_model *model, uint16_t company_id) +{ + if (company_id != CID_VENDOR) { + return -BLE_HS_EINVAL; + } + + has_reg_fault = false; + + return 0; +} + +static int +fault_test(struct bt_mesh_model *model, uint8_t test_id, uint16_t company_id) +{ + if (company_id != CID_VENDOR) { + return -BLE_HS_EINVAL; + } + + if (test_id != STANDARD_TEST_ID && test_id != TEST_ID) { + return -BLE_HS_EINVAL; + } + + recent_test_id = test_id; + has_reg_fault = true; + bt_mesh_fault_update(bt_mesh_model_elem(model)); + + return 0; +} + +static const struct bt_mesh_health_srv_cb health_srv_cb = { + .fault_get_cur = &fault_get_cur, + .fault_get_reg = &fault_get_reg, + .fault_clear = &fault_clear, + .fault_test = &fault_test, +}; + +static struct bt_mesh_health_srv health_srv = { + .cb = &health_srv_cb, +}; + +static struct bt_mesh_model_pub health_pub; + +static void +health_pub_init(void) +{ + health_pub.msg = BT_MESH_HEALTH_FAULT_MSG(0); +} + +static struct bt_mesh_model_pub gen_level_pub; +static struct bt_mesh_model_pub gen_onoff_pub; + +static uint8_t gen_on_off_state; +static int16_t gen_level_state; + +static int gen_onoff_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx) +{ + struct os_mbuf *msg = NET_BUF_SIMPLE(3); + uint8_t *status; + int rc; + + console_printf("#mesh-onoff STATUS\n"); + + bt_mesh_model_msg_init(msg, BT_MESH_MODEL_OP_2(0x82, 0x04)); + status = net_buf_simple_add(msg, 1); + *status = gen_on_off_state; + + rc = bt_mesh_model_send(model, ctx, msg, NULL, NULL); + if (rc) { + console_printf("#mesh-onoff STATUS: send status failed\n"); + } + + os_mbuf_free_chain(msg); + return rc; +} + +static int gen_onoff_get(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + console_printf("#mesh-onoff GET\n"); + + return gen_onoff_status(model, ctx); +} + +static int gen_onoff_set(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + console_printf("#mesh-onoff SET\n"); + + gen_on_off_state = buf->om_data[0]; + hal_gpio_write(LED_2, !gen_on_off_state); + + return gen_onoff_status(model, ctx); +} + +static int gen_onoff_set_unack(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + console_printf("#mesh-onoff SET-UNACK\n"); + + gen_on_off_state = buf->om_data[0]; + hal_gpio_write(LED_2, !gen_on_off_state); + return 0; +} + +static const struct bt_mesh_model_op gen_onoff_op[] = { + { BT_MESH_MODEL_OP_2(0x82, 0x01), 0, gen_onoff_get }, + { BT_MESH_MODEL_OP_2(0x82, 0x02), 2, gen_onoff_set }, + { BT_MESH_MODEL_OP_2(0x82, 0x03), 2, gen_onoff_set_unack }, + BT_MESH_MODEL_OP_END, +}; + +static int gen_level_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx) +{ + struct os_mbuf *msg = NET_BUF_SIMPLE(4); + int rc; + + console_printf("#mesh-level STATUS\n"); + + bt_mesh_model_msg_init(msg, BT_MESH_MODEL_OP_2(0x82, 0x08)); + net_buf_simple_add_le16(msg, gen_level_state); + + rc = bt_mesh_model_send(model, ctx, msg, NULL, NULL); + if (rc) { + console_printf("#mesh-level STATUS: send status failed\n"); + } + + os_mbuf_free_chain(msg); + return rc; +} + +static int gen_level_get(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + console_printf("#mesh-level GET\n"); + + return gen_level_status(model, ctx); +} + +static int gen_level_set(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + int16_t level; + int rc; + + level = (int16_t) net_buf_simple_pull_le16(buf); + console_printf("#mesh-level SET: level=%d\n", level); + + rc = gen_level_status(model, ctx); + if (rc) { + return rc; + } + + gen_level_state = level; + console_printf("#mesh-level: level=%d\n", gen_level_state); + return 0; +} + +static int gen_level_set_unack(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + int16_t level; + + level = (int16_t) net_buf_simple_pull_le16(buf); + console_printf("#mesh-level SET-UNACK: level=%d\n", level); + + gen_level_state = level; + console_printf("#mesh-level: level=%d\n", gen_level_state); + return 0; +} + +static int gen_delta_set(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + int16_t delta_level; + + delta_level = (int16_t) net_buf_simple_pull_le16(buf); + console_printf("#mesh-level DELTA-SET: delta_level=%d\n", delta_level); + + gen_level_status(model, ctx); + + gen_level_state += delta_level; + console_printf("#mesh-level: level=%d\n", gen_level_state); + return 0; +} + +static int gen_delta_set_unack(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + int16_t delta_level; + + delta_level = (int16_t) net_buf_simple_pull_le16(buf); + console_printf("#mesh-level DELTA-SET: delta_level=%d\n", delta_level); + + gen_level_state += delta_level; + console_printf("#mesh-level: level=%d\n", gen_level_state); + return 0; +} + +static int gen_move_set(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + return 0; +} + +static int gen_move_set_unack(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + return 0; +} + +static const struct bt_mesh_model_op gen_level_op[] = { + { BT_MESH_MODEL_OP_2(0x82, 0x05), 0, gen_level_get }, + { BT_MESH_MODEL_OP_2(0x82, 0x06), 3, gen_level_set }, + { BT_MESH_MODEL_OP_2(0x82, 0x07), 3, gen_level_set_unack }, + { BT_MESH_MODEL_OP_2(0x82, 0x09), 5, gen_delta_set }, + { BT_MESH_MODEL_OP_2(0x82, 0x0a), 5, gen_delta_set_unack }, + { BT_MESH_MODEL_OP_2(0x82, 0x0b), 3, gen_move_set }, + { BT_MESH_MODEL_OP_2(0x82, 0x0c), 3, gen_move_set_unack }, + BT_MESH_MODEL_OP_END, +}; + +static struct bt_mesh_model root_models[] = { + BT_MESH_MODEL_CFG_SRV, + BT_MESH_MODEL_HEALTH_SRV(&health_srv, &health_pub), + BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_ONOFF_SRV, gen_onoff_op, + &gen_onoff_pub, NULL), + BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_LEVEL_SRV, gen_level_op, + &gen_level_pub, NULL), +}; + +static struct bt_mesh_model_pub vnd_model_pub; + +static int vnd_model_recv(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct os_mbuf *msg = NET_BUF_SIMPLE(3); + int rc; + + console_printf("#vendor-model-recv\n"); + + console_printf("data:%s len:%d\n", bt_hex(buf->om_data, buf->om_len), + buf->om_len); + + bt_mesh_model_msg_init(msg, BT_MESH_MODEL_OP_3(0x01, CID_VENDOR)); + os_mbuf_append(msg, buf->om_data, buf->om_len); + + rc = bt_mesh_model_send(model, ctx, msg, NULL, NULL); + if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { + console_printf("#vendor-model-recv: send rsp failed\n"); + } + + os_mbuf_free_chain(msg); + return rc; +} + +static const struct bt_mesh_model_op vnd_model_op[] = { + { BT_MESH_MODEL_OP_3(0x01, CID_VENDOR), 0, vnd_model_recv }, + BT_MESH_MODEL_OP_END, +}; + +static struct bt_mesh_model vnd_models[] = { + BT_MESH_MODEL_VND(CID_VENDOR, BT_MESH_MODEL_ID_GEN_ONOFF_SRV, vnd_model_op, + &vnd_model_pub, NULL), +}; + +static struct bt_mesh_elem elements[] = { + BT_MESH_ELEM(0, root_models, vnd_models), +}; + +static const struct bt_mesh_comp comp = { + .cid = CID_VENDOR, + .elem = elements, + .elem_count = ARRAY_SIZE(elements), +}; + +static int output_number(bt_mesh_output_action_t action, uint32_t number) +{ + console_printf("OOB Number: %lu\n", number); + + return 0; +} + +static void prov_complete(uint16_t net_idx, uint16_t addr) +{ + console_printf("Local node provisioned, primary address 0x%04x\n", addr); +} + +static const uint8_t dev_uuid[16] = MYNEWT_VAL(BLE_MESH_DEV_UUID); + +static const struct bt_mesh_prov prov = { + .uuid = dev_uuid, + .output_size = 4, + .output_actions = BT_MESH_DISPLAY_NUMBER | BT_MESH_BEEP | BT_MESH_VIBRATE | BT_MESH_BLINK, + .output_number = output_number, + .complete = prov_complete, +}; + +static void +blemesh_on_reset(int reason) +{ + BLE_HS_LOG(ERROR, "Resetting state; reason=%d\n", reason); +} + +static void +blemesh_on_sync(void) +{ + int err; + ble_addr_t addr; + + console_printf("Bluetooth initialized\n"); + + /* Use NRPA */ + err = ble_hs_id_gen_rnd(1, &addr); + assert(err == 0); + err = ble_hs_id_set_rnd(addr.val); + assert(err == 0); + + err = bt_mesh_init(addr.type, &prov, &comp); + if (err) { + console_printf("Initializing mesh failed (err %d)\n", err); + return; + } + +#if (MYNEWT_VAL(BLE_MESH_SHELL)) + shell_register_default_module("mesh"); +#endif + + console_printf("Mesh initialized\n"); + + if (IS_ENABLED(CONFIG_SETTINGS)) { + settings_load(); + } + + if (bt_mesh_is_provisioned()) { + printk("Mesh network restored from flash\n"); + } +} + +int +main(int argc, char **argv) +{ + +#ifdef ARCH_sim + mcu_sim_parse_args(argc, argv); +#endif + + /* Initialize OS */ + sysinit(); + + /* Initialize the NimBLE host configuration. */ + ble_hs_cfg.reset_cb = blemesh_on_reset; + ble_hs_cfg.sync_cb = blemesh_on_sync; + ble_hs_cfg.store_status_cb = ble_store_util_status_rr; + + hal_gpio_init_out(LED_2, 0); + + health_pub_init(); + + while (1) { + os_eventq_run(os_eventq_dflt_get()); + } + return 0; +} diff --git a/lib/bt/host/nimble/nimble/apps/blemesh/syscfg.yml b/lib/bt/host/nimble/nimble/apps/blemesh/syscfg.yml new file mode 100644 index 00000000..4424b14d --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/blemesh/syscfg.yml @@ -0,0 +1,42 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +syscfg.vals: + # Enable the shell task. + SHELL_TASK: 1 + + # Set log level to info (disable debug logging). + LOG_LEVEL: 1 + + # Default task settings + OS_MAIN_STACK_SIZE: 768 + + # MCUmgr SMP is not supported in this app, so disable smp-over-shell. + SHELL_MGMT: 0 + + BLE_MESH: 1 + MSYS_1_BLOCK_COUNT: 48 + + BLE_MESH_ADV_BUF_COUNT: 20 + BLE_MESH_TX_SEG_MAX: 6 + + BLE_MESH_SETTINGS: 0 + CONFIG_NFFS: 0 + + # Whether to save data to sys/config, or just keep it in RAM. + BLE_STORE_CONFIG_PERSIST: 0 diff --git a/lib/bt/host/nimble/nimble/apps/blemesh_light/pkg.yml b/lib/bt/host/nimble/nimble/apps/blemesh_light/pkg.yml new file mode 100644 index 00000000..bb37ffb5 --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/blemesh_light/pkg.yml @@ -0,0 +1,35 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +pkg.name: apps/blemesh_light +pkg.type: app +pkg.description: Sample application for BLE Mesh node with Light model +pkg.author: "Michał Narajowski " +pkg.homepage: "http://mynewt.apache.org/" +pkg.keywords: + +pkg.deps: + - "@apache-mynewt-core/kernel/os" + - "@apache-mynewt-core/sys/console/full" + - "@apache-mynewt-core/sys/log/full" + - "@apache-mynewt-core/sys/log/modlog" + - "@apache-mynewt-core/sys/stats/full" + - "@apache-mynewt-core/sys/shell" + - nimble/host + - nimble/host/services/gap + - nimble/host/services/gatt + - nimble/host/store/config diff --git a/lib/bt/host/nimble/nimble/apps/blemesh_light/src/light_model.c b/lib/bt/host/nimble/nimble/apps/blemesh_light/src/light_model.c new file mode 100644 index 00000000..f666fe26 --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/blemesh_light/src/light_model.c @@ -0,0 +1,242 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "syscfg/syscfg.h" + +#if MYNEWT_VAL(BLE_MESH_SHELL_MODELS) + +#include "mesh/mesh.h" +#include "bsp.h" +#include "pwm/pwm.h" +#include "light_model.h" +#include "ws2812.h" + +#if (!MYNEWT_VAL(USE_NEOPIXEL)) +#if MYNEWT_VAL(PWM_0) +struct pwm_dev *pwm0; +#endif +#if MYNEWT_VAL(PWM_1) +struct pwm_dev *pwm1; +#endif +#if MYNEWT_VAL(PWM_2) +struct pwm_dev *pwm2; +#endif +#if MYNEWT_VAL(PWM_3) +struct pwm_dev *pwm3; +#endif + +static uint16_t top_val; +#endif + +#if (MYNEWT_VAL(USE_NEOPIXEL)) +static uint32_t neopixel[WS2812_NUM_LED]; +#endif + +static uint8_t gen_onoff_state; +static int16_t gen_level_state; + +static void light_set_lightness(uint8_t percentage) +{ +#if (!MYNEWT_VAL(USE_NEOPIXEL)) + int rc; + + uint16_t pwm_val = (uint16_t) (percentage * top_val / 100); + +#if MYNEWT_VAL(PWM_0) + rc = pwm_set_duty_cycle(pwm0, 0, pwm_val); + assert(rc == 0); +#endif +#if MYNEWT_VAL(PWM_1) + rc = pwm_set_duty_cycle(pwm1, 0, pwm_val); + assert(rc == 0); +#endif +#if MYNEWT_VAL(PWM_2) + rc = pwm_set_duty_cycle(pwm2, 0, pwm_val); + assert(rc == 0); +#endif +#if MYNEWT_VAL(PWM_3) + rc = pwm_set_duty_cycle(pwm3, 0, pwm_val); + assert(rc == 0); +#endif +#else + int i; + uint32_t lightness; + uint8_t max_lightness = 0x1f; + + lightness = (uint8_t) (percentage * max_lightness / 100); + + for (i = 0; i < WS2812_NUM_LED; i++) { + neopixel[i] = (lightness | lightness << 8 | lightness << 16); + } + ws2812_write(neopixel); +#endif +} + +static void update_light_state(void) +{ + uint16_t level = (uint16_t)gen_level_state; + int percent = 100 * level / 0xffff; + + if (gen_onoff_state == 0) { + percent = 0; + } + light_set_lightness((uint8_t) percent); +} + +int light_model_gen_onoff_get(struct bt_mesh_model *model, uint8_t *state) +{ + *state = gen_onoff_state; + return 0; +} + +int light_model_gen_onoff_set(struct bt_mesh_model *model, uint8_t state) +{ + gen_onoff_state = state; + update_light_state(); + return 0; +} + +int light_model_gen_level_get(struct bt_mesh_model *model, int16_t *level) +{ + *level = gen_level_state; + return 0; +} + +int light_model_gen_level_set(struct bt_mesh_model *model, int16_t level) +{ + gen_level_state = level; + if ((uint16_t)gen_level_state > 0x0000) { + gen_onoff_state = 1; + } + if ((uint16_t)gen_level_state == 0x0000) { + gen_onoff_state = 0; + } + update_light_state(); + return 0; +} + +int light_model_light_lightness_get(struct bt_mesh_model *model, int16_t *lightness) +{ + return light_model_gen_level_get(model, lightness); +} + +int light_model_light_lightness_set(struct bt_mesh_model *model, int16_t lightness) +{ + return light_model_gen_level_set(model, lightness); +} + +#if (!MYNEWT_VAL(USE_NEOPIXEL)) +struct pwm_dev_cfg dev_conf = { + .n_cycles = 0, + .int_prio = 3, +}; + +#if MYNEWT_VAL(PWM_0) +static struct pwm_chan_cfg led1_conf = { + .pin = LED_1, + .inverted = true, +}; +#endif + +#if MYNEWT_VAL(PWM_1) +static struct pwm_chan_cfg led2_conf = { + .pin = LED_2, + .inverted = true, +}; +#endif + +#if MYNEWT_VAL(PWM_2) +static struct pwm_chan_cfg led3_conf = { + .pin = LED_3, + .inverted = true, +}; +#endif +#endif + +#if MYNEWT_VAL(PWM_3) +static struct pwm_chan_cfg led4_conf = { + .pin = LED_4, + .inverted = true, +}; +#endif + +#if (!MYNEWT_VAL(USE_NEOPIXEL)) +void init_pwm_dev(struct pwm_dev **pwm, char *dev_name, struct pwm_chan_cfg *chan_cfg) +{ + int rc = 0; + + *pwm = (struct pwm_dev *) os_dev_open(dev_name, 0, NULL); + assert(pwm); + rc = pwm_configure_device(*pwm, &dev_conf); + assert(rc == 0); + rc = pwm_configure_channel(*pwm, 0, chan_cfg); + assert(rc == 0); + rc = pwm_enable(*pwm); + assert(rc == 0); +} + +int pwm_init(void) +{ + +#if MYNEWT_VAL(PWM_0) + init_pwm_dev(&pwm0, "pwm0", &led1_conf); +#endif + +#if MYNEWT_VAL(PWM_1) + init_pwm_dev(&pwm1, "pwm1", &led2_conf); +#endif + +#if MYNEWT_VAL(PWM_2) + init_pwm_dev(&pwm2, "pwm2", &led3_conf); +#endif + +#if MYNEWT_VAL(PWM_3) + init_pwm_dev(&pwm3, "pwm3", &led4_conf); +#endif + + if (!pwm0) { + return 0; + } + + top_val = (uint16_t) pwm_get_top_value(pwm0); + update_light_state(); + + return 0; +} +#endif +#endif + +int light_model_init(void) +{ +#if MYNEWT_VAL(BLE_MESH_SHELL_MODELS) + int rc; +#if (!MYNEWT_VAL(USE_NEOPIXEL)) + rc = pwm_init(); + assert(rc == 0); +#else + rc = ws2812_init(); + assert(rc == 0); + update_light_state(); +#endif + return rc; +#else + return 0; +#endif +} + diff --git a/lib/bt/host/nimble/nimble/apps/blemesh_light/src/light_model.h b/lib/bt/host/nimble/nimble/apps/blemesh_light/src/light_model.h new file mode 100644 index 00000000..1f6f3c44 --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/blemesh_light/src/light_model.h @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + * Copyright (c) 2017 Intel Corporation + * + */ + +#ifndef __BT_MESH_LIGHT_MODEL_H +#define __BT_MESH_LIGHT_MODEL_H + +#include "syscfg/syscfg.h" +#include "mesh/mesh.h" + +int light_model_gen_onoff_get(struct bt_mesh_model *model, uint8_t *state); +int light_model_gen_onoff_set(struct bt_mesh_model *model, uint8_t state); +int light_model_gen_level_get(struct bt_mesh_model *model, int16_t *level); +int light_model_gen_level_set(struct bt_mesh_model *model, int16_t level); +int light_model_light_lightness_get(struct bt_mesh_model *model, int16_t *lightness); +int light_model_light_lightness_set(struct bt_mesh_model *model, int16_t lightness); +int light_model_init(void); + +#endif diff --git a/lib/bt/host/nimble/nimble/apps/blemesh_light/src/main.c b/lib/bt/host/nimble/nimble/apps/blemesh_light/src/main.c new file mode 100644 index 00000000..70deede1 --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/blemesh_light/src/main.c @@ -0,0 +1,113 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +#include +#include "sysinit/sysinit.h" +#include "os/os.h" +#include "mesh/mesh.h" +#include "console/console.h" +#include "bsp/bsp.h" +#include "shell/shell.h" + +#include "host/ble_hs.h" +#include "mesh/glue.h" +#include "mesh/testing.h" +#include "mesh/model_srv.h" +#include "light_model.h" + + +static void model_bound_cb(uint16_t addr, struct bt_mesh_model *model, + uint16_t key_idx) +{ + int rc; + + console_printf("Model bound: remote addr 0x%04x key_idx 0x%04x model %p\n", + addr, key_idx, model); + + if (model->id != BT_MESH_MODEL_ID_GEN_LEVEL_SRV) { + return; + } + + /* Hack for demo purposes */ + rc = bt_test_bind_app_key_to_model(model, key_idx, + BT_MESH_MODEL_ID_LIGHT_LIGHTNESS_SRV); + + if (rc) { + console_printf("Failed to bind light lightness srv model to app_key"); + } else { + console_printf("Successfuly bound light lightness srv model to app_key"); + } +} + +static struct bt_test_cb bt_test_cb = { + .mesh_model_bound = model_bound_cb, +}; + +static void +blemesh_on_reset(int reason) +{ + BLE_HS_LOG(ERROR, "Resetting state; reason=%d\n", reason); +} + +static void +blemesh_on_sync(void) +{ + console_printf("Bluetooth initialized\n"); + + shell_register_default_module("mesh"); + + bt_test_cb_register(&bt_test_cb); + + light_model_init(); + bt_mesh_set_gen_onoff_srv_cb(light_model_gen_onoff_get, + light_model_gen_onoff_set); + bt_mesh_set_gen_level_srv_cb(light_model_gen_level_get, + light_model_gen_level_set); + bt_mesh_set_light_lightness_srv_cb(light_model_light_lightness_get, + light_model_light_lightness_set); + + console_printf("Mesh initialized\n"); + + if (IS_ENABLED(CONFIG_SETTINGS)) { + settings_load(); + } + + if (bt_mesh_is_provisioned()) { + printk("Mesh network restored from flash\n"); + } + + /* Hack for demo purposes */ + bt_test_shell_init(); +} + +int +main(void) +{ + /* Initialize OS */ + sysinit(); + + /* Initialize the NimBLE host configuration. */ + ble_hs_cfg.reset_cb = blemesh_on_reset; + ble_hs_cfg.sync_cb = blemesh_on_sync; + ble_hs_cfg.store_status_cb = ble_store_util_status_rr; + + while (1) { + os_eventq_run(os_eventq_dflt_get()); + } + return 0; +} diff --git a/lib/bt/host/nimble/nimble/apps/blemesh_light/src/ws2812.c b/lib/bt/host/nimble/nimble/apps/blemesh_light/src/ws2812.c new file mode 100644 index 00000000..f27a182b --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/blemesh_light/src/ws2812.c @@ -0,0 +1,138 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "syscfg/syscfg.h" + +#if (MYNEWT_VAL(USE_NEOPIXEL)) + +#include +#include +#include +#include "sysinit/sysinit.h" +#include "os/os.h" +#include "bsp/bsp.h" +#include "pwm/pwm.h" +#include "nrfx.h" +#include "nrfx_pwm.h" +#include "ws2812.h" + +#define BITS_PER_SEQ (24) +#define BIT0 (0x8000 | 6) +#define BIT1 (0x8000 | 11) + +static const nrfx_pwm_t pwm = NRFX_PWM_INSTANCE(WS2812_PWM); + +static const nrfx_pwm_config_t pwm_config = { + .output_pins = { WS2812_GPIO, NRFX_PWM_PIN_NOT_USED, NRFX_PWM_PIN_NOT_USED, NRFX_PWM_PIN_NOT_USED }, + .irq_priority = 3, + .base_clock = NRF_PWM_CLK_16MHz, + .count_mode = NRF_PWM_MODE_UP, + .top_value = 20, + .load_mode = NRF_PWM_LOAD_COMMON, + .step_mode = NRF_PWM_STEP_AUTO, +}; + +static uint16_t pwm_seq_values[2][BITS_PER_SEQ]; + +static const nrf_pwm_sequence_t pwm_seq[2] = { + { + .values.p_raw = pwm_seq_values[0], + .length = BITS_PER_SEQ, + .repeats = 0, + .end_delay = 0, + }, { + .values.p_raw = pwm_seq_values[1], + .length = BITS_PER_SEQ, + .repeats = 0, + .end_delay = 0, + }, +}; + +static uint32_t led_color[WS2812_NUM_LED]; +static int led_idx; + +static void +load_pixel(void) +{ + uint16_t *seq_values; + uint32_t grb; + int i; + + seq_values = pwm_seq_values[led_idx & 1]; + grb = led_color[led_idx]; + + for (i = 0; i < BITS_PER_SEQ; i++) { + *seq_values = grb & 0x800000 ? BIT1 : BIT0; + grb <<= 1; + seq_values++; + } + + led_idx++; +} + +static void +pwm_handler_func(nrfx_pwm_evt_type_t event_type) +{ + switch (event_type) { + case NRFX_PWM_EVT_END_SEQ0: + case NRFX_PWM_EVT_END_SEQ1: + load_pixel(); + break; + default: + break; + } +} + +int +ws2812_init(void) +{ + nrfx_err_t err; + + err = nrfx_pwm_init(&pwm, &pwm_config, pwm_handler_func); + + return err != NRFX_SUCCESS; +} + +int +ws2812_write(const uint32_t *rgb) +{ + uint32_t grb; + int i; + + for (i = 0; i < WS2812_NUM_LED; i++) { + grb = 0; + grb |= (rgb[i] & 0x00FF00) << 8; + grb |= (rgb[i] & 0xFF0000) >> 8; + grb |= (rgb[i] & 0x0000FF); + + led_color[i] = grb; + } + + led_idx = 0; + + load_pixel(); + load_pixel(); + nrfx_pwm_complex_playback(&pwm, &pwm_seq[0], &pwm_seq[1], WS2812_NUM_LED, + NRFX_PWM_FLAG_SIGNAL_END_SEQ0 | + NRFX_PWM_FLAG_SIGNAL_END_SEQ1 | + NRFX_PWM_FLAG_STOP); + + return 0; +} +#endif diff --git a/lib/bt/host/nimble/nimble/apps/blemesh_light/src/ws2812.h b/lib/bt/host/nimble/nimble/apps/blemesh_light/src/ws2812.h new file mode 100644 index 00000000..93b8faf8 --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/blemesh_light/src/ws2812.h @@ -0,0 +1,42 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef __WS2812_H__ +#define __WS2812_H__ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define WS2812_PWM 0 +#define WS2812_GPIO 30 +#define WS2812_NUM_LED 32 + +int ws2812_init(void); + +int ws2812_write(const uint32_t *rgb); + +#ifdef __cplusplus +} +#endif + +#endif /* __WS2812_H__ */ diff --git a/lib/bt/host/nimble/nimble/apps/blemesh_light/syscfg.yml b/lib/bt/host/nimble/nimble/apps/blemesh_light/syscfg.yml new file mode 100644 index 00000000..826d9d5f --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/blemesh_light/syscfg.yml @@ -0,0 +1,66 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +syscfg.defs: + USE_NEOPIXEL: + value: 0 + +syscfg.vals: + # Enable the shell task. + SHELL_TASK: 1 + + # Set log level to info (disable debug logging). + LOG_LEVEL: 1 + + # Default task settings + OS_MAIN_STACK_SIZE: 768 + + # SMP is not supported in this app, so disable smp-over-shell. + SHELL_MGMT: 0 + + MSYS_1_BLOCK_COUNT: 80 + + BLE_MESH_ADV_BUF_COUNT: 20 + BLE_MESH_TX_SEG_MAX: 6 + + BLE_MESH: 1 + BLE_MESH_SHELL: 1 + BLE_MESH_PROV: 1 + BLE_MESH_PB_ADV: 1 + BLE_MESH_PB_GATT: 1 + BLE_MESH_GATT_PROXY: 1 + BLE_MESH_TESTING: 1 + BLE_MESH_FRIEND: 0 + BLE_MESH_CFG_CLI: 1 + BLE_MESH_HEALTH_CLI: 0 + BLE_MESH_SHELL_MODELS: 1 + BLE_MESH_OOB_OUTPUT_ACTIONS: 0 + BLE_MESH_SETTINGS: 0 + CONFIG_NFFS: 0 + + USE_NEOPIXEL: 0 + + # Whether to save data to sys/config, or just keep it in RAM. + BLE_STORE_CONFIG_PERSIST: 0 + +syscfg.vals.BLE_MESH_SHELL_MODELS: + PWM_0: 1 + PWM_1: 1 + PWM_2: 1 + PWM_3: 1 + diff --git a/lib/bt/host/nimble/nimble/apps/blemesh_models_example_1/README.md b/lib/bt/host/nimble/nimble/apps/blemesh_models_example_1/README.md new file mode 100644 index 00000000..fde49536 --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/blemesh_models_example_1/README.md @@ -0,0 +1,79 @@ +### Bluetooth: Mesh OnOff Model + + +#### Overview + +This is a simple application demonstrating a Bluetooth mesh multi-element node. +Each element has a mesh onoff client and server +model which controls one of the 4 sets of buttons and LEDs . + +Prior to provisioning, an unprovisioned beacon is broadcast that contains +a UUID. Each button controls the state of its +corresponding LED and does not initiate any mesh activity. + +The models for button 1 and LED 1 are in the node's root element. +The 3 remaining button/LED pairs are in elements 1 through 3. +Assuming the provisioner assigns 0x100 to the root element, +the secondary elements will appear at 0x101, 0x102 and 0x103. + +After provisioning, the button clients must +be configured to publish and the LED servers to subscribe. + +If a LED server is provided with a publish address, it will +also publish its status on an onoff state change. + +If a button is pressed only once within a 1 second interval, it sends an +"on" message. If it is pressed more than once, it +sends an "off" message. The buttons are quite noisy and are debounced in +the button_pressed() interrupt handler. An interrupt within 250ms of the +previous is discarded. This might seem a little clumsy, but the alternative of +using one button for "on" and another for "off" would reduce the number +of onoff clients from 4 to 2. + +#### Requirements +************ + +This sample has been tested on the Nordic nRF52840-PDK board, but would +likely also run on the nrf52_pca10040 board. + +#### Building and Running +******************** + +Prior to provisioning, each button controls its corresponding LED as one +would expect with an actual switch. + +Provisioning is done using the BlueZ meshctl utility. Below is an example that +binds button 2 and LED 1 to application key 1. It then configures button 2 +to publish to group 0xc000 and LED 1 to subscribe to that group. + +``` +discover-unprovisioned on +provision +menu config +target 0100 +appkey-add 1 +bind 0 1 1000 # bind appkey 1 to LED server on element 0 (unicast 0100) +sub-add 0100 c000 1000 # add subscription to group address c000 to the LED server +bind 1 1 1001 # bind appkey 1 to button 2 on element 1 (unicast 0101) +pub-set 0101 c000 1 0 0 1001 # publish button 2 to group address c000 +``` + +The meshctl utility maintains a persistent JSON database containing +the mesh configuration. As additional nodes (boards) are provisioned, it +assigns sequential unicast addresses based on the number of elements +supported by the node. This example supports 4 elements per node. + +The first or root element of the node contains models for configuration, +health, and onoff. The secondary elements only +have models for onoff. The meshctl target for configuration must be the +root element's unicast address as it is the only one that has a +configuration server model. + +If meshctl is gracefully exited, it can be restarted and reconnected to +network 0x0. + +The meshctl utility also supports a onoff model client that can be used to +change the state of any LED that is bound to application key 0x1. +This is done by setting the target to the unicast address of the element +that has that LED's model and issuing the onoff command. +Group addresses are not supported. diff --git a/lib/bt/host/nimble/nimble/apps/blemesh_models_example_1/pkg.yml b/lib/bt/host/nimble/nimble/apps/blemesh_models_example_1/pkg.yml new file mode 100644 index 00000000..44e2d79c --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/blemesh_models_example_1/pkg.yml @@ -0,0 +1,32 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +pkg.name: apps/blemesh_models_example_1 +pkg.type: app +pkg.description: Sample application for BLE Mesh node with on/off model on nRF52840pdk +pkg.author: "Michał Narajowski " +pkg.homepage: "http://mynewt.apache.org/" +pkg.keywords: + +pkg.deps: + - "@apache-mynewt-core/kernel/os" + - "@apache-mynewt-core/sys/console/full" + - "@apache-mynewt-core/sys/log/full" + - "@apache-mynewt-core/sys/stats/full" + - nimble/host + - nimble/host/services/gap + - nimble/host/services/gatt diff --git a/lib/bt/host/nimble/nimble/apps/blemesh_models_example_1/src/main.c b/lib/bt/host/nimble/nimble/apps/blemesh_models_example_1/src/main.c new file mode 100644 index 00000000..80a69c70 --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/blemesh_models_example_1/src/main.c @@ -0,0 +1,700 @@ +/* main.c - Application main entry point */ + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/* + * This application is specific to the Nordic nRF52840-PDK board. + * + * It supports the 4 buttons and 4 LEDs as mesh clients and servers. + * + * Prior to provisioning, a button inverts the state of the + * corresponding LED. + * + * Button and LED 1 are in the root node. + * The 3 remaining button/LED pairs are in element 1 through 3. + * Assuming the provisioner assigns 0x100 to the root node, + * the secondary elements will appear at 0x101, 0x102 and 0x103. + * + * It's anticipated that after provisioning, the button clients would + * be configured to publish and the LED servers to subscribe. + * + * If a LED server is provided with a publish address, it will + * also publish its status on a state change. + * + * Messages from a button to its corresponding LED are ignored as + * the LED's state has already been changed locally by the button client. + * + * The buttons are debounced at a nominal 250ms. That value can be + * changed as needed. + * + */ + +#include "os/mynewt.h" +#include "bsp/bsp.h" +#include "console/console.h" +#include "hal/hal_gpio.h" +#include "host/ble_hs.h" +#include "mesh/glue.h" +#include "mesh/mesh.h" + +#define CID_RUNTIME 0x05C3 + +/* Model Operation Codes */ +#define BT_MESH_MODEL_OP_GEN_ONOFF_GET BT_MESH_MODEL_OP_2(0x82, 0x01) +#define BT_MESH_MODEL_OP_GEN_ONOFF_SET BT_MESH_MODEL_OP_2(0x82, 0x02) +#define BT_MESH_MODEL_OP_GEN_ONOFF_SET_UNACK BT_MESH_MODEL_OP_2(0x82, 0x03) +#define BT_MESH_MODEL_OP_GEN_ONOFF_STATUS BT_MESH_MODEL_OP_2(0x82, 0x04) + +static int gen_onoff_set(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf); + +static int gen_onoff_set_unack(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf); + +static int gen_onoff_get(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf); + +static int gen_onoff_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf); + +/* + * Client Configuration Declaration + */ + +static struct bt_mesh_cfg_cli cfg_cli = { +}; + +/* + * Health Server Declaration + */ + +static struct bt_mesh_health_srv health_srv = { +}; + +/* + * Publication Declarations + * + * The publication messages are initialized to the + * the size of the opcode + content + * + * For publication, the message must be in static or global as + * it is re-transmitted several times. This occurs + * after the function that called bt_mesh_model_publish() has + * exited and the stack is no longer valid. + * + * Note that the additional 4 bytes for the AppMIC is not needed + * because it is added to a stack variable at the time a + * transmission occurs. + * + */ + +static struct bt_mesh_model_pub health_pub; +static struct bt_mesh_model_pub gen_onoff_pub_srv; +static struct bt_mesh_model_pub gen_onoff_pub_cli; +static struct bt_mesh_model_pub gen_onoff_pub_srv_s_0; +static struct bt_mesh_model_pub gen_onoff_pub_cli_s_0; +static struct bt_mesh_model_pub gen_onoff_pub_srv_s_1; +static struct bt_mesh_model_pub gen_onoff_pub_cli_s_1; +static struct bt_mesh_model_pub gen_onoff_pub_srv_s_2; +static struct bt_mesh_model_pub gen_onoff_pub_cli_s_2; + +static struct os_mbuf *bt_mesh_pub_msg_health_pub; +static struct os_mbuf *bt_mesh_pub_msg_gen_onoff_pub_srv; +static struct os_mbuf *bt_mesh_pub_msg_gen_onoff_pub_cli; +static struct os_mbuf *bt_mesh_pub_msg_gen_onoff_pub_srv_s_0; +static struct os_mbuf *bt_mesh_pub_msg_gen_onoff_pub_cli_s_0; +static struct os_mbuf *bt_mesh_pub_msg_gen_onoff_pub_srv_s_1; +static struct os_mbuf *bt_mesh_pub_msg_gen_onoff_pub_cli_s_1; +static struct os_mbuf *bt_mesh_pub_msg_gen_onoff_pub_srv_s_2; +static struct os_mbuf *bt_mesh_pub_msg_gen_onoff_pub_cli_s_2; + +void init_pub(void) +{ + bt_mesh_pub_msg_health_pub = NET_BUF_SIMPLE(1 + 3 + 0); + bt_mesh_pub_msg_gen_onoff_pub_srv = NET_BUF_SIMPLE(2 + 2); + bt_mesh_pub_msg_gen_onoff_pub_cli = NET_BUF_SIMPLE(2 + 2); + bt_mesh_pub_msg_gen_onoff_pub_srv_s_0 = NET_BUF_SIMPLE(2 + 2); + bt_mesh_pub_msg_gen_onoff_pub_cli_s_0 = NET_BUF_SIMPLE(2 + 2); + bt_mesh_pub_msg_gen_onoff_pub_srv_s_1 = NET_BUF_SIMPLE(2 + 2); + bt_mesh_pub_msg_gen_onoff_pub_cli_s_1 = NET_BUF_SIMPLE(2 + 2); + bt_mesh_pub_msg_gen_onoff_pub_srv_s_2 = NET_BUF_SIMPLE(2 + 2); + bt_mesh_pub_msg_gen_onoff_pub_cli_s_2 = NET_BUF_SIMPLE(2 + 2); + + health_pub.msg = bt_mesh_pub_msg_health_pub; + gen_onoff_pub_srv.msg = bt_mesh_pub_msg_gen_onoff_pub_srv; + gen_onoff_pub_cli.msg = bt_mesh_pub_msg_gen_onoff_pub_cli; + gen_onoff_pub_srv_s_0.msg = bt_mesh_pub_msg_gen_onoff_pub_srv_s_0; + gen_onoff_pub_cli_s_0.msg = bt_mesh_pub_msg_gen_onoff_pub_cli_s_0; + gen_onoff_pub_srv_s_1.msg = bt_mesh_pub_msg_gen_onoff_pub_srv_s_1; + gen_onoff_pub_cli_s_1.msg = bt_mesh_pub_msg_gen_onoff_pub_cli_s_1; + gen_onoff_pub_srv_s_2.msg = bt_mesh_pub_msg_gen_onoff_pub_srv_s_2; + gen_onoff_pub_cli_s_2.msg = bt_mesh_pub_msg_gen_onoff_pub_cli_s_2; +} + +/* + * Models in an element must have unique op codes. + * + * The mesh stack dispatches a message to the first model in an element + * that is also bound to an app key and supports the op code in the + * received message. + * + */ + +/* + * OnOff Model Server Op Dispatch Table + * + */ + +static const struct bt_mesh_model_op gen_onoff_srv_op[] = { + { BT_MESH_MODEL_OP_GEN_ONOFF_GET, 0, gen_onoff_get }, + { BT_MESH_MODEL_OP_GEN_ONOFF_SET, 2, gen_onoff_set }, + { BT_MESH_MODEL_OP_GEN_ONOFF_SET_UNACK, 2, gen_onoff_set_unack }, + BT_MESH_MODEL_OP_END, +}; + +/* + * OnOff Model Client Op Dispatch Table + */ + +static const struct bt_mesh_model_op gen_onoff_cli_op[] = { + { BT_MESH_MODEL_OP_GEN_ONOFF_STATUS, 1, gen_onoff_status }, + BT_MESH_MODEL_OP_END, +}; + +struct onoff_state { + uint8_t current; + uint8_t previous; + uint8_t led_gpio_pin; +}; + +/* + * Declare and Initialize Element Contexts + * Change to select different GPIO output pins + */ + +static struct onoff_state onoff_state_arr[] = { + { .led_gpio_pin = LED_1 }, + { .led_gpio_pin = LED_2 }, + { .led_gpio_pin = LED_3 }, + { .led_gpio_pin = LED_4 }, +}; + +/* + * + * Element Model Declarations + * + * Element 0 Root Models + */ + +static struct bt_mesh_model root_models[] = { + BT_MESH_MODEL_CFG_SRV, + BT_MESH_MODEL_CFG_CLI(&cfg_cli), + BT_MESH_MODEL_HEALTH_SRV(&health_srv, &health_pub), + BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_ONOFF_SRV, gen_onoff_srv_op, + &gen_onoff_pub_srv, &onoff_state_arr[0]), + BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_ONOFF_CLI, gen_onoff_cli_op, + &gen_onoff_pub_cli, &onoff_state_arr[0]), +}; + +/* + * Element 1 Models + */ + +static struct bt_mesh_model secondary_0_models[] = { + BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_ONOFF_SRV, gen_onoff_srv_op, + &gen_onoff_pub_srv_s_0, &onoff_state_arr[1]), + BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_ONOFF_CLI, gen_onoff_cli_op, + &gen_onoff_pub_cli_s_0, &onoff_state_arr[1]), +}; + +/* + * Element 2 Models + */ + +static struct bt_mesh_model secondary_1_models[] = { + BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_ONOFF_SRV, gen_onoff_srv_op, + &gen_onoff_pub_srv_s_1, &onoff_state_arr[2]), + BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_ONOFF_CLI, gen_onoff_cli_op, + &gen_onoff_pub_cli_s_1, &onoff_state_arr[2]), +}; + +/* + * Element 3 Models + */ + +static struct bt_mesh_model secondary_2_models[] = { + BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_ONOFF_SRV, gen_onoff_srv_op, + &gen_onoff_pub_srv_s_2, &onoff_state_arr[3]), + BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_ONOFF_CLI, gen_onoff_cli_op, + &gen_onoff_pub_cli_s_2, &onoff_state_arr[3]), +}; + +/* + * Button to Client Model Assignments + */ + +struct bt_mesh_model *mod_cli_sw[] = { + &root_models[4], + &secondary_0_models[1], + &secondary_1_models[1], + &secondary_2_models[1], +}; + +/* + * LED to Server Model Assigmnents + */ + +struct bt_mesh_model *mod_srv_sw[] = { + &root_models[3], + &secondary_0_models[0], + &secondary_1_models[0], + &secondary_2_models[0], +}; + +/* + * Root and Secondary Element Declarations + */ + +static struct bt_mesh_elem elements[] = { + BT_MESH_ELEM(0, root_models, BT_MESH_MODEL_NONE), + BT_MESH_ELEM(0, secondary_0_models, BT_MESH_MODEL_NONE), + BT_MESH_ELEM(0, secondary_1_models, BT_MESH_MODEL_NONE), + BT_MESH_ELEM(0, secondary_2_models, BT_MESH_MODEL_NONE), +}; + +static const struct bt_mesh_comp comp = { + .cid = CID_RUNTIME, + .elem = elements, + .elem_count = ARRAY_SIZE(elements), +}; + +struct sw { + uint8_t sw_num; + uint8_t onoff_state; + struct os_callout button_work; + struct os_callout button_timer; +}; + + +static uint8_t button_press_cnt; +static struct sw sw; + +static uint8_t trans_id; +static uint32_t time, last_time; +static uint16_t primary_addr; +static uint16_t primary_net_idx; + +/* + * Generic OnOff Model Server Message Handlers + * + * Mesh Model Specification 3.1.1 + * + */ + +static int gen_onoff_get(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 1 + 4); + struct onoff_state *state = model->user_data; + int rc; + + BT_INFO("addr 0x%04x onoff 0x%02x", + bt_mesh_model_elem(model)->addr, state->current); + bt_mesh_model_msg_init(msg, BT_MESH_MODEL_OP_GEN_ONOFF_STATUS); + net_buf_simple_add_u8(msg, state->current); + + rc = bt_mesh_model_send(model, ctx, msg, NULL, NULL); + if (rc) { + BT_ERR("Unable to send On Off Status response"); + } + + os_mbuf_free_chain(msg); + return rc; +} + +static int gen_onoff_set_unack(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct os_mbuf *msg = model->pub->msg; + struct onoff_state *state = model->user_data; + int err; + + state->current = net_buf_simple_pull_u8(buf); + BT_INFO("addr 0x%02x state 0x%02x", + bt_mesh_model_elem(model)->addr, state->current); + + /* Pin set low turns on LED's on the nrf52840-pca10056 board */ + hal_gpio_write(state->led_gpio_pin, + state->current ? 0 : 1); + + /* + * If a server has a publish address, it is required to + * publish status on a state change + * + * See Mesh Profile Specification 3.7.6.1.2 + * + * Only publish if there is an assigned address + */ + + if (state->previous != state->current && + model->pub->addr != BT_MESH_ADDR_UNASSIGNED) { + BT_INFO("publish last 0x%02x cur 0x%02x", + state->previous, + state->current); + state->previous = state->current; + bt_mesh_model_msg_init(msg, + BT_MESH_MODEL_OP_GEN_ONOFF_STATUS); + net_buf_simple_add_u8(msg, state->current); + err = bt_mesh_model_publish(model); + if (err != 0) { + BT_ERR("bt_mesh_model_publish err %d", err); + return err; + } + } + return 0; +} + +static int gen_onoff_set(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + BT_INFO(""); + int rc; + + rc = gen_onoff_set_unack(model, ctx, buf); + if (rc != 0) { + return rc; + } + + rc = gen_onoff_get(model, ctx, buf); + if (rc != 0) { + return rc; + } + return 0; +} + +static int gen_onoff_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + uint8_t state; + + state = net_buf_simple_pull_u8(buf); + + BT_INFO("Node 0x%04x OnOff status from 0x%04x with state 0x%02x", + bt_mesh_model_elem(model)->addr, ctx->addr, state); + return 0; +} + +static int output_number(bt_mesh_output_action_t action, uint32_t number) +{ + BT_INFO("OOB Number %u", number); + return 0; +} + +static int output_string(const char *str) +{ + BT_INFO("OOB String %s", str); + return 0; +} + +static void prov_complete(uint16_t net_idx, uint16_t addr) +{ + BT_INFO("provisioning complete for net_idx 0x%04x addr 0x%04x", + net_idx, addr); + primary_addr = addr; + primary_net_idx = net_idx; +} + +static void prov_reset(void) +{ + bt_mesh_prov_enable(BT_MESH_PROV_ADV | BT_MESH_PROV_GATT); +} + +static uint8_t dev_uuid[16] = MYNEWT_VAL(BLE_MESH_DEV_UUID); + +#define BUTTON_DEBOUNCE_DELAY_MS 250 + +/* + * Map GPIO pins to button number + * Change to select different GPIO input pins + */ + +static uint8_t pin_to_sw(int pin_pos) +{ + switch (pin_pos) { + case BUTTON_1: return 0; + case BUTTON_2: return 1; + case BUTTON_3: return 2; + case BUTTON_4: return 3; + default:break; + } + + BT_ERR("No match for GPIO pin 0x%08x", pin_pos); + return 0; +} + +static void button_pressed(struct os_event *ev) +{ + int pin_pos = (int ) ev->ev_arg; + /* + * One button press within a 1 second interval sends an on message + * More than one button press sends an off message + */ + + time = k_uptime_get_32(); + + /* debounce the switch */ + if (time < last_time + BUTTON_DEBOUNCE_DELAY_MS) { + last_time = time; + return; + } + + if (button_press_cnt == 0) { + os_callout_reset(&sw.button_timer, os_time_ms_to_ticks32(K_SECONDS(1))); + } + + BT_INFO("button_press_cnt 0x%02x", button_press_cnt); + button_press_cnt++; + + /* The variable pin_pos is the pin position in the GPIO register, + * not the pin number. It's assumed that only one bit is set. + */ + + sw.sw_num = pin_to_sw(pin_pos); + last_time = time; +} + +/* + * Button Count Timer Worker + */ + +static void button_cnt_timer(struct os_event *work) +{ + struct sw *button_sw = work->ev_arg; + + button_sw->onoff_state = button_press_cnt == 1 ? 1 : 0; + BT_INFO("button_press_cnt 0x%02x onoff_state 0x%02x", + button_press_cnt, button_sw->onoff_state); + button_press_cnt = 0; + os_callout_reset(&sw.button_work, 0); +} + +/* + * Button Pressed Worker Task + */ + +static void button_pressed_worker(struct os_event *work) +{ + struct os_mbuf *msg = NET_BUF_SIMPLE(1); + struct bt_mesh_model *mod_cli, *mod_srv; + struct bt_mesh_model_pub *pub_cli, *pub_srv; + struct sw *sw = work->ev_arg; + uint8_t sw_idx = sw->sw_num; + int err; + + mod_cli = mod_cli_sw[sw_idx]; + pub_cli = mod_cli->pub; + + mod_srv = mod_srv_sw[sw_idx]; + pub_srv = mod_srv->pub; + (void)pub_srv; + + /* If unprovisioned, just call the set function. + * The intent is to have switch-like behavior + * prior to provisioning. Once provisioned, + * the button and its corresponding led are no longer + * associated and act independently. So, if a button is to + * control its associated led after provisioning, the button + * must be configured to either publish to the led's unicast + * address or a group to which the led is subscribed. + */ + + if (primary_addr == BT_MESH_ADDR_UNASSIGNED) { + struct bt_mesh_msg_ctx ctx = { + .addr = sw_idx + primary_addr, + }; + + /* This is a dummy message sufficient + * for the led server + */ + + net_buf_simple_add_u8(msg, sw->onoff_state); + gen_onoff_set_unack(mod_srv, &ctx, msg); + goto done; + } + + if (pub_cli->addr == BT_MESH_ADDR_UNASSIGNED) { + goto done; + } + + BT_INFO("publish to 0x%04x onoff 0x%04x sw_idx 0x%04x", + pub_cli->addr, sw->onoff_state, sw_idx); + bt_mesh_model_msg_init(pub_cli->msg, + BT_MESH_MODEL_OP_GEN_ONOFF_SET); + net_buf_simple_add_u8(pub_cli->msg, sw->onoff_state); + net_buf_simple_add_u8(pub_cli->msg, trans_id++); + err = bt_mesh_model_publish(mod_cli); + if (err) { + BT_ERR("bt_mesh_model_publish err %d", err); + } + +done: + os_mbuf_free_chain(msg); +} + +/* Disable OOB security for SILabs Android app */ + +static const struct bt_mesh_prov prov = { + .uuid = dev_uuid, +#if 1 + .output_size = 6, + .output_actions = (BT_MESH_DISPLAY_NUMBER | BT_MESH_DISPLAY_STRING), + .output_number = output_number, + .output_string = output_string, +#else +.output_size = 0, + .output_actions = 0, + .output_number = 0, +#endif + .complete = prov_complete, + .reset = prov_reset, +}; + +void init_led(uint8_t dev) +{ + hal_gpio_init_out(onoff_state_arr[dev].led_gpio_pin, 1); +} + +static struct os_event button_event; + +static void +gpio_irq_handler(void *arg) +{ + button_event.ev_arg = arg; + os_eventq_put(os_eventq_dflt_get(), &button_event); +} + +void init_button(int button) +{ + button_event.ev_cb = button_pressed; + + hal_gpio_irq_init(button, gpio_irq_handler, (void *)button, + HAL_GPIO_TRIG_FALLING, HAL_GPIO_PULL_UP); + hal_gpio_irq_enable(button); +} + +static void +blemesh_on_reset(int reason) +{ + BLE_HS_LOG(ERROR, "Resetting state; reason=%d\n", reason); +} + +static void +blemesh_on_sync(void) +{ + int err; + ble_addr_t addr; + + console_printf("Bluetooth initialized\n"); + + /* Use NRPA */ + err = ble_hs_id_gen_rnd(1, &addr); + assert(err == 0); + err = ble_hs_id_set_rnd(addr.val); + assert(err == 0); + + err = bt_mesh_init(addr.type, &prov, &comp); + if (err) { + console_printf("Initializing mesh failed (err %d)\n", err); + return; + } + + if (IS_ENABLED(CONFIG_SETTINGS)) { + settings_load(); + } + + if (bt_mesh_is_provisioned()) { + console_printf("Mesh network restored from flash\n"); + } + + bt_mesh_prov_enable(BT_MESH_PROV_GATT | BT_MESH_PROV_ADV); + + console_printf("Mesh initialized\n"); +} + +int main(void) +{ +#ifdef ARCH_sim + mcu_sim_parse_args(argc, argv); +#endif + + /* Initialize OS */ + sysinit(); + + BT_INFO("Initializing..."); + + /* Initialize the button debouncer */ + last_time = k_uptime_get_32(); + + /* Initialize button worker task*/ + os_callout_init(&sw.button_work, os_eventq_dflt_get(), + button_pressed_worker, &sw); + + /* Initialize button count timer */ + os_callout_init(&sw.button_timer, os_eventq_dflt_get(), + button_cnt_timer, &sw); + + /* Initialize LED's */ + init_led(0); + init_led(1); + init_led(2); + init_led(3); + + init_button(BUTTON_1); + init_button(BUTTON_2); + init_button(BUTTON_3); + init_button(BUTTON_4); + + init_pub(); + + /* Initialize the NimBLE host configuration. */ + ble_hs_cfg.reset_cb = blemesh_on_reset; + ble_hs_cfg.sync_cb = blemesh_on_sync; + ble_hs_cfg.store_status_cb = ble_store_util_status_rr; + + while (1) { + os_eventq_run(os_eventq_dflt_get()); + } + + return 0; +} diff --git a/lib/bt/host/nimble/nimble/apps/blemesh_models_example_1/syscfg.yml b/lib/bt/host/nimble/nimble/apps/blemesh_models_example_1/syscfg.yml new file mode 100644 index 00000000..969753fe --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/blemesh_models_example_1/syscfg.yml @@ -0,0 +1,38 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +syscfg.vals: + # Set log level to info (disable debug logging). + LOG_LEVEL: 1 + + # Default task settings + OS_MAIN_STACK_SIZE: 768 + + # SMP is not supported in this app, so disable smp-over-shell. + SHELL_MGMT: 0 + + MSYS_1_BLOCK_COUNT: 48 + + BLE_MESH: 1 + BLE_MESH_CFG_CLI: 1 + BLE_MESH_DEV_UUID: "((uint8_t[16]){0xdd, 0xdd, 0})" + BLE_MESH_SETTINGS: 0 + CONFIG_NFFS: 0 + + BLE_MESH_ADV_BUF_COUNT: 20 + BLE_MESH_TX_SEG_MAX: 6 diff --git a/lib/bt/host/nimble/nimble/apps/blemesh_models_example_2/README.md b/lib/bt/host/nimble/nimble/apps/blemesh_models_example_2/README.md new file mode 100644 index 00000000..7bec909e --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/blemesh_models_example_2/README.md @@ -0,0 +1,101 @@ +#### Bluetooth: Mesh Generic OnOff, Generic Level, Lighting & Vendor Models + +##### Overview +******** + +This is a application demonstrating a Bluetooth mesh node in +which Root element has following models + +- Generic OnOff Server +- Generic OnOff Client +- Generic Level Server +- Generic Level Client +- Generic Default Transition Time Server +- Generic Default Transition Time Client +- Generic Power OnOff Server +- Generic Power OnOff Setup Server +- Generic Power OnOff Client +- Light Lightness Server +- Light Lightness Setup Server +- Light Lightness Client +- Light CTL Server +- Light CTL Setup Server +- Light CTL Client +- Vendor Model + +And Secondary element has following models + +- Generic Level Server +- Generic Level Client +- Light CTL Temperature Server + +Prior to provisioning, an unprovisioned beacon is broadcast that contains +a unique UUID. Each button controls the state of its +corresponding LED and does not initiate any mesh activity + +##### Associations of Models with hardware +************************************ +For the nRF52840-PDK board, these are the model associations: + +* LED1 is associated with generic OnOff Server's state which is part of Root element +* LED2 is associated with Vendor Model which is part of Root element +* LED3 is associated with generic Level (ROOT) / Light Lightness Actual value +* LED4 is associated with generic Level (Secondary) / Light CTL Temperature value +* Button1 and Button2 are associated with gen. OnOff Client or Vendor Model which is part of Root element +* Button3 and Button4 are associated with gen. Level Client / Light Lightness Client / Light CTL Client which is part of Root element + +States of Servers are bounded as per Bluetooth SIG Mesh Model Specification v1.0 + +After provisioning, the button clients must +be configured to publish and the LED servers to subscribe. +If a server is provided with a publish address, it will +also publish its relevant status. + +##### Requirements +************ +This sample has been tested on the Nordic nRF52840-PDK board, but would +likely also run on the nrf52_pca10040 board. + + +##### Running +************ + +Provisioning is done using the BlueZ meshctl utility. In this example, we'll use meshctl commands to bind: + +- Button1, Button2, and LED1 to application key 1. It then configures Button1 and Button2 + to publish to group 0xC000 and LED1 to subscribe to that group. +- Button3, Button4, and LED3 to application key 1. It then configures Button3 and Button4 + to publish to group 0xC000 and LED3 to subscribe to that group. + +``` +discover-unprovisioned on +provision +menu config +target 0100 +appkey-add 1 +bind 0 1 1000 +bind 0 1 1001 +bind 0 1 1002 +bind 0 1 1003 +sub-add 0100 c000 1000 +sub-add 0100 c000 1002 +pub-set 0100 c000 1 0 5 1001 +pub-set 0100 c000 1 0 5 1003 +``` + +The meshctl utility maintains a persistent JSON database containing +the mesh configuration. As additional nodes (boards) are provisioned, it +assigns sequential unicast addresses based on the number of elements +supported by the node. This example supports 2 elements per node. + +The meshctl target for configuration must be the root element's unicast +address as it is the only one that has a configuration server model. If +meshctl is gracefully exited, it can be restarted and reconnected to +network 0x0. + +The meshctl utility also supports a onoff model client that can be used to +change the state of any LED that is bound to application key 0x1. +This is done by setting the target to the unicast address of the element +that has that LED's model and issuing the onoff command. +Group addresses are not supported. + diff --git a/lib/bt/host/nimble/nimble/apps/blemesh_models_example_2/pkg.yml b/lib/bt/host/nimble/nimble/apps/blemesh_models_example_2/pkg.yml new file mode 100644 index 00000000..194b5546 --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/blemesh_models_example_2/pkg.yml @@ -0,0 +1,38 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +pkg.name: apps/blemesh_models_example_2 +pkg.type: app +pkg.description: Sample application for BLE Mesh node with on/off, level, light and vendor models on nRF52840pdk +pkg.author: "Michał Narajowski " +pkg.homepage: "http://mynewt.apache.org/" +pkg.keywords: + +pkg.deps: + - "@apache-mynewt-core/kernel/os" + - "@apache-mynewt-core/sys/console/full" + - "@apache-mynewt-core/sys/log/full" + - "@apache-mynewt-core/sys/stats/full" + - "@apache-mynewt-core/encoding/base64" + - "@apache-mynewt-core/sys/config" + - nimble/host + - nimble/host/services/gap + - nimble/host/services/gatt + +pkg.lflags: + - -DFLOAT_SUPPORT + - -lm diff --git a/lib/bt/host/nimble/nimble/apps/blemesh_models_example_2/src/app_gpio.c b/lib/bt/host/nimble/nimble/apps/blemesh_models_example_2/src/app_gpio.c new file mode 100644 index 00000000..76e361af --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/blemesh_models_example_2/src/app_gpio.c @@ -0,0 +1,95 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* Bluetooth: Mesh Generic OnOff, Generic Level, Lighting & Vendor Models + * + * Copyright (c) 2018 Vikrant More + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "bsp/bsp.h" +#include "console/console.h" +#include "hal/hal_gpio.h" +#include "mesh/mesh.h" + +#include "app_gpio.h" +#include "publisher.h" + +int button_device[] = { + BUTTON_1, + BUTTON_2, + BUTTON_3, + BUTTON_4, +}; + +int led_device[] = { + LED_1, + LED_2, + LED_3, + LED_4, +}; + +static struct os_callout button_work; + +static void button_pressed(struct os_event *ev) +{ + os_callout_reset(&button_work, 0); +} + +static struct os_event button_event; + +static void gpio_irq_handler(void *arg) +{ + button_event.ev_arg = arg; + os_eventq_put(os_eventq_dflt_get(), &button_event); +} + +void app_gpio_init(void) +{ + /* LEDs configiuratin & setting */ + + hal_gpio_init_out(led_device[0], 1); + hal_gpio_init_out(led_device[1], 1); + hal_gpio_init_out(led_device[2], 1); + hal_gpio_init_out(led_device[3], 1); + + /* Buttons configiuratin & setting */ + + os_callout_init(&button_work, os_eventq_dflt_get(), publish, NULL); + + button_event.ev_cb = button_pressed; + + hal_gpio_irq_init(button_device[0], gpio_irq_handler, NULL, + HAL_GPIO_TRIG_FALLING, HAL_GPIO_PULL_UP); + hal_gpio_irq_enable(button_device[0]); + + hal_gpio_irq_init(button_device[1], gpio_irq_handler, NULL, + HAL_GPIO_TRIG_FALLING, HAL_GPIO_PULL_UP); + hal_gpio_irq_enable(button_device[1]); + + hal_gpio_irq_init(button_device[2], gpio_irq_handler, NULL, + HAL_GPIO_TRIG_FALLING, HAL_GPIO_PULL_UP); + hal_gpio_irq_enable(button_device[2]); + + hal_gpio_irq_init(button_device[3], gpio_irq_handler, NULL, + HAL_GPIO_TRIG_FALLING, HAL_GPIO_PULL_UP); + hal_gpio_irq_enable(button_device[3]); +} + diff --git a/lib/bt/host/nimble/nimble/apps/blemesh_models_example_2/src/app_gpio.h b/lib/bt/host/nimble/nimble/apps/blemesh_models_example_2/src/app_gpio.h new file mode 100644 index 00000000..6eabc1ce --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/blemesh_models_example_2/src/app_gpio.h @@ -0,0 +1,36 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* Bluetooth: Mesh Generic OnOff, Generic Level, Lighting & Vendor Models + * + * Copyright (c) 2018 Vikrant More + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef _APP_GPIO_H +#define _APP_GPIO_H + +/* GPIO */ +extern int button_device[]; +extern int led_device[]; + +void app_gpio_init(void); + +#endif diff --git a/lib/bt/host/nimble/nimble/apps/blemesh_models_example_2/src/ble_mesh.c b/lib/bt/host/nimble/nimble/apps/blemesh_models_example_2/src/ble_mesh.c new file mode 100644 index 00000000..021d8e08 --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/blemesh_models_example_2/src/ble_mesh.c @@ -0,0 +1,116 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* Bluetooth: Mesh Generic OnOff, Generic Level, Lighting & Vendor Models + * + * Copyright (c) 2018 Vikrant More + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "console/console.h" + +#include "common.h" +#include "ble_mesh.h" +#include "device_composition.h" + +#define OOB_AUTH_ENABLE 1 + +#ifdef OOB_AUTH_ENABLE + +static int output_number(bt_mesh_output_action_t action, uint32_t number) +{ + printk("OOB Number: %lu\n", number); + return 0; +} + +static int output_string(const char *str) +{ + printk("OOB String: %s\n", str); + return 0; +} + +#endif + +static void prov_complete(uint16_t net_idx, uint16_t addr) +{ + printk("Local node provisioned, primary address 0x%04x\n", addr); +} + +static void prov_reset(void) +{ + bt_mesh_prov_enable(BT_MESH_PROV_ADV | BT_MESH_PROV_GATT); +} + +static uint8_t dev_uuid[16] = MYNEWT_VAL(BLE_MESH_DEV_UUID); + +static const struct bt_mesh_prov prov = { + .uuid = dev_uuid, + +#ifdef OOB_AUTH_ENABLE + + .output_size = 6, + .output_actions = BT_MESH_DISPLAY_NUMBER | BT_MESH_DISPLAY_STRING, + .output_number = output_number, + .output_string = output_string, + +#endif + + .complete = prov_complete, + .reset = prov_reset, +}; + +void blemesh_on_reset(int reason) +{ + BLE_HS_LOG(ERROR, "Resetting state; reason=%d\n", reason); +} + +void blemesh_on_sync(void) +{ + int err; + ble_addr_t addr; + + console_printf("Bluetooth initialized\n"); + + /* Use NRPA */ + err = ble_hs_id_gen_rnd(1, &addr); + assert(err == 0); + err = ble_hs_id_set_rnd(addr.val); + assert(err == 0); + + err = bt_mesh_init(addr.type, &prov, &comp); + if (err) { + console_printf("Initializing mesh failed (err %d)\n", err); + return; + } + + if (IS_ENABLED(CONFIG_SETTINGS)) { + settings_load(); + } + + if (bt_mesh_is_provisioned()) { + console_printf("Mesh network restored from flash\n"); + } + + bt_mesh_prov_enable(BT_MESH_PROV_GATT | BT_MESH_PROV_ADV); + + console_printf("Mesh initialized\n"); + + bt_initialized(); +} diff --git a/lib/bt/host/nimble/nimble/apps/blemesh_models_example_2/src/ble_mesh.h b/lib/bt/host/nimble/nimble/apps/blemesh_models_example_2/src/ble_mesh.h new file mode 100644 index 00000000..c02af243 --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/blemesh_models_example_2/src/ble_mesh.h @@ -0,0 +1,73 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* Bluetooth: Mesh Generic OnOff, Generic Level, Lighting & Vendor Models + * + * Copyright (c) 2018 Vikrant More + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef _BLE_MESH_H +#define _BLE_MESH_H + +#include "mesh/mesh.h" +#include "mesh/glue.h" + +/* Model Operation Codes */ +#define BT_MESH_MODEL_OP_GEN_ONOFF_GET BT_MESH_MODEL_OP_2(0x82, 0x01) +#define BT_MESH_MODEL_OP_GEN_ONOFF_SET BT_MESH_MODEL_OP_2(0x82, 0x02) +#define BT_MESH_MODEL_OP_GEN_ONOFF_SET_UNACK BT_MESH_MODEL_OP_2(0x82, 0x03) +#define BT_MESH_MODEL_OP_GEN_ONOFF_STATUS BT_MESH_MODEL_OP_2(0x82, 0x04) + +#define BT_MESH_MODEL_OP_GEN_LEVEL_GET BT_MESH_MODEL_OP_2(0x82, 0x05) +#define BT_MESH_MODEL_OP_GEN_LEVEL_SET BT_MESH_MODEL_OP_2(0x82, 0x06) +#define BT_MESH_MODEL_OP_GEN_LEVEL_SET_UNACK BT_MESH_MODEL_OP_2(0x82, 0x07) +#define BT_MESH_MODEL_OP_GEN_LEVEL_STATUS BT_MESH_MODEL_OP_2(0x82, 0x08) +#define BT_MESH_MODEL_OP_GEN_DELTA_SET BT_MESH_MODEL_OP_2(0x82, 0x09) +#define BT_MESH_MODEL_OP_GEN_DELTA_SET_UNACK BT_MESH_MODEL_OP_2(0x82, 0x0A) +#define BT_MESH_MODEL_OP_GEN_MOVE_SET BT_MESH_MODEL_OP_2(0x82, 0x0B) +#define BT_MESH_MODEL_OP_GEN_MOVE_SET_UNACK BT_MESH_MODEL_OP_2(0x82, 0x0C) + +#define BT_MESH_MODEL_GEN_DEF_TRANS_TIME_STATUS BT_MESH_MODEL_OP_2(0x82, 0x10) + +#define BT_MESH_MODEL_GEN_ONPOWERUP_STATUS BT_MESH_MODEL_OP_2(0x82, 0x12) + +#define BT_MESH_MODEL_LIGHT_LIGHTNESS_STATUS BT_MESH_MODEL_OP_2(0x82, 0x4E) +#define BT_MESH_MODEL_LIGHT_LIGHTNESS_LINEAR_STATUS \ + BT_MESH_MODEL_OP_2(0x82, 0x52) +#define BT_MESH_MODEL_LIGHT_LIGHTNESS_LAST_STATUS \ + BT_MESH_MODEL_OP_2(0x82, 0x54) +#define BT_MESH_MODEL_LIGHT_LIGHTNESS_DEFAULT_STATUS \ + BT_MESH_MODEL_OP_2(0x82, 0x56) +#define BT_MESH_MODEL_LIGHT_LIGHTNESS_RANGE_STATUS \ + BT_MESH_MODEL_OP_2(0x82, 0x58) + +#define BT_MESH_MODEL_LIGHT_CTL_STATUS BT_MESH_MODEL_OP_2(0x82, 0x60) +#define BT_MESH_MODEL_LIGHT_CTL_TEMP_RANGE_STATUS \ + BT_MESH_MODEL_OP_2(0x82, 0x63) +#define BT_MESH_MODEL_LIGHT_CTL_TEMP_STATUS BT_MESH_MODEL_OP_2(0x82, 0x66) +#define BT_MESH_MODEL_LIGHT_CTL_DEFAULT_STATUS BT_MESH_MODEL_OP_2(0x82, 0x68) + +void blemesh_on_reset(int reason); +void blemesh_on_sync(void); +void init_pub(void); + +#endif + diff --git a/lib/bt/host/nimble/nimble/apps/blemesh_models_example_2/src/common.h b/lib/bt/host/nimble/nimble/apps/blemesh_models_example_2/src/common.h new file mode 100644 index 00000000..57cd401a --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/blemesh_models_example_2/src/common.h @@ -0,0 +1,33 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* Bluetooth: Mesh Generic OnOff, Generic Level, Lighting & Vendor Models + * + * Copyright (c) 2018 Vikrant More + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef _COMMON_H +#define _COMMON_H + +void update_light_state(void); +void bt_initialized(void); + +#endif diff --git a/lib/bt/host/nimble/nimble/apps/blemesh_models_example_2/src/device_composition.c b/lib/bt/host/nimble/nimble/apps/blemesh_models_example_2/src/device_composition.c new file mode 100644 index 00000000..40ede501 --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/blemesh_models_example_2/src/device_composition.c @@ -0,0 +1,2863 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* Bluetooth: Mesh Generic OnOff, Generic Level, Lighting & Vendor Models + * + * Copyright (c) 2018 Vikrant More + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "console/console.h" +#include "hal/hal_gpio.h" +#include "mesh/mesh.h" + +#include "app_gpio.h" +#include "storage.h" + +#include "ble_mesh.h" +#include "device_composition.h" +#include "state_binding.h" +#include "transition.h" + +static struct bt_mesh_health_srv health_srv = { +}; + +static struct bt_mesh_model_pub health_pub; + +static struct bt_mesh_model_pub gen_onoff_srv_pub_root; +static struct bt_mesh_model_pub gen_onoff_cli_pub_root; +static struct bt_mesh_model_pub gen_level_srv_pub_root; +static struct bt_mesh_model_pub gen_level_cli_pub_root; +static struct bt_mesh_model_pub gen_def_trans_time_srv_pub; +static struct bt_mesh_model_pub gen_def_trans_time_cli_pub; +static struct bt_mesh_model_pub gen_power_onoff_srv_pub; +static struct bt_mesh_model_pub gen_power_onoff_cli_pub; +static struct bt_mesh_model_pub light_lightness_srv_pub; +static struct bt_mesh_model_pub light_lightness_cli_pub; +static struct bt_mesh_model_pub light_ctl_srv_pub; +static struct bt_mesh_model_pub light_ctl_cli_pub; +static struct bt_mesh_model_pub vnd_pub; +static struct bt_mesh_model_pub gen_level_srv_pub_s0; +static struct bt_mesh_model_pub gen_level_cli_pub_s0; + + +static struct os_mbuf *bt_mesh_pub_msg_health_pub; +static struct os_mbuf *bt_mesh_pub_msg_gen_onoff_srv_pub_root; +static struct os_mbuf *bt_mesh_pub_msg_gen_onoff_cli_pub_root; +static struct os_mbuf *bt_mesh_pub_msg_gen_level_srv_pub_root; +static struct os_mbuf *bt_mesh_pub_msg_gen_level_cli_pub_root; +static struct os_mbuf *bt_mesh_pub_msg_gen_def_trans_time_srv_pub; +static struct os_mbuf *bt_mesh_pub_msg_gen_def_trans_time_cli_pub; +static struct os_mbuf *bt_mesh_pub_msg_gen_power_onoff_srv_pub; +static struct os_mbuf *bt_mesh_pub_msg_gen_power_onoff_cli_pub; +static struct os_mbuf *bt_mesh_pub_msg_light_lightness_srv_pub; +static struct os_mbuf *bt_mesh_pub_msg_light_lightness_cli_pub; +static struct os_mbuf *bt_mesh_pub_msg_light_ctl_srv_pub; +static struct os_mbuf *bt_mesh_pub_msg_light_ctl_cli_pub; +static struct os_mbuf *bt_mesh_pub_msg_vnd_pub; +static struct os_mbuf *bt_mesh_pub_msg_gen_level_srv_pub_s0; +static struct os_mbuf *bt_mesh_pub_msg_gen_level_cli_pub_s0; + + +void init_pub(void) +{ + bt_mesh_pub_msg_health_pub = NET_BUF_SIMPLE(1 + 3 + 0); + bt_mesh_pub_msg_gen_onoff_srv_pub_root = NET_BUF_SIMPLE(2 + 3); + bt_mesh_pub_msg_gen_onoff_cli_pub_root = NET_BUF_SIMPLE(2 + 4); + bt_mesh_pub_msg_gen_level_srv_pub_root = NET_BUF_SIMPLE(2 + 5); + bt_mesh_pub_msg_gen_level_cli_pub_root = NET_BUF_SIMPLE(2 + 7); + bt_mesh_pub_msg_gen_power_onoff_srv_pub = NET_BUF_SIMPLE(2 + 1); + bt_mesh_pub_msg_gen_power_onoff_cli_pub = NET_BUF_SIMPLE(2 + 1); + bt_mesh_pub_msg_gen_def_trans_time_srv_pub = NET_BUF_SIMPLE(2 + 1); + bt_mesh_pub_msg_gen_def_trans_time_cli_pub = NET_BUF_SIMPLE(2 + 1); + bt_mesh_pub_msg_light_lightness_srv_pub = NET_BUF_SIMPLE(2 + 5); + bt_mesh_pub_msg_light_lightness_cli_pub = NET_BUF_SIMPLE(2 + 5); + bt_mesh_pub_msg_light_ctl_srv_pub = NET_BUF_SIMPLE(2 + 9); + bt_mesh_pub_msg_light_ctl_cli_pub = NET_BUF_SIMPLE(2 + 9); + bt_mesh_pub_msg_vnd_pub = NET_BUF_SIMPLE(3 + 6); + bt_mesh_pub_msg_gen_level_srv_pub_s0 = NET_BUF_SIMPLE(2 + 5); + bt_mesh_pub_msg_gen_level_cli_pub_s0 = NET_BUF_SIMPLE(2 + 7); + + + health_pub.msg = bt_mesh_pub_msg_health_pub; + gen_onoff_srv_pub_root.msg = bt_mesh_pub_msg_gen_onoff_srv_pub_root; + gen_onoff_cli_pub_root.msg = bt_mesh_pub_msg_gen_onoff_cli_pub_root; + gen_level_srv_pub_root.msg = bt_mesh_pub_msg_gen_level_srv_pub_root; + gen_level_cli_pub_root.msg = bt_mesh_pub_msg_gen_level_cli_pub_root; + gen_power_onoff_srv_pub.msg = bt_mesh_pub_msg_gen_power_onoff_srv_pub; + gen_power_onoff_cli_pub.msg = bt_mesh_pub_msg_gen_power_onoff_cli_pub; + gen_def_trans_time_srv_pub.msg = bt_mesh_pub_msg_gen_def_trans_time_srv_pub; + gen_def_trans_time_cli_pub.msg = bt_mesh_pub_msg_gen_def_trans_time_cli_pub; + light_lightness_srv_pub.msg = bt_mesh_pub_msg_light_lightness_srv_pub; + light_lightness_cli_pub.msg = bt_mesh_pub_msg_light_lightness_cli_pub; + light_ctl_srv_pub.msg = bt_mesh_pub_msg_light_ctl_srv_pub; + light_ctl_cli_pub.msg = bt_mesh_pub_msg_light_ctl_cli_pub; + vnd_pub.msg = bt_mesh_pub_msg_vnd_pub; + gen_level_srv_pub_s0.msg = bt_mesh_pub_msg_gen_level_srv_pub_s0; + gen_level_cli_pub_s0.msg = bt_mesh_pub_msg_gen_level_cli_pub_s0; +} + +/* Definitions of models user data (Start) */ +struct generic_onoff_state gen_onoff_srv_root_user_data = { + .transition = &lightness_transition, +}; + +struct generic_level_state gen_level_srv_root_user_data = { + .transition = &lightness_transition, +}; + +struct gen_def_trans_time_state gen_def_trans_time_srv_user_data; + +struct generic_onpowerup_state gen_power_onoff_srv_user_data; + +struct light_lightness_state light_lightness_srv_user_data = { + .transition = &lightness_transition, +}; + +struct light_ctl_state light_ctl_srv_user_data = { + .transition = &lightness_transition, +}; + +struct vendor_state vnd_user_data; + +struct generic_level_state gen_level_srv_s0_user_data = { + .transition = &temp_transition, +}; +/* Definitions of models user data (End) */ + +static struct bt_mesh_elem elements[]; + +/* message handlers (Start) */ + +/* Generic OnOff Server message handlers */ +static int gen_onoff_get(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 3 + 4); + struct generic_onoff_state *state = model->user_data; + + bt_mesh_model_msg_init(msg, BT_MESH_MODEL_OP_GEN_ONOFF_STATUS); + net_buf_simple_add_u8(msg, state->onoff); + + if (state->transition->counter) { + calculate_rt(state->transition); + net_buf_simple_add_u8(msg, state->target_onoff); + net_buf_simple_add_u8(msg, state->transition->rt); + } + + if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { + printk("Unable to send GEN_ONOFF_SRV Status response\n"); + } + + os_mbuf_free_chain(msg); + return 0; +} + +int gen_onoff_publish(struct bt_mesh_model *model) +{ + int err; + struct os_mbuf *msg = model->pub->msg; + struct generic_onoff_state *state = model->user_data; + + if (model->pub->addr == BT_MESH_ADDR_UNASSIGNED) { + return 0; + } + + bt_mesh_model_msg_init(msg, BT_MESH_MODEL_OP_GEN_ONOFF_STATUS); + net_buf_simple_add_u8(msg, state->onoff); + + if (state->transition->counter) { + calculate_rt(state->transition); + net_buf_simple_add_u8(msg, state->target_onoff); + net_buf_simple_add_u8(msg, state->transition->rt); + } + + err = bt_mesh_model_publish(model); + if (err) { + printk("bt_mesh_model_publish err %d\n", err); + } + return err; +} + +static int gen_onoff_set_unack(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + uint8_t tid, onoff, tt, delay; + int64_t now; + int err; + struct generic_onoff_state *state = model->user_data; + + onoff = net_buf_simple_pull_u8(buf); + tid = net_buf_simple_pull_u8(buf); + + if (onoff > STATE_ON) { + return 0; + } + + now = k_uptime_get(); + if (state->last_tid == tid && + state->last_src_addr == ctx->addr && + state->last_dst_addr == ctx->recv_dst && + (now - state->last_msg_timestamp <= K_SECONDS(6))) { + return 0; + } + + switch (buf->om_len) { + case 0x00: /* No optional fields are available */ + tt = default_tt; + delay = 0; + break; + case 0x02: /* Optional fields are available */ + tt = net_buf_simple_pull_u8(buf); + if ((tt & 0x3F) == 0x3F) { + return 0; + } + + delay = net_buf_simple_pull_u8(buf); + break; + default: + return 0; + } + + *ptr_counter = 0; + os_callout_stop(ptr_timer); + + state->last_tid = tid; + state->last_src_addr = ctx->addr; + state->last_dst_addr = ctx->recv_dst; + state->last_msg_timestamp = now; + state->target_onoff = onoff; + + if (state->target_onoff != state->onoff) { + onoff_tt_values(state, tt, delay); + } else { + return gen_onoff_publish(model); + } + + /* For Instantaneous Transition */ + if (state->transition->counter == 0) { + state->onoff = state->target_onoff; + } + + state->transition->just_started = true; + err = gen_onoff_publish(model); + onoff_handler(state); + if (err) { + return err; + } + return 0; +} + +static int gen_onoff_set(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + uint8_t tid, onoff, tt, delay; + int64_t now; + int rc; + struct generic_onoff_state *state = model->user_data; + + onoff = net_buf_simple_pull_u8(buf); + tid = net_buf_simple_pull_u8(buf); + + if (onoff > STATE_ON) { + return 0; + } + + now = k_uptime_get(); + if (state->last_tid == tid && + state->last_src_addr == ctx->addr && + state->last_dst_addr == ctx->recv_dst && + (now - state->last_msg_timestamp <= K_SECONDS(6))) { + rc = gen_onoff_get(model, ctx, buf); + return rc; + } + + switch (buf->om_len) { + case 0x00: /* No optional fields are available */ + tt = default_tt; + delay = 0; + break; + case 0x02: /* Optional fields are available */ + tt = net_buf_simple_pull_u8(buf); + if ((tt & 0x3F) == 0x3F) { + return 0; + } + + delay = net_buf_simple_pull_u8(buf); + break; + default: + return 0; + } + + *ptr_counter = 0; + os_callout_stop(ptr_timer); + + state->last_tid = tid; + state->last_src_addr = ctx->addr; + state->last_dst_addr = ctx->recv_dst; + state->last_msg_timestamp = now; + state->target_onoff = onoff; + + if (state->target_onoff != state->onoff) { + onoff_tt_values(state, tt, delay); + } else { + gen_onoff_get(model, ctx, buf); + rc = gen_onoff_publish(model); + return rc; + } + + /* For Instantaneous Transition */ + if (state->transition->counter == 0) { + state->onoff = state->target_onoff; + } + + state->transition->just_started = true; + gen_onoff_get(model, ctx, buf); + rc = gen_onoff_publish(model); + onoff_handler(state); + return rc; +} + +/* Generic OnOff Client message handlers */ +static int gen_onoff_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + printk("Acknownledgement from GEN_ONOFF_SRV\n"); + printk("Present OnOff = %02x\n", net_buf_simple_pull_u8(buf)); + + if (buf->om_len == 2) { + printk("Target OnOff = %02x\n", net_buf_simple_pull_u8(buf)); + printk("Remaining Time = %02x\n", net_buf_simple_pull_u8(buf)); + } + return 0; +} + +/* Generic Level Server message handlers */ +static int gen_level_get(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 5 + 4); + struct generic_level_state *state = model->user_data; + + bt_mesh_model_msg_init(msg, BT_MESH_MODEL_OP_GEN_LEVEL_STATUS); + net_buf_simple_add_le16(msg, state->level); + + if (state->transition->counter) { + calculate_rt(state->transition); + net_buf_simple_add_le16(msg, state->target_level); + net_buf_simple_add_u8(msg, state->transition->rt); + } + + if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { + printk("Unable to send GEN_LEVEL_SRV Status response\n"); + } + + os_mbuf_free_chain(msg); + return 0; +} + +int gen_level_publish(struct bt_mesh_model *model) +{ + int err; + struct os_mbuf *msg = model->pub->msg; + struct generic_level_state *state = model->user_data; + + if (model->pub->addr == BT_MESH_ADDR_UNASSIGNED) { + return 0; + } + + bt_mesh_model_msg_init(msg, BT_MESH_MODEL_OP_GEN_LEVEL_STATUS); + net_buf_simple_add_le16(msg, state->level); + + if (state->transition->counter) { + calculate_rt(state->transition); + net_buf_simple_add_le16(msg, state->target_level); + net_buf_simple_add_u8(msg, state->transition->rt); + } + + err = bt_mesh_model_publish(model); + if (err) { + printk("bt_mesh_model_publish err %d\n", err); + } + return err; +} + +static int gen_level_set_unack(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + uint8_t tid, tt, delay; + int16_t level; + int64_t now; + struct generic_level_state *state = model->user_data; + + level = (int16_t) net_buf_simple_pull_le16(buf); + tid = net_buf_simple_pull_u8(buf); + + now = k_uptime_get(); + if (state->last_tid == tid && + state->last_src_addr == ctx->addr && + state->last_dst_addr == ctx->recv_dst && + (now - state->last_msg_timestamp <= K_SECONDS(6))) { + return 0; + } + + switch (buf->om_len) { + case 0x00: /* No optional fields are available */ + tt = default_tt; + delay = 0; + break; + case 0x02: /* Optional fields are available */ + tt = net_buf_simple_pull_u8(buf); + if ((tt & 0x3F) == 0x3F) { + return 0; + } + + delay = net_buf_simple_pull_u8(buf); + break; + default: + return 0; + } + + *ptr_counter = 0; + os_callout_stop(ptr_timer); + + state->last_tid = tid; + state->last_src_addr = ctx->addr; + state->last_dst_addr = ctx->recv_dst; + state->last_msg_timestamp = now; + state->target_level = level; + + if (state->target_level != state->level) { + level_tt_values(state, tt, delay); + } else { + gen_level_publish(model); + return 0; + } + + /* For Instantaneous Transition */ + if (state->transition->counter == 0) { + state->level = state->target_level; + } + + state->transition->just_started = true; + gen_level_publish(model); + + if (bt_mesh_model_elem(model)->addr == elements[0].addr) { + /* Root element */ + transition_type = LEVEL_TT; + level_lightness_handler(state); + } else if (bt_mesh_model_elem(model)->addr == elements[1].addr) { + /* Secondary element */ + transition_type = LEVEL_TEMP_TT; + level_temp_handler(state); + } + return 0; +} + +static int gen_level_set(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + uint8_t tid, tt, delay; + int16_t level; + int64_t now; + struct generic_level_state *state = model->user_data; + + level = (int16_t) net_buf_simple_pull_le16(buf); + tid = net_buf_simple_pull_u8(buf); + + now = k_uptime_get(); + if (state->last_tid == tid && + state->last_src_addr == ctx->addr && + state->last_dst_addr == ctx->recv_dst && + (now - state->last_msg_timestamp <= K_SECONDS(6))) { + gen_level_get(model, ctx, buf); + return 0; + } + + switch (buf->om_len) { + case 0x00: /* No optional fields are available */ + tt = default_tt; + delay = 0; + break; + case 0x02: /* Optional fields are available */ + tt = net_buf_simple_pull_u8(buf); + if ((tt & 0x3F) == 0x3F) { + return 0; + } + + delay = net_buf_simple_pull_u8(buf); + break; + default: + return 0; + } + + *ptr_counter = 0; + os_callout_stop(ptr_timer); + + state->last_tid = tid; + state->last_src_addr = ctx->addr; + state->last_dst_addr = ctx->recv_dst; + state->last_msg_timestamp = now; + state->target_level = level; + + if (state->target_level != state->level) { + level_tt_values(state, tt, delay); + } else { + gen_level_get(model, ctx, buf); + gen_level_publish(model); + return 0; + } + + /* For Instantaneous Transition */ + if (state->transition->counter == 0) { + state->level = state->target_level; + } + + state->transition->just_started = true; + gen_level_get(model, ctx, buf); + gen_level_publish(model); + + if (bt_mesh_model_elem(model)->addr == elements[0].addr) { + /* Root element */ + transition_type = LEVEL_TT; + level_lightness_handler(state); + } else if (bt_mesh_model_elem(model)->addr == elements[1].addr) { + /* Secondary element */ + transition_type = LEVEL_TEMP_TT; + level_temp_handler(state); + } + return 0; +} + +static int gen_delta_set_unack(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + uint8_t tid, tt, delay; + int32_t tmp32, delta; + int64_t now; + struct generic_level_state *state = model->user_data; + + delta = (int32_t) net_buf_simple_pull_le32(buf); + tid = net_buf_simple_pull_u8(buf); + + now = k_uptime_get(); + if (state->last_tid == tid && + state->last_src_addr == ctx->addr && + state->last_dst_addr == ctx->recv_dst && + (now - state->last_msg_timestamp <= K_SECONDS(6))) { + + if (state->last_delta == delta) { + return 0; + } + tmp32 = state->last_level + delta; + + } else { + state->last_level = state->level; + tmp32 = state->level + delta; + } + + switch (buf->om_len) { + case 0x00: /* No optional fields are available */ + tt = default_tt; + delay = 0; + break; + case 0x02: /* Optional fields are available */ + tt = net_buf_simple_pull_u8(buf); + if ((tt & 0x3F) == 0x3F) { + return 0; + } + + delay = net_buf_simple_pull_u8(buf); + break; + default: + return 0; + } + + *ptr_counter = 0; + os_callout_stop(ptr_timer); + + state->last_tid = tid; + state->last_src_addr = ctx->addr; + state->last_dst_addr = ctx->recv_dst; + state->last_msg_timestamp = now; + state->last_delta = delta; + + if (tmp32 < INT16_MIN) { + tmp32 = INT16_MIN; + } else if (tmp32 > INT16_MAX) { + tmp32 = INT16_MAX; + } + + state->target_level = tmp32; + + if (state->target_level != state->level) { + level_tt_values(state, tt, delay); + } else { + return gen_level_publish(model); + } + + /* For Instantaneous Transition */ + if (state->transition->counter == 0) { + state->level = state->target_level; + } + + state->transition->just_started = true; + gen_level_publish(model); + + if (bt_mesh_model_elem(model)->addr == elements[0].addr) { + /* Root element */ + transition_type = LEVEL_TT_DELTA; + level_lightness_handler(state); + } else if (bt_mesh_model_elem(model)->addr == + elements[1].addr) { + /* Secondary element */ + transition_type = LEVEL_TEMP_TT_DELTA; + level_temp_handler(state); + } + return 0; +} + +static int gen_delta_set(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + uint8_t tid, tt, delay; + int32_t tmp32, delta; + int64_t now; + struct generic_level_state *state = model->user_data; + + delta = (int32_t) net_buf_simple_pull_le32(buf); + tid = net_buf_simple_pull_u8(buf); + + now = k_uptime_get(); + if (state->last_tid == tid && + state->last_src_addr == ctx->addr && + state->last_dst_addr == ctx->recv_dst && + (now - state->last_msg_timestamp <= K_SECONDS(6))) { + + if (state->last_delta == delta) { + gen_level_get(model, ctx, buf); + return 0; + } + tmp32 = state->last_level + delta; + + } else { + state->last_level = state->level; + tmp32 = state->level + delta; + } + + switch (buf->om_len) { + case 0x00: /* No optional fields are available */ + tt = default_tt; + delay = 0; + break; + case 0x02: /* Optional fields are available */ + tt = net_buf_simple_pull_u8(buf); + if ((tt & 0x3F) == 0x3F) { + return 0; + } + + delay = net_buf_simple_pull_u8(buf); + break; + default: + return 0; + } + + *ptr_counter = 0; + os_callout_stop(ptr_timer); + + state->last_tid = tid; + state->last_src_addr = ctx->addr; + state->last_dst_addr = ctx->recv_dst; + state->last_msg_timestamp = now; + state->last_delta = delta; + + if (tmp32 < INT16_MIN) { + tmp32 = INT16_MIN; + } else if (tmp32 > INT16_MAX) { + tmp32 = INT16_MAX; + } + + state->target_level = tmp32; + + if (state->target_level != state->level) { + level_tt_values(state, tt, delay); + } else { + gen_level_get(model, ctx, buf); + gen_level_publish(model); + return 0; + } + + /* For Instantaneous Transition */ + if (state->transition->counter == 0) { + state->level = state->target_level; + } + + state->transition->just_started = true; + gen_level_get(model, ctx, buf); + gen_level_publish(model); + + if (bt_mesh_model_elem(model)->addr == elements[0].addr) { + /* Root element */ + transition_type = LEVEL_TT_DELTA; + level_lightness_handler(state); + } else if (bt_mesh_model_elem(model)->addr == elements[1].addr) { + /* Secondary element */ + transition_type = LEVEL_TEMP_TT_DELTA; + level_temp_handler(state); + } + return 0; +} + +static int gen_level_move_get(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 5 + 4); + struct generic_level_state *state = model->user_data; + int rc; + + bt_mesh_model_msg_init(msg, BT_MESH_MODEL_OP_GEN_LEVEL_STATUS); + net_buf_simple_add_le16(msg, state->level); + + if (state->transition->counter) { + if (state->last_delta < 0) { + net_buf_simple_add_le16(msg, INT16_MIN); + } else { /* 0 should not be possible */ + net_buf_simple_add_le16(msg, INT16_MAX); + } + + net_buf_simple_add_u8(msg, UNKNOWN_VALUE); + } + + rc = bt_mesh_model_send(model, ctx, msg, NULL, NULL); + if (rc) { + printk("Unable to send GEN_LEVEL_SRV Status response\n"); + } + + os_mbuf_free_chain(msg); + return rc; +} + +static int gen_level_move_publish(struct bt_mesh_model *model) +{ + int err; + struct os_mbuf *msg = model->pub->msg; + struct generic_level_state *state = model->user_data; + + if (model->pub->addr == BT_MESH_ADDR_UNASSIGNED) { + return 0; + } + + bt_mesh_model_msg_init(msg, BT_MESH_MODEL_OP_GEN_LEVEL_STATUS); + net_buf_simple_add_le16(msg, state->level); + + if (state->transition->counter) { + if (state->last_delta < 0) { + net_buf_simple_add_le16(msg, INT16_MIN); + } else { /* 0 should not be possible */ + net_buf_simple_add_le16(msg, INT16_MAX); + } + + net_buf_simple_add_u8(msg, UNKNOWN_VALUE); + } + + err = bt_mesh_model_publish(model); + if (err) { + printk("bt_mesh_model_publish err %d\n", err); + } + return err; +} + +static int gen_move_set_unack(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + uint8_t tid, tt, delay; + int16_t delta; + int32_t tmp32; + int64_t now; + struct generic_level_state *state = model->user_data; + int rc; + + delta = (int16_t) net_buf_simple_pull_le16(buf); + tid = net_buf_simple_pull_u8(buf); + + now = k_uptime_get(); + if (state->last_tid == tid && + state->last_src_addr == ctx->addr && + state->last_dst_addr == ctx->recv_dst && + (now - state->last_msg_timestamp <= K_SECONDS(6))) { + return 0; + } + + switch (buf->om_len) { + case 0x00: /* No optional fields are available */ + tt = default_tt; + delay = 0; + break; + case 0x02: /* Optional fields are available */ + tt = net_buf_simple_pull_u8(buf); + if ((tt & 0x3F) == 0x3F) { + return 0; + } + + delay = net_buf_simple_pull_u8(buf); + break; + default: + return 0; + } + + *ptr_counter = 0; + os_callout_stop(ptr_timer); + + state->last_tid = tid; + state->last_src_addr = ctx->addr; + state->last_dst_addr = ctx->recv_dst; + state->last_msg_timestamp = now; + state->last_delta = delta; + + tmp32 = state->level + delta; + if (tmp32 < INT16_MIN) { + tmp32 = INT16_MIN; + } else if (tmp32 > INT16_MAX) { + tmp32 = INT16_MAX; + } + + state->target_level = tmp32; + + if (state->target_level != state->level) { + level_tt_values(state, tt, delay); + } else { + rc = gen_level_move_publish(model); + return rc; + } + + if (state->transition->counter == 0) { + return 0; + } + + state->transition->just_started = true; + rc = gen_level_move_publish(model); + + if (bt_mesh_model_elem(model)->addr == elements[0].addr) { + /* Root element */ + transition_type = LEVEL_TT_MOVE; + level_lightness_handler(state); + } else if (bt_mesh_model_elem(model)->addr == elements[1].addr) { + /* Secondary element */ + transition_type = LEVEL_TEMP_TT_MOVE; + level_temp_handler(state); + } + return rc; +} + +static int gen_move_set(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + uint8_t tid, tt, delay; + int16_t delta; + int32_t tmp32; + int64_t now; + struct generic_level_state *state = model->user_data; + int rc; + + delta = (int16_t) net_buf_simple_pull_le16(buf); + tid = net_buf_simple_pull_u8(buf); + + now = k_uptime_get(); + if (state->last_tid == tid && + state->last_src_addr == ctx->addr && + state->last_dst_addr == ctx->recv_dst && + (now - state->last_msg_timestamp <= K_SECONDS(6))) { + rc = gen_level_move_get(model, ctx, buf); + return rc; + } + + switch (buf->om_len) { + case 0x00: /* No optional fields are available */ + tt = default_tt; + delay = 0; + break; + case 0x02: /* Optional fields are available */ + tt = net_buf_simple_pull_u8(buf); + if ((tt & 0x3F) == 0x3F) { + return 0; + } + + delay = net_buf_simple_pull_u8(buf); + break; + default: + return 0; + } + + *ptr_counter = 0; + os_callout_stop(ptr_timer); + + state->last_tid = tid; + state->last_src_addr = ctx->addr; + state->last_dst_addr = ctx->recv_dst; + state->last_msg_timestamp = now; + state->last_delta = delta; + + tmp32 = state->level + delta; + if (tmp32 < INT16_MIN) { + tmp32 = INT16_MIN; + } else if (tmp32 > INT16_MAX) { + tmp32 = INT16_MAX; + } + + state->target_level = tmp32; + + if (state->target_level != state->level) { + level_tt_values(state, tt, delay); + } else { + gen_level_move_get(model, ctx, buf); + rc = gen_level_move_publish(model); + return rc; + } + + if (state->transition->counter == 0) { + return 0; + } + + state->transition->just_started = true; + gen_level_move_get(model, ctx, buf); + rc = gen_level_move_publish(model); + + if (bt_mesh_model_elem(model)->addr == elements[0].addr) { + /* Root element */ + transition_type = LEVEL_TT_MOVE; + level_lightness_handler(state); + } else if (bt_mesh_model_elem(model)->addr == elements[1].addr) { + /* Secondary element */ + transition_type = LEVEL_TEMP_TT_MOVE; + level_temp_handler(state); + } + return rc; +} + +/* Generic Level Client message handlers */ +static int gen_level_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + printk("Acknownledgement from GEN_LEVEL_SRV\n"); + printk("Present Level = %04x\n", net_buf_simple_pull_le16(buf)); + + if (buf->om_len == 3) { + printk("Target Level = %04x\n", net_buf_simple_pull_le16(buf)); + printk("Remaining Time = %02x\n", net_buf_simple_pull_u8(buf)); + } + return 0; +} + +/* Generic Default Transition Time Server message handlers */ +static int gen_def_trans_time_get(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 1 + 4); + struct gen_def_trans_time_state *state = model->user_data; + + bt_mesh_model_msg_init(msg, BT_MESH_MODEL_GEN_DEF_TRANS_TIME_STATUS); + net_buf_simple_add_u8(msg, state->tt); + + if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { + printk("Unable to send GEN_DEF_TT_SRV Status response\n"); + } + + os_mbuf_free_chain(msg); + return 0; +} + +static int gen_def_trans_time_publish(struct bt_mesh_model *model) +{ + int err; + struct os_mbuf *msg = model->pub->msg; + struct gen_def_trans_time_state *state = model->user_data; + + if (model->pub->addr == BT_MESH_ADDR_UNASSIGNED) { + return 0; + } + + bt_mesh_model_msg_init(msg, BT_MESH_MODEL_GEN_DEF_TRANS_TIME_STATUS); + net_buf_simple_add_u8(msg, state->tt); + + err = bt_mesh_model_publish(model); + if (err) { + printk("bt_mesh_model_publish err %d\n", err); + } + return err; +} + +static bool gen_def_trans_time_setunack(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + uint8_t tt; + struct gen_def_trans_time_state *state = model->user_data; + + tt = net_buf_simple_pull_u8(buf); + + /* Here, Model specification is silent about tid implementation */ + + if ((tt & 0x3F) == 0x3F) { + return false; + } + + if (state->tt != tt) { + state->tt = tt; + default_tt = tt; + + save_on_flash(GEN_DEF_TRANS_TIME_STATE); + } + + return true; +} + +static int gen_def_trans_time_set_unack(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + if (gen_def_trans_time_setunack(model, ctx, buf) == true) { + return gen_def_trans_time_publish(model); + } + return 0; +} + +static int gen_def_trans_time_set(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + if (gen_def_trans_time_setunack(model, ctx, buf) == true) { + gen_def_trans_time_get(model, ctx, buf); + return gen_def_trans_time_publish(model); + } + return 0; +} + +/* Generic Default Transition Time Client message handlers */ +static int gen_def_trans_time_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + printk("Acknownledgement from GEN_DEF_TT_SRV\n"); + printk("Transition Time = %02x\n", net_buf_simple_pull_u8(buf)); + return 0; +} + +/* Generic Power OnOff Server message handlers */ +static int gen_onpowerup_get(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 1 + 4); + struct generic_onpowerup_state *state = model->user_data; + int rc; + + bt_mesh_model_msg_init(msg, BT_MESH_MODEL_GEN_ONPOWERUP_STATUS); + net_buf_simple_add_u8(msg, state->onpowerup); + + rc = bt_mesh_model_send(model, ctx, msg, NULL, NULL); + if (rc) { + printk("Unable to send GEN_POWER_ONOFF_SRV Status response\n"); + } + + os_mbuf_free_chain(msg); + return rc; +} + +/* Generic Power OnOff Client message handlers */ +static int gen_onpowerup_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + printk("Acknownledgement from GEN_POWER_ONOFF_SRV\n"); + printk("OnPowerUp = %02x\n", net_buf_simple_pull_u8(buf)); + return 0; +} + +/* Generic Power OnOff Setup Server message handlers */ + +static int gen_onpowerup_publish(struct bt_mesh_model *model) +{ + int err; + struct os_mbuf *msg = model->pub->msg; + struct generic_onpowerup_state *state = model->user_data; + + if (model->pub->addr == BT_MESH_ADDR_UNASSIGNED) { + return 0; + } + + bt_mesh_model_msg_init(msg, BT_MESH_MODEL_GEN_ONPOWERUP_STATUS); + net_buf_simple_add_u8(msg, state->onpowerup); + + err = bt_mesh_model_publish(model); + if (err) { + printk("bt_mesh_model_publish err %d\n", err); + } + return err; +} + +static bool gen_onpowerup_setunack(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + uint8_t onpowerup; + struct generic_onpowerup_state *state = model->user_data; + + onpowerup = net_buf_simple_pull_u8(buf); + + /* Here, Model specification is silent about tid implementation */ + + if (onpowerup > STATE_RESTORE) { + return false; + } + + if (state->onpowerup != onpowerup) { + state->onpowerup = onpowerup; + + save_on_flash(GEN_ONPOWERUP_STATE); + } + + return true; +} + +static int gen_onpowerup_set_unack(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + if (gen_onpowerup_setunack(model, ctx, buf) == true) { + return gen_onpowerup_publish(model); + } + return 0; +} + +static int gen_onpowerup_set(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + if (gen_onpowerup_setunack(model, ctx, buf) == true) { + gen_onpowerup_get(model, ctx, buf); + return gen_onpowerup_publish(model); + } + return 0; +} + +/* Vendor Model message handlers*/ +static int vnd_get(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct os_mbuf *msg = NET_BUF_SIMPLE(3 + 6 + 4); + struct vendor_state *state = model->user_data; + int err; + + /* This is dummy response for demo purpose */ + state->response = 0xA578FEB3; + + bt_mesh_model_msg_init(msg, BT_MESH_MODEL_OP_3(0x04, CID_RUNTIME)); + net_buf_simple_add_le16(msg, state->current); + net_buf_simple_add_le32(msg, state->response); + + err = bt_mesh_model_send(model, ctx, msg, NULL, NULL); + if (err) { + printk("Unable to send VENDOR Status response\n"); + } + + os_mbuf_free_chain(msg); + return err; +} + +static int vnd_set_unack(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + uint8_t tid; + int current; + int64_t now; + struct vendor_state *state = model->user_data; + + current = net_buf_simple_pull_le16(buf); + tid = net_buf_simple_pull_u8(buf); + + now = k_uptime_get(); + if (state->last_tid == tid && + state->last_src_addr == ctx->addr && + state->last_dst_addr == ctx->recv_dst && + (now - state->last_msg_timestamp <= K_SECONDS(6))) { + return 0; + } + + state->last_tid = tid; + state->last_src_addr = ctx->addr; + state->last_dst_addr = ctx->recv_dst; + state->last_msg_timestamp = now; + state->current = current; + + printk("Vendor model message = %04x\n", state->current); + + if (state->current == STATE_ON) { + /* LED2 On */ + hal_gpio_write(led_device[1], 0); + } else { + /* LED2 Off */ + hal_gpio_write(led_device[1], 1); + } + return 0; +} + +static int vnd_set(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + int rc; + rc = vnd_set_unack(model, ctx, buf); + if (rc) { + return rc; + } + rc = vnd_get(model, ctx, buf); + if (rc) { + return rc; + } + return 0; +} + +static int vnd_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + printk("Acknownledgement from Vendor\n"); + printk("cmd = %04x\n", net_buf_simple_pull_le16(buf)); + printk("response = %08lx\n", net_buf_simple_pull_le32(buf)); + return 0; +} + +/* Light Lightness Server message handlers */ +static int light_lightness_get(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 5 + 4); + struct light_lightness_state *state = model->user_data; + + bt_mesh_model_msg_init(msg, BT_MESH_MODEL_LIGHT_LIGHTNESS_STATUS); + net_buf_simple_add_le16(msg, state->actual); + + if (state->transition->counter) { + calculate_rt(state->transition); + net_buf_simple_add_le16(msg, state->target_actual); + net_buf_simple_add_u8(msg, state->transition->rt); + } + + if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { + printk("Unable to send LightLightnessAct Status response\n"); + } + + os_mbuf_free_chain(msg); + return 0; +} + +int light_lightness_publish(struct bt_mesh_model *model) +{ + int err; + struct os_mbuf *msg = model->pub->msg; + struct light_lightness_state *state = model->user_data; + + if (model->pub->addr == BT_MESH_ADDR_UNASSIGNED) { + return 0; + } + + bt_mesh_model_msg_init(msg, BT_MESH_MODEL_LIGHT_LIGHTNESS_STATUS); + net_buf_simple_add_le16(msg, state->actual); + + if (state->transition->counter) { + calculate_rt(state->transition); + net_buf_simple_add_le16(msg, state->target_actual); + net_buf_simple_add_u8(msg, state->transition->rt); + } + + err = bt_mesh_model_publish(model); + if (err) { + printk("bt_mesh_model_publish err %d\n", err); + } + return err; +} + +static int light_lightness_set_unack(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + uint8_t tid, tt, delay; + uint16_t actual; + int64_t now; + struct light_lightness_state *state = model->user_data; + + actual = net_buf_simple_pull_le16(buf); + tid = net_buf_simple_pull_u8(buf); + + now = k_uptime_get(); + if (state->last_tid == tid && + state->last_src_addr == ctx->addr && + state->last_dst_addr == ctx->recv_dst && + (now - state->last_msg_timestamp <= K_SECONDS(6))) { + return 0; + } + + switch (buf->om_len) { + case 0x00: /* No optional fields are available */ + tt = default_tt; + delay = 0; + break; + case 0x02: /* Optional fields are available */ + tt = net_buf_simple_pull_u8(buf); + if ((tt & 0x3F) == 0x3F) { + return 0; + } + + delay = net_buf_simple_pull_u8(buf); + break; + default: + return 0; + } + + *ptr_counter = 0; + os_callout_stop(ptr_timer); + + state->last_tid = tid; + state->last_src_addr = ctx->addr; + state->last_dst_addr = ctx->recv_dst; + state->last_msg_timestamp = now; + + if (actual > 0 && actual < state->light_range_min) { + actual = state->light_range_min; + } else if (actual > state->light_range_max) { + actual = state->light_range_max; + } + + state->target_actual = actual; + + if (state->target_actual != state->actual) { + light_lightness_actual_tt_values(state, tt, delay); + } else { + light_lightness_publish(model); + return 0; + } + + /* For Instantaneous Transition */ + if (state->transition->counter == 0) { + state->actual = state->target_actual; + } + + state->transition->just_started = true; + light_lightness_publish(model); + light_lightness_actual_handler(state); + return 0; +} + +static int light_lightness_set(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + uint8_t tid, tt, delay; + uint16_t actual; + int64_t now; + struct light_lightness_state *state = model->user_data; + int rc; + + actual = net_buf_simple_pull_le16(buf); + tid = net_buf_simple_pull_u8(buf); + + now = k_uptime_get(); + if (state->last_tid == tid && + state->last_src_addr == ctx->addr && + state->last_dst_addr == ctx->recv_dst && + (now - state->last_msg_timestamp <= K_SECONDS(6))) { + return light_lightness_get(model, ctx, buf); + } + + switch (buf->om_len) { + case 0x00: /* No optional fields are available */ + tt = default_tt; + delay = 0; + break; + case 0x02: /* Optional fields are available */ + tt = net_buf_simple_pull_u8(buf); + if ((tt & 0x3F) == 0x3F) { + return 0; + } + + delay = net_buf_simple_pull_u8(buf); + break; + default: + return 0; + } + + *ptr_counter = 0; + os_callout_stop(ptr_timer); + + state->last_tid = tid; + state->last_src_addr = ctx->addr; + state->last_dst_addr = ctx->recv_dst; + state->last_msg_timestamp = now; + + if (actual > 0 && actual < state->light_range_min) { + actual = state->light_range_min; + } else if (actual > state->light_range_max) { + actual = state->light_range_max; + } + + state->target_actual = actual; + + if (state->target_actual != state->actual) { + light_lightness_actual_tt_values(state, tt, delay); + } else { + rc = light_lightness_get(model, ctx, buf); + light_lightness_publish(model); + return rc; + } + + /* For Instantaneous Transition */ + if (state->transition->counter == 0) { + state->actual = state->target_actual; + } + + state->transition->just_started = true; + light_lightness_get(model, ctx, buf); + light_lightness_publish(model); + light_lightness_actual_handler(state); + return 0; +} + +static int light_lightness_linear_get(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 5 + 4); + struct light_lightness_state *state = model->user_data; + + bt_mesh_model_msg_init(msg, + BT_MESH_MODEL_LIGHT_LIGHTNESS_LINEAR_STATUS); + net_buf_simple_add_le16(msg, state->linear); + + if (state->transition->counter) { + calculate_rt(state->transition); + net_buf_simple_add_le16(msg, state->target_linear); + net_buf_simple_add_u8(msg, state->transition->rt); + } + + if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { + printk("Unable to send LightLightnessLin Status response\n"); + } + + os_mbuf_free_chain(msg); + return 0; +} + +int light_lightness_linear_publish(struct bt_mesh_model *model) +{ + int err; + struct os_mbuf *msg = model->pub->msg; + struct light_lightness_state *state = model->user_data; + + if (model->pub->addr == BT_MESH_ADDR_UNASSIGNED) { + return 0; + } + + bt_mesh_model_msg_init(msg, + BT_MESH_MODEL_LIGHT_LIGHTNESS_LINEAR_STATUS); + net_buf_simple_add_le16(msg, state->linear); + + if (state->transition->counter) { + calculate_rt(state->transition); + net_buf_simple_add_le16(msg, state->target_linear); + net_buf_simple_add_u8(msg, state->transition->rt); + } + + err = bt_mesh_model_publish(model); + if (err) { + printk("bt_mesh_model_publish err %d\n", err); + } + return err; +} + +static int light_lightness_linear_set_unack(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + uint8_t tid, tt, delay; + uint16_t linear; + int64_t now; + struct light_lightness_state *state = model->user_data; + + linear = net_buf_simple_pull_le16(buf); + tid = net_buf_simple_pull_u8(buf); + + now = k_uptime_get(); + if (state->last_tid == tid && + state->last_src_addr == ctx->addr && + state->last_dst_addr == ctx->recv_dst && + (now - state->last_msg_timestamp <= K_SECONDS(6))) { + return 0; + } + + switch (buf->om_len) { + case 0x00: /* No optional fields are available */ + tt = default_tt; + delay = 0; + break; + case 0x02: /* Optional fields are available */ + tt = net_buf_simple_pull_u8(buf); + if ((tt & 0x3F) == 0x3F) { + return 0; + } + + delay = net_buf_simple_pull_u8(buf); + break; + default: + return 0; + } + + *ptr_counter = 0; + os_callout_stop(ptr_timer); + + state->last_tid = tid; + state->last_src_addr = ctx->addr; + state->last_dst_addr = ctx->recv_dst; + state->last_msg_timestamp = now; + state->target_linear = linear; + + if (state->target_linear != state->linear) { + light_lightness_linear_tt_values(state, tt, delay); + } else { + light_lightness_linear_publish(model); + return 0; + } + + /* For Instantaneous Transition */ + if (state->transition->counter == 0) { + state->linear = state->target_linear; + } + + state->transition->just_started = true; + light_lightness_linear_publish(model); + light_lightness_linear_handler(state); + return 0; +} + +static int light_lightness_linear_set(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + uint8_t tid, tt, delay; + uint16_t linear; + int64_t now; + struct light_lightness_state *state = model->user_data; + int rc; + + linear = net_buf_simple_pull_le16(buf); + tid = net_buf_simple_pull_u8(buf); + + now = k_uptime_get(); + if (state->last_tid == tid && + state->last_src_addr == ctx->addr && + state->last_dst_addr == ctx->recv_dst && + (now - state->last_msg_timestamp <= K_SECONDS(6))) { + return light_lightness_linear_get(model, ctx, buf); + } + + switch (buf->om_len) { + case 0x00: /* No optional fields are available */ + tt = default_tt; + delay = 0; + break; + case 0x02: /* Optional fields are available */ + tt = net_buf_simple_pull_u8(buf); + if ((tt & 0x3F) == 0x3F) { + return 0; + } + + delay = net_buf_simple_pull_u8(buf); + break; + default: + return 0; + } + + *ptr_counter = 0; + os_callout_stop(ptr_timer); + + state->last_tid = tid; + state->last_src_addr = ctx->addr; + state->last_dst_addr = ctx->recv_dst; + state->last_msg_timestamp = now; + state->target_linear = linear; + + if (state->target_linear != state->linear) { + light_lightness_linear_tt_values(state, tt, delay); + } else { + rc = light_lightness_linear_get(model, ctx, buf); + light_lightness_linear_publish(model); + return rc; + } + + /* For Instantaneous Transition */ + if (state->transition->counter == 0) { + state->linear = state->target_linear; + } + + state->transition->just_started = true; + light_lightness_linear_get(model, ctx, buf); + light_lightness_linear_publish(model); + light_lightness_linear_handler(state); + return 0; +} + +static int light_lightness_last_get(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 2 + 4); + struct light_lightness_state *state = model->user_data; + + bt_mesh_model_msg_init(msg, BT_MESH_MODEL_LIGHT_LIGHTNESS_LAST_STATUS); + net_buf_simple_add_le16(msg, state->last); + + if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { + printk("Unable to send LightLightnessLast Status response\n"); + } + + os_mbuf_free_chain(msg); + return 0; +} + +static int light_lightness_default_get(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 2 + 4); + struct light_lightness_state *state = model->user_data; + + bt_mesh_model_msg_init(msg, + BT_MESH_MODEL_LIGHT_LIGHTNESS_DEFAULT_STATUS); + net_buf_simple_add_le16(msg, state->def); + + if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { + printk("Unable to send LightLightnessDef Status response\n"); + } + + os_mbuf_free_chain(msg); + return 0; +} + +static int light_lightness_range_get(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 5 + 4); + struct light_lightness_state *state = model->user_data; + + state->status_code = RANGE_SUCCESSFULLY_UPDATED; + + bt_mesh_model_msg_init(msg, BT_MESH_MODEL_LIGHT_LIGHTNESS_RANGE_STATUS); + net_buf_simple_add_u8(msg, state->status_code); + net_buf_simple_add_le16(msg, state->light_range_min); + net_buf_simple_add_le16(msg, state->light_range_max); + + if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { + printk("Unable to send LightLightnessRange Status response\n"); + } + + os_mbuf_free_chain(msg); + return 0; +} + +/* Light Lightness Setup Server message handlers */ + +static int light_lightness_default_publish(struct bt_mesh_model *model) +{ + int err; + struct os_mbuf *msg = model->pub->msg; + struct light_lightness_state *state = model->user_data; + + if (model->pub->addr == BT_MESH_ADDR_UNASSIGNED) { + return 0; + } + + bt_mesh_model_msg_init(msg, + BT_MESH_MODEL_LIGHT_LIGHTNESS_DEFAULT_STATUS); + net_buf_simple_add_le16(msg, state->def); + + err = bt_mesh_model_publish(model); + if (err) { + printk("bt_mesh_model_publish err %d\n", err); + } + return err; +} + +static int light_lightness_default_set_unack(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + uint16_t lightness; + struct light_lightness_state *state = model->user_data; + + lightness = net_buf_simple_pull_le16(buf); + + /* Here, Model specification is silent about tid implementation */ + + if (state->def != lightness) { + state->def = lightness; + light_ctl_srv_user_data.lightness_def = state->def; + + save_on_flash(LIGHTNESS_TEMP_DEF_STATE); + } + + return light_lightness_default_publish(model); +} + +static int light_lightness_default_set(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + int rc; + rc = light_lightness_default_set_unack(model, ctx, buf); + if (rc) { + return rc; + } + rc = light_lightness_default_get(model, ctx, buf); + if (rc) { + return rc; + } + rc = light_lightness_default_publish(model); + if (rc) { + return rc; + } + return 0; +} + +static int light_lightness_range_publish(struct bt_mesh_model *model) +{ + int err; + struct os_mbuf *msg = model->pub->msg; + struct light_lightness_state *state = model->user_data; + + if (model->pub->addr == BT_MESH_ADDR_UNASSIGNED) { + return 0; + } + + bt_mesh_model_msg_init(msg, BT_MESH_MODEL_LIGHT_LIGHTNESS_RANGE_STATUS); + net_buf_simple_add_u8(msg, state->status_code); + net_buf_simple_add_le16(msg, state->light_range_min); + net_buf_simple_add_le16(msg, state->light_range_max); + + err = bt_mesh_model_publish(model); + if (err) { + printk("bt_mesh_model_publish err %d\n", err); + } + return err; +} + +static bool light_lightness_range_setunack(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + uint16_t min, max; + struct light_lightness_state *state = model->user_data; + + min = net_buf_simple_pull_le16(buf); + max = net_buf_simple_pull_le16(buf); + + /* Here, Model specification is silent about tid implementation */ + + if (min == 0 || max == 0) { + return false; + } else { + if (min <= max) { + state->status_code = RANGE_SUCCESSFULLY_UPDATED; + + if (state->light_range_min != min || + state->light_range_max != max) { + + state->light_range_min = min; + state->light_range_max = max; + + save_on_flash(LIGHTNESS_RANGE); + } + } else { + /* The provided value for Range Max cannot be set */ + state->status_code = CANNOT_SET_RANGE_MAX; + return false; + } + } + + return true; +} + +static int light_lightness_range_set_unack(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + if (light_lightness_range_setunack(model, ctx, buf) == true) { + return light_lightness_range_publish(model); + } + return 0; +} + +static int light_lightness_range_set(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + int rc; + if (light_lightness_range_setunack(model, ctx, buf) == true) { + rc = light_lightness_range_get(model, ctx, buf); + if (rc) { + return rc; + } + rc = light_lightness_range_publish(model); + if (rc) { + return rc; + } + } + return 0; +} + +/* Light Lightness Client message handlers */ +static int light_lightness_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + printk("Acknownledgement from LIGHT_LIGHTNESS_SRV (Actual)\n"); + printk("Present Lightness = %04x\n", net_buf_simple_pull_le16(buf)); + + if (buf->om_len == 3) { + printk("Target Lightness = %04x\n", + net_buf_simple_pull_le16(buf)); + printk("Remaining Time = %02x\n", net_buf_simple_pull_u8(buf)); + } + return 0; +} + +static int light_lightness_linear_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + printk("Acknownledgement from LIGHT_LIGHTNESS_SRV (Linear)\n"); + printk("Present Lightness = %04x\n", net_buf_simple_pull_le16(buf)); + + if (buf->om_len == 3) { + printk("Target Lightness = %04x\n", + net_buf_simple_pull_le16(buf)); + printk("Remaining Time = %02x\n", net_buf_simple_pull_u8(buf)); + } + return 0; +} + +static int light_lightness_last_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + printk("Acknownledgement from LIGHT_LIGHTNESS_SRV (Last)\n"); + printk("Lightness = %04x\n", net_buf_simple_pull_le16(buf)); + return 0; +} + +static int light_lightness_default_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + printk("Acknownledgement from LIGHT_LIGHTNESS_SRV (Default)\n"); + printk("Lightness = %04x\n", net_buf_simple_pull_le16(buf)); + return 0; +} + +static int light_lightness_range_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + printk("Acknownledgement from LIGHT_LIGHTNESS_SRV (Lightness Range)\n"); + printk("Status Code = %02x\n", net_buf_simple_pull_u8(buf)); + printk("Range Min = %04x\n", net_buf_simple_pull_le16(buf)); + printk("Range Max = %04x\n", net_buf_simple_pull_le16(buf)); + return 0; +} + +/* Light CTL Server message handlers */ +static int light_ctl_get(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 9 + 4); + struct light_ctl_state *state = model->user_data; + + bt_mesh_model_msg_init(msg, BT_MESH_MODEL_LIGHT_CTL_STATUS); + net_buf_simple_add_le16(msg, state->lightness); + net_buf_simple_add_le16(msg, state->temp); + + if (state->transition->counter) { + calculate_rt(state->transition); + net_buf_simple_add_le16(msg, state->target_lightness); + net_buf_simple_add_le16(msg, state->target_temp); + net_buf_simple_add_u8(msg, state->transition->rt); + } + + if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { + printk("Unable to send LightCTL Status response\n"); + } + + os_mbuf_free_chain(msg); + return 0; +} + +int light_ctl_publish(struct bt_mesh_model *model) +{ + int err; + struct os_mbuf *msg = model->pub->msg; + struct light_ctl_state *state = model->user_data; + + if (model->pub->addr == BT_MESH_ADDR_UNASSIGNED) { + return 0; + } + + bt_mesh_model_msg_init(msg, BT_MESH_MODEL_LIGHT_CTL_STATUS); + + /* Here, as per Model specification, status should be + * made up of lightness & temperature values only + */ + net_buf_simple_add_le16(msg, state->lightness); + net_buf_simple_add_le16(msg, state->temp); + + if (state->transition->counter) { + calculate_rt(state->transition); + net_buf_simple_add_le16(msg, state->target_lightness); + net_buf_simple_add_le16(msg, state->target_temp); + net_buf_simple_add_u8(msg, state->transition->rt); + } + + err = bt_mesh_model_publish(model); + if (err) { + printk("bt_mesh_model_publish err %d\n", err); + } + return err; +} + +static int light_ctl_set_unack(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + uint8_t tid, tt, delay; + int16_t delta_uv; + uint16_t lightness, temp; + int64_t now; + struct light_ctl_state *state = model->user_data; + + lightness = net_buf_simple_pull_le16(buf); + temp = net_buf_simple_pull_le16(buf); + delta_uv = (int16_t) net_buf_simple_pull_le16(buf); + tid = net_buf_simple_pull_u8(buf); + + if (temp < TEMP_MIN || temp > TEMP_MAX) { + return 0; + } + + now = k_uptime_get(); + if (state->last_tid == tid && + state->last_src_addr == ctx->addr && + state->last_dst_addr == ctx->recv_dst && + (now - state->last_msg_timestamp <= K_SECONDS(6))) { + return 0; + } + + switch (buf->om_len) { + case 0x00: /* No optional fields are available */ + tt = default_tt; + delay = 0; + break; + case 0x02: /* Optional fields are available */ + tt = net_buf_simple_pull_u8(buf); + if ((tt & 0x3F) == 0x3F) { + return 0; + } + + delay = net_buf_simple_pull_u8(buf); + break; + default: + return 0; + } + + *ptr_counter = 0; + os_callout_stop(ptr_timer); + + state->last_tid = tid; + state->last_src_addr = ctx->addr; + state->last_dst_addr = ctx->recv_dst; + state->last_msg_timestamp = now; + state->target_lightness = lightness; + + if (temp < state->temp_range_min) { + temp = state->temp_range_min; + } else if (temp > state->temp_range_max) { + temp = state->temp_range_max; + } + + state->target_temp = temp; + state->target_delta_uv = delta_uv; + + if (state->target_lightness != state->lightness || + state->target_temp != state->temp || + state->target_delta_uv != state->delta_uv) { + light_ctl_tt_values(state, tt, delay); + } else { + light_ctl_publish(model); + return 0; + } + + /* For Instantaneous Transition */ + if (state->transition->counter == 0) { + state->lightness = state->target_lightness; + state->temp = state->target_temp; + state->delta_uv = state->target_delta_uv; + } + + state->transition->just_started = true; + light_ctl_publish(model); + light_ctl_handler(state); + return 0; +} + +static int light_ctl_set(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + uint8_t tid, tt, delay; + int16_t delta_uv; + uint16_t lightness, temp; + int64_t now; + struct light_ctl_state *state = model->user_data; + + lightness = net_buf_simple_pull_le16(buf); + temp = net_buf_simple_pull_le16(buf); + delta_uv = (int16_t) net_buf_simple_pull_le16(buf); + tid = net_buf_simple_pull_u8(buf); + + if (temp < TEMP_MIN || temp > TEMP_MAX) { + return 0; + } + + now = k_uptime_get(); + if (state->last_tid == tid && + state->last_src_addr == ctx->addr && + state->last_dst_addr == ctx->recv_dst && + (now - state->last_msg_timestamp <= K_SECONDS(6))) { + light_ctl_get(model, ctx, buf); + return 0; + } + + switch (buf->om_len) { + case 0x00: /* No optional fields are available */ + tt = default_tt; + delay = 0; + break; + case 0x02: /* Optional fields are available */ + tt = net_buf_simple_pull_u8(buf); + if ((tt & 0x3F) == 0x3F) { + return 0; + } + + delay = net_buf_simple_pull_u8(buf); + break; + default: + return 0; + } + + *ptr_counter = 0; + os_callout_stop(ptr_timer); + + state->last_tid = tid; + state->last_src_addr = ctx->addr; + state->last_dst_addr = ctx->recv_dst; + state->last_msg_timestamp = now; + state->target_lightness = lightness; + + if (temp < state->temp_range_min) { + temp = state->temp_range_min; + } else if (temp > state->temp_range_max) { + temp = state->temp_range_max; + } + + state->target_temp = temp; + state->target_delta_uv = delta_uv; + + if (state->target_lightness != state->lightness || + state->target_temp != state->temp || + state->target_delta_uv != state->delta_uv) { + light_ctl_tt_values(state, tt, delay); + } else { + light_ctl_get(model, ctx, buf); + light_ctl_publish(model); + return 0; + } + + /* For Instantaneous Transition */ + if (state->transition->counter == 0) { + state->lightness = state->target_lightness; + state->temp = state->target_temp; + state->delta_uv = state->target_delta_uv; + } + + state->transition->just_started = true; + light_ctl_get(model, ctx, buf); + light_ctl_publish(model); + light_ctl_handler(state); + return 0; +} + +static int light_ctl_temp_range_get(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 5 + 4); + struct light_ctl_state *state = model->user_data; + int rc; + + state->status_code = RANGE_SUCCESSFULLY_UPDATED; + + bt_mesh_model_msg_init(msg, BT_MESH_MODEL_LIGHT_CTL_TEMP_RANGE_STATUS); + net_buf_simple_add_u8(msg, state->status_code); + net_buf_simple_add_le16(msg, state->temp_range_min); + net_buf_simple_add_le16(msg, state->temp_range_max); + + rc = bt_mesh_model_send(model, ctx, msg, NULL, NULL); + if (rc) { + printk("Unable to send LightCTL Temp Range Status response\n"); + } + + os_mbuf_free_chain(msg); + return rc; +} + +static int light_ctl_default_get(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 6 + 4); + struct light_ctl_state *state = model->user_data; + int rc; + + bt_mesh_model_msg_init(msg, BT_MESH_MODEL_LIGHT_CTL_DEFAULT_STATUS); + net_buf_simple_add_le16(msg, state->lightness_def); + net_buf_simple_add_le16(msg, state->temp_def); + net_buf_simple_add_le16(msg, state->delta_uv_def); + + rc = bt_mesh_model_send(model, ctx, msg, NULL, NULL); + if (rc) { + printk("Unable to send LightCTL Default Status response\n"); + } + + os_mbuf_free_chain(msg); + return rc; +} + +/* Light CTL Setup Server message handlers */ + +static int light_ctl_default_publish(struct bt_mesh_model *model) +{ + int err; + struct os_mbuf *msg = model->pub->msg; + struct light_ctl_state *state = model->user_data; + + if (model->pub->addr == BT_MESH_ADDR_UNASSIGNED) { + return 0; + } + + bt_mesh_model_msg_init(msg, BT_MESH_MODEL_LIGHT_CTL_DEFAULT_STATUS); + net_buf_simple_add_le16(msg, state->lightness_def); + net_buf_simple_add_le16(msg, state->temp_def); + net_buf_simple_add_le16(msg, state->delta_uv_def); + + err = bt_mesh_model_publish(model); + if (err) { + printk("bt_mesh_model_publish err %d\n", err); + } + return err; +} + +static bool light_ctl_default_setunack(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + uint16_t lightness, temp; + int16_t delta_uv; + struct light_ctl_state *state = model->user_data; + + lightness = net_buf_simple_pull_le16(buf); + temp = net_buf_simple_pull_le16(buf); + delta_uv = (int16_t) net_buf_simple_pull_le16(buf); + + /* Here, Model specification is silent about tid implementation */ + + if (temp < TEMP_MIN || temp > TEMP_MAX) { + return false; + } + + if (temp < state->temp_range_min) { + temp = state->temp_range_min; + } else if (temp > state->temp_range_max) { + temp = state->temp_range_max; + } + + if (state->lightness_def != lightness || state->temp_def != temp || + state->delta_uv_def != delta_uv) { + state->lightness_def = lightness; + state->temp_def = temp; + state->delta_uv_def = delta_uv; + + save_on_flash(LIGHTNESS_TEMP_DEF_STATE); + } + + return true; +} + +static int light_ctl_default_set_unack(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + if (light_ctl_default_setunack(model, ctx, buf) == true) { + return light_ctl_default_publish(model); + } + return 0; +} + +static int light_ctl_default_set(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + if (light_ctl_default_setunack(model, ctx, buf) == true) { + light_ctl_default_get(model, ctx, buf); + return light_ctl_default_publish(model); + } + return 0; +} + +static int light_ctl_temp_range_publish(struct bt_mesh_model *model) +{ + int err; + struct os_mbuf *msg = model->pub->msg; + struct light_ctl_state *state = model->user_data; + + if (model->pub->addr == BT_MESH_ADDR_UNASSIGNED) { + return 0; + } + + bt_mesh_model_msg_init(msg, BT_MESH_MODEL_LIGHT_CTL_TEMP_RANGE_STATUS); + net_buf_simple_add_u8(msg, state->status_code); + net_buf_simple_add_le16(msg, state->temp_range_min); + net_buf_simple_add_le16(msg, state->temp_range_max); + + err = bt_mesh_model_publish(model); + if (err) { + printk("bt_mesh_model_publish err %d\n", err); + } + return err; +} + +static bool light_ctl_temp_range_setunack(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + uint16_t min, max; + struct light_ctl_state *state = model->user_data; + + min = net_buf_simple_pull_le16(buf); + max = net_buf_simple_pull_le16(buf); + + /* Here, Model specification is silent about tid implementation */ + + /* This is as per 6.1.3.1 in Mesh Model Specification */ + if (min < TEMP_MIN || min > TEMP_MAX || + max < TEMP_MIN || max > TEMP_MAX) { + return false; + } + + if (min <= max) { + state->status_code = RANGE_SUCCESSFULLY_UPDATED; + + if (state->temp_range_min != min || + state->temp_range_max != max) { + + state->temp_range_min = min; + state->temp_range_max = max; + + save_on_flash(TEMPERATURE_RANGE); + } + } else { + /* The provided value for Range Max cannot be set */ + state->status_code = CANNOT_SET_RANGE_MAX; + return false; + } + + return true; +} + +static int light_ctl_temp_range_set_unack(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + if (light_ctl_temp_range_setunack(model, ctx, buf) == true) { + return light_ctl_temp_range_publish(model); + } + return 0; +} + +static int light_ctl_temp_range_set(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + if (light_ctl_temp_range_setunack(model, ctx, buf) == true) { + light_ctl_temp_range_get(model, ctx, buf); + return light_ctl_temp_range_publish(model); + } + return 0; +} + +/* Light CTL Client message handlers */ +static int light_ctl_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + printk("Acknownledgement from LIGHT_CTL_SRV\n"); + printk("Present CTL Lightness = %04x\n", net_buf_simple_pull_le16(buf)); + printk("Present CTL Temperature = %04x\n", + net_buf_simple_pull_le16(buf)); + + if (buf->om_len == 5) { + printk("Target CTL Lightness = %04x\n", + net_buf_simple_pull_le16(buf)); + printk("Target CTL Temperature = %04x\n", + net_buf_simple_pull_le16(buf)); + printk("Remaining Time = %02x\n", net_buf_simple_pull_u8(buf)); + } + return 0; +} + +static int light_ctl_temp_range_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + printk("Acknownledgement from LIGHT_CTL_SRV (Temperature Range)\n"); + printk("Status Code = %02x\n", net_buf_simple_pull_u8(buf)); + printk("Range Min = %04x\n", net_buf_simple_pull_le16(buf)); + printk("Range Max = %04x\n", net_buf_simple_pull_le16(buf)); + return 0; +} + +static int light_ctl_temp_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + printk("Acknownledgement from LIGHT_CTL_TEMP_SRV\n"); + printk("Present CTL Temperature = %04x\n", + net_buf_simple_pull_le16(buf)); + printk("Present CTL Delta UV = %04x\n", + net_buf_simple_pull_le16(buf)); + + if (buf->om_len == 5) { + printk("Target CTL Temperature = %04x\n", + net_buf_simple_pull_le16(buf)); + printk("Target CTL Delta UV = %04x\n", + net_buf_simple_pull_le16(buf)); + printk("Remaining Time = %02x\n", net_buf_simple_pull_u8(buf)); + } + return 0; +} + +static int light_ctl_default_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + printk("Acknownledgement from LIGHT_CTL_SRV (Default)\n"); + printk("Lightness = %04x\n", net_buf_simple_pull_le16(buf)); + printk("Temperature = %04x\n", net_buf_simple_pull_le16(buf)); + printk("Delta UV = %04x\n", net_buf_simple_pull_le16(buf)); + return 0; +} + +/* Light CTL Temp. Server message handlers */ +static int light_ctl_temp_get(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 9 + 4); + struct light_ctl_state *state = model->user_data; + + bt_mesh_model_msg_init(msg, BT_MESH_MODEL_LIGHT_CTL_TEMP_STATUS); + net_buf_simple_add_le16(msg, state->temp); + net_buf_simple_add_le16(msg, state->delta_uv); + + if (state->transition->counter) { + calculate_rt(state->transition); + net_buf_simple_add_le16(msg, state->target_temp); + net_buf_simple_add_le16(msg, state->target_delta_uv); + net_buf_simple_add_u8(msg, state->transition->rt); + } + + if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { + printk("Unable to send LightCTL Temp. Status response\n"); + } + + os_mbuf_free_chain(msg); + return 0; +} + +int light_ctl_temp_publish(struct bt_mesh_model *model) +{ + int err; + struct os_mbuf *msg = model->pub->msg; + struct light_ctl_state *state = model->user_data; + + if (model->pub->addr == BT_MESH_ADDR_UNASSIGNED) { + return 0; + } + + bt_mesh_model_msg_init(msg, BT_MESH_MODEL_LIGHT_CTL_TEMP_STATUS); + net_buf_simple_add_le16(msg, state->temp); + net_buf_simple_add_le16(msg, state->delta_uv); + + if (state->transition->counter) { + calculate_rt(state->transition); + net_buf_simple_add_le16(msg, state->target_temp); + net_buf_simple_add_le16(msg, state->target_delta_uv); + net_buf_simple_add_u8(msg, state->transition->rt); + } + + err = bt_mesh_model_publish(model); + if (err) { + printk("bt_mesh_model_publish err %d\n", err); + } + return err; +} + +static int light_ctl_temp_set_unack(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + uint8_t tid, tt, delay; + int16_t delta_uv; + uint16_t temp; + int64_t now; + struct light_ctl_state *state = model->user_data; + + temp = net_buf_simple_pull_le16(buf); + delta_uv = (int16_t) net_buf_simple_pull_le16(buf); + tid = net_buf_simple_pull_u8(buf); + + if (temp < TEMP_MIN || temp > TEMP_MAX) { + return 0; + } + + now = k_uptime_get(); + if (state->last_tid == tid && + state->last_src_addr == ctx->addr && + state->last_dst_addr == ctx->recv_dst && + (now - state->last_msg_timestamp <= K_SECONDS(6))) { + return 0; + } + + switch (buf->om_len) { + case 0x00: /* No optional fields are available */ + tt = default_tt; + delay = 0; + break; + case 0x02: /* Optional fields are available */ + tt = net_buf_simple_pull_u8(buf); + if ((tt & 0x3F) == 0x3F) { + return 0; + } + + delay = net_buf_simple_pull_u8(buf); + break; + default: + return 0; + } + + *ptr_counter = 0; + os_callout_stop(ptr_timer); + + state->last_tid = tid; + state->last_src_addr = ctx->addr; + state->last_dst_addr = ctx->recv_dst; + state->last_msg_timestamp = now; + + if (temp < state->temp_range_min) { + temp = state->temp_range_min; + } else if (temp > state->temp_range_max) { + temp = state->temp_range_max; + } + + state->target_temp = temp; + state->target_delta_uv = delta_uv; + + if (state->target_temp != state->temp || + state->target_delta_uv != state->delta_uv) { + light_ctl_temp_tt_values(state, tt, delay); + } else { + light_ctl_temp_publish(model); + return 0; + } + + /* For Instantaneous Transition */ + if (state->transition->counter == 0) { + state->temp = state->target_temp; + state->delta_uv = state->target_delta_uv; + } + + state->transition->just_started = true; + light_ctl_temp_publish(model); + light_ctl_temp_handler(state); + return 0; +} + +static int light_ctl_temp_set(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + uint8_t tid, tt, delay; + int16_t delta_uv; + uint16_t temp; + int64_t now; + struct light_ctl_state *state = model->user_data; + + temp = net_buf_simple_pull_le16(buf); + delta_uv = (int16_t) net_buf_simple_pull_le16(buf); + tid = net_buf_simple_pull_u8(buf); + + if (temp < TEMP_MIN || temp > TEMP_MAX) { + return 0; + } + + now = k_uptime_get(); + if (state->last_tid == tid && + state->last_src_addr == ctx->addr && + state->last_dst_addr == ctx->recv_dst && + (now - state->last_msg_timestamp <= K_SECONDS(6))) { + return light_ctl_temp_get(model, ctx, buf); + } + + switch (buf->om_len) { + case 0x00: /* No optional fields are available */ + tt = default_tt; + delay = 0; + break; + case 0x02: /* Optional fields are available */ + tt = net_buf_simple_pull_u8(buf); + if ((tt & 0x3F) == 0x3F) { + return 0; + } + + delay = net_buf_simple_pull_u8(buf); + break; + default: + return 0; + } + + *ptr_counter = 0; + os_callout_stop(ptr_timer); + + state->last_tid = tid; + state->last_src_addr = ctx->addr; + state->last_dst_addr = ctx->recv_dst; + state->last_msg_timestamp = now; + + if (temp < state->temp_range_min) { + temp = state->temp_range_min; + } else if (temp > state->temp_range_max) { + temp = state->temp_range_max; + } + + state->target_temp = temp; + state->target_delta_uv = delta_uv; + + if (state->target_temp != state->temp || + state->target_delta_uv != state->delta_uv) { + light_ctl_temp_tt_values(state, tt, delay); + } else { + light_ctl_temp_get(model, ctx, buf); + light_ctl_temp_publish(model); + return 0; + } + + /* For Instantaneous Transition */ + if (state->transition->counter == 0) { + state->temp = state->target_temp; + state->delta_uv = state->target_delta_uv; + } + + state->transition->just_started = true; + light_ctl_temp_get(model, ctx, buf); + light_ctl_temp_publish(model); + light_ctl_temp_handler(state); + return 0; +} + +/* message handlers (End) */ + +/* Mapping of message handlers for Generic OnOff Server (0x1000) */ +static const struct bt_mesh_model_op gen_onoff_srv_op[] = { + { BT_MESH_MODEL_OP_2(0x82, 0x01), 0, gen_onoff_get }, + { BT_MESH_MODEL_OP_2(0x82, 0x02), 2, gen_onoff_set }, + { BT_MESH_MODEL_OP_2(0x82, 0x03), 2, gen_onoff_set_unack }, + BT_MESH_MODEL_OP_END, +}; + +/* Mapping of message handlers for Generic OnOff Client (0x1001) */ +static const struct bt_mesh_model_op gen_onoff_cli_op[] = { + { BT_MESH_MODEL_OP_2(0x82, 0x04), 1, gen_onoff_status }, + BT_MESH_MODEL_OP_END, +}; + +/* Mapping of message handlers for Generic Levl Server (0x1002) */ +static const struct bt_mesh_model_op gen_level_srv_op[] = { + { BT_MESH_MODEL_OP_2(0x82, 0x05), 0, gen_level_get }, + { BT_MESH_MODEL_OP_2(0x82, 0x06), 3, gen_level_set }, + { BT_MESH_MODEL_OP_2(0x82, 0x07), 3, gen_level_set_unack }, + { BT_MESH_MODEL_OP_2(0x82, 0x09), 5, gen_delta_set }, + { BT_MESH_MODEL_OP_2(0x82, 0x0A), 5, gen_delta_set_unack }, + { BT_MESH_MODEL_OP_2(0x82, 0x0B), 3, gen_move_set }, + { BT_MESH_MODEL_OP_2(0x82, 0x0C), 3, gen_move_set_unack }, + BT_MESH_MODEL_OP_END, +}; + +/* Mapping of message handlers for Generic Level Client (0x1003) */ +static const struct bt_mesh_model_op gen_level_cli_op[] = { + { BT_MESH_MODEL_OP_2(0x82, 0x08), 2, gen_level_status }, + BT_MESH_MODEL_OP_END, +}; + +/* Mapping of message handlers for Generic Default TT Server (0x1004) */ +static const struct bt_mesh_model_op gen_def_trans_time_srv_op[] = { + { BT_MESH_MODEL_OP_2(0x82, 0x0D), 0, gen_def_trans_time_get }, + { BT_MESH_MODEL_OP_2(0x82, 0x0E), 1, gen_def_trans_time_set }, + { BT_MESH_MODEL_OP_2(0x82, 0x0F), 1, gen_def_trans_time_set_unack }, + BT_MESH_MODEL_OP_END, +}; + +/* Mapping of message handlers for Generic Default TT Client (0x1005) */ +static const struct bt_mesh_model_op gen_def_trans_time_cli_op[] = { + { BT_MESH_MODEL_OP_2(0x82, 0x10), 1, gen_def_trans_time_status }, + BT_MESH_MODEL_OP_END, +}; + +/* Mapping of message handlers for Generic Power OnOff Server (0x1006) */ +static const struct bt_mesh_model_op gen_power_onoff_srv_op[] = { + { BT_MESH_MODEL_OP_2(0x82, 0x11), 0, gen_onpowerup_get }, + BT_MESH_MODEL_OP_END, +}; + +/* Mapping of message handlers for Generic Power OnOff Setup Server (0x1007) */ +static const struct bt_mesh_model_op gen_power_onoff_setup_srv_op[] = { + { BT_MESH_MODEL_OP_2(0x82, 0x13), 1, gen_onpowerup_set }, + { BT_MESH_MODEL_OP_2(0x82, 0x14), 1, gen_onpowerup_set_unack }, + BT_MESH_MODEL_OP_END, +}; + +/* Mapping of message handlers for Generic Power OnOff Client (0x1008) */ +static const struct bt_mesh_model_op gen_power_onoff_cli_op[] = { + { BT_MESH_MODEL_OP_2(0x82, 0x12), 1, gen_onpowerup_status }, + BT_MESH_MODEL_OP_END, +}; + +/* Mapping of message handlers for Light Lightness Server (0x1300) */ +static const struct bt_mesh_model_op light_lightness_srv_op[] = { + { BT_MESH_MODEL_OP_2(0x82, 0x4B), 0, light_lightness_get }, + { BT_MESH_MODEL_OP_2(0x82, 0x4C), 3, light_lightness_set }, + { BT_MESH_MODEL_OP_2(0x82, 0x4D), 3, light_lightness_set_unack }, + { BT_MESH_MODEL_OP_2(0x82, 0x4F), 0, light_lightness_linear_get }, + { BT_MESH_MODEL_OP_2(0x82, 0x50), 3, light_lightness_linear_set }, + { BT_MESH_MODEL_OP_2(0x82, 0x51), 3, + light_lightness_linear_set_unack }, + { BT_MESH_MODEL_OP_2(0x82, 0x53), 0, light_lightness_last_get }, + { BT_MESH_MODEL_OP_2(0x82, 0x55), 0, light_lightness_default_get }, + { BT_MESH_MODEL_OP_2(0x82, 0x57), 0, light_lightness_range_get }, + BT_MESH_MODEL_OP_END, +}; + +/* Mapping of message handlers for Light Lightness Setup Server (0x1301) */ +static const struct bt_mesh_model_op light_lightness_setup_srv_op[] = { + { BT_MESH_MODEL_OP_2(0x82, 0x59), 2, light_lightness_default_set }, + { BT_MESH_MODEL_OP_2(0x82, 0x5A), 2, + light_lightness_default_set_unack }, + { BT_MESH_MODEL_OP_2(0x82, 0x5B), 4, light_lightness_range_set }, + { BT_MESH_MODEL_OP_2(0x82, 0x5C), 4, light_lightness_range_set_unack }, + BT_MESH_MODEL_OP_END, +}; + +/* Mapping of message handlers for Light Lightness Client (0x1302) */ +static const struct bt_mesh_model_op light_lightness_cli_op[] = { + { BT_MESH_MODEL_OP_2(0x82, 0x4E), 2, light_lightness_status }, + { BT_MESH_MODEL_OP_2(0x82, 0x52), 2, light_lightness_linear_status }, + { BT_MESH_MODEL_OP_2(0x82, 0x54), 2, light_lightness_last_status }, + { BT_MESH_MODEL_OP_2(0x82, 0x56), 2, light_lightness_default_status }, + { BT_MESH_MODEL_OP_2(0x82, 0x58), 5, light_lightness_range_status }, + BT_MESH_MODEL_OP_END, +}; + +/* Mapping of message handlers for Light CTL Server (0x1303) */ +static const struct bt_mesh_model_op light_ctl_srv_op[] = { + { BT_MESH_MODEL_OP_2(0x82, 0x5D), 0, light_ctl_get }, + { BT_MESH_MODEL_OP_2(0x82, 0x5E), 7, light_ctl_set }, + { BT_MESH_MODEL_OP_2(0x82, 0x5F), 7, light_ctl_set_unack }, + { BT_MESH_MODEL_OP_2(0x82, 0x62), 0, light_ctl_temp_range_get }, + { BT_MESH_MODEL_OP_2(0x82, 0x67), 0, light_ctl_default_get }, + BT_MESH_MODEL_OP_END, +}; + +/* Mapping of message handlers for Light CTL Setup Server (0x1304) */ +static const struct bt_mesh_model_op light_ctl_setup_srv_op[] = { + { BT_MESH_MODEL_OP_2(0x82, 0x69), 6, light_ctl_default_set }, + { BT_MESH_MODEL_OP_2(0x82, 0x6A), 6, light_ctl_default_set_unack }, + { BT_MESH_MODEL_OP_2(0x82, 0x6B), 4, light_ctl_temp_range_set }, + { BT_MESH_MODEL_OP_2(0x82, 0x6C), 4, light_ctl_temp_range_set_unack }, + BT_MESH_MODEL_OP_END, +}; + +/* Mapping of message handlers for Light CTL Client (0x1305) */ +static const struct bt_mesh_model_op light_ctl_cli_op[] = { + { BT_MESH_MODEL_OP_2(0x82, 0x60), 4, light_ctl_status }, + { BT_MESH_MODEL_OP_2(0x82, 0x63), 5, light_ctl_temp_range_status }, + { BT_MESH_MODEL_OP_2(0x82, 0x66), 4, light_ctl_temp_status }, + { BT_MESH_MODEL_OP_2(0x82, 0x68), 6, light_ctl_default_status }, + BT_MESH_MODEL_OP_END, +}; + +/* Mapping of message handlers for Light CTL Temp. Server (0x1306) */ +static const struct bt_mesh_model_op light_ctl_temp_srv_op[] = { + { BT_MESH_MODEL_OP_2(0x82, 0x61), 0, light_ctl_temp_get }, + { BT_MESH_MODEL_OP_2(0x82, 0x64), 5, light_ctl_temp_set }, + { BT_MESH_MODEL_OP_2(0x82, 0x65), 5, light_ctl_temp_set_unack }, + BT_MESH_MODEL_OP_END, +}; + +/* Mapping of message handlers for Vendor (0x4321) */ +static const struct bt_mesh_model_op vnd_ops[] = { + { BT_MESH_MODEL_OP_3(0x01, CID_RUNTIME), 0, vnd_get }, + { BT_MESH_MODEL_OP_3(0x02, CID_RUNTIME), 3, vnd_set }, + { BT_MESH_MODEL_OP_3(0x03, CID_RUNTIME), 3, vnd_set_unack }, + { BT_MESH_MODEL_OP_3(0x04, CID_RUNTIME), 6, vnd_status }, + BT_MESH_MODEL_OP_END, +}; + +struct bt_mesh_model root_models[] = { + BT_MESH_MODEL_CFG_SRV, + BT_MESH_MODEL_HEALTH_SRV(&health_srv, &health_pub), + + BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_ONOFF_SRV, + gen_onoff_srv_op, &gen_onoff_srv_pub_root, + &gen_onoff_srv_root_user_data), + BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_ONOFF_CLI, + gen_onoff_cli_op, &gen_onoff_cli_pub_root, + NULL), + + BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_LEVEL_SRV, + gen_level_srv_op, &gen_level_srv_pub_root, + &gen_level_srv_root_user_data), + BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_LEVEL_CLI, + gen_level_cli_op, &gen_level_cli_pub_root, + NULL), + + BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_DEF_TRANS_TIME_SRV, + gen_def_trans_time_srv_op, + &gen_def_trans_time_srv_pub, + &gen_def_trans_time_srv_user_data), + BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_DEF_TRANS_TIME_CLI, + gen_def_trans_time_cli_op, + &gen_def_trans_time_cli_pub, + NULL), + + BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_POWER_ONOFF_SRV, + gen_power_onoff_srv_op, &gen_power_onoff_srv_pub, + &gen_power_onoff_srv_user_data), + BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_POWER_ONOFF_SETUP_SRV, + gen_power_onoff_setup_srv_op, + &gen_power_onoff_srv_pub, + &gen_power_onoff_srv_user_data), + BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_POWER_ONOFF_CLI, + gen_power_onoff_cli_op, &gen_power_onoff_cli_pub, + NULL), + + BT_MESH_MODEL(BT_MESH_MODEL_ID_LIGHT_LIGHTNESS_SRV, + light_lightness_srv_op, &light_lightness_srv_pub, + &light_lightness_srv_user_data), + BT_MESH_MODEL(BT_MESH_MODEL_ID_LIGHT_LIGHTNESS_SETUP_SRV, + light_lightness_setup_srv_op, + &light_lightness_srv_pub, + &light_lightness_srv_user_data), + BT_MESH_MODEL(BT_MESH_MODEL_ID_LIGHT_LIGHTNESS_CLI, + light_lightness_cli_op, &light_lightness_cli_pub, + NULL), + + BT_MESH_MODEL(BT_MESH_MODEL_ID_LIGHT_CTL_SRV, + light_ctl_srv_op, &light_ctl_srv_pub, + &light_ctl_srv_user_data), + BT_MESH_MODEL(BT_MESH_MODEL_ID_LIGHT_CTL_SETUP_SRV, + light_ctl_setup_srv_op, &light_ctl_srv_pub, + &light_ctl_srv_user_data), + BT_MESH_MODEL(BT_MESH_MODEL_ID_LIGHT_CTL_CLI, + light_ctl_cli_op, &light_ctl_cli_pub, + NULL), +}; + +struct bt_mesh_model vnd_models[] = { + BT_MESH_MODEL_VND(CID_RUNTIME, 0x4321, vnd_ops, + &vnd_pub, &vnd_user_data), +}; + +struct bt_mesh_model s0_models[] = { + BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_LEVEL_SRV, + gen_level_srv_op, &gen_level_srv_pub_s0, + &gen_level_srv_s0_user_data), + BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_LEVEL_CLI, + gen_level_cli_op, &gen_level_cli_pub_s0, + NULL), + + BT_MESH_MODEL(BT_MESH_MODEL_ID_LIGHT_CTL_TEMP_SRV, + light_ctl_temp_srv_op, &light_ctl_srv_pub, + &light_ctl_srv_user_data), +}; + +static struct bt_mesh_elem elements[] = { + BT_MESH_ELEM(0, root_models, vnd_models), + BT_MESH_ELEM(0, s0_models, BT_MESH_MODEL_NONE), +}; + +const struct bt_mesh_comp comp = { + .cid = CID_RUNTIME, + .elem = elements, + .elem_count = ARRAY_SIZE(elements), +}; + diff --git a/lib/bt/host/nimble/nimble/apps/blemesh_models_example_2/src/device_composition.h b/lib/bt/host/nimble/nimble/apps/blemesh_models_example_2/src/device_composition.h new file mode 100644 index 00000000..1b5bf5f7 --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/blemesh_models_example_2/src/device_composition.h @@ -0,0 +1,177 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* Bluetooth: Mesh Generic OnOff, Generic Level, Lighting & Vendor Models + * + * Copyright (c) 2018 Vikrant More + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef _DEVICE_COMPOSITION_H +#define _DEVICE_COMPOSITION_H + +#define CID_RUNTIME 0x05C3 + +#define STATE_OFF 0x00 +#define STATE_ON 0x01 +#define STATE_DEFAULT 0x01 +#define STATE_RESTORE 0x02 + +/* Following 4 values are as per Mesh Model specification */ +#define LIGHTNESS_MIN 0x0001 +#define LIGHTNESS_MAX 0xFFFF +#define TEMP_MIN 0x0320 +#define TEMP_MAX 0x4E20 + +/* Refer 7.2 of Mesh Model Specification */ +#define RANGE_SUCCESSFULLY_UPDATED 0x00 +#define CANNOT_SET_RANGE_MIN 0x01 +#define CANNOT_SET_RANGE_MAX 0x02 + +struct generic_onoff_state { + uint8_t onoff; + uint8_t target_onoff; + + uint8_t last_tid; + uint16_t last_src_addr; + uint16_t last_dst_addr; + int64_t last_msg_timestamp; + + int32_t tt_delta; + + struct transition *transition; +}; + +struct generic_level_state { + int16_t level; + int16_t target_level; + + int16_t last_level; + int32_t last_delta; + + uint8_t last_tid; + uint16_t last_src_addr; + uint16_t last_dst_addr; + int64_t last_msg_timestamp; + + int32_t tt_delta; + + struct transition *transition; +}; + +struct generic_onpowerup_state { + uint8_t onpowerup; +}; + +struct gen_def_trans_time_state { + uint8_t tt; +}; + +struct vendor_state { + int current; + uint32_t response; + uint8_t last_tid; + uint16_t last_src_addr; + uint16_t last_dst_addr; + int64_t last_msg_timestamp; +}; + +struct light_lightness_state { + uint16_t linear; + uint16_t target_linear; + + uint16_t actual; + uint16_t target_actual; + + uint16_t last; + uint16_t def; + + uint8_t status_code; + uint16_t light_range_min; + uint16_t light_range_max; + uint32_t lightness_range; + + uint8_t last_tid; + uint16_t last_src_addr; + uint16_t last_dst_addr; + int64_t last_msg_timestamp; + + int32_t tt_delta_actual; + int32_t tt_delta_linear; + + struct transition *transition; +}; + +struct light_ctl_state { + uint16_t lightness; + uint16_t target_lightness; + + uint16_t temp; + uint16_t target_temp; + + int16_t delta_uv; + int16_t target_delta_uv; + + uint8_t status_code; + uint16_t temp_range_min; + uint16_t temp_range_max; + uint32_t temperature_range; + + uint16_t lightness_def; + uint16_t temp_def; + uint32_t lightness_temp_def; + int16_t delta_uv_def; + + uint32_t lightness_temp_last; + + uint8_t last_tid; + uint16_t last_src_addr; + uint16_t last_dst_addr; + int64_t last_msg_timestamp; + + int32_t tt_delta_lightness; + int32_t tt_delta_temp; + int32_t tt_delta_duv; + + struct transition *transition; +}; + +extern struct generic_onoff_state gen_onoff_srv_root_user_data; +extern struct generic_level_state gen_level_srv_root_user_data; +extern struct gen_def_trans_time_state gen_def_trans_time_srv_user_data; +extern struct generic_onpowerup_state gen_power_onoff_srv_user_data; +extern struct light_lightness_state light_lightness_srv_user_data; +extern struct light_ctl_state light_ctl_srv_user_data; +extern struct generic_level_state gen_level_srv_s0_user_data; + +extern struct bt_mesh_model root_models[]; +extern struct bt_mesh_model vnd_models[]; +extern struct bt_mesh_model s0_models[]; + +extern const struct bt_mesh_comp comp; + +int gen_onoff_publish(struct bt_mesh_model *model); +int gen_level_publish(struct bt_mesh_model *model); +int light_lightness_publish(struct bt_mesh_model *model); +int light_lightness_linear_publish(struct bt_mesh_model *model); +int light_ctl_publish(struct bt_mesh_model *model); +int light_ctl_temp_publish(struct bt_mesh_model *model); + +#endif diff --git a/lib/bt/host/nimble/nimble/apps/blemesh_models_example_2/src/main.c b/lib/bt/host/nimble/nimble/apps/blemesh_models_example_2/src/main.c new file mode 100644 index 00000000..741367b4 --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/blemesh_models_example_2/src/main.c @@ -0,0 +1,250 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* Bluetooth: Mesh Generic OnOff, Generic Level, Lighting & Vendor Models + * + * Copyright (c) 2018 Vikrant More + * + * SPDX-License-Identifier: Apache-2.0 + */ + + +#include "console/console.h" +#include "hal/hal_gpio.h" +#include "mesh/mesh.h" + +#include "app_gpio.h" +#include "storage.h" + +#include "ble_mesh.h" +#include "device_composition.h" +#include "no_transition_work_handler.h" +#include "publisher.h" +#include "state_binding.h" +#include "transition.h" + +static bool reset; + +static void light_default_var_init(void) +{ + gen_def_trans_time_srv_user_data.tt = 0x00; + + gen_power_onoff_srv_user_data.onpowerup = STATE_DEFAULT; + + light_lightness_srv_user_data.light_range_min = LIGHTNESS_MIN; + light_lightness_srv_user_data.light_range_max = LIGHTNESS_MAX; + light_lightness_srv_user_data.last = LIGHTNESS_MAX; + light_lightness_srv_user_data.def = LIGHTNESS_MAX; + + /* Following 2 values are as per specification */ + light_ctl_srv_user_data.temp_range_min = TEMP_MIN; + light_ctl_srv_user_data.temp_range_max = TEMP_MAX; + + light_ctl_srv_user_data.temp_def = TEMP_MIN; + + light_ctl_srv_user_data.lightness_temp_last = + (uint32_t) ((LIGHTNESS_MAX << 16) | TEMP_MIN); +} + +static void light_default_status_init(void) +{ + uint16_t lightness; + + lightness = (uint16_t) (light_ctl_srv_user_data.lightness_temp_last >> 16); + + if (lightness) { + gen_onoff_srv_root_user_data.onoff = STATE_ON; + } else { + gen_onoff_srv_root_user_data.onoff = STATE_OFF; + } + + /* Retrieve Default Lightness & Temperature Values */ + + if (light_ctl_srv_user_data.lightness_temp_def) { + light_ctl_srv_user_data.lightness_def = (uint16_t) + (light_ctl_srv_user_data.lightness_temp_def >> 16); + + light_ctl_srv_user_data.temp_def = (uint16_t) + (light_ctl_srv_user_data.lightness_temp_def); + } + + light_lightness_srv_user_data.def = + light_ctl_srv_user_data.lightness_def; + + light_ctl_srv_user_data.temp = light_ctl_srv_user_data.temp_def; + + /* Retrieve Range of Lightness & Temperature */ + + if (light_lightness_srv_user_data.lightness_range) { + light_lightness_srv_user_data.light_range_max = (uint16_t) + (light_lightness_srv_user_data.lightness_range >> 16); + + light_lightness_srv_user_data.light_range_min = (uint16_t) + (light_lightness_srv_user_data.lightness_range); + } + + if (light_ctl_srv_user_data.temperature_range) { + light_ctl_srv_user_data.temp_range_max = (uint16_t) + (light_ctl_srv_user_data.temperature_range >> 16); + + light_ctl_srv_user_data.temp_range_min = (uint16_t) + (light_ctl_srv_user_data.temperature_range); + } + + switch (gen_power_onoff_srv_user_data.onpowerup) { + case STATE_OFF: + gen_onoff_srv_root_user_data.onoff = STATE_OFF; + state_binding(ONOFF, ONOFF_TEMP); + break; + case STATE_DEFAULT: + gen_onoff_srv_root_user_data.onoff = STATE_ON; + state_binding(ONOFF, ONOFF_TEMP); + break; + case STATE_RESTORE: + light_lightness_srv_user_data.last = (uint16_t) + (light_ctl_srv_user_data.lightness_temp_last >> 16); + + light_ctl_srv_user_data.temp = + (uint16_t) (light_ctl_srv_user_data.lightness_temp_last); + + state_binding(ONPOWERUP, ONOFF_TEMP); + break; + } + + default_tt = gen_def_trans_time_srv_user_data.tt; +} + +void update_light_state(void) +{ + uint8_t power, color; + + power = 100 * ((float) lightness / 65535); + color = 100 * ((float) (temperature + 32768) / 65535); + + printk("power-> %d, color-> %d\n", power, color); + + if (lightness) { + /* LED1 On */ + hal_gpio_write(led_device[0], 0); + } else { + /* LED1 Off */ + hal_gpio_write(led_device[0], 1); + } + + if (power < 50) { + /* LED3 On */ + hal_gpio_write(led_device[2], 0); + } else { + /* LED3 Off */ + hal_gpio_write(led_device[2], 1); + } + + if (color < 50) { + /* LED4 On */ + hal_gpio_write(led_device[3], 0); + } else { + /* LED4 Off */ + hal_gpio_write(led_device[3], 1); + } + + if (*ptr_counter == 0 || reset == false) { + reset = true; + os_callout_reset(&no_transition_work, 0); + } +} + +static void short_time_multireset_bt_mesh_unprovisioning(void) +{ + if (reset_counter >= 4) { + reset_counter = 0; + printk("BT Mesh reset\n"); + bt_mesh_reset(); + } else { + printk("Reset Counter -> %d\n", reset_counter); + reset_counter++; + } + + save_on_flash(RESET_COUNTER); +} + +static void reset_counter_timer_handler(struct os_event *dummy) +{ + reset_counter = 0; + save_on_flash(RESET_COUNTER); + printk("Reset Counter set to Zero\n"); +} + +struct os_callout reset_counter_timer; + +static void init_timers(void) +{ + + os_callout_init(&reset_counter_timer, os_eventq_dflt_get(), + reset_counter_timer_handler, NULL); + os_callout_reset(&reset_counter_timer, + os_time_ms_to_ticks32(K_MSEC(7000))); + + no_transition_work_init(); +} + +void bt_initialized(void) +{ + light_default_status_init(); + + update_light_state(); + + randomize_publishers_TID(); + + short_time_multireset_bt_mesh_unprovisioning(); +} + +int main(void) +{ +#ifdef ARCH_sim + mcu_sim_parse_args(argc, argv); +#endif + + /* Initialize OS */ + sysinit(); + + light_default_var_init(); + + app_gpio_init(); + + init_timers(); + + transition_timers_init(); + + init_pub(); + + ps_settings_init(); + + printk("Initializing...\n"); + + /* Initialize the NimBLE host configuration. */ + ble_hs_cfg.reset_cb = blemesh_on_reset; + ble_hs_cfg.sync_cb = blemesh_on_sync; + ble_hs_cfg.store_status_cb = ble_store_util_status_rr; + + while (1) { + os_eventq_run(os_eventq_dflt_get()); + } + + return 0; +} diff --git a/lib/bt/host/nimble/nimble/apps/blemesh_models_example_2/src/no_transition_work_handler.c b/lib/bt/host/nimble/nimble/apps/blemesh_models_example_2/src/no_transition_work_handler.c new file mode 100644 index 00000000..58630bdc --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/blemesh_models_example_2/src/no_transition_work_handler.c @@ -0,0 +1,89 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* Bluetooth: Mesh Generic OnOff, Generic Level, Lighting & Vendor Models + * + * Copyright (c) 2018 Vikrant More + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "ble_mesh.h" +#include "device_composition.h" + +#include "storage.h" + +static void unsolicitedly_publish_states_work_handler(struct os_event *work) +{ + gen_onoff_publish(&root_models[2]); + gen_level_publish(&root_models[4]); + light_lightness_publish(&root_models[11]); + light_lightness_linear_publish(&root_models[11]); + light_ctl_publish(&root_models[14]); + + gen_level_publish(&s0_models[0]); + light_ctl_temp_publish(&s0_models[2]); +} + +struct os_callout unsolicitedly_publish_states_work; + +static void unsolicitedly_publish_states_timer_handler(struct os_event *dummy) +{ + os_callout_reset(&unsolicitedly_publish_states_work, 0); +} + +struct os_callout unsolicitedly_publish_states_timer; + +static void save_lightness_temp_last_values_timer_handler(struct os_event *dummy) +{ + save_on_flash(LIGHTNESS_TEMP_LAST_STATE); +} + +struct os_callout save_lightness_temp_last_values_timer; + +static void no_transition_work_handler(struct os_event *work) +{ + os_callout_reset(&unsolicitedly_publish_states_timer, + os_time_ms_to_ticks32(K_MSEC(5000))); + + /* If Lightness & Temperature values remains stable for + * 10 Seconds then & then only get stored on SoC flash. + */ + if (gen_power_onoff_srv_user_data.onpowerup == STATE_RESTORE) { + os_callout_reset(&save_lightness_temp_last_values_timer, + os_time_ms_to_ticks32( + K_MSEC(10000))); + } +} + +struct os_callout no_transition_work; + +void no_transition_work_init(void) +{ + os_callout_init(&no_transition_work, os_eventq_dflt_get(), + no_transition_work_handler, NULL); + os_callout_init(&save_lightness_temp_last_values_timer, + os_eventq_dflt_get(), + save_lightness_temp_last_values_timer_handler, + NULL); + os_callout_init(&unsolicitedly_publish_states_work, os_eventq_dflt_get(), + unsolicitedly_publish_states_work_handler, NULL); + os_callout_init(&unsolicitedly_publish_states_timer, os_eventq_dflt_get(), + unsolicitedly_publish_states_timer_handler, NULL); +} diff --git a/lib/bt/host/nimble/nimble/apps/blemesh_models_example_2/src/no_transition_work_handler.h b/lib/bt/host/nimble/nimble/apps/blemesh_models_example_2/src/no_transition_work_handler.h new file mode 100644 index 00000000..a747dfda --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/blemesh_models_example_2/src/no_transition_work_handler.h @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* Bluetooth: Mesh Generic OnOff, Generic Level, Lighting & Vendor Models + * + * Copyright (c) 2018 Vikrant More + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef _NO_TRANSITION_WORK_HANDLER_H +#define _NO_TRANSITION_WORK_HANDLER_H + +extern struct os_callout no_transition_work; + +void no_transition_work_init(void); + +#endif diff --git a/lib/bt/host/nimble/nimble/apps/blemesh_models_example_2/src/publisher.c b/lib/bt/host/nimble/nimble/apps/blemesh_models_example_2/src/publisher.c new file mode 100644 index 00000000..f8cf849d --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/blemesh_models_example_2/src/publisher.c @@ -0,0 +1,266 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* Bluetooth: Mesh Generic OnOff, Generic Level, Lighting & Vendor Models + * + * Copyright (c) 2018 Vikrant More + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "console/console.h" +#include "hal/hal_gpio.h" + +#include "app_gpio.h" + +#include "ble_mesh.h" +#include "device_composition.h" +#include "publisher.h" + +#define ONOFF +#define GENERIC_LEVEL +/* #define LIGHT_CTL */ +/* #define LIGHT_CTL_TEMP */ + +static bool is_randomization_of_TIDs_done; + +#if (defined(ONOFF) || defined(ONOFF_TT)) +static uint8_t tid_onoff; +#elif defined(VND_MODEL_TEST) +static uint8_t tid_vnd; +#endif + +static uint8_t tid_level; + +void randomize_publishers_TID(void) +{ +#if (defined(ONOFF) || defined(ONOFF_TT)) + bt_rand(&tid_onoff, sizeof(tid_onoff)); +#elif defined(VND_MODEL_TEST) + bt_rand(&tid_vnd, sizeof(tid_vnd)); +#endif + + bt_rand(&tid_level, sizeof(tid_level)); + + is_randomization_of_TIDs_done = true; +} + +static uint32_t button_read(int button) +{ + return (uint32_t) hal_gpio_read(button); +} + +void publish(struct os_event *work) +{ + int err = 0; + + if (is_randomization_of_TIDs_done == false) { + return; + } + + if (button_read(button_device[0]) == 0) { +#if defined(ONOFF) + bt_mesh_model_msg_init(root_models[3].pub->msg, + BT_MESH_MODEL_OP_GEN_ONOFF_SET_UNACK); + net_buf_simple_add_u8(root_models[3].pub->msg, 0x01); + net_buf_simple_add_u8(root_models[3].pub->msg, tid_onoff++); + err = bt_mesh_model_publish(&root_models[3]); +#elif defined(ONOFF_TT) + bt_mesh_model_msg_init(root_models[3].pub->msg, + BT_MESH_MODEL_OP_GEN_ONOFF_SET_UNACK); + net_buf_simple_add_u8(root_models[3].pub->msg, 0x01); + net_buf_simple_add_u8(root_models[3].pub->msg, tid_onoff++); + net_buf_simple_add_u8(root_models[3].pub->msg, 0x45); + net_buf_simple_add_u8(root_models[3].pub->msg, 0x28); + err = bt_mesh_model_publish(&root_models[3]); +#elif defined(VND_MODEL_TEST) + bt_mesh_model_msg_init(vnd_models[0].pub->msg, + BT_MESH_MODEL_OP_3(0x03, CID_RUNTIME)); + net_buf_simple_add_le16(vnd_models[0].pub->msg, 0x0001); + net_buf_simple_add_u8(vnd_models[0].pub->msg, tid_vnd++); + err = bt_mesh_model_publish(&vnd_models[0]); +#endif + + } else if (button_read(button_device[1]) == 0) { +#if defined(ONOFF) + bt_mesh_model_msg_init(root_models[3].pub->msg, + BT_MESH_MODEL_OP_GEN_ONOFF_SET_UNACK); + net_buf_simple_add_u8(root_models[3].pub->msg, 0x00); + net_buf_simple_add_u8(root_models[3].pub->msg, tid_onoff++); + err = bt_mesh_model_publish(&root_models[3]); +#elif defined(ONOFF_TT) + bt_mesh_model_msg_init(root_models[3].pub->msg, + BT_MESH_MODEL_OP_GEN_ONOFF_SET_UNACK); + net_buf_simple_add_u8(root_models[3].pub->msg, 0x00); + net_buf_simple_add_u8(root_models[3].pub->msg, tid_onoff++); + net_buf_simple_add_u8(root_models[3].pub->msg, 0x45); + net_buf_simple_add_u8(root_models[3].pub->msg, 0x28); + err = bt_mesh_model_publish(&root_models[3]); +#elif defined(VND_MODEL_TEST) + bt_mesh_model_msg_init(vnd_models[0].pub->msg, + BT_MESH_MODEL_OP_3(0x03, CID_RUNTIME)); + net_buf_simple_add_le16(vnd_models[0].pub->msg, 0x0000); + net_buf_simple_add_u8(vnd_models[0].pub->msg, tid_vnd++); + err = bt_mesh_model_publish(&vnd_models[0]); +#endif + + } else if (button_read(button_device[2]) == 0) { +#if defined(GENERIC_LEVEL) + bt_mesh_model_msg_init(root_models[5].pub->msg, + BT_MESH_MODEL_OP_GEN_LEVEL_SET_UNACK); + net_buf_simple_add_le16(root_models[5].pub->msg, LEVEL_S25); + net_buf_simple_add_u8(root_models[5].pub->msg, tid_level++); + err = bt_mesh_model_publish(&root_models[5]); +#elif defined(ONOFF_GET) + bt_mesh_model_msg_init(root_models[3].pub->msg, + BT_MESH_MODEL_OP_GEN_ONOFF_GET); + err = bt_mesh_model_publish(&root_models[3]); +#elif defined(GENERIC_DELTA_LEVEL) + bt_mesh_model_msg_init(root_models[5].pub->msg, + BT_MESH_MODEL_OP_GEN_DELTA_SET_UNACK); + net_buf_simple_add_le32(root_models[5].pub->msg, 100); + net_buf_simple_add_u8(root_models[5].pub->msg, tid_level++); + err = bt_mesh_model_publish(&root_models[5]); +#elif defined(GENERIC_MOVE_LEVEL_TT) + bt_mesh_model_msg_init(root_models[5].pub->msg, + BT_MESH_MODEL_OP_GEN_MOVE_SET_UNACK); + net_buf_simple_add_le16(root_models[5].pub->msg, 13100); + net_buf_simple_add_u8(root_models[5].pub->msg, tid_level++); + net_buf_simple_add_u8(root_models[5].pub->msg, 0x45); + net_buf_simple_add_u8(root_models[5].pub->msg, 0x00); + err = bt_mesh_model_publish(&root_models[5]); +#elif defined(LIGHT_LIGHTNESS_TT) + bt_mesh_model_msg_init(root_models[13].pub->msg, + BT_MESH_MODEL_OP_2(0x82, 0x4D)); + net_buf_simple_add_le16(root_models[13].pub->msg, LEVEL_U25); + net_buf_simple_add_u8(root_models[13].pub->msg, tid_level++); + net_buf_simple_add_u8(root_models[13].pub->msg, 0x45); + net_buf_simple_add_u8(root_models[13].pub->msg, 0x28); + err = bt_mesh_model_publish(&root_models[13]); +#elif defined(LIGHT_CTL) + bt_mesh_model_msg_init(root_models[16].pub->msg, + BT_MESH_MODEL_OP_2(0x82, 0x5F)); + /* Lightness */ + net_buf_simple_add_le16(root_models[16].pub->msg, LEVEL_U25); + /* Temperature (value should be from 0x0320 to 0x4E20 */ + /* This is as per 6.1.3.1 in Mesh Model Specification */ + net_buf_simple_add_le16(root_models[16].pub->msg, 0x0320); + /* Delta UV */ + net_buf_simple_add_le16(root_models[16].pub->msg, 0x0000); + net_buf_simple_add_u8(root_models[16].pub->msg, tid_level++); + err = bt_mesh_model_publish(&root_models[16]); +#elif defined(LIGHT_CTL_TT) + bt_mesh_model_msg_init(root_models[16].pub->msg, + BT_MESH_MODEL_OP_2(0x82, 0x5F)); + /* Lightness */ + net_buf_simple_add_le16(root_models[16].pub->msg, LEVEL_U25); + /* Temperature (value should be from 0x0320 to 0x4E20 */ + /* This is as per 6.1.3.1 in Mesh Model Specification */ + net_buf_simple_add_le16(root_models[16].pub->msg, 0x0320); + /* Delta UV */ + net_buf_simple_add_le16(root_models[16].pub->msg, 0x0000); + net_buf_simple_add_u8(root_models[16].pub->msg, tid_level++); + net_buf_simple_add_u8(root_models[16].pub->msg, 0x45); + net_buf_simple_add_u8(root_models[16].pub->msg, 0x00); + err = bt_mesh_model_publish(&root_models[16]); +#elif defined(LIGHT_CTL_TEMP) + bt_mesh_model_msg_init(root_models[16].pub->msg, + BT_MESH_MODEL_OP_2(0x82, 0x65)); + /* Temperature (value should be from 0x0320 to 0x4E20 */ + /* This is as per 6.1.3.1 in Mesh Model Specification */ + net_buf_simple_add_le16(root_models[16].pub->msg, 0x0320); + /* Delta UV */ + net_buf_simple_add_le16(root_models[16].pub->msg, 0x0000); + net_buf_simple_add_u8(root_models[16].pub->msg, tid_level++); + err = bt_mesh_model_publish(&root_models[16]); +#endif + + } else if (button_read(button_device[3]) == 0) { +#if defined(GENERIC_LEVEL) + bt_mesh_model_msg_init(root_models[5].pub->msg, + BT_MESH_MODEL_OP_GEN_LEVEL_SET_UNACK); + net_buf_simple_add_le16(root_models[5].pub->msg, LEVEL_S100); + net_buf_simple_add_u8(root_models[5].pub->msg, tid_level++); + err = bt_mesh_model_publish(&root_models[5]); +#elif defined(GENERIC_DELTA_LEVEL) + bt_mesh_model_msg_init(root_models[5].pub->msg, + BT_MESH_MODEL_OP_GEN_DELTA_SET_UNACK); + net_buf_simple_add_le32(root_models[5].pub->msg, -100); + net_buf_simple_add_u8(root_models[5].pub->msg, tid_level++); + err = bt_mesh_model_publish(&root_models[5]); +#elif defined(GENERIC_MOVE_LEVEL_TT) + bt_mesh_model_msg_init(root_models[5].pub->msg, + BT_MESH_MODEL_OP_GEN_MOVE_SET_UNACK); + net_buf_simple_add_le16(root_models[5].pub->msg, -13100); + net_buf_simple_add_u8(root_models[5].pub->msg, tid_level++); + net_buf_simple_add_u8(root_models[5].pub->msg, 0x45); + net_buf_simple_add_u8(root_models[5].pub->msg, 0x00); + err = bt_mesh_model_publish(&root_models[5]); +#elif defined(LIGHT_LIGHTNESS_TT) + bt_mesh_model_msg_init(root_models[13].pub->msg, + BT_MESH_MODEL_OP_2(0x82, 0x4D)); + net_buf_simple_add_le16(root_models[13].pub->msg, LEVEL_U100); + net_buf_simple_add_u8(root_models[13].pub->msg, tid_level++); + net_buf_simple_add_u8(root_models[13].pub->msg, 0x45); + net_buf_simple_add_u8(root_models[13].pub->msg, 0x28); + err = bt_mesh_model_publish(&root_models[13]); +#elif defined(LIGHT_CTL) + bt_mesh_model_msg_init(root_models[16].pub->msg, + BT_MESH_MODEL_OP_2(0x82, 0x5F)); + /* Lightness */ + net_buf_simple_add_le16(root_models[16].pub->msg, LEVEL_U100); + /* Temperature (value should be from 0x0320 to 0x4E20 */ + /* This is as per 6.1.3.1 in Mesh Model Specification */ + net_buf_simple_add_le16(root_models[16].pub->msg, 0x4E20); + /* Delta UV */ + net_buf_simple_add_le16(root_models[16].pub->msg, 0x0000); + net_buf_simple_add_u8(root_models[16].pub->msg, tid_level++); + err = bt_mesh_model_publish(&root_models[16]); +#elif defined(LIGHT_CTL_TT) + bt_mesh_model_msg_init(root_models[16].pub->msg, + BT_MESH_MODEL_OP_2(0x82, 0x5F)); + /* Lightness */ + net_buf_simple_add_le16(root_models[16].pub->msg, LEVEL_U100); + /* Temperature (value should be from 0x0320 to 0x4E20 */ + /* This is as per 6.1.3.1 in Mesh Model Specification */ + net_buf_simple_add_le16(root_models[16].pub->msg, 0x4E20); + /* Delta UV */ + net_buf_simple_add_le16(root_models[16].pub->msg, 0x0000); + net_buf_simple_add_u8(root_models[16].pub->msg, tid_level++); + net_buf_simple_add_u8(root_models[16].pub->msg, 0x45); + net_buf_simple_add_u8(root_models[16].pub->msg, 0x00); + err = bt_mesh_model_publish(&root_models[16]); +#elif defined(LIGHT_CTL_TEMP) + bt_mesh_model_msg_init(root_models[16].pub->msg, + BT_MESH_MODEL_OP_2(0x82, 0x65)); + /* Temperature (value should be from 0x0320 to 0x4E20 */ + /* This is as per 6.1.3.1 in Mesh Model Specification */ + net_buf_simple_add_le16(root_models[16].pub->msg, 0x4E20); + /* Delta UV */ + net_buf_simple_add_le16(root_models[16].pub->msg, 0x0000); + net_buf_simple_add_u8(root_models[16].pub->msg, tid_level++); + err = bt_mesh_model_publish(&root_models[16]); +#endif + } + + if (err) { + printk("bt_mesh_model_publish: err: %d\n", err); + } +} + diff --git a/lib/bt/host/nimble/nimble/apps/blemesh_models_example_2/src/publisher.h b/lib/bt/host/nimble/nimble/apps/blemesh_models_example_2/src/publisher.h new file mode 100644 index 00000000..09b740b4 --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/blemesh_models_example_2/src/publisher.h @@ -0,0 +1,46 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* Bluetooth: Mesh Generic OnOff, Generic Level, Lighting & Vendor Models + * + * Copyright (c) 2018 Vikrant More + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef _PUBLISHER_H +#define _PUBLISHER_H + +/* Others */ +#define LEVEL_S0 -32768 +#define LEVEL_S25 -16384 +#define LEVEL_S50 0 +#define LEVEL_S75 16384 +#define LEVEL_S100 32767 + +#define LEVEL_U0 0 +#define LEVEL_U25 16384 +#define LEVEL_U50 32768 +#define LEVEL_U75 49152 +#define LEVEL_U100 65535 + +void randomize_publishers_TID(void); +void publish(struct os_event *work); + +#endif diff --git a/lib/bt/host/nimble/nimble/apps/blemesh_models_example_2/src/state_binding.c b/lib/bt/host/nimble/nimble/apps/blemesh_models_example_2/src/state_binding.c new file mode 100644 index 00000000..0778f787 --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/blemesh_models_example_2/src/state_binding.c @@ -0,0 +1,308 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* Bluetooth: Mesh Generic OnOff, Generic Level, Lighting & Vendor Models + * + * Copyright (c) 2018 Vikrant More + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include "ble_mesh.h" +#include "device_composition.h" +#include "state_binding.h" +#include "transition.h" + + +uint16_t lightness, target_lightness; +int16_t temperature, target_temperature; + +static int32_t ceiling(float num) +{ + int32_t inum; + + inum = (int32_t) num; + if (num == (float) inum) { + return inum; + } + + return inum + 1; +} + +uint16_t actual_to_linear(uint16_t val) +{ + float tmp; + + tmp = ((float) val / 65535); + + return (uint16_t) ceiling(65535 * tmp * tmp); +} + +uint16_t linear_to_actual(uint16_t val) +{ + return (uint16_t) (65535 * sqrt(((float) val / 65535))); +} + +static void constrain_lightness(uint16_t var) +{ + if (var > 0 && var < light_lightness_srv_user_data.light_range_min) { + var = light_lightness_srv_user_data.light_range_min; + } else if (var > light_lightness_srv_user_data.light_range_max) { + var = light_lightness_srv_user_data.light_range_max; + } + + lightness = var; +} + +static void constrain_lightness2(uint16_t var) +{ + /* This is as per Mesh Model Specification 3.3.2.2.3 */ + if (var > 0 && var < light_lightness_srv_user_data.light_range_min) { + if (gen_level_srv_root_user_data.last_delta < 0) { + var = 0U; + } else { + var = light_lightness_srv_user_data.light_range_min; + } + } else if (var > light_lightness_srv_user_data.light_range_max) { + var = light_lightness_srv_user_data.light_range_max; + } + + lightness = var; +} + +static void constrain_target_lightness(uint16_t var) +{ + if (var > 0 && + var < light_lightness_srv_user_data.light_range_min) { + var = light_lightness_srv_user_data.light_range_min; + } else if (var > light_lightness_srv_user_data.light_range_max) { + var = light_lightness_srv_user_data.light_range_max; + } + + target_lightness = var; +} + +static int16_t light_ctl_temp_to_level(uint16_t temp) +{ + float tmp; + + /* Mesh Model Specification 6.1.3.1.1 2nd formula start */ + + tmp = (temp - light_ctl_srv_user_data.temp_range_min) * 65535; + + tmp = tmp / (light_ctl_srv_user_data.temp_range_max - + light_ctl_srv_user_data.temp_range_min); + + return (int16_t) (tmp - 32768); + + /* 6.1.3.1.1 2nd formula end */ +} + +static uint16_t level_to_light_ctl_temp(int16_t level) +{ + uint16_t tmp; + float diff; + + /* Mesh Model Specification 6.1.3.1.1 1st formula start */ + diff = (float) (light_ctl_srv_user_data.temp_range_max - + light_ctl_srv_user_data.temp_range_min) / 65535; + + + tmp = (uint16_t) ((level + 32768) * diff); + + return (light_ctl_srv_user_data.temp_range_min + tmp); + + /* 6.1.3.1.1 1st formula end */ +} + +void state_binding(uint8_t light, uint8_t temp) +{ + switch (temp) { + case ONOFF_TEMP: + case CTL_TEMP: + temperature = + light_ctl_temp_to_level(light_ctl_srv_user_data.temp); + + gen_level_srv_s0_user_data.level = temperature; + break; + case LEVEL_TEMP: + temperature = gen_level_srv_s0_user_data.level; + light_ctl_srv_user_data.temp = + level_to_light_ctl_temp(temperature); + break; + default: + break; + } + + switch (light) { + case ONPOWERUP: + if (gen_onoff_srv_root_user_data.onoff == STATE_OFF) { + lightness = 0U; + } else if (gen_onoff_srv_root_user_data.onoff == STATE_ON) { + lightness = light_lightness_srv_user_data.last; + } + break; + case ONOFF: + if (gen_onoff_srv_root_user_data.onoff == STATE_OFF) { + lightness = 0U; + } else if (gen_onoff_srv_root_user_data.onoff == STATE_ON) { + if (light_lightness_srv_user_data.def == 0) { + lightness = light_lightness_srv_user_data.last; + } else { + lightness = light_lightness_srv_user_data.def; + } + } + break; + case LEVEL: + lightness = gen_level_srv_root_user_data.level + 32768; + break; + case DELTA_LEVEL: + lightness = gen_level_srv_root_user_data.level + 32768; + constrain_lightness2(lightness); + goto jump; + case ACTUAL: + lightness = light_lightness_srv_user_data.actual; + break; + case LINEAR: + lightness = + linear_to_actual(light_lightness_srv_user_data.linear); + break; + case CTL: + lightness = light_ctl_srv_user_data.lightness; + break; + default: + break; + } + + constrain_lightness(lightness); + +jump: + if (lightness != 0) { + light_lightness_srv_user_data.last = lightness; + } + + if (lightness) { + gen_onoff_srv_root_user_data.onoff = STATE_ON; + } else { + gen_onoff_srv_root_user_data.onoff = STATE_OFF; + } + + gen_level_srv_root_user_data.level = lightness - 32768; + light_lightness_srv_user_data.actual = lightness; + light_lightness_srv_user_data.linear = actual_to_linear(lightness); + light_ctl_srv_user_data.lightness = lightness; +} + +void calculate_lightness_target_values(uint8_t type) +{ + bool set_light_ctl_temp_target_value; + uint16_t tmp; + + set_light_ctl_temp_target_value = true; + + switch (type) { + case ONOFF: + if (gen_onoff_srv_root_user_data.target_onoff == 0) { + tmp = 0U; + } else { + if (light_lightness_srv_user_data.def == 0) { + tmp = light_lightness_srv_user_data.last; + } else { + tmp = light_lightness_srv_user_data.def; + } + } + break; + case LEVEL: + tmp = gen_level_srv_root_user_data.target_level + 32768; + break; + case ACTUAL: + tmp = light_lightness_srv_user_data.target_actual; + break; + case LINEAR: + tmp = linear_to_actual(light_lightness_srv_user_data.target_linear); + break; + case CTL: + set_light_ctl_temp_target_value = false; + + tmp = light_ctl_srv_user_data.target_lightness; + + target_temperature = light_ctl_temp_to_level(light_ctl_srv_user_data.target_temp); + gen_level_srv_s0_user_data.target_level = target_temperature; + break; + default: + return; + } + + constrain_target_lightness(tmp); + + if (target_lightness) { + gen_onoff_srv_root_user_data.target_onoff = STATE_ON; + } else { + gen_onoff_srv_root_user_data.target_onoff = STATE_OFF; + } + + gen_level_srv_root_user_data.target_level = target_lightness - 32768; + + light_lightness_srv_user_data.target_actual = target_lightness; + + light_lightness_srv_user_data.target_linear = + actual_to_linear(target_lightness); + + light_ctl_srv_user_data.target_lightness = target_lightness; + + if (set_light_ctl_temp_target_value) { + target_temperature = light_ctl_srv_user_data.temp; + light_ctl_srv_user_data.target_temp = target_temperature; + } +} + +void calculate_temp_target_values(uint8_t type) +{ + bool set_light_ctl_delta_uv_target_value; + + set_light_ctl_delta_uv_target_value = true; + + switch (type) { + case LEVEL_TEMP: + target_temperature = gen_level_srv_s0_user_data.target_level; + light_ctl_srv_user_data.target_temp = + level_to_light_ctl_temp(target_temperature); + break; + case CTL_TEMP: + set_light_ctl_delta_uv_target_value = false; + + target_temperature = light_ctl_temp_to_level(light_ctl_srv_user_data.target_temp); + gen_level_srv_s0_user_data.target_level = target_temperature; + break; + default: + return; + } + + target_lightness = light_ctl_srv_user_data.lightness; + light_ctl_srv_user_data.target_lightness = target_lightness; + + if (set_light_ctl_delta_uv_target_value) { + + light_ctl_srv_user_data.target_delta_uv = + light_ctl_srv_user_data.delta_uv; + } +} + diff --git a/lib/bt/host/nimble/nimble/apps/blemesh_models_example_2/src/state_binding.h b/lib/bt/host/nimble/nimble/apps/blemesh_models_example_2/src/state_binding.h new file mode 100644 index 00000000..0498e5c6 --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/blemesh_models_example_2/src/state_binding.h @@ -0,0 +1,53 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* Bluetooth: Mesh Generic OnOff, Generic Level, Lighting & Vendor Models + * + * Copyright (c) 2018 Vikrant More + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef _STATE_BINDING_H +#define _STATE_BINDING_H + +enum state_binding { + ONPOWERUP = 0x01, + ONOFF, + LEVEL, + DELTA_LEVEL, + ACTUAL, + LINEAR, + CTL, + IGNORE, + + ONOFF_TEMP, + LEVEL_TEMP, + CTL_TEMP, + IGNORE_TEMP +}; + +extern uint16_t lightness, target_lightness; +extern int16_t temperature, target_temperature; + +void state_binding(uint8_t lightness, uint8_t temperature); +void calculate_lightness_target_values(uint8_t type); +void calculate_temp_target_values(uint8_t type); + +#endif diff --git a/lib/bt/host/nimble/nimble/apps/blemesh_models_example_2/src/storage.c b/lib/bt/host/nimble/nimble/apps/blemesh_models_example_2/src/storage.c new file mode 100644 index 00000000..d0ef980a --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/blemesh_models_example_2/src/storage.c @@ -0,0 +1,255 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* Bluetooth: Mesh Generic OnOff, Generic Level, Lighting & Vendor Models + * + * Copyright (c) 2018 Vikrant More + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "base64/base64.h" +#include "console/console.h" +#include "mesh/mesh.h" + +#include "ble_mesh.h" +#include "device_composition.h" +#include "storage.h" + +static uint8_t storage_id; +uint8_t reset_counter; + +static void save_reset_counter(void) +{ + char buf[5]; + + settings_str_from_bytes(&reset_counter, sizeof(reset_counter), buf, + sizeof(buf)); + + settings_save_one("ps/rc", buf); +} + +static void save_gen_def_trans_time_state(void) +{ + char buf[5]; + + settings_str_from_bytes(&gen_def_trans_time_srv_user_data.tt, + sizeof(gen_def_trans_time_srv_user_data.tt), + buf, sizeof(buf)); + + settings_save_one("ps/gdtt", buf); +} + +static void save_gen_onpowerup_state(void) +{ + char buf[5]; + + settings_str_from_bytes(&gen_power_onoff_srv_user_data.onpowerup, + sizeof(gen_power_onoff_srv_user_data.onpowerup), + buf, sizeof(buf)); + + settings_save_one("ps/gpo", buf); + + if (gen_power_onoff_srv_user_data.onpowerup == 0x02) { + save_on_flash(LIGHTNESS_TEMP_LAST_STATE); + } +} + +static void save_lightness_temp_def_state(void) +{ + char buf[12]; + + light_ctl_srv_user_data.lightness_temp_def = + (uint32_t) ((light_ctl_srv_user_data.lightness_def << 16) | + light_ctl_srv_user_data.temp_def); + + settings_str_from_bytes(&light_ctl_srv_user_data.lightness_temp_def, + sizeof(light_ctl_srv_user_data.lightness_temp_def), + buf, sizeof(buf)); + + settings_save_one("ps/ltd", buf); +} + +static void save_lightness_temp_last_state(void) +{ + char buf[12]; + + light_ctl_srv_user_data.lightness_temp_last = + (uint32_t) ((light_ctl_srv_user_data.lightness << 16) | + light_ctl_srv_user_data.temp); + + settings_str_from_bytes(&light_ctl_srv_user_data.lightness_temp_last, + sizeof(light_ctl_srv_user_data.lightness_temp_last), + buf, sizeof(buf)); + + settings_save_one("ps/ltl", buf); + + printk("Light CTL Last values have beed saved !!\n"); +} + +static void save_lightness_range(void) +{ + char buf[12]; + + light_lightness_srv_user_data.lightness_range = + (uint32_t) ((light_lightness_srv_user_data.light_range_max << 16) | + light_lightness_srv_user_data.light_range_min); + + settings_str_from_bytes(&light_lightness_srv_user_data.lightness_range, + sizeof(light_lightness_srv_user_data.lightness_range), + buf, sizeof(buf)); + + settings_save_one("ps/lr", buf); +} + +static void save_temperature_range(void) +{ + char buf[12]; + + light_ctl_srv_user_data.temperature_range = + (uint32_t) ((light_ctl_srv_user_data.temp_range_max << 16) | + light_ctl_srv_user_data.temp_range_min); + + settings_str_from_bytes(&light_ctl_srv_user_data.temperature_range, + sizeof(light_ctl_srv_user_data.temperature_range), + buf, sizeof(buf)); + + settings_save_one("ps/tr", buf); +} + +static void storage_work_handler(struct os_event *work) +{ + switch (storage_id) { + case RESET_COUNTER: + save_reset_counter(); + break; + case GEN_DEF_TRANS_TIME_STATE: + save_gen_def_trans_time_state(); + break; + case GEN_ONPOWERUP_STATE: + save_gen_onpowerup_state(); + break; + case LIGHTNESS_TEMP_DEF_STATE: + save_lightness_temp_def_state(); + break; + case LIGHTNESS_TEMP_LAST_STATE: + save_lightness_temp_last_state(); + break; + case LIGHTNESS_RANGE: + save_lightness_range(); + break; + case TEMPERATURE_RANGE: + save_temperature_range(); + break; + } +} + +struct os_callout storage_work; + +void save_on_flash(uint8_t id) +{ + storage_id = id; + os_callout_reset(&storage_work, 0); +} + +static int ps_set(int argc, char **argv, char *val) +{ + int len; + + if (argc == 1) { + if (!strcmp(argv[0], "rc")) { + len = sizeof(reset_counter); + + return settings_bytes_from_str(val, &reset_counter, + &len); + } + + if (!strcmp(argv[0], "gdtt")) { + len = sizeof(gen_def_trans_time_srv_user_data.tt); + + return settings_bytes_from_str(val, + &gen_def_trans_time_srv_user_data.tt, &len); + } + + if (!strcmp(argv[0], "gpo")) { + len = sizeof(gen_power_onoff_srv_user_data.onpowerup); + + return settings_bytes_from_str(val, + &gen_power_onoff_srv_user_data.onpowerup, &len); + } + + if (!strcmp(argv[0], "ltd")) { + len = sizeof(light_ctl_srv_user_data.lightness_temp_def); + + return settings_bytes_from_str(val, + &light_ctl_srv_user_data.lightness_temp_def, + &len); + } + + if (!strcmp(argv[0], "ltl")) { + len = sizeof(light_ctl_srv_user_data. + lightness_temp_last); + + return settings_bytes_from_str(val, + &light_ctl_srv_user_data.lightness_temp_last, + &len); + } + + if (!strcmp(argv[0], "lr")) { + len = sizeof(light_lightness_srv_user_data. + lightness_range); + + return settings_bytes_from_str(val, + &light_lightness_srv_user_data.lightness_range, + &len); + } + + if (!strcmp(argv[0], "tr")) { + len = sizeof(light_ctl_srv_user_data. + temperature_range); + + return settings_bytes_from_str(val, + &light_ctl_srv_user_data. temperature_range, + &len); + } + } + + return -ENOENT; +} + +static struct conf_handler ps_settings = { + .ch_name = "ps", + .ch_set = ps_set, +}; + +int ps_settings_init(void) +{ + int err; + + os_callout_init(&storage_work, os_eventq_dflt_get(), + storage_work_handler, NULL); + + err = conf_register(&ps_settings); + if (err) { + printk("ps_settings_register failed (err %d)", err); + return err; + } + + return 0; +} diff --git a/lib/bt/host/nimble/nimble/apps/blemesh_models_example_2/src/storage.h b/lib/bt/host/nimble/nimble/apps/blemesh_models_example_2/src/storage.h new file mode 100644 index 00000000..5e636f62 --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/blemesh_models_example_2/src/storage.h @@ -0,0 +1,47 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* Bluetooth: Mesh Generic OnOff, Generic Level, Lighting & Vendor Models + * + * Copyright (c) 2018 Vikrant More + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef _STORAGE_H +#define _STORAGE_H + +enum ps_variables_id { + RESET_COUNTER = 0x01, + GEN_DEF_TRANS_TIME_STATE, + GEN_ONPOWERUP_STATE, + LIGHTNESS_TEMP_DEF_STATE, + LIGHTNESS_TEMP_LAST_STATE, + LIGHTNESS_RANGE, + TEMPERATURE_RANGE +}; + +extern uint8_t reset_counter; + +extern struct os_callout storage_work; + +int ps_settings_init(void); +void save_on_flash(uint8_t id); + +#endif diff --git a/lib/bt/host/nimble/nimble/apps/blemesh_models_example_2/src/transition.c b/lib/bt/host/nimble/nimble/apps/blemesh_models_example_2/src/transition.c new file mode 100644 index 00000000..e31af75d --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/blemesh_models_example_2/src/transition.c @@ -0,0 +1,792 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* Bluetooth: Mesh Generic OnOff, Generic Level, Lighting & Vendor Models + * + * Copyright (c) 2018 Vikrant More + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "ble_mesh.h" +#include "common.h" +#include "device_composition.h" +#include "state_binding.h" +#include "transition.h" + +struct os_callout onoff_work; +struct os_callout level_lightness_work; +struct os_callout level_temp_work; +struct os_callout light_lightness_actual_work; +struct os_callout light_lightness_linear_work; +struct os_callout light_ctl_work; +struct os_callout light_ctl_temp_work; + +struct os_callout dummy_timer; + +uint8_t transition_type, default_tt; +uint32_t *ptr_counter; +struct os_callout *ptr_timer = &dummy_timer; + +struct transition lightness_transition, temp_transition; + +/* Function to calculate Remaining Time (Start) */ + +void calculate_rt(struct transition *transition) +{ + uint8_t steps, resolution; + int32_t duration_remainder; + int64_t now; + + if (transition->just_started) { + transition->rt = transition->tt; + } else { + now = k_uptime_get(); + duration_remainder = transition->total_duration - + (now - transition->start_timestamp); + + if (duration_remainder > 620000) { + /* > 620 seconds -> resolution = 0b11 [10 minutes] */ + resolution = 0x03; + steps = duration_remainder / 600000; + } else if (duration_remainder > 62000) { + /* > 62 seconds -> resolution = 0b10 [10 seconds] */ + resolution = 0x02; + steps = duration_remainder / 10000; + } else if (duration_remainder > 6200) { + /* > 6.2 seconds -> resolution = 0b01 [1 seconds] */ + resolution = 0x01; + steps = duration_remainder / 1000; + } else if (duration_remainder > 0) { + /* <= 6.2 seconds -> resolution = 0b00 [100 ms] */ + resolution = 0x00; + steps = duration_remainder / 100; + } else { + resolution = 0x00; + steps = 0x00; + } + + transition->rt = (resolution << 6) | steps; + } +} + +/* Function to calculate Remaining Time (End) */ + +static void bound_states_transition_type_reassignment(uint8_t type) +{ + switch (type) { + case ONOFF: + case LEVEL: + case ACTUAL: + case LINEAR: + light_ctl_srv_user_data.transition = &lightness_transition; + break; + case CTL: + light_ctl_srv_user_data.transition = &lightness_transition; + gen_level_srv_s0_user_data.transition = &lightness_transition; + break; + case LEVEL_TEMP: + case CTL_TEMP: + gen_level_srv_s0_user_data.transition = &temp_transition; + light_ctl_srv_user_data.transition = &temp_transition; + break; + default: + break; + } +} + +static void tt_values_calculator(struct transition *transition) +{ + uint8_t steps_multiplier, resolution; + + resolution = (transition->tt >> 6); + steps_multiplier = (transition->tt & 0x3F); + + switch (resolution) { + case 0: /* 100ms */ + transition->total_duration = steps_multiplier * 100; + break; + case 1: /* 1 second */ + transition->total_duration = steps_multiplier * 1000; + break; + case 2: /* 10 seconds */ + transition->total_duration = steps_multiplier * 10000; + break; + case 3: /* 10 minutes */ + transition->total_duration = steps_multiplier * 600000; + break; + } + + transition->counter = ((float) transition->total_duration / 100); + + if (transition->counter > DEVICE_SPECIFIC_RESOLUTION) { + transition->counter = DEVICE_SPECIFIC_RESOLUTION; + } + + ptr_counter = &transition->counter; +} + +void onoff_tt_values(struct generic_onoff_state *state, uint8_t tt, uint8_t delay) +{ + bound_states_transition_type_reassignment(ONOFF); + calculate_lightness_target_values(ONOFF); + state->transition->tt = tt; + state->transition->delay = delay; + + if (tt != 0) { + tt_values_calculator(state->transition); + } else { + return; + } + + state->transition->quo_tt = state->transition->total_duration / + state->transition->counter; + + state->tt_delta = ((float) (lightness - target_lightness) / + state->transition->counter); +} + +void level_tt_values(struct generic_level_state *state, uint8_t tt, uint8_t delay) +{ + if (state == &gen_level_srv_root_user_data) { + bound_states_transition_type_reassignment(LEVEL); + calculate_lightness_target_values(LEVEL); + } else if (state == &gen_level_srv_s0_user_data) { + bound_states_transition_type_reassignment(LEVEL_TEMP); + calculate_temp_target_values(LEVEL_TEMP); + } + state->transition->tt = tt; + state->transition->delay = delay; + + if (tt != 0) { + tt_values_calculator(state->transition); + } else { + return; + } + + state->transition->quo_tt = state->transition->total_duration / + state->transition->counter; + + state->tt_delta = ((float) (state->level - state->target_level) / + state->transition->counter); +} + +void light_lightness_actual_tt_values(struct light_lightness_state *state, + uint8_t tt, uint8_t delay) +{ + bound_states_transition_type_reassignment(ACTUAL); + calculate_lightness_target_values(ACTUAL); + state->transition->tt = tt; + state->transition->delay = delay; + + if (tt != 0) { + tt_values_calculator(state->transition); + } else { + return; + } + + state->transition->quo_tt = state->transition->total_duration / + state->transition->counter; + + state->tt_delta_actual = + ((float) (state->actual - state->target_actual) / + state->transition->counter); +} + +void light_lightness_linear_tt_values(struct light_lightness_state *state, + uint8_t tt, uint8_t delay) +{ + bound_states_transition_type_reassignment(LINEAR); + calculate_lightness_target_values(LINEAR); + state->transition->tt = tt; + state->transition->delay = delay; + + if (tt != 0) { + tt_values_calculator(state->transition); + } else { + return; + } + + state->transition->quo_tt = state->transition->total_duration / + state->transition->counter; + + state->tt_delta_linear = + ((float) (state->linear - state->target_linear) / + state->transition->counter); +} + +void light_ctl_tt_values(struct light_ctl_state *state, uint8_t tt, uint8_t delay) +{ + bound_states_transition_type_reassignment(CTL); + calculate_lightness_target_values(CTL); + state->transition->tt = tt; + state->transition->delay = delay; + + if (tt != 0) { + tt_values_calculator(state->transition); + } else { + return; + } + + state->transition->quo_tt = state->transition->total_duration / + state->transition->counter; + + state->tt_delta_lightness = + ((float) (state->lightness - state->target_lightness) / + state->transition->counter); + + state->tt_delta_temp = + ((float) (state->temp - state->target_temp) / + state->transition->counter); + + state->tt_delta_duv = + ((float) (state->delta_uv - state->target_delta_uv) / + state->transition->counter); +} + +void light_ctl_temp_tt_values(struct light_ctl_state *state, + uint8_t tt, uint8_t delay) +{ + bound_states_transition_type_reassignment(CTL_TEMP); + calculate_temp_target_values(CTL_TEMP); + state->transition->tt = tt; + state->transition->delay = delay; + + if (tt != 0) { + tt_values_calculator(state->transition); + } else { + return; + } + + state->transition->quo_tt = state->transition->total_duration / + state->transition->counter; + + state->tt_delta_temp = ((float) (state->temp - state->target_temp) / + state->transition->counter); + + state->tt_delta_duv = + ((float) (state->delta_uv - state->target_delta_uv) / + state->transition->counter); +} + +/* Timers related handlers & threads (Start) */ +static void onoff_work_handler(struct os_event *work) +{ + struct generic_onoff_state *state = &gen_onoff_srv_root_user_data; + + if (state->transition->just_started) { + state->transition->just_started = false; + + if (state->transition->counter == 0) { + state_binding(ONOFF, IGNORE_TEMP); + update_light_state(); + + os_callout_stop(ptr_timer); + } else { + state->transition->start_timestamp = k_uptime_get(); + + if (state->target_onoff == STATE_ON) { + state->onoff = STATE_ON; + } + } + + return; + } + + if (state->transition->counter != 0) { + state->transition->counter--; + + lightness -= state->tt_delta; + + state_binding(IGNORE, IGNORE_TEMP); + update_light_state(); + } + + if (state->transition->counter == 0) { + state->onoff = state->target_onoff; + lightness = target_lightness; + + state_binding(IGNORE, IGNORE_TEMP); + update_light_state(); + + os_callout_stop(ptr_timer); + } +} + +static void level_lightness_work_handler(struct os_event *work) +{ + uint8_t level; + struct generic_level_state *state = &gen_level_srv_root_user_data; + + switch (transition_type) { + case LEVEL_TT: + level = LEVEL; + break; + case LEVEL_TT_DELTA: + level = DELTA_LEVEL; + break; + case LEVEL_TT_MOVE: + level = LEVEL; + break; + default: + return; + } + + if (state->transition->just_started) { + state->transition->just_started = false; + + if (state->transition->counter == 0) { + state_binding(level, IGNORE_TEMP); + update_light_state(); + + os_callout_stop(ptr_timer); + } else { + state->transition->start_timestamp = k_uptime_get(); + } + + return; + } + + if (state->transition->counter != 0) { + state->transition->counter--; + + state->level -= state->tt_delta; + + state_binding(level, IGNORE_TEMP); + update_light_state(); + } + + if (state->transition->counter == 0) { + state->level = state->target_level; + + state_binding(level, IGNORE_TEMP); + update_light_state(); + + os_callout_stop(ptr_timer); + } +} + +static void level_temp_work_handler(struct os_event *work) +{ + struct generic_level_state *state = &gen_level_srv_s0_user_data; + + switch (transition_type) { + case LEVEL_TEMP_TT: + break; + case LEVEL_TEMP_TT_DELTA: + break; + case LEVEL_TEMP_TT_MOVE: + break; + default: + return; + } + + if (state->transition->just_started) { + state->transition->just_started = false; + + if (state->transition->counter == 0) { + state_binding(IGNORE, LEVEL_TEMP); + update_light_state(); + + os_callout_stop(ptr_timer); + } else { + state->transition->start_timestamp = k_uptime_get(); + } + + return; + } + + if (state->transition->counter != 0) { + state->transition->counter--; + + state->level -= state->tt_delta; + + state_binding(IGNORE, LEVEL_TEMP); + update_light_state(); + } + + if (state->transition->counter == 0) { + state->level = state->target_level; + + state_binding(IGNORE, LEVEL_TEMP); + update_light_state(); + + os_callout_stop(ptr_timer); + } +} + +static void light_lightness_actual_work_handler(struct os_event *work) +{ + struct light_lightness_state *state = &light_lightness_srv_user_data; + + if (state->transition->just_started) { + state->transition->just_started = false; + + if (state->transition->counter == 0) { + state_binding(ACTUAL, IGNORE_TEMP); + update_light_state(); + + os_callout_stop(ptr_timer); + } else { + state->transition->start_timestamp = k_uptime_get(); + } + + return; + } + + if (state->transition->counter != 0) { + state->transition->counter--; + + state->actual -= state->tt_delta_actual; + + state_binding(ACTUAL, IGNORE_TEMP); + update_light_state(); + } + + if (state->transition->counter == 0) { + state->actual = state->target_actual; + + state_binding(ACTUAL, IGNORE_TEMP); + update_light_state(); + + os_callout_stop(ptr_timer); + } +} + +static void light_lightness_linear_work_handler(struct os_event *work) +{ + struct light_lightness_state *state = &light_lightness_srv_user_data; + + if (state->transition->just_started) { + state->transition->just_started = false; + + if (state->transition->counter == 0) { + state_binding(LINEAR, IGNORE_TEMP); + update_light_state(); + + os_callout_stop(ptr_timer); + } else { + state->transition->start_timestamp = k_uptime_get(); + } + + return; + } + + if (state->transition->counter != 0) { + state->transition->counter--; + + state->linear -= state->tt_delta_linear; + + state_binding(LINEAR, IGNORE_TEMP); + update_light_state(); + } + + if (state->transition->counter == 0) { + state->linear = state->target_linear; + + state_binding(LINEAR, IGNORE_TEMP); + update_light_state(); + + os_callout_stop(ptr_timer); + } +} + +static void light_ctl_work_handler(struct os_event *work) +{ + struct light_ctl_state *state = &light_ctl_srv_user_data; + + if (state->transition->just_started) { + state->transition->just_started = false; + + if (state->transition->counter == 0) { + state_binding(CTL, CTL_TEMP); + update_light_state(); + + os_callout_stop(ptr_timer); + } else { + state->transition->start_timestamp = k_uptime_get(); + } + + return; + } + + if (state->transition->counter != 0) { + state->transition->counter--; + + /* Lightness */ + state->lightness -= state->tt_delta_lightness; + + /* Temperature */ + state->temp -= state->tt_delta_temp; + + /* Delta_UV */ + state->delta_uv -= state->tt_delta_duv; + + state_binding(CTL, CTL_TEMP); + update_light_state(); + } + + if (state->transition->counter == 0) { + state->lightness = state->target_lightness; + state->temp = state->target_temp; + state->delta_uv = state->target_delta_uv; + + state_binding(CTL, CTL_TEMP); + update_light_state(); + + os_callout_stop(ptr_timer); + } +} + +static void light_ctl_temp_work_handler(struct os_event *work) +{ + struct light_ctl_state *state = &light_ctl_srv_user_data; + + if (state->transition->just_started) { + state->transition->just_started = false; + + if (state->transition->counter == 0) { + state_binding(IGNORE, CTL_TEMP); + update_light_state(); + + os_callout_stop(ptr_timer); + } else { + state->transition->start_timestamp = k_uptime_get(); + } + + return; + } + + if (state->transition->counter != 0) { + state->transition->counter--; + + /* Temperature */ + state->temp -= state->tt_delta_temp; + + /* Delta UV */ + state->delta_uv -= state->tt_delta_duv; + + state_binding(IGNORE, CTL_TEMP); + update_light_state(); + } + + if (state->transition->counter == 0) { + state->temp = state->target_temp; + state->delta_uv = state->target_delta_uv; + + state_binding(IGNORE, CTL_TEMP); + update_light_state(); + + os_callout_stop(ptr_timer); + } +} + +static void dummy_timer_handler(struct os_event *ev) +{ } + +static void onoff_tt_handler(struct os_event *ev) +{ + struct generic_onoff_state *state = ev->ev_arg; + + assert(state != NULL); + os_callout_reset(&onoff_work, 0); + os_callout_reset(&state->transition->timer, + os_time_ms_to_ticks32( + K_MSEC(state->transition->quo_tt))); +} + +static void level_lightness_tt_handler(struct os_event *ev) +{ + struct generic_level_state *state = ev->ev_arg; + + assert(state != NULL); + os_callout_reset(&level_lightness_work, 0); + os_callout_reset(&state->transition->timer, + os_time_ms_to_ticks32( + K_MSEC(state->transition->quo_tt))); +} + +static void level_temp_tt_handler(struct os_event *ev) +{ + struct generic_level_state *state = ev->ev_arg; + + assert(state != NULL); + os_callout_reset(&level_temp_work, 0); + os_callout_reset(&state->transition->timer, + os_time_ms_to_ticks32( + K_MSEC(state->transition->quo_tt))); +} + +static void light_lightness_actual_tt_handler(struct os_event *ev) +{ + struct light_lightness_state *state = ev->ev_arg; + + assert(state != NULL); + os_callout_reset(&light_lightness_actual_work, 0); + os_callout_reset(&state->transition->timer, + os_time_ms_to_ticks32( + K_MSEC(state->transition->quo_tt))); +} + +static void light_lightness_linear_tt_handler(struct os_event *ev) +{ + struct light_lightness_state *state = ev->ev_arg; + + assert(state != NULL); + os_callout_reset(&light_lightness_linear_work, 0); + os_callout_reset(&state->transition->timer, + os_time_ms_to_ticks32( + K_MSEC(state->transition->quo_tt))); +} + +static void light_ctl_tt_handler(struct os_event *ev) +{ + struct light_ctl_state *state = ev->ev_arg; + + assert(state != NULL); + os_callout_reset(&light_ctl_work, 0); + os_callout_reset(&state->transition->timer, + os_time_ms_to_ticks32( + K_MSEC(state->transition->quo_tt))); +} + +static void light_ctl_temp_tt_handler(struct os_event *ev) +{ + struct light_ctl_state *state = ev->ev_arg; + + assert(state != NULL); + os_callout_reset(&light_ctl_temp_work, 0); + os_callout_reset(&state->transition->timer, + os_time_ms_to_ticks32( + K_MSEC(state->transition->quo_tt))); +} +/* Timers related handlers & threads (End) */ + +/* Messages handlers (Start) */ +void onoff_handler(struct generic_onoff_state *state) +{ + ptr_timer = &state->transition->timer; + + os_callout_init(ptr_timer, os_eventq_dflt_get(), + onoff_tt_handler, NULL); + ptr_timer->c_ev.ev_arg = state; + os_callout_reset(ptr_timer, + os_time_ms_to_ticks32( + K_MSEC(5 * state->transition->delay))); +} + +void level_lightness_handler(struct generic_level_state *state) +{ + ptr_timer = &state->transition->timer; + + os_callout_init(ptr_timer, os_eventq_dflt_get(), + level_lightness_tt_handler, NULL); + ptr_timer->c_ev.ev_arg = state; + os_callout_reset(ptr_timer, + os_time_ms_to_ticks32( + K_MSEC(5 * state->transition->delay))); +} + +void level_temp_handler(struct generic_level_state *state) +{ + ptr_timer = &state->transition->timer; + + os_callout_init(ptr_timer, os_eventq_dflt_get(), + level_temp_tt_handler, NULL); + ptr_timer->c_ev.ev_arg = state; + os_callout_reset(ptr_timer, + os_time_ms_to_ticks32( + K_MSEC(5 * state->transition->delay))); +} + +void light_lightness_actual_handler(struct light_lightness_state *state) +{ + ptr_timer = &state->transition->timer; + + os_callout_init(ptr_timer, os_eventq_dflt_get(), + light_lightness_actual_tt_handler, NULL); + ptr_timer->c_ev.ev_arg = state; + os_callout_reset(ptr_timer, + os_time_ms_to_ticks32( + K_MSEC(5 * state->transition->delay))); +} + +void light_lightness_linear_handler(struct light_lightness_state *state) +{ + ptr_timer = &state->transition->timer; + + os_callout_init(ptr_timer, os_eventq_dflt_get(), + light_lightness_linear_tt_handler, NULL); + ptr_timer->c_ev.ev_arg = state; + os_callout_reset(ptr_timer, + os_time_ms_to_ticks32( + K_MSEC(5 * state->transition->delay))); +} + +void light_ctl_handler(struct light_ctl_state *state) +{ + ptr_timer = &state->transition->timer; + + os_callout_init(ptr_timer, os_eventq_dflt_get(), + light_ctl_tt_handler, NULL); + ptr_timer->c_ev.ev_arg = state; + os_callout_reset(ptr_timer, + os_time_ms_to_ticks32( + K_MSEC(5 * state->transition->delay))); +} + +void light_ctl_temp_handler(struct light_ctl_state *state) +{ + ptr_timer = &state->transition->timer; + + os_callout_init(ptr_timer, os_eventq_dflt_get(), + light_ctl_temp_tt_handler, NULL); + ptr_timer->c_ev.ev_arg = state; + os_callout_reset(ptr_timer, + os_time_ms_to_ticks32( + K_MSEC(5 * state->transition->delay))); +} +/* Messages handlers (End) */ + +void transition_timers_init(void) +{ + os_callout_init(&onoff_work, os_eventq_dflt_get(), + onoff_work_handler, NULL); + + os_callout_init(&level_lightness_work, os_eventq_dflt_get(), + level_lightness_work_handler, NULL); + os_callout_init(&level_temp_work, os_eventq_dflt_get(), + level_temp_work_handler, NULL); + + os_callout_init(&light_lightness_actual_work, + os_eventq_dflt_get(), + light_lightness_actual_work_handler, NULL); + os_callout_init(&light_lightness_linear_work, + os_eventq_dflt_get(), + light_lightness_linear_work_handler, NULL); + + os_callout_init(&light_ctl_work, os_eventq_dflt_get(), + light_ctl_work_handler, NULL); + os_callout_init(&light_ctl_temp_work, os_eventq_dflt_get(), + light_ctl_temp_work_handler, NULL); + + os_callout_init(&dummy_timer, os_eventq_dflt_get(), + dummy_timer_handler, NULL); +} + diff --git a/lib/bt/host/nimble/nimble/apps/blemesh_models_example_2/src/transition.h b/lib/bt/host/nimble/nimble/apps/blemesh_models_example_2/src/transition.h new file mode 100644 index 00000000..dc3e8da1 --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/blemesh_models_example_2/src/transition.h @@ -0,0 +1,87 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* Bluetooth: Mesh Generic OnOff, Generic Level, Lighting & Vendor Models + * + * Copyright (c) 2018 Vikrant More + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef _TRANSITION_H +#define _TRANSITION_H + +#define UNKNOWN_VALUE 0x3F +#define DEVICE_SPECIFIC_RESOLUTION 10 + +enum level_transition_types { + LEVEL_TT, + LEVEL_TT_DELTA, + LEVEL_TT_MOVE, + + LEVEL_TEMP_TT, + LEVEL_TEMP_TT_DELTA, + LEVEL_TEMP_TT_MOVE, +}; + +struct transition { + bool just_started; + uint8_t tt; + uint8_t rt; + uint8_t delay; + uint32_t quo_tt; + uint32_t counter; + uint32_t total_duration; + int64_t start_timestamp; + + struct os_callout timer; +}; + +extern uint8_t transition_type, default_tt; +extern uint32_t *ptr_counter; +extern struct os_callout *ptr_timer; + +extern struct transition lightness_transition, temp_transition; + +extern struct os_callout dummy_timer; + +void calculate_rt(struct transition *transition); + + +void onoff_tt_values(struct generic_onoff_state *state, uint8_t tt, uint8_t delay); +void level_tt_values(struct generic_level_state *state, uint8_t tt, uint8_t delay); +void light_lightness_actual_tt_values(struct light_lightness_state *state, + uint8_t tt, uint8_t delay); +void light_lightness_linear_tt_values(struct light_lightness_state *state, + uint8_t tt, uint8_t delay); +void light_ctl_tt_values(struct light_ctl_state *state, uint8_t tt, uint8_t delay); +void light_ctl_temp_tt_values(struct light_ctl_state *state, + uint8_t tt, uint8_t delay); + +void onoff_handler(struct generic_onoff_state *state); +void level_lightness_handler(struct generic_level_state *state); +void level_temp_handler(struct generic_level_state *state); +void light_lightness_actual_handler(struct light_lightness_state *state); +void light_lightness_linear_handler(struct light_lightness_state *state); +void light_ctl_handler(struct light_ctl_state *state); +void light_ctl_temp_handler(struct light_ctl_state *state); + +void transition_timers_init(void); + +#endif diff --git a/lib/bt/host/nimble/nimble/apps/blemesh_models_example_2/syscfg.yml b/lib/bt/host/nimble/nimble/apps/blemesh_models_example_2/syscfg.yml new file mode 100644 index 00000000..b56e9d2d --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/blemesh_models_example_2/syscfg.yml @@ -0,0 +1,60 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +syscfg.vals: + # Set log level to info (disable debug logging). + LOG_LEVEL: 1 + + # Default task settings + OS_MAIN_STACK_SIZE: 4096 + + # SMP is not supported in this app, so disable smp-over-shell. + SHELL_MGMT: 0 + + MSYS_1_BLOCK_COUNT: 48 + + FLOAT_USER: 1 + HARD_FLOAT: 1 + + BLE_MESH_DEV_UUID: "((uint8_t[16]){0xdd, 0xdd, 0})" + BLE_MESH_ADV_BUF_COUNT: 60 + BLE_MESH_TX_SEG_MAX: 6 + BLE_MESH_TX_SEG_MSG_COUNT: 3 + BLE_MESH_RX_SEG_MSG_COUNT: 3 + BLE_MESH_CRPL: 128 + BLE_MESH_RPL_STORE_TIMEOUT: 120 + BLE_MESH_MSG_CACHE_SIZE: 100 + + BLE_MESH_SETTINGS: 1 + CONFIG_FCB: 1 + + BLE_MESH: 1 + BLE_MESH_RELAY: 1 + BLE_MESH_LOW_POWER: 0 + BLE_MESH_LPN_AUTO: 0 + BLE_MESH_FRIEND: 0 + + BLE_MESH_PROV: 1 + BLE_MESH_PB_ADV: 1 + BLE_MESH_PB_GATT: 1 + BLE_MESH_GATT_PROXY: 1 + + BLE_MESH_SUBNET_COUNT: 2 + BLE_MESH_APP_KEY_COUNT: 2 + BLE_MESH_MODEL_GROUP_COUNT: 2 + BLE_MESH_LABEL_COUNT: 3 diff --git a/lib/bt/host/nimble/nimble/apps/blemesh_shell/pkg.yml b/lib/bt/host/nimble/nimble/apps/blemesh_shell/pkg.yml new file mode 100644 index 00000000..a7e457ed --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/blemesh_shell/pkg.yml @@ -0,0 +1,35 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +pkg.name: apps/blemesh_shell +pkg.type: app +pkg.description: Sample application for BLE Mesh node with shell support +pkg.author: "Michał Narajowski " +pkg.homepage: "http://mynewt.apache.org/" +pkg.keywords: + +pkg.deps: + - "@apache-mynewt-core/kernel/os" + - "@apache-mynewt-core/sys/console/full" + - "@apache-mynewt-core/sys/log/full" + - "@apache-mynewt-core/sys/log/modlog" + - "@apache-mynewt-core/sys/stats/full" + - "@apache-mynewt-core/sys/shell" + - nimble/host + - nimble/host/services/gap + - nimble/host/services/gatt + - nimble/host/store/config diff --git a/lib/bt/host/nimble/nimble/apps/blemesh_shell/src/main.c b/lib/bt/host/nimble/nimble/apps/blemesh_shell/src/main.c new file mode 100644 index 00000000..fcf80127 --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/blemesh_shell/src/main.c @@ -0,0 +1,114 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#define MESH_LOG_MODULE BLE_MESH_LOG + +#include +#include "os/mynewt.h" +#include "mesh/mesh.h" +#include "console/console.h" +#include "hal/hal_system.h" +#include "hal/hal_gpio.h" +#include "bsp/bsp.h" +#include "shell/shell.h" + +/* BLE */ +#include "nimble/ble.h" +#include "host/ble_hs.h" +#include "services/gap/ble_svc_gap.h" +#include "mesh/glue.h" +#include "mesh/testing.h" + + +void net_recv_ev(uint8_t ttl, uint8_t ctl, uint16_t src, uint16_t dst, + const void *payload, size_t payload_len) +{ + console_printf("Received net packet: ttl 0x%02x ctl 0x%02x src 0x%04x " + "dst 0x%04x " "payload_len %d\n", ttl, ctl, src, dst, + payload_len); +} + +static void model_bound_cb(uint16_t addr, struct bt_mesh_model *model, + uint16_t key_idx) +{ + console_printf("Model bound: remote addr 0x%04x key_idx 0x%04x model %p\n", + addr, key_idx, model); +} + +static void model_unbound_cb(uint16_t addr, struct bt_mesh_model *model, + uint16_t key_idx) +{ + console_printf("Model unbound: remote addr 0x%04x key_idx 0x%04x " + "model %p\n", addr, key_idx, model); +} + +static void invalid_bearer_cb(uint8_t opcode) +{ + console_printf("Invalid bearer: opcode 0x%02x\n", opcode); +} + +static void incomp_timer_exp_cb(void) +{ + console_printf("Incomplete timer expired\n"); +} + +static struct bt_test_cb bt_test_cb = { + .mesh_net_recv = net_recv_ev, + .mesh_model_bound = model_bound_cb, + .mesh_model_unbound = model_unbound_cb, + .mesh_prov_invalid_bearer = invalid_bearer_cb, + .mesh_trans_incomp_timer_exp = incomp_timer_exp_cb, +}; + +static void +blemesh_on_reset(int reason) +{ + BLE_HS_LOG(ERROR, "Resetting state; reason=%d\n", reason); +} + +static void +blemesh_on_sync(void) +{ + console_printf("Bluetooth initialized\n"); + + shell_register_default_module("mesh"); + + if (IS_ENABLED(CONFIG_BT_TESTING)) { + bt_test_cb_register(&bt_test_cb); + } +} + +int +main(void) +{ + /* Initialize OS */ + sysinit(); + + /* Initialize the NimBLE host configuration. */ + ble_hs_cfg.reset_cb = blemesh_on_reset; + ble_hs_cfg.sync_cb = blemesh_on_sync; + ble_hs_cfg.store_status_cb = ble_store_util_status_rr; + + hal_gpio_init_out(LED_2, 0); + + while (1) { + os_eventq_run(os_eventq_dflt_get()); + } + return 0; +} diff --git a/lib/bt/host/nimble/nimble/apps/blemesh_shell/syscfg.yml b/lib/bt/host/nimble/nimble/apps/blemesh_shell/syscfg.yml new file mode 100644 index 00000000..0756e46f --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/blemesh_shell/syscfg.yml @@ -0,0 +1,60 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +syscfg.vals: + # Enable the shell task. + SHELL_TASK: 1 + + # Set log level to info (disable debug logging). + LOG_LEVEL: 0 + + # Default task settings + OS_MAIN_STACK_SIZE: 768 + + # SMP is not supported in this app, so disable smp-over-shell. + SHELL_MGMT: 0 + + MSYS_1_BLOCK_COUNT: 80 + + BLE_MESH_ADV_BUF_COUNT: 20 + BLE_MESH_TX_SEG_MAX: 6 + + BLE_MESH: 1 + BLE_MESH_SHELL: 1 + BLE_MESH_PROV: 1 + BLE_MESH_PROVISIONER: 1 + BLE_MESH_RELAY: 1 + BLE_MESH_PB_ADV: 1 + BLE_MESH_PB_GATT: 1 + BLE_MESH_LOW_POWER: 1 + BLE_MESH_LPN_AUTO: 0 + BLE_MESH_GATT_PROXY: 1 + BLE_MESH_LABEL_COUNT: 2 + BLE_MESH_SUBNET_COUNT: 2 + BLE_MESH_MODEL_GROUP_COUNT: 2 + BLE_MESH_MODEL_EXTENSIONS: 1 + BLE_MESH_APP_KEY_COUNT: 4 + BLE_MESH_IV_UPDATE_TEST: 1 + BLE_MESH_TESTING: 1 + BLE_MESH_FRIEND: 1 + BLE_MESH_CFG_CLI: 1 + BLE_MESH_SETTINGS: 0 + CONFIG_NFFS: 0 + + # Whether to save data to sys/config, or just keep it in RAM. + BLE_STORE_CONFIG_PERSIST: 0 diff --git a/lib/bt/host/nimble/nimble/apps/bleprph/pkg.yml b/lib/bt/host/nimble/nimble/apps/bleprph/pkg.yml new file mode 100644 index 00000000..0bb0d9d0 --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/bleprph/pkg.yml @@ -0,0 +1,44 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +pkg.name: apps/bleprph +pkg.type: app +pkg.description: Simple BLE peripheral application. +pkg.author: "Apache Mynewt " +pkg.homepage: "http://mynewt.apache.org/" +pkg.keywords: + +pkg.deps: + - "@apache-mynewt-core/boot/split" + - "@mcuboot/boot/bootutil" + - "@apache-mynewt-core/kernel/os" + - "@apache-mynewt-core/mgmt/imgmgr" + - "@apache-mynewt-core/mgmt/smp" + - "@apache-mynewt-core/mgmt/smp/transport/ble" + - "@apache-mynewt-core/sys/console/full" + - "@apache-mynewt-core/sys/log/full" + - "@apache-mynewt-core/sys/log/modlog" + - "@apache-mynewt-core/sys/stats/full" + - "@apache-mynewt-core/sys/sysinit" + - "@apache-mynewt-core/sys/id" + - nimble/host + - nimble/host/services/ans + - nimble/host/services/dis + - nimble/host/services/gap + - nimble/host/services/gatt + - nimble/host/store/config + - nimble/host/util diff --git a/lib/bt/host/nimble/nimble/apps/bleprph/src/bleprph.h b/lib/bt/host/nimble/nimble/apps/bleprph/src/bleprph.h new file mode 100644 index 00000000..5ec34610 --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/bleprph/src/bleprph.h @@ -0,0 +1,61 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_BLEPRPH_ +#define H_BLEPRPH_ + +#include +#include "nimble/ble.h" +#include "modlog/modlog.h" +#ifdef __cplusplus +extern "C" { +#endif + +struct ble_hs_cfg; +struct ble_gatt_register_ctxt; + +/** GATT server. */ +#define GATT_SVR_SVC_ALERT_UUID 0x1811 +#define GATT_SVR_CHR_SUP_NEW_ALERT_CAT_UUID 0x2A47 +#define GATT_SVR_CHR_NEW_ALERT 0x2A46 +#define GATT_SVR_CHR_SUP_UNR_ALERT_CAT_UUID 0x2A48 +#define GATT_SVR_CHR_UNR_ALERT_STAT_UUID 0x2A45 +#define GATT_SVR_CHR_ALERT_NOT_CTRL_PT 0x2A44 + +void gatt_svr_register_cb(struct ble_gatt_register_ctxt *ctxt, void *arg); +int gatt_svr_init(void); + +/* PHY support */ +#if MYNEWT_VAL(BLEPRPH_LE_PHY_SUPPORT) +#define CONN_HANDLE_INVALID 0xffff + +void phy_init(void); +void phy_conn_changed(uint16_t handle); +void phy_update(uint8_t phy); +#endif + +/** Misc. */ +void print_bytes(const uint8_t *bytes, int len); +void print_addr(const void *addr); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/bt/host/nimble/nimble/apps/bleprph/src/gatt_svr.c b/lib/bt/host/nimble/nimble/apps/bleprph/src/gatt_svr.c new file mode 100644 index 00000000..632ef4fb --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/bleprph/src/gatt_svr.c @@ -0,0 +1,204 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#include +#include +#include "bsp/bsp.h" +#include "host/ble_hs.h" +#include "host/ble_uuid.h" +#include "bleprph.h" + +/** + * The vendor specific security test service consists of two characteristics: + * o random-number-generator: generates a random 32-bit number each time + * it is read. This characteristic can only be read over an encrypted + * connection. + * o static-value: a single-byte characteristic that can always be read, + * but can only be written over an encrypted connection. + */ + +/* 59462f12-9543-9999-12c8-58b459a2712d */ +static const ble_uuid128_t gatt_svr_svc_sec_test_uuid = + BLE_UUID128_INIT(0x2d, 0x71, 0xa2, 0x59, 0xb4, 0x58, 0xc8, 0x12, + 0x99, 0x99, 0x43, 0x95, 0x12, 0x2f, 0x46, 0x59); + +/* 5c3a659e-897e-45e1-b016-007107c96df6 */ +static const ble_uuid128_t gatt_svr_chr_sec_test_rand_uuid = + BLE_UUID128_INIT(0xf6, 0x6d, 0xc9, 0x07, 0x71, 0x00, 0x16, 0xb0, + 0xe1, 0x45, 0x7e, 0x89, 0x9e, 0x65, 0x3a, 0x5c); + +/* 5c3a659e-897e-45e1-b016-007107c96df7 */ +static const ble_uuid128_t gatt_svr_chr_sec_test_static_uuid = + BLE_UUID128_INIT(0xf7, 0x6d, 0xc9, 0x07, 0x71, 0x00, 0x16, 0xb0, + 0xe1, 0x45, 0x7e, 0x89, 0x9e, 0x65, 0x3a, 0x5c); + +static uint8_t gatt_svr_sec_test_static_val; + +static int +gatt_svr_chr_access_sec_test(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, + void *arg); + +static const struct ble_gatt_svc_def gatt_svr_svcs[] = { + { + /*** Service: Security test. */ + .type = BLE_GATT_SVC_TYPE_PRIMARY, + .uuid = &gatt_svr_svc_sec_test_uuid.u, + .characteristics = (struct ble_gatt_chr_def[]) { { + /*** Characteristic: Random number generator. */ + .uuid = &gatt_svr_chr_sec_test_rand_uuid.u, + .access_cb = gatt_svr_chr_access_sec_test, + .flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_READ_ENC, + }, { + /*** Characteristic: Static value. */ + .uuid = &gatt_svr_chr_sec_test_static_uuid.u, + .access_cb = gatt_svr_chr_access_sec_test, + .flags = BLE_GATT_CHR_F_READ | + BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_WRITE_ENC, + }, { + 0, /* No more characteristics in this service. */ + } }, + }, + + { + 0, /* No more services. */ + }, +}; + +static int +gatt_svr_chr_write(struct os_mbuf *om, uint16_t min_len, uint16_t max_len, + void *dst, uint16_t *len) +{ + uint16_t om_len; + int rc; + + om_len = OS_MBUF_PKTLEN(om); + if (om_len < min_len || om_len > max_len) { + return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; + } + + rc = ble_hs_mbuf_to_flat(om, dst, max_len, len); + if (rc != 0) { + return BLE_ATT_ERR_UNLIKELY; + } + + return 0; +} + +static int +gatt_svr_chr_access_sec_test(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, + void *arg) +{ + const ble_uuid_t *uuid; + int rand_num; + int rc; + + uuid = ctxt->chr->uuid; + + /* Determine which characteristic is being accessed by examining its + * 128-bit UUID. + */ + + if (ble_uuid_cmp(uuid, &gatt_svr_chr_sec_test_rand_uuid.u) == 0) { + assert(ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR); + + /* Respond with a 32-bit random number. */ + rand_num = rand(); + rc = os_mbuf_append(ctxt->om, &rand_num, sizeof rand_num); + return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; + } + + if (ble_uuid_cmp(uuid, &gatt_svr_chr_sec_test_static_uuid.u) == 0) { + switch (ctxt->op) { + case BLE_GATT_ACCESS_OP_READ_CHR: + rc = os_mbuf_append(ctxt->om, &gatt_svr_sec_test_static_val, + sizeof gatt_svr_sec_test_static_val); + return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; + + case BLE_GATT_ACCESS_OP_WRITE_CHR: + rc = gatt_svr_chr_write(ctxt->om, + sizeof gatt_svr_sec_test_static_val, + sizeof gatt_svr_sec_test_static_val, + &gatt_svr_sec_test_static_val, NULL); + return rc; + + default: + assert(0); + return BLE_ATT_ERR_UNLIKELY; + } + } + + /* Unknown characteristic; the nimble stack should not have called this + * function. + */ + assert(0); + return BLE_ATT_ERR_UNLIKELY; +} + +void +gatt_svr_register_cb(struct ble_gatt_register_ctxt *ctxt, void *arg) +{ + char buf[BLE_UUID_STR_LEN]; + + switch (ctxt->op) { + case BLE_GATT_REGISTER_OP_SVC: + MODLOG_DFLT(DEBUG, "registered service %s with handle=%d\n", + ble_uuid_to_str(ctxt->svc.svc_def->uuid, buf), + ctxt->svc.handle); + break; + + case BLE_GATT_REGISTER_OP_CHR: + MODLOG_DFLT(DEBUG, "registering characteristic %s with " + "def_handle=%d val_handle=%d\n", + ble_uuid_to_str(ctxt->chr.chr_def->uuid, buf), + ctxt->chr.def_handle, + ctxt->chr.val_handle); + break; + + case BLE_GATT_REGISTER_OP_DSC: + MODLOG_DFLT(DEBUG, "registering descriptor %s with handle=%d\n", + ble_uuid_to_str(ctxt->dsc.dsc_def->uuid, buf), + ctxt->dsc.handle); + break; + + default: + assert(0); + break; + } +} + +int +gatt_svr_init(void) +{ + int rc; + + rc = ble_gatts_count_cfg(gatt_svr_svcs); + if (rc != 0) { + return rc; + } + + rc = ble_gatts_add_svcs(gatt_svr_svcs); + if (rc != 0) { + return rc; + } + + return 0; +} diff --git a/lib/bt/host/nimble/nimble/apps/bleprph/src/main.c b/lib/bt/host/nimble/nimble/apps/bleprph/src/main.c new file mode 100644 index 00000000..60a3aea9 --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/bleprph/src/main.c @@ -0,0 +1,372 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#include +#include +#include +#include "os/mynewt.h" +#include "bsp/bsp.h" +#include "hal/hal_gpio.h" +#include "console/console.h" +#include "hal/hal_system.h" +#include "config/config.h" +#include "split/split.h" +#if MYNEWT_VAL(BLE_SVC_DIS_FIRMWARE_REVISION_READ_PERM) >= 0 +#include "bootutil/image.h" +#include "imgmgr/imgmgr.h" +#include "services/dis/ble_svc_dis.h" +#endif + +/* BLE */ +#include "nimble/ble.h" +#include "host/ble_hs.h" +#include "host/util/util.h" +#include "services/gap/ble_svc_gap.h" + +/* Application-specified header. */ +#include "bleprph.h" + +static int bleprph_gap_event(struct ble_gap_event *event, void *arg); + +/** + * Logs information about a connection to the console. + */ +static void +bleprph_print_conn_desc(struct ble_gap_conn_desc *desc) +{ + MODLOG_DFLT(INFO, "handle=%d our_ota_addr_type=%d our_ota_addr=", + desc->conn_handle, desc->our_ota_addr.type); + print_addr(desc->our_ota_addr.val); + MODLOG_DFLT(INFO, " our_id_addr_type=%d our_id_addr=", + desc->our_id_addr.type); + print_addr(desc->our_id_addr.val); + MODLOG_DFLT(INFO, " peer_ota_addr_type=%d peer_ota_addr=", + desc->peer_ota_addr.type); + print_addr(desc->peer_ota_addr.val); + MODLOG_DFLT(INFO, " peer_id_addr_type=%d peer_id_addr=", + desc->peer_id_addr.type); + print_addr(desc->peer_id_addr.val); + MODLOG_DFLT(INFO, " conn_itvl=%d conn_latency=%d supervision_timeout=%d " + "encrypted=%d authenticated=%d bonded=%d\n", + desc->conn_itvl, desc->conn_latency, + desc->supervision_timeout, + desc->sec_state.encrypted, + desc->sec_state.authenticated, + desc->sec_state.bonded); +} + +/** + * Enables advertising with the following parameters: + * o General discoverable mode. + * o Undirected connectable mode. + */ +static void +bleprph_advertise(void) +{ + uint8_t own_addr_type; + struct ble_gap_adv_params adv_params; + struct ble_hs_adv_fields fields; + const char *name; + int rc; + + /* Figure out address to use while advertising (no privacy for now) */ + rc = ble_hs_id_infer_auto(0, &own_addr_type); + if (rc != 0) { + MODLOG_DFLT(ERROR, "error determining address type; rc=%d\n", rc); + return; + } + + /** + * Set the advertisement data included in our advertisements: + * o Flags (indicates advertisement type and other general info). + * o Advertising tx power. + * o Device name. + * o 16-bit service UUIDs (alert notifications). + */ + + memset(&fields, 0, sizeof fields); + + /* Advertise two flags: + * o Discoverability in forthcoming advertisement (general) + * o BLE-only (BR/EDR unsupported). + */ + fields.flags = BLE_HS_ADV_F_DISC_GEN | + BLE_HS_ADV_F_BREDR_UNSUP; + + /* Indicate that the TX power level field should be included; have the + * stack fill this value automatically. This is done by assiging the + * special value BLE_HS_ADV_TX_PWR_LVL_AUTO. + */ + fields.tx_pwr_lvl_is_present = 1; + fields.tx_pwr_lvl = BLE_HS_ADV_TX_PWR_LVL_AUTO; + + name = ble_svc_gap_device_name(); + fields.name = (uint8_t *)name; + fields.name_len = strlen(name); + fields.name_is_complete = 1; + + fields.uuids16 = (ble_uuid16_t[]){ + BLE_UUID16_INIT(GATT_SVR_SVC_ALERT_UUID) + }; + fields.num_uuids16 = 1; + fields.uuids16_is_complete = 1; + + rc = ble_gap_adv_set_fields(&fields); + if (rc != 0) { + MODLOG_DFLT(ERROR, "error setting advertisement data; rc=%d\n", rc); + return; + } + + /* Begin advertising. */ + memset(&adv_params, 0, sizeof adv_params); + adv_params.conn_mode = BLE_GAP_CONN_MODE_UND; + adv_params.disc_mode = BLE_GAP_DISC_MODE_GEN; + rc = ble_gap_adv_start(own_addr_type, NULL, BLE_HS_FOREVER, + &adv_params, bleprph_gap_event, NULL); + if (rc != 0) { + MODLOG_DFLT(ERROR, "error enabling advertisement; rc=%d\n", rc); + return; + } +} + +/** + * The nimble host executes this callback when a GAP event occurs. The + * application associates a GAP event callback with each connection that forms. + * bleprph uses the same callback for all connections. + * + * @param event The type of event being signalled. + * @param ctxt Various information pertaining to the event. + * @param arg Application-specified argument; unuesd by + * bleprph. + * + * @return 0 if the application successfully handled the + * event; nonzero on failure. The semantics + * of the return code is specific to the + * particular GAP event being signalled. + */ +static int +bleprph_gap_event(struct ble_gap_event *event, void *arg) +{ + struct ble_gap_conn_desc desc; + int rc; + + switch (event->type) { + case BLE_GAP_EVENT_CONNECT: + /* A new connection was established or a connection attempt failed. */ + MODLOG_DFLT(INFO, "connection %s; status=%d ", + event->connect.status == 0 ? "established" : "failed", + event->connect.status); + if (event->connect.status == 0) { + rc = ble_gap_conn_find(event->connect.conn_handle, &desc); + assert(rc == 0); + bleprph_print_conn_desc(&desc); + +#if MYNEWT_VAL(BLEPRPH_LE_PHY_SUPPORT) + phy_conn_changed(event->connect.conn_handle); +#endif + } + MODLOG_DFLT(INFO, "\n"); + + if (event->connect.status != 0) { + /* Connection failed; resume advertising. */ + bleprph_advertise(); + } + return 0; + + case BLE_GAP_EVENT_DISCONNECT: + MODLOG_DFLT(INFO, "disconnect; reason=%d ", event->disconnect.reason); + bleprph_print_conn_desc(&event->disconnect.conn); + MODLOG_DFLT(INFO, "\n"); + +#if MYNEWT_VAL(BLEPRPH_LE_PHY_SUPPORT) + phy_conn_changed(CONN_HANDLE_INVALID); +#endif + + /* Connection terminated; resume advertising. */ + bleprph_advertise(); + return 0; + + case BLE_GAP_EVENT_CONN_UPDATE: + /* The central has updated the connection parameters. */ + MODLOG_DFLT(INFO, "connection updated; status=%d ", + event->conn_update.status); + rc = ble_gap_conn_find(event->conn_update.conn_handle, &desc); + assert(rc == 0); + bleprph_print_conn_desc(&desc); + MODLOG_DFLT(INFO, "\n"); + return 0; + + case BLE_GAP_EVENT_ADV_COMPLETE: + MODLOG_DFLT(INFO, "advertise complete; reason=%d", + event->adv_complete.reason); + bleprph_advertise(); + return 0; + + case BLE_GAP_EVENT_ENC_CHANGE: + /* Encryption has been enabled or disabled for this connection. */ + MODLOG_DFLT(INFO, "encryption change event; status=%d ", + event->enc_change.status); + rc = ble_gap_conn_find(event->connect.conn_handle, &desc); + assert(rc == 0); + bleprph_print_conn_desc(&desc); + MODLOG_DFLT(INFO, "\n"); + return 0; + + case BLE_GAP_EVENT_SUBSCRIBE: + MODLOG_DFLT(INFO, "subscribe event; conn_handle=%d attr_handle=%d " + "reason=%d prevn=%d curn=%d previ=%d curi=%d\n", + event->subscribe.conn_handle, + event->subscribe.attr_handle, + event->subscribe.reason, + event->subscribe.prev_notify, + event->subscribe.cur_notify, + event->subscribe.prev_indicate, + event->subscribe.cur_indicate); + return 0; + + case BLE_GAP_EVENT_MTU: + MODLOG_DFLT(INFO, "mtu update event; conn_handle=%d cid=%d mtu=%d\n", + event->mtu.conn_handle, + event->mtu.channel_id, + event->mtu.value); + return 0; + + case BLE_GAP_EVENT_REPEAT_PAIRING: + /* We already have a bond with the peer, but it is attempting to + * establish a new secure link. This app sacrifices security for + * convenience: just throw away the old bond and accept the new link. + */ + + /* Delete the old bond. */ + rc = ble_gap_conn_find(event->repeat_pairing.conn_handle, &desc); + assert(rc == 0); + ble_store_util_delete_peer(&desc.peer_id_addr); + + /* Return BLE_GAP_REPEAT_PAIRING_RETRY to indicate that the host should + * continue with the pairing operation. + */ + return BLE_GAP_REPEAT_PAIRING_RETRY; + +#if MYNEWT_VAL(BLEPRPH_LE_PHY_SUPPORT) + case BLE_GAP_EVENT_PHY_UPDATE_COMPLETE: + /* XXX: assume symmetric phy for now */ + phy_update(event->phy_updated.tx_phy); + return 0; +#endif + } + + return 0; +} + +static void +bleprph_on_reset(int reason) +{ + MODLOG_DFLT(ERROR, "Resetting state; reason=%d\n", reason); +} + +static void +bleprph_on_sync(void) +{ + int rc; + + /* Make sure we have proper identity address set (public preferred) */ + rc = ble_hs_util_ensure_addr(0); + assert(rc == 0); + + /* Begin advertising. */ + bleprph_advertise(); +} + +/** + * main + * + * The main task for the project. This function initializes the packages, + * then starts serving events from default event queue. + * + * @return int NOTE: this function should never return! + */ +static int +main_fn(int argc, char **argv) +{ +#if MYNEWT_VAL(BLE_SVC_DIS_FIRMWARE_REVISION_READ_PERM) >= 0 + struct image_version ver; + static char ver_str[IMGMGR_NMGR_MAX_VER]; +#endif + int rc; + + /* Initialize OS */ + sysinit(); + + /* Initialize the NimBLE host configuration. */ + ble_hs_cfg.reset_cb = bleprph_on_reset; + ble_hs_cfg.sync_cb = bleprph_on_sync; + ble_hs_cfg.gatts_register_cb = gatt_svr_register_cb; + ble_hs_cfg.store_status_cb = ble_store_util_status_rr; + + rc = gatt_svr_init(); + assert(rc == 0); + +#if MYNEWT_VAL(BLE_SVC_DIS_FIRMWARE_REVISION_READ_PERM) >= 0 + /* Set firmware version in DIS */ + imgr_my_version(&ver); + imgr_ver_str(&ver, ver_str); + ble_svc_dis_firmware_revision_set(ver_str); +#endif + +#if MYNEWT_VAL(BLEPRPH_LE_PHY_SUPPORT) + phy_init(); +#endif + + conf_load(); + + /* If this app is acting as the loader in a split image setup, jump into + * the second stage application instead of starting the OS. + */ +#if MYNEWT_VAL(SPLIT_LOADER) + { + void *entry; + rc = split_app_go(&entry, true); + if (rc == 0) { + hal_system_start(entry); + } + } +#endif + + /* + * As the last thing, process events from default event queue. + */ + while (1) { + os_eventq_run(os_eventq_dflt_get()); + } + return 0; +} + +int +main(int argc, char **argv) +{ +#if BABBLESIM + extern void bsim_init(int argc, char** argv, void *main_fn); + bsim_init(argc, argv, main_fn); +#else + main_fn(argc, argv); +#endif + + return 0; +} diff --git a/lib/bt/host/nimble/nimble/apps/bleprph/src/misc.c b/lib/bt/host/nimble/nimble/apps/bleprph/src/misc.c new file mode 100644 index 00000000..640b7ff8 --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/bleprph/src/misc.c @@ -0,0 +1,43 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "bleprph.h" + +/** + * Utility function to log an array of bytes. + */ +void +print_bytes(const uint8_t *bytes, int len) +{ + int i; + + for (i = 0; i < len; i++) { + MODLOG_DFLT(INFO, "%s0x%02x", i != 0 ? ":" : "", bytes[i]); + } +} + +void +print_addr(const void *addr) +{ + const uint8_t *u8p; + + u8p = addr; + MODLOG_DFLT(INFO, "%02x:%02x:%02x:%02x:%02x:%02x", + u8p[5], u8p[4], u8p[3], u8p[2], u8p[1], u8p[0]); +} diff --git a/lib/bt/host/nimble/nimble/apps/bleprph/src/phy.c b/lib/bt/host/nimble/nimble/apps/bleprph/src/phy.c new file mode 100644 index 00000000..c6fb2b35 --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/bleprph/src/phy.c @@ -0,0 +1,128 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "os/mynewt.h" +#include "bsp/bsp.h" +#include "hal/hal_gpio.h" +#include "host/ble_gap.h" +#include "bleprph.h" + +#if MYNEWT_VAL(BLEPRPH_LE_PHY_SUPPORT) + +static const int button_gpio[4] = MYNEWT_VAL(BLEPRPH_LE_PHY_BUTTON_GPIO); +static const int led_gpio[3] = MYNEWT_VAL(BLEPRPH_LE_PHY_LED_GPIO); + +#define PHY_TO_PTR(_mask, _opts) (void *)(((_opts) << 16) | ((_mask))) +#define PTR_TO_PHY_MASK(_ptr) (uint8_t)(((int)_ptr) & 0x0ff) +#define PTR_TO_PHY_OPTS(_ptr) (uint8_t)(((int)_ptr) >> 16) + +static struct os_event gpio_event; + +static uint16_t conn_handle = CONN_HANDLE_INVALID; + +static void +gpio_irq_handler(void *arg) +{ + gpio_event.ev_arg = arg; + os_eventq_put(os_eventq_dflt_get(), &gpio_event); +} + +static void +gpio_event_handler(struct os_event *ev) +{ + uint8_t phy_mask; + uint8_t phy_opts; + int sr; + + OS_ENTER_CRITICAL(sr); + phy_mask = PTR_TO_PHY_MASK(ev->ev_arg); + phy_opts = PTR_TO_PHY_OPTS(ev->ev_arg); + OS_EXIT_CRITICAL(sr); + + if (conn_handle != CONN_HANDLE_INVALID) { + ble_gap_set_prefered_le_phy(conn_handle, phy_mask, phy_mask, phy_opts); + } +} + +static void +setup_button_gpio(int button, uint8_t phy_mask, uint8_t phy_opts) +{ + if (button < 0) { + return; + } + + hal_gpio_irq_init(button, gpio_irq_handler, PHY_TO_PTR(phy_mask, phy_opts), + HAL_GPIO_TRIG_FALLING, HAL_GPIO_PULL_UP); + hal_gpio_irq_enable(button); +} + +void +phy_init(void) +{ + gpio_event.ev_cb = gpio_event_handler; + + /* + * XXX: we could make this configurable, but for now assume all pins are + * valid, buttons gpio pins are pulled-up and LEDs are active-low - this + * is valid for nRF52840 PDK. + */ + setup_button_gpio(button_gpio[0], BLE_GAP_LE_PHY_1M_MASK, + BLE_GAP_LE_PHY_CODED_ANY); + setup_button_gpio(button_gpio[1], BLE_GAP_LE_PHY_2M_MASK, + BLE_GAP_LE_PHY_CODED_ANY); + setup_button_gpio(button_gpio[2], BLE_GAP_LE_PHY_CODED_MASK, + BLE_GAP_LE_PHY_CODED_S2); + setup_button_gpio(button_gpio[3], BLE_GAP_LE_PHY_CODED_MASK, + BLE_GAP_LE_PHY_CODED_S8); + + hal_gpio_init_out(led_gpio[0], 1); + hal_gpio_init_out(led_gpio[1], 1); + hal_gpio_init_out(led_gpio[2], 1); +} + +void +phy_conn_changed(uint16_t handle) +{ + uint8_t phy = 0; + + conn_handle = handle; + + if (handle != CONN_HANDLE_INVALID) { + /* XXX: assume symmetric phy for now */ + ble_gap_read_le_phy(handle, &phy, &phy); + } + + phy_update(phy); +} + +void +phy_update(uint8_t phy) +{ + if (conn_handle == CONN_HANDLE_INVALID) { + hal_gpio_write(led_gpio[0], 1); + hal_gpio_write(led_gpio[1], 1); + hal_gpio_write(led_gpio[2], 1); + } else { + hal_gpio_write(led_gpio[0], !(phy == BLE_GAP_LE_PHY_1M)); + hal_gpio_write(led_gpio[1], !(phy == BLE_GAP_LE_PHY_2M)); + hal_gpio_write(led_gpio[2], !(phy == BLE_GAP_LE_PHY_CODED)); + } +} + +#endif diff --git a/lib/bt/host/nimble/nimble/apps/bleprph/syscfg.yml b/lib/bt/host/nimble/nimble/apps/bleprph/syscfg.yml new file mode 100644 index 00000000..e00eb3b8 --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/bleprph/syscfg.yml @@ -0,0 +1,71 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +syscfg.defs: + BLEPRPH_LE_PHY_SUPPORT: + description: > + Enable support for changing PHY preference on active connection. + PHY preference change is triggered by configured GPIO pins. + Current PHY is indicated using LEDs connected to configured + GPIO pins. + value: 0 + BLEPRPH_LE_PHY_BUTTON_GPIO: + description: > + GPIO pins for changing PHY preference on active connection. This + is an array of 4 GPIO pin numbers for 1M, 2M, LE Coded S=2 and + LE Coded S=8 respectively. + value: "(int[]){ BUTTON_1, BUTTON_2, BUTTON_3, BUTTON_4 }" + BLEPRPH_LE_PHY_LED_GPIO: + description: > + GPIO pins for indicating current PHY on active connection. This + is an array of 3 GPIO pin numbers for 1M, 2M and LE Coded + respectively. + value: "(int[]){ LED_1, LED_2, LED_3 }" + +syscfg.vals: + # Disable central and observer roles. + BLE_ROLE_BROADCASTER: 1 + BLE_ROLE_CENTRAL: 0 + BLE_ROLE_OBSERVER: 0 + BLE_ROLE_PERIPHERAL: 1 + + # Configure DIS + BLE_SVC_DIS_FIRMWARE_REVISION_READ_PERM: 1 + + # Log reboot messages to a flash circular buffer. + REBOOT_LOG_FCB: 1 + LOG_FCB: 1 + CONFIG_FCB: 1 + + # Enable smp commands. + STATS_MGMT: 1 + LOG_MGMT: 1 + CONFIG_MGMT: 1 + + # OS main/default task + OS_MAIN_STACK_SIZE: 512 + + # Lots of smaller mbufs are required for smp using typical BLE ATT MTU + # values. + MSYS_1_BLOCK_COUNT: 22 + MSYS_1_BLOCK_SIZE: 110 + + BLE_SVC_GAP_DEVICE_NAME: '"nimble-bleprph"' + + # Whether to save data to sys/config, or just keep it in RAM. + BLE_STORE_CONFIG_PERSIST: 0 diff --git a/lib/bt/host/nimble/nimble/apps/blestress/README.md b/lib/bt/host/nimble/nimble/apps/blestress/README.md new file mode 100644 index 00000000..8524397a --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/blestress/README.md @@ -0,0 +1,201 @@ +BLE Stress Tests + +****************************************************************************** + QUICK TIPS: + You need 2 controllers supported by MyNewt. One will become TX device, + the other will become RX device. + + 1. Set role (TX=0, RX=1) for current device in syscfg.yml + BLE_STRESS_TEST_ROLE: 1 + + The RX has LED2 turned on. + + 2. Set (in syscfg.yml) number of times to repeat each tested action + in use case, e.g. do 1000 times connect/disconnect to complete use case. + RX_STRESS_REPEAT: 1000 + + 3. To perform only specific test, go to rx_stress.c, + find definition of rx_stress_main_task_fn(void *arg) and in place of + for-loop just paste function rx_stress_start(i), where i is id of test. + +****************************************************************************** + +No | Use case +----------------------------------------------------------------------------- + 1 | Stress Connect -> Connect Cancel - repeat 1000 + | RX: Nothing + | TX: Connect/Connect cancel + | + 2 | Stress Connect/Disconnect legacy - repeat 1000 + | RX: Advertise legacy + | TX: Connect/Disconnect + | + 3 | Stress Connect/Disconnect ext adv - repeat 1000 + | RX: Advertise Ext + | TX: Connect/Disconnect + | + 4 | Stress connection params update (TX) - (1000) + | RX: Advertise + | TX: Connect/Connect param update + | + 5 | Stress connection params update (RX) - (1000) + | RX: Advertise/Connect param update + | TX: Connect + | + 6 | Stress Scan + | RX: Advertise a known data pattern + | TX: Scan and check received data with pattern + | + 7 | Stress PHY Update (TX) - (1000) + | RX: Advertise + | TX: Connect/Phy update + | + 8 | Stress PHY update (RX) - (1000) + | RX: Advertise/Phy update + | TX: Connect + | + 9 | Stress multi connection + | RX: Advertise Ext + | TX: Establish and maintain as many instances as possible + | +10 | Stress L2CAP send + | RX: Send 64kB of data with L2CAP + | TX: Measure bit rate and max received data MTU + | +11 | Stress Advertise/Connect/Continue Adv/Disconnect + | RX: Advertise Ext/Continue same advertise after connect + | TX: Connect + | +12 | Stress GATT indicating + | RX: Indicate + | TX: Receive indication. Measure average time of indicating. + | +13 | Stress GATT notification + | RX: Notify. Measure average time of notifying. + | TX: Count the number of received notification. + | +14 | Stress GATT Subscribe/Notify/Unsubscribe + | RX: Notify on subscribe + | TX: Measure the average time from sending a subscription request + | to receiving a notification. + | +15 | Stress Connect/Send/Disconnect + | RX: Advertise/Send via ATT/Disconnect + | TX: Receive notification. Measure time of notifying. + +****************************************************************************** + Concept: + The RX device advertises data containing a UUID128 of test use case that + should be performed. The TX device scan for known UUIDs, when it finds, + adapts to the advertised use case and runs a test. + + Stress Task vs Semaphore: + The rx_stress_start_auto function starts main stress test task that runs + all stress tests one by one. The tests are based on event handling, so to + synchronize main task with events, a semaphore is used. On use case start + there is 0 tokens for semaphore. After main task starts one of use cases, + it comes across a semaphore and has to wait. When use case is completed, + 1 token is released, so main task can use it to pass through semaphore. + The tx_stress_start_auto function works analogically. + + + Newt target set example: + rymek@rymek:~/projects/bletiny_proj$ newt target show bletest_tx + targets/bletest_tx + app=@apache-mynewt-nimble/apps/blestress + bsp=@apache-mynewt-core/hw/bsp/nordic_pca10056 + build_profile=debug + syscfg=BLE_STRESS_TEST_ROLE=0 + rymek@rymek:~/projects/bletiny_proj$ newt target show bletest_rx + targets/bletest_rx + app=@apache-mynewt-nimble/apps/blestress + bsp=@apache-mynewt-core/hw/bsp/nordic_pca10056 + build_profile=debug + syscfg=BLE_STRESS_TEST_ROLE=1 + + + Example of expected logs on TX side(LOG_LEVEL > 1): + Start test num 1 + >>>>>>>>>>>>>>>>>>>> Stress test 1 completed + Start scan for test + Start test num 2 + >>>>>>>>>>>>>>>>>>>> Stress test 2 completed + Start scan for test + Start test num 3 + >>>>>>>>>>>>>>>>>>>> Stress test 3 completed + Start scan for test + Start test num 4 + >>>>>>>>>>>>>>>>>>>> Stress test 4 completed + Start scan for test + Start test num 5 + >>>>>>>>>>>>>>>>>>>> Stress test 5 completed + Start scan for test + Start test num 6 + >>>>>>>>>>>>>>>>>>>> Stress test 6 completed + Start scan for test + Start test num 7 + >>>>>>>>>>>>>>>>>>>> Stress test 7 completed + Start scan for test + Start test num 8 + >>>>>>>>>>>>>>>>>>>> Stress test 8 completed + All tests completed + Tests results: + Use case 1 - Stress Connect -> Connect Cancel: + Con attempts = 20 + Con success = 0 + Use case 2 - Stress Connect/Disconnect legacy: + Con attempts = 20 + Con success = 20 + Use case 3 - Stress Connect/Disconnect ext adv: + Con attempts = 20 + Con success = 20 + Use case 4 - Stress connection params update (TX): + Params updates = 20 + Use case 5 - Stress connection params update (RX): + Params updates = 20 + Use case 6 - Stress Scan: + Received first packets = 20 + Received all packets = 20 + Use case 7 - Stress PHY Update (TX): + PHY updates = 20 + Use case 8 - Stress Connect -> Connect Cancel: + PHY updates = 20 + + + Example of expected logs on RX side(LOG_LEVEL > 1): + Start test num 2 + >>>>>>>>>>>>>>>>>>>> Stress test 2 completed + Start test num 3 + >>>>>>>>>>>>>>>>>>>> Stress test 3 completed + Start test num 4 + >>>>>>>>>>>>>>>>>>>> Stress test 4 completed + Start test num 5 + >>>>>>>>>>>>>>>>>>>> Stress test 5 completed + Start test num 6 + Received signal to switch test + Start test num 7 + >>>>>>>>>>>>>>>>>>>> Stress test 7 completed + Start test num 8 + >>>>>>>>>>>>>>>>>>>> Stress test 8 completed + All tests completed + Tests results: + Use case 1 - Stress Connect -> Connect Cancel: + Con attempts = 0 + Con success = 0 + Use case 2 - Stress Connect/Disconnect legacy: + Con attempts = 20 + Con success = 20 + Use case 3 - Stress Connect/Disconnect ext adv: + Con attempts = 20 + Con success = 20 + Use case 4 - Stress connection params update (TX): + Params updates = 20 + Use case 5 - Stress connection params update (RX): + Params updates = 20 + Use case 6 - Stress Scan: + Received first packets = 0 + Received all packets = 0 + Use case 7 - Stress PHY Update (TX): + PHY updates = 20 + Use case 8 - Stress Connect -> Connect Cancel: + PHY updates = 20 diff --git a/lib/bt/host/nimble/nimble/apps/blestress/pkg.yml b/lib/bt/host/nimble/nimble/apps/blestress/pkg.yml new file mode 100644 index 00000000..21013eb5 --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/blestress/pkg.yml @@ -0,0 +1,37 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +pkg.name: "apps/blestress" +pkg.type: app +pkg.description: "Stress tests sample application." +pkg.keywords: + +pkg.deps: + - "@mcuboot/boot/bootutil" + - "@apache-mynewt-core/sys/log/modlog" + - "@apache-mynewt-core/kernel/os" + - "@apache-mynewt-core/sys/console/full" + - "@apache-mynewt-core/sys/log/full" + - "@apache-mynewt-core/sys/stats/full" + - "@apache-mynewt-core/sys/id" + - "@apache-mynewt-nimble/nimble/host" + - "@apache-mynewt-nimble/nimble/host/util" + - "@apache-mynewt-nimble/nimble/host/services/gap" + - "@apache-mynewt-nimble/nimble/host/services/gatt" + - "@apache-mynewt-nimble/nimble/host/store/config" diff --git a/lib/bt/host/nimble/nimble/apps/blestress/src/main.c b/lib/bt/host/nimble/nimble/apps/blestress/src/main.c new file mode 100644 index 00000000..ec28ed8a --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/blestress/src/main.c @@ -0,0 +1,99 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#include +#include "os/mynewt.h" +#include "config/config.h" +#include "bsp.h" +#include "hal/hal_gpio.h" + +/* BLE */ +#include "nimble/ble.h" +#include "host/ble_hs.h" +#include "host/util/util.h" +#include "services/gap/ble_svc_gap.h" + +/* Application-specified header. */ +#include "rx_stress.h" +#include "tx_stress.h" +#include "stress_gatt.h" + +static void +stress_test_on_reset(int reason) +{ + MODLOG_DFLT(ERROR, "Resetting state; reason=%d\n", reason); +} + +static void +stress_test_on_sync(void) +{ + int rc; + + /* Make sure we have proper identity address set (public preferred) */ + rc = ble_hs_util_ensure_addr(1); + assert(rc == 0); + +#if MYNEWT_VAL(BLE_STRESS_TEST_ROLE) + rx_stress_start_auto(); +#else + tx_stress_start_auto(); +#endif +} + +/** + * main + * + * The main task for the project. This function initializes the packages, + * then starts serving events from default event queue. + * + * @return int NOTE: this function should never return! + */ +int +main(void) +{ + int rc; + + sysinit(); + + ble_hs_cfg.reset_cb = stress_test_on_reset; + ble_hs_cfg.sync_cb = stress_test_on_sync; + ble_hs_cfg.store_status_cb = ble_store_util_status_rr; + + /* Please do not change name. Otherwise some tests could fail. */ + rc = ble_svc_gap_device_name_set("STRESS"); + assert(rc == 0); + + conf_load(); + + rc = gatt_svr_init(); + assert(rc == 0); + +#if MYNEWT_VAL(BLE_STRESS_TEST_ROLE) + /* RX device */ + ble_hs_cfg.gatts_register_cb = gatt_svr_register_cb; + hal_gpio_init_out(LED_2, 1); + hal_gpio_toggle(LED_2); +#endif + + while (1) { + os_eventq_run(os_eventq_dflt_get()); + } + return 0; +} diff --git a/lib/bt/host/nimble/nimble/apps/blestress/src/misc.c b/lib/bt/host/nimble/nimble/apps/blestress/src/misc.c new file mode 100644 index 00000000..bd71a871 --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/blestress/src/misc.c @@ -0,0 +1,224 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "misc.h" + +void +rand_bytes(uint8_t *data, int len) { + int i; + + for (i = 0; i < len; ++i) { + data[i] = (uint8_t) rand() % UINT8_MAX; + } +} + +/** + * Utility function to log an array of bytes. + */ +void +print_bytes(const uint8_t *bytes, int len) +{ + int i; + + for (i = 0; i < len; i++) { + MODLOG_DFLT(DEBUG, "%s0x%02x", i != 0 ? ":" : "", bytes[i]); + } +} + +void +print_addr(const void *addr) +{ + const uint8_t *u8p; + + u8p = addr; + MODLOG_DFLT(DEBUG, "%02x:%02x:%02x:%02x:%02x:%02x", + u8p[5], u8p[4], u8p[3], u8p[2], u8p[1], u8p[0]); +} + +void +print_mbuf(const struct os_mbuf *om) +{ + while (om != NULL) { + print_bytes(om->om_data, om->om_len); + om = SLIST_NEXT(om, om_next); + + if(om == NULL) { + return; + } + + /* Separate buf fields with colon to maintain continuity */ + MODLOG_DFLT(DEBUG, ":"); + } +} + +char * +addr_str(const void *addr) +{ + /* 6 bytes of MAC address * 2 signs for each byte in string + 5 colons to + * separate bytes + 1 byte for null-character appended by sprintf + */ + static char buf[6 * 2 + 5 + 1]; + const uint8_t *u8p; + + u8p = addr; + sprintf(buf, "%02x:%02x:%02x:%02x:%02x:%02x", + u8p[5], u8p[4], u8p[3], u8p[2], u8p[1], u8p[0]); + + return buf; +} + +void +print_uuid(const ble_uuid_t *uuid) +{ + char buf[BLE_UUID_STR_LEN]; + + MODLOG_DFLT(DEBUG, "%s", ble_uuid_to_str(uuid, buf)); +} + +/** + * Logs information about a connection to the console. + */ +void +print_conn_desc(const struct ble_gap_conn_desc *desc) +{ + MODLOG_DFLT(DEBUG, "handle=%d our_ota_addr_type=%d our_ota_addr=%s ", + desc->conn_handle, desc->our_ota_addr.type, + addr_str(desc->our_ota_addr.val)); + MODLOG_DFLT(DEBUG, "our_id_addr_type=%d our_id_addr=%s ", + desc->our_id_addr.type, addr_str(desc->our_id_addr.val)); + MODLOG_DFLT(DEBUG, "peer_ota_addr_type=%d peer_ota_addr=%s ", + desc->peer_ota_addr.type, addr_str(desc->peer_ota_addr.val)); + MODLOG_DFLT(DEBUG, "peer_id_addr_type=%d peer_id_addr=%s ", + desc->peer_id_addr.type, addr_str(desc->peer_id_addr.val)); + MODLOG_DFLT(DEBUG, "conn_itvl=%d conn_latency=%d supervision_timeout=%d " + "encrypted=%d authenticated=%d bonded=%d", + desc->conn_itvl, desc->conn_latency, + desc->supervision_timeout, + desc->sec_state.encrypted, + desc->sec_state.authenticated, + desc->sec_state.bonded); +} + +void +print_adv_fields(const struct ble_hs_adv_fields *fields) +{ + char s[BLE_HS_ADV_MAX_SZ]; + const uint8_t *u8p; + int i; + + if (fields->flags != 0) { + MODLOG_DFLT(DEBUG, " flags=0x%02x\n", fields->flags); + } + + if (fields->uuids16 != NULL) { + MODLOG_DFLT(DEBUG, " uuids16(%scomplete)=", + fields->uuids16_is_complete ? "" : "in"); + for (i = 0; i < fields->num_uuids16; i++) { + print_uuid(&fields->uuids16[i].u); + MODLOG_DFLT(DEBUG, " "); + } + MODLOG_DFLT(DEBUG, "\n"); + } + + if (fields->uuids32 != NULL) { + MODLOG_DFLT(DEBUG, " uuids32(%scomplete)=", + fields->uuids32_is_complete ? "" : "in"); + for (i = 0; i < fields->num_uuids32; i++) { + print_uuid(&fields->uuids32[i].u); + MODLOG_DFLT(DEBUG, " "); + } + MODLOG_DFLT(DEBUG, "\n"); + } + + if (fields->uuids128 != NULL) { + MODLOG_DFLT(DEBUG, " uuids128(%scomplete)=", + fields->uuids128_is_complete ? "" : "in"); + for (i = 0; i < fields->num_uuids128; i++) { + print_uuid(&fields->uuids128[i].u); + MODLOG_DFLT(DEBUG, " "); + } + MODLOG_DFLT(DEBUG, "\n"); + } + + if (fields->name != NULL) { + assert(fields->name_len < sizeof s - 1); + memcpy(s, fields->name, fields->name_len); + s[fields->name_len] = '\0'; + MODLOG_DFLT(DEBUG, " name(%scomplete)=%s\n", + fields->name_is_complete ? "" : "in", s); + } + + if (fields->tx_pwr_lvl_is_present) { + MODLOG_DFLT(DEBUG, " tx_pwr_lvl=%d\n", fields->tx_pwr_lvl); + } + + if (fields->slave_itvl_range != NULL) { + MODLOG_DFLT(DEBUG, " slave_itvl_range="); + print_bytes(fields->slave_itvl_range, BLE_HS_ADV_SLAVE_ITVL_RANGE_LEN); + MODLOG_DFLT(DEBUG, "\n"); + } + + if (fields->svc_data_uuid16 != NULL) { + MODLOG_DFLT(DEBUG, " svc_data_uuid16="); + print_bytes(fields->svc_data_uuid16, fields->svc_data_uuid16_len); + MODLOG_DFLT(DEBUG, "\n"); + } + + if (fields->public_tgt_addr != NULL) { + MODLOG_DFLT(DEBUG, " public_tgt_addr="); + u8p = fields->public_tgt_addr; + for (i = 0; i < fields->num_public_tgt_addrs; i++) { + MODLOG_DFLT(DEBUG, "public_tgt_addr=%s ", addr_str(u8p)); + u8p += BLE_HS_ADV_PUBLIC_TGT_ADDR_ENTRY_LEN; + } + MODLOG_DFLT(DEBUG, "\n"); + } + + if (fields->appearance_is_present) { + MODLOG_DFLT(DEBUG, " appearance=0x%04x\n", fields->appearance); + } + + if (fields->adv_itvl_is_present) { + MODLOG_DFLT(DEBUG, " adv_itvl=0x%04x\n", fields->adv_itvl); + } + + if (fields->svc_data_uuid32 != NULL) { + MODLOG_DFLT(DEBUG, " svc_data_uuid32="); + print_bytes(fields->svc_data_uuid32, fields->svc_data_uuid32_len); + MODLOG_DFLT(DEBUG, "\n"); + } + + if (fields->svc_data_uuid128 != NULL) { + MODLOG_DFLT(DEBUG, " svc_data_uuid128="); + print_bytes(fields->svc_data_uuid128, fields->svc_data_uuid128_len); + MODLOG_DFLT(DEBUG, "\n"); + } + + if (fields->uri != NULL) { + MODLOG_DFLT(DEBUG, " uri="); + print_bytes(fields->uri, fields->uri_len); + MODLOG_DFLT(DEBUG, "\n"); + } + + if (fields->mfg_data != NULL) { + MODLOG_DFLT(DEBUG, " mfg_data="); + print_bytes(fields->mfg_data, fields->mfg_data_len); + MODLOG_DFLT(DEBUG, "\n"); + } +} diff --git a/lib/bt/host/nimble/nimble/apps/blestress/src/misc.h b/lib/bt/host/nimble/nimble/apps/blestress/src/misc.h new file mode 100644 index 00000000..39e5e737 --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/blestress/src/misc.h @@ -0,0 +1,54 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef BLE_TGT_MISC_H +#define BLE_TGT_MISC_H + +#include +#include +#include +#include "host/ble_hs.h" +#include "host/ble_uuid.h" +#include "host/ble_hs_adv.h" + +#ifdef __cplusplus +extern "C" { +#endif + +void rand_bytes(uint8_t *data, int len); + +void print_bytes(const uint8_t *bytes, int len); + +void print_addr(const void *addr); + +void print_mbuf(const struct os_mbuf *om); + +char *addr_str(const void *addr); + +void print_uuid(const ble_uuid_t *uuid); + +void print_conn_desc(const struct ble_gap_conn_desc *desc); + +void print_adv_fields(const struct ble_hs_adv_fields *fields); + +#ifdef __cplusplus +} +#endif + +#endif //BLE_TGT_MISC_H diff --git a/lib/bt/host/nimble/nimble/apps/blestress/src/rx_stress.c b/lib/bt/host/nimble/nimble/apps/blestress/src/rx_stress.c new file mode 100644 index 00000000..61561b24 --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/blestress/src/rx_stress.c @@ -0,0 +1,1489 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#include "rx_stress.h" + +/* UUID128 of stress test use cases*/ +static uint8_t rx_stress_uuid128[STRESS_UUIDS_NUM][16]; + +static struct com_stress_test_ctx rx_stress_ctxD = { + .conn_handle = 0xffff, + .cur_test_id = 0, + .s6_rcv_adv_first = 0, + .s6_rcv_adv_suc = 0, +}; + +static struct com_stress_test_ctx *rx_stress_ctx = &rx_stress_ctxD; + +#define EXTENDED_ADVERT 0 +#define LEGACY_ADVERT 1 +/* Advertising instances ids */ +#define SWITCHER_INSTANCE 0 +#define TEST_INSTANCE 1 +/* Main test task priority. Set a high value so that the task does not + * interfere with event handling */ +#define RX_STRESS_MAIN_TASK_PRIO 0xf0 + +/* Advertising settings */ +struct rx_stress_adv_set { + uint8_t instance; + uint8_t *instance_uuid128; + uint8_t legacy_pdu; + ble_gap_event_fn *cb; + const uint8_t *pattern_data; + int pattern_len; +}; + +/* Define task stack and task object */ +#define RX_STRESS_MAIN_TASK_STACK_SIZE (2000) +static struct os_task rx_stress_main_task; +static os_stack_t rx_stress_main_task_stack[RX_STRESS_MAIN_TASK_STACK_SIZE]; +static struct os_sem rx_stress_main_sem; + +static void +rx_stress_on_test_finish(int test_num) +{ + console_printf("\033[0;32m\nStress test %d completed\033[0m\n", test_num); + os_sem_release(&rx_stress_main_sem); +} + +static int +rx_stress_adv_start(uint8_t instance) +{ + int rc; + + /* Resume advertising earlier configured instance */ + rc = ble_gap_ext_adv_start(instance, 0, 0); + assert (rc == 0 || rc == 2); + MODLOG_DFLT(INFO, "Instance %d started; rc: %d\n", instance, rc); + return rc; +} + +static int +rx_stress_adv_start_with_rand_addr(uint8_t instance) +{ + int rc; + ble_addr_t addr; + + ble_gap_ext_adv_stop(instance); + + rc = ble_hs_id_gen_rnd(1, &addr); + assert (rc == 0); + + /* Set random address for advertising instance */ + rc = ble_gap_ext_adv_set_addr(instance, &addr); + assert (rc == 0); + + return rx_stress_adv_start(instance); +} + +static void +rx_stress_simple_adv(struct rx_stress_adv_set *adv_set) +{ + uint8_t own_addr_type; + struct ble_gap_ext_adv_params params; + struct ble_hs_adv_fields fields; + struct os_mbuf *adv_data; + ble_addr_t addr; + const char *name; + int rc; + int pattern_len; + + /* Determine own address type */ + rc = ble_hs_id_infer_auto(0, &own_addr_type); + if (rc != 0) { + MODLOG_DFLT(ERROR, "\033[0;31mError determining own address type; " + "rc=%d\033[0m\n", rc); + return; + } + + /* Use defaults for non-set fields */ + memset(&fields, 0, sizeof fields); + /* General Discoverable and BrEdrNotSupported */ + fields.flags = BLE_HS_ADV_F_DISC_GEN | BLE_HS_ADV_F_BREDR_UNSUP; + /* Set device name as instance name */ + name = ble_svc_gap_device_name(); + fields.name = (uint8_t *) name; + fields.name_len = strlen(name); + fields.name_is_complete = 1; + /* Set UUID 128 data service */ + fields.svc_data_uuid128 = adv_set->instance_uuid128; + fields.svc_data_uuid128_len = 16; + + /* Use defaults for non-set params */ + memset(¶ms, 0, sizeof(params)); + /* Set adv mode */ + if (adv_set->legacy_pdu == 1) { + params.connectable = 1; + params.scannable = 1; + } else if (adv_set->pattern_len < 255) { + params.connectable = 1; + } + + params.own_addr_type = own_addr_type; + params.primary_phy = BLE_HCI_LE_PHY_1M; + /* If legacy, this param will be lowered by API */ + params.secondary_phy = BLE_HCI_LE_PHY_2M; + params.sid = adv_set->instance; + params.legacy_pdu = adv_set->legacy_pdu; + + ble_gap_set_prefered_default_le_phy(TX_PHY_MASK, RX_PHY_MASK); + + rc = ble_gap_ext_adv_remove(adv_set->instance); + assert(rc == 0 || rc == BLE_HS_EALREADY); + + /* Configure instance with the params set */ + rc = ble_gap_ext_adv_configure(adv_set->instance, ¶ms, + NULL, adv_set->cb, NULL); + assert (rc == 0); + + if (own_addr_type == 0) { + rc = ble_hs_id_copy_addr(BLE_ADDR_PUBLIC, addr.val, NULL); + assert (rc == 0); + } else { + rc = ble_hs_id_gen_rnd(1, &addr); + assert (rc == 0); + + /* Set random address for advertising instance */ + rc = ble_gap_ext_adv_set_addr(adv_set->instance, &addr); + assert (rc == 0); + } + + /* Get mbuf for adv data */ + adv_data = os_msys_get_pkthdr(16, 0); + assert(adv_data != NULL); + + /* Fill mbuf with adv fields - structured data */ + rc = ble_hs_adv_set_fields_mbuf(&fields, adv_data); + + if (rc) { + os_mbuf_free_chain(adv_data); + assert(0); + } + + pattern_len = min(adv_set->pattern_len, + MYNEWT_VAL(BLE_EXT_ADV_MAX_SIZE) - adv_data->om_len); + + /* Append to mbuf pattern data - raw data */ + rc = os_mbuf_append(adv_data, adv_set->pattern_data, pattern_len); + + if (rc) { + os_mbuf_free_chain(adv_data); + assert(0); + } + + /* Include mbuf data in advertisement */ + rc = ble_gap_ext_adv_set_data(adv_set->instance, adv_data); + assert (rc == 0); + + /* Start advertising */ + rc = ble_gap_ext_adv_start(adv_set->instance, 0, 0); + assert (rc == 0); + + MODLOG_DFLT(INFO, "instance %u started\n", adv_set->instance); +} + +static int +rx_stress_0_gap_event(struct ble_gap_event *event, void *arg) +{ + switch (event->type) { + case BLE_GAP_EVENT_CONNECT: + /* A new connection was established or a connection attempt failed */ + if (event->connect.status == 0) { + MODLOG_DFLT(INFO, "\nConnection established; status=%d; num=%d\n", + event->connect.status, + ++rx_stress_ctx->con_stat[0].num); + + /* Stop test advert */ + ble_gap_ext_adv_stop(TEST_INSTANCE); + ble_gap_terminate(event->connect.conn_handle, + BLE_ERR_REM_USER_CONN_TERM); + } else { + /* Connection failed; resume advertising */ + MODLOG_DFLT(INFO, "Connection failed; status=%d ", + event->connect.status); + } + return 0; + case BLE_GAP_EVENT_DISCONNECT: + MODLOG_DFLT(INFO, "Disconnect; reason=%d \n", + event->disconnect.reason); + console_printf("\033[0;32mReceived signal to switch test\033[0m\n"); + /* Add token to semaphore. Main task will start next test. */ + os_sem_release(&rx_stress_main_sem); + + return 0; + default: + MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type); + return 0; + } +} + +static int +rx_stress_2_gap_event(struct ble_gap_event *event, void *arg) +{ + switch (event->type) { + case BLE_GAP_EVENT_CONNECT: + ++rx_stress_ctx->con_stat[2].attempts_num; + /* A new connection was established or a connection attempt failed. */ + if (event->connect.status == 0) { + MODLOG_DFLT(INFO, "\nConnection established; status=%d; num=%d\n", + event->connect.status, + ++rx_stress_ctx->con_stat[2].num); + } else { + /* Connection failed; resume advertising. */ + MODLOG_DFLT(INFO, "Connection failed; status=%d ", + event->connect.status); + + rx_stress_adv_start(TEST_INSTANCE); + } + return 0; + + case BLE_GAP_EVENT_DISCONNECT: + MODLOG_DFLT(INFO, "Disconnect; reason=%d \n", + event->disconnect.reason); + console_printf("\033[0;32m>\033[0m"); + if (rx_stress_ctx->con_stat[2].num >= MYNEWT_VAL(BLE_STRESS_REPEAT)) { + rx_stress_on_test_finish(2); + } else { + /* Connection terminated; resume advertising. */ + rx_stress_adv_start(TEST_INSTANCE); + } + return 0; + + default: + MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type); + return 0; + } +} + +static int +rx_stress_3_gap_event(struct ble_gap_event *event, void *arg) +{ + switch (event->type) { + case BLE_GAP_EVENT_CONNECT: + ++rx_stress_ctx->con_stat[3].attempts_num; + /* A new connection was established or a connection attempt failed. */ + if (event->connect.status == 0) { + MODLOG_DFLT(INFO, "\nConnection established; status=%d; num=%d\n", + event->connect.status, + ++rx_stress_ctx->con_stat[3].num); + } else { + /* Connection failed; resume advertising. */ + rx_stress_adv_start(TEST_INSTANCE); + } + return 0; + + case BLE_GAP_EVENT_DISCONNECT: + MODLOG_DFLT(INFO, "Disconnect; reason=%d \n", + event->disconnect.reason); + console_printf("\033[0;32m>\033[0m"); + if (rx_stress_ctx->con_stat[3].num >= MYNEWT_VAL(BLE_STRESS_REPEAT)) { + rx_stress_on_test_finish(3); + } else { + /* Connection terminated; resume advertising. */ + rx_stress_adv_start(TEST_INSTANCE); + } + return 0; + + default: + MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type); + return 0; + } +} + +static int +rx_stress_4_gap_event(struct ble_gap_event *event, void *arg) +{ + struct ble_gap_conn_desc desc; + int rc; + + switch (event->type) { + case BLE_GAP_EVENT_CONNECT: + ++rx_stress_ctx->con_stat[4].attempts_num; + /* A new connection was established or a connection attempt failed. */ + if (event->connect.status == 0) { + MODLOG_DFLT(INFO, "Connection established; status=%d; num=%d", + event->connect.status, + ++rx_stress_ctx->con_stat[4].num); + + /* Remember connection handler */ + rc = ble_gap_conn_find(event->connect.conn_handle, &desc); + assert(rc == 0); + rx_stress_ctx->conn_handle = event->connect.conn_handle; + } else { + /* Connection failed; resume advertising. */ + MODLOG_DFLT(INFO, "Connection failed; status=%d ", + event->connect.status); + rx_stress_adv_start(TEST_INSTANCE); + } + return 0; + + case BLE_GAP_EVENT_DISCONNECT: + MODLOG_DFLT(INFO, "Disconnect; reason=%d ", event->disconnect.reason); + if (rx_stress_ctx->con_stat[4].prms_upd_num >= + MYNEWT_VAL(BLE_STRESS_REPEAT)) { + rx_stress_on_test_finish(4); + } else { + /* Connection terminated; resume advertising. */ + rx_stress_adv_start(TEST_INSTANCE); + } + return 0; + + case BLE_GAP_EVENT_CONN_UPDATE: + if (event->conn_update.status != 0) { + MODLOG_DFLT(INFO, "Connection update failed\n"); + } else { + MODLOG_DFLT(INFO, "Connection updated; num=%d\n", + ++rx_stress_ctx->con_stat[4].prms_upd_num); + console_printf("\033[0;32m>\033[0m"); + } + + if (rx_stress_ctx->con_stat[4].prms_upd_num >= + MYNEWT_VAL(BLE_STRESS_REPEAT)) { + /* Test completed. */ + ble_gap_terminate(rx_stress_ctx->conn_handle, + BLE_ERR_REM_USER_CONN_TERM); + } + return 0; + + default: + MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type); + return 0; + } +} + +static int +rx_stress_5_con_update(void) +{ + int rc; + + /* With every next update at least one param must change. Otherwise no + * event occurs and test will not be continued */ + struct ble_gap_upd_params params = { + .itvl_min = BLE_GAP_INITIAL_CONN_ITVL_MIN, + .itvl_max = BLE_GAP_INITIAL_CONN_ITVL_MAX, + .latency = BLE_GAP_INITIAL_CONN_LATENCY, + /* So let's change e.g. timeout value. Put ...% 2 ? 1 : 2 to make sure + * that value won't grow significantly and will be different with every + * iteration. */ + .supervision_timeout = BLE_GAP_INITIAL_SUPERVISION_TIMEOUT + + (rx_stress_ctx->con_stat[5].prms_upd_num % 2 ? + 1 : 2), + .min_ce_len = BLE_GAP_INITIAL_CONN_MIN_CE_LEN, + .max_ce_len = BLE_GAP_INITIAL_CONN_MAX_CE_LEN, + }; + + rc = ble_gap_update_params(rx_stress_ctx->conn_handle, ¶ms); + + if (rc == BLE_HS_ENOTCONN) { + MODLOG_DFLT(INFO, "Device disconnected. Connection update failed\n"); + assert(0); + } + + if (rc != 0) { + MODLOG_DFLT(ERROR, "\033[0;31mError during connection update; " + "rc=%d\033[0m\n", rc); + assert(0); + } + + return rc; +} + +static int +rx_stress_5_gap_event(struct ble_gap_event *event, void *arg) +{ + struct ble_gap_conn_desc desc; + int rc; + + switch (event->type) { + case BLE_GAP_EVENT_CONNECT: + ++rx_stress_ctx->con_stat[5].attempts_num; + + /* A new connection was established or a connection attempt failed. */ + if (event->connect.status == 0) { + MODLOG_DFLT(INFO, "Connection established; status=%d; num=%d", + event->connect.status, + ++rx_stress_ctx->con_stat[5].num); + + /* Remember connection handler */ + rc = ble_gap_conn_find(event->connect.conn_handle, &desc); + assert(rc == 0); + rx_stress_ctx->conn_handle = event->connect.conn_handle; + + /* Update connection. */ + rc = rx_stress_5_con_update(); + assert(rc == 0); + } else { + /* Connection failed; resume advertising. */ + MODLOG_DFLT(INFO, "Connection failed; status=%d ", + event->connect.status); + rx_stress_adv_start(TEST_INSTANCE); + } + return 0; + + case BLE_GAP_EVENT_DISCONNECT: + MODLOG_DFLT(INFO, "Disconnect; reason=%d ", event->disconnect.reason); + if (rx_stress_ctx->con_stat[5].prms_upd_num >= + MYNEWT_VAL(BLE_STRESS_REPEAT)) { + rx_stress_on_test_finish(5); + } else { + /* Connection terminated; resume advertising. */ + rx_stress_adv_start(TEST_INSTANCE); + } + return 0; + + case BLE_GAP_EVENT_CONN_UPDATE: + if (event->conn_update.status != 0) { + MODLOG_DFLT(INFO, "Connection update failed\n"); + } else { + MODLOG_DFLT(INFO, "Connection updated; num=%d\n", + ++rx_stress_ctx->con_stat[5].prms_upd_num); + console_printf("\033[0;32m>\033[0m"); + } + + if (rx_stress_ctx->con_stat[5].prms_upd_num >= + MYNEWT_VAL(BLE_STRESS_REPEAT)) { + /* Test completed. */ + ble_gap_terminate(rx_stress_ctx->conn_handle, + BLE_ERR_REM_USER_CONN_TERM); + } else { + /* Update connection. */ + rc = rx_stress_5_con_update(); + assert(rc == 0); + } + return 0; + + default: + MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type); + return 0; + } +} + +static int +rx_stress_6_gap_event(struct ble_gap_event *event, void *arg) +{ + MODLOG_DFLT(INFO, "Event occurs=%d\n", event->type); + return 0; +} + +static int +rx_stress_7_gap_event(struct ble_gap_event *event, void *arg) +{ + struct ble_gap_conn_desc desc; + int rc; + + switch (event->type) { + case BLE_GAP_EVENT_CONNECT: + ++rx_stress_ctx->con_stat[7].attempts_num; + + /* A new connection was established or a connection attempt failed. */ + if (event->connect.status == 0) { + MODLOG_DFLT(INFO, "Connection established; status=%d; num=%d", + event->connect.status, + ++rx_stress_ctx->con_stat[7].num); + + /* Remember connection handler */ + rc = ble_gap_conn_find(event->connect.conn_handle, &desc); + assert(rc == 0); + rx_stress_ctx->conn_handle = event->connect.conn_handle; + } else { + /* Connection failed; resume advertising. */ + MODLOG_DFLT(INFO, "Connection failed; status=%d ", + event->connect.status); + rx_stress_adv_start(TEST_INSTANCE); + } + return 0; + + case BLE_GAP_EVENT_DISCONNECT: + MODLOG_DFLT(INFO, "Disconnect; reason=%d ", event->disconnect.reason); + if (rx_stress_ctx->con_stat[7].phy_upd_num >= + MYNEWT_VAL(BLE_STRESS_REPEAT)) { + rx_stress_on_test_finish(7); + } else { + /* Connection terminated; resume advertising. */ + rx_stress_adv_start(TEST_INSTANCE); + } + return 0; + + case BLE_GAP_EVENT_PHY_UPDATE_COMPLETE: + if (event->phy_updated.status != 0) { + MODLOG_DFLT(INFO, "PHY update failed\n"); + } else { + MODLOG_DFLT(INFO, "PHY updated; num=%d\n", + ++rx_stress_ctx->con_stat[7].phy_upd_num); + console_printf("\033[0;32m>\033[0m"); + } + + if (rx_stress_ctx->con_stat[7].phy_upd_num >= + MYNEWT_VAL(BLE_STRESS_REPEAT)) { + /* Test completed. */ + ble_gap_terminate(rx_stress_ctx->conn_handle, + BLE_ERR_REM_USER_CONN_TERM); + } + return 0; + + default: + MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type); + return 0; + } +} + +static int +rx_stress_8_con_update(void) +{ + int rc; + uint8_t tx_phys_mask; + uint8_t rx_phys_mask; + + ble_gap_read_le_phy(rx_stress_ctx->conn_handle, &tx_phys_mask, + &rx_phys_mask); + + /* With every next update at least one param must change */ + switch (rx_phys_mask) { + case BLE_GAP_LE_PHY_1M_MASK: + rx_phys_mask = BLE_GAP_LE_PHY_2M_MASK; + break; + case BLE_GAP_LE_PHY_2M_MASK: +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CODED_PHY) + rx_phys_mask = BLE_GAP_LE_PHY_CODED_MASK; + break; + case BLE_GAP_LE_PHY_CODED_MASK: +#endif + rx_phys_mask = BLE_GAP_LE_PHY_1M_MASK; + break; + default: + rx_phys_mask = BLE_GAP_LE_PHY_1M_MASK; + break; + } + + switch (tx_phys_mask) { + case BLE_GAP_LE_PHY_1M_MASK: + tx_phys_mask = BLE_GAP_LE_PHY_2M_MASK; + break; + case BLE_GAP_LE_PHY_2M_MASK: +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CODED_PHY) + tx_phys_mask = BLE_GAP_LE_PHY_CODED_MASK; + break; + case BLE_GAP_LE_PHY_CODED_MASK: +#endif + tx_phys_mask = BLE_GAP_LE_PHY_1M_MASK; + break; + default: + tx_phys_mask = BLE_GAP_LE_PHY_1M_MASK; + break; + } + + rc = ble_gap_set_prefered_le_phy(rx_stress_ctx->conn_handle, + tx_phys_mask, rx_phys_mask, 0); + + if (rc == BLE_HS_ENOTCONN) { + MODLOG_DFLT(INFO, "Device disconnected. Connection update failed\n"); + return rc; + } + + if (rc != 0) { + MODLOG_DFLT(ERROR, "\033[0;31mError during PHY update; " + "rc=%d\033[0m\n", rc); + } + + return rc; +} + +static int +rx_stress_8_gap_event(struct ble_gap_event *event, void *arg) +{ + struct ble_gap_conn_desc desc; + int rc; + + switch (event->type) { + case BLE_GAP_EVENT_CONNECT: + ++rx_stress_ctx->con_stat[8].attempts_num; + + /* A new connection was established or a connection attempt failed. */ + if (event->connect.status == 0) { + MODLOG_DFLT(INFO, "Connection established; status=%d; num=%d", + event->connect.status, + ++rx_stress_ctx->con_stat[8].num); + + /* Remember connection handler */ + rc = ble_gap_conn_find(event->connect.conn_handle, &desc); + assert(rc == 0); + rx_stress_ctx->conn_handle = event->connect.conn_handle; + + /* Update connection. */ + rc = rx_stress_8_con_update(); + assert(rc == 0); + } else { + /* Connection failed; resume advertising. */ + MODLOG_DFLT(INFO, "Connection failed; status=%d ", + event->connect.status); + rx_stress_adv_start(TEST_INSTANCE); + } + return 0; + + case BLE_GAP_EVENT_DISCONNECT: + MODLOG_DFLT(INFO, "Disconnect; reason=%d ", event->disconnect.reason); + if (rx_stress_ctx->con_stat[8].phy_upd_num >= + MYNEWT_VAL(BLE_STRESS_REPEAT)) { + rx_stress_on_test_finish(8); + } else { + /* Connection terminated; resume advertising. */ + rx_stress_adv_start(TEST_INSTANCE); + } + return 0; + + case BLE_GAP_EVENT_PHY_UPDATE_COMPLETE: + if (event->phy_updated.status != 0) { + MODLOG_DFLT(INFO, "PHY update failed\n"); + } else { + MODLOG_DFLT(INFO, "PHY updated; num=%d; rx:%d, tx:%d\n", + ++rx_stress_ctx->con_stat[8].phy_upd_num, + event->phy_updated.rx_phy, event->phy_updated.tx_phy); + console_printf("\033[0;32m>\033[0m"); + } + + if (rx_stress_ctx->con_stat[8].phy_upd_num >= + MYNEWT_VAL(BLE_STRESS_REPEAT)) { + /* Test completed. */ + ble_gap_terminate(rx_stress_ctx->conn_handle, + BLE_ERR_REM_USER_CONN_TERM); + } else { + /* Update connection. */ + rc = rx_stress_8_con_update(); + assert(rc == 0); + } + return 0; + + default: + MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type); + return 0; + } +} + +static int +rx_stress_9_gap_event(struct ble_gap_event *event, void *arg) +{ + switch (event->type) { + case BLE_GAP_EVENT_CONNECT: + ++rx_stress_ctx->con_stat[9].attempts_num; + + /* A new connection was established or a connection attempt failed. */ + if (event->connect.status == 0) { + MODLOG_DFLT(INFO, "Connection established; status=%d; num=%d", + event->connect.status, + ++rx_stress_ctx->con_stat[9].num); + console_printf("\033[0;32m>\033[0m"); + + /* Remember max number of established connections */ + if (rx_stress_ctx->con_stat[9].num > + rx_stress_ctx->con_stat[9].max_num) { + rx_stress_ctx->con_stat[9].max_num = rx_stress_ctx->con_stat[9].num; + } + } else { + /* Connection failed; resume advertising. */ + MODLOG_DFLT(INFO, "Connection failed; status=%d ", + event->connect.status); + } + return 0; + + case BLE_GAP_EVENT_DISCONNECT: + MODLOG_DFLT(INFO, "Disconnect; reason=%d ", event->disconnect.reason); + console_printf("\033[0;31mX\033[0m"); + MODLOG_DFLT(INFO, "Connections num: %d\n", + --rx_stress_ctx->con_stat[9].num); + + if (rx_stress_ctx->con_stat[9].num != 0 && + rx_stress_ctx->con_stat[9].num < + MYNEWT_VAL(BLE_MAX_CONNECTIONS) + 1) { + rx_stress_adv_start_with_rand_addr(TEST_INSTANCE); + } else { + /* When TX device has terminated all connections, stop advertising. */ + ble_gap_ext_adv_stop(TEST_INSTANCE); + rx_stress_on_test_finish(9); + } + return 0; + + case BLE_GAP_EVENT_ADV_COMPLETE: + /* Stop test when TX device has terminated all connections or + * number of connections has reached the max possible value. */ + if (rx_stress_ctx->con_stat[9].num != 0 && + rx_stress_ctx->con_stat[9].num < + MYNEWT_VAL(BLE_MAX_CONNECTIONS) + 1) { + rx_stress_adv_start_with_rand_addr(TEST_INSTANCE); + } + return 0; + + default: + MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type); + } + return 0; +} + +static void +tx_stress_10_l2cap_update_event(uint16_t conn_handle, int status, void *arg) +{ + if (status == 0) { + MODLOG_DFLT(INFO, "L2CAP params updated\n"); + } else { + MODLOG_DFLT(INFO, "L2CAP params update failed; rc=%d\n", status); + assert(0); + } +} + +static int +rx_stress_10_l2cap_event(struct ble_l2cap_event *event, void *arg) +{ + int rc; + struct os_mbuf *data_buf; + static int data_len = 1000; + static int send_cnt = 0; + static bool stalled = false; + struct ble_l2cap_chan_info chan_info; + + switch (event->type) { + case BLE_L2CAP_EVENT_COC_CONNECTED: + if (event->connect.status) { + MODLOG_DFLT(INFO, "LE COC error: %d\n", event->connect.status); + return 0; + } + + ble_l2cap_get_chan_info(event->connect.chan, &chan_info); + + MODLOG_DFLT(INFO, + "LE COC connected, conn: %d, chan: 0x%08lx, scid: 0x%04x, " + "dcid: 0x%04x, our_mtu: 0x%04x, peer_mtu: 0x%04x\n", + event->connect.conn_handle, + (uint32_t) event->connect.chan, + chan_info.scid, + chan_info.dcid, + chan_info.our_l2cap_mtu, + chan_info.peer_l2cap_mtu); + + struct ble_l2cap_sig_update_params params = { + .itvl_min = 0x0006,//BLE_GAP_INITIAL_CONN_ITVL_MIN + .itvl_max = 0x0006,//BLE_GAP_INITIAL_CONN_ITVL_MIN + .slave_latency = 0x0000, + .timeout_multiplier = 0x0100, + }; + + rc = ble_l2cap_sig_update(event->connect.conn_handle, ¶ms, + &tx_stress_10_l2cap_update_event, NULL); + assert(rc == 0); + return 0; + + case BLE_L2CAP_EVENT_COC_DISCONNECTED: + MODLOG_DFLT(INFO, "LE CoC disconnected, chan: 0x%08lx\n", + (uint32_t) event->disconnect.chan); + return 0; + + case BLE_L2CAP_EVENT_COC_ACCEPT: + stress_l2cap_coc_accept(event->accept.peer_sdu_size, + event->accept.chan); + return 0; + + case BLE_L2CAP_EVENT_COC_DATA_RECEIVED: + stress_l2cap_coc_recv(event->receive.chan, event->receive.sdu_rx); + + MODLOG_DFLT(INFO, "L2CAP server received data; num=%d\n", + ++rx_stress_ctx->rcv_num); + rx_stress_ctx->chan = event->receive.chan; + + /* In this use case, receiving any data by RX device L2CAP server means + * request from TX device to send data. */ + + /* Do not send if stalled on the last sending. */ + if (stalled) { + return 0; + } + break; + + case BLE_L2CAP_EVENT_COC_TX_UNSTALLED: + MODLOG_DFLT(INFO, "L2CAP unstalled event\n"); + + stalled = false; + + /* Send if was stalled on the last request to send. */ + if (rx_stress_ctx->rcv_num > send_cnt) { + break; + } + return 0; + + default: + MODLOG_DFLT(INFO, "Other L2CAP event occurs: %d\n", event->type); + return 0; + } + + /* Send pattern data */ + + /* Get mbuf for adv data */ + data_buf = os_msys_get_pkthdr(data_len, 0); + + MODLOG_DFLT(INFO, "Data buf %s\n", data_buf ? "OK" : "NOK"); + assert(data_buf != NULL); + + /* Fill mbuf with the pattern */ + stress_fill_mbuf_with_pattern(data_buf, data_len); + + /* Send data */ + rc = ble_l2cap_send(rx_stress_ctx->chan, data_buf); + MODLOG_DFLT(INFO, "Return code=%d\n", rc); + if (rc) { + if (rc == BLE_HS_ESTALLED) { + MODLOG_DFLT(INFO, "L2CAP stalled - waiting\n"); + stalled = true; + } else { + MODLOG_DFLT(INFO, "Sending data via L2CAP failed with error " + "code %d\n", rc); + } + } + + MODLOG_DFLT(INFO, " %d, %d\n", ++send_cnt, data_len); + data_len += 500; + + return 0; +} + +static int +rx_stress_10_gap_event(struct ble_gap_event *event, void *arg) +{ + struct ble_gap_conn_desc out_desc; + + switch (event->type) { + case BLE_GAP_EVENT_CONNECT: + ++rx_stress_ctx->con_stat[10].attempts_num; + + /* A new connection was established or a connection attempt failed. */ + if (event->connect.status == 0) { + MODLOG_DFLT(INFO, "Connection established; status=%d; num=%d", + event->connect.status, + ++rx_stress_ctx->con_stat[10].num); + + ble_gap_conn_find(event->connect.conn_handle, &out_desc); + MODLOG_DFLT(INFO, "Address %s", + addr_str(out_desc.peer_id_addr.val)); + } else { + /* Connection failed; resume advertising. */ + MODLOG_DFLT(INFO, "Connection failed; status=%d ", + event->connect.status); + } + return 0; + + case BLE_GAP_EVENT_DISCONNECT: + MODLOG_DFLT(INFO, "Disconnect; reason=%d ", event->disconnect.reason); + rx_stress_ctx->completed[10] = true; + rx_stress_on_test_finish(10); + return 0; + + default: + MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type); + return 0; + } +} + +static int +rx_stress_11_gap_event(struct ble_gap_event *event, void *arg) +{ + switch (event->type) { + case BLE_GAP_EVENT_CONNECT: + ++rx_stress_ctx->con_stat[11].attempts_num; + /* A new connection was established or a connection attempt failed. */ + if (event->connect.status == 0) { + MODLOG_DFLT(INFO, "\nConnection established; status=%d; num=%d\n", + event->connect.status, + ++rx_stress_ctx->con_stat[11].num); + } else { + MODLOG_DFLT(INFO, "\033[0;31mError: connection attempt failed; " + "status=%d\033[0m\n", event->connect.status); + } + + ble_gap_terminate(event->connect.conn_handle, + BLE_ERR_REM_USER_CONN_TERM); + return 0; + + case BLE_GAP_EVENT_DISCONNECT: + MODLOG_DFLT(INFO, "Disconnect; reason=%d \n", + event->disconnect.reason); + console_printf("\033[0;32m>\033[0m"); + if (rx_stress_ctx->con_stat[11].num >= MYNEWT_VAL(BLE_STRESS_REPEAT)) { + rx_stress_on_test_finish(11); + } else { + /* Connection terminated; resume advertising. */ + rx_stress_adv_start(TEST_INSTANCE); + } + return 0; + + default: + MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type); + return 0; + } +} + +static int +rx_stress_12_gap_event(struct ble_gap_event *event, void *arg) +{ + int om_len = 10000; + struct os_mbuf *om; + int64_t us = 0; + int rc; + + switch (event->type) { + case BLE_GAP_EVENT_CONNECT: + /* A new connection was established or a connection attempt failed */ + if (event->connect.status == 0) { + MODLOG_DFLT(INFO, "\nConnection established; status=%d; num=%d\n", + event->connect.status, + ++rx_stress_ctx->con_stat[12].num); + rx_stress_ctx->conn_handle = event->connect.conn_handle; + + break; + } else { + /* Connection failed; resume advertising */ + MODLOG_DFLT(INFO, "Connection failed; status=%d ", + event->connect.status); + } + return 0; + + case BLE_GAP_EVENT_DISCONNECT: + MODLOG_DFLT(INFO, "Disconnect; reason=%d \n", + event->disconnect.reason); + + rx_stress_ctx->s12_notif_time = rx_stress_ctx->time_sum / + rx_stress_ctx->send_num; + + MODLOG_DFLT(INFO, "Average time: %d us\n", + rx_stress_ctx->s12_notif_time); + + rx_stress_on_test_finish(12); + return 0; + + case BLE_GAP_EVENT_NOTIFY_TX: + rx_stress_ctx->end_us = os_get_uptime_usec(); + MODLOG_DFLT(INFO, "Notify TX event\n"); + + if (!event->notify_tx.status) { + /* Send next only after previous indication is done */ + return 0; + } + assert(event->notify_tx.status == BLE_HS_EDONE); + + if (rx_stress_ctx->send_num++ >= MYNEWT_VAL(BLE_STRESS_REPEAT)) { + ble_gap_terminate(event->notify_tx.conn_handle, + BLE_ERR_REM_USER_CONN_TERM); + return 0; + } + + /* Time of data sending */ + us = rx_stress_ctx->end_us - rx_stress_ctx->begin_us; + console_printf("Indication time: %lld\n", us); + rx_stress_ctx->time_sum += us; + console_printf("\033[0;32m>\033[0m"); + break; + + default: + MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type); + return 0; + } + + /* Indicate data pattern */ + rx_stress_ctx->begin_us = os_get_uptime_usec(); + om = os_msys_get_pkthdr(om_len, 0); + stress_fill_mbuf_with_pattern(om, om_len); + rc = ble_gatts_indicate_custom(rx_stress_ctx->conn_handle, hrs_hrm_handle, + om); + assert(rc == 0); + return 0; +} + +static struct ble_npl_event rx_stress_13_notify_ev; + +static void +rx_stress_13_notify_ev_func(struct ble_npl_event *ev) +{ + struct os_mbuf *om; + int rc; + + om = ble_hs_mbuf_from_flat(test_6_pattern, 10); + rc = ble_gatts_notify_custom(rx_stress_ctx->conn_handle, + hrs_hrm_handle, om); + assert(rc == 0); + +} + +static int +rx_stress_13_gap_event(struct ble_gap_event *event, void *arg) +{ + switch (event->type) { + case BLE_GAP_EVENT_CONNECT: + /* A new connection was established or a connection attempt failed */ + if (event->connect.status == 0) { + MODLOG_DFLT(INFO, "\nConnection established; status=%d; num=%d\n", + event->connect.status, + ++rx_stress_ctx->con_stat[13].num); + rx_stress_ctx->conn_handle = event->connect.conn_handle; + + rx_stress_ctx->begin_us = os_get_uptime_usec(); + + ble_npl_eventq_put((struct ble_npl_eventq *)os_eventq_dflt_get(), + &rx_stress_13_notify_ev); + break; + } else { + /* Connection failed; resume advertising */ + MODLOG_DFLT(INFO, "Connection failed; status=%d ", + event->connect.status); + assert(0); + } + return 0; + + case BLE_GAP_EVENT_DISCONNECT: + MODLOG_DFLT(INFO, "Disconnect; reason=%d \n", + event->disconnect.reason); + + rx_stress_ctx->time_sum = rx_stress_ctx->end_us - + rx_stress_ctx->begin_us; + + rx_stress_ctx->s13_notif_time = rx_stress_ctx->time_sum / + rx_stress_ctx->send_num; + + MODLOG_DFLT(INFO, "Average time: %d us\n", + rx_stress_ctx->s13_notif_time); + rx_stress_on_test_finish(13); + return 0; + + case BLE_GAP_EVENT_NOTIFY_TX: + MODLOG_DFLT(INFO, "Notify TX event; num=%d\n", + ++rx_stress_ctx->send_num); + assert(event->notify_tx.status == 0); + + if (rx_stress_ctx->send_num >= MYNEWT_VAL(BLE_STRESS_REPEAT)) { + rx_stress_ctx->end_us = os_get_uptime_usec(); + ble_gap_terminate(event->connect.conn_handle, + BLE_ERR_REM_USER_CONN_TERM); + return 0; + } + + ble_npl_eventq_put((struct ble_npl_eventq *)os_eventq_dflt_get(), + &rx_stress_13_notify_ev); + break; + + default: + MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type); + return 0; + } + + om = ble_hs_mbuf_from_flat(test_6_pattern, 10); + rc = ble_gatts_notify_custom(rx_stress_ctx->conn_handle, + hrs_hrm_handle, om); + assert(rc == 0); + return 0; +} + +static int +rx_stress_14_gap_event(struct ble_gap_event *event, void *arg) +{ + int bytes_num = 10000; + static struct os_mbuf *om = NULL; + int rc; + + switch (event->type) { + case BLE_GAP_EVENT_CONNECT: + /* A new connection was established or a connection attempt failed */ + if (event->connect.status == 0) { + MODLOG_DFLT(INFO, "\nConnection established; status=%d; num=%d\n", + event->connect.status, + ++rx_stress_ctx->con_stat[14].num); + rx_stress_ctx->conn_handle = event->connect.conn_handle; + } else { + /* Connection failed; resume advertising */ + MODLOG_DFLT(INFO, "Connection failed; status=%d ", + event->connect.status); + assert(0); + } + return 0; + + case BLE_GAP_EVENT_DISCONNECT: + MODLOG_DFLT(INFO, "Disconnect; reason=%d \n", + event->disconnect.reason); + + rx_stress_ctx->s14_notif_time = rx_stress_ctx->time_sum / + rx_stress_ctx->send_num; + + MODLOG_DFLT(INFO, "Average time: %d us\n", + rx_stress_ctx->s14_notif_time); + + rx_stress_on_test_finish(14); + return 0; + + case BLE_GAP_EVENT_NOTIFY_TX: + MODLOG_DFLT(INFO, "Notify TX event\n"); + assert(event->notify_tx.status == 0); + return 0; + + case BLE_GAP_EVENT_SUBSCRIBE: + MODLOG_DFLT(INFO, "Subscribe event\n"); + + if (event->subscribe.cur_notify) { + MODLOG_DFLT(INFO, "Notification subscribed\n"); + + ++rx_stress_ctx->send_num; + + /* Notify data pattern */ + om = ble_hs_mbuf_from_flat(test_6_pattern, bytes_num); + + rc = ble_gatts_notify_custom(rx_stress_ctx->conn_handle, + hrs_hrm_handle, om); + assert(rc == 0); + + console_printf("\033[0;32m>\033[0m"); + } else if (event->subscribe.prev_notify) { + MODLOG_DFLT(INFO, "Notification unsubscribed\n"); + } else { + MODLOG_DFLT(INFO, "Other subscription\n"); + } + return 0; + + default: + MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type); + return 0; + } +} + +static int +rx_stress_15_gap_event(struct ble_gap_event *event, void *arg) +{ + switch (event->type) { + case BLE_GAP_EVENT_CONNECT: + ++rx_stress_ctx->con_stat[15].attempts_num; + /* A new connection was established or a connection attempt failed. */ + if (event->connect.status == 0) { + MODLOG_DFLT(INFO, "\nConnection established; status=%d; num=%d\n", + event->connect.status, + ++rx_stress_ctx->con_stat[15].num); + } else { + MODLOG_DFLT(INFO, "\033[0;31mError: connection attempt failed; " + "status=%d\033[0m\n", event->connect.status); + assert(0); + } + return 0; + + case BLE_GAP_EVENT_DISCONNECT: + MODLOG_DFLT(INFO, "Disconnect; reason=%d \n", + event->disconnect.reason); + console_printf("\033[0;32m>\033[0m"); + if (rx_stress_ctx->con_stat[15].num >= MYNEWT_VAL(BLE_STRESS_REPEAT)) { + rx_stress_on_test_finish(15); + } else { + /* Connection terminated; resume advertising. */ + rx_stress_adv_start(TEST_INSTANCE); + } + return 0; + + default: + MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type); + return 0; + } +} + +/* Advert settings for each test. */ +static struct rx_stress_adv_set rx_stress_adv_sets[] = { + { + .instance = SWITCHER_INSTANCE, + .instance_uuid128 = rx_stress_uuid128[0], + .legacy_pdu = LEGACY_ADVERT, + .cb = rx_stress_0_gap_event, + .pattern_data = NULL, + .pattern_len = 0, + }, + { + .instance = SWITCHER_INSTANCE, + .instance_uuid128 = rx_stress_uuid128[0], + .legacy_pdu = LEGACY_ADVERT, + .cb = rx_stress_0_gap_event, + .pattern_data = NULL, + .pattern_len = 0, + }, + { + .instance = TEST_INSTANCE, + .instance_uuid128 = rx_stress_uuid128[2], + .legacy_pdu = LEGACY_ADVERT, + .cb = rx_stress_2_gap_event, + .pattern_data = NULL, + .pattern_len = 0, + }, + { + .instance = TEST_INSTANCE, + .instance_uuid128 = rx_stress_uuid128[3], + .legacy_pdu = EXTENDED_ADVERT, + .cb = rx_stress_3_gap_event, + .pattern_data = NULL, + .pattern_len = 0, + }, + { + .instance = TEST_INSTANCE, + .instance_uuid128 = rx_stress_uuid128[4], + .legacy_pdu = LEGACY_ADVERT, + .cb = rx_stress_4_gap_event, + .pattern_data = NULL, + .pattern_len = 0, + }, + { + .instance = TEST_INSTANCE, + .instance_uuid128 = rx_stress_uuid128[5], + .legacy_pdu = LEGACY_ADVERT, + .cb = rx_stress_5_gap_event, + .pattern_data = NULL, + .pattern_len = 0, + }, + { + .instance = TEST_INSTANCE, + .instance_uuid128 = rx_stress_uuid128[6], + .legacy_pdu = EXTENDED_ADVERT, + .cb = rx_stress_6_gap_event, + .pattern_data = test_6_pattern, + .pattern_len = 1640, + }, + { + .instance = TEST_INSTANCE, + .instance_uuid128 = rx_stress_uuid128[7], + .legacy_pdu = EXTENDED_ADVERT, + .cb = rx_stress_7_gap_event, + .pattern_data = NULL, + .pattern_len = 0, + }, + { + .instance = TEST_INSTANCE, + .instance_uuid128 = rx_stress_uuid128[8], + .legacy_pdu = EXTENDED_ADVERT, + .cb = rx_stress_8_gap_event, + .pattern_data = NULL, + .pattern_len = 0, + }, + { + .instance = TEST_INSTANCE, + .instance_uuid128 = rx_stress_uuid128[9], + .legacy_pdu = EXTENDED_ADVERT, + .cb = rx_stress_9_gap_event, + .pattern_data = NULL, + .pattern_len = 0, + }, + { + .instance = TEST_INSTANCE, + .instance_uuid128 = rx_stress_uuid128[10], + .legacy_pdu = EXTENDED_ADVERT, + .cb = rx_stress_10_gap_event, + .pattern_data = NULL, + .pattern_len = 0, + }, + { + .instance = TEST_INSTANCE, + .instance_uuid128 = rx_stress_uuid128[11], + .legacy_pdu = EXTENDED_ADVERT, + .cb = rx_stress_11_gap_event, + .pattern_data = NULL, + .pattern_len = 0, + }, + { + .instance = TEST_INSTANCE, + .instance_uuid128 = rx_stress_uuid128[12], + .legacy_pdu = EXTENDED_ADVERT, + .cb = rx_stress_12_gap_event, + .pattern_data = NULL, + .pattern_len = 0, + }, + { + .instance = TEST_INSTANCE, + .instance_uuid128 = rx_stress_uuid128[13], + .legacy_pdu = EXTENDED_ADVERT, + .cb = rx_stress_13_gap_event, + .pattern_data = NULL, + .pattern_len = 0, + }, + { + .instance = TEST_INSTANCE, + .instance_uuid128 = rx_stress_uuid128[14], + .legacy_pdu = EXTENDED_ADVERT, + .cb = rx_stress_14_gap_event, + .pattern_data = NULL, + .pattern_len = 0, + }, + { + .instance = TEST_INSTANCE, + .instance_uuid128 = rx_stress_uuid128[15], + .legacy_pdu = EXTENDED_ADVERT, + .cb = rx_stress_15_gap_event, + .pattern_data = NULL, + .pattern_len = 0, + }, +}; + +static void +rx_stress_start(int test_num) +{ + int rc; + + /* Init semaphore with 0 tokens. */ + os_sem_init(&rx_stress_main_sem, 0); + + console_printf("\033[1;36mStart test num %d - ", test_num); + + /* Start test. */ + switch (test_num) { + case 2: + console_printf("Stress Connect/Disconnect legacy\033[0m\n"); + rx_stress_simple_adv(&rx_stress_adv_sets[2]); + break; + case 3: + console_printf("Stress Connect/Disconnect ext adv\033[0m\n"); + rx_stress_simple_adv(&rx_stress_adv_sets[3]); + break; + case 4: + console_printf("Stress connection params update (TX)\033[0m\n"); + rx_stress_simple_adv(&rx_stress_adv_sets[4]); + break; + case 5: + console_printf("Stress connection params update (RX)\033[0m\n"); + rx_stress_simple_adv(&rx_stress_adv_sets[5]); + break; + case 6: + /* Start SWITCHER advert that gives possibility to remotely start + * next test advert */ + console_printf("Stress Scan\033[0m\n"); + rx_stress_simple_adv(&rx_stress_adv_sets[0]); + rx_stress_simple_adv(&rx_stress_adv_sets[6]); + break; + case 7: + console_printf("Stress PHY Update (TX)\033[0m\n"); + rx_stress_simple_adv(&rx_stress_adv_sets[7]); + break; + case 8: + console_printf("Stress PHY Update (RX)\033[0m\n"); + rx_stress_simple_adv(&rx_stress_adv_sets[8]); + break; + case 9: + console_printf("Stress multi connection\033[0m\n"); + rx_stress_simple_adv(&rx_stress_adv_sets[9]); + break; + case 10: + console_printf("Stress L2CAP send\033[0m\n"); + rc = ble_l2cap_create_server(TEST_PSM, STRESS_COC_MTU, + rx_stress_10_l2cap_event, NULL); + assert(rc == 0); + rx_stress_simple_adv(&rx_stress_adv_sets[10]); + break; + case 11: + console_printf("Stress Advertise/Connect/Disconnect\033[0m\n"); + rx_stress_simple_adv(&rx_stress_adv_sets[11]); + break; + case 12: + console_printf("Stress GATT indication\033[0m\n"); + rx_stress_simple_adv(&rx_stress_adv_sets[12]); + break; + case 13: + console_printf("Stress GATT notification\033[0m\n"); + ble_npl_event_init(&rx_stress_13_notify_ev, + rx_stress_13_notify_ev_func, NULL); + rx_stress_simple_adv(&rx_stress_adv_sets[13]); + break; + case 14: + console_printf("Stress GATT Subscribe/Notify/Unsubscribe\033[0m\n"); + rx_stress_simple_adv(&rx_stress_adv_sets[14]); + break; + case 15: + console_printf("Stress Connect/Send/Disconnect\033[0m\n"); + rx_stress_simple_adv(&rx_stress_adv_sets[15]); + break; + default: + console_printf("\033[0;31mFound test, but do not know how to perform." + "\033[0m\n"); + assert(0); + } + + /* Wait for the test to finish. Then 1 token will be released + * allowing to pass through semaphore. */ + os_sem_pend(&rx_stress_main_sem, OS_TIMEOUT_NEVER); + + ble_gap_ext_adv_stop(SWITCHER_INSTANCE); + + stress_clear_ctx_reusable_var(rx_stress_ctx); +} + +static void +stress_uuid_init() +{ + uint8_t i; + + for (i = 0; i < STRESS_UUIDS_NUM; ++i) { + /* Fill all 16 bytes of UUID128 */ + rx_stress_uuid128[i][0] = 0xC0; + rx_stress_uuid128[i][1] = 0xDE; + rx_stress_uuid128[i][2] = i; + memcpy(&rx_stress_uuid128[i][3], MYNEWT_VAL(BLE_STRESS_UUID_BASE), 13); + } +} + +static void +rx_stress_read_command_cb(void) +{ + console_printf("Start testing\n"); + os_sem_release(&rx_stress_main_sem); +} + +static void +rx_stress_main_task_fn(void *arg) +{ + int i; + + stress_uuid_init(); + + console_printf("\033[1;36mRX device\033[0m\n"); + console_printf("Press ENTER to start: \n"); + console_init(&rx_stress_read_command_cb); + + /* Waite for pressing ENTER in console */ + os_sem_pend(&rx_stress_main_sem, OS_TIMEOUT_NEVER); + + /* Standard tests perform */ + for (i = 11; i < STRESS_UUIDS_NUM; ++i) { + if (i == 7 || i == 8 || i == 13) { + /* 7,8: PHY update tests cause that the device during the next test + * will stuck somewhere and will reset. Skip them for now. + * 13: Should work after fixing ble_gatts_notify_custom (nimble issue on GitHub)*/ + continue; + } + /* Start test. */ + rx_stress_start(i); + } + + /* Print tests results */ + com_stress_print_report(rx_stress_ctx); + + /* Task should never return */ + while (1) { + } +} + +void +rx_stress_start_auto() +{ + /* Start task that will run all stress tests one by one. */ + os_task_init(&rx_stress_main_task, "rx_stress_main_task", + rx_stress_main_task_fn, NULL, RX_STRESS_MAIN_TASK_PRIO, + OS_WAIT_FOREVER, rx_stress_main_task_stack, + RX_STRESS_MAIN_TASK_STACK_SIZE); +} diff --git a/lib/bt/host/nimble/nimble/apps/blestress/src/rx_stress.h b/lib/bt/host/nimble/nimble/apps/blestress/src/rx_stress.h new file mode 100644 index 00000000..62f84117 --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/blestress/src/rx_stress.h @@ -0,0 +1,53 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef _BLE_STRESS_RX_H +#define _BLE_STRESS_RX_H + +#include +#include +#include +#include +#include + +/* BLE */ +#include "nimble/ble.h" +#include "host/ble_hs.h" +#include "services/gap/ble_svc_gap.h" +#include "host/ble_gap.h" +#include + +#include "misc.h" +#include "stress.h" +#include "stress_gatt.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Executes stress tests one by one. + */ +void rx_stress_start_auto(); + +#ifdef __cplusplus +} +#endif + +#endif //_BLE_STRESS_RX_H diff --git a/lib/bt/host/nimble/nimble/apps/blestress/src/stress.c b/lib/bt/host/nimble/nimble/apps/blestress/src/stress.c new file mode 100644 index 00000000..1bdbafa9 --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/blestress/src/stress.c @@ -0,0 +1,392 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "stress.h" + +static struct os_callout stress_timer_callout; + +void +com_stress_print_report(const struct com_stress_test_ctx *test_ctxs) +{ + console_printf("\033[0;32mAll tests completed\033[0m\n"); + console_printf("Tests results:\n"); + + console_printf( + "\033[0;33mUse case 1 - Stress Connect -> Connect Cancel: \n\033[0m"); + console_printf("Con attempts = %d\n", test_ctxs->con_stat[1].attempts_num); + console_printf("Con success = %d\n", test_ctxs->con_stat[1].num); + + console_printf( + "\033[0;33mUse case 2 - Stress Connect/Disconnect legacy: \n\033[0m"); + console_printf("Con attempts = %d\n", test_ctxs->con_stat[2].attempts_num); + console_printf("Con success = %d\n", test_ctxs->con_stat[2].num); + + console_printf( + "\033[0;33mUse case 3 - Stress Connect/Disconnect ext adv: \n\033[0m"); + console_printf("Con attempts = %d\n", test_ctxs->con_stat[3].attempts_num); + console_printf("Con success = %d\n", test_ctxs->con_stat[3].num); + + console_printf( + "\033[0;33mUse case 4 - Stress connection params update (TX): \n\033[0m"); + console_printf("Params updates = %d\n", + test_ctxs->con_stat[4].prms_upd_num); + + console_printf( + "\033[0;33mUse case 5 - Stress connection params update (RX): \n\033[0m"); + console_printf("Params updates = %d\n", + test_ctxs->con_stat[5].prms_upd_num); + + console_printf("\033[0;33mUse case 6 - Stress Scan: \n\033[0m"); + console_printf("Received first packets = %d\n", + test_ctxs->s6_rcv_adv_first); + console_printf("Received all packets = %d\n", test_ctxs->s6_rcv_adv_suc); + + console_printf("\033[0;33mUse case 7 - Stress PHY Update (TX): \n\033[0m"); + console_printf("PHY updates = %d\n", test_ctxs->con_stat[7].phy_upd_num); + + console_printf("\033[0;33mUse case 8 - Stress PHY Update (RX): \n\033[0m"); + console_printf("PHY updates = %d\n", test_ctxs->con_stat[8].phy_upd_num); + + console_printf( + "\033[0;33mUse case 9 - Stress multi connection: \n\033[0m"); + console_printf("Max reached num of connections = %d\n", + test_ctxs->con_stat[9].max_num); + + console_printf("\033[0;33mUse case 10 - Stress L2CAP send: \n\033[0m"); + console_printf("Average bit rate = %d\n", test_ctxs->s10_bit_rate); + console_printf("Max received MTU = %lld\n", test_ctxs->s10_max_mtu); + + console_printf("\033[0;33mUse case 11 - " + "Stress Advertise/Connect/Continue adv \n\033[0m"); +// console_printf(" = %d\n",); + + console_printf("\033[0;33mUse case 12 - " + "Stress GATT indication: \n\033[0m"); + console_printf("Average bit rate = %d\n", test_ctxs->s12_notif_time); + + console_printf("\033[0;33mUse case 13 - " + "Stress GATT notification: \n\033[0m"); + console_printf("Average time = %d\n", test_ctxs->s13_notif_time); + + console_printf("\033[0;33mUse case 14 - " + "Stress GATT Subscribe/Notify/Unsubscribe: \n\033[0m"); + console_printf("Average time = %d\n", test_ctxs->s14_notif_time); + + console_printf("\033[0;33mUse case 15 - " + "Stress Connect/Send/Disconnect: \n\033[0m"); + console_printf("Con num = %d\n", test_ctxs->con_stat[15].num); +} + +void +stress_clear_ctx_reusable_var(struct com_stress_test_ctx *ctx) +{ + ctx->cur_test_id = 0; + ctx->dev_addr.type = 0; + ctx->dev_addr.type = 0; + ctx->conn_handle = 0; + ctx->chan = 0; + ctx->rcv_data_bytes = 0; + ctx->rcv_num = 0; + ctx->send_num = 0; + ctx->begin_us = 0; + ctx->end_us = 0; + ctx->time_sum = 0; + ctx->bytes_sum = 0; + ctx->timeout_flag = 0; + ctx->rcv_data_flag = 0; +} + +int +stress_fill_mbuf_with_pattern(struct os_mbuf *om, uint16_t len) +{ + int rc, i, mul, rest; + + mul = len / STRESS_PAT_LEN; + rest = len % STRESS_PAT_LEN; + + for (i = 0; i < mul; ++i) { + rc = os_mbuf_append(om, &test_6_pattern[0], STRESS_PAT_LEN); + + if (rc) { + os_mbuf_free_chain(om); + assert(0); + } + } + + rc = os_mbuf_append(om, &test_6_pattern[0], rest); + + if (rc) { + os_mbuf_free_chain(om); + assert(0); + } + + return rc; +} + +void +stress_l2cap_coc_recv(struct ble_l2cap_chan *chan, struct os_mbuf *sdu) +{ + int rc; + console_printf("LE CoC SDU received, chan: 0x%08lx, data len %d\n", + (uint32_t) chan, OS_MBUF_PKTLEN(sdu)); + + rc = os_mbuf_free_chain(sdu); + assert(rc == 0); + + /* Get buffer for data chain */ + sdu = os_msys_get_pkthdr(STRESS_COC_MTU, 0); + assert(sdu != NULL); + + /* Receive data chain */ + rc = ble_l2cap_recv_ready(chan, sdu); + assert(rc == 0); +} + +void +stress_l2cap_coc_accept(uint16_t peer_mtu, struct ble_l2cap_chan *chan) +{ + struct os_mbuf *sdu_rx; + int rc; + + console_printf("LE CoC accepting, chan: 0x%08lx, peer_mtu %d\n", + (uint32_t) chan, peer_mtu); + + sdu_rx = os_msys_get_pkthdr(STRESS_COC_MTU, 0); + assert(sdu_rx != NULL); + + rc = ble_l2cap_recv_ready(chan, sdu_rx); + assert(rc == 0); +} + +void +stress_start_timer(uint32_t timeout_ms, os_event_fn *ev_cb) +{ + int rc; + + os_callout_stop(&stress_timer_callout); + + os_callout_init(&stress_timer_callout, os_eventq_dflt_get(), ev_cb, NULL); + + rc = os_callout_reset(&stress_timer_callout, + os_time_ms_to_ticks32(timeout_ms)); + + assert(rc == 0); +} + +int64_t +stress_calc_bit_rate(int64_t us, int64_t bytes_num) +{ + int bit_rate; + + /* Multiply by 1000000 so you don't lose accuracy */ + bit_rate = 1000000 * bytes_num / us; + + return bit_rate; +} + +static int +stress_disc_dsc_fn(uint16_t conn_handle, + const struct ble_gatt_error *error, + uint16_t chr_val_handle, + const struct ble_gatt_dsc *dsc, + void *arg) +{ + struct stress_gatt_search_ctx *search_ctx; + static bool found = false; + + search_ctx = (struct stress_gatt_search_ctx *) arg; + + if (error->status == 0) { + if (!ble_uuid_cmp(&dsc->uuid.u, &search_ctx->dsc_uuid.u) && !found) { + MODLOG_DFLT(INFO, "Found chr descriptor\n"); + search_ctx->dsc_handle = dsc->handle; + MODLOG_DFLT(INFO, "uuid=%#06x; handle=%#06x", dsc->uuid.u16.value, + dsc->handle); + found = true; + } + return 0; + } + + if (error->status == BLE_HS_EDONE) { + MODLOG_DFLT(INFO, "Done descriptor discovery\n"); + + if (found) { + found = false; + search_ctx->disc_end_fn(search_ctx); + return 0; + } + + MODLOG_DFLT(ERROR, "\033[0;31mDid not find particular descriptor" + "\033[0m\n"); + return 0; + } + + MODLOG_DFLT(ERROR, "\033[0;31mError during descriptor discovery" + "\033[0m\n"); + assert(0); + return 0; +} + +static int +stress_disc_chr_fn(uint16_t conn_handle, + const struct ble_gatt_error *error, + const struct ble_gatt_chr *chr, void *arg) +{ + int rc; + struct stress_gatt_search_ctx *search_ctx; + static bool found = false; + + search_ctx = (struct stress_gatt_search_ctx *) arg; + + if (error->status == 0) { + MODLOG_DFLT(INFO, "Found characteristic\n"); + search_ctx->chr_start_handle = chr->val_handle; + found = true; + return 0; + } + + if (error->status == BLE_HS_EDONE) { + MODLOG_DFLT(INFO, "Done characteristic discovery\n"); + + if (found) { + found = false; + + if (search_ctx->search_goal == STRESS_FIND_CHR) { + search_ctx->disc_end_fn(search_ctx); + return 0; + } + + rc = ble_gattc_disc_all_dscs(conn_handle, + search_ctx->chr_start_handle, + search_ctx->srv_end_handle, + &stress_disc_dsc_fn, search_ctx); + assert(rc == 0); + return 0; + } + + MODLOG_DFLT(ERROR, "\033[0;31mDid not find particular " + "characteristic\033[0m\n"); + return 0; + } + + MODLOG_DFLT(ERROR, + "\033[0;31mError during characteristic discovery\033[0m\n"); + assert(0); + return 0; +} + +static int +stress_disc_svc_fn(uint16_t conn_handle, const struct ble_gatt_error *error, + const struct ble_gatt_svc *service, void *arg) +{ + int rc; + struct stress_gatt_search_ctx *search_ctx = NULL; + static bool found = false; + + search_ctx = (struct stress_gatt_search_ctx *) arg; + + if (error->status == 0) { + MODLOG_DFLT(INFO, "Found service\n"); + search_ctx->srv_start_handle = service->start_handle; + search_ctx->srv_end_handle = service->end_handle; + found = true; + return 0; + } + + if (error->status == BLE_HS_EDONE) { + MODLOG_DFLT(INFO, "Done service discovery\n"); + + if (found) { + found = false; + if (search_ctx->search_goal == STRESS_FIND_SRV) { + search_ctx->disc_end_fn(search_ctx); + return 0; + } + + rc = ble_gattc_disc_chrs_by_uuid(conn_handle, + search_ctx->srv_start_handle, + search_ctx->srv_end_handle, + &search_ctx->chr_uuid.u, + &stress_disc_chr_fn, + search_ctx); + MODLOG_DFLT(INFO, "rc=%d\n", rc); + assert(rc == 0); + return 0; + } + + MODLOG_DFLT(ERROR, + "\033[0;31mDid not find particular service\033[0m\n"); + return 0; + } + + MODLOG_DFLT(ERROR, "\033[0;31mError during service discovery\033[0m\n"); + assert(0); + return 0; +} + +static void +stress_gatt_find_handle(uint16_t conn_handle, const ble_uuid_t *srv_uuid, + const ble_uuid_t *chr_uuid, const ble_uuid_t *dsc_uuid, + stress_gatt_disc_end_fn *disc_end_fn, + uint8_t search_goal) +{ + static struct stress_gatt_search_ctx search_ctx; + int rc; + + search_ctx.conn_handle = conn_handle; + ble_uuid_copy((ble_uuid_any_t *) &search_ctx.srv_uuid, srv_uuid); + ble_uuid_copy((ble_uuid_any_t *) &search_ctx.chr_uuid, chr_uuid); + ble_uuid_copy((ble_uuid_any_t *) &search_ctx.dsc_uuid, dsc_uuid); + search_ctx.disc_end_fn = disc_end_fn; + search_ctx.search_goal = search_goal; + search_ctx.srv_start_handle = 0; + search_ctx.srv_end_handle = 0; + search_ctx.chr_start_handle = 0; + search_ctx.dsc_handle = 0; + + rc = ble_gattc_disc_svc_by_uuid(conn_handle, srv_uuid, &stress_disc_svc_fn, + &search_ctx); + assert(rc == 0); +} + +void +stress_find_svc_handle(uint16_t conn_handle, const ble_uuid_t *srv_uuid, + stress_gatt_disc_end_fn *disc_end_fn) +{ + stress_gatt_find_handle(conn_handle, srv_uuid, NULL, NULL, disc_end_fn, + STRESS_FIND_SRV); +} + +void +stress_find_chr_handle(uint16_t conn_handle, const ble_uuid_t *srv_uuid, + const ble_uuid_t *chr_uuid, + stress_gatt_disc_end_fn *disc_end_fn) +{ + stress_gatt_find_handle(conn_handle, srv_uuid, chr_uuid, NULL, disc_end_fn, + STRESS_FIND_CHR); +} + +void +stress_find_dsc_handle(uint16_t conn_handle, const ble_uuid_t *srv_uuid, + const ble_uuid_t *chr_uuid, const ble_uuid_t *dsc_uuid, + stress_gatt_disc_end_fn *disc_end_fn) +{ + stress_gatt_find_handle(conn_handle, srv_uuid, chr_uuid, dsc_uuid, + disc_end_fn, STRESS_FIND_DSC); +} diff --git a/lib/bt/host/nimble/nimble/apps/blestress/src/stress.h b/lib/bt/host/nimble/nimble/apps/blestress/src/stress.h new file mode 100644 index 00000000..db4fbb36 --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/blestress/src/stress.h @@ -0,0 +1,376 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef BLE_TGT_STRESS_H +#define BLE_TGT_STRESS_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#define STRESS_UUIDS_NUM (16) + +/* No preferred PHY */ +#define TX_PHY_MASK 0 +#define RX_PHY_MASK 0 +/* L2CAP SDU */ +#define STRESS_COC_MTU (64000) + +#define STRESS_FIND_SRV 1 +#define STRESS_FIND_CHR 2 +#define STRESS_FIND_DSC 3 +/* L2CAP PSM */ +#define TEST_PSM 0x80 + +struct stress_gatt_search_ctx; +typedef void stress_gatt_disc_end_fn(struct stress_gatt_search_ctx *search_ctx); + +struct stress_gatt_search_ctx { + /* Connection handle */ + uint16_t conn_handle; + + /* Wanted service uuid */ + ble_uuid16_t srv_uuid; + + /* Wanted characteristic uuid */ + ble_uuid16_t chr_uuid; + + /* Wanted descriptor uuid */ + ble_uuid16_t dsc_uuid; + + /* Search goal: 1 - service, 2 - characteristic, 3 - descriptor */ + uint8_t search_goal; + + /* Callback after reaching the goal */ + stress_gatt_disc_end_fn *disc_end_fn; + + /* Service start handle */ + uint16_t srv_start_handle; + + /* Service end handle */ + uint16_t srv_end_handle; + + /* Characteristic start handle */ + uint16_t chr_start_handle; + + /* Descriptor handle */ + uint16_t dsc_handle; +}; + +struct stress_con_stat { + /* Number of successful connection attempts */ + int num; + + /* Max number of established connections */ + int max_num; + + /* Number connection attempts */ + int attempts_num; + + /* Number of params updates */ + int prms_upd_num; + + /* Number of PHY updates */ + int phy_upd_num; +}; + +/* Common stress test context. + * (Reusable) - auxiliary variable + * (Stress x) - variable contains result of x stress test */ +struct com_stress_test_ctx { + /* Which of tests are completed already. Each element for different + * stress test. */ + bool completed[STRESS_UUIDS_NUM]; + + /* Connection stats. Each element for different stress test. */ + struct stress_con_stat con_stat[STRESS_UUIDS_NUM]; + +/* Reusable variables */ + + /* Stress test number */ + int cur_test_id; + + /* Instance address */ + ble_addr_t dev_addr; + + /* Connection handler */ + uint16_t conn_handle; + + /* L2CAP channel (Reusable) */ + struct ble_l2cap_chan * chan; + + /* Size of received data */ + int64_t rcv_data_bytes; + + /* Number of received packages */ + int rcv_num; + + /* Number of send actions */ + int send_num; + + /* Variables for bit rate measurement */ + int64_t begin_us; + int64_t end_us; + int64_t time_sum; + int64_t bytes_sum; + + /* Timeout flag */ + bool timeout_flag; + + /* Data received flag */ + bool rcv_data_flag; + + uint16_t start_handle; + uint16_t end_handle; + uint16_t dsc_handle; + +/* Variables for test results */ + /* Reached timeout of scanning for test */ + bool scan_timeout; + + /* Number of received first packets of adverts */ + int s6_rcv_adv_first; + + /* Number of full received adverts */ + int s6_rcv_adv_suc; + + /* L2CAP Send Bit Rate */ + int s10_bit_rate; + + /* Average indication time */ + int s12_notif_time; + + /* Average notification time */ + int s13_notif_time; + + /* Average notification time */ + int s14_notif_time; + + /* Max size of received MTU */ + int64_t s10_max_mtu; +}; + +#define STRESS_PAT_LEN 1650 + +static const uint8_t test_6_pattern[STRESS_PAT_LEN] = { + /* Random data */ + 0x00, 0x02, 0x00, 0x04, 0x00, 0x06, 0x00, 0x08, 0x00, 0x0a, + 0x00, 0x0c, 0x00, 0x0e, 0x00, 0x10, 0x00, 0x12, 0x00, 0x14, + 0x00, 0x16, 0x00, 0x18, 0x00, 0x1a, 0x00, 0x1c, 0x00, 0x1e, + 0x00, 0x20, 0x00, 0x22, 0x00, 0x24, 0x00, 0x26, 0x00, 0x28, + 0x00, 0x2a, 0x00, 0x2c, 0x00, 0x2e, 0x00, 0x30, 0x00, 0x32, + 0x00, 0x34, 0x00, 0x36, 0x00, 0x38, 0x00, 0x3a, 0x00, 0x3c, + 0x00, 0x3e, 0x00, 0x40, 0x00, 0x42, 0x00, 0x44, 0x00, 0x46, + 0x00, 0x48, 0x00, 0x4a, 0x00, 0x4c, 0x00, 0x4e, 0x00, 0x50, + 0x00, 0x52, 0x00, 0x54, 0x00, 0x56, 0x00, 0x58, 0x00, 0x5a, + 0x00, 0x5c, 0x00, 0x5e, 0x00, 0x60, 0x00, 0x62, 0x00, 0x64, + 0x00, 0x66, 0x00, 0x68, 0x00, 0x6a, 0x00, 0x6c, 0x00, 0x6e, + 0x00, 0x70, 0x00, 0x72, 0x00, 0x74, 0x00, 0x76, 0x00, 0x78, + 0x00, 0x7a, 0x00, 0x7c, 0x00, 0x7e, 0x00, 0x80, 0x00, 0x82, + 0x00, 0x84, 0x00, 0x86, 0x00, 0x88, 0x00, 0x8a, 0x00, 0x8c, + 0x00, 0x8e, 0x00, 0x90, 0x00, 0x92, 0x00, 0x94, 0x00, 0x96, + 0x00, 0x98, 0x00, 0x9a, 0x00, 0x9c, 0x00, 0x9e, 0x00, 0xa0, + 0x00, 0xa2, 0x00, 0xa4, 0x00, 0xa6, 0x00, 0xa8, 0x00, 0xaa, + 0x00, 0xac, 0x00, 0xae, 0x00, 0xb0, 0x00, 0xb2, 0x00, 0xb4, + 0x00, 0xb6, 0x00, 0xb8, 0x00, 0xba, 0x00, 0xbc, 0x00, 0xbe, + 0x00, 0xc0, 0x00, 0xc2, 0x00, 0xc4, 0x00, 0xc6, 0x00, 0xc8, + 0x00, 0xca, 0x00, 0xcc, 0x00, 0xce, 0x00, 0xd0, 0x00, 0xd2, + 0x00, 0xd4, 0x00, 0xd6, 0x00, 0xd8, 0x00, 0xda, 0x00, 0xdc, + 0x00, 0xde, 0x00, 0xe0, 0x00, 0xe2, 0x00, 0xe4, 0x00, 0xe6, + 0x00, 0xe8, 0x00, 0xea, 0x00, 0xec, 0x00, 0xee, 0x00, 0xf0, + 0x00, 0xf2, 0x00, 0xf4, 0x00, 0xf6, 0x00, 0xf8, 0x00, 0xfa, + 0x00, 0xfc, 0x00, 0xfe, 0x01, 0x01, 0x01, 0x03, 0x01, 0x05, + 0x01, 0x07, 0x01, 0x09, 0x01, 0x0b, 0x01, 0x0d, 0x01, 0x0f, + 0x01, 0x11, 0x01, 0x13, 0x01, 0x15, 0x01, 0x17, 0x01, 0x19, + 0x01, 0x1b, 0x01, 0x1d, 0x01, 0x1f, 0x01, 0x21, 0x01, 0x23, + 0x01, 0x25, 0x01, 0x27, 0x01, 0x29, 0x01, 0x2b, 0x01, 0x2d, + 0x01, 0x2f, 0x01, 0x31, 0x01, 0x33, 0x01, 0x35, 0x01, 0x37, + 0x01, 0x39, 0x01, 0x3b, 0x01, 0x3d, 0x01, 0x3f, 0x01, 0x41, + 0x01, 0x43, 0x01, 0x45, 0x01, 0x47, 0x01, 0x49, 0x01, 0x4b, + 0x01, 0x4d, 0x01, 0x4f, 0x01, 0x51, 0x01, 0x53, 0x01, 0x55, + 0x01, 0x57, 0x01, 0x59, 0x01, 0x5b, 0x01, 0x5d, 0x01, 0x5f, + 0x01, 0x61, 0x01, 0x63, 0x01, 0x65, 0x01, 0x67, 0x01, 0x69, + 0x01, 0x6b, 0x01, 0x6d, 0x01, 0x6f, 0x01, 0x71, 0x01, 0x73, + 0x01, 0x75, 0x01, 0x77, 0x01, 0x79, 0x01, 0x7b, 0x01, 0x7d, + 0x01, 0x7f, 0x01, 0x81, 0x01, 0x83, 0x01, 0x85, 0x01, 0x87, + 0x01, 0x89, 0x01, 0x8b, 0x01, 0x8d, 0x01, 0x8f, 0x01, 0x91, + 0x01, 0x93, 0x01, 0x95, 0x01, 0x97, 0x01, 0x99, 0x01, 0x9b, + 0x01, 0x9d, 0x01, 0x9f, 0x01, 0xa1, 0x01, 0xa3, 0x01, 0xa5, + 0x01, 0xa7, 0x01, 0xa9, 0x01, 0xab, 0x01, 0xad, 0x01, 0xaf, + 0x01, 0xb1, 0x01, 0xb3, 0x01, 0xb5, 0x01, 0xb7, 0x01, 0xb9, + 0x01, 0xbb, 0x01, 0xbd, 0x01, 0xbf, 0x01, 0xc1, 0x01, 0xc3, + 0x01, 0xc5, 0x01, 0xc7, 0x01, 0xc9, 0x01, 0xcb, 0x01, 0xcd, + 0x01, 0xcf, 0x01, 0xd1, 0x01, 0xd3, 0x01, 0xd5, 0x01, 0xd7, + 0x01, 0xd9, 0x01, 0xdb, 0x01, 0xdd, 0x01, 0xdf, 0x01, 0xe1, + 0x01, 0xe3, 0x01, 0xe5, 0x01, 0xe7, 0x01, 0xe9, 0x01, 0xeb, + 0x01, 0xed, 0x01, 0xef, 0x01, 0xf1, 0x01, 0xf3, 0x01, 0xf5, + 0x01, 0xf7, 0x01, 0xf9, 0x01, 0xfb, 0x01, 0xfd, 0x02, 0x00, + 0x02, 0x02, 0x02, 0x04, 0x02, 0x06, 0x02, 0x08, 0x02, 0x0a, + 0x02, 0x0c, 0x02, 0x0e, 0x02, 0x10, 0x02, 0x12, 0x02, 0x14, + 0x02, 0x16, 0x02, 0x18, 0x02, 0x1a, 0x02, 0x1c, 0x02, 0x1e, + 0x02, 0x20, 0x02, 0x22, 0x02, 0x24, 0x02, 0x26, 0x02, 0x28, + 0x02, 0x2a, 0x02, 0x2c, 0x02, 0x2e, 0x02, 0x30, 0x02, 0x32, + 0x02, 0x34, 0x02, 0x36, 0x02, 0x38, 0x02, 0x3a, 0x02, 0x3c, + 0x02, 0x3e, 0x02, 0x40, 0x02, 0x42, 0x02, 0x44, 0x02, 0x46, + 0x02, 0x48, 0x02, 0x4a, 0x02, 0x4c, 0x02, 0x4e, 0x02, 0x50, + 0x02, 0x52, 0x02, 0x54, 0x02, 0x56, 0x02, 0x58, 0x02, 0x5a, + 0x02, 0x5c, 0x02, 0x5e, 0x02, 0x60, 0x02, 0x62, 0x02, 0x64, + 0x02, 0x66, 0x02, 0x68, 0x02, 0x6a, 0x02, 0x6c, 0x02, 0x6e, + 0x02, 0x70, 0x02, 0x72, 0x02, 0x74, 0x02, 0x76, 0x02, 0x78, + 0x02, 0x7a, 0x02, 0x7c, 0x02, 0x7e, 0x02, 0x80, 0x02, 0x82, + 0x02, 0x84, 0x02, 0x86, 0x02, 0x88, 0x02, 0x8a, 0x02, 0x8c, + 0x02, 0x8e, 0x02, 0x90, 0x02, 0x92, 0x02, 0x94, 0x02, 0x96, + 0x02, 0x98, 0x02, 0x9a, 0x02, 0x9c, 0x02, 0x9e, 0x02, 0xa0, + 0x02, 0xa2, 0x02, 0xa4, 0x02, 0xa6, 0x02, 0xa8, 0x02, 0xaa, + 0x02, 0xac, 0x02, 0xae, 0x02, 0xb0, 0x02, 0xb2, 0x02, 0xb4, + 0x02, 0xb6, 0x02, 0xb8, 0x02, 0xba, 0x02, 0xbc, 0x02, 0xbe, + 0x02, 0xc0, 0x02, 0xc2, 0x02, 0xc4, 0x02, 0xc6, 0x02, 0xc8, + 0x02, 0xca, 0x02, 0xcc, 0x02, 0xce, 0x02, 0xd0, 0x02, 0xd2, + 0x02, 0xd4, 0x02, 0xd6, 0x02, 0xd8, 0x02, 0xda, 0x02, 0xdc, + 0x02, 0xde, 0x02, 0xe0, 0x02, 0xe2, 0x02, 0xe4, 0x02, 0xe6, + 0x02, 0xe8, 0x02, 0xea, 0x02, 0xec, 0x02, 0xee, 0x02, 0xf0, + 0x02, 0xf2, 0x02, 0xf4, 0x02, 0xf6, 0x02, 0xf8, 0x02, 0xfa, + 0x02, 0xfc, 0x02, 0xfe, 0x03, 0x01, 0x03, 0x03, 0x03, 0x05, + 0x03, 0x07, 0x03, 0x09, 0x03, 0x0b, 0x03, 0x0d, 0x03, 0x0f, + 0x03, 0x11, 0x03, 0x13, 0x03, 0x15, 0x03, 0x17, 0x03, 0x19, + 0x03, 0x1b, 0x03, 0x1d, 0x03, 0x1f, 0x03, 0x21, 0x03, 0x23, + 0x03, 0x25, 0x03, 0x27, 0x03, 0x29, 0x03, 0x2b, 0x03, 0x2d, + 0x03, 0x2f, 0x03, 0x31, 0x03, 0x33, 0x03, 0x35, 0x03, 0x37, + 0x03, 0x39, 0x03, 0x3b, 0x03, 0x3d, 0x03, 0x3f, 0x03, 0x41, + 0x03, 0x43, 0x03, 0x45, 0x03, 0x47, 0x03, 0x49, 0x03, 0x4b, + 0x03, 0x4d, 0x03, 0x4f, 0x03, 0x51, 0x03, 0x53, 0x03, 0x55, + 0x03, 0x57, 0x03, 0x59, 0x03, 0x5b, 0x03, 0x5d, 0x03, 0x5f, + 0x03, 0x61, 0x03, 0x63, 0x03, 0x65, 0x03, 0x67, 0x03, 0x69, + 0x03, 0x6b, 0x03, 0x6d, 0x03, 0x6f, 0x03, 0x71, 0x03, 0x73, + 0x03, 0x75, 0x03, 0x77, 0x03, 0x79, 0x03, 0x7b, 0x03, 0x7d, + 0x03, 0x7f, 0x03, 0x81, 0x03, 0x83, 0x03, 0x85, 0x03, 0x87, + 0x03, 0x89, 0x03, 0x8b, 0x03, 0x8d, 0x03, 0x8f, 0x03, 0x91, + 0x03, 0x93, 0x03, 0x95, 0x03, 0x97, 0x03, 0x99, 0x03, 0x9b, + 0x03, 0x9d, 0x03, 0x9f, 0x03, 0xa1, 0x03, 0xa3, 0x03, 0xa5, + 0x03, 0xa7, 0x03, 0xa9, 0x03, 0xab, 0x03, 0xad, 0x03, 0xaf, + 0x03, 0xb1, 0x03, 0xb3, 0x03, 0xb5, 0x03, 0xb7, 0x03, 0xb9, + 0x03, 0xbb, 0x03, 0xbd, 0x03, 0xbf, 0x03, 0xc1, 0x03, 0xc3, + 0x03, 0xc5, 0x03, 0xc7, 0x03, 0xc9, 0x03, 0xcb, 0x03, 0xcd, + 0x03, 0xcf, 0x03, 0xd1, 0x03, 0xd3, 0x03, 0xd5, 0x03, 0xd7, + 0x03, 0xd9, 0x03, 0xdb, 0x03, 0xdd, 0x03, 0xdf, 0x03, 0xe1, + 0x03, 0xe3, 0x03, 0xe5, 0x03, 0xe7, 0x03, 0xe9, 0x03, 0xeb, + 0x03, 0xed, 0x03, 0xef, 0x03, 0xf1, 0x03, 0xf3, 0x03, 0xf5, + 0x03, 0xf7, 0x03, 0xf9, 0x03, 0xfb, 0x03, 0xfd, 0x04, 0x00, + 0x04, 0x02, 0x04, 0x04, 0x04, 0x06, 0x04, 0x08, 0x04, 0x0a, + 0x04, 0x0c, 0x04, 0x0e, 0x04, 0x10, 0x04, 0x12, 0x04, 0x14, + 0x04, 0x16, 0x04, 0x18, 0x04, 0x1a, 0x04, 0x1c, 0x04, 0x1e, + 0x04, 0x20, 0x04, 0x22, 0x04, 0x24, 0x04, 0x26, 0x04, 0x28, + 0x04, 0x2a, 0x04, 0x2c, 0x04, 0x2e, 0x04, 0x30, 0x04, 0x32, + 0x04, 0x34, 0x04, 0x36, 0x04, 0x38, 0x04, 0x3a, 0x04, 0x3c, + 0x04, 0x3e, 0x04, 0x40, 0x04, 0x42, 0x04, 0x44, 0x04, 0x46, + 0x04, 0x48, 0x04, 0x4a, 0x04, 0x4c, 0x04, 0x4e, 0x04, 0x50, + 0x04, 0x52, 0x04, 0x54, 0x04, 0x56, 0x04, 0x58, 0x04, 0x5a, + 0x04, 0x5c, 0x04, 0x5e, 0x04, 0x60, 0x04, 0x62, 0x04, 0x64, + 0x04, 0x66, 0x04, 0x68, 0x04, 0x6a, 0x04, 0x6c, 0x04, 0x6e, + 0x04, 0x70, 0x04, 0x72, 0x04, 0x74, 0x04, 0x76, 0x04, 0x78, + 0x04, 0x7a, 0x04, 0x7c, 0x04, 0x7e, 0x04, 0x80, 0x04, 0x82, + 0x04, 0x84, 0x04, 0x86, 0x04, 0x88, 0x04, 0x8a, 0x04, 0x8c, + 0x04, 0x8e, 0x04, 0x90, 0x04, 0x92, 0x04, 0x94, 0x04, 0x96, + 0x04, 0x98, 0x04, 0x9a, 0x04, 0x9c, 0x04, 0x9e, 0x04, 0xa0, + 0x04, 0xa2, 0x04, 0xa4, 0x04, 0xa6, 0x04, 0xa8, 0x04, 0xaa, + 0x04, 0xac, 0x04, 0xae, 0x04, 0xb0, 0x04, 0xb2, 0x04, 0xb4, + 0x04, 0xb6, 0x04, 0xb8, 0x04, 0xba, 0x04, 0xbc, 0x04, 0xbe, + 0x04, 0xc0, 0x04, 0xc2, 0x04, 0xc4, 0x04, 0xc6, 0x04, 0xc8, + 0x04, 0xca, 0x04, 0xcc, 0x04, 0xce, 0x04, 0xd0, 0x04, 0xd2, + 0x04, 0xd4, 0x04, 0xd6, 0x04, 0xd8, 0x04, 0xda, 0x04, 0xdc, + 0x04, 0xde, 0x04, 0xe0, 0x04, 0xe2, 0x04, 0xe4, 0x04, 0xe6, + 0x04, 0xe8, 0x04, 0xea, 0x04, 0xec, 0x04, 0xee, 0x04, 0xf0, + 0x04, 0xf2, 0x04, 0xf4, 0x04, 0xf6, 0x04, 0xf8, 0x04, 0xfa, + 0x04, 0xfc, 0x04, 0xfe, 0x05, 0x01, 0x05, 0x03, 0x05, 0x05, + 0x05, 0x07, 0x05, 0x09, 0x05, 0x0b, 0x05, 0x0d, 0x05, 0x0f, + 0x05, 0x11, 0x05, 0x13, 0x05, 0x15, 0x05, 0x17, 0x05, 0x19, + 0x05, 0x1b, 0x05, 0x1d, 0x05, 0x1f, 0x05, 0x21, 0x05, 0x23, + 0x05, 0x25, 0x05, 0x27, 0x05, 0x29, 0x05, 0x2b, 0x05, 0x2d, + 0x05, 0x2f, 0x05, 0x31, 0x05, 0x33, 0x05, 0x35, 0x05, 0x37, + 0x05, 0x39, 0x05, 0x3b, 0x05, 0x3d, 0x05, 0x3f, 0x05, 0x41, + 0x05, 0x43, 0x05, 0x45, 0x05, 0x47, 0x05, 0x49, 0x05, 0x4b, + 0x05, 0x4d, 0x05, 0x4f, 0x05, 0x51, 0x05, 0x53, 0x05, 0x55, + 0x05, 0x57, 0x05, 0x59, 0x05, 0x5b, 0x05, 0x5d, 0x05, 0x5f, + 0x05, 0x61, 0x05, 0x63, 0x05, 0x65, 0x05, 0x67, 0x05, 0x69, + 0x05, 0x6b, 0x05, 0x6d, 0x05, 0x6f, 0x05, 0x71, 0x05, 0x73, + 0x05, 0x75, 0x05, 0x77, 0x05, 0x79, 0x05, 0x7b, 0x05, 0x7d, + 0x05, 0x7f, 0x05, 0x81, 0x05, 0x83, 0x05, 0x85, 0x05, 0x87, + 0x05, 0x89, 0x05, 0x8b, 0x05, 0x8d, 0x05, 0x8f, 0x05, 0x91, + 0x05, 0x93, 0x05, 0x95, 0x05, 0x97, 0x05, 0x99, 0x05, 0x9b, + 0x05, 0x9d, 0x05, 0x9f, 0x05, 0xa1, 0x05, 0xa3, 0x05, 0xa5, + 0x05, 0xa7, 0x05, 0xa9, 0x05, 0xab, 0x05, 0xad, 0x05, 0xaf, + 0x05, 0xb1, 0x05, 0xb3, 0x05, 0xb5, 0x05, 0xb7, 0x05, 0xb9, + 0x05, 0xbb, 0x05, 0xbd, 0x05, 0xbf, 0x05, 0xc1, 0x05, 0xc3, + 0x05, 0xc5, 0x05, 0xc7, 0x05, 0xc9, 0x05, 0xcb, 0x05, 0xcd, + 0x05, 0xcf, 0x05, 0xd1, 0x05, 0xd3, 0x05, 0xd5, 0x05, 0xd7, + 0x05, 0xd9, 0x05, 0xdb, 0x05, 0xdd, 0x05, 0xdf, 0x05, 0xe1, + 0x05, 0xe3, 0x05, 0xe5, 0x05, 0xe7, 0x05, 0xe9, 0x05, 0xeb, + 0x05, 0xed, 0x05, 0xef, 0x05, 0xf1, 0x05, 0xf3, 0x05, 0xf5, + 0x05, 0xf7, 0x05, 0xf9, 0x05, 0xfb, 0x05, 0xfd, 0x06, 0x00, + 0x06, 0x02, 0x06, 0x04, 0x06, 0x06, 0x06, 0x08, 0x06, 0x0a, + 0x06, 0x0c, 0x06, 0x0e, 0x06, 0x10, 0x06, 0x12, 0x06, 0x14, + 0x06, 0x16, 0x06, 0x18, 0x06, 0x1a, 0x06, 0x1c, 0x06, 0x1e, + 0x06, 0x20, 0x06, 0x22, 0x06, 0x24, 0x06, 0x26, 0x06, 0x28, + 0x06, 0x2a, 0x06, 0x2c, 0x06, 0x2e, 0x06, 0x30, 0x06, 0x32, + 0x06, 0x34, 0x06, 0x36, 0x06, 0x38, 0x06, 0x3a, 0x06, 0x3c, + 0x06, 0x3e, 0x06, 0x40, 0x06, 0x42, 0x06, 0x44, 0x06, 0x46, + 0x06, 0x48, 0x06, 0x4a, 0x06, 0x4c, 0x06, 0x4e, 0x06, 0x50, + 0x06, 0x52, 0x06, 0x54, 0x06, 0x56, 0x06, 0x58, 0x06, 0x5a, + 0x06, 0x5c, 0x06, 0x5e, 0x06, 0x60, 0x06, 0x62, 0x06, 0x64, + 0x06, 0x66, 0x06, 0x68, 0x06, 0x6a, 0x06, 0x6c, 0x06, 0x6e, + 0x06, 0x70, 0x06, 0x72, 0x06, 0x74, 0x06, 0x76, 0x06, 0x78, +}; + +void stress_clear_ctx_reusable_var(struct com_stress_test_ctx *ctx); + +void com_stress_print_report(const struct com_stress_test_ctx test_ctxs[]); + +int stress_fill_mbuf_with_pattern(struct os_mbuf *om, uint16_t len); + +void stress_l2cap_coc_recv(struct ble_l2cap_chan *chan, struct os_mbuf *sdu); + +void stress_l2cap_coc_accept(uint16_t peer_mtu, struct ble_l2cap_chan *chan); + +void stress_start_timer(uint32_t timeout_ms, os_event_fn *ev_cb); + +int64_t stress_calc_bit_rate(int64_t us, int64_t bytes_num); + +void stress_find_svc_handle(uint16_t conn_handle, const ble_uuid_t *srv_uuid, + stress_gatt_disc_end_fn *disc_end_fn); +void stress_find_chr_handle(uint16_t conn_handle, const ble_uuid_t *srv_uuid, + const ble_uuid_t *chr_uuid, + stress_gatt_disc_end_fn *disc_end_fn); + +void stress_find_dsc_handle(uint16_t conn_handle, const ble_uuid_t *srv_uuid, + const ble_uuid_t *chr_uuid, const ble_uuid_t *dsc_uuid, + stress_gatt_disc_end_fn *disc_end_fn); +#ifdef __cplusplus +} +#endif + +#endif //BLE_TGT_STRESS_H diff --git a/lib/bt/host/nimble/nimble/apps/blestress/src/stress_gatt.c b/lib/bt/host/nimble/nimble/apps/blestress/src/stress_gatt.c new file mode 100644 index 00000000..a6d845c5 --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/blestress/src/stress_gatt.c @@ -0,0 +1,155 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "stress_gatt.h" + +uint16_t hrs_hrm_handle = 0xffff; + +static int +stress_gatt_access_cb(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, void *arg); + +static const struct ble_gatt_svc_def gatt_svr_svcs[] = { + { + /* Service: Heart-rate */ + .type = BLE_GATT_SVC_TYPE_PRIMARY, + .uuid = BLE_UUID16_DECLARE(STRESS_GATT_UUID), + .characteristics = (struct ble_gatt_chr_def[]) { + { + /* Characteristic: read test */ + .uuid = BLE_UUID16_DECLARE(STRESS_GATT_READ_UUID), + .access_cb = stress_gatt_access_cb, + .val_handle = &hrs_hrm_handle, + .flags = BLE_GATT_CHR_F_READ, + }, + { + /* Characteristic: write test */ + .uuid = BLE_UUID16_DECLARE(STRESS_GATT_WRITE_UUID), + .access_cb = stress_gatt_access_cb, + .val_handle = &hrs_hrm_handle, + .flags = BLE_GATT_CHR_F_WRITE, + }, + { + /* Characteristic: notify test */ + .uuid = BLE_UUID16_DECLARE(STRESS_GATT_NOTIFY_UUID), + .access_cb = stress_gatt_access_cb, + .val_handle = &hrs_hrm_handle, + .flags = BLE_GATT_CHR_F_NOTIFY, + }, + { + /* Characteristic: indicate test */ + .uuid = BLE_UUID16_DECLARE(STRESS_GATT_INDICATE_UUID), + .access_cb = stress_gatt_access_cb, + .val_handle = &hrs_hrm_handle, + .flags = BLE_GATT_CHR_F_INDICATE, + }, + { + 0, /* No more characteristics in this service */ + },} + }, + { + 0, /* No more services */ + }, +}; + +static int +stress_gatt_access_cb(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, void *arg) +{ + /* Sensor location, set to "Chest" */ + static uint8_t chr_value[] = "Hello"; + uint16_t uuid; + int rc; + + //chr_value = (uint8_t)rand() % 256; + uuid = ble_uuid_u16(ctxt->chr->uuid); + + switch(uuid){ + case STRESS_GATT_READ_UUID: + MODLOG_DFLT(INFO, "GATT Read event\n"); + rc = os_mbuf_append(ctxt->om, &chr_value, sizeof(chr_value)); + assert(rc == 0); + //return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; + return 0; + case STRESS_GATT_WRITE_UUID: + MODLOG_DFLT(INFO, "GATT Write event\n"); + print_mbuf(ctxt->om); + return 0; + case STRESS_GATT_NOTIFY_UUID: + MODLOG_DFLT(INFO, "GATT Notify event\n"); + return 0; + case STRESS_GATT_INDICATE_UUID: + MODLOG_DFLT(INFO, "GATT Indicate event\n"); + return 0; + default: + MODLOG_DFLT(ERROR, "GATT UUID does not exist\n"); + assert(0); + return BLE_ATT_ERR_UNLIKELY; + } +} + +void +gatt_svr_register_cb(struct ble_gatt_register_ctxt *ctxt, void *arg) +{ + char buf[BLE_UUID_STR_LEN]; + + switch (ctxt->op) { + case BLE_GATT_REGISTER_OP_SVC: + MODLOG_DFLT(DEBUG, "registered service %s with handle=%d\n", + ble_uuid_to_str(ctxt->svc.svc_def->uuid, buf), + ctxt->svc.handle); + break; + + case BLE_GATT_REGISTER_OP_CHR: + MODLOG_DFLT(DEBUG, "registering characteristic %s with " + "def_handle=%d val_handle=%d\n", + ble_uuid_to_str(ctxt->chr.chr_def->uuid, buf), + ctxt->chr.def_handle, + ctxt->chr.val_handle); + break; + + case BLE_GATT_REGISTER_OP_DSC: + MODLOG_DFLT(DEBUG, "registering descriptor %s with handle=%d\n", + ble_uuid_to_str(ctxt->dsc.dsc_def->uuid, buf), + ctxt->dsc.handle); + break; + + default: + assert(0); + break; + } +} + +int +gatt_svr_init(void) +{ + int rc; + + rc = ble_gatts_count_cfg(gatt_svr_svcs); + if (rc != 0) { + return rc; + } + + rc = ble_gatts_add_svcs(gatt_svr_svcs); + if (rc != 0) { + return rc; + } + + return 0; +} diff --git a/lib/bt/host/nimble/nimble/apps/blestress/src/stress_gatt.h b/lib/bt/host/nimble/nimble/apps/blestress/src/stress_gatt.h new file mode 100644 index 00000000..3344fe2d --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/blestress/src/stress_gatt.h @@ -0,0 +1,54 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef BLE_TGT_STRESS_GATT_H +#define BLE_TGT_STRESS_GATT_H + +#include +#include +#include +#include "host/ble_hs.h" +#include "host/ble_uuid.h" +#include "nimble/ble.h" +#include "modlog/modlog.h" +#include "misc.h" + +#ifdef __cplusplus +extern "C" { +#endif + +extern uint16_t hrs_hrm_handle; + +/* Heart-rate configuration */ +#define STRESS_GATT_UUID 0xC0DE +#define STRESS_GATT_READ_UUID 0xC1DE +#define STRESS_GATT_WRITE_UUID 0xC2DE +#define STRESS_GATT_INDICATE_UUID 0xC3DE +#define STRESS_GATT_NOTIFY_UUID 0xC4DE + +int gatt_svr_init(void); + +void gatt_svr_register_cb(struct ble_gatt_register_ctxt *ctxt, void *arg); + +#ifdef __cplusplus +} +#endif + + +#endif //BLE_TGT_STRESS_GATT_H diff --git a/lib/bt/host/nimble/nimble/apps/blestress/src/tx_stress.c b/lib/bt/host/nimble/nimble/apps/blestress/src/tx_stress.c new file mode 100644 index 00000000..1711f9fb --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/blestress/src/tx_stress.c @@ -0,0 +1,1670 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#include +#include +#include +#include "tx_stress.h" + +/* Main test task priority. Set a high value so that the task does not + * interfere with event handling */ +#define TX_STRESS_MAIN_TASK_PRIO 0xf0 +#define BASE_UUID_LEN 13 + +/* Contexts for stress tests. */ +static struct com_stress_test_ctx tx_stress_ctxD = { + .conn_handle = 0xffff, + .cur_test_id = 0, +}; + +static struct com_stress_test_ctx *tx_stress_ctx; + +/* Define stack, object and semaphore for test main task. */ +#define TX_STRESS_MAIN_TASK_STACK_SIZE (500) +static struct os_task tx_stress_main_task; +static os_stack_t tx_stress_main_task_stack[TX_STRESS_MAIN_TASK_STACK_SIZE]; +static struct os_sem tx_stress_main_sem; +/* Test use case and address of test advertiser. */ +static int tx_stress_use_case; +static int completed_tests = 0; + +static void +tx_stress_on_test_finish(int test_num) +{ + console_printf("\033[0;32m\nStress test %d completed\033[0m\n", test_num); + ++completed_tests; + tx_stress_ctx->completed[test_num] = true; + os_sem_release(&tx_stress_main_sem); +} + +static void +tx_stress_simple_scan(ble_gap_event_fn *cb, uint16_t duration) +{ + uint8_t own_addr_type; + struct ble_gap_ext_disc_params params = {0}; + int rc; + + /* Figure out address to use while scanning. */ + rc = ble_hs_id_infer_auto(0, &own_addr_type); + if (rc != 0) { + console_printf("\033[0;31mError determining own address type; " + "rc=%d\033[0m\n", rc); + assert(0); + } + + params.itvl = BLE_GAP_SCAN_FAST_INTERVAL_MAX; + params.passive = 1; + params.window = BLE_GAP_SCAN_FAST_WINDOW; + + rc = ble_gap_ext_disc(own_addr_type, duration, 0, 1, 0, 0, ¶ms, NULL, + cb, NULL); + + if (rc != 0) { + console_printf("\033[0;31mError initiating GAP discovery procedure" + "; rc=%d\033[0m\n", rc); + } +} + +static int +tx_stress_simple_connect(ble_gap_event_fn *cb, int test_num) +{ + uint8_t own_addr_type; + int rc; + + /* Set so any PHY mask allowed. */ + ble_gap_set_prefered_default_le_phy(TX_PHY_MASK, RX_PHY_MASK); + + /* Figure out address to use while connecting. */ + rc = ble_hs_id_infer_auto(0, &own_addr_type); + if (rc != 0) { + MODLOG_DFLT(INFO, "\033[0;31mError determining own address type; " + "rc=%d\033[0m\n", rc); + return rc; + } + + MODLOG_DFLT(INFO, "Connection attempt: %d\n", + ++tx_stress_ctx->con_stat[test_num].attempts_num); + + rc = ble_gap_ext_connect(own_addr_type, &tx_stress_ctx->dev_addr, + 10000, + BLE_GAP_LE_PHY_1M_MASK | BLE_GAP_LE_PHY_2M_MASK, + NULL, NULL, NULL, cb, NULL); + + if (rc != 0) { + MODLOG_DFLT(INFO, "\033[0;31mError during connection; rc=%d\033[0m\n", + rc); + } + + return rc; +} + +static int +tx_stress_find_test(struct ble_gap_ext_disc_desc *ext_disc) +{ + struct ble_hs_adv_fields fields; + int data_len; + int rc; + + /* Parser refuses greater length data than 31. But known UUID128 will be + * in first 31 bytes of adv data first packet. */ + if (ext_disc->length_data > 31) { + data_len = 31; + } else { + data_len = ext_disc->length_data; + } + + /* Parse part of adv data. */ + ble_hs_adv_parse_fields(&fields, ext_disc->data, data_len); + print_adv_fields(&fields); + + /* UUID128 service data of stress test advert includes only UUID128. */ + if (fields.svc_data_uuid128_len != 16) { + return -1; + } + + /* Check if service data include known UUID128. */ + rc = memcmp(fields.svc_data_uuid128, (uint8_t[]) {0xC0, 0xDE}, 2); + if (rc) { + return -1; + } + + rc = memcmp(fields.svc_data_uuid128 + 3, MYNEWT_VAL(BLE_STRESS_UUID_BASE), + BASE_UUID_LEN); + + if (rc != 0) { + return -1; + } + + /* This UUID 128 byte indicates the stress test ID to be executed. */ + return fields.svc_data_uuid128[2]; +} + +static int +tx_stress_switcher_gap_event(struct ble_gap_event *event, void *arg) +{ + int rc; + + switch (event->type) { + case BLE_GAP_EVENT_CONNECT: + /* A new connection was established or a connection attempt failed. */ + if (event->connect.status == 0) { + MODLOG_DFLT(INFO, "Success to connect to device\n"); + return 0; + } else if (event->connect.status == BLE_HS_ETIMEOUT_HCI) { + MODLOG_DFLT(INFO, "Connection timeout\n"); + } else { + MODLOG_DFLT(INFO, "Error: connection attempt failed; status=%d\n", + event->connect.status); + } + /* Connect to rx device just to give it a signal to switch test. */ + tx_stress_simple_connect(tx_stress_switcher_gap_event, + tx_stress_ctx->cur_test_id); + return 0; + + case BLE_GAP_EVENT_DISCONNECT: + os_sem_release(&tx_stress_main_sem); + return 0; + + case BLE_GAP_EVENT_EXT_DISC: + /* Check if caught advert contains known UUID128. The UUID128 + * contains the ID of test use case to be executed. */ + rc = tx_stress_find_test(&event->ext_disc); + if (rc == 0) { + tx_stress_ctx->dev_addr = event->ext_disc.addr; + /* Stop scanning. */ + ble_gap_disc_cancel(); + /* Add token to semaphore. Main task will start the test. */ + os_sem_release(&tx_stress_main_sem); + } + return 0; + + case BLE_GAP_EVENT_DISC_COMPLETE: + MODLOG_DFLT(INFO, "Discovery complete; reason=%d\n", + event->disc_complete.reason); + return 0; + + default: + MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type); + return 0; + } +} + +static void +tx_stress_switch_test() +{ + tx_stress_simple_scan(tx_stress_switcher_gap_event, 0); + os_sem_pend(&tx_stress_main_sem, OS_TIMEOUT_NEVER); + + tx_stress_simple_connect(tx_stress_switcher_gap_event, 0); +} + +static int +tx_stress_1_gap_event(struct ble_gap_event *event, void *arg) +{ + switch (event->type) { + case BLE_GAP_EVENT_CONNECT: + /* A new connection was established or a connection attempt failed. */ + MODLOG_DFLT(INFO, "Connection %s; status=%d ", + event->connect.status == 0 ? "established" : "failed", + event->connect.status); + + if (event->connect.status == 0) { + /* Connection successfully established. In this use case + * it is error of 'Connect cancel'. Stress test failed. */ + MODLOG_DFLT(INFO, "Success to connect to device\n"); + ++tx_stress_ctx->con_stat[1].num; + + ble_gap_terminate(event->connect.conn_handle, BLE_ERR_NO_PAIRING); + } + return 0; + + case BLE_GAP_EVENT_DISCONNECT: + MODLOG_DFLT(INFO, "Disconnect; reason=%d ", event->disconnect.reason); + return 0; + + default: + MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type); + return 0; + } +} + +static void +tx_stress_1_test() +{ + int rc; + uint8_t own_addr_type; + ble_addr_t rnd_rx_addr; + int delay_time; + + rc = ble_gap_disc_active(); + assert(rc == 0); + + /* Figure out address to use while advertising. */ + rc = ble_hs_id_infer_auto(0, &own_addr_type); + if (rc != 0) { + MODLOG_DFLT(INFO, "\033[0;31mError determining own address type; " + "rc=%d\033[0m\n", rc); + os_sem_release(&tx_stress_main_sem); + return; + } + + while (tx_stress_ctx->con_stat[1].attempts_num < + MYNEWT_VAL(BLE_STRESS_REPEAT)) { + /* Rand ble address to connect*/ + rc = ble_hs_id_gen_rnd(1, &rnd_rx_addr); + assert (rc == 0); + + MODLOG_DFLT(INFO, "Connection attempt; num=%d\n", + ++tx_stress_ctx->con_stat[1].attempts_num); + + rc = ble_gap_connect(own_addr_type, &rnd_rx_addr, 10000, NULL, + tx_stress_1_gap_event, NULL); + + if (rc != 0) { + MODLOG_DFLT(INFO, "\033[0;31mConnection error; rc=%d\033[0m\n", + rc); + os_sem_release(&tx_stress_main_sem); + return; + } + + MODLOG_DFLT(INFO, "Connect cancel\n"); + ble_gap_conn_cancel(); + console_printf("\033[0;32m>\033[0m"); + } + + console_printf( + "\033[0;32m\nFirst part of test completed\nStart second part: " + "Connect->random delay->cancel\n\033[0m"); + + while (tx_stress_ctx->con_stat[1].attempts_num < + 2 * MYNEWT_VAL(BLE_STRESS_REPEAT)) { + /* Rand ble address to connect*/ + rc = ble_hs_id_gen_rnd(1, &rnd_rx_addr); + assert (rc == 0); + + MODLOG_DFLT(INFO, "Connection attempt; num=%d\n", + ++tx_stress_ctx->con_stat[1].attempts_num); + + delay_time = rand() % 1000; + + MODLOG_DFLT(INFO, "Time to delay=%d\n", delay_time); + + rc = ble_gap_connect(own_addr_type, &rnd_rx_addr, 10000, NULL, + tx_stress_1_gap_event, NULL); + + if (rc != 0) { + MODLOG_DFLT(INFO, "\033[0;31mConnection error; rc=%d\033[0m\n", + rc); + os_sem_release(&tx_stress_main_sem); + return; + } + + os_time_delay(os_time_ms_to_ticks32(delay_time)); + + MODLOG_DFLT(INFO, "Connect cancel\n"); + ble_gap_conn_cancel(); + console_printf("\033[0;32m>\033[0m"); + } + + tx_stress_on_test_finish(1); +} + +static int +tx_stress_2_gap_event(struct ble_gap_event *event, void *arg) +{ + struct ble_gap_conn_desc desc; + int rc; + + switch (event->type) { + case BLE_GAP_EVENT_CONNECT: + /* A new connection was established or a connection attempt failed. */ + if (event->connect.status == 0) { + MODLOG_DFLT(INFO, "Success to connect to device\n"); + + ++tx_stress_ctx->con_stat[2].num; + rc = ble_gap_conn_find(event->connect.conn_handle, &desc); + assert(rc == 0); + + tx_stress_ctx->conn_handle = desc.conn_handle; + + ble_gap_terminate(event->connect.conn_handle, + BLE_ERR_REM_USER_CONN_TERM); + } else { + MODLOG_DFLT(INFO, "\033[0;31mError: connection attempt failed; " + "status=%d\033[0m\n", event->connect.status); + os_sem_release(&tx_stress_main_sem); + } + return 0; + + case BLE_GAP_EVENT_DISCONNECT: + MODLOG_DFLT(INFO, "Disconnect; reason=%d \n", + event->disconnect.reason); + console_printf("\033[0;32m>\033[0m"); + + if (tx_stress_ctx->con_stat[2].num >= MYNEWT_VAL(BLE_STRESS_REPEAT)) { + tx_stress_on_test_finish(2); + return 0; + } + tx_stress_simple_connect(tx_stress_2_gap_event, + tx_stress_ctx->cur_test_id); + return 0; + + default: + MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type); + return 0; + } +} + +static int +tx_stress_3_gap_event(struct ble_gap_event *event, void *arg) +{ + int rc; + + switch (event->type) { + case BLE_GAP_EVENT_CONNECT: + /* A new connection was established or a connection attempt failed. */ + if (event->connect.status == 0) { + ++tx_stress_ctx->con_stat[3].num; + MODLOG_DFLT(INFO, "Success to connect to device\n"); + + rc = ble_gap_terminate(event->connect.conn_handle, + BLE_ERR_REM_USER_CONN_TERM); + + MODLOG_DFLT(INFO, "rc=%d\n", rc); + } else { + MODLOG_DFLT(INFO, "\033[0;31mError: connection attempt failed; " + "status=%d\033[0m\n", event->connect.status); + os_sem_release(&tx_stress_main_sem); + } + return 0; + + case BLE_GAP_EVENT_DISCONNECT: + MODLOG_DFLT(INFO, "Disconnect; reason=%d \n", + event->disconnect.reason); + console_printf("\033[0;32m>\033[0m"); + + if (tx_stress_ctx->con_stat[3].num >= MYNEWT_VAL(BLE_STRESS_REPEAT)) { + tx_stress_on_test_finish(3); + return 0; + } + tx_stress_simple_connect(tx_stress_3_gap_event, + tx_stress_ctx->cur_test_id); + return 0; + + default: + MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type); + return 0; + } +} + +static int +tx_stress_4_con_update(void) +{ + int rc; + + /* With every next update at least one param must change. Otherwise no + * event occurs and test will not be continued */ + struct ble_gap_upd_params params = { + .itvl_min = BLE_GAP_INITIAL_CONN_ITVL_MIN, + .itvl_max = BLE_GAP_INITIAL_CONN_ITVL_MAX, + .latency = BLE_GAP_INITIAL_CONN_LATENCY, + /* So let's change e.g. timeout value. Put ...% 2 ? 1 : 2 to make sure + * that value won't grow significantly and will be different with every + * iteration. */ + .supervision_timeout = BLE_GAP_INITIAL_SUPERVISION_TIMEOUT + + (tx_stress_ctx->con_stat[4].prms_upd_num % 2 ? + 1 : 2), + .min_ce_len = BLE_GAP_INITIAL_CONN_MIN_CE_LEN, + .max_ce_len = BLE_GAP_INITIAL_CONN_MAX_CE_LEN, + }; + + rc = ble_gap_update_params(tx_stress_ctx->conn_handle, ¶ms); + + if (rc == BLE_HS_ENOTCONN) { + MODLOG_DFLT(INFO, "Device disconnected. Connection update failed\n"); + assert(0); + } + + if (rc != 0) { + MODLOG_DFLT(ERROR, "\033[0;31mError during connection update; " + "rc=%d\033[0m\n", rc); + assert(0); + } + + return rc; +} + +static int +tx_stress_4_gap_event(struct ble_gap_event *event, void *arg) +{ + int rc; + + switch (event->type) { + case BLE_GAP_EVENT_CONNECT: + ++tx_stress_ctx->con_stat[4].attempts_num; + /* A new connection was established or a connection attempt failed. */ + if (event->connect.status == 0) { + MODLOG_DFLT(INFO, "Success to connect to device\n"); + + ++tx_stress_ctx->con_stat[4].num; + tx_stress_ctx->conn_handle = event->connect.conn_handle; + + tx_stress_4_con_update(); + } else { + MODLOG_DFLT(INFO, "\033[0;31mError: connection attempt failed; " + "status=%d\033[0m\n", event->connect.status); + os_sem_release(&tx_stress_main_sem); + } + return 0; + + case BLE_GAP_EVENT_DISCONNECT: + MODLOG_DFLT(INFO, "Disconnect; reason=%d \n", + event->disconnect.reason); + tx_stress_on_test_finish(4); + return 0; + + case BLE_GAP_EVENT_CONN_UPDATE: + if (event->conn_update.status != 0) { + MODLOG_DFLT(INFO, "Connection update failed\n"); + } else { + MODLOG_DFLT(INFO, "Connection updated; num=%d\n", + ++tx_stress_ctx->con_stat[4].prms_upd_num); + console_printf("\033[0;32m>\033[0m"); + } + + if (tx_stress_ctx->con_stat[4].prms_upd_num >= + MYNEWT_VAL(BLE_STRESS_REPEAT)) { + ble_gap_terminate(tx_stress_ctx->conn_handle, + BLE_ERR_REM_USER_CONN_TERM); + } else { + /* Update connection. */ + rc = tx_stress_4_con_update(); + + if (rc != 0) { + MODLOG_DFLT(INFO, "\033[0;31mError: update fail; " + "rc=%d\033[0m\n", rc); + os_sem_release(&tx_stress_main_sem); + } + } + return 0; + + case BLE_GAP_EVENT_CONN_UPDATE_REQ: + MODLOG_DFLT(INFO, "Connection update request\n"); + return 0; + + default: + MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type); + return 0; + } +} + +static int +tx_stress_5_gap_event(struct ble_gap_event *event, void *arg) +{ + switch (event->type) { + case BLE_GAP_EVENT_CONNECT: + /* A new connection was established or a connection attempt failed. */ + if (event->connect.status == 0) { + MODLOG_DFLT(INFO, "Success to connect to device\n"); + + ++tx_stress_ctx->con_stat[5].num; + tx_stress_ctx->conn_handle = event->connect.conn_handle; + } else { + console_printf("\033[0;31mError: Update fail; " + "status=%d\033[0m\n", event->connect.status); + os_sem_release(&tx_stress_main_sem); + } + return 0; + + case BLE_GAP_EVENT_DISCONNECT: + MODLOG_DFLT(INFO, "Disconnect; reason=%d \n", + event->disconnect.reason); + tx_stress_on_test_finish(5); + return 0; + + case BLE_GAP_EVENT_CONN_UPDATE: + if (event->conn_update.status != 0) { + MODLOG_DFLT(INFO, "Connection update failed\n"); + } else { + MODLOG_DFLT(INFO, "Connection updated; num=%d\n", + ++tx_stress_ctx->con_stat[5].prms_upd_num); + console_printf("\033[0;32m>\033[0m"); + } + + if (tx_stress_ctx->con_stat[5].prms_upd_num >= + MYNEWT_VAL(BLE_STRESS_REPEAT)) { + ble_gap_terminate(tx_stress_ctx->conn_handle, + BLE_ERR_REM_USER_CONN_TERM); + } + return 0; + + case BLE_GAP_EVENT_CONN_UPDATE_REQ: + MODLOG_DFLT(INFO, "Connection update request\n"); + return 0; + + default: + MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type); + return 0; + } +} + +static int +tx_stress_6_gap_event(struct ble_gap_event *event, void *arg) +{ + static int start_id = 0; + int use_case = 0; + int adv_pattern_len; + const uint8_t *adv_pattern; + + switch (event->type) { + case BLE_GAP_EVENT_EXT_DISC: + + /* Instance 0 reserved for SWITCH advert. */ + if (event->ext_disc.sid == 0) { + return 0; + } + + /* Check if advertiser is known rx device. */ + if (memcmp(tx_stress_ctx->dev_addr.val, + event->ext_disc.addr.val, 6) != 0) { + return 0; + } + + /* Return -1 if not first package of advert. */ + use_case = tx_stress_find_test(&event->ext_disc); + + if (use_case > 0) { + /* If first package of advert */ + ++tx_stress_ctx->s6_rcv_adv_first; + start_id = 0; + adv_pattern = &event->ext_disc.data[29]; + adv_pattern_len = event->ext_disc.length_data - 29; + } else { + if (start_id == 0) { + return 0; + } + /* If not first package of advert */ + adv_pattern = event->ext_disc.data; + adv_pattern_len = event->ext_disc.length_data; + } + + /* Check data pattern */ + if (memcmp(adv_pattern, test_6_pattern + start_id, + adv_pattern_len) != 0) { + /* Pattern does not match. May lost some data or package. + * Reset data pattern index. */ + start_id = 0; + return 0; + } + + /* At the next adv data package, start comparing from this index.*/ + start_id += adv_pattern_len; + + /* Check if last packet of advert. */ + if (event->ext_disc.data_status == + BLE_GAP_EXT_ADV_DATA_STATUS_COMPLETE) { + /* Got all packets of advert. */ + ++tx_stress_ctx->s6_rcv_adv_suc; + MODLOG_DFLT(INFO, "Got all packets of advert. num=%d\n", + tx_stress_ctx->s6_rcv_adv_suc); + console_printf("\033[0;32m>\033[0m"); + start_id = 0; + + if (tx_stress_ctx->s6_rcv_adv_suc >= + MYNEWT_VAL(BLE_STRESS_REPEAT)) { + /* Stop scanning. */ + ble_gap_disc_cancel(); + tx_stress_on_test_finish(6); + } + } + return 0; + + case BLE_GAP_EVENT_DISC_COMPLETE: + MODLOG_DFLT(INFO, "Discovery complete; reason=%d\n", + event->disc_complete.reason); + return 0; + + default: + MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type); + return 0; + } +} + +static void +tx_stress_6_perform(void) +{ + tx_stress_simple_scan(tx_stress_6_gap_event, 0); + os_sem_pend(&tx_stress_main_sem, OS_TIMEOUT_NEVER); + tx_stress_switch_test(); +} + +static int +tx_stress_7_phy_update(void) +{ + int rc; + uint8_t tx_phys_mask; + uint8_t rx_phys_mask; + + ble_gap_read_le_phy(tx_stress_ctx->conn_handle, &tx_phys_mask, + &rx_phys_mask); + + + /* With every next update at least one param must change */ + switch (rx_phys_mask) { + case BLE_GAP_LE_PHY_1M_MASK: + rx_phys_mask = BLE_GAP_LE_PHY_2M_MASK; + break; + case BLE_GAP_LE_PHY_2M_MASK: +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CODED_PHY) + rx_phys_mask = BLE_GAP_LE_PHY_CODED_MASK; + break; + case BLE_GAP_LE_PHY_CODED_MASK: +#endif + rx_phys_mask = BLE_GAP_LE_PHY_1M_MASK; + break; + default: + rx_phys_mask = BLE_GAP_LE_PHY_1M_MASK; + break; + } + + switch (tx_phys_mask) { + case BLE_GAP_LE_PHY_1M_MASK: + tx_phys_mask = BLE_GAP_LE_PHY_2M_MASK; + break; + case BLE_GAP_LE_PHY_2M_MASK: +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CODED_PHY) + tx_phys_mask = BLE_GAP_LE_PHY_CODED_MASK; + break; + case BLE_GAP_LE_PHY_CODED_MASK: +#endif + tx_phys_mask = BLE_GAP_LE_PHY_1M_MASK; + break; + default: + tx_phys_mask = BLE_GAP_LE_PHY_1M_MASK; + break; + } + + rc = ble_gap_set_prefered_le_phy(tx_stress_ctx->conn_handle, + tx_phys_mask, rx_phys_mask, 0); + + if (rc == BLE_HS_ENOTCONN) { + MODLOG_DFLT(INFO, "Device disconnected. Connection update failed\n"); + return rc; + } + + if (rc != 0) { + MODLOG_DFLT(ERROR, "\033[0;31mError during PHY update; " + "rc=%d\033[0m\n", rc); + } + + return rc; +} + +static int +tx_stress_7_gap_event(struct ble_gap_event *event, void *arg) +{ + int rc; + + switch (event->type) { + case BLE_GAP_EVENT_CONNECT: + /* A new connection was established or a connection attempt failed. */ + if (event->connect.status == 0) { + MODLOG_DFLT(INFO, "Success to connect to device\n"); + + ++tx_stress_ctx->con_stat[7].num; + tx_stress_ctx->conn_handle = event->connect.conn_handle; + + tx_stress_7_phy_update(); + } else { + console_printf("\033[0;31mError: Update fail; " + "status=%d\033[0m\n", event->connect.status); + os_sem_release(&tx_stress_main_sem); + } + return 0; + + case BLE_GAP_EVENT_DISCONNECT: + MODLOG_DFLT(INFO, "Disconnect; reason=%d \n", + event->disconnect.reason); + tx_stress_on_test_finish(7); + return 0; + + case BLE_GAP_EVENT_CONN_UPDATE: + MODLOG_DFLT(INFO, "Connection updated\n"); + return 0; + + case BLE_GAP_EVENT_CONN_UPDATE_REQ: + MODLOG_DFLT(INFO, "Connection update request\n"); + return 0; + + case BLE_GAP_EVENT_PHY_UPDATE_COMPLETE: + if (event->phy_updated.status != 0) { + MODLOG_DFLT(INFO, "PHY update failed\n"); + } else { + MODLOG_DFLT(INFO, "PHY updated; num=%d; rx:%d, tx:%d\n", + ++tx_stress_ctx->con_stat[7].phy_upd_num, + event->phy_updated.rx_phy, event->phy_updated.tx_phy); + console_printf("\033[0;32m>\033[0m"); + } + + if (tx_stress_ctx->con_stat[7].phy_upd_num >= + MYNEWT_VAL(BLE_STRESS_REPEAT)) { + ble_gap_terminate(tx_stress_ctx->conn_handle, + BLE_ERR_REM_USER_CONN_TERM); + } else { + /* Update connection. */ + rc = tx_stress_7_phy_update(); + if (rc != 0) { + console_printf("\033[0;31mError: PHPY update fail; " + "rc=%d\033[0m\n", event->phy_updated.status); + assert(0); + } + } + return 0; + + default: + MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type); + return 0; + } +} + +static int +tx_stress_8_gap_event(struct ble_gap_event *event, void *arg) +{ + switch (event->type) { + case BLE_GAP_EVENT_CONNECT: + /* A new connection was established or a connection attempt failed. */ + if (event->connect.status == 0) { + MODLOG_DFLT(INFO, "Success to connect to device\n"); + + ++tx_stress_ctx->con_stat[8].num; + tx_stress_ctx->conn_handle = event->connect.conn_handle; + } else { + MODLOG_DFLT(INFO, "\033[0;31mError: connection attempt failed; " + "status=%d\033[0m\n", event->connect.status); + os_sem_release(&tx_stress_main_sem); + } + return 0; + + case BLE_GAP_EVENT_DISCONNECT: + MODLOG_DFLT(INFO, "Disconnect; reason=%d \n", + event->disconnect.reason); + tx_stress_on_test_finish(8); + return 0; + + case BLE_GAP_EVENT_CONN_UPDATE: + if (event->conn_update.status != 0) { + MODLOG_DFLT(INFO, "Connection update failed\n"); + } else { + MODLOG_DFLT(INFO, "Connection updated; num=%d\n", + ++tx_stress_ctx->con_stat[8].prms_upd_num); + } + return 0; + + case BLE_GAP_EVENT_CONN_UPDATE_REQ: + MODLOG_DFLT(INFO, "Connection update request\n"); + return 0; + + case BLE_GAP_EVENT_PHY_UPDATE_COMPLETE: + if (event->phy_updated.status != 0) { + MODLOG_DFLT(INFO, "PHY update failed\n"); + } else { + MODLOG_DFLT(INFO, "PHY updated; num=%d\n", + ++tx_stress_ctx->con_stat[8].phy_upd_num); + console_printf("\033[0;32m>\033[0m"); + } + + if (tx_stress_ctx->con_stat[8].phy_upd_num >= + MYNEWT_VAL(BLE_STRESS_REPEAT)) { + ble_gap_terminate(tx_stress_ctx->conn_handle, + BLE_ERR_REM_USER_CONN_TERM); + } + return 0; + + default: + MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type); + return 0; + } +} + +static int +tx_stress_9_gap_event(struct ble_gap_event *event, void *arg) +{ + ble_addr_t addr; + int test; + + switch (event->type) { + case BLE_GAP_EVENT_EXT_DISC: + /* Looking for next instance of test 9 advert. */ + test = tx_stress_find_test(&event->ext_disc); + /* To avoid messing by rest of test 9 events in queue, check if handle + * filled */ + if (test == 9 && tx_stress_ctx->conn_handle == 0xffff) { + tx_stress_ctx->conn_handle = 0; + ble_gap_disc_cancel(); + tx_stress_ctx->dev_addr = event->ext_disc.addr; + tx_stress_simple_connect(tx_stress_9_gap_event, + tx_stress_ctx->cur_test_id); + } + return 0; + + case BLE_GAP_EVENT_DISC_COMPLETE: + if (event->disc_complete.reason == 0 && !tx_stress_ctx->completed[9]) { + console_printf("\033[0;31mScanning timeout\033[0m"); + tx_stress_ctx->completed[9] = true; + os_sem_release(&tx_stress_main_sem); + return 0; + } + break; + + case BLE_GAP_EVENT_CONNECT: + /* A new connection was established or a connection attempt failed. */ + if (event->connect.status == 0) { + MODLOG_DFLT(INFO, "Success to connect to device\n"); + MODLOG_DFLT(INFO, "Connections num: %d\n", + ++tx_stress_ctx->con_stat[9].num); + console_printf("\033[0;32m>\033[0m"); + /* Remember max number of handled connections */ + if (tx_stress_ctx->con_stat[9].num > + tx_stress_ctx->con_stat[9].max_num) { + tx_stress_ctx->con_stat[9].max_num = tx_stress_ctx->con_stat[9].num; + } + } else { + MODLOG_DFLT(INFO, "\033[0;31mError: connection attempt failed; " + "status=%d\033[0m\n", event->connect.status); + } + break; + + case BLE_GAP_EVENT_DISCONNECT: + MODLOG_DFLT(INFO, "Disconnect; reason=%d ", event->disconnect.reason); + console_printf("\033[0;31mX\033[0m"); + MODLOG_DFLT(INFO, "Connections num: %d\n", + --tx_stress_ctx->con_stat[9].num); + + if (tx_stress_ctx->con_stat[9].num == 0) { + os_sem_release(&tx_stress_main_sem); + return 0; + } + break; + + default: + MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type); + return 0; + } + + if (tx_stress_ctx->completed[9]) { + return 0; + } + + /* End use case after specified number of scan times or max config number + * of connections */ + if (tx_stress_ctx->con_stat[9].attempts_num < + MYNEWT_VAL(BLE_STRESS_REPEAT) && + tx_stress_ctx->con_stat[9].max_num < MYNEWT_VAL(BLE_MAX_CONNECTIONS)) { + if (ble_gap_disc_active() == 0) { + /* Generate and set new random address */ + ble_hs_id_gen_rnd(0, &addr); + ble_hs_id_set_rnd(addr.val); + tx_stress_ctx->conn_handle = 0xffff; + /* Scan for next instance of the test. */ + tx_stress_simple_scan(tx_stress_9_gap_event, 2000); + } + } else { + tx_stress_ctx->completed[9] = true; + os_sem_release(&tx_stress_main_sem); + } + return 0; +} + +static void +tx_stress_9_perform() +{ + int i, rc; + + /* Scan for next instance of the test. */ + tx_stress_simple_scan(tx_stress_9_gap_event, 2000); + + os_sem_pend(&tx_stress_main_sem, OS_TIMEOUT_NEVER); + + /* On use case finishing terminate all handled connections */ + for (i = 0; i <= MYNEWT_VAL(BLE_MAX_CONNECTIONS); ++i) { + rc = ble_gap_conn_find(i, NULL); + if (rc == 0) { + MODLOG_DFLT(INFO, "Terminating...\n"); + ble_gap_terminate(i, BLE_ERR_REM_USER_CONN_TERM); + } + } + + os_sem_pend(&tx_stress_main_sem, OS_TIMEOUT_NEVER); + tx_stress_on_test_finish(9); +} + +static int +tx_stress_10_l2cap_send(struct ble_l2cap_chan *chan, uint8_t *data, + int data_len) +{ + struct os_mbuf *data_buf; + int rc; + + /* Get mbuf for adv data */ + data_buf = os_msys_get_pkthdr(data_len, 0); + assert(data != NULL); + + /* Fill mbuf with pattern data */ + rc = os_mbuf_append(data_buf, data, data_len); + + if (rc) { + os_mbuf_free_chain(data_buf); + assert(0); + } + + /* Send data with L2CAP */ + rc = ble_l2cap_send(chan, data_buf); + + return rc; +} + +void tx_stress_10_timer_ev_cb(struct os_event *ev) +{ + assert(ev != NULL); + + if (tx_stress_ctx->rcv_data_flag) { + return; + } + + tx_stress_ctx->timeout_flag = true; + MODLOG_DFLT(INFO, "L2CAP receiving timeout\n"); + ble_gap_terminate(tx_stress_ctx->conn_handle, + BLE_ERR_REM_USER_CONN_TERM); +} + +static void +tx_stress_10_l2cap_send_req() +{ + int rc; + /* Send a request to the RX device */ + + MODLOG_DFLT(INFO, "L2CAP sending request\n"); + tx_stress_ctx->timeout_flag = false; + tx_stress_ctx->rcv_data_flag = false; + stress_start_timer(7000, tx_stress_10_timer_ev_cb); + + /* Get the sending begin time */ + tx_stress_ctx->begin_us = os_get_uptime_usec(); + + /* Send anything just to give a signal to start sending data + * by RX device */ + rc = tx_stress_10_l2cap_send(tx_stress_ctx->chan, (uint8_t *) "S", + sizeof("S")); + assert(rc == 0); +} + +static int +tx_stress_10_l2cap_event(struct ble_l2cap_event *event, void *arg) +{ + static int i = 0; + int64_t us = 0; + struct ble_l2cap_chan_info chan_info; + + switch (event->type) { + case BLE_L2CAP_EVENT_COC_CONNECTED: + /* A new L2CAP connection was established. */ + if (event->connect.status == 0) { + MODLOG_DFLT(INFO, "Established L2CAP connection\n"); + tx_stress_ctx->chan = event->connect.chan; + + ble_l2cap_get_chan_info(event->connect.chan, &chan_info); + + MODLOG_DFLT(INFO, + "LE COC connected, conn: %d, chan: 0x%08lx, scid: 0x%04x, " + "dcid: 0x%04x, our_mtu: 0x%04x, peer_mtu: 0x%04x\n", + event->connect.conn_handle, + (uint32_t) event->connect.chan, + chan_info.scid, + chan_info.dcid, + chan_info.our_l2cap_mtu, + chan_info.peer_l2cap_mtu); + + tx_stress_10_l2cap_send_req(); + } + return 0; + + case BLE_L2CAP_EVENT_COC_DISCONNECTED: + MODLOG_DFLT(INFO, "Remote device disconnected from L2CAP server\n"); + return 0; + + case BLE_L2CAP_EVENT_COC_ACCEPT: + stress_l2cap_coc_accept(event->accept.peer_sdu_size, + event->accept.chan); + return 0; + + case BLE_L2CAP_EVENT_COC_DATA_RECEIVED: + /* Get the time of data receive */ + tx_stress_ctx->end_us = os_get_uptime_usec(); + + /* And test after timeout */ + if (tx_stress_ctx->timeout_flag) { + ble_gap_terminate(tx_stress_ctx->conn_handle, + BLE_ERR_REM_USER_CONN_TERM); + return 0; + } + + tx_stress_ctx->rcv_data_flag = true; + + stress_l2cap_coc_recv(event->receive.chan, event->receive.sdu_rx); + + /* Time of data sending */ + us = tx_stress_ctx->end_us - tx_stress_ctx->begin_us; + MODLOG_DFLT(INFO, "Time of receiving L2CAP data: %ld \n", + tx_stress_ctx->end_us); + + /* Remember size of entire mbuf chain */ + tx_stress_ctx->rcv_data_bytes = OS_MBUF_PKTLEN( + event->receive.sdu_rx); + MODLOG_DFLT(INFO, "Num of received bytes: %lld\n", + tx_stress_ctx->rcv_data_bytes); + + /* Calculate the bit rate of this send */ + tx_stress_ctx->s10_bit_rate = + stress_calc_bit_rate(us, tx_stress_ctx->rcv_data_bytes); + MODLOG_DFLT(INFO, "Bit rate: %d B/s\n", tx_stress_ctx->s10_bit_rate); + + /* Remember the sum of bytes and the time to calculate the average + * bit rate. */ + tx_stress_ctx->bytes_sum += tx_stress_ctx->rcv_data_bytes; + tx_stress_ctx->time_sum += us; + + /* Remember max received MTU */ + if (tx_stress_ctx->s10_max_mtu < tx_stress_ctx->rcv_data_bytes) { + tx_stress_ctx->s10_max_mtu = tx_stress_ctx->rcv_data_bytes; + } + console_printf("\033[0;32m>\033[0m"); + MODLOG_DFLT(INFO, "Loop nr: %d\n", ++i); + + tx_stress_10_l2cap_send_req(); + return 0; + + case BLE_L2CAP_EVENT_COC_TX_UNSTALLED: + MODLOG_DFLT(INFO, "L2CAP event unstalled\n"); + return 0; + + default: + MODLOG_DFLT(INFO, "Other L2CAP event occurs; rc=%d\n", event->type); + return 0; + } +} + +static int +tx_stress_10_gap_event(struct ble_gap_event *event, void *arg) +{ + int rc; + struct os_mbuf *sdu_rx; + + switch (event->type) { + case BLE_GAP_EVENT_CONNECT: + /* A new connection was established or a connection attempt failed. */ + if (event->connect.status == 0) { + MODLOG_DFLT(INFO, "Success to connect to device; num: %d\n", + ++tx_stress_ctx->con_stat[10].num); + + sdu_rx = os_msys_get_pkthdr(STRESS_COC_MTU, 0); + assert(sdu_rx != NULL); + + tx_stress_ctx->conn_handle = event->connect.conn_handle; + rc = ble_l2cap_connect(event->connect.conn_handle, TEST_PSM, + STRESS_COC_MTU, sdu_rx, + tx_stress_10_l2cap_event, NULL); + assert(rc == 0); + } else { + MODLOG_DFLT(INFO, "\033[0;31mError: connection attempt failed; " + "status=%d\033[0m\n", event->connect.status); + } + return 0; + + case BLE_GAP_EVENT_DISCONNECT: + tx_stress_ctx->s10_bit_rate = 1000000 * tx_stress_ctx->bytes_sum / + tx_stress_ctx->time_sum; + + MODLOG_DFLT(INFO, "Average bit rate: %d B/s\n", + tx_stress_ctx->s10_bit_rate); + tx_stress_on_test_finish(10); + return 0; + + default: + MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type); + return 0; + } +} + +static int +tx_stress_11_gap_event(struct ble_gap_event *event, void *arg) +{ + int test; + + switch (event->type) { + case BLE_GAP_EVENT_EXT_DISC: + /* Looking for next instance of test 9 advert. */ + test = tx_stress_find_test(&event->ext_disc); + + /* To avoid messing by rest of test 9 events in queue, check if handle + * filled */ + if (test == 11 && tx_stress_ctx->conn_handle == 0xffff) { + tx_stress_ctx->conn_handle = 0; + ble_gap_disc_cancel(); + tx_stress_ctx->dev_addr = event->ext_disc.addr; + tx_stress_simple_connect(tx_stress_11_gap_event, + tx_stress_ctx->cur_test_id); + } + return 0; + + case BLE_GAP_EVENT_CONNECT: + /* A new connection was established or a connection attempt failed. */ + if (event->connect.status == 0) { + ++tx_stress_ctx->con_stat[11].num; + MODLOG_DFLT(INFO, "Success to connect to device\n"); + } else { + MODLOG_DFLT(INFO, "\033[0;31mError: connection attempt failed; " + "status=%d\033[0m\n", event->connect.status); + break; + } + return 0; + + case BLE_GAP_EVENT_DISCONNECT: + MODLOG_DFLT(INFO, "Disconnect; reason=%d \n", + event->disconnect.reason); + console_printf("\033[0;32m>\033[0m"); + + if (tx_stress_ctx->con_stat[11].num >= MYNEWT_VAL(BLE_STRESS_REPEAT)) { + tx_stress_on_test_finish(11); + return 0; + } + break; + + default: + MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type); + return 0; + } + + tx_stress_ctx->conn_handle = 0xffff; + + /* Scan for next instance of the test. */ + tx_stress_simple_scan(tx_stress_11_gap_event, 750); + + return 0; +} + +static int +tx_stress_12_gap_event(struct ble_gap_event *event, void *arg) +{ + switch (event->type) { + case BLE_GAP_EVENT_CONNECT: + /* A new connection was established or a connection attempt failed. */ + if (event->connect.status == 0) { + ++tx_stress_ctx->con_stat[12].num; + MODLOG_DFLT(INFO, "Success to connect to device\n"); + tx_stress_ctx->conn_handle = event->connect.conn_handle; + } else { + MODLOG_DFLT(INFO, "\033[0;31mError: connection attempt failed; " + "status=%d\033[0m\n", event->connect.status); + } + return 0; + + case BLE_GAP_EVENT_DISCONNECT: + MODLOG_DFLT(INFO, "Disconnect; reason=%d \n", + event->disconnect.reason); + /* Finish test after first disconnection */ + tx_stress_on_test_finish(12); + return 0; + + case BLE_GAP_EVENT_NOTIFY_RX: + /* Received indication */ + MODLOG_DFLT(INFO, "Notify RX event\n"); + console_printf("\033[0;32m>\033[0m"); + return 0; + + default: + MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type); + return 0; + } +} + +static int +tx_stress_13_gap_event(struct ble_gap_event *event, void *arg) +{ + switch (event->type) { + case BLE_GAP_EVENT_CONNECT: + /* A new connection was established or a connection attempt failed. */ + if (event->connect.status == 0) { + ++tx_stress_ctx->con_stat[13].num; + MODLOG_DFLT(INFO, "Success to connect to device\n"); + tx_stress_ctx->conn_handle = event->connect.conn_handle; + } else { + MODLOG_DFLT(INFO, "\033[0;31mError: connection attempt failed; " + "status=%d\033[0m\n", event->connect.status); + assert(0); + } + return 0; + + case BLE_GAP_EVENT_DISCONNECT: + MODLOG_DFLT(INFO, "Disconnect; reason=%d \n", + event->disconnect.reason); + /* Finish test after disconnection */ + tx_stress_on_test_finish(13); + return 0; + + case BLE_GAP_EVENT_NOTIFY_RX: + MODLOG_DFLT(INFO, "Notify RX event\n"); + console_printf("\033[0;32m>\033[0m"); + ++tx_stress_ctx->rcv_num; + return 0; + + default: + MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type); + return 0; + } +} + +static int +tx_stress_14_subs_cb(uint16_t conn_handle, const struct ble_gatt_error *error, + struct ble_gatt_attr *attr, void *arg) +{ + struct os_mbuf *om; + bool *sub; + int rc; + + assert(error->status == 0); + + /* If the first subscription after finding cccd */ + if (arg == NULL) { + return 0; + } + + sub = (bool *)arg; + + /* Enable notifications */ + if (*sub == 0) { + *sub = true; + om = ble_hs_mbuf_from_flat((uint8_t[]) {0x01, 0x00}, 2); + + tx_stress_ctx->begin_us = tx_stress_ctx->end_us; + + rc = ble_gattc_write(tx_stress_ctx->conn_handle, + tx_stress_ctx->dsc_handle, om, + tx_stress_14_subs_cb, arg); + assert(rc == 0); + } + + return 0; +} + +static void +tx_stress_14_disc_cccd_fn(struct stress_gatt_search_ctx *search_ctx) +{ + int rc; + struct os_mbuf *om; + MODLOG_DFLT(INFO, "CCCD found\n"); + + /* Enable notifications */ + om = ble_hs_mbuf_from_flat((uint8_t[]) {0x01, 0x00}, 2); + tx_stress_ctx->begin_us = os_get_uptime_usec(); + tx_stress_ctx->dsc_handle = search_ctx->dsc_handle; + + rc = ble_gattc_write(tx_stress_ctx->conn_handle, + tx_stress_ctx->dsc_handle, om, + tx_stress_14_subs_cb, NULL); + assert(rc == 0); +} + +static int +tx_stress_14_gap_event(struct ble_gap_event *event, void *arg) +{ + int64_t us = 0; + struct os_mbuf *om; + int rc; + static bool subscribed = true; + + switch (event->type) { + case BLE_GAP_EVENT_CONNECT: + /* A new connection was established or a connection attempt failed. */ + if (event->connect.status == 0) { + ++tx_stress_ctx->con_stat[14].num; + MODLOG_DFLT(INFO, "Success to connect to device\n"); + tx_stress_ctx->conn_handle = event->connect.conn_handle; + + /* Find CCCD handle (with default UUID16 = 0x2902) */ + stress_find_dsc_handle(event->connect.conn_handle, + BLE_UUID16_DECLARE(STRESS_GATT_UUID), + BLE_UUID16_DECLARE(STRESS_GATT_NOTIFY_UUID), + BLE_UUID16_DECLARE(0x2902), + &tx_stress_14_disc_cccd_fn); + } else { + MODLOG_DFLT(INFO, "\033[0;31mError: connection attempt failed; " + "status=%d\033[0m\n", event->connect.status); + assert(0); + } + return 0; + + case BLE_GAP_EVENT_DISCONNECT: + MODLOG_DFLT(INFO, "Disconnect; reason=%d \n", + event->disconnect.reason); + /* Calc average notifying time */ + if (tx_stress_ctx->rcv_num > 0) { + tx_stress_ctx->s14_notif_time = tx_stress_ctx->time_sum / + tx_stress_ctx->rcv_num; + } + MODLOG_DFLT(INFO, "Average notification time: %d\n", + tx_stress_ctx->s14_notif_time); + /* Finish test after first disconnection */ + tx_stress_on_test_finish(14); + return 0; + + case BLE_GAP_EVENT_NOTIFY_RX: + tx_stress_ctx->end_us = os_get_uptime_usec(); + MODLOG_DFLT(INFO, "Notify RX event\n"); + + /* Time of data sending */ + us = tx_stress_ctx->end_us - tx_stress_ctx->begin_us; + MODLOG_DFLT(INFO, "Notification time: %lld\n us", us); + + tx_stress_ctx->time_sum += us; + console_printf("\033[0;32m>\033[0m"); + + /* Perform use case specified number of times */ + if (++tx_stress_ctx->rcv_num >= MYNEWT_VAL(BLE_STRESS_REPEAT)) { + rc = ble_gap_terminate(event->notify_rx.conn_handle, + BLE_ERR_REM_USER_CONN_TERM); + MODLOG_DFLT(INFO, "rc=%d\n", rc); + assert(rc == 0); + return 0; + } + + /* Disable notifications */ + subscribed = false; + om = ble_hs_mbuf_from_flat( + (uint8_t[]) {0x00, 0x00}, 2); + + rc = ble_gattc_write(tx_stress_ctx->conn_handle, + tx_stress_ctx->dsc_handle, om, + tx_stress_14_subs_cb, &subscribed); + assert(rc == 0); + return 0; + + default: + MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type); + return 0; + } +} + +static int +tx_stress_15_write_cb(uint16_t conn_handle, const struct ble_gatt_error *error, + struct ble_gatt_attr *attr, void *arg) +{ + /* Disconnect */ + ble_gap_terminate(conn_handle, BLE_ERR_REM_USER_CONN_TERM); + console_printf("\033[0;32m>\033[0m"); + return 0; +} + +static void +tx_stress_15_disc_chr_fn(struct stress_gatt_search_ctx *search_ctx) +{ + int rc; + struct os_mbuf *om; + + /* Send some data */ + MODLOG_DFLT(INFO, "Write to chr\n"); + om = ble_hs_mbuf_from_flat(test_6_pattern, 20); + + rc = ble_gattc_write(tx_stress_ctx->conn_handle, + search_ctx->chr_start_handle, om, + tx_stress_15_write_cb, NULL); + assert(rc == 0); +} + +static int +tx_stress_15_gap_event(struct ble_gap_event *event, void *arg) +{ + switch (event->type) { + case BLE_GAP_EVENT_CONNECT: + /* A new connection was established or a connection attempt failed. */ + if (event->connect.status == 0) { + MODLOG_DFLT(INFO, "Success to connect to device; num: %d\n", + ++tx_stress_ctx->con_stat[15].num); + tx_stress_ctx->conn_handle = event->connect.conn_handle; + + /* Find characteristic handle */ + stress_find_chr_handle(event->connect.conn_handle, + BLE_UUID16_DECLARE(STRESS_GATT_UUID), + BLE_UUID16_DECLARE(STRESS_GATT_WRITE_UUID), + &tx_stress_15_disc_chr_fn); + } else { + MODLOG_DFLT(INFO, "\033[0;31mError: connection attempt failed; " + "status=%d\033[0m\n", event->connect.status); + assert(0); + } + return 0; + + case BLE_GAP_EVENT_DISCONNECT: + /* Perform use case specified number of times */ + if (tx_stress_ctx->con_stat[15].num >= MYNEWT_VAL(BLE_STRESS_REPEAT)) { + tx_stress_on_test_finish(15); + return 0; + } + /* Reconnect */ + tx_stress_simple_connect(tx_stress_15_gap_event, 15); + return 0; + + default: + MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type); + return 0; + } +} + +static int +scan_for_test_gap_event(struct ble_gap_event *event, void *arg) +{ + int use_case; + int rc; + + switch (event->type) { + case BLE_GAP_EVENT_EXT_DISC: + /* Check if caught advert contains known UUID128. The UUID128 + * contains the ID of test use case to be executed. */ + use_case = tx_stress_find_test(&event->ext_disc); + if (use_case > 0) { + rc = ble_gap_disc_cancel(); + tx_stress_ctx->dev_addr = event->ext_disc.addr; + + /* After discovery cancel there are still some events in queue. */ + if (rc == 0) { + tx_stress_use_case = use_case; + /* Add token to semaphore. Main task will start the test. */ + os_sem_release(&tx_stress_main_sem); + } + } + return 0; + + case BLE_GAP_EVENT_DISC_COMPLETE: + /* On timeout */ + tx_stress_ctx->scan_timeout = true; + console_printf("\033[1;36mDiscover complete\033[0m\n"); + os_sem_release(&tx_stress_main_sem); + return 0; + + default: + MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type); + return 0; + } +} + +static void +tx_stress_test_perform(int test_num) +{ + /* Perform every test only once */ +// if (test_num <= 0 || tx_stress_ctx->completed[test_num] == true) { +// return; +// } + + tx_stress_ctx->cur_test_id = test_num; + tx_stress_ctx->completed[test_num] = false; + tx_stress_ctx->conn_handle = 0xffff; + + console_printf("\033[1;36mStart test num %d - ", test_num); + + /* Start test */ + switch (test_num) { + case 0: + return; + case 1: + console_printf("Stress Connect -> Connect Cancel\033[0m\n"); + tx_stress_1_test(); + break; + case 2: + console_printf("Stress Connect/Disconnect legacy\033[0m\n"); + tx_stress_simple_connect(&tx_stress_2_gap_event, 2); + break; + case 3: + console_printf("Stress Connect/Disconnect ext adv\033[0m\n"); + tx_stress_simple_connect(&tx_stress_3_gap_event, 3); + break; + case 4: + console_printf("Stress connection params update (TX)\033[0m\n"); + tx_stress_simple_connect(&tx_stress_4_gap_event, 4); + break; + case 5: + console_printf("Stress connection params update (RX)\033[0m\n"); + tx_stress_simple_connect(&tx_stress_5_gap_event, 5); + break; + case 6: + console_printf("Stress Scan\033[0m\n"); + tx_stress_6_perform(); + break; + case 7: + console_printf("Stress PHY Update (TX)\033[0m\n"); + tx_stress_simple_connect(&tx_stress_7_gap_event, 7); + break; + case 8: + console_printf("Stress PHY Update (RX)\033[0m\n"); + tx_stress_simple_connect(&tx_stress_8_gap_event, 8); + break; + case 9: + console_printf("Stress multi connection\033[0m\n"); + tx_stress_9_perform(); + break; + case 10: + console_printf("Stress L2CAP send\033[0m\n"); + tx_stress_simple_connect(&tx_stress_10_gap_event, 10); + break; + case 11: + console_printf("Stress Advertise/Connect/Disconnect\033[0m\n"); + tx_stress_simple_connect(&tx_stress_11_gap_event, 11); + break; + case 12: + console_printf("Stress GATT indication\033[0m\n"); + tx_stress_simple_connect(&tx_stress_12_gap_event, 12); + break; + case 13: + console_printf("Stress GATT notification\033[0m\n"); + tx_stress_simple_connect(&tx_stress_13_gap_event, 13); + break; + case 14: + console_printf("Stress GATT Subscribe/Notify/Unsubscribe\033[0m\n"); + tx_stress_simple_connect(&tx_stress_14_gap_event, 14); + break; + case 15: + console_printf("Stress Connect/Send/Disconnect\033[0m\n"); + tx_stress_simple_connect(&tx_stress_15_gap_event, 15); + break; + default: + console_printf("\033[0;31mFound test, but do not know how to perform." + "\033[0m\n"); + assert(0); + } + + /* Wait for the test to finish. Then 1 token will be released + * allowing to pass through semaphore. */ + os_sem_pend(&tx_stress_main_sem, OS_TIMEOUT_NEVER); + + stress_clear_ctx_reusable_var(tx_stress_ctx); +} + +static void +tx_stress_read_command_cb(void) +{ + console_printf("Start testing\n"); + os_sem_release(&tx_stress_main_sem); +} + +static void +tx_stress_main_task_fn(void *arg) +{ + int rc; + + tx_stress_ctx = &tx_stress_ctxD; + + console_printf("\033[1;36mTX device\033[0m\n"); + console_printf("Press ENTER to start: \n"); + console_init(&tx_stress_read_command_cb); + + /* Waite for pressing ENTER in console */ + os_sem_pend(&tx_stress_main_sem, OS_TIMEOUT_NEVER); + + /* Init semaphore with 0 tokens. */ + rc = os_sem_init(&tx_stress_main_sem, 0); + assert(rc == 0); + + /* Start test 1 - Connect/Connect cancel */ + //tx_stress_test_perform(1); + + while (1) { + console_printf("\033[0;36mStart scan for test\033[0m\n"); + + /* Scan for known UUID128 of one of the stress tests. */ + tx_stress_simple_scan(scan_for_test_gap_event, 2000); + + /* Wait for the scan to find the test. Then 1 token will be + * released allowing to pass through semaphore. */ + os_sem_pend(&tx_stress_main_sem, OS_TIMEOUT_NEVER); + if (tx_stress_ctx->scan_timeout) { + break; + } + + /* Start test. */ + tx_stress_test_perform(tx_stress_use_case); + tx_stress_use_case = -1; + } + + /* Print tests results */ + com_stress_print_report(tx_stress_ctx); + + /* Task should never return */ + while (1) { + /* Delay used only to prevent watchdog to reset the device. */ + os_time_delay(os_time_ms_to_ticks32(2000)); + } +} + +void tx_stress_start_auto() +{ + /* Start task that will run all stress tests one by one. */ + os_task_init(&tx_stress_main_task, "tx_stress_main_task", + tx_stress_main_task_fn, NULL, TX_STRESS_MAIN_TASK_PRIO, + OS_WAIT_FOREVER, tx_stress_main_task_stack, + TX_STRESS_MAIN_TASK_STACK_SIZE); +} diff --git a/lib/bt/host/nimble/nimble/apps/blestress/src/tx_stress.h b/lib/bt/host/nimble/nimble/apps/blestress/src/tx_stress.h new file mode 100644 index 00000000..83ed3020 --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/blestress/src/tx_stress.h @@ -0,0 +1,49 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef _BLE_STRESS_TX_H +#define _BLE_STRESS_TX_H + +#include +#include +#include +#include + +/* BLE */ +#include "nimble/ble.h" +#include "host/ble_hs.h" + +#include "misc.h" +#include "stress.h" +#include "stress_gatt.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Scan and execute tests one by one. + */ +void tx_stress_start_auto(); + +#ifdef __cplusplus +} +#endif + +#endif //_BLE_STRESS_TX_H diff --git a/lib/bt/host/nimble/nimble/apps/blestress/syscfg.yml b/lib/bt/host/nimble/nimble/apps/blestress/syscfg.yml new file mode 100644 index 00000000..3acf280b --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/blestress/syscfg.yml @@ -0,0 +1,77 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +# Settings this app defines. +syscfg.defs: + BLE_STRESS_TEST_ROLE: + description: 0 - TX device, 1 - RX device + value: 0 + + BLE_STRESS_REPEAT: + description: Number of times to repeat each stress test use case + value: 50 + + BLE_STRESS_UUID_BASE: + description: Part of the test UUID that is specific to the current + device couple. + value: ((uint8_t[13]){0xA5, 0x4A, 0xB4, 0x44, 0xC3, 0xBF, 0xB5, + 0xF8, 0xF9, 0x42, 0x83, 0xA1, 0xDA}) + +# Settings this app overrides. +syscfg.vals: + # Change these settings: + + # Set 0 to print all debug logs, but keep in mind that plenty of + # adv packets will be lost during scan tests. + LOG_LEVEL: 1 + + # The maximum number of concurrent connections. For some devices, this + # value will need to be reduced due to the RAM capacity. + BLE_MAX_CONNECTIONS: 50 + + # Should not change these settings: + + # Enable Extended Advertising + BLE_EXT_ADV: 1 + + # Max advertising data size + BLE_EXT_ADV_MAX_SIZE: 1650 + + # Number of multi-advertising instances. Note that due + # to historical reasons total number of advertising + # instances is BLE_MULTI_ADV_INSTANCES + 1 as instance + # 0 is always available + BLE_MULTI_ADV_INSTANCES: 2 + + # Controller uses msys pool for storing advertising data and scan responses. + # Since we advertise a lot of data (~4k in total) at the same time we need + # to increase block count. + MSYS_1_BLOCK_COUNT: 50 + + # + BLE_L2CAP_COC_MAX_NUM: 2 + + # Enable 2M PHY + BLE_LL_CFG_FEAT_LE_2M_PHY: 1 + + # Enable CODED PHY + BLE_LL_CFG_FEAT_LE_CODED_PHY: 1 + + # Whether to save data to sys/config, or just keep it in RAM. + BLE_STORE_CONFIG_PERSIST: 0 diff --git a/lib/bt/host/nimble/nimble/apps/btshell/pkg.yml b/lib/bt/host/nimble/nimble/apps/btshell/pkg.yml new file mode 100644 index 00000000..abbee73c --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/btshell/pkg.yml @@ -0,0 +1,39 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +pkg.name: apps/btshell +pkg.type: app +pkg.description: Shell application exposing the nimble GAP and GATT. +pkg.author: "Apache Mynewt " +pkg.homepage: "http://mynewt.apache.org/" +pkg.keywords: + +pkg.deps: + - "@apache-mynewt-core/kernel/os" + - "@apache-mynewt-core/sys/log/full" + - "@apache-mynewt-core/sys/log/modlog" + - "@apache-mynewt-core/sys/stats/full" + - "@apache-mynewt-core/sys/console/full" + - "@apache-mynewt-core/sys/shell" + - nimble/host + - nimble/host/services/gap + - nimble/host/services/gatt + - nimble/host/store/config + - nimble/host/util + +pkg.deps.BTSHELL_ANS: + - nimble/host/services/ans diff --git a/lib/bt/host/nimble/nimble/apps/btshell/src/btshell.h b/lib/bt/host/nimble/nimble/apps/btshell/src/btshell.h new file mode 100644 index 00000000..f52d65f5 --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/btshell/src/btshell.h @@ -0,0 +1,216 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_BTSHELL_PRIV_ +#define H_BTSHELL_PRIV_ + +#include +#include "os/mynewt.h" +#include "nimble/ble.h" +#include "nimble/nimble_opt.h" +#include "modlog/modlog.h" + +#include "host/ble_gatt.h" +#include "host/ble_gap.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct ble_gap_white_entry; +struct ble_hs_adv_fields; +struct ble_gap_upd_params; +struct ble_gap_conn_params; +struct hci_adv_params; +struct ble_l2cap_sig_update_req; +struct ble_l2cap_sig_update_params; +union ble_store_value; +union ble_store_key; +struct ble_gap_adv_params; +struct ble_gap_conn_desc; +struct ble_gap_disc_params; + +struct btshell_dsc { + SLIST_ENTRY(btshell_dsc) next; + struct ble_gatt_dsc dsc; +}; +SLIST_HEAD(btshell_dsc_list, btshell_dsc); + +struct btshell_chr { + SLIST_ENTRY(btshell_chr) next; + struct ble_gatt_chr chr; + + struct btshell_dsc_list dscs; +}; +SLIST_HEAD(btshell_chr_list, btshell_chr); + +struct btshell_svc { + SLIST_ENTRY(btshell_svc) next; + struct ble_gatt_svc svc; + struct btshell_chr_list chrs; + bool discovered; +}; + +SLIST_HEAD(btshell_svc_list, btshell_svc); + +struct btshell_l2cap_coc { + SLIST_ENTRY(btshell_l2cap_coc) next; + struct ble_l2cap_chan *chan; + bool stalled; +}; + +SLIST_HEAD(btshell_l2cap_coc_list, btshell_l2cap_coc); + +struct btshell_conn { + uint16_t handle; + struct btshell_svc_list svcs; + struct btshell_l2cap_coc_list coc_list; +}; + +#define NAME_FILTER_LEN_MAX 20 + +struct btshell_scan_opts { + uint16_t limit; + uint8_t ignore_legacy:1; + uint8_t periodic_only:1; + uint8_t name_filter_len; + char name_filter[NAME_FILTER_LEN_MAX]; +}; + +extern struct btshell_conn btshell_conns[MYNEWT_VAL(BLE_MAX_CONNECTIONS)]; +extern int btshell_num_conns; + +int btshell_exchange_mtu(uint16_t conn_handle); +int btshell_disc_svcs(uint16_t conn_handle); +int btshell_disc_svc_by_uuid(uint16_t conn_handle, const ble_uuid_t *uuid); +int btshell_disc_all_chrs(uint16_t conn_handle, uint16_t start_handle, + uint16_t end_handle); +int btshell_disc_all_chrs_in_svc(uint16_t conn_handle, struct btshell_svc *svc); +int btshell_disc_chrs_by_uuid(uint16_t conn_handle, uint16_t start_handle, + uint16_t end_handle, const ble_uuid_t *uuid); +int btshell_disc_all_dscs(uint16_t conn_handle, uint16_t start_handle, + uint16_t end_handle); +int btshell_disc_full(uint16_t conn_handle); +int btshell_find_inc_svcs(uint16_t conn_handle, uint16_t start_handle, + uint16_t end_handle); +int btshell_read(uint16_t conn_handle, uint16_t attr_handle); +int btshell_read_long(uint16_t conn_handle, uint16_t attr_handle, + uint16_t offset); +int btshell_read_by_uuid(uint16_t conn_handle, uint16_t start_handle, + uint16_t end_handle, const ble_uuid_t *uuid); +int btshell_read_mult(uint16_t conn_handle, uint16_t *attr_handles, + int num_attr_handles); +int btshell_write(uint16_t conn_handle, uint16_t attr_handle, + struct os_mbuf *om); +int btshell_write_no_rsp(uint16_t conn_handle, uint16_t attr_handle, + struct os_mbuf *om); +int btshell_write_long(uint16_t conn_handle, uint16_t attr_handle, + uint16_t offset, struct os_mbuf *om); +int btshell_write_reliable(uint16_t conn_handle, + struct ble_gatt_attr *attrs, int num_attrs); +#if MYNEWT_VAL(BLE_EXT_ADV) +int btshell_ext_adv_configure(uint8_t instance, + const struct ble_gap_ext_adv_params *params, + int8_t *selected_tx_power); +int btshell_ext_adv_start(uint8_t instance, int duration, + int max_events, bool restart); +int btshell_ext_adv_stop(uint8_t instance); +#endif +int btshell_adv_start(uint8_t own_addr_type, const ble_addr_t *direct_addr, + int32_t duration_ms, + const struct ble_gap_adv_params *params, + bool restart); +int btshell_adv_stop(void); +int btshell_conn_initiate(uint8_t own_addr_type, const ble_addr_t *peer_addr, + int32_t duration_ms, + struct ble_gap_conn_params *params); +int btshell_ext_conn_initiate(uint8_t own_addr_type, + const ble_addr_t *peer_addr, + int32_t duration_ms, + struct ble_gap_conn_params *phy_1m_params, + struct ble_gap_conn_params *phy_2m_params, + struct ble_gap_conn_params *phy_coded_params); +int btshell_conn_cancel(void); +int btshell_term_conn(uint16_t conn_handle, uint8_t reason); +int btshell_wl_set(ble_addr_t *addrs, int addrs_count); +int btshell_scan(uint8_t own_addr_type, int32_t duration_ms, + const struct ble_gap_disc_params *disc_params, void *cb_args); +int btshell_ext_scan(uint8_t own_addr_type, uint16_t duration, uint16_t period, + uint8_t filter_duplicates, uint8_t filter_policy, + uint8_t limited, + const struct ble_gap_ext_disc_params *uncoded_params, + const struct ble_gap_ext_disc_params *coded_params, + void *cb_args); +int btshell_scan_cancel(void); +int btshell_update_conn(uint16_t conn_handle, + struct ble_gap_upd_params *params); +void btshell_notify(uint16_t attr_handle); +int btshell_datalen(uint16_t conn_handle, uint16_t tx_octets, + uint16_t tx_time); +int btshell_l2cap_update(uint16_t conn_handle, + struct ble_l2cap_sig_update_params *params); +int btshell_sec_start(uint16_t conn_handle); +int btshell_sec_pair(uint16_t conn_handle); +int btshell_sec_unpair(ble_addr_t *peer_addr); +int btshell_sec_restart(uint16_t conn_handle, uint8_t key_size, + uint8_t *ltk, uint16_t ediv, + uint64_t rand_val, int auth); +int btshell_tx_start(uint16_t conn_handle, uint16_t len, uint16_t rate, + uint16_t num); +void btshell_tx_stop(void); +int btshell_rssi(uint16_t conn_handle, int8_t *out_rssi); +int btshell_l2cap_create_srv(uint16_t psm, uint16_t mtu, int accept_response); +int btshell_l2cap_connect(uint16_t conn, uint16_t psm, uint16_t mtu, uint8_t num); +int btshell_l2cap_disconnect(uint16_t conn, uint16_t idx); +int btshell_l2cap_send(uint16_t conn, uint16_t idx, uint16_t bytes); +int btshell_l2cap_reconfig(uint16_t conn_handle, uint16_t mtu, + uint8_t num, uint8_t idxs[]); + +int btshell_gap_event(struct ble_gap_event *event, void *arg); +void btshell_sync_stats(uint16_t handle); + +/** GATT server. */ +#define GATT_SVR_SVC_ALERT_UUID 0x1811 +#define GATT_SVR_CHR_SUP_NEW_ALERT_CAT_UUID 0x2A47 +#define GATT_SVR_CHR_NEW_ALERT 0x2A46 +#define GATT_SVR_CHR_SUP_UNR_ALERT_CAT_UUID 0x2A48 +#define GATT_SVR_CHR_UNR_ALERT_STAT_UUID 0x2A45 +#define GATT_SVR_CHR_ALERT_NOT_CTRL_PT 0x2A44 + +void gatt_svr_register_cb(struct ble_gatt_register_ctxt *ctxt, void *arg); +int gatt_svr_init(void); +void gatt_svr_print_svcs(void); + +/** Misc. */ +void print_bytes(const uint8_t *bytes, int len); +void print_mbuf(const struct os_mbuf *om); +void print_addr(const void *addr); +void print_uuid(const ble_uuid_t *uuid); +int svc_is_empty(const struct btshell_svc *svc); +uint16_t chr_end_handle(const struct btshell_svc *svc, + const struct btshell_chr *chr); +int chr_is_empty(const struct btshell_svc *svc, const struct btshell_chr *chr); +void print_conn_desc(const struct ble_gap_conn_desc *desc); +void print_svc(struct btshell_svc *svc); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/bt/host/nimble/nimble/apps/btshell/src/cmd.c b/lib/bt/host/nimble/nimble/apps/btshell/src/cmd.c new file mode 100644 index 00000000..ddb76c82 --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/btshell/src/cmd.c @@ -0,0 +1,4688 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#include +#include +#include +#include "os/mynewt.h" +#include "bsp/bsp.h" + +#include "nimble/ble.h" +#include "nimble/nimble_opt.h" +#include "nimble/hci_common.h" +#include "host/ble_gap.h" +#include "host/ble_hs_adv.h" +#include "host/ble_sm.h" +#include "host/ble_eddystone.h" +#include "host/ble_hs_id.h" +#include "services/gatt/ble_svc_gatt.h" +#include "../src/ble_hs_priv.h" + +#include "console/console.h" +#include "shell/shell.h" + +#include "cmd.h" +#include "btshell.h" +#include "cmd_gatt.h" +#include "cmd_l2cap.h" + +#define BTSHELL_MODULE "btshell" + +int +cmd_parse_conn_start_end(uint16_t *out_conn, uint16_t *out_start, + uint16_t *out_end) +{ + int rc; + + *out_conn = parse_arg_uint16("conn", &rc); + if (rc != 0) { + return rc; + } + + *out_start = parse_arg_uint16("start", &rc); + if (rc != 0) { + return rc; + } + + *out_end = parse_arg_uint16("end", &rc); + if (rc != 0) { + return rc; + } + + return 0; +} + +static const struct kv_pair cmd_own_addr_types[] = { + { "public", BLE_OWN_ADDR_PUBLIC }, + { "random", BLE_OWN_ADDR_RANDOM }, + { "rpa_pub", BLE_OWN_ADDR_RPA_PUBLIC_DEFAULT }, + { "rpa_rnd", BLE_OWN_ADDR_RPA_RANDOM_DEFAULT }, + { NULL } +}; + +static const struct kv_pair cmd_peer_addr_types[] = { + { "public", BLE_ADDR_PUBLIC }, + { "random", BLE_ADDR_RANDOM }, + { "public_id", BLE_ADDR_PUBLIC_ID }, + { "random_id", BLE_ADDR_RANDOM_ID }, + { NULL } +}; + +static const struct kv_pair cmd_addr_type[] = { + { "public", BLE_ADDR_PUBLIC }, + { "random", BLE_ADDR_RANDOM }, + { NULL } +}; + + +static int +parse_dev_addr(const char *prefix, const struct kv_pair *addr_types, + ble_addr_t *addr) +{ + char name[32]; + int rc; + int written = 0; + + if (!prefix) { + name[0] = '\0'; + } else { + written = snprintf(name, sizeof(name), "%s", prefix); + if (written >= sizeof(name) || written < 0) { + return EINVAL; + } + } + + rc = snprintf(name + written, sizeof(name) - written, "%s", "addr"); + if (rc >= sizeof(name) - written || rc < 0) { + return EINVAL; + } + written += rc; + + rc = parse_arg_addr(name, addr); + if (rc == ENOENT) { + /* not found */ + return rc; + } else if (rc == EAGAIN) { + /* address found, but no type provided */ + rc = written; + written = snprintf(name + written, sizeof(name) - written, "%s", "_type"); + if (written >= sizeof(name) - rc || written < 0) { + return EINVAL; + } + addr->type = parse_arg_kv(name, addr_types, &rc); + if (rc == ENOENT) { + addr->type = BLE_ADDR_PUBLIC; + } else if (rc != 0) { + return rc; + } + } else if (rc != 0) { + /* error parsing address */ + return rc; + } else { + /* full address found, but let's just make sure there is no type arg */ + rc = written; + written = snprintf(name + written, sizeof(name) - written, "%s", "_type"); + if (written >= sizeof(name) - rc || written < 0) { + return EINVAL; + } + if (parse_arg_extract(name)) { + return E2BIG; + } + } + + return 0; +} + +/***************************************************************************** + * $advertise * + *****************************************************************************/ +static const struct kv_pair cmd_adv_filt_types[] = { + { "none", BLE_HCI_ADV_FILT_NONE }, + { "scan", BLE_HCI_ADV_FILT_SCAN }, + { "conn", BLE_HCI_ADV_FILT_CONN }, + { "both", BLE_HCI_ADV_FILT_BOTH }, + { NULL } +}; + +#if MYNEWT_VAL(BLE_EXT_ADV) +static struct kv_pair cmd_ext_adv_phy_opts[] = { + { "1M", 0x01 }, + { "2M", 0x02 }, + { "coded", 0x03 }, + { NULL } +}; + +static int +cmd_advertise_configure(int argc, char **argv) +{ + struct ble_gap_ext_adv_params params = {0}; + int8_t selected_tx_power; + uint8_t instance; + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + instance = parse_arg_uint8_dflt("instance", 0, &rc); + if (rc != 0 || instance >= BLE_ADV_INSTANCES) { + console_printf("invalid instance\n"); + return rc; + } + + memset(¶ms, 0, sizeof(params)); + + params.legacy_pdu = parse_arg_bool_dflt("legacy", 0, &rc); + if (rc != 0) { + console_printf("invalid 'legacy' parameter\n"); + return rc; + } + + if (params.legacy_pdu) { + params.connectable = 1; + params.scannable = 1; + } + + params.connectable = parse_arg_bool_dflt("connectable", params.connectable, &rc); + if (rc != 0) { + console_printf("invalid 'connectable' parameter\n"); + return rc; + } + + params.scannable = parse_arg_bool_dflt("scannable", params.scannable, &rc); + if (rc != 0) { + console_printf("invalid 'scannable' parameter\n"); + return rc; + } + + params.high_duty_directed = parse_arg_bool_dflt("high_duty", 0, &rc); + if (rc != 0) { + console_printf("invalid 'high_duty' parameter\n"); + return rc; + } + + params.anonymous = parse_arg_bool_dflt("anonymous", 0, &rc); + if (rc != 0) { + console_printf("invalid 'anonymous' parameter\n"); + return rc; + } + + params.include_tx_power = parse_arg_bool_dflt("include_tx_power", 0, &rc); + if (rc != 0) { + console_printf("invalid 'include_tx_power' parameter\n"); + return rc; + } + + params.scan_req_notif = parse_arg_bool_dflt("scan_req_notif", 0, &rc); + if (rc != 0) { + console_printf("invalid 'scan_req_notif' parameter\n"); + return rc; + } + + rc = parse_dev_addr("peer_", cmd_peer_addr_types, ¶ms.peer); + if (rc == 0) { + params.directed = 1; + } else if (rc == ENOENT) { + /* skip, no peer address provided */ + } else { + console_printf("invalid 'peer_addr' parameter\n"); + return rc; + } + + + params.directed = parse_arg_bool_dflt("directed", params.directed, &rc); + if (rc != 0) { + console_printf("invalid 'directed' parameter\n"); + return rc; + } + + if (params.directed && params.legacy_pdu) { + params.scannable = 0; + } + + params.own_addr_type = parse_arg_kv_dflt("own_addr_type", + cmd_own_addr_types, + BLE_OWN_ADDR_PUBLIC, &rc); + if (rc != 0) { + console_printf("invalid 'own_addr_type' parameter\n"); + return rc; + } + + params.channel_map = parse_arg_uint8_dflt("channel_map", 0, &rc); + if (rc != 0) { + console_printf("invalid 'channel_map' parameter\n"); + return rc; + } + + params.filter_policy = parse_arg_kv_dflt("filter", cmd_adv_filt_types, + BLE_HCI_ADV_FILT_NONE, &rc); + if (rc != 0) { + console_printf("invalid 'filter' parameter\n"); + return rc; + } + + params.itvl_min = parse_arg_time_dflt("interval_min", 625, 0, &rc); + if (rc != 0) { + console_printf("invalid 'interval_min' parameter\n"); + return rc; + } + + params.itvl_max = parse_arg_time_dflt("interval_max", 625, 0, &rc); + if (rc != 0) { + console_printf("invalid 'interval_max' parameter\n"); + return rc; + } + + params.tx_power = parse_arg_long_bounds_dflt("tx_power", + -127, 127, 127, &rc); + if (rc != 0) { + console_printf("invalid 'tx_power' parameter\n"); + return rc; + } + + params.primary_phy = parse_arg_kv_dflt("primary_phy", cmd_ext_adv_phy_opts, + 1, &rc); + if (rc != 0) { + console_printf("invalid 'primary_phy' parameter\n"); + return rc; + } + + params.secondary_phy = parse_arg_kv_dflt("secondary_phy", + cmd_ext_adv_phy_opts, + params.primary_phy, &rc); + if (rc != 0) { + console_printf("invalid 'secondary_phy' parameter\n"); + return rc; + } + + params.sid = parse_arg_uint8_dflt("sid", 0, &rc); + if (rc != 0) { + console_printf("invalid 'sid' parameter\n"); + return rc; + } + + rc = btshell_ext_adv_configure(instance, ¶ms, &selected_tx_power); + if (rc) { + console_printf("failed to configure advertising instance\n"); + return rc; + } + + console_printf("Instance %u configured (selected tx power: %d)\n", + instance, selected_tx_power); + + return 0; +} + +static int +cmd_advertise_set_addr(int argc, char **argv) +{ + ble_addr_t addr; + uint8_t instance; + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + instance = parse_arg_uint8_dflt("instance", 0, &rc); + if (rc != 0 || instance >= BLE_ADV_INSTANCES) { + console_printf("invalid instance\n"); + return rc; + } + + rc = parse_arg_mac("addr", addr.val); + if (rc != 0) { + console_printf("invalid 'addr' parameter\n"); + return rc; + } + + addr.type = BLE_ADDR_RANDOM; + + rc = ble_gap_ext_adv_set_addr(instance, &addr); + if (rc) { + console_printf("failed to start advertising instance\n"); + return rc; + } + + return 0; +} + +static int +cmd_advertise_start(int argc, char **argv) +{ + int max_events; + uint8_t instance; + int duration; + bool restart; + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + instance = parse_arg_uint8_dflt("instance", 0, &rc); + if (rc != 0 || instance >= BLE_ADV_INSTANCES) { + console_printf("invalid instance\n"); + return rc; + } + + duration = parse_arg_uint16_dflt("duration", 0, &rc); + if (rc != 0) { + console_printf("invalid 'duration' parameter\n"); + return rc; + } + + max_events = parse_arg_uint8_dflt("max_events", 0, &rc); + if (rc != 0) { + console_printf("invalid 'max_events' parameter\n"); + return rc; + } + + restart = parse_arg_bool_dflt("restart", 0, &rc); + if (rc != 0) { + console_printf("invalid 'restart' parameter\n"); + return rc; + } + + rc = btshell_ext_adv_start(instance, duration, max_events, restart); + if (rc) { + console_printf("failed to start advertising instance\n"); + return rc; + } + + return 0; +} + +static int +cmd_advertise_stop(int argc, char **argv) +{ + uint8_t instance; + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + instance = parse_arg_uint8_dflt("instance", 0, &rc); + if (rc != 0 || instance >= BLE_ADV_INSTANCES) { + console_printf("invalid instance\n"); + return rc; + } + + rc = btshell_ext_adv_stop(instance); + if (rc) { + console_printf("failed to stop advertising instance\n"); + return rc; + } + + return 0; +} + +static int +cmd_advertise_remove(int argc, char **argv) +{ + uint8_t instance; + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + instance = parse_arg_uint8_dflt("instance", 0, &rc); + if (rc != 0 || instance >= BLE_ADV_INSTANCES) { + console_printf("invalid instance\n"); + return rc; + } + + rc = ble_gap_ext_adv_remove(instance); + if (rc) { + console_printf("failed to remove advertising instance\n"); + return rc; + } + + return 0; +} + +#if MYNEWT_VAL(SHELL_CMD_HELP) +static const struct shell_param advertise_configure_params[] = { + {"instance", "default: 0"}, + {"connectable", "connectable advertising, usage: =[0-1], default: 0"}, + {"scannable", "scannable advertising, usage: =[0-1], default: 0"}, + {"directed", "directed advertising, usage: =[0-1], default: 0"}, + {"peer_addr_type", "usage: =[public|random|public_id|random_id], default: public"}, + {"peer_addr", "usage: =[XX:XX:XX:XX:XX:XX]"}, + {"own_addr_type", "usage: =[public|random|rpa_pub|rpa_rnd], default: public"}, + {"channel_map", "usage: =[0x00-0xff], default: 0"}, + {"filter", "usage: =[none|scan|conn|both], default: none"}, + {"interval_min", "usage: =[0-UINT32_MAX], default: 0"}, + {"interval_max", "usage: =[0-UINT32_MAX], default: 0"}, + {"tx_power", "usage: =[-127-127], default: 127"}, + {"primary_phy", "usage: =[1M|coded], default: 1M"}, + {"secondary_phy", "usage: =[1M|2M|coded], default: primary_phy"}, + {"sid", "usage: =[0-UINT8_MAX], default: 0"}, + {"high_duty", "usage: =[0-1], default: 0"}, + {"anonymous", "enable anonymous advertising, usage: =[0-1], default: 0"}, + {"legacy", "use legacy PDUs, usage: =[0-1], default: 0"}, + {"include_tx_power", "include TX power in PDU, usage: =[0-1], default: 0"}, + {"scan_req_notif", "enable Scan Request notification usage: =[0-1], default: 0"}, + {NULL, NULL} +}; + +static const struct shell_cmd_help advertise_configure_help = { + .summary = "configure new advertising instance", + .usage = NULL, + .params = advertise_configure_params, +}; + +static const struct shell_param advertise_set_addr_params[] = { + {"instance", "default: 0"}, + {"addr", "usage: =[XX:XX:XX:XX:XX:XX]"}, + {NULL, NULL} +}; + +static const struct shell_cmd_help advertise_set_addr_help = { + .summary = "set advertising instance random address", + .usage = NULL, + .params = advertise_set_addr_params, +}; + +static const struct shell_param advertise_start_params[] = { + {"instance", "default: 0"}, + {"duration", "advertising duration in 10ms units, default: 0 (forever)"}, + {"max_events", "max number of advertising events, default: 0 (no limit)"}, + {"restart", "restart advertising after disconnect, usage: =[0-1], default: 0"}, + {NULL, NULL} +}; + +static const struct shell_cmd_help advertise_start_help = { + .summary = "start advertising instance", + .usage = NULL, + .params = advertise_start_params, +}; + +static const struct shell_param advertise_stop_params[] = { + {"instance", "default: 0"}, + {NULL, NULL} +}; + +static const struct shell_cmd_help advertise_stop_help = { + .summary = "stop advertising instance", + .usage = NULL, + .params = advertise_stop_params, +}; + +static const struct shell_param advertise_remove_params[] = { + {"instance", "default: 0"}, + {NULL, NULL} +}; + +static const struct shell_cmd_help advertise_remove_help = { + .summary = "remove advertising instance", + .usage = NULL, + .params = advertise_remove_params, +}; +#endif + +#else +static const struct kv_pair cmd_adv_conn_modes[] = { + { "non", BLE_GAP_CONN_MODE_NON }, + { "und", BLE_GAP_CONN_MODE_UND }, + { "dir", BLE_GAP_CONN_MODE_DIR }, + { NULL } +}; + +static const struct kv_pair cmd_adv_disc_modes[] = { + { "non", BLE_GAP_DISC_MODE_NON }, + { "ltd", BLE_GAP_DISC_MODE_LTD }, + { "gen", BLE_GAP_DISC_MODE_GEN }, + { NULL } +}; + +static int +cmd_advertise(int argc, char **argv) +{ + struct ble_gap_adv_params params; + int32_t duration_ms; + ble_addr_t peer_addr; + ble_addr_t *peer_addr_param = &peer_addr; + uint8_t own_addr_type; + bool restart; + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + if (argc > 1 && strcmp(argv[1], "stop") == 0) { + rc = btshell_adv_stop(); + if (rc != 0) { + console_printf("advertise stop fail: %d\n", rc); + return rc; + } + + return 0; + } + + params.conn_mode = parse_arg_kv_dflt("conn", cmd_adv_conn_modes, + BLE_GAP_CONN_MODE_UND, &rc); + if (rc != 0) { + console_printf("invalid 'conn' parameter\n"); + return rc; + } + + params.disc_mode = parse_arg_kv_dflt("discov", cmd_adv_disc_modes, + BLE_GAP_DISC_MODE_GEN, &rc); + if (rc != 0) { + console_printf("invalid 'discov' parameter\n"); + return rc; + } + + rc = parse_dev_addr("peer_", cmd_peer_addr_types, &peer_addr); + if (rc == ENOENT) { + peer_addr_param = NULL; + } else if (rc != 0) { + console_printf("invalid 'peer_addr' parameter\n"); + return rc; + } + + restart = parse_arg_bool_dflt("restart", 0, &rc); + if (rc != 0) { + console_printf("invalid 'restart' parameter\n"); + return rc; + } + + own_addr_type = parse_arg_kv_dflt("own_addr_type", cmd_own_addr_types, + BLE_OWN_ADDR_PUBLIC, &rc); + if (rc != 0) { + console_printf("invalid 'own_addr_type' parameter\n"); + return rc; + } + + params.channel_map = parse_arg_uint8_dflt("channel_map", 0, &rc); + if (rc != 0) { + console_printf("invalid 'channel_map' parameter\n"); + return rc; + } + + params.filter_policy = parse_arg_kv_dflt("filter", cmd_adv_filt_types, + BLE_HCI_ADV_FILT_NONE, &rc); + if (rc != 0) { + console_printf("invalid 'filter' parameter\n"); + return rc; + } + + params.itvl_min = parse_arg_time_dflt("interval_min", 625, 0, &rc); + if (rc != 0) { + console_printf("invalid 'interval_min' parameter\n"); + return rc; + } + + params.itvl_max = parse_arg_time_dflt("interval_max", 625, 0, &rc); + if (rc != 0) { + console_printf("invalid 'interval_max' parameter\n"); + return rc; + } + + params.high_duty_cycle = parse_arg_bool_dflt("high_duty", 0, &rc); + if (rc != 0) { + console_printf("invalid 'high_duty' parameter\n"); + return rc; + } + + duration_ms = parse_arg_long_bounds_dflt("duration", 1, INT32_MAX, + BLE_HS_FOREVER, &rc); + if (rc != 0) { + console_printf("invalid 'duration' parameter\n"); + return rc; + } + + rc = btshell_adv_start(own_addr_type, peer_addr_param, duration_ms, + ¶ms, restart); + if (rc != 0) { + console_printf("advertise fail: %d\n", rc); + return rc; + } + + return 0; +} + +#if MYNEWT_VAL(SHELL_CMD_HELP) +static const struct shell_param advertise_params[] = { + {"stop", "stop advertising procedure"}, + {"conn", "connectable mode, usage: =[non|und|dir], default: und"}, + {"discov", "discoverable mode, usage: =[non|ltd|gen], default: gen"}, + {"peer_addr_type", "usage: =[public|random|public_id|random_id], default: public"}, + {"peer_addr", "usage: =[XX:XX:XX:XX:XX:XX]"}, + {"own_addr_type", "usage: =[public|random|rpa_pub|rpa_rnd], default: public"}, + {"channel_map", "usage: =[0x00-0xff], default: 0"}, + {"filter", "usage: =[none|scan|conn|both], default: none"}, + {"interval_min", "usage: =[0-UINT16_MAX], default: 0"}, + {"interval_max", "usage: =[0-UINT16_MAX], default: 0"}, + {"high_duty", "usage: =[0-1], default: 0"}, + {"duration", "usage: =[1-INT32_MAX], default: INT32_MAX"}, + {"restart", "restart advertising after disconnect, usage: =[0-1], default: 0"}, + {NULL, NULL} +}; + +static const struct shell_cmd_help advertise_help = { + .summary = "start/stop advertising with specific parameters", + .usage = NULL, + .params = advertise_params, +}; +#endif +#endif + +/***************************************************************************** + * $connect * + *****************************************************************************/ + +static struct kv_pair cmd_ext_conn_phy_opts[] = { + { "none", 0x00 }, + { "1M", 0x01 }, + { "coded", 0x02 }, + { "both", 0x03 }, + { "all", 0x04 }, + { NULL } +}; + +static int +cmd_connect(int argc, char **argv) +{ + struct ble_gap_conn_params phy_1M_params = {0}; + struct ble_gap_conn_params phy_coded_params = {0}; + struct ble_gap_conn_params phy_2M_params = {0}; + uint8_t ext; + int32_t duration_ms; + ble_addr_t peer_addr; + ble_addr_t *peer_addr_param = &peer_addr; + int own_addr_type; + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + if (argc > 1 && strcmp(argv[1], "cancel") == 0) { + rc = btshell_conn_cancel(); + if (rc != 0) { + console_printf("connection cancel fail: %d\n", rc); + return rc; + } + + return 0; + } + + ext = parse_arg_kv_dflt("extended", cmd_ext_conn_phy_opts, 0, &rc); + if (rc != 0) { + console_printf("invalid 'extended' parameter\n"); + return rc; + } + + rc = parse_dev_addr("peer_", cmd_peer_addr_types, &peer_addr); + if (rc == ENOENT) { + /* With no "peer_addr" specified we'll use white list */ + peer_addr_param = NULL; + } else if (rc != 0) { + console_printf("invalid 'peer_addr' parameter\n"); + return rc; + } + + own_addr_type = parse_arg_kv_dflt("own_addr_type", cmd_own_addr_types, + BLE_OWN_ADDR_PUBLIC, &rc); + if (rc != 0) { + console_printf("invalid 'own_addr_type' parameter\n"); + return rc; + } + + duration_ms = parse_arg_long_bounds_dflt("duration", 1, INT32_MAX, 0, &rc); + if (rc != 0) { + console_printf("invalid 'duration' parameter\n"); + return rc; + } + + phy_1M_params.scan_itvl = parse_arg_time_dflt("scan_interval", 625, 0x0010, &rc); + if (rc != 0) { + console_printf("invalid 'scan_interval' parameter\n"); + return rc; + } + + phy_1M_params.scan_window = parse_arg_time_dflt("scan_window", 625, 0x0010, &rc); + if (rc != 0) { + console_printf("invalid 'scan_window' parameter\n"); + return rc; + } + + phy_1M_params.itvl_min = parse_arg_time_dflt("interval_min", 1250, + BLE_GAP_INITIAL_CONN_ITVL_MIN, + &rc); + if (rc != 0) { + console_printf("invalid 'interval_min' parameter\n"); + return rc; + } + + phy_1M_params.itvl_max = parse_arg_time_dflt("interval_max", 1250, + BLE_GAP_INITIAL_CONN_ITVL_MAX, + &rc); + if (rc != 0) { + console_printf("invalid 'interval_max' parameter\n"); + return rc; + } + + phy_1M_params.latency = parse_arg_uint16_dflt("latency", 0, &rc); + if (rc != 0) { + console_printf("invalid 'latency' parameter\n"); + return rc; + } + + phy_1M_params.supervision_timeout = parse_arg_time_dflt("timeout", 10000, + 0x0100, &rc); + if (rc != 0) { + console_printf("invalid 'timeout' parameter\n"); + return rc; + } + + phy_1M_params.min_ce_len = parse_arg_time_dflt("min_conn_event_len", 625, + 0x0010, &rc); + if (rc != 0) { + console_printf("invalid 'min_conn_event_len' parameter\n"); + return rc; + } + + phy_1M_params.max_ce_len = parse_arg_time_dflt("max_conn_event_len", 625, + 0x0300, &rc); + if (rc != 0) { + console_printf("invalid 'max_conn_event_len' parameter\n"); + return rc; + } + + if (ext == 0x00) { + rc = btshell_conn_initiate(own_addr_type, peer_addr_param, duration_ms, + &phy_1M_params); + if (rc) { + console_printf("error connecting; rc=%d\n", rc); + } + return rc; + } + + if (ext == 0x01) { + rc = btshell_ext_conn_initiate(own_addr_type, peer_addr_param, + duration_ms, &phy_1M_params, + NULL, NULL); + if (rc) { + console_printf("error connecting; rc=%d\n", rc); + } + return rc; + } + + /* Get coded params */ + phy_coded_params.scan_itvl = parse_arg_time_dflt("coded_scan_interval", + 625, 0x0010, &rc); + if (rc != 0) { + console_printf("invalid 'coded_scan_interval' parameter\n"); + return rc; + } + + phy_coded_params.scan_window = parse_arg_time_dflt("coded_scan_window", + 625, 0x0010, &rc); + if (rc != 0) { + console_printf("invalid 'coded_scan_window' parameter\n"); + return rc; + } + + phy_coded_params.itvl_min = parse_arg_time_dflt("coded_interval_min", 1250, + BLE_GAP_INITIAL_CONN_ITVL_MIN, + &rc); + if (rc != 0) { + console_printf("invalid 'coded_interval_min' parameter\n"); + return rc; + } + + phy_coded_params.itvl_max = parse_arg_time_dflt("coded_interval_max", 1250, + BLE_GAP_INITIAL_CONN_ITVL_MAX, + &rc); + if (rc != 0) { + console_printf("invalid 'coded_interval_max' parameter\n"); + return rc; + } + + phy_coded_params.latency = + parse_arg_uint16_dflt("coded_latency", 0, &rc); + if (rc != 0) { + console_printf("invalid 'coded_latency' parameter\n"); + return rc; + } + + phy_coded_params.supervision_timeout = + parse_arg_time_dflt("coded_timeout", 10000, 0x0100, &rc); + + if (rc != 0) { + console_printf("invalid 'coded_timeout' parameter\n"); + return rc; + } + + phy_coded_params.min_ce_len = + parse_arg_time_dflt("coded_min_conn_event", 625, 0x0010, &rc); + if (rc != 0) { + console_printf("invalid 'coded_min_conn_event' parameter\n"); + return rc; + } + + phy_coded_params.max_ce_len = parse_arg_time_dflt("coded_max_conn_event", + 625, 0x0300, &rc); + if (rc != 0) { + console_printf("invalid 'coded_max_conn_event' parameter\n"); + return rc; + } + + /* Get 2M params */ + phy_2M_params.itvl_min = parse_arg_time_dflt("2M_interval_min", 1250, + BLE_GAP_INITIAL_CONN_ITVL_MIN, + &rc); + if (rc != 0) { + console_printf("invalid '2M_interval_min' parameter\n"); + return rc; + } + + phy_2M_params.itvl_max = parse_arg_time_dflt("2M_interval_max", 1250, + BLE_GAP_INITIAL_CONN_ITVL_MAX, &rc); + if (rc != 0) { + console_printf("invalid '2M_interval_max' parameter\n"); + return rc; + } + + phy_2M_params.latency = + parse_arg_uint16_dflt("2M_latency", 0, &rc); + if (rc != 0) { + console_printf("invalid '2M_latency' parameter\n"); + return rc; + } + + phy_2M_params.supervision_timeout = parse_arg_time_dflt("2M_timeout", 10000, + 0x0100, &rc); + + if (rc != 0) { + console_printf("invalid '2M_timeout' parameter\n"); + return rc; + } + + phy_2M_params.min_ce_len = parse_arg_time_dflt("2M_min_conn_event", 625, + 0x0010, &rc); + if (rc != 0) { + console_printf("invalid '2M_min_conn_event' parameter\n"); + return rc; + } + + phy_2M_params.max_ce_len = parse_arg_time_dflt("2M_max_conn_event", 625, + 0x0300, &rc); + if (rc != 0) { + console_printf("invalid '2M_max_conn_event' parameter\n"); + return rc; + } + + if (ext == 0x02) { + rc = btshell_ext_conn_initiate(own_addr_type, peer_addr_param, + duration_ms, NULL, NULL, &phy_coded_params); + return rc; + } + + if (ext == 0x03) { + rc = btshell_ext_conn_initiate(own_addr_type, peer_addr_param, + duration_ms, &phy_1M_params, NULL, + &phy_coded_params); + return rc; + } + + rc = btshell_ext_conn_initiate(own_addr_type, peer_addr_param, + duration_ms, &phy_1M_params, + &phy_2M_params, + &phy_coded_params); + return rc; +} + +#if MYNEWT_VAL(SHELL_CMD_HELP) +static const struct shell_param connect_params[] = { + {"cancel", "cancel connection procedure"}, + {"extended", "usage: =[none|1M|coded|both|all], default: none"}, + {"peer_addr_type", "usage: =[public|random|public_id|random_id], default: public"}, + {"peer_addr", "usage: =[XX:XX:XX:XX:XX:XX]"}, + {"own_addr_type", "usage: =[public|random|rpa_pub|rpa_rnd], default: public"}, + {"duration", "usage: =[1-INT32_MAX], default: 0"}, + {"scan_interval", "usage: =[0-UINT16_MAX], default: 0x0010"}, + {"scan_window", "usage: =[0-UINT16_MAX], default: 0x0010"}, + {"interval_min", "usage: =[0-UINT16_MAX], default: 30"}, + {"interval_max", "usage: =[0-UINT16_MAX], default: 50"}, + {"latency", "usage: =[UINT16], default: 0"}, + {"timeout", "usage: =[UINT16], default: 0x0100"}, + {"min_conn_event_len", "usage: =[UINT16], default: 0x0010"}, + {"max_conn_event_len", "usage: =[UINT16], default: 0x0300"}, + {"coded_scan_interval", "usage: =[0-UINT16_MAX], default: 0x0010"}, + {"coded_scan_window", "usage: =[0-UINT16_MAX], default: 0x0010"}, + {"coded_interval_min", "usage: =[0-UINT16_MAX], default: 30"}, + {"coded_interval_max", "usage: =[0-UINT16_MAX], default: 50"}, + {"coded_latency", "usage: =[UINT16], default: 0"}, + {"coded_timeout", "usage: =[UINT16], default: 0x0100"}, + {"coded_min_conn_event_len", "usage: =[UINT16], default: 0x0010"}, + {"coded_max_conn_event_len", "usage: =[UINT16], default: 0x0300"}, + {"2M_interval_min", "usage: =[0-UINT16_MAX], default: 30"}, + {"2M_interval_max", "usage: =[0-UINT16_MAX], default: 50"}, + {"2M_latency", "usage: =[UINT16], default: 0"}, + {"2M_timeout", "usage: =[UINT16], default: 0x0100"}, + {"2M_min_conn_event_len", "usage: =[UINT16], default: 0x0010"}, + {"2M_max_conn_event_len", "usage: =[UINT16], default: 0x0300"}, + {NULL, NULL} +}; + +static const struct shell_cmd_help connect_help = { + .summary = "start/stop connection procedure with specific parameters", + .usage = NULL, + .params = connect_params, +}; +#endif + +/***************************************************************************** + * $disconnect * + *****************************************************************************/ + +static int +cmd_disconnect(int argc, char **argv) +{ + uint16_t conn_handle; + uint8_t reason; + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + conn_handle = parse_arg_uint16("conn", &rc); + if (rc != 0) { + console_printf("invalid 'conn' parameter\n"); + return rc; + } + + reason = parse_arg_uint8_dflt("reason", BLE_ERR_REM_USER_CONN_TERM, &rc); + if (rc != 0) { + console_printf("invalid 'reason' parameter\n"); + return rc; + } + + rc = btshell_term_conn(conn_handle, reason); + if (rc != 0) { + console_printf("error terminating connection; rc=%d\n", rc); + return rc; + } + + return 0; +} + +static int +cmd_show_conn(int argc, char **argv) +{ + struct ble_gap_conn_desc conn_desc; + struct btshell_conn *conn; + int rc; + int i; + + for (i = 0; i < btshell_num_conns; i++) { + conn = btshell_conns + i; + + rc = ble_gap_conn_find(conn->handle, &conn_desc); + if (rc == 0) { + print_conn_desc(&conn_desc); + } + } + + return 0; +} + +static int +cmd_show_addr(int argc, char **argv) +{ + uint8_t id_addr[6]; + int rc; + + console_printf("public_id_addr="); + rc = ble_hs_id_copy_addr(BLE_ADDR_PUBLIC, id_addr, NULL); + if (rc == 0) { + print_addr(id_addr); + } else { + console_printf("none"); + } + + console_printf(" random_id_addr="); + rc = ble_hs_id_copy_addr(BLE_ADDR_RANDOM, id_addr, NULL); + if (rc == 0) { + print_addr(id_addr); + } else { + console_printf("none"); + } + console_printf("\n"); + + return 0; +} + +#if MYNEWT_VAL(SHELL_CMD_HELP) +static const struct shell_param disconnect_params[] = { + {"conn", "connection handle parameter, usage: ="}, + {"reason", "disconnection reason, usage: =[UINT8], default: 19 (remote user terminated connection)"}, + {NULL, NULL} +}; + +static const struct shell_cmd_help disconnect_help = { + .summary = "disconnect command", + .usage = NULL, + .params = disconnect_params, +}; +#endif + +/***************************************************************************** + * $set-scan-opts * + *****************************************************************************/ + +static struct btshell_scan_opts g_scan_opts = { + .limit = UINT16_MAX, + .ignore_legacy = 0, + .periodic_only = 0, + .name_filter_len = 0, +}; + +static int +cmd_set_scan_opts(int argc, char **argv) +{ + char *name_filter; + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + g_scan_opts.limit = parse_arg_uint16_dflt("decode_limit", UINT16_MAX, &rc); + if (rc != 0) { + console_printf("invalid 'decode_limit' parameter\n"); + return rc; + } + + g_scan_opts.ignore_legacy = parse_arg_bool_dflt("ignore_legacy", 0, &rc); + if (rc != 0) { + console_printf("invalid 'ignore_legacy' parameter\n"); + return rc; + } + + g_scan_opts.periodic_only = parse_arg_bool_dflt("periodic_only", 0, &rc); + if (rc != 0) { + console_printf("invalid 'periodic_only' parameter\n"); + return rc; + } + + name_filter = parse_arg_extract("name_filter"); + if (name_filter) { + strncpy(g_scan_opts.name_filter, name_filter, NAME_FILTER_LEN_MAX); + g_scan_opts.name_filter[NAME_FILTER_LEN_MAX - 1] = '\0'; + } else { + g_scan_opts.name_filter[0] = '\0'; + } + + g_scan_opts.name_filter_len = strlen(g_scan_opts.name_filter); + + return rc; +} + +#if MYNEWT_VAL(SHELL_CMD_HELP) +static const struct shell_param set_scan_opts_params[] = { + {"decode_limit", "usage: =[0-UINT16_MAX], default: UINT16_MAX"}, + {"ignore_legacy", "usage: =[0-1], default: 0"}, + {"periodic_only", "usage: =[0-1], default: 0"}, + {"name_filter", "usage: =name, default: {none}"}, + {NULL, NULL} +}; + +static const struct shell_cmd_help set_scan_opts_help = { + .summary = "set scan options", + .usage = NULL, + .params = set_scan_opts_params, +}; +#endif + +/***************************************************************************** + * $scan * + *****************************************************************************/ + +static const struct kv_pair cmd_scan_filt_policies[] = { + { "no_wl", BLE_HCI_SCAN_FILT_NO_WL }, + { "use_wl", BLE_HCI_SCAN_FILT_USE_WL }, + { "no_wl_inita", BLE_HCI_SCAN_FILT_NO_WL_INITA }, + { "use_wl_inita", BLE_HCI_SCAN_FILT_USE_WL_INITA }, + { NULL } +}; + +static struct kv_pair cmd_scan_ext_types[] = { + { "none", 0x00 }, + { "1M", 0x01 }, + { "coded", 0x02 }, + { "both", 0x03 }, + { NULL } +}; + +static struct btshell_scan_opts g_scan_opts; + +static int +cmd_scan(int argc, char **argv) +{ + struct ble_gap_disc_params params = {0}; + struct ble_gap_ext_disc_params uncoded = {0}; + struct ble_gap_ext_disc_params coded = {0}; + uint8_t extended; + int32_t duration_ms; + uint8_t own_addr_type; + uint16_t duration; + uint16_t period; + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + if (argc > 1 && strcmp(argv[1], "cancel") == 0) { + rc = btshell_scan_cancel(); + if (rc != 0) { + console_printf("scan cancel fail: %d\n", rc); + return rc; + } + return 0; + } + + extended = parse_arg_kv_dflt("extended", cmd_scan_ext_types, 0, &rc); + if (rc != 0) { + console_printf("invalid 'extended' parameter\n"); + return rc; + } + + duration_ms = parse_arg_time_dflt("duration", 10000, BLE_HS_FOREVER, &rc); + if (rc != 0) { + console_printf("invalid 'duration' parameter\n"); + return rc; + } + + params.limited = parse_arg_bool_dflt("limited", 0, &rc); + if (rc != 0) { + console_printf("invalid 'limited' parameter\n"); + return rc; + } + + params.passive = parse_arg_bool_dflt("passive", 0, &rc); + if (rc != 0) { + console_printf("invalid 'passive' parameter\n"); + return rc; + } + + params.itvl = parse_arg_time_dflt("interval", 625, 0, &rc); + if (rc != 0) { + console_printf("invalid 'interval' parameter\n"); + return rc; + } + + params.window = parse_arg_time_dflt("window", 625, 0, &rc); + if (rc != 0) { + console_printf("invalid 'window' parameter\n"); + return rc; + } + + params.filter_policy = parse_arg_kv_dflt("filter", cmd_scan_filt_policies, + BLE_HCI_SCAN_FILT_NO_WL, &rc); + if (rc != 0) { + console_printf("invalid 'filter' parameter\n"); + return rc; + } + + params.filter_duplicates = parse_arg_bool_dflt("nodups", 0, &rc); + if (rc != 0) { + console_printf("invalid 'nodups' parameter\n"); + return rc; + } + + own_addr_type = parse_arg_kv_dflt("own_addr_type", cmd_own_addr_types, + BLE_OWN_ADDR_PUBLIC, &rc); + if (rc != 0) { + console_printf("invalid 'own_addr_type' parameter\n"); + return rc; + } + + if (extended == 0) { + rc = btshell_scan(own_addr_type, duration_ms, ¶ms, &g_scan_opts); + if (rc != 0) { + console_printf("error scanning; rc=%d\n", rc); + return rc; + } + + return 0; + } + + /* Copy above parameters to uncoded params */ + uncoded.passive = params.passive; + uncoded.itvl = params.itvl; + uncoded.window = params.window; + + duration = parse_arg_time_dflt("extended_duration", 10000, 0, &rc); + if (rc != 0) { + console_printf("invalid 'extended_duration' parameter\n"); + return rc; + } + + period = parse_arg_time_dflt("extended_period", 1280000, 0, &rc); + if (rc != 0) { + console_printf("invalid 'extended_period' parameter\n"); + return rc; + } + + coded.itvl = parse_arg_time_dflt("longrange_interval", 625, 0, &rc); + if (rc != 0) { + console_printf("invalid 'longrange_interval' parameter\n"); + return rc; + } + + coded.window = parse_arg_time_dflt("longrange_window", 625, 0, &rc); + if (rc != 0) { + console_printf("invalid 'longrange_window' parameter\n"); + return rc; + } + + coded.passive = parse_arg_uint16_dflt("longrange_passive", 0, &rc); + if (rc != 0) { + console_printf("invalid 'longrange_passive' parameter\n"); + return rc; + } + + switch (extended) { + case 0x01: + rc = btshell_ext_scan(own_addr_type, duration, period, + params.filter_duplicates, params.filter_policy, + params.limited, &uncoded, NULL, + &g_scan_opts); + break; + case 0x02: + rc = btshell_ext_scan(own_addr_type, duration, period, + params.filter_duplicates, params.filter_policy, + params.limited, NULL, &coded, + &g_scan_opts); + break; + case 0x03: + rc = btshell_ext_scan(own_addr_type, duration, period, + params.filter_duplicates, params.filter_policy, + params.limited, &uncoded, &coded, + &g_scan_opts); + break; + default: + assert(0); + break; + } + + if (rc != 0) { + console_printf("error scanning; rc=%d\n", rc); + } + + return rc; +} + +#if MYNEWT_VAL(SHELL_CMD_HELP) +static const struct shell_param scan_params[] = { + {"cancel", "cancel scan procedure"}, + {"extended", "usage: =[none|1M|coded|both], default: none"}, + {"duration", "usage: =[1-INT32_MAX], default: INT32_MAX"}, + {"limited", "usage: =[0-1], default: 0"}, + {"passive", "usage: =[0-1], default: 0"}, + {"interval", "usage: =[0-UINT16_MAX], default: 0"}, + {"window", "usage: =[0-UINT16_MAX], default: 0"}, + {"filter", "usage: =[no_wl|use_wl|no_wl_inita|use_wl_inita], default: no_wl"}, + {"nodups", "usage: =[0-1], default: 0"}, + {"own_addr_type", "usage: =[public|random|rpa_pub|rpa_rnd], default: public"}, + {"extended_duration", "usage: =[0-UINT16_MAX], default: 0"}, + {"extended_period", "usage: =[0-UINT16_MAX], default: 0"}, + {"longrange_interval", "usage: =[0-UINT16_MAX], default: 0"}, + {"longrange_window", "usage: =[0-UINT16_MAX], default: 0"}, + {"longrange_passive", "usage: =[0-1], default: 0"}, + {NULL, NULL} +}; + +static const struct shell_cmd_help scan_help = { + .summary = "start/stop scan procedure with specific parameters", + .usage = NULL, + .params = scan_params, +}; +#endif + +/***************************************************************************** + * $set * + *****************************************************************************/ + +static int +cmd_set_addr(void) +{ + ble_addr_t addr; + int rc; + + rc = parse_dev_addr(NULL, cmd_addr_type, &addr); + if (rc != 0) { + console_printf("invalid 'addr' parameter\n"); + return rc; + } + + switch (addr.type) { +#if MYNEWT_VAL(BLE_CONTROLLER) + case BLE_ADDR_PUBLIC: + /* We shouldn't be writing to the controller's address (g_dev_addr). + * There is no standard way to set the local public address, so this is + * our only option at the moment. + */ + memcpy(g_dev_addr, addr.val, 6); + ble_hs_id_set_pub(g_dev_addr); + break; +#endif + + case BLE_ADDR_RANDOM: + rc = ble_hs_id_set_rnd(addr.val); + if (rc != 0) { + return rc; + } + break; + + default: + return BLE_HS_EUNKNOWN; + } + + return 0; +} + +static int +cmd_set(int argc, char **argv) +{ + uint16_t mtu; + uint8_t irk[16]; + int good = 0; + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + rc = parse_arg_find_idx("addr"); + if (rc != -1) { + rc = cmd_set_addr(); + if (rc != 0) { + return rc; + } + good = 1; + } + + mtu = parse_arg_uint16("mtu", &rc); + if (rc == 0) { + rc = ble_att_set_preferred_mtu(mtu); + if (rc == 0) { + good = 1; + } + } else if (rc != ENOENT) { + console_printf("invalid 'mtu' parameter\n"); + return rc; + } + + rc = parse_arg_byte_stream_exact_length("irk", irk, 16); + if (rc == 0) { + good = 1; + ble_hs_pvcy_set_our_irk(irk); + } else if (rc != ENOENT) { + console_printf("invalid 'irk' parameter\n"); + return rc; + } + + if (!good) { + console_printf("Error: no valid settings specified\n"); + return -1; + } + + return 0; +} + +#if MYNEWT_VAL(SHELL_CMD_HELP) +static const struct shell_param set_params[] = { + {"addr", "set device address, usage: =[XX:XX:XX:XX:XX:XX]"}, + {"addr_type", "set device address type, usage: =[public|random], default: public"}, + {"mtu", "Maximum Transimssion Unit, usage: =[0-UINT16_MAX]"}, + {"irk", "Identity Resolving Key, usage: =[XX:XX...], len=16 octets"}, + {NULL, NULL} +}; + +static const struct shell_cmd_help set_help = { + .summary = "set device parameters", + .usage = NULL, + .params = set_params, +}; +#endif + +/***************************************************************************** + * $set-adv-data * + *****************************************************************************/ + +#define CMD_ADV_DATA_MAX_UUIDS16 8 +#define CMD_ADV_DATA_MAX_UUIDS32 8 +#define CMD_ADV_DATA_MAX_UUIDS128 2 +#define CMD_ADV_DATA_MAX_PUBLIC_TGT_ADDRS 8 +#define CMD_ADV_DATA_SVC_DATA_UUID16_MAX_LEN BLE_HS_ADV_MAX_FIELD_SZ +#define CMD_ADV_DATA_SVC_DATA_UUID32_MAX_LEN BLE_HS_ADV_MAX_FIELD_SZ +#define CMD_ADV_DATA_SVC_DATA_UUID128_MAX_LEN BLE_HS_ADV_MAX_FIELD_SZ +#define CMD_ADV_DATA_URI_MAX_LEN BLE_HS_ADV_MAX_FIELD_SZ +#define CMD_ADV_DATA_MFG_DATA_MAX_LEN BLE_HS_ADV_MAX_FIELD_SZ + +#if MYNEWT_VAL(BLE_EXT_ADV) +static void +update_pattern(uint8_t *buf, int counter) +{ + int i; + + for (i = 0; i < 10; i += 2) { + counter += 2; + buf[i] = (counter / 1000) << 4 | (counter / 100 % 10); + buf[i + 1] = (counter / 10 % 10) << 4 | (counter % 10); + } +} +#endif + +static int +cmd_set_adv_data_or_scan_rsp(int argc, char **argv, bool scan_rsp, + bool periodic) +{ + static bssnz_t ble_uuid16_t uuids16[CMD_ADV_DATA_MAX_UUIDS16]; + static bssnz_t ble_uuid32_t uuids32[CMD_ADV_DATA_MAX_UUIDS32]; + static bssnz_t ble_uuid128_t uuids128[CMD_ADV_DATA_MAX_UUIDS128]; + static bssnz_t uint8_t + public_tgt_addrs[CMD_ADV_DATA_MAX_PUBLIC_TGT_ADDRS] + [BLE_HS_ADV_PUBLIC_TGT_ADDR_ENTRY_LEN]; + static bssnz_t uint8_t slave_itvl_range[BLE_HS_ADV_SLAVE_ITVL_RANGE_LEN]; + static bssnz_t uint8_t + svc_data_uuid16[CMD_ADV_DATA_SVC_DATA_UUID16_MAX_LEN]; + static bssnz_t uint8_t + svc_data_uuid32[CMD_ADV_DATA_SVC_DATA_UUID32_MAX_LEN]; + static bssnz_t uint8_t + svc_data_uuid128[CMD_ADV_DATA_SVC_DATA_UUID128_MAX_LEN]; + static bssnz_t uint8_t uri[CMD_ADV_DATA_URI_MAX_LEN]; + static bssnz_t uint8_t mfg_data[CMD_ADV_DATA_MFG_DATA_MAX_LEN]; + struct ble_hs_adv_fields adv_fields; + uint32_t uuid32; + uint16_t uuid16; + uint8_t uuid128[16]; + uint8_t public_tgt_addr[BLE_HS_ADV_PUBLIC_TGT_ADDR_ENTRY_LEN]; + uint8_t eddystone_url_body_len; + uint8_t eddystone_url_suffix; + uint8_t eddystone_url_scheme; + int8_t eddystone_measured_power = 0; + char eddystone_url_body[BLE_EDDYSTONE_URL_MAX_LEN]; + char *eddystone_url_full; + int svc_data_uuid16_len; + int svc_data_uuid32_len; + int svc_data_uuid128_len; + int uri_len; + int mfg_data_len; + int tmp; + int rc; +#if MYNEWT_VAL(BLE_EXT_ADV) + uint8_t instance; + uint8_t extra_data[10]; + uint16_t counter; + uint16_t extra_data_len; + struct os_mbuf *adv_data; +#endif + + /* cannot set scan rsp for periodic */ + if (scan_rsp && periodic) { + return -1; + } + + memset(&adv_fields, 0, sizeof adv_fields); + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + +#if MYNEWT_VAL(BLE_EXT_ADV) + instance = parse_arg_uint8_dflt("instance", 0, &rc); + if (rc != 0 || instance >= BLE_ADV_INSTANCES) { + console_printf("invalid instance\n"); + return rc; + } +#endif + + tmp = parse_arg_uint8("flags", &rc); + if (rc == 0) { + adv_fields.flags = tmp; + } else if (rc != ENOENT) { + console_printf("invalid 'flags' parameter\n"); + return rc; + } + + while (1) { + uuid16 = parse_arg_uint16("uuid16", &rc); + if (rc == 0) { + if (adv_fields.num_uuids16 >= CMD_ADV_DATA_MAX_UUIDS16) { + console_printf("invalid 'uuid16' parameter\n"); + return EINVAL; + } + uuids16[adv_fields.num_uuids16] = (ble_uuid16_t) BLE_UUID16_INIT(uuid16); + adv_fields.num_uuids16++; + } else if (rc == ENOENT) { + break; + } else { + console_printf("invalid 'uuid16' parameter\n"); + return rc; + } + } + if (adv_fields.num_uuids16 > 0) { + adv_fields.uuids16 = uuids16; + } + + tmp = parse_arg_bool_dflt("uuids16_is_complete", 0, &rc); + if (rc != 0) { + console_printf("invalid 'uuids16_is_complete' parameter\n"); + return rc; + } + + while (1) { + uuid32 = parse_arg_uint32("uuid32", &rc); + if (rc == 0) { + if (adv_fields.num_uuids32 >= CMD_ADV_DATA_MAX_UUIDS32) { + console_printf("invalid 'uuid32' parameter\n"); + return EINVAL; + } + uuids32[adv_fields.num_uuids32] = (ble_uuid32_t) BLE_UUID32_INIT(uuid32); + adv_fields.num_uuids32++; + } else if (rc == ENOENT) { + break; + } else { + console_printf("invalid 'uuid32' parameter\n"); + return rc; + } + } + if (adv_fields.num_uuids32 > 0) { + adv_fields.uuids32 = uuids32; + } + + tmp = parse_arg_bool_dflt("uuids32_is_complete", 0, &rc); + if (rc != 0) { + console_printf("invalid 'uuids32_is_complete' parameter\n"); + return rc; + } + + while (1) { + rc = parse_arg_byte_stream_exact_length("uuid128", uuid128, 16); + if (rc == 0) { + if (adv_fields.num_uuids128 >= CMD_ADV_DATA_MAX_UUIDS128) { + console_printf("invalid 'uuid128' parameter\n"); + return EINVAL; + } + ble_uuid_init_from_buf((ble_uuid_any_t *) &uuids128[adv_fields.num_uuids128], + uuid128, 16); + adv_fields.num_uuids128++; + } else if (rc == ENOENT) { + break; + } else { + console_printf("invalid 'uuid128' parameter\n"); + return rc; + } + } + if (adv_fields.num_uuids128 > 0) { + adv_fields.uuids128 = uuids128; + } + + tmp = parse_arg_bool_dflt("uuids128_is_complete", 0, &rc); + if (rc != 0) { + console_printf("invalid 'uuids128_is_complete' parameter\n"); + return rc; + } + + adv_fields.name = (uint8_t *)parse_arg_extract("name"); + if (adv_fields.name != NULL) { + adv_fields.name_len = strlen((char *)adv_fields.name); + } + + tmp = parse_arg_long_bounds("tx_power_level", INT8_MIN, INT8_MAX, &rc); + if (rc == 0) { + adv_fields.tx_pwr_lvl = tmp; + adv_fields.tx_pwr_lvl_is_present = 1; + } else if (rc != ENOENT) { + console_printf("invalid 'tx_power_level' parameter\n"); + return rc; + } + + rc = parse_arg_byte_stream_exact_length("slave_interval_range", + slave_itvl_range, + BLE_HS_ADV_SLAVE_ITVL_RANGE_LEN); + if (rc == 0) { + adv_fields.slave_itvl_range = slave_itvl_range; + } else if (rc != ENOENT) { + console_printf("invalid 'slave_interval_range' parameter\n"); + return rc; + } + + rc = parse_arg_byte_stream("service_data_uuid16", + CMD_ADV_DATA_SVC_DATA_UUID16_MAX_LEN, + svc_data_uuid16, &svc_data_uuid16_len); + if (rc == 0) { + adv_fields.svc_data_uuid16 = svc_data_uuid16; + adv_fields.svc_data_uuid16_len = svc_data_uuid16_len; + } else if (rc != ENOENT) { + console_printf("invalid 'service_data_uuid16' parameter\n"); + return rc; + } + + while (1) { + rc = parse_arg_byte_stream_exact_length( + "public_target_address", public_tgt_addr, + BLE_HS_ADV_PUBLIC_TGT_ADDR_ENTRY_LEN); + if (rc == 0) { + if (adv_fields.num_public_tgt_addrs >= + CMD_ADV_DATA_MAX_PUBLIC_TGT_ADDRS) { + + console_printf("invalid 'public_target_address' parameter\n"); + return EINVAL; + } + memcpy(public_tgt_addrs[adv_fields.num_public_tgt_addrs], + public_tgt_addr, BLE_HS_ADV_PUBLIC_TGT_ADDR_ENTRY_LEN); + adv_fields.num_public_tgt_addrs++; + } else if (rc == ENOENT) { + break; + } else { + console_printf("invalid 'public_target_address' parameter\n"); + return rc; + } + } + if (adv_fields.num_public_tgt_addrs > 0) { + adv_fields.public_tgt_addr = (void *)public_tgt_addrs; + } + + adv_fields.appearance = parse_arg_uint16("appearance", &rc); + if (rc == 0) { + adv_fields.appearance_is_present = 1; + } else if (rc != ENOENT) { + console_printf("invalid 'appearance' parameter\n"); + return rc; + } + + adv_fields.adv_itvl = parse_arg_uint16("advertising_interval", &rc); + if (rc == 0) { + adv_fields.adv_itvl_is_present = 1; + } else if (rc != ENOENT) { + console_printf("invalid 'advertising_interval' parameter\n"); + return rc; + } + + rc = parse_arg_byte_stream("service_data_uuid32", + CMD_ADV_DATA_SVC_DATA_UUID32_MAX_LEN, + svc_data_uuid32, &svc_data_uuid32_len); + if (rc == 0) { + adv_fields.svc_data_uuid32 = svc_data_uuid32; + adv_fields.svc_data_uuid32_len = svc_data_uuid32_len; + } else if (rc != ENOENT) { + console_printf("invalid 'service_data_uuid32' parameter\n"); + return rc; + } + + rc = parse_arg_byte_stream("service_data_uuid128", + CMD_ADV_DATA_SVC_DATA_UUID128_MAX_LEN, + svc_data_uuid128, &svc_data_uuid128_len); + if (rc == 0) { + adv_fields.svc_data_uuid128 = svc_data_uuid128; + adv_fields.svc_data_uuid128_len = svc_data_uuid128_len; + } else if (rc != ENOENT) { + console_printf("invalid 'service_data_uuid128' parameter\n"); + return rc; + } + + rc = parse_arg_byte_stream("uri", CMD_ADV_DATA_URI_MAX_LEN, uri, &uri_len); + if (rc == 0) { + adv_fields.uri = uri; + adv_fields.uri_len = uri_len; + } else if (rc != ENOENT) { + console_printf("invalid 'uri' parameter\n"); + return rc; + } + + rc = parse_arg_byte_stream("mfg_data", CMD_ADV_DATA_MFG_DATA_MAX_LEN, + mfg_data, &mfg_data_len); + if (rc == 0) { + adv_fields.mfg_data = mfg_data; + adv_fields.mfg_data_len = mfg_data_len; + } else if (rc != ENOENT) { + console_printf("invalid 'mfg_data' parameter\n"); + return rc; + } + + tmp = parse_arg_long_bounds("eddystone_measured_power", -100, 20, &rc); + if (rc == 0) { + eddystone_measured_power = tmp; + } else if (rc != ENOENT) { + console_printf("invalid 'eddystone_measured_power' parameter\n"); + return rc; + } + + eddystone_url_full = parse_arg_extract("eddystone_url"); + if (eddystone_url_full != NULL) { + rc = parse_eddystone_url(eddystone_url_full, &eddystone_url_scheme, + eddystone_url_body, + &eddystone_url_body_len, + &eddystone_url_suffix); + if (rc != 0) { + goto done; + } + + rc = ble_eddystone_set_adv_data_url(&adv_fields, eddystone_url_scheme, + eddystone_url_body, + eddystone_url_body_len, + eddystone_url_suffix, + eddystone_measured_power); + } else { +#if MYNEWT_VAL(BLE_EXT_ADV) + /* Default to legacy PDUs size, mbuf chain will be increased if needed + */ + adv_data = os_msys_get_pkthdr(BLE_HCI_MAX_ADV_DATA_LEN, 0); + if (!adv_data) { + rc = ENOMEM; + goto done; + } + + rc = ble_hs_adv_set_fields_mbuf(&adv_fields, adv_data); + if (rc) { + os_mbuf_free_chain(adv_data); + goto done; + } + + /* Append some extra data, if requested */ + extra_data_len = parse_arg_uint16("extra_data_len", &rc); + if (rc == 0) { + counter = 0; + extra_data_len = min(extra_data_len, 1650); + while (counter < extra_data_len) { + update_pattern(extra_data, counter); + + rc = os_mbuf_append(adv_data, extra_data, + min(extra_data_len - counter, 10)); + if (rc) { + os_mbuf_free_chain(adv_data); + goto done; + } + + counter += 10; + } + } + + if (scan_rsp) { + rc = ble_gap_ext_adv_rsp_set_data(instance, adv_data); +#if MYNEWT_VAL(BLE_PERIODIC_ADV) + } else if (periodic) { + rc = ble_gap_periodic_adv_set_data(instance, adv_data); +#endif + } else { + rc = ble_gap_ext_adv_set_data(instance, adv_data); + } +#else + if (scan_rsp) { + rc = ble_gap_adv_rsp_set_fields(&adv_fields); + } else { + rc = ble_gap_adv_set_fields(&adv_fields); + } +#endif + } +done: + if (rc != 0) { + console_printf("error setting advertisement data; rc=%d\n", rc); + return rc; + } + + return 0; +} + +static int +cmd_set_adv_data(int argc, char **argv) +{ + return cmd_set_adv_data_or_scan_rsp(argc, argv, false, false); +} + +static int +cmd_set_scan_rsp(int argc, char **argv) +{ + return cmd_set_adv_data_or_scan_rsp(argc, argv, true, false); +} + +#if MYNEWT_VAL(SHELL_CMD_HELP) +static const struct shell_param set_adv_data_params[] = { + {"instance", "default: 0"}, + {"flags", "usage: =[0-UINT8_MAX]"}, + {"uuid16", "usage: =[UINT16]"}, + {"uuid16_is_complete", "usage: =[0-1], default=0"}, + {"uuid32", "usage: =[UINT32]"}, + {"uuid32_is_complete", "usage: =[0-1], default=0"}, + {"uuid128", "usage: =[XX:XX...], len=16 octets"}, + {"uuid128_is_complete", "usage: =[0-1], default=0"}, + {"tx_power_level", "usage: =[INT8_MIN-INT8_MAX]"}, + {"slave_interval_range", "usage: =[XX:XX:XX:XX]"}, + {"public_target_address", "usage: =[XX:XX:XX:XX:XX:XX]"}, + {"appearance", "usage: =[UINT16]"}, + {"name", "usage: =[string]"}, + {"advertising_interval", "usage: =[UINT16]"}, + {"service_data_uuid16", "usage: =[XX:XX...]"}, + {"service_data_uuid32", "usage: =[XX:XX...]"}, + {"service_data_uuid128", "usage: =[XX:XX...]"}, + {"uri", "usage: =[XX:XX...]"}, + {"mfg_data", "usage: =[XX:XX...]"}, + {"measured_power", "usage: =[-100-20]"}, + {"eddystone_url", "usage: =[string]"}, +#if MYNEWT_VAL(BLE_EXT_ADV) + {"extra_data_len", "usage: =[UINT16]"}, +#endif + {NULL, NULL} +}; + +static const struct shell_cmd_help set_adv_data_help = { + .summary = "set advertising data", + .usage = NULL, + .params = set_adv_data_params, +}; + +static const struct shell_cmd_help set_scan_rsp_help = { + .summary = "set scan response", + .usage = NULL, + .params = set_adv_data_params, +}; +#endif + +/***************************************************************************** + * $set-priv-mode * + *****************************************************************************/ + +static int +cmd_set_priv_mode(int argc, char **argv) +{ + ble_addr_t addr; + uint8_t priv_mode; + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + rc = parse_dev_addr(NULL, cmd_addr_type, &addr); + if (rc != 0) { + console_printf("invalid 'addr' parameter\n"); + return rc; + } + + priv_mode = parse_arg_uint8("mode", &rc); + if (rc != 0) { + console_printf("missing mode\n"); + return rc; + } + + return ble_gap_set_priv_mode(&addr, priv_mode); +} + +#if MYNEWT_VAL(SHELL_CMD_HELP) +static const struct shell_param set_priv_mode_params[] = { + {"addr", "set priv mode for device address, usage: =[XX:XX:XX:XX:XX:XX]"}, + {"addr_type", "set priv mode for device address type, usage: =[public|random], default: public"}, + {"mode", "set priv mode, usage: =[0-UINT8_MAX]"}, + {NULL, NULL} +}; + +static const struct shell_cmd_help set_priv_mode_help = { + .summary = "set priv mode", + .usage = NULL, + .params = set_priv_mode_params, +}; +#endif + +/***************************************************************************** + * $white-list * + *****************************************************************************/ + +#define CMD_WL_MAX_SZ 8 + +static int +cmd_white_list(int argc, char **argv) +{ + static ble_addr_t addrs[CMD_WL_MAX_SZ]; + int addrs_cnt; + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + addrs_cnt = 0; + while (1) { + if (addrs_cnt >= CMD_WL_MAX_SZ) { + return EINVAL; + } + + rc = parse_dev_addr(NULL, cmd_addr_type, &addrs[addrs_cnt]); + if (rc == ENOENT) { + break; + } else if (rc != 0) { + console_printf("invalid 'addr' parameter #%d\n", addrs_cnt + 1); + return rc; + } + + addrs_cnt++; + } + + if (addrs_cnt == 0) { + return EINVAL; + } + + btshell_wl_set(addrs, addrs_cnt); + + return 0; +} + +#if MYNEWT_VAL(SHELL_CMD_HELP) +static const struct shell_param white_list_params[] = { + {"addr", "white-list device addresses, usage: =[XX:XX:XX:XX:XX:XX]"}, + {"addr_type", "white-list address types, usage: =[public|random]"}, + {NULL, NULL} +}; + +static const struct shell_cmd_help white_list_help = { + .summary = "set white-list addresses", + .usage = NULL, + .params = white_list_params, +}; +#endif + +/***************************************************************************** + * $conn-rssi * + *****************************************************************************/ + +static int +cmd_conn_rssi(int argc, char **argv) +{ + uint16_t conn_handle; + int8_t rssi; + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + conn_handle = parse_arg_uint16("conn", &rc); + if (rc != 0) { + console_printf("invalid 'conn' parameter\n"); + return rc; + } + + rc = btshell_rssi(conn_handle, &rssi); + if (rc != 0) { + console_printf("error reading rssi; rc=%d\n", rc); + return rc; + } + + console_printf("conn=%d rssi=%d\n", conn_handle, rssi); + + return 0; +} + +#if MYNEWT_VAL(SHELL_CMD_HELP) +static const struct shell_param conn_rssi_params[] = { + {"conn", "connection handle parameter, usage: ="}, + {NULL, NULL} +}; + +static const struct shell_cmd_help conn_rssi_help = { + .summary = "check connection rssi", + .usage = NULL, + .params = conn_rssi_params, +}; +#endif + +/***************************************************************************** + * $conn-update-params * + *****************************************************************************/ + +static int +cmd_conn_update_params(int argc, char **argv) +{ + struct ble_gap_upd_params params; + uint16_t conn_handle; + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + conn_handle = parse_arg_uint16("conn", &rc); + if (rc != 0) { + console_printf("invalid 'conn' parameter\n"); + return rc; + } + + params.itvl_min = parse_arg_time_dflt("interval_min", 1250, + BLE_GAP_INITIAL_CONN_ITVL_MIN, + &rc); + if (rc != 0) { + console_printf("invalid 'interval_min' parameter\n"); + return rc; + } + + params.itvl_max = parse_arg_time_dflt("interval_max", 1250, + BLE_GAP_INITIAL_CONN_ITVL_MAX, + &rc); + if (rc != 0) { + console_printf("invalid 'interval_max' parameter\n"); + return rc; + } + + params.latency = parse_arg_uint16_dflt("latency", 0, &rc); + if (rc != 0) { + console_printf("invalid 'latency' parameter\n"); + return rc; + } + + params.supervision_timeout = parse_arg_time_dflt("timeout", 10000, 0x0100, + &rc); + if (rc != 0) { + console_printf("invalid 'timeout' parameter\n"); + return rc; + } + + params.min_ce_len = parse_arg_time_dflt("min_conn_event_len", 625, + 0x0010, &rc); + if (rc != 0) { + console_printf("invalid 'min_conn_event_len' parameter\n"); + return rc; + } + + params.max_ce_len = parse_arg_time_dflt("max_conn_event_len", 625, + 0x0300, &rc); + if (rc != 0) { + console_printf("invalid 'max_conn_event_len' parameter\n"); + return rc; + } + + rc = btshell_update_conn(conn_handle, ¶ms); + if (rc != 0) { + console_printf("error updating connection; rc=%d\n", rc); + return rc; + } + + return 0; +} + +#if MYNEWT_VAL(SHELL_CMD_HELP) +static const struct shell_param conn_update_params_params[] = { + {"conn", "conn_update_paramsion handle, usage: ="}, + {"interval_min", "usage: =[0-UINT16_MAX], default: 30"}, + {"interval_max", "usage: =[0-UINT16_MAX], default: 50"}, + {"latency", "usage: =[UINT16], default: 0"}, + {"timeout", "usage: =[UINT16], default: 0x0100"}, + {"min_conn_event_len", "usage: =[UINT16], default: 0x0010"}, + {"max_conn_event_len", "usage: =[UINT16], default: 0x0300"}, + {NULL, NULL} +}; + +static const struct shell_cmd_help conn_update_params_help = { + .summary = "update connection parameters", + .usage = "conn_update_params usage", + .params = conn_update_params_params, +}; +#endif + +/***************************************************************************** + * $conn-datalen * + *****************************************************************************/ + +static int +cmd_conn_datalen(int argc, char **argv) +{ + uint16_t conn_handle; + uint16_t tx_octets; + uint16_t tx_time; + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + conn_handle = parse_arg_uint16("conn", &rc); + if (rc != 0) { + console_printf("invalid 'conn' parameter\n"); + return rc; + } + + tx_octets = parse_arg_uint16("octets", &rc); + if (rc != 0) { + console_printf("invalid 'octets' parameter\n"); + return rc; + } + + tx_time = parse_arg_uint16("time", &rc); + if (rc != 0) { + console_printf("invalid 'time' parameter\n"); + return rc; + } + + rc = btshell_datalen(conn_handle, tx_octets, tx_time); + if (rc != 0) { + console_printf("error setting data length; rc=%d\n", rc); + return rc; + } + + return 0; +} + +#if MYNEWT_VAL(SHELL_CMD_HELP) +static const struct shell_param conn_datalen_params[] = { + {"conn", "Connection handle, usage: ="}, + {"octets", "Max payload size to include in LL Data PDU, " + "range=<27-251>, usage: ="}, + {"time", "Max number of microseconds the controller should use to tx " + "single LL packet, range=<328-17040>, usage: ="}, + {NULL, NULL} +}; + +static const struct shell_cmd_help conn_datalen_help = { + .summary = "set data length parameters for connection", + .usage = NULL, + .params = conn_datalen_params, +}; +#endif + +/***************************************************************************** + * keystore * + *****************************************************************************/ + +static const struct kv_pair cmd_keystore_entry_type[] = { + { "msec", BLE_STORE_OBJ_TYPE_PEER_SEC }, + { "ssec", BLE_STORE_OBJ_TYPE_OUR_SEC }, + { "cccd", BLE_STORE_OBJ_TYPE_CCCD }, + { NULL } +}; + +static int +cmd_keystore_parse_keydata(int argc, char **argv, union ble_store_key *out, + int *obj_type) +{ + int rc; + + memset(out, 0, sizeof(*out)); + *obj_type = parse_arg_kv("type", cmd_keystore_entry_type, &rc); + if (rc != 0) { + console_printf("invalid 'type' parameter\n"); + return rc; + } + + switch (*obj_type) { + case BLE_STORE_OBJ_TYPE_PEER_SEC: + case BLE_STORE_OBJ_TYPE_OUR_SEC: + rc = parse_dev_addr(NULL, cmd_addr_type, &out->sec.peer_addr); + if (rc != 0) { + console_printf("invalid 'addr' parameter\n"); + return rc; + } + + out->sec.ediv = parse_arg_uint16("ediv", &rc); + if (rc != 0) { + console_printf("invalid 'ediv' parameter\n"); + return rc; + } + + out->sec.rand_num = parse_arg_uint64("rand", &rc); + if (rc != 0) { + console_printf("invalid 'rand' parameter\n"); + return rc; + } + return 0; + + default: + return EINVAL; + } +} + +static int +cmd_keystore_parse_valuedata(int argc, char **argv, + int obj_type, + union ble_store_key *key, + union ble_store_value *out) +{ + int rc; + int valcnt = 0; + memset(out, 0, sizeof(*out)); + + switch (obj_type) { + case BLE_STORE_OBJ_TYPE_PEER_SEC: + case BLE_STORE_OBJ_TYPE_OUR_SEC: + rc = parse_arg_byte_stream_exact_length("ltk", out->sec.ltk, 16); + if (rc == 0) { + out->sec.ltk_present = 1; + swap_in_place(out->sec.ltk, 16); + valcnt++; + } else if (rc != ENOENT) { + console_printf("invalid 'ltk' parameter\n"); + return rc; + } + rc = parse_arg_byte_stream_exact_length("irk", out->sec.irk, 16); + if (rc == 0) { + out->sec.irk_present = 1; + swap_in_place(out->sec.irk, 16); + valcnt++; + } else if (rc != ENOENT) { + console_printf("invalid 'irk' parameter\n"); + return rc; + } + rc = parse_arg_byte_stream_exact_length("csrk", out->sec.csrk, 16); + if (rc == 0) { + out->sec.csrk_present = 1; + swap_in_place(out->sec.csrk, 16); + valcnt++; + } else if (rc != ENOENT) { + console_printf("invalid 'csrk' parameter\n"); + return rc; + } + out->sec.peer_addr = key->sec.peer_addr; + out->sec.ediv = key->sec.ediv; + out->sec.rand_num = key->sec.rand_num; + break; + } + + if (valcnt) { + return 0; + } + return -1; +} + +/***************************************************************************** + * keystore-add * + *****************************************************************************/ + +static int +cmd_keystore_add(int argc, char **argv) +{ + union ble_store_key key; + union ble_store_value value; + int obj_type; + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + rc = cmd_keystore_parse_keydata(argc, argv, &key, &obj_type); + + if (rc) { + return rc; + } + + rc = cmd_keystore_parse_valuedata(argc, argv, obj_type, &key, &value); + + if (rc) { + return rc; + } + + switch(obj_type) { + case BLE_STORE_OBJ_TYPE_PEER_SEC: + rc = ble_store_write_peer_sec(&value.sec); + break; + case BLE_STORE_OBJ_TYPE_OUR_SEC: + rc = ble_store_write_our_sec(&value.sec); + break; + case BLE_STORE_OBJ_TYPE_CCCD: + rc = ble_store_write_cccd(&value.cccd); + break; + default: + rc = ble_store_write(obj_type, &value); + } + return rc; +} + +#if MYNEWT_VAL(SHELL_CMD_HELP) +static const struct shell_param keystore_add_params[] = { + {"type", "entry type, usage: ="}, + {"addr_type", "usage: ="}, + {"addr", "usage: ="}, + {"ediv", "usage: ="}, + {"rand", "usage: ="}, + {"ltk", "usage: =, len=16 octets"}, + {"irk", "usage: =, len=16 octets"}, + {"csrk", "usage: =, len=16 octets"}, + {NULL, NULL} +}; + +static const struct shell_cmd_help keystore_add_help = { + .summary = "add data to keystore", + .usage = NULL, + .params = keystore_add_params, +}; +#endif + +/***************************************************************************** + * keystore-del * + *****************************************************************************/ + +static int +cmd_keystore_del(int argc, char **argv) +{ + union ble_store_key key; + int obj_type; + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + rc = cmd_keystore_parse_keydata(argc, argv, &key, &obj_type); + + if (rc) { + return rc; + } + rc = ble_store_delete(obj_type, &key); + return rc; +} + +#if MYNEWT_VAL(SHELL_CMD_HELP) +static const struct shell_param keystore_del_params[] = { + {"type", "entry type, usage: ="}, + {"addr_type", "usage: ="}, + {"addr", "usage: ="}, + {"ediv", "usage: ="}, + {"rand", "usage: ="}, + {NULL, NULL} +}; + +static const struct shell_cmd_help keystore_del_help = { + .summary = "remove data from keystore", + .usage = NULL, + .params = keystore_del_params, +}; +#endif + +/***************************************************************************** + * keystore-show * + *****************************************************************************/ + +static int +cmd_keystore_iterator(int obj_type, + union ble_store_value *val, + void *cookie) { + + switch (obj_type) { + case BLE_STORE_OBJ_TYPE_PEER_SEC: + case BLE_STORE_OBJ_TYPE_OUR_SEC: + console_printf("Key: "); + if (ble_addr_cmp(&val->sec.peer_addr, BLE_ADDR_ANY) == 0) { + console_printf("ediv=%u ", val->sec.ediv); + console_printf("ediv=%llu ", val->sec.rand_num); + } else { + console_printf("addr_type=%u ", val->sec.peer_addr.type); + print_addr(val->sec.peer_addr.val); + } + console_printf("\n"); + + if (val->sec.ltk_present) { + console_printf(" LTK: "); + print_bytes(val->sec.ltk, 16); + console_printf("\n"); + } + if (val->sec.irk_present) { + console_printf(" IRK: "); + print_bytes(val->sec.irk, 16); + console_printf("\n"); + } + if (val->sec.csrk_present) { + console_printf(" CSRK: "); + print_bytes(val->sec.csrk, 16); + console_printf("\n"); + } + break; + case BLE_STORE_OBJ_TYPE_CCCD: + console_printf("Key: "); + console_printf("addr_type=%u ", val->cccd.peer_addr.type); + print_addr(val->cccd.peer_addr.val); + console_printf("\n"); + + console_printf(" char_val_handle: %d\n", val->cccd.chr_val_handle); + console_printf(" flags: 0x%02x\n", val->cccd.flags); + console_printf(" changed: %d\n", val->cccd.value_changed); + break; + } + return 0; +} + +static int +cmd_keystore_show(int argc, char **argv) +{ + int type; + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + type = parse_arg_kv("type", cmd_keystore_entry_type, &rc); + if (rc != 0) { + console_printf("invalid 'type' parameter\n"); + return rc; + } + + ble_store_iterate(type, &cmd_keystore_iterator, NULL); + return 0; +} + +#if MYNEWT_VAL(SHELL_CMD_HELP) +static const struct shell_param keystore_show_params[] = { + {"type", "entry type, usage: ="}, + {NULL, NULL} +}; + +static const struct shell_cmd_help keystore_show_help = { + .summary = "show data in keystore", + .usage = NULL, + .params = keystore_show_params, +}; +#endif + +#if NIMBLE_BLE_SM +/***************************************************************************** + * $show-oob-sc * + *****************************************************************************/ + +extern struct ble_sm_sc_oob_data oob_data_local; +extern struct ble_sm_sc_oob_data oob_data_remote; + +static int +cmd_show_oob_sc(int argc, char **argv) +{ + console_printf("Local OOB Data: r="); + print_bytes(oob_data_local.r, 16); + console_printf(" c="); + print_bytes(oob_data_local.c, 16); + console_printf("\n"); + + console_printf("Remote OOB Data: r="); + print_bytes(oob_data_remote.r, 16); + console_printf(" c="); + print_bytes(oob_data_remote.c, 16); + console_printf("\n"); + + return 0; +} + +/***************************************************************************** + * $auth-passkey * + *****************************************************************************/ + +static int +cmd_auth_passkey(int argc, char **argv) +{ + uint16_t conn_handle; + struct ble_sm_io pk; + char *yesno; + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + conn_handle = parse_arg_uint16("conn", &rc); + if (rc != 0) { + console_printf("invalid 'conn' parameter\n"); + return rc; + } + + pk.action = parse_arg_uint16("action", &rc); + if (rc != 0) { + console_printf("invalid 'action' parameter\n"); + return rc; + } + + switch (pk.action) { + case BLE_SM_IOACT_INPUT: + case BLE_SM_IOACT_DISP: + /* passkey is 6 digit number */ + pk.passkey = parse_arg_long_bounds("key", 0, 999999, &rc); + if (rc != 0) { + console_printf("invalid 'key' parameter\n"); + return rc; + } + break; + + case BLE_SM_IOACT_OOB: + rc = parse_arg_byte_stream_exact_length("oob", pk.oob, 16); + if (rc != 0) { + console_printf("invalid 'oob' parameter\n"); + return rc; + } + break; + + case BLE_SM_IOACT_NUMCMP: + yesno = parse_arg_extract("yesno"); + if (yesno == NULL) { + console_printf("invalid 'yesno' parameter\n"); + return EINVAL; + } + + switch (yesno[0]) { + case 'y': + case 'Y': + pk.numcmp_accept = 1; + break; + case 'n': + case 'N': + pk.numcmp_accept = 0; + break; + + default: + console_printf("invalid 'yesno' parameter\n"); + return EINVAL; + } + break; + + case BLE_SM_IOACT_OOB_SC: + rc = parse_arg_byte_stream_exact_length("r", oob_data_remote.r, 16); + if (rc != 0 && rc != ENOENT) { + console_printf("invalid 'r' parameter\n"); + return rc; + } + + rc = parse_arg_byte_stream_exact_length("c", oob_data_remote.c, 16); + if (rc != 0 && rc != ENOENT) { + console_printf("invalid 'c' parameter\n"); + return rc; + } + pk.oob_sc_data.local = &oob_data_local; + if (ble_hs_cfg.sm_oob_data_flag) { + pk.oob_sc_data.remote = &oob_data_remote; + } else { + pk.oob_sc_data.remote = NULL; + } + break; + + default: + console_printf("invalid passkey action action=%d\n", pk.action); + return EINVAL; + } + + rc = ble_sm_inject_io(conn_handle, &pk); + if (rc != 0) { + console_printf("error providing passkey; rc=%d\n", rc); + return rc; + } + + return 0; +} + +#if MYNEWT_VAL(SHELL_CMD_HELP) +static const struct shell_param auth_passkey_params[] = { + {"conn", "connection handle, usage: ="}, + {"action", "auth action type, usage: ="}, + {"key", "usage: =[0-999999]"}, + {"oob", "usage: =[XX:XX...], len=16 octets"}, + {"yesno", "usage: =[string]"}, + {NULL, NULL} +}; + +static const struct shell_cmd_help auth_passkey_help = { + .summary = "set authorization passkey options", + .usage = NULL, + .params = auth_passkey_params, +}; +#endif + +/***************************************************************************** + * $security-pair * + *****************************************************************************/ + +static int +cmd_security_pair(int argc, char **argv) +{ + uint16_t conn_handle; + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + conn_handle = parse_arg_uint16("conn", &rc); + if (rc != 0) { + console_printf("invalid 'conn' parameter\n"); + return rc; + } + + rc = btshell_sec_pair(conn_handle); + if (rc != 0) { + console_printf("error initiating pairing; rc=%d\n", rc); + return rc; + } + + return 0; +} + +#if MYNEWT_VAL(SHELL_CMD_HELP) +static const struct shell_param security_pair_params[] = { + {"conn", "connection handle, usage: ="}, + {NULL, NULL} +}; + +static const struct shell_cmd_help security_pair_help = { + .summary = "start pairing procedure for connection", + .usage = NULL, + .params = security_pair_params, +}; +#endif + +/***************************************************************************** + * $security-unpair * + *****************************************************************************/ + +static int +cmd_security_unpair(int argc, char **argv) +{ + ble_addr_t peer; + int rc; + int oldest; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + rc = parse_arg_bool_dflt("oldest", 0, &oldest); + if (rc != 0) { + console_printf("invalid 'oldest' parameter\n"); + return rc; + } + + if (oldest) { + rc = ble_gap_unpair_oldest_peer(); + console_printf("Unpair oldest status: 0x%02x\n", rc); + return 0; + } + + rc = parse_dev_addr("peer_", cmd_peer_addr_types, &peer); + if (rc != 0) { + console_printf("invalid peer address\n"); + return rc; + } + + rc = ble_gap_unpair(&peer); + if (rc != 0) { + console_printf("error unpairing; rc=%d\n", rc); + return rc; + } + + return 0; +} + +#if MYNEWT_VAL(SHELL_CMD_HELP) +static const struct shell_param security_unpair_params[] = { + {"oldest", "usage: =[true|false], default: false"}, + {"peer_addr_type", "usage: =[public|random|public_id|random_id], default: public"}, + {"peer_addr", "usage: =[XX:XX:XX:XX:XX:XX]"}, + {NULL, NULL} +}; + +static const struct shell_cmd_help security_unpair_help = { + .summary = "unpair a peer device", + .usage = NULL, + .params = security_unpair_params, +}; +#endif + +/***************************************************************************** + * $security-start * + *****************************************************************************/ + +static int +cmd_security_start(int argc, char **argv) +{ + uint16_t conn_handle; + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + conn_handle = parse_arg_uint16("conn", &rc); + if (rc != 0) { + console_printf("invalid 'conn' parameter\n"); + return rc; + } + + rc = btshell_sec_start(conn_handle); + if (rc != 0) { + console_printf("error starting security; rc=%d\n", rc); + return rc; + } + + return 0; +} + +#if MYNEWT_VAL(SHELL_CMD_HELP) +static const struct shell_param security_start_params[] = { + {"conn", "connection handle, usage: ="}, + {NULL, NULL} +}; + +static const struct shell_cmd_help security_start_help = { + .summary = "start security procedure for connection", + .usage = NULL, + .params = security_start_params, +}; +#endif + +/***************************************************************************** + * $security-encryption * + *****************************************************************************/ + +static int +cmd_security_encryption(int argc, char **argv) +{ + uint16_t conn_handle; + uint16_t ediv; + uint64_t rand_val; + uint8_t ltk[16]; + uint8_t key_size; + int rc; + int auth; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + conn_handle = parse_arg_uint16("conn", &rc); + if (rc != 0) { + console_printf("invalid 'conn' parameter\n"); + return rc; + } + + ediv = parse_arg_uint16("ediv", &rc); + if (rc == ENOENT) { + rc = btshell_sec_restart(conn_handle, 0, NULL, 0, 0, 0); + } else { + rand_val = parse_arg_uint64("rand", &rc); + if (rc != 0) { + console_printf("invalid 'rand' parameter\n"); + return rc; + } + + auth = parse_arg_bool("auth", &rc); + if (rc != 0) { + console_printf("invalid 'auth' parameter\n"); + return rc; + } + + key_size = parse_arg_uint8("key_size", &rc); + if (rc != 0) { + console_printf("invalid 'key_size' parameter\n"); + return rc; + } + + rc = parse_arg_byte_stream_exact_length("ltk", ltk, 16); + if (rc != 0) { + console_printf("invalid 'ltk' parameter\n"); + return rc; + } + + rc = btshell_sec_restart(conn_handle, key_size, + ltk, ediv, rand_val, auth); + } + + if (rc != 0) { + console_printf("error initiating encryption; rc=%d\n", rc); + return rc; + } + + return 0; +} + +#if MYNEWT_VAL(SHELL_CMD_HELP) +static const struct shell_param security_encryption_params[] = { + {"conn", "connection handle, usage: ="}, + {"ediv", "usage: =[UINT16]"}, + {"rand", "usage: =[UINT64]"}, + {"auth", "usage: =[0-1]"}, + {"ltk", "usage: =[XX:XX...], len=16 octets"}, + {NULL, NULL} +}; + +static const struct shell_cmd_help security_encryption_help = { + .summary = "start encryption procedure for connection", + .usage = NULL, + .params = security_encryption_params, +}; +#endif + +/***************************************************************************** + * $security-set-data * + *****************************************************************************/ + +static int +cmd_security_set_data(int argc, char **argv) +{ + uint8_t tmp; + int good; + int rc; + + good = 0; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + tmp = parse_arg_bool("oob_flag", &rc); + if (rc == 0) { + ble_hs_cfg.sm_oob_data_flag = tmp; + good++; + } else if (rc != ENOENT) { + console_printf("invalid 'oob_flag' parameter\n"); + return rc; + } + + tmp = parse_arg_bool("mitm_flag", &rc); + if (rc == 0) { + good++; + ble_hs_cfg.sm_mitm = tmp; + } else if (rc != ENOENT) { + console_printf("invalid 'mitm_flag' parameter\n"); + return rc; + } + + tmp = parse_arg_uint8("io_capabilities", &rc); + if (rc == 0) { + good++; + ble_hs_cfg.sm_io_cap = tmp; + } else if (rc != ENOENT) { + console_printf("invalid 'io_capabilities' parameter\n"); + return rc; + } + + tmp = parse_arg_uint8("our_key_dist", &rc); + if (rc == 0) { + good++; + ble_hs_cfg.sm_our_key_dist = tmp; + } else if (rc != ENOENT) { + console_printf("invalid 'our_key_dist' parameter\n"); + return rc; + } + + tmp = parse_arg_uint8("their_key_dist", &rc); + if (rc == 0) { + good++; + ble_hs_cfg.sm_their_key_dist = tmp; + } else if (rc != ENOENT) { + console_printf("invalid 'their_key_dist' parameter\n"); + return rc; + } + + tmp = parse_arg_bool("bonding", &rc); + if (rc == 0) { + good++; + ble_hs_cfg.sm_bonding = tmp; + } else if (rc != ENOENT) { + console_printf("invalid 'bonding' parameter\n"); + return rc; + } + + tmp = parse_arg_bool("sc", &rc); + if (rc == 0) { + good++; + ble_hs_cfg.sm_sc = tmp; + } else if (rc != ENOENT) { + console_printf("invalid 'sc' parameter\n"); + return rc; + } + + if (!good) { + console_printf("Error: no valid settings specified\n"); + return -1; + } + + return 0; +} + +#if MYNEWT_VAL(SHELL_CMD_HELP) +static const struct shell_param security_set_data_params[] = { + {"oob_flag", "usage: =[0-1]"}, + {"mitm_flag", "usage: =[0-1]"}, + {"io_capabilities", "usage: =[UINT8]"}, + {"our_key_dist", "usage: =[UINT8]"}, + {"their_key_dist", "usage: =[UINT8]"}, + {"bonding", "usage: =[0-1]"}, + {"sc", "usage: =[0-1]"}, + {NULL, NULL} +}; + +static const struct shell_cmd_help security_set_data_help = { + .summary = "set security data", + .usage = NULL, + .params = security_set_data_params, +}; +#endif +#endif + +/***************************************************************************** + * $test-tx * + * * + * Command to transmit 'num' packets of size 'len' at rate 'r' to + * handle 'h' Note that length must be <= 251. The rate is in msecs. + * + *****************************************************************************/ + +static int +cmd_test_tx(int argc, char **argv) +{ + int rc; + uint16_t conn; + uint16_t len; + uint16_t rate; + uint16_t num; + uint8_t stop; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + stop = parse_arg_uint8_dflt("stop", 0, &rc); + if (rc != 0) { + console_printf("invalid 'stop' parameter\n"); + return rc; + } + + if (stop) { + btshell_tx_stop(); + return 0; + } + + conn = parse_arg_uint16("conn", &rc); + if (rc != 0) { + console_printf("invalid 'conn' parameter\n"); + return rc; + } + + len = parse_arg_uint16("length", &rc); + if (rc != 0) { + console_printf("invalid 'length' parameter\n"); + return rc; + } + if ((len > 251) || (len < 4)) { + console_printf("error: len must be between 4 and 251, inclusive"); + } + + rate = parse_arg_uint16_dflt("rate", 1, &rc); + if (rc != 0) { + console_printf("invalid 'rate' parameter\n"); + return rc; + } + + num = parse_arg_uint16_dflt("num", 1, &rc); + if (rc != 0) { + console_printf("invalid 'num' parameter\n"); + return rc; + } + + rc = btshell_tx_start(conn, len, rate, num); + return rc; +} + +#if MYNEWT_VAL(SHELL_CMD_HELP) +static const struct shell_param test_tx_params[] = { + {"conn", "handle to tx to, usage: ="}, + {"length", "size of packet, usage: ="}, + {"rate", "rate of tx, usage: =, default=1"}, + {"num", "number of packets, usage: =, default=1"}, + {"stop", "stop sending, usage: 1 to stop, default 0"}, + {NULL, NULL} +}; + +static const struct shell_cmd_help test_tx_help = { + .summary = "test packet transmission", + .usage = NULL, + .params = test_tx_params, +}; +#endif + +/***************************************************************************** + * $phy-set * + *****************************************************************************/ + +static int +cmd_phy_set(int argc, char **argv) +{ + uint16_t conn; + uint8_t tx_phys_mask; + uint8_t rx_phys_mask; + uint16_t phy_opts; + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + conn = parse_arg_uint16("conn", &rc); + if (rc != 0) { + console_printf("invalid 'conn' parameter\n"); + return rc; + } + + tx_phys_mask = parse_arg_uint8("tx_phys_mask", &rc); + if (rc != 0) { + console_printf("invalid 'tx_phys_mask' parameter\n"); + return rc; + } + + rx_phys_mask = parse_arg_uint8("rx_phys_mask", &rc); + if (rc != 0) { + console_printf("invalid 'rx_phys_mask' parameter\n"); + return rc; + } + + phy_opts = parse_arg_uint16("phy_opts", &rc); + if (rc != 0) { + console_printf("invalid 'phy_opts' parameter\n"); + return rc; + } + + return ble_gap_set_prefered_le_phy(conn, tx_phys_mask, rx_phys_mask, + phy_opts); +} + +#if MYNEWT_VAL(SHELL_CMD_HELP) +static const struct shell_param phy_set_params[] = { + {"conn", "connection handle, usage: ="}, + {"tx_phys_mask", "usage: ="}, + {"rx_phys_mask", "usage: ="}, + {"phy_opts", "usage: ="}, + {NULL, NULL} +}; + +static const struct shell_cmd_help phy_set_help = { + .summary = "set preferred PHYs", + .usage = NULL, + .params = phy_set_params, +}; +#endif + +/***************************************************************************** + * $phy-set-default * + *****************************************************************************/ + +static int +cmd_phy_set_default(int argc, char **argv) +{ + uint8_t tx_phys_mask; + uint8_t rx_phys_mask; + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + tx_phys_mask = parse_arg_uint8("tx_phys_mask", &rc); + if (rc != 0) { + console_printf("invalid 'tx_phys_mask' parameter\n"); + return rc; + } + + rx_phys_mask = parse_arg_uint8("rx_phys_mask", &rc); + if (rc != 0) { + console_printf("invalid 'rx_phys_mask' parameter\n"); + return rc; + } + + return ble_gap_set_prefered_default_le_phy(tx_phys_mask, rx_phys_mask); +} + +#if MYNEWT_VAL(SHELL_CMD_HELP) +static const struct shell_param phy_set_default_params[] = { + {"tx_phys_mask", "usage: ="}, + {"rx_phys_mask", "usage: ="}, + {NULL, NULL} +}; + +static const struct shell_cmd_help phy_set_default_help = { + .summary = "set preferred default PHYs", + .usage = NULL, + .params = phy_set_default_params, +}; +#endif + +/***************************************************************************** + * $phy-read * + *****************************************************************************/ + +static int +cmd_phy_read(int argc, char **argv) +{ + uint16_t conn = 0; + uint8_t tx_phy; + uint8_t rx_phy; + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + conn = parse_arg_uint16("conn", &rc); + if (rc != 0) { + console_printf("invalid 'conn' parameter\n"); + return rc; + } + + rc = ble_gap_read_le_phy(conn, &tx_phy, &rx_phy); + if (rc != 0) { + console_printf("Could not read PHY error: %d\n", rc); + return rc; + } + + console_printf("TX_PHY: %d\n", tx_phy); + console_printf("RX_PHY: %d\n", tx_phy); + + return 0; +} + +#if MYNEWT_VAL(SHELL_CMD_HELP) +static const struct shell_param phy_read_params[] = { + {"conn", "connection handle, usage: ="}, + {NULL, NULL} +}; + +static const struct shell_cmd_help phy_read_help = { + .summary = "read PHYs", + .usage = NULL, + .params = phy_read_params, +}; +#endif + +/***************************************************************************** + * $host-enable * + *****************************************************************************/ + +static int +cmd_host_enable(int argc, char **argv) +{ + int rc; + + rc = gatt_svr_init(); + assert(rc == 0); + + ble_hs_sched_start(); + + return 0; +} + +#if MYNEWT_VAL(SHELL_CMD_HELP) +static const struct shell_cmd_help host_enable_help = { + .summary = "start the NimBLE host", + .usage = NULL, + .params = NULL, +}; +#endif + +/***************************************************************************** + * $host-disable * + *****************************************************************************/ + +static void +on_stop(int status, void *arg) +{ + if (status == 0) { + console_printf("host stopped\n"); + } else { + console_printf("host failed to stop; rc=%d\n", status); + } +} + +static int +cmd_host_disable(int argc, char **argv) +{ + static struct ble_hs_stop_listener listener; + int rc; + + rc = ble_hs_stop(&listener, on_stop, NULL); + if (rc) { + return rc; + } + + ble_gatts_reset(); + + return 0; +} + +#if MYNEWT_VAL(SHELL_CMD_HELP) +static const struct shell_cmd_help host_disable_help = { + .summary = "stop the NimBLE host", + .usage = NULL, + .params = NULL, +}; + +/***************************************************************************** + * $gatt-discover * + *****************************************************************************/ + +static const struct shell_param gatt_discover_characteristic_params[] = { + {"conn", "connection handle, usage: ="}, + {"uuid", "discover by uuid, usage: =[UUID]"}, + {"start", "start handle, usage: ="}, + {"end", "end handle, usage: ="}, + {NULL, NULL} +}; + +static const struct shell_cmd_help gatt_discover_characteristic_help = { + .summary = "perform characteristic discovery procedure", + .usage = NULL, + .params = gatt_discover_characteristic_params, +}; + +static const struct shell_param gatt_discover_descriptor_params[] = { + {"conn", "connection handle, usage: ="}, + {"start", "start handle, usage: ="}, + {"end", "end handle, usage: ="}, + {NULL, NULL} +}; + +static const struct shell_cmd_help gatt_discover_descriptor_help = { + .summary = "perform descriptor discovery procedure", + .usage = NULL, + .params = gatt_discover_descriptor_params, +}; + +static const struct shell_param gatt_discover_service_params[] = { + {"conn", "connection handle, usage: ="}, + {"uuid", "discover by uuid, usage: =[UUID]"}, + {NULL, NULL} +}; + +static const struct shell_cmd_help gatt_discover_service_help = { + .summary = "perform service discovery procedure", + .usage = NULL, + .params = gatt_discover_service_params, +}; + +static const struct shell_param gatt_discover_full_params[] = { + {"conn", "connection handle, usage: ="}, + {NULL, NULL} +}; + +static const struct shell_cmd_help gatt_discover_full_help = { + .summary = "perform full discovery procedure", + .usage = NULL, + .params = gatt_discover_full_params, +}; + +/***************************************************************************** + * $gatt-exchange-mtu * + *****************************************************************************/ + +static const struct shell_param gatt_exchange_mtu_params[] = { + {"conn", "connection handle, usage: ="}, + {NULL, NULL} +}; + +static const struct shell_cmd_help gatt_exchange_mtu_help = { + .summary = "perform mtu exchange procedure", + .usage = NULL, + .params = gatt_exchange_mtu_params, +}; + +/***************************************************************************** + * $gatt-find-included-services * + *****************************************************************************/ + +static const struct shell_param gatt_find_included_services_params[] = { + {"conn", "connection handle, usage: ="}, + {"start", "start handle, usage: ="}, + {"end", "end handle, usage: ="}, + {NULL, NULL} +}; + +static const struct shell_cmd_help gatt_find_included_services_help = { + .summary = "perform find included services procedure", + .usage = NULL, + .params = gatt_find_included_services_params, +}; + +/***************************************************************************** + * $gatt-notify * + *****************************************************************************/ + +static const struct shell_param gatt_notify_params[] = { + {"attr", "attribute handle, usage: ="}, + {NULL, NULL} +}; + +static const struct shell_cmd_help gatt_notify_help = { + .summary = "notify about attribute value changed", + .usage = NULL, + .params = gatt_notify_params, +}; + +/***************************************************************************** + * $gatt-read * + *****************************************************************************/ + +static const struct shell_param gatt_read_params[] = { + {"conn", "connection handle, usage: ="}, + {"long", "is read long, usage: =[0-1], default=0"}, + {"attr", "attribute handle, usage: ="}, + {"offset", "offset value, usage: ="}, + {"uuid", "read by uuid, usage: =[UUID]"}, + {"start", "start handle, usage: ="}, + {"end", "end handle, usage: ="}, + {NULL, NULL} +}; + +static const struct shell_cmd_help gatt_read_help = { + .summary = "perform gatt read procedure", + .usage = NULL, + .params = gatt_read_params, +}; + +/***************************************************************************** + * $gatt-service-changed * + *****************************************************************************/ + +static const struct shell_param gatt_service_changed_params[] = { + {"start", "start handle, usage: ="}, + {"end", "end handle, usage: ="}, + {NULL, NULL} +}; + +static const struct shell_cmd_help gatt_service_changed_help = { + .summary = "send service changed indication", + .usage = NULL, + .params = gatt_service_changed_params, +}; + +/***************************************************************************** + * $gatt-service-visibility * + *****************************************************************************/ + +static const struct shell_param gatt_service_visibility_params[] = { + {"handle", "usage: ="}, + {"visibility", "usage: =<0-1>"}, + {NULL, NULL} +}; + +static const struct shell_cmd_help gatt_service_visibility_help = { + .summary = "change service visibility", + .usage = NULL, + .params = gatt_service_visibility_params, +}; + +/***************************************************************************** + * $gatt-show * + *****************************************************************************/ + +static const struct shell_param gatt_show_params[] = { + {NULL, NULL} +}; + +static const struct shell_cmd_help gatt_show_help = { + .summary = "show discovered gatt database", + .usage = NULL, + .params = gatt_show_params, +}; + +static const struct shell_cmd_help gatt_show_local_help = { + .summary = "show local gatt database", + .usage = NULL, + .params = gatt_show_params, +}; + +static const struct shell_cmd_help gatt_show_addr_help = { + .summary = "show device address", + .usage = NULL, + .params = gatt_show_params, +}; + +static const struct shell_cmd_help gatt_show_conn_help = { + .summary = "show connections information", + .usage = NULL, + .params = gatt_show_params, +}; + +/***************************************************************************** + * $gatt-write * + *****************************************************************************/ + +static const struct shell_param gatt_write_params[] = { + {"conn", "connection handle, usage: ="}, + {"no_rsp", "write without response, usage: =[0-1], default=0"}, + {"long", "is write long, usage: =[0-1], default=0"}, + {"attr", "attribute handle, usage: ="}, + {"offset", "attribute handle, usage: ="}, + {"value", "usage: ="}, + {NULL, NULL} +}; + +static const struct shell_cmd_help gatt_write_help = { + .summary = "perform gatt write procedure", + .usage = NULL, + .params = gatt_write_params, +}; +#endif + +#if MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM) +#if MYNEWT_VAL(SHELL_CMD_HELP) +/***************************************************************************** + * $l2cap-update * + *****************************************************************************/ + +static const struct shell_param l2cap_update_params[] = { + {"conn", "connection handle, usage: ="}, + {"interval_min", "usage: =[0-UINT16_MAX], default: 30"}, + {"interval_max", "usage: =[0-UINT16_MAX], default: 50"}, + {"latency", "usage: =[UINT16], default: 0"}, + {"timeout", "usage: =[UINT16], default: 0x0100"}, + {NULL, NULL} +}; + +static const struct shell_cmd_help l2cap_update_help = { + .summary = "update l2cap parameters for connection", + .usage = NULL, + .params = l2cap_update_params, +}; + +/***************************************************************************** + * $l2cap-create-server * + *****************************************************************************/ + +static const struct shell_param l2cap_create_server_params[] = { + {"psm", "usage: ="}, + {"mtu", "usage: = not more than BTSHELL_COC_MTU, default BTSHELL_COC_MTU"}, + {"error", "usage: used for PTS testing:"}, + {"", "0 - always accept"}, + {"", "1 - reject with insufficient authentication"}, + {"", "2 - reject with insufficient authorization"}, + {"", "3 - reject with insufficient key size"}, + {NULL, NULL} +}; + +static const struct shell_cmd_help l2cap_create_server_help = { + .summary = "create l2cap server", + .usage = NULL, + .params = l2cap_create_server_params, +}; + +/***************************************************************************** + * $l2cap-connect * + *****************************************************************************/ + +static const struct shell_param l2cap_connect_params[] = { + {"conn", "connection handle, usage: ="}, + {"psm", "usage: ="}, + {"num", "usage: number of connection created in a row: [1-5]"}, + {"mtu", "usage: = not more than BTSHELL_COC_MTU, default BTSHELL_COC_MTU"}, + {NULL, NULL} +}; + +static const struct shell_cmd_help l2cap_connect_help = { + .summary = "perform l2cap connect procedure", + .usage = NULL, + .params = l2cap_connect_params, +}; + +/***************************************************************************** + * $l2cap-disconnect * + *****************************************************************************/ + +static const struct shell_param l2cap_disconnect_params[] = { + {"conn", "connection handle, usage: ="}, + {"idx", "usage: ="}, + {NULL, NULL} +}; + +static const struct shell_cmd_help l2cap_disconnect_help = { + .summary = "perform l2cap disconnect procedure", + .usage = "use gatt-show-coc to get the parameters", + .params = l2cap_disconnect_params, +}; + +/***************************************************************************** + * $l2cap-reconfig * + *****************************************************************************/ + +static const struct shell_param l2cap_reconfig_params[] = { + {"conn", "connection handle, usage: ="}, + {"mtu", "new mtu, usage: =, default: 0 (no change)"}, + {"idxs", "list of channel indexes, usage: idxs=1,3"}, + {NULL, NULL} +}; + +static const struct shell_cmd_help l2cap_reconfig_help = { + .summary = "perform l2cap reconfigure procedure", + .usage = "use gatt-show-coc to get the parameters", + .params = l2cap_reconfig_params, +}; + +/***************************************************************************** + * $l2cap-send * + *****************************************************************************/ + +static const struct shell_param l2cap_send_params[] = { + {"conn", "connection handle, usage: ="}, + {"idx", "usage: ="}, + {"bytes", "number of bytes to send, usage: ="}, + {NULL, NULL} +}; + +static const struct shell_cmd_help l2cap_send_help = { + .summary = "perform l2cap send procedure", + .usage = "use l2cap-show-coc to get the parameters", + .params = l2cap_send_params, +}; + +/***************************************************************************** + * $l2cap-show-coc * + *****************************************************************************/ + +static const struct shell_param l2cap_show_coc_params[] = { + {NULL, NULL} +}; + +static const struct shell_cmd_help l2cap_show_coc_help = { + .summary = "show coc information", + .usage = NULL, + .params = l2cap_show_coc_params, +}; + +#endif +#endif + +#if MYNEWT_VAL(BLE_PERIODIC_ADV) +static int +cmd_periodic_configure(int argc, char **argv) +{ + struct ble_gap_periodic_adv_params params = {0}; + uint8_t instance; + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + instance = parse_arg_uint8_dflt("instance", 0, &rc); + if (rc != 0 || instance >= BLE_ADV_INSTANCES) { + console_printf("invalid instance\n"); + return rc; + } + + memset(¶ms, 0, sizeof(params)); + + params.include_tx_power = parse_arg_bool_dflt("include_tx_power", 0, &rc); + if (rc != 0) { + console_printf("invalid 'include_tx_power' parameter\n"); + return rc; + } + + params.itvl_min = parse_arg_time_dflt("interval_min", 1250, 0, &rc); + if (rc != 0) { + console_printf("invalid 'interval_min' parameter\n"); + return rc; + } + + params.itvl_max = parse_arg_time_dflt("interval_max", 1250, params.itvl_min, + &rc); + if (rc != 0) { + console_printf("invalid 'interval_max' parameter\n"); + return rc; + } + + rc = ble_gap_periodic_adv_configure(instance, ¶ms); + if (rc) { + console_printf("failed to configure periodic advertising\n"); + return rc; + } + + console_printf("Instance %u configured for periodic advertising\n", + instance); + + return 0; +} + +static int +cmd_periodic_set_adv_data(int argc, char **argv) +{ + return cmd_set_adv_data_or_scan_rsp(argc, argv, false, true); +} + +static int +cmd_periodic_start(int argc, char **argv) +{ + uint8_t instance; + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + instance = parse_arg_uint8_dflt("instance", 0, &rc); + if (rc != 0 || instance >= BLE_ADV_INSTANCES) { + console_printf("invalid instance\n"); + return rc; + } + + rc = ble_gap_periodic_adv_start(instance); + if (rc) { + console_printf("failed to start periodic advertising\n"); + return rc; + } + + return 0; +} + +static int +cmd_periodic_stop(int argc, char **argv) +{ + uint8_t instance; + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + instance = parse_arg_uint8_dflt("instance", 0, &rc); + if (rc != 0 || instance >= BLE_ADV_INSTANCES) { + console_printf("invalid instance\n"); + return rc; + } + + rc = ble_gap_periodic_adv_stop(instance); + if (rc) { + console_printf("failed to stop periodic advertising\n"); + return rc; + } + + return 0; +} + +#if MYNEWT_VAL(SHELL_CMD_HELP) +static const struct shell_param periodic_configure_params[] = { + {"instance", "default: 0"}, + {"interval_min", "usage: =[0-UINT32_MAX], default: 0"}, + {"interval_max", "usage: =[0-UINT32_MAX], default: interval_min"}, + {"tx_power", "include TX power, usage: =[0-1], default: 0"}, + {NULL, NULL} +}; + +static const struct shell_cmd_help periodic_configure_help = { + .summary = "configure periodic advertising for instance", + .usage = NULL, + .params = periodic_configure_params, +}; + +static const struct shell_param periodic_start_params[] = { + {"instance", "default: 0"}, + {NULL, NULL} +}; + +static const struct shell_cmd_help periodic_start_help = { + .summary = "start periodic advertising for instance", + .usage = NULL, + .params = periodic_start_params, +}; + +static const struct shell_param periodic_stop_params[] = { + {"instance", "default: 0"}, + {NULL, NULL} +}; + +static const struct shell_cmd_help periodic_stop_help = { + .summary = "stop periodic advertising for instance", + .usage = NULL, + .params = periodic_stop_params, +}; +#endif + +static int +cmd_sync_create(int argc, char **argv) +{ + struct ble_gap_periodic_sync_params params = { 0 }; + ble_addr_t addr; + ble_addr_t *addr_param = &addr; + uint8_t sid; + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + if (argc > 1 && strcmp(argv[1], "cancel") == 0) { + rc = ble_gap_periodic_adv_sync_create_cancel(); + if (rc != 0) { + console_printf("Sync create cancel fail: %d\n", rc); + return rc; + } + + return 0; + } + + rc = parse_dev_addr("peer_", cmd_addr_type, &addr); + if (rc == ENOENT) { + /* With no "peer_addr" specified we'll use periodic list */ + addr_param = NULL; + } else if (rc != 0) { + console_printf("invalid 'addr' parameter\n"); + return rc; + } + + sid = parse_arg_uint8_dflt("sid", 0, &rc); + if (rc != 0) { + console_printf("invalid 'sid' parameter\n"); + return rc; + } + + params.skip = parse_arg_uint16_dflt("skip", 0, &rc); + if (rc != 0) { + console_printf("invalid 'skip' parameter\n"); + return rc; + } + + params.sync_timeout = parse_arg_time_dflt("sync_timeout", 10000, 2000, &rc); + if (rc != 0) { + console_printf("invalid 'sync_timeout' parameter\n"); + return rc; + } + + params.reports_disabled = parse_arg_bool_dflt("reports_disabled", false, &rc); + if (rc != 0) { + console_printf("invalid 'reports_disabled' parameter\n"); + return rc; + } + + rc = ble_gap_periodic_adv_sync_create(addr_param, sid, ¶ms, + btshell_gap_event, NULL); + if (rc) { + console_printf("Failed to create sync (%d)\n", rc); + } + + return rc; +} + +#if MYNEWT_VAL(SHELL_CMD_HELP) +static const struct shell_param sync_create_params[] = { + {"cancel", "cancel periodic sync establishment procedure"}, + {"peer_addr_type", "usage: =[public|random], default: public"}, + {"peer_addr", "usage: =[XX:XX:XX:XX:XX:XX]"}, + {"sid", "usage: =[UINT8], default: 0"}, + {"skip", "usage: =[0-0x01F3], default: 0x0000"}, + {"sync_timeout", "usage: =[0x000A-0x4000], default: 0x07D0"}, + {"reports_disabled", "disable reports, usage: =[0-1], default: 0"}, + {NULL, NULL} +}; + +static const struct shell_cmd_help sync_create_help = { + .summary = "start/stop periodic sync procedure with specific parameters", + .usage = NULL, + .params = sync_create_params, +}; +#endif + +#if MYNEWT_VAL(BLE_PERIODIC_ADV_SYNC_TRANSFER) +static int +cmd_sync_transfer(int argc, char **argv) +{ + uint16_t service_data; + uint16_t conn_handle; + uint16_t sync_handle; + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + conn_handle = parse_arg_uint16("conn", &rc); + if (rc != 0) { + console_printf("invalid 'conn' parameter\n"); + return rc; + } + + sync_handle = parse_arg_uint16_dflt("sync_handle", 0, &rc); + if (rc != 0) { + console_printf("invalid 'sync_handle' parameter\n"); + return rc; + } + + service_data = parse_arg_uint16_dflt("service_data", 0, &rc); + if (rc != 0) { + console_printf("invalid 'service_data' parameter\n"); + return rc; + } + + rc = ble_gap_periodic_adv_sync_transfer(sync_handle, conn_handle, + service_data); + if (rc) { + console_printf("Failed to transfer sync (%d)\n", rc); + } + + return rc; +} + +static int +cmd_sync_reporting(int argc, char **argv) +{ + uint16_t sync_handle; + bool enable; + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + sync_handle = parse_arg_uint16_dflt("sync_handle", 0, &rc); + if (rc != 0) { + console_printf("invalid 'sync_handle' parameter\n"); + return rc; + } + + enable = parse_arg_bool_dflt("enabled", 0, &rc); + if (rc != 0) { + console_printf("invalid 'enabled' parameter\n"); + return rc; + } + + rc = ble_gap_periodic_adv_sync_reporting(sync_handle, enable); + if (rc) { + console_printf("Failed to %s reporting (%d)\n", + enable ? "enable" : "disable", rc); + } + + return rc; +} + +#if MYNEWT_VAL(SHELL_CMD_HELP) +static const struct shell_param sync_transfer_params[] = { + {"conn", "connection handle, usage: ="}, + {"sync_handle", "sync handle, usage: =[UINT16], default: 0"}, + {"service_data", "service data, usage: =[UINT16], default: 0"}, + {NULL, NULL} +}; + +static const struct shell_cmd_help sync_transfer_help = { + .summary = "start periodic sync transfer procedure with specific parameters", + .usage = NULL, + .params = sync_transfer_params, +}; + +static const struct shell_param sync_reporting_params[] = { + {"sync_handle", "sync handle, usage: =[UINT16], default: 0"}, + {"enabled", "toggle reporting, usage: =[0-1], default: 0"}, + {NULL, NULL} +}; + +static const struct shell_cmd_help sync_reporting_help = { + .summary = "configure periodic advertising sync reporting", + .usage = NULL, + .params = sync_reporting_params, +}; +#endif + +static int +cmd_sync_transfer_set_info(int argc, char **argv) +{ + uint16_t service_data; + uint16_t conn_handle; + uint8_t instance; + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + conn_handle = parse_arg_uint16("conn", &rc); + if (rc != 0) { + console_printf("invalid 'conn' parameter\n"); + return rc; + } + + instance = parse_arg_uint8_dflt("instance", 0, &rc); + if (rc != 0) { + console_printf("invalid 'instance' parameter\n"); + return rc; + } + + service_data = parse_arg_uint16_dflt("service_data", 0, &rc); + if (rc != 0) { + console_printf("invalid 'service_data' parameter\n"); + return rc; + } + + rc = ble_gap_periodic_adv_sync_set_info(instance, conn_handle, + service_data); + if (rc) { + console_printf("Failed to transfer sync (%d)\n", rc); + } + + return rc; +} + +#if MYNEWT_VAL(SHELL_CMD_HELP) +static const struct shell_param sync_transfer_set_info_params[] = { + {"conn", "connection handle, usage: ="}, + {"instance", "advertising instance, usage: =[UINT8], default: 0"}, + {"service_data", "service data, usage: =[UINT16], default: 0"}, + {NULL, NULL} +}; + +static const struct shell_cmd_help sync_transfer_set_info_help = { + .summary = "start periodic sync transfer procedure with specific parameters", + .usage = NULL, + .params = sync_transfer_set_info_params, +}; +#endif + +static int +cmd_sync_transfer_receive(int argc, char **argv) +{ + struct ble_gap_periodic_sync_params params = { 0 }; + uint16_t conn_handle; + bool disable; + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + conn_handle = parse_arg_uint16("conn", &rc); + if (rc != 0) { + console_printf("invalid 'conn' parameter\n"); + return rc; + } + + disable = parse_arg_bool_dflt("disable", false, &rc); + if (rc != 0) { + console_printf("invalid 'disable' parameter\n"); + return rc; + } + + if (disable) { + rc = ble_gap_periodic_adv_sync_receive(conn_handle, NULL, NULL, NULL); + if (rc) { + console_printf("Failed to disable sync transfer reception (%d)\n", rc); + } + + return rc; + } + + params.skip = parse_arg_uint16_dflt("skip", 0, &rc); + if (rc != 0) { + console_printf("invalid 'skip' parameter\n"); + return rc; + } + + params.sync_timeout = parse_arg_time_dflt("sync_timeout", 10000, 10, &rc); + if (rc != 0) { + console_printf("invalid 'sync_timeout' parameter\n"); + return rc; + } + + params.reports_disabled = parse_arg_bool_dflt("reports_disabled", false, &rc); + if (rc != 0) { + console_printf("invalid 'reports_disabled' parameter\n"); + return rc; + } + + rc = ble_gap_periodic_adv_sync_receive(conn_handle, ¶ms, btshell_gap_event, + NULL); + if (rc) { + console_printf("Failed to enable sync transfer reception (%d)\n", rc); + } + + return rc; +} + +#if MYNEWT_VAL(SHELL_CMD_HELP) +static const struct shell_param sync_transfer_receive_params[] = { + {"conn", "connection handle, usage: ="}, + {"disable", "disable transfer reception, usage: =[0-1], default: 0"}, + {"skip", "usage: =[0-0x01F3], default: 0x0000"}, + {"sync_timeout", "usage: =[0x000A-0x4000], default: 0x000A"}, + {"reports_disabled", "disable reports, usage: =[0-1], default: 0"}, + {NULL, NULL} +}; +#endif + +static const struct shell_cmd_help sync_transfer_receive_help = { + .summary = "start/stop periodic sync reception with specific parameters", + .usage = NULL, + .params = sync_transfer_receive_params, +}; +#endif + +static int +cmd_sync_terminate(int argc, char **argv) +{ + uint16_t sync_handle; + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + sync_handle = parse_arg_uint16_dflt("sync_handle", 0, &rc); + if (rc != 0) { + console_printf("invalid 'sync_handle' parameter\n"); + return rc; + } + + rc = ble_gap_periodic_adv_sync_terminate(sync_handle); + if (rc) { + console_printf("Failed to terminate sync (%d)\n", rc); + } + + return rc; +} + +#if MYNEWT_VAL(SHELL_CMD_HELP) +static const struct shell_param sync_terminate_params[] = { + {"sync_handle", "usage: =[UINT16], default: 0"}, + {NULL, NULL} +}; + +static const struct shell_cmd_help sync_terminate_help = { + .summary = "terminate periodic sync", + .usage = NULL, + .params = sync_terminate_params, +}; +#endif + +static int +cmd_sync_stats(int argc, char **argv) +{ + uint16_t sync_handle; + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + sync_handle = parse_arg_uint16_dflt("sync_handle", 0, &rc); + if (rc != 0) { + console_printf("invalid 'sync_handle' parameter\n"); + return rc; + } + + btshell_sync_stats(sync_handle); + + return 0; +} + +#if MYNEWT_VAL(SHELL_CMD_HELP) +static const struct shell_param sync_stats_params[] = { + {"sync_handle", "usage: =[UINT16], default: 0"}, + {NULL, NULL} +}; + +static const struct shell_cmd_help sync_stats_help = { + .summary = "show sync stats", + .usage = NULL, + .params = sync_stats_params, +}; +#endif +#endif + +static const struct shell_cmd btshell_commands[] = { +#if MYNEWT_VAL(BLE_EXT_ADV) + { + .sc_cmd = "advertise-configure", + .sc_cmd_func = cmd_advertise_configure, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &advertise_configure_help, +#endif + }, + { + .sc_cmd = "advertise-set-addr", + .sc_cmd_func = cmd_advertise_set_addr, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &advertise_set_addr_help, +#endif + }, + { + .sc_cmd = "advertise-set-adv-data", + .sc_cmd_func = cmd_set_adv_data, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &set_adv_data_help, +#endif + }, + { + .sc_cmd = "advertise-set-scan-rsp", + .sc_cmd_func = cmd_set_scan_rsp, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &set_scan_rsp_help, +#endif + }, + { + .sc_cmd = "advertise-start", + .sc_cmd_func = cmd_advertise_start, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &advertise_start_help, +#endif + }, + { + .sc_cmd = "advertise-stop", + .sc_cmd_func = cmd_advertise_stop, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &advertise_stop_help, +#endif + }, + { + .sc_cmd = "advertise-remove", + .sc_cmd_func = cmd_advertise_remove, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &advertise_remove_help, +#endif + }, +#else + { + .sc_cmd = "advertise", + .sc_cmd_func = cmd_advertise, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &advertise_help, +#endif + }, +#endif + { + .sc_cmd = "connect", + .sc_cmd_func = cmd_connect, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &connect_help, +#endif + }, + { + .sc_cmd = "disconnect", + .sc_cmd_func = cmd_disconnect, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &disconnect_help, +#endif + }, + { + .sc_cmd = "show-addr", + .sc_cmd_func = cmd_show_addr, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &gatt_show_addr_help, +#endif + }, + { + .sc_cmd = "show-conn", + .sc_cmd_func = cmd_show_conn, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &gatt_show_conn_help, +#endif + }, + { + .sc_cmd = "set-scan-opts", + .sc_cmd_func = cmd_set_scan_opts, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &set_scan_opts_help, +#endif + }, + { + .sc_cmd = "scan", + .sc_cmd_func = cmd_scan, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &scan_help, +#endif + }, + { + .sc_cmd = "set", + .sc_cmd_func = cmd_set, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &set_help, +#endif + }, +#if !MYNEWT_VAL(BLE_EXT_ADV) + { + .sc_cmd = "set-adv-data", + .sc_cmd_func = cmd_set_adv_data, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &set_adv_data_help, +#endif + }, + { + .sc_cmd = "set-scan-rsp", + .sc_cmd_func = cmd_set_scan_rsp, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &set_scan_rsp_help, +#endif + }, +#endif + { + .sc_cmd = "set-priv-mode", + .sc_cmd_func = cmd_set_priv_mode, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &set_priv_mode_help, +#endif + }, + { + .sc_cmd = "white-list", + .sc_cmd_func = cmd_white_list, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &white_list_help, +#endif + }, + { + .sc_cmd = "conn-rssi", + .sc_cmd_func = cmd_conn_rssi, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &conn_rssi_help, +#endif + }, + { + .sc_cmd = "conn-update-params", + .sc_cmd_func = cmd_conn_update_params, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &conn_update_params_help, +#endif + }, + { + .sc_cmd = "conn-datalen", + .sc_cmd_func = cmd_conn_datalen, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &conn_datalen_help, +#endif + }, + { + .sc_cmd = "gatt-discover-characteristic", + .sc_cmd_func = cmd_gatt_discover_characteristic, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &gatt_discover_characteristic_help, +#endif + }, + { + .sc_cmd = "gatt-discover-descriptor", + .sc_cmd_func = cmd_gatt_discover_descriptor, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &gatt_discover_descriptor_help, +#endif + }, + { + .sc_cmd = "gatt-discover-service", + .sc_cmd_func = cmd_gatt_discover_service, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &gatt_discover_service_help, +#endif + }, + { + .sc_cmd = "gatt-discover-full", + .sc_cmd_func = cmd_gatt_discover_full, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &gatt_discover_full_help, +#endif + }, + { + .sc_cmd = "gatt-find-included-services", + .sc_cmd_func = cmd_gatt_find_included_services, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &gatt_find_included_services_help, +#endif + }, + { + .sc_cmd = "gatt-exchange-mtu", + .sc_cmd_func = cmd_gatt_exchange_mtu, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &gatt_exchange_mtu_help, +#endif + }, + { + .sc_cmd = "gatt-read", + .sc_cmd_func = cmd_gatt_read, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &gatt_read_help, +#endif + }, + { + .sc_cmd = "gatt-notify", + .sc_cmd_func = cmd_gatt_notify, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &gatt_notify_help, +#endif + }, + { + .sc_cmd = "gatt-service-changed", + .sc_cmd_func = cmd_gatt_service_changed, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &gatt_service_changed_help, +#endif + }, + { + .sc_cmd = "gatt-service-visibility", + .sc_cmd_func = cmd_gatt_service_visibility, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &gatt_service_visibility_help, +#endif + }, + { + .sc_cmd = "gatt-show", + .sc_cmd_func = cmd_gatt_show, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &gatt_show_help, +#endif + }, + { + .sc_cmd = "gatt-show-local", + .sc_cmd_func = cmd_gatt_show_local, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &gatt_show_local_help, +#endif + }, + { + .sc_cmd = "gatt-write", + .sc_cmd_func = cmd_gatt_write, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &gatt_write_help, +#endif + }, +#if MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM) + { + .sc_cmd = "l2cap-update", + .sc_cmd_func = cmd_l2cap_update, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &l2cap_update_help, +#endif + }, + { + .sc_cmd = "l2cap-create-server", + .sc_cmd_func = cmd_l2cap_create_server, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &l2cap_create_server_help, +#endif + }, + { + .sc_cmd = "l2cap-connect", + .sc_cmd_func = cmd_l2cap_connect, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &l2cap_connect_help, +#endif + }, + { + .sc_cmd = "l2cap-reconfig", + .sc_cmd_func = cmd_l2cap_reconfig, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &l2cap_reconfig_help, +#endif + }, + { + .sc_cmd = "l2cap-disconnect", + .sc_cmd_func = cmd_l2cap_disconnect, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &l2cap_disconnect_help, +#endif + }, + { + .sc_cmd = "l2cap-send", + .sc_cmd_func = cmd_l2cap_send, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &l2cap_send_help, +#endif + }, + { + .sc_cmd = "l2cap-show-coc", + .sc_cmd_func = cmd_l2cap_show_coc, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &l2cap_show_coc_help, +#endif + }, +#endif + { + .sc_cmd = "keystore-add", + .sc_cmd_func = cmd_keystore_add, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &keystore_add_help, +#endif + }, + { + .sc_cmd = "keystore-del", + .sc_cmd_func = cmd_keystore_del, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &keystore_del_help, +#endif + }, + { + .sc_cmd = "keystore-show", + .sc_cmd_func = cmd_keystore_show, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &keystore_show_help, +#endif + }, +#if NIMBLE_BLE_SM + { + .sc_cmd = "show-oob-sc", + .sc_cmd_func = cmd_show_oob_sc, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = NULL, +#endif + }, + { + .sc_cmd = "auth-passkey", + .sc_cmd_func = cmd_auth_passkey, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &auth_passkey_help, +#endif + }, + { + .sc_cmd = "security-pair", + .sc_cmd_func = cmd_security_pair, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &security_pair_help, +#endif + }, + { + .sc_cmd = "security-unpair", + .sc_cmd_func = cmd_security_unpair, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &security_unpair_help, +#endif + }, + { + .sc_cmd = "security-start", + .sc_cmd_func = cmd_security_start, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &security_start_help, +#endif + }, + { + .sc_cmd = "security-encryption", + .sc_cmd_func = cmd_security_encryption, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &security_encryption_help, +#endif + }, + { + .sc_cmd = "security-set-data", + .sc_cmd_func = cmd_security_set_data, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &security_set_data_help, +#endif + }, +#endif + { + .sc_cmd = "test-tx", + .sc_cmd_func = cmd_test_tx, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &test_tx_help, +#endif + }, + { + .sc_cmd = "phy-set", + .sc_cmd_func = cmd_phy_set, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &phy_set_help, +#endif + }, + { + .sc_cmd = "phy-set-default", + .sc_cmd_func = cmd_phy_set_default, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &phy_set_default_help, +#endif + }, + { + .sc_cmd = "phy-read", + .sc_cmd_func = cmd_phy_read, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &phy_read_help, +#endif + }, + { + .sc_cmd = "host-enable", + .sc_cmd_func = cmd_host_enable, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &host_enable_help, +#endif + }, + { + .sc_cmd = "host-disable", + .sc_cmd_func = cmd_host_disable, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &host_disable_help, +#endif + }, +#if MYNEWT_VAL(BLE_PERIODIC_ADV) + { + .sc_cmd = "periodic-configure", + .sc_cmd_func = cmd_periodic_configure, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &periodic_configure_help, +#endif + }, + { + .sc_cmd = "periodic-set-adv-data", + .sc_cmd_func = cmd_periodic_set_adv_data, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &set_adv_data_help, +#endif + }, + { + .sc_cmd = "periodic-start", + .sc_cmd_func = cmd_periodic_start, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &periodic_start_help, +#endif + }, + { + .sc_cmd = "periodic-stop", + .sc_cmd_func = cmd_periodic_stop, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &periodic_stop_help, +#endif + }, + { + .sc_cmd = "sync-create", + .sc_cmd_func = cmd_sync_create, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &sync_create_help, +#endif + }, + { + .sc_cmd = "sync-terminate", + .sc_cmd_func = cmd_sync_terminate, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &sync_terminate_help, +#endif + }, + { + .sc_cmd = "sync-stats", + .sc_cmd_func = cmd_sync_stats, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &sync_stats_help, +#endif + }, +#if MYNEWT_VAL(BLE_PERIODIC_ADV_SYNC_TRANSFER) + { + .sc_cmd = "sync-transfer", + .sc_cmd_func = cmd_sync_transfer, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &sync_transfer_help, +#endif + }, + { + .sc_cmd = "sync-transfer-set-info", + .sc_cmd_func = cmd_sync_transfer_set_info, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &sync_transfer_set_info_help, +#endif + }, + { + .sc_cmd = "sync-transfer-receive", + .sc_cmd_func = cmd_sync_transfer_receive, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &sync_transfer_receive_help, +#endif + }, + { + .sc_cmd = "sync-reporting", + .sc_cmd_func = cmd_sync_reporting, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &sync_reporting_help, +#endif + }, +#endif +#endif + { 0 }, +}; + + +void +cmd_init(void) +{ + shell_register(BTSHELL_MODULE, btshell_commands); + shell_register_default_module(BTSHELL_MODULE); +} diff --git a/lib/bt/host/nimble/nimble/apps/btshell/src/cmd.h b/lib/bt/host/nimble/nimble/apps/btshell/src/cmd.h new file mode 100644 index 00000000..63bd50d1 --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/btshell/src/cmd.h @@ -0,0 +1,68 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef CMD_H +#define CMD_H + +#include +#include "host/ble_uuid.h" + +struct kv_pair { + char *key; + int val; +}; + +uint32_t parse_arg_time_dflt(char *name, int step, uint32_t dflt, int *out_status); +const struct kv_pair *parse_kv_find(const struct kv_pair *kvs, char *name); +int parse_arg_find_idx(const char *key); +char *parse_arg_extract(const char *key); +long parse_arg_long_bounds(char *name, long min, long max, int *out_status); +long parse_arg_long_bounds_dflt(char *name, long min, long max, + long dflt, int *out_status); +uint64_t parse_arg_uint64_bounds(char *name, uint64_t min, + uint64_t max, int *out_status); +long parse_arg_long(char *name, int *staus); +uint8_t parse_arg_bool(char *name, int *status); +uint8_t parse_arg_bool_dflt(char *name, uint8_t dflt, int *out_status); +uint8_t parse_arg_uint8(char *name, int *status); +uint8_t parse_arg_uint8_dflt(char *name, uint8_t dflt, int *out_status); +uint16_t parse_arg_uint16(char *name, int *status); +uint16_t parse_arg_uint16_dflt(char *name, uint16_t dflt, int *out_status); +uint32_t parse_arg_uint32(char *name, int *out_status); +uint32_t parse_arg_uint32_dflt(char *name, uint32_t dflt, int *out_status); +uint64_t parse_arg_uint64(char *name, int *out_status); +int parse_arg_kv(char *name, const struct kv_pair *kvs, int *out_status); +int parse_arg_kv_dflt(char *name, const struct kv_pair *kvs, int def_val, + int *out_status); +int parse_arg_byte_stream(char *name, int max_len, uint8_t *dst, int *out_len); +int parse_arg_uint8_list_with_separator(char *name, char *separator, int max_len, + uint8_t *dst, int *out_len); +int parse_arg_byte_stream_exact_length(char *name, uint8_t *dst, int len); +int parse_arg_mac(char *name, uint8_t *dst); +int parse_arg_addr(char *name, ble_addr_t *addr); +int parse_arg_uuid(char *name, ble_uuid_any_t *uuid); +int parse_arg_all(int argc, char **argv); +int parse_eddystone_url(char *full_url, uint8_t *out_scheme, char *out_body, + uint8_t *out_body_len, uint8_t *out_suffix); +int cmd_parse_conn_start_end(uint16_t *out_conn, uint16_t *out_start, + uint16_t *out_end); + +void cmd_init(void); + +#endif diff --git a/lib/bt/host/nimble/nimble/apps/btshell/src/cmd_gatt.c b/lib/bt/host/nimble/nimble/apps/btshell/src/cmd_gatt.c new file mode 100644 index 00000000..ba3799e5 --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/btshell/src/cmd_gatt.c @@ -0,0 +1,587 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#include + +#include "bsp/bsp.h" +#include "host/ble_hs_mbuf.h" +#include "host/ble_gap.h" +#include "services/gatt/ble_svc_gatt.h" +#include "console/console.h" +#include "btshell.h" +#include "cmd.h" +#include "cmd_gatt.h" + +#define CMD_BUF_SZ 256 +static bssnz_t uint8_t cmd_buf[CMD_BUF_SZ]; + +/***************************************************************************** + * $gatt-discover * + *****************************************************************************/ + +int +cmd_gatt_discover_characteristic(int argc, char **argv) +{ + uint16_t start_handle; + uint16_t conn_handle; + uint16_t end_handle; + ble_uuid_any_t uuid; + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + rc = cmd_parse_conn_start_end(&conn_handle, &start_handle, &end_handle); + if (rc != 0) { + console_printf("invalid 'conn start end' parameter\n"); + return rc; + } + + rc = parse_arg_uuid("uuid", &uuid); + if (rc == 0) { + rc = btshell_disc_chrs_by_uuid(conn_handle, start_handle, end_handle, + &uuid.u); + } else if (rc == ENOENT) { + rc = btshell_disc_all_chrs(conn_handle, start_handle, end_handle); + } else { + console_printf("invalid 'uuid' parameter\n"); + return rc; + } + if (rc != 0) { + console_printf("error discovering characteristics; rc=%d\n", rc); + return rc; + } + + return 0; +} + +int +cmd_gatt_discover_descriptor(int argc, char **argv) +{ + uint16_t start_handle; + uint16_t conn_handle; + uint16_t end_handle; + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + rc = cmd_parse_conn_start_end(&conn_handle, &start_handle, &end_handle); + if (rc != 0) { + console_printf("invalid 'conn start end' parameter\n"); + return rc; + } + + rc = btshell_disc_all_dscs(conn_handle, start_handle, end_handle); + if (rc != 0) { + console_printf("error discovering descriptors; rc=%d\n", rc); + return rc; + } + + return 0; +} + +int +cmd_gatt_discover_service(int argc, char **argv) +{ + ble_uuid_any_t uuid; + int conn_handle; + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + conn_handle = parse_arg_uint16("conn", &rc); + if (rc != 0) { + console_printf("invalid 'conn' parameter\n"); + return rc; + } + + rc = parse_arg_uuid("uuid", &uuid); + if (rc == 0) { + rc = btshell_disc_svc_by_uuid(conn_handle, &uuid.u); + } else if (rc == ENOENT) { + rc = btshell_disc_svcs(conn_handle); + } else { + console_printf("invalid 'uuid' parameter\n"); + return rc; + } + + if (rc != 0) { + console_printf("error discovering services; rc=%d\n", rc); + return rc; + } + + return 0; +} + +int +cmd_gatt_discover_full(int argc, char **argv) +{ + int conn_handle; + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + conn_handle = parse_arg_uint16("conn", &rc); + if (rc != 0) { + console_printf("invalid 'conn' parameter\n"); + return rc; + } + + rc = btshell_disc_full(conn_handle); + if (rc != 0) { + console_printf("error discovering all; rc=%d\n", rc); + return rc; + } + + return 0; +} + +/***************************************************************************** + * $gatt-exchange-mtu * + *****************************************************************************/ + +int +cmd_gatt_exchange_mtu(int argc, char **argv) +{ + uint16_t conn_handle; + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + conn_handle = parse_arg_uint16("conn", &rc); + if (rc != 0) { + console_printf("invalid 'conn' parameter\n"); + return rc; + } + + rc = btshell_exchange_mtu(conn_handle); + if (rc != 0) { + console_printf("error exchanging mtu; rc=%d\n", rc); + return rc; + } + + return 0; +} + +/***************************************************************************** + * $gatt-notify * + *****************************************************************************/ + +int +cmd_gatt_notify(int argc, char **argv) +{ + uint16_t attr_handle; + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + attr_handle = parse_arg_uint16("attr", &rc); + if (rc != 0) { + console_printf("invalid 'attr' parameter\n"); + return rc; + } + + btshell_notify(attr_handle); + + return 0; +} + +/***************************************************************************** + * $gatt-read * + *****************************************************************************/ + +#define CMD_READ_MAX_ATTRS 8 + +int +cmd_gatt_read(int argc, char **argv) +{ + static uint16_t attr_handles[CMD_READ_MAX_ATTRS]; + uint16_t conn_handle; + uint16_t start; + uint16_t end; + uint16_t offset; + ble_uuid_any_t uuid; + uint8_t num_attr_handles; + int is_uuid; + int is_long; + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + conn_handle = parse_arg_uint16("conn", &rc); + if (rc != 0) { + console_printf("invalid 'conn' parameter\n"); + return rc; + } + + is_long = parse_arg_long("long", &rc); + if (rc == ENOENT) { + is_long = 0; + } else if (rc != 0) { + console_printf("invalid 'long' parameter\n"); + return rc; + } + + for (num_attr_handles = 0; + num_attr_handles < CMD_READ_MAX_ATTRS; + num_attr_handles++) { + + attr_handles[num_attr_handles] = parse_arg_uint16("attr", &rc); + if (rc == ENOENT) { + break; + } else if (rc != 0) { + console_printf("invalid 'attr' parameter\n"); + return rc; + } + } + + rc = parse_arg_uuid("uuid", &uuid); + if (rc == ENOENT) { + is_uuid = 0; + } else if (rc == 0) { + is_uuid = 1; + } else { + console_printf("invalid 'uuid' parameter\n"); + return rc; + } + + start = parse_arg_uint16("start", &rc); + if (rc == ENOENT) { + start = 0; + } else if (rc != 0) { + console_printf("invalid 'start' parameter\n"); + return rc; + } + + end = parse_arg_uint16("end", &rc); + if (rc == ENOENT) { + end = 0; + } else if (rc != 0) { + console_printf("invalid 'end' parameter\n"); + return rc; + } + + offset = parse_arg_uint16("offset", &rc); + if (rc == ENOENT) { + offset = 0; + } else if (rc != 0) { + console_printf("invalid 'offset' parameter\n"); + return rc; + } + + if (num_attr_handles == 1) { + if (is_long) { + rc = btshell_read_long(conn_handle, attr_handles[0], offset); + } else { + rc = btshell_read(conn_handle, attr_handles[0]); + } + } else if (num_attr_handles > 1) { + rc = btshell_read_mult(conn_handle, attr_handles, num_attr_handles); + } else if (is_uuid) { + if (start == 0 || end == 0) { + rc = EINVAL; + } else { + rc = btshell_read_by_uuid(conn_handle, start, end, &uuid.u); + } + } else { + rc = EINVAL; + } + + if (rc != 0) { + console_printf("error reading characteristic; rc=%d\n", rc); + return rc; + } + + return 0; +} + + +/***************************************************************************** + * $gatt-service-changed * + *****************************************************************************/ + +int +cmd_gatt_service_changed(int argc, char **argv) +{ + uint16_t start; + uint16_t end; + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + start = parse_arg_uint16("start", &rc); + if (rc != 0) { + console_printf("invalid 'start' parameter\n"); + return rc; + } + + end = parse_arg_uint16("end", &rc); + if (rc != 0) { + console_printf("invalid 'end' parameter\n"); + return rc; + } + + ble_svc_gatt_changed(start, end); + + return 0; +} + +/***************************************************************************** + * $gatt-service-visibility * + *****************************************************************************/ + +int +cmd_gatt_service_visibility(int argc, char **argv) +{ + uint16_t handle; + bool vis; + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + handle = parse_arg_uint16("handle", &rc); + if (rc != 0) { + console_printf("invalid 'handle' parameter\n"); + return rc; + } + + vis = parse_arg_bool("visibility", &rc); + if (rc != 0) { + console_printf("invalid 'visibility' parameter\n"); + return rc; + } + + ble_gatts_svc_set_visibility(handle, vis); + + return 0; +} + +/***************************************************************************** + * $gatt-find-included-services * + *****************************************************************************/ + +int +cmd_gatt_find_included_services(int argc, char **argv) +{ + uint16_t start_handle; + uint16_t conn_handle; + uint16_t end_handle; + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + rc = cmd_parse_conn_start_end(&conn_handle, &start_handle, &end_handle); + if (rc != 0) { + console_printf("invalid 'conn start end' parameter\n"); + return rc; + } + + rc = btshell_find_inc_svcs(conn_handle, start_handle, end_handle); + if (rc != 0) { + console_printf("error finding included services; rc=%d\n", rc); + return rc; + } + + return 0; +} + +/***************************************************************************** + * $gatt-show * + *****************************************************************************/ + +int +cmd_gatt_show(int argc, char **argv) +{ + struct btshell_conn *conn; + struct btshell_svc *svc; + int i; + + for (i = 0; i < btshell_num_conns; i++) { + conn = btshell_conns + i; + + console_printf("CONNECTION: handle=%d\n", conn->handle); + + SLIST_FOREACH(svc, &conn->svcs, next) { + print_svc(svc); + } + } + + return 0; +} + +int +cmd_gatt_show_local(int argc, char **argv) +{ + gatt_svr_print_svcs(); + return 0; +} + +/***************************************************************************** + * $gatt-write * + *****************************************************************************/ + +int +cmd_gatt_write(int argc, char **argv) +{ + struct ble_gatt_attr attrs[MYNEWT_VAL(BLE_GATT_WRITE_MAX_ATTRS)] = { { 0 } }; + uint16_t attr_handle; + uint16_t conn_handle; + uint16_t offset; + int total_attr_len; + int num_attrs; + int attr_len; + int is_long; + int no_rsp; + int rc; + int i; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + conn_handle = parse_arg_uint16("conn", &rc); + if (rc != 0) { + console_printf("invalid 'conn' parameter\n"); + return rc; + } + + no_rsp = parse_arg_bool_dflt("no_rsp", 0, &rc); + if (rc != 0) { + console_printf("invalid 'no_rsp' parameter\n"); + return rc; + } + + is_long = parse_arg_bool_dflt("long", 0, &rc); + if (rc != 0) { + console_printf("invalid 'long' parameter\n"); + return rc; + } + + total_attr_len = 0; + num_attrs = 0; + while (1) { + attr_handle = parse_arg_uint16("attr", &rc); + if (rc == ENOENT) { + break; + } else if (rc != 0) { + rc = -rc; + console_printf("invalid 'attr' parameter\n"); + goto done; + } + + rc = parse_arg_byte_stream("value", sizeof cmd_buf - total_attr_len, + cmd_buf + total_attr_len, &attr_len); + if (rc == ENOENT) { + break; + } else if (rc != 0) { + console_printf("invalid 'value' parameter\n"); + goto done; + } + + offset = parse_arg_uint16("offset", &rc); + if (rc == ENOENT) { + offset = 0; + } else if (rc != 0) { + console_printf("invalid 'offset' parameter\n"); + return rc; + } + + if (num_attrs >= sizeof attrs / sizeof attrs[0]) { + rc = -EINVAL; + goto done; + } + + attrs[num_attrs].handle = attr_handle; + attrs[num_attrs].offset = offset; + attrs[num_attrs].om = ble_hs_mbuf_from_flat(cmd_buf + total_attr_len, + attr_len); + if (attrs[num_attrs].om == NULL) { + goto done; + } + + total_attr_len += attr_len; + num_attrs++; + } + + if (no_rsp) { + if (num_attrs != 1) { + rc = -EINVAL; + goto done; + } + rc = btshell_write_no_rsp(conn_handle, attrs[0].handle, attrs[0].om); + attrs[0].om = NULL; + } else if (is_long) { + if (num_attrs != 1) { + rc = -EINVAL; + goto done; + } + rc = btshell_write_long(conn_handle, attrs[0].handle, + attrs[0].offset, attrs[0].om); + attrs[0].om = NULL; + } else if (num_attrs > 1) { + rc = btshell_write_reliable(conn_handle, attrs, num_attrs); + } else if (num_attrs == 1) { + rc = btshell_write(conn_handle, attrs[0].handle, attrs[0].om); + attrs[0].om = NULL; + } else { + rc = -EINVAL; + } + +done: + for (i = 0; i < sizeof attrs / sizeof attrs[0]; i++) { + os_mbuf_free_chain(attrs[i].om); + } + + if (rc != 0) { + console_printf("error writing characteristic; rc=%d\n", rc); + } + + return rc; +} diff --git a/lib/bt/host/nimble/nimble/apps/btshell/src/cmd_gatt.h b/lib/bt/host/nimble/nimble/apps/btshell/src/cmd_gatt.h new file mode 100644 index 00000000..70536d03 --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/btshell/src/cmd_gatt.h @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef CMD_GATT_H +#define CMD_GATT_H + +#include "cmd.h" + +int cmd_gatt_discover_characteristic(int argc, char **argv); +int cmd_gatt_discover_descriptor(int argc, char **argv); +int cmd_gatt_discover_service(int argc, char **argv); +int cmd_gatt_discover_full(int argc, char **argv); +int cmd_gatt_find_included_services(int argc, char **argv); +int cmd_gatt_exchange_mtu(int argc, char **argv); +int cmd_gatt_notify(int argc, char **argv); +int cmd_gatt_read(int argc, char **argv); +int cmd_gatt_service_changed(int argc, char **argv); +int cmd_gatt_service_visibility(int argc, char **argv); +int cmd_gatt_show(int argc, char **argv); +int cmd_gatt_show_local(int argc, char **argv); +int cmd_gatt_write(int argc, char **argv); + +#endif diff --git a/lib/bt/host/nimble/nimble/apps/btshell/src/cmd_l2cap.c b/lib/bt/host/nimble/nimble/apps/btshell/src/cmd_l2cap.c new file mode 100644 index 00000000..e74e3bf3 --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/btshell/src/cmd_l2cap.c @@ -0,0 +1,325 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#include + +#include "syscfg/syscfg.h" +#include "host/ble_gap.h" +#include "host/ble_l2cap.h" +#include "console/console.h" +#include "btshell.h" +#include "cmd.h" +#include "cmd_l2cap.h" + + +/***************************************************************************** + * $l2cap-update * + *****************************************************************************/ + +int +cmd_l2cap_update(int argc, char **argv) +{ + struct ble_l2cap_sig_update_params params; + uint16_t conn_handle; + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + conn_handle = parse_arg_uint16("conn", &rc); + if (rc != 0) { + console_printf("invalid 'conn' parameter\n"); + return rc; + } + + params.itvl_min = parse_arg_uint16_dflt("interval_min", + BLE_GAP_INITIAL_CONN_ITVL_MIN, + &rc); + if (rc != 0) { + console_printf("invalid 'interval_min' parameter\n"); + return rc; + } + + params.itvl_max = parse_arg_uint16_dflt("interval_max", + BLE_GAP_INITIAL_CONN_ITVL_MAX, + &rc); + if (rc != 0) { + console_printf("invalid 'interval_max' parameter\n"); + return rc; + } + + params.slave_latency = parse_arg_uint16_dflt("latency", 0, &rc); + if (rc != 0) { + console_printf("invalid 'latency' parameter\n"); + return rc; + } + + params.timeout_multiplier = parse_arg_uint16_dflt("timeout", 0x0100, &rc); + if (rc != 0) { + console_printf("invalid 'timeout' parameter\n"); + return rc; + } + + rc = btshell_l2cap_update(conn_handle, ¶ms); + if (rc != 0) { + console_printf("error txing l2cap update; rc=%d\n", rc); + return rc; + } + + return 0; +} + +/***************************************************************************** + * $l2cap-create-server * + *****************************************************************************/ + +int +cmd_l2cap_create_server(int argc, char **argv) +{ + uint16_t psm = 0; + uint16_t mtu; + int error; + int accept_response = 0; + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + psm = parse_arg_uint16("psm", &rc); + if (rc != 0) { + console_printf("invalid 'psm' parameter\n"); + return rc; + } + + error = parse_arg_uint32_dflt("error", 0, &rc); + if (rc != 0) { + console_printf("invalid 'error' parameter\n"); + return rc; + } + + mtu = parse_arg_uint16_dflt("mtu", 0, &rc); + if (rc != 0) { + console_printf("invalid 'mtu' parameter\n"); + return rc; + } + + switch (error) { + case 1: + accept_response = BLE_HS_EAUTHEN; + break; + case 2: + accept_response = BLE_HS_EAUTHOR; + break; + case 3: + accept_response = BLE_HS_EENCRYPT_KEY_SZ; + break; + } + + rc = btshell_l2cap_create_srv(psm, mtu, accept_response); + if (rc) { + console_printf("Server create error: 0x%02x\n", rc); + return rc; + } + + console_printf("Server created successfully\n"); + return 0; +} + +/***************************************************************************** + * $l2cap-connect * + *****************************************************************************/ + +int +cmd_l2cap_connect(int argc, char **argv) +{ + uint16_t conn = 0; + uint16_t psm = 0; + uint16_t mtu; + uint8_t num; + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + conn = parse_arg_uint16("conn", &rc); + if (rc != 0) { + console_printf("invalid 'conn' parameter\n"); + return rc; + } + + psm = parse_arg_uint16("psm", &rc); + if (rc != 0) { + console_printf("invalid 'psm' parameter\n"); + return rc; + } + + mtu = parse_arg_uint16_dflt("mtu", 0, &rc); + if (rc != 0) { + console_printf("invalid 'mtu' parameter\n"); + return rc; + } + + num = parse_arg_uint8_dflt("num", 1, &rc); + if (rc != 0) { + console_printf("invalid 'num' parameter\n"); + return rc; + } + + return btshell_l2cap_connect(conn, psm, mtu, num); +} + +/***************************************************************************** + * $l2cap-disconnect * + *****************************************************************************/ + +int +cmd_l2cap_disconnect(int argc, char **argv) +{ + uint16_t conn; + uint16_t idx; + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + conn = parse_arg_uint16("conn", &rc); + if (rc != 0) { + console_printf("invalid 'conn' parameter\n"); + return rc; + } + + idx = parse_arg_uint16("idx", &rc); + if (rc != 0) { + console_printf("invalid 'idx' parameter\n"); + return rc; + } + + return btshell_l2cap_disconnect(conn, idx); +} + +/***************************************************************************** + * $l2cap-send * + *****************************************************************************/ + +int +cmd_l2cap_send(int argc, char **argv) +{ + uint16_t conn; + uint16_t idx; + uint16_t bytes; + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + conn = parse_arg_uint16("conn", &rc); + if (rc != 0) { + console_printf("invalid 'conn' parameter\n"); + return rc; + } + + idx = parse_arg_uint16("idx", &rc); + if (rc != 0) { + console_printf("invalid 'idx' parameter\n"); + return rc; + } + + bytes = parse_arg_uint16("bytes", &rc); + if (rc != 0) { + console_printf("invalid 'bytes' parameter\n"); + return rc; + } + + return btshell_l2cap_send(conn, idx, bytes); +} + +int +cmd_l2cap_show_coc(int argc, char **argv) +{ + struct btshell_conn *conn = NULL; + struct btshell_l2cap_coc *coc; + int i, j; + + for (i = 0; i < btshell_num_conns; i++) { + conn = btshell_conns + i; + + if (SLIST_EMPTY(&conn->coc_list)) { + continue; + } + + console_printf("conn_handle: 0x%04x\n", conn->handle); + j = 0; + SLIST_FOREACH(coc, &conn->coc_list, next) { + console_printf(" idx: %i, chan pointer = %p\n", j++, coc->chan); + } + } + + return 0; +} + +int +cmd_l2cap_reconfig(int argc, char **argv) +{ +#if MYNEWT_VAL(BLE_L2CAP_ENHANCED_COC) + uint16_t conn; + uint16_t mtu; + uint8_t idxs[5]; + int num; + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + conn = parse_arg_uint16("conn", &rc); + if (rc != 0) { + console_printf("invalid 'conn' parameter\n"); + return rc; + } + + mtu = parse_arg_uint16_dflt("mtu", 0,&rc); + if (rc != 0) { + console_printf("invalid 'mtu' parameter\n"); + return rc; + } + + rc = parse_arg_uint8_list_with_separator("idxs", ",", 5, idxs, &num); + if (rc != 0) { + console_printf("invalid 'idxs' parameter\n"); + return rc; + } + + return btshell_l2cap_reconfig(conn, mtu, num, idxs); +#else + console_printf("To enable this features set BLE_L2CAP_ENHANCED_COC\n"); + return ENOTSUP; +#endif +} diff --git a/lib/bt/host/nimble/nimble/apps/btshell/src/cmd_l2cap.h b/lib/bt/host/nimble/nimble/apps/btshell/src/cmd_l2cap.h new file mode 100644 index 00000000..d366fe26 --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/btshell/src/cmd_l2cap.h @@ -0,0 +1,33 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef CMD_L2CAP_H +#define CMD_L2CAP_H + +#include "cmd.h" + +int cmd_l2cap_update(int argc, char **argv); +int cmd_l2cap_create_server(int argc, char **argv); +int cmd_l2cap_connect(int argc, char **argv); +int cmd_l2cap_disconnect(int argc, char **argv); +int cmd_l2cap_send(int argc, char **argv); +int cmd_l2cap_show_coc(int argc, char **argv); +int cmd_l2cap_reconfig(int argc, char **argv); + +#endif diff --git a/lib/bt/host/nimble/nimble/apps/btshell/src/gatt_svr.c b/lib/bt/host/nimble/nimble/apps/btshell/src/gatt_svr.c new file mode 100644 index 00000000..dc025ca1 --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/btshell/src/gatt_svr.c @@ -0,0 +1,646 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#include +#include "bsp/bsp.h" +#include "console/console.h" +#include "host/ble_hs.h" +#include "host/ble_uuid.h" +#include "host/ble_gatt.h" +#include "btshell.h" + +/* 0000xxxx-8c26-476f-89a7-a108033a69c7 */ +#define PTS_UUID_DECLARE(uuid16) \ + ((const ble_uuid_t *) (&(ble_uuid128_t) BLE_UUID128_INIT( \ + 0xc7, 0x69, 0x3a, 0x03, 0x08, 0xa1, 0xa7, 0x89, \ + 0x6f, 0x47, 0x26, 0x8c, uuid16, uuid16 >> 8, 0x00, 0x00 \ + ))) + +#define PTS_SVC 0x0001 +#define PTS_CHR_READ 0x0002 +#define PTS_CHR_WRITE 0x0003 +#define PTS_CHR_RELIABLE_WRITE 0x0004 +#define PTS_CHR_WRITE_NO_RSP 0x0005 +#define PTS_CHR_READ_WRITE 0x0006 +#define PTS_CHR_READ_WRITE_ENC 0x0007 +#define PTS_CHR_READ_WRITE_AUTHEN 0x0008 +#define PTS_DSC_READ 0x0009 +#define PTS_DSC_WRITE 0x000a +#define PTS_DSC_READ_WRITE 0x000b +#define PTS_DSC_READ_WRITE_ENC 0x000c +#define PTS_DSC_READ_WRITE_AUTHEN 0x000d + +#define PTS_LONG_SVC 0x0011 +#define PTS_LONG_CHR_READ 0x0012 +#define PTS_LONG_CHR_WRITE 0x0013 +#define PTS_LONG_CHR_RELIABLE_WRITE 0x0014 +#define PTS_LONG_CHR_READ_WRITE 0x0015 +#define PTS_LONG_CHR_READ_WRITE_ALT 0x0016 +#define PTS_LONG_CHR_READ_WRITE_ENC 0x0017 +#define PTS_LONG_CHR_READ_WRITE_AUTHEN 0x0018 +#define PTS_LONG_DSC_READ 0x0019 +#define PTS_LONG_DSC_WRITE 0x001a +#define PTS_LONG_DSC_READ_WRITE 0x001b +#define PTS_LONG_DSC_READ_WRITE_ENC 0x001c +#define PTS_LONG_DSC_READ_WRITE_AUTHEN 0x001d + +#define PTS_INC_SVC 0x001e +#define PTS_CHR_READ_WRITE_ALT 0x001f + +/** + * The vendor specific security test service consists of two characteristics: + * o random-number-generator: generates a random 32-bit number each time + * it is read. This characteristic can only be read over an encrypted + * connection. + * o static-value: a single-byte characteristic that can always be read, + * but can only be written over an encrypted connection. + */ + +/* 59462f12-9543-9999-12c8-58b459a2712d */ +static const ble_uuid128_t gatt_svr_svc_sec_test_uuid = + BLE_UUID128_INIT(0x2d, 0x71, 0xa2, 0x59, 0xb4, 0x58, 0xc8, 0x12, + 0x99, 0x99, 0x43, 0x95, 0x12, 0x2f, 0x46, 0x59); + +/* 5c3a659e-897e-45e1-b016-007107c96df6 */ +static const ble_uuid128_t gatt_svr_chr_sec_test_rand_uuid = + BLE_UUID128_INIT(0xf6, 0x6d, 0xc9, 0x07, 0x71, 0x00, 0x16, 0xb0, + 0xe1, 0x45, 0x7e, 0x89, 0x9e, 0x65, 0x3a, 0x5c); + +/* 5c3a659e-897e-45e1-b016-007107c96df7 */ +static const ble_uuid128_t gatt_svr_chr_sec_test_static_uuid = + BLE_UUID128_INIT(0xf7, 0x6d, 0xc9, 0x07, 0x71, 0x00, 0x16, 0xb0, + 0xe1, 0x45, 0x7e, 0x89, 0x9e, 0x65, 0x3a, 0x5c); + +/* 5c3a659e-897e-45e1-b016-007107c96df8 */ +static const ble_uuid128_t gatt_svr_chr_sec_test_static_auth_uuid = + BLE_UUID128_INIT(0xf8, 0x6d, 0xc9, 0x07, 0x71, 0x00, 0x16, 0xb0, + 0xe1, 0x45, 0x7e, 0x89, 0x9e, 0x65, 0x3a, 0x5c); + +static uint8_t gatt_svr_sec_test_static_val; + +static uint8_t gatt_svr_pts_static_val; +static uint8_t gatt_svr_pts_static_long_val[30]; +static uint8_t gatt_svr_pts_static_long_val_alt[30]; + +static int +gatt_svr_chr_access_sec_test(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, + void *arg); + +static int +gatt_svr_access_test(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, + void *arg); + +static int +gatt_svr_long_access_test(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, + void *arg); + +static const struct ble_gatt_svc_def gatt_svr_svcs[] = { + { + /*** Service: PTS test. */ + .type = BLE_GATT_SVC_TYPE_PRIMARY, + .uuid = PTS_UUID_DECLARE(PTS_SVC), + .characteristics = (struct ble_gatt_chr_def[]) { { + .uuid = PTS_UUID_DECLARE(PTS_CHR_READ), + .access_cb = gatt_svr_access_test, + .flags = BLE_GATT_CHR_F_READ, + }, { + .uuid = PTS_UUID_DECLARE(PTS_CHR_WRITE), + .access_cb = gatt_svr_access_test, + .flags = BLE_GATT_CHR_F_WRITE, + }, { + .uuid = PTS_UUID_DECLARE(PTS_CHR_RELIABLE_WRITE), + .access_cb = gatt_svr_access_test, + .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_RELIABLE_WRITE, + }, { + .uuid = PTS_UUID_DECLARE(PTS_CHR_WRITE_NO_RSP), + .access_cb = gatt_svr_access_test, + .flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_WRITE_NO_RSP, + }, { + .uuid = PTS_UUID_DECLARE(PTS_CHR_READ_WRITE), + .access_cb = gatt_svr_access_test, + .flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_WRITE, + }, { + .uuid = PTS_UUID_DECLARE(PTS_CHR_READ_WRITE_ENC), + .access_cb = gatt_svr_access_test, + .flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_READ_ENC | + BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_WRITE_ENC, + .min_key_size = 16, + }, { + .uuid = PTS_UUID_DECLARE(PTS_CHR_READ_WRITE_AUTHEN), + .access_cb = gatt_svr_access_test, + .flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_READ_AUTHEN | + BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_WRITE_AUTHEN, + + .descriptors = (struct ble_gatt_dsc_def[]){ { + .uuid = PTS_UUID_DECLARE(PTS_DSC_READ), + .access_cb = gatt_svr_access_test, + .att_flags = BLE_ATT_F_READ, + }, { + .uuid = PTS_UUID_DECLARE(PTS_DSC_WRITE), + .access_cb = gatt_svr_access_test, + .att_flags = BLE_ATT_F_WRITE, + }, { + .uuid = PTS_UUID_DECLARE(PTS_DSC_READ_WRITE), + .access_cb = gatt_svr_access_test, + .att_flags = BLE_ATT_F_READ | BLE_ATT_F_WRITE, + }, { + .uuid = PTS_UUID_DECLARE(PTS_DSC_READ_WRITE_ENC), + .access_cb = gatt_svr_access_test, + .att_flags = BLE_ATT_F_READ | BLE_ATT_F_READ_ENC | + BLE_ATT_F_WRITE | BLE_ATT_F_WRITE_ENC, + .min_key_size = 16, + }, { + .uuid = PTS_UUID_DECLARE(PTS_DSC_READ_WRITE_AUTHEN), + .access_cb = gatt_svr_access_test, + .att_flags = BLE_ATT_F_READ | BLE_ATT_F_READ_AUTHEN | + BLE_ATT_F_WRITE | BLE_ATT_F_WRITE_AUTHEN, + }, { + 0, /* No more descriptors in this characteristic. */ + } } + }, { + 0, /* No more characteristics in this service. */ + } }, + }, + + { + /*** Service: PTS long test. */ + .type = BLE_GATT_SVC_TYPE_PRIMARY, + .uuid = PTS_UUID_DECLARE(PTS_LONG_SVC), + .characteristics = (struct ble_gatt_chr_def[]) { { + .uuid = PTS_UUID_DECLARE(PTS_LONG_CHR_READ), + .access_cb = gatt_svr_long_access_test, + .flags = BLE_GATT_CHR_F_READ, + }, { + .uuid = PTS_UUID_DECLARE(PTS_LONG_CHR_WRITE), + .access_cb = gatt_svr_long_access_test, + .flags = BLE_GATT_CHR_F_WRITE, + }, { + .uuid = PTS_UUID_DECLARE(PTS_LONG_CHR_RELIABLE_WRITE), + .access_cb = gatt_svr_long_access_test, + .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_RELIABLE_WRITE, + }, { + .uuid = PTS_UUID_DECLARE(PTS_LONG_CHR_READ_WRITE), + .access_cb = gatt_svr_long_access_test, + .flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_WRITE, + }, { + .uuid = PTS_UUID_DECLARE(PTS_LONG_CHR_READ_WRITE_ALT), + .access_cb = gatt_svr_long_access_test, + .flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_WRITE, + }, { + .uuid = PTS_UUID_DECLARE(PTS_LONG_CHR_READ_WRITE_ENC), + .access_cb = gatt_svr_long_access_test, + .flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_READ_ENC | + BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_WRITE_ENC, + .min_key_size = 16, + }, { + .uuid = PTS_UUID_DECLARE(PTS_LONG_CHR_READ_WRITE_AUTHEN), + .access_cb = gatt_svr_long_access_test, + .flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_READ_AUTHEN | + BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_WRITE_AUTHEN, + + .descriptors = (struct ble_gatt_dsc_def[]){ { + .uuid = PTS_UUID_DECLARE(PTS_LONG_DSC_READ), + .access_cb = gatt_svr_long_access_test, + .att_flags = BLE_ATT_F_READ, + }, { + .uuid = PTS_UUID_DECLARE(PTS_LONG_DSC_WRITE), + .access_cb = gatt_svr_long_access_test, + .att_flags = BLE_ATT_F_WRITE, + }, { + .uuid = PTS_UUID_DECLARE(PTS_LONG_DSC_READ_WRITE), + .access_cb = gatt_svr_long_access_test, + .att_flags = BLE_ATT_F_READ | BLE_ATT_F_WRITE, + }, { + .uuid = PTS_UUID_DECLARE(PTS_LONG_DSC_READ_WRITE_ENC), + .access_cb = gatt_svr_long_access_test, + .att_flags = BLE_ATT_F_READ | BLE_ATT_F_READ_ENC | + BLE_ATT_F_WRITE | BLE_ATT_F_WRITE_ENC, + .min_key_size = 16, + }, { + .uuid = PTS_UUID_DECLARE(PTS_LONG_DSC_READ_WRITE_AUTHEN), + .access_cb = gatt_svr_long_access_test, + .att_flags = BLE_ATT_F_READ | BLE_ATT_F_READ_AUTHEN | + BLE_ATT_F_WRITE | BLE_ATT_F_WRITE_AUTHEN, + }, { + 0, /* No more descriptors in this characteristic. */ + } } + }, { + 0, /* No more characteristics in this service. */ + } }, + }, + + { + /*** Service: Security test. */ + .type = BLE_GATT_SVC_TYPE_PRIMARY, + .uuid = &gatt_svr_svc_sec_test_uuid.u, + .characteristics = (struct ble_gatt_chr_def[]) { { + /*** Characteristic: Random number generator. */ + .uuid = &gatt_svr_chr_sec_test_rand_uuid.u, + .access_cb = gatt_svr_chr_access_sec_test, + .flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_READ_ENC, + }, { + /*** Characteristic: Static value. */ + .uuid = &gatt_svr_chr_sec_test_static_uuid.u, + .access_cb = gatt_svr_chr_access_sec_test, + .flags = BLE_GATT_CHR_F_READ | + BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_WRITE_ENC, + }, { + /*** Characteristic: Static value. */ + .uuid = &gatt_svr_chr_sec_test_static_auth_uuid.u, + .access_cb = gatt_svr_chr_access_sec_test, + .flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_READ_AUTHEN, + }, { + 0, /* No more characteristics in this service. */ + } }, + }, + + { + 0, /* No more services. */ + }, +}; + +static const struct ble_gatt_svc_def *inc_svcs[] = { + &gatt_svr_svcs[0], + NULL, +}; + +static const struct ble_gatt_svc_def gatt_svr_inc_svcs[] = { + { + .type = BLE_GATT_SVC_TYPE_PRIMARY, + .uuid = PTS_UUID_DECLARE(PTS_INC_SVC), + .includes = inc_svcs, + .characteristics = (struct ble_gatt_chr_def[]) {{ + .uuid = PTS_UUID_DECLARE(PTS_CHR_READ_WRITE_ALT), + .access_cb = gatt_svr_access_test, + .flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_WRITE, + }, { + 0, /* No more characteristics */ + }, }, + }, + + { + 0, /* No more services. */ + }, +}; + +static int +gatt_svr_chr_write(struct os_mbuf *om, uint16_t min_len, uint16_t max_len, + void *dst, uint16_t *len) +{ + uint16_t om_len; + int rc; + + om_len = OS_MBUF_PKTLEN(om); + if (om_len < min_len || om_len > max_len) { + return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; + } + + rc = ble_hs_mbuf_to_flat(om, dst, max_len, len); + if (rc != 0) { + return BLE_ATT_ERR_UNLIKELY; + } + + return 0; +} + +static int +gatt_svr_chr_access_sec_test(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, + void *arg) +{ + const ble_uuid_t *uuid; + int rand_num; + int rc; + + uuid = ctxt->chr->uuid; + + /* Determine which characteristic is being accessed by examining its + * 128-bit UUID. + */ + + if (ble_uuid_cmp(uuid, &gatt_svr_chr_sec_test_rand_uuid.u) == 0) { + assert(ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR); + + /* Respond with a 32-bit random number. */ + rand_num = rand(); + rc = os_mbuf_append(ctxt->om, &rand_num, sizeof rand_num); + return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; + } + + if (ble_uuid_cmp(uuid, &gatt_svr_chr_sec_test_static_uuid.u) == 0 || + ble_uuid_cmp(uuid, &gatt_svr_chr_sec_test_static_auth_uuid.u) == 0) { + switch (ctxt->op) { + case BLE_GATT_ACCESS_OP_READ_CHR: + rc = os_mbuf_append(ctxt->om, &gatt_svr_sec_test_static_val, + sizeof gatt_svr_sec_test_static_val); + return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; + + case BLE_GATT_ACCESS_OP_WRITE_CHR: + rc = gatt_svr_chr_write(ctxt->om, + sizeof gatt_svr_sec_test_static_val, + sizeof gatt_svr_sec_test_static_val, + &gatt_svr_sec_test_static_val, NULL); + return rc; + + default: + assert(0); + return BLE_ATT_ERR_UNLIKELY; + } + } + + /* Unknown characteristic; the nimble stack should not have called this + * function. + */ + assert(0); + return BLE_ATT_ERR_UNLIKELY; +} + +/* This method is used for PTS testing only, to extract 16 bit value + * from 128 bit vendor specific UUID. + */ +static uint16_t +extract_uuid16_from_pts_uuid128(const ble_uuid_t *uuid) +{ + const uint8_t *u8ptr; + uint16_t uuid16; + + u8ptr = BLE_UUID128(uuid)->value; + uuid16 = u8ptr[12]; + uuid16 |= (uint16_t)u8ptr[13] << 8; + return uuid16; +} + +static int +gatt_svr_access_test(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, + void *arg) +{ + uint16_t uuid16; + int rc; + + uuid16 = extract_uuid16_from_pts_uuid128(ctxt->chr->uuid); + assert(uuid16 != 0); + + switch (uuid16) { + case PTS_CHR_READ: + assert(ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR); + rc = os_mbuf_append(ctxt->om, &gatt_svr_pts_static_val, + sizeof gatt_svr_pts_static_val); + return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; + + case PTS_CHR_WRITE: + case PTS_CHR_RELIABLE_WRITE: + case PTS_CHR_WRITE_NO_RSP: + if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) { + rc = gatt_svr_chr_write(ctxt->om,0, + sizeof gatt_svr_pts_static_val, + &gatt_svr_pts_static_val, NULL); + return rc; + } else if (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR) { + rc = os_mbuf_append(ctxt->om, &gatt_svr_pts_static_val, + sizeof gatt_svr_pts_static_val); + return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; + } + assert(0); + break; + case PTS_CHR_READ_WRITE: + case PTS_CHR_READ_WRITE_ENC: + case PTS_CHR_READ_WRITE_AUTHEN: + case PTS_CHR_READ_WRITE_ALT: + if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) { + rc = gatt_svr_chr_write(ctxt->om,0, + sizeof gatt_svr_pts_static_val, + &gatt_svr_pts_static_val, NULL); + return rc; + } else if (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR) { + rc = os_mbuf_append(ctxt->om, &gatt_svr_pts_static_val, + sizeof gatt_svr_pts_static_val); + return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; + } + assert(0); + break; + case PTS_DSC_READ: + assert(ctxt->op == BLE_GATT_ACCESS_OP_READ_DSC); + rc = os_mbuf_append(ctxt->om, &gatt_svr_pts_static_val, + sizeof gatt_svr_pts_static_val); + return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; + + case PTS_DSC_WRITE: + assert(ctxt->op == BLE_GATT_ACCESS_OP_WRITE_DSC); + rc = gatt_svr_chr_write(ctxt->om,0, + sizeof gatt_svr_pts_static_val, + &gatt_svr_pts_static_val, NULL); + return rc; + + case PTS_DSC_READ_WRITE: + case PTS_DSC_READ_WRITE_ENC: + case PTS_DSC_READ_WRITE_AUTHEN: + if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_DSC) { + rc = gatt_svr_chr_write(ctxt->om,0, + sizeof gatt_svr_pts_static_val, + &gatt_svr_pts_static_val, NULL); + return rc; + } else if (ctxt->op == BLE_GATT_ACCESS_OP_READ_DSC) { + rc = os_mbuf_append(ctxt->om, &gatt_svr_pts_static_val, + sizeof gatt_svr_pts_static_val); + return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; + } + assert(0); + break; + default: + assert(0); + break; + } + + return BLE_ATT_ERR_UNLIKELY; +} + +static int +gatt_svr_long_access_test(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, + void *arg) +{ + uint16_t uuid16; + int rc; + + uuid16 = extract_uuid16_from_pts_uuid128(ctxt->chr->uuid); + assert(uuid16 != 0); + + switch (uuid16) { + case PTS_LONG_CHR_READ: + assert(ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR); + rc = os_mbuf_append(ctxt->om, &gatt_svr_pts_static_long_val, + sizeof gatt_svr_pts_static_long_val); + return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; + + case PTS_LONG_CHR_WRITE: + case PTS_LONG_CHR_RELIABLE_WRITE: + assert(ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR); + rc = gatt_svr_chr_write(ctxt->om,0, + sizeof gatt_svr_pts_static_long_val, + &gatt_svr_pts_static_long_val, NULL); + return rc; + + case PTS_LONG_CHR_READ_WRITE: + if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) { + rc = gatt_svr_chr_write(ctxt->om,0, + sizeof gatt_svr_pts_static_long_val, + &gatt_svr_pts_static_long_val, NULL); + return rc; + } else if (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR) { + rc = os_mbuf_append(ctxt->om, &gatt_svr_pts_static_long_val, + sizeof gatt_svr_pts_static_long_val); + return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; + } + assert(0); + break; + + case PTS_LONG_CHR_READ_WRITE_ALT: + if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) { + rc = gatt_svr_chr_write(ctxt->om,0, + sizeof gatt_svr_pts_static_long_val_alt, + &gatt_svr_pts_static_long_val_alt, NULL); + return rc; + } else if (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR) { + rc = os_mbuf_append(ctxt->om, &gatt_svr_pts_static_long_val_alt, + sizeof gatt_svr_pts_static_long_val_alt); + return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; + } + assert(0); + break; + + case PTS_LONG_CHR_READ_WRITE_ENC: + case PTS_LONG_CHR_READ_WRITE_AUTHEN: + if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) { + rc = gatt_svr_chr_write(ctxt->om,0, + sizeof gatt_svr_pts_static_long_val, + &gatt_svr_pts_static_long_val, NULL); + return rc; + } else if (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR) { + rc = os_mbuf_append(ctxt->om, &gatt_svr_pts_static_long_val, + sizeof gatt_svr_pts_static_long_val); + return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; + } + assert(0); + break; + + case PTS_LONG_DSC_READ: + assert(ctxt->op == BLE_GATT_ACCESS_OP_READ_DSC); + rc = os_mbuf_append(ctxt->om, &gatt_svr_pts_static_long_val, + sizeof gatt_svr_pts_static_long_val); + return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; + + case PTS_LONG_DSC_WRITE: + assert(ctxt->op == BLE_GATT_ACCESS_OP_WRITE_DSC); + rc = gatt_svr_chr_write(ctxt->om,0, + sizeof gatt_svr_pts_static_long_val, + &gatt_svr_pts_static_long_val, NULL); + return rc; + + case PTS_LONG_DSC_READ_WRITE: + case PTS_LONG_DSC_READ_WRITE_ENC: + case PTS_LONG_DSC_READ_WRITE_AUTHEN: + if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_DSC) { + rc = gatt_svr_chr_write(ctxt->om,0, + sizeof gatt_svr_pts_static_long_val, + &gatt_svr_pts_static_long_val, NULL); + return rc; + } else if (ctxt->op == BLE_GATT_ACCESS_OP_READ_DSC) { + rc = os_mbuf_append(ctxt->om, &gatt_svr_pts_static_long_val, + sizeof gatt_svr_pts_static_long_val); + return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; + } + assert(0); + break; + + default: + assert(0); + return BLE_ATT_ERR_UNLIKELY; + } +} + +void +gatt_svr_register_cb(struct ble_gatt_register_ctxt *ctxt, void *arg) +{ + char buf[BLE_UUID_STR_LEN]; + + switch (ctxt->op) { + case BLE_GATT_REGISTER_OP_SVC: + MODLOG_DFLT(DEBUG, "registered service %s with handle=%d\n", + ble_uuid_to_str(ctxt->svc.svc_def->uuid, buf), + ctxt->svc.handle); + break; + + case BLE_GATT_REGISTER_OP_CHR: + MODLOG_DFLT(DEBUG, "registering characteristic %s with " + "def_handle=%d val_handle=%d\n", + ble_uuid_to_str(ctxt->chr.chr_def->uuid, buf), + ctxt->chr.def_handle, + ctxt->chr.val_handle); + break; + + case BLE_GATT_REGISTER_OP_DSC: + MODLOG_DFLT(DEBUG, "registering descriptor %s with handle=%d\n", + ble_uuid_to_str(ctxt->dsc.dsc_def->uuid, buf), + ctxt->dsc.handle); + break; + + default: + assert(0); + break; + } +} + +void +gatt_svr_print_svcs(void) +{ + ble_gatts_show_local(); +} + +int +gatt_svr_init(void) +{ + int rc; + + rc = ble_gatts_count_cfg(gatt_svr_svcs); + if (rc != 0) { + return rc; + } + + rc = ble_gatts_add_svcs(gatt_svr_svcs); + if (rc != 0) { + return rc; + } + + rc = ble_gatts_count_cfg(gatt_svr_inc_svcs); + if (rc != 0) { + return rc; + } + + rc = ble_gatts_add_svcs(gatt_svr_inc_svcs); + if (rc != 0) { + return rc; + } + + return 0; +} diff --git a/lib/bt/host/nimble/nimble/apps/btshell/src/main.c b/lib/bt/host/nimble/nimble/apps/btshell/src/main.c new file mode 100644 index 00000000..cc69e5d1 --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/btshell/src/main.c @@ -0,0 +1,2669 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#include +#include +#include +#include "os/mynewt.h" +#include "bsp/bsp.h" +#include "log/log.h" +#include "stats/stats.h" +#include "hal/hal_gpio.h" +#include "console/console.h" +#include "btshell.h" +#include "cmd.h" + +/* BLE */ +#include "nimble/ble.h" +#include "nimble/nimble_opt.h" +#include "host/ble_hs.h" +#include "host/ble_hs_adv.h" +#include "host/ble_uuid.h" +#include "host/ble_att.h" +#include "host/ble_gap.h" +#include "host/ble_gatt.h" +#include "host/ble_store.h" +#include "host/ble_sm.h" +#include "host/util/util.h" + +/* Mandatory services. */ +#include "services/gap/ble_svc_gap.h" +#include "services/gatt/ble_svc_gatt.h" + +/* XXX: An app should not include private headers from a library. The btshell + * app uses some of nimble's internal details for logging. + */ +#include "../src/ble_hs_conn_priv.h" +#include "../src/ble_hs_atomic_priv.h" +#include "../src/ble_hs_priv.h" + +#if MYNEWT_VAL(BLE_ROLE_CENTRAL) +#define BTSHELL_MAX_SVCS 32 +#define BTSHELL_MAX_CHRS 64 +#define BTSHELL_MAX_DSCS 64 +#else +#define BTSHELL_MAX_SVCS 1 +#define BTSHELL_MAX_CHRS 1 +#define BTSHELL_MAX_DSCS 1 +#endif + +#if MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM) +#define BTSHELL_COC_MTU (256) +/* We use same pool for incoming and outgoing sdu */ +#define BTSHELL_COC_BUF_COUNT (3 * MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM)) + +#define INT_TO_PTR(x) (void *)((intptr_t)(x)) +#define PTR_TO_INT(x) (int) ((intptr_t)(x)) +#endif + +bssnz_t struct btshell_conn btshell_conns[MYNEWT_VAL(BLE_MAX_CONNECTIONS)]; +int btshell_num_conns; + +static os_membuf_t btshell_svc_mem[ + OS_MEMPOOL_SIZE(BTSHELL_MAX_SVCS, sizeof(struct btshell_svc)) +]; +static struct os_mempool btshell_svc_pool; + +static os_membuf_t btshell_chr_mem[ + OS_MEMPOOL_SIZE(BTSHELL_MAX_CHRS, sizeof(struct btshell_chr)) +]; +static struct os_mempool btshell_chr_pool; + +static os_membuf_t btshell_dsc_mem[ + OS_MEMPOOL_SIZE(BTSHELL_MAX_DSCS, sizeof(struct btshell_dsc)) +]; +static struct os_mempool btshell_dsc_pool; + +#if MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM) +static os_membuf_t btshell_coc_conn_mem[ + OS_MEMPOOL_SIZE(MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM), + sizeof(struct btshell_l2cap_coc)) +]; +static struct os_mempool btshell_coc_conn_pool; + +static os_membuf_t btshell_sdu_coc_mem[ + OS_MEMPOOL_SIZE(BTSHELL_COC_BUF_COUNT, BTSHELL_COC_MTU) +]; +struct os_mbuf_pool sdu_os_mbuf_pool; +static struct os_mempool sdu_coc_mbuf_mempool; +#endif + +static struct os_callout btshell_tx_timer; +struct btshell_tx_data_s +{ + uint16_t tx_num; + uint16_t tx_num_requested; + uint16_t tx_rate; + uint16_t tx_conn_handle; + uint16_t tx_len; + struct ble_hs_conn *conn; +}; +static struct btshell_tx_data_s btshell_tx_data; +int btshell_full_disc_prev_chr_val; + +struct ble_sm_sc_oob_data oob_data_local; +struct ble_sm_sc_oob_data oob_data_remote; + +#define XSTR(s) STR(s) +#ifndef STR +#define STR(s) #s +#endif + + +#ifdef DEVICE_NAME +#define BTSHELL_AUTO_DEVICE_NAME XSTR(DEVICE_NAME) +#else +#define BTSHELL_AUTO_DEVICE_NAME "" +#endif + +#if MYNEWT_VAL(BLE_EXT_ADV) +struct { + bool restart; + uint16_t conn_handle; +} ext_adv_restart[BLE_ADV_INSTANCES]; +#endif + +static struct { + bool restart; + uint8_t own_addr_type; + ble_addr_t direct_addr; + int32_t duration_ms; + struct ble_gap_adv_params params; +} adv_params; + +static void +btshell_print_error(char *msg, uint16_t conn_handle, + const struct ble_gatt_error *error) +{ + if (msg == NULL) { + msg = "ERROR"; + } + + console_printf("%s: conn_handle=%d status=%d att_handle=%d\n", + msg, conn_handle, error->status, error->att_handle); +} + +static void +btshell_print_adv_fields(const struct ble_hs_adv_fields *fields) +{ + const uint8_t *u8p; + int i; + + if (fields->flags != 0) { + console_printf(" flags=0x%02x:\n", fields->flags); + + if (!(fields->flags & BLE_HS_ADV_F_DISC_LTD) && + !(fields->flags & BLE_HS_ADV_F_DISC_GEN)) { + console_printf(" Non-discoverable mode\n"); + } + + if (fields->flags & BLE_HS_ADV_F_DISC_LTD) { + console_printf(" Limited discoverable mode\n"); + } + + if (fields->flags & BLE_HS_ADV_F_DISC_GEN) { + console_printf(" General discoverable mode\n"); + } + + if (fields->flags & BLE_HS_ADV_F_BREDR_UNSUP) { + console_printf(" BR/EDR not supported\n"); + } + } + + if (fields->uuids16 != NULL) { + console_printf(" uuids16(%scomplete)=", + fields->uuids16_is_complete ? "" : "in"); + for (i = 0; i < fields->num_uuids16; i++) { + print_uuid(&fields->uuids16[i].u); + console_printf(" "); + } + console_printf("\n"); + } + + if (fields->uuids32 != NULL) { + console_printf(" uuids32(%scomplete)=", + fields->uuids32_is_complete ? "" : "in"); + for (i = 0; i < fields->num_uuids32; i++) { + print_uuid(&fields->uuids32[i].u); + console_printf(" "); + } + console_printf("\n"); + } + + if (fields->uuids128 != NULL) { + console_printf(" uuids128(%scomplete)=", + fields->uuids128_is_complete ? "" : "in"); + for (i = 0; i < fields->num_uuids128; i++) { + print_uuid(&fields->uuids128[i].u); + console_printf(" "); + } + console_printf("\n"); + } + + if (fields->name != NULL) { + console_printf(" name(%scomplete)=", + fields->name_is_complete ? "" : "in"); + console_write((char *)fields->name, fields->name_len); + console_printf("\n"); + } + + if (fields->tx_pwr_lvl_is_present) { + console_printf(" tx_pwr_lvl=%d\n", fields->tx_pwr_lvl); + } + + if (fields->slave_itvl_range != NULL) { + console_printf(" slave_itvl_range="); + print_bytes(fields->slave_itvl_range, + BLE_HS_ADV_SLAVE_ITVL_RANGE_LEN); + console_printf("\n"); + } + + if (fields->svc_data_uuid16 != NULL) { + console_printf(" svc_data_uuid16="); + print_bytes(fields->svc_data_uuid16, + fields->svc_data_uuid16_len); + console_printf("\n"); + } + + if (fields->public_tgt_addr != NULL) { + console_printf(" public_tgt_addr="); + u8p = fields->public_tgt_addr; + for (i = 0; i < fields->num_public_tgt_addrs; i++) { + print_addr(u8p); + u8p += BLE_HS_ADV_PUBLIC_TGT_ADDR_ENTRY_LEN; + } + console_printf("\n"); + } + + if (fields->appearance_is_present) { + console_printf(" appearance=0x%04x\n", fields->appearance); + } + + if (fields->adv_itvl_is_present) { + console_printf(" adv_itvl=0x%04x\n", fields->adv_itvl); + } + + if (fields->svc_data_uuid32 != NULL) { + console_printf(" svc_data_uuid32="); + print_bytes(fields->svc_data_uuid32, + fields->svc_data_uuid32_len); + console_printf("\n"); + } + + if (fields->svc_data_uuid128 != NULL) { + console_printf(" svc_data_uuid128="); + print_bytes(fields->svc_data_uuid128, + fields->svc_data_uuid128_len); + console_printf("\n"); + } + + if (fields->uri != NULL) { + console_printf(" uri="); + print_bytes(fields->uri, fields->uri_len); + console_printf("\n"); + } + + if (fields->mfg_data != NULL) { + console_printf(" mfg_data="); + print_bytes(fields->mfg_data, fields->mfg_data_len); + console_printf("\n"); + } +} + +static int +btshell_conn_find_idx(uint16_t handle) +{ + int i; + + for (i = 0; i < btshell_num_conns; i++) { + if (btshell_conns[i].handle == handle) { + return i; + } + } + + return -1; +} + +static struct btshell_conn * +btshell_conn_find(uint16_t handle) +{ + int idx; + + idx = btshell_conn_find_idx(handle); + if (idx == -1) { + return NULL; + } else { + return btshell_conns + idx; + } +} + +static struct btshell_svc * +btshell_svc_find_prev(struct btshell_conn *conn, uint16_t svc_start_handle) +{ + struct btshell_svc *prev; + struct btshell_svc *svc; + + prev = NULL; + SLIST_FOREACH(svc, &conn->svcs, next) { + if (svc->svc.start_handle >= svc_start_handle) { + break; + } + + prev = svc; + } + + return prev; +} + +static struct btshell_svc * +btshell_svc_find(struct btshell_conn *conn, uint16_t svc_start_handle, + struct btshell_svc **out_prev) +{ + struct btshell_svc *prev; + struct btshell_svc *svc; + + prev = btshell_svc_find_prev(conn, svc_start_handle); + if (prev == NULL) { + svc = SLIST_FIRST(&conn->svcs); + } else { + svc = SLIST_NEXT(prev, next); + } + + if (svc != NULL && svc->svc.start_handle != svc_start_handle) { + svc = NULL; + } + + if (out_prev != NULL) { + *out_prev = prev; + } + return svc; +} + +static struct btshell_svc * +btshell_svc_find_range(struct btshell_conn *conn, uint16_t attr_handle) +{ + struct btshell_svc *svc; + + SLIST_FOREACH(svc, &conn->svcs, next) { + if (svc->svc.start_handle <= attr_handle && + svc->svc.end_handle >= attr_handle) { + + return svc; + } + } + + return NULL; +} + +static void +btshell_chr_delete(struct btshell_chr *chr) +{ + struct btshell_dsc *dsc; + + while ((dsc = SLIST_FIRST(&chr->dscs)) != NULL) { + SLIST_REMOVE_HEAD(&chr->dscs, next); + os_memblock_put(&btshell_dsc_pool, dsc); + } + + os_memblock_put(&btshell_chr_pool, chr); +} + +static void +btshell_svc_delete(struct btshell_svc *svc) +{ + struct btshell_chr *chr; + + while ((chr = SLIST_FIRST(&svc->chrs)) != NULL) { + SLIST_REMOVE_HEAD(&svc->chrs, next); + btshell_chr_delete(chr); + } + + os_memblock_put(&btshell_svc_pool, svc); +} + +static struct btshell_svc * +btshell_svc_add(uint16_t conn_handle, const struct ble_gatt_svc *gatt_svc) +{ + struct btshell_conn *conn; + struct btshell_svc *prev; + struct btshell_svc *svc; + + conn = btshell_conn_find(conn_handle); + if (conn == NULL) { + MODLOG_DFLT(DEBUG, "RECEIVED SERVICE FOR UNKNOWN CONNECTION; " + "HANDLE=%d\n", + conn_handle); + return NULL; + } + + svc = btshell_svc_find(conn, gatt_svc->start_handle, &prev); + if (svc != NULL) { + /* Service already discovered. */ + return svc; + } + + svc = os_memblock_get(&btshell_svc_pool); + if (svc == NULL) { + MODLOG_DFLT(DEBUG, "OOM WHILE DISCOVERING SERVICE\n"); + return NULL; + } + memset(svc, 0, sizeof *svc); + + svc->svc = *gatt_svc; + SLIST_INIT(&svc->chrs); + + if (prev == NULL) { + SLIST_INSERT_HEAD(&conn->svcs, svc, next); + } else { + SLIST_INSERT_AFTER(prev, svc, next); + } + + return svc; +} + +static struct btshell_chr * +btshell_chr_find_prev(const struct btshell_svc *svc, uint16_t chr_val_handle) +{ + struct btshell_chr *prev; + struct btshell_chr *chr; + + prev = NULL; + SLIST_FOREACH(chr, &svc->chrs, next) { + if (chr->chr.val_handle >= chr_val_handle) { + break; + } + + prev = chr; + } + + return prev; +} + +static struct btshell_chr * +btshell_chr_find(const struct btshell_svc *svc, uint16_t chr_val_handle, + struct btshell_chr **out_prev) +{ + struct btshell_chr *prev; + struct btshell_chr *chr; + + prev = btshell_chr_find_prev(svc, chr_val_handle); + if (prev == NULL) { + chr = SLIST_FIRST(&svc->chrs); + } else { + chr = SLIST_NEXT(prev, next); + } + + if (chr != NULL && chr->chr.val_handle != chr_val_handle) { + chr = NULL; + } + + if (out_prev != NULL) { + *out_prev = prev; + } + return chr; +} + +static struct btshell_chr * +btshell_chr_add(uint16_t conn_handle, uint16_t svc_start_handle, + const struct ble_gatt_chr *gatt_chr) +{ + struct btshell_conn *conn; + struct btshell_chr *prev; + struct btshell_chr *chr; + struct btshell_svc *svc; + + conn = btshell_conn_find(conn_handle); + if (conn == NULL) { + MODLOG_DFLT(DEBUG, "RECEIVED SERVICE FOR UNKNOWN CONNECTION; " + "HANDLE=%d\n", + conn_handle); + return NULL; + } + + svc = btshell_svc_find(conn, svc_start_handle, NULL); + if (svc == NULL) { + MODLOG_DFLT(DEBUG, "CAN'T FIND SERVICE FOR DISCOVERED CHR; HANDLE=%d\n", + conn_handle); + return NULL; + } + + chr = btshell_chr_find(svc, gatt_chr->val_handle, &prev); + if (chr != NULL) { + /* Characteristic already discovered. */ + return chr; + } + + chr = os_memblock_get(&btshell_chr_pool); + if (chr == NULL) { + MODLOG_DFLT(DEBUG, "OOM WHILE DISCOVERING CHARACTERISTIC\n"); + return NULL; + } + memset(chr, 0, sizeof *chr); + + chr->chr = *gatt_chr; + + if (prev == NULL) { + SLIST_INSERT_HEAD(&svc->chrs, chr, next); + } else { + SLIST_NEXT(prev, next) = chr; + } + + return chr; +} + +static struct btshell_dsc * +btshell_dsc_find_prev(const struct btshell_chr *chr, uint16_t dsc_handle) +{ + struct btshell_dsc *prev; + struct btshell_dsc *dsc; + + prev = NULL; + SLIST_FOREACH(dsc, &chr->dscs, next) { + if (dsc->dsc.handle >= dsc_handle) { + break; + } + + prev = dsc; + } + + return prev; +} + +static struct btshell_dsc * +btshell_dsc_find(const struct btshell_chr *chr, uint16_t dsc_handle, + struct btshell_dsc **out_prev) +{ + struct btshell_dsc *prev; + struct btshell_dsc *dsc; + + prev = btshell_dsc_find_prev(chr, dsc_handle); + if (prev == NULL) { + dsc = SLIST_FIRST(&chr->dscs); + } else { + dsc = SLIST_NEXT(prev, next); + } + + if (dsc != NULL && dsc->dsc.handle != dsc_handle) { + dsc = NULL; + } + + if (out_prev != NULL) { + *out_prev = prev; + } + return dsc; +} + +static struct btshell_dsc * +btshell_dsc_add(uint16_t conn_handle, uint16_t chr_val_handle, + const struct ble_gatt_dsc *gatt_dsc) +{ + struct btshell_conn *conn; + struct btshell_dsc *prev; + struct btshell_dsc *dsc; + struct btshell_svc *svc; + struct btshell_chr *chr; + + conn = btshell_conn_find(conn_handle); + if (conn == NULL) { + MODLOG_DFLT(DEBUG, "RECEIVED SERVICE FOR UNKNOWN CONNECTION; " + "HANDLE=%d\n", + conn_handle); + return NULL; + } + + svc = btshell_svc_find_range(conn, chr_val_handle); + if (svc == NULL) { + MODLOG_DFLT(DEBUG, "CAN'T FIND SERVICE FOR DISCOVERED DSC; HANDLE=%d\n", + conn_handle); + return NULL; + } + + chr = btshell_chr_find(svc, chr_val_handle, NULL); + if (chr == NULL) { + MODLOG_DFLT(DEBUG, "CAN'T FIND CHARACTERISTIC FOR DISCOVERED DSC; " + "HANDLE=%d\n", + conn_handle); + return NULL; + } + + dsc = btshell_dsc_find(chr, gatt_dsc->handle, &prev); + if (dsc != NULL) { + /* Descriptor already discovered. */ + return dsc; + } + + dsc = os_memblock_get(&btshell_dsc_pool); + if (dsc == NULL) { + console_printf("OOM WHILE DISCOVERING DESCRIPTOR\n"); + return NULL; + } + memset(dsc, 0, sizeof *dsc); + + dsc->dsc = *gatt_dsc; + + if (prev == NULL) { + SLIST_INSERT_HEAD(&chr->dscs, dsc, next); + } else { + SLIST_NEXT(prev, next) = dsc; + } + + return dsc; +} + +static struct btshell_conn * +btshell_conn_add(struct ble_gap_conn_desc *desc) +{ + struct btshell_conn *conn; + + assert(btshell_num_conns < MYNEWT_VAL(BLE_MAX_CONNECTIONS)); + + conn = btshell_conns + btshell_num_conns; + btshell_num_conns++; + + conn->handle = desc->conn_handle; + SLIST_INIT(&conn->svcs); + SLIST_INIT(&conn->coc_list); + + return conn; +} + +static void +btshell_conn_delete_idx(int idx) +{ + struct btshell_conn *conn; + struct btshell_svc *svc; + + assert(idx >= 0 && idx < btshell_num_conns); + + conn = btshell_conns + idx; + while ((svc = SLIST_FIRST(&conn->svcs)) != NULL) { + SLIST_REMOVE_HEAD(&conn->svcs, next); + btshell_svc_delete(svc); + } + + /* This '#if' is not strictly necessary. It is here to prevent a spurious + * warning from being reported. + */ +#if MYNEWT_VAL(BLE_MAX_CONNECTIONS) > 1 + int i; + for (i = idx + 1; i < btshell_num_conns; i++) { + btshell_conns[i - 1] = btshell_conns[i]; + } +#endif + + btshell_num_conns--; +} + +static int +btshell_on_mtu(uint16_t conn_handle, const struct ble_gatt_error *error, + uint16_t mtu, void *arg) +{ + switch (error->status) { + case 0: + console_printf("mtu exchange complete: conn_handle=%d mtu=%d\n", + conn_handle, mtu); + break; + + default: + btshell_print_error(NULL, conn_handle, error); + break; + } + + return 0; +} + +static void +btshell_full_disc_complete(int rc) +{ + console_printf("full discovery complete; rc=%d\n", rc); + btshell_full_disc_prev_chr_val = 0; +} + +static void +btshell_disc_full_dscs(uint16_t conn_handle) +{ + struct btshell_conn *conn; + struct btshell_chr *chr; + struct btshell_svc *svc; + int rc; + + conn = btshell_conn_find(conn_handle); + if (conn == NULL) { + MODLOG_DFLT(DEBUG, "Failed to discover descriptors for conn=%d; " + "not connected\n", conn_handle); + btshell_full_disc_complete(BLE_HS_ENOTCONN); + return; + } + + SLIST_FOREACH(svc, &conn->svcs, next) { + SLIST_FOREACH(chr, &svc->chrs, next) { + if (!chr_is_empty(svc, chr) && + SLIST_EMPTY(&chr->dscs) && + btshell_full_disc_prev_chr_val <= chr->chr.def_handle) { + + rc = btshell_disc_all_dscs(conn_handle, + chr->chr.val_handle, + chr_end_handle(svc, chr)); + if (rc != 0) { + btshell_full_disc_complete(rc); + } + + btshell_full_disc_prev_chr_val = chr->chr.val_handle; + return; + } + } + } + + /* All descriptors discovered. */ + btshell_full_disc_complete(0); +} + +static void +btshell_disc_full_chrs(uint16_t conn_handle) +{ + struct btshell_conn *conn; + struct btshell_svc *svc; + int rc; + + conn = btshell_conn_find(conn_handle); + if (conn == NULL) { + MODLOG_DFLT(DEBUG, "Failed to discover characteristics for conn=%d; " + "not connected\n", conn_handle); + btshell_full_disc_complete(BLE_HS_ENOTCONN); + return; + } + + SLIST_FOREACH(svc, &conn->svcs, next) { + if (!svc->discovered) { + rc = btshell_disc_all_chrs_in_svc(conn_handle, svc); + if (rc != 0) { + btshell_full_disc_complete(rc); + } + return; + } + } + + /* All characteristics discovered. */ + btshell_disc_full_dscs(conn_handle); +} + +static int +btshell_on_disc_s(uint16_t conn_handle, const struct ble_gatt_error *error, + const struct ble_gatt_svc *service, void *arg) +{ + switch (error->status) { + case 0: + btshell_svc_add(conn_handle, service); + break; + + case BLE_HS_EDONE: + console_printf("service discovery successful\n"); + if (btshell_full_disc_prev_chr_val > 0) { + btshell_disc_full_chrs(conn_handle); + } + break; + + default: + btshell_print_error(NULL, conn_handle, error); + break; + } + + return 0; +} + +static int +btshell_on_disc_c(uint16_t conn_handle, const struct ble_gatt_error *error, + const struct ble_gatt_chr *chr, void *arg) +{ + intptr_t svc_start_handle; + + svc_start_handle = (intptr_t)arg; + + switch (error->status) { + case 0: + btshell_chr_add(conn_handle, svc_start_handle, chr); + break; + + case BLE_HS_EDONE: + console_printf("characteristic discovery successful\n"); + if (btshell_full_disc_prev_chr_val > 0) { + btshell_disc_full_chrs(conn_handle); + } + break; + + default: + btshell_print_error(NULL, conn_handle, error); + break; + } + + return 0; +} + +static int +btshell_on_disc_c_in_s(uint16_t conn_handle, const struct ble_gatt_error *error, + const struct ble_gatt_chr *chr, void *arg) +{ + struct btshell_svc *svc = arg; + + switch (error->status) { + case 0: + btshell_chr_add(conn_handle, svc->svc.start_handle, chr); + break; + + case BLE_HS_EDONE: + svc->discovered = true; + console_printf("characteristic discovery successful\n"); + if (btshell_full_disc_prev_chr_val > 0) { + btshell_disc_full_chrs(conn_handle); + } + break; + + default: + btshell_print_error(NULL, conn_handle, error); + break; + } + + return 0; +} + +static int +btshell_on_disc_d(uint16_t conn_handle, const struct ble_gatt_error *error, + uint16_t chr_val_handle, const struct ble_gatt_dsc *dsc, + void *arg) +{ + switch (error->status) { + case 0: + btshell_dsc_add(conn_handle, chr_val_handle, dsc); + break; + + case BLE_HS_EDONE: + console_printf("descriptor discovery successful\n"); + if (btshell_full_disc_prev_chr_val > 0) { + btshell_disc_full_dscs(conn_handle); + } + break; + + default: + btshell_print_error(NULL, conn_handle, error); + break; + } + + return 0; +} + +static int +btshell_on_read(uint16_t conn_handle, const struct ble_gatt_error *error, + struct ble_gatt_attr *attr, void *arg) +{ + switch (error->status) { + case 0: + console_printf("characteristic read; conn_handle=%d " + "attr_handle=%d len=%d value=", conn_handle, + attr->handle, OS_MBUF_PKTLEN(attr->om)); + print_mbuf(attr->om); + console_printf("\n"); + break; + + case BLE_HS_EDONE: + console_printf("characteristic read complete\n"); + break; + + default: + btshell_print_error(NULL, conn_handle, error); + break; + } + + return 0; +} + +static int +btshell_on_write(uint16_t conn_handle, const struct ble_gatt_error *error, + struct ble_gatt_attr *attr, void *arg) +{ + switch (error->status) { + case 0: + console_printf("characteristic write complete; conn_handle=%d " + "attr_handle=%d\n", conn_handle, attr->handle); + break; + + default: + btshell_print_error(NULL, conn_handle, error); + break; + } + + return 0; +} + +static int +btshell_on_write_reliable(uint16_t conn_handle, + const struct ble_gatt_error *error, + struct ble_gatt_attr *attrs, uint8_t num_attrs, + void *arg) +{ + int i; + + switch (error->status) { + case 0: + console_printf("characteristic write reliable complete; " + "conn_handle=%d", conn_handle); + + for (i = 0; i < num_attrs; i++) { + console_printf(" attr_handle=%d len=%d value=", attrs[i].handle, + OS_MBUF_PKTLEN(attrs[i].om)); + print_mbuf(attrs[i].om); + } + console_printf("\n"); + break; + + default: + btshell_print_error(NULL, conn_handle, error); + break; + } + + return 0; +} + +static void +btshell_decode_adv_data(const uint8_t *adv_data, uint8_t adv_data_len, void *arg) +{ + struct btshell_scan_opts *scan_opts = arg; + struct ble_hs_adv_fields fields; + + console_printf(" data_length=%d data=", adv_data_len); + + if (scan_opts) { + adv_data_len = min(adv_data_len, scan_opts->limit); + } + + print_bytes(adv_data, adv_data_len); + + console_printf(" fields:\n"); + ble_hs_adv_parse_fields(&fields, adv_data, adv_data_len); + btshell_print_adv_fields(&fields); +} + +#if MYNEWT_VAL(BLE_EXT_ADV) +static void +btshell_decode_event_type(struct ble_gap_ext_disc_desc *desc, void *arg) +{ + const struct ble_hs_adv_field *ad_name = NULL; + struct btshell_scan_opts *scan_opts = arg; + uint8_t directed = 0; + + if (scan_opts && scan_opts->name_filter_len) { + if (ble_hs_adv_find_field(BLE_HS_ADV_TYPE_COMP_NAME, desc->data, + desc->length_data, &ad_name)) { + ble_hs_adv_find_field(BLE_HS_ADV_TYPE_INCOMP_NAME, desc->data, + desc->length_data, &ad_name); + } + + if (!ad_name) { + return; + } + + if (ad_name->length < scan_opts->name_filter_len) { + return; + } + + if (strncasecmp(scan_opts->name_filter, (const char *)ad_name->value, + scan_opts->name_filter_len)) { + return; + } + } + + if (desc->props & BLE_HCI_ADV_LEGACY_MASK) { + if (scan_opts && scan_opts->ignore_legacy) { + return; + } + + console_printf("Legacy PDU type %d", desc->legacy_event_type); + if (desc->legacy_event_type == BLE_HCI_ADV_RPT_EVTYPE_DIR_IND) { + directed = 1; + } + goto common_data; + } else { + if (scan_opts && scan_opts->periodic_only && desc->periodic_adv_itvl == 0) { + return; + } + } + + console_printf("Extended adv: "); + if (desc->props & BLE_HCI_ADV_CONN_MASK) { + console_printf("'conn' "); + } + if (desc->props & BLE_HCI_ADV_SCAN_MASK) { + console_printf("'scan' "); + } + if (desc->props & BLE_HCI_ADV_DIRECT_MASK) { + console_printf("'dir' "); + directed = 1; + } + + if (desc->props & BLE_HCI_ADV_SCAN_RSP_MASK) { + console_printf("'scan rsp' "); + } + + switch(desc->data_status) { + case BLE_GAP_EXT_ADV_DATA_STATUS_COMPLETE: + console_printf("complete"); + break; + case BLE_GAP_EXT_ADV_DATA_STATUS_INCOMPLETE: + console_printf("incomplete"); + break; + case BLE_GAP_EXT_ADV_DATA_STATUS_TRUNCATED: + console_printf("truncated"); + break; + default: + console_printf("reserved %d", desc->data_status); + break; + } + +common_data: + console_printf(" rssi=%d txpower=%d, pphy=%d, sphy=%d, sid=%d," + " periodic_adv_itvl=%u, addr_type=%d addr=", + desc->rssi, desc->tx_power, desc->prim_phy, desc->sec_phy, + desc->sid, desc->periodic_adv_itvl, desc->addr.type); + print_addr(desc->addr.val); + if (directed) { + console_printf(" init_addr_type=%d inita=", desc->direct_addr.type); + print_addr(desc->direct_addr.val); + } + + console_printf("\n"); + + if(!desc->length_data) { + return; + } + + btshell_decode_adv_data(desc->data, desc->length_data, arg); +} +#endif + +static int +btshell_restart_adv(struct ble_gap_event *event) +{ + int rc = 0; +#if MYNEWT_VAL(BLE_EXT_ADV) + uint8_t i; +#endif + + if (event->type != BLE_GAP_EVENT_DISCONNECT) { + return -1; + } + +#if MYNEWT_VAL(BLE_EXT_ADV) + for (i = 0; i < BLE_ADV_INSTANCES; ++i) { + if (ext_adv_restart[i].restart && + (ext_adv_restart[i].conn_handle == + event->disconnect.conn.conn_handle)) { + rc = ble_gap_ext_adv_start(i, 0, 0); + break; + } + } +#else + if (!adv_params.restart) { + return 0; + } + + rc = ble_gap_adv_start(adv_params.own_addr_type, &adv_params.direct_addr, + adv_params.duration_ms, &adv_params.params, + btshell_gap_event, NULL); +#endif + + return rc; +} + +#if MYNEWT_VAL(BLE_PERIODIC_ADV) +struct psync { + bool established; + unsigned int complete; + unsigned int truncated; + size_t off; + bool changed; + uint8_t data[1650]; /* TODO make this configurable */ +}; + +static struct psync g_periodic_data[MYNEWT_VAL(BLE_MAX_PERIODIC_SYNCS)]; + +void +btshell_sync_stats(uint16_t handle) +{ + struct psync *psync; + + if (handle >= MYNEWT_VAL(BLE_MAX_PERIODIC_SYNCS)) { + return; + } + + psync = &g_periodic_data[handle]; + if (!psync->established) { + console_printf("Sync not established\n"); + return; + } + + console_printf("completed=%u truncated=%u\n", + psync->complete, psync->truncated); +} + +static void +handle_periodic_report(struct ble_gap_event *event) +{ + struct psync *psync; + uint16_t handle = event->periodic_report.sync_handle; + + if (handle >= MYNEWT_VAL(BLE_MAX_PERIODIC_SYNCS)) { + return; + } + + psync = &g_periodic_data[handle]; + + if (psync->changed || + memcmp(psync->data + psync->off, event->periodic_report.data, + event->periodic_report.data_length)) { + /* first fragment with changed data */ + if (!psync->changed) { + console_printf("Sync data changed, completed=%u, truncated=%u\n", + psync->complete, psync->truncated); + } + + psync->changed = true; + + console_printf("Sync report handle=%u status=", handle); + switch(event->periodic_report.data_status) { + case BLE_HCI_PERIODIC_DATA_STATUS_COMPLETE: + console_printf("complete"); + break; + case BLE_HCI_PERIODIC_DATA_STATUS_INCOMPLETE: + console_printf("incomplete"); + break; + case BLE_HCI_PERIODIC_DATA_STATUS_TRUNCATED: + console_printf("truncated"); + break; + default: + console_printf("reserved 0x%x", event->periodic_report.data_status); + break; + } + + btshell_decode_adv_data(event->periodic_report.data, + event->periodic_report.data_length, NULL); + + psync->complete = 0; + psync->truncated = 0; + } + + /* cache data */ + memcpy(psync->data + psync->off, event->periodic_report.data, + event->periodic_report.data_length); + + switch(event->periodic_report.data_status) { + case BLE_HCI_PERIODIC_DATA_STATUS_INCOMPLETE: + psync->off += event->periodic_report.data_length; + break; + case BLE_HCI_PERIODIC_DATA_STATUS_COMPLETE: + psync->complete++; + psync->off = 0; + psync->changed = false; + break; + case BLE_HCI_PERIODIC_DATA_STATUS_TRUNCATED: + psync->truncated++; + psync->off = 0; + psync->changed = false; + break; + default: + break; + } +} +#endif + +int +btshell_gap_event(struct ble_gap_event *event, void *arg) +{ + struct ble_gap_conn_desc desc; + int conn_idx; + int rc; +#if MYNEWT_VAL(BLE_PERIODIC_ADV) + struct psync *psync; +#endif + + switch (event->type) { + case BLE_GAP_EVENT_CONNECT: + console_printf("connection %s; status=%d ", + event->connect.status == 0 ? "established" : "failed", + event->connect.status); + + if (event->connect.status == 0) { + rc = ble_gap_conn_find(event->connect.conn_handle, &desc); + assert(rc == 0); + print_conn_desc(&desc); + btshell_conn_add(&desc); + } + return 0; + + case BLE_GAP_EVENT_DISCONNECT: + console_printf("disconnect; reason=%d ", event->disconnect.reason); + print_conn_desc(&event->disconnect.conn); + + conn_idx = btshell_conn_find_idx(event->disconnect.conn.conn_handle); + if (conn_idx != -1) { + btshell_conn_delete_idx(conn_idx); + } + + return btshell_restart_adv(event); +#if MYNEWT_VAL(BLE_EXT_ADV) + case BLE_GAP_EVENT_EXT_DISC: + btshell_decode_event_type(&event->ext_disc, arg); + return 0; +#endif + case BLE_GAP_EVENT_DISC: + console_printf("received advertisement; event_type=%d rssi=%d " + "addr_type=%d addr=", event->disc.event_type, + event->disc.rssi, event->disc.addr.type); + print_addr(event->disc.addr.val); + + /* + * There is no adv data to print in case of connectable + * directed advertising + */ + if (event->disc.event_type == BLE_HCI_ADV_RPT_EVTYPE_DIR_IND) { + console_printf("\nConnectable directed advertising event\n"); + return 0; + } + + btshell_decode_adv_data(event->disc.data, event->disc.length_data, arg); + + return 0; + + case BLE_GAP_EVENT_CONN_UPDATE: + console_printf("connection updated; status=%d ", + event->conn_update.status); + rc = ble_gap_conn_find(event->conn_update.conn_handle, &desc); + assert(rc == 0); + print_conn_desc(&desc); + return 0; + + case BLE_GAP_EVENT_CONN_UPDATE_REQ: + console_printf("connection update request\n"); + *event->conn_update_req.self_params = + *event->conn_update_req.peer_params; + return 0; + + case BLE_GAP_EVENT_PASSKEY_ACTION: + console_printf("passkey action event; action=%d", + event->passkey.params.action); + if (event->passkey.params.action == BLE_SM_IOACT_NUMCMP) { + console_printf(" numcmp=%lu", + (unsigned long)event->passkey.params.numcmp); + } + console_printf("\n"); + return 0; + + + case BLE_GAP_EVENT_DISC_COMPLETE: + console_printf("discovery complete; reason=%d\n", + event->disc_complete.reason); + return 0; + + case BLE_GAP_EVENT_ADV_COMPLETE: +#if MYNEWT_VAL(BLE_EXT_ADV) + console_printf("advertise complete; reason=%d, instance=%u, handle=%d\n", + event->adv_complete.reason, event->adv_complete.instance, + event->adv_complete.conn_handle); + + ext_adv_restart[event->adv_complete.instance].conn_handle = + event->adv_complete.conn_handle; +#else + console_printf("advertise complete; reason=%d\n", + event->adv_complete.reason); +#endif + return 0; + + case BLE_GAP_EVENT_ENC_CHANGE: + console_printf("encryption change event; status=%d ", + event->enc_change.status); + rc = ble_gap_conn_find(event->enc_change.conn_handle, &desc); + assert(rc == 0); + print_conn_desc(&desc); + return 0; + + case BLE_GAP_EVENT_NOTIFY_RX: + console_printf("notification rx event; attr_handle=%d indication=%d " + "len=%d data=", + event->notify_rx.attr_handle, + event->notify_rx.indication, + OS_MBUF_PKTLEN(event->notify_rx.om)); + + print_mbuf(event->notify_rx.om); + console_printf("\n"); + return 0; + + case BLE_GAP_EVENT_NOTIFY_TX: + console_printf("notification tx event; status=%d attr_handle=%d " + "indication=%d\n", + event->notify_tx.status, + event->notify_tx.attr_handle, + event->notify_tx.indication); + return 0; + + case BLE_GAP_EVENT_SUBSCRIBE: + console_printf("subscribe event; conn_handle=%d attr_handle=%d " + "reason=%d prevn=%d curn=%d previ=%d curi=%d\n", + event->subscribe.conn_handle, + event->subscribe.attr_handle, + event->subscribe.reason, + event->subscribe.prev_notify, + event->subscribe.cur_notify, + event->subscribe.prev_indicate, + event->subscribe.cur_indicate); + return 0; + + case BLE_GAP_EVENT_MTU: + console_printf("mtu update event; conn_handle=%d cid=%d mtu=%d\n", + event->mtu.conn_handle, + event->mtu.channel_id, + event->mtu.value); + return 0; + + case BLE_GAP_EVENT_IDENTITY_RESOLVED: + console_printf("identity resolved "); + rc = ble_gap_conn_find(event->identity_resolved.conn_handle, &desc); + assert(rc == 0); + print_conn_desc(&desc); + return 0; + + case BLE_GAP_EVENT_PHY_UPDATE_COMPLETE: + console_printf("PHY update complete; status=%d, conn_handle=%d " + " tx_phy=%d, rx_phy=%d\n", + event->phy_updated.status, + event->phy_updated.conn_handle, + event->phy_updated.tx_phy, + event->phy_updated.rx_phy); + return 0; + + case BLE_GAP_EVENT_REPEAT_PAIRING: + /* We already have a bond with the peer, but it is attempting to + * establish a new secure link. This app sacrifices security for + * convenience: just throw away the old bond and accept the new link. + */ + + /* Delete the old bond. */ + rc = ble_gap_conn_find(event->repeat_pairing.conn_handle, &desc); + assert(rc == 0); + ble_store_util_delete_peer(&desc.peer_id_addr); + + /* Return BLE_GAP_REPEAT_PAIRING_RETRY to indicate that the host should + * continue with the pairing operation. + */ + return BLE_GAP_REPEAT_PAIRING_RETRY; +#if MYNEWT_VAL(BLE_PERIODIC_ADV) + case BLE_GAP_EVENT_PERIODIC_SYNC: + if (event->periodic_sync.status) { + console_printf("Periodic Sync Establishment Failed; status=%u\n", + event->periodic_sync.status); + } else { + console_printf("Periodic Sync Established; sync_handle=%u sid=%u " + "phy=%u adv_interval=%u ca=%u addr_type=%u addr=", + event->periodic_sync.sync_handle, + event->periodic_sync.sid, event->periodic_sync.adv_phy, + event->periodic_sync.per_adv_ival, + event->periodic_sync.adv_clk_accuracy, + event->periodic_sync.adv_addr.type); + print_addr(event->periodic_sync.adv_addr.val); + console_printf("\n"); + + /* TODO non-NimBLE controllers may not start handles from 0 */ + if (event->periodic_sync.sync_handle >= MYNEWT_VAL(BLE_MAX_PERIODIC_SYNCS)) { + console_printf("Unable to prepare cache for sync data\n"); + } else { + psync = &g_periodic_data[event->periodic_sync.sync_handle]; + memset(psync, 0, sizeof(*psync)); + psync->changed = true; + psync->established = true; + } + } + return 0; + case BLE_GAP_EVENT_PERIODIC_SYNC_LOST: + /* TODO non-NimBLE controllers may not start handles from 0 */ + if (event->periodic_sync_lost.sync_handle >= MYNEWT_VAL(BLE_MAX_PERIODIC_SYNCS)) { + console_printf("Periodic Sync Lost; sync_handle=%d reason=%d\n", + event->periodic_sync_lost.sync_handle, + event->periodic_sync_lost.reason); + } else { + psync = &g_periodic_data[event->periodic_sync_lost.sync_handle]; + + console_printf("Periodic Sync Lost; sync_handle=%d reason=%d completed=%u truncated=%u\n", + event->periodic_sync_lost.sync_handle, + event->periodic_sync_lost.reason, + psync->complete, psync->truncated); + + memset(psync, 0, sizeof(*psync)); + } + return 0; + case BLE_GAP_EVENT_PERIODIC_REPORT: + handle_periodic_report(event); + return 0; +#if MYNEWT_VAL(BLE_PERIODIC_ADV_SYNC_TRANSFER) + case BLE_GAP_EVENT_PERIODIC_TRANSFER: + console_printf("Periodic Sync Transfer Received on conn=%u; status=%u," + " sync_handle=%u sid=%u phy=%u adv_interval=%u ca=%u " + "addr_type=%u addr=", + event->periodic_transfer.conn_handle, + event->periodic_transfer.status, + event->periodic_transfer.sync_handle, + event->periodic_transfer.sid, + event->periodic_transfer.adv_phy, + event->periodic_transfer.per_adv_itvl, + event->periodic_transfer.adv_clk_accuracy, + event->periodic_transfer.adv_addr.type); + print_addr(event->periodic_transfer.adv_addr.val); + console_printf("\n"); + + if (!event->periodic_transfer.status) { + /* TODO non-NimBLE controllers may not start handles from 0 */ + if (event->periodic_transfer.sync_handle >= MYNEWT_VAL(BLE_MAX_PERIODIC_SYNCS)) { + console_printf("Unable to prepare cache for sync data\n"); + } else { + psync = &g_periodic_data[event->periodic_transfer.sync_handle]; + memset(psync, 0, sizeof(*psync)); + psync->changed = true; + psync->established = true; + } + } + return 0; +#endif +#endif + default: + return 0; + } +} + +static void +btshell_on_l2cap_update(uint16_t conn_handle, int status, void *arg) +{ + console_printf("l2cap update complete; conn_handle=%d status=%d\n", + conn_handle, status); +} + +static void +btshell_tx_timer_cb(struct os_event *ev) +{ + uint8_t i; + uint8_t len; + int32_t timeout; + struct ble_l2cap_hdr l2cap_hdr; + struct os_mbuf *om; + + if ((btshell_tx_data.tx_num == 0) || (btshell_tx_data.tx_len == 0)) { + return; + } + + console_printf("Sending %d/%d len: %d\n", + btshell_tx_data.tx_num_requested - btshell_tx_data.tx_num + 1, + btshell_tx_data.tx_num_requested, btshell_tx_data.tx_len); + + len = btshell_tx_data.tx_len; + + om = NULL; + if (os_msys_num_free() >= 4) { + om = os_msys_get_pkthdr(len + BLE_L2CAP_HDR_SZ, BLE_HCI_DATA_HDR_SZ); + } + + if (om) { + /* + * NOTE: CID is 0xffff so it is not confused with valid l2cap channel. + * The rest of the data gets filled with incrementing pattern starting + * from 0. + */ + put_le16(&l2cap_hdr.len, len); + put_le16(&l2cap_hdr.cid, 0xffff); + + os_mbuf_append(om, (void *)&l2cap_hdr, BLE_L2CAP_HDR_SZ); + + for (i = 0; i < len; ++i) { + os_mbuf_append(om, (void *)&i, 1); + } + + ble_hs_lock(); + ble_hs_hci_acl_tx_now(btshell_tx_data.conn, &om); + ble_hs_unlock(); + + --btshell_tx_data.tx_num; + } + + if (btshell_tx_data.tx_num) { + timeout = (int32_t)btshell_tx_data.tx_rate; + timeout = (timeout * OS_TICKS_PER_SEC) / 1000; + os_callout_reset(&btshell_tx_timer, timeout); + } +} + +int +btshell_exchange_mtu(uint16_t conn_handle) +{ + int rc; + + rc = ble_gattc_exchange_mtu(conn_handle, btshell_on_mtu, NULL); + return rc; +} + +int +btshell_disc_all_chrs(uint16_t conn_handle, uint16_t start_handle, + uint16_t end_handle) +{ + intptr_t svc_start_handle; + int rc; + + svc_start_handle = start_handle; + rc = ble_gattc_disc_all_chrs(conn_handle, start_handle, end_handle, + btshell_on_disc_c, (void *)svc_start_handle); + return rc; +} + +int +btshell_disc_all_chrs_in_svc(uint16_t conn_handle, struct btshell_svc *svc) +{ + int rc; + + rc = ble_gattc_disc_all_chrs(conn_handle, svc->svc.start_handle, + svc->svc.end_handle, btshell_on_disc_c_in_s, + svc); + return rc; +} + +int +btshell_disc_chrs_by_uuid(uint16_t conn_handle, uint16_t start_handle, + uint16_t end_handle, const ble_uuid_t *uuid) +{ + intptr_t svc_start_handle; + int rc; + + svc_start_handle = start_handle; + rc = ble_gattc_disc_chrs_by_uuid(conn_handle, start_handle, end_handle, + uuid, btshell_on_disc_c, + (void *)svc_start_handle); + return rc; +} + +int +btshell_disc_svcs(uint16_t conn_handle) +{ + int rc; + + rc = ble_gattc_disc_all_svcs(conn_handle, btshell_on_disc_s, NULL); + return rc; +} + +int +btshell_disc_svc_by_uuid(uint16_t conn_handle, const ble_uuid_t *uuid) +{ + int rc; + + rc = ble_gattc_disc_svc_by_uuid(conn_handle, uuid, + btshell_on_disc_s, NULL); + return rc; +} + +int +btshell_disc_all_dscs(uint16_t conn_handle, uint16_t start_handle, + uint16_t end_handle) +{ + int rc; + + rc = ble_gattc_disc_all_dscs(conn_handle, start_handle, end_handle, + btshell_on_disc_d, NULL); + return rc; +} + +int +btshell_disc_full(uint16_t conn_handle) +{ + struct btshell_conn *conn; + struct btshell_svc *svc; + + /* Undiscover everything first. */ + conn = btshell_conn_find(conn_handle); + if (conn == NULL) { + return BLE_HS_ENOTCONN; + } + + while ((svc = SLIST_FIRST(&conn->svcs)) != NULL) { + SLIST_REMOVE_HEAD(&conn->svcs, next); + btshell_svc_delete(svc); + } + + btshell_full_disc_prev_chr_val = 1; + btshell_disc_svcs(conn_handle); + + return 0; +} + +int +btshell_find_inc_svcs(uint16_t conn_handle, uint16_t start_handle, + uint16_t end_handle) +{ + int rc; + + rc = ble_gattc_find_inc_svcs(conn_handle, start_handle, end_handle, + btshell_on_disc_s, NULL); + return rc; +} + +int +btshell_read(uint16_t conn_handle, uint16_t attr_handle) +{ + struct os_mbuf *om; + int rc; + + if (conn_handle == BLE_HS_CONN_HANDLE_NONE) { + rc = ble_att_svr_read_local(attr_handle, &om); + if (rc == 0) { + console_printf("read local; attr_handle=%d len=%d value=", + attr_handle, OS_MBUF_PKTLEN(om)); + print_mbuf(om); + console_printf("\n"); + + os_mbuf_free_chain(om); + } + } else { + rc = ble_gattc_read(conn_handle, attr_handle, btshell_on_read, NULL); + } + return rc; +} + +int +btshell_read_long(uint16_t conn_handle, uint16_t attr_handle, uint16_t offset) +{ + int rc; + + rc = ble_gattc_read_long(conn_handle, attr_handle, offset, + btshell_on_read, NULL); + return rc; +} + +int +btshell_read_by_uuid(uint16_t conn_handle, uint16_t start_handle, + uint16_t end_handle, const ble_uuid_t *uuid) +{ + int rc; + + rc = ble_gattc_read_by_uuid(conn_handle, start_handle, end_handle, uuid, + btshell_on_read, NULL); + return rc; +} + +int +btshell_read_mult(uint16_t conn_handle, uint16_t *attr_handles, + int num_attr_handles) +{ + int rc; + + rc = ble_gattc_read_mult(conn_handle, attr_handles, num_attr_handles, + btshell_on_read, NULL); + return rc; +} + +int +btshell_write(uint16_t conn_handle, uint16_t attr_handle, struct os_mbuf *om) +{ + int rc; + + if (conn_handle == BLE_HS_CONN_HANDLE_NONE) { + rc = ble_att_svr_write_local(attr_handle, om); + } else { + rc = ble_gattc_write(conn_handle, attr_handle, om, + btshell_on_write, NULL); + } + + return rc; +} + +int +btshell_write_no_rsp(uint16_t conn_handle, uint16_t attr_handle, + struct os_mbuf *om) +{ + int rc; + + rc = ble_gattc_write_no_rsp(conn_handle, attr_handle, om); + + return rc; +} + +int +btshell_write_long(uint16_t conn_handle, uint16_t attr_handle, + uint16_t offset, struct os_mbuf *om) +{ + int rc; + + rc = ble_gattc_write_long(conn_handle, attr_handle, offset, + om, btshell_on_write, NULL); + return rc; +} + +int +btshell_write_reliable(uint16_t conn_handle, + struct ble_gatt_attr *attrs, + int num_attrs) +{ + int rc; + + rc = ble_gattc_write_reliable(conn_handle, attrs, num_attrs, + btshell_on_write_reliable, NULL); + return rc; +} + +#if MYNEWT_VAL(BLE_EXT_ADV) +int +btshell_ext_adv_configure(uint8_t instance, + const struct ble_gap_ext_adv_params *params, + int8_t *selected_tx_power) +{ + return ble_gap_ext_adv_configure(instance, params, selected_tx_power, + btshell_gap_event, NULL); +} + +int +btshell_ext_adv_start(uint8_t instance, int duration, + int max_events, bool restart) +{ + int rc; + + /* Advertising restart doesn't make sense + * with limited duration or events + */ + if (restart && (duration == 0) && (max_events == 0)) { + ext_adv_restart[instance].restart = restart; + } + + rc = ble_gap_ext_adv_start(instance, duration, max_events); + + return rc; +} + +int +btshell_ext_adv_stop(uint8_t instance) +{ + int rc; + + ext_adv_restart[instance].restart = false; + + rc = ble_gap_ext_adv_stop(instance); + + return rc; +} +#endif + +int +btshell_adv_stop(void) +{ + int rc; + + adv_params.restart = false; + + rc = ble_gap_adv_stop(); + return rc; +} + +int +btshell_adv_start(uint8_t own_addr_type, const ble_addr_t *direct_addr, + int32_t duration_ms, const struct ble_gap_adv_params *params, + bool restart) +{ + int rc; + + if (restart) { + adv_params.restart = restart; + adv_params.own_addr_type = own_addr_type; + adv_params.duration_ms = duration_ms; + + if (direct_addr) { + memcpy(&adv_params.direct_addr, direct_addr, sizeof(adv_params.direct_addr)); + } + + if (params) { + memcpy(&adv_params.params, params, sizeof(adv_params.params)); + } + } + + rc = ble_gap_adv_start(own_addr_type, direct_addr, duration_ms, params, + btshell_gap_event, NULL); + return rc; +} + +int +btshell_conn_initiate(uint8_t own_addr_type, const ble_addr_t *peer_addr, + int32_t duration_ms, struct ble_gap_conn_params *params) +{ + int rc; + + rc = ble_gap_connect(own_addr_type, peer_addr, duration_ms, params, + btshell_gap_event, NULL); + + return rc; +} + +int +btshell_ext_conn_initiate(uint8_t own_addr_type, const ble_addr_t *peer_addr, + int32_t duration_ms, + struct ble_gap_conn_params *phy_1m_params, + struct ble_gap_conn_params *phy_2m_params, + struct ble_gap_conn_params *phy_coded_params) +{ +#if !MYNEWT_VAL(BLE_EXT_ADV) + console_printf("BLE extended advertising not supported."); + console_printf(" Configure nimble host to enable it\n"); + return 0; +#else + int rc; + uint8_t phy_mask = 0; + + if (phy_1m_params) { + phy_mask |= BLE_GAP_LE_PHY_1M_MASK; + } + + if (phy_2m_params) { + phy_mask |= BLE_GAP_LE_PHY_2M_MASK; + } + + if (phy_coded_params) { + phy_mask |= BLE_GAP_LE_PHY_CODED_MASK; + } + + rc = ble_gap_ext_connect(own_addr_type, peer_addr, duration_ms, phy_mask, + phy_1m_params, phy_2m_params, phy_coded_params, + btshell_gap_event, NULL); + + return rc; +#endif +} + +int +btshell_conn_cancel(void) +{ + int rc; + + rc = ble_gap_conn_cancel(); + return rc; +} + +int +btshell_term_conn(uint16_t conn_handle, uint8_t reason) +{ + int rc; + + rc = ble_gap_terminate(conn_handle, reason); + return rc; +} + +int +btshell_wl_set(ble_addr_t *addrs, int addrs_count) +{ + int rc; + + rc = ble_gap_wl_set(addrs, addrs_count); + return rc; +} + +int +btshell_scan(uint8_t own_addr_type, int32_t duration_ms, + const struct ble_gap_disc_params *disc_params, void *cb_args) +{ + int rc; + + rc = ble_gap_disc(own_addr_type, duration_ms, disc_params, + btshell_gap_event, cb_args); + return rc; +} + +int +btshell_ext_scan(uint8_t own_addr_type, uint16_t duration, uint16_t period, + uint8_t filter_duplicates, uint8_t filter_policy, + uint8_t limited, + const struct ble_gap_ext_disc_params *uncoded_params, + const struct ble_gap_ext_disc_params *coded_params, + void *cb_args) +{ +#if !MYNEWT_VAL(BLE_EXT_ADV) + console_printf("BLE extended advertising not supported."); + console_printf(" Configure nimble host to enable it\n"); + return 0; +#else + int rc; + + rc = ble_gap_ext_disc(own_addr_type, duration, period, filter_duplicates, + filter_policy, limited, uncoded_params, coded_params, + btshell_gap_event, cb_args); + return rc; +#endif +} + +int +btshell_scan_cancel(void) +{ + int rc; + + rc = ble_gap_disc_cancel(); + return rc; +} + +int +btshell_update_conn(uint16_t conn_handle, struct ble_gap_upd_params *params) +{ + int rc; + + rc = ble_gap_update_params(conn_handle, params); + return rc; +} + +void +btshell_notify(uint16_t attr_handle) +{ + ble_gatts_chr_updated(attr_handle); +} + +int +btshell_datalen(uint16_t conn_handle, uint16_t tx_octets, uint16_t tx_time) +{ + int rc; + + rc = ble_hs_hci_util_set_data_len(conn_handle, tx_octets, tx_time); + return rc; +} + +int +btshell_l2cap_update(uint16_t conn_handle, + struct ble_l2cap_sig_update_params *params) +{ + int rc; + + rc = ble_l2cap_sig_update(conn_handle, params, btshell_on_l2cap_update, + NULL); + return rc; +} + +int +btshell_sec_pair(uint16_t conn_handle) +{ +#if !NIMBLE_BLE_SM + return BLE_HS_ENOTSUP; +#endif + + int rc; + + rc = ble_gap_pair_initiate(conn_handle); + return rc; +} + +int +btshell_sec_unpair(ble_addr_t *peer_addr) +{ +#if !NIMBLE_BLE_SM + return BLE_HS_ENOTSUP; +#endif + + int rc; + + rc = ble_gap_unpair(peer_addr); + return rc; +} + +int +btshell_sec_start(uint16_t conn_handle) +{ +#if !NIMBLE_BLE_SM + return BLE_HS_ENOTSUP; +#endif + + int rc; + + rc = ble_gap_security_initiate(conn_handle); + return rc; +} + +int +btshell_sec_restart(uint16_t conn_handle, + uint8_t key_size, + uint8_t *ltk, + uint16_t ediv, + uint64_t rand_val, + int auth) +{ +#if !NIMBLE_BLE_SM + return BLE_HS_ENOTSUP; +#endif + + struct ble_store_value_sec value_sec; + struct ble_store_key_sec key_sec; + struct ble_gap_conn_desc desc; + ble_hs_conn_flags_t conn_flags; + int rc; + + if (ltk == NULL) { + /* The user is requesting a store lookup. */ + rc = ble_gap_conn_find(conn_handle, &desc); + if (rc != 0) { + return rc; + } + + memset(&key_sec, 0, sizeof key_sec); + key_sec.peer_addr = desc.peer_id_addr; + + rc = ble_hs_atomic_conn_flags(conn_handle, &conn_flags); + if (rc != 0) { + return rc; + } + if (conn_flags & BLE_HS_CONN_F_MASTER) { + rc = ble_store_read_peer_sec(&key_sec, &value_sec); + } else { + rc = ble_store_read_our_sec(&key_sec, &value_sec); + } + if (rc != 0) { + return rc; + } + + ltk = value_sec.ltk; + key_size = value_sec.key_size; + ediv = value_sec.ediv; + rand_val = value_sec.rand_num; + auth = value_sec.authenticated; + } + + rc = ble_gap_encryption_initiate(conn_handle, key_size, ltk, + ediv, rand_val, auth); + return rc; +} + +/** + * Called to start transmitting 'num' packets at rate 'rate' of size 'size' + * to connection handle 'handle' + * + * @param handle + * @param len + * @param rate + * @param num + * + * @return int + */ +int +btshell_tx_start(uint16_t conn_handle, uint16_t len, uint16_t rate, uint16_t num) +{ + /* Cannot be currently in a session */ + if (num == 0) { + return 0; + } + + /* Do not allow start if already in progress */ + if (btshell_tx_data.tx_num != 0) { + return -1; + } + + /* XXX: for now, must have contiguous mbuf space */ + if ((len + 4) > MYNEWT_VAL_MSYS_1_BLOCK_SIZE) { + return -2; + } + + btshell_tx_data.tx_num = num; + btshell_tx_data.tx_num_requested = num; + btshell_tx_data.tx_rate = rate; + btshell_tx_data.tx_len = len; + btshell_tx_data.tx_conn_handle = conn_handle; + + ble_hs_lock(); + btshell_tx_data.conn = ble_hs_conn_find(conn_handle); + ble_hs_unlock(); + + if (!btshell_tx_data.conn) { + console_printf("Could not find ble_hs_conn for handle: %d\n", + conn_handle); + return -1; + } + + os_callout_reset(&btshell_tx_timer, 0); + + return 0; +} + +void +btshell_tx_stop(void) +{ + os_callout_stop(&btshell_tx_timer); + btshell_tx_data.tx_num = 0; +} + +int +btshell_rssi(uint16_t conn_handle, int8_t *out_rssi) +{ + int rc; + + rc = ble_gap_conn_rssi(conn_handle, out_rssi); + if (rc != 0) { + return rc; + } + + return 0; +} + +static void +btshell_on_reset(int reason) +{ + console_printf("Error: Resetting state; reason=%d\n", reason); +} + +static void +btshell_on_sync(void) +{ + /* Make sure we have proper identity address set (public preferred) */ + if (ble_hs_util_ensure_addr(0) != 0) { + console_printf("Failed to set identity address\n"); + } + +#if MYNEWT_VAL(BLE_SM_SC) + int rc; + + rc = ble_sm_sc_oob_generate_data(&oob_data_local); + if (rc) { + console_printf("Error: generating oob data; reason=%d\n", rc); + return; + } +#endif + + console_printf("Host and controller synced\n"); +} + +#if MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM) != 0 + +static int +btshell_l2cap_coc_add(uint16_t conn_handle, struct ble_l2cap_chan *chan) +{ + struct btshell_conn *conn; + struct btshell_l2cap_coc *coc; + struct btshell_l2cap_coc *prev, *cur; + + conn = btshell_conn_find(conn_handle); + assert(conn != NULL); + + coc = os_memblock_get(&btshell_coc_conn_pool); + if (!coc) { + return ENOMEM; + } + + coc->chan = chan; + + prev = NULL; + SLIST_FOREACH(cur, &conn->coc_list, next) { + prev = cur; + } + + if (prev == NULL) { + SLIST_INSERT_HEAD(&conn->coc_list, coc, next); + } else { + SLIST_INSERT_AFTER(prev, coc, next); + } + + return 0; +} + +static void +btshell_l2cap_coc_remove(uint16_t conn_handle, struct ble_l2cap_chan *chan) +{ + struct btshell_conn *conn; + struct btshell_l2cap_coc *coc; + struct btshell_l2cap_coc *cur; + + conn = btshell_conn_find(conn_handle); + assert(conn != NULL); + + coc = NULL; + SLIST_FOREACH(cur, &conn->coc_list, next) { + if (cur->chan == chan) { + coc = cur; + break; + } + } + + if (!coc) { + return; + } + + SLIST_REMOVE(&conn->coc_list, coc, btshell_l2cap_coc, next); + os_memblock_put(&btshell_coc_conn_pool, coc); +} + +static void +btshell_l2cap_coc_recv(struct ble_l2cap_chan *chan, struct os_mbuf *sdu) +{ + console_printf("LE CoC SDU received, chan: 0x%08lx, data len %d\n", + (uint32_t) chan, OS_MBUF_PKTLEN(sdu)); + + os_mbuf_free_chain(sdu); + sdu = os_mbuf_get_pkthdr(&sdu_os_mbuf_pool, 0); + assert(sdu != NULL); + + if (ble_l2cap_recv_ready(chan, sdu) != 0) { + assert(0); + } +} + +static int +btshell_l2cap_coc_accept(uint16_t conn_handle, uint16_t peer_mtu, + struct ble_l2cap_chan *chan) +{ + struct os_mbuf *sdu_rx; + + console_printf("LE CoC accepting, chan: 0x%08lx, peer_mtu %d\n", + (uint32_t) chan, peer_mtu); + + sdu_rx = os_mbuf_get_pkthdr(&sdu_os_mbuf_pool, 0); + if (!sdu_rx) { + return BLE_HS_ENOMEM; + } + + return ble_l2cap_recv_ready(chan, sdu_rx); +} + +static void +btshell_l2cap_coc_unstalled(uint16_t conn_handle, struct ble_l2cap_chan *chan) +{ + struct btshell_conn *conn; + struct btshell_l2cap_coc *coc; + struct btshell_l2cap_coc *cur; + + conn = btshell_conn_find(conn_handle); + assert(conn != NULL); + + coc = NULL; + SLIST_FOREACH(cur, &conn->coc_list, next) { + if (cur->chan == chan) { + coc = cur; + break; + } + } + + if (!coc) { + return; + } + + coc->stalled = false; +} + +static int +btshell_l2cap_event(struct ble_l2cap_event *event, void *arg) +{ + int accept_response; + struct ble_l2cap_chan_info chan_info; + + switch(event->type) { + case BLE_L2CAP_EVENT_COC_CONNECTED: + if (event->connect.status) { + console_printf("LE COC error: %d\n", event->connect.status); + return 0; + } + + if (ble_l2cap_get_chan_info(event->connect.chan, &chan_info)) { + assert(0); + } + + console_printf("LE COC connected, conn: %d, chan: %p, psm: 0x%02x, scid: 0x%04x, " + "dcid: 0x%04x, our_mps: %d, our_mtu: %d, peer_mps: %d, peer_mtu: %d\n", + event->connect.conn_handle, event->connect.chan, + chan_info.psm, chan_info.scid, chan_info.dcid, + chan_info.our_l2cap_mtu, chan_info.our_coc_mtu, chan_info.peer_l2cap_mtu, chan_info.peer_coc_mtu); + + btshell_l2cap_coc_add(event->connect.conn_handle, + event->connect.chan); + + return 0; + case BLE_L2CAP_EVENT_COC_DISCONNECTED: + console_printf("LE CoC disconnected, chan: %p\n", + event->disconnect.chan); + + btshell_l2cap_coc_remove(event->disconnect.conn_handle, + event->disconnect.chan); + return 0; + case BLE_L2CAP_EVENT_COC_ACCEPT: + accept_response = PTR_TO_INT(arg); + if (accept_response) { + return accept_response; + } + + return btshell_l2cap_coc_accept(event->accept.conn_handle, + event->accept.peer_sdu_size, + event->accept.chan); + + case BLE_L2CAP_EVENT_COC_DATA_RECEIVED: + btshell_l2cap_coc_recv(event->receive.chan, event->receive.sdu_rx); + return 0; + case BLE_L2CAP_EVENT_COC_RECONFIG_COMPLETED: + + if (ble_l2cap_get_chan_info(event->reconfigured.chan, &chan_info)) { + assert(0); + } + + console_printf("LE CoC reconfigure completed status 0x%02x," \ + "chan: %p\n", + event->reconfigured.status, + event->reconfigured.chan); + + if (event->reconfigured.status == 0) { + console_printf("\t our_mps: %d our_mtu %d\n", chan_info.our_l2cap_mtu, chan_info.our_coc_mtu); + } + return 0; + case BLE_L2CAP_EVENT_COC_PEER_RECONFIGURED: + + if (ble_l2cap_get_chan_info(event->reconfigured.chan, &chan_info)) { + assert(0); + } + + console_printf("LE CoC peer reconfigured status 0x%02x," \ + "chan: %p\n", + event->reconfigured.status, + event->reconfigured.chan); + + if (event->reconfigured.status == 0) { + console_printf("\t peer_mps: %d peer_mtu %d\n", chan_info.peer_l2cap_mtu, chan_info.peer_coc_mtu); + } + + return 0; + case BLE_L2CAP_EVENT_COC_TX_UNSTALLED: + console_printf("L2CAP CoC channel %p unstalled, last sdu sent with err=0x%02x\n", + event->tx_unstalled.chan, event->tx_unstalled.status); + btshell_l2cap_coc_unstalled(event->tx_unstalled.conn_handle, event->tx_unstalled.chan); + return 0; + default: + return 0; + } +} +#endif + +int +btshell_l2cap_create_srv(uint16_t psm, uint16_t mtu, int accept_response) +{ +#if MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM) == 0 + console_printf("BLE L2CAP LE COC not supported."); + console_printf(" Configure nimble host to enable it\n"); + return 0; +#else + + if (mtu == 0 || mtu > BTSHELL_COC_MTU) { + mtu = BTSHELL_COC_MTU; + } + + return ble_l2cap_create_server(psm, mtu, btshell_l2cap_event, + INT_TO_PTR(accept_response)); +#endif +} + +int +btshell_l2cap_connect(uint16_t conn_handle, uint16_t psm, uint16_t mtu, uint8_t num) +{ +#if MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM) == 0 + console_printf("BLE L2CAP LE COC not supported."); + console_printf(" Configure nimble host to enable it\n"); + return 0; +#else + + struct os_mbuf *sdu_rx[num]; + int i; + + if (mtu == 0 || mtu > BTSHELL_COC_MTU) { + mtu = BTSHELL_COC_MTU; + } + + console_printf("L2CAP CoC MTU: %d, max available %d\n", mtu, BTSHELL_COC_MTU); + + for (i = 0; i < num; i++) { + sdu_rx[i] = os_mbuf_get_pkthdr(&sdu_os_mbuf_pool, 0); + assert(sdu_rx != NULL); + } + + if (num == 1) { + return ble_l2cap_connect(conn_handle, psm, mtu, sdu_rx[0], + btshell_l2cap_event, NULL); + } + + return ble_l2cap_enhanced_connect(conn_handle, psm, mtu, + num, sdu_rx,btshell_l2cap_event, NULL); +#endif +} + +int +btshell_l2cap_disconnect(uint16_t conn_handle, uint16_t idx) +{ +#if MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM) == 0 + console_printf("BLE L2CAP LE COC not supported."); + console_printf(" Configure nimble host to enable it\n"); + return 0; +#else + + struct btshell_conn *conn; + struct btshell_l2cap_coc *coc; + int i; + int rc = 0; + + conn = btshell_conn_find(conn_handle); + assert(conn != NULL); + + i = 0; + SLIST_FOREACH(coc, &conn->coc_list, next) { + if (i == idx) { + break; + } + i++; + } + assert(coc != NULL); + + rc = ble_l2cap_disconnect(coc->chan); + if (rc) { + console_printf("Could not disconnect channel rc=%d\n", rc); + } + + return rc; +#endif +} + +int +btshell_l2cap_reconfig(uint16_t conn_handle, uint16_t mtu, + uint8_t num, uint8_t idxs[]) +{ + struct btshell_conn *conn; + struct btshell_l2cap_coc *coc; + struct ble_l2cap_chan * chans[5] = {0}; + int i, j; + int cnt; + + conn = btshell_conn_find(conn_handle); + if (conn == NULL) { + console_printf("conn=%d does not exist\n", conn_handle); + return 0; + } + + i = 0; + j = 0; + cnt = 0; + SLIST_FOREACH(coc, &conn->coc_list, next) { + for (i = 0; i < num; i++) { + if (idxs[i] == j) { + chans[cnt] = coc->chan; + cnt++; + break; + } + } + j++; + } + + if (cnt != num) { + console_printf("Missing coc? (%d!=%d)\n", num, cnt); + return BLE_HS_EINVAL; + } + + return ble_l2cap_reconfig(chans, cnt, mtu); +} + +int +btshell_l2cap_send(uint16_t conn_handle, uint16_t idx, uint16_t bytes) +{ +#if MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM) == 0 + console_printf("BLE L2CAP LE COC not supported."); + console_printf(" Configure nimble host to enable it\n"); + return 0; +#else + + struct btshell_conn *conn; + struct btshell_l2cap_coc *coc; + struct os_mbuf *sdu_tx; + uint8_t b[] = {0x00, 0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88, 0x99}; + int i; + int rc; + + console_printf("conn=%d, idx=%d, bytes=%d\n", conn_handle, idx, bytes); + + conn = btshell_conn_find(conn_handle); + if (conn == NULL) { + console_printf("conn=%d does not exist\n", conn_handle); + return 0; + } + + i = 0; + SLIST_FOREACH(coc, &conn->coc_list, next) { + if (i == idx) { + break; + } + i++; + } + if (coc == NULL) { + console_printf("Are you sure your channel exist?\n"); + return 0; + } + + if (coc->stalled) { + console_printf("Channel is stalled, wait ...\n"); + return 0; + } + + sdu_tx = os_mbuf_get_pkthdr(&sdu_os_mbuf_pool, 0); + if (sdu_tx == NULL) { + console_printf("No memory in the test sdu pool\n"); + return 0; + } + + /* For the testing purpose we fill up buffer with known data, easy + * to validate on other side. In this loop we add as many full chunks as we + * can + */ + for (i = 0; i < bytes / sizeof(b); i++) { + rc = os_mbuf_append(sdu_tx, b, sizeof(b)); + if (rc) { + console_printf("Cannot append data %i !\n", i); + os_mbuf_free_chain(sdu_tx); + return rc; + } + } + + /* Here we add the rest < sizeof(b) */ + rc = os_mbuf_append(sdu_tx, b, bytes - (sizeof(b) * i)); + if (rc) { + console_printf("Cannot append data %i !\n", i); + os_mbuf_free_chain(sdu_tx); + return rc; + } + + rc = ble_l2cap_send(coc->chan, sdu_tx); + if (rc) { + if (rc == BLE_HS_ESTALLED) { + console_printf("CoC module is stalled with data. Wait for unstalled \n"); + coc->stalled = true; + } else { + console_printf("Could not send data rc=%d\n", rc); + } + os_mbuf_free_chain(sdu_tx); + } + + return rc; + +#endif +} + +static void +btshell_init_ext_adv_restart(void) +{ +#if MYNEWT_VAL(BLE_EXT_ADV) + int i; + + for (i = 0; i < BLE_ADV_INSTANCES; ++i) { + ext_adv_restart[i].conn_handle = BLE_HS_CONN_HANDLE_NONE; + } +#endif +} + +/** + * main + * + * The main task for the project. This function initializes the packages, + * then starts serving events from default event queue. + * + * @return int NOTE: this function should never return! + */ +static int +main_fn(int argc, char **argv) +{ + int rc; + +#ifdef ARCH_sim + mcu_sim_parse_args(argc, argv); +#endif + + /* Initialize OS */ + sysinit(); + + /* Initialize some application specific memory pools. */ + rc = os_mempool_init(&btshell_svc_pool, BTSHELL_MAX_SVCS, + sizeof (struct btshell_svc), btshell_svc_mem, + "btshell_svc_pool"); + assert(rc == 0); + + rc = os_mempool_init(&btshell_chr_pool, BTSHELL_MAX_CHRS, + sizeof (struct btshell_chr), btshell_chr_mem, + "btshell_chr_pool"); + assert(rc == 0); + + rc = os_mempool_init(&btshell_dsc_pool, BTSHELL_MAX_DSCS, + sizeof (struct btshell_dsc), btshell_dsc_mem, + "btshell_dsc_pool"); + assert(rc == 0); + +#if MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM) != 0 + /* For testing we want to support all the available channels */ + rc = os_mempool_init(&sdu_coc_mbuf_mempool, BTSHELL_COC_BUF_COUNT, + BTSHELL_COC_MTU, btshell_sdu_coc_mem, + "btshell_coc_sdu_pool"); + assert(rc == 0); + + rc = os_mbuf_pool_init(&sdu_os_mbuf_pool, &sdu_coc_mbuf_mempool, + BTSHELL_COC_MTU, BTSHELL_COC_BUF_COUNT); + assert(rc == 0); + + rc = os_mempool_init(&btshell_coc_conn_pool, + MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM), + sizeof (struct btshell_l2cap_coc), btshell_coc_conn_mem, + "btshell_coc_conn_pool"); + assert(rc == 0); +#endif + + /* Initialize the NimBLE host configuration. */ + ble_hs_cfg.reset_cb = btshell_on_reset; + ble_hs_cfg.sync_cb = btshell_on_sync; + ble_hs_cfg.gatts_register_cb = gatt_svr_register_cb; + ble_hs_cfg.store_status_cb = ble_store_util_status_rr; + + rc = gatt_svr_init(); + assert(rc == 0); + + cmd_init(); + + /* Set the default device name. */ + rc = ble_svc_gap_device_name_set("nimble-btshell"); + assert(rc == 0); + + /* Create a callout (timer). This callout is used by the "tx" btshell + * command to repeatedly send packets of sequential data bytes. + */ + os_callout_init(&btshell_tx_timer, os_eventq_dflt_get(), + btshell_tx_timer_cb, NULL); + + btshell_init_ext_adv_restart(); + + while (1) { + os_eventq_run(os_eventq_dflt_get()); + } + /* os start should never return. If it does, this should be an error */ + assert(0); + + return 0; +} + +int +main(int argc, char **argv) +{ +#if BABBLESIM + extern void bsim_init(int argc, char** argv, void *main_fn); + bsim_init(argc, argv, main_fn); +#else + main_fn(argc, argv); +#endif + + return 0; +} diff --git a/lib/bt/host/nimble/nimble/apps/btshell/src/misc.c b/lib/bt/host/nimble/nimble/apps/btshell/src/misc.c new file mode 100644 index 00000000..e100eb79 --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/btshell/src/misc.c @@ -0,0 +1,163 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "console/console.h" +#include "host/ble_uuid.h" +#include "host/ble_gap.h" + +#include "btshell.h" + +/** + * Utility function to log an array of bytes. + */ +void +print_bytes(const uint8_t *bytes, int len) +{ + int i; + + for (i = 0; i < len; i++) { + console_printf("%s0x%02x", i != 0 ? ":" : "", bytes[i]); + } +} + +void +print_mbuf(const struct os_mbuf *om) +{ + int colon; + + colon = 0; + while (om != NULL) { + if (colon) { + console_printf(":"); + } else { + colon = 1; + } + print_bytes(om->om_data, om->om_len); + om = SLIST_NEXT(om, om_next); + } +} + +void +print_addr(const void *addr) +{ + const uint8_t *u8p; + + u8p = addr; + console_printf("%02x:%02x:%02x:%02x:%02x:%02x", + u8p[5], u8p[4], u8p[3], u8p[2], u8p[1], u8p[0]); +} + +void +print_uuid(const ble_uuid_t *uuid) +{ + char buf[BLE_UUID_STR_LEN]; + + ble_uuid_to_str(uuid, buf); + + console_printf("%s", buf); +} + +int +svc_is_empty(const struct btshell_svc *svc) +{ + return svc->svc.end_handle <= svc->svc.start_handle; +} + +uint16_t +chr_end_handle(const struct btshell_svc *svc, const struct btshell_chr *chr) +{ + const struct btshell_chr *next_chr; + + next_chr = SLIST_NEXT(chr, next); + if (next_chr != NULL) { + return next_chr->chr.def_handle - 1; + } else { + return svc->svc.end_handle; + } +} + +int +chr_is_empty(const struct btshell_svc *svc, const struct btshell_chr *chr) +{ + return chr_end_handle(svc, chr) <= chr->chr.val_handle; +} + +void +print_conn_desc(const struct ble_gap_conn_desc *desc) +{ + console_printf("handle=%d our_ota_addr_type=%d our_ota_addr=", + desc->conn_handle, desc->our_ota_addr.type); + print_addr(desc->our_ota_addr.val); + console_printf(" our_id_addr_type=%d our_id_addr=", + desc->our_id_addr.type); + print_addr(desc->our_id_addr.val); + console_printf(" peer_ota_addr_type=%d peer_ota_addr=", + desc->peer_ota_addr.type); + print_addr(desc->peer_ota_addr.val); + console_printf(" peer_id_addr_type=%d peer_id_addr=", + desc->peer_id_addr.type); + print_addr(desc->peer_id_addr.val); + console_printf(" conn_itvl=%d conn_latency=%d supervision_timeout=%d" + " key_size=%d encrypted=%d authenticated=%d bonded=%d\n", + desc->conn_itvl, desc->conn_latency, + desc->supervision_timeout, + desc->sec_state.key_size, + desc->sec_state.encrypted, + desc->sec_state.authenticated, + desc->sec_state.bonded); +} + +static void +print_dsc(struct btshell_dsc *dsc) +{ + console_printf(" dsc_handle=%d uuid=", dsc->dsc.handle); + print_uuid(&dsc->dsc.uuid.u); + console_printf("\n"); +} + +static void +print_chr(struct btshell_chr *chr) +{ + struct btshell_dsc *dsc; + + console_printf(" def_handle=%d val_handle=%d properties=0x%02x " + "uuid=", chr->chr.def_handle, chr->chr.val_handle, + chr->chr.properties); + print_uuid(&chr->chr.uuid.u); + console_printf("\n"); + + SLIST_FOREACH(dsc, &chr->dscs, next) { + print_dsc(dsc); + } +} + +void +print_svc(struct btshell_svc *svc) +{ + struct btshell_chr *chr; + + console_printf(" start=%d end=%d uuid=", svc->svc.start_handle, + svc->svc.end_handle); + print_uuid(&svc->svc.uuid.u); + console_printf("\n"); + + SLIST_FOREACH(chr, &svc->chrs, next) { + print_chr(chr); + } +} diff --git a/lib/bt/host/nimble/nimble/apps/btshell/src/parse.c b/lib/bt/host/nimble/nimble/apps/btshell/src/parse.c new file mode 100644 index 00000000..e4dbade4 --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/btshell/src/parse.c @@ -0,0 +1,736 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "console/console.h" +#include "host/ble_hs.h" +#include "host/ble_uuid.h" +#include "host/ble_eddystone.h" +#include "cmd.h" +#include "btshell.h" + +#define CMD_MAX_ARGS 16 + +static char *cmd_args[CMD_MAX_ARGS][2]; +static int cmd_num_args; + +int +parse_arg_find_idx(const char *key) +{ + int i; + + for (i = 0; i < cmd_num_args; i++) { + if (strcmp(cmd_args[i][0], key) == 0) { + return i; + } + } + + return -1; +} + +char * +parse_arg_peek(const char *key) +{ + int i; + + for (i = 0; i < cmd_num_args; i++) { + if (strcmp(cmd_args[i][0], key) == 0) { + return cmd_args[i][1]; + } + } + + return NULL; +} + +char * +parse_arg_extract(const char *key) +{ + int i; + + for (i = 0; i < cmd_num_args; i++) { + if (strcmp(cmd_args[i][0], key) == 0) { + /* Erase parameter. */ + cmd_args[i][0][0] = '\0'; + + return cmd_args[i][1]; + } + } + + return NULL; +} + +/** + * Determines which number base to use when parsing the specified numeric + * string. This just avoids base '0' so that numbers don't get interpreted as + * octal. + */ +static int +parse_arg_long_base(char *sval) +{ + if (sval[0] == '0' && sval[1] == 'x') { + return 0; + } else { + return 10; + } +} + +long +parse_long_bounds(char *sval, long min, long max, int *out_status) +{ + char *endptr; + long lval; + + lval = strtol(sval, &endptr, parse_arg_long_base(sval)); + if (sval[0] != '\0' && *endptr == '\0' && + lval >= min && lval <= max) { + + *out_status = 0; + return lval; + } + + *out_status = EINVAL; + return 0; +} + +long +parse_arg_long_bounds_peek(char *name, long min, long max, int *out_status) +{ + char *sval; + + sval = parse_arg_peek(name); + if (sval == NULL) { + *out_status = ENOENT; + return 0; + } + return parse_long_bounds(sval, min, max, out_status); +} + +long +parse_arg_long_bounds(char *name, long min, long max, int *out_status) +{ + char *sval; + + sval = parse_arg_extract(name); + if (sval == NULL) { + *out_status = ENOENT; + return 0; + } + return parse_long_bounds(sval, min, max, out_status); +} + +long +parse_arg_long_bounds_dflt(char *name, long min, long max, + long dflt, int *out_status) +{ + long val; + int rc; + + val = parse_arg_long_bounds(name, min, max, &rc); + if (rc == ENOENT) { + rc = 0; + val = dflt; + } + + *out_status = rc; + + return val; +} + +uint64_t +parse_arg_uint64_bounds(char *name, uint64_t min, uint64_t max, int *out_status) +{ + char *endptr; + char *sval; + uint64_t lval; + + sval = parse_arg_extract(name); + if (sval == NULL) { + *out_status = ENOENT; + return 0; + } + + lval = strtoull(sval, &endptr, parse_arg_long_base(sval)); + if (sval[0] != '\0' && *endptr == '\0' && + lval >= min && lval <= max) { + + *out_status = 0; + return lval; + } + + *out_status = EINVAL; + return 0; +} + +long +parse_arg_long(char *name, int *out_status) +{ + return parse_arg_long_bounds(name, LONG_MIN, LONG_MAX, out_status); +} + +uint8_t +parse_arg_bool(char *name, int *out_status) +{ + return parse_arg_long_bounds(name, 0, 1, out_status); +} + +uint8_t +parse_arg_bool_dflt(char *name, uint8_t dflt, int *out_status) +{ + return parse_arg_long_bounds_dflt(name, 0, 1, dflt, out_status); +} + +uint8_t +parse_arg_uint8(char *name, int *out_status) +{ + return parse_arg_long_bounds(name, 0, UINT8_MAX, out_status); +} + +uint16_t +parse_arg_uint16(char *name, int *out_status) +{ + return parse_arg_long_bounds(name, 0, UINT16_MAX, out_status); +} + +uint16_t +parse_arg_uint16_peek(char *name, int *out_status) +{ + return parse_arg_long_bounds_peek(name, 0, UINT16_MAX, out_status); +} + +uint32_t +parse_arg_uint32(char *name, int *out_status) +{ + return parse_arg_uint64_bounds(name, 0, UINT32_MAX, out_status); +} + +uint64_t +parse_arg_uint64(char *name, int *out_status) +{ + return parse_arg_uint64_bounds(name, 0, UINT64_MAX, out_status); +} + +uint8_t +parse_arg_uint8_dflt(char *name, uint8_t dflt, int *out_status) +{ + uint8_t val; + int rc; + + val = parse_arg_uint8(name, &rc); + if (rc == ENOENT) { + val = dflt; + rc = 0; + } + + *out_status = rc; + return val; +} + +uint16_t +parse_arg_uint16_dflt(char *name, uint16_t dflt, int *out_status) +{ + uint16_t val; + int rc; + + val = parse_arg_uint16(name, &rc); + if (rc == ENOENT) { + val = dflt; + rc = 0; + } + + *out_status = rc; + return val; +} + +uint32_t +parse_arg_uint32_dflt(char *name, uint32_t dflt, int *out_status) +{ + uint32_t val; + int rc; + + val = parse_arg_uint32(name, &rc); + if (rc == ENOENT) { + val = dflt; + rc = 0; + } + + *out_status = rc; + return val; +} + +static uint32_t +parse_time_unit_mult(const char *str) +{ + if (!strcasecmp(str, "us")) { + return 1; + } else if (!strcasecmp(str, "ms")) { + return 1000; + } else if (!strcasecmp(str, "s")) { + return 1000000; + } + + return 0; +} + +static uint32_t +parse_time_us(const char *str, int *out_status) +{ + uint32_t val = 0; + uint32_t val_div = 1; + uint32_t val_mult = 1; + uint32_t val_us; + + while (isdigit((unsigned char)*str)) { + val *= 10; + val += *str - '0'; + str++; + } + + if (*str == '.') { + str++; + while (isdigit((unsigned char)*str)) { + val *= 10; + val += *str - '0'; + val_div *= 10; + str++; + } + } + + val_mult = parse_time_unit_mult(str); + if (val_mult == 0) { + *out_status = EINVAL; + return 0; + } + + if (val_mult > val_div) { + val_us = val * (val_mult / val_div); + } else { + val_us = val * (val_div / val_mult); + } + + *out_status = 0; + + return val_us; +} + +uint32_t +parse_arg_time_dflt(char *name, int step_us, uint32_t dflt, int *out_status) +{ + const char *arg; + uint32_t val; + int rc; + + arg = parse_arg_peek(name); + if (!arg) { + *out_status = 0; + return dflt; + } + + val = parse_time_us(arg, &rc); + if (rc) { + val = parse_arg_uint32(name, &rc); + if (rc == ENOENT) { + *out_status = 0; + return dflt; + } + } else { + val /= step_us; + parse_arg_extract(name); + } + + *out_status = rc; + return val; +} + +const struct kv_pair * +parse_kv_find(const struct kv_pair *kvs, char *name) +{ + const struct kv_pair *kv; + int i; + + for (i = 0; kvs[i].key != NULL; i++) { + kv = kvs + i; + if (strcmp(name, kv->key) == 0) { + return kv; + } + } + + return NULL; +} + +int +parse_arg_kv(char *name, const struct kv_pair *kvs, int *out_status) +{ + const struct kv_pair *kv; + char *sval; + + sval = parse_arg_extract(name); + if (sval == NULL) { + *out_status = ENOENT; + return -1; + } + + kv = parse_kv_find(kvs, sval); + if (kv == NULL) { + *out_status = EINVAL; + return -1; + } + + *out_status = 0; + return kv->val; +} + +int +parse_arg_kv_dflt(char *name, const struct kv_pair *kvs, int def_val, + int *out_status) +{ + int val; + int rc; + + val = parse_arg_kv(name, kvs, &rc); + if (rc == ENOENT) { + rc = 0; + val = def_val; + } + + *out_status = rc; + + return val; +} + + +static int +parse_arg_byte_stream_delim(char *sval, char *delims, int max_len, + uint8_t *dst, int *out_len) +{ + unsigned long ul; + char *endptr; + char *token; + int i; + char *tok_ptr; + + i = 0; + for (token = strtok_r(sval, delims, &tok_ptr); + token != NULL; + token = strtok_r(NULL, delims, &tok_ptr)) { + + if (i >= max_len) { + return EINVAL; + } + + ul = strtoul(token, &endptr, 16); + if (sval[0] == '\0' || *endptr != '\0' || ul > UINT8_MAX) { + return -1; + } + + dst[i] = ul; + i++; + } + + *out_len = i; + + return 0; +} + +int +parse_arg_byte_stream(char *name, int max_len, uint8_t *dst, int *out_len) +{ + char *sval; + + sval = parse_arg_extract(name); + if (sval == NULL) { + return ENOENT; + } + + return parse_arg_byte_stream_delim(sval, ":-", max_len, dst, out_len); +} + +int +parse_arg_uint8_list_with_separator(char *name, char *separator, int max_len, + uint8_t *dst, int *out_len) +{ + char *sval; + + sval = parse_arg_extract(name); + if (sval == NULL) { + return ENOENT; + } + + return parse_arg_byte_stream_delim(sval, separator, max_len, dst, out_len); +} + +int +parse_arg_byte_stream_exact_length(char *name, uint8_t *dst, int len) +{ + int actual_len; + int rc; + + rc = parse_arg_byte_stream(name, len, dst, &actual_len); + if (rc != 0) { + return rc; + } + + if (actual_len != len) { + return EINVAL; + } + + return 0; +} + +static void +parse_reverse_bytes(uint8_t *bytes, int len) +{ + uint8_t tmp; + int i; + + for (i = 0; i < len / 2; i++) { + tmp = bytes[i]; + bytes[i] = bytes[len - i - 1]; + bytes[len - i - 1] = tmp; + } +} + +int +parse_arg_mac(char *name, uint8_t *dst) +{ + int rc; + + rc = parse_arg_byte_stream_exact_length(name, dst, 6); + if (rc != 0) { + return rc; + } + + parse_reverse_bytes(dst, 6); + + return 0; +} + +int +parse_arg_addr(char *name, ble_addr_t *addr) +{ + char *arg; + size_t len; + uint8_t addr_type; + bool addr_type_found; + int rc; + + arg = parse_arg_peek(name); + if (!arg) { + return ENOENT; + } + + len = strlen(arg); + if (len < 2) { + return EINVAL; + } + + addr_type_found = false; + if ((arg[len - 2] == ':') || (arg[len - 2] == '-')) { + if (tolower(arg[len - 1]) == 'p') { + addr_type = BLE_ADDR_PUBLIC; + addr_type_found = true; + } else if (tolower(arg[len - 1]) == 'r') { + addr_type = BLE_ADDR_RANDOM; + addr_type_found = true; + } + + if (addr_type_found) { + arg[len - 2] = '\0'; + } +} + + rc = parse_arg_mac(name, addr->val); + if (rc != 0) { + return rc; + } + + if (addr_type_found) { + addr->type = addr_type; + } else { + rc = EAGAIN; + } + + return rc; +} + +int +parse_arg_uuid(char *str, ble_uuid_any_t *uuid) +{ + uint16_t uuid16; + uint8_t val[16]; + int len; + int rc; + + uuid16 = parse_arg_uint16_peek(str, &rc); + switch (rc) { + case ENOENT: + parse_arg_extract(str); + return ENOENT; + + case 0: + len = 2; + val[0] = uuid16; + val[1] = uuid16 >> 8; + parse_arg_extract(str); + break; + + default: + len = 16; + rc = parse_arg_byte_stream_exact_length(str, val, 16); + if (rc != 0) { + return EINVAL; + } + parse_reverse_bytes(val, 16); + break; + } + + rc = ble_uuid_init_from_buf(uuid, val, len); + if (rc != 0) { + return EINVAL; + } else { + return 0; + } +} + +int +parse_arg_all(int argc, char **argv) +{ + char *key; + char *val; + int i; + char *tok_ptr; + + cmd_num_args = 0; + + for (i = 0; i < argc; i++) { + key = strtok_r(argv[i], "=", &tok_ptr); + val = strtok_r(NULL, "=", &tok_ptr); + + if (key != NULL && val != NULL) { + if (strlen(key) == 0) { + console_printf("Error: invalid argument: %s\n", argv[i]); + return -1; + } + + if (cmd_num_args >= CMD_MAX_ARGS) { + console_printf("Error: too many arguments"); + return -1; + } + + cmd_args[cmd_num_args][0] = key; + cmd_args[cmd_num_args][1] = val; + cmd_num_args++; + } + } + + return 0; +} + +int +parse_eddystone_url(char *full_url, uint8_t *out_scheme, char *out_body, + uint8_t *out_body_len, uint8_t *out_suffix) +{ + static const struct { + char *s; + uint8_t scheme; + } schemes[] = { + { "http://www.", BLE_EDDYSTONE_URL_SCHEME_HTTP_WWW }, + { "https://www.", BLE_EDDYSTONE_URL_SCHEME_HTTPS_WWW }, + { "http://", BLE_EDDYSTONE_URL_SCHEME_HTTP }, + { "https://", BLE_EDDYSTONE_URL_SCHEME_HTTPS }, + }; + + static const struct { + char *s; + uint8_t code; + } suffixes[] = { + { ".com/", BLE_EDDYSTONE_URL_SUFFIX_COM_SLASH }, + { ".org/", BLE_EDDYSTONE_URL_SUFFIX_ORG_SLASH }, + { ".edu/", BLE_EDDYSTONE_URL_SUFFIX_EDU_SLASH }, + { ".net/", BLE_EDDYSTONE_URL_SUFFIX_NET_SLASH }, + { ".info/", BLE_EDDYSTONE_URL_SUFFIX_INFO_SLASH }, + { ".biz/", BLE_EDDYSTONE_URL_SUFFIX_BIZ_SLASH }, + { ".gov/", BLE_EDDYSTONE_URL_SUFFIX_GOV_SLASH }, + { ".com", BLE_EDDYSTONE_URL_SUFFIX_COM }, + { ".org", BLE_EDDYSTONE_URL_SUFFIX_ORG }, + { ".edu", BLE_EDDYSTONE_URL_SUFFIX_EDU }, + { ".net", BLE_EDDYSTONE_URL_SUFFIX_NET }, + { ".info", BLE_EDDYSTONE_URL_SUFFIX_INFO }, + { ".biz", BLE_EDDYSTONE_URL_SUFFIX_BIZ }, + { ".gov", BLE_EDDYSTONE_URL_SUFFIX_GOV }, + }; + + char *prefix; + char *suffix; + int full_url_len; + int prefix_len; + int suffix_len; + int suffix_idx; + int rc; + int i; + + full_url_len = strlen(full_url); + + rc = BLE_HS_EINVAL; + for (i = 0; i < sizeof schemes / sizeof schemes[0]; i++) { + prefix = schemes[i].s; + prefix_len = strlen(schemes[i].s); + + if (full_url_len >= prefix_len && + memcmp(full_url, prefix, prefix_len) == 0) { + + *out_scheme = i; + rc = 0; + break; + } + } + if (rc != 0) { + return rc; + } + + rc = BLE_HS_EINVAL; + for (i = 0; i < sizeof suffixes / sizeof suffixes[0]; i++) { + suffix = suffixes[i].s; + suffix_len = strlen(suffixes[i].s); + + suffix_idx = full_url_len - suffix_len; + if (suffix_idx >= prefix_len && + memcmp(full_url + suffix_idx, suffix, suffix_len) == 0) { + + *out_suffix = i; + rc = 0; + break; + } + } + if (rc != 0) { + *out_suffix = BLE_EDDYSTONE_URL_SUFFIX_NONE; + *out_body_len = full_url_len - prefix_len; + } else { + *out_body_len = full_url_len - prefix_len - suffix_len; + } + + memcpy(out_body, full_url + prefix_len, *out_body_len); + + return 0; +} diff --git a/lib/bt/host/nimble/nimble/apps/btshell/syscfg.yml b/lib/bt/host/nimble/nimble/apps/btshell/syscfg.yml new file mode 100644 index 00000000..1535b336 --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/btshell/syscfg.yml @@ -0,0 +1,45 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +syscfg.defs: + BTSHELL_ANS: + description: Include support for the alert notification service. + value: 1 + +syscfg.vals: + # Enable the shell task. + SHELL_TASK: 1 + + # Set log level to info (disable debug logging). + LOG_LEVEL: 1 + + # Disable security manager (pairing and bonding). + BLE_SM_LEGACY: 0 + BLE_SM_SC: 0 + + # Default task settings + OS_MAIN_STACK_SIZE: 512 + + # SMP is not supported in this app, so disable smp-over-shell. + SHELL_MGMT: 0 + + # Whether to save data to sys/config, or just keep it in RAM. + BLE_STORE_CONFIG_PERSIST: 0 + +syscfg.vals.BLE_MESH: + MSYS_1_BLOCK_COUNT: 16 diff --git a/lib/bt/host/nimble/nimble/apps/bttester/README b/lib/bt/host/nimble/nimble/apps/bttester/README new file mode 100644 index 00000000..29db2eba --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/bttester/README @@ -0,0 +1,14 @@ +Title: Bluetooth tester application + +Description: + +Tester application uses binary protocol to control Mynewt Nimble stack +and is aimed at automated testing. It uses Bluetooth Testing Protocol (BTP) +to drive Bluetooth stack. BTP commands and events are received and buffered for +further processing. +-------------------------------------------------------------------------------- +Supported Profiles: + +GAP, GATT, SM, L2CAP, MESH +-------------------------------------------------------------------------------- + diff --git a/lib/bt/host/nimble/nimble/apps/bttester/pkg.yml b/lib/bt/host/nimble/nimble/apps/bttester/pkg.yml new file mode 100644 index 00000000..219e5298 --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/bttester/pkg.yml @@ -0,0 +1,42 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +pkg.name: apps/bttester +pkg.type: app +pkg.description: Bluetooth tester application +pkg.author: "Apache Mynewt " +pkg.homepage: "http://mynewt.apache.org/" +pkg.keywords: + +pkg.deps: + - "@apache-mynewt-core/kernel/os" + - "@apache-mynewt-core/sys/console/full" + - "@apache-mynewt-core/sys/log/full" + - "@apache-mynewt-core/sys/log/modlog" + - "@apache-mynewt-core/sys/stats/full" + - "@apache-mynewt-core/sys/shell" + - "@apache-mynewt-nimble/nimble/host" + - "@apache-mynewt-nimble/nimble/host/util" + - "@apache-mynewt-nimble/nimble/host/services/gap" + - "@apache-mynewt-nimble/nimble/host/services/gatt" + - "@apache-mynewt-nimble/nimble/host/services/dis" + - "@apache-mynewt-nimble/nimble/host/store/config" + - "@apache-mynewt-core/hw/drivers/uart" + - "@apache-mynewt-core/hw/drivers/rtt" + diff --git a/lib/bt/host/nimble/nimble/apps/bttester/src/atomic.h b/lib/bt/host/nimble/nimble/apps/bttester/src/atomic.h new file mode 100644 index 00000000..66283e9a --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/bttester/src/atomic.h @@ -0,0 +1,405 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* atomic operations */ + +/* + * Copyright (c) 1997-2015, Wind River Systems, Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __ATOMIC_H__ +#define __ATOMIC_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +typedef int atomic_t; +typedef atomic_t atomic_val_t; + +/** + * @defgroup atomic_apis Atomic Services APIs + * @ingroup kernel_apis + * @{ + */ + +/** + * @brief Atomic compare-and-set. + * + * This routine performs an atomic compare-and-set on @a target. If the current + * value of @a target equals @a old_value, @a target is set to @a new_value. + * If the current value of @a target does not equal @a old_value, @a target + * is left unchanged. + * + * @param target Address of atomic variable. + * @param old_value Original value to compare against. + * @param new_value New value to store. + * @return 1 if @a new_value is written, 0 otherwise. + */ +static inline int atomic_cas(atomic_t *target, atomic_val_t old_value, + atomic_val_t new_value) +{ + return __atomic_compare_exchange_n(target, &old_value, new_value, + 0, __ATOMIC_SEQ_CST, + __ATOMIC_SEQ_CST); +} + +/** + * + * @brief Atomic addition. + * + * This routine performs an atomic addition on @a target. + * + * @param target Address of atomic variable. + * @param value Value to add. + * + * @return Previous value of @a target. + */ +static inline atomic_val_t atomic_add(atomic_t *target, atomic_val_t value) +{ + return __atomic_fetch_add(target, value, __ATOMIC_SEQ_CST); +} + +/** + * + * @brief Atomic subtraction. + * + * This routine performs an atomic subtraction on @a target. + * + * @param target Address of atomic variable. + * @param value Value to subtract. + * + * @return Previous value of @a target. + */ + +static inline atomic_val_t atomic_sub(atomic_t *target, atomic_val_t value) +{ + return __atomic_fetch_sub(target, value, __ATOMIC_SEQ_CST); +} + +/** + * + * @brief Atomic increment. + * + * This routine performs an atomic increment by 1 on @a target. + * + * @param target Address of atomic variable. + * + * @return Previous value of @a target. + */ + +static inline atomic_val_t atomic_inc(atomic_t *target) +{ + return atomic_add(target, 1); +} + +/** + * + * @brief Atomic decrement. + * + * This routine performs an atomic decrement by 1 on @a target. + * + * @param target Address of atomic variable. + * + * @return Previous value of @a target. + */ + +static inline atomic_val_t atomic_dec(atomic_t *target) +{ + return atomic_sub(target, 1); +} + +/** + * + * @brief Atomic get. + * + * This routine performs an atomic read on @a target. + * + * @param target Address of atomic variable. + * + * @return Value of @a target. + */ + +static inline atomic_val_t atomic_get(const atomic_t *target) +{ + return __atomic_load_n(target, __ATOMIC_SEQ_CST); +} + +/** + * + * @brief Atomic get-and-set. + * + * This routine atomically sets @a target to @a value and returns + * the previous value of @a target. + * + * @param target Address of atomic variable. + * @param value Value to write to @a target. + * + * @return Previous value of @a target. + */ + +static inline atomic_val_t atomic_set(atomic_t *target, atomic_val_t value) +{ + /* This builtin, as described by Intel, is not a traditional + * test-and-set operation, but rather an atomic exchange operation. It + * writes value into *ptr, and returns the previous contents of *ptr. + */ + return __atomic_exchange_n(target, value, __ATOMIC_SEQ_CST); +} + +/** + * + * @brief Atomic clear. + * + * This routine atomically sets @a target to zero and returns its previous + * value. (Hence, it is equivalent to atomic_set(target, 0).) + * + * @param target Address of atomic variable. + * + * @return Previous value of @a target. + */ + +static inline atomic_val_t atomic_clear(atomic_t *target) +{ + return atomic_set(target, 0); +} + +/** + * + * @brief Atomic bitwise inclusive OR. + * + * This routine atomically sets @a target to the bitwise inclusive OR of + * @a target and @a value. + * + * @param target Address of atomic variable. + * @param value Value to OR. + * + * @return Previous value of @a target. + */ + +static inline atomic_val_t atomic_or(atomic_t *target, atomic_val_t value) +{ + return __atomic_fetch_or(target, value, __ATOMIC_SEQ_CST); +} + +/** + * + * @brief Atomic bitwise exclusive OR (XOR). + * + * This routine atomically sets @a target to the bitwise exclusive OR (XOR) of + * @a target and @a value. + * + * @param target Address of atomic variable. + * @param value Value to XOR + * + * @return Previous value of @a target. + */ + +static inline atomic_val_t atomic_xor(atomic_t *target, atomic_val_t value) +{ + return __atomic_fetch_xor(target, value, __ATOMIC_SEQ_CST); +} + +/** + * + * @brief Atomic bitwise AND. + * + * This routine atomically sets @a target to the bitwise AND of @a target + * and @a value. + * + * @param target Address of atomic variable. + * @param value Value to AND. + * + * @return Previous value of @a target. + */ + +static inline atomic_val_t atomic_and(atomic_t *target, atomic_val_t value) +{ + return __atomic_fetch_and(target, value, __ATOMIC_SEQ_CST); +} + +/** + * + * @brief Atomic bitwise NAND. + * + * This routine atomically sets @a target to the bitwise NAND of @a target + * and @a value. (This operation is equivalent to target = ~(target & value).) + * + * @param target Address of atomic variable. + * @param value Value to NAND. + * + * @return Previous value of @a target. + */ + +static inline atomic_val_t atomic_nand(atomic_t *target, atomic_val_t value) +{ + return __atomic_fetch_nand(target, value, __ATOMIC_SEQ_CST); +} + + /** + * @brief Initialize an atomic variable. + * + * This macro can be used to initialize an atomic variable. For example, + * @code atomic_t my_var = ATOMIC_INIT(75); @endcode + * + * @param i Value to assign to atomic variable. + */ +#define ATOMIC_INIT(i) (i) + + /** + * @cond INTERNAL_HIDDEN + */ + +#define ATOMIC_BITS (sizeof(atomic_val_t) * 8) +#define ATOMIC_MASK(bit) (1 << ((bit) & (ATOMIC_BITS - 1))) +#define ATOMIC_ELEM(addr, bit) ((addr) + ((bit) / ATOMIC_BITS)) + + /** + * INTERNAL_HIDDEN @endcond + */ + + /** + * @brief Define an array of atomic variables. + * + * This macro defines an array of atomic variables containing at least + * @a num_bits bits. + * + * @note + * If used from file scope, the bits of the array are initialized to zero; + * if used from within a function, the bits are left uninitialized. + * + * @param name Name of array of atomic variables. + * @param num_bits Number of bits needed. + */ +#define ATOMIC_DEFINE(name, num_bits) \ + atomic_t name[1 + ((num_bits) - 1) / ATOMIC_BITS] + + /** + * @brief Atomically test a bit. + * + * This routine tests whether bit number @a bit of @a target is set or not. + * The target may be a single atomic variable or an array of them. + * + * @param target Address of atomic variable or array. + * @param bit Bit number (starting from 0). + * + * @return 1 if the bit was set, 0 if it wasn't. + */ + static inline int + atomic_test_bit(const atomic_t *target, int bit) + { + atomic_val_t val = atomic_get(ATOMIC_ELEM(target, bit)); + + return (1 & (val >> (bit & (ATOMIC_BITS - 1)))); + } + + /** + * @brief Atomically test and clear a bit. + * + * Atomically clear bit number @a bit of @a target and return its old value. + * The target may be a single atomic variable or an array of them. + * + * @param target Address of atomic variable or array. + * @param bit Bit number (starting from 0). + * + * @return 1 if the bit was set, 0 if it wasn't. + */ + static inline int + atomic_test_and_clear_bit(atomic_t *target, int bit) + { + atomic_val_t mask = ATOMIC_MASK(bit); + atomic_val_t old; + + old = atomic_and(ATOMIC_ELEM(target, bit), ~mask); + + return (old & mask) != 0; + } + + /** + * @brief Atomically set a bit. + * + * Atomically set bit number @a bit of @a target and return its old value. + * The target may be a single atomic variable or an array of them. + * + * @param target Address of atomic variable or array. + * @param bit Bit number (starting from 0). + * + * @return 1 if the bit was set, 0 if it wasn't. + */ + static inline int + atomic_test_and_set_bit(atomic_t *target, int bit) + { + atomic_val_t mask = ATOMIC_MASK(bit); + atomic_val_t old; + + old = atomic_or(ATOMIC_ELEM(target, bit), mask); + + return (old & mask) != 0; + } + + /** + * @brief Atomically clear a bit. + * + * Atomically clear bit number @a bit of @a target. + * The target may be a single atomic variable or an array of them. + * + * @param target Address of atomic variable or array. + * @param bit Bit number (starting from 0). + * + * @return N/A + */ + static inline void + atomic_clear_bit(atomic_t *target, int bit) + { + atomic_val_t mask = ATOMIC_MASK(bit); + + atomic_and(ATOMIC_ELEM(target, bit), ~mask); + } + + /** + * @brief Atomically set a bit. + * + * Atomically set bit number @a bit of @a target. + * The target may be a single atomic variable or an array of them. + * + * @param target Address of atomic variable or array. + * @param bit Bit number (starting from 0). + * + * @return N/A + */ + static inline void + atomic_set_bit(atomic_t *target, int bit) + { + atomic_val_t mask = ATOMIC_MASK(bit); + + atomic_or(ATOMIC_ELEM(target, bit), mask); + } + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + +#endif /* __ATOMIC_H__ */ diff --git a/lib/bt/host/nimble/nimble/apps/bttester/src/bttester.c b/lib/bt/host/nimble/nimble/apps/bttester/src/bttester.c new file mode 100644 index 00000000..76aebbf1 --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/bttester/src/bttester.c @@ -0,0 +1,379 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* bttester.c - Bluetooth Tester */ + +/* + * Copyright (c) 2015-2016 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +#include "syscfg/syscfg.h" +#include "console/console.h" + +#include "bttester_pipe.h" +#include "bttester.h" + +#define CMD_QUEUED 2 + +static struct os_eventq avail_queue; +static struct os_eventq *cmds_queue; +static struct os_event bttester_ev[CMD_QUEUED]; + +struct btp_buf { + struct os_event *ev; + union { + uint8_t data[BTP_MTU]; + struct btp_hdr hdr; + }; +}; + +static struct btp_buf cmd_buf[CMD_QUEUED]; + +static void supported_commands(uint8_t *data, uint16_t len) +{ + uint8_t buf[1]; + struct core_read_supported_commands_rp *rp = (void *) buf; + + memset(buf, 0, sizeof(buf)); + + tester_set_bit(buf, CORE_READ_SUPPORTED_COMMANDS); + tester_set_bit(buf, CORE_READ_SUPPORTED_SERVICES); + tester_set_bit(buf, CORE_REGISTER_SERVICE); + tester_set_bit(buf, CORE_UNREGISTER_SERVICE); + + tester_send(BTP_SERVICE_ID_CORE, CORE_READ_SUPPORTED_COMMANDS, + BTP_INDEX_NONE, (uint8_t *) rp, sizeof(buf)); +} + +static void supported_services(uint8_t *data, uint16_t len) +{ + uint8_t buf[1]; + struct core_read_supported_services_rp *rp = (void *) buf; + + memset(buf, 0, sizeof(buf)); + + tester_set_bit(buf, BTP_SERVICE_ID_CORE); + tester_set_bit(buf, BTP_SERVICE_ID_GAP); + tester_set_bit(buf, BTP_SERVICE_ID_GATT); +#if MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM) + tester_set_bit(buf, BTP_SERVICE_ID_L2CAP); +#endif /* MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM) */ +#if MYNEWT_VAL(BLE_MESH) + tester_set_bit(buf, BTP_SERVICE_ID_MESH); +#endif /* MYNEWT_VAL(BLE_MESH) */ + tester_set_bit(buf, BTP_SERVICE_ID_GATTC); + + tester_send(BTP_SERVICE_ID_CORE, CORE_READ_SUPPORTED_SERVICES, + BTP_INDEX_NONE, (uint8_t *) rp, sizeof(buf)); +} + +static void register_service(uint8_t *data, uint16_t len) +{ + struct core_register_service_cmd *cmd = (void *) data; + uint8_t status; + + switch (cmd->id) { + case BTP_SERVICE_ID_GAP: + status = tester_init_gap(); + /* Rsp with success status will be handled by bt enable cb */ + if (status == BTP_STATUS_FAILED) { + goto rsp; + } + return; + case BTP_SERVICE_ID_GATT: + status = tester_init_gatt(); + break; +#if MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM) + case BTP_SERVICE_ID_L2CAP: + status = tester_init_l2cap(); + break; +#endif /* MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM) */ +#if MYNEWT_VAL(BLE_MESH) + case BTP_SERVICE_ID_MESH: + status = tester_init_mesh(); + break; +#endif /* MYNEWT_VAL(BLE_MESH) */ + default: + status = BTP_STATUS_FAILED; + break; + } + +rsp: + tester_rsp(BTP_SERVICE_ID_CORE, CORE_REGISTER_SERVICE, BTP_INDEX_NONE, + status); +} + +static void unregister_service(uint8_t *data, uint16_t len) +{ + struct core_unregister_service_cmd *cmd = (void *) data; + uint8_t status; + + switch (cmd->id) { + case BTP_SERVICE_ID_GAP: + status = tester_unregister_gap(); + break; + case BTP_SERVICE_ID_GATT: + status = tester_unregister_gatt(); + break; +#if MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM) + case BTP_SERVICE_ID_L2CAP: + status = tester_unregister_l2cap(); + break; +#endif /* MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM) */ +#if MYNEWT_VAL(BLE_MESH) + case BTP_SERVICE_ID_MESH: + status = tester_unregister_mesh(); + break; +#endif /* MYNEWT_VAL(BLE_MESH) */ + default: + status = BTP_STATUS_FAILED; + break; + } + + tester_rsp(BTP_SERVICE_ID_CORE, CORE_UNREGISTER_SERVICE, BTP_INDEX_NONE, + status); +} + +static void handle_core(uint8_t opcode, uint8_t index, uint8_t *data, + uint16_t len) +{ + if (index != BTP_INDEX_NONE) { + tester_rsp(BTP_SERVICE_ID_CORE, opcode, index, + BTP_STATUS_FAILED); + return; + } + + switch (opcode) { + case CORE_READ_SUPPORTED_COMMANDS: + supported_commands(data, len); + return; + case CORE_READ_SUPPORTED_SERVICES: + supported_services(data, len); + return; + case CORE_REGISTER_SERVICE: + register_service(data, len); + return; + case CORE_UNREGISTER_SERVICE: + unregister_service(data, len); + return; + default: + tester_rsp(BTP_SERVICE_ID_CORE, opcode, BTP_INDEX_NONE, + BTP_STATUS_UNKNOWN_CMD); + return; + } +} + +static void cmd_handler(struct os_event *ev) +{ + uint16_t len; + struct btp_buf *cmd; + + if (!ev || !ev->ev_arg) { + return; + } + + cmd = ev->ev_arg; + + len = sys_le16_to_cpu(cmd->hdr.len); + if (MYNEWT_VAL(BTTESTER_BTP_LOG)) { + console_printf("[DBG] received %d bytes: %s\n", + sizeof(cmd->hdr) + len, + bt_hex(cmd->data, + sizeof(cmd->hdr) + len)); + } + + /* TODO + * verify if service is registered before calling handler + */ + + switch (cmd->hdr.service) { + case BTP_SERVICE_ID_CORE: + handle_core(cmd->hdr.opcode, cmd->hdr.index, + cmd->hdr.data, len); + break; + case BTP_SERVICE_ID_GAP: + tester_handle_gap(cmd->hdr.opcode, cmd->hdr.index, + cmd->hdr.data, len); + break; + case BTP_SERVICE_ID_GATT: + tester_handle_gatt(cmd->hdr.opcode, cmd->hdr.index, + cmd->hdr.data, len); + break; +#if MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM) + case BTP_SERVICE_ID_L2CAP: + tester_handle_l2cap(cmd->hdr.opcode, cmd->hdr.index, + cmd->hdr.data, len); + break; +#endif /* MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM) */ +#if MYNEWT_VAL(BLE_MESH) + case BTP_SERVICE_ID_MESH: + tester_handle_mesh(cmd->hdr.opcode, cmd->hdr.index, + cmd->hdr.data, len); + break; +#endif /* MYNEWT_VAL(BLE_MESH) */ + case BTP_SERVICE_ID_GATTC: + tester_handle_gattc(cmd->hdr.opcode, cmd->hdr.index, + cmd->hdr.data, len); + break; + default: + tester_rsp(cmd->hdr.service, cmd->hdr.opcode, + cmd->hdr.index, BTP_STATUS_FAILED); + break; + } + + os_eventq_put(&avail_queue, ev); +} + +static uint8_t *recv_cb(uint8_t *buf, size_t *off) +{ + struct btp_hdr *cmd = (void *) buf; + struct os_event *new_ev; + struct btp_buf *new_buf, *old_buf; + uint16_t len; + + if (*off < sizeof(*cmd)) { + return buf; + } + + len = sys_le16_to_cpu(cmd->len); + if (len > BTP_MTU - sizeof(*cmd)) { + *off = 0; + return buf; + } + + if (*off < sizeof(*cmd) + len) { + return buf; + } + + new_ev = os_eventq_get_no_wait(&avail_queue); + if (!new_ev) { + SYS_LOG_ERR("BT tester: RX overflow"); + *off = 0; + return buf; + } + + old_buf = CONTAINER_OF(buf, struct btp_buf, data); + os_eventq_put(cmds_queue, old_buf->ev); + + new_buf = new_ev->ev_arg; + *off = 0; + return new_buf->data; +} + +static void avail_queue_init(void) +{ + int i; + + os_eventq_init(&avail_queue); + + for (i = 0; i < CMD_QUEUED; i++) { + cmd_buf[i].ev = &bttester_ev[i]; + bttester_ev[i].ev_cb = cmd_handler; + bttester_ev[i].ev_arg = &cmd_buf[i]; + + os_eventq_put(&avail_queue, &bttester_ev[i]); + } +} + +void bttester_evq_set(struct os_eventq *evq) +{ + cmds_queue = evq; +} + +void tester_init(void) +{ + struct os_event *ev; + struct btp_buf *buf; + + avail_queue_init(); + bttester_evq_set(os_eventq_dflt_get()); + + ev = os_eventq_get(&avail_queue); + buf = ev->ev_arg; + + if (bttester_pipe_init()) { + SYS_LOG_ERR("Failed to initialize pipe"); + return; + } + + bttester_pipe_register(buf->data, BTP_MTU, recv_cb); + + tester_send(BTP_SERVICE_ID_CORE, CORE_EV_IUT_READY, BTP_INDEX_NONE, + NULL, 0); +} + +void tester_send(uint8_t service, uint8_t opcode, uint8_t index, uint8_t *data, + size_t len) +{ + struct btp_hdr msg; + + msg.service = service; + msg.opcode = opcode; + msg.index = index; + msg.len = len; + + bttester_pipe_send((uint8_t *)&msg, sizeof(msg)); + if (data && len) { + bttester_pipe_send(data, len); + } + + if (MYNEWT_VAL(BTTESTER_BTP_LOG)) { + console_printf("[DBG] send %d bytes hdr: %s\n", sizeof(msg), + bt_hex((char *) &msg, sizeof(msg))); + if (data && len) { + console_printf("[DBG] send %d bytes data: %s\n", len, + bt_hex((char *) data, len)); + } + } +} + +void tester_send_buf(uint8_t service, uint8_t opcode, uint8_t index, + struct os_mbuf *data) +{ + struct btp_hdr msg; + + msg.service = service; + msg.opcode = opcode; + msg.index = index; + msg.len = os_mbuf_len(data); + + bttester_pipe_send((uint8_t *)&msg, sizeof(msg)); + if (data && msg.len) { + bttester_pipe_send_buf(data); + } +} + +void tester_rsp(uint8_t service, uint8_t opcode, uint8_t index, uint8_t status) +{ + struct btp_status s; + + if (status == BTP_STATUS_SUCCESS) { + tester_send(service, opcode, index, NULL, 0); + return; + } + + s.code = status; + tester_send(service, BTP_STATUS, index, (uint8_t *) &s, sizeof(s)); +} diff --git a/lib/bt/host/nimble/nimble/apps/bttester/src/bttester.h b/lib/bt/host/nimble/nimble/apps/bttester/src/bttester.h new file mode 100644 index 00000000..641db8bd --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/bttester/src/bttester.h @@ -0,0 +1,1321 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* bttester.h - Bluetooth tester headers */ + +/* + * Copyright (C) 2021 Codecoup + * Copyright (c) 2015-2016 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __BTTESTER_H__ +#define __BTTESTER_H__ + +#include "syscfg/syscfg.h" +#include "host/ble_gatt.h" + +#if MYNEWT_VAL(BLE_MESH) +#include "mesh/glue.h" +#else +#include "glue.h" +#endif + +#define BTP_MTU MYNEWT_VAL(BTTESTER_BTP_DATA_SIZE_MAX) +#define BTP_DATA_MAX_SIZE (BTP_MTU - sizeof(struct btp_hdr)) + +#define BTP_INDEX_NONE 0xff + +#define BTP_SERVICE_ID_CORE 0 +#define BTP_SERVICE_ID_GAP 1 +#define BTP_SERVICE_ID_GATT 2 +#define BTP_SERVICE_ID_L2CAP 3 +#define BTP_SERVICE_ID_MESH 4 +#define BTP_SERVICE_ID_GATTC 6 + +#define BTP_STATUS_SUCCESS 0x00 +#define BTP_STATUS_FAILED 0x01 +#define BTP_STATUS_UNKNOWN_CMD 0x02 +#define BTP_STATUS_NOT_READY 0x03 + +#define SYS_LOG_DBG(fmt, ...) \ + if (MYNEWT_VAL(BTTESTER_DEBUG)) { \ + console_printf("[DBG] %s: " fmt "\n", \ + __func__, ## __VA_ARGS__); \ + } +#define SYS_LOG_INF(fmt, ...) console_printf("[INF] %s: " fmt "\n", \ + __func__, ## __VA_ARGS__); +#define SYS_LOG_ERR(fmt, ...) console_printf("[WRN] %s: " fmt "\n", \ + __func__, ## __VA_ARGS__); + +#define SYS_LOG_LEVEL SYS_LOG_LEVEL_DEBUG +#define SYS_LOG_DOMAIN "bttester" + +#define sys_cpu_to_le32 htole32 +#define sys_le32_to_cpu le32toh +#define sys_cpu_to_le16 htole16 + +struct btp_hdr { + uint8_t service; + uint8_t opcode; + uint8_t index; + uint16_t len; + uint8_t data[0]; +} __packed; + +#define BTP_STATUS 0x00 +struct btp_status { + uint8_t code; +} __packed; + +/* Core Service */ +#define CORE_READ_SUPPORTED_COMMANDS 0x01 +struct core_read_supported_commands_rp { + uint8_t data[0]; +} __packed; + +#define CORE_READ_SUPPORTED_SERVICES 0x02 +struct core_read_supported_services_rp { + uint8_t data[0]; +} __packed; + +#define CORE_REGISTER_SERVICE 0x03 +struct core_register_service_cmd { + uint8_t id; +} __packed; + +#define CORE_UNREGISTER_SERVICE 0x04 +struct core_unregister_service_cmd { + uint8_t id; +} __packed; + +/* events */ +#define CORE_EV_IUT_READY 0x80 + +/* GAP Service */ +/* commands */ +#define GAP_READ_SUPPORTED_COMMANDS 0x01 +struct gap_read_supported_commands_rp { + uint8_t data[0]; +} __packed; + +#define GAP_READ_CONTROLLER_INDEX_LIST 0x02 +struct gap_read_controller_index_list_rp { + uint8_t num; + uint8_t index[0]; +} __packed; + +#define GAP_SETTINGS_POWERED 0 +#define GAP_SETTINGS_CONNECTABLE 1 +#define GAP_SETTINGS_FAST_CONNECTABLE 2 +#define GAP_SETTINGS_DISCOVERABLE 3 +#define GAP_SETTINGS_BONDABLE 4 +#define GAP_SETTINGS_LINK_SEC_3 5 +#define GAP_SETTINGS_SSP 6 +#define GAP_SETTINGS_BREDR 7 +#define GAP_SETTINGS_HS 8 +#define GAP_SETTINGS_LE 9 +#define GAP_SETTINGS_ADVERTISING 10 +#define GAP_SETTINGS_SC 11 +#define GAP_SETTINGS_DEBUG_KEYS 12 +#define GAP_SETTINGS_PRIVACY 13 +#define GAP_SETTINGS_CONTROLLER_CONFIG 14 +#define GAP_SETTINGS_STATIC_ADDRESS 15 + +#define GAP_READ_CONTROLLER_INFO 0x03 +struct gap_read_controller_info_rp { + uint8_t address[6]; + uint32_t supported_settings; + uint32_t current_settings; + uint8_t cod[3]; + uint8_t name[249]; + uint8_t short_name[11]; +} __packed; + +#define GAP_RESET 0x04 +struct gap_reset_rp { + uint32_t current_settings; +} __packed; + +#define GAP_SET_POWERED 0x05 +struct gap_set_powered_cmd { + uint8_t powered; +} __packed; +struct gap_set_powered_rp { + uint32_t current_settings; +} __packed; + +#define GAP_SET_CONNECTABLE 0x06 +struct gap_set_connectable_cmd { + uint8_t connectable; +} __packed; +struct gap_set_connectable_rp { + uint32_t current_settings; +} __packed; + +#define GAP_SET_FAST_CONNECTABLE 0x07 +struct gap_set_fast_connectable_cmd { + uint8_t fast_connectable; +} __packed; +struct gap_set_fast_connectable_rp { + uint32_t current_settings; +} __packed; + +#define GAP_NON_DISCOVERABLE 0x00 +#define GAP_GENERAL_DISCOVERABLE 0x01 +#define GAP_LIMITED_DISCOVERABLE 0x02 + +#define GAP_SET_DISCOVERABLE 0x08 +struct gap_set_discoverable_cmd { + uint8_t discoverable; +} __packed; +struct gap_set_discoverable_rp { + uint32_t current_settings; +} __packed; + +#define GAP_SET_BONDABLE 0x09 +struct gap_set_bondable_cmd { + uint8_t bondable; +} __packed; +struct gap_set_bondable_rp { + uint32_t current_settings; +} __packed; + +#define GAP_START_ADVERTISING 0x0a +struct gap_start_advertising_cmd { + uint8_t adv_data_len; + uint8_t scan_rsp_len; + uint8_t adv_data[0]; + uint8_t scan_rsp[0]; +} __packed; +struct gap_start_advertising_rp { + uint32_t current_settings; +} __packed; + +#define GAP_STOP_ADVERTISING 0x0b +struct gap_stop_advertising_rp { + uint32_t current_settings; +} __packed; + +#define GAP_DISCOVERY_FLAG_LE 0x01 +#define GAP_DISCOVERY_FLAG_BREDR 0x02 +#define GAP_DISCOVERY_FLAG_LIMITED 0x04 +#define GAP_DISCOVERY_FLAG_LE_ACTIVE_SCAN 0x08 +#define GAP_DISCOVERY_FLAG_LE_OBSERVE 0x10 + +#define GAP_START_DISCOVERY 0x0c +struct gap_start_discovery_cmd { + uint8_t flags; +} __packed; + +#define GAP_STOP_DISCOVERY 0x0d + +#define GAP_CONNECT 0x0e +struct gap_connect_cmd { + uint8_t address_type; + uint8_t address[6]; +} __packed; + +#define GAP_DISCONNECT 0x0f +struct gap_disconnect_cmd { + uint8_t address_type; + uint8_t address[6]; +} __packed; + +#define GAP_IO_CAP_DISPLAY_ONLY 0 +#define GAP_IO_CAP_DISPLAY_YESNO 1 +#define GAP_IO_CAP_KEYBOARD_ONLY 2 +#define GAP_IO_CAP_NO_INPUT_OUTPUT 3 +#define GAP_IO_CAP_KEYBOARD_DISPLAY 4 + +#define GAP_SET_IO_CAP 0x10 +struct gap_set_io_cap_cmd { + uint8_t io_cap; +} __packed; + +#define GAP_PAIR 0x11 +struct gap_pair_cmd { + uint8_t address_type; + uint8_t address[6]; +} __packed; + +#define GAP_UNPAIR 0x12 +struct gap_unpair_cmd { + uint8_t address_type; + uint8_t address[6]; +} __packed; + +#define GAP_PASSKEY_ENTRY 0x13 +struct gap_passkey_entry_cmd { + uint8_t address_type; + uint8_t address[6]; + uint32_t passkey; +} __packed; + +#define GAP_PASSKEY_CONFIRM 0x14 +struct gap_passkey_confirm_cmd { + uint8_t address_type; + uint8_t address[6]; + uint8_t match; +} __packed; + +#define GAP_START_DIRECT_ADV 0x15 +struct gap_start_direct_adv_cmd { + uint8_t address_type; + uint8_t address[6]; + uint8_t high_duty; +} __packed; + +#define GAP_CONN_PARAM_UPDATE 0x16 +struct gap_conn_param_update_cmd { + uint8_t address_type; + uint8_t address[6]; + uint16_t conn_itvl_min; + uint16_t conn_itvl_max; + uint16_t conn_latency; + uint16_t supervision_timeout; +} __packed; + +#define GAP_PAIRING_CONSENT_RSP 0x17 +struct gap_pairing_consent_rsp_cmd { + uint8_t address_type; + uint8_t address[6]; + uint8_t consent; +} __packed; + +#define GAP_OOB_LEGACY_SET_DATA 0x18 +struct gap_oob_legacy_set_data_cmd { + uint8_t oob_data[16]; +} __packed; + +#define GAP_OOB_SC_GET_LOCAL_DATA 0x19 +struct gap_oob_sc_get_local_data_rp { + uint8_t r[16]; + uint8_t c[16]; +} __packed; + +#define GAP_OOB_SC_SET_REMOTE_DATA 0x1a +struct gap_oob_sc_set_remote_data_cmd { + uint8_t r[16]; + uint8_t c[16]; +} __packed; + +#define GAP_SET_MITM 0x1b +struct gap_set_mitm_cmd { + uint8_t mitm; +} __packed; + +#define GAP_SET_FILTER_ACCEPT_LIST 0x1c +struct gap_set_filter_accept_list_cmd { + uint8_t list_len; + ble_addr_t addrs[]; +} __packed; +/* events */ +#define GAP_EV_NEW_SETTINGS 0x80 +struct gap_new_settings_ev { + uint32_t current_settings; +} __packed; + +#define GAP_DEVICE_FOUND_FLAG_RSSI 0x01 +#define GAP_DEVICE_FOUND_FLAG_AD 0x02 +#define GAP_DEVICE_FOUND_FLAG_SD 0x04 + +#define GAP_EV_DEVICE_FOUND 0x81 +struct gap_device_found_ev { + uint8_t address_type; + uint8_t address[6]; + int8_t rssi; + uint8_t flags; + uint16_t eir_data_len; + uint8_t eir_data[0]; +} __packed; + +#define GAP_EV_DEVICE_CONNECTED 0x82 +struct gap_device_connected_ev { + uint8_t address_type; + uint8_t address[6]; + uint16_t conn_itvl; + uint16_t conn_latency; + uint16_t supervision_timeout; +} __packed; + +#define GAP_EV_DEVICE_DISCONNECTED 0x83 +struct gap_device_disconnected_ev { + uint8_t address_type; + uint8_t address[6]; +} __packed; + +#define GAP_EV_PASSKEY_DISPLAY 0x84 +struct gap_passkey_display_ev { + uint8_t address_type; + uint8_t address[6]; + uint32_t passkey; +} __packed; + +#define GAP_EV_PASSKEY_ENTRY_REQ 0x85 +struct gap_passkey_entry_req_ev { + uint8_t address_type; + uint8_t address[6]; +} __packed; + +#define GAP_EV_PASSKEY_CONFIRM_REQ 0x86 +struct gap_passkey_confirm_req_ev { + uint8_t address_type; + uint8_t address[6]; + uint32_t passkey; +} __packed; + +#define GAP_EV_IDENTITY_RESOLVED 0x87 +struct gap_identity_resolved_ev { + uint8_t address_type; + uint8_t address[6]; + uint8_t identity_address_type; + uint8_t identity_address[6]; +} __packed; + +#define GAP_EV_CONN_PARAM_UPDATE 0x88 +struct gap_conn_param_update_ev { + uint8_t address_type; + uint8_t address[6]; + uint16_t conn_itvl; + uint16_t conn_latency; + uint16_t supervision_timeout; +} __packed; + +#define GAP_EV_SEC_LEVEL_CHANGED 0x89 +struct gap_sec_level_changed_ev { + uint8_t address_type; + uint8_t address[6]; + uint8_t level; +} __packed; + +#define GAP_EV_PAIRING_CONSENT_REQ 0x8a +struct gap_pairing_consent_req_ev { + uint8_t address_type; + uint8_t address[6]; +} __packed; + +/* GATT Service */ +/* commands */ +#define GATT_READ_SUPPORTED_COMMANDS 0x01 +struct gatt_read_supported_commands_rp { + uint8_t data[0]; +} __packed; + +#define GATT_SERVICE_PRIMARY 0x00 +#define GATT_SERVICE_SECONDARY 0x01 + +#define GATT_ADD_SERVICE 0x02 +struct gatt_add_service_cmd { + uint8_t type; + uint8_t uuid_length; + uint8_t uuid[0]; +} __packed; +struct gatt_add_service_rp { + uint16_t svc_id; +} __packed; + +#define GATT_ADD_CHARACTERISTIC 0x03 +struct gatt_add_characteristic_cmd { + uint16_t svc_id; + uint8_t properties; + uint8_t permissions; + uint8_t uuid_length; + uint8_t uuid[0]; +} __packed; +struct gatt_add_characteristic_rp { + uint16_t char_id; +} __packed; + +#define GATT_ADD_DESCRIPTOR 0x04 +struct gatt_add_descriptor_cmd { + uint16_t char_id; + uint8_t permissions; + uint8_t uuid_length; + uint8_t uuid[0]; +} __packed; +struct gatt_add_descriptor_rp { + uint16_t desc_id; +} __packed; + +#define GATT_ADD_INCLUDED_SERVICE 0x05 +struct gatt_add_included_service_cmd { + uint16_t svc_id; +} __packed; +struct gatt_add_included_service_rp { + uint16_t included_service_id; +} __packed; + +#define GATT_SET_VALUE 0x06 + struct gatt_set_value_cmd { + uint16_t attr_id; + uint16_t len; + uint8_t value[0]; +} __packed; + +#define GATT_START_SERVER 0x07 +struct gatt_start_server_rp { + uint16_t db_attr_off; + uint8_t db_attr_cnt; +} __packed; + +#define GATT_SET_ENC_KEY_SIZE 0x09 +struct gatt_set_enc_key_size_cmd { + uint16_t attr_id; + uint8_t key_size; +} __packed; + +/* Gatt Client */ +struct gatt_service { + uint16_t start_handle; + uint16_t end_handle; + uint8_t uuid_length; + uint8_t uuid[0]; +} __packed; + +struct gatt_included { + uint16_t included_handle; + struct gatt_service service; +} __packed; + +struct gatt_read_uuid_chr { + uint16_t handle; + uint8_t data[0]; +} __packed; + +struct gatt_characteristic { + uint16_t characteristic_handle; + uint16_t value_handle; + uint8_t properties; + uint8_t uuid_length; + uint8_t uuid[0]; +} __packed; + +struct gatt_descriptor { + uint16_t descriptor_handle; + uint8_t uuid_length; + uint8_t uuid[0]; +} __packed; + +#define GATT_EXCHANGE_MTU 0x0a + +#define GATT_DISC_ALL_PRIM_SVCS 0x0b +struct gatt_disc_all_prim_svcs_cmd { + uint8_t address_type; + uint8_t address[6]; +} __packed; +struct gatt_disc_all_prim_svcs_rp { + uint8_t services_count; + struct gatt_service services[0]; +} __packed; + +#define GATT_DISC_PRIM_UUID 0x0c +struct gatt_disc_prim_uuid_cmd { + uint8_t address_type; + uint8_t address[6]; + uint8_t uuid_length; + uint8_t uuid[0]; +} __packed; +struct gatt_disc_prim_uuid_rp { + uint8_t services_count; + struct gatt_service services[0]; +} __packed; + +#define GATT_FIND_INCLUDED 0x0d +struct gatt_find_included_cmd { + uint8_t address_type; + uint8_t address[6]; + uint16_t start_handle; + uint16_t end_handle; +} __packed; +struct gatt_find_included_rp { + uint8_t services_count; + struct gatt_included included[0]; +} __packed; + +#define GATT_DISC_ALL_CHRC 0x0e +struct gatt_disc_all_chrc_cmd { + uint8_t address_type; + uint8_t address[6]; + uint16_t start_handle; + uint16_t end_handle; +} __packed; +struct gatt_disc_chrc_rp { + uint8_t characteristics_count; + struct gatt_characteristic characteristics[0]; +} __packed; + +#define GATT_DISC_CHRC_UUID 0x0f +struct gatt_disc_chrc_uuid_cmd { + uint8_t address_type; + uint8_t address[6]; + uint16_t start_handle; + uint16_t end_handle; + uint8_t uuid_length; + uint8_t uuid[0]; +} __packed; + +#define GATT_DISC_ALL_DESC 0x10 +struct gatt_disc_all_desc_cmd { + uint8_t address_type; + uint8_t address[6]; + uint16_t start_handle; + uint16_t end_handle; +} __packed; +struct gatt_disc_all_desc_rp { + uint8_t descriptors_count; + struct gatt_descriptor descriptors[0]; +} __packed; + +#define GATT_READ 0x11 +struct gatt_read_cmd { + uint8_t address_type; + uint8_t address[6]; + uint16_t handle; +} __packed; +struct gatt_read_rp { + uint8_t att_response; + uint16_t data_length; + uint8_t data[0]; +} __packed; + +#define GATT_READ_UUID 0x12 +struct gatt_read_uuid_cmd { + uint8_t address_type; + uint8_t address[6]; + uint16_t start_handle; + uint16_t end_handle; + uint8_t uuid_length; + uint8_t uuid[0]; +} __packed; + +#define GATT_READ_LONG 0x13 +struct gatt_read_long_cmd { + uint8_t address_type; + uint8_t address[6]; + uint16_t handle; + uint16_t offset; +} __packed; + +#define GATT_READ_MULTIPLE 0x14 +struct gatt_read_multiple_cmd { + uint8_t address_type; + uint8_t address[6]; + uint8_t handles_count; + uint16_t handles[0]; +} __packed; + +#define GATT_WRITE_WITHOUT_RSP 0x15 +struct gatt_write_without_rsp_cmd { + uint8_t address_type; + uint8_t address[6]; + uint16_t handle; + uint16_t data_length; + uint8_t data[0]; +} __packed; + +#define GATT_SIGNED_WRITE_WITHOUT_RSP 0x16 +struct gatt_signed_write_without_rsp_cmd { + uint8_t address_type; + uint8_t address[6]; + uint16_t handle; + uint16_t data_length; + uint8_t data[0]; +} __packed; + +#define GATT_WRITE 0x17 +struct gatt_write_cmd { + uint8_t address_type; + uint8_t address[6]; + uint16_t handle; + uint16_t data_length; + uint8_t data[0]; +} __packed; + +#define GATT_WRITE_LONG 0x18 +struct gatt_write_long_cmd { + uint8_t address_type; + uint8_t address[6]; + uint16_t handle; + uint16_t offset; + uint16_t data_length; + uint8_t data[0]; +} __packed; + +#define GATT_RELIABLE_WRITE 0x19 +struct gatt_reliable_write_cmd { + uint8_t address_type; + uint8_t address[6]; + uint16_t handle; + uint16_t offset; + uint16_t data_length; + uint8_t data[0]; +} __packed; + +#define GATT_CFG_NOTIFY 0x1a +#define GATT_CFG_INDICATE 0x1b +struct gatt_cfg_notify_cmd { + uint8_t address_type; + uint8_t address[6]; + uint8_t enable; + uint16_t ccc_handle; +} __packed; + +#define GATT_GET_ATTRIBUTES 0x1c +struct gatt_get_attributes_cmd { + uint16_t start_handle; + uint16_t end_handle; + uint8_t type_length; + uint8_t type[0]; +} __packed; +struct gatt_get_attributes_rp { + uint8_t attrs_count; + uint8_t attrs[0]; +} __packed; +struct gatt_attr { + uint16_t handle; + uint8_t permission; + uint8_t type_length; + uint8_t type[0]; +} __packed; + +#define GATT_GET_ATTRIBUTE_VALUE 0x1d +struct gatt_get_attribute_value_cmd { + uint8_t address_type; + uint8_t address[6]; + uint16_t handle; +} __packed; +struct gatt_get_attribute_value_rp { + uint8_t att_response; + uint16_t value_length; + uint8_t value[0]; +} __packed; + +#define GATT_CHANGE_DATABASE 0x1e +struct gatt_change_database { + uint16_t start_handle; + uint16_t end_handle; + uint8_t visibility; +} __packed; + +/* GATT events */ +#define GATT_EV_NOTIFICATION 0x80 +struct gatt_notification_ev { + uint8_t address_type; + uint8_t address[6]; + uint8_t type; + uint16_t handle; + uint16_t data_length; + uint8_t data[0]; +} __packed; + +#define GATT_EV_ATTR_VALUE_CHANGED 0x81 +struct gatt_attr_value_changed_ev { + uint16_t handle; + uint16_t data_length; + uint8_t data[0]; +} __packed; + +static inline void tester_set_bit(uint8_t *addr, unsigned int bit) +{ + uint8_t *p = addr + (bit / 8); + + *p |= BIT(bit % 8); +} + +static inline uint8_t tester_test_bit(const uint8_t *addr, unsigned int bit) +{ + const uint8_t *p = addr + (bit / 8); + + return *p & BIT(bit % 8); +} + +/* L2CAP Service */ +/* commands */ +#define L2CAP_READ_SUPPORTED_COMMANDS 0x01 +struct l2cap_read_supported_commands_rp { + uint8_t data[0]; +} __packed; + +#define L2CAP_CONNECT_OPT_ECFC 0x01 +#define L2CAP_CONNECT_OPT_HOLD_CREDIT 0x02 + +#define L2CAP_CONNECT 0x02 +struct l2cap_connect_cmd { + uint8_t address_type; + uint8_t address[6]; + uint16_t psm; + uint16_t mtu; + uint8_t num; + uint8_t options; +} __packed; + +struct l2cap_connect_rp { + uint8_t num; + uint8_t chan_ids[0]; +} __packed; + +#define L2CAP_DISCONNECT 0x03 +struct l2cap_disconnect_cmd { + uint8_t chan_id; +} __packed; + +#define L2CAP_SEND_DATA 0x04 +struct l2cap_send_data_cmd { + uint8_t chan_id; + uint16_t data_len; + uint8_t data[]; +} __packed; + +#define L2CAP_TRANSPORT_BREDR 0x00 +#define L2CAP_TRANSPORT_LE 0x01 + +#define L2CAP_LISTEN 0x05 +struct l2cap_listen_cmd { + uint16_t psm; + uint8_t transport; + uint16_t mtu; + uint16_t response; +} __packed; + +#define L2CAP_ACCEPT_CONNECTION 0x06 +struct l2cap_accept_connection_cmd { + uint8_t chan_id; + uint16_t result; +} __packed; + +#define L2CAP_RECONFIGURE 0x07 +struct l2cap_reconfigure_cmd { + uint8_t address_type; + uint8_t address[6]; + uint16_t mtu; + uint8_t num; + uint8_t idxs[]; +} __packed; + +#define L2CAP_CREDITS 0x08 +struct l2cap_credits_cmd { + uint8_t chan_id; +} __packed; + +/* events */ +#define L2CAP_EV_CONNECTION_REQ 0x80 +struct l2cap_connection_req_ev { + uint8_t chan_id; + uint16_t psm; + uint8_t address_type; + uint8_t address[6]; +} __packed; + +#define L2CAP_EV_CONNECTED 0x81 +struct l2cap_connected_ev { + uint8_t chan_id; + uint16_t psm; + uint16_t peer_mtu; + uint16_t peer_mps; + uint16_t our_mtu; + uint16_t our_mps; + uint8_t address_type; + uint8_t address[6]; +} __packed; + +#define L2CAP_EV_DISCONNECTED 0x82 +struct l2cap_disconnected_ev { + uint16_t result; + uint8_t chan_id; + uint16_t psm; + uint8_t address_type; + uint8_t address[6]; +} __packed; + +#define L2CAP_EV_DATA_RECEIVED 0x83 +struct l2cap_data_received_ev { + uint8_t chan_id; + uint16_t data_length; + uint8_t data[0]; +} __packed; + +#define L2CAP_EV_RECONFIGURED 0x84 +struct l2cap_reconfigured_ev { + uint8_t chan_id; + uint16_t peer_mtu; + uint16_t peer_mps; + uint16_t our_mtu; + uint16_t our_mps; +} __packed; + +/* MESH Service */ +/* commands */ +#define MESH_READ_SUPPORTED_COMMANDS 0x01 +struct mesh_read_supported_commands_rp { + uint8_t data[0]; +} __packed; + +#define MESH_OUT_BLINK BIT(0) +#define MESH_OUT_BEEP BIT(1) +#define MESH_OUT_VIBRATE BIT(2) +#define MESH_OUT_DISPLAY_NUMBER BIT(3) +#define MESH_OUT_DISPLAY_STRING BIT(4) + +#define MESH_IN_PUSH BIT(0) +#define MESH_IN_TWIST BIT(1) +#define MESH_IN_ENTER_NUMBER BIT(2) +#define MESH_IN_ENTER_STRING BIT(3) + +#define MESH_CONFIG_PROVISIONING 0x02 +struct mesh_config_provisioning_cmd { + uint8_t uuid[16]; + uint8_t static_auth[16]; + uint8_t out_size; + uint16_t out_actions; + uint8_t in_size; + uint16_t in_actions; +} __packed; + +#define MESH_PROVISION_NODE 0x03 +struct mesh_provision_node_cmd { + uint8_t net_key[16]; + uint16_t net_key_idx; + uint8_t flags; + uint32_t iv_index; + uint32_t seq_num; + uint16_t addr; + uint8_t dev_key[16]; +} __packed; + +#define MESH_INIT 0x04 +#define MESH_RESET 0x05 +#define MESH_INPUT_NUMBER 0x06 +struct mesh_input_number_cmd { + uint32_t number; +} __packed; + +#define MESH_INPUT_STRING 0x07 +struct mesh_input_string_cmd { + uint8_t string_len; + uint8_t string[0]; +} __packed; + +#define MESH_IVU_TEST_MODE 0x08 +struct mesh_ivu_test_mode_cmd { + uint8_t enable; +} __packed; + +#define MESH_IVU_TOGGLE_STATE 0x09 + +#define MESH_NET_SEND 0x0a +struct mesh_net_send_cmd { + uint8_t ttl; + uint16_t src; + uint16_t dst; + uint8_t payload_len; + uint8_t payload[0]; +} __packed; + +#define MESH_HEALTH_GENERATE_FAULTS 0x0b +struct mesh_health_generate_faults_rp { + uint8_t test_id; + uint8_t cur_faults_count; + uint8_t reg_faults_count; + uint8_t current_faults[0]; + uint8_t registered_faults[0]; +} __packed; + +#define MESH_HEALTH_CLEAR_FAULTS 0x0c + +#define MESH_LPN 0x0d +struct mesh_lpn_set_cmd { + uint8_t enable; +} __packed; + +#define MESH_LPN_POLL 0x0e + +#define MESH_MODEL_SEND 0x0f +struct mesh_model_send_cmd { + uint16_t src; + uint16_t dst; + uint8_t payload_len; + uint8_t payload[0]; +} __packed; + +#define MESH_LPN_SUBSCRIBE 0x10 +struct mesh_lpn_subscribe_cmd { + uint16_t address; +} __packed; + +#define MESH_LPN_UNSUBSCRIBE 0x11 +struct mesh_lpn_unsubscribe_cmd { + uint16_t address; +} __packed; + +#define MESH_RPL_CLEAR 0x12 +#define MESH_PROXY_IDENTITY 0x13 + +/* events */ +#define MESH_EV_OUT_NUMBER_ACTION 0x80 +struct mesh_out_number_action_ev { + uint16_t action; + uint32_t number; +} __packed; + +#define MESH_EV_OUT_STRING_ACTION 0x81 +struct mesh_out_string_action_ev { + uint8_t string_len; + uint8_t string[0]; +} __packed; + +#define MESH_EV_IN_ACTION 0x82 +struct mesh_in_action_ev { + uint16_t action; + uint8_t size; +} __packed; + +#define MESH_EV_PROVISIONED 0x83 + +#define MESH_PROV_BEARER_PB_ADV 0x00 +#define MESH_PROV_BEARER_PB_GATT 0x01 +#define MESH_EV_PROV_LINK_OPEN 0x84 +struct mesh_prov_link_open_ev { + uint8_t bearer; +} __packed; + +#define MESH_EV_PROV_LINK_CLOSED 0x85 +struct mesh_prov_link_closed_ev { + uint8_t bearer; +} __packed; + +#define MESH_EV_NET_RECV 0x86 +struct mesh_net_recv_ev { + uint8_t ttl; + uint8_t ctl; + uint16_t src; + uint16_t dst; + uint8_t payload_len; + uint8_t payload[0]; +} __packed; + +#define MESH_EV_INVALID_BEARER 0x87 +struct mesh_invalid_bearer_ev { + uint8_t opcode; +} __packed; + +#define MESH_EV_INCOMP_TIMER_EXP 0x88 + +#define MESH_EV_LPN_ESTABLISHED 0x8b +struct mesh_lpn_established_ev { + uint16_t net_idx; + uint16_t friend_addr; + uint8_t queue_size; + uint8_t recv_win; +} __packed; + +#define MESH_EV_LPN_TERMINATED 0x8c +struct mesh_lpn_terminated_ev { + uint16_t net_idx; + uint16_t friend_addr; +} __packed; + +#define MESH_EV_LPN_POLLED 0x8d +struct mesh_lpn_polled_ev { + uint16_t net_idx; + uint16_t friend_addr; + uint8_t retry; +} __packed; + +void tester_init(void); +void tester_rsp(uint8_t service, uint8_t opcode, uint8_t index, uint8_t status); +void tester_send(uint8_t service, uint8_t opcode, uint8_t index, uint8_t *data, + size_t len); +void tester_send_buf(uint8_t service, uint8_t opcode, uint8_t index, + struct os_mbuf *buf); + +uint8_t tester_init_gap(void); +uint8_t tester_unregister_gap(void); +void tester_handle_gap(uint8_t opcode, uint8_t index, uint8_t *data, + uint16_t len); +uint8_t tester_init_gatt(void); +uint8_t tester_unregister_gatt(void); +void tester_handle_gatt(uint8_t opcode, uint8_t index, uint8_t *data, + uint16_t len); +void tester_handle_gattc(uint8_t opcode, uint8_t index, uint8_t *data, + uint16_t len); +int tester_gattc_notify_rx_ev(uint16_t conn_handle, uint16_t attr_handle, + uint8_t indication, struct os_mbuf *om); +int tester_gatt_subscribe_ev(uint16_t conn_handle, uint16_t attr_handle, uint8_t reason, + uint8_t prev_notify, uint8_t cur_notify, + uint8_t prev_indicate, uint8_t cur_indicate); + +#if MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM) +uint8_t tester_init_l2cap(void); +uint8_t tester_unregister_l2cap(void); +void tester_handle_l2cap(uint8_t opcode, uint8_t index, uint8_t *data, + uint16_t len); +#endif + +#if MYNEWT_VAL(BLE_MESH) +uint8_t tester_init_mesh(void); +uint8_t tester_unregister_mesh(void); +void tester_handle_mesh(uint8_t opcode, uint8_t index, uint8_t *data, uint16_t len); +#endif /* MYNEWT_VAL(BLE_MESH) */ + +void gatt_svr_register_cb(struct ble_gatt_register_ctxt *ctxt, void *arg); +int gatt_svr_init(void); + +/* GATT Client Service */ +/* commands */ +#define GATTC_READ_SUPPORTED_COMMANDS 0x01 +struct gattc_read_supported_commands_rp { + uint8_t data[0]; +} __packed; + +#define GATTC_EXCHANGE_MTU 0x02 + +#define GATTC_DISC_ALL_PRIM_SVCS 0x03 +struct gattc_disc_all_prim_svcs_cmd { + uint8_t address_type; + uint8_t address[6]; +} __packed; + +#define GATTC_DISC_PRIM_UUID 0x04 +struct gattc_disc_prim_uuid_cmd { + uint8_t address_type; + uint8_t address[6]; + uint8_t uuid_length; + uint8_t uuid[0]; +} __packed; + +#define GATTC_FIND_INCLUDED 0x05 +struct gattc_find_included_cmd { + uint8_t address_type; + uint8_t address[6]; + uint16_t start_handle; + uint16_t end_handle; +} __packed; + +#define GATTC_DISC_ALL_CHRC 0x06 +struct gattc_disc_all_chrc_cmd { + uint8_t address_type; + uint8_t address[6]; + uint16_t start_handle; + uint16_t end_handle; +} __packed; + +#define GATTC_DISC_CHRC_UUID 0x07 +struct gattc_disc_chrc_uuid_cmd { + uint8_t address_type; + uint8_t address[6]; + uint16_t start_handle; + uint16_t end_handle; + uint8_t uuid_length; + uint8_t uuid[0]; +} __packed; + +#define GATTC_DISC_ALL_DESC 0x08 +struct gattc_disc_all_desc_cmd { + uint8_t address_type; + uint8_t address[6]; + uint16_t start_handle; + uint16_t end_handle; +} __packed; + +#define GATTC_READ 0x09 +struct gattc_read_cmd { + uint8_t address_type; + uint8_t address[6]; + uint16_t handle; +} __packed; + +#define GATTC_READ_UUID 0x0a +struct gattc_read_uuid_cmd { + uint8_t address_type; + uint8_t address[6]; + uint16_t start_handle; + uint16_t end_handle; + uint8_t uuid_length; + uint8_t uuid[0]; +} __packed; +struct gattc_read_uuid_rp { + uint8_t address_type; + uint8_t address[6]; + uint8_t status; + uint16_t data_length; + uint8_t value_length; + uint8_t data[0]; +} __packed; + +#define GATTC_READ_LONG 0x0b +struct gattc_read_long_cmd { + uint8_t address_type; + uint8_t address[6]; + uint16_t handle; + uint16_t offset; +} __packed; + +#define GATTC_READ_MULTIPLE 0x0c +struct gattc_read_multiple_cmd { + uint8_t address_type; + uint8_t address[6]; + uint8_t handles_count; + uint16_t handles[0]; +} __packed; + +#define GATTC_WRITE_WITHOUT_RSP 0x0d +struct gattc_write_without_rsp_cmd { + uint8_t address_type; + uint8_t address[6]; + uint16_t handle; + uint16_t data_length; + uint8_t data[0]; +} __packed; + +#define GATTC_SIGNED_WRITE_WITHOUT_RSP 0x0e +struct gattc_signed_write_without_rsp_cmd { + uint8_t address_type; + uint8_t address[6]; + uint16_t handle; + uint16_t data_length; + uint8_t data[0]; +} __packed; + +#define GATTC_WRITE 0x0f +struct gattc_write_cmd { + uint8_t address_type; + uint8_t address[6]; + uint16_t handle; + uint16_t data_length; + uint8_t data[0]; +} __packed; + +#define GATTC_WRITE_LONG 0x10 +struct gattc_write_long_cmd { + uint8_t address_type; + uint8_t address[6]; + uint16_t handle; + uint16_t offset; + uint16_t data_length; + uint8_t data[0]; +} __packed; + +#define GATTC_RELIABLE_WRITE 0x11 +struct gattc_reliable_write_cmd { + uint8_t address_type; + uint8_t address[6]; + uint16_t handle; + uint16_t offset; + uint16_t data_length; + uint8_t data[0]; +} __packed; + +#define GATTC_CFG_NOTIFY 0x12 +#define GATTC_CFG_INDICATE 0x13 +struct gattc_cfg_notify_cmd { + uint8_t address_type; + uint8_t address[6]; + uint8_t enable; + uint16_t ccc_handle; +} __packed; + +/* events */ +#define GATTC_EV_MTU_EXCHANGED 0x80 +struct gattc_exchange_mtu_ev { + uint8_t address_type; + uint8_t address[6]; + uint16_t mtu; +} __packed; + +#define GATTC_DISC_ALL_PRIM_RP 0x81 +struct gattc_disc_prim_svcs_rp { + uint8_t address_type; + uint8_t address[6]; + uint8_t status; + uint8_t services_count; + uint8_t data[0]; +} __packed; + +#define GATTC_DISC_PRIM_UUID_RP 0x82 + +#define GATTC_FIND_INCLUDED_RP 0x83 +struct gattc_find_included_rp { + uint8_t address_type; + uint8_t address[6]; + uint8_t status; + uint8_t services_count; + struct gatt_included included[0]; +} __packed; + +#define GATTC_DISC_ALL_CHRC_RP 0x84 +#define GATTC_DISC_CHRC_UUID_RP 0x85 +struct gattc_disc_chrc_rp { + uint8_t address_type; + uint8_t address[6]; + uint8_t status; + uint8_t characteristics_count; + struct gatt_characteristic characteristics[0]; +} __packed; + +#define GATTC_DISC_ALL_DESC_RP 0x86 +struct gattc_disc_all_desc_rp { + uint8_t address_type; + uint8_t address[6]; + uint8_t status; + uint8_t descriptors_count; + struct gatt_descriptor descriptors[0]; +} __packed; + +#define GATTC_READ_RP 0x87 +#define GATTC_READ_UUID_RP 0x88 +#define GATTC_READ_LONG_RP 0x89 +#define GATTC_READ_MULTIPLE_RP 0x8a +struct gattc_read_rp { + uint8_t address_type; + uint8_t address[6]; + uint8_t status; + uint16_t data_length; + uint8_t data[0]; +} __packed; + +#define GATTC_WRITE_RP 0x8b +struct gattc_write_rp { + uint8_t address_type; + uint8_t address[6]; + uint8_t status; +} __packed; +#define GATTC_WRITE_LONG_RP 0x8c +#define GATTC_RELIABLE_WRITE_RP 0x8d +#define GATTC_RELIABLE_WRITE_RP 0x8d +#define GATTC_CFG_NOTIFY_RP 0x8e +#define GATTC_CFG_INDICATE_RP 0x8f +struct subscribe_rp { + uint8_t address_type; + uint8_t address[6]; + uint8_t status; +} __packed; + +#define GATTC_EV_NOTIFICATION_RXED 0x90 +struct gattc_notification_ev { + uint8_t address_type; + uint8_t address[6]; + uint8_t type; + uint16_t handle; + uint16_t data_length; + uint8_t data[0]; +} __packed; + +#endif /* __BTTESTER_H__ */ diff --git a/lib/bt/host/nimble/nimble/apps/bttester/src/bttester_pipe.h b/lib/bt/host/nimble/nimble/apps/bttester/src/bttester_pipe.h new file mode 100644 index 00000000..64b63cd6 --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/bttester/src/bttester_pipe.h @@ -0,0 +1,40 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef __BTTESTER_PIPE_H__ +#define __BTTESTER_PIPE_H__ + +#include +#include "bttester.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef uint8_t *(*bttester_pipe_recv_cb)(uint8_t *buf, size_t *off); +void bttester_pipe_register(uint8_t *buf, size_t len, bttester_pipe_recv_cb cb); +int bttester_pipe_send(const uint8_t *data, int len); +int bttester_pipe_send_buf(struct os_mbuf *buf); +int bttester_pipe_init(void); + +#ifdef __cplusplus +} +#endif + +#endif /* __BTTESTER_PIPE_H__ */ diff --git a/lib/bt/host/nimble/nimble/apps/bttester/src/gap.c b/lib/bt/host/nimble/nimble/apps/bttester/src/gap.c new file mode 100644 index 00000000..74b956b9 --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/bttester/src/gap.c @@ -0,0 +1,1775 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* gap.c - Bluetooth GAP Tester */ + +/* + * Copyright (c) 2015-2016 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "host/ble_gap.h" +#include "host/util/util.h" +#include "console/console.h" + +#include "../../../nimble/host/src/ble_hs_pvcy_priv.h" +#include "../../../nimble/host/src/ble_hs_hci_priv.h" +#include "../../../nimble/host/src/ble_sm_priv.h" + +#include "bttester.h" + +#define CONTROLLER_INDEX 0 +#define CONTROLLER_NAME "btp_tester" + +#define BLE_AD_DISCOV_MASK (BLE_HS_ADV_F_DISC_LTD | BLE_HS_ADV_F_DISC_GEN) +#define ADV_BUF_LEN (sizeof(struct gap_device_found_ev) + 2 * 31) + +/* parameter values to reject in CPUP if all match the pattern */ +#define REJECT_INTERVAL_MIN 0x0C80 +#define REJECT_INTERVAL_MAX 0x0C80 +#define REJECT_LATENCY 0x0000 +#define REJECT_SUPERVISION_TIMEOUT 0x0C80 + +const uint8_t irk[16] = { + 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, + 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, +}; + +static uint8_t oob[16]; +static struct ble_sm_sc_oob_data oob_data_local; +static struct ble_sm_sc_oob_data oob_data_remote; + +static uint16_t current_settings; +uint8_t own_addr_type; +static ble_addr_t peer_id_addr; +static ble_addr_t peer_ota_addr; +static bool encrypted = false; + +static struct os_callout update_params_co; +static struct gap_conn_param_update_cmd update_params; + +static struct os_callout connected_ev_co; +static struct gap_device_connected_ev connected_ev; +#define CONNECTED_EV_DELAY_MS(itvl) 8 * BLE_HCI_CONN_ITVL * itvl / 1000 +static int connection_attempts; +#if MYNEWT_VAL(BTTESTER_PRIVACY_MODE) && MYNEWT_VAL(BTTESTER_USE_NRPA) +static int64_t advertising_start; +static struct os_callout bttester_nrpa_rotate_timer; +#endif + +static const struct ble_gap_conn_params dflt_conn_params = { + .scan_itvl = 0x0010, + .scan_window = 0x0010, + .itvl_min = BLE_GAP_INITIAL_CONN_ITVL_MIN, + .itvl_max = BLE_GAP_INITIAL_CONN_ITVL_MAX, + .latency = 0, + .supervision_timeout = 0x0100, + .min_ce_len = 0x0010, + .max_ce_len = 0x0300, +}; + +static void conn_param_update(struct os_event *ev); + + +static int gap_conn_find_by_addr(const ble_addr_t *dev_addr, + struct ble_gap_conn_desc *out_desc) +{ + ble_addr_t addr = *dev_addr; + + if (memcmp(BLE_ADDR_ANY, &peer_id_addr, 6) == 0) { + return ble_gap_conn_find_by_addr(&addr, out_desc); + } + + if (BLE_ADDR_IS_RPA(&addr)) { + if(ble_addr_cmp(&peer_ota_addr, &addr) != 0) { + return -1; + } + + return ble_gap_conn_find_by_addr(&addr, out_desc); + } else { + if(ble_addr_cmp(&peer_id_addr, &addr) != 0) { + return -1; + } + + if (BLE_ADDR_IS_RPA(&peer_ota_addr)) { + /* Change addr type to ID addr */ + addr.type |= 2; + } + + return ble_gap_conn_find_by_addr(&addr, out_desc); + } +} + +static int gap_event_cb(struct ble_gap_event *event, void *arg); + +static void supported_commands(uint8_t *data, uint16_t len) +{ + uint8_t cmds[3]; + struct gap_read_supported_commands_rp *rp = (void *) &cmds; + + SYS_LOG_DBG(""); + + memset(cmds, 0, sizeof(cmds)); + + tester_set_bit(cmds, GAP_READ_SUPPORTED_COMMANDS); + tester_set_bit(cmds, GAP_READ_CONTROLLER_INDEX_LIST); + tester_set_bit(cmds, GAP_READ_CONTROLLER_INFO); + tester_set_bit(cmds, GAP_SET_CONNECTABLE); + tester_set_bit(cmds, GAP_SET_DISCOVERABLE); + tester_set_bit(cmds, GAP_SET_BONDABLE); + tester_set_bit(cmds, GAP_START_ADVERTISING); + tester_set_bit(cmds, GAP_STOP_ADVERTISING); + tester_set_bit(cmds, GAP_START_DISCOVERY); + tester_set_bit(cmds, GAP_STOP_DISCOVERY); + tester_set_bit(cmds, GAP_CONNECT); + tester_set_bit(cmds, GAP_DISCONNECT); + tester_set_bit(cmds, GAP_SET_IO_CAP); + tester_set_bit(cmds, GAP_PAIR); + tester_set_bit(cmds, GAP_UNPAIR); + tester_set_bit(cmds, GAP_PASSKEY_ENTRY); + tester_set_bit(cmds, GAP_PASSKEY_CONFIRM); + tester_set_bit(cmds, GAP_START_DIRECT_ADV); + tester_set_bit(cmds, GAP_CONN_PARAM_UPDATE); + tester_set_bit(cmds, GAP_OOB_LEGACY_SET_DATA); + tester_set_bit(cmds, GAP_OOB_SC_GET_LOCAL_DATA); + tester_set_bit(cmds, GAP_OOB_SC_SET_REMOTE_DATA); + tester_set_bit(cmds, GAP_SET_MITM); + + tester_send(BTP_SERVICE_ID_GAP, GAP_READ_SUPPORTED_COMMANDS, + CONTROLLER_INDEX, (uint8_t *) rp, sizeof(cmds)); +} + +static void controller_index_list(uint8_t *data, uint16_t len) +{ + struct gap_read_controller_index_list_rp *rp; + uint8_t buf[sizeof(*rp) + 1]; + + SYS_LOG_DBG(""); + + rp = (void *) buf; + + rp->num = 1; + rp->index[0] = CONTROLLER_INDEX; + + tester_send(BTP_SERVICE_ID_GAP, GAP_READ_CONTROLLER_INDEX_LIST, + BTP_INDEX_NONE, (uint8_t *) rp, sizeof(buf)); +} + +static void controller_info(uint8_t *data, uint16_t len) +{ + struct gap_read_controller_info_rp rp; + uint32_t supported_settings = 0; + ble_addr_t addr; + int rc; + + SYS_LOG_DBG(""); + + rc = ble_hs_pvcy_set_our_irk(irk); + assert(rc == 0); + + memset(&rp, 0, sizeof(rp)); + + /* Make sure we have proper identity address set (public preferred) */ + rc = ble_hs_util_ensure_addr(1); + assert(rc == 0); + rc = ble_hs_id_copy_addr(BLE_ADDR_RANDOM, addr.val, NULL); + assert(rc == 0); + + if (MYNEWT_VAL(BTTESTER_PRIVACY_MODE)) { + if (MYNEWT_VAL(BTTESTER_USE_NRPA)) { + own_addr_type = BLE_OWN_ADDR_RANDOM; + rc = ble_hs_id_gen_rnd(1, &addr); + assert(rc == 0); + rc = ble_hs_id_set_rnd(addr.val); + assert(rc == 0); + } else { + own_addr_type = BLE_OWN_ADDR_RPA_RANDOM_DEFAULT; + } + current_settings |= BIT(GAP_SETTINGS_PRIVACY); + supported_settings |= BIT(GAP_SETTINGS_PRIVACY); + memcpy(rp.address, addr.val, sizeof(rp.address)); + } else { + rc = ble_hs_id_copy_addr(BLE_ADDR_PUBLIC, rp.address, NULL); + if (rc) { + own_addr_type = BLE_OWN_ADDR_RANDOM; + memcpy(rp.address, addr.val, sizeof(rp.address)); + supported_settings |= BIT(GAP_SETTINGS_STATIC_ADDRESS); + current_settings |= BIT(GAP_SETTINGS_STATIC_ADDRESS); + } else { + own_addr_type = BLE_OWN_ADDR_PUBLIC; + } + } + + supported_settings |= BIT(GAP_SETTINGS_POWERED); + supported_settings |= BIT(GAP_SETTINGS_CONNECTABLE); + supported_settings |= BIT(GAP_SETTINGS_BONDABLE); + supported_settings |= BIT(GAP_SETTINGS_LE); + supported_settings |= BIT(GAP_SETTINGS_ADVERTISING); + supported_settings |= BIT(GAP_SETTINGS_SC); + + if (ble_hs_cfg.sm_bonding) { + current_settings |= BIT(GAP_SETTINGS_BONDABLE); + } + if (ble_hs_cfg.sm_sc) { + current_settings |= BIT(GAP_SETTINGS_SC); + } + + rp.supported_settings = sys_cpu_to_le32(supported_settings); + rp.current_settings = sys_cpu_to_le32(current_settings); + + memcpy(rp.name, CONTROLLER_NAME, sizeof(CONTROLLER_NAME)); + + tester_send(BTP_SERVICE_ID_GAP, GAP_READ_CONTROLLER_INFO, + CONTROLLER_INDEX, (uint8_t *) &rp, sizeof(rp)); +} + +static struct ble_gap_adv_params adv_params = { + .conn_mode = BLE_GAP_CONN_MODE_NON, + .disc_mode = BLE_GAP_DISC_MODE_NON, +}; + +#if MYNEWT_VAL(BTTESTER_PRIVACY_MODE) && MYNEWT_VAL(BTTESTER_USE_NRPA) +static void rotate_nrpa_cb(struct os_event *ev) +{ + int rc; + ble_addr_t addr; + int32_t duration_ms = BLE_HS_FOREVER; + int32_t remaining_time; + os_time_t remaining_ticks; + + if (adv_params.disc_mode == BLE_GAP_DISC_MODE_LTD) { + duration_ms = MYNEWT_VAL(BTTESTER_LTD_ADV_TIMEOUT); + } + + ble_gap_adv_stop(); + rc = ble_hs_id_gen_rnd(1, &addr); + assert(rc == 0); + rc = ble_hs_id_set_rnd(addr.val); + assert(rc == 0); + + ble_gap_adv_start(own_addr_type, NULL, duration_ms, + &adv_params, gap_event_cb, NULL); + + remaining_time = os_get_uptime_usec() - advertising_start; + if (remaining_time > 0) { + advertising_start = os_get_uptime_usec(); + os_time_ms_to_ticks(remaining_time, &remaining_ticks); + os_callout_reset(&bttester_nrpa_rotate_timer, + remaining_ticks); + } +} +#endif + +static void set_connectable(uint8_t *data, uint16_t len) +{ + const struct gap_set_connectable_cmd *cmd = (void *) data; + struct gap_set_connectable_rp rp; + + SYS_LOG_DBG(""); + + if (cmd->connectable) { + current_settings |= BIT(GAP_SETTINGS_CONNECTABLE); + adv_params.conn_mode = BLE_GAP_CONN_MODE_UND; + } else { + current_settings &= ~BIT(GAP_SETTINGS_CONNECTABLE); + adv_params.conn_mode = BLE_GAP_CONN_MODE_NON; + } + + rp.current_settings = sys_cpu_to_le32(current_settings); + + tester_send(BTP_SERVICE_ID_GAP, GAP_SET_CONNECTABLE, CONTROLLER_INDEX, + (uint8_t *) &rp, sizeof(rp)); +} + +static uint8_t ad_flags = BLE_HS_ADV_F_BREDR_UNSUP; + +static void set_discoverable(uint8_t *data, uint16_t len) +{ + const struct gap_set_discoverable_cmd *cmd = (void *) data; + struct gap_set_discoverable_rp rp; + + SYS_LOG_DBG(""); + + switch (cmd->discoverable) { + case GAP_NON_DISCOVERABLE: + ad_flags &= ~(BLE_HS_ADV_F_DISC_GEN | BLE_HS_ADV_F_DISC_LTD); + adv_params.disc_mode = BLE_GAP_DISC_MODE_NON; + current_settings &= ~BIT(GAP_SETTINGS_DISCOVERABLE); + break; + case GAP_GENERAL_DISCOVERABLE: + ad_flags &= ~BLE_HS_ADV_F_DISC_LTD; + ad_flags |= BLE_HS_ADV_F_DISC_GEN; + adv_params.disc_mode = BLE_GAP_DISC_MODE_GEN; + current_settings |= BIT(GAP_SETTINGS_DISCOVERABLE); + break; + case GAP_LIMITED_DISCOVERABLE: + ad_flags &= ~BLE_HS_ADV_F_DISC_GEN; + ad_flags |= BLE_HS_ADV_F_DISC_LTD; + adv_params.disc_mode = BLE_GAP_DISC_MODE_LTD; + current_settings |= BIT(GAP_SETTINGS_DISCOVERABLE); + break; + default: + tester_rsp(BTP_SERVICE_ID_GAP, GAP_SET_DISCOVERABLE, + CONTROLLER_INDEX, BTP_STATUS_FAILED); + return; + } + + rp.current_settings = sys_cpu_to_le32(current_settings); + + tester_send(BTP_SERVICE_ID_GAP, GAP_SET_DISCOVERABLE, CONTROLLER_INDEX, + (uint8_t *) &rp, sizeof(rp)); +} + +static void set_bondable(const uint8_t *data, uint16_t len) +{ + const struct gap_set_bondable_cmd *cmd = (void *) data; + struct gap_set_bondable_rp rp; + + SYS_LOG_DBG(""); + + ble_hs_cfg.sm_bonding = cmd->bondable; + if (ble_hs_cfg.sm_bonding) { + current_settings |= BIT(GAP_SETTINGS_BONDABLE); + } else { + current_settings &= ~BIT(GAP_SETTINGS_BONDABLE); + } + + rp.current_settings = sys_cpu_to_le32(current_settings); + + tester_send(BTP_SERVICE_ID_GAP, GAP_SET_BONDABLE, CONTROLLER_INDEX, + (uint8_t *) &rp, sizeof(rp)); +} + +static struct bt_data ad[10] = { + BT_DATA(BLE_HS_ADV_TYPE_FLAGS, &ad_flags, sizeof(ad_flags)), +}; +static struct bt_data sd[10]; + +static int set_ad(const struct bt_data *ad, size_t ad_len, + uint8_t *buf, uint8_t *buf_len) +{ + int i; + + for (i = 0; i < ad_len; i++) { + buf[(*buf_len)++] = ad[i].data_len + 1; + buf[(*buf_len)++] = ad[i].type; + + memcpy(&buf[*buf_len], ad[i].data, + ad[i].data_len); + *buf_len += ad[i].data_len; + } + + return 0; +} + +static void start_advertising(const uint8_t *data, uint16_t len) +{ + const struct gap_start_advertising_cmd *cmd = (void *) data; + struct gap_start_advertising_rp rp; + int32_t duration_ms = BLE_HS_FOREVER; + uint8_t buf[BLE_HS_ADV_MAX_SZ]; + uint8_t buf_len = 0; + uint8_t adv_len, sd_len; + int err; + + int i; + + SYS_LOG_DBG(""); + + for (i = 0, adv_len = 1; i < cmd->adv_data_len; adv_len++) { + if (adv_len >= ARRAY_SIZE(ad)) { + SYS_LOG_ERR("ad[] Out of memory"); + goto fail; + } + + ad[adv_len].type = cmd->adv_data[i++]; + ad[adv_len].data_len = cmd->adv_data[i++]; + ad[adv_len].data = &cmd->adv_data[i]; + i += ad[adv_len].data_len; + } + + for (i = 0, sd_len = 0; i < cmd->scan_rsp_len; sd_len++) { + if (sd_len >= ARRAY_SIZE(sd)) { + SYS_LOG_ERR("sd[] Out of memory"); + goto fail; + } + + sd[sd_len].type = cmd->scan_rsp[i++]; + sd[sd_len].data_len = cmd->scan_rsp[i++]; + sd[sd_len].data = &cmd->scan_rsp[i]; + i += sd[sd_len].data_len; + } + + err = set_ad(ad, adv_len, buf, &buf_len); + if (err) { + goto fail; + } + + err = ble_gap_adv_set_data(buf, buf_len); + if (err != 0) { + goto fail; + } + + if (sd_len) { + buf_len = 0; + + err = set_ad(sd, sd_len, buf, &buf_len); + if (err) { + SYS_LOG_ERR("Advertising failed: err %d", err); + goto fail; + } + + err = ble_gap_adv_rsp_set_data(buf, buf_len); + if (err != 0) { + SYS_LOG_ERR("Advertising failed: err %d", err); + goto fail; + } + } + + if (adv_params.disc_mode == BLE_GAP_DISC_MODE_LTD) { + duration_ms = MYNEWT_VAL(BTTESTER_LTD_ADV_TIMEOUT); + } + +#if MYNEWT_VAL(BTTESTER_PRIVACY_MODE) && MYNEWT_VAL(BTTESTER_USE_NRPA) + if (MYNEWT_VAL(BTTESTER_NRPA_TIMEOUT) < duration_ms / 1000) { + advertising_start = os_get_uptime_usec(); + os_callout_reset(&bttester_nrpa_rotate_timer, + OS_TICKS_PER_SEC * MYNEWT_VAL(BTTESTER_NRPA_TIMEOUT)); + } +#endif + err = ble_gap_adv_start(own_addr_type, NULL, duration_ms, + &adv_params, gap_event_cb, NULL); + if (err) { + SYS_LOG_ERR("Advertising failed: err %d", err); + goto fail; + } + + current_settings |= BIT(GAP_SETTINGS_ADVERTISING); + rp.current_settings = sys_cpu_to_le32(current_settings); + + tester_send(BTP_SERVICE_ID_GAP, GAP_START_ADVERTISING, CONTROLLER_INDEX, + (uint8_t *) &rp, sizeof(rp)); + return; +fail: + tester_rsp(BTP_SERVICE_ID_GAP, GAP_START_ADVERTISING, CONTROLLER_INDEX, + BTP_STATUS_FAILED); +} + +static void stop_advertising(const uint8_t *data, uint16_t len) +{ + struct gap_stop_advertising_rp rp; + + SYS_LOG_DBG(""); + + if (ble_gap_adv_stop() != 0) { + tester_rsp(BTP_SERVICE_ID_GAP, GAP_STOP_ADVERTISING, + CONTROLLER_INDEX, BTP_STATUS_FAILED); + return; + } + + current_settings &= ~BIT(GAP_SETTINGS_ADVERTISING); + rp.current_settings = sys_cpu_to_le32(current_settings); + + tester_send(BTP_SERVICE_ID_GAP, GAP_STOP_ADVERTISING, CONTROLLER_INDEX, + (uint8_t *) &rp, sizeof(rp)); +} + +static uint8_t get_ad_flags(const uint8_t *data, uint8_t data_len) +{ + uint8_t len, i; + + /* Parse advertisement to get flags */ + for (i = 0; i < data_len; i += len - 1) { + len = data[i++]; + if (!len) { + break; + } + + /* Check if field length is correct */ + if (len > (data_len - i) || (data_len - i) < 1) { + break; + } + + switch (data[i++]) { + case BLE_HS_ADV_TYPE_FLAGS: + return data[i]; + default: + break; + } + } + + return 0; +} + +static uint8_t discovery_flags; +static struct os_mbuf *adv_buf; + +static void store_adv(const ble_addr_t *addr, int8_t rssi, + const uint8_t *data, uint8_t len) +{ + struct gap_device_found_ev *ev; + + /* cleanup */ + net_buf_simple_init(adv_buf, 0); + + ev = net_buf_simple_add(adv_buf, sizeof(*ev)); + + memcpy(ev->address, addr->val, sizeof(ev->address)); + ev->address_type = addr->type; + ev->rssi = rssi; + ev->flags = GAP_DEVICE_FOUND_FLAG_AD | GAP_DEVICE_FOUND_FLAG_RSSI; + ev->eir_data_len = len; + memcpy(net_buf_simple_add(adv_buf, len), data, len); +} + +static void device_found(ble_addr_t *addr, int8_t rssi, uint8_t evtype, + const uint8_t *data, uint8_t len) +{ + struct gap_device_found_ev *ev; + ble_addr_t a; + + /* if General/Limited Discovery - parse Advertising data to get flags */ + if (!(discovery_flags & GAP_DISCOVERY_FLAG_LE_OBSERVE) && + (evtype != BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP)) { + uint8_t flags = get_ad_flags(data, len); + + /* ignore non-discoverable devices */ + if (!(flags & BLE_AD_DISCOV_MASK)) { + SYS_LOG_DBG("Non discoverable, skipping"); + return; + } + + /* if Limited Discovery - ignore general discoverable devices */ + if ((discovery_flags & GAP_DISCOVERY_FLAG_LIMITED) && + !(flags & BLE_HS_ADV_F_DISC_LTD)) { + SYS_LOG_DBG("General discoverable, skipping"); + return; + } + } + + /* attach Scan Response data */ + if (evtype == BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP) { + /* skip if there is no pending advertisement */ + if (!adv_buf->om_len) { + SYS_LOG_INF("No pending advertisement, skipping"); + return; + } + + ev = (void *) adv_buf->om_data; + a.type = ev->address_type; + memcpy(a.val, ev->address, sizeof(a.val)); + + /* + * in general, the Scan Response comes right after the + * Advertisement, but if not if send stored event and ignore + * this one + */ + if (ble_addr_cmp(addr, &a)) { + SYS_LOG_INF("Address does not match, skipping"); + goto done; + } + + ev->eir_data_len += len; + ev->flags |= GAP_DEVICE_FOUND_FLAG_SD; + + memcpy(net_buf_simple_add(adv_buf, len), data, len); + + goto done; + } + + /* + * if there is another pending advertisement, send it and store the + * current one + */ + if (adv_buf->om_len) { + tester_send(BTP_SERVICE_ID_GAP, GAP_EV_DEVICE_FOUND, + CONTROLLER_INDEX, adv_buf->om_data, + adv_buf->om_len); + } + + store_adv(addr, rssi, data, len); + + /* if Active Scan and scannable event - wait for Scan Response */ + if ((discovery_flags & GAP_DISCOVERY_FLAG_LE_ACTIVE_SCAN) && + (evtype == BLE_HCI_ADV_RPT_EVTYPE_ADV_IND || + evtype == BLE_HCI_ADV_RPT_EVTYPE_SCAN_IND)) { + SYS_LOG_DBG("Waiting for scan response"); + return; + } +done: + tester_send(BTP_SERVICE_ID_GAP, GAP_EV_DEVICE_FOUND, + CONTROLLER_INDEX, adv_buf->om_data, adv_buf->om_len); +} + +static int discovery_cb(struct ble_gap_event *event, void *arg) +{ + if (event->type == BLE_GAP_EVENT_DISC) { + device_found(&event->disc.addr, event->disc.rssi, + event->disc.event_type, event->disc.data, + event->disc.length_data); + } + + return 0; +} + +static void start_discovery(const uint8_t *data, uint16_t len) +{ + const struct gap_start_discovery_cmd *cmd = (void *) data; + struct ble_gap_disc_params params = {0}; + uint8_t status; + + SYS_LOG_DBG(""); + + /* only LE scan is supported */ + if (cmd->flags & GAP_DISCOVERY_FLAG_BREDR) { + status = BTP_STATUS_FAILED; + goto reply; + } + + params.passive = (cmd->flags & GAP_DISCOVERY_FLAG_LE_ACTIVE_SCAN) == 0; + params.limited = (cmd->flags & GAP_DISCOVERY_FLAG_LIMITED) > 0; + params.filter_duplicates = 1; + + if (ble_gap_disc(own_addr_type, BLE_HS_FOREVER, + ¶ms, discovery_cb, NULL) != 0) { + status = BTP_STATUS_FAILED; + goto reply; + } + + net_buf_simple_init(adv_buf, 0); + discovery_flags = cmd->flags; + + status = BTP_STATUS_SUCCESS; +reply: + tester_rsp(BTP_SERVICE_ID_GAP, GAP_START_DISCOVERY, CONTROLLER_INDEX, + status); +} + +static void stop_discovery(const uint8_t *data, uint16_t len) +{ + uint8_t status = BTP_STATUS_SUCCESS; + + SYS_LOG_DBG(""); + + if (ble_gap_disc_cancel() != 0) { + status = BTP_STATUS_FAILED; + } + + tester_rsp(BTP_SERVICE_ID_GAP, GAP_STOP_DISCOVERY, CONTROLLER_INDEX, + status); +} + + +/* Bluetooth Core Spec v5.1 | Section 10.7.1 + * If a privacy-enabled Peripheral, that has a stored bond, + * receives a resolvable private address, the Host may resolve + * the resolvable private address [...] + * If the resolution is successful, the Host may accept the connection. + * If the resolution procedure fails, then the Host shall disconnect + * with the error code "Authentication failure" [...] + */ +static void periph_privacy(struct ble_gap_conn_desc desc) +{ +#if !MYNEWT_VAL(BTTESTER_PRIVACY_MODE) + return; +#endif + int count; + + SYS_LOG_DBG(""); + + ble_store_util_count(BLE_STORE_OBJ_TYPE_PEER_SEC, &count); + if (count > 0 && BLE_ADDR_IS_RPA(&desc.peer_id_addr)) { + SYS_LOG_DBG("Authentication failure, disconnecting"); + ble_gap_terminate(desc.conn_handle, BLE_ERR_AUTH_FAIL); + } +} + +static void device_connected_ev_send(struct os_event *ev) +{ + struct ble_gap_conn_desc desc; + int rc; + + SYS_LOG_DBG(""); + + rc = gap_conn_find_by_addr((ble_addr_t *)&connected_ev, &desc); + if (rc) { + tester_rsp(BTP_SERVICE_ID_GAP, GAP_EV_DEVICE_CONNECTED, + CONTROLLER_INDEX, BTP_STATUS_FAILED); + return; + } + + tester_send(BTP_SERVICE_ID_GAP, GAP_EV_DEVICE_CONNECTED, + CONTROLLER_INDEX, (uint8_t *) &connected_ev, + sizeof(connected_ev)); + + periph_privacy(desc); +} + +static void le_connected(uint16_t conn_handle, int status) +{ + struct ble_gap_conn_desc desc; + ble_addr_t *addr; + int rc; + + SYS_LOG_DBG(""); + + if (status != 0) { + return; + } + + rc = ble_gap_conn_find(conn_handle, &desc); + if (rc) { + return; + } + + peer_id_addr = desc.peer_id_addr; + peer_ota_addr = desc.peer_ota_addr; + + addr = &desc.peer_id_addr; + + memcpy(connected_ev.address, addr->val, sizeof(connected_ev.address)); + connected_ev.address_type = addr->type; + connected_ev.conn_itvl = desc.conn_itvl; + connected_ev.conn_latency = desc.conn_latency; + connected_ev.supervision_timeout = desc.supervision_timeout; + +#if MYNEWT_VAL(BTTESTER_CONN_RETRY) + os_callout_reset(&connected_ev_co, + os_time_ms_to_ticks32( + CONNECTED_EV_DELAY_MS(desc.conn_itvl))); +#else + tester_send(BTP_SERVICE_ID_GAP, GAP_EV_DEVICE_CONNECTED, + CONTROLLER_INDEX, (uint8_t *) &connected_ev, + sizeof(connected_ev)); +#endif +} + +static void le_disconnected(struct ble_gap_conn_desc *conn, int reason) +{ + struct gap_device_disconnected_ev ev; + ble_addr_t *addr = &conn->peer_ota_addr; + + SYS_LOG_DBG(""); + +#if MYNEWT_VAL(BTTESTER_CONN_RETRY) + int rc; + + if ((reason == BLE_HS_HCI_ERR(BLE_ERR_CONN_ESTABLISHMENT)) && + os_callout_queued(&connected_ev_co)) { + if (connection_attempts < MYNEWT_VAL(BTTESTER_CONN_RETRY)) { + os_callout_stop(&connected_ev_co); + + /* try connecting again */ + rc = ble_gap_connect(own_addr_type, addr, 0, + &dflt_conn_params, gap_event_cb, + NULL); + + if (rc == 0) { + connection_attempts++; + return; + } + } + } else if (os_callout_queued(&connected_ev_co)) { + os_callout_stop(&connected_ev_co); + return; + } +#endif + + connection_attempts = 0; + memset(&connected_ev, 0, sizeof(connected_ev)); + + memcpy(ev.address, addr->val, sizeof(ev.address)); + ev.address_type = addr->type; + + tester_send(BTP_SERVICE_ID_GAP, GAP_EV_DEVICE_DISCONNECTED, + CONTROLLER_INDEX, (uint8_t *) &ev, sizeof(ev)); +} + +static void auth_passkey_oob(uint16_t conn_handle) +{ + struct ble_gap_conn_desc desc; + struct ble_sm_io pk; + int rc; + + SYS_LOG_DBG(""); + + rc = ble_gap_conn_find(conn_handle, &desc); + if (rc) { + return; + } + + memcpy(pk.oob, oob, sizeof(oob)); + pk.action = BLE_SM_IOACT_OOB; + + rc = ble_sm_inject_io(conn_handle, &pk); + assert(rc == 0); +} + +static void auth_passkey_display(uint16_t conn_handle, unsigned int passkey) +{ + struct ble_gap_conn_desc desc; + struct gap_passkey_display_ev ev; + ble_addr_t *addr; + struct ble_sm_io pk; + int rc; + + SYS_LOG_DBG(""); + + rc = ble_gap_conn_find(conn_handle, &desc); + if (rc) { + return; + } + + rc = ble_hs_hci_util_rand(&pk.passkey, sizeof(pk.passkey)); + assert(rc == 0); + /* Max value is 999999 */ + pk.passkey %= 1000000; + pk.action = BLE_SM_IOACT_DISP; + + rc = ble_sm_inject_io(conn_handle, &pk); + assert(rc == 0); + + addr = &desc.peer_ota_addr; + + memcpy(ev.address, addr->val, sizeof(ev.address)); + ev.address_type = addr->type; + ev.passkey = sys_cpu_to_le32(pk.passkey); + + tester_send(BTP_SERVICE_ID_GAP, GAP_EV_PASSKEY_DISPLAY, + CONTROLLER_INDEX, (uint8_t *) &ev, sizeof(ev)); +} + +static void auth_passkey_entry(uint16_t conn_handle) +{ + struct ble_gap_conn_desc desc; + struct gap_passkey_entry_req_ev ev; + ble_addr_t *addr; + int rc; + + SYS_LOG_DBG(""); + + rc = ble_gap_conn_find(conn_handle, &desc); + if (rc) { + return; + } + + addr = &desc.peer_ota_addr; + + memcpy(ev.address, addr->val, sizeof(ev.address)); + ev.address_type = addr->type; + + tester_send(BTP_SERVICE_ID_GAP, GAP_EV_PASSKEY_ENTRY_REQ, + CONTROLLER_INDEX, (uint8_t *) &ev, sizeof(ev)); +} + +static void auth_passkey_numcmp(uint16_t conn_handle, unsigned int passkey) +{ + struct ble_gap_conn_desc desc; + struct gap_passkey_confirm_req_ev ev; + ble_addr_t *addr; + int rc; + + SYS_LOG_DBG(""); + + rc = ble_gap_conn_find(conn_handle, &desc); + if (rc) { + return; + } + + addr = &desc.peer_ota_addr; + + memcpy(ev.address, addr->val, sizeof(ev.address)); + ev.address_type = addr->type; + ev.passkey = sys_cpu_to_le32(passkey); + + tester_send(BTP_SERVICE_ID_GAP, GAP_EV_PASSKEY_CONFIRM_REQ, + CONTROLLER_INDEX, (uint8_t *) &ev, sizeof(ev)); +} + +static void auth_passkey_oob_sc(uint16_t conn_handle) +{ + int rc; + struct ble_sm_io pk; + + SYS_LOG_DBG(""); + + memset(&pk, 0, sizeof(pk)); + + pk.oob_sc_data.local = &oob_data_local; + + if (ble_hs_cfg.sm_oob_data_flag) { + pk.oob_sc_data.remote = &oob_data_remote; + } + + pk.action = BLE_SM_IOACT_OOB_SC; + rc = ble_sm_inject_io(conn_handle, &pk); + if (rc != 0) { + console_printf("error providing oob; rc=%d\n", rc); + } +} + +static void le_passkey_action(uint16_t conn_handle, + struct ble_gap_passkey_params *params) +{ + SYS_LOG_DBG(""); + + switch (params->action) { + case BLE_SM_IOACT_NONE: + break; + case BLE_SM_IOACT_OOB: + auth_passkey_oob(conn_handle); + break; + case BLE_SM_IOACT_INPUT: + auth_passkey_entry(conn_handle); + break; + case BLE_SM_IOACT_DISP: + auth_passkey_display(conn_handle, params->numcmp); + break; + case BLE_SM_IOACT_NUMCMP: + auth_passkey_numcmp(conn_handle, params->numcmp); + break; + case BLE_SM_IOACT_OOB_SC: + auth_passkey_oob_sc(conn_handle); + break; + default: + assert(0); + } +} + +static void le_identity_resolved(uint16_t conn_handle) +{ + struct ble_gap_conn_desc desc; + struct gap_identity_resolved_ev ev; + int rc; + + SYS_LOG_DBG(""); + + rc = ble_gap_conn_find(conn_handle, &desc); + if (rc) { + return; + } + + peer_id_addr = desc.peer_id_addr; + peer_ota_addr = desc.peer_ota_addr; + + ev.address_type = desc.peer_ota_addr.type; + memcpy(ev.address, desc.peer_ota_addr.val, sizeof(ev.address)); + + ev.identity_address_type = desc.peer_id_addr.type; + memcpy(ev.identity_address, desc.peer_id_addr.val, + sizeof(ev.identity_address)); + + tester_send(BTP_SERVICE_ID_GAP, GAP_EV_IDENTITY_RESOLVED, + CONTROLLER_INDEX, (uint8_t *) &ev, sizeof(ev)); +} + +static void le_conn_param_update(struct ble_gap_conn_desc *desc) +{ + struct gap_conn_param_update_ev ev; + + SYS_LOG_DBG(""); + + ev.address_type = desc->peer_ota_addr.type; + memcpy(ev.address, desc->peer_ota_addr.val, sizeof(ev.address)); + + ev.conn_itvl = desc->conn_itvl; + ev.conn_latency = desc->conn_latency; + ev.supervision_timeout = desc->supervision_timeout; + + tester_send(BTP_SERVICE_ID_GAP, GAP_EV_CONN_PARAM_UPDATE, + CONTROLLER_INDEX, (uint8_t *) &ev, sizeof(ev)); +} + +static void le_encryption_changed(struct ble_gap_conn_desc *desc) +{ + struct gap_sec_level_changed_ev ev; + + SYS_LOG_DBG(""); + + encrypted = (bool) desc->sec_state.encrypted; + + ev.address_type = desc->peer_ota_addr.type; + memcpy(ev.address, desc->peer_ota_addr.val, sizeof(ev.address)); + ev.level = 0; + + if (desc->sec_state.encrypted) { + if (desc->sec_state.authenticated) { + if (desc->sec_state.key_size == 16) { + ev.level = 3; + } else { + ev.level = 2; + } + } else { + ev.level = 1; + } + } + + tester_send(BTP_SERVICE_ID_GAP, GAP_EV_SEC_LEVEL_CHANGED, + CONTROLLER_INDEX, (uint8_t *) &ev, sizeof(ev)); +} + +static void print_bytes(const uint8_t *bytes, int len) +{ + int i; + + for (i = 0; i < len; i++) { + console_printf("%s0x%02x", i != 0 ? ":" : "", bytes[i]); + } +} + +static void print_mbuf(const struct os_mbuf *om) +{ + int colon; + + colon = 0; + while (om != NULL) { + if (colon) { + console_printf(":"); + } else { + colon = 1; + } + print_bytes(om->om_data, om->om_len); + om = SLIST_NEXT(om, om_next); + } +} + +static void print_addr(const void *addr) +{ + const uint8_t *u8p; + + u8p = addr; + console_printf("%02x:%02x:%02x:%02x:%02x:%02x", + u8p[5], u8p[4], u8p[3], u8p[2], u8p[1], u8p[0]); +} + +static void print_conn_desc(const struct ble_gap_conn_desc *desc) +{ + console_printf("handle=%d our_ota_addr_type=%d our_ota_addr=", + desc->conn_handle, desc->our_ota_addr.type); + print_addr(desc->our_ota_addr.val); + console_printf(" our_id_addr_type=%d our_id_addr=", + desc->our_id_addr.type); + print_addr(desc->our_id_addr.val); + console_printf(" peer_ota_addr_type=%d peer_ota_addr=", + desc->peer_ota_addr.type); + print_addr(desc->peer_ota_addr.val); + console_printf(" peer_id_addr_type=%d peer_id_addr=", + desc->peer_id_addr.type); + print_addr(desc->peer_id_addr.val); + console_printf(" conn_itvl=%d conn_latency=%d supervision_timeout=%d " + "key_sz=%d encrypted=%d authenticated=%d bonded=%d\n", + desc->conn_itvl, desc->conn_latency, + desc->supervision_timeout, + desc->sec_state.key_size, + desc->sec_state.encrypted, + desc->sec_state.authenticated, + desc->sec_state.bonded); +} + +static void adv_complete(void) +{ + struct gap_new_settings_ev ev; + + current_settings &= ~BIT(GAP_SETTINGS_ADVERTISING); + ev.current_settings = sys_cpu_to_le32(current_settings); + + tester_send(BTP_SERVICE_ID_GAP, GAP_EV_NEW_SETTINGS, CONTROLLER_INDEX, + (uint8_t *) &ev, sizeof(ev)); +} + +static int gap_event_cb(struct ble_gap_event *event, void *arg) +{ + struct ble_gap_conn_desc desc; + int rc; + + switch (event->type) { + case BLE_GAP_EVENT_ADV_COMPLETE: + console_printf("advertising complete; reason=%d\n", + event->adv_complete.reason); + break; + case BLE_GAP_EVENT_CONNECT: + console_printf("connection %s; status=%d ", + event->connect.status == 0 ? "established" : "failed", + event->connect.status); + if (event->connect.status == 0) { + rc = ble_gap_conn_find(event->connect.conn_handle, &desc); + assert(rc == 0); + print_conn_desc(&desc); + } + + if (desc.role == BLE_GAP_ROLE_SLAVE) { + adv_complete(); + } + + le_connected(event->connect.conn_handle, + event->connect.status); + break; + case BLE_GAP_EVENT_DISCONNECT: + console_printf("disconnect; reason=%d ", event->disconnect.reason); + print_conn_desc(&event->disconnect.conn); + le_disconnected(&event->disconnect.conn, + event->disconnect.reason); + break; + case BLE_GAP_EVENT_ENC_CHANGE: + console_printf("encryption change event; status=%d ", event->enc_change.status); + rc = ble_gap_conn_find(event->enc_change.conn_handle, &desc); + assert(rc == 0); + print_conn_desc(&desc); + le_encryption_changed(&desc); + break; + case BLE_GAP_EVENT_PASSKEY_ACTION: + console_printf("passkey action event; action=%d", + event->passkey.params.action); + if (event->passkey.params.action == BLE_SM_IOACT_NUMCMP) { + console_printf(" numcmp=%lu", + (unsigned long)event->passkey.params.numcmp); + } + console_printf("\n"); + le_passkey_action(event->passkey.conn_handle, + &event->passkey.params); + break; + case BLE_GAP_EVENT_IDENTITY_RESOLVED: + console_printf("identity resolved "); + rc = ble_gap_conn_find(event->identity_resolved.conn_handle, &desc); + assert(rc == 0); + print_conn_desc(&desc); + le_identity_resolved(event->identity_resolved.conn_handle); + break; + case BLE_GAP_EVENT_NOTIFY_RX: + console_printf("notification rx event; attr_handle=%d indication=%d " + "len=%d data=", + event->notify_rx.attr_handle, + event->notify_rx.indication, + OS_MBUF_PKTLEN(event->notify_rx.om)); + + print_mbuf(event->notify_rx.om); + console_printf("\n"); + tester_gattc_notify_rx_ev(event->notify_rx.conn_handle, + event->notify_rx.attr_handle, + event->notify_rx.indication, + event->notify_rx.om); + break; + case BLE_GAP_EVENT_SUBSCRIBE: + console_printf("subscribe event; conn_handle=%d attr_handle=%d " + "reason=%d prevn=%d curn=%d previ=%d curi=%d\n", + event->subscribe.conn_handle, + event->subscribe.attr_handle, + event->subscribe.reason, + event->subscribe.prev_notify, + event->subscribe.cur_notify, + event->subscribe.prev_indicate, + event->subscribe.cur_indicate); + tester_gatt_subscribe_ev(event->subscribe.conn_handle, + event->subscribe.attr_handle, + event->subscribe.reason, + event->subscribe.prev_notify, + event->subscribe.cur_notify, + event->subscribe.prev_indicate, + event->subscribe.cur_indicate); + break; + case BLE_GAP_EVENT_REPEAT_PAIRING: + console_printf("repeat pairing event; conn_handle=%d " + "cur_key_sz=%d cur_auth=%d cur_sc=%d " + "new_key_sz=%d new_auth=%d new_sc=%d " + "new_bonding=%d\n", + event->repeat_pairing.conn_handle, + event->repeat_pairing.cur_key_size, + event->repeat_pairing.cur_authenticated, + event->repeat_pairing.cur_sc, + event->repeat_pairing.new_key_size, + event->repeat_pairing.new_authenticated, + event->repeat_pairing.new_sc, + event->repeat_pairing.new_bonding); + rc = ble_gap_conn_find(event->repeat_pairing.conn_handle, &desc); + assert(rc == 0); + rc = ble_store_util_delete_peer(&desc.peer_id_addr); + assert(rc == 0); + return BLE_GAP_REPEAT_PAIRING_RETRY; + case BLE_GAP_EVENT_CONN_UPDATE: + console_printf("connection update event; status=%d ", + event->conn_update.status); + rc = ble_gap_conn_find(event->conn_update.conn_handle, &desc); + assert(rc == 0); + print_conn_desc(&desc); + le_conn_param_update(&desc); + break; + case BLE_GAP_EVENT_CONN_UPDATE_REQ: + console_printf("connection update request event; " + "conn_handle=%d itvl_min=%d itvl_max=%d " + "latency=%d supervision_timoeut=%d " + "min_ce_len=%d max_ce_len=%d\n", + event->conn_update_req.conn_handle, + event->conn_update_req.peer_params->itvl_min, + event->conn_update_req.peer_params->itvl_max, + event->conn_update_req.peer_params->latency, + event->conn_update_req.peer_params->supervision_timeout, + event->conn_update_req.peer_params->min_ce_len, + event->conn_update_req.peer_params->max_ce_len); + + *event->conn_update_req.self_params = + *event->conn_update_req.peer_params; + break; + case BLE_GAP_EVENT_L2CAP_UPDATE_REQ: + console_printf("connection update request event; " + "conn_handle=%d itvl_min=%d itvl_max=%d " + "latency=%d supervision_timoeut=%d " + "min_ce_len=%d max_ce_len=%d\n", + event->conn_update_req.conn_handle, + event->conn_update_req.peer_params->itvl_min, + event->conn_update_req.peer_params->itvl_max, + event->conn_update_req.peer_params->latency, + event->conn_update_req.peer_params->supervision_timeout, + event->conn_update_req.peer_params->min_ce_len, + event->conn_update_req.peer_params->max_ce_len); + if (event->conn_update_req.peer_params->itvl_min == REJECT_INTERVAL_MIN && + event->conn_update_req.peer_params->itvl_max == REJECT_INTERVAL_MAX && + event->conn_update_req.peer_params->latency == REJECT_LATENCY && + event->conn_update_req.peer_params->supervision_timeout == REJECT_SUPERVISION_TIMEOUT) { + return EINVAL; + } + + default: + break; + } + + return 0; +} + +static void connect(const uint8_t *data, uint16_t len) +{ + uint8_t status = BTP_STATUS_SUCCESS; + ble_addr_t *addr = (ble_addr_t *) data; + + SYS_LOG_DBG(""); + + if (ble_addr_cmp(BLE_ADDR_ANY, addr) == 0) { + addr = NULL; + } + + if (ble_gap_connect(own_addr_type, addr, 0, + &dflt_conn_params, gap_event_cb, NULL)) { + status = BTP_STATUS_FAILED; + } + + tester_rsp(BTP_SERVICE_ID_GAP, GAP_CONNECT, CONTROLLER_INDEX, status); +} + +static void disconnect(const uint8_t *data, uint16_t len) +{ + struct ble_gap_conn_desc desc; + uint8_t status; + int rc; + + SYS_LOG_DBG(""); + + rc = gap_conn_find_by_addr((ble_addr_t *)data, &desc); + if (rc) { + status = BTP_STATUS_FAILED; + goto rsp; + } + + if (ble_gap_terminate(desc.conn_handle, BLE_ERR_REM_USER_CONN_TERM)) { + status = BTP_STATUS_FAILED; + } else { + status = BTP_STATUS_SUCCESS; + } + +rsp: + tester_rsp(BTP_SERVICE_ID_GAP, GAP_DISCONNECT, CONTROLLER_INDEX, + status); +} + +static void set_io_cap(const uint8_t *data, uint16_t len) +{ + const struct gap_set_io_cap_cmd *cmd = (void *) data; + uint8_t status; + + SYS_LOG_DBG(""); + + switch (cmd->io_cap) { + case GAP_IO_CAP_DISPLAY_ONLY: + ble_hs_cfg.sm_io_cap = BLE_SM_IO_CAP_DISP_ONLY; + ble_hs_cfg.sm_mitm = 1; + break; + case GAP_IO_CAP_KEYBOARD_DISPLAY: + ble_hs_cfg.sm_io_cap = BLE_SM_IO_CAP_KEYBOARD_DISP; + ble_hs_cfg.sm_mitm = 1; + break; + case GAP_IO_CAP_NO_INPUT_OUTPUT: + ble_hs_cfg.sm_io_cap = BLE_SM_IO_CAP_NO_IO; + ble_hs_cfg.sm_mitm = 0; + break; + case GAP_IO_CAP_KEYBOARD_ONLY: + ble_hs_cfg.sm_io_cap = BLE_SM_IO_CAP_KEYBOARD_ONLY; + ble_hs_cfg.sm_mitm = 1; + break; + case GAP_IO_CAP_DISPLAY_YESNO: + ble_hs_cfg.sm_io_cap = BLE_SM_IO_CAP_DISP_YES_NO; + ble_hs_cfg.sm_mitm = 1; + break; + default: + status = BTP_STATUS_FAILED; + goto rsp; + } + + status = BTP_STATUS_SUCCESS; + +rsp: + tester_rsp(BTP_SERVICE_ID_GAP, GAP_SET_IO_CAP, CONTROLLER_INDEX, + status); +} + +static void pair(const uint8_t *data, uint16_t len) +{ + struct ble_gap_conn_desc desc; + uint8_t status; + int rc; + + SYS_LOG_DBG(""); + + rc = gap_conn_find_by_addr((ble_addr_t *)data, &desc); + if (rc) { + status = BTP_STATUS_FAILED; + goto rsp; + } + + if (ble_gap_security_initiate(desc.conn_handle)) { + status = BTP_STATUS_FAILED; + goto rsp; + } + + status = BTP_STATUS_SUCCESS; + +rsp: + tester_rsp(BTP_SERVICE_ID_GAP, GAP_PAIR, CONTROLLER_INDEX, status); +} + +static void unpair(const uint8_t *data, uint16_t len) +{ + uint8_t status; + int err; + + SYS_LOG_DBG(""); + + err = ble_gap_unpair((ble_addr_t *) data); + status = (uint8_t) (err != 0 ? BTP_STATUS_FAILED : BTP_STATUS_SUCCESS); + tester_rsp(BTP_SERVICE_ID_GAP, GAP_UNPAIR, CONTROLLER_INDEX, status); +} + +static void passkey_entry(const uint8_t *data, uint16_t len) +{ + const struct gap_passkey_entry_cmd *cmd = (void *) data; + struct ble_gap_conn_desc desc; + struct ble_sm_io pk; + uint8_t status; + int rc; + + SYS_LOG_DBG(""); + + rc = gap_conn_find_by_addr((ble_addr_t *)data, &desc); + if (rc) { + status = BTP_STATUS_FAILED; + goto rsp; + } + + pk.action = BLE_SM_IOACT_INPUT; + pk.passkey = sys_le32_to_cpu(cmd->passkey); + + rc = ble_sm_inject_io(desc.conn_handle, &pk); + if (rc) { + status = BTP_STATUS_FAILED; + goto rsp; + } + + status = BTP_STATUS_SUCCESS; + +rsp: + tester_rsp(BTP_SERVICE_ID_GAP, GAP_PASSKEY_ENTRY, CONTROLLER_INDEX, + status); +} + +static void passkey_confirm(const uint8_t *data, uint16_t len) +{ + const struct gap_passkey_confirm_cmd *cmd = (void *) data; + struct ble_gap_conn_desc desc; + struct ble_sm_io pk; + uint8_t status; + int rc; + + SYS_LOG_DBG(""); + + rc = gap_conn_find_by_addr((ble_addr_t *)data, &desc); + if (rc) { + status = BTP_STATUS_FAILED; + goto rsp; + } + + pk.action = BLE_SM_IOACT_NUMCMP; + pk.numcmp_accept = cmd->match; + + rc = ble_sm_inject_io(desc.conn_handle, &pk); + if (rc) { + console_printf("sm inject io failed"); + status = BTP_STATUS_FAILED; + goto rsp; + } + + status = BTP_STATUS_SUCCESS; + +rsp: + tester_rsp(BTP_SERVICE_ID_GAP, GAP_PASSKEY_CONFIRM, CONTROLLER_INDEX, + status); +} + +static void start_direct_adv(const uint8_t *data, uint16_t len) +{ + const struct gap_start_direct_adv_cmd *cmd = (void *) data; + struct gap_start_advertising_rp rp; + static struct ble_gap_adv_params adv_params = { + .conn_mode = BLE_GAP_CONN_MODE_DIR, + }; + int err; + + SYS_LOG_DBG(""); + + adv_params.high_duty_cycle = cmd->high_duty; + + err = ble_gap_adv_start(own_addr_type, (ble_addr_t *)data, + BLE_HS_FOREVER, &adv_params, + gap_event_cb, NULL); + if (err) { + SYS_LOG_ERR("Advertising failed: err %d", err); + goto fail; + } + + current_settings |= BIT(GAP_SETTINGS_ADVERTISING); + rp.current_settings = sys_cpu_to_le32(current_settings); + + tester_send(BTP_SERVICE_ID_GAP, GAP_START_DIRECT_ADV, CONTROLLER_INDEX, + (uint8_t *) &rp, sizeof(rp)); + return; +fail: + tester_rsp(BTP_SERVICE_ID_GAP, GAP_START_DIRECT_ADV, CONTROLLER_INDEX, + BTP_STATUS_FAILED); +} + +static void conn_param_update_cb(uint16_t conn_handle, int status, void *arg) +{ + console_printf("conn param update complete; conn_handle=%d status=%d\n", + conn_handle, status); +} + +static int conn_param_update_slave(uint16_t conn_handle, + const struct gap_conn_param_update_cmd *cmd) +{ + int rc; + struct ble_l2cap_sig_update_params params; + + params.itvl_min = cmd->conn_itvl_min; + params.itvl_max = cmd->conn_itvl_max; + params.slave_latency = cmd->conn_latency; + params.timeout_multiplier = cmd->supervision_timeout; + + rc = ble_l2cap_sig_update(conn_handle, ¶ms, + conn_param_update_cb, NULL); + if (rc) { + SYS_LOG_ERR("Failed to send update params: rc=%d", rc); + } + + return 0; +} + +static int conn_param_update_master(uint16_t conn_handle, + const struct gap_conn_param_update_cmd *cmd) +{ + int rc; + struct ble_gap_upd_params params; + + params.itvl_min = cmd->conn_itvl_min; + params.itvl_max = cmd->conn_itvl_max; + params.latency = cmd->conn_latency; + params.supervision_timeout = cmd->supervision_timeout; + params.min_ce_len = 0; + params.max_ce_len = 0; + rc = ble_gap_update_params(conn_handle, ¶ms); + if (rc) { + SYS_LOG_ERR("Failed to send update params: rc=%d", rc); + } + + return rc; +} + +static void conn_param_update(struct os_event *ev) +{ + struct ble_gap_conn_desc desc; + int rc; + + SYS_LOG_DBG(""); + + rc = gap_conn_find_by_addr((ble_addr_t *)&update_params, &desc); + if (rc) { + goto rsp; + } + + if ((desc.conn_itvl >= update_params.conn_itvl_min) && + (desc.conn_itvl <= update_params.conn_itvl_max) && + (desc.conn_latency == update_params.conn_latency) && + (desc.supervision_timeout == update_params.supervision_timeout)) { + goto rsp; + } + + if (desc.role == BLE_GAP_ROLE_MASTER) { + rc = conn_param_update_master(desc.conn_handle, &update_params); + } else { + rc = conn_param_update_slave(desc.conn_handle, &update_params); + } + + if (rc == 0) { + return; + } + +rsp: + SYS_LOG_ERR("Conn param update fail; rc=%d", rc); +} + +static void conn_param_update_async(const uint8_t *data, uint16_t len) +{ + const struct gap_conn_param_update_cmd *cmd = (void *) data; + update_params = *cmd; + + os_callout_reset(&update_params_co, 0); + + tester_rsp(BTP_SERVICE_ID_GAP, GAP_CONN_PARAM_UPDATE, CONTROLLER_INDEX, + BTP_STATUS_SUCCESS); +} + +static void oob_legacy_set_data(const uint8_t *data, uint16_t len) +{ + const struct gap_oob_legacy_set_data_cmd *cmd = (void *) data; + + ble_hs_cfg.sm_oob_data_flag = 1; + memcpy(oob, cmd->oob_data, sizeof(oob)); + + tester_rsp(BTP_SERVICE_ID_GAP, GAP_OOB_LEGACY_SET_DATA, + CONTROLLER_INDEX, BTP_STATUS_SUCCESS); +} + +static void oob_sc_get_local_data(const uint8_t *data, uint16_t len) +{ + struct gap_oob_sc_get_local_data_rp rp; + + memcpy(rp.r, oob_data_local.r, 16); + memcpy(rp.c, oob_data_local.c, 16); + + tester_send(BTP_SERVICE_ID_GAP, GAP_OOB_SC_GET_LOCAL_DATA, + CONTROLLER_INDEX, (uint8_t *) &rp, sizeof(rp)); +} + +static void oob_sc_set_remote_data(const uint8_t *data, uint16_t len) +{ + const struct gap_oob_sc_set_remote_data_cmd *cmd = (void *) data; + + ble_hs_cfg.sm_oob_data_flag = 1; + memcpy(oob_data_remote.r, cmd->r, 16); + memcpy(oob_data_remote.c, cmd->c, 16); + + tester_rsp(BTP_SERVICE_ID_GAP, GAP_OOB_SC_SET_REMOTE_DATA, + CONTROLLER_INDEX, BTP_STATUS_SUCCESS); +} + +static void set_mitm(const uint8_t *data, uint16_t len) +{ + const struct gap_set_mitm_cmd *cmd = (void *) data; + + ble_hs_cfg.sm_mitm = cmd->mitm; + + tester_rsp(BTP_SERVICE_ID_GAP, GAP_SET_MITM, + CONTROLLER_INDEX, BTP_STATUS_SUCCESS); +} + +static void set_filter_accept_list(const uint8_t *data, uint16_t len) +{ + uint8_t status = BTP_STATUS_SUCCESS; + struct gap_set_filter_accept_list_cmd *tmp = + (struct gap_set_filter_accept_list_cmd *) data; + + SYS_LOG_DBG(""); + + /* + * Check if the nb of bytes received matches the len of addrs list. + * Then set the filter accept list. + */ + if (((len - sizeof(tmp->list_len))/sizeof(ble_addr_t) != + tmp->list_len) || ble_gap_wl_set(tmp->addrs, tmp->list_len)) { + status = BTP_STATUS_FAILED; + } + + tester_rsp(BTP_SERVICE_ID_GAP, GAP_SET_FILTER_ACCEPT_LIST, + CONTROLLER_INDEX, status); +} + +void tester_handle_gap(uint8_t opcode, uint8_t index, uint8_t *data, + uint16_t len) +{ + switch (opcode) { + case GAP_READ_SUPPORTED_COMMANDS: + case GAP_READ_CONTROLLER_INDEX_LIST: + if (index != BTP_INDEX_NONE){ + tester_rsp(BTP_SERVICE_ID_GAP, opcode, index, + BTP_STATUS_FAILED); + return; + } + break; + default: + if (index != CONTROLLER_INDEX){ + tester_rsp(BTP_SERVICE_ID_GAP, opcode, index, + BTP_STATUS_FAILED); + return; + } + break; + } + + switch (opcode) { + case GAP_READ_SUPPORTED_COMMANDS: + supported_commands(data, len); + return; + case GAP_READ_CONTROLLER_INDEX_LIST: + controller_index_list(data, len); + return; + case GAP_READ_CONTROLLER_INFO: + controller_info(data, len); + return; + case GAP_SET_CONNECTABLE: + set_connectable(data, len); + return; + case GAP_SET_DISCOVERABLE: + set_discoverable(data, len); + return; + case GAP_SET_BONDABLE: + set_bondable(data, len); + return; + case GAP_START_ADVERTISING: + start_advertising(data, len); + return; + case GAP_STOP_ADVERTISING: + stop_advertising(data, len); + return; + case GAP_START_DISCOVERY: + start_discovery(data, len); + return; + case GAP_STOP_DISCOVERY: + stop_discovery(data, len); + return; + case GAP_CONNECT: + connect(data, len); + return; + case GAP_DISCONNECT: + disconnect(data, len); + return; + case GAP_SET_IO_CAP: + set_io_cap(data, len); + return; + case GAP_PAIR: + pair(data, len); + return; + case GAP_UNPAIR: + unpair(data, len); + return; + case GAP_PASSKEY_ENTRY: + passkey_entry(data, len); + return; + case GAP_PASSKEY_CONFIRM: + passkey_confirm(data, len); + return; + case GAP_START_DIRECT_ADV: + start_direct_adv(data, len); + return; + case GAP_CONN_PARAM_UPDATE: + conn_param_update_async(data, len); + return; + case GAP_OOB_LEGACY_SET_DATA: + oob_legacy_set_data(data, len); + return; + case GAP_OOB_SC_GET_LOCAL_DATA: + oob_sc_get_local_data(data, len); + return; + case GAP_OOB_SC_SET_REMOTE_DATA: + oob_sc_set_remote_data(data, len); + return; + case GAP_SET_MITM: + set_mitm(data, len); + return; + case GAP_SET_FILTER_ACCEPT_LIST: + set_filter_accept_list(data, len); + return; + default: + tester_rsp(BTP_SERVICE_ID_GAP, opcode, index, + BTP_STATUS_UNKNOWN_CMD); + return; + } +} + +static void tester_init_gap_cb(int err) +{ + if (err) { + tester_rsp(BTP_SERVICE_ID_CORE, CORE_REGISTER_SERVICE, + BTP_INDEX_NONE, BTP_STATUS_FAILED); + return; + } + + current_settings = 0; + current_settings |= BIT(GAP_SETTINGS_POWERED); + current_settings |= BIT(GAP_SETTINGS_LE); + + os_callout_init(&update_params_co, os_eventq_dflt_get(), + conn_param_update, NULL); + + os_callout_init(&connected_ev_co, os_eventq_dflt_get(), + device_connected_ev_send, NULL); + + tester_rsp(BTP_SERVICE_ID_CORE, CORE_REGISTER_SERVICE, BTP_INDEX_NONE, + BTP_STATUS_SUCCESS); +} + +uint8_t tester_init_gap(void) +{ +#if MYNEWT_VAL(BLE_SM_SC) + int rc; + + rc = ble_sm_sc_oob_generate_data(&oob_data_local); + if (rc) { + console_printf("Error: generating oob data; reason=%d\n", rc); + return BTP_STATUS_FAILED; + } +#endif +#if MYNEWT_VAL(BTTESTER_PRIVACY_MODE) && MYNEWT_VAL(BTTESTER_USE_NRPA) + os_callout_init(&bttester_nrpa_rotate_timer, os_eventq_dflt_get(), + rotate_nrpa_cb, NULL); +#endif + adv_buf = NET_BUF_SIMPLE(ADV_BUF_LEN); + + tester_init_gap_cb(BTP_STATUS_SUCCESS); + return BTP_STATUS_SUCCESS; +} + +uint8_t tester_unregister_gap(void) +{ + return BTP_STATUS_SUCCESS; +} diff --git a/lib/bt/host/nimble/nimble/apps/bttester/src/gatt.c b/lib/bt/host/nimble/nimble/apps/bttester/src/gatt.c new file mode 100644 index 00000000..412227d8 --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/bttester/src/gatt.c @@ -0,0 +1,2098 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* gatt.c - Bluetooth GATT Server Tester */ + +/* + * Copyright (c) 2015-2016 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +#include "host/ble_gap.h" +#include "host/ble_gatt.h" +#include "console/console.h" +#include "services/gatt/ble_svc_gatt.h" +#include "../../../nimble/host/src/ble_att_priv.h" +#include "../../../nimble/host/src/ble_gatt_priv.h" + +#include "bttester.h" + +#define CONTROLLER_INDEX 0 +#define MAX_BUFFER_SIZE 2048 + +/* 0000xxxx-8c26-476f-89a7-a108033a69c7 */ +#define PTS_UUID_DECLARE(uuid16) \ + ((const ble_uuid_t *) (&(ble_uuid128_t) BLE_UUID128_INIT( \ + 0xc7, 0x69, 0x3a, 0x03, 0x08, 0xa1, 0xa7, 0x89, \ + 0x6f, 0x47, 0x26, 0x8c, uuid16, uuid16 >> 8, 0x00, 0x00 \ + ))) + +/* 0000xxxx-8c26-476f-89a7-a108033a69c6 */ +#define PTS_UUID_DECLARE_ALT(uuid16) \ + ((const ble_uuid_t *) (&(ble_uuid128_t) BLE_UUID128_INIT( \ + 0xc6, 0x69, 0x3a, 0x03, 0x08, 0xa1, 0xa7, 0x89, \ + 0x6f, 0x47, 0x26, 0x8c, uuid16, uuid16 >> 8, 0x00, 0x00 \ + ))) + +#define PTS_SVC 0x0001 +#define PTS_CHR_READ 0x0002 +#define PTS_CHR_WRITE 0x0003 +#define PTS_CHR_RELIABLE_WRITE 0x0004 +#define PTS_CHR_WRITE_NO_RSP 0x0005 +#define PTS_CHR_READ_WRITE 0x0006 +#define PTS_CHR_READ_WRITE_ENC 0x0007 +#define PTS_CHR_READ_WRITE_AUTHEN 0x0008 +#define PTS_DSC_READ 0x0009 +#define PTS_DSC_WRITE 0x000a +#define PTS_DSC_READ_WRITE 0x000b +#define PTS_CHR_NOTIFY 0x0025 +#define PTS_LONG_CHR_READ_WRITE 0x0015 +#define PTS_LONG_CHR_READ_WRITE_ALT 0x0016 +#define PTS_LONG_DSC_READ_WRITE 0x001b +#define PTS_INC_SVC 0x001e +#define PTS_CHR_READ_WRITE_ALT 0x001f + +static uint8_t gatt_svr_pts_static_long_val[300]; +static uint8_t gatt_svr_pts_static_val[30]; +static uint8_t gatt_svr_pts_static_short_val; +static uint8_t notify_state; +static uint8_t indicate_state; +static uint16_t myconn_handle; +static struct os_callout notify_tx_timer; +uint16_t notify_handle; +uint8_t notify_value = 90; + +struct find_attr_data { + ble_uuid_any_t *uuid; + int attr_type; + void *ptr; + uint16_t handle; +}; + +static int +gatt_svr_read_write_test(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, + void *arg); + +static int +gatt_svr_read_write_auth_test(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, + void *arg); + +static int +gatt_svr_read_write_enc_test(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, + void *arg); + +static int +gatt_svr_dsc_read_write_test(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, + void *arg); + +static int +gatt_svr_write_no_rsp_test(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, + void *arg); + +static int +gatt_svr_rel_write_test(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, + void *arg); + +static int +gatt_svr_read_write_long_test(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, + void *arg); + +static int +gatt_svr_dsc_read_test(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, + void *arg); + +static int +gatt_svr_dsc_read_write_long_test(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, + void *arg); + +static const struct ble_gatt_svc_def gatt_svr_inc_svcs[] = { + { + .type = BLE_GATT_SVC_TYPE_PRIMARY, + .uuid = PTS_UUID_DECLARE(PTS_INC_SVC), + .characteristics = (struct ble_gatt_chr_def[]) {{ + .uuid = PTS_UUID_DECLARE(PTS_CHR_READ_WRITE_ALT), + .access_cb = gatt_svr_read_write_test, + .flags = BLE_GATT_CHR_F_WRITE | + BLE_GATT_CHR_F_READ, + }, { + 0, + } }, + }, + + { + 0, /* No more services. */ + }, +}; + +static const struct ble_gatt_svc_def *inc_svcs[] = { + &gatt_svr_inc_svcs[0], + NULL, +}; + +static const struct ble_gatt_svc_def gatt_svr_svcs[] = { + { + /*** Service: PTS test. */ + .type = BLE_GATT_SVC_TYPE_PRIMARY, + .uuid = PTS_UUID_DECLARE(PTS_SVC), + .includes = inc_svcs, + .characteristics = (struct ble_gatt_chr_def[]) { { + .uuid = PTS_UUID_DECLARE(PTS_CHR_READ_WRITE), + .access_cb = gatt_svr_read_write_test, + .flags = BLE_GATT_CHR_F_READ | + BLE_GATT_CHR_F_WRITE, + .descriptors = (struct ble_gatt_dsc_def[]) { { + .uuid = PTS_UUID_DECLARE(PTS_DSC_READ_WRITE), + .access_cb = gatt_svr_dsc_read_write_test, + .att_flags = BLE_ATT_F_READ | + BLE_ATT_F_WRITE, + }, { + .uuid = PTS_UUID_DECLARE(PTS_LONG_DSC_READ_WRITE), + .access_cb = gatt_svr_dsc_read_write_long_test, + .att_flags = BLE_ATT_F_READ | + BLE_ATT_F_WRITE, + }, { + .uuid = PTS_UUID_DECLARE(PTS_DSC_READ), + .access_cb = gatt_svr_dsc_read_test, + .att_flags = BLE_ATT_F_READ, + }, { + 0, /* No more descriptors in this characteristic */ + } } + }, { + .uuid = PTS_UUID_DECLARE(PTS_CHR_WRITE_NO_RSP), + .access_cb = gatt_svr_write_no_rsp_test, + .flags = BLE_GATT_CHR_F_READ | + BLE_GATT_CHR_F_WRITE | + BLE_GATT_CHR_F_WRITE_NO_RSP, + }, { + .uuid = PTS_UUID_DECLARE(PTS_CHR_READ_WRITE_AUTHEN), + .access_cb = gatt_svr_read_write_auth_test, + .flags = BLE_GATT_CHR_F_READ_AUTHEN | + BLE_GATT_CHR_F_READ | + BLE_GATT_CHR_F_WRITE_AUTHEN | + BLE_GATT_CHR_F_WRITE | + BLE_GATT_CHR_F_WRITE_AUTHEN, + }, { + .uuid = PTS_UUID_DECLARE(PTS_CHR_RELIABLE_WRITE), + .access_cb = gatt_svr_rel_write_test, + .flags = BLE_GATT_CHR_F_WRITE | + BLE_GATT_CHR_F_RELIABLE_WRITE, + }, { + .uuid = PTS_UUID_DECLARE(PTS_CHR_READ_WRITE_ENC), + .access_cb = gatt_svr_read_write_enc_test, + .flags = BLE_GATT_CHR_F_READ_ENC | + BLE_GATT_CHR_F_READ | + BLE_GATT_CHR_F_WRITE | + BLE_GATT_CHR_F_WRITE_ENC, + .min_key_size = 16, + }, { + .uuid = PTS_UUID_DECLARE(PTS_LONG_CHR_READ_WRITE), + .access_cb = gatt_svr_read_write_long_test, + .flags = BLE_GATT_CHR_F_WRITE | + BLE_GATT_CHR_F_READ, + }, { + .uuid = PTS_UUID_DECLARE(PTS_LONG_CHR_READ_WRITE_ALT), + .access_cb = gatt_svr_read_write_long_test, + .flags = BLE_GATT_CHR_F_WRITE | + BLE_GATT_CHR_F_READ, + }, { + .uuid = PTS_UUID_DECLARE(PTS_CHR_NOTIFY), + .access_cb = gatt_svr_read_write_test, + .val_handle = ¬ify_handle, + .flags = BLE_GATT_CHR_F_NOTIFY | + BLE_GATT_CHR_F_INDICATE, + }, { + 0, /* No more characteristics in this service. */ + } }, + }, + + { + .type = BLE_GATT_SVC_TYPE_PRIMARY, + .uuid = PTS_UUID_DECLARE_ALT(PTS_SVC), + .characteristics = (struct ble_gatt_chr_def[]) { { + .uuid = PTS_UUID_DECLARE_ALT(PTS_CHR_READ_WRITE), + .access_cb = gatt_svr_read_write_test, + .flags = BLE_GATT_CHR_F_WRITE | + BLE_GATT_CHR_F_READ, + }, { + 0, /* No more characteristics in this service */ + } }, + }, + + { + 0, /* No more services. */ + }, +}; + +static void attr_value_changed_ev(uint16_t handle, struct os_mbuf *data) +{ + struct gatt_attr_value_changed_ev *ev; + struct os_mbuf *buf = os_msys_get(0, 0); + + SYS_LOG_DBG(""); + + net_buf_simple_init(buf, 0); + ev = net_buf_simple_add(buf, sizeof(*ev)); + + ev->handle = sys_cpu_to_le16(handle); + ev->data_length = sys_cpu_to_le16(os_mbuf_len(data)); + os_mbuf_appendfrom(buf, data, 0, os_mbuf_len(data)); + + tester_send_buf(BTP_SERVICE_ID_GATT, GATT_EV_ATTR_VALUE_CHANGED, + CONTROLLER_INDEX, buf); +} + +static int +gatt_svr_chr_write(uint16_t conn_handle, uint16_t attr_handle, + struct os_mbuf *om, uint16_t min_len, uint16_t max_len, + void *dst, uint16_t *len) +{ + uint16_t om_len; + int rc; + + om_len = OS_MBUF_PKTLEN(om); + if (om_len < min_len || om_len > max_len) { + return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; + } + + rc = ble_hs_mbuf_to_flat(om, dst, max_len, len); + if (rc != 0) { + return BLE_ATT_ERR_UNLIKELY; + } + + attr_value_changed_ev(attr_handle, om); + + return 0; +} + +static uint16_t +extract_uuid16_from_pts_uuid128(const ble_uuid_t *uuid) +{ + const uint8_t *u8ptr; + uint16_t uuid16; + + u8ptr = BLE_UUID128(uuid)->value; + uuid16 = u8ptr[12]; + uuid16 |= (uint16_t)u8ptr[13] << 8; + return uuid16; +} + +static int +gatt_svr_read_write_test(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, + void *arg) +{ + uint16_t uuid16; + int rc; + + uuid16 = extract_uuid16_from_pts_uuid128(ctxt->chr->uuid); + assert(uuid16 != 0); + + switch (uuid16) { + case PTS_CHR_READ_WRITE: + case PTS_CHR_READ_WRITE_ALT: + if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) { + rc = gatt_svr_chr_write(conn_handle, attr_handle, + ctxt->om, 0, + sizeof gatt_svr_pts_static_short_val, + &gatt_svr_pts_static_short_val, NULL); + return rc; + } else if (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR) { + rc = os_mbuf_append(ctxt->om, &gatt_svr_pts_static_short_val, + sizeof gatt_svr_pts_static_short_val); + return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; + } + default: + assert(0); + return BLE_ATT_ERR_UNLIKELY; + } +} + +static int +gatt_svr_read_write_long_test(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, + void *arg) +{ + uint16_t uuid16; + int rc; + + uuid16 = extract_uuid16_from_pts_uuid128(ctxt->chr->uuid); + assert(uuid16 != 0); + + switch (uuid16) { + case PTS_LONG_CHR_READ_WRITE: + case PTS_LONG_CHR_READ_WRITE_ALT: + if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) { + rc = gatt_svr_chr_write(conn_handle, attr_handle, + ctxt->om, 0, + sizeof gatt_svr_pts_static_long_val, + &gatt_svr_pts_static_long_val, NULL); + return rc; + } else if (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR) { + rc = os_mbuf_append(ctxt->om, &gatt_svr_pts_static_long_val, + sizeof gatt_svr_pts_static_long_val); + return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; + } + default: + assert(0); + return BLE_ATT_ERR_UNLIKELY; + } +} + +static int +gatt_svr_read_write_auth_test(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, + void *arg) +{ + uint16_t uuid16; + int rc; + + uuid16 = extract_uuid16_from_pts_uuid128(ctxt->chr->uuid); + assert(uuid16 != 0); + + switch (uuid16) { + case PTS_CHR_READ_WRITE_AUTHEN: + if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) { + rc = gatt_svr_chr_write(conn_handle, attr_handle, + ctxt->om, 0, + sizeof gatt_svr_pts_static_val, + &gatt_svr_pts_static_val, NULL); + return rc; + } else if (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR) { + rc = os_mbuf_append(ctxt->om, &gatt_svr_pts_static_val, + sizeof gatt_svr_pts_static_val); + return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; + } + default: + assert(0); + return BLE_ATT_ERR_UNLIKELY; + } +} + +static int +gatt_svr_read_write_enc_test(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, + void *arg) +{ + uint16_t uuid16; + int rc; + + uuid16 = extract_uuid16_from_pts_uuid128(ctxt->chr->uuid); + assert(uuid16 != 0); + + switch (uuid16) { + case PTS_CHR_READ_WRITE_ENC: + if (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR) { + rc = os_mbuf_append(ctxt->om, &gatt_svr_pts_static_val, + sizeof gatt_svr_pts_static_val); + return rc; + } else if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) { + rc = gatt_svr_chr_write(conn_handle, attr_handle, + ctxt->om, 0, + sizeof gatt_svr_pts_static_val, + &gatt_svr_pts_static_val, NULL); + return rc; + } + default: + assert(0); + return BLE_ATT_ERR_UNLIKELY; + } +} + +static int +gatt_svr_dsc_read_write_test(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, + void *arg) +{ + uint16_t uuid16; + int rc; + + uuid16 = extract_uuid16_from_pts_uuid128(ctxt->chr->uuid); + assert(uuid16 != 0); + + switch (uuid16) { + case PTS_DSC_READ_WRITE: + if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_DSC) { + rc = gatt_svr_chr_write(conn_handle, attr_handle, + ctxt->om, 0, + sizeof gatt_svr_pts_static_short_val, + &gatt_svr_pts_static_short_val, NULL); + return rc; + } else if (ctxt->op == BLE_GATT_ACCESS_OP_READ_DSC) { + rc = os_mbuf_append(ctxt->om, &gatt_svr_pts_static_short_val, + sizeof gatt_svr_pts_static_short_val); + return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; + } + default: + assert(0); + return BLE_ATT_ERR_UNLIKELY; + } +} + +static int +gatt_svr_dsc_read_write_long_test(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, + void *arg) +{ + uint16_t uuid16; + int rc; + + uuid16 = extract_uuid16_from_pts_uuid128(ctxt->chr->uuid); + assert(uuid16 != 0); + + switch (uuid16) { + case PTS_LONG_DSC_READ_WRITE: + if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_DSC) { + rc = gatt_svr_chr_write(conn_handle, attr_handle, + ctxt->om, 0, + sizeof gatt_svr_pts_static_long_val, + &gatt_svr_pts_static_long_val, NULL); + return rc; + } else if (ctxt->op == BLE_GATT_ACCESS_OP_READ_DSC) { + rc = os_mbuf_append(ctxt->om, &gatt_svr_pts_static_long_val, + sizeof gatt_svr_pts_static_long_val); + return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; + } + default: + assert(0); + return BLE_ATT_ERR_UNLIKELY; + } +} + +static int +gatt_svr_dsc_read_test(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, + void *arg) +{ + uint16_t uuid16; + int rc; + + uuid16 = extract_uuid16_from_pts_uuid128(ctxt->chr->uuid); + assert(uuid16 != 0); + + switch (uuid16) { + case PTS_DSC_READ: + if (ctxt->op == BLE_GATT_ACCESS_OP_READ_DSC) { + rc = os_mbuf_append(ctxt->om, &gatt_svr_pts_static_long_val, + sizeof gatt_svr_pts_static_long_val); + return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; + } + default: + assert(0); + return BLE_ATT_ERR_UNLIKELY; + } +} + +static int +gatt_svr_write_no_rsp_test(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, + void *arg) +{ + uint16_t uuid16; + int rc; + + uuid16 = extract_uuid16_from_pts_uuid128(ctxt->chr->uuid); + assert(uuid16 != 0); + + switch (uuid16) { + case PTS_CHR_WRITE_NO_RSP: + if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) { + rc = gatt_svr_chr_write(conn_handle, attr_handle, + ctxt->om, 0, + sizeof gatt_svr_pts_static_short_val, + &gatt_svr_pts_static_short_val, NULL); + return rc; + } else if (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR) { + rc = os_mbuf_append(ctxt->om, &gatt_svr_pts_static_short_val, + sizeof gatt_svr_pts_static_short_val); + return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; + } + default: + assert(0); + return BLE_ATT_ERR_UNLIKELY; + } +} + +static int +gatt_svr_rel_write_test(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, + void *arg) +{ + uint16_t uuid16; + int rc; + + uuid16 = extract_uuid16_from_pts_uuid128(ctxt->chr->uuid); + assert(uuid16 != 0); + + switch (uuid16) { + case PTS_CHR_RELIABLE_WRITE: + if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) { + rc = gatt_svr_chr_write(conn_handle, attr_handle, + ctxt->om, 0, + sizeof gatt_svr_pts_static_val, + &gatt_svr_pts_static_val, NULL); + return rc; + } + + default: + assert(0); + return BLE_ATT_ERR_UNLIKELY; + } +} + +static void start_server(uint8_t *data, uint16_t len) +{ + struct gatt_start_server_rp rp; + + SYS_LOG_DBG(""); + + ble_gatts_show_local(); + + ble_svc_gatt_changed(0x0001, 0xffff); + + rp.db_attr_off = 0; + rp.db_attr_cnt = 0; + + tester_send(BTP_SERVICE_ID_GATT, GATT_START_SERVER, CONTROLLER_INDEX, + (uint8_t *) &rp, sizeof(rp)); +} + +/* Convert UUID from BTP command to bt_uuid */ +static uint8_t btp2bt_uuid(const uint8_t *uuid, uint8_t len, + ble_uuid_any_t *bt_uuid) +{ + uint16_t le16; + + switch (len) { + case 0x02: /* UUID 16 */ + bt_uuid->u.type = BLE_UUID_TYPE_16; + memcpy(&le16, uuid, sizeof(le16)); + BLE_UUID16(bt_uuid)->value = sys_le16_to_cpu(le16); + break; + case 0x10: /* UUID 128*/ + bt_uuid->u.type = BLE_UUID_TYPE_128; + memcpy(BLE_UUID128(bt_uuid)->value, uuid, 16); + break; + default: + return BTP_STATUS_FAILED; + } + return BTP_STATUS_SUCCESS; +} + +/* + * gatt_buf - cache used by a gatt client (to cache data read/discovered) + * and gatt server (to store attribute user_data). + * It is not intended to be used by client and server at the same time. + */ +static struct { + uint16_t len; + uint8_t buf[MAX_BUFFER_SIZE]; +} gatt_buf; + +static void *gatt_buf_add(const void *data, size_t len) +{ + void *ptr = gatt_buf.buf + gatt_buf.len; + + if ((len + gatt_buf.len) > MAX_BUFFER_SIZE) { + return NULL; + } + + if (data) { + memcpy(ptr, data, len); + } else { + (void)memset(ptr, 0, len); + } + + gatt_buf.len += len; + + SYS_LOG_DBG("%d/%d used", gatt_buf.len, MAX_BUFFER_SIZE); + + return ptr; +} + +static void *gatt_buf_reserve(size_t len) +{ + return gatt_buf_add(NULL, len); +} + +static void gatt_buf_clear(void) +{ + (void)memset(&gatt_buf, 0, sizeof(gatt_buf)); +} + +static void discover_destroy(void) +{ + gatt_buf_clear(); +} + +static void read_destroy(void) +{ + gatt_buf_clear(); +} + +static int read_cb(uint16_t conn_handle, + const struct ble_gatt_error *error, + struct ble_gatt_attr *attr, + void *arg) +{ + struct gatt_read_rp *rp = (void *) gatt_buf.buf; + uint8_t btp_opcode = (uint8_t) (int) arg; + + SYS_LOG_DBG("status=%d", error->status); + + if (error->status != 0 && error->status != BLE_HS_EDONE) { + rp->att_response = (uint8_t) BLE_HS_ATT_ERR(error->status); + tester_send(BTP_SERVICE_ID_GATT, btp_opcode, + CONTROLLER_INDEX, gatt_buf.buf, gatt_buf.len); + read_destroy(); + return 0; + } + + if (!gatt_buf_add(attr->om->om_data, attr->om->om_len)) { + tester_rsp(BTP_SERVICE_ID_GATT, btp_opcode, + CONTROLLER_INDEX, BTP_STATUS_FAILED); + read_destroy(); + return 0; + } + + rp->data_length += attr->om->om_len; + tester_send(BTP_SERVICE_ID_GATT, btp_opcode, + CONTROLLER_INDEX, gatt_buf.buf, gatt_buf.len); + read_destroy(); + + return 0; +} + +static void read(uint8_t *data, uint16_t len) +{ + const struct gatt_read_cmd *cmd = (void *) data; + struct ble_gap_conn_desc conn; + int rc; + + SYS_LOG_DBG(""); + + rc = ble_gap_conn_find_by_addr((ble_addr_t *)data, &conn); + if (rc) { + goto fail; + } + + /* Clear buffer */ + read_destroy(); + + if (!gatt_buf_reserve(sizeof(struct gatt_read_rp))) { + goto fail; + } + + if (ble_gattc_read(conn.conn_handle, sys_le16_to_cpu(cmd->handle), + read_cb, (void *)GATT_READ)) { + read_destroy(); + goto fail; + } + + return; + +fail: + tester_rsp(BTP_SERVICE_ID_GATT, GATT_READ, CONTROLLER_INDEX, + BTP_STATUS_FAILED); +} + +static int read_long_cb(uint16_t conn_handle, + const struct ble_gatt_error *error, + struct ble_gatt_attr *attr, + void *arg) +{ + struct gatt_read_rp *rp = (void *) gatt_buf.buf; + uint8_t btp_opcode = (uint8_t) (int) arg; + + SYS_LOG_DBG("status=%d", error->status); + + if (error->status != 0 && error->status != BLE_HS_EDONE) { + rp->att_response = (uint8_t) BLE_HS_ATT_ERR(error->status); + tester_send(BTP_SERVICE_ID_GATT, btp_opcode, + CONTROLLER_INDEX, gatt_buf.buf, gatt_buf.len); + read_destroy(); + return 0; + } + + if (error->status == BLE_HS_EDONE) { + tester_send(BTP_SERVICE_ID_GATT, btp_opcode, + CONTROLLER_INDEX, gatt_buf.buf, gatt_buf.len); + read_destroy(); + return 0; + } + + if (gatt_buf_add(attr->om->om_data, attr->om->om_len) == NULL) { + tester_rsp(BTP_SERVICE_ID_GATT, btp_opcode, + CONTROLLER_INDEX, BTP_STATUS_FAILED); + read_destroy(); + return BLE_HS_ENOMEM; + } + + rp->data_length += attr->om->om_len; + + return 0; +} + +static void read_long(uint8_t *data, uint16_t len) +{ + const struct gatt_read_long_cmd *cmd = (void *) data; + struct ble_gap_conn_desc conn; + int rc; + + SYS_LOG_DBG(""); + + rc = ble_gap_conn_find_by_addr((ble_addr_t *)data, &conn); + if (rc) { + goto fail; + } + + /* Clear buffer */ + read_destroy(); + + if (!gatt_buf_reserve(sizeof(struct gatt_read_rp))) { + goto fail; + } + + if (ble_gattc_read_long(conn.conn_handle, + sys_le16_to_cpu(cmd->handle), + sys_le16_to_cpu(cmd->offset), + read_long_cb, (void *)GATT_READ_LONG)) { + read_destroy(); + goto fail; + } + + return; + +fail: + tester_rsp(BTP_SERVICE_ID_GATT, GATT_READ_LONG, CONTROLLER_INDEX, + BTP_STATUS_FAILED); +} + +static void read_multiple(uint8_t *data, uint16_t len) +{ + const struct gatt_read_multiple_cmd *cmd = (void *) data; + uint16_t handles[cmd->handles_count]; + struct ble_gap_conn_desc conn; + int rc, i; + + SYS_LOG_DBG(""); + + for (i = 0; i < ARRAY_SIZE(handles); i++) { + handles[i] = sys_le16_to_cpu(cmd->handles[i]); + } + + rc = ble_gap_conn_find_by_addr((ble_addr_t *)data, &conn); + if (rc) { + goto fail; + } + + /* Clear buffer */ + read_destroy(); + + if (!gatt_buf_reserve(sizeof(struct gatt_read_rp))) { + goto fail; + } + + if (ble_gattc_read_mult(conn.conn_handle, handles, + cmd->handles_count, read_cb, + (void *)GATT_READ_MULTIPLE)) { + read_destroy(); + goto fail; + } + + return; + +fail: + tester_rsp(BTP_SERVICE_ID_GATT, GATT_READ_MULTIPLE, CONTROLLER_INDEX, + BTP_STATUS_FAILED); +} + +static void write_without_rsp(uint8_t *data, uint16_t len, uint8_t op, bool sign) +{ + const struct gatt_write_without_rsp_cmd *cmd = (void *) data; + struct ble_gap_conn_desc conn; + uint8_t status = BTP_STATUS_SUCCESS; + int rc; + + SYS_LOG_DBG(""); + + rc = ble_gap_conn_find_by_addr((ble_addr_t *)data, &conn); + if (rc) { + status = BTP_STATUS_FAILED; + goto rsp; + } + + if (ble_gattc_write_no_rsp_flat(conn.conn_handle, + sys_le16_to_cpu(cmd->handle), cmd->data, + sys_le16_to_cpu(cmd->data_length))) { + status = BTP_STATUS_FAILED; + } + +rsp: + tester_rsp(BTP_SERVICE_ID_GATT, op, CONTROLLER_INDEX, status); +} + +static int write_rsp(uint16_t conn_handle, const struct ble_gatt_error *error, + struct ble_gatt_attr *attr, + void *arg) +{ + uint8_t err = (uint8_t) error->status; + uint8_t btp_opcode = (uint8_t) (int) arg; + + SYS_LOG_DBG(""); + + tester_send(BTP_SERVICE_ID_GATT, btp_opcode, + CONTROLLER_INDEX, &err, sizeof(err)); + return 0; +} + +static void write(uint8_t *data, uint16_t len) +{ + const struct gatt_write_cmd *cmd = (void *) data; + struct ble_gap_conn_desc conn; + int rc; + + SYS_LOG_DBG(""); + + rc = ble_gap_conn_find_by_addr((ble_addr_t *)data, &conn); + if (rc) { + goto fail; + } + + if (ble_gattc_write_flat(conn.conn_handle, sys_le16_to_cpu(cmd->handle), + cmd->data, sys_le16_to_cpu(cmd->data_length), + write_rsp, (void *) GATT_WRITE)) { + goto fail; + } + + return; + +fail: + tester_rsp(BTP_SERVICE_ID_GATT, GATT_WRITE, CONTROLLER_INDEX, + BTP_STATUS_FAILED); +} + +static void write_long(uint8_t *data, uint16_t len) +{ + const struct gatt_write_long_cmd *cmd = (void *) data; + struct ble_gap_conn_desc conn; + struct os_mbuf *om = NULL; + int rc = 0; + + SYS_LOG_DBG(""); + + rc = ble_gap_conn_find_by_addr((ble_addr_t *)data, &conn); + if (rc) { + goto fail; + } + + om = ble_hs_mbuf_from_flat(cmd->data, sys_le16_to_cpu(cmd->data_length)); + if (!om) { + SYS_LOG_ERR("Insufficient resources"); + goto fail; + } + + rc = ble_gattc_write_long(conn.conn_handle, + sys_le16_to_cpu(cmd->handle), + sys_le16_to_cpu(cmd->offset), + om, write_rsp, + (void *) GATT_WRITE_LONG); + if (!rc) { + return; + } + +fail: + SYS_LOG_ERR("Failed to send Write Long request, rc=%d", rc); + os_mbuf_free_chain(om); + tester_rsp(BTP_SERVICE_ID_GATT, GATT_WRITE_LONG, CONTROLLER_INDEX, + BTP_STATUS_FAILED); +} + +static int reliable_write_rsp(uint16_t conn_handle, + const struct ble_gatt_error *error, + struct ble_gatt_attr *attrs, + uint8_t num_attrs, + void *arg) +{ + uint8_t err = (uint8_t) error->status; + + SYS_LOG_DBG("Reliable write status %d", err); + + tester_send(BTP_SERVICE_ID_GATT, GATT_RELIABLE_WRITE, + CONTROLLER_INDEX, &err, sizeof(err)); + return 0; +} + +static void reliable_write(uint8_t *data, uint16_t len) +{ + const struct gatt_reliable_write_cmd *cmd = (void *) data; + struct ble_gap_conn_desc conn; + struct ble_gatt_attr attr; + struct os_mbuf *om = NULL; + int rc; + + SYS_LOG_DBG(""); + + rc = ble_gap_conn_find_by_addr((ble_addr_t *)data, &conn); + if (rc) { + goto fail; + } + + om = ble_hs_mbuf_from_flat(cmd->data, sys_le16_to_cpu(cmd->data_length)); + /* This is required, because Nimble checks if + * the data is longer than offset + */ + if (os_mbuf_extend(om, sys_le16_to_cpu(cmd->offset) + 1) == NULL) { + goto fail; + } + + attr.handle = sys_le16_to_cpu(cmd->handle); + attr.offset = sys_le16_to_cpu(cmd->offset); + attr.om = om; + + if (ble_gattc_write_reliable(conn.conn_handle, &attr, 1, + reliable_write_rsp, NULL)) { + goto fail; + } + + return; + +fail: + os_mbuf_free_chain(om); + + tester_rsp(BTP_SERVICE_ID_GATT, GATT_WRITE_LONG, CONTROLLER_INDEX, + BTP_STATUS_FAILED); +} + +static struct bt_gatt_subscribe_params { + uint16_t ccc_handle; + uint16_t value; + uint16_t value_handle; +} subscribe_params; + +static void read_uuid(uint8_t *data, uint16_t len) +{ + const struct gatt_read_uuid_cmd *cmd = (void *) data; + struct ble_gap_conn_desc conn; + ble_uuid_any_t uuid; + int rc; + + SYS_LOG_DBG(""); + + rc = ble_gap_conn_find_by_addr((ble_addr_t *)data, &conn); + if (rc) { + goto fail; + } + + if (btp2bt_uuid(cmd->uuid, cmd->uuid_length, &uuid)) { + goto fail; + } + + /* Clear buffer */ + read_destroy(); + + if (!gatt_buf_reserve(sizeof(struct gatt_read_rp))) { + goto fail; + } + + if (ble_gattc_read_by_uuid(conn.conn_handle, + sys_le16_to_cpu(cmd->start_handle), + sys_le16_to_cpu(cmd->end_handle), &uuid.u, + read_long_cb, (void *)GATT_READ_UUID)) { + read_destroy(); + goto fail; + } + + return; + +fail: + tester_rsp(BTP_SERVICE_ID_GATT, GATT_READ, CONTROLLER_INDEX, + BTP_STATUS_FAILED); +} + +static int disc_prim_uuid_cb(uint16_t conn_handle, + const struct ble_gatt_error *error, + const struct ble_gatt_svc *gatt_svc, void *arg) +{ + struct gatt_disc_prim_uuid_rp *rp = (void *) gatt_buf.buf; + struct gatt_service *service; + const ble_uuid_any_t *uuid; + uint8_t uuid_length; + uint8_t opcode = (uint8_t) (int) arg; + + SYS_LOG_DBG(""); + + if (error->status != 0 && error->status != BLE_HS_EDONE) { + tester_rsp(BTP_SERVICE_ID_GATT, opcode, + CONTROLLER_INDEX, BTP_STATUS_FAILED); + discover_destroy(); + return 0; + } + + if (error->status == BLE_HS_EDONE) { + tester_send(BTP_SERVICE_ID_GATT, opcode, + CONTROLLER_INDEX, gatt_buf.buf, gatt_buf.len); + discover_destroy(); + return 0; + } + + uuid = &gatt_svc->uuid; + uuid_length = (uint8_t) (uuid->u.type == BLE_UUID_TYPE_16 ? 2 : 16); + + service = gatt_buf_reserve(sizeof(*service) + uuid_length); + if (!service) { + tester_rsp(BTP_SERVICE_ID_GATT, opcode, + CONTROLLER_INDEX, BTP_STATUS_FAILED); + discover_destroy(); + return BLE_HS_ENOMEM; + } + + service->start_handle = sys_cpu_to_le16(gatt_svc->start_handle); + service->end_handle = sys_cpu_to_le16(gatt_svc->end_handle); + service->uuid_length = uuid_length; + + if (uuid->u.type == BLE_UUID_TYPE_16) { + uint16_t u16 = sys_cpu_to_le16(BLE_UUID16(uuid)->value); + memcpy(service->uuid, &u16, uuid_length); + } else { + memcpy(service->uuid, BLE_UUID128(uuid)->value, + uuid_length); + } + + rp->services_count++; + + return 0; +} + +static int disc_all_desc_cb(uint16_t conn_handle, + const struct ble_gatt_error *error, + uint16_t chr_val_handle, + const struct ble_gatt_dsc *gatt_dsc, + void *arg) +{ + struct gatt_disc_all_desc_rp *rp = (void *) gatt_buf.buf; + struct gatt_descriptor *dsc; + const ble_uuid_any_t *uuid; + uint8_t uuid_length; + + SYS_LOG_DBG(""); + + if (error->status != 0 && error->status != BLE_HS_EDONE) { + tester_rsp(BTP_SERVICE_ID_GATT, GATT_DISC_ALL_DESC, + CONTROLLER_INDEX, BTP_STATUS_FAILED); + discover_destroy(); + return 0; + } + + if (error->status == BLE_HS_EDONE) { + tester_send(BTP_SERVICE_ID_GATT, GATT_DISC_ALL_DESC, + CONTROLLER_INDEX, gatt_buf.buf, gatt_buf.len); + discover_destroy(); + return 0; + } + + uuid = &gatt_dsc->uuid; + uuid_length = (uint8_t) (uuid->u.type == BLE_UUID_TYPE_16 ? 2 : 16); + + dsc = gatt_buf_reserve(sizeof(*dsc) + uuid_length); + if (!dsc) { + tester_rsp(BTP_SERVICE_ID_GATT, GATT_DISC_ALL_DESC, + CONTROLLER_INDEX, BTP_STATUS_FAILED); + discover_destroy(); + return BLE_HS_ENOMEM; + } + + dsc->descriptor_handle = sys_cpu_to_le16(gatt_dsc->handle); + dsc->uuid_length = uuid_length; + + if (uuid->u.type == BLE_UUID_TYPE_16) { + uint16_t u16 = sys_cpu_to_le16(BLE_UUID16(uuid)->value); + memcpy(dsc->uuid, &u16, uuid_length); + } else { + memcpy(dsc->uuid, BLE_UUID128(uuid)->value, uuid_length); + } + + rp->descriptors_count++; + + return 0; +} + +static void disc_all_prim_svcs(uint8_t *data, uint16_t len) +{ + struct ble_gap_conn_desc conn; + int rc; + + SYS_LOG_DBG(""); + + rc = ble_gap_conn_find_by_addr((ble_addr_t *)data, &conn); + if (rc) { + goto fail; + } + + if (!gatt_buf_reserve(sizeof(struct gatt_disc_all_prim_svcs_rp))) { + goto fail; + } + + if (ble_gattc_disc_all_svcs(conn.conn_handle, disc_prim_uuid_cb, + (void *) GATT_DISC_ALL_PRIM_SVCS)) { + discover_destroy(); + goto fail; + } + + return; + +fail: + tester_rsp(BTP_SERVICE_ID_GATT, GATT_DISC_ALL_PRIM_SVCS, + CONTROLLER_INDEX, BTP_STATUS_FAILED); +} + +static void disc_all_desc(uint8_t *data, uint16_t len) +{ + const struct gatt_disc_all_desc_cmd *cmd = (void *) data; + struct ble_gap_conn_desc conn; + uint16_t start_handle, end_handle; + int rc; + + SYS_LOG_DBG(""); + + rc = ble_gap_conn_find_by_addr((ble_addr_t *)data, &conn); + if (rc) { + goto fail; + } + + if (!gatt_buf_reserve(sizeof(struct gatt_disc_all_desc_rp))) { + goto fail; + } + + start_handle = sys_le16_to_cpu(cmd->start_handle) - 1; + end_handle = sys_le16_to_cpu(cmd->end_handle); + + rc = ble_gattc_disc_all_dscs(conn.conn_handle, start_handle, end_handle, + disc_all_desc_cb, NULL); + + SYS_LOG_DBG("rc=%d", rc); + + if (rc) { + discover_destroy(); + goto fail; + } + + return; + +fail: + tester_rsp(BTP_SERVICE_ID_GATT, GATT_DISC_ALL_DESC, CONTROLLER_INDEX, + BTP_STATUS_FAILED); +} + +static int find_included_cb(uint16_t conn_handle, + const struct ble_gatt_error *error, + const struct ble_gatt_svc *gatt_svc, void *arg) +{ + struct gatt_find_included_rp *rp = (void *) gatt_buf.buf; + struct gatt_included *included; + const ble_uuid_any_t *uuid; + int service_handle = (int) arg; + uint8_t uuid_length; + + SYS_LOG_DBG(""); + + if (error->status != 0 && error->status != BLE_HS_EDONE) { + tester_rsp(BTP_SERVICE_ID_GATT, GATT_FIND_INCLUDED, + CONTROLLER_INDEX, BTP_STATUS_FAILED); + discover_destroy(); + return 0; + } + + if (error->status == BLE_HS_EDONE) { + tester_send(BTP_SERVICE_ID_GATT, GATT_FIND_INCLUDED, + CONTROLLER_INDEX, gatt_buf.buf, gatt_buf.len); + discover_destroy(); + return 0; + } + + uuid = &gatt_svc->uuid; + uuid_length = (uint8_t) (uuid->u.type == BLE_UUID_TYPE_16 ? 2 : 16); + + + included = gatt_buf_reserve(sizeof(*included) + uuid_length); + if (!included) { + tester_rsp(BTP_SERVICE_ID_GATT, GATT_FIND_INCLUDED, + CONTROLLER_INDEX, BTP_STATUS_FAILED); + discover_destroy(); + return BLE_HS_ENOMEM; + } + + /* FIXME */ + included->included_handle = sys_cpu_to_le16(service_handle + 1 + + rp->services_count); + included->service.start_handle = sys_cpu_to_le16(gatt_svc->start_handle); + included->service.end_handle = sys_cpu_to_le16(gatt_svc->end_handle); + included->service.uuid_length = uuid_length; + + if (uuid->u.type == BLE_UUID_TYPE_16) { + uint16_t u16 = sys_cpu_to_le16(BLE_UUID16(uuid)->value); + memcpy(included->service.uuid, &u16, uuid_length); + } else { + memcpy(included->service.uuid, BLE_UUID128(uuid)->value, + uuid_length); + } + + rp->services_count++; + + return 0; +} + +static int disc_chrc_cb(uint16_t conn_handle, + const struct ble_gatt_error *error, + const struct ble_gatt_chr *gatt_chr, void *arg) +{ + struct gatt_disc_chrc_rp *rp = (void *) gatt_buf.buf; + struct gatt_characteristic *chrc; + const ble_uuid_any_t *uuid; + uint8_t btp_opcode = (uint8_t) (int) arg; + uint8_t uuid_length; + + SYS_LOG_DBG(""); + + if (error->status != 0 && error->status != BLE_HS_EDONE) { + tester_rsp(BTP_SERVICE_ID_GATT, btp_opcode, + CONTROLLER_INDEX, BTP_STATUS_FAILED); + discover_destroy(); + return 0; + } + + if (error->status == BLE_HS_EDONE) { + tester_send(BTP_SERVICE_ID_GATT, btp_opcode, + CONTROLLER_INDEX, gatt_buf.buf, gatt_buf.len); + discover_destroy(); + return 0; + } + + uuid = &gatt_chr->uuid; + uuid_length = (uint8_t) (uuid->u.type == BLE_UUID_TYPE_16 ? 2 : 16); + + chrc = gatt_buf_reserve(sizeof(*chrc) + uuid_length); + if (!chrc) { + tester_rsp(BTP_SERVICE_ID_GATT, btp_opcode, + CONTROLLER_INDEX, BTP_STATUS_FAILED); + discover_destroy(); + return BLE_HS_ENOMEM; + } + + chrc->characteristic_handle = sys_cpu_to_le16(gatt_chr->def_handle); + chrc->properties = gatt_chr->properties; + chrc->value_handle = sys_cpu_to_le16(gatt_chr->val_handle); + chrc->uuid_length = uuid_length; + + if (uuid->u.type == BLE_UUID_TYPE_16) { + uint16_t u16 = sys_cpu_to_le16(BLE_UUID16(uuid)->value); + memcpy(chrc->uuid, &u16, uuid_length); + } else { + memcpy(chrc->uuid, BLE_UUID128(uuid)->value, + uuid_length); + } + + rp->characteristics_count++; + + return 0; +} + +static void disc_chrc_uuid(uint8_t *data, uint16_t len) +{ + const struct gatt_disc_chrc_uuid_cmd *cmd = (void *) data; + struct ble_gap_conn_desc conn; + uint16_t start_handle, end_handle; + ble_uuid_any_t uuid; + int rc; + + SYS_LOG_DBG(""); + + rc = ble_gap_conn_find_by_addr((ble_addr_t *)data, &conn); + if (rc) { + goto fail; + } + + if (btp2bt_uuid(cmd->uuid, cmd->uuid_length, &uuid)) { + goto fail; + } + + if (!gatt_buf_reserve(sizeof(struct gatt_disc_chrc_rp))) { + goto fail; + } + + start_handle = sys_le16_to_cpu(cmd->start_handle); + end_handle = sys_le16_to_cpu(cmd->end_handle); + + if (ble_gattc_disc_chrs_by_uuid(conn.conn_handle, start_handle, + end_handle, &uuid.u, disc_chrc_cb, + (void *)GATT_DISC_CHRC_UUID)) { + discover_destroy(); + goto fail; + } + + return; + +fail: + tester_rsp(BTP_SERVICE_ID_GATT, GATT_DISC_CHRC_UUID, CONTROLLER_INDEX, + BTP_STATUS_FAILED); +} + +static void disc_prim_uuid(uint8_t *data, uint16_t len) +{ + const struct gatt_disc_prim_uuid_cmd *cmd = (void *) data; + struct ble_gap_conn_desc conn; + ble_uuid_any_t uuid; + int rc; + + SYS_LOG_DBG(""); + + rc = ble_gap_conn_find_by_addr((ble_addr_t *)data, &conn); + if (rc) { + goto fail; + } + + if (btp2bt_uuid(cmd->uuid, cmd->uuid_length, &uuid)) { + goto fail; + } + + if (!gatt_buf_reserve(sizeof(struct gatt_disc_prim_uuid_rp))) { + goto fail; + } + + if (ble_gattc_disc_svc_by_uuid(conn.conn_handle, + &uuid.u, disc_prim_uuid_cb, + (void *) GATT_DISC_PRIM_UUID)) { + discover_destroy(); + goto fail; + } + + return; + +fail: + tester_rsp(BTP_SERVICE_ID_GATT, GATT_DISC_PRIM_UUID, CONTROLLER_INDEX, + BTP_STATUS_FAILED); +} + +static void disc_all_chrc(uint8_t *data, uint16_t len) +{ + const struct gatt_disc_all_chrc_cmd *cmd = (void *) data; + struct ble_gap_conn_desc conn; + uint16_t start_handle, end_handle; + int rc; + + SYS_LOG_DBG(""); + + rc = ble_gap_conn_find_by_addr((ble_addr_t *)data, &conn); + if (rc) { + SYS_LOG_DBG("Conn find failed"); + goto fail; + } + + if (!gatt_buf_reserve(sizeof(struct gatt_disc_chrc_rp))) { + SYS_LOG_DBG("Buf reserve failed"); + goto fail; + } + + start_handle = sys_le16_to_cpu(cmd->start_handle); + end_handle = sys_le16_to_cpu(cmd->end_handle); + + rc = ble_gattc_disc_all_chrs(conn.conn_handle, start_handle, end_handle, + disc_chrc_cb, (void *)GATT_DISC_ALL_CHRC); + if (rc) { + discover_destroy(); + goto fail; + } + + return; + +fail: + tester_rsp(BTP_SERVICE_ID_GATT, GATT_DISC_ALL_CHRC, CONTROLLER_INDEX, + BTP_STATUS_FAILED); +} + +static void find_included(uint8_t *data, uint16_t len) +{ + const struct gatt_find_included_cmd *cmd = (void *) data; + struct ble_gap_conn_desc conn; + uint16_t start_handle, end_handle; + int service_handle_arg; + int rc; + + SYS_LOG_DBG(""); + + rc = ble_gap_conn_find_by_addr((ble_addr_t *)data, &conn); + if (rc) { + goto fail; + } + + if (!gatt_buf_reserve(sizeof(struct gatt_find_included_rp))) { + goto fail; + } + + start_handle = sys_le16_to_cpu(cmd->start_handle); + end_handle = sys_le16_to_cpu(cmd->end_handle); + service_handle_arg = start_handle; + + if (ble_gattc_find_inc_svcs(conn.conn_handle, start_handle, end_handle, + find_included_cb, + (void *)service_handle_arg)) { + discover_destroy(); + goto fail; + } + + return; + +fail: + tester_rsp(BTP_SERVICE_ID_GATT, GATT_FIND_INCLUDED, CONTROLLER_INDEX, + BTP_STATUS_FAILED); +} + +static int exchange_func(uint16_t conn_handle, + const struct ble_gatt_error *error, + uint16_t mtu, void *arg) +{ + SYS_LOG_DBG(""); + + if (error->status) { + tester_rsp(BTP_SERVICE_ID_GATT, GATT_EXCHANGE_MTU, + CONTROLLER_INDEX, BTP_STATUS_FAILED); + + return 0; +} + + tester_rsp(BTP_SERVICE_ID_GATT, GATT_EXCHANGE_MTU, CONTROLLER_INDEX, + BTP_STATUS_SUCCESS); + + return 0; +} + +static void exchange_mtu(uint8_t *data, uint16_t len) +{ + struct ble_gap_conn_desc conn; + int rc; + + SYS_LOG_DBG(""); + + rc = ble_gap_conn_find_by_addr((ble_addr_t *)data, &conn); + if (rc) { + goto fail; + } + + if (ble_gattc_exchange_mtu(conn.conn_handle, exchange_func, NULL)) { + goto fail; + } + + return; +fail: + tester_rsp(BTP_SERVICE_ID_GATT, GATT_EXCHANGE_MTU, + CONTROLLER_INDEX, BTP_STATUS_FAILED); +} + +static int enable_subscription(uint16_t conn_handle, uint16_t ccc_handle, + uint16_t value) +{ + uint8_t op; + + SYS_LOG_DBG(""); + + op = (uint8_t) (value == 0x0001 ? GATT_CFG_NOTIFY : GATT_CFG_INDICATE); + + if (ble_gattc_write_flat(conn_handle, ccc_handle, + &value, sizeof(value), NULL, NULL)) { + return -EINVAL; + } + + subscribe_params.ccc_handle = value; + + tester_rsp(BTP_SERVICE_ID_GATT, op, CONTROLLER_INDEX, + BTP_STATUS_SUCCESS); + return 0; +} + +static int disable_subscription(uint16_t conn_handle, uint16_t ccc_handle) +{ + uint16_t value = 0x00; + + SYS_LOG_DBG(""); + + /* Fail if CCC handle doesn't match */ + if (ccc_handle != subscribe_params.ccc_handle) { + SYS_LOG_ERR("CCC handle doesn't match"); + return -EINVAL; + } + + if (ble_gattc_write_no_rsp_flat(conn_handle, ccc_handle, + &value, sizeof(value))) { + return -EINVAL; + } + + subscribe_params.ccc_handle = 0; + return 0; +} + +static void config_subscription(uint8_t *data, uint16_t len, uint8_t op) +{ + const struct gatt_cfg_notify_cmd *cmd = (void *) data; + struct ble_gap_conn_desc conn; + uint16_t ccc_handle = sys_le16_to_cpu(cmd->ccc_handle); + uint8_t status; + int rc; + + SYS_LOG_DBG(""); + + rc = ble_gap_conn_find_by_addr((ble_addr_t *)data, &conn); + if (rc) { + tester_rsp(BTP_SERVICE_ID_GATT, op, CONTROLLER_INDEX, + BTP_STATUS_FAILED); + return; + } + + if (cmd->enable) { + uint16_t value; + + if (op == GATT_CFG_NOTIFY) { + value = 0x0001; + } else { + value = 0x0002; + } + + /* on success response will be sent from callback */ + if (enable_subscription(conn.conn_handle, + ccc_handle, value) == 0) { + return; + } + + status = BTP_STATUS_FAILED; + } else { + if (disable_subscription(conn.conn_handle, ccc_handle) < 0) { + status = BTP_STATUS_FAILED; + } else { + status = BTP_STATUS_SUCCESS; + } + } + + SYS_LOG_DBG("Config subscription (op %u) status %u", op, status); + + tester_rsp(BTP_SERVICE_ID_GATT, op, CONTROLLER_INDEX, status); +} + +#define BTP_PERM_F_READ 0x01 +#define BTP_PERM_F_WRITE 0x02 +#define BTP_PERM_F_READ_ENC 0x04 +#define BTP_PERM_F_WRITE_ENC 0x08 +#define BTP_PERM_F_READ_AUTHEN 0x10 +#define BTP_PERM_F_WRITE_AUTHEN 0x20 +#define BTP_PERM_F_READ_AUTHOR 0x40 +#define BTP_PERM_F_WRITE_AUTHOR 0x80 + +static int flags_hs2btp_map[] = { + BTP_PERM_F_READ, + BTP_PERM_F_WRITE, + BTP_PERM_F_READ_ENC, + BTP_PERM_F_READ_AUTHEN, + BTP_PERM_F_READ_AUTHOR, + BTP_PERM_F_WRITE_ENC, + BTP_PERM_F_WRITE_AUTHEN, + BTP_PERM_F_WRITE_AUTHOR, +}; + +static uint8_t flags_hs2btp(uint8_t flags) +{ + int i; + uint8_t ret = 0; + + for (i = 0; i < 8; ++i) { + if (flags & BIT(i)) { + ret |= flags_hs2btp_map[i]; + } + } + + return ret; +} + +static void get_attrs(uint8_t *data, uint16_t len) +{ + const struct gatt_get_attributes_cmd *cmd = (void *) data; + struct gatt_get_attributes_rp *rp; + struct gatt_attr *gatt_attr; + struct os_mbuf *buf = os_msys_get(0, 0); + uint16_t start_handle, end_handle; + struct ble_att_svr_entry *entry = NULL; + ble_uuid_any_t uuid; + ble_uuid_t *uuid_ptr = NULL; + uint8_t count = 0; + char str[BLE_UUID_STR_LEN]; + + SYS_LOG_DBG(""); + + memset(str, 0, sizeof(str)); + memset(&uuid, 0, sizeof(uuid)); + start_handle = sys_le16_to_cpu(cmd->start_handle); + end_handle = sys_le16_to_cpu(cmd->end_handle); + + if (cmd->type_length) { + if (btp2bt_uuid(cmd->type, cmd->type_length, &uuid)) { + goto fail; + } + + ble_uuid_to_str(&uuid.u, str); + SYS_LOG_DBG("start 0x%04x end 0x%04x, uuid %s", start_handle, + end_handle, str); + + uuid_ptr = &uuid.u; + } else { + SYS_LOG_DBG("start 0x%04x end 0x%04x", start_handle, end_handle); + } + + net_buf_simple_init(buf, 0); + rp = net_buf_simple_add(buf, sizeof(*rp)); + + entry = ble_att_svr_find_by_uuid(entry, uuid_ptr, end_handle); + while (entry) { + + if (entry->ha_handle_id < start_handle) { + entry = ble_att_svr_find_by_uuid(entry, + uuid_ptr, end_handle); + continue; + } + + gatt_attr = net_buf_simple_add(buf, sizeof(*gatt_attr)); + gatt_attr->handle = sys_cpu_to_le16(entry->ha_handle_id); + gatt_attr->permission = flags_hs2btp(entry->ha_flags); + + if (entry->ha_uuid->type == BLE_UUID_TYPE_16) { + gatt_attr->type_length = 2; + net_buf_simple_add_le16(buf, + BLE_UUID16(entry->ha_uuid)->value); + } else { + gatt_attr->type_length = 16; + net_buf_simple_add_mem(buf, + BLE_UUID128(entry->ha_uuid)->value, + gatt_attr->type_length); + } + + count++; + + entry = ble_att_svr_find_by_uuid(entry, uuid_ptr, end_handle); + } + + rp->attrs_count = count; + + tester_send_buf(BTP_SERVICE_ID_GATT, GATT_GET_ATTRIBUTES, + CONTROLLER_INDEX, buf); + + goto free; +fail: + tester_rsp(BTP_SERVICE_ID_GATT, GATT_GET_ATTRIBUTES, CONTROLLER_INDEX, + BTP_STATUS_FAILED); +free: + os_mbuf_free_chain(buf); +} + +static void get_attr_val(uint8_t *data, uint16_t len) +{ + const struct gatt_get_attribute_value_cmd *cmd = (void *) data; + struct gatt_get_attribute_value_rp *rp; + struct ble_gap_conn_desc conn; + struct os_mbuf *buf = os_msys_get(0, 0); + uint16_t handle = sys_cpu_to_le16(cmd->handle); + uint8_t out_att_err; + int conn_status; + + conn_status = ble_gap_conn_find_by_addr((ble_addr_t *)data, &conn); + + if (conn_status) { + net_buf_simple_init(buf, 0); + rp = net_buf_simple_add(buf, sizeof(*rp)); + + ble_att_svr_read_handle(BLE_HS_CONN_HANDLE_NONE, + handle, 0, buf, + &out_att_err); + + rp->att_response = out_att_err; + rp->value_length = os_mbuf_len(buf) - sizeof(*rp); + + tester_send_buf(BTP_SERVICE_ID_GATT, GATT_GET_ATTRIBUTE_VALUE, + CONTROLLER_INDEX, buf); + + goto free; + } else { + net_buf_simple_init(buf, 0); + rp = net_buf_simple_add(buf, sizeof(*rp)); + + ble_att_svr_read_handle(conn.conn_handle, + handle, 0, buf, + &out_att_err); + + rp->att_response = out_att_err; + rp->value_length = os_mbuf_len(buf) - sizeof(*rp); + + tester_send_buf(BTP_SERVICE_ID_GATT, GATT_GET_ATTRIBUTE_VALUE, + CONTROLLER_INDEX, buf); + + goto free; + } + +free: + os_mbuf_free_chain(buf); +} + +static void change_database(uint8_t *data, uint16_t len) +{ + const struct gatt_change_database *cmd = (void *) data; + + SYS_LOG_DBG("") + + ble_gatts_show_local(); + + ble_svc_gatt_changed(cmd->start_handle, cmd->end_handle); + + tester_rsp(BTP_SERVICE_ID_GATT, GATT_CHANGE_DATABASE, CONTROLLER_INDEX, + BTP_STATUS_SUCCESS); + + return; +} + +static void supported_commands(uint8_t *data, uint16_t len) +{ + uint8_t cmds[4]; + struct gatt_read_supported_commands_rp *rp = (void *) cmds; + + SYS_LOG_DBG(""); + + memset(cmds, 0, sizeof(cmds)); + + tester_set_bit(cmds, GATT_READ_SUPPORTED_COMMANDS); + tester_set_bit(cmds, GATT_START_SERVER); + tester_set_bit(cmds, GATT_EXCHANGE_MTU); + tester_set_bit(cmds, GATT_DISC_ALL_PRIM_SVCS); + tester_set_bit(cmds, GATT_DISC_PRIM_UUID); + tester_set_bit(cmds, GATT_FIND_INCLUDED); + tester_set_bit(cmds, GATT_DISC_ALL_CHRC); + tester_set_bit(cmds, GATT_DISC_CHRC_UUID); + tester_set_bit(cmds, GATT_DISC_ALL_DESC); + tester_set_bit(cmds, GATT_READ); + tester_set_bit(cmds, GATT_READ_LONG); + tester_set_bit(cmds, GATT_READ_MULTIPLE); + tester_set_bit(cmds, GATT_WRITE_WITHOUT_RSP); +#if 0 + tester_set_bit(cmds, GATT_SIGNED_WRITE_WITHOUT_RSP); +#endif + tester_set_bit(cmds, GATT_WRITE); + tester_set_bit(cmds, GATT_WRITE_LONG); + tester_set_bit(cmds, GATT_CFG_NOTIFY); + tester_set_bit(cmds, GATT_CFG_INDICATE); + tester_set_bit(cmds, GATT_GET_ATTRIBUTES); + tester_set_bit(cmds, GATT_GET_ATTRIBUTE_VALUE); + tester_set_bit(cmds, GATT_CHANGE_DATABASE); + + tester_send(BTP_SERVICE_ID_GATT, GATT_READ_SUPPORTED_COMMANDS, + CONTROLLER_INDEX, (uint8_t *) rp, sizeof(cmds)); +} + +enum attr_type { + BLE_GATT_ATTR_SVC = 0, + BLE_GATT_ATTR_CHR, + BLE_GATT_ATTR_DSC, +}; + +void tester_handle_gatt(uint8_t opcode, uint8_t index, uint8_t *data, + uint16_t len) +{ + switch (opcode) { + case GATT_READ_SUPPORTED_COMMANDS: + supported_commands(data, len); + return; + case GATT_START_SERVER: + start_server(data, len); + return; + case GATT_EXCHANGE_MTU: + exchange_mtu(data, len); + return; + case GATT_DISC_ALL_PRIM_SVCS: + disc_all_prim_svcs(data, len); + return; + case GATT_DISC_PRIM_UUID: + disc_prim_uuid(data, len); + return; + case GATT_FIND_INCLUDED: + find_included(data, len); + return; + case GATT_DISC_ALL_CHRC: + disc_all_chrc(data, len); + return; + case GATT_DISC_CHRC_UUID: + disc_chrc_uuid(data, len); + return; + case GATT_DISC_ALL_DESC: + disc_all_desc(data, len); + return; + case GATT_CHANGE_DATABASE: + change_database(data, len); + return; + case GATT_READ: + read(data, len); + return; + case GATT_READ_UUID: + read_uuid(data, len); + return; + case GATT_READ_LONG: + read_long(data, len); + return; + case GATT_READ_MULTIPLE: + read_multiple(data, len); + return; + case GATT_WRITE_WITHOUT_RSP: + write_without_rsp(data, len, opcode, false); + return; +#if 0 + case GATT_SIGNED_WRITE_WITHOUT_RSP: + write_without_rsp(data, len, opcode, true); + return; +#endif + case GATT_WRITE: + write(data, len); + return; + case GATT_WRITE_LONG: + write_long(data, len); + return; + case GATT_RELIABLE_WRITE: + reliable_write(data, len); + return; + case GATT_CFG_NOTIFY: + case GATT_CFG_INDICATE: + config_subscription(data, len, opcode); + return; + case GATT_GET_ATTRIBUTES: + get_attrs(data, len); + return; + case GATT_GET_ATTRIBUTE_VALUE: + get_attr_val(data, len); + return; + default: + tester_rsp(BTP_SERVICE_ID_GATT, opcode, index, + BTP_STATUS_UNKNOWN_CMD); + return; + } +} + +int tester_gatt_notify_rx_ev(uint16_t conn_handle, uint16_t attr_handle, + uint8_t indication, struct os_mbuf *om) +{ + struct gatt_notification_ev *ev; + struct ble_gap_conn_desc conn; + struct os_mbuf *buf = os_msys_get(0, 0); + const ble_addr_t *addr; + + SYS_LOG_DBG(""); + + if (!subscribe_params.ccc_handle) { + goto fail; + } + + if (ble_gap_conn_find(conn_handle, &conn)) { + goto fail; + } + + net_buf_simple_init(buf, 0); + ev = net_buf_simple_add(buf, sizeof(*ev)); + + addr = &conn.peer_ota_addr; + + ev->address_type = addr->type; + memcpy(ev->address, addr->val, sizeof(ev->address)); + ev->type = (uint8_t) (indication ? 0x02 : 0x01); + ev->handle = sys_cpu_to_le16(attr_handle); + ev->data_length = sys_cpu_to_le16(os_mbuf_len(om)); + os_mbuf_appendfrom(buf, om, 0, os_mbuf_len(om)); + + tester_send_buf(BTP_SERVICE_ID_GATT, GATT_EV_NOTIFICATION, + CONTROLLER_INDEX, buf); + +fail: + os_mbuf_free_chain(buf); + return 0; +} + +void notify_test_stop(void) +{ + os_callout_stop(¬ify_tx_timer); +} + +void notify_test_reset(void) +{ + int rc; + + rc = os_callout_reset(¬ify_tx_timer, OS_TICKS_PER_SEC); + assert(rc == 0); +} + +void notify_test(struct os_event *ev) +{ + static uint8_t ntf[1]; + struct os_mbuf *om; + int rc; + + if (!notify_state && !indicate_state) { + notify_test_stop(); + notify_value = 90; + return; + } + + ntf[0] = notify_value; + + notify_value++; + if (notify_value == 160) { + notify_value = 90; + } + + om = ble_hs_mbuf_from_flat(ntf, sizeof(ntf)); + + if (notify_state) { + rc = ble_gatts_notify_custom(myconn_handle, notify_handle, om); + assert(rc == 0); + } + + if (indicate_state) { + rc = ble_gatts_indicate_custom(myconn_handle, notify_handle, om); + assert(rc == 0); + } +} + +int tester_gatt_subscribe_ev(uint16_t conn_handle, uint16_t attr_handle, uint8_t reason, + uint8_t prev_notify, uint8_t cur_notify, + uint8_t prev_indicate, uint8_t cur_indicate) +{ + SYS_LOG_DBG(""); + myconn_handle = conn_handle; + + if (cur_notify == 0 && cur_indicate == 0) { + SYS_LOG_INF("Unsubscribed"); + memset(&subscribe_params, 0, sizeof(subscribe_params)); + return 0; + } + + if (cur_notify) { + SYS_LOG_INF("Subscribed to notifications"); + if (attr_handle == notify_handle) { + notify_state = cur_notify; + } + } + + if (cur_indicate) { + SYS_LOG_INF("Subscribed to indications"); + if (attr_handle == notify_handle) { + indicate_state = cur_indicate; + } + } + + + if (notify_state || indicate_state) { + notify_test_reset(); + } else { + notify_test_stop(); + } + + return 0; +} + +void +gatt_svr_register_cb(struct ble_gatt_register_ctxt *ctxt, void *arg) +{ + char buf[BLE_UUID_STR_LEN]; + + switch (ctxt->op) { + case BLE_GATT_REGISTER_OP_SVC: + MODLOG_DFLT(DEBUG, "registered service %s with handle=%d\n", + ble_uuid_to_str(ctxt->svc.svc_def->uuid, buf), + ctxt->svc.handle); + break; + + case BLE_GATT_REGISTER_OP_CHR: + MODLOG_DFLT(DEBUG, "registering characteristic %s with " + "def_handle=%d val_handle=%d\n", + ble_uuid_to_str(ctxt->chr.chr_def->uuid, buf), + ctxt->chr.def_handle, + ctxt->chr.val_handle); + break; + + case BLE_GATT_REGISTER_OP_DSC: + MODLOG_DFLT(DEBUG, "registering descriptor %s with handle=%d\n", + ble_uuid_to_str(ctxt->dsc.dsc_def->uuid, buf), + ctxt->dsc.handle); + break; + + default: + assert(0); + break; + } +} + +int gatt_svr_init(void) +{ + int rc; + + rc = ble_gatts_count_cfg(gatt_svr_inc_svcs); + if (rc != 0) { + return rc; + } + + rc = ble_gatts_add_svcs(gatt_svr_inc_svcs); + if (rc != 0) { + return rc; + } + + rc = ble_gatts_count_cfg(gatt_svr_svcs); + if (rc != 0) { + return rc; + } + + rc = ble_gatts_add_svcs(gatt_svr_svcs); + if (rc != 0) { + return rc; + } + + return 0; +} + +uint8_t tester_init_gatt(void) +{ + os_callout_init(¬ify_tx_timer, os_eventq_dflt_get(), + notify_test, NULL); + + return BTP_STATUS_SUCCESS; +} + +uint8_t tester_unregister_gatt(void) +{ + return BTP_STATUS_SUCCESS; +} diff --git a/lib/bt/host/nimble/nimble/apps/bttester/src/gatt_cl.c b/lib/bt/host/nimble/nimble/apps/bttester/src/gatt_cl.c new file mode 100644 index 00000000..c551bb17 --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/bttester/src/gatt_cl.c @@ -0,0 +1,1483 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#include +#include + +#include "host/ble_gap.h" +#include "host/ble_gatt.h" +#include "console/console.h" +#include "services/gatt/ble_svc_gatt.h" +#include "../../../nimble/host/src/ble_att_priv.h" +#include "../../../nimble/host/src/ble_gatt_priv.h" + +#include "bttester.h" + +#define CONTROLLER_INDEX 0 +#define MAX_BUFFER_SIZE 2048 + +/* Convert UUID from BTP command to bt_uuid */ +static uint8_t btp2bt_uuid(const uint8_t *uuid, uint8_t len, + ble_uuid_any_t *bt_uuid) +{ + uint16_t le16; + + switch (len) { + case 0x02: /* UUID 16 */ + bt_uuid->u.type = BLE_UUID_TYPE_16; + memcpy(&le16, uuid, sizeof(le16)); + BLE_UUID16(bt_uuid)->value = sys_le16_to_cpu(le16); + break; + case 0x10: /* UUID 128*/ + bt_uuid->u.type = BLE_UUID_TYPE_128; + memcpy(BLE_UUID128(bt_uuid)->value, uuid, 16); + break; + default: + return BTP_STATUS_FAILED; + } + return BTP_STATUS_SUCCESS; +} + +/* + * gatt_buf - cache used by a gatt client (to cache data read/discovered) + * and gatt server (to store attribute user_data). + * It is not intended to be used by client and server at the same time. + */ +static struct { + uint16_t len; + uint8_t buf[MAX_BUFFER_SIZE]; + uint16_t cnt; +} gatt_buf; +static struct bt_gatt_subscribe_params { + uint16_t ccc_handle; + uint16_t value; + uint16_t value_handle; +} subscribe_params; + +static void *gatt_buf_add(const void *data, size_t len) +{ + void *ptr = gatt_buf.buf + gatt_buf.len; + + if ((len + gatt_buf.len) > MAX_BUFFER_SIZE) { + return NULL; + } + + if (data) { + memcpy(ptr, data, len); + } else { + (void) memset(ptr, 0, len); + } + + gatt_buf.len += len; + + SYS_LOG_DBG("%d/%d used", gatt_buf.len, MAX_BUFFER_SIZE); + + return ptr; +} + +static void *gatt_buf_reserve(size_t len) +{ + return gatt_buf_add(NULL, len); +} + +static void gatt_buf_clear(void) +{ + (void) memset(&gatt_buf, 0, sizeof(gatt_buf)); +} + +static void discover_destroy(void) +{ + gatt_buf_clear(); +} + +static void read_destroy() +{ + gatt_buf_clear(); +} + +static int tester_mtu_exchanged_ev(uint16_t conn_handle, + const struct ble_gatt_error *error, + uint16_t mtu, void *arg) +{ + struct gattc_exchange_mtu_ev *ev; + struct ble_gap_conn_desc conn; + struct os_mbuf *buf = os_msys_get(0, 0); + const ble_addr_t *addr; + + SYS_LOG_DBG(""); + + if (ble_gap_conn_find(conn_handle, &conn)) { + goto fail; + } + + net_buf_simple_init(buf, 0); + ev = net_buf_simple_add(buf, sizeof(*ev)); + + addr = &conn.peer_ota_addr; + + ev->address_type = addr->type; + memcpy(ev->address, addr->val, sizeof(ev->address)); + + ev->mtu = mtu; + + tester_send_buf(BTP_SERVICE_ID_GATTC, GATTC_EV_MTU_EXCHANGED, + CONTROLLER_INDEX, buf); +fail: + os_mbuf_free_chain(buf); + return 0; +} + +static void exchange_mtu(uint8_t *data, uint16_t len) +{ + struct ble_gap_conn_desc conn; + uint8_t status = BTP_STATUS_SUCCESS; + int rc; + + SYS_LOG_DBG(""); + + rc = ble_gap_conn_find_by_addr((ble_addr_t *) data, &conn); + if (rc) { + status = BTP_STATUS_FAILED; + goto rsp; + } + + if (ble_gattc_exchange_mtu(conn.conn_handle, tester_mtu_exchanged_ev, NULL)) { + status = BTP_STATUS_FAILED; + goto rsp; + } + +rsp: + tester_rsp(BTP_SERVICE_ID_GATTC, GATTC_EXCHANGE_MTU, + CONTROLLER_INDEX, status); +} + +static int disc_prim_svcs_cb(uint16_t conn_handle, + const struct ble_gatt_error *error, + const struct ble_gatt_svc *gatt_svc, void *arg) +{ + struct gattc_disc_prim_svcs_rp *rp; + struct ble_gap_conn_desc conn; + struct gatt_service *service; + const ble_uuid_any_t *uuid; + const ble_addr_t *addr; + uint8_t uuid_length; + struct os_mbuf *buf = os_msys_get(0, 0); + uint8_t opcode = (uint8_t) (int) arg; + uint8_t err = (uint8_t) error->status; + int rc = 0; + + SYS_LOG_DBG(""); + + if (ble_gap_conn_find(conn_handle, &conn)) { + goto free; + } + + net_buf_simple_init(buf, 0); + rp = net_buf_simple_add(buf, sizeof(*rp)); + + addr = &conn.peer_ota_addr; + + rp->address_type = addr->type; + memcpy(rp->address, addr->val, sizeof(rp->address)); + + rp->status = err; + if (error->status != 0 && error->status != BLE_HS_EDONE) { + rp->services_count = 0; + tester_send_buf(BTP_SERVICE_ID_GATTC, opcode, + CONTROLLER_INDEX, buf); + discover_destroy(); + goto free; + } + + if (error->status == BLE_HS_EDONE) { + rp->status = 0; + rp->services_count = gatt_buf.cnt; + os_mbuf_append(buf, gatt_buf.buf, gatt_buf.len); + tester_send_buf(BTP_SERVICE_ID_GATTC, opcode, + CONTROLLER_INDEX, buf); + discover_destroy(); + goto free; + } + + uuid = &gatt_svc->uuid; + uuid_length = (uint8_t) (uuid->u.type == BLE_UUID_TYPE_16 ? 2 : 16); + + service = gatt_buf_reserve(sizeof(*service) + uuid_length); + if (!service) { + discover_destroy(); + rc = BLE_HS_ENOMEM; + goto free; + } + + service->start_handle = sys_cpu_to_le16(gatt_svc->start_handle); + service->end_handle = sys_cpu_to_le16(gatt_svc->end_handle); + service->uuid_length = uuid_length; + + if (uuid->u.type == BLE_UUID_TYPE_16) { + uint16_t u16 = sys_cpu_to_le16(BLE_UUID16(uuid)->value); + memcpy(service->uuid, &u16, uuid_length); + } else { + memcpy(service->uuid, BLE_UUID128(uuid)->value, + uuid_length); + } + + gatt_buf.cnt++; + +free: + os_mbuf_free_chain(buf); + return rc; +} + +static void disc_all_prim_svcs(uint8_t *data, uint16_t len) +{ + struct ble_gap_conn_desc conn; + uint8_t status = BTP_STATUS_SUCCESS; + int rc; + + SYS_LOG_DBG(""); + + rc = ble_gap_conn_find_by_addr((ble_addr_t *) data, &conn); + if (rc) { + status = BTP_STATUS_FAILED; + goto rsp; + } + + if (ble_gattc_disc_all_svcs(conn.conn_handle, disc_prim_svcs_cb, + (void *) GATTC_DISC_ALL_PRIM_RP)) { + discover_destroy(); + status = BTP_STATUS_FAILED; + goto rsp; + } + +rsp: + tester_rsp(BTP_SERVICE_ID_GATTC, GATTC_DISC_ALL_PRIM_SVCS, + CONTROLLER_INDEX, status); +} + +static void disc_prim_uuid(uint8_t *data, uint16_t len) +{ + const struct gattc_disc_prim_uuid_cmd *cmd = (void *) data; + struct ble_gap_conn_desc conn; + ble_uuid_any_t uuid; + uint8_t status = BTP_STATUS_SUCCESS; + int rc; + + SYS_LOG_DBG(""); + + rc = ble_gap_conn_find_by_addr((ble_addr_t *) data, &conn); + if (rc) { + status = BTP_STATUS_FAILED; + goto rsp; + } + + if (btp2bt_uuid(cmd->uuid, cmd->uuid_length, &uuid)) { + status = BTP_STATUS_FAILED; + goto rsp; + } + + if (ble_gattc_disc_svc_by_uuid(conn.conn_handle, + &uuid.u, disc_prim_svcs_cb, + (void *) GATTC_DISC_PRIM_UUID_RP)) { + discover_destroy(); + status = BTP_STATUS_FAILED; + goto rsp; + } + +rsp: + tester_rsp(BTP_SERVICE_ID_GATTC, GATTC_DISC_PRIM_UUID, CONTROLLER_INDEX, + status); +} + +static int find_included_cb(uint16_t conn_handle, + const struct ble_gatt_error *error, + const struct ble_gatt_svc *gatt_svc, void *arg) +{ + struct gattc_find_included_rp *rp; + struct gatt_included *included; + const ble_uuid_any_t *uuid; + int service_handle = (int) arg; + uint8_t uuid_length; + uint8_t err = (uint8_t) error->status; + struct os_mbuf *buf = os_msys_get(0, 0); + const ble_addr_t *addr; + struct ble_gap_conn_desc conn; + int rc = 0; + + SYS_LOG_DBG(""); + + if (ble_gap_conn_find(conn_handle, &conn)) { + rc = BLE_HS_EINVAL; + goto free; + } + + net_buf_simple_init(buf, 0); + rp = net_buf_simple_add(buf, sizeof(*rp)); + + addr = &conn.peer_ota_addr; + + rp->address_type = addr->type; + memcpy(rp->address, addr->val, sizeof(rp->address)); + + rp->status = err; + + SYS_LOG_DBG(""); + if (error->status != 0 && error->status != BLE_HS_EDONE) { + rp->services_count = 0; + tester_send_buf(BTP_SERVICE_ID_GATTC, GATTC_FIND_INCLUDED_RP, + CONTROLLER_INDEX, buf); + discover_destroy(); + goto free; + } + + if (error->status == BLE_HS_EDONE) { + rp->status = 0; + rp->services_count = gatt_buf.cnt; + os_mbuf_append(buf, gatt_buf.buf, gatt_buf.len); + tester_send_buf(BTP_SERVICE_ID_GATTC, GATTC_FIND_INCLUDED_RP, + CONTROLLER_INDEX, buf); + discover_destroy(); + goto free; + } + + uuid = &gatt_svc->uuid; + uuid_length = (uint8_t) (uuid->u.type == BLE_UUID_TYPE_16 ? 2 : 16); + + included = gatt_buf_reserve(sizeof(*included) + uuid_length); + if (!included) { + discover_destroy(); + rc = BLE_HS_ENOMEM; + goto free; + } + + included->included_handle = sys_cpu_to_le16(service_handle + 1 + + rp->services_count); + included->service.start_handle = sys_cpu_to_le16(gatt_svc->start_handle); + included->service.end_handle = sys_cpu_to_le16(gatt_svc->end_handle); + included->service.uuid_length = uuid_length; + + if (uuid->u.type == BLE_UUID_TYPE_16) { + uint16_t u16 = sys_cpu_to_le16(BLE_UUID16(uuid)->value); + memcpy(included->service.uuid, &u16, uuid_length); + } else { + memcpy(included->service.uuid, BLE_UUID128(uuid)->value, + uuid_length); + } + + gatt_buf.cnt++; + +free: + os_mbuf_free_chain(buf); + return rc; +} + +static void find_included(uint8_t *data, uint16_t len) +{ + const struct gattc_find_included_cmd *cmd = (void *) data; + struct ble_gap_conn_desc conn; + uint16_t start_handle, end_handle; + int service_handle_arg; + uint8_t status = BTP_STATUS_SUCCESS; + int rc; + + SYS_LOG_DBG(""); + + rc = ble_gap_conn_find_by_addr((ble_addr_t *) data, &conn); + if (rc) { + status = BTP_STATUS_FAILED; + goto rsp; + } + + start_handle = sys_le16_to_cpu(cmd->start_handle); + end_handle = sys_le16_to_cpu(cmd->end_handle); + service_handle_arg = start_handle; + + if (ble_gattc_find_inc_svcs(conn.conn_handle, start_handle, end_handle, + find_included_cb, + (void *) service_handle_arg)) { + discover_destroy(); + status = BTP_STATUS_FAILED; + goto rsp; + } + +rsp: + tester_rsp(BTP_SERVICE_ID_GATTC, GATTC_FIND_INCLUDED, CONTROLLER_INDEX, + status); +} + +static int disc_chrc_cb(uint16_t conn_handle, + const struct ble_gatt_error *error, + const struct ble_gatt_chr *gatt_chr, void *arg) +{ + struct gattc_disc_chrc_rp *rp; + struct gatt_characteristic *chrc; + const ble_uuid_any_t *uuid; + uint8_t uuid_length; + uint8_t opcode = (uint8_t) (int) arg; + uint8_t err = (uint8_t) error->status; + struct os_mbuf *buf = os_msys_get(0, 0); + const ble_addr_t *addr; + struct ble_gap_conn_desc conn; + int rc = 0; + + SYS_LOG_DBG(""); + if (ble_gap_conn_find(conn_handle, &conn)) { + goto free; + } + + net_buf_simple_init(buf, 0); + rp = net_buf_simple_add(buf, sizeof(*rp)); + + addr = &conn.peer_ota_addr; + + rp->address_type = addr->type; + memcpy(rp->address, addr->val, sizeof(rp->address)); + + rp->status = err; + + if (ble_gap_conn_find(conn_handle, &conn)) { + rc = BLE_HS_EINVAL; + goto free; + } + + if (error->status != 0 && error->status != BLE_HS_EDONE) { + rp->characteristics_count = 0; + tester_send_buf(BTP_SERVICE_ID_GATTC, opcode, + CONTROLLER_INDEX, buf); + discover_destroy(); + goto free; + } + + if (error->status == BLE_HS_EDONE) { + rp->status = 0; + rp->characteristics_count = gatt_buf.cnt; + os_mbuf_append(buf, gatt_buf.buf, gatt_buf.len); + tester_send_buf(BTP_SERVICE_ID_GATTC, opcode, + CONTROLLER_INDEX, buf); + discover_destroy(); + goto free; + } + + uuid = &gatt_chr->uuid; + uuid_length = (uint8_t) (uuid->u.type == BLE_UUID_TYPE_16 ? 2 : 16); + + chrc = gatt_buf_reserve(sizeof(*chrc) + uuid_length); + if (!chrc) { + discover_destroy(); + rc = BLE_HS_ENOMEM; + goto free; + } + + chrc->characteristic_handle = sys_cpu_to_le16(gatt_chr->def_handle); + chrc->properties = gatt_chr->properties; + chrc->value_handle = sys_cpu_to_le16(gatt_chr->val_handle); + chrc->uuid_length = uuid_length; + + if (uuid->u.type == BLE_UUID_TYPE_16) { + uint16_t u16 = sys_cpu_to_le16(BLE_UUID16(uuid)->value); + memcpy(chrc->uuid, &u16, uuid_length); + } else { + memcpy(chrc->uuid, BLE_UUID128(uuid)->value, + uuid_length); + } + + gatt_buf.cnt++; +free: + os_mbuf_free_chain(buf); + return rc; +} + +static void disc_all_chrc(uint8_t *data, uint16_t len) +{ + const struct gattc_disc_all_chrc_cmd *cmd = (void *) data; + struct ble_gap_conn_desc conn; + uint16_t start_handle, end_handle; + uint8_t status = BTP_STATUS_SUCCESS; + int rc; + + SYS_LOG_DBG(""); + + rc = ble_gap_conn_find_by_addr((ble_addr_t *) data, &conn); + if (rc) { + SYS_LOG_DBG("Conn find rsped"); + status = BTP_STATUS_FAILED; + goto rsp; + } + + start_handle = sys_le16_to_cpu(cmd->start_handle); + end_handle = sys_le16_to_cpu(cmd->end_handle); + + rc = ble_gattc_disc_all_chrs(conn.conn_handle, start_handle, end_handle, + disc_chrc_cb, (void *) GATTC_DISC_ALL_CHRC_RP); + if (rc) { + discover_destroy(); + status = BTP_STATUS_FAILED; + goto rsp; + } + +rsp: + tester_rsp(BTP_SERVICE_ID_GATTC, GATTC_DISC_ALL_CHRC, CONTROLLER_INDEX, + status); +} + +static void disc_chrc_uuid(uint8_t *data, uint16_t len) +{ + const struct gattc_disc_chrc_uuid_cmd *cmd = (void *) data; + struct ble_gap_conn_desc conn; + uint16_t start_handle, end_handle; + ble_uuid_any_t uuid; + uint8_t status = BTP_STATUS_SUCCESS; + int rc; + + SYS_LOG_DBG(""); + + rc = ble_gap_conn_find_by_addr((ble_addr_t *) data, &conn); + if (rc) { + status = BTP_STATUS_FAILED; + goto rsp; + } + + if (btp2bt_uuid(cmd->uuid, cmd->uuid_length, &uuid)) { + status = BTP_STATUS_FAILED; + goto rsp; + } + + start_handle = sys_le16_to_cpu(cmd->start_handle); + end_handle = sys_le16_to_cpu(cmd->end_handle); + + rc = ble_gattc_disc_chrs_by_uuid(conn.conn_handle, start_handle, + end_handle, &uuid.u, disc_chrc_cb, + (void *) GATTC_DISC_CHRC_UUID_RP); + if (rc) { + discover_destroy(); + status = BTP_STATUS_FAILED; + goto rsp; + } + +rsp: + tester_rsp(BTP_SERVICE_ID_GATTC, GATTC_DISC_CHRC_UUID, CONTROLLER_INDEX, + status); +} + +static int disc_all_desc_cb(uint16_t conn_handle, + const struct ble_gatt_error *error, + uint16_t chr_val_handle, + const struct ble_gatt_dsc *gatt_dsc, + void *arg) +{ + struct gattc_disc_all_desc_rp *rp; + struct gatt_descriptor *dsc; + const ble_uuid_any_t *uuid; + uint8_t uuid_length; + uint8_t err = (uint8_t) error->status; + struct os_mbuf *buf = os_msys_get(0, 0); + const ble_addr_t *addr; + struct ble_gap_conn_desc conn; + int rc = 0; + + SYS_LOG_DBG(""); + + if (ble_gap_conn_find(conn_handle, &conn)) { + rc = BLE_HS_EINVAL; + goto free; + } + + net_buf_simple_init(buf, 0); + rp = net_buf_simple_add(buf, sizeof(*rp)); + + addr = &conn.peer_ota_addr; + + rp->address_type = addr->type; + memcpy(rp->address, addr->val, sizeof(rp->address)); + + rp->status = err; + + if (error->status != 0 && error->status != BLE_HS_EDONE) { + rp->descriptors_count = 0; + tester_send_buf(BTP_SERVICE_ID_GATTC, GATTC_DISC_ALL_DESC_RP, + CONTROLLER_INDEX, buf); + discover_destroy(); + goto free; + } + + if (error->status == BLE_HS_EDONE) { + rp->status = 0; + rp->descriptors_count = gatt_buf.cnt; + os_mbuf_append(buf, gatt_buf.buf, gatt_buf.len); + tester_send_buf(BTP_SERVICE_ID_GATTC, GATTC_DISC_ALL_DESC_RP, + CONTROLLER_INDEX, buf); + discover_destroy(); + goto free; + } + + uuid = &gatt_dsc->uuid; + uuid_length = (uint8_t) (uuid->u.type == BLE_UUID_TYPE_16 ? 2 : 16); + + dsc = gatt_buf_reserve(sizeof(*dsc) + uuid_length); + if (!dsc) { + discover_destroy(); + rc = BLE_HS_ENOMEM; + goto free; + } + + dsc->descriptor_handle = sys_cpu_to_le16(gatt_dsc->handle); + dsc->uuid_length = uuid_length; + + if (uuid->u.type == BLE_UUID_TYPE_16) { + uint16_t u16 = sys_cpu_to_le16(BLE_UUID16(uuid)->value); + memcpy(dsc->uuid, &u16, uuid_length); + } else { + memcpy(dsc->uuid, BLE_UUID128(uuid)->value, uuid_length); + } + + gatt_buf.cnt++; + +free: + os_mbuf_free_chain(buf); + return rc; +} + +static void disc_all_desc(uint8_t *data, uint16_t len) +{ + const struct gattc_disc_all_desc_cmd *cmd = (void *) data; + struct ble_gap_conn_desc conn; + uint16_t start_handle, end_handle; + uint8_t status = BTP_STATUS_SUCCESS; + int rc; + + SYS_LOG_DBG(""); + + rc = ble_gap_conn_find_by_addr((ble_addr_t *) data, &conn); + if (rc) { + status = BTP_STATUS_FAILED; + goto rsp; + } + + start_handle = sys_le16_to_cpu(cmd->start_handle) - 1; + end_handle = sys_le16_to_cpu(cmd->end_handle); + + rc = ble_gattc_disc_all_dscs(conn.conn_handle, start_handle, end_handle, + disc_all_desc_cb, (void *) GATTC_DISC_ALL_DESC); + + SYS_LOG_DBG("rc=%d", rc); + + if (rc) { + discover_destroy(); + status = BTP_STATUS_FAILED; + goto rsp; + } + +rsp: + tester_rsp(BTP_SERVICE_ID_GATTC, GATTC_DISC_ALL_DESC, CONTROLLER_INDEX, + status); +} + +static int read_cb(uint16_t conn_handle, + const struct ble_gatt_error *error, + struct ble_gatt_attr *attr, + void *arg) +{ + struct gattc_read_rp *rp; + uint8_t opcode = (uint8_t) (int) arg; + struct os_mbuf *buf = os_msys_get(0, 0); + const ble_addr_t *addr; + struct ble_gap_conn_desc conn; + int rc = 0; + + if (ble_gap_conn_find(conn_handle, &conn)) { + rc = BLE_HS_EINVAL; + goto free; + } + + net_buf_simple_init(buf, 0); + rp = net_buf_simple_add(buf, sizeof(*rp)); + + addr = &conn.peer_ota_addr; + + rp->address_type = addr->type; + memcpy(rp->address, addr->val, sizeof(rp->address)); + + SYS_LOG_DBG("status=%d", error->status); + + if (error->status != 0 && error->status != BLE_HS_EDONE) { + rp->status = (uint8_t) BLE_HS_ATT_ERR(error->status); + rp->data_length = 0; + tester_send_buf(BTP_SERVICE_ID_GATTC, opcode, + CONTROLLER_INDEX, buf); + read_destroy(); + goto free; + } + + if (!gatt_buf_add(attr->om->om_data, attr->om->om_len)) { + read_destroy(); + rc = BLE_HS_ENOMEM; + goto free; + } + + rp->status = 0; + rp->data_length = attr->om->om_len; + os_mbuf_appendfrom(buf, attr->om, 0, os_mbuf_len(attr->om)); + tester_send_buf(BTP_SERVICE_ID_GATTC, opcode, + CONTROLLER_INDEX, buf); + read_destroy(); +free: + os_mbuf_free_chain(buf); + return rc; +} + +static void read(uint8_t *data, uint16_t len) +{ + const struct gattc_read_cmd *cmd = (void *) data; + struct ble_gap_conn_desc conn; + uint8_t status = BTP_STATUS_SUCCESS; + int rc; + + SYS_LOG_DBG(""); + + rc = ble_gap_conn_find_by_addr((ble_addr_t *) data, &conn); + if (rc) { + status = BTP_STATUS_FAILED; + goto rsp; + } + + /* Clear buffer */ + read_destroy(); + + if (ble_gattc_read(conn.conn_handle, sys_le16_to_cpu(cmd->handle), + read_cb, (void *) GATTC_READ_RP)) { + read_destroy(); + status = BTP_STATUS_FAILED; + goto rsp; + } + +rsp: + tester_rsp(BTP_SERVICE_ID_GATTC, GATTC_READ, CONTROLLER_INDEX, + status); +} + +static int read_uuid_cb(uint16_t conn_handle, + const struct ble_gatt_error *error, + struct ble_gatt_attr *attr, + void *arg) +{ + struct gattc_read_uuid_rp *rp; + struct gatt_read_uuid_chr *chr; + uint8_t opcode = (uint8_t) (int) arg; + uint8_t err = (uint8_t) error->status; + struct os_mbuf *buf = os_msys_get(0, 0); + const ble_addr_t *addr; + struct ble_gap_conn_desc conn; + int rc = 0; + static uint16_t attr_len; + + SYS_LOG_DBG("status=%d", error->status); + + if (ble_gap_conn_find(conn_handle, &conn)) { + rc = BLE_HS_EINVAL; + goto free; + } + + net_buf_simple_init(buf, 0); + rp = net_buf_simple_add(buf, sizeof(*rp)); + + addr = &conn.peer_ota_addr; + + rp->address_type = addr->type; + memcpy(rp->address, addr->val, sizeof(rp->address)); + + rp->status = err; + + if (error->status != 0 && error->status != BLE_HS_EDONE) { + rp->data_length = 0; + tester_send_buf(BTP_SERVICE_ID_GATTC, opcode, + CONTROLLER_INDEX, buf); + read_destroy(); + goto free; + } + + if (error->status == BLE_HS_EDONE) { + rp->data_length = gatt_buf.len; + rp->value_length = attr_len; + rp->status = 0; + os_mbuf_append(buf, gatt_buf.buf+1, gatt_buf.len-1); + tester_send_buf(BTP_SERVICE_ID_GATTC, opcode, + CONTROLLER_INDEX, buf); + read_destroy(); + goto free; + } + + if (error->status == 0) { + attr_len = attr->om->om_len; + } + + if (gatt_buf_add(attr->om->om_data, attr->om->om_len) == NULL) { + read_destroy(); + rc = BLE_HS_ENOMEM; + goto free; + } + + chr = gatt_buf_reserve(sizeof(*chr) + attr->om->om_len); + if (!chr) { + read_destroy(); + rc = BLE_HS_ENOMEM; + goto free; + } + + chr->handle = sys_cpu_to_be16(attr->handle); + memcpy(chr->data, attr->om->om_data, attr->om->om_len); + +free: + os_mbuf_free_chain(buf); + return rc; +} + +static void read_uuid(uint8_t *data, uint16_t len) +{ + const struct gattc_read_uuid_cmd *cmd = (void *) data; + struct ble_gap_conn_desc conn; + ble_uuid_any_t uuid; + uint8_t status = BTP_STATUS_SUCCESS; + int rc; + + SYS_LOG_DBG(""); + + rc = ble_gap_conn_find_by_addr((ble_addr_t *) data, &conn); + if (rc) { + status = BTP_STATUS_FAILED; + goto rsp; + } + + if (btp2bt_uuid(cmd->uuid, cmd->uuid_length, &uuid)) { + status = BTP_STATUS_FAILED; + goto rsp; + } + + /* Clear buffer */ + read_destroy(); + + if (ble_gattc_read_by_uuid(conn.conn_handle, + sys_le16_to_cpu(cmd->start_handle), + sys_le16_to_cpu(cmd->end_handle), &uuid.u, + read_uuid_cb, (void *) GATTC_READ_UUID_RP)) { + read_destroy(); + status = BTP_STATUS_FAILED; + } + +rsp: + tester_rsp(BTP_SERVICE_ID_GATTC, GATTC_READ_UUID, CONTROLLER_INDEX, + status); +} + +static int read_long_cb(uint16_t conn_handle, + const struct ble_gatt_error *error, + struct ble_gatt_attr *attr, + void *arg) +{ + struct gattc_read_rp *rp;; + uint8_t opcode = (uint8_t) (int) arg; + uint8_t err = (uint8_t) error->status; + struct os_mbuf *buf = os_msys_get(0, 0); + const ble_addr_t *addr; + struct ble_gap_conn_desc conn; + int rc = 0; + + SYS_LOG_DBG("status=%d", error->status); + + if (ble_gap_conn_find(conn_handle, &conn)) { + rc = BLE_HS_EINVAL; + goto free; + } + + net_buf_simple_init(buf, 0); + rp = net_buf_simple_add(buf, sizeof(*rp)); + + addr = &conn.peer_ota_addr; + + rp->address_type = addr->type; + memcpy(rp->address, addr->val, sizeof(rp->address)); + + rp->status = err; + + if (error->status != 0 && error->status != BLE_HS_EDONE) { + rp->data_length = 0; + tester_send_buf(BTP_SERVICE_ID_GATTC, opcode, + CONTROLLER_INDEX, buf); + read_destroy(); + goto free; + } + + if (error->status == BLE_HS_EDONE) { + rp->status = 0; + rp->data_length = gatt_buf.len; + os_mbuf_append(buf, gatt_buf.buf, gatt_buf.len); + tester_send_buf(BTP_SERVICE_ID_GATTC, opcode, + CONTROLLER_INDEX, buf); + read_destroy(); + goto free; + } + + if (gatt_buf_add(attr->om->om_data, attr->om->om_len) == NULL) { + read_destroy(); + rc = BLE_HS_ENOMEM; + goto free; + } + + rp->data_length += attr->om->om_len; + +free: + os_mbuf_free_chain(buf); + return rc; +} + +static void read_long(uint8_t *data, uint16_t len) +{ + const struct gattc_read_long_cmd *cmd = (void *) data; + struct ble_gap_conn_desc conn; + uint8_t status = BTP_STATUS_SUCCESS; + int rc; + + SYS_LOG_DBG(""); + + rc = ble_gap_conn_find_by_addr((ble_addr_t *) data, &conn); + if (rc) { + status = BTP_STATUS_FAILED; + goto rsp; + } + + /* Clear buffer */ + read_destroy(); + + if (ble_gattc_read_long(conn.conn_handle, + sys_le16_to_cpu(cmd->handle), + sys_le16_to_cpu(cmd->offset), + read_long_cb, (void *) GATTC_READ_LONG_RP)) { + read_destroy(); + status = BTP_STATUS_FAILED; + } + +rsp: + tester_rsp(BTP_SERVICE_ID_GATTC, GATTC_READ_LONG, CONTROLLER_INDEX, + status); +} + +static void read_multiple(uint8_t *data, uint16_t len) +{ + const struct gattc_read_multiple_cmd *cmd = (void *) data; + uint16_t handles[cmd->handles_count]; + struct ble_gap_conn_desc conn; + uint8_t status = BTP_STATUS_SUCCESS; + int rc, i; + + SYS_LOG_DBG(""); + + for (i = 0; i < ARRAY_SIZE(handles); i++) { + handles[i] = sys_le16_to_cpu(cmd->handles[i]); + } + + rc = ble_gap_conn_find_by_addr((ble_addr_t *) data, &conn); + if (rc) { + status = BTP_STATUS_FAILED; + goto rsp; + } + + /* Clear buffer */ + read_destroy(); + + if (ble_gattc_read_mult(conn.conn_handle, handles, + cmd->handles_count, read_cb, (void *) GATTC_READ_MULTIPLE_RP)) { + read_destroy(); + status = BTP_STATUS_FAILED; + } + +rsp: + tester_rsp(BTP_SERVICE_ID_GATTC, GATTC_READ_MULTIPLE, CONTROLLER_INDEX, + status); +} + +static void write_without_rsp(uint8_t *data, uint16_t len, uint8_t op, bool sign) +{ + const struct gattc_write_without_rsp_cmd *cmd = (void *) data; + struct ble_gap_conn_desc conn; + uint8_t status = BTP_STATUS_SUCCESS; + int rc; + + SYS_LOG_DBG(""); + + rc = ble_gap_conn_find_by_addr((ble_addr_t *) data, &conn); + if (rc) { + status = BTP_STATUS_FAILED; + goto rsp; + } + + if (ble_gattc_write_no_rsp_flat(conn.conn_handle, + sys_le16_to_cpu(cmd->handle), cmd->data, + sys_le16_to_cpu(cmd->data_length))) { + status = BTP_STATUS_FAILED; + } + +rsp: + tester_rsp(BTP_SERVICE_ID_GATTC, op, CONTROLLER_INDEX, status); +} + +static int write_cb(uint16_t conn_handle, const struct ble_gatt_error *error, + struct ble_gatt_attr *attr, + void *arg) +{ + struct gattc_write_rp *rp; + uint8_t err = (uint8_t) error->status; + uint8_t opcode = (uint8_t) (int) arg; + struct os_mbuf *buf = os_msys_get(0, 0); + const ble_addr_t *addr; + struct ble_gap_conn_desc conn; + int rc = 0; + + SYS_LOG_DBG(""); + + if (ble_gap_conn_find(conn_handle, &conn)) { + rc = BLE_HS_EINVAL; + goto free; + } + + net_buf_simple_init(buf, 0); + rp = net_buf_simple_add(buf, sizeof(*rp)); + + addr = &conn.peer_ota_addr; + + rp->address_type = addr->type; + memcpy(rp->address, addr->val, sizeof(rp->address)); + + rp->status = err; + tester_send_buf(BTP_SERVICE_ID_GATTC, opcode, + CONTROLLER_INDEX, buf); +free: + os_mbuf_free_chain(buf); + return rc; +} + +static void write(uint8_t *data, uint16_t len) +{ + const struct gattc_write_cmd *cmd = (void *) data; + struct ble_gap_conn_desc conn; + uint8_t status = BTP_STATUS_SUCCESS; + int rc; + + SYS_LOG_DBG(""); + + rc = ble_gap_conn_find_by_addr((ble_addr_t *) data, &conn); + if (rc) { + status = BTP_STATUS_FAILED; + goto rsp; + } + + if (ble_gattc_write_flat(conn.conn_handle, sys_le16_to_cpu(cmd->handle), + cmd->data, sys_le16_to_cpu(cmd->data_length), + write_cb, (void *) GATTC_WRITE_RP)) { + status = BTP_STATUS_FAILED; + } + +rsp: + tester_rsp(BTP_SERVICE_ID_GATTC, GATTC_WRITE, CONTROLLER_INDEX, + status); +} + +static void write_long(uint8_t *data, uint16_t len) +{ + const struct gattc_write_long_cmd *cmd = (void *) data; + struct ble_gap_conn_desc conn; + struct os_mbuf *om = NULL; + uint8_t status = BTP_STATUS_SUCCESS; + int rc = 0; + + SYS_LOG_DBG(""); + + rc = ble_gap_conn_find_by_addr((ble_addr_t *) data, &conn); + if (rc) { + goto fail; + } + + om = ble_hs_mbuf_from_flat(cmd->data, sys_le16_to_cpu(cmd->data_length)); + if (!om) { + SYS_LOG_ERR("Insufficient resources"); + status = BTP_STATUS_FAILED; + goto fail; + } + + rc = ble_gattc_write_long(conn.conn_handle, + sys_le16_to_cpu(cmd->handle), + sys_le16_to_cpu(cmd->offset), + om, write_cb, + (void *) GATTC_WRITE_LONG_RP); + if (rc) { + status = BTP_STATUS_FAILED; + } else { + goto rsp; + } + +fail: + SYS_LOG_ERR("Failed to send Write Long request, rc=%d", rc); + os_mbuf_free_chain(om); +rsp: + tester_rsp(BTP_SERVICE_ID_GATTC, GATTC_WRITE_LONG, CONTROLLER_INDEX, + status); +} + +static int reliable_write_cb(uint16_t conn_handle, + const struct ble_gatt_error *error, + struct ble_gatt_attr *attrs, + uint8_t num_attrs, + void *arg) +{ + struct gattc_write_rp *rp; + uint8_t err = (uint8_t) error->status; + struct os_mbuf *buf = os_msys_get(0, 0); + const ble_addr_t *addr; + struct ble_gap_conn_desc conn; + int rc = 0; + + SYS_LOG_DBG(""); + + if (ble_gap_conn_find(conn_handle, &conn)) { + rc = BLE_HS_EINVAL; + goto free; + } + + net_buf_simple_init(buf, 0); + rp = net_buf_simple_add(buf, sizeof(*rp)); + + addr = &conn.peer_ota_addr; + + rp->address_type = addr->type; + memcpy(rp->address, addr->val, sizeof(rp->address)); + + rp->status = err; + tester_send_buf(BTP_SERVICE_ID_GATTC, GATTC_RELIABLE_WRITE_RP, + CONTROLLER_INDEX, buf); +free: + os_mbuf_free_chain(buf); + return rc; +} + +static void reliable_write(uint8_t *data, uint16_t len) +{ + const struct gattc_reliable_write_cmd *cmd = (void *) data; + struct ble_gap_conn_desc conn; + struct ble_gatt_attr attr; + struct os_mbuf *om = NULL; + uint8_t status = BTP_STATUS_SUCCESS; + int rc; + + SYS_LOG_DBG(""); + + rc = ble_gap_conn_find_by_addr((ble_addr_t *) data, &conn); + if (rc) { + status = BTP_STATUS_FAILED; + goto fail; + } + + om = ble_hs_mbuf_from_flat(cmd->data, sys_le16_to_cpu(cmd->data_length)); + /* This is required, because Nimble checks if + * the data is longer than offset + */ + if (os_mbuf_extend(om, sys_le16_to_cpu(cmd->offset) + 1) == NULL) { + status = BTP_STATUS_FAILED; + goto fail; + } + + attr.handle = sys_le16_to_cpu(cmd->handle); + attr.offset = sys_le16_to_cpu(cmd->offset); + attr.om = om; + + if (ble_gattc_write_reliable(conn.conn_handle, &attr, 1, + reliable_write_cb, NULL)) { + status = BTP_STATUS_FAILED; + goto fail; + } else { + goto rsp; + } + +fail: + os_mbuf_free_chain(om); +rsp: + tester_rsp(BTP_SERVICE_ID_GATTC, GATTC_WRITE_LONG, CONTROLLER_INDEX, + status); +} + +static int subscribe_cb(uint16_t conn_handle, + const struct ble_gatt_error *error, + struct ble_gatt_attr *attrs, + void *arg) +{ + struct subscribe_rp *rp; + uint8_t err = (uint8_t) error->status; + uint8_t opcode = (uint8_t) (int) arg; + struct os_mbuf *buf = os_msys_get(0, 0); + const ble_addr_t *addr; + struct ble_gap_conn_desc conn; + int rc = 0; + + SYS_LOG_DBG(""); + + if (ble_gap_conn_find(conn_handle, &conn)) { + rc = BLE_HS_EINVAL; + goto free; + } + + net_buf_simple_init(buf, 0); + rp = net_buf_simple_add(buf, sizeof(*rp)); + + addr = &conn.peer_ota_addr; + + rp->address_type = addr->type; + memcpy(rp->address, addr->val, sizeof(rp->address)); + + rp->status = err; + tester_send_buf(BTP_SERVICE_ID_GATTC, opcode, + CONTROLLER_INDEX, buf); +free: + os_mbuf_free_chain(buf); + return rc; +} + +static int enable_subscription(uint16_t conn_handle, uint16_t ccc_handle, + uint16_t value) +{ + uint32_t opcode; + + SYS_LOG_DBG(""); + + opcode = (uint32_t) (value == 0x0001 ? GATTC_CFG_NOTIFY_RP : GATTC_CFG_INDICATE_RP); + + if (ble_gattc_write_flat(conn_handle, ccc_handle, + &value, sizeof(value), subscribe_cb, (void *) opcode)) { + return -EINVAL; + } + + subscribe_params.ccc_handle = value; + + return 0; +} + +static int disable_subscription(uint16_t conn_handle, uint16_t ccc_handle) +{ + uint16_t value = 0x00; + uint32_t opcode; + + SYS_LOG_DBG(""); + + opcode = (uint32_t) (value == 0x0001 ? GATTC_CFG_NOTIFY_RP : GATTC_CFG_INDICATE_RP); + + /* Fail if CCC handle doesn't match */ + if (ccc_handle != subscribe_params.ccc_handle) { + SYS_LOG_ERR("CCC handle doesn't match"); + return -EINVAL; + } + + if (ble_gattc_write_flat(conn_handle, ccc_handle, + &value, sizeof(value), subscribe_cb, (void *) opcode)) { + return -EINVAL; + } + + subscribe_params.ccc_handle = 0; + return 0; +} + +static void config_subscription(uint8_t *data, uint16_t len, uint8_t op) +{ + const struct gattc_cfg_notify_cmd *cmd = (void *) data; + struct ble_gap_conn_desc conn; + uint16_t ccc_handle = sys_le16_to_cpu(cmd->ccc_handle); + uint8_t status = BTP_STATUS_SUCCESS; + int rc; + + SYS_LOG_DBG(""); + + rc = ble_gap_conn_find_by_addr((ble_addr_t *) data, &conn); + if (rc) { + status = BTP_STATUS_FAILED; + goto rsp; + } + + if (cmd->enable) { + uint16_t value; + + if (op == GATTC_CFG_NOTIFY) { + value = 0x0001; + } else { + value = 0x0002; + } + + if (enable_subscription(conn.conn_handle, + ccc_handle, value) != 0) { + status = BTP_STATUS_FAILED; + goto rsp; + } + } else { + if (disable_subscription(conn.conn_handle, ccc_handle) < 0) { + status = BTP_STATUS_FAILED; + } else { + status = BTP_STATUS_SUCCESS; + } + } + +rsp: + SYS_LOG_DBG("Config subscription (op %u) status %u", op, status); + + tester_rsp(BTP_SERVICE_ID_GATTC, op, CONTROLLER_INDEX, status); +} + +int tester_gattc_notify_rx_ev(uint16_t conn_handle, uint16_t attr_handle, + uint8_t indication, struct os_mbuf *om) +{ + struct gattc_notification_ev *ev; + struct ble_gap_conn_desc conn; + struct os_mbuf *buf = os_msys_get(0, 0); + const ble_addr_t *addr; + + SYS_LOG_DBG(""); + + if (!subscribe_params.ccc_handle) { + goto fail; + } + + if (ble_gap_conn_find(conn_handle, &conn)) { + goto fail; + } + + net_buf_simple_init(buf, 0); + ev = net_buf_simple_add(buf, sizeof(*ev)); + + addr = &conn.peer_ota_addr; + + ev->address_type = addr->type; + memcpy(ev->address, addr->val, sizeof(ev->address)); + ev->type = (uint8_t) (indication ? 0x02 : 0x01); + ev->handle = sys_cpu_to_le16(attr_handle); + ev->data_length = sys_cpu_to_le16(os_mbuf_len(om)); + os_mbuf_appendfrom(buf, om, 0, os_mbuf_len(om)); + + tester_send_buf(BTP_SERVICE_ID_GATTC, GATTC_EV_NOTIFICATION_RXED, + CONTROLLER_INDEX, buf); + +fail: + os_mbuf_free_chain(buf); + return 0; +} + +static void supported_commands(uint8_t *data, uint16_t len) +{ + uint8_t cmds[3]; + struct gatt_read_supported_commands_rp *rp = (void *) cmds; + + SYS_LOG_DBG(""); + + memset(cmds, 0, sizeof(cmds)); + + tester_set_bit(cmds, GATTC_READ_SUPPORTED_COMMANDS); + tester_set_bit(cmds, GATTC_EXCHANGE_MTU); + tester_set_bit(cmds, GATTC_DISC_ALL_PRIM_SVCS); + tester_set_bit(cmds, GATTC_DISC_PRIM_UUID); + tester_set_bit(cmds, GATTC_FIND_INCLUDED); + tester_set_bit(cmds, GATTC_DISC_ALL_CHRC); + tester_set_bit(cmds, GATTC_DISC_CHRC_UUID); + tester_set_bit(cmds, GATTC_DISC_ALL_DESC); + tester_set_bit(cmds, GATTC_READ); + tester_set_bit(cmds, GATTC_READ_UUID); + tester_set_bit(cmds, GATTC_READ_LONG); + tester_set_bit(cmds, GATTC_READ_MULTIPLE); + tester_set_bit(cmds, GATTC_WRITE_WITHOUT_RSP); +#if 0 + tester_set_bit(cmds, GATTC_SIGNED_WRITE_WITHOUT_RSP); +#endif + tester_set_bit(cmds, GATTC_WRITE); + tester_set_bit(cmds, GATTC_WRITE_LONG); + tester_set_bit(cmds, GATTC_RELIABLE_WRITE); + tester_set_bit(cmds, GATTC_CFG_NOTIFY); + tester_set_bit(cmds, GATTC_CFG_INDICATE); + + tester_send(BTP_SERVICE_ID_GATTC, GATTC_READ_SUPPORTED_COMMANDS, + CONTROLLER_INDEX, (uint8_t *) rp, sizeof(cmds)); +} + +void tester_handle_gattc(uint8_t opcode, uint8_t index, uint8_t *data, + uint16_t len) +{ + switch (opcode) { + case GATTC_READ_SUPPORTED_COMMANDS: + supported_commands(data, len); + return; + case GATTC_EXCHANGE_MTU: + exchange_mtu(data, len); + return; + case GATTC_DISC_ALL_PRIM_SVCS: + disc_all_prim_svcs(data, len); + return; + case GATTC_DISC_PRIM_UUID: + disc_prim_uuid(data, len); + return; + case GATTC_FIND_INCLUDED: + find_included(data, len); + return; + case GATTC_DISC_ALL_CHRC: + disc_all_chrc(data, len); + return; + case GATTC_DISC_CHRC_UUID: + disc_chrc_uuid(data, len); + return; + case GATTC_DISC_ALL_DESC: + disc_all_desc(data, len); + return; + case GATTC_READ: + read(data, len); + return; + case GATTC_READ_UUID: + read_uuid(data, len); + return; + case GATTC_READ_LONG: + read_long(data, len); + return; + case GATTC_READ_MULTIPLE: + read_multiple(data, len); + return; + case GATTC_WRITE_WITHOUT_RSP: + write_without_rsp(data, len, opcode, false); + return; +#if 0 + case GATTC_SIGNED_WRITE_WITHOUT_RSP: + write_without_rsp(data, len, opcode, true); + return; +#endif + case GATTC_WRITE: + write(data, len); + return; + case GATTC_WRITE_LONG: + write_long(data, len); + return; + case GATTC_RELIABLE_WRITE: + reliable_write(data, len); + return; + case GATTC_CFG_NOTIFY: + case GATTC_CFG_INDICATE: + config_subscription(data, len, opcode); + return; + default: + tester_rsp(BTP_SERVICE_ID_GATTC, opcode, index, + BTP_STATUS_UNKNOWN_CMD); + return; + } +} \ No newline at end of file diff --git a/lib/bt/host/nimble/nimble/apps/bttester/src/glue.c b/lib/bt/host/nimble/nimble/apps/bttester/src/glue.c new file mode 100644 index 00000000..3e606062 --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/bttester/src/glue.c @@ -0,0 +1,129 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "syscfg/syscfg.h" + +#if !MYNEWT_VAL(BLE_MESH) +#include +#include +#include "os/os.h" +#include "os/os_mbuf.h" +#include "glue.h" + + +#define ASSERT_NOT_CHAIN(om) assert(SLIST_NEXT(om, om_next) == NULL) + +const char *bt_hex(const void *buf, size_t len) +{ + static const char hex[] = "0123456789abcdef"; + static char hexbufs[4][137]; + static uint8_t curbuf; + const uint8_t *b = buf; + char *str; + int i; + + str = hexbufs[curbuf++]; + curbuf %= ARRAY_SIZE(hexbufs); + + len = min(len, (sizeof(hexbufs[0]) - 1) / 2); + + for (i = 0; i < len; i++) { + str[i * 2] = hex[b[i] >> 4]; + str[i * 2 + 1] = hex[b[i] & 0xf]; + } + + str[i * 2] = '\0'; + + return str; +} + +struct os_mbuf * NET_BUF_SIMPLE(uint16_t size) +{ + struct os_mbuf *buf; + + buf = os_msys_get(size, 0); + assert(buf); + + return buf; +} + +/* This is by purpose */ +void net_buf_simple_init(struct os_mbuf *buf, + size_t reserve_head) +{ + /* This is called in Zephyr after init. + * Note in Mynewt case we don't care abour reserved head*/ + buf->om_data = &buf->om_databuf[buf->om_pkthdr_len] + reserve_head; + buf->om_len = 0; +} + +void +net_buf_simple_add_le16(struct os_mbuf *om, uint16_t val) +{ + val = htole16(val); + os_mbuf_append(om, &val, sizeof(val)); + ASSERT_NOT_CHAIN(om); +} + +void +net_buf_simple_add_be16(struct os_mbuf *om, uint16_t val) +{ + val = htobe16(val); + os_mbuf_append(om, &val, sizeof(val)); + ASSERT_NOT_CHAIN(om); +} + +void +net_buf_simple_add_be32(struct os_mbuf *om, uint32_t val) +{ + val = htobe32(val); + os_mbuf_append(om, &val, sizeof(val)); + ASSERT_NOT_CHAIN(om); +} + +void +net_buf_simple_add_u8(struct os_mbuf *om, uint8_t val) +{ + os_mbuf_append(om, &val, 1); + ASSERT_NOT_CHAIN(om); +} + +void* +net_buf_simple_add(struct os_mbuf *om, uint8_t len) +{ + void * tmp; + + tmp = os_mbuf_extend(om, len); + ASSERT_NOT_CHAIN(om); + + return tmp; +} + +uint8_t * +net_buf_simple_push(struct os_mbuf *om, uint8_t len) +{ + uint8_t headroom = om->om_data - &om->om_databuf[om->om_pkthdr_len]; + + assert(headroom >= len); + om->om_data -= len; + om->om_len += len; + + return om->om_data; +} +#endif diff --git a/lib/bt/host/nimble/nimble/apps/bttester/src/glue.h b/lib/bt/host/nimble/nimble/apps/bttester/src/glue.h new file mode 100644 index 00000000..65085601 --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/bttester/src/glue.h @@ -0,0 +1,63 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef __GLUE_H__ +#define __GLUE_H__ + +#include "os/endian.h" + +#define uint8_t uint8_t +#define int8_t int8_t +#define uint16_t uint16_t +#define uint32_t uint32_t +#define int32_t int32_t + +#ifndef BIT +#define BIT(n) (1UL << (n)) +#endif + +#define __packed __attribute__((__packed__)) + +#define sys_le16_to_cpu le16toh + +struct bt_data { + uint8_t type; + uint8_t data_len; + const uint8_t *data; +}; + +#define BT_DATA(_type, _data, _data_len) \ + { \ + .type = (_type), \ + .data_len = (_data_len), \ + .data = (const uint8_t *)(_data), \ + } + +struct os_mbuf * NET_BUF_SIMPLE(uint16_t size); +void net_buf_simple_init(struct os_mbuf *buf, size_t reserve_head); +void net_buf_simple_add_le16(struct os_mbuf *om, uint16_t val); +void net_buf_simple_add_u8(struct os_mbuf *om, uint8_t val); +void *net_buf_simple_add(struct os_mbuf *om, uint8_t len); +uint8_t *net_buf_simple_push(struct os_mbuf *om, uint8_t len); + +#define net_buf_simple_add_mem(a,b,c) os_mbuf_append(a,b,c) + +const char *bt_hex(const void *buf, size_t len); + +#endif /* __GLUE_H__ */ diff --git a/lib/bt/host/nimble/nimble/apps/bttester/src/l2cap.c b/lib/bt/host/nimble/nimble/apps/bttester/src/l2cap.c new file mode 100644 index 00000000..7890e7cc --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/bttester/src/l2cap.c @@ -0,0 +1,765 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* l2cap.c - Bluetooth L2CAP Tester */ + +/* + * Copyright (c) 2016 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "syscfg/syscfg.h" + +#if MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM) + +#include "console/console.h" +#include "host/ble_gap.h" +#include "host/ble_l2cap.h" + +#include "../../../nimble/host/src/ble_l2cap_priv.h" + +#include "bttester.h" + +#define CONTROLLER_INDEX 0 +#define CHANNELS MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM) +#define TESTER_COC_MTU MYNEWT_VAL(BTTESTER_L2CAP_COC_MTU) +#define TESTER_COC_BUF_COUNT (3 * MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM)) + +static os_membuf_t tester_sdu_coc_mem[ + OS_MEMPOOL_SIZE(TESTER_COC_BUF_COUNT, TESTER_COC_MTU) +]; + +struct os_mbuf_pool sdu_os_mbuf_pool; +static struct os_mempool sdu_coc_mbuf_mempool; +static bool hold_credit = false; + +static struct channel { + uint8_t chan_id; /* Internal number that identifies L2CAP channel. */ + uint8_t state; + struct ble_l2cap_chan *chan; +} channels[CHANNELS]; + +static uint8_t recv_cb_buf[TESTER_COC_MTU + sizeof(struct l2cap_data_received_ev)]; + +static struct channel *get_free_channel(void) +{ + uint8_t i; + struct channel *chan; + + for (i = 0; i < CHANNELS; i++) { + if (channels[i].state) { + continue; + } + + chan = &channels[i]; + chan->chan_id = i; + + return chan; + } + + return NULL; +} + +struct channel *find_channel(struct ble_l2cap_chan *chan) +{ + int i; + + for (i = 0; i < CHANNELS; ++i) { + if (channels[i].chan == chan) { + return &channels[i]; + } + } + + return NULL; +} + +struct channel *get_channel(uint8_t chan_id) +{ + if (chan_id >= CHANNELS) { + return NULL; + } + + return &channels[chan_id]; +} + +static void +tester_l2cap_coc_recv(struct ble_l2cap_chan *chan, struct os_mbuf *sdu) +{ + SYS_LOG_DBG("LE CoC SDU received, chan: 0x%08lx, data len %d", + (uint32_t) chan, OS_MBUF_PKTLEN(sdu)); + + os_mbuf_free_chain(sdu); + if (!hold_credit) { + sdu = os_mbuf_get_pkthdr(&sdu_os_mbuf_pool, 0); + assert(sdu != NULL); + + + ble_l2cap_recv_ready(chan, sdu); + } +} + +static void recv_cb(uint16_t conn_handle, struct ble_l2cap_chan *chan, + struct os_mbuf *buf, void *arg) +{ + struct l2cap_data_received_ev *ev = (void *) recv_cb_buf; + struct channel *channel = find_channel(chan); + assert(channel != NULL); + + ev->chan_id = channel->chan_id; + ev->data_length = OS_MBUF_PKTLEN(buf); + + if (ev->data_length > TESTER_COC_MTU) { + SYS_LOG_ERR("Too large sdu received, truncating data"); + ev->data_length = TESTER_COC_MTU; + } + os_mbuf_copydata(buf, 0, ev->data_length, ev->data); + + tester_send(BTP_SERVICE_ID_L2CAP, L2CAP_EV_DATA_RECEIVED, + CONTROLLER_INDEX, recv_cb_buf, sizeof(*ev) + ev->data_length); + + tester_l2cap_coc_recv(chan, buf); +} + +static void unstalled_cb(uint16_t conn_handle, struct ble_l2cap_chan *chan, + int status, void *arg) +{ + if (status) { + tester_rsp(BTP_SERVICE_ID_L2CAP, L2CAP_SEND_DATA, + CONTROLLER_INDEX, BTP_STATUS_FAILED); + } else { + tester_rsp(BTP_SERVICE_ID_L2CAP, L2CAP_SEND_DATA, + CONTROLLER_INDEX, BTP_STATUS_SUCCESS); + } +} + +static void reconfigured_ev(uint16_t conn_handle, struct ble_l2cap_chan *chan, + struct ble_l2cap_chan_info *chan_info, + int status) +{ + struct l2cap_reconfigured_ev ev; + struct channel *channel; + + if (status != 0) { + return; + } + + channel = find_channel(chan); + assert(channel != NULL); + + ev.chan_id = channel->chan_id; + ev.peer_mtu = chan_info->peer_coc_mtu; + ev.peer_mps = chan_info->peer_l2cap_mtu; + ev.our_mtu = chan_info->our_coc_mtu; + ev.our_mps = chan_info->our_l2cap_mtu; + + tester_send(BTP_SERVICE_ID_L2CAP, L2CAP_EV_RECONFIGURED, + CONTROLLER_INDEX, (uint8_t *) &ev, sizeof(ev)); +} + +static void connected_cb(uint16_t conn_handle, struct ble_l2cap_chan *chan, + struct ble_l2cap_chan_info *chan_info, void *arg) +{ + struct l2cap_connected_ev ev; + struct ble_gap_conn_desc desc; + struct channel *channel = find_channel(chan); + + if (channel == NULL) { + channel = get_free_channel(); + } + + ev.chan_id = channel->chan_id; + ev.psm = chan_info->psm; + ev.peer_mtu = chan_info->peer_coc_mtu; + ev.peer_mps = chan_info->peer_l2cap_mtu; + ev.our_mtu = chan_info->our_coc_mtu; + ev.our_mps = chan_info->our_l2cap_mtu; + channel->state = 1; + channel->chan = chan; + + if (!ble_gap_conn_find(conn_handle, &desc)) { + ev.address_type = desc.peer_ota_addr.type; + memcpy(ev.address, desc.peer_ota_addr.val, + sizeof(ev.address)); + } + + tester_send(BTP_SERVICE_ID_L2CAP, L2CAP_EV_CONNECTED, CONTROLLER_INDEX, + (uint8_t *) &ev, sizeof(ev)); +} + +static void disconnected_cb(uint16_t conn_handle, struct ble_l2cap_chan *chan, + struct ble_l2cap_chan_info *chan_info, void *arg) +{ + struct l2cap_disconnected_ev ev; + struct ble_gap_conn_desc desc; + struct channel *channel; + + memset(&ev, 0, sizeof(struct l2cap_disconnected_ev)); + + channel = find_channel(chan); + assert(channel != NULL); + + channel->state = 0; + channel->chan = chan; + ev.chan_id = channel->chan_id; + ev.psm = chan_info->psm; + + if (!ble_gap_conn_find(conn_handle, &desc)) { + ev.address_type = desc.peer_ota_addr.type; + memcpy(ev.address, desc.peer_ota_addr.val, + sizeof(ev.address)); + } + + tester_send(BTP_SERVICE_ID_L2CAP, L2CAP_EV_DISCONNECTED, + CONTROLLER_INDEX, (uint8_t *) &ev, sizeof(ev)); +} + +static int accept_cb(uint16_t conn_handle, uint16_t peer_mtu, + struct ble_l2cap_chan *chan) +{ + struct os_mbuf *sdu_rx; + + SYS_LOG_DBG("LE CoC accepting, chan: 0x%08lx, peer_mtu %d", + (uint32_t) chan, peer_mtu); + + sdu_rx = os_mbuf_get_pkthdr(&sdu_os_mbuf_pool, 0); + if (!sdu_rx) { + return BLE_HS_ENOMEM; + } + + ble_l2cap_recv_ready(chan, sdu_rx); + + return 0; +} + +static int +tester_l2cap_event(struct ble_l2cap_event *event, void *arg) +{ + struct ble_l2cap_chan_info chan_info; + int accept_response; + struct ble_gap_conn_desc conn; + + switch (event->type) { + case BLE_L2CAP_EVENT_COC_CONNECTED: + if (ble_l2cap_get_chan_info(event->connect.chan, &chan_info)) { + assert(0); + } + + if (event->connect.status) { + console_printf("LE COC error: %d\n", event->connect.status); + return 0; + } + + console_printf("LE COC connected, conn: %d, chan: 0x%08lx, " + "psm: 0x%02x, scid: 0x%04x, dcid: 0x%04x, " + "our_mps: %d, our_mtu: %d, peer_mps: %d, " + "peer_mtu: %d\n", event->connect.conn_handle, + (uint32_t) event->connect.chan, chan_info.psm, + chan_info.scid, chan_info.dcid, + chan_info.our_l2cap_mtu, chan_info.our_coc_mtu, + chan_info.peer_l2cap_mtu, chan_info.peer_coc_mtu); + + connected_cb(event->connect.conn_handle, + event->connect.chan, &chan_info, arg); + + return 0; + case BLE_L2CAP_EVENT_COC_DISCONNECTED: + if (ble_l2cap_get_chan_info(event->disconnect.chan, + &chan_info)) { + assert(0); + } + console_printf("LE CoC disconnected, chan: 0x%08lx\n", + (uint32_t) event->disconnect.chan); + + disconnected_cb(event->disconnect.conn_handle, + event->disconnect.chan, &chan_info, arg); + return 0; + case BLE_L2CAP_EVENT_COC_ACCEPT: + ble_l2cap_get_chan_info(event->accept.chan, &chan_info); + if (chan_info.psm == 0x00F2) { + /* TSPX_psm_authentication_required */ + ble_gap_conn_find(event->accept.conn_handle, &conn); + if (!conn.sec_state.authenticated) { + return BLE_HS_EAUTHEN; + } + } else if (chan_info.psm == 0x00F3) { + /* TSPX_psm_authorization_required */ + ble_gap_conn_find(event->accept.conn_handle, &conn); + if (!conn.sec_state.encrypted) { + return BLE_HS_EAUTHOR; + } + return BLE_HS_EAUTHOR; + } else if (chan_info.psm == 0x00F4) { + /* TSPX_psm_encryption_key_size_required */ + ble_gap_conn_find(event->accept.conn_handle, &conn); + if (conn.sec_state.key_size < 16) { + return BLE_HS_EENCRYPT_KEY_SZ; + } + } + + accept_response = POINTER_TO_INT(arg); + if (accept_response) { + return accept_response; + } + + console_printf("LE CoC accept, chan: 0x%08lx, handle: %u, sdu_size: %u\n", + (uint32_t) event->accept.chan, + event->accept.conn_handle, + event->accept.peer_sdu_size); + + return accept_cb(event->accept.conn_handle, + event->accept.peer_sdu_size, + event->accept.chan); + + case BLE_L2CAP_EVENT_COC_DATA_RECEIVED: + console_printf("LE CoC data received, chan: 0x%08lx, handle: %u, sdu_len: %u\n", + (uint32_t) event->receive.chan, + event->receive.conn_handle, + OS_MBUF_PKTLEN(event->receive.sdu_rx)); + + recv_cb(event->receive.conn_handle, event->receive.chan, + event->receive.sdu_rx, arg); + return 0; + case BLE_L2CAP_EVENT_COC_TX_UNSTALLED: + console_printf("LE CoC tx unstalled, chan: 0x%08lx, handle: %u, status: %d\n", + (uint32_t) event->tx_unstalled.chan, + event->tx_unstalled.conn_handle, + event->tx_unstalled.status); + + unstalled_cb(event->tx_unstalled.conn_handle, + event->tx_unstalled.chan, + event->tx_unstalled.status, arg); + return 0; + case BLE_L2CAP_EVENT_COC_RECONFIG_COMPLETED: + if (ble_l2cap_get_chan_info(event->reconfigured.chan, + &chan_info)) { + assert(0); + } + console_printf("LE CoC reconfigure completed status 0x%02x, " + "chan: 0x%08lx\n", event->reconfigured.status, + (uint32_t) event->reconfigured.chan); + + if (event->reconfigured.status == 0) { + console_printf("\t our_mps: %d our_mtu %d\n", + chan_info.our_l2cap_mtu, chan_info.our_coc_mtu); + } + + reconfigured_ev(event->reconfigured.conn_handle, + event->reconfigured.chan, + &chan_info, + event->reconfigured.status); + return 0; + case BLE_L2CAP_EVENT_COC_PEER_RECONFIGURED: + if (ble_l2cap_get_chan_info(event->reconfigured.chan, + &chan_info)) { + assert(0); + } + console_printf("LE CoC peer reconfigured status 0x%02x, " + "chan: 0x%08lx\n", event->reconfigured.status, + (uint32_t) event->reconfigured.chan); + + if (event->reconfigured.status == 0) { + console_printf("\t peer_mps: %d peer_mtu %d\n", + chan_info.peer_l2cap_mtu, chan_info.peer_coc_mtu); + } + + reconfigured_ev(event->reconfigured.conn_handle, + event->reconfigured.chan, + &chan_info, + event->reconfigured.status); + return 0; + default: + return 0; + } +} + +static void connect(uint8_t *data, uint16_t len) +{ + const struct l2cap_connect_cmd *cmd = (void *) data; + uint8_t rp_buf[sizeof(struct l2cap_connect_rp) + cmd->num]; + struct l2cap_connect_rp *rp = (void *) rp_buf; + struct ble_gap_conn_desc desc; + struct channel *chan; + struct os_mbuf *sdu_rx[cmd->num]; + ble_addr_t *addr = (void *) data; + uint16_t mtu = htole16(cmd->mtu); + int rc; + int i, j; + bool ecfc = cmd->options & L2CAP_CONNECT_OPT_ECFC; + hold_credit = cmd->options & L2CAP_CONNECT_OPT_HOLD_CREDIT; + + SYS_LOG_DBG("connect: type: %d addr: %s", addr->type, bt_hex(addr->val, 6)); + + if (mtu == 0 || mtu > TESTER_COC_MTU) { + mtu = TESTER_COC_MTU; + } + + rc = ble_gap_conn_find_by_addr(addr, &desc); + if (rc) { + SYS_LOG_ERR("GAP conn find failed"); + goto fail; + } + + rp->num = cmd->num; + + for (i = 0; i < cmd->num; i++) { + chan = get_free_channel(); + if (!chan) { + SYS_LOG_ERR("No free channels"); + goto fail; + } + /* temporarily mark channel as used to select next one */ + chan->state = 1; + + rp->chan_ids[i] = chan->chan_id; + + sdu_rx[i] = os_mbuf_get_pkthdr(&sdu_os_mbuf_pool, 0); + if (sdu_rx[i] == NULL) { + SYS_LOG_ERR("Failed to alloc buf"); + goto fail; + } + } + + /* mark selected channels as unused again */ + for (i = 0; i < cmd->num; i++) { + for (j = 0; j < CHANNELS; j++) { + if (rp->chan_ids[i] == channels[j].chan_id) { + channels[j].state = 0; + } + } + } + + if (cmd->num == 1 && !ecfc) { + rc = ble_l2cap_connect(desc.conn_handle, htole16(cmd->psm), + mtu, sdu_rx[0], + tester_l2cap_event, NULL); + } else if (ecfc) { + rc = ble_l2cap_enhanced_connect(desc.conn_handle, + htole16(cmd->psm), mtu, + cmd->num, sdu_rx, + tester_l2cap_event, NULL); + } else { + SYS_LOG_ERR("Invalid 'num' parameter value"); + goto fail; + } + + if (rc) { + SYS_LOG_ERR("L2CAP connect failed\n"); + goto fail; + } + + tester_send(BTP_SERVICE_ID_L2CAP, L2CAP_CONNECT, CONTROLLER_INDEX, + (uint8_t *) rp, sizeof(rp_buf)); + + return; + +fail: + tester_rsp(BTP_SERVICE_ID_L2CAP, L2CAP_CONNECT, CONTROLLER_INDEX, + BTP_STATUS_FAILED); +} + +static void disconnect(const uint8_t *data, uint16_t len) +{ + const struct l2cap_disconnect_cmd *cmd = (void *) data; + struct channel *chan; + uint8_t status; + int err; + + SYS_LOG_DBG(""); + + chan = get_channel(cmd->chan_id); + assert(chan != NULL); + + err = ble_l2cap_disconnect(chan->chan); + if (err) { + status = BTP_STATUS_FAILED; + goto rsp; + } + + status = BTP_STATUS_SUCCESS; + +rsp: + tester_rsp(BTP_SERVICE_ID_L2CAP, L2CAP_DISCONNECT, CONTROLLER_INDEX, + status); +} + +static void send_data(const uint8_t *data, uint16_t len) +{ + const struct l2cap_send_data_cmd *cmd = (void *) data; + struct os_mbuf *sdu_tx = NULL; + int rc; + uint16_t data_len = sys_le16_to_cpu(cmd->data_len); + struct channel *chan = get_channel(cmd->chan_id); + + SYS_LOG_DBG("cmd->chan_id=%d", cmd->chan_id); + + if (!chan) { + SYS_LOG_ERR("Invalid channel\n"); + goto fail; + } + + /* FIXME: For now, fail if data length exceeds buffer length */ + if (data_len > TESTER_COC_MTU) { + SYS_LOG_ERR("Data length exceeds buffer length"); + goto fail; + } + + sdu_tx = os_mbuf_get_pkthdr(&sdu_os_mbuf_pool, 0); + if (sdu_tx == NULL) { + SYS_LOG_ERR("No memory in the test sdu pool\n"); + goto fail; + } + + os_mbuf_append(sdu_tx, cmd->data, data_len); + + /* ble_l2cap_send takes ownership of the sdu */ + rc = ble_l2cap_send(chan->chan, sdu_tx); + if (rc == 0 || rc == BLE_HS_ESTALLED) { + tester_rsp(BTP_SERVICE_ID_L2CAP, L2CAP_SEND_DATA, CONTROLLER_INDEX, + BTP_STATUS_SUCCESS); + return; + } + + SYS_LOG_ERR("Unable to send data: %d", rc); + os_mbuf_free_chain(sdu_tx); + +fail: + tester_rsp(BTP_SERVICE_ID_L2CAP, L2CAP_SEND_DATA, CONTROLLER_INDEX, + BTP_STATUS_FAILED); +} + +static int +l2cap_coc_err2hs_err(uint16_t coc_err) +{ + switch (coc_err) { + case BLE_L2CAP_COC_ERR_UNKNOWN_LE_PSM: + return BLE_HS_ENOTSUP; + case BLE_L2CAP_COC_ERR_NO_RESOURCES: + return BLE_HS_ENOMEM; + case BLE_L2CAP_COC_ERR_INSUFFICIENT_AUTHEN: + return BLE_HS_EAUTHEN; + case BLE_L2CAP_COC_ERR_INSUFFICIENT_AUTHOR: + return BLE_HS_EAUTHOR; + case BLE_L2CAP_COC_ERR_INSUFFICIENT_ENC: + return BLE_HS_EENCRYPT; + case BLE_L2CAP_COC_ERR_INSUFFICIENT_KEY_SZ: + return BLE_HS_EENCRYPT_KEY_SZ; + case BLE_L2CAP_COC_ERR_UNACCEPTABLE_PARAMETERS: + return BLE_HS_EINVAL; + default: + return 0; + } +} + +static int +l2cap_btp_listen_err2coc_err(uint16_t coc_err) +{ + switch (coc_err) { + case 0x01: + return BLE_L2CAP_COC_ERR_INSUFFICIENT_AUTHEN; + case 0x02: + return BLE_L2CAP_COC_ERR_INSUFFICIENT_AUTHOR; + case 0x03: + return BLE_L2CAP_COC_ERR_INSUFFICIENT_KEY_SZ; + case 0x04: + return BLE_L2CAP_COC_ERR_INSUFFICIENT_ENC; + default: + return 0; + } +} + +static void listen(const uint8_t *data, uint16_t len) +{ + const struct l2cap_listen_cmd *cmd = (void *) data; + uint16_t mtu = htole16(cmd->mtu); + uint16_t rsp = htole16(cmd->response); + int rc; + + SYS_LOG_DBG(""); + + if (mtu == 0 || mtu > TESTER_COC_MTU) { + mtu = TESTER_COC_MTU; + } + + rsp = l2cap_btp_listen_err2coc_err(rsp); + rsp = l2cap_coc_err2hs_err(rsp); + + /* TODO: Handle cmd->transport flag */ + rc = ble_l2cap_create_server(cmd->psm, mtu, tester_l2cap_event, + INT_TO_POINTER(rsp)); + if (rc) { + goto fail; + } + + tester_rsp(BTP_SERVICE_ID_L2CAP, L2CAP_LISTEN, CONTROLLER_INDEX, + BTP_STATUS_SUCCESS); + return; + +fail: + tester_rsp(BTP_SERVICE_ID_L2CAP, L2CAP_LISTEN, CONTROLLER_INDEX, + BTP_STATUS_FAILED); +} + +static void credits(uint8_t *data, uint16_t len) +{ + const struct l2cap_credits_cmd *cmd = (void *)data; + struct os_mbuf *sdu; + int rc; + + struct channel *channel = get_channel(cmd->chan_id); + if (channel == NULL) { + goto fail; + } + + sdu = os_mbuf_get_pkthdr(&sdu_os_mbuf_pool, 0); + if (sdu == NULL) { + os_mbuf_free_chain(sdu); + goto fail; + } + + rc = ble_l2cap_recv_ready(channel->chan, sdu); + if (rc != 0) { + goto fail; + } + tester_rsp(BTP_SERVICE_ID_L2CAP, L2CAP_CREDITS, CONTROLLER_INDEX, + BTP_STATUS_SUCCESS); + return; +fail: + tester_rsp(BTP_SERVICE_ID_L2CAP, L2CAP_CREDITS, CONTROLLER_INDEX, + BTP_STATUS_FAILED); +} + +static void reconfigure(const uint8_t *data, uint16_t len) +{ + const struct l2cap_reconfigure_cmd *cmd = (void *) data; + uint16_t mtu = htole16(cmd->mtu); + struct ble_gap_conn_desc desc; + ble_addr_t *addr = (void *) data; + struct ble_l2cap_chan *chans[cmd->num]; + struct channel *channel; + int rc; + int i; + + SYS_LOG_DBG(""); + + if (mtu == 0 || mtu > TESTER_COC_MTU) { + mtu = TESTER_COC_MTU; + } + + rc = ble_gap_conn_find_by_addr(addr, &desc); + if (rc) { + SYS_LOG_ERR("GAP conn find failed"); + goto fail; + } + + for (i = 0; i < cmd->num; ++i) { + channel = get_channel(cmd->idxs[i]); + if (channel == NULL) { + goto fail; + } + chans[i] = channel->chan; + } + + rc = ble_l2cap_reconfig(chans, cmd->num, mtu); + if (rc) { + goto fail; + } + + tester_rsp(BTP_SERVICE_ID_L2CAP, L2CAP_RECONFIGURE, CONTROLLER_INDEX, + BTP_STATUS_SUCCESS); + return; + +fail: + tester_rsp(BTP_SERVICE_ID_L2CAP, L2CAP_RECONFIGURE, CONTROLLER_INDEX, + BTP_STATUS_FAILED); +} + +static void supported_commands(uint8_t *data, uint16_t len) +{ + uint8_t cmds[1]; + struct l2cap_read_supported_commands_rp *rp = (void *) cmds; + + memset(cmds, 0, sizeof(cmds)); + + tester_set_bit(cmds, L2CAP_READ_SUPPORTED_COMMANDS); + tester_set_bit(cmds, L2CAP_CONNECT); + tester_set_bit(cmds, L2CAP_DISCONNECT); + tester_set_bit(cmds, L2CAP_LISTEN); + tester_set_bit(cmds, L2CAP_SEND_DATA); + tester_set_bit(cmds, L2CAP_RECONFIGURE); + + tester_send(BTP_SERVICE_ID_L2CAP, L2CAP_READ_SUPPORTED_COMMANDS, + CONTROLLER_INDEX, (uint8_t *) rp, sizeof(cmds)); +} + +void tester_handle_l2cap(uint8_t opcode, uint8_t index, uint8_t *data, + uint16_t len) +{ + switch (opcode) { + case L2CAP_READ_SUPPORTED_COMMANDS: + supported_commands(data, len); + return; + case L2CAP_CONNECT: + connect(data, len); + return; + case L2CAP_DISCONNECT: + disconnect(data, len); + return; + case L2CAP_SEND_DATA: + send_data(data, len); + return; + case L2CAP_LISTEN: + listen(data, len); + return; + case L2CAP_RECONFIGURE: + reconfigure(data, len); + return; + case L2CAP_CREDITS: + credits(data, len); + return; + default: + tester_rsp(BTP_SERVICE_ID_L2CAP, opcode, index, + BTP_STATUS_UNKNOWN_CMD); + return; + } +} + +uint8_t tester_init_l2cap(void) +{ + int rc; + + /* For testing we want to support all the available channels */ + rc = os_mempool_init(&sdu_coc_mbuf_mempool, TESTER_COC_BUF_COUNT, + TESTER_COC_MTU, tester_sdu_coc_mem, + "tester_coc_sdu_pool"); + assert(rc == 0); + + rc = os_mbuf_pool_init(&sdu_os_mbuf_pool, &sdu_coc_mbuf_mempool, + TESTER_COC_MTU, TESTER_COC_BUF_COUNT); + assert(rc == 0); + + return BTP_STATUS_SUCCESS; +} + +uint8_t tester_unregister_l2cap(void) +{ + return BTP_STATUS_SUCCESS; +} + +#endif diff --git a/lib/bt/host/nimble/nimble/apps/bttester/src/main.c b/lib/bt/host/nimble/nimble/apps/bttester/src/main.c new file mode 100644 index 00000000..ea130805 --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/bttester/src/main.c @@ -0,0 +1,72 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* main.c - Application main entry point */ + +/* + * Copyright (c) 2015-2016 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "sysinit/sysinit.h" + +#include "modlog/modlog.h" +#include "host/ble_uuid.h" +#include "host/ble_hs.h" + +#include "bttester.h" + +static void on_reset(int reason) +{ + MODLOG_DFLT(ERROR, "Resetting state; reason=%d\n", reason); +} + +static void on_sync(void) +{ + MODLOG_DFLT(INFO, "Bluetooth initialized\n"); + + tester_init(); +} + +int main(int argc, char **argv) +{ + int rc; + +#ifdef ARCH_sim + mcu_sim_parse_args(argc, argv); +#endif + + /* Initialize OS */ + sysinit(); + + /* Initialize the NimBLE host configuration. */ + ble_hs_cfg.reset_cb = on_reset; + ble_hs_cfg.sync_cb = on_sync; + ble_hs_cfg.gatts_register_cb = gatt_svr_register_cb, + ble_hs_cfg.store_status_cb = ble_store_util_status_rr; + + rc = gatt_svr_init(); + assert(rc == 0); + + while (1) { + os_eventq_run(os_eventq_dflt_get()); + } + return 0; +} diff --git a/lib/bt/host/nimble/nimble/apps/bttester/src/mesh.c b/lib/bt/host/nimble/nimble/apps/bttester/src/mesh.c new file mode 100644 index 00000000..46bf7699 --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/bttester/src/mesh.c @@ -0,0 +1,993 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* mesh.c - Bluetooth Mesh Tester */ + +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "syscfg/syscfg.h" + +#if MYNEWT_VAL(BLE_MESH) + +#include + +#include "mesh/mesh.h" +#include "mesh/glue.h" +#include "mesh/testing.h" +#include "console/console.h" + +#include "bttester.h" + +extern uint8_t own_addr_type; + +#define CONTROLLER_INDEX 0 +#define CID_LOCAL 0x0002 + +/* Health server data */ +#define CUR_FAULTS_MAX 4 +#define HEALTH_TEST_ID 0x00 + +static uint8_t cur_faults[CUR_FAULTS_MAX]; +static uint8_t reg_faults[CUR_FAULTS_MAX * 2]; + +/* Provision node data */ +static uint8_t net_key[16]; +static uint16_t net_key_idx; +static uint8_t flags; +static uint32_t iv_index; +static uint16_t addr; +static uint8_t dev_key[16]; +static uint8_t input_size; + +/* Configured provisioning data */ +static uint8_t dev_uuid[16]; +static uint8_t static_auth[16]; + +/* Vendor Model data */ +#define VND_MODEL_ID_1 0x1234 + +/* Model send data */ +#define MODEL_BOUNDS_MAX 2 + +static struct model_data { + struct bt_mesh_model *model; + uint16_t addr; + uint16_t appkey_idx; +} model_bound[MODEL_BOUNDS_MAX]; + +static struct { + uint16_t local; + uint16_t dst; + uint16_t net_idx; +} net = { + .local = BT_MESH_ADDR_UNASSIGNED, + .dst = BT_MESH_ADDR_UNASSIGNED, +}; + +static void supported_commands(uint8_t *data, uint16_t len) +{ + struct os_mbuf *buf = NET_BUF_SIMPLE(BTP_DATA_MAX_SIZE); + + net_buf_simple_init(buf, 0); + + /* 1st octet */ + memset(net_buf_simple_add(buf, 1), 0, 1); + tester_set_bit(buf->om_data, MESH_READ_SUPPORTED_COMMANDS); + tester_set_bit(buf->om_data, MESH_CONFIG_PROVISIONING); + tester_set_bit(buf->om_data, MESH_PROVISION_NODE); + tester_set_bit(buf->om_data, MESH_INIT); + tester_set_bit(buf->om_data, MESH_RESET); + tester_set_bit(buf->om_data, MESH_INPUT_NUMBER); + tester_set_bit(buf->om_data, MESH_INPUT_STRING); + /* 2nd octet */ + tester_set_bit(buf->om_data, MESH_IVU_TEST_MODE); + tester_set_bit(buf->om_data, MESH_IVU_TOGGLE_STATE); + tester_set_bit(buf->om_data, MESH_NET_SEND); + tester_set_bit(buf->om_data, MESH_HEALTH_GENERATE_FAULTS); + tester_set_bit(buf->om_data, MESH_HEALTH_CLEAR_FAULTS); + tester_set_bit(buf->om_data, MESH_LPN); + tester_set_bit(buf->om_data, MESH_LPN_POLL); + tester_set_bit(buf->om_data, MESH_MODEL_SEND); + /* 3rd octet */ + memset(net_buf_simple_add(buf, 1), 0, 1); +#if MYNEWT_VAL(BLE_MESH_TESTING) + tester_set_bit(buf->om_data, MESH_LPN_SUBSCRIBE); + tester_set_bit(buf->om_data, MESH_LPN_UNSUBSCRIBE); + tester_set_bit(buf->om_data, MESH_RPL_CLEAR); +#endif /* CONFIG_BT_TESTING */ + tester_set_bit(buf->om_data, MESH_PROXY_IDENTITY); + + tester_send_buf(BTP_SERVICE_ID_MESH, MESH_READ_SUPPORTED_COMMANDS, + CONTROLLER_INDEX, buf); +} + +static void get_faults(uint8_t *faults, uint8_t faults_size, uint8_t *dst, uint8_t *count) +{ + uint8_t i, limit = *count; + + for (i = 0, *count = 0; i < faults_size && *count < limit; i++) { + if (faults[i]) { + *dst++ = faults[i]; + (*count)++; + } + } +} + +static int fault_get_cur(struct bt_mesh_model *model, uint8_t *test_id, + uint16_t *company_id, uint8_t *faults, uint8_t *fault_count) +{ + SYS_LOG_DBG(""); + + *test_id = HEALTH_TEST_ID; + *company_id = CID_LOCAL; + + get_faults(cur_faults, sizeof(cur_faults), faults, fault_count); + + return 0; +} + +static int fault_get_reg(struct bt_mesh_model *model, uint16_t company_id, + uint8_t *test_id, uint8_t *faults, uint8_t *fault_count) +{ + SYS_LOG_DBG("company_id 0x%04x", company_id); + + if (company_id != CID_LOCAL) { + return -EINVAL; + } + + *test_id = HEALTH_TEST_ID; + + get_faults(reg_faults, sizeof(reg_faults), faults, fault_count); + + return 0; +} + +static int fault_clear(struct bt_mesh_model *model, uint16_t company_id) +{ + SYS_LOG_DBG("company_id 0x%04x", company_id); + + if (company_id != CID_LOCAL) { + return -EINVAL; + } + + memset(reg_faults, 0, sizeof(reg_faults)); + + return 0; +} + +static int fault_test(struct bt_mesh_model *model, uint8_t test_id, + uint16_t company_id) +{ + SYS_LOG_DBG("test_id 0x%02x company_id 0x%04x", test_id, company_id); + + if (company_id != CID_LOCAL || test_id != HEALTH_TEST_ID) { + return -EINVAL; + } + + return 0; +} + +static const struct bt_mesh_health_srv_cb health_srv_cb = { + .fault_get_cur = fault_get_cur, + .fault_get_reg = fault_get_reg, + .fault_clear = fault_clear, + .fault_test = fault_test, +}; + +static struct bt_mesh_health_srv health_srv = { + .cb = &health_srv_cb, +}; + +static struct bt_mesh_model_pub health_pub; + +static void +health_pub_init(void) +{ + health_pub.msg = BT_MESH_HEALTH_FAULT_MSG(CUR_FAULTS_MAX); +} + +static struct bt_mesh_cfg_cli cfg_cli = { +}; + +void show_faults(uint8_t test_id, uint16_t cid, uint8_t *faults, size_t fault_count) +{ + size_t i; + + if (!fault_count) { + SYS_LOG_DBG("Health Test ID 0x%02x Company ID 0x%04x: " + "no faults", test_id, cid); + return; + } + + SYS_LOG_DBG("Health Test ID 0x%02x Company ID 0x%04x Fault Count %zu: ", + test_id, cid, fault_count); + + for (i = 0; i < fault_count; i++) { + SYS_LOG_DBG("0x%02x", faults[i]); + } +} + +static void health_current_status(struct bt_mesh_health_cli *cli, uint16_t addr, + uint8_t test_id, uint16_t cid, uint8_t *faults, + size_t fault_count) +{ + SYS_LOG_DBG("Health Current Status from 0x%04x", addr); + show_faults(test_id, cid, faults, fault_count); +} + +static struct bt_mesh_health_cli health_cli = { + .current_status = health_current_status, +}; + +static struct bt_mesh_model root_models[] = { + BT_MESH_MODEL_CFG_SRV, + BT_MESH_MODEL_CFG_CLI(&cfg_cli), + BT_MESH_MODEL_HEALTH_SRV(&health_srv, &health_pub), + BT_MESH_MODEL_HEALTH_CLI(&health_cli), +}; + +static struct bt_mesh_model vnd_models[] = { + BT_MESH_MODEL_VND(CID_LOCAL, VND_MODEL_ID_1, BT_MESH_MODEL_NO_OPS, NULL, + NULL), +}; + +static struct bt_mesh_elem elements[] = { + BT_MESH_ELEM(0, root_models, vnd_models), +}; + +static void link_open(bt_mesh_prov_bearer_t bearer) +{ + struct mesh_prov_link_open_ev ev; + + SYS_LOG_DBG("bearer 0x%02x", bearer); + + switch (bearer) { + case BT_MESH_PROV_ADV: + ev.bearer = MESH_PROV_BEARER_PB_ADV; + break; + case BT_MESH_PROV_GATT: + ev.bearer = MESH_PROV_BEARER_PB_GATT; + break; + default: + SYS_LOG_ERR("Invalid bearer"); + + return; + } + + tester_send(BTP_SERVICE_ID_MESH, MESH_EV_PROV_LINK_OPEN, + CONTROLLER_INDEX, (uint8_t *) &ev, sizeof(ev)); +} + +static void link_close(bt_mesh_prov_bearer_t bearer) +{ + struct mesh_prov_link_closed_ev ev; + + SYS_LOG_DBG("bearer 0x%02x", bearer); + + switch (bearer) { + case BT_MESH_PROV_ADV: + ev.bearer = MESH_PROV_BEARER_PB_ADV; + break; + case BT_MESH_PROV_GATT: + ev.bearer = MESH_PROV_BEARER_PB_GATT; + break; + default: + SYS_LOG_ERR("Invalid bearer"); + + return; + } + + tester_send(BTP_SERVICE_ID_MESH, MESH_EV_PROV_LINK_CLOSED, + CONTROLLER_INDEX, (uint8_t *) &ev, sizeof(ev)); +} + +static int output_number(bt_mesh_output_action_t action, uint32_t number) +{ + struct mesh_out_number_action_ev ev; + + SYS_LOG_DBG("action 0x%04x number 0x%08lx", action, number); + + ev.action = sys_cpu_to_le16(action); + ev.number = sys_cpu_to_le32(number); + + tester_send(BTP_SERVICE_ID_MESH, MESH_EV_OUT_NUMBER_ACTION, + CONTROLLER_INDEX, (uint8_t *) &ev, sizeof(ev)); + + return 0; +} + +static int output_string(const char *str) +{ + struct mesh_out_string_action_ev *ev; + struct os_mbuf *buf = NET_BUF_SIMPLE(BTP_DATA_MAX_SIZE); + + SYS_LOG_DBG("str %s", str); + + net_buf_simple_init(buf, 0); + + ev = net_buf_simple_add(buf, sizeof(*ev)); + ev->string_len = strlen(str); + + net_buf_simple_add_mem(buf, str, ev->string_len); + + tester_send_buf(BTP_SERVICE_ID_MESH, MESH_EV_OUT_STRING_ACTION, + CONTROLLER_INDEX, buf); + + os_mbuf_free_chain(buf); + return 0; +} + +static int input(bt_mesh_input_action_t action, uint8_t size) +{ + struct mesh_in_action_ev ev; + + SYS_LOG_DBG("action 0x%04x number 0x%02x", action, size); + + input_size = size; + + ev.action = sys_cpu_to_le16(action); + ev.size = size; + + tester_send(BTP_SERVICE_ID_MESH, MESH_EV_IN_ACTION, CONTROLLER_INDEX, + (uint8_t *) &ev, sizeof(ev)); + + return 0; +} + +static uint8_t vnd_app_key[16]; +static uint16_t vnd_app_key_idx = 0x000f; + +static void prov_complete(uint16_t net_idx, uint16_t addr) +{ + SYS_LOG_DBG("net_idx 0x%04x addr 0x%04x", net_idx, addr); + + net.net_idx = net_idx, + net.local = addr; + net.dst = addr; + + tester_send(BTP_SERVICE_ID_MESH, MESH_EV_PROVISIONED, CONTROLLER_INDEX, + NULL, 0); +} + +static void prov_reset(void) +{ + SYS_LOG_DBG(""); + + bt_mesh_prov_enable(BT_MESH_PROV_ADV | BT_MESH_PROV_GATT); +} + +static const struct bt_mesh_comp comp = { + .cid = CID_LOCAL, + .elem = elements, + .elem_count = ARRAY_SIZE(elements), +}; + +static struct bt_mesh_prov prov = { + .uuid = dev_uuid, + .static_val = static_auth, + .static_val_len = sizeof(static_auth), + .output_number = output_number, + .output_string = output_string, + .input = input, + .link_open = link_open, + .link_close = link_close, + .complete = prov_complete, + .reset = prov_reset, +}; + +static void config_prov(uint8_t *data, uint16_t len) +{ + const struct mesh_config_provisioning_cmd *cmd = (void *) data; + + SYS_LOG_DBG(""); + + memcpy(dev_uuid, cmd->uuid, sizeof(dev_uuid)); + memcpy(static_auth, cmd->static_auth, sizeof(static_auth)); + + prov.output_size = cmd->out_size; + prov.output_actions = sys_le16_to_cpu(cmd->out_actions); + prov.input_size = cmd->in_size; + prov.input_actions = sys_le16_to_cpu(cmd->in_actions); + + tester_rsp(BTP_SERVICE_ID_MESH, MESH_CONFIG_PROVISIONING, + CONTROLLER_INDEX, BTP_STATUS_SUCCESS); +} + +static void provision_node(uint8_t *data, uint16_t len) +{ + const struct mesh_provision_node_cmd *cmd = (void *) data; + + SYS_LOG_DBG(""); + + memcpy(dev_key, cmd->dev_key, sizeof(dev_key)); + memcpy(net_key, cmd->net_key, sizeof(net_key)); + + addr = sys_le16_to_cpu(cmd->addr); + flags = cmd->flags; + iv_index = sys_le32_to_cpu(cmd->iv_index); + net_key_idx = sys_le16_to_cpu(cmd->net_key_idx); + + tester_rsp(BTP_SERVICE_ID_MESH, MESH_PROVISION_NODE, + CONTROLLER_INDEX, BTP_STATUS_SUCCESS); +} + +static void init(uint8_t *data, uint16_t len) +{ + uint8_t status = BTP_STATUS_SUCCESS; + int err; + + SYS_LOG_DBG(""); + + err = bt_mesh_init(own_addr_type, &prov, &comp); + if (err) { + status = BTP_STATUS_FAILED; + + goto rsp; + } + + if (addr) { + err = bt_mesh_provision(net_key, net_key_idx, flags, iv_index, + addr, dev_key); + if (err) { + status = BTP_STATUS_FAILED; + } + } else { + err = bt_mesh_prov_enable(BT_MESH_PROV_ADV | BT_MESH_PROV_GATT); + if (err) { + status = BTP_STATUS_FAILED; + } + } + +rsp: + tester_rsp(BTP_SERVICE_ID_MESH, MESH_INIT, CONTROLLER_INDEX, + status); +} + +static void reset(uint8_t *data, uint16_t len) +{ + SYS_LOG_DBG(""); + + bt_mesh_reset(); + + tester_rsp(BTP_SERVICE_ID_MESH, MESH_RESET, CONTROLLER_INDEX, + BTP_STATUS_SUCCESS); +} + +static void input_number(uint8_t *data, uint16_t len) +{ + const struct mesh_input_number_cmd *cmd = (void *) data; + uint8_t status = BTP_STATUS_SUCCESS; + uint32_t number; + int err; + + number = sys_le32_to_cpu(cmd->number); + + SYS_LOG_DBG("number 0x%04lx", number); + + err = bt_mesh_input_number(number); + if (err) { + status = BTP_STATUS_FAILED; + } + + tester_rsp(BTP_SERVICE_ID_MESH, MESH_INPUT_NUMBER, CONTROLLER_INDEX, + status); +} + +static void input_string(uint8_t *data, uint16_t len) +{ + const struct mesh_input_string_cmd *cmd = (void *) data; + uint8_t status = BTP_STATUS_SUCCESS; + uint8_t str_auth[16]; + int err; + + SYS_LOG_DBG(""); + + if (cmd->string_len > sizeof(str_auth)) { + SYS_LOG_ERR("Too long input (%u chars required)", input_size); + status = BTP_STATUS_FAILED; + goto rsp; + } else if (cmd->string_len < input_size) { + SYS_LOG_ERR("Too short input (%u chars required)", input_size); + status = BTP_STATUS_FAILED; + goto rsp; + } + + strncpy((char *)str_auth, (char *)cmd->string, cmd->string_len); + + err = bt_mesh_input_string((char *)str_auth); + if (err) { + status = BTP_STATUS_FAILED; + } + +rsp: + tester_rsp(BTP_SERVICE_ID_MESH, MESH_INPUT_STRING, CONTROLLER_INDEX, + status); +} + +static void ivu_test_mode(uint8_t *data, uint16_t len) +{ + const struct mesh_ivu_test_mode_cmd *cmd = (void *) data; + + SYS_LOG_DBG("enable 0x%02x", cmd->enable); + + bt_mesh_iv_update_test(cmd->enable ? true : false); + + tester_rsp(BTP_SERVICE_ID_MESH, MESH_IVU_TEST_MODE, CONTROLLER_INDEX, + BTP_STATUS_SUCCESS); +} + +static void ivu_toggle_state(uint8_t *data, uint16_t len) +{ + bool result; + + SYS_LOG_DBG(""); + + result = bt_mesh_iv_update(); + if (!result) { + SYS_LOG_ERR("Failed to toggle the IV Update state"); + } + + tester_rsp(BTP_SERVICE_ID_MESH, MESH_IVU_TOGGLE_STATE, CONTROLLER_INDEX, + result ? BTP_STATUS_SUCCESS : BTP_STATUS_FAILED); +} + +static void lpn(uint8_t *data, uint16_t len) +{ + struct mesh_lpn_set_cmd *cmd = (void *) data; + bool enable; + int err; + + SYS_LOG_DBG("enable 0x%02x", cmd->enable); + + enable = cmd->enable ? true : false; + err = bt_mesh_lpn_set(enable); + if (err) { + SYS_LOG_ERR("Failed to toggle LPN (err %d)", err); + } + + tester_rsp(BTP_SERVICE_ID_MESH, MESH_LPN, CONTROLLER_INDEX, + err ? BTP_STATUS_FAILED : BTP_STATUS_SUCCESS); +} + +static void lpn_poll(uint8_t *data, uint16_t len) +{ + int err; + + SYS_LOG_DBG(""); + + err = bt_mesh_lpn_poll(); + if (err) { + SYS_LOG_ERR("Failed to send poll msg (err %d)", err); + } + + tester_rsp(BTP_SERVICE_ID_MESH, MESH_LPN_POLL, CONTROLLER_INDEX, + err ? BTP_STATUS_FAILED : BTP_STATUS_SUCCESS); +} + +static void net_send(uint8_t *data, uint16_t len) +{ + struct mesh_net_send_cmd *cmd = (void *) data; + struct os_mbuf *msg = NET_BUF_SIMPLE(UINT8_MAX); + struct bt_mesh_msg_ctx ctx = { + .net_idx = net.net_idx, + .app_idx = vnd_app_key_idx, + .addr = sys_le16_to_cpu(cmd->dst), + .send_ttl = cmd->ttl, + }; + int err; + + SYS_LOG_DBG("ttl 0x%02x dst 0x%04x payload_len %d", ctx.send_ttl, + ctx.addr, cmd->payload_len); + + if (!bt_mesh_app_key_exists(vnd_app_key_idx)) { + (void)bt_mesh_app_key_add(vnd_app_key_idx, net.net_idx, + vnd_app_key); + vnd_models[0].keys[0] = vnd_app_key_idx; + } + + net_buf_simple_add_mem(msg, cmd->payload, cmd->payload_len); + + err = bt_mesh_model_send(&vnd_models[0], &ctx, msg, NULL, NULL); + if (err) { + SYS_LOG_ERR("Failed to send (err %d)", err); + } + + tester_rsp(BTP_SERVICE_ID_MESH, MESH_NET_SEND, CONTROLLER_INDEX, + err ? BTP_STATUS_FAILED : BTP_STATUS_SUCCESS); + + os_mbuf_free_chain(msg); +} + +static void health_generate_faults(uint8_t *data, uint16_t len) +{ + struct mesh_health_generate_faults_rp *rp; + struct os_mbuf *buf = NET_BUF_SIMPLE(sizeof(*rp) + sizeof(cur_faults) + + sizeof(reg_faults)); + uint8_t some_faults[] = { 0x01, 0x02, 0x03, 0xff, 0x06 }; + uint8_t cur_faults_count, reg_faults_count; + + rp = net_buf_simple_add(buf, sizeof(*rp)); + + cur_faults_count = min(sizeof(cur_faults), sizeof(some_faults)); + memcpy(cur_faults, some_faults, cur_faults_count); + net_buf_simple_add_mem(buf, cur_faults, cur_faults_count); + rp->cur_faults_count = cur_faults_count; + + reg_faults_count = min(sizeof(reg_faults), sizeof(some_faults)); + memcpy(reg_faults, some_faults, reg_faults_count); + net_buf_simple_add_mem(buf, reg_faults, reg_faults_count); + rp->reg_faults_count = reg_faults_count; + + bt_mesh_fault_update(&elements[0]); + + tester_send_buf(BTP_SERVICE_ID_MESH, MESH_HEALTH_GENERATE_FAULTS, + CONTROLLER_INDEX, buf); +} + +static void health_clear_faults(uint8_t *data, uint16_t len) +{ + SYS_LOG_DBG(""); + + memset(cur_faults, 0, sizeof(cur_faults)); + memset(reg_faults, 0, sizeof(reg_faults)); + + bt_mesh_fault_update(&elements[0]); + + tester_rsp(BTP_SERVICE_ID_MESH, MESH_HEALTH_CLEAR_FAULTS, + CONTROLLER_INDEX, BTP_STATUS_SUCCESS); +} + +static void model_send(uint8_t *data, uint16_t len) +{ + struct mesh_model_send_cmd *cmd = (void *) data; + struct os_mbuf *msg = NET_BUF_SIMPLE(UINT8_MAX); + struct bt_mesh_msg_ctx ctx = { + .net_idx = net.net_idx, + .app_idx = BT_MESH_KEY_DEV, + .addr = sys_le16_to_cpu(cmd->dst), + .send_ttl = BT_MESH_TTL_DEFAULT, + }; + struct bt_mesh_model *model = NULL; + int err, i; + uint16_t src = sys_le16_to_cpu(cmd->src); + + /* Lookup source address */ + for (i = 0; i < ARRAY_SIZE(model_bound); i++) { + if (bt_mesh_model_elem(model_bound[i].model)->addr == src) { + model = model_bound[i].model; + ctx.app_idx = model_bound[i].appkey_idx; + + break; + } + } + + if (!model) { + SYS_LOG_ERR("Model not found"); + err = -EINVAL; + + goto fail; + } + + SYS_LOG_DBG("src 0x%04x dst 0x%04x model %p payload_len %d", src, + ctx.addr, model, cmd->payload_len); + + net_buf_simple_add_mem(msg, cmd->payload, cmd->payload_len); + + err = bt_mesh_model_send(model, &ctx, msg, NULL, NULL); + if (err) { + SYS_LOG_ERR("Failed to send (err %d)", err); + } + +fail: + tester_rsp(BTP_SERVICE_ID_MESH, MESH_MODEL_SEND, CONTROLLER_INDEX, + err ? BTP_STATUS_FAILED : BTP_STATUS_SUCCESS); + + os_mbuf_free_chain(msg); +} + +#if MYNEWT_VAL(BLE_MESH_TESTING) +static void lpn_subscribe(uint8_t *data, uint16_t len) +{ + struct mesh_lpn_subscribe_cmd *cmd = (void *) data; + uint16_t address = sys_le16_to_cpu(cmd->address); + int err; + + SYS_LOG_DBG("address 0x%04x", address); + + err = bt_test_mesh_lpn_group_add(address); + if (err) { + SYS_LOG_ERR("Failed to subscribe (err %d)", err); + } + + tester_rsp(BTP_SERVICE_ID_MESH, MESH_LPN_SUBSCRIBE, CONTROLLER_INDEX, + err ? BTP_STATUS_FAILED : BTP_STATUS_SUCCESS); +} + +static void lpn_unsubscribe(uint8_t *data, uint16_t len) +{ + struct mesh_lpn_unsubscribe_cmd *cmd = (void *) data; + uint16_t address = sys_le16_to_cpu(cmd->address); + int err; + + SYS_LOG_DBG("address 0x%04x", address); + + err = bt_test_mesh_lpn_group_remove(&address, 1); + if (err) { + SYS_LOG_ERR("Failed to unsubscribe (err %d)", err); + } + + tester_rsp(BTP_SERVICE_ID_MESH, MESH_LPN_UNSUBSCRIBE, CONTROLLER_INDEX, + err ? BTP_STATUS_FAILED : BTP_STATUS_SUCCESS); +} + +static void rpl_clear(uint8_t *data, uint16_t len) +{ + int err; + + SYS_LOG_DBG(""); + + err = bt_test_mesh_rpl_clear(); + if (err) { + SYS_LOG_ERR("Failed to clear RPL (err %d)", err); + } + + tester_rsp(BTP_SERVICE_ID_MESH, MESH_RPL_CLEAR, CONTROLLER_INDEX, + err ? BTP_STATUS_FAILED : BTP_STATUS_SUCCESS); +} +#endif /* MYNEWT_VAL(BLE_MESH_TESTING) */ + +static void proxy_identity_enable(uint8_t *data, uint16_t len) +{ + int err; + + SYS_LOG_DBG(""); + + err = bt_mesh_proxy_identity_enable(); + if (err) { + SYS_LOG_ERR("Failed to enable proxy identity (err %d)", err); + } + + tester_rsp(BTP_SERVICE_ID_MESH, MESH_PROXY_IDENTITY, CONTROLLER_INDEX, + err ? BTP_STATUS_FAILED : BTP_STATUS_SUCCESS); +} + +void tester_handle_mesh(uint8_t opcode, uint8_t index, uint8_t *data, uint16_t len) +{ + switch (opcode) { + case MESH_READ_SUPPORTED_COMMANDS: + supported_commands(data, len); + break; + case MESH_CONFIG_PROVISIONING: + config_prov(data, len); + break; + case MESH_PROVISION_NODE: + provision_node(data, len); + break; + case MESH_INIT: + init(data, len); + break; + case MESH_RESET: + reset(data, len); + break; + case MESH_INPUT_NUMBER: + input_number(data, len); + break; + case MESH_INPUT_STRING: + input_string(data, len); + break; + case MESH_IVU_TEST_MODE: + ivu_test_mode(data, len); + break; + case MESH_IVU_TOGGLE_STATE: + ivu_toggle_state(data, len); + break; + case MESH_LPN: + lpn(data, len); + break; + case MESH_LPN_POLL: + lpn_poll(data, len); + break; + case MESH_NET_SEND: + net_send(data, len); + break; + case MESH_HEALTH_GENERATE_FAULTS: + health_generate_faults(data, len); + break; + case MESH_HEALTH_CLEAR_FAULTS: + health_clear_faults(data, len); + break; + case MESH_MODEL_SEND: + model_send(data, len); + break; +#if MYNEWT_VAL(BLE_MESH_TESTING) + case MESH_LPN_SUBSCRIBE: + lpn_subscribe(data, len); + break; + case MESH_LPN_UNSUBSCRIBE: + lpn_unsubscribe(data, len); + break; + case MESH_RPL_CLEAR: + rpl_clear(data, len); + break; +#endif /* MYNEWT_VAL(BLE_MESH_TESTING) */ + case MESH_PROXY_IDENTITY: + proxy_identity_enable(data, len); + break; + default: + tester_rsp(BTP_SERVICE_ID_MESH, opcode, index, + BTP_STATUS_UNKNOWN_CMD); + break; + } +} + +void net_recv_ev(uint8_t ttl, uint8_t ctl, uint16_t src, uint16_t dst, const void *payload, + size_t payload_len) +{ + struct os_mbuf *buf = NET_BUF_SIMPLE(UINT8_MAX); + struct mesh_net_recv_ev *ev; + + SYS_LOG_DBG("ttl 0x%02x ctl 0x%02x src 0x%04x dst 0x%04x " + "payload_len %d", ttl, ctl, src, dst, payload_len); + + if (payload_len > net_buf_simple_tailroom(buf)) { + SYS_LOG_ERR("Payload size exceeds buffer size"); + + goto done; + } + + ev = net_buf_simple_add(buf, sizeof(*ev)); + ev->ttl = ttl; + ev->ctl = ctl; + ev->src = sys_cpu_to_le16(src); + ev->dst = sys_cpu_to_le16(dst); + ev->payload_len = payload_len; + net_buf_simple_add_mem(buf, payload, payload_len); + + tester_send_buf(BTP_SERVICE_ID_MESH, MESH_EV_NET_RECV, CONTROLLER_INDEX, + buf); +done: + os_mbuf_free_chain(buf); +} + +static void model_bound_cb(uint16_t addr, struct bt_mesh_model *model, + uint16_t key_idx) +{ + int i; + + SYS_LOG_DBG("remote addr 0x%04x key_idx 0x%04x model %p", + addr, key_idx, model); + + for (i = 0; i < ARRAY_SIZE(model_bound); i++) { + if (!model_bound[i].model) { + model_bound[i].model = model; + model_bound[i].addr = addr; + model_bound[i].appkey_idx = key_idx; + + return; + } + } + + SYS_LOG_ERR("model_bound is full"); +} + +static void model_unbound_cb(uint16_t addr, struct bt_mesh_model *model, + uint16_t key_idx) +{ + int i; + + SYS_LOG_DBG("remote addr 0x%04x key_idx 0x%04x model %p", + addr, key_idx, model); + + for (i = 0; i < ARRAY_SIZE(model_bound); i++) { + if (model_bound[i].model == model) { + model_bound[i].model = NULL; + model_bound[i].addr = 0x0000; + model_bound[i].appkey_idx = BT_MESH_KEY_UNUSED; + + return; + } + } + + SYS_LOG_INF("model not found"); +} + +static void invalid_bearer_cb(uint8_t opcode) +{ + struct mesh_invalid_bearer_ev ev = { + .opcode = opcode, + }; + + SYS_LOG_DBG("opcode 0x%02x", opcode); + + tester_send(BTP_SERVICE_ID_MESH, MESH_EV_INVALID_BEARER, + CONTROLLER_INDEX, (uint8_t *) &ev, sizeof(ev)); +} + +static void incomp_timer_exp_cb(void) +{ + tester_send(BTP_SERVICE_ID_MESH, MESH_EV_INCOMP_TIMER_EXP, + CONTROLLER_INDEX, NULL, 0); +} + +static struct bt_test_cb bt_test_cb = { + .mesh_net_recv = net_recv_ev, + .mesh_model_bound = model_bound_cb, + .mesh_model_unbound = model_unbound_cb, + .mesh_prov_invalid_bearer = invalid_bearer_cb, + .mesh_trans_incomp_timer_exp = incomp_timer_exp_cb, +}; + +static void lpn_established(uint16_t friend_addr) +{ + + struct bt_mesh_lpn *lpn = &bt_mesh.lpn; + struct mesh_lpn_established_ev ev = { lpn->sub->net_idx, friend_addr, lpn->queue_size, + lpn->recv_win }; + + SYS_LOG_DBG("Friendship (as LPN) established with " + "Friend 0x%04x Queue Size %d Receive Window %d", + friend_addr, lpn->queue_size, lpn->recv_win); + + tester_send(BTP_SERVICE_ID_MESH, MESH_EV_LPN_ESTABLISHED, + CONTROLLER_INDEX, (uint8_t *) &ev, sizeof(ev)); +} + +static void lpn_terminated(uint16_t friend_addr) +{ + struct bt_mesh_lpn *lpn = &bt_mesh.lpn; + struct mesh_lpn_terminated_ev ev = { lpn->sub->net_idx, friend_addr }; + + SYS_LOG_DBG("Friendship (as LPN) lost with Friend " + "0x%04x", friend_addr); + + tester_send(BTP_SERVICE_ID_MESH, MESH_EV_LPN_TERMINATED, + CONTROLLER_INDEX, (uint8_t *) &ev, sizeof(ev)); +} + +void lpn_cb(uint16_t friend_addr, bool established) +{ + if (established) { + lpn_established(friend_addr); + } else { + lpn_terminated(friend_addr); + } +} + +uint8_t tester_init_mesh(void) +{ + health_pub_init(); + + if (IS_ENABLED(CONFIG_BT_TESTING)) { + bt_mesh_lpn_set_cb(lpn_cb); + bt_test_cb_register(&bt_test_cb); + } + + return BTP_STATUS_SUCCESS; +} + +uint8_t tester_unregister_mesh(void) +{ + return BTP_STATUS_SUCCESS; +} + +#endif /* MYNEWT_VAL(BLE_MESH) */ diff --git a/lib/bt/host/nimble/nimble/apps/bttester/src/rtt_pipe.c b/lib/bt/host/nimble/nimble/apps/bttester/src/rtt_pipe.c new file mode 100644 index 00000000..4e667709 --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/bttester/src/rtt_pipe.c @@ -0,0 +1,136 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "syscfg/syscfg.h" + +#if MYNEWT_VAL(BTTESTER_PIPE_RTT) + +#include "os/mynewt.h" +#include "console/console.h" +#include "rtt/SEGGER_RTT.h" + +#include "bttester_pipe.h" + +static struct hal_timer rtt_timer; + +static bttester_pipe_recv_cb app_cb; + +static uint8_t *recv_buf; +static size_t recv_buf_len; +static size_t recv_off; + +static uint8_t rtt_buf_up[MYNEWT_VAL(BTTESTER_RTT_BUFFER_SIZE_UP)]; +static uint8_t rtt_buf_down[MYNEWT_VAL(BTTESTER_RTT_BUFFER_SIZE_DOWN)]; +static int rtt_index_up, rtt_index_down; + +#define RTT_INPUT_POLL_INTERVAL_MIN 10 /* ms */ +#define RTT_INPUT_POLL_INTERVAL_STEP 10 /* ms */ +#define RTT_INPUT_POLL_INTERVAL_MAX 250 /* ms */ + +static int rtt_pipe_get_char(unsigned int index) +{ + char c; + int r; + + r = (int)SEGGER_RTT_Read(index, &c, 1u); + if (r == 1) { + r = (int)(unsigned char)c; + } else { + r = -1; + } + + return r; +} + +static void +rtt_pipe_poll_func(void *arg) +{ + static uint32_t itvl_ms = RTT_INPUT_POLL_INTERVAL_MIN; + static int key = -1; + int avail = recv_buf_len - recv_off; + + if (key < 0) { + key = rtt_pipe_get_char((unsigned int) rtt_index_down); + } + + if (key < 0) { + itvl_ms += RTT_INPUT_POLL_INTERVAL_STEP; + itvl_ms = min(itvl_ms, RTT_INPUT_POLL_INTERVAL_MAX); + } else { + while (key >= 0 && avail > 0) { + recv_buf[recv_off] = (uint8_t) key; + recv_off++; + avail = recv_buf_len - recv_off; + key = rtt_pipe_get_char((unsigned int) rtt_index_down); + } + + /* + * Call application callback with received data. Application + * may provide new buffer or alter data offset. + */ + recv_buf = app_cb(recv_buf, &recv_off); + + itvl_ms = RTT_INPUT_POLL_INTERVAL_MIN; + } + + os_cputime_timer_relative(&rtt_timer, itvl_ms * 1000); +} + +int +bttester_pipe_send(const uint8_t *data, int len) +{ + SEGGER_RTT_Write((unsigned int) rtt_index_up, data, (unsigned int) len); + return 0; +} + +void +bttester_pipe_register(uint8_t *buf, size_t len, bttester_pipe_recv_cb cb) +{ + recv_buf = buf; + recv_buf_len = len; + app_cb = cb; +} + +int +bttester_pipe_init(void) +{ + rtt_index_up = SEGGER_RTT_AllocUpBuffer(MYNEWT_VAL(BTTESTER_RTT_BUFFER_NAME), + rtt_buf_up, sizeof(rtt_buf_up), + SEGGER_RTT_MODE_BLOCK_IF_FIFO_FULL); + + if (rtt_index_up < 0) { + return -1; + } + + rtt_index_down = SEGGER_RTT_AllocDownBuffer(MYNEWT_VAL(BTTESTER_RTT_BUFFER_NAME), + rtt_buf_down, sizeof(rtt_buf_down), + SEGGER_RTT_MODE_BLOCK_IF_FIFO_FULL); + + if (rtt_index_down < 0) { + return -1; + } + + console_printf("Using up-buffer #%d\n", rtt_index_up); + console_printf("Using down-buffer #%d\n", rtt_index_down); + + os_cputime_timer_init(&rtt_timer, rtt_pipe_poll_func, NULL); + os_cputime_timer_relative(&rtt_timer, 200000); + return 0; +} +#endif /* MYNEWT_VAL(BTTESTER_PIPE_RTT) */ diff --git a/lib/bt/host/nimble/nimble/apps/bttester/src/uart_pipe.c b/lib/bt/host/nimble/nimble/apps/bttester/src/uart_pipe.c new file mode 100644 index 00000000..1118d9af --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/bttester/src/uart_pipe.c @@ -0,0 +1,281 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "syscfg/syscfg.h" + +#if MYNEWT_VAL(BTTESTER_PIPE_UART) + +#include "os/mynewt.h" +#include "uart/uart.h" + +#include "bttester_pipe.h" + +static uint8_t *recv_buf; +static size_t recv_buf_len; +static bttester_pipe_recv_cb app_cb; +static size_t recv_off; + +struct uart_pipe_ring { + uint8_t head; + uint8_t tail; + uint16_t size; + uint8_t *buf; +}; + +static struct uart_dev *uart_dev; +static struct uart_pipe_ring cr_tx; +static uint8_t cr_tx_buf[MYNEWT_VAL(BTTESTER_BTP_DATA_SIZE_MAX)]; +typedef void (*console_write_char)(struct uart_dev*, uint8_t); +static console_write_char write_char_cb; + +static struct uart_pipe_ring cr_rx; +static uint8_t cr_rx_buf[MYNEWT_VAL(BTTESTER_BTP_DATA_SIZE_MAX)]; +static volatile bool uart_console_rx_stalled; + +struct os_event rx_ev; + +static inline int +inc_and_wrap(int i, int max) +{ + return (i + 1) & (max - 1); +} + +static void +uart_pipe_ring_add_char(struct uart_pipe_ring *cr, char ch) +{ + cr->buf[cr->head] = ch; + cr->head = inc_and_wrap(cr->head, cr->size); +} + +static uint8_t +uart_pipe_ring_pull_char(struct uart_pipe_ring *cr) +{ + uint8_t ch; + + ch = cr->buf[cr->tail]; + cr->tail = inc_and_wrap(cr->tail, cr->size); + return ch; +} + +static bool +uart_pipe_ring_is_full(const struct uart_pipe_ring *cr) +{ + return inc_and_wrap(cr->head, cr->size) == cr->tail; +} + +static bool +uart_pipe_ring_is_empty(const struct uart_pipe_ring *cr) +{ + return cr->head == cr->tail; +} + +static void +uart_pipe_queue_char(struct uart_dev *uart_dev, uint8_t ch) +{ + int sr; + + if ((uart_dev->ud_dev.od_flags & OS_DEV_F_STATUS_OPEN) == 0) { + return; + } + + OS_ENTER_CRITICAL(sr); + while (uart_pipe_ring_is_full(&cr_tx)) { + /* TX needs to drain */ + uart_start_tx(uart_dev); + OS_EXIT_CRITICAL(sr); + if (os_started()) { + os_time_delay(1); + } + OS_ENTER_CRITICAL(sr); + } + uart_pipe_ring_add_char(&cr_tx, ch); + OS_EXIT_CRITICAL(sr); +} + +/* + * Interrupts disabled when console_tx_char/console_rx_char are called. + * Characters sent only in blocking mode. + */ +static int +uart_console_tx_char(void *arg) +{ + if (uart_pipe_ring_is_empty(&cr_tx)) { + return -1; + } + return uart_pipe_ring_pull_char(&cr_tx); +} + +/* + * Interrupts disabled when console_tx_char/console_rx_char are called. + */ +static int +uart_console_rx_char(void *arg, uint8_t byte) +{ + if (uart_pipe_ring_is_full(&cr_rx)) { + uart_console_rx_stalled = true; + return -1; + } + + uart_pipe_ring_add_char(&cr_rx, byte); + + if (!rx_ev.ev_queued) { + os_eventq_put(os_eventq_dflt_get(), &rx_ev); + } + + return 0; +} + +static int +uart_pipe_handle_char(int key) +{ + recv_buf[recv_off] = (uint8_t) key; + recv_off++; + + return 0; +} + +static void +uart_console_rx_char_event(struct os_event *ev) +{ + static int b = -1; + int sr; + int ret; + + /* We may have unhandled character - try it first */ + if (b >= 0) { + ret = uart_pipe_handle_char(b); + if (ret < 0) { + return; + } + } + + while (!uart_pipe_ring_is_empty(&cr_rx)) { + OS_ENTER_CRITICAL(sr); + b = uart_pipe_ring_pull_char(&cr_rx); + OS_EXIT_CRITICAL(sr); + + /* If UART RX was stalled due to a full receive buffer, restart RX now + * that we have removed a byte from the buffer. + */ + if (uart_console_rx_stalled) { + uart_console_rx_stalled = false; + uart_start_rx(uart_dev); + } + + ret = uart_pipe_handle_char(b); + if (ret < 0) { + return; + } + } + + /* + * Call application callback with received data. Application + * may provide new buffer or alter data offset. + */ + recv_buf = app_cb(recv_buf, &recv_off); + + b = -1; +} + +int +bttester_pipe_send(const uint8_t *data, int len) +{ + int i; + + /* Assure that there is a write cb installed; this enables to debug + * code that is faulting before the console was initialized. + */ + if (!write_char_cb) { + return -1; + } + + for (i = 0; i < len; ++i) { + write_char_cb(uart_dev, data[i]); + } + + uart_start_tx(uart_dev); + + return 0; +} + +int +bttester_pipe_send_buf(struct os_mbuf *buf) +{ + int i, len; + struct os_mbuf *om; + + /* Assure that there is a write cb installed; this enables to debug + * code that is faulting before the console was initialized. + */ + if (!write_char_cb) { + return -1; + } + + for (om = buf; om; om = SLIST_NEXT(om, om_next)) { + len = om->om_len; + for (i = 0; i < len; ++i) { + write_char_cb(uart_dev, om->om_data[i]); + } + } + + uart_start_tx(uart_dev); + + return 0; +} + +int +bttester_pipe_init(void) +{ + struct uart_conf uc = { + .uc_speed = MYNEWT_VAL(CONSOLE_UART_BAUD), + .uc_databits = 8, + .uc_stopbits = 1, + .uc_parity = UART_PARITY_NONE, + .uc_flow_ctl = MYNEWT_VAL(CONSOLE_UART_FLOW_CONTROL), + .uc_tx_char = uart_console_tx_char, + .uc_rx_char = uart_console_rx_char, + }; + + cr_tx.size = sizeof(cr_tx_buf); + cr_tx.buf = cr_tx_buf; + write_char_cb = uart_pipe_queue_char; + + cr_rx.size = sizeof(cr_rx_buf); + cr_rx.buf = cr_rx_buf; + + rx_ev.ev_cb = uart_console_rx_char_event; + + if (!uart_dev) { + uart_dev = (struct uart_dev *)os_dev_open(MYNEWT_VAL(CONSOLE_UART_DEV), + OS_TIMEOUT_NEVER, &uc); + if (!uart_dev) { + return -1; + } + } + return 0; +} + +void +bttester_pipe_register(uint8_t *buf, size_t len, bttester_pipe_recv_cb cb) +{ + recv_buf = buf; + recv_buf_len = len; + app_cb = cb; +} +#endif /* MYNEWT_VAL(BTTESTER_PIPE_UART) */ diff --git a/lib/bt/host/nimble/nimble/apps/bttester/syscfg.yml b/lib/bt/host/nimble/nimble/apps/bttester/syscfg.yml new file mode 100644 index 00000000..330e38d0 --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/bttester/syscfg.yml @@ -0,0 +1,142 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +# Package: apps/blemesh + +syscfg.defs: + BTTESTER_PIPE_UART: + description: 'Set communication pipe to UART' + value: 1 + + BTTESTER_PIPE_RTT: + description: 'Set communication pipe to RTT' + value: 0 + + BTTESTER_RTT_BUFFER_NAME: + description: Bttester rtt pipe buffer name + value: '"bttester"' + + BTTESTER_RTT_BUFFER_SIZE_UP: + description: Bttester upstream buffer size + value: MYNEWT_VAL(BTTESTER_BTP_DATA_SIZE_MAX) + + BTTESTER_RTT_BUFFER_SIZE_DOWN: + description: Bttester downstream buffer size + value: MYNEWT_VAL(BTTESTER_BTP_DATA_SIZE_MAX) + + BTTESTER_PRIVACY_MODE: + description: Enable privacy mode (RPA or NRPA) + value: 0 + + BTTESTER_USE_NRPA: + description: Use Non Resolvable Private Address + value: 0 + + BTTESTER_LTD_ADV_TIMEOUT: + description: Limited advertising timeout + value: 30000 + + BTTESTER_CONN_RETRY: + description: Retry connections when connection failed to be established + value: 0 + + BTTESTER_BTP_DATA_SIZE_MAX: + description: Maximum BTP payload + value: 2048 + + BTTESTER_CONN_PARAM_UPDATE: + description: Trigger conn param update after connection establish + value: 0 + + BTTESTER_DEBUG: + description: Enable debug logging + value: 0 + + BTTESTER_BTP_LOG: + description: Enable logging BTP traffic + value: 0 + + BTTESTER_L2CAP_COC_MTU: + description: Maximum MTU size the application can handle + value: 230 + + BTTESTER_NRPA_TIMEOUT: + description: NRPA rotation timeout in seconds + value: 5 + +syscfg.vals: + OS_MAIN_STACK_SIZE: 512 + SHELL_TASK: 0 + SHELL_NEWTMGR: 0 + LOG_LEVEL: 12 + MSYS_1_BLOCK_COUNT: 100 + + BLE_MONITOR_RTT: 1 + CONSOLE_RTT: 0 + CONSOLE_UART: 0 + CONSOLE_UART_FLOW_CONTROL: UART_FLOW_CTL_RTS_CTS + RTT_NUM_BUFFERS_UP: 0 + RTT_NUM_BUFFERS_DOWN: 0 + + BLE_L2CAP_COC_MAX_NUM: 2 + BLE_L2CAP_SIG_MAX_PROCS: 2 + BLE_L2CAP_ENHANCED_COC: 1 + BLE_VERSION: 52 + # Some testcases require MPS < MTU + BLE_L2CAP_COC_MPS: 100 + BLE_RPA_TIMEOUT: 30 + BLE_SM_BONDING: 1 + BLE_SM_MITM: 0 + BLE_SM_SC: 1 + BLE_SM_OUR_KEY_DIST: 7 + BLE_SM_THEIR_KEY_DIST: 7 + BLE_SVC_GAP_CENTRAL_ADDRESS_RESOLUTION: 1 + BLE_SVC_GAP_PPCP_MIN_CONN_INTERVAL: 9 + BLE_SVC_GAP_PPCP_MAX_CONN_INTERVAL: 30 + BLE_SVC_GAP_PPCP_SUPERVISION_TMO: 2000 + BLE_SVC_GAP_APPEARANCE_WRITE_PERM: 0 + BLE_SVC_GAP_DEVICE_NAME_WRITE_PERM: 0 + BLE_STORE_CONFIG_PERSIST: 0 + + BLE_MESH: 1 + BLE_MESH_SHELL: 0 + BLE_MESH_PROV: 1 + BLE_MESH_RELAY: 1 + BLE_MESH_PB_ADV: 1 + BLE_MESH_PB_GATT: 1 + BLE_MESH_LOW_POWER: 1 + BLE_MESH_LPN_AUTO: 0 + BLE_MESH_GATT_PROXY: 1 + BLE_MESH_LABEL_COUNT: 2 + BLE_MESH_SUBNET_COUNT: 2 + BLE_MESH_MODEL_GROUP_COUNT: 2 + BLE_MESH_APP_KEY_COUNT: 4 + BLE_MESH_IV_UPDATE_TEST: 1 + BLE_MESH_TESTING: 1 + BLE_MESH_FRIEND: 1 + BLE_MESH_CFG_CLI: 1 + BLE_MESH_RX_SDU_MAX: 110 + BLE_MESH_HEALTH_CLI: 1 + BLE_MESH_FRIEND_QUEUE_SIZE: 16 + BLE_MESH_SEG_RETRANSMIT_ATTEMPTS: 6 + BLE_MESH_RX_SEG_MAX: 13 + BLE_MESH_TX_SEG_MSG_COUNT: 2 + BLE_MAX_CONNECTIONS: 8 + + BLE_MESH_ADV_BUF_COUNT: 20 + BLE_MESH_TX_SEG_MAX: 6 diff --git a/lib/bt/host/nimble/nimble/apps/central/pkg.yml b/lib/bt/host/nimble/nimble/apps/central/pkg.yml new file mode 100644 index 00000000..5f5b27a9 --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/central/pkg.yml @@ -0,0 +1,33 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +pkg.name: "apps/central" +pkg.type: app +pkg.description: "Basic central application" +pkg.author: "Krzysztof Kopyściński " + +pkg.deps: + - "@apache-mynewt-core/kernel/os" + - "@apache-mynewt-core/sys/console/full" + - "@apache-mynewt-core/sys/log/full" + - "@apache-mynewt-core/sys/stats/full" + - "@apache-mynewt-core/sys/log/modlog" + - "@apache-mynewt-nimble/nimble/host" + - "@apache-mynewt-nimble/nimble/host/util/" + - "@apache-mynewt-nimble/nimble/host/store/config" diff --git a/lib/bt/host/nimble/nimble/apps/central/src/main.c b/lib/bt/host/nimble/nimble/apps/central/src/main.c new file mode 100755 index 00000000..c088b947 --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/central/src/main.c @@ -0,0 +1,185 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "sysinit/sysinit.h" +#include "os/os.h" +#include "console/console.h" +#include "host/ble_hs.h" +#include "host/util/util.h" +#include "log/log.h" + +static uint8_t g_own_addr_type; + +static void +ble_app_set_addr(void) +{ + ble_addr_t addr; + int rc; + + /* generate new non-resolvable private address */ + rc = ble_hs_id_gen_rnd(0, &addr); + assert(rc == 0); + + /* set generated address */ + rc = ble_hs_id_set_rnd(addr.val); + assert(rc == 0); +} + +/* scan_event() calls scan(), so forward declaration is required */ +static void scan(void); + +/* connection has separate event handler from scan */ +static int +conn_event(struct ble_gap_event *event, void *arg) +{ + switch (event->type) { + case BLE_GAP_EVENT_CONNECT: + if (event->connect.status == 0) { + MODLOG_DFLT(INFO,"Connection was established\n"); + ble_gap_terminate(event->connect.conn_handle, 0x13); + } else { + MODLOG_DFLT(INFO,"Connection failed, error code: %i\n", + event->connect.status); + } + break; + case BLE_GAP_EVENT_DISCONNECT: + MODLOG_DFLT(INFO,"Disconnected, reason code: %i\n", + event->disconnect.reason); + scan(); + break; + case BLE_GAP_EVENT_CONN_UPDATE_REQ: + MODLOG_DFLT(INFO,"Connection update request received\n"); + break; + case BLE_GAP_EVENT_CONN_UPDATE: + if (event->conn_update.status == 0) { + MODLOG_DFLT(INFO,"Connection update successful\n"); + } else { + MODLOG_DFLT(INFO,"Connection update failed; reson: %d\n", + event->conn_update.status); + } + break; + default: + MODLOG_DFLT(INFO,"Connection event type not supported, %d\n", + event->type); + break; + } + return 0; +} + +static int +scan_event(struct ble_gap_event *event, void *arg) +{ + /* predef_uuid stores information about UUID of device, + that we connect to */ + const ble_uuid128_t predef_uuid = + BLE_UUID128_INIT(0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff); + struct ble_hs_adv_fields parsed_fields; + int uuid_cmp_result; + + memset(&parsed_fields, 0, sizeof(parsed_fields)); + + switch (event->type) { + /* advertising report has been received during discovery procedure */ + case BLE_GAP_EVENT_DISC: + MODLOG_DFLT(INFO, "Advertising report received! Checking UUID...\n"); + ble_hs_adv_parse_fields(&parsed_fields, event->disc.data, + event->disc.length_data); + /* Predefined UUID is compared to recieved one; + if doesn't fit - end procedure and go back to scanning, + else - connect. */ + uuid_cmp_result = ble_uuid_cmp(&predef_uuid.u, &parsed_fields.uuids128->u); + if (uuid_cmp_result) { + MODLOG_DFLT(INFO, "UUID doesn't fit\n"); + } else { + MODLOG_DFLT(INFO, "UUID fits, connecting...\n"); + ble_gap_disc_cancel(); + ble_gap_connect(g_own_addr_type, &(event->disc.addr), 10000, + NULL, conn_event, NULL); + } + break; + case BLE_GAP_EVENT_DISC_COMPLETE: + MODLOG_DFLT(INFO,"Discovery completed, reason: %d\n", + event->disc_complete.reason); + scan(); + break; + default: + MODLOG_DFLT(ERROR, "Discovery event not handled\n"); + break; + } + return 0; +} + +static void +scan(void) +{ + int rc; + + /* set scan parameters: + - scan interval in 0.625ms units + - scan window in 0.625ms units + - filter policy - 0 if whitelisting not used + - limited - should limited discovery be used + - passive - should passive scan be used + - filter duplicates - 1 enables filtering duplicated advertisements */ + const struct ble_gap_disc_params scan_params = {10000, 200, 0, 0, 0, 1}; + + /* performs discovery procedure */ + rc = ble_gap_disc(g_own_addr_type, 10000, &scan_params,scan_event, NULL); + assert(rc == 0); +} + +static void +on_sync(void) +{ + int rc; + /* Generate a non-resolvable private address. */ + ble_app_set_addr(); + + /* g_own_addr_type will store type of addres our BSP uses */ + + rc = ble_hs_util_ensure_addr(0); + rc = ble_hs_id_infer_auto(0, &g_own_addr_type); + assert(rc == 0); + /* begin scanning */ + scan(); +} + +static void +on_reset(int reason) +{ + console_printf("Resetting state; reason=%d\n", reason); +} + +int +main(int argc, char **argv) +{ + /* Initialize all packages. */ + sysinit(); + + ble_hs_cfg.sync_cb = on_sync; + ble_hs_cfg.reset_cb = on_reset; + + /* As the last thing, process events from default event queue. */ + while (1) { + os_eventq_run(os_eventq_dflt_get()); + } + + return 0; +} diff --git a/lib/bt/host/nimble/nimble/apps/central/syscfg.yml b/lib/bt/host/nimble/nimble/apps/central/syscfg.yml new file mode 100644 index 00000000..c23ba042 --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/central/syscfg.yml @@ -0,0 +1,26 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + + +syscfg.vals: + BLE_ROLE_BROADCASTER: 0 + BLE_ROLE_CENTRAL: 1 + BLE_ROLE_OBSERVER: 1 + BLE_ROLE_PERIPHERAL: 0 + + # Whether to save data to sys/config, or just keep it in RAM. + BLE_STORE_CONFIG_PERSIST: 0 diff --git a/lib/bt/host/nimble/nimble/apps/ext_advertiser/pkg.yml b/lib/bt/host/nimble/nimble/apps/ext_advertiser/pkg.yml new file mode 100644 index 00000000..32df4efc --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/ext_advertiser/pkg.yml @@ -0,0 +1,38 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +pkg.name: apps/ext_advertiser +pkg.type: app +pkg.description: Extended Advertising sample application. +pkg.author: "Szymon Janc" +pkg.email: "szymon.janc@codecoup.pl" +pkg.homepage: "http://mynewt.apache.org/" +pkg.keywords: + +pkg.deps: + - nimble/host + - nimble/host/util + - nimble/host/services/gap + - nimble/host/services/gatt + - nimble/host/store/config + - "@apache-mynewt-core/kernel/os" + - "@apache-mynewt-core/sys/console/full" + - "@apache-mynewt-core/sys/log/full" + - "@apache-mynewt-core/sys/stats/full" + - "@apache-mynewt-core/sys/sysinit" + - "@apache-mynewt-core/sys/id" diff --git a/lib/bt/host/nimble/nimble/apps/ext_advertiser/src/main.c b/lib/bt/host/nimble/nimble/apps/ext_advertiser/src/main.c new file mode 100644 index 00000000..9cb6c6fe --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/ext_advertiser/src/main.c @@ -0,0 +1,544 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#include +#include +#include + +#include "os/mynewt.h" +#include "console/console.h" +#include "config/config.h" +#include "nimble/ble.h" +#include "host/ble_hs.h" +#include "host/util/util.h" + +#include"patterns.h" + +static uint8_t id_addr_type; + +static void start_legacy_duration(uint8_t pattern, bool configure); +static void start_ext_max_events(uint8_t pattern, bool configure); + +static int +start_ext_max_events_gap_event(struct ble_gap_event *event, void *arg) +{ + static uint8_t pattern = 1; + + switch (event->type) { + case BLE_GAP_EVENT_ADV_COMPLETE: + break; + default: + assert(0); + return 0; + } + + assert(event->adv_complete.instance == 4); + assert(event->adv_complete.reason == BLE_HS_ETIMEOUT); + assert(event->adv_complete.num_ext_adv_events == 10); + + console_printf("instance %u terminated\n", event->adv_complete.instance); + + pattern++; + + start_ext_max_events(pattern, false); + + return 0; +} + +/* Starts advertising instance with 100 max events and changing adv data pattern + * and SID. + */ +static void +start_ext_max_events(uint8_t pattern, bool configure) +{ + struct ble_gap_ext_adv_params params; + static uint8_t adv_data[600]; + struct os_mbuf *data; + uint8_t instance = 4; + ble_addr_t addr; + int events = 10; + int rc; + + if (configure) { + /* use defaults for non-set params */ + memset (¶ms, 0, sizeof(params)); + + /* advertise using random addr */ + params.own_addr_type = BLE_OWN_ADDR_RANDOM; + + params.primary_phy = BLE_HCI_LE_PHY_1M; + params.secondary_phy = BLE_HCI_LE_PHY_1M; + params.tx_power = 127; + params.sid = 4; + + /* allow larger interval, 400 * 0.625ms with 100 events will give up to + * ~2.5 seconds for instance + */ + params.itvl_min = BLE_GAP_ADV_FAST_INTERVAL1_MIN; + params.itvl_max = 400; + + /* configure instance 4 */ + rc = ble_gap_ext_adv_configure(instance, ¶ms, NULL, + start_ext_max_events_gap_event, NULL); + assert (rc == 0); + + /* set random (NRPA) address for instance */ + rc = ble_hs_id_gen_rnd(1, &addr); + assert (rc == 0); + + rc = ble_gap_ext_adv_set_addr(instance, &addr ); + assert (rc == 0); + } + + /* in this case both advertising data and scan response is allowed, but + * both are limited to 31 bytes each + */ + + /* get mbuf for adv data */ + data = os_msys_get_pkthdr(600, 0); + assert(data); + + memset(adv_data, pattern, sizeof(adv_data)); + + /* fill mbuf with adv data */ + rc = os_mbuf_append(data, adv_data, 600); + assert(rc == 0); + + rc = ble_gap_ext_adv_set_data(instance, data); + assert (rc == 0); + + /* start advertising */ + rc = ble_gap_ext_adv_start(instance, 0, events); + assert (rc == 0); + + console_printf("instance %u started (PDUs with max events %d)\n", + instance, events); +} + +static int +start_legacy_duration_gap_event(struct ble_gap_event *event, void *arg) +{ + static uint8_t pattern = 1; + + switch (event->type) { + case BLE_GAP_EVENT_ADV_COMPLETE: + break; + default: + assert(0); + return 0; + } + + assert(event->adv_complete.instance == 3); + assert(event->adv_complete.reason == BLE_HS_ETIMEOUT); + + console_printf("instance %u terminated\n", event->adv_complete.instance); + + pattern++; + + start_legacy_duration(pattern, false); + + return 0; +} + +/* Starts advertising instance with 5sec timeout and changing adv data pattern + * and SID. + */ +static void +start_legacy_duration(uint8_t pattern, bool configure) +{ + struct ble_gap_ext_adv_params params; + uint8_t adv_data[31]; + struct os_mbuf *data; + uint8_t instance = 3; + ble_addr_t addr; + int duration = 500; /* 5seconds, 10ms units */ + int rc; + + if (configure) { + /* use defaults for non-set params */ + memset (¶ms, 0, sizeof(params)); + + /* enable advertising using legacy PDUs */ + params.legacy_pdu = 1; + + /* advertise using random addr */ + params.own_addr_type = BLE_OWN_ADDR_RANDOM; + + params.primary_phy = BLE_HCI_LE_PHY_1M; + params.secondary_phy = BLE_HCI_LE_PHY_1M; + params.tx_power = 127; + params.sid = 3; + + /* configure instance 3 */ + rc = ble_gap_ext_adv_configure(instance, ¶ms, NULL, + start_legacy_duration_gap_event, NULL); + assert (rc == 0); + + /* set random (NRPA) address for instance */ + rc = ble_hs_id_gen_rnd(1, &addr); + assert (rc == 0); + + rc = ble_gap_ext_adv_set_addr(instance, &addr ); + assert (rc == 0); + } + + /* in this case both advertising data and scan response is allowed, but + * both are limited to 31 bytes each + */ + + /* get mbuf for adv data */ + data = os_msys_get_pkthdr(31, 0); + assert(data); + + memset(adv_data, pattern, sizeof(adv_data)); + + /* fill mbuf with adv data */ + rc = os_mbuf_append(data, adv_data, 31); + assert(rc == 0); + + rc = ble_gap_ext_adv_set_data(instance, data); + assert (rc == 0); + + /* start advertising */ + rc = ble_gap_ext_adv_start(instance, duration, 0); + assert (rc == 0); + + console_printf("instance %u started (legacy PDUs with duration %d)\n", + instance, duration); +} + +/* this is simple non-connectable scannable instance using legacy PUDs that + * runs forever + */ +static void +start_scannable_legacy_ext(void) +{ + struct ble_gap_ext_adv_params params; + struct os_mbuf *data; + uint8_t instance = 2; + ble_addr_t addr; + int rc; + + /* use defaults for non-set params */ + memset (¶ms, 0, sizeof(params)); + + /* enable scannable advertising using legacy PDUs */ + params.scannable = 1; + params.legacy_pdu = 1; + + /* advertise using random addr */ + params.own_addr_type = BLE_OWN_ADDR_RANDOM; + + params.primary_phy = BLE_HCI_LE_PHY_1M; + params.secondary_phy = BLE_HCI_LE_PHY_1M; + params.tx_power = 127; + params.sid = 2; + + /* configure instance 2 */ + rc = ble_gap_ext_adv_configure(instance, ¶ms, NULL, NULL, NULL); + assert (rc == 0); + + /* set random (NRPA) address for instance */ + rc = ble_hs_id_gen_rnd(1, &addr); + assert (rc == 0); + + rc = ble_gap_ext_adv_set_addr(instance, &addr ); + assert (rc == 0); + + /* in this case both advertising data and scan response is allowed, but + * both are limited to 31 bytes each + */ + + /* get mbuf for adv data */ + data = os_msys_get_pkthdr(31, 0); + assert(data); + + /* fill mbuf with adv data */ + rc = os_mbuf_append(data, ext_adv_pattern_1, 31); + assert(rc == 0); + + rc = ble_gap_ext_adv_set_data(instance, data); + assert (rc == 0); + + /* get mbuf for scan rsp data */ + data = os_msys_get_pkthdr(31, 0); + assert(data); + + /* fill mbuf with scan rsp data */ + rc = os_mbuf_append(data, ext_adv_pattern_1 + 31, 31); + assert(rc == 0); + + rc = ble_gap_ext_adv_rsp_set_data(instance, data); + assert (rc == 0); + + /* start advertising */ + rc = ble_gap_ext_adv_start(instance, 0, 0); + assert (rc == 0); + + console_printf("instance %u started (scannable legacy PDUs)\n", instance); +} + +static int +scannable_ext_gap_event(struct ble_gap_event *event, void *arg) +{ + switch (event->type) { + default: + break; + } + + return 0; +} + +/* this is simple scannable instance that runs forever + * TODO Get scan request notifications. + */ +static void +start_scannable_ext(void) +{ + struct ble_gap_ext_adv_params params; + struct os_mbuf *data; + uint8_t instance = 1; + ble_addr_t addr; + int rc; + + /* use defaults for non-set params */ + memset (¶ms, 0, sizeof(params)); + + /* enable scannable advertising */ + params.scannable = 1; + + /* enable scan request notification */ + params.scan_req_notif = 1; + + /* advertise using random addr */ + params.own_addr_type = BLE_OWN_ADDR_RANDOM; + + params.primary_phy = BLE_HCI_LE_PHY_1M; + params.secondary_phy = BLE_HCI_LE_PHY_1M; + params.tx_power = 127; + params.sid = 1; + + /* configure instance 1 */ + rc = ble_gap_ext_adv_configure(instance, ¶ms, NULL, + scannable_ext_gap_event, NULL); + assert (rc == 0); + + /* set random (NRPA) address for instance */ + rc = ble_hs_id_gen_rnd(1, &addr); + assert (rc == 0); + + rc = ble_gap_ext_adv_set_addr(instance, &addr ); + assert (rc == 0); + + /* in this case only scan response is allowed */ + + /* get mbuf for scan rsp data */ + data = os_msys_get_pkthdr(sizeof(ext_adv_pattern_1), 0); + assert(data); + + /* fill mbuf with scan rsp data */ + rc = os_mbuf_append(data, ext_adv_pattern_1, sizeof(ext_adv_pattern_1)); + assert(rc == 0); + + rc = ble_gap_ext_adv_rsp_set_data(instance, data); + assert (rc == 0); + + /* start advertising */ + rc = ble_gap_ext_adv_start(instance, 0, 0); + assert (rc == 0); + + console_printf("instance %u started (scannable)\n", instance); +} + +/* this is simple non-connectable instance that runs forever */ +static void +start_non_connectable_ext(void) +{ + struct ble_gap_ext_adv_params params; + struct os_mbuf *data; + uint8_t instance = 0; + int rc; + + /* use defaults for non-set params */ + memset (¶ms, 0, sizeof(params)); + + /* advertise using ID addr */ + params.own_addr_type = id_addr_type; + + params.primary_phy = BLE_HCI_LE_PHY_1M; + params.secondary_phy = BLE_HCI_LE_PHY_1M; + params.tx_power = 127; + params.sid = 0; + + /* configure instance 0 */ + rc = ble_gap_ext_adv_configure(instance, ¶ms, NULL, NULL, NULL); + assert (rc == 0); + + /* in this case only advertisign data is allowed */ + + /* get mbuf for adv data */ + data = os_msys_get_pkthdr(sizeof(ext_adv_pattern_1), 0); + assert(data); + + /* fill mbuf with adv data */ + rc = os_mbuf_append(data, ext_adv_pattern_1, sizeof(ext_adv_pattern_1)); + assert(rc == 0); + + rc = ble_gap_ext_adv_set_data(instance, data); + assert (rc == 0); + + /* start advertising */ + rc = ble_gap_ext_adv_start(instance, 0, 0); + assert (rc == 0); + + console_printf("instance %u started (non-con non-scan)\n", instance); +} + +static void start_periodic(void) +{ + struct ble_gap_periodic_adv_params pparams; + struct ble_gap_ext_adv_params params; + struct ble_hs_adv_fields adv_fields; + struct os_mbuf *data; + uint8_t instance = 5; + ble_addr_t addr; + int rc; + + /* For periodic we use nstance with non-connectable advertising */ + memset (¶ms, 0, sizeof(params)); + + /* advertise using random addr */ + params.own_addr_type = BLE_OWN_ADDR_RANDOM; + + params.primary_phy = BLE_HCI_LE_PHY_1M; + params.secondary_phy = BLE_HCI_LE_PHY_1M; + params.tx_power = 127; + params.sid = 5; + + /* configure instance 5 */ + rc = ble_gap_ext_adv_configure(instance, ¶ms, NULL, NULL, NULL); + assert (rc == 0); + + /* set random (NRPA) address for instance */ + rc = ble_hs_id_gen_rnd(1, &addr); + assert (rc == 0); + + rc = ble_gap_ext_adv_set_addr(instance, &addr ); + assert (rc == 0); + + memset(&adv_fields, 0, sizeof(adv_fields)); + adv_fields.name = (const uint8_t *)"nimble with periodic"; + adv_fields.name_len = strlen((char *)adv_fields.name); + + /* Default to legacy PDUs size, mbuf chain will be increased if needed + */ + data = os_msys_get_pkthdr(BLE_HCI_MAX_ADV_DATA_LEN, 0); + assert(data); + + rc = ble_hs_adv_set_fields_mbuf(&adv_fields, data); + assert(rc == 0); + + rc = ble_gap_ext_adv_set_data(instance, data); + assert(rc == 0); + + /* configure periodic advertising */ + memset(&pparams, 0, sizeof(pparams)); + pparams.include_tx_power = 1; + pparams.itvl_min = 160; + pparams.itvl_max = 240; + + rc = ble_gap_periodic_adv_configure(instance, &pparams); + assert(rc == 0); + + /* get mbuf for periodic data */ + data = os_msys_get_pkthdr(sizeof(ext_adv_pattern_1), 0); + assert(data); + + /* fill mbuf with periodic data */ + rc = os_mbuf_append(data, ext_adv_pattern_1, sizeof(ext_adv_pattern_1)); + assert(rc == 0); + + rc = ble_gap_periodic_adv_set_data(instance, data); + assert (rc == 0); + + /* start periodic advertising */ + rc = ble_gap_periodic_adv_start(instance); + assert (rc == 0); + + /* start advertising */ + rc = ble_gap_ext_adv_start(instance, 0, 0); + assert (rc == 0); + + console_printf("instance %u started (periodic)\n", instance); +} + +static void +on_sync(void) +{ + int rc; + + console_printf("Synced, starting advertising\n"); + + /* Make sure we have proper identity address set (public preferred) */ + rc = ble_hs_util_ensure_addr(0); + assert(rc == 0); + + /* configure global address */ + rc = ble_hs_id_infer_auto(0, &id_addr_type); + assert(rc == 0); + + start_non_connectable_ext(); + + start_scannable_ext(); + + start_scannable_legacy_ext(); + + start_legacy_duration(0, true); + + start_ext_max_events(0, true); + + start_periodic(); +} + +/* + * main + * + * The main task for the project. This function initializes the packages, + * then starts serving events from default event queue. + * + * @return int NOTE: this function should never return! + */ +int +main(void) +{ + /* Initialize OS */ + sysinit(); + + console_printf("Extended Advertising sample application\n"); + + /* Set sync callback */ + ble_hs_cfg.sync_cb = on_sync; + + /* As the last thing, process events from default event queue */ + while (1) { + os_eventq_run(os_eventq_dflt_get()); + } + return 0; +} diff --git a/lib/bt/host/nimble/nimble/apps/ext_advertiser/src/patterns.h b/lib/bt/host/nimble/nimble/apps/ext_advertiser/src/patterns.h new file mode 100644 index 00000000..9485d0d4 --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/ext_advertiser/src/patterns.h @@ -0,0 +1,186 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +static const uint8_t ext_adv_pattern_1[] = { + 0x00, 0x02, 0x00, 0x04, 0x00, 0x06, 0x00, 0x08, 0x00, 0x0a, + 0x00, 0x0c, 0x00, 0x0e, 0x00, 0x10, 0x00, 0x12, 0x00, 0x14, + 0x00, 0x16, 0x00, 0x18, 0x00, 0x1a, 0x00, 0x1c, 0x00, 0x1e, + 0x00, 0x20, 0x00, 0x22, 0x00, 0x24, 0x00, 0x26, 0x00, 0x28, + 0x00, 0x2a, 0x00, 0x2c, 0x00, 0x2e, 0x00, 0x30, 0x00, 0x32, + 0x00, 0x34, 0x00, 0x36, 0x00, 0x38, 0x00, 0x3a, 0x00, 0x3c, + 0x00, 0x3e, 0x00, 0x40, 0x00, 0x42, 0x00, 0x44, 0x00, 0x46, + 0x00, 0x48, 0x00, 0x4a, 0x00, 0x4c, 0x00, 0x4e, 0x00, 0x50, + 0x00, 0x52, 0x00, 0x54, 0x00, 0x56, 0x00, 0x58, 0x00, 0x5a, + 0x00, 0x5c, 0x00, 0x5e, 0x00, 0x60, 0x00, 0x62, 0x00, 0x64, + 0x00, 0x66, 0x00, 0x68, 0x00, 0x6a, 0x00, 0x6c, 0x00, 0x6e, + 0x00, 0x70, 0x00, 0x72, 0x00, 0x74, 0x00, 0x76, 0x00, 0x78, + 0x00, 0x7a, 0x00, 0x7c, 0x00, 0x7e, 0x00, 0x80, 0x00, 0x82, + 0x00, 0x84, 0x00, 0x86, 0x00, 0x88, 0x00, 0x8a, 0x00, 0x8c, + 0x00, 0x8e, 0x00, 0x90, 0x00, 0x92, 0x00, 0x94, 0x00, 0x96, + 0x00, 0x98, 0x00, 0x9a, 0x00, 0x9c, 0x00, 0x9e, 0x00, 0xa0, + 0x00, 0xa2, 0x00, 0xa4, 0x00, 0xa6, 0x00, 0xa8, 0x00, 0xaa, + 0x00, 0xac, 0x00, 0xae, 0x00, 0xb0, 0x00, 0xb2, 0x00, 0xb4, + 0x00, 0xb6, 0x00, 0xb8, 0x00, 0xba, 0x00, 0xbc, 0x00, 0xbe, + 0x00, 0xc0, 0x00, 0xc2, 0x00, 0xc4, 0x00, 0xc6, 0x00, 0xc8, + 0x00, 0xca, 0x00, 0xcc, 0x00, 0xce, 0x00, 0xd0, 0x00, 0xd2, + 0x00, 0xd4, 0x00, 0xd6, 0x00, 0xd8, 0x00, 0xda, 0x00, 0xdc, + 0x00, 0xde, 0x00, 0xe0, 0x00, 0xe2, 0x00, 0xe4, 0x00, 0xe6, + 0x00, 0xe8, 0x00, 0xea, 0x00, 0xec, 0x00, 0xee, 0x00, 0xf0, + 0x00, 0xf2, 0x00, 0xf4, 0x00, 0xf6, 0x00, 0xf8, 0x00, 0xfa, + 0x00, 0xfc, 0x00, 0xfe, 0x01, 0x01, 0x01, 0x03, 0x01, 0x05, + 0x01, 0x07, 0x01, 0x09, 0x01, 0x0b, 0x01, 0x0d, 0x01, 0x0f, + 0x01, 0x11, 0x01, 0x13, 0x01, 0x15, 0x01, 0x17, 0x01, 0x19, + 0x01, 0x1b, 0x01, 0x1d, 0x01, 0x1f, 0x01, 0x21, 0x01, 0x23, + 0x01, 0x25, 0x01, 0x27, 0x01, 0x29, 0x01, 0x2b, 0x01, 0x2d, + 0x01, 0x2f, 0x01, 0x31, 0x01, 0x33, 0x01, 0x35, 0x01, 0x37, + 0x01, 0x39, 0x01, 0x3b, 0x01, 0x3d, 0x01, 0x3f, 0x01, 0x41, + 0x01, 0x43, 0x01, 0x45, 0x01, 0x47, 0x01, 0x49, 0x01, 0x4b, + 0x01, 0x4d, 0x01, 0x4f, 0x01, 0x51, 0x01, 0x53, 0x01, 0x55, + 0x01, 0x57, 0x01, 0x59, 0x01, 0x5b, 0x01, 0x5d, 0x01, 0x5f, + 0x01, 0x61, 0x01, 0x63, 0x01, 0x65, 0x01, 0x67, 0x01, 0x69, + 0x01, 0x6b, 0x01, 0x6d, 0x01, 0x6f, 0x01, 0x71, 0x01, 0x73, + 0x01, 0x75, 0x01, 0x77, 0x01, 0x79, 0x01, 0x7b, 0x01, 0x7d, + 0x01, 0x7f, 0x01, 0x81, 0x01, 0x83, 0x01, 0x85, 0x01, 0x87, + 0x01, 0x89, 0x01, 0x8b, 0x01, 0x8d, 0x01, 0x8f, 0x01, 0x91, + 0x01, 0x93, 0x01, 0x95, 0x01, 0x97, 0x01, 0x99, 0x01, 0x9b, + 0x01, 0x9d, 0x01, 0x9f, 0x01, 0xa1, 0x01, 0xa3, 0x01, 0xa5, + 0x01, 0xa7, 0x01, 0xa9, 0x01, 0xab, 0x01, 0xad, 0x01, 0xaf, + 0x01, 0xb1, 0x01, 0xb3, 0x01, 0xb5, 0x01, 0xb7, 0x01, 0xb9, + 0x01, 0xbb, 0x01, 0xbd, 0x01, 0xbf, 0x01, 0xc1, 0x01, 0xc3, + 0x01, 0xc5, 0x01, 0xc7, 0x01, 0xc9, 0x01, 0xcb, 0x01, 0xcd, + 0x01, 0xcf, 0x01, 0xd1, 0x01, 0xd3, 0x01, 0xd5, 0x01, 0xd7, + 0x01, 0xd9, 0x01, 0xdb, 0x01, 0xdd, 0x01, 0xdf, 0x01, 0xe1, + 0x01, 0xe3, 0x01, 0xe5, 0x01, 0xe7, 0x01, 0xe9, 0x01, 0xeb, + 0x01, 0xed, 0x01, 0xef, 0x01, 0xf1, 0x01, 0xf3, 0x01, 0xf5, + 0x01, 0xf7, 0x01, 0xf9, 0x01, 0xfb, 0x01, 0xfd, 0x02, 0x00, + 0x02, 0x02, 0x02, 0x04, 0x02, 0x06, 0x02, 0x08, 0x02, 0x0a, + 0x02, 0x0c, 0x02, 0x0e, 0x02, 0x10, 0x02, 0x12, 0x02, 0x14, + 0x02, 0x16, 0x02, 0x18, 0x02, 0x1a, 0x02, 0x1c, 0x02, 0x1e, + 0x02, 0x20, 0x02, 0x22, 0x02, 0x24, 0x02, 0x26, 0x02, 0x28, + 0x02, 0x2a, 0x02, 0x2c, 0x02, 0x2e, 0x02, 0x30, 0x02, 0x32, + 0x02, 0x34, 0x02, 0x36, 0x02, 0x38, 0x02, 0x3a, 0x02, 0x3c, + 0x02, 0x3e, 0x02, 0x40, 0x02, 0x42, 0x02, 0x44, 0x02, 0x46, + 0x02, 0x48, 0x02, 0x4a, 0x02, 0x4c, 0x02, 0x4e, 0x02, 0x50, + 0x02, 0x52, 0x02, 0x54, 0x02, 0x56, 0x02, 0x58, 0x02, 0x5a, + 0x02, 0x5c, 0x02, 0x5e, 0x02, 0x60, 0x02, 0x62, 0x02, 0x64, + 0x02, 0x66, 0x02, 0x68, 0x02, 0x6a, 0x02, 0x6c, 0x02, 0x6e, + 0x02, 0x70, 0x02, 0x72, 0x02, 0x74, 0x02, 0x76, 0x02, 0x78, + 0x02, 0x7a, 0x02, 0x7c, 0x02, 0x7e, 0x02, 0x80, 0x02, 0x82, + 0x02, 0x84, 0x02, 0x86, 0x02, 0x88, 0x02, 0x8a, 0x02, 0x8c, + 0x02, 0x8e, 0x02, 0x90, 0x02, 0x92, 0x02, 0x94, 0x02, 0x96, + 0x02, 0x98, 0x02, 0x9a, 0x02, 0x9c, 0x02, 0x9e, 0x02, 0xa0, + 0x02, 0xa2, 0x02, 0xa4, 0x02, 0xa6, 0x02, 0xa8, 0x02, 0xaa, + 0x02, 0xac, 0x02, 0xae, 0x02, 0xb0, 0x02, 0xb2, 0x02, 0xb4, + 0x02, 0xb6, 0x02, 0xb8, 0x02, 0xba, 0x02, 0xbc, 0x02, 0xbe, + 0x02, 0xc0, 0x02, 0xc2, 0x02, 0xc4, 0x02, 0xc6, 0x02, 0xc8, + 0x02, 0xca, 0x02, 0xcc, 0x02, 0xce, 0x02, 0xd0, 0x02, 0xd2, + 0x02, 0xd4, 0x02, 0xd6, 0x02, 0xd8, 0x02, 0xda, 0x02, 0xdc, + 0x02, 0xde, 0x02, 0xe0, 0x02, 0xe2, 0x02, 0xe4, 0x02, 0xe6, + 0x02, 0xe8, 0x02, 0xea, 0x02, 0xec, 0x02, 0xee, 0x02, 0xf0, + 0x02, 0xf2, 0x02, 0xf4, 0x02, 0xf6, 0x02, 0xf8, 0x02, 0xfa, + 0x02, 0xfc, 0x02, 0xfe, 0x03, 0x01, 0x03, 0x03, 0x03, 0x05, + 0x03, 0x07, 0x03, 0x09, 0x03, 0x0b, 0x03, 0x0d, 0x03, 0x0f, + 0x03, 0x11, 0x03, 0x13, 0x03, 0x15, 0x03, 0x17, 0x03, 0x19, + 0x03, 0x1b, 0x03, 0x1d, 0x03, 0x1f, 0x03, 0x21, 0x03, 0x23, + 0x03, 0x25, 0x03, 0x27, 0x03, 0x29, 0x03, 0x2b, 0x03, 0x2d, + 0x03, 0x2f, 0x03, 0x31, 0x03, 0x33, 0x03, 0x35, 0x03, 0x37, + 0x03, 0x39, 0x03, 0x3b, 0x03, 0x3d, 0x03, 0x3f, 0x03, 0x41, + 0x03, 0x43, 0x03, 0x45, 0x03, 0x47, 0x03, 0x49, 0x03, 0x4b, + 0x03, 0x4d, 0x03, 0x4f, 0x03, 0x51, 0x03, 0x53, 0x03, 0x55, + 0x03, 0x57, 0x03, 0x59, 0x03, 0x5b, 0x03, 0x5d, 0x03, 0x5f, + 0x03, 0x61, 0x03, 0x63, 0x03, 0x65, 0x03, 0x67, 0x03, 0x69, + 0x03, 0x6b, 0x03, 0x6d, 0x03, 0x6f, 0x03, 0x71, 0x03, 0x73, + 0x03, 0x75, 0x03, 0x77, 0x03, 0x79, 0x03, 0x7b, 0x03, 0x7d, + 0x03, 0x7f, 0x03, 0x81, 0x03, 0x83, 0x03, 0x85, 0x03, 0x87, + 0x03, 0x89, 0x03, 0x8b, 0x03, 0x8d, 0x03, 0x8f, 0x03, 0x91, + 0x03, 0x93, 0x03, 0x95, 0x03, 0x97, 0x03, 0x99, 0x03, 0x9b, + 0x03, 0x9d, 0x03, 0x9f, 0x03, 0xa1, 0x03, 0xa3, 0x03, 0xa5, + 0x03, 0xa7, 0x03, 0xa9, 0x03, 0xab, 0x03, 0xad, 0x03, 0xaf, + 0x03, 0xb1, 0x03, 0xb3, 0x03, 0xb5, 0x03, 0xb7, 0x03, 0xb9, + 0x03, 0xbb, 0x03, 0xbd, 0x03, 0xbf, 0x03, 0xc1, 0x03, 0xc3, + 0x03, 0xc5, 0x03, 0xc7, 0x03, 0xc9, 0x03, 0xcb, 0x03, 0xcd, + 0x03, 0xcf, 0x03, 0xd1, 0x03, 0xd3, 0x03, 0xd5, 0x03, 0xd7, + 0x03, 0xd9, 0x03, 0xdb, 0x03, 0xdd, 0x03, 0xdf, 0x03, 0xe1, + 0x03, 0xe3, 0x03, 0xe5, 0x03, 0xe7, 0x03, 0xe9, 0x03, 0xeb, + 0x03, 0xed, 0x03, 0xef, 0x03, 0xf1, 0x03, 0xf3, 0x03, 0xf5, + 0x03, 0xf7, 0x03, 0xf9, 0x03, 0xfb, 0x03, 0xfd, 0x04, 0x00, + 0x04, 0x02, 0x04, 0x04, 0x04, 0x06, 0x04, 0x08, 0x04, 0x0a, + 0x04, 0x0c, 0x04, 0x0e, 0x04, 0x10, 0x04, 0x12, 0x04, 0x14, + 0x04, 0x16, 0x04, 0x18, 0x04, 0x1a, 0x04, 0x1c, 0x04, 0x1e, + 0x04, 0x20, 0x04, 0x22, 0x04, 0x24, 0x04, 0x26, 0x04, 0x28, + 0x04, 0x2a, 0x04, 0x2c, 0x04, 0x2e, 0x04, 0x30, 0x04, 0x32, + 0x04, 0x34, 0x04, 0x36, 0x04, 0x38, 0x04, 0x3a, 0x04, 0x3c, + 0x04, 0x3e, 0x04, 0x40, 0x04, 0x42, 0x04, 0x44, 0x04, 0x46, + 0x04, 0x48, 0x04, 0x4a, 0x04, 0x4c, 0x04, 0x4e, 0x04, 0x50, + 0x04, 0x52, 0x04, 0x54, 0x04, 0x56, 0x04, 0x58, 0x04, 0x5a, + 0x04, 0x5c, 0x04, 0x5e, 0x04, 0x60, 0x04, 0x62, 0x04, 0x64, + 0x04, 0x66, 0x04, 0x68, 0x04, 0x6a, 0x04, 0x6c, 0x04, 0x6e, + 0x04, 0x70, 0x04, 0x72, 0x04, 0x74, 0x04, 0x76, 0x04, 0x78, + 0x04, 0x7a, 0x04, 0x7c, 0x04, 0x7e, 0x04, 0x80, 0x04, 0x82, + 0x04, 0x84, 0x04, 0x86, 0x04, 0x88, 0x04, 0x8a, 0x04, 0x8c, + 0x04, 0x8e, 0x04, 0x90, 0x04, 0x92, 0x04, 0x94, 0x04, 0x96, + 0x04, 0x98, 0x04, 0x9a, 0x04, 0x9c, 0x04, 0x9e, 0x04, 0xa0, + 0x04, 0xa2, 0x04, 0xa4, 0x04, 0xa6, 0x04, 0xa8, 0x04, 0xaa, + 0x04, 0xac, 0x04, 0xae, 0x04, 0xb0, 0x04, 0xb2, 0x04, 0xb4, + 0x04, 0xb6, 0x04, 0xb8, 0x04, 0xba, 0x04, 0xbc, 0x04, 0xbe, + 0x04, 0xc0, 0x04, 0xc2, 0x04, 0xc4, 0x04, 0xc6, 0x04, 0xc8, + 0x04, 0xca, 0x04, 0xcc, 0x04, 0xce, 0x04, 0xd0, 0x04, 0xd2, + 0x04, 0xd4, 0x04, 0xd6, 0x04, 0xd8, 0x04, 0xda, 0x04, 0xdc, + 0x04, 0xde, 0x04, 0xe0, 0x04, 0xe2, 0x04, 0xe4, 0x04, 0xe6, + 0x04, 0xe8, 0x04, 0xea, 0x04, 0xec, 0x04, 0xee, 0x04, 0xf0, + 0x04, 0xf2, 0x04, 0xf4, 0x04, 0xf6, 0x04, 0xf8, 0x04, 0xfa, + 0x04, 0xfc, 0x04, 0xfe, 0x05, 0x01, 0x05, 0x03, 0x05, 0x05, + 0x05, 0x07, 0x05, 0x09, 0x05, 0x0b, 0x05, 0x0d, 0x05, 0x0f, + 0x05, 0x11, 0x05, 0x13, 0x05, 0x15, 0x05, 0x17, 0x05, 0x19, + 0x05, 0x1b, 0x05, 0x1d, 0x05, 0x1f, 0x05, 0x21, 0x05, 0x23, + 0x05, 0x25, 0x05, 0x27, 0x05, 0x29, 0x05, 0x2b, 0x05, 0x2d, + 0x05, 0x2f, 0x05, 0x31, 0x05, 0x33, 0x05, 0x35, 0x05, 0x37, + 0x05, 0x39, 0x05, 0x3b, 0x05, 0x3d, 0x05, 0x3f, 0x05, 0x41, + 0x05, 0x43, 0x05, 0x45, 0x05, 0x47, 0x05, 0x49, 0x05, 0x4b, + 0x05, 0x4d, 0x05, 0x4f, 0x05, 0x51, 0x05, 0x53, 0x05, 0x55, + 0x05, 0x57, 0x05, 0x59, 0x05, 0x5b, 0x05, 0x5d, 0x05, 0x5f, + 0x05, 0x61, 0x05, 0x63, 0x05, 0x65, 0x05, 0x67, 0x05, 0x69, + 0x05, 0x6b, 0x05, 0x6d, 0x05, 0x6f, 0x05, 0x71, 0x05, 0x73, + 0x05, 0x75, 0x05, 0x77, 0x05, 0x79, 0x05, 0x7b, 0x05, 0x7d, + 0x05, 0x7f, 0x05, 0x81, 0x05, 0x83, 0x05, 0x85, 0x05, 0x87, + 0x05, 0x89, 0x05, 0x8b, 0x05, 0x8d, 0x05, 0x8f, 0x05, 0x91, + 0x05, 0x93, 0x05, 0x95, 0x05, 0x97, 0x05, 0x99, 0x05, 0x9b, + 0x05, 0x9d, 0x05, 0x9f, 0x05, 0xa1, 0x05, 0xa3, 0x05, 0xa5, + 0x05, 0xa7, 0x05, 0xa9, 0x05, 0xab, 0x05, 0xad, 0x05, 0xaf, + 0x05, 0xb1, 0x05, 0xb3, 0x05, 0xb5, 0x05, 0xb7, 0x05, 0xb9, + 0x05, 0xbb, 0x05, 0xbd, 0x05, 0xbf, 0x05, 0xc1, 0x05, 0xc3, + 0x05, 0xc5, 0x05, 0xc7, 0x05, 0xc9, 0x05, 0xcb, 0x05, 0xcd, + 0x05, 0xcf, 0x05, 0xd1, 0x05, 0xd3, 0x05, 0xd5, 0x05, 0xd7, + 0x05, 0xd9, 0x05, 0xdb, 0x05, 0xdd, 0x05, 0xdf, 0x05, 0xe1, + 0x05, 0xe3, 0x05, 0xe5, 0x05, 0xe7, 0x05, 0xe9, 0x05, 0xeb, + 0x05, 0xed, 0x05, 0xef, 0x05, 0xf1, 0x05, 0xf3, 0x05, 0xf5, + 0x05, 0xf7, 0x05, 0xf9, 0x05, 0xfb, 0x05, 0xfd, 0x06, 0x00, + 0x06, 0x02, 0x06, 0x04, 0x06, 0x06, 0x06, 0x08, 0x06, 0x0a, + 0x06, 0x0c, 0x06, 0x0e, 0x06, 0x10, 0x06, 0x12, 0x06, 0x14, + 0x06, 0x16, 0x06, 0x18, 0x06, 0x1a, 0x06, 0x1c, 0x06, 0x1e, + 0x06, 0x20, 0x06, 0x22, 0x06, 0x24, 0x06, 0x26, 0x06, 0x28, + 0x06, 0x2a, 0x06, 0x2c, 0x06, 0x2e, 0x06, 0x30, 0x06, 0x32, + 0x06, 0x34, 0x06, 0x36, 0x06, 0x38, 0x06, 0x3a, 0x06, 0x3c, + 0x06, 0x3e, 0x06, 0x40, 0x06, 0x42, 0x06, 0x44, 0x06, 0x46, + 0x06, 0x48, 0x06, 0x4a, 0x06, 0x4c, 0x06, 0x4e, 0x06, 0x50, + 0x06, 0x52, 0x06, 0x54, 0x06, 0x56, 0x06, 0x58, 0x06, 0x5a, + 0x06, 0x5c, 0x06, 0x5e, 0x06, 0x60, 0x06, 0x62, 0x06, 0x64, + 0x06, 0x66, 0x06, 0x68, 0x06, 0x6a, 0x06, 0x6c, 0x06, 0x6e, + 0x06, 0x70, 0x06, 0x72, 0x06, 0x74, 0x06, 0x76, 0x06, 0x78 +}; diff --git a/lib/bt/host/nimble/nimble/apps/ext_advertiser/syscfg.yml b/lib/bt/host/nimble/nimble/apps/ext_advertiser/syscfg.yml new file mode 100644 index 00000000..f157ab82 --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/ext_advertiser/syscfg.yml @@ -0,0 +1,51 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + + +syscfg.vals: + # Disable not used GAP roles (we only do non-connectable + # advertising here) + BLE_ROLE_BROADCASTER: 1 + BLE_ROLE_CENTRAL: 0 + BLE_ROLE_OBSERVER: 0 + BLE_ROLE_PERIPHERAL: 0 + + # Disable unused eddystone featdure. + BLE_EDDYSTONE: 0 + + # Enable Extended Advertising + BLE_EXT_ADV: 1 + + # Enable Periodic Advertising + BLE_PERIODIC_ADV: 1 + + # Max advertising data size + BLE_EXT_ADV_MAX_SIZE: 1650 + + # Number of multi-advertising instances. Note that due + # to historical reasonds total number of advertising + # instances is BLE_MULTI_ADV_INSTANCES + 1 as instance + # 0 is always available + BLE_MULTI_ADV_INSTANCES: 5 + + # Controller uses msys pool for storing advertising data and scan responses. + # Since we advertise a lot of data (~6k in total) at the same time we need + # to increase block count. + MSYS_1_BLOCK_COUNT: 32 + + # Whether to save data to sys/config, or just keep it in RAM. + BLE_STORE_CONFIG_PERSIST: 0 diff --git a/lib/bt/host/nimble/nimble/apps/mesh_badge/README.md b/lib/bt/host/nimble/nimble/apps/mesh_badge/README.md new file mode 100644 index 00000000..8d2b3fa3 --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/mesh_badge/README.md @@ -0,0 +1,48 @@ +### Mesh Badge + + +##### Overview +******** + +This sample app for the reel board showcases Bluetooth Mesh + +The app starts off as a regular Bluetooth GATT peripheral application. +Install the the "nRF Connect" app on your phone (available both for +Android and iOS) to access the service that the app exposes. The service +can also be accessed with any Bluetooth LE GATT client from your PC, +however these instructions focus on the necessary steps for phones. + +##### Steps to set up +*************** + +* On your phone, use the nRF Connect app to Scan for devices and look + for "reel board" +* Connect to the device. You'll see a single service - select it +* Request to write to the characteristic by pressing on the upward pointing + arrow symbol +* Select "Text" to enter text instead of hex +* Enter your name (or any other arbitrary text). Multiple words + separated by spaces are possible. The font used on the reel display + allows three rows of up to 12 characters + wide text. You can force line breaks with a comma. +* Press "Send" - this will trigger pairing since this is a protected + characteristic. The passkey for the pairing will be shown on the board's + display. Enter the passkey in your phone. +* Once pairing is complete the board will show the text you sent. If + you're not happy with it you can try writing something else. +* When you're happy with the text, disconnect from the board (exit the app or + go back to the device scan page) +* Once disconnected the board switches over to Bluetooth Mesh mode, and you + can't connect to it anymore over GATT. + +If you configure multiple boards like this they can communicate with +each other over mesh: by pressing the user button on the board the first +word (name) of the stored text will be sent to all other boards in +the network and cause the other boards to display " says hi!". + +To reset a board to its initial state (disable mesh, erase the stored +text, and make it connectable over GATT): + +* Keep the user button pressed when powering on (or press the reset button + when powered) +* Wait until "Reseting Device" is shown diff --git a/lib/bt/host/nimble/nimble/apps/mesh_badge/pkg.yml b/lib/bt/host/nimble/nimble/apps/mesh_badge/pkg.yml new file mode 100644 index 00000000..d1276a51 --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/mesh_badge/pkg.yml @@ -0,0 +1,37 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +pkg.name: apps/mesh_badge +pkg.type: app +pkg.description: Sample app for the reel board that showcases Bluetooth Mesh +pkg.author: "Apache Mynewt " +pkg.homepage: "http://mynewt.apache.org/" +pkg.keywords: + +pkg.deps: + - "@apache-mynewt-core/hw/drivers/display/cfb" + - "@apache-mynewt-core/hw/drivers/display/ssd1673" + - "@apache-mynewt-core/kernel/os" + - "@apache-mynewt-core/sys/console/full" + - "@apache-mynewt-core/sys/log/full" + - "@apache-mynewt-core/sys/log/modlog" + - "@apache-mynewt-core/sys/stats/full" + - "@apache-mynewt-core/sys/shell" + - nimble/host + - nimble/host/services/gap + - nimble/host/services/gatt + - nimble/host/store/config diff --git a/lib/bt/host/nimble/nimble/apps/mesh_badge/src/board.h b/lib/bt/host/nimble/nimble/apps/mesh_badge/src/board.h new file mode 100644 index 00000000..af77e0b4 --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/mesh_badge/src/board.h @@ -0,0 +1,15 @@ +/* + * Copyright (c) 2018 Phytec Messtechnik GmbH + * Copyright (c) 2018 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "mesh/mesh.h" + +void board_refresh_display(void); +void board_show_text(const char *text, bool center, int32_t duration); +void board_blink_leds(void); +void board_add_hello(uint16_t addr, const char *name); +void board_add_heartbeat(uint16_t addr, uint8_t hops); +int board_init(void); diff --git a/lib/bt/host/nimble/nimble/apps/mesh_badge/src/gatt_svr.c b/lib/bt/host/nimble/nimble/apps/mesh_badge/src/gatt_svr.c new file mode 100644 index 00000000..c6005a68 --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/mesh_badge/src/gatt_svr.c @@ -0,0 +1,226 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#include +#include + +#include "services/gap/ble_svc_gap.h" +#include "bsp/bsp.h" +#include "host/ble_hs.h" +#include "host/ble_uuid.h" + +#include "mesh.h" +#include "board.h" +#include "mesh_badge.h" + +static const ble_uuid16_t gatt_cud_uuid = BLE_UUID16_INIT(0x2901); +static const ble_uuid16_t gatt_cpf_uuid = BLE_UUID16_INIT(0x2904); + +/** @brief GATT Characteristic Presentation Format Attribute Value. */ +struct bt_gatt_cpf { + /** Format of the value of the characteristic */ + uint8_t format; + /** Exponent field to determine how the value of this characteristic is further formatted */ + int8_t exponent; + /** Unit of the characteristic */ + uint16_t unit; + /** Name space of the description */ + uint8_t name_space; + /** Description of the characteristic as defined in a higher layer profile */ + uint16_t description; +} __packed; + +#define CPF_FORMAT_UTF8 0x19 + +static const struct bt_gatt_cpf name_cpf = { + .format = CPF_FORMAT_UTF8, +}; + +static const ble_uuid128_t name_uuid = BLE_UUID128_INIT( + 0xf0, 0xde, 0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12, + 0x78, 0x56, 0x34, 0x12, 0x78, 0x56, 0x34, 0x12); + +static const ble_uuid128_t name_enc_uuid = BLE_UUID128_INIT( + 0xf1, 0xde, 0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12, + 0x78, 0x56, 0x34, 0x12, 0x78, 0x56, 0x34, 0x12); + +static int +gatt_svr_chr_access(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, + void *arg); + +static const struct ble_gatt_svc_def gatt_svr_svcs[] = { + { + .type = BLE_GATT_SVC_TYPE_PRIMARY, + .uuid = &name_uuid.u, + .characteristics = (struct ble_gatt_chr_def[]) { { + .uuid = &name_enc_uuid.u, + .access_cb = gatt_svr_chr_access, + .flags = BLE_GATT_CHR_F_READ | + BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_WRITE_ENC, + .descriptors = (struct ble_gatt_dsc_def[]) { { + .uuid = &gatt_cud_uuid.u, + .access_cb = gatt_svr_chr_access, + .att_flags = BLE_ATT_F_READ, + }, { + .uuid = &gatt_cpf_uuid.u, + .access_cb = gatt_svr_chr_access, + .att_flags = BLE_ATT_F_READ, + }, { + 0, /* No more descriptors in this characteristic. */ + } } + }, { + 0, /* No more characteristics in this service. */ + } }, + }, + + { + 0, /* No more services. */ + }, +}; + +static int read_name(struct os_mbuf *om) +{ + const char *value = bt_get_name(); + int rc; + + rc = os_mbuf_append(om, value, (uint16_t) strlen(value)); + return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; +} + +static int write_name(struct os_mbuf *om) +{ + char name[MYNEWT_VAL(BLE_SVC_GAP_DEVICE_NAME_MAX_LENGTH)]; + uint16_t len; + uint16_t om_len; + int rc; + + om_len = OS_MBUF_PKTLEN(om); + if (om_len >= sizeof(name)) { + return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; + } + + rc = ble_hs_mbuf_to_flat(om, name, sizeof(name) - 1, &len); + if (rc != 0) { + return BLE_ATT_ERR_UNLIKELY; + } + + name[len] = '\0'; + + rc = bt_set_name(name); + if (rc) { + return BLE_ATT_ERR_INSUFFICIENT_RES; + } + + board_refresh_display(); + + return 0; +} + +static int +gatt_svr_chr_access(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, + void *arg) +{ + const ble_uuid_t *uuid; + int rc; + + uuid = ctxt->chr->uuid; + + if (ble_uuid_cmp(uuid, &name_enc_uuid.u) == 0) { + switch (ctxt->op) { + case BLE_GATT_ACCESS_OP_READ_CHR: + rc = read_name(ctxt->om); + return rc; + + case BLE_GATT_ACCESS_OP_WRITE_CHR: + rc = write_name(ctxt->om); + return rc; + + default: + assert(0); + return BLE_ATT_ERR_UNLIKELY; + } + } else if (ble_uuid_cmp(uuid, &gatt_cud_uuid.u) == 0) { + rc = os_mbuf_append(ctxt->om, "Badge Name", + (uint16_t) strlen("Badge Name")); + return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; + } else if (ble_uuid_cmp(uuid, &gatt_cpf_uuid.u) == 0) { + rc = os_mbuf_append(ctxt->om, &name_cpf, + (uint16_t) sizeof(name_cpf)); + return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; + } + + /* Unknown characteristic; the nimble stack should not have called this + * function. + */ + assert(0); + return BLE_ATT_ERR_UNLIKELY; +} + +void +gatt_svr_register_cb(struct ble_gatt_register_ctxt *ctxt, void *arg) +{ + char buf[BLE_UUID_STR_LEN]; + + switch (ctxt->op) { + case BLE_GATT_REGISTER_OP_SVC: + MODLOG_DFLT(DEBUG, "registered service %s with handle=%d\n", + ble_uuid_to_str(ctxt->svc.svc_def->uuid, buf), + ctxt->svc.handle); + break; + + case BLE_GATT_REGISTER_OP_CHR: + MODLOG_DFLT(DEBUG, "registering characteristic %s with " + "def_handle=%d val_handle=%d\n", + ble_uuid_to_str(ctxt->chr.chr_def->uuid, buf), + ctxt->chr.def_handle, + ctxt->chr.val_handle); + break; + + case BLE_GATT_REGISTER_OP_DSC: + MODLOG_DFLT(DEBUG, "registering descriptor %s with handle=%d\n", + ble_uuid_to_str(ctxt->dsc.dsc_def->uuid, buf), + ctxt->dsc.handle); + break; + + default: + assert(0); + break; + } +} + +int +gatt_svr_init(void) +{ + int rc; + + rc = ble_gatts_count_cfg(gatt_svr_svcs); + if (rc != 0) { + return rc; + } + + rc = ble_gatts_add_svcs(gatt_svr_svcs); + if (rc != 0) { + return rc; + } + + return 0; +} diff --git a/lib/bt/host/nimble/nimble/apps/mesh_badge/src/main.c b/lib/bt/host/nimble/nimble/apps/mesh_badge/src/main.c new file mode 100644 index 00000000..d856d816 --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/mesh_badge/src/main.c @@ -0,0 +1,393 @@ +/* + * Copyright (c) 2018 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "console/console.h" +#include "host/ble_gap.h" +#include "mesh/glue.h" +#include "services/gap/ble_svc_gap.h" +#include "base64/base64.h" + +#include "mesh_badge.h" +#include "mesh.h" +#include "board.h" + +static char badge_name[MYNEWT_VAL(BLE_SVC_GAP_DEVICE_NAME_MAX_LENGTH)]; + +#define MESH_BADGE_NAME_ENCODE_SIZE \ + BASE64_ENCODE_SIZE(sizeof(badge_name)) + +static bool reset_mesh; + +void print_addr(const void *addr) +{ + const uint8_t *u8p; + + u8p = addr; + MODLOG_DFLT(INFO, "%02x:%02x:%02x:%02x:%02x:%02x", + u8p[5], u8p[4], u8p[3], u8p[2], u8p[1], u8p[0]); +} + +static void +print_conn_desc(struct ble_gap_conn_desc *desc) +{ + MODLOG_DFLT(INFO, "handle=%d our_ota_addr_type=%d our_ota_addr=", + desc->conn_handle, desc->our_ota_addr.type); + print_addr(desc->our_ota_addr.val); + MODLOG_DFLT(INFO, " our_id_addr_type=%d our_id_addr=", + desc->our_id_addr.type); + print_addr(desc->our_id_addr.val); + MODLOG_DFLT(INFO, " peer_ota_addr_type=%d peer_ota_addr=", + desc->peer_ota_addr.type); + print_addr(desc->peer_ota_addr.val); + MODLOG_DFLT(INFO, " peer_id_addr_type=%d peer_id_addr=", + desc->peer_id_addr.type); + print_addr(desc->peer_id_addr.val); + MODLOG_DFLT(INFO, " conn_itvl=%d conn_latency=%d supervision_timeout=%d " + "encrypted=%d authenticated=%d bonded=%d\n", + desc->conn_itvl, desc->conn_latency, + desc->supervision_timeout, + desc->sec_state.encrypted, + desc->sec_state.authenticated, + desc->sec_state.bonded); +} + +static int gap_event(struct ble_gap_event *event, void *arg); + +static void advertise(void) +{ + uint8_t own_addr_type; + struct ble_gap_adv_params adv_params; + struct ble_hs_adv_fields fields; + const char *name; + int rc; + + /* Figure out address to use while advertising (no privacy for now) */ + rc = ble_hs_id_infer_auto(0, &own_addr_type); + if (rc != 0) { + MODLOG_DFLT(ERROR, "error determining address type; rc=%d\n", rc); + return; + } + + /** + * Set the advertisement data included in our advertisements: + * o Flags (indicates advertisement type and other general info). + * o Advertising tx power. + * o Device name. + * o 16-bit service UUIDs (alert notifications). + */ + + memset(&fields, 0, sizeof fields); + + /* Advertise two flags: + * o Discoverability in forthcoming advertisement (general) + * o BLE-only (BR/EDR unsupported). + */ + fields.flags = BLE_HS_ADV_F_DISC_GEN | + BLE_HS_ADV_F_BREDR_UNSUP; + +#if 0 + /* Indicate that the TX power level field should be included; have the + * stack fill this value automatically. This is done by assiging the + * special value BLE_HS_ADV_TX_PWR_LVL_AUTO. + */ + fields.tx_pwr_lvl_is_present = 1; + fields.tx_pwr_lvl = BLE_HS_ADV_TX_PWR_LVL_AUTO; +#endif + + name = ble_svc_gap_device_name(); + fields.name = (uint8_t *)name; + fields.name_len = (uint8_t) strlen(name); + fields.name_is_complete = 1; + + rc = ble_gap_adv_set_fields(&fields); + if (rc != 0) { + MODLOG_DFLT(ERROR, "error setting advertisement data; rc=%d\n", rc); + return; + } + + /* Begin advertising. */ + memset(&adv_params, 0, sizeof adv_params); + adv_params.conn_mode = BLE_GAP_CONN_MODE_UND; + adv_params.disc_mode = BLE_GAP_DISC_MODE_GEN; + rc = ble_gap_adv_start(own_addr_type, NULL, BLE_HS_FOREVER, + &adv_params, gap_event, NULL); + if (rc != 0) { + MODLOG_DFLT(ERROR, "error enabling advertisement; rc=%d\n", rc); + return; + } +} + +static void passkey_display(uint16_t conn_handle) +{ + char buf[20]; + struct ble_sm_io pk; + int rc; + + bt_rand(&pk.passkey, sizeof(pk.passkey)); + /* Max value is 999999 */ + pk.passkey %= 1000000; + pk.action = BLE_SM_IOACT_DISP; + + rc = ble_sm_inject_io(conn_handle, &pk); + assert(rc == 0); + + snprintk(buf, sizeof(buf), "Passkey:\n%06lu", pk.passkey); + + printk("%s\n", buf); + board_show_text(buf, false, K_FOREVER); +} + +static void pairing_complete(uint16_t conn_handle, bool bonded) +{ + printk("Pairing Complete\n"); + board_show_text("Pairing Complete", false, K_SECONDS(2)); +} + +static void pairing_failed(uint16_t conn_handle) +{ + printk("Pairing Failed\n"); + board_show_text("Pairing Failed", false, K_SECONDS(2)); +} + +static void connected(uint16_t conn_handle, int err) +{ + printk("Connected (err 0x%02x)\n", err); + + if (err) { + board_show_text("Connection failed", false, K_SECONDS(2)); + } else { + board_show_text("Connected", false, K_FOREVER); + } +} + +static void disconnected(uint16_t conn_handle, int reason) +{ + printk("Disconnected (reason 0x%02x)\n", reason); + + if (strcmp(MYNEWT_VAL(BLE_SVC_GAP_DEVICE_NAME), bt_get_name()) != 0 && + !mesh_is_initialized()) { + /* Mesh will take over advertising control */ + ble_gap_adv_stop(); + mesh_start(); + } else { + board_show_text("Disconnected", false, K_SECONDS(2)); + } +} + +static int gap_event(struct ble_gap_event *event, void *arg) +{ + struct ble_gap_conn_desc desc; + int rc; + + switch (event->type) { + case BLE_GAP_EVENT_CONNECT: + /* A new connection was established or a connection attempt failed. */ + MODLOG_DFLT(INFO, "connection %s; status=%d ", + event->connect.status == 0 ? "established" : "failed", + event->connect.status); + if (event->connect.status == 0) { + rc = ble_gap_conn_find(event->connect.conn_handle, &desc); + assert(rc == 0); + print_conn_desc(&desc); + connected(event->connect.conn_handle, + event->connect.status); + } + MODLOG_DFLT(INFO, "\n"); + + if (event->connect.status != 0) { + /* Connection failed; resume advertising. */ + advertise(); + } + return 0; + + case BLE_GAP_EVENT_DISCONNECT: + MODLOG_DFLT(INFO, "disconnect; reason=%d ", event->disconnect.reason); + print_conn_desc(&event->disconnect.conn); + MODLOG_DFLT(INFO, "\n"); + + /* Connection terminated; resume advertising. */ + advertise(); + + disconnected(event->disconnect.conn.conn_handle, + event->disconnect.reason); + return 0; + + case BLE_GAP_EVENT_CONN_UPDATE: + /* The central has updated the connection parameters. */ + MODLOG_DFLT(INFO, "connection updated; status=%d ", + event->conn_update.status); + rc = ble_gap_conn_find(event->connect.conn_handle, &desc); + assert(rc == 0); + print_conn_desc(&desc); + MODLOG_DFLT(INFO, "\n"); + return 0; + + case BLE_GAP_EVENT_ENC_CHANGE: + /* Encryption has been enabled or disabled for this connection. */ + MODLOG_DFLT(INFO, "encryption change event; status=%d ", + event->enc_change.status); + rc = ble_gap_conn_find(event->connect.conn_handle, &desc); + assert(rc == 0); + print_conn_desc(&desc); + MODLOG_DFLT(INFO, "\n"); + + if (desc.sec_state.bonded) { + pairing_complete(event->enc_change.conn_handle, true); + } else if(desc.sec_state.encrypted) { + pairing_complete(event->enc_change.conn_handle, false); + } else { + pairing_failed(event->enc_change.conn_handle); + } + return 0; + + case BLE_GAP_EVENT_PASSKEY_ACTION: + MODLOG_DFLT(INFO, "passkey action event; conn_handle=%d action=%d numcmp=%d\n", + event->passkey.conn_handle, + event->passkey.params.action, + event->passkey.params.numcmp); + passkey_display(event->passkey.conn_handle); + return 0; + + case BLE_GAP_EVENT_REPEAT_PAIRING: + /* We already have a bond with the peer, but it is attempting to + * establish a new secure link. This app sacrifices security for + * convenience: just throw away the old bond and accept the new link. + */ + + /* Delete the old bond. */ + rc = ble_gap_conn_find(event->repeat_pairing.conn_handle, &desc); + assert(rc == 0); + ble_store_util_delete_peer(&desc.peer_id_addr); + + /* Return BLE_GAP_REPEAT_PAIRING_RETRY to indicate that the host should + * continue with the pairing operation. + */ + return BLE_GAP_REPEAT_PAIRING_RETRY; + + } + + return 0; +} + +static void on_sync(void) +{ + int err; + ble_addr_t addr; + + /* Use NRPA */ + err = ble_hs_id_gen_rnd(1, &addr); + assert(err == 0); + err = ble_hs_id_set_rnd(addr.val); + assert(err == 0); + + printk("Bluetooth initialized\n"); + + err = mesh_init(addr.type); + if (err) { + printk("Initializing mesh failed (err %d)\n", err); + return; + } + + printk("Mesh initialized\n"); + + if (IS_ENABLED(CONFIG_SETTINGS)) { + settings_load(); + } + + if (reset_mesh) { + bt_mesh_reset(); + reset_mesh = false; + } + + if (!mesh_is_initialized()) { + advertise(); + } else { + printk("Already provisioned\n"); + ble_svc_gap_device_name_set(bt_get_name()); + } + + board_refresh_display(); + + printk("Board started\n"); +} + +void schedule_mesh_reset(void) +{ + reset_mesh = true; +} + +static void on_reset(int reason) +{ + MODLOG_DFLT(ERROR, "Resetting state; reason=%d\n", reason); +} + +const char *bt_get_name(void) +{ + char buf[MESH_BADGE_NAME_ENCODE_SIZE]; + int rc, len; + + rc = conf_get_stored_value("mesh_badge/badge_name", + buf, sizeof(buf)); + if (rc == OS_ENOENT) { + bt_set_name(MYNEWT_VAL(BLE_SVC_GAP_DEVICE_NAME)); + } else { + assert(rc == 0); + } + + memset(badge_name, '\0', sizeof(badge_name)); + len = base64_decode(buf, badge_name); + if (len < 0) { + bt_set_name(MYNEWT_VAL(BLE_SVC_GAP_DEVICE_NAME)); + } + + return badge_name; +} + +int bt_set_name(const char *name) +{ + char buf[MESH_BADGE_NAME_ENCODE_SIZE]; + int rc; + + memset(badge_name, '\0', sizeof(badge_name)); + memcpy(badge_name, name, strlen(name)); + base64_encode(badge_name, sizeof(badge_name), buf, 1); + rc = conf_save_one("mesh_badge/badge_name", buf); + assert(rc == 0); + + return 0; +} + +int main(void) +{ + int err; + + /* Initialize OS */ + sysinit(); + + err = board_init(); + if (err) { + printk("board init failed (err %d)\n", err); + assert(err == 0); + } + + /* Initialize the NimBLE host configuration. */ + ble_hs_cfg.reset_cb = on_reset; + ble_hs_cfg.sync_cb = on_sync; + ble_hs_cfg.gatts_register_cb = gatt_svr_register_cb; + ble_hs_cfg.store_status_cb = ble_store_util_status_rr; + ble_hs_cfg.sm_io_cap = BLE_SM_IO_CAP_DISP_ONLY; + + err = gatt_svr_init(); + assert(err == 0); + + /* + * As the last thing, process events from default event queue. + */ + while (1) { + os_eventq_run(os_eventq_dflt_get()); + } + return 0; +} diff --git a/lib/bt/host/nimble/nimble/apps/mesh_badge/src/mesh.c b/lib/bt/host/nimble/nimble/apps/mesh_badge/src/mesh.c new file mode 100644 index 00000000..ee999172 --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/mesh_badge/src/mesh.c @@ -0,0 +1,313 @@ +/* + * Copyright (c) 2018 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "console/console.h" +#include "mesh/mesh.h" + +#include "mesh_badge.h" +#include "mesh.h" +#include "board.h" + +#define BT_COMP_ID_LF 0x05f1 + +#define MOD_LF 0x0000 +#define OP_HELLO 0xbb +#define OP_HEARTBEAT 0xbc +#define OP_VND_HELLO BT_MESH_MODEL_OP_3(OP_HELLO, BT_COMP_ID_LF) +#define OP_VND_HEARTBEAT BT_MESH_MODEL_OP_3(OP_HEARTBEAT, BT_COMP_ID_LF) + +#define DEFAULT_TTL 31 +#define GROUP_ADDR 0xc123 +#define NET_IDX 0x000 +#define APP_IDX 0x000 +#define FLAGS 0 +static struct ble_npl_callout hello_work; +static struct ble_npl_callout mesh_start_work; + +static void heartbeat(const struct bt_mesh_hb_sub *sub, uint8_t hops, + uint16_t feat) +{ + board_show_text("Heartbeat Received", false, K_SECONDS(2)); +} + +static struct bt_mesh_cfg_cli cfg_cli = { +}; + +static void attention_on(struct bt_mesh_model *model) +{ + board_show_text("Attention!", false, K_SECONDS(2)); +} + +static void attention_off(struct bt_mesh_model *model) +{ + board_refresh_display(); +} + +static const struct bt_mesh_health_srv_cb health_srv_cb = { + .attn_on = attention_on, + .attn_off = attention_off, +}; + +static struct bt_mesh_health_srv health_srv = { + .cb = &health_srv_cb, +}; + +static struct os_mbuf *bt_mesh_pub_msg_health_pub; +static struct bt_mesh_model_pub health_pub; + +static struct bt_mesh_model root_models[] = { + BT_MESH_MODEL_CFG_SRV, + BT_MESH_MODEL_CFG_CLI(&cfg_cli), + BT_MESH_MODEL_HEALTH_SRV(&health_srv, &health_pub), +}; + +static void vnd_hello(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + char str[32]; + size_t len; + + printk("Hello message from 0x%04x\n", ctx->addr); + + if (ctx->addr == bt_mesh_model_elem(model)->addr) { + printk("Ignoring message from self\n"); + return; + } + + len = min(buf->om_len, 8); + memcpy(str, buf->om_data, len); + str[len] = '\0'; + + board_add_hello(ctx->addr, str); + + strcpy(str + len, " says hi!"); + + board_show_text(str, false, K_SECONDS(3)); + + board_blink_leds(); +} + +static void vnd_heartbeat(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + uint8_t init_ttl, hops; + + /* Ignore messages from self */ + if (ctx->addr == bt_mesh_model_elem(model)->addr) { + return; + } + + init_ttl = net_buf_simple_pull_u8(buf); + hops = init_ttl - ctx->recv_ttl + 1; + + printk("Heartbeat from 0x%04x over %u hop%s\n", ctx->addr, + hops, hops == 1 ? "" : "s"); + + board_add_heartbeat(ctx->addr, hops); +} + +static const struct bt_mesh_model_op vnd_ops[] = { + { OP_VND_HELLO, 1, vnd_hello }, + { OP_VND_HEARTBEAT, 1, vnd_heartbeat }, + BT_MESH_MODEL_OP_END, +}; + +static int pub_update(struct bt_mesh_model *mod) +{ + struct os_mbuf *msg = mod->pub->msg; + + printk("Preparing to send heartbeat\n"); + + bt_mesh_model_msg_init(msg, OP_VND_HEARTBEAT); + net_buf_simple_add_u8(msg, DEFAULT_TTL); + + return 0; +} + +static struct os_mbuf *bt_mesh_pub_msg_vnd_pub; +static struct bt_mesh_model_pub vnd_pub; + +static struct bt_mesh_model vnd_models[] = { + BT_MESH_MODEL_VND(BT_COMP_ID_LF, MOD_LF, vnd_ops, &vnd_pub, NULL), +}; + +static struct bt_mesh_elem elements[] = { + BT_MESH_ELEM(0, root_models, vnd_models), +}; + +static const struct bt_mesh_comp comp = { + .cid = BT_COMP_ID_LF, + .elem = elements, + .elem_count = ARRAY_SIZE(elements), +}; + +static size_t first_name_len(const char *name) +{ + size_t len; + + for (len = 0; *name; name++, len++) { + switch (*name) { + case ' ': + case ',': + case '\n': + return len; + } + } + + return len; +} + +static void send_hello(struct ble_npl_event *work) +{ + struct os_mbuf *msg = NET_BUF_SIMPLE(3 + 8 + 4); + struct bt_mesh_msg_ctx ctx = { + .net_idx = NET_IDX, + .app_idx = APP_IDX, + .addr = GROUP_ADDR, + .send_ttl = DEFAULT_TTL, + }; + const char *name = bt_get_name(); + + bt_mesh_model_msg_init(msg, OP_VND_HELLO); + net_buf_simple_add_mem(msg, name, first_name_len(name)); + + if (bt_mesh_model_send(&vnd_models[0], &ctx, msg, NULL, NULL) == 0) { + board_show_text("Saying \"hi!\" to everyone", false, + K_SECONDS(2)); + } else { + board_show_text("Sending Failed!", false, K_SECONDS(2)); + } + + os_mbuf_free_chain(msg); +} + +void mesh_send_hello(void) +{ + k_work_submit(&hello_work); +} + +static int provision_and_configure(void) +{ + static const uint8_t net_key[16] = { + 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, + 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, + }; + static const uint8_t app_key[16] = { + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + }; + static const uint16_t iv_index; + struct bt_mesh_cfg_mod_pub pub = { + .addr = GROUP_ADDR, + .app_idx = APP_IDX, + .ttl = DEFAULT_TTL, + .period = BT_MESH_PUB_PERIOD_SEC(10), + }; + uint8_t dev_key[16]; + uint16_t addr; + int err; + + err = bt_rand(dev_key, sizeof(dev_key)); + if (err) { + return err; + } + + do { + err = bt_rand(&addr, sizeof(addr)); + if (err) { + return err; + } + } while (!addr); + + /* Make sure it's a unicast address (highest bit unset) */ + addr &= ~0x8000; + + err = bt_mesh_provision(net_key, NET_IDX, FLAGS, iv_index, addr, + dev_key); + if (err) { + return err; + } + + printk("Configuring...\n"); + + /* Add Application Key */ + bt_mesh_cfg_app_key_add(NET_IDX, addr, NET_IDX, APP_IDX, app_key, NULL); + + /* Bind to vendor model */ + bt_mesh_cfg_mod_app_bind_vnd(NET_IDX, addr, addr, APP_IDX, + MOD_LF, BT_COMP_ID_LF, NULL); + + /* Bind to Health model */ + bt_mesh_cfg_mod_app_bind(NET_IDX, addr, addr, APP_IDX, + BT_MESH_MODEL_ID_HEALTH_SRV, NULL); + + /* Add model subscription */ + bt_mesh_cfg_mod_sub_add_vnd(NET_IDX, addr, addr, GROUP_ADDR, + MOD_LF, BT_COMP_ID_LF, NULL); + + bt_mesh_cfg_mod_pub_set_vnd(NET_IDX, addr, addr, MOD_LF, BT_COMP_ID_LF, + &pub, NULL); + + printk("Configuration complete\n"); + + return addr; +} + +static void start_mesh(struct ble_npl_event *work) +{ + int err; + + err = provision_and_configure(); + if (err < 0) { + board_show_text("Starting Mesh Failed", false, + K_SECONDS(2)); + } else { + char buf[32]; + + snprintk(buf, sizeof(buf), + "Mesh Started\nAddr: 0x%04x", err); + board_show_text(buf, false, K_SECONDS(4)); + } +} + +void mesh_start(void) +{ + k_work_submit(&mesh_start_work); +} + +bool mesh_is_initialized(void) +{ + return bt_mesh_is_provisioned(); +} + +uint16_t mesh_get_addr(void) +{ + return elements[0].addr; +} + +int mesh_init(uint8_t addr_type) +{ + static const uint8_t dev_uuid[16] = { 0xc0, 0xff, 0xee }; + static const struct bt_mesh_prov prov = { + .uuid = dev_uuid, + }; + + hb_cb = { .recv = heartbeat }; + + bt_mesh_pub_msg_health_pub = NET_BUF_SIMPLE(0); + health_pub.msg = bt_mesh_pub_msg_health_pub; + + bt_mesh_pub_msg_vnd_pub = NET_BUF_SIMPLE(3 + 1); + vnd_pub.msg = bt_mesh_pub_msg_vnd_pub; + vnd_pub.update = pub_update; + + k_work_init(&hello_work, send_hello); + k_work_init(&mesh_start_work, start_mesh); + + return bt_mesh_init(addr_type, &prov, &comp); +} diff --git a/lib/bt/host/nimble/nimble/apps/mesh_badge/src/mesh.h b/lib/bt/host/nimble/nimble/apps/mesh_badge/src/mesh.h new file mode 100644 index 00000000..33ee7cd2 --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/mesh_badge/src/mesh.h @@ -0,0 +1,14 @@ +/* + * Copyright (c) 2018 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "mesh/mesh.h" + +void mesh_send_hello(void); + +uint16_t mesh_get_addr(void); +bool mesh_is_initialized(void); +void mesh_start(void); +int mesh_init(uint8_t addr_type); diff --git a/lib/bt/host/nimble/nimble/apps/mesh_badge/src/mesh_badge.h b/lib/bt/host/nimble/nimble/apps/mesh_badge/src/mesh_badge.h new file mode 100644 index 00000000..a156f1ce --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/mesh_badge/src/mesh_badge.h @@ -0,0 +1,28 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +struct ble_gatt_register_ctxt; + +void gatt_svr_register_cb(struct ble_gatt_register_ctxt *ctxt, void *arg); +int gatt_svr_init(void); + +void schedule_mesh_reset(void); +const char *bt_get_name(void); +int bt_set_name(const char *); + diff --git a/lib/bt/host/nimble/nimble/apps/mesh_badge/src/reel_board.c b/lib/bt/host/nimble/nimble/apps/mesh_badge/src/reel_board.c new file mode 100644 index 00000000..5e5f6b40 --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/mesh_badge/src/reel_board.c @@ -0,0 +1,508 @@ +/* + * Copyright (c) 2018 Phytec Messtechnik GmbH + * Copyright (c) 2018 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "os/mynewt.h" +#include "bsp/bsp.h" +#include "console/console.h" +#include "hal/hal_flash.h" +#include "hal/hal_gpio.h" +#include "mesh/glue.h" +#include "services/gap/ble_svc_gap.h" + +#include "mesh_badge.h" +#include "display/cfb.h" +#include "mesh.h" +#include "board.h" + +#define printk console_printf + +enum font_size { + FONT_BIG = 0, + FONT_MEDIUM = 1, + FONT_SMALL = 2, +}; + +struct font_info { + uint8_t columns; +} fonts[] = { + [FONT_BIG] = { .columns = 12 }, + [FONT_MEDIUM] = { .columns = 16 }, + [FONT_SMALL] = { .columns = 25 }, +}; + +#define LONG_PRESS_TIMEOUT K_SECONDS(1) + +#define STAT_COUNT 128 + +#define EDGE (GPIO_INT_EDGE | GPIO_INT_DOUBLE_EDGE) + +#ifdef SW0_GPIO_FLAGS +#define PULL_UP SW0_GPIO_FLAGS +#else +#define PULL_UP 0 +#endif + +static struct os_dev *epd_dev; +static bool pressed; +static bool stats_view; +static struct k_work_delayable epd_work; +static struct k_work_delayable long_press_work; + +static struct { + int pin; +} leds[] = { + { .pin = LED_1, }, + { .pin = RGB_LED_RED, }, + { .pin = RGB_LED_GRN, }, + { .pin = RGB_LED_BLU, }, +}; + +struct k_work_delayable led_timer; + +static size_t print_line(enum font_size font_size, int row, const char *text, + size_t len, bool center) +{ + uint8_t font_height, font_width; + char line[fonts[FONT_SMALL].columns + 1]; + int pad; + + cfb_framebuffer_set_font(epd_dev, font_size); + + len = min(len, fonts[font_size].columns); + memcpy(line, text, len); + line[len] = '\0'; + + if (center) { + pad = (fonts[font_size].columns - len) / 2; + } else { + pad = 0; + } + + cfb_get_font_size(epd_dev, font_size, &font_width, &font_height); + + if (cfb_print(epd_dev, line, font_width * pad, font_height * row)) { + printk("Failed to print a string\n"); + } + + return len; +} + +static size_t get_len(enum font_size font, const char *text) +{ + const char *space = NULL; + size_t i; + + for (i = 0; i <= fonts[font].columns; i++) { + switch (text[i]) { + case '\n': + case '\0': + return i; + case ' ': + space = &text[i]; + break; + default: + continue; + } + } + + /* If we got more characters than fits a line, and a space was + * encountered, fall back to the last space. + */ + if (space) { + return space - text; + } + + return fonts[font].columns; +} + +void board_blink_leds(void) +{ + k_work_reschedule(&led_timer, K_MSEC(100)); +} + +void board_show_text(const char *text, bool center, int32_t duration) +{ + int i; + + cfb_framebuffer_clear(epd_dev, false); + + for (i = 0; i < 3; i++) { + size_t len; + + while (*text == ' ' || *text == '\n') { + text++; + } + + len = get_len(FONT_BIG, text); + if (!len) { + break; + } + + text += print_line(FONT_BIG, i, text, len, center); + if (!*text) { + break; + } + } + + cfb_framebuffer_finalize(epd_dev); + + if (duration != K_FOREVER) { + k_work_reschedule(&epd_work, duration); + } +} + +static struct stat { + uint16_t addr; + char name[9]; + uint8_t min_hops; + uint8_t max_hops; + uint16_t hello_count; + uint16_t heartbeat_count; +} stats[STAT_COUNT] = { + [0 ... (STAT_COUNT - 1)] = { + .min_hops = BT_MESH_TTL_MAX, + .max_hops = 0, + }, +}; + +static uint32_t stat_count; + +#define NO_UPDATE -1 + +static int add_hello(uint16_t addr, const char *name) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(stats); i++) { + struct stat *stat = &stats[i]; + + if (!stat->addr) { + stat->addr = addr; + strncpy(stat->name, name, sizeof(stat->name) - 1); + stat->hello_count = 1; + stat_count++; + return i; + } + + if (stat->addr == addr) { + /* Update name, incase it has changed */ + strncpy(stat->name, name, sizeof(stat->name) - 1); + + if (stat->hello_count < 0xffff) { + stat->hello_count++; + return i; + } + + return NO_UPDATE; + } + } + + return NO_UPDATE; +} + +static int add_heartbeat(uint16_t addr, uint8_t hops) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(stats); i++) { + struct stat *stat = &stats[i]; + + if (!stat->addr) { + stat->addr = addr; + stat->heartbeat_count = 1; + stat->min_hops = hops; + stat->max_hops = hops; + stat_count++; + return i; + } + + if (stat->addr == addr) { + if (hops < stat->min_hops) { + stat->min_hops = hops; + } else if (hops > stat->max_hops) { + stat->max_hops = hops; + } + + if (stat->heartbeat_count < 0xffff) { + stat->heartbeat_count++; + return i; + } + + return NO_UPDATE; + } + } + + return NO_UPDATE; +} + +void board_add_hello(uint16_t addr, const char *name) +{ + uint32_t sort_i; + + sort_i = add_hello(addr, name); + if (sort_i != NO_UPDATE) { + } +} + +void board_add_heartbeat(uint16_t addr, uint8_t hops) +{ + uint32_t sort_i; + + sort_i = add_heartbeat(addr, hops); + if (sort_i != NO_UPDATE) { + } +} + +static void show_statistics(void) +{ + int top[4] = { -1, -1, -1, -1 }; + int len, i, line = 0; + struct stat *stat; + char str[32]; + + cfb_framebuffer_clear(epd_dev, false); + + len = snprintk(str, sizeof(str), + "Own Address: 0x%04x", mesh_get_addr()); + print_line(FONT_SMALL, line++, str, len, false); + + len = snprintk(str, sizeof(str), + "Node Count: %lu", stat_count + 1); + print_line(FONT_SMALL, line++, str, len, false); + + /* Find the top sender */ + for (i = 0; i < ARRAY_SIZE(stats); i++) { + int j; + + stat = &stats[i]; + if (!stat->addr) { + break; + } + + if (!stat->hello_count) { + continue; + } + + for (j = 0; j < ARRAY_SIZE(top); j++) { + if (top[j] < 0) { + top[j] = i; + break; + } + + if (stat->hello_count <= stats[top[j]].hello_count) { + continue; + } + + /* Move other elements down the list */ + if (j < ARRAY_SIZE(top) - 1) { + memmove(&top[j + 1], &top[j], + ((ARRAY_SIZE(top) - j - 1) * + sizeof(top[j]))); + } + + top[j] = i; + break; + } + } + + if (stat_count >= 0) { + len = snprintk(str, sizeof(str), "Most messages from:"); + print_line(FONT_SMALL, line++, str, len, false); + + for (i = 0; i < ARRAY_SIZE(top); i++) { + if (top[i] < 0) { + break; + } + + stat = &stats[top[i]]; + + len = snprintk(str, sizeof(str), "%-3u 0x%04x %s", + stat->hello_count, stat->addr, + stat->name); + print_line(FONT_SMALL, line++, str, len, false); + } + } + + cfb_framebuffer_finalize(epd_dev); +} + +static void epd_update(struct ble_npl_event *work) +{ + char buf[MYNEWT_VAL(BLE_SVC_GAP_DEVICE_NAME_MAX_LENGTH)]; + int i; + + if (stats_view) { + show_statistics(); + return; + } + + strncpy(buf, bt_get_name(), sizeof(buf)); + + /* Convert commas to newlines */ + for (i = 0; buf[i] != '\0'; i++) { + if (buf[i] == ',') { + buf[i] = '\n'; + } + } + + board_show_text(buf, true, K_FOREVER); +} + +static void long_press(struct ble_npl_event *work) +{ + /* Treat as release so actual release doesn't send messages */ + pressed = false; + stats_view = !stats_view; + board_refresh_display(); +} + +static bool button_is_pressed(void) +{ + uint32_t val; + + val = (uint32_t) hal_gpio_read(BUTTON_1); + + return !val; +} + +static void button_interrupt(struct os_event *ev) +{ + int pin_pos = (int ) ev->ev_arg; + + if (button_is_pressed() == pressed) { + return; + } + + pressed = !pressed; + printk("Button %s\n", pressed ? "pressed" : "released"); + + if (pressed) { + k_work_reschedule(&long_press_work, LONG_PRESS_TIMEOUT); + return; + } + + k_work_cancel_delayable(&long_press_work); + + if (!mesh_is_initialized()) { + return; + } + + /* Short press does currently nothing in statistics view */ + if (stats_view) { + return; + } + + if (pin_pos == BUTTON_1) { + mesh_send_hello(); + } +} + +static struct os_event button_event; + +static void +gpio_irq_handler(void *arg) +{ + button_event.ev_arg = arg; + os_eventq_put(os_eventq_dflt_get(), &button_event); +} + +static int configure_button(void) +{ + button_event.ev_cb = button_interrupt; + + hal_gpio_irq_init(BUTTON_1, gpio_irq_handler, (void *) BUTTON_1, + HAL_GPIO_TRIG_BOTH, HAL_GPIO_PULL_UP); + hal_gpio_irq_enable(BUTTON_1); + + return 0; +} + +static void led_timeout(struct ble_npl_event *work) +{ + static int led_cntr; + int i; + + /* Disable all LEDs */ + for (i = 0; i < ARRAY_SIZE(leds); i++) { + hal_gpio_write(leds[i].pin, 1); + } + + /* Stop after 5 iterations */ + if (led_cntr > (ARRAY_SIZE(leds) * 5)) { + led_cntr = 0; + return; + } + + /* Select and enable current LED */ + i = led_cntr++ % ARRAY_SIZE(leds); + hal_gpio_write(leds[i].pin, 0); + + k_work_reschedule(&led_timer, K_MSEC(100)); +} + +static int configure_leds(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(leds); i++) { + hal_gpio_init_out(leds[i].pin, 1); + } + + k_work_init_delayable(&led_timer, led_timeout); + return 0; +} + +static int erase_storage(void) +{ + bt_set_name(MYNEWT_VAL(BLE_SVC_GAP_DEVICE_NAME)); + ble_store_clear(); + schedule_mesh_reset(); + return 0; +} + +void board_refresh_display(void) +{ + k_work_reschedule(&epd_work, K_NO_WAIT); +} + +int board_init(void) +{ + epd_dev = os_dev_lookup(MYNEWT_VAL(SSD1673_OS_DEV_NAME)); + if (epd_dev == NULL) { + printk("SSD1673 device not found\n"); + return -ENODEV; + } + + if (cfb_framebuffer_init(epd_dev)) { + printk("Framebuffer initialization failed\n"); + return -EIO; + } + + cfb_framebuffer_clear(epd_dev, true); + + if (configure_button()) { + printk("Failed to configure button\n"); + return -EIO; + } + + if (configure_leds()) { + printk("LED init failed\n"); + return -EIO; + } + + k_work_init_delayable(&epd_work, epd_update); + k_work_init_delayable(&long_press_work, long_press); + + pressed = button_is_pressed(); + if (pressed) { + printk("Erasing storage\n"); + board_show_text("Resetting Device", false, K_SECONDS(4)); + erase_storage(); + } + + return 0; +} diff --git a/lib/bt/host/nimble/nimble/apps/mesh_badge/syscfg.yml b/lib/bt/host/nimble/nimble/apps/mesh_badge/syscfg.yml new file mode 100644 index 00000000..2b8f457d --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/mesh_badge/syscfg.yml @@ -0,0 +1,61 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +# Package: apps/mesh_badge + +syscfg.vals: + # Enable the shell task. + SHELL_TASK: 1 + + # Set log level to info (disable debug logging). + LOG_LEVEL: 1 + + # Whether to save data to sys/config, or just keep it in RAM. + BLE_STORE_CONFIG_PERSIST: 0 + + # Default task settings + OS_MAIN_STACK_SIZE: 768 + + # Newtmgr is not supported in this app, so disable newtmgr-over-shell. + SHELL_NEWTMGR: 0 + + REEL_BOARD_ENABLE_ACTIVE_MODE: 1 + SPI_0_MASTER: 1 + + BLE_MESH: 1 + MSYS_1_BLOCK_COUNT: 48 + + BLE_SVC_GAP_DEVICE_NAME: '"reel board"' + + BLE_SM_SC: 1 + BLE_SM_BONDING: 1 + BLE_MESH_RELAY: 1 + BLE_MESH_GATT_PROXY: 0 + BLE_MESH_PB_ADV: 0 + BLE_MESH_PB_GATT: 0 + BLE_MESH_ADV_BUF_COUNT: 30 + BLE_MESH_LABEL_COUNT: 0 + BLE_MESH_CFG_CLI: 1 + BLE_MESH_TX_SEG_MAX: 6 + BLE_MESH_TX_SEG_MSG_COUNT: 3 + BLE_MESH_RX_SEG_MSG_COUNT: 3 + BLE_MESH_CRPL: 128 + BLE_MESH_RPL_STORE_TIMEOUT: 120 + BLE_MESH_SETTINGS: 1 + CONFIG_NFFS: 1 + BLE_MESH_PB_ADV: 1 \ No newline at end of file diff --git a/lib/bt/host/nimble/nimble/apps/peripheral/pkg.yml b/lib/bt/host/nimble/nimble/apps/peripheral/pkg.yml new file mode 100644 index 00000000..d5c862c8 --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/peripheral/pkg.yml @@ -0,0 +1,35 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +pkg.name: "apps/peripheral" +pkg.type: app +pkg.description: "Basic perihperal application" +pkg.author: "Krzysztof Kopyściński krzysztof.kopyscinski@codecoup.pl" + + +pkg.deps: + - "@apache-mynewt-core/kernel/os" + - "@apache-mynewt-core/sys/console/full" + - "@apache-mynewt-core/sys/log/full" + - "@apache-mynewt-core/sys/stats/full" + - "@apache-mynewt-core/sys/log/modlog" + - "@apache-mynewt-nimble/nimble/host" + - "@apache-mynewt-nimble/nimble/host/util/" + - "@apache-mynewt-nimble/nimble/host/services/gap" + - "@apache-mynewt-nimble/nimble/host/store/config" diff --git a/lib/bt/host/nimble/nimble/apps/peripheral/src/main.c b/lib/bt/host/nimble/nimble/apps/peripheral/src/main.c new file mode 100755 index 00000000..f22579a7 --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/peripheral/src/main.c @@ -0,0 +1,166 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#include +#include +#include + +#include "os/os.h" +#include "sysinit/sysinit.h" +#include "log/log.h" +#include "host/ble_hs.h" +#include "host/util/util.h" +#include "services/gap/ble_svc_gap.h" + +static uint8_t g_own_addr_type; +static uint16_t conn_handle; +static const char *device_name = "Mynewt"; + +/* adv_event() calls advertise(), so forward declaration is required */ +static void advertise(void); + +static int +adv_event(struct ble_gap_event *event, void *arg) +{ + switch (event->type) { + case BLE_GAP_EVENT_ADV_COMPLETE: + MODLOG_DFLT(INFO,"Advertising completed, termination code: %d\n", + event->adv_complete.reason); + advertise(); + break; + case BLE_GAP_EVENT_CONNECT: + assert(event->connect.status == 0); + MODLOG_DFLT(INFO, "connection %s; status=%d\n", + event->connect.status == 0 ? "established" : "failed", + event->connect.status); + break; + case BLE_GAP_EVENT_CONN_UPDATE_REQ: + /* connected device requests update of connection parameters, + and these are being filled in - NULL sets default values */ + MODLOG_DFLT(INFO, "updating conncetion parameters...\n"); + event->conn_update_req.conn_handle = conn_handle; + event->conn_update_req.peer_params = NULL; + MODLOG_DFLT(INFO, "connection parameters updated!\n"); + break; + case BLE_GAP_EVENT_DISCONNECT: + MODLOG_DFLT(INFO, "disconnect; reason=%d\n", + event->disconnect.reason); + + /* reset conn_handle */ + conn_handle = BLE_HS_CONN_HANDLE_NONE; + + /* Connection terminated; resume advertising */ + advertise(); + break; + default: + MODLOG_DFLT(ERROR, "Advertising event not handled," + "event code: %u\n", event->type); + break; + } + return 0; +} + +static void +advertise(void) +{ + int rc; + + /* set adv parameters */ + struct ble_gap_adv_params adv_params; + struct ble_hs_adv_fields fields; + /* advertising payload is split into advertising data and advertising + response, because all data cannot fit into single packet; name of device + is sent as response to scan request */ + struct ble_hs_adv_fields rsp_fields; + + /* fill all fields and parameters with zeros */ + memset(&adv_params, 0, sizeof(adv_params)); + memset(&fields, 0, sizeof(fields)); + memset(&rsp_fields, 0, sizeof(rsp_fields)); + + adv_params.conn_mode = BLE_GAP_CONN_MODE_UND; + adv_params.disc_mode = BLE_GAP_DISC_MODE_GEN; + + fields.flags = BLE_HS_ADV_F_DISC_GEN | + BLE_HS_ADV_F_BREDR_UNSUP; + fields.uuids128 = BLE_UUID128(BLE_UUID128_DECLARE( + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff)); + fields.num_uuids128 = 1; + fields.uuids128_is_complete = 0;; + fields.tx_pwr_lvl = BLE_HS_ADV_TX_PWR_LVL_AUTO; + + rsp_fields.name = (uint8_t *)device_name; + rsp_fields.name_len = strlen(device_name); + rsp_fields.name_is_complete = 1; + + rc = ble_gap_adv_set_fields(&fields); + assert(rc == 0); + + rc = ble_gap_adv_rsp_set_fields(&rsp_fields); + + MODLOG_DFLT(INFO,"Starting advertising...\n"); + + rc = ble_gap_adv_start(g_own_addr_type, NULL, 100, + &adv_params, adv_event, NULL); + assert(rc == 0); +} + +static void +on_sync(void) +{ + int rc; + + /* g_own_addr_type will store type of addres our BSP uses */ + rc = ble_hs_util_ensure_addr(0); + assert(rc == 0); + rc = ble_hs_id_infer_auto(0, &g_own_addr_type); + assert(rc == 0); + /* begin advertising */ + advertise(); +} + +static void +on_reset(int reason) +{ + MODLOG_DFLT(INFO, "Resetting state; reason=%d\n", reason); +} + +int +main(int argc, char **argv) +{ + int rc; + + /* Initialize all packages. */ + sysinit(); + + ble_hs_cfg.sync_cb = on_sync; + ble_hs_cfg.reset_cb = on_reset; + + rc = ble_svc_gap_device_name_set(device_name); + assert(rc == 0); + + /* As the last thing, process events from default event queue. */ + while (1) { + os_eventq_run(os_eventq_dflt_get()); + } + + return 0; +} diff --git a/lib/bt/host/nimble/nimble/apps/peripheral/syscfg.yml b/lib/bt/host/nimble/nimble/apps/peripheral/syscfg.yml new file mode 100644 index 00000000..0b954231 --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/peripheral/syscfg.yml @@ -0,0 +1,26 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + + +syscfg.vals: + BLE_ROLE_BROADCASTER: 1 + BLE_ROLE_CENTRAL: 0 + BLE_ROLE_OBSERVER: 0 + BLE_ROLE_PERIPHERAL: 1 + + # Whether to save data to sys/config, or just keep it in RAM. + BLE_STORE_CONFIG_PERSIST: 0 diff --git a/lib/bt/host/nimble/nimble/apps/scanner/pkg.yml b/lib/bt/host/nimble/nimble/apps/scanner/pkg.yml new file mode 100644 index 00000000..442ab9db --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/scanner/pkg.yml @@ -0,0 +1,34 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +pkg.name: "apps/scanner" +pkg.type: app +pkg.description: "Basic scanning application" +pkg.author: "Krzysztof Kopyściński " + + +pkg.deps: + - "@apache-mynewt-core/kernel/os" + - "@apache-mynewt-core/sys/console/full" + - "@apache-mynewt-core/sys/log/full" + - "@apache-mynewt-core/sys/stats/full" + - "@apache-mynewt-core/sys/log/modlog" + - "@apache-mynewt-nimble/nimble/host" + - "@apache-mynewt-nimble/nimble/host/util/" + - "@apache-mynewt-nimble/nimble/host/store/config" diff --git a/lib/bt/host/nimble/nimble/apps/scanner/src/main.c b/lib/bt/host/nimble/nimble/apps/scanner/src/main.c new file mode 100644 index 00000000..5158d0d5 --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/scanner/src/main.c @@ -0,0 +1,260 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "sysinit/sysinit.h" +#include "os/os.h" +#include "console/console.h" +#include "host/ble_hs.h" +#include "host/util/util.h" +#include "log/log.h" + +/* scan_event() calls scan(), so forward declaration is required */ +static void scan(void); + +static void +ble_app_set_addr(void) +{ + ble_addr_t addr; + int rc; + + /* generate new non-resolvable private address */ + rc = ble_hs_id_gen_rnd(1, &addr); + assert(rc == 0); + + /* set generated address */ + rc = ble_hs_id_set_rnd(addr.val); + assert(rc == 0); +} + +static void +print_uuid(const ble_uuid_t *uuid) +{ + char buf[BLE_UUID_STR_LEN]; + + MODLOG_DFLT(DEBUG, "%s", ble_uuid_to_str(uuid, buf)); +} + +/* Utility function to log an array of bytes. */ +static void +print_bytes(const uint8_t *bytes, int len) +{ + int i; + + for (i = 0; i < len; i++) { + MODLOG_DFLT(DEBUG, "%s0x%02x", i != 0 ? ":" : "", bytes[i]); + } +} + +static char * +addr_str(const void *addr) +{ + static char buf[6 * 2 + 5 + 1]; + const uint8_t *u8p; + + u8p = addr; + sprintf(buf, "%02x:%02x:%02x:%02x:%02x:%02x", + u8p[5], u8p[4], u8p[3], u8p[2], u8p[1], u8p[0]); + + return buf; +} + +static void +print_adv_fields(const struct ble_hs_adv_fields *fields) +{ + char s[BLE_HS_ADV_MAX_SZ]; + const uint8_t *u8p; + int i; + + if (fields->flags != 0) { + MODLOG_DFLT(DEBUG, " flags=0x%02x\n", fields->flags); + } + + if (fields->uuids16 != NULL) { + MODLOG_DFLT(DEBUG, " uuids16(%scomplete)=", + fields->uuids16_is_complete ? "" : "in"); + for (i = 0; i < fields->num_uuids16; i++) { + print_uuid(&fields->uuids16[i].u); + MODLOG_DFLT(DEBUG, " "); + } + MODLOG_DFLT(DEBUG, "\n"); + } + + if (fields->uuids32 != NULL) { + MODLOG_DFLT(DEBUG, " uuids32(%scomplete)=", + fields->uuids32_is_complete ? "" : "in"); + for (i = 0; i < fields->num_uuids32; i++) { + print_uuid(&fields->uuids32[i].u); + MODLOG_DFLT(DEBUG, " "); + } + MODLOG_DFLT(DEBUG, "\n"); + } + + if (fields->uuids128 != NULL) { + MODLOG_DFLT(DEBUG, " uuids128(%scomplete)=", + fields->uuids128_is_complete ? "" : "in"); + for (i = 0; i < fields->num_uuids128; i++) { + print_uuid(&fields->uuids128[i].u); + MODLOG_DFLT(DEBUG, " "); + } + MODLOG_DFLT(DEBUG, "\n"); + } + + if (fields->name != NULL) { + assert(fields->name_len < sizeof s - 1); + memcpy(s, fields->name, fields->name_len); + s[fields->name_len] = '\0'; + MODLOG_DFLT(DEBUG, " name(%scomplete)=%s\n", + fields->name_is_complete ? "" : "in", s); + } + + if (fields->tx_pwr_lvl_is_present) { + MODLOG_DFLT(DEBUG, " tx_pwr_lvl=%d\n", fields->tx_pwr_lvl); + } + + if (fields->slave_itvl_range != NULL) { + MODLOG_DFLT(DEBUG, " slave_itvl_range="); + print_bytes(fields->slave_itvl_range, BLE_HS_ADV_SLAVE_ITVL_RANGE_LEN); + MODLOG_DFLT(DEBUG, "\n"); + } + + if (fields->svc_data_uuid16 != NULL) { + MODLOG_DFLT(DEBUG, " svc_data_uuid16="); + print_bytes(fields->svc_data_uuid16, fields->svc_data_uuid16_len); + MODLOG_DFLT(DEBUG, "\n"); + } + + if (fields->public_tgt_addr != NULL) { + MODLOG_DFLT(DEBUG, " public_tgt_addr="); + u8p = fields->public_tgt_addr; + for (i = 0; i < fields->num_public_tgt_addrs; i++) { + MODLOG_DFLT(DEBUG, "public_tgt_addr=%s ", addr_str(u8p)); + u8p += BLE_HS_ADV_PUBLIC_TGT_ADDR_ENTRY_LEN; + } + MODLOG_DFLT(DEBUG, "\n"); + } + + if (fields->appearance_is_present) { + MODLOG_DFLT(DEBUG, " appearance=0x%04x\n", fields->appearance); + } + + if (fields->adv_itvl_is_present) { + MODLOG_DFLT(DEBUG, " adv_itvl=0x%04x\n", fields->adv_itvl); + } + + if (fields->svc_data_uuid32 != NULL) { + MODLOG_DFLT(DEBUG, " svc_data_uuid32="); + print_bytes(fields->svc_data_uuid32, fields->svc_data_uuid32_len); + MODLOG_DFLT(DEBUG, "\n"); + } + + if (fields->svc_data_uuid128 != NULL) { + MODLOG_DFLT(DEBUG, " svc_data_uuid128="); + print_bytes(fields->svc_data_uuid128, fields->svc_data_uuid128_len); + MODLOG_DFLT(DEBUG, "\n"); + } + + if (fields->uri != NULL) { + MODLOG_DFLT(DEBUG, " uri="); + print_bytes(fields->uri, fields->uri_len); + MODLOG_DFLT(DEBUG, "\n"); + } + + if (fields->mfg_data != NULL) { + MODLOG_DFLT(DEBUG, " mfg_data="); + print_bytes(fields->mfg_data, fields->mfg_data_len); + MODLOG_DFLT(DEBUG, "\n"); + } +} + +static int +scan_event(struct ble_gap_event *event, void *arg) +{ + struct ble_hs_adv_fields fields; + int rc; + switch (event->type) { + /* advertising report has been received during discovery procedure */ + case BLE_GAP_EVENT_DISC: + MODLOG_DFLT(ERROR, "Advertising report received!\n"); + rc = ble_hs_adv_parse_fields(&fields, event->disc.data, + event->disc.length_data); + if (rc != 0) { + return 0; + } + print_adv_fields(&fields); + return 0; + /* discovery procedure has terminated */ + case BLE_GAP_EVENT_DISC_COMPLETE: + MODLOG_DFLT(INFO, "Discovery completed, terminaton code: %d\n", + event->disc_complete.reason); + scan(); + return 0; + default: + MODLOG_DFLT(ERROR, "Discovery event not handled\n"); + return 0; + } +} + +static void +scan(void) +{ + /* set scan parameters */ + struct ble_gap_disc_params scan_params; + scan_params.itvl = 500; + scan_params.window = 250; + scan_params.filter_policy = 0; + scan_params.limited = 0; + scan_params.passive = 1; + scan_params.filter_duplicates = 1; + /* performs discovery procedure; value of own_addr_type is hard-coded, + because NRPA is used */ + ble_gap_disc(BLE_OWN_ADDR_RANDOM, 1000, &scan_params, scan_event, NULL); +} + +static void +on_sync(void) +{ + /* Generate a non-resolvable private address. */ + ble_app_set_addr(); + + /* begin scanning */ + scan(); +} + +static void +on_reset(int reason) +{ + console_printf("Resetting state; reason=%d\n", reason); +} + +int +main(int argc, char **argv) +{ + /* Initialize all packages. */ + sysinit(); + + ble_hs_cfg.sync_cb = on_sync; + ble_hs_cfg.reset_cb = on_reset; + + /* As the last thing, process events from default event queue. */ + while (1) { + os_eventq_run(os_eventq_dflt_get()); + } + + return 0; +} diff --git a/lib/bt/host/nimble/nimble/apps/scanner/syscfg.yml b/lib/bt/host/nimble/nimble/apps/scanner/syscfg.yml new file mode 100644 index 00000000..568a2c6b --- /dev/null +++ b/lib/bt/host/nimble/nimble/apps/scanner/syscfg.yml @@ -0,0 +1,26 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + + +syscfg.vals: + BLE_ROLE_BROADCASTER: 0 + BLE_ROLE_CENTRAL: 0 + BLE_ROLE_OBSERVER: 1 + BLE_ROLE_PERIPHERAL: 0 + + # Whether to save data to sys/config, or just keep it in RAM. + BLE_STORE_CONFIG_PERSIST: 0 diff --git a/lib/bt/host/nimble/nimble/babblesim/README.md b/lib/bt/host/nimble/nimble/babblesim/README.md new file mode 100644 index 00000000..c9ba3ef3 --- /dev/null +++ b/lib/bt/host/nimble/nimble/babblesim/README.md @@ -0,0 +1 @@ +BabbleSim support for Apache NimBLE diff --git a/lib/bt/host/nimble/nimble/babblesim/core/include/argparse.h b/lib/bt/host/nimble/nimble/babblesim/core/include/argparse.h new file mode 100644 index 00000000..7f98b028 --- /dev/null +++ b/lib/bt/host/nimble/nimble/babblesim/core/include/argparse.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2017 Oticon A/S + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef BSIM_NRF_ARGS_H +#define BSIM_NRF_ARGS_H + +#include +#include "NRF_hw_args.h" +#include "bs_cmd_line.h" +#include "bs_cmd_line_typical.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct nrf52_bsim_args_t { + BS_BASIC_DEVICE_OPTIONS_FIELDS + nrf_hw_sub_args_t nrf_hw; +}; + +struct nrf52_bsim_args_t *nrfbsim_argsparse(int argc, char *argv[]); +void nrfbsim_register_args(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/bt/host/nimble/nimble/babblesim/core/include/cmsis.h b/lib/bt/host/nimble/nimble/babblesim/core/include/cmsis.h new file mode 100644 index 00000000..8f9a6a3d --- /dev/null +++ b/lib/bt/host/nimble/nimble/babblesim/core/include/cmsis.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2020 Oticon A/S + * Copyright (c) 2021 Codecoup + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * This header defines replacements for inline + * ARM Cortex-M CMSIS intrinsics. + */ + +#ifndef BOARDS_POSIX_NRF52_BSIM_CMSIS_H +#define BOARDS_POSIX_NRF52_BSIM_CMSIS_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* Implement the following ARM intrinsics as no-op: + * - ARM Data Synchronization Barrier + * - ARM Data Memory Synchronization Barrier + * - ARM Instruction Synchronization Barrier + * - ARM No Operation + */ +#ifndef __DMB +#define __DMB() +#endif + +#ifndef __DSB +#define __DSB() +#endif + +#ifndef __ISB +#define __ISB() +#endif + +#ifndef __NOP +#define __NOP() +#endif + +void NVIC_SystemReset(void); +void __disable_irq(void); +void __enable_irq(void); +uint32_t __get_PRIMASK(void); + +#ifdef __cplusplus +} +#endif + +#endif /* BOARDS_POSIX_NRF52_BSIM_CMSIS_H */ diff --git a/lib/bt/host/nimble/nimble/babblesim/core/include/core_cm4.h b/lib/bt/host/nimble/nimble/babblesim/core/include/core_cm4.h new file mode 100644 index 00000000..ef8f9c3f --- /dev/null +++ b/lib/bt/host/nimble/nimble/babblesim/core/include/core_cm4.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2020 Oticon A/S + * Copyright (c) 2021 Codecoup + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef _BSIM_CORE_CM4_H +#define _BSIM_CORE_CM4_H + +#include + +/* Include the original ext_NRF52_hw_models core_cm4.h */ +#include <../HW_models/core_cm4.h> + +/* Add missing function definitions */ +extern void NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority); +extern void NVIC_EnableIRQ(IRQn_Type IRQn); +extern void NVIC_DisableIRQ(IRQn_Type IRQn); + +void __WFI(void); + +#ifndef __REV +#define __REV __builtin_bswap32 +#endif + +#endif diff --git a/lib/bt/host/nimble/nimble/babblesim/core/include/time_machine.h b/lib/bt/host/nimble/nimble/babblesim/core/include/time_machine.h new file mode 100644 index 00000000..da28d013 --- /dev/null +++ b/lib/bt/host/nimble/nimble/babblesim/core/include/time_machine.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2017 Oticon A/S + * Copyright (c) 2021 Codecoup + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef _TIME_MACHINE_H +#define _TIME_MACHINE_H + +#include "bs_types.h" + +#ifdef __cplusplus +extern "C"{ +#endif + +extern bs_time_t now; +bs_time_t tm_get_abs_time(void); +bs_time_t tm_get_hw_time(void); +bs_time_t tm_hw_time_to_abs_time(bs_time_t hwtime); +bs_time_t tm_abs_time_to_hw_time(bs_time_t abstime); + +void tm_reset_hw_times(void); + +void tm_find_next_timer_to_trigger(void); +bs_time_t tm_get_next_timer_abstime(void); + +void tm_update_last_phy_sync_time(bs_time_t abs_time); + +void tm_set_phy_max_resync_offset(bs_time_t offset_in_us); + +void tm_run_forever(void); + +void tm_sleep_until_hw_time(bs_time_t hw_time); + +void tm_sleep_until_abs_time(bs_time_t time); + +void tm_start(void); + +void tm_tick(void); + +void tm_tick_limited(bs_time_t max_time_diff); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/bt/host/nimble/nimble/babblesim/core/pkg.yml b/lib/bt/host/nimble/nimble/babblesim/core/pkg.yml new file mode 100644 index 00000000..1a1ec766 --- /dev/null +++ b/lib/bt/host/nimble/nimble/babblesim/core/pkg.yml @@ -0,0 +1,32 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +pkg.name: babblesim/core +pkg.description: time machine, irq handeler, core +pkg.author: "Apache Mynewt " +pkg.homepage: "http://mynewt.apache.org/" +pkg.keywords: + +pkg.deps: + - "babblesim/sdk" + +pkg.cflags: -std=gnu99 + +pkg.init: + bsim_start: 9999 diff --git a/lib/bt/host/nimble/nimble/babblesim/core/src/argparse.c b/lib/bt/host/nimble/nimble/babblesim/core/src/argparse.c new file mode 100644 index 00000000..c1b6cdaf --- /dev/null +++ b/lib/bt/host/nimble/nimble/babblesim/core/src/argparse.c @@ -0,0 +1,179 @@ +/* + * Copyright (c) 2017 Oticon A/S + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include +#include +#include "bs_tracing.h" +#include "bs_oswrap.h" +#include "bs_dump_files.h" +#include "argparse.h" +#include "NRF_hw_args.h" +#include "bs_cmd_line.h" +#include "bs_dynargs.h" +#include "bs_cmd_line_typical.h" +#include "NRF_HWLowL.h" +#include "controller/ble_ll.h" + +static bs_args_struct_t *args_struct; +static struct nrf52_bsim_args_t arg; +const char *bogus_sim_id = "bogus"; + +static void cmd_trace_lvl_found(char *argv, int offset) +{ + bs_trace_set_level(arg.verb); +} + +static void cmd_gdev_nbr_found(char *argv, int offset) +{ + bs_trace_set_prefix_dev(arg.global_device_nbr); +} + +static bool nosim; +static void cmd_nosim_found(char *argv, int offset) +{ + hwll_set_nosim(true); +} + +static void cmd_bdaddr_found(char *argv, int offset) +{ + union { + uint64_t u64; + uint8_t u8[8]; + } bdaddr; + char *endptr; + + if (sscanf(&argv[offset], "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx", + &bdaddr.u8[5], &bdaddr.u8[4], &bdaddr.u8[3], &bdaddr.u8[2], + &bdaddr.u8[1], &bdaddr.u8[0]) < 6) { + bdaddr.u64 = strtoull(&argv[offset], &endptr, 0); + if (*endptr) { + return; + } + + bdaddr.u64 = htole64(bdaddr.u64); + } + + ble_ll_set_public_addr(bdaddr.u8); +} + +static void print_no_sim_warning(void) +{ + bs_trace_warning("Neither simulation id or the device number " + "have been set. I assume you want to run " + "without a BabbleSim phy (-nosim)\n"); + bs_trace_warning("If this is not what you wanted, check with " + "--help how to set them\n"); + bs_trace_raw(3, "setting sim_id to 'bogus', device number to 0 " + "and nosim\n"); +} + +void nrfbsim_register_args(void) +{ +#define args (&arg) + /* This define is quite ugly, but allows reusing the definitions + * provided by the utils library */ + static bs_args_struct_t args_struct_toadd[] = { + ARG_TABLE_S_ID, + ARG_TABLE_P_ID_2G4, + ARG_TABLE_DEV_NBR, + ARG_TABLE_GDEV_NBR, + ARG_TABLE_VERB, + ARG_TABLE_SEED, + ARG_TABLE_COLOR, + ARG_TABLE_NOCOLOR, + ARG_TABLE_FORCECOLOR, + _NRF_HW_SUB_CMD_ARG_STRUCT, + /* + * Fields: + * manual, mandatory, switch, + * option_name, var_name, type, + * destination, callback, + * description + */ + { false, false , false, + "A", "bdaddr", 's', + NULL, cmd_bdaddr_found, "Device public address"}, + {false, false, true, + "nosim", "", 'b', + (void *)&nosim, cmd_nosim_found, + "(debug feature) Do not connect to the phy"}, + BS_DUMP_FILES_ARGS, + {true, false, false, + "argsmain", "arg", 'l', + NULL, NULL, + "The arguments that follow will be passed to main (default)"}, + ARG_TABLE_ENDMARKER + }; +#undef args + + bs_add_dynargs(&args_struct, args_struct_toadd); +} + +/** + * Check the arguments provided in the command line: set args based on it or + * defaults, and check they are correct + */ +struct nrf52_bsim_args_t *nrfbsim_argsparse(int argc, char *argv[]) +{ + bs_args_set_defaults(args_struct); + arg.verb = 2; + bs_trace_set_level(arg.verb); + nrf_hw_sub_cmline_set_defaults(&arg.nrf_hw); + static const char default_phy[] = "2G4"; + + for (int i = 1; i < argc; i++) { + if (bs_is_option(argv[i], "argsmain", 0)) { + continue; + } + + if (!bs_args_parse_one_arg(argv[i], args_struct)) { + bs_args_print_switches_help(args_struct); + bs_trace_error_line("Incorrect option %s\n", + argv[i]); + } + } + + /** + * If the user did not set the simulation id or device number + * we assume he wanted to run with nosim (but warn him) + */ + if ((!nosim) && (arg.s_id == NULL) && (arg.device_nbr == UINT_MAX)) { + print_no_sim_warning(); + nosim = true; + hwll_set_nosim(true); + } + if (nosim) { + if (arg.s_id == NULL) { + arg.s_id = (char *)bogus_sim_id; + } + if (arg.device_nbr == UINT_MAX) { + arg.device_nbr = 0; + } + } + + if (arg.device_nbr == UINT_MAX) { + bs_args_print_switches_help(args_struct); + bs_trace_error_line("The command line option " + "needs to be set\n"); + } + if (arg.global_device_nbr == UINT_MAX) { + arg.global_device_nbr = arg.device_nbr; + bs_trace_set_prefix_dev(arg.global_device_nbr); + } + if (!arg.s_id) { + bs_args_print_switches_help(args_struct); + bs_trace_error_line("The command line option " + "needs to be set\n"); + } + if (!arg.p_id) { + arg.p_id = (char *)default_phy; + } + + if (arg.rseed == UINT_MAX) { + arg.rseed = 0x1000 + arg.device_nbr; + } + return &arg; +} diff --git a/lib/bt/host/nimble/nimble/babblesim/core/src/cmsis.c b/lib/bt/host/nimble/nimble/babblesim/core/src/cmsis.c new file mode 100644 index 00000000..9beb3290 --- /dev/null +++ b/lib/bt/host/nimble/nimble/babblesim/core/src/cmsis.c @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2020 Nordic Semiconductor ASA + * Copyright (c) 2020 Oticon A/S + * Copyright (c) 2021 Codecoup + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "irq_ctrl.h" + +#include "irq_sources.h" +#include +#include "cmsis.h" +#include "os/sim.h" + +#include +#include + +extern void (* systemVectors[256])(void); + +/* + * Replacement for ARMs NVIC functions() + */ +void NVIC_SetPendingIRQ(IRQn_Type IRQn) +{ + hw_irq_ctrl_raise_im_from_sw(IRQn); +} + +void NVIC_ClearPendingIRQ(IRQn_Type IRQn) +{ + hw_irq_ctrl_clear_irq(IRQn); +} + +void NVIC_DisableIRQ(IRQn_Type IRQn) +{ + hw_irq_ctrl_disable_irq(IRQn); +} + +void NVIC_EnableIRQ(IRQn_Type IRQn) +{ + hw_irq_ctrl_enable_irq(IRQn); +} + +void NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority) +{ + hw_irq_ctrl_prio_set(IRQn, priority); +} + +uint32_t NVIC_GetPriority(IRQn_Type IRQn) +{ + return hw_irq_ctrl_get_prio(IRQn); +} + +void NVIC_SystemReset(void) +{ + inner_main_clean_up(1); +} + +/* + * Replacements for some other CMSIS functions + */ +void __enable_irq(void) +{ + hw_irq_ctrl_change_lock(false); +} + +void __disable_irq(void) +{ + hw_irq_ctrl_change_lock(true); +} + +uint32_t __get_PRIMASK(void) +{ + return hw_irq_ctrl_get_current_lock(); +} + +void NVIC_SetVector(IRQn_Type IRQn, uint32_t vector) +{ + systemVectors[(int32_t)IRQn + 16] = (void(*)(void))vector; +} diff --git a/lib/bt/host/nimble/nimble/babblesim/core/src/irq_handler.c b/lib/bt/host/nimble/nimble/babblesim/core/src/irq_handler.c new file mode 100644 index 00000000..f72aaf24 --- /dev/null +++ b/lib/bt/host/nimble/nimble/babblesim/core/src/irq_handler.c @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2017 Oticon A/S + * + * SPDX-License-Identifier: Apache-2.0 + * + * SW side of the IRQ handling + */ + +#include +#include "irq_ctrl.h" +#include "irq_sources.h" +#include "os/sim.h" + +static int currently_running_irq = -1; +extern void (* const systemVectors[256])(void); + +/** + * When an interrupt is raised, this function is called to handle it and, if + * needed, swap to a re-enabled thread + * + * Note that even that this function is executing in a Zephyr thread, it is + * effectively the model of the interrupt controller passing context to the IRQ + * handler and therefore its priority handling + */ + +void posix_interrupt_raised(void) +{ + uint64_t irq_lock; + int irq_nbr; + + irq_lock = hw_irq_ctrl_get_current_lock(); + + if (irq_lock) { + /* "spurious" wakes can happen with interrupts locked */ + return; + } + + while ((irq_nbr = hw_irq_ctrl_get_highest_prio_irq()) != -1) { + int last_current_running_prio = hw_irq_ctrl_get_cur_prio(); + int last_running_irq = currently_running_irq; + + hw_irq_ctrl_set_cur_prio(hw_irq_ctrl_get_prio(irq_nbr)); + hw_irq_ctrl_clear_irq(irq_nbr); + + currently_running_irq = irq_nbr; + systemVectors[irq_nbr + 16](); + currently_running_irq = last_running_irq; + + hw_irq_ctrl_set_cur_prio(last_current_running_prio); + } +} + +/** + * Thru this function the IRQ controller can raise an immediate interrupt which + * will interrupt the SW itself + * (this function should only be called from the HW model code, from SW threads) + */ +void posix_irq_handler_im_from_sw(void) +{ + /* + * if a higher priority interrupt than the possibly currently running is + * pending we go immediately into irq_handler() to vector into its + * handler + */ + if (hw_irq_ctrl_get_highest_prio_irq() != -1) { + posix_interrupt_raised(); + } +} diff --git a/lib/bt/host/nimble/nimble/babblesim/core/src/main_config.c b/lib/bt/host/nimble/nimble/babblesim/core/src/main_config.c new file mode 100644 index 00000000..ef8cb04d --- /dev/null +++ b/lib/bt/host/nimble/nimble/babblesim/core/src/main_config.c @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2017-2018 Oticon A/S + * Copyright (c) 2021 Codecoup + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "NRF_HW_model_top.h" +#include "NRF_HWLowL.h" +#include "bs_tracing.h" +#include "bs_symbols.h" +#include "bs_types.h" +#include "bs_rand_main.h" +#include "bs_pc_backchannel.h" +#include "bs_dump_files.h" +#include "argparse.h" +#include "time_machine.h" +#include "os/mynewt.h" +#include +#include "os/sim.h" + +uint global_device_nbr; +struct nrf52_bsim_args_t *args; + +void +bst_tick(bs_time_t time) +{ + return; +} + +uint8_t +inner_main_clean_up(int exit_code) +{ + hwll_terminate_simulation(); + nrf_hw_models_free_all(); + bs_dump_files_close_all(); + + bs_clean_back_channels(); + return 0; +} + +uint8_t +main_clean_up_trace_wrap(void) +{ + return inner_main_clean_up(0); +} + +void +bsim_init(int argc, char** argv, int (*main_fn)(int argc, char **arg)) +{ + setvbuf(stdout, NULL, _IOLBF, 512); + setvbuf(stderr, NULL, _IOLBF, 512); + + bs_trace_register_cleanup_function(main_clean_up_trace_wrap); + bs_trace_register_time_function(tm_get_abs_time); + + nrf_hw_pre_init(); + nrfbsim_register_args(); + + args = nrfbsim_argsparse(argc, argv); + global_device_nbr = args->global_device_nbr; + + bs_read_function_names_from_Tsymbols(argv[0]); + + nrf_hw_initialize(&args->nrf_hw); + os_init(main_fn); + os_start(); + + while (1) { + sleep(1); + } +} + +void +bsim_start(void) +{ + bs_trace_raw(9, "%s: Connecting to phy...\n", __func__); + hwll_connect_to_phy(args->device_nbr, args->s_id, args->p_id); + bs_trace_raw(9, "%s: Connected\n", __func__); + + bs_random_init(args->rseed); + bs_dump_files_open(args->s_id, args->global_device_nbr); +} diff --git a/lib/bt/host/nimble/nimble/babblesim/core/src/time_machine.c b/lib/bt/host/nimble/nimble/babblesim/core/src/time_machine.c new file mode 100644 index 00000000..38b3f424 --- /dev/null +++ b/lib/bt/host/nimble/nimble/babblesim/core/src/time_machine.c @@ -0,0 +1,255 @@ +/* + * Copyright (c) 2017-2018 Oticon A/S + * Copyright (c) 2021 Codecoup + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "NRF_HW_model_top.h" +#include "NRF_HWLowL.h" +#include "bs_tracing.h" +#include "bs_types.h" +#include "bs_utils.h" +#include +#include +#include +#include +#include + +/* Note: All timers are relative to hw_time and NOT to 'now' */ +extern bs_time_t timer_nrf_main_timer; + +/* The events priorities are as in this list from top to bottom + * Priority being which timer executes first if several trigger at the same + * instant + */ +static enum { + NRF_HW_MAIN_TIMER = 0, + NUMBER_OF_TIMERS, + NONE +} next_timer_index = NONE; + +static bs_time_t *Timer_list[NUMBER_OF_TIMERS] = { + &timer_nrf_main_timer, +}; +static bs_time_t next_timer_time = TIME_NEVER; + +/* + * Current absolute time of this device, as the device knows it. + * It is never reset: + */ +bs_time_t now; +/* Current time the HW of this device things it is */ +static bs_time_t hw_time; +/* + * Offset between the current absolute time of the device and the HW time + * That is, the absolute time when the HW_time got reset + */ +static bs_time_t hw_time_delta; + +/* Last time we synchronized with the bsim PHY, in device abs time */ +static bs_time_t last_bsim_phy_sync_time; + +#define BSIM_DEFAULT_PHY_MAX_RESYNC_OFFSET 1000000 +/* At least every second we will inform the simulator about our timing */ +static bs_time_t max_resync_offset = BSIM_DEFAULT_PHY_MAX_RESYNC_OFFSET; + +/** + * Set the maximum amount of time the device will spend without talking + * (synching) with the phy. + * This does not change the functional behavior of the Zephyr code or of the + * radio emulation, and it is only relevant if special test code running in the + * device interacts behind the scenes with other devices test code. + * Setting for example a value of 5ms will ensure that this device time + * will never be more than 5ms away from the phy. Setting it in all devices + * to 5ms would then ensure no device time is farther apart than 5ms from any + * other. + * + * Note that setting low values has a performance penalty. + */ +void +tm_set_phy_max_resync_offset(bs_time_t offset_in_us) +{ + max_resync_offset = offset_in_us; +} + +/** + * Return the absolute current time (no HW model except the RADIO + * should look into this) + */ +bs_time_t +tm_get_abs_time(void) +{ + return now; +} + +/** + * Return the current HW time + */ +bs_time_t +tm_get_hw_time(void) +{ + return hw_time; +} + +bs_time_t +posix_get_hw_cycle(void) +{ + return tm_get_hw_time(); +} + +/** + * Reset the HW time + */ +static void +tm_reset_hw_time(void) +{ + hw_time = 0; + hw_time_delta = now; + if (now != 0) { + bs_trace_error_line("Reset not supposed to happen after " + "initialization\n"); + } +} + +/** + * Update the current hw_time value given the absolute time + */ +INLINE void +tm_update_HW_time(void) +{ + hw_time = now - hw_time_delta; +} + +/* + * Reset the HW time + */ +void +tm_reset_hw_times(void) +{ + tm_reset_hw_time(); +} + +/** + * Advance the internal time values of this device until time + */ +void +tm_sleep_until_abs_time(bs_time_t time) +{ + if (time >= now) { + /* + * Ensure that at least we sync with the phy + * every max_resync_offset + */ + if (time > last_bsim_phy_sync_time + max_resync_offset) { + hwll_sync_time_with_phy(time); + last_bsim_phy_sync_time = time; + } + + now = time; + } else { + /* LCOV_EXCL_START */ + bs_trace_warning_manual_time_line(now, "next_time_time " + "corrupted (%"PRItime"<= %"PRItime", timer idx=%i)\n", + time, now, next_timer_index); + /* LCOV_EXCL_STOP */ + } + tm_update_HW_time(); +} + +/** + * Keep track of the last time we synchronized the time with the scheduler + */ +void +tm_update_last_phy_sync_time(bs_time_t abs_time) +{ + last_bsim_phy_sync_time = abs_time; +} + +/** + * Advance the internal time values of this device + * until the HW time reaches hw_time + */ +void +tm_sleep_until_hw_time(bs_time_t hw_time) +{ + bs_time_t next_time = TIME_NEVER; + + if (hw_time != TIME_NEVER) { + next_time = hw_time + hw_time_delta; + } + + tm_sleep_until_abs_time(next_time); +} + +/** + * Look into all timers and update next_timer accordingly + * To be called each time a "timed process" updates its timer + */ +void +tm_find_next_timer_to_trigger(void) +{ + next_timer_time = *Timer_list[0]; + next_timer_index = 0; + + for (uint i = 1; i < NUMBER_OF_TIMERS; i++) { + if (next_timer_time > *Timer_list[i]) { + next_timer_time = *Timer_list[i]; + next_timer_index = i; + } + } +} + +bs_time_t +tm_get_next_timer_abstime(void) +{ + return next_timer_time + hw_time_delta; +} + +bs_time_t +tm_hw_time_to_abs_time(bs_time_t hwtime) +{ + if (hwtime == TIME_NEVER) { + return TIME_NEVER; + } + return hwtime + hw_time_delta; +} + +bs_time_t +tm_abs_time_to_hw_time(bs_time_t abstime) +{ + if (abstime == TIME_NEVER) { + return TIME_NEVER; + } + return abstime - hw_time_delta; +} + +void +tm_tick_limited(bs_time_t max_time_diff) +{ + bs_time_t time_to_wait; + + if (max_time_diff != TIME_NEVER && now + max_time_diff < next_timer_time) { + time_to_wait = now + max_time_diff; + } else { + time_to_wait = next_timer_time; + } + + tm_sleep_until_hw_time(time_to_wait); + switch (next_timer_index) { + case NRF_HW_MAIN_TIMER: + nrf_hw_some_timer_reached(); + break; + default: + bs_trace_error_time_line("next_timer_index " + "corrupted\n"); + break; + } + tm_find_next_timer_to_trigger(); +} + +void +tm_tick(void) +{ + tm_tick_limited(TIME_NEVER); +} diff --git a/lib/bt/host/nimble/nimble/babblesim/edtt/hci_test/pkg.yml b/lib/bt/host/nimble/nimble/babblesim/edtt/hci_test/pkg.yml new file mode 100644 index 00000000..6f1dd4bb --- /dev/null +++ b/lib/bt/host/nimble/nimble/babblesim/edtt/hci_test/pkg.yml @@ -0,0 +1,32 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +pkg.name: babblesim/edtt/hci_test +pkg.type: app +pkg.description: nRF52 on BabbleSim - EDTT tester +pkg.author: "Codecoup" +pkg.homepage: "http://mynewt.apache.org/" +pkg.keywords: + +pkg.deps: + - "@apache-mynewt-core/sys/console/stub" + - "@apache-mynewt-core/sys/log/stub" + - "@apache-mynewt-core/sys/stats/full" + - "@apache-mynewt-core/kernel/os" + - nimble/transport + - babblesim/edtt/hci_transport diff --git a/lib/bt/host/nimble/nimble/babblesim/edtt/hci_test/src/main.c b/lib/bt/host/nimble/nimble/babblesim/edtt/hci_test/src/main.c new file mode 100644 index 00000000..0ca152cf --- /dev/null +++ b/lib/bt/host/nimble/nimble/babblesim/edtt/hci_test/src/main.c @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2021 Codecoup + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "os/mynewt.h" +#include "ble_hci_edtt.h" + +static int +main_fn(int argc, char **argv) +{ + sysinit(); + + edtt_init(); + + while (1) { + os_eventq_run(os_eventq_dflt_get()); + } + return 0; +} + +int +main(int argc, char **argv) +{ + extern void bsim_init(int argc, char** argv, void *main_fn); + bsim_init(argc, argv, main_fn); + + return 0; +} diff --git a/lib/bt/host/nimble/nimble/babblesim/edtt/hci_test/syscfg.yml b/lib/bt/host/nimble/nimble/babblesim/edtt/hci_test/syscfg.yml new file mode 100644 index 00000000..8a7909a6 --- /dev/null +++ b/lib/bt/host/nimble/nimble/babblesim/edtt/hci_test/syscfg.yml @@ -0,0 +1,30 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +syscfg.vals: + BLE_TRANSPORT_LL: native + BLE_TRANSPORT_HS: custom + + # EDTT requires 0x123456789ABC address for first device + # and 0x456789ABCDEF for second + BLE_LL_PUBLIC_DEV_ADDR: 0x123456789ABC +# BLE_LL_PUBLIC_DEV_ADDR: 0x456789ABCDEF + + # For LL/CON/ADV/BV-09-C, LL/CON/ADV/BV-10-C + BLE_LL_CFG_FEAT_LE_CSA2: 1 diff --git a/lib/bt/host/nimble/nimble/babblesim/edtt/hci_transport/include/ble_hci_edtt.h b/lib/bt/host/nimble/nimble/babblesim/edtt/hci_transport/include/ble_hci_edtt.h new file mode 100644 index 00000000..d590b11d --- /dev/null +++ b/lib/bt/host/nimble/nimble/babblesim/edtt/hci_transport/include/ble_hci_edtt.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2021 Codecoup + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_BLE_HCI_EDTT_ +#define H_BLE_HCI_EDTT_ + +#ifdef __cplusplus +extern "C" { +#endif + +int edtt_init(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/bt/host/nimble/nimble/babblesim/edtt/hci_transport/include/commands.h b/lib/bt/host/nimble/nimble/babblesim/edtt/hci_transport/include/commands.h new file mode 100644 index 00000000..d0f8cce2 --- /dev/null +++ b/lib/bt/host/nimble/nimble/babblesim/edtt/hci_transport/include/commands.h @@ -0,0 +1,242 @@ +/* + * Copyright (c) 2019 Oticon A/S + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef EDDT_APP_COMMANDS_H +#define EDDT_APP_COMMANDS_H + +enum commands_t { + CMD_NOTHING = 0, + CMD_ECHO_REQ, + CMD_ECHO_RSP, + CMD_INQUIRE_REQ, + CMD_INQUIRE_RSP, + CMD_DISCONNECT_REQ, + CMD_DISCONNECT_RSP, + CMD_READ_REMOTE_VERSION_INFORMATION_REQ, + CMD_READ_REMOTE_VERSION_INFORMATION_RSP, + CMD_SET_EVENT_MASK_REQ, + CMD_SET_EVENT_MASK_RSP, + CMD_RESET_REQ, + CMD_RESET_RSP, + CMD_READ_TRANSMIT_POWER_LEVEL_REQ, + CMD_READ_TRANSMIT_POWER_LEVEL_RSP, + CMD_SET_CONTROLLER_TO_HOST_FLOW_CONTROL_REQ, + CMD_SET_CONTROLLER_TO_HOST_FLOW_CONTROL_RSP, + CMD_HOST_BUFFER_SIZE_REQ, + CMD_HOST_BUFFER_SIZE_RSP, + CMD_HOST_NUMBER_OF_COMPLETED_PACKETS_REQ, + CMD_HOST_NUMBER_OF_COMPLETED_PACKETS_RSP, + CMD_SET_EVENT_MASK_PAGE_2_REQ, + CMD_SET_EVENT_MASK_PAGE_2_RSP, + CMD_WRITE_LE_HOST_SUPPORT_REQ, + CMD_WRITE_LE_HOST_SUPPORT_RSP, + CMD_READ_AUTHENTICATED_PAYLOAD_TIMEOUT_REQ, + CMD_READ_AUTHENTICATED_PAYLOAD_TIMEOUT_RSP, + CMD_WRITE_AUTHENTICATED_PAYLOAD_TIMEOUT_REQ, + CMD_WRITE_AUTHENTICATED_PAYLOAD_TIMEOUT_RSP, + CMD_READ_LOCAL_VERSION_INFORMATION_REQ, + CMD_READ_LOCAL_VERSION_INFORMATION_RSP, + CMD_READ_LOCAL_SUPPORTED_COMMANDS_REQ, + CMD_READ_LOCAL_SUPPORTED_COMMANDS_RSP, + CMD_READ_LOCAL_SUPPORTED_FEATURES_REQ, + CMD_READ_LOCAL_SUPPORTED_FEATURES_RSP, + CMD_READ_BUFFER_SIZE_REQ, + CMD_READ_BUFFER_SIZE_RSP, + CMD_READ_BD_ADDR_REQ, + CMD_READ_BD_ADDR_RSP, + CMD_READ_RSSI_REQ, + CMD_READ_RSSI_RSP, + CMD_LE_SET_EVENT_MASK_REQ, + CMD_LE_SET_EVENT_MASK_RSP, + CMD_LE_READ_BUFFER_SIZE_REQ, + CMD_LE_READ_BUFFER_SIZE_RSP, + CMD_LE_READ_LOCAL_SUPPORTED_FEATURES_REQ, + CMD_LE_READ_LOCAL_SUPPORTED_FEATURES_RSP, + CMD_LE_SET_RANDOM_ADDRESS_REQ, + CMD_LE_SET_RANDOM_ADDRESS_RSP, + CMD_LE_SET_ADVERTISING_PARAMETERS_REQ, + CMD_LE_SET_ADVERTISING_PARAMETERS_RSP, + CMD_LE_READ_ADVERTISING_CHANNEL_TX_POWER_REQ, + CMD_LE_READ_ADVERTISING_CHANNEL_TX_POWER_RSP, + CMD_LE_SET_ADVERTISING_DATA_REQ, + CMD_LE_SET_ADVERTISING_DATA_RSP, + CMD_LE_SET_SCAN_RESPONSE_DATA_REQ, + CMD_LE_SET_SCAN_RESPONSE_DATA_RSP, + CMD_LE_SET_ADVERTISING_ENABLE_REQ, + CMD_LE_SET_ADVERTISING_ENABLE_RSP, + CMD_LE_SET_SCAN_PARAMETERS_REQ, + CMD_LE_SET_SCAN_PARAMETERS_RSP, + CMD_LE_SET_SCAN_ENABLE_REQ, + CMD_LE_SET_SCAN_ENABLE_RSP, + CMD_LE_CREATE_CONNECTION_REQ, + CMD_LE_CREATE_CONNECTION_RSP, + CMD_LE_CREATE_CONNECTION_CANCEL_REQ, + CMD_LE_CREATE_CONNECTION_CANCEL_RSP, + CMD_LE_READ_FILTER_ACCEPT_LIST_SIZE_REQ, + CMD_LE_READ_FILTER_ACCEPT_LIST_SIZE_RSP, + CMD_LE_CLEAR_FILTER_ACCEPT_LIST_REQ, + CMD_LE_CLEAR_FILTER_ACCEPT_LIST_RSP, + CMD_LE_ADD_DEVICE_TO_FILTER_ACCEPT_LIST_REQ, + CMD_LE_ADD_DEVICE_TO_FILTER_ACCEPT_LIST_RSP, + CMD_LE_REMOVE_DEVICE_FROM_FILTER_ACCEPT_LIST_REQ, + CMD_LE_REMOVE_DEVICE_FROM_FILTER_ACCEPT_LIST_RSP, + CMD_LE_CONNECTION_UPDATE_REQ, + CMD_LE_CONNECTION_UPDATE_RSP, + CMD_LE_SET_HOST_CHANNEL_CLASSIFICATION_REQ, + CMD_LE_SET_HOST_CHANNEL_CLASSIFICATION_RSP, + CMD_LE_READ_CHANNEL_MAP_REQ, + CMD_LE_READ_CHANNEL_MAP_RSP, + CMD_LE_READ_REMOTE_FEATURES_REQ, + CMD_LE_READ_REMOTE_FEATURES_RSP, + CMD_LE_ENCRYPT_REQ, + CMD_LE_ENCRYPT_RSP, + CMD_LE_RAND_REQ, + CMD_LE_RAND_RSP, + CMD_LE_START_ENCRYPTION_REQ, + CMD_LE_START_ENCRYPTION_RSP, + CMD_LE_LONG_TERM_KEY_REQUEST_REPLY_REQ, + CMD_LE_LONG_TERM_KEY_REQUEST_REPLY_RSP, + CMD_LE_LONG_TERM_KEY_REQUEST_NEGATIVE_REPLY_REQ, + CMD_LE_LONG_TERM_KEY_REQUEST_NEGATIVE_REPLY_RSP, + CMD_LE_READ_SUPPORTED_STATES_REQ, + CMD_LE_READ_SUPPORTED_STATES_RSP, + CMD_LE_RECEIVER_TEST_REQ, + CMD_LE_RECEIVER_TEST_RSP, + CMD_LE_TRANSMITTER_TEST_REQ, + CMD_LE_TRANSMITTER_TEST_RSP, + CMD_LE_TEST_END_REQ, + CMD_LE_TEST_END_RSP, + CMD_LE_REMOTE_CONNECTION_PARAMETER_REQUEST_REPLY_REQ, + CMD_LE_REMOTE_CONNECTION_PARAMETER_REQUEST_REPLY_RSP, + CMD_LE_REMOTE_CONNECTION_PARAMETER_REQUEST_NEGATIVE_REPLY_REQ, + CMD_LE_REMOTE_CONNECTION_PARAMETER_REQUEST_NEGATIVE_REPLY_RSP, + CMD_LE_SET_DATA_LENGTH_REQ, + CMD_LE_SET_DATA_LENGTH_RSP, + CMD_LE_READ_SUGGESTED_DEFAULT_DATA_LENGTH_REQ, + CMD_LE_READ_SUGGESTED_DEFAULT_DATA_LENGTH_RSP, + CMD_LE_WRITE_SUGGESTED_DEFAULT_DATA_LENGTH_REQ, + CMD_LE_WRITE_SUGGESTED_DEFAULT_DATA_LENGTH_RSP, + CMD_LE_READ_LOCAL_P_256_PUBLIC_KEY_COMMAND_REQ, + CMD_LE_READ_LOCAL_P_256_PUBLIC_KEY_COMMAND_RSP, + CMD_LE_GENERATE_DHKEY_COMMAND_REQ, + CMD_LE_GENERATE_DHKEY_COMMAND_RSP, + CMD_LE_ADD_DEVICE_TO_RESOLVING_LIST_REQ, + CMD_LE_ADD_DEVICE_TO_RESOLVING_LIST_RSP, + CMD_LE_REMOVE_DEVICE_FROM_RESOLVING_LIST_REQ, + CMD_LE_REMOVE_DEVICE_FROM_RESOLVING_LIST_RSP, + CMD_LE_CLEAR_RESOLVING_LIST_REQ, + CMD_LE_CLEAR_RESOLVING_LIST_RSP, + CMD_LE_READ_RESOLVING_LIST_SIZE_REQ, + CMD_LE_READ_RESOLVING_LIST_SIZE_RSP, + CMD_LE_READ_PEER_RESOLVABLE_ADDRESS_REQ, + CMD_LE_READ_PEER_RESOLVABLE_ADDRESS_RSP, + CMD_LE_READ_LOCAL_RESOLVABLE_ADDRESS_REQ, + CMD_LE_READ_LOCAL_RESOLVABLE_ADDRESS_RSP, + CMD_LE_SET_ADDRESS_RESOLUTION_ENABLE_REQ, + CMD_LE_SET_ADDRESS_RESOLUTION_ENABLE_RSP, + CMD_LE_SET_RESOLVABLE_PRIVATE_ADDRESS_TIMEOUT_REQ, + CMD_LE_SET_RESOLVABLE_PRIVATE_ADDRESS_TIMEOUT_RSP, + CMD_LE_READ_MAXIMUM_DATA_LENGTH_REQ, + CMD_LE_READ_MAXIMUM_DATA_LENGTH_RSP, + CMD_LE_READ_PHY_REQ, + CMD_LE_READ_PHY_RSP, + CMD_LE_SET_DEFAULT_PHY_REQ, + CMD_LE_SET_DEFAULT_PHY_RSP, + CMD_LE_SET_PHY_REQ, + CMD_LE_SET_PHY_RSP, + CMD_LE_ENHANCED_RECEIVER_TEST_REQ, + CMD_LE_ENHANCED_RECEIVER_TEST_RSP, + CMD_LE_ENHANCED_TRANSMITTER_TEST_REQ, + CMD_LE_ENHANCED_TRANSMITTER_TEST_RSP, + CMD_LE_SET_EXTENDED_ADVERTISING_PARAMETERS_REQ, + CMD_LE_SET_EXTENDED_ADVERTISING_PARAMETERS_RSP, + CMD_LE_SET_EXTENDED_ADVERTISING_DATA_REQ, + CMD_LE_SET_EXTENDED_ADVERTISING_DATA_RSP, + CMD_LE_SET_EXTENDED_SCAN_RESPONSE_DATA_REQ, + CMD_LE_SET_EXTENDED_SCAN_RESPONSE_DATA_RSP, + CMD_LE_SET_EXTENDED_ADVERTISING_ENABLE_REQ, + CMD_LE_SET_EXTENDED_ADVERTISING_ENABLE_RSP, + CMD_LE_READ_MAXIMUM_ADVERTISING_DATA_LENGTH_REQ, + CMD_LE_READ_MAXIMUM_ADVERTISING_DATA_LENGTH_RSP, + CMD_LE_READ_NUMBER_OF_SUPPORTED_ADVERTISING_SETS_REQ, + CMD_LE_READ_NUMBER_OF_SUPPORTED_ADVERTISING_SETS_RSP, + CMD_LE_REMOVE_ADVERTISING_SET_REQ, + CMD_LE_REMOVE_ADVERTISING_SET_RSP, + CMD_LE_CLEAR_ADVERTISING_SETS_REQ, + CMD_LE_CLEAR_ADVERTISING_SETS_RSP, + CMD_LE_SET_PERIODIC_ADVERTISING_PARAMETERS_REQ, + CMD_LE_SET_PERIODIC_ADVERTISING_PARAMETERS_RSP, + CMD_LE_SET_PERIODIC_ADVERTISING_DATA_REQ, + CMD_LE_SET_PERIODIC_ADVERTISING_DATA_RSP, + CMD_LE_SET_PERIODIC_ADVERTISING_ENABLE_REQ, + CMD_LE_SET_PERIODIC_ADVERTISING_ENABLE_RSP, + CMD_LE_SET_EXTENDED_SCAN_PARAMETERS_REQ, + CMD_LE_SET_EXTENDED_SCAN_PARAMETERS_RSP, + CMD_LE_SET_EXTENDED_SCAN_ENABLE_REQ, + CMD_LE_SET_EXTENDED_SCAN_ENABLE_RSP, + CMD_LE_EXTENDED_CREATE_CONNECTION_REQ, + CMD_LE_EXTENDED_CREATE_CONNECTION_RSP, + CMD_LE_PERIODIC_ADVERTISING_CREATE_SYNC_REQ, + CMD_LE_PERIODIC_ADVERTISING_CREATE_SYNC_RSP, + CMD_LE_PERIODIC_ADVERTISING_CREATE_SYNC_CANCEL_REQ, + CMD_LE_PERIODIC_ADVERTISING_CREATE_SYNC_CANCEL_RSP, + CMD_LE_PERIODIC_ADVERTISING_TERMINATE_SYNC_REQ, + CMD_LE_PERIODIC_ADVERTISING_TERMINATE_SYNC_RSP, + CMD_LE_ADD_DEVICE_TO_PERIODIC_ADVERTISER_LIST_REQ, + CMD_LE_ADD_DEVICE_TO_PERIODIC_ADVERTISER_LIST_RSP, + CMD_LE_REMOVE_DEVICE_FROM_PERIODIC_ADVERTISER_LIST_REQ, + CMD_LE_REMOVE_DEVICE_FROM_PERIODIC_ADVERTISER_LIST_RSP, + CMD_LE_CLEAR_PERIODIC_ADVERTISER_LIST_REQ, + CMD_LE_CLEAR_PERIODIC_ADVERTISER_LIST_RSP, + CMD_LE_READ_PERIODIC_ADVERTISER_LIST_SIZE_REQ, + CMD_LE_READ_PERIODIC_ADVERTISER_LIST_SIZE_RSP, + CMD_LE_READ_TRANSMIT_POWER_REQ, + CMD_LE_READ_TRANSMIT_POWER_RSP, + CMD_LE_READ_RF_PATH_COMPENSATION_REQ, + CMD_LE_READ_RF_PATH_COMPENSATION_RSP, + CMD_LE_WRITE_RF_PATH_COMPENSATION_REQ, + CMD_LE_WRITE_RF_PATH_COMPENSATION_RSP, + CMD_LE_SET_PRIVACY_MODE_REQ, + CMD_LE_SET_PRIVACY_MODE_RSP, + CMD_WRITE_BD_ADDR_REQ, + CMD_WRITE_BD_ADDR_RSP, + CMD_FLUSH_EVENTS_REQ, + CMD_FLUSH_EVENTS_RSP, + CMD_HAS_EVENT_REQ, + CMD_HAS_EVENT_RSP, + CMD_GET_EVENT_REQ, + CMD_GET_EVENT_RSP, + CMD_LE_FLUSH_DATA_REQ, + CMD_LE_FLUSH_DATA_RSP, + CMD_LE_DATA_READY_REQ, + CMD_LE_DATA_READY_RSP, + CMD_LE_DATA_WRITE_REQ, + CMD_LE_DATA_WRITE_RSP, + CMD_LE_DATA_READ_REQ, + CMD_LE_DATA_READ_RSP, + CMD_GATT_SERVICE_SET_REQ, + CMD_GATT_SERVICE_SET_RSP, + CMD_GATT_SERVICE_NOTIFY_REQ, + CMD_GATT_SERVICE_NOTIFY_RSP, + CMD_GATT_SERVICE_INDICATE_REQ, + CMD_GATT_SERVICE_INDICATE_RSP, + CMD_GAP_ADVERTISING_MODE_REQ, + CMD_GAP_ADVERTISING_MODE_RSP, + CMD_GAP_ADVERTISING_DATA_REQ, + CMD_GAP_ADVERTISING_DATA_RSP, + CMD_GAP_SCANNING_MODE_REQ, + CMD_GAP_SCANNING_MODE_RSP, + CMD_READ_STATIC_ADDRESSES_REQ, + CMD_READ_STATIC_ADDRESSES_RSP, + CMD_READ_KEY_HIERARCHY_ROOTS_REQ, + CMD_READ_KEY_HIERARCHY_ROOTS_RSP, + CMD_GAP_READ_IRK_REQ, + CMD_GAP_READ_IRK_RSP, + CMD_GAP_ROLE_REQ, + CMD_GAP_ROLE_RSP +}; + +#endif /* EDDT_APP_COMMANDS_H */ diff --git a/lib/bt/host/nimble/nimble/babblesim/edtt/hci_transport/include/edtt_driver.h b/lib/bt/host/nimble/nimble/babblesim/edtt/hci_transport/include/edtt_driver.h new file mode 100644 index 00000000..3b7d2533 --- /dev/null +++ b/lib/bt/host/nimble/nimble/babblesim/edtt/hci_transport/include/edtt_driver.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2019 Oticon A/S + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef EDTT_DRIVER_H +#define EDTT_DRIVER_H + +#include +#include + +#define EDTTT_NONBLOCK 0 +#define EDTTT_BLOCK 1 + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Generic EDTT interface + */ +bool edtt_start(void); +bool edtt_stop(void); +int edtt_read(uint8_t *ptr, size_t size, int flags); +int edtt_write(uint8_t *ptr, size_t size, int flags); + +/** + * Exclusive functions for the BabbleSim driver + */ +void enable_edtt_mode(void); +void set_edtt_autoshutdown(bool mode); + +#ifdef __cplusplus +} +#endif + +#endif /* EDTT_DRIVER_H */ diff --git a/lib/bt/host/nimble/nimble/babblesim/edtt/hci_transport/pkg.yml b/lib/bt/host/nimble/nimble/babblesim/edtt/hci_transport/pkg.yml new file mode 100644 index 00000000..8ad24cd3 --- /dev/null +++ b/lib/bt/host/nimble/nimble/babblesim/edtt/hci_transport/pkg.yml @@ -0,0 +1,38 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +pkg.name: babblesim/edtt/hci_transport +pkg.description: Provides communication with EDTT bridge over named pipes +pkg.author: "Apache Mynewt " +pkg.homepage: "http://mynewt.apache.org/" +pkg.deps: + - "@apache-mynewt-core/sys/console/stub" + - "@apache-mynewt-core/sys/log/stub" + - "@apache-mynewt-core/sys/stats/full" + - "@apache-mynewt-core/kernel/os" + - nimble/controller + +# allows to override settings from nimble/transport +pkg.subpriority: 1 + +pkg.apis: + - ble_transport + +pkg.init: + ble_hci_edtt_init: 249 diff --git a/lib/bt/host/nimble/nimble/babblesim/edtt/hci_transport/src/ble_hci_edtt.c b/lib/bt/host/nimble/nimble/babblesim/edtt/hci_transport/src/ble_hci_edtt.c new file mode 100644 index 00000000..aa70f056 --- /dev/null +++ b/lib/bt/host/nimble/nimble/babblesim/edtt/hci_transport/src/ble_hci_edtt.c @@ -0,0 +1,813 @@ +/* + * Copyright (c) 2019 Oticon A/S + * Copyright (c) 2021 Codecoup + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include "sysinit/sysinit.h" +#include "syscfg/syscfg.h" +#include "os/os_eventq.h" +#include "controller/ble_ll.h" + +/* BLE */ +#include "nimble/ble.h" +#include "nimble/hci_common.h" + +#include "bs_symbols.h" +#include "bs_types.h" +#include "time_machine.h" +#include "ble_hci_edtt.h" +#include "edtt_driver.h" +#include "commands.h" + +#define BLE_HCI_EDTT_NONE 0x00 +#define BLE_HCI_EDTT_CMD 0x01 +#define BLE_HCI_EDTT_ACL 0x02 +#define BLE_HCI_EDTT_EVT 0x04 + +#define BT_HCI_OP_VS_WRITE_BD_ADDR 0xFC06 + +/* A packet for queueing EDTT/HCI commands and events */ +struct ble_hci_edtt_pkt { + struct os_event ev; + STAILQ_ENTRY(ble_hci_edtt_pkt) next; + uint32_t timestamp; + uint8_t type; + void *data; +}; + +static struct os_eventq edtt_q_svc; +static struct os_eventq edtt_q_data; +static struct os_eventq edtt_q_event; +static uint8_t edtt_q_event_count; + +static uint16_t waiting_opcode; +static enum commands_t waiting_response; + +static struct os_task edtt_poller_task; +static struct os_task edtt_service_task; + +#if EDTT_HCI_LOGS +extern unsigned int global_device_nbr; +static FILE *fptr; + +static void +log_hex_array(uint8_t *buf, int len) +{ + int i; + for (i = 0; i < len; i++) { + fprintf(fptr, "%02X ", buf[i]); + } +} + +static void +log_hci_cmd(uint16_t opcode, uint8_t *buf, int len) +{ + if (fptr) { + fprintf(fptr, "> %llu %02d %02d ", now, BLE_HCI_OCF(opcode), (BLE_HCI_OGF(opcode))); + log_hex_array(buf, len); + fputs("\n\n", fptr); + fflush(fptr); + } +} + +static void +log_hci_evt(struct ble_hci_ev *hdr) +{ + if (fptr) { + fprintf(fptr, "< %llu %02d ", now, hdr->opcode); + log_hex_array(hdr->data, hdr->length); + fputs("\n\n", fptr); + fflush(fptr); + } +} + +static void +log_hci_init() +{ + int flen = (int) strlen(MYNEWT_VAL(EDTT_HCI_LOG_FILE)) + 7; + char *fpath = (char *) calloc(flen, sizeof(char)); + sprintf(fpath, "%s_%02d.log", MYNEWT_VAL(EDTT_HCI_LOG_FILE), global_device_nbr); + fptr = fopen(fpath, "w"); + free(fpath); +} +#endif + +static int +ble_hci_edtt_acl_tx(struct os_mbuf *om) +{ + struct ble_hci_edtt_pkt *pkt; + + /* If this packet is zero length, just free it */ + if (OS_MBUF_PKTLEN(om) == 0) { + os_mbuf_free_chain(om); + return 0; + } + + pkt = calloc(1, sizeof(*pkt)); + pkt->type = BLE_HCI_EDTT_ACL; + pkt->data = om; + + os_eventq_put(&edtt_q_svc, &pkt->ev); + + return 0; +} + +static int +ble_hci_edtt_cmdevt_tx(uint8_t *hci_ev, uint8_t edtt_type) +{ + struct ble_hci_edtt_pkt *pkt; + + pkt = calloc(1, sizeof(*pkt)); + pkt->type = edtt_type; + pkt->data = hci_ev; + + os_eventq_put(&edtt_q_svc, &pkt->ev); + + return 0; +} + +/** + * Sends an HCI event from the controller to the host. + * + * @param cmd The HCI event to send. This buffer must be + * allocated via ble_hci_trans_buf_alloc(). + * + * @return 0 on success; + * A BLE_ERR_[...] error code on failure. + */ +int +ble_transport_to_hs_evt(void *buf) +{ + return ble_hci_edtt_cmdevt_tx(buf, BLE_HCI_EDTT_EVT); +} + +/** + * Sends ACL data from controller to host. + * + * @param om The ACL data packet to send. + * + * @return 0 on success; + * A BLE_ERR_[...] error code on failure. + */ +int +ble_transport_to_hs_acl(struct os_mbuf *om) +{ + return ble_hci_edtt_acl_tx(om); +} + +/** + * @brief Clean out excess bytes from the input buffer + */ +static void +read_excess_bytes(uint16_t size) +{ + if (size > 0) { + uint8_t buffer[size]; + + edtt_read((uint8_t *) buffer, size, EDTTT_BLOCK); + bs_trace_raw_time(3, "command size wrong! (%u extra bytes removed)", size); + } +} + +/** + * @brief Provide an error response when an HCI command send failed + */ +static void +error_response(int error) +{ + uint16_t response = waiting_response; + int le_error = error; + uint16_t size = sizeof(le_error); + + edtt_write((uint8_t *) &response, sizeof(response), EDTTT_BLOCK); + edtt_write((uint8_t *) &size, sizeof(size), EDTTT_BLOCK); + edtt_write((uint8_t *) &le_error, sizeof(le_error), EDTTT_BLOCK); + waiting_response = CMD_NOTHING; + waiting_opcode = 0; +} + +/** + * @brief Allocate buffer for HCI command, fill in parameters and send the + * command + */ +static int +send_hci_cmd_to_ctrl(uint16_t opcode, uint8_t param_len, uint16_t response) { + struct ble_hci_cmd *buf; + int err = 0; + waiting_response = response; + waiting_opcode = opcode; + + buf = ble_transport_alloc_cmd(); + if (buf != NULL) { + buf->opcode = opcode; + buf->length = param_len; + + if (param_len) { + edtt_read((uint8_t *) buf->data, param_len, EDTTT_BLOCK); + } + +#if EDTT_HCI_LOGS + log_hci_cmd(opcode, buf->data, param_len); +#endif + + err = ble_transport_to_ll_cmd(buf); + if (err) { + ble_transport_free(buf); + bs_trace_raw_time(3, "Failed to send HCI command %d (err %d)", opcode, err); + error_response(err); + } + } else { + bs_trace_raw_time(3, "Failed to create buffer for HCI command 0x%04x", opcode); + error_response(-1); + } + return err; +} + +/** + * @brief Echo function - echo input received + */ +static void +echo(uint16_t size) +{ + uint16_t response = CMD_ECHO_RSP; + + edtt_write((uint8_t *) &response, sizeof(response), EDTTT_BLOCK); + edtt_write((uint8_t *) &size, sizeof(size), EDTTT_BLOCK); + + if (size > 0) { + uint8_t buff[size]; + + edtt_read(buff, size, EDTTT_BLOCK); + edtt_write(buff, size, EDTTT_BLOCK); + } +} + +/** + * @brief Handle Command Complete HCI event + */ +static void +command_complete(struct ble_hci_ev *evt) +{ + struct ble_hci_ev_command_complete *evt_cc = (void *) evt->data; + uint16_t response = waiting_response; + uint16_t size = evt->length - sizeof(evt_cc->num_packets) - sizeof(evt_cc->opcode); + + if (evt_cc->opcode == 0) { + /* ignore nop */ + } else if (evt_cc->opcode == waiting_opcode) { + bs_trace_raw_time(9, "Command complete for 0x%04x", waiting_opcode); + + edtt_write((uint8_t *) &response, sizeof(response), EDTTT_BLOCK); + edtt_write((uint8_t *) &size, sizeof(size), EDTTT_BLOCK); + edtt_write((uint8_t *) &evt_cc->status, sizeof(evt_cc->status), EDTTT_BLOCK); + edtt_write((uint8_t *) &evt_cc->return_params, size - sizeof(evt_cc->status), EDTTT_BLOCK); + waiting_opcode = 0; + } else { + bs_trace_raw_time(5, "Not waiting for 0x(%04x) command status," + " expected 0x(%04x)", evt_cc->opcode, waiting_opcode); + } +} + +/** + * @brief Handle Command Status HCI event + */ +static void +command_status(struct ble_hci_ev *evt) +{ + struct ble_hci_ev_command_status *evt_cs = (void *) evt->data; + uint16_t opcode = evt_cs->opcode; + uint16_t response = waiting_response; + uint16_t size; + + size = evt->length - sizeof(evt_cs->num_packets) - sizeof(evt_cs->opcode); + + if (opcode == waiting_opcode) { + bs_trace_raw_time(9, "Command status for 0x%04x", waiting_opcode); + + edtt_write((uint8_t *) &response, sizeof(response), EDTTT_BLOCK); + edtt_write((uint8_t *) &size, sizeof(size), EDTTT_BLOCK); + edtt_write((uint8_t *) &evt_cs->status, sizeof(evt_cs->status), EDTTT_BLOCK); + edtt_write((uint8_t *) &evt_cs->num_packets, size - sizeof(evt_cs->status), EDTTT_BLOCK); + waiting_opcode = 0; + } else { + bs_trace_raw_time(5, "Not waiting for 0x(%04x) command status," + " expected 0x(%04x)", opcode, waiting_opcode); + } +} + +static void +free_data(struct ble_hci_edtt_pkt *pkt) +{ + assert(pkt); + os_mbuf_free_chain(pkt->data); + free(pkt); +} + +static void +free_event(struct ble_hci_edtt_pkt *pkt) +{ + assert(pkt); + ble_transport_free(pkt->data); + free(pkt); +} + +/** + * @brief Allocate and store an event in the event queue + */ +static struct ble_hci_edtt_pkt * +queue_event(struct ble_hci_ev *evt) +{ + struct ble_hci_edtt_pkt *pkt; + + pkt = calloc(1, sizeof(*pkt)); + assert(pkt); + pkt->timestamp = tm_get_hw_time(); + pkt->type = BLE_HCI_EDTT_EVT; + pkt->data = evt; + + os_eventq_put(&edtt_q_event, &pkt->ev); + edtt_q_event_count++; + + return pkt; +} + +static struct ble_hci_edtt_pkt * +queue_data(struct os_mbuf *om) +{ + struct ble_hci_edtt_pkt *pkt; + + pkt = calloc(1, sizeof(*pkt)); + assert(pkt); + pkt->timestamp = tm_get_hw_time(); + pkt->type = BLE_HCI_EDTT_ACL; + pkt->data = om; + + os_eventq_put(&edtt_q_data, &pkt->ev); + + return pkt; +} + + +static void * +dup_complete_evt(void *evt) +{ + struct ble_hci_ev *evt_copy; + + /* max evt size is always 257 */ + evt_copy = ble_transport_alloc_evt(0); + memcpy(evt_copy, evt, 257); + ble_transport_free(evt); + + return evt_copy; +} + +/** + * @brief Thread to service events and ACL data packets from the HCI input queue + */ +static void +service_events(void *arg) +{ + struct ble_hci_edtt_pkt *pkt; + struct ble_hci_ev *evt; + struct ble_hci_ev_num_comp_pkts *evt_ncp; + + while (1) { + pkt = (void *)os_eventq_get(&edtt_q_svc); + + if (pkt->type == BLE_HCI_EDTT_EVT) { + evt = (void *)pkt->data; + +#if EDTT_HCI_LOGS + log_hci_evt(hdr); +#endif + + /* Prepare and send EDTT events */ + switch (evt->opcode) { + case BLE_HCI_EVCODE_COMMAND_COMPLETE: + evt = dup_complete_evt(evt); + queue_event(evt); + command_complete(evt); + break; + case BLE_HCI_EVCODE_COMMAND_STATUS: + evt = dup_complete_evt(evt); + queue_event(evt); + command_status(evt); + break; + case BLE_HCI_EVCODE_NUM_COMP_PKTS: + evt_ncp = (void *)evt->data; + /* This should always be true for NimBLE LL */ + assert(evt_ncp->count == 1); + if (evt_ncp->completed[0].packets == 0) { + /* Discard, because EDTT does not like it */ + ble_transport_free(evt); + } else { + queue_event(evt); + } + break; + case BLE_HCI_OPCODE_NOP: + /* Ignore noop bytes from Link layer */ + ble_transport_free(evt); + break; + default: + /* Queue HCI events. We will send them to EDTT + * on CMD_GET_EVENT_REQ. */ + queue_event(evt); + } + } else if (pkt->type == BLE_HCI_EDTT_ACL) { + queue_data(pkt->data); + } + + free(pkt); + } +} + +/** + * @brief Flush all HCI events from the input-copy queue + */ +static void +flush_events(uint16_t size) +{ + uint16_t response = CMD_FLUSH_EVENTS_RSP; + struct ble_hci_edtt_pkt *pkt; + + while ((pkt = (void *)os_eventq_get_no_wait(&edtt_q_event))) { + free_event(pkt); + edtt_q_event_count--; + } + read_excess_bytes(size); + size = 0; + + edtt_write((uint8_t *)&response, sizeof(response), EDTTT_BLOCK); + edtt_write((uint8_t *)&size, sizeof(size), EDTTT_BLOCK); +} + +/** + * @brief Get next available HCI event from the input-copy queue + */ +static void +get_event(uint16_t size) +{ + uint16_t response = CMD_GET_EVENT_RSP; + struct ble_hci_edtt_pkt *pkt; + struct ble_hci_ev *evt; + + read_excess_bytes(size); + size = 0; + + edtt_write((uint8_t *)&response, sizeof(response), EDTTT_BLOCK); + pkt = (void*)os_eventq_get(&edtt_q_event); + if (pkt) { + evt = pkt->data; + size = sizeof(pkt->timestamp) + sizeof(*evt) + evt->length; + + edtt_write((uint8_t *)&size, sizeof(size), EDTTT_BLOCK); + edtt_write((uint8_t *)&pkt->timestamp, sizeof(pkt->timestamp), EDTTT_BLOCK); + edtt_write((uint8_t *)evt, sizeof(*evt) + evt->length, EDTTT_BLOCK); + + free_event(pkt); + + edtt_q_event_count--; + } else { + edtt_write((uint8_t *)&size, sizeof(size), EDTTT_BLOCK); + } +} + +/** + * @brief Get next available HCI events from the input-copy queue + */ +static void +get_events(uint16_t size) +{ + uint16_t response = CMD_GET_EVENT_RSP; + struct ble_hci_edtt_pkt *pkt; + struct ble_hci_ev *evt; + uint8_t count = edtt_q_event_count; + + read_excess_bytes(size); + size = 0; + + edtt_write((uint8_t *)&response, sizeof(response), EDTTT_BLOCK); + edtt_write((uint8_t *)&count, sizeof(count), EDTTT_BLOCK); + + while (count--) { + pkt = (void *)os_eventq_get_no_wait(&edtt_q_event); + assert(pkt); + evt = pkt->data; + size = sizeof(pkt->timestamp) + sizeof(*evt) + evt->length; + + edtt_write((uint8_t *)&size, sizeof(size), EDTTT_BLOCK); + edtt_write((uint8_t *)&pkt->timestamp, sizeof(pkt->timestamp), EDTTT_BLOCK); + edtt_write((uint8_t *)evt, sizeof(*evt) + evt->length, EDTTT_BLOCK); + + free_event(pkt); + + edtt_q_event_count--; + } +} + +/** + * @brief Check whether an HCI event is available in the input-copy queue + */ +static void +has_event(uint16_t size) +{ + struct has_event_resp { + uint16_t response; + uint16_t size; + uint8_t count; + } __attribute__((packed)); + struct has_event_resp le_response = { + .response = CMD_HAS_EVENT_RSP, + .size = 1, + .count = edtt_q_event_count + }; + + if (size > 0) { + read_excess_bytes(size); + } + edtt_write((uint8_t *) &le_response, sizeof(le_response), EDTTT_BLOCK); +} + +/** + * @brief Flush all ACL Data Packages from the input-copy queue + */ +static void +le_flush_data(uint16_t size) +{ + uint16_t response = CMD_LE_FLUSH_DATA_RSP; + struct ble_hci_edtt_pkt *pkt; + + while ((pkt = (void *)os_eventq_get_no_wait(&edtt_q_data))) { + free_data(pkt); + } + + read_excess_bytes(size); + size = 0; + + edtt_write((uint8_t *)&response, sizeof(response), EDTTT_BLOCK); + edtt_write((uint8_t *)&size, sizeof(size), EDTTT_BLOCK); +} + +/** + * @brief Check whether an ACL Data Package is available in the input-copy queue + */ +static void +le_data_ready(uint16_t size) +{ + struct has_data_resp { + uint16_t response; + uint16_t size; + uint8_t empty; + } __attribute__((packed)); + struct has_data_resp le_response = { + .response = CMD_LE_DATA_READY_RSP, + .size = 1, + .empty = 0 + }; + + if (size > 0) { + read_excess_bytes(size); + } + + /* There's no API to check if eventq is empty but a little hack will do... */ + if (edtt_q_data.evq_list.stqh_first == NULL) { + le_response.empty = 1; + } + + edtt_write((uint8_t *) &le_response, sizeof(le_response), EDTTT_BLOCK); +} + +/** + * @brief Get next available HCI Data Package from the input-copy queue + */ +static void +le_data_read(uint16_t size) +{ + uint16_t response = CMD_LE_DATA_READ_RSP; + struct ble_hci_edtt_pkt *pkt; + struct os_mbuf *om; + + read_excess_bytes(size); + size = 0; + + edtt_write((uint8_t *)&response, sizeof(response), EDTTT_BLOCK); + pkt = (void *)os_eventq_get(&edtt_q_data); + if (pkt) { + om = pkt->data; + + size = sizeof(pkt->timestamp) + OS_MBUF_PKTLEN(om); + + edtt_write((uint8_t *)&size, sizeof(size), EDTTT_BLOCK); + edtt_write((uint8_t *)&pkt->timestamp, sizeof(pkt->timestamp), EDTTT_BLOCK); + + while (om != NULL) { + edtt_write((uint8_t *)om->om_data, om->om_len, EDTTT_BLOCK); + om = SLIST_NEXT(om, om_next); + } + + free_data(pkt); + } else { + edtt_write((uint8_t *)&size, sizeof(size), EDTTT_BLOCK); + } +} + +/** + * @brief Write ACL Data Package to the Controller + */ +static void +le_data_write(uint16_t size) +{ + struct data_write_resp { + uint16_t code; + uint16_t size; + uint8_t status; + } __attribute__((packed)); + struct data_write_resp response = { + .code = CMD_LE_DATA_WRITE_RSP, + .size = 1, + .status = 0 + }; + struct os_mbuf *om; + struct hci_data_hdr hdr; + int err; + + if (size >= sizeof(hdr)) { + om = ble_transport_alloc_acl_from_hs(); + if (om) { + edtt_read((void *)&hdr, sizeof(hdr), EDTTT_BLOCK); + size -= sizeof(hdr); + + os_mbuf_append(om, &hdr, sizeof(hdr)); + + if (size >= hdr.hdh_len) { + /* Don't care, we have plenty of stack */ + uint8_t tmp[hdr.hdh_len]; + + edtt_read(tmp, hdr.hdh_len, EDTTT_BLOCK); + size -= hdr.hdh_len; + + os_mbuf_append(om, tmp, hdr.hdh_len); + } + + err = ble_transport_to_ll_acl(om); + if (err) { + bs_trace_raw_time(3, "Failed to send ACL Data (err %d)", err); + } + } else { + err = -2; /* Failed to allocate data buffer */ + bs_trace_raw_time(3, "Failed to create buffer for ACL Data."); + } + } else { + /* Size too small for header (handle and data length) */ + err = -3; + } + read_excess_bytes(size); + + response.status = err; + edtt_write((uint8_t *) &response, sizeof(response), EDTTT_BLOCK); +} + +static void +fake_write_bd_addr_cc() +{ + struct ble_hci_ev_command_complete *ev; + struct ble_hci_ev *hci_ev; + waiting_opcode = BT_HCI_OP_VS_WRITE_BD_ADDR; + waiting_response = CMD_WRITE_BD_ADDR_RSP; + + hci_ev = ble_transport_alloc_evt(0); + if (hci_ev) { + hci_ev->opcode = BLE_HCI_EVCODE_COMMAND_COMPLETE; + hci_ev->length = sizeof(*ev); + ev = (void *) hci_ev->data; + + ev->num_packets = 1; + ev->opcode = waiting_opcode; + ev->status = 0; + ble_hci_edtt_cmdevt_tx((uint8_t *) hci_ev, BLE_HCI_EDTT_EVT); + } +} + +/* Reads and executes EDTT commands. */ +static void +edtt_poller(void *arg) { + uint16_t command; + uint16_t size; + uint16_t opcode; + uint8_t bdaddr[6]; + + /* Initialize HCI command opcode and response variables */ + waiting_opcode = 0; + waiting_response = CMD_NOTHING; + edtt_q_event_count = 0; + + /* Initialize and start EDTT system */ + enable_edtt_mode(); + set_edtt_autoshutdown(true); + edtt_start(); + +#if EDTT_HCI_LOGS + log_hci_init(); +#endif + + while (1) { + edtt_read((uint8_t *) &command, sizeof(command), EDTTT_BLOCK); + edtt_read((uint8_t *) &size, sizeof(size), EDTTT_BLOCK); + + bs_trace_raw_time(4, "command 0x%04X received (size %u) " + "events=%u\n", + command, size, edtt_q_event_count); + + switch (command) { + case CMD_ECHO_REQ: + echo(size); + break; + case CMD_FLUSH_EVENTS_REQ: + flush_events(size); + break; + case CMD_HAS_EVENT_REQ: + has_event(size); + break; + case CMD_GET_EVENT_REQ: { + uint8_t multiple; + + edtt_read((uint8_t *) &multiple, sizeof(multiple), EDTTT_BLOCK); + if (multiple) + get_events(--size); + else + get_event(--size); + } + break; + case CMD_LE_FLUSH_DATA_REQ: + le_flush_data(size); + break; + case CMD_LE_DATA_READY_REQ: + le_data_ready(size); + break; + case CMD_LE_DATA_WRITE_REQ: + le_data_write(size); + break; + case CMD_LE_DATA_READ_REQ: + le_data_read(size); + break; + case CMD_WRITE_BD_ADDR_REQ: + edtt_read((uint8_t *) &opcode, sizeof(opcode), EDTTT_BLOCK); + + if (opcode == BT_HCI_OP_VS_WRITE_BD_ADDR) { + edtt_read((uint8_t *) &bdaddr, sizeof(bdaddr), EDTTT_BLOCK); + ble_ll_set_public_addr(bdaddr); + fake_write_bd_addr_cc(); + } else { + assert(0); + } + break; + default: + if (size >= 2) { + edtt_read((uint8_t *) &opcode, sizeof(opcode), EDTTT_BLOCK); + send_hci_cmd_to_ctrl(opcode, size - 2, command + 1); + } + } + } +} + +int +edtt_init(void) +{ + os_stack_t dummy_stack; + int rc; + + rc = os_task_init(&edtt_poller_task, "edttpoll", edtt_poller, NULL, + MYNEWT_VAL(EDTT_POLLER_PRIO), OS_WAIT_FOREVER, + &dummy_stack, 1); + assert(rc == 0); + + rc = os_task_init(&edtt_service_task, "edttsvc", service_events, NULL, + MYNEWT_VAL(EDTT_POLLER_PRIO) + 1, OS_WAIT_FOREVER, + &dummy_stack, 1); + assert(rc == 0); + + return 0; +} + +/** + * Initializes the EDTT HCI transport module. + * + * @return 0 on success; + * A BLE_ERR_[...] error code on failure. + */ +void +ble_hci_edtt_init(void) +{ + /* Ensure this function only gets called by sysinit. */ + SYSINIT_ASSERT_ACTIVE(); + + os_eventq_init(&edtt_q_svc); + os_eventq_init(&edtt_q_event); + os_eventq_init(&edtt_q_data); +} diff --git a/lib/bt/host/nimble/nimble/babblesim/edtt/hci_transport/src/edtt_driver_bsim.c b/lib/bt/host/nimble/nimble/babblesim/edtt/hci_transport/src/edtt_driver_bsim.c new file mode 100644 index 00000000..5fce5263 --- /dev/null +++ b/lib/bt/host/nimble/nimble/babblesim/edtt/hci_transport/src/edtt_driver_bsim.c @@ -0,0 +1,289 @@ +/* + * Copyright (c) 2019 Oticon A/S + * Copyright (c) 2021 Codecoup + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include + +#include "bs_tracing.h" +#include "bs_oswrap.h" +#include "bs_pc_base_fifo_user.h" + +#include +#include "edtt_driver.h" +#include "os/os_sched.h" + +/* Recheck if something arrived from the EDTT every 5ms */ +#define EDTT_IF_RECHECK_DELTA 5 /* ms */ + +/* We want the runs to be deterministic => we want to resync with the Phy + * before we retry any read so the bridge device may also run + */ +#define EDTT_SIMU_RESYNC_TIME_WITH_EDTT \ + (EDTT_IF_RECHECK_DELTA * 1000 - 1) + +static int edtt_mode_enabled = 0; + +/* In this mode, when the EDTTool closes the FIFO we automatically terminate + * this simulated device. If false, we just continue running + */ +static int edtt_autoshutdown; + +#define TO_DEVICE 0 +#define TO_BRIDGE 1 +static int fifo[2] = { -1, -1 }; +static char *fifo_path[2] = {NULL, NULL}; + +extern unsigned int global_device_nbr; + +static void edttd_clean_up(void); +static void edptd_create_fifo_if(void); +static int fifo_low_level_read(uint8_t *bufptr, int size); + +bool edtt_start(void) +{ + if (edtt_mode_enabled == false) { + /* otherwise we don't try to open the EDTT interface */ + return true; + } + + edptd_create_fifo_if(); + + extern void tm_set_phy_max_resync_offset(uint64_t offset_in_us); + tm_set_phy_max_resync_offset(EDTT_SIMU_RESYNC_TIME_WITH_EDTT); + return true; +} + +bool edtt_stop(void) +{ + if (edtt_mode_enabled == false) { + /* otherwise we don't try to open the EDTT interface */ + return true; + } + + bs_trace_raw(9, "EDTTT: %s called\n", __func__); + edttd_clean_up(); + edtt_mode_enabled = false; + return true; +} + +static void +print_hex_array(int lvl, uint8_t *buf, int len) +{ + char str[2*len]; + char *p = str; + int i; + for (i = 0; i < len; i++) { + if (i > 0) *p++ = ' '; + sprintf(p++, "%02X", buf[i]); + } + *p = '\n'; + bs_trace_raw(lvl, str); +} + +/** + * Attempt to read size bytes thru the EDTT IF into the buffer <*ptr> + * can be set to EDTTT_BLOCK or EDTTT_NONBLOCK + * + * If set to EDTTT_BLOCK it will block the calling thread until + * bytes have been read or the interface has been closed. + * If set to EDTTT_NONBLOCK it returns as soon as there is no more data to be + * read + * + * Returns the amount of read bytes, or -1 on error + */ +int edtt_read(uint8_t *ptr, size_t size, int flags) +{ + uint8_t *buf = ptr; + + if (edtt_mode_enabled == false) { + return -1; + } + + bs_trace_raw_time(8, "EDTT: Asked to read %i bytes\n", size); + int read = 0; + + while (size > 0) { + int received_bytes; + + received_bytes = fifo_low_level_read(ptr, size); + if (received_bytes < 0) { + return -1; + } else if (received_bytes > 0) { + size -= received_bytes; + ptr += received_bytes; + read += received_bytes; + } else { + if (flags & EDTTT_BLOCK) { + bs_trace_raw_time(9, "EDTT: No enough data yet," + "sleeping for %i ms\n", + EDTT_IF_RECHECK_DELTA); + os_sched_sleep(os_sched_get_current_task(), + os_time_ms_to_ticks32(EDTT_IF_RECHECK_DELTA)); + os_sched(NULL); + } else { + bs_trace_raw_time(9, "EDTT: No enough data yet," + "returning\n"); + break; + } + } + } + + if (read > 0) { + bs_trace_raw_time(8, "Read %i bytes:\n", read); + print_hex_array(8, buf, read); + } + return read; +} + +/** + * Write bytes from toward the EDTTool + * + * is ignored in this driver, all writes to the tool are + * instantaneous + */ +int edtt_write(uint8_t *ptr, size_t size, int flags) +{ + if (edtt_mode_enabled == false) { + return -1; + } + + bs_trace_raw_time(6, "EDTT: Asked to write %i bytes: ", size); + print_hex_array(6, ptr, size); + + if (write(fifo[TO_BRIDGE], ptr, size) != size) { + if (errno == EPIPE) { + bs_trace_error_line("EDTT IF suddenly closed by other " + "end\n"); + } + if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) { + bs_trace_error_line("EDTT IF to bridge filled up (FIFO " + "size needs to be increased)\n"); + } + bs_trace_error_line("EDTT IF: Unexpected error on write\n"); + } + return size; +} + +/* + * Applications may want to enable the EDTT interface only in some + * cases. By default it is not enabled in this driver. This function + * must be called once before starting it to do so + */ +void enable_edtt_mode(void) +{ + edtt_mode_enabled = true; +} + +/** + * Automatically terminate this device execution once the EDTTool disconnects + */ +void set_edtt_autoshutdown(bool Mode) +{ + edtt_autoshutdown = Mode; +} + +static void edptd_create_fifo_if(void) +{ + int flags; + + bs_trace_raw_time(9, "Bringing EDTT IF up (waiting for other side)\n"); + + if (pb_com_path == NULL) { + bs_trace_error_line("Not connected to Phy." + "EDTT IF cannot be brough up\n"); + } + + /* At this point we have connected to the Phy so the COM folder does + * already exist + * also SIGPIPE is already ignored + */ + + fifo_path[TO_DEVICE] = (char *)bs_calloc(pb_com_path_length + 30, + sizeof(char)); + fifo_path[TO_BRIDGE] = (char *)bs_calloc(pb_com_path_length + 30, + sizeof(char)); + sprintf(fifo_path[TO_DEVICE], "%s/Device%i.PTTin", + pb_com_path, global_device_nbr); + sprintf(fifo_path[TO_BRIDGE], "%s/Device%i.PTTout", + pb_com_path, global_device_nbr); + + if ((pb_create_fifo_if_not_there(fifo_path[TO_DEVICE]) != 0) + || (pb_create_fifo_if_not_there(fifo_path[TO_BRIDGE]) != 0)) { + bs_trace_error_line("Couldnt create FIFOs for EDTT IF\n"); + } + + /* we block here until the bridge opens its end */ + fifo[TO_BRIDGE] = open(fifo_path[TO_BRIDGE], O_WRONLY); + if (fifo[TO_BRIDGE] == -1) { + bs_trace_error_line("Couldn't create FIFOs for EDTT IF\n"); + } + + flags = fcntl(fifo[TO_BRIDGE], F_GETFL); + flags |= O_NONBLOCK; + fcntl(fifo[TO_BRIDGE], F_SETFL, flags); + + /* we will block here until the bridge opens its end */ + fifo[TO_DEVICE] = open(fifo_path[TO_DEVICE], O_RDONLY); + if (fifo[TO_DEVICE] == -1) { + bs_trace_error_line("Couldn't create FIFOs for EDTT IF\n"); + } + + flags = fcntl(fifo[TO_DEVICE], F_GETFL); + flags |= O_NONBLOCK; + fcntl(fifo[TO_DEVICE], F_SETFL, flags); +} + +static void edttd_clean_up(void) +{ + for (int dir = TO_DEVICE ; dir <= TO_BRIDGE ; dir++) { + if (fifo_path[dir]) { + if (fifo[dir] != -1) { + close(fifo[dir]); + remove(fifo_path[dir]); + fifo[dir] = -1; + } + free(fifo_path[dir]); + fifo_path[dir] = NULL; + } + } + if (pb_com_path != NULL) { + rmdir(pb_com_path); + } +} + +static int fifo_low_level_read(uint8_t *bufptr, int size) +{ + int received_bytes = read(fifo[TO_DEVICE], bufptr, size); + + if ((received_bytes == -1) && (errno == EAGAIN)) { + return 0; + } else if (received_bytes == EOF || received_bytes == 0) { + /*The FIFO was closed by the bridge*/ + if (edtt_autoshutdown) { + bs_trace_raw_time(3, "EDTT: FIFO closed " + "(ptt_autoshutdown==true) =>" + " Terminate\n"); + edttd_clean_up(); + bs_trace_exit_line("\n"); + } else { + bs_trace_raw_time(3, "EDTT: FIFO closed " + "(ptt_autoshutdown==false) => We close " + "the FIFOs and move on\n"); + edttd_clean_up(); + edtt_mode_enabled = false; + return -1; + } + } else if (received_bytes == -1) { + bs_trace_error_line("EDTT: Unexpected error\n"); + } + + return received_bytes; +} diff --git a/lib/bt/host/nimble/nimble/babblesim/edtt/hci_transport/syscfg.yml b/lib/bt/host/nimble/nimble/babblesim/edtt/hci_transport/syscfg.yml new file mode 100644 index 00000000..ad12f7b5 --- /dev/null +++ b/lib/bt/host/nimble/nimble/babblesim/edtt/hci_transport/syscfg.yml @@ -0,0 +1,40 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +syscfg.defs: + EDTT_HCI_LOG_FILE: + description: > + Path to the HCI log file. Skip file extension + because device id and .log will be appended. + value: "hci_logs" + EDTT_HCI_LOGS: + description: Turn on HCI commands logging. + value: 0 + EDTT_POLLER_PRIO: + description: 'Priority of native EDTT poller task.' + type: task_priority + value: 2 + +syscfg.vals: + BLE_TRANSPORT_ACL_COUNT: 32 + BLE_TRANSPORT_EVT_COUNT: 64 + +syscfg.restrictions: + - BLE_TRANSPORT_HS == "custom" + - BLE_TRANSPORT_LL == "native" \ No newline at end of file diff --git a/lib/bt/host/nimble/nimble/babblesim/hw/bsp/nrf52_bsim/bsp.yml b/lib/bt/host/nimble/nimble/babblesim/hw/bsp/nrf52_bsim/bsp.yml new file mode 100644 index 00000000..edd2189d --- /dev/null +++ b/lib/bt/host/nimble/nimble/babblesim/hw/bsp/nrf52_bsim/bsp.yml @@ -0,0 +1,60 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +bsp.name: "nRF52 DK" +bsp.url: https://www.nordicsemi.com/Software-and-Tools/Development-Kits/nRF52-DK +bsp.maker: "Nordic Semiconductor" +bsp.arch: bsim_arch +bsp.compiler: "@apache-mynewt-core/compiler/sim" +bsp.downloadscript: "hw/bsp/nrf52_bsim/nordic_pca10040_download.sh" +bsp.debugscript: "hw/bsp/nrf52_bsim/nordic_pca10040_debug.sh" +bsp.downloadscript.WINDOWS.OVERWRITE: "hw/bsp/nrf52_bsim/nordic_pca10040_download.cmd" +bsp.debugscript.WINDOWS.OVERWRITE: "hw/bsp/nrf52_bsim/nordic_pca10040_debug.cmd" + +bsp.flash_map: + areas: + # System areas. + FLASH_AREA_BOOTLOADER: + device: 0 + offset: 0x00000000 + size: 16kB + FLASH_AREA_IMAGE_0: + device: 0 + offset: 0x00008000 + size: 232kB + FLASH_AREA_IMAGE_1: + device: 0 + offset: 0x00042000 + size: 232kB + FLASH_AREA_IMAGE_SCRATCH: + device: 0 + offset: 0x0007c000 + size: 4kB + + # User areas. + FLASH_AREA_REBOOT_LOG: + user_id: 0 + device: 0 + offset: 0x00004000 + size: 16kB + FLASH_AREA_NFFS: + user_id: 1 + device: 0 + offset: 0x0007d000 + size: 12kB diff --git a/lib/bt/host/nimble/nimble/babblesim/hw/bsp/nrf52_bsim/include/bsp/bsp.h b/lib/bt/host/nimble/nimble/babblesim/hw/bsp/nrf52_bsim/include/bsp/bsp.h new file mode 100644 index 00000000..c096b887 --- /dev/null +++ b/lib/bt/host/nimble/nimble/babblesim/hw/bsp/nrf52_bsim/include/bsp/bsp.h @@ -0,0 +1,91 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_BSP_H +#define H_BSP_H + +#include + +#include "os/mynewt.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Define special stackos sections */ +#define sec_data_core __attribute__((section(".data.core"))) +#define sec_bss_core __attribute__((section(".bss.core"))) +#define sec_bss_nz_core __attribute__((section(".bss.core.nz"))) + +/* More convenient section placement macros. */ +#define bssnz_t sec_bss_nz_core + +extern uint8_t _ram_start; +#define RAM_SIZE 0x10000 + +/* LED pins */ +#define LED_1 (17) +#define LED_2 (18) +#define LED_3 (19) +#define LED_4 (20) +#define LED_BLINK_PIN (LED_1) + +/* Buttons */ +#define BUTTON_1 (13) +#define BUTTON_2 (14) +#define BUTTON_3 (15) +#define BUTTON_4 (16) + +/* Arduino pins */ +#define ARDUINO_PIN_D0 11 +#define ARDUINO_PIN_D1 12 +#define ARDUINO_PIN_D2 13 +#define ARDUINO_PIN_D3 14 +#define ARDUINO_PIN_D4 15 +#define ARDUINO_PIN_D5 16 +#define ARDUINO_PIN_D6 17 +#define ARDUINO_PIN_D7 18 +#define ARDUINO_PIN_D8 19 +#define ARDUINO_PIN_D9 20 +#define ARDUINO_PIN_D10 22 +#define ARDUINO_PIN_D11 23 +#define ARDUINO_PIN_D12 24 +#define ARDUINO_PIN_D13 25 +#define ARDUINO_PIN_A0 3 +#define ARDUINO_PIN_A1 4 +#define ARDUINO_PIN_A2 28 +#define ARDUINO_PIN_A3 29 +#define ARDUINO_PIN_A4 30 +#define ARDUINO_PIN_A5 31 + +#define ARDUINO_PIN_RX ARDUINO_PIN_D0 +#define ARDUINO_PIN_TX ARDUINO_PIN_D1 + +#define ARDUINO_PIN_SCL 27 +#define ARDUINO_PIN_SDA 26 + +#define ARDUINO_PIN_SCK ARDUINO_PIN_D13 +#define ARDUINO_PIN_MOSI ARDUINO_PIN_D11 +#define ARDUINO_PIN_MISO ARDUINO_PIN_D12 + +#ifdef __cplusplus +} +#endif + +#endif /* H_BSP_H */ diff --git a/lib/bt/host/nimble/nimble/babblesim/hw/bsp/nrf52_bsim/include/os/os_arch.h b/lib/bt/host/nimble/nimble/babblesim/hw/bsp/nrf52_bsim/include/os/os_arch.h new file mode 100644 index 00000000..32bc97bc --- /dev/null +++ b/lib/bt/host/nimble/nimble/babblesim/hw/bsp/nrf52_bsim/include/os/os_arch.h @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef _OS_ARCH_ARM_H +#define _OS_ARCH_ARM_H + +#include +#include "syscfg/syscfg.h" +#include "mcu/cmsis_nvic.h" +#include "mcu/cortex_m4.h" +#include +#include "mcu/mcu_sim.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* CPU status register */ +typedef uint32_t os_sr_t; + +/* Stack element */ +typedef uint32_t os_stack_t; + +struct stack_frame; +void os_arch_frame_init(struct stack_frame *sf); + +/* Stack sizes for common OS tasks */ +#define OS_SANITY_STACK_SIZE (2000) +#if MYNEWT_VAL(OS_SYSVIEW) +#define OS_IDLE_STACK_SIZE (80) +#else +#define OS_IDLE_STACK_SIZE (4000) +#endif + +static inline int +os_arch_in_isr(void) +{ + return hw_irq_ctrl_get_irq_status(); +} + +/* Include common arch definitions and APIs */ +#include "os/arch/common.h" + +#ifdef __cplusplus +} +#endif + +#endif /* _OS_ARCH_ARM_H */ diff --git a/lib/bt/host/nimble/nimble/babblesim/hw/bsp/nrf52_bsim/include/os/sim.h b/lib/bt/host/nimble/nimble/babblesim/hw/bsp/nrf52_bsim/include/os/sim.h new file mode 100644 index 00000000..3d8837b8 --- /dev/null +++ b/lib/bt/host/nimble/nimble/babblesim/hw/bsp/nrf52_bsim/include/os/sim.h @@ -0,0 +1,60 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_KERNEL_SIM_ +#define H_KERNEL_SIM_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include "os/mynewt.h" +#include "mcu/mcu_sim.h" +struct os_task; +struct stack_frame; + +struct stack_frame { + int sf_mainsp; /* stack on which main() is executing */ + sigjmp_buf sf_jb; + struct os_task *sf_task; +}; + +void sim_task_start(struct stack_frame *sf, int rc); +os_stack_t *sim_task_stack_init(struct os_task *t, os_stack_t *stack_top, + int size); +os_error_t sim_os_start(void); +void sim_os_stop(void); +os_error_t sim_os_init(void); +void sim_ctx_sw(struct os_task *next_t); +os_sr_t sim_save_sr(void); +void sim_restore_sr(os_sr_t osr); +int sim_in_critical(void); +void sim_tick_idle(os_time_t ticks); +int sig_block_irq_on(); +void sig_unblock_irq_off(); + +uint8_t inner_main_clean_up(int exit_code); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/bt/host/nimble/nimble/babblesim/hw/bsp/nrf52_bsim/nordic_pca10040_debug.cmd b/lib/bt/host/nimble/nimble/babblesim/hw/bsp/nrf52_bsim/nordic_pca10040_debug.cmd new file mode 100755 index 00000000..3444fd32 --- /dev/null +++ b/lib/bt/host/nimble/nimble/babblesim/hw/bsp/nrf52_bsim/nordic_pca10040_debug.cmd @@ -0,0 +1,22 @@ +@rem +@rem Licensed to the Apache Software Foundation (ASF) under one +@rem or more contributor license agreements. See the NOTICE file +@rem distributed with this work for additional information +@rem regarding copyright ownership. The ASF licenses this file +@rem to you under the Apache License, Version 2.0 (the +@rem "License"); you may not use this file except in compliance +@rem with the License. You may obtain a copy of the License at +@rem +@rem http://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, +@rem software distributed under the License is distributed on an +@rem "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@rem KIND, either express or implied. See the License for the +@rem specific language governing permissions and limitations +@rem under the License. +@rem + +@rem Execute a shell with a script of the same name and .sh extension + +@bash "%~dp0%~n0.sh" diff --git a/lib/bt/host/nimble/nimble/babblesim/hw/bsp/nrf52_bsim/nordic_pca10040_debug.sh b/lib/bt/host/nimble/nimble/babblesim/hw/bsp/nrf52_bsim/nordic_pca10040_debug.sh new file mode 100755 index 00000000..1e248e4e --- /dev/null +++ b/lib/bt/host/nimble/nimble/babblesim/hw/bsp/nrf52_bsim/nordic_pca10040_debug.sh @@ -0,0 +1,45 @@ +#!/bin/sh +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +# Called with following variables set: +# - CORE_PATH is absolute path to @apache-mynewt-core +# - BSP_PATH is absolute path to hw/bsp/bsp_name +# - BIN_BASENAME is the path to prefix to target binary, +# .elf appended to name is the ELF file +# - FEATURES holds the target features string +# - EXTRA_JTAG_CMD holds extra parameters to pass to jtag software +# - RESET set if target should be reset when attaching +# - NO_GDB set if we should not start gdb to debug +# + +. $CORE_PATH/hw/scripts/jlink.sh + +FILE_NAME=$BIN_BASENAME.elf + +if [ $# -gt 2 ]; then + SPLIT_ELF_NAME=$3.elf + # TODO -- this magic number 0x42000 is the location of the second image + # slot. we should either get this from a flash map file or somehow learn + # this from the image itself + EXTRA_GDB_CMDS="add-symbol-file $SPLIT_ELF_NAME 0x8000 -readnow" +fi + +JLINK_DEV="nRF52" + +jlink_debug diff --git a/lib/bt/host/nimble/nimble/babblesim/hw/bsp/nrf52_bsim/nordic_pca10040_download.cmd b/lib/bt/host/nimble/nimble/babblesim/hw/bsp/nrf52_bsim/nordic_pca10040_download.cmd new file mode 100755 index 00000000..3444fd32 --- /dev/null +++ b/lib/bt/host/nimble/nimble/babblesim/hw/bsp/nrf52_bsim/nordic_pca10040_download.cmd @@ -0,0 +1,22 @@ +@rem +@rem Licensed to the Apache Software Foundation (ASF) under one +@rem or more contributor license agreements. See the NOTICE file +@rem distributed with this work for additional information +@rem regarding copyright ownership. The ASF licenses this file +@rem to you under the Apache License, Version 2.0 (the +@rem "License"); you may not use this file except in compliance +@rem with the License. You may obtain a copy of the License at +@rem +@rem http://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, +@rem software distributed under the License is distributed on an +@rem "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@rem KIND, either express or implied. See the License for the +@rem specific language governing permissions and limitations +@rem under the License. +@rem + +@rem Execute a shell with a script of the same name and .sh extension + +@bash "%~dp0%~n0.sh" diff --git a/lib/bt/host/nimble/nimble/babblesim/hw/bsp/nrf52_bsim/nordic_pca10040_download.sh b/lib/bt/host/nimble/nimble/babblesim/hw/bsp/nrf52_bsim/nordic_pca10040_download.sh new file mode 100755 index 00000000..08d45b46 --- /dev/null +++ b/lib/bt/host/nimble/nimble/babblesim/hw/bsp/nrf52_bsim/nordic_pca10040_download.sh @@ -0,0 +1,40 @@ +#!/bin/sh +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +# Called with following variables set: +# - CORE_PATH is absolute path to @apache-mynewt-core +# - BSP_PATH is absolute path to hw/bsp/bsp_name +# - BIN_BASENAME is the path to prefix to target binary, +# .elf appended to name is the ELF file +# - IMAGE_SLOT is the image slot to download to (for non-mfg-image, non-boot) +# - FEATURES holds the target features string +# - EXTRA_JTAG_CMD holds extra parameters to pass to jtag software +# - MFG_IMAGE is "1" if this is a manufacturing image +# - FLASH_OFFSET contains the flash offset to download to +# - BOOT_LOADER is set if downloading a bootloader + +. $CORE_PATH/hw/scripts/jlink.sh + +if [ "$MFG_IMAGE" ]; then + FLASH_OFFSET=0x0 +fi + +JLINK_DEV="nRF52" + +common_file_to_load +jlink_load diff --git a/lib/bt/host/nimble/nimble/babblesim/hw/bsp/nrf52_bsim/pkg.yml b/lib/bt/host/nimble/nimble/babblesim/hw/bsp/nrf52_bsim/pkg.yml new file mode 100644 index 00000000..e22e7674 --- /dev/null +++ b/lib/bt/host/nimble/nimble/babblesim/hw/bsp/nrf52_bsim/pkg.yml @@ -0,0 +1,37 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +pkg.name: babblesim/hw/bsp/nrf52_bsim +pkg.type: bsp +pkg.description: nRF52 on BabbleSim +pkg.author: "Apache Mynewt " +pkg.homepage: "http://mynewt.apache.org/" + +pkg.cflags: + - '-DNRF52832_XXAA' + - '-DBABBLESIM' + +pkg.cflags.HARDFLOAT: + - -mfloat-abi=hard -mfpu=fpv4-sp-d16 + +pkg.deps: + - "babblesim/core" + - "babblesim/hw/mcu/nordic/nrf52_bsim" + - "@apache-mynewt-core/hw/drivers/uart/uart_hal" + - "@apache-mynewt-nimble/nimble/controller" diff --git a/lib/bt/host/nimble/nimble/babblesim/hw/bsp/nrf52_bsim/src/arch/bsim_arch/os_arch.c b/lib/bt/host/nimble/nimble/babblesim/hw/bsp/nrf52_bsim/src/arch/bsim_arch/os_arch.c new file mode 100644 index 00000000..8277ae7c --- /dev/null +++ b/lib/bt/host/nimble/nimble/babblesim/hw/bsp/nrf52_bsim/src/arch/bsim_arch/os_arch.c @@ -0,0 +1,169 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include + +static pthread_mutex_t bsim_ctx_sw_mutex = PTHREAD_MUTEX_INITIALIZER; +static int bsim_pend_sv; + +struct task_info { + pthread_t tid; + pthread_cond_t cond; + void *arg; +}; + +static void * +task_wrapper(void *arg) +{ + struct os_task *me = arg; + struct task_info *ti = me->t_arg; + + pthread_mutex_lock(&bsim_ctx_sw_mutex); + if (g_current_task != me) { + pthread_cond_wait(&ti->cond, &bsim_ctx_sw_mutex); + assert(g_current_task == me); + } + + me->t_func(ti->arg); + + assert(0); +} + +os_stack_t * +os_arch_task_stack_init(struct os_task *t, os_stack_t *stack_top, int size) +{ + struct task_info *ti; + int err; + + ti = calloc(1, sizeof(*ti)); + + pthread_cond_init(&ti->cond, NULL); + ti->arg = t->t_arg; + t->t_arg = ti; + + err = pthread_create(&ti->tid, NULL, task_wrapper, t); + assert(err == 0); + + pthread_setname_np(ti->tid, t->t_name); + + return stack_top; +} + +os_error_t +os_arch_os_start(void) +{ + struct os_task *next_t; + struct task_info *ti; + + os_tick_init(OS_TICKS_PER_SEC, 7); + + next_t = os_sched_next_task(); + assert(next_t); + os_sched_set_current_task(next_t); + + g_os_started = 1; + + ti = next_t->t_arg; + pthread_cond_signal(&ti->cond); + + return 0; +} + +os_error_t +os_arch_os_init(void) +{ + STAILQ_INIT(&g_os_task_list); + TAILQ_INIT(&g_os_run_list); + TAILQ_INIT(&g_os_sleep_list); + + os_init_idle_task(); + + return OS_OK; +} + +void +os_arch_ctx_sw(struct os_task *next_t) +{ + os_sched_ctx_sw_hook(next_t); + bsim_pend_sv = 1; +} + +static void +do_ctx_sw(void) +{ + struct os_task *next_t; + struct os_task *me; + struct task_info *ti, *next_ti; + + next_t = os_sched_next_task(); + assert(next_t); + + bsim_pend_sv = 0; + + assert(g_current_task); + me = g_current_task; + ti = me->t_arg; + + if (me == next_t) { + return; + } + + g_current_task = next_t; + next_ti = g_current_task->t_arg; + + pthread_cond_signal(&next_ti->cond); + pthread_cond_wait(&ti->cond, &bsim_ctx_sw_mutex); + assert(g_current_task == me); +} + +os_sr_t +os_arch_save_sr(void) +{ + return hw_irq_ctrl_change_lock(1); +} + +void +os_arch_restore_sr(os_sr_t osr) +{ + hw_irq_ctrl_change_lock(osr); + + if (!osr && bsim_pend_sv) { + do_ctx_sw(); + } +} + +int +os_arch_in_critical(void) +{ + return hw_irq_ctrl_get_current_lock(); +} + +void +__assert_func(const char *file, int line, const char *func, const char *e) +{ +#if MYNEWT_VAL(OS_ASSERT_CB) + os_assert_cb(); +#endif + _Exit(1); +} diff --git a/lib/bt/host/nimble/nimble/babblesim/hw/bsp/nrf52_bsim/src/arch/bsim_arch/startup_nrf52_bsim.c b/lib/bt/host/nimble/nimble/babblesim/hw/bsp/nrf52_bsim/src/arch/bsim_arch/startup_nrf52_bsim.c new file mode 100644 index 00000000..d4810689 --- /dev/null +++ b/lib/bt/host/nimble/nimble/babblesim/hw/bsp/nrf52_bsim/src/arch/bsim_arch/startup_nrf52_bsim.c @@ -0,0 +1,231 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "nrf.h" + +/************************************************************************************************** + Macros +**************************************************************************************************/ + +/*! Weak symbol reference. */ +#define WEAK __attribute__ ((weak)) + +/************************************************************************************************** + Functions +**************************************************************************************************/ + +extern void SystemInit(void); +static void SystemDefaultHandler(void); + +/* Core vectors. */ +void WEAK Reset_Handler(void); +void WEAK NMI_Handler(void); +void WEAK HardFault_Handler(void); +void WEAK MemoryManagement_Handler(void); +void WEAK BusFault_Handler(void); +void WEAK UsageFault_Handler(void); +void WEAK SVC_Handler(void); +void WEAK DebugMon_Handler(void); +void WEAK PendSV_Handler(void); +void WEAK SysTick_Handler(void); +void WEAK POWER_CLOCK_IRQHandler(void); +void WEAK RADIO_IRQHandler(void); +void WEAK UARTE0_UART0_IRQHandler(void); +void WEAK SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0_IRQHandler(void); +void WEAK SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1_IRQHandler(void); +void WEAK NFCT_IRQHandler(void); +void WEAK GPIOTE_IRQHandler(void); +void WEAK SAADC_IRQHandler(void); +void WEAK TIMER0_IRQHandler(void); +void WEAK TIMER1_IRQHandler(void); +void WEAK TIMER2_IRQHandler(void); +void WEAK RTC0_IRQHandler(void); +void WEAK TEMP_IRQHandler(void); +void WEAK RNG_IRQHandler(void); +void WEAK ECB_IRQHandler(void); +void WEAK CCM_AAR_IRQHandler(void); +void WEAK WDT_IRQHandler(void); +void WEAK RTC1_IRQHandler(void); +void WEAK QDEC_IRQHandler(void); +void WEAK COMP_LPCOMP_IRQHandler(void); +void WEAK SWI0_EGU0_IRQHandler(void); +void WEAK SWI1_EGU1_IRQHandler(void); +void WEAK SWI2_EGU2_IRQHandler(void); +void WEAK SWI3_EGU3_IRQHandler(void); +void WEAK SWI4_EGU4_IRQHandler(void); +void WEAK SWI5_EGU5_IRQHandler(void); +void WEAK TIMER3_IRQHandler(void); +void WEAK TIMER4_IRQHandler(void); +void WEAK PWM0_IRQHandler(void); +void WEAK PDM_IRQHandler(void); +void WEAK MWU_IRQHandler(void); +void WEAK PWM1_IRQHandler(void); +void WEAK PWM2_IRQHandler(void); +void WEAK SPIM2_SPIS2_SPI2_IRQHandler(void); +void WEAK RTC2_IRQHandler(void); +void WEAK I2S_IRQHandler(void); +void WEAK FPU_IRQHandler(void); + +/* Assign default weak references. Override these values by defining a new function with the same name. */ +#pragma weak NMI_Handler = SystemDefaultHandler +#pragma weak HardFault_Handler = SystemDefaultHandler +#pragma weak MemoryManagement_Handler = SystemDefaultHandler +#pragma weak BusFault_Handler = SystemDefaultHandler +#pragma weak UsageFault_Handler = SystemDefaultHandler +#pragma weak SVC_Handler = SystemDefaultHandler +#pragma weak DebugMon_Handler = SystemDefaultHandler +#pragma weak PendSV_Handler = SystemDefaultHandler +#pragma weak SysTick_Handler = SystemDefaultHandler +#pragma weak POWER_CLOCK_IRQHandler = SystemDefaultHandler +#pragma weak RADIO_IRQHandler = SystemDefaultHandler +#pragma weak UARTE0_UART0_IRQHandler = SystemDefaultHandler +#pragma weak SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0_IRQHandler = SystemDefaultHandler +#pragma weak SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1_IRQHandler = SystemDefaultHandler +#pragma weak NFCT_IRQHandler = SystemDefaultHandler +#pragma weak GPIOTE_IRQHandler = SystemDefaultHandler +#pragma weak SAADC_IRQHandler = SystemDefaultHandler +#pragma weak TIMER0_IRQHandler = SystemDefaultHandler +#pragma weak TIMER1_IRQHandler = SystemDefaultHandler +#pragma weak TIMER2_IRQHandler = SystemDefaultHandler +#pragma weak RTC0_IRQHandler = SystemDefaultHandler +#pragma weak TEMP_IRQHandler = SystemDefaultHandler +#pragma weak RNG_IRQHandler = SystemDefaultHandler +#pragma weak ECB_IRQHandler = SystemDefaultHandler +#pragma weak CCM_AAR_IRQHandler = SystemDefaultHandler +#pragma weak WDT_IRQHandler = SystemDefaultHandler +#pragma weak RTC1_IRQHandler = SystemDefaultHandler +#pragma weak QDEC_IRQHandler = SystemDefaultHandler +#pragma weak COMP_LPCOMP_IRQHandler = SystemDefaultHandler +#pragma weak SWI0_EGU0_IRQHandler = SystemDefaultHandler +#pragma weak SWI1_EGU1_IRQHandler = SystemDefaultHandler +#pragma weak SWI2_EGU2_IRQHandler = SystemDefaultHandler +#pragma weak SWI3_EGU3_IRQHandler = SystemDefaultHandler +#pragma weak SWI4_EGU4_IRQHandler = SystemDefaultHandler +#pragma weak SWI5_EGU5_IRQHandler = SystemDefaultHandler +#pragma weak TIMER3_IRQHandler = SystemDefaultHandler +#pragma weak TIMER4_IRQHandler = SystemDefaultHandler +#pragma weak PWM0_IRQHandler = SystemDefaultHandler +#pragma weak PDM_IRQHandler = SystemDefaultHandler +#pragma weak MWU_IRQHandler = SystemDefaultHandler +#pragma weak PWM1_IRQHandler = SystemDefaultHandler +#pragma weak PWM2_IRQHandler = SystemDefaultHandler +#pragma weak SPIM2_SPIS2_SPI2_IRQHandler = SystemDefaultHandler +#pragma weak RTC2_IRQHandler = SystemDefaultHandler +#pragma weak I2S_IRQHandler = SystemDefaultHandler +#pragma weak FPU_IRQHandler = SystemDefaultHandler + +/************************************************************************************************** + Global variables +**************************************************************************************************/ + +/*! Core vector table */ +void (* systemVectors[256])(void) = +{ + 0, /* 0: The initial stack pointer */ + Reset_Handler, /* 1: The reset handler */ + NMI_Handler, /* 2: The NMI handler */ + HardFault_Handler, /* 3: The hard fault handler */ + MemoryManagement_Handler, /* 4: The MPU fault handler */ + BusFault_Handler, /* 5: The bus fault handler */ + UsageFault_Handler, /* 6: The usage fault handler */ + 0, /* 7: Reserved */ + 0, /* 8: Reserved */ + 0, /* 9: Reserved */ + 0, /* 10: Reserved */ + SVC_Handler, /* 11: SVCall handler */ + DebugMon_Handler, /* 12: Debug monitor handler */ + 0, /* 13: Reserved */ + PendSV_Handler, /* 14: The PendSV handler */ + SysTick_Handler, /* 15: The SysTick handler */ + + /* External interrupts */ + POWER_CLOCK_IRQHandler, /* 16: POWER_CLOCK */ + RADIO_IRQHandler, /* 17: RADIO */ + UARTE0_UART0_IRQHandler, /* 18: UART0 */ + SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0_IRQHandler, /* 19: SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0 */ + SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1_IRQHandler, /* 20: SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1 */ + NFCT_IRQHandler, /* 21: NFCT */ + GPIOTE_IRQHandler, /* 22: GPIOTE */ + SAADC_IRQHandler, /* 23: SAADC */ + TIMER0_IRQHandler, /* 24: TIMER0 */ + TIMER1_IRQHandler, /* 25: TIMER1 */ + TIMER2_IRQHandler, /* 26: TIMER2 */ + RTC0_IRQHandler, /* 27: RTC0 */ + TEMP_IRQHandler, /* 28: TEMP */ + RNG_IRQHandler, /* 29: RNG */ + ECB_IRQHandler, /* 30: ECB */ + CCM_AAR_IRQHandler, /* 31: CCM_AAR */ + WDT_IRQHandler, /* 32: WDT */ + RTC1_IRQHandler, /* 33: RTC1 */ + QDEC_IRQHandler, /* 34: QDEC */ + COMP_LPCOMP_IRQHandler, /* 35: COMP_LPCOMP */ + SWI0_EGU0_IRQHandler, /* 36: SWI0_EGU0 */ + SWI1_EGU1_IRQHandler, /* 37: SWI1_EGU1 */ + SWI2_EGU2_IRQHandler, /* 38: SWI2_EGU2 */ + SWI3_EGU3_IRQHandler, /* 39: SWI3_EGU3 */ + SWI4_EGU4_IRQHandler, /* 40: SWI4_EGU4 */ + SWI5_EGU5_IRQHandler, /* 41: SWI5_EGU5 */ + TIMER3_IRQHandler, /* 42: TIMER3 */ + TIMER4_IRQHandler, /* 43: TIMER4 */ + PWM0_IRQHandler, /* 44: PWM0 */ + PDM_IRQHandler, /* 45: PDM */ + 0, /* 46: Reserved */ + 0, /* 47: Reserved */ + MWU_IRQHandler, /* 48: MWU */ + PWM1_IRQHandler, /* 49: PWM1 */ + PWM2_IRQHandler, /* 50: PWM2 */ + SPIM2_SPIS2_SPI2_IRQHandler, /* 51: SPIM2_SPIS2_SPI2 */ + RTC2_IRQHandler, /* 52: RTC2 */ + I2S_IRQHandler, /* 53: I2S */ + FPU_IRQHandler, /* 54: FPU */ + 0, /* 55: Reserved */ + 0, /* 56: Reserved */ + 0, /* 57: Reserved */ + 0, /* 58: Reserved */ + 0, /* 59: Reserved */ + 0, /* 60: Reserved */ + 0, /* 61: Reserved */ + 0, /* 62: Reserved */ + 0 /* 63: Reserved */ + /* 64..127: Reserved */ +}; + +/*************************************************************************************************/ +/*! + * \brief Reset handler. + */ +/*************************************************************************************************/ +void Reset_Handler(void) +{ + /* Core initialization. */ + SystemInit(); +} + +/*************************************************************************************************/ +/*! + * \brief Default vector handler. + * + * \param None. + */ +/*************************************************************************************************/ +static void SystemDefaultHandler(void) +{ + volatile unsigned int forever = 1; + while (forever); +} diff --git a/lib/bt/host/nimble/nimble/babblesim/hw/bsp/nrf52_bsim/src/hal_bsp.c b/lib/bt/host/nimble/nimble/babblesim/hw/bsp/nrf52_bsim/src/hal_bsp.c new file mode 100644 index 00000000..502f9a92 --- /dev/null +++ b/lib/bt/host/nimble/nimble/babblesim/hw/bsp/nrf52_bsim/src/hal_bsp.c @@ -0,0 +1,160 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#include +#include +#include "os/mynewt.h" +#include "nrfx.h" +#include "flash_map/flash_map.h" +#include "hal/hal_bsp.h" +#include "hal/hal_flash.h" +#include "hal/hal_system.h" +#include "mcu/nrf52_hal.h" +#include "mcu/nrf52_periph.h" +#include "mcu/native_bsp.h" +#include "bsp/bsp.h" +#include "defs/sections.h" +#include "uart_hal/uart_hal.h" +#include "uart/uart.h" + +/* + * What memory to include in coredump. + */ +static const struct hal_bsp_mem_dump dump_cfg[] = { + [0] = { + .hbmd_start = &_ram_start, + .hbmd_size = RAM_SIZE + } +}; + +const struct hal_flash * +hal_bsp_flash_dev(uint8_t id) +{ + switch (id) { + case 0: + return &native_flash_dev; + default: + return NULL; + } +} + +const struct hal_bsp_mem_dump * +hal_bsp_core_dump(int *area_cnt) +{ + *area_cnt = sizeof(dump_cfg) / sizeof(dump_cfg[0]); + return dump_cfg; +} + +int +hal_bsp_power_state(int state) +{ + return (0); +} + +/** + * Returns the configured priority for the given interrupt. If no priority + * configured, return the priority passed in + * + * @param irq_num + * @param pri + * + * @return uint32_t + */ +uint32_t +hal_bsp_get_nvic_priority(int irq_num, uint32_t pri) +{ + uint32_t cfg_pri; + + switch (irq_num) { + /* Radio gets highest priority */ + case RADIO_IRQn: + cfg_pri = 0; + break; + default: + cfg_pri = pri; + } + return cfg_pri; +} + +static void +nrf52_periph_create_timers(void) +{ + int rc; + + (void)rc; + +#if MYNEWT_VAL(TIMER_0) + rc = hal_timer_init(0, NULL); + assert(rc == 0); +#endif +#if MYNEWT_VAL(TIMER_1) + rc = hal_timer_init(1, NULL); + assert(rc == 0); +#endif +#if MYNEWT_VAL(TIMER_2) + rc = hal_timer_init(2, NULL); + assert(rc == 0); +#endif +#if MYNEWT_VAL(TIMER_3) + rc = hal_timer_init(3, NULL); + assert(rc == 0); +#endif +#if MYNEWT_VAL(TIMER_4) + rc = hal_timer_init(4, NULL); + assert(rc == 0); +#endif +#if MYNEWT_VAL(TIMER_5) + rc = hal_timer_init(5, NULL); + assert(rc == 0); +#endif + +#if MYNEWT_VAL(OS_CPUTIME_TIMER_NUM) >= 0 + rc = os_cputime_init(MYNEWT_VAL(OS_CPUTIME_FREQ)); + assert(rc == 0); +#endif +} + +static struct uart_dev os_bsp_uart0; +static struct uart_dev os_bsp_uart1; + +void +hal_bsp_init(void) +{ + /* Make sure system clocks have started */ + hal_system_clock_start(); + + /* Create all available nRF52840 peripherals */ +// nrf52_periph_create(); + nrf52_periph_create_timers(); + + int rc; + + rc = os_dev_create((struct os_dev *) &os_bsp_uart0, "uart0", + OS_DEV_INIT_PRIMARY, 0, uart_hal_init, (void *) NULL); + assert(rc == 0); + rc = os_dev_create((struct os_dev *) &os_bsp_uart1, "uart1", + OS_DEV_INIT_PRIMARY, 0, uart_hal_init, (void *) NULL); + assert(rc == 0); +} + +void +hal_bsp_deinit(void) +{ +} diff --git a/lib/bt/host/nimble/nimble/babblesim/hw/bsp/nrf52_bsim/syscfg.yml b/lib/bt/host/nimble/nimble/babblesim/hw/bsp/nrf52_bsim/syscfg.yml new file mode 100644 index 00000000..5a45e9d0 --- /dev/null +++ b/lib/bt/host/nimble/nimble/babblesim/hw/bsp/nrf52_bsim/syscfg.yml @@ -0,0 +1,74 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +syscfg.defs: + BSP_NRF52: + description: 'Set to indicate that BSP has NRF52' + value: 1 + SOFT_PWM: + description: 'Enable soft PWM' + value: 0 + ENC_FLASH_DEV: + description: 'Encrypting flash driver over interal flash for testing' + value: 0 + UARTBB_0: + description: 'Enable bit-banger UART 0' + value: 0 + RAM_RESIDENT: + description: 'Compile app to be loaded to RAM' + value: 0 + +syscfg.vals: + HAL_SBRK: 0 + OS_TICKS_PER_SEC: 1024 + OS_MAIN_STACK_SIZE: 8000 + MCU_TIMER_POLLER_PRIO: 0 + BLE_LL_PRIO: 1 + MCU_UART_POLLER_PRIO: 9 + + # Enable nRF52832 MCU + MCU_TARGET: nRF52832 + # Set default pins for peripherals + UART_0_PIN_TX: 6 + UART_0_PIN_RX: 8 + UART_0_PIN_RTS: 5 + UART_0_PIN_CTS: 7 + SPI_0_MASTER_PIN_SCK: 23 + SPI_0_MASTER_PIN_MOSI: 24 + SPI_0_MASTER_PIN_MISO: 25 + SPI_0_SLAVE_PIN_SCK: 23 + SPI_0_SLAVE_PIN_MOSI: 24 + SPI_0_SLAVE_PIN_MISO: 25 + SPI_0_SLAVE_PIN_SS: 22 + I2C_0_PIN_SCL: 27 + I2C_0_PIN_SDA: 26 + + CONFIG_FCB_FLASH_AREA: FLASH_AREA_NFFS + REBOOT_LOG_FLASH_AREA: FLASH_AREA_REBOOT_LOG + NFFS_FLASH_AREA: FLASH_AREA_NFFS + COREDUMP_FLASH_AREA: FLASH_AREA_IMAGE_1 + MCU_DCDC_ENABLED: 1 + MCU_LFCLK_SOURCE: LFXO + BOOT_SERIAL_DETECT_PIN: 13 # Button 1 + +syscfg.vals.BLE_CONTROLLER: + TIMER_0: 0 + TIMER_5: 1 + OS_CPUTIME_FREQ: 32768 + OS_CPUTIME_TIMER_NUM: 5 + BLE_LL_RFMGMT_ENABLE_TIME: 1500 diff --git a/lib/bt/host/nimble/nimble/babblesim/hw/mcu/nordic/nrf52_bsim/include/mcu/cmsis_nvic.h b/lib/bt/host/nimble/nimble/babblesim/hw/mcu/nordic/nrf52_bsim/include/mcu/cmsis_nvic.h new file mode 100644 index 00000000..11a812d3 --- /dev/null +++ b/lib/bt/host/nimble/nimble/babblesim/hw/mcu/nordic/nrf52_bsim/include/mcu/cmsis_nvic.h @@ -0,0 +1,28 @@ +/* mbed Microcontroller Library - cmsis_nvic + * Copyright (c) 2009-2011 ARM Limited. All rights reserved. + * + * CMSIS-style functionality to support dynamic vectors + */ + +#ifndef MBED_CMSIS_NVIC_H +#define MBED_CMSIS_NVIC_H + +#include +#include "nrf.h" + +#define NVIC_NUM_VECTORS (16 + 38) // CORE + MCU Peripherals +#define NVIC_USER_IRQ_OFFSET 16 + +#ifdef __cplusplus +extern "C" { +#endif + +void NVIC_Relocate(void); +void NVIC_SetVector(IRQn_Type IRQn, uint32_t vector); +uint32_t NVIC_GetVector(IRQn_Type IRQn); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/bt/host/nimble/nimble/babblesim/hw/mcu/nordic/nrf52_bsim/include/mcu/cortex_m4.h b/lib/bt/host/nimble/nimble/babblesim/hw/mcu/nordic/nrf52_bsim/include/mcu/cortex_m4.h new file mode 100644 index 00000000..93f20444 --- /dev/null +++ b/lib/bt/host/nimble/nimble/babblesim/hw/mcu/nordic/nrf52_bsim/include/mcu/cortex_m4.h @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef __MCU_CORTEX_M4_H__ +#define __MCU_CORTEX_M4_H__ + +#include "nrf.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* __MCU_CORTEX_M4_H__ */ diff --git a/lib/bt/host/nimble/nimble/babblesim/hw/mcu/nordic/nrf52_bsim/include/mcu/mcu.h b/lib/bt/host/nimble/nimble/babblesim/hw/mcu/nordic/nrf52_bsim/include/mcu/mcu.h new file mode 100644 index 00000000..1950c85f --- /dev/null +++ b/lib/bt/host/nimble/nimble/babblesim/hw/mcu/nordic/nrf52_bsim/include/mcu/mcu.h @@ -0,0 +1,69 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef __MCU_MCU_H_ +#define __MCU_MCU_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Defines for naming GPIOs. NOTE: the nordic chip docs use numeric labels for + * ports. Port A corresponds to Port 0, B to 1, etc. The nrf52832 has only one + * port and thus uses pins 0 - 31. The nrf52840 has two ports but Port 1 only + * has 16 pins. + */ +#define MCU_GPIO_PORTA(pin) ((0 * 16) + (pin)) +#define MCU_GPIO_PORTB(pin) ((1 * 16) + (pin)) + +#if NRF52 + +#define MCU_SYSVIEW_INTERRUPTS \ + "I#1=Reset,I#2=MNI,I#3=HardFault,I#4=MemoryMgmt,I#5=BusFault,I#6=UsageFault," \ + "I#11=SVCall,I#12=DebugMonitor,I#14=PendSV,I#15=SysTick," \ + "I#16=POWER_CLOCK,I#17=RADIO,I#18=UARTE0_UART0,I#19=SPIx0_TWIx0," \ + "I#20=SPIx1_TWIx1,I#21=NFCT,I#22=GPIOTE,I#23=SAADC," \ + "I#24=TIMER0,I#25=TIMER1,I#26=TIMER2,I#27=RTC0,I#28=TEMP,I#29=RNG,I#30=ECB," \ + "I#31=CCM_AAR,I#32=WDT,I#33=RTC1,I#34=QDEC,I#35=COMP_LPCOMP,I#36=SWI0_EGU0," \ + "I#37=SWI1_EGU1,I#38=SWI2_EGU2,I#39=SWI3_EGU3,I#40=SWI4_EGU4,I#41=SWI5_EGU5," \ + "I#42=TIMER3,I#43=TIMER4,I#44=PWM0,I#45=PDM,I#48=MWU,I#49=PWM1,I#50=PWM2," \ + "I#51=SPIx2,I#52=RTC2,I#53=I2S,I#54=FPU" + +#elif NRF52840_XXAA + +#define MCU_SYSVIEW_INTERRUPTS \ + "I#1=Reset,I#2=MNI,I#3=HardFault,I#4=MemoryMgmt,I#5=BusFault,I#6=UsageFault," \ + "I#11=SVCall,I#12=DebugMonitor,I#14=PendSV,I#15=SysTick," \ + "I#16=POWER_CLOCK,I#17=RADIO,I#18=UARTE0_UART0,I#19=SPIx0_TWIx0," \ + "I#20=SPIx1_TWIx1,I#21=NFCT,I#22=GPIOTE,I#23=SAADC," \ + "I#24=TIMER0,I#25=TIMER1,I#26=TIMER2,I#27=RTC0,I#28=TEMP,I#29=RNG,I#30=ECB," \ + "I#31=CCM_AAR,I#32=WDT,I#33=RTC1,I#34=QDEC,I#35=COMP_LPCOMP,I#36=SWI0_EGU0," \ + "I#37=SWI1_EGU1,I#38=SWI2_EGU2,I#39=SWI3_EGU3,I#40=SWI4_EGU4,I#41=SWI5_EGU5," \ + "I#42=TIMER3,I#43=TIMER4,I#44=PWM0,I#45=PDM,I#48=MWU,I#49=PWM1,I#50=PWM2," \ + "I#51=SPIx2,I#52=RTC2,I#53=I2S,I#54=FPU,I#55=USBD," \ + "I#56=UARTE1,I#57=QSPI,I#58=CRYPTOCELL,I#61=PWM3,I#63=SPIM3" + +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* __MCU_MCU_H_ */ diff --git a/lib/bt/host/nimble/nimble/babblesim/hw/mcu/nordic/nrf52_bsim/include/mcu/mcu_sim.h b/lib/bt/host/nimble/nimble/babblesim/hw/mcu/nordic/nrf52_bsim/include/mcu/mcu_sim.h new file mode 100644 index 00000000..26f6cb98 --- /dev/null +++ b/lib/bt/host/nimble/nimble/babblesim/hw/mcu/nordic/nrf52_bsim/include/mcu/mcu_sim.h @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +#ifndef __MCU_SIM_H__ +#define __MCU_SIM_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +extern char *native_flash_file; +extern char *native_uart_log_file; +extern const char *native_uart_dev_strs[]; + +void mcu_sim_parse_args(int argc, char **argv); + +void static inline hal_debug_break(void) {} + +#ifdef __cplusplus +} +#endif + +#endif /* __MCU_SIM_H__ */ diff --git a/lib/bt/host/nimble/nimble/babblesim/hw/mcu/nordic/nrf52_bsim/include/mcu/native_bsp.h b/lib/bt/host/nimble/nimble/babblesim/hw/mcu/nordic/nrf52_bsim/include/mcu/native_bsp.h new file mode 100644 index 00000000..dd794871 --- /dev/null +++ b/lib/bt/host/nimble/nimble/babblesim/hw/mcu/nordic/nrf52_bsim/include/mcu/native_bsp.h @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +#ifndef H_NATIVE_BSP_ +#define H_NATIVE_BSP_ + +#ifdef __cplusplus +extern "C" { +#endif + +extern const struct hal_flash native_flash_dev; + +int uart_set_dev(int port, const char *dev_str); + +#ifdef __cplusplus +} +#endif + +#endif /* H_NATIVE_BSP_ */ diff --git a/lib/bt/host/nimble/nimble/babblesim/hw/mcu/nordic/nrf52_bsim/include/mcu/nrf52_clock.h b/lib/bt/host/nimble/nimble/babblesim/hw/mcu/nordic/nrf52_bsim/include/mcu/nrf52_clock.h new file mode 100644 index 00000000..d86aa98b --- /dev/null +++ b/lib/bt/host/nimble/nimble/babblesim/hw/mcu/nordic/nrf52_bsim/include/mcu/nrf52_clock.h @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_NRF52_CLOCK_ +#define H_NRF52_CLOCK_ + +#ifdef __cplusplus + extern "C" { +#endif + +/** + * Request HFXO clock be turned on. Note that each request must have a + * corresponding release. + * + * @return int 0: hfxo was already on. 1: hfxo was turned on. + */ +int nrf52_clock_hfxo_request(void); + +/** + * Release the HFXO; caller no longer needs the HFXO to be turned on. Each call + * to release should have been preceeded by a corresponding call to request the + * HFXO + * + * + * @return int 0: HFXO not stopped by this call (others using it) 1: HFXO + * stopped. + */ +int nrf52_clock_hfxo_release(void); + +#ifdef __cplusplus +} +#endif + +#endif /* H_NRF52_CLOCK_ */ diff --git a/lib/bt/host/nimble/nimble/babblesim/hw/mcu/nordic/nrf52_bsim/include/mcu/nrf52_hal.h b/lib/bt/host/nimble/nimble/babblesim/hw/mcu/nordic/nrf52_bsim/include/mcu/nrf52_hal.h new file mode 100644 index 00000000..df9a0165 --- /dev/null +++ b/lib/bt/host/nimble/nimble/babblesim/hw/mcu/nordic/nrf52_bsim/include/mcu/nrf52_hal.h @@ -0,0 +1,101 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_NRF52_HAL_ +#define H_NRF52_HAL_ + +#include "cmsis.h" + +#ifdef __cplusplus + extern "C" { +#endif + +/* Helper functions to enable/disable interrupts. */ +#define __HAL_DISABLE_INTERRUPTS(x) \ + do { \ + x = __get_PRIMASK(); \ + __disable_irq(); \ + } while(0); + +#define __HAL_ENABLE_INTERRUPTS(x) \ + do { \ + if (!x) { \ + __enable_irq(); \ + } \ + } while(0); + +struct nrf52_uart_cfg { + int8_t suc_pin_tx; /* pins for IO */ + int8_t suc_pin_rx; + int8_t suc_pin_rts; + int8_t suc_pin_cts; +}; +const struct nrf52_uart_cfg *bsp_uart_config(void); + +struct nrf52_hal_i2c_cfg { + int scl_pin; + int sda_pin; + uint32_t i2c_frequency; +}; +struct hal_flash; +extern const struct hal_flash nrf52k_flash_dev; +extern const struct hal_flash nrf52k_qspi_dev; + +/* SPI configuration (used for both master and slave) */ +struct nrf52_hal_spi_cfg { + uint8_t sck_pin; + uint8_t mosi_pin; + uint8_t miso_pin; + uint8_t ss_pin; +}; + +/* + * GPIO pin mapping + * + * The logical GPIO pin numbers (0 to N) are mapped to ports in the following + * manner: + * pins 0 - 31: Port 0 + * pins 32 - 48: Port 1. + * + * The nrf52832 has only one port with 32 pins. The nrf52840 has 48 pins and + * uses two ports. + * + * NOTE: in order to save code space, there is no checking done to see if the + * user specifies a pin that is not used by the processor. If an invalid pin + * number is used unexpected and/or erroneous behavior will result. + */ +#if defined(NRF52832_XXAA) || defined(NRF52810_XXAA) || defined(NRF52811_XXAA) +#define HAL_GPIO_INDEX(pin) (pin) +#define HAL_GPIO_PORT(pin) (NRF_P0) +#define HAL_GPIO_MASK(pin) (1 << pin) +#define HAL_GPIOTE_PIN_MASK GPIOTE_CONFIG_PSEL_Msk +#endif + +#ifdef NRF52840_XXAA +#define HAL_GPIO_INDEX(pin) ((pin) & 0x1F) +#define HAL_GPIO_PORT(pin) ((pin) > 31 ? NRF_P1 : NRF_P0) +#define HAL_GPIO_MASK(pin) (1 << HAL_GPIO_INDEX(pin)) +#define HAL_GPIOTE_PIN_MASK (0x3FUL << GPIOTE_CONFIG_PSEL_Pos) +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* H_NRF52_HAL_ */ diff --git a/lib/bt/host/nimble/nimble/babblesim/hw/mcu/nordic/nrf52_bsim/include/mcu/nrf52_periph.h b/lib/bt/host/nimble/nimble/babblesim/hw/mcu/nordic/nrf52_bsim/include/mcu/nrf52_periph.h new file mode 100644 index 00000000..0e49371b --- /dev/null +++ b/lib/bt/host/nimble/nimble/babblesim/hw/mcu/nordic/nrf52_bsim/include/mcu/nrf52_periph.h @@ -0,0 +1,33 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_NRF52_PERIPH_ +#define H_NRF52_PERIPH_ + +#ifdef __cplusplus + extern "C" { +#endif + +void nrf52_periph_create(void); + +#ifdef __cplusplus +} +#endif + +#endif /* H_NRF52_PERIPH_ */ diff --git a/lib/bt/host/nimble/nimble/babblesim/hw/mcu/nordic/nrf52_bsim/pkg.yml b/lib/bt/host/nimble/nimble/babblesim/hw/mcu/nordic/nrf52_bsim/pkg.yml new file mode 100644 index 00000000..99e59a26 --- /dev/null +++ b/lib/bt/host/nimble/nimble/babblesim/hw/mcu/nordic/nrf52_bsim/pkg.yml @@ -0,0 +1,32 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +pkg.name: babblesim/hw/mcu/nordic/nrf52_bsim +pkg.description: nRF52 on BabbleSim +pkg.author: "Apache Mynewt " +pkg.homepage: "http://mynewt.apache.org/" + +pkg.lflags: + - -lpthread + +pkg.deps: + - "babblesim/core" + +pkg.deps.BLE_CONTROLLER: + - "@apache-mynewt-nimble/nimble/drivers/nrf52" diff --git a/lib/bt/host/nimble/nimble/babblesim/hw/mcu/nordic/nrf52_bsim/src/hal_flash.c b/lib/bt/host/nimble/nimble/babblesim/hw/mcu/nordic/nrf52_bsim/src/hal_flash.c new file mode 100644 index 00000000..b39b5158 --- /dev/null +++ b/lib/bt/host/nimble/nimble/babblesim/hw/mcu/nordic/nrf52_bsim/src/hal_flash.c @@ -0,0 +1,289 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "os/mynewt.h" + +#include "hal/hal_flash_int.h" +#include "mcu/mcu_sim.h" + +char *native_flash_file; +static int file = -1; +static void *file_loc; + +static int native_flash_init(const struct hal_flash *dev); +static int native_flash_read(const struct hal_flash *dev, uint32_t address, + void *dst, uint32_t length); +static int native_flash_write(const struct hal_flash *dev, uint32_t address, + const void *src, uint32_t length); +static int native_flash_erase_sector(const struct hal_flash *dev, + uint32_t sector_address); +static int native_flash_sector_info(const struct hal_flash *dev, int idx, + uint32_t *address, uint32_t *size); + +static const struct hal_flash_funcs native_flash_funcs = { + .hff_read = native_flash_read, + .hff_write = native_flash_write, + .hff_erase_sector = native_flash_erase_sector, + .hff_sector_info = native_flash_sector_info, + .hff_init = native_flash_init +}; + +#if MYNEWT_VAL(MCU_FLASH_STYLE_ST) +static const uint32_t native_flash_sectors[] = { + 0x00000000, /* 16 * 1024 */ + 0x00004000, /* 16 * 1024 */ + 0x00008000, /* 16 * 1024 */ + 0x0000c000, /* 16 * 1024 */ + 0x00010000, /* 64 * 1024 */ + 0x00020000, /* 128 * 1024 */ + 0x00040000, /* 128 * 1024 */ + 0x00060000, /* 128 * 1024 */ + 0x00080000, /* 128 * 1024 */ + 0x000a0000, /* 128 * 1024 */ + 0x000c0000, /* 128 * 1024 */ + 0x000e0000, /* 128 * 1024 */ +}; +#elif MYNEWT_VAL(MCU_FLASH_STYLE_NORDIC) +static uint32_t native_flash_sectors[1024 * 1024 / 2048]; +#else +#error "Need to specify either MCU_FLASH_STYLE_NORDIC or MCU_FLASH_STYLE_ST" +#endif + +#define FLASH_NUM_AREAS (int)(sizeof native_flash_sectors / \ + sizeof native_flash_sectors[0]) + +const struct hal_flash native_flash_dev = { + .hf_itf = &native_flash_funcs, + .hf_base_addr = 0, + .hf_size = 1024 * 1024, + .hf_sector_cnt = FLASH_NUM_AREAS, + .hf_align = MYNEWT_VAL(MCU_FLASH_MIN_WRITE_SIZE), + .hf_erased_val = 0xff, +}; + +static void +flash_native_erase(uint32_t addr, uint32_t len) +{ + memset(file_loc + addr, 0xff, len); +} + +static void +flash_native_file_open(char *name) +{ + int created = 0; + char tmpl[] = "/tmp/native_flash.XXXXXX"; + + extern int ftruncate(int fd, off_t length); + + if (file != -1) { + close(file); + file = -1; + } + + if (name) { + file = open(name, O_RDWR); + if (file < 0) { + file = open(name, O_RDWR | O_CREAT, 0660); + assert(file > 0); + created = 1; + } + } else { + file = mkstemp(tmpl); + assert(file > 0); + created = 1; + } + + if (created) { + if (ftruncate(file, native_flash_dev.hf_size) < 0) { + assert(0); + } + } + + if (file_loc != NULL) { + munmap(file_loc, native_flash_dev.hf_size); + } + + file_loc = mmap(0, native_flash_dev.hf_size, + PROT_READ | PROT_WRITE, MAP_SHARED, file, 0); + assert(file_loc != MAP_FAILED); + if (created) { + flash_native_erase(0, native_flash_dev.hf_size); + } + + /* If using a temporary file, unlink it immediately. */ + if (name == NULL) { + remove(tmpl); + } +} + +static void +flash_native_ensure_file_open(void) +{ + if (file == 0) { + flash_native_file_open(NULL); + } +} + +static int +flash_native_write_internal(uint32_t address, const void *src, uint32_t length, + int allow_overwrite) +{ + static uint8_t buf[256]; + uint32_t cur; + uint32_t end; + int chunk_sz; + int rc; + int i; + + if (length == 0) { + return 0; + } + + end = address + length; + + flash_native_ensure_file_open(); + + cur = address; + while (cur < end) { + if (end - cur < sizeof buf) { + chunk_sz = end - cur; + } else { + chunk_sz = sizeof buf; + } + + /* Ensure data is not being overwritten. */ + if (!allow_overwrite) { + rc = native_flash_read(NULL, cur, buf, chunk_sz); + assert(rc == 0); + for (i = 0; i < chunk_sz; i++) { + assert(buf[i] == 0xff); + } + } + + cur += chunk_sz; + } + + memcpy((char *)file_loc + address, src, length); + + return 0; +} + +static int +native_flash_write(const struct hal_flash *dev, uint32_t address, + const void *src, uint32_t length) +{ + assert(address % native_flash_dev.hf_align == 0); + return flash_native_write_internal(address, src, length, 0); +} + +int +flash_native_memset(uint32_t offset, uint8_t c, uint32_t len) +{ + memset(file_loc + offset, c, len); + return 0; +} + +static int +native_flash_read(const struct hal_flash *dev, uint32_t address, void *dst, + uint32_t length) +{ + flash_native_ensure_file_open(); + memcpy(dst, (char *)file_loc + address, length); + + return 0; +} + +static int +find_area(uint32_t address) +{ + int i; + + for (i = 0; i < FLASH_NUM_AREAS; i++) { + if (native_flash_sectors[i] == address) { + return i; + } + } + + return -1; +} + +static int +flash_sector_len(int sector) +{ + uint32_t end; + + if (sector == FLASH_NUM_AREAS - 1) { + end = native_flash_dev.hf_size + native_flash_sectors[0]; + } else { + end = native_flash_sectors[sector + 1]; + } + return end - native_flash_sectors[sector]; +} + +static int +native_flash_erase_sector(const struct hal_flash *dev, uint32_t sector_address) +{ + int area_id; + uint32_t len; + + flash_native_ensure_file_open(); + + area_id = find_area(sector_address); + if (area_id == -1) { + return -1; + } + len = flash_sector_len(area_id); + flash_native_erase(sector_address, len); + return 0; +} + +static int +native_flash_sector_info(const struct hal_flash *dev, int idx, + uint32_t *address, uint32_t *size) +{ + assert(idx < FLASH_NUM_AREAS); + + *address = native_flash_sectors[idx]; + *size = flash_sector_len(idx); + return 0; +} + +static int +native_flash_init(const struct hal_flash *dev) +{ + //if (native_flash_file) { + flash_native_file_open(native_flash_file); + //} +#if MYNEWT_VAL(MCU_FLASH_STYLE_NORDIC) + int i; + + for (i = 0; i < FLASH_NUM_AREAS; i++) { + native_flash_sectors[i] = i * 2048; + } +#endif + return 0; +} diff --git a/lib/bt/host/nimble/nimble/babblesim/hw/mcu/nordic/nrf52_bsim/src/hal_hw_id.c b/lib/bt/host/nimble/nimble/babblesim/hw/mcu/nordic/nrf52_bsim/src/hal_hw_id.c new file mode 100644 index 00000000..09e39801 --- /dev/null +++ b/lib/bt/host/nimble/nimble/babblesim/hw/mcu/nordic/nrf52_bsim/src/hal_hw_id.c @@ -0,0 +1,73 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#include + +#include "hal/hal_bsp.h" + +#include "hal_native_priv.h" + +#ifndef min +#define min(a, b) ((a)<(b)?(a):(b)) +#endif + +static uint8_t hal_hw_id[HAL_BSP_MAX_ID_LEN]; +static uint8_t hal_hw_id_len; + +int +hal_bsp_hw_id_len(void) +{ + if (hal_hw_id_len != 0) { + return hal_hw_id_len; + } else { + return HAL_BSP_MAX_ID_LEN; + } +} + +/* + * This can be used as the unique hardware identifier for the platform, as + * it's supposed to be unique for this particular MCU. + */ +int +hal_bsp_hw_id(uint8_t *id, int max_len) +{ + if (hal_hw_id_len) { + if (max_len > hal_hw_id_len) { + max_len = hal_hw_id_len; + } + memcpy(id, hal_hw_id, max_len); + return max_len; + } + if (max_len > HAL_BSP_MAX_ID_LEN) { + max_len = HAL_BSP_MAX_ID_LEN; + } + memset(id, 0x42, max_len); + return max_len; +} + +void +hal_bsp_set_hw_id(const uint8_t *id, int len) +{ + if (len > HAL_BSP_MAX_ID_LEN) { + len = HAL_BSP_MAX_ID_LEN; + } + hal_hw_id_len = len; + memcpy(hal_hw_id, id, len); +} diff --git a/lib/bt/host/nimble/nimble/babblesim/hw/mcu/nordic/nrf52_bsim/src/hal_native_priv.h b/lib/bt/host/nimble/nimble/babblesim/hw/mcu/nordic/nrf52_bsim/src/hal_native_priv.h new file mode 100644 index 00000000..10600537 --- /dev/null +++ b/lib/bt/host/nimble/nimble/babblesim/hw/mcu/nordic/nrf52_bsim/src/hal_native_priv.h @@ -0,0 +1,27 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef __HAL_NATIVE_PRIV_H__ +#define __HAL_NATIVE_PRIV_H__ + +#include + +void hal_bsp_set_hw_id(const uint8_t *id, int len); + +#endif /* __HAL_NATIVE_PRIV_H__ */ diff --git a/lib/bt/host/nimble/nimble/babblesim/hw/mcu/nordic/nrf52_bsim/src/hal_os_tick.c b/lib/bt/host/nimble/nimble/babblesim/hw/mcu/nordic/nrf52_bsim/src/hal_os_tick.c new file mode 100644 index 00000000..a2d7170a --- /dev/null +++ b/lib/bt/host/nimble/nimble/babblesim/hw/mcu/nordic/nrf52_bsim/src/hal_os_tick.c @@ -0,0 +1,226 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#include "os/mynewt.h" +#include "hal/hal_os_tick.h" +#include "nrf.h" +#include "mcu/cmsis_nvic.h" +#include "mcu/mcu_sim.h" +#include +#include +#include +#include +#include + +/* The OS scheduler requires a low-frequency timer. */ +#if MYNEWT_VAL(OS_SCHEDULING) && !MYNEWT_VAL(MCU_LFCLK_SOURCE) + #error The OS scheduler requires a low-frequency timer; configure MCU_LFCLK_SOURCE +#endif + +#define RTC_FREQ 32768 /* in Hz */ +#define OS_TICK_TIMER NRF_RTC1 +#define OS_TICK_IRQ RTC1_IRQn +#define OS_TICK_CMPREG 3 /* generate timer interrupt */ +#define RTC_COMPARE_INT_MASK(ccreg) (1UL << ((ccreg) + 16)) + +struct hal_os_tick +{ + int ticks_per_ostick; + os_time_t max_idle_ticks; + uint32_t lastocmp; +}; + +struct hal_os_tick g_hal_os_tick; + +/* + * Implement (x - y) where the range of both 'x' and 'y' is limited to 24-bits. + * + * For example: + * + * sub24(0, 0xffffff) = 1 + * sub24(0xffffff, 0xfffffe) = 1 + * sub24(0xffffff, 0) = -1 + * sub24(0x7fffff, 0) = 8388607 + * sub24(0x800000, 0) = -8388608 + */ +static inline int +sub24(uint32_t x, uint32_t y) +{ + int result; + + assert(x <= 0xffffff); + assert(y <= 0xffffff); + + result = x - y; + if (result & 0x800000) { + return (result | 0xff800000); + } else { + return (result & 0x007fffff); + } +} + +static inline uint32_t +nrf52_os_tick_counter(void) +{ + return nrf_rtc_counter_get(OS_TICK_TIMER); +} + +static inline void +nrf52_os_tick_set_ocmp(uint32_t ocmp) +{ + int delta; + uint32_t counter; + + OS_ASSERT_CRITICAL(); + while (1) { + ocmp &= 0xffffff; + nrf_rtc_cc_set(OS_TICK_TIMER, OS_TICK_CMPREG, ocmp); + counter = nrf52_os_tick_counter(); + /* + * From nRF52 Product specification + * + * - If Counter is 'N' writing (N) or (N + 1) to CC register + * may not trigger a compare event. + * + * - If Counter is 'N' writing (N + 2) to CC register is guaranteed + * to trigger a compare event at 'N + 2'. + */ + delta = sub24(ocmp, counter); + if (delta > 2) { + break; + } + ocmp += g_hal_os_tick.ticks_per_ostick; + } +} + +static void +nrf52_timer_handler(void) +{ + int delta; + int ticks; + os_sr_t sr; + uint32_t counter; + + os_trace_isr_enter(); + OS_ENTER_CRITICAL(sr); + + /* Calculate elapsed ticks and advance OS time. */ + + counter = nrf52_os_tick_counter(); + delta = sub24(counter, g_hal_os_tick.lastocmp); + ticks = delta / g_hal_os_tick.ticks_per_ostick; + os_time_advance(ticks); + + /* Clear timer interrupt */ + OS_TICK_TIMER->EVENTS_COMPARE[OS_TICK_CMPREG] = 0; + + /* Update the time associated with the most recent tick */ + g_hal_os_tick.lastocmp = (g_hal_os_tick.lastocmp + + (ticks * g_hal_os_tick.ticks_per_ostick)) & 0xffffff; + + /* Update the output compare to interrupt at the next tick */ + nrf52_os_tick_set_ocmp(g_hal_os_tick.lastocmp + g_hal_os_tick.ticks_per_ostick); + + OS_EXIT_CRITICAL(sr); + os_trace_isr_exit(); +} + +/* Wait For Interrupt */ +void +__WFI(void) +{ + while (hw_irq_ctrl_get_irq_status() == 0) { + tm_tick(); + } +} + +void +os_tick_idle(os_time_t ticks) +{ + uint32_t ocmp; + + OS_ASSERT_CRITICAL(); + + if (ticks > 0) { + /* + * Enter tickless regime during long idle durations. + */ + if (ticks > g_hal_os_tick.max_idle_ticks) { + ticks = g_hal_os_tick.max_idle_ticks; + } + ocmp = g_hal_os_tick.lastocmp + (ticks*g_hal_os_tick.ticks_per_ostick); + nrf52_os_tick_set_ocmp(ocmp); + } + + __WFI(); + + if (ticks > 0) { + /* + * Update OS time before anything else when coming out of + * the tickless regime. + */ + nrf52_timer_handler(); + } +} + +extern void nrf_rtc_regw_sideeffects(int i); + +void +os_tick_init(uint32_t os_ticks_per_sec, int prio) +{ + uint32_t sr; + + assert(RTC_FREQ % os_ticks_per_sec == 0); + + g_hal_os_tick.lastocmp = 0; + g_hal_os_tick.ticks_per_ostick = RTC_FREQ / os_ticks_per_sec; + + /* + * The maximum number of OS ticks allowed to elapse during idle is + * limited to 1/4th the number of timer ticks before the 24-bit counter + * rolls over. + */ + g_hal_os_tick.max_idle_ticks = (1UL << 22) / g_hal_os_tick.ticks_per_ostick; + + /* disable interrupts */ + OS_ENTER_CRITICAL(sr); + + /* Set isr in vector table and enable interrupt */ + NVIC_SetPriority(OS_TICK_IRQ, prio); + NVIC_SetVector(OS_TICK_IRQ, (uint32_t)nrf52_timer_handler); + NVIC_EnableIRQ(OS_TICK_IRQ); + + /* + * Program the OS_TICK_TIMER to operate at 32KHz and trigger an output + * compare interrupt at a rate of 'os_ticks_per_sec'. + */ + nrf_rtc_task_trigger(OS_TICK_TIMER, NRF_RTC_TASK_STOP); + nrf_rtc_task_trigger(OS_TICK_TIMER, NRF_RTC_TASK_CLEAR); + nrf_rtc_event_disable(OS_TICK_TIMER, 0xffffffff); + nrf_rtc_int_disable(OS_TICK_TIMER, 0xffffffff); + nrf_rtc_int_enable(OS_TICK_TIMER, RTC_COMPARE_INT_MASK(OS_TICK_CMPREG)); + + OS_TICK_TIMER->EVENTS_COMPARE[OS_TICK_CMPREG] = 0; + nrf_rtc_cc_set(OS_TICK_TIMER, OS_TICK_CMPREG, g_hal_os_tick.ticks_per_ostick); + + nrf_rtc_task_trigger(OS_TICK_TIMER, NRF_RTC_TASK_START); + + OS_EXIT_CRITICAL(sr); +} diff --git a/lib/bt/host/nimble/nimble/babblesim/hw/mcu/nordic/nrf52_bsim/src/hal_reset_cause.c b/lib/bt/host/nimble/nimble/babblesim/hw/mcu/nordic/nrf52_bsim/src/hal_reset_cause.c new file mode 100644 index 00000000..32182c1e --- /dev/null +++ b/lib/bt/host/nimble/nimble/babblesim/hw/mcu/nordic/nrf52_bsim/src/hal_reset_cause.c @@ -0,0 +1,47 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#include "hal/hal_system.h" + +enum hal_reset_reason +hal_reset_cause(void) +{ + static enum hal_reset_reason reason; + uint32_t reg; + + if (reason) { + return reason; + } + reg = NRF_POWER->RESETREAS; + + if (reg & (POWER_RESETREAS_DOG_Msk | POWER_RESETREAS_LOCKUP_Msk)) { + reason = HAL_RESET_WATCHDOG; + } else if (reg & POWER_RESETREAS_SREQ_Msk) { + reason = HAL_RESET_SOFT; + } else if (reg & POWER_RESETREAS_RESETPIN_Msk) { + reason = HAL_RESET_PIN; + } else if (reg & POWER_RESETREAS_OFF_Msk) { + reason = HAL_RESET_SYS_OFF_INT; + } else { + reason = HAL_RESET_POR; /* could also be brownout */ + } + NRF_POWER->RESETREAS = reg; + return reason; +} diff --git a/lib/bt/host/nimble/nimble/babblesim/hw/mcu/nordic/nrf52_bsim/src/hal_system.c b/lib/bt/host/nimble/nimble/babblesim/hw/mcu/nordic/nrf52_bsim/src/hal_system.c new file mode 100644 index 00000000..4f46f346 --- /dev/null +++ b/lib/bt/host/nimble/nimble/babblesim/hw/mcu/nordic/nrf52_bsim/src/hal_system.c @@ -0,0 +1,128 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "syscfg/syscfg.h" +#include "hal/hal_system.h" +#include "hal/hal_debug.h" +#include "nrf.h" +#include "cmsis.h" +#include "mcu/mcu_sim.h" +#include "hal/nrf_clock.h" + +/** + * Function called at startup. Called after BSS and .data initialized but + * prior to the _start function. + * + * NOTE: this function is called by both the bootloader and the application. + * If you add code here that you do not want executed in either case you need + * to conditionally compile it using the config variable BOOT_LOADER (will + * be set to 1 in case of bootloader build) + * + */ +void +hal_system_init(void) +{ +#if MYNEWT_VAL(MCU_DCDC_ENABLED) + NRF_POWER->DCDCEN = 1; +#endif +} + +void +hal_system_reset(void) +{ + +#if MYNEWT_VAL(HAL_SYSTEM_RESET_CB) + hal_system_reset_cb(); +#endif + + while (1) { + HAL_DEBUG_BREAK(); + NVIC_SystemReset(); + } +} + +int +hal_debugger_connected(void) +{ + return 0; +} + +/** + * hal system clock start + * + * Makes sure the LFCLK and/or HFCLK is started. + */ +void +hal_system_clock_start(void) +{ +#if MYNEWT_VAL(MCU_LFCLK_SOURCE) + uint32_t regmsk; + uint32_t regval; + uint32_t clksrc; + + regmsk = CLOCK_LFCLKSTAT_STATE_Msk | CLOCK_LFCLKSTAT_SRC_Msk; + regval = CLOCK_LFCLKSTAT_STATE_Running << CLOCK_LFCLKSTAT_STATE_Pos; + +#if MYNEWT_VAL_CHOICE(MCU_LFCLK_SOURCE, LFXO) + regval |= CLOCK_LFCLKSTAT_SRC_Xtal << CLOCK_LFCLKSTAT_SRC_Pos; + clksrc = CLOCK_LFCLKSRC_SRC_Xtal; +#elif MYNEWT_VAL_CHOICE(MCU_LFCLK_SOURCE, LFSYNTH) + regval |= CLOCK_LFCLKSTAT_SRC_Synth << CLOCK_LFCLKSTAT_SRC_Pos; + clksrc = CLOCK_LFCLKSRC_SRC_Synth; +#elif MYNEWT_VAL_CHOICE(MCU_LFCLK_SOURCE, LFRC) + regval |= CLOCK_LFCLKSTAT_SRC_RC << CLOCK_LFCLKSTAT_SRC_Pos; + clksrc = CLOCK_LFCLKSRC_SRC_RC; +#else + #error Unknown LFCLK source selected +#endif + +#if MYNEWT_VAL_CHOICE(MCU_LFCLK_SOURCE, LFSYNTH) + /* Must turn on HFLCK for synthesized 32768 crystal */ + nrf52_clock_hfxo_request(); +#else + /* Make sure HFCLK is stopped */ + nrf_clock_task_trigger(NRF_CLOCK, NRF_CLOCK_TASK_HFCLKSTOP); +#endif + + /* Check if this clock source is already running */ + if ((NRF_CLOCK_regs.LFCLKSTAT & regmsk) != regval) { + + nrf_clock_task_trigger(NRF_CLOCK, NRF_CLOCK_TASK_LFCLKSTOP); + NRF_CLOCK_regs.EVENTS_LFCLKSTARTED = 0; + NRF_CLOCK_regs.LFCLKSRC = clksrc; + nrf_clock_task_trigger(NRF_CLOCK, NRF_CLOCK_TASK_LFCLKSTART); + + /* Wait here till started! */ +// while (1) { +// if (NRF_CLOCK_regs.EVENTS_LFCLKSTARTED) { +// if ((NRF_CLOCK_regs.LFCLKSTAT & regmsk) == regval) { +// break; +// } +// } +// } + } +#endif +} + + +void* +NRF_RADIO_BASE_FUN(void) +{ + return NULL; +} diff --git a/lib/bt/host/nimble/nimble/babblesim/hw/mcu/nordic/nrf52_bsim/src/hal_timer.c b/lib/bt/host/nimble/nimble/babblesim/hw/mcu/nordic/nrf52_bsim/src/hal_timer.c new file mode 100644 index 00000000..0e3d914c --- /dev/null +++ b/lib/bt/host/nimble/nimble/babblesim/hw/mcu/nordic/nrf52_bsim/src/hal_timer.c @@ -0,0 +1,949 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#include +#include +#include +#include +#include "os/mynewt.h" +#include "mcu/cmsis_nvic.h" +#include "hal/hal_timer.h" +#include "nrf.h" +#include "mcu/nrf52_hal.h" +#include "mcu/nrf52_clock.h" +#include "hal/nrf_timer.h" + +/* IRQ prototype */ +typedef void (*hal_timer_irq_handler_t)(void); + +/* User CC 2 for reading counter, CC 3 for timer isr */ +#define NRF_TIMER_CC_READ (NRF_TIMER_CC_CHANNEL2) +#define NRF_TIMER_CC_INT (3) + +/* Output compare 2 used for RTC timers */ +#define NRF_RTC_TIMER_CC_INT (2) + +/* Maximum number of hal timers used */ +#define NRF52_HAL_TIMER_MAX (6) + +/* Maximum timer frequency */ +#define NRF52_MAX_TIMER_FREQ (16000000) + +struct nrf52_hal_timer { + uint8_t tmr_enabled; + uint8_t tmr_irq_num; + uint8_t tmr_rtc; + uint8_t tmr_pad; + uint32_t tmr_cntr; + uint32_t timer_isrs; + uint32_t tmr_freq; + void *tmr_reg; + TAILQ_HEAD(hal_timer_qhead, hal_timer) hal_timer_q; +}; + +#if MYNEWT_VAL(TIMER_0) +struct nrf52_hal_timer nrf52_hal_timer0; +#endif +#if MYNEWT_VAL(TIMER_1) +struct nrf52_hal_timer nrf52_hal_timer1; +#endif +#if MYNEWT_VAL(TIMER_2) +struct nrf52_hal_timer nrf52_hal_timer2; +#endif +#if MYNEWT_VAL(TIMER_3) +struct nrf52_hal_timer nrf52_hal_timer3; +#endif +#if MYNEWT_VAL(TIMER_4) +struct nrf52_hal_timer nrf52_hal_timer4; +#endif +#if MYNEWT_VAL(TIMER_5) +struct nrf52_hal_timer nrf52_hal_timer5; +#endif + +static const struct nrf52_hal_timer *nrf52_hal_timers[NRF52_HAL_TIMER_MAX] = { +#if MYNEWT_VAL(TIMER_0) + &nrf52_hal_timer0, +#else + NULL, +#endif +#if MYNEWT_VAL(TIMER_1) + &nrf52_hal_timer1, +#else + NULL, +#endif +#if MYNEWT_VAL(TIMER_2) + &nrf52_hal_timer2, +#else + NULL, +#endif +#if MYNEWT_VAL(TIMER_3) + &nrf52_hal_timer3, +#else + NULL, +#endif +#if MYNEWT_VAL(TIMER_4) + &nrf52_hal_timer4, +#else + NULL, +#endif +#if MYNEWT_VAL(TIMER_5) + &nrf52_hal_timer5 +#else + NULL +#endif +}; + +/* Resolve timer number into timer structure */ +#define NRF52_HAL_TIMER_RESOLVE(__n, __v) \ + if ((__n) >= NRF52_HAL_TIMER_MAX) { \ + rc = EINVAL; \ + goto err; \ + } \ + (__v) = (struct nrf52_hal_timer *) nrf52_hal_timers[(__n)]; \ + if ((__v) == NULL) { \ + rc = EINVAL; \ + goto err; \ + } + +/* Interrupt mask for interrupt enable/clear */ +#define NRF_TIMER_INT_MASK(x) ((1 << (uint32_t)(x)) << 16) + +static uint32_t +nrf_read_timer_cntr(NRF_TIMER_Type *hwtimer) +{ + uint32_t tcntr; + + /* Force a capture of the timer into 'cntr' capture channel; read it */ + nrf_timer_task_trigger(hwtimer, NRF_TIMER_TASK_CAPTURE2); + tcntr = hwtimer->CC[NRF_TIMER_CC_READ]; + + return tcntr; +} + +/** + * nrf timer set ocmp + * + * Set the OCMP used by the timer to the desired expiration tick + * + * NOTE: Must be called with interrupts disabled. + * + * @param timer Pointer to timer. + */ +static void +nrf_timer_set_ocmp(struct nrf52_hal_timer *bsptimer, uint32_t expiry) +{ + int32_t delta_t; + uint32_t temp; + uint32_t cntr; + NRF_TIMER_Type *hwtimer; + NRF_RTC_Type *rtctimer; + + if (bsptimer->tmr_rtc) { + rtctimer = (NRF_RTC_Type *)bsptimer->tmr_reg; + nrf_rtc_int_disable(rtctimer, NRF_TIMER_INT_MASK(NRF_RTC_TIMER_CC_INT)); + temp = bsptimer->tmr_cntr; + cntr = nrf_rtc_counter_get(rtctimer); + if (rtctimer->EVENTS_OVRFLW) { + temp += (1UL << 24); + cntr = nrf_rtc_counter_get(rtctimer); + } + temp |= cntr; + delta_t = (int32_t)(expiry - temp); + + /* + * The nRF52xxx documentation states that COMPARE event is guaranteed + * only if value written to CC register is at least 2 greater than the + * current counter value. We also need to account for possible extra + * tick during calculations so effectively any delta less than 3 needs + * to be handled differently. TICK event is used to have interrupt on + * each subsequent tick so we won't miss any and in case we detected + * mentioned extra tick during calculations, interrupt is triggered + * immediately. Delta 0 or less means we should always fire immediately. + */ + if (delta_t < 1) { + nrf_rtc_int_disable(rtctimer, RTC_INTENCLR_TICK_Msk); + NVIC_SetPendingIRQ(bsptimer->tmr_irq_num); + } else if (delta_t < 3 && 0) { + nrf_rtc_int_enable(rtctimer, RTC_INTENSET_TICK_Msk); + if (nrf_rtc_counter_get(rtctimer) != cntr) { + NVIC_SetPendingIRQ(bsptimer->tmr_irq_num); + } + } else { + nrf_rtc_int_disable(rtctimer, RTC_INTENCLR_TICK_Msk); + + if (delta_t < (1UL << 24)) { + nrf_rtc_cc_set(rtctimer, NRF_RTC_TIMER_CC_INT, expiry & 0x00ffffff); + } else { + /* CC too far ahead. Just make sure we set compare far ahead */ + nrf_rtc_cc_set(rtctimer, NRF_RTC_TIMER_CC_INT, cntr + (1UL << 23)); + } + nrf_rtc_int_enable(rtctimer, NRF_TIMER_INT_MASK(NRF_RTC_TIMER_CC_INT)); + } + } else { + hwtimer = bsptimer->tmr_reg; + + /* Disable ocmp interrupt and set new value */ + nrf_timer_int_disable(hwtimer, NRF_TIMER_INT_MASK(NRF_TIMER_CC_INT)); + + /* Set output compare register to timer expiration */ + nrf_timer_cc_set(hwtimer, NRF_TIMER_CC_INT, expiry); + + /* Clear interrupt flag */ + hwtimer->EVENTS_COMPARE[NRF_TIMER_CC_INT] = 0; + + /* Enable the output compare interrupt */ + nrf_timer_int_enable(hwtimer, NRF_TIMER_INT_MASK(NRF_TIMER_CC_INT)); + + /* Force interrupt to occur as we may have missed it */ + if ((int32_t)(nrf_read_timer_cntr(hwtimer) - expiry) >= 0) { + NVIC_SetPendingIRQ(bsptimer->tmr_irq_num); + } + } +} + +/* Disable output compare used for timer */ +static void +nrf_timer_disable_ocmp(NRF_TIMER_Type *hwtimer) +{ + nrf_timer_int_disable(hwtimer, NRF_TIMER_INT_MASK(NRF_TIMER_CC_INT)); +} + +static void +nrf_rtc_disable_ocmp(NRF_RTC_Type *rtctimer) +{ + nrf_rtc_int_disable(rtctimer, NRF_TIMER_INT_MASK(NRF_RTC_TIMER_CC_INT)); + nrf_rtc_int_disable(rtctimer, RTC_INTENCLR_TICK_Msk); +} + +static uint32_t +hal_timer_read_bsptimer(struct nrf52_hal_timer *bsptimer) +{ + uint32_t low32; + uint32_t ctx; + uint32_t tcntr; + NRF_RTC_Type *rtctimer; + + rtctimer = (NRF_RTC_Type *)bsptimer->tmr_reg; + __HAL_DISABLE_INTERRUPTS(ctx); + tcntr = bsptimer->tmr_cntr; + low32 = nrf_rtc_counter_get(rtctimer); + if (rtctimer->EVENTS_OVRFLW) { + tcntr += (1UL << 24); + bsptimer->tmr_cntr = tcntr; + low32 = nrf_rtc_counter_get(rtctimer); + rtctimer->EVENTS_OVRFLW = 0; + NVIC_SetPendingIRQ(bsptimer->tmr_irq_num); + } + tcntr |= low32; + __HAL_ENABLE_INTERRUPTS(ctx); + + return tcntr; +} + +#if (MYNEWT_VAL(TIMER_0) || MYNEWT_VAL(TIMER_1) || MYNEWT_VAL(TIMER_2) || \ + MYNEWT_VAL(TIMER_3) || MYNEWT_VAL(TIMER_4) || MYNEWT_VAL(TIMER_5)) +/** + * hal timer chk queue + * + * + * @param bsptimer + */ +static void +hal_timer_chk_queue(struct nrf52_hal_timer *bsptimer) +{ + uint32_t tcntr; + uint32_t ctx; + struct hal_timer *timer; + + /* disable interrupts */ + __HAL_DISABLE_INTERRUPTS(ctx); + while ((timer = TAILQ_FIRST(&bsptimer->hal_timer_q)) != NULL) { + if (bsptimer->tmr_rtc) { + tcntr = hal_timer_read_bsptimer(bsptimer); + } else { + tcntr = nrf_read_timer_cntr(bsptimer->tmr_reg); + } + if ((int32_t)(tcntr - timer->expiry) >= 0) { + TAILQ_REMOVE(&bsptimer->hal_timer_q, timer, link); + timer->link.tqe_prev = NULL; + timer->cb_func(timer->cb_arg); + } else { + break; + } + } + + /* Any timers left on queue? If so, we need to set OCMP */ + timer = TAILQ_FIRST(&bsptimer->hal_timer_q); + if (timer) { + nrf_timer_set_ocmp(bsptimer, timer->expiry); + } else { + if (bsptimer->tmr_rtc) { + nrf_rtc_disable_ocmp((NRF_RTC_Type *)bsptimer->tmr_reg); + } else { + nrf_timer_disable_ocmp(bsptimer->tmr_reg); + } + } + __HAL_ENABLE_INTERRUPTS(ctx); +} +#endif + +/** + * hal timer irq handler + * + * Generic HAL timer irq handler. + * + * @param tmr + */ +/** + * hal timer irq handler + * + * This is the global timer interrupt routine. + * + */ +#if (MYNEWT_VAL(TIMER_0) || MYNEWT_VAL(TIMER_1) || MYNEWT_VAL(TIMER_2) || \ + MYNEWT_VAL(TIMER_3) || MYNEWT_VAL(TIMER_4)) + +static void +hal_timer_irq_handler(struct nrf52_hal_timer *bsptimer) +{ + uint32_t compare; + NRF_TIMER_Type *hwtimer; + + os_trace_isr_enter(); + + /* Check interrupt source. If set, clear them */ + hwtimer = bsptimer->tmr_reg; + compare = hwtimer->EVENTS_COMPARE[NRF_TIMER_CC_INT]; + if (compare) { + hwtimer->EVENTS_COMPARE[NRF_TIMER_CC_INT] = 0; + } + + /* XXX: make these stats? */ + /* Count # of timer isrs */ + ++bsptimer->timer_isrs; + + /* + * NOTE: we dont check the 'compare' variable here due to how the timer + * is implemented on this chip. There is no way to force an output + * compare, so if we are late setting the output compare (i.e. the timer + * counter is already passed the output compare value), we use the NVIC + * to set a pending interrupt. This means that there will be no compare + * flag set, so all we do is check to see if the compare interrupt is + * enabled. + */ + if (hwtimer->INTENCLR & NRF_TIMER_INT_MASK(NRF_TIMER_CC_INT)) { + hal_timer_chk_queue(bsptimer); + /* XXX: Recommended by nordic to make sure interrupts are cleared */ + compare = hwtimer->EVENTS_COMPARE[NRF_TIMER_CC_INT]; + } + + os_trace_isr_exit(); +} +#endif + +#if MYNEWT_VAL(TIMER_5) +static void +hal_rtc_timer_irq_handler(struct nrf52_hal_timer *bsptimer) +{ + uint32_t overflow; + uint32_t compare; + uint32_t tick; + NRF_RTC_Type *rtctimer; + + os_trace_isr_enter(); + + /* Check interrupt source. If set, clear them */ + rtctimer = (NRF_RTC_Type *)bsptimer->tmr_reg; + compare = rtctimer->EVENTS_COMPARE[NRF_RTC_TIMER_CC_INT]; + if (compare) { + rtctimer->EVENTS_COMPARE[NRF_RTC_TIMER_CC_INT] = 0; + } + + tick = rtctimer->EVENTS_TICK; + if (tick) { + rtctimer->EVENTS_TICK = 0; + } + + overflow = rtctimer->EVENTS_OVRFLW; + if (overflow) { + rtctimer->EVENTS_OVRFLW = 0; + bsptimer->tmr_cntr += (1UL << 24); + } + + /* Count # of timer isrs */ + ++bsptimer->timer_isrs; + + /* + * NOTE: we dont check the 'compare' variable here due to how the timer + * is implemented on this chip. There is no way to force an output + * compare, so if we are late setting the output compare (i.e. the timer + * counter is already passed the output compare value), we use the NVIC + * to set a pending interrupt. This means that there will be no compare + * flag set, so all we do is check to see if the compare interrupt is + * enabled. + */ + hal_timer_chk_queue(bsptimer); + + /* Recommended by nordic to make sure interrupts are cleared */ + compare = rtctimer->EVENTS_COMPARE[NRF_RTC_TIMER_CC_INT]; + + os_trace_isr_exit(); +} +#endif + +#if MYNEWT_VAL(TIMER_0) +void +nrf52_timer0_irq_handler(void) +{ + hal_timer_irq_handler(&nrf52_hal_timer0); +} +#endif + +#if MYNEWT_VAL(TIMER_1) +void +nrf52_timer1_irq_handler(void) +{ + hal_timer_irq_handler(&nrf52_hal_timer1); +} +#endif + +#if MYNEWT_VAL(TIMER_2) +void +nrf52_timer2_irq_handler(void) +{ + hal_timer_irq_handler(&nrf52_hal_timer2); +} +#endif + +#if MYNEWT_VAL(TIMER_3) +void +nrf52_timer3_irq_handler(void) +{ + hal_timer_irq_handler(&nrf52_hal_timer3); +} +#endif + +#if MYNEWT_VAL(TIMER_4) +void +nrf52_timer4_irq_handler(void) +{ + hal_timer_irq_handler(&nrf52_hal_timer4); +} +#endif + +#if MYNEWT_VAL(TIMER_5) +void +nrf52_timer5_irq_handler(void) +{ + hal_rtc_timer_irq_handler(&nrf52_hal_timer5); +} +#endif + +/** + * hal timer init + * + * Initialize platform specific timer items + * + * @param timer_num Timer number to initialize + * @param cfg Pointer to platform specific configuration + * + * @return int 0: success; error code otherwise + */ +int +hal_timer_init(int timer_num, void *cfg) +{ + int rc; + uint8_t irq_num; + struct nrf52_hal_timer *bsptimer; + void *hwtimer; + hal_timer_irq_handler_t irq_isr; + + NRF52_HAL_TIMER_RESOLVE(timer_num, bsptimer); + + /* If timer is enabled do not allow init */ + if (bsptimer->tmr_enabled) { + rc = EINVAL; + goto err; + } + + switch (timer_num) { +#if MYNEWT_VAL(TIMER_0) + case 0: + irq_num = TIMER0_IRQn; + hwtimer = NRF_TIMER0; + irq_isr = nrf52_timer0_irq_handler; + break; +#endif +#if MYNEWT_VAL(TIMER_1) + case 1: + irq_num = TIMER1_IRQn; + hwtimer = NRF_TIMER1; + irq_isr = nrf52_timer1_irq_handler; + break; +#endif +#if MYNEWT_VAL(TIMER_2) + case 2: + irq_num = TIMER2_IRQn; + hwtimer = NRF_TIMER2; + irq_isr = nrf52_timer2_irq_handler; + break; +#endif +#if MYNEWT_VAL(TIMER_3) + case 3: + irq_num = TIMER3_IRQn; + hwtimer = NRF_TIMER3; + irq_isr = nrf52_timer3_irq_handler; + break; +#endif +#if MYNEWT_VAL(TIMER_4) + case 4: + irq_num = TIMER4_IRQn; + hwtimer = NRF_TIMER4; + irq_isr = nrf52_timer4_irq_handler; + break; +#endif +#if MYNEWT_VAL(TIMER_5) + case 5: + irq_num = RTC0_IRQn; + hwtimer = NRF_RTC0; + irq_isr = nrf52_timer5_irq_handler; + bsptimer->tmr_rtc = 1; + break; +#endif + default: + hwtimer = NULL; + break; + } + + if (hwtimer == NULL) { + rc = EINVAL; + goto err; + } + + bsptimer->tmr_reg = hwtimer; + bsptimer->tmr_irq_num = irq_num; + + /* Disable IRQ, set priority and set vector in table */ + NVIC_DisableIRQ(irq_num); + NVIC_SetPriority(irq_num, (1 << __NVIC_PRIO_BITS) - 1); + NVIC_SetVector(irq_num, (uint32_t)irq_isr); + + return 0; + +err: + return rc; +} + +/** + * hal timer config + * + * Configure a timer to run at the desired frequency. This starts the timer. + * + * @param timer_num + * @param freq_hz + * + * @return int + */ +int +hal_timer_config(int timer_num, uint32_t freq_hz) +{ + int rc; + uint8_t prescaler; + uint32_t ctx; + uint32_t div; + uint32_t min_delta; + uint32_t max_delta; + struct nrf52_hal_timer *bsptimer; + NRF_TIMER_Type *hwtimer; +#if MYNEWT_VAL(TIMER_5) + NRF_RTC_Type *rtctimer; +#endif + + NRF52_HAL_TIMER_RESOLVE(timer_num, bsptimer); + +#if MYNEWT_VAL(TIMER_5) + if (timer_num == 5) { + /* NOTE: we only allow the RTC frequency to be set at 32768 */ + if (bsptimer->tmr_enabled || (freq_hz != 32768) || + (bsptimer->tmr_reg == NULL)) { + rc = EINVAL; + goto err; + } + + bsptimer->tmr_freq = freq_hz; + bsptimer->tmr_enabled = 1; + + __HAL_DISABLE_INTERRUPTS(ctx); + + rtctimer = (NRF_RTC_Type *)bsptimer->tmr_reg; + + /* Stop the timer first */ + nrf_rtc_task_trigger(rtctimer, NRF_RTC_TASK_STOP); + nrf_rtc_task_trigger(rtctimer, NRF_RTC_TASK_CLEAR); + + /* Always no prescaler */ + rtctimer->PRESCALER = 0; + + /* Clear overflow events and set overflow interrupt */ + rtctimer->EVENTS_OVRFLW = 0; + nrf_rtc_int_enable(rtctimer, RTC_INTENSET_OVRFLW_Msk); + + /* Start the timer */ + nrf_rtc_task_trigger(rtctimer, NRF_RTC_TASK_START); + /* Set isr in vector table and enable interrupt */ + NVIC_EnableIRQ(bsptimer->tmr_irq_num); + + __HAL_ENABLE_INTERRUPTS(ctx); + return 0; + } +#endif + + /* Set timer to desired frequency */ + div = NRF52_MAX_TIMER_FREQ / freq_hz; + + /* + * Largest prescaler is 2^9 and must make sure frequency not too high. + * If hwtimer is NULL it means that the timer was not initialized prior + * to call. + */ + if (bsptimer->tmr_enabled || (div == 0) || (div > 512) || + (bsptimer->tmr_reg == NULL)) { + rc = EINVAL; + goto err; + } + + if (div == 1) { + prescaler = 0; + } else { + /* Find closest prescaler */ + for (prescaler = 1; prescaler < 10; ++prescaler) { + if (div <= (1 << prescaler)) { + min_delta = div - (1 << (prescaler - 1)); + max_delta = (1 << prescaler) - div; + if (min_delta < max_delta) { + prescaler -= 1; + } + break; + } + } + } + + /* Now set the actual frequency */ + bsptimer->tmr_freq = NRF52_MAX_TIMER_FREQ / (1 << prescaler); + bsptimer->tmr_enabled = 1; + + /* disable interrupts */ + __HAL_DISABLE_INTERRUPTS(ctx); + +#if MYNEWT_VAL_CHOICE(MCU_HFCLK_SOURCE, HFXO) + /* Make sure HFXO is started */ + nrf52_clock_hfxo_request(); +#endif + hwtimer = bsptimer->tmr_reg; + + /* Stop the timer first */ + nrf_timer_task_trigger(hwtimer, NRF_TIMER_TASK_STOP); + nrf_timer_task_trigger(hwtimer, NRF_TIMER_TASK_CLEAR); + + /* Put the timer in timer mode using 32 bits. */ + nrf_timer_mode_set(hwtimer, NRF_TIMER_MODE_TIMER); + hwtimer->BITMODE = TIMER_BITMODE_BITMODE_32Bit; + + /* Set the pre-scalar */ + hwtimer->PRESCALER = prescaler; + + /* Start the timer */ + nrf_timer_task_trigger(hwtimer, NRF_TIMER_TASK_START); + + NVIC_EnableIRQ(bsptimer->tmr_irq_num); + + __HAL_ENABLE_INTERRUPTS(ctx); + + return 0; + +err: + return rc; +} + +/** + * hal timer deinit + * + * De-initialize a HW timer. + * + * @param timer_num + * + * @return int + */ +int +hal_timer_deinit(int timer_num) +{ + int rc; + uint32_t ctx; + struct nrf52_hal_timer *bsptimer; + NRF_TIMER_Type *hwtimer; + NRF_RTC_Type *rtctimer; + + rc = 0; + NRF52_HAL_TIMER_RESOLVE(timer_num, bsptimer); + + __HAL_DISABLE_INTERRUPTS(ctx); + if (bsptimer->tmr_rtc) { + rtctimer = (NRF_RTC_Type *)bsptimer->tmr_reg; + nrf_rtc_int_disable(rtctimer, NRF_TIMER_INT_MASK(NRF_RTC_TIMER_CC_INT)); + nrf_rtc_task_trigger(rtctimer, NRF_RTC_TASK_STOP); + } else { + hwtimer = (NRF_TIMER_Type *)bsptimer->tmr_reg; + nrf_timer_int_disable(hwtimer, NRF_TIMER_INT_MASK(NRF_TIMER_CC_INT)); + hwtimer->TASKS_SHUTDOWN = 1; + } + bsptimer->tmr_enabled = 0; + bsptimer->tmr_reg = NULL; + +#if MYNEWT_VAL_CHOICE(MCU_HFCLK_SOURCE, HFXO) + if (timer_num != 5) { + nrf52_clock_hfxo_release(); + } +#endif + __HAL_ENABLE_INTERRUPTS(ctx); + +err: + return rc; +} + +/** + * hal timer get resolution + * + * Get the resolution of the timer. This is the timer period, in nanoseconds + * + * @param timer_num + * + * @return uint32_t The + */ +uint32_t +hal_timer_get_resolution(int timer_num) +{ + int rc; + uint32_t resolution; + struct nrf52_hal_timer *bsptimer; + + NRF52_HAL_TIMER_RESOLVE(timer_num, bsptimer); + + resolution = 1000000000 / bsptimer->tmr_freq; + return resolution; + +err: + rc = 0; + return rc; +} + +/** + * hal timer read + * + * Returns the timer counter. NOTE: if the timer is a 16-bit timer, only + * the lower 16 bits are valid. If the timer is a 64-bit timer, only the + * low 32-bits are returned. + * + * @return uint32_t The timer counter register. + */ +uint32_t +hal_timer_read(int timer_num) +{ + int rc; + uint32_t tcntr; + struct nrf52_hal_timer *bsptimer; + + NRF52_HAL_TIMER_RESOLVE(timer_num, bsptimer); + if (bsptimer->tmr_rtc) { + tcntr = hal_timer_read_bsptimer(bsptimer); + } else { + tcntr = nrf_read_timer_cntr(bsptimer->tmr_reg); + } + + return tcntr; + + /* Assert here since there is no invalid return code */ +err: + assert(0); + rc = 0; + return rc; +} + +/** + * hal timer delay + * + * Blocking delay for n ticks + * + * @param timer_num + * @param ticks + * + * @return int 0 on success; error code otherwise. + */ +int +hal_timer_delay(int timer_num, uint32_t ticks) +{ + uint32_t until; + + until = hal_timer_read(timer_num) + ticks; + while ((int32_t)(hal_timer_read(timer_num) - until) <= 0) { + /* Loop here till finished */ + } + + return 0; +} + +/** + * + * Initialize the HAL timer structure with the callback and the callback + * argument. Also initializes the HW specific timer pointer. + * + * @param cb_func + * + * @return int + */ +int +hal_timer_set_cb(int timer_num, struct hal_timer *timer, hal_timer_cb cb_func, + void *arg) +{ + int rc; + struct nrf52_hal_timer *bsptimer; + + NRF52_HAL_TIMER_RESOLVE(timer_num, bsptimer); + + timer->cb_func = cb_func; + timer->cb_arg = arg; + timer->link.tqe_prev = NULL; + timer->bsp_timer = bsptimer; + + rc = 0; + +err: + return rc; +} + +int +hal_timer_start(struct hal_timer *timer, uint32_t ticks) +{ + int rc; + uint32_t tick; + struct nrf52_hal_timer *bsptimer; + + /* Set the tick value at which the timer should expire */ + bsptimer = (struct nrf52_hal_timer *)timer->bsp_timer; + if (bsptimer->tmr_rtc) { + tick = hal_timer_read_bsptimer(bsptimer) + ticks; + } else { + tick = nrf_read_timer_cntr(bsptimer->tmr_reg) + ticks; + } + rc = hal_timer_start_at(timer, tick); + return rc; +} + +int +hal_timer_start_at(struct hal_timer *timer, uint32_t tick) +{ + uint32_t ctx; + struct hal_timer *entry; + struct nrf52_hal_timer *bsptimer; + + if ((timer == NULL) || (timer->link.tqe_prev != NULL) || + (timer->cb_func == NULL)) { + return EINVAL; + } + bsptimer = (struct nrf52_hal_timer *)timer->bsp_timer; + timer->expiry = tick; + + __HAL_DISABLE_INTERRUPTS(ctx); + + if (TAILQ_EMPTY(&bsptimer->hal_timer_q)) { + TAILQ_INSERT_HEAD(&bsptimer->hal_timer_q, timer, link); + } else { + TAILQ_FOREACH(entry, &bsptimer->hal_timer_q, link) { + if ((int32_t)(timer->expiry - entry->expiry) < 0) { + TAILQ_INSERT_BEFORE(entry, timer, link); + break; + } + } + if (!entry) { + TAILQ_INSERT_TAIL(&bsptimer->hal_timer_q, timer, link); + } + } + + /* If this is the head, we need to set new OCMP */ + if (timer == TAILQ_FIRST(&bsptimer->hal_timer_q)) { + nrf_timer_set_ocmp(bsptimer, timer->expiry); + } + + __HAL_ENABLE_INTERRUPTS(ctx); + + return 0; +} + +/** + * hal timer stop + * + * Stop a timer. + * + * @param timer + * + * @return int + */ +int +hal_timer_stop(struct hal_timer *timer) +{ + uint32_t ctx; + int reset_ocmp; + struct hal_timer *entry; + struct nrf52_hal_timer *bsptimer; + + if (timer == NULL) { + return EINVAL; + } + + bsptimer = (struct nrf52_hal_timer *)timer->bsp_timer; + + __HAL_DISABLE_INTERRUPTS(ctx); + + if (timer->link.tqe_prev != NULL) { + reset_ocmp = 0; + if (timer == TAILQ_FIRST(&bsptimer->hal_timer_q)) { + /* If first on queue, we will need to reset OCMP */ + entry = TAILQ_NEXT(timer, link); + reset_ocmp = 1; + } + TAILQ_REMOVE(&bsptimer->hal_timer_q, timer, link); + timer->link.tqe_prev = NULL; + if (reset_ocmp) { + if (entry) { + nrf_timer_set_ocmp((struct nrf52_hal_timer *)entry->bsp_timer, + entry->expiry); + } else { + if (bsptimer->tmr_rtc) { + nrf_rtc_disable_ocmp((NRF_RTC_Type *)bsptimer->tmr_reg); + } else { + nrf_timer_disable_ocmp(bsptimer->tmr_reg); + } + } + } + } + + __HAL_ENABLE_INTERRUPTS(ctx); + + return 0; +} diff --git a/lib/bt/host/nimble/nimble/babblesim/hw/mcu/nordic/nrf52_bsim/src/hal_uart.c b/lib/bt/host/nimble/nimble/babblesim/hw/mcu/nordic/nrf52_bsim/src/hal_uart.c new file mode 100644 index 00000000..41ac9b1d --- /dev/null +++ b/lib/bt/host/nimble/nimble/babblesim/hw/mcu/nordic/nrf52_bsim/src/hal_uart.c @@ -0,0 +1,478 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "os/mynewt.h" +#include "hal/hal_uart.h" +#include "bsp/bsp.h" + +#ifdef MN_LINUX +#include +#endif +#ifdef MN_OSX +#include +#endif +#ifdef MN_FreeBSD +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mcu/mcu_sim.h" +#include "native_uart_cfg_priv.h" +#include "syscfg/syscfg.h" + +#define UART_CNT 2 + +#if MYNEWT_VAL(CONSOLE_UART_TX_BUF_SIZE) +#define UART_MAX_BYTES_PER_POLL MYNEWT_VAL(CONSOLE_UART_TX_BUF_SIZE) - 2 +#else +#define UART_MAX_BYTES_PER_POLL 64 +#endif +#define UART_POLLER_STACK_SZ OS_STACK_ALIGN(1024) + +struct uart { + int u_open; + int u_fd; + int u_tx_run; + int u_rx_char; + hal_uart_rx_char u_rx_func; + hal_uart_tx_char u_tx_func; + hal_uart_tx_done u_tx_done; + void *u_func_arg; +}; + +const char *native_uart_dev_strs[UART_CNT]; + +/* + * XXXX should try to use O_ASYNC/SIGIO for byte arrival notification, + * so we wouldn't need an OS for pseudo ttys. + */ +char *native_uart_log_file = NULL; +static int uart_log_fd = -1; + +static struct uart uarts[UART_CNT]; +static int uart_poller_running; +static struct os_task uart_poller_task; +static os_stack_t uart_poller_stack[UART_POLLER_STACK_SZ]; + +static void +uart_open_log(void) +{ + if (native_uart_log_file && uart_log_fd < 0) { + uart_log_fd = open(native_uart_log_file, O_WRONLY | O_CREAT | O_TRUNC, + 0666); + assert(uart_log_fd >= 0); + } +} + +static void +uart_log_data(struct uart *u, int istx, uint8_t data) +{ + static struct { + struct uart *uart; + int istx; + uint32_t time; + int chars_in_line; + } state = { + .uart = NULL, + .istx = 0 + }; + uint32_t now; + char tmpbuf[32]; + int len; + + if (uart_log_fd < 0) { + return; + } + now = os_time_get(); + if (state.uart) { + if (u != state.uart || now != state.time || istx != state.istx) { + /* + * End current printout. + */ + if (write(uart_log_fd, "\n", 1) != 1) { + assert(0); + } + state.uart = NULL; + } else { + if (state.chars_in_line == 8) { + if (write(uart_log_fd, "\n\t", 2) != 2) { + assert(0); + } + state.chars_in_line = 0; + } + len = snprintf(tmpbuf, sizeof(tmpbuf), "%c (%02x) ", + isalnum(data) ? data : '?', data); + if (write(uart_log_fd, tmpbuf, len) != len) { + assert(0); + } + state.chars_in_line++; + } + } + if (u && state.uart == NULL) { + len = snprintf(tmpbuf, sizeof(tmpbuf), "%u:uart%d %s\n\t%c (%02x) ", + now, u - uarts, istx ? "tx" : "rx", isalnum(data) ? data : '?', data); + if (write(uart_log_fd, tmpbuf, len) != len) { + assert(0); + } + state.chars_in_line = 1; + state.uart = u; + state.istx = istx; + state.time = now; + } +} + +static int +uart_transmit_char(struct uart *uart) +{ + int sr; + int rc; + char ch; + + OS_ENTER_CRITICAL(sr); + rc = uart->u_tx_func(uart->u_func_arg); + if (rc < 0) { + /* + * No more data to send. + */ + uart->u_tx_run = 0; + if (uart->u_tx_done) { + uart->u_tx_done(uart->u_func_arg); + } + OS_EXIT_CRITICAL(sr); + return 0; + } + ch = rc; + uart_log_data(uart, 1, ch); + OS_EXIT_CRITICAL(sr); + rc = write(uart->u_fd, &ch, 1); + if (rc <= 0) { + /* XXX EOF/error, what now? */ + return -1; + } + return 0; +} + +static void +uart_poller(void *arg) +{ + int i; + int rc; + int bytes; + int sr; + int didwork; + unsigned char ch; + struct uart *uart; + + while (1) { + for (i = 0; i < UART_CNT; i++) { + if (!uarts[i].u_open) { + continue; + } + uart = &uarts[i]; + + for (bytes = 0; bytes < UART_MAX_BYTES_PER_POLL; bytes++) { + didwork = 0; + if (uart->u_tx_run) { + uart_transmit_char(uart); + didwork = 1; + } + if (uart->u_rx_char < 0) { + rc = read(uart->u_fd, &ch, 1); + if (rc == 0) { + /* XXX EOF, what now? */ + assert(0); + } else if (rc > 0) { + uart->u_rx_char = ch; + } + } + if (uart->u_rx_char >= 0) { + OS_ENTER_CRITICAL(sr); + uart_log_data(uart, 0, uart->u_rx_char); + rc = uart->u_rx_func(uart->u_func_arg, uart->u_rx_char); + /* Delivered */ + if (rc >= 0) { + uart->u_rx_char = -1; + didwork = 1; + } + OS_EXIT_CRITICAL(sr); + } + if (!didwork) { + break; + } + } + } + uart_log_data(NULL, 0, 0); + os_time_delay(OS_TICKS_PER_SEC / 100); + } +} + +static void +set_nonblock(int fd) +{ + int flags; + + flags = fcntl(fd, F_GETFL); + if (flags == -1) { + const char msg[] = "fcntl(F_GETFL) fail"; + write(1, msg, sizeof(msg)); + return; + } + if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0) { + const char msg[] = "fcntl(F_SETFL) fail"; + write(1, msg, sizeof(msg)); + return; + } +} + +static int +uart_pty_set_attr(int fd) +{ + struct termios tios; + + if (tcgetattr(fd, &tios)) { + const char msg[] = "tcgetattr() failed"; + write(1, msg, sizeof(msg)); + return -1; + } + + tios.c_cflag &= ~(CSIZE | CSTOPB | PARENB); + tios.c_cflag |= CS8 | CREAD; + tios.c_iflag = IGNPAR; + tios.c_oflag = 0; + tios.c_lflag = 0; + if (tcsetattr(fd, TCSAFLUSH, &tios) < 0) { + const char msg[] = "tcsetattr() failed"; + write(1, msg, sizeof(msg)); + return -1; + } + return 0; +} + +static int +uart_pty(int port) +{ + int fd; + int loop_slave; + char pty_name[32]; + char msg[64]; + + if (openpty(&fd, &loop_slave, pty_name, NULL, NULL) < 0) { + const char msg[] = "openpty() failed"; + write(1, msg, sizeof(msg)); + return -1; + } + + if (uart_pty_set_attr(loop_slave)) { + goto err; + } + + snprintf(msg, sizeof(msg), "uart%d at %s\n", port, pty_name); + write(1, msg, strlen(msg)); + return fd; + err: + close(fd); + close(loop_slave); + return -1; +} + +/** + * Opens an external device terminal (/dev/cu.<...>). + */ +static int +uart_open_dev(int port, int32_t baudrate, uint8_t databits, + uint8_t stopbits, enum hal_uart_parity parity, + enum hal_uart_flow_ctl flow_ctl) +{ + const char *filename; + int fd; + int rc; + + filename = native_uart_dev_strs[port]; + assert(filename != NULL); + + fd = open(filename, O_RDWR); + if (fd < 0) { + return -1; + } + + rc = uart_dev_set_attr(fd, baudrate, databits, + stopbits, parity, flow_ctl); + if (rc != 0) { + close(fd); + return rc; + } + + dprintf(1, "uart%d at %s\n", port, filename); + + return fd; +} + +void +hal_uart_start_tx(int port) +{ + int sr; + + if (port >= UART_CNT || uarts[port].u_open == 0) { + return; + } + OS_ENTER_CRITICAL(sr); + uarts[port].u_tx_run = 1; + if (!os_started()) { + /* + * XXX this is a hack. + */ + uart_transmit_char(&uarts[port]); + } + OS_EXIT_CRITICAL(sr); +} + +void +hal_uart_start_rx(int port) +{ + /* nothing to do here */ +} + +void +hal_uart_blocking_tx(int port, uint8_t data) +{ + if (port >= UART_CNT || uarts[port].u_open == 0) { + return; + } + + /* XXX: Count statistics and add error checking here. */ + (void) write(uarts[port].u_fd, &data, sizeof(data)); +} + +int +hal_uart_init_cbs(int port, hal_uart_tx_char tx_func, hal_uart_tx_done tx_done, + hal_uart_rx_char rx_func, void *arg) +{ + struct uart *uart; + int rc; + + if (port >= UART_CNT) { + return -1; + } + + uart = &uarts[port]; + if (uart->u_open) { + return -1; + } + uart->u_tx_func = tx_func; + uart->u_tx_done = tx_done; + uart->u_rx_func = rx_func; + uart->u_func_arg = arg; + uart->u_rx_char = -1; + + if (!uart_poller_running) { + uart_poller_running = 1; + rc = os_task_init(&uart_poller_task, "uartpoll", uart_poller, NULL, + MYNEWT_VAL(MCU_UART_POLLER_PRIO), OS_WAIT_FOREVER, uart_poller_stack, + UART_POLLER_STACK_SZ); + assert(rc == 0); + } + return 0; +} + +int +hal_uart_config(int port, int32_t baudrate, uint8_t databits, uint8_t stopbits, + enum hal_uart_parity parity, enum hal_uart_flow_ctl flow_ctl) +{ + struct uart *uart; + + if (port >= UART_CNT) { + return -1; + } + + uart = &uarts[port]; + if (uart->u_open) { + return -1; + } + + if (native_uart_dev_strs[port] == NULL) { + uart->u_fd = uart_pty(port); + } else { + uart->u_fd = uart_open_dev(port, baudrate, databits, stopbits, + parity, flow_ctl); + } + + if (uart->u_fd < 0) { + return -1; + } + set_nonblock(uart->u_fd); + + + uart_open_log(); + uart->u_open = 1; + return 0; +} + +int +hal_uart_close(int port) +{ + struct uart *uart; + int rc; + + if (port >= UART_CNT) { + rc = -1; + goto err; + } + + uart = &uarts[port]; + if (!uart->u_open) { + rc = -1; + goto err; + } + + close(uart->u_fd); + + uart->u_open = 0; + return (0); + err: + return (rc); +} + +int +hal_uart_init(int port, void *arg) +{ + return (0); +} + +int +uart_set_dev(int port, const char *dev_str) +{ + if (port < 0 || port >= UART_CNT) { + return SYS_EINVAL; + } + + if (uarts[port].u_open) { + return SYS_EBUSY; + } + + native_uart_dev_strs[port] = dev_str; + + return 0; +} diff --git a/lib/bt/host/nimble/nimble/babblesim/hw/mcu/nordic/nrf52_bsim/src/hal_watchdog.c b/lib/bt/host/nimble/nimble/babblesim/hw/mcu/nordic/nrf52_bsim/src/hal_watchdog.c new file mode 100644 index 00000000..d94c7e33 --- /dev/null +++ b/lib/bt/host/nimble/nimble/babblesim/hw/mcu/nordic/nrf52_bsim/src/hal_watchdog.c @@ -0,0 +1,36 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "hal/hal_watchdog.h" + +int +hal_watchdog_init(uint32_t expire_msecs) +{ + return (0); +} + +void +hal_watchdog_enable(void) +{ +} + +void +hal_watchdog_tickle(void) +{ +} diff --git a/lib/bt/host/nimble/nimble/babblesim/hw/mcu/nordic/nrf52_bsim/src/native_uart_cfg.c b/lib/bt/host/nimble/nimble/babblesim/hw/mcu/nordic/nrf52_bsim/src/native_uart_cfg.c new file mode 100644 index 00000000..5e38ac7b --- /dev/null +++ b/lib/bt/host/nimble/nimble/babblesim/hw/mcu/nordic/nrf52_bsim/src/native_uart_cfg.c @@ -0,0 +1,248 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#include +#include +#include +#include +#include + +/* B0 defined in bits/termios.h collides with nrfx/mdk/nrf52.h */ +#undef B0 + +#include "os/mynewt.h" +#include "native_uart_cfg_priv.h" + +/* uint64 is used here to accommodate speed_t, whatever that is. */ +static const uint64_t uart_baud_table[][2] = { +#ifdef B50 + { 50, B50 }, +#endif +#ifdef B75 + { 75, B75 }, +#endif +#ifdef B110 + { 110, B110 }, +#endif +#ifdef B134 + { 134, B134 }, +#endif +#ifdef B150 + { 150, B150 }, +#endif +#ifdef B200 + { 200, B200 }, +#endif +#ifdef B300 + { 300, B300 }, +#endif +#ifdef B600 + { 600, B600 }, +#endif +#ifdef B1200 + { 1200, B1200 }, +#endif +#ifdef B1800 + { 1800, B1800 }, +#endif +#ifdef B2400 + { 2400, B2400 }, +#endif +#ifdef B4800 + { 4800, B4800 }, +#endif +#ifdef B9600 + { 9600, B9600 }, +#endif +#ifdef B19200 + { 19200, B19200 }, +#endif +#ifdef B38400 + { 38400, B38400 }, +#endif +#ifdef B57600 + { 57600, B57600 }, +#endif +#ifdef B115200 + { 115200, B115200 }, +#endif +#ifdef B230400 + { 230400, B230400 }, +#endif +#ifdef B460800 + { 460800, B460800 }, +#endif +#ifdef B500000 + { 500000, B500000 }, +#endif +#ifdef B576000 + { 576000, B576000 }, +#endif +#ifdef B921600 + { 921600, B921600 }, +#endif +#ifdef B1000000 + { 1000000, B1000000 }, +#endif +#ifdef B1152000 + { 1152000, B1152000 }, +#endif +#ifdef B1500000 + { 1500000, B1500000 }, +#endif +#ifdef B2000000 + { 2000000, B2000000 }, +#endif +#ifdef B2500000 + { 2500000, B2500000 }, +#endif +#ifdef B3000000 + { 3000000, B3000000 }, +#endif +#ifdef B3500000 + { 3500000, B3500000 }, +#endif +#ifdef B3710000 + { 3710000, B3710000 }, +#endif +#ifdef B4000000 + { 4000000, B4000000 }, +#endif +}; +#define UART_BAUD_TABLE_SZ (sizeof uart_baud_table / sizeof uart_baud_table[0]) + +/** + * Returns 0 on failure. + */ +speed_t +uart_baud_to_speed(int_least32_t baud) +{ + int i; + + for (i = 0; i < UART_BAUD_TABLE_SZ; i++) { + if (uart_baud_table[i][0] == baud) { + return uart_baud_table[i][1]; + } + } + + return 0; +} + +/** + * Configures an external device terminal (/dev/cu.<...>). + */ +int +uart_dev_set_attr(int fd, int32_t baudrate, uint8_t databits, + uint8_t stopbits, enum hal_uart_parity parity, + enum hal_uart_flow_ctl flow_ctl) +{ + struct termios tty; + speed_t speed; + int rc; + + assert(fd >= 0); + + memset(&tty, 0, sizeof(tty)); + cfmakeraw(&tty); + + speed = uart_baud_to_speed(baudrate); + if (speed == 0) { + fprintf(stderr, "invalid baud rate: %d\n", (int)baudrate); + assert(0); + } + + tty.c_cflag |= (speed | CLOCAL | CREAD); + + /* Set flow control. */ + switch (flow_ctl) { + case HAL_UART_FLOW_CTL_NONE: + tty.c_cflag &= ~CRTSCTS; + break; + + case HAL_UART_FLOW_CTL_RTS_CTS: + tty.c_cflag |= CRTSCTS; + break; + + default: + fprintf(stderr, "invalid flow control setting: %d\n", flow_ctl); + return -1; + } + + errno = 0; + rc = cfsetospeed(&tty, speed); + if (rc != 0) { + fprintf(stderr, "cfsetospeed failed; %d (%s) baudrate=%d\n", + errno, strerror(errno), (int)baudrate); + return -1; + } + + errno = 0; + rc = cfsetispeed(&tty, speed); + if (rc != 0) { + fprintf(stderr, "cfsetispeed failed; %d (%s) baudrate=%d\n", + errno, strerror(errno), (int)baudrate); + return -1; + } + + switch (databits) { + case 7: + tty.c_cflag |= CS7; + + switch (parity) { + case HAL_UART_PARITY_ODD: + tty.c_cflag |= PARENB; + tty.c_cflag |= PARODD; + tty.c_cflag &= ~CSTOPB; + tty.c_cflag &= ~CSIZE; + break; + + case HAL_UART_PARITY_EVEN: + tty.c_cflag |= PARENB; + tty.c_cflag &= ~PARODD; + tty.c_cflag &= ~CSTOPB; + tty.c_cflag &= ~CSIZE; + break; + + default: + return SYS_EINVAL; + } + + case 8: + if (parity != HAL_UART_PARITY_NONE) { + return SYS_EINVAL; + } + tty.c_cflag |= CS8; + tty.c_cflag &= ~PARENB; + tty.c_cflag &= ~CSTOPB; + tty.c_cflag &= ~CSIZE; + break; + + default: + return SYS_EINVAL; + } + + rc = tcsetattr(fd, TCSANOW, &tty); + if (rc != 0) { + fprintf(stderr, "tcsetattr failed; %d (%s)\n", errno, strerror(errno)); + return -1; + } + + return 0; +} diff --git a/lib/bt/host/nimble/nimble/babblesim/hw/mcu/nordic/nrf52_bsim/src/native_uart_cfg_priv.h b/lib/bt/host/nimble/nimble/babblesim/hw/mcu/nordic/nrf52_bsim/src/native_uart_cfg_priv.h new file mode 100644 index 00000000..786a68ad --- /dev/null +++ b/lib/bt/host/nimble/nimble/babblesim/hw/mcu/nordic/nrf52_bsim/src/native_uart_cfg_priv.h @@ -0,0 +1,31 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_NATIVE_UART_CFG_PRIV_ +#define H_NATIVE_UART_CFG_PRIV_ + +#include +#include "hal/hal_uart.h" + +speed_t uart_baud_to_speed(int_least32_t baud); +int uart_dev_set_attr(int fd, int32_t baudrate, uint8_t databits, + uint8_t stopbits, enum hal_uart_parity parity, + enum hal_uart_flow_ctl flow_ctl); + +#endif diff --git a/lib/bt/host/nimble/nimble/babblesim/hw/mcu/nordic/nrf52_bsim/src/nrf52_clock.c b/lib/bt/host/nimble/nimble/babblesim/hw/mcu/nordic/nrf52_bsim/src/nrf52_clock.c new file mode 100644 index 00000000..25ccc7fd --- /dev/null +++ b/lib/bt/host/nimble/nimble/babblesim/hw/mcu/nordic/nrf52_bsim/src/nrf52_clock.c @@ -0,0 +1,103 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +#include +#include +#include +#include +#include "mcu/nrf52_hal.h" +#include "nrfx.h" +#include "syscfg/syscfg.h" +#include "os/os.h" + +static uint8_t nrf52_clock_hfxo_refcnt; + +/** + * Request HFXO clock be turned on. Note that each request must have a + * corresponding release. + * + * @return int 0: hfxo was already on. 1: hfxo was turned on. + */ +int +nrf52_clock_hfxo_request(void) +{ + int started; + uint32_t ctx; + +#if MYNEWT_VAL_CHOICE(MCU_HFCLK_SOURCE, HFINT) + /* Cannot enable/disable hfxo if it is not present */ + assert(0); +#endif + + started = 0; + __HAL_DISABLE_INTERRUPTS(ctx); + assert(nrf52_clock_hfxo_refcnt < 0xff); + if (nrf52_clock_hfxo_refcnt == 0) { + /* Check the current STATE and SRC of HFCLK */ + if ((NRF_CLOCK->HFCLKSTAT & + (CLOCK_HFCLKSTAT_SRC_Msk | CLOCK_HFCLKSTAT_STATE_Msk)) != + (CLOCK_HFCLKSTAT_SRC_Xtal << CLOCK_HFCLKSTAT_SRC_Pos | + CLOCK_HFCLKSTAT_STATE_Running << CLOCK_HFCLKSTAT_STATE_Pos)) { + NRF_CLOCK->EVENTS_HFCLKSTARTED = 0; + nrf_clock_task_trigger(NRF_CLOCK, NRF_CLOCK_TASK_HFCLKSTART); + while (!NRF_CLOCK->EVENTS_HFCLKSTARTED) { +#if BABBLESIM + tm_tick(); +#endif + } + } + started = 1; + } + ++nrf52_clock_hfxo_refcnt; + __HAL_ENABLE_INTERRUPTS(ctx); + + return started; +} + +/** + * Release the HFXO. This means that the caller no longer needs the HFXO to be + * turned on. Each call to release should have been preceeded by a corresponding + * call to request the HFXO + * + * + * @return int 0: HFXO not stopped by this call (others using it) 1: HFXO + * stopped. + */ +int +nrf52_clock_hfxo_release(void) +{ + int stopped; + uint32_t ctx; + +#if MYNEWT_VAL_CHOICE(MCU_HFCLK_SOURCE, HFINT) + /* Cannot enable/disable hfxo if it is not present */ + assert(0); +#endif + + stopped = 0; + __HAL_DISABLE_INTERRUPTS(ctx); + assert(nrf52_clock_hfxo_refcnt != 0); + --nrf52_clock_hfxo_refcnt; + if (nrf52_clock_hfxo_refcnt == 0) { + nrf_clock_task_trigger(NRF_CLOCK, NRF_CLOCK_TASK_HFCLKSTOP); + stopped = 1; + } + __HAL_ENABLE_INTERRUPTS(ctx); + + return stopped; +} diff --git a/lib/bt/host/nimble/nimble/babblesim/hw/mcu/nordic/nrf52_bsim/src/system_nrf52.c b/lib/bt/host/nimble/nimble/babblesim/hw/mcu/nordic/nrf52_bsim/src/system_nrf52.c new file mode 100644 index 00000000..00224cdc --- /dev/null +++ b/lib/bt/host/nimble/nimble/babblesim/hw/mcu/nordic/nrf52_bsim/src/system_nrf52.c @@ -0,0 +1,37 @@ +/* Copyright (c) 2012 ARM LIMITED + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of ARM nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "mcu/cmsis_nvic.h" +#include "nrf.h" + + +void SystemInit(void) +{ +} diff --git a/lib/bt/host/nimble/nimble/babblesim/hw/mcu/nordic/nrf52_bsim/syscfg.yml b/lib/bt/host/nimble/nimble/babblesim/hw/mcu/nordic/nrf52_bsim/syscfg.yml new file mode 100644 index 00000000..d6b66959 --- /dev/null +++ b/lib/bt/host/nimble/nimble/babblesim/hw/mcu/nordic/nrf52_bsim/syscfg.yml @@ -0,0 +1,526 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +syscfg.defs: + MCU_TARGET: + description: > + Specifies target MCU, shall be set by BSP. + value: + restrictions: + - $notnull + choices: + - nRF52810 + - nRF52811 + - nRF52832 + - nRF52840 + + MCU_FLASH_MIN_WRITE_SIZE: + description: > + Specifies the required alignment for internal flash writes. + Used internally by the newt tool. + value: 1 + + MCU_DCDC_ENABLED: + description: > + Specifies whether or not to enable DC/DC regulator. This requires + external circuitry so is defined to be zero by default and + expected to be overridden by the BSP. + value: 0 + + MCU_HFCLK_SOURCE: + description: > + Selected source for high frequency clock (HFCLK). + Selecting HFXO will still mostly use the HFINT but will switch to HFXO when requested (BLE, certain timers, etc...) + Selecting HFINT should only be used in the case where an external 32MHz crystal oscillator is not present. + value: HFXO + choices: + - HFXO + - HFINT + restrictions: + - '(MCU_HFCLK_SOURCE == "HFXO") || (MCU_LFCLK_SOURCE != "LFSYNTH")' + + MCU_LFCLK_SOURCE: + description: > + Selected source for low frequency clock (LFCLK). + value: + choices: + - LFRC # 32.768 kHz RC oscillator + - LFXO # 32.768 kHz crystal oscillator + - LFSYNTH # 32.768 kHz synthesized from HFCLK + + MCU_I2C_RECOVERY_DELAY_USEC: + description: > + Time to wait for activity on SCL line after triggering start task + before restarting TWI controller. This is to recover from state + where controller is unresponsive due to glitch on I2C bus. + Note: Default value seems to work fine, but may need to be tuned. + value: 100 + + MCU_BUS_DRIVER_I2C_USE_TWIM: + description: > + Enables usage of i2c_nrf52_twim bus driver for I2C. + If disabled, standard i2c_hal driver is used. + value: 0 + + MCU_GPIO_USE_PORT_EVENT: + description: > + When enabled, hal_gpio will use GPIOTE PORT event instead of PIN + events for interrupts. This mode may be less accurate (i.e. pulse + length needs to be longer in order to be detected) but it reduces + power consumption since it does not require HFCLK to be running. + Refer to nRF52xxx Product Specification document for more details. + value: 0 + + MCU_DEBUG_IGNORE_BKPT: + description: > + When enabled, asm(bkpt) will be ignored. If not set, it will hit + the breakpoint wherever it gets called, For example, reset and crash + value: 0 + + +# MCU peripherals definitions + I2C_0: + description: 'Enable nRF52xxx I2C (TWI) 0' + value: 0 + restrictions: + - '!(SPI_0_MASTER && ((MCU_TARGET == "nrf52832") || (MCU_TARGET == "nrf52840")))' + - '!(SPI_0_SLAVE && ((MCU_TARGET == "nrf52832") || (MCU_TARGET == "nrf52840")))' + - '!(SPI_1_MASTER && (MCU_TARGET == "nrf52811"))' + - '!(SPI_1_SLAVE && (MCU_TARGET == "nrf52811"))' + I2C_0_PIN_SCL: + description: 'SCL pin for I2C_0' + value: '' + I2C_0_PIN_SDA: + description: 'SDA pin for I2C_0' + value: '' + I2C_0_FREQ_KHZ: + description: 'Frequency [kHz] for I2C_0' + value: 100 + + I2C_1: + description: 'Enable nRF52xxx I2C (TWI) 1' + value: 0 + restrictions: + - "!SPI_1_MASTER" + - "!SPI_1_SLAVE" + I2C_1_PIN_SCL: + description: 'SCL pin for I2C_1' + value: '' + I2C_1_PIN_SDA: + description: 'SDA pin for I2C_1' + value: '' + I2C_1_FREQ_KHZ: + description: 'Frequency [kHz] for I2C_1' + value: 100 + + SPI_0_MASTER: + description: 'Enable nRF52xxx SPI Master 0' + value: 0 + restrictions: + - "!SPI_0_SLAVE" + - '!(I2C_0 && ((MCU_TARGET == "nrf52832") || (MCU_TARGET == "nrf52840")))' + SPI_0_MASTER_PIN_SCK: + description: 'SCK pin for SPI_0_MASTER' + value: '' + SPI_0_MASTER_PIN_MOSI: + description: 'MOSI pin for SPI_0_MASTER' + value: '' + SPI_0_MASTER_PIN_MISO: + description: 'MISO pin for SPI_0_MASTER' + value: '' + + SPI_0_SLAVE: + description: 'Enable nRF52xxx SPI Slave 0' + value: 0 + restrictions: + - "!SPI_0_MASTER" + - '!(I2C_0 && ((MCU_TARGET == "nrf52832") || (MCU_TARGET == "nrf52840")))' + SPI_0_SLAVE_PIN_SCK: + description: 'SCK pin for SPI_0_SLAVE' + value: '' + SPI_0_SLAVE_PIN_MOSI: + description: 'MOSI pin for SPI_0_SLAVE' + value: '' + SPI_0_SLAVE_PIN_MISO: + description: 'MISO pin for SPI_0_SLAVE' + value: '' + SPI_0_SLAVE_PIN_SS: + description: 'SS pin for SPI_0_SLAVE' + value: '' + + SPI_1_MASTER: + description: 'Enable nRF52xxx SPI Master 1' + value: 0 + restrictions: + - "!SPI_1_SLAVE" + - '!(I2C_1 && ((MCU_TARGET == "nrf52832") || (MCU_TARGET == "nrf52840")))' + - '!(I2C_0 && ((MCU_TARGET == "nrf52811")))' + SPI_1_MASTER_PIN_SCK: + description: 'SCK pin for SPI_1_MASTER' + value: '' + SPI_1_MASTER_PIN_MOSI: + description: 'MOSI pin for SPI_1_MASTER' + value: '' + SPI_1_MASTER_PIN_MISO: + description: 'MISO pin for SPI_1_MASTER' + value: '' + + SPI_1_SLAVE: + description: 'Enable nRF52xxx SPI Slave 1' + value: 0 + restrictions: + - "!SPI_1_MASTER" + - '!(I2C_1 && ((MCU_TARGET == "nrf52832") || (MCU_TARGET == "nrf52840")))' + - '!(I2C_0 && ((MCU_TARGET == "nrf52811")))' + SPI_1_SLAVE_PIN_SCK: + description: 'SCK pin for SPI_1_SLAVE' + value: '' + SPI_1_SLAVE_PIN_MOSI: + description: 'MOSI pin for SPI_1_SLAVE' + value: '' + SPI_1_SLAVE_PIN_MISO: + description: 'MISO pin for SPI_1_SLAVE' + value: '' + SPI_1_SLAVE_PIN_SS: + description: 'SS pin for SPI_1_SLAVE' + value: '' + + SPI_2_MASTER: + description: 'Enable nRF52xxx SPI Master 2' + value: 0 + restrictions: + - "!SPI_2_SLAVE" + SPI_2_MASTER_PIN_SCK: + description: 'SCK pin for SPI_2_MASTER' + value: '' + SPI_2_MASTER_PIN_MOSI: + description: 'MOSI pin for SPI_2_MASTER' + value: '' + SPI_2_MASTER_PIN_MISO: + description: 'MISO pin for SPI_2_MASTER' + value: '' + + SPI_2_SLAVE: + description: 'Enable nRF52xxx SPI Slave 2' + value: 0 + restrictions: + - "!SPI_2_MASTER" + SPI_2_SLAVE_PIN_SCK: + description: 'SCK pin for SPI_2_SLAVE' + value: '' + SPI_2_SLAVE_PIN_MOSI: + description: 'MOSI pin for SPI_2_SLAVE' + value: '' + SPI_2_SLAVE_PIN_MISO: + description: 'MISO pin for SPI_2_SLAVE' + value: '' + SPI_2_SLAVE_PIN_SS: + description: 'SS pin for SPI_2_SLAVE' + value: '' + + SPI_3_MASTER: + description: 'Enable nRF52xxx SPI Master 3' + value: 0 + restrictions: + - 'MCU_TARGET == "nRF52840" || !SPI_3_MASTER' + SPI_3_MASTER_PIN_SCK: + description: 'SCK pin for SPI_3_MASTER' + value: '' + SPI_3_MASTER_PIN_MOSI: + description: 'MOSI pin for SPI_3_MASTER' + value: '' + SPI_3_MASTER_PIN_MISO: + description: 'MISO pin for SPI_3_MASTER' + value: '' + + ADC_0: + description: 'Enable nRF52xxx ADC 0' + value: 0 + + ADC_0_REFMV_0: + description: 'reference mV in AREF0 if used' + value: 0 + + PWM_0: + description: 'Enable nRF52xxx PWM 0' + value: 0 + PWM_1: + description: 'Enable nRF52xxx PWM 1' + value: 0 + PWM_2: + description: 'Enable nRF52xxx PWM 2' + value: 0 + PWM_3: + description: 'Enable nRF52xxx PWM 3' + value: 0 + restrictions: + - 'MCU_TARGET == "nRF52840" || !PWM_3' + + TRNG: + description: 'Enable nRF52xxx TRNG' + value: 0 + + CRYPTO: + description: 'Enable nRF52xxx CRYPTO' + value: 0 + + UART_0: + description: 'Enable nRF52xxx UART0' + value: 1 + UART_0_PIN_TX: + description: 'TX pin for UART0' + value: '' + UART_0_PIN_RX: + description: 'RX pin for UART0' + value: '' + UART_0_PIN_RTS: + description: 'RTS pin for UART0' + value: -1 + UART_0_PIN_CTS: + description: 'CTS pin for UART0' + value: -1 + + UART_1: + description: 'Enable nRF52xxx UART1' + value: 0 + restrictions: + - 'MCU_TARGET == "nRF52840" || !UART_1' + UART_1_PIN_TX: + description: 'TX pin for UART1' + value: '' + UART_1_PIN_RX: + description: 'RX pin for UART1' + value: '' + UART_1_PIN_RTS: + description: 'RTS pin for UART1' + value: -1 + UART_1_PIN_CTS: + description: 'CTS pin for UART1' + value: -1 + + TEMP: + description: 'Enable nRF52xxx internal temperature mesurement' + value: 0 + + TIMER_0: + description: 'Enable nRF52xxx Timer 0' + value: 1 + TIMER_1: + description: 'Enable nRF52xxx Timer 1' + value: 0 + TIMER_2: + description: 'Enable nRF52xxx Timer 2' + value: 0 + TIMER_3: + description: 'Enable nRF52xxx Timer 3' + value: 0 + TIMER_4: + description: 'Enable nRF52xxx Timer 4' + value: 0 + TIMER_5: + description: 'Enable nRF52xxx RTC 0' + value: 0 + + QSPI_ENABLE: + description: 'NRF52 QSPI' + value: 0 + + QSPI_READOC: + description: > + QSPI Command to use + 0 - 0x09 Fast Read + 1 - 0x3B Fast Read Dual Output + 2 - 0xBB Fast Read Dual I/O + 3 - 0x6B Fast Read Quad Output + 4 - 0xEB Fast Read Quad I/O + value: 0 + QSPI_WRITEOC: + description: > + QSPI Command to use + 0 - 0x02 Page program + 1 - 0xA2 Page program Dual Data + 2 - 0x32 Page program Quad Data + 3 - 0x38 Page program Quad I/O + value: 0 + QSPI_ADDRMODE: + description: 'Address lentgh 0=24 bits, 1=32 bits' + value: 0 + QSPI_DPMCONFIG: + description: 'Deep power mode enable' + value: 0 + QSPI_SCK_DELAY: + description: > + Minimum amount of time that the CSN pin must stay high + before it can go low again. Value is specified in number of 16 + MHz periods (62.5 ns). + value: 0 + QSPI_SCK_FREQ: + description: '32MHz clock divider (0-31). Clock = 32MHz / (1+divider)' + value: 0 + QSPI_SPI_MODE: + description: 'SPI 0=Mode0 or 1=Mode3' + value: 0 + + QSPI_FLASH_SECTOR_SIZE: + description: 'QSPI sector size. In most cases it should be 4096.' + value: 0 + QSPI_FLASH_PAGE_SIZE: + description: > + QSPI page size. Writes can only be performed to one page at a time. + In most cases it should be 256. + value: 0 + + QSPI_FLASH_SECTOR_COUNT: + description: 'QSPI sector count' + value: -1 + QSPI_PIN_CS: + description: 'CS pin for QSPI' + value: -1 + QSPI_PIN_SCK: + description: 'SCK pin for QSPI' + value: -1 + QSPI_PIN_DIO0: + description: 'DIO0 pin for QSPI' + value: -1 + QSPI_PIN_DIO1: + description: 'DIO1 pin for QSPI' + value: -1 + QSPI_PIN_DIO2: + description: 'DIO2 pin for QSPI' + value: -1 + QSPI_PIN_DIO3: + description: 'DIO3 pin for QSPI' + value: -1 + + NFC_PINS_AS_GPIO: + description: 'Use NFC pins as GPIOs instead of NFC functionality' + value: 1 + + GPIO_AS_PIN_RESET: + description: 'Enable pin reset' + value: 0 + +# Deprecated settings + + MCU_NRF52832: + description: Use MCU_TARGET instead + value: 0 + restrictions: + - "!MCU_NRF52840" + deprecated: 1 + MCU_NRF52840: + description: Use MCU_TARGET instead + value: 0 + restrictions: + - "!MCU_NRF52832" + deprecated: 1 + + XTAL_32768: + description: Use MCU_LFCLK_SOURCE instead + value: 0 + restrictions: + - "!XTAL_RC" + - "!XTAL_32768_SYNTH" + deprecated: 1 + XTAL_RC: + description: Use MCU_LFCLK_SOURCE instead + value: 0 + restrictions: + - "!XTAL_32768" + - "!XTAL_32768_SYNTH" + deprecated: 1 + XTAL_32768_SYNTH: + description: Use MCU_LFCLK_SOURCE instead + value: 0 + restrictions: + - "!XTAL_32768" + - "!XTAL_RC" + deprecated: 1 + + MCU_NATIVE_USE_SIGNALS: + description: > + Whether to use POSIX signals to implement context switches. Valid + values are as follows: + 1: More correctness; less stability. The OS tick timer will + cause a high-priority task to preempt a low-priority task. + This causes stability issues because a task can be preempted + while it is in the middle of a system call, potentially + causing deadlock or memory corruption. + + 0: Less correctness; more stability. The OS tick timer only + runs while the idle task is active. Therefore, a sleeping + high-priority task will not preempt a low-priority task due + to a timing event (e.g., delay or callout expired). + However, this version of sim does not suffer from the + stability issues that affect the "signals" implementation. + + Unit tests should use 1. Long-running sim processes should use 0. + + value: 1 + MCU_NATIVE: + description: > + Set to indicate that we are using native mcu. + value: 1 + MCU_FLASH_STYLE_ST: + description: Emulated flash layout is similar to one in STM32. + value: 0 + restrictions: + - "!MCU_FLASH_STYLE_NORDIC" + MCU_FLASH_STYLE_NORDIC: + description: > + Emulated flash layout is similar to one in NRF51/2 and SAMD21. + value: 1 + restrictions: + - "!MCU_FLASH_STYLE_ST" + MCU_UART_POLLER_PRIO: + description: 'Priority of native UART poller task.' + type: task_priority + value: 1 + MCU_TIMER_POLLER_PRIO: + description: 'Priority of native HAL timer task.' + type: task_priority + value: 0 + +syscfg.vals: + OS_TICKS_PER_SEC: 128 + +syscfg.vals.MCU_NRF52832: + MCU_TARGET: nRF52832 +syscfg.vals.MCU_NRF52840: + MCU_TARGET: nRF52840 + +syscfg.vals.XTAL_32768: + MCU_LFCLK_SOURCE: LFXO +syscfg.vals.XTAL_RC: + MCU_LFCLK_SOURCE: LFRC +syscfg.vals.XTAL_32768_SYNTH: + MCU_LFCLK_SOURCE: LFSYNTH + +syscfg.restrictions: + - "!I2C_0 || (I2C_0_PIN_SCL && I2C_0_PIN_SDA)" + - "!I2C_1 || (I2C_1_PIN_SCL && I2C_1_PIN_SDA)" + - "!SPI_0_MASTER || (SPI_0_MASTER_PIN_SCK && SPI_0_MASTER_PIN_MOSI && SPI_0_MASTER_PIN_MISO)" + - "!SPI_1_MASTER || (SPI_1_MASTER_PIN_SCK && SPI_1_MASTER_PIN_MOSI && SPI_1_MASTER_PIN_MISO)" + - "!SPI_2_MASTER || (SPI_2_MASTER_PIN_SCK && SPI_2_MASTER_PIN_MOSI && SPI_2_MASTER_PIN_MISO)" + - "!SPI_3_MASTER || (SPI_3_MASTER_PIN_SCK && SPI_3_MASTER_PIN_MOSI && SPI_3_MASTER_PIN_MISO)" + - "!SPI_0_SLAVE || (SPI_0_SLAVE_PIN_SCK && SPI_0_SLAVE_PIN_MOSI && SPI_0_SLAVE_PIN_MISO && SPI_0_SLAVE_PIN_SS)" + - "!SPI_1_SLAVE || (SPI_1_SLAVE_PIN_SCK && SPI_1_SLAVE_PIN_MOSI && SPI_1_SLAVE_PIN_MISO && SPI_1_SLAVE_PIN_SS)" + - "!SPI_2_SLAVE || (SPI_2_SLAVE_PIN_SCK && SPI_2_SLAVE_PIN_MOSI && SPI_2_SLAVE_PIN_MISO && SPI_2_SLAVE_PIN_SS)" + - "!UART_0 || (UART_0_PIN_TX && UART_0_PIN_RX)" + - "!UART_1 || (UART_1_PIN_TX && UART_1_PIN_RX)" + - "(OS_TICKS_PER_SEC == 128 || OS_TICKS_PER_SEC == 256 || OS_TICKS_PER_SEC == 512 || OS_TICKS_PER_SEC == 1024)" diff --git a/lib/bt/host/nimble/nimble/babblesim/sdk/.gitignore b/lib/bt/host/nimble/nimble/babblesim/sdk/.gitignore new file mode 100644 index 00000000..b269fe62 --- /dev/null +++ b/lib/bt/host/nimble/nimble/babblesim/sdk/.gitignore @@ -0,0 +1,3 @@ +components +nrfx +src \ No newline at end of file diff --git a/lib/bt/host/nimble/nimble/babblesim/sdk/pkg.yml b/lib/bt/host/nimble/nimble/babblesim/sdk/pkg.yml new file mode 100644 index 00000000..a542a304 --- /dev/null +++ b/lib/bt/host/nimble/nimble/babblesim/sdk/pkg.yml @@ -0,0 +1,47 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +pkg.name: babblesim/sdk +pkg.type: sdk +pkg.description: External files required to build BabbleSim +pkg.author: "Apache Mynewt " +pkg.homepage: "https://mynewt.apache.org/" + +pkg.cflags: -std=gnu99 + +pkg.include_dirs: + - components/ext_NRF52_hw_models/src/nrfx_config + - components/ext_NRF52_hw_models/src/nrfx/nrfx_replacements + - components/ext_NRF52_hw_models/src/nrfx/mdk_replacements + - components/ext_NRF52_hw_models/src/HW_models + - components/libUtilv1/src + - components/libPhyComv1/src + - components/libRandv2/src + - components/ext_libCryptov1/src + - nrfx + - nrfx/drivers + - nrfx/hal + - nrfx/mdk + +pkg.pre_build_cmds: + scripts/link_babblesim.sh: 1 + scripts/link_nrfx.sh: 2 + +pkg.lflags: + - -ldl diff --git a/lib/bt/host/nimble/nimble/babblesim/sdk/scripts/link_babblesim.sh b/lib/bt/host/nimble/nimble/babblesim/sdk/scripts/link_babblesim.sh new file mode 100755 index 00000000..9254a18a --- /dev/null +++ b/lib/bt/host/nimble/nimble/babblesim/sdk/scripts/link_babblesim.sh @@ -0,0 +1,47 @@ +#!/bin/bash +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +if [ -z ${BSIM_COMPONENTS_PATH+x} ]; then + echo "This board requires the BabbleSim simulator. Please set" \ + "the environment variable BSIM_COMPONENTS_PATH to point to its components" \ + "folder. More information can be found in" \ + "https://babblesim.github.io/folder_structure_and_env.html" + exit 1 +fi + +if [ -z ${BSIM_OUT_PATH+x} ]; then + echo "This board requires the BabbleSim simulator. Please set" \ + "the environment variable BSIM_OUT_PATH to point to the folder where the" \ + "simulator is compiled to. More information can be found in" \ + "https://babblesim.github.io/folder_structure_and_env.html" + exit 1 +fi + +ln -sfn "${BSIM_COMPONENTS_PATH}" ./components + +mkdir -p ./src/ +cp "${BSIM_OUT_PATH}"/lib/*.32.a ./src/ + +# XXX: Workaround for bad linking by newt. Sometimes newt will link +# nrf weak functions from nrf_hal_originals.o instead of their BabbleSim +# replacements inside libNRF52_hw_models.32.a. But as long as the other +# weak functions, that do not have their replacements, are not used, +# we can just remove the file from the .a library here. +ar d ./src/libNRF52_hw_models.32.a nrf_hal_originals.o diff --git a/lib/bt/host/nimble/nimble/babblesim/sdk/scripts/link_nrfx.sh b/lib/bt/host/nimble/nimble/babblesim/sdk/scripts/link_nrfx.sh new file mode 100755 index 00000000..6bc894e2 --- /dev/null +++ b/lib/bt/host/nimble/nimble/babblesim/sdk/scripts/link_nrfx.sh @@ -0,0 +1,21 @@ +#!/bin/bash +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +ln -sfn ${NRFX_BASE} ./nrfx diff --git a/lib/bt/host/nimble/nimble/babblesim/targets/blecent/pkg.yml b/lib/bt/host/nimble/nimble/babblesim/targets/blecent/pkg.yml new file mode 100644 index 00000000..45182193 --- /dev/null +++ b/lib/bt/host/nimble/nimble/babblesim/targets/blecent/pkg.yml @@ -0,0 +1,23 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +pkg.name: babblesim/targets/blecent +pkg.type: target +pkg.descrption: Sample target for running blecent on BabbleSim +pkg.author: "Apache Mynewt " +pkg.homepage: "https://mynewt.apache.org/" diff --git a/lib/bt/host/nimble/nimble/babblesim/targets/blecent/syscfg.yml b/lib/bt/host/nimble/nimble/babblesim/targets/blecent/syscfg.yml new file mode 100644 index 00000000..eb7d46ed --- /dev/null +++ b/lib/bt/host/nimble/nimble/babblesim/targets/blecent/syscfg.yml @@ -0,0 +1,20 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +syscfg.vals: + BLE_LL_PUBLIC_DEV_ADDR: 0xbabb1e000002 diff --git a/lib/bt/host/nimble/nimble/babblesim/targets/blecent/target.yml b/lib/bt/host/nimble/nimble/babblesim/targets/blecent/target.yml new file mode 100644 index 00000000..b4e69674 --- /dev/null +++ b/lib/bt/host/nimble/nimble/babblesim/targets/blecent/target.yml @@ -0,0 +1,21 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +target.app: "@apache-mynewt-nimble/apps/blecent" +target.bsp: "@apache-mynewt-nimble/babblesim/hw/bsp/nrf52_bsim" +target.build_profile: debug diff --git a/lib/bt/host/nimble/nimble/babblesim/targets/blehci/pkg.yml b/lib/bt/host/nimble/nimble/babblesim/targets/blehci/pkg.yml new file mode 100644 index 00000000..c8cdf78c --- /dev/null +++ b/lib/bt/host/nimble/nimble/babblesim/targets/blehci/pkg.yml @@ -0,0 +1,23 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +pkg.name: babblesim/targets/blehci +pkg.type: target +pkg.descrption: Sample target for running blehci on BabbleSim +pkg.author: "Apache Mynewt " +pkg.homepage: "https://mynewt.apache.org/" diff --git a/lib/bt/host/nimble/nimble/babblesim/targets/blehci/syscfg.yml b/lib/bt/host/nimble/nimble/babblesim/targets/blehci/syscfg.yml new file mode 100644 index 00000000..aec4c4a4 --- /dev/null +++ b/lib/bt/host/nimble/nimble/babblesim/targets/blehci/syscfg.yml @@ -0,0 +1,21 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +syscfg.vals: + BLE_LL_PUBLIC_DEV_ADDR: 0xbabb1e000001 + BLE_HCI_TRANSPORT: uart diff --git a/lib/bt/host/nimble/nimble/babblesim/targets/blehci/target.yml b/lib/bt/host/nimble/nimble/babblesim/targets/blehci/target.yml new file mode 100644 index 00000000..23953eb8 --- /dev/null +++ b/lib/bt/host/nimble/nimble/babblesim/targets/blehci/target.yml @@ -0,0 +1,21 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +target.app: "@apache-mynewt-nimble/apps/blehci" +target.bsp: "@apache-mynewt-nimble/babblesim/hw/bsp/nrf52_bsim" +target.build_profile: debug diff --git a/lib/bt/host/nimble/nimble/babblesim/targets/bleprph/pkg.yml b/lib/bt/host/nimble/nimble/babblesim/targets/bleprph/pkg.yml new file mode 100644 index 00000000..e8209bd3 --- /dev/null +++ b/lib/bt/host/nimble/nimble/babblesim/targets/bleprph/pkg.yml @@ -0,0 +1,23 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +pkg.name: babblesim/targets/bleprph +pkg.type: target +pkg.descrption: Sample target for running bleprph on BabbleSim +pkg.author: "Apache Mynewt " +pkg.homepage: "https://mynewt.apache.org/" diff --git a/lib/bt/host/nimble/nimble/babblesim/targets/bleprph/syscfg.yml b/lib/bt/host/nimble/nimble/babblesim/targets/bleprph/syscfg.yml new file mode 100644 index 00000000..279d94a2 --- /dev/null +++ b/lib/bt/host/nimble/nimble/babblesim/targets/bleprph/syscfg.yml @@ -0,0 +1,20 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +syscfg.vals: + BLE_LL_PUBLIC_DEV_ADDR: 0xbabb1e000003 diff --git a/lib/bt/host/nimble/nimble/babblesim/targets/bleprph/target.yml b/lib/bt/host/nimble/nimble/babblesim/targets/bleprph/target.yml new file mode 100644 index 00000000..e7a08543 --- /dev/null +++ b/lib/bt/host/nimble/nimble/babblesim/targets/bleprph/target.yml @@ -0,0 +1,21 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +target.app: "@apache-mynewt-nimble/apps/bleprph" +target.bsp: "@apache-mynewt-nimble/babblesim/hw/bsp/nrf52_bsim" +target.build_profile: debug diff --git a/lib/bt/host/nimble/nimble/babblesim/targets/btshell/pkg.yml b/lib/bt/host/nimble/nimble/babblesim/targets/btshell/pkg.yml new file mode 100644 index 00000000..cc0b4bc2 --- /dev/null +++ b/lib/bt/host/nimble/nimble/babblesim/targets/btshell/pkg.yml @@ -0,0 +1,23 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +pkg.name: babblesim/targets/btshell +pkg.type: target +pkg.descrption: Sample target for running btshell on BabbleSim +pkg.author: "Apache Mynewt " +pkg.homepage: "https://mynewt.apache.org/" diff --git a/lib/bt/host/nimble/nimble/babblesim/targets/btshell/syscfg.yml b/lib/bt/host/nimble/nimble/babblesim/targets/btshell/syscfg.yml new file mode 100644 index 00000000..152a6652 --- /dev/null +++ b/lib/bt/host/nimble/nimble/babblesim/targets/btshell/syscfg.yml @@ -0,0 +1,20 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +syscfg.vals: + BLE_LL_PUBLIC_DEV_ADDR: 0xbabb1e000004 diff --git a/lib/bt/host/nimble/nimble/babblesim/targets/btshell/target.yml b/lib/bt/host/nimble/nimble/babblesim/targets/btshell/target.yml new file mode 100644 index 00000000..3545e2d8 --- /dev/null +++ b/lib/bt/host/nimble/nimble/babblesim/targets/btshell/target.yml @@ -0,0 +1,21 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +target.app: "@apache-mynewt-nimble/apps/btshell" +target.bsp: "@apache-mynewt-nimble/babblesim/hw/bsp/nrf52_bsim" +target.build_profile: debug diff --git a/lib/bt/host/nimble/nimble/babblesim/targets/edtthci/pkg.yml b/lib/bt/host/nimble/nimble/babblesim/targets/edtthci/pkg.yml new file mode 100644 index 00000000..a08f47b8 --- /dev/null +++ b/lib/bt/host/nimble/nimble/babblesim/targets/edtthci/pkg.yml @@ -0,0 +1,24 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +pkg.name: babblesim/targets/edtthci +pkg.type: target +pkg.description: +pkg.author: +pkg.homepage: diff --git a/lib/bt/host/nimble/nimble/babblesim/targets/edtthci/syscfg.yml b/lib/bt/host/nimble/nimble/babblesim/targets/edtthci/syscfg.yml new file mode 100644 index 00000000..972659e3 --- /dev/null +++ b/lib/bt/host/nimble/nimble/babblesim/targets/edtthci/syscfg.yml @@ -0,0 +1,55 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +syscfg.vals: + # EDTT requires 0x123456789ABC address for the first device + # and 0x456789ABCDEF for the second one + BLE_LL_PUBLIC_DEV_ADDR: 0x123456789ABC # d=1 + # BLE_LL_PUBLIC_DEV_ADDR: 0x456789ABCDEF # d=2 + + EDTT_HCI_LOG_FILE: ("hci_logs") + EDTT_HCI_LOGS: 1 + + BLE_LL_CFG_FEAT_LE_ENCRYPTION: 1 + BLE_LL_CFG_FEAT_CONN_PARAM_REQ: 1 + BLE_LL_CFG_FEAT_SLAVE_INIT_FEAT_XCHG: 1 + BLE_LL_CFG_FEAT_LE_PING: 1 + BLE_LL_CFG_FEAT_DATA_LEN_EXT: 1 + BLE_LL_CFG_FEAT_LL_PRIVACY: 1 + BLE_LL_CFG_FEAT_LE_CSA2: 1 + BLE_LL_CFG_FEAT_LE_2M_PHY: 1 +# BLE_LL_CFG_FEAT_LE_CODED_PHY: 1 # not modeled in bsim + BLE_LL_CFG_FEAT_LL_EXT_ADV: 1 + BLE_LL_CFG_FEAT_LL_PERIODIC_ADV: 1 + BLE_LL_CFG_FEAT_LL_PERIODIC_ADV_SYNC_TRANSFER: 1 + BLE_LL_CFG_FEAT_CTRL_TO_HOST_FLOW_CONTROL: 1 + BLE_LL_CFG_FEAT_LL_SCA_UPDATE: 1 + + BLE_ROLE_CENTRAL: 1 + BLE_ROLE_PERIPHERAL: 1 + BLE_ROLE_BROADCASTER: 1 + BLE_ROLE_OBSERVER: 1 + + BLE_VERSION: 52 + BLE_CONTROLLER: 1 + BLE_LL_ROLE_CENTRAL: 1 + BLE_LL_ROLE_PERIPHERAL: 1 + BLE_LL_ROLE_BROADCASTER: 1 + BLE_LL_ROLE_OBSERVER: 1 + BLE_HW_WHITELIST_ENABLE: 1 diff --git a/lib/bt/host/nimble/nimble/babblesim/targets/edtthci/target.yml b/lib/bt/host/nimble/nimble/babblesim/targets/edtthci/target.yml new file mode 100644 index 00000000..3990ba4e --- /dev/null +++ b/lib/bt/host/nimble/nimble/babblesim/targets/edtthci/target.yml @@ -0,0 +1,22 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +target.app: "@apache-mynewt-nimble/babblesim/edtt/hci_test" +target.bsp: "@apache-mynewt-nimble/babblesim/hw/bsp/nrf52_bsim" +target.build_profile: debug diff --git a/lib/bt/host/nimble/nimble/docs/.gitignore b/lib/bt/host/nimble/nimble/docs/.gitignore new file mode 100644 index 00000000..2abe8a03 --- /dev/null +++ b/lib/bt/host/nimble/nimble/docs/.gitignore @@ -0,0 +1,5 @@ +xml +node_modules +_build +doxygen_* +*.pyc diff --git a/lib/bt/host/nimble/nimble/docs/Makefile b/lib/bt/host/nimble/nimble/docs/Makefile new file mode 100644 index 00000000..9c8793a1 --- /dev/null +++ b/lib/bt/host/nimble/nimble/docs/Makefile @@ -0,0 +1,25 @@ +# Make a preview site for Sphinx & Doxygen output + +SPHINXOPTS = +SPHINXBUILD = sphinx-build +SPHINXPROJ = Mynewt +SOURCEDIR = . +BUILDDIR = _build/sphinx + +.PHONY: Makefile clean preview doxygen + +clean: + rm -rf _build + +preview: _build doxygen sphinx + +_build: + mkdir -p _build + +doxygen: + mkdir -p _build/html + cd .. && doxygen docs/doxygen.xml + +sphinx: + sphinx-build . _build/sphinx + mv _build/sphinx _build/html/documentation diff --git a/lib/bt/host/nimble/nimble/docs/README.rst b/lib/bt/host/nimble/nimble/docs/README.rst new file mode 100644 index 00000000..ef2871c6 --- /dev/null +++ b/lib/bt/host/nimble/nimble/docs/README.rst @@ -0,0 +1,33 @@ +NimBLE Bluetooth Stack Documentation +################################# + +This folder holds the documentation for the NimBLE Bluetooth stack from the +`Apache Mynewt`_ project. It is built using `Sphinx`_. +The source code also contains inline comments in `Doxygen`_ +format to document the APIs. + +The complete project documentation can be found at `mynewt documentation`_ + +.. contents:: + +Writing Documentation +======================= + +See: https://github.com/apache/mynewt-documentation#writing-documentation + +Previewing Changes +========================== + +In order to preview any changes you make you must first install a Sphinx +toolchain as described at https://github.com/apache/mynewt-documentation#id3. + Then: + +.. code-block:: bash + + $ cd docs + $ make clean && make preview && (cd _build/html && python -m SimpleHTTPServer 8080) + +.. _Apache Mynewt: https://mynewt.apache.org/ +.. _mynewt documentation: https://github.com/apache/mynewt-documentation +.. _Sphinx: http://www.sphinx-doc.org/ +.. _Doxygen: http://www.doxygen.org/ diff --git a/lib/bt/host/nimble/nimble/docs/ble_hs/ble_att.rst b/lib/bt/host/nimble/nimble/docs/ble_hs/ble_att.rst new file mode 100644 index 00000000..2025784d --- /dev/null +++ b/lib/bt/host/nimble/nimble/docs/ble_hs/ble_att.rst @@ -0,0 +1,22 @@ +NimBLE Host ATT Client Reference +-------------------------------- + +Introduction +~~~~~~~~~~~~ + +The Attribute Protocol (ATT) is a mid-level protocol that all BLE devices use to exchange data. Data is exchanged when +an ATT client reads or writes an attribute belonging to an ATT server. Any device that needs to send or receive data +must support both the client and server functionality of the ATT protocol. The only devices which do not support ATT +are the most basic ones: broadcasters and observers (i.e., beaconing devices and listening devices). + +Most ATT functionality is not interesting to an application. Rather than use ATT directly, an application uses the +higher level GATT profile, which sits directly above ATT in the host. NimBLE exposes the few bits of ATT functionality +which are not encompassed by higher level GATT functions. This section documents the ATT functionality that the NimBLE +host exposes to the application. + +API +~~~~~~ + +.. doxygengroup:: bt_host + :content-only: + :members: diff --git a/lib/bt/host/nimble/nimble/docs/ble_hs/ble_gap.rst b/lib/bt/host/nimble/nimble/docs/ble_hs/ble_gap.rst new file mode 100644 index 00000000..d5c99854 --- /dev/null +++ b/lib/bt/host/nimble/nimble/docs/ble_hs/ble_gap.rst @@ -0,0 +1,14 @@ +NimBLE Host GAP Reference +------------------------- + +Introduction +~~~~~~~~~~~~ + +The Generic Access Profile (GAP) is responsible for all connecting, advertising, scanning, and connection updating operations. + +API +~~~~~~ + +.. doxygengroup:: bt_host_gap + :content-only: + :members: diff --git a/lib/bt/host/nimble/nimble/docs/ble_hs/ble_gattc.rst b/lib/bt/host/nimble/nimble/docs/ble_hs/ble_gattc.rst new file mode 100644 index 00000000..4668c5d9 --- /dev/null +++ b/lib/bt/host/nimble/nimble/docs/ble_hs/ble_gattc.rst @@ -0,0 +1,15 @@ +NimBLE Host GATT Client Reference +--------------------------------- + +Introduction +~~~~~~~~~~~~ + +The Generic Attribute Profile (GATT) manages all activities involving services, characteristics, and descriptors. The +client half of the GATT API initiates GATT procedures. + +API +~~~~~~ + +.. doxygengroup:: bt_gatt + :content-only: + :members: diff --git a/lib/bt/host/nimble/nimble/docs/ble_hs/ble_gatts.rst b/lib/bt/host/nimble/nimble/docs/ble_hs/ble_gatts.rst new file mode 100644 index 00000000..0a823f0b --- /dev/null +++ b/lib/bt/host/nimble/nimble/docs/ble_hs/ble_gatts.rst @@ -0,0 +1,15 @@ +NimBLE Host GATT Server Reference +--------------------------------- + +Introduction +~~~~~~~~~~~~ + +The Generic Attribute Profile (GATT) manages all activities involving services, characteristics, and descriptors. The +server half of the GATT API handles registration and responding to GATT clients. + +API +~~~~~~ + +.. doxygengroup:: bt_gatt + :content-only: + :members: diff --git a/lib/bt/host/nimble/nimble/docs/ble_hs/ble_hs.rst b/lib/bt/host/nimble/nimble/docs/ble_hs/ble_hs.rst new file mode 100644 index 00000000..844ede1e --- /dev/null +++ b/lib/bt/host/nimble/nimble/docs/ble_hs/ble_hs.rst @@ -0,0 +1,27 @@ +NimBLE Host +----------- + +Introduction +~~~~~~~~~~~~ + +At a high level, the NimBLE stack is divided into two components: + +- Host +- Controller + +This document is an API reference for the host component. If you are +interested in the general structure of the NimBLE stack and its non-host +components, you might want to read the :doc:`../index`. + +The host sits directly below the application, and it serves as the +interface to the application for all BLE operations. + +.. toctree:: + :titlesonly: + + Return Codes + GAP + GATT Client + GATT Server + Identity + ATT diff --git a/lib/bt/host/nimble/nimble/docs/ble_hs/ble_hs_id.rst b/lib/bt/host/nimble/nimble/docs/ble_hs/ble_hs_id.rst new file mode 100644 index 00000000..dbb47c94 --- /dev/null +++ b/lib/bt/host/nimble/nimble/docs/ble_hs/ble_hs_id.rst @@ -0,0 +1,45 @@ +NimBLE Host Identity Reference +------------------------------ + +Introduction +~~~~~~~~~~~~ + +The identity API provides facilities for querying and configuring your device's addresses. BLE's addressing scheme is +quite involved; the summary that follows is only a brief introduction. + +BLE defines four address types: + ++---------------------------------+---------------------------------------------------------------------------------------------------+-------------+----------------------------------------------+ +| Type | Description | Identity? | Configured with | ++=================================+===================================================================================================+=============+==============================================+ +| Public | Address assigned by manufacturer; the three most significant bytes form the manufacturer's OUI. | Yes | N/A; read from controller at startup. | ++---------------------------------+---------------------------------------------------------------------------------------------------+-------------+----------------------------------------------+ +| Static random | Randomly generated address. | Yes | *ble_hs_id_set_rnd()* | ++---------------------------------+---------------------------------------------------------------------------------------------------+-------------+----------------------------------------------+ +| Resolvable private (RPA) | Address randomly generated from an identity address and an identity resolving key (IRK). | No | N/A; generated by controller periodically. | ++---------------------------------+---------------------------------------------------------------------------------------------------+-------------+----------------------------------------------+ +| Non-resolvable private (NRPA) | Randomly generated address. | No | *ble_hs_id_set_rnd()* | ++---------------------------------+---------------------------------------------------------------------------------------------------+-------------+----------------------------------------------+ + +Identity Addresses +^^^^^^^^^^^^^^^^^^ + +The third column in the above table indicates the *identity* property of each address type. An identity address never +changes, and a device can be identified by one of its unique identity addresses. + +Non-identity addresses are used by devices supporting BLE privacy. A device using the privacy feature frequently changes +its own address to a newly-generated non-identity address. By cycling its address, the device makes it impossible for +eavesdroppers to track its location. + +A device can have up to two identity addresses at once: one public and one static random. As indicated in the above table, +the public identity address cannot be configured; the static random identity address can be set by calling *ble_hs_id_set_rnd()*. + +The address type is selected on a per-GAP-procedure basis. Each time you initiate a GAP procedure, you indicate which +address type the device should use for the duration of the procedure. + +Header +~~~~~~ + +.. code-block:: cpp + + #include "host/ble_hs.h" diff --git a/lib/bt/host/nimble/nimble/docs/ble_hs/ble_hs_return_codes.rst b/lib/bt/host/nimble/nimble/docs/ble_hs/ble_hs_return_codes.rst new file mode 100644 index 00000000..c69cc4f8 --- /dev/null +++ b/lib/bt/host/nimble/nimble/docs/ble_hs/ble_hs_return_codes.rst @@ -0,0 +1,437 @@ +NimBLE Host Return Codes +------------------------ + +.. contents:: + :local: + :depth: 2 + +Introduction +~~~~~~~~~~~~ + +Summary +^^^^^^^ + +The NimBLE host reports status to the application via a set of return codes. The host encompasses several layers of the Bluetooth specification that each defines its own set of status codes. Rather than "abstract away" information from lower layers that the application developer might find useful, the NimBLE host aims to indicate precisely what happened when something fails. Consequently, the host utilizes a rather large set of return codes. + +A return code of 0 indicates success. For failure conditions, the return codes are partitioned into five separate sets: + ++---------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| Set | Condition | ++===========================+=============================================================================================================================================================================================================+ +| Core | Errors detected internally by the NimBLE host. | ++---------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| ATT | The ATT server has reported a failure via the transmission of an ATT Error Response. The return code corresponds to the value of the Error Code field in the response. | ++---------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| HCI | The controller has reported an error to the host via a command complete or command status HCI event. The return code corresponds to the value of the Status field in the event. | ++---------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| L2CAP | An L2CAP signaling procedure has failed and an L2CAP Command Reject was sent as a result. The return code corresponds to the value of the Reason field in the command. | ++---------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| Security manager (us) | The host detected an error during a security manager procedure and sent a Pairing Failed command to the peer. The return code corresponds to the value of the Reason field in the Pairing Failed command. | ++---------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| Security manager (peer) | A security manager procedure failed because the peer sent us a Pairing Failed command. The return code corresponds to the value of the Reason field in the Pairing Failed command. | ++---------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ + +The return codes in the core set are defined by the NimBLE Host. The other sets are defined in the Bluetooth specification; the codes in this latter group are referred to as *formal status codes*. As defined in the Bluetooth specification, the formal status code sets are not disjoint. That is, they overlap. For example, the spec defines a status code of 1 to have all of the following meanings: + ++---------+----------------------------+ +| Layer | Meaning | ++=========+============================+ +| ATT | Invalid handle. | ++---------+----------------------------+ +| HCI | Unknown HCI command. | ++---------+----------------------------+ +| L2CAP | Signalling MTU exceeded. | ++---------+----------------------------+ +| SM | Passkey entry failed. | ++---------+----------------------------+ + +Clearly, the host can't just return an unadorned formal status code and expect the application to make sense of it. To resolve this ambiguity, the NimBLE host divides the full range of an int into several subranges. Each subrange corresponds to one of the five return code sets. For example, the ATT set is mapped onto the subrange *[0x100, 0x200)*. To indicate an ATT error of 3 (write not permitted), the NimBLE host returns a value 0x103 to the application. + +The host defines a set of convenience macros for converting from a formal status code to NimBLE host status code. These macros are documented in the table below. + ++----------------------------+---------------------------+--------------+ +| Macro | Status code set | Base value | ++============================+===========================+==============+ +| BLE\_HS\_ATT\_ERR() | ATT | 0x100 | ++----------------------------+---------------------------+--------------+ +| BLE\_HS\_HCI\_ERR() | HCI | 0x200 | ++----------------------------+---------------------------+--------------+ +| BLE\_HS\_L2C\_ERR() | L2CAP | 0x300 | ++----------------------------+---------------------------+--------------+ +| BLE\_HS\_SM\_US\_ERR() | Security manager (us) | 0x400 | ++----------------------------+---------------------------+--------------+ +| BLE\_HS\_SM\_PEER\_ERR() | Security manager (peer) | 0x500 | ++----------------------------+---------------------------+--------------+ + +Example +^^^^^^^ + +The following example demonstrates how an application might determine which error is being reported by the host. In this example, the application performs the GAP encryption procedure and checks the return code. To simplify the example, the application uses a hypothetical *my\_blocking\_enc\_proc()* function, which blocks until the pairing operation has completed. + +.. code:: c + + void + encrypt_connection(uint16_t conn_handle) + { + int rc; + + /* Perform a blocking GAP encryption procedure. */ + rc = my_blocking_enc_proc(conn_handle); + switch (rc) { + case 0: + console_printf("success - link successfully encrypted\n"); + break; + + case BLE_HS_ENOTCONN: + console_printf("failure - no connection with handle %d\n", + conn_handle); + break; + + case BLE_HS_ERR_SM_US_BASE(BLE_SM_ERR_CONFIRM_MISMATCH): + console_printf("failure - mismatch in peer's confirm and random " + "commands.\n"); + break; + + case BLE_HS_ERR_SM_PEER_BASE(BLE_SM_ERR_CONFIRM_MISMATCH): + console_printf("failure - peer reports mismatch in our confirm and " + "random commands.\n"); + break; + + default: + console_printf("failure - other error: 0x%04x\n", rc); + break; + } + } + +Return Code Reference +~~~~~~~~~~~~~~~~~~~~~ + +Header +^^^^^^ + +All NimBLE host return codes are made accessible by including the following header: + +.. code:: c + + #include "host/ble_hs.h" + +Return codes - Core +^^^^^^^^^^^^^^^^^^^ + +The precise meaning of each of these error codes depends on the function that returns it. +The API reference for a particular function indicates the conditions under which each of these codes are returned. + ++---------+------------------------------+---------------------------------------------------------------------------------------------+ +| Value | Name | Condition | ++=========+==============================+=============================================================================================+ +| 0x00 | *N/A* | Success | ++---------+------------------------------+---------------------------------------------------------------------------------------------+ +| 0x01 | BLE\_HS\_EAGAIN | Temporary failure; try again. | ++---------+------------------------------+---------------------------------------------------------------------------------------------+ +| 0x02 | BLE\_HS\_EALREADY | Operation already in progress or completed. | ++---------+------------------------------+---------------------------------------------------------------------------------------------+ +| 0x03 | BLE\_HS\_EINVAL | One or more arguments are invalid. | ++---------+------------------------------+---------------------------------------------------------------------------------------------+ +| 0x04 | BLE\_HS\_EMSGSIZE | The provided buffer is too small. | ++---------+------------------------------+---------------------------------------------------------------------------------------------+ +| 0x05 | BLE\_HS\_ENOENT | No entry matching the specified criteria. | ++---------+------------------------------+---------------------------------------------------------------------------------------------+ +| 0x06 | BLE\_HS\_ENOMEM | Operation failed due to resource exhaustion. | ++---------+------------------------------+---------------------------------------------------------------------------------------------+ +| 0x07 | BLE\_HS\_ENOTCONN | No open connection with the specified handle. | ++---------+------------------------------+---------------------------------------------------------------------------------------------+ +| 0x08 | BLE\_HS\_ENOTSUP | Operation disabled at compile time. | ++---------+------------------------------+---------------------------------------------------------------------------------------------+ +| 0x09 | BLE\_HS\_EAPP | Application callback behaved unexpectedly. | ++---------+------------------------------+---------------------------------------------------------------------------------------------+ +| 0x0a | BLE\_HS\_EBADDATA | Command from peer is invalid. | ++---------+------------------------------+---------------------------------------------------------------------------------------------+ +| 0x0b | BLE\_HS\_EOS | Mynewt OS error. | ++---------+------------------------------+---------------------------------------------------------------------------------------------+ +| 0x0c | BLE\_HS\_ECONTROLLER | Event from controller is invalid. | ++---------+------------------------------+---------------------------------------------------------------------------------------------+ +| 0x0d | BLE\_HS\_ETIMEOUT | Operation timed out. | ++---------+------------------------------+---------------------------------------------------------------------------------------------+ +| 0x0e | BLE\_HS\_EDONE | Operation completed successfully. | ++---------+------------------------------+---------------------------------------------------------------------------------------------+ +| 0x0f | BLE\_HS\_EBUSY | Operation cannot be performed until procedure completes. | ++---------+------------------------------+---------------------------------------------------------------------------------------------+ +| 0x10 | BLE\_HS\_EREJECT | Peer rejected a connection parameter update request. | ++---------+------------------------------+---------------------------------------------------------------------------------------------+ +| 0x11 | BLE\_HS\_EUNKNOWN | Unexpected failure; catch all. | ++---------+------------------------------+---------------------------------------------------------------------------------------------+ +| 0x12 | BLE\_HS\_EROLE | Operation requires different role (e.g., central vs. peripheral). | ++---------+------------------------------+---------------------------------------------------------------------------------------------+ +| 0x13 | BLE\_HS\_ETIMEOUT\_HCI | HCI request timed out; controller unresponsive. | ++---------+------------------------------+---------------------------------------------------------------------------------------------+ +| 0x14 | BLE\_HS\_ENOMEM\_EVT | Controller failed to send event due to memory exhaustion (combined host-controller only). | ++---------+------------------------------+---------------------------------------------------------------------------------------------+ +| 0x15 | BLE\_HS\_ENOADDR | Operation requires an identity address but none configured. | ++---------+------------------------------+---------------------------------------------------------------------------------------------+ +| 0x16 | BLE\_HS\_ENOTSYNCED | Attempt to use the host before it is synced with controller. | ++---------+------------------------------+---------------------------------------------------------------------------------------------+ +| 0x17 | BLE\_HS\_EAUTHEN | Insufficient authentication. | ++---------+------------------------------+---------------------------------------------------------------------------------------------+ +| 0x18 | BLE\_HS\_EAUTHOR | Insufficient authorization. | ++---------+------------------------------+---------------------------------------------------------------------------------------------+ +| 0x19 | BLE\_HS\_EENCRYPT | Insufficient encryption level. | ++---------+------------------------------+---------------------------------------------------------------------------------------------+ +| 0x1a | BLE\_HS\_EENCRYPT\_KEY\_SZ | Insufficient key size. | ++---------+------------------------------+---------------------------------------------------------------------------------------------+ +| 0x1b | BLE\_HS\_ESTORE\_CAP | Storage at capacity. | ++---------+------------------------------+---------------------------------------------------------------------------------------------+ +| 0x1c | BLE\_HS\_ESTORE\_FAIL | Storage IO error. | ++---------+------------------------------+---------------------------------------------------------------------------------------------+ + +Return codes - ATT +^^^^^^^^^^^^^^^^^^ + ++----------------+----------------+--------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +| NimBLE Value | Formal Value | Name | Condition | ++================+================+============================================+===========================================================================================================================================+ +| 0x0101 | 0x01 | BLE\_ATT\_ERR\_INVALID\_HANDLE | The attribute handle given was not valid on this server. | ++----------------+----------------+--------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +| 0x0102 | 0x02 | BLE\_ATT\_ERR\_READ\_NOT\_PERMITTED | The attribute cannot be read. | ++----------------+----------------+--------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +| 0x0103 | 0x03 | BLE\_ATT\_ERR\_WRITE\_NOT\_PERMITTED | The attribute cannot be written. | ++----------------+----------------+--------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +| 0x0104 | 0x04 | BLE\_ATT\_ERR\_INVALID\_PDU | The attribute PDU was invalid. | ++----------------+----------------+--------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +| 0x0105 | 0x05 | BLE\_ATT\_ERR\_INSUFFICIENT\_AUTHEN | The attribute requires authentication before it can be read or written. | ++----------------+----------------+--------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +| 0x0106 | 0x06 | BLE\_ATT\_ERR\_REQ\_NOT\_SUPPORTED | Attribute server does not support the request received from the client. | ++----------------+----------------+--------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +| 0x0107 | 0x07 | BLE\_ATT\_ERR\_INVALID\_OFFSET | Offset specified was past the end of the attribute. | ++----------------+----------------+--------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +| 0x0108 | 0x08 | BLE\_ATT\_ERR\_INSUFFICIENT\_AUTHOR | The attribute requires authorization before it can be read or written. | ++----------------+----------------+--------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +| 0x0109 | 0x09 | BLE\_ATT\_ERR\_PREPARE\_QUEUE\_FULL | Too many prepare writes have been queued. | ++----------------+----------------+--------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +| 0x010a | 0x0a | BLE\_ATT\_ERR\_ATTR\_NOT\_FOUND | No attribute found within the given attribute handle range. | ++----------------+----------------+--------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +| 0x010b | 0x0b | BLE\_ATT\_ERR\_ATTR\_NOT\_LONG | The attribute cannot be read or written using the Read Blob Request. | ++----------------+----------------+--------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +| 0x010c | 0x0c | BLE\_ATT\_ERR\_INSUFFICIENT\_KEY\_SZ | The Encryption Key Size used for encrypting this link is insufficient. | ++----------------+----------------+--------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +| 0x010d | 0x0d | BLE\_ATT\_ERR\_INVALID\_ATTR\_VALUE\_LEN | The attribute value length is invalid for the operation. | ++----------------+----------------+--------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +| 0x010e | 0x0e | BLE\_ATT\_ERR\_UNLIKELY | The attribute request that was requested has encountered an error that was unlikely, and therefore could not be completed as requested. | ++----------------+----------------+--------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +| 0x010f | 0x0f | BLE\_ATT\_ERR\_INSUFFICIENT\_ENC | The attribute requires encryption before it can be read or written. | ++----------------+----------------+--------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +| 0x0110 | 0x10 | BLE\_ATT\_ERR\_UNSUPPORTED\_GROUP | The attribute type is not a supported grouping attribute as defined by a higher layer specification. | ++----------------+----------------+--------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +| 0x0111 | 0x11 | BLE\_ATT\_ERR\_INSUFFICIENT\_RES | Insufficient Resources to complete the request. | ++----------------+----------------+--------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ + +Return codes - HCI +^^^^^^^^^^^^^^^^^^ + ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| NimBLE Value | Formal Value | Name | Condition | ++================+================+====================================+================================================================================+ +| 0x0201 | 0x01 | BLE\_ERR\_UNKNOWN\_HCI\_CMD | Unknown HCI Command | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x0202 | 0x02 | BLE\_ERR\_UNK\_CONN\_ID | Unknown Connection Identifier | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x0203 | 0x03 | BLE\_ERR\_HW\_FAIL | Hardware Failure | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x0204 | 0x04 | BLE\_ERR\_PAGE\_TMO | Page Timeout | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x0205 | 0x05 | BLE\_ERR\_AUTH\_FAIL | Authentication Failure | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x0206 | 0x06 | BLE\_ERR\_PINKEY\_MISSING | PIN or Key Missing | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x0207 | 0x07 | BLE\_ERR\_MEM\_CAPACITY | Memory Capacity Exceeded | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x0208 | 0x08 | BLE\_ERR\_CONN\_SPVN\_TMO | Connection Timeout | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x0209 | 0x09 | BLE\_ERR\_CONN\_LIMIT | Connection Limit Exceeded | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x020a | 0x0a | BLE\_ERR\_SYNCH\_CONN\_LIMIT | Synchronous Connection Limit To A Device Exceeded | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x020b | 0x0b | BLE\_ERR\_ACL\_CONN\_EXISTS | ACL Connection Already Exists | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x020c | 0x0c | BLE\_ERR\_CMD\_DISALLOWED | Command Disallowed | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x020d | 0x0d | BLE\_ERR\_CONN\_REJ\_RESOURCES | Connection Rejected due to Limited Resources | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x020e | 0x0e | BLE\_ERR\_CONN\_REJ\_SECURITY | Connection Rejected Due To Security Reasons | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x020f | 0x0f | BLE\_ERR\_CONN\_REJ\_BD\_ADDR | Connection Rejected due to Unacceptable BD\_ADDR | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x0210 | 0x10 | BLE\_ERR\_CONN\_ACCEPT\_TMO | Connection Accept Timeout Exceeded | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x0211 | 0x11 | BLE\_ERR\_UNSUPPORTED | Unsupported Feature or Parameter Value | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x0212 | 0x12 | BLE\_ERR\_INV\_HCI\_CMD\_PARMS | Invalid HCI Command Parameters | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x0213 | 0x13 | BLE\_ERR\_REM\_USER\_CONN\_TERM | Remote User Terminated Connection | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x0214 | 0x14 | BLE\_ERR\_RD\_CONN\_TERM\_RESRCS | Remote Device Terminated Connection due to Low Resources | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x0215 | 0x15 | BLE\_ERR\_RD\_CONN\_TERM\_PWROFF | Remote Device Terminated Connection due to Power Off | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x0216 | 0x16 | BLE\_ERR\_CONN\_TERM\_LOCAL | Connection Terminated By Local Host | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x0217 | 0x17 | BLE\_ERR\_REPEATED\_ATTEMPTS | Repeated Attempts | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x0218 | 0x18 | BLE\_ERR\_NO\_PAIRING | Pairing Not Allowed | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x0219 | 0x19 | BLE\_ERR\_UNK\_LMP | Unknown LMP PDU | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x021a | 0x1a | BLE\_ERR\_UNSUPP\_REM\_FEATURE | Unsupported Remote Feature / Unsupported LMP Feature | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x021b | 0x1b | BLE\_ERR\_SCO\_OFFSET | SCO Offset Rejected | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x021c | 0x1c | BLE\_ERR\_SCO\_ITVL | SCO Interval Rejected | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x021d | 0x1d | BLE\_ERR\_SCO\_AIR\_MODE | SCO Air Mode Rejected | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x021e | 0x1e | BLE\_ERR\_INV\_LMP\_LL\_PARM | Invalid LMP Parameters / Invalid LL Parameters | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x021f | 0x1f | BLE\_ERR\_UNSPECIFIED | Unspecified Error | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x0220 | 0x20 | BLE\_ERR\_UNSUPP\_LMP\_LL\_PARM | Unsupported LMP Parameter Value / Unsupported LL Parameter Value | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x0221 | 0x21 | BLE\_ERR\_NO\_ROLE\_CHANGE | Role Change Not Allowed | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x0222 | 0x22 | BLE\_ERR\_LMP\_LL\_RSP\_TMO | LMP Response Timeout / LL Response Timeout | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x0223 | 0x23 | BLE\_ERR\_LMP\_COLLISION | LMP Error Transaction Collision | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x0224 | 0x24 | BLE\_ERR\_LMP\_PDU | LMP PDU Not Allowed | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x0225 | 0x25 | BLE\_ERR\_ENCRYPTION\_MODE | Encryption Mode Not Acceptable | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x0226 | 0x26 | BLE\_ERR\_LINK\_KEY\_CHANGE | Link Key cannot be Changed | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x0227 | 0x27 | BLE\_ERR\_UNSUPP\_QOS | Requested QoS Not Supported | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x0228 | 0x28 | BLE\_ERR\_INSTANT\_PASSED | Instant Passed | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x0229 | 0x29 | BLE\_ERR\_UNIT\_KEY\_PAIRING | Pairing With Unit Key Not Supported | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x022a | 0x2a | BLE\_ERR\_DIFF\_TRANS\_COLL | Different Transaction Collision | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x022c | 0x2c | BLE\_ERR\_QOS\_PARM | QoS Unacceptable Parameter | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x022d | 0x2d | BLE\_ERR\_QOS\_REJECTED | QoS Rejected | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x022e | 0x2e | BLE\_ERR\_CHAN\_CLASS | Channel Classification Not Supported | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x022f | 0x2f | BLE\_ERR\_INSUFFICIENT\_SEC | Insufficient Security | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x0230 | 0x30 | BLE\_ERR\_PARM\_OUT\_OF\_RANGE | Parameter Out Of Mandatory Range | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x0232 | 0x32 | BLE\_ERR\_PENDING\_ROLE\_SW | Role Switch Pending | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x0234 | 0x34 | BLE\_ERR\_RESERVED\_SLOT | Reserved Slot Violation | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x0235 | 0x35 | BLE\_ERR\_ROLE\_SW\_FAIL | Role Switch Failed | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x0236 | 0x36 | BLE\_ERR\_INQ\_RSP\_TOO\_BIG | Extended Inquiry Response Too Large | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x0237 | 0x37 | BLE\_ERR\_SEC\_SIMPLE\_PAIR | Secure Simple Pairing Not Supported By Host | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x0238 | 0x38 | BLE\_ERR\_HOST\_BUSY\_PAIR | Host Busy - Pairing | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x0239 | 0x39 | BLE\_ERR\_CONN\_REJ\_CHANNEL | Connection Rejected due to No Suitable Channel Found | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x023a | 0x3a | BLE\_ERR\_CTLR\_BUSY | Controller Busy | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x023b | 0x3b | BLE\_ERR\_CONN\_PARMS | Unacceptable Connection Parameters | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x023c | 0x3c | BLE\_ERR\_DIR\_ADV\_TMO | Directed Advertising Timeout | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x023d | 0x3d | BLE\_ERR\_CONN\_TERM\_MIC | Connection Terminated due to MIC Failure | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x023e | 0x3e | BLE\_ERR\_CONN\_ESTABLISHMENT | Connection Failed to be Established | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x023f | 0x3f | BLE\_ERR\_MAC\_CONN\_FAIL | MAC Connection Failed | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x0240 | 0x40 | BLE\_ERR\_COARSE\_CLK\_ADJ | Coarse Clock Adjustment Rejected but Will Try to Adjust Using Clock Dragging | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ + +Return codes - L2CAP +^^^^^^^^^^^^^^^^^^^^ + ++----------------+----------------+----------------------------------------------+------------------------------------------------------+ +| NimBLE Value | Formal Value | Name | Condition | ++================+================+==============================================+======================================================+ +| 0x0300 | 0x00 | BLE\_L2CAP\_SIG\_ERR\_CMD\_NOT\_UNDERSTOOD | Invalid or unsupported incoming L2CAP sig command. | ++----------------+----------------+----------------------------------------------+------------------------------------------------------+ +| 0x0301 | 0x01 | BLE\_L2CAP\_SIG\_ERR\_MTU\_EXCEEDED | Incoming packet too large. | ++----------------+----------------+----------------------------------------------+------------------------------------------------------+ +| 0x0302 | 0x02 | BLE\_L2CAP\_SIG\_ERR\_INVALID\_CID | No channel with specified ID. | ++----------------+----------------+----------------------------------------------+------------------------------------------------------+ + +Return codes - Security manager (us) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + ++----------------+----------------+-----------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +| NimBLE Value | Formal Value | Name | Condition | ++================+================+===================================+===========================================================================================================================================+ +| 0x0401 | 0x01 | BLE\_SM\_ERR\_PASSKEY | The user input of passkey failed, for example, the user cancelled the operation. | ++----------------+----------------+-----------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +| 0x0402 | 0x02 | BLE\_SM\_ERR\_OOB | The OOB data is not available. | ++----------------+----------------+-----------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +| 0x0403 | 0x03 | BLE\_SM\_ERR\_AUTHREQ | The pairing procedure cannot be performed as authentication requirements cannot be met due to IO capabilities of one or both devices. | ++----------------+----------------+-----------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +| 0x0404 | 0x04 | BLE\_SM\_ERR\_CONFIRM\_MISMATCH | The confirm value does not match the calculated compare value. | ++----------------+----------------+-----------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +| 0x0405 | 0x05 | BLE\_SM\_ERR\_PAIR\_NOT\_SUPP | Pairing is not supported by the device. | ++----------------+----------------+-----------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +| 0x0406 | 0x06 | BLE\_SM\_ERR\_ENC\_KEY\_SZ | The resultant encryption key size is insufficient for the security requirements of this device. | ++----------------+----------------+-----------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +| 0x0407 | 0x07 | BLE\_SM\_ERR\_CMD\_NOT\_SUPP | The SMP command received is not supported on this device. | ++----------------+----------------+-----------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +| 0x0408 | 0x08 | BLE\_SM\_ERR\_UNSPECIFIED | Pairing failed due to an unspecified reason. | ++----------------+----------------+-----------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +| 0x0409 | 0x09 | BLE\_SM\_ERR\_REPEATED | Pairing or authentication procedure is disallowed because too little time has elapsed since last pairing request or security request. | ++----------------+----------------+-----------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +| 0x040a | 0x0a | BLE\_SM\_ERR\_INVAL | The Invalid Parameters error code indicates that the command length is invalid or that a parameter is outside of the specified range. | ++----------------+----------------+-----------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +| 0x040b | 0x0b | BLE\_SM\_ERR\_DHKEY | Indicates to the remote device that the DHKey Check value received doesn’t match the one calculated by the local device. | ++----------------+----------------+-----------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +| 0x040c | 0x0c | BLE\_SM\_ERR\_NUMCMP | Indicates that the confirm values in the numeric comparison protocol do not match. | ++----------------+----------------+-----------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +| 0x040d | 0x0d | BLE\_SM\_ERR\_ALREADY | Indicates that the pairing over the LE transport failed due to a Pairing Request sent over the BR/EDR transport in process. | ++----------------+----------------+-----------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +| 0x040e | 0x0e | BLE\_SM\_ERR\_CROSS\_TRANS | Indicates that the BR/EDR Link Key generated on the BR/EDR transport cannot be used to derive and distribute keys for the LE transport. | ++----------------+----------------+-----------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ + +Return codes - Security manager (peer) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + ++----------------+----------------+-----------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +| NimBLE Value | Formal Value | Name | Condition | ++================+================+===================================+===========================================================================================================================================+ +| 0x0501 | 0x01 | BLE\_SM\_ERR\_PASSKEY | The user input of passkey failed, for example, the user cancelled the operation. | ++----------------+----------------+-----------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +| 0x0502 | 0x02 | BLE\_SM\_ERR\_OOB | The OOB data is not available. | ++----------------+----------------+-----------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +| 0x0503 | 0x03 | BLE\_SM\_ERR\_AUTHREQ | The pairing procedure cannot be performed as authentication requirements cannot be met due to IO capabilities of one or both devices. | ++----------------+----------------+-----------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +| 0x0504 | 0x04 | BLE\_SM\_ERR\_CONFIRM\_MISMATCH | The confirm value does not match the calculated compare value. | ++----------------+----------------+-----------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +| 0x0505 | 0x05 | BLE\_SM\_ERR\_PAIR\_NOT\_SUPP | Pairing is not supported by the device. | ++----------------+----------------+-----------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +| 0x0506 | 0x06 | BLE\_SM\_ERR\_ENC\_KEY\_SZ | The resultant encryption key size is insufficient for the security requirements of this device. | ++----------------+----------------+-----------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +| 0x0507 | 0x07 | BLE\_SM\_ERR\_CMD\_NOT\_SUPP | The SMP command received is not supported on this device. | ++----------------+----------------+-----------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +| 0x0508 | 0x08 | BLE\_SM\_ERR\_UNSPECIFIED | Pairing failed due to an unspecified reason. | ++----------------+----------------+-----------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +| 0x0509 | 0x09 | BLE\_SM\_ERR\_REPEATED | Pairing or authentication procedure is disallowed because too little time has elapsed since last pairing request or security request. | ++----------------+----------------+-----------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +| 0x050a | 0x0a | BLE\_SM\_ERR\_INVAL | The Invalid Parameters error code indicates that the command length is invalid or that a parameter is outside of the specified range. | ++----------------+----------------+-----------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +| 0x050b | 0x0b | BLE\_SM\_ERR\_DHKEY | Indicates to the remote device that the DHKey Check value received doesn’t match the one calculated by the local device. | ++----------------+----------------+-----------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +| 0x050c | 0x0c | BLE\_SM\_ERR\_NUMCMP | Indicates that the confirm values in the numeric comparison protocol do not match. | ++----------------+----------------+-----------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +| 0x050d | 0x0d | BLE\_SM\_ERR\_ALREADY | Indicates that the pairing over the LE transport failed due to a Pairing Request sent over the BR/EDR transport in process. | ++----------------+----------------+-----------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +| 0x050e | 0x0e | BLE\_SM\_ERR\_CROSS\_TRANS | Indicates that the BR/EDR Link Key generated on the BR/EDR transport cannot be used to derive and distribute keys for the LE transport. | ++----------------+----------------+-----------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ diff --git a/lib/bt/host/nimble/nimble/docs/ble_sec.rst b/lib/bt/host/nimble/nimble/docs/ble_sec.rst new file mode 100644 index 00000000..0cc15e63 --- /dev/null +++ b/lib/bt/host/nimble/nimble/docs/ble_sec.rst @@ -0,0 +1,76 @@ +NimBLE Security +--------------- + +The Bluetooth Low Energy security model includes five distinct security +concepts as listed below. For detailed specifications, see BLUETOOTH +SPECIFICATION Version 4.2 [Vol 1, Part A]. + +- **Pairing**: The process for creating one or more shared secret keys. + In LE a single link key is generated by combining contributions from + each device into a link key used during pairing. + +- **Bonding**: The act of storing the keys created during pairing for + use in subsequent connections in order to form a trusted device pair. + +- **Device authentication**: Verification that the two devices have the + same keys (verify device identity) + +- **Encryption**: Keeps message confidential. Encryption in Bluetooth + LE uses AES-CCM cryptography and is performed in the *Controller*. + +- **Message integrity**: Protects against message forgeries. + +Bluetooth LE uses four association models depending on the I/O +capabilities of the devices. + +- **Just Works**: designed for scenarios where at least one of the + devices does not have a display capable of displaying a six digit + number nor does it have a keyboard capable of entering six decimal + digits. + +- **Numeric Comparison**: designed for scenarios where both devices are + capable of displaying a six digit number and both are capable of + having the user enter "yes" or "no". A good example of this model is + the cell phone / PC scenario. + +- **Out of Band**: designed for scenarios where an Out of Band + mechanism is used to both discover the devices as well as to exchange + or transfer cryptographic numbers used in the pairing process. + +- **Passkey Entry**: designed for the scenario where one device has + input capability but does not have the capability to display six + digits and the other device has output capabilities. A good example + of this model is the PC and keyboard scenario. + +Key Generation +~~~~~~~~~~~~~~ + +Key generation for all purposes in Bluetooth LE is performed by the +*Host* on each LE device independent of any other LE device. + +Privacy Feature +~~~~~~~~~~~~~~~ + +Bluetooth LE supports an optional feature during connection mode and +connection procedures that reduces the ability to track a LE device over +a period of time by changing the Bluetooth device address on a frequent +basis. + +There are two variants of the privacy feature. + +- In the first variant, private addresses are resolved and generated by + the *Host*. +- In the second variant, private addresses are resolved and generated + by the *Controller* without involving the Host after the Host + provides the Controller device identity information. The Host may + provide the Controller with a complete resolving list or a subset of + the resolving list. Device filtering becomes possible in the second + variant when address resolution is performed in the Controller + because the peer’s device identity address can be resolved prior to + checking whether it is in the white list. + +**Note**: When address resolution is performed exclusively in the Host, +a device may experience increased power consumption because device +filtering must be disabled. For more details on the privacy feature, +refer to BLUETOOTH SPECIFICATION Version 4.2 [Vol 3, Part C] (Published +02 December 2014), Page 592. diff --git a/lib/bt/host/nimble/nimble/docs/ble_setup/ble_addr.rst b/lib/bt/host/nimble/nimble/docs/ble_setup/ble_addr.rst new file mode 100644 index 00000000..0a67a5f7 --- /dev/null +++ b/lib/bt/host/nimble/nimble/docs/ble_setup/ble_addr.rst @@ -0,0 +1,63 @@ +Configure device address +------------------------ + +A BLE device needs an address to do just about anything. For information +on the various types of Bluetooth addresses, see the `NimBLE Host +Identity Reference :doc:`<../ble_hs/ble_hs_id/ble_hs_id>`. + +There are several methods for assigning an address to a NimBLE device. +The available options are documented below: + +Method 1: Configure nRF hardware with a public address +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When Mynewt is running on a Nordic nRF platform, the NimBLE controller +will attempt to read a public address out of the board's FICR or UICR +registers. The controller uses the following logic while trying to read +an address from hardware: + +1. If the *DEVICEADDRTYPE* FICR register is written, read the address + programmed in the *DEVICEADDR[0]* and *DEVICEADDR[1]* FICR registers. +2. Else if the upper 16 bits of the *CUSTOMER[1]* UICR register are 0, + read the address programmed in the *CUSTOMER[0]* and *CUSTOMER[1]* + UCI registers. +3. Else, no address available. + +Method 2: Hardcode a public address in the Mynewt target +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The NimBLE controller package exports a +:doc:`syscfg <../../../os/modules/sysinitconfig/sysinitconfig>` setting +called ``BLE_PUBLIC_DEV_ADDR``. This setting can be overridden at the +application or target level to configure a public Bluetooth address. For +example, a target can assign the public address *11:22:33:44:55:66* as +follows: + +:: + + syscfg.vals: + BLE_PUBLIC_DEV_ADDR: '(uint8_t[6]){0x66, 0x55, 0x44, 0x33, 0x22, 0x11}' + +This setting takes the form of a C expression. Specifically, the value +is a designated initializer expressing a six-byte array. Also note that +the bytes are reversed, as an array is inherently little-endian, while +addresses are generally expressed in big-endian. + +Note: this method takes precedence over method 1. Whatever is written to +the ``BLE_PUBLIC_DEV_ADDR`` setting is the address that gets used. + +Method 3: Configure a random address at runtime +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Random addresses get configured through the NimBLE host. The following +two functions are used in random address configuration: + +- :doc:`ble_hs_id_gen_rnd <../ble_hs/ble_hs_id/functions/ble_hs_id_gen_rnd>`: + Generates a new random address. +- :doc:`ble_hs_id_set_rnd <../ble_hs/ble_hs_id/functions/ble_hs_id_set_rnd>`: + Sets the device's random address. + +For an example of how this is done, see the :doc:`<../../../os/tutorials/ibeacon>`. + +*Note:* A NimBLE device can be configured with multiple addresses; at +most one of each address type. diff --git a/lib/bt/host/nimble/nimble/docs/ble_setup/ble_lp_clock.rst b/lib/bt/host/nimble/nimble/docs/ble_setup/ble_lp_clock.rst new file mode 100644 index 00000000..34a967fe --- /dev/null +++ b/lib/bt/host/nimble/nimble/docs/ble_setup/ble_lp_clock.rst @@ -0,0 +1,67 @@ +Configure clock for controller +------------------------------ + +The NimBLE stack uses OS cputime for scheduling various events inside +controller. Since the code of controller is optimized to work with 32768 +Hz clock, the OS cputime has to be configured accordingly. + +To make things easier, controller package (``net/nimble/controller``) +defines new system configuration setting ``BLE_LP_CLOCK`` as sets it to +``1`` so other packages can be configured if necessary. The next section +describes configuration required for controller to work properly. + +System configuration +~~~~~~~~~~~~~~~~~~~~ + +**Note:** All BSPs based on nRF5x have below settings automatically +applied when ``BLE_LP_CLOCK`` is set, there is no need to configure this +in application. + +The following things need to be configured for NimBLE controller to work +properly: + +- OS cputime frequency shall be set to ``32768`` +- OS cputime timer source shall be set to 32768 Hz clock source +- Default 1 MHz clock source can be disabled if not used by application +- 32768 Hz clock source shall be enabled +- Crystal settling time shall be set to non-zero value (see below) + +For example, on nRF52 platform timer 5 can be used as source for 32768 +Hz clock. Also, timer 0 can be disabled since this is the default source +for OS cputime clock and is no longer used. The configuration will look +as below: + +:: + + syscfg.vals: + OS_CPUTIME_FREQ: 32768 + OS_CPUTIME_TIMER_NUM: 5 + TIMER_0: 0 + TIMER_5: 1 + BLE_XTAL_SETTLE_TIME: 1500 + +On nRF51 platform the only difference is to use timer 3 instead of timer +5. + +On platforms without 32768 Hz crystal available it usually can be +synthesized by setting ``XTAL_32768_SYNTH`` to ``1`` - this is also +already configured in existing BSPs. + +Crystal settle time configuration +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The configuration variable ``BLE_XTAL_SETTLE_TIME`` is used by the +controller to turn on the necessary clock source(s) for the radio and +associated peripherals prior to Bluetooth events (advertising, scanning, +connections, etc). For the nRF5x platforms, the HFXO needs to be turned +on prior to using the radio and the ``BLE_XTAL_SETTLE_TIME`` must be set +to accommodate this time. The amount of time required is board +dependent, so users must characterize their hardware and set +``BLE_XTAL_SETTLE_TIME`` accordingly. The current value of 1500 +microseconds is a fairly long time and was intended to work for most, if +not all, platforms. + +Note that changing this time will impact battery life with the amount +depending on the application. The HFXO draws a fairly large amount of +current when running so keeping this time as small as possible will +reduce overall current drain. diff --git a/lib/bt/host/nimble/nimble/docs/ble_setup/ble_setup_intro.rst b/lib/bt/host/nimble/nimble/docs/ble_setup/ble_setup_intro.rst new file mode 100644 index 00000000..806817c6 --- /dev/null +++ b/lib/bt/host/nimble/nimble/docs/ble_setup/ble_setup_intro.rst @@ -0,0 +1,13 @@ +NimBLE Setup +------------ + +Most NimBLE initialization is done automatically by +:doc:`sysinit <../../../os/modules/sysinitconfig/sysinitconfig>`. This +section documents the few bits of initialization that an application +must perform manually. + +.. toctree:: + + ble_lp_clock + ble_addr + ble_sync_cb diff --git a/lib/bt/host/nimble/nimble/docs/ble_setup/ble_sync_cb.rst b/lib/bt/host/nimble/nimble/docs/ble_setup/ble_sync_cb.rst new file mode 100644 index 00000000..b14a3582 --- /dev/null +++ b/lib/bt/host/nimble/nimble/docs/ble_setup/ble_sync_cb.rst @@ -0,0 +1,80 @@ +Respond to *sync* and *reset* events +------------------------------------ + +sync +~~~~ + +The NimBLE stack is inoperable while the host and controller are out of +sync. In a combined host-controller app, the sync happens immediately at +startup. When the host and controller are separate, sync typically +occurs in under a second after the application starts. An application +learns when sync is achieved by configuring the host's *sync callback*: +``ble_hs_cfg.sync_cb``. The host calls the sync callback whenever sync +is acquired. The sync callback has the following form: + +.. code-block:: cpp + + typedef void ble_hs_sync_fn(void); + +Because the NimBLE stack begins in the unsynced state, the application +should delay all BLE operations until the sync callback has been called. + +reset +~~~~~ + +Another event indicated by the host is a *controller reset*. The NimBLE +stack resets itself when a catastrophic error occurs, such as loss of +communication between the host and controller. Upon resetting, the host +drops all BLE connections and loses sync with the controller. After a +reset, the application should refrain from using the host until sync is +again signaled via the sync callback. + +An application learns of a host reset by configuring the host's *reset +callback*: ``ble_hs_cfg.reset_cb``. This callback has the following +form: + +.. code-block:: cpp + + typedef void ble_hs_reset_fn(int reason); + +The ``reason`` parameter is a :doc:`NimBLE host return +code <../ble_hs/ble_hs_return_codes>`. + +Example +~~~~~~~ + +The following example demonstrates the configuration of the sync and +reset callbacks. + +.. code-block:: cpp + + #include "sysinit/sysinit.h" + #include "console/console.h" + #include "host/ble_hs.h" + + static void + on_sync(void) + { + /* Begin advertising, scanning for peripherals, etc. */ + } + + static void + on_reset(int reason) + { + console_printf("Resetting state; reason=%d\n", reason); + } + + int + main(void) + { + /* Initialize all packages. */ + sysinit(); + + ble_hs_cfg.sync_cb = on_sync; + ble_hs_cfg.reset_cb = on_reset; + + /* As the last thing, process events from default event queue. */ + while (1) { + os_eventq_run(os_eventq_dflt_get()); + } + } diff --git a/lib/bt/host/nimble/nimble/docs/btshell/btshell_GAP.rst b/lib/bt/host/nimble/nimble/docs/btshell/btshell_GAP.rst new file mode 100644 index 00000000..738d146c --- /dev/null +++ b/lib/bt/host/nimble/nimble/docs/btshell/btshell_GAP.rst @@ -0,0 +1,660 @@ +GAP API for btshell +=================== + +Generic Access Profile (GAP) defines the generic procedures related to discovery of Bluetooth devices (idle mode +procedures) and link management aspects of connecting to Bluetooth devices (connecting mode procedures). It also defines +procedures related to use of different security levels. + +Several different modes and procedures may be performed simultaneously over an LE physical transport. The following +modes and procedures are defined for use over an LE physical transport: + +1. **Broadcast mode and observation procedure** + + - These allow two devices to communicate in a unidirectional connectionless manner using the advertising events. + +2. **Discovery modes and procedures** + + - All devices shall be in either non-discoverable mode or one of the discoverable modes. + - A device in the discoverable mode shall be in either the general discoverable mode or the limited discoverable mode. + - A device in non-discoverable mode will not be discovered by any device that is performing either the general + discovery procedure or the limited discovery procedure. + +3. **Connection modes and procedures** + + - allow a device to establish a connection to another device. + - allow updating of parameters of the connection + - allow termination of the connection + +4. **Bonding modes and procedures** + + - Bonding allows two connected devices to exchange and store security and identity information to create a trusted + relationship. + - Bonding can occur only between two devices in bondable mode. + +Available commands +~~~~~~~~~~~~~~~~~~ + +Parameters default values are marked red. + +Configuration +------------- + ++---------------------+-----------------+----------------------------+---------------------------------------------------------------------------------------------------------+ +| **Command** | **Parmeters** | \*\* Possible values\*\* | **Description** | ++=====================+=================+============================+=========================================================================================================+ +| **set** | | | Set configuration options | ++---------------------+-----------------+----------------------------+---------------------------------------------------------------------------------------------------------+ +| | addr | XX:XX:XX:XX:XX:XX | Local device address | ++---------------------+-----------------+----------------------------+---------------------------------------------------------------------------------------------------------+ +| | addr\_type | ``public`` | Local device address type | ++---------------------+-----------------+----------------------------+---------------------------------------------------------------------------------------------------------+ +| | | random | Use random address for scan requests | ++---------------------+-----------------+----------------------------+---------------------------------------------------------------------------------------------------------+ +| | mtu | [23-UINT16\_MAX] | GATT Maximum Transmission Unit (MTU) | ++---------------------+-----------------+----------------------------+---------------------------------------------------------------------------------------------------------+ +| | irk | XX:XX:XX... | Local Identity Resolving Key (16 byte | ++---------------------+-----------------+----------------------------+---------------------------------------------------------------------------------------------------------+ +| **set-priv-mode** | | | Set privacy mode for device | ++---------------------+-----------------+----------------------------+---------------------------------------------------------------------------------------------------------+ +| | addr | XX:XX:XX:XX:XX:XX | Remote device address | ++---------------------+-----------------+----------------------------+---------------------------------------------------------------------------------------------------------+ +| | addr\_type | ``public`` | Remote device public address type | ++---------------------+-----------------+----------------------------+---------------------------------------------------------------------------------------------------------+ +| | | random | Remote device random address type | ++---------------------+-----------------+----------------------------+---------------------------------------------------------------------------------------------------------+ +| | mode | [``0``-1] | 0 - use network privacy, 1 - use device privacy | ++---------------------+-----------------+----------------------------+---------------------------------------------------------------------------------------------------------+ +| **white-list** | | | Add devices to white list (this command accepts multiple instances of addr and addr\_type parameters) | ++---------------------+-----------------+----------------------------+---------------------------------------------------------------------------------------------------------+ +| | addr | XX:XX:XX:XX:XX:XX | Remote device address | ++---------------------+-----------------+----------------------------+---------------------------------------------------------------------------------------------------------+ +| | addr\_type | ``public`` | Remote device public address type | ++---------------------+-----------------+----------------------------+---------------------------------------------------------------------------------------------------------+ +| | | random | Remote device random address type | ++---------------------+-----------------+----------------------------+---------------------------------------------------------------------------------------------------------+ + +Device discovery and connection +------------------------------- + ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| **Command** | **Parmeters** | \*\* Possible values\*\* | **Description** | ++==========================+================================+============================+============================================================================================================+ +| **scan** | | | Discover remote devices | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | cancel | | cancel ongoing scan procedure | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | extended | ``none`` | Start legacy scan | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | | 1M | Start extended scan on 1M PHY | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | | coded | Start extended scan on Coded PHY | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | | both | Start extended scan on both PHYs | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | duration | [1-``INT32_MAX``], | Duration of scan in milliseconds | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | limited | [``0``-1] | Use limited discovery procedure | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | passive | [``0``-1] | Use passive scan | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | interval | [``0``-UINT16\_MAX] | Scan interval, if 0 use stack's default | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | window | [``0``-UINT16\_MAX] | Scan window, if 0 use stack's default | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | filter | ``no_wl`` | Scan filter policy - Accept all advertising packets | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | | use\_wl | Accept only advertising packets from devices on White List | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | | no\_wl\_inita | Accept all advertising packets (including directed RPA) | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | | use\_wl\_inita | Accept only advertising packets from devices on White List (including directed RPA) | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | nodups | [``0``-1] | Disable duplicates filtering | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | own\_addr\_type | ``public`` | Use public address for scan requests | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | | random | Use random address for scan requests | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | | rpa\_pub | Use RPA address for scan requests (fallback to public if no IRK) | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | | rpa\_rnd | Use RPA address for scan requests (fallback to random if no IRK) | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | extended\_duration | [``0``-UINT16\_MAX] | Duration of extended scan in 10 milliseconds | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | extended\_period | [``0``-UINT16\_MAX] | Periodic scan interval in 1.28 seconds (0 disabled) | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | longrange\_interval | [``0``-UINT16\_MAX] | Scan interval for Coded Scan , if 0 use stack's default | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | longrange\_window | [``0``-UINT16\_MAX] | Scan window for Coded Scan , if 0 use stack's default | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | longrange\_passive | [``0``-1] | Use passive scan for Coded Scan | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| **connect** | | | Initiate connection to remote device | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | cancel | | Cancel ongoing connection procedure | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | extended | ``none`` | Use legacy connection procedure | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | | 1M | Extended connect using 1M PHY scan parameters | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | | coded | Extended connect using Coded PHY scan parameters | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | | both | Extended connect using 1M and Coded PHYs scan parameters | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | | all | Extended connect using 1M and Coded PHYs scan parameters (Provide also connection parameters for 2M PHY) | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | peer\_addr\_type | ``public`` | Remote device public address type | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | | random | Remote device random address type | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | | public\_id | Remote device public address type (Identity) | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | | random\_id | Remote device random address type (Identity) | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | peer\_addr | XX:XX:XX:XX:XX:XX | Remote device address | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | own\_addr\_type | ``public`` | Use public address for scan requests | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | | random | Use random address for scan requests | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | | rpa\_pub | Use RPA address for scan requests (fallback to public if no IRK) | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | | rpa\_rnd | Use RPA address for scan requests (fallback to random if no IRK) | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | duration | [``0``-INT32\_MAX] | Connection attempt duration, if 0 use stack's default | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | scan\_interval | [0-UINT16\_MAX] | Scan interval, default: 0x0010 | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | scan\_window | [0-UINT16\_MAX] | Scan window, default: 0x0010 | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | interval\_min | [0-UINT16\_MAX] | Minimum connection interval, default: 30 | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | interval\_max | [0-UINT16\_MAX] | Maximum connection interval, default: 50 | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | latency | [UINT16] | Connection latency, default: 0 | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | timeout | [UINT16] | Connection timeout, default: 0x0100 | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | min\_conn\_event\_len | [UINT16] | Minimum length of connection event, default: 0x0010 | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | max\_conn\_event\_len | [UINT16] | Maximum length of connection event, default: 0x0300 | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | coded\_scan\_interval | [0-UINT16\_MAX] | Coded PHY Scan interval, default: 0x0010 | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | coded\_scan\_window | [0-UINT16\_MAX] | Coded PHY Scan window, default: 0x0010 | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | coded\_interval\_min | [0-UINT16\_MAX] | Coded PHY Minimum connection interval, default: 30 | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | coded\_interval\_max | [0-UINT16\_MAX] | Coded PHY Maximum connection interval, default: 50 | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | coded\_latency | [UINT16] | Coded PHY Connection latency, default: 0 | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | coded\_timeout | [UINT16] | Coded PHY Connection timeout, default: 0x0100 | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | coded\_min\_conn\_event\_len | [UINT16] | Coded PHY Minimum length of connection event, default: 0x0010 | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | coded\_max\_conn\_event\_len | [UINT16] | Coded PHY Maximum length of connection event, default: 0x0300 | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | 2M\_scan\_interval | [0-UINT16\_MAX] | 2M PHY Scan interval, default: 0x0010 | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | 2M\_scan\_window | [0-UINT16\_MAX] | 2M PHY Scan window, default: 0x0010 | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | 2M\_interval\_min | [0-UINT16\_MAX] | 2M PHY Minimum connection interval, default: 30 | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | 2M\_interval\_max | [0-UINT16\_MAX] | 2M PHY Maximum connection interval, default: 50 | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | 2M\_latency | [UINT16] | 2M PHY Connection latency, default: 0 | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | 2M\_timeout | [UINT16] | 2M PHY Connection timeout, default: 0x0100 | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | 2M\_min\_conn\_event\_len | [UINT16] | 2M PHY Minimum length of connection event, default: 0x0010 | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | 2M\_max\_conn\_event\_len | [UINT16] | 2M PHY Maximum length of connection event, default: 0x0300 | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| **disconnect** | | | Disconnect exisiting connection | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | conn | [UINT16] | Connection handle | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | reason | [UINT8] | Disconnect reason | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| **show-addr** | | | Show local public and random identity addresses | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| **show-conn** | | | Show current connections | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| **conn-rssi** | | | Obtain RSSI of specified connection | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | conn | [UINT16] | Connection handle | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| **conn-update-params** | | | Update parameters of specified connection | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | conn | [UINT16] | Connection handle | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | interval\_min | [0-UINT16\_MAX] | Minimum connection interval, default: 30 | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | interval\_max | [0-UINT16\_MAX] | Maximum connection interval, default: 50 | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | latency | [UINT16] | Connection latency, default: 0 | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | timeout | [UINT16] | Connection timeout, default: 0x0100 | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | min\_conn\_event\_len | [UINT16] | Minimum length of connection event, default: 0x0010 | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | max\_conn\_event\_len | [UINT16] | Maximum length of connection event, default: 0x0300 | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| **conn-datalen** | | | Set DLE parmaeters for connection | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | conn | [UINT16] | Connection handle | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | octets | [UINT16] | Maximum transmission packet size | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | time | [UINT16] | Maximum transmission packet time | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| **phy-set** | | | Set prefered PHYs used for connection | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | conn | [UINT16] | Connection handle | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | tx\_phys\_mask | [UINT8] | Prefered PHYs on TX is mask of following bits0x00 - no preference0x01 - 1M, 0x02 - 2M, 0x04 - Coded | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | rx\_phys\_mask | [UINT8] | Prefered PHYs on RX is mask of following bits0x00 - no preference0x01 - 1M, 0x02 - 2M, 0x04 - Coded | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | phy\_opts | [UINT16] | Options for Coded PHY 0 - any coding, 1 - prefer S2, 2 - prefer S8 | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| **phy-set-default** | | | Set default prefered PHYs used for new connection | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | tx\_phys\_mask | [UINT8] | Prefered PHYs on TX is mask of following bits0x00 - no preference0x01 - 1M, 0x02 - 2M, 0x04 - Coded | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | rx\_phys\_mask | [UINT8] | Prefered PHYs on RX is mask of following bits0x00 - no preference0x01 - 1M, 0x02 - 2M, 0x04 - Coded | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| **phy-read** | | | Read connection current PHY | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | conn | [UINT16] | Connection handle | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| **l2cap-update** | | | Update connection parameters | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | interval\_min | [0-UINT16\_MAX] | Minimum connection interval, default: 30 | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | interval\_max | [0-UINT16\_MAX] | Maximum connection interval, default: 50 | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | latency | [UINT16] | Connection latency, default: 0 | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | timeout | [UINT16] | Connection timeout, default: 0x0100 | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ + +Security +-------- + ++---------------------------+--------------------+----------------------------+----------------------------------------------------------------------------------------------------------------------------+ +| **Command** | **Parmeters** | \*\* Possible values\*\* | **Description** | ++===========================+====================+============================+============================================================================================================================+ +| **security-set-data** | | | Set security configuration | ++---------------------------+--------------------+----------------------------+----------------------------------------------------------------------------------------------------------------------------+ +| | oob-flag | [``0``-1] | Set Out-Of-Band (OOB) flag in Security Manager | ++---------------------------+--------------------+----------------------------+----------------------------------------------------------------------------------------------------------------------------+ +| | mitm-flag | [``0``-1] | Set Man-In-The-Middle (MITM) flag in Security Manager | ++---------------------------+--------------------+----------------------------+----------------------------------------------------------------------------------------------------------------------------+ +| | io\_capabilities | 0 | Set Input-Output Capabilities to "DisplayOnly" | ++---------------------------+--------------------+----------------------------+----------------------------------------------------------------------------------------------------------------------------+ +| | | 1 | Set Input-Output Capabilities to "DisplayYesNo" | ++---------------------------+--------------------+----------------------------+----------------------------------------------------------------------------------------------------------------------------+ +| | | 2 | Set Input-Output Capabilities to "KeyboardOnly" | ++---------------------------+--------------------+----------------------------+----------------------------------------------------------------------------------------------------------------------------+ +| | | 3 | Set Input-Output Capabilities to "NoInputNoOutput" | ++---------------------------+--------------------+----------------------------+----------------------------------------------------------------------------------------------------------------------------+ +| | | 4 | Set Input-Output Capabilities to "KeyboardDisplay" | ++---------------------------+--------------------+----------------------------+----------------------------------------------------------------------------------------------------------------------------+ +| | our\_key\_dist | [UINT8] | Set Local Keys Distribution, this is a bit field of possible values: LTK (0x01), IRK (0x02), CSRK (0x04), LTK\_SC(0x08) | ++---------------------------+--------------------+----------------------------+----------------------------------------------------------------------------------------------------------------------------+ +| | their\_key\_dist | [UINT8] | Set Remote Keys Distribution, this is a bit field of possible values: LTK (0x01), IRK (0x02), CSRK (0x04), LTK\_SC(0x08) | ++---------------------------+--------------------+----------------------------+----------------------------------------------------------------------------------------------------------------------------+ +| | bonding-flag | [``0``-1] | Set Bonding flag in Security Manager | ++---------------------------+--------------------+----------------------------+----------------------------------------------------------------------------------------------------------------------------+ +| | sc-flag | [``0``-1] | Set Secure Connections flag in Security Manager | ++---------------------------+--------------------+----------------------------+----------------------------------------------------------------------------------------------------------------------------+ +| **security-pair** | | | Start pairing procedure | ++---------------------------+--------------------+----------------------------+----------------------------------------------------------------------------------------------------------------------------+ +| | conn | [UINT16] | Connection handle | ++---------------------------+--------------------+----------------------------+----------------------------------------------------------------------------------------------------------------------------+ +| **security-encryption** | | | Start encryption procedure | ++---------------------------+--------------------+----------------------------+----------------------------------------------------------------------------------------------------------------------------+ +| | conn | [UINT16] | Connection handle | ++---------------------------+--------------------+----------------------------+----------------------------------------------------------------------------------------------------------------------------+ +| | ediv | [UINT16] | EDIV for LTK to use (use storage if not provided) | ++---------------------------+--------------------+----------------------------+----------------------------------------------------------------------------------------------------------------------------+ +| | rand | [UINT64] | Rand for LTK | ++---------------------------+--------------------+----------------------------+----------------------------------------------------------------------------------------------------------------------------+ +| | ltk | XX:XX:XX... | LTK (16 bytes) | ++---------------------------+--------------------+----------------------------+----------------------------------------------------------------------------------------------------------------------------+ +| **security-start** | | | Start security procedure (This starts either pairing or encryption depending if keys are stored) | ++---------------------------+--------------------+----------------------------+----------------------------------------------------------------------------------------------------------------------------+ +| | conn | [UINT16] | Connection handle | ++---------------------------+--------------------+----------------------------+----------------------------------------------------------------------------------------------------------------------------+ +| **auth-passkey** | | | Reply to Passkey request | ++---------------------------+--------------------+----------------------------+----------------------------------------------------------------------------------------------------------------------------+ +| | conn | [UINT16] | Connection handle | ++---------------------------+--------------------+----------------------------+----------------------------------------------------------------------------------------------------------------------------+ +| | action | [UINT16] | Action to reply (as received in event) | ++---------------------------+--------------------+----------------------------+----------------------------------------------------------------------------------------------------------------------------+ +| | key | [0-999999] | Passkey to reply (Input or Display action) | ++---------------------------+--------------------+----------------------------+----------------------------------------------------------------------------------------------------------------------------+ +| | oob | XX:XX:XX:... | Out-Of-Band secret (16 bytes) (OOB action) | ++---------------------------+--------------------+----------------------------+----------------------------------------------------------------------------------------------------------------------------+ +| | yesno | Yy-Ny | Confirm passkey (for Passkey Confirm action) | ++---------------------------+--------------------+----------------------------+----------------------------------------------------------------------------------------------------------------------------+ + +Advertising with Extended Advertising enabled +--------------------------------------------- + ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| **Command** | **Parmeters** | \*\* Possible values\*\* | **Description** | ++==============================+==========================+============================+=====================================================================================+ +| **advertise-configure** | | | Configure new advertising instance | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | instance | [``0``-UINT8\_MAX] | Advertising instance | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | connectable | [``0``-1] | Use connectable advertising | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | scannable | [``0``-1] | Use scannable advertising | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | peer\_addr\_type | ``public`` | Remote device public address type | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | | random | Remote device random address type | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | | public\_id | Remote device public address type (Identity) | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | | random\_id | Remote device random address type (Identity) | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | peer\_addr | XX:XX:XX:XX:XX:XX | Remote device address - if provided perform directed advertising | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | own\_addr\_type | ``public`` | Use public address for scan requests | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | | random | Use random address for scan requests | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | | rpa\_pub | Use RPA address for scan requests (fallback to public if no IRK) | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | | rpa\_rnd | Use RPA address for scan requests (fallback to random if no IRK) | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | channel\_map | [``0``-UINT8\_MAX} | Primary advertising channels map. If 0 use all channels. | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | filter | ``none`` | Advertising filter policy - no filtering, no whitelist used | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | | scan | process all connection requests but only scans from white list | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | | conn | process all scan request but only connection requests from white list | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | | both | ignore all scan and connection requests unless in white list | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | interval\_min | [``0``-UINT32\_MAX] | Minimum advertising interval in 0.625 miliseconds If 0 use stack default. | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | interval\_max | [``0``-UINT32\_MAX] | Maximum advertising interval in 0.625 miliseconds If 0 use stack default. | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | rx\_power | [-127 - ``127``] | Advertising TX power in dBm | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | primary\_phy | ``1M`` | Use 1M PHY on primary advertising channels | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | | ``coded`` | Use Coded PHY on primary advertising channels | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | secondary\_phy | ``1M`` | Use 1M PHY on secondary advertising channels | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | | ``coded`` | Use coded PHY on primary advertising channels | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | | ``2M`` | Use 2M PHY on primary advertising channels | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | sid | [``0``-16] | Adsertising instance SID | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | high\_duty | [``0``-1] | Use high\_duty advertising | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | anonymous | [``0``-1] | Use anonymous advertising | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | legacy | [``0``-1] | Use legacy PDUs for advertising | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | include\_tx\_power | [``0``-1] | Include TX power information in advertising PDUs | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | scan\_req\_notif | [``0``-1] | Enable SCAN\_REQ notifications | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| **advertise-set-addr** | | | Configure *random* address for instance | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | instance | [``0``-UINT8\_MAX] | Advertising instance | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | addr | XX:XX:XX:XX:XX:XX | Random address | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| **advertise-set-adv-data** | | | Configure advertising instance ADV\_DATA. This allow to configure following TLVs: | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| **advertise-set-scan-rsp** | | | Configure advertising instance SCAN\_RSP. This allow to configure following TLVs: | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | instance | [``0``-UINT8\_MAX] | Advertising instance | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | flags | [``0``-UINT8\_MAX] | Flags value | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | uuid16 | [UINT16] | 16-bit UUID value (can be passed multiple times) | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | uuid16\_is\_complete | [``0``-1] | I 16-bit UUID list is complete | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | uuid32 | [UINT32] | 32-bit UUID value (can be passed multiple times) | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | uuid32\_is\_complete | [``0``-1] | I 32-bit UUID list is complete | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | uuid128 | XX:XX:XX:... | 128-bit UUID value (16 bytes) (can be passed multiple times) | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | uuid128\_is\_complete | [``0``-1] | I 128-bit UUID list is complete | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | tx\_power\_level | [-127 - 127] | TX Power level to include | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | appearance | [UINT16] | Appearance | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | name | string | Name | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | advertising\_interval | [UINT16] | Advertising interval | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | service\_data\_uuid32 | XX:XX:XX:... | 32-bit UUID service data | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | service\_data\_uuid128 | XX:XX:XX:... | 128-bit UUID service data | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | uri | XX:XX:XX:... | URI | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | msg\_data | XX:XX:XX:... | Manufacturer data | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | eddystone\_url | string | Eddystone with specified URL | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| **advertise-start** | | | Start advertising with configured instance | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | instance | [``0``-UINT8\_MAX] | Advertising instance | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | duration | [``0``-UINT16\_MAX] | Advertising duration in 10ms units. 0 - forver | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | max\_events | [``0``-UINT8\_MAX] | Maximum number of advertising events. 0 - no limit | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| **advertise-stop** | | | Stop advertising | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | instance | [``0``-UINT8\_MAX] | Advertising instance | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| **advertise-remove** | | | Remove configured advertising instance | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | instance | [``0``-UINT8\_MAX] | Advertising instance | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ + +Legacy Advertising with Extended Advertising disabled +----------------------------------------------------- + ++--------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| **Command** | **Parmeters** | \*\* Possible values\*\* | **Description** | ++====================+==========================+============================+=====================================================================================+ +| **advertise** | | | Enable advertising | ++--------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | stop | | Stop enabled advertising | ++--------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | conn | ``und`` | Connectable mode: undirected | ++--------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | | non | non-connectable | ++--------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | | dir | directed | ++--------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | discov | ``gen`` | Discoverable mode: general discoverable | ++--------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | | ltd | limited discoverable | ++--------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | | non | non-discoverable | ++--------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | scannable | [``0``-1] | Use scannable advertising | ++--------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | peer\_addr\_type | ``public`` | Remote device public address type | ++--------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | | random | Remote device random address type | ++--------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | | public\_id | Remote device public address type (Identity) | ++--------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | | random\_id | Remote device random address type (Identity) | ++--------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | peer\_addr | XX:XX:XX:XX:XX:XX | Remote device address - if provided perform directed advertising | ++--------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | own\_addr\_type | ``public`` | Use public address for scan requests | ++--------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | | random | Use random address for scan requests | ++--------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | | rpa\_pub | Use RPA address for scan requests (fallback to public if no IRK) | ++--------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | | rpa\_rnd | Use RPA address for scan requests (fallback to random if no IRK) | ++--------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | channel\_map | [``0``-UINT8\_MAX} | Primary advertising channels map. If 0 use all channels. | ++--------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | filter | ``none`` | Advertising filter policy - no filtering, no whitelist used | ++--------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | | scan | process all connection requests but only scans from white list | ++--------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | | conn | process all scan request but only connection requests from white list | ++--------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | | both | ignore all scan and connection requests unless in white list | ++--------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | interval\_min | [``0``-UINT32\_MAX] | Minimum advertising interval in 0.625 miliseconds If 0 use stack default. | ++--------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | interval\_max | [``0``-UINT32\_MAX] | Maximum advertising interval in 0.625 miliseconds If 0 use stack default. | ++--------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | high\_duty | [``0``-1] | Use high\_duty advertising | ++--------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | duration | [``1``-INT32\_MAX] | Advertising duration in ms | ++--------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| **set-adv-data** | | | Configure advertising instance ADV\_DATA. This allow to configure following TLVs: | ++--------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| **set-scan-rsp** | | | Configure advertising instance SCAN\_RSP. This allow to configure following TLVs: | ++--------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | flags | [``0``-UINT8\_MAX] | Flags value | ++--------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | uuid16 | [UINT16] | 16-bit UUID value (can be passed multiple times) | ++--------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | uuid16\_is\_complete | [``0``-1] | I 16-bit UUID list is complete | ++--------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | uuid32 | [UINT32] | 32-bit UUID value (can be passed multiple times) | ++--------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | uuid32\_is\_complete | [``0``-1] | I 32-bit UUID list is complete | ++--------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | uuid128 | XX:XX:XX:... | 128-bit UUID value (16 bytes) (can be passed multiple times) | ++--------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | uuid128\_is\_complete | [``0``-1] | I 128-bit UUID list is complete | ++--------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | tx\_power\_level | [-127 - 127] | TX Power level to include | ++--------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | appearance | [UINT16] | Appearance | ++--------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | name | string | Name | ++--------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | advertising\_interval | [UINT16] | Advertising interval | ++--------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | service\_data\_uuid32 | XX:XX:XX:... | 32-bit UUID service data | ++--------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | service\_data\_uuid128 | XX:XX:XX:... | 128-bit UUID service data | ++--------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | uri | XX:XX:XX:... | URI | ++--------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | msg\_data | XX:XX:XX:... | Manufacturer data | ++--------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | eddystone\_url | string | Eddystone with specified URL | ++--------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ + +L2CAP Connection Oriented Channels +---------------------------------- + ++---------------------------+-----------------+----------------------------+----------------------------------------------------+ +| **Command** | **Parmeters** | \*\* Possible values\*\* | **Description** | ++===========================+=================+============================+====================================================+ +| **l2cap-create-server** | | | Create L2CAP server | ++---------------------------+-----------------+----------------------------+----------------------------------------------------+ +| | psm | [UINT16] | PSM | ++---------------------------+-----------------+----------------------------+----------------------------------------------------+ +| **l2cap-connect** | | | Connect to remote L2CAP server | ++---------------------------+-----------------+----------------------------+----------------------------------------------------+ +| | conn | [UINT16] | Connection handle | ++---------------------------+-----------------+----------------------------+----------------------------------------------------+ +| | psm | [UINT16] | PSM | ++---------------------------+-----------------+----------------------------+----------------------------------------------------+ +| **l2cap-disconnect** | | | Disconnec from L2CAP server | ++---------------------------+-----------------+----------------------------+----------------------------------------------------+ +| | conn | [UINT16] | Connection handle | ++---------------------------+-----------------+----------------------------+----------------------------------------------------+ +| | idx | [UINT16] | L2CAP connection oriented channel identifier | ++---------------------------+-----------------+----------------------------+----------------------------------------------------+ +| **l2cap-send** | | | Send data over connected L2CAP channel | ++---------------------------+-----------------+----------------------------+----------------------------------------------------+ +| | conn | [UINT16] | Connection handle | ++---------------------------+-----------------+----------------------------+----------------------------------------------------+ +| | idx | [UINT16] | L2CAP connection oriented channel identifier | ++---------------------------+-----------------+----------------------------+----------------------------------------------------+ +| | bytes | [UINT16] | Number of bytes to send (hardcoded data pattern) | ++---------------------------+-----------------+----------------------------+----------------------------------------------------+ +| **l2cap-show-coc** | | | Show connected L2CAP channels | ++---------------------------+-----------------+----------------------------+----------------------------------------------------+ + +Keys storage +------------ + ++---------------------+-----------------+----------------------------+----------------------------------------------------+ +| **Command** | **Parmeters** | \*\* Possible values\*\* | **Description** | ++=====================+=================+============================+====================================================+ +| **keystore-add** | | | Add keys to storage | ++---------------------+-----------------+----------------------------+----------------------------------------------------+ +| | type | msec | Master Key | ++---------------------+-----------------+----------------------------+----------------------------------------------------+ +| | | ssec | Slave Key | ++---------------------+-----------------+----------------------------+----------------------------------------------------+ +| | | cccd | Client Characteristic Configuration Descriptor | ++---------------------+-----------------+----------------------------+----------------------------------------------------+ +| | addr | XX:XX:XX:XX:XX:XX | Device address | ++---------------------+-----------------+----------------------------+----------------------------------------------------+ +| | addr\_type | ``public`` | Device address type | ++---------------------+-----------------+----------------------------+----------------------------------------------------+ +| | | random | Use random address for scan requests | ++---------------------+-----------------+----------------------------+----------------------------------------------------+ +| | ediv | [UINT16] | EDIV for LTK to add | ++---------------------+-----------------+----------------------------+----------------------------------------------------+ +| | rand | [UINT64] | Rand for LTK | ++---------------------+-----------------+----------------------------+----------------------------------------------------+ +| | ltk | XX:XX:XX... | LTK (16 bytes) | ++---------------------+-----------------+----------------------------+----------------------------------------------------+ +| | irk | XX:XX:XX... | Identity Resolving Key (16 bytes) | ++---------------------+-----------------+----------------------------+----------------------------------------------------+ +| | csrk | XX:XX:XX... | Connection Signature Resolving Key (16 bytes) | ++---------------------+-----------------+----------------------------+----------------------------------------------------+ +| **keystore-del** | | | Delete keys from storage | ++---------------------+-----------------+----------------------------+----------------------------------------------------+ +| | type | msec | Master Key | ++---------------------+-----------------+----------------------------+----------------------------------------------------+ +| | | ssec | Slave Key | ++---------------------+-----------------+----------------------------+----------------------------------------------------+ +| | | cccd | Client Characteristic Configuration Descriptor | ++---------------------+-----------------+----------------------------+----------------------------------------------------+ +| | addr | XX:XX:XX:XX:XX:XX | Device address | ++---------------------+-----------------+----------------------------+----------------------------------------------------+ +| | addr\_type | ``public`` | Device address type | ++---------------------+-----------------+----------------------------+----------------------------------------------------+ +| | | random | Use random address for scan requests | ++---------------------+-----------------+----------------------------+----------------------------------------------------+ +| | ediv | [UINT16] | EDIV for LTK to remove | ++---------------------+-----------------+----------------------------+----------------------------------------------------+ +| | rand | [UINT64] | Rand for LTK | ++---------------------+-----------------+----------------------------+----------------------------------------------------+ +| **keystore-show** | | | Show stored keys | ++---------------------+-----------------+----------------------------+----------------------------------------------------+ +| | type | msec | Master Keys | ++---------------------+-----------------+----------------------------+----------------------------------------------------+ +| | | ssec | Slave Keys | ++---------------------+-----------------+----------------------------+----------------------------------------------------+ +| | | cccd | Client Characteristic Configuration Descriptor s | ++---------------------+-----------------+----------------------------+----------------------------------------------------+ diff --git a/lib/bt/host/nimble/nimble/docs/btshell/btshell_GATT.rst b/lib/bt/host/nimble/nimble/docs/btshell/btshell_GATT.rst new file mode 100644 index 00000000..0fe465fe --- /dev/null +++ b/lib/bt/host/nimble/nimble/docs/btshell/btshell_GATT.rst @@ -0,0 +1,108 @@ +GATT feature API for btshell +============================ + +GATT(GENERIC ATTRIBUTE PROFILE) describes a service framework using the Attribute Protocol for discovering services, +and for reading and writing characteristic values on a peer device. There are 11 features defined in the GATT Profile, +and each of the features is mapped to procedures and sub-procedures: + +Available commands +~~~~~~~~~~~~~~~~~~ + +Parameters default values (if applicable) are marked red. + +Configuration +------------- + ++------------------------------------+-----------------+----------------------------+-----------------------------------------------------------+ +| **Command** | **Parmeters** | \*\* Possible values\*\* | **Description** | ++====================================+=================+============================+===========================================================+ +| **gatt-discover-characteristic** | | | Discover GATT characteristics | ++------------------------------------+-----------------+----------------------------+-----------------------------------------------------------+ +| | conn | [UINT16] | Connection handle | ++------------------------------------+-----------------+----------------------------+-----------------------------------------------------------+ +| | uuid | [UINT16] | Characteristic UUID | ++------------------------------------+-----------------+----------------------------+-----------------------------------------------------------+ +| | start | [UINT16] | Discovery start handle | ++------------------------------------+-----------------+----------------------------+-----------------------------------------------------------+ +| | end | [UINT16] | Discovery end handle | ++------------------------------------+-----------------+----------------------------+-----------------------------------------------------------+ +| **gatt-discover-descriptor** | | | Discover GATT descriptors | ++------------------------------------+-----------------+----------------------------+-----------------------------------------------------------+ +| | conn | [UINT16] | Connection handle | ++------------------------------------+-----------------+----------------------------+-----------------------------------------------------------+ +| | start | [UINT16] | Discovery start handle | ++------------------------------------+-----------------+----------------------------+-----------------------------------------------------------+ +| | end | [UINT16] | Discovery end handle | ++------------------------------------+-----------------+----------------------------+-----------------------------------------------------------+ +| **gatt-discover-service** | | | Discover services | ++------------------------------------+-----------------+----------------------------+-----------------------------------------------------------+ +| | conn | [UINT16] | Connection handle | ++------------------------------------+-----------------+----------------------------+-----------------------------------------------------------+ +| | uuid16 | [UINT16] | Service UUID | ++------------------------------------+-----------------+----------------------------+-----------------------------------------------------------+ +| **gatt-discover-full** | | | Discover services, characteristic and descriptors | ++------------------------------------+-----------------+----------------------------+-----------------------------------------------------------+ +| | conn | [UINT16] | Connection handle | ++------------------------------------+-----------------+----------------------------+-----------------------------------------------------------+ +| **gatt-find-included-services** | | | Find included services | ++------------------------------------+-----------------+----------------------------+-----------------------------------------------------------+ +| | conn | [UINT16] | Connection handle | ++------------------------------------+-----------------+----------------------------+-----------------------------------------------------------+ +| | start | [UINT16] | Discovery start handle | ++------------------------------------+-----------------+----------------------------+-----------------------------------------------------------+ +| | end | [UINT16] | Discovery end handle | ++------------------------------------+-----------------+----------------------------+-----------------------------------------------------------+ +| **gatt-exchange-mtu** | | | Initiate ATT MTU exchange procedure | ++------------------------------------+-----------------+----------------------------+-----------------------------------------------------------+ +| | conn | [UINT16] | Connection handle | ++------------------------------------+-----------------+----------------------------+-----------------------------------------------------------+ +| **gatt-read** | | | Read attribute | ++------------------------------------+-----------------+----------------------------+-----------------------------------------------------------+ +| | conn | [UINT16] | Connection handle | ++------------------------------------+-----------------+----------------------------+-----------------------------------------------------------+ +| | long | [``0``-1] | Long read | ++------------------------------------+-----------------+----------------------------+-----------------------------------------------------------+ +| | attr | [UINT16] | Attribute handle | ++------------------------------------+-----------------+----------------------------+-----------------------------------------------------------+ +| | offset | [UINT16] | Long read offset value | ++------------------------------------+-----------------+----------------------------+-----------------------------------------------------------+ +| | uuid | [UINT16] | Characteristic UUID | ++------------------------------------+-----------------+----------------------------+-----------------------------------------------------------+ +| | start | [UINT16] | Discovery start handle | ++------------------------------------+-----------------+----------------------------+-----------------------------------------------------------+ +| | end | [UINT16] | Discovery end handle | ++------------------------------------+-----------------+----------------------------+-----------------------------------------------------------+ +| **gatt-notify** | | | Send notification or indication to all subscribed peers | ++------------------------------------+-----------------+----------------------------+-----------------------------------------------------------+ +| | attr | [UINT16] | Attribute handle | ++------------------------------------+-----------------+----------------------------+-----------------------------------------------------------+ +| **gatt-service-changed** | | | Send Services Changed notification | ++------------------------------------+-----------------+----------------------------+-----------------------------------------------------------+ +| | start | [UINT16] | Start handle | ++------------------------------------+-----------------+----------------------------+-----------------------------------------------------------+ +| | end | [UINT16] | End handle | ++------------------------------------+-----------------+----------------------------+-----------------------------------------------------------+ +| **gatt-service-visibility** | | | Set service visibility | ++------------------------------------+-----------------+----------------------------+-----------------------------------------------------------+ +| | handle | [UINT16] | Service handle | ++------------------------------------+-----------------+----------------------------+-----------------------------------------------------------+ +| | visibility | [``0``-1] | Service visibility | ++------------------------------------+-----------------+----------------------------+-----------------------------------------------------------+ +| **gatt-show** | | | Show remote devices discovered databases structure | ++------------------------------------+-----------------+----------------------------+-----------------------------------------------------------+ +| **gatt-show-local** | | | Show local database structure | ++------------------------------------+-----------------+----------------------------+-----------------------------------------------------------+ +| **gatt-write** | | | Write attribute | ++------------------------------------+-----------------+----------------------------+-----------------------------------------------------------+ +| | conn | [UINT16] | Connection handle | ++------------------------------------+-----------------+----------------------------+-----------------------------------------------------------+ +| | no\_rsp | [``0``-1] | Use Write Without Response | ++------------------------------------+-----------------+----------------------------+-----------------------------------------------------------+ +| | long | [``0``-1] | Use Long Write procedure | ++------------------------------------+-----------------+----------------------------+-----------------------------------------------------------+ +| | attr | [UINT16] | Attribute handle | ++------------------------------------+-----------------+----------------------------+-----------------------------------------------------------+ +| | offset | [UINT16] | Long write offset value | ++------------------------------------+-----------------+----------------------------+-----------------------------------------------------------+ +| | value | XX:XX:XX... | Data to write | ++------------------------------------+-----------------+----------------------------+-----------------------------------------------------------+ diff --git a/lib/bt/host/nimble/nimble/docs/btshell/btshell_advdata.rst b/lib/bt/host/nimble/nimble/docs/btshell/btshell_advdata.rst new file mode 100644 index 00000000..eabfcb3b --- /dev/null +++ b/lib/bt/host/nimble/nimble/docs/btshell/btshell_advdata.rst @@ -0,0 +1,47 @@ +Advertisement Data Fields +------------------------- + +This part defines the advertisement data fields used in the ``btshell`` app. For a complete list of all data types and +formats used for Extended Inquiry Response (EIR), Advertising Data (AD), and OOB data blocks, refer to the Supplement +to the Bluetooth Core Specification, CSSv6, available for download +`here `__. + ++---------------------------+-----------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+----------------------------------------------+ +| **Name** | **Definition** | **Details** | **btshell Notes** | ++===========================+=====================================================+===========================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================+==============================================+ +| flags | Indicates basic information about the advertiser. | Flags used over the LE physical channel are: \* Limited Discoverable Mode \* General Discoverable Mode \* BR/EDR Not Supported \* Simultaneous LE and BR/EDR to Same Device Capable (Controller) \* Simultaneous LE and BR/EDR to Same Device Capable (Host) | NimBLE will auto-calculate if set to 0. | ++---------------------------+-----------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+----------------------------------------------+ +| uuid16 | 16-bit Bluetooth Service UUIDs | Indicates the Service UUID list is incomplete i.e. more 16-bit Service UUIDs available. 16 bit UUIDs shall only be used if they are assigned by the Bluetooth SIG. | Set repeatedly for multiple service UUIDs. | ++---------------------------+-----------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+----------------------------------------------+ +| uuid16\_is\_complete | 16-bit Bluetooth Service UUIDs | Indicates the Service UUID list is complete. 16 bit UUIDs shall only be used if they are assigned by the Bluetooth SIG. | | ++---------------------------+-----------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+----------------------------------------------+ +| uuid32 | 32-bit Bluetooth Service UUIDs | Indicates the Service UUID list is incomplete i.e. more 32-bit Service UUIDs available. 32 bit UUIDs shall only be used if they are assigned by the Bluetooth SIG. | Set repeatedly for multiple service UUIDs. | ++---------------------------+-----------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+----------------------------------------------+ +| uuid32\_is\_complete | 32-bit Bluetooth Service UUIDs | Indicates the Service UUID list is complete. 32 bit UUIDs shall only be used if they are assigned by the Bluetooth SIG. | | ++---------------------------+-----------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+----------------------------------------------+ +| uuid128 | Global 128-bit Service UUIDs | More 128-bit Service UUIDs available. | Set repeatedly for multiple service UUIDs. | ++---------------------------+-----------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+----------------------------------------------+ +| uuid128\_is\_complete | Global 128-bit Service UUIDs | Complete list of 128-bit Service UUIDs | | ++---------------------------+-----------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+----------------------------------------------+ +| tx\_power\_level | TX Power Level | Indicates the transmitted power level of the packet containing the data type. The TX Power Level data type may be used to calculate path loss on a received packet using the following equation: pathloss = Tx Power Level – RSSI where “RSSI” is the received signal strength, in dBm, of the packet received. | NimBLE will auto-calculate if set to -128. | ++---------------------------+-----------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+----------------------------------------------+ +| slave\_interval\_range | Slave Connection Interval Range | Contains the Peripheral’s preferred connection interval range, for all logical connections. Size: 4 Octets . The first 2 octets defines the minimum value for the connection interval in the following manner: connIntervalmin = Conn\_Interval\_Min \* 1.25 ms Conn\_Interval\_Min range: 0x0006 to 0x0C80 Value of 0xFFFF indicates no specific minimum. The other 2 octets defines the maximum value for the connection interval in the following manner: connIntervalmax = Conn\_Interval\_Max \* 1.25 ms Conn\_Interval\_Max range: 0x0006 to 0x0C80 Conn\_Interval\_Max shall be equal to or greater than the Conn\_Interval\_Min. Value of 0xFFFF indicates no specific maximum. | | ++---------------------------+-----------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+----------------------------------------------+ +| service\_data\_uuid16 | Service Data - 16 bit UUID | Size: 2 or more octets The first 2 octets contain the 16 bit Service UUID followed by additional service data | | ++---------------------------+-----------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+----------------------------------------------+ +| public\_target\_address | Public Target Address | Defines the address of one or more intended recipients of an advertisement when one or more devices were bonded using a public address. This data type shall exist only once. It may be sent in either the Advertising or Scan Response data, but not both. | | ++---------------------------+-----------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+----------------------------------------------+ +| appearance | Appearance | Defines the external appearance of the device. The Appearance data type shall exist only once. It may be sent in either the Advertising or Scan Response data, but not both. | | ++---------------------------+-----------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+----------------------------------------------+ +| advertising\_interval | Advertising Interval | Contains the advInterval value as defined in the Core specification, Volume 6, Part B, Section 4.4.2.2. | | ++---------------------------+-----------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+----------------------------------------------+ +| service\_data\_uuid32 | Service Data - 32 bit UUID | Size: 4 or more octets The first 4 octets contain the 32 bit Service UUID followed by additional service data | | ++---------------------------+-----------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+----------------------------------------------+ +| service\_data\_uuid128 | Service Data - 128 bit UUID | Size: 16 or more octets The first 16 octets contain the 128 bit Service UUID followed by additional service data | | ++---------------------------+-----------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+----------------------------------------------+ +| uri | Uniform Resource Identifier (URI) | Scheme name string and URI as a UTF-8 string | | ++---------------------------+-----------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+----------------------------------------------+ +| mfg\_data | Manufacturer Specific data | Size: 2 or more octets The first 2 octets contain the Company Identifier Code followed by additional manufacturer specific data | | ++---------------------------+-----------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+----------------------------------------------+ +| eddystone\_url | | | | ++---------------------------+-----------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+----------------------------------------------+ diff --git a/lib/bt/host/nimble/nimble/docs/btshell/btshell_api.rst b/lib/bt/host/nimble/nimble/docs/btshell/btshell_api.rst new file mode 100644 index 00000000..28a1021c --- /dev/null +++ b/lib/bt/host/nimble/nimble/docs/btshell/btshell_api.rst @@ -0,0 +1,153 @@ +API for btshell app +------------------- + +"btshell" is one of the sample applications that come with Mynewt. It is a shell application which provides a basic +interface to the host-side of the BLE stack. "btshell" includes all the possible roles (Central/Peripheral) and they may +be run simultaneously. You can run btshell on a board and issue commands that make it behave as a central or a peripheral +with different peers. + +**btshell** is a new application that uses shell subsystem introduced in Mynewt 1.1 and has updated commands and +parameters names. Thanks to support for tab completion commands names are more descriptive and self-explanatory +without requiring extensive typing. + +Highlighted below are some of the ways you can use the API to establish connections and discover services and +characteristics from peer devices. For descriptions of the full API, go to the next sections on +:doc:`btshell_GAP` and :doc:`btshell_GATT`. + +.. contents:: + :local: + :depth: 2 + +.. toctree:: + :hidden: + :titlesonly: + + GAP + GATT + btshell_advdata + +Set device address. +~~~~~~~~~~~~~~~~~~~ + +On startup, btshell has the following identity address configuration: + +- Public address: None +- Random address: None + +The below ``set`` commands can be used to change the address configuration: + +:: + + set addr_type=public addr= + set addr_type=random addr= + +For example: + +:: + + set addr_type=public addr=01:02:03:04:05:06 + set addr_type=random addr=c1:aa:bb:cc:dd:ee + +The address configuration can be viewed with the ``show-addr`` command, as follows: + +:: + + show-addr + public_id_addr=01:02:03:04:05:06 random_id_addr=c1:aa:bb:cc:dd:ee + +Initiate a direct connection to a device +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In this case, your board is acting as a central and initiating a connection with another BLE device. The example +assumes you know the address of the peer, either by scanning for available peers or because you have set up the peer +yourself. + +.. code-block:: none + :emphasize-lines: 1 + + connect peer_addr=d4:f5:13:53:d2:43 + connection established; handle=1 our_ota_addr_type=0 our_ota_addr=0a:0b:0c:0d:0e:0f out_id_addr_type=0 our_id_addr=0a:0b:0c:0d:0e:0f peer_addr_type=0 peer_addr=43:d2:53:13:f5:d4 conn_itvl=40 conn_latency=0 supervision_timeout=256 encrypted=0 authenticated=0 bonded=0 + +The ``handle=1`` in the output indicates that it is connection-1. + +Configure advertisements to include device name +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In this case, your board is acting as a peripheral. + +With Extended Advertising enabled (should be executed after advertise-configure): + +:: + + advertise-set-adv-data name= + +With Extended Advertising disabled: + +:: + + set-adv-data name= + +Begin sending undirected general advertisements +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In this case, your board is acting as a peripheral. + +With Extended Advertising enabled: + +:: + + advertise-configure connectable=1 legacy=1 scannable=1 + advertise-start + +With Extended Advertising disabled: + +:: + + advertise conn=und discov=gen + +Show established connections. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +:: + + gatt-show-conn + +Discover and display peer's services, characteristics, and descriptors. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This is how you discover and then display the services of the peer you established earlier across connection-1. + +.. code-block:: none + :emphasize-lines: 1,2 + + gatt-discover-full conn=1 + gatt-show + [ts=132425ssb, mod=64 level=2] CONNECTION: handle=1 addr=d4:f5:13:53:d2:43 + [ts=132428ssb, mod=64 level=2] start=1 end=5 uuid=0x1800 + [ts=132433ssb, mod=64 level=2] start=6 end=16 uuid=0x1808 + [ts=132437ssb, mod=64 level=2] start=17 end=31 uuid=0x180a + [ts=132441ssb, mod=64 level=2] start=32 end=65535 uuid=00000000-0000-1000-1000000000000000 + + +Read an attribute belonging to the peer +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +:: + + gatt-read conn=1 attr=21 + +Write to an attribute belonging to the peer +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +:: + + gatt-write conn=1 attr=3 value=0x01:0x02:0x03 + +Perform a passive scan +~~~~~~~~~~~~~~~~~~~~~~ + +This is how you tell your board to listen to all advertisements around it. The duration is specified in ms. + +:: + + scan duration=1000 passive=1 filter=no_wl diff --git a/lib/bt/host/nimble/nimble/docs/conf.py b/lib/bt/host/nimble/nimble/docs/conf.py new file mode 100644 index 00000000..0aaf5c83 --- /dev/null +++ b/lib/bt/host/nimble/nimble/docs/conf.py @@ -0,0 +1,177 @@ +# -*- coding: utf-8 -*- +# +# Mynewt documentation build configuration file, created by +# sphinx-quickstart on Tue Jan 10 11:33:44 2017. +# +# This file is execfile()d with the current directory set to its +# containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +# +import os +import sys +sys.path.insert(0, os.path.abspath('_ext')) + + +# -- General configuration ------------------------------------------------ + +# If your documentation needs a minimal Sphinx version, state it here. +# +# needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [ + 'sphinx.ext.autodoc', 'breathe', 'sphinx.ext.todo', + 'sphinx.ext.extlinks' +] + +# Add any paths that contain templates here, relative to this directory. +templates_path = [] + +# The suffix(es) of source filenames. +# You can specify multiple suffix as a list of string: +# +source_suffix = '.rst' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = u'NimBLE Bluetooth Stack' +copyright = u'Copyright © 2018 The Apache Software Foundation, Licensed under the Apache License, Version 2.0 Apache and the Apache feather logo are trademarks of The Apache Software Foundation.' +author = u'The Apache Software Foundation' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = u'1.0' +# The full version, including alpha/beta/rc tags. +release = u'1.0.0-b1' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +# +# This is also used if you do content translation via gettext catalogs. +# Usually you set "language" from the command line for these cases. +language = None + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This patterns also effect to html_static_path and html_extra_path +exclude_patterns = ['_build', 'README.rst', 'Thumbs.db', '.DS_Store'] + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +highlight_language = 'none' + +# If true, `todo` and `todoList` produce output, else they produce nothing. +todo_include_todos = False + + +# -- Options for HTML output ---------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +# + +html_theme = 'alabaster' +html_theme_path = [] +html_sidebars = { + '**': [ + 'about.html', + 'navigation.html', + 'relations.html', + 'searchbox.html', + 'donate.html', + ] +} + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +# +html_theme_options = { +} + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = [] + + +# -- Options for HTMLHelp output ------------------------------------------ + +# Output file base name for HTML help builder. +htmlhelp_basename = 'Mynewtdoc' + + +# -- Options for LaTeX output --------------------------------------------- + +latex_elements = { + # The paper size ('letterpaper' or 'a4paper'). + # + # 'papersize': 'letterpaper', + + # The font size ('10pt', '11pt' or '12pt'). + # + # 'pointsize': '10pt', + + # Additional stuff for the LaTeX preamble. + # + # 'preamble': '', + + # Latex figure (float) alignment + # + # 'figure_align': 'htbp', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, +# author, documentclass [howto, manual, or own class]). +latex_documents = [ + (master_doc, 'Mynewt.tex', u'NimBLE Bluetooth Stack', + u'The Apache Software Foundation', 'manual'), +] + + +# -- Options for manual page output --------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + (master_doc, 'mynewt', u'Mynewt Documentation', + [author], 1) +] + + +# -- Options for Texinfo output ------------------------------------------- + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + (master_doc, 'Mynewt', u'NimBLE Bluetooth Stack', + author, 'Mynewt', 'One line description of project.', + 'Miscellaneous'), +] + +breathe_projects = { + "mynewt": "_build/xml" +} +breathe_default_project = "mynewt" +breathe_domain_by_extension = { + "h" : "c", +} diff --git a/lib/bt/host/nimble/nimble/docs/doxygen.xml b/lib/bt/host/nimble/nimble/docs/doxygen.xml new file mode 100644 index 00000000..fb000de3 --- /dev/null +++ b/lib/bt/host/nimble/nimble/docs/doxygen.xml @@ -0,0 +1,2433 @@ +# Doxyfile 1.8.11 + +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project. +# +# All text after a double hash (##) is considered a comment and is placed in +# front of the TAG it is preceding. +# +# All text after a single hash (#) is considered a comment and will be ignored. +# The format is: +# TAG = value [value, ...] +# For lists, items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (\" \"). + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the config file +# that follow. The default is UTF-8 which is also the encoding used for all text +# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv +# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv +# for the list of possible encodings. +# The default value is: UTF-8. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by +# double-quotes, unless you are using Doxywizard) that should identify the +# project for which the documentation is generated. This name is used in the +# title of most generated pages and in a few other places. +# The default value is: My Project. + +PROJECT_NAME = "Apache Mynewt" + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. This +# could be handy for archiving the generated documentation or if some version +# control system is used. + +PROJECT_NUMBER = + +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer a +# quick idea about the purpose of the project. Keep the description short. + +PROJECT_BRIEF = + +# With the PROJECT_LOGO tag one can specify a logo or an icon that is included +# in the documentation. The maximum height of the logo should not exceed 55 +# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy +# the logo to the output directory. + +PROJECT_LOGO = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path +# into which the generated documentation will be written. If a relative path is +# entered, it will be relative to the location where doxygen was started. If +# left blank the current directory will be used. + +OUTPUT_DIRECTORY = docs/ + +# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub- +# directories (in 2 levels) under the output directory of each output format and +# will distribute the generated files over these directories. Enabling this +# option can be useful when feeding doxygen a huge amount of source files, where +# putting all generated files in the same directory would otherwise causes +# performance problems for the file system. +# The default value is: NO. + +CREATE_SUBDIRS = NO + +# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII +# characters to appear in the names of generated files. If set to NO, non-ASCII +# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode +# U+3044. +# The default value is: NO. + +ALLOW_UNICODE_NAMES = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, +# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), +# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, +# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), +# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, +# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, +# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, +# Ukrainian and Vietnamese. +# The default value is: English. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member +# descriptions after the members that are listed in the file and class +# documentation (similar to Javadoc). Set to NO to disable this. +# The default value is: YES. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief +# description of a member or function before the detailed description +# +# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. +# The default value is: YES. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator that is +# used to form the text in various listings. Each string in this list, if found +# as the leading text of the brief description, will be stripped from the text +# and the result, after processing the whole list, is used as the annotated +# text. Otherwise, the brief description is used as-is. If left blank, the +# following values are used ($name is automatically replaced with the name of +# the entity):The $name class, The $name widget, The $name file, is, provides, +# specifies, contains, represents, a, an and the. + +ABBREVIATE_BRIEF = + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# doxygen will generate a detailed section even if there is only a brief +# description. +# The default value is: NO. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. +# The default value is: NO. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path +# before files name in the file list and in the header files. If set to NO the +# shortest path that makes the file name unique will be used +# The default value is: YES. + +FULL_PATH_NAMES = YES + +# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. +# Stripping is only done if one of the specified strings matches the left-hand +# part of the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the path to +# strip. +# +# Note that you can specify absolute paths here, but also relative paths, which +# will be relative from the directory where doxygen is started. +# This tag requires that the tag FULL_PATH_NAMES is set to YES. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the +# path mentioned in the documentation of a class, which tells the reader which +# header file to include in order to use a class. If left blank only the name of +# the header file containing the class definition is used. Otherwise one should +# specify the list of include paths that are normally passed to the compiler +# using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but +# less readable) file names. This can be useful is your file systems doesn't +# support long names like on DOS, Mac, or CD-ROM. +# The default value is: NO. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the +# first line (until the first dot) of a Javadoc-style comment as the brief +# description. If set to NO, the Javadoc-style will behave just like regular Qt- +# style comments (thus requiring an explicit @brief command for a brief +# description.) +# The default value is: NO. + +JAVADOC_AUTOBRIEF = YES + +# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first +# line (until the first dot) of a Qt-style comment as the brief description. If +# set to NO, the Qt-style will behave just like regular Qt-style comments (thus +# requiring an explicit \brief command for a brief description.) +# The default value is: NO. + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a +# multi-line C++ special comment block (i.e. a block of //! or /// comments) as +# a brief description. This used to be the default behavior. The new default is +# to treat a multi-line C++ comment block as a detailed description. Set this +# tag to YES if you prefer the old behavior instead. +# +# Note that setting this tag to YES also means that rational rose comments are +# not recognized any more. +# The default value is: NO. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the +# documentation from any documented member that it re-implements. +# The default value is: YES. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new +# page for each member. If set to NO, the documentation of a member will be part +# of the file/class/namespace that contains it. +# The default value is: NO. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen +# uses this value to replace tabs by spaces in code fragments. +# Minimum value: 1, maximum value: 16, default value: 4. + +TAB_SIZE = 4 + +# This tag can be used to specify a number of aliases that act as commands in +# the documentation. An alias has the form: +# name=value +# For example adding +# "sideeffect=@par Side Effects:\n" +# will allow you to put the command \sideeffect (or @sideeffect) in the +# documentation, which will result in a user-defined paragraph with heading +# "Side Effects:". You can put \n's in the value part of an alias to insert +# newlines. + +ALIASES = + +# This tag can be used to specify a number of word-keyword mappings (TCL only). +# A mapping has the form "name=value". For example adding "class=itcl::class" +# will allow you to use the command class in the itcl::class meaning. + +TCL_SUBST = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources +# only. Doxygen will then generate output that is more tailored for C. For +# instance, some of the names that are used will be different. The list of all +# members will be omitted, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_FOR_C = YES + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or +# Python sources only. Doxygen will then generate output that is more tailored +# for that language. For instance, namespaces will be presented as packages, +# qualified scopes will look different, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources. Doxygen will then generate output that is tailored for Fortran. +# The default value is: NO. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for VHDL. +# The default value is: NO. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given +# extension. Doxygen has a built-in mapping, but you can override or extend it +# using this tag. The format is ext=language, where ext is a file extension, and +# language is one of the parsers supported by doxygen: IDL, Java, Javascript, +# C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran: +# FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran: +# Fortran. In the later case the parser tries to guess whether the code is fixed +# or free formatted code, this is the default for Fortran type files), VHDL. For +# instance to make doxygen treat .inc files as Fortran files (default is PHP), +# and .f files as C (default is Fortran), use: inc=Fortran f=C. +# +# Note: For files without extension you can use no_extension as a placeholder. +# +# Note that for custom extensions you also need to set FILE_PATTERNS otherwise +# the files are not read by doxygen. + +EXTENSION_MAPPING = + +# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments +# according to the Markdown format, which allows for more readable +# documentation. See http://daringfireball.net/projects/markdown/ for details. +# The output of markdown processing is further processed by doxygen, so you can +# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in +# case of backward compatibilities issues. +# The default value is: YES. + +MARKDOWN_SUPPORT = YES + +# When enabled doxygen tries to link words that correspond to documented +# classes, or namespaces to their corresponding documentation. Such a link can +# be prevented in individual cases by putting a % sign in front of the word or +# globally by setting AUTOLINK_SUPPORT to NO. +# The default value is: YES. + +AUTOLINK_SUPPORT = YES + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should set this +# tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); +# versus func(std::string) {}). This also make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. +# The default value is: NO. + +BUILTIN_STL_SUPPORT = NO + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. +# The default value is: NO. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip (see: +# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen +# will parse them like normal C++ but will assume all classes use public instead +# of private inheritance when no explicit protection keyword is present. +# The default value is: NO. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate +# getter and setter methods for a property. Setting this option to YES will make +# doxygen to replace the get and set methods by a property in the documentation. +# This will only work if the methods are indeed getting or setting a simple +# type. If this is not the case, or you want to show the methods anyway, you +# should set this option to NO. +# The default value is: YES. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. +# The default value is: NO. + +DISTRIBUTE_GROUP_DOC = NO + +# If one adds a struct or class to a group and this option is enabled, then also +# any nested class or struct is added to the same group. By default this option +# is disabled and one has to add nested compounds explicitly via \ingroup. +# The default value is: NO. + +GROUP_NESTED_COMPOUNDS = NO + +# Set the SUBGROUPING tag to YES to allow class member groups of the same type +# (for instance a group of public functions) to be put as a subgroup of that +# type (e.g. under the Public Functions section). Set it to NO to prevent +# subgrouping. Alternatively, this can be done per class using the +# \nosubgrouping command. +# The default value is: YES. + +SUBGROUPING = YES + +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions +# are shown inside the group in which they are included (e.g. using \ingroup) +# instead of on a separate page (for HTML and Man pages) or section (for LaTeX +# and RTF). +# +# Note that this feature does not work in combination with +# SEPARATE_MEMBER_PAGES. +# The default value is: NO. + +INLINE_GROUPED_CLASSES = NO + +# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions +# with only public data fields or simple typedef fields will be shown inline in +# the documentation of the scope in which they are defined (i.e. file, +# namespace, or group documentation), provided this scope is documented. If set +# to NO, structs, classes, and unions are shown on a separate page (for HTML and +# Man pages) or section (for LaTeX and RTF). +# The default value is: NO. + +INLINE_SIMPLE_STRUCTS = YES + +# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or +# enum is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically be +# useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. +# The default value is: NO. + +TYPEDEF_HIDES_STRUCT = NO + +# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This +# cache is used to resolve symbols given their name and scope. Since this can be +# an expensive process and often the same symbol appears multiple times in the +# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small +# doxygen will become slower. If the cache is too large, memory is wasted. The +# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range +# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 +# symbols. At the end of a run doxygen will report the cache usage and suggest +# the optimal cache size from a speed point of view. +# Minimum value: 0, maximum value: 9, default value: 0. + +LOOKUP_CACHE_SIZE = 0 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in +# documentation are documented, even if no documentation was available. Private +# class members and static file members will be hidden unless the +# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. +# Note: This will also disable the warnings about undocumented members that are +# normally produced when WARNINGS is set to YES. +# The default value is: NO. + +EXTRACT_ALL = YES + +# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will +# be included in the documentation. +# The default value is: NO. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal +# scope will be included in the documentation. +# The default value is: NO. + +EXTRACT_PACKAGE = NO + +# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be +# included in the documentation. +# The default value is: NO. + +EXTRACT_STATIC = NO + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined +# locally in source files will be included in the documentation. If set to NO, +# only classes defined in header files are included. Does not have any effect +# for Java sources. +# The default value is: YES. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. If set to YES, local methods, +# which are defined in the implementation section but not in the interface are +# included in the documentation. If set to NO, only methods in the interface are +# included. +# The default value is: NO. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base name of +# the file that contains the anonymous namespace. By default anonymous namespace +# are hidden. +# The default value is: NO. + +EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all +# undocumented members inside documented classes or files. If set to NO these +# members will be included in the various overviews, but no documentation +# section is generated. This option has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. If set +# to NO, these classes will be included in the various overviews. This option +# has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend +# (class|struct|union) declarations. If set to NO, these declarations will be +# included in the documentation. +# The default value is: NO. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any +# documentation blocks found inside the body of a function. If set to NO, these +# blocks will be appended to the function's detailed documentation block. +# The default value is: NO. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation that is typed after a +# \internal command is included. If the tag is set to NO then the documentation +# will be excluded. Set it to YES to include the internal documentation. +# The default value is: NO. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file +# names in lower-case letters. If set to YES, upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. +# The default value is: system dependent. + +CASE_SENSE_NAMES = NO + +# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with +# their full class and namespace scopes in the documentation. If set to YES, the +# scope will be hidden. +# The default value is: NO. + +HIDE_SCOPE_NAMES = NO + +# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will +# append additional text to a page's title, such as Class Reference. If set to +# YES the compound reference will be hidden. +# The default value is: NO. + +HIDE_COMPOUND_REFERENCE= NO + +# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of +# the files that are included by a file in the documentation of that file. +# The default value is: YES. + +SHOW_INCLUDE_FILES = YES + +# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each +# grouped member an include statement to the documentation, telling the reader +# which file to include in order to use the member. +# The default value is: NO. + +SHOW_GROUPED_MEMB_INC = NO + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include +# files with double quotes in the documentation rather than with sharp brackets. +# The default value is: NO. + +FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the +# documentation for inline members. +# The default value is: YES. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the +# (detailed) documentation of file and class members alphabetically by member +# name. If set to NO, the members will appear in declaration order. +# The default value is: YES. + +SORT_MEMBER_DOCS = NO + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief +# descriptions of file, namespace and class members alphabetically by member +# name. If set to NO, the members will appear in declaration order. Note that +# this will also influence the order of the classes in the class list. +# The default value is: NO. + +SORT_BRIEF_DOCS = NO + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the +# (brief and detailed) documentation of class members so that constructors and +# destructors are listed first. If set to NO the constructors will appear in the +# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. +# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief +# member documentation. +# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting +# detailed member documentation. +# The default value is: NO. + +SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy +# of group names into alphabetical order. If set to NO the group names will +# appear in their defined order. +# The default value is: NO. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by +# fully-qualified names, including namespaces. If set to NO, the class list will +# be sorted only by class name, not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the alphabetical +# list. +# The default value is: NO. + +SORT_BY_SCOPE_NAME = NO + +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper +# type resolution of all parameters of a function it will reject a match between +# the prototype and the implementation of a member function even if there is +# only one candidate or it is obvious which candidate to choose by doing a +# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still +# accept a match between prototype and implementation in such cases. +# The default value is: NO. + +STRICT_PROTO_MATCHING = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo +# list. This list is created by putting \todo commands in the documentation. +# The default value is: YES. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test +# list. This list is created by putting \test commands in the documentation. +# The default value is: YES. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug +# list. This list is created by putting \bug commands in the documentation. +# The default value is: YES. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO) +# the deprecated list. This list is created by putting \deprecated commands in +# the documentation. +# The default value is: YES. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional documentation +# sections, marked by \if ... \endif and \cond +# ... \endcond blocks. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the +# initial value of a variable or macro / define can have for it to appear in the +# documentation. If the initializer consists of more lines than specified here +# it will be hidden. Use a value of 0 to hide initializers completely. The +# appearance of the value of individual variables and macros / defines can be +# controlled using \showinitializer or \hideinitializer command in the +# documentation regardless of this setting. +# Minimum value: 0, maximum value: 10000, default value: 30. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at +# the bottom of the documentation of classes and structs. If set to YES, the +# list will mention the files that were used to generate the documentation. +# The default value is: YES. + +SHOW_USED_FILES = YES + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This +# will remove the Files entry from the Quick Index and from the Folder Tree View +# (if specified). +# The default value is: YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces +# page. This will remove the Namespaces entry from the Quick Index and from the +# Folder Tree View (if specified). +# The default value is: YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command command input-file, where command is the value of the +# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided +# by doxygen. Whatever the program writes to standard output is used as the file +# version. For an example see the documentation. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. To create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. You can +# optionally specify a file name after the option, if omitted DoxygenLayout.xml +# will be used as the name of the layout file. +# +# Note that if you run doxygen from a directory containing a file called +# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE +# tag is left empty. + +LAYOUT_FILE = + +# The CITE_BIB_FILES tag can be used to specify one or more bib files containing +# the reference definitions. This must be a list of .bib files. The .bib +# extension is automatically appended if omitted. This requires the bibtex tool +# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info. +# For LaTeX the style of the bibliography can be controlled using +# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the +# search path. See also \cite for info how to create references. + +CITE_BIB_FILES = + +#--------------------------------------------------------------------------- +# Configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated to +# standard output by doxygen. If QUIET is set to YES this implies that the +# messages are off. +# The default value is: NO. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES +# this implies that the warnings are on. +# +# Tip: Turn warnings on while writing the documentation. +# The default value is: YES. + +WARNINGS = YES + +# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate +# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag +# will automatically be disabled. +# The default value is: YES. + +WARN_IF_UNDOCUMENTED = YES + +# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some parameters +# in a documented function, or documenting parameters that don't exist or using +# markup commands wrongly. +# The default value is: YES. + +WARN_IF_DOC_ERROR = YES + +# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that +# are documented, but have no documentation for their parameters or return +# value. If set to NO, doxygen will only warn about wrong or incomplete +# parameter documentation, but not about the absence of documentation. +# The default value is: NO. + +WARN_NO_PARAMDOC = NO + +# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when +# a warning is encountered. +# The default value is: NO. + +WARN_AS_ERROR = NO + +# The WARN_FORMAT tag determines the format of the warning messages that doxygen +# can produce. The string should contain the $file, $line, and $text tags, which +# will be replaced by the file and line number from which the warning originated +# and the warning text. Optionally the format may contain $version, which will +# be replaced by the version of the file (if it could be obtained via +# FILE_VERSION_FILTER) +# The default value is: $file:$line: $text. + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning and error +# messages should be written. If left blank the output is written to standard +# error (stderr). + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# Configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag is used to specify the files and/or directories that contain +# documented source files. You may enter file names like myfile.cpp or +# directories like /usr/src/myproject. Separate the files or directories with +# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING +# Note: If this tag is empty the current directory is searched. + +INPUT = nimble/host/include/host \ + nimble/host/mesh/include/mesh + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses +# libiconv (or the iconv built into libc) for the transcoding. See the libiconv +# documentation (see: http://www.gnu.org/software/libiconv) for the list of +# possible encodings. +# The default value is: UTF-8. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and +# *.h) to filter out the source-files in the directories. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# read by doxygen. +# +# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp, +# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, +# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, +# *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.pyw, *.f90, *.f, *.for, *.tcl, +# *.vhd, *.vhdl, *.ucf, *.qsf, *.as and *.js. + +FILE_PATTERNS = *.h + +# The RECURSIVE tag can be used to specify whether or not subdirectories should +# be searched for input files as well. +# The default value is: NO. + +RECURSIVE = YES + +# The EXCLUDE tag can be used to specify files and/or directories that should be +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. +# +# Note that relative paths are relative to the directory from which doxygen is +# run. + +EXCLUDE = docs + +# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded +# from the input. +# The default value is: NO. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories for example use the pattern */test/* + +EXCLUDE_PATTERNS = *bin/* \ + *src/ext* \ + *lwip_base* \ + *mbedtls* \ + *.md \ + *.yml + + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories use the pattern */test/* + +EXCLUDE_SYMBOLS = __* + +# The EXAMPLE_PATH tag can be used to specify one or more files or directories +# that contain example code fragments that are included (see the \include +# command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and +# *.h) to filter out the source-files in the directories. If left blank all +# files are included. + +EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude commands +# irrespective of the value of the RECURSIVE tag. +# The default value is: NO. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or directories +# that contain images that are to be included in the documentation (see the +# \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command: +# +# +# +# where is the value of the INPUT_FILTER tag, and is the +# name of an input file. Doxygen will then use the output that the filter +# program writes to standard output. If FILTER_PATTERNS is specified, this tag +# will be ignored. +# +# Note that the filter must not add or remove lines; it is applied before the +# code is scanned, but not when the output code is generated. If lines are added +# or removed, the anchors will not be placed correctly. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: pattern=filter +# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how +# filters are used. If the FILTER_PATTERNS tag is empty or if none of the +# patterns match the file name, INPUT_FILTER is applied. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will also be used to filter the input files that are used for +# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). +# The default value is: NO. + +FILTER_SOURCE_FILES = NO + +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and +# it is also possible to disable source filtering for a specific pattern using +# *.ext= (so without naming a filter). +# This tag requires that the tag FILTER_SOURCE_FILES is set to YES. + +FILTER_SOURCE_PATTERNS = + +# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that +# is part of the input, its contents will be placed on the main page +# (index.html). This can be useful if you have a project on for instance GitHub +# and want to reuse the introduction page also for the doxygen output. + +USE_MDFILE_AS_MAINPAGE = + +#--------------------------------------------------------------------------- +# Configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will be +# generated. Documented entities will be cross-referenced with these sources. +# +# Note: To get rid of all source code in the generated output, make sure that +# also VERBATIM_HEADERS is set to NO. +# The default value is: NO. + +SOURCE_BROWSER = YES + +# Setting the INLINE_SOURCES tag to YES will include the body of functions, +# classes and enums directly into the documentation. +# The default value is: NO. + +INLINE_SOURCES = YES + +# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any +# special comment blocks from generated source code fragments. Normal C, C++ and +# Fortran comments will always remain visible. +# The default value is: YES. + +STRIP_CODE_COMMENTS = NO + +# If the REFERENCED_BY_RELATION tag is set to YES then for each documented +# function all documented functions referencing it will be listed. +# The default value is: NO. + +REFERENCED_BY_RELATION = YES + +# If the REFERENCES_RELATION tag is set to YES then for each documented function +# all documented entities called/used by that function will be listed. +# The default value is: NO. + +REFERENCES_RELATION = NO + +# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set +# to YES then the hyperlinks from functions in REFERENCES_RELATION and +# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will +# link to the documentation. +# The default value is: YES. + +REFERENCES_LINK_SOURCE = YES + +# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the +# source code will show a tooltip with additional information such as prototype, +# brief description and links to the definition and documentation. Since this +# will make the HTML file larger and loading of large files a bit slower, you +# can opt to disable this feature. +# The default value is: YES. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +SOURCE_TOOLTIPS = YES + +# If the USE_HTAGS tag is set to YES then the references to source code will +# point to the HTML generated by the htags(1) tool instead of doxygen built-in +# source browser. The htags tool is part of GNU's global source tagging system +# (see http://www.gnu.org/software/global/global.html). You will need version +# 4.8.6 or higher. +# +# To use it do the following: +# - Install the latest version of global +# - Enable SOURCE_BROWSER and USE_HTAGS in the config file +# - Make sure the INPUT points to the root of the source tree +# - Run doxygen as normal +# +# Doxygen will invoke htags (and that will in turn invoke gtags), so these +# tools must be available from the command line (i.e. in the search path). +# +# The result: instead of the source browser generated by doxygen, the links to +# source code will now point to the output of htags. +# The default value is: NO. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a +# verbatim copy of the header file for each class for which an include is +# specified. Set to NO to disable this. +# See also: Section \class. +# The default value is: YES. + +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# Configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all +# compounds will be generated. Enable this if the project contains a lot of +# classes, structs, unions or interfaces. +# The default value is: YES. + +ALPHABETICAL_INDEX = YES + +# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in +# which the alphabetical index list will be split. +# Minimum value: 1, maximum value: 20, default value: 5. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all classes will +# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag +# can be used to specify a prefix (or a list of prefixes) that should be ignored +# while generating the index headers. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output +# The default value is: YES. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_OUTPUT = _build/html/api + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each +# generated HTML page (for example: .htm, .php, .asp). +# The default value is: .html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a user-defined HTML header file for +# each generated HTML page. If the tag is left blank doxygen will generate a +# standard header. +# +# To get valid HTML the header file that includes any scripts and style sheets +# that doxygen needs, which is dependent on the configuration options used (e.g. +# the setting GENERATE_TREEVIEW). It is highly recommended to start with a +# default header using +# doxygen -w html new_header.html new_footer.html new_stylesheet.css +# YourConfigFile +# and then modify the file new_header.html. See also section "Doxygen usage" +# for information on how to generate the default header that doxygen normally +# uses. +# Note: The header is subject to change so you typically have to regenerate the +# default header when upgrading to a newer version of doxygen. For a description +# of the possible markers and block names see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each +# generated HTML page. If the tag is left blank doxygen will generate a standard +# footer. See HTML_HEADER for more information on how to generate a default +# footer and what special commands can be used inside the footer. See also +# section "Doxygen usage" for information on how to generate the default footer +# that doxygen normally uses. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style +# sheet that is used by each HTML page. It can be used to fine-tune the look of +# the HTML output. If left blank doxygen will generate a default style sheet. +# See also section "Doxygen usage" for information on how to generate the style +# sheet that doxygen normally uses. +# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as +# it is more robust and this tag (HTML_STYLESHEET) will in the future become +# obsolete. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_STYLESHEET = + +# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined +# cascading style sheets that are included after the standard style sheets +# created by doxygen. Using this option one can overrule certain style aspects. +# This is preferred over using HTML_STYLESHEET since it does not replace the +# standard style sheet and is therefore more robust against future updates. +# Doxygen will copy the style sheet files to the output directory. +# Note: The order of the extra style sheet files is of importance (e.g. the last +# style sheet in the list overrules the setting of the previous ones in the +# list). For an example see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_STYLESHEET = + +# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the HTML output directory. Note +# that these files will be copied to the base HTML output directory. Use the +# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that the +# files will be copied as-is; there are no commands or markers available. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_FILES = + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen +# will adjust the colors in the style sheet and background images according to +# this color. Hue is specified as an angle on a colorwheel, see +# http://en.wikipedia.org/wiki/Hue for more information. For instance the value +# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 +# purple, and 360 is red again. +# Minimum value: 0, maximum value: 359, default value: 220. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_HUE = 220 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors +# in the HTML output. For a value of 0 the output will use grayscales only. A +# value of 255 will produce the most vivid colors. +# Minimum value: 0, maximum value: 255, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_SAT = 100 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the +# luminance component of the colors in the HTML output. Values below 100 +# gradually make the output lighter, whereas values above 100 make the output +# darker. The value divided by 100 is the actual gamma applied, so 80 represents +# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not +# change the gamma. +# Minimum value: 40, maximum value: 240, default value: 80. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_GAMMA = 80 + +# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML +# page will contain the date and time when the page was generated. Setting this +# to YES can help to show when doxygen was last run and thus if the +# documentation is up to date. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_TIMESTAMP = NO + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_DYNAMIC_SECTIONS = NO + +# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries +# shown in the various tree structured indices initially; the user can expand +# and collapse entries dynamically later on. Doxygen will expand the tree to +# such a level that at most the specified number of entries are visible (unless +# a fully collapsed tree already exceeds this amount). So setting the number of +# entries 1 will produce a full collapsed tree by default. 0 is a special value +# representing an infinite number of entries and will result in a full expanded +# tree by default. +# Minimum value: 0, maximum value: 9999, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_INDEX_NUM_ENTRIES = 100 + +# If the GENERATE_DOCSET tag is set to YES, additional index files will be +# generated that can be used as input for Apple's Xcode 3 integrated development +# environment (see: http://developer.apple.com/tools/xcode/), introduced with +# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a +# Makefile in the HTML output directory. Running make will produce the docset in +# that directory and running make install will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at +# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html +# for more information. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_DOCSET = NO + +# This tag determines the name of the docset feed. A documentation feed provides +# an umbrella under which multiple documentation sets from a single provider +# (such as a company or product suite) can be grouped. +# The default value is: Doxygen generated docs. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# This tag specifies a string that should uniquely identify the documentation +# set bundle. This should be a reverse domain-name style string, e.g. +# com.mycompany.MyDocSet. Doxygen will append .docset to the name. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify +# the documentation publisher. This should be a reverse domain-name style +# string, e.g. com.mycompany.MyDocSet.documentation. +# The default value is: org.doxygen.Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_ID = org.doxygen.Publisher + +# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. +# The default value is: Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_NAME = Publisher + +# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three +# additional HTML index files: index.hhp, index.hhc, and index.hhk. The +# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop +# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on +# Windows. +# +# The HTML Help Workshop contains a compiler that can convert all HTML output +# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML +# files are now used as the Windows 98 help format, and will replace the old +# Windows help format (.hlp) on all Windows platforms in the future. Compressed +# HTML files also contain an index, a table of contents, and you can search for +# words in the documentation. The HTML workshop also contains a viewer for +# compressed HTML files. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_HTMLHELP = NO + +# The CHM_FILE tag can be used to specify the file name of the resulting .chm +# file. You can add a path in front of the file if the result should not be +# written to the html output directory. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_FILE = + +# The HHC_LOCATION tag can be used to specify the location (absolute path +# including file name) of the HTML help compiler (hhc.exe). If non-empty, +# doxygen will try to run the HTML help compiler on the generated index.hhp. +# The file has to be specified with full path. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +HHC_LOCATION = + +# The GENERATE_CHI flag controls if a separate .chi index file is generated +# (YES) or that it should be included in the master .chm file (NO). +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +GENERATE_CHI = NO + +# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc) +# and project file content. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_INDEX_ENCODING = + +# The BINARY_TOC flag controls whether a binary table of contents is generated +# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it +# enables the Previous and Next buttons. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members to +# the table of contents of the HTML help documentation and to the tree view. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that +# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help +# (.qch) of the generated HTML documentation. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify +# the file name of the resulting .qch file. The path specified is relative to +# the HTML output folder. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help +# Project output. For more information please see Qt Help Project / Namespace +# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace). +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_NAMESPACE = org.doxygen.Project + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt +# Help Project output. For more information please see Qt Help Project / Virtual +# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual- +# folders). +# The default value is: doc. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_VIRTUAL_FOLDER = doc + +# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom +# filter to add. For more information please see Qt Help Project / Custom +# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- +# filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see Qt Help Project / Custom +# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- +# filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's filter section matches. Qt Help Project / Filter Attributes (see: +# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_SECT_FILTER_ATTRS = + +# The QHG_LOCATION tag can be used to specify the location of Qt's +# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the +# generated .qhp file. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be +# generated, together with the HTML files, they form an Eclipse help plugin. To +# install this plugin and make it available under the help contents menu in +# Eclipse, the contents of the directory containing the HTML and XML files needs +# to be copied into the plugins directory of eclipse. The name of the directory +# within the plugins directory should be the same as the ECLIPSE_DOC_ID value. +# After copying Eclipse needs to be restarted before the help appears. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the Eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have this +# name. Each documentation set should have its own identifier. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. + +ECLIPSE_DOC_ID = org.doxygen.Project + +# If you want full control over the layout of the generated HTML pages it might +# be necessary to disable the index and replace it with your own. The +# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top +# of each HTML page. A value of NO enables the index and the value YES disables +# it. Since the tabs in the index contain the same information as the navigation +# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +DISABLE_INDEX = NO + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. If the tag +# value is set to YES, a side panel will be generated containing a tree-like +# index structure (just like the one that is generated for HTML Help). For this +# to work a browser that supports JavaScript, DHTML, CSS and frames is required +# (i.e. any modern browser). Windows users are probably better off using the +# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can +# further fine-tune the look of the index. As an example, the default style +# sheet generated by doxygen has an example that shows how to put an image at +# the root of the tree instead of the PROJECT_NAME. Since the tree basically has +# the same information as the tab index, you could consider setting +# DISABLE_INDEX to YES when enabling this option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_TREEVIEW = YES + +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that +# doxygen will group on one line in the generated HTML documentation. +# +# Note that a value of 0 will completely suppress the enum values from appearing +# in the overview section. +# Minimum value: 0, maximum value: 20, default value: 4. +# This tag requires that the tag GENERATE_HTML is set to YES. + +ENUM_VALUES_PER_LINE = 4 + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used +# to set the initial width (in pixels) of the frame in which the tree is shown. +# Minimum value: 0, maximum value: 1500, default value: 250. +# This tag requires that the tag GENERATE_HTML is set to YES. + +TREEVIEW_WIDTH = 250 + +# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to +# external symbols imported via tag files in a separate window. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +EXT_LINKS_IN_WINDOW = NO + +# Use this tag to change the font size of LaTeX formulas included as images in +# the HTML documentation. When you change the font size after a successful +# doxygen run you need to manually remove any form_*.png images from the HTML +# output directory to force them to be regenerated. +# Minimum value: 8, maximum value: 50, default value: 10. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_FONTSIZE = 10 + +# Use the FORMULA_TRANPARENT tag to determine whether or not the images +# generated for formulas are transparent PNGs. Transparent PNGs are not +# supported properly for IE 6.0, but are supported on all modern browsers. +# +# Note that when changing this option you need to delete any form_*.png files in +# the HTML output directory before the changes have effect. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_TRANSPARENT = YES + +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see +# http://www.mathjax.org) which uses client side Javascript for the rendering +# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX +# installed or if you want to formulas look prettier in the HTML output. When +# enabled you may also need to install MathJax separately and configure the path +# to it using the MATHJAX_RELPATH option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +USE_MATHJAX = NO + +# When MathJax is enabled you can set the default output format to be used for +# the MathJax output. See the MathJax site (see: +# http://docs.mathjax.org/en/latest/output.html) for more details. +# Possible values are: HTML-CSS (which is slower, but has the best +# compatibility), NativeMML (i.e. MathML) and SVG. +# The default value is: HTML-CSS. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_FORMAT = HTML-CSS + +# When MathJax is enabled you need to specify the location relative to the HTML +# output directory using the MATHJAX_RELPATH option. The destination directory +# should contain the MathJax.js script. For instance, if the mathjax directory +# is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax +# Content Delivery Network so you can quickly see the result without installing +# MathJax. However, it is strongly recommended to install a local copy of +# MathJax from http://www.mathjax.org before deployment. +# The default value is: http://cdn.mathjax.org/mathjax/latest. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest + +# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax +# extension names that should be enabled during MathJax rendering. For example +# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_EXTENSIONS = + +# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces +# of code that will be used on startup of the MathJax code. See the MathJax site +# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an +# example see the documentation. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_CODEFILE = + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box for +# the HTML output. The underlying search engine uses javascript and DHTML and +# should work on any modern browser. Note that when using HTML help +# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) +# there is already a search function so this one should typically be disabled. +# For large projects the javascript based search engine can be slow, then +# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to +# search using the keyboard; to jump to the search box use + S +# (what the is depends on the OS and browser, but it is typically +# , /